Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dosbox-staging/dosbox-staging.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatryk Obara <dreamer.tan@gmail.com>2019-09-15 01:58:06 +0300
committerPatryk Obara <dreamer.tan@gmail.com>2019-09-15 01:58:06 +0300
commit670fea4047fa0de0d0786a4392023a01a830a101 (patch)
tree23dbb65065a54ccd6f1e0ff7a4420e088f8707d8
parent963dfbb0538a21f0e834dac3395a2c89fd246a89 (diff)
parentdda3b2a51f6dbc61d08be82dd47e18b784cb2158 (diff)
Merge branch 'svn/trunk'v0.75.0-pre
-rw-r--r--AUTHORS12
-rw-r--r--COPYING339
-rw-r--r--ChangeLog707
-rw-r--r--INSTALL105
-rw-r--r--Makefile.am4
-rw-r--r--NEWS685
-rw-r--r--README1577
-rw-r--r--THANKS29
-rw-r--r--VERSION1
-rw-r--r--acinclude.m4398
-rwxr-xr-xautogen.sh14
-rw-r--r--configure.ac635
-rw-r--r--docs/Makefile.am8
-rw-r--r--docs/PORTING50
-rw-r--r--docs/README.video35
-rw-r--r--docs/dosbox.1391
-rw-r--r--include/Makefile.am40
-rw-r--r--include/bios.h136
-rw-r--r--include/bios_disk.h91
-rw-r--r--include/callback.h111
-rw-r--r--include/control.h93
-rw-r--r--include/cpu.h492
-rw-r--r--include/cross.h109
-rw-r--r--include/debug.h35
-rw-r--r--include/dma.h118
-rw-r--r--include/dos_inc.h653
-rw-r--r--include/dos_system.h291
-rw-r--r--include/dosbox.h75
-rw-r--r--include/fpu.h176
-rw-r--r--include/hardware.h54
-rw-r--r--include/inout.h79
-rw-r--r--include/ipx.h160
-rw-r--r--include/ipxserver.h48
-rw-r--r--include/joystick.h49
-rw-r--r--include/keyboard.h53
-rw-r--r--include/logging.h90
-rw-r--r--include/mapper.h40
-rw-r--r--include/mem.h219
-rw-r--r--include/midi.h60
-rw-r--r--include/mixer.h122
-rw-r--r--include/mouse.h42
-rw-r--r--include/paging.h367
-rw-r--r--include/pci_bus.h86
-rw-r--r--include/pic.h63
-rw-r--r--include/programs.h92
-rw-r--r--include/regs.h169
-rw-r--r--include/render.h98
-rw-r--r--include/serialport.h447
-rw-r--r--include/setup.h348
-rw-r--r--include/shell.h147
-rw-r--r--include/support.h60
-rw-r--r--include/timer.h38
-rw-r--r--include/vga.h532
-rw-r--r--include/video.h84
-rw-r--r--scripts/captures.bat1
-rw-r--r--scripts/dosbox-installer.nsi184
-rw-r--r--scripts/editconf.bat1
-rw-r--r--scripts/ega-switch.pl32
-rw-r--r--scripts/font-switch.pl25
-rw-r--r--scripts/resetconf.bat1
-rw-r--r--scripts/resetmapper.bat1
-rw-r--r--src/Makefile.am21
-rw-r--r--src/cpu/Makefile.am7
-rw-r--r--src/cpu/callback.cpp690
-rw-r--r--src/cpu/core_dyn_x86.cpp543
-rw-r--r--src/cpu/core_dyn_x86/Makefile.am2
-rw-r--r--src/cpu/core_dyn_x86/cache.h643
-rw-r--r--src/cpu/core_dyn_x86/decoder.h2722
-rw-r--r--src/cpu/core_dyn_x86/dyn_fpu.h669
-rw-r--r--src/cpu/core_dyn_x86/dyn_fpu_dh.h497
-rw-r--r--src/cpu/core_dyn_x86/helpers.h87
-rw-r--r--src/cpu/core_dyn_x86/risc_x86.h1072
-rw-r--r--src/cpu/core_dyn_x86/string.h164
-rw-r--r--src/cpu/core_dynrec.cpp336
-rw-r--r--src/cpu/core_dynrec/Makefile.am5
-rw-r--r--src/cpu/core_dynrec/cache.h665
-rw-r--r--src/cpu/core_dynrec/decoder.h617
-rw-r--r--src/cpu/core_dynrec/decoder_basic.h1276
-rw-r--r--src/cpu/core_dynrec/decoder_opcodes.h1438
-rw-r--r--src/cpu/core_dynrec/dyn_fpu.h696
-rw-r--r--src/cpu/core_dynrec/operators.h1996
-rw-r--r--src/cpu/core_dynrec/risc_armv4le-common.h113
-rw-r--r--src/cpu/core_dynrec/risc_armv4le-o3.h1302
-rw-r--r--src/cpu/core_dynrec/risc_armv4le-thumb-iw.h1505
-rw-r--r--src/cpu/core_dynrec/risc_armv4le-thumb-niw.h1538
-rw-r--r--src/cpu/core_dynrec/risc_armv4le-thumb.h1335
-rw-r--r--src/cpu/core_dynrec/risc_armv4le.h36
-rw-r--r--src/cpu/core_dynrec/risc_armv8le.h1238
-rw-r--r--src/cpu/core_dynrec/risc_mipsel32.h750
-rw-r--r--src/cpu/core_dynrec/risc_x64.h707
-rw-r--r--src/cpu/core_dynrec/risc_x86.h516
-rw-r--r--src/cpu/core_full.cpp99
-rw-r--r--src/cpu/core_full/Makefile.am3
-rw-r--r--src/cpu/core_full/ea_lookup.h255
-rw-r--r--src/cpu/core_full/load.h518
-rw-r--r--src/cpu/core_full/loadwrite.h57
-rw-r--r--src/cpu/core_full/op.h667
-rw-r--r--src/cpu/core_full/optable.h832
-rw-r--r--src/cpu/core_full/save.h117
-rw-r--r--src/cpu/core_full/string.h252
-rw-r--r--src/cpu/core_full/support.h269
-rw-r--r--src/cpu/core_normal.cpp211
-rw-r--r--src/cpu/core_normal/Makefile.am3
-rw-r--r--src/cpu/core_normal/helpers.h162
-rw-r--r--src/cpu/core_normal/prefix_0f.h616
-rw-r--r--src/cpu/core_normal/prefix_66.h718
-rw-r--r--src/cpu/core_normal/prefix_66_0f.h465
-rw-r--r--src/cpu/core_normal/prefix_none.h1176
-rw-r--r--src/cpu/core_normal/string.h257
-rw-r--r--src/cpu/core_normal/support.h96
-rw-r--r--src/cpu/core_normal/table_ea.h185
-rw-r--r--src/cpu/core_prefetch.cpp316
-rw-r--r--src/cpu/core_simple.cpp207
-rw-r--r--src/cpu/cpu.cpp2430
-rw-r--r--src/cpu/flags.cpp1188
-rw-r--r--src/cpu/instructions.h936
-rw-r--r--src/cpu/lazyflags.h134
-rw-r--r--src/cpu/modrm.cpp210
-rw-r--r--src/cpu/modrm.h64
-rw-r--r--src/cpu/paging.cpp890
-rw-r--r--src/debug/Makefile.am4
-rw-r--r--src/debug/debug.cpp2588
-rw-r--r--src/debug/debug_disasm.cpp1123
-rw-r--r--src/debug/debug_gui.cpp291
-rw-r--r--src/debug/debug_inc.h60
-rw-r--r--src/debug/debug_win32.cpp82
-rw-r--r--src/debug/disasm_tables.h191
-rw-r--r--src/dos/Makefile.am10
-rw-r--r--src/dos/cdrom.cpp218
-rw-r--r--src/dos/cdrom.h397
-rw-r--r--src/dos/cdrom_aspi_win32.cpp768
-rw-r--r--src/dos/cdrom_image.cpp758
-rw-r--r--src/dos/cdrom_ioctl_linux.cpp97
-rw-r--r--src/dos/cdrom_ioctl_os2.cpp152
-rw-r--r--src/dos/cdrom_ioctl_win32.cpp623
-rw-r--r--src/dos/dev_con.h440
-rw-r--r--src/dos/dos.cpp1274
-rw-r--r--src/dos/dos_classes.cpp519
-rw-r--r--src/dos/dos_codepages.h1131
-rw-r--r--src/dos/dos_devices.cpp196
-rw-r--r--src/dos/dos_execute.cpp515
-rw-r--r--src/dos/dos_files.cpp1334
-rw-r--r--src/dos/dos_ioctl.cpp233
-rw-r--r--src/dos/dos_keyboard_layout.cpp1302
-rw-r--r--src/dos/dos_keyboard_layout_data.h5393
-rw-r--r--src/dos/dos_memory.cpp448
-rw-r--r--src/dos/dos_misc.cpp214
-rw-r--r--src/dos/dos_mscdex.cpp1359
-rw-r--r--src/dos/dos_programs.cpp1835
-rw-r--r--src/dos/dos_tables.cpp179
-rw-r--r--src/dos/drive_cache.cpp947
-rw-r--r--src/dos/drive_fat.cpp1435
-rw-r--r--src/dos/drive_iso.cpp563
-rw-r--r--src/dos/drive_local.cpp655
-rw-r--r--src/dos/drive_overlay.cpp1182
-rw-r--r--src/dos/drive_virtual.cpp276
-rw-r--r--src/dos/drives.cpp230
-rw-r--r--src/dos/drives.h467
-rw-r--r--src/dos/scsidefs.h283
-rw-r--r--src/dos/wnaspi32.h354
-rw-r--r--src/dosbox.cpp793
-rw-r--r--src/dosbox.icobin0 -> 83966 bytes
-rw-r--r--src/fpu/Makefile.am5
-rw-r--r--src/fpu/fpu.cpp630
-rw-r--r--src/fpu/fpu_instructions.h640
-rw-r--r--src/fpu/fpu_instructions_x86.h1359
-rw-r--r--src/gui/Makefile.am11
-rw-r--r--src/gui/dosbox_logo.h540
-rw-r--r--src/gui/dosbox_splash.h1028
-rw-r--r--src/gui/midi.cpp229
-rw-r--r--src/gui/midi_alsa.h200
-rw-r--r--src/gui/midi_coreaudio.h195
-rw-r--r--src/gui/midi_coremidi.h146
-rw-r--r--src/gui/midi_oss.h76
-rw-r--r--src/gui/midi_win32.h117
-rw-r--r--src/gui/render.cpp633
-rw-r--r--src/gui/render_loops.h157
-rw-r--r--src/gui/render_scalers.cpp440
-rw-r--r--src/gui/render_scalers.h139
-rw-r--r--src/gui/render_simple.h121
-rw-r--r--src/gui/render_templates.h580
-rw-r--r--src/gui/render_templates_hq.h85
-rw-r--r--src/gui/render_templates_hq2x.h1896
-rw-r--r--src/gui/render_templates_hq3x.h2872
-rw-r--r--src/gui/render_templates_sai.h229
-rw-r--r--src/gui/sdl_gui.cpp629
-rw-r--r--src/gui/sdl_mapper.cpp2561
-rw-r--r--src/gui/sdlmain.cpp2229
-rw-r--r--src/hardware/Makefile.am15
-rw-r--r--src/hardware/adlib.cpp868
-rw-r--r--src/hardware/adlib.h162
-rw-r--r--src/hardware/cmos.cpp330
-rw-r--r--src/hardware/dbopl.cpp1521
-rw-r--r--src/hardware/dbopl.h259
-rw-r--r--src/hardware/disney.cpp399
-rw-r--r--src/hardware/dma.cpp410
-rw-r--r--src/hardware/gameblaster.cpp173
-rw-r--r--src/hardware/gus.cpp923
-rw-r--r--src/hardware/hardware.cpp778
-rw-r--r--src/hardware/iohandler.cpp529
-rw-r--r--src/hardware/ipx.cpp1200
-rw-r--r--src/hardware/ipxserver.cpp232
-rw-r--r--src/hardware/joystick.cpp337
-rw-r--r--src/hardware/keyboard.cpp403
-rw-r--r--src/hardware/mame/Makefile.am9
-rw-r--r--src/hardware/mame/emu.h140
-rw-r--r--src/hardware/mame/fmopl.cpp2577
-rw-r--r--src/hardware/mame/fmopl.h113
-rw-r--r--src/hardware/mame/saa1099.cpp471
-rw-r--r--src/hardware/mame/saa1099.h120
-rw-r--r--src/hardware/mame/sn76496.cpp518
-rw-r--r--src/hardware/mame/sn76496.h181
-rw-r--r--src/hardware/mame/ymdeltat.cpp650
-rw-r--r--src/hardware/mame/ymdeltat.h87
-rw-r--r--src/hardware/mame/ymf262.cpp2792
-rw-r--r--src/hardware/mame/ymf262.h40
-rw-r--r--src/hardware/memory.cpp614
-rw-r--r--src/hardware/mixer.cpp719
-rw-r--r--src/hardware/mpu401.cpp676
-rw-r--r--src/hardware/opl.cpp1462
-rw-r--r--src/hardware/opl.h201
-rw-r--r--src/hardware/pci_bus.cpp444
-rw-r--r--src/hardware/pci_devices.h18
-rw-r--r--src/hardware/pcspeaker.cpp360
-rw-r--r--src/hardware/pic.cpp624
-rw-r--r--src/hardware/sblaster.cpp1738
-rw-r--r--src/hardware/serialport/Makefile.am9
-rw-r--r--src/hardware/serialport/directserial.cpp318
-rw-r--r--src/hardware/serialport/directserial.h70
-rw-r--r--src/hardware/serialport/libserial.cpp708
-rw-r--r--src/hardware/serialport/libserial.h56
-rw-r--r--src/hardware/serialport/misc_util.cpp329
-rw-r--r--src/hardware/serialport/misc_util.h122
-rw-r--r--src/hardware/serialport/nullmodem.cpp588
-rw-r--r--src/hardware/serialport/nullmodem.h120
-rw-r--r--src/hardware/serialport/serialdummy.cpp102
-rw-r--r--src/hardware/serialport/serialdummy.h48
-rw-r--r--src/hardware/serialport/serialport.cpp1298
-rw-r--r--src/hardware/serialport/softmodem.cpp837
-rw-r--r--src/hardware/serialport/softmodem.h250
-rw-r--r--src/hardware/tandy_sound.cpp357
-rw-r--r--src/hardware/timer.cpp473
-rw-r--r--src/hardware/vga.cpp263
-rw-r--r--src/hardware/vga_attr.cpp296
-rw-r--r--src/hardware/vga_crtc.cpp435
-rw-r--r--src/hardware/vga_dac.cpp218
-rw-r--r--src/hardware/vga_draw.cpp1622
-rw-r--r--src/hardware/vga_gfx.cpp235
-rw-r--r--src/hardware/vga_memory.cpp988
-rw-r--r--src/hardware/vga_misc.cpp146
-rw-r--r--src/hardware/vga_other.cpp867
-rw-r--r--src/hardware/vga_paradise.cpp241
-rw-r--r--src/hardware/vga_s3.cpp565
-rw-r--r--src/hardware/vga_seq.cpp160
-rw-r--r--src/hardware/vga_tseng.cpp807
-rw-r--r--src/hardware/vga_xga.cpp1319
-rw-r--r--src/ints/Makefile.am7
-rw-r--r--src/ints/bios.cpp1361
-rw-r--r--src/ints/bios_disk.cpp609
-rw-r--r--src/ints/bios_keyboard.cpp680
-rw-r--r--src/ints/ems.cpp1471
-rw-r--r--src/ints/int10.cpp769
-rw-r--r--src/ints/int10.h239
-rw-r--r--src/ints/int10_char.cpp721
-rw-r--r--src/ints/int10_memory.cpp1634
-rw-r--r--src/ints/int10_misc.cpp311
-rw-r--r--src/ints/int10_modes.cpp1515
-rw-r--r--src/ints/int10_pal.cpp424
-rw-r--r--src/ints/int10_put_pixel.cpp260
-rw-r--r--src/ints/int10_vesa.cpp612
-rw-r--r--src/ints/int10_video_state.cpp383
-rw-r--r--src/ints/int10_vptable.cpp797
-rw-r--r--src/ints/mouse.cpp1179
-rw-r--r--src/ints/xms.cpp485
-rw-r--r--src/ints/xms.h34
-rw-r--r--src/libs/Makefile.am3
-rw-r--r--src/libs/gui_tk/Doxyfile238
-rw-r--r--src/libs/gui_tk/Makefile.am4
-rw-r--r--src/libs/gui_tk/gui_tk.cpp1706
-rw-r--r--src/libs/gui_tk/gui_tk.h2224
-rw-r--r--src/libs/zmbv/Makefile.am1
-rw-r--r--src/libs/zmbv/drvproc.cpp212
-rw-r--r--src/libs/zmbv/makedll.mk17
-rw-r--r--src/libs/zmbv/resource.h8
-rw-r--r--src/libs/zmbv/resource.rc40
-rw-r--r--src/libs/zmbv/zmbv.cpp549
-rw-r--r--src/libs/zmbv/zmbv.def4
-rw-r--r--src/libs/zmbv/zmbv.h118
-rw-r--r--src/libs/zmbv/zmbv.inf105
-rw-r--r--src/libs/zmbv/zmbv.sln21
-rw-r--r--src/libs/zmbv/zmbv.vcproj141
-rw-r--r--src/libs/zmbv/zmbv_mingw.def2
-rw-r--r--src/libs/zmbv/zmbv_vfw.cpp426
-rw-r--r--src/libs/zmbv/zmbv_vfw.h61
-rw-r--r--src/libs/zmbv/zmbv_vfw.rc123
-rw-r--r--src/misc/Makefile.am4
-rw-r--r--src/misc/cross.cpp304
-rw-r--r--src/misc/messages.cpp144
-rw-r--r--src/misc/programs.cpp825
-rw-r--r--src/misc/setup.cpp1208
-rw-r--r--src/misc/support.cpp190
-rw-r--r--src/platform/Makefile.am3
-rw-r--r--src/platform/sdl-win32.diff55
-rw-r--r--src/platform/visualc/Makefile.am1
-rw-r--r--src/platform/visualc/config.h78
-rw-r--r--src/platform/visualc/ntddcdrm.h320
-rw-r--r--src/platform/visualc/ntddscsi.h174
-rw-r--r--src/platform/visualc/unistd.h10
-rw-r--r--src/shell/Makefile.am4
-rw-r--r--src/shell/shell.cpp766
-rw-r--r--src/shell/shell_batch.cpp216
-rw-r--r--src/shell/shell_cmds.cpp1333
-rw-r--r--src/shell/shell_misc.cpp642
-rw-r--r--src/winres.rc37
-rw-r--r--visualc_net/Makefile.am1
-rw-r--r--visualc_net/dosbox.sln21
-rw-r--r--visualc_net/dosbox.vcproj920
317 files changed, 160077 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 000000000..eb365e5d6
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,12 @@
+The DOSBox Team
+---------------
+
+Sjoerd v.d. Berg <harekiet>
+Peter Veenstra <qbix79>
+Ulf Wohlers <finsterr>
+Tommy Frössman <fanskapet>
+Dean Beeler <canadacow>
+Sebastian Strohhäcker <c2woody>
+Ralf Grillenberger <h-a-l-9000>
+
+nick_without_<> @ users.sourceforge.net
diff --git a/COPYING b/COPYING
new file mode 100644
index 000000000..6b29640cd
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 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) <year> <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.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 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) year 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/ChangeLog b/ChangeLog
new file mode 100644
index 000000000..d35031ae4
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,707 @@
+0.74-2
+ - Rewrite auto/max cycles algorithm to work better with windows 7, other
+ OSes might benefit as well.
+ - Update 64bit recompiler to work on OSX and Linux.
+ - Several improvements to make the recompilers work with newer compilers and
+ add some workarounds about clang confusing itself.
+ - Fix several variables being the wrong size in the recompiler.
+ - Support absolute 64 bit addressing. (DRC:64 bit error messages)
+ - Zero extend data in dynrec core for LLVM compilation/
+ - Reduce overhead of the Mac version with a lot. Results in a speed increase.
+ - Replace NV_PixelDataRange with the more common ARB_PixelBufferObject
+ extension. Should help with output=opengl.
+ - Reuse graphics window if possible instead of always creating a new one!
+ - Add patches to work better when called by WINE:
+ - support WINE style namemangling.
+ - allow Z:\ to be moved to a different drive.
+ - Try to fix stuttering audio with opengl output on Linux. (or at least
+ improve it. Linux users might need to increase the prebuffer option a bit)
+ - Fix automake and autoconf problems.
+ - Fix problems related to packed struct layouts.
+ - Fix compilation on gcc 4.4 and gcc 4.6.1.
+ - Fix compilation with -DPIC.
+ - Hopefully fix crash on shutdown, when unknown condition is encountered.
+ - Fix -lto with gcc.
+ - Fix clang compliation with asm fpu core.
+ - Fix mapper crash on startup and when changing the mapper key.
+ - Fix compilation in VS2015.
+ - Fix compilation on mingw64.
+ - Fix compilation on Frisbee and newer clang.
+ - Fix compilation machines that have X11 libraries installed, but use a SDL
+ without X11.
+ - Fix a few problems relating to video capturing:
+ - Writing out the index too often. (slowdown with longer captures)
+ - Not being aware of when only the refresh rate changed.
+ - Fix call order when bitshifting the return order (VS C /O2 builds).
+ - Fix sign-extension error in S3 draw funcion, i.e. win3.1 analog clock.
+ - Fix expanddot not caring about the size of the inputbuffer.
+ - Some fixes for the debugger related to starting a program through debug.com.
+ - Bring OS/2 port code up to date.
+ - Reduce the amount of warning when compiling with quite a bit.
+ - Allow 15/26/32 bits input to the opengl output.
+ - Add cmd-q as exit key for macs.
+ - Fix icon and titlebar on very old setups.
+ - Fix off by one display size calculation errors with very large displays.
+ - Fix out of bounds access the unused byte in the 32 bit colour value of the
+ hardware mouse cursor pixel was affected.
+ - Fix depreciated warnings on Mac, allow soundfont loading for coreaudio.
+ - Keep repeating a pressed key if another key was unpressed.
+ - Fix crash on OS X related to putting junk in the titlebar.
+ - Extend fullresolution=0x0 to work on Linux and Mac.
+ - Change gameblaster and tandy sound core to latest mame version.
+ - Improve numlock and capslock support on Linux and Macs. Still not perfect
+ on Windows.
+ - Added even more code to workaround Windows behaving weirdly with alt-tab.
+
+0.74
+ - Several small game specific fixes/hacks/support. (Offensive,
+ Roadhog, GTA installer, Kingdom O' Magic soundcard detection,
+ Pirate booter, Armored Fist installer)
+ - Add the S3-specific 640x480 256 color mode. (fixes regression in "Wooden
+ Ships and Iron Men" and "I Have No Mouth And I Must Scream")
+ - Fix a stack overflow that could crash DOSBox.
+ - Add fake microphone input. (fixes Talking Parrot)
+ - Modify adlib turn off code, so that it doesn't turn off in
+ cases where the same sound is repeated over and over again.
+ - Several small fixes to the CDROM audio code. (HOMM2, Redneck
+ Rampage and others)
+ - Several improvements to the CDROM emulation code. (fixes Alpha
+ Storm and GT Racing 97)
+ - Some small CPU fixes that might fix something.
+ - Handle opcode 0xff subcode 7 as invalid instruction. (fixes dif-2 & others)
+ - Some hercules fixes. (Testdrive)
+ - Improve support for blanked parts that wrap around to the start of
+ the screen. (fixes Magic Circle demo and Sid&Al)
+ - Remove old OPL cores as the new ones seem to work very nicely.
+ - Modify movie recording code so that the movies aren't corrupt when
+ you exit dosbox without stopping the movie.
+ - Change RGB3x scaler to look more pretty.
+ - Improve initial register values compatility of the GUS.
+ - Added autodetection for Gameblaster. (games can find it now)
+ - Change render preferences a bit to be more compatible with Windows 7.
+ - Add DOS fixes to terminate program. (fixes Fortune Teller)
+ - Add FFREEP. (fixes Trucks)
+ - Improve FPU ST80 in C mode when writing zero. (fixes Antigok)
+ - Add special int10 scanline function. (fixes mz700 and probably lots
+ of games that mess with them)
+ - Fix scrolling in rarely used video modes. (fixes Orphee)
+ - Modify game specific hacks a bit so that Kick off 3 works again.
+ - Lots of fixes to the INT10 video parameter table. (Seven spirits
+ of ra and others)
+ - Add VGA blanking in machine=vgaonly. (used by Alien Carnage)
+ - CGA, PCJr, Tandy: Add video blanking, change display start latch
+ timing, sync pulse width correction.
+ - PCJr, Tandy: implement vertical retrace interrupt.
+ - PCJr, CGA: line-by-line video emulation.
+ - PCJr: support on-screen change of color modes 4medium to 16low. (used
+ by Ghostbusters booter)
+ - Hercules: Add green and amber monochrome support.
+ - All machines: only update the video timing when needed. (Jungle Hunt,
+ others that synchronize to the video screen might profit)
+ - Several small DOS fixes.
+ - Some UMB related fixes. (The Legacy without UMB)
+ - Fix version number of DSP for SB 1.5. (fixes a few games)
+ - Several VGA emulation improvements. (Allertone football manager)
+ - Some Tandy fixes. (Mech Warrior)
+ - Small improvements and fixes to the OPL emulation.
+ - Add low level Tandy DAC emulation.
+ - Some EMS fixes. (fixes Mortal Kombat and others)
+ - Change SoundBlaster DSP reset mechanism, add sb irq acknowledge logic.
+ (fixes stmik-based applications)
+ - Some interrupt pointer location modifications. (fixes Tinker Tales)
+ - Some fixes to the BOOT code. (fixes Last Mission)
+ - Respect write-only file information. (fixes Champions of Zulala)
+ - Some RTC fix. (fixes Tully Bodine and others)
+ - Improve mouse emulation to work better with Water World.
+ - Hopefully fix the translation of the configuration file.
+ - Speed up and fixes for the recompiler core. (pitfall2 pcjr)
+ - Change memory start location. (fixes 7th Guest installer)
+ - Several fixes to the batch file handling. (Shift and
+ use the typed first %0 instead of the parsed %0)
+ - Improve file redirection and redirected line ends. (fixes
+ Phantasmagoria 2 DOS installer)
+ - Fix compilation with new MAC os X version.
+ - Add 16C550A FIFO support to the serial port emulation.
+ - Improve modem emulation to get higher speeds.
+ - Change default samplerates to 44100, blocksize to 1024 and prebuffer to 20,
+ so that hopefully certain soundcards produce more fluent sound playback.
+ - Add some rarely used, but for some games critical flags to
+ the internal commands.
+ - Add -userconf flag, so that the userspecific configuration can
+ easily be used together with -conf configfile.
+ - Improve internal timing with repeating timers (especially with
+ the dynamic core).
+
+0.73
+ - Add two new opl2+opl3 emulators. (better speed, different implementation
+ approach)
+ - Improved DRO recording/better file structure.
+ - Add EGA emulation.
+ - Add special vga machine mode. Supports more of the exotic tricks like
+ changing the palette during screen updates, 9x16 fonts etc.
+ - Added special machine modes for the following svga cards:
+ - S3
+ - Paradise
+ - Tseng
+ - Fix problems with the vga split line feature.
+ - Improve vesa emulation.
+ - Add optional selection of old vesa mode for games that don't work
+ with certain vesa features.
+ - Improve video BIOS emulation to behave more like a real bios.
+ - Fixes for emulated 4bpp graphics modes.
+ - Fixes to paging system.
+ - Various fixes and improvements for the recompiling core.
+ - Add arm backend for the recompiling core.
+ - Add some mscdex quirks when dealing with files that are exactly 8.3 long.
+ - Small fixes to batch file handling.
+ - Small fixes to the XMS memory handling.
+ - Various fixes for aligned memory on hosts that want it.
+ - Various improvements to the mouse.
+ - Fixes and small speed ups to the debugger.
+ - Fix and improve lot's of compilation problems. (curses detection,
+ GCC 3.4 and GCC 4.X fixes)
+ - Added some basic auto keyboard layout handling. (windows only currently)
+ - Add basic support for evdev keyboard driver.
+ - Various fixes to the timer. (improve mode 2 timer changes,
+ implement mode 1, improve gate2 handling)
+ - Add audio extraction and mci audio support. Should enable CDROM audio
+ for Vista and adds volume control.
+ - Improve the directory cache speed a lot, especially with mounting slow
+ media like network paths.
+ - Various fixes to the create temporary file call.
+ - Don't keep batchfiles open during execution. Allows rewriting of the
+ active batchfile. (menu programs use this trick sometimes)
+ - Fix problems with filenames with 2 extensions.
+ - Add some more lowlevel dos tables.
+ - Fixes to hercules emulation.
+ - Fix flag handling for special case of ROR.
+ - Make the batchfile handling in regard to IF more flexible.
+ - Fixes to scrolling/panning feature.
+ - Add prefetch queue emulation.
+ - Make the emulated cpu type selectable. This is mainly the
+ identification commands and the way paging works.
+ - Some special EMS functionality added. (OS handles, zero-page handling)
+ - Improve support for EMS when booting a different OS.
+ - Improve cdrom speed detection by games.
+ - Improve stability of cycle guessing code, when there is background
+ activity.
+ - Fix various mscdex and cdrom detection schemes.
+ - Added Coremidi support on Mac OS X.
+ - Improve support for DOS devices when used to detect the existance
+ of directories in various ways.
+ - Add IRQ 2 emulation on VRET. (ega only)
+ - Added video parameter table and video state functionality.
+ - Increase default freespace to 250 MB.
+ - Some fixes to the fat filesystem handling for disk images.
+ - Some soundblaster fixes and command additions.
+ - Fix mixer 16bit direct transfers on bigendian hosts.
+
+0.72
+ - Fixed unitialized variable in joystick. (Fixes crashes on Vista and
+ Mac OS X)
+ - Some bugfixes and speedups to the 64 bit recompiling core.
+ - Fixed sign flag on soundblaster dma transfers (Space Quest 6 intro)
+ - Fixed a bug in keyboard layout processing code and fixed certain
+ layouts.
+ - Fixed Dreamweb.
+ - Improved speed unlocking when running cycles=max.
+ - Fixed a crash related to the tab completion in the shell.
+ - Improved aspect correction code. Should now be like how a real monitor
+ handles it.
+ - Fixed a bug in the xms status report code. (Blake Stone 1.0 shareware)
+ - Added a lot more keyboard layouts.
+ - Fix crash related to changing the scaler before a screen was created.
+ - Hopefully fixed compilation on *bsd.
+ - Enabled auto cpu core selection for recompiling core as well.
+ - Made the used joystick selectable when 4axis is specified.
+ - Added some hints for inexperienced DOS users to the shell.
+
+0.71
+ - Add a new recompiling cpu core, which should be easier to port.
+ - Add 64 bit version of the recompiling core.
+ - Add mipsel 32 bit version of the recompiling core.
+ - Fix a few small problems with FCBs. (fixes Jewels of darkness and
+ cyrus chess)
+ - Raise some more exceptions. (fixes vbdos)
+ - Fix a few problems with the dynamic core. (fixes Inner Words,
+ Archmimedean Dynasty and others)
+ - Improve/Fix fallback code for certain graphics cards.
+ - Fix a few cd audio related bugs.
+ - Add an undocumented MSCDEX feature. (Fixes Ultimate Domain)
+ - Fix some pcspeaker mode. (fixes Test Drive and similar games)
+ - Improve dos keyinput handling. (fixes Wing Commander 3 exit dialog)
+ - Remove Exit condition on fully nested mode. (fixes some demo)
+ - Add image file size detection.
+ - Add/Fix some ansi codes. (fixes PC Larn and certain versions of
+ infocom games)
+ - Several general DOS fixes. (fixes nba95, hexit and various other games)
+ - Add some valid input checks. (fixes 3d body adventure and similar
+ games)
+ - Fix digital joystick centering problem.
+ - Reenable textmode 54 and 55.
+ - Fix a pelmask problem with univbe 5.0 lite. (fixes Panzer General)
+ - Fix minor mixer underflow.
+ - Some general image and bios disk emulation fixes.
+ - Hopefully fix compilation on BSD and darwin.
+ - Try using ioctl cdrom access by default if possible.
+ - Fix some svga detection routine. (fixes Grandest Fleet 2 and Bobby Fischer
+ Teaches Chess)
+ - You can now close DOSBox using the status window in win32.
+ - Add support for NX enabled systems.
+ - Fix a casting error which only showed with certain compilers. (fixes
+ various games under mac os x and 64 bit linux)
+ - Improve timer and add gate 2 support. (fixes various games and
+ joystick problems)
+ - Improve mouse. Add undocumented backdoor. (fixes Last half of Darkness,
+ PC-BLOX and others)
+ - Add/improve support for ~ and ~username in all commands.
+ - Fix a font problem with the pcjr/tandy. (fixes personal deskmate 2)
+ - Change dma routine a bit. (fixes ticks in sound in various games)
+ - Allow read-only diskimages to be booted. (fixes various booter
+ games)
+ - Add basic hidden file support on cdrom images. (fixes Player
+ Manager 2)
+ - Add some rarely used functionality to the int10 mode setup. (fixes
+ WW2 Battles of the South pacific)
+ - Add ability to force scaler usage.
+ - Speed up flag generation and make it more 386-like.
+ - Some colourful feedback in the mapper.
+ - General code cleanup.
+
+0.70
+ - Improve register handling and support with XMS.
+ - Fix some issues with deleting open files.(windows only issue)
+ - Add dummy LPT1 class. (windows only issue)
+ - Improve some of the internal dos commands. (choice, copy and shift)
+ - Improve ROM area. (for games that use it for random numbers or
+ overwrite it as some sort of detection thing)
+ - Improve compatibility of dynamic core by making it handle certain
+ pagefaults earlier.
+ - Move internal dos tables around so we have more umb memory.
+ - Add some dos tables.
+ - Dynamic core supports io exceptions.
+ - Move some interrupt handlers to XT Bios locations.
+ - Add a dynamic fpu on x86.
+ - Improve fpu on non-x86.
+ - Trapflag gets strict priority over hardware IRQs.
+ - Trapflag support for the dynamic core.
+ - Add dummy TRx handling.
+ - Fix a few rarely used character functions.
+ - Improve auto cycle guessing code.
+ - Improve and extend the joystick support.
+ - Add autofire support.
+ - Improve the mapper so you can map keys to the joystick and vice versa.
+ - A few game specific video card fixes.
+ - Fix some 64 bit cpu bugs.
+ - Add support for certain cdrom detection schemes.
+ - Improve HSG/Red Book support.
+ - Improve MSCDEX.
+ - Improve dynamic core support under intel macs.
+ - Add basic support for clipper programs.
+ - Add support for different keyboard layouts.
+ - Add auto core guessing.
+ - Fix a few flags bugs.
+ - Fix a few small cpu bugs.
+ - Improve soundblaster detection rate by various programs.
+ - Improve EMS emulation. (allow mapping of non standard regions)
+ - Improve keyboard input codes on various OS-es.
+ - Fix problems with filenames having stackdata in them.
+ - Changed a few basic operations in DOSBox so they take emulated time.
+ - Improve dos ioctl functions.
+ - Extend cpu core so they are capable of detecting and raising a few
+ more exception types.
+ - Improve DOS functions when dealing with virtual drive.
+ - Improve FAT drives.
+ - Better handling of volume-labels in file functions.
+ - Image disk cycling capability. (prompt)
+ - Try to reduce the impact of using an analog joystick.
+ - Several measures to avoid code invalidation on certain types
+ of self modification in the dynamic core.
+ - Add dynamic core memory function inlining.
+ - A few small mouse improvements. (some games are using things they
+ shouldn't)
+ - Add nullmodem emulation.(h-a-l-9000)
+ - Some small cga and hercules fixes.
+ - Add more scalers (hq2x/hq3x/sai). (Kronuz)
+ - Change configuration file loading support. It now supports
+ multiple configuration files.
+ - Make dynamic core capable of running some win32s programs.
+ - Fix and add some rare soundblaster modes. (Srecko)
+ - Better soundblaster mixer controls. (Srecko)
+ - Make soundblaster installation under windows much easier.
+ - Add device control channel handling.
+ - GEMMIS support (ems under windows).
+ - Support more colours in win 3. (vasyl)
+ - Don't show unmounted drives in windows filemanager.
+ - Fix some bugs in the int13 handler.
+ - Simulate some side-effects of bios interrupt handlers on flags.
+ - Add IPX functions needed by netbios.
+ - Make ports take emulated time.
+ - Tabcompletion is now aware of the CD command.
+ - Add suppport for the dac pel mask.
+ - Fixes to hercules emulation, better detection and bank switching.
+ - Fixes to tandy emulation, 640x200x16 mode and different sizes bank.
+ - EGA/VGA memory changes detection for faster rendering.
+ - Gus 16 bit fixes.
+ - Many timer improvements.
+ - Some pcjr fixes.
+ - Some booter fixes.
+ - Many small fixes.
+
+0.65
+ - Fixed FAT writing.
+ - Added some more missing DOS functions.
+ - Improved PIC so that it actually honours irq 2/9.
+ - Improved intelligent MPU-401 mode so that more games work with it.
+ - Some mouse fixes.
+ - Changed DMA transfers a bit so they bypass the paging tables.
+ - Added S3 XGA functionality.
+ - Improved paging so that read and write faults are handled differently.
+ - Rewrote exception handling a bit (no exception 0x0B with dos4gw anymore).
+ - Added IO exceptions in all but the dynamic core.
+ - Some ems improvements.
+ - Added midi-device selection code for the windows hosts.
+ - Fix crashes/segfaults related to the disabling of the pcspeaker.
+ - Added some more FILES=XX detection tricks.
+ - Fixed some vga detection schemes.
+ - Fixed screenshot corruption when using -noconsole in a read-only directory.
+ - Fix wrong scaled screenshots.
+ - Added some hidden file functions when using diskimages. (helps with cdrom
+ detection schemes)
+ - Fixed a bug in the mixer code, that muted the music in certain games.
+ - Added an assembly fpu core.
+ - Made the shell more flexible for batch files.
+ - Check for unaligned memory acces fixes hangups on ARM processors.
+ - Some 64 bit fixes.
+ - Added code to change configuration at runtime.
+ - Improved ADPCM emulation.
+ - Fixed a few cpu instructions.
+ - Always report vesa 2.0 and fix some colour issues with vesa games.
+ - Fix video mode 0x06 and 0x0a.
+ - Improvements to the joystick emulation. 4 buttons are supported as well.
+ - Add VCPI emulation for Origin games.
+ - Fixed a lot of things in the boot code. Most booters work now.
+ - Lots of improvements to the IPX emulation.
+ - Rewritten modem emulation. Should work with more games.
+ - Improvements to the dos memory managment routines.
+ - Add UMB (upper memory blocks) support.
+ - Emulate the pause key.
+ - Improve Composite CGA mode emulation.
+ - Lots of vga compatibility changes.
+ - Improved support for chained video modes.
+ - Improved mode and palette handling in cga modes.
+ - Mount accepts ~ now.
+ - Added a few of the EGA RIL functions.
+ - Added TandyDAC emulation.
+ - OS/2 support.
+ - Improved and speed up the dynamic cpu core.
+ - Fix some errors in the CD-ROM emulation layer.
+ - Added an automatic work-around for some graphics chipsets.
+ - Add PCjr support.
+ - Allow mousedriver to be replaced. Fixes a few games that come with their
+ own (internal) driver.
+ - Improved dynamic cpu core so it can handle pagefaults and some obscure
+ types of self-modifying code.
+ - Added -noautoexec switch to skip the contents of [autoexec] in the
+ configuration file.
+ - Improved v86 mode emulation (mainly for Strike Commander).
+ - Improved timer behavior.
+ - Improved extended keyboard support.
+ - Enhanced and added several DOS tables.
+ - Made core_full endian safe.
+ - Made pagefaults endian safe.
+ - Add support for moviecapturing
+ - Add support for 15/16/32 bit videomodes.
+ - Add some more VESA modi (4 bit).
+ - Add 1024x768 output.
+ - Changed screenrendering so it only draws changes to the screen.
+ - Allow remapping of the EMS page when the dma transfer was started from
+ the page frame
+ - Made EMS and DMA work together when playing from a mapped memory page.
+ - Renamed several configuration options, so that they are unique.
+ - Merged mpu and intelligent into one option.
+ - Merged fullfixed and fullresolution.
+ - Extended keys should be handled better.
+ - F11 and F12 work.
+ - Compilation fixes for various platforms.
+ - Fix a few crashes when giving bad input.
+ - Removed interp2x and added few new scalers.
+ - Reintroduce the lockfree mouse. (autolock=false)
+ - Add a larger cache for the dynamic cpu core.
+ - Improved soundblaster DSP, so it gets detected by creative tools.
+ - Lots of bugfixes.
+ - Even more bugfixes.
+
+0.63
+ - Fixed crash with keymapper (ctrl-f1) and output=surface.
+ - Added unmounting.
+ - Fixed multiple issues with drive labels.
+ - Fixed most if not all FILES=XX problems.
+ - Added redirection in the shell.
+ - Fixed crashes with subst.
+ - Fixed multiple crashes with the drive images support.
+ - Added a missing fpu instruction.
+ - Fixed some cpu and fpu instructions.
+ - Fixed a small bug related to font loading.
+ - Rewrote the devices support.
+ - Added capslock/numlock checks on startup.
+ - Fixed wave writing.
+ - A few internal DOS fixes.
+ - Timer fixes for the hybrid loader.
+ - Some small soundblaster fixes.
+ - The drive cache can now be cleared by a keycombo. (CTRL-F4)
+ - A few keyboard fixes.
+ - Compilation fixes on various platforms.
+ - Quite some debugger improvements.
+ - Fixed dir only showing files after the first run on cdrom drives.
+ - Added some cdrom detection checks.
+ - Enabled insert in the shell. (Easier editing of commands)
+ - Changed order in which executables appear with tab-completion.
+ - Fixed some issues with raw opl recording and using a slightly different
+ format
+
+0.62
+ - Added blinking support in the shell and some color fixes.
+ - Fixed commandline parsing when .bat files involved (fixes -exit)
+ - Fixed issues with tabs in commandline not being processed correctly.
+ - Cleaned/improved shutdown sequence.
+ - Added some more bios functions (wait and delay functions).
+ - Made our XMS driver conform the specs better. (c2woody)
+ - Added support for some more ems functions.
+ - Added intelligent mpu401 emulation. (Srecko)
+ - Added soundblaster 16 emulation.
+ - Rewrote GUS emulation to sound more authentic.
+ - Improved pc speaker emulation.
+ - Added an internal (programmable) mixer.
+ - Added support a few soundblaster/adlib detection routines.
+ - Fixed lot's of bugs related to DMA transfers.
+ - Added interpolating prebuffering mixer routines.
+ - Added recording of OPL commands and raw midi.
+ - Fixed some bugs with the wave recording.
+ - Changed sensitivity settings of the mouse.
+ - Added ps2 mouse-emulation in bios interrupts (c2woody).
+ - Fixed some bugs with mouse emulation limits.
+ - Fixed a bug with an unterminated string in the drivelabel.
+ - Changed file search routines a bit to be more compatible.
+ - Added support for attribute-searching with fcb's.
+ - Added basic SDA.
+ - Added TPA and DIB.
+ - Added Lot's of missing dos tables (c2woody).
+ - Changed psp and dta functions to use dta.
+ - Returned filename in ds:dx in create-random-file (c2woody).
+ - Fixed a bug with date and time used on open files.
+ - Some mscdex fixes.
+ - Added the -version switch, which makes dosbox report its version.
+ - Added a keymapper.
+ - Added basic IPX emulation.
+ - Added cdrom iso support and floppy images support.
+ - Added the possibity to boot another dos version.
+ - Added Serial passthrough support (win32 only).
+ - Added the possibility to pause dosbox.
+ - Changed OpenGL so that it is initialized only when used.
+ - Make dosbox run at higher priority when active and lower when inactive.
+ - Added direct draw output support (win32 only).
+ - Added current running program to title bar.
+ - Rewrote video emulation to support new scalers.
+ - Added new graphics scalers like advmame3x,tv2x.
+ - Added a support for a few anti-debugger tricks.
+ - Improved the handling of the tab-key.
+ - Improved support for the numeric keyboard.
+ - Fixed a few cpu opcodes.
+ - Added cpu core simple (for lowerend machines)
+ - Fixed some nasty bugs in the dynamic cpu core.
+ - Added a few (rarely used) fpu opcodes.
+ - Fixed various issues with GCC 3.4.
+ - Many internal timer improvements (PIT and PIC).
+ - Added some more PIC commands (c2woody).
+ - Added BCD counting to the timers.
+ - Fix some vesa functions.
+ - Add some basic support for 132x25 and 132x45 textmodes.
+ - Improved Tandy emulation a lot.
+ - Lowered cpu usage when dosbox is idle.
+ - Allow virtualisation of some basic IO-ports (c2woody).
+
+
+0.61
+ - Added a beta dynamic cpu for x86 hosts (very unstable)
+ - Added opengl and hardware overlay display output
+ - Rewrote the vga screen updates to go in lines
+ - Added paging and v86 support to cpu emulation
+ - Added a config option to simulate a certain type of machine
+ - Added hercules graphics emulation
+ - Made CGA/TANDY modes more compatible
+ - Updated textmode drawing routines to support blinking colors
+ - Fixed VESA set page function that was documented wrong
+ - Fixed some wrongly emulated cpu opcodes.
+ - improved exception handling
+ - debugger: fixes; logging of gdt,lgt,idt, new commands(Fizzban)
+ - fixed some mscdex issues (drive letter header error, added get directory entry)
+ - added/fixed some bios funcs
+ - added some rarely used xms functions (thanks c2woody!)
+ - implemented GUS emulation
+ - Added 16-bit DMA support (for GUS and eventually SB16)
+ - Fixed many small bugs in filehandling routines
+ - Many small FPU fixes (c2woody/Fizzban)
+ - Some keyboard improvements (pharlab games)
+ - Some Timer and cmos/rtc fixes (Mirek/Srecko/Others)
+ - Lot's of mouse fixes (Help from various people)
+ - Enabled internal modem
+ - Made the DOS parsing routines a bit more flexible
+ - Added Subst (Srecko)
+ - Added cdrom ioctl support for linux (prompt)
+ - Many internal DOS fixes: memory/files/datastructures.
+ - Got some help from c2woody in allowing more than 1 irq being served
+ - Disabled DPMI (not needed anymore. DOSBox handles almost every extender)
+ - Search configfile in $HOME directory if none present in current directory
+ - Added another way to switch to protected mode. (Thanks Morten Eriksen!)
+ - Fixed some odd badly documented behaviour with PSP/DTA
+ - Added some warnings on opening of readonly files in writemode(DOS default).
+ - Many shell enhanchements
+ - Fixed a win32 specific bug dealing with filenames starting with a "."
+ - Fixed some bugs with the directory structure: not found/can't save errors
+
+0.60
+ - rewrote memory system for future paging support
+ - fixed several EMS and XMS bugs and rewrite for new memory system
+ - added some support for tandy video modes
+ - added MAME Tandy 3 voice emulation
+ - added MAME CMS/GameBlaster emulation
+ - added serial port emulation with virtual tcp/ip modem (somewhat buggy)
+ - sound blaster emulation is now sb pro 2.0 compatible
+ - added basic support for 32-bit protected mode
+ - VGA now tries to emulate an S3 Trio 64 card with 2 MB
+ - VESA 2.0 support for some 256 color modes
+ - rewrote large piece of video bios code for better compatibility
+ - added support for the not inheritance flags.
+ - created functions for creating child psp.
+ - updated errorcodes of findfirst (thanks Mirek!)
+ - rewrote loggingsystem to generate less warnings
+ - added dos protected mode interface (dpmi)
+ - added cdrom label support
+ - improved cdrom audio playing
+ - fixed and improved directory cache
+ - debugger shows selector- and cpu mode info
+ - added SELINFO (selector information) command to debugger
+ - added reference counting for dos files
+ - added tab-completion
+ - added basic fpu support.
+ - fixed several bugs with case sensitive filesystems.
+ - added more shell commands and improved their behaviour.
+ - mouse improvements.
+ - real time clock improvements.
+ - DMA fixes.
+ - Improved .BAT file support.
+
+0.58
+ - fixed date and time issues with fcbs
+ - added more commands to the internal Shell
+ - corrected config system when a old configfile was used
+ - fixed cga put and get pixel
+ - fixed some vga register getting reset to wrong values
+ - improved support for foreign keyboards
+ - improved joystick support
+ - made dosbox multithreaded again
+ - lot's of soundblaster fixes
+ - dma fixes
+ - cdrom support
+ - midi support
+ - added scale2x
+ - reenabled screenshot support
+ - joystick support fixes
+ - mouse improvements
+ - support for writing wavefiles
+ - added directory cache and longfilename support (longfilenames will be mangled)
+ - mouse fixes
+ - date and time updates at z:\
+ - added (partial) direct disk support. (works probably only if directory is mounted under a:\)
+ - added support for env variables. (must be set before starting dosbox: DOSBOX_SECTION_PROPERTY=value
+ like DOSBOX_SBLASTER_IRQ=1)
+0.57
+ - added support for command /C
+ - fixed all fcb-write functions
+ - fixed fcb-parseline
+ - added debugger under linux/freebsd
+ - added debugger memory breakpoints and autolog function (heavy debug)
+ - added loadfix.com program that eats up memory (default 64kb)
+ Usage : loadfix [-option] [programname] [parameters]...
+ Example: loadfix mm2 (Allocates 64kb and starts executable mm2)
+ loadfix -32 mm2 (Allocates 32kb and starts executable mm2)
+ loadfix -128 (Allocates 128kb)
+ loadfix -f (frees all previous allocated memory)
+ - added echoing of characters for input function
+ - added support for backspace for input function
+ - added partial support for int10:01 set cursortype
+ - fixed most of the problems/bugs with character input.
+ - fixed allocationinfo call.(darksun series)
+ - improved dos support for non-existant functions
+ - Split screen support
+ - prefix 66 67 support
+ - rewrote timingscheme so 1000 hz timers don't cause problems anymore
+ - update adlib emulation
+ - fixed some isues with the mouse (double clicks and visible when it shouldn't be)
+ - improved mouse behaviour (mickey/pixel rate) and detection routines.
+ - basic ansi.sys support
+ - Disney sound system emulation
+ - rewrote upcase/lowcase functions so they work fine with gcc3.2
+ - SHELL: added rename and delete
+ - added support for command /C. Fixed crashes in the shell
+ - fixed various bugs when exiting dosbox
+ - fixed a bug in XMS
+ - fixed a bug with the joystick when pressing a button
+ - create nicer configfiles.
+ - bios_disk function improved.
+ - trapflag support
+ - improved vertical retrace timing.
+ - PIT Timer improvements and many bug fixes
+ - Many many bug fixes to the DOS subsystem
+ - Support for memory allocation strategy
+ - rewrote cpu mainloop to act more like a real cpu
+
+0.56
+ - added support for a configclass/configfile
+ - added support for writing out the configclass into a configfile
+ - removed the language file and made it internal
+ - added support for writing the language file (will override the internal one)
+ - improved mousesupport
+ - updated readme
+ - support for screenshots
+ - some cpu-bug fixes
+ - dma changes
+ - Real Sound support
+ - EMM fixes and new functions.
+ - VGA fixes
+ - new wildcompare
+ - support for size and disktype at mount.
+ - added new debugger functionalities: start/trace into INTs, write processor status log,
+ step over rep and loop instructions, breakpoint support without using INT 03 (heavy debugging switch)
+ - Added more cpu instructions and changed the string operations.
+ - Added classes for most of the internal dos structures.
+ - Rewrote most of the fcb calls to use normal dos calls.
+
+0.55
+ - fixed the errors/warnings in prefix_66.h and prefix_66_of.h (decimal too large becomming unsigned).
+ - fixed compilation error on FreeBSD when #disable_joystick was defined
+ - int10_writechar has been updated to move the cursor position.
+ - changed the basedir routines to use the current working dir instead of argv[0]. This will fix and brake things :)
+ - illegal command, now displays the command
+ - wildcmp updated to be case insensitive
+ - added fcb:open,close,findfirst, findnext.
+ - fixed rename in drive_local
+ - added new features to the debugger: breakpoint support / data view / command line
+ - partial support of list of lists (dos info block)
+ - full emm 3.2 support
+ - partial emm 4.0 support
+ - fixes to graphics core fonts (text in sierra games is now correct)
+ - improved support for user mousehandlers
+ - fixed EGA graphics
+ - fixed VGA graphics
+ - fixed write with size 0
+ - changed memory management.
+ - fixed and cleaned up the cpu flags.
+ - changed interrupt handler.
+ - speeded up the graphics.
+ - speeded up the cpu-core
+ - changed dma
+ - improved dma streams from emm memory
+ - added some cga videomodes
+ - added more funtions to the keyboard handler
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 000000000..3fa05e9d3
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,105 @@
+Things needed for compilation.
+
+SDL
+ The Simple DirectMedia Library available at http://www.libsdl.org
+ The dll distributed with the windows version of DOSBox is slightly
+ modified. You can find the changes in the sourcepackage of DOSBox
+ (src/platform/sdl-win32.diff). If you want the patched sourcetree
+ send us an email. (see README)
+ Licensed under LGPL
+ Note that only version 1.2 and its subversions (1.2.8, 1.2.13 etc.)
+ are currently supported.
+
+Curses (optional)
+ If you want to enable the debugger you need a curses library.
+ ncurses should be installed on just about every unix distro.
+ For win32 get pdcurses at http://pdcurses.sourceforge.net
+ License: Open source
+
+Libpng (optional)
+ Needed for the screenshots.
+ For win32 get libpng from http://gnuwin32.sourceforge.net/packages.html
+ See http://www.libpng.org/pub/png/ for more details.
+ License: Open Source
+
+Zlib (optional)
+ Needed by libpng.
+ For win32 get libz (rename to zlib) from http://gnuwin32.sourceforge.net/packages.html
+ See http://www.zlib.net for more details.
+ License: Open Source
+
+SDL_Net (optional)
+ For modem/ipx support. Get it from http://www.libsdl.org/projects/SDL_net/
+ Licensed under LGPL
+
+SDL_Sound
+ For compressed audio on diskimages. (optional)
+ This is for cue/bin cdrom images with compressed (mp3/ogg) audio tracks.
+ Get it from http://icculus.org/SDL_sound
+ Licenced under LGPL
+
+ALSA_Headers
+ (optional)
+ for Alsa support under linux. Part of the linux kernel sources
+ Licensed under LGPL
+
+If you want compile from developer sources (SVN) under a unix system, you'll also need
+automake (>=1.6), autoconf(>=2.50). Should be available at http://www.gnu.org
+
+For building on unix systems.
+If you are building from developer sources run ./autogen.sh first before doing the following.
+
+1. ./configure
+2. make
+
+In step 1 you could add the following switches:
+--enable-debug
+ enables the internal debugger. --enable-debug=heavy enables even more
+ debug options. DOSBox should then be run from a xterm and when the sdl-
+ window is active press alt-pause to enter the debugger.
+
+--enable-core-inline
+ enables some memory increasing inlines. This greatly increases
+ compiletime for maybe a increase in speed.
+
+--disable-fpu
+ disables the emulated fpu. Although the fpu emulation code isn't
+ finished and isn't entirely accurate it's advised to leave it on.
+
+--disable-fpu-x86
+--disable-fpu-x64
+ disables the assembly fpu core. Although relatively new, the x86/x64 fpu
+ core has more accuracy then the regular fpu core.
+
+--disable-dynamic-x86
+ disables the dynamic x86 specific cpu core. Although it might be
+ be a bit unstable, it can greatly improve the speed of dosbox on x86
+ hosts.
+ Please note that this option on x86 will result in a different
+ dynamic/recompiling cpu core being compiled then the default.
+ For more information see the option --disable-dynrec
+
+--disable-dynrec
+ disables the recompiling cpu core. Currently x86 and x86_64 only.
+ You can activate this core on x86 by disabling the dynamic-x86 core.
+
+--disable-dynamic-core
+ disables all dynamic cores. (same effect as
+ --disable-dynamic-x86 --disable-dynrec)
+
+--disable-opengl
+ disables OpenGL-support (output mode that can be selected in the
+ DOSBox configuration file).
+
+--disable-unaligned-memory
+ disables unaligned memory access.
+
+Check the src subdir for the binary.
+
+NOTE: If capslock and numlock appear to be broken. open
+src/ints/bios_keyboard.cpp and go to line 30 and read there how to fix it.
+
+
+Build instructions for VC++6
+Don't use VC++ 6: it creates faulty code in core_normal.cpp
+Later Visual Studio versions work fine (vs2003/.net up to vs2010)
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 000000000..31fcda102
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,4 @@
+# Main Makefile for DOSBox
+
+EXTRA_DIST = autogen.sh
+SUBDIRS = src include docs visualc_net
diff --git a/NEWS b/NEWS
new file mode 100644
index 000000000..bf6b7a131
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,685 @@
+0.74-2
+A maintenance release for DOSBox 0.74, which solves the following problems:
+
+- Windows: Fix auto/max cycles algorithm on Windows 7, which helps with
+ stuttering audio.
+- Mac OS X: Bring a 64 bit version and improve performance.
+- Linux: Fix the 64bit dynrec cpu core and a lot of compilation problems.
+ Add patches for the WINE Team.
+
+The game compatibility should be identical to 0.74.
+See the Changelog for a full list of changes.
+
+0.74
+ - Several small game specific fixes/hacks/support. (Offensive,
+ Roadhog, GTA installer, Kingdom O' Magic soundcard detection,
+ Pirate booter, Armored Fist installer)
+ - Add the S3-specific 640x480 256 color mode. (fixes regression in "Wooden
+ Ships and Iron Men" and "I Have No Mouth And I Must Scream")
+ - Fix a stack overflow that could crash DOSBox.
+ - Add fake microphone input. (fixes Talking Parrot)
+ - Modify adlib turn off code, so that it doesn't turn off in
+ cases where the same sound is repeated over and over again.
+ - Several small fixes to the CDROM audio code. (HOMM2, Redneck
+ Rampage and others)
+ - Several improvements to the CDROM emulation code. (fixes Alpha
+ Storm and GT Racing 97)
+ - Some small CPU fixes that might fix something.
+ - Handle opcode 0xff subcode 7 as invalid instruction. (fixes dif-2 & others)
+ - Some hercules fixes. (Testdrive)
+ - Improve support for blanked parts that wrap around to the start of
+ the screen. (fixes Magic Circle demo and Sid&Al)
+ - Remove old OPL cores as the new ones seem to work very nicely.
+ - Modify movie recording code so that the movies aren't corrupt when
+ you exit dosbox without stopping the movie.
+ - Change RGB3x scaler to look more pretty.
+ - Improve initial register values compatility of the GUS.
+ - Added autodetection for Gameblaster. (games can find it now)
+ - Change render preferences a bit to be more compatible with Windows 7.
+ - Add DOS fixes to terminate program. (fixes Fortune Teller)
+ - Add FFREEP. (fixes Trucks)
+ - Improve FPU ST80 in C mode when writing zero. (fixes Antigok)
+ - Add special int10 scanline function. (fixes mz700 and probably lots
+ of games that mess with them)
+ - Fix scrolling in rarely used video modes. (fixes Orphee)
+ - Modify game specific hacks a bit so that Kick off 3 works again.
+ - Lots of fixes to the INT10 video parameter table. (Seven spirits
+ of ra and others)
+ - Add VGA blanking in machine=vgaonly. (used by Alien Carnage)
+ - CGA, PCJr, Tandy: Add video blanking, change display start latch
+ timing, sync pulse width correction.
+ - PCJr, Tandy: implement vertical retrace interrupt.
+ - PCJr, CGA: line-by-line video emulation.
+ - PCJr: support on-screen change of color modes 4medium to 16low. (used
+ by Ghostbusters booter)
+ - Hercules: Add green and amber monochrome support.
+ - All machines: only update the video timing when needed. (Jungle Hunt,
+ others that synchronize to the video screen might profit)
+ - Several small DOS fixes.
+ - Some UMB related fixes. (The Legacy without UMB)
+ - Fix version number of DSP for SB 1.5. (fixes a few games)
+ - Several VGA emulation improvements. (Allertone football manager)
+ - Some Tandy fixes. (Mech Warrior)
+ - Small improvements and fixes to the OPL emulation.
+ - Add low level Tandy DAC emulation.
+ - Some EMS fixes. (fixes Mortal Kombat and others)
+ - Change SoundBlaster DSP reset mechanism, add sb irq acknowledge logic.
+ (fixes stmik-based applications)
+ - Some interrupt pointer location modifications. (fixes Tinker Tales)
+ - Some fixes to the BOOT code. (fixes Last Mission)
+ - Respect write-only file information. (fixes Champions of Zulala)
+ - Some RTC fix. (fixes Tully Bodine and others)
+ - Improve mouse emulation to work better with Water World.
+ - Hopefully fix the translation of the configuration file.
+ - Speed up and fixes for the recompiler core. (pitfall2 pcjr)
+ - Change memory start location. (fixes 7th Guest installer)
+ - Several fixes to the batch file handling. (Shift and
+ use the typed first %0 instead of the parsed %0)
+ - Improve file redirection and redirected line ends. (fixes
+ Phantasmagoria 2 DOS installer)
+ - Fix compilation with new MAC os X version.
+ - Add 16C550A FIFO support to the serial port emulation.
+ - Improve modem emulation to get higher speeds.
+ - Change default samplerates to 44100, blocksize to 1024 and prebuffer to 20,
+ so that hopefully certain soundcards produce more fluent sound playback.
+ - Add some rarely used, but for some games critical flags to
+ the internal commands.
+ - Add -userconf flag, so that the userspecific configuration can
+ easily be used together with -conf configfile.
+ - Improve internal timing with repeating timers (especially with
+ the dynamic core).
+
+0.73
+ - Add two new opl2+opl3 emulators. (better speed, different implementation
+ approach)
+ - Improved DRO recording/better file structure.
+ - Add EGA emulation.
+ - Add special vga machine mode. Supports more of the exotic tricks like
+ changing the palette during screen updates, 9x16 fonts etc.
+ - Added special machine modes for the following svga cards:
+ - S3
+ - Paradise
+ - Tseng
+ - Fix problems with the vga split line feature.
+ - Improve vesa emulation.
+ - Add optional selection of old vesa mode for games that don't work
+ with certain vesa features.
+ - Improve video BIOS emulation to behave more like a real bios.
+ - Fixes for emulated 4bpp graphics modes.
+ - Fixes to paging system.
+ - Various fixes and improvements for the recompiling core.
+ - Add arm backend for the recompiling core.
+ - Add some mscdex quirks when dealing with files that are exactly 8.3 long.
+ - Small fixes to batch file handling.
+ - Small fixes to the XMS memory handling.
+ - Various fixes for aligned memory on hosts that want it.
+ - Various improvements to the mouse.
+ - Fixes and small speed ups to the debugger.
+ - Fix and improve lot's of compilation problems. (curses detection,
+ GCC 3.4 and GCC 4.X fixes)
+ - Added some basic auto keyboard layout handling. (windows only currently)
+ - Add basic support for evdev keyboard driver.
+ - Various fixes to the timer. (improve mode 2 timer changes,
+ implement mode 1, improve gate2 handling)
+ - Add audio extraction and mci audio support. Should enable CDROM audio
+ for Vista and adds volume control.
+ - Improve the directory cache speed a lot, especially with mounting slow
+ media like network paths.
+ - Various fixes to the create temporary file call.
+ - Don't keep batchfiles open during execution. Allows rewriting of the
+ active batchfile. (menu programs use this trick sometimes)
+ - Fix problems with filenames with 2 extensions.
+ - Add some more lowlevel dos tables.
+ - Fixes to hercules emulation.
+ - Fix flag handling for special case of ROR.
+ - Make the batchfile handling in regard to IF more flexible.
+ - Fixes to scrolling/panning feature.
+ - Add prefetch queue emulation.
+ - Make the emulated cpu type selectable. This is mainly the
+ identification commands and the way paging works.
+ - Some special EMS functionality added. (OS handles, zero-page handling)
+ - Improve support for EMS when booting a different OS.
+ - Improve cdrom speed detection by games.
+ - Improve stability of cycle guessing code, when there is background
+ activity.
+ - Fix various mscdex and cdrom detection schemes.
+ - Added Coremidi support on Mac OS X.
+ - Improve support for DOS devices when used to detect the existance
+ of directories in various ways.
+ - Add IRQ 2 emulation on VRET. (ega only)
+ - Added video parameter table and video state functionality.
+ - Increase default freespace to 250 MB.
+ - Some fixes to the fat filesystem handling for disk images.
+ - Some soundblaster fixes and command additions.
+ - Fix mixer 16bit direct transfers on bigendian hosts.
+
+0.72
+ - Fixed unitialized variable in joystick. (Fixes crashes on Vista and
+ Mac OS X)
+ - Some bugfixes and speedups to the 64 bit recompiling core.
+ - Fixed sign flag on soundblaster dma transfers (Space Quest 6 intro)
+ - Fixed a bug in keyboard layout processing code and fixed certain
+ layouts.
+ - Fixed Dreamweb.
+ - Improved speed unlocking when running cycles=max.
+ - Fixed a crash related to the tab completion in the shell.
+ - Improved aspect correction code. Should now be like how a real monitor
+ handles it.
+ - Fixed a bug in the xms status report code. (Blake Stone 1.0 shareware)
+ - Added a lot more keyboard layouts.
+ - Fix crash related to changing the scaler before a screen was created.
+ - Hopefully fixed compilation on *bsd.
+ - Enabled auto cpu core selection for recompiling core as well.
+ - Made the used joystick selectable when 4axis is specified.
+ - Added some hints for inexperienced DOS users to the shell.
+
+0.71
+ - Add a new recompiling cpu core, which should be easier to port.
+ - Add 64 bit version of the recompiling core.
+ - Add mipsel 32 bit version of the recompiling core.
+ - Fix a few small problems with FCBs. (fixes Jewels of darkness and
+ cyrus chess)
+ - Raise some more exceptions. (fixes vbdos)
+ - Fix a few problems with the dynamic core. (fixes Inner Words,
+ Archmimedean Dynasty and others)
+ - Improve/Fix fallback code for certain graphics cards.
+ - Fix a few cd audio related bugs.
+ - Add an undocumented MSCDEX feature. (Fixes Ultimate Domain)
+ - Fix some pcspeaker mode. (fixes Test Drive and similar games)
+ - Improve dos keyinput handling. (fixes Wing Commander 3 exit dialog)
+ - Remove Exit condition on fully nested mode. (fixes some demo)
+ - Add image file size detection.
+ - Add/Fix some ansi codes. (fixes PC Larn and certain versions of
+ infocom games)
+ - Several general DOS fixes. (fixes nba95, hexit and various other games)
+ - Add some valid input checks. (fixes 3d body adventure and similar
+ games)
+ - Fix digital joystick centering problem.
+ - Reenable textmode 54 and 55.
+ - Fix a pelmask problem with univbe 5.0 lite. (fixes Panzer General)
+ - Fix minor mixer underflow.
+ - Some general image and bios disk emulation fixes.
+ - Hopefully fix compilation on BSD and darwin.
+ - Try using ioctl cdrom access by default if possible.
+ - Fix some svga detection routine. (fixes Grandest Fleet 2 and Bobby Fischer
+ Teaches Chess)
+ - You can now close DOSBox using the status window in win32.
+ - Add support for NX enabled systems.
+ - Fix a casting error which only showed with certain compilers. (fixes
+ various games under mac os x and 64 bit linux)
+ - Improve timer and add gate 2 support. (fixes various games and
+ joystick problems)
+ - Improve mouse. Add undocumented backdoor. (fixes Last half of Darkness,
+ PC-BLOX and others)
+ - Add/improve support for ~ and ~username in all commands.
+ - Fix a font problem with the pcjr/tandy. (fixes personal deskmate 2)
+ - Change dma routine a bit. (fixes ticks in sound in various games)
+ - Allow read-only diskimages to be booted. (fixes various booter
+ games)
+ - Add basic hidden file support on cdrom images. (fixes Player
+ Manager 2)
+ - Add some rarely used functionality to the int10 mode setup. (fixes
+ WW2 Battles of the South pacific)
+ - Add ability to force scaler usage.
+ - Speed up flag generation and make it more 386-like.
+ - Some colourful feedback in the mapper.
+ - General code cleanup.
+
+0.70
+ - Improve register handling and support with XMS.
+ - Fix some issues with deleting open files.(windows only issue)
+ - Add dummy LPT1 class. (windows only issue)
+ - Improve some of the internal dos commands. (choice, copy and shift)
+ - Improve ROM area. (for games that use it for random numbers or
+ overwrite it as some sort of detection thing)
+ - Improve compatibility of dynamic core by making it handle certain
+ pagefaults earlier.
+ - Move internal dos tables around so we have more umb memory.
+ - Add some dos tables.
+ - Dynamic core supports io exceptions.
+ - Move some interrupt handlers to XT Bios locations.
+ - Add a dynamic fpu on x86.
+ - Improve fpu on non-x86.
+ - Trapflag gets strict priority over hardware IRQs.
+ - Trapflag support for the dynamic core.
+ - Add dummy TRx handling.
+ - Fix a few rarely used character functions.
+ - Improve auto cycle guessing code.
+ - Improve and extend the joystick support.
+ - Add autofire support.
+ - Improve the mapper so you can map keys to the joystick and vice versa.
+ - A few game specific video card fixes.
+ - Fix some 64 bit cpu bugs.
+ - Add support for certain cdrom detection schemes.
+ - Improve HSG/Red Book support.
+ - Improve MSCDEX.
+ - Improve dynamic core support under intel macs.
+ - Add basic support for clipper programs.
+ - Add support for different keyboard layouts.
+ - Add auto core guessing.
+ - Fix a few flags bugs.
+ - Fix a few small cpu bugs.
+ - Improve soundblaster detection rate by various programs.
+ - Improve EMS emulation. (allow mapping of non standard regions)
+ - Improve keyboard input codes on various OS-es.
+ - Fix problems with filenames having stackdata in them.
+ - Changed a few basic operations in DOSBox so they take emulated time.
+ - Improve dos ioctl functions.
+ - Extend cpu core so they are capable of detecting and raising a few
+ more exception types.
+ - Improve DOS functions when dealing with virtual drive.
+ - Improve FAT drives.
+ - Better handling of volume-labels in file functions.
+ - Image disk cycling capability. (prompt)
+ - Try to reduce the impact of using an analog joystick.
+ - Several measures to avoid code invalidation on certain types
+ of self modification in the dynamic core.
+ - Add dynamic core memory function inlining.
+ - A few small mouse improvements. (some games are using things they
+ shouldn't)
+ - Add nullmodem emulation.(h-a-l-9000)
+ - Some small cga and hercules fixes.
+ - Add more scalers (hq2x/hq3x/sai). (Kronuz)
+ - Change configuration file loading support. It now supports
+ multiple configuration files.
+ - Make dynamic core capable of running some win32s programs.
+ - Fix and add some rare soundblaster modes. (Srecko)
+ - Better soundblaster mixer controls. (Srecko)
+ - Make soundblaster installation under windows much easier.
+ - Add device control channel handling.
+ - GEMMIS support (ems under windows).
+ - Support more colours in win 3. (vasyl)
+ - Don't show unmounted drives in windows filemanager.
+ - Fix some bugs in the int13 handler.
+ - Simulate some side-effects of bios interrupt handlers on flags.
+ - Add IPX functions needed by netbios.
+ - Make ports take emulated time.
+ - Tabcompletion is now aware of the CD command.
+ - Add suppport for the dac pel mask.
+ - Fixes to hercules emulation, better detection and bank switching.
+ - Fixes to tandy emulation, 640x200x16 mode and different sizes bank.
+ - EGA/VGA memory changes detection for faster rendering.
+ - Gus 16 bit fixes.
+ - Many timer improvements.
+ - Some pcjr fixes.
+ - Some booter fixes.
+ - Many small fixes.
+
+0.65
+ - Fixed FAT writing.
+ - Added some more missing DOS functions.
+ - Improved PIC so that it actually honours irq 2/9.
+ - Improved intelligent MPU-401 mode so that more games work with it.
+ - Some mouse fixes.
+ - Changed DMA transfers a bit so they bypass the paging tables.
+ - Added S3 XGA functionality.
+ - Improved paging so that read and write faults are handled differently.
+ - Rewrote exception handling a bit (no exception 0x0B with dos4gw anymore).
+ - Added IO exceptions in all but the dynamic core.
+ - Some ems improvements.
+ - Added midi-device selection code for the windows hosts.
+ - Fix crashes/segfaults related to the disabling of the pcspeaker.
+ - Added some more FILES=XX detection tricks.
+ - Fixed some vga detection schemes.
+ - Fixed screenshot corruption when using -noconsole in a read-only directory.
+ - Fix wrong scaled screenshots.
+ - Added some hidden file functions when using diskimages. (helps with cdrom
+ detection schemes)
+ - Fixed a bug in the mixer code, that muted the music in certain games.
+ - Added an assembly fpu core.
+ - Made the shell more flexible for batch files.
+ - Check for unaligned memory acces fixes hangups on ARM processors.
+ - Some 64 bit fixes.
+ - Added code to change configuration at runtime.
+ - Improved ADPCM emulation.
+ - Fixed a few cpu instructions.
+ - Always report vesa 2.0 and fix some colour issues with vesa games.
+ - Fix video mode 0x06 and 0x0a.
+ - Improvements to the joystick emulation. 4 buttons are supported as well.
+ - Add VCPI emulation for Origin games.
+ - Fixed a lot of things in the boot code. Most booters work now.
+ - Lots of improvements to the IPX emulation.
+ - Rewritten modem emulation. Should work with more games.
+ - Improvements to the dos memory managment routines.
+ - Add UMB (upper memory blocks) support.
+ - Emulate the pause key.
+ - Improve Composite CGA mode emulation.
+ - Lots of vga compatibility changes.
+ - Improved support for chained video modes.
+ - Improved mode and palette handling in cga modes.
+ - Mount accepts ~ now.
+ - Added a few of the EGA RIL functions.
+ - Added TandyDAC emulation.
+ - OS/2 support.
+ - Improved and speed up the dynamic cpu core.
+ - Fix some errors in the CD-ROM emulation layer.
+ - Added an automatic work-around for some graphics chipsets.
+ - Add PCjr support.
+ - Allow mousedriver to be replaced. Fixes a few games that come with their
+ own (internal) driver.
+ - Improved dynamic cpu core so it can handle pagefaults and some obscure
+ types of self-modifying code.
+ - Added -noautoexec switch to skip the contents of [autoexec] in the
+ configuration file.
+ - Improved v86 mode emulation (mainly for Strike Commander).
+ - Improved timer behavior.
+ - Improved extended keyboard support.
+ - Enhanced and added several DOS tables.
+ - Made core_full endian safe.
+ - Made pagefaults endian safe.
+ - Add support for moviecapturing
+ - Add support for 15/16/32 bit videomodes.
+ - Add some more VESA modi (4 bit).
+ - Add 1024x768 output.
+ - Changed screenrendering so it only draws changes to the screen.
+ - Allow remapping of the EMS page when the dma transfer was started from
+ the page frame
+ - Made EMS and DMA work together when playing from a mapped memory page.
+ - Renamed several configuration options, so that they are unique.
+ - Merged mpu and intelligent into one option.
+ - Merged fullfixed and fullresolution.
+ - Extended keys should be handled better.
+ - F11 and F12 work.
+ - Compilation fixes for various platforms.
+ - Fix a few crashes when giving bad input.
+ - Removed interp2x and added few new scalers.
+ - Reintroduce the lockfree mouse. (autolock=false)
+ - Add a larger cache for the dynamic cpu core.
+ - Improved soundblaster DSP, so it gets detected by creative tools.
+ - Lots of bugfixes.
+ - Even more bugfixes.
+
+0.63
+ - Fixed crash with keymapper (ctrl-f1) and output=surface.
+ - Added unmounting.
+ - Fixed multiple issues with drive labels.
+ - Fixed most if not all FILES=XX problems.
+ - Added redirection in the shell.
+ - Fixed crashes with subst.
+ - Fixed multiple crashes with the drive images support.
+ - Added a missing fpu instruction.
+ - Fixed some cpu and fpu instructions.
+ - Fixed a small bug related to font loading.
+ - Rewrote the devices support.
+ - Added capslock/numlock checks on startup.
+ - Fixed wave writing.
+ - A few internal DOS fixes.
+ - Timer fixes for the hybrid loader.
+ - Some small soundblaster fixes.
+ - The drive cache can now be cleared by a keycombo. (CTRL-F4)
+ - A few keyboard fixes.
+ - Compilation fixes on various platforms.
+ - Quite some debugger improvements.
+ - Fixed dir only showing files after the first run on cdrom drives.
+ - Added some cdrom detection checks.
+ - Enabled insert in the shell. (Easier editing of commands)
+ - Changed order in which executables appear with tab-completion.
+ - Fixed some issues with raw opl recording and using a slightly different
+ format
+
+0.62
+ - Added blinking support in the shell and some color fixes.
+ - Fixed commandline parsing when .bat files involved (fixes -exit)
+ - Fixed issues with tabs in commandline not being processed correctly.
+ - Cleaned/improved shutdown sequence.
+ - Added some more bios functions (wait and delay functions).
+ - Made our XMS driver conform the specs better. (c2woody)
+ - Added support for some more ems functions.
+ - Added intelligent mpu401 emulation. (Srecko)
+ - Added soundblaster 16 emulation.
+ - Rewrote GUS emulation to sound more authentic.
+ - Improved pc speaker emulation.
+ - Added an internal (programmable) mixer.
+ - Added support a few soundblaster/adlib detection routines.
+ - Fixed lot's of bugs related to DMA transfers.
+ - Added interpolating prebuffering mixer routines.
+ - Added recording of OPL commands and raw midi.
+ - Fixed some bugs with the wave recording.
+ - Changed sensitivity settings of the mouse.
+ - Added ps2 mouse-emulation in bios interrupts (c2woody).
+ - Fixed some bugs with mouse emulation limits.
+ - Fixed a bug with an unterminated string in the drivelabel.
+ - Changed file search routines a bit to be more compatible.
+ - Added support for attribute-searching with fcb's.
+ - Added basic SDA.
+ - Added TPA and DIB.
+ - Added Lot's of missing dos tables (c2woody).
+ - Changed psp and dta functions to use dta.
+ - Returned filename in ds:dx in create-random-file (c2woody).
+ - Fixed a bug with date and time used on open files.
+ - Some mscdex fixes.
+ - Added the -version switch, which makes dosbox report its version.
+ - Added a keymapper.
+ - Added basic IPX emulation.
+ - Added cdrom iso support and floppy images support.
+ - Added the possibity to boot another dos version.
+ - Added Serial passthrough support (win32 only).
+ - Added the possibility to pause dosbox.
+ - Changed OpenGL so that it is initialized only when used.
+ - Make dosbox run at higher priority when active and lower when inactive.
+ - Added direct draw output support (win32 only).
+ - Added current running program to title bar.
+ - Rewrote video emulation to support new scalers.
+ - Added new graphics scalers like advmame3x,tv2x.
+ - Added a support for a few anti-debugger tricks.
+ - Improved the handling of the tab-key.
+ - Improved support for the numeric keyboard.
+ - Fixed a few cpu opcodes.
+ - Added cpu core simple (for lowerend machines)
+ - Fixed some nasty bugs in the dynamic cpu core.
+ - Added a few (rarely used) fpu opcodes.
+ - Fixed various issues with GCC 3.4.
+ - Many internal timer improvements (PIT and PIC).
+ - Added some more PIC commands (c2woody).
+ - Added BCD counting to the timers.
+ - Fix some vesa functions.
+ - Add some basic support for 132x25 and 132x45 textmodes.
+ - Improved Tandy emulation a lot.
+ - Lowered cpu usage when dosbox is idle.
+ - Allow virtualisation of some basic IO-ports (c2woody).
+
+0.61
+ - Added a beta dynamic cpu for x86 hosts (very unstable)
+ - Added opengl and hardware overlay display output
+ - Rewrote the vga screen updates to go in lines
+ - Added paging and v86 support to cpu emulation
+ - Added a config option to simulate a certain type of machine
+ - Added hercules graphics emulation
+ - Made CGA/TANDY modes more compatible
+ - Updated textmode drawing routines to support blinking colors
+ - Fixed VESA set page function that was documented wrong
+ - Fixed some wrongly emulated cpu opcodes.
+ - improved exception handling
+ - debugger: fixes; logging of gdt,lgt,idt, new commands(Fizzban)
+ - fixed some mscdex issues (drive letter header error, added get directory entry)
+ - added/fixed some bios funcs
+ - added some rarely used xms functions (thanks c2woody!)
+ - implemented GUS emulation
+ - Added 16-bit DMA support (for GUS and eventually SB16)
+ - Fixed many small bugs in filehandling routines
+ - Many small FPU fixes (c2woody/Fizzban)
+ - Some keyboard improvements (pharlab games)
+ - Some Timer and cmos/rtc fixes (Mirek/Srecko/Others)
+ - Lot's of mouse fixes (Help from various people)
+ - Enabled internal modem
+ - Made the DOS parsing routines a bit more flexible
+ - Added Subst (Srecko)
+ - Added cdrom ioctl support for linux (prompt)
+ - Many internal DOS fixes: memory/files/datastructures.
+ - Got some help from c2woody in allowing more than 1 irq being served
+ - Disabled DPMI (not needed anymore. DOSBox handles almost every extender)
+ - Search configfile in $HOME directory if none present in current directory
+ - Added another way to switch to protected mode. (Thanks Morten Eriksen!)
+ - Fixed some odd badly documented behaviour with PSP/DTA
+ - Added some warnings on opening of readonly files in writemode(DOS default).
+ - Many shell enhanchements
+ - Fixed a win32 specific bug dealing with filenames starting with a "."
+ - Fixed some bugs with the directory structure: not found/can't save errors
+
+0.60
+ - rewrote memory system for future paging support
+ - fixed several EMS and XMS bugs and rewrite for new memory system
+ - added some support for tandy video modes
+ - added MAME Tandy 3 voice emulation
+ - added MAME CMS/GameBlaster emulation
+ - added serial port emulation with virtual tcp/ip modem (somewhat buggy)
+ - sound blaster emulation is now sb pro 2.0 compatible
+ - added basic support for 32-bit protected mode
+ - VGA now tries to emulate an S3 Trio 64 card with 2 MB
+ - VESA 2.0 support for some 256 color modes
+ - rewrote large piece of video bios code for better compatibility
+ - added support for the not inheritance flags.
+ - created functions for creating child psp.
+ - updated errorcodes of findfirst (thanks Mirek!)
+ - rewrote loggingsystem to generate less warnings
+ - added dos protected mode interface (dpmi)
+ - added cdrom label support
+ - improved cdrom audio playing
+ - fixed and improved directory cache
+ - debugger shows selector- and cpu mode info
+ - added SELINFO (selector information) command to debugger
+ - added reference counting for dos files
+ - added tab-completion
+ - added basic fpu support.
+ - fixed several bugs with case sensitive filesystems.
+ - added more shell commands and improved their behaviour.
+ - mouse improvements.
+ - real time clock improvements.
+ - DMA fixes.
+ - Improved .BAT file support.
+
+0.58
+ - fixed date and time issues with fcbs
+ - added more commands to the internal Shell
+ - corrected config system when a old configfile was used
+ - fixed cga put and get pixel
+ - fixed some vga register getting reset to wrong values
+ - improved support for foreign keyboards
+ - improved joystick support
+ - made dosbox multithreaded again
+ - lot's of soundblaster fixes
+ - dma fixes
+ - cdrom support
+ - midi support
+ - added scale2x
+ - reenabled screenshot support
+ - joystick support fixes
+ - mouse improvements
+ - support for writing wavefiles
+ - added directory cache and longfilename support (longfilenames will be mangled)
+ - mouse fixes
+
+
+0.57
+ - added support for command /C
+ - fixed all fcb-write functions
+ - fixed fcb-parseline
+ - added debugger under linux/freebsd
+ - added debugger memory breakpoints and autolog function (heavy debug)
+ - added loadfix.com program that eats up memory (default 64kb)
+ Usage : loadfix [-option] [programname] [parameters]...
+ Example: loadfix mm2 (Allocates 64kb and starts executable mm2)
+ loadfix -32 mm2 (Allocates 32kb and starts executable mm2)
+ loadfix -128 (Allocates 128kb)
+ loadfix -f (frees all previous allocated memory)
+ - added echoing of characters for input function
+ - added support for backspace for input function
+ - added partial support for int10:01 set cursortype
+ - fixed most of the problems/bugs with character input.
+ - fixed allocationinfo call.(darksun series)
+ - improved dos support for non-existant functions
+ - Split screen support
+ - prefix 66 67 support
+ - rewrote timingscheme so 1000 hz timers don't cause problems anymore
+ - update adlib emulation
+ - fixed some isues with the mouse (double clicks and visible when it shouldn't be)
+ - improved mouse behaviour (mickey/pixel rate) and detection routines.
+ - basic ansi.sys support
+ - Disney sound system emulation
+ - rewrote upcase/lowcase functions so they work fine with gcc3.2
+ - SHELL: added rename and delete
+ - added support for command /C. Fixed crashes in the shell
+ - fixed various bugs when exiting dosbox
+ - fixed a bug in XMS
+ - fixed a bug with the joystick when pressing a button
+ - create nicer configfiles.
+ - bios_disk function improved.
+ - trapflag support
+ - improved vertical retrace timing.
+ - PIT Timer improvements and many bug fixes
+ - Many many bug fixes to the DOS subsystem
+ - Support for memory allocation strategy
+ - rewrote cpu mainloop to act more like a real cpu
+
+0.56
+ - added support for a configclass/configfile
+ - added support for writing out the configclass into a configfile
+ - removed the language file and made it internal
+ - added support for writing the language file (will override the internal one)
+ - improved mousesupport
+ - updated readme
+ - support for screenshots
+ - some cpu-bug fixes
+ - dma changes
+ - Real Sound support
+ - EMM fixes and new functions.
+ - VGA fixes
+ - new wildcompare
+ - support for size and disktype at mount.
+ - added new debugger functionalities: start/trace into INTs, write processor status log,
+ step over rep and loop instructions, breakpoint support without using INT 03 (heavy debugging switch)
+ - Added more cpu instructions and changed the string operations.
+ - Added classes for most of the internal dos structures.
+ - Rewrote most of the fcb calls to use normal dos calls.
+
+0.55
+ - fixed the errors/warnings in prefix_66.h and prefix_66_of.h (decimal too large becomming unsigned).
+ - fixed compilation error on FreeBSD when #disable_joystick was defined
+ - int10_writechar has been updated to move the cursor position.
+ - changed the basedir routines to use the current working dir instead of argv[0]. This will fix and brake things :)
+ - illegal command, now displays the command
+ - wildcmp updated to be case insensitive
+ - added fcb:open,close,findfirst, findnext.
+ - fixed rename in drive_local
+ - added new features to the debugger: breakpoint support / data view / command line
+ - partial support of list of lists (dos info block)
+ - full emm 3.2 support
+ - partial emm 4.0 support
+ - fixes to graphics core fonts (text in sierra games is now correct)
+ - improved support for user mousehandlers
+ - fixed EGA graphics
+ - fixed VGA graphics
+ - fixed write with size 0
+ - changed memory management.
+ - fixed and cleaned up the cpu flags.
+ - changed interrupt handler.
+ - speeded up the graphics.
+ - speeded up the cpu-core
+ - changed dma
+ - improved dma streams from emm memory
+ - added some cga videomodes
+ - added more funtions to the keyboard handler
+
+0.50:
+ -added F3 to repeat the last typed command.
+ -made it possible to change the shellmessages(dosshell). so
+ you can costumize it.(dosbox.lang)
+ -changed cpu core.
+ -Fixed a lot of errors with the keyboard: shift-f1 and
+ alt-f1 now works.
+ -Fixed some division errors.
+ -made a plugin system.
+ -added a lot of real 386 mode instructions.
+ -made it possible to resize the screen.
+ -Mayor source cleanup/reorganisation.
+ -Complete rewrite of the graphics routines. Should make it
+ possible to implement more fancy things like 2xsai,interpolation.
+ -changed the sound playback.
+ -Changed the vga drawing to only draw on memory changes, instead
+ of drawing an entire frame.
+ -fixes to the soundblaster/dma code should be able to play 4-bit
+ adpcm compressed sounds.
+ -added the correct time to dir.
+ -bugfixes to batch-file handling.
+ -Lot's of small bugfixes.(Dune1&2,wolf3d, many more).
+ -Released the source.
diff --git a/README b/README
new file mode 100644
index 000000000..5323229fc
--- /dev/null
+++ b/README
@@ -0,0 +1,1577 @@
+DOSBox v0.74-2 Manual (always use the latest version from www.dosbox.com)
+
+
+
+=====
+NOTE:
+=====
+
+While we are hoping that one day DOSBox will run all programs ever made for
+the PC, we are not there yet.
+At present, DOSBox running on a high-end machine will roughly be the equivalent
+of a Pentium I PC. DOSBox can be configured to run a wide range of DOS games,
+from CGA/Tandy/PCjr classics up to games from the Quake era.
+
+
+
+======
+INDEX:
+======
+
+1. Quickstart
+2. Start (FAQ)
+3. Command Line Parameters
+4. Internal Programs
+5. Special Keys
+6. Joystick/Gamepad
+7. KeyMapper
+8. Keyboard Layout
+9. Serial Multiplayer feature
+10. How to speed up/slow down DOSBox
+11. Troubleshooting
+12. DOSBox Status Window
+13. The configuration (options) file
+14. The language file
+15. Building your own version of DOSBox
+16. Special thanks
+17. Contact
+
+
+
+==============
+1. Quickstart:
+==============
+
+Type INTRO in DOSBox for a quick tour.
+It is essential that you get familiar with the idea of mounting, DOSBox does not
+automatically make any drive (or a part of it) accessible to the emulation. See
+the FAQ entry "How to start?" as well as the description of the MOUNT command
+(Section 4: "Internal Programs"). If you have your game on a cdrom you may try
+this guide: https://www.vogons.org/viewtopic.php?t=8933
+
+
+
+===============
+2. Start (FAQ):
+===============
+
+START: How to start?
+AUTOMATION: Do I always have to type these "mount" commands?
+FULLSCREEN: How do I change to fullscreen?
+FULLSCREEN: My fullscreen is too large.
+CD-ROM: My CD-ROM doesn't work.
+CD-ROM: The game/application can't find its CD-ROM.
+MOUSE: The mouse doesn't work.
+SOUND: There is no sound.
+SOUND: What sound hardware does DOSBox presently emulate?
+SOUND: The sound stutters or sounds stretched/weird.
+KEYBOARD: I can't type \ or : in DOSBox.
+KEYBOARD: Right Shift and "\" doesn't work in DOSBox. (Windows only)
+KEYBOARD: The keyboard lags.
+CONTROL: The character/cursor/mouse pointer always moves into one direction!
+SPEED: The game/application runs much too slow/too fast!
+CRASH: The game/application does not run at all/crashes!
+CRASH: DOSBox crashes on startup!
+GAME: My Build game(Duke3D/Blood/Shadow Warrior) has problems.
+SAFETY: Can DOSBox harm my computer?
+OPTIONS: I would like to change DOSBox's options.
+HELP: Great Manual, but I still don't get it.
+
+
+
+START: How to start?
+ At the beginning you've got a Z:\> instead of a C:\> at the prompt.
+ You have to make your directories available as drives in DOSBox by using
+ the "mount" command. For example, in Windows "mount C D:\GAMES" will give
+ you a C drive in DOSBox which points to your Windows D:\GAMES directory
+ (that was created before). In Linux, "mount c /home/username" will give you
+ a C drive in DOSBox which points to /home/username in Linux.
+ To change to the drive mounted like above, type "C:". If everything went
+ fine, DOSBox will display the prompt "C:\>".
+
+
+AUTOMATION: Do I always have to type these commands?
+ In the DOSBox configuration file is an [autoexec] section. The commands
+ present there are run when DOSBox starts, so you can use this section
+ for the mounting. Look at Section 13: "The configuration (options) file".
+
+
+FULLSCREEN: How do I change to fullscreen?
+ Press alt-enter. Alternatively: Edit the configuration file of DOSBox and
+ change the option fullscreen=false to fullscreen=true. If fullscreen looks
+ wrong in your opinion: Play with the options: fullresolution, output and
+ aspect in the configuration file of DOSBox. To get back from fullscreen
+ mode: Press alt-enter again.
+
+
+FULLSCREEN: My fullscreen is too large.
+ This is can be a problem on Windows 10, if you have display scaling
+ set to a value above 100%. Windows in that case will resize the screen
+ on top of dosbox resizing the screen, which can happen for the output:
+ ddraw, opengl, openglnb, overlay. You can disable this Windows behaviour
+ by enabling a specific compatibility setting:
+
+ - Right-click the DOSBox icon and select "Properties".
+ - Go to the "Compatibility" tab.
+ - Click on "Change high DPI settings".
+ - Tick "Override high DPI scaling behaviour" and set it to "Application".
+ - Apply the changes by clicking on "OK".
+
+ Unfortunately, this compatibility option causes some side effects in
+ windowed mode, and in this case you will need to change the resolution
+ in the config/Options file for windowresolution (e.g. 1024x768).
+
+ Alternatively, you can disable the display scaling and or use a lower
+ fullresolution value.
+
+CD-ROM: My CD-ROM doesn't work.
+ To mount your CD-ROM in DOSBox you have to specify some additional options
+ when mounting the CD-ROM.
+ To enable CD-ROM support (includes MSCDEX) in Windows:
+ - mount d f:\ -t cdrom
+ in Linux:
+ - mount d /media/cdrom -t cdrom
+
+ In some cases you might want to use a different CD-ROM interface,
+ for example if CD audio does not work:
+ To enable SDL-support (does not include low-level CD access!):
+ - mount d f:\ -t cdrom -usecd 0 -noioctl
+ To enable ioctl access using digital audio extraction for CD audio
+ (Windows-only, useful for Vista):
+ - mount d f:\ -t cdrom -ioctl_dx
+ To enable ioctl access using MCI for CD audio (Windows-only):
+ - mount d f:\ -t cdrom -ioctl_mci
+ To force ioctl-only access (Windows-only):
+ - mount d f:\ -t cdrom -ioctl_dio
+ To enable low-level aspi-support (win98 with aspi-layer installed):
+ - mount d f:\ -t cdrom -aspi
+
+ explanation: - d driveletter you will get in DOSBox (d is the best,
+ don't change it!)
+ - f:\ location of CD-ROM on your PC. In most cases it will
+ be d:\ or e:\
+ - 0 The number of the CD-ROM drive, reported by "mount -cd"
+ (note that this value is only needed when using SDL
+ for CD audio, otherwise it is ignored)
+ See also the next question: The game/application can't find its CD-ROM.
+
+
+CD-ROM: The game/application can't find its CD-ROM.
+ Be sure to mount the CD-ROM with -t cdrom switch, this will enable the
+ MSCDEX interface required by DOS games to interface with CD-ROMs.
+ Also try adding the correct label (-label LABEL) to the mount command,
+ where LABEL is the CD-label (volume ID) of the CD-ROM.
+ Under Windows you can specify -ioctl, -aspi or -noioctl. Look at the
+ description of the mount command in Section 4: "Internal programs"
+ for their meaning and the
+ additional audio-CD related options -ioctl_dx, -ioctl_mci, -ioctl_dio.
+
+ Try creating a CD-ROM image (preferably CUE/BIN pair) and use the
+ DOSBox's internal IMGMOUNT tool to mount the image (the CUE sheet).
+ This enables very good low-level CD-ROM support on any operating system.
+
+
+MOUSE: The mouse doesn't work.
+ Usually, DOSBox detects when a game uses mouse control. When you click on
+ the screen it should get locked (confined to the DOSBox window) and work.
+ With certain games, the DOSBox mouse detection doesn't work. In that case
+ you will have to lock the mouse manually by pressing CTRL-F10.
+
+
+SOUND: There is no sound.
+ Be sure that the sound is correctly configured in the game. This might be
+ done during the installation or with a setup/setsound utility that
+ accompanies the game. First see if an autodetection option is provided. If
+ there is none try selecting SoundBlaster or SoundBlaster 16 with the default
+ settings being "address=220 irq=7 dma=1" (sometimes highdma=5). You might
+ also want to select Sound Canvas/SCC/MPU-401/General MIDI/Wave Blaster
+ at "address=330 IRQ=2" as music device.
+ The parameters of the emulated sound cards can be changed in the DOSBox
+ configuration file.
+ If you still don't get any sound set the core to normal in DOSBox
+ configuration and use some lower fixed cycles value (like cycles=2000). Also
+ assure that your host operating sound does provide sound.
+ In certain cases it might be useful to use a different emulated sound device
+ like a SoundBlaster Pro (sbtype=sbpro1 in the DOSBox configuration file) or
+ the Gravis Ultrasound (gus=true).
+
+
+SOUND: What sound hardware does DOSBox presently emulate?
+ DOSBox emulates several legacy sound devices:
+ - Internal PC speaker/Buzzer
+ This emulation includes both the tone generator and several forms of
+ digital sound output through the internal speaker.
+ - Creative CMS/Gameblaster
+ The is the first card released by Creative Labs(R). The default
+ configuration places it on address 220. It is disabled by default.
+ - Tandy 3 voice
+ The emulation of this sound hardware is complete with the exception of
+ the noise channel. The noise channel is not very well documented and as
+ such is only a best guess as to the sound's accuracy. It is disabled as
+ default.
+ - Tandy DAC
+ Some games may require turning off SoundBlaster emulation (sbtype=none)
+ for better Tandy DAC sound support. Don't forget to set the sbtype back to
+ sb16 if you don't use Tandy sound.
+ - Adlib
+ This emulation is almost perfect and includes the Adlib's ability to
+ almost play digitized sound. Placed at address 220 (also on 388).
+ - SoundBlaster 16 / SoundBlaster Pro I & II / SoundBlaster I & II
+ By default DOSBox provides SoundBlaster 16 level 16-bit stereo sound.
+ You can select a different SoundBlaster version in the configuration of
+ DOSBox. AWE32 music is not emulated as you can use MPU-401 instead
+ (see below).
+ - Disney Sound Source and Covox Speech Thing
+ Using the printer port, this sound device outputs digital sound only.
+ Placed at LPT1
+ - Gravis Ultrasound
+ The emulation of this hardware is nearly complete, though the MIDI
+ capabilities have been left out, since an MPU-401 has been emulated
+ in other code. For Gravis music you also have to install Gravis drivers
+ inside DOSBox. It is disabled by default.
+ - MPU-401
+ A MIDI passthrough interface is also emulated. This method of sound
+ output will only work when used with external device/emulator.
+ Every Windows XP/Vista/7 and Mac OS X has got a default emulator
+ compatible with: Sound Canvas/SCC/General Standard/General MIDI/Wave
+ Blaster. A different device/emulator is needed for
+ Roland LAPC/CM-32L/MT-32 compatibility.
+
+
+SOUND: The sound stutters or sounds stretched/weird.
+ You may be using too much CPU power to keep DOSBox running at the current
+ speed. You can lower the cycles, skip frames, reduce the sampling rate of
+ the respective sound device, increase the prebuffer. See Section 13: "The
+ configuration (options) file".
+ If you are using 'cycles=max' or 'cycles=auto', then make sure that there is
+ no background processes interfering! (especially if they access the harddisk)
+ Also look at Section 10: "How to speed up/slow down DOSBox" as well as
+ Section 11: "Troubleshooting".
+
+
+KEYBOARD: I can't type \ or : in DOSBox.
+ This can happen in various cases, like your host keyboard layout does not
+ have a matching DOS layout representation (or it was not correctly
+ detected), or the key mapping is wrong.
+ Some possible fixes:
+ 1. Use / instead, or ALT-58 for : and ALT-92 for \.
+ 2. Change the DOS keyboard layout (see Section 8: "Keyboard Layout").
+ 3. Add the commands you want to execute to the [autoexec] section
+ of the DOSBox configuration file.
+ 4. Open the DOSBox configuration file and change the usescancodes entry.
+ 5. Switch the keyboard layout of your operating system.
+
+ Note that if the host layout can not be identified, or keyboardlayout is
+ set to none in the DOSBox configuration file, the standard US layout is
+ used. In this configuration try the keys around "enter" for the key \
+ (backslash), and for the key : (colon) use shift and the keys between
+ "enter" and "L".
+
+
+KEYBOARD: Right Shift and "\" doesn't work in DOSBox. (Windows only)
+ This may happen if Windows thinks that you have more than one keyboard
+ connected to your PC when you use some remote control devices.
+ To verity this problem run cmd.exe, navigate to DOSBox program folder
+ and type:
+ set sdl_videodriver=windib
+ dosbox.exe
+ check whether keyboard started to work properly. As windib is slower it is
+ best to use one of the two solutions provided here:
+ https://www.vogons.org/viewtopic.php?t=24072
+
+
+KEYBOARD: The keyboard lags.
+ Lower the priority setting in the DOSBox configuration file, for example
+ set "priority=normal,normal". You might also want to try lowering the
+ cycles (use a fixed cycle amount to start with, like cycles=10000).
+
+
+CONTROL: The character/cursor/mouse pointer always moves into one direction!
+ See if it still happens if you disable the joystick emulation,
+ set joysticktype=none in the [joystick] section of your DOSBox
+ configuration file. Maybe also try unplugging any joystick/gamepad.
+ If you want to use the joystick in the game, try setting timed=false
+ and be sure to calibrate the joystick (both in your OS as well as
+ in the game or the game's setup program).
+
+
+SPEED: The game/application runs much too slow/too fast!
+ Look at Section 10: "How to speed up/slow down DOSBox" for more
+ information.
+
+
+CRASH: The game/application does not run at all/crashes!
+ Look at Section 11: "Troubleshooting".
+
+
+CRASH: DOSBox crashes on startup!
+ Look at Section 11: "Troubleshooting".
+
+
+GAME: My Build game(Duke3D/Blood/Shadow Warrior) has problems.
+ First of all, try to find a port of the game. Those will offer a better
+ experience. To fix the graphics problem that occurs in DOSBox on higher
+ resolutions: Open the configuration file of DOSBox and search for
+ machine=svga_s3. Change svga_s3 to vesa_nolfb
+ Change memsize=16 to memsize=63
+
+
+SAFETY: Can DOSBox harm my computer?
+ DOSBox can not harm your computer more than any other resource demanding
+ program. Increasing the cycles does not overclock your real CPU.
+ Setting the cycles too high has a negative performance effect on the
+ software running inside DOSBox.
+
+
+OPTIONS: I would like to change DOSBox's options.
+ Look at Section 13: "The configuration (options) file".
+
+
+HELP: Great Manual, but I still don't get it.
+ For more questions read the rest of this Manual. You may also look at:
+ guides located at https://www.vogons.org/viewforum.php?f=53
+ the wiki of DOSBox https://www.dosbox.com/wiki/
+ the site/forum: https://www.dosbox.com
+
+
+
+===========================
+3. Command Line Parameters:
+===========================
+
+An overview of the command line options you can give to DOSBox. Although
+in most cases it is easier to use DOSBox's configuration file instead.
+See Section 13: "The configuration (options) file".
+
+To be able to use Command Line Parameters:
+(Windows) open cmd.exe or command.com or edit the shortcut to dosbox.exe
+(Linux) use console
+(Mac OS X) start terminal.app and navigate to:
+ /applications/dosbox.app/contents/macos/dosbox
+
+The options are valid for all operating systems unless noted in the option
+description:
+
+dosbox [name] [-exit] [-c command] [-fullscreen] [-userconf]
+ [-conf congfigfilelocation] [-lang languagefilelocation]
+ [-machine machine type] [-noconsole] [-startmapper] [-noautoexec]
+ [-securemode] [-scaler scaler | -forcescaler scaler] [-version]
+ [-socket socket]
+
+dosbox -version
+dosbox -editconf program
+dosbox -opencaptures program
+dosbox -printconf
+dosbox -eraseconf
+dosbox -erasemapper
+
+ name
+ If "name" is a directory it will mount that as the C: drive.
+ If "name" is an executable it will mount the directory of "name"
+ as the C: drive and execute "name".
+
+ -exit
+ DOSBox will close itself when the DOS application "name" ends.
+
+ -c command
+ Runs the specified command before running "name". Multiple commands
+ can be specified. Each command should start with "-c" though.
+ A command can be: an Internal Program, a DOS command or an executable
+ on a mounted drive.
+
+ -fullscreen
+ Starts DOSBox in fullscreen mode.
+
+ -userconf
+ Start DOSBox with the users specific configuration file. Can be used
+ together with multiple -conf parameters, but -userconf will always be
+ loaded before them.
+
+ -conf configfilelocation
+ Start DOSBox with the options specified in "configfilelocation".
+ Multiple -conf options may be present.
+ See Section 13: "The configuration (options) file" for more details.
+
+ -lang languagefilelocation
+ Start DOSBox using the language specified in "languagefilelocation".
+ See Section 14: "The Language File" for more details.
+
+ -machine machinetype
+ Setup DOSBox to emulate a specific type of machine. Valid choices are:
+ hercules, cga, ega, pcjr, tandy, svga_s3 (default) as well as
+ the additional SVGA chipsets listed in the DOSBox configuration file.
+ svga_s3 enables VESA emulation as well.
+ For some special VGA effects the machinetype vgaonly can be used,
+ note that this disables SVGA capabilities and might be slower due to the
+ higher emulation precision.
+ The machinetype affects the video card and the available sound cards.
+
+ -noconsole (Windows Only)
+ Start DOSBox without showing the DOSBox status window (console).
+ Output will be redirected to stdout.txt and stderr.txt
+
+ -startmapper
+ Enter the keymapper directly on startup. Useful for people with
+ keyboard problems.
+
+ -noautoexec
+ Skips the [autoexec] section of the loaded configuration file.
+
+ -securemode
+ Same as -noautoexec, but adds config.com -securemode at the
+ bottom of AUTOEXEC.BAT (which in turn disables any changes to how
+ the drives are mounted inside DOSBox).
+
+ -scaler scaler
+ Uses the scaler specified by "scaler". See the DOSBox configuration file
+ for the available scalers.
+
+ -forcescaler scaler
+ Similar to the -scaler parameter, but tries to force usage of
+ the specified scaler even if it might not fit.
+
+ -version
+ output version information and exit. Useful for frontends.
+
+ -editconf program
+ calls program with as first parameter the configuration file.
+ You can specify this command more than once. In this case it will
+ move to second program if the first one fails to start.
+
+ -opencaptures program
+ calls program with as first parameter the location of the captures
+ folder.
+
+ -printconf
+ prints the location of the default configuration file.
+
+ -resetconf
+ removes the default configuration file.
+
+ -resetmapper
+ removes the mapperfile used by the default clean configuration file.
+
+ -socket
+ passes the socket number to the nullmodem emulation. See Section 9:
+ "Serial Multiplayer feature."
+
+Note: If a name/command/configfilelocation/languagefilelocation contains
+ a space, put the whole name/command/configfilelocation/languagefilelocation
+ between quotes ("command or file name"). If you need to use quotes within
+ quotes (most likely with -c and mount):
+ Windows and OS/2 users can use single quotes inside the double quotes.
+ Other people should be able to use escaped double quotes inside the
+ double quotes.
+ Windows: -c "mount c 'c:\My folder with DOS games\'"
+ Linux: -c "mount c \"/tmp/name with space\""
+
+A rather unusual example, just to demonstrate what you can do (Windows):
+dosbox D:\folder\file.exe -c "MOUNT Y H:\MyFolder"
+ This mounts D:\folder as C:\ and runs file.exe.
+ Before it does that, it will first mount H:\MyFolder as the Y drive.
+
+In Windows, you can also drag directories/files onto the DOSBox executable.
+
+
+
+=====================
+4. Internal Programs:
+=====================
+
+DOSBox supports most of the DOS commands found in command.com.
+To get a list of the internal commands type "HELP" at the prompt.
+
+In addition, the following commands are available:
+
+MOUNT "Emulated Drive letter" "Real Drive or Directory"
+ [-t type] [-aspi] [-ioctl] [-noioctl] [-usecd number] [-size drivesize]
+ [-label drivelabel] [-freesize size_in_mb]
+ [-freesize size_in_kb (floppies)]
+MOUNT -cd
+MOUNT -u "Emulated Drive letter"
+
+ Program to mount local directories as drives inside DOSBox.
+
+ "Emulated Drive letter"
+ The driveletter inside DOSBox (for example C).
+
+ "Real Drive letter (usually for CD-ROMs in Windows) or Directory"
+ The local directory you want accessible inside DOSBox.
+
+ -t type
+ Type of the mounted directory.
+ Supported are: dir (default), floppy, cdrom.
+
+ -size drivesize
+ (experts only)
+ Sets the size of the drive, where drivesize is of the form
+ "bps,spc,tcl,fcl":
+ bps: bytes per sector, by default 512 for regular drives and
+ 2048 for CD-ROM drives
+ spc: sectors per cluster, usually between 1 and 127
+ tcl: total clusters, between 1 and 65534
+ fcl: total free clusters, between 1 and tcl
+
+ -freesize size_in_mb | size_in_kb
+ Sets the amount of free space available on a drive
+ in megabytes (regular drives) or kilobytes (floppy drives).
+ This is a simpler version of -size.
+
+ -label drivelabel
+ Sets the name of the drive to "drivelabel". Needed on some systems
+ if the CD-ROM label isn't read correctly (useful when a program
+ can't find its CD-ROM). If you don't specify a label
+ and no lowlevel support is selected (that is omitting the -usecd #
+ and/or -aspi parameters, or specifying -noioctl):
+ For Windows: label is extracted from "Real Drive".
+ For Linux: label is set to NO_LABEL.
+
+ If you do specify a label, this label will be kept as long as the drive
+ is mounted. It will not be updated !!
+
+ -aspi
+ Forces use of the aspi layer. Only valid if mounting a CD-ROM under
+ Windows systems with an ASPI-Layer.
+
+ -ioctl (automatic selection of the CD audio interface)
+ -ioctl_dx (digital audio extraction used for CD audio)
+ -ioctl_dio (ioctl calls used for CD audio)
+ -ioctl_mci (MCI used for CD audio)
+ Forces use of ioctl commands. Only valid if mounting a CD-ROM under
+ a Windows OS which support them (Win2000/XP/NT).
+ The various choices only differ in the way CD audio is handled,
+ preferably -ioctl_dio is used (lowest workload), but this might not
+ work on all systems, so -ioctl_dx (or -ioctl_mci) can be used.
+
+ -noioctl
+ Forces use of the SDL CD-ROM layer. Valid on all systems.
+
+ -usecd number
+ Valid on all systems, under Windows the -noioctl switch has to be
+ present to make use of the -usecd switch.
+ Enables to select the drive that should be used by SDL. Use this if
+ the wrong or no CD-ROM drive is mounted while using the SDL CD-ROM
+ interface. "number" can be found by "MOUNT -cd".
+
+ -cd
+ Displays all CD-ROM drives detected by SDL, and their numbers.
+ See the information at the -usecd entry above.
+
+ -u
+ Removes the mount. Doesn't work for Z:\.
+
+ Note: It's possible to mount a local directory as CD-ROM drive,
+ but hardware support is then missing.
+
+ Basically MOUNT allows you to connect real hardware to DOSBox's emulated PC.
+ So MOUNT C C:\GAMES tells DOSBox to use your C:\GAMES directory as drive C:
+ in DOSBox. MOUNT C E:\SomeFolder tells DOSBox to use your E:\SomeFolder
+ directory as drive C: in DOSBox.
+
+ Mounting your entire C drive with MOUNT C C:\ is NOT recommended! The same
+ is true for mounting the root of any other drive, except for CD-ROMs (due to
+ their read-only nature).
+ Otherwise if you or DOSBox make a mistake you may lose all your files.
+ Also never mount a "Windows" or "Program Files" folders or their subfolders
+ in Windows Vista/7 as DOSBox may not work correctly, or will stop working
+ correctly later. It is recommended to keep all your dos applications/games
+ in a simple folder (for example c:\dosgames) and mount that.
+
+ You should always install your game inside DOSBox.
+ So if you have the game on CD you always (even after installation!)
+ have to mount both: folder as a harddisk drive and a CD-ROM.
+ HardDisk should always be mounted as c
+ CD-ROM should always be mounted as d
+ Floppy should always be mounted as a (or b)
+
+ Basic MOUNT Examples for normal usage (Windows):
+
+ 1. To mount a folder as a harddisk drive:
+ mount c d:\dosgames
+
+ 3. To mount your CD-ROM drive E as CD-ROM drive D in DOSBox:
+ mount d e:\ -t cdrom
+
+ 2. To mount your drive a: as a floppy:
+ mount a a:\ -t floppy
+
+ Advanced MOUNT examples (Windows):
+
+ 4. To mount a hard disk drive with ~870 mb free diskspace (simple version):
+ mount c d:\dosgames -freesize 870
+
+ 5. To mount a drive with ~870 mb free diskspace (experts only, full control):
+ mount c d:\dosgames -size 512,127,16513,13500
+
+ 1. To mount c:\dosgames\floppy as a floppy:
+ mount a c:\dosgames\floppy -t floppy
+
+
+ Other MOUNT examples:
+
+ 3. To mount system CD-ROM drive at mountpoint /media/cdrom as CD-ROM drive D
+ in DOSBox:
+ mount d /media/cdrom -t cdrom -usecd 0
+
+ 6. To mount /home/user/dosgames as drive C in DOSBox:
+ mount c /home/user/dosgames
+
+ 7. To mount the directory where DOSBox was started as C in DOSBox:
+ mount c .
+ (note the . which represents the directory where DOSBox was started,
+ on Windows Vista/7 don't use this if you installed DOSBox
+ to your "Program Files" folder)
+
+ If you want to mount a CD image or floppy image, check IMGMOUNT.
+ MOUNT also works with images but only if you use external program,
+ for example (both are free):
+ - Daemon Tools Lite (for CD images),
+ - Virtual Floppy Drive (for floppy images).
+ Although IMGMOUNT can give better compatibility.
+
+
+MEM
+ Program to display the amount and type of free memory.
+
+
+VER
+VER set major_version [minor_version]
+ Display the current DOSBox version and reported DOS version
+ (parameterless usage).
+ Change the reported DOS version with the "set" parameter,
+ for example: "VER set 6 22" to have DOSBox report DOS 6.22 as version number.
+
+
+CONFIG -writeconf filelocation
+CONFIG -writeconf
+CONFIG -wcp filelocation
+CONFIG -wcd
+CONFIG -writelang filelocation
+CONFIG -axadd
+CONFIG -axclear
+CONFIG -axtype
+CONFIG -r [parameters]
+CONFIG -l
+CONFIG -help
+CONFIG -help sections
+CONFIG -help section
+CONFIG -help section property
+CONFIG -securemode
+CONFIG -set "section property=value"
+CONFIG -get "section property"
+
+ CONFIG can be used to change or query various settings of DOSBox
+ during runtime. It can save the current settings and language strings to
+ disk. Information about all possible sections and properties can
+ be found in Section 13: "The configuration (options) file".
+
+ -writeconf filelocation
+ (or -wc filelocation)
+ Write the current configuration settings to a file in a specified location
+ relative to the DOSBox config directory. Relative and absolute paths are
+ possible. "filelocation" is located on the local drive, not a mounted
+ drive in DOSBox.
+
+ The configuration file controls various settings of DOSBox:
+ the amount of emulated memory, the emulated sound cards and many more
+ things. It allows access to AUTOEXEC.BAT as well.
+ See Section 13: "The configuration (options) file" for more information.
+
+ -writeconf
+ (or -wc)
+ Write the configuration to the primary loaded config file.
+
+ -wcp filelocation
+ Write the current configuration settings to the specified file in or
+ relative to the DOSBox program start directory. Realtive and absolute
+ paths are possible. This is located on a drive on the host, not a mounted
+ drive in DOSBox. It is useful if you keep DOSBox on a removable media.
+ If file is omitted, the configuration will be written to dosbox.conf.
+
+ -wcd
+ Write the current configuration to the default config file.
+
+
+ -writelang filelocation
+ (or -wl filelocation)
+ Write the current language settings to a file in a specified location.
+ "filelocation" is located on the local drive, not a mounted drive
+ in DOSBox. The language file controls all visible output of the internal
+ commands and the internal DOS.
+ See Section 14: "The Language File" for more information.
+
+ -axadd "line1" "line2" ...
+ Adds a command line to the autoexec section.
+
+ -axclear
+ Clears the autoexec section.
+
+ -axtype
+ Prints the content of the autoexec section.
+
+ -r [parameters]
+ Restart DOSBox, either with the parameters that were used to start the
+ current instance or any that are appended.
+
+ -l
+ lists DOSBox parameters:
+ - the configuration directory
+ - the config files that were used when starting this session
+ - the command line parameters DOSBox was started with
+
+ -h, -help, -?
+ Displays an overvie of the config commands.
+
+ -h, -help, -? sections
+ Displays the list of sections in the config file.
+
+ -h, -help, -? section
+ Displays the list of properties contained in the specified section.
+
+ -h, -help, -? section property
+ Shows information about the specified property in the specified section:
+ - purpose of the property
+ - possible values, current value, default value
+ - wether it can definitely not be changed at runtime
+
+ -securemode
+ Switches DOSBox to a more secure mode. In this mode the internal
+ commands MOUNT, IMGMOUNT and BOOT won't work. It's not possible either
+ to create a new configfile or languagefile in this mode.
+ (Warning: you can only undo this mode by restarting DOSBox.)
+
+ -set "section property=value"
+ CONFIG will attempt to set the property to new value.
+
+ -get "section property"
+ The current value of the property is reported and stored in the
+ environment variable %CONFIG%. This can be used to store the value
+ when using batch files.
+
+ Both "-set" and "-get" work from batch files and can be used to set up your
+ own preferences for each game. Although it may be easier to use separate
+ DOSBox's configuration files for each game instead.
+
+ Examples:
+ 1. To create a configuration file in your c:\dosgames directory:
+ config -writeconf c:\dosgames\dosbox.conf
+ 2. To set the cpu cycles to 10000:
+ config -set "cpu cycles=10000"
+ 3. To turn EMS memory emulation off:
+ config -set "dos ems=false"
+ 4. To check which cpu core is being used.
+ config -get "cpu core"
+ 5. To view the list of possible cpu cores:
+ config -help cpu core
+ 6. To change the machine type and restart:
+ config -set "machine cga"
+ config -wc -r
+ 7. To configure the autoexec section to auto-mount a directory at start:
+ config -axadd "mount c c:\dosgames" "c:"
+ config -wc
+ 8. To create a specific config file in the config directory:
+ config -set "dos ems=false"
+ config -set "cpu cycles=10000"
+ config -set "core dynamic"
+ config -axadd "mount c c:\dosgames" "c:" "cd my_game" "my_game"
+ config -wc my_config.conf
+ 9. To restart DOSBox from a specific config file in the config directory:
+ config -r -conf my_config.conf
+
+LOADFIX [-size] [program] [program-parameters]
+LOADFIX -f
+ Program to reduce the amount of available conventional memory.
+ Useful for old programs which don't expect much memory to be free.
+
+ -size
+ number of kilobytes to "eat up", default = 64kb
+
+ -f
+ frees all previously allocated memory
+
+ Examples:
+ 1. To start mm2.exe and allocate 64kb memory
+ (mm2 will have 64 kb less available):
+ loadfix mm2
+ 2. To start mm2.exe and allocate 32kb memory:
+ loadfix -32 mm2
+ 3. To free previous allocated memory:
+ loadfix -f
+
+
+RESCAN [Drive:] [-All]
+ Make DOSBox reread the directory structure. Useful if you changed something
+ on a mounted drive outside of DOSBox. (CTRL - F4 does this as well!)
+
+ Drive:
+ Drive to refresh.
+
+ -All
+ Refresh all drives.
+
+ if both a Drive: and -All are missing, then the current drive will be
+ refreshed.
+
+
+MIXER
+ Makes DOSBox display its current volume settings.
+ Here's how you can change them:
+
+ mixer channel left:right [/NOSHOW] [/LISTMIDI]
+
+ channel
+ Can be one of the following: MASTER, DISNEY, SPKR, GUS, SB, FM [, CDAUDIO].
+ CDAUDIO is only available if a CD-ROM interface with volume control is
+ enabled (CD image, ioctl_dx).
+
+ left:right
+ The volume levels in percentages. If you put a D in front it will be
+ in decibel (Example: mixer gus d-10).
+
+ /NOSHOW
+ Prevents DOSBox from showing the result if you set one
+ of the volume levels.
+
+ /LISTMIDI
+ In Windows lists the available midi devices on your PC. To select a device
+ other than the Windows default midi-mapper, change the line 'midiconfig='
+ in the [midi] section of the configuration file to 'midiconfig=id', where
+ 'id' is the number for the device as listed by LISTMIDI. eg. midiconfig=2
+
+ In Linux this option doesn't work, but you get similar results by using
+ 'pmidi -l' in console. Then change the line 'midiconfig=' to
+ 'midiconfig=port', where 'port' is the port for the device as listed by
+ 'pmidi -l'. eg. midiconfig=128:0
+
+
+IMGMOUNT
+ A utility to mount disk images and CD-ROM images in DOSBox.
+
+ IMGMOUNT DRIVE [imagefile] -t [image_type] -fs [image_format]
+ -size [sectorsbytesize, sectorsperhead, heads, cylinders]
+ IMGMOUNT DRIVE [imagefile1 imagefile2 .. imagefileN] -t cdrom -fs iso
+
+ imagefile
+ Location of the image file to mount in DOSBox. The location can be
+ on a mounted drive inside DOSBox, or on your real disk. It is possible
+ to mount CD-ROM images (ISOs or CUE/BIN or CUE/IMG) too.
+ If you need CD swapping capabilities, specify all images in succession
+ (see the next entry).
+ CUE/BIN pairs and cue/img are the preferred CD-ROM image types as they can
+ store audio tracks compared to ISOs (which are data-only). For
+ the CUE/BIN mounting always specify the CUE sheet.
+
+ imagefile1 imagefile2 .. imagefileN
+ Location of the image files to mount in DOSBox. Specifying a number
+ of image files is only allowed for CD-ROM images.
+ The CD's can be swapped with CTRL-F4 at any time.
+ This is required for games which use multiple CD-ROMs and require the CD
+ to be switched during the gameplay at some point.
+
+ -t
+ The following are valid image types:
+ floppy: Specifies a floppy image. DOSBox will automatically identify
+ the disk geometry (360K, 1.2MB, 720K, 1.44MB, etc).
+ cdrom: Specifies a CD-ROM image. The geometry is automatic and
+ set for this size. This can be an iso or a cue/bin pair or
+ a cue/img pair.
+ hdd: Specifies a harddrive image. The proper CHS geometry must be set
+ for this to work.
+
+ -fs
+ The following are valid file system formats:
+ iso: Specifies the ISO 9660 CD-ROM format.
+ fat: Specifies that the image uses the FAT file system. DOSBox will
+ attempt to mount this image as a drive in DOSBox and make
+ the files available from inside DOSBox.
+ none: DOSBox will make no attempt to read the file system on the disk.
+ This is useful if you need to format it or if you want to boot
+ the disk using the BOOT command. When using the "none"
+ filesystem, you must specify the drive number (2 or 3,
+ where 2 = master, 3 = slave) rather than a drive letter.
+ For example, to mount a 70MB image as the slave drive device,
+ you would type (without the quotes):
+ "imgmount 3 d:\test.img -size 512,63,16,142 -fs none"
+ Compare this with a mount to be able to access the drive
+ within DOSBox, which would read as:
+ "imgmount e: d:\test.img -size 512,63,16,142"
+
+ -size
+ The Cylinders, Heads and Sectors of the drive.
+ Required to mount hard drive images.
+
+ An example how to mount CD-ROM images (in Linux):
+ 1. imgmount d /tmp/cdimage1.cue /tmp/cdimage2.cue -t cdrom
+ or (which also works):
+ 2a. mount c /tmp
+ 2b. imgmount d c:\cdimage1.cue c:\cdimage2.cue -t cdrom
+ (in Windows):
+ imgmount d f:\img\CD1.cue f:\img\CD2.cue f:\img\CD3.cue -t cdrom
+ imgmount d "g:\img\7th Guest CD1.cue" "g:\img\7th Guest CD2.cue" -t cdrom
+ Don't forget that you can also use MOUNT with images, but only if you use
+ external program, for example (both are free):
+ - Daemon Tools Lite (for CD images),
+ - Virtual Floppy Drive (for floppy images).
+ Although IMGMOUNT can give better compatibility.
+
+
+BOOT
+ Boot will start floppy images or hard disk images independent of
+ the operating system emulation offered by DOSBox. This will allow you to
+ play booter floppies or boot other operating systems inside DOSBox.
+ If the target emulated system is PCjr (machine=pcjr) the boot command
+ can be used to load PCjr cartridges (.jrc).
+
+ BOOT [diskimg1.img diskimg2.img .. diskimgN.img] [-l driveletter]
+ BOOT [cart.jrc] (PCjr only)
+
+ diskimg1.img diskimg2.img .. diskimgN.img
+ This can be any number of floppy disk images one wants mounted after
+ DOSBox boots the specified drive letter.
+ To swap between images, hit CTRL-F4 to change from the current disk
+ to the next disk in the list. The list will loop back from the last
+ disk image to the beginning.
+
+ [-l driveletter]
+ This parameter allows you to specify the drive to boot from.
+ The default is the A drive, the floppy drive. You can also boot
+ a hard drive image mounted as master by specifying "-l C"
+ without the quotes, or the drive as slave by specifying "-l D"
+
+ cart.jrc (PCjr only)
+ When emulation of a PCjr is enabled, cartridges can be loaded with
+ the BOOT command. Support is still limited.
+
+
+IPX
+
+ You need to enable IPX networking in the configuration file of DOSBox.
+
+ All of the IPX networking is managed through the internal DOSBox program
+ IPXNET. For help on the IPX networking from inside DOSBox, type
+ "IPXNET HELP" (without quotes) and the program will list the commands
+ and relevant documentation.
+
+ With regard to actually setting up a network, one system needs to be
+ the server. To set this up, type "IPXNET STARTSERVER" (without the quotes)
+ in a DOSBox session. The server DOSBox session will automatically add
+ itself to the virtual IPX network. For every additional computer that
+ should be part of the virtual IPX network, you'll need to type
+ "IPXNET CONNECT <computer host name or IP>".
+ For example, if your server is at bob.dosbox.com, you would type
+ "IPXNET CONNECT bob.dosbox.com" on every non-server system.
+
+ To play games that need Netbios a file named NETBIOS.EXE from Novell is
+ needed. Establish the IPX connection as explained above, then run
+ "netbios.exe".
+
+ The following is an IPXNET command reference:
+
+ IPXNET CONNECT
+
+ IPXNET CONNECT opens a connection to an IPX tunneling server
+ running on another DOSBox session. The "address" parameter specifies
+ the IP address or host name of the server computer. You can also
+ specify the UDP port to use. By default IPXNET uses port 213 - the
+ assigned IANA port for IPX tunneling - for its connection.
+
+ The syntax for IPXNET CONNECT is:
+ IPXNET CONNECT address <port>
+
+ IPXNET DISCONNECT
+
+ IPXNET DISCONNECT closes the connection to the IPX tunneling server.
+
+ The syntax for IPXNET DISCONNECT is:
+ IPXNET DISCONNECT
+
+ IPXNET STARTSERVER
+
+ IPXNET STARTSERVER starts an IPX tunneling server on this DOSBox
+ session. By default, the server will accept connections on UDP port
+ 213, though this can be changed. Once the server is started, DOSBox
+ will automatically start a client connection to the IPX tunneling server.
+
+ The syntax for IPXNET STARTSERVER is:
+ IPXNET STARTSERVER <port>
+
+ If the server is behind a router, UDP port <port> needs to be forwarded
+ to that computer.
+
+ On Linux/Unix-based systems port numbers smaller than 1023 can only be
+ used with root privileges. Use ports greater than 1023 on those systems.
+
+ IPXNET STOPSERVER
+
+ IPXNET STOPSERVER stops the IPX tunneling server running on this DOSBox
+ session. Care should be taken to ensure that all other connections have
+ terminated as well, since stopping the server may cause lockups on other
+ machines that are still using the IPX tunneling server.
+
+ The syntax for IPXNET STOPSERVER is:
+ IPXNET STOPSERVER
+
+ IPXNET PING
+
+ IPXNET PING broadcasts a ping request through the IPX tunneled network.
+ In response, all other connected computers will respond to the ping
+ and report the time it took to receive and send the ping message.
+
+ The syntax for IPXNET PING is:
+ IPXNET PING
+
+ IPXNET STATUS
+
+ IPXNET STATUS reports the current state of this DOSBox session's
+ IPX tunneling network. For a list of all computers connected to the
+ network use the IPXNET PING command.
+
+ The syntax for IPXNET STATUS is:
+ IPXNET STATUS
+
+
+KEYB [keyboardlayoutcode [codepage [codepagefile]]]
+
+ Change the keyboard layout. For detailed information about keyboard layouts
+ please see Section 8: "Keyboard Layout".
+
+ [keyboardlayoutcode] is a string consisting of five or less characters,
+ examples are PL214 (Polish typists) or PL457 (Polish programmers).
+ It specifies the keyboard layout to be used.
+ The list of all layouts built into DOSBox is here:
+ https://www.vogons.org/viewtopic.php?t=21824
+
+ [codepage] is the number of the codepage to be used. The keyboard layout
+ has to provide support for the specified codepage, otherwise the layout
+ loading will fail.
+ If no codepage is specified, an appropriate codepage for the requested
+ layout is chosen automatically.
+
+ [codepagefile] can be used to load codepages that are yet not compiled
+ into DOSBox. This is only needed when DOSBox does not find the codepage.
+ If no codepagefile is specified, but you place all ten ega.cpx files
+ (from FreeDOS) in the DOSBox program folder, an appropriate codepagefile
+ for the requested layout/codepage is chosen automatically.
+
+ Examples:
+ 1. To load the polish typist keys layout (automatically uses codepage 852):
+ keyb pl214
+ 2. To load one of russian keyboard layouts with codepage 866:
+ keyb ru441 866
+ In order to type russian characters press ALT+RIGHT-SHIFT.
+ 3. To load one of french keyboard layouts with codepage 850 (where the
+ codepage is defined in EGACPI.DAT):
+ keyb fr189 850 EGACPI.DAT
+ 4. To load codepage 858 (without a keyboard layout):
+ keyb none 858
+ This can be used to change the codepage for the FreeDOS keyb2 utility.
+ 5. To display the current codepage and, if loaded, the keyboard layout:
+ keyb
+
+
+
+For more information use the /? command line switch with the programs.
+
+
+
+================
+5. Special Keys:
+================
+
+ALT-ENTER Switch to full screen and back.
+ALT-PAUSE Pause emulation (hit ALT-PAUSE again to continue).
+CTRL-F1 Start the keymapper.
+CTRL-F4 Change between mounted floppy/CD images. Update directory cache
+ for all drives.
+CTRL-ALT-F5 Start/Stop creating a movie of the screen. (avi video capturing)
+CTRL-F5 Save a screenshot. (PNG format)
+CTRL-F6 Start/Stop recording sound output to a wave file.
+CTRL-ALT-F7 Start/Stop recording of OPL commands. (DRO format)
+CTRL-ALT-F8 Start/Stop the recording of raw MIDI commands.
+CTRL-F7 Decrease frameskip.
+CTRL-F8 Increase frameskip.
+CTRL-F9 Kill DOSBox.
+CTRL-F10 Capture/Release the mouse.
+CTRL-F11 Slow down emulation (Decrease DOSBox Cycles).
+CTRL-F12 Speed up emulation (Increase DOSBox Cycles)*.
+ALT-F12 Unlock speed (turbo button/fast forward)**.
+CTRL-ALT-HOME Restart DOSBox.
+F11, ALT-F11 (machine=cga) change tint in NTSC output modes***.
+F11 (machine=hercules) cycle through amber, green, white colouring***.
+
+*NOTE: Once you increase your DOSBox cycles beyond your computer CPU resources,
+ it will produce the same effect as slowing down the emulation.
+ This maximum will vary from computer to computer.
+
+**NOTE: You need free CPU resources for this (the more you have, the faster
+ it goes), so it won't work at all with cycles=max or a too high amount
+ of fixed cycles. You have to keep the keys pressed for it to work!
+
+***NOTE: These keys won't work if you saved a mapper file earlier with
+ a different machine type. So either reassign them or reset the mapper.
+
+These are the default keybindings. They can be changed in the keymapper
+(see Section 7: "KeyMapper").
+
+In Mac OS X you can try using cmd(applekey) together with Ctrl (and/or ) Fn,
+if the key doesn't work i.e. fn-cmd-ctrl-F1, but some keys may still need
+remapping (in Linux too).
+
+Saved/recorded files can be found in:
+ (Windows) "Start/WinLogo Menu"->"All Programs"->"DOSBox-0.74-2"->Extras
+ (Linux) ~/.dosbox/capture
+ (Mac OS X) "~/Library/Preferences/capture"
+This can be changed in the DOSBox configuration file.
+
+
+
+====================
+6. Joystick/Gamepad:
+====================
+
+The standard joystick port in DOS supports a maximum of 4 axes and 4 buttons.
+For more, different modifications of that configuration were used.
+
+To force DOSBox to use a different type of emulated joystick/gamepad, the entry
+"joysticktype" in the [joystick] section of the DOSBox configuration file can
+be used.
+
+none - disables controller support.
+auto - (default) autodetects whether you have one or two controllers connected:
+ if you have one - '4axis' setting is used,
+ if you have two - '2axis' setting is used.
+2axis - If you have two controllers connected, each will emulate a joystick
+ with 2 axes and 2 buttons. If you have only one controller connected,
+ it will emulate a joystick with only 2 axis and 2 buttons.
+4axis - supports only first controller, emulates a joystick
+ with 4 axis and 4 buttons or a gamepad with 2axis and 6 buttons.
+4axis_2 - supports only second controller.
+fcs - supports only first controller, emulates ThrustMaster
+ Flight Control System, with 3-axes, 4 buttons and 1 hat.
+ch - supports only first controller, emulates CH Flightstick,
+ with 4-axes, 6 buttons and 1 hat, but you cannot press more
+ than one button at the same time.
+
+You also have to configure controller properly inside the game.
+It is important to remember that if you saved the mapperfile without joystick
+connected, or with a different joystick setting, your new setting will not work
+properly, or not work at all, until you reset DOSBox's mapperfile.
+
+If controller is working properly outside DOSBox, but doesn't calibrate properly
+inside DOSBox, try a different 'timed' setting in DOSBox's configuration file.
+
+
+
+=============
+7. KeyMapper:
+=============
+
+Start the DOSBox mapper either with CTRL-F1 (see Section 5: "Special Keys") or
+-startmapper (see Section 3: "Command Line Parameters").
+You are presented with a virtual keyboard and a virtual joystick.
+
+These virtual devices correspond to the keys and events DOSBox will
+report to the DOS applications. If you click on a button with your mouse,
+you can see in the lower left corner with which event it is associated
+(EVENT) and to what events it is currently bound.
+
+Event: EVENT
+BIND: BIND (the real key/button/axis you push with your finger/hand)
+
+ Add Del
+mod1 hold Next
+mod2
+mod3
+
+
+EVENT
+ The key or joystick axis/button/hat DOSBox will report to DOS applications.
+ (the event that will happen during the game, (eg. shooting/jumping/walking)
+BIND
+ The key on your real keyboard or the axis/button/hat on your real
+ joystick(s) (as reported by SDL), which is connected to the EVENT.
+mod1,2,3
+ Modifiers. These are keys you need to have to be pressed while pressing
+ BIND. mod1 = CTRL and mod2 = ALT. These are generally only used when you
+ want to change the special keys of DOSBox.
+Add
+ Add a new BIND to this EVENT. Basically add a key from your keyboard or an
+ event from the joystick (button press, axis/hat movement) which will
+ produce the EVENT in DOSBox.
+Del
+ Delete the BIND to this EVENT. If an EVENT has no BINDS, then it is not
+ possible to trigger this event in DOSBox (that is there's no way to type
+ the key or use the respective action of the joystick).
+Next
+ Go through the list of bindings which map to this EVENT.
+
+
+Example:
+Q1. You want to have the X on your keyboard to type a Z in DOSBox.
+ A. Click on the Z on the keyboard mapper. Click "Add".
+ Now press the X key on your keyboard.
+
+Q2. If you click "Next" a couple of times, you will notice that the Z on your
+ keyboard also produces an Z in DOSBox.
+ A. Therefore select the Z again, and click "Next" until you have the Z on
+ your keyboard. Now click "Del".
+
+Q3. If you try it out in DOSBox, you will notice that pressing X makes ZX
+ appear.
+ A. The X on your keyboard is still mapped to the X as well! Click on
+ the X in the keyboard mapper and search with "Next" until you find the
+ mapped key X. Click "Del".
+
+
+Examples of remapping the joystick:
+ You have a joystick attached, it is working fine under DOSBox and you
+ want to play some keyboard-only game with the joystick (it is assumed
+ that the game is controlled by the arrows on the keyboard):
+ 1. Start the mapper, then click on one of the left keyboard arrow.
+ EVENT should be key_left. Now click on Add and move your joystick
+ in the respective direction, this should add an event to the BIND.
+ 2. Repeat the above for the missing three directions, additionally
+ the buttons of the joystick can be remapped as well (fire/jump).
+ 3. Click on Save, then on Exit and test it with some game.
+
+ You want to swap the y-axis of the joystick because some flightsim uses
+ the up/down joystick movement in a way you don't like, and it is not
+ configurable in the game itself:
+ 1. Start the mapper and click on Y- in the first joystick field.
+ EVENT should be jaxis_0_1-.
+ 2. Click on Del to remove the current binding, then click Add and move
+ your joystick downwards. A new bind should be created.
+ 3. Repeat this for Y+, save the layout and finally test it with some game.
+
+ If you want to remap anything to your d-pad/hat you will have to change
+ 'joysticktype=auto' to 'joysticktype=fcs' in configuration file. Maybe this
+ will be improved in the next DOSBox version.
+
+
+If you change the default mapping, you can save your changes by clicking on
+"Save". DOSBox will save the mapping to a location specified in
+the configuration file (the mapperfile= entry). At startup, DOSBox will load
+your mapperfile, if it is present in the DOSBox configuration file.
+
+
+
+===================
+8. Keyboard Layout:
+===================
+
+To switch to a different keyboard layout, either the entry "keyboardlayout"
+in the [dos] section of the DOSBox configuration file or the internal DOSBox
+program keyb.com (Section 4: "Internal Programs") can be used. Both accept
+DOS conforming language codes (see below), but only by using keyb.com a
+custom codepage can be specified.
+
+The default keyboardlayout=auto currently works under Windows only. The language
+is chosen according to the OS language, but the keyboard layout is not detected.
+
+Layout switching
+ DOSBox supports a number of keyboard layouts and codepages by default,
+ in this case just the layout identifier needs to be specified (like
+ keyboardlayout=PL214 in the DOSBox configuration file, or using "keyb PL214"
+ at the DOSBox command prompt). The list of all layouts built into DOSBox is
+ here: https://www.vogons.org/viewtopic.php?t=21824
+
+ Some keyboard layouts (for example layout GK319 codepage 869 and layout RU441
+ codepage 808) have support for dual layouts that can be accessed by pressing
+ LeftALT+RrightSHIFT for one layout and LeftALT+LeftSHIFT for the other.
+ Some keyboard layouts (for example layout LT456 codepage 771) have support
+ for three layouts, third can be accessed by pressing LeftALT+LeftCTRL
+
+Supported external files
+ The FreeDOS .kl files are supported (FreeDOS keyb2 keyboard layoutfiles) as
+ well as the FreeDOS keyboard.sys/keybrd2.sys/keybrd3.sys libraries which
+ consist of all available .kl files.
+ See http://www.freedos.org/ for precompiled keyboard layouts if
+ the DOSBox-integrated layouts don't work for some reason, or if updated or
+ new layouts become available.
+
+ Both .CPI (MS-DOS and compatible codepage files) and .CPX (FreeDOS
+ UPX-compressed codepage files) can be used. Some codepages are compiled
+ into DOSBox, so it is mostly not needed to care about external codepage
+ files. If you need a different (or custom) codepage file, copy it into
+ the directory of the DOSBox so it is accessible for DOSBox.
+ If you place all ten ega.cpx files (from FreeDOS) in DOSBox folder,
+ an appropriate codepagefile for the requested layout/codepage is
+ chosen automatically.
+
+ Additional layouts can be added by copying the corresponding .kl file into
+ the directory of the DOSBox configuration file and using the first part of
+ the filename as language code.
+ Example: For the file UZ.KL (keyboard layout for Uzbekistan) specify
+ "keyboardlayout=uz" in the DOSBox configuration file.
+ The integration of keyboard layout packages (like keybrd2.sys) works similar.
+
+Note that the keyboard layout allows foreign characters to be entered, but
+there is NO support for them in filenames. Try to avoid them both inside
+DOSBox as well as in files on your host operating system that are accessible
+by DOSBox.
+
+
+
+==============================
+9. Serial Multiplayer feature:
+==============================
+
+DOSBox can emulate a serial nullmodem cable over network and internet.
+It can be configured through the [serialports] section in the DOSBox
+configuration file.
+
+To create a nullmodem connection, one side needs to act as the server and
+one as the client.
+
+The server needs to be set up in the DOSBox configuration file like this:
+ serial1=nullmodem
+
+The client:
+ serial1=nullmodem server:<IP or name of the server>
+
+Now start your game and choose nullmodem / serial cable / already connected
+as multiplayer method on COM1. Set the same baudrate on both computers.
+
+Furthermore, additional parameters can be specified to control the behavior
+of the nullmodem connection. These are all parameters:
+
+ * port: - TCP port number. Default: 23
+ * rxdelay: - how long (milliseconds) to delay received data if the
+ interface is not ready. Increase this value if you encounter
+ overrun errors in the DOSBox status window. Default: 100
+ * txdelay: - how long to gather data before sending a packet. Default: 12
+ (reduces Network overhead)
+ * server: - This nullmodem will be a client connecting to the specified
+ server. (No server argument: be a server.)
+ * transparent:1 - Only send the serial data, no RTS/DTR handshake. Use this
+ when connecting to anything other than a nullmodem.
+ * telnet:1 - Interpret Telnet data from the remote site. Automatically
+ sets transparent.
+ * usedtr:1 - The connection will not be established until DTR is switched
+ on by the DOS program. Useful for modem terminals.
+ Automatically sets transparent.
+ * inhsocket:1 - Use a socket passed to DOSBox by command line. Automatically
+ sets transparent. (Socket Inheritance: It is used for
+ playing old DOS door games on new BBS software.)
+
+Example: Be a server listening on TCP port 5000.
+ serial1=nullmodem server:<IP or name of the server> port:5000 rxdelay:1000
+
+
+
+=====================================
+10. How to speed up/slow down DOSBox:
+=====================================
+
+DOSBox emulates the CPU, the sound and graphic cards, and other peripherals
+of a PC, all at the same time. The speed of an emulated DOS application
+depends on how many instructions can be emulated, which is adjustable
+(number of cycles).
+
+CPU Cycles (speed up/slow down)
+ By default (cycles=auto) DOSBox tries to detect whether a game needs to
+ be run with as many instructions emulated per time interval as possible
+ (cycles=max, sometimes this results in game working too fast or unstable),
+ or whether to use fixed amount of cycles (cycles=3000, sometimes this results
+ in game working too slow or too fast). But you can always manually force
+ a different setting in the DOSBox's configuration file.
+
+ You can force the slow or fast behavior by setting a fixed amount of cycles
+ in the DOSBox's configuration file. If you set for example cycles=10000, the
+ DOSBox window will display a line "CPU speed: fixed 10000 cycles" at the top.
+ In this mode you can reduce the amount of cycles even more by hitting CTRL-F11
+ (you can go as low as you want) or raise it by hitting CTRL-F12 as much as you
+ want, but you will be limited by the power of one core of your computer's CPU.
+ You can see how much free time your real CPU's cores have by looking at
+ the Task Manager in Windows 2000/XP/Vista/7 and the System Monitor
+ in Windows 95/98/ME. Once 100% of the power of your computer's real CPU's one
+ core is used, there is no further way to speed up DOSBox (it will actually
+ start to slow down), unless you reduce the load generated by the non-CPU parts
+ of DOSBox. DOSBox can use only one core of your CPU, so If you have
+ for example a CPU with 4 cores, DOSBox will not be able to use the power
+ of three other cores.
+
+ You can also force the fast behavior by setting cycles=max in the DOSBox
+ configuration file. The DOSBox window will display a line
+ "CPU speed: max 100% cycles" at the top then. This time you won't have to care
+ how much free time your real CPU cores have, because DOSBox will always use
+ 100% of your real CPU's one core. In this mode you can reduce the amount
+ of your real CPU's core usage by CTRL-F11 or raise it with CTRL-F12.
+
+CPU Core (speed up)
+ On x86 architectures you can try to force the usage of a dynamically
+ recompiling core (set core=dynamic in the DOSBox configuration file).
+ This usually gives better results if the auto detection (core=auto) fails.
+ It is best accompanied by cycles=max. But you may also try using it with
+ high amounts of cycles (for example 20000 or more). Note that there might be
+ games that work worse/crash with the dynamic core (so save your game often),
+ or do not work at all!
+
+Graphics emulation (speed up)
+ VGA emulation is a demanding part of DOSBox in terms of actual CPU usage.
+ Increase the number of frames skipped (in increments of one) by pressing
+ CTRL-F8. Your CPU usage should decrease when using a fixed cycle setting,
+ and you will be able to increase cycles with CTRL-F12.
+ You can repeat this until the game runs fast enough for you.
+ Please note that this is a trade-off: you lose in fluidity of video what
+ you gain in speed.
+
+Sound emulation (speed up)
+ You can also try to disable the sound through the setup utility of the game
+ to reduce load on your CPU further. Setting nosound=true in DOSBox's
+ configuration does NOT disable the emulation of sound devices, just
+ the output of sound will be disabled.
+
+Also try to close every program but DOSBox to reserve as much resources
+as possible for DOSBox.
+
+
+Advanced cycles configuration:
+The cycles=auto and cycles=max settings can be parameterized to have
+different startup defaults. The syntax is
+ cycles=auto ["realmode default"] ["protected mode default"%]
+ [limit "cycle limit"]
+ cycles=max ["protected mode default"%] [limit "cycle limit"]
+Example:
+ cycles=auto 5000 80% limit 20000
+ will use cycles=5000 for real mode games, 80% CPU throttling for
+ protected mode games along with a hard cycle limit of 20000
+
+
+
+====================
+11. Troubleshooting:
+====================
+
+General tip:
+ Check messages in the DOSBox status window. See Section 12: "DOSBox Status Window".
+
+DOSBox crashes right after starting it:
+ - use different values for the output= entry in your DOSBox
+ configuration file
+ - try to update your graphics card driver and DirectX
+ - (Linux) set the environment variable SDL_AUDIODRIVER to alsa or oss.
+
+Running a certain game closes DOSBox, crashes with some message or hangs:
+ - see if it works with a default DOSBox installation
+ (unmodified configuration file)
+ - try it with sound disabled (use the sound configuration
+ program that comes with the game, additionally you can
+ set sbtype=none and gus=false in the DOSBox configuration file)
+ - change some entries of the DOSBox configuration file, especially try:
+ core=normal
+ fixed cycles (for example cycles=10000)
+ ems=false
+ xms=false
+ or combinations of the above settings,
+ similar the machine settings that control the emulated chipset and
+ functionality:
+ machine=vesa_nolfb
+ or
+ machine=vgaonly
+ - use loadfix before starting the game
+
+The game exits to the DOSBox prompt with some error message:
+ - read the error message closely and try to locate the error
+ - try the hints at the above sections
+ - mount differently as some games are picky about the locations,
+ for example if you used "mount d d:\oldgames\game" try
+ "mount c d:\oldgames\game" and "mount c d:\oldgames"
+ - if the game requires a CD-ROM be sure you used "-t cdrom" when
+ mounting and try different additional parameters (the ioctl,
+ usecd and label switches, see the appropriate section)
+ - check the file permissions of the game files (remove read-only
+ attributes, add write permissions etc.)
+ - try reinstalling the game within DOSBox
+
+
+
+=========================
+12. DOSBox Status Window:
+=========================
+
+DOSBox's status window contains useful information about your current
+configuration, your actions in DOSBox, errors which occurred and more.
+Check these messages in case you encounter any problems with DOSBox.
+
+To display the DOSBox status window:
+ (Windows) The status window is being started together with main DOSBox window.
+ (Linux) You may have to start DOSBox from a console to see the status window.
+ (Mac OS X) Right click on DOSBox.app, choose "Show Package Contents"->
+ ->enter "Contents"->enter "MacOS"->run "DOSBox"
+
+
+
+=====================================
+13. The configuration (options) file:
+=====================================
+
+The configuration file is automatically created the first time you run DOSBox.
+The file can be found in:
+ (Windows) "Start/WinLogo Menu"->"All Programs"->"DOSBox-0.74-2"->Options
+ (Linux) ~/.dosbox/dosbox-0.74-2.conf
+ (Mac OS X) "~/Library/Preferences/DOSBox 0.74-2 Preferences"
+The file is divided into several sections. Each section starts with a
+[section name] line. The settings are the property=value lines where value can
+be altered to customize DOSBox.
+# and % indicate comment-lines.
+
+
+An extra configuration file can be generated by CONFIG.COM, which can be found
+on the internal DOSBox Z: drive when you start up DOSBox. Look in the Section 4:
+"Internal programs" for usage of CONFIG.COM. You can start DOSBox with
+the -conf switch to load the generated file and use its settings.
+
+DOSBox will load configuration files that are specified with -conf. If none were
+specified, it will try to load "dosbox.conf" from the local directory.
+If there is none, DOSBox will load the user configuration file.
+This file will be created if it doesn't exist.
+
+Important!: In Windows Vista/7 the configuration file won't work correctly
+if it is located in "Windows" or "Program Files" folder or their subfolders,
+or directly on c:\, so the best place for storing extra configuration files is
+for example: C:\oldgames
+
+
+
+======================
+14. The Language File:
+======================
+
+A language file can be generated by CONFIG.COM, which can be found on the
+internal DOSBox Z: drive when you start up DOSBox. Look in the Section 4:
+"Internal programs" for usage of CONFIG.COM.
+Read the language file, and you will hopefully understand how to change it.
+Start DOSBox with the -lang switch to use your new language file.
+Alternatively, you can setup the filename in the configuration file
+in the [dosbox] section. There's a language= entry that can be changed with
+the filelocation.
+
+
+
+========================================
+15. Building your own version of DOSBox:
+========================================
+
+Download the source.
+Check the INSTALL in the source distribution.
+
+
+
+===================
+16. Special thanks:
+===================
+
+See the THANKS file.
+
+
+
+============
+17. Contact:
+============
+
+See the site:
+https://www.dosbox.com
+for an email address (The Crew-page).
+
+
diff --git a/THANKS b/THANKS
new file mode 100644
index 000000000..8b0e5d96f
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,29 @@
+We would like to thank:
+
+
+Vlad R. of the vdmsound project for excellent sound blaster info.
+Tatsuyuki Satoh of the Mame Team for making an excellent FM emulator.
+Jarek Burczynski for the new OPL3 emulator.
+Ken Silverman for his work on an OPL2 emulator.
+
+The Bochs and DOSemu projects which I used for information.
+FreeDOS for ideas in making my shell.
+
+Pierre-Yves Gérardy for hosting the old Beta Board.
+Colin Snover for hosting our forum.
+
+Sourceforge for hosting our homepage and other development tools.
+Mirek Luza, for his moderation of the forums.
+eL_Pusher, DosFreak and MiniMax for their moderation of VOGONS forum.
+
+crazyc, gulikoza, M-HT for their work on the dynrec core.
+
+Jantien for the version management.
+Shawn, Johannes and Marcus for creating the MAC OS X version.
+Jochen for creating the OS/2 version.
+Ido Beeri for the icon.
+ripsaw8080 for his hard debugging work.
+GOG Team for the splash screen.
+All the people who submitted a bug.
+The Beta Testers.
+
diff --git a/VERSION b/VERSION
new file mode 100644
index 000000000..a6e956f06
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.74-2
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644
index 000000000..9f2993559
--- /dev/null
+++ b/acinclude.m4
@@ -0,0 +1,398 @@
+dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS
+dnl
+AC_DEFUN([AM_PATH_SDL],
+[dnl
+dnl Get the cflags and libraries from the sdl-config script
+dnl
+AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)],
+ sdl_prefix="$withval", sdl_prefix="")
+AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)],
+ sdl_exec_prefix="$withval", sdl_exec_prefix="")
+AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program],
+ , enable_sdltest=yes)
+
+ if test x$sdl_exec_prefix != x ; then
+ sdl_args="$sdl_args --exec-prefix=$sdl_exec_prefix"
+ if test x${SDL_CONFIG+set} != xset ; then
+ SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config
+ fi
+ fi
+ if test x$sdl_prefix != x ; then
+ sdl_args="$sdl_args --prefix=$sdl_prefix"
+ if test x${SDL_CONFIG+set} != xset ; then
+ SDL_CONFIG=$sdl_prefix/bin/sdl-config
+ fi
+ fi
+
+ AC_PATH_PROG(SDL_CONFIG, sdl-config, no)
+ min_sdl_version=ifelse([$1], ,0.11.0,$1)
+ AC_MSG_CHECKING(for SDL - version >= $min_sdl_version)
+ no_sdl=""
+ if test "$SDL_CONFIG" = "no" ; then
+ no_sdl=yes
+ else
+ SDL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags`
+ SDL_LIBS=`$SDL_CONFIG $sdlconf_args --libs`
+
+ sdl_major_version=`$SDL_CONFIG $sdl_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+ sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+ sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+ if test "x$enable_sdltest" = "xyes" ; then
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $SDL_CFLAGS"
+ LIBS="$LIBS $SDL_LIBS"
+dnl
+dnl Now check if the installed SDL is sufficiently new. (Also sanity
+dnl checks the results of sdl-config to some extent
+dnl
+ rm -f conf.sdltest
+ AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "SDL.h"
+
+char*
+my_strdup (char *str)
+{
+ char *new_str;
+
+ if (str)
+ {
+ new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char));
+ strcpy (new_str, str);
+ }
+ else
+ new_str = NULL;
+
+ return new_str;
+}
+
+int main (int argc, char *argv[])
+{
+ int major, minor, micro;
+ char *tmp_version;
+
+ /* This hangs on some systems (?)
+ system ("touch conf.sdltest");
+ */
+ { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); }
+
+ /* HP/UX 9 (%@#!) writes to sscanf strings */
+ tmp_version = my_strdup("$min_sdl_version");
+ if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+ printf("%s, bad version string\n", "$min_sdl_version");
+ exit(1);
+ }
+
+ if (($sdl_major_version > major) ||
+ (($sdl_major_version == major) && ($sdl_minor_version > minor)) ||
+ (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro)))
+ {
+ return 0;
+ }
+ else
+ {
+ printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version);
+ printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro);
+ printf("*** best to upgrade to the required version.\n");
+ printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n");
+ printf("*** to point to the correct copy of sdl-config, and remove the file\n");
+ printf("*** config.cache before re-running configure\n");
+ return 1;
+ }
+}
+
+],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ if test "x$no_sdl" = x ; then
+ AC_MSG_RESULT(yes)
+ ifelse([$2], , :, [$2])
+ else
+ AC_MSG_RESULT(no)
+ if test "$SDL_CONFIG" = "no" ; then
+ echo "*** The sdl-config script installed by SDL could not be found"
+ echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in"
+ echo "*** your path, or set the SDL_CONFIG environment variable to the"
+ echo "*** full path to sdl-config."
+ else
+ if test -f conf.sdltest ; then
+ :
+ else
+ echo "*** Could not run SDL test program, checking why..."
+ CFLAGS="$CFLAGS $SDL_CFLAGS"
+ LIBS="$LIBS $SDL_LIBS"
+ AC_TRY_LINK([
+#include <stdio.h>
+#include "SDL.h"
+], [ return 0; ],
+ [ echo "*** The test program compiled, but did not run. This usually means"
+ echo "*** that the run-time linker is not finding SDL or finding the wrong"
+ echo "*** version of SDL. If it is not finding SDL, you'll need to set your"
+ echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+ echo "*** to the installed location Also, make sure you have run ldconfig if that"
+ echo "*** is required on your system"
+ echo "***"
+ echo "*** If you have an old version installed, it is best to remove it, although"
+ echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+ [ echo "*** The test program failed to compile or link. See the file config.log for the"
+ echo "*** exact error that occurred. This usually means SDL was incorrectly installed"
+ echo "*** or that you have moved SDL since it was installed. In the latter case, you"
+ echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ SDL_CFLAGS=""
+ SDL_LIBS=""
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(SDL_CFLAGS)
+ AC_SUBST(SDL_LIBS)
+ rm -f conf.sdltest
+])
+
+dnl Configure Paths for Alsa
+dnl Some modifications by Richard Boulton <richard-alsa@tartarus.org>
+dnl Christopher Lansdown <lansdoct@cs.alfred.edu>
+dnl Jaroslav Kysela <perex@suse.cz>
+dnl Last modification: alsa.m4,v 1.22 2002/05/27 11:14:20 tiwai Exp
+dnl AM_PATH_ALSA([MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for libasound, and define ALSA_CFLAGS and ALSA_LIBS as appropriate.
+dnl enables arguments --with-alsa-prefix=
+dnl --with-alsa-enc-prefix=
+dnl --disable-alsatest (this has no effect, as yet)
+dnl
+dnl For backwards compatibility, if ACTION_IF_NOT_FOUND is not specified,
+dnl and the alsa libraries are not found, a fatal AC_MSG_ERROR() will result.
+dnl
+AC_DEFUN([AM_PATH_ALSA],
+[dnl Save the original CFLAGS, LDFLAGS, and LIBS
+alsa_save_CFLAGS="$CFLAGS"
+alsa_save_LDFLAGS="$LDFLAGS"
+alsa_save_LIBS="$LIBS"
+alsa_found=yes
+
+dnl
+dnl Get the cflags and libraries for alsa
+dnl
+AC_ARG_WITH(alsa-prefix,
+[ --with-alsa-prefix=PFX Prefix where Alsa library is installed(optional)],
+[alsa_prefix="$withval"], [alsa_prefix=""])
+
+AC_ARG_WITH(alsa-inc-prefix,
+[ --with-alsa-inc-prefix=PFX Prefix where include libraries are (optional)],
+[alsa_inc_prefix="$withval"], [alsa_inc_prefix=""])
+
+dnl FIXME: this is not yet implemented
+AC_ARG_ENABLE(alsatest,
+[ --disable-alsatest Do not try to compile and run a test Alsa program],
+[enable_alsatest=no],
+[enable_alsatest=yes])
+
+dnl Add any special include directories
+AC_MSG_CHECKING(for ALSA CFLAGS)
+if test "$alsa_inc_prefix" != "" ; then
+ ALSA_CFLAGS="$ALSA_CFLAGS -I$alsa_inc_prefix"
+ CFLAGS="$CFLAGS -I$alsa_inc_prefix"
+fi
+AC_MSG_RESULT($ALSA_CFLAGS)
+
+dnl add any special lib dirs
+AC_MSG_CHECKING(for ALSA LDFLAGS)
+if test "$alsa_prefix" != "" ; then
+ ALSA_LIBS="$ALSA_LIBS -L$alsa_prefix"
+ LDFLAGS="$LDFLAGS $ALSA_LIBS"
+fi
+
+dnl add the alsa library
+ALSA_LIBS="$ALSA_LIBS -lasound -lm -ldl -lpthread"
+LIBS=`echo $LIBS | sed 's/-lm//'`
+LIBS=`echo $LIBS | sed 's/-ldl//'`
+LIBS=`echo $LIBS | sed 's/-lpthread//'`
+LIBS=`echo $LIBS | sed 's/ //'`
+LIBS="$ALSA_LIBS $LIBS"
+AC_MSG_RESULT($ALSA_LIBS)
+
+dnl Check for a working version of libasound that is of the right version.
+min_alsa_version=ifelse([$1], ,0.1.1,$1)
+AC_MSG_CHECKING(for libasound headers version >= $min_alsa_version)
+no_alsa=""
+ alsa_min_major_version=`echo $min_alsa_version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+ alsa_min_minor_version=`echo $min_alsa_version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+ alsa_min_micro_version=`echo $min_alsa_version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+
+AC_LANG_SAVE
+AC_LANG_C
+AC_TRY_COMPILE([
+#include <alsa/asoundlib.h>
+], [
+/* ensure backward compatibility */
+#if !defined(SND_LIB_MAJOR) && defined(SOUNDLIB_VERSION_MAJOR)
+#define SND_LIB_MAJOR SOUNDLIB_VERSION_MAJOR
+#endif
+#if !defined(SND_LIB_MINOR) && defined(SOUNDLIB_VERSION_MINOR)
+#define SND_LIB_MINOR SOUNDLIB_VERSION_MINOR
+#endif
+#if !defined(SND_LIB_SUBMINOR) && defined(SOUNDLIB_VERSION_SUBMINOR)
+#define SND_LIB_SUBMINOR SOUNDLIB_VERSION_SUBMINOR
+#endif
+
+# if(SND_LIB_MAJOR > $alsa_min_major_version)
+ exit(0);
+# else
+# if(SND_LIB_MAJOR < $alsa_min_major_version)
+# error not present
+# endif
+
+# if(SND_LIB_MINOR > $alsa_min_minor_version)
+ exit(0);
+# else
+# if(SND_LIB_MINOR < $alsa_min_minor_version)
+# error not present
+# endif
+
+# if(SND_LIB_SUBMINOR < $alsa_min_micro_version)
+# error not present
+# endif
+# endif
+# endif
+exit(0);
+],
+ [AC_MSG_RESULT(found.)],
+ [AC_MSG_RESULT(not present.)
+ ifelse([$3], , [AC_MSG_ERROR(Sufficiently new version of libasound not found.)])
+ alsa_found=no]
+)
+AC_LANG_RESTORE
+
+dnl Now that we know that we have the right version, let's see if we have the library and not just the headers.
+AC_CHECK_LIB([asound], [snd_ctl_open],,
+ [ifelse([$3], , [AC_MSG_ERROR(No linkable libasound was found.)])
+ alsa_found=no]
+)
+
+if test "x$alsa_found" = "xyes" ; then
+ ifelse([$2], , :, [$2])
+ LIBS=`echo $LIBS | sed 's/-lasound//g'`
+ LIBS=`echo $LIBS | sed 's/ //'`
+ LIBS="-lasound $LIBS"
+fi
+if test "x$alsa_found" = "xno" ; then
+ ifelse([$3], , :, [$3])
+ CFLAGS="$alsa_save_CFLAGS"
+ LDFLAGS="$alsa_save_LDFLAGS"
+ LIBS="$alsa_save_LIBS"
+ ALSA_CFLAGS=""
+ ALSA_LIBS=""
+fi
+
+dnl That should be it. Now just export out symbols:
+AC_SUBST(ALSA_CFLAGS)
+AC_SUBST(ALSA_LIBS)
+])
+
+AH_TOP([
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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 Library 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.
+ */
+])
+
+AH_BOTTOM([#if C_ATTRIBUTE_ALWAYS_INLINE
+#define INLINE inline __attribute__((always_inline))
+#else
+#define INLINE inline
+#endif])
+
+AH_BOTTOM([#if C_ATTRIBUTE_FASTCALL
+#define DB_FASTCALL __attribute__((fastcall))
+#else
+#define DB_FASTCALL
+#endif])
+
+
+AH_BOTTOM([#if C_HAS_ATTRIBUTE
+#define GCC_ATTRIBUTE(x) __attribute__ ((x))
+#else
+#define GCC_ATTRIBUTE(x) /* attribute not supported */
+#endif])
+
+AH_BOTTOM([#if C_HAS_BUILTIN_EXPECT
+#define GCC_UNLIKELY(x) __builtin_expect((x),0)
+#define GCC_LIKELY(x) __builtin_expect((x),1)
+#else
+#define GCC_UNLIKELY(x) (x)
+#define GCC_LIKELY(x) (x)
+#endif])
+
+AH_BOTTOM([
+typedef double Real64;
+
+#if SIZEOF_UNSIGNED_CHAR != 1
+# error "sizeof (unsigned char) != 1"
+#else
+ typedef unsigned char Bit8u;
+ typedef signed char Bit8s;
+#endif
+
+#if SIZEOF_UNSIGNED_SHORT != 2
+# error "sizeof (unsigned short) != 2"
+#else
+ typedef unsigned short Bit16u;
+ typedef signed short Bit16s;
+#endif
+
+#if SIZEOF_UNSIGNED_INT == 4
+ typedef unsigned int Bit32u;
+ typedef signed int Bit32s;
+#elif SIZEOF_UNSIGNED_LONG == 4
+ typedef unsigned long Bit32u;
+ typedef signed long Bit32s;
+#else
+# error "can't find sizeof(type) of 4 bytes!"
+#endif
+
+#if SIZEOF_UNSIGNED_LONG == 8
+ typedef unsigned long Bit64u;
+ typedef signed long Bit64s;
+#elif SIZEOF_UNSIGNED_LONG_LONG == 8
+ typedef unsigned long long Bit64u;
+ typedef signed long long Bit64s;
+#else
+# error "can't find data type of 8 bytes"
+#endif
+
+#if SIZEOF_INT_P == 4
+ typedef Bit32u Bitu;
+ typedef Bit32s Bits;
+#else
+ typedef Bit64u Bitu;
+ typedef Bit64s Bits;
+#endif
+
+])
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 000000000..4d7d5ba70
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+echo "Generating build information using aclocal, autoheader, automake and autoconf"
+echo "This may take a while ..."
+
+# Regenerate configuration files.
+
+aclocal
+autoheader
+automake --include-deps --add-missing --copy
+autoconf
+
+echo "Now you are ready to run ./configure."
+echo "You can also run ./configure --help for extra features to enable/disable."
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 000000000..a084e926e
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,635 @@
+dnl Init.
+AC_INIT(dosbox,SVN)
+AC_PREREQ(2.50)
+AC_CONFIG_SRCDIR(README)
+
+dnl Detect the canonical host and target build environment
+AC_CANONICAL_HOST
+AC_CANONICAL_BUILD
+
+dnl Setup for automake
+AM_INIT_AUTOMAKE
+AC_CONFIG_HEADER(config.h)
+
+dnl Checks for programs.
+AC_PROG_MAKE_SET
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_CXX
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+
+dnl Some needed libaries for OS2
+dnl perharps join this with the other target depended checks. move them upwards
+if test x$host = xi386-pc-os2-emx ; then
+ CXXFLAGS="$CXXFLAGS -Zmt"
+ LDFLAGS="$LDFLAGS -Zomf -Zmt"
+fi
+
+dnl Check for SDL
+SDL_VERSION=1.2.0
+AM_PATH_SDL($SDL_VERSION,
+ :,
+ AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!])
+)
+LIBS="$LIBS $SDL_LIBS"
+CPPFLAGS="$CPPFLAGS $SDL_CFLAGS"
+
+dnl Check if SDL is 1.2.x (1.3 not supported)
+AC_MSG_CHECKING([SDL version only being 1.2.X])
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+#include "SDL.h"
+void blah(){
+#if SDL_MINOR_VERSION != 2
+#error "Only SDL 1.2 supported"
+#endif
+;
+}
+])],AC_MSG_RESULT([yes]),[
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([Only libSDL 1.2.X supported])])
+
+dnl Checks for header files.
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_C_INLINE
+AC_TYPE_SIZE_T
+AC_STRUCT_TM
+AC_CHECK_SIZEOF(unsigned char)
+AC_CHECK_SIZEOF(unsigned short)
+AC_CHECK_SIZEOF(unsigned int)
+AC_CHECK_SIZEOF(unsigned long)
+AC_CHECK_SIZEOF(unsigned long long)
+AC_CHECK_SIZEOF(int *)
+
+dnl some semi complex check for sys/socket so it works on darwin as well
+AC_CHECK_HEADERS([stdlib.h sys/types.h])
+AC_CHECK_HEADERS([sys/socket.h netinet/in.h pwd.h], [], [],
+[#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+])
+
+dnl check for the socklen_t (darwin doesn't always have it)
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+])],[],[AC_DEFINE([socklen_t],[int],[Define to `int` if you don't have socklen_t])])
+
+AC_MSG_CHECKING(if environ can be included)
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#include <unistd.h>
+#include <stdlib.h>]],[[*environ;]])],
+[AC_MSG_RESULT(yes);AC_DEFINE(ENVIRON_INCLUDED,1,[environ can be included])],AC_MSG_RESULT(no))
+
+AC_MSG_CHECKING(if environ can be linked)
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[extern char ** environ;]],[[*environ;]])],
+[AC_MSG_RESULT(yes);AC_DEFINE(ENVIRON_LINKED,1,[environ can be linked])],AC_MSG_RESULT(no))
+
+AC_MSG_CHECKING([if dirent includes d_type])
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+#include <sys/types.h>
+#include <dirent.h>
+void blah(){
+struct dirent d_test;
+d_test.d_type = 0;
+}])],[AC_MSG_RESULT(yes);AC_DEFINE(DIRENT_HAS_D_TYPE,1,[struct dirent has d_type])],AC_MSG_RESULT(no))
+
+
+dnl Check for powf
+AC_MSG_CHECKING(for powf in libm);
+LIBS_BACKUP=$LIBS;
+LIBS="$LIBS -lm";
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <math.h>]],[[
+ powf(1.0f, 1.0f);
+]])], [AC_MSG_RESULT(yes)], [AC_DEFINE([DB_HAVE_NO_POWF],[1],[libm doesn't include powf])])
+LIBS=$LIBS_BACKUP
+
+dnl Look for clock_gettime, a DB_HAVE_CLOCK_GETTIME is set when present
+AH_TEMPLATE([DB_HAVE_CLOCK_GETTIME],[Determines if the function clock_gettime is available.])
+AC_SEARCH_LIBS([clock_gettime], [rt] , [found_clock_gettime=yes], [found_clock_gettime=no])
+if test x$found_clock_gettime = xyes; then
+ AC_DEFINE(DB_HAVE_CLOCK_GETTIME)
+fi
+
+dnl Checks for libraries.
+
+#Check if the compiler support attributes
+AH_TEMPLATE([C_HAS_ATTRIBUTE],[Determines if the compiler supports attributes for structures.])
+AC_MSG_CHECKING(if compiler allows __attribute__)
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+typedef struct { } __attribute__((packed)) junk;]],
+[[ ]])],[ AC_MSG_RESULT(yes);AC_DEFINE(C_HAS_ATTRIBUTE)],AC_MSG_RESULT(no))
+
+
+#Check if the compiler supports certain attributes
+OLDCFLAGS="$CFLAGS"
+CFLAGS="-Werror"
+
+AH_TEMPLATE([C_ATTRIBUTE_ALWAYS_INLINE],[Determines if the compiler supports always_inline attribute.])
+AC_MSG_CHECKING(if compiler allows __attribute__((always_inline)) )
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([ inline void __attribute__((always_inline)) test(){}
+])],[ AC_MSG_RESULT(yes);AC_DEFINE(C_ATTRIBUTE_ALWAYS_INLINE)],AC_MSG_RESULT(no))
+
+AH_TEMPLATE([C_ATTRIBUTE_FASTCALL],[Determines if the compiler supports fastcall attribute.])
+AC_MSG_CHECKING(if compiler allows __attribute__((fastcall)) )
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([ void __attribute__((fastcall)) test(){}
+])],[ AC_MSG_RESULT(yes);AC_DEFINE(C_ATTRIBUTE_FASTCALL)],AC_MSG_RESULT(no))
+
+
+CFLAGS="$OLDCFLAGS"
+
+
+#Check if the compiler supports __builtin_expect
+#Switch language to c++
+AC_LANG_PUSH(C++)
+AH_TEMPLATE([C_HAS_BUILTIN_EXPECT],[Determines if the compilers supports __builtin_expect for branch prediction.])
+AC_MSG_CHECKING(if compiler allows __builtin_expect)
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[
+int x=10;if( __builtin_expect ((x==1),0) ) ;
+]])], [ AC_MSG_RESULT(yes);AC_DEFINE(C_HAS_BUILTIN_EXPECT)],AC_MSG_RESULT(no))
+#switch language back
+AC_LANG_POP(C++)
+
+dnl test if compiler supports -mno-ms-bitfields as it is bugged
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991
+BACKUP_CFLAGS="$CFLAGS"
+CFLAGS="-mno-ms-bitfields"
+AC_MSG_CHECKING(if compiler supports -mno-ms-bitfields)
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+void blah(){
+;
+}
+])],[
+AC_MSG_RESULT([yes])
+CXXFLAGS="$CXXFLAGS -mno-ms-bitfields"
+],[AC_MSG_RESULT([no])])
+CFLAGS="$BACKUP_CFLAGS"
+
+dnl enable disable alsa and pass it's cflags to CXXFLAGS
+AC_ARG_ENABLE(alsa-midi,
+AC_HELP_STRING([--enable-alsa-midi],[compile with alsa midi support (default yes)]),
+[ case "${enableval}" in
+ yes) alsa_midi=true;;
+ no) alsa_midi=false;;
+esac],
+[alsa_midi=true])
+if test x$alsa_midi = xtrue ; then
+ AM_PATH_ALSA(0.9.0, AC_DEFINE(HAVE_ALSA,1,[Define to 1 to use ALSA for MIDI]) , : )
+ CXXFLAGS="$CXXFLAGS $ALSA_CFLAGS"
+fi
+
+#Check for big endian machine, should #define WORDS_BIGENDIAN if so
+AC_C_BIGENDIAN
+
+#Features to enable/disable
+AH_TEMPLATE(C_DEBUG,[Define to 1 to enable internal debugger, requires libcurses])
+AH_TEMPLATE(C_HEAVY_DEBUG,[Define to 1 to enable heavy debugging, also have to enable C_DEBUG])
+AC_ARG_ENABLE(debug,AC_HELP_STRING([--enable-debug],[Enable debug mode]),[
+ AC_CHECK_HEADER(curses.h,have_curses_h=yes,)
+ AC_CHECK_LIB(curses, initscr, have_curses_lib=yes, , )
+ AC_CHECK_LIB(ncurses, initscr, have_ncurses_lib=yes, , )
+ AC_CHECK_LIB(pdcurses, initscr, have_pdcurses_lib=yes, , )
+
+ if test x$enable_debug = xno; then
+ AC_MSG_RESULT([Debugger not enabled])
+ elif test x$have_ncurses_lib = xyes -a x$have_curses_h = xyes ; then
+ LIBS="$LIBS -lncurses"
+ AC_DEFINE(C_DEBUG,1)
+ if test x$enable_debug = xheavy ; then
+ AC_DEFINE(C_HEAVY_DEBUG,1)
+ fi
+ elif test x$have_curses_lib = xyes -a x$have_curses_h = xyes ; then
+ LIBS="$LIBS -lcurses"
+ AC_DEFINE(C_DEBUG,1)
+ if test x$enable_debug = xheavy ; then
+ AC_DEFINE(C_HEAVY_DEBUG,1)
+ fi
+ elif test x$have_pdcurses_lib = xyes -a x$have_curses_h = xyes ; then
+ LIBS="$LIBS -lpdcurses"
+ AC_DEFINE(C_DEBUG,1)
+ if test x$enable_debug = xheavy ; then
+ AC_DEFINE(C_HEAVY_DEBUG,1)
+ fi
+ else
+ AC_MSG_ERROR([Can't find curses, which is required for debug mode])
+ fi
+],)
+
+AH_TEMPLATE(C_CORE_INLINE,[Define to 1 to use inlined memory functions in cpu core])
+AC_ARG_ENABLE(core-inline,AC_HELP_STRING([--disable-core-inline],[Disable inlined memory handling in CPU Core]),,enable_core_inline=yes)
+AC_MSG_CHECKING(whether memory handling in the CPU Core will be inlined)
+if test x$enable_core_inline = xyes ; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(C_CORE_INLINE,1)
+else
+ AC_MSG_RESULT(no)
+fi
+
+dnl The target cpu checks for dynamic cores
+AH_TEMPLATE(C_TARGETCPU,[The type of cpu this target has])
+AC_MSG_CHECKING(for target cpu type)
+case "$host_cpu" in
+ x86_64 | amd64)
+ AC_DEFINE(C_TARGETCPU,X86_64)
+ AC_MSG_RESULT(x86-64 bit compatible)
+ c_targetcpu="x86_64"
+ c_unalignedmemory=yes
+ ;;
+ i?86)
+ AC_DEFINE(C_TARGETCPU,X86)
+ AC_MSG_RESULT(x86 compatible)
+ c_targetcpu="x86"
+ c_unalignedmemory=yes
+ ;;
+ powerpc*)
+ AC_DEFINE(C_TARGETCPU,POWERPC)
+ AC_MSG_RESULT(Power PC)
+ c_targetcpu="powerpc"
+ c_unalignedmemory=yes
+ ;;
+ m68k*)
+ AC_DEFINE(C_TARGETCPU,M68K)
+ AC_MSG_RESULT(Motorola 68000)
+ c_targetcpu="m68k"
+ c_unalignedmemory=yes
+ ;;
+ armv7l)
+ AC_DEFINE(C_TARGETCPU,ARMV7LE)
+ AC_MSG_RESULT(ARMv7 Little Endian)
+ c_targetcpu="arm"
+ c_unalignedmemory=yes
+ ;;
+ armv6l)
+ AC_DEFINE(C_TARGETCPU,ARMV4LE)
+ AC_MSG_RESULT(ARMv6 Little Endian)
+ c_targetcpu="arm"
+ dnl c_unalignedmemory=yes
+ ;;
+ aarch64)
+ AC_DEFINE(C_TARGETCPU,ARMV8LE)
+ AC_MSG_RESULT(ARMv8 Little Endian 64-bit)
+ c_targetcpu="arm"
+ c_unalignedmemory=yes
+ ;;
+ *)
+ AC_DEFINE(C_TARGETCPU,UNKNOWN)
+ AC_MSG_RESULT(unknown)
+ c_unalignedmemory=no
+ ;;
+esac
+
+dnl check for size of pointer being 4
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+#if SIZEOF_INT_P != 4
+#error size intp is not 4, this not something to worry about
+#endif
+void blah() {
+;
+}
+])],[c_sizep=4],[c_sizep=0])
+
+
+dnl automake 1.14 and upwards rewrite the host to have always 64 bit unless i386 as host is passed
+dnl this can make building a 32 bit executable a bit tricky, as dosbox relies on the host to select the
+dnl dynamic/dynrec core
+AC_MSG_CHECKING([whether Apple user wants to override the build process to produce a 32 bit binary])
+case "$host" in
+ *-*-darwin*)
+ if test x$c_targetcpu = xx86_64 -a x$c_sizep = x4 ; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(C_TARGETCPU,X86)
+ c_targetcpu="x86"
+ else
+ AC_MSG_RESULT(no)
+ fi
+ ;;
+ *)
+ AC_MSG_RESULT([no, not on Apple])
+ ;;
+esac
+
+AC_ARG_ENABLE(dynamic-core,AC_HELP_STRING([--disable-dynamic-core],[Disable all dynamic cores]),,enable_dynamic_core=yes)
+
+AH_TEMPLATE(C_DYNAMIC_X86,[Define to 1 to use x86 dynamic cpu core])
+AC_ARG_ENABLE(dynamic-x86,AC_HELP_STRING([--disable-dynamic-x86],[Disable x86 dynamic cpu core]),,enable_dynamic_x86=yes)
+AC_MSG_CHECKING(whether x86 dynamic cpu core will be enabled)
+if test x$enable_dynamic_x86 = xno -o x$enable_dynamic_core = xno; then
+ AC_MSG_RESULT(no)
+else
+ if test x$c_targetcpu = xx86 ; then
+ AC_DEFINE(C_DYNAMIC_X86,1)
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+fi
+
+AH_TEMPLATE(C_DYNREC,[Define to 1 to use recompiling cpu core. Can not be used together with the dynamic-x86 core])
+AC_ARG_ENABLE(dynrec,AC_HELP_STRING([--disable-dynrec],[Disable recompiling cpu core]),,enable_dynrec=yes)
+AC_MSG_CHECKING(whether recompiling cpu core will be enabled)
+if test x$enable_dynrec = xno -o x$enable_dynamic_core = xno; then
+ AC_MSG_RESULT(no)
+else
+dnl x86 only enable it if dynamic-x86 is disabled.
+ if test x$c_targetcpu = xx86 ; then
+ if test x$enable_dynamic_x86 = xno ; then
+ AC_DEFINE(C_DYNREC,1)
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT([no, using dynamic-x86])
+ fi
+ else
+ if test x$c_targetcpu = xx86_64 ; then
+ AC_DEFINE(C_DYNREC,1)
+ AC_MSG_RESULT(yes)
+ else
+ if test x$c_targetcpu = xarm ; then
+ AC_DEFINE(C_DYNREC,1)
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+ fi
+ fi
+fi
+
+AH_TEMPLATE(C_FPU,[Define to 1 to enable floating point emulation])
+AC_ARG_ENABLE(fpu,AC_HELP_STRING([--disable-fpu],[Disable fpu support]),,enable_fpu=yes)
+AC_MSG_CHECKING(whether fpu emulation will be enabled)
+if test x$enable_fpu = xyes ; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(C_FPU,1)
+else
+ AC_MSG_RESULT(no)
+fi
+
+AH_TEMPLATE(C_FPU_X86,[Define to 1 to use a x86/x64 assembly fpu core])
+AC_ARG_ENABLE(fpu-x86,AC_HELP_STRING([--disable-fpu-x86],[Disable x86 assembly fpu core]),,enable_fpu_x86=yes)
+AC_ARG_ENABLE(fpu-x64,AC_HELP_STRING([--disable-fpu-x64],[Disable x64 assembly fpu core]),,enable_fpu_x64=yes)
+AC_MSG_CHECKING(whether the x86/x64 assembly fpu core will be enabled)
+if test x$enable_fpu_x86 = xno -o x$enable_fpu_x64 = xno; then
+ AC_MSG_RESULT(no)
+else
+ if test x$enable_fpu = xyes; then
+ if test x$c_targetcpu = xx86 -o x$c_targetcpu = xx86_64; then
+ AC_DEFINE(C_FPU_X86,1)
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+ else
+ AC_MSG_RESULT(no)
+ fi
+fi
+
+AH_TEMPLATE(C_UNALIGNED_MEMORY,[Define to 1 to use a unaligned memory access])
+AC_ARG_ENABLE(unaligned_memory,AC_HELP_STRING([--disable-unaligned-memory],[Disable unaligned memory access]),,enable_unaligned_memory=yes)
+AC_MSG_CHECKING(whether to enable unaligned memory access)
+if test x$enable_unaligned_memory = xyes -a x$c_unalignedmemory = xyes; then
+ AC_DEFINE(C_UNALIGNED_MEMORY,1)
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+
+AH_TEMPLATE(C_SSHOT,[Define to 1 to enable screenshots, requires libpng])
+AC_ARG_ENABLE(screenshots,AC_HELP_STRING([--disable-screenshots],[Disable screenshots and movie recording]),,enable_screenshots=yes)
+AC_CHECK_HEADER(png.h,have_png_h=yes,)
+AC_CHECK_LIB(png, png_get_io_ptr, have_png_lib=yes, ,-lz)
+AC_MSG_CHECKING([whether screenshots will be enabled])
+if test x$enable_screenshots = xyes; then
+ if test x$have_png_lib = xyes -a x$have_png_h = xyes ; then
+ LIBS="$LIBS -lpng -lz"
+ AC_DEFINE(C_SSHOT,1)
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no, can't find libpng.])
+ fi
+else
+ AC_MSG_RESULT([no])
+fi
+
+AH_TEMPLATE(C_MODEM,[Define to 1 to enable internal modem support, requires SDL_net])
+AH_TEMPLATE(C_IPX,[Define to 1 to enable IPX over Internet networking, requires SDL_net])
+AC_CHECK_HEADER(SDL_net.h,have_sdl_net_h=yes,)
+
+if test x$host = xi386-pc-os2-emx ; then
+ AC_MSG_CHECKING(for SDLNet_Init in SDL_net);
+ LIBS_BACKUP=$LIBS;
+ LIBS="$LIBS -lSDL_Net";
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <SDL_Net.h>]],[[
+ SDLNet_Init ();
+ ]])], [AC_MSG_RESULT(yes); have_sdl_net_lib=yes], AC_MSG_RESULT(no))
+ LIBS=$LIBS_BACKUP
+else
+AC_CHECK_LIB(SDL_net, SDLNet_Init, have_sdl_net_lib=yes, , )
+fi
+if test x$have_sdl_net_lib = xyes -a x$have_sdl_net_h = xyes ; then
+ LIBS="$LIBS -lSDL_net"
+ AC_DEFINE(C_MODEM,1)
+ AC_DEFINE(C_IPX,1)
+else
+ AC_MSG_WARN([Can't find SDL_net, internal modem and ipx disabled])
+fi
+
+AH_TEMPLATE(C_X11_XKB,[define to 1 if you have XKBlib.h and X11 lib])
+AC_CHECK_LIB(X11, main, have_x11_lib=yes, have_x11_lib=no, )
+AC_CHECK_HEADER(X11/XKBlib.h, have_x11_h=yes, have_x11_h=no, )
+AC_MSG_CHECKING(for XKBlib support)
+if test x$have_x11_lib = xyes -a x$have_x11_h = xyes ; then
+ LIBS="$LIBS -lX11"
+ AC_DEFINE(C_X11_XKB,1)
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+
+AH_TEMPLATE(C_OPENGL,[Define to 1 to use opengl display output support])
+AC_CHECK_LIB(GL, main, have_gl_lib=yes, have_gl_lib=no , )
+AC_CHECK_LIB(opengl32, main, have_opengl32_lib=yes,have_opengl32_lib=no , )
+AC_CHECK_HEADER(GL/gl.h, have_gl_h=yes , have_gl_h=no , )
+AC_ARG_ENABLE(opengl,AC_HELP_STRING([--disable-opengl],[Disable opengl support]),,enable_opengl=yes)
+AC_MSG_CHECKING(whether opengl display output will be enabled)
+if test x$enable_opengl = xyes; then
+case "$host" in
+ *-*-darwin*)
+ AC_MSG_RESULT(yes)
+ LIBS="$LIBS -framework OpenGL"
+ AC_DEFINE(C_OPENGL,1)
+ ;;
+ *)
+ if test x$have_gl_h = xyes -a x$have_gl_lib = xyes ; then
+ AC_MSG_RESULT(yes)
+ LIBS="$LIBS -lGL"
+ AC_DEFINE(C_OPENGL,1)
+ elif test x$have_gl_h = xyes -a x$have_opengl32_lib = xyes ; then
+ AC_MSG_RESULT(yes)
+ LIBS="$LIBS -lopengl32"
+ AC_DEFINE(C_OPENGL,1)
+ else
+ AC_MSG_RESULT(no)
+ fi
+ ;;
+esac
+else
+ AC_MSG_RESULT(no)
+fi
+
+AH_TEMPLATE(C_DDRAW,[Define to 1 to enable output=ddraw (Win32)])
+AC_ARG_ENABLE(ddraw,AC_HELP_STRING([--disable-ddraw],[Disable ddraw support]),,enable_ddraw=yes)
+AC_CHECK_HEADER(ddraw.h, have_ddraw_h=yes , have_ddraw_h=no , )
+AC_MSG_CHECKING(whether ddraw display output will be enabled)
+if test x$enable_ddraw = xyes; then
+case "$host" in
+ *-*-cygwin* | *-*-mingw32*)
+ if test x$have_ddraw_h = xyes ; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(C_DDRAW,1)
+ else
+ AC_MSG_RESULT(no)
+ fi
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+esac
+fi
+
+AH_TEMPLATE(C_SDL_SOUND,[Define to 1 to enable SDL_sound support])
+AC_CHECK_HEADER(SDL_sound.h,have_SDL_sound_h=yes,)
+AC_CHECK_LIB(SDL_sound, Sound_Init, have_SDL_sound_init=yes,,)
+AC_CHECK_LIB(SDL_sound, Sound_Seek, have_SDL_sound_seek=yes,,)
+if test x$have_SDL_sound_h = xyes -a x$have_SDL_sound_init = xyes ; then
+ if test x$have_SDL_sound_seek = xyes ; then
+ LIBS="-lSDL_sound $LIBS"
+ AC_DEFINE(C_SDL_SOUND,1)
+ else
+ AC_MSG_WARN([Can't find SoundSeek in libSDL_Sound, libSDL_sound support disabled])
+ fi
+else
+ AC_MSG_WARN([Can't find libSDL_sound, libSDL_sound support disabled])
+fi
+
+dnl Check for mprotect. Needed for 64 bits linux
+AH_TEMPLATE(C_HAVE_MPROTECT,[Define to 1 if you have the mprotect function])
+AC_CHECK_HEADER([sys/mman.h], [
+AC_CHECK_FUNC([mprotect],[AC_DEFINE(C_HAVE_MPROTECT,1)])
+])
+
+dnl Check for realpath. Used on Linux
+AC_CHECK_FUNCS([realpath])
+
+dnl Setpriority
+AH_TEMPLATE(C_SET_PRIORITY,[Define to 1 if you have setpriority support])
+AC_MSG_CHECKING(for setpriority support)
+AC_LINK_IFELSE([AC_LANG_SOURCE([
+#include <sys/resource.h>
+int main(int argc,char * argv[]) {
+ return setpriority (PRIO_PROCESS, 0,PRIO_MIN+PRIO_MAX);
+};
+])],AC_MSG_RESULT(yes);AC_DEFINE(C_SET_PRIORITY,1),AC_MSG_RESULT(no))
+
+
+dnl Some target detection and actions for them
+case "$host" in
+ *-*-cygwin* | *-*-mingw32*)
+ LIBS="$LIBS -lwinmm"
+ AC_DEFINE(C_DIRECTSERIAL, 1, [ Define to 1 if you want serial passthrough support (Win32, Posix and OS/2 only).])
+ if test x$have_sdl_net_lib = xyes -a x$have_sdl_net_h = xyes ; then
+ LIBS="$LIBS -lws2_32"
+ fi
+ ;;
+ *-*-darwin*)
+ dnl We have a problem here: both Mac OS X and Darwin report
+ dnl the same signature "powerpc-apple-darwin*" - so we have
+ dnl to do more to distinguish them.
+ dnl For now I am lazy and do not add proper detection code.
+ AC_DEFINE(MACOSX, 1, [Compiling on Mac OS X])
+ LIBS="$LIBS -framework CoreMIDI -framework AudioUnit -framework AudioToolbox"
+ AC_DEFINE(C_DIRECTSERIAL, 1, [ Define to 1 if you want serial passthrough support (Win32, Posix and OS/2).])
+ ;;
+ *-*-linux*)
+ AC_DEFINE(LINUX, 1, [Compiling on GNU/Linux])
+ AC_DEFINE(C_DIRECTSERIAL, 1, [ Define to 1 if you want serial passthrough support (Win32, Posix and OS/2).])
+ ;;
+ *-*-freebsd* | *-*-dragonfly* | *-*-netbsd* | *-*-openbsd*)
+ dnl Disabled directserial for now. It doesn't do anything without
+ dnl specifying an extra ifdef in directserial_posix.*
+ dnl directserial detection should be rewritten to test for the needed
+ dnl functions and headers. I currently do not know
+ dnl which ones are needed for BSD
+ AC_DEFINE(BSD, 1, [Compiling on BSD])
+ AC_DEFINE(C_DIRECTSERIAL, 1, [ Define to 1 if you want serial passthrough support (Win32, Posix and OS/2).])
+ ;;
+ *-*-os2-emx*)
+ AC_DEFINE(OS2, 1, [Compiling on OS/2 EMX])
+ AC_DEFINE(C_DIRECTSERIAL, 1, [ Define to 1 if you want serial passthrough support (Win32, Posix and OS/2).])
+ ;;
+esac
+
+dnl Some stuff for the icon.
+case "$host" in
+ *-*-cygwin* | *-*-mingw32*)
+ dnl Some stuff for the ico
+ AC_CHECK_TOOL(WINDRES, windres, :)
+ ;;
+ *)
+ WINDRES=":"
+ ;;
+esac
+ AM_CONDITIONAL(HAVE_WINDRES, test "x$WINDRES" != "x:")
+ AC_SUBST(WINDRES)
+
+
+AC_CONFIG_FILES([
+Makefile
+src/Makefile
+src/cpu/Makefile
+src/cpu/core_full/Makefile
+src/cpu/core_normal/Makefile
+src/cpu/core_dyn_x86/Makefile
+src/cpu/core_dynrec/Makefile
+src/debug/Makefile
+src/dos/Makefile
+src/fpu/Makefile
+src/gui/Makefile
+src/hardware/Makefile
+src/hardware/mame/Makefile
+src/hardware/serialport/Makefile
+src/ints/Makefile
+src/libs/Makefile
+src/libs/zmbv/Makefile
+src/libs/gui_tk/Makefile
+src/misc/Makefile
+src/shell/Makefile
+src/platform/Makefile
+src/platform/visualc/Makefile
+visualc_net/Makefile
+include/Makefile
+docs/Makefile
+])
+AC_OUTPUT
diff --git a/docs/Makefile.am b/docs/Makefile.am
new file mode 100644
index 000000000..682aab2a2
--- /dev/null
+++ b/docs/Makefile.am
@@ -0,0 +1,8 @@
+# Main Makefile for DOSBox
+
+man_MANS = dosbox.1
+EXTRA_DIST = $(man_MANS) README.video PORTING
+
+
+
+
diff --git a/docs/PORTING b/docs/PORTING
new file mode 100644
index 000000000..4a47607e6
--- /dev/null
+++ b/docs/PORTING
@@ -0,0 +1,50 @@
+Some notes about porting DOSBox to systems with certain restrictions,
+like handheld devices.
+
+General:
+ - depending on where you start off with the port, assure that the
+ config.h entries are correct/exhausting, like GCC_ATTRIBUTE is
+ required (struct packing) but is undefined if you base the port
+ on msvc sources which have a special config.h
+
+If memory is a constraint:
+ - in paging.h out-comment the USE_FULL_TLB define to enable special
+ TLB linking code that uses less memory
+ drawback: none (the code is not heavily tested though)
+ gain: reduces memory requirements about ~15mb
+ - in render.h lower the scaler integration:
+ #define RENDER_USE_ADVANCED_SCALERS 1
+ or
+ #define RENDER_USE_ADVANCED_SCALERS 0
+ drawback: complex scalers and the scaler cache are disabled,
+ be sure to test if this affects speed!
+ with define RENDER_USE_ADVANCED_SCALERS==0 most simple
+ scalers are disabled as well, some graphics modes won't
+ work due to reduced cache sizes
+ gain: ~2mb with RENDER_USE_ADVANCED_SCALERS==1
+ ~5mb with RENDER_USE_ADVANCED_SCALERS==0
+ - in dos_system.h reduce the drive cache entries:
+ #define MAX_OPENDIRS 256
+ drawback: some apps might not work with large directory trees
+ gain: ~1mb per mounted drive
+ - remove the GUS emulation (gus.cpp, especially GUSRam[1024*1024] )
+ drawback: no gravis ultrasound
+ gain: reduces memory requirements about 1mb
+ - reduce the size of the emulated graphics memory:
+ see the memory sizing in SVGA_Setup_*, especially the defaults
+ in vga_s3.cpp's SVGA_Setup_S3Trio
+ drawback: some graphics modes won't work then
+ gain: reduces memory requirements
+ TODO: fully check this, introduce hard limits
+
+If speed is a constraint:
+ - see if the simple core is faster, possibly remove the normal core
+ set the simple core as default
+ drawback: one game is known to not work with the simple core;
+ the simple core does only work for games which don't use paging
+ (when paging is requested the normal core is used automatically)
+ gain: the simple core should be somewhat faster
+ TODO: add possibility to easily remove the normal core, use fullcore fallback
+ - raise the default frameskip value
+ drawback: minor graphics smoothness loss for some games (video playback)
+ gain: reduces graphics load
diff --git a/docs/README.video b/docs/README.video
new file mode 100644
index 000000000..1d15b4f9c
--- /dev/null
+++ b/docs/README.video
@@ -0,0 +1,35 @@
+Starting with version 0.65, DOSBox allows you to create movies out of screen
+output.
+
+To record a movie, you have to press CTRL-ALT-F5.
+To stop/end the recording, you have to press CTRL-ALT-F5 again.
+
+To play the recorded movie, you need a movie player which can handle the
+ZMBV codec. MS Windows users can find this codec in the start menu entry of
+DOSBox. Users of Linux and other OSes should look for a movie player that
+uses the ffmpeg libary (you may need to update or ask your distribution to
+upgrade).
+
+FAQ:
+Q: During the display of the movies the sound is lagging.
+A: Check your display properties to see whether your refresh rate is set to
+at least 70 hz. Try playing the movie in virtualdub (http://virtualdub.sf.net)
+
+Q: Why does the resulting movie consist of multiple files?
+A: Each time the game changes resolution, DOSBox creates a new movie file,
+because a movie file can only contain one resolution.
+
+Q: Can I set the cycles higher than my PC can handle during recording?
+A: Yes. During recording, the game might play slowly and stuttering, but the
+resulting movie should play at the intended speed and have no stuttering.
+
+Q: CTRL-ALT-F5 switches to the console under linux.
+A: 1. Start DOSBox like this: dosbox -startmapper
+ 2. Click on Video, click on Add
+ 3. Press the key you want (for example scroll lock or printscreen)
+ 4. Click exit.
+ 5. You can make movies by pressing scroll lock or whichever key you
+ selected.
+
+Q: The colours are wrong and I'm using 64 bit windows
+A: Look here: http://vogons.zetafleet.com/viewtopic.php?t=12133
diff --git a/docs/dosbox.1 b/docs/dosbox.1
new file mode 100644
index 000000000..72c2459b9
--- /dev/null
+++ b/docs/dosbox.1
@@ -0,0 +1,391 @@
+.\" Hey, EMACS: -*- nroff -*-
+.TH DOSBOX 1 "Sep 08, 2015"
+.\" Please adjust this date whenever revising the manpage.
+.SH NAME
+dosbox \- an x86/DOS emulator with sound/graphics
+.SH SYNOPSIS
+.B dosbox
+.B [\-fullscreen]
+.B [\-startmapper]
+.B [\-noautoexec]
+.B [\-securemode]
+.B [\-userconf]
+.BI "[\-scaler " scaler ]
+.BI "[\-forcescaler " scaler ]
+.BI "[\-conf " configfile ]
+.BI "[\-lang " langfile ]
+.BI "[\-machine " machinetype ]
+.BI "[\-socket " socketnumber ]
+.BI "[\-c " command ]
+.B [\-exit]
+.B [file]
+.LP
+.B dosbox \-version
+.LP
+.BI "dosbox \-editconf" " program"
+.LP
+.BI "dosbox \-opencaptures" " program"
+.LP
+.B dosbox \-printconf
+.LP
+.B dosbox \-eraseconf
+.LP
+.B dosbox \-resetconf
+.LP
+.B dosbox \-erasemapper
+.LP
+.B dosbox \-resetmapper
+.SH DESCRIPTION
+This manual page briefly documents
+.BR "dosbox" ", an x86/DOS emulator."
+.LP
+.RB "The optional " file " argument should be a DOS executable or a"
+directory. If it is a dos executable (.com .exe .bat) the program will
+run automatically. If it is a directory, a DOS session will run with
+the directory mounted as C:\\.
+.LP
+.RI "For an introduction type " INTRO
+.RB "inside " dosbox .
+.SH OPTIONS
+A summary of options is included below.
+.TP
+.B \-fullscreen
+.RB "Start " dosbox " in fullscreen mode."
+.TP
+.B \-startmapper
+.RB "Start the internal keymapper on startup of " dosbox ". You can use it to change the keys " dosbox " uses."
+.TP
+.B \-noautoexec
+Skips the [autoexec] section of the loaded configuration file.
+.TP
+.B \-securemode
+.RB "Same as " \-noautoexec ", but adds " "config.com \-securemode"
+at the end of
+.I AUTOEXEC.BAT
+(which in turn disables any changes to how the drives are mounted
+.RB "inside " dosbox )
+.TP
+.B \-userconf
+Load the configuration file located in ~/.dosbox. Can be combined with
+.RB "the " \-conf " option."
+.TP
+.BI \-scaler " scaler"
+.RI "Uses the graphical scaler specified by " scaler ". See the configuration"
+file for the available scalers
+.TP
+.BI \-forcescaler " scaler"
+.RB "Similar to the " \-scaler " parameter, but tries to force usage of"
+the specified scaler even if it might not fit.
+.TP
+.BI \-conf " configfile"
+.RB "Start " dosbox " with the options specified in "
+.IR configfile ". This file has a section in which you can put commands you "
+wish to execute on startup. Multiple
+.IR configfiles " can be present at the commandline."
+.TP
+.BI \-lang " langfile"
+.RB "Start " dosbox " with the language specified in "
+.IR langfile .
+.TP
+.BI \-machine " machinetype"
+.RB "Setup " dosbox " to emulate a specific type of machine."
+.RI "Valid choices are: " "hercules, cga, tandy, pcjr, ega, vgaonly, svga_s3(default), svga_et3000, svga_et4000, svga_paradise, vesa_nolfb, vesa_oldvbe".
+The machinetype has influence on both the videocard and the available
+soundcards.
+.TP
+.BI \-socket " socketnumber"
+.RI "Passes the socket number " socketnumber " to the nullmodem emulation. See README for details."
+.TP
+.BI \-c " command"
+.RI "Runs the specified " command " before running "
+.BR file .
+.RI "Multiple commands can be specified. Each " command " should start with "
+.BR \-c " though. A command can be:"
+an Internal Program, a DOS command or an executable on a mounted drive.
+.TP
+.B "\-exit "
+.BR "dosbox" " will close itself when the DOS program specified by "file " ends."
+.TP
+.B \-version
+Output version information and exit. Useful for frontends.
+.TP
+.BI \-editconf " program"
+.RI calls " program" " with as first parameter the configuration file."
+You can specify this command more than once. In this case it will
+.RI " move to second " program " if the first one fails to start."
+.TP
+.BI \-opencaptures " program"
+.RI "calls " program " with as first parameter the location of the captures folder."
+.TP
+.B \-printconf
+prints the location of the default configuration file.
+.TP
+.B \-eraseconf, \-resetconf
+removes the default configuration file.
+.TP
+.B \-erasemapper, \-resetmapper
+removes the mapperfile configured in the clean default configuration file.
+.SH "INTERNAL COMMANDS"
+.B dosbox
+supports most of the DOS commands found in command.com. In addition, the
+following extra commands are available:
+.HP
+.BI "MOUNT [\-t " type "] [\-size " size ]
+.I driveletter sourcedirectory
+.B [\-ioctl]
+.BI "[\-usecd " number "] [\-label " drivelabel "] [\-freesize " freesize ]
+.LP
+.B MOUNT \-cd
+.LP
+.B MOUNT \-u driveletter
+.LP
+.RB "Program to mount local directories as drives inside " dosbox .
+.RS
+.TP
+.I driveletter
+The driveletter inside dosbox (eg. C).
+.TP
+.I sourcedirectory
+The local directory you want to have inside dosbox.
+.TP
+.BI \-t " type"
+Type of the mounted directory. Supported are: dir (standard), floppy, cdrom.
+.TP
+.BI \-size " drivesize"
+Sets the size of the drive. See the examples in the README for details.
+.TP
+.BI \-freesize " size_in_mb"
+Sets the amount of free space available on a drive in MB's. This is a more
+.RB "simple version of " \-size .
+.TP
+.BI \-label " drivelabel"
+.RI "Sets the name of the drive to " drivelabel ". Needed on some"
+systems if the cd label isn't read correctly. Useful when a
+program can't find its cdrom. If you don't specify a label and no
+.RB "lowlevel support is selected (" "\-usecd #" " and/or " "\-ioctl/aspi" "):"
+.RS
+.LP
+For win32: label is extracted from "Real Drive".
+.TP
+For Linux: label is set to NO_LABEL.
+.TP
+If you do specify a label this label will be kept as long as the drive
+is mounted. It will not be updated !!
+.RE
+.TP
+.B \-ioctl
+Forces to use ioctl commands.
+.TP
+.BI \-usecd " number"
+Forces to use SDL cdrom support for drive number.
+.IR Number " can be found by "
+.BR \-cd ".
+.TP
+.B \-cd
+.RB "Displays all detected cdrom drives and their numbers. Use with " \-usecd "."
+.TP
+.B \-u
+Unmounts a mounted drive. Doesn't work on virtual Drives (like Z:\\)
+.RE
+.PP
+.B "Example:"
+.TP
+.RB "To mount your /home/dos/dosgames directory as C drive in " dosbox :
+.RS
+mount c /home/dos/dosgames
+.RE
+.TP
+.B MEM
+.LP
+Display the amount of free memory
+.TP
+.B CONFIG [\-writeconf] [\-writelang] file
+.LP
+.B CONFIG \-securemode
+.LP
+.RB "Write the current configuration or language settings to " file ,
+which is located on the local filesystem. Not a mounted drive in
+.BR dosbox .
+.RS
+.TP
+.B \-securemode
+.RB "Switches " dosbox " to a more secure mode. In this mode the"
+.RI "internal commands " MOUNT ", " IMGMOUNT " and " BOOT " won\'t work."
+It\'s not possible
+either to create a new configfile or languagefile in this mode.
+(Warning you can only undo this mode by restarting
+.BR dosbox .)
+.RE
+.LP
+The configuration file controls various settings of
+.BR dosbox ": The amount of emulated memory,"
+the emulated soundcards and many
+.RI "more things. It further allows access to " AUTOEXEC.BAT .
+.LP
+The language file controls all visible output of the internal commands and
+the internal dos.
+.RB "See the section " FILES " for more information."
+.TP
+.B LOADFIX [\-size] [programname] [parameters]
+.LP
+.B LOADFIX \-f
+.LP
+Program to reduce the amount of memory available. Useful for old programs which don't expect much memory to be free.
+.RS
+.TP
+.B [programname]
+The name of the program which is executed after loadfix eats up its memory.
+.TP
+.B [parameters]
+.RB "Parameters given to the " programname " executable."
+.TP
+.B \-size
+The amount of memory to eat up (in kb). Example \-32, \-64 or \-128
+.TP
+.B \-f
+Frees all memory eaten up by loadfix.
+.RE
+.TP
+.B RESCAN [\-All] [Drive:]
+.LP
+.RB "Make " dosbox " reread the directory structure. Useful if you changed
+.RB "something on a mounted drive outside " dosbox ".(CTRL\-F4 does"
+this as well!)
+.RS
+.TP
+.B \-All
+.R Reread directory structure for all drives.
+.TP
+.B Drive:
+.RB "Reread directory structure for drive " Drive:
+.RE
+.LP
+.RB "If both " \-All " and " Drive: " are missing, then the current drive is used.
+.TP
+.B IMGMOUNT
+.LP
+.RB "A utility to mount disk images and CD\(hyROM images in " dosbox .
+.TP
+.RB "Read the " README " of " dosbox " for the full and correct syntax."
+.RE
+.TP
+.B BOOT
+.LP
+Boot will start floppy images or hard disk images independent of the
+.RB "operating system emulation offered by " dosbox ". This will allow you to play booter floppies or boot to other operating systems inside " dosbox .
+.TP
+.RB "Read the " README " of " dosbox " for the full and correct syntax."
+.RE
+.TP
+.B IPX
+.LP
+.RB "You need to enable IPX networking in the configuration file of " dosbox .
+.RB "All of the IPX networking is managed through the internal " dosbox " program
+.BR IPXNET ". For help on the IPX networking from inside " dosbox ", type"
+.BR "IPXNET HELP" " and the program will list out the commands and relevant documentation."
+.TP
+.RB "Read the " README " of " dosbox " for the full and correct syntax."
+.RE
+.TP
+.B KEYB
+.LP
+Keyb can change the keyboardlayout and the codepage used inside dosbox.
+.TP
+.RB "Read the " README " of " dosbox " for the full and correct syntax."
+.RE
+.SH FILES
+Configuration and language files use a format similar to Windows .ini files.
+If no configfile is specified at the commandline, a file named
+.BR dosbox.conf " (if present in the current directory) will be"
+loaded automatically. If a configfile is specified at the commandline
+that one will be used instead. If no configfile is specified or found
+in the current directory
+.RB " then dosbox will load one from " ~/.dosbox/ ". It will try to create "
+one if there is none.
+.SH "SPECIAL KEYS"
+.TP 12m
+.IP ALT\-ENTER
+Go full screen and back.
+.IP ALT\-PAUSE
+Pause emulation.
+.IP CTRL\-F1
+Start the keymapper.
+.IP CTRL\-ALT\-F5
+Start/Stop creating a movie of the screen.
+.IP CTRL\-F4
+Swap mounted disk\(hyimage (Only used with imgmount). Update directory cache
+for all drives!
+.IP CTRL\-F5
+Save a screenshot.(png)
+.IP CTRL\-F6
+Start/Stop recording sound output to a wave file.
+.IP CTRL\-ALT\-F7
+Start/Stop recording of OPL commands.
+.IP CTRL\-ALT\-F8
+Start/Stop the recording of raw MIDI commands.
+.IP CTRL\-F7
+Decrease frameskip.
+.IP CTRL\-F8
+Increase frameskip.
+.IP CTRL\-F9
+Kill dosbox.
+.IP CTRL\-F10
+Capture/Release the mouse.
+.IP CTRL\-F11
+Slow down emulation (Increase dosbox Cycles).
+.IP CTRL\-F12
+Speed up emulation (Decrease dosbox Cycles).
+.IP ALT\-F12
+Unlock speed (turbo button).
+.PP
+These are the default keybindings. They can be changed in the keymapper.
+.PP
+Saved/recorded files can be found in current_directory/capture
+(can be changed in the configfile).
+.RB "The directory has to exist prior to starting " dosbox " else nothing"
+gets saved/recorded !
+.PP
+.BR "Note: " "Once you increase your " dosbox " cycles beyond your computer's maximum
+capacity, it will produce the same effect as slowing down the emulation.
+This maximum will vary from computer to computer, there is no standard.
+.SH "SYSTEM REQUIREMENTS"
+Fast machine. My guess would be Pentium\-2 400+ to get decent emulation
+of games written for an 286 machine.
+For protected mode games a 1 Ghz machine is recommended and don't expect
+them to run fast though!! Be sure to read the next section on how to speed
+it up somewhat.
+.SS "To run resource\-demanding games"
+.BR dosbox " emulates the CPU, the sound and graphic cards, and some other"
+.RB " stuff, all at the same time. You can overclock " dosbox " by using CTRL\-F12, but"
+you'll be limited by the power of your actual CPU. You can see how much free
+time your true CPU has by various utils (top). Once 100% of your real CPU time is
+.RB "used there is no further way to speed up " dosbox " unless you reduce the load"
+.RB "generated by the non\-CPU parts of " dosbox .
+.PP
+So:
+.PP
+.RB "Close every program but " dosbox .
+.PP
+.RB "Overclock " dosbox " until 100% of your CPU is used.(CTRL\-F12)"
+.PP
+.RB "Since VGA emulation is the most demanding part of " dosbox " in terms of actual"
+CPU usage, we'll start here. Increase the number of frames skipped (in
+increments of one) by pressing CTRL\-F8. Your CPU usage should decrease.
+Go back one step and repeat this until the game runs fast enough for you.
+Please note that this is a trade off: you lose in fluidity of video what you
+gain in speed.
+.SH NOTES
+.RB "While we hope that, one day, " dosbox " will run virtually all programs ever made for the PC..."
+.RB "we are not there yet. At present, " dosbox " run on a 1.7 Gigahertz PC is roughly the equivalent of a 25MHz 386 PC."
+While the 0.60 release has added support for "protected mode" allowing for more complex and recent programs,
+but note that this support is early in development and nowhere near as complete as the support for 386 real\-mode
+games (or earlier). Also note that "protected mode" games need substantially more resources and may
+.RB "require a much faster processor for you to run it properly in " dosbox .
+.SH BUGS
+Not all DOS programs work properly.
+.BR dosbox " will exit without warning if an error occurred."
+.SH "SEE ALSO"
+The README in /usr/share/doc/dosbox
+.SH AUTHOR
+This manual page was written by Peter Veenstra <H.P.Veenstra@student.rug.nl> and James Oakley <jfunk@funktronics.ca>,
+for the Debian system (but may be used by others).
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 000000000..570d78c61
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1,40 @@
+noinst_HEADERS = \
+bios.h \
+bios_disk.h \
+callback.h \
+cpu.h \
+cross.h \
+control.h \
+debug.h \
+dma.h \
+dos_inc.h \
+dos_system.h \
+dosbox.h \
+fpu.h \
+hardware.h \
+inout.h \
+joystick.h \
+ipx.h \
+ipxserver.h \
+keyboard.h \
+logging.h \
+mapper.h \
+mem.h \
+midi.h \
+mixer.h \
+mouse.h \
+paging.h \
+pci_bus.h \
+pic.h \
+programs.h \
+render.h \
+regs.h \
+render.h \
+serialport.h \
+setup.h \
+shell.h \
+support.h \
+timer.h \
+vga.h \
+video.h
+
diff --git a/include/bios.h b/include/bios.h
new file mode 100644
index 000000000..2a7787a3d
--- /dev/null
+++ b/include/bios.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_BIOS_H
+#define DOSBOX_BIOS_H
+
+#define BIOS_BASE_ADDRESS_COM1 0x400
+#define BIOS_BASE_ADDRESS_COM2 0x402
+#define BIOS_BASE_ADDRESS_COM3 0x404
+#define BIOS_BASE_ADDRESS_COM4 0x406
+#define BIOS_ADDRESS_LPT1 0x408
+#define BIOS_ADDRESS_LPT2 0x40a
+#define BIOS_ADDRESS_LPT3 0x40c
+/* 0x40e is reserved */
+#define BIOS_CONFIGURATION 0x410
+/* 0x412 is reserved */
+#define BIOS_MEMORY_SIZE 0x413
+#define BIOS_TRUE_MEMORY_SIZE 0x415
+/* #define bios_expansion_memory_size (*(unsigned int *) 0x415) */
+#define BIOS_KEYBOARD_STATE 0x417
+#define BIOS_KEYBOARD_FLAGS1 BIOS_KEYBOARD_STATE
+#define BIOS_KEYBOARD_FLAGS2 0x418
+#define BIOS_KEYBOARD_TOKEN 0x419
+/* used for keyboard input with Alt-Number */
+#define BIOS_KEYBOARD_BUFFER_HEAD 0x41a
+#define BIOS_KEYBOARD_BUFFER_TAIL 0x41c
+#define BIOS_KEYBOARD_BUFFER 0x41e
+/* #define bios_keyboard_buffer (*(unsigned int *) 0x41e) */
+#define BIOS_DRIVE_ACTIVE 0x43e
+#define BIOS_DRIVE_RUNNING 0x43f
+#define BIOS_DISK_MOTOR_TIMEOUT 0x440
+#define BIOS_DISK_STATUS 0x441
+/* #define bios_fdc_result_buffer (*(unsigned short *) 0x442) */
+#define BIOS_VIDEO_MODE 0x449
+#define BIOS_SCREEN_COLUMNS 0x44a
+#define BIOS_VIDEO_MEMORY_USED 0x44c
+#define BIOS_VIDEO_MEMORY_ADDRESS 0x44e
+#define BIOS_VIDEO_CURSOR_POS 0x450
+
+
+#define BIOS_CURSOR_SHAPE 0x460
+#define BIOS_CURSOR_LAST_LINE 0x460
+#define BIOS_CURSOR_FIRST_LINE 0x461
+#define BIOS_CURRENT_SCREEN_PAGE 0x462
+#define BIOS_VIDEO_PORT 0x463
+#define BIOS_VDU_CONTROL 0x465
+#define BIOS_VDU_COLOR_REGISTER 0x466
+/* 0x467-0x468 is reserved */
+#define BIOS_LAST_UNEXPECTED_IRQ 0x46b
+#define BIOS_TIMER 0x46c
+#define BIOS_24_HOURS_FLAG 0x470
+#define BIOS_KEYBOARD_FLAGS 0x471
+#define BIOS_CTRL_ALT_DEL_FLAG 0x472
+#define BIOS_HARDDISK_COUNT 0x475
+/* 0x474, 0x476, 0x477 is reserved */
+#define BIOS_LPT1_TIMEOUT 0x478
+#define BIOS_LPT2_TIMEOUT 0x479
+#define BIOS_LPT3_TIMEOUT 0x47a
+/* 0x47b is reserved */
+#define BIOS_COM1_TIMEOUT 0x47c
+#define BIOS_COM2_TIMEOUT 0x47d
+#define BIOS_COM3_TIMEOUT 0x47e
+#define BIOS_COM4_TIMEOUT 0x47f
+/* 0x47e is reserved */ //<- why that?
+/* 0x47f-0x4ff is unknow for me */
+#define BIOS_KEYBOARD_BUFFER_START 0x480
+#define BIOS_KEYBOARD_BUFFER_END 0x482
+
+#define BIOS_ROWS_ON_SCREEN_MINUS_1 0x484
+#define BIOS_FONT_HEIGHT 0x485
+
+#define BIOS_VIDEO_INFO_0 0x487
+#define BIOS_VIDEO_INFO_1 0x488
+#define BIOS_VIDEO_INFO_2 0x489
+#define BIOS_VIDEO_COMBO 0x48a
+
+#define BIOS_KEYBOARD_FLAGS3 0x496
+#define BIOS_KEYBOARD_LEDS 0x497
+
+#define BIOS_WAIT_FLAG_POINTER 0x498
+#define BIOS_WAIT_FLAG_COUNT 0x49c
+#define BIOS_WAIT_FLAG_ACTIVE 0x4a0
+#define BIOS_WAIT_FLAG_TEMP 0x4a1
+
+
+#define BIOS_PRINT_SCREEN_FLAG 0x500
+
+#define BIOS_VIDEO_SAVEPTR 0x4a8
+
+
+#define BIOS_DEFAULT_HANDLER_LOCATION (RealMake(0xf000,0xff53))
+#define BIOS_DEFAULT_INT5_LOCATION (RealMake(0xf000,0xff54))
+#define BIOS_DEFAULT_IRQ0_LOCATION (RealMake(0xf000,0xfea5))
+#define BIOS_DEFAULT_IRQ1_LOCATION (RealMake(0xf000,0xe987))
+#define BIOS_DEFAULT_IRQ2_LOCATION (RealMake(0xf000,0xff55))
+#define BIOS_DEFAULT_RESET_LOCATION (RealMake(0xf000,0xe05b))
+
+/* maximum of scancodes handled by keyboard bios routines */
+#define MAX_SCAN_CODE 0x58
+
+/* The Section handling Bios Disk Access */
+//#define BIOS_MAX_DISK 10
+
+//#define MAX_SWAPPABLE_DISKS 20
+
+void BIOS_ZeroExtendedSize(bool in);
+void char_out(Bit8u chr,Bit32u att,Bit8u page);
+void INT10_StartUp(void);
+void INT16_StartUp(void);
+void INT2A_StartUp(void);
+void INT2F_StartUp(void);
+void INT33_StartUp(void);
+void INT13_StartUp(void);
+
+bool BIOS_AddKeyToBuffer(Bit16u code);
+
+void INT10_ReloadRomFonts();
+
+void BIOS_SetComPorts (Bit16u baseaddr[]);
+
+#endif
diff --git a/include/bios_disk.h b/include/bios_disk.h
new file mode 100644
index 000000000..31537269b
--- /dev/null
+++ b/include/bios_disk.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_BIOS_DISK_H
+#define DOSBOX_BIOS_DISK_H
+
+#include <stdio.h>
+#ifndef DOSBOX_MEM_H
+#include "mem.h"
+#endif
+#ifndef DOSBOX_DOS_INC_H
+#include "dos_inc.h"
+#endif
+#ifndef DOSBOX_BIOS_H
+#include "bios.h"
+#endif
+
+/* The Section handling Bios Disk Access */
+#define BIOS_MAX_DISK 10
+
+#define MAX_SWAPPABLE_DISKS 20
+struct diskGeo {
+ Bit32u ksize; /* Size in kilobytes */
+ Bit16u secttrack; /* Sectors per track */
+ Bit16u headscyl; /* Heads per cylinder */
+ Bit16u cylcount; /* Cylinders per side */
+ Bit16u biosval; /* Type to return from BIOS */
+};
+extern diskGeo DiskGeometryList[];
+
+class imageDisk {
+public:
+ Bit8u Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data);
+ Bit8u Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data);
+ Bit8u Read_AbsoluteSector(Bit32u sectnum, void * data);
+ Bit8u Write_AbsoluteSector(Bit32u sectnum, void * data);
+
+ void Set_Geometry(Bit32u setHeads, Bit32u setCyl, Bit32u setSect, Bit32u setSectSize);
+ void Get_Geometry(Bit32u * getHeads, Bit32u *getCyl, Bit32u *getSect, Bit32u *getSectSize);
+ Bit8u GetBiosType(void);
+ Bit32u getSectSize(void);
+ imageDisk(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk);
+ ~imageDisk() { if(diskimg != NULL) { fclose(diskimg); } };
+
+ bool hardDrive;
+ bool active;
+ FILE *diskimg;
+ Bit8u diskname[512];
+ Bit8u floppytype;
+
+ Bit32u sector_size;
+ Bit32u heads,cylinders,sectors;
+private:
+ Bit32u current_fpos;
+ enum { NONE,READ,WRITE } last_action;
+};
+
+void updateDPT(void);
+void incrementFDD(void);
+
+#define MAX_HDD_IMAGES 2
+
+#define MAX_DISK_IMAGES (2 + MAX_HDD_IMAGES)
+
+extern imageDisk *imageDiskList[MAX_DISK_IMAGES];
+extern imageDisk *diskSwap[MAX_SWAPPABLE_DISKS];
+extern Bit32s swapPosition;
+extern Bit16u imgDTASeg; /* Real memory location of temporary DTA pointer for fat image disk access */
+extern RealPt imgDTAPtr; /* Real memory location of temporary DTA pointer for fat image disk access */
+extern DOS_DTA *imgDTA;
+
+void swapInDisks(void);
+void swapInNextDisk(void);
+bool getSwapRequest(void);
+
+#endif
diff --git a/include/callback.h b/include/callback.h
new file mode 100644
index 000000000..03008e45c
--- /dev/null
+++ b/include/callback.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_CALLBACK_H
+#define DOSBOX_CALLBACK_H
+
+#ifndef DOSBOX_MEM_H
+#include "mem.h"
+#endif
+
+typedef Bitu (*CallBack_Handler)(void);
+extern CallBack_Handler CallBack_Handlers[];
+
+enum { CB_RETN,CB_RETF,CB_RETF8,CB_RETF_STI,CB_RETF_CLI,
+ CB_IRET,CB_IRETD,CB_IRET_STI,CB_IRET_EOI_PIC1,
+ CB_IRQ0,CB_IRQ1,CB_IRQ9,CB_IRQ12,CB_IRQ12_RET,CB_IRQ6_PCJR,CB_MOUSE,
+ CB_INT29,CB_INT16,CB_HOOKABLE,CB_TDE_IRET,CB_IPXESR,CB_IPXESR_RET,
+ CB_INT21,CB_INT13,CB_VESA_WAIT,CB_VESA_PM };
+
+#define CB_MAX 128
+#define CB_SIZE 32
+#define CB_SEG 0xF000
+#define CB_SOFFSET 0x1000
+
+enum {
+ CBRET_NONE=0,CBRET_STOP=1
+};
+
+extern Bit8u lastint;
+
+static INLINE RealPt CALLBACK_RealPointer(Bitu callback) {
+ return RealMake(CB_SEG,(Bit16u)(CB_SOFFSET+callback*CB_SIZE));
+}
+static INLINE PhysPt CALLBACK_PhysPointer(Bitu callback) {
+ return PhysMake(CB_SEG,(Bit16u)(CB_SOFFSET+callback*CB_SIZE));
+}
+
+static INLINE PhysPt CALLBACK_GetBase(void) {
+ return (CB_SEG << 4) + CB_SOFFSET;
+}
+
+Bitu CALLBACK_Allocate();
+
+void CALLBACK_Idle(void);
+
+
+void CALLBACK_RunRealInt(Bit8u intnum);
+void CALLBACK_RunRealFar(Bit16u seg,Bit16u off);
+
+bool CALLBACK_Setup(Bitu callback,CallBack_Handler handler,Bitu type,const char* descr);
+Bitu CALLBACK_Setup(Bitu callback,CallBack_Handler handler,Bitu type,PhysPt addr,const char* descr);
+
+const char* CALLBACK_GetDescription(Bitu callback);
+bool CALLBACK_Free(Bitu callback);
+
+void CALLBACK_SCF(bool val);
+void CALLBACK_SZF(bool val);
+void CALLBACK_SIF(bool val);
+
+extern Bitu call_priv_io;
+
+
+class CALLBACK_HandlerObject{
+private:
+ bool installed;
+ Bitu m_callback;
+ enum {NONE,SETUP,SETUPAT} m_type;
+ struct {
+ RealPt old_vector;
+ Bit8u interrupt;
+ bool installed;
+ } vectorhandler;
+public:
+ CALLBACK_HandlerObject():installed(false),m_type(NONE) {
+ vectorhandler.installed=false;
+ }
+ ~CALLBACK_HandlerObject();
+
+ //Install and allocate a callback.
+ void Install(CallBack_Handler handler,Bitu type,const char* description);
+ void Install(CallBack_Handler handler,Bitu type,PhysPt addr,const char* description);
+
+ void Uninstall();
+
+ //Only allocate a callback number
+ void Allocate(CallBack_Handler handler,const char* description=0);
+ Bit16u Get_callback() {
+ return (Bit16u)m_callback;
+ }
+ RealPt Get_RealPointer() {
+ return CALLBACK_RealPointer(m_callback);
+ }
+ void Set_RealVec(Bit8u vec);
+};
+#endif
diff --git a/include/control.h b/include/control.h
new file mode 100644
index 000000000..6d1dffbda
--- /dev/null
+++ b/include/control.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_CONTROL_H
+#define DOSBOX_CONTROL_H
+
+#ifdef _MSC_VER
+#pragma warning ( disable : 4786 )
+#pragma warning ( disable : 4290 )
+#endif
+
+#ifndef DOSBOX_PROGRAMS_H
+#include "programs.h"
+#endif
+#ifndef DOSBOX_SETUP_H
+#include "setup.h"
+#endif
+
+#ifndef CH_LIST
+#define CH_LIST
+#include <list>
+#endif
+
+#ifndef CH_VECTOR
+#define CH_VECTOR
+#include <vector>
+#endif
+
+#ifndef CH_STRING
+#define CH_STRING
+#include <string>
+#endif
+
+
+
+
+class Config{
+public:
+ CommandLine * cmdline;
+private:
+ std::list<Section*> sectionlist;
+ typedef std::list<Section*>::iterator it;
+ typedef std::list<Section*>::reverse_iterator reverse_it;
+ typedef std::list<Section*>::const_iterator const_it;
+ typedef std::list<Section*>::const_reverse_iterator const_reverse_it;
+ void (* _start_function)(void);
+ bool secure_mode; //Sandbox mode
+public:
+ bool initialised;
+ std::vector<std::string> startup_params;
+ std::vector<std::string> configfiles;
+ Config(CommandLine * cmd):cmdline(cmd),secure_mode(false) {
+ startup_params.push_back(cmdline->GetFileName());
+ cmdline->FillVector(startup_params);
+ initialised=false;
+ }
+ ~Config();
+
+ Section_line * AddSection_line(char const * const _name,void (*_initfunction)(Section*));
+ Section_prop * AddSection_prop(char const * const _name,void (*_initfunction)(Section*),bool canchange=false);
+
+ Section* GetSection(int index);
+ Section* GetSection(std::string const&_sectionname) const;
+ Section* GetSectionFromProperty(char const * const prop) const;
+
+ void SetStartUp(void (*_function)(void));
+ void Init();
+ void ShutDown();
+ void StartUp();
+ bool PrintConfig(char const * const configfilename) const;
+ bool ParseConfigFile(char const * const configfilename);
+ void ParseEnv(char ** envp);
+ bool SecureMode() const { return secure_mode; }
+ void SwitchToSecureMode() { secure_mode = true; }//can't be undone
+};
+
+#endif
diff --git a/include/cpu.h b/include/cpu.h
new file mode 100644
index 000000000..ecbabd7c2
--- /dev/null
+++ b/include/cpu.h
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_CPU_H
+#define DOSBOX_CPU_H
+
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+#ifndef DOSBOX_REGS_H
+#include "regs.h"
+#endif
+#ifndef DOSBOX_MEM_H
+#include "mem.h"
+#endif
+
+#define CPU_AUTODETERMINE_NONE 0x00
+#define CPU_AUTODETERMINE_CORE 0x01
+#define CPU_AUTODETERMINE_CYCLES 0x02
+
+#define CPU_AUTODETERMINE_SHIFT 0x02
+#define CPU_AUTODETERMINE_MASK 0x03
+
+#define CPU_CYCLES_LOWER_LIMIT 200
+
+
+#define CPU_ARCHTYPE_MIXED 0xff
+#define CPU_ARCHTYPE_386SLOW 0x30
+#define CPU_ARCHTYPE_386FAST 0x35
+#define CPU_ARCHTYPE_486OLDSLOW 0x40
+#define CPU_ARCHTYPE_486NEWSLOW 0x45
+#define CPU_ARCHTYPE_PENTIUMSLOW 0x50
+
+/* CPU Cycle Timing */
+extern Bit32s CPU_Cycles;
+extern Bit32s CPU_CycleLeft;
+extern Bit32s CPU_CycleMax;
+extern Bit32s CPU_OldCycleMax;
+extern Bit32s CPU_CyclePercUsed;
+extern Bit32s CPU_CycleLimit;
+extern Bit64s CPU_IODelayRemoved;
+extern bool CPU_CycleAutoAdjust;
+extern bool CPU_SkipCycleAutoAdjust;
+extern Bitu CPU_AutoDetermineMode;
+
+extern Bitu CPU_ArchitectureType;
+
+extern Bitu CPU_PrefetchQueueSize;
+
+/* Some common Defines */
+/* A CPU Handler */
+typedef Bits (CPU_Decoder)(void);
+extern CPU_Decoder * cpudecoder;
+
+Bits CPU_Core_Normal_Run(void);
+Bits CPU_Core_Normal_Trap_Run(void);
+Bits CPU_Core_Simple_Run(void);
+Bits CPU_Core_Simple_Trap_Run(void);
+Bits CPU_Core_Full_Run(void);
+Bits CPU_Core_Dyn_X86_Run(void);
+Bits CPU_Core_Dyn_X86_Trap_Run(void);
+Bits CPU_Core_Dynrec_Run(void);
+Bits CPU_Core_Dynrec_Trap_Run(void);
+Bits CPU_Core_Prefetch_Run(void);
+Bits CPU_Core_Prefetch_Trap_Run(void);
+
+void CPU_Enable_SkipAutoAdjust(void);
+void CPU_Disable_SkipAutoAdjust(void);
+void CPU_Reset_AutoAdjust(void);
+
+
+//CPU Stuff
+
+extern Bit16u parity_lookup[256];
+
+bool CPU_LLDT(Bitu selector);
+bool CPU_LTR(Bitu selector);
+void CPU_LIDT(Bitu limit,Bitu base);
+void CPU_LGDT(Bitu limit,Bitu base);
+
+Bitu CPU_STR(void);
+Bitu CPU_SLDT(void);
+Bitu CPU_SIDT_base(void);
+Bitu CPU_SIDT_limit(void);
+Bitu CPU_SGDT_base(void);
+Bitu CPU_SGDT_limit(void);
+
+void CPU_ARPL(Bitu & dest_sel,Bitu src_sel);
+void CPU_LAR(Bitu selector,Bitu & ar);
+void CPU_LSL(Bitu selector,Bitu & limit);
+
+void CPU_SET_CRX(Bitu cr,Bitu value);
+bool CPU_WRITE_CRX(Bitu cr,Bitu value);
+Bitu CPU_GET_CRX(Bitu cr);
+bool CPU_READ_CRX(Bitu cr,Bit32u & retvalue);
+
+bool CPU_WRITE_DRX(Bitu dr,Bitu value);
+bool CPU_READ_DRX(Bitu dr,Bit32u & retvalue);
+
+bool CPU_WRITE_TRX(Bitu dr,Bitu value);
+bool CPU_READ_TRX(Bitu dr,Bit32u & retvalue);
+
+Bitu CPU_SMSW(void);
+bool CPU_LMSW(Bitu word);
+
+void CPU_VERR(Bitu selector);
+void CPU_VERW(Bitu selector);
+
+void CPU_JMP(bool use32,Bitu selector,Bitu offset,Bitu oldeip);
+void CPU_CALL(bool use32,Bitu selector,Bitu offset,Bitu oldeip);
+void CPU_RET(bool use32,Bitu bytes,Bitu oldeip);
+void CPU_IRET(bool use32,Bitu oldeip);
+void CPU_HLT(Bitu oldeip);
+
+bool CPU_POPF(Bitu use32);
+bool CPU_PUSHF(Bitu use32);
+bool CPU_CLI(void);
+bool CPU_STI(void);
+
+bool CPU_IO_Exception(Bitu port,Bitu size);
+void CPU_RunException(void);
+
+void CPU_ENTER(bool use32,Bitu bytes,Bitu level);
+
+#define CPU_INT_SOFTWARE 0x1
+#define CPU_INT_EXCEPTION 0x2
+#define CPU_INT_HAS_ERROR 0x4
+#define CPU_INT_NOIOPLCHECK 0x8
+
+void CPU_Interrupt(Bitu num,Bitu type,Bitu oldeip);
+static INLINE void CPU_HW_Interrupt(Bitu num) {
+ CPU_Interrupt(num,0,reg_eip);
+}
+static INLINE void CPU_SW_Interrupt(Bitu num,Bitu oldeip) {
+ CPU_Interrupt(num,CPU_INT_SOFTWARE,oldeip);
+}
+static INLINE void CPU_SW_Interrupt_NoIOPLCheck(Bitu num,Bitu oldeip) {
+ CPU_Interrupt(num,CPU_INT_SOFTWARE|CPU_INT_NOIOPLCHECK,oldeip);
+}
+
+bool CPU_PrepareException(Bitu which,Bitu error);
+void CPU_Exception(Bitu which,Bitu error=0);
+
+bool CPU_SetSegGeneral(SegNames seg,Bitu value);
+bool CPU_PopSeg(SegNames seg,bool use32);
+
+bool CPU_CPUID(void);
+Bitu CPU_Pop16(void);
+Bitu CPU_Pop32(void);
+void CPU_Push16(Bitu value);
+void CPU_Push32(Bitu value);
+
+void CPU_SetFlags(Bitu word,Bitu mask);
+
+
+#define EXCEPTION_UD 6
+#define EXCEPTION_TS 10
+#define EXCEPTION_NP 11
+#define EXCEPTION_SS 12
+#define EXCEPTION_GP 13
+#define EXCEPTION_PF 14
+
+#define CR0_PROTECTION 0x00000001
+#define CR0_MONITORPROCESSOR 0x00000002
+#define CR0_FPUEMULATION 0x00000004
+#define CR0_TASKSWITCH 0x00000008
+#define CR0_FPUPRESENT 0x00000010
+#define CR0_PAGING 0x80000000
+
+
+// *********************************************************************
+// Descriptor
+// *********************************************************************
+
+#define DESC_INVALID 0x00
+#define DESC_286_TSS_A 0x01
+#define DESC_LDT 0x02
+#define DESC_286_TSS_B 0x03
+#define DESC_286_CALL_GATE 0x04
+#define DESC_TASK_GATE 0x05
+#define DESC_286_INT_GATE 0x06
+#define DESC_286_TRAP_GATE 0x07
+
+#define DESC_386_TSS_A 0x09
+#define DESC_386_TSS_B 0x0b
+#define DESC_386_CALL_GATE 0x0c
+#define DESC_386_INT_GATE 0x0e
+#define DESC_386_TRAP_GATE 0x0f
+
+/* EU/ED Expand UP/DOWN RO/RW Read Only/Read Write NA/A Accessed */
+#define DESC_DATA_EU_RO_NA 0x10
+#define DESC_DATA_EU_RO_A 0x11
+#define DESC_DATA_EU_RW_NA 0x12
+#define DESC_DATA_EU_RW_A 0x13
+#define DESC_DATA_ED_RO_NA 0x14
+#define DESC_DATA_ED_RO_A 0x15
+#define DESC_DATA_ED_RW_NA 0x16
+#define DESC_DATA_ED_RW_A 0x17
+
+/* N/R Readable NC/C Confirming A/NA Accessed */
+#define DESC_CODE_N_NC_A 0x18
+#define DESC_CODE_N_NC_NA 0x19
+#define DESC_CODE_R_NC_A 0x1a
+#define DESC_CODE_R_NC_NA 0x1b
+#define DESC_CODE_N_C_A 0x1c
+#define DESC_CODE_N_C_NA 0x1d
+#define DESC_CODE_R_C_A 0x1e
+#define DESC_CODE_R_C_NA 0x1f
+
+#ifdef _MSC_VER
+#pragma pack (1)
+#endif
+
+struct S_Descriptor {
+#ifdef WORDS_BIGENDIAN
+ Bit32u base_0_15 :16;
+ Bit32u limit_0_15 :16;
+ Bit32u base_24_31 :8;
+ Bit32u g :1;
+ Bit32u big :1;
+ Bit32u r :1;
+ Bit32u avl :1;
+ Bit32u limit_16_19 :4;
+ Bit32u p :1;
+ Bit32u dpl :2;
+ Bit32u type :5;
+ Bit32u base_16_23 :8;
+#else
+ Bit32u limit_0_15 :16;
+ Bit32u base_0_15 :16;
+ Bit32u base_16_23 :8;
+ Bit32u type :5;
+ Bit32u dpl :2;
+ Bit32u p :1;
+ Bit32u limit_16_19 :4;
+ Bit32u avl :1;
+ Bit32u r :1;
+ Bit32u big :1;
+ Bit32u g :1;
+ Bit32u base_24_31 :8;
+#endif
+}GCC_ATTRIBUTE(packed);
+
+struct G_Descriptor {
+#ifdef WORDS_BIGENDIAN
+ Bit32u selector: 16;
+ Bit32u offset_0_15 :16;
+ Bit32u offset_16_31 :16;
+ Bit32u p :1;
+ Bit32u dpl :2;
+ Bit32u type :5;
+ Bit32u reserved :3;
+ Bit32u paramcount :5;
+#else
+ Bit32u offset_0_15 :16;
+ Bit32u selector :16;
+ Bit32u paramcount :5;
+ Bit32u reserved :3;
+ Bit32u type :5;
+ Bit32u dpl :2;
+ Bit32u p :1;
+ Bit32u offset_16_31 :16;
+#endif
+} GCC_ATTRIBUTE(packed);
+
+struct TSS_16 {
+ Bit16u back; /* Back link to other task */
+ Bit16u sp0; /* The CK stack pointer */
+ Bit16u ss0; /* The CK stack selector */
+ Bit16u sp1; /* The parent KL stack pointer */
+ Bit16u ss1; /* The parent KL stack selector */
+ Bit16u sp2; /* Unused */
+ Bit16u ss2; /* Unused */
+ Bit16u ip; /* The instruction pointer */
+ Bit16u flags; /* The flags */
+ Bit16u ax, cx, dx, bx; /* The general purpose registers */
+ Bit16u sp, bp, si, di; /* The special purpose registers */
+ Bit16u es; /* The extra selector */
+ Bit16u cs; /* The code selector */
+ Bit16u ss; /* The application stack selector */
+ Bit16u ds; /* The data selector */
+ Bit16u ldt; /* The local descriptor table */
+} GCC_ATTRIBUTE(packed);
+
+struct TSS_32 {
+ Bit32u back; /* Back link to other task */
+ Bit32u esp0; /* The CK stack pointer */
+ Bit32u ss0; /* The CK stack selector */
+ Bit32u esp1; /* The parent KL stack pointer */
+ Bit32u ss1; /* The parent KL stack selector */
+ Bit32u esp2; /* Unused */
+ Bit32u ss2; /* Unused */
+ Bit32u cr3; /* The page directory pointer */
+ Bit32u eip; /* The instruction pointer */
+ Bit32u eflags; /* The flags */
+ Bit32u eax, ecx, edx, ebx; /* The general purpose registers */
+ Bit32u esp, ebp, esi, edi; /* The special purpose registers */
+ Bit32u es; /* The extra selector */
+ Bit32u cs; /* The code selector */
+ Bit32u ss; /* The application stack selector */
+ Bit32u ds; /* The data selector */
+ Bit32u fs; /* And another extra selector */
+ Bit32u gs; /* ... and another one */
+ Bit32u ldt; /* The local descriptor table */
+} GCC_ATTRIBUTE(packed);
+
+#ifdef _MSC_VER
+#pragma pack()
+#endif
+class Descriptor
+{
+public:
+ Descriptor() { saved.fill[0]=saved.fill[1]=0; }
+
+ void Load(PhysPt address);
+ void Save(PhysPt address);
+
+ PhysPt GetBase (void) {
+ return (saved.seg.base_24_31<<24) | (saved.seg.base_16_23<<16) | saved.seg.base_0_15;
+ }
+ Bitu GetLimit (void) {
+ Bitu limit = (saved.seg.limit_16_19<<16) | saved.seg.limit_0_15;
+ if (saved.seg.g) return (limit<<12) | 0xFFF;
+ return limit;
+ }
+ Bitu GetOffset(void) {
+ return (saved.gate.offset_16_31 << 16) | saved.gate.offset_0_15;
+ }
+ Bitu GetSelector(void) {
+ return saved.gate.selector;
+ }
+ Bitu Type(void) {
+ return saved.seg.type;
+ }
+ Bitu Conforming(void) {
+ return saved.seg.type & 8;
+ }
+ Bitu DPL(void) {
+ return saved.seg.dpl;
+ }
+ Bitu Big(void) {
+ return saved.seg.big;
+ }
+public:
+ union {
+ S_Descriptor seg;
+ G_Descriptor gate;
+ Bit32u fill[2];
+ } saved;
+};
+
+class DescriptorTable {
+public:
+ PhysPt GetBase (void) { return table_base; }
+ Bitu GetLimit (void) { return table_limit; }
+ void SetBase (PhysPt _base) { table_base = _base; }
+ void SetLimit (Bitu _limit) { table_limit= _limit; }
+
+ bool GetDescriptor (Bitu selector, Descriptor& desc) {
+ selector&=~7;
+ if (selector>=table_limit) return false;
+ desc.Load(table_base+(selector));
+ return true;
+ }
+protected:
+ PhysPt table_base;
+ Bitu table_limit;
+};
+
+class GDTDescriptorTable : public DescriptorTable {
+public:
+ bool GetDescriptor(Bitu selector, Descriptor& desc) {
+ Bitu address=selector & ~7;
+ if (selector & 4) {
+ if (address>=ldt_limit) return false;
+ desc.Load(ldt_base+address);
+ return true;
+ } else {
+ if (address>=table_limit) return false;
+ desc.Load(table_base+address);
+ return true;
+ }
+ }
+ bool SetDescriptor(Bitu selector, Descriptor& desc) {
+ Bitu address=selector & ~7;
+ if (selector & 4) {
+ if (address>=ldt_limit) return false;
+ desc.Save(ldt_base+address);
+ return true;
+ } else {
+ if (address>=table_limit) return false;
+ desc.Save(table_base+address);
+ return true;
+ }
+ }
+ Bitu SLDT(void) {
+ return ldt_value;
+ }
+ bool LLDT(Bitu value) {
+ if ((value&0xfffc)==0) {
+ ldt_value=0;
+ ldt_base=0;
+ ldt_limit=0;
+ return true;
+ }
+ Descriptor desc;
+ if (!GetDescriptor(value,desc)) return !CPU_PrepareException(EXCEPTION_GP,value);
+ if (desc.Type()!=DESC_LDT) return !CPU_PrepareException(EXCEPTION_GP,value);
+ if (!desc.saved.seg.p) return !CPU_PrepareException(EXCEPTION_NP,value);
+ ldt_base=desc.GetBase();
+ ldt_limit=desc.GetLimit();
+ ldt_value=value;
+ return true;
+ }
+private:
+ PhysPt ldt_base;
+ Bitu ldt_limit;
+ Bitu ldt_value;
+};
+
+class TSS_Descriptor : public Descriptor {
+public:
+ Bitu IsBusy(void) {
+ return saved.seg.type & 2;
+ }
+ Bitu Is386(void) {
+ return saved.seg.type & 8;
+ }
+ void SetBusy(bool busy) {
+ if (busy) saved.seg.type|=2;
+ else saved.seg.type&=~2;
+ }
+};
+
+
+struct CPUBlock {
+ Bitu cpl; /* Current Privilege */
+ Bitu mpl;
+ Bitu cr0;
+ bool pmode; /* Is Protected mode enabled */
+ GDTDescriptorTable gdt;
+ DescriptorTable idt;
+ struct {
+ Bitu mask,notmask;
+ bool big;
+ } stack;
+ struct {
+ bool big;
+ } code;
+ struct {
+ Bitu cs,eip;
+ CPU_Decoder * old_decoder;
+ } hlt;
+ struct {
+ Bitu which,error;
+ } exception;
+ Bits direction;
+ bool trap_skip;
+ Bit32u drx[8];
+ Bit32u trx[8];
+};
+
+extern CPUBlock cpu;
+
+static INLINE void CPU_SetFlagsd(Bitu word) {
+ Bitu mask=cpu.cpl ? FMASK_NORMAL : FMASK_ALL;
+ CPU_SetFlags(word,mask);
+}
+
+static INLINE void CPU_SetFlagsw(Bitu word) {
+ Bitu mask=(cpu.cpl ? FMASK_NORMAL : FMASK_ALL) & 0xffff;
+ CPU_SetFlags(word,mask);
+}
+
+
+#endif
diff --git a/include/cross.h b/include/cross.h
new file mode 100644
index 000000000..90b65315a
--- /dev/null
+++ b/include/cross.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_CROSS_H
+#define DOSBOX_CROSS_H
+
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string>
+
+#if defined (_MSC_VER) /* MS Visual C++ */
+#include <direct.h>
+#include <io.h>
+#define LONGTYPE(a) a##i64
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#else /* LINUX / GCC */
+#include <dirent.h>
+#include <unistd.h>
+#define LONGTYPE(a) a##LL
+#endif
+
+#define CROSS_LEN 512 /* Maximum filename size */
+
+
+#if defined (WIN32) || defined (OS2) /* Win 32 & OS/2*/
+#define CROSS_FILENAME(blah)
+#define CROSS_FILESPLIT '\\'
+#define F_OK 0
+#else
+#define CROSS_FILENAME(blah) strreplace(blah,'\\','/')
+#define CROSS_FILESPLIT '/'
+#endif
+
+#define CROSS_NONE 0
+#define CROSS_FILE 1
+#define CROSS_DIR 2
+#if defined (WIN32)
+#define ftruncate(blah,blah2) chsize(blah,blah2)
+#endif
+
+//Solaris maybe others
+#if defined (DB_HAVE_NO_POWF)
+#include <math.h>
+static inline float powf (float x, float y) { return (float) pow (x,y); }
+#endif
+
+class Cross {
+public:
+ static void GetPlatformConfigDir(std::string& in);
+ static void GetPlatformConfigName(std::string& in);
+ static void CreatePlatformConfigDir(std::string& in);
+ static void ResolveHomedir(std::string & temp_line);
+ static void CreateDir(std::string const& temp);
+ static bool IsPathAbsolute(std::string const& in);
+};
+
+
+#if defined (WIN32)
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from
+#include <windows.h>
+
+typedef struct dir_struct {
+ HANDLE handle;
+ char base_path[MAX_PATH+4];
+ WIN32_FIND_DATA search_data;
+} dir_information;
+
+#else
+
+//#include <sys/types.h> //Included above
+#include <dirent.h>
+
+typedef struct dir_struct {
+ DIR* dir;
+ char base_path[CROSS_LEN];
+} dir_information;
+
+#endif
+
+dir_information* open_directory(const char* dirname);
+bool read_directory_first(dir_information* dirp, char* entry_name, bool& is_directory);
+bool read_directory_next(dir_information* dirp, char* entry_name, bool& is_directory);
+void close_directory(dir_information* dirp);
+
+FILE *fopen_wrap(const char *path, const char *mode);
+#endif
diff --git a/include/debug.h b/include/debug.h
new file mode 100644
index 000000000..7871d6384
--- /dev/null
+++ b/include/debug.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+void DEBUG_SetupConsole(void);
+void DEBUG_DrawScreen(void);
+bool DEBUG_Breakpoint(void);
+bool DEBUG_IntBreakpoint(Bit8u intNum);
+void DEBUG_Enable(bool pressed);
+void DEBUG_CheckExecuteBreakpoint(Bit16u seg, Bit32u off);
+bool DEBUG_ExitLoop(void);
+void DEBUG_RefreshPage(char scroll);
+Bitu DEBUG_EnableDebugger(void);
+
+extern Bitu cycle_count;
+extern Bitu debugCallback;
+
+#ifdef C_HEAVY_DEBUG
+bool DEBUG_HeavyIsBreakpoint(void);
+void DEBUG_HeavyWriteLogInstruction(void);
+#endif
diff --git a/include/dma.h b/include/dma.h
new file mode 100644
index 000000000..eea35f5b2
--- /dev/null
+++ b/include/dma.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_DMA_H
+#define DOSBOX_DMA_H
+
+enum DMAEvent {
+ DMA_REACHED_TC,
+ DMA_MASKED,
+ DMA_UNMASKED,
+ DMA_TRANSFEREND
+};
+
+class DmaChannel;
+typedef void (* DMA_CallBack)(DmaChannel * chan,DMAEvent event);
+
+class DmaChannel {
+public:
+ Bit32u pagebase;
+ Bit16u baseaddr;
+ Bit32u curraddr;
+ Bit16u basecnt;
+ Bit16u currcnt;
+ Bit8u channum;
+ Bit8u pagenum;
+ Bit8u DMA16;
+ bool increment;
+ bool autoinit;
+ Bit8u trantype;
+ bool masked;
+ bool tcount;
+ bool request;
+ DMA_CallBack callback;
+
+ DmaChannel(Bit8u num, bool dma16);
+ void DoCallBack(DMAEvent event) {
+ if (callback) (*callback)(this,event);
+ }
+ void SetMask(bool _mask) {
+ masked=_mask;
+ DoCallBack(masked ? DMA_MASKED : DMA_UNMASKED);
+ }
+ void Register_Callback(DMA_CallBack _cb) {
+ callback = _cb;
+ SetMask(masked);
+ if (callback) Raise_Request();
+ else Clear_Request();
+ }
+ void ReachedTC(void) {
+ tcount=true;
+ DoCallBack(DMA_REACHED_TC);
+ }
+ void SetPage(Bit8u val) {
+ pagenum=val;
+ pagebase=(pagenum >> DMA16) << (16+DMA16);
+ }
+ void Raise_Request(void) {
+ request=true;
+ }
+ void Clear_Request(void) {
+ request=false;
+ }
+ Bitu Read(Bitu size, Bit8u * buffer);
+ Bitu Write(Bitu size, Bit8u * buffer);
+};
+
+class DmaController {
+private:
+ Bit8u ctrlnum;
+ bool flipflop;
+ DmaChannel *DmaChannels[4];
+public:
+ IO_ReadHandleObject DMA_ReadHandler[0x12];
+ IO_WriteHandleObject DMA_WriteHandler[0x12];
+ DmaController(Bit8u num) {
+ flipflop = false;
+ ctrlnum = num; /* first or second DMA controller */
+ for(Bit8u i=0;i<4;i++) {
+ DmaChannels[i] = new DmaChannel(i+ctrlnum*4,ctrlnum==1);
+ }
+ }
+ ~DmaController(void) {
+ for(Bit8u i=0;i<4;i++) {
+ delete DmaChannels[i];
+ }
+ }
+ DmaChannel * GetChannel(Bit8u chan) {
+ if (chan<4) return DmaChannels[chan];
+ else return NULL;
+ }
+ void WriteControllerReg(Bitu reg,Bitu val,Bitu len);
+ Bitu ReadControllerReg(Bitu reg,Bitu len);
+};
+
+DmaChannel * GetDMAChannel(Bit8u chan);
+
+void CloseSecondDMAController(void);
+bool SecondDMAControllerAvailable(void);
+
+void DMA_SetWrapping(Bitu wrap);
+
+#endif
diff --git a/include/dos_inc.h b/include/dos_inc.h
new file mode 100644
index 000000000..67f83571c
--- /dev/null
+++ b/include/dos_inc.h
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_DOS_INC_H
+#define DOSBOX_DOS_INC_H
+
+#ifndef DOSBOX_DOS_SYSTEM_H
+#include "dos_system.h"
+#endif
+#ifndef DOSBOX_MEM_H
+#include "mem.h"
+#endif
+
+#include <stddef.h> //for offsetof
+
+#ifdef _MSC_VER
+#pragma pack (1)
+#endif
+struct CommandTail{
+ Bit8u count; /* number of bytes returned */
+ char buffer[127]; /* the buffer itself */
+} GCC_ATTRIBUTE(packed);
+#ifdef _MSC_VER
+#pragma pack ()
+#endif
+
+struct DOS_Date {
+ Bit16u year;
+ Bit8u month;
+ Bit8u day;
+};
+
+struct DOS_Version {
+ Bit8u major,minor,revision;
+};
+
+
+#ifdef _MSC_VER
+#pragma pack (1)
+#endif
+union bootSector {
+ struct entries {
+ Bit8u jump[3];
+ Bit8u oem_name[8];
+ Bit16u bytesect;
+ Bit8u sectclust;
+ Bit16u reserve_sect;
+ Bit8u misc[496];
+ } bootdata;
+ Bit8u rawdata[512];
+} GCC_ATTRIBUTE(packed);
+#ifdef _MSC_VER
+#pragma pack ()
+#endif
+
+
+enum { MCB_FREE=0x0000,MCB_DOS=0x0008 };
+enum { RETURN_EXIT=0,RETURN_CTRLC=1,RETURN_ABORT=2,RETURN_TSR=3};
+
+#define DOS_FILES 127
+#define DOS_DRIVES 26
+#define DOS_DEVICES 10
+
+
+// dos swappable area is 0x320 bytes beyond the sysvars table
+// device driver chain is inside sysvars
+#define DOS_INFOBLOCK_SEG 0x80 // sysvars (list of lists)
+#define DOS_CONDRV_SEG 0xa0
+#define DOS_CONSTRING_SEG 0xa8
+#define DOS_SDA_SEG 0xb2 // dos swappable area
+#define DOS_SDA_OFS 0
+#define DOS_CDS_SEG 0x108
+#define DOS_FIRST_SHELL 0x118
+#define DOS_MEM_START 0x16f //First Segment that DOS can use
+
+#define DOS_PRIVATE_SEGMENT 0xc800
+#define DOS_PRIVATE_SEGMENT_END 0xd000
+
+/* internal Dos Tables */
+
+extern DOS_File * Files[DOS_FILES];
+extern DOS_Drive * Drives[DOS_DRIVES];
+extern DOS_Device * Devices[DOS_DEVICES];
+
+extern Bit8u dos_copybuf[0x10000];
+
+
+void DOS_SetError(Bit16u code);
+
+/* File Handling Routines */
+
+enum { STDIN=0,STDOUT=1,STDERR=2,STDAUX=3,STDPRN=4};
+enum { HAND_NONE=0,HAND_FILE,HAND_DEVICE};
+
+/* Routines for File Class */
+void DOS_SetupFiles (void);
+bool DOS_ReadFile(Bit16u handle,Bit8u * data,Bit16u * amount, bool fcb = false);
+bool DOS_WriteFile(Bit16u handle,Bit8u * data,Bit16u * amount,bool fcb = false);
+bool DOS_SeekFile(Bit16u handle,Bit32u * pos,Bit32u type,bool fcb = false);
+bool DOS_CloseFile(Bit16u handle,bool fcb = false);
+bool DOS_FlushFile(Bit16u handle);
+bool DOS_DuplicateEntry(Bit16u entry,Bit16u * newentry);
+bool DOS_ForceDuplicateEntry(Bit16u entry,Bit16u newentry);
+bool DOS_GetFileDate(Bit16u entry, Bit16u* otime, Bit16u* odate);
+
+/* Routines for Drive Class */
+bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry,bool fcb = false);
+bool DOS_OpenFileExtended(char const * name, Bit16u flags, Bit16u createAttr, Bit16u action, Bit16u *entry, Bit16u* status);
+bool DOS_CreateFile(char const * name,Bit16u attribute,Bit16u * entry, bool fcb = false);
+bool DOS_UnlinkFile(char const * const name);
+bool DOS_FindFirst(char *search,Bit16u attr,bool fcb_findfirst=false);
+bool DOS_FindNext(void);
+bool DOS_Canonicalize(char const * const name,char * const big);
+bool DOS_CreateTempFile(char * const name,Bit16u * entry);
+bool DOS_FileExists(char const * const name);
+
+/* Helper Functions */
+bool DOS_MakeName(char const * const name,char * const fullname,Bit8u * drive);
+/* Drive Handing Routines */
+Bit8u DOS_GetDefaultDrive(void);
+void DOS_SetDefaultDrive(Bit8u drive);
+bool DOS_SetDrive(Bit8u drive);
+bool DOS_GetCurrentDir(Bit8u drive,char * const buffer);
+bool DOS_ChangeDir(char const * const dir);
+bool DOS_MakeDir(char const * const dir);
+bool DOS_RemoveDir(char const * const dir);
+bool DOS_Rename(char const * const oldname,char const * const newname);
+bool DOS_GetFreeDiskSpace(Bit8u drive,Bit16u * bytes,Bit8u * sectors,Bit16u * clusters,Bit16u * free);
+bool DOS_GetFileAttr(char const * const name,Bit16u * attr);
+bool DOS_SetFileAttr(char const * const name,Bit16u attr);
+
+/* IOCTL Stuff */
+bool DOS_IOCTL(void);
+bool DOS_GetSTDINStatus();
+Bit8u DOS_FindDevice(char const * name);
+void DOS_SetupDevices(void);
+
+/* Execute and new process creation */
+bool DOS_NewPSP(Bit16u pspseg,Bit16u size);
+bool DOS_ChildPSP(Bit16u pspseg,Bit16u size);
+bool DOS_Execute(char * name,PhysPt block,Bit8u flags);
+void DOS_Terminate(Bit16u pspseg,bool tsr,Bit8u exitcode);
+
+/* Memory Handling Routines */
+void DOS_SetupMemory(void);
+bool DOS_AllocateMemory(Bit16u * segment,Bit16u * blocks);
+bool DOS_ResizeMemory(Bit16u segment,Bit16u * blocks);
+bool DOS_FreeMemory(Bit16u segment);
+void DOS_FreeProcessMemory(Bit16u pspseg);
+Bit16u DOS_GetMemory(Bit16u pages);
+bool DOS_SetMemAllocStrategy(Bit16u strat);
+Bit16u DOS_GetMemAllocStrategy(void);
+void DOS_BuildUMBChain(bool umb_active,bool ems_active);
+bool DOS_LinkUMBsToMemChain(Bit16u linkstate);
+
+/* FCB stuff */
+bool DOS_FCBOpen(Bit16u seg,Bit16u offset);
+bool DOS_FCBCreate(Bit16u seg,Bit16u offset);
+bool DOS_FCBClose(Bit16u seg,Bit16u offset);
+bool DOS_FCBFindFirst(Bit16u seg,Bit16u offset);
+bool DOS_FCBFindNext(Bit16u seg,Bit16u offset);
+Bit8u DOS_FCBRead(Bit16u seg,Bit16u offset, Bit16u numBlocks);
+Bit8u DOS_FCBWrite(Bit16u seg,Bit16u offset,Bit16u numBlocks);
+Bit8u DOS_FCBRandomRead(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore);
+Bit8u DOS_FCBRandomWrite(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore);
+bool DOS_FCBGetFileSize(Bit16u seg,Bit16u offset);
+bool DOS_FCBDeleteFile(Bit16u seg,Bit16u offset);
+bool DOS_FCBRenameFile(Bit16u seg, Bit16u offset);
+void DOS_FCBSetRandomRecord(Bit16u seg, Bit16u offset);
+Bit8u FCB_Parsename(Bit16u seg,Bit16u offset,Bit8u parser ,char *string, Bit8u *change);
+bool DOS_GetAllocationInfo(Bit8u drive,Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters);
+
+/* Extra DOS Interrupts */
+void DOS_SetupMisc(void);
+
+/* The DOS Tables */
+void DOS_SetupTables(void);
+
+/* Internal DOS Setup Programs */
+void DOS_SetupPrograms(void);
+
+/* Initialize Keyboard Layout */
+void DOS_KeyboardLayout_Init(Section* sec);
+
+bool DOS_LayoutKey(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3);
+
+enum {
+ KEYB_NOERROR=0,
+ KEYB_FILENOTFOUND,
+ KEYB_INVALIDFILE,
+ KEYB_LAYOUTNOTFOUND,
+ KEYB_INVALIDCPFILE
+};
+
+
+static INLINE Bit16u long2para(Bit32u size) {
+ if (size>0xFFFF0) return 0xffff;
+ if (size&0xf) return (Bit16u)((size>>4)+1);
+ else return (Bit16u)(size>>4);
+}
+
+
+static INLINE Bit16u DOS_PackTime(Bit16u hour,Bit16u min,Bit16u sec) {
+ return (hour&0x1f)<<11 | (min&0x3f) << 5 | ((sec/2)&0x1f);
+}
+
+static INLINE Bit16u DOS_PackDate(Bit16u year,Bit16u mon,Bit16u day) {
+ return ((year-1980)&0x7f)<<9 | (mon&0x3f) << 5 | (day&0x1f);
+}
+
+/* Dos Error Codes */
+#define DOSERR_NONE 0
+#define DOSERR_FUNCTION_NUMBER_INVALID 1
+#define DOSERR_FILE_NOT_FOUND 2
+#define DOSERR_PATH_NOT_FOUND 3
+#define DOSERR_TOO_MANY_OPEN_FILES 4
+#define DOSERR_ACCESS_DENIED 5
+#define DOSERR_INVALID_HANDLE 6
+#define DOSERR_MCB_DESTROYED 7
+#define DOSERR_INSUFFICIENT_MEMORY 8
+#define DOSERR_MB_ADDRESS_INVALID 9
+#define DOSERR_ENVIRONMENT_INVALID 10
+#define DOSERR_FORMAT_INVALID 11
+#define DOSERR_ACCESS_CODE_INVALID 12
+#define DOSERR_DATA_INVALID 13
+#define DOSERR_RESERVED 14
+#define DOSERR_FIXUP_OVERFLOW 14
+#define DOSERR_INVALID_DRIVE 15
+#define DOSERR_REMOVE_CURRENT_DIRECTORY 16
+#define DOSERR_NOT_SAME_DEVICE 17
+#define DOSERR_NO_MORE_FILES 18
+#define DOSERR_FILE_ALREADY_EXISTS 80
+
+
+/* Remains some classes used to access certain things */
+#define sOffset(s,m) ((char*)&(((s*)NULL)->m)-(char*)NULL)
+#define sGet(s,m) GetIt(sizeof(((s *)&pt)->m),(PhysPt)sOffset(s,m))
+#define sSave(s,m,val) SaveIt(sizeof(((s *)&pt)->m),(PhysPt)sOffset(s,m),val)
+
+class MemStruct {
+public:
+ Bitu GetIt(Bitu size,PhysPt addr) {
+ switch (size) {
+ case 1:return mem_readb(pt+addr);
+ case 2:return mem_readw(pt+addr);
+ case 4:return mem_readd(pt+addr);
+ }
+ return 0;
+ }
+ void SaveIt(Bitu size,PhysPt addr,Bitu val) {
+ switch (size) {
+ case 1:mem_writeb(pt+addr,(Bit8u)val);break;
+ case 2:mem_writew(pt+addr,(Bit16u)val);break;
+ case 4:mem_writed(pt+addr,(Bit32u)val);break;
+ }
+ }
+ void SetPt(Bit16u seg) { pt=PhysMake(seg,0);}
+ void SetPt(Bit16u seg,Bit16u off) { pt=PhysMake(seg,off);}
+ void SetPt(RealPt addr) { pt=Real2Phys(addr);}
+protected:
+ PhysPt pt;
+};
+
+class DOS_PSP :public MemStruct {
+public:
+ DOS_PSP (Bit16u segment) { SetPt(segment);seg=segment;};
+ void MakeNew (Bit16u memSize);
+ void CopyFileTable (DOS_PSP* srcpsp,bool createchildpsp);
+ Bit16u FindFreeFileEntry (void);
+ void CloseFiles (void);
+
+ void SaveVectors (void);
+ void RestoreVectors (void);
+ void SetSize (Bit16u size) { sSave(sPSP,next_seg,size); };
+ Bit16u GetSize (void) { return (Bit16u)sGet(sPSP,next_seg); };
+ void SetEnvironment (Bit16u envseg) { sSave(sPSP,environment,envseg); };
+ Bit16u GetEnvironment (void) { return (Bit16u)sGet(sPSP,environment); };
+ Bit16u GetSegment (void) { return seg; };
+ void SetFileHandle (Bit16u index, Bit8u handle);
+ Bit8u GetFileHandle (Bit16u index);
+ void SetParent (Bit16u parent) { sSave(sPSP,psp_parent,parent); };
+ Bit16u GetParent (void) { return (Bit16u)sGet(sPSP,psp_parent); };
+ void SetStack (RealPt stackpt) { sSave(sPSP,stack,stackpt); };
+ RealPt GetStack (void) { return sGet(sPSP,stack); };
+ void SetInt22 (RealPt int22pt) { sSave(sPSP,int_22,int22pt); };
+ RealPt GetInt22 (void) { return sGet(sPSP,int_22); };
+ void SetFCB1 (RealPt src);
+ void SetFCB2 (RealPt src);
+ void SetCommandTail (RealPt src);
+ bool SetNumFiles (Bit16u fileNum);
+ Bit16u FindEntryByHandle (Bit8u handle);
+
+private:
+ #ifdef _MSC_VER
+ #pragma pack(1)
+ #endif
+ struct sPSP {
+ Bit8u exit[2]; /* CP/M-like exit poimt */
+ Bit16u next_seg; /* Segment of first byte beyond memory allocated or program */
+ Bit8u fill_1; /* single char fill */
+ Bit8u far_call; /* far call opcode */
+ RealPt cpm_entry; /* CPM Service Request address*/
+ RealPt int_22; /* Terminate Address */
+ RealPt int_23; /* Break Address */
+ RealPt int_24; /* Critical Error Address */
+ Bit16u psp_parent; /* Parent PSP Segment */
+ Bit8u files[20]; /* File Table - 0xff is unused */
+ Bit16u environment; /* Segment of evironment table */
+ RealPt stack; /* SS:SP Save point for int 0x21 calls */
+ Bit16u max_files; /* Maximum open files */
+ RealPt file_table; /* Pointer to File Table PSP:0x18 */
+ RealPt prev_psp; /* Pointer to previous PSP */
+ Bit8u interim_flag;
+ Bit8u truename_flag;
+ Bit16u nn_flags;
+ Bit16u dos_version;
+ Bit8u fill_2[14]; /* Lot's of unused stuff i can't care aboue */
+ Bit8u service[3]; /* INT 0x21 Service call int 0x21;retf; */
+ Bit8u fill_3[9]; /* This has some blocks with FCB info */
+ Bit8u fcb1[16]; /* first FCB */
+ Bit8u fcb2[16]; /* second FCB */
+ Bit8u fill_4[4]; /* unused */
+ CommandTail cmdtail;
+ } GCC_ATTRIBUTE(packed);
+ #ifdef _MSC_VER
+ #pragma pack()
+ #endif
+ Bit16u seg;
+public:
+ static Bit16u rootpsp;
+};
+
+class DOS_ParamBlock:public MemStruct {
+public:
+ DOS_ParamBlock(PhysPt addr) {pt=addr;}
+ void Clear(void);
+ void LoadData(void);
+ void SaveData(void); /* Save it as an exec block */
+ #ifdef _MSC_VER
+ #pragma pack (1)
+ #endif
+ struct sOverlay {
+ Bit16u loadseg;
+ Bit16u relocation;
+ } GCC_ATTRIBUTE(packed);
+ struct sExec {
+ Bit16u envseg;
+ RealPt cmdtail;
+ RealPt fcb1;
+ RealPt fcb2;
+ RealPt initsssp;
+ RealPt initcsip;
+ }GCC_ATTRIBUTE(packed);
+ #ifdef _MSC_VER
+ #pragma pack()
+ #endif
+ sExec exec;
+ sOverlay overlay;
+};
+
+class DOS_InfoBlock:public MemStruct {
+public:
+ DOS_InfoBlock () {};
+ void SetLocation(Bit16u seg);
+ void SetFirstMCB(Bit16u _first_mcb);
+ void SetBuffers(Bit16u x,Bit16u y);
+ void SetCurDirStruct(Bit32u _curdirstruct);
+ void SetFCBTable(Bit32u _fcbtable);
+ void SetDeviceChainStart(Bit32u _devchain);
+ void SetDiskBufferHeadPt(Bit32u _dbheadpt);
+ void SetStartOfUMBChain(Bit16u _umbstartseg);
+ void SetUMBChainState(Bit8u _umbchaining);
+ void SetBlockDevices(Bit8u _count);
+ Bit16u GetStartOfUMBChain(void);
+ Bit8u GetUMBChainState(void);
+ RealPt GetPointer(void);
+ Bit32u GetDeviceChain(void);
+
+ #ifdef _MSC_VER
+ #pragma pack(1)
+ #endif
+ struct sDIB {
+ Bit8u unknown1[4];
+ Bit16u magicWord; // -0x22 needs to be 1
+ Bit8u unknown2[8];
+ Bit16u regCXfrom5e; // -0x18 CX from last int21/ah=5e
+ Bit16u countLRUcache; // -0x16 LRU counter for FCB caching
+ Bit16u countLRUopens; // -0x14 LRU counter for FCB openings
+ Bit8u stuff[6]; // -0x12 some stuff, hopefully never used....
+ Bit16u sharingCount; // -0x0c sharing retry count
+ Bit16u sharingDelay; // -0x0a sharing retry delay
+ RealPt diskBufPtr; // -0x08 pointer to disk buffer
+ Bit16u ptrCONinput; // -0x04 pointer to con input
+ Bit16u firstMCB; // -0x02 first memory control block
+ RealPt firstDPB; // 0x00 first drive parameter block
+ RealPt firstFileTable; // 0x04 first system file table
+ RealPt activeClock; // 0x08 active clock device header
+ RealPt activeCon; // 0x0c active console device header
+ Bit16u maxSectorLength; // 0x10 maximum bytes per sector of any block device;
+ RealPt diskInfoBuffer; // 0x12 pointer to disk info buffer
+ RealPt curDirStructure; // 0x16 pointer to current array of directory structure
+ RealPt fcbTable; // 0x1a pointer to system FCB table
+ Bit16u protFCBs; // 0x1e protected fcbs
+ Bit8u blockDevices; // 0x20 installed block devices
+ Bit8u lastdrive; // 0x21 lastdrive
+ Bit32u nulNextDriver; // 0x22 NUL driver next pointer
+ Bit16u nulAttributes; // 0x26 NUL driver aattributes
+ Bit32u nulStrategy; // 0x28 NUL driver strategy routine
+ Bit8u nulString[8]; // 0x2c NUL driver name string
+ Bit8u joindedDrives; // 0x34 joined drives
+ Bit16u specialCodeSeg; // 0x35 special code segment
+ RealPt setverPtr; // 0x37 pointer to setver
+ Bit16u a20FixOfs; // 0x3b a20 fix routine offset
+ Bit16u pspLastIfHMA; // 0x3d psp of last program (if dos in hma)
+ Bit16u buffers_x; // 0x3f x in BUFFERS x,y
+ Bit16u buffers_y; // 0x41 y in BUFFERS x,y
+ Bit8u bootDrive; // 0x43 boot drive
+ Bit8u useDwordMov; // 0x44 use dword moves
+ Bit16u extendedSize; // 0x45 size of extended memory
+ Bit32u diskBufferHeadPt; // 0x47 pointer to least-recently used buffer header
+ Bit16u dirtyDiskBuffers; // 0x4b number of dirty disk buffers
+ Bit32u lookaheadBufPt; // 0x4d pointer to lookahead buffer
+ Bit16u lookaheadBufNumber; // 0x51 number of lookahead buffers
+ Bit8u bufferLocation; // 0x53 workspace buffer location
+ Bit32u workspaceBuffer; // 0x54 pointer to workspace buffer
+ Bit8u unknown3[11]; // 0x58
+ Bit8u chainingUMB; // 0x63 bit0: UMB chain linked to MCB chain
+ Bit16u minMemForExec; // 0x64 minimum paragraphs needed for current program
+ Bit16u startOfUMBChain; // 0x66 segment of first UMB-MCB
+ Bit16u memAllocScanStart; // 0x68 start paragraph for memory allocation
+ } GCC_ATTRIBUTE(packed);
+ #ifdef _MSC_VER
+ #pragma pack ()
+ #endif
+ Bit16u seg;
+};
+
+class DOS_DTA:public MemStruct{
+public:
+ DOS_DTA(RealPt addr) { SetPt(addr); }
+
+ void SetupSearch(Bit8u _sdrive,Bit8u _sattr,char * _pattern);
+ void SetResult(const char * _name,Bit32u _size,Bit16u _date,Bit16u _time,Bit8u _attr);
+
+ Bit8u GetSearchDrive(void);
+ void GetSearchParams(Bit8u & _sattr,char * _spattern);
+ void GetResult(char * _name,Bit32u & _size,Bit16u & _date,Bit16u & _time,Bit8u & _attr);
+
+ void SetDirID(Bit16u entry) { sSave(sDTA,dirID,entry); };
+ void SetDirIDCluster(Bit16u entry) { sSave(sDTA,dirCluster,entry); };
+ Bit16u GetDirID(void) { return (Bit16u)sGet(sDTA,dirID); };
+ Bit16u GetDirIDCluster(void) { return (Bit16u)sGet(sDTA,dirCluster); };
+private:
+ #ifdef _MSC_VER
+ #pragma pack(1)
+ #endif
+ struct sDTA {
+ Bit8u sdrive; /* The Drive the search is taking place */
+ Bit8u sname[8]; /* The Search pattern for the filename */
+ Bit8u sext[3]; /* The Search pattern for the extenstion */
+ Bit8u sattr; /* The Attributes that need to be found */
+ Bit16u dirID; /* custom: dir-search ID for multiple searches at the same time */
+ Bit16u dirCluster; /* custom (drive_fat only): cluster number for multiple searches at the same time */
+ Bit8u fill[4];
+ Bit8u attr;
+ Bit16u time;
+ Bit16u date;
+ Bit32u size;
+ char name[DOS_NAMELENGTH_ASCII];
+ } GCC_ATTRIBUTE(packed);
+ #ifdef _MSC_VER
+ #pragma pack()
+ #endif
+};
+
+class DOS_FCB: public MemStruct {
+public:
+ DOS_FCB(Bit16u seg,Bit16u off,bool allow_extended=true);
+ void Create(bool _extended);
+ void SetName(Bit8u _drive,char * _fname,char * _ext);
+ void SetSizeDateTime(Bit32u _size,Bit16u _date,Bit16u _time);
+ void GetSizeDateTime(Bit32u & _size,Bit16u & _date,Bit16u & _time);
+ void GetName(char * fillname);
+ void FileOpen(Bit8u _fhandle);
+ void FileClose(Bit8u & _fhandle);
+ void GetRecord(Bit16u & _cur_block,Bit8u & _cur_rec);
+ void SetRecord(Bit16u _cur_block,Bit8u _cur_rec);
+ void GetSeqData(Bit8u & _fhandle,Bit16u & _rec_size);
+ void SetSeqData(Bit8u _fhandle,Bit16u _rec_size);
+ void GetRandom(Bit32u & _random);
+ void SetRandom(Bit32u _random);
+ Bit8u GetDrive(void);
+ bool Extended(void);
+ void GetAttr(Bit8u & attr);
+ void SetAttr(Bit8u attr);
+ void SetResult(Bit32u size,Bit16u date,Bit16u time,Bit8u attr);
+ bool Valid(void);
+ void ClearBlockRecsize(void);
+private:
+ bool extended;
+ PhysPt real_pt;
+ #ifdef _MSC_VER
+ #pragma pack (1)
+ #endif
+ struct sFCB {
+ Bit8u drive; /* Drive number 0=default, 1=A, etc */
+ Bit8u filename[8]; /* Space padded name */
+ Bit8u ext[3]; /* Space padded extension */
+ Bit16u cur_block; /* Current Block */
+ Bit16u rec_size; /* Logical record size */
+ Bit32u filesize; /* File Size */
+ Bit16u date;
+ Bit16u time;
+ /* Reserved Block should be 8 bytes */
+ Bit8u sft_entries;
+ Bit8u share_attributes;
+ Bit8u extra_info;
+ /* Maybe swap file_handle and sft_entries now that fcbs
+ * aren't stored in the psp filetable anymore */
+ Bit8u file_handle;
+ Bit8u reserved[4];
+ /* end */
+ Bit8u cur_rec; /* Current record in current block */
+ Bit32u rndm; /* Current relative record number */
+ } GCC_ATTRIBUTE(packed);
+ #ifdef _MSC_VER
+ #pragma pack ()
+ #endif
+};
+
+class DOS_MCB : public MemStruct{
+public:
+ DOS_MCB(Bit16u seg) { SetPt(seg); }
+ void SetFileName(char const * const _name) { MEM_BlockWrite(pt+offsetof(sMCB,filename),_name,8); }
+ void GetFileName(char * const _name) { MEM_BlockRead(pt+offsetof(sMCB,filename),_name,8);_name[8]=0;}
+ void SetType(Bit8u _type) { sSave(sMCB,type,_type);}
+ void SetSize(Bit16u _size) { sSave(sMCB,size,_size);}
+ void SetPSPSeg(Bit16u _pspseg) { sSave(sMCB,psp_segment,_pspseg);}
+ Bit8u GetType(void) { return (Bit8u)sGet(sMCB,type);}
+ Bit16u GetSize(void) { return (Bit16u)sGet(sMCB,size);}
+ Bit16u GetPSPSeg(void) { return (Bit16u)sGet(sMCB,psp_segment);}
+private:
+ #ifdef _MSC_VER
+ #pragma pack (1)
+ #endif
+ struct sMCB {
+ Bit8u type;
+ Bit16u psp_segment;
+ Bit16u size;
+ Bit8u unused[3];
+ Bit8u filename[8];
+ } GCC_ATTRIBUTE(packed);
+ #ifdef _MSC_VER
+ #pragma pack ()
+ #endif
+};
+
+class DOS_SDA : public MemStruct {
+public:
+ DOS_SDA(Bit16u _seg,Bit16u _offs) { SetPt(_seg,_offs); }
+ void Init();
+ void SetDrive(Bit8u _drive) { sSave(sSDA,current_drive, _drive); }
+ void SetDTA(Bit32u _dta) { sSave(sSDA,current_dta, _dta); }
+ void SetPSP(Bit16u _psp) { sSave(sSDA,current_psp, _psp); }
+ Bit8u GetDrive(void) { return (Bit8u)sGet(sSDA,current_drive); }
+ Bit16u GetPSP(void) { return (Bit16u)sGet(sSDA,current_psp); }
+ Bit32u GetDTA(void) { return (Bit32u)sGet(sSDA,current_dta); }
+
+
+private:
+ #ifdef _MSC_VER
+ #pragma pack (1)
+ #endif
+ struct sSDA {
+ Bit8u crit_error_flag; /* 0x00 Critical Error Flag */
+ Bit8u inDOS_flag; /* 0x01 InDOS flag (count of active INT 21 calls) */
+ Bit8u drive_crit_error; /* 0x02 Drive on which current critical error occurred or FFh */
+ Bit8u locus_of_last_error; /* 0x03 locus of last error */
+ Bit16u extended_error_code; /* 0x04 extended error code of last error */
+ Bit8u suggested_action; /* 0x06 suggested action for last error */
+ Bit8u error_class; /* 0x07 class of last error*/
+ Bit32u last_error_pointer; /* 0x08 ES:DI pointer for last error */
+ Bit32u current_dta; /* 0x0C current DTA (Disk Transfer Address) */
+ Bit16u current_psp; /* 0x10 current PSP */
+ Bit16u sp_int_23; /* 0x12 stores SP across an INT 23 */
+ Bit16u return_code; /* 0x14 return code from last process termination (zerod after reading with AH=4Dh) */
+ Bit8u current_drive; /* 0x16 current drive */
+ Bit8u extended_break_flag; /* 0x17 extended break flag */
+ Bit8u fill[2]; /* 0x18 flag: code page switching || flag: copy of previous byte in case of INT 24 Abort*/
+ } GCC_ATTRIBUTE(packed);
+ #ifdef _MSC_VER
+ #pragma pack()
+ #endif
+};
+extern DOS_InfoBlock dos_infoblock;
+
+struct DOS_Block {
+ DOS_Date date;
+ DOS_Version version;
+ Bit16u firstMCB;
+ Bit16u errorcode;
+ Bit16u psp(){return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetPSP();};
+ void psp(Bit16u _seg){ DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetPSP(_seg);};
+ Bit16u env;
+ RealPt cpmentry;
+ RealPt dta(){return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetDTA();};
+ void dta(RealPt _dta){DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDTA(_dta);};
+ Bit8u return_code,return_mode;
+
+ Bit8u current_drive;
+ bool verify;
+ bool breakcheck;
+ bool echo; // if set to true dev_con::read will echo input
+ bool direct_output;
+ bool internal_output;
+ struct {
+ RealPt mediaid;
+ RealPt tempdta;
+ RealPt tempdta_fcbdelete;
+ RealPt dbcs;
+ RealPt filenamechar;
+ RealPt collatingseq;
+ RealPt upcase;
+ Bit8u* country;//Will be copied to dos memory. resides in real mem
+ Bit16u dpb; //Fake Disk parameter system using only the first entry so the drive letter matches
+ } tables;
+ Bit16u loaded_codepage;
+};
+
+extern DOS_Block dos;
+
+static INLINE Bit8u RealHandle(Bit16u handle) {
+ DOS_PSP psp(dos.psp());
+ return psp.GetFileHandle(handle);
+}
+
+#endif
diff --git a/include/dos_system.h b/include/dos_system.h
new file mode 100644
index 000000000..1c3289b38
--- /dev/null
+++ b/include/dos_system.h
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_DOS_SYSTEM_H
+#define DOSBOX_DOS_SYSTEM_H
+
+#include <vector>
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+#ifndef DOSBOX_CROSS_H
+#include "cross.h"
+#endif
+#ifndef DOSBOX_SUPPORT_H
+#include "support.h"
+#endif
+#ifndef DOSBOX_MEM_H
+#include "mem.h"
+#endif
+
+#define DOS_NAMELENGTH 12
+#define DOS_NAMELENGTH_ASCII (DOS_NAMELENGTH+1)
+#define DOS_FCBNAME 15
+#define DOS_DIRDEPTH 8
+#define DOS_PATHLENGTH 80
+#define DOS_TEMPSIZE 1024
+
+enum {
+ DOS_ATTR_READ_ONLY= 0x01,
+ DOS_ATTR_HIDDEN= 0x02,
+ DOS_ATTR_SYSTEM= 0x04,
+ DOS_ATTR_VOLUME= 0x08,
+ DOS_ATTR_DIRECTORY= 0x10,
+ DOS_ATTR_ARCHIVE= 0x20,
+ DOS_ATTR_DEVICE= 0x40
+};
+
+struct FileStat_Block {
+ Bit32u size;
+ Bit16u time;
+ Bit16u date;
+ Bit16u attr;
+};
+
+class DOS_DTA;
+
+class DOS_File {
+public:
+ DOS_File():flags(0) { name=0; refCtr = 0; hdrive=0xff; };
+ DOS_File(const DOS_File& orig);
+ DOS_File & operator= (const DOS_File & orig);
+ virtual ~DOS_File(){if(name) delete [] name;};
+ virtual bool Read(Bit8u * data,Bit16u * size)=0;
+ virtual bool Write(Bit8u * data,Bit16u * size)=0;
+ virtual bool Seek(Bit32u * pos,Bit32u type)=0;
+ virtual bool Close()=0;
+ virtual Bit16u GetInformation(void)=0;
+ virtual void SetName(const char* _name) { if (name) delete[] name; name = new char[strlen(_name)+1]; strcpy(name,_name); }
+ virtual char* GetName(void) { return name; };
+ virtual bool IsOpen() { return open; };
+ virtual bool IsName(const char* _name) { if (!name) return false; return strcasecmp(name,_name)==0; };
+ virtual void AddRef() { refCtr++; };
+ virtual Bits RemoveRef() { return --refCtr; };
+ virtual bool UpdateDateTimeFromHost() { return true; }
+ void SetDrive(Bit8u drv) { hdrive=drv;}
+ Bit8u GetDrive(void) { return hdrive;}
+ Bit32u flags;
+ Bit16u time;
+ Bit16u date;
+ Bit16u attr;
+ Bits refCtr;
+ bool open;
+ char* name;
+/* Some Device Specific Stuff */
+private:
+ Bit8u hdrive;
+};
+
+class DOS_Device : public DOS_File {
+public:
+ DOS_Device(const DOS_Device& orig):DOS_File(orig) {
+ devnum=orig.devnum;
+ open=true;
+ }
+ DOS_Device & operator= (const DOS_Device & orig) {
+ DOS_File::operator=(orig);
+ devnum=orig.devnum;
+ open=true;
+ return *this;
+ }
+ DOS_Device():DOS_File(),devnum(0){};
+ virtual bool Read(Bit8u * data,Bit16u * size);
+ virtual bool Write(Bit8u * data,Bit16u * size);
+ virtual bool Seek(Bit32u * pos,Bit32u type);
+ virtual bool Close();
+ virtual Bit16u GetInformation(void);
+ virtual bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode);
+ virtual bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode);
+ void SetDeviceNumber(Bitu num) { devnum=num;}
+private:
+ Bitu devnum;
+};
+
+class localFile : public DOS_File {
+public:
+ localFile(const char* name, FILE * handle);
+ bool Read(Bit8u * data,Bit16u * size);
+ bool Write(Bit8u * data,Bit16u * size);
+ bool Seek(Bit32u * pos,Bit32u type);
+ bool Close();
+ Bit16u GetInformation(void);
+ bool UpdateDateTimeFromHost(void);
+ void FlagReadOnlyMedium(void);
+ void Flush(void);
+ FILE * fhandle; //todo handle this properly
+private:
+ bool read_only_medium;
+ enum { NONE,READ,WRITE } last_action;
+};
+
+/* The following variable can be lowered to free up some memory.
+ * The negative side effect: The stored searches will be turned over faster.
+ * Should not have impact on systems with few directory entries. */
+#define MAX_OPENDIRS 2048
+//Can be high as it's only storage (16 bit variable)
+
+class DOS_Drive_Cache {
+public:
+ DOS_Drive_Cache (void);
+ DOS_Drive_Cache (const char* path);
+ ~DOS_Drive_Cache (void);
+
+ enum TDirSort { NOSORT, ALPHABETICAL, DIRALPHABETICAL, ALPHABETICALREV, DIRALPHABETICALREV };
+
+ void SetBaseDir (const char* path);
+ void SetDirSort (TDirSort sort) { sortDirType = sort; };
+ bool OpenDir (const char* path, Bit16u& id);
+ bool ReadDir (Bit16u id, char* &result);
+
+ void ExpandName (char* path);
+ char* GetExpandName (const char* path);
+ bool GetShortName (const char* fullname, char* shortname);
+
+ bool FindFirst (char* path, Bit16u& id);
+ bool FindNext (Bit16u id, char* &result);
+
+ void CacheOut (const char* path, bool ignoreLastDir = false);
+ void AddEntry (const char* path, bool checkExist = false);
+ void AddEntryDirOverlay (const char* path, bool checkExist = false);
+
+ void DeleteEntry (const char* path, bool ignoreLastDir = false);
+
+ void EmptyCache (void);
+ void SetLabel (const char* name,bool cdrom,bool allowupdate);
+ char* GetLabel (void) { return label; };
+
+ class CFileInfo {
+ public:
+ CFileInfo(void) {
+ orgname[0] = shortname[0] = 0;
+ isOverlayDir = isDir = false;
+ id = MAX_OPENDIRS;
+ nextEntry = shortNr = 0;
+ }
+ ~CFileInfo(void) {
+ for (Bit32u i=0; i<fileList.size(); i++) delete fileList[i];
+ fileList.clear();
+ longNameList.clear();
+ };
+ char orgname [CROSS_LEN];
+ char shortname [DOS_NAMELENGTH_ASCII];
+ bool isOverlayDir;
+ bool isDir;
+ Bit16u id;
+ Bitu nextEntry;
+ Bitu shortNr;
+ // contents
+ std::vector<CFileInfo*> fileList;
+ std::vector<CFileInfo*> longNameList;
+ };
+
+private:
+ void ClearFileInfo(CFileInfo *dir);
+ void DeleteFileInfo(CFileInfo *dir);
+
+ bool RemoveTrailingDot (char* shortname);
+ Bits GetLongName (CFileInfo* info, char* shortname);
+ void CreateShortName (CFileInfo* dir, CFileInfo* info);
+ Bitu CreateShortNameID (CFileInfo* dir, const char* name);
+ int CompareShortname (const char* compareName, const char* shortName);
+ bool SetResult (CFileInfo* dir, char * &result, Bitu entryNr);
+ bool IsCachedIn (CFileInfo* dir);
+ CFileInfo* FindDirInfo (const char* path, char* expandedPath);
+ bool RemoveSpaces (char* str);
+ bool OpenDir (CFileInfo* dir, const char* path, Bit16u& id);
+ void CreateEntry (CFileInfo* dir, const char* name, bool is_directory);
+ void CopyEntry (CFileInfo* dir, CFileInfo* from);
+ Bit16u GetFreeID (CFileInfo* dir);
+ void Clear (void);
+
+ CFileInfo* dirBase;
+ char dirPath [CROSS_LEN];
+ char basePath [CROSS_LEN];
+ bool dirFirstTime;
+ TDirSort sortDirType;
+ CFileInfo* save_dir;
+ char save_path [CROSS_LEN];
+ char save_expanded [CROSS_LEN];
+
+ Bit16u srchNr;
+ CFileInfo* dirSearch [MAX_OPENDIRS];
+ char dirSearchName [MAX_OPENDIRS];
+ CFileInfo* dirFindFirst [MAX_OPENDIRS];
+ Bit16u nextFreeFindFirst;
+
+ char label [CROSS_LEN];
+ bool updatelabel;
+};
+
+class DOS_Drive {
+public:
+ DOS_Drive();
+ virtual ~DOS_Drive(){};
+ virtual bool FileOpen(DOS_File * * file,char * name,Bit32u flags)=0;
+ virtual bool FileCreate(DOS_File * * file,char * name,Bit16u attributes)=0;
+ virtual bool FileUnlink(char * _name)=0;
+ virtual bool RemoveDir(char * _dir)=0;
+ virtual bool MakeDir(char * _dir)=0;
+ virtual bool TestDir(char * _dir)=0;
+ virtual bool FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst=false)=0;
+ virtual bool FindNext(DOS_DTA & dta)=0;
+ virtual bool GetFileAttr(char * name,Bit16u * attr)=0;
+ virtual bool Rename(char * oldname,char * newname)=0;
+ virtual bool AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters)=0;
+ virtual bool FileExists(const char* name)=0;
+ virtual bool FileStat(const char* name, FileStat_Block * const stat_block)=0;
+ virtual Bit8u GetMediaByte(void)=0;
+ virtual void SetDir(const char* path) { strcpy(curdir,path); };
+ virtual void EmptyCache(void) { dirCache.EmptyCache(); };
+ virtual bool isRemote(void)=0;
+ virtual bool isRemovable(void)=0;
+ virtual Bits UnMount(void)=0;
+
+ char * GetInfo(void);
+ char curdir[DOS_PATHLENGTH];
+ char info[256];
+ /* Can be overridden for example in iso images */
+ virtual char const * GetLabel(){return dirCache.GetLabel();};
+
+ DOS_Drive_Cache dirCache;
+
+ // disk cycling functionality (request resources)
+ virtual void Activate(void) {};
+};
+
+enum { OPEN_READ=0, OPEN_WRITE=1, OPEN_READWRITE=2, OPEN_READ_NO_MOD=4, DOS_NOT_INHERIT=128};
+enum { DOS_SEEK_SET=0,DOS_SEEK_CUR=1,DOS_SEEK_END=2};
+
+
+/*
+ A multiplex handler should read the registers to check what function is being called
+ If the handler returns false dos will stop checking other handlers
+*/
+
+typedef bool (MultiplexHandler)(void);
+void DOS_AddMultiplexHandler(MultiplexHandler * handler);
+void DOS_DelMultiplexHandler(MultiplexHandler * handler);
+
+/* AddDevice stores the pointer to a created device */
+void DOS_AddDevice(DOS_Device * adddev);
+/* DelDevice destroys the device that is pointed to. */
+void DOS_DelDevice(DOS_Device * dev);
+
+void VFILE_Register(const char * name,Bit8u * data,Bit32u size);
+#endif
diff --git a/include/dosbox.h b/include/dosbox.h
new file mode 100644
index 000000000..b5e091b9f
--- /dev/null
+++ b/include/dosbox.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_DOSBOX_H
+#define DOSBOX_DOSBOX_H
+
+#include "config.h"
+
+GCC_ATTRIBUTE(noreturn) void E_Exit(const char * message,...) GCC_ATTRIBUTE( __format__(__printf__, 1, 2));
+
+void MSG_Add(const char*,const char*); //add messages to the internal languagefile
+const char* MSG_Get(char const *); //get messages from the internal languagefile
+
+class Section;
+
+typedef Bitu (LoopHandler)(void);
+
+void DOSBOX_RunMachine();
+void DOSBOX_SetLoop(LoopHandler * handler);
+void DOSBOX_SetNormalLoop();
+
+void DOSBOX_Init(void);
+
+class Config;
+extern Config * control;
+
+enum MachineType {
+ MCH_HERC,
+ MCH_CGA,
+ MCH_TANDY,
+ MCH_PCJR,
+ MCH_EGA,
+ MCH_VGA
+};
+
+enum SVGACards {
+ SVGA_None,
+ SVGA_S3Trio,
+ SVGA_TsengET4K,
+ SVGA_TsengET3K,
+ SVGA_ParadisePVGA1A
+};
+
+extern SVGACards svgaCard;
+extern MachineType machine;
+extern bool SDLNetInited;
+
+#define IS_TANDY_ARCH ((machine==MCH_TANDY) || (machine==MCH_PCJR))
+#define IS_EGAVGA_ARCH ((machine==MCH_EGA) || (machine==MCH_VGA))
+#define IS_VGA_ARCH (machine==MCH_VGA)
+#define TANDY_ARCH_CASE MCH_TANDY: case MCH_PCJR
+#define EGAVGA_ARCH_CASE MCH_EGA: case MCH_VGA
+#define VGA_ARCH_CASE MCH_VGA
+
+#ifndef DOSBOX_LOGGING_H
+#include "logging.h"
+#endif // the logging system.
+
+#endif /* DOSBOX_DOSBOX_H */
diff --git a/include/fpu.h b/include/fpu.h
new file mode 100644
index 000000000..44acd31bc
--- /dev/null
+++ b/include/fpu.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_FPU_H
+#define DOSBOX_FPU_H
+
+#ifndef DOSBOX_DOSBOX_H
+//So the right config.h gets included for C_DEBUG
+#include "dosbox.h"
+#endif
+
+#ifndef DOSBOX_MEM_H
+#include "mem.h"
+#endif
+
+void FPU_ESC0_Normal(Bitu rm);
+void FPU_ESC0_EA(Bitu func,PhysPt ea);
+void FPU_ESC1_Normal(Bitu rm);
+void FPU_ESC1_EA(Bitu func,PhysPt ea);
+void FPU_ESC2_Normal(Bitu rm);
+void FPU_ESC2_EA(Bitu func,PhysPt ea);
+void FPU_ESC3_Normal(Bitu rm);
+void FPU_ESC3_EA(Bitu func,PhysPt ea);
+void FPU_ESC4_Normal(Bitu rm);
+void FPU_ESC4_EA(Bitu func,PhysPt ea);
+void FPU_ESC5_Normal(Bitu rm);
+void FPU_ESC5_EA(Bitu func,PhysPt ea);
+void FPU_ESC6_Normal(Bitu rm);
+void FPU_ESC6_EA(Bitu func,PhysPt ea);
+void FPU_ESC7_Normal(Bitu rm);
+void FPU_ESC7_EA(Bitu func,PhysPt ea);
+
+
+typedef union {
+ double d;
+#ifndef WORDS_BIGENDIAN
+ struct {
+ Bit32u lower;
+ Bit32s upper;
+ } l;
+#else
+ struct {
+ Bit32s upper;
+ Bit32u lower;
+ } l;
+#endif
+ Bit64s ll;
+} FPU_Reg;
+
+typedef struct {
+ Bit32u m1;
+ Bit32u m2;
+ Bit16u m3;
+
+ Bit16u d1;
+ Bit32u d2;
+} FPU_P_Reg;
+
+enum FPU_Tag {
+ TAG_Valid = 0,
+ TAG_Zero = 1,
+ TAG_Weird = 2,
+ TAG_Empty = 3
+};
+
+enum FPU_Round {
+ ROUND_Nearest = 0,
+ ROUND_Down = 1,
+ ROUND_Up = 2,
+ ROUND_Chop = 3
+};
+
+typedef struct {
+ FPU_Reg regs[9];
+ FPU_P_Reg p_regs[9];
+ FPU_Tag tags[9];
+ Bit16u cw,cw_mask_all;
+ Bit16u sw;
+ Bit32u top;
+ FPU_Round round;
+} FPU_rec;
+
+
+//get pi from a real library
+#define PI 3.14159265358979323846
+#define L2E 1.4426950408889634
+#define L2T 3.3219280948873623
+#define LN2 0.69314718055994531
+#define LG2 0.3010299956639812
+
+
+extern FPU_rec fpu;
+
+#define TOP fpu.top
+#define STV(i) ( (fpu.top+ (i) ) & 7 )
+
+
+Bit16u FPU_GetTag(void);
+void FPU_FLDCW(PhysPt addr);
+
+static INLINE void FPU_SetTag(Bit16u tag){
+ for(Bitu i=0;i<8;i++)
+ fpu.tags[i] = static_cast<FPU_Tag>((tag >>(2*i))&3);
+}
+
+static INLINE void FPU_SetCW(Bitu word){
+ fpu.cw = (Bit16u)word;
+ fpu.cw_mask_all = (Bit16u)(word | 0x3f);
+ fpu.round = (FPU_Round)((word >> 10) & 3);
+}
+
+
+static INLINE Bitu FPU_GET_TOP(void) {
+ return (fpu.sw & 0x3800)>>11;
+}
+
+static INLINE void FPU_SET_TOP(Bitu val){
+ fpu.sw &= ~0x3800;
+ fpu.sw |= (val&7)<<11;
+}
+
+
+static INLINE void FPU_SET_C0(Bitu C){
+ fpu.sw &= ~0x0100;
+ if(C) fpu.sw |= 0x0100;
+}
+
+static INLINE void FPU_SET_C1(Bitu C){
+ fpu.sw &= ~0x0200;
+ if(C) fpu.sw |= 0x0200;
+}
+
+static INLINE void FPU_SET_C2(Bitu C){
+ fpu.sw &= ~0x0400;
+ if(C) fpu.sw |= 0x0400;
+}
+
+static INLINE void FPU_SET_C3(Bitu C){
+ fpu.sw &= ~0x4000;
+ if(C) fpu.sw |= 0x4000;
+}
+
+#define DB_FPU_STACK_CHECK_NONE 0
+#define DB_FPU_STACK_CHECK_LOG 1
+#define DB_FPU_STACK_CHECK_EXIT 2
+//NONE is 0.74 behavior: not care about stack overflow/underflow
+//Overflow is always logged/exited on.
+//Underflow can be controlled with by this.
+//LOG is giving a message when encountered
+//EXIT is to hard exit.
+//Currently pop is ignored in release mode and overflow is exit.
+//in debug mode: pop will log and overflow is exit.
+#if C_DEBUG
+#define DB_FPU_STACK_CHECK_POP DB_FPU_STACK_CHECK_LOG
+#define DB_FPU_STACK_CHECK_PUSH DB_FPU_STACK_CHECK_EXIT
+#else
+#define DB_FPU_STACK_CHECK_POP DB_FPU_STACK_CHECK_NONE
+#define DB_FPU_STACK_CHECK_PUSH DB_FPU_STACK_CHECK_EXIT
+#endif
+
+#endif
diff --git a/include/hardware.h b/include/hardware.h
new file mode 100644
index 000000000..3a9608685
--- /dev/null
+++ b/include/hardware.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_HARDWARE_H
+#define DOSBOX_HARDWARE_H
+
+#include <stdio.h>
+
+class Section;
+enum OPL_Mode {
+ OPL_none,OPL_cms,OPL_opl2,OPL_dualopl2,OPL_opl3,OPL_opl3gold
+};
+#define CAPTURE_WAVE 0x01
+#define CAPTURE_OPL 0x02
+#define CAPTURE_MIDI 0x04
+#define CAPTURE_IMAGE 0x08
+#define CAPTURE_VIDEO 0x10
+
+extern Bitu CaptureState;
+
+void OPL_Init(Section* sec,OPL_Mode mode);
+void CMS_Init(Section* sec);
+void OPL_ShutDown(Section* sec);
+void CMS_ShutDown(Section* sec);
+
+bool SB_Get_Address(Bitu& sbaddr, Bitu& sbirq, Bitu& sbdma);
+bool TS_Get_Address(Bitu& tsaddr, Bitu& tsirq, Bitu& tsdma);
+
+extern Bit8u adlib_commandreg;
+FILE * OpenCaptureFile(const char * type,const char * ext);
+
+void CAPTURE_AddWave(Bit32u freq, Bit32u len, Bit16s * data);
+#define CAPTURE_FLAG_DBLW 0x1
+#define CAPTURE_FLAG_DBLH 0x2
+void CAPTURE_AddImage(Bitu width, Bitu height, Bitu bpp, Bitu pitch, Bitu flags, float fps, Bit8u * data, Bit8u * pal);
+void CAPTURE_AddMidi(bool sysex, Bitu len, Bit8u * data);
+
+#endif
diff --git a/include/inout.h b/include/inout.h
new file mode 100644
index 000000000..3cd1b563a
--- /dev/null
+++ b/include/inout.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_INOUT_H
+#define DOSBOX_INOUT_H
+
+#define IO_MAX (64*1024+3)
+
+#define IO_MB 0x1
+#define IO_MW 0x2
+#define IO_MD 0x4
+#define IO_MA (IO_MB | IO_MW | IO_MD )
+
+typedef Bitu IO_ReadHandler(Bitu port,Bitu iolen);
+typedef void IO_WriteHandler(Bitu port,Bitu val,Bitu iolen);
+
+extern IO_WriteHandler * io_writehandlers[3][IO_MAX];
+extern IO_ReadHandler * io_readhandlers[3][IO_MAX];
+
+void IO_RegisterReadHandler(Bitu port,IO_ReadHandler * handler,Bitu mask,Bitu range=1);
+void IO_RegisterWriteHandler(Bitu port,IO_WriteHandler * handler,Bitu mask,Bitu range=1);
+
+void IO_FreeReadHandler(Bitu port,Bitu mask,Bitu range=1);
+void IO_FreeWriteHandler(Bitu port,Bitu mask,Bitu range=1);
+
+void IO_WriteB(Bitu port,Bitu val);
+void IO_WriteW(Bitu port,Bitu val);
+void IO_WriteD(Bitu port,Bitu val);
+
+Bitu IO_ReadB(Bitu port);
+Bitu IO_ReadW(Bitu port);
+Bitu IO_ReadD(Bitu port);
+
+/* Classes to manage the IO objects created by the various devices.
+ * The io objects will remove itself on destruction.*/
+class IO_Base{
+protected:
+ bool installed;
+ Bitu m_port, m_mask,m_range;
+public:
+ IO_Base():installed(false){};
+};
+class IO_ReadHandleObject: private IO_Base{
+public:
+ void Install(Bitu port,IO_ReadHandler * handler,Bitu mask,Bitu range=1);
+ void Uninstall();
+ ~IO_ReadHandleObject();
+};
+class IO_WriteHandleObject: private IO_Base{
+public:
+ void Install(Bitu port,IO_WriteHandler * handler,Bitu mask,Bitu range=1);
+ void Uninstall();
+ ~IO_WriteHandleObject();
+};
+
+static INLINE void IO_Write(Bitu port,Bit8u val) {
+ IO_WriteB(port,val);
+}
+static INLINE Bit8u IO_Read(Bitu port){
+ return (Bit8u)IO_ReadB(port);
+}
+
+#endif
diff --git a/include/ipx.h b/include/ipx.h
new file mode 100644
index 000000000..fb88c0d94
--- /dev/null
+++ b/include/ipx.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_IPX_H
+#define DOSBOX_IPX_H
+
+// Uncomment this for a lot of debug messages:
+//#define IPX_DEBUGMSG
+
+#ifdef IPX_DEBUGMSG
+#define LOG_IPX LOG_MSG
+#else
+#if defined (_MSC_VER)
+#define LOG_IPX
+#else
+#define LOG_IPX(...)
+#endif
+#endif
+
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+#ifndef DOSBOX_MEM_H
+#include "mem.h"
+#endif
+
+// In Use Flag codes
+#define USEFLAG_AVAILABLE 0x00
+#define USEFLAG_AESTEMP 0xe0
+#define USEFLAG_IPXCRIT 0xf8
+#define USEFLAG_SPXLISTEN 0xf9
+#define USEFLAG_PROCESSING 0xfa
+#define USEFLAG_HOLDING 0xfb
+#define USEFLAG_AESWAITING 0xfc
+#define USEFLAG_AESCOUNT 0xfd
+#define USEFLAG_LISTENING 0xfe
+#define USEFLAG_SENDING 0xff
+
+// Completion codes
+#define COMP_SUCCESS 0x00
+#define COMP_REMOTETERM 0xec
+#define COMP_DISCONNECT 0xed
+#define COMP_INVALIDID 0xee
+#define COMP_SPXTABLEFULL 0xef
+#define COMP_EVENTNOTCANCELED 0xf9
+#define COMP_NOCONNECTION 0xfa
+#define COMP_CANCELLED 0xfc
+#define COMP_MALFORMED 0xfd
+#define COMP_UNDELIVERABLE 0xfe
+#define COMP_HARDWAREERROR 0xff
+
+#ifdef _MSC_VER
+#pragma pack(1)
+#endif
+
+// For Uint8 type
+#include "SDL_net.h"
+
+struct PackedIP {
+ Uint32 host;
+ Uint16 port;
+} GCC_ATTRIBUTE(packed);
+
+struct nodeType {
+ Uint8 node[6];
+} GCC_ATTRIBUTE(packed) ;
+
+struct IPXHeader {
+ Uint8 checkSum[2];
+ Uint8 length[2];
+ Uint8 transControl; // Transport control
+ Uint8 pType; // Packet type
+
+ struct transport {
+ Uint8 network[4];
+ union addrtype {
+ nodeType byNode;
+ PackedIP byIP ;
+ } GCC_ATTRIBUTE(packed) addr;
+ Uint8 socket[2];
+ } dest, src;
+} GCC_ATTRIBUTE(packed);
+
+struct fragmentDescriptor {
+ Bit16u offset;
+ Bit16u segment;
+ Bit16u size;
+};
+
+#define IPXBUFFERSIZE 1424
+
+class ECBClass {
+public:
+ RealPt ECBAddr;
+ bool isInESRList;
+ ECBClass *prevECB; // Linked List
+ ECBClass *nextECB;
+
+ Bit8u iuflag; // Need to save data since we are not always in
+ Bit16u mysocket; // real mode
+
+ Bit8u* databuffer; // received data is stored here until we get called
+ Bitu buflen; // by Interrupt
+
+#ifdef IPX_DEBUGMSG
+ Bitu SerialNumber;
+#endif
+
+ ECBClass(Bit16u segment, Bit16u offset);
+ Bit16u getSocket(void);
+
+ Bit8u getInUseFlag(void);
+
+ void setInUseFlag(Bit8u flagval);
+
+ void setCompletionFlag(Bit8u flagval);
+
+ Bit16u getFragCount(void);
+
+ bool writeData();
+ void writeDataBuffer(Bit8u* buffer, Bit16u length);
+
+ void getFragDesc(Bit16u descNum, fragmentDescriptor *fragDesc);
+ RealPt getESRAddr(void);
+
+ void NotifyESR(void);
+
+ void setImmAddress(Bit8u *immAddr);
+ void getImmAddress(Bit8u* immAddr);
+
+ ~ECBClass();
+};
+
+// The following routines may not be needed on all systems. On my build of SDL the IPaddress structure is 8 octects
+// and therefore screws up my IPXheader structure since it needs to be packed.
+
+void UnpackIP(PackedIP ipPack, IPaddress * ipAddr);
+void PackIP(IPaddress ipAddr, PackedIP *ipPack);
+
+#ifdef _MSC_VER
+#pragma pack()
+#endif
+
+#endif
diff --git a/include/ipxserver.h b/include/ipxserver.h
new file mode 100644
index 000000000..b4fa9fe80
--- /dev/null
+++ b/include/ipxserver.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_IPXSERVER_H_
+#define DOSBOX_IPXSERVER_H_
+
+#if C_IPX
+
+#include "SDL_net.h"
+
+struct packetBuffer {
+ Bit8u buffer[1024];
+ Bit16s packetSize; // Packet size remaining in read
+ Bit16s packetRead; // Bytes read of total packet
+ bool inPacket; // In packet reception flag
+ bool connected; // Connected flag
+ bool waitsize;
+};
+
+#define SOCKETTABLESIZE 16
+#define CONVIP(hostvar) hostvar & 0xff, (hostvar >> 8) & 0xff, (hostvar >> 16) & 0xff, (hostvar >> 24) & 0xff
+#define CONVIPX(hostvar) hostvar[0], hostvar[1], hostvar[2], hostvar[3], hostvar[4], hostvar[5]
+
+
+void IPX_StopServer();
+bool IPX_StartServer(Bit16u portnum);
+bool IPX_isConnectedToServer(Bits tableNum, IPaddress ** ptrAddr);
+
+Bit8u packetCRC(Bit8u *buffer, Bit16u bufSize);
+
+#endif
+
+#endif
diff --git a/include/joystick.h b/include/joystick.h
new file mode 100644
index 000000000..c3782c98e
--- /dev/null
+++ b/include/joystick.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_JOYSTICK_H
+#define DOSBOX_JOYSTICK_H
+void JOYSTICK_Enable(Bitu which,bool enabled);
+
+void JOYSTICK_Button(Bitu which,Bitu num,bool pressed);
+
+void JOYSTICK_Move_X(Bitu which,float x);
+
+void JOYSTICK_Move_Y(Bitu which,float y);
+
+bool JOYSTICK_IsEnabled(Bitu which);
+
+bool JOYSTICK_GetButton(Bitu which, Bitu num);
+
+float JOYSTICK_GetMove_X(Bitu which);
+
+float JOYSTICK_GetMove_Y(Bitu which);
+
+enum JoystickType {
+ JOY_NONE,
+ JOY_AUTO,
+ JOY_2AXIS,
+ JOY_4AXIS,
+ JOY_4AXIS_2,
+ JOY_FCS,
+ JOY_CH
+};
+
+extern JoystickType joytype;
+extern bool button_wrapping_enabled;
+#endif
diff --git a/include/keyboard.h b/include/keyboard.h
new file mode 100644
index 000000000..125c9cabb
--- /dev/null
+++ b/include/keyboard.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_KEYBOARD_H
+#define DOSBOX_KEYBOARD_H
+
+enum KBD_KEYS {
+ KBD_NONE,
+ KBD_1, KBD_2, KBD_3, KBD_4, KBD_5, KBD_6, KBD_7, KBD_8, KBD_9, KBD_0,
+ KBD_q, KBD_w, KBD_e, KBD_r, KBD_t, KBD_y, KBD_u, KBD_i, KBD_o, KBD_p,
+ KBD_a, KBD_s, KBD_d, KBD_f, KBD_g, KBD_h, KBD_j, KBD_k, KBD_l, KBD_z,
+ KBD_x, KBD_c, KBD_v, KBD_b, KBD_n, KBD_m,
+ KBD_f1, KBD_f2, KBD_f3, KBD_f4, KBD_f5, KBD_f6, KBD_f7, KBD_f8, KBD_f9, KBD_f10,KBD_f11,KBD_f12,
+
+ /*Now the weirder keys */
+
+ KBD_esc,KBD_tab,KBD_backspace,KBD_enter,KBD_space,
+ KBD_leftalt,KBD_rightalt,KBD_leftctrl,KBD_rightctrl,KBD_leftshift,KBD_rightshift,
+ KBD_capslock,KBD_scrolllock,KBD_numlock,
+
+ KBD_grave,KBD_minus,KBD_equals,KBD_backslash,KBD_leftbracket,KBD_rightbracket,
+ KBD_semicolon,KBD_quote,KBD_period,KBD_comma,KBD_slash,KBD_extra_lt_gt,
+
+ KBD_printscreen,KBD_pause,
+ KBD_insert,KBD_home,KBD_pageup,KBD_delete,KBD_end,KBD_pagedown,
+ KBD_left,KBD_up,KBD_down,KBD_right,
+
+ KBD_kp1,KBD_kp2,KBD_kp3,KBD_kp4,KBD_kp5,KBD_kp6,KBD_kp7,KBD_kp8,KBD_kp9,KBD_kp0,
+ KBD_kpdivide,KBD_kpmultiply,KBD_kpminus,KBD_kpplus,KBD_kpenter,KBD_kpperiod,
+
+
+ KBD_LAST
+};
+
+void KEYBOARD_ClrBuffer(void);
+void KEYBOARD_AddKey(KBD_KEYS keytype,bool pressed);
+
+#endif
diff --git a/include/logging.h b/include/logging.h
new file mode 100644
index 000000000..0e9c40eab
--- /dev/null
+++ b/include/logging.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_LOGGING_H
+#define DOSBOX_LOGGING_H
+enum LOG_TYPES {
+ LOG_ALL,
+ LOG_VGA, LOG_VGAGFX,LOG_VGAMISC,LOG_INT10,
+ LOG_SB,LOG_DMACONTROL,
+ LOG_FPU,LOG_CPU,LOG_PAGING,
+ LOG_FCB,LOG_FILES,LOG_IOCTL,LOG_EXEC,LOG_DOSMISC,
+ LOG_PIT,LOG_KEYBOARD,LOG_PIC,
+ LOG_MOUSE,LOG_BIOS,LOG_GUI,LOG_MISC,
+ LOG_IO,
+ LOG_PCI,
+ LOG_MAX
+};
+
+enum LOG_SEVERITIES {
+ LOG_NORMAL,
+ LOG_WARN,
+ LOG_ERROR
+};
+
+#if C_DEBUG
+class LOG
+{
+ LOG_TYPES d_type;
+ LOG_SEVERITIES d_severity;
+public:
+
+ LOG (LOG_TYPES type , LOG_SEVERITIES severity):
+ d_type(type),
+ d_severity(severity)
+ {}
+ void operator() (char const* buf, ...) GCC_ATTRIBUTE(__format__(__printf__, 2, 3)); //../src/debug/debug_gui.cpp
+
+};
+
+void DEBUG_ShowMsg(char const* format,...) GCC_ATTRIBUTE(__format__(__printf__, 1, 2));
+#define LOG_MSG DEBUG_ShowMsg
+
+#else //C_DEBUG
+
+struct LOG
+{
+ LOG(LOG_TYPES , LOG_SEVERITIES ) { }
+ void operator()(char const* ) { }
+ void operator()(char const* , double ) { }
+ void operator()(char const* , double , double ) { }
+ void operator()(char const* , double , double , double ) { }
+ void operator()(char const* , double , double , double , double ) { }
+ void operator()(char const* , double , double , double , double , double ) { }
+ void operator()(char const* , double , double , double , double , double , double ) { }
+ void operator()(char const* , double , double , double , double , double , double , double) { }
+
+
+
+ void operator()(char const* , char const* ) { }
+ void operator()(char const* , char const* , double ) { }
+ void operator()(char const* , char const* , double ,double ) { }
+ void operator()(char const* , double , char const* ) { }
+ void operator()(char const* , double , double, char const* ) { }
+ void operator()(char const* , char const*, char const*) { }
+
+ void operator()(char const* , double , double , double , char const* ) { }
+}; //add missing operators to here
+ //try to avoid anything smaller than bit32...
+void GFX_ShowMsg(char const* format,...) GCC_ATTRIBUTE(__format__(__printf__, 1, 2));
+#define LOG_MSG GFX_ShowMsg
+
+#endif //C_DEBUG
+
+
+#endif //DOSBOX_LOGGING_H
diff --git a/include/mapper.h b/include/mapper.h
new file mode 100644
index 000000000..0fd953e48
--- /dev/null
+++ b/include/mapper.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_MAPPER_H
+#define DOSBOX_MAPPER_H
+
+enum MapKeys {
+ MK_f1,MK_f2,MK_f3,MK_f4,MK_f5,MK_f6,MK_f7,MK_f8,MK_f9,MK_f10,MK_f11,MK_f12,
+ MK_return,MK_kpminus,MK_scrolllock,MK_printscreen,MK_pause,MK_home
+
+};
+
+typedef void (MAPPER_Handler)(bool pressed);
+void MAPPER_AddHandler(MAPPER_Handler * handler,MapKeys key,Bitu mods,char const * const eventname,char const * const buttonname);
+void MAPPER_Init(void);
+void MAPPER_StartUp(Section * sec);
+void MAPPER_Run(bool pressed);
+void MAPPER_RunInternal();
+void MAPPER_LosingFocus(void);
+
+
+#define MMOD1 0x1
+#define MMOD2 0x2
+
+#endif
diff --git a/include/mem.h b/include/mem.h
new file mode 100644
index 000000000..99b082e88
--- /dev/null
+++ b/include/mem.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_MEM_H
+#define DOSBOX_MEM_H
+
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+
+typedef Bit32u PhysPt;
+typedef Bit8u * HostPt;
+typedef Bit32u RealPt;
+
+typedef Bit32s MemHandle;
+
+#define MEM_PAGESIZE 4096
+
+extern HostPt MemBase;
+HostPt GetMemBase(void);
+
+bool MEM_A20_Enabled(void);
+void MEM_A20_Enable(bool enable);
+
+/* Memory management / EMS mapping */
+HostPt MEM_GetBlockPage(void);
+Bitu MEM_FreeTotal(void); //Free 4 kb pages
+Bitu MEM_FreeLargest(void); //Largest free 4 kb pages block
+Bitu MEM_TotalPages(void); //Total amount of 4 kb pages
+Bitu MEM_AllocatedPages(MemHandle handle); // amount of allocated pages of handle
+MemHandle MEM_AllocatePages(Bitu pages,bool sequence);
+MemHandle MEM_GetNextFreePage(void);
+PhysPt MEM_AllocatePage(void);
+void MEM_ReleasePages(MemHandle handle);
+bool MEM_ReAllocatePages(MemHandle & handle,Bitu pages,bool sequence);
+
+MemHandle MEM_NextHandle(MemHandle handle);
+MemHandle MEM_NextHandleAt(MemHandle handle,Bitu where);
+
+/*
+ The folowing six functions are used everywhere in the end so these should be changed for
+ Working on big or little endian machines
+*/
+
+#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY)
+
+static INLINE Bit8u host_readb(HostPt off) {
+ return off[0];
+}
+static INLINE Bit16u host_readw(HostPt off) {
+ return off[0] | (off[1] << 8);
+}
+static INLINE Bit32u host_readd(HostPt off) {
+ return off[0] | (off[1] << 8) | (off[2] << 16) | (off[3] << 24);
+}
+static INLINE void host_writeb(HostPt off,Bit8u val) {
+ off[0]=val;
+}
+static INLINE void host_writew(HostPt off,Bit16u val) {
+ off[0]=(Bit8u)(val);
+ off[1]=(Bit8u)(val >> 8);
+}
+static INLINE void host_writed(HostPt off,Bit32u val) {
+ off[0]=(Bit8u)(val);
+ off[1]=(Bit8u)(val >> 8);
+ off[2]=(Bit8u)(val >> 16);
+ off[3]=(Bit8u)(val >> 24);
+}
+
+#else
+
+static INLINE Bit8u host_readb(HostPt off) {
+ return *(Bit8u *)off;
+}
+static INLINE Bit16u host_readw(HostPt off) {
+ return *(Bit16u *)off;
+}
+static INLINE Bit32u host_readd(HostPt off) {
+ return *(Bit32u *)off;
+}
+static INLINE void host_writeb(HostPt off,Bit8u val) {
+ *(Bit8u *)(off)=val;
+}
+static INLINE void host_writew(HostPt off,Bit16u val) {
+ *(Bit16u *)(off)=val;
+}
+static INLINE void host_writed(HostPt off,Bit32u val) {
+ *(Bit32u *)(off)=val;
+}
+
+#endif
+
+
+static INLINE void var_write(Bit8u * var, Bit8u val) {
+ host_writeb((HostPt)var, val);
+}
+
+static INLINE void var_write(Bit16u * var, Bit16u val) {
+ host_writew((HostPt)var, val);
+}
+
+static INLINE void var_write(Bit32u * var, Bit32u val) {
+ host_writed((HostPt)var, val);
+}
+
+/* The Folowing six functions are slower but they recognize the paged memory system */
+
+Bit8u mem_readb(PhysPt pt);
+Bit16u mem_readw(PhysPt pt);
+Bit32u mem_readd(PhysPt pt);
+
+void mem_writeb(PhysPt pt,Bit8u val);
+void mem_writew(PhysPt pt,Bit16u val);
+void mem_writed(PhysPt pt,Bit32u val);
+
+static INLINE void phys_writeb(PhysPt addr,Bit8u val) {
+ host_writeb(MemBase+addr,val);
+}
+static INLINE void phys_writew(PhysPt addr,Bit16u val){
+ host_writew(MemBase+addr,val);
+}
+static INLINE void phys_writed(PhysPt addr,Bit32u val){
+ host_writed(MemBase+addr,val);
+}
+
+static INLINE Bit8u phys_readb(PhysPt addr) {
+ return host_readb(MemBase+addr);
+}
+static INLINE Bit16u phys_readw(PhysPt addr){
+ return host_readw(MemBase+addr);
+}
+static INLINE Bit32u phys_readd(PhysPt addr){
+ return host_readd(MemBase+addr);
+}
+
+/* These don't check for alignment, better be sure it's correct */
+
+void MEM_BlockWrite(PhysPt pt,void const * const data,Bitu size);
+void MEM_BlockRead(PhysPt pt,void * data,Bitu size);
+void MEM_BlockCopy(PhysPt dest,PhysPt src,Bitu size);
+void MEM_StrCopy(PhysPt pt,char * data,Bitu size);
+
+void mem_memcpy(PhysPt dest,PhysPt src,Bitu size);
+Bitu mem_strlen(PhysPt pt);
+void mem_strcpy(PhysPt dest,PhysPt src);
+
+/* The folowing functions are all shortcuts to the above functions using physical addressing */
+
+static INLINE Bit8u real_readb(Bit16u seg,Bit16u off) {
+ return mem_readb((seg<<4)+off);
+}
+static INLINE Bit16u real_readw(Bit16u seg,Bit16u off) {
+ return mem_readw((seg<<4)+off);
+}
+static INLINE Bit32u real_readd(Bit16u seg,Bit16u off) {
+ return mem_readd((seg<<4)+off);
+}
+
+static INLINE void real_writeb(Bit16u seg,Bit16u off,Bit8u val) {
+ mem_writeb(((seg<<4)+off),val);
+}
+static INLINE void real_writew(Bit16u seg,Bit16u off,Bit16u val) {
+ mem_writew(((seg<<4)+off),val);
+}
+static INLINE void real_writed(Bit16u seg,Bit16u off,Bit32u val) {
+ mem_writed(((seg<<4)+off),val);
+}
+
+
+static INLINE Bit16u RealSeg(RealPt pt) {
+ return (Bit16u)(pt>>16);
+}
+
+static INLINE Bit16u RealOff(RealPt pt) {
+ return (Bit16u)(pt&0xffff);
+}
+
+static INLINE PhysPt Real2Phys(RealPt pt) {
+ return (RealSeg(pt)<<4) +RealOff(pt);
+}
+
+static INLINE PhysPt PhysMake(Bit16u seg,Bit16u off) {
+ return (seg<<4)+off;
+}
+
+static INLINE RealPt RealMake(Bit16u seg,Bit16u off) {
+ return (seg<<16)+off;
+}
+
+static INLINE void RealSetVec(Bit8u vec,RealPt pt) {
+ mem_writed(vec<<2,pt);
+}
+
+static INLINE void RealSetVec(Bit8u vec,RealPt pt,RealPt &old) {
+ old = mem_readd(vec<<2);
+ mem_writed(vec<<2,pt);
+}
+
+static INLINE RealPt RealGetVec(Bit8u vec) {
+ return mem_readd(vec<<2);
+}
+
+#endif
+
diff --git a/include/midi.h b/include/midi.h
new file mode 100644
index 000000000..ea9afdada
--- /dev/null
+++ b/include/midi.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_MIDI_H
+#define DOSBOX_MIDI_H
+
+#ifndef DOSBOX_PROGRAMS_H
+#include "programs.h"
+#endif
+
+class MidiHandler {
+public:
+ MidiHandler();
+ virtual bool Open(const char * /*conf*/) { return true; };
+ virtual void Close(void) {};
+ virtual void PlayMsg(Bit8u * /*msg*/) {};
+ virtual void PlaySysex(Bit8u * /*sysex*/,Bitu /*len*/) {};
+ virtual const char * GetName(void) { return "none"; };
+ virtual void ListAll(Program * base) {};
+ virtual ~MidiHandler() { };
+ MidiHandler * next;
+};
+
+
+#define SYSEX_SIZE 8192
+struct DB_Midi {
+ Bitu status;
+ Bitu cmd_len;
+ Bitu cmd_pos;
+ Bit8u cmd_buf[8];
+ Bit8u rt_buf[8];
+ struct {
+ Bit8u buf[SYSEX_SIZE];
+ Bitu used;
+ Bitu delay;
+ Bit32u start;
+ } sysex;
+ bool available;
+ MidiHandler * handler;
+};
+
+extern DB_Midi midi;
+
+#endif
diff --git a/include/mixer.h b/include/mixer.h
new file mode 100644
index 000000000..3c8175376
--- /dev/null
+++ b/include/mixer.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_MIXER_H
+#define DOSBOX_MIXER_H
+
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+
+typedef void (*MIXER_MixHandler)(Bit8u * sampdate,Bit32u len);
+typedef void (*MIXER_Handler)(Bitu len);
+
+enum BlahModes {
+ MIXER_8MONO,MIXER_8STEREO,
+ MIXER_16MONO,MIXER_16STEREO
+};
+
+enum MixerModes {
+ M_8M,M_8S,
+ M_16M,M_16S
+};
+
+#define MIXER_BUFSIZE (16*1024)
+#define MIXER_BUFMASK (MIXER_BUFSIZE-1)
+extern Bit8u MixTemp[MIXER_BUFSIZE];
+
+#define MAX_AUDIO ((1<<(16-1))-1)
+#define MIN_AUDIO -(1<<(16-1))
+
+class MixerChannel {
+public:
+ void SetVolume(float _left,float _right);
+ void SetScale( float f );
+ void UpdateVolume(void);
+ void SetFreq(Bitu _freq);
+ void Mix(Bitu _needed);
+ void AddSilence(void); //Fill up until needed
+
+ template<class Type,bool stereo,bool signeddata,bool nativeorder>
+ void AddSamples(Bitu len, const Type* data);
+
+ void AddSamples_m8(Bitu len, const Bit8u * data);
+ void AddSamples_s8(Bitu len, const Bit8u * data);
+ void AddSamples_m8s(Bitu len, const Bit8s * data);
+ void AddSamples_s8s(Bitu len, const Bit8s * data);
+ void AddSamples_m16(Bitu len, const Bit16s * data);
+ void AddSamples_s16(Bitu len, const Bit16s * data);
+ void AddSamples_m16u(Bitu len, const Bit16u * data);
+ void AddSamples_s16u(Bitu len, const Bit16u * data);
+ void AddSamples_m32(Bitu len, const Bit32s * data);
+ void AddSamples_s32(Bitu len, const Bit32s * data);
+ void AddSamples_m16_nonnative(Bitu len, const Bit16s * data);
+ void AddSamples_s16_nonnative(Bitu len, const Bit16s * data);
+ void AddSamples_m16u_nonnative(Bitu len, const Bit16u * data);
+ void AddSamples_s16u_nonnative(Bitu len, const Bit16u * data);
+ void AddSamples_m32_nonnative(Bitu len, const Bit32s * data);
+ void AddSamples_s32_nonnative(Bitu len, const Bit32s * data);
+
+ void AddStretched(Bitu len,Bit16s * data); //Strech block up into needed data
+
+ void FillUp(void);
+ void Enable(bool _yesno);
+ MIXER_Handler handler;
+ float volmain[2];
+ float scale;
+ Bit32s volmul[2];
+
+ //This gets added the frequency counter each mixer step
+ Bitu freq_add;
+ //When this flows over a new sample needs to be read from the device
+ Bitu freq_counter;
+ //Timing on how many samples have been done and were needed by th emixer
+ Bitu done, needed;
+ //Previous and next samples
+ Bits prevSample[2];
+ Bits nextSample[2];
+ const char * name;
+ bool interpolate;
+ bool enabled;
+ MixerChannel * next;
+};
+
+MixerChannel * MIXER_AddChannel(MIXER_Handler handler,Bitu freq,const char * name);
+MixerChannel * MIXER_FindChannel(const char * name);
+/* Find the device you want to delete with findchannel "delchan gets deleted" */
+void MIXER_DelChannel(MixerChannel* delchan);
+
+/* Object to maintain a mixerchannel; As all objects it registers itself with create
+ * and removes itself when destroyed. */
+class MixerObject{
+private:
+ bool installed;
+ char m_name[32];
+public:
+ MixerObject():installed(false){};
+ MixerChannel* Install(MIXER_Handler handler,Bitu freq,const char * name);
+ ~MixerObject();
+};
+
+
+/* PC Speakers functions, tightly related to the timer functions */
+void PCSPEAKER_SetCounter(Bitu cntr,Bitu mode);
+void PCSPEAKER_SetType(Bitu mode);
+
+#endif
diff --git a/include/mouse.h b/include/mouse.h
new file mode 100644
index 000000000..f161463ba
--- /dev/null
+++ b/include/mouse.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+#ifndef DOSBOX_MOUSE_H
+#define DOSBOX_MOUSE_H
+
+
+void Mouse_ShowCursor(void);
+void Mouse_HideCursor(void);
+
+bool Mouse_SetPS2State(bool use);
+
+void Mouse_ChangePS2Callback(Bit16u pseg, Bit16u pofs);
+
+
+void Mouse_CursorMoved(float xrel,float yrel,float x,float y,bool emulate);
+void Mouse_CursorSet(float x,float y);
+void Mouse_ButtonPressed(Bit8u button);
+void Mouse_ButtonReleased(Bit8u button);
+
+void Mouse_AutoLock(bool enable);
+void Mouse_BeforeNewVideoMode(bool setmode);
+void Mouse_AfterNewVideoMode(bool setmode);
+
+#endif
diff --git a/include/paging.h b/include/paging.h
new file mode 100644
index 000000000..4fc68214b
--- /dev/null
+++ b/include/paging.h
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_PAGING_H
+#define DOSBOX_PAGING_H
+
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+#ifndef DOSBOX_MEM_H
+#include "mem.h"
+#endif
+
+// disable this to reduce the size of the TLB
+// NOTE: does not work with the dynamic core (dynrec is fine)
+#define USE_FULL_TLB
+
+class PageDirectory;
+
+#define MEM_PAGE_SIZE (4096)
+#define XMS_START (0x110)
+
+#if defined(USE_FULL_TLB)
+#define TLB_SIZE (1024*1024)
+#else
+#define TLB_SIZE 65536 // This must a power of 2 and greater then LINK_START
+#define BANK_SHIFT 28
+#define BANK_MASK 0xffff // always the same as TLB_SIZE-1?
+#define TLB_BANKS ((1024*1024/TLB_SIZE)-1)
+#endif
+
+#define PFLAG_READABLE 0x1
+#define PFLAG_WRITEABLE 0x2
+#define PFLAG_HASROM 0x4
+#define PFLAG_HASCODE32 0x8 //Page contains 32-bit dynamic code
+#define PFLAG_NOCODE 0x10 //No dynamic code can be generated here
+#define PFLAG_INIT 0x20 //No dynamic code can be generated here
+#define PFLAG_HASCODE16 0x40 //Page contains 16-bit dynamic code
+#define PFLAG_HASCODE (PFLAG_HASCODE32|PFLAG_HASCODE16)
+
+#define LINK_START ((1024+64)/4) //Start right after the HMA
+
+//Allow 128 mb of memory to be linked
+#define PAGING_LINKS (128*1024/4)
+
+class PageHandler {
+public:
+ virtual ~PageHandler(void) { }
+ virtual Bitu readb(PhysPt addr);
+ virtual Bitu readw(PhysPt addr);
+ virtual Bitu readd(PhysPt addr);
+ virtual void writeb(PhysPt addr,Bitu val);
+ virtual void writew(PhysPt addr,Bitu val);
+ virtual void writed(PhysPt addr,Bitu val);
+ virtual HostPt GetHostReadPt(Bitu phys_page);
+ virtual HostPt GetHostWritePt(Bitu phys_page);
+ virtual bool readb_checked(PhysPt addr,Bit8u * val);
+ virtual bool readw_checked(PhysPt addr,Bit16u * val);
+ virtual bool readd_checked(PhysPt addr,Bit32u * val);
+ virtual bool writeb_checked(PhysPt addr,Bitu val);
+ virtual bool writew_checked(PhysPt addr,Bitu val);
+ virtual bool writed_checked(PhysPt addr,Bitu val);
+ Bitu flags;
+};
+
+/* Some other functions */
+void PAGING_Enable(bool enabled);
+bool PAGING_Enabled(void);
+
+Bitu PAGING_GetDirBase(void);
+void PAGING_SetDirBase(Bitu cr3);
+void PAGING_InitTLB(void);
+void PAGING_ClearTLB(void);
+
+void PAGING_LinkPage(Bitu lin_page,Bitu phys_page);
+void PAGING_LinkPage_ReadOnly(Bitu lin_page,Bitu phys_page);
+void PAGING_UnlinkPages(Bitu lin_page,Bitu pages);
+/* This maps the page directly, only use when paging is disabled */
+void PAGING_MapPage(Bitu lin_page,Bitu phys_page);
+bool PAGING_MakePhysPage(Bitu & page);
+bool PAGING_ForcePageInit(Bitu lin_addr);
+
+void MEM_SetLFB(Bitu page, Bitu pages, PageHandler *handler, PageHandler *mmiohandler);
+void MEM_SetPageHandler(Bitu phys_page, Bitu pages, PageHandler * handler);
+void MEM_ResetPageHandler(Bitu phys_page, Bitu pages);
+
+
+#ifdef _MSC_VER
+#pragma pack (1)
+#endif
+struct X86_PageEntryBlock{
+#ifdef WORDS_BIGENDIAN
+ Bit32u base:20;
+ Bit32u avl:3;
+ Bit32u g:1;
+ Bit32u pat:1;
+ Bit32u d:1;
+ Bit32u a:1;
+ Bit32u pcd:1;
+ Bit32u pwt:1;
+ Bit32u us:1;
+ Bit32u wr:1;
+ Bit32u p:1;
+#else
+ Bit32u p:1;
+ Bit32u wr:1;
+ Bit32u us:1;
+ Bit32u pwt:1;
+ Bit32u pcd:1;
+ Bit32u a:1;
+ Bit32u d:1;
+ Bit32u pat:1;
+ Bit32u g:1;
+ Bit32u avl:3;
+ Bit32u base:20;
+#endif
+} GCC_ATTRIBUTE(packed);
+#ifdef _MSC_VER
+#pragma pack ()
+#endif
+
+
+union X86PageEntry {
+ Bit32u load;
+ X86_PageEntryBlock block;
+};
+
+#if !defined(USE_FULL_TLB)
+typedef struct {
+ HostPt read;
+ HostPt write;
+ PageHandler * readhandler;
+ PageHandler * writehandler;
+ Bit32u phys_page;
+} tlb_entry;
+#endif
+
+struct PagingBlock {
+ Bitu cr3;
+ Bitu cr2;
+ struct {
+ Bitu page;
+ PhysPt addr;
+ } base;
+#if defined(USE_FULL_TLB)
+ struct {
+ HostPt read[TLB_SIZE];
+ HostPt write[TLB_SIZE];
+ PageHandler * readhandler[TLB_SIZE];
+ PageHandler * writehandler[TLB_SIZE];
+ Bit32u phys_page[TLB_SIZE];
+ } tlb;
+#else
+ tlb_entry tlbh[TLB_SIZE];
+ tlb_entry *tlbh_banks[TLB_BANKS];
+#endif
+ struct {
+ Bitu used;
+ Bit32u entries[PAGING_LINKS];
+ } links;
+ Bit32u firstmb[LINK_START];
+ bool enabled;
+};
+
+extern PagingBlock paging;
+
+/* Some support functions */
+
+PageHandler * MEM_GetPageHandler(Bitu phys_page);
+
+
+/* Unaligned address handlers */
+Bit16u mem_unalignedreadw(PhysPt address);
+Bit32u mem_unalignedreadd(PhysPt address);
+void mem_unalignedwritew(PhysPt address,Bit16u val);
+void mem_unalignedwrited(PhysPt address,Bit32u val);
+
+bool mem_unalignedreadw_checked(PhysPt address,Bit16u * val);
+bool mem_unalignedreadd_checked(PhysPt address,Bit32u * val);
+bool mem_unalignedwritew_checked(PhysPt address,Bit16u val);
+bool mem_unalignedwrited_checked(PhysPt address,Bit32u val);
+
+#if defined(USE_FULL_TLB)
+
+static INLINE HostPt get_tlb_read(PhysPt address) {
+ return paging.tlb.read[address>>12];
+}
+static INLINE HostPt get_tlb_write(PhysPt address) {
+ return paging.tlb.write[address>>12];
+}
+static INLINE PageHandler* get_tlb_readhandler(PhysPt address) {
+ return paging.tlb.readhandler[address>>12];
+}
+static INLINE PageHandler* get_tlb_writehandler(PhysPt address) {
+ return paging.tlb.writehandler[address>>12];
+}
+
+/* Use these helper functions to access linear addresses in readX/writeX functions */
+static INLINE PhysPt PAGING_GetPhysicalPage(PhysPt linePage) {
+ return (paging.tlb.phys_page[linePage>>12]<<12);
+}
+
+static INLINE PhysPt PAGING_GetPhysicalAddress(PhysPt linAddr) {
+ return (paging.tlb.phys_page[linAddr>>12]<<12)|(linAddr&0xfff);
+}
+
+#else
+
+void PAGING_InitTLBBank(tlb_entry **bank);
+
+static INLINE tlb_entry *get_tlb_entry(PhysPt address) {
+ Bitu index=(address>>12);
+ if (TLB_BANKS && (index >= TLB_SIZE)) {
+ Bitu bank=(address>>BANK_SHIFT) - 1;
+ if (!paging.tlbh_banks[bank])
+ PAGING_InitTLBBank(&paging.tlbh_banks[bank]);
+ return &paging.tlbh_banks[bank][index & BANK_MASK];
+ }
+ return &paging.tlbh[index];
+}
+
+static INLINE HostPt get_tlb_read(PhysPt address) {
+ return get_tlb_entry(address)->read;
+}
+static INLINE HostPt get_tlb_write(PhysPt address) {
+ return get_tlb_entry(address)->write;
+}
+static INLINE PageHandler* get_tlb_readhandler(PhysPt address) {
+ return get_tlb_entry(address)->readhandler;
+}
+static INLINE PageHandler* get_tlb_writehandler(PhysPt address) {
+ return get_tlb_entry(address)->writehandler;
+}
+
+/* Use these helper functions to access linear addresses in readX/writeX functions */
+static INLINE PhysPt PAGING_GetPhysicalPage(PhysPt linePage) {
+ tlb_entry *entry = get_tlb_entry(linePage);
+ return (entry->phys_page<<12);
+}
+
+static INLINE PhysPt PAGING_GetPhysicalAddress(PhysPt linAddr) {
+ tlb_entry *entry = get_tlb_entry(linAddr);
+ return (entry->phys_page<<12)|(linAddr&0xfff);
+}
+#endif
+
+/* Special inlined memory reading/writing */
+
+static INLINE Bit8u mem_readb_inline(PhysPt address) {
+ HostPt tlb_addr=get_tlb_read(address);
+ if (tlb_addr) return host_readb(tlb_addr+address);
+ else return (Bit8u)(get_tlb_readhandler(address))->readb(address);
+}
+
+static INLINE Bit16u mem_readw_inline(PhysPt address) {
+ if ((address & 0xfff)<0xfff) {
+ HostPt tlb_addr=get_tlb_read(address);
+ if (tlb_addr) return host_readw(tlb_addr+address);
+ else return (Bit16u)(get_tlb_readhandler(address))->readw(address);
+ } else return mem_unalignedreadw(address);
+}
+
+static INLINE Bit32u mem_readd_inline(PhysPt address) {
+ if ((address & 0xfff)<0xffd) {
+ HostPt tlb_addr=get_tlb_read(address);
+ if (tlb_addr) return host_readd(tlb_addr+address);
+ else return (get_tlb_readhandler(address))->readd(address);
+ } else return mem_unalignedreadd(address);
+}
+
+static INLINE void mem_writeb_inline(PhysPt address,Bit8u val) {
+ HostPt tlb_addr=get_tlb_write(address);
+ if (tlb_addr) host_writeb(tlb_addr+address,val);
+ else (get_tlb_writehandler(address))->writeb(address,val);
+}
+
+static INLINE void mem_writew_inline(PhysPt address,Bit16u val) {
+ if ((address & 0xfff)<0xfff) {
+ HostPt tlb_addr=get_tlb_write(address);
+ if (tlb_addr) host_writew(tlb_addr+address,val);
+ else (get_tlb_writehandler(address))->writew(address,val);
+ } else mem_unalignedwritew(address,val);
+}
+
+static INLINE void mem_writed_inline(PhysPt address,Bit32u val) {
+ if ((address & 0xfff)<0xffd) {
+ HostPt tlb_addr=get_tlb_write(address);
+ if (tlb_addr) host_writed(tlb_addr+address,val);
+ else (get_tlb_writehandler(address))->writed(address,val);
+ } else mem_unalignedwrited(address,val);
+}
+
+
+static INLINE bool mem_readb_checked(PhysPt address, Bit8u * val) {
+ HostPt tlb_addr=get_tlb_read(address);
+ if (tlb_addr) {
+ *val=host_readb(tlb_addr+address);
+ return false;
+ } else return (get_tlb_readhandler(address))->readb_checked(address, val);
+}
+
+static INLINE bool mem_readw_checked(PhysPt address, Bit16u * val) {
+ if ((address & 0xfff)<0xfff) {
+ HostPt tlb_addr=get_tlb_read(address);
+ if (tlb_addr) {
+ *val=host_readw(tlb_addr+address);
+ return false;
+ } else return (get_tlb_readhandler(address))->readw_checked(address, val);
+ } else return mem_unalignedreadw_checked(address, val);
+}
+
+static INLINE bool mem_readd_checked(PhysPt address, Bit32u * val) {
+ if ((address & 0xfff)<0xffd) {
+ HostPt tlb_addr=get_tlb_read(address);
+ if (tlb_addr) {
+ *val=host_readd(tlb_addr+address);
+ return false;
+ } else return (get_tlb_readhandler(address))->readd_checked(address, val);
+ } else return mem_unalignedreadd_checked(address, val);
+}
+
+static INLINE bool mem_writeb_checked(PhysPt address,Bit8u val) {
+ HostPt tlb_addr=get_tlb_write(address);
+ if (tlb_addr) {
+ host_writeb(tlb_addr+address,val);
+ return false;
+ } else return (get_tlb_writehandler(address))->writeb_checked(address,val);
+}
+
+static INLINE bool mem_writew_checked(PhysPt address,Bit16u val) {
+ if ((address & 0xfff)<0xfff) {
+ HostPt tlb_addr=get_tlb_write(address);
+ if (tlb_addr) {
+ host_writew(tlb_addr+address,val);
+ return false;
+ } else return (get_tlb_writehandler(address))->writew_checked(address,val);
+ } else return mem_unalignedwritew_checked(address,val);
+}
+
+static INLINE bool mem_writed_checked(PhysPt address,Bit32u val) {
+ if ((address & 0xfff)<0xffd) {
+ HostPt tlb_addr=get_tlb_write(address);
+ if (tlb_addr) {
+ host_writed(tlb_addr+address,val);
+ return false;
+ } else return (get_tlb_writehandler(address))->writed_checked(address,val);
+ } else return mem_unalignedwrited_checked(address,val);
+}
+
+
+#endif
diff --git a/include/pci_bus.h b/include/pci_bus.h
new file mode 100644
index 000000000..3b8e332ee
--- /dev/null
+++ b/include/pci_bus.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_PCI_H
+#define DOSBOX_PCI_H
+
+//#define PCI_FUNCTIONALITY_ENABLED 0
+
+#if defined PCI_FUNCTIONALITY_ENABLED
+
+#define PCI_MAX_PCIDEVICES 10
+#define PCI_MAX_PCIFUNCTIONS 8
+
+
+class PCI_Device {
+private:
+ Bits pci_id, pci_subfunction;
+ Bit16u vendor_id, device_id;
+
+ // subdevices declarations, they will respond to pci functions 1 to 7
+ // (main device is attached to function 0)
+ Bitu num_subdevices;
+ PCI_Device* subdevices[PCI_MAX_PCIFUNCTIONS-1];
+
+public:
+ PCI_Device(Bit16u vendor, Bit16u device);
+
+ Bits PCIId(void) {
+ return pci_id;
+ }
+ Bits PCISubfunction(void) {
+ return pci_subfunction;
+ }
+ Bit16u VendorID(void) {
+ return vendor_id;
+ }
+ Bit16u DeviceID(void) {
+ return device_id;
+ }
+
+ void SetPCIId(Bitu number, Bits subfct);
+
+ bool AddSubdevice(PCI_Device* dev);
+ void RemoveSubdevice(Bits subfct);
+
+ PCI_Device* GetSubdevice(Bits subfct);
+
+ Bit16u NumSubdevices(void) {
+ if (num_subdevices>PCI_MAX_PCIFUNCTIONS-1) return (Bit16u)(PCI_MAX_PCIFUNCTIONS-1);
+ return (Bit16u)num_subdevices;
+ }
+
+ Bits GetNextSubdeviceNumber(void) {
+ if (num_subdevices>=PCI_MAX_PCIFUNCTIONS-1) return -1;
+ return (Bits)num_subdevices+1;
+ }
+
+ virtual Bits ParseReadRegister(Bit8u regnum)=0;
+ virtual bool OverrideReadRegister(Bit8u regnum, Bit8u* rval, Bit8u* rval_mask)=0;
+ virtual Bits ParseWriteRegister(Bit8u regnum,Bit8u value)=0;
+ virtual bool InitializeRegisters(Bit8u registers[256])=0;
+
+};
+
+bool PCI_IsInitialized();
+
+RealPt PCI_GetPModeInterface(void);
+
+#endif
+
+#endif
diff --git a/include/pic.h b/include/pic.h
new file mode 100644
index 000000000..5022c7c9c
--- /dev/null
+++ b/include/pic.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_PIC_H
+#define DOSBOX_PIC_H
+
+
+/* CPU Cycle Timing */
+extern Bit32s CPU_Cycles;
+extern Bit32s CPU_CycleLeft;
+extern Bit32s CPU_CycleMax;
+
+typedef void (PIC_EOIHandler) (void);
+typedef void (* PIC_EventHandler)(Bitu val);
+
+
+extern Bitu PIC_IRQCheck;
+extern Bitu PIC_Ticks;
+
+static INLINE float PIC_TickIndex(void) {
+ return (CPU_CycleMax-CPU_CycleLeft-CPU_Cycles)/(float)CPU_CycleMax;
+}
+
+static INLINE Bits PIC_TickIndexND(void) {
+ return CPU_CycleMax-CPU_CycleLeft-CPU_Cycles;
+}
+
+static INLINE Bits PIC_MakeCycles(double amount) {
+ return (Bits)(CPU_CycleMax*amount);
+}
+
+static INLINE double PIC_FullIndex(void) {
+ return PIC_Ticks+(double)PIC_TickIndex();
+}
+
+void PIC_ActivateIRQ(Bitu irq);
+void PIC_DeActivateIRQ(Bitu irq);
+
+void PIC_runIRQs(void);
+bool PIC_RunQueue(void);
+
+//Delay in milliseconds
+void PIC_AddEvent(PIC_EventHandler handler,float delay,Bitu val=0);
+void PIC_RemoveEvents(PIC_EventHandler handler);
+void PIC_RemoveSpecificEvents(PIC_EventHandler handler, Bitu val);
+
+void PIC_SetIRQMask(Bitu irq, bool masked);
+#endif
diff --git a/include/programs.h b/include/programs.h
new file mode 100644
index 000000000..ac48bd890
--- /dev/null
+++ b/include/programs.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_PROGRAMS_H
+#define DOSBOX_PROGRAMS_H
+
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+#ifndef DOSBOX_DOS_INC_H
+#include "dos_inc.h"
+#endif
+
+#ifndef CH_LIST
+#define CH_LIST
+#include <list>
+#endif
+
+#ifndef CH_STRING
+#define CH_STRING
+#include <string>
+#endif
+
+class CommandLine {
+public:
+ CommandLine(int argc,char const * const argv[]);
+ CommandLine(char const * const name,char const * const cmdline);
+ const char * GetFileName(){ return file_name.c_str();}
+
+ bool FindExist(char const * const name,bool remove=false);
+ bool FindHex(char const * const name,int & value,bool remove=false);
+ bool FindInt(char const * const name,int & value,bool remove=false);
+ bool FindString(char const * const name,std::string & value,bool remove=false);
+ bool FindCommand(unsigned int which,std::string & value);
+ bool FindStringBegin(char const * const begin,std::string & value, bool remove=false);
+ bool FindStringRemain(char const * const name,std::string & value);
+ bool FindStringRemainBegin(char const * const name,std::string & value);
+ bool GetStringRemain(std::string & value);
+ int GetParameterFromList(const char* const params[], std::vector<std::string> & output);
+ void FillVector(std::vector<std::string> & vector);
+ unsigned int GetCount(void);
+ void Shift(unsigned int amount=1);
+ Bit16u Get_arglength();
+
+private:
+ typedef std::list<std::string>::iterator cmd_it;
+ std::list<std::string> cmds;
+ std::string file_name;
+ bool FindEntry(char const * const name,cmd_it & it,bool neednext=false);
+};
+
+class Program {
+public:
+ Program();
+ virtual ~Program(){
+ delete cmd;
+ delete psp;
+ }
+ std::string temp_line;
+ CommandLine * cmd;
+ DOS_PSP * psp;
+ virtual void Run(void)=0;
+ bool GetEnvStr(const char * entry,std::string & result);
+ bool GetEnvNum(Bitu num,std::string & result);
+ Bitu GetEnvCount(void);
+ bool SetEnv(const char * entry,const char * new_string);
+ void WriteOut(const char * format,...); /* Write to standard output */
+ void WriteOut_NoParsing(const char * format); /* Write to standard output, no parsing */
+ void ChangeToLongCmd();
+
+};
+
+typedef void (PROGRAMS_Main)(Program * * make);
+void PROGRAMS_MakeFile(char const * const name,PROGRAMS_Main * main);
+
+#endif
diff --git a/include/regs.h b/include/regs.h
new file mode 100644
index 000000000..a25ff80c3
--- /dev/null
+++ b/include/regs.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_REGS_H
+#define DOSBOX_REGS_H
+
+#ifndef DOSBOX_MEM_H
+#include "mem.h"
+#endif
+
+#define FLAG_CF 0x00000001
+#define FLAG_PF 0x00000004
+#define FLAG_AF 0x00000010
+#define FLAG_ZF 0x00000040
+#define FLAG_SF 0x00000080
+#define FLAG_OF 0x00000800
+
+#define FLAG_TF 0x00000100
+#define FLAG_IF 0x00000200
+#define FLAG_DF 0x00000400
+
+#define FLAG_IOPL 0x00003000
+#define FLAG_NT 0x00004000
+#define FLAG_VM 0x00020000
+#define FLAG_AC 0x00040000
+#define FLAG_ID 0x00200000
+
+#define FMASK_TEST (FLAG_CF | FLAG_PF | FLAG_AF | FLAG_ZF | FLAG_SF | FLAG_OF)
+#define FMASK_NORMAL (FMASK_TEST | FLAG_DF | FLAG_TF | FLAG_IF )
+#define FMASK_ALL (FMASK_NORMAL | FLAG_IOPL | FLAG_NT)
+
+#define SETFLAGBIT(TYPE,TEST) if (TEST) reg_flags|=FLAG_ ## TYPE; else reg_flags&=~FLAG_ ## TYPE
+
+#define GETFLAG(TYPE) (reg_flags & FLAG_ ## TYPE)
+#define GETFLAGBOOL(TYPE) ((reg_flags & FLAG_ ## TYPE) ? true : false )
+
+#define GETFLAG_IOPL ((reg_flags & FLAG_IOPL) >> 12)
+
+struct Segment {
+ Bit16u val;
+ PhysPt phys; /* The phyiscal address start in emulated machine */
+};
+
+enum SegNames { es=0,cs,ss,ds,fs,gs};
+
+struct Segments {
+ Bit16u val[8];
+ PhysPt phys[8];
+};
+
+union GenReg32 {
+ Bit32u dword[1];
+ Bit16u word[2];
+ Bit8u byte[4];
+};
+
+#ifdef WORDS_BIGENDIAN
+
+#define DW_INDEX 0
+#define W_INDEX 1
+#define BH_INDEX 2
+#define BL_INDEX 3
+
+#else
+
+#define DW_INDEX 0
+#define W_INDEX 0
+#define BH_INDEX 1
+#define BL_INDEX 0
+
+#endif
+
+struct CPU_Regs {
+ GenReg32 regs[8],ip;
+ Bitu flags;
+};
+
+extern Segments Segs;
+extern CPU_Regs cpu_regs;
+
+static INLINE PhysPt SegPhys(SegNames index) {
+ return Segs.phys[index];
+}
+
+static INLINE Bit16u SegValue(SegNames index) {
+ return (Bit16u)Segs.val[index];
+}
+
+static INLINE RealPt RealMakeSeg(SegNames index,Bit16u off) {
+ return RealMake(SegValue(index),off);
+}
+
+
+static INLINE void SegSet16(Bitu index,Bit16u val) {
+ Segs.val[index]=val;
+ Segs.phys[index]=val << 4;
+}
+
+enum {
+ REGI_AX, REGI_CX, REGI_DX, REGI_BX,
+ REGI_SP, REGI_BP, REGI_SI, REGI_DI
+};
+
+enum {
+ REGI_AL, REGI_CL, REGI_DL, REGI_BL,
+ REGI_AH, REGI_CH, REGI_DH, REGI_BH
+};
+
+
+//macros to convert a 3-bit register index to the correct register
+#define reg_8l(reg) (cpu_regs.regs[(reg)].byte[BL_INDEX])
+#define reg_8h(reg) (cpu_regs.regs[(reg)].byte[BH_INDEX])
+#define reg_8(reg) ((reg) & 4 ? reg_8h((reg) & 3) : reg_8l((reg) & 3))
+#define reg_16(reg) (cpu_regs.regs[(reg)].word[W_INDEX])
+#define reg_32(reg) (cpu_regs.regs[(reg)].dword[DW_INDEX])
+
+#define reg_al cpu_regs.regs[REGI_AX].byte[BL_INDEX]
+#define reg_ah cpu_regs.regs[REGI_AX].byte[BH_INDEX]
+#define reg_ax cpu_regs.regs[REGI_AX].word[W_INDEX]
+#define reg_eax cpu_regs.regs[REGI_AX].dword[DW_INDEX]
+
+#define reg_bl cpu_regs.regs[REGI_BX].byte[BL_INDEX]
+#define reg_bh cpu_regs.regs[REGI_BX].byte[BH_INDEX]
+#define reg_bx cpu_regs.regs[REGI_BX].word[W_INDEX]
+#define reg_ebx cpu_regs.regs[REGI_BX].dword[DW_INDEX]
+
+#define reg_cl cpu_regs.regs[REGI_CX].byte[BL_INDEX]
+#define reg_ch cpu_regs.regs[REGI_CX].byte[BH_INDEX]
+#define reg_cx cpu_regs.regs[REGI_CX].word[W_INDEX]
+#define reg_ecx cpu_regs.regs[REGI_CX].dword[DW_INDEX]
+
+#define reg_dl cpu_regs.regs[REGI_DX].byte[BL_INDEX]
+#define reg_dh cpu_regs.regs[REGI_DX].byte[BH_INDEX]
+#define reg_dx cpu_regs.regs[REGI_DX].word[W_INDEX]
+#define reg_edx cpu_regs.regs[REGI_DX].dword[DW_INDEX]
+
+#define reg_si cpu_regs.regs[REGI_SI].word[W_INDEX]
+#define reg_esi cpu_regs.regs[REGI_SI].dword[DW_INDEX]
+
+#define reg_di cpu_regs.regs[REGI_DI].word[W_INDEX]
+#define reg_edi cpu_regs.regs[REGI_DI].dword[DW_INDEX]
+
+#define reg_sp cpu_regs.regs[REGI_SP].word[W_INDEX]
+#define reg_esp cpu_regs.regs[REGI_SP].dword[DW_INDEX]
+
+#define reg_bp cpu_regs.regs[REGI_BP].word[W_INDEX]
+#define reg_ebp cpu_regs.regs[REGI_BP].dword[DW_INDEX]
+
+#define reg_ip cpu_regs.ip.word[W_INDEX]
+#define reg_eip cpu_regs.ip.dword[DW_INDEX]
+
+#define reg_flags cpu_regs.flags
+
+#endif
diff --git a/include/render.h b/include/render.h
new file mode 100644
index 000000000..6c8df98a1
--- /dev/null
+++ b/include/render.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_RENDER_H
+#define DOSBOX_RENDER_H
+
+// 0: complex scalers off, scaler cache off, some simple scalers off, memory requirements reduced
+// 1: complex scalers off, scaler cache off, all simple scalers on
+// 2: complex scalers off, scaler cache on
+// 3: complex scalers on
+#define RENDER_USE_ADVANCED_SCALERS 3
+
+#include "../src/gui/render_scalers.h"
+
+#define RENDER_SKIP_CACHE 16
+//Enable this for scalers to support 0 input for empty lines
+//#define RENDER_NULL_INPUT
+
+typedef struct {
+ struct {
+ Bit8u red;
+ Bit8u green;
+ Bit8u blue;
+ Bit8u unused;
+ } rgb[256];
+ union {
+ Bit16u b16[256];
+ Bit32u b32[256];
+ } lut;
+ bool changed;
+ Bit8u modified[256];
+ Bitu first;
+ Bitu last;
+} RenderPal_t;
+
+typedef struct {
+ struct {
+ Bitu width, start;
+ Bitu height;
+ Bitu bpp;
+ bool dblw,dblh;
+ double ratio;
+ float fps;
+ } src;
+ struct {
+ Bitu count;
+ Bitu max;
+ Bitu index;
+ Bit8u hadSkip[RENDER_SKIP_CACHE];
+ } frameskip;
+ struct {
+ Bitu size;
+ scalerMode_t inMode;
+ scalerMode_t outMode;
+ scalerOperation_t op;
+ bool clearCache;
+ bool forced;
+ ScalerLineHandler_t lineHandler;
+ ScalerLineHandler_t linePalHandler;
+ ScalerComplexHandler_t complexHandler;
+ Bitu blocks, lastBlock;
+ Bitu outPitch;
+ Bit8u *outWrite;
+ Bitu cachePitch;
+ Bit8u *cacheRead;
+ Bitu inHeight, inLine, outLine;
+ } scale;
+ RenderPal_t pal;
+ bool updating;
+ bool active;
+ bool aspect;
+ bool fullFrame;
+} Render_t;
+
+extern Render_t render;
+extern ScalerLineHandler_t RENDER_DrawLine;
+void RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,float fps,double ratio,bool dblw,bool dblh);
+bool RENDER_StartUpdate(void);
+void RENDER_EndUpdate(bool abort);
+void RENDER_SetPal(Bit8u entry,Bit8u red,Bit8u green,Bit8u blue);
+
+
+#endif
diff --git a/include/serialport.h b/include/serialport.h
new file mode 100644
index 000000000..e9bbc543a
--- /dev/null
+++ b/include/serialport.h
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_SERIALPORT_H
+#define DOSBOX_SERIALPORT_H
+
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+#ifndef DOSBOX_INOUT_H
+#include "inout.h"
+#endif
+#ifndef DOSBOX_TIMER_H
+#include "timer.h"
+#endif
+#ifndef DOSBOX_DOS_INC_H
+#include "dos_inc.h"
+#endif
+#ifndef DOSBOX_PROGRAMS_H
+#include "programs.h"
+#endif
+
+// set this to 1 for serial debugging in release mode
+#define SERIAL_DBG_FORCED 0
+
+#if (C_DEBUG || SERIAL_DBG_FORCED)
+#define SERIAL_DEBUG 1
+#endif
+
+#if SERIAL_DEBUG
+#include "hardware.h"
+#endif
+
+// Serial port interface
+
+class MyFifo {
+public:
+ MyFifo(Bitu maxsize_) {
+ maxsize=size=maxsize_;
+ pos=used=0;
+ data=new Bit8u[size];
+ }
+ ~MyFifo() {
+ delete[] data;
+ }
+ INLINE Bitu getFree(void) {
+ return size-used;
+ }
+ bool isEmpty() {
+ return used==0;
+ }
+ bool isFull() {
+ return (size-used)==0;
+ }
+
+ INLINE Bitu getUsage(void) {
+ return used;
+ }
+ void setSize(Bitu newsize)
+ {
+ size=newsize;
+ pos=used=0;
+ }
+ void clear(void) {
+ pos=used=0;
+ data[0]=0;
+ }
+
+ bool addb(Bit8u _val) {
+ Bitu where=pos+used;
+ if (where>=size) where-=size;
+ if(used>=size) {
+ // overwrite last byte
+ if(where==0) where=size-1;
+ else where--;
+ data[where]=_val;
+ return false;
+ }
+ data[where]=_val;
+ used++;
+ return true;
+ }
+ Bit8u getb() {
+ if (!used) return data[pos];
+ Bitu where=pos;
+ used--;
+ if(used) pos++;
+ if (pos>=size) pos-=size;
+ return data[where];
+ }
+ Bit8u getTop() {
+ Bitu where=pos+used;
+ if (where>=size) where-=size;
+ if(used>=size) {
+ if(where==0) where=size-1;
+ else where--;
+ }
+ return data[where];
+ }
+
+ Bit8u probeByte() {
+ return data[pos];
+ }
+private:
+ Bit8u * data;
+ Bitu maxsize,size,pos,used;
+};
+
+class CSerial {
+public:
+
+#if SERIAL_DEBUG
+ FILE * debugfp;
+ bool dbg_modemcontrol; // RTS,CTS,DTR,DSR,RI,CD
+ bool dbg_serialtraffic;
+ bool dbg_register;
+ bool dbg_interrupt;
+ bool dbg_aux;
+ void log_ser(bool active, char const* format,...);
+#endif
+
+ static bool getBituSubstring(const char* name,Bitu* data, CommandLine* cmd);
+
+ bool InstallationSuccessful;// check after constructing. If
+ // something was wrong, delete it right away.
+
+ // Constructor takes com port number (0-3)
+ CSerial(Bitu id, CommandLine* cmd);
+
+ virtual ~CSerial();
+
+ IO_ReadHandleObject ReadHandler[8];
+ IO_WriteHandleObject WriteHandler[8];
+
+ float bytetime; // how long a byte takes to transmit/receive in milliseconds
+ void changeLineProperties();
+ Bitu idnumber;
+
+ void setEvent(Bit16u type, float duration);
+ void removeEvent(Bit16u type);
+ void handleEvent(Bit16u type);
+ virtual void handleUpperEvent(Bit16u type)=0;
+
+ // defines for event type
+#define SERIAL_TX_LOOPBACK_EVENT 0
+#define SERIAL_THR_LOOPBACK_EVENT 1
+#define SERIAL_ERRMSG_EVENT 2
+
+#define SERIAL_TX_EVENT 3
+#define SERIAL_RX_EVENT 4
+#define SERIAL_POLLING_EVENT 5
+#define SERIAL_THR_EVENT 6
+#define SERIAL_RX_TIMEOUT_EVENT 7
+
+#define SERIAL_BASE_EVENT_COUNT 7
+
+#define COMNUMBER idnumber+1
+
+ Bitu irq;
+
+ // CSerial requests an update of the input lines
+ virtual void updateMSR()=0;
+
+ // Control lines from prepherial to serial port
+ bool getDTR();
+ bool getRTS();
+
+ bool getRI();
+ bool getCD();
+ bool getDSR();
+ bool getCTS();
+
+ void setRI(bool value);
+ void setDSR(bool value);
+ void setCD(bool value);
+ void setCTS(bool value);
+
+ // From serial port to prepherial
+ // set output lines
+ virtual void setRTSDTR(bool rts, bool dtr)=0;
+ virtual void setRTS(bool val)=0;
+ virtual void setDTR(bool val)=0;
+
+ // Register access
+ void Write_THR(Bit8u data);
+ void Write_IER(Bit8u data);
+ void Write_FCR(Bit8u data);
+ void Write_LCR(Bit8u data);
+ void Write_MCR(Bit8u data);
+ // Really old hardware seems to have the delta part of this register writable
+ void Write_MSR(Bit8u data);
+ void Write_SPR(Bit8u data);
+ void Write_reserved(Bit8u data, Bit8u address);
+
+ Bitu Read_RHR();
+ Bitu Read_IER();
+ Bitu Read_ISR();
+ Bitu Read_LCR();
+ Bitu Read_MCR();
+ Bitu Read_LSR();
+ Bitu Read_MSR();
+ Bitu Read_SPR();
+
+ // If a byte comes from loopback or prepherial, put it in here.
+ void receiveByte(Bit8u data);
+ void receiveByteEx(Bit8u data, Bit8u error);
+
+ // If an error was received, put it here (in LSR register format)
+ void receiveError(Bit8u errorword);
+
+ // depratched
+ // connected device checks, if port can receive data:
+ bool CanReceiveByte();
+
+ // when THR was shifted to TX
+ void ByteTransmitting();
+
+ // When done sending, notify here
+ void ByteTransmitted();
+
+ // Transmit byte to prepherial
+ virtual void transmitByte(Bit8u val, bool first)=0;
+
+ // switch break state to the passed value
+ virtual void setBreak(bool value)=0;
+
+ // change baudrate, number of bits, parity, word length al at once
+ virtual void updatePortConfig(Bit16u divider, Bit8u lcr)=0;
+
+ void Init_Registers();
+
+ bool Putchar(Bit8u data, bool wait_dtr, bool wait_rts, Bitu timeout);
+ bool Getchar(Bit8u* data, Bit8u* lsr, bool wait_dsr, Bitu timeout);
+
+
+private:
+
+ DOS_Device* mydosdevice;
+
+ // I used this spec: st16c450v420.pdf
+
+ void ComputeInterrupts();
+
+ // a sub-interrupt is triggered
+ void rise(Bit8u priority);
+
+ // clears the pending sub-interrupt
+ void clear(Bit8u priority);
+
+ #define ERROR_PRIORITY 4 // overrun, parity error, frame error, break
+ #define RX_PRIORITY 1 // a byte has been received
+ #define TX_PRIORITY 2 // tx buffer has become empty
+ #define MSR_PRIORITY 8 // CRS, DSR, RI, DCD change
+ #define TIMEOUT_PRIORITY 0x10
+ #define NONE_PRIORITY 0
+
+ Bit8u waiting_interrupts; // these are on, but maybe not enabled
+
+ // 16C550
+ // read/write name
+
+ Bit16u baud_divider;
+ #define RHR_OFFSET 0 // r Receive Holding Register, also LSB of Divisor Latch (r/w)
+ // Data: whole byte
+ #define THR_OFFSET 0 // w Transmit Holding Register
+ // Data: whole byte
+ Bit8u IER; // r/w Interrupt Enable Register, also MSB of Divisor Latch
+ #define IER_OFFSET 1
+
+ bool irq_active;
+
+ #define RHR_INT_Enable_MASK 0x1
+ #define THR_INT_Enable_MASK 0x2
+ #define Receive_Line_INT_Enable_MASK 0x4
+ #define Modem_Status_INT_Enable_MASK 0x8
+
+ Bit8u ISR; // r Interrupt Status Register
+ #define ISR_OFFSET 2
+
+ #define ISR_CLEAR_VAL 0x1
+ #define ISR_FIFOTIMEOUT_VAL 0xc
+ #define ISR_ERROR_VAL 0x6
+ #define ISR_RX_VAL 0x4
+ #define ISR_TX_VAL 0x2
+ #define ISR_MSR_VAL 0x0
+public:
+ Bit8u LCR; // r/w Line Control Register
+private:
+ #define LCR_OFFSET 3
+ // bit0: word length bit0
+ // bit1: word length bit1
+ // bit2: stop bits
+ // bit3: parity enable
+ // bit4: even parity
+ // bit5: set parity
+ // bit6: set break
+ // bit7: divisor latch enable
+
+
+ #define LCR_BREAK_MASK 0x40
+ #define LCR_DIVISOR_Enable_MASK 0x80
+ #define LCR_PORTCONFIG_MASK 0x3F
+
+ #define LCR_PARITY_NONE 0x0
+ #define LCR_PARITY_ODD 0x8
+ #define LCR_PARITY_EVEN 0x18
+ #define LCR_PARITY_MARK 0x28
+ #define LCR_PARITY_SPACE 0x38
+
+ #define LCR_DATABITS_5 0x0
+ #define LCR_DATABITS_6 0x1
+ #define LCR_DATABITS_7 0x2
+ #define LCR_DATABITS_8 0x3
+
+ #define LCR_STOPBITS_1 0x0
+ #define LCR_STOPBITS_MORE_THAN_1 0x4
+
+ // Modem Control Register
+ // r/w
+ #define MCR_OFFSET 4
+ bool dtr; // bit0: DTR
+ bool rts; // bit1: RTS
+ bool op1; // bit2: OP1
+ bool op2; // bit3: OP2
+ bool loopback; // bit4: loop back enable
+
+ #define MCR_DTR_MASK 0x1
+ #define MCR_RTS_MASK 0x2
+ #define MCR_OP1_MASK 0x4
+ #define MCR_OP2_MASK 0x8
+ #define MCR_LOOPBACK_Enable_MASK 0x10
+public:
+ Bit8u LSR; // r Line Status Register
+private:
+
+ #define LSR_OFFSET 5
+
+ #define LSR_RX_DATA_READY_MASK 0x1
+ #define LSR_OVERRUN_ERROR_MASK 0x2
+ #define LSR_PARITY_ERROR_MASK 0x4
+ #define LSR_FRAMING_ERROR_MASK 0x8
+ #define LSR_RX_BREAK_MASK 0x10
+ #define LSR_TX_HOLDING_EMPTY_MASK 0x20
+ #define LSR_TX_EMPTY_MASK 0x40
+
+ #define LSR_ERROR_MASK 0x1e
+
+ // error printing
+ bool errormsg_pending;
+ Bitu framingErrors;
+ Bitu parityErrors;
+ Bitu overrunErrors;
+ Bitu txOverrunErrors;
+ Bitu overrunIF0;
+ Bitu breakErrors;
+
+
+ // Modem Status Register
+ // r
+ #define MSR_OFFSET 6
+ bool d_cts; // bit0: deltaCTS
+ bool d_dsr; // bit1: deltaDSR
+ bool d_ri; // bit2: deltaRI
+ bool d_cd; // bit3: deltaCD
+ bool cts; // bit4: CTS
+ bool dsr; // bit5: DSR
+ bool ri; // bit6: RI
+ bool cd; // bit7: CD
+
+ #define MSR_delta_MASK 0xf
+ #define MSR_LINE_MASK 0xf0
+
+ #define MSR_dCTS_MASK 0x1
+ #define MSR_dDSR_MASK 0x2
+ #define MSR_dRI_MASK 0x4
+ #define MSR_dCD_MASK 0x8
+ #define MSR_CTS_MASK 0x10
+ #define MSR_DSR_MASK 0x20
+ #define MSR_RI_MASK 0x40
+ #define MSR_CD_MASK 0x80
+
+ Bit8u SPR; // r/w Scratchpad Register
+ #define SPR_OFFSET 7
+
+
+ // For loopback purposes...
+ Bit8u loopback_data;
+ void transmitLoopbackByte(Bit8u val, bool value);
+
+ // 16C550 (FIFO)
+ public: // todo remove
+ MyFifo* rxfifo;
+ private:
+ MyFifo* txfifo;
+ MyFifo* errorfifo;
+ Bitu errors_in_fifo;
+ Bitu rx_interrupt_threshold;
+ Bitu fifosize;
+ Bit8u FCR;
+ bool sync_guardtime;
+ #define FIFO_STATUS_ACTIVE 0xc0 // FIFO is active AND works ;)
+ #define FIFO_ERROR 0x80
+ #define FCR_ACTIVATE 0x01
+ #define FCR_CLEAR_RX 0x02
+ #define FCR_CLEAR_TX 0x04
+ #define FCR_OFFSET 2
+ #define FIFO_FLOWCONTROL 0x20
+};
+
+extern CSerial* serialports[];
+const Bit8u serial_defaultirq[] = { 4, 3, 4, 3 };
+const Bit16u serial_baseaddr[] = {0x3f8,0x2f8,0x3e8,0x2e8};
+const char* const serial_comname[]={"COM1","COM2","COM3","COM4"};
+
+// the COM devices
+
+class device_COM : public DOS_Device {
+public:
+ // Creates a COM device that communicates with the num-th parallel port, i.e. is LPTnum
+ device_COM(class CSerial* sc);
+ ~device_COM();
+ bool Read(Bit8u * data,Bit16u * size);
+ bool Write(Bit8u * data,Bit16u * size);
+ bool Seek(Bit32u * pos,Bit32u type);
+ bool Close();
+ Bit16u GetInformation(void);
+private:
+ CSerial* sclass;
+};
+
+#endif
diff --git a/include/setup.h b/include/setup.h
new file mode 100644
index 000000000..27c917764
--- /dev/null
+++ b/include/setup.h
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_SETUP_H
+#define DOSBOX_SETUP_H
+
+#ifdef _MSC_VER
+#pragma warning ( disable : 4786 )
+#pragma warning ( disable : 4290 )
+#endif
+
+
+#ifndef CH_LIST
+#define CH_LIST
+#include <list>
+#endif
+
+#ifndef CH_VECTOR
+#define CH_VECTOR
+#include <vector>
+#endif
+
+#ifndef CH_STRING
+#define CH_STRING
+#include <string>
+#endif
+
+#ifndef CH_CSTDIO
+#define CH_CSTDIO
+#include <stdio.h>
+#endif
+
+
+class Hex {
+private:
+ int _hex;
+public:
+ Hex(int in):_hex(in) { };
+ Hex():_hex(0) { };
+ bool operator==(Hex const& other) {return _hex == other._hex;}
+ operator int () const { return _hex; }
+};
+
+class Value {
+/*
+ * Multitype storage container that is aware of the currently stored type in it.
+ * Value st = "hello";
+ * Value in = 1;
+ * st = 12 //Exception
+ * in = 12 //works
+ */
+private:
+ Hex _hex;
+ bool _bool;
+ int _int;
+ std::string* _string;
+ double _double;
+public:
+ class WrongType { }; // Conversion error class
+ enum Etype { V_NONE, V_HEX, V_BOOL, V_INT, V_STRING, V_DOUBLE,V_CURRENT} type;
+
+ /* Constructors */
+ Value() :_string(0), type(V_NONE) { };
+ Value(Hex in) :_hex(in), type(V_HEX) { };
+ Value(int in) :_int(in), type(V_INT) { };
+ Value(bool in) :_bool(in), type(V_BOOL) { };
+ Value(double in) :_double(in), type(V_DOUBLE) { };
+ Value(std::string const& in) :_string(new std::string(in)),type(V_STRING) { };
+ Value(char const * const in) :_string(new std::string(in)),type(V_STRING) { };
+ Value(Value const& in):_string(0) {plaincopy(in);}
+ ~Value() { destroy();};
+ Value(std::string const& in,Etype _t) :_hex(0),_bool(false),_int(0),_string(0),_double(0),type(V_NONE) {SetValue(in,_t);}
+
+ /* Assigment operators */
+ Value& operator= (Hex in) { return copy(Value(in));}
+ Value& operator= (int in) { return copy(Value(in));}
+ Value& operator= (bool in) { return copy(Value(in));}
+ Value& operator= (double in) { return copy(Value(in));}
+ Value& operator= (std::string const& in) { return copy(Value(in));}
+ Value& operator= (char const * const in) { return copy(Value(in));}
+ Value& operator= (Value const& in) { return copy(Value(in));}
+
+ bool operator== (Value const & other);
+ operator bool () const;
+ operator Hex () const;
+ operator int () const;
+ operator double () const;
+ operator char const* () const;
+ bool SetValue(std::string const& in,Etype _type = V_CURRENT);
+ std::string ToString() const;
+
+private:
+ void destroy() throw();
+ Value& copy(Value const& in);
+ void plaincopy(Value const& in) throw();
+ bool set_hex(std::string const& in);
+ bool set_int(std::string const&in);
+ bool set_bool(std::string const& in);
+ void set_string(std::string const& in);
+ bool set_double(std::string const& in);
+};
+
+class Property {
+public:
+ struct Changeable { enum Value {Always, WhenIdle,OnlyAtStart};};
+ const std::string propname;
+
+ Property(std::string const& _propname, Changeable::Value when):propname(_propname),change(when) { }
+ void Set_values(const char * const * in);
+ void Set_help(std::string const& str);
+ char const* Get_help();
+ virtual bool SetValue(std::string const& str)=0;
+ Value const& GetValue() const { return value;}
+ Value const& Get_Default_Value() const { return default_value; }
+ //CheckValue returns true, if value is in suggested_values;
+ //Type specific properties are encouraged to override this and check for type
+ //specific features.
+ virtual bool CheckValue(Value const& in, bool warn);
+public:
+ virtual ~Property(){ }
+ virtual const std::vector<Value>& GetValues() const;
+ Value::Etype Get_type(){return default_value.type;}
+ Changeable::Value getChange() {return change;}
+
+protected:
+ //Set interval value to in or default if in is invalid. force always sets the value.
+ //Can be overriden to set a different value if invalid.
+ virtual bool SetVal(Value const& in, bool forced,bool warn=true) {
+ if(forced || CheckValue(in,warn)) {
+ value = in; return true;
+ } else {
+ value = default_value; return false;
+ }
+ }
+ Value value;
+ std::vector<Value> suggested_values;
+ typedef std::vector<Value>::iterator iter;
+ Value default_value;
+ const Changeable::Value change;
+};
+
+class Prop_int:public Property {
+public:
+ Prop_int(std::string const& _propname,Changeable::Value when, int _value)
+ :Property(_propname,when) {
+ default_value = value = _value;
+ min = max = -1;
+ }
+ Prop_int(std::string const& _propname,Changeable::Value when, int _min,int _max,int _value)
+ :Property(_propname,when) {
+ default_value = value = _value;
+ min = _min;
+ max = _max;
+ }
+ int getMin() { return min;}
+ int getMax() { return max;}
+ void SetMinMax(Value const& min,Value const& max) {this->min = min; this->max=max;}
+ bool SetValue(std::string const& in);
+ ~Prop_int(){ }
+ virtual bool CheckValue(Value const& in, bool warn);
+ // Override SetVal, so it takes min,max in account when there are no suggested values
+ virtual bool SetVal(Value const& in, bool forced,bool warn=true);
+
+private:
+ Value min,max;
+};
+
+class Prop_double:public Property {
+public:
+ Prop_double(std::string const & _propname, Changeable::Value when, double _value)
+ :Property(_propname,when){
+ default_value = value = _value;
+ }
+ bool SetValue(std::string const& input);
+ ~Prop_double(){ }
+};
+
+class Prop_bool:public Property {
+public:
+ Prop_bool(std::string const& _propname, Changeable::Value when, bool _value)
+ :Property(_propname,when) {
+ default_value = value = _value;
+ }
+ bool SetValue(std::string const& in);
+ ~Prop_bool(){ }
+};
+
+class Prop_string:public Property{
+public:
+ Prop_string(std::string const& _propname, Changeable::Value when, char const * const _value)
+ :Property(_propname,when) {
+ default_value = value = _value;
+ }
+ bool SetValue(std::string const& in);
+ virtual bool CheckValue(Value const& in, bool warn);
+ ~Prop_string(){ }
+};
+class Prop_path:public Prop_string{
+public:
+ std::string realpath;
+ Prop_path(std::string const& _propname, Changeable::Value when, char const * const _value)
+ :Prop_string(_propname,when,_value) {
+ default_value = value = _value;
+ realpath = _value;
+ }
+ bool SetValue(std::string const& in);
+ ~Prop_path(){ }
+};
+
+class Prop_hex:public Property {
+public:
+ Prop_hex(std::string const& _propname, Changeable::Value when, Hex _value)
+ :Property(_propname,when) {
+ default_value = value = _value;
+ }
+ bool SetValue(std::string const& in);
+ ~Prop_hex(){ }
+};
+
+#define NO_SUCH_PROPERTY "PROP_NOT_EXIST"
+class Section {
+private:
+ typedef void (*SectionFunction)(Section*);
+ /* Wrapper class around startup and shutdown functions. the variable
+ * canchange indicates it can be called on configuration changes */
+ struct Function_wrapper {
+ SectionFunction function;
+ bool canchange;
+ Function_wrapper(SectionFunction const _fun,bool _ch){
+ function=_fun;
+ canchange=_ch;
+ }
+ };
+ std::list<Function_wrapper> initfunctions;
+ std::list<Function_wrapper> destroyfunctions;
+ std::string sectionname;
+public:
+ Section(std::string const& _sectionname):sectionname(_sectionname) { }
+
+ void AddInitFunction(SectionFunction func,bool canchange=false);
+ void AddDestroyFunction(SectionFunction func,bool canchange=false);
+ void ExecuteInit(bool initall=true);
+ void ExecuteDestroy(bool destroyall=true);
+ const char* GetName() const {return sectionname.c_str();}
+
+ virtual std::string GetPropValue(std::string const& _property) const =0;
+ virtual bool HandleInputline(std::string const& _line)=0;
+ virtual void PrintData(FILE* outfile) const =0;
+ virtual ~Section() { /*Children must call executedestroy ! */}
+};
+
+class Prop_multival;
+class Prop_multival_remain;
+class Section_prop:public Section {
+private:
+ std::list<Property*> properties;
+ typedef std::list<Property*>::iterator it;
+ typedef std::list<Property*>::const_iterator const_it;
+
+public:
+ Section_prop(std::string const& _sectionname):Section(_sectionname){}
+ Prop_int* Add_int(std::string const& _propname, Property::Changeable::Value when, int _value=0);
+ Prop_string* Add_string(std::string const& _propname, Property::Changeable::Value when, char const * const _value=NULL);
+ Prop_path* Add_path(std::string const& _propname, Property::Changeable::Value when, char const * const _value=NULL);
+ Prop_bool* Add_bool(std::string const& _propname, Property::Changeable::Value when, bool _value=false);
+ Prop_hex* Add_hex(std::string const& _propname, Property::Changeable::Value when, Hex _value=0);
+// void Add_double(char const * const _propname, double _value=0.0);
+ Prop_multival *Add_multi(std::string const& _propname, Property::Changeable::Value when,std::string const& sep);
+ Prop_multival_remain *Add_multiremain(std::string const& _propname, Property::Changeable::Value when,std::string const& sep);
+
+ Property* Get_prop(int index);
+ int Get_int(std::string const& _propname) const;
+ const char* Get_string(std::string const& _propname) const;
+ bool Get_bool(std::string const& _propname) const;
+ Hex Get_hex(std::string const& _propname) const;
+ double Get_double(std::string const& _propname) const;
+ Prop_path* Get_path(std::string const& _propname) const;
+ Prop_multival* Get_multival(std::string const& _propname) const;
+ Prop_multival_remain* Get_multivalremain(std::string const& _propname) const;
+ bool HandleInputline(std::string const& gegevens);
+ void PrintData(FILE* outfile) const;
+ virtual std::string GetPropValue(std::string const& _property) const;
+ //ExecuteDestroy should be here else the destroy functions use destroyed properties
+ virtual ~Section_prop();
+};
+
+class Prop_multival:public Property{
+protected:
+ Section_prop* section;
+ std::string separator;
+ void make_default_value();
+public:
+ Prop_multival(std::string const& _propname, Changeable::Value when,std::string const& sep):Property(_propname,when), section(new Section_prop("")),separator(sep) {
+ default_value = value = "";
+ }
+ Section_prop *GetSection() { return section; }
+ const Section_prop *GetSection() const { return section; }
+ virtual bool SetValue(std::string const& input);
+ virtual const std::vector<Value>& GetValues() const;
+ ~Prop_multival() { delete section; }
+}; //value bevat totale string. setvalue zet elk van de sub properties en checked die.
+
+class Prop_multival_remain:public Prop_multival{
+public:
+ Prop_multival_remain(std::string const& _propname, Changeable::Value when,std::string const& sep):Prop_multival(_propname,when,sep){ }
+
+ virtual bool SetValue(std::string const& input);
+};
+
+
+class Section_line: public Section{
+public:
+ Section_line(std::string const& _sectionname):Section(_sectionname){}
+ ~Section_line(){ExecuteDestroy(true);}
+ bool HandleInputline(std::string const& gegevens);
+ void PrintData(FILE* outfile) const;
+ virtual std::string GetPropValue(std::string const& _property) const;
+ std::string data;
+};
+
+class Module_base {
+ /* Base for all hardware and software "devices" */
+protected:
+ Section* m_configuration;
+public:
+ Module_base(Section* configuration){m_configuration=configuration;};
+// Module_base(Section* configuration, SaveState* state) {};
+ virtual ~Module_base(){/*LOG_MSG("executed")*/;};//Destructors are required
+ /* Returns true if succesful.*/
+ virtual bool Change_Config(Section* /*newconfig*/) {return false;} ;
+};
+#endif
diff --git a/include/shell.h b/include/shell.h
new file mode 100644
index 000000000..fc4728562
--- /dev/null
+++ b/include/shell.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_SHELL_H
+#define DOSBOX_SHELL_H
+
+#include <ctype.h>
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+#ifndef DOSBOX_PROGRAMS_H
+#include "programs.h"
+#endif
+
+#include <string>
+#include <list>
+
+#define CMD_MAXLINE 4096
+#define CMD_MAXCMDS 20
+#define CMD_OLDSIZE 4096
+extern Bitu call_shellstop;
+class DOS_Shell;
+
+/* first_shell is used to add and delete stuff from the shell env
+ * by "external" programs. (config) */
+extern DOS_Shell * first_shell;
+
+
+class BatchFile {
+public:
+ BatchFile(DOS_Shell * host,char const* const resolved_name,char const* const entered_name, char const * const cmd_line);
+ virtual ~BatchFile();
+ virtual bool ReadLine(char * line);
+ bool Goto(char * where);
+ void Shift(void);
+ Bit16u file_handle;
+ Bit32u location;
+ bool echo;
+ DOS_Shell * shell;
+ BatchFile * prev;
+ CommandLine * cmd;
+ std::string filename;
+};
+
+class AutoexecEditor;
+class DOS_Shell : public Program {
+private:
+ friend class AutoexecEditor;
+ std::list<std::string> l_history, l_completion;
+
+ char *completion_start;
+ Bit16u completion_index;
+
+public:
+
+ DOS_Shell();
+
+ void Run(void);
+ void RunInternal(void); //for command /C
+/* A load of subfunctions */
+ void ParseLine(char * line);
+ Bitu GetRedirection(char *s, char **ifn, char **ofn,bool * append);
+ void InputCommand(char * line);
+ void ShowPrompt();
+ void DoCommand(char * cmd);
+ bool Execute(char * name,char * args);
+ /* Checks if it matches a hardware-property */
+ bool CheckConfig(char* cmd_in,char*line);
+/* Some internal used functions */
+ char * Which(char * name);
+/* Some supported commands */
+ void CMD_HELP(char * args);
+ void CMD_CLS(char * args);
+ void CMD_COPY(char * args);
+ void CMD_DATE(char * args);
+ void CMD_TIME(char * args);
+ void CMD_DIR(char * args);
+ void CMD_DELETE(char * args);
+ void CMD_ECHO(char * args);
+ void CMD_EXIT(char * args);
+ void CMD_MKDIR(char * args);
+ void CMD_CHDIR(char * args);
+ void CMD_RMDIR(char * args);
+ void CMD_SET(char * args);
+ void CMD_IF(char * args);
+ void CMD_GOTO(char * args);
+ void CMD_TYPE(char * args);
+ void CMD_REM(char * args);
+ void CMD_RENAME(char * args);
+ void CMD_CALL(char * args);
+ void SyntaxError(void);
+ void CMD_PAUSE(char * args);
+ void CMD_SUBST(char* args);
+ void CMD_LOADHIGH(char* args);
+ void CMD_CHOICE(char * args);
+ void CMD_ATTRIB(char * args);
+ void CMD_PATH(char * args);
+ void CMD_SHIFT(char * args);
+ void CMD_VER(char * args);
+ /* The shell's variables */
+ Bit16u input_handle;
+ BatchFile * bf;
+ bool echo;
+ bool exit;
+ bool call;
+};
+
+struct SHELL_Cmd {
+ const char * name; /* Command name*/
+ Bit32u flags; /* Flags about the command */
+ void (DOS_Shell::*handler)(char * args); /* Handler for this command */
+ const char * help; /* String with command help */
+};
+
+/* Object to manage lines in the autoexec.bat The lines get removed from
+ * the file if the object gets destroyed. The environment is updated
+ * as well if the line set a a variable */
+class AutoexecObject{
+private:
+ bool installed;
+ std::string buf;
+public:
+ AutoexecObject():installed(false){ };
+ void Install(std::string const &in);
+ void InstallBefore(std::string const &in);
+ ~AutoexecObject();
+private:
+ void CreateAutoexec(void);
+};
+
+#endif
diff --git a/include/support.h b/include/support.h
new file mode 100644
index 000000000..bd21e6541
--- /dev/null
+++ b/include/support.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_SUPPORT_H
+#define DOSBOX_SUPPORT_H
+
+#include <string.h>
+#include <string>
+#include <ctype.h>
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+
+#if defined (_MSC_VER) /* MS Visual C++ */
+#define strcasecmp(a,b) stricmp(a,b)
+#define strncasecmp(a,b,n) _strnicmp(a,b,n)
+#endif
+
+#define safe_strncpy(a,b,n) do { strncpy((a),(b),(n)-1); (a)[(n)-1] = 0; } while (0)
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+void strreplace(char * str,char o,char n);
+char *ltrim(char *str);
+char *rtrim(char *str);
+char *trim(char * str);
+char * upcase(char * str);
+char * lowcase(char * str);
+
+bool ScanCMDBool(char * cmd,char const * const check);
+char * ScanCMDRemain(char * cmd);
+char * StripWord(char *&cmd);
+bool IsDecWord(char * word);
+bool IsHexWord(char * word);
+Bits ConvDecWord(char * word);
+Bits ConvHexWord(char * word);
+
+void trim(std::string& str);
+void upcase(std::string &str);
+void lowcase(std::string &str);
+
+#endif
diff --git a/include/timer.h b/include/timer.h
new file mode 100644
index 000000000..d723e0130
--- /dev/null
+++ b/include/timer.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_TIMER_H
+#define DOSBOX_TIMER_H
+
+/* underlying clock rate in HZ */
+#include <SDL.h>
+
+#define PIT_TICK_RATE 1193182
+
+#define GetTicks() SDL_GetTicks()
+
+typedef void (*TIMER_TickHandler)(void);
+
+/* Register a function that gets called everytime if 1 or more ticks pass */
+void TIMER_AddTickHandler(TIMER_TickHandler handler);
+void TIMER_DelTickHandler(TIMER_TickHandler handler);
+
+/* This will add 1 milliscond to all timers */
+void TIMER_AddTick(void);
+
+#endif
diff --git a/include/vga.h b/include/vga.h
new file mode 100644
index 000000000..e204105bb
--- /dev/null
+++ b/include/vga.h
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_VGA_H
+#define DOSBOX_VGA_H
+
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+
+//Don't enable keeping changes and mapping lfb probably...
+#define VGA_LFB_MAPPED
+//#define VGA_KEEP_CHANGES
+#define VGA_CHANGE_SHIFT 9
+
+class PageHandler;
+
+
+enum VGAModes {
+ M_CGA2, M_CGA4,
+ M_EGA, M_VGA,
+ M_LIN4, M_LIN8, M_LIN15, M_LIN16, M_LIN32,
+ M_TEXT,
+ M_HERC_GFX, M_HERC_TEXT,
+ M_CGA16, M_TANDY2, M_TANDY4, M_TANDY16, M_TANDY_TEXT,
+ M_ERROR
+};
+
+
+#define CLK_25 25175
+#define CLK_28 28322
+
+#define MIN_VCO 180000
+#define MAX_VCO 360000
+
+#define S3_CLOCK_REF 14318 /* KHz */
+#define S3_CLOCK(_M,_N,_R) ((S3_CLOCK_REF * ((_M) + 2)) / (((_N) + 2) * (1 << (_R))))
+#define S3_MAX_CLOCK 150000 /* KHz */
+
+#define S3_XGA_1024 0x00
+#define S3_XGA_1152 0x01
+#define S3_XGA_640 0x40
+#define S3_XGA_800 0x80
+#define S3_XGA_1280 0xc0
+#define S3_XGA_WMASK (S3_XGA_640|S3_XGA_800|S3_XGA_1024|S3_XGA_1152|S3_XGA_1280)
+
+#define S3_XGA_8BPP 0x00
+#define S3_XGA_16BPP 0x10
+#define S3_XGA_32BPP 0x30
+#define S3_XGA_CMASK (S3_XGA_8BPP|S3_XGA_16BPP|S3_XGA_32BPP)
+
+typedef struct {
+ bool attrindex;
+} VGA_Internal;
+
+typedef struct {
+/* Memory handlers */
+ Bitu mh_mask;
+
+/* Video drawing */
+ Bitu display_start;
+ Bitu real_start;
+ bool retrace; /* A retrace is active */
+ Bitu scan_len;
+ Bitu cursor_start;
+
+/* Some other screen related variables */
+ Bitu line_compare;
+ bool chained; /* Enable or Disabled Chain 4 Mode */
+ bool compatible_chain4;
+
+ /* Pixel Scrolling */
+ Bit8u pel_panning; /* Amount of pixels to skip when starting horizontal line */
+ Bit8u hlines_skip;
+ Bit8u bytes_skip;
+ Bit8u addr_shift;
+
+/* Specific stuff memory write/read handling */
+
+ Bit8u read_mode;
+ Bit8u write_mode;
+ Bit8u read_map_select;
+ Bit8u color_dont_care;
+ Bit8u color_compare;
+ Bit8u data_rotate;
+ Bit8u raster_op;
+
+ Bit32u full_bit_mask;
+ Bit32u full_map_mask;
+ Bit32u full_not_map_mask;
+ Bit32u full_set_reset;
+ Bit32u full_not_enable_set_reset;
+ Bit32u full_enable_set_reset;
+ Bit32u full_enable_and_set_reset;
+} VGA_Config;
+
+typedef enum {
+ PART,
+ DRAWLINE,
+ EGALINE
+} Drawmode;
+
+typedef struct {
+ bool resizing;
+ Bitu width;
+ Bitu height;
+ Bitu blocks;
+ Bitu address;
+ Bitu panning;
+ Bitu bytes_skip;
+ Bit8u *linear_base;
+ Bitu linear_mask;
+ Bitu address_add;
+ Bitu line_length;
+ Bitu address_line_total;
+ Bitu address_line;
+ Bitu lines_total;
+ Bitu vblank_skip;
+ Bitu lines_done;
+ Bitu lines_scaled;
+ Bitu split_line;
+ Bitu parts_total;
+ Bitu parts_lines;
+ Bitu parts_left;
+ Bitu byte_panning_shift;
+ struct {
+ double framestart;
+ double vrstart, vrend; // V-retrace
+ double hrstart, hrend; // H-retrace
+ double hblkstart, hblkend; // H-blanking
+ double vblkstart, vblkend; // V-Blanking
+ double vdend, vtotal;
+ double hdend, htotal;
+ double parts;
+ } delay;
+ Bitu bpp;
+ double aspect_ratio;
+ bool double_scan;
+ bool doublewidth,doubleheight;
+ Bit8u font[64*1024];
+ Bit8u * font_tables[2];
+ Bitu blinking;
+ bool blink;
+ bool char9dot;
+ struct {
+ Bitu address;
+ Bit8u sline,eline;
+ Bit8u count,delay;
+ Bit8u enabled;
+ } cursor;
+ Drawmode mode;
+ bool vret_triggered;
+ bool vga_override;
+} VGA_Draw;
+
+typedef struct {
+ Bit8u curmode;
+ Bit16u originx, originy;
+ Bit8u fstackpos, bstackpos;
+ Bit8u forestack[4];
+ Bit8u backstack[4];
+ Bit16u startaddr;
+ Bit8u posx, posy;
+ Bit8u mc[64][64];
+} VGA_HWCURSOR;
+
+typedef struct {
+ Bit8u reg_lock1;
+ Bit8u reg_lock2;
+ Bit8u reg_31;
+ Bit8u reg_35;
+ Bit8u reg_36; // RAM size
+ Bit8u reg_3a; // 4/8/doublepixel bit in there
+ Bit8u reg_40; // 8415/A functionality register
+ Bit8u reg_41; // BIOS flags
+ Bit8u reg_43;
+ Bit8u reg_45; // Hardware graphics cursor
+ Bit8u reg_50;
+ Bit8u reg_51;
+ Bit8u reg_52;
+ Bit8u reg_55;
+ Bit8u reg_58;
+ Bit8u reg_6b; // LFB BIOS scratchpad
+ Bit8u ex_hor_overflow;
+ Bit8u ex_ver_overflow;
+ Bit16u la_window;
+ Bit8u misc_control_2;
+ Bit8u ext_mem_ctrl;
+ Bitu xga_screen_width;
+ VGAModes xga_color_mode;
+ struct {
+ Bit8u r;
+ Bit8u n;
+ Bit8u m;
+ } clk[4],mclk;
+ struct {
+ Bit8u lock;
+ Bit8u cmd;
+ } pll;
+ VGA_HWCURSOR hgc;
+} VGA_S3;
+
+typedef struct {
+ Bit8u mode_control;
+ Bit8u enable_bits;
+} VGA_HERC;
+
+typedef struct {
+ Bit8u index;
+ Bit8u htotal;
+ Bit8u hdend;
+ Bit8u hsyncp;
+ Bit8u hsyncw;
+ Bit8u vtotal;
+ Bit8u vdend;
+ Bit8u vadjust;
+ Bit8u vsyncp;
+ Bit8u vsyncw;
+ Bit8u max_scanline;
+ Bit16u lightpen;
+ bool lightpen_triggered;
+ Bit8u cursor_start;
+ Bit8u cursor_end;
+} VGA_OTHER;
+
+typedef struct {
+ Bit8u pcjr_flipflop;
+ Bit8u mode_control;
+ Bit8u color_select;
+ Bit8u disp_bank;
+ Bit8u reg_index;
+ Bit8u gfx_control;
+ Bit8u palette_mask;
+ Bit8u extended_ram;
+ Bit8u border_color;
+ Bit8u line_mask, line_shift;
+ Bit8u draw_bank, mem_bank;
+ Bit8u *draw_base, *mem_base;
+ Bitu addr_mask;
+} VGA_TANDY;
+
+typedef struct {
+ Bit8u index;
+ Bit8u reset;
+ Bit8u clocking_mode;
+ Bit8u map_mask;
+ Bit8u character_map_select;
+ Bit8u memory_mode;
+} VGA_Seq;
+
+typedef struct {
+ Bit8u palette[16];
+ Bit8u mode_control;
+ Bit8u horizontal_pel_panning;
+ Bit8u overscan_color;
+ Bit8u color_plane_enable;
+ Bit8u color_select;
+ Bit8u index;
+ Bit8u disabled; // Used for disabling the screen.
+ // Bit0: screen disabled by attribute controller index
+ // Bit1: screen disabled by sequencer index 1 bit 5
+ // These are put together in one variable for performance reasons:
+ // the line drawing function is called maybe 60*480=28800 times/s,
+ // and we only need to check one variable for zero this way.
+} VGA_Attr;
+
+typedef struct {
+ Bit8u horizontal_total;
+ Bit8u horizontal_display_end;
+ Bit8u start_horizontal_blanking;
+ Bit8u end_horizontal_blanking;
+ Bit8u start_horizontal_retrace;
+ Bit8u end_horizontal_retrace;
+ Bit8u vertical_total;
+ Bit8u overflow;
+ Bit8u preset_row_scan;
+ Bit8u maximum_scan_line;
+ Bit8u cursor_start;
+ Bit8u cursor_end;
+ Bit8u start_address_high;
+ Bit8u start_address_low;
+ Bit8u cursor_location_high;
+ Bit8u cursor_location_low;
+ Bit8u vertical_retrace_start;
+ Bit8u vertical_retrace_end;
+ Bit8u vertical_display_end;
+ Bit8u offset;
+ Bit8u underline_location;
+ Bit8u start_vertical_blanking;
+ Bit8u end_vertical_blanking;
+ Bit8u mode_control;
+ Bit8u line_compare;
+
+ Bit8u index;
+ bool read_only;
+} VGA_Crtc;
+
+typedef struct {
+ Bit8u index;
+ Bit8u set_reset;
+ Bit8u enable_set_reset;
+ Bit8u color_compare;
+ Bit8u data_rotate;
+ Bit8u read_map_select;
+ Bit8u mode;
+ Bit8u miscellaneous;
+ Bit8u color_dont_care;
+ Bit8u bit_mask;
+} VGA_Gfx;
+
+typedef struct {
+ Bit8u red;
+ Bit8u green;
+ Bit8u blue;
+} RGBEntry;
+
+typedef struct {
+ Bit8u bits; /* DAC bits, usually 6 or 8 */
+ Bit8u pel_mask;
+ Bit8u pel_index;
+ Bit8u state;
+ Bit8u write_index;
+ Bit8u read_index;
+ Bitu first_changed;
+ Bit8u combine[16];
+ RGBEntry rgb[0x100];
+ Bit16u xlat16[256];
+} VGA_Dac;
+
+typedef struct {
+ Bitu readStart, writeStart;
+ Bitu bankMask;
+ Bitu bank_read_full;
+ Bitu bank_write_full;
+ Bit8u bank_read;
+ Bit8u bank_write;
+ Bitu bank_size;
+} VGA_SVGA;
+
+typedef union {
+ Bit32u d;
+ Bit8u b[4];
+} VGA_Latch;
+
+typedef struct {
+ Bit8u* linear;
+ Bit8u* linear_orgptr;
+} VGA_Memory;
+
+typedef struct {
+ //Add a few more just to be safe
+ Bit8u* map; /* allocated dynamically: [(VGA_MEMORY >> VGA_CHANGE_SHIFT) + 32] */
+ Bit8u checkMask, frame, writeMask;
+ bool active;
+ Bit32u clearMask;
+ Bit32u start, last;
+ Bit32u lastAddress;
+} VGA_Changes;
+
+typedef struct {
+ Bit32u page;
+ Bit32u addr;
+ Bit32u mask;
+ PageHandler *handler;
+} VGA_LFB;
+
+typedef struct {
+ VGAModes mode; /* The mode the vga system is in */
+ Bit8u misc_output;
+ VGA_Draw draw;
+ VGA_Config config;
+ VGA_Internal internal;
+/* Internal module groups */
+ VGA_Seq seq;
+ VGA_Attr attr;
+ VGA_Crtc crtc;
+ VGA_Gfx gfx;
+ VGA_Dac dac;
+ VGA_Latch latch;
+ VGA_S3 s3;
+ VGA_SVGA svga;
+ VGA_HERC herc;
+ VGA_TANDY tandy;
+ VGA_OTHER other;
+ VGA_Memory mem;
+ Bit32u vmemwrap; /* this is assumed to be power of 2 */
+ Bit8u* fastmem; /* memory for fast (usually 16-color) rendering, always twice as big as vmemsize */
+ Bit8u* fastmem_orgptr;
+ Bit32u vmemsize;
+#ifdef VGA_KEEP_CHANGES
+ VGA_Changes changes;
+#endif
+ VGA_LFB lfb;
+} VGA_Type;
+
+
+/* Hercules Palette function */
+void Herc_Palette(void);
+
+/* Functions for different resolutions */
+void VGA_SetMode(VGAModes mode);
+void VGA_DetermineMode(void);
+void VGA_SetupHandlers(void);
+void VGA_StartResize(Bitu delay=50);
+void VGA_SetupDrawing(Bitu val);
+void VGA_CheckScanLength(void);
+void VGA_ChangedBank(void);
+
+/* Some DAC/Attribute functions */
+void VGA_DAC_CombineColor(Bit8u attr,Bit8u pal);
+void VGA_DAC_SetEntry(Bitu entry,Bit8u red,Bit8u green,Bit8u blue);
+void VGA_ATTR_SetPalette(Bit8u index,Bit8u val);
+
+typedef enum {CGA, EGA, MONO} EGAMonitorMode;
+
+void VGA_ATTR_SetEGAMonitorPalette(EGAMonitorMode m);
+
+/* The VGA Subfunction startups */
+void VGA_SetupAttr(void);
+void VGA_SetupMemory(Section* sec);
+void VGA_SetupDAC(void);
+void VGA_SetupCRTC(void);
+void VGA_SetupMisc(void);
+void VGA_SetupGFX(void);
+void VGA_SetupSEQ(void);
+void VGA_SetupOther(void);
+void VGA_SetupXGA(void);
+
+/* Some Support Functions */
+void VGA_SetClock(Bitu which,Bitu target);
+void VGA_DACSetEntirePalette(void);
+void VGA_StartRetrace(void);
+void VGA_StartUpdateLFB(void);
+void VGA_SetBlinking(Bitu enabled);
+void VGA_SetCGA2Table(Bit8u val0,Bit8u val1);
+void VGA_SetCGA4Table(Bit8u val0,Bit8u val1,Bit8u val2,Bit8u val3);
+void VGA_ActivateHardwareCursor(void);
+void VGA_KillDrawing(void);
+
+void VGA_SetOverride(bool vga_override);
+
+extern VGA_Type vga;
+
+/* Support for modular SVGA implementation */
+/* Video mode extra data to be passed to FinishSetMode_SVGA().
+ This structure will be in flux until all drivers (including S3)
+ are properly separated. Right now it contains only three overflow
+ fields in S3 format and relies on drivers re-interpreting those.
+ For reference:
+ ver_overflow:X|line_comp10|X|vretrace10|X|vbstart10|vdispend10|vtotal10
+ hor_overflow:X|X|X|hretrace8|X|hblank8|hdispend8|htotal8
+ offset is not currently used by drivers (useful only for S3 itself)
+ It also contains basic int10 mode data - number, vtotal, htotal
+ */
+typedef struct {
+ Bit8u ver_overflow;
+ Bit8u hor_overflow;
+ Bitu offset;
+ Bitu modeNo;
+ Bitu htotal;
+ Bitu vtotal;
+} VGA_ModeExtraData;
+
+// Vector function prototypes
+typedef void (*tWritePort)(Bitu reg,Bitu val,Bitu iolen);
+typedef Bitu (*tReadPort)(Bitu reg,Bitu iolen);
+typedef void (*tFinishSetMode)(Bitu crtc_base, VGA_ModeExtraData* modeData);
+typedef void (*tDetermineMode)();
+typedef void (*tSetClock)(Bitu which,Bitu target);
+typedef Bitu (*tGetClock)();
+typedef bool (*tHWCursorActive)();
+typedef bool (*tAcceptsMode)(Bitu modeNo);
+
+struct SVGA_Driver {
+ tWritePort write_p3d5;
+ tReadPort read_p3d5;
+ tWritePort write_p3c5;
+ tReadPort read_p3c5;
+ tWritePort write_p3c0;
+ tReadPort read_p3c1;
+ tWritePort write_p3cf;
+ tReadPort read_p3cf;
+
+ tFinishSetMode set_video_mode;
+ tDetermineMode determine_mode;
+ tSetClock set_clock;
+ tGetClock get_clock;
+ tHWCursorActive hardware_cursor_active;
+ tAcceptsMode accepts_mode;
+};
+
+extern SVGA_Driver svga;
+
+void SVGA_Setup_S3Trio(void);
+void SVGA_Setup_TsengET4K(void);
+void SVGA_Setup_TsengET3K(void);
+void SVGA_Setup_ParadisePVGA1A(void);
+void SVGA_Setup_Driver(void);
+
+// Amount of video memory required for a mode, implemented in int10_modes.cpp
+Bitu VideoModeMemSize(Bitu mode);
+
+extern Bit32u ExpandTable[256];
+extern Bit32u FillTable[16];
+extern Bit32u CGA_2_Table[16];
+extern Bit32u CGA_4_Table[256];
+extern Bit32u CGA_4_HiRes_Table[256];
+extern Bit32u CGA_16_Table[256];
+extern Bit32u TXT_Font_Table[16];
+extern Bit32u TXT_FG_Table[16];
+extern Bit32u TXT_BG_Table[16];
+extern Bit32u Expand16Table[4][16];
+extern Bit32u Expand16BigTable[0x10000];
+
+
+#endif
diff --git a/include/video.h b/include/video.h
new file mode 100644
index 000000000..3696ced70
--- /dev/null
+++ b/include/video.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_VIDEO_H
+#define DOSBOX_VIDEO_H
+
+#define REDUCE_JOYSTICK_POLLING
+
+typedef enum {
+ GFX_CallBackReset,
+ GFX_CallBackStop,
+ GFX_CallBackRedraw
+} GFX_CallBackFunctions_t;
+
+typedef void (*GFX_CallBack_t)( GFX_CallBackFunctions_t function );
+
+struct GFX_PalEntry {
+ Bit8u r;
+ Bit8u g;
+ Bit8u b;
+ Bit8u unused;
+};
+
+#define GFX_CAN_8 0x0001
+#define GFX_CAN_15 0x0002
+#define GFX_CAN_16 0x0004
+#define GFX_CAN_32 0x0008
+
+#define GFX_LOVE_8 0x0010
+#define GFX_LOVE_15 0x0020
+#define GFX_LOVE_16 0x0040
+#define GFX_LOVE_32 0x0080
+
+#define GFX_RGBONLY 0x0100
+
+#define GFX_SCALING 0x1000
+#define GFX_HARDWARE 0x2000
+
+#define GFX_CAN_RANDOM 0x4000 //If the interface can also do random access surface
+
+void GFX_Events(void);
+void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries);
+Bitu GFX_GetBestMode(Bitu flags);
+Bitu GFX_GetRGB(Bit8u red,Bit8u green,Bit8u blue);
+Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,GFX_CallBack_t cb);
+
+void GFX_ResetScreen(void);
+void GFX_Start(void);
+void GFX_Stop(void);
+void GFX_SwitchFullScreen(void);
+bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch);
+void GFX_EndUpdate( const Bit16u *changedLines );
+void GFX_GetSize(int &width, int &height, bool &fullscreen);
+void GFX_LosingFocus(void);
+
+#if defined (WIN32)
+bool GFX_SDLUsingWinDIB(void);
+#endif
+
+#if defined (REDUCE_JOYSTICK_POLLING)
+void MAPPER_UpdateJoysticks(void);
+#endif
+
+/* Mouse related */
+void GFX_CaptureMouse(void);
+extern bool mouselocked; //true if mouse is confined to window
+
+#endif
diff --git a/scripts/captures.bat b/scripts/captures.bat
new file mode 100644
index 000000000..8eebbc8c3
--- /dev/null
+++ b/scripts/captures.bat
@@ -0,0 +1 @@
+DOSBox.exe -opencaptures explorer.exe \ No newline at end of file
diff --git a/scripts/dosbox-installer.nsi b/scripts/dosbox-installer.nsi
new file mode 100644
index 000000000..debbb3695
--- /dev/null
+++ b/scripts/dosbox-installer.nsi
@@ -0,0 +1,184 @@
+!define VER_MAYOR 0
+!define VER_MINOR 74
+!define APP_NAME "DOSBox ${VER_MAYOR}.${VER_MINOR} Installer"
+!define COMP_NAME "DOSBox Team"
+!define COPYRIGHT "Copyright © 2002-2019 DOSBox Team"
+!define DESCRIPTION "DOSBox Installer"
+
+VIProductVersion "${VER_MAYOR}.${VER_MINOR}.0.0"
+VIAddVersionKey "ProductName" "${APP_NAME}"
+VIAddVersionKey "CompanyName" "${COMP_NAME}"
+VIAddVersionKey "FileDescription" "${DESCRIPTION}"
+VIAddVersionKey "FileVersion" "${VER_MAYOR}.${VER_MINOR}.0.0"
+VIAddVersionKey "ProductVersion" "${VER_MAYOR}, ${VER_MINOR}, 0, 0"
+VIAddVersionKey "LegalCopyright" "${COPYRIGHT}"
+
+; The name of the installer
+Name "${APP_NAME}"
+
+; The file to write
+OutFile "DOSBox${VER_MAYOR}.${VER_MINOR}-win32-installer.exe"
+
+; The default installation directory
+InstallDir "$PROGRAMFILES\DOSBox-${VER_MAYOR}.${VER_MINOR}"
+
+; The text to prompt the user to enter a directory
+DirText "This will install DOSBox v${VER_MAYOR}.${VER_MINOR} on your computer. Choose a directory"
+SetCompressor /solid lzma
+
+
+LicenseData COPYING
+LicenseText "DOSBox v${VER_MAYOR}.${VER_MINOR} License" "Next >"
+
+; Else vista enables compatibility mode
+RequestExecutionLevel admin
+; Shortcuts in all users
+
+ComponentText "Select components for DOSBox"
+; The stuff to install
+Section "!Core files" Core
+SetShellVarContext all
+
+ ; Set output path to the installation directory.
+ ClearErrors
+ SetOutPath $INSTDIR
+ IfErrors error_createdir
+ SectionIn RO
+
+ ; Put file there
+
+ CreateDirectory "$INSTDIR\Video Codec"
+ CreateDirectory "$INSTDIR\Documentation"
+ SetOutPath "$INSTDIR\Documentation"
+ File /oname=README.txt README
+ File /oname=COPYING.txt COPYING
+ File /oname=THANKS.txt THANKS
+ File /oname=NEWS.txt NEWS
+ File /oname=AUTHORS.txt AUTHORS
+ File /oname=INSTALL.txt INSTALL
+ SetOutPath "$INSTDIR"
+
+ File "/oname=DOSBox ${VER_MAYOR}.${VER_MINOR} Manual.txt" README
+ File "/oname=DOSBox.exe" DOSBox.exe
+ File SDL.dll
+ File SDL_net.dll
+ File "/oname=Video Codec\zmbv.dll" zmbv.dll
+ File "/oname=Video Codec\zmbv.inf" zmbv.inf
+ File "/oname=Video Codec\Video Instructions.txt" README.video
+ File "/oname=DOSBox ${VER_MAYOR}.${VER_MINOR} Options.bat" editconf.bat
+ File "/oname=Reset KeyMapper.bat" resetmapper.bat
+ File "/oname=Reset Options.bat" resetconf.bat
+ File "/oname=Screenshots & Recordings.bat" captures.bat
+
+ CreateDirectory "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}"
+ CreateDirectory "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras"
+ CreateDirectory "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\Video"
+ CreateDirectory "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Options"
+ CreateShortCut "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\DOSBox ${VER_MAYOR}.${VER_MINOR}.lnk" "$INSTDIR\DOSBox.exe" "-userconf" "$INSTDIR\DOSBox.exe" 0
+ CreateShortCut "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\DOSBox ${VER_MAYOR}.${VER_MINOR} Manual.lnk" "$INSTDIR\Documentation\README.txt"
+ CreateShortCut "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\DOSBox ${VER_MAYOR}.${VER_MINOR} (noconsole).lnk" "$INSTDIR\DOSBox.exe" "-noconsole -userconf" "$INSTDIR\DOSBox.exe" 0
+ CreateShortCut "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
+ CreateShortCut "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\Screenshots & Recordings.lnk" "$INSTDIR\DOSBox.exe" "-opencaptures explorer.exe"
+
+ CreateShortCut "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Options\DOSBox ${VER_MAYOR}.${VER_MINOR} Options.lnk" "$INSTDIR\DOSBox.exe" "-editconf notepad.exe -editconf $\"%SystemRoot%\system32\notepad.exe$\" -editconf $\"%WINDIR%\notepad.exe$\""
+ CreateShortCut "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Options\Reset Options.lnk" "$INSTDIR\DOSBox.exe" "-eraseconf"
+ CreateShortCut "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Options\Reset KeyMapper.lnk" "$INSTDIR\DOSBox.exe" "-erasemapper"
+
+ CreateShortCut "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\Video\Video instructions.lnk" "$INSTDIR\Video Codec\Video Instructions.txt"
+;change outpath so the working directory gets set to zmbv
+SetOutPath "$INSTDIR\Video Codec"
+ ; Shortcut creation depends on wether we are 9x of NT
+ ClearErrors
+ ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
+ IfErrors we_9x we_nt
+we_nt:
+ ;shortcut for win NT
+ CreateShortCut "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\Video\Install movie codec.lnk" "rundll32" "setupapi,InstallHinfSection DefaultInstall 128 $INSTDIR\Video Codec\zmbv.inf"
+ goto end
+we_9x:
+ ;shortcut for we_9x
+ CreateShortCut "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\Video\Install movie codec.lnk" "rundll" "setupx.dll,InstallHinfSection DefaultInstall 128 $INSTDIR\Video Codec\zmbv.inf"
+end:
+SetOutPath $INSTDIR
+WriteUninstaller "uninstall.exe"
+
+ goto end_section
+
+error_createdir:
+ MessageBox MB_OK "Can't create DOSBox program directory, aborting."
+ Abort
+ goto end_section
+
+end_section:
+SectionEnd ; end the section
+
+Section "Desktop Shortcut" SecDesktop
+SetShellVarContext all
+
+CreateShortCut "$DESKTOP\DOSBox ${VER_MAYOR}.${VER_MINOR}.lnk" "$INSTDIR\DOSBox.exe" "-userconf" "$INSTDIR\DOSBox.exe" 0
+
+SectionEnd ; end the section
+
+
+UninstallText "This will uninstall DOSBox v${VER_MAYOR}.${VER_MINOR}. Hit next to continue."
+
+Section "Uninstall"
+
+; Shortcuts in all users
+SetShellVarContext all
+
+ Delete "$DESKTOP\DOSBox ${VER_MAYOR}.${VER_MINOR}.lnk"
+ ; remove registry keys
+ ; remove files
+ Delete $INSTDIR\Documentation\README.txt
+ Delete $INSTDIR\Documentation\COPYING.txt
+ Delete $INSTDIR\Documentation\THANKS.txt
+ Delete $INSTDIR\Documentation\NEWS.txt
+ Delete $INSTDIR\Documentation\AUTHORS.txt
+ Delete $INSTDIR\Documentation\INSTALL.txt
+ Delete "$INSTDIR\DOSBox ${VER_MAYOR}.${VER_MINOR} Manual.txt"
+ Delete "$INSTDIR\DOSBox.exe"
+ Delete $INSTDIR\SDL.dll
+ Delete $INSTDIR\SDL_net.dll
+ Delete "$INSTDIR\Video Codec\zmbv.dll"
+ Delete "$INSTDIR\Video Codec\zmbv.inf"
+ Delete "$INSTDIR\Video Codec\Video Instructions.txt"
+ ;Files left by sdl taking over the console
+ Delete $INSTDIR\stdout.txt
+ Delete $INSTDIR\stderr.txt
+ Delete "$INSTDIR\DOSBox ${VER_MAYOR}.${VER_MINOR} Options.bat"
+ Delete "$INSTDIR\Reset KeyMapper.bat"
+ Delete "$INSTDIR\Reset Options.bat"
+ Delete "$INSTDIR\Screenshots & Recordings.bat"
+
+ ; MUST REMOVE UNINSTALLER, too
+ Delete $INSTDIR\uninstall.exe
+
+
+ Delete "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\DOSBox ${VER_MAYOR}.${VER_MINOR}.lnk"
+ Delete "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\DOSBox ${VER_MAYOR}.${VER_MINOR} Manual.lnk"
+ Delete "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\DOSBox ${VER_MAYOR}.${VER_MINOR} (noconsole).lnk"
+ Delete "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\Uninstall.lnk"
+ Delete "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\Screenshots & Recordings.lnk"
+
+ Delete "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Options\DOSBox ${VER_MAYOR}.${VER_MINOR} Options.lnk"
+ Delete "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Options\Reset Options.lnk"
+ Delete "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Options\Reset KeyMapper.lnk"
+
+ Delete "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\Video\Video instructions.lnk"
+ Delete "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\Video\Install movie codec.lnk"
+
+
+
+; remove shortcuts, if any.
+; remove directories used.
+ RMDir "$INSTDIR\Documentation"
+ RMDir "$INSTDIR\Video Codec"
+ RMDir "$INSTDIR"
+ RMDir "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Options"
+ RMDir "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras\Video"
+ RMDir "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}\Extras"
+ RMDir "$SMPROGRAMS\DOSBox-${VER_MAYOR}.${VER_MINOR}"
+SectionEnd
+
+; eof
diff --git a/scripts/editconf.bat b/scripts/editconf.bat
new file mode 100644
index 000000000..a48d102fb
--- /dev/null
+++ b/scripts/editconf.bat
@@ -0,0 +1 @@
+DOSBox.exe -editconf notepad.exe -editconf %SystemRoot%\system32\notepad.exe -editconf %WINDIR%\notepad.exe \ No newline at end of file
diff --git a/scripts/ega-switch.pl b/scripts/ega-switch.pl
new file mode 100644
index 000000000..6795b458c
--- /dev/null
+++ b/scripts/ega-switch.pl
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+use integer;
+open (THEFILE,'>','../src/hardware/ega-switch.h')
+ or die "Can't open my file $!";
+
+print THEFILE "switch (bit_mask) {\n";
+for ($i = 0; $i < 256; $i++) {
+ print THEFILE "\tcase $i:\n";
+ $b=128;
+ $add=0;
+ do {
+ if ($i & $b) {
+ print THEFILE "\t{\n";
+ print THEFILE "\t\tBit8u color=0;\n";
+ print THEFILE "\t\tif (pixels.b[0] & $b) color|=1;\n";
+ print THEFILE "\t\tif (pixels.b[1] & $b) color|=2;\n";
+ print THEFILE "\t\tif (pixels.b[2] & $b) color|=4;\n";
+ print THEFILE "\t\tif (pixels.b[3] & $b) color|=8;\n";
+ print THEFILE "\t\t*(write_pixels+$add)=color;\n";
+ print THEFILE "\t\t*(write_pixels+$add+512*1024)=color;\n";
+ print THEFILE "\t}\n";
+ }
+
+ $b=$b >> 1;
+ $add=$add+1;
+ } until ($b == 0);
+ print THEFILE "\tbreak;\n";
+}
+print THEFILE "}\n";
+
+
+close (THEFILE);
diff --git a/scripts/font-switch.pl b/scripts/font-switch.pl
new file mode 100644
index 000000000..727478010
--- /dev/null
+++ b/scripts/font-switch.pl
@@ -0,0 +1,25 @@
+#!/usr/bin/perl
+use integer;
+open (THEFILE,'>','../src/hardware/font-switch.h')
+ or die "Can't open my file $!";
+
+print THEFILE "switch (bit_mask) {\n";
+for ($i = 0; $i < 256; $i++) {
+ print THEFILE "\tcase $i:\n";
+ $b=128;
+ $add=0;
+ do {
+ if ($i & $b) {
+ print THEFILE "\t\t*(draw+$add)=fg;\n";
+ } else {
+ print THEFILE "\t\t*(draw+$add)=bg;\n";
+ }
+ $b=$b >> 1;
+ $add=$add+1;
+ } until ($b == 0);
+ print THEFILE "\tbreak;\n";
+}
+print THEFILE "}\n";
+
+
+close (THEFILE);
diff --git a/scripts/resetconf.bat b/scripts/resetconf.bat
new file mode 100644
index 000000000..0ad3b95b5
--- /dev/null
+++ b/scripts/resetconf.bat
@@ -0,0 +1 @@
+DOSBox.exe -resetconf \ No newline at end of file
diff --git a/scripts/resetmapper.bat b/scripts/resetmapper.bat
new file mode 100644
index 000000000..86299bbc7
--- /dev/null
+++ b/scripts/resetmapper.bat
@@ -0,0 +1 @@
+DOSBox.exe -resetmapper \ No newline at end of file
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 000000000..a4029e835
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,21 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+SUBDIRS = cpu debug dos fpu gui hardware libs ints misc shell platform
+
+bin_PROGRAMS = dosbox
+
+if HAVE_WINDRES
+ico_stuff = winres.rc
+endif
+
+.rc.o:
+ $(WINDRES) -o $@ $<
+
+dosbox_SOURCES = dosbox.cpp $(ico_stuff)
+dosbox_LDADD = cpu/libcpu.a debug/libdebug.a dos/libdos.a fpu/libfpu.a hardware/libhardware.a gui/libgui.a \
+ ints/libints.a misc/libmisc.a shell/libshell.a hardware/mame/libmame.a \
+ hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a
+
+EXTRA_DIST = winres.rc dosbox.ico
+
+
diff --git a/src/cpu/Makefile.am b/src/cpu/Makefile.am
new file mode 100644
index 000000000..5c63a9a46
--- /dev/null
+++ b/src/cpu/Makefile.am
@@ -0,0 +1,7 @@
+SUBDIRS = core_full core_normal core_dyn_x86 core_dynrec
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libcpu.a
+libcpu_a_SOURCES = callback.cpp cpu.cpp flags.cpp modrm.cpp modrm.h core_full.cpp instructions.h \
+ paging.cpp lazyflags.h core_normal.cpp core_simple.cpp core_prefetch.cpp \
+ core_dyn_x86.cpp core_dynrec.cpp
diff --git a/src/cpu/callback.cpp b/src/cpu/callback.cpp
new file mode 100644
index 000000000..136888a6c
--- /dev/null
+++ b/src/cpu/callback.cpp
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dosbox.h"
+#include "callback.h"
+#include "mem.h"
+#include "cpu.h"
+
+/* CallBack are located at 0xF000:0x1000 (see CB_SEG and CB_SOFFSET in callback.h)
+ And they are 16 bytes each and you can define them to behave in certain ways like a
+ far return or and IRET
+*/
+
+CallBack_Handler CallBack_Handlers[CB_MAX];
+char* CallBack_Description[CB_MAX];
+
+static Bitu call_stop,call_idle,call_default;
+Bitu call_priv_io;
+
+static Bitu illegal_handler(void) {
+ E_Exit("Illegal CallBack Called");
+ return 1;
+}
+
+Bitu CALLBACK_Allocate(void) {
+ for (Bitu i=1;(i<CB_MAX);i++) {
+ if (CallBack_Handlers[i]==&illegal_handler) {
+ CallBack_Handlers[i]=0;
+ return i;
+ }
+ }
+ E_Exit("CALLBACK:Can't allocate handler.");
+ return 0;
+}
+
+void CALLBACK_DeAllocate(Bitu in) {
+ CallBack_Handlers[in]=&illegal_handler;
+}
+
+
+void CALLBACK_Idle(void) {
+/* this makes the cpu execute instructions to handle irq's and then come back */
+ Bitu oldIF=GETFLAG(IF);
+ SETFLAGBIT(IF,true);
+ Bit16u oldcs=SegValue(cs);
+ Bit32u oldeip=reg_eip;
+ SegSet16(cs,CB_SEG);
+ reg_eip=CB_SOFFSET+call_idle*CB_SIZE;
+ DOSBOX_RunMachine();
+ reg_eip=oldeip;
+ SegSet16(cs,oldcs);
+ SETFLAGBIT(IF,oldIF);
+ if (!CPU_CycleAutoAdjust && CPU_Cycles>0)
+ CPU_Cycles=0;
+}
+
+static Bitu default_handler(void) {
+ LOG(LOG_CPU,LOG_ERROR)("Illegal Unhandled Interrupt Called %X",lastint);
+ return CBRET_NONE;
+}
+
+static Bitu stop_handler(void) {
+ return CBRET_STOP;
+}
+
+
+
+void CALLBACK_RunRealFar(Bit16u seg,Bit16u off) {
+ reg_sp-=4;
+ mem_writew(SegPhys(ss)+reg_sp,RealOff(CALLBACK_RealPointer(call_stop)));
+ mem_writew(SegPhys(ss)+reg_sp+2,RealSeg(CALLBACK_RealPointer(call_stop)));
+ Bit32u oldeip=reg_eip;
+ Bit16u oldcs=SegValue(cs);
+ reg_eip=off;
+ SegSet16(cs,seg);
+ DOSBOX_RunMachine();
+ reg_eip=oldeip;
+ SegSet16(cs,oldcs);
+}
+
+void CALLBACK_RunRealInt(Bit8u intnum) {
+ Bit32u oldeip=reg_eip;
+ Bit16u oldcs=SegValue(cs);
+ reg_eip=CB_SOFFSET+(CB_MAX*CB_SIZE)+(intnum*6);
+ SegSet16(cs,CB_SEG);
+ DOSBOX_RunMachine();
+ reg_eip=oldeip;
+ SegSet16(cs,oldcs);
+}
+
+void CALLBACK_SZF(bool val) {
+ Bit16u tempf = mem_readw(SegPhys(ss)+reg_sp+4);
+ if (val) tempf |= FLAG_ZF;
+ else tempf &= ~FLAG_ZF;
+ mem_writew(SegPhys(ss)+reg_sp+4,tempf);
+}
+
+void CALLBACK_SCF(bool val) {
+ Bit16u tempf = mem_readw(SegPhys(ss)+reg_sp+4);
+ if (val) tempf |= FLAG_CF;
+ else tempf &= ~FLAG_CF;
+ mem_writew(SegPhys(ss)+reg_sp+4,tempf);
+}
+
+void CALLBACK_SIF(bool val) {
+ Bit16u tempf = mem_readw(SegPhys(ss)+reg_sp+4);
+ if (val) tempf |= FLAG_IF;
+ else tempf &= ~FLAG_IF;
+ mem_writew(SegPhys(ss)+reg_sp+4,tempf);
+}
+
+void CALLBACK_SetDescription(Bitu nr, const char* descr) {
+ if (descr) {
+ CallBack_Description[nr] = new char[strlen(descr)+1];
+ strcpy(CallBack_Description[nr],descr);
+ } else
+ CallBack_Description[nr] = 0;
+}
+
+const char* CALLBACK_GetDescription(Bitu nr) {
+ if (nr>=CB_MAX) return 0;
+ return CallBack_Description[nr];
+}
+
+Bitu CALLBACK_SetupExtra(Bitu callback, Bitu type, PhysPt physAddress, bool use_cb=true) {
+ if (callback>=CB_MAX)
+ return 0;
+ switch (type) {
+ case CB_RETN:
+ if (use_cb) {
+ phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x00,(Bit8u)0xC3); //A RETN Instruction
+ return (use_cb?5:1);
+ case CB_RETF:
+ if (use_cb) {
+ phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x00,(Bit8u)0xCB); //A RETF Instruction
+ return (use_cb?5:1);
+ case CB_RETF8:
+ if (use_cb) {
+ phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x00,(Bit8u)0xCA); //A RETF 8 Instruction
+ phys_writew(physAddress+0x01,(Bit16u)0x0008);
+ return (use_cb?7:3);
+ case CB_RETF_STI:
+ phys_writeb(physAddress+0x00,(Bit8u)0xFB); //STI
+ if (use_cb) {
+ phys_writeb(physAddress+0x01,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x02,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x03,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x01,(Bit8u)0xCB); //A RETF Instruction
+ return (use_cb?6:2);
+ case CB_RETF_CLI:
+ phys_writeb(physAddress+0x00,(Bit8u)0xFA); //CLI
+ if (use_cb) {
+ phys_writeb(physAddress+0x01,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x02,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x03,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x01,(Bit8u)0xCB); //A RETF Instruction
+ return (use_cb?6:2);
+ case CB_IRET:
+ if (use_cb) {
+ phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x00,(Bit8u)0xCF); //An IRET Instruction
+ return (use_cb?5:1);
+ case CB_IRETD:
+ if (use_cb) {
+ phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x00,(Bit8u)0x66); //An IRETD Instruction
+ phys_writeb(physAddress+0x01,(Bit8u)0xCF);
+ return (use_cb?6:2);
+ case CB_IRET_STI:
+ phys_writeb(physAddress+0x00,(Bit8u)0xFB); //STI
+ if (use_cb) {
+ phys_writeb(physAddress+0x01,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x02,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x03,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x01,(Bit8u)0xCF); //An IRET Instruction
+ return (use_cb?6:2);
+ case CB_IRET_EOI_PIC1:
+ if (use_cb) {
+ phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x00,(Bit8u)0x50); // push ax
+ phys_writeb(physAddress+0x01,(Bit8u)0xb0); // mov al, 0x20
+ phys_writeb(physAddress+0x02,(Bit8u)0x20);
+ phys_writeb(physAddress+0x03,(Bit8u)0xe6); // out 0x20, al
+ phys_writeb(physAddress+0x04,(Bit8u)0x20);
+ phys_writeb(physAddress+0x05,(Bit8u)0x58); // pop ax
+ phys_writeb(physAddress+0x06,(Bit8u)0xcf); //An IRET Instruction
+ return (use_cb?0x0b:0x07);
+ case CB_IRQ0: // timer int8
+ phys_writeb(physAddress+0x00,(Bit8u)0xFB); //STI
+ if (use_cb) {
+ phys_writeb(physAddress+0x01,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x02,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x03,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x01,(Bit8u)0x1e); // push ds
+ phys_writeb(physAddress+0x02,(Bit8u)0x50); // push ax
+ phys_writeb(physAddress+0x03,(Bit8u)0x52); // push dx
+ phys_writew(physAddress+0x04,(Bit16u)0x1ccd); // int 1c
+ phys_writeb(physAddress+0x06,(Bit8u)0xfa); // cli
+ phys_writew(physAddress+0x07,(Bit16u)0x20b0); // mov al, 0x20
+ phys_writew(physAddress+0x09,(Bit16u)0x20e6); // out 0x20, al
+ phys_writeb(physAddress+0x0b,(Bit8u)0x5a); // pop dx
+ phys_writeb(physAddress+0x0c,(Bit8u)0x58); // pop ax
+ phys_writeb(physAddress+0x0d,(Bit8u)0x1f); // pop ds
+ phys_writeb(physAddress+0x0e,(Bit8u)0xcf); //An IRET Instruction
+ return (use_cb?0x13:0x0f);
+ case CB_IRQ1: // keyboard int9
+ phys_writeb(physAddress+0x00,(Bit8u)0x50); // push ax
+ phys_writew(physAddress+0x01,(Bit16u)0x60e4); // in al, 0x60
+ phys_writew(physAddress+0x03,(Bit16u)0x4fb4); // mov ah, 0x4f
+ phys_writeb(physAddress+0x05,(Bit8u)0xf9); // stc
+ phys_writew(physAddress+0x06,(Bit16u)0x15cd); // int 15
+ if (use_cb) {
+ phys_writew(physAddress+0x08,(Bit16u)0x0473); // jc skip
+ phys_writeb(physAddress+0x0a,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x0b,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x0c,(Bit16u)callback); //The immediate word
+ // jump here to (skip):
+ physAddress+=6;
+ }
+ phys_writeb(physAddress+0x08,(Bit8u)0xfa); // cli
+ phys_writew(physAddress+0x09,(Bit16u)0x20b0); // mov al, 0x20
+ phys_writew(physAddress+0x0b,(Bit16u)0x20e6); // out 0x20, al
+ phys_writeb(physAddress+0x0d,(Bit8u)0x58); // pop ax
+ phys_writeb(physAddress+0x0e,(Bit8u)0xcf); //An IRET Instruction
+ phys_writeb(physAddress+0x0f,(Bit8u)0xfa); // cli
+ phys_writew(physAddress+0x10,(Bit16u)0x20b0); // mov al, 0x20
+ phys_writew(physAddress+0x12,(Bit16u)0x20e6); // out 0x20, al
+ phys_writeb(physAddress+0x14,(Bit8u)0x55); // push bp
+ phys_writew(physAddress+0x15,(Bit16u)0x05cd); // int 5
+ phys_writeb(physAddress+0x17,(Bit8u)0x5d); // pop bp
+ phys_writeb(physAddress+0x18,(Bit8u)0x58); // pop ax
+ phys_writeb(physAddress+0x19,(Bit8u)0xcf); //An IRET Instruction
+ return (use_cb?0x20:0x1a);
+ case CB_IRQ9: // pic cascade interrupt
+ if (use_cb) {
+ phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x00,(Bit8u)0x50); // push ax
+ phys_writew(physAddress+0x01,(Bit16u)0x61b0); // mov al, 0x61
+ phys_writew(physAddress+0x03,(Bit16u)0xa0e6); // out 0xa0, al
+ phys_writew(physAddress+0x05,(Bit16u)0x0acd); // int a
+ phys_writeb(physAddress+0x07,(Bit8u)0xfa); // cli
+ phys_writeb(physAddress+0x08,(Bit8u)0x58); // pop ax
+ phys_writeb(physAddress+0x09,(Bit8u)0xcf); //An IRET Instruction
+ return (use_cb?0x0e:0x0a);
+ case CB_IRQ12: // ps2 mouse int74
+ if (!use_cb) E_Exit("int74 callback must implement a callback handler!");
+ phys_writeb(physAddress+0x00,(Bit8u)0xfb); // sti
+ phys_writeb(physAddress+0x01,(Bit8u)0x1e); // push ds
+ phys_writeb(physAddress+0x02,(Bit8u)0x06); // push es
+ phys_writew(physAddress+0x03,(Bit16u)0x6066); // pushad
+ phys_writeb(physAddress+0x05,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x06,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x07,(Bit16u)callback); //The immediate word
+ phys_writeb(physAddress+0x09,(Bit8u)0x50); // push ax
+ phys_writew(physAddress+0x0a,(Bit16u)0x20b0); // mov al, 0x20
+ phys_writew(physAddress+0x0c,(Bit16u)0xa0e6); // out 0xa0, al
+ phys_writew(physAddress+0x0e,(Bit16u)0x20e6); // out 0x20, al
+ phys_writeb(physAddress+0x10,(Bit8u)0x58); // pop ax
+ phys_writeb(physAddress+0x11,(Bit8u)0xfc); // cld
+ phys_writeb(physAddress+0x12,(Bit8u)0xCB); //A RETF Instruction
+ return 0x13;
+ case CB_IRQ12_RET: // ps2 mouse int74 return
+ phys_writeb(physAddress+0x00,(Bit8u)0xfa); // cli
+ phys_writew(physAddress+0x01,(Bit16u)0x20b0); // mov al, 0x20
+ phys_writew(physAddress+0x03,(Bit16u)0xa0e6); // out 0xa0, al
+ phys_writew(physAddress+0x05,(Bit16u)0x20e6); // out 0x20, al
+ if (use_cb) {
+ phys_writeb(physAddress+0x07,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x08,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x09,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writew(physAddress+0x07,(Bit16u)0x6166); // popad
+ phys_writeb(physAddress+0x09,(Bit8u)0x07); // pop es
+ phys_writeb(physAddress+0x0a,(Bit8u)0x1f); // pop ds
+ phys_writeb(physAddress+0x0b,(Bit8u)0xcf); //An IRET Instruction
+ return (use_cb?0x10:0x0c);
+ case CB_IRQ6_PCJR: // pcjr keyboard interrupt
+ phys_writeb(physAddress+0x00,(Bit8u)0x50); // push ax
+ phys_writew(physAddress+0x01,(Bit16u)0x60e4); // in al, 0x60
+ phys_writew(physAddress+0x03,(Bit16u)0xe03c); // cmp al, 0xe0
+ if (use_cb) {
+ phys_writew(physAddress+0x05,(Bit16u)0x0b74); // je skip
+ phys_writeb(physAddress+0x07,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x08,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x09,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ } else {
+ phys_writew(physAddress+0x05,(Bit16u)0x0774); // je skip
+ }
+ phys_writeb(physAddress+0x07,(Bit8u)0x1e); // push ds
+ phys_writew(physAddress+0x08,(Bit16u)0x406a); // push 0x0040
+ phys_writeb(physAddress+0x0a,(Bit8u)0x1f); // pop ds
+ phys_writew(physAddress+0x0b,(Bit16u)0x09cd); // int 9
+ phys_writeb(physAddress+0x0d,(Bit8u)0x1f); // pop ds
+ // jump here to (skip):
+ phys_writeb(physAddress+0x0e,(Bit8u)0xfa); // cli
+ phys_writew(physAddress+0x0f,(Bit16u)0x20b0); // mov al, 0x20
+ phys_writew(physAddress+0x11,(Bit16u)0x20e6); // out 0x20, al
+ phys_writeb(physAddress+0x13,(Bit8u)0x58); // pop ax
+ phys_writeb(physAddress+0x14,(Bit8u)0xcf); //An IRET Instruction
+ return (use_cb?0x19:0x15);
+ case CB_MOUSE:
+ phys_writew(physAddress+0x00,(Bit16u)0x07eb); // jmp i33hd
+ physAddress+=9;
+ // jump here to (i33hd):
+ if (use_cb) {
+ phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x00,(Bit8u)0xCF); //An IRET Instruction
+ return (use_cb?0x0e:0x0a);
+ case CB_INT16:
+ phys_writeb(physAddress+0x00,(Bit8u)0xFB); //STI
+ if (use_cb) {
+ phys_writeb(physAddress+0x01,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x02,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x03,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x01,(Bit8u)0xCF); //An IRET Instruction
+ for (Bitu i=0;i<=0x0b;i++) phys_writeb(physAddress+0x02+i,0x90);
+ phys_writew(physAddress+0x0e,(Bit16u)0xedeb); //jmp callback
+ return (use_cb?0x10:0x0c);
+ case CB_INT29: // fast console output
+ if (use_cb) {
+ phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x00,(Bit8u)0x50); // push ax
+ phys_writeb(physAddress+0x01,(Bit8u)0x53); // push bx
+ phys_writew(physAddress+0x02,(Bit16u)0x0eb4); // mov ah, 0x0e
+ phys_writeb(physAddress+0x04,(Bit8u)0xbb); // mov bx,
+ phys_writew(physAddress+0x05,(Bit16u)0x0007); // 0x0007
+ phys_writew(physAddress+0x07,(Bit16u)0x10cd); // int 10
+ phys_writeb(physAddress+0x09,(Bit8u)0x5b); // pop bx
+ phys_writeb(physAddress+0x0a,(Bit8u)0x58); // pop ax
+ phys_writeb(physAddress+0x0b,(Bit8u)0xcf); //An IRET Instruction
+ return (use_cb?0x10:0x0c);
+ case CB_HOOKABLE:
+ phys_writeb(physAddress+0x00,(Bit8u)0xEB); //jump near
+ phys_writeb(physAddress+0x01,(Bit8u)0x03); //offset
+ phys_writeb(physAddress+0x02,(Bit8u)0x90); //NOP
+ phys_writeb(physAddress+0x03,(Bit8u)0x90); //NOP
+ phys_writeb(physAddress+0x04,(Bit8u)0x90); //NOP
+ if (use_cb) {
+ phys_writeb(physAddress+0x05,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x06,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x07,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x05,(Bit8u)0xCB); //A RETF Instruction
+ return (use_cb?0x0a:0x06);
+ case CB_TDE_IRET: // TandyDAC end transfer
+ if (use_cb) {
+ phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x00,(Bit8u)0x50); // push ax
+ phys_writeb(physAddress+0x01,(Bit8u)0xb8); // mov ax, 0x91fb
+ phys_writew(physAddress+0x02,(Bit16u)0x91fb);
+ phys_writew(physAddress+0x04,(Bit16u)0x15cd); // int 15
+ phys_writeb(physAddress+0x06,(Bit8u)0xfa); // cli
+ phys_writew(physAddress+0x07,(Bit16u)0x20b0); // mov al, 0x20
+ phys_writew(physAddress+0x09,(Bit16u)0x20e6); // out 0x20, al
+ phys_writeb(physAddress+0x0b,(Bit8u)0x58); // pop ax
+ phys_writeb(physAddress+0x0c,(Bit8u)0xcf); //An IRET Instruction
+ return (use_cb?0x11:0x0d);
+/* case CB_IPXESR: // IPX ESR
+ if (!use_cb) E_Exit("ipx esr must implement a callback handler!");
+ phys_writeb(physAddress+0x00,(Bit8u)0x1e); // push ds
+ phys_writeb(physAddress+0x01,(Bit8u)0x06); // push es
+ phys_writew(physAddress+0x02,(Bit16u)0xa00f); // push fs
+ phys_writew(physAddress+0x04,(Bit16u)0xa80f); // push gs
+ phys_writeb(physAddress+0x06,(Bit8u)0x60); // pusha
+ phys_writeb(physAddress+0x07,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x08,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x09,(Bit16u)callback); //The immediate word
+ phys_writeb(physAddress+0x0b,(Bit8u)0xCB); //A RETF Instruction
+ return 0x0c;
+ case CB_IPXESR_RET: // IPX ESR return
+ if (use_cb) E_Exit("ipx esr return must not implement a callback handler!");
+ phys_writeb(physAddress+0x00,(Bit8u)0xfa); // cli
+ phys_writew(physAddress+0x01,(Bit16u)0x20b0); // mov al, 0x20
+ phys_writew(physAddress+0x03,(Bit16u)0xa0e6); // out 0xa0, al
+ phys_writew(physAddress+0x05,(Bit16u)0x20e6); // out 0x20, al
+ phys_writeb(physAddress+0x07,(Bit8u)0x61); // popa
+ phys_writew(physAddress+0x08,(Bit16u)0xA90F); // pop gs
+ phys_writew(physAddress+0x0a,(Bit16u)0xA10F); // pop fs
+ phys_writeb(physAddress+0x0c,(Bit8u)0x07); // pop es
+ phys_writeb(physAddress+0x0d,(Bit8u)0x1f); // pop ds
+ phys_writeb(physAddress+0x0e,(Bit8u)0xcf); //An IRET Instruction
+ return 0x0f; */
+ case CB_INT21:
+ phys_writeb(physAddress+0x00,(Bit8u)0xFB); //STI
+ if (use_cb) {
+ phys_writeb(physAddress+0x01,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x02,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x03,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x01,(Bit8u)0xCF); //An IRET Instruction
+ phys_writeb(physAddress+0x02,(Bit8u)0xCB); //A RETF Instruction
+ phys_writeb(physAddress+0x03,(Bit8u)0x51); // push cx
+ phys_writeb(physAddress+0x04,(Bit8u)0xB9); // mov cx,
+ phys_writew(physAddress+0x05,(Bit16u)0x0140); // 0x140
+ phys_writew(physAddress+0x07,(Bit16u)0xFEE2); // loop $-2
+ phys_writeb(physAddress+0x09,(Bit8u)0x59); // pop cx
+ phys_writeb(physAddress+0x0A,(Bit8u)0xCF); //An IRET Instruction
+ return (use_cb?15:11);
+ case CB_INT13:
+ phys_writeb(physAddress+0x00,(Bit8u)0xFB); //STI
+ if (use_cb) {
+ phys_writeb(physAddress+0x01,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x02,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x03,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writeb(physAddress+0x01,(Bit8u)0xCF); //An IRET Instruction
+ phys_writew(physAddress+0x02,(Bit16u)0x0ECD); // int 0e
+ phys_writeb(physAddress+0x04,(Bit8u)0xCF); //An IRET Instruction
+ return (use_cb?9:5);
+ case CB_VESA_WAIT:
+ if (use_cb) E_Exit("VESA wait must not implement a callback handler!");
+ phys_writeb(physAddress+0x00,(Bit8u)0xFB); // sti
+ phys_writeb(physAddress+0x01,(Bit8u)0x50); // push ax
+ phys_writeb(physAddress+0x02,(Bit8u)0x52); // push dx
+ phys_writeb(physAddress+0x03,(Bit8u)0xBA); // mov dx,
+ phys_writew(physAddress+0x04,(Bit16u)0x03DA); // 0x3da
+ phys_writeb(physAddress+0x06,(Bit8u)0xEC); // in al,dx
+ phys_writew(physAddress+0x07,(Bit16u)0x08A8); // test al,8
+ phys_writew(physAddress+0x09,(Bit16u)0xFB75); // jne $-5
+ phys_writeb(physAddress+0x0B,(Bit8u)0xEC); // in al,dx
+ phys_writew(physAddress+0x0C,(Bit16u)0x08A8); // test al,8
+ phys_writew(physAddress+0x0E,(Bit16u)0xFB74); // je $-5
+ phys_writeb(physAddress+0x10,(Bit8u)0x5A); // pop dx
+ phys_writeb(physAddress+0x11,(Bit8u)0x58); // pop ax
+ phys_writeb(physAddress+0x12,(Bit8u)0xCB); //A RETF Instruction
+ return 19;
+ case CB_VESA_PM:
+ if (use_cb) {
+ phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4
+ phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction
+ phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word
+ physAddress+=4;
+ }
+ phys_writew(physAddress+0x00,(Bit16u)0xC3F6); // test bl,
+ phys_writeb(physAddress+0x02,(Bit8u)0x80); // 0x80
+ phys_writew(physAddress+0x03,(Bit16u)0x1674); // je $+22
+ phys_writew(physAddress+0x05,(Bit16u)0x5066); // push ax
+ phys_writew(physAddress+0x07,(Bit16u)0x5266); // push dx
+ phys_writew(physAddress+0x09,(Bit16u)0xBA66); // mov dx,
+ phys_writew(physAddress+0x0B,(Bit16u)0x03DA); // 0x3da
+ phys_writeb(physAddress+0x0D,(Bit8u)0xEC); // in al,dx
+ phys_writew(physAddress+0x0E,(Bit16u)0x08A8); // test al,8
+ phys_writew(physAddress+0x10,(Bit16u)0xFB75); // jne $-5
+ phys_writeb(physAddress+0x12,(Bit8u)0xEC); // in al,dx
+ phys_writew(physAddress+0x13,(Bit16u)0x08A8); // test al,8
+ phys_writew(physAddress+0x15,(Bit16u)0xFB74); // je $-5
+ phys_writew(physAddress+0x17,(Bit16u)0x5A66); // pop dx
+ phys_writew(physAddress+0x19,(Bit16u)0x5866); // pop ax
+ if (use_cb)
+ phys_writeb(physAddress+0x1B,(Bit8u)0xC3); //A RETN Instruction
+ return (use_cb?32:27);
+ default:
+ E_Exit("CALLBACK:Setup:Illegal type %d",type);
+ }
+ return 0;
+}
+
+bool CALLBACK_Setup(Bitu callback,CallBack_Handler handler,Bitu type,const char* descr) {
+ if (callback>=CB_MAX) return false;
+ CALLBACK_SetupExtra(callback,type,CALLBACK_PhysPointer(callback)+0,(handler!=NULL));
+ CallBack_Handlers[callback]=handler;
+ CALLBACK_SetDescription(callback,descr);
+ return true;
+}
+
+Bitu CALLBACK_Setup(Bitu callback,CallBack_Handler handler,Bitu type,PhysPt addr,const char* descr) {
+ if (callback>=CB_MAX) return 0;
+ Bitu csize=CALLBACK_SetupExtra(callback,type,addr,(handler!=NULL));
+ if (csize>0) {
+ CallBack_Handlers[callback]=handler;
+ CALLBACK_SetDescription(callback,descr);
+ }
+ return csize;
+}
+
+void CALLBACK_RemoveSetup(Bitu callback) {
+ for (Bitu i = 0;i < CB_SIZE;i++) {
+ phys_writeb(CALLBACK_PhysPointer(callback)+i ,(Bit8u) 0x00);
+ }
+}
+
+void CALLBACK_HandlerObject::Uninstall(){
+ if(!installed) return;
+ if(m_type == CALLBACK_HandlerObject::SETUP) {
+ if(vectorhandler.installed){
+ //See if we are the current handler. if so restore the old one
+ if(RealGetVec(vectorhandler.interrupt) == Get_RealPointer()) {
+ RealSetVec(vectorhandler.interrupt,vectorhandler.old_vector);
+ } else
+ LOG(LOG_MISC,LOG_WARN)("Interrupt vector changed on %X %s",vectorhandler.interrupt,CALLBACK_GetDescription(m_callback));
+ }
+ CALLBACK_RemoveSetup(m_callback);
+ } else if(m_type == CALLBACK_HandlerObject::SETUPAT){
+ E_Exit("Callback:SETUP at not handled yet.");
+ } else if(m_type == CALLBACK_HandlerObject::NONE){
+ //Do nothing. Merely DeAllocate the callback
+ } else E_Exit("what kind of callback is this!");
+ if(CallBack_Description[m_callback]) delete [] CallBack_Description[m_callback];
+ CallBack_Description[m_callback] = 0;
+ CALLBACK_DeAllocate(m_callback);
+ installed=false;
+}
+
+CALLBACK_HandlerObject::~CALLBACK_HandlerObject(){
+ Uninstall();
+}
+
+void CALLBACK_HandlerObject::Install(CallBack_Handler handler,Bitu type,const char* description){
+ if(!installed) {
+ installed=true;
+ m_type=SETUP;
+ m_callback=CALLBACK_Allocate();
+ CALLBACK_Setup(m_callback,handler,type,description);
+ } else E_Exit("Callback handler object already installed");
+}
+void CALLBACK_HandlerObject::Install(CallBack_Handler handler,Bitu type,PhysPt addr,const char* description){
+ if(!installed) {
+ installed=true;
+ m_type=SETUP;
+ m_callback=CALLBACK_Allocate();
+ CALLBACK_Setup(m_callback,handler,type,addr,description);
+ } else E_Exit("Callback handler object already installed");
+}
+
+void CALLBACK_HandlerObject::Allocate(CallBack_Handler handler,const char* description) {
+ if(!installed) {
+ installed=true;
+ m_type=NONE;
+ m_callback=CALLBACK_Allocate();
+ CALLBACK_SetDescription(m_callback,description);
+ CallBack_Handlers[m_callback]=handler;
+ } else E_Exit("Callback handler object already installed");
+}
+
+void CALLBACK_HandlerObject::Set_RealVec(Bit8u vec){
+ if(!vectorhandler.installed) {
+ vectorhandler.installed=true;
+ vectorhandler.interrupt=vec;
+ RealSetVec(vec,Get_RealPointer(),vectorhandler.old_vector);
+ } else E_Exit ("double usage of vector handler");
+}
+
+void CALLBACK_Init(Section* /*sec*/) {
+ Bitu i;
+ for (i=0;i<CB_MAX;i++) {
+ CallBack_Handlers[i]=&illegal_handler;
+ }
+
+ /* Setup the Stop Handler */
+ call_stop=CALLBACK_Allocate();
+ CallBack_Handlers[call_stop]=stop_handler;
+ CALLBACK_SetDescription(call_stop,"stop");
+ phys_writeb(CALLBACK_PhysPointer(call_stop)+0,0xFE);
+ phys_writeb(CALLBACK_PhysPointer(call_stop)+1,0x38);
+ phys_writew(CALLBACK_PhysPointer(call_stop)+2,(Bit16u)call_stop);
+
+ /* Setup the idle handler */
+ call_idle=CALLBACK_Allocate();
+ CallBack_Handlers[call_idle]=stop_handler;
+ CALLBACK_SetDescription(call_idle,"idle");
+ for (i=0;i<=11;i++) phys_writeb(CALLBACK_PhysPointer(call_idle)+i,0x90);
+ phys_writeb(CALLBACK_PhysPointer(call_idle)+12,0xFE);
+ phys_writeb(CALLBACK_PhysPointer(call_idle)+13,0x38);
+ phys_writew(CALLBACK_PhysPointer(call_idle)+14,(Bit16u)call_idle);
+
+ /* Default handlers for unhandled interrupts that have to be non-null */
+ call_default=CALLBACK_Allocate();
+ CALLBACK_Setup(call_default,&default_handler,CB_IRET,"default");
+
+ /* Only setup default handler for first part of interrupt table */
+ for (Bit16u ct=0;ct<0x60;ct++) {
+ real_writed(0,ct*4,CALLBACK_RealPointer(call_default));
+ }
+ for (Bit16u ct=0x68;ct<0x70;ct++) {
+ real_writed(0,ct*4,CALLBACK_RealPointer(call_default));
+ }
+ /* Setup block of 0xCD 0xxx instructions */
+ PhysPt rint_base=CALLBACK_GetBase()+CB_MAX*CB_SIZE;
+ for (i=0;i<=0xff;i++) {
+ phys_writeb(rint_base,0xCD);
+ phys_writeb(rint_base+1,(Bit8u)i);
+ phys_writeb(rint_base+2,0xFE);
+ phys_writeb(rint_base+3,0x38);
+ phys_writew(rint_base+4,(Bit16u)call_stop);
+ rint_base+=6;
+
+ }
+ // setup a few interrupt handlers that point to bios IRETs by default
+ real_writed(0,0x66*4,CALLBACK_RealPointer(call_default)); //war2d
+ real_writed(0,0x67*4,CALLBACK_RealPointer(call_default));
+ if (machine==MCH_CGA) real_writed(0,0x68*4,0); //Popcorn
+ real_writed(0,0x5c*4,CALLBACK_RealPointer(call_default)); //Network stuff
+ //real_writed(0,0xf*4,0); some games don't like it
+
+ call_priv_io=CALLBACK_Allocate();
+
+ // virtualizable in-out opcodes
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x00,(Bit8u)0xec); // in al, dx
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x01,(Bit8u)0xcb); // retf
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x02,(Bit8u)0xed); // in ax, dx
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x03,(Bit8u)0xcb); // retf
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x04,(Bit8u)0x66); // in eax, dx
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x05,(Bit8u)0xed);
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x06,(Bit8u)0xcb); // retf
+
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x08,(Bit8u)0xee); // out dx, al
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x09,(Bit8u)0xcb); // retf
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x0a,(Bit8u)0xef); // out dx, ax
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x0b,(Bit8u)0xcb); // retf
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x0c,(Bit8u)0x66); // out dx, eax
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x0d,(Bit8u)0xef);
+ phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x0e,(Bit8u)0xcb); // retf
+}
diff --git a/src/cpu/core_dyn_x86.cpp b/src/cpu/core_dyn_x86.cpp
new file mode 100644
index 000000000..17e842219
--- /dev/null
+++ b/src/cpu/core_dyn_x86.cpp
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+
+#if (C_DYNAMIC_X86)
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#if defined (WIN32)
+#include <windows.h>
+#include <winbase.h>
+#endif
+
+#if (C_HAVE_MPROTECT)
+#include <sys/mman.h>
+
+#include <limits.h>
+#ifndef PAGESIZE
+#define PAGESIZE 4096
+#endif
+#endif /* C_HAVE_MPROTECT */
+
+#include "callback.h"
+#include "regs.h"
+#include "mem.h"
+#include "cpu.h"
+#include "debug.h"
+#include "paging.h"
+#include "inout.h"
+#include "fpu.h"
+
+#define CACHE_MAXSIZE (4096*3)
+#define CACHE_TOTAL (1024*1024*8)
+#define CACHE_PAGES (512)
+#define CACHE_BLOCKS (64*1024)
+#define CACHE_ALIGN (16)
+#define DYN_HASH_SHIFT (4)
+#define DYN_PAGE_HASH (4096>>DYN_HASH_SHIFT)
+#define DYN_LINKS (16)
+
+//#define DYN_LOG 1 //Turn logging on
+
+
+#if C_FPU
+#define CPU_FPU 1 //Enable FPU escape instructions
+#endif
+
+enum {
+ G_EAX,G_ECX,G_EDX,G_EBX,
+ G_ESP,G_EBP,G_ESI,G_EDI,
+ G_ES,G_CS,G_SS,G_DS,G_FS,G_GS,
+ G_FLAGS,G_NEWESP,G_EIP,
+ G_EA,G_STACK,G_CYCLES,
+ G_TMPB,G_TMPW,G_SHIFT,
+ G_EXIT,
+ G_MAX,
+};
+
+enum SingleOps {
+ SOP_INC,SOP_DEC,
+ SOP_NOT,SOP_NEG,
+};
+
+enum DualOps {
+ DOP_ADD,DOP_ADC,
+ DOP_SUB,DOP_SBB,
+ DOP_CMP,DOP_XOR,
+ DOP_AND,DOP_OR,
+ DOP_TEST,
+ DOP_MOV,
+ DOP_XCHG,
+};
+
+enum ShiftOps {
+ SHIFT_ROL,SHIFT_ROR,
+ SHIFT_RCL,SHIFT_RCR,
+ SHIFT_SHL,SHIFT_SHR,
+ SHIFT_SAL,SHIFT_SAR,
+};
+
+enum BranchTypes {
+ BR_O,BR_NO,BR_B,BR_NB,
+ BR_Z,BR_NZ,BR_BE,BR_NBE,
+ BR_S,BR_NS,BR_P,BR_NP,
+ BR_L,BR_NL,BR_LE,BR_NLE
+};
+
+
+enum BlockReturn {
+ BR_Normal=0,
+ BR_Cycles,
+ BR_Link1,BR_Link2,
+ BR_Opcode,
+#if (C_DEBUG)
+ BR_OpcodeFull,
+#endif
+ BR_Iret,
+ BR_CallBack,
+ BR_SMCBlock
+};
+
+#define SMC_CURRENT_BLOCK 0xffff
+
+
+#define DYNFLG_HAS16 0x1 //Would like 8-bit host reg support
+#define DYNFLG_HAS8 0x2 //Would like 16-bit host reg support
+#define DYNFLG_LOAD 0x4 //Load value when accessed
+#define DYNFLG_SAVE 0x8 //Needs to be saved back at the end of block
+#define DYNFLG_CHANGED 0x10 //Value is in a register and changed from load
+#define DYNFLG_ACTIVE 0x20 //Register has an active value
+
+class GenReg;
+class CodePageHandler;
+
+struct DynReg {
+ Bitu flags;
+ GenReg * genreg;
+ void * data;
+};
+
+enum DynAccess {
+ DA_d,DA_w,
+ DA_bh,DA_bl
+};
+
+enum ByteCombo {
+ BC_ll,BC_lh,
+ BC_hl,BC_hh,
+};
+
+static DynReg DynRegs[G_MAX];
+#define DREG(_WHICH_) &DynRegs[G_ ## _WHICH_ ]
+
+static struct {
+ Bitu ea,tmpb,tmpd,stack,shift,newesp;
+} extra_regs;
+
+static void IllegalOption(const char* msg) {
+ E_Exit("DynCore: illegal option in %s",msg);
+}
+
+#include "core_dyn_x86/cache.h"
+
+static struct {
+ Bitu callback;
+ Bit32u readdata;
+} core_dyn;
+
+static struct {
+ Bit32u state[32];
+ FPU_P_Reg temp,temp2;
+ Bit32u dh_fpu_enabled;
+ Bit32u state_used;
+ Bit32u cw,host_cw;
+ Bit8u temp_state[128];
+} dyn_dh_fpu;
+
+
+#include "core_dyn_x86/risc_x86.h"
+
+struct DynState {
+ DynReg regs[G_MAX];
+};
+
+static void dyn_flags_host_to_gen(void) {
+ gen_dop_word(DOP_MOV,true,DREG(EXIT),DREG(FLAGS));
+ gen_dop_word_imm(DOP_AND,true,DREG(EXIT),FMASK_TEST);
+ gen_load_flags(DREG(EXIT));
+ gen_releasereg(DREG(EXIT));
+ gen_releasereg(DREG(FLAGS));
+}
+
+static void dyn_flags_gen_to_host(void) {
+ gen_save_flags(DREG(EXIT));
+ gen_dop_word_imm(DOP_AND,true,DREG(EXIT),FMASK_TEST);
+ gen_dop_word_imm(DOP_AND,true,DREG(FLAGS),~FMASK_TEST);
+ gen_dop_word(DOP_OR,true,DREG(FLAGS),DREG(EXIT)); //flags are marked for save
+ gen_releasereg(DREG(EXIT));
+ gen_releasereg(DREG(FLAGS));
+}
+
+static void dyn_savestate(DynState * state) {
+ for (Bitu i=0;i<G_MAX;i++) {
+ state->regs[i].flags=DynRegs[i].flags;
+ state->regs[i].genreg=DynRegs[i].genreg;
+ }
+}
+
+static void dyn_loadstate(DynState * state) {
+ for (Bitu i=0;i<G_MAX;i++) {
+ gen_setupreg(&DynRegs[i],&state->regs[i]);
+ }
+}
+
+static void dyn_synchstate(DynState * state) {
+ for (Bitu i=0;i<G_MAX;i++) {
+ gen_synchreg(&DynRegs[i],&state->regs[i]);
+ }
+}
+
+static void dyn_saveregister(DynReg * src_reg, DynReg * dst_reg) {
+ dst_reg->flags=src_reg->flags;
+ dst_reg->genreg=src_reg->genreg;
+}
+
+static void dyn_restoreregister(DynReg * src_reg, DynReg * dst_reg) {
+ dst_reg->flags=src_reg->flags;
+ dst_reg->genreg=src_reg->genreg;
+ dst_reg->genreg->dynreg=dst_reg; // necessary when register has been released
+}
+
+#include "core_dyn_x86/decoder.h"
+
+#if defined (_MSC_VER)
+#define DH_FPU_SAVE_REINIT \
+{ \
+ __asm { \
+ __asm fnsave dyn_dh_fpu.state[0] \
+ } \
+ dyn_dh_fpu.state_used=false; \
+ dyn_dh_fpu.state[0]|=0x3f; \
+}
+#else
+#define DH_FPU_SAVE_REINIT \
+{ \
+ __asm__ volatile ( \
+ "fnsave %0 \n" \
+ : "=m" (dyn_dh_fpu.state[0]) \
+ : \
+ : "memory" \
+ ); \
+ dyn_dh_fpu.state_used=false; \
+ dyn_dh_fpu.state[0]|=0x3f; \
+}
+#endif
+
+
+Bits CPU_Core_Dyn_X86_Run(void) {
+ /* Determine the linear address of CS:EIP */
+restart_core:
+ PhysPt ip_point=SegPhys(cs)+reg_eip;
+#if C_DEBUG
+#if C_HEAVY_DEBUG
+ if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
+#endif
+#endif
+ CodePageHandler * chandler=0;
+ if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) {
+ CPU_Exception(cpu.exception.which,cpu.exception.error);
+ goto restart_core;
+ }
+ if (!chandler) {
+ if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
+ return CPU_Core_Normal_Run();
+ }
+ /* Find correct Dynamic Block to run */
+ CacheBlock * block=chandler->FindCacheBlock(ip_point&4095);
+ if (!block) {
+ if (!chandler->invalidation_map || (chandler->invalidation_map[ip_point&4095]<4)) {
+ block=CreateCacheBlock(chandler,ip_point,32);
+ } else {
+ Bitu old_cycles=CPU_Cycles;
+ CPU_Cycles=1;
+ Bits nc_retcode=CPU_Core_Normal_Run();
+ if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
+ if (!nc_retcode) {
+ CPU_Cycles=old_cycles-1;
+ goto restart_core;
+ }
+ CPU_CycleLeft+=old_cycles;
+ return nc_retcode;
+ }
+ }
+run_block:
+ cache.block.running=0;
+ BlockReturn ret=gen_runcode(block->cache.start);
+#if C_DEBUG
+ cycle_count += 32;
+#endif
+ switch (ret) {
+ case BR_Iret:
+#if C_DEBUG
+#if C_HEAVY_DEBUG
+ if (DEBUG_HeavyIsBreakpoint()) {
+ if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
+ return debugCallback;
+ }
+#endif
+#endif
+ if (!GETFLAG(TF)) {
+ if (GETFLAG(IF) && PIC_IRQCheck) {
+ if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
+ return CBRET_NONE;
+ }
+ goto restart_core;
+ }
+ cpudecoder=CPU_Core_Dyn_X86_Trap_Run;
+ if (!dyn_dh_fpu.state_used) return CBRET_NONE;
+ DH_FPU_SAVE_REINIT
+ return CBRET_NONE;
+ case BR_Normal:
+ /* Maybe check if we staying in the same page? */
+#if C_DEBUG
+#if C_HEAVY_DEBUG
+ if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
+#endif
+#endif
+ goto restart_core;
+ case BR_Cycles:
+#if C_DEBUG
+#if C_HEAVY_DEBUG
+ if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
+#endif
+#endif
+ if (!dyn_dh_fpu.state_used) return CBRET_NONE;
+ DH_FPU_SAVE_REINIT
+ return CBRET_NONE;
+ case BR_CallBack:
+ if (!dyn_dh_fpu.state_used) return core_dyn.callback;
+ DH_FPU_SAVE_REINIT
+ return core_dyn.callback;
+ case BR_SMCBlock:
+// LOG_MSG("selfmodification of running block at %x:%x",SegValue(cs),reg_eip);
+ cpu.exception.which=0;
+ // fallthrough, let the normal core handle the block-modifying instruction
+ case BR_Opcode:
+ CPU_CycleLeft+=CPU_Cycles;
+ CPU_Cycles=1;
+ if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
+ return CPU_Core_Normal_Run();
+#if (C_DEBUG)
+ case BR_OpcodeFull:
+ CPU_CycleLeft+=CPU_Cycles;
+ CPU_Cycles=1;
+ if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
+ return CPU_Core_Full_Run();
+#endif
+ case BR_Link1:
+ case BR_Link2:
+ {
+ Bitu temp_ip=SegPhys(cs)+reg_eip;
+ CodePageHandler * temp_handler=(CodePageHandler *)get_tlb_readhandler(temp_ip);
+ if (temp_handler->flags & (cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16)) {
+ block=temp_handler->FindCacheBlock(temp_ip & 4095);
+ if (!block) goto restart_core;
+ cache.block.running->LinkTo(ret==BR_Link2,block);
+ goto run_block;
+ }
+ }
+ goto restart_core;
+ }
+ if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
+ return CBRET_NONE;
+}
+
+Bits CPU_Core_Dyn_X86_Trap_Run(void) {
+ Bits oldCycles = CPU_Cycles;
+ CPU_Cycles = 1;
+ cpu.trap_skip = false;
+
+ Bits ret=CPU_Core_Normal_Run();
+ if (!cpu.trap_skip) CPU_HW_Interrupt(1);
+ CPU_Cycles = oldCycles-1;
+ cpudecoder = &CPU_Core_Dyn_X86_Run;
+
+ return ret;
+}
+
+void CPU_Core_Dyn_X86_Init(void) {
+ Bits i;
+ /* Setup the global registers and their flags */
+ for (i=0;i<G_MAX;i++) DynRegs[i].genreg=0;
+ DynRegs[G_EAX].data=&reg_eax;
+ DynRegs[G_EAX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
+ DynRegs[G_ECX].data=&reg_ecx;
+ DynRegs[G_ECX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
+ DynRegs[G_EDX].data=&reg_edx;
+ DynRegs[G_EDX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
+ DynRegs[G_EBX].data=&reg_ebx;
+ DynRegs[G_EBX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
+
+ DynRegs[G_EBP].data=&reg_ebp;
+ DynRegs[G_EBP].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
+ DynRegs[G_ESP].data=&reg_esp;
+ DynRegs[G_ESP].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
+ DynRegs[G_EDI].data=&reg_edi;
+ DynRegs[G_EDI].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
+ DynRegs[G_ESI].data=&reg_esi;
+ DynRegs[G_ESI].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
+
+ DynRegs[G_ES].data=&Segs.phys[es];
+ DynRegs[G_ES].flags=DYNFLG_LOAD|DYNFLG_SAVE;
+ DynRegs[G_CS].data=&Segs.phys[cs];
+ DynRegs[G_CS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
+ DynRegs[G_SS].data=&Segs.phys[ss];
+ DynRegs[G_SS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
+ DynRegs[G_DS].data=&Segs.phys[ds];
+ DynRegs[G_DS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
+ DynRegs[G_FS].data=&Segs.phys[fs];
+ DynRegs[G_FS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
+ DynRegs[G_GS].data=&Segs.phys[gs];
+ DynRegs[G_GS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
+
+ DynRegs[G_FLAGS].data=&reg_flags;
+ DynRegs[G_FLAGS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
+
+ DynRegs[G_NEWESP].data=&extra_regs.newesp;
+ DynRegs[G_NEWESP].flags=0;
+
+ DynRegs[G_EIP].data=&reg_eip;
+ DynRegs[G_EIP].flags=DYNFLG_LOAD|DYNFLG_SAVE;
+
+ DynRegs[G_EA].data=&extra_regs.ea;
+ DynRegs[G_EA].flags=0;
+ DynRegs[G_STACK].data=&extra_regs.stack;
+ DynRegs[G_STACK].flags=0;
+ DynRegs[G_CYCLES].data=&CPU_Cycles;
+ DynRegs[G_CYCLES].flags=DYNFLG_LOAD|DYNFLG_SAVE;
+ DynRegs[G_TMPB].data=&extra_regs.tmpb;
+ DynRegs[G_TMPB].flags=DYNFLG_HAS8|DYNFLG_HAS16;
+ DynRegs[G_TMPW].data=&extra_regs.tmpd;
+ DynRegs[G_TMPW].flags=DYNFLG_HAS16;
+ DynRegs[G_SHIFT].data=&extra_regs.shift;
+ DynRegs[G_SHIFT].flags=DYNFLG_HAS8|DYNFLG_HAS16;
+ DynRegs[G_EXIT].data=0;
+ DynRegs[G_EXIT].flags=DYNFLG_HAS16;
+ /* Init the generator */
+ gen_init();
+
+ /* Init the fpu state */
+ dyn_dh_fpu.dh_fpu_enabled=true;
+ dyn_dh_fpu.state_used=false;
+ dyn_dh_fpu.cw=0x37f;
+#if defined (_MSC_VER)
+ __asm {
+ __asm finit
+ __asm fsave dyn_dh_fpu.state[0]
+ __asm fstcw dyn_dh_fpu.host_cw
+ }
+#else
+ __asm__ volatile (
+ "finit \n"
+ "fsave %0 \n"
+ "fstcw %1 \n"
+ : "=m" (dyn_dh_fpu.state[0]), "=m" (dyn_dh_fpu.host_cw)
+ :
+ : "memory"
+ );
+#endif
+
+ return;
+}
+
+void CPU_Core_Dyn_X86_Cache_Init(bool enable_cache) {
+ /* Initialize code cache and dynamic blocks */
+ cache_init(enable_cache);
+}
+
+void CPU_Core_Dyn_X86_Cache_Close(void) {
+ cache_close();
+}
+
+void CPU_Core_Dyn_X86_Cache_Reset(void) {
+ cache_reset();
+}
+
+void CPU_Core_Dyn_X86_SetFPUMode(bool dh_fpu) {
+ dyn_dh_fpu.dh_fpu_enabled=dh_fpu;
+}
+
+Bit32u fpu_state[32];
+
+void CPU_Core_Dyn_X86_SaveDHFPUState(void) {
+ if (dyn_dh_fpu.dh_fpu_enabled) {
+ if (dyn_dh_fpu.state_used!=0) {
+#if defined (_MSC_VER)
+ __asm {
+ __asm fsave fpu_state[0]
+ __asm finit
+ }
+#else
+ __asm__ volatile (
+ "fsave %0 \n"
+ "finit \n"
+ : "=m" (fpu_state[0])
+ :
+ : "memory"
+ );
+#endif
+ }
+ }
+}
+
+void CPU_Core_Dyn_X86_RestoreDHFPUState(void) {
+ if (dyn_dh_fpu.dh_fpu_enabled) {
+ if (dyn_dh_fpu.state_used!=0) {
+#if defined (_MSC_VER)
+ __asm {
+ __asm frstor fpu_state[0]
+ }
+#else
+ __asm__ volatile (
+ "frstor %0 \n"
+ :
+ : "m" (fpu_state[0])
+ :
+ );
+#endif
+ }
+ }
+}
+
+#else
+
+void CPU_Core_Dyn_X86_SaveDHFPUState(void) {
+}
+
+void CPU_Core_Dyn_X86_RestoreDHFPUState(void) {
+}
+
+#endif
diff --git a/src/cpu/core_dyn_x86/Makefile.am b/src/cpu/core_dyn_x86/Makefile.am
new file mode 100644
index 000000000..3d9be0906
--- /dev/null
+++ b/src/cpu/core_dyn_x86/Makefile.am
@@ -0,0 +1,2 @@
+noinst_HEADERS = cache.h helpers.h decoder.h risc_x86.h string.h \
+ dyn_fpu.h dyn_fpu_dh.h \ No newline at end of file
diff --git a/src/cpu/core_dyn_x86/cache.h b/src/cpu/core_dyn_x86/cache.h
new file mode 100644
index 000000000..08a3526b4
--- /dev/null
+++ b/src/cpu/core_dyn_x86/cache.h
@@ -0,0 +1,643 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+class CacheBlock {
+public:
+ void Clear(void);
+ void LinkTo(Bitu index,CacheBlock * toblock) {
+ assert(toblock);
+ link[index].to=toblock;
+ link[index].next=toblock->link[index].from;
+ toblock->link[index].from=this;
+ }
+ struct {
+ Bit16u start,end; //Where the page is the original code
+ CodePageHandler * handler; //Page containing this code
+ } page;
+ struct {
+ Bit8u * start; //Where in the cache are we
+ Bitu size;
+ CacheBlock * next;
+ Bit8u * wmapmask;
+ Bit16u maskstart;
+ Bit16u masklen;
+ } cache;
+ struct {
+ Bitu index;
+ CacheBlock * next;
+ } hash;
+ struct {
+ CacheBlock * to;
+ CacheBlock * next;
+ CacheBlock * from;
+ } link[2];
+ CacheBlock * crossblock;
+};
+
+static struct {
+ struct {
+ CacheBlock * first;
+ CacheBlock * active;
+ CacheBlock * free;
+ CacheBlock * running;
+ } block;
+ Bit8u * pos;
+ CodePageHandler * free_pages;
+ CodePageHandler * used_pages;
+ CodePageHandler * last_page;
+} cache;
+
+static CacheBlock link_blocks[2];
+
+class CodePageHandler : public PageHandler {
+public:
+ CodePageHandler() {
+ invalidation_map=NULL;
+ }
+ void SetupAt(Bitu _phys_page,PageHandler * _old_pagehandler) {
+ phys_page=_phys_page;
+ old_pagehandler=_old_pagehandler;
+ flags=old_pagehandler->flags|(cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16);
+ flags&=~PFLAG_WRITEABLE;
+ active_blocks=0;
+ active_count=16;
+ memset(&hash_map,0,sizeof(hash_map));
+ memset(&write_map,0,sizeof(write_map));
+ if (invalidation_map!=NULL) {
+ free(invalidation_map);
+ invalidation_map=NULL;
+ }
+ }
+ bool InvalidateRange(Bitu start,Bitu end) {
+ Bits index=1+(end>>DYN_HASH_SHIFT);
+ bool is_current_block=false;
+ Bit32u ip_point=SegPhys(cs)+reg_eip;
+ ip_point=(PAGING_GetPhysicalPage(ip_point)-(phys_page<<12))+(ip_point&0xfff);
+ while (index>=0) {
+ Bitu map=0;
+ for (Bitu count=start;count<=end;count++) map+=write_map[count];
+ if (!map) return is_current_block;
+ CacheBlock * block=hash_map[index];
+ while (block) {
+ CacheBlock * nextblock=block->hash.next;
+ if (start<=block->page.end && end>=block->page.start) {
+ if (ip_point<=block->page.end && ip_point>=block->page.start) is_current_block=true;
+ block->Clear();
+ }
+ block=nextblock;
+ }
+ index--;
+ }
+ return is_current_block;
+ }
+ void writeb(PhysPt addr,Bitu val){
+ if (GCC_UNLIKELY(old_pagehandler->flags&PFLAG_HASROM)) return;
+ if (GCC_UNLIKELY((old_pagehandler->flags&PFLAG_READABLE)!=PFLAG_READABLE)) {
+ E_Exit("wb:non-readable code page found that is no ROM page");
+ }
+ addr&=4095;
+ if (host_readb(hostmem+addr)==(Bit8u)val) return;
+ host_writeb(hostmem+addr,val);
+ if (!*(Bit8u*)&write_map[addr]) {
+ if (active_blocks) return;
+ active_count--;
+ if (!active_count) Release();
+ return;
+ } else if (!invalidation_map) {
+ invalidation_map=(Bit8u*)malloc(4096);
+ memset(invalidation_map,0,4096);
+ }
+ invalidation_map[addr]++;
+ InvalidateRange(addr,addr);
+ }
+ void writew(PhysPt addr,Bitu val){
+ if (GCC_UNLIKELY(old_pagehandler->flags&PFLAG_HASROM)) return;
+ if (GCC_UNLIKELY((old_pagehandler->flags&PFLAG_READABLE)!=PFLAG_READABLE)) {
+ E_Exit("ww:non-readable code page found that is no ROM page");
+ }
+ addr&=4095;
+ if (host_readw(hostmem+addr)==(Bit16u)val) return;
+ host_writew(hostmem+addr,val);
+ if (!*(Bit16u*)&write_map[addr]) {
+ if (active_blocks) return;
+ active_count--;
+ if (!active_count) Release();
+ return;
+ } else if (!invalidation_map) {
+ invalidation_map=(Bit8u*)malloc(4096);
+ memset(invalidation_map,0,4096);
+ }
+ (*(Bit16u*)&invalidation_map[addr])+=0x101;
+ InvalidateRange(addr,addr+1);
+ }
+ void writed(PhysPt addr,Bitu val){
+ if (GCC_UNLIKELY(old_pagehandler->flags&PFLAG_HASROM)) return;
+ if (GCC_UNLIKELY((old_pagehandler->flags&PFLAG_READABLE)!=PFLAG_READABLE)) {
+ E_Exit("wd:non-readable code page found that is no ROM page");
+ }
+ addr&=4095;
+ if (host_readd(hostmem+addr)==(Bit32u)val) return;
+ host_writed(hostmem+addr,val);
+ if (!*(Bit32u*)&write_map[addr]) {
+ if (active_blocks) return;
+ active_count--;
+ if (!active_count) Release();
+ return;
+ } else if (!invalidation_map) {
+ invalidation_map=(Bit8u*)malloc(4096);
+ memset(invalidation_map,0,4096);
+ }
+ (*(Bit32u*)&invalidation_map[addr])+=0x1010101;
+ InvalidateRange(addr,addr+3);
+ }
+ bool writeb_checked(PhysPt addr,Bitu val) {
+ if (GCC_UNLIKELY(old_pagehandler->flags&PFLAG_HASROM)) return false;
+ if (GCC_UNLIKELY((old_pagehandler->flags&PFLAG_READABLE)!=PFLAG_READABLE)) {
+ E_Exit("cb:non-readable code page found that is no ROM page");
+ }
+ addr&=4095;
+ if (host_readb(hostmem+addr)==(Bit8u)val) return false;
+ if (!*(Bit8u*)&write_map[addr]) {
+ if (!active_blocks) {
+ active_count--;
+ if (!active_count) Release();
+ }
+ } else {
+ if (!invalidation_map) {
+ invalidation_map=(Bit8u*)malloc(4096);
+ memset(invalidation_map,0,4096);
+ }
+ invalidation_map[addr]++;
+ if (InvalidateRange(addr,addr)) {
+ cpu.exception.which=SMC_CURRENT_BLOCK;
+ return true;
+ }
+ }
+ host_writeb(hostmem+addr,val);
+ return false;
+ }
+ bool writew_checked(PhysPt addr,Bitu val) {
+ if (GCC_UNLIKELY(old_pagehandler->flags&PFLAG_HASROM)) return false;
+ if (GCC_UNLIKELY((old_pagehandler->flags&PFLAG_READABLE)!=PFLAG_READABLE)) {
+ E_Exit("cw:non-readable code page found that is no ROM page");
+ }
+ addr&=4095;
+ if (host_readw(hostmem+addr)==(Bit16u)val) return false;
+ if (!*(Bit16u*)&write_map[addr]) {
+ if (!active_blocks) {
+ active_count--;
+ if (!active_count) Release();
+ }
+ } else {
+ if (!invalidation_map) {
+ invalidation_map=(Bit8u*)malloc(4096);
+ memset(invalidation_map,0,4096);
+ }
+ (*(Bit16u*)&invalidation_map[addr])+=0x101;
+ if (InvalidateRange(addr,addr+1)) {
+ cpu.exception.which=SMC_CURRENT_BLOCK;
+ return true;
+ }
+ }
+ host_writew(hostmem+addr,val);
+ return false;
+ }
+ bool writed_checked(PhysPt addr,Bitu val) {
+ if (GCC_UNLIKELY(old_pagehandler->flags&PFLAG_HASROM)) return false;
+ if (GCC_UNLIKELY((old_pagehandler->flags&PFLAG_READABLE)!=PFLAG_READABLE)) {
+ E_Exit("cd:non-readable code page found that is no ROM page");
+ }
+ addr&=4095;
+ if (host_readd(hostmem+addr)==(Bit32u)val) return false;
+ if (!*(Bit32u*)&write_map[addr]) {
+ if (!active_blocks) {
+ active_count--;
+ if (!active_count) Release();
+ }
+ } else {
+ if (!invalidation_map) {
+ invalidation_map=(Bit8u*)malloc(4096);
+ memset(invalidation_map,0,4096);
+ }
+ (*(Bit32u*)&invalidation_map[addr])+=0x1010101;
+ if (InvalidateRange(addr,addr+3)) {
+ cpu.exception.which=SMC_CURRENT_BLOCK;
+ return true;
+ }
+ }
+ host_writed(hostmem+addr,val);
+ return false;
+ }
+ void AddCacheBlock(CacheBlock * block) {
+ Bitu index=1+(block->page.start>>DYN_HASH_SHIFT);
+ block->hash.next=hash_map[index];
+ block->hash.index=index;
+ hash_map[index]=block;
+ block->page.handler=this;
+ active_blocks++;
+ }
+ void AddCrossBlock(CacheBlock * block) {
+ block->hash.next=hash_map[0];
+ block->hash.index=0;
+ hash_map[0]=block;
+ block->page.handler=this;
+ active_blocks++;
+ }
+ void DelCacheBlock(CacheBlock * block) {
+ active_blocks--;
+ active_count=16;
+ CacheBlock * * where=&hash_map[block->hash.index];
+ while (*where!=block) {
+ where=&((*where)->hash.next);
+ //Will crash if a block isn't found, which should never happen.
+ }
+ *where=block->hash.next;
+ if (GCC_UNLIKELY(block->cache.wmapmask!=NULL)) {
+ for (Bitu i=block->page.start;i<block->cache.maskstart;i++) {
+ if (write_map[i]) write_map[i]--;
+ }
+ Bitu maskct=0;
+ for (Bitu i=block->cache.maskstart;i<=block->page.end;i++,maskct++) {
+ if (write_map[i]) {
+ if ((maskct>=block->cache.masklen) || (!block->cache.wmapmask[maskct])) write_map[i]--;
+ }
+ }
+ free(block->cache.wmapmask);
+ block->cache.wmapmask=NULL;
+ } else {
+ for (Bitu i=block->page.start;i<=block->page.end;i++) {
+ if (write_map[i]) write_map[i]--;
+ }
+ }
+ }
+ void Release(void) {
+ MEM_SetPageHandler(phys_page,1,old_pagehandler);
+ PAGING_ClearTLB();
+ if (prev) prev->next=next;
+ else cache.used_pages=next;
+ if (next) next->prev=prev;
+ else cache.last_page=prev;
+ next=cache.free_pages;
+ cache.free_pages=this;
+ prev=0;
+ }
+ void ClearRelease(void) {
+ for (Bitu index=0;index<(1+DYN_PAGE_HASH);index++) {
+ CacheBlock * block=hash_map[index];
+ while (block) {
+ CacheBlock * nextblock=block->hash.next;
+ block->page.handler=0; //No need, full clear
+ block->Clear();
+ block=nextblock;
+ }
+ }
+ Release();
+ }
+ CacheBlock * FindCacheBlock(Bitu start) {
+ CacheBlock * block=hash_map[1+(start>>DYN_HASH_SHIFT)];
+ while (block) {
+ if (block->page.start==start) return block;
+ block=block->hash.next;
+ }
+ return 0;
+ }
+ HostPt GetHostReadPt(Bitu phys_page) {
+ hostmem=old_pagehandler->GetHostReadPt(phys_page);
+ return hostmem;
+ }
+ HostPt GetHostWritePt(Bitu phys_page) {
+ return GetHostReadPt( phys_page );
+ }
+public:
+ Bit8u write_map[4096];
+ Bit8u * invalidation_map;
+ CodePageHandler * next, * prev;
+private:
+ PageHandler * old_pagehandler;
+ CacheBlock * hash_map[1+DYN_PAGE_HASH];
+ Bitu active_blocks;
+ Bitu active_count;
+ HostPt hostmem;
+ Bitu phys_page;
+};
+
+
+static INLINE void cache_addunsedblock(CacheBlock * block) {
+ block->cache.next=cache.block.free;
+ cache.block.free=block;
+}
+
+static CacheBlock * cache_getblock(void) {
+ CacheBlock * ret=cache.block.free;
+ if (!ret) E_Exit("Ran out of CacheBlocks" );
+ cache.block.free=ret->cache.next;
+ ret->cache.next=0;
+ return ret;
+}
+
+void CacheBlock::Clear(void) {
+ Bitu ind;
+ /* Check if this is not a cross page block */
+ if (hash.index) for (ind=0;ind<2;ind++) {
+ CacheBlock * fromlink=link[ind].from;
+ link[ind].from=0;
+ while (fromlink) {
+ CacheBlock * nextlink=fromlink->link[ind].next;
+ fromlink->link[ind].next=0;
+ fromlink->link[ind].to=&link_blocks[ind];
+ fromlink=nextlink;
+ }
+ if (link[ind].to!=&link_blocks[ind]) {
+ CacheBlock * * wherelink=&link[ind].to->link[ind].from;
+ while (*wherelink != this && *wherelink) {
+ wherelink = &(*wherelink)->link[ind].next;
+ }
+ if(*wherelink)
+ *wherelink = (*wherelink)->link[ind].next;
+ else
+ LOG(LOG_CPU,LOG_ERROR)("Cache anomaly. please investigate");
+ }
+ } else
+ cache_addunsedblock(this);
+ if (crossblock) {
+ crossblock->crossblock=0;
+ crossblock->Clear();
+ crossblock=0;
+ }
+ if (page.handler) {
+ page.handler->DelCacheBlock(this);
+ page.handler=0;
+ }
+ if (cache.wmapmask){
+ free(cache.wmapmask);
+ cache.wmapmask=NULL;
+ }
+}
+
+
+static CacheBlock * cache_openblock(void) {
+ CacheBlock * block=cache.block.active;
+ /* check for enough space in this block */
+ Bitu size=block->cache.size;
+ CacheBlock * nextblock=block->cache.next;
+ if (block->page.handler)
+ block->Clear();
+ while (size<CACHE_MAXSIZE) {
+ if (!nextblock)
+ goto skipresize;
+ size+=nextblock->cache.size;
+ CacheBlock * tempblock=nextblock->cache.next;
+ if (nextblock->page.handler)
+ nextblock->Clear();
+ cache_addunsedblock(nextblock);
+ nextblock=tempblock;
+ }
+skipresize:
+ block->cache.size=size;
+ block->cache.next=nextblock;
+ cache.pos=block->cache.start;
+ return block;
+}
+
+static void cache_closeblock(void) {
+ CacheBlock * block=cache.block.active;
+ block->link[0].to=&link_blocks[0];
+ block->link[1].to=&link_blocks[1];
+ block->link[0].from=0;
+ block->link[1].from=0;
+ block->link[0].next=0;
+ block->link[1].next=0;
+ /* Close the block with correct alignments */
+ Bitu written=cache.pos-block->cache.start;
+ if (written>block->cache.size) {
+ if (!block->cache.next) {
+ if (written>block->cache.size+CACHE_MAXSIZE) E_Exit("CacheBlock overrun 1 %d",written-block->cache.size);
+ } else E_Exit("CacheBlock overrun 2 written %d size %d",written,block->cache.size);
+ } else {
+ Bitu new_size;
+ Bitu left=block->cache.size-written;
+ /* Smaller than cache align then don't bother to resize */
+ if (left>CACHE_ALIGN) {
+ new_size=((written-1)|(CACHE_ALIGN-1))+1;
+ CacheBlock * newblock=cache_getblock();
+ newblock->cache.start=block->cache.start+new_size;
+ newblock->cache.size=block->cache.size-new_size;
+ newblock->cache.next=block->cache.next;
+ block->cache.next=newblock;
+ block->cache.size=new_size;
+ }
+ }
+ /* Advance the active block pointer */
+ if (!block->cache.next) {
+// LOG_MSG("Cache full restarting");
+ cache.block.active=cache.block.first;
+ } else {
+ cache.block.active=block->cache.next;
+ }
+}
+
+static INLINE void cache_addb(Bit8u val) {
+ *cache.pos++=val;
+}
+
+static INLINE void cache_addw(Bit16u val) {
+ *(Bit16u*)cache.pos=val;
+ cache.pos+=2;
+}
+
+static INLINE void cache_addd(Bit32u val) {
+ *(Bit32u*)cache.pos=val;
+ cache.pos+=4;
+}
+
+
+static void gen_return(BlockReturn retcode);
+
+static Bit8u * cache_code_start_ptr=NULL;
+static Bit8u * cache_code=NULL;
+static Bit8u * cache_code_link_blocks=NULL;
+static CacheBlock * cache_blocks=NULL;
+
+/* Define temporary pagesize so the MPROTECT case and the regular case share as much code as possible */
+#if (C_HAVE_MPROTECT)
+#define PAGESIZE_TEMP PAGESIZE
+#else
+#define PAGESIZE_TEMP 4096
+#endif
+
+static bool cache_initialized = false;
+
+static void cache_init(bool enable) {
+ Bits i;
+ if (enable) {
+ if (cache_initialized) return;
+ cache_initialized = true;
+ if (cache_blocks == NULL) {
+ cache_blocks=(CacheBlock*)malloc(CACHE_BLOCKS*sizeof(CacheBlock));
+ if(!cache_blocks) E_Exit("Allocating cache_blocks has failed");
+ memset(cache_blocks,0,sizeof(CacheBlock)*CACHE_BLOCKS);
+ cache.block.free=&cache_blocks[0];
+ for (i=0;i<CACHE_BLOCKS-1;i++) {
+ cache_blocks[i].link[0].to=(CacheBlock *)1;
+ cache_blocks[i].link[1].to=(CacheBlock *)1;
+ cache_blocks[i].cache.next=&cache_blocks[i+1];
+ }
+ }
+ if (cache_code_start_ptr==NULL) {
+#if defined (WIN32)
+ cache_code_start_ptr=(Bit8u*)VirtualAlloc(0,CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP,
+ MEM_COMMIT,PAGE_EXECUTE_READWRITE);
+ if (!cache_code_start_ptr)
+ cache_code_start_ptr=(Bit8u*)malloc(CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP);
+#else
+ cache_code_start_ptr=(Bit8u*)malloc(CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP);
+#endif
+ if(!cache_code_start_ptr) E_Exit("Allocating dynamic core cache memory failed");
+
+ cache_code=(Bit8u*)(((Bitu)cache_code_start_ptr + PAGESIZE_TEMP-1) & ~(PAGESIZE_TEMP-1)); //Bitu is same size as a pointer.
+
+ cache_code_link_blocks=cache_code;
+ cache_code+=PAGESIZE_TEMP;
+
+#if (C_HAVE_MPROTECT)
+ if(mprotect(cache_code_link_blocks,CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP,PROT_WRITE|PROT_READ|PROT_EXEC))
+ LOG_MSG("Setting execute permission on the code cache has failed!");
+#endif
+ CacheBlock * block=cache_getblock();
+ cache.block.first=block;
+ cache.block.active=block;
+ block->cache.start=&cache_code[0];
+ block->cache.size=CACHE_TOTAL;
+ block->cache.next=0; //Last block in the list
+ }
+ /* Setup the default blocks for block linkage returns */
+ cache.pos=&cache_code_link_blocks[0];
+ link_blocks[0].cache.start=cache.pos;
+ gen_return(BR_Link1);
+ cache.pos=&cache_code_link_blocks[32];
+ link_blocks[1].cache.start=cache.pos;
+ gen_return(BR_Link2);
+ cache.free_pages=0;
+ cache.last_page=0;
+ cache.used_pages=0;
+ /* Setup the code pages */
+ for (i=0;i<CACHE_PAGES;i++) {
+ CodePageHandler * newpage=new CodePageHandler();
+ newpage->next=cache.free_pages;
+ cache.free_pages=newpage;
+ }
+ }
+}
+
+static void cache_close(void) {
+/* for (;;) {
+ if (cache.used_pages) {
+ CodePageHandler * cpage=cache.used_pages;
+ CodePageHandler * npage=cache.used_pages->next;
+ cpage->ClearRelease();
+ delete cpage;
+ cache.used_pages=npage;
+ } else break;
+ }
+ if (cache_blocks != NULL) {
+ free(cache_blocks);
+ cache_blocks = NULL;
+ }
+ if (cache_code_start_ptr != NULL) {
+ ### care: under windows VirtualFree() has to be used if
+ ### VirtualAlloc was used for memory allocation
+ free(cache_code_start_ptr);
+ cache_code_start_ptr = NULL;
+ }
+ cache_code = NULL;
+ cache_code_link_blocks = NULL;
+ cache_initialized = false; */
+}
+
+static void cache_reset(void) {
+ if (cache_initialized) {
+ for (;;) {
+ if (cache.used_pages) {
+ CodePageHandler * cpage=cache.used_pages;
+ CodePageHandler * npage=cache.used_pages->next;
+ cpage->ClearRelease();
+ delete cpage;
+ cache.used_pages=npage;
+ } else break;
+ }
+
+ if (cache_blocks == NULL) {
+ cache_blocks=(CacheBlock*)malloc(CACHE_BLOCKS*sizeof(CacheBlock));
+ if(!cache_blocks) E_Exit("Allocating cache_blocks has failed");
+ }
+ memset(cache_blocks,0,sizeof(CacheBlock)*CACHE_BLOCKS);
+ cache.block.free=&cache_blocks[0];
+ for (Bits i=0;i<CACHE_BLOCKS-1;i++) {
+ cache_blocks[i].link[0].to=(CacheBlock *)1;
+ cache_blocks[i].link[1].to=(CacheBlock *)1;
+ cache_blocks[i].cache.next=&cache_blocks[i+1];
+ }
+
+ if (cache_code_start_ptr==NULL) {
+#if defined (WIN32)
+ cache_code_start_ptr=(Bit8u*)VirtualAlloc(0,CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP,
+ MEM_COMMIT,PAGE_EXECUTE_READWRITE);
+ if (!cache_code_start_ptr)
+ cache_code_start_ptr=(Bit8u*)malloc(CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP);
+#else
+ cache_code_start_ptr=(Bit8u*)malloc(CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP);
+#endif
+ if (!cache_code_start_ptr) E_Exit("Allocating dynamic core cache memory failed");
+
+ cache_code=(Bit8u*)(((Bitu)cache_code_start_ptr + PAGESIZE_TEMP-1) & ~(PAGESIZE_TEMP-1)); //Bitu is same size as a pointer.
+
+ cache_code_link_blocks=cache_code;
+ cache_code+=PAGESIZE_TEMP;
+
+#if (C_HAVE_MPROTECT)
+ if(mprotect(cache_code_link_blocks,CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP,PROT_WRITE|PROT_READ|PROT_EXEC))
+ LOG_MSG("Setting execute permission on the code cache has failed!");
+#endif
+ }
+
+ CacheBlock * block=cache_getblock();
+ cache.block.first=block;
+ cache.block.active=block;
+ block->cache.start=&cache_code[0];
+ block->cache.size=CACHE_TOTAL;
+ block->cache.next=0; //Last block in the list
+
+ /* Setup the default blocks for block linkage returns */
+ cache.pos=&cache_code_link_blocks[0];
+ link_blocks[0].cache.start=cache.pos;
+ gen_return(BR_Link1);
+ cache.pos=&cache_code_link_blocks[32];
+ link_blocks[1].cache.start=cache.pos;
+ gen_return(BR_Link2);
+ cache.free_pages=0;
+ cache.last_page=0;
+ cache.used_pages=0;
+ /* Setup the code pages */
+ for (Bitu i=0;i<CACHE_PAGES;i++) {
+ CodePageHandler * newpage=new CodePageHandler();
+ newpage->next=cache.free_pages;
+ cache.free_pages=newpage;
+ }
+ }
+}
diff --git a/src/cpu/core_dyn_x86/decoder.h b/src/cpu/core_dyn_x86/decoder.h
new file mode 100644
index 000000000..416f10b24
--- /dev/null
+++ b/src/cpu/core_dyn_x86/decoder.h
@@ -0,0 +1,2722 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#define X86_DYNFPU_DH_ENABLED
+#define X86_INLINED_MEMACCESS
+
+
+enum REP_Type {
+ REP_NONE=0,REP_NZ,REP_Z
+};
+
+static struct DynDecode {
+ PhysPt code;
+ PhysPt code_start;
+ PhysPt op_start;
+ bool big_op;
+ bool big_addr;
+ REP_Type rep;
+ Bitu cycles;
+ CacheBlock * block;
+ CacheBlock * active_block;
+ struct {
+ CodePageHandler * code;
+ Bitu index;
+ Bit8u * wmap;
+ Bit8u * invmap;
+ Bitu first;
+ } page;
+ struct {
+ Bitu val;
+ Bitu mod;
+ Bitu rm;
+ Bitu reg;
+ } modrm;
+ DynReg * segprefix;
+} decode;
+
+static bool MakeCodePage(Bitu lin_addr,CodePageHandler * &cph) {
+ Bit8u rdval;
+ const Bitu cflag = cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16;
+ //Ensure page contains memory:
+ if (GCC_UNLIKELY(mem_readb_checked(lin_addr,&rdval))) return true;
+ PageHandler * handler=get_tlb_readhandler(lin_addr);
+ if (handler->flags & PFLAG_HASCODE) {
+ cph=( CodePageHandler *)handler;
+ if (handler->flags & cflag) return false;
+ cph->ClearRelease();
+ cph=0;
+ handler=get_tlb_readhandler(lin_addr);
+ }
+ if (handler->flags & PFLAG_NOCODE) {
+ if (PAGING_ForcePageInit(lin_addr)) {
+ handler=get_tlb_readhandler(lin_addr);
+ if (handler->flags & PFLAG_HASCODE) {
+ cph=( CodePageHandler *)handler;
+ if (handler->flags & cflag) return false;
+ cph->ClearRelease();
+ cph=0;
+ handler=get_tlb_readhandler(lin_addr);
+ }
+ }
+ if (handler->flags & PFLAG_NOCODE) {
+ LOG_MSG("DYNX86:Can't run code in this page!");
+ cph=0; return false;
+ }
+ }
+ Bitu lin_page=lin_addr >> 12;
+ Bitu phys_page=lin_page;
+ if (!PAGING_MakePhysPage(phys_page)) {
+ LOG_MSG("DYNX86:Can't find physpage");
+ cph=0; return false;
+ }
+ /* Find a free CodePage */
+ if (!cache.free_pages) {
+ if (cache.used_pages!=decode.page.code) cache.used_pages->ClearRelease();
+ else {
+ if ((cache.used_pages->next) && (cache.used_pages->next!=decode.page.code))
+ cache.used_pages->next->ClearRelease();
+ else {
+ LOG_MSG("DYNX86:Invalid cache links");
+ cache.used_pages->ClearRelease();
+ }
+ }
+ }
+ CodePageHandler * cpagehandler=cache.free_pages;
+ cache.free_pages=cache.free_pages->next;
+ cpagehandler->prev=cache.last_page;
+ cpagehandler->next=0;
+ if (cache.last_page) cache.last_page->next=cpagehandler;
+ cache.last_page=cpagehandler;
+ if (!cache.used_pages) cache.used_pages=cpagehandler;
+ cpagehandler->SetupAt(phys_page,handler);
+ MEM_SetPageHandler(phys_page,1,cpagehandler);
+ PAGING_UnlinkPages(lin_page,1);
+ cph=cpagehandler;
+ return false;
+}
+
+static Bit8u decode_fetchb(void) {
+ if (GCC_UNLIKELY(decode.page.index>=4096)) {
+ /* Advance to the next page */
+ decode.active_block->page.end=4095;
+ /* trigger possible page fault here */
+ decode.page.first++;
+ Bitu fetchaddr=decode.page.first << 12;
+ mem_readb(fetchaddr);
+ MakeCodePage(fetchaddr,decode.page.code);
+ CacheBlock * newblock=cache_getblock();
+ decode.active_block->crossblock=newblock;
+ newblock->crossblock=decode.active_block;
+ decode.active_block=newblock;
+ decode.active_block->page.start=0;
+ decode.page.code->AddCrossBlock(decode.active_block);
+ decode.page.wmap=decode.page.code->write_map;
+ decode.page.invmap=decode.page.code->invalidation_map;
+ decode.page.index=0;
+ }
+ decode.page.wmap[decode.page.index]+=0x01;
+ decode.page.index++;
+ decode.code+=1;
+ return mem_readb(decode.code-1);
+}
+static Bit16u decode_fetchw(void) {
+ if (GCC_UNLIKELY(decode.page.index>=4095)) {
+ Bit16u val=decode_fetchb();
+ val|=decode_fetchb() << 8;
+ return val;
+ }
+ *(Bit16u *)&decode.page.wmap[decode.page.index]+=0x0101;
+ decode.code+=2;decode.page.index+=2;
+ return mem_readw(decode.code-2);
+}
+static Bit32u decode_fetchd(void) {
+ if (GCC_UNLIKELY(decode.page.index>=4093)) {
+ Bit32u val=decode_fetchb();
+ val|=decode_fetchb() << 8;
+ val|=decode_fetchb() << 16;
+ val|=decode_fetchb() << 24;
+ return val;
+ /* Advance to the next page */
+ }
+ *(Bit32u *)&decode.page.wmap[decode.page.index]+=0x01010101;
+ decode.code+=4;decode.page.index+=4;
+ return mem_readd(decode.code-4);
+}
+
+#define START_WMMEM 64
+
+static INLINE void decode_increase_wmapmask(Bitu size) {
+ Bitu mapidx;
+ CacheBlock* activecb=decode.active_block;
+ if (GCC_UNLIKELY(!activecb->cache.wmapmask)) {
+ activecb->cache.wmapmask=(Bit8u*)malloc(START_WMMEM);
+ memset(activecb->cache.wmapmask,0,START_WMMEM);
+ activecb->cache.maskstart=decode.page.index;
+ activecb->cache.masklen=START_WMMEM;
+ mapidx=0;
+ } else {
+ mapidx=decode.page.index-activecb->cache.maskstart;
+ if (GCC_UNLIKELY(mapidx+size>=activecb->cache.masklen)) {
+ Bitu newmasklen=activecb->cache.masklen*4;
+ if (newmasklen<mapidx+size) newmasklen=((mapidx+size)&~3)*2;
+ Bit8u* tempmem=(Bit8u*)malloc(newmasklen);
+ memset(tempmem,0,newmasklen);
+ memcpy(tempmem,activecb->cache.wmapmask,activecb->cache.masklen);
+ free(activecb->cache.wmapmask);
+ activecb->cache.wmapmask=tempmem;
+ activecb->cache.masklen=newmasklen;
+ }
+ }
+ switch (size) {
+ case 1 : activecb->cache.wmapmask[mapidx]+=0x01; break;
+ case 2 : (*(Bit16u*)&activecb->cache.wmapmask[mapidx])+=0x0101; break;
+ case 4 : (*(Bit32u*)&activecb->cache.wmapmask[mapidx])+=0x01010101; break;
+ }
+}
+
+static bool decode_fetchb_imm(Bitu & val) {
+ if (decode.page.index<4096) {
+ if (decode.page.invmap != NULL) {
+ if (decode.page.invmap[decode.page.index] == 0) {
+ val=(Bit32u)decode_fetchb();
+ return false;
+ }
+ HostPt tlb_addr=get_tlb_read(decode.code);
+ if (tlb_addr) {
+ val=(Bitu)(tlb_addr+decode.code);
+ decode_increase_wmapmask(1);
+ decode.code++;
+ decode.page.index++;
+ return true;
+ }
+ }
+ }
+ val=(Bit32u)decode_fetchb();
+ return false;
+}
+static bool decode_fetchw_imm(Bitu & val) {
+ if (decode.page.index<4095) {
+ if (decode.page.invmap != NULL) {
+ if ((decode.page.invmap[decode.page.index] == 0) &&
+ (decode.page.invmap[decode.page.index + 1] == 0)
+ ) {
+ val=decode_fetchw();
+ return false;
+ }
+ HostPt tlb_addr=get_tlb_read(decode.code);
+ if (tlb_addr) {
+ val=(Bitu)(tlb_addr+decode.code);
+ decode_increase_wmapmask(2);
+ decode.code+=2;
+ decode.page.index+=2;
+ return true;
+ }
+ }
+ }
+ val=decode_fetchw();
+ return false;
+}
+static bool decode_fetchd_imm(Bitu & val) {
+ if (decode.page.index<4093) {
+ if (decode.page.invmap != NULL) {
+ if ((decode.page.invmap[decode.page.index] == 0) &&
+ (decode.page.invmap[decode.page.index + 1] == 0) &&
+ (decode.page.invmap[decode.page.index + 2] == 0) &&
+ (decode.page.invmap[decode.page.index + 3] == 0)
+ ) {
+ val=decode_fetchd();
+ return false;
+ }
+ HostPt tlb_addr=get_tlb_read(decode.code);
+ if (tlb_addr) {
+ val=(Bitu)(tlb_addr+decode.code);
+ decode_increase_wmapmask(4);
+ decode.code+=4;
+ decode.page.index+=4;
+ return true;
+ }
+ }
+ }
+ val=decode_fetchd();
+ return false;
+}
+
+
+static void dyn_reduce_cycles(void) {
+ gen_protectflags();
+ if (!decode.cycles) decode.cycles++;
+ gen_dop_word_imm(DOP_SUB,true,DREG(CYCLES),decode.cycles);
+}
+
+static void dyn_save_noncritical_regs(void) {
+ gen_releasereg(DREG(EAX));
+ gen_releasereg(DREG(ECX));
+ gen_releasereg(DREG(EDX));
+ gen_releasereg(DREG(EBX));
+ gen_releasereg(DREG(ESP));
+ gen_releasereg(DREG(EBP));
+ gen_releasereg(DREG(ESI));
+ gen_releasereg(DREG(EDI));
+}
+
+static void dyn_save_critical_regs(void) {
+ dyn_save_noncritical_regs();
+ gen_releasereg(DREG(FLAGS));
+ gen_releasereg(DREG(EIP));
+ gen_releasereg(DREG(CYCLES));
+}
+
+static void dyn_set_eip_last_end(DynReg * endreg) {
+ gen_protectflags();
+ gen_lea(endreg,DREG(EIP),0,0,decode.code-decode.code_start);
+ gen_dop_word_imm(DOP_ADD,decode.big_op,DREG(EIP),decode.op_start-decode.code_start);
+}
+
+static INLINE void dyn_set_eip_end(void) {
+ gen_protectflags();
+ gen_dop_word_imm(DOP_ADD,cpu.code.big,DREG(EIP),decode.code-decode.code_start);
+}
+
+static INLINE void dyn_set_eip_end(DynReg * endreg) {
+ gen_protectflags();
+ if (cpu.code.big) gen_dop_word(DOP_MOV,true,DREG(TMPW),DREG(EIP));
+ else gen_extend_word(false,DREG(TMPW),DREG(EIP));
+ gen_dop_word_imm(DOP_ADD,cpu.code.big,DREG(TMPW),decode.code-decode.code_start);
+}
+
+static INLINE void dyn_set_eip_last(void) {
+ gen_protectflags();
+ gen_dop_word_imm(DOP_ADD,cpu.code.big,DREG(EIP),decode.op_start-decode.code_start);
+}
+
+
+enum save_info_type {db_exception, cycle_check, normal, fpu_restore};
+
+
+static struct {
+ save_info_type type;
+ DynState state;
+ Bit8u * branch_pos;
+ Bit32u eip_change;
+ Bitu cycles;
+ Bit8u * return_pos;
+} save_info[512];
+
+Bitu used_save_info=0;
+
+
+static BlockReturn DynRunException(Bit32u eip_add,Bit32u cycle_sub,Bit32u dflags) {
+ reg_flags=(dflags&FMASK_TEST) | (reg_flags&(~FMASK_TEST));
+ reg_eip+=eip_add;
+ CPU_Cycles-=cycle_sub;
+ if (cpu.exception.which==SMC_CURRENT_BLOCK) return BR_SMCBlock;
+ CPU_Exception(cpu.exception.which,cpu.exception.error);
+ return BR_Normal;
+}
+
+static void dyn_check_bool_exception(DynReg * check) {
+ gen_dop_byte(DOP_OR,check,0,check,0);
+ save_info[used_save_info].branch_pos=gen_create_branch_long(BR_NZ);
+ dyn_savestate(&save_info[used_save_info].state);
+ if (!decode.cycles) decode.cycles++;
+ save_info[used_save_info].cycles=decode.cycles;
+ save_info[used_save_info].eip_change=decode.op_start-decode.code_start;
+ if (!cpu.code.big) save_info[used_save_info].eip_change&=0xffff;
+ save_info[used_save_info].type=db_exception;
+ used_save_info++;
+}
+
+static void dyn_check_bool_exception_al(void) {
+ cache_addw(0xc00a); // or al, al
+ save_info[used_save_info].branch_pos=gen_create_branch_long(BR_NZ);
+ dyn_savestate(&save_info[used_save_info].state);
+ if (!decode.cycles) decode.cycles++;
+ save_info[used_save_info].cycles=decode.cycles;
+ save_info[used_save_info].eip_change=decode.op_start-decode.code_start;
+ if (!cpu.code.big) save_info[used_save_info].eip_change&=0xffff;
+ save_info[used_save_info].type=db_exception;
+ used_save_info++;
+}
+
+#include "pic.h"
+
+static void dyn_check_irqrequest(void) {
+ gen_load_host(&PIC_IRQCheck,DREG(TMPB),4);
+ gen_dop_word(DOP_OR,true,DREG(TMPB),DREG(TMPB));
+ save_info[used_save_info].branch_pos=gen_create_branch_long(BR_NZ);
+ gen_releasereg(DREG(TMPB));
+ dyn_savestate(&save_info[used_save_info].state);
+ if (!decode.cycles) decode.cycles++;
+ save_info[used_save_info].cycles=decode.cycles;
+ save_info[used_save_info].eip_change=decode.code-decode.code_start;
+ if (!cpu.code.big) save_info[used_save_info].eip_change&=0xffff;
+ save_info[used_save_info].type=normal;
+ used_save_info++;
+}
+
+static void dyn_check_bool_exception_ne(void) {
+ save_info[used_save_info].branch_pos=gen_create_branch_long(BR_Z);
+ dyn_savestate(&save_info[used_save_info].state);
+ if (!decode.cycles) decode.cycles++;
+ save_info[used_save_info].cycles=decode.cycles;
+ save_info[used_save_info].eip_change=decode.op_start-decode.code_start;
+ if (!cpu.code.big) save_info[used_save_info].eip_change&=0xffff;
+ save_info[used_save_info].type=db_exception;
+ used_save_info++;
+}
+
+static void dyn_fill_blocks(void) {
+ for (Bitu sct=0; sct<used_save_info; sct++) {
+ gen_fill_branch_long(save_info[sct].branch_pos);
+ switch (save_info[sct].type) {
+ case db_exception:
+ dyn_loadstate(&save_info[sct].state);
+ decode.cycles=save_info[sct].cycles;
+ dyn_save_critical_regs();
+ if (cpu.code.big) gen_call_function((void *)&DynRunException,"%Id%Id%F",save_info[sct].eip_change,save_info[sct].cycles);
+ else gen_call_function((void *)&DynRunException,"%Iw%Id%F",save_info[sct].eip_change,save_info[sct].cycles);
+ gen_return_fast(BR_Normal,true);
+ break;
+ case cycle_check:
+ gen_return(BR_Cycles);
+ break;
+ case normal:
+ dyn_loadstate(&save_info[sct].state);
+ gen_dop_word_imm(DOP_ADD,decode.big_op,DREG(EIP),save_info[sct].eip_change);
+ dyn_save_critical_regs();
+ gen_return(BR_Cycles);
+ break;
+ case fpu_restore:
+ dyn_loadstate(&save_info[sct].state);
+ gen_load_host(&dyn_dh_fpu.state_used,DREG(TMPB),4);
+ gen_sop_word(SOP_INC,true,DREG(TMPB));
+ GenReg * gr1=FindDynReg(DREG(TMPB));
+ cache_addb(0xdd); // FRSTOR fpu.state (fpu_restore)
+ cache_addb(0x25);
+ cache_addd((Bit32u)(&(dyn_dh_fpu.state[0])));
+ cache_addb(0x89); // mov fpu.state_used,1
+ cache_addb(0x05|(gr1->index<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.state_used)));
+ gen_releasereg(DREG(TMPB));
+ dyn_synchstate(&save_info[sct].state);
+ gen_create_jump(save_info[sct].return_pos);
+ break;
+ }
+ }
+ used_save_info=0;
+}
+
+
+#if !defined(X86_INLINED_MEMACCESS)
+static void dyn_read_byte(DynReg * addr,DynReg * dst,Bitu high) {
+ gen_protectflags();
+ gen_call_function((void *)&mem_readb_checked,"%Dd%Id",addr,&core_dyn.readdata);
+ dyn_check_bool_exception_al();
+ gen_mov_host(&core_dyn.readdata,dst,1,high);
+}
+static void dyn_write_byte(DynReg * addr,DynReg * val,Bitu high) {
+ gen_protectflags();
+ if (high) gen_call_function((void *)&mem_writeb_checked,"%Dd%Dh",addr,val);
+ else gen_call_function((void *)&mem_writeb_checked,"%Dd%Dd",addr,val);
+ dyn_check_bool_exception_al();
+}
+static void dyn_read_word(DynReg * addr,DynReg * dst,bool dword) {
+ gen_protectflags();
+ if (dword) gen_call_function((void *)&mem_readd_checked,"%Dd%Id",addr,&core_dyn.readdata);
+ else gen_call_function((void *)&mem_readw_checked,"%Dd%Id",addr,&core_dyn.readdata);
+ dyn_check_bool_exception_al();
+ gen_mov_host(&core_dyn.readdata,dst,dword?4:2);
+}
+static void dyn_write_word(DynReg * addr,DynReg * val,bool dword) {
+ gen_protectflags();
+ if (dword) gen_call_function((void *)&mem_writed_checked,"%Dd%Dd",addr,val);
+ else gen_call_function((void *)&mem_writew_checked,"%Dd%Dd",addr,val);
+ dyn_check_bool_exception_al();
+}
+static void dyn_read_byte_release(DynReg * addr,DynReg * dst,Bitu high) {
+ gen_protectflags();
+ gen_call_function((void *)&mem_readb_checked,"%Ddr%Id",addr,&core_dyn.readdata);
+ dyn_check_bool_exception_al();
+ gen_mov_host(&core_dyn.readdata,dst,1,high);
+}
+static void dyn_write_byte_release(DynReg * addr,DynReg * val,Bitu high) {
+ gen_protectflags();
+ if (high) gen_call_function((void *)&mem_writeb_checked,"%Ddr%Dh",addr,val);
+ else gen_call_function((void *)&mem_writeb_checked,"%Ddr%Dd",addr,val);
+ dyn_check_bool_exception_al();
+}
+static void dyn_read_word_release(DynReg * addr,DynReg * dst,bool dword) {
+ gen_protectflags();
+ if (dword) gen_call_function((void *)&mem_readd_checked,"%Ddr%Id",addr,&core_dyn.readdata);
+ else gen_call_function((void *)&mem_readw_checked,"%Ddr%Id",addr,&core_dyn.readdata);
+ dyn_check_bool_exception_al();
+ gen_mov_host(&core_dyn.readdata,dst,dword?4:2);
+}
+static void dyn_write_word_release(DynReg * addr,DynReg * val,bool dword) {
+ gen_protectflags();
+ if (dword) gen_call_function((void *)&mem_writed_checked,"%Ddr%Dd",addr,val);
+ else gen_call_function((void *)&mem_writew_checked,"%Ddr%Dd",addr,val);
+ dyn_check_bool_exception_al();
+}
+
+#else
+
+static void dyn_read_intro(DynReg * addr,bool release_addr=true) {
+ gen_protectflags();
+
+ if (addr->genreg) {
+ // addr already in a register
+ Bit8u reg_idx=(Bit8u)addr->genreg->index;
+ x86gen.regs[X86_REG_ECX]->Clear();
+ if (reg_idx!=1) {
+ cache_addw(0xc88b+(reg_idx<<8)); //Mov ecx,reg
+ }
+ x86gen.regs[X86_REG_EAX]->Clear();
+ if (release_addr) gen_releasereg(addr);
+ } else {
+ // addr still in memory, directly move into ecx
+ x86gen.regs[X86_REG_EAX]->Clear();
+ x86gen.regs[X86_REG_ECX]->Clear();
+ cache_addw(0x0d8b); //Mov ecx,[data]
+ cache_addd((Bit32u)addr->data);
+ }
+ x86gen.regs[X86_REG_EDX]->Clear();
+
+ cache_addw(0xc18b); // mov eax,ecx
+}
+
+bool mem_readb_checked_dcx86(PhysPt address) {
+ return get_tlb_readhandler(address)->readb_checked(address, (Bit8u*)(&core_dyn.readdata));
+}
+
+static void dyn_read_byte(DynReg * addr,DynReg * dst,Bitu high) {
+ dyn_read_intro(addr,false);
+
+ cache_addw(0xe8c1); // shr eax,0x0c
+ cache_addb(0x0c);
+ cache_addw(0x048b); // mov eax,paging.tlb.read[eax*TYPE Bit32u]
+ cache_addb(0x85);
+ cache_addd((Bit32u)(&paging.tlb.read[0]));
+ cache_addw(0xc085); // test eax,eax
+ Bit8u* je_loc=gen_create_branch(BR_Z);
+
+
+ cache_addw(0x048a); // mov al,[eax+ecx]
+ cache_addb(0x08);
+
+ Bit8u* jmp_loc=gen_create_jump();
+ gen_fill_branch(je_loc);
+ cache_addb(0x51); // push ecx
+ cache_addb(0xe8);
+ cache_addd(((Bit32u)&mem_readb_checked_dcx86) - (Bit32u)cache.pos-4);
+ cache_addw(0xc483); // add esp,4
+ cache_addb(0x04);
+ cache_addw(0x012c); // sub al,1
+
+ dyn_check_bool_exception_ne();
+
+ cache_addw(0x058a); //mov al,[]
+ cache_addd((Bit32u)(&core_dyn.readdata));
+
+ gen_fill_jump(jmp_loc);
+
+ x86gen.regs[X86_REG_EAX]->notusable=true;
+ GenReg * genreg=FindDynReg(dst);
+ x86gen.regs[X86_REG_EAX]->notusable=false;
+ cache_addw(0xc08a+(genreg->index<<11)+(high?0x2000:0));
+ dst->flags|=DYNFLG_CHANGED;
+}
+
+static void dyn_read_byte_release(DynReg * addr,DynReg * dst,Bitu high) {
+ dyn_read_intro(addr);
+
+ cache_addw(0xe8c1); // shr eax,0x0c
+ cache_addb(0x0c);
+ cache_addw(0x048b); // mov eax,paging.tlb.read[eax*TYPE Bit32u]
+ cache_addb(0x85);
+ cache_addd((Bit32u)(&paging.tlb.read[0]));
+ cache_addw(0xc085); // test eax,eax
+ Bit8u* je_loc=gen_create_branch(BR_Z);
+
+
+ cache_addw(0x048a); // mov al,[eax+ecx]
+ cache_addb(0x08);
+
+ Bit8u* jmp_loc=gen_create_jump();
+ gen_fill_branch(je_loc);
+ cache_addb(0x51); // push ecx
+ cache_addb(0xe8);
+ cache_addd(((Bit32u)&mem_readb_checked_dcx86) - (Bit32u)cache.pos-4);
+ cache_addw(0xc483); // add esp,4
+ cache_addb(0x04);
+ cache_addw(0x012c); // sub al,1
+
+ dyn_check_bool_exception_ne();
+
+ cache_addw(0x058a); //mov al,[]
+ cache_addd((Bit32u)(&core_dyn.readdata));
+
+ gen_fill_jump(jmp_loc);
+
+ x86gen.regs[X86_REG_EAX]->notusable=true;
+ GenReg * genreg=FindDynReg(dst);
+ x86gen.regs[X86_REG_EAX]->notusable=false;
+ cache_addw(0xc08a+(genreg->index<<11)+(high?0x2000:0));
+ dst->flags|=DYNFLG_CHANGED;
+}
+
+bool mem_readd_checked_dcx86(PhysPt address) {
+ if ((address & 0xfff)<0xffd) {
+ HostPt tlb_addr=get_tlb_read(address);
+ if (tlb_addr) {
+ core_dyn.readdata=host_readd(tlb_addr+address);
+ return false;
+ } else {
+ return get_tlb_readhandler(address)->readd_checked(address, &core_dyn.readdata);
+ }
+ } else return mem_unalignedreadd_checked(address, &core_dyn.readdata);
+}
+
+static void dyn_read_word(DynReg * addr,DynReg * dst,bool dword) {
+ if (dword) {
+ dyn_read_intro(addr,false);
+
+ cache_addw(0xe8d1); // shr eax,0x1
+ Bit8u* jb_loc1=gen_create_branch(BR_B);
+ cache_addw(0xe8d1); // shr eax,0x1
+ Bit8u* jb_loc2=gen_create_branch(BR_B);
+ cache_addw(0xe8c1); // shr eax,0x0a
+ cache_addb(0x0a);
+ cache_addw(0x048b); // mov eax,paging.tlb.read[eax*TYPE Bit32u]
+ cache_addb(0x85);
+ cache_addd((Bit32u)(&paging.tlb.read[0]));
+ cache_addw(0xc085); // test eax,eax
+ Bit8u* je_loc=gen_create_branch(BR_Z);
+
+ GenReg * genreg=FindDynReg(dst,true);
+
+ cache_addw(0x048b+(genreg->index <<(8+3))); // mov dest,[eax+ecx]
+ cache_addb(0x08);
+
+ Bit8u* jmp_loc=gen_create_jump();
+ gen_fill_branch(jb_loc1);
+ gen_fill_branch(jb_loc2);
+ gen_fill_branch(je_loc);
+ cache_addb(0x51); // push ecx
+ cache_addb(0xe8);
+ cache_addd(((Bit32u)&mem_readd_checked_dcx86) - (Bit32u)cache.pos-4);
+ cache_addw(0xc483); // add esp,4
+ cache_addb(0x04);
+ cache_addw(0x012c); // sub al,1
+
+ dyn_check_bool_exception_ne();
+
+ gen_mov_host(&core_dyn.readdata,dst,4);
+ dst->flags|=DYNFLG_CHANGED;
+
+ gen_fill_jump(jmp_loc);
+ } else {
+ gen_protectflags();
+ gen_call_function((void *)&mem_readw_checked,"%Dd%Id",addr,&core_dyn.readdata);
+ dyn_check_bool_exception_al();
+ gen_mov_host(&core_dyn.readdata,dst,2);
+ }
+}
+
+static void dyn_read_word_release(DynReg * addr,DynReg * dst,bool dword) {
+ if (dword) {
+ dyn_read_intro(addr);
+
+ cache_addw(0xe8d1); // shr eax,0x1
+ Bit8u* jb_loc1=gen_create_branch(BR_B);
+ cache_addw(0xe8d1); // shr eax,0x1
+ Bit8u* jb_loc2=gen_create_branch(BR_B);
+ cache_addw(0xe8c1); // shr eax,0x0a
+ cache_addb(0x0a);
+ cache_addw(0x048b); // mov eax,paging.tlb.read[eax*TYPE Bit32u]
+ cache_addb(0x85);
+ cache_addd((Bit32u)(&paging.tlb.read[0]));
+ cache_addw(0xc085); // test eax,eax
+ Bit8u* je_loc=gen_create_branch(BR_Z);
+
+ GenReg * genreg=FindDynReg(dst,true);
+
+ cache_addw(0x048b+(genreg->index <<(8+3))); // mov dest,[eax+ecx]
+ cache_addb(0x08);
+
+ Bit8u* jmp_loc=gen_create_jump();
+ gen_fill_branch(jb_loc1);
+ gen_fill_branch(jb_loc2);
+ gen_fill_branch(je_loc);
+ cache_addb(0x51); // push ecx
+ cache_addb(0xe8);
+ cache_addd(((Bit32u)&mem_readd_checked_dcx86) - (Bit32u)cache.pos-4);
+ cache_addw(0xc483); // add esp,4
+ cache_addb(0x04);
+ cache_addw(0x012c); // sub al,1
+
+ dyn_check_bool_exception_ne();
+
+ gen_mov_host(&core_dyn.readdata,dst,4);
+ dst->flags|=DYNFLG_CHANGED;
+
+ gen_fill_jump(jmp_loc);
+ } else {
+ gen_protectflags();
+ gen_call_function((void *)&mem_readw_checked,"%Ddr%Id",addr,&core_dyn.readdata);
+ dyn_check_bool_exception_al();
+ gen_mov_host(&core_dyn.readdata,dst,2);
+ }
+}
+
+static void dyn_write_intro(DynReg * addr,bool release_addr=true) {
+ gen_protectflags();
+
+ if (addr->genreg) {
+ // addr in a register
+ Bit8u reg_idx_addr=(Bit8u)addr->genreg->index;
+
+ x86gen.regs[X86_REG_EAX]->Clear();
+ x86gen.regs[X86_REG_EAX]->notusable=true;
+ x86gen.regs[X86_REG_ECX]->Clear();
+ x86gen.regs[X86_REG_ECX]->notusable=true;
+
+ if (reg_idx_addr) {
+ // addr!=eax
+ cache_addb(0x8b); //Mov eax,reg
+ cache_addb(0xc0+reg_idx_addr);
+ }
+ if (release_addr) gen_releasereg(addr);
+ } else {
+ // addr still in memory, directly move into eax
+ x86gen.regs[X86_REG_EAX]->Clear();
+ x86gen.regs[X86_REG_EAX]->notusable=true;
+ x86gen.regs[X86_REG_ECX]->Clear();
+ x86gen.regs[X86_REG_ECX]->notusable=true;
+ cache_addb(0xa1); //Mov eax,[data]
+ cache_addd((Bit32u)addr->data);
+ }
+
+ cache_addw(0xc88b); // mov ecx,eax
+}
+
+static void dyn_write_byte(DynReg * addr,DynReg * val,bool high) {
+ dyn_write_intro(addr,false);
+
+ GenReg * genreg=FindDynReg(val);
+ cache_addw(0xe9c1); // shr ecx,0x0c
+ cache_addb(0x0c);
+ cache_addw(0x0c8b); // mov ecx,paging.tlb.read[ecx*TYPE Bit32u]
+ cache_addb(0x8d);
+ cache_addd((Bit32u)(&paging.tlb.write[0]));
+ cache_addw(0xc985); // test ecx,ecx
+ Bit8u* je_loc=gen_create_branch(BR_Z);
+
+ cache_addw(0x0488+(genreg->index<<11)+(high?0x2000:0)); // mov [eax+ecx],reg
+ cache_addb(0x08);
+
+ Bit8u* jmp_loc=gen_create_jump();
+ gen_fill_branch(je_loc);
+
+ if (GCC_UNLIKELY(high)) cache_addw(0xe086+((genreg->index+(genreg->index<<3))<<8));
+ cache_addb(0x52); // push edx
+ cache_addb(0x50+genreg->index);
+ cache_addb(0x50); // push eax
+ if (GCC_UNLIKELY(high)) cache_addw(0xe086+((genreg->index+(genreg->index<<3))<<8));
+ cache_addb(0xe8);
+ cache_addd(((Bit32u)&mem_writeb_checked) - (Bit32u)cache.pos-4);
+ cache_addw(0xc483); // add esp,8
+ cache_addb(0x08);
+ cache_addw(0x012c); // sub al,1
+ cache_addb(0x5a); // pop edx
+
+ // Restore registers to be used again
+ x86gen.regs[X86_REG_EAX]->notusable=false;
+ x86gen.regs[X86_REG_ECX]->notusable=false;
+
+ dyn_check_bool_exception_ne();
+
+ gen_fill_jump(jmp_loc);
+}
+
+static void dyn_write_byte_release(DynReg * addr,DynReg * val,bool high) {
+ dyn_write_intro(addr);
+
+ GenReg * genreg=FindDynReg(val);
+ cache_addw(0xe9c1); // shr ecx,0x0c
+ cache_addb(0x0c);
+ cache_addw(0x0c8b); // mov ecx,paging.tlb.read[ecx*TYPE Bit32u]
+ cache_addb(0x8d);
+ cache_addd((Bit32u)(&paging.tlb.write[0]));
+ cache_addw(0xc985); // test ecx,ecx
+ Bit8u* je_loc=gen_create_branch(BR_Z);
+
+ cache_addw(0x0488+(genreg->index<<11)+(high?0x2000:0)); // mov [eax+ecx],reg
+ cache_addb(0x08);
+
+ Bit8u* jmp_loc=gen_create_jump();
+ gen_fill_branch(je_loc);
+
+ cache_addb(0x52); // push edx
+ if (GCC_UNLIKELY(high)) cache_addw(0xe086+((genreg->index+(genreg->index<<3))<<8));
+ cache_addb(0x50+genreg->index);
+ cache_addb(0x50); // push eax
+ if (GCC_UNLIKELY(high)) cache_addw(0xe086+((genreg->index+(genreg->index<<3))<<8));
+ cache_addb(0xe8);
+ cache_addd(((Bit32u)&mem_writeb_checked) - (Bit32u)cache.pos-4);
+ cache_addw(0xc483); // add esp,8
+ cache_addb(0x08);
+ cache_addw(0x012c); // sub al,1
+ cache_addb(0x5a); // pop edx
+
+ // Restore registers to be used again
+ x86gen.regs[X86_REG_EAX]->notusable=false;
+ x86gen.regs[X86_REG_ECX]->notusable=false;
+
+ dyn_check_bool_exception_ne();
+
+ gen_fill_jump(jmp_loc);
+}
+
+static void dyn_write_word(DynReg * addr,DynReg * val,bool dword) {
+ if (dword) {
+ dyn_write_intro(addr,false);
+
+ GenReg * genreg=FindDynReg(val);
+ cache_addw(0xe9d1); // shr ecx,0x1
+ Bit8u* jb_loc1=gen_create_branch(BR_B);
+ cache_addw(0xe9d1); // shr ecx,0x1
+ Bit8u* jb_loc2=gen_create_branch(BR_B);
+ cache_addw(0xe9c1); // shr ecx,0x0a
+ cache_addb(0x0a);
+ cache_addw(0x0c8b); // mov ecx,paging.tlb.read[ecx*TYPE Bit32u]
+ cache_addb(0x8d);
+ cache_addd((Bit32u)(&paging.tlb.write[0]));
+ cache_addw(0xc985); // test ecx,ecx
+ Bit8u* je_loc=gen_create_branch(BR_Z);
+
+ cache_addw(0x0489+(genreg->index <<(8+3))); // mov [eax+ecx],reg
+ cache_addb(0x08);
+
+ Bit8u* jmp_loc=gen_create_jump();
+ gen_fill_branch(jb_loc1);
+ gen_fill_branch(jb_loc2);
+ gen_fill_branch(je_loc);
+
+ cache_addb(0x52); // push edx
+ cache_addb(0x50+genreg->index);
+ cache_addb(0x50); // push eax
+ cache_addb(0xe8);
+ cache_addd(((Bit32u)&mem_writed_checked) - (Bit32u)cache.pos-4);
+ cache_addw(0xc483); // add esp,8
+ cache_addb(0x08);
+ cache_addw(0x012c); // sub al,1
+ cache_addb(0x5a); // pop edx
+
+ // Restore registers to be used again
+ x86gen.regs[X86_REG_EAX]->notusable=false;
+ x86gen.regs[X86_REG_ECX]->notusable=false;
+
+ dyn_check_bool_exception_ne();
+
+ gen_fill_jump(jmp_loc);
+ } else {
+ gen_protectflags();
+ gen_call_function((void *)&mem_writew_checked,"%Dd%Dd",addr,val);
+ dyn_check_bool_exception_al();
+ }
+}
+
+static void dyn_write_word_release(DynReg * addr,DynReg * val,bool dword) {
+ if (dword) {
+ dyn_write_intro(addr);
+
+ GenReg * genreg=FindDynReg(val);
+ cache_addw(0xe9d1); // shr ecx,0x1
+ Bit8u* jb_loc1=gen_create_branch(BR_B);
+ cache_addw(0xe9d1); // shr ecx,0x1
+ Bit8u* jb_loc2=gen_create_branch(BR_B);
+ cache_addw(0xe9c1); // shr ecx,0x0a
+ cache_addb(0x0a);
+ cache_addw(0x0c8b); // mov ecx,paging.tlb.read[ecx*TYPE Bit32u]
+ cache_addb(0x8d);
+ cache_addd((Bit32u)(&paging.tlb.write[0]));
+ cache_addw(0xc985); // test ecx,ecx
+ Bit8u* je_loc=gen_create_branch(BR_Z);
+
+ cache_addw(0x0489+(genreg->index <<(8+3))); // mov [eax+ecx],reg
+ cache_addb(0x08);
+
+ Bit8u* jmp_loc=gen_create_jump();
+ gen_fill_branch(jb_loc1);
+ gen_fill_branch(jb_loc2);
+ gen_fill_branch(je_loc);
+
+ cache_addb(0x52); // push edx
+ cache_addb(0x50+genreg->index);
+ cache_addb(0x50); // push eax
+ cache_addb(0xe8);
+ cache_addd(((Bit32u)&mem_writed_checked) - (Bit32u)cache.pos-4);
+ cache_addw(0xc483); // add esp,8
+ cache_addb(0x08);
+ cache_addw(0x012c); // sub al,1
+ cache_addb(0x5a); // pop edx
+
+ // Restore registers to be used again
+ x86gen.regs[X86_REG_EAX]->notusable=false;
+ x86gen.regs[X86_REG_ECX]->notusable=false;
+
+ dyn_check_bool_exception_ne();
+
+ gen_fill_jump(jmp_loc);
+ } else {
+ gen_protectflags();
+ gen_call_function((void *)&mem_writew_checked,"%Ddr%Dd",addr,val);
+ dyn_check_bool_exception_al();
+ }
+}
+
+#endif
+
+
+static void dyn_push_unchecked(DynReg * dynreg) {
+ gen_protectflags();
+ gen_lea(DREG(STACK),DREG(ESP),0,0,decode.big_op?(-4):(-2));
+ gen_dop_word_var(DOP_AND,true,DREG(STACK),&cpu.stack.mask);
+ gen_dop_word_var(DOP_AND,true,DREG(ESP),&cpu.stack.notmask);
+ gen_dop_word(DOP_OR,true,DREG(ESP),DREG(STACK));
+ gen_dop_word(DOP_ADD,true,DREG(STACK),DREG(SS));
+ if (decode.big_op) {
+ gen_call_function((void *)&mem_writed,"%Drd%Dd",DREG(STACK),dynreg);
+ } else {
+ //Can just push the whole 32-bit word as operand
+ gen_call_function((void *)&mem_writew,"%Drd%Dd",DREG(STACK),dynreg);
+ }
+}
+
+static void dyn_push(DynReg * dynreg) {
+ gen_protectflags();
+ gen_lea(DREG(STACK),DREG(ESP),0,0,decode.big_op?(-4):(-2));
+ gen_dop_word(DOP_MOV,true,DREG(NEWESP),DREG(ESP));
+ gen_dop_word_var(DOP_AND,true,DREG(STACK),&cpu.stack.mask);
+ gen_dop_word_var(DOP_AND,true,DREG(NEWESP),&cpu.stack.notmask);
+ gen_dop_word(DOP_OR,true,DREG(NEWESP),DREG(STACK));
+ gen_dop_word(DOP_ADD,true,DREG(STACK),DREG(SS));
+ if (decode.big_op) {
+ gen_call_function((void *)&mem_writed_checked,"%Drd%Dd",DREG(STACK),dynreg);
+ } else {
+ //Can just push the whole 32-bit word as operand
+ gen_call_function((void *)&mem_writew_checked,"%Drd%Dd",DREG(STACK),dynreg);
+ }
+ dyn_check_bool_exception_al();
+ /* everything was ok, change register now */
+ gen_dop_word(DOP_MOV,true,DREG(ESP),DREG(NEWESP));
+ gen_releasereg(DREG(NEWESP));
+}
+
+static void dyn_pop(DynReg * dynreg,bool checked=true) {
+ gen_protectflags();
+ gen_dop_word(DOP_MOV,true,DREG(STACK),DREG(ESP));
+ gen_dop_word_var(DOP_AND,true,DREG(STACK),&cpu.stack.mask);
+ gen_dop_word(DOP_ADD,true,DREG(STACK),DREG(SS));
+ if (checked) {
+ if (decode.big_op) {
+ gen_call_function((void *)&mem_readd_checked,"%Drd%Id",DREG(STACK),&core_dyn.readdata);
+ } else {
+ gen_call_function((void *)&mem_readw_checked,"%Drd%Id",DREG(STACK),&core_dyn.readdata);
+ }
+ dyn_check_bool_exception_al();
+ gen_mov_host(&core_dyn.readdata,dynreg,decode.big_op?4:2);
+ } else {
+ if (decode.big_op) {
+ gen_call_function((void *)&mem_readd,"%Rd%Drd",dynreg,DREG(STACK));
+ } else {
+ gen_call_function((void *)&mem_readw,"%Rw%Drd",dynreg,DREG(STACK));
+ }
+ }
+ if (dynreg!=DREG(ESP)) {
+ gen_lea(DREG(STACK),DREG(ESP),0,0,decode.big_op?4:2);
+ gen_dop_word_var(DOP_AND,true,DREG(STACK),&cpu.stack.mask);
+ gen_dop_word_var(DOP_AND,true,DREG(ESP),&cpu.stack.notmask);
+ gen_dop_word(DOP_OR,true,DREG(ESP),DREG(STACK));
+ }
+}
+
+static INLINE void dyn_get_modrm(void) {
+ decode.modrm.val=decode_fetchb();
+ decode.modrm.mod=(decode.modrm.val >> 6) & 3;
+ decode.modrm.reg=(decode.modrm.val >> 3) & 7;
+ decode.modrm.rm=(decode.modrm.val & 7);
+}
+
+static void dyn_fill_ea(bool addseg=true, DynReg * reg_ea=DREG(EA)) {
+ DynReg * segbase;
+ if (!decode.big_addr) {
+ Bits imm;
+ switch (decode.modrm.mod) {
+ case 0:imm=0;break;
+ case 1:imm=(Bit8s)decode_fetchb();break;
+ case 2:imm=(Bit16s)decode_fetchw();break;
+ }
+ DynReg * extend_src=reg_ea;
+ switch (decode.modrm.rm) {
+ case 0:/* BX+SI */
+ gen_lea(reg_ea,DREG(EBX),DREG(ESI),0,imm);
+ segbase=DREG(DS);
+ break;
+ case 1:/* BX+DI */
+ gen_lea(reg_ea,DREG(EBX),DREG(EDI),0,imm);
+ segbase=DREG(DS);
+ break;
+ case 2:/* BP+SI */
+ gen_lea(reg_ea,DREG(EBP),DREG(ESI),0,imm);
+ segbase=DREG(SS);
+ break;
+ case 3:/* BP+DI */
+ gen_lea(reg_ea,DREG(EBP),DREG(EDI),0,imm);
+ segbase=DREG(SS);
+ break;
+ case 4:/* SI */
+ if (imm) gen_lea(reg_ea,DREG(ESI),0,0,imm);
+ else extend_src=DREG(ESI);
+ segbase=DREG(DS);
+ break;
+ case 5:/* DI */
+ if (imm) gen_lea(reg_ea,DREG(EDI),0,0,imm);
+ else extend_src=DREG(EDI);
+ segbase=DREG(DS);
+ break;
+ case 6:/* imm/BP */
+ if (!decode.modrm.mod) {
+ imm=decode_fetchw();
+ gen_dop_word_imm(DOP_MOV,true,reg_ea,imm);
+ segbase=DREG(DS);
+ goto skip_extend_word;
+ } else {
+ gen_lea(reg_ea,DREG(EBP),0,0,imm);
+ segbase=DREG(SS);
+ }
+ break;
+ case 7: /* BX */
+ if (imm) gen_lea(reg_ea,DREG(EBX),0,0,imm);
+ else extend_src=DREG(EBX);
+ segbase=DREG(DS);
+ break;
+ }
+ gen_extend_word(false,reg_ea,extend_src);
+skip_extend_word:
+ if (addseg) {
+ gen_lea(reg_ea,reg_ea,decode.segprefix ? decode.segprefix : segbase,0,0);
+ }
+ } else {
+ Bits imm=0;
+ DynReg * base=0;DynReg * scaled=0;Bitu scale=0;
+ switch (decode.modrm.rm) {
+ case 0:base=DREG(EAX);segbase=DREG(DS);break;
+ case 1:base=DREG(ECX);segbase=DREG(DS);break;
+ case 2:base=DREG(EDX);segbase=DREG(DS);break;
+ case 3:base=DREG(EBX);segbase=DREG(DS);break;
+ case 4: /* SIB */
+ {
+ Bitu sib=decode_fetchb();
+ static DynReg * scaledtable[8]={
+ DREG(EAX),DREG(ECX),DREG(EDX),DREG(EBX),
+ 0,DREG(EBP),DREG(ESI),DREG(EDI),
+ };
+ scaled=scaledtable[(sib >> 3) &7];
+ scale=(sib >> 6);
+ switch (sib & 7) {
+ case 0:base=DREG(EAX);segbase=DREG(DS);break;
+ case 1:base=DREG(ECX);segbase=DREG(DS);break;
+ case 2:base=DREG(EDX);segbase=DREG(DS);break;
+ case 3:base=DREG(EBX);segbase=DREG(DS);break;
+ case 4:base=DREG(ESP);segbase=DREG(SS);break;
+ case 5:
+ if (decode.modrm.mod) {
+ base=DREG(EBP);segbase=DREG(SS);
+ } else {
+ segbase=DREG(DS);
+ Bitu val;
+ if (decode_fetchd_imm(val)) {
+ gen_mov_host((void*)val,DREG(EA),4);
+ if (!addseg) {
+ gen_lea(reg_ea,DREG(EA),scaled,scale,0);
+ } else {
+ DynReg** seg = decode.segprefix ? &decode.segprefix : &segbase;
+ gen_lea(DREG(EA),DREG(EA),scaled,scale,0);
+ gen_lea(reg_ea,DREG(EA),*seg,0,0);
+ }
+ return;
+ }
+ imm=(Bit32s)val;
+ }
+ break;
+ case 6:base=DREG(ESI);segbase=DREG(DS);break;
+ case 7:base=DREG(EDI);segbase=DREG(DS);break;
+ }
+ }
+ break; /* SIB Break */
+ case 5:
+ if (decode.modrm.mod) {
+ base=DREG(EBP);segbase=DREG(SS);
+ } else {
+ imm=(Bit32s)decode_fetchd();segbase=DREG(DS);
+ }
+ break;
+ case 6:base=DREG(ESI);segbase=DREG(DS);break;
+ case 7:base=DREG(EDI);segbase=DREG(DS);break;
+ }
+ switch (decode.modrm.mod) {
+ case 1:imm=(Bit8s)decode_fetchb();break;
+ case 2: {
+ Bitu val;
+ if (decode_fetchd_imm(val)) {
+ gen_mov_host((void*)val,DREG(EA),4);
+ if (!addseg) {
+ gen_lea(DREG(EA),DREG(EA),scaled,scale,0);
+ gen_lea(reg_ea,DREG(EA),base,0,0);
+ } else {
+ DynReg** seg = decode.segprefix ? &decode.segprefix : &segbase;
+ if (!base) {
+ gen_lea(DREG(EA),DREG(EA),scaled,scale,0);
+ gen_lea(reg_ea,DREG(EA),*seg,0,0);
+ } else if (!scaled) {
+ gen_lea(DREG(EA),DREG(EA),*seg,0,0);
+ gen_lea(reg_ea,DREG(EA),base,0,0);
+ } else {
+ gen_lea(DREG(EA),DREG(EA),scaled,scale,0);
+ gen_lea(DREG(EA),DREG(EA),base,0,0);
+ gen_lea(reg_ea,DREG(EA),decode.segprefix ? decode.segprefix : segbase,0,0);
+ }
+ }
+ return;
+ }
+
+ imm=(Bit32s)val;
+ break;
+ }
+ }
+ if (!addseg) {
+ gen_lea(reg_ea,base,scaled,scale,imm);
+ } else {
+ DynReg** seg = decode.segprefix ? &decode.segprefix : &segbase;
+ if (!base) gen_lea(reg_ea,*seg,scaled,scale,imm);
+ else if (!scaled) gen_lea(reg_ea,base,*seg,0,imm);
+ else {
+ gen_lea(DREG(EA),base,scaled,scale,imm);
+ gen_lea(reg_ea,DREG(EA),decode.segprefix ? decode.segprefix : segbase,0,0);
+ }
+ }
+ }
+}
+
+
+static void dyn_dop_word_imm(DualOps op,bool dword,DynReg * dr1) {
+ Bitu val;
+ if (dword) {
+ if (decode_fetchd_imm(val)) {
+ gen_dop_word_imm_mem(op,true,dr1,(void*)val);
+ return;
+ }
+ } else {
+ if (decode_fetchw_imm(val)) {
+ gen_dop_word_imm_mem(op,false,dr1,(void*)val);
+ return;
+ }
+ }
+ gen_dop_word_imm(op,dword,dr1,val);
+}
+
+static void dyn_dop_byte_imm(DualOps op,DynReg * dr1,Bit8u di1) {
+ Bitu val;
+ if (decode_fetchb_imm(val)) {
+ gen_dop_byte_imm_mem(op,dr1,di1,(void*)val);
+ } else {
+ gen_dop_byte_imm(op,dr1,di1,(Bit8u)val);
+ }
+}
+
+
+#include "helpers.h"
+#include "string.h"
+
+
+static void dyn_dop_ebgb(DualOps op) {
+ dyn_get_modrm();DynReg * rm_reg=&DynRegs[decode.modrm.reg&3];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ if ((op<=DOP_TEST) && (op!=DOP_ADC && op!=DOP_SBB)) set_skipflags(true);
+ dyn_read_byte(DREG(EA),DREG(TMPB),false);
+ if (op<=DOP_TEST) {
+ if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
+ else set_skipflags(false);
+ }
+ gen_dop_byte(op,DREG(TMPB),0,rm_reg,decode.modrm.reg&4);
+ if (op!=DOP_CMP) dyn_write_byte_release(DREG(EA),DREG(TMPB),false);
+ else gen_releasereg(DREG(EA));
+ gen_releasereg(DREG(TMPB));
+ } else {
+ if (op<=DOP_TEST) {
+ if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
+ else gen_discardflags();
+ }
+ gen_dop_byte(op,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4,rm_reg,decode.modrm.reg&4);
+ }
+}
+
+
+static void dyn_dop_gbeb(DualOps op) {
+ dyn_get_modrm();DynReg * rm_reg=&DynRegs[decode.modrm.reg&3];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ if ((op<=DOP_TEST) && (op!=DOP_ADC && op!=DOP_SBB)) set_skipflags(true);
+ dyn_read_byte_release(DREG(EA),DREG(TMPB),false);
+ if (op<=DOP_TEST) {
+ if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
+ else set_skipflags(false);
+ }
+ gen_dop_byte(op,rm_reg,decode.modrm.reg&4,DREG(TMPB),0);
+ gen_releasereg(DREG(TMPB));
+ } else {
+ if (op<=DOP_TEST) {
+ if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
+ else gen_discardflags();
+ }
+ gen_dop_byte(op,rm_reg,decode.modrm.reg&4,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4);
+ }
+}
+
+static void dyn_mov_ebib(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ gen_call_write(DREG(EA),decode_fetchb(),1);
+ dyn_check_bool_exception_al();
+ } else {
+ gen_dop_byte_imm(DOP_MOV,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4,decode_fetchb());
+ }
+}
+
+static void dyn_mov_ebgb(void) {
+ dyn_get_modrm();
+ DynReg * rm_reg=&DynRegs[decode.modrm.reg&3];Bitu rm_regi=decode.modrm.reg&4;
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ dyn_write_byte_release(DREG(EA),rm_reg,rm_regi==4);
+ } else {
+ gen_dop_byte(DOP_MOV,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4,rm_reg,rm_regi);
+ }
+}
+
+static void dyn_mov_gbeb(void) {
+ dyn_get_modrm();
+ DynReg * rm_reg=&DynRegs[decode.modrm.reg&3];Bitu rm_regi=decode.modrm.reg&4;
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ dyn_read_byte_release(DREG(EA),rm_reg,rm_regi);
+ } else {
+ gen_dop_byte(DOP_MOV,rm_reg,rm_regi,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4);
+ }
+}
+
+static void dyn_dop_evgv(DualOps op) {
+ dyn_get_modrm();
+ DynReg * rm_reg=&DynRegs[decode.modrm.reg];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ if ((op<=DOP_TEST) && (op!=DOP_ADC && op!=DOP_SBB)) set_skipflags(true);
+ dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op);
+ if (op<=DOP_TEST) {
+ if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
+ else set_skipflags(false);
+ }
+ gen_dop_word(op,decode.big_op,DREG(TMPW),rm_reg);
+ if (op!=DOP_CMP) dyn_write_word_release(DREG(EA),DREG(TMPW),decode.big_op);
+ else gen_releasereg(DREG(EA));
+ gen_releasereg(DREG(TMPW));
+ } else {
+ if (op<=DOP_TEST) {
+ if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
+ else gen_discardflags();
+ }
+ gen_dop_word(op,decode.big_op,&DynRegs[decode.modrm.rm],rm_reg);
+ }
+}
+
+static void dyn_imul_gvev(Bitu immsize) {
+ dyn_get_modrm();DynReg * src;
+ DynReg * rm_reg=&DynRegs[decode.modrm.reg];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();dyn_read_word_release(DREG(EA),DREG(TMPW),decode.big_op);
+ src=DREG(TMPW);
+ } else {
+ src=&DynRegs[decode.modrm.rm];
+ }
+ gen_needflags();
+ switch (immsize) {
+ case 0:gen_imul_word(decode.big_op,rm_reg,src);break;
+ case 1:gen_imul_word_imm(decode.big_op,rm_reg,src,(Bit8s)decode_fetchb());break;
+ case 2:gen_imul_word_imm(decode.big_op,rm_reg,src,(Bit16s)decode_fetchw());break;
+ case 4:gen_imul_word_imm(decode.big_op,rm_reg,src,(Bit32s)decode_fetchd());break;
+ }
+ gen_releasereg(DREG(TMPW));
+}
+
+static void dyn_dop_gvev(DualOps op) {
+ dyn_get_modrm();
+ DynReg * rm_reg=&DynRegs[decode.modrm.reg];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ if ((op<=DOP_TEST) && (op!=DOP_ADC && op!=DOP_SBB)) set_skipflags(true);
+ dyn_read_word_release(DREG(EA),DREG(TMPW),decode.big_op);
+ if (op<=DOP_TEST) {
+ if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
+ else set_skipflags(false);
+ }
+ gen_dop_word(op,decode.big_op,rm_reg,DREG(TMPW));
+ gen_releasereg(DREG(TMPW));
+ } else {
+ if (op<=DOP_TEST) {
+ if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
+ else gen_discardflags();
+ }
+ gen_dop_word(op,decode.big_op,rm_reg,&DynRegs[decode.modrm.rm]);
+ }
+}
+
+static void dyn_mov_evgv(void) {
+ dyn_get_modrm();
+ DynReg * rm_reg=&DynRegs[decode.modrm.reg];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ dyn_write_word_release(DREG(EA),rm_reg,decode.big_op);
+ } else {
+ gen_dop_word(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.rm],rm_reg);
+ }
+}
+
+static void dyn_mov_gvev(void) {
+ dyn_get_modrm();
+ DynReg * rm_reg=&DynRegs[decode.modrm.reg];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ dyn_read_word_release(DREG(EA),rm_reg,decode.big_op);
+ } else {
+ gen_dop_word(DOP_MOV,decode.big_op,rm_reg,&DynRegs[decode.modrm.rm]);
+ }
+}
+static void dyn_mov_eviv(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ gen_call_write(DREG(EA),decode.big_op ? decode_fetchd() : decode_fetchw(),decode.big_op?4:2);
+ dyn_check_bool_exception_al();
+ } else {
+ gen_dop_word_imm(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.rm],decode.big_op ? decode_fetchd() : decode_fetchw());
+ }
+}
+
+static void dyn_mov_ev_gb(bool sign) {
+ dyn_get_modrm();DynReg * rm_reg=&DynRegs[decode.modrm.reg];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ dyn_read_byte_release(DREG(EA),DREG(TMPB),false);
+ gen_extend_byte(sign,decode.big_op,rm_reg,DREG(TMPB),0);
+ gen_releasereg(DREG(TMPB));
+ } else {
+ gen_extend_byte(sign,decode.big_op,rm_reg,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4);
+ }
+}
+
+static void dyn_mov_ev_gw(bool sign) {
+ if (!decode.big_op) {
+ dyn_mov_gvev();
+ return;
+ }
+ dyn_get_modrm();DynReg * rm_reg=&DynRegs[decode.modrm.reg];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ dyn_read_word_release(DREG(EA),DREG(TMPW),false);
+ gen_extend_word(sign,rm_reg,DREG(TMPW));
+ gen_releasereg(DREG(TMPW));
+ } else {
+ gen_extend_word(sign,rm_reg,&DynRegs[decode.modrm.rm]);
+ }
+}
+
+static void dyn_cmpxchg_evgv(void) {
+ dyn_get_modrm();
+ DynReg * rm_reg=&DynRegs[decode.modrm.reg];
+ gen_protectflags();
+ if (decode.modrm.mod<3) {
+ gen_releasereg(DREG(EAX));
+ gen_releasereg(DREG(TMPB));
+ gen_releasereg(rm_reg);
+
+ dyn_fill_ea();
+ dyn_read_word(DREG(EA),DREG(TMPB),decode.big_op);
+ gen_dop_word(DOP_CMP,decode.big_op,DREG(EAX),DREG(TMPB));
+ Bit8u * branch=gen_create_branch(BR_NZ);
+
+ // eax==mem -> mem:=rm_reg
+ dyn_write_word_release(DREG(EA),rm_reg,decode.big_op);
+ gen_setzeroflag();
+ gen_releasereg(DREG(EAX));
+ gen_releasereg(DREG(TMPB));
+ gen_releasereg(rm_reg);
+
+ Bit8u * jump=gen_create_jump();
+
+ gen_fill_branch(branch);
+ // eax!=mem -> eax:=mem
+ dyn_write_word_release(DREG(EA),DREG(TMPB),decode.big_op); // cmpxchg always issues write
+ gen_dop_word(DOP_MOV,decode.big_op,DREG(EAX),DREG(TMPB));
+ gen_clearzeroflag();
+ gen_releasereg(DREG(EAX));
+ gen_releasereg(DREG(TMPB));
+ gen_releasereg(rm_reg);
+
+ gen_fill_jump(jump);
+ } else {
+ gen_releasereg(DREG(EAX));
+ gen_releasereg(&DynRegs[decode.modrm.rm]);
+ gen_releasereg(rm_reg);
+
+ gen_dop_word(DOP_CMP,decode.big_op,DREG(EAX),&DynRegs[decode.modrm.rm]);
+ Bit8u * branch=gen_create_branch(BR_NZ);
+
+ // eax==rm -> rm:=rm_reg
+ gen_dop_word(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.rm],rm_reg);
+ gen_setzeroflag();
+ gen_releasereg(DREG(EAX));
+ gen_releasereg(&DynRegs[decode.modrm.rm]);
+ gen_releasereg(rm_reg);
+
+ Bit8u * jump=gen_create_jump();
+
+ gen_fill_branch(branch);
+ // eax!=rm -> eax:=rm
+ gen_dop_word(DOP_MOV,decode.big_op,DREG(EAX),&DynRegs[decode.modrm.rm]);
+ gen_clearzeroflag();
+ gen_releasereg(DREG(EAX));
+ gen_releasereg(&DynRegs[decode.modrm.rm]);
+ gen_releasereg(rm_reg);
+
+ gen_fill_jump(jump);
+ }
+}
+
+static void dyn_dshift_ev_gv(bool left,bool immediate) {
+ dyn_get_modrm();
+ DynReg * rm_reg=&DynRegs[decode.modrm.reg];
+ DynReg * ea_reg;
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();ea_reg=DREG(TMPW);
+ dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op);
+ } else ea_reg=&DynRegs[decode.modrm.rm];
+ gen_needflags();
+ if (immediate) gen_dshift_imm(decode.big_op,left,ea_reg,rm_reg,decode_fetchb());
+ else gen_dshift_cl(decode.big_op,left,ea_reg,rm_reg,DREG(ECX));
+ if (decode.modrm.mod<3) {
+ dyn_write_word_release(DREG(EA),DREG(TMPW),decode.big_op);
+ gen_releasereg(DREG(TMPW));
+ }
+}
+
+
+static DualOps grp1_table[8]={DOP_ADD,DOP_OR,DOP_ADC,DOP_SBB,DOP_AND,DOP_SUB,DOP_XOR,DOP_CMP};
+static void dyn_grp1_eb_ib(void) {
+ dyn_get_modrm();
+ DualOps op=grp1_table[decode.modrm.reg];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ if ((op<=DOP_TEST) && (op!=DOP_ADC && op!=DOP_SBB)) set_skipflags(true);
+ dyn_read_byte(DREG(EA),DREG(TMPB),false);
+ if (op<=DOP_TEST) {
+ if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
+ else set_skipflags(false);
+ }
+ gen_dop_byte_imm(op,DREG(TMPB),0,decode_fetchb());
+ if (op!=DOP_CMP) dyn_write_byte_release(DREG(EA),DREG(TMPB),false);
+ else gen_releasereg(DREG(EA));
+ gen_releasereg(DREG(TMPB));
+ } else {
+ if (op<=DOP_TEST) {
+ if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
+ else gen_discardflags();
+ }
+ dyn_dop_byte_imm(op,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4);
+ }
+}
+
+static void dyn_grp1_ev_ivx(bool withbyte) {
+ dyn_get_modrm();
+ DualOps op=grp1_table[decode.modrm.reg];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ if ((op<=DOP_TEST) && (op!=DOP_ADC && op!=DOP_SBB)) set_skipflags(true);
+ dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op);
+ if (op<=DOP_TEST) {
+ if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
+ else set_skipflags(false);
+ }
+ if (!withbyte) {
+ dyn_dop_word_imm(op,decode.big_op,DREG(TMPW));
+ } else {
+ gen_dop_word_imm(op,decode.big_op,DREG(TMPW),(Bit8s)decode_fetchb());
+ }
+ if (op!=DOP_CMP) dyn_write_word_release(DREG(EA),DREG(TMPW),decode.big_op);
+ else gen_releasereg(DREG(EA));
+ gen_releasereg(DREG(TMPW));
+ } else {
+ if (op<=DOP_TEST) {
+ if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
+ else gen_discardflags();
+ }
+ if (!withbyte) {
+ dyn_dop_word_imm(op,decode.big_op,&DynRegs[decode.modrm.rm]);
+ } else {
+ gen_dop_word_imm(op,decode.big_op,&DynRegs[decode.modrm.rm],(Bit8s)decode_fetchb());
+ }
+ }
+}
+
+enum grp2_types {
+ grp2_1,grp2_imm,grp2_cl,
+};
+
+static void dyn_grp2_eb(grp2_types type) {
+ dyn_get_modrm();DynReg * src;Bit8u src_i;
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();dyn_read_byte(DREG(EA),DREG(TMPB),false);
+ src=DREG(TMPB);
+ src_i=0;
+ } else {
+ src=&DynRegs[decode.modrm.rm&3];
+ src_i=decode.modrm.rm&4;
+ }
+ switch (type) {
+ case grp2_1:
+ /* rotates (first 4 ops) alter cf/of only; shifts (last 4 ops) alter all flags */
+ if (decode.modrm.reg < 4) gen_needflags();
+ else gen_discardflags();
+ gen_shift_byte_imm(decode.modrm.reg,src,src_i,1);
+ break;
+ case grp2_imm: {
+ Bit8u imm=decode_fetchb();
+ if (imm) {
+ /* rotates (first 4 ops) alter cf/of only; shifts (last 4 ops) alter all flags */
+ if (decode.modrm.reg < 4) gen_needflags();
+ else gen_discardflags();
+ gen_shift_byte_imm(decode.modrm.reg,src,src_i,imm);
+ } else return;
+ }
+ break;
+ case grp2_cl:
+ gen_needflags(); /* flags must not be changed on ecx==0 */
+ gen_shift_byte_cl (decode.modrm.reg,src,src_i,DREG(ECX));
+ break;
+ }
+ if (decode.modrm.mod<3) {
+ dyn_write_byte_release(DREG(EA),src,false);
+ gen_releasereg(src);
+ }
+}
+
+static void dyn_grp2_ev(grp2_types type) {
+ dyn_get_modrm();DynReg * src;
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op);
+ src=DREG(TMPW);
+ } else {
+ src=&DynRegs[decode.modrm.rm];
+ }
+ switch (type) {
+ case grp2_1:
+ /* rotates (first 4 ops) alter cf/of only; shifts (last 4 ops) alter all flags */
+ if (decode.modrm.reg < 4) gen_needflags();
+ else gen_discardflags();
+ gen_shift_word_imm(decode.modrm.reg,decode.big_op,src,1);
+ break;
+ case grp2_imm: {
+ Bitu val;
+ if (decode_fetchb_imm(val)) {
+ if (decode.modrm.reg < 4) gen_needflags();
+ else gen_discardflags();
+ gen_load_host((void*)val,DREG(TMPB),1);
+ gen_shift_word_cl(decode.modrm.reg,decode.big_op,src,DREG(TMPB));
+ gen_releasereg(DREG(TMPB));
+ break;
+ }
+ Bit8u imm=(Bit8u)val;
+ if (imm) {
+ /* rotates (first 4 ops) alter cf/of only; shifts (last 4 ops) alter all flags */
+ if (decode.modrm.reg < 4) gen_needflags();
+ else gen_discardflags();
+ gen_shift_word_imm(decode.modrm.reg,decode.big_op,src,imm);
+ } else return;
+ }
+ break;
+ case grp2_cl:
+ gen_needflags(); /* flags must not be changed on ecx==0 */
+ gen_shift_word_cl (decode.modrm.reg,decode.big_op,src,DREG(ECX));
+ break;
+ }
+ if (decode.modrm.mod<3) {
+ dyn_write_word_release(DREG(EA),src,decode.big_op);
+ gen_releasereg(src);
+ }
+}
+
+static void dyn_grp3_eb(void) {
+ dyn_get_modrm();DynReg * src;Bit8u src_i;
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ if ((decode.modrm.reg==0) || (decode.modrm.reg==3)) set_skipflags(true);
+ dyn_read_byte(DREG(EA),DREG(TMPB),false);
+ src=DREG(TMPB);src_i=0;
+ } else {
+ src=&DynRegs[decode.modrm.rm&3];
+ src_i=decode.modrm.rm&4;
+ }
+ switch (decode.modrm.reg) {
+ case 0x0: /* test eb,ib */
+ set_skipflags(false);gen_dop_byte_imm(DOP_TEST,src,src_i,decode_fetchb());
+ goto skipsave;
+ case 0x2: /* NOT Eb */
+ gen_sop_byte(SOP_NOT,src,src_i);
+ break;
+ case 0x3: /* NEG Eb */
+ set_skipflags(false);gen_sop_byte(SOP_NEG,src,src_i);
+ break;
+ case 0x4: /* mul Eb */
+ gen_needflags();gen_mul_byte(false,DREG(EAX),src,src_i);
+ goto skipsave;
+ case 0x5: /* imul Eb */
+ gen_needflags();gen_mul_byte(true,DREG(EAX),src,src_i);
+ goto skipsave;
+ case 0x6: /* div Eb */
+ case 0x7: /* idiv Eb */
+ /* EAX could be used, so precache it */
+ if (decode.modrm.mod==3)
+ gen_dop_byte(DOP_MOV,DREG(TMPB),0,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4);
+ gen_releasereg(DREG(EAX));
+ gen_call_function((decode.modrm.reg==6) ? (void *)&dyn_helper_divb : (void *)&dyn_helper_idivb,
+ "%Rd%Dd",DREG(TMPB),DREG(TMPB));
+ dyn_check_bool_exception(DREG(TMPB));
+ goto skipsave;
+ }
+ /* Save the result if memory op */
+ if (decode.modrm.mod<3) dyn_write_byte_release(DREG(EA),src,false);
+skipsave:
+ gen_releasereg(DREG(TMPB));gen_releasereg(DREG(EA));
+}
+
+static void dyn_grp3_ev(void) {
+ dyn_get_modrm();DynReg * src;
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();src=DREG(TMPW);
+ if ((decode.modrm.reg==0) || (decode.modrm.reg==3)) set_skipflags(true);
+ dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op);
+ } else src=&DynRegs[decode.modrm.rm];
+ switch (decode.modrm.reg) {
+ case 0x0: /* test ev,iv */
+ set_skipflags(false);gen_dop_word_imm(DOP_TEST,decode.big_op,src,decode.big_op ? decode_fetchd() : decode_fetchw());
+ goto skipsave;
+ case 0x2: /* NOT Ev */
+ gen_sop_word(SOP_NOT,decode.big_op,src);
+ break;
+ case 0x3: /* NEG Eb */
+ set_skipflags(false);gen_sop_word(SOP_NEG,decode.big_op,src);
+ break;
+ case 0x4: /* mul Eb */
+ gen_needflags();gen_mul_word(false,DREG(EAX),DREG(EDX),decode.big_op,src);
+ goto skipsave;
+ case 0x5: /* imul Eb */
+ gen_needflags();gen_mul_word(true,DREG(EAX),DREG(EDX),decode.big_op,src);
+ goto skipsave;
+ case 0x6: /* div Eb */
+ case 0x7: /* idiv Eb */
+ /* EAX could be used, so precache it */
+ if (decode.modrm.mod==3)
+ gen_dop_word(DOP_MOV,decode.big_op,DREG(TMPW),&DynRegs[decode.modrm.rm]);
+ gen_releasereg(DREG(EAX));gen_releasereg(DREG(EDX));
+ void * func=(decode.modrm.reg==6) ?
+ (decode.big_op ? (void *)&dyn_helper_divd : (void *)&dyn_helper_divw) :
+ (decode.big_op ? (void *)&dyn_helper_idivd : (void *)&dyn_helper_idivw);
+ gen_call_function(func,"%Rd%Dd",DREG(TMPB),DREG(TMPW));
+ dyn_check_bool_exception(DREG(TMPB));
+ gen_releasereg(DREG(TMPB));
+ goto skipsave;
+ }
+ /* Save the result if memory op */
+ if (decode.modrm.mod<3) dyn_write_word_release(DREG(EA),src,decode.big_op);
+skipsave:
+ gen_releasereg(DREG(TMPW));gen_releasereg(DREG(EA));
+}
+
+static void dyn_mov_ev_seg(void) {
+ dyn_get_modrm();
+ gen_load_host(&Segs.val[decode.modrm.reg],DREG(TMPW),2);
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ dyn_write_word_release(DREG(EA),DREG(TMPW),false);
+ } else {
+ gen_dop_word(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.rm],DREG(TMPW));
+ }
+ gen_releasereg(DREG(TMPW));
+}
+
+static void dyn_load_seg(SegNames seg,DynReg * src) {
+ gen_call_function((void *)&CPU_SetSegGeneral,"%Rd%Id%Drw",DREG(TMPB),seg,src);
+ dyn_check_bool_exception(DREG(TMPB));
+ gen_releasereg(DREG(TMPB));
+ gen_releasereg(&DynRegs[G_ES+seg]);
+}
+
+static void dyn_load_seg_off_ea(SegNames seg) {
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ gen_lea(DREG(TMPB),DREG(EA),0,0,decode.big_op ? 4:2);
+ dyn_read_word(DREG(TMPB),DREG(TMPB),false);
+ dyn_read_word_release(DREG(EA),DREG(TMPW),decode.big_op);
+ dyn_load_seg(seg,DREG(TMPB));gen_releasereg(DREG(TMPB));
+ gen_dop_word(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.reg],DREG(TMPW));
+ } else {
+ IllegalOption("dyn_load_seg_off_ea");
+ }
+}
+
+static void dyn_mov_seg_ev(void) {
+ dyn_get_modrm();
+ SegNames seg=(SegNames)decode.modrm.reg;
+ if (GCC_UNLIKELY(seg==cs)) IllegalOption("dyn_mov_seg_ev");
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ dyn_read_word(DREG(EA),DREG(EA),false);
+ dyn_load_seg(seg,DREG(EA));
+ gen_releasereg(DREG(EA));
+ } else {
+ dyn_load_seg(seg,&DynRegs[decode.modrm.rm]);
+ }
+}
+
+static void dyn_push_seg(SegNames seg) {
+ gen_load_host(&Segs.val[seg],DREG(TMPW),2);
+ dyn_push(DREG(TMPW));
+ gen_releasereg(DREG(TMPW));
+}
+
+static void dyn_pop_seg(SegNames seg) {
+ gen_releasereg(DREG(ESP));
+ gen_call_function((void *)&CPU_PopSeg,"%Rd%Id%Id",DREG(TMPB),seg,decode.big_op);
+ dyn_check_bool_exception(DREG(TMPB));
+ gen_releasereg(DREG(TMPB));
+ gen_releasereg(&DynRegs[G_ES+seg]);
+ gen_releasereg(DREG(ESP));
+}
+
+static void dyn_pop_ev(void) {
+ dyn_pop(DREG(TMPW));
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+// dyn_write_word_release(DREG(EA),DREG(TMPW),decode.big_op);
+ if (decode.big_op) gen_call_function((void *)&mem_writed_inline,"%Ddr%Dd",DREG(EA),DREG(TMPW));
+ else gen_call_function((void *)&mem_writew_inline,"%Ddr%Dd",DREG(EA),DREG(TMPW));
+ } else {
+ gen_dop_word(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.rm],DREG(TMPW));
+ }
+ gen_releasereg(DREG(TMPW));
+}
+
+static void dyn_enter(void) {
+ gen_releasereg(DREG(ESP));
+ gen_releasereg(DREG(EBP));
+ Bitu bytes=decode_fetchw();
+ Bitu level=decode_fetchb();
+ gen_call_function((void *)&CPU_ENTER,"%Id%Id%Id",decode.big_op,bytes,level);
+}
+
+static void dyn_leave(void) {
+ gen_protectflags();
+ gen_dop_word_var(DOP_MOV,true,DREG(TMPW),&cpu.stack.mask);
+ gen_sop_word(SOP_NOT,true,DREG(TMPW));
+ gen_dop_word(DOP_AND,true,DREG(ESP),DREG(TMPW));
+ gen_dop_word(DOP_MOV,true,DREG(TMPW),DREG(EBP));
+ gen_dop_word_var(DOP_AND,true,DREG(TMPW),&cpu.stack.mask);
+ gen_dop_word(DOP_OR,true,DREG(ESP),DREG(TMPW));
+ dyn_pop(DREG(EBP),false);
+ gen_releasereg(DREG(TMPW));
+}
+
+static void dyn_segprefix(SegNames seg) {
+// if (GCC_UNLIKELY((Bitu)(decode.segprefix))) IllegalOption("dyn_segprefix");
+ decode.segprefix=&DynRegs[G_ES+seg];
+}
+
+static void dyn_closeblock(void) {
+ //Shouldn't create empty block normally but let's do it like this
+ gen_protectflags();
+ dyn_fill_blocks();
+ cache_closeblock();
+}
+
+static void dyn_normal_exit(BlockReturn code) {
+ gen_protectflags();
+ dyn_reduce_cycles();
+ dyn_set_eip_last();
+ dyn_save_critical_regs();
+ gen_return(code);
+ dyn_closeblock();
+}
+
+static void dyn_exit_link(Bits eip_change) {
+ gen_protectflags();
+ gen_dop_word_imm(DOP_ADD,decode.big_op,DREG(EIP),(decode.code-decode.code_start)+eip_change);
+ dyn_reduce_cycles();
+ dyn_save_critical_regs();
+ gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlock,cache.start));
+ dyn_closeblock();
+}
+
+static void dyn_branched_exit(BranchTypes btype,Bit32s eip_add) {
+ Bitu eip_base=decode.code-decode.code_start;
+ gen_needflags();
+ gen_protectflags();
+ dyn_save_noncritical_regs();
+ gen_releasereg(DREG(FLAGS));
+ gen_releasereg(DREG(EIP));
+
+ gen_preloadreg(DREG(CYCLES));
+ gen_preloadreg(DREG(EIP));
+ DynReg save_cycles,save_eip;
+ dyn_saveregister(DREG(CYCLES),&save_cycles);
+ dyn_saveregister(DREG(EIP),&save_eip);
+ Bit8u * data=gen_create_branch(btype);
+
+ /* Branch not taken */
+ dyn_reduce_cycles();
+ gen_dop_word_imm(DOP_ADD,decode.big_op,DREG(EIP),eip_base);
+ gen_releasereg(DREG(CYCLES));
+ gen_releasereg(DREG(EIP));
+ gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlock,cache.start));
+ gen_fill_branch(data);
+
+ /* Branch taken */
+ dyn_restoreregister(&save_cycles,DREG(CYCLES));
+ dyn_restoreregister(&save_eip,DREG(EIP));
+ dyn_reduce_cycles();
+ gen_dop_word_imm(DOP_ADD,decode.big_op,DREG(EIP),eip_base+eip_add);
+ gen_releasereg(DREG(CYCLES));
+ gen_releasereg(DREG(EIP));
+ gen_jmp_ptr(&decode.block->link[1].to,offsetof(CacheBlock,cache.start));
+ dyn_closeblock();
+}
+
+enum LoopTypes {
+ LOOP_NONE,LOOP_NE,LOOP_E,LOOP_JCXZ
+};
+
+static void dyn_loop(LoopTypes type) {
+ dyn_reduce_cycles();
+ Bits eip_add=(Bit8s)decode_fetchb();
+ Bitu eip_base=decode.code-decode.code_start;
+ Bit8u * branch1=0;Bit8u * branch2=0;
+ dyn_save_critical_regs();
+ switch (type) {
+ case LOOP_E:
+ gen_needflags();
+ branch1=gen_create_branch(BR_NZ);
+ break;
+ case LOOP_NE:
+ gen_needflags();
+ branch1=gen_create_branch(BR_Z);
+ break;
+ }
+ gen_protectflags();
+ switch (type) {
+ case LOOP_E:
+ case LOOP_NE:
+ case LOOP_NONE:
+ gen_sop_word(SOP_DEC,decode.big_addr,DREG(ECX));
+ gen_releasereg(DREG(ECX));
+ branch2=gen_create_branch(BR_Z);
+ break;
+ case LOOP_JCXZ:
+ gen_dop_word(DOP_OR,decode.big_addr,DREG(ECX),DREG(ECX));
+ gen_releasereg(DREG(ECX));
+ branch2=gen_create_branch(BR_NZ);
+ break;
+ }
+ gen_lea(DREG(EIP),DREG(EIP),0,0,eip_base+eip_add);
+ gen_releasereg(DREG(EIP));
+ gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlock,cache.start));
+ if (branch1) {
+ gen_fill_branch(branch1);
+ gen_sop_word(SOP_DEC,decode.big_addr,DREG(ECX));
+ gen_releasereg(DREG(ECX));
+ }
+ /* Branch taken */
+ gen_fill_branch(branch2);
+ gen_lea(DREG(EIP),DREG(EIP),0,0,eip_base);
+ gen_releasereg(DREG(EIP));
+ gen_jmp_ptr(&decode.block->link[1].to,offsetof(CacheBlock,cache.start));
+ dyn_closeblock();
+}
+
+static void dyn_ret_near(Bitu bytes) {
+ gen_protectflags();
+ dyn_reduce_cycles();
+ dyn_pop(DREG(EIP));
+ if (bytes) gen_dop_word_imm(DOP_ADD,true,DREG(ESP),bytes);
+ dyn_save_critical_regs();
+ gen_return(BR_Normal);
+ dyn_closeblock();
+}
+
+static void dyn_call_near_imm(void) {
+ Bits imm;
+ if (decode.big_op) imm=(Bit32s)decode_fetchd();
+ else imm=(Bit16s)decode_fetchw();
+ dyn_set_eip_end(DREG(TMPW));
+ dyn_push(DREG(TMPW));
+ gen_dop_word_imm(DOP_ADD,decode.big_op,DREG(TMPW),imm);
+ if (cpu.code.big) gen_dop_word(DOP_MOV,true,DREG(EIP),DREG(TMPW));
+ else gen_extend_word(false,DREG(EIP),DREG(TMPW));
+ dyn_reduce_cycles();
+ dyn_save_critical_regs();
+ gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlock,cache.start));
+ dyn_closeblock();
+}
+
+static void dyn_ret_far(Bitu bytes) {
+ gen_protectflags();
+ dyn_reduce_cycles();
+ dyn_set_eip_last_end(DREG(TMPW));
+ dyn_flags_gen_to_host();
+ dyn_save_critical_regs();
+ gen_call_function((void*)&CPU_RET,"%Id%Id%Drd",decode.big_op,bytes,DREG(TMPW));
+ gen_return_fast(BR_Normal);
+ dyn_closeblock();
+}
+
+static void dyn_call_far_imm(void) {
+ Bitu sel,off;
+ off=decode.big_op ? decode_fetchd() : decode_fetchw();
+ sel=decode_fetchw();
+ dyn_reduce_cycles();
+ dyn_set_eip_last_end(DREG(TMPW));
+ dyn_flags_gen_to_host();
+ dyn_save_critical_regs();
+ gen_call_function((void*)&CPU_CALL,"%Id%Id%Id%Drd",decode.big_op,sel,off,DREG(TMPW));
+ gen_return_fast(BR_Normal);
+ dyn_closeblock();
+}
+
+static void dyn_jmp_far_imm(void) {
+ Bitu sel,off;
+ gen_protectflags();
+ off=decode.big_op ? decode_fetchd() : decode_fetchw();
+ sel=decode_fetchw();
+ dyn_reduce_cycles();
+ dyn_set_eip_last_end(DREG(TMPW));
+ dyn_flags_gen_to_host();
+ dyn_save_critical_regs();
+ gen_call_function((void*)&CPU_JMP,"%Id%Id%Id%Drd",decode.big_op,sel,off,DREG(TMPW));
+ gen_return_fast(BR_Normal);
+ dyn_closeblock();
+}
+
+static void dyn_iret(void) {
+ gen_protectflags();
+ dyn_flags_gen_to_host();
+ dyn_reduce_cycles();
+ dyn_set_eip_last_end(DREG(TMPW));
+ dyn_save_critical_regs();
+ gen_call_function((void*)&CPU_IRET,"%Id%Drd",decode.big_op,DREG(TMPW));
+ gen_return_fast(BR_Iret);
+ dyn_closeblock();
+}
+
+static void dyn_interrupt(Bitu num) {
+ gen_protectflags();
+ dyn_flags_gen_to_host();
+ dyn_reduce_cycles();
+ dyn_set_eip_last_end(DREG(TMPW));
+ dyn_save_critical_regs();
+ gen_call_function((void*)&CPU_Interrupt,"%Id%Id%Drd",num,CPU_INT_SOFTWARE,DREG(TMPW));
+ gen_return_fast(BR_Normal);
+ dyn_closeblock();
+}
+
+static void dyn_add_iocheck(Bitu access_size) {
+ gen_call_function((void *)&CPU_IO_Exception,"%Dw%Id",DREG(EDX),access_size);
+ dyn_check_bool_exception_al();
+}
+
+static void dyn_add_iocheck_var(Bit8u accessed_port,Bitu access_size) {
+ gen_call_function((void *)&CPU_IO_Exception,"%Id%Id",accessed_port,access_size);
+ dyn_check_bool_exception_al();
+}
+
+#ifdef X86_DYNFPU_DH_ENABLED
+#include "dyn_fpu_dh.h"
+#define dh_fpu_startup() { \
+ fpu_used=true; \
+ gen_protectflags(); \
+ gen_load_host(&dyn_dh_fpu.state_used,DREG(TMPB),4); \
+ gen_dop_word_imm(DOP_CMP,true,DREG(TMPB),0); \
+ gen_releasereg(DREG(TMPB)); \
+ save_info[used_save_info].branch_pos=gen_create_branch_long(BR_Z); \
+ dyn_savestate(&save_info[used_save_info].state); \
+ save_info[used_save_info].return_pos=cache.pos; \
+ save_info[used_save_info].type=fpu_restore; \
+ used_save_info++; \
+}
+#endif
+#include "dyn_fpu.h"
+
+static CacheBlock * CreateCacheBlock(CodePageHandler * codepage,PhysPt start,Bitu max_opcodes) {
+ Bits i;
+/* Init a load of variables */
+ decode.code_start=start;
+ decode.code=start;
+ Bitu cycles=0;
+ decode.page.code=codepage;
+ decode.page.index=start&4095;
+ decode.page.wmap=codepage->write_map;
+ decode.page.invmap=codepage->invalidation_map;
+ decode.page.first=start >> 12;
+ decode.active_block=decode.block=cache_openblock();
+ decode.block->page.start=decode.page.index;
+ codepage->AddCacheBlock(decode.block);
+
+ gen_save_host_direct(&cache.block.running,(Bit32u)decode.block);
+ for (i=0;i<G_MAX;i++) {
+ DynRegs[i].flags&=~(DYNFLG_ACTIVE|DYNFLG_CHANGED);
+ DynRegs[i].genreg=0;
+ }
+ gen_reinit();
+ /* Start with the cycles check */
+ gen_protectflags();
+ gen_dop_word_imm(DOP_CMP,true,DREG(CYCLES),0);
+ save_info[used_save_info].branch_pos=gen_create_branch_long(BR_LE);
+ save_info[used_save_info].type=cycle_check;
+ used_save_info++;
+ gen_releasereg(DREG(CYCLES));
+ decode.cycles=0;
+#ifdef X86_DYNFPU_DH_ENABLED
+ bool fpu_used=false;
+#endif
+ while (max_opcodes--) {
+/* Init prefixes */
+ decode.big_addr=cpu.code.big;
+ decode.big_op=cpu.code.big;
+ decode.segprefix=0;
+ decode.rep=REP_NONE;
+ decode.cycles++;
+ decode.op_start=decode.code;
+restart_prefix:
+ Bitu opcode;
+ if (!decode.page.invmap) opcode=decode_fetchb();
+ else {
+ if (decode.page.index<4096) {
+ if (GCC_UNLIKELY(decode.page.invmap[decode.page.index]>=4)) goto illegalopcode;
+ opcode=decode_fetchb();
+ } else {
+ opcode=decode_fetchb();
+ if (GCC_UNLIKELY(decode.page.invmap &&
+ (decode.page.invmap[decode.page.index-1]>=4))) goto illegalopcode;
+ }
+ }
+ switch (opcode) {
+
+ case 0x00:dyn_dop_ebgb(DOP_ADD);break;
+ case 0x01:dyn_dop_evgv(DOP_ADD);break;
+ case 0x02:dyn_dop_gbeb(DOP_ADD);break;
+ case 0x03:dyn_dop_gvev(DOP_ADD);break;
+ case 0x04:gen_discardflags();gen_dop_byte_imm(DOP_ADD,DREG(EAX),0,decode_fetchb());break;
+ case 0x05:gen_discardflags();dyn_dop_word_imm(DOP_ADD,decode.big_op,DREG(EAX));break;
+ case 0x06:dyn_push_seg(es);break;
+ case 0x07:dyn_pop_seg(es);break;
+
+ case 0x08:dyn_dop_ebgb(DOP_OR);break;
+ case 0x09:dyn_dop_evgv(DOP_OR);break;
+ case 0x0a:dyn_dop_gbeb(DOP_OR);break;
+ case 0x0b:dyn_dop_gvev(DOP_OR);break;
+ case 0x0c:gen_discardflags();gen_dop_byte_imm(DOP_OR,DREG(EAX),0,decode_fetchb());break;
+ case 0x0d:gen_discardflags();gen_dop_word_imm(DOP_OR,decode.big_op,DREG(EAX),decode.big_op ? decode_fetchd() : decode_fetchw());break;
+ case 0x0e:dyn_push_seg(cs);break;
+ case 0x0f:
+ {
+ Bitu dual_code=decode_fetchb();
+ switch (dual_code) {
+ /* Short conditional jumps */
+ case 0x80:case 0x81:case 0x82:case 0x83:case 0x84:case 0x85:case 0x86:case 0x87:
+ case 0x88:case 0x89:case 0x8a:case 0x8b:case 0x8c:case 0x8d:case 0x8e:case 0x8f:
+ dyn_branched_exit((BranchTypes)(dual_code&0xf),
+ decode.big_op ? (Bit32s)decode_fetchd() : (Bit16s)decode_fetchw());
+ goto finish_block;
+ /* PUSH/POP FS */
+ case 0xa0:dyn_push_seg(fs);break;
+ case 0xa1:dyn_pop_seg(fs);break;
+ /* SHLD Imm/cl*/
+ case 0xa4:dyn_dshift_ev_gv(true,true);break;
+ case 0xa5:dyn_dshift_ev_gv(true,false);break;
+ /* PUSH/POP GS */
+ case 0xa8:dyn_push_seg(gs);break;
+ case 0xa9:dyn_pop_seg(gs);break;
+ /* SHRD Imm/cl*/
+ case 0xac:dyn_dshift_ev_gv(false,true);break;
+ case 0xad:dyn_dshift_ev_gv(false,false);break;
+ /* Imul Ev,Gv */
+ case 0xaf:dyn_imul_gvev(0);break;
+ /* CMPXCHG */
+ case 0xb1:dyn_cmpxchg_evgv();break;
+ /* LFS,LGS */
+ case 0xb4:
+ dyn_get_modrm();
+ if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
+ dyn_load_seg_off_ea(fs);
+ break;
+ case 0xb5:
+ dyn_get_modrm();
+ if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
+ dyn_load_seg_off_ea(gs);
+ break;
+ /* MOVZX Gv,Eb/Ew */
+ case 0xb6:dyn_mov_ev_gb(false);break;
+ case 0xb7:dyn_mov_ev_gw(false);break;
+ /* MOVSX Gv,Eb/Ew */
+ case 0xbe:dyn_mov_ev_gb(true);break;
+ case 0xbf:dyn_mov_ev_gw(true);break;
+
+ default:
+#if DYN_LOG
+ LOG_MSG("Unhandled dual opcode 0F%02X",dual_code);
+#endif
+ goto illegalopcode;
+ }
+ }break;
+
+ case 0x10:dyn_dop_ebgb(DOP_ADC);break;
+ case 0x11:dyn_dop_evgv(DOP_ADC);break;
+ case 0x12:dyn_dop_gbeb(DOP_ADC);break;
+ case 0x13:dyn_dop_gvev(DOP_ADC);break;
+ case 0x14:gen_needcarry();gen_dop_byte_imm(DOP_ADC,DREG(EAX),0,decode_fetchb());break;
+ case 0x15:gen_needcarry();gen_dop_word_imm(DOP_ADC,decode.big_op,DREG(EAX),decode.big_op ? decode_fetchd() : decode_fetchw());break;
+ case 0x16:dyn_push_seg(ss);break;
+ case 0x17:dyn_pop_seg(ss);break;
+
+ case 0x18:dyn_dop_ebgb(DOP_SBB);break;
+ case 0x19:dyn_dop_evgv(DOP_SBB);break;
+ case 0x1a:dyn_dop_gbeb(DOP_SBB);break;
+ case 0x1b:dyn_dop_gvev(DOP_SBB);break;
+ case 0x1c:gen_needcarry();gen_dop_byte_imm(DOP_SBB,DREG(EAX),0,decode_fetchb());break;
+ case 0x1d:gen_needcarry();gen_dop_word_imm(DOP_SBB,decode.big_op,DREG(EAX),decode.big_op ? decode_fetchd() : decode_fetchw());break;
+ case 0x1e:dyn_push_seg(ds);break;
+ case 0x1f:dyn_pop_seg(ds);break;
+ case 0x20:dyn_dop_ebgb(DOP_AND);break;
+ case 0x21:dyn_dop_evgv(DOP_AND);break;
+ case 0x22:dyn_dop_gbeb(DOP_AND);break;
+ case 0x23:dyn_dop_gvev(DOP_AND);break;
+ case 0x24:gen_discardflags();gen_dop_byte_imm(DOP_AND,DREG(EAX),0,decode_fetchb());break;
+ case 0x25:gen_discardflags();dyn_dop_word_imm(DOP_AND,decode.big_op,DREG(EAX));break;
+ case 0x26:dyn_segprefix(es);goto restart_prefix;
+
+ case 0x28:dyn_dop_ebgb(DOP_SUB);break;
+ case 0x29:dyn_dop_evgv(DOP_SUB);break;
+ case 0x2a:dyn_dop_gbeb(DOP_SUB);break;
+ case 0x2b:dyn_dop_gvev(DOP_SUB);break;
+ case 0x2c:gen_discardflags();gen_dop_byte_imm(DOP_SUB,DREG(EAX),0,decode_fetchb());break;
+ case 0x2d:gen_discardflags();gen_dop_word_imm(DOP_SUB,decode.big_op,DREG(EAX),decode.big_op ? decode_fetchd() : decode_fetchw());break;
+ case 0x2e:dyn_segprefix(cs);goto restart_prefix;
+
+ case 0x30:dyn_dop_ebgb(DOP_XOR);break;
+ case 0x31:dyn_dop_evgv(DOP_XOR);break;
+ case 0x32:dyn_dop_gbeb(DOP_XOR);break;
+ case 0x33:dyn_dop_gvev(DOP_XOR);break;
+ case 0x34:gen_discardflags();gen_dop_byte_imm(DOP_XOR,DREG(EAX),0,decode_fetchb());break;
+ case 0x35:gen_discardflags();gen_dop_word_imm(DOP_XOR,decode.big_op,DREG(EAX),decode.big_op ? decode_fetchd() : decode_fetchw());break;
+ case 0x36:dyn_segprefix(ss);goto restart_prefix;
+
+ case 0x38:dyn_dop_ebgb(DOP_CMP);break;
+ case 0x39:dyn_dop_evgv(DOP_CMP);break;
+ case 0x3a:dyn_dop_gbeb(DOP_CMP);break;
+ case 0x3b:dyn_dop_gvev(DOP_CMP);break;
+ case 0x3c:gen_discardflags();gen_dop_byte_imm(DOP_CMP,DREG(EAX),0,decode_fetchb());break;
+ case 0x3d:gen_discardflags();gen_dop_word_imm(DOP_CMP,decode.big_op,DREG(EAX),decode.big_op ? decode_fetchd() : decode_fetchw());break;
+ case 0x3e:dyn_segprefix(ds);goto restart_prefix;
+
+ /* INC/DEC general register */
+ case 0x40:case 0x41:case 0x42:case 0x43:case 0x44:case 0x45:case 0x46:case 0x47:
+ gen_needcarry();gen_sop_word(SOP_INC,decode.big_op,&DynRegs[opcode&7]);
+ break;
+ case 0x48:case 0x49:case 0x4a:case 0x4b:case 0x4c:case 0x4d:case 0x4e:case 0x4f:
+ gen_needcarry();gen_sop_word(SOP_DEC,decode.big_op,&DynRegs[opcode&7]);
+ break;
+ /* PUSH/POP General register */
+ case 0x50:case 0x51:case 0x52:case 0x53:case 0x55:case 0x56:case 0x57:
+ dyn_push(&DynRegs[opcode&7]);
+ break;
+ case 0x54: /* PUSH SP is special */
+ gen_dop_word(DOP_MOV,true,DREG(TMPW),DREG(ESP));
+ dyn_push(DREG(TMPW));
+ gen_releasereg(DREG(TMPW));
+ break;
+ case 0x58:case 0x59:case 0x5a:case 0x5b:case 0x5c:case 0x5d:case 0x5e:case 0x5f:
+ dyn_pop(&DynRegs[opcode&7]);
+ break;
+ case 0x60: /* PUSHA */
+ gen_dop_word(DOP_MOV,true,DREG(TMPW),DREG(ESP));
+ for (i=G_EAX;i<=G_EDI;i++) {
+ dyn_push_unchecked((i!=G_ESP) ? &DynRegs[i] : DREG(TMPW));
+ }
+ gen_releasereg(DREG(TMPW));
+ break;
+ case 0x61: /* POPA */
+ for (i=G_EDI;i>=G_EAX;i--) {
+ dyn_pop((i!=G_ESP) ? &DynRegs[i] : DREG(TMPW),false);
+ }
+ gen_releasereg(DREG(TMPW));
+ break;
+ //segprefix FS,GS
+ case 0x64:dyn_segprefix(fs);goto restart_prefix;
+ case 0x65:dyn_segprefix(gs);goto restart_prefix;
+ //Push immediates
+ //Operand size
+ case 0x66:decode.big_op=!cpu.code.big;goto restart_prefix;
+ //Address size
+ case 0x67:decode.big_addr=!cpu.code.big;goto restart_prefix;
+ case 0x68: /* PUSH Iv */
+ gen_dop_word_imm(DOP_MOV,decode.big_op,DREG(TMPW),decode.big_op ? decode_fetchd() : decode_fetchw());
+ dyn_push(DREG(TMPW));
+ gen_releasereg(DREG(TMPW));
+ break;
+ /* Imul Ivx */
+ case 0x69:dyn_imul_gvev(decode.big_op ? 4 : 2);break;
+ case 0x6a: /* PUSH Ibx */
+ gen_dop_word_imm(DOP_MOV,true,DREG(TMPW),(Bit8s)decode_fetchb());
+ dyn_push(DREG(TMPW));
+ gen_releasereg(DREG(TMPW));
+ break;
+ /* Imul Ibx */
+ case 0x6b:dyn_imul_gvev(1);break;
+ /* Short conditional jumps */
+ case 0x70:case 0x71:case 0x72:case 0x73:case 0x74:case 0x75:case 0x76:case 0x77:
+ case 0x78:case 0x79:case 0x7a:case 0x7b:case 0x7c:case 0x7d:case 0x7e:case 0x7f:
+ dyn_branched_exit((BranchTypes)(opcode&0xf),(Bit8s)decode_fetchb());
+ goto finish_block;
+ /* Group 1 */
+ case 0x80:dyn_grp1_eb_ib();break;
+ case 0x81:dyn_grp1_ev_ivx(false);break;
+ case 0x82:dyn_grp1_eb_ib();break;
+ case 0x83:dyn_grp1_ev_ivx(true);break;
+ /* TEST Gb,Eb Gv,Ev */
+ case 0x84:dyn_dop_gbeb(DOP_TEST);break;
+ case 0x85:dyn_dop_gvev(DOP_TEST);break;
+ /* XCHG Eb,Gb Ev,Gv */
+ case 0x86:dyn_dop_ebgb(DOP_XCHG);break;
+ case 0x87:dyn_dop_evgv(DOP_XCHG);break;
+ /* MOV e,g and g,e */
+ case 0x88:dyn_mov_ebgb();break;
+ case 0x89:dyn_mov_evgv();break;
+ case 0x8a:dyn_mov_gbeb();break;
+ case 0x8b:dyn_mov_gvev();break;
+ /* MOV ev,seg */
+ case 0x8c:dyn_mov_ev_seg();break;
+ /* LEA Gv */
+ case 0x8d:
+ dyn_get_modrm();
+ if (decode.big_op) {
+ dyn_fill_ea(false,&DynRegs[decode.modrm.reg]);
+ } else {
+ dyn_fill_ea(false);
+ gen_dop_word(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.reg],DREG(EA));
+ gen_releasereg(DREG(EA));
+ }
+ break;
+ /* Mov seg,ev */
+ case 0x8e:dyn_mov_seg_ev();break;
+ /* POP Ev */
+ case 0x8f:dyn_pop_ev();break;
+ case 0x90: //NOP
+ case 0x9b: //WAIT/FWAIT
+ break;
+ //XCHG ax,reg
+ case 0x91:case 0x92:case 0x93:case 0x94:case 0x95:case 0x96:case 0x97:
+ gen_dop_word(DOP_XCHG,decode.big_op,DREG(EAX),&DynRegs[opcode&07]);
+ break;
+ /* CBW/CWDE */
+ case 0x98:
+ gen_cbw(decode.big_op,DREG(EAX));
+ break;
+ /* CWD/CDQ */
+ case 0x99:
+ gen_cwd(decode.big_op,DREG(EAX),DREG(EDX));
+ break;
+ /* CALL FAR Ip */
+ case 0x9a:dyn_call_far_imm();goto finish_block;
+ case 0x9c: //PUSHF
+ gen_protectflags();
+ gen_releasereg(DREG(ESP));
+ dyn_flags_gen_to_host();
+ gen_call_function((void *)&CPU_PUSHF,"%Rd%Id",DREG(TMPB),decode.big_op);
+ dyn_check_bool_exception(DREG(TMPB));
+ gen_releasereg(DREG(TMPB));
+ break;
+ case 0x9d: //POPF
+ gen_releasereg(DREG(ESP));
+ gen_releasereg(DREG(FLAGS));
+ gen_call_function((void *)&CPU_POPF,"%Rd%Id",DREG(TMPB),decode.big_op);
+ dyn_check_bool_exception(DREG(TMPB));
+ dyn_flags_host_to_gen();
+ gen_releasereg(DREG(TMPB));
+ dyn_check_irqrequest();
+ break;
+ /* MOV AL,direct addresses */
+ case 0xa0:
+ gen_lea(DREG(EA),decode.segprefix ? decode.segprefix : DREG(DS),0,0,
+ decode.big_addr ? decode_fetchd() : decode_fetchw());
+ dyn_read_byte_release(DREG(EA),DREG(EAX),false);
+ break;
+ /* MOV AX,direct addresses */
+ case 0xa1:
+ gen_lea(DREG(EA),decode.segprefix ? decode.segprefix : DREG(DS),0,0,
+ decode.big_addr ? decode_fetchd() : decode_fetchw());
+ dyn_read_word_release(DREG(EA),DREG(EAX),decode.big_op);
+ break;
+ /* MOV direct address,AL */
+ case 0xa2:
+ if (decode.big_addr) {
+ Bitu val;
+ if (decode_fetchd_imm(val)) {
+ gen_lea_imm_mem(DREG(EA),decode.segprefix ? decode.segprefix : DREG(DS),(void*)val);
+ } else {
+ gen_lea(DREG(EA),decode.segprefix ? decode.segprefix : DREG(DS),0,0,(Bits)val);
+ }
+ dyn_write_byte_release(DREG(EA),DREG(EAX),false);
+ } else {
+ gen_lea(DREG(EA),decode.segprefix ? decode.segprefix : DREG(DS),0,0,decode_fetchw());
+ dyn_write_byte_release(DREG(EA),DREG(EAX),false);
+ }
+ break;
+ /* MOV direct addresses,AX */
+ case 0xa3:
+ gen_lea(DREG(EA),decode.segprefix ? decode.segprefix : DREG(DS),0,0,
+ decode.big_addr ? decode_fetchd() : decode_fetchw());
+ dyn_write_word_release(DREG(EA),DREG(EAX),decode.big_op);
+ break;
+ /* MOVSB/W/D*/
+ case 0xa4:dyn_string(STR_MOVSB);break;
+ case 0xa5:dyn_string(decode.big_op ? STR_MOVSD : STR_MOVSW);break;
+ /* TEST AL,AX Imm */
+ case 0xa8:gen_discardflags();gen_dop_byte_imm(DOP_TEST,DREG(EAX),0,decode_fetchb());break;
+ case 0xa9:gen_discardflags();gen_dop_word_imm(DOP_TEST,decode.big_op,DREG(EAX),decode.big_op ? decode_fetchd() : decode_fetchw());break;
+ /* STOSB/W/D*/
+ case 0xaa:dyn_string(STR_STOSB);break;
+ case 0xab:dyn_string(decode.big_op ? STR_STOSD : STR_STOSW);break;
+ /* LODSB/W/D*/
+ case 0xac:dyn_string(STR_LODSB);break;
+ case 0xad:dyn_string(decode.big_op ? STR_LODSD : STR_LODSW);break;
+ //Mov Byte reg,Imm byte
+ case 0xb0:case 0xb1:case 0xb2:case 0xb3:case 0xb4:case 0xb5:case 0xb6:case 0xb7:
+ gen_dop_byte_imm(DOP_MOV,&DynRegs[opcode&3],opcode&4,decode_fetchb());
+ break;
+ //Mov word reg imm byte,word,
+ case 0xb8:case 0xb9:case 0xba:case 0xbb:case 0xbc:case 0xbd:case 0xbe:case 0xbf:
+ if (decode.big_op) {
+ dyn_dop_word_imm(DOP_MOV,decode.big_op,&DynRegs[opcode&7]);break;
+ } else {
+ gen_dop_word_imm(DOP_MOV,decode.big_op,&DynRegs[opcode&7],decode_fetchw());break;
+ }
+ break;
+ //GRP2 Eb/Ev,Ib
+ case 0xc0:dyn_grp2_eb(grp2_imm);break;
+ case 0xc1:dyn_grp2_ev(grp2_imm);break;
+ //RET near Iw / Ret
+ case 0xc2:dyn_ret_near(decode_fetchw());goto finish_block;
+ case 0xc3:dyn_ret_near(0);goto finish_block;
+ //LES
+ case 0xc4:
+ dyn_get_modrm();
+ if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
+ dyn_load_seg_off_ea(es);
+ break;
+ //LDS
+ case 0xc5:
+ dyn_get_modrm();
+ if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
+ dyn_load_seg_off_ea(ds);
+ break;
+ // MOV Eb/Ev,Ib/Iv
+ case 0xc6:dyn_mov_ebib();break;
+ case 0xc7:dyn_mov_eviv();break;
+ //ENTER and LEAVE
+ case 0xc8:dyn_enter();break;
+ case 0xc9:dyn_leave();break;
+ //RET far Iw / Ret
+ case 0xca:dyn_ret_far(decode_fetchw());goto finish_block;
+ case 0xcb:dyn_ret_far(0);goto finish_block;
+ /* Interrupt */
+#if !(C_DEBUG)
+ case 0xcd:dyn_interrupt(decode_fetchb());goto finish_block;
+#endif
+ /* IRET */
+ case 0xcf:dyn_iret();goto finish_block;
+
+ //GRP2 Eb/Ev,1
+ case 0xd0:dyn_grp2_eb(grp2_1);break;
+ case 0xd1:dyn_grp2_ev(grp2_1);break;
+ //GRP2 Eb/Ev,CL
+ case 0xd2:dyn_grp2_eb(grp2_cl);break;
+ case 0xd3:dyn_grp2_ev(grp2_cl);break;
+ //FPU
+#ifdef CPU_FPU
+ case 0xd8:
+#ifdef X86_DYNFPU_DH_ENABLED
+ if (dyn_dh_fpu.dh_fpu_enabled) {
+ if (fpu_used) dh_fpu_esc0();
+ else {
+ dh_fpu_startup();
+ dh_fpu_esc0();
+ }
+ } else
+#endif
+ dyn_fpu_esc0();
+ break;
+ case 0xd9:
+#ifdef X86_DYNFPU_DH_ENABLED
+ if (dyn_dh_fpu.dh_fpu_enabled) {
+ if (fpu_used) dh_fpu_esc1();
+ else {
+ dh_fpu_startup();
+ dh_fpu_esc1();
+ }
+ } else
+#endif
+ dyn_fpu_esc1();
+ break;
+ case 0xda:
+#ifdef X86_DYNFPU_DH_ENABLED
+ if (dyn_dh_fpu.dh_fpu_enabled) {
+ if (fpu_used) dh_fpu_esc2();
+ else {
+ dh_fpu_startup();
+ dh_fpu_esc2();
+ }
+ } else
+#endif
+ dyn_fpu_esc2();
+ break;
+ case 0xdb:
+#ifdef X86_DYNFPU_DH_ENABLED
+ if (dyn_dh_fpu.dh_fpu_enabled) {
+ if (fpu_used) dh_fpu_esc3();
+ else {
+ dh_fpu_startup();
+ dh_fpu_esc3();
+ }
+ } else
+#endif
+ dyn_fpu_esc3();
+ break;
+ case 0xdc:
+#ifdef X86_DYNFPU_DH_ENABLED
+ if (dyn_dh_fpu.dh_fpu_enabled) {
+ if (fpu_used) dh_fpu_esc4();
+ else {
+ dh_fpu_startup();
+ dh_fpu_esc4();
+ }
+ } else
+#endif
+ dyn_fpu_esc4();
+ break;
+ case 0xdd:
+#ifdef X86_DYNFPU_DH_ENABLED
+ if (dyn_dh_fpu.dh_fpu_enabled) {
+ if (fpu_used) dh_fpu_esc5();
+ else {
+ dh_fpu_startup();
+ dh_fpu_esc5();
+ }
+ } else
+#endif
+ dyn_fpu_esc5();
+ break;
+ case 0xde:
+#ifdef X86_DYNFPU_DH_ENABLED
+ if (dyn_dh_fpu.dh_fpu_enabled) {
+ if (fpu_used) dh_fpu_esc6();
+ else {
+ dh_fpu_startup();
+ dh_fpu_esc6();
+ }
+ } else
+#endif
+ dyn_fpu_esc6();
+ break;
+ case 0xdf:
+#ifdef X86_DYNFPU_DH_ENABLED
+ if (dyn_dh_fpu.dh_fpu_enabled) {
+ if (fpu_used) dh_fpu_esc7();
+ else {
+ dh_fpu_startup();
+ dh_fpu_esc7();
+ }
+ } else
+#endif
+ dyn_fpu_esc7();
+ break;
+#endif
+ //Loops
+ case 0xe2:dyn_loop(LOOP_NONE);goto finish_block;
+ case 0xe3:dyn_loop(LOOP_JCXZ);goto finish_block;
+ //IN AL/AX,imm
+ case 0xe4: {
+ Bitu port=decode_fetchb();
+ dyn_add_iocheck_var(port,1);
+ gen_call_function((void*)&IO_ReadB,"%Id%Rl",port,DREG(EAX));
+ } break;
+ case 0xe5: {
+ Bitu port=decode_fetchb();
+ dyn_add_iocheck_var(port,decode.big_op?4:2);
+ if (decode.big_op) {
+ gen_call_function((void*)&IO_ReadD,"%Id%Rd",port,DREG(EAX));
+ } else {
+ gen_call_function((void*)&IO_ReadW,"%Id%Rw",port,DREG(EAX));
+ }
+ } break;
+ //OUT imm,AL
+ case 0xe6: {
+ Bitu port=decode_fetchb();
+ dyn_add_iocheck_var(port,1);
+ gen_call_function((void*)&IO_WriteB,"%Id%Dl",port,DREG(EAX));
+ } break;
+ case 0xe7: {
+ Bitu port=decode_fetchb();
+ dyn_add_iocheck_var(port,decode.big_op?4:2);
+ if (decode.big_op) {
+ gen_call_function((void*)&IO_WriteD,"%Id%Dd",port,DREG(EAX));
+ } else {
+ gen_call_function((void*)&IO_WriteW,"%Id%Dw",port,DREG(EAX));
+ }
+ } break;
+ case 0xe8: /* CALL Ivx */
+ dyn_call_near_imm();
+ goto finish_block;
+ case 0xe9: /* Jmp Ivx */
+ dyn_exit_link(decode.big_op ? (Bit32s)decode_fetchd() : (Bit16s)decode_fetchw());
+ goto finish_block;
+ case 0xea: /* JMP FAR Ip */
+ dyn_jmp_far_imm();
+ goto finish_block;
+ /* Jmp Ibx */
+ case 0xeb:dyn_exit_link((Bit8s)decode_fetchb());goto finish_block;
+ /* IN AL/AX,DX*/
+ case 0xec:
+ dyn_add_iocheck(1);
+ gen_call_function((void*)&IO_ReadB,"%Dw%Rl",DREG(EDX),DREG(EAX));
+ break;
+ case 0xed:
+ dyn_add_iocheck(decode.big_op?4:2);
+ if (decode.big_op) {
+ gen_call_function((void*)&IO_ReadD,"%Dw%Rd",DREG(EDX),DREG(EAX));
+ } else {
+ gen_call_function((void*)&IO_ReadW,"%Dw%Rw",DREG(EDX),DREG(EAX));
+ }
+ break;
+ /* OUT DX,AL/AX */
+ case 0xee:
+ dyn_add_iocheck(1);
+ gen_call_function((void*)&IO_WriteB,"%Dw%Dl",DREG(EDX),DREG(EAX));
+ break;
+ case 0xef:
+ dyn_add_iocheck(decode.big_op?4:2);
+ if (decode.big_op) {
+ gen_call_function((void*)&IO_WriteD,"%Dw%Dd",DREG(EDX),DREG(EAX));
+ } else {
+ gen_call_function((void*)&IO_WriteW,"%Dw%Dw",DREG(EDX),DREG(EAX));
+ }
+ break;
+ case 0xf0: //LOCK
+ break;
+ case 0xf2: //REPNE/NZ
+ decode.rep=REP_NZ;
+ goto restart_prefix;
+ case 0xf3: //REPE/Z
+ decode.rep=REP_Z;
+ goto restart_prefix;
+ /* Change carry flag */
+ case 0xf5: //CMC
+ case 0xf8: //CLC
+ case 0xf9: //STC
+ gen_needflags();
+ cache_addb(opcode);break;
+ /* GRP 3 Eb/EV */
+ case 0xf6:dyn_grp3_eb();break;
+ case 0xf7:dyn_grp3_ev();break;
+ /* Change interrupt flag */
+ case 0xfa: //CLI
+ gen_releasereg(DREG(FLAGS));
+ gen_call_function((void *)&CPU_CLI,"%Rd",DREG(TMPB));
+ dyn_check_bool_exception(DREG(TMPB));
+ gen_releasereg(DREG(TMPB));
+ break;
+ case 0xfb: //STI
+ gen_releasereg(DREG(FLAGS));
+ gen_call_function((void *)&CPU_STI,"%Rd",DREG(TMPB));
+ dyn_check_bool_exception(DREG(TMPB));
+ gen_releasereg(DREG(TMPB));
+ dyn_check_irqrequest();
+ if (max_opcodes<=0) max_opcodes=1; //Allow 1 extra opcode
+ break;
+ case 0xfc: //CLD
+ gen_protectflags();
+ gen_dop_word_imm(DOP_AND,true,DREG(FLAGS),~FLAG_DF);
+ gen_save_host_direct(&cpu.direction,1);
+ break;
+ case 0xfd: //STD
+ gen_protectflags();
+ gen_dop_word_imm(DOP_OR,true,DREG(FLAGS),FLAG_DF);
+ gen_save_host_direct(&cpu.direction,-1);
+ break;
+ /* GRP 4 Eb and callback's */
+ case 0xfe:
+ dyn_get_modrm();
+ switch (decode.modrm.reg) {
+ case 0x0://INC Eb
+ case 0x1://DEC Eb
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();dyn_read_byte(DREG(EA),DREG(TMPB),false);
+ gen_needcarry();
+ gen_sop_byte(decode.modrm.reg==0 ? SOP_INC : SOP_DEC,DREG(TMPB),0);
+ dyn_write_byte_release(DREG(EA),DREG(TMPB),false);
+ gen_releasereg(DREG(TMPB));
+ } else {
+ gen_needcarry();
+ gen_sop_byte(decode.modrm.reg==0 ? SOP_INC : SOP_DEC,
+ &DynRegs[decode.modrm.rm&3],decode.modrm.rm&4);
+ }
+ break;
+ case 0x7: //CALBACK Iw
+ gen_save_host_direct(&core_dyn.callback,decode_fetchw());
+ dyn_set_eip_end();
+ dyn_reduce_cycles();
+ dyn_save_critical_regs();
+ gen_return(BR_CallBack);
+ dyn_closeblock();
+ goto finish_block;
+ }
+ break;
+
+ case 0xff:
+ {
+ dyn_get_modrm();DynReg * src;
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea();
+ dyn_read_word(DREG(EA),DREG(TMPW),decode.big_op);
+ src=DREG(TMPW);
+ } else src=&DynRegs[decode.modrm.rm];
+ switch (decode.modrm.reg) {
+ case 0x0://INC Ev
+ case 0x1://DEC Ev
+ gen_needcarry();
+ gen_sop_word(decode.modrm.reg==0 ? SOP_INC : SOP_DEC,decode.big_op,src);
+ if (decode.modrm.mod<3){
+ dyn_write_word_release(DREG(EA),DREG(TMPW),decode.big_op);
+ gen_releasereg(DREG(TMPW));
+ }
+ break;
+ case 0x2: /* CALL Ev */
+ gen_lea(DREG(TMPB),DREG(EIP),0,0,decode.code-decode.code_start);
+ dyn_push(DREG(TMPB));
+ gen_releasereg(DREG(TMPB));
+ gen_dop_word(DOP_MOV,decode.big_op,DREG(EIP),src);
+ goto core_close_block;
+ case 0x4: /* JMP Ev */
+ gen_dop_word(DOP_MOV,decode.big_op,DREG(EIP),src);
+ goto core_close_block;
+ case 0x3: /* CALL Ep */
+ case 0x5: /* JMP Ep */
+ gen_protectflags();
+ dyn_flags_gen_to_host();
+ gen_lea(DREG(EA),DREG(EA),0,0,decode.big_op ? 4: 2);
+ dyn_read_word(DREG(EA),DREG(EA),false);
+ dyn_set_eip_last_end(DREG(TMPB));
+ dyn_save_critical_regs();
+ gen_call_function(
+ decode.modrm.reg == 3 ? (void*)&CPU_CALL : (void*)&CPU_JMP,
+ decode.big_op ? "%Id%Drw%Drd%Drd" : "%Id%Drw%Drw%Drd",
+ decode.big_op,DREG(EA),DREG(TMPW),DREG(TMPB));
+ dyn_flags_host_to_gen();
+ goto core_close_block;
+ case 0x6: /* PUSH Ev */
+ gen_releasereg(DREG(EA));
+ dyn_push(src);
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("CPU:GRP5:Illegal opcode 0xff");
+ goto illegalopcode;
+ }}
+ break;
+ default:
+#if DYN_LOG
+// LOG_MSG("Dynamic unhandled opcode %X",opcode);
+#endif
+ goto illegalopcode;
+ }
+ }
+ // link to next block because the maximum number of opcodes has been reached
+ dyn_set_eip_end();
+ dyn_reduce_cycles();
+ dyn_save_critical_regs();
+ gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlock,cache.start));
+ dyn_closeblock();
+ goto finish_block;
+core_close_block:
+ dyn_reduce_cycles();
+ dyn_save_critical_regs();
+ gen_return(BR_Normal);
+ dyn_closeblock();
+ goto finish_block;
+illegalopcode:
+ dyn_set_eip_last();
+ dyn_reduce_cycles();
+ dyn_save_critical_regs();
+ gen_return(BR_Opcode);
+ dyn_closeblock();
+ goto finish_block;
+#if (C_DEBUG)
+ dyn_set_eip_last();
+ dyn_reduce_cycles();
+ dyn_save_critical_regs();
+ gen_return(BR_OpcodeFull);
+ dyn_closeblock();
+ goto finish_block;
+#endif
+finish_block:
+ /* Setup the correct end-address */
+ decode.active_block->page.end=--decode.page.index;
+// LOG_MSG("Created block size %d start %d end %d",decode.block->cache.size,decode.block->page.start,decode.block->page.end);
+ return decode.block;
+}
diff --git a/src/cpu/core_dyn_x86/dyn_fpu.h b/src/cpu/core_dyn_x86/dyn_fpu.h
new file mode 100644
index 000000000..ec4afc681
--- /dev/null
+++ b/src/cpu/core_dyn_x86/dyn_fpu.h
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#if C_FPU
+
+#include <math.h>
+#include <float.h>
+#include "cross.h"
+#include "mem.h"
+#include "fpu.h"
+#include "cpu.h"
+
+
+static void FPU_FDECSTP(){
+ TOP = (TOP - 1) & 7;
+}
+
+static void FPU_FINCSTP(){
+ TOP = (TOP + 1) & 7;
+}
+
+static void FPU_FNSTCW(PhysPt addr){
+ mem_writew(addr,fpu.cw);
+}
+
+static void FPU_FFREE(Bitu st) {
+ fpu.tags[st]=TAG_Empty;
+}
+
+
+#if C_FPU_X86
+#include "../../fpu/fpu_instructions_x86.h"
+#else
+#include "../../fpu/fpu_instructions.h"
+#endif
+
+
+#define dyn_fpu_top() { \
+ gen_protectflags(); \
+ gen_load_host(&TOP,DREG(EA),4); \
+ gen_dop_word_imm(DOP_ADD,true,DREG(EA),decode.modrm.rm); \
+ gen_dop_word_imm(DOP_AND,true,DREG(EA),7); \
+ gen_load_host(&TOP,DREG(TMPB),4); \
+}
+
+static void dyn_eatree() {
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ switch (group){
+ case 0x00: /* FADD ST,STi */
+ gen_call_function((void*)&FPU_FADD_EA,"%Ddr",DREG(TMPB));
+ break;
+ case 0x01: /* FMUL ST,STi */
+ gen_call_function((void*)&FPU_FMUL_EA,"%Ddr",DREG(TMPB));
+ break;
+ case 0x02: /* FCOM STi */
+ gen_call_function((void*)&FPU_FCOM_EA,"%Ddr",DREG(TMPB));
+ break;
+ case 0x03: /* FCOMP STi */
+ gen_call_function((void*)&FPU_FCOM_EA,"%Ddr",DREG(TMPB));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ case 0x04: /* FSUB ST,STi */
+ gen_call_function((void*)&FPU_FSUB_EA,"%Ddr",DREG(TMPB));
+ break;
+ case 0x05: /* FSUBR ST,STi */
+ gen_call_function((void*)&FPU_FSUBR_EA,"%Ddr",DREG(TMPB));
+ break;
+ case 0x06: /* FDIV ST,STi */
+ gen_call_function((void*)&FPU_FDIV_EA,"%Ddr",DREG(TMPB));
+ break;
+ case 0x07: /* FDIVR ST,STi */
+ gen_call_function((void*)&FPU_FDIVR_EA,"%Ddr",DREG(TMPB));
+ break;
+ default:
+ break;
+ }
+}
+
+static void dyn_fpu_esc0(){
+ dyn_get_modrm();
+ if (decode.modrm.val >= 0xc0) {
+ dyn_fpu_top();
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ switch (group){
+ case 0x00: //FADD ST,STi /
+ gen_call_function((void*)&FPU_FADD,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ case 0x01: // FMUL ST,STi /
+ gen_call_function((void*)&FPU_FMUL,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ case 0x02: // FCOM STi /
+ gen_call_function((void*)&FPU_FCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ case 0x03: // FCOMP STi /
+ gen_call_function((void*)&FPU_FCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ case 0x04: // FSUB ST,STi /
+ gen_call_function((void*)&FPU_FSUB,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ case 0x05: // FSUBR ST,STi /
+ gen_call_function((void*)&FPU_FSUBR,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ case 0x06: // FDIV ST,STi /
+ gen_call_function((void*)&FPU_FDIV,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ case 0x07: // FDIVR ST,STi /
+ gen_call_function((void*)&FPU_FDIVR,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ default:
+ break;
+ }
+ } else {
+ dyn_fill_ea();
+ gen_call_function((void*)&FPU_FLD_F32_EA,"%Ddr",DREG(EA));
+ gen_load_host(&TOP,DREG(TMPB),4);
+ dyn_eatree();
+ }
+}
+
+static void dyn_fpu_esc1(){
+ dyn_get_modrm();
+ if (decode.modrm.val >= 0xc0) {
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ switch (group){
+ case 0x00: /* FLD STi */
+ gen_protectflags();
+ gen_load_host(&TOP,DREG(EA),4);
+ gen_dop_word_imm(DOP_ADD,true,DREG(EA),decode.modrm.rm);
+ gen_dop_word_imm(DOP_AND,true,DREG(EA),7);
+ gen_call_function((void*)&FPU_PREP_PUSH,"");
+ gen_load_host(&TOP,DREG(TMPB),4);
+ gen_call_function((void*)&FPU_FST,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x01: /* FXCH STi */
+ dyn_fpu_top();
+ gen_call_function((void*)&FPU_FXCH,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ case 0x02: /* FNOP */
+ gen_call_function((void*)&FPU_FNOP,"");
+ break;
+ case 0x03: /* FSTP STi */
+ dyn_fpu_top();
+ gen_call_function((void*)&FPU_FST,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ case 0x04:
+ switch(sub){
+ case 0x00: /* FCHS */
+ gen_call_function((void*)&FPU_FCHS,"");
+ break;
+ case 0x01: /* FABS */
+ gen_call_function((void*)&FPU_FABS,"");
+ break;
+ case 0x02: /* UNKNOWN */
+ case 0x03: /* ILLEGAL */
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",group,sub);
+ break;
+ case 0x04: /* FTST */
+ gen_call_function((void*)&FPU_FTST,"");
+ break;
+ case 0x05: /* FXAM */
+ gen_call_function((void*)&FPU_FXAM,"");
+ break;
+ case 0x06: /* FTSTP (cyrix)*/
+ case 0x07: /* UNKNOWN */
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",group,sub);
+ break;
+ }
+ break;
+ case 0x05:
+ switch(sub){
+ case 0x00: /* FLD1 */
+ gen_call_function((void*)&FPU_FLD1,"");
+ break;
+ case 0x01: /* FLDL2T */
+ gen_call_function((void*)&FPU_FLDL2T,"");
+ break;
+ case 0x02: /* FLDL2E */
+ gen_call_function((void*)&FPU_FLDL2E,"");
+ break;
+ case 0x03: /* FLDPI */
+ gen_call_function((void*)&FPU_FLDPI,"");
+ break;
+ case 0x04: /* FLDLG2 */
+ gen_call_function((void*)&FPU_FLDLG2,"");
+ break;
+ case 0x05: /* FLDLN2 */
+ gen_call_function((void*)&FPU_FLDLN2,"");
+ break;
+ case 0x06: /* FLDZ*/
+ gen_call_function((void*)&FPU_FLDZ,"");
+ break;
+ case 0x07: /* ILLEGAL */
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",group,sub);
+ break;
+ }
+ break;
+ case 0x06:
+ switch(sub){
+ case 0x00: /* F2XM1 */
+ gen_call_function((void*)&FPU_F2XM1,"");
+ break;
+ case 0x01: /* FYL2X */
+ gen_call_function((void*)&FPU_FYL2X,"");
+ break;
+ case 0x02: /* FPTAN */
+ gen_call_function((void*)&FPU_FPTAN,"");
+ break;
+ case 0x03: /* FPATAN */
+ gen_call_function((void*)&FPU_FPATAN,"");
+ break;
+ case 0x04: /* FXTRACT */
+ gen_call_function((void*)&FPU_FXTRACT,"");
+ break;
+ case 0x05: /* FPREM1 */
+ gen_call_function((void*)&FPU_FPREM1,"");
+ break;
+ case 0x06: /* FDECSTP */
+ gen_call_function((void*)&FPU_FDECSTP,"");
+ break;
+ case 0x07: /* FINCSTP */
+ gen_call_function((void*)&FPU_FINCSTP,"");
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",group,sub);
+ break;
+ }
+ break;
+ case 0x07:
+ switch(sub){
+ case 0x00: /* FPREM */
+ gen_call_function((void*)&FPU_FPREM,"");
+ break;
+ case 0x01: /* FYL2XP1 */
+ gen_call_function((void*)&FPU_FYL2XP1,"");
+ break;
+ case 0x02: /* FSQRT */
+ gen_call_function((void*)&FPU_FSQRT,"");
+ break;
+ case 0x03: /* FSINCOS */
+ gen_call_function((void*)&FPU_FSINCOS,"");
+ break;
+ case 0x04: /* FRNDINT */
+ gen_call_function((void*)&FPU_FRNDINT,"");
+ break;
+ case 0x05: /* FSCALE */
+ gen_call_function((void*)&FPU_FSCALE,"");
+ break;
+ case 0x06: /* FSIN */
+ gen_call_function((void*)&FPU_FSIN,"");
+ break;
+ case 0x07: /* FCOS */
+ gen_call_function((void*)&FPU_FCOS,"");
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",group,sub);
+ break;
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",group,sub);
+ break;
+ }
+ } else {
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ dyn_fill_ea();
+ switch(group){
+ case 0x00: /* FLD float*/
+ gen_protectflags();
+ gen_call_function((void*)&FPU_PREP_PUSH,"");
+ gen_load_host(&TOP,DREG(TMPB),4);
+ gen_call_function((void*)&FPU_FLD_F32,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x01: /* UNKNOWN */
+ LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",group,sub);
+ break;
+ case 0x02: /* FST float*/
+ gen_call_function((void*)&FPU_FST_F32,"%Ddr",DREG(EA));
+ break;
+ case 0x03: /* FSTP float*/
+ gen_call_function((void*)&FPU_FST_F32,"%Ddr",DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ case 0x04: /* FLDENV */
+ gen_call_function((void*)&FPU_FLDENV,"%Ddr",DREG(EA));
+ break;
+ case 0x05: /* FLDCW */
+ gen_call_function((void *)&FPU_FLDCW,"%Ddr",DREG(EA));
+ break;
+ case 0x06: /* FSTENV */
+ gen_call_function((void *)&FPU_FSTENV,"%Ddr",DREG(EA));
+ break;
+ case 0x07: /* FNSTCW*/
+ gen_call_function((void *)&FPU_FNSTCW,"%Ddr",DREG(EA));
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ }
+}
+
+static void dyn_fpu_esc2(){
+ dyn_get_modrm();
+ if (decode.modrm.val >= 0xc0) {
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ switch(group){
+ case 0x05:
+ switch(sub){
+ case 0x01: /* FUCOMPP */
+ gen_protectflags();
+ gen_load_host(&TOP,DREG(EA),4);
+ gen_dop_word_imm(DOP_ADD,true,DREG(EA),1);
+ gen_dop_word_imm(DOP_AND,true,DREG(EA),7);
+ gen_load_host(&TOP,DREG(TMPB),4);
+ gen_call_function((void *)&FPU_FUCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ gen_call_function((void *)&FPU_FPOP,"");
+ gen_call_function((void *)&FPU_FPOP,"");
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 2:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 2:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ } else {
+ dyn_fill_ea();
+ gen_call_function((void*)&FPU_FLD_I32_EA,"%Ddr",DREG(EA));
+ gen_load_host(&TOP,DREG(TMPB),4);
+ dyn_eatree();
+ }
+}
+
+static void dyn_fpu_esc3(){
+ dyn_get_modrm();
+ if (decode.modrm.val >= 0xc0) {
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ switch (group) {
+ case 0x04:
+ switch (sub) {
+ case 0x00: //FNENI
+ case 0x01: //FNDIS
+ LOG(LOG_FPU,LOG_ERROR)("8087 only fpu code used esc 3: group 4: subfuntion :%d",sub);
+ break;
+ case 0x02: //FNCLEX FCLEX
+ gen_call_function((void*)&FPU_FCLEX,"");
+ break;
+ case 0x03: //FNINIT FINIT
+ gen_call_function((void*)&FPU_FINIT,"");
+ break;
+ case 0x04: //FNSETPM
+ case 0x05: //FRSTPM
+// LOG(LOG_FPU,LOG_ERROR)("80267 protected mode (un)set. Nothing done");
+ break;
+ default:
+ E_Exit("ESC 3:ILLEGAL OPCODE group %d subfunction %d",group,sub);
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 3:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ } else {
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ dyn_fill_ea();
+ switch(group){
+ case 0x00: /* FILD */
+ gen_call_function((void*)&FPU_PREP_PUSH,"");
+ gen_protectflags();
+ gen_load_host(&TOP,DREG(TMPB),4);
+ gen_call_function((void*)&FPU_FLD_I32,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x01: /* FISTTP */
+ LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",group,sub);
+ break;
+ case 0x02: /* FIST */
+ gen_call_function((void*)&FPU_FST_I32,"%Ddr",DREG(EA));
+ break;
+ case 0x03: /* FISTP */
+ gen_call_function((void*)&FPU_FST_I32,"%Ddr",DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ case 0x05: /* FLD 80 Bits Real */
+ gen_call_function((void*)&FPU_PREP_PUSH,"");
+ gen_call_function((void*)&FPU_FLD_F80,"%Ddr",DREG(EA));
+ break;
+ case 0x07: /* FSTP 80 Bits Real */
+ gen_call_function((void*)&FPU_FST_F80,"%Ddr",DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",group,sub);
+ }
+ }
+}
+
+static void dyn_fpu_esc4(){
+ dyn_get_modrm();
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ if (decode.modrm.val >= 0xc0) {
+ dyn_fpu_top();
+ switch(group){
+ case 0x00: /* FADD STi,ST*/
+ gen_call_function((void*)&FPU_FADD,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x01: /* FMUL STi,ST*/
+ gen_call_function((void*)&FPU_FMUL,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x02: /* FCOM*/
+ gen_call_function((void*)&FPU_FCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ case 0x03: /* FCOMP*/
+ gen_call_function((void*)&FPU_FCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ case 0x04: /* FSUBR STi,ST*/
+ gen_call_function((void*)&FPU_FSUBR,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x05: /* FSUB STi,ST*/
+ gen_call_function((void*)&FPU_FSUB,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x06: /* FDIVR STi,ST*/
+ gen_call_function((void*)&FPU_FDIVR,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x07: /* FDIV STi,ST*/
+ gen_call_function((void*)&FPU_FDIV,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ default:
+ break;
+ }
+ } else {
+ dyn_fill_ea();
+ gen_call_function((void*)&FPU_FLD_F64_EA,"%Ddr",DREG(EA));
+ gen_load_host(&TOP,DREG(TMPB),4);
+ dyn_eatree();
+ }
+}
+
+static void dyn_fpu_esc5(){
+ dyn_get_modrm();
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ if (decode.modrm.val >= 0xc0) {
+ dyn_fpu_top();
+ switch(group){
+ case 0x00: /* FFREE STi */
+ gen_call_function((void*)&FPU_FFREE,"%Ddr",DREG(EA));
+ break;
+ case 0x01: /* FXCH STi*/
+ gen_call_function((void*)&FPU_FXCH,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ case 0x02: /* FST STi */
+ gen_call_function((void*)&FPU_FST,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ case 0x03: /* FSTP STi*/
+ gen_call_function((void*)&FPU_FST,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ case 0x04: /* FUCOM STi */
+ gen_call_function((void*)&FPU_FUCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ case 0x05: /*FUCOMP STi */
+ gen_call_function((void*)&FPU_FUCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 5:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ gen_releasereg(DREG(EA));
+ gen_releasereg(DREG(TMPB));
+ } else {
+ dyn_fill_ea();
+ switch(group){
+ case 0x00: /* FLD double real*/
+ gen_call_function((void*)&FPU_PREP_PUSH,"");
+ gen_protectflags();
+ gen_load_host(&TOP,DREG(TMPB),4);
+ gen_call_function((void*)&FPU_FLD_F64,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x01: /* FISTTP longint*/
+ LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",group,sub);
+ break;
+ case 0x02: /* FST double real*/
+ gen_call_function((void*)&FPU_FST_F64,"%Ddr",DREG(EA));
+ break;
+ case 0x03: /* FSTP double real*/
+ gen_call_function((void*)&FPU_FST_F64,"%Ddr",DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ case 0x04: /* FRSTOR */
+ gen_call_function((void*)&FPU_FRSTOR,"%Ddr",DREG(EA));
+ break;
+ case 0x06: /* FSAVE */
+ gen_call_function((void*)&FPU_FSAVE,"%Ddr",DREG(EA));
+ break;
+ case 0x07: /*FNSTSW */
+ gen_protectflags();
+ gen_load_host(&TOP,DREG(TMPB),4);
+ gen_call_function((void*)&FPU_SET_TOP,"%Dd",DREG(TMPB));
+ gen_load_host(&fpu.sw,DREG(TMPB),4);
+ gen_call_function((void*)&mem_writew,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",group,sub);
+ }
+ }
+}
+
+static void dyn_fpu_esc6(){
+ dyn_get_modrm();
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ if (decode.modrm.val >= 0xc0) {
+ dyn_fpu_top();
+ switch(group){
+ case 0x00: /*FADDP STi,ST*/
+ gen_call_function((void*)&FPU_FADD,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x01: /* FMULP STi,ST*/
+ gen_call_function((void*)&FPU_FMUL,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x02: /* FCOMP5*/
+ gen_call_function((void*)&FPU_FCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break; /* TODO IS THIS ALLRIGHT ????????? */
+ case 0x03: /*FCOMPP*/
+ if(sub != 1) {
+ LOG(LOG_FPU,LOG_WARN)("ESC 6:Unhandled group %d subfunction %d",group,sub);
+ return;
+ }
+ gen_load_host(&TOP,DREG(EA),4);
+ gen_dop_word_imm(DOP_ADD,true,DREG(EA),1);
+ gen_dop_word_imm(DOP_AND,true,DREG(EA),7);
+ gen_call_function((void*)&FPU_FCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,""); /* extra pop at the bottom*/
+ break;
+ case 0x04: /* FSUBRP STi,ST*/
+ gen_call_function((void*)&FPU_FSUBR,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x05: /* FSUBP STi,ST*/
+ gen_call_function((void*)&FPU_FSUB,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x06: /* FDIVRP STi,ST*/
+ gen_call_function((void*)&FPU_FDIVR,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x07: /* FDIVP STi,ST*/
+ gen_call_function((void*)&FPU_FDIV,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ default:
+ break;
+ }
+ gen_call_function((void*)&FPU_FPOP,"");
+ } else {
+ dyn_fill_ea();
+ gen_call_function((void*)&FPU_FLD_I16_EA,"%Ddr",DREG(EA));
+ gen_load_host(&TOP,DREG(TMPB),4);
+ dyn_eatree();
+ }
+}
+
+static void dyn_fpu_esc7(){
+ dyn_get_modrm();
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ if (decode.modrm.val >= 0xc0) {
+ switch (group){
+ case 0x00: /* FFREEP STi*/
+ dyn_fpu_top();
+ gen_call_function((void*)&FPU_FFREE,"%Ddr",DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ case 0x01: /* FXCH STi*/
+ dyn_fpu_top();
+ gen_call_function((void*)&FPU_FXCH,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ break;
+ case 0x02: /* FSTP STi*/
+ case 0x03: /* FSTP STi*/
+ dyn_fpu_top();
+ gen_call_function((void*)&FPU_FST,"%Ddr%Ddr",DREG(TMPB),DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ case 0x04:
+ switch(sub){
+ case 0x00: /* FNSTSW AX*/
+ gen_load_host(&TOP,DREG(TMPB),4);
+ gen_call_function((void*)&FPU_SET_TOP,"%Ddr",DREG(TMPB));
+ gen_mov_host(&fpu.sw,DREG(EAX),2);
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ } else {
+ dyn_fill_ea();
+ switch(group){
+ case 0x00: /* FILD Bit16s */
+ gen_call_function((void*)&FPU_PREP_PUSH,"");
+ gen_load_host(&TOP,DREG(TMPB),4);
+ gen_call_function((void*)&FPU_FLD_I16,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x01:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",group,sub);
+ break;
+ case 0x02: /* FIST Bit16s */
+ gen_call_function((void*)&FPU_FST_I16,"%Ddr",DREG(EA));
+ break;
+ case 0x03: /* FISTP Bit16s */
+ gen_call_function((void*)&FPU_FST_I16,"%Ddr",DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ case 0x04: /* FBLD packed BCD */
+ gen_call_function((void*)&FPU_PREP_PUSH,"");
+ gen_load_host(&TOP,DREG(TMPB),4);
+ gen_call_function((void*)&FPU_FBLD,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x05: /* FILD Bit64s */
+ gen_call_function((void*)&FPU_PREP_PUSH,"");
+ gen_load_host(&TOP,DREG(TMPB),4);
+ gen_call_function((void*)&FPU_FLD_I64,"%Ddr%Ddr",DREG(EA),DREG(TMPB));
+ break;
+ case 0x06: /* FBSTP packed BCD */
+ gen_call_function((void*)&FPU_FBST,"%Ddr",DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ case 0x07: /* FISTP Bit64s */
+ gen_call_function((void*)&FPU_FST_I64,"%Ddr",DREG(EA));
+ gen_call_function((void*)&FPU_FPOP,"");
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ }
+}
+
+#endif
diff --git a/src/cpu/core_dyn_x86/dyn_fpu_dh.h b/src/cpu/core_dyn_x86/dyn_fpu_dh.h
new file mode 100644
index 000000000..03b6bd5d4
--- /dev/null
+++ b/src/cpu/core_dyn_x86/dyn_fpu_dh.h
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#if C_FPU
+
+static void FPU_FLD_16(PhysPt addr) {
+ dyn_dh_fpu.temp.m1 = (Bit32u)mem_readw(addr);
+}
+
+static void FPU_FST_16(PhysPt addr) {
+ mem_writew(addr,(Bit16u)dyn_dh_fpu.temp.m1);
+}
+
+static void FPU_FLD_32(PhysPt addr) {
+ dyn_dh_fpu.temp.m1 = mem_readd(addr);
+}
+
+static void FPU_FST_32(PhysPt addr) {
+ mem_writed(addr,dyn_dh_fpu.temp.m1);
+}
+
+static void FPU_FLD_64(PhysPt addr) {
+ dyn_dh_fpu.temp.m1 = mem_readd(addr);
+ dyn_dh_fpu.temp.m2 = mem_readd(addr+4);
+}
+
+static void FPU_FST_64(PhysPt addr) {
+ mem_writed(addr,dyn_dh_fpu.temp.m1);
+ mem_writed(addr+4,dyn_dh_fpu.temp.m2);
+}
+
+static void FPU_FLD_80(PhysPt addr) {
+ dyn_dh_fpu.temp.m1 = mem_readd(addr);
+ dyn_dh_fpu.temp.m2 = mem_readd(addr+4);
+ dyn_dh_fpu.temp.m3 = mem_readw(addr+8);
+}
+
+static void FPU_FST_80(PhysPt addr) {
+ mem_writed(addr,dyn_dh_fpu.temp.m1);
+ mem_writed(addr+4,dyn_dh_fpu.temp.m2);
+ mem_writew(addr+8,dyn_dh_fpu.temp.m3);
+}
+
+static void FPU_FLDCW_DH(PhysPt addr){
+ dyn_dh_fpu.cw = mem_readw(addr);
+ dyn_dh_fpu.temp.m1 = (Bit32u)(dyn_dh_fpu.cw|0x3f);
+}
+
+static void FPU_FNSTCW_DH(PhysPt addr){
+ mem_writew(addr,(Bit16u)(dyn_dh_fpu.cw&0xffff));
+}
+
+static void FPU_FNINIT_DH(void){
+ dyn_dh_fpu.cw = 0x37f;
+}
+
+static void FPU_FSTENV_DH(PhysPt addr){
+ if(!cpu.code.big) {
+ mem_writew(addr+0,(Bit16u)dyn_dh_fpu.cw);
+ mem_writew(addr+2,(Bit16u)dyn_dh_fpu.temp.m2);
+ mem_writew(addr+4,dyn_dh_fpu.temp.m3);
+ } else {
+ mem_writed(addr+0,dyn_dh_fpu.temp.m1);
+ mem_writew(addr+0,(Bit16u)dyn_dh_fpu.cw);
+ mem_writed(addr+4,dyn_dh_fpu.temp.m2);
+ mem_writed(addr+8,dyn_dh_fpu.temp.m3);
+ }
+}
+
+static void FPU_FLDENV_DH(PhysPt addr){
+ if(!cpu.code.big) {
+ dyn_dh_fpu.cw = (Bit32u)mem_readw(addr);
+ dyn_dh_fpu.temp.m1 = dyn_dh_fpu.cw|0x3f;
+ dyn_dh_fpu.temp.m2 = (Bit32u)mem_readw(addr+2);
+ dyn_dh_fpu.temp.m3 = mem_readw(addr+4);
+ } else {
+ dyn_dh_fpu.cw = (Bit32u)mem_readw(addr);
+ dyn_dh_fpu.temp.m1 = mem_readd(addr)|0x3f;
+ dyn_dh_fpu.temp.m2 = mem_readd(addr+4);
+ dyn_dh_fpu.temp.m3 = mem_readw(addr+8);
+ dyn_dh_fpu.temp.d1 = mem_readw(addr+10);
+ }
+}
+
+static void FPU_FSAVE_DH(PhysPt addr){
+ if (!cpu.code.big) {
+ mem_writew(addr,(Bit16u)dyn_dh_fpu.cw);
+ addr+=2;
+ mem_writeb(addr++,dyn_dh_fpu.temp_state[0x04]);
+ mem_writeb(addr++,dyn_dh_fpu.temp_state[0x05]);
+ mem_writeb(addr++,dyn_dh_fpu.temp_state[0x08]);
+ mem_writeb(addr++,dyn_dh_fpu.temp_state[0x09]);
+ mem_writeb(addr++,dyn_dh_fpu.temp_state[0x0c]);
+ mem_writeb(addr++,dyn_dh_fpu.temp_state[0x0d]);
+ mem_writeb(addr++,dyn_dh_fpu.temp_state[0x10]);
+ mem_writeb(addr++,dyn_dh_fpu.temp_state[0x11]);
+ mem_writeb(addr++,dyn_dh_fpu.temp_state[0x14]);
+ mem_writeb(addr++,dyn_dh_fpu.temp_state[0x15]);
+ mem_writeb(addr++,dyn_dh_fpu.temp_state[0x18]);
+ mem_writeb(addr++,dyn_dh_fpu.temp_state[0x19]);
+ for(Bitu i=28;i<108;i++) mem_writeb(addr++,dyn_dh_fpu.temp_state[i]);
+ } else {
+ mem_writew(addr,(Bit16u)dyn_dh_fpu.cw);
+ addr+=2;
+ for(Bitu i=2;i<108;i++) mem_writeb(addr++,dyn_dh_fpu.temp_state[i]);
+ }
+}
+
+static void FPU_FRSTOR_DH(PhysPt addr){
+ if (!cpu.code.big) {
+ dyn_dh_fpu.cw = (Bit32u)mem_readw(addr);
+ dyn_dh_fpu.temp_state[0x00] = mem_readb(addr++)|0x3f;
+ dyn_dh_fpu.temp_state[0x01] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0x04] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0x05] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0x08] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0x09] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0x0c] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0x0d] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0x10] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0x11] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0x14] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0x15] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0x18] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0x19] = mem_readb(addr++);
+ for(Bitu i=28;i<108;i++) dyn_dh_fpu.temp_state[i] = mem_readb(addr++);
+ } else {
+ dyn_dh_fpu.cw = (Bit32u)mem_readw(addr);
+ for(Bitu i=0;i<108;i++) dyn_dh_fpu.temp_state[i] = mem_readb(addr++);
+ dyn_dh_fpu.temp_state[0]|=0x3f;
+ }
+}
+
+static void dh_fpu_esc0(){
+ dyn_get_modrm();
+ if (decode.modrm.val >= 0xc0) {
+ cache_addb(0xd8);
+ cache_addb(decode.modrm.val);
+ } else {
+ dyn_fill_ea();
+ gen_call_function((void*)&FPU_FLD_32,"%Ddr",DREG(EA));
+ cache_addb(0xd8);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ }
+}
+
+static void dh_fpu_esc1(){
+ dyn_get_modrm();
+ if (decode.modrm.val >= 0xc0) {
+ cache_addb(0xd9);
+ cache_addb(decode.modrm.val);
+ } else {
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ dyn_fill_ea();
+ switch(group){
+ case 0x00: /* FLD float*/
+ gen_call_function((void*)&FPU_FLD_32,"%Ddr",DREG(EA));
+ cache_addb(0xd9);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ break;
+ case 0x01: /* UNKNOWN */
+ LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",group,sub);
+ break;
+ case 0x02: /* FST float*/
+ cache_addb(0xd9);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FST_32,"%Ddr",DREG(EA));
+ break;
+ case 0x03: /* FSTP float*/
+ cache_addb(0xd9);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FST_32,"%Ddr",DREG(EA));
+ break;
+ case 0x04: /* FLDENV */
+ gen_call_function((void*)&FPU_FLDENV_DH,"%Ddr",DREG(EA));
+ cache_addb(0xd9);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ break;
+ case 0x05: /* FLDCW */
+ gen_call_function((void *)&FPU_FLDCW_DH,"%Ddr",DREG(EA));
+ cache_addb(0xd9);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ break;
+ case 0x06: /* FSTENV */
+ cache_addb(0xd9);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FSTENV_DH,"%Ddr",DREG(EA));
+ break;
+ case 0x07: /* FNSTCW*/
+ gen_call_function((void*)&FPU_FNSTCW_DH,"%Ddr",DREG(EA));
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ }
+}
+
+static void dh_fpu_esc2(){
+ dyn_get_modrm();
+ if (decode.modrm.val >= 0xc0) {
+ cache_addb(0xda);
+ cache_addb(decode.modrm.val);
+ } else {
+ dyn_fill_ea();
+ gen_call_function((void*)&FPU_FLD_32,"%Ddr",DREG(EA));
+ cache_addb(0xda);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ }
+}
+
+static void dh_fpu_esc3(){
+ dyn_get_modrm();
+ if (decode.modrm.val >= 0xc0) {
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ switch (group) {
+ case 0x04:
+ switch (sub) {
+ case 0x00: //FNENI
+ case 0x01: //FNDIS
+ LOG(LOG_FPU,LOG_ERROR)("8087 only fpu code used esc 3: group 4: subfuntion :%d",sub);
+ break;
+ case 0x02: //FNCLEX FCLEX
+ cache_addb(0xdb);
+ cache_addb(decode.modrm.val);
+ break;
+ case 0x03: //FNINIT FINIT
+ gen_call_function((void*)&FPU_FNINIT_DH,"");
+ cache_addb(0xdb);
+ cache_addb(decode.modrm.val);
+ break;
+ case 0x04: //FNSETPM
+ case 0x05: //FRSTPM
+// LOG(LOG_FPU,LOG_ERROR)("80267 protected mode (un)set. Nothing done");
+ break;
+ default:
+ E_Exit("ESC 3:ILLEGAL OPCODE group %d subfunction %d",group,sub);
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 3:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ } else {
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ dyn_fill_ea();
+ switch(group){
+ case 0x00: /* FILD */
+ gen_call_function((void*)&FPU_FLD_32,"%Ddr",DREG(EA));
+ cache_addb(0xdb);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ break;
+ case 0x01: /* FISTTP */
+ LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",group,sub);
+ break;
+ case 0x02: /* FIST */
+ cache_addb(0xdb);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FST_32,"%Ddr",DREG(EA));
+ break;
+ case 0x03: /* FISTP */
+ cache_addb(0xdb);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FST_32,"%Ddr",DREG(EA));
+ break;
+ case 0x05: /* FLD 80 Bits Real */
+ gen_call_function((void*)&FPU_FLD_80,"%Ddr",DREG(EA));
+ cache_addb(0xdb);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ break;
+ case 0x07: /* FSTP 80 Bits Real */
+ cache_addb(0xdb);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FST_80,"%Ddr",DREG(EA));
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",group,sub);
+ }
+ }
+}
+
+static void dh_fpu_esc4(){
+ dyn_get_modrm();
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ if (decode.modrm.val >= 0xc0) {
+ cache_addb(0xdc);
+ cache_addb(decode.modrm.val);
+ } else {
+ dyn_fill_ea();
+ gen_call_function((void*)&FPU_FLD_64,"%Ddr",DREG(EA));
+ cache_addb(0xdc);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ }
+}
+
+static void dh_fpu_esc5(){
+ dyn_get_modrm();
+ if (decode.modrm.val >= 0xc0) {
+ cache_addb(0xdd);
+ cache_addb(decode.modrm.val);
+ } else {
+ dyn_fill_ea();
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ switch(group){
+ case 0x00: /* FLD double real*/
+ gen_call_function((void*)&FPU_FLD_64,"%Ddr",DREG(EA));
+ cache_addb(0xdd);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ break;
+ case 0x01: /* FISTTP longint*/
+ LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",group,sub);
+ break;
+ case 0x02: /* FST double real*/
+ cache_addb(0xdd);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FST_64,"%Ddr",DREG(EA));
+ break;
+ case 0x03: /* FSTP double real*/
+ cache_addb(0xdd);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FST_64,"%Ddr",DREG(EA));
+ break;
+ case 0x04: /* FRSTOR */
+ gen_call_function((void*)&FPU_FRSTOR_DH,"%Ddr",DREG(EA));
+ cache_addb(0xdd);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp_state[0])));
+ break;
+ case 0x06: /* FSAVE */
+ cache_addb(0xdd);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp_state[0])));
+ gen_call_function((void*)&FPU_FSAVE_DH,"%Ddr",DREG(EA));
+ cache_addb(0xdb);
+ cache_addb(0xe3);
+ break;
+ case 0x07: /* FNSTSW */
+ cache_addb(0xdd);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FST_16,"%Ddr",DREG(EA));
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",group,sub);
+ }
+ }
+}
+
+static void dh_fpu_esc6(){
+ dyn_get_modrm();
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ if (decode.modrm.val >= 0xc0) {
+ cache_addb(0xde);
+ cache_addb(decode.modrm.val);
+ } else {
+ dyn_fill_ea();
+ gen_call_function((void*)&FPU_FLD_16,"%Ddr",DREG(EA));
+ cache_addb(0xde);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ }
+}
+
+static void dh_fpu_esc7(){
+ dyn_get_modrm();
+ Bitu group=(decode.modrm.val >> 3) & 7;
+ Bitu sub=(decode.modrm.val & 7);
+ if (decode.modrm.val >= 0xc0) {
+ switch (group){
+ case 0x00: /* FFREEP STi*/
+ cache_addb(0xdf);
+ cache_addb(decode.modrm.val);
+ break;
+ case 0x01: /* FXCH STi*/
+ cache_addb(0xdf);
+ cache_addb(decode.modrm.val);
+ break;
+ case 0x02: /* FSTP STi*/
+ case 0x03: /* FSTP STi*/
+ cache_addb(0xdf);
+ cache_addb(decode.modrm.val);
+ break;
+ case 0x04:
+ switch(sub){
+ case 0x00: /* FNSTSW AX*/
+ cache_addb(0xdd);
+ cache_addb(0x05|(0x07<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_load_host(&(dyn_dh_fpu.temp.m1),DREG(TMPB),4);
+ gen_dop_word(DOP_MOV,false,DREG(EAX),DREG(TMPB));
+ gen_releasereg(DREG(TMPB));
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ } else {
+ dyn_fill_ea();
+ switch(group){
+ case 0x00: /* FILD Bit16s */
+ gen_call_function((void*)&FPU_FLD_16,"%Ddr",DREG(EA));
+ cache_addb(0xdf);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ break;
+ case 0x01:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",group,sub);
+ break;
+ case 0x02: /* FIST Bit16s */
+ cache_addb(0xdf);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FST_16,"%Ddr",DREG(EA));
+ break;
+ case 0x03: /* FISTP Bit16s */
+ cache_addb(0xdf);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FST_16,"%Ddr",DREG(EA));
+ break;
+ case 0x04: /* FBLD packed BCD */
+ gen_call_function((void*)&FPU_FLD_80,"%Ddr",DREG(EA));
+ cache_addb(0xdf);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ break;
+ case 0x05: /* FILD Bit64s */
+ gen_call_function((void*)&FPU_FLD_64,"%Ddr",DREG(EA));
+ cache_addb(0xdf);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ break;
+ case 0x06: /* FBSTP packed BCD */
+ cache_addb(0xdf);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FST_80,"%Ddr",DREG(EA));
+ break;
+ case 0x07: /* FISTP Bit64s */
+ cache_addb(0xdf);
+ cache_addb(0x05|(decode.modrm.reg<<3));
+ cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1)));
+ gen_call_function((void*)&FPU_FST_64,"%Ddr",DREG(EA));
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ }
+}
+
+#endif
diff --git a/src/cpu/core_dyn_x86/helpers.h b/src/cpu/core_dyn_x86/helpers.h
new file mode 100644
index 000000000..0cea15cda
--- /dev/null
+++ b/src/cpu/core_dyn_x86/helpers.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+static bool dyn_helper_divb(Bit8u val) {
+ if (!val) return CPU_PrepareException(0,0);
+ Bitu quo=reg_ax / val;
+ Bit8u rem=(Bit8u)(reg_ax % val);
+ Bit8u quo8=(Bit8u)(quo&0xff);
+ if (quo>0xff) return CPU_PrepareException(0,0);
+ reg_ah=rem;
+ reg_al=quo8;
+ return false;
+}
+
+static bool dyn_helper_idivb(Bit8s val) {
+ if (!val) return CPU_PrepareException(0,0);
+ Bits quo=(Bit16s)reg_ax / val;
+ Bit8s rem=(Bit8s)((Bit16s)reg_ax % val);
+ Bit8s quo8s=(Bit8s)(quo&0xff);
+ if (quo!=(Bit16s)quo8s) return CPU_PrepareException(0,0);
+ reg_ah=rem;
+ reg_al=quo8s;
+ return false;
+}
+
+static bool dyn_helper_divw(Bit16u val) {
+ if (!val) return CPU_PrepareException(0,0);
+ Bitu num=(reg_dx<<16)|reg_ax;
+ Bitu quo=num/val;
+ Bit16u rem=(Bit16u)(num % val);
+ Bit16u quo16=(Bit16u)(quo&0xffff);
+ if (quo!=(Bit32u)quo16) return CPU_PrepareException(0,0);
+ reg_dx=rem;
+ reg_ax=quo16;
+ return false;
+}
+
+static bool dyn_helper_idivw(Bit16s val) {
+ if (!val) return CPU_PrepareException(0,0);
+ Bits num=(reg_dx<<16)|reg_ax;
+ Bits quo=num/val;
+ Bit16s rem=(Bit16s)(num % val);
+ Bit16s quo16s=(Bit16s)quo;
+ if (quo!=(Bit32s)quo16s) return CPU_PrepareException(0,0);
+ reg_dx=rem;
+ reg_ax=quo16s;
+ return false;
+}
+
+static bool dyn_helper_divd(Bit32u val) {
+ if (!val) return CPU_PrepareException(0,0);
+ Bit64u num=(((Bit64u)reg_edx)<<32)|reg_eax;
+ Bit64u quo=num/val;
+ Bit32u rem=(Bit32u)(num % val);
+ Bit32u quo32=(Bit32u)(quo&0xffffffff);
+ if (quo!=(Bit64u)quo32) return CPU_PrepareException(0,0);
+ reg_edx=rem;
+ reg_eax=quo32;
+ return false;
+}
+
+static bool dyn_helper_idivd(Bit32s val) {
+ if (!val) return CPU_PrepareException(0,0);
+ Bit64s num=(((Bit64u)reg_edx)<<32)|reg_eax;
+ Bit64s quo=num/val;
+ Bit32s rem=(Bit32s)(num % val);
+ Bit32s quo32s=(Bit32s)(quo&0xffffffff);
+ if (quo!=(Bit64s)quo32s) return CPU_PrepareException(0,0);
+ reg_edx=rem;
+ reg_eax=quo32s;
+ return false;
+}
diff --git a/src/cpu/core_dyn_x86/risc_x86.h b/src/cpu/core_dyn_x86/risc_x86.h
new file mode 100644
index 000000000..5bf626ee5
--- /dev/null
+++ b/src/cpu/core_dyn_x86/risc_x86.h
@@ -0,0 +1,1072 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+static void gen_init(void);
+
+/* End of needed */
+
+#define X86_REGS 7
+#define X86_REG_EAX 0x00
+#define X86_REG_ECX 0x01
+#define X86_REG_EDX 0x02
+#define X86_REG_EBX 0x03
+#define X86_REG_EBP 0x04
+#define X86_REG_ESI 0x05
+#define X86_REG_EDI 0x06
+
+#define X86_REG_MASK(_REG_) (1 << X86_REG_ ## _REG_)
+
+static struct {
+ bool flagsactive;
+ Bitu last_used;
+ GenReg * regs[X86_REGS];
+} x86gen;
+
+class GenReg {
+public:
+ GenReg(Bit8u _index) {
+ index=_index;
+ notusable=false;dynreg=0;
+ }
+ DynReg * dynreg;
+ Bitu last_used; //Keeps track of last assigned regs
+ Bit8u index;
+ bool notusable;
+ void Load(DynReg * _dynreg,bool stale=false) {
+ if (!_dynreg) return;
+ if (GCC_UNLIKELY((Bitu)dynreg)) Clear();
+ dynreg=_dynreg;
+ last_used=x86gen.last_used;
+ dynreg->flags&=~DYNFLG_CHANGED;
+ dynreg->genreg=this;
+ if ((!stale) && (dynreg->flags & (DYNFLG_LOAD|DYNFLG_ACTIVE))) {
+ cache_addw(0x058b+(index << (8+3))); //Mov reg,[data]
+ cache_addd((Bit32u)dynreg->data);
+ }
+ dynreg->flags|=DYNFLG_ACTIVE;
+ }
+ void Save(void) {
+ if (GCC_UNLIKELY(!((Bitu)dynreg))) IllegalOption("GenReg->Save");
+ dynreg->flags&=~DYNFLG_CHANGED;
+ cache_addw(0x0589+(index << (8+3))); //Mov [data],reg
+ cache_addd((Bit32u)dynreg->data);
+ }
+ void Release(void) {
+ if (GCC_UNLIKELY(!((Bitu)dynreg))) return;
+ if (dynreg->flags&DYNFLG_CHANGED && dynreg->flags&DYNFLG_SAVE) {
+ Save();
+ }
+ dynreg->flags&=~(DYNFLG_CHANGED|DYNFLG_ACTIVE);
+ dynreg->genreg=0;dynreg=0;
+ }
+ void Clear(void) {
+ if (!dynreg) return;
+ if (dynreg->flags&DYNFLG_CHANGED) {
+ Save();
+ }
+ dynreg->genreg=0;dynreg=0;
+ }
+};
+
+static BlockReturn gen_runcode(Bit8u * code) {
+ BlockReturn retval;
+#if defined (_MSC_VER)
+ __asm {
+/* Prepare the flags */
+ mov eax,[code]
+ push ebx
+ push ebp
+ push esi
+ push edi
+ mov ebx,[reg_flags]
+ and ebx,FMASK_TEST
+ push offset(return_address)
+ push ebx
+ jmp eax
+/* Restore the flags */
+return_address:
+ /* return here with flags in ecx */
+ and dword ptr [reg_flags],~FMASK_TEST
+ and ecx,FMASK_TEST
+ or [reg_flags],ecx
+ pop edi
+ pop esi
+ pop ebp
+ pop ebx
+ mov [retval],eax
+ }
+#elif defined (MACOSX)
+ register Bit32u tempflags=reg_flags & FMASK_TEST;
+ __asm__ volatile (
+ "pushl %%ebx \n"
+ "pushl %%ebp \n"
+ "pushl $(run_return_adress) \n"
+ "pushl %2 \n"
+ "jmp *%3 \n"
+ "run_return_adress: \n"
+ "popl %%ebp \n"
+ "popl %%ebx \n"
+ :"=a" (retval), "=c" (tempflags)
+ :"r" (tempflags),"r" (code)
+ :"%edx","%edi","%esi","cc","memory"
+ );
+ reg_flags=(reg_flags & ~FMASK_TEST) | (tempflags & FMASK_TEST);
+#else
+ register Bit32u tempflags=reg_flags & FMASK_TEST;
+ __asm__ volatile (
+ "pushl %%ebp \n"
+ "pushl $(run_return_adress) \n"
+ "pushl %2 \n"
+ "jmp *%3 \n"
+ "run_return_adress: \n"
+ "popl %%ebp \n"
+ :"=a" (retval), "=c" (tempflags)
+ :"r" (tempflags),"r" (code)
+ :"%edx","%ebx","%edi","%esi","cc","memory"
+ );
+ reg_flags=(reg_flags & ~FMASK_TEST) | (tempflags & FMASK_TEST);
+#endif
+ return retval;
+}
+
+static GenReg * FindDynReg(DynReg * dynreg,bool stale=false) {
+ x86gen.last_used++;
+ if (dynreg->genreg) {
+ dynreg->genreg->last_used=x86gen.last_used;
+ return dynreg->genreg;
+ }
+ /* Find best match for selected global reg */
+ Bits i;
+ Bits first_used,first_index;
+ first_used=-1;
+ if (dynreg->flags & DYNFLG_HAS8) {
+ /* Has to be eax,ebx,ecx,edx */
+ for (i=first_index=0;i<=X86_REG_EBX;i++) {
+ GenReg * genreg=x86gen.regs[i];
+ if (genreg->notusable) continue;
+ if (!(genreg->dynreg)) {
+ genreg->Load(dynreg,stale);
+ return genreg;
+ }
+ if (genreg->last_used<(Bitu)first_used) {
+ first_used=genreg->last_used;
+ first_index=i;
+ }
+ }
+ } else {
+ for (i=first_index=X86_REGS-1;i>=0;i--) {
+ GenReg * genreg=x86gen.regs[i];
+ if (genreg->notusable) continue;
+ if (!(genreg->dynreg)) {
+ genreg->Load(dynreg,stale);
+ return genreg;
+ }
+ if (genreg->last_used<(Bitu)first_used) {
+ first_used=genreg->last_used;
+ first_index=i;
+ }
+ }
+ }
+ /* No free register found use earliest assigned one */
+ GenReg * newreg=x86gen.regs[first_index];
+ newreg->Load(dynreg,stale);
+ return newreg;
+}
+
+static GenReg * ForceDynReg(GenReg * genreg,DynReg * dynreg) {
+ genreg->last_used=++x86gen.last_used;
+ if (dynreg->genreg==genreg) return genreg;
+ if (genreg->dynreg) genreg->Clear();
+ if (dynreg->genreg) dynreg->genreg->Clear();
+ genreg->Load(dynreg);
+ return genreg;
+}
+
+static void gen_preloadreg(DynReg * dynreg) {
+ FindDynReg(dynreg);
+}
+
+static void gen_releasereg(DynReg * dynreg) {
+ GenReg * genreg=dynreg->genreg;
+ if (genreg) genreg->Release();
+ else dynreg->flags&=~(DYNFLG_ACTIVE|DYNFLG_CHANGED);
+}
+
+static void gen_setupreg(DynReg * dnew,DynReg * dsetup) {
+ dnew->flags=dsetup->flags;
+ if (dnew->genreg==dsetup->genreg) return;
+ /* Not the same genreg must be wrong */
+ if (dnew->genreg) {
+ /* Check if the genreg i'm changing is actually linked to me */
+ if (dnew->genreg->dynreg==dnew) dnew->genreg->dynreg=0;
+ }
+ dnew->genreg=dsetup->genreg;
+ if (dnew->genreg) dnew->genreg->dynreg=dnew;
+}
+
+static void gen_synchreg(DynReg * dnew,DynReg * dsynch) {
+ /* First make sure the registers match */
+ if (dnew->genreg!=dsynch->genreg) {
+ if (dnew->genreg) dnew->genreg->Clear();
+ if (dsynch->genreg) {
+ dsynch->genreg->Load(dnew);
+ }
+ }
+ /* Always use the loadonce flag from either state */
+ dnew->flags|=(dsynch->flags & dnew->flags&DYNFLG_ACTIVE);
+ if ((dnew->flags ^ dsynch->flags) & DYNFLG_CHANGED) {
+ /* Ensure the changed value gets saved */
+ if (dnew->flags & DYNFLG_CHANGED) {
+ dnew->genreg->Save();
+ } else dnew->flags|=DYNFLG_CHANGED;
+ }
+}
+
+static void gen_needflags(void) {
+ if (!x86gen.flagsactive) {
+ x86gen.flagsactive=true;
+ cache_addb(0x9d); //POPFD
+ }
+}
+
+static void gen_protectflags(void) {
+ if (x86gen.flagsactive) {
+ x86gen.flagsactive=false;
+ cache_addb(0x9c); //PUSHFD
+ }
+}
+
+static void gen_discardflags(void) {
+ if (!x86gen.flagsactive) {
+ x86gen.flagsactive=true;
+ cache_addw(0xc483); //ADD ESP,4
+ cache_addb(0x4);
+ }
+}
+
+static void gen_needcarry(void) {
+ if (!x86gen.flagsactive) {
+ x86gen.flagsactive=true;
+ cache_addw(0x2cd1); //SHR DWORD [ESP],1
+ cache_addb(0x24);
+ cache_addd(0x0424648d); //LEA ESP,[ESP+4]
+ }
+}
+
+static void gen_setzeroflag(void) {
+ if (x86gen.flagsactive) IllegalOption("gen_setzeroflag");
+ cache_addw(0x0c83); //OR DWORD [ESP],0x40
+ cache_addw(0x4024);
+}
+
+static void gen_clearzeroflag(void) {
+ if (x86gen.flagsactive) IllegalOption("gen_clearzeroflag");
+ cache_addw(0x2483); //AND DWORD [ESP],~0x40
+ cache_addw(0xbf24);
+}
+
+static bool skip_flags=false;
+
+static void set_skipflags(bool state) {
+ if (!state) gen_discardflags();
+ skip_flags=state;
+}
+
+static void gen_reinit(void) {
+ x86gen.last_used=0;
+ x86gen.flagsactive=false;
+ for (Bitu i=0;i<X86_REGS;i++) {
+ x86gen.regs[i]->dynreg=0;
+ }
+}
+
+
+static void gen_load_host(void * data,DynReg * dr1,Bitu size) {
+ GenReg * gr1=FindDynReg(dr1,true);
+ switch (size) {
+ case 1:cache_addw(0xb60f);break; //movzx byte
+ case 2:cache_addw(0xb70f);break; //movzx word
+ case 4:cache_addb(0x8b);break; //mov
+ default:
+ IllegalOption("gen_load_host");
+ }
+ cache_addb(0x5+(gr1->index<<3));
+ cache_addd((Bit32u)data);
+ dr1->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_mov_host(void * data,DynReg * dr1,Bitu size,Bit8u di1=0) {
+ GenReg * gr1=FindDynReg(dr1,(size==4));
+ switch (size) {
+ case 1:cache_addb(0x8a);break; //mov byte
+ case 2:cache_addb(0x66); //mov word
+ case 4:cache_addb(0x8b);break; //mov
+ default:
+ IllegalOption("gen_load_host");
+ }
+ cache_addb(0x5+((gr1->index+(di1?4:0))<<3));
+ cache_addd((Bit32u)data);
+ dr1->flags|=DYNFLG_CHANGED;
+}
+
+
+static void gen_dop_byte(DualOps op,DynReg * dr1,Bit8u di1,DynReg * dr2,Bit8u di2) {
+ GenReg * gr1=FindDynReg(dr1);GenReg * gr2=FindDynReg(dr2);
+ Bit8u tmp;
+ switch (op) {
+ case DOP_ADD: tmp=0x02; break;
+ case DOP_ADC: tmp=0x12; break;
+ case DOP_SUB: tmp=0x2a; break;
+ case DOP_SBB: tmp=0x1a; break;
+ case DOP_CMP: tmp=0x3a; goto nochange;
+ case DOP_XOR: tmp=0x32; break;
+ case DOP_AND: tmp=0x22; if ((dr1==dr2) && (di1==di2)) goto nochange; break;
+ case DOP_OR: tmp=0x0a; if ((dr1==dr2) && (di1==di2)) goto nochange; break;
+ case DOP_TEST: tmp=0x84; goto nochange;
+ case DOP_MOV: if ((dr1==dr2) && (di1==di2)) return; tmp=0x8a; break;
+ case DOP_XCHG: tmp=0x86; dr2->flags|=DYNFLG_CHANGED; break;
+ default:
+ IllegalOption("gen_dop_byte");
+ }
+ dr1->flags|=DYNFLG_CHANGED;
+nochange:
+ cache_addw(tmp|(0xc0+((gr1->index+di1)<<3)+gr2->index+di2)<<8);
+}
+
+static void gen_dop_byte_imm(DualOps op,DynReg * dr1,Bit8u di1,Bitu imm) {
+ GenReg * gr1=FindDynReg(dr1);
+ Bit16u tmp;
+ switch (op) {
+ case DOP_ADD: tmp=0xc080; break;
+ case DOP_ADC: tmp=0xd080; break;
+ case DOP_SUB: tmp=0xe880; break;
+ case DOP_SBB: tmp=0xd880; break;
+ case DOP_CMP: tmp=0xf880; goto nochange; //Doesn't change
+ case DOP_XOR: tmp=0xf080; break;
+ case DOP_AND: tmp=0xe080; break;
+ case DOP_OR: tmp=0xc880; break;
+ case DOP_TEST: tmp=0xc0f6; goto nochange; //Doesn't change
+ case DOP_MOV: cache_addb(0xb0+gr1->index+di1);
+ dr1->flags|=DYNFLG_CHANGED;
+ goto finish;
+ default:
+ IllegalOption("gen_dop_byte_imm");
+ }
+ dr1->flags|=DYNFLG_CHANGED;
+nochange:
+ cache_addw(tmp+((gr1->index+di1)<<8));
+finish:
+ cache_addb(imm);
+}
+
+static void gen_dop_byte_imm_mem(DualOps op,DynReg * dr1,Bit8u di1,void* data) {
+ GenReg * gr1=FindDynReg(dr1);
+ Bit16u tmp;
+ switch (op) {
+ case DOP_ADD: tmp=0x0502; break;
+ case DOP_ADC: tmp=0x0512; break;
+ case DOP_SUB: tmp=0x052a; break;
+ case DOP_SBB: tmp=0x051a; break;
+ case DOP_CMP: tmp=0x053a; goto nochange; //Doesn't change
+ case DOP_XOR: tmp=0x0532; break;
+ case DOP_AND: tmp=0x0522; break;
+ case DOP_OR: tmp=0x050a; break;
+ case DOP_TEST: tmp=0x0584; goto nochange; //Doesn't change
+ case DOP_MOV: tmp=0x058A; break;
+ default:
+ IllegalOption("gen_dop_byte_imm_mem");
+ }
+ dr1->flags|=DYNFLG_CHANGED;
+nochange:
+ cache_addw(tmp+((gr1->index+di1)<<11));
+ cache_addd((Bit32u)data);
+}
+
+static void gen_sop_byte(SingleOps op,DynReg * dr1,Bit8u di1) {
+ GenReg * gr1=FindDynReg(dr1);
+ Bit16u tmp;
+ switch (op) {
+ case SOP_INC: tmp=0xc0FE; break;
+ case SOP_DEC: tmp=0xc8FE; break;
+ case SOP_NOT: tmp=0xd0f6; break;
+ case SOP_NEG: tmp=0xd8f6; break;
+ default:
+ IllegalOption("gen_sop_byte");
+ }
+ cache_addw(tmp + ((gr1->index+di1)<<8));
+ dr1->flags|=DYNFLG_CHANGED;
+}
+
+
+static void gen_extend_word(bool sign,DynReg * ddr,DynReg * dsr) {
+ GenReg * gsr=FindDynReg(dsr);
+ GenReg * gdr=FindDynReg(ddr,true);
+ if (sign) cache_addw(0xbf0f);
+ else cache_addw(0xb70f);
+ cache_addb(0xc0+(gdr->index<<3)+(gsr->index));
+ ddr->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_extend_byte(bool sign,bool dword,DynReg * ddr,DynReg * dsr,Bit8u dsi) {
+ GenReg * gsr=FindDynReg(dsr);
+ GenReg * gdr=FindDynReg(ddr,dword);
+ if (!dword) cache_addb(0x66);
+ if (sign) cache_addw(0xbe0f);
+ else cache_addw(0xb60f);
+ cache_addb(0xc0+(gdr->index<<3)+(gsr->index+dsi));
+ ddr->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_lea(DynReg * ddr,DynReg * dsr1,DynReg * dsr2,Bitu scale,Bits imm) {
+ GenReg * gdr=FindDynReg(ddr);
+ Bitu imm_size;
+ Bit8u rm_base=(gdr->index << 3);
+ if (dsr1) {
+ GenReg * gsr1=FindDynReg(dsr1);
+ if (!imm && (gsr1->index!=0x5)) {
+ imm_size=0; rm_base+=0x0; //no imm
+ } else if ((imm>=-128 && imm<=127)) {
+ imm_size=1;rm_base+=0x40; //Signed byte imm
+ } else {
+ imm_size=4;rm_base+=0x80; //Signed dword imm
+ }
+ if (dsr2) {
+ GenReg * gsr2=FindDynReg(dsr2);
+ cache_addb(0x8d); //LEA
+ cache_addb(rm_base+0x4); //The sib indicator
+ Bit8u sib=(gsr1->index)+(gsr2->index<<3)+(scale<<6);
+ cache_addb(sib);
+ } else {
+ if ((ddr==dsr1) && !imm_size) return;
+ cache_addb(0x8d); //LEA
+ cache_addb(rm_base+gsr1->index);
+ }
+ } else {
+ if (dsr2) {
+ GenReg * gsr2=FindDynReg(dsr2);
+ cache_addb(0x8d); //LEA
+ cache_addb(rm_base+0x4); //The sib indicator
+ Bit8u sib=(5+(gsr2->index<<3)+(scale<<6));
+ cache_addb(sib);
+ imm_size=4;
+ } else {
+ cache_addb(0x8d); //LEA
+ cache_addb(rm_base+0x05); //dword imm
+ imm_size=4;
+ }
+ }
+ switch (imm_size) {
+ case 0: break;
+ case 1:cache_addb(imm);break;
+ case 4:cache_addd(imm);break;
+ }
+ ddr->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_lea_imm_mem(DynReg * ddr,DynReg * dsr,void* data) {
+ GenReg * gdr=FindDynReg(ddr);
+ Bit8u rm_base=(gdr->index << 3);
+ cache_addw(0x058b+(rm_base<<8));
+ cache_addd((Bit32u)data);
+ GenReg * gsr=FindDynReg(dsr);
+ cache_addb(0x8d); //LEA
+ cache_addb(rm_base+0x44);
+ cache_addb(rm_base+gsr->index);
+ cache_addb(0x00);
+ ddr->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_dop_word(DualOps op,bool dword,DynReg * dr1,DynReg * dr2) {
+ GenReg * gr2=FindDynReg(dr2);
+ GenReg * gr1=FindDynReg(dr1,dword && op==DOP_MOV);
+ Bit8u tmp;
+ switch (op) {
+ case DOP_ADD: tmp=0x03; break;
+ case DOP_ADC: tmp=0x13; break;
+ case DOP_SUB: tmp=0x2b; break;
+ case DOP_SBB: tmp=0x1b; break;
+ case DOP_CMP: tmp=0x3b; goto nochange;
+ case DOP_XOR: tmp=0x33; break;
+ case DOP_AND: tmp=0x23; if (dr1==dr2) goto nochange; break;
+ case DOP_OR: tmp=0x0b; if (dr1==dr2) goto nochange; break;
+ case DOP_TEST: tmp=0x85; goto nochange;
+ case DOP_MOV: if (dr1==dr2) return; tmp=0x8b; break;
+ case DOP_XCHG:
+ dr2->flags|=DYNFLG_CHANGED;
+ if (dword && !((dr1->flags&DYNFLG_HAS8) ^ (dr2->flags&DYNFLG_HAS8))) {
+ dr1->genreg=gr2;dr1->genreg->dynreg=dr1;
+ dr2->genreg=gr1;dr2->genreg->dynreg=dr2;
+ dr1->flags|=DYNFLG_CHANGED;
+ return;
+ }
+ tmp=0x87;
+ break;
+ default:
+ IllegalOption("gen_dop_word");
+ }
+ dr1->flags|=DYNFLG_CHANGED;
+nochange:
+ if (!dword) cache_addb(0x66);
+ cache_addw(tmp|(0xc0+(gr1->index<<3)+gr2->index)<<8);
+}
+
+static void gen_dop_word_imm(DualOps op,bool dword,DynReg * dr1,Bits imm) {
+ GenReg * gr1=FindDynReg(dr1,dword && op==DOP_MOV);
+ Bit16u tmp;
+ if (!dword) cache_addb(0x66);
+ switch (op) {
+ case DOP_ADD: tmp=0xc081; break;
+ case DOP_ADC: tmp=0xd081; break;
+ case DOP_SUB: tmp=0xe881; break;
+ case DOP_SBB: tmp=0xd881; break;
+ case DOP_CMP: tmp=0xf881; goto nochange; //Doesn't change
+ case DOP_XOR: tmp=0xf081; break;
+ case DOP_AND: tmp=0xe081; break;
+ case DOP_OR: tmp=0xc881; break;
+ case DOP_TEST: tmp=0xc0f7; goto nochange; //Doesn't change
+ case DOP_MOV: cache_addb(0xb8+(gr1->index)); dr1->flags|=DYNFLG_CHANGED; goto finish;
+ default:
+ IllegalOption("gen_dop_word_imm");
+ }
+ dr1->flags|=DYNFLG_CHANGED;
+nochange:
+ cache_addw(tmp+(gr1->index<<8));
+finish:
+ if (dword) cache_addd(imm);
+ else cache_addw(imm);
+}
+
+static void gen_dop_word_imm_mem(DualOps op,bool dword,DynReg * dr1,void* data) {
+ GenReg * gr1=FindDynReg(dr1,dword && op==DOP_MOV);
+ Bit16u tmp;
+ switch (op) {
+ case DOP_ADD: tmp=0x0503; break;
+ case DOP_ADC: tmp=0x0513; break;
+ case DOP_SUB: tmp=0x052b; break;
+ case DOP_SBB: tmp=0x051b; break;
+ case DOP_CMP: tmp=0x053b; goto nochange; //Doesn't change
+ case DOP_XOR: tmp=0x0533; break;
+ case DOP_AND: tmp=0x0523; break;
+ case DOP_OR: tmp=0x050b; break;
+ case DOP_TEST: tmp=0x0585; goto nochange; //Doesn't change
+ case DOP_MOV:
+ gen_mov_host(data,dr1,dword?4:2);
+ dr1->flags|=DYNFLG_CHANGED;
+ return;
+ default:
+ IllegalOption("gen_dop_word_imm_mem");
+ }
+ dr1->flags|=DYNFLG_CHANGED;
+nochange:
+ if (!dword) cache_addb(0x66);
+ cache_addw(tmp+(gr1->index<<11));
+ cache_addd((Bit32u)data);
+}
+
+static void gen_dop_word_var(DualOps op,bool dword,DynReg * dr1,void* drd) {
+ GenReg * gr1=FindDynReg(dr1,dword && op==DOP_MOV);
+ Bit8u tmp;
+ switch (op) {
+ case DOP_ADD: tmp=0x03; break;
+ case DOP_ADC: tmp=0x13; break;
+ case DOP_SUB: tmp=0x2b; break;
+ case DOP_SBB: tmp=0x1b; break;
+ case DOP_CMP: tmp=0x3b; break;
+ case DOP_XOR: tmp=0x33; break;
+ case DOP_AND: tmp=0x23; break;
+ case DOP_OR: tmp=0x0b; break;
+ case DOP_TEST: tmp=0x85; break;
+ case DOP_MOV: tmp=0x8b; break;
+ case DOP_XCHG: tmp=0x87; break;
+ default:
+ IllegalOption("gen_dop_word_var");
+ }
+ if (!dword) cache_addb(0x66);
+ cache_addw(tmp|(0x05+((gr1->index)<<3))<<8);
+ cache_addd((Bit32u)drd);
+}
+
+static void gen_imul_word(bool dword,DynReg * dr1,DynReg * dr2) {
+ GenReg * gr1=FindDynReg(dr1);GenReg * gr2=FindDynReg(dr2);
+ dr1->flags|=DYNFLG_CHANGED;
+ if (!dword) {
+ cache_addd(0xaf0f66|(0xc0+(gr1->index<<3)+gr2->index)<<24);
+ } else {
+ cache_addw(0xaf0f);
+ cache_addb(0xc0+(gr1->index<<3)+gr2->index);
+ }
+}
+
+static void gen_imul_word_imm(bool dword,DynReg * dr1,DynReg * dr2,Bits imm) {
+ GenReg * gr1=FindDynReg(dr1);GenReg * gr2=FindDynReg(dr2);
+ if (!dword) cache_addb(0x66);
+ if ((imm>=-128 && imm<=127)) {
+ cache_addb(0x6b);
+ cache_addb(0xc0+(gr1->index<<3)+gr2->index);
+ cache_addb(imm);
+ } else {
+ cache_addb(0x69);
+ cache_addb(0xc0+(gr1->index<<3)+gr2->index);
+ if (dword) cache_addd(imm);
+ else cache_addw(imm);
+ }
+ dr1->flags|=DYNFLG_CHANGED;
+}
+
+
+static void gen_sop_word(SingleOps op,bool dword,DynReg * dr1) {
+ GenReg * gr1=FindDynReg(dr1);
+ if (!dword) cache_addb(0x66);
+ switch (op) {
+ case SOP_INC:cache_addb(0x40+gr1->index);break;
+ case SOP_DEC:cache_addb(0x48+gr1->index);break;
+ case SOP_NOT:cache_addw(0xd0f7+(gr1->index<<8));break;
+ case SOP_NEG:cache_addw(0xd8f7+(gr1->index<<8));break;
+ default:
+ IllegalOption("gen_sop_word");
+ }
+ dr1->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_shift_byte_cl(Bitu op,DynReg * dr1,Bit8u di1,DynReg * drecx) {
+ ForceDynReg(x86gen.regs[X86_REG_ECX],drecx);
+ GenReg * gr1=FindDynReg(dr1);
+ cache_addw(0xc0d2+(((Bit16u)op) << 11)+ ((gr1->index+di1)<<8));
+ dr1->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_shift_byte_imm(Bitu op,DynReg * dr1,Bit8u di1,Bit8u imm) {
+ GenReg * gr1=FindDynReg(dr1);
+ cache_addw(0xc0c0+(((Bit16u)op) << 11) + ((gr1->index+di1)<<8));
+ cache_addb(imm);
+ dr1->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_shift_word_cl(Bitu op,bool dword,DynReg * dr1,DynReg * drecx) {
+ ForceDynReg(x86gen.regs[X86_REG_ECX],drecx);
+ GenReg * gr1=FindDynReg(dr1);
+ if (!dword) cache_addb(0x66);
+ cache_addw(0xc0d3+(((Bit16u)op) << 11) + ((gr1->index)<<8));
+ dr1->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_shift_word_imm(Bitu op,bool dword,DynReg * dr1,Bit8u imm) {
+ GenReg * gr1=FindDynReg(dr1);
+ dr1->flags|=DYNFLG_CHANGED;
+ if (!dword) {
+ cache_addd(0x66|((0xc0c1+((Bit16u)op << 11) + (gr1->index<<8))|imm<<16)<<8);
+ } else {
+ cache_addw(0xc0c1+((Bit16u)op << 11) + (gr1->index<<8));
+ cache_addb(imm);
+ }
+}
+
+static void gen_cbw(bool dword,DynReg * dyn_ax) {
+ ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax);
+ if (!dword) cache_addb(0x66);
+ cache_addb(0x98);
+ dyn_ax->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_cwd(bool dword,DynReg * dyn_ax,DynReg * dyn_dx) {
+ ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax);
+ ForceDynReg(x86gen.regs[X86_REG_EDX],dyn_dx);
+ dyn_ax->flags|=DYNFLG_CHANGED;
+ dyn_dx->flags|=DYNFLG_CHANGED;
+ if (!dword) cache_addw(0x9966);
+ else cache_addb(0x99);
+}
+
+static void gen_mul_byte(bool imul,DynReg * dyn_ax,DynReg * dr1,Bit8u di1) {
+ ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax);
+ GenReg * gr1=FindDynReg(dr1);
+ if (imul) cache_addw(0xe8f6+((gr1->index+di1)<<8));
+ else cache_addw(0xe0f6+((gr1->index+di1)<<8));
+ dyn_ax->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_mul_word(bool imul,DynReg * dyn_ax,DynReg * dyn_dx,bool dword,DynReg * dr1) {
+ ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax);
+ ForceDynReg(x86gen.regs[X86_REG_EDX],dyn_dx);
+ GenReg * gr1=FindDynReg(dr1);
+ if (!dword) cache_addb(0x66);
+ if (imul) cache_addw(0xe8f7+(gr1->index<<8));
+ else cache_addw(0xe0f7+(gr1->index<<8));
+ dyn_ax->flags|=DYNFLG_CHANGED;
+ dyn_dx->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_dshift_imm(bool dword,bool left,DynReg * dr1,DynReg * dr2,Bitu imm) {
+ GenReg * gr1=FindDynReg(dr1);
+ GenReg * gr2=FindDynReg(dr2);
+ if (!dword) cache_addb(0x66);
+ if (left) cache_addw(0xa40f); //SHLD IMM
+ else cache_addw(0xac0f); //SHRD IMM
+ cache_addb(0xc0+gr1->index+(gr2->index<<3));
+ cache_addb(imm);
+ dr1->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_dshift_cl(bool dword,bool left,DynReg * dr1,DynReg * dr2,DynReg * drecx) {
+ ForceDynReg(x86gen.regs[X86_REG_ECX],drecx);
+ GenReg * gr1=FindDynReg(dr1);
+ GenReg * gr2=FindDynReg(dr2);
+ if (!dword) cache_addb(0x66);
+ if (left) cache_addw(0xa50f); //SHLD CL
+ else cache_addw(0xad0f); //SHRD CL
+ cache_addb(0xc0+gr1->index+(gr2->index<<3));
+ dr1->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_call_function(void * func,char const* ops,...) {
+ Bits paramcount=0;
+ bool release_flags=false;
+ struct ParamInfo {
+ const char * line;
+ Bitu value;
+ } pinfo[32];
+ ParamInfo * retparam=0;
+ /* Clear the EAX Genreg for usage */
+ x86gen.regs[X86_REG_EAX]->Clear();
+ x86gen.regs[X86_REG_EAX]->notusable=true;
+ /* Save the flags */
+ if (GCC_UNLIKELY(!skip_flags)) gen_protectflags();
+ /* Scan for the amount of params */
+ if (ops) {
+ va_list params;
+ va_start(params,ops);
+#if defined (MACOSX)
+ Bitu stack_used=0;
+ bool free_flags=false;
+#endif
+ Bits pindex=0;
+ while (*ops) {
+ if (*ops=='%') {
+ pinfo[pindex].line=ops+1;
+ pinfo[pindex].value=va_arg(params,Bitu);
+#if defined (MACOSX)
+ const char * scan=pinfo[pindex].line;
+ if ((*scan=='I') || (*scan=='D')) stack_used+=4;
+ else if (*scan=='F') free_flags=true;
+#endif
+ pindex++;
+ }
+ ops++;
+ }
+ va_end(params);
+
+#if defined (MACOSX)
+ /* align stack */
+ stack_used+=4; // saving esp on stack as well
+
+ cache_addw(0xc48b); // mov eax,esp
+ cache_addb(0x2d); // sub eax,stack_used
+ cache_addd(stack_used);
+ cache_addw(0xe083); // and eax,0xfffffff0
+ cache_addb(0xf0);
+ cache_addb(0x05); // sub eax,stack_used
+ cache_addd(stack_used);
+ cache_addb(0x94); // xchg eax,esp
+ if (free_flags) {
+ cache_addw(0xc083); // add eax,4
+ cache_addb(0x04);
+ }
+ cache_addb(0x50); // push eax (==old esp)
+#endif
+
+ paramcount=0;
+ while (pindex) {
+ pindex--;
+ const char * scan=pinfo[pindex].line;
+ switch (*scan++) {
+ case 'I': /* immediate value */
+ paramcount++;
+ cache_addb(0x68); //Push immediate
+ cache_addd(pinfo[pindex].value); //Push value
+ break;
+ case 'D': /* Dynamic register */
+ {
+ bool release=false;
+ paramcount++;
+ DynReg * dynreg=(DynReg *)pinfo[pindex].value;
+ GenReg * genreg=FindDynReg(dynreg);
+ scanagain:
+ switch (*scan++) {
+ case 'd':
+ cache_addb(0x50+genreg->index); //Push reg
+ break;
+ case 'w':
+ cache_addw(0xb70f); //MOVZX EAX,reg
+ cache_addb(0xc0+genreg->index);
+ cache_addb(0x50); //Push EAX
+ break;
+ case 'l':
+ cache_addw(0xb60f); //MOVZX EAX,reg[0]
+ cache_addb(0xc0+genreg->index);
+ cache_addb(0x50); //Push EAX
+ break;
+ case 'h':
+ cache_addw(0xb60f); //MOVZX EAX,reg[1]
+ cache_addb(0xc4+genreg->index);
+ cache_addb(0x50); //Push EAX
+ break;
+ case 'r': /* release the reg afterwards */
+ release=true;
+ goto scanagain;
+ default:
+ IllegalOption("gen_call_function param:DREG");
+ }
+ if (release) gen_releasereg(dynreg);
+ }
+ break;
+ case 'R': /* Dynamic register to get the return value */
+ retparam =&pinfo[pindex];
+ pinfo[pindex].line=scan;
+ break;
+ case 'F': /* Release flags from stack */
+ release_flags=true;
+ break;
+ default:
+ IllegalOption("gen_call_function unknown param");
+ }
+ }
+#if defined (MACOSX)
+ if (free_flags) release_flags=false;
+ } else {
+ /* align stack */
+ Bit32u stack_used=8; // saving esp and return address on the stack
+
+ cache_addw(0xc48b); // mov eax,esp
+ cache_addb(0x2d); // sub eax,stack_used
+ cache_addd(stack_used);
+ cache_addw(0xe083); // and eax,0xfffffff0
+ cache_addb(0xf0);
+ cache_addb(0x05); // sub eax,stack_used
+ cache_addd(stack_used);
+ cache_addb(0x94); // xchg eax,esp
+ cache_addb(0x50); // push esp (==old esp)
+#endif
+ }
+
+ /* Clear some unprotected registers */
+ x86gen.regs[X86_REG_ECX]->Clear();
+ x86gen.regs[X86_REG_EDX]->Clear();
+ /* Do the actual call to the procedure */
+ cache_addb(0xe8);
+ cache_addd((Bit32u)func - (Bit32u)cache.pos-4);
+ /* Restore the params of the stack */
+ if (paramcount) {
+ cache_addw(0xc483); //add ESP,imm byte
+ cache_addb(paramcount*4+(release_flags?4:0));
+ } else if (release_flags) {
+ cache_addw(0xc483); //add ESP,imm byte
+ cache_addb(4);
+ }
+ /* Save the return value in correct register */
+ if (retparam) {
+ DynReg * dynreg=(DynReg *)retparam->value;
+ GenReg * genreg=FindDynReg(dynreg);
+ if (genreg->index) // test for (e)ax/al
+ switch (*retparam->line) {
+ case 'd':
+ cache_addw(0xc08b+(genreg->index <<(8+3))); //mov reg,eax
+ break;
+ case 'w':
+ cache_addb(0x66);
+ cache_addw(0xc08b+(genreg->index <<(8+3))); //mov reg,eax
+ break;
+ case 'l':
+ cache_addw(0xc08a+(genreg->index <<(8+3))); //mov reg,eax
+ break;
+ case 'h':
+ cache_addw(0xc08a+((genreg->index+4) <<(8+3))); //mov reg,eax
+ break;
+ }
+ dynreg->flags|=DYNFLG_CHANGED;
+ }
+ /* Restore EAX registers to be used again */
+ x86gen.regs[X86_REG_EAX]->notusable=false;
+
+#if defined (MACOSX)
+ /* restore stack */
+ cache_addb(0x5c); // pop esp
+#endif
+}
+
+static void gen_call_write(DynReg * dr,Bit32u val,Bitu write_size) {
+ /* Clear the EAX Genreg for usage */
+ x86gen.regs[X86_REG_EAX]->Clear();
+ x86gen.regs[X86_REG_EAX]->notusable=true;
+ gen_protectflags();
+
+#if defined (MACOSX)
+ /* align stack */
+ Bitu stack_used=12;
+
+ cache_addw(0xc48b); // mov eax,esp
+ cache_addb(0x2d); // sub eax,stack_used
+ cache_addd(stack_used);
+ cache_addw(0xe083); // and eax,0xfffffff0
+ cache_addb(0xf0);
+ cache_addb(0x05); // sub eax,stack_used
+ cache_addd(stack_used);
+ cache_addb(0x94); // xchg eax,esp
+ cache_addb(0x50); // push eax (==old esp)
+#endif
+
+ cache_addb(0x68); //PUSH val
+ cache_addd(val);
+ GenReg * genreg=FindDynReg(dr);
+ cache_addb(0x50+genreg->index); //PUSH reg
+
+ /* Clear some unprotected registers */
+ x86gen.regs[X86_REG_ECX]->Clear();
+ x86gen.regs[X86_REG_EDX]->Clear();
+ /* Do the actual call to the procedure */
+ cache_addb(0xe8);
+ switch (write_size) {
+ case 1: cache_addd((Bit32u)mem_writeb_checked - (Bit32u)cache.pos-4); break;
+ case 2: cache_addd((Bit32u)mem_writew_checked - (Bit32u)cache.pos-4); break;
+ case 4: cache_addd((Bit32u)mem_writed_checked - (Bit32u)cache.pos-4); break;
+ default: IllegalOption("gen_call_write");
+ }
+
+ cache_addw(0xc483); //ADD ESP,8
+ cache_addb(2*4);
+ x86gen.regs[X86_REG_EAX]->notusable=false;
+ gen_releasereg(dr);
+
+#if defined (MACOSX)
+ /* restore stack */
+ cache_addb(0x5c); // pop esp
+#endif
+}
+
+static Bit8u * gen_create_branch(BranchTypes type) {
+ /* First free all registers */
+ cache_addw(0x70+type);
+ return (cache.pos-1);
+}
+
+static void gen_fill_branch(Bit8u * data,Bit8u * from=cache.pos) {
+#if C_DEBUG
+ Bits len=from-data;
+ if (len<0) len=-len;
+ if (len>126) LOG_MSG("Big jump %d",len);
+#endif
+ *data=(from-data-1);
+}
+
+static Bit8u * gen_create_branch_long(BranchTypes type) {
+ cache_addw(0x800f+(type<<8));
+ cache_addd(0);
+ return (cache.pos-4);
+}
+
+static void gen_fill_branch_long(Bit8u * data,Bit8u * from=cache.pos) {
+ *(Bit32u*)data=(from-data-4);
+}
+
+static Bit8u * gen_create_jump(Bit8u * to=0) {
+ /* First free all registers */
+ cache_addb(0xe9);
+ cache_addd(to-(cache.pos+4));
+ return (cache.pos-4);
+}
+
+static void gen_fill_jump(Bit8u * data,Bit8u * to=cache.pos) {
+ *(Bit32u*)data=(to-data-4);
+}
+
+
+static void gen_jmp_ptr(void * ptr,Bits imm=0) {
+ cache_addb(0xa1);
+ cache_addd((Bit32u)ptr);
+ cache_addb(0xff); //JMP EA
+ if (!imm) { //NO EBP
+ cache_addb(0x20);
+ } else if ((imm>=-128 && imm<=127)) {
+ cache_addb(0x60);
+ cache_addb(imm);
+ } else {
+ cache_addb(0xa0);
+ cache_addd(imm);
+ }
+}
+
+static void gen_save_flags(DynReg * dynreg) {
+ if (GCC_UNLIKELY(x86gen.flagsactive)) IllegalOption("gen_save_flags");
+ GenReg * genreg=FindDynReg(dynreg);
+ cache_addb(0x8b); //MOV REG,[esp]
+ cache_addw(0x2404+(genreg->index << 3));
+ dynreg->flags|=DYNFLG_CHANGED;
+}
+
+static void gen_load_flags(DynReg * dynreg) {
+ if (GCC_UNLIKELY(x86gen.flagsactive)) IllegalOption("gen_load_flags");
+ cache_addw(0xc483); //ADD ESP,4
+ cache_addb(0x4);
+ GenReg * genreg=FindDynReg(dynreg);
+ cache_addb(0x50+genreg->index); //PUSH 32
+}
+
+static void gen_save_host_direct(void * data,Bits imm) {
+ cache_addw(0x05c7); //MOV [],dword
+ cache_addd((Bit32u)data);
+ cache_addd(imm);
+}
+
+static void gen_return(BlockReturn retcode) {
+ gen_protectflags();
+ cache_addb(0x59); //POP ECX, the flags
+ if (retcode==0) cache_addw(0xc033); //MOV EAX, 0
+ else {
+ cache_addb(0xb8); //MOV EAX, retcode
+ cache_addd(retcode);
+ }
+ cache_addb(0xc3); //RET
+}
+
+static void gen_return_fast(BlockReturn retcode,bool ret_exception=false) {
+ if (GCC_UNLIKELY(x86gen.flagsactive)) IllegalOption("gen_return_fast");
+ cache_addw(0x0d8b); //MOV ECX, the flags
+ cache_addd((Bit32u)&cpu_regs.flags);
+ if (!ret_exception) {
+ cache_addw(0xc483); //ADD ESP,4
+ cache_addb(0x4);
+ if (retcode==0) cache_addw(0xc033); //MOV EAX, 0
+ else {
+ cache_addb(0xb8); //MOV EAX, retcode
+ cache_addd(retcode);
+ }
+ }
+ cache_addb(0xc3); //RET
+}
+
+static void gen_init(void) {
+ x86gen.regs[X86_REG_EAX]=new GenReg(0);
+ x86gen.regs[X86_REG_ECX]=new GenReg(1);
+ x86gen.regs[X86_REG_EDX]=new GenReg(2);
+ x86gen.regs[X86_REG_EBX]=new GenReg(3);
+ x86gen.regs[X86_REG_EBP]=new GenReg(5);
+ x86gen.regs[X86_REG_ESI]=new GenReg(6);
+ x86gen.regs[X86_REG_EDI]=new GenReg(7);
+}
+
+
diff --git a/src/cpu/core_dyn_x86/string.h b/src/cpu/core_dyn_x86/string.h
new file mode 100644
index 000000000..60cfc8e06
--- /dev/null
+++ b/src/cpu/core_dyn_x86/string.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+enum STRING_OP {
+ STR_OUTSB=0,STR_OUTSW,STR_OUTSD,
+ STR_INSB=4,STR_INSW,STR_INSD,
+ STR_MOVSB=8,STR_MOVSW,STR_MOVSD,
+ STR_LODSB=12,STR_LODSW,STR_LODSD,
+ STR_STOSB=16,STR_STOSW,STR_STOSD,
+ STR_SCASB=20,STR_SCASW,STR_SCASD,
+ STR_CMPSB=24,STR_CMPSW,STR_CMPSD
+};
+
+static void dyn_string(STRING_OP op) {
+ DynReg * si_base=decode.segprefix ? decode.segprefix : DREG(DS);
+ DynReg * di_base=DREG(ES);
+ DynReg * tmp_reg;bool usesi;bool usedi;
+ gen_protectflags();
+ if (decode.rep) {
+ gen_dop_word_imm(DOP_SUB,true,DREG(CYCLES),decode.cycles);
+ gen_releasereg(DREG(CYCLES));
+ decode.cycles=0;
+ }
+ /* Check what each string operation will be using */
+ switch (op) {
+ case STR_MOVSB: case STR_MOVSW: case STR_MOVSD:
+ case STR_CMPSB: case STR_CMPSW: case STR_CMPSD:
+ tmp_reg=DREG(TMPB);usesi=true;usedi=true;break;
+ case STR_LODSB: case STR_LODSW: case STR_LODSD:
+ tmp_reg=DREG(EAX);usesi=true;usedi=false;break;
+ case STR_OUTSB: case STR_OUTSW: case STR_OUTSD:
+ tmp_reg=DREG(TMPB);usesi=true;usedi=false;break;
+ case STR_SCASB: case STR_SCASW: case STR_SCASD:
+ case STR_STOSB: case STR_STOSW: case STR_STOSD:
+ tmp_reg=DREG(EAX);usesi=false;usedi=true;break;
+ case STR_INSB: case STR_INSW: case STR_INSD:
+ tmp_reg=DREG(TMPB);usesi=false;usedi=true;break;
+ default:
+ IllegalOption("dyn_string op");
+ }
+ gen_load_host(&cpu.direction,DREG(TMPW),4);
+ switch (op & 3) {
+ case 0:break;
+ case 1:gen_shift_word_imm(SHIFT_SHL,true,DREG(TMPW),1);break;
+ case 2:gen_shift_word_imm(SHIFT_SHL,true,DREG(TMPW),2);break;
+ default:
+ IllegalOption("dyn_string shift");
+
+ }
+ if (usesi) {
+ gen_preloadreg(DREG(ESI));
+ DynRegs[G_ESI].flags|=DYNFLG_CHANGED;
+ gen_preloadreg(si_base);
+ }
+ if (usedi) {
+ gen_preloadreg(DREG(EDI));
+ DynRegs[G_EDI].flags|=DYNFLG_CHANGED;
+ gen_preloadreg(di_base);
+ }
+ if (decode.rep) {
+ gen_preloadreg(DREG(ECX));
+ DynRegs[G_ECX].flags|=DYNFLG_CHANGED;
+ }
+ DynState rep_state;
+ dyn_savestate(&rep_state);
+ Bit8u * rep_start=cache.pos;
+ Bit8u * rep_ecx_jmp;
+ /* Check if ECX!=zero */
+ if (decode.rep) {
+ gen_dop_word(DOP_OR,decode.big_addr,DREG(ECX),DREG(ECX));
+ rep_ecx_jmp=gen_create_branch_long(BR_Z);
+ }
+ if (usesi) {
+ if (!decode.big_addr) {
+ gen_extend_word(false,DREG(EA),DREG(ESI));
+ gen_lea(DREG(EA),si_base,DREG(EA),0,0);
+ } else {
+ gen_lea(DREG(EA),si_base,DREG(ESI),0,0);
+ }
+ switch (op&3) {
+ case 0:dyn_read_byte(DREG(EA),tmp_reg,false);break;
+ case 1:dyn_read_word(DREG(EA),tmp_reg,false);break;
+ case 2:dyn_read_word(DREG(EA),tmp_reg,true);break;
+ }
+ switch (op) {
+ case STR_OUTSB:
+ gen_call_function((void*)&IO_WriteB,"%Id%Dl",DREG(EDX),tmp_reg);break;
+ case STR_OUTSW:
+ gen_call_function((void*)&IO_WriteW,"%Id%Dw",DREG(EDX),tmp_reg);break;
+ case STR_OUTSD:
+ gen_call_function((void*)&IO_WriteD,"%Id%Dd",DREG(EDX),tmp_reg);break;
+ }
+ }
+ if (usedi) {
+ if (!decode.big_addr) {
+ gen_extend_word(false,DREG(EA),DREG(EDI));
+ gen_lea(DREG(EA),di_base,DREG(EA),0,0);
+ } else {
+ gen_lea(DREG(EA),di_base,DREG(EDI),0,0);
+ }
+ /* Maybe something special to be done to fill the value */
+ switch (op) {
+ case STR_INSB:
+ gen_call_function((void*)&IO_ReadB,"%Dw%Rl",DREG(EDX),tmp_reg);
+ case STR_MOVSB:
+ case STR_STOSB:
+ dyn_write_byte(DREG(EA),tmp_reg,false);
+ break;
+ case STR_INSW:
+ gen_call_function((void*)&IO_ReadW,"%Dw%Rw",DREG(EDX),tmp_reg);
+ case STR_MOVSW:
+ case STR_STOSW:
+ dyn_write_word(DREG(EA),tmp_reg,false);
+ break;
+ case STR_INSD:
+ gen_call_function((void*)&IO_ReadD,"%Dw%Rd",DREG(EDX),tmp_reg);
+ case STR_MOVSD:
+ case STR_STOSD:
+ dyn_write_word(DREG(EA),tmp_reg,true);
+ break;
+ default:
+ IllegalOption("dyn_string op");
+ }
+ }
+ gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPB));
+
+ /* update registers */
+ if (usesi) gen_dop_word(DOP_ADD,decode.big_addr,DREG(ESI),DREG(TMPW));
+ if (usedi) gen_dop_word(DOP_ADD,decode.big_addr,DREG(EDI),DREG(TMPW));
+
+ if (decode.rep) {
+ gen_sop_word(SOP_DEC,decode.big_addr,DREG(ECX));
+ gen_sop_word(SOP_DEC,true,DREG(CYCLES));
+ gen_releasereg(DREG(CYCLES));
+ dyn_savestate(&save_info[used_save_info].state);
+ save_info[used_save_info].branch_pos=gen_create_branch_long(BR_LE);
+ save_info[used_save_info].eip_change=decode.op_start-decode.code_start;
+ save_info[used_save_info].type=normal;
+ used_save_info++;
+
+ /* Jump back to start of ECX check */
+ dyn_synchstate(&rep_state);
+ gen_create_jump(rep_start);
+
+ dyn_loadstate(&rep_state);
+ gen_fill_branch_long(rep_ecx_jmp);
+ }
+ gen_releasereg(DREG(TMPW));
+}
diff --git a/src/cpu/core_dynrec.cpp b/src/cpu/core_dynrec.cpp
new file mode 100644
index 000000000..9e232916a
--- /dev/null
+++ b/src/cpu/core_dynrec.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+
+#if (C_DYNREC)
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#if defined (WIN32)
+#include <windows.h>
+#include <winbase.h>
+#endif
+
+#if (C_HAVE_MPROTECT)
+#include <sys/mman.h>
+
+#include <limits.h>
+#ifndef PAGESIZE
+#define PAGESIZE 4096
+#endif
+#endif /* C_HAVE_MPROTECT */
+
+#include "callback.h"
+#include "regs.h"
+#include "mem.h"
+#include "cpu.h"
+#include "debug.h"
+#include "paging.h"
+#include "inout.h"
+#include "lazyflags.h"
+#include "pic.h"
+
+#define CACHE_MAXSIZE (4096*2)
+#define CACHE_TOTAL (1024*1024*8)
+#define CACHE_PAGES (512)
+#define CACHE_BLOCKS (128*1024)
+#define CACHE_ALIGN (16)
+#define DYN_HASH_SHIFT (4)
+#define DYN_PAGE_HASH (4096>>DYN_HASH_SHIFT)
+#define DYN_LINKS (16)
+
+
+//#define DYN_LOG 1 //Turn Logging on.
+
+
+#if C_FPU
+#define CPU_FPU 1 //Enable FPU escape instructions
+#endif
+
+
+// the emulated x86 registers
+#define DRC_REG_EAX 0
+#define DRC_REG_ECX 1
+#define DRC_REG_EDX 2
+#define DRC_REG_EBX 3
+#define DRC_REG_ESP 4
+#define DRC_REG_EBP 5
+#define DRC_REG_ESI 6
+#define DRC_REG_EDI 7
+
+// the emulated x86 segment registers
+#define DRC_SEG_ES 0
+#define DRC_SEG_CS 1
+#define DRC_SEG_SS 2
+#define DRC_SEG_DS 3
+#define DRC_SEG_FS 4
+#define DRC_SEG_GS 5
+
+
+// access to a general register
+#define DRCD_REG_VAL(reg) (&cpu_regs.regs[reg].dword)
+// access to a segment register
+#define DRCD_SEG_VAL(seg) (&Segs.val[seg])
+// access to the physical value of a segment register/selector
+#define DRCD_SEG_PHYS(seg) (&Segs.phys[seg])
+
+// access to an 8bit general register
+#define DRCD_REG_BYTE(reg,idx) (&cpu_regs.regs[reg].byte[idx?BH_INDEX:BL_INDEX])
+// access to 16/32bit general registers
+#define DRCD_REG_WORD(reg,dwrd) ((dwrd)?((void*)(&cpu_regs.regs[reg].dword[DW_INDEX])):((void*)(&cpu_regs.regs[reg].word[W_INDEX])))
+
+
+enum BlockReturn {
+ BR_Normal=0,
+ BR_Cycles,
+ BR_Link1,BR_Link2,
+ BR_Opcode,
+#if (C_DEBUG)
+ BR_OpcodeFull,
+#endif
+ BR_Iret,
+ BR_CallBack,
+ BR_SMCBlock
+};
+
+// identificator to signal self-modification of the currently executed block
+#define SMC_CURRENT_BLOCK 0xffff
+
+
+static void IllegalOptionDynrec(const char* msg) {
+ E_Exit("DynrecCore: illegal option in %s",msg);
+}
+
+static struct {
+ BlockReturn (*runcode)(Bit8u*); // points to code that can start a block
+ Bitu callback; // the occurred callback
+ Bitu readdata; // spare space used when reading from memory
+ Bit32u protected_regs[8]; // space to save/restore register values
+} core_dynrec;
+
+
+#include "core_dynrec/cache.h"
+
+#define X86 0x01
+#define X86_64 0x02
+#define MIPSEL 0x03
+#define ARMV4LE 0x04
+#define ARMV7LE 0x05
+#define ARMV8LE 0x07
+
+#if C_TARGETCPU == X86_64
+#include "core_dynrec/risc_x64.h"
+#elif C_TARGETCPU == X86
+#include "core_dynrec/risc_x86.h"
+#elif C_TARGETCPU == MIPSEL
+#include "core_dynrec/risc_mipsel32.h"
+#elif (C_TARGETCPU == ARMV4LE) || (C_TARGETCPU == ARMV7LE)
+#include "core_dynrec/risc_armv4le.h"
+#elif C_TARGETCPU == ARMV8LE
+#include "core_dynrec/risc_armv8le.h"
+#endif
+
+#include "core_dynrec/decoder.h"
+
+CacheBlockDynRec * LinkBlocks(BlockReturn ret) {
+ CacheBlockDynRec * block=NULL;
+ // the last instruction was a control flow modifying instruction
+ Bitu temp_ip=SegPhys(cs)+reg_eip;
+ CodePageHandlerDynRec * temp_handler=(CodePageHandlerDynRec *)get_tlb_readhandler(temp_ip);
+ if (temp_handler->flags & (cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16)) {
+ // see if the target is an already translated block
+ block=temp_handler->FindCacheBlock(temp_ip & 4095);
+ if (block) { // found it, link the current block to
+ cache.block.running->LinkTo(ret==BR_Link2,block);
+ }
+ }
+ return block;
+}
+
+/*
+ The core tries to find the block that should be executed next.
+ If such a block is found, it is run, otherwise the instruction
+ stream starting at ip_point is translated (see decoder.h) and
+ makes up a new code block that will be run.
+ When control is returned to CPU_Core_Dynrec_Run (which might
+ be right after the block is run, or somewhen long after that
+ due to the direct cacheblock linking) the returncode decides
+ the next action. This might be continuing the translation and
+ execution process, or returning from the core etc.
+*/
+
+Bits CPU_Core_Dynrec_Run(void) {
+ for (;;) {
+ // Determine the linear address of CS:EIP
+ PhysPt ip_point=SegPhys(cs)+reg_eip;
+ #if C_HEAVY_DEBUG
+ if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
+ #endif
+
+ CodePageHandlerDynRec * chandler=0;
+ // see if the current page is present and contains code
+ if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) {
+ // page not present, throw the exception
+ CPU_Exception(cpu.exception.which,cpu.exception.error);
+ continue;
+ }
+
+ // page doesn't contain code or is special
+ if (GCC_UNLIKELY(!chandler)) return CPU_Core_Normal_Run();
+
+ // find correct Dynamic Block to run
+ CacheBlockDynRec * block=chandler->FindCacheBlock(ip_point&4095);
+ if (!block) {
+ // no block found, thus translate the instruction stream
+ // unless the instruction is known to be modified
+ if (!chandler->invalidation_map || (chandler->invalidation_map[ip_point&4095]<4)) {
+ // translate up to 32 instructions
+ block=CreateCacheBlock(chandler,ip_point,32);
+ } else {
+ // let the normal core handle this instruction to avoid zero-sized blocks
+ Bitu old_cycles=CPU_Cycles;
+ CPU_Cycles=1;
+ Bits nc_retcode=CPU_Core_Normal_Run();
+ if (!nc_retcode) {
+ CPU_Cycles=old_cycles-1;
+ continue;
+ }
+ CPU_CycleLeft+=old_cycles;
+ return nc_retcode;
+ }
+ }
+
+run_block:
+ cache.block.running=0;
+ // now we're ready to run the dynamic code block
+// BlockReturn ret=((BlockReturn (*)(void))(block->cache.start))();
+ BlockReturn ret=core_dynrec.runcode(block->cache.start);
+
+ switch (ret) {
+ case BR_Iret:
+#if C_DEBUG
+#if C_HEAVY_DEBUG
+ if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
+#endif
+#endif
+ if (!GETFLAG(TF)) {
+ if (GETFLAG(IF) && PIC_IRQCheck) return CBRET_NONE;
+ break;
+ }
+ // trapflag is set, switch to the trap-aware decoder
+ cpudecoder=CPU_Core_Dynrec_Trap_Run;
+ return CBRET_NONE;
+
+ case BR_Normal:
+ // the block was exited due to a non-predictable control flow
+ // modifying instruction (like ret) or some nontrivial cpu state
+ // changing instruction (for example switch to/from pmode),
+ // or the maximum number of instructions to translate was reached
+#if C_DEBUG
+#if C_HEAVY_DEBUG
+ if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
+#endif
+#endif
+ break;
+
+ case BR_Cycles:
+ // cycles went negative, return from the core to handle
+ // external events, schedule the pic...
+#if C_DEBUG
+#if C_HEAVY_DEBUG
+ if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
+#endif
+#endif
+ return CBRET_NONE;
+
+ case BR_CallBack:
+ // the callback code is executed in dosbox.conf, return the callback number
+ FillFlags();
+ return core_dynrec.callback;
+
+ case BR_SMCBlock:
+// LOG_MSG("selfmodification of running block at %x:%x",SegValue(cs),reg_eip);
+ cpu.exception.which=0;
+ // fallthrough, let the normal core handle the block-modifying instruction
+ case BR_Opcode:
+ // some instruction has been encountered that could not be translated
+ // (thus it is not part of the code block), the normal core will
+ // handle this instruction
+ CPU_CycleLeft+=CPU_Cycles;
+ CPU_Cycles=1;
+ return CPU_Core_Normal_Run();
+
+#if (C_DEBUG)
+ case BR_OpcodeFull:
+ CPU_CycleLeft+=CPU_Cycles;
+ CPU_Cycles=1;
+ return CPU_Core_Full_Run();
+#endif
+
+ case BR_Link1:
+ case BR_Link2:
+ block=LinkBlocks(ret);
+ if (block) goto run_block;
+ break;
+
+ default:
+ E_Exit("Invalid return code %d", ret);
+ }
+ }
+ return CBRET_NONE;
+}
+
+Bits CPU_Core_Dynrec_Trap_Run(void) {
+ Bits oldCycles = CPU_Cycles;
+ CPU_Cycles = 1;
+ cpu.trap_skip = false;
+
+ // let the normal core execute the next (only one!) instruction
+ Bits ret=CPU_Core_Normal_Run();
+
+ // trap to int1 unless the last instruction deferred this
+ // (allows hardware interrupts to be served without interaction)
+ if (!cpu.trap_skip) CPU_HW_Interrupt(1);
+
+ CPU_Cycles = oldCycles-1;
+ // continue (either the trapflag was clear anyways, or the int1 cleared it)
+ cpudecoder = &CPU_Core_Dynrec_Run;
+
+ return ret;
+}
+
+void CPU_Core_Dynrec_Init(void) {
+}
+
+void CPU_Core_Dynrec_Cache_Init(bool enable_cache) {
+ // Initialize code cache and dynamic blocks
+ cache_init(enable_cache);
+}
+
+void CPU_Core_Dynrec_Cache_Close(void) {
+ cache_close();
+}
+
+#endif
diff --git a/src/cpu/core_dynrec/Makefile.am b/src/cpu/core_dynrec/Makefile.am
new file mode 100644
index 000000000..f135543e8
--- /dev/null
+++ b/src/cpu/core_dynrec/Makefile.am
@@ -0,0 +1,5 @@
+noinst_HEADERS = cache.h decoder.h decoder_basic.h decoder_opcodes.h \
+ dyn_fpu.h operators.h risc_x64.h risc_x86.h risc_mipsel32.h \
+ risc_armv4le.h risc_armv4le-common.h \
+ risc_armv4le-o3.h risc_armv4le-thumb.h \
+ risc_armv4le-thumb-iw.h risc_armv4le-thumb-niw.h risc_armv8le.h
diff --git a/src/cpu/core_dynrec/cache.h b/src/cpu/core_dynrec/cache.h
new file mode 100644
index 000000000..9ae81eb3f
--- /dev/null
+++ b/src/cpu/core_dynrec/cache.h
@@ -0,0 +1,665 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+class CodePageHandlerDynRec; // forward
+
+// basic cache block representation
+class CacheBlockDynRec {
+public:
+ void Clear(void);
+ // link this cache block to another block, index specifies the code
+ // path (always zero for unconditional links, 0/1 for conditional ones
+ void LinkTo(Bitu index,CacheBlockDynRec * toblock) {
+ assert(toblock);
+ link[index].to=toblock;
+ link[index].next=toblock->link[index].from; // set target block
+ toblock->link[index].from=this; // remember who links me
+ }
+ struct {
+ Bit16u start,end; // where in the page is the original code
+ CodePageHandlerDynRec * handler; // page containing this code
+ } page;
+ struct {
+ Bit8u * start; // where in the cache are we
+ Bitu size;
+ CacheBlockDynRec * next;
+ // writemap masking maskpointer/start/length
+ // to allow holes in the writemap
+ Bit8u * wmapmask;
+ Bit16u maskstart;
+ Bit16u masklen;
+ } cache;
+ struct {
+ Bitu index;
+ CacheBlockDynRec * next;
+ } hash;
+ struct {
+ CacheBlockDynRec * to; // this block can transfer control to the to-block
+ CacheBlockDynRec * next;
+ CacheBlockDynRec * from; // the from-block can transfer control to this block
+ } link[2]; // maximum two links (conditional jumps)
+ CacheBlockDynRec * crossblock;
+};
+
+static struct {
+ struct {
+ CacheBlockDynRec * first; // the first cache block in the list
+ CacheBlockDynRec * active; // the current cache block
+ CacheBlockDynRec * free; // pointer to the free list
+ CacheBlockDynRec * running; // the last block that was entered for execution
+ } block;
+ Bit8u * pos; // position in the cache block
+ CodePageHandlerDynRec * free_pages; // pointer to the free list
+ CodePageHandlerDynRec * used_pages; // pointer to the list of used pages
+ CodePageHandlerDynRec * last_page; // the last used page
+} cache;
+
+
+// cache memory pointers, to be malloc'd later
+static Bit8u * cache_code_start_ptr=NULL;
+static Bit8u * cache_code=NULL;
+static Bit8u * cache_code_link_blocks=NULL;
+
+static CacheBlockDynRec * cache_blocks=NULL;
+static CacheBlockDynRec link_blocks[2]; // default linking (specially marked)
+
+
+// the CodePageHandlerDynRec class provides access to the contained
+// cache blocks and intercepts writes to the code for special treatment
+class CodePageHandlerDynRec : public PageHandler {
+public:
+ CodePageHandlerDynRec() {
+ invalidation_map=NULL;
+ }
+
+ void SetupAt(Bitu _phys_page,PageHandler * _old_pagehandler) {
+ // initialize this codepage handler
+ phys_page=_phys_page;
+ // save the old pagehandler to provide direct read access to the memory,
+ // and to be able to restore it later on
+ old_pagehandler=_old_pagehandler;
+
+ // adjust flags
+ flags=old_pagehandler->flags|(cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16);
+ flags&=~PFLAG_WRITEABLE;
+
+ active_blocks=0;
+ active_count=16;
+
+ // initialize the maps with zero (no cache blocks as well as code present)
+ memset(&hash_map,0,sizeof(hash_map));
+ memset(&write_map,0,sizeof(write_map));
+ if (invalidation_map!=NULL) {
+ free(invalidation_map);
+ invalidation_map=NULL;
+ }
+ }
+
+ // clear out blocks that contain code which has been modified
+ bool InvalidateRange(Bitu start,Bitu end) {
+ Bits index=1+(end>>DYN_HASH_SHIFT);
+ bool is_current_block=false; // if the current block is modified, it has to be exited as soon as possible
+
+ Bit32u ip_point=SegPhys(cs)+reg_eip;
+ ip_point=(PAGING_GetPhysicalPage(ip_point)-(phys_page<<12))+(ip_point&0xfff);
+ while (index>=0) {
+ Bitu map=0;
+ // see if there is still some code in the range
+ for (Bitu count=start;count<=end;count++) map+=write_map[count];
+ if (!map) return is_current_block; // no more code, finished
+
+ CacheBlockDynRec * block=hash_map[index];
+ while (block) {
+ CacheBlockDynRec * nextblock=block->hash.next;
+ // test if this block is in the range
+ if (start<=block->page.end && end>=block->page.start) {
+ if (ip_point<=block->page.end && ip_point>=block->page.start) is_current_block=true;
+ block->Clear(); // clear the block, decrements the write_map accordingly
+ }
+ block=nextblock;
+ }
+ index--;
+ }
+ return is_current_block;
+ }
+
+ // the following functions will clean all cache blocks that are invalid now due to the write
+ void writeb(PhysPt addr,Bitu val){
+ addr&=4095;
+ if (host_readb(hostmem+addr)==(Bit8u)val) return;
+ host_writeb(hostmem+addr,val);
+ // see if there's code where we are writing to
+ if (!host_readb(&write_map[addr])) {
+ if (active_blocks) return; // still some blocks in this page
+ active_count--;
+ if (!active_count) Release(); // delay page releasing until active_count is zero
+ return;
+ } else if (!invalidation_map) {
+ invalidation_map=(Bit8u*)malloc(4096);
+ memset(invalidation_map,0,4096);
+ }
+ invalidation_map[addr]++;
+ InvalidateRange(addr,addr);
+ }
+ void writew(PhysPt addr,Bitu val){
+ addr&=4095;
+ if (host_readw(hostmem+addr)==(Bit16u)val) return;
+ host_writew(hostmem+addr,val);
+ // see if there's code where we are writing to
+ if (!host_readw(&write_map[addr])) {
+ if (active_blocks) return; // still some blocks in this page
+ active_count--;
+ if (!active_count) Release(); // delay page releasing until active_count is zero
+ return;
+ } else if (!invalidation_map) {
+ invalidation_map=(Bit8u*)malloc(4096);
+ memset(invalidation_map,0,4096);
+ }
+#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY)
+ host_writew(&invalidation_map[addr],
+ host_readw(&invalidation_map[addr])+0x101);
+#else
+ (*(Bit16u*)&invalidation_map[addr])+=0x101;
+#endif
+ InvalidateRange(addr,addr+1);
+ }
+ void writed(PhysPt addr,Bitu val){
+ addr&=4095;
+ if (host_readd(hostmem+addr)==(Bit32u)val) return;
+ host_writed(hostmem+addr,val);
+ // see if there's code where we are writing to
+ if (!host_readd(&write_map[addr])) {
+ if (active_blocks) return; // still some blocks in this page
+ active_count--;
+ if (!active_count) Release(); // delay page releasing until active_count is zero
+ return;
+ } else if (!invalidation_map) {
+ invalidation_map=(Bit8u*)malloc(4096);
+ memset(invalidation_map,0,4096);
+ }
+#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY)
+ host_writed(&invalidation_map[addr],
+ host_readd(&invalidation_map[addr])+0x1010101);
+#else
+ (*(Bit32u*)&invalidation_map[addr])+=0x1010101;
+#endif
+ InvalidateRange(addr,addr+3);
+ }
+ bool writeb_checked(PhysPt addr,Bitu val) {
+ addr&=4095;
+ if (host_readb(hostmem+addr)==(Bit8u)val) return false;
+ // see if there's code where we are writing to
+ if (!host_readb(&write_map[addr])) {
+ if (!active_blocks) {
+ // no blocks left in this page, still delay the page releasing a bit
+ active_count--;
+ if (!active_count) Release();
+ }
+ } else {
+ if (!invalidation_map) {
+ invalidation_map=(Bit8u*)malloc(4096);
+ memset(invalidation_map,0,4096);
+ }
+ invalidation_map[addr]++;
+ if (InvalidateRange(addr,addr)) {
+ cpu.exception.which=SMC_CURRENT_BLOCK;
+ return true;
+ }
+ }
+ host_writeb(hostmem+addr,val);
+ return false;
+ }
+ bool writew_checked(PhysPt addr,Bitu val) {
+ addr&=4095;
+ if (host_readw(hostmem+addr)==(Bit16u)val) return false;
+ // see if there's code where we are writing to
+ if (!host_readw(&write_map[addr])) {
+ if (!active_blocks) {
+ // no blocks left in this page, still delay the page releasing a bit
+ active_count--;
+ if (!active_count) Release();
+ }
+ } else {
+ if (!invalidation_map) {
+ invalidation_map=(Bit8u*)malloc(4096);
+ memset(invalidation_map,0,4096);
+ }
+#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY)
+ host_writew(&invalidation_map[addr],
+ host_readw(&invalidation_map[addr])+0x101);
+#else
+ (*(Bit16u*)&invalidation_map[addr])+=0x101;
+#endif
+ if (InvalidateRange(addr,addr+1)) {
+ cpu.exception.which=SMC_CURRENT_BLOCK;
+ return true;
+ }
+ }
+ host_writew(hostmem+addr,val);
+ return false;
+ }
+ bool writed_checked(PhysPt addr,Bitu val) {
+ addr&=4095;
+ if (host_readd(hostmem+addr)==(Bit32u)val) return false;
+ // see if there's code where we are writing to
+ if (!host_readd(&write_map[addr])) {
+ if (!active_blocks) {
+ // no blocks left in this page, still delay the page releasing a bit
+ active_count--;
+ if (!active_count) Release();
+ }
+ } else {
+ if (!invalidation_map) {
+ invalidation_map=(Bit8u*)malloc(4096);
+ memset(invalidation_map,0,4096);
+ }
+#if defined(WORDS_BIGENDIAN) || !defined(C_UNALIGNED_MEMORY)
+ host_writed(&invalidation_map[addr],
+ host_readd(&invalidation_map[addr])+0x1010101);
+#else
+ (*(Bit32u*)&invalidation_map[addr])+=0x1010101;
+#endif
+ if (InvalidateRange(addr,addr+3)) {
+ cpu.exception.which=SMC_CURRENT_BLOCK;
+ return true;
+ }
+ }
+ host_writed(hostmem+addr,val);
+ return false;
+ }
+
+ // add a cache block to this page and note it in the hash map
+ void AddCacheBlock(CacheBlockDynRec * block) {
+ Bitu index=1+(block->page.start>>DYN_HASH_SHIFT);
+ block->hash.next=hash_map[index]; // link to old block at index from the new block
+ block->hash.index=index;
+ hash_map[index]=block; // put new block at hash position
+ block->page.handler=this;
+ active_blocks++;
+ }
+ // there's a block whose code started in a different page
+ void AddCrossBlock(CacheBlockDynRec * block) {
+ block->hash.next=hash_map[0];
+ block->hash.index=0;
+ hash_map[0]=block;
+ block->page.handler=this;
+ active_blocks++;
+ }
+ // remove a cache block
+ void DelCacheBlock(CacheBlockDynRec * block) {
+ active_blocks--;
+ active_count=16;
+ CacheBlockDynRec * * bwhere=&hash_map[block->hash.index];
+ while (*bwhere!=block) {
+ bwhere=&((*bwhere)->hash.next);
+ //Will crash if a block isn't found, which should never happen.
+ }
+ *bwhere=block->hash.next;
+
+ // remove the cleared block from the write map
+ if (GCC_UNLIKELY(block->cache.wmapmask!=NULL)) {
+ // first part is not influenced by the mask
+ for (Bitu i=block->page.start;i<block->cache.maskstart;i++) {
+ if (write_map[i]) write_map[i]--;
+ }
+ Bitu maskct=0;
+ // last part sticks to the writemap mask
+ for (Bitu i=block->cache.maskstart;i<=block->page.end;i++,maskct++) {
+ if (write_map[i]) {
+ // only adjust writemap if it isn't masked
+ if ((maskct>=block->cache.masklen) || (!block->cache.wmapmask[maskct])) write_map[i]--;
+ }
+ }
+ free(block->cache.wmapmask);
+ block->cache.wmapmask=NULL;
+ } else {
+ for (Bitu i=block->page.start;i<=block->page.end;i++) {
+ if (write_map[i]) write_map[i]--;
+ }
+ }
+ }
+
+ void Release(void) {
+ MEM_SetPageHandler(phys_page,1,old_pagehandler); // revert to old handler
+ PAGING_ClearTLB();
+
+ // remove page from the lists
+ if (prev) prev->next=next;
+ else cache.used_pages=next;
+ if (next) next->prev=prev;
+ else cache.last_page=prev;
+ next=cache.free_pages;
+ cache.free_pages=this;
+ prev=0;
+ }
+ void ClearRelease(void) {
+ // clear out all cache blocks in this page
+ for (Bitu index=0;index<(1+DYN_PAGE_HASH);index++) {
+ CacheBlockDynRec * block=hash_map[index];
+ while (block) {
+ CacheBlockDynRec * nextblock=block->hash.next;
+ block->page.handler=0; // no need, full clear
+ block->Clear();
+ block=nextblock;
+ }
+ }
+ Release(); // now can release this page
+ }
+
+ CacheBlockDynRec * FindCacheBlock(Bitu start) {
+ CacheBlockDynRec * block=hash_map[1+(start>>DYN_HASH_SHIFT)];
+ // see if there's a cache block present at the start address
+ while (block) {
+ if (block->page.start==start) return block; // found
+ block=block->hash.next;
+ }
+ return 0; // none found
+ }
+
+ HostPt GetHostReadPt(Bitu phys_page) {
+ hostmem=old_pagehandler->GetHostReadPt(phys_page);
+ return hostmem;
+ }
+ HostPt GetHostWritePt(Bitu phys_page) {
+ return GetHostReadPt( phys_page );
+ }
+public:
+ // the write map, there are write_map[i] cache blocks that cover the byte at address i
+ Bit8u write_map[4096];
+ Bit8u * invalidation_map;
+ CodePageHandlerDynRec * next, * prev; // page linking
+private:
+ PageHandler * old_pagehandler;
+
+ // hash map to quickly find the cache blocks in this page
+ CacheBlockDynRec * hash_map[1+DYN_PAGE_HASH];
+
+ Bitu active_blocks; // the number of cache blocks in this page
+ Bitu active_count; // delaying parameter to not immediately release a page
+ HostPt hostmem;
+ Bitu phys_page;
+};
+
+
+static INLINE void cache_addunusedblock(CacheBlockDynRec * block) {
+ // block has become unused, add it to the freelist
+ block->cache.next=cache.block.free;
+ cache.block.free=block;
+}
+
+static CacheBlockDynRec * cache_getblock(void) {
+ // get a free cache block and advance the free pointer
+ CacheBlockDynRec * ret=cache.block.free;
+ if (!ret) E_Exit("Ran out of CacheBlocks" );
+ cache.block.free=ret->cache.next;
+ ret->cache.next=0;
+ return ret;
+}
+
+void CacheBlockDynRec::Clear(void) {
+ Bitu ind;
+ // check if this is not a cross page block
+ if (hash.index) for (ind=0;ind<2;ind++) {
+ CacheBlockDynRec * fromlink=link[ind].from;
+ link[ind].from=0;
+ while (fromlink) {
+ CacheBlockDynRec * nextlink=fromlink->link[ind].next;
+ // clear the next-link and let the block point to the standard linkcode
+ fromlink->link[ind].next=0;
+ fromlink->link[ind].to=&link_blocks[ind];
+
+ fromlink=nextlink;
+ }
+ if (link[ind].to!=&link_blocks[ind]) {
+ // not linked to the standard linkcode, find the block that links to this block
+ CacheBlockDynRec * * wherelink=&link[ind].to->link[ind].from;
+ while (*wherelink != this && *wherelink) {
+ wherelink = &(*wherelink)->link[ind].next;
+ }
+ // now remove the link
+ if(*wherelink)
+ *wherelink = (*wherelink)->link[ind].next;
+ else {
+ LOG(LOG_CPU,LOG_ERROR)("Cache anomaly. please investigate");
+ }
+ }
+ } else
+ cache_addunusedblock(this);
+ if (crossblock) {
+ // clear out the crossblock (in the page before) as well
+ crossblock->crossblock=0;
+ crossblock->Clear();
+ crossblock=0;
+ }
+ if (page.handler) {
+ // clear out the code page handler
+ page.handler->DelCacheBlock(this);
+ page.handler=0;
+ }
+ if (cache.wmapmask){
+ free(cache.wmapmask);
+ cache.wmapmask=NULL;
+ }
+}
+
+
+static CacheBlockDynRec * cache_openblock(void) {
+ CacheBlockDynRec * block=cache.block.active;
+ // check for enough space in this block
+ Bitu size=block->cache.size;
+ CacheBlockDynRec * nextblock=block->cache.next;
+ if (block->page.handler)
+ block->Clear();
+ // block size must be at least CACHE_MAXSIZE
+ while (size<CACHE_MAXSIZE) {
+ if (!nextblock)
+ goto skipresize;
+ // merge blocks
+ size+=nextblock->cache.size;
+ CacheBlockDynRec * tempblock=nextblock->cache.next;
+ if (nextblock->page.handler)
+ nextblock->Clear();
+ // block is free now
+ cache_addunusedblock(nextblock);
+ nextblock=tempblock;
+ }
+skipresize:
+ // adjust parameters and open this block
+ block->cache.size=size;
+ block->cache.next=nextblock;
+ cache.pos=block->cache.start;
+ return block;
+}
+
+static void cache_closeblock(void) {
+ CacheBlockDynRec * block=cache.block.active;
+ // links point to the default linking code
+ block->link[0].to=&link_blocks[0];
+ block->link[1].to=&link_blocks[1];
+ block->link[0].from=0;
+ block->link[1].from=0;
+ block->link[0].next=0;
+ block->link[1].next=0;
+ // close the block with correct alignment
+ Bitu written=(Bitu)(cache.pos-block->cache.start);
+ if (written>block->cache.size) {
+ if (!block->cache.next) {
+ if (written>block->cache.size+CACHE_MAXSIZE) E_Exit("CacheBlock overrun 1 %d",written-block->cache.size);
+ } else E_Exit("CacheBlock overrun 2 written %d size %d",written,block->cache.size);
+ } else {
+ Bitu new_size;
+ Bitu left=block->cache.size-written;
+ // smaller than cache align then don't bother to resize
+ if (left>CACHE_ALIGN) {
+ new_size=((written-1)|(CACHE_ALIGN-1))+1;
+ CacheBlockDynRec * newblock=cache_getblock();
+ // align block now to CACHE_ALIGN
+ newblock->cache.start=block->cache.start+new_size;
+ newblock->cache.size=block->cache.size-new_size;
+ newblock->cache.next=block->cache.next;
+ block->cache.next=newblock;
+ block->cache.size=new_size;
+ }
+ }
+ // advance the active block pointer
+ if (!block->cache.next || (block->cache.next->cache.start>(cache_code_start_ptr + CACHE_TOTAL - CACHE_MAXSIZE))) {
+// LOG_MSG("Cache full restarting");
+ cache.block.active=cache.block.first;
+ } else {
+ cache.block.active=block->cache.next;
+ }
+}
+
+
+// place an 8bit value into the cache
+static INLINE void cache_addb(Bit8u val) {
+ *cache.pos++=val;
+}
+
+// place a 16bit value into the cache
+static INLINE void cache_addw(Bit16u val) {
+ *(Bit16u*)cache.pos=val;
+ cache.pos+=2;
+}
+
+// place a 32bit value into the cache
+static INLINE void cache_addd(Bit32u val) {
+ *(Bit32u*)cache.pos=val;
+ cache.pos+=4;
+}
+
+// place a 64bit value into the cache
+static INLINE void cache_addq(Bit64u val) {
+ *(Bit64u*)cache.pos=val;
+ cache.pos+=8;
+}
+
+
+static void dyn_return(BlockReturn retcode,bool ret_exception);
+static void dyn_run_code(void);
+
+
+/* Define temporary pagesize so the MPROTECT case and the regular case share as much code as possible */
+#if (C_HAVE_MPROTECT)
+#define PAGESIZE_TEMP PAGESIZE
+#else
+#define PAGESIZE_TEMP 4096
+#endif
+
+static bool cache_initialized = false;
+
+static void cache_init(bool enable) {
+ Bits i;
+ if (enable) {
+ // see if cache is already initialized
+ if (cache_initialized) return;
+ cache_initialized = true;
+ if (cache_blocks == NULL) {
+ // allocate the cache blocks memory
+ cache_blocks=(CacheBlockDynRec*)malloc(CACHE_BLOCKS*sizeof(CacheBlockDynRec));
+ if(!cache_blocks) E_Exit("Allocating cache_blocks has failed");
+ memset(cache_blocks,0,sizeof(CacheBlockDynRec)*CACHE_BLOCKS);
+ cache.block.free=&cache_blocks[0];
+ // initialize the cache blocks
+ for (i=0;i<CACHE_BLOCKS-1;i++) {
+ cache_blocks[i].link[0].to=(CacheBlockDynRec *)1;
+ cache_blocks[i].link[1].to=(CacheBlockDynRec *)1;
+ cache_blocks[i].cache.next=&cache_blocks[i+1];
+ }
+ }
+ if (cache_code_start_ptr==NULL) {
+ // allocate the code cache memory
+#if defined (WIN32)
+ cache_code_start_ptr=(Bit8u*)VirtualAlloc(0,CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP,
+ MEM_COMMIT,PAGE_EXECUTE_READWRITE);
+ if (!cache_code_start_ptr)
+ cache_code_start_ptr=(Bit8u*)malloc(CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP);
+#else
+ cache_code_start_ptr=(Bit8u*)malloc(CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP-1+PAGESIZE_TEMP);
+#endif
+ if(!cache_code_start_ptr) E_Exit("Allocating dynamic cache failed");
+
+ // align the cache at a page boundary
+ cache_code=(Bit8u*)(((Bitu)cache_code_start_ptr + PAGESIZE_TEMP-1) & ~(PAGESIZE_TEMP-1));//Bitu is same size as a pointer.
+
+ cache_code_link_blocks=cache_code;
+ cache_code=cache_code+PAGESIZE_TEMP;
+
+#if (C_HAVE_MPROTECT)
+ if(mprotect(cache_code_link_blocks,CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP,PROT_WRITE|PROT_READ|PROT_EXEC))
+ LOG_MSG("Setting execute permission on the code cache has failed");
+#endif
+ CacheBlockDynRec * block=cache_getblock();
+ cache.block.first=block;
+ cache.block.active=block;
+ block->cache.start=&cache_code[0];
+ block->cache.size=CACHE_TOTAL;
+ block->cache.next=0; // last block in the list
+ }
+ // setup the default blocks for block linkage returns
+ cache.pos=&cache_code_link_blocks[0];
+ link_blocks[0].cache.start=cache.pos;
+ // link code that returns with a special return code
+ dyn_return(BR_Link1,false);
+ cache.pos=&cache_code_link_blocks[32];
+ link_blocks[1].cache.start=cache.pos;
+ // link code that returns with a special return code
+ dyn_return(BR_Link2,false);
+
+ cache.pos=&cache_code_link_blocks[64];
+ core_dynrec.runcode=(BlockReturn (*)(Bit8u*))cache.pos;
+// link_blocks[1].cache.start=cache.pos;
+ dyn_run_code();
+
+ cache.free_pages=0;
+ cache.last_page=0;
+ cache.used_pages=0;
+ // setup the code pages
+ for (i=0;i<CACHE_PAGES;i++) {
+ CodePageHandlerDynRec * newpage=new CodePageHandlerDynRec();
+ newpage->next=cache.free_pages;
+ cache.free_pages=newpage;
+ }
+ }
+}
+
+static void cache_close(void) {
+/* for (;;) {
+ if (cache.used_pages) {
+ CodePageHandler * cpage=cache.used_pages;
+ CodePageHandler * npage=cache.used_pages->next;
+ cpage->ClearRelease();
+ delete cpage;
+ cache.used_pages=npage;
+ } else break;
+ }
+ if (cache_blocks != NULL) {
+ free(cache_blocks);
+ cache_blocks = NULL;
+ }
+ if (cache_code_start_ptr != NULL) {
+ ### care: under windows VirtualFree() has to be used if
+ ### VirtualAlloc was used for memory allocation
+ free(cache_code_start_ptr);
+ cache_code_start_ptr = NULL;
+ }
+ cache_code = NULL;
+ cache_code_link_blocks = NULL;
+ cache_initialized = false; */
+}
diff --git a/src/cpu/core_dynrec/decoder.h b/src/cpu/core_dynrec/decoder.h
new file mode 100644
index 000000000..b0bd8dbc2
--- /dev/null
+++ b/src/cpu/core_dynrec/decoder.h
@@ -0,0 +1,617 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+#include "decoder_basic.h"
+#include "operators.h"
+#include "decoder_opcodes.h"
+
+#include "dyn_fpu.h"
+
+/*
+ The function CreateCacheBlock translates the instruction stream
+ until either an unhandled instruction is found, the maximum
+ number of translated instructions is reached or some critical
+ instruction is encountered.
+*/
+
+static CacheBlockDynRec * CreateCacheBlock(CodePageHandlerDynRec * codepage,PhysPt start,Bitu max_opcodes) {
+ // initialize a load of variables
+ decode.code_start=start;
+ decode.code=start;
+ decode.page.code=codepage;
+ decode.page.index=start&4095;
+ decode.page.wmap=codepage->write_map;
+ decode.page.invmap=codepage->invalidation_map;
+ decode.page.first=start >> 12;
+ decode.active_block=decode.block=cache_openblock();
+ decode.block->page.start=(Bit16u)decode.page.index;
+ codepage->AddCacheBlock(decode.block);
+
+ InitFlagsOptimization();
+
+ // every codeblock that is run sets cache.block.running to itself
+ // so the block linking knows the last executed block
+ gen_mov_direct_ptr(&cache.block.running,(DRC_PTR_SIZE_IM)decode.block);
+
+ // start with the cycles check
+ gen_mov_word_to_reg(FC_RETOP,&CPU_Cycles,true);
+ save_info_dynrec[used_save_info_dynrec].branch_pos=gen_create_branch_long_leqzero(FC_RETOP);
+ save_info_dynrec[used_save_info_dynrec].type=cycle_check;
+ used_save_info_dynrec++;
+
+ decode.cycles=0;
+ while (max_opcodes--) {
+ // Init prefixes
+ decode.big_addr=cpu.code.big;
+ decode.big_op=cpu.code.big;
+ decode.seg_prefix=0;
+ decode.seg_prefix_used=false;
+ decode.rep=REP_NONE;
+ decode.cycles++;
+ decode.op_start=decode.code;
+restart_prefix:
+ Bitu opcode;
+ if (!decode.page.invmap) opcode=decode_fetchb();
+ else {
+ // some entries in the invalidation map, see if the next
+ // instruction is known to be modified a lot
+ if (decode.page.index<4096) {
+ if (GCC_UNLIKELY(decode.page.invmap[decode.page.index]>=4)) goto illegalopcode;
+ opcode=decode_fetchb();
+ } else {
+ // switch to the next page
+ opcode=decode_fetchb();
+ if (GCC_UNLIKELY(decode.page.invmap &&
+ (decode.page.invmap[decode.page.index-1]>=4))) goto illegalopcode;
+ }
+ }
+ switch (opcode) {
+ // instructions 'op reg8,reg8' and 'op [],reg8'
+ case 0x00:dyn_dop_ebgb(DOP_ADD);break;
+ case 0x08:dyn_dop_ebgb(DOP_OR);break;
+ case 0x10:dyn_dop_ebgb(DOP_ADC);break;
+ case 0x18:dyn_dop_ebgb(DOP_SBB);break;
+ case 0x20:dyn_dop_ebgb(DOP_AND);break;
+ case 0x28:dyn_dop_ebgb(DOP_SUB);break;
+ case 0x30:dyn_dop_ebgb(DOP_XOR);break;
+ case 0x38:dyn_dop_ebgb(DOP_CMP);break;
+
+ // instructions 'op reg8,reg8' and 'op reg8,[]'
+ case 0x02:dyn_dop_gbeb(DOP_ADD);break;
+ case 0x0a:dyn_dop_gbeb(DOP_OR);break;
+ case 0x12:dyn_dop_gbeb(DOP_ADC);break;
+ case 0x1a:dyn_dop_gbeb(DOP_SBB);break;
+ case 0x22:dyn_dop_gbeb(DOP_AND);break;
+ case 0x2a:dyn_dop_gbeb(DOP_SUB);break;
+ case 0x32:dyn_dop_gbeb(DOP_XOR);break;
+ case 0x3a:dyn_dop_gbeb(DOP_CMP);break;
+
+ // instructions 'op reg16/32,reg16/32' and 'op [],reg16/32'
+ case 0x01:dyn_dop_evgv(DOP_ADD);break;
+ case 0x09:dyn_dop_evgv(DOP_OR);break;
+ case 0x11:dyn_dop_evgv(DOP_ADC);break;
+ case 0x19:dyn_dop_evgv(DOP_SBB);break;
+ case 0x21:dyn_dop_evgv(DOP_AND);break;
+ case 0x29:dyn_dop_evgv(DOP_SUB);break;
+ case 0x31:dyn_dop_evgv(DOP_XOR);break;
+ case 0x39:dyn_dop_evgv(DOP_CMP);break;
+
+ // instructions 'op reg16/32,reg16/32' and 'op reg16/32,[]'
+ case 0x03:dyn_dop_gvev(DOP_ADD);break;
+ case 0x0b:dyn_dop_gvev(DOP_OR);break;
+ case 0x13:dyn_dop_gvev(DOP_ADC);break;
+ case 0x1b:dyn_dop_gvev(DOP_SBB);break;
+ case 0x23:dyn_dop_gvev(DOP_AND);break;
+ case 0x2b:dyn_dop_gvev(DOP_SUB);break;
+ case 0x33:dyn_dop_gvev(DOP_XOR);break;
+ case 0x3b:dyn_dop_gvev(DOP_CMP);break;
+
+ // instructions 'op reg8,imm8'
+ case 0x04:dyn_dop_byte_imm(DOP_ADD,DRC_REG_EAX,0);break;
+ case 0x0c:dyn_dop_byte_imm(DOP_OR,DRC_REG_EAX,0);break;
+ case 0x14:dyn_dop_byte_imm(DOP_ADC,DRC_REG_EAX,0);break;
+ case 0x1c:dyn_dop_byte_imm(DOP_SBB,DRC_REG_EAX,0);break;
+ case 0x24:dyn_dop_byte_imm(DOP_AND,DRC_REG_EAX,0);break;
+ case 0x2c:dyn_dop_byte_imm(DOP_SUB,DRC_REG_EAX,0);break;
+ case 0x34:dyn_dop_byte_imm(DOP_XOR,DRC_REG_EAX,0);break;
+ case 0x3c:dyn_dop_byte_imm(DOP_CMP,DRC_REG_EAX,0);break;
+
+ // instructions 'op reg16/32,imm16/32'
+ case 0x05:dyn_dop_word_imm(DOP_ADD,DRC_REG_EAX);break;
+ case 0x0d:dyn_dop_word_imm_old(DOP_OR,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
+ case 0x15:dyn_dop_word_imm_old(DOP_ADC,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
+ case 0x1d:dyn_dop_word_imm_old(DOP_SBB,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
+ case 0x25:dyn_dop_word_imm(DOP_AND,DRC_REG_EAX);break;
+ case 0x2d:dyn_dop_word_imm_old(DOP_SUB,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
+ case 0x35:dyn_dop_word_imm_old(DOP_XOR,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
+ case 0x3d:dyn_dop_word_imm_old(DOP_CMP,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
+
+ // push segment register onto stack
+ case 0x06:dyn_push_seg(DRC_SEG_ES);break;
+ case 0x0e:dyn_push_seg(DRC_SEG_CS);break;
+ case 0x16:dyn_push_seg(DRC_SEG_SS);break;
+ case 0x1e:dyn_push_seg(DRC_SEG_DS);break;
+
+ // pop segment register from stack
+ case 0x07:dyn_pop_seg(DRC_SEG_ES);break;
+ case 0x17:dyn_pop_seg(DRC_SEG_SS);break;
+ case 0x1f:dyn_pop_seg(DRC_SEG_DS);break;
+
+ // segment prefixes
+ case 0x26:dyn_segprefix(DRC_SEG_ES);goto restart_prefix;
+ case 0x2e:dyn_segprefix(DRC_SEG_CS);goto restart_prefix;
+ case 0x36:dyn_segprefix(DRC_SEG_SS);goto restart_prefix;
+ case 0x3e:dyn_segprefix(DRC_SEG_DS);goto restart_prefix;
+ case 0x64:dyn_segprefix(DRC_SEG_FS);goto restart_prefix;
+ case 0x65:dyn_segprefix(DRC_SEG_GS);goto restart_prefix;
+
+// case 0x27: DAA missing
+// case 0x2f: DAS missing
+// case 0x37: AAA missing
+// case 0x3f: AAS missing
+
+ // dual opcodes
+ case 0x0f:
+ {
+ Bitu dual_code=decode_fetchb();
+ switch (dual_code) {
+ case 0x00:
+ if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegalopcode;
+ dyn_grp6();
+ break;
+ case 0x01:
+ if (dyn_grp7()) goto finish_block;
+ break;
+/* case 0x02:
+ if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegalopcode;
+ dyn_larlsl(true);
+ break;
+ case 0x03:
+ if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegalopcode;
+ dyn_larlsl(false);
+ break; */
+
+ case 0x20:dyn_mov_from_crx();break;
+ case 0x22:dyn_mov_to_crx();goto finish_block;
+
+ // short conditional jumps
+ case 0x80:case 0x81:case 0x82:case 0x83:case 0x84:case 0x85:case 0x86:case 0x87:
+ case 0x88:case 0x89:case 0x8a:case 0x8b:case 0x8c:case 0x8d:case 0x8e:case 0x8f:
+ dyn_branched_exit((BranchTypes)(dual_code&0xf),
+ decode.big_op ? (Bit32s)decode_fetchd() : (Bit16s)decode_fetchw());
+ goto finish_block;
+
+ // conditional byte set instructions
+/* case 0x90:case 0x91:case 0x92:case 0x93:case 0x94:case 0x95:case 0x96:case 0x97:
+ case 0x98:case 0x99:case 0x9a:case 0x9b:case 0x9c:case 0x9d:case 0x9e:case 0x9f:
+ dyn_set_byte_on_condition((BranchTypes)(dual_code&0xf));
+ AcquireFlags(FMASK_TEST);
+ break; */
+
+ // push/pop segment registers
+ case 0xa0:dyn_push_seg(DRC_SEG_FS);break;
+ case 0xa1:dyn_pop_seg(DRC_SEG_FS);break;
+ case 0xa8:dyn_push_seg(DRC_SEG_GS);break;
+ case 0xa9:dyn_pop_seg(DRC_SEG_GS);break;
+
+ // double shift instructions
+ case 0xa4:dyn_dshift_ev_gv(true,true);break;
+ case 0xa5:dyn_dshift_ev_gv(true,false);break;
+ case 0xac:dyn_dshift_ev_gv(false,true);break;
+ case 0xad:dyn_dshift_ev_gv(false,false);break;
+
+ case 0xaf:dyn_imul_gvev(0);break;
+
+ // lfs
+ case 0xb4:
+ dyn_get_modrm();
+ if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
+ dyn_load_seg_off_ea(DRC_SEG_FS);
+ break;
+ // lgs
+ case 0xb5:
+ dyn_get_modrm();
+ if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
+ dyn_load_seg_off_ea(DRC_SEG_GS);
+ break;
+
+ // zero-extending moves
+ case 0xb6:dyn_movx_ev_gb(false);break;
+ case 0xb7:dyn_movx_ev_gw(false);break;
+
+ // sign-extending moves
+ case 0xbe:dyn_movx_ev_gb(true);break;
+ case 0xbf:dyn_movx_ev_gw(true);break;
+
+ default:
+#if DYN_LOG
+// LOG_MSG("Unhandled dual opcode 0F%02X",dual_code);
+#endif
+ goto illegalopcode;
+ }
+ break;
+ }
+
+ // 'inc/dec reg16/32'
+ case 0x40:case 0x41:case 0x42:case 0x43:case 0x44:case 0x45:case 0x46:case 0x47:
+ dyn_sop_word(SOP_INC,opcode&7);
+ break;
+ case 0x48:case 0x49:case 0x4a:case 0x4b:case 0x4c:case 0x4d:case 0x4e:case 0x4f:
+ dyn_sop_word(SOP_DEC,opcode&7);
+ break;
+
+ // 'push/pop reg16/32'
+ case 0x50:case 0x51:case 0x52:case 0x53:case 0x54:case 0x55:case 0x56:case 0x57:
+ dyn_push_reg(opcode&7);
+ break;
+ case 0x58:case 0x59:case 0x5a:case 0x5b:case 0x5c:case 0x5d:case 0x5e:case 0x5f:
+ dyn_pop_reg(opcode&7);
+ break;
+
+ case 0x60:
+ if (decode.big_op) gen_call_function_raw((void *)&dynrec_pusha_dword);
+ else gen_call_function_raw((void *)&dynrec_pusha_word);
+ break;
+ case 0x61:
+ if (decode.big_op) gen_call_function_raw((void *)&dynrec_popa_dword);
+ else gen_call_function_raw((void *)&dynrec_popa_word);
+ break;
+
+// case 0x62: BOUND missing
+// case 0x61: ARPL missing
+
+ case 0x66:decode.big_op=!cpu.code.big;goto restart_prefix;
+ case 0x67:decode.big_addr=!cpu.code.big;goto restart_prefix;
+
+ // 'push imm8/16/32'
+ case 0x68:
+ dyn_push_word_imm(decode.big_op ? decode_fetchd() : decode_fetchw());
+ break;
+ case 0x6a:
+ dyn_push_byte_imm((Bit8s)decode_fetchb());
+ break;
+
+ // signed multiplication
+ case 0x69:dyn_imul_gvev(decode.big_op ? 4 : 2);break;
+ case 0x6b:dyn_imul_gvev(1);break;
+
+// case 0x6c to 0x6f missing (ins/outs)
+
+ // short conditional jumps
+ case 0x70:case 0x71:case 0x72:case 0x73:case 0x74:case 0x75:case 0x76:case 0x77:
+ case 0x78:case 0x79:case 0x7a:case 0x7b:case 0x7c:case 0x7d:case 0x7e:case 0x7f:
+ dyn_branched_exit((BranchTypes)(opcode&0xf),(Bit8s)decode_fetchb());
+ goto finish_block;
+
+ // 'op []/reg8,imm8'
+ case 0x80:
+ case 0x82:dyn_grp1_eb_ib();break;
+
+ // 'op []/reg16/32,imm16/32'
+ case 0x81:dyn_grp1_ev_iv(false);break;
+ case 0x83:dyn_grp1_ev_iv(true);break;
+
+ // 'test []/reg8/16/32,reg8/16/32'
+ case 0x84:dyn_dop_gbeb(DOP_TEST);break;
+ case 0x85:dyn_dop_gvev(DOP_TEST);break;
+
+ // 'xchg reg8/16/32,[]/reg8/16/32'
+ case 0x86:dyn_dop_ebgb_xchg();break;
+ case 0x87:dyn_dop_evgv_xchg();break;
+
+ // 'mov []/reg8/16/32,reg8/16/32'
+ case 0x88:dyn_dop_ebgb_mov();break;
+ case 0x89:dyn_dop_evgv_mov();break;
+ // 'mov reg8/16/32,[]/reg8/16/32'
+ case 0x8a:dyn_dop_gbeb_mov();break;
+ case 0x8b:dyn_dop_gvev_mov();break;
+
+ // move segment register into memory or a 16bit register
+ case 0x8c:dyn_mov_ev_seg();break;
+
+ // load effective address
+ case 0x8d:dyn_lea();break;
+
+ // move a value from memory or a 16bit register into a segment register
+ case 0x8e:dyn_mov_seg_ev();break;
+
+ // 'pop []'
+ case 0x8f:dyn_pop_ev();break;
+
+ case 0x90: // nop
+ case 0x9b: // wait
+ case 0xf0: // lock
+ break;
+
+ case 0x91:case 0x92:case 0x93:case 0x94:case 0x95:case 0x96:case 0x97:
+ dyn_xchg_ax(opcode&7);
+ break;
+
+ // sign-extend al into ax/sign-extend ax into eax
+ case 0x98:dyn_cbw();break;
+ // sign-extend ax into dx:ax/sign-extend eax into edx:eax
+ case 0x99:dyn_cwd();break;
+
+ case 0x9a:dyn_call_far_imm();goto finish_block;
+
+ case 0x9c: // pushf
+ AcquireFlags(FMASK_TEST);
+ gen_call_function_I((void *)&CPU_PUSHF,decode.big_op);
+ dyn_check_exception(FC_RETOP);
+ break;
+ case 0x9d: // popf
+ gen_call_function_I((void *)&CPU_POPF,decode.big_op);
+ dyn_check_exception(FC_RETOP);
+ InvalidateFlags();
+ break;
+
+ case 0x9e:dyn_sahf();break;
+// case 0x9f: LAHF missing
+
+ // 'mov al/ax,[]'
+ case 0xa0:
+ dyn_mov_byte_al_direct(decode.big_addr ? decode_fetchd() : decode_fetchw());
+ break;
+ case 0xa1:
+ dyn_mov_byte_ax_direct(decode.big_addr ? decode_fetchd() : decode_fetchw());
+ break;
+ // 'mov [],al/ax'
+ case 0xa2:
+ dyn_mov_byte_direct_al();
+ break;
+ case 0xa3:
+ dyn_mov_byte_direct_ax(decode.big_addr ? decode_fetchd() : decode_fetchw());
+ break;
+
+
+// case 0xa6 to 0xaf string operations, some missing
+
+ // movsb/w/d
+ case 0xa4:dyn_string(STR_MOVSB);break;
+ case 0xa5:dyn_string(decode.big_op ? STR_MOVSD : STR_MOVSW);break;
+
+ // stosb/w/d
+ case 0xaa:dyn_string(STR_STOSB);break;
+ case 0xab:dyn_string(decode.big_op ? STR_STOSD : STR_STOSW);break;
+
+ // lodsb/w/d
+ case 0xac:dyn_string(STR_LODSB);break;
+ case 0xad:dyn_string(decode.big_op ? STR_LODSD : STR_LODSW);break;
+
+
+ // 'test reg8/16/32,imm8/16/32'
+ case 0xa8:dyn_dop_byte_imm(DOP_TEST,DRC_REG_EAX,0);break;
+ case 0xa9:dyn_dop_word_imm_old(DOP_TEST,DRC_REG_EAX,decode.big_op ? decode_fetchd() : decode_fetchw());break;
+
+ // 'mov reg8/16/32,imm8/16/32'
+ case 0xb0:case 0xb1:case 0xb2:case 0xb3:case 0xb4:case 0xb5:case 0xb6:case 0xb7:
+ dyn_mov_byte_imm(opcode&3,(opcode>>2)&1,decode_fetchb());
+ break;
+ case 0xb8:case 0xb9:case 0xba:case 0xbb:case 0xbc:case 0xbd:case 0xbe:case 0xbf:
+ dyn_mov_word_imm(opcode&7);break;
+ break;
+
+ // 'shiftop []/reg8,imm8/1/cl'
+ case 0xc0:dyn_grp2_eb(grp2_imm);break;
+ case 0xd0:dyn_grp2_eb(grp2_1);break;
+ case 0xd2:dyn_grp2_eb(grp2_cl);break;
+
+ // 'shiftop []/reg16/32,imm8/1/cl'
+ case 0xc1:dyn_grp2_ev(grp2_imm);break;
+ case 0xd1:dyn_grp2_ev(grp2_1);break;
+ case 0xd3:dyn_grp2_ev(grp2_cl);break;
+
+ // retn [param]
+ case 0xc2:dyn_ret_near(decode_fetchw());goto finish_block;
+ case 0xc3:dyn_ret_near(0);goto finish_block;
+
+ // les
+ case 0xc4:
+ dyn_get_modrm();
+ if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
+ dyn_load_seg_off_ea(DRC_SEG_ES);
+ break;
+ // lds
+ case 0xc5:
+ dyn_get_modrm();
+ if (GCC_UNLIKELY(decode.modrm.mod==3)) goto illegalopcode;
+ dyn_load_seg_off_ea(DRC_SEG_DS);break;
+
+ // 'mov []/reg8/16/32,imm8/16/32'
+ case 0xc6:dyn_dop_ebib_mov();break;
+ case 0xc7:dyn_dop_eviv_mov();break;
+
+ case 0xc8:dyn_enter();break;
+ case 0xc9:dyn_leave();break;
+
+ // retf [param]
+ case 0xca:dyn_ret_far(decode_fetchw());goto finish_block;
+ case 0xcb:dyn_ret_far(0);goto finish_block;
+
+ // int/iret
+#if !(C_DEBUG)
+ case 0xcd:dyn_interrupt(decode_fetchb());goto finish_block;
+#endif
+ case 0xcf:dyn_iret();goto finish_block;
+
+// case 0xd4: AAM missing
+// case 0xd5: AAD missing
+
+// case 0xd6: SALC missing
+// case 0xd7: XLAT missing
+
+
+#ifdef CPU_FPU
+ // floating point instructions
+ case 0xd8:
+ dyn_fpu_esc0();
+ break;
+ case 0xd9:
+ dyn_fpu_esc1();
+ break;
+ case 0xda:
+ dyn_fpu_esc2();
+ break;
+ case 0xdb:
+ dyn_fpu_esc3();
+ break;
+ case 0xdc:
+ dyn_fpu_esc4();
+ break;
+ case 0xdd:
+ dyn_fpu_esc5();
+ break;
+ case 0xde:
+ dyn_fpu_esc6();
+ break;
+ case 0xdf:
+ dyn_fpu_esc7();
+ break;
+#endif
+
+
+ // loop instructions
+ case 0xe0:dyn_loop(LOOP_NE);goto finish_block;
+ case 0xe1:dyn_loop(LOOP_E);goto finish_block;
+ case 0xe2:dyn_loop(LOOP_NONE);goto finish_block;
+ case 0xe3:dyn_loop(LOOP_JCXZ);goto finish_block;
+
+
+ // 'in al/ax/eax,port_imm'
+ case 0xe4:dyn_read_port_byte_direct(decode_fetchb());break;
+ case 0xe5:dyn_read_port_word_direct(decode_fetchb());break;
+ // 'out port_imm,al/ax/eax'
+ case 0xe6:dyn_write_port_byte_direct(decode_fetchb());break;
+ case 0xe7:dyn_write_port_word_direct(decode_fetchb());break;
+
+ // 'in al/ax/eax,port_dx'
+ case 0xec:dyn_read_port_byte();break;
+ case 0xed:dyn_read_port_word();break;
+ // 'out port_dx,al/ax/eax'
+ case 0xee:dyn_write_port_byte();break;
+ case 0xef:dyn_write_port_word();break;
+
+
+ // 'call near imm16/32'
+ case 0xe8:
+ dyn_call_near_imm();
+ goto finish_block;
+ // 'jmp near imm16/32'
+ case 0xe9:
+ dyn_exit_link(decode.big_op ? (Bit32s)decode_fetchd() : (Bit16s)decode_fetchw());
+ goto finish_block;
+ // 'jmp far'
+ case 0xea:
+ dyn_jmp_far_imm();
+ goto finish_block;
+ // 'jmp short imm8'
+ case 0xeb:
+ dyn_exit_link((Bit8s)decode_fetchb());
+ goto finish_block;
+
+
+ // repeat prefixes
+ case 0xf2:
+ decode.rep=REP_NZ;
+ goto restart_prefix;
+ case 0xf3:
+ decode.rep=REP_Z;
+ goto restart_prefix;
+
+ case 0xf5: //CMC
+ gen_call_function_raw((void*)dynrec_cmc);
+ break;
+ case 0xf8: //CLC
+ gen_call_function_raw((void*)dynrec_clc);
+ break;
+ case 0xf9: //STC
+ gen_call_function_raw((void*)dynrec_stc);
+ break;
+
+ case 0xf6:dyn_grp3_eb();break;
+ case 0xf7:dyn_grp3_ev();break;
+
+ case 0xfa: //CLI
+ gen_call_function_raw((void *)&CPU_CLI);
+ dyn_check_exception(FC_RETOP);
+ break;
+ case 0xfb: //STI
+ gen_call_function_raw((void *)&CPU_STI);
+ dyn_check_exception(FC_RETOP);
+ if (max_opcodes<=0) max_opcodes=1; //Allow 1 extra opcode
+ break;
+
+ case 0xfc: //CLD
+ gen_call_function_raw((void*)dynrec_cld);
+ break;
+ case 0xfd: //STD
+ gen_call_function_raw((void*)dynrec_std);
+ break;
+
+ case 0xfe:
+ if (dyn_grp4_eb()) goto finish_block;
+ break;
+ case 0xff:
+ switch (dyn_grp4_ev()) {
+ case 0:
+ break;
+ case 1:
+ goto core_close_block;
+ case 2:
+ goto illegalopcode;
+ default:
+ break;
+ }
+ break;
+
+ default:
+#if DYN_LOG
+// LOG_MSG("Dynrec unhandled opcode %X",opcode);
+#endif
+ goto illegalopcode;
+ }
+ }
+ // link to next block because the maximum number of opcodes has been reached
+ dyn_set_eip_end();
+ dyn_reduce_cycles();
+ gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlockDynRec,cache.start));
+ dyn_closeblock();
+ goto finish_block;
+core_close_block:
+ dyn_reduce_cycles();
+ dyn_return(BR_Normal);
+ dyn_closeblock();
+ goto finish_block;
+illegalopcode:
+ // some unhandled opcode has been encountered
+ dyn_set_eip_last();
+ dyn_reduce_cycles();
+ dyn_return(BR_Opcode); // tell the core what happened
+ dyn_closeblock();
+ goto finish_block;
+finish_block:
+
+ // setup the correct end-address
+ decode.page.index--;
+ decode.active_block->page.end=(Bit16u)decode.page.index;
+// LOG_MSG("Created block size %d start %d end %d",decode.block->cache.size,decode.block->page.start,decode.block->page.end);
+
+ return decode.block;
+}
diff --git a/src/cpu/core_dynrec/decoder_basic.h b/src/cpu/core_dynrec/decoder_basic.h
new file mode 100644
index 000000000..c8e2a8ef2
--- /dev/null
+++ b/src/cpu/core_dynrec/decoder_basic.h
@@ -0,0 +1,1276 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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 file provides some definitions and basic level functions
+ that use code generating functions from risc_?.h
+ Important is the function call generation including parameter
+ loading, the effective address calculation and the memory
+ access functions.
+*/
+
+
+// instructions that use one operand
+enum SingleOps {
+ SOP_INC,SOP_DEC,
+ SOP_NOT,SOP_NEG
+};
+
+// instructions that use two operand
+enum DualOps {
+ DOP_ADD,DOP_ADC,
+ DOP_SUB,DOP_SBB,
+ DOP_CMP,DOP_XOR,
+ DOP_AND,DOP_OR,
+ DOP_TEST,
+ DOP_MOV,
+ DOP_XCHG
+};
+
+// shift and rotate functions
+enum ShiftOps {
+ SHIFT_ROL,SHIFT_ROR,
+ SHIFT_RCL,SHIFT_RCR,
+ SHIFT_SHL,SHIFT_SHR,
+ SHIFT_SAL,SHIFT_SAR
+};
+
+// branch conditions
+enum BranchTypes {
+ BR_O,BR_NO,BR_B,BR_NB,
+ BR_Z,BR_NZ,BR_BE,BR_NBE,
+ BR_S,BR_NS,BR_P,BR_NP,
+ BR_L,BR_NL,BR_LE,BR_NLE
+};
+
+// string instructions
+enum StringOps {
+ STR_OUTSB=0,STR_OUTSW,STR_OUTSD,
+ STR_INSB=4,STR_INSW,STR_INSD,
+ STR_MOVSB=8,STR_MOVSW,STR_MOVSD,
+ STR_LODSB=12,STR_LODSW,STR_LODSD,
+ STR_STOSB=16,STR_STOSW,STR_STOSD,
+ STR_SCASB=20,STR_SCASW,STR_SCASD,
+ STR_CMPSB=24,STR_CMPSW,STR_CMPSD
+};
+
+// repeat prefix type (for string operations)
+enum REP_Type {
+ REP_NONE=0,REP_NZ,REP_Z
+};
+
+// loop type
+enum LoopTypes {
+ LOOP_NONE,LOOP_NE,LOOP_E,LOOP_JCXZ
+};
+
+// rotate operand type
+enum grp2_types {
+ grp2_1,grp2_imm,grp2_cl
+};
+
+// opcode mapping for group1 instructions
+static DualOps grp1_table[8]={
+ DOP_ADD,DOP_OR,DOP_ADC,DOP_SBB,DOP_AND,DOP_SUB,DOP_XOR,DOP_CMP
+};
+
+
+// decoding information used during translation of a code block
+static struct DynDecode {
+ PhysPt code; // pointer to next byte in the instruction stream
+ PhysPt code_start; // pointer to the start of the current code block
+ PhysPt op_start; // pointer to the start of the current instruction
+ bool big_op; // operand modifier
+ bool big_addr; // address modifier
+ REP_Type rep; // current repeat prefix
+ Bitu cycles; // number cycles used by currently translated code
+ bool seg_prefix_used; // segment overridden
+ Bit8u seg_prefix; // segment prefix (if seg_prefix_used==true)
+
+ // block that contains the first instruction translated
+ CacheBlockDynRec * block;
+ // block that contains the current byte of the instruction stream
+ CacheBlockDynRec * active_block;
+
+ // the active page (containing the current byte of the instruction stream)
+ struct {
+ CodePageHandlerDynRec * code;
+ Bitu index; // index to the current byte of the instruction stream
+ Bit8u * wmap; // write map that indicates code presence for every byte of this page
+ Bit8u * invmap; // invalidation map
+ Bitu first; // page number
+ } page;
+
+ // modrm state of the current instruction (if used)
+ struct {
+// Bitu val;
+ Bitu mod;
+ Bitu rm;
+ Bitu reg;
+ } modrm;
+} decode;
+
+
+static bool MakeCodePage(Bitu lin_addr,CodePageHandlerDynRec * &cph) {
+ Bit8u rdval;
+ const Bitu cflag = cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16;
+ //Ensure page contains memory:
+ if (GCC_UNLIKELY(mem_readb_checked(lin_addr,&rdval))) return true;
+
+ PageHandler * handler=get_tlb_readhandler(lin_addr);
+ if (handler->flags & PFLAG_HASCODE) {
+ // this is a codepage handler, make sure it matches current code size
+ cph=(CodePageHandlerDynRec *)handler;
+ if (handler->flags & cflag) return false;
+ // wrong code size/stale dynamic code, drop it
+ cph->ClearRelease();
+ cph=0;
+ // handler was changed, refresh
+ handler=get_tlb_readhandler(lin_addr);
+ }
+ if (handler->flags & PFLAG_NOCODE) {
+ if (PAGING_ForcePageInit(lin_addr)) {
+ handler=get_tlb_readhandler(lin_addr);
+ if (handler->flags & PFLAG_HASCODE) {
+ cph=(CodePageHandlerDynRec *)handler;
+ if (handler->flags & cflag) return false;
+ cph->ClearRelease();
+ cph=0;
+ handler=get_tlb_readhandler(lin_addr);
+ }
+ }
+ if (handler->flags & PFLAG_NOCODE) {
+ LOG_MSG("DYNREC:Can't run code in this page");
+ cph=0;
+ return false;
+ }
+ }
+ Bitu lin_page=lin_addr>>12;
+ Bitu phys_page=lin_page;
+ // find the physical page that the linear page is mapped to
+ if (!PAGING_MakePhysPage(phys_page)) {
+ LOG_MSG("DYNREC:Can't find physpage");
+ cph=0;
+ return false;
+ }
+ // find a free CodePage
+ if (!cache.free_pages) {
+ if (cache.used_pages!=decode.page.code) cache.used_pages->ClearRelease();
+ else {
+ // try another page to avoid clearing our source-crosspage
+ if ((cache.used_pages->next) && (cache.used_pages->next!=decode.page.code))
+ cache.used_pages->next->ClearRelease();
+ else {
+ LOG_MSG("DYNREC:Invalid cache links");
+ cache.used_pages->ClearRelease();
+ }
+ }
+ }
+ CodePageHandlerDynRec * cpagehandler=cache.free_pages;
+ cache.free_pages=cache.free_pages->next;
+
+ // adjust previous and next page pointer
+ cpagehandler->prev=cache.last_page;
+ cpagehandler->next=0;
+ if (cache.last_page) cache.last_page->next=cpagehandler;
+ cache.last_page=cpagehandler;
+ if (!cache.used_pages) cache.used_pages=cpagehandler;
+
+ // initialize the code page handler and add the handler to the memory page
+ cpagehandler->SetupAt(phys_page,handler);
+ MEM_SetPageHandler(phys_page,1,cpagehandler);
+ PAGING_UnlinkPages(lin_page,1);
+ cph=cpagehandler;
+ return false;
+}
+
+static void decode_advancepage(void) {
+ // Advance to the next page
+ decode.active_block->page.end=4095;
+ // trigger possible page fault here
+ decode.page.first++;
+ Bitu faddr=decode.page.first << 12;
+ mem_readb(faddr);
+ MakeCodePage(faddr,decode.page.code);
+ CacheBlockDynRec * newblock=cache_getblock();
+ decode.active_block->crossblock=newblock;
+ newblock->crossblock=decode.active_block;
+ decode.active_block=newblock;
+ decode.active_block->page.start=0;
+ decode.page.code->AddCrossBlock(decode.active_block);
+ decode.page.wmap=decode.page.code->write_map;
+ decode.page.invmap=decode.page.code->invalidation_map;
+ decode.page.index=0;
+}
+
+// fetch the next byte of the instruction stream
+static Bit8u decode_fetchb(void) {
+ if (GCC_UNLIKELY(decode.page.index>=4096)) {
+ decode_advancepage();
+ }
+ decode.page.wmap[decode.page.index]+=0x01;
+ decode.page.index++;
+ decode.code+=1;
+ return mem_readb(decode.code-1);
+}
+// fetch the next word of the instruction stream
+static Bit16u decode_fetchw(void) {
+ if (GCC_UNLIKELY(decode.page.index>=4095)) {
+ Bit16u val=decode_fetchb();
+ val|=decode_fetchb() << 8;
+ return val;
+ }
+ *(Bit16u *)&decode.page.wmap[decode.page.index]+=0x0101;
+ decode.code+=2;decode.page.index+=2;
+ return mem_readw(decode.code-2);
+}
+// fetch the next dword of the instruction stream
+static Bit32u decode_fetchd(void) {
+ if (GCC_UNLIKELY(decode.page.index>=4093)) {
+ Bit32u val=decode_fetchb();
+ val|=decode_fetchb() << 8;
+ val|=decode_fetchb() << 16;
+ val|=decode_fetchb() << 24;
+ return val;
+ /* Advance to the next page */
+ }
+ *(Bit32u *)&decode.page.wmap[decode.page.index]+=0x01010101;
+ decode.code+=4;decode.page.index+=4;
+ return mem_readd(decode.code-4);
+}
+
+#define START_WMMEM 64
+
+// adjust writemap mask to care for map holes due to special
+// codefetch functions
+static void INLINE decode_increase_wmapmask(Bitu size) {
+ Bitu mapidx;
+ CacheBlockDynRec* activecb=decode.active_block;
+ if (GCC_UNLIKELY(!activecb->cache.wmapmask)) {
+ // no mask memory yet allocated, start with a small buffer
+ activecb->cache.wmapmask=(Bit8u*)malloc(START_WMMEM);
+ memset(activecb->cache.wmapmask,0,START_WMMEM);
+ activecb->cache.maskstart=decode.page.index; // start of buffer is current code position
+ activecb->cache.masklen=START_WMMEM;
+ mapidx=0;
+ } else {
+ mapidx=decode.page.index-activecb->cache.maskstart;
+ if (GCC_UNLIKELY(mapidx+size>=activecb->cache.masklen)) {
+ // mask buffer too small, increase
+ Bitu newmasklen=activecb->cache.masklen*4;
+ if (newmasklen<mapidx+size) newmasklen=((mapidx+size)&~3)*2;
+ Bit8u* tempmem=(Bit8u*)malloc(newmasklen);
+ memset(tempmem,0,newmasklen);
+ memcpy(tempmem,activecb->cache.wmapmask,activecb->cache.masklen);
+ free(activecb->cache.wmapmask);
+ activecb->cache.wmapmask=tempmem;
+ activecb->cache.masklen=newmasklen;
+ }
+ }
+ // update mask entries
+ switch (size) {
+ case 1 : activecb->cache.wmapmask[mapidx]+=0x01; break;
+ case 2 : (*(Bit16u*)&activecb->cache.wmapmask[mapidx])+=0x0101; break;
+ case 4 : (*(Bit32u*)&activecb->cache.wmapmask[mapidx])+=0x01010101; break;
+ }
+}
+
+// fetch a byte, val points to the code location if possible,
+// otherwise val contains the current value read from the position
+static bool decode_fetchb_imm(Bitu & val) {
+ if (GCC_UNLIKELY(decode.page.index>=4096)) {
+ decode_advancepage();
+ }
+ // see if position is directly accessible
+ if (decode.page.invmap != NULL) {
+ if (decode.page.invmap[decode.page.index] == 0) {
+ // position not yet modified
+ val=(Bit32u)decode_fetchb();
+ return false;
+ }
+
+ HostPt tlb_addr=get_tlb_read(decode.code);
+ if (tlb_addr) {
+ val=(Bitu)(tlb_addr+decode.code);
+ decode_increase_wmapmask(1);
+ decode.code++;
+ decode.page.index++;
+ return true;
+ }
+ }
+ // first time decoding or not directly accessible, just fetch the value
+ val=(Bit32u)decode_fetchb();
+ return false;
+}
+
+// fetch a word, val points to the code location if possible,
+// otherwise val contains the current value read from the position
+static bool decode_fetchw_imm(Bitu & val) {
+ if (decode.page.index<4095) {
+ if (decode.page.invmap != NULL) {
+ if ((decode.page.invmap[decode.page.index] == 0) &&
+ (decode.page.invmap[decode.page.index + 1] == 0)) {
+ // position not yet modified
+ val=decode_fetchw();
+ return false;
+ }
+
+ HostPt tlb_addr=get_tlb_read(decode.code);
+ // see if position is directly accessible
+ if (tlb_addr) {
+ val=(Bitu)(tlb_addr+decode.code);
+ decode_increase_wmapmask(2);
+ decode.code+=2;
+ decode.page.index+=2;
+ return true;
+ }
+ }
+ }
+ // first time decoding or not directly accessible, just fetch the value
+ val=decode_fetchw();
+ return false;
+}
+
+// fetch a dword, val points to the code location if possible,
+// otherwise val contains the current value read from the position
+static bool decode_fetchd_imm(Bitu & val) {
+ if (decode.page.index<4093) {
+ if (decode.page.invmap != NULL) {
+ if ((decode.page.invmap[decode.page.index] == 0) &&
+ (decode.page.invmap[decode.page.index + 1] == 0) &&
+ (decode.page.invmap[decode.page.index + 2] == 0) &&
+ (decode.page.invmap[decode.page.index + 3] == 0)) {
+ // position not yet modified
+ val=decode_fetchd();
+ return false;
+ }
+
+ HostPt tlb_addr=get_tlb_read(decode.code);
+ // see if position is directly accessible
+ if (tlb_addr) {
+ val=(Bitu)(tlb_addr+decode.code);
+ decode_increase_wmapmask(4);
+ decode.code+=4;
+ decode.page.index+=4;
+ return true;
+ }
+ }
+ }
+ // first time decoding or not directly accessible, just fetch the value
+ val=decode_fetchd();
+ return false;
+}
+
+
+// modrm decoding helper
+static void INLINE dyn_get_modrm(void) {
+ Bitu val=decode_fetchb();
+ decode.modrm.mod=(val >> 6) & 3;
+ decode.modrm.reg=(val >> 3) & 7;
+ decode.modrm.rm=(val & 7);
+}
+
+
+#ifdef DRC_USE_SEGS_ADDR
+
+#define MOV_SEG_VAL_TO_HOST_REG(host_reg, seg_index) gen_mov_seg16_to_reg(host_reg,(DRC_PTR_SIZE_IM)(DRCD_SEG_VAL(seg_index)) - (DRC_PTR_SIZE_IM)(&Segs))
+
+#define MOV_SEG_PHYS_TO_HOST_REG(host_reg, seg_index) gen_mov_seg32_to_reg(host_reg,(DRC_PTR_SIZE_IM)(DRCD_SEG_PHYS(seg_index)) - (DRC_PTR_SIZE_IM)(&Segs))
+#define ADD_SEG_PHYS_TO_HOST_REG(host_reg, seg_index) gen_add_seg32_to_reg(host_reg,(DRC_PTR_SIZE_IM)(DRCD_SEG_PHYS(seg_index)) - (DRC_PTR_SIZE_IM)(&Segs))
+
+#else
+
+#define MOV_SEG_VAL_TO_HOST_REG(host_reg, seg_index) gen_mov_word_to_reg(host_reg,DRCD_SEG_VAL(seg_index),false)
+
+#define MOV_SEG_PHYS_TO_HOST_REG(host_reg, seg_index) gen_mov_word_to_reg(host_reg,DRCD_SEG_PHYS(seg_index),true)
+#define ADD_SEG_PHYS_TO_HOST_REG(host_reg, seg_index) gen_add(host_reg,DRCD_SEG_PHYS(seg_index))
+
+#endif
+
+
+#ifdef DRC_USE_REGS_ADDR
+
+#define MOV_REG_VAL_TO_HOST_REG(host_reg, reg_index) gen_mov_regval32_to_reg(host_reg,(DRC_PTR_SIZE_IM)(DRCD_REG_VAL(reg_index)) - (DRC_PTR_SIZE_IM)(&cpu_regs))
+#define ADD_REG_VAL_TO_HOST_REG(host_reg, reg_index) gen_add_regval32_to_reg(host_reg,(DRC_PTR_SIZE_IM)(DRCD_REG_VAL(reg_index)) - (DRC_PTR_SIZE_IM)(&cpu_regs))
+
+#define MOV_REG_WORD16_TO_HOST_REG(host_reg, reg_index) gen_mov_regval16_to_reg(host_reg,(DRC_PTR_SIZE_IM)(DRCD_REG_WORD(reg_index,false)) - (DRC_PTR_SIZE_IM)(&cpu_regs))
+#define MOV_REG_WORD32_TO_HOST_REG(host_reg, reg_index) gen_mov_regval32_to_reg(host_reg,(DRC_PTR_SIZE_IM)(DRCD_REG_WORD(reg_index,true)) - (DRC_PTR_SIZE_IM)(&cpu_regs))
+#define MOV_REG_WORD_TO_HOST_REG(host_reg, reg_index, dword) gen_mov_regword_to_reg(host_reg,(DRC_PTR_SIZE_IM)(DRCD_REG_WORD(reg_index,dword)) - (DRC_PTR_SIZE_IM)(&cpu_regs), dword)
+
+#define MOV_REG_WORD16_FROM_HOST_REG(host_reg, reg_index) gen_mov_regval16_from_reg(host_reg,(DRC_PTR_SIZE_IM)(DRCD_REG_WORD(reg_index,false)) - (DRC_PTR_SIZE_IM)(&cpu_regs))
+#define MOV_REG_WORD32_FROM_HOST_REG(host_reg, reg_index) gen_mov_regval32_from_reg(host_reg,(DRC_PTR_SIZE_IM)(DRCD_REG_WORD(reg_index,true)) - (DRC_PTR_SIZE_IM)(&cpu_regs))
+#define MOV_REG_WORD_FROM_HOST_REG(host_reg, reg_index, dword) gen_mov_regword_from_reg(host_reg,(DRC_PTR_SIZE_IM)(DRCD_REG_WORD(reg_index,dword)) - (DRC_PTR_SIZE_IM)(&cpu_regs), dword)
+
+#define MOV_REG_BYTE_TO_HOST_REG_LOW(host_reg, reg_index, high_byte) gen_mov_regbyte_to_reg_low(host_reg,(DRC_PTR_SIZE_IM)(DRCD_REG_BYTE(reg_index,high_byte)) - (DRC_PTR_SIZE_IM)(&cpu_regs))
+#define MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(host_reg, reg_index, high_byte) gen_mov_regbyte_to_reg_low_canuseword(host_reg,(DRC_PTR_SIZE_IM)(DRCD_REG_BYTE(reg_index,high_byte)) - (DRC_PTR_SIZE_IM)(&cpu_regs))
+#define MOV_REG_BYTE_FROM_HOST_REG_LOW(host_reg, reg_index, high_byte) gen_mov_regbyte_from_reg_low(host_reg,(DRC_PTR_SIZE_IM)(DRCD_REG_BYTE(reg_index,high_byte)) - (DRC_PTR_SIZE_IM)(&cpu_regs))
+
+#else
+
+#define MOV_REG_VAL_TO_HOST_REG(host_reg, reg_index) gen_mov_word_to_reg(host_reg,DRCD_REG_VAL(reg_index),true)
+#define ADD_REG_VAL_TO_HOST_REG(host_reg, reg_index) gen_add(host_reg,DRCD_REG_VAL(reg_index))
+
+#define MOV_REG_WORD16_TO_HOST_REG(host_reg, reg_index) gen_mov_word_to_reg(host_reg,DRCD_REG_WORD(reg_index,false),false)
+#define MOV_REG_WORD32_TO_HOST_REG(host_reg, reg_index) gen_mov_word_to_reg(host_reg,DRCD_REG_WORD(reg_index,true),true)
+#define MOV_REG_WORD_TO_HOST_REG(host_reg, reg_index, dword) gen_mov_word_to_reg(host_reg,DRCD_REG_WORD(reg_index,dword),dword)
+
+#define MOV_REG_WORD16_FROM_HOST_REG(host_reg, reg_index) gen_mov_word_from_reg(host_reg,DRCD_REG_WORD(reg_index,false),false)
+#define MOV_REG_WORD32_FROM_HOST_REG(host_reg, reg_index) gen_mov_word_from_reg(host_reg,DRCD_REG_WORD(reg_index,true),true)
+#define MOV_REG_WORD_FROM_HOST_REG(host_reg, reg_index, dword) gen_mov_word_from_reg(host_reg,DRCD_REG_WORD(reg_index,dword),dword)
+
+#define MOV_REG_BYTE_TO_HOST_REG_LOW(host_reg, reg_index, high_byte) gen_mov_byte_to_reg_low(host_reg,DRCD_REG_BYTE(reg_index,high_byte))
+#define MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(host_reg, reg_index, high_byte) gen_mov_byte_to_reg_low_canuseword(host_reg,DRCD_REG_BYTE(reg_index,high_byte))
+#define MOV_REG_BYTE_FROM_HOST_REG_LOW(host_reg, reg_index, high_byte) gen_mov_byte_from_reg_low(host_reg,DRCD_REG_BYTE(reg_index,high_byte))
+
+#endif
+
+
+#define DYN_LEA_MEM_MEM(ea_reg, op1, op2, scale, imm) dyn_lea_mem_mem(ea_reg,op1,op2,scale,imm)
+
+#if defined(DRC_USE_REGS_ADDR) && defined(DRC_USE_SEGS_ADDR)
+
+#define DYN_LEA_SEG_PHYS_REG_VAL(ea_reg, op1_index, op2_index, scale, imm) dyn_lea_segphys_regval(ea_reg,op1_index,op2_index,scale,imm)
+#define DYN_LEA_REG_VAL_REG_VAL(ea_reg, op1_index, op2_index, scale, imm) dyn_lea_regval_regval(ea_reg,op1_index,op2_index,scale,imm)
+#define DYN_LEA_MEM_REG_VAL(ea_reg, op1, op2_index, scale, imm) dyn_lea_mem_regval(ea_reg,op1,op2_index,scale,imm)
+
+#elif defined(DRC_USE_REGS_ADDR)
+
+#define DYN_LEA_SEG_PHYS_REG_VAL(ea_reg, op1_index, op2_index, scale, imm) dyn_lea_mem_regval(ea_reg,DRCD_SEG_PHYS(op1_index),op2_index,scale,imm)
+#define DYN_LEA_REG_VAL_REG_VAL(ea_reg, op1_index, op2_index, scale, imm) dyn_lea_regval_regval(ea_reg,op1_index,op2_index,scale,imm)
+#define DYN_LEA_MEM_REG_VAL(ea_reg, op1, op2_index, scale, imm) dyn_lea_mem_regval(ea_reg,op1,op2_index,scale,imm)
+
+#elif defined(DRC_USE_SEGS_ADDR)
+
+#define DYN_LEA_SEG_PHYS_REG_VAL(ea_reg, op1_index, op2_index, scale, imm) dyn_lea_segphys_mem(ea_reg,op1_index,DRCD_REG_VAL(op2_index),scale,imm)
+#define DYN_LEA_REG_VAL_REG_VAL(ea_reg, op1_index, op2_index, scale, imm) dyn_lea_mem_mem(ea_reg,DRCD_REG_VAL(op1_index),DRCD_REG_VAL(op2_index),scale,imm)
+#define DYN_LEA_MEM_REG_VAL(ea_reg, op1, op2_index, scale, imm) dyn_lea_mem_mem(ea_reg,op1,DRCD_REG_VAL(op2_index),scale,imm)
+
+#else
+
+#define DYN_LEA_SEG_PHYS_REG_VAL(ea_reg, op1_index, op2_index, scale, imm) dyn_lea_mem_mem(ea_reg,DRCD_SEG_PHYS(op1_index),DRCD_REG_VAL(op2_index),scale,imm)
+#define DYN_LEA_REG_VAL_REG_VAL(ea_reg, op1_index, op2_index, scale, imm) dyn_lea_mem_mem(ea_reg,DRCD_REG_VAL(op1_index),DRCD_REG_VAL(op2_index),scale,imm)
+#define DYN_LEA_MEM_REG_VAL(ea_reg, op1, op2_index, scale, imm) dyn_lea_mem_mem(ea_reg,op1,DRCD_REG_VAL(op2_index),scale,imm)
+
+#endif
+
+
+
+// adjust CPU_Cycles value
+static void dyn_reduce_cycles(void) {
+ if (!decode.cycles) decode.cycles++;
+ gen_sub_direct_word(&CPU_Cycles,decode.cycles,true);
+}
+
+
+// set reg to the start of the next instruction
+// set reg_eip to the start of the current instruction
+static INLINE void dyn_set_eip_last_end(HostReg reg) {
+ gen_mov_word_to_reg(reg,&reg_eip,true);
+ gen_add_imm(reg,(Bit32u)(decode.code-decode.code_start));
+ gen_add_direct_word(&reg_eip,decode.op_start-decode.code_start,decode.big_op);
+}
+
+// set reg_eip to the start of the current instruction
+static INLINE void dyn_set_eip_last(void) {
+ gen_add_direct_word(&reg_eip,decode.op_start-decode.code_start,cpu.code.big);
+}
+
+// set reg_eip to the start of the next instruction
+static INLINE void dyn_set_eip_end(void) {
+ gen_add_direct_word(&reg_eip,decode.code-decode.code_start,cpu.code.big);
+}
+
+// set reg_eip to the start of the next instruction plus an offset (imm)
+static INLINE void dyn_set_eip_end(HostReg reg,Bit32u imm=0) {
+ gen_mov_word_to_reg(reg,&reg_eip,true); //get_extend_word will mask off the upper bits
+ //gen_mov_word_to_reg(reg,&reg_eip,decode.big_op);
+ gen_add_imm(reg,(Bit32u)(decode.code-decode.code_start+imm));
+ if (!decode.big_op) gen_extend_word(false,reg);
+}
+
+
+
+// the following functions generate function calls
+// parameters are loaded by generating code using gen_load_param_ which
+// is architecture dependent
+// R=host register; I=32bit immediate value; A=address value; m=memory
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_R(void * func,Bitu op) {
+ gen_load_param_reg(op,0);
+ return gen_call_function_setup(func, 1);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_R3(void * func,Bitu op) {
+ gen_load_param_reg(op,2);
+ return gen_call_function_setup(func, 3, true);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_RI(void * func,Bitu op1,Bitu op2) {
+ gen_load_param_imm(op2,1);
+ gen_load_param_reg(op1,0);
+ return gen_call_function_setup(func, 2);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_RA(void * func,Bitu op1,DRC_PTR_SIZE_IM op2) {
+ gen_load_param_addr(op2,1);
+ gen_load_param_reg(op1,0);
+ return gen_call_function_setup(func, 2);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_RR(void * func,Bitu op1,Bitu op2) {
+ gen_load_param_reg(op2,1);
+ gen_load_param_reg(op1,0);
+ return gen_call_function_setup(func, 2);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_IR(void * func,Bitu op1,Bitu op2) {
+ gen_load_param_reg(op2,1);
+ gen_load_param_imm(op1,0);
+ return gen_call_function_setup(func, 2);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_I(void * func,Bitu op) {
+ gen_load_param_imm(op,0);
+ return gen_call_function_setup(func, 1);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_II(void * func,Bitu op1,Bitu op2) {
+ gen_load_param_imm(op2,1);
+ gen_load_param_imm(op1,0);
+ return gen_call_function_setup(func, 2);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_III(void * func,Bitu op1,Bitu op2,Bitu op3) {
+ gen_load_param_imm(op3,2);
+ gen_load_param_imm(op2,1);
+ gen_load_param_imm(op1,0);
+ return gen_call_function_setup(func, 3);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_IA(void * func,Bitu op1,DRC_PTR_SIZE_IM op2) {
+ gen_load_param_addr(op2,1);
+ gen_load_param_imm(op1,0);
+ return gen_call_function_setup(func, 2);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_IIR(void * func,Bitu op1,Bitu op2,Bitu op3) {
+ gen_load_param_reg(op3,2);
+ gen_load_param_imm(op2,1);
+ gen_load_param_imm(op1,0);
+ return gen_call_function_setup(func, 3);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_IIIR(void * func,Bitu op1,Bitu op2,Bitu op3,Bitu op4) {
+ gen_load_param_reg(op4,3);
+ gen_load_param_imm(op3,2);
+ gen_load_param_imm(op2,1);
+ gen_load_param_imm(op1,0);
+ return gen_call_function_setup(func, 4);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_IRRR(void * func,Bitu op1,Bitu op2,Bitu op3,Bitu op4) {
+ gen_load_param_reg(op4,3);
+ gen_load_param_reg(op3,2);
+ gen_load_param_reg(op2,1);
+ gen_load_param_imm(op1,0);
+ return gen_call_function_setup(func, 4);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_m(void * func,Bitu op) {
+ gen_load_param_mem(op,2);
+ return gen_call_function_setup(func, 3, true);
+}
+
+static DRC_PTR_SIZE_IM INLINE gen_call_function_mm(void * func,Bitu op1,Bitu op2) {
+ gen_load_param_mem(op2,3);
+ gen_load_param_mem(op1,2);
+ return gen_call_function_setup(func, 4, true);
+}
+
+
+
+enum save_info_type {db_exception, cycle_check, string_break};
+
+
+// function that is called on exceptions
+static BlockReturn DynRunException(Bit32u eip_add,Bit32u cycle_sub) {
+ reg_eip+=eip_add;
+ CPU_Cycles-=cycle_sub;
+ if (cpu.exception.which==SMC_CURRENT_BLOCK) return BR_SMCBlock;
+ CPU_Exception(cpu.exception.which,cpu.exception.error);
+ return BR_Normal;
+}
+
+
+// array with information about code that is generated at the
+// end of a cache block because it is rarely reached (like exceptions)
+static struct {
+ save_info_type type;
+ DRC_PTR_SIZE_IM branch_pos;
+ Bit32u eip_change;
+ Bitu cycles;
+} save_info_dynrec[512];
+
+Bitu used_save_info_dynrec=0;
+
+
+// return from current block, with returncode
+static void dyn_return(BlockReturn retcode,bool ret_exception=false) {
+ if (!ret_exception) {
+ gen_mov_dword_to_reg_imm(FC_RETOP,retcode);
+ }
+ gen_return_function();
+}
+
+static void dyn_run_code(void) {
+ gen_run_code();
+ gen_return_function();
+}
+
+// fill in code at the end of the block that contains rarely-executed code
+// which is executed conditionally (like exceptions)
+static void dyn_fill_blocks(void) {
+ for (Bitu sct=0; sct<used_save_info_dynrec; sct++) {
+ gen_fill_branch_long(save_info_dynrec[sct].branch_pos);
+ switch (save_info_dynrec[sct].type) {
+ case db_exception:
+ // code for exception handling, load cycles and call DynRunException
+ decode.cycles=save_info_dynrec[sct].cycles;
+ if (cpu.code.big) gen_call_function_II((void *)&DynRunException,save_info_dynrec[sct].eip_change,save_info_dynrec[sct].cycles);
+ else gen_call_function_II((void *)&DynRunException,save_info_dynrec[sct].eip_change&0xffff,save_info_dynrec[sct].cycles);
+ dyn_return(BR_Normal,true);
+ break;
+ case cycle_check:
+ // cycles are <=0 so exit the core
+ dyn_return(BR_Cycles);
+ break;
+ case string_break:
+ // interrupt looped string instruction, can be continued later
+ gen_add_direct_word(&reg_eip,save_info_dynrec[sct].eip_change,decode.big_op);
+ dyn_return(BR_Cycles);
+ break;
+ }
+ }
+ used_save_info_dynrec=0;
+}
+
+
+static void dyn_closeblock(void) {
+ //Shouldn't create empty block normally but let's do it like this
+ dyn_fill_blocks();
+ cache_block_before_close();
+ cache_closeblock();
+ cache_block_closing(decode.block->cache.start,decode.block->cache.size);
+}
+
+
+// add a check that can branch to the exception handling
+static void dyn_check_exception(HostReg reg) {
+ save_info_dynrec[used_save_info_dynrec].branch_pos=gen_create_branch_long_nonzero(reg,false);
+ if (!decode.cycles) decode.cycles++;
+ save_info_dynrec[used_save_info_dynrec].cycles=decode.cycles;
+ // in case of an exception eip will point to the start of the current instruction
+ save_info_dynrec[used_save_info_dynrec].eip_change=decode.op_start-decode.code_start;
+ if (!cpu.code.big) save_info_dynrec[used_save_info_dynrec].eip_change&=0xffff;
+ save_info_dynrec[used_save_info_dynrec].type=db_exception;
+ used_save_info_dynrec++;
+}
+
+
+
+bool DRC_CALL_CONV mem_readb_checked_drc(PhysPt address) DRC_FC;
+bool DRC_CALL_CONV mem_readb_checked_drc(PhysPt address) {
+ HostPt tlb_addr=get_tlb_read(address);
+ if (tlb_addr) {
+ *((Bit8u*)(&core_dynrec.readdata))=host_readb(tlb_addr+address);
+ return false;
+ } else {
+ return get_tlb_readhandler(address)->readb_checked(address, (Bit8u*)(&core_dynrec.readdata));
+ }
+}
+
+bool DRC_CALL_CONV mem_writeb_checked_drc(PhysPt address,Bit8u val) DRC_FC;
+bool DRC_CALL_CONV mem_writeb_checked_drc(PhysPt address,Bit8u val) {
+ HostPt tlb_addr=get_tlb_write(address);
+ if (tlb_addr) {
+ host_writeb(tlb_addr+address,val);
+ return false;
+ } else return get_tlb_writehandler(address)->writeb_checked(address,val);
+}
+
+bool DRC_CALL_CONV mem_readw_checked_drc(PhysPt address) DRC_FC;
+bool DRC_CALL_CONV mem_readw_checked_drc(PhysPt address) {
+ if ((address & 0xfff)<0xfff) {
+ HostPt tlb_addr=get_tlb_read(address);
+ if (tlb_addr) {
+ *((Bit16u*)(&core_dynrec.readdata))=host_readw(tlb_addr+address);
+ return false;
+ } else return get_tlb_readhandler(address)->readw_checked(address, (Bit16u*)(&core_dynrec.readdata));
+ } else return mem_unalignedreadw_checked(address, ((Bit16u*)(&core_dynrec.readdata)));
+}
+
+bool DRC_CALL_CONV mem_readd_checked_drc(PhysPt address) DRC_FC;
+bool DRC_CALL_CONV mem_readd_checked_drc(PhysPt address) {
+ if ((address & 0xfff)<0xffd) {
+ HostPt tlb_addr=get_tlb_read(address);
+ if (tlb_addr) {
+ *((Bit32u*)(&core_dynrec.readdata))=host_readd(tlb_addr+address);
+ return false;
+ } else return get_tlb_readhandler(address)->readd_checked(address, (Bit32u*)(&core_dynrec.readdata));
+ } else return mem_unalignedreadd_checked(address, ((Bit32u*)(&core_dynrec.readdata)));
+}
+
+bool DRC_CALL_CONV mem_writew_checked_drc(PhysPt address,Bit16u val) DRC_FC;
+bool DRC_CALL_CONV mem_writew_checked_drc(PhysPt address,Bit16u val) {
+ if ((address & 0xfff)<0xfff) {
+ HostPt tlb_addr=get_tlb_write(address);
+ if (tlb_addr) {
+ host_writew(tlb_addr+address,val);
+ return false;
+ } else return get_tlb_writehandler(address)->writew_checked(address,val);
+ } else return mem_unalignedwritew_checked(address,val);
+}
+
+bool DRC_CALL_CONV mem_writed_checked_drc(PhysPt address,Bit32u val) DRC_FC;
+bool DRC_CALL_CONV mem_writed_checked_drc(PhysPt address,Bit32u val) {
+ if ((address & 0xfff)<0xffd) {
+ HostPt tlb_addr=get_tlb_write(address);
+ if (tlb_addr) {
+ host_writed(tlb_addr+address,val);
+ return false;
+ } else return get_tlb_writehandler(address)->writed_checked(address,val);
+ } else return mem_unalignedwrited_checked(address,val);
+}
+
+
+// functions that enable access to the memory
+
+// read a byte from a given address and store it in reg_dst
+static void dyn_read_byte(HostReg reg_addr,HostReg reg_dst) {
+ gen_mov_regs(FC_OP1,reg_addr);
+ gen_call_function_raw((void *)&mem_readb_checked_drc);
+ dyn_check_exception(FC_RETOP);
+ gen_mov_byte_to_reg_low(reg_dst,&core_dynrec.readdata);
+}
+static void dyn_read_byte_canuseword(HostReg reg_addr,HostReg reg_dst) {
+ gen_mov_regs(FC_OP1,reg_addr);
+ gen_call_function_raw((void *)&mem_readb_checked_drc);
+ dyn_check_exception(FC_RETOP);
+ gen_mov_byte_to_reg_low_canuseword(reg_dst,&core_dynrec.readdata);
+}
+
+// write a byte from reg_val into the memory given by the address
+static void dyn_write_byte(HostReg reg_addr,HostReg reg_val) {
+ gen_mov_regs(FC_OP2,reg_val);
+ gen_mov_regs(FC_OP1,reg_addr);
+ gen_call_function_raw((void *)&mem_writeb_checked_drc);
+ dyn_check_exception(FC_RETOP);
+}
+
+// read a 32bit (dword=true) or 16bit (dword=false) value
+// from a given address and store it in reg_dst
+static void dyn_read_word(HostReg reg_addr,HostReg reg_dst,bool dword) {
+ gen_mov_regs(FC_OP1,reg_addr);
+ if (dword) gen_call_function_raw((void *)&mem_readd_checked_drc);
+ else gen_call_function_raw((void *)&mem_readw_checked_drc);
+ dyn_check_exception(FC_RETOP);
+ gen_mov_word_to_reg(reg_dst,&core_dynrec.readdata,dword);
+}
+
+// write a 32bit (dword=true) or 16bit (dword=false) value
+// from reg_val into the memory given by the address
+static void dyn_write_word(HostReg reg_addr,HostReg reg_val,bool dword) {
+// if (!dword) gen_extend_word(false,reg_val);
+ gen_mov_regs(FC_OP2,reg_val);
+ gen_mov_regs(FC_OP1,reg_addr);
+ if (dword) gen_call_function_raw((void *)&mem_writed_checked_drc);
+ else gen_call_function_raw((void *)&mem_writew_checked_drc);
+ dyn_check_exception(FC_RETOP);
+}
+
+
+
+// effective address calculation helper, op2 has to be present!
+// loads op1 into ea_reg and adds the scaled op2 and the immediate to it
+static void dyn_lea_mem_mem(HostReg ea_reg,void* op1,void* op2,Bitu scale,Bits imm) {
+ if (scale || imm) {
+ if (op1!=NULL) {
+ gen_mov_word_to_reg(ea_reg,op1,true);
+ gen_mov_word_to_reg(TEMP_REG_DRC,op2,true);
+
+ gen_lea(ea_reg,TEMP_REG_DRC,scale,imm);
+ } else {
+ gen_mov_word_to_reg(ea_reg,op2,true);
+ gen_lea(ea_reg,scale,imm);
+ }
+ } else {
+ gen_mov_word_to_reg(ea_reg,op2,true);
+ if (op1!=NULL) gen_add(ea_reg,op1);
+ }
+}
+
+#ifdef DRC_USE_REGS_ADDR
+// effective address calculation helper
+// loads op1 into ea_reg and adds the scaled op2 and the immediate to it
+// op1 is cpu_regs[op1_index], op2 is cpu_regs[op2_index]
+static void dyn_lea_regval_regval(HostReg ea_reg,Bitu op1_index,Bitu op2_index,Bitu scale,Bits imm) {
+ if (scale || imm) {
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,op1_index);
+ MOV_REG_VAL_TO_HOST_REG(TEMP_REG_DRC,op2_index);
+
+ gen_lea(ea_reg,TEMP_REG_DRC,scale,imm);
+ } else {
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,op2_index);
+ ADD_REG_VAL_TO_HOST_REG(ea_reg,op1_index);
+ }
+}
+
+// effective address calculation helper
+// loads op1 into ea_reg and adds the scaled op2 and the immediate to it
+// op2 is cpu_regs[op2_index]
+static void dyn_lea_mem_regval(HostReg ea_reg,void* op1,Bitu op2_index,Bitu scale,Bits imm) {
+ if (scale || imm) {
+ if (op1!=NULL) {
+ gen_mov_word_to_reg(ea_reg,op1,true);
+ MOV_REG_VAL_TO_HOST_REG(TEMP_REG_DRC,op2_index);
+
+ gen_lea(ea_reg,TEMP_REG_DRC,scale,imm);
+ } else {
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,op2_index);
+ gen_lea(ea_reg,scale,imm);
+ }
+ } else {
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,op2_index);
+ if (op1!=NULL) gen_add(ea_reg,op1);
+ }
+}
+#endif
+
+#ifdef DRC_USE_SEGS_ADDR
+#ifdef DRC_USE_REGS_ADDR
+// effective address calculation helper
+// loads op1 into ea_reg and adds the scaled op2 and the immediate to it
+// op1 is Segs[op1_index], op2 is cpu_regs[op2_index]
+static void dyn_lea_segphys_regval(HostReg ea_reg,Bitu op1_index,Bitu op2_index,Bitu scale,Bits imm) {
+ if (scale || imm) {
+ MOV_SEG_PHYS_TO_HOST_REG(ea_reg,op1_index);
+ MOV_REG_VAL_TO_HOST_REG(TEMP_REG_DRC,op2_index);
+
+ gen_lea(ea_reg,TEMP_REG_DRC,scale,imm);
+ } else {
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,op2_index);
+ ADD_SEG_PHYS_TO_HOST_REG(ea_reg,op1_index);
+ }
+}
+
+#else
+
+// effective address calculation helper, op2 has to be present!
+// loads op1 into ea_reg and adds the scaled op2 and the immediate to it
+// op1 is Segs[op1_index]
+static void dyn_lea_segphys_mem(HostReg ea_reg,Bitu op1_index,void* op2,Bitu scale,Bits imm) {
+ if (scale || imm) {
+ MOV_SEG_PHYS_TO_HOST_REG(ea_reg,op1_index);
+ gen_mov_word_to_reg(TEMP_REG_DRC,op2,true);
+
+ gen_lea(ea_reg,TEMP_REG_DRC,scale,imm);
+ } else {
+ gen_mov_word_to_reg(ea_reg,op2,true);
+ ADD_SEG_PHYS_TO_HOST_REG(ea_reg,op1_index);
+ }
+}
+#endif
+#endif
+
+// calculate the effective address and store it in ea_reg
+static void dyn_fill_ea(HostReg ea_reg,bool addseg=true) {
+ Bit8u seg_base=DRC_SEG_DS;
+ if (!decode.big_addr) {
+ Bits imm;
+ switch (decode.modrm.mod) {
+ case 0:imm=0;break;
+ case 1:imm=(Bit8s)decode_fetchb();break;
+ case 2:imm=(Bit16s)decode_fetchw();break;
+ }
+ switch (decode.modrm.rm) {
+ case 0:// BX+SI
+ DYN_LEA_REG_VAL_REG_VAL(ea_reg,DRC_REG_EBX,DRC_REG_ESI,0,imm);
+ break;
+ case 1:// BX+DI
+ DYN_LEA_REG_VAL_REG_VAL(ea_reg,DRC_REG_EBX,DRC_REG_EDI,0,imm);
+ break;
+ case 2:// BP+SI
+ DYN_LEA_REG_VAL_REG_VAL(ea_reg,DRC_REG_EBP,DRC_REG_ESI,0,imm);
+ seg_base=DRC_SEG_SS;
+ break;
+ case 3:// BP+DI
+ DYN_LEA_REG_VAL_REG_VAL(ea_reg,DRC_REG_EBP,DRC_REG_EDI,0,imm);
+ seg_base=DRC_SEG_SS;
+ break;
+ case 4:// SI
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,DRC_REG_ESI);
+ if (imm) gen_add_imm(ea_reg,(Bit32u)imm);
+ break;
+ case 5:// DI
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,DRC_REG_EDI);
+ if (imm) gen_add_imm(ea_reg,(Bit32u)imm);
+ break;
+ case 6:// imm/BP
+ if (!decode.modrm.mod) {
+ imm=decode_fetchw();
+ gen_mov_dword_to_reg_imm(ea_reg,(Bit32u)imm);
+ goto skip_extend_word;
+ } else {
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,DRC_REG_EBP);
+ gen_add_imm(ea_reg,(Bit32u)imm);
+ seg_base=DRC_SEG_SS;
+ }
+ break;
+ case 7: // BX
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,DRC_REG_EBX);
+ if (imm) gen_add_imm(ea_reg,(Bit32u)imm);
+ break;
+ }
+ // zero out the high 16bit so ea_reg can be used as full register
+ gen_extend_word(false,ea_reg);
+skip_extend_word:
+ if (addseg) {
+ // add the physical segment value if requested
+ ADD_SEG_PHYS_TO_HOST_REG(ea_reg,(decode.seg_prefix_used ? decode.seg_prefix : seg_base));
+ }
+ } else {
+ Bits imm=0;
+ Bit8u base_reg;
+ Bit8u scaled_reg;
+ Bitu scale=0;
+ switch (decode.modrm.rm) {
+ case 0:base_reg=DRC_REG_EAX;break;
+ case 1:base_reg=DRC_REG_ECX;break;
+ case 2:base_reg=DRC_REG_EDX;break;
+ case 3:base_reg=DRC_REG_EBX;break;
+ case 4: // SIB
+ {
+ Bitu sib=decode_fetchb();
+ bool scaled_reg_used=false;
+ static Bit8u scaledtable[8]={
+ DRC_REG_EAX,DRC_REG_ECX,DRC_REG_EDX,DRC_REG_EBX,
+ 0,DRC_REG_EBP,DRC_REG_ESI,DRC_REG_EDI
+ };
+ // see if scaling should be used and which register is to be scaled in this case
+ if (((sib >> 3) &7)!=4) scaled_reg_used=true;
+ scaled_reg=scaledtable[(sib >> 3) &7];
+ scale=(sib >> 6);
+
+ switch (sib & 7) {
+ case 0:base_reg=DRC_REG_EAX;break;
+ case 1:base_reg=DRC_REG_ECX;break;
+ case 2:base_reg=DRC_REG_EDX;break;
+ case 3:base_reg=DRC_REG_EBX;break;
+ case 4:base_reg=DRC_REG_ESP;seg_base=DRC_SEG_SS;break;
+ case 5:
+ if (decode.modrm.mod) {
+ base_reg=DRC_REG_EBP;seg_base=DRC_SEG_SS;
+ } else {
+ // no basereg, maybe scalereg
+ Bitu val;
+ // try to get a pointer to the next dword code position
+ if (decode_fetchd_imm(val)) {
+ // succeeded, use the pointer to avoid code invalidation
+ if (!addseg) {
+ if (!scaled_reg_used) {
+ gen_mov_word_to_reg(ea_reg,(void*)val,true);
+ } else {
+ DYN_LEA_MEM_REG_VAL(ea_reg,NULL,scaled_reg,scale,0);
+ gen_add(ea_reg,(void*)val);
+ }
+ } else {
+ if (!scaled_reg_used) {
+ MOV_SEG_PHYS_TO_HOST_REG(ea_reg,(decode.seg_prefix_used ? decode.seg_prefix : seg_base));
+ } else {
+ DYN_LEA_SEG_PHYS_REG_VAL(ea_reg,(decode.seg_prefix_used ? decode.seg_prefix : seg_base),scaled_reg,scale,0);
+ }
+ gen_add(ea_reg,(void*)val);
+ }
+ return;
+ }
+ // couldn't get a pointer, use the current value
+ imm=(Bit32s)val;
+
+ if (!addseg) {
+ if (!scaled_reg_used) {
+ gen_mov_dword_to_reg_imm(ea_reg,(Bit32u)imm);
+ } else {
+ DYN_LEA_MEM_REG_VAL(ea_reg,NULL,scaled_reg,scale,imm);
+ }
+ } else {
+ if (!scaled_reg_used) {
+ MOV_SEG_PHYS_TO_HOST_REG(ea_reg,(decode.seg_prefix_used ? decode.seg_prefix : seg_base));
+ if (imm) gen_add_imm(ea_reg,(Bit32u)imm);
+ } else {
+ DYN_LEA_SEG_PHYS_REG_VAL(ea_reg,(decode.seg_prefix_used ? decode.seg_prefix : seg_base),scaled_reg,scale,imm);
+ }
+ }
+
+ return;
+ }
+ break;
+ case 6:base_reg=DRC_REG_ESI;break;
+ case 7:base_reg=DRC_REG_EDI;break;
+ }
+ // basereg, maybe scalereg
+ switch (decode.modrm.mod) {
+ case 1:
+ imm=(Bit8s)decode_fetchb();
+ break;
+ case 2: {
+ Bitu val;
+ // try to get a pointer to the next dword code position
+ if (decode_fetchd_imm(val)) {
+ // succeeded, use the pointer to avoid code invalidation
+ if (!addseg) {
+ if (!scaled_reg_used) {
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,base_reg);
+ gen_add(ea_reg,(void*)val);
+ } else {
+ DYN_LEA_REG_VAL_REG_VAL(ea_reg,base_reg,scaled_reg,scale,0);
+ gen_add(ea_reg,(void*)val);
+ }
+ } else {
+ if (!scaled_reg_used) {
+ MOV_SEG_PHYS_TO_HOST_REG(ea_reg,(decode.seg_prefix_used ? decode.seg_prefix : seg_base));
+ } else {
+ DYN_LEA_SEG_PHYS_REG_VAL(ea_reg,(decode.seg_prefix_used ? decode.seg_prefix : seg_base),scaled_reg,scale,0);
+ }
+ ADD_REG_VAL_TO_HOST_REG(ea_reg,base_reg);
+ gen_add(ea_reg,(void*)val);
+ }
+ return;
+ }
+ // couldn't get a pointer, use the current value
+ imm=(Bit32s)val;
+ break;
+ }
+ }
+
+ if (!addseg) {
+ if (!scaled_reg_used) {
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,base_reg);
+ gen_add_imm(ea_reg,(Bit32u)imm);
+ } else {
+ DYN_LEA_REG_VAL_REG_VAL(ea_reg,base_reg,scaled_reg,scale,imm);
+ }
+ } else {
+ if (!scaled_reg_used) {
+ MOV_SEG_PHYS_TO_HOST_REG(ea_reg,(decode.seg_prefix_used ? decode.seg_prefix : seg_base));
+ ADD_REG_VAL_TO_HOST_REG(ea_reg,base_reg);
+ if (imm) gen_add_imm(ea_reg,(Bit32u)imm);
+ } else {
+ DYN_LEA_SEG_PHYS_REG_VAL(ea_reg,(decode.seg_prefix_used ? decode.seg_prefix : seg_base),scaled_reg,scale,imm);
+ ADD_REG_VAL_TO_HOST_REG(ea_reg,base_reg);
+ }
+ }
+
+ return;
+ }
+ break; // SIB Break
+ case 5:
+ if (decode.modrm.mod) {
+ base_reg=DRC_REG_EBP;seg_base=DRC_SEG_SS;
+ } else {
+ // no base, no scalereg
+
+ imm=(Bit32s)decode_fetchd();
+ if (!addseg) {
+ gen_mov_dword_to_reg_imm(ea_reg,(Bit32u)imm);
+ } else {
+ MOV_SEG_PHYS_TO_HOST_REG(ea_reg,(decode.seg_prefix_used ? decode.seg_prefix : seg_base));
+ if (imm) gen_add_imm(ea_reg,(Bit32u)imm);
+ }
+
+ return;
+ }
+ break;
+ case 6:base_reg=DRC_REG_ESI;break;
+ case 7:base_reg=DRC_REG_EDI;break;
+ }
+
+ // no scalereg, but basereg
+
+ switch (decode.modrm.mod) {
+ case 1:
+ imm=(Bit8s)decode_fetchb();
+ break;
+ case 2: {
+ Bitu val;
+ // try to get a pointer to the next dword code position
+ if (decode_fetchd_imm(val)) {
+ // succeeded, use the pointer to avoid code invalidation
+ if (!addseg) {
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,base_reg);
+ gen_add(ea_reg,(void*)val);
+ } else {
+ MOV_SEG_PHYS_TO_HOST_REG(ea_reg,(decode.seg_prefix_used ? decode.seg_prefix : seg_base));
+ ADD_REG_VAL_TO_HOST_REG(ea_reg,base_reg);
+ gen_add(ea_reg,(void*)val);
+ }
+ return;
+ }
+ // couldn't get a pointer, use the current value
+ imm=(Bit32s)val;
+ break;
+ }
+ }
+
+ if (!addseg) {
+ MOV_REG_VAL_TO_HOST_REG(ea_reg,base_reg);
+ if (imm) gen_add_imm(ea_reg,(Bit32u)imm);
+ } else {
+ MOV_SEG_PHYS_TO_HOST_REG(ea_reg,(decode.seg_prefix_used ? decode.seg_prefix : seg_base));
+ ADD_REG_VAL_TO_HOST_REG(ea_reg,base_reg);
+ if (imm) gen_add_imm(ea_reg,(Bit32u)imm);
+ }
+ }
+}
+
+
+
+// add code that checks if port access is allowed
+// the port is given in a register
+static void dyn_add_iocheck(HostReg reg_port,Bitu access_size) {
+ if (cpu.pmode) {
+ gen_call_function_RI((void *)&CPU_IO_Exception,reg_port,access_size);
+ dyn_check_exception(FC_RETOP);
+ }
+}
+
+// add code that checks if port access is allowed
+// the port is a constant
+static void dyn_add_iocheck_var(Bit8u accessed_port,Bitu access_size) {
+ if (cpu.pmode) {
+ gen_call_function_II((void *)&CPU_IO_Exception,accessed_port,access_size);
+ dyn_check_exception(FC_RETOP);
+ }
+}
+
+
+
+// save back the address register
+static void gen_protect_addr_reg(void) {
+#ifdef DRC_PROTECT_ADDR_REG
+ gen_mov_word_from_reg(FC_ADDR,&core_dynrec.protected_regs[FC_ADDR],true);
+#endif
+}
+
+// restore the address register
+static void gen_restore_addr_reg(void) {
+#ifdef DRC_PROTECT_ADDR_REG
+ gen_mov_word_to_reg(FC_ADDR,&core_dynrec.protected_regs[FC_ADDR],true);
+#endif
+}
+
+// save back an arbitrary register
+static void gen_protect_reg(HostReg reg) {
+ gen_mov_word_from_reg(reg,&core_dynrec.protected_regs[reg],true);
+}
+
+// restore an arbitrary register
+static void gen_restore_reg(HostReg reg) {
+ gen_mov_word_to_reg(reg,&core_dynrec.protected_regs[reg],true);
+}
+
+// restore an arbitrary register into a different register
+static void gen_restore_reg(HostReg reg,HostReg dest_reg) {
+ gen_mov_word_to_reg(dest_reg,&core_dynrec.protected_regs[reg],true);
+}
+
+
+
+// flags optimization functions
+// they try to find out if a function can be replaced by another
+// one that does not generate any flags at all
+
+static Bitu mf_functions_num=0;
+static struct {
+ Bit8u* pos;
+ void* fct_ptr;
+ Bitu ftype;
+} mf_functions[64];
+
+static void InitFlagsOptimization(void) {
+ mf_functions_num=0;
+}
+
+// replace all queued functions with their simpler variants
+// because the current instruction destroys all condition flags and
+// the flags are not required before
+static void InvalidateFlags(void) {
+#ifdef DRC_FLAGS_INVALIDATION
+ for (Bitu ct=0; ct<mf_functions_num; ct++) {
+ gen_fill_function_ptr(mf_functions[ct].pos,mf_functions[ct].fct_ptr,mf_functions[ct].ftype);
+ }
+ mf_functions_num=0;
+#endif
+}
+
+// replace all queued functions with their simpler variants
+// because the current instruction destroys all condition flags and
+// the flags are not required before
+static void InvalidateFlags(void* current_simple_function,Bitu flags_type) {
+#ifdef DRC_FLAGS_INVALIDATION
+ for (Bitu ct=0; ct<mf_functions_num; ct++) {
+ gen_fill_function_ptr(mf_functions[ct].pos,mf_functions[ct].fct_ptr,mf_functions[ct].ftype);
+ }
+ mf_functions_num=1;
+ mf_functions[0].pos=cache.pos;
+ mf_functions[0].fct_ptr=current_simple_function;
+ mf_functions[0].ftype=flags_type;
+#endif
+}
+
+// enqueue this instruction, if later an instruction is encountered that
+// destroys all condition flags and the flags weren't needed in-between
+// this function can be replaced by a simpler one as well
+static void InvalidateFlagsPartially(void* current_simple_function,Bitu flags_type) {
+#ifdef DRC_FLAGS_INVALIDATION
+ mf_functions[mf_functions_num].pos=cache.pos;
+ mf_functions[mf_functions_num].fct_ptr=current_simple_function;
+ mf_functions[mf_functions_num].ftype=flags_type;
+ mf_functions_num++;
+#endif
+}
+
+// enqueue this instruction, if later an instruction is encountered that
+// destroys all condition flags and the flags weren't needed in-between
+// this function can be replaced by a simpler one as well
+static void InvalidateFlagsPartially(void* current_simple_function,DRC_PTR_SIZE_IM cpos,Bitu flags_type) {
+#ifdef DRC_FLAGS_INVALIDATION
+ mf_functions[mf_functions_num].pos=(Bit8u*)cpos;
+ mf_functions[mf_functions_num].fct_ptr=current_simple_function;
+ mf_functions[mf_functions_num].ftype=flags_type;
+ mf_functions_num++;
+#endif
+}
+
+// the current function needs the condition flags thus reset the queue
+static void AcquireFlags(Bitu flags_mask) {
+#ifdef DRC_FLAGS_INVALIDATION
+ mf_functions_num=0;
+#endif
+}
diff --git a/src/cpu/core_dynrec/decoder_opcodes.h b/src/cpu/core_dynrec/decoder_opcodes.h
new file mode 100644
index 000000000..67eaee2c9
--- /dev/null
+++ b/src/cpu/core_dynrec/decoder_opcodes.h
@@ -0,0 +1,1438 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+/*
+ The functions in this file are called almost exclusively by decoder.h,
+ they translate an (or a set of) instruction(s) into hostspecific code
+ and use the lower-level functions from decoder_basic.h and generating
+ functions from risc_?.h
+ Some complex instructions as well as most instructions that can modify
+ the condition flags are translated by generating a call to a C-function
+ (see operators.h). Parameter loading and result writeback is done
+ according to the instruction.
+*/
+
+static void dyn_dop_ebgb(DualOps op) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_read_byte_canuseword(FC_ADDR,FC_OP1);
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP2,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ dyn_dop_byte_gencall(op);
+
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) {
+ gen_restore_addr_reg();
+ dyn_write_byte(FC_ADDR,FC_RETOP);
+ }
+ } else {
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP1,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP2,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ dyn_dop_byte_gencall(op);
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_RETOP,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ }
+}
+
+static void dyn_dop_ebgb_mov(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ MOV_REG_BYTE_TO_HOST_REG_LOW(FC_TMP_BA1,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ dyn_write_byte(FC_ADDR,FC_TMP_BA1);
+ } else {
+ MOV_REG_BYTE_TO_HOST_REG_LOW(FC_TMP_BA1,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_TMP_BA1,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ }
+}
+
+static void dyn_dop_ebib_mov(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ gen_mov_byte_to_reg_low_imm(FC_TMP_BA1,decode_fetchb());
+ dyn_write_byte(FC_ADDR,FC_TMP_BA1);
+ } else {
+ gen_mov_byte_to_reg_low_imm(FC_TMP_BA1,decode_fetchb());
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_TMP_BA1,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ }
+}
+
+static void dyn_dop_ebgb_xchg(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_read_byte(FC_ADDR,FC_TMP_BA1);
+ MOV_REG_BYTE_TO_HOST_REG_LOW(FC_TMP_BA2,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_TMP_BA1,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ gen_restore_addr_reg();
+ dyn_write_byte(FC_ADDR,FC_TMP_BA2);
+ } else {
+ MOV_REG_BYTE_TO_HOST_REG_LOW(FC_TMP_BA1,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ MOV_REG_BYTE_TO_HOST_REG_LOW(FC_TMP_BA2,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_TMP_BA1,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_TMP_BA2,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ }
+}
+
+static void dyn_dop_gbeb(DualOps op) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_read_byte_canuseword(FC_ADDR,FC_OP2);
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP1,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ dyn_dop_byte_gencall(op);
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_RETOP,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ } else {
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP2,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP1,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ dyn_dop_byte_gencall(op);
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_RETOP,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ }
+}
+
+static void dyn_dop_gbeb_mov(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_read_byte(FC_ADDR,FC_TMP_BA1);
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_TMP_BA1,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ } else {
+ MOV_REG_BYTE_TO_HOST_REG_LOW(FC_TMP_BA1,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_TMP_BA1,decode.modrm.reg&3,(decode.modrm.reg>>2)&1);
+ }
+}
+
+static void dyn_dop_evgv(DualOps op) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_read_word(FC_ADDR,FC_OP1,decode.big_op);
+ gen_protect_addr_reg();
+ MOV_REG_WORD_TO_HOST_REG(FC_OP2,decode.modrm.reg,decode.big_op);
+ dyn_dop_word_gencall(op,decode.big_op);
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) {
+ gen_restore_addr_reg();
+ dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op);
+ }
+ } else {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,decode.modrm.rm,decode.big_op);
+ MOV_REG_WORD_TO_HOST_REG(FC_OP2,decode.modrm.reg,decode.big_op);
+ dyn_dop_word_gencall(op,decode.big_op);
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,decode.modrm.rm,decode.big_op);
+ }
+}
+
+static void dyn_dop_evgv_mov(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,decode.modrm.reg,decode.big_op);
+ dyn_write_word(FC_ADDR,FC_OP1,decode.big_op);
+ } else {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,decode.modrm.reg,decode.big_op);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,decode.modrm.rm,decode.big_op);
+ }
+}
+
+static void dyn_dop_eviv_mov(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP1,decode_fetchd());
+ else gen_mov_word_to_reg_imm(FC_OP1,decode_fetchw());
+ dyn_write_word(FC_ADDR,FC_OP1,decode.big_op);
+ } else {
+ if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP1,decode_fetchd());
+ else gen_mov_word_to_reg_imm(FC_OP1,decode_fetchw());
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,decode.modrm.rm,decode.big_op);
+ }
+}
+
+static void dyn_dop_evgv_xchg(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_read_word(FC_ADDR,FC_OP1,decode.big_op);
+ MOV_REG_WORD_TO_HOST_REG(FC_OP2,decode.modrm.reg,decode.big_op);
+
+ gen_protect_reg(FC_OP1);
+ gen_restore_addr_reg();
+ dyn_write_word(FC_ADDR,FC_OP2,decode.big_op);
+ gen_restore_reg(FC_OP1);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,decode.modrm.reg,decode.big_op);
+ } else {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,decode.modrm.rm,decode.big_op);
+ MOV_REG_WORD_TO_HOST_REG(FC_OP2,decode.modrm.reg,decode.big_op);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,decode.modrm.reg,decode.big_op);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP2,decode.modrm.rm,decode.big_op);
+ }
+}
+
+static void dyn_xchg_ax(Bit8u reg) {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,DRC_REG_EAX,decode.big_op);
+ MOV_REG_WORD_TO_HOST_REG(FC_OP2,reg,decode.big_op);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,reg,decode.big_op);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP2,DRC_REG_EAX,decode.big_op);
+}
+
+static void dyn_dop_gvev(DualOps op) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_read_word(FC_ADDR,FC_OP2,decode.big_op);
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,decode.modrm.reg,decode.big_op);
+ dyn_dop_word_gencall(op,decode.big_op);
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) {
+ gen_restore_addr_reg();
+ MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,decode.modrm.reg,decode.big_op);
+ }
+ } else {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP2,decode.modrm.rm,decode.big_op);
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,decode.modrm.reg,decode.big_op);
+ dyn_dop_word_gencall(op,decode.big_op);
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,decode.modrm.reg,decode.big_op);
+ }
+}
+
+static void dyn_dop_gvev_mov(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_read_word(FC_ADDR,FC_OP1,decode.big_op);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,decode.modrm.reg,decode.big_op);
+ } else {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,decode.modrm.rm,decode.big_op);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,decode.modrm.reg,decode.big_op);
+ }
+}
+
+static void dyn_dop_byte_imm(DualOps op,Bit8u reg,Bit8u idx) {
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP1,reg,idx);
+ gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,decode_fetchb());
+ dyn_dop_byte_gencall(op);
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_RETOP,reg,idx);
+}
+
+static void dyn_dop_byte_imm_mem(DualOps op,Bit8u reg,Bit8u idx) {
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP1,reg,idx);
+ Bitu val;
+ if (decode_fetchb_imm(val)) {
+ gen_mov_byte_to_reg_low_canuseword(FC_OP2,(void*)val);
+ } else {
+ gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,(Bit8u)val);
+ }
+ dyn_dop_byte_gencall(op);
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_RETOP,reg,idx);
+}
+
+static void dyn_prep_word_imm(Bit8u reg) {
+ Bitu val;
+ if (decode.big_op) {
+ if (decode_fetchd_imm(val)) {
+ gen_mov_word_to_reg(FC_OP2,(void*)val,true);
+ return;
+ }
+ } else {
+ if (decode_fetchw_imm(val)) {
+ gen_mov_word_to_reg(FC_OP2,(void*)val,false);
+ return;
+ }
+ }
+ if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,(Bit32u)val);
+ else gen_mov_word_to_reg_imm(FC_OP2,(Bit16u)val);
+}
+
+static void dyn_dop_word_imm(DualOps op,Bit8u reg) {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,reg,decode.big_op);
+ dyn_prep_word_imm(reg);
+ dyn_dop_word_gencall(op,decode.big_op);
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,reg,decode.big_op);
+}
+
+static void dyn_dop_word_imm_old(DualOps op,Bit8u reg,Bitu imm) {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,reg,decode.big_op);
+ if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,(Bit32u)imm);
+ else gen_mov_word_to_reg_imm(FC_OP2,(Bit16u)imm);
+ dyn_dop_word_gencall(op,decode.big_op);
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,reg,decode.big_op);
+}
+
+static void dyn_mov_byte_imm(Bit8u reg,Bit8u idx,Bit8u imm) {
+ gen_mov_byte_to_reg_low_imm(FC_TMP_BA1,imm);
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_TMP_BA1,reg,idx);
+}
+
+static void dyn_mov_word_imm(Bit8u reg) {
+ Bitu val;
+ if (decode.big_op) {
+ if (decode_fetchd_imm(val)) {
+ gen_mov_word_to_reg(FC_OP1,(void*)val,true);
+ MOV_REG_WORD32_FROM_HOST_REG(FC_OP1,reg);
+ return;
+ }
+ } else {
+ if (decode_fetchw_imm(val)) {
+ gen_mov_word_to_reg(FC_OP1,(void*)val,false);
+ MOV_REG_WORD16_FROM_HOST_REG(FC_OP1,reg);
+ return;
+ }
+ }
+ if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP1,(Bit32u)val);
+ else gen_mov_word_to_reg_imm(FC_OP1,(Bit16u)val);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,reg,decode.big_op);
+}
+
+
+static void dyn_sop_word(SingleOps op,Bit8u reg) {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,reg,decode.big_op);
+ dyn_sop_word_gencall(op,decode.big_op);
+ MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,reg,decode.big_op);
+}
+
+
+static void dyn_mov_byte_al_direct(Bitu imm) {
+ MOV_SEG_PHYS_TO_HOST_REG(FC_ADDR,(decode.seg_prefix_used ? decode.seg_prefix : DRC_SEG_DS));
+ gen_add_imm(FC_ADDR,imm);
+ dyn_read_byte(FC_ADDR,FC_TMP_BA1);
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_TMP_BA1,DRC_REG_EAX,0);
+}
+
+static void dyn_mov_byte_ax_direct(Bitu imm) {
+ MOV_SEG_PHYS_TO_HOST_REG(FC_ADDR,(decode.seg_prefix_used ? decode.seg_prefix : DRC_SEG_DS));
+ gen_add_imm(FC_ADDR,imm);
+ dyn_read_word(FC_ADDR,FC_OP1,decode.big_op);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,DRC_REG_EAX,decode.big_op);
+}
+
+static void dyn_mov_byte_direct_al() {
+ MOV_SEG_PHYS_TO_HOST_REG(FC_ADDR,(decode.seg_prefix_used ? decode.seg_prefix : DRC_SEG_DS));
+ if (decode.big_addr) {
+ Bitu val;
+ if (decode_fetchd_imm(val)) {
+ gen_add(FC_ADDR,(void*)val);
+ } else {
+ gen_add_imm(FC_ADDR,(Bit32u)val);
+ }
+ } else {
+ gen_add_imm(FC_ADDR,decode_fetchw());
+ }
+ MOV_REG_BYTE_TO_HOST_REG_LOW(FC_TMP_BA1,DRC_REG_EAX,0);
+ dyn_write_byte(FC_ADDR,FC_TMP_BA1);
+}
+
+static void dyn_mov_byte_direct_ax(Bitu imm) {
+ MOV_SEG_PHYS_TO_HOST_REG(FC_ADDR,(decode.seg_prefix_used ? decode.seg_prefix : DRC_SEG_DS));
+ gen_add_imm(FC_ADDR,imm);
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,DRC_REG_EAX,decode.big_op);
+ dyn_write_word(FC_ADDR,FC_OP1,decode.big_op);
+}
+
+
+static void dyn_movx_ev_gb(bool sign) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_read_byte(FC_ADDR,FC_TMP_BA1);
+ gen_extend_byte(sign,FC_TMP_BA1);
+ MOV_REG_WORD_FROM_HOST_REG(FC_TMP_BA1,decode.modrm.reg,decode.big_op);
+ } else {
+ MOV_REG_BYTE_TO_HOST_REG_LOW(FC_TMP_BA1,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ gen_extend_byte(sign,FC_TMP_BA1);
+ MOV_REG_WORD_FROM_HOST_REG(FC_TMP_BA1,decode.modrm.reg,decode.big_op);
+ }
+}
+
+static void dyn_movx_ev_gw(bool sign) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_read_word(FC_ADDR,FC_OP1,false);
+ gen_extend_word(sign,FC_OP1);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,decode.modrm.reg,decode.big_op);
+ } else {
+ MOV_REG_WORD16_TO_HOST_REG(FC_OP1,decode.modrm.rm);
+ gen_extend_word(sign,FC_OP1);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,decode.modrm.reg,decode.big_op);
+ }
+}
+
+
+static void dyn_mov_ev_seg(void) {
+ dyn_get_modrm();
+ MOV_SEG_VAL_TO_HOST_REG(FC_OP1,decode.modrm.reg);
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_write_word(FC_ADDR,FC_OP1,false);
+ } else {
+ if (decode.big_op) gen_extend_word(false,FC_OP1);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,decode.modrm.rm,decode.big_op);
+ }
+}
+
+
+static void dyn_lea(void) {
+ dyn_get_modrm();
+ dyn_fill_ea(FC_ADDR,false);
+ MOV_REG_WORD_FROM_HOST_REG(FC_ADDR,decode.modrm.reg,decode.big_op);
+}
+
+
+static void dyn_push_seg(Bit8u seg) {
+ MOV_SEG_VAL_TO_HOST_REG(FC_OP1,seg);
+ if (decode.big_op) {
+ gen_extend_word(false,FC_OP1);
+ gen_call_function_raw((void*)&dynrec_push_dword);
+ } else {
+ gen_call_function_raw((void*)&dynrec_push_word);
+ }
+}
+
+static void dyn_pop_seg(Bit8u seg) {
+ gen_call_function_II((void *)&CPU_PopSeg,seg,decode.big_op);
+ dyn_check_exception(FC_RETOP);
+}
+
+static void dyn_push_reg(Bit8u reg) {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,reg,decode.big_op);
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_push_dword);
+ else gen_call_function_raw((void*)&dynrec_push_word);
+}
+
+static void dyn_pop_reg(Bit8u reg) {
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_pop_dword);
+ else gen_call_function_raw((void*)&dynrec_pop_word);
+ MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,reg,decode.big_op);
+}
+
+static void dyn_push_byte_imm(Bit8s imm) {
+ gen_mov_dword_to_reg_imm(FC_OP1,(Bit32u)imm);
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_push_dword);
+ else gen_call_function_raw((void*)&dynrec_push_word);
+}
+
+static void dyn_push_word_imm(Bitu imm) {
+ if (decode.big_op) {
+ gen_mov_dword_to_reg_imm(FC_OP1,imm);
+ gen_call_function_raw((void*)&dynrec_push_dword);
+ } else {
+ gen_mov_word_to_reg_imm(FC_OP1,(Bit16u)imm);
+ gen_call_function_raw((void*)&dynrec_push_word);
+ }
+}
+
+static void dyn_pop_ev(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ // save original ESP
+ MOV_REG_WORD32_TO_HOST_REG(FC_OP2,DRC_REG_ESP);
+ gen_protect_reg(FC_OP2);
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_pop_dword);
+ else gen_call_function_raw((void*)&dynrec_pop_word);
+ dyn_fill_ea(FC_ADDR);
+ gen_mov_regs(FC_OP2,FC_RETOP);
+ gen_mov_regs(FC_OP1,FC_ADDR);
+ if (decode.big_op) gen_call_function_raw((void *)&mem_writed_checked_drc);
+ else gen_call_function_raw((void *)&mem_writew_checked_drc);
+ gen_extend_byte(false,FC_RETOP); // bool -> dword
+ DRC_PTR_SIZE_IM no_fault = gen_create_branch_on_zero(FC_RETOP, true);
+ // restore original ESP
+ gen_restore_reg(FC_OP2);
+ MOV_REG_WORD32_FROM_HOST_REG(FC_OP2,DRC_REG_ESP);
+ dyn_check_exception(FC_RETOP);
+ gen_fill_branch(no_fault);
+ } else {
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_pop_dword);
+ else gen_call_function_raw((void*)&dynrec_pop_word);
+ MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,decode.modrm.rm,decode.big_op);
+ }
+}
+
+
+static void dyn_segprefix(Bit8u seg) {
+// if (GCC_UNLIKELY(decode.seg_prefix_used)) IllegalOptionDynrec("dyn_segprefix");
+ decode.seg_prefix=seg;
+ decode.seg_prefix_used=true;
+}
+
+ static void dyn_mov_seg_ev(void) {
+ dyn_get_modrm();
+ if (GCC_UNLIKELY(decode.modrm.reg==DRC_SEG_CS)) IllegalOptionDynrec("dyn_mov_seg_ev");
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_read_word(FC_ADDR,FC_RETOP,false);
+ } else {
+ MOV_REG_WORD16_TO_HOST_REG(FC_RETOP,decode.modrm.rm);
+ }
+ gen_call_function_IR((void *)&CPU_SetSegGeneral,decode.modrm.reg,FC_RETOP);
+ dyn_check_exception(FC_RETOP);
+}
+
+static void dyn_load_seg_off_ea(Bit8u seg) {
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_read_word(FC_ADDR,FC_OP1,decode.big_op);
+ gen_protect_reg(FC_OP1);
+
+ gen_restore_addr_reg();
+ gen_add_imm(FC_ADDR,decode.big_op ? 4:2);
+ dyn_read_word(FC_ADDR,FC_RETOP,false);
+
+ gen_call_function_IR((void *)&CPU_SetSegGeneral,seg,FC_RETOP);
+ dyn_check_exception(FC_RETOP);
+
+ gen_restore_reg(FC_OP1);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,decode.modrm.reg,decode.big_op);
+ } else {
+ IllegalOptionDynrec("dyn_load_seg_off_ea");
+ }
+}
+
+
+
+static void dyn_imul_gvev(Bitu immsize) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_read_word(FC_ADDR,FC_OP1,decode.big_op);
+ } else {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,decode.modrm.rm,decode.big_op);
+ }
+
+ switch (immsize) {
+ case 0:
+ MOV_REG_WORD_TO_HOST_REG(FC_OP2,decode.modrm.reg,decode.big_op);
+ break;
+ case 1:
+ if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,(Bit8s)decode_fetchb());
+ else gen_mov_word_to_reg_imm(FC_OP2,(Bit8s)decode_fetchb());
+ break;
+ case 2:
+ if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,(Bit16s)decode_fetchw());
+ else gen_mov_word_to_reg_imm(FC_OP2,(Bit16s)decode_fetchw());
+ break;
+ case 4:
+ if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,(Bit32s)decode_fetchd());
+ else gen_mov_word_to_reg_imm(FC_OP2,(Bit16u)((Bit32s)decode_fetchd()));
+ break;
+ }
+
+ if (decode.big_op) gen_call_function_raw((void*)dynrec_dimul_dword);
+ else gen_call_function_raw((void*)dynrec_dimul_word);
+
+ MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,decode.modrm.reg,decode.big_op);
+}
+
+static void dyn_dshift_ev_gv(bool left,bool immediate) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_read_word(FC_ADDR,FC_OP1,decode.big_op);
+ } else {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,decode.modrm.rm,decode.big_op);
+ }
+ MOV_REG_WORD_TO_HOST_REG(FC_OP2,decode.modrm.reg,decode.big_op);
+ if (immediate) gen_mov_byte_to_reg_low_imm(FC_OP3,decode_fetchb());
+ else MOV_REG_BYTE_TO_HOST_REG_LOW(FC_OP3,DRC_REG_ECX,0);
+ if (decode.big_op) dyn_dpshift_dword_gencall(left);
+ else dyn_dpshift_word_gencall(left);
+
+ if (decode.modrm.mod<3) {
+ gen_restore_addr_reg();
+ dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op);
+ } else {
+ MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,decode.modrm.rm,decode.big_op);
+ }
+}
+
+
+static void dyn_grp1_eb_ib(void) {
+ dyn_get_modrm();
+ DualOps op=grp1_table[decode.modrm.reg];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_read_byte_canuseword(FC_ADDR,FC_OP1);
+ gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,decode_fetchb());
+ dyn_dop_byte_gencall(op);
+
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) {
+ gen_restore_addr_reg();
+ dyn_write_byte(FC_ADDR,FC_RETOP);
+ }
+ } else {
+ dyn_dop_byte_imm_mem(op,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ }
+}
+
+static void dyn_grp1_ev_iv(bool withbyte) {
+ dyn_get_modrm();
+ DualOps op=grp1_table[decode.modrm.reg];
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_read_word(FC_ADDR,FC_OP1,decode.big_op);
+
+ if (!withbyte) {
+ dyn_prep_word_imm(FC_OP2);
+ } else {
+ Bits imm=(Bit8s)decode_fetchb();
+ if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,(Bit32u)imm);
+ else gen_mov_word_to_reg_imm(FC_OP2,(Bit16u)imm);
+ }
+
+ dyn_dop_word_gencall(op,decode.big_op);
+
+ if ((op!=DOP_CMP) && (op!=DOP_TEST)) {
+ gen_restore_addr_reg();
+ dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op);
+ }
+ } else {
+ if (!withbyte) {
+ dyn_dop_word_imm(op,decode.modrm.rm);
+ } else {
+ Bits imm=withbyte ? (Bit8s)decode_fetchb() : (decode.big_op ? decode_fetchd(): decode_fetchw());
+ dyn_dop_word_imm_old(op,decode.modrm.rm,imm);
+ }
+ }
+}
+
+
+static void dyn_grp2_eb(grp2_types type) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_read_byte_canuseword(FC_ADDR,FC_OP1);
+ } else {
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP1,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ }
+ switch (type) {
+ case grp2_1:
+ gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,1);
+ dyn_shift_byte_gencall((ShiftOps)decode.modrm.reg);
+ break;
+ case grp2_imm: {
+ Bit8u imm=decode_fetchb();
+ if (imm) {
+ gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,imm&0x1f);
+ dyn_shift_byte_gencall((ShiftOps)decode.modrm.reg);
+ } else return;
+ }
+ break;
+ case grp2_cl:
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP2,DRC_REG_ECX,0);
+ gen_and_imm(FC_OP2,0x1f);
+ dyn_shift_byte_gencall((ShiftOps)decode.modrm.reg);
+ break;
+ }
+ if (decode.modrm.mod<3) {
+ gen_restore_addr_reg();
+ dyn_write_byte(FC_ADDR,FC_RETOP);
+ } else {
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_RETOP,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ }
+}
+
+static void dyn_grp2_ev(grp2_types type) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_read_word(FC_ADDR,FC_OP1,decode.big_op);
+ } else {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,decode.modrm.rm,decode.big_op);
+ }
+ switch (type) {
+ case grp2_1:
+ gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,1);
+ dyn_shift_word_gencall((ShiftOps)decode.modrm.reg,decode.big_op);
+ break;
+ case grp2_imm: {
+ Bitu val;
+ if (decode_fetchb_imm(val)) {
+ gen_mov_byte_to_reg_low_canuseword(FC_OP2,(void*)val);
+ gen_and_imm(FC_OP2,0x1f);
+ dyn_shift_word_gencall((ShiftOps)decode.modrm.reg,decode.big_op);
+ break;
+ }
+ Bit8u imm=(Bit8u)val;
+ if (imm) {
+ gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,imm&0x1f);
+ dyn_shift_word_gencall((ShiftOps)decode.modrm.reg,decode.big_op);
+ } else return;
+ }
+ break;
+ case grp2_cl:
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP2,DRC_REG_ECX,0);
+ gen_and_imm(FC_OP2,0x1f);
+ dyn_shift_word_gencall((ShiftOps)decode.modrm.reg,decode.big_op);
+ break;
+ }
+ if (decode.modrm.mod<3) {
+ gen_restore_addr_reg();
+ dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op);
+ } else {
+ MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,decode.modrm.rm,decode.big_op);
+ }
+}
+
+
+static void dyn_grp3_eb(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ if ((decode.modrm.reg==2) || (decode.modrm.reg==3)) gen_protect_addr_reg();
+ dyn_read_byte_canuseword(FC_ADDR,FC_OP1);
+ } else {
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP1,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ }
+ switch (decode.modrm.reg) {
+ case 0x0: // test eb,ib
+ gen_mov_byte_to_reg_low_imm_canuseword(FC_OP2,decode_fetchb());
+ dyn_dop_byte_gencall(DOP_TEST);
+ return;
+ case 0x2: // NOT Eb
+ dyn_sop_byte_gencall(SOP_NOT);
+ break;
+ case 0x3: // NEG Eb
+ dyn_sop_byte_gencall(SOP_NEG);
+ break;
+ case 0x4: // mul Eb
+ gen_call_function_raw((void*)&dynrec_mul_byte);
+ return;
+ case 0x5: // imul Eb
+ gen_call_function_raw((void*)&dynrec_imul_byte);
+ return;
+ case 0x6: // div Eb
+ gen_call_function_raw((void*)&dynrec_div_byte);
+ dyn_check_exception(FC_RETOP);
+ return;
+ case 0x7: // idiv Eb
+ gen_call_function_raw((void*)&dynrec_idiv_byte);
+ dyn_check_exception(FC_RETOP);
+ return;
+ }
+ // Save the result if memory op
+ if (decode.modrm.mod<3) {
+ gen_restore_addr_reg();
+ dyn_write_byte(FC_ADDR,FC_RETOP);
+ } else {
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_RETOP,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ }
+}
+
+static void dyn_grp3_ev(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ if ((decode.modrm.reg==2) || (decode.modrm.reg==3)) gen_protect_addr_reg();
+ dyn_read_word(FC_ADDR,FC_OP1,decode.big_op);
+ } else {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,decode.modrm.rm,decode.big_op);
+ }
+ switch (decode.modrm.reg) {
+ case 0x0: // test ev,iv
+ if (decode.big_op) gen_mov_dword_to_reg_imm(FC_OP2,decode_fetchd());
+ else gen_mov_word_to_reg_imm(FC_OP2,decode_fetchw());
+ dyn_dop_word_gencall(DOP_TEST,decode.big_op);
+ return;
+ case 0x2: // NOT Ev
+ dyn_sop_word_gencall(SOP_NOT,decode.big_op);
+ break;
+ case 0x3: // NEG Eb
+ dyn_sop_word_gencall(SOP_NEG,decode.big_op);
+ break;
+ case 0x4: // mul Eb
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_mul_dword);
+ else gen_call_function_raw((void*)&dynrec_mul_word);
+ return;
+ case 0x5: // imul Eb
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_imul_dword);
+ else gen_call_function_raw((void*)&dynrec_imul_word);
+ return;
+ case 0x6: // div Eb
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_div_dword);
+ else gen_call_function_raw((void*)&dynrec_div_word);
+ dyn_check_exception(FC_RETOP);
+ return;
+ case 0x7: // idiv Eb
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_idiv_dword);
+ else gen_call_function_raw((void*)&dynrec_idiv_word);
+ dyn_check_exception(FC_RETOP);
+ return;
+ }
+ // Save the result if memory op
+ if (decode.modrm.mod<3) {
+ gen_restore_addr_reg();
+ dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op);
+ } else {
+ MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,decode.modrm.rm,decode.big_op);
+ }
+}
+
+
+static bool dyn_grp4_eb(void) {
+ dyn_get_modrm();
+ switch (decode.modrm.reg) {
+ case 0x0://INC Eb
+ case 0x1://DEC Eb
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_read_byte_canuseword(FC_ADDR,FC_OP1);
+ dyn_sop_byte_gencall(decode.modrm.reg==0 ? SOP_INC : SOP_DEC);
+ gen_restore_addr_reg();
+ dyn_write_byte(FC_ADDR,FC_RETOP);
+ } else {
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP1,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ dyn_sop_byte_gencall(decode.modrm.reg==0 ? SOP_INC : SOP_DEC);
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_RETOP,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ }
+ break;
+ case 0x7: //CALBACK Iw
+ gen_mov_direct_dword(&core_dynrec.callback,decode_fetchw());
+ dyn_set_eip_end();
+ dyn_reduce_cycles();
+ dyn_return(BR_CallBack);
+ dyn_closeblock();
+ return true;
+ default:
+ IllegalOptionDynrec("dyn_grp4_eb");
+ break;
+ }
+ return false;
+}
+
+static Bitu dyn_grp4_ev(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ if ((decode.modrm.reg<2) || (decode.modrm.reg==3) || (decode.modrm.reg==5)) gen_protect_addr_reg();
+ dyn_read_word(FC_ADDR,FC_OP1,decode.big_op);
+ } else {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,decode.modrm.rm,decode.big_op);
+ }
+ switch (decode.modrm.reg) {
+ case 0x0://INC Ev
+ case 0x1://DEC Ev
+ dyn_sop_word_gencall(decode.modrm.reg==0 ? SOP_INC : SOP_DEC,decode.big_op);
+ if (decode.modrm.mod<3) {
+ gen_restore_addr_reg();
+ dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op);
+ } else {
+ MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,decode.modrm.rm,decode.big_op);
+ }
+ break;
+ case 0x2: // CALL Ev
+ gen_mov_regs(FC_ADDR,FC_OP1);
+ gen_protect_addr_reg();
+ gen_mov_word_to_reg(FC_OP1,decode.big_op?(void*)(&reg_eip):(void*)(&reg_ip),decode.big_op);
+ gen_add_imm(FC_OP1,(Bit32u)(decode.code-decode.code_start));
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_push_dword);
+ else gen_call_function_raw((void*)&dynrec_push_word);
+
+ gen_restore_addr_reg();
+ gen_mov_word_from_reg(FC_ADDR,decode.big_op?(void*)(&reg_eip):(void*)(&reg_ip),decode.big_op);
+ return 1;
+ case 0x4: // JMP Ev
+ gen_mov_word_from_reg(FC_OP1,decode.big_op?(void*)(&reg_eip):(void*)(&reg_ip),decode.big_op);
+ return 1;
+ case 0x3: // CALL Ep
+ case 0x5: // JMP Ep
+ if (!decode.big_op) gen_extend_word(false,FC_OP1);
+ if (decode.modrm.mod<3) gen_restore_addr_reg();
+ gen_protect_reg(FC_OP1);
+ gen_add_imm(FC_ADDR,decode.big_op?4:2);
+ dyn_read_word(FC_ADDR,FC_OP2,decode.big_op);
+ gen_extend_word(false,FC_OP2);
+
+ dyn_set_eip_last_end(FC_RETOP);
+ gen_restore_reg(FC_OP1,FC_ADDR);
+ gen_call_function_IRRR(decode.modrm.reg == 3 ? (void*)(&CPU_CALL) : (void*)(&CPU_JMP),
+ decode.big_op,FC_OP2,FC_ADDR,FC_RETOP);
+ return 1;
+ case 0x6: // PUSH Ev
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_push_dword);
+ else gen_call_function_raw((void*)&dynrec_push_word);
+ break;
+ default:
+// IllegalOptionDynrec("dyn_grp4_ev");
+ return 2;
+ }
+ return 0;
+}
+
+
+static bool dyn_grp6(void) {
+ dyn_get_modrm();
+ switch (decode.modrm.reg) {
+ case 0x00: // SLDT
+ case 0x01: // STR
+ if (decode.modrm.reg==0) gen_call_function_raw((void*)CPU_SLDT);
+ else gen_call_function_raw((void*)CPU_STR);
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_write_word(FC_ADDR,FC_RETOP,false);
+ } else {
+ MOV_REG_WORD16_FROM_HOST_REG(FC_RETOP,decode.modrm.rm);
+ }
+ break;
+ case 0x02: // LLDT
+ case 0x03: // LTR
+ case 0x04: // VERR
+ case 0x05: // VERW
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_read_word(FC_ADDR,FC_RETOP,false);
+ } else {
+ MOV_REG_WORD16_TO_HOST_REG(FC_RETOP,decode.modrm.rm);
+ }
+ gen_extend_word(false,FC_RETOP);
+ switch (decode.modrm.reg) {
+ case 0x02: // LLDT
+// if (cpu.cpl) return CPU_PrepareException(EXCEPTION_GP,0);
+ if (cpu.cpl) E_Exit("lldt cpl>0");
+ gen_call_function_R((void*)CPU_LLDT,FC_RETOP);
+ dyn_check_exception(FC_RETOP);
+ break;
+ case 0x03: // LTR
+// if (cpu.cpl) return CPU_PrepareException(EXCEPTION_GP,0);
+ if (cpu.cpl) E_Exit("ltr cpl>0");
+ gen_call_function_R((void*)CPU_LTR,FC_RETOP);
+ dyn_check_exception(FC_RETOP);
+ break;
+ case 0x04: // VERR
+ gen_call_function_R((void*)CPU_VERR,FC_RETOP);
+ break;
+ case 0x05: // VERW
+ gen_call_function_R((void*)CPU_VERW,FC_RETOP);
+ break;
+ }
+ break;
+ default: IllegalOptionDynrec("dyn_grp6");
+ }
+ return false;
+}
+
+static bool dyn_grp7(void) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ switch (decode.modrm.reg) {
+ case 0x00: // SGDT
+ gen_call_function_raw((void*)CPU_SGDT_limit);
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_write_word(FC_ADDR,FC_RETOP,false);
+ gen_call_function_raw((void*)CPU_SGDT_base);
+ gen_restore_addr_reg();
+ gen_add_imm(FC_ADDR,2);
+ dyn_write_word(FC_ADDR,FC_RETOP,true);
+ break;
+ case 0x01: // SIDT
+ gen_call_function_raw((void*)CPU_SIDT_limit);
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_write_word(FC_ADDR,FC_RETOP,false);
+ gen_call_function_raw((void*)CPU_SIDT_base);
+ gen_restore_addr_reg();
+ gen_add_imm(FC_ADDR,2);
+ dyn_write_word(FC_ADDR,FC_RETOP,true);
+ break;
+ case 0x02: // LGDT
+ case 0x03: // LIDT
+// if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ if (cpu.pmode && cpu.cpl) IllegalOptionDynrec("lgdt nonpriviledged");
+ dyn_fill_ea(FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_read_word(FC_ADDR,FC_OP1,false);
+ gen_extend_word(false,FC_OP1);
+ gen_protect_reg(FC_OP1);
+
+ gen_restore_addr_reg();
+ gen_add_imm(FC_ADDR,2);
+ dyn_read_word(FC_ADDR,FC_OP2,true);
+ if (!decode.big_op) gen_and_imm(FC_OP2,0xffffff);
+
+ gen_restore_reg(FC_OP1);
+ if (decode.modrm.reg==2) gen_call_function_RR((void*)CPU_LGDT,FC_OP1,FC_OP2);
+ else gen_call_function_RR((void*)CPU_LIDT,FC_OP1,FC_OP2);
+ break;
+ case 0x04: // SMSW
+ gen_call_function_raw((void*)CPU_SMSW);
+ dyn_fill_ea(FC_ADDR);
+ dyn_write_word(FC_ADDR,FC_RETOP,false);
+ break;
+ case 0x06: // LMSW
+ dyn_fill_ea(FC_ADDR);
+ dyn_read_word(FC_ADDR,FC_RETOP,false);
+ gen_call_function_R((void*)CPU_LMSW,FC_RETOP);
+ dyn_check_exception(FC_RETOP);
+ dyn_set_eip_end();
+ dyn_reduce_cycles();
+ dyn_return(BR_Normal);
+ dyn_closeblock();
+ return true;
+ case 0x07: // INVLPG
+// if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ if (cpu.pmode && cpu.cpl) IllegalOptionDynrec("invlpg nonpriviledged");
+ gen_call_function_raw((void*)PAGING_ClearTLB);
+ break;
+ default: IllegalOptionDynrec("dyn_grp7_1");
+ }
+ } else {
+ switch (decode.modrm.reg) {
+ case 0x04: // SMSW
+ gen_call_function_raw((void*)CPU_SMSW);
+ MOV_REG_WORD16_FROM_HOST_REG(FC_RETOP,decode.modrm.rm);
+ break;
+ case 0x06: // LMSW
+ MOV_REG_WORD16_TO_HOST_REG(FC_RETOP,decode.modrm.rm);
+ gen_call_function_R((void*)CPU_LMSW,FC_RETOP);
+ dyn_check_exception(FC_RETOP);
+ dyn_set_eip_end();
+ dyn_reduce_cycles();
+ dyn_return(BR_Normal);
+ dyn_closeblock();
+ return true;
+ default: IllegalOptionDynrec("dyn_grp7_2");
+ }
+ }
+ return false;
+}
+
+
+/*
+static void dyn_larlsl(bool is_lar) {
+ dyn_get_modrm();
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_read_word(FC_ADDR,FC_RETOP,false);
+ } else {
+ MOV_REG_WORD16_TO_HOST_REG(FC_RETOP,decode.modrm.rm);
+ }
+ gen_extend_word(false,FC_RETOP);
+ if (is_lar) gen_call_function((void*)CPU_LAR,"%R%A",FC_RETOP,(DRC_PTR_SIZE_IM)&core_dynrec.readdata);
+ else gen_call_function((void*)CPU_LSL,"%R%A",FC_RETOP,(DRC_PTR_SIZE_IM)&core_dynrec.readdata);
+ DRC_PTR_SIZE_IM brnz=gen_create_branch_on_nonzero(FC_RETOP,true);
+ gen_mov_word_to_reg(FC_OP2,&core_dynrec.readdata,true);
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP2,decode.modrm.reg,decode.big_op);
+ gen_fill_branch(brnz);
+}
+*/
+
+
+static void dyn_mov_from_crx(void) {
+ dyn_get_modrm();
+ gen_call_function_IA((void*)CPU_READ_CRX,decode.modrm.reg,(DRC_PTR_SIZE_IM)&core_dynrec.readdata);
+ dyn_check_exception(FC_RETOP);
+ gen_mov_word_to_reg(FC_OP2,&core_dynrec.readdata,true);
+ MOV_REG_WORD32_FROM_HOST_REG(FC_OP2,decode.modrm.rm);
+}
+
+static void dyn_mov_to_crx(void) {
+ dyn_get_modrm();
+ MOV_REG_WORD32_TO_HOST_REG(FC_RETOP,decode.modrm.rm);
+ gen_call_function_IR((void*)CPU_WRITE_CRX,decode.modrm.reg,FC_RETOP);
+ dyn_check_exception(FC_RETOP);
+ dyn_set_eip_end();
+ dyn_reduce_cycles();
+ dyn_return(BR_Normal);
+ dyn_closeblock();
+}
+
+
+static void dyn_cbw(void) {
+ if (decode.big_op) {
+ MOV_REG_WORD16_TO_HOST_REG(FC_OP1,DRC_REG_EAX);
+ gen_call_function_raw((void *)&dynrec_cwde);
+ MOV_REG_WORD32_FROM_HOST_REG(FC_RETOP,DRC_REG_EAX);
+ } else {
+ MOV_REG_BYTE_TO_HOST_REG_LOW_CANUSEWORD(FC_OP1,DRC_REG_EAX,0);
+ gen_call_function_raw((void *)&dynrec_cbw);
+ MOV_REG_WORD16_FROM_HOST_REG(FC_RETOP,DRC_REG_EAX);
+ }
+}
+
+static void dyn_cwd(void) {
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,DRC_REG_EAX,decode.big_op);
+ if (decode.big_op) {
+ gen_call_function_raw((void *)&dynrec_cdq);
+ MOV_REG_WORD32_FROM_HOST_REG(FC_RETOP,DRC_REG_EDX);
+ } else {
+ gen_call_function_raw((void *)&dynrec_cwd);
+ MOV_REG_WORD16_FROM_HOST_REG(FC_RETOP,DRC_REG_EDX);
+ }
+}
+
+static void dyn_sahf(void) {
+ MOV_REG_WORD16_TO_HOST_REG(FC_OP1,DRC_REG_EAX);
+ gen_call_function_raw((void *)&dynrec_sahf);
+ InvalidateFlags();
+}
+
+
+static void dyn_exit_link(Bits eip_change) {
+ gen_add_direct_word(&reg_eip,(decode.code-decode.code_start)+eip_change,decode.big_op);
+ dyn_reduce_cycles();
+ gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlockDynRec,cache.start));
+ dyn_closeblock();
+}
+
+
+static void dyn_branched_exit(BranchTypes btype,Bit32s eip_add) {
+ Bitu eip_base=decode.code-decode.code_start;
+ dyn_reduce_cycles();
+
+ dyn_branchflag_to_reg(btype);
+ DRC_PTR_SIZE_IM data=gen_create_branch_on_nonzero(FC_RETOP,true);
+
+ // Branch not taken
+ gen_add_direct_word(&reg_eip,eip_base,decode.big_op);
+ gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlockDynRec,cache.start));
+ gen_fill_branch(data);
+
+ // Branch taken
+ gen_add_direct_word(&reg_eip,eip_base+eip_add,decode.big_op);
+ gen_jmp_ptr(&decode.block->link[1].to,offsetof(CacheBlockDynRec,cache.start));
+ dyn_closeblock();
+}
+
+/*
+static void dyn_set_byte_on_condition(BranchTypes btype) {
+ dyn_get_modrm();
+ dyn_branchflag_to_reg(btype);
+ gen_and_imm(FC_RETOP,1);
+ if (decode.modrm.mod<3) {
+ dyn_fill_ea(FC_ADDR);
+ dyn_write_byte(FC_ADDR,FC_RETOP);
+ } else {
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_RETOP,decode.modrm.rm&3,(decode.modrm.rm>>2)&1);
+ }
+}
+*/
+
+static void dyn_loop(LoopTypes type) {
+ dyn_reduce_cycles();
+ Bits eip_add=(Bit8s)decode_fetchb();
+ Bitu eip_base=decode.code-decode.code_start;
+ DRC_PTR_SIZE_IM branch1=0;
+ DRC_PTR_SIZE_IM branch2=0;
+ switch (type) {
+ case LOOP_E:
+ dyn_branchflag_to_reg(BR_NZ);
+ branch1=gen_create_branch_on_nonzero(FC_RETOP,true);
+ break;
+ case LOOP_NE:
+ dyn_branchflag_to_reg(BR_Z);
+ branch1=gen_create_branch_on_nonzero(FC_RETOP,true);
+ break;
+ }
+ switch (type) {
+ case LOOP_E:
+ case LOOP_NE:
+ case LOOP_NONE:
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,DRC_REG_ECX,decode.big_addr);
+ gen_add_imm(FC_OP1,(Bit32u)(-1));
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,DRC_REG_ECX,decode.big_addr);
+ branch2=gen_create_branch_on_zero(FC_OP1,decode.big_addr);
+ break;
+ case LOOP_JCXZ:
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,DRC_REG_ECX,decode.big_addr);
+ branch2=gen_create_branch_on_nonzero(FC_OP1,decode.big_addr);
+ break;
+ }
+ gen_add_direct_word(&reg_eip,eip_base+eip_add,true);
+ gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlockDynRec,cache.start));
+ if (branch1) {
+ gen_fill_branch(branch1);
+ MOV_REG_WORD_TO_HOST_REG(FC_OP1,DRC_REG_ECX,decode.big_addr);
+ gen_add_imm(FC_OP1,(Bit32u)(-1));
+ MOV_REG_WORD_FROM_HOST_REG(FC_OP1,DRC_REG_ECX,decode.big_addr);
+ }
+ // Branch taken
+ gen_fill_branch(branch2);
+ gen_add_direct_word(&reg_eip,eip_base,decode.big_op);
+ gen_jmp_ptr(&decode.block->link[1].to,offsetof(CacheBlockDynRec,cache.start));
+ dyn_closeblock();
+}
+
+
+static void dyn_ret_near(Bitu bytes) {
+ dyn_reduce_cycles();
+
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_pop_dword);
+ else {
+ gen_call_function_raw((void*)&dynrec_pop_word);
+ gen_extend_word(false,FC_RETOP);
+ }
+ gen_mov_word_from_reg(FC_RETOP,decode.big_op?(void*)(&reg_eip):(void*)(&reg_ip),true);
+
+ if (bytes) gen_add_direct_word(&reg_esp,bytes,true);
+ dyn_return(BR_Normal);
+ dyn_closeblock();
+}
+
+static void dyn_call_near_imm(void) {
+ Bits imm;
+ if (decode.big_op) imm=(Bit32s)decode_fetchd();
+ else imm=(Bit16s)decode_fetchw();
+ dyn_set_eip_end(FC_OP1);
+ if (decode.big_op) gen_call_function_raw((void*)&dynrec_push_dword);
+ else gen_call_function_raw((void*)&dynrec_push_word);
+
+ dyn_set_eip_end(FC_OP1,imm);
+ gen_mov_word_from_reg(FC_OP1,decode.big_op?(void*)(&reg_eip):(void*)(&reg_ip),decode.big_op);
+
+ dyn_reduce_cycles();
+ gen_jmp_ptr(&decode.block->link[0].to,offsetof(CacheBlockDynRec,cache.start));
+ dyn_closeblock();
+}
+
+static void dyn_ret_far(Bitu bytes) {
+ dyn_reduce_cycles();
+ dyn_set_eip_last_end(FC_RETOP);
+ gen_call_function_IIR((void*)&CPU_RET,decode.big_op,bytes,FC_RETOP);
+ dyn_return(BR_Normal);
+ dyn_closeblock();
+}
+
+static void dyn_call_far_imm(void) {
+ Bitu sel,off;
+ off=decode.big_op ? decode_fetchd() : decode_fetchw();
+ sel=decode_fetchw();
+ dyn_reduce_cycles();
+ dyn_set_eip_last_end(FC_RETOP);
+ gen_call_function_IIIR((void*)&CPU_CALL,decode.big_op,sel,off,FC_RETOP);
+ dyn_return(BR_Normal);
+ dyn_closeblock();
+}
+
+static void dyn_jmp_far_imm(void) {
+ Bitu sel,off;
+ off=decode.big_op ? decode_fetchd() : decode_fetchw();
+ sel=decode_fetchw();
+ dyn_reduce_cycles();
+ dyn_set_eip_last_end(FC_RETOP);
+ gen_call_function_IIIR((void*)&CPU_JMP,decode.big_op,sel,off,FC_RETOP);
+ dyn_return(BR_Normal);
+ dyn_closeblock();
+}
+
+static void dyn_iret(void) {
+ dyn_reduce_cycles();
+ dyn_set_eip_last_end(FC_RETOP);
+ gen_call_function_IR((void*)&CPU_IRET,decode.big_op,FC_RETOP);
+ dyn_return(BR_Iret);
+ dyn_closeblock();
+}
+
+static void dyn_interrupt(Bit8u num) {
+ dyn_reduce_cycles();
+ dyn_set_eip_last_end(FC_RETOP);
+ gen_call_function_IIR((void*)&CPU_Interrupt,num,CPU_INT_SOFTWARE,FC_RETOP);
+ dyn_return(BR_Normal);
+ dyn_closeblock();
+}
+
+
+
+static void dyn_string(StringOps op) {
+ if (decode.rep) MOV_REG_WORD_TO_HOST_REG(FC_OP1,DRC_REG_ECX,decode.big_addr);
+ else gen_mov_dword_to_reg_imm(FC_OP1,1);
+ gen_mov_word_to_reg(FC_OP2,&cpu.direction,true);
+ Bit8u di_base_addr=decode.seg_prefix_used ? decode.seg_prefix : DRC_SEG_DS;
+ switch (op) {
+ case STR_MOVSB:
+ if (decode.big_addr) gen_call_function_mm((void*)&dynrec_movsb_dword,(Bitu)DRCD_SEG_PHYS(di_base_addr),(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES));
+ else gen_call_function_mm((void*)&dynrec_movsb_word,(Bitu)DRCD_SEG_PHYS(di_base_addr),(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES));
+ break;
+ case STR_MOVSW:
+ if (decode.big_addr) gen_call_function_mm((void*)&dynrec_movsw_dword,(Bitu)DRCD_SEG_PHYS(di_base_addr),(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES));
+ else gen_call_function_mm((void*)&dynrec_movsw_word,(Bitu)DRCD_SEG_PHYS(di_base_addr),(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES));
+ break;
+ case STR_MOVSD:
+ if (decode.big_addr) gen_call_function_mm((void*)&dynrec_movsd_dword,(Bitu)DRCD_SEG_PHYS(di_base_addr),(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES));
+ else gen_call_function_mm((void*)&dynrec_movsd_word,(Bitu)DRCD_SEG_PHYS(di_base_addr),(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES));
+ break;
+
+ case STR_LODSB:
+ if (decode.big_addr) gen_call_function_m((void*)&dynrec_lodsb_dword,(Bitu)DRCD_SEG_PHYS(di_base_addr));
+ else gen_call_function_m((void*)&dynrec_lodsb_word,(Bitu)DRCD_SEG_PHYS(di_base_addr));
+ break;
+ case STR_LODSW:
+ if (decode.big_addr) gen_call_function_m((void*)&dynrec_lodsw_dword,(Bitu)DRCD_SEG_PHYS(di_base_addr));
+ else gen_call_function_m((void*)&dynrec_lodsw_word,(Bitu)DRCD_SEG_PHYS(di_base_addr));
+ break;
+ case STR_LODSD:
+ if (decode.big_addr) gen_call_function_m((void*)&dynrec_lodsd_dword,(Bitu)DRCD_SEG_PHYS(di_base_addr));
+ else gen_call_function_m((void*)&dynrec_lodsd_word,(Bitu)DRCD_SEG_PHYS(di_base_addr));
+ break;
+
+ case STR_STOSB:
+ if (decode.big_addr) gen_call_function_m((void*)&dynrec_stosb_dword,(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES));
+ else gen_call_function_m((void*)&dynrec_stosb_word,(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES));
+ break;
+ case STR_STOSW:
+ if (decode.big_addr) gen_call_function_m((void*)&dynrec_stosw_dword,(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES));
+ else gen_call_function_m((void*)&dynrec_stosw_word,(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES));
+ break;
+ case STR_STOSD:
+ if (decode.big_addr) gen_call_function_m((void*)&dynrec_stosd_dword,(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES));
+ else gen_call_function_m((void*)&dynrec_stosd_word,(Bitu)DRCD_SEG_PHYS(DRC_SEG_ES));
+ break;
+ default: IllegalOptionDynrec("dyn_string");
+ }
+ if (decode.rep) MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,DRC_REG_ECX,decode.big_addr);
+
+ if (op<STR_SCASB) {
+ // those string operations are allowed for premature termination
+ // when not enough cycles left
+ if (!decode.big_addr) gen_extend_word(false,FC_RETOP);
+ save_info_dynrec[used_save_info_dynrec].branch_pos=gen_create_branch_long_nonzero(FC_RETOP,true);
+ save_info_dynrec[used_save_info_dynrec].eip_change=decode.op_start-decode.code_start;
+ save_info_dynrec[used_save_info_dynrec].type=string_break;
+ used_save_info_dynrec++;
+ }
+}
+
+
+static void dyn_read_port_byte_direct(Bit8u port) {
+ dyn_add_iocheck_var(port,1);
+ gen_call_function_I((void*)&IO_ReadB,port);
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_RETOP,DRC_REG_EAX,0);
+}
+
+static void dyn_read_port_word_direct(Bit8u port) {
+ dyn_add_iocheck_var(port,decode.big_op?4:2);
+ gen_call_function_I(decode.big_op?((void*)&IO_ReadD):((void*)&IO_ReadW),port);
+ MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,DRC_REG_EAX,decode.big_op);
+}
+
+static void dyn_write_port_byte_direct(Bit8u port) {
+ dyn_add_iocheck_var(port,1);
+ MOV_REG_BYTE_TO_HOST_REG_LOW(FC_RETOP,DRC_REG_EAX,0);
+ gen_extend_byte(false,FC_RETOP);
+ gen_call_function_IR((void*)&IO_WriteB,port,FC_RETOP);
+}
+
+static void dyn_write_port_word_direct(Bit8u port) {
+ dyn_add_iocheck_var(port,decode.big_op?4:2);
+ MOV_REG_WORD_TO_HOST_REG(FC_RETOP,DRC_REG_EAX,decode.big_op);
+ if (!decode.big_op) gen_extend_word(false,FC_RETOP);
+ gen_call_function_IR(decode.big_op?((void*)&IO_WriteD):((void*)&IO_WriteW),port,FC_RETOP);
+}
+
+
+static void dyn_read_port_byte(void) {
+ MOV_REG_WORD16_TO_HOST_REG(FC_ADDR,DRC_REG_EDX);
+ gen_extend_word(false,FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_add_iocheck(FC_ADDR,1);
+ gen_restore_addr_reg();
+ gen_call_function_R((void*)&IO_ReadB,FC_ADDR);
+ MOV_REG_BYTE_FROM_HOST_REG_LOW(FC_RETOP,DRC_REG_EAX,0);
+}
+
+static void dyn_read_port_word(void) {
+ MOV_REG_WORD16_TO_HOST_REG(FC_ADDR,DRC_REG_EDX);
+ gen_extend_word(false,FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_add_iocheck(FC_ADDR,decode.big_op?4:2);
+ gen_restore_addr_reg();
+ gen_call_function_R(decode.big_op?((void*)&IO_ReadD):((void*)&IO_ReadW),FC_ADDR);
+ MOV_REG_WORD_FROM_HOST_REG(FC_RETOP,DRC_REG_EAX,decode.big_op);
+}
+
+static void dyn_write_port_byte(void) {
+ MOV_REG_WORD16_TO_HOST_REG(FC_ADDR,DRC_REG_EDX);
+ gen_extend_word(false,FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_add_iocheck(FC_ADDR,1);
+ MOV_REG_BYTE_TO_HOST_REG_LOW(FC_RETOP,DRC_REG_EAX,0);
+ gen_extend_byte(false,FC_RETOP);
+ gen_restore_addr_reg();
+ gen_call_function_RR((void*)&IO_WriteB,FC_ADDR,FC_RETOP);
+}
+
+static void dyn_write_port_word(void) {
+ MOV_REG_WORD16_TO_HOST_REG(FC_ADDR,DRC_REG_EDX);
+ gen_extend_word(false,FC_ADDR);
+ gen_protect_addr_reg();
+ dyn_add_iocheck(FC_ADDR,decode.big_op?4:2);
+ MOV_REG_WORD_TO_HOST_REG(FC_RETOP,DRC_REG_EAX,decode.big_op);
+ if (!decode.big_op) gen_extend_word(false,FC_RETOP);
+ gen_restore_addr_reg();
+ gen_call_function_RR(decode.big_op?((void*)&IO_WriteD):((void*)&IO_WriteW),FC_ADDR,FC_RETOP);
+}
+
+
+static void dyn_enter(void) {
+ Bitu bytes=decode_fetchw();
+ Bitu level=decode_fetchb();
+ gen_call_function_III((void *)&CPU_ENTER,decode.big_op,bytes,level);
+}
+
+static void dynrec_leave_word(void) {
+ reg_esp&=cpu.stack.notmask;
+ reg_esp|=(reg_ebp&cpu.stack.mask);
+ reg_bp=(Bit16u)CPU_Pop16();
+}
+
+static void dynrec_leave_dword(void) {
+ reg_esp&=cpu.stack.notmask;
+ reg_esp|=(reg_ebp&cpu.stack.mask);
+ reg_ebp=CPU_Pop32();
+}
+
+static void dyn_leave(void) {
+ if (decode.big_op) gen_call_function_raw((void *)dynrec_leave_dword);
+ else gen_call_function_raw((void *)dynrec_leave_word);
+}
+
+
+static void dynrec_pusha_word(void) {
+ Bit16u old_sp=reg_sp;
+ CPU_Push16(reg_ax);CPU_Push16(reg_cx);CPU_Push16(reg_dx);CPU_Push16(reg_bx);
+ CPU_Push16(old_sp);CPU_Push16(reg_bp);CPU_Push16(reg_si);CPU_Push16(reg_di);
+}
+
+static void dynrec_pusha_dword(void) {
+ Bitu tmpesp = reg_esp;
+ CPU_Push32(reg_eax);CPU_Push32(reg_ecx);CPU_Push32(reg_edx);CPU_Push32(reg_ebx);
+ CPU_Push32(tmpesp);CPU_Push32(reg_ebp);CPU_Push32(reg_esi);CPU_Push32(reg_edi);
+}
+
+static void dynrec_popa_word(void) {
+ reg_di=(Bit16u)CPU_Pop16();reg_si=(Bit16u)CPU_Pop16();
+ reg_bp=(Bit16u)CPU_Pop16();CPU_Pop16(); //Don't save SP
+ reg_bx=(Bit16u)CPU_Pop16();reg_dx=(Bit16u)CPU_Pop16();
+ reg_cx=(Bit16u)CPU_Pop16();reg_ax=(Bit16u)CPU_Pop16();
+}
+
+static void dynrec_popa_dword(void) {
+ reg_edi=CPU_Pop32();reg_esi=CPU_Pop32();reg_ebp=CPU_Pop32();CPU_Pop32(); //Don't save ESP
+ reg_ebx=CPU_Pop32();reg_edx=CPU_Pop32();reg_ecx=CPU_Pop32();reg_eax=CPU_Pop32();
+}
diff --git a/src/cpu/core_dynrec/dyn_fpu.h b/src/cpu/core_dynrec/dyn_fpu.h
new file mode 100644
index 000000000..739b4bb9d
--- /dev/null
+++ b/src/cpu/core_dynrec/dyn_fpu.h
@@ -0,0 +1,696 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+#include "dosbox.h"
+#if C_FPU
+
+#include <math.h>
+#include <float.h>
+#include "cross.h"
+#include "mem.h"
+#include "fpu.h"
+#include "cpu.h"
+
+
+static void FPU_FDECSTP(){
+ TOP = (TOP - 1) & 7;
+}
+
+static void FPU_FINCSTP(){
+ TOP = (TOP + 1) & 7;
+}
+
+static void FPU_FNSTCW(PhysPt addr){
+ mem_writew(addr,fpu.cw);
+}
+
+static void FPU_FFREE(Bitu st) {
+ fpu.tags[st]=TAG_Empty;
+}
+
+
+#if C_FPU_X86
+#include "../../fpu/fpu_instructions_x86.h"
+#else
+#include "../../fpu/fpu_instructions.h"
+#endif
+
+
+static INLINE void dyn_fpu_top() {
+ gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
+ gen_add_imm(FC_OP2,decode.modrm.rm);
+ gen_and_imm(FC_OP2,7);
+ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true);
+}
+
+static INLINE void dyn_fpu_top_swapped() {
+ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true);
+ gen_add_imm(FC_OP1,decode.modrm.rm);
+ gen_and_imm(FC_OP1,7);
+ gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
+}
+
+static void dyn_eatree() {
+// Bitu group = (decode.modrm.val >> 3) & 7;
+ Bitu group = decode.modrm.reg&7; //It is already that, but compilers.
+ switch (group){
+ case 0x00: // FADD ST,STi
+ gen_call_function_R((void*)&FPU_FADD_EA,FC_OP1);
+ break;
+ case 0x01: // FMUL ST,STi
+ gen_call_function_R((void*)&FPU_FMUL_EA,FC_OP1);
+ break;
+ case 0x02: // FCOM STi
+ gen_call_function_R((void*)&FPU_FCOM_EA,FC_OP1);
+ break;
+ case 0x03: // FCOMP STi
+ gen_call_function_R((void*)&FPU_FCOM_EA,FC_OP1);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ case 0x04: // FSUB ST,STi
+ gen_call_function_R((void*)&FPU_FSUB_EA,FC_OP1);
+ break;
+ case 0x05: // FSUBR ST,STi
+ gen_call_function_R((void*)&FPU_FSUBR_EA,FC_OP1);
+ break;
+ case 0x06: // FDIV ST,STi
+ gen_call_function_R((void*)&FPU_FDIV_EA,FC_OP1);
+ break;
+ case 0x07: // FDIVR ST,STi
+ gen_call_function_R((void*)&FPU_FDIVR_EA,FC_OP1);
+ break;
+ default:
+ break;
+ }
+}
+
+static void dyn_fpu_esc0(){
+ dyn_get_modrm();
+// if (decode.modrm.val >= 0xc0) {
+ if (decode.modrm.mod == 3) {
+ dyn_fpu_top();
+ switch (decode.modrm.reg){
+ case 0x00: //FADD ST,STi
+ gen_call_function_RR((void*)&FPU_FADD,FC_OP1,FC_OP2);
+ break;
+ case 0x01: // FMUL ST,STi
+ gen_call_function_RR((void*)&FPU_FMUL,FC_OP1,FC_OP2);
+ break;
+ case 0x02: // FCOM STi
+ gen_call_function_RR((void*)&FPU_FCOM,FC_OP1,FC_OP2);
+ break;
+ case 0x03: // FCOMP STi
+ gen_call_function_RR((void*)&FPU_FCOM,FC_OP1,FC_OP2);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ case 0x04: // FSUB ST,STi
+ gen_call_function_RR((void*)&FPU_FSUB,FC_OP1,FC_OP2);
+ break;
+ case 0x05: // FSUBR ST,STi
+ gen_call_function_RR((void*)&FPU_FSUBR,FC_OP1,FC_OP2);
+ break;
+ case 0x06: // FDIV ST,STi
+ gen_call_function_RR((void*)&FPU_FDIV,FC_OP1,FC_OP2);
+ break;
+ case 0x07: // FDIVR ST,STi
+ gen_call_function_RR((void*)&FPU_FDIVR,FC_OP1,FC_OP2);
+ break;
+ default:
+ break;
+ }
+ } else {
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FLD_F32_EA,FC_ADDR);
+ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true);
+ dyn_eatree();
+ }
+}
+
+
+static void dyn_fpu_esc1(){
+ dyn_get_modrm();
+// if (decode.modrm.val >= 0xc0) {
+ if (decode.modrm.mod == 3) {
+ switch (decode.modrm.reg){
+ case 0x00: /* FLD STi */
+ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true);
+ gen_add_imm(FC_OP1,decode.modrm.rm);
+ gen_and_imm(FC_OP1,7);
+ gen_protect_reg(FC_OP1);
+ gen_call_function_raw((void*)&FPU_PREP_PUSH);
+ gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
+ gen_restore_reg(FC_OP1);
+ gen_call_function_RR((void*)&FPU_FST,FC_OP1,FC_OP2);
+ break;
+ case 0x01: /* FXCH STi */
+ dyn_fpu_top();
+ gen_call_function_RR((void*)&FPU_FXCH,FC_OP1,FC_OP2);
+ break;
+ case 0x02: /* FNOP */
+ gen_call_function_raw((void*)&FPU_FNOP);
+ break;
+ case 0x03: /* FSTP STi */
+ dyn_fpu_top();
+ gen_call_function_RR((void*)&FPU_FST,FC_OP1,FC_OP2);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ case 0x04:
+ switch(decode.modrm.rm){
+ case 0x00: /* FCHS */
+ gen_call_function_raw((void*)&FPU_FCHS);
+ break;
+ case 0x01: /* FABS */
+ gen_call_function_raw((void*)&FPU_FABS);
+ break;
+ case 0x02: /* UNKNOWN */
+ case 0x03: /* ILLEGAL */
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",decode.modrm.reg,decode.modrm.rm);
+ break;
+ case 0x04: /* FTST */
+ gen_call_function_raw((void*)&FPU_FTST);
+ break;
+ case 0x05: /* FXAM */
+ gen_call_function_raw((void*)&FPU_FXAM);
+ break;
+ case 0x06: /* FTSTP (cyrix)*/
+ case 0x07: /* UNKNOWN */
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ break;
+ case 0x05:
+ switch(decode.modrm.rm){
+ case 0x00: /* FLD1 */
+ gen_call_function_raw((void*)&FPU_FLD1);
+ break;
+ case 0x01: /* FLDL2T */
+ gen_call_function_raw((void*)&FPU_FLDL2T);
+ break;
+ case 0x02: /* FLDL2E */
+ gen_call_function_raw((void*)&FPU_FLDL2E);
+ break;
+ case 0x03: /* FLDPI */
+ gen_call_function_raw((void*)&FPU_FLDPI);
+ break;
+ case 0x04: /* FLDLG2 */
+ gen_call_function_raw((void*)&FPU_FLDLG2);
+ break;
+ case 0x05: /* FLDLN2 */
+ gen_call_function_raw((void*)&FPU_FLDLN2);
+ break;
+ case 0x06: /* FLDZ*/
+ gen_call_function_raw((void*)&FPU_FLDZ);
+ break;
+ case 0x07: /* ILLEGAL */
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ break;
+ case 0x06:
+ switch(decode.modrm.rm){
+ case 0x00: /* F2XM1 */
+ gen_call_function_raw((void*)&FPU_F2XM1);
+ break;
+ case 0x01: /* FYL2X */
+ gen_call_function_raw((void*)&FPU_FYL2X);
+ break;
+ case 0x02: /* FPTAN */
+ gen_call_function_raw((void*)&FPU_FPTAN);
+ break;
+ case 0x03: /* FPATAN */
+ gen_call_function_raw((void*)&FPU_FPATAN);
+ break;
+ case 0x04: /* FXTRACT */
+ gen_call_function_raw((void*)&FPU_FXTRACT);
+ break;
+ case 0x05: /* FPREM1 */
+ gen_call_function_raw((void*)&FPU_FPREM1);
+ break;
+ case 0x06: /* FDECSTP */
+ gen_call_function_raw((void*)&FPU_FDECSTP);
+ break;
+ case 0x07: /* FINCSTP */
+ gen_call_function_raw((void*)&FPU_FINCSTP);
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ break;
+ case 0x07:
+ switch(decode.modrm.rm){
+ case 0x00: /* FPREM */
+ gen_call_function_raw((void*)&FPU_FPREM);
+ break;
+ case 0x01: /* FYL2XP1 */
+ gen_call_function_raw((void*)&FPU_FYL2XP1);
+ break;
+ case 0x02: /* FSQRT */
+ gen_call_function_raw((void*)&FPU_FSQRT);
+ break;
+ case 0x03: /* FSINCOS */
+ gen_call_function_raw((void*)&FPU_FSINCOS);
+ break;
+ case 0x04: /* FRNDINT */
+ gen_call_function_raw((void*)&FPU_FRNDINT);
+ break;
+ case 0x05: /* FSCALE */
+ gen_call_function_raw((void*)&FPU_FSCALE);
+ break;
+ case 0x06: /* FSIN */
+ gen_call_function_raw((void*)&FPU_FSIN);
+ break;
+ case 0x07: /* FCOS */
+ gen_call_function_raw((void*)&FPU_FCOS);
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ } else {
+ switch(decode.modrm.reg){
+ case 0x00: /* FLD float*/
+ gen_call_function_raw((void*)&FPU_PREP_PUSH);
+ dyn_fill_ea(FC_OP1);
+ gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
+ gen_call_function_RR((void*)&FPU_FLD_F32,FC_OP1,FC_OP2);
+ break;
+ case 0x01: /* UNKNOWN */
+ LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ break;
+ case 0x02: /* FST float*/
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FST_F32,FC_ADDR);
+ break;
+ case 0x03: /* FSTP float*/
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FST_F32,FC_ADDR);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ case 0x04: /* FLDENV */
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FLDENV,FC_ADDR);
+ break;
+ case 0x05: /* FLDCW */
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void *)&FPU_FLDCW,FC_ADDR);
+ break;
+ case 0x06: /* FSTENV */
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void *)&FPU_FSTENV,FC_ADDR);
+ break;
+ case 0x07: /* FNSTCW*/
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void *)&FPU_FNSTCW,FC_ADDR);
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ }
+}
+
+static void dyn_fpu_esc2(){
+ dyn_get_modrm();
+// if (decode.modrm.val >= 0xc0) {
+ if (decode.modrm.mod == 3) {
+ switch(decode.modrm.reg){
+ case 0x05:
+ switch(decode.modrm.rm){
+ case 0x01: /* FUCOMPP */
+ gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
+ gen_add_imm(FC_OP2,1);
+ gen_and_imm(FC_OP2,7);
+ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true);
+ gen_call_function_RR((void *)&FPU_FUCOM,FC_OP1,FC_OP2);
+ gen_call_function_raw((void *)&FPU_FPOP);
+ gen_call_function_raw((void *)&FPU_FPOP);
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 2:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 2:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ } else {
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FLD_I32_EA,FC_ADDR);
+ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true);
+ dyn_eatree();
+ }
+}
+
+static void dyn_fpu_esc3(){
+ dyn_get_modrm();
+// if (decode.modrm.val >= 0xc0) {
+ if (decode.modrm.mod == 3) {
+ switch (decode.modrm.reg) {
+ case 0x04:
+ switch (decode.modrm.rm) {
+ case 0x00: //FNENI
+ case 0x01: //FNDIS
+ LOG(LOG_FPU,LOG_ERROR)("8087 only fpu code used esc 3: group 4: subfuntion: %d",decode.modrm.rm);
+ break;
+ case 0x02: //FNCLEX FCLEX
+ gen_call_function_raw((void*)&FPU_FCLEX);
+ break;
+ case 0x03: //FNINIT FINIT
+ gen_call_function_raw((void*)&FPU_FINIT);
+ break;
+ case 0x04: //FNSETPM
+ case 0x05: //FRSTPM
+// LOG(LOG_FPU,LOG_ERROR)("80267 protected mode (un)set. Nothing done");
+ break;
+ default:
+ E_Exit("ESC 3:ILLEGAL OPCODE group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 3:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ } else {
+ switch(decode.modrm.reg){
+ case 0x00: /* FILD */
+ gen_call_function_raw((void*)&FPU_PREP_PUSH);
+ dyn_fill_ea(FC_OP1);
+ gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
+ gen_call_function_RR((void*)&FPU_FLD_I32,FC_OP1,FC_OP2);
+ break;
+ case 0x01: /* FISTTP */
+ LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ break;
+ case 0x02: /* FIST */
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FST_I32,FC_ADDR);
+ break;
+ case 0x03: /* FISTP */
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FST_I32,FC_ADDR);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ case 0x05: /* FLD 80 Bits Real */
+ gen_call_function_raw((void*)&FPU_PREP_PUSH);
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FLD_F80,FC_ADDR);
+ break;
+ case 0x07: /* FSTP 80 Bits Real */
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FST_F80,FC_ADDR);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ }
+ }
+}
+
+static void dyn_fpu_esc4(){
+ dyn_get_modrm();
+// if (decode.modrm.val >= 0xc0) {
+ if (decode.modrm.mod == 3) {
+ switch(decode.modrm.reg){
+ case 0x00: /* FADD STi,ST*/
+ dyn_fpu_top_swapped();
+ gen_call_function_RR((void*)&FPU_FADD,FC_OP1,FC_OP2);
+ break;
+ case 0x01: /* FMUL STi,ST*/
+ dyn_fpu_top_swapped();
+ gen_call_function_RR((void*)&FPU_FMUL,FC_OP1,FC_OP2);
+ break;
+ case 0x02: /* FCOM*/
+ dyn_fpu_top();
+ gen_call_function_RR((void*)&FPU_FCOM,FC_OP1,FC_OP2);
+ break;
+ case 0x03: /* FCOMP*/
+ dyn_fpu_top();
+ gen_call_function_RR((void*)&FPU_FCOM,FC_OP1,FC_OP2);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ case 0x04: /* FSUBR STi,ST*/
+ dyn_fpu_top_swapped();
+ gen_call_function_RR((void*)&FPU_FSUBR,FC_OP1,FC_OP2);
+ break;
+ case 0x05: /* FSUB STi,ST*/
+ dyn_fpu_top_swapped();
+ gen_call_function_RR((void*)&FPU_FSUB,FC_OP1,FC_OP2);
+ break;
+ case 0x06: /* FDIVR STi,ST*/
+ dyn_fpu_top_swapped();
+ gen_call_function_RR((void*)&FPU_FDIVR,FC_OP1,FC_OP2);
+ break;
+ case 0x07: /* FDIV STi,ST*/
+ dyn_fpu_top_swapped();
+ gen_call_function_RR((void*)&FPU_FDIV,FC_OP1,FC_OP2);
+ break;
+ default:
+ break;
+ }
+ } else {
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FLD_F64_EA,FC_ADDR);
+ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true);
+ dyn_eatree();
+ }
+}
+
+static void dyn_fpu_esc5(){
+ dyn_get_modrm();
+// if (decode.modrm.val >= 0xc0) {
+ if (decode.modrm.mod == 3) {
+ dyn_fpu_top();
+ switch(decode.modrm.reg){
+ case 0x00: /* FFREE STi */
+ gen_call_function_R((void*)&FPU_FFREE,FC_OP2);
+ break;
+ case 0x01: /* FXCH STi*/
+ gen_call_function_RR((void*)&FPU_FXCH,FC_OP1,FC_OP2);
+ break;
+ case 0x02: /* FST STi */
+ gen_call_function_RR((void*)&FPU_FST,FC_OP1,FC_OP2);
+ break;
+ case 0x03: /* FSTP STi*/
+ gen_call_function_RR((void*)&FPU_FST,FC_OP1,FC_OP2);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ case 0x04: /* FUCOM STi */
+ gen_call_function_RR((void*)&FPU_FUCOM,FC_OP1,FC_OP2);
+ break;
+ case 0x05: /*FUCOMP STi */
+ gen_call_function_RR((void*)&FPU_FUCOM,FC_OP1,FC_OP2);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 5:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ } else {
+ switch(decode.modrm.reg){
+ case 0x00: /* FLD double real*/
+ gen_call_function_raw((void*)&FPU_PREP_PUSH);
+ dyn_fill_ea(FC_OP1);
+ gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
+ gen_call_function_RR((void*)&FPU_FLD_F64,FC_OP1,FC_OP2);
+ break;
+ case 0x01: /* FISTTP longint*/
+ LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ break;
+ case 0x02: /* FST double real*/
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FST_F64,FC_ADDR);
+ break;
+ case 0x03: /* FSTP double real*/
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FST_F64,FC_ADDR);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ case 0x04: /* FRSTOR */
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FRSTOR,FC_ADDR);
+ break;
+ case 0x06: /* FSAVE */
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FSAVE,FC_ADDR);
+ break;
+ case 0x07: /*FNSTSW */
+ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true);
+ gen_call_function_R((void*)&FPU_SET_TOP,FC_OP1);
+ dyn_fill_ea(FC_OP1);
+ gen_mov_word_to_reg(FC_OP2,(void*)(&fpu.sw),false);
+ gen_call_function_RR((void*)&mem_writew,FC_OP1,FC_OP2);
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ }
+ }
+}
+
+static void dyn_fpu_esc6(){
+ dyn_get_modrm();
+// if (decode.modrm.val >= 0xc0) {
+ if (decode.modrm.mod == 3) {
+ switch(decode.modrm.reg){
+ case 0x00: /*FADDP STi,ST*/
+ dyn_fpu_top_swapped();
+ gen_call_function_RR((void*)&FPU_FADD,FC_OP1,FC_OP2);
+ break;
+ case 0x01: /* FMULP STi,ST*/
+ dyn_fpu_top_swapped();
+ gen_call_function_RR((void*)&FPU_FMUL,FC_OP1,FC_OP2);
+ break;
+ case 0x02: /* FCOMP5*/
+ dyn_fpu_top();
+ gen_call_function_RR((void*)&FPU_FCOM,FC_OP1,FC_OP2);
+ break; /* TODO IS THIS ALLRIGHT ????????? */
+ case 0x03: /*FCOMPP*/
+ if(decode.modrm.rm != 1) {
+ LOG(LOG_FPU,LOG_WARN)("ESC 6:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ return;
+ }
+ gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
+ gen_add_imm(FC_OP2,1);
+ gen_and_imm(FC_OP2,7);
+ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true);
+ gen_call_function_RR((void*)&FPU_FCOM,FC_OP1,FC_OP2);
+ gen_call_function_raw((void*)&FPU_FPOP); /* extra pop at the bottom*/
+ break;
+ case 0x04: /* FSUBRP STi,ST*/
+ dyn_fpu_top_swapped();
+ gen_call_function_RR((void*)&FPU_FSUBR,FC_OP1,FC_OP2);
+ break;
+ case 0x05: /* FSUBP STi,ST*/
+ dyn_fpu_top_swapped();
+ gen_call_function_RR((void*)&FPU_FSUB,FC_OP1,FC_OP2);
+ break;
+ case 0x06: /* FDIVRP STi,ST*/
+ dyn_fpu_top_swapped();
+ gen_call_function_RR((void*)&FPU_FDIVR,FC_OP1,FC_OP2);
+ break;
+ case 0x07: /* FDIVP STi,ST*/
+ dyn_fpu_top_swapped();
+ gen_call_function_RR((void*)&FPU_FDIV,FC_OP1,FC_OP2);
+ break;
+ default:
+ break;
+ }
+ gen_call_function_raw((void*)&FPU_FPOP);
+ } else {
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FLD_I16_EA,FC_ADDR);
+ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true);
+ dyn_eatree();
+ }
+}
+
+static void dyn_fpu_esc7(){
+ dyn_get_modrm();
+// if (decode.modrm.val >= 0xc0) {
+ if (decode.modrm.mod == 3) {
+ switch (decode.modrm.reg){
+ case 0x00: /* FFREEP STi */
+ dyn_fpu_top();
+ gen_call_function_R((void*)&FPU_FFREE,FC_OP2);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ case 0x01: /* FXCH STi*/
+ dyn_fpu_top();
+ gen_call_function_RR((void*)&FPU_FXCH,FC_OP1,FC_OP2);
+ break;
+ case 0x02: /* FSTP STi*/
+ case 0x03: /* FSTP STi*/
+ dyn_fpu_top();
+ gen_call_function_RR((void*)&FPU_FST,FC_OP1,FC_OP2);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ case 0x04:
+ switch(decode.modrm.rm){
+ case 0x00: /* FNSTSW AX*/
+ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true);
+ gen_call_function_R((void*)&FPU_SET_TOP,FC_OP1);
+ gen_mov_word_to_reg(FC_OP1,(void*)(&fpu.sw),false);
+ MOV_REG_WORD16_FROM_HOST_REG(FC_OP1,DRC_REG_EAX);
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ } else {
+ switch(decode.modrm.reg){
+ case 0x00: /* FILD Bit16s */
+ gen_call_function_raw((void*)&FPU_PREP_PUSH);
+ dyn_fill_ea(FC_OP1);
+ gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
+ gen_call_function_RR((void*)&FPU_FLD_I16,FC_OP1,FC_OP2);
+ break;
+ case 0x01:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ break;
+ case 0x02: /* FIST Bit16s */
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FST_I16,FC_ADDR);
+ break;
+ case 0x03: /* FISTP Bit16s */
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FST_I16,FC_ADDR);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ case 0x04: /* FBLD packed BCD */
+ gen_call_function_raw((void*)&FPU_PREP_PUSH);
+ dyn_fill_ea(FC_OP1);
+ gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
+ gen_call_function_RR((void*)&FPU_FBLD,FC_OP1,FC_OP2);
+ break;
+ case 0x05: /* FILD Bit64s */
+ gen_call_function_raw((void*)&FPU_PREP_PUSH);
+ dyn_fill_ea(FC_OP1);
+ gen_mov_word_to_reg(FC_OP2,(void*)(&TOP),true);
+ gen_call_function_RR((void*)&FPU_FLD_I64,FC_OP1,FC_OP2);
+ break;
+ case 0x06: /* FBSTP packed BCD */
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FBST,FC_ADDR);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ case 0x07: /* FISTP Bit64s */
+ dyn_fill_ea(FC_ADDR);
+ gen_call_function_R((void*)&FPU_FST_I64,FC_ADDR);
+ gen_call_function_raw((void*)&FPU_FPOP);
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",decode.modrm.reg,decode.modrm.rm);
+ break;
+ }
+ }
+}
+
+#endif
diff --git a/src/cpu/core_dynrec/operators.h b/src/cpu/core_dynrec/operators.h
new file mode 100644
index 000000000..5f6283504
--- /dev/null
+++ b/src/cpu/core_dynrec/operators.h
@@ -0,0 +1,1996 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+static Bit8u DRC_CALL_CONV dynrec_add_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_add_byte(Bit8u op1,Bit8u op2) {
+ lf_var1b=op1;
+ lf_var2b=op2;
+ lf_resb=(Bit8u)(lf_var1b+lf_var2b);
+ lflags.type=t_ADDb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_add_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_add_byte_simple(Bit8u op1,Bit8u op2) {
+ return op1+op2;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_adc_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_adc_byte(Bit8u op1,Bit8u op2) {
+ lflags.oldcf=get_CF()!=0;
+ lf_var1b=op1;
+ lf_var2b=op2;
+ lf_resb=(Bit8u)(lf_var1b+lf_var2b+lflags.oldcf);
+ lflags.type=t_ADCb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_adc_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_adc_byte_simple(Bit8u op1,Bit8u op2) {
+ return (Bit8u)(op1+op2+(Bitu)(get_CF()!=0));
+}
+
+static Bit8u DRC_CALL_CONV dynrec_sub_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_sub_byte(Bit8u op1,Bit8u op2) {
+ lf_var1b=op1;
+ lf_var2b=op2;
+ lf_resb=(Bit8u)(lf_var1b-lf_var2b);
+ lflags.type=t_SUBb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_sub_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_sub_byte_simple(Bit8u op1,Bit8u op2) {
+ return op1-op2;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_sbb_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_sbb_byte(Bit8u op1,Bit8u op2) {
+ lflags.oldcf=get_CF()!=0;
+ lf_var1b=op1;
+ lf_var2b=op2;
+ lf_resb=(Bit8u)(lf_var1b-(lf_var2b+lflags.oldcf));
+ lflags.type=t_SBBb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_sbb_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_sbb_byte_simple(Bit8u op1,Bit8u op2) {
+ return (Bit8u)(op1-(op2+(Bitu)(get_CF()!=0)));
+}
+
+static void DRC_CALL_CONV dynrec_cmp_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static void DRC_CALL_CONV dynrec_cmp_byte(Bit8u op1,Bit8u op2) {
+ lf_var1b=op1;
+ lf_var2b=op2;
+ lf_resb=(Bit8u)(lf_var1b-lf_var2b);
+ lflags.type=t_CMPb;
+}
+
+static void DRC_CALL_CONV dynrec_cmp_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static void DRC_CALL_CONV dynrec_cmp_byte_simple(Bit8u op1,Bit8u op2) {
+}
+
+static Bit8u DRC_CALL_CONV dynrec_xor_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_xor_byte(Bit8u op1,Bit8u op2) {
+ lf_var1b=op1;
+ lf_var2b=op2;
+ lf_resb=lf_var1b ^ lf_var2b;
+ lflags.type=t_XORb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_xor_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_xor_byte_simple(Bit8u op1,Bit8u op2) {
+ return op1 ^ op2;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_and_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_and_byte(Bit8u op1,Bit8u op2) {
+ lf_var1b=op1;
+ lf_var2b=op2;
+ lf_resb=lf_var1b & lf_var2b;
+ lflags.type=t_ANDb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_and_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_and_byte_simple(Bit8u op1,Bit8u op2) {
+ return op1 & op2;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_or_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_or_byte(Bit8u op1,Bit8u op2) {
+ lf_var1b=op1;
+ lf_var2b=op2;
+ lf_resb=lf_var1b | lf_var2b;
+ lflags.type=t_ORb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_or_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_or_byte_simple(Bit8u op1,Bit8u op2) {
+ return op1 | op2;
+}
+
+static void DRC_CALL_CONV dynrec_test_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static void DRC_CALL_CONV dynrec_test_byte(Bit8u op1,Bit8u op2) {
+ lf_var1b=op1;
+ lf_var2b=op2;
+ lf_resb=lf_var1b & lf_var2b;
+ lflags.type=t_TESTb;
+}
+
+static void DRC_CALL_CONV dynrec_test_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static void DRC_CALL_CONV dynrec_test_byte_simple(Bit8u op1,Bit8u op2) {
+}
+
+static Bit16u DRC_CALL_CONV dynrec_add_word(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_add_word(Bit16u op1,Bit16u op2) {
+ lf_var1w=op1;
+ lf_var2w=op2;
+ lf_resw=(Bit16u)(lf_var1w+lf_var2w);
+ lflags.type=t_ADDw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_add_word_simple(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_add_word_simple(Bit16u op1,Bit16u op2) {
+ return op1+op2;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_adc_word(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_adc_word(Bit16u op1,Bit16u op2) {
+ lflags.oldcf=get_CF()!=0;
+ lf_var1w=op1;
+ lf_var2w=op2;
+ lf_resw=(Bit16u)(lf_var1w+lf_var2w+lflags.oldcf);
+ lflags.type=t_ADCw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_adc_word_simple(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_adc_word_simple(Bit16u op1,Bit16u op2) {
+ return (Bit16u)(op1+op2+(Bitu)(get_CF()!=0));
+}
+
+static Bit16u DRC_CALL_CONV dynrec_sub_word(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_sub_word(Bit16u op1,Bit16u op2) {
+ lf_var1w=op1;
+ lf_var2w=op2;
+ lf_resw=(Bit16u)(lf_var1w-lf_var2w);
+ lflags.type=t_SUBw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_sub_word_simple(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_sub_word_simple(Bit16u op1,Bit16u op2) {
+ return op1-op2;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_sbb_word(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_sbb_word(Bit16u op1,Bit16u op2) {
+ lflags.oldcf=get_CF()!=0;
+ lf_var1w=op1;
+ lf_var2w=op2;
+ lf_resw=(Bit16u)(lf_var1w-(lf_var2w+lflags.oldcf));
+ lflags.type=t_SBBw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_sbb_word_simple(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_sbb_word_simple(Bit16u op1,Bit16u op2) {
+ return (Bit16u)(op1-(op2+(Bitu)(get_CF()!=0)));
+}
+
+static void DRC_CALL_CONV dynrec_cmp_word(Bit16u op1,Bit16u op2) DRC_FC;
+static void DRC_CALL_CONV dynrec_cmp_word(Bit16u op1,Bit16u op2) {
+ lf_var1w=op1;
+ lf_var2w=op2;
+ lf_resw=(Bit16u)(lf_var1w-lf_var2w);
+ lflags.type=t_CMPw;
+}
+
+static void DRC_CALL_CONV dynrec_cmp_word_simple(Bit16u op1,Bit16u op2) DRC_FC;
+static void DRC_CALL_CONV dynrec_cmp_word_simple(Bit16u op1,Bit16u op2) {
+}
+
+static Bit16u DRC_CALL_CONV dynrec_xor_word(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_xor_word(Bit16u op1,Bit16u op2) {
+ lf_var1w=op1;
+ lf_var2w=op2;
+ lf_resw=lf_var1w ^ lf_var2w;
+ lflags.type=t_XORw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_xor_word_simple(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_xor_word_simple(Bit16u op1,Bit16u op2) {
+ return op1 ^ op2;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_and_word(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_and_word(Bit16u op1,Bit16u op2) {
+ lf_var1w=op1;
+ lf_var2w=op2;
+ lf_resw=lf_var1w & lf_var2w;
+ lflags.type=t_ANDw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_and_word_simple(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_and_word_simple(Bit16u op1,Bit16u op2) {
+ return op1 & op2;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_or_word(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_or_word(Bit16u op1,Bit16u op2) {
+ lf_var1w=op1;
+ lf_var2w=op2;
+ lf_resw=lf_var1w | lf_var2w;
+ lflags.type=t_ORw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_or_word_simple(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_or_word_simple(Bit16u op1,Bit16u op2) {
+ return op1 | op2;
+}
+
+static void DRC_CALL_CONV dynrec_test_word(Bit16u op1,Bit16u op2) DRC_FC;
+static void DRC_CALL_CONV dynrec_test_word(Bit16u op1,Bit16u op2) {
+ lf_var1w=op1;
+ lf_var2w=op2;
+ lf_resw=lf_var1w & lf_var2w;
+ lflags.type=t_TESTw;
+}
+
+static void DRC_CALL_CONV dynrec_test_word_simple(Bit16u op1,Bit16u op2) DRC_FC;
+static void DRC_CALL_CONV dynrec_test_word_simple(Bit16u op1,Bit16u op2) {
+}
+
+static Bit32u DRC_CALL_CONV dynrec_add_dword(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_add_dword(Bit32u op1,Bit32u op2) {
+ lf_var1d=op1;
+ lf_var2d=op2;
+ lf_resd=lf_var1d+lf_var2d;
+ lflags.type=t_ADDd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_add_dword_simple(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_add_dword_simple(Bit32u op1,Bit32u op2) {
+ return op1 + op2;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_adc_dword(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_adc_dword(Bit32u op1,Bit32u op2) {
+ lflags.oldcf=get_CF()!=0;
+ lf_var1d=op1;
+ lf_var2d=op2;
+ lf_resd=lf_var1d+lf_var2d+lflags.oldcf;
+ lflags.type=t_ADCd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_adc_dword_simple(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_adc_dword_simple(Bit32u op1,Bit32u op2) {
+ return op1+op2+(Bitu)(get_CF()!=0);
+}
+
+static Bit32u DRC_CALL_CONV dynrec_sub_dword(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_sub_dword(Bit32u op1,Bit32u op2) {
+ lf_var1d=op1;
+ lf_var2d=op2;
+ lf_resd=lf_var1d-lf_var2d;
+ lflags.type=t_SUBd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_sub_dword_simple(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_sub_dword_simple(Bit32u op1,Bit32u op2) {
+ return op1-op2;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_sbb_dword(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_sbb_dword(Bit32u op1,Bit32u op2) {
+ lflags.oldcf=get_CF()!=0;
+ lf_var1d=op1;
+ lf_var2d=op2;
+ lf_resd=lf_var1d-(lf_var2d+lflags.oldcf);
+ lflags.type=t_SBBd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_sbb_dword_simple(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_sbb_dword_simple(Bit32u op1,Bit32u op2) {
+ return op1-(op2+(Bitu)(get_CF()!=0));
+}
+
+static void DRC_CALL_CONV dynrec_cmp_dword(Bit32u op1,Bit32u op2) DRC_FC;
+static void DRC_CALL_CONV dynrec_cmp_dword(Bit32u op1,Bit32u op2) {
+ lf_var1d=op1;
+ lf_var2d=op2;
+ lf_resd=lf_var1d-lf_var2d;
+ lflags.type=t_CMPd;
+}
+
+static void DRC_CALL_CONV dynrec_cmp_dword_simple(Bit32u op1,Bit32u op2) DRC_FC;
+static void DRC_CALL_CONV dynrec_cmp_dword_simple(Bit32u op1,Bit32u op2) {
+}
+
+static Bit32u DRC_CALL_CONV dynrec_xor_dword(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_xor_dword(Bit32u op1,Bit32u op2) {
+ lf_var1d=op1;
+ lf_var2d=op2;
+ lf_resd=lf_var1d ^ lf_var2d;
+ lflags.type=t_XORd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_xor_dword_simple(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_xor_dword_simple(Bit32u op1,Bit32u op2) {
+ return op1 ^ op2;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_and_dword(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_and_dword(Bit32u op1,Bit32u op2) {
+ lf_var1d=op1;
+ lf_var2d=op2;
+ lf_resd=lf_var1d & lf_var2d;
+ lflags.type=t_ANDd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_and_dword_simple(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_and_dword_simple(Bit32u op1,Bit32u op2) {
+ return op1 & op2;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_or_dword(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_or_dword(Bit32u op1,Bit32u op2) {
+ lf_var1d=op1;
+ lf_var2d=op2;
+ lf_resd=lf_var1d | lf_var2d;
+ lflags.type=t_ORd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_or_dword_simple(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_or_dword_simple(Bit32u op1,Bit32u op2) {
+ return op1 | op2;
+}
+
+static void DRC_CALL_CONV dynrec_test_dword(Bit32u op1,Bit32u op2) DRC_FC;
+static void DRC_CALL_CONV dynrec_test_dword(Bit32u op1,Bit32u op2) {
+ lf_var1d=op1;
+ lf_var2d=op2;
+ lf_resd=lf_var1d & lf_var2d;
+ lflags.type=t_TESTd;
+}
+
+static void DRC_CALL_CONV dynrec_test_dword_simple(Bit32u op1,Bit32u op2) DRC_FC;
+static void DRC_CALL_CONV dynrec_test_dword_simple(Bit32u op1,Bit32u op2) {
+}
+
+
+static void dyn_dop_byte_gencall(DualOps op) {
+ switch (op) {
+ case DOP_ADD:
+ InvalidateFlags((void*)&dynrec_add_byte_simple,t_ADDb);
+ gen_call_function_raw((void*)&dynrec_add_byte);
+ break;
+ case DOP_ADC:
+ AcquireFlags(FLAG_CF);
+ InvalidateFlagsPartially((void*)&dynrec_adc_byte_simple,t_ADCb);
+ gen_call_function_raw((void*)&dynrec_adc_byte);
+ break;
+ case DOP_SUB:
+ InvalidateFlags((void*)&dynrec_sub_byte_simple,t_SUBb);
+ gen_call_function_raw((void*)&dynrec_sub_byte);
+ break;
+ case DOP_SBB:
+ AcquireFlags(FLAG_CF);
+ InvalidateFlagsPartially((void*)&dynrec_sbb_byte_simple,t_SBBb);
+ gen_call_function_raw((void*)&dynrec_sbb_byte);
+ break;
+ case DOP_CMP:
+ InvalidateFlags((void*)&dynrec_cmp_byte_simple,t_CMPb);
+ gen_call_function_raw((void*)&dynrec_cmp_byte);
+ break;
+ case DOP_XOR:
+ InvalidateFlags((void*)&dynrec_xor_byte_simple,t_XORb);
+ gen_call_function_raw((void*)&dynrec_xor_byte);
+ break;
+ case DOP_AND:
+ InvalidateFlags((void*)&dynrec_and_byte_simple,t_ANDb);
+ gen_call_function_raw((void*)&dynrec_and_byte);
+ break;
+ case DOP_OR:
+ InvalidateFlags((void*)&dynrec_or_byte_simple,t_ORb);
+ gen_call_function_raw((void*)&dynrec_or_byte);
+ break;
+ case DOP_TEST:
+ InvalidateFlags((void*)&dynrec_test_byte_simple,t_TESTb);
+ gen_call_function_raw((void*)&dynrec_test_byte);
+ break;
+ default: IllegalOptionDynrec("dyn_dop_byte_gencall");
+ }
+}
+
+static void dyn_dop_word_gencall(DualOps op,bool dword) {
+ if (dword) {
+ switch (op) {
+ case DOP_ADD:
+ InvalidateFlags((void*)&dynrec_add_dword_simple,t_ADDd);
+ gen_call_function_raw((void*)&dynrec_add_dword);
+ break;
+ case DOP_ADC:
+ AcquireFlags(FLAG_CF);
+ InvalidateFlagsPartially((void*)&dynrec_adc_dword_simple,t_ADCd);
+ gen_call_function_raw((void*)&dynrec_adc_dword);
+ break;
+ case DOP_SUB:
+ InvalidateFlags((void*)&dynrec_sub_dword_simple,t_SUBd);
+ gen_call_function_raw((void*)&dynrec_sub_dword);
+ break;
+ case DOP_SBB:
+ AcquireFlags(FLAG_CF);
+ InvalidateFlagsPartially((void*)&dynrec_sbb_dword_simple,t_SBBd);
+ gen_call_function_raw((void*)&dynrec_sbb_dword);
+ break;
+ case DOP_CMP:
+ InvalidateFlags((void*)&dynrec_cmp_dword_simple,t_CMPd);
+ gen_call_function_raw((void*)&dynrec_cmp_dword);
+ break;
+ case DOP_XOR:
+ InvalidateFlags((void*)&dynrec_xor_dword_simple,t_XORd);
+ gen_call_function_raw((void*)&dynrec_xor_dword);
+ break;
+ case DOP_AND:
+ InvalidateFlags((void*)&dynrec_and_dword_simple,t_ANDd);
+ gen_call_function_raw((void*)&dynrec_and_dword);
+ break;
+ case DOP_OR:
+ InvalidateFlags((void*)&dynrec_or_dword_simple,t_ORd);
+ gen_call_function_raw((void*)&dynrec_or_dword);
+ break;
+ case DOP_TEST:
+ InvalidateFlags((void*)&dynrec_test_dword_simple,t_TESTd);
+ gen_call_function_raw((void*)&dynrec_test_dword);
+ break;
+ default: IllegalOptionDynrec("dyn_dop_dword_gencall");
+ }
+ } else {
+ switch (op) {
+ case DOP_ADD:
+ InvalidateFlags((void*)&dynrec_add_word_simple,t_ADDw);
+ gen_call_function_raw((void*)&dynrec_add_word);
+ break;
+ case DOP_ADC:
+ AcquireFlags(FLAG_CF);
+ InvalidateFlagsPartially((void*)&dynrec_adc_word_simple,t_ADCw);
+ gen_call_function_raw((void*)&dynrec_adc_word);
+ break;
+ case DOP_SUB:
+ InvalidateFlags((void*)&dynrec_sub_word_simple,t_SUBw);
+ gen_call_function_raw((void*)&dynrec_sub_word);
+ break;
+ case DOP_SBB:
+ AcquireFlags(FLAG_CF);
+ InvalidateFlagsPartially((void*)&dynrec_sbb_word_simple,t_SBBw);
+ gen_call_function_raw((void*)&dynrec_sbb_word);
+ break;
+ case DOP_CMP:
+ InvalidateFlags((void*)&dynrec_cmp_word_simple,t_CMPw);
+ gen_call_function_raw((void*)&dynrec_cmp_word);
+ break;
+ case DOP_XOR:
+ InvalidateFlags((void*)&dynrec_xor_word_simple,t_XORw);
+ gen_call_function_raw((void*)&dynrec_xor_word);
+ break;
+ case DOP_AND:
+ InvalidateFlags((void*)&dynrec_and_word_simple,t_ANDw);
+ gen_call_function_raw((void*)&dynrec_and_word);
+ break;
+ case DOP_OR:
+ InvalidateFlags((void*)&dynrec_or_word_simple,t_ORw);
+ gen_call_function_raw((void*)&dynrec_or_word);
+ break;
+ case DOP_TEST:
+ InvalidateFlags((void*)&dynrec_test_word_simple,t_TESTw);
+ gen_call_function_raw((void*)&dynrec_test_word);
+ break;
+ default: IllegalOptionDynrec("dyn_dop_word_gencall");
+ }
+ }
+}
+
+
+static Bit8u DRC_CALL_CONV dynrec_inc_byte(Bit8u op) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_inc_byte(Bit8u op) {
+ LoadCF;
+ lf_var1b=op;
+ lf_resb=lf_var1b+1;
+ lflags.type=t_INCb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_inc_byte_simple(Bit8u op) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_inc_byte_simple(Bit8u op) {
+ return op+1;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_dec_byte(Bit8u op) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_dec_byte(Bit8u op) {
+ LoadCF;
+ lf_var1b=op;
+ lf_resb=lf_var1b-1;
+ lflags.type=t_DECb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_dec_byte_simple(Bit8u op) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_dec_byte_simple(Bit8u op) {
+ return op-1;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_not_byte(Bit8u op) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_not_byte(Bit8u op) {
+ return ~op;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_neg_byte(Bit8u op) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_neg_byte(Bit8u op) {
+ lf_var1b=op;
+ lf_resb=0-lf_var1b;
+ lflags.type=t_NEGb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_neg_byte_simple(Bit8u op) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_neg_byte_simple(Bit8u op) {
+ return 0-op;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_inc_word(Bit16u op) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_inc_word(Bit16u op) {
+ LoadCF;
+ lf_var1w=op;
+ lf_resw=lf_var1w+1;
+ lflags.type=t_INCw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_inc_word_simple(Bit16u op) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_inc_word_simple(Bit16u op) {
+ return op+1;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_dec_word(Bit16u op) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_dec_word(Bit16u op) {
+ LoadCF;
+ lf_var1w=op;
+ lf_resw=lf_var1w-1;
+ lflags.type=t_DECw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_dec_word_simple(Bit16u op) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_dec_word_simple(Bit16u op) {
+ return op-1;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_not_word(Bit16u op) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_not_word(Bit16u op) {
+ return ~op;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_neg_word(Bit16u op) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_neg_word(Bit16u op) {
+ lf_var1w=op;
+ lf_resw=0-lf_var1w;
+ lflags.type=t_NEGw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_neg_word_simple(Bit16u op) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_neg_word_simple(Bit16u op) {
+ return 0-op;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_inc_dword(Bit32u op) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_inc_dword(Bit32u op) {
+ LoadCF;
+ lf_var1d=op;
+ lf_resd=lf_var1d+1;
+ lflags.type=t_INCd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_inc_dword_simple(Bit32u op) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_inc_dword_simple(Bit32u op) {
+ return op+1;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_dec_dword(Bit32u op) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_dec_dword(Bit32u op) {
+ LoadCF;
+ lf_var1d=op;
+ lf_resd=lf_var1d-1;
+ lflags.type=t_DECd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_dec_dword_simple(Bit32u op) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_dec_dword_simple(Bit32u op) {
+ return op-1;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_not_dword(Bit32u op) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_not_dword(Bit32u op) {
+ return ~op;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_neg_dword(Bit32u op) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_neg_dword(Bit32u op) {
+ lf_var1d=op;
+ lf_resd=0-lf_var1d;
+ lflags.type=t_NEGd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_neg_dword_simple(Bit32u op) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_neg_dword_simple(Bit32u op) {
+ return 0-op;
+}
+
+
+static void dyn_sop_byte_gencall(SingleOps op) {
+ switch (op) {
+ case SOP_INC:
+ InvalidateFlagsPartially((void*)&dynrec_inc_byte_simple,t_INCb);
+ gen_call_function_raw((void*)&dynrec_inc_byte);
+ break;
+ case SOP_DEC:
+ InvalidateFlagsPartially((void*)&dynrec_dec_byte_simple,t_DECb);
+ gen_call_function_raw((void*)&dynrec_dec_byte);
+ break;
+ case SOP_NOT:
+ gen_call_function_raw((void*)&dynrec_not_byte);
+ break;
+ case SOP_NEG:
+ InvalidateFlags((void*)&dynrec_neg_byte_simple,t_NEGb);
+ gen_call_function_raw((void*)&dynrec_neg_byte);
+ break;
+ default: IllegalOptionDynrec("dyn_sop_byte_gencall");
+ }
+}
+
+static void dyn_sop_word_gencall(SingleOps op,bool dword) {
+ if (dword) {
+ switch (op) {
+ case SOP_INC:
+ InvalidateFlagsPartially((void*)&dynrec_inc_dword_simple,t_INCd);
+ gen_call_function_raw((void*)&dynrec_inc_dword);
+ break;
+ case SOP_DEC:
+ InvalidateFlagsPartially((void*)&dynrec_dec_dword_simple,t_DECd);
+ gen_call_function_raw((void*)&dynrec_dec_dword);
+ break;
+ case SOP_NOT:
+ gen_call_function_raw((void*)&dynrec_not_dword);
+ break;
+ case SOP_NEG:
+ InvalidateFlags((void*)&dynrec_neg_dword_simple,t_NEGd);
+ gen_call_function_raw((void*)&dynrec_neg_dword);
+ break;
+ default: IllegalOptionDynrec("dyn_sop_dword_gencall");
+ }
+ } else {
+ switch (op) {
+ case SOP_INC:
+ InvalidateFlagsPartially((void*)&dynrec_inc_word_simple,t_INCw);
+ gen_call_function_raw((void*)&dynrec_inc_word);
+ break;
+ case SOP_DEC:
+ InvalidateFlagsPartially((void*)&dynrec_dec_word_simple,t_DECw);
+ gen_call_function_raw((void*)&dynrec_dec_word);
+ break;
+ case SOP_NOT:
+ gen_call_function_raw((void*)&dynrec_not_word);
+ break;
+ case SOP_NEG:
+ InvalidateFlags((void*)&dynrec_neg_word_simple,t_NEGw);
+ gen_call_function_raw((void*)&dynrec_neg_word);
+ break;
+ default: IllegalOptionDynrec("dyn_sop_word_gencall");
+ }
+ }
+}
+
+
+static Bit8u DRC_CALL_CONV dynrec_rol_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_rol_byte(Bit8u op1,Bit8u op2) {
+ if (!(op2&0x7)) {
+ if (op2&0x18) {
+ FillFlagsNoCFOF();
+ SETFLAGBIT(CF,op1 & 1);
+ SETFLAGBIT(OF,(op1 & 1) ^ (op1 >> 7));
+ }
+ return op1;
+ }
+ FillFlagsNoCFOF();
+ lf_var1b=op1;
+ lf_var2b=op2&0x07;
+ lf_resb=(lf_var1b << lf_var2b) | (lf_var1b >> (8-lf_var2b));
+ SETFLAGBIT(CF,lf_resb & 1);
+ SETFLAGBIT(OF,(lf_resb & 1) ^ (lf_resb >> 7));
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_rol_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_rol_byte_simple(Bit8u op1,Bit8u op2) {
+ if (!(op2&0x7)) return op1;
+ return (op1 << (op2&0x07)) | (op1 >> (8-(op2&0x07)));
+}
+
+static Bit8u DRC_CALL_CONV dynrec_ror_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_ror_byte(Bit8u op1,Bit8u op2) {
+ if (!(op2&0x7)) {
+ if (op2&0x18) {
+ FillFlagsNoCFOF();
+ SETFLAGBIT(CF,op1>>7);
+ SETFLAGBIT(OF,(op1>>7) ^ ((op1>>6) & 1));
+ }
+ return op1;
+ }
+ FillFlagsNoCFOF();
+ lf_var1b=op1;
+ lf_var2b=op2&0x07;
+ lf_resb=(lf_var1b >> lf_var2b) | (lf_var1b << (8-lf_var2b));
+ SETFLAGBIT(CF,lf_resb & 0x80);
+ SETFLAGBIT(OF,(lf_resb ^ (lf_resb<<1)) & 0x80);
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_ror_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_ror_byte_simple(Bit8u op1,Bit8u op2) {
+ if (!(op2&0x7)) return op1;
+ return (op1 >> (op2&0x07)) | (op1 << (8-(op2&0x07)));
+}
+
+static Bit8u DRC_CALL_CONV dynrec_rcl_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_rcl_byte(Bit8u op1,Bit8u op2) {
+ if (op2%9) {
+ Bit8u cf=(Bit8u)FillFlags()&0x1;
+ lf_var1b=op1;
+ lf_var2b=op2%9;
+ lf_resb=(lf_var1b << lf_var2b) | (cf << (lf_var2b-1)) | (lf_var1b >> (9-lf_var2b));
+ SETFLAGBIT(CF,((lf_var1b >> (8-lf_var2b)) & 1));
+ SETFLAGBIT(OF,(reg_flags & 1) ^ (lf_resb >> 7));
+ return lf_resb;
+ } else return op1;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_rcr_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_rcr_byte(Bit8u op1,Bit8u op2) {
+ if (op2%9) {
+ Bit8u cf=(Bit8u)FillFlags()&0x1;
+ lf_var1b=op1;
+ lf_var2b=op2%9;
+ lf_resb=(lf_var1b >> lf_var2b) | (cf << (8-lf_var2b)) | (lf_var1b << (9-lf_var2b)); \
+ SETFLAGBIT(CF,(lf_var1b >> (lf_var2b - 1)) & 1);
+ SETFLAGBIT(OF,(lf_resb ^ (lf_resb<<1)) & 0x80);
+ return lf_resb;
+ } else return op1;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_shl_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_shl_byte(Bit8u op1,Bit8u op2) {
+ if (!op2) return op1;
+ lf_var1b=op1;
+ lf_var2b=op2;
+ lf_resb=lf_var1b << lf_var2b;
+ lflags.type=t_SHLb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_shl_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_shl_byte_simple(Bit8u op1,Bit8u op2) {
+ if (!op2) return op1;
+ return op1 << op2;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_shr_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_shr_byte(Bit8u op1,Bit8u op2) {
+ if (!op2) return op1;
+ lf_var1b=op1;
+ lf_var2b=op2;
+ lf_resb=lf_var1b >> lf_var2b;
+ lflags.type=t_SHRb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_shr_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_shr_byte_simple(Bit8u op1,Bit8u op2) {
+ if (!op2) return op1;
+ return op1 >> op2;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_sar_byte(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_sar_byte(Bit8u op1,Bit8u op2) {
+ if (!op2) return op1;
+ lf_var1b=op1;
+ lf_var2b=op2;
+ if (lf_var2b>8) lf_var2b=8;
+ if (lf_var1b & 0x80) {
+ lf_resb=(lf_var1b >> lf_var2b)| (0xff << (8 - lf_var2b));
+ } else {
+ lf_resb=lf_var1b >> lf_var2b;
+ }
+ lflags.type=t_SARb;
+ return lf_resb;
+}
+
+static Bit8u DRC_CALL_CONV dynrec_sar_byte_simple(Bit8u op1,Bit8u op2) DRC_FC;
+static Bit8u DRC_CALL_CONV dynrec_sar_byte_simple(Bit8u op1,Bit8u op2) {
+ if (!op2) return op1;
+ if (op2>8) op2=8;
+ if (op1 & 0x80) return (op1 >> op2) | (0xff << (8 - op2));
+ else return op1 >> op2;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_rol_word(Bit16u op1,Bit8u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_rol_word(Bit16u op1,Bit8u op2) {
+ if (!(op2&0xf)) {
+ if (op2&0x10) {
+ FillFlagsNoCFOF();
+ SETFLAGBIT(CF,op1 & 1);
+ SETFLAGBIT(OF,(op1 & 1) ^ (op1 >> 15));
+ }
+ return op1;
+ }
+ FillFlagsNoCFOF();
+ lf_var1w=op1;
+ lf_var2b=op2&0xf;
+ lf_resw=(lf_var1w << lf_var2b) | (lf_var1w >> (16-lf_var2b));
+ SETFLAGBIT(CF,lf_resw & 1);
+ SETFLAGBIT(OF,(lf_resw & 1) ^ (lf_resw >> 15));
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_rol_word_simple(Bit16u op1,Bit8u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_rol_word_simple(Bit16u op1,Bit8u op2) {
+ if (!(op2&0xf)) return op1;
+ return (op1 << (op2&0xf)) | (op1 >> (16-(op2&0xf)));
+}
+
+static Bit16u DRC_CALL_CONV dynrec_ror_word(Bit16u op1,Bit8u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_ror_word(Bit16u op1,Bit8u op2) {
+ if (!(op2&0xf)) {
+ if (op2&0x10) {
+ FillFlagsNoCFOF();
+ SETFLAGBIT(CF,op1>>15);
+ SETFLAGBIT(OF,(op1>>15) ^ ((op1>>14) & 1));
+ }
+ return op1;
+ }
+ FillFlagsNoCFOF();
+ lf_var1w=op1;
+ lf_var2b=op2&0xf;
+ lf_resw=(lf_var1w >> lf_var2b) | (lf_var1w << (16-lf_var2b));
+ SETFLAGBIT(CF,lf_resw & 0x8000);
+ SETFLAGBIT(OF,(lf_resw ^ (lf_resw<<1)) & 0x8000);
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_ror_word_simple(Bit16u op1,Bit8u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_ror_word_simple(Bit16u op1,Bit8u op2) {
+ if (!(op2&0xf)) return op1;
+ return (op1 >> (op2&0xf)) | (op1 << (16-(op2&0xf)));
+}
+
+static Bit16u DRC_CALL_CONV dynrec_rcl_word(Bit16u op1,Bit8u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_rcl_word(Bit16u op1,Bit8u op2) {
+ if (op2%17) {
+ Bit16u cf=(Bit16u)FillFlags()&0x1;
+ lf_var1w=op1;
+ lf_var2b=op2%17;
+ lf_resw=(lf_var1w << lf_var2b) | (cf << (lf_var2b-1)) | (lf_var1w >> (17-lf_var2b));
+ SETFLAGBIT(CF,((lf_var1w >> (16-lf_var2b)) & 1));
+ SETFLAGBIT(OF,(reg_flags & 1) ^ (lf_resw >> 15));
+ return lf_resw;
+ } else return op1;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_rcr_word(Bit16u op1,Bit8u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_rcr_word(Bit16u op1,Bit8u op2) {
+ if (op2%17) {
+ Bit16u cf=(Bit16u)FillFlags()&0x1;
+ lf_var1w=op1;
+ lf_var2b=op2%17;
+ lf_resw=(lf_var1w >> lf_var2b) | (cf << (16-lf_var2b)) | (lf_var1w << (17-lf_var2b));
+ SETFLAGBIT(CF,(lf_var1w >> (lf_var2b - 1)) & 1);
+ SETFLAGBIT(OF,(lf_resw ^ (lf_resw<<1)) & 0x8000);
+ return lf_resw;
+ } else return op1;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_shl_word(Bit16u op1,Bit8u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_shl_word(Bit16u op1,Bit8u op2) {
+ if (!op2) return op1;
+ lf_var1w=op1;
+ lf_var2b=op2;
+ lf_resw=lf_var1w << lf_var2b;
+ lflags.type=t_SHLw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_shl_word_simple(Bit16u op1,Bit8u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_shl_word_simple(Bit16u op1,Bit8u op2) {
+ if (!op2) return op1;
+ return op1 << op2;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_shr_word(Bit16u op1,Bit8u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_shr_word(Bit16u op1,Bit8u op2) {
+ if (!op2) return op1;
+ lf_var1w=op1;
+ lf_var2b=op2;
+ lf_resw=lf_var1w >> lf_var2b;
+ lflags.type=t_SHRw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_shr_word_simple(Bit16u op1,Bit8u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_shr_word_simple(Bit16u op1,Bit8u op2) {
+ if (!op2) return op1;
+ return op1 >> op2;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_sar_word(Bit16u op1,Bit8u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_sar_word(Bit16u op1,Bit8u op2) {
+ if (!op2) return op1;
+ lf_var1w=op1;
+ lf_var2b=op2;
+ if (lf_var2b>16) lf_var2b=16;
+ if (lf_var1w & 0x8000) {
+ lf_resw=(lf_var1w >> lf_var2b) | (0xffff << (16 - lf_var2b));
+ } else {
+ lf_resw=lf_var1w >> lf_var2b;
+ }
+ lflags.type=t_SARw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_sar_word_simple(Bit16u op1,Bit8u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_sar_word_simple(Bit16u op1,Bit8u op2) {
+ if (!op2) return op1;
+ if (op2>16) op2=16;
+ if (op1 & 0x8000) return (op1 >> op2) | (0xffff << (16 - op2));
+ else return op1 >> op2;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_rol_dword(Bit32u op1,Bit8u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_rol_dword(Bit32u op1,Bit8u op2) {
+ if (!op2) return op1;
+ FillFlagsNoCFOF();
+ lf_var1d=op1;
+ lf_var2b=op2;
+ lf_resd=(lf_var1d << lf_var2b) | (lf_var1d >> (32-lf_var2b));
+ SETFLAGBIT(CF,lf_resd & 1);
+ SETFLAGBIT(OF,(lf_resd & 1) ^ (lf_resd >> 31));
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_rol_dword_simple(Bit32u op1,Bit8u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_rol_dword_simple(Bit32u op1,Bit8u op2) {
+ if (!op2) return op1;
+ return (op1 << op2) | (op1 >> (32-op2));
+}
+
+static Bit32u DRC_CALL_CONV dynrec_ror_dword(Bit32u op1,Bit8u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_ror_dword(Bit32u op1,Bit8u op2) {
+ if (!op2) return op1;
+ FillFlagsNoCFOF();
+ lf_var1d=op1;
+ lf_var2b=op2;
+ lf_resd=(lf_var1d >> lf_var2b) | (lf_var1d << (32-lf_var2b));
+ SETFLAGBIT(CF,lf_resd & 0x80000000);
+ SETFLAGBIT(OF,(lf_resd ^ (lf_resd<<1)) & 0x80000000);
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_ror_dword_simple(Bit32u op1,Bit8u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_ror_dword_simple(Bit32u op1,Bit8u op2) {
+ if (!op2) return op1;
+ return (op1 >> op2) | (op1 << (32-op2));
+}
+
+static Bit32u DRC_CALL_CONV dynrec_rcl_dword(Bit32u op1,Bit8u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_rcl_dword(Bit32u op1,Bit8u op2) {
+ if (!op2) return op1;
+ Bit32u cf=(Bit32u)FillFlags()&0x1;
+ lf_var1d=op1;
+ lf_var2b=op2;
+ if (lf_var2b==1) {
+ lf_resd=(lf_var1d << 1) | cf;
+ } else {
+ lf_resd=(lf_var1d << lf_var2b) | (cf << (lf_var2b-1)) | (lf_var1d >> (33-lf_var2b));
+ }
+ SETFLAGBIT(CF,((lf_var1d >> (32-lf_var2b)) & 1));
+ SETFLAGBIT(OF,(reg_flags & 1) ^ (lf_resd >> 31));
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_rcr_dword(Bit32u op1,Bit8u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_rcr_dword(Bit32u op1,Bit8u op2) {
+ if (op2) {
+ Bit32u cf=(Bit32u)FillFlags()&0x1;
+ lf_var1d=op1;
+ lf_var2b=op2;
+ if (lf_var2b==1) {
+ lf_resd=lf_var1d >> 1 | cf << 31;
+ } else {
+ lf_resd=(lf_var1d >> lf_var2b) | (cf << (32-lf_var2b)) | (lf_var1d << (33-lf_var2b));
+ }
+ SETFLAGBIT(CF,(lf_var1d >> (lf_var2b - 1)) & 1);
+ SETFLAGBIT(OF,(lf_resd ^ (lf_resd<<1)) & 0x80000000);
+ return lf_resd;
+ } else return op1;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_shl_dword(Bit32u op1,Bit8u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_shl_dword(Bit32u op1,Bit8u op2) {
+ if (!op2) return op1;
+ lf_var1d=op1;
+ lf_var2b=op2;
+ lf_resd=lf_var1d << lf_var2b;
+ lflags.type=t_SHLd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_shl_dword_simple(Bit32u op1,Bit8u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_shl_dword_simple(Bit32u op1,Bit8u op2) {
+ if (!op2) return op1;
+ return op1 << op2;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_shr_dword(Bit32u op1,Bit8u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_shr_dword(Bit32u op1,Bit8u op2) {
+ if (!op2) return op1;
+ lf_var1d=op1;
+ lf_var2b=op2;
+ lf_resd=lf_var1d >> lf_var2b;
+ lflags.type=t_SHRd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_shr_dword_simple(Bit32u op1,Bit8u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_shr_dword_simple(Bit32u op1,Bit8u op2) {
+ if (!op2) return op1;
+ return op1 >> op2;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_sar_dword(Bit32u op1,Bit8u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_sar_dword(Bit32u op1,Bit8u op2) {
+ if (!op2) return op1;
+ lf_var2b=op2;
+ lf_var1d=op1;
+ if (lf_var1d & 0x80000000) {
+ lf_resd=(lf_var1d >> lf_var2b) | (0xffffffff << (32 - lf_var2b));
+ } else {
+ lf_resd=lf_var1d >> lf_var2b;
+ }
+ lflags.type=t_SARd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_sar_dword_simple(Bit32u op1,Bit8u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_sar_dword_simple(Bit32u op1,Bit8u op2) {
+ if (!op2) return op1;
+ if (op1 & 0x80000000) return (op1 >> op2) | (0xffffffff << (32 - op2));
+ else return op1 >> op2;
+}
+
+static void dyn_shift_byte_gencall(ShiftOps op) {
+ switch (op) {
+ case SHIFT_ROL:
+ InvalidateFlagsPartially((void*)&dynrec_rol_byte_simple,t_ROLb);
+ gen_call_function_raw((void*)&dynrec_rol_byte);
+ break;
+ case SHIFT_ROR:
+ InvalidateFlagsPartially((void*)&dynrec_ror_byte_simple,t_RORb);
+ gen_call_function_raw((void*)&dynrec_ror_byte);
+ break;
+ case SHIFT_RCL:
+ AcquireFlags(FLAG_CF);
+ gen_call_function_raw((void*)&dynrec_rcl_byte);
+ break;
+ case SHIFT_RCR:
+ AcquireFlags(FLAG_CF);
+ gen_call_function_raw((void*)&dynrec_rcr_byte);
+ break;
+ case SHIFT_SHL:
+ case SHIFT_SAL:
+ InvalidateFlagsPartially((void*)&dynrec_shl_byte_simple,t_SHLb);
+ gen_call_function_raw((void*)&dynrec_shl_byte);
+ break;
+ case SHIFT_SHR:
+ InvalidateFlagsPartially((void*)&dynrec_shr_byte_simple,t_SHRb);
+ gen_call_function_raw((void*)&dynrec_shr_byte);
+ break;
+ case SHIFT_SAR:
+ InvalidateFlagsPartially((void*)&dynrec_sar_byte_simple,t_SARb);
+ gen_call_function_raw((void*)&dynrec_sar_byte);
+ break;
+ default: IllegalOptionDynrec("dyn_shift_byte_gencall");
+ }
+}
+
+static void dyn_shift_word_gencall(ShiftOps op,bool dword) {
+ if (dword) {
+ switch (op) {
+ case SHIFT_ROL:
+ InvalidateFlagsPartially((void*)&dynrec_rol_dword_simple,t_ROLd);
+ gen_call_function_raw((void*)&dynrec_rol_dword);
+ break;
+ case SHIFT_ROR:
+ InvalidateFlagsPartially((void*)&dynrec_ror_dword_simple,t_RORd);
+ gen_call_function_raw((void*)&dynrec_ror_dword);
+ break;
+ case SHIFT_RCL:
+ AcquireFlags(FLAG_CF);
+ gen_call_function_raw((void*)&dynrec_rcl_dword);
+ break;
+ case SHIFT_RCR:
+ AcquireFlags(FLAG_CF);
+ gen_call_function_raw((void*)&dynrec_rcr_dword);
+ break;
+ case SHIFT_SHL:
+ case SHIFT_SAL:
+ InvalidateFlagsPartially((void*)&dynrec_shl_dword_simple,t_SHLd);
+ gen_call_function_raw((void*)&dynrec_shl_dword);
+ break;
+ case SHIFT_SHR:
+ InvalidateFlagsPartially((void*)&dynrec_shr_dword_simple,t_SHRd);
+ gen_call_function_raw((void*)&dynrec_shr_dword);
+ break;
+ case SHIFT_SAR:
+ InvalidateFlagsPartially((void*)&dynrec_sar_dword_simple,t_SARd);
+ gen_call_function_raw((void*)&dynrec_sar_dword);
+ break;
+ default: IllegalOptionDynrec("dyn_shift_dword_gencall");
+ }
+ } else {
+ switch (op) {
+ case SHIFT_ROL:
+ InvalidateFlagsPartially((void*)&dynrec_rol_word_simple,t_ROLw);
+ gen_call_function_raw((void*)&dynrec_rol_word);
+ break;
+ case SHIFT_ROR:
+ InvalidateFlagsPartially((void*)&dynrec_ror_word_simple,t_RORw);
+ gen_call_function_raw((void*)&dynrec_ror_word);
+ break;
+ case SHIFT_RCL:
+ AcquireFlags(FLAG_CF);
+ gen_call_function_raw((void*)&dynrec_rcl_word);
+ break;
+ case SHIFT_RCR:
+ AcquireFlags(FLAG_CF);
+ gen_call_function_raw((void*)&dynrec_rcr_word);
+ break;
+ case SHIFT_SHL:
+ case SHIFT_SAL:
+ InvalidateFlagsPartially((void*)&dynrec_shl_word_simple,t_SHLw);
+ gen_call_function_raw((void*)&dynrec_shl_word);
+ break;
+ case SHIFT_SHR:
+ InvalidateFlagsPartially((void*)&dynrec_shr_word_simple,t_SHRw);
+ gen_call_function_raw((void*)&dynrec_shr_word);
+ break;
+ case SHIFT_SAR:
+ InvalidateFlagsPartially((void*)&dynrec_sar_word_simple,t_SARw);
+ gen_call_function_raw((void*)&dynrec_sar_word);
+ break;
+ default: IllegalOptionDynrec("dyn_shift_word_gencall");
+ }
+ }
+}
+
+static Bit16u DRC_CALL_CONV dynrec_dshl_word(Bit16u op1,Bit16u op2,Bit8u op3) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_dshl_word(Bit16u op1,Bit16u op2,Bit8u op3) {
+ Bit8u val=op3 & 0x1f;
+ if (!val) return op1;
+ lf_var2b=val;
+ lf_var1d=(op1<<16)|op2;
+ Bit32u tempd=lf_var1d << lf_var2b;
+ if (lf_var2b>16) tempd |= (op2 << (lf_var2b - 16));
+ lf_resw=(Bit16u)(tempd >> 16);
+ lflags.type=t_DSHLw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_dshl_word_simple(Bit16u op1,Bit16u op2,Bit8u op3) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_dshl_word_simple(Bit16u op1,Bit16u op2,Bit8u op3) {
+ Bit8u val=op3 & 0x1f;
+ if (!val) return op1;
+ Bit32u tempd=(Bit32u)((((Bit32u)op1)<<16)|op2) << val;
+ if (val>16) tempd |= (op2 << (val - 16));
+ return (Bit16u)(tempd >> 16);
+}
+
+static Bit32u DRC_CALL_CONV dynrec_dshl_dword(Bit32u op1,Bit32u op2,Bit8u op3) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_dshl_dword(Bit32u op1,Bit32u op2,Bit8u op3) {
+ Bit8u val=op3 & 0x1f;
+ if (!val) return op1;
+ lf_var2b=val;
+ lf_var1d=op1;
+ lf_resd=(lf_var1d << lf_var2b) | (op2 >> (32-lf_var2b));
+ lflags.type=t_DSHLd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_dshl_dword_simple(Bit32u op1,Bit32u op2,Bit8u op3) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_dshl_dword_simple(Bit32u op1,Bit32u op2,Bit8u op3) {
+ Bit8u val=op3 & 0x1f;
+ if (!val) return op1;
+ return (op1 << val) | (op2 >> (32-val));
+}
+
+static Bit16u DRC_CALL_CONV dynrec_dshr_word(Bit16u op1,Bit16u op2,Bit8u op3) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_dshr_word(Bit16u op1,Bit16u op2,Bit8u op3) {
+ Bit8u val=op3 & 0x1f;
+ if (!val) return op1;
+ lf_var2b=val;
+ lf_var1d=(op2<<16)|op1;
+ Bit32u tempd=lf_var1d >> lf_var2b;
+ if (lf_var2b>16) tempd |= (op2 << (32-lf_var2b ));
+ lf_resw=(Bit16u)(tempd);
+ lflags.type=t_DSHRw;
+ return lf_resw;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_dshr_word_simple(Bit16u op1,Bit16u op2,Bit8u op3) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_dshr_word_simple(Bit16u op1,Bit16u op2,Bit8u op3) {
+ Bit8u val=op3 & 0x1f;
+ if (!val) return op1;
+ Bit32u tempd=(Bit32u)((((Bit32u)op2)<<16)|op1) >> val;
+ if (val>16) tempd |= (op2 << (32-val));
+ return (Bit16u)(tempd);
+}
+
+static Bit32u DRC_CALL_CONV dynrec_dshr_dword(Bit32u op1,Bit32u op2,Bit8u op3) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_dshr_dword(Bit32u op1,Bit32u op2,Bit8u op3) {
+ Bit8u val=op3 & 0x1f;
+ if (!val) return op1;
+ lf_var2b=val;
+ lf_var1d=op1;
+ lf_resd=(lf_var1d >> lf_var2b) | (op2 << (32-lf_var2b));
+ lflags.type=t_DSHRd;
+ return lf_resd;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_dshr_dword_simple(Bit32u op1,Bit32u op2,Bit8u op3) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_dshr_dword_simple(Bit32u op1,Bit32u op2,Bit8u op3) {
+ Bit8u val=op3 & 0x1f;
+ if (!val) return op1;
+ return (op1 >> val) | (op2 << (32-val));
+}
+
+static void dyn_dpshift_word_gencall(bool left) {
+ if (left) {
+ DRC_PTR_SIZE_IM proc_addr=gen_call_function_R3((void*)&dynrec_dshl_word,FC_OP3);
+ InvalidateFlagsPartially((void*)&dynrec_dshl_word_simple,proc_addr,t_DSHLw);
+ } else {
+ DRC_PTR_SIZE_IM proc_addr=gen_call_function_R3((void*)&dynrec_dshr_word,FC_OP3);
+ InvalidateFlagsPartially((void*)&dynrec_dshr_word_simple,proc_addr,t_DSHRw);
+ }
+}
+
+static void dyn_dpshift_dword_gencall(bool left) {
+ if (left) {
+ DRC_PTR_SIZE_IM proc_addr=gen_call_function_R3((void*)&dynrec_dshl_dword,FC_OP3);
+ InvalidateFlagsPartially((void*)&dynrec_dshl_dword_simple,proc_addr,t_DSHLd);
+ } else {
+ DRC_PTR_SIZE_IM proc_addr=gen_call_function_R3((void*)&dynrec_dshr_dword,FC_OP3);
+ InvalidateFlagsPartially((void*)&dynrec_dshr_dword_simple,proc_addr,t_DSHRd);
+ }
+}
+
+
+
+static Bit32u DRC_CALL_CONV dynrec_get_of(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_of(void) { return TFLG_O; }
+static Bit32u DRC_CALL_CONV dynrec_get_nof(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_nof(void) { return TFLG_NO; }
+static Bit32u DRC_CALL_CONV dynrec_get_cf(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_cf(void) { return TFLG_B; }
+static Bit32u DRC_CALL_CONV dynrec_get_ncf(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_ncf(void) { return TFLG_NB; }
+static Bit32u DRC_CALL_CONV dynrec_get_zf(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_zf(void) { return TFLG_Z; }
+static Bit32u DRC_CALL_CONV dynrec_get_nzf(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_nzf(void) { return TFLG_NZ; }
+static Bit32u DRC_CALL_CONV dynrec_get_sf(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_sf(void) { return TFLG_S; }
+static Bit32u DRC_CALL_CONV dynrec_get_nsf(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_nsf(void) { return TFLG_NS; }
+static Bit32u DRC_CALL_CONV dynrec_get_pf(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_pf(void) { return TFLG_P; }
+static Bit32u DRC_CALL_CONV dynrec_get_npf(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_npf(void) { return TFLG_NP; }
+
+static Bit32u DRC_CALL_CONV dynrec_get_cf_or_zf(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_cf_or_zf(void) { return TFLG_BE; }
+static Bit32u DRC_CALL_CONV dynrec_get_ncf_and_nzf(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_ncf_and_nzf(void) { return TFLG_NBE; }
+static Bit32u DRC_CALL_CONV dynrec_get_sf_neq_of(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_sf_neq_of(void) { return TFLG_L; }
+static Bit32u DRC_CALL_CONV dynrec_get_sf_eq_of(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_sf_eq_of(void) { return TFLG_NL; }
+static Bit32u DRC_CALL_CONV dynrec_get_zf_or_sf_neq_of(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_zf_or_sf_neq_of(void) { return TFLG_LE; }
+static Bit32u DRC_CALL_CONV dynrec_get_nzf_and_sf_eq_of(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_get_nzf_and_sf_eq_of(void) { return TFLG_NLE; }
+
+
+static void dyn_branchflag_to_reg(BranchTypes btype) {
+ switch (btype) {
+ case BR_O:gen_call_function_raw((void*)&dynrec_get_of);break;
+ case BR_NO:gen_call_function_raw((void*)&dynrec_get_nof);break;
+ case BR_B:gen_call_function_raw((void*)&dynrec_get_cf);break;
+ case BR_NB:gen_call_function_raw((void*)&dynrec_get_ncf);break;
+ case BR_Z:gen_call_function_raw((void*)&dynrec_get_zf);break;
+ case BR_NZ:gen_call_function_raw((void*)&dynrec_get_nzf);break;
+ case BR_BE:gen_call_function_raw((void*)&dynrec_get_cf_or_zf);break;
+ case BR_NBE:gen_call_function_raw((void*)&dynrec_get_ncf_and_nzf);break;
+
+ case BR_S:gen_call_function_raw((void*)&dynrec_get_sf);break;
+ case BR_NS:gen_call_function_raw((void*)&dynrec_get_nsf);break;
+ case BR_P:gen_call_function_raw((void*)&dynrec_get_pf);break;
+ case BR_NP:gen_call_function_raw((void*)&dynrec_get_npf);break;
+ case BR_L:gen_call_function_raw((void*)&dynrec_get_sf_neq_of);break;
+ case BR_NL:gen_call_function_raw((void*)&dynrec_get_sf_eq_of);break;
+ case BR_LE:gen_call_function_raw((void*)&dynrec_get_zf_or_sf_neq_of);break;
+ case BR_NLE:gen_call_function_raw((void*)&dynrec_get_nzf_and_sf_eq_of);break;
+ }
+}
+
+
+static void DRC_CALL_CONV dynrec_mul_byte(Bit8u op) DRC_FC;
+static void DRC_CALL_CONV dynrec_mul_byte(Bit8u op) {
+ FillFlagsNoCFOF();
+ reg_ax=reg_al*op;
+ SETFLAGBIT(ZF,reg_al == 0);
+ if (reg_ax & 0xff00) {
+ SETFLAGBIT(CF,true);
+ SETFLAGBIT(OF,true);
+ } else {
+ SETFLAGBIT(CF,false);
+ SETFLAGBIT(OF,false);
+ }
+}
+
+static void DRC_CALL_CONV dynrec_imul_byte(Bit8u op) DRC_FC;
+static void DRC_CALL_CONV dynrec_imul_byte(Bit8u op) {
+ FillFlagsNoCFOF();
+ reg_ax=((Bit8s)reg_al) * ((Bit8s)op);
+ if ((reg_ax & 0xff80)==0xff80 || (reg_ax & 0xff80)==0x0000) {
+ SETFLAGBIT(CF,false);
+ SETFLAGBIT(OF,false);
+ } else {
+ SETFLAGBIT(CF,true);
+ SETFLAGBIT(OF,true);
+ }
+}
+
+static void DRC_CALL_CONV dynrec_mul_word(Bit16u op) DRC_FC;
+static void DRC_CALL_CONV dynrec_mul_word(Bit16u op) {
+ FillFlagsNoCFOF();
+ Bitu tempu=(Bitu)reg_ax*(Bitu)op;
+ reg_ax=(Bit16u)(tempu);
+ reg_dx=(Bit16u)(tempu >> 16);
+ SETFLAGBIT(ZF,reg_ax == 0);
+ if (reg_dx) {
+ SETFLAGBIT(CF,true);
+ SETFLAGBIT(OF,true);
+ } else {
+ SETFLAGBIT(CF,false);
+ SETFLAGBIT(OF,false);
+ }
+}
+
+static void DRC_CALL_CONV dynrec_imul_word(Bit16u op) DRC_FC;
+static void DRC_CALL_CONV dynrec_imul_word(Bit16u op) {
+ FillFlagsNoCFOF();
+ Bits temps=((Bit16s)reg_ax)*((Bit16s)op);
+ reg_ax=(Bit16s)(temps);
+ reg_dx=(Bit16s)(temps >> 16);
+ if (((temps & 0xffff8000)==0xffff8000 || (temps & 0xffff8000)==0x0000)) {
+ SETFLAGBIT(CF,false);
+ SETFLAGBIT(OF,false);
+ } else {
+ SETFLAGBIT(CF,true);
+ SETFLAGBIT(OF,true);
+ }
+}
+
+static void DRC_CALL_CONV dynrec_mul_dword(Bit32u op) DRC_FC;
+static void DRC_CALL_CONV dynrec_mul_dword(Bit32u op) {
+ FillFlagsNoCFOF();
+ Bit64u tempu=(Bit64u)reg_eax*(Bit64u)op;
+ reg_eax=(Bit32u)(tempu);
+ reg_edx=(Bit32u)(tempu >> 32);
+ SETFLAGBIT(ZF,reg_eax == 0);
+ if (reg_edx) {
+ SETFLAGBIT(CF,true);
+ SETFLAGBIT(OF,true);
+ } else {
+ SETFLAGBIT(CF,false);
+ SETFLAGBIT(OF,false);
+ }
+}
+
+static void DRC_CALL_CONV dynrec_imul_dword(Bit32u op) DRC_FC;
+static void DRC_CALL_CONV dynrec_imul_dword(Bit32u op) {
+ FillFlagsNoCFOF();
+ Bit64s temps=((Bit64s)((Bit32s)reg_eax))*((Bit64s)((Bit32s)op));
+ reg_eax=(Bit32u)(temps);
+ reg_edx=(Bit32u)(temps >> 32);
+ if ((reg_edx==0xffffffff) && (reg_eax & 0x80000000) ) {
+ SETFLAGBIT(CF,false);
+ SETFLAGBIT(OF,false);
+ } else if ( (reg_edx==0x00000000) && (reg_eax< 0x80000000) ) {
+ SETFLAGBIT(CF,false);
+ SETFLAGBIT(OF,false);
+ } else {
+ SETFLAGBIT(CF,true);
+ SETFLAGBIT(OF,true);
+ }
+}
+
+
+static bool DRC_CALL_CONV dynrec_div_byte(Bit8u op) DRC_FC;
+static bool DRC_CALL_CONV dynrec_div_byte(Bit8u op) {
+ Bitu val=op;
+ if (val==0) return CPU_PrepareException(0,0);
+ Bitu quo=reg_ax / val;
+ Bit8u rem=(Bit8u)(reg_ax % val);
+ Bit8u quo8=(Bit8u)(quo&0xff);
+ if (quo>0xff) return CPU_PrepareException(0,0);
+ reg_ah=rem;
+ reg_al=quo8;
+ return false;
+}
+
+static bool DRC_CALL_CONV dynrec_idiv_byte(Bit8u op) DRC_FC;
+static bool DRC_CALL_CONV dynrec_idiv_byte(Bit8u op) {
+ Bits val=(Bit8s)op;
+ if (val==0) return CPU_PrepareException(0,0);
+ Bits quo=((Bit16s)reg_ax) / val;
+ Bit8s rem=(Bit8s)((Bit16s)reg_ax % val);
+ Bit8s quo8s=(Bit8s)(quo&0xff);
+ if (quo!=(Bit16s)quo8s) return CPU_PrepareException(0,0);
+ reg_ah=rem;
+ reg_al=quo8s;
+ return false;
+}
+
+static bool DRC_CALL_CONV dynrec_div_word(Bit16u op) DRC_FC;
+static bool DRC_CALL_CONV dynrec_div_word(Bit16u op) {
+ Bitu val=op;
+ if (val==0) return CPU_PrepareException(0,0);
+ Bitu num=((Bit32u)reg_dx<<16)|reg_ax;
+ Bitu quo=num/val;
+ Bit16u rem=(Bit16u)(num % val);
+ Bit16u quo16=(Bit16u)(quo&0xffff);
+ if (quo!=(Bit32u)quo16) return CPU_PrepareException(0,0);
+ reg_dx=rem;
+ reg_ax=quo16;
+ return false;
+}
+
+static bool DRC_CALL_CONV dynrec_idiv_word(Bit16u op) DRC_FC;
+static bool DRC_CALL_CONV dynrec_idiv_word(Bit16u op) {
+ Bits val=(Bit16s)op;
+ if (val==0) return CPU_PrepareException(0,0);
+ Bits num=(Bit32s)((reg_dx<<16)|reg_ax);
+ Bits quo=num/val;
+ Bit16s rem=(Bit16s)(num % val);
+ Bit16s quo16s=(Bit16s)quo;
+ if (quo!=(Bit32s)quo16s) return CPU_PrepareException(0,0);
+ reg_dx=rem;
+ reg_ax=quo16s;
+ return false;
+}
+
+static bool DRC_CALL_CONV dynrec_div_dword(Bit32u op) DRC_FC;
+static bool DRC_CALL_CONV dynrec_div_dword(Bit32u op) {
+ Bitu val=op;
+ if (val==0) return CPU_PrepareException(0,0);
+ Bit64u num=(((Bit64u)reg_edx)<<32)|reg_eax;
+ Bit64u quo=num/val;
+ Bit32u rem=(Bit32u)(num % val);
+ Bit32u quo32=(Bit32u)(quo&0xffffffff);
+ if (quo!=(Bit64u)quo32) return CPU_PrepareException(0,0);
+ reg_edx=rem;
+ reg_eax=quo32;
+ return false;
+}
+
+static bool DRC_CALL_CONV dynrec_idiv_dword(Bit32u op) DRC_FC;
+static bool DRC_CALL_CONV dynrec_idiv_dword(Bit32u op) {
+ Bits val=(Bit32s)op;
+ if (val==0) return CPU_PrepareException(0,0);
+ Bit64s num=(((Bit64u)reg_edx)<<32)|reg_eax;
+ Bit64s quo=num/val;
+ Bit32s rem=(Bit32s)(num % val);
+ Bit32s quo32s=(Bit32s)(quo&0xffffffff);
+ if (quo!=(Bit64s)quo32s) return CPU_PrepareException(0,0);
+ reg_edx=rem;
+ reg_eax=quo32s;
+ return false;
+}
+
+
+static Bit16u DRC_CALL_CONV dynrec_dimul_word(Bit16u op1,Bit16u op2) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_dimul_word(Bit16u op1,Bit16u op2) {
+ FillFlagsNoCFOF();
+ Bits res=((Bit16s)op1) * ((Bit16s)op2);
+ if ((res>-32768) && (res<32767)) {
+ SETFLAGBIT(CF,false);
+ SETFLAGBIT(OF,false);
+ } else {
+ SETFLAGBIT(CF,true);
+ SETFLAGBIT(OF,true);
+ }
+ return (Bit16u)(res & 0xffff);
+}
+
+static Bit32u DRC_CALL_CONV dynrec_dimul_dword(Bit32u op1,Bit32u op2) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_dimul_dword(Bit32u op1,Bit32u op2) {
+ FillFlagsNoCFOF();
+ Bit64s res=((Bit64s)((Bit32s)op1))*((Bit64s)((Bit32s)op2));
+ if ((res>-((Bit64s)(2147483647)+1)) && (res<(Bit64s)2147483647)) {
+ SETFLAGBIT(CF,false);
+ SETFLAGBIT(OF,false);
+ } else {
+ SETFLAGBIT(CF,true);
+ SETFLAGBIT(OF,true);
+ }
+ return (Bit32s)res;
+}
+
+
+
+static Bit16u DRC_CALL_CONV dynrec_cbw(Bit8u op) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_cbw(Bit8u op) {
+ return (Bit8s)op;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_cwde(Bit16u op) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_cwde(Bit16u op) {
+ return (Bit16s)op;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_cwd(Bit16u op) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_cwd(Bit16u op) {
+ if (op & 0x8000) return 0xffff;
+ else return 0;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_cdq(Bit32u op) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_cdq(Bit32u op) {
+ if (op & 0x80000000) return 0xffffffff;
+ else return 0;
+}
+
+
+static void DRC_CALL_CONV dynrec_sahf(Bit16u op) DRC_FC;
+static void DRC_CALL_CONV dynrec_sahf(Bit16u op) {
+ SETFLAGBIT(OF,get_OF());
+ lflags.type=t_UNKNOWN;
+ CPU_SetFlags(op>>8,FMASK_NORMAL & 0xff);
+}
+
+
+static void DRC_CALL_CONV dynrec_cmc(void) DRC_FC;
+static void DRC_CALL_CONV dynrec_cmc(void) {
+ FillFlags();
+ SETFLAGBIT(CF,!(reg_flags & FLAG_CF));
+}
+static void DRC_CALL_CONV dynrec_clc(void) DRC_FC;
+static void DRC_CALL_CONV dynrec_clc(void) {
+ FillFlags();
+ SETFLAGBIT(CF,false);
+}
+static void DRC_CALL_CONV dynrec_stc(void) DRC_FC;
+static void DRC_CALL_CONV dynrec_stc(void) {
+ FillFlags();
+ SETFLAGBIT(CF,true);
+}
+static void DRC_CALL_CONV dynrec_cld(void) DRC_FC;
+static void DRC_CALL_CONV dynrec_cld(void) {
+ SETFLAGBIT(DF,false);
+ cpu.direction=1;
+}
+static void DRC_CALL_CONV dynrec_std(void) DRC_FC;
+static void DRC_CALL_CONV dynrec_std(void) {
+ SETFLAGBIT(DF,true);
+ cpu.direction=-1;
+}
+
+
+static Bit16u DRC_CALL_CONV dynrec_movsb_word(Bit16u count,Bit16s add_index,PhysPt si_base,PhysPt di_base) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_movsb_word(Bit16u count,Bit16s add_index,PhysPt si_base,PhysPt di_base) {
+ Bit16u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=(Bit16u)(count-CPU_Cycles);
+ count=(Bit16u)CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ for (;count>0;count--) {
+ mem_writeb(di_base+reg_di,mem_readb(si_base+reg_si));
+ reg_si+=add_index;
+ reg_di+=add_index;
+ }
+ return count_left;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_movsb_dword(Bit32u count,Bit32s add_index,PhysPt si_base,PhysPt di_base) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_movsb_dword(Bit32u count,Bit32s add_index,PhysPt si_base,PhysPt di_base) {
+ Bit32u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=count-CPU_Cycles;
+ count=CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ for (;count>0;count--) {
+ mem_writeb(di_base+reg_edi,mem_readb(si_base+reg_esi));
+ reg_esi+=add_index;
+ reg_edi+=add_index;
+ }
+ return count_left;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_movsw_word(Bit16u count,Bit16s add_index,PhysPt si_base,PhysPt di_base) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_movsw_word(Bit16u count,Bit16s add_index,PhysPt si_base,PhysPt di_base) {
+ Bit16u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=(Bit16u)(count-CPU_Cycles);
+ count=(Bit16u)CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ add_index<<=1;
+ for (;count>0;count--) {
+ mem_writew(di_base+reg_di,mem_readw(si_base+reg_si));
+ reg_si+=add_index;
+ reg_di+=add_index;
+ }
+ return count_left;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_movsw_dword(Bit32u count,Bit32s add_index,PhysPt si_base,PhysPt di_base) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_movsw_dword(Bit32u count,Bit32s add_index,PhysPt si_base,PhysPt di_base) {
+ Bit32u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=count-CPU_Cycles;
+ count=CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ add_index<<=1;
+ for (;count>0;count--) {
+ mem_writew(di_base+reg_edi,mem_readw(si_base+reg_esi));
+ reg_esi+=add_index;
+ reg_edi+=add_index;
+ }
+ return count_left;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_movsd_word(Bit16u count,Bit16s add_index,PhysPt si_base,PhysPt di_base) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_movsd_word(Bit16u count,Bit16s add_index,PhysPt si_base,PhysPt di_base) {
+ Bit16u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=(Bit16u)(count-CPU_Cycles);
+ count=(Bit16u)CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ add_index<<=2;
+ for (;count>0;count--) {
+ mem_writed(di_base+reg_di,mem_readd(si_base+reg_si));
+ reg_si+=add_index;
+ reg_di+=add_index;
+ }
+ return count_left;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_movsd_dword(Bit32u count,Bit32s add_index,PhysPt si_base,PhysPt di_base) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_movsd_dword(Bit32u count,Bit32s add_index,PhysPt si_base,PhysPt di_base) {
+ Bit32u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=count-CPU_Cycles;
+ count=CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ add_index<<=2;
+ for (;count>0;count--) {
+ mem_writed(di_base+reg_edi,mem_readd(si_base+reg_esi));
+ reg_esi+=add_index;
+ reg_edi+=add_index;
+ }
+ return count_left;
+}
+
+
+static Bit16u DRC_CALL_CONV dynrec_lodsb_word(Bit16u count,Bit16s add_index,PhysPt si_base) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_lodsb_word(Bit16u count,Bit16s add_index,PhysPt si_base) {
+ Bit16u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=(Bit16u)(count-CPU_Cycles);
+ count=(Bit16u)CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ for (;count>0;count--) {
+ reg_al=mem_readb(si_base+reg_si);
+ reg_si+=add_index;
+ }
+ return count_left;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_lodsb_dword(Bit32u count,Bit32s add_index,PhysPt si_base) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_lodsb_dword(Bit32u count,Bit32s add_index,PhysPt si_base) {
+ Bit32u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=count-CPU_Cycles;
+ count=CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ for (;count>0;count--) {
+ reg_al=mem_readb(si_base+reg_esi);
+ reg_esi+=add_index;
+ }
+ return count_left;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_lodsw_word(Bit16u count,Bit16s add_index,PhysPt si_base) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_lodsw_word(Bit16u count,Bit16s add_index,PhysPt si_base) {
+ Bit16u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=(Bit16u)(count-CPU_Cycles);
+ count=(Bit16u)CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ add_index<<=1;
+ for (;count>0;count--) {
+ reg_ax=mem_readw(si_base+reg_si);
+ reg_si+=add_index;
+ }
+ return count_left;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_lodsw_dword(Bit32u count,Bit32s add_index,PhysPt si_base) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_lodsw_dword(Bit32u count,Bit32s add_index,PhysPt si_base) {
+ Bit32u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=count-CPU_Cycles;
+ count=CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ add_index<<=1;
+ for (;count>0;count--) {
+ reg_ax=mem_readw(si_base+reg_esi);
+ reg_esi+=add_index;
+ }
+ return count_left;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_lodsd_word(Bit16u count,Bit16s add_index,PhysPt si_base) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_lodsd_word(Bit16u count,Bit16s add_index,PhysPt si_base) {
+ Bit16u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=(Bit16u)(count-CPU_Cycles);
+ count=(Bit16u)CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ add_index<<=2;
+ for (;count>0;count--) {
+ reg_eax=mem_readd(si_base+reg_si);
+ reg_si+=add_index;
+ }
+ return count_left;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_lodsd_dword(Bit32u count,Bit32s add_index,PhysPt si_base) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_lodsd_dword(Bit32u count,Bit32s add_index,PhysPt si_base) {
+ Bit32u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=count-CPU_Cycles;
+ count=CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ add_index<<=2;
+ for (;count>0;count--) {
+ reg_eax=mem_readd(si_base+reg_esi);
+ reg_esi+=add_index;
+ }
+ return count_left;
+}
+
+
+static Bit16u DRC_CALL_CONV dynrec_stosb_word(Bit16u count,Bit16s add_index,PhysPt di_base) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_stosb_word(Bit16u count,Bit16s add_index,PhysPt di_base) {
+ Bit16u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=(Bit16u)(count-CPU_Cycles);
+ count=(Bit16u)CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ for (;count>0;count--) {
+ mem_writeb(di_base+reg_di,reg_al);
+ reg_di+=add_index;
+ }
+ return count_left;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_stosb_dword(Bit32u count,Bit32s add_index,PhysPt di_base) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_stosb_dword(Bit32u count,Bit32s add_index,PhysPt di_base) {
+ Bit32u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=count-CPU_Cycles;
+ count=CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ for (;count>0;count--) {
+ mem_writeb(di_base+reg_edi,reg_al);
+ reg_edi+=add_index;
+ }
+ return count_left;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_stosw_word(Bit16u count,Bit16s add_index,PhysPt di_base) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_stosw_word(Bit16u count,Bit16s add_index,PhysPt di_base) {
+ Bit16u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=(Bit16u)(count-CPU_Cycles);
+ count=(Bit16u)CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ add_index<<=1;
+ for (;count>0;count--) {
+ mem_writew(di_base+reg_di,reg_ax);
+ reg_di+=add_index;
+ }
+ return count_left;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_stosw_dword(Bit32u count,Bit32s add_index,PhysPt di_base) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_stosw_dword(Bit32u count,Bit32s add_index,PhysPt di_base) {
+ Bit32u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=count-CPU_Cycles;
+ count=CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ add_index<<=1;
+ for (;count>0;count--) {
+ mem_writew(di_base+reg_edi,reg_ax);
+ reg_edi+=add_index;
+ }
+ return count_left;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_stosd_word(Bit16u count,Bit16s add_index,PhysPt di_base) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_stosd_word(Bit16u count,Bit16s add_index,PhysPt di_base) {
+ Bit16u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=(Bit16u)(count-CPU_Cycles);
+ count=(Bit16u)CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ add_index<<=2;
+ for (;count>0;count--) {
+ mem_writed(di_base+reg_di,reg_eax);
+ reg_di+=add_index;
+ }
+ return count_left;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_stosd_dword(Bit32u count,Bit32s add_index,PhysPt di_base) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_stosd_dword(Bit32u count,Bit32s add_index,PhysPt di_base) {
+ Bit32u count_left;
+ if (count<(Bitu)CPU_Cycles) {
+ count_left=0;
+ } else {
+ count_left=count-CPU_Cycles;
+ count=CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ add_index<<=2;
+ for (;count>0;count--) {
+ mem_writed(di_base+reg_edi,reg_eax);
+ reg_edi+=add_index;
+ }
+ return count_left;
+}
+
+
+static void DRC_CALL_CONV dynrec_push_word(Bit16u value) DRC_FC;
+static void DRC_CALL_CONV dynrec_push_word(Bit16u value) {
+ Bit32u new_esp=(reg_esp&cpu.stack.notmask)|((reg_esp-2)&cpu.stack.mask);
+ mem_writew(SegPhys(ss) + (new_esp & cpu.stack.mask),value);
+ reg_esp=new_esp;
+}
+
+static void DRC_CALL_CONV dynrec_push_dword(Bit32u value) DRC_FC;
+static void DRC_CALL_CONV dynrec_push_dword(Bit32u value) {
+ Bit32u new_esp=(reg_esp&cpu.stack.notmask)|((reg_esp-4)&cpu.stack.mask);
+ mem_writed(SegPhys(ss) + (new_esp & cpu.stack.mask) ,value);
+ reg_esp=new_esp;
+}
+
+static Bit16u DRC_CALL_CONV dynrec_pop_word(void) DRC_FC;
+static Bit16u DRC_CALL_CONV dynrec_pop_word(void) {
+ Bit16u val=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask));
+ reg_esp=(reg_esp&cpu.stack.notmask)|((reg_esp+2)&cpu.stack.mask);
+ return val;
+}
+
+static Bit32u DRC_CALL_CONV dynrec_pop_dword(void) DRC_FC;
+static Bit32u DRC_CALL_CONV dynrec_pop_dword(void) {
+ Bit32u val=mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask));
+ reg_esp=(reg_esp&cpu.stack.notmask)|((reg_esp+4)&cpu.stack.mask);
+ return val;
+}
diff --git a/src/cpu/core_dynrec/risc_armv4le-common.h b/src/cpu/core_dynrec/risc_armv4le-common.h
new file mode 100644
index 000000000..9b0cae61e
--- /dev/null
+++ b/src/cpu/core_dynrec/risc_armv4le-common.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+/* ARMv4 (little endian) backend by M-HT (common data/functions) */
+
+
+// some configuring defines that specify the capabilities of this architecture
+// or aspects of the recompiling
+
+// protect FC_ADDR over function calls if necessaray
+// #define DRC_PROTECT_ADDR_REG
+
+// try to use non-flags generating functions if possible
+#define DRC_FLAGS_INVALIDATION
+// try to replace _simple functions by code
+#define DRC_FLAGS_INVALIDATION_DCODE
+
+// type with the same size as a pointer
+#define DRC_PTR_SIZE_IM Bit32u
+
+// calling convention modifier
+#define DRC_CALL_CONV /* nothing */
+#define DRC_FC /* nothing */
+
+// use FC_REGS_ADDR to hold the address of "cpu_regs" and to access it using FC_REGS_ADDR
+#define DRC_USE_REGS_ADDR
+// use FC_SEGS_ADDR to hold the address of "Segs" and to access it using FC_SEGS_ADDR
+#define DRC_USE_SEGS_ADDR
+
+// register mapping
+typedef Bit8u HostReg;
+
+// "lo" registers
+#define HOST_r0 0
+#define HOST_r1 1
+#define HOST_r2 2
+#define HOST_r3 3
+#define HOST_r4 4
+#define HOST_r5 5
+#define HOST_r6 6
+#define HOST_r7 7
+// "hi" registers
+#define HOST_r8 8
+#define HOST_r9 9
+#define HOST_r10 10
+#define HOST_r11 11
+#define HOST_r12 12
+#define HOST_r13 13
+#define HOST_r14 14
+#define HOST_r15 15
+
+// register aliases
+// "lo" registers
+#define HOST_a1 HOST_r0
+#define HOST_a2 HOST_r1
+#define HOST_a3 HOST_r2
+#define HOST_a4 HOST_r3
+#define HOST_v1 HOST_r4
+#define HOST_v2 HOST_r5
+#define HOST_v3 HOST_r6
+#define HOST_v4 HOST_r7
+// "hi" registers
+#define HOST_v5 HOST_r8
+#define HOST_v6 HOST_r9
+#define HOST_v7 HOST_r10
+#define HOST_v8 HOST_r11
+#define HOST_ip HOST_r12
+#define HOST_sp HOST_r13
+#define HOST_lr HOST_r14
+#define HOST_pc HOST_r15
+
+
+static void cache_block_closing(Bit8u* block_start,Bitu block_size) {
+#if (__ARM_EABI__)
+ //flush cache - eabi
+ register unsigned long _beg __asm ("a1") = (unsigned long)(block_start); // block start
+ register unsigned long _end __asm ("a2") = (unsigned long)(block_start+block_size); // block end
+ register unsigned long _flg __asm ("a3") = 0;
+ register unsigned long _par __asm ("r7") = 0xf0002; // sys_cacheflush
+ __asm __volatile ("swi 0x0"
+ : // no outputs
+ : "r" (_beg), "r" (_end), "r" (_flg), "r" (_par)
+ );
+#else
+// GP2X BEGIN
+ //flush cache - old abi
+ register unsigned long _beg __asm ("a1") = (unsigned long)(block_start); // block start
+ register unsigned long _end __asm ("a2") = (unsigned long)(block_start+block_size); // block end
+ register unsigned long _flg __asm ("a3") = 0;
+ __asm __volatile ("swi 0x9f0002 @ sys_cacheflush"
+ : // no outputs
+ : "r" (_beg), "r" (_end), "r" (_flg)
+ );
+// GP2X END
+#endif
+}
diff --git a/src/cpu/core_dynrec/risc_armv4le-o3.h b/src/cpu/core_dynrec/risc_armv4le-o3.h
new file mode 100644
index 000000000..88a1f0692
--- /dev/null
+++ b/src/cpu/core_dynrec/risc_armv4le-o3.h
@@ -0,0 +1,1302 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+/* ARMv4/ARMv7 (little endian) backend by M-HT (arm version) */
+
+
+// temporary registers
+#define temp1 HOST_ip
+#define temp2 HOST_v3
+#define temp3 HOST_v4
+
+// register that holds function return values
+#define FC_RETOP HOST_a1
+
+// register used for address calculations,
+#define FC_ADDR HOST_v1 // has to be saved across calls, see DRC_PROTECT_ADDR_REG
+
+// register that holds the first parameter
+#define FC_OP1 HOST_a1
+
+// register that holds the second parameter
+#define FC_OP2 HOST_a2
+
+// special register that holds the third parameter for _R3 calls (byte accessible)
+#define FC_OP3 HOST_v2
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA1 HOST_a1
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA2 HOST_a2
+
+// temporary register for LEA
+#define TEMP_REG_DRC HOST_v2
+
+// used to hold the address of "cpu_regs" - preferably filled in function gen_run_code
+#define FC_REGS_ADDR HOST_v7
+
+// used to hold the address of "Segs" - preferably filled in function gen_run_code
+#define FC_SEGS_ADDR HOST_v8
+
+// used to hold the address of "core_dynrec.readdata" - filled in function gen_run_code
+#define readdata_addr HOST_v5
+
+
+// helper macro
+#define ROTATE_SCALE(x) ( (x)?(32 - x):(0) )
+
+
+// instruction encodings
+
+// move
+// mov dst, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define MOV_IMM(dst, imm, rimm) (0xe3a00000 + ((dst) << 12) + (imm) + ((rimm) << 7) )
+// mov dst, src, lsl #imm
+#define MOV_REG_LSL_IMM(dst, src, imm) (0xe1a00000 + ((dst) << 12) + (src) + ((imm) << 7) )
+// movs dst, src, lsl #imm
+#define MOVS_REG_LSL_IMM(dst, src, imm) (0xe1b00000 + ((dst) << 12) + (src) + ((imm) << 7) )
+// mov dst, src, lsr #imm
+#define MOV_REG_LSR_IMM(dst, src, imm) (0xe1a00020 + ((dst) << 12) + (src) + ((imm) << 7) )
+// mov dst, src, asr #imm
+#define MOV_REG_ASR_IMM(dst, src, imm) (0xe1a00040 + ((dst) << 12) + (src) + ((imm) << 7) )
+// mov dst, src, lsl rreg
+#define MOV_REG_LSL_REG(dst, src, rreg) (0xe1a00010 + ((dst) << 12) + (src) + ((rreg) << 8) )
+// mov dst, src, lsr rreg
+#define MOV_REG_LSR_REG(dst, src, rreg) (0xe1a00030 + ((dst) << 12) + (src) + ((rreg) << 8) )
+// mov dst, src, asr rreg
+#define MOV_REG_ASR_REG(dst, src, rreg) (0xe1a00050 + ((dst) << 12) + (src) + ((rreg) << 8) )
+// mov dst, src, ror rreg
+#define MOV_REG_ROR_REG(dst, src, rreg) (0xe1a00070 + ((dst) << 12) + (src) + ((rreg) << 8) )
+// mvn dst, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define MVN_IMM(dst, imm, rimm) (0xe3e00000 + ((dst) << 12) + (imm) + ((rimm) << 7) )
+#if C_TARGETCPU == ARMV7LE
+// movw dst, #imm @ 0 <= imm <= 65535
+#define MOVW(dst, imm) (0xe3000000 + ((dst) << 12) + (((imm) & 0xf000) << 4) + ((imm) & 0x0fff) )
+// movt dst, #imm @ 0 <= imm <= 65535
+#define MOVT(dst, imm) (0xe3400000 + ((dst) << 12) + (((imm) & 0xf000) << 4) + ((imm) & 0x0fff) )
+#endif
+
+// arithmetic
+// add dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define ADD_IMM(dst, src, imm, rimm) (0xe2800000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
+// add dst, src1, src2, lsl #imm
+#define ADD_REG_LSL_IMM(dst, src1, src2, imm) (0xe0800000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
+// sub dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define SUB_IMM(dst, src, imm, rimm) (0xe2400000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
+// sub dst, src1, src2, lsl #imm
+#define SUB_REG_LSL_IMM(dst, src1, src2, imm) (0xe0400000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
+// rsb dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define RSB_IMM(dst, src, imm, rimm) (0xe2600000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
+// cmp src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define CMP_IMM(src, imm, rimm) (0xe3500000 + ((src) << 16) + (imm) + ((rimm) << 7) )
+// nop
+#if C_TARGETCPU == ARMV7LE
+#define NOP (0xe320f000)
+#else
+#define NOP MOV_REG_LSL_IMM(HOST_r0, HOST_r0, 0)
+#endif
+
+// logical
+// tst src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define TST_IMM(src, imm, rimm) (0xe3100000 + ((src) << 16) + (imm) + ((rimm) << 7) )
+// and dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define AND_IMM(dst, src, imm, rimm) (0xe2000000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
+// and dst, src1, src2, lsl #imm
+#define AND_REG_LSL_IMM(dst, src1, src2, imm) (0xe0000000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
+// orr dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define ORR_IMM(dst, src, imm, rimm) (0xe3800000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
+// orr dst, src1, src2, lsl #imm
+#define ORR_REG_LSL_IMM(dst, src1, src2, imm) (0xe1800000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
+// orr dst, src1, src2, lsr #imm
+#define ORR_REG_LSR_IMM(dst, src1, src2, imm) (0xe1800020 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
+// eor dst, src1, src2, lsl #imm
+#define EOR_REG_LSL_IMM(dst, src1, src2, imm) (0xe0200000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
+// bic dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define BIC_IMM(dst, src, imm, rimm) (0xe3c00000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
+// bic dst, src1, src2, lsl #imm @ 0 <= imm <= 31
+#define BIC_REG_LSL_IMM(dst, src1, src2, imm) (0xe1c00000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) )
+
+// load
+// ldr reg, [addr, #imm] @ 0 <= imm < 4096
+#define LDR_IMM(reg, addr, imm) (0xe5900000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+// ldr reg, [addr, #-(imm)] @ 0 <= imm < 4096
+#define LDR_IMM_M(reg, addr, imm) (0xe5100000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+// ldrh reg, [addr, #imm] @ 0 <= imm < 256
+#define LDRH_IMM(reg, addr, imm) (0xe1d000b0 + ((reg) << 12) + ((addr) << 16) + (((imm) & 0xf0) << 4) + ((imm) & 0x0f) )
+// ldrh reg, [addr, #-(imm)] @ 0 <= imm < 256
+#define LDRH_IMM_M(reg, addr, imm) (0xe15000b0 + ((reg) << 12) + ((addr) << 16) + (((imm) & 0xf0) << 4) + ((imm) & 0x0f) )
+// ldrb reg, [addr, #imm] @ 0 <= imm < 4096
+#define LDRB_IMM(reg, addr, imm) (0xe5d00000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+// ldrb reg, [addr, #-(imm)] @ 0 <= imm < 4096
+#define LDRB_IMM_M(reg, addr, imm) (0xe5500000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+// ldr reg, [addr1, addr2, lsl #imm] @ 0 <= imm < 31
+#define LDR_REG_LSL_IMM(reg, addr1, addr2, imm) (0xe7900000 + ((reg) << 12) + ((addr1) << 16) + (addr2) + ((imm) << 7) )
+
+// store
+// str reg, [addr, #imm] @ 0 <= imm < 4096
+#define STR_IMM(reg, addr, imm) (0xe5800000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+// str reg, [addr, #-(imm)] @ 0 <= imm < 4096
+#define STR_IMM_M(reg, addr, imm) (0xe5000000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+// strh reg, [addr, #imm] @ 0 <= imm < 256
+#define STRH_IMM(reg, addr, imm) (0xe1c000b0 + ((reg) << 12) + ((addr) << 16) + (((imm) & 0xf0) << 4) + ((imm) & 0x0f) )
+// strh reg, [addr, #-(imm)] @ 0 <= imm < 256
+#define STRH_IMM_M(reg, addr, imm) (0xe14000b0 + ((reg) << 12) + ((addr) << 16) + (((imm) & 0xf0) << 4) + ((imm) & 0x0f) )
+// strb reg, [addr, #imm] @ 0 <= imm < 4096
+#define STRB_IMM(reg, addr, imm) (0xe5c00000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+// strb reg, [addr, #-(imm)] @ 0 <= imm < 4096
+#define STRB_IMM_M(reg, addr, imm) (0xe5400000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+
+// branch
+// beq pc+imm @ 0 <= imm < 32M & imm mod 4 = 0
+#define BEQ_FWD(imm) (0x0a000000 + ((imm) >> 2) )
+// bne pc+imm @ 0 <= imm < 32M & imm mod 4 = 0
+#define BNE_FWD(imm) (0x1a000000 + ((imm) >> 2) )
+// ble pc+imm @ 0 <= imm < 32M & imm mod 4 = 0
+#define BLE_FWD(imm) (0xda000000 + ((imm) >> 2) )
+// b pc+imm @ 0 <= imm < 32M & imm mod 4 = 0
+#define B_FWD(imm) (0xea000000 + ((imm) >> 2) )
+// bx reg
+#define BX(reg) (0xe12fff10 + (reg) )
+#if C_TARGETCPU == ARMV7LE
+// blx reg
+#define BLX_REG(reg) (0xe12fff30 + (reg) )
+
+// extend
+// sxth dst, src, ror #rimm @ rimm = 0 | 8 | 16 | 24
+#define SXTH(dst, src, rimm) (0xe6bf0070 + ((dst) << 12) + (src) + (((rimm) & 24) << 7) )
+// sxtb dst, src, ror #rimm @ rimm = 0 | 8 | 16 | 24
+#define SXTB(dst, src, rimm) (0xe6af0070 + ((dst) << 12) + (src) + (((rimm) & 24) << 7) )
+// uxth dst, src, ror #rimm @ rimm = 0 | 8 | 16 | 24
+#define UXTH(dst, src, rimm) (0xe6ff0070 + ((dst) << 12) + (src) + (((rimm) & 24) << 7) )
+// uxtb dst, src, ror #rimm @ rimm = 0 | 8 | 16 | 24
+#define UXTB(dst, src, rimm) (0xe6ef0070 + ((dst) << 12) + (src) + (((rimm) & 24) << 7) )
+
+// bit field
+// bfi dst, src, #lsb, #width @ lsb >= 0, width >= 1, lsb+width <= 32
+#define BFI(dst, src, lsb, width) (0xe7c00010 + ((dst) << 12) + (src) + ((lsb) << 7) + (((lsb) + (width) - 1) << 16) )
+// bfc dst, #lsb, #width @ lsb >= 0, width >= 1, lsb+width <= 32
+#define BFC(dst, lsb, width) (0xe7c0001f + ((dst) << 12) + ((lsb) << 7) + (((lsb) + (width) - 1) << 16) )
+#endif
+
+
+// move a full register from reg_src to reg_dst
+static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) {
+ if(reg_src == reg_dst) return;
+ cache_addd( MOV_REG_LSL_IMM(reg_dst, reg_src, 0) ); // mov reg_dst, reg_src
+}
+
+// helper function
+static bool val_is_operand2(Bit32u value, Bit32u *val_shift) {
+ Bit32u shift;
+
+ if (GCC_UNLIKELY(value == 0)) {
+ *val_shift = 0;
+ return true;
+ }
+
+ shift = 0;
+ while ((value & 3) == 0) {
+ value>>=2;
+ shift+=2;
+ }
+
+ if ((value >> 8) != 0) return false;
+
+ *val_shift = shift;
+ return true;
+}
+
+#if C_TARGETCPU != ARMV7LE
+// helper function
+static Bits get_imm_gen_len(Bit32u imm) {
+ Bits ret;
+ if (imm == 0) {
+ return 1;
+ } else {
+ ret = 0;
+ while (imm) {
+ while ((imm & 3) == 0) {
+ imm>>=2;
+ }
+ ret++;
+ imm>>=8;
+ }
+ return ret;
+ }
+}
+
+// helper function
+static Bits get_min_imm_gen_len(Bit32u imm) {
+ Bits num1, num2;
+
+ num1 = get_imm_gen_len(imm);
+ num2 = get_imm_gen_len(~imm);
+
+ return (num1 <= num2)?num1:num2;
+}
+#endif
+
+// move a 32bit constant value into dest_reg
+static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) {
+#if C_TARGETCPU == ARMV7LE
+ Bit32u scale;
+
+ if ( val_is_operand2(imm, &scale) ) {
+ cache_addd( MOV_IMM(dest_reg, imm >> scale, ROTATE_SCALE(scale)) ); // mov dest_reg, #imm
+ } else if ( val_is_operand2(~imm, &scale) ) {
+ cache_addd( MVN_IMM(dest_reg, (~imm) >> scale, ROTATE_SCALE(scale)) ); // mvn dest_reg, #~imm
+ } else {
+ cache_addd( MOVW(dest_reg, imm & 0xffff) ); // movw dest_reg, #(imm & 0xffff)
+
+ if (imm >= 0x10000)
+ {
+ cache_addd( MOVT(dest_reg, imm >> 16) ); // movt dest_reg, #(imm >> 16)
+ }
+ }
+#else
+ Bit32u imm2, first, scale;
+
+ scale = 0;
+ first = 1;
+ imm2 = ~imm;
+
+ if (get_imm_gen_len(imm) <= get_imm_gen_len(imm2)) {
+ if (imm == 0) {
+ cache_addd( MOV_IMM(dest_reg, 0, 0) ); // mov dest_reg, #0
+ } else {
+ while (imm) {
+ while ((imm & 3) == 0) {
+ imm>>=2;
+ scale+=2;
+ }
+ if (first) {
+ cache_addd( MOV_IMM(dest_reg, imm & 0xff, ROTATE_SCALE(scale)) ); // mov dest_reg, #((imm & 0xff) << scale)
+ first = 0;
+ } else {
+ cache_addd( ORR_IMM(dest_reg, dest_reg, imm & 0xff, ROTATE_SCALE(scale)) ); // orr dest_reg, dest_reg, #((imm & 0xff) << scale)
+ }
+ imm>>=8;
+ scale+=8;
+ }
+ }
+ } else {
+ if (imm2 == 0) {
+ cache_addd( MVN_IMM(dest_reg, 0, 0) ); // mvn dest_reg, #0
+ } else {
+ while (imm2) {
+ while ((imm2 & 3) == 0) {
+ imm2>>=2;
+ scale+=2;
+ }
+ if (first) {
+ cache_addd( MVN_IMM(dest_reg, imm2 & 0xff, ROTATE_SCALE(scale)) ); // mvn dest_reg, #((imm2 & 0xff) << scale)
+ first = 0;
+ } else {
+ cache_addd( BIC_IMM(dest_reg, dest_reg, imm2 & 0xff, ROTATE_SCALE(scale)) ); // bic dest_reg, dest_reg, #((imm2 & 0xff) << scale)
+ }
+ imm2>>=8;
+ scale+=8;
+ }
+ }
+ }
+#endif
+}
+
+// helper function
+static bool gen_mov_memval_to_reg_helper(HostReg dest_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) {
+ switch (size) {
+ case 4:
+#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE))
+ if ((data & 3) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 4096)) {
+ cache_addd( LDR_IMM(dest_reg, addr_reg, data - addr_data) ); // ldr dest_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data) && (data > addr_data - 4096)) {
+ cache_addd( LDR_IMM_M(dest_reg, addr_reg, addr_data - data) ); // ldr dest_reg, [addr_reg, #-(addr_data - data)]
+ return true;
+ }
+ }
+ break;
+ case 2:
+#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE))
+ if ((data & 1) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 256)) {
+ cache_addd( LDRH_IMM(dest_reg, addr_reg, data - addr_data) ); // ldrh dest_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data) && (data > addr_data - 256)) {
+ cache_addd( LDRH_IMM_M(dest_reg, addr_reg, addr_data - data) ); // ldrh dest_reg, [addr_reg, #-(addr_data - data)]
+ return true;
+ }
+ }
+ break;
+ case 1:
+ if ((data >= addr_data) && (data < addr_data + 4096)) {
+ cache_addd( LDRB_IMM(dest_reg, addr_reg, data - addr_data) ); // ldrb dest_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data) && (data > addr_data - 4096)) {
+ cache_addd( LDRB_IMM_M(dest_reg, addr_reg, addr_data - data) ); // ldrb dest_reg, [addr_reg, #-(addr_data - data)]
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+// helper function
+static bool gen_mov_memval_to_reg(HostReg dest_reg, void *data, Bitu size) {
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true;
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true;
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true;
+ return false;
+}
+
+// helper function for gen_mov_word_to_reg
+static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,HostReg data_reg) {
+ // alignment....
+ if (dword) {
+#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE))
+ if ((Bit32u)data & 3) {
+ if ( ((Bit32u)data & 3) == 2 ) {
+ cache_addd( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg]
+ cache_addd( LDRH_IMM(temp2, data_reg, 2) ); // ldrh temp2, [data_reg, #2]
+ cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 16) ); // orr dest_reg, dest_reg, temp2, lsl #16
+ } else {
+ cache_addd( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg]
+ cache_addd( LDRH_IMM(temp2, data_reg, 1) ); // ldrh temp2, [data_reg, #1]
+ cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 8) ); // orr dest_reg, dest_reg, temp2, lsl #8
+ cache_addd( LDRB_IMM(temp2, data_reg, 3) ); // ldrb temp2, [data_reg, #3]
+ cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 24) ); // orr dest_reg, dest_reg, temp2, lsl #24
+ }
+ } else
+#endif
+ {
+ cache_addd( LDR_IMM(dest_reg, data_reg, 0) ); // ldr dest_reg, [data_reg]
+ }
+ } else {
+#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE))
+ if ((Bit32u)data & 1) {
+ cache_addd( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg]
+ cache_addd( LDRB_IMM(temp2, data_reg, 1) ); // ldrb temp2, [data_reg, #1]
+ cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 8) ); // orr dest_reg, dest_reg, temp2, lsl #8
+ } else
+#endif
+ {
+ cache_addd( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg]
+ }
+ }
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) {
+ if (!gen_mov_memval_to_reg(dest_reg, data, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(temp1, (Bit32u)data);
+ gen_mov_word_to_reg_helper(dest_reg, data, dword, temp1);
+ }
+}
+
+// move a 16bit constant value into dest_reg
+// the upper 16bit of the destination register may be destroyed
+static void INLINE gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) {
+ gen_mov_dword_to_reg_imm(dest_reg, (Bit32u)imm);
+}
+
+// helper function
+static bool gen_mov_memval_from_reg_helper(HostReg src_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) {
+ switch (size) {
+ case 4:
+#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE))
+ if ((data & 3) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 4096)) {
+ cache_addd( STR_IMM(src_reg, addr_reg, data - addr_data) ); // str src_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data) && (data > addr_data - 4096)) {
+ cache_addd( STR_IMM_M(src_reg, addr_reg, addr_data - data) ); // str src_reg, [addr_reg, #-(addr_data - data)]
+ return true;
+ }
+ }
+ break;
+ case 2:
+#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE))
+ if ((data & 1) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 256)) {
+ cache_addd( STRH_IMM(src_reg, addr_reg, data - addr_data) ); // strh src_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data) && (data > addr_data - 256)) {
+ cache_addd( STRH_IMM_M(src_reg, addr_reg, addr_data - data) ); // strh src_reg, [addr_reg, #-(addr_data - data)]
+ return true;
+ }
+ }
+ break;
+ case 1:
+ if ((data >= addr_data) && (data < addr_data + 4096)) {
+ cache_addd( STRB_IMM(src_reg, addr_reg, data - addr_data) ); // strb src_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data) && (data > addr_data - 4096)) {
+ cache_addd( STRB_IMM_M(src_reg, addr_reg, addr_data - data) ); // strb src_reg, [addr_reg, #-(addr_data - data)]
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+// helper function
+static bool gen_mov_memval_from_reg(HostReg src_reg, void *dest, Bitu size) {
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true;
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true;
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true;
+ return false;
+}
+
+// helper function for gen_mov_word_from_reg
+static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, HostReg data_reg) {
+ // alignment....
+ if (dword) {
+#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE))
+ if ((Bit32u)dest & 3) {
+ if ( ((Bit32u)dest & 3) == 2 ) {
+ cache_addd( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg]
+ cache_addd( MOV_REG_LSR_IMM(temp2, src_reg, 16) ); // mov temp2, src_reg, lsr #16
+ cache_addd( STRH_IMM(temp2, data_reg, 2) ); // strh temp2, [data_reg, #2]
+ } else {
+ cache_addd( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg]
+ cache_addd( MOV_REG_LSR_IMM(temp2, src_reg, 8) ); // mov temp2, src_reg, lsr #8
+ cache_addd( STRH_IMM(temp2, data_reg, 1) ); // strh temp2, [data_reg, #1]
+ cache_addd( MOV_REG_LSR_IMM(temp2, temp2, 16) ); // mov temp2, temp2, lsr #16
+ cache_addd( STRB_IMM(temp2, data_reg, 3) ); // strb temp2, [data_reg, #3]
+ }
+ } else
+#endif
+ {
+ cache_addd( STR_IMM(src_reg, data_reg, 0) ); // str src_reg, [data_reg]
+ }
+ } else {
+#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE))
+ if ((Bit32u)dest & 1) {
+ cache_addd( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg]
+ cache_addd( MOV_REG_LSR_IMM(temp2, src_reg, 8) ); // mov temp2, src_reg, lsr #8
+ cache_addd( STRB_IMM(temp2, data_reg, 1) ); // strb temp2, [data_reg, #1]
+ } else
+#endif
+ {
+ cache_addd( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg]
+ }
+ }
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into memory
+static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) {
+ if (!gen_mov_memval_from_reg(src_reg, dest, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest);
+ gen_mov_word_from_reg_helper(src_reg, dest, dword, temp1);
+ }
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) {
+ if (!gen_mov_memval_to_reg(dest_reg, data, 1)) {
+ gen_mov_dword_to_reg_imm(temp1, (Bit32u)data);
+ cache_addd( LDRB_IMM(dest_reg, temp1, 0) ); // ldrb dest_reg, [temp1]
+ }
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) {
+ gen_mov_byte_to_reg_low(dest_reg, data);
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) {
+ cache_addd( MOV_IMM(dest_reg, imm, 0) ); // mov dest_reg, #(imm)
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) {
+ gen_mov_byte_to_reg_low_imm(dest_reg, imm);
+}
+
+// move the lowest 8bit of a register into memory
+static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) {
+ if (!gen_mov_memval_from_reg(src_reg, dest, 1)) {
+ gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest);
+ cache_addd( STRB_IMM(src_reg, temp1, 0) ); // strb src_reg, [temp1]
+ }
+}
+
+
+
+// convert an 8bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_byte(bool sign,HostReg reg) {
+ if (sign) {
+#if C_TARGETCPU == ARMV7LE
+ cache_addd( SXTB(reg, reg, 0) ); // sxtb reg, reg
+#else
+ cache_addd( MOV_REG_LSL_IMM(reg, reg, 24) ); // mov reg, reg, lsl #24
+ cache_addd( MOV_REG_ASR_IMM(reg, reg, 24) ); // mov reg, reg, asr #24
+#endif
+ } else {
+#if C_TARGETCPU == ARMV7LE
+ cache_addd( UXTB(reg, reg, 0) ); // uxtb reg, reg
+#else
+ cache_addd( AND_IMM(reg, reg, 0xff, 0) ); // and reg, reg, #0xff
+#endif
+ }
+}
+
+// convert a 16bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_word(bool sign,HostReg reg) {
+ if (sign) {
+#if C_TARGETCPU == ARMV7LE
+ cache_addd( SXTH(reg, reg, 0) ); // sxth reg, reg
+#else
+ cache_addd( MOV_REG_LSL_IMM(reg, reg, 16) ); // mov reg, reg, lsl #16
+ cache_addd( MOV_REG_ASR_IMM(reg, reg, 16) ); // mov reg, reg, asr #16
+#endif
+ } else {
+#if C_TARGETCPU == ARMV7LE
+ cache_addd( UXTH(reg, reg, 0) ); // uxth reg, reg
+#else
+ cache_addd( MOV_REG_LSL_IMM(reg, reg, 16) ); // mov reg, reg, lsl #16
+ cache_addd( MOV_REG_LSR_IMM(reg, reg, 16) ); // mov reg, reg, lsr #16
+#endif
+ }
+}
+
+// add a 32bit value from memory to a full register
+static void gen_add(HostReg reg,void* op) {
+ gen_mov_word_to_reg(temp3, op, 1);
+ cache_addd( ADD_REG_LSL_IMM(reg, reg, temp3, 0) ); // add reg, reg, temp3
+}
+
+// add a 32bit constant value to a full register
+static void gen_add_imm(HostReg reg,Bit32u imm) {
+ Bit32u imm2, scale;
+
+ if(!imm) return;
+
+ imm2 = (Bit32u) (-((Bit32s)imm));
+
+ if ( val_is_operand2(imm, &scale) ) {
+ cache_addd( ADD_IMM(reg, reg, imm >> scale, ROTATE_SCALE(scale)) ); // add reg, reg, #imm
+ } else if ( val_is_operand2(imm2, &scale) ) {
+ cache_addd( SUB_IMM(reg, reg, imm2 >> scale, ROTATE_SCALE(scale)) ); // sub reg, reg, #(-imm)
+#if C_TARGETCPU == ARMV7LE
+ } else if (imm2 < 0x10000) {
+ cache_addd( MOVW(temp2, imm2) ); // movw temp2, #(-imm)
+ cache_addd( SUB_REG_LSL_IMM(reg, reg, temp2, 0) ); // sub reg, reg, temp2
+#endif
+ } else {
+#if C_TARGETCPU != ARMV7LE
+ if (get_min_imm_gen_len(imm) <= get_min_imm_gen_len(imm2)) {
+#endif
+ gen_mov_dword_to_reg_imm(temp2, imm);
+ cache_addd( ADD_REG_LSL_IMM(reg, reg, temp2, 0) ); // add reg, reg, temp2
+#if C_TARGETCPU != ARMV7LE
+ } else {
+ gen_mov_dword_to_reg_imm(temp2, imm2);
+ cache_addd( SUB_REG_LSL_IMM(reg, reg, temp2, 0) ); // sub reg, reg, temp2
+ }
+#endif
+ }
+}
+
+// and a 32bit constant value with a full register
+static void gen_and_imm(HostReg reg,Bit32u imm) {
+ Bit32u imm2, scale;
+
+ imm2 = ~imm;
+ if(!imm2) return;
+
+ if (!imm) {
+ cache_addd( MOV_IMM(reg, 0, 0) ); // mov reg, #0
+ } else if ( val_is_operand2(imm, &scale) ) {
+ cache_addd( AND_IMM(reg, reg, imm >> scale, ROTATE_SCALE(scale)) ); // and reg, reg, #imm
+ } else if ( val_is_operand2(imm2, &scale) ) {
+ cache_addd( BIC_IMM(reg, reg, imm2 >> scale, ROTATE_SCALE(scale)) ); // bic reg, reg, #(~imm)
+#if C_TARGETCPU == ARMV7LE
+ } else if (imm2 < 0x10000) {
+ cache_addd( MOVW(temp2, imm2) ); // movw temp2, #(~imm)
+ cache_addd( BIC_REG_LSL_IMM(reg, reg, temp2, 0) ); // bic reg, reg, temp2
+#endif
+ } else {
+ gen_mov_dword_to_reg_imm(temp2, imm);
+ cache_addd( AND_REG_LSL_IMM(reg, reg, temp2, 0) ); // and reg, reg, temp2
+ }
+}
+
+
+// move a 32bit constant value into memory
+static void gen_mov_direct_dword(void* dest,Bit32u imm) {
+ gen_mov_dword_to_reg_imm(temp3, imm);
+ gen_mov_word_from_reg(temp3, dest, 1);
+}
+
+// move an address into memory
+static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) {
+ gen_mov_direct_dword(dest,(Bit32u)imm);
+}
+
+// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value
+static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) {
+ if (!dword) imm &= 0xffff;
+ if(!imm) return;
+
+ if (!gen_mov_memval_to_reg(temp3, dest, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest);
+ gen_mov_word_to_reg_helper(temp3, dest, dword, temp1);
+ }
+ gen_add_imm(temp3, imm);
+ if (!gen_mov_memval_from_reg(temp3, dest, (dword)?4:2)) {
+ gen_mov_word_from_reg_helper(temp3, dest, dword, temp1);
+ }
+}
+
+// add an 8bit constant value to a dword memory value
+static void gen_add_direct_byte(void* dest,Bit8s imm) {
+ gen_add_direct_word(dest, (Bit32s)imm, 1);
+}
+
+// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value
+static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) {
+ Bit32u imm2, scale;
+
+ if (!dword) imm &= 0xffff;
+ if(!imm) return;
+
+ if (!gen_mov_memval_to_reg(temp3, dest, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest);
+ gen_mov_word_to_reg_helper(temp3, dest, dword, temp1);
+ }
+
+ imm2 = (Bit32u) (-((Bit32s)imm));
+
+ if ( val_is_operand2(imm, &scale) ) {
+ cache_addd( SUB_IMM(temp3, temp3, imm >> scale, ROTATE_SCALE(scale)) ); // sub temp3, temp3, #imm
+ } else if ( val_is_operand2(imm2, &scale) ) {
+ cache_addd( ADD_IMM(temp3, temp3, imm2 >> scale, ROTATE_SCALE(scale)) ); // add temp3, temp3, #(-imm)
+#if C_TARGETCPU == ARMV7LE
+ } else if (imm2 < 0x10000) {
+ cache_addd( MOVW(temp2, imm2) ); // movw temp2, #(-imm)
+ cache_addd( ADD_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // add temp3, temp3, temp2
+#endif
+ } else {
+#if C_TARGETCPU != ARMV7LE
+ if (get_min_imm_gen_len(imm) <= get_min_imm_gen_len(imm2)) {
+#endif
+ gen_mov_dword_to_reg_imm(temp2, imm);
+ cache_addd( SUB_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // sub temp3, temp3, temp2
+#if C_TARGETCPU != ARMV7LE
+ } else {
+ gen_mov_dword_to_reg_imm(temp2, imm2);
+ cache_addd( ADD_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // add temp3, temp3, temp2
+ }
+#endif
+ }
+
+ if (!gen_mov_memval_from_reg(temp3, dest, (dword)?4:2)) {
+ gen_mov_word_from_reg_helper(temp3, dest, dword, temp1);
+ }
+}
+
+// subtract an 8bit constant value from a dword memory value
+static void gen_sub_direct_byte(void* dest,Bit8s imm) {
+ gen_sub_direct_word(dest, (Bit32s)imm, 1);
+}
+
+// effective address calculation, destination is dest_reg
+// scale_reg is scaled by scale (scale_reg*(2^scale)) and
+// added to dest_reg, then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) {
+ cache_addd( ADD_REG_LSL_IMM(dest_reg, dest_reg, scale_reg, scale) ); // add dest_reg, dest_reg, scale_reg, lsl #(scale)
+ gen_add_imm(dest_reg, imm);
+}
+
+// effective address calculation, destination is dest_reg
+// dest_reg is scaled by scale (dest_reg*(2^scale)),
+// then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) {
+ if (scale) {
+ cache_addd( MOV_REG_LSL_IMM(dest_reg, dest_reg, scale) ); // mov dest_reg, dest_reg, lsl #(scale)
+ }
+ gen_add_imm(dest_reg, imm);
+}
+
+// generate a call to a parameterless function
+static void INLINE gen_call_function_raw(void * func) {
+#if C_TARGETCPU == ARMV7LE
+ cache_addd( MOVW(temp1, ((Bit32u)func) & 0xffff) ); // movw temp1, #(func & 0xffff)
+ cache_addd( MOVT(temp1, ((Bit32u)func) >> 16) ); // movt temp1, #(func >> 16)
+ cache_addd( BLX_REG(temp1) ); // blx temp1
+#else
+ cache_addd( LDR_IMM(temp1, HOST_pc, 4) ); // ldr temp1, [pc, #4]
+ cache_addd( ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4
+ cache_addd( BX(temp1) ); // bx temp1
+ cache_addd((Bit32u)func); // .int func
+#endif
+}
+
+// generate a call to a function with paramcount parameters
+// note: the parameters are loaded in the architecture specific way
+// using the gen_load_param_ functions below
+static Bit32u INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) {
+ Bit32u proc_addr = (Bit32u)cache.pos;
+ gen_call_function_raw(func);
+ return proc_addr;
+}
+
+#if (1)
+// max of 4 parameters in a1-a4
+
+// load an immediate value as param'th function parameter
+static void INLINE gen_load_param_imm(Bitu imm,Bitu param) {
+ gen_mov_dword_to_reg_imm(param, imm);
+}
+
+// load an address as param'th function parameter
+static void INLINE gen_load_param_addr(Bitu addr,Bitu param) {
+ gen_mov_dword_to_reg_imm(param, addr);
+}
+
+// load a host-register as param'th function parameter
+static void INLINE gen_load_param_reg(Bitu reg,Bitu param) {
+ gen_mov_regs(param, reg);
+}
+
+// load a value from memory as param'th function parameter
+static void INLINE gen_load_param_mem(Bitu mem,Bitu param) {
+ gen_mov_word_to_reg(param, (void *)mem, 1);
+}
+#else
+ other arm abis
+#endif
+
+// jump to an address pointed at by ptr, offset is in imm
+static void gen_jmp_ptr(void * ptr,Bits imm=0) {
+ Bit32u scale;
+
+ gen_mov_word_to_reg(temp3, ptr, 1);
+
+#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE))
+// (*ptr) should be word aligned
+ if ((imm & 0x03) == 0) {
+#endif
+ if ((imm >= 0) && (imm < 4096)) {
+ cache_addd( LDR_IMM(temp1, temp3, imm) ); // ldr temp1, [temp3, #imm]
+ } else {
+ gen_mov_dword_to_reg_imm(temp2, imm);
+ cache_addd( LDR_REG_LSL_IMM(temp1, temp3, temp2, 0) ); // ldr temp1, [temp3, temp2]
+ }
+#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE))
+ } else {
+ gen_add_imm(temp3, imm);
+
+ cache_addd( LDRB_IMM(temp1, temp3, 0) ); // ldrb temp1, [temp3]
+ cache_addd( LDRB_IMM(temp2, temp3, 1) ); // ldrb temp2, [temp3, #1]
+ cache_addd( ORR_REG_LSL_IMM(temp1, temp1, temp2, 8) ); // orr temp1, temp1, temp2, lsl #8
+ cache_addd( LDRB_IMM(temp2, temp3, 2) ); // ldrb temp2, [temp3, #2]
+ cache_addd( ORR_REG_LSL_IMM(temp1, temp1, temp2, 16) ); // orr temp1, temp1, temp2, lsl #16
+ cache_addd( LDRB_IMM(temp2, temp3, 3) ); // ldrb temp2, [temp3, #3]
+ cache_addd( ORR_REG_LSL_IMM(temp1, temp1, temp2, 24) ); // orr temp1, temp1, temp2, lsl #24
+ }
+#endif
+
+ cache_addd( BX(temp1) ); // bx temp1
+}
+
+// short conditional jump (+-127 bytes) if register is zero
+// the destination is set by gen_fill_branch() later
+static Bit32u gen_create_branch_on_zero(HostReg reg,bool dword) {
+ if (dword) {
+ cache_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0
+ } else {
+ cache_addd( MOVS_REG_LSL_IMM(temp1, reg, 16) ); // movs temp1, reg, lsl #16
+ }
+ cache_addd( BEQ_FWD(0) ); // beq j
+ return ((Bit32u)cache.pos-4);
+}
+
+// short conditional jump (+-127 bytes) if register is nonzero
+// the destination is set by gen_fill_branch() later
+static Bit32u gen_create_branch_on_nonzero(HostReg reg,bool dword) {
+ if (dword) {
+ cache_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0
+ } else {
+ cache_addd( MOVS_REG_LSL_IMM(temp1, reg, 16) ); // movs temp1, reg, lsl #16
+ }
+ cache_addd( BNE_FWD(0) ); // bne j
+ return ((Bit32u)cache.pos-4);
+}
+
+// calculate relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch(DRC_PTR_SIZE_IM data) {
+#if C_DEBUG
+ Bits len=(Bit32u)cache.pos-(data+8);
+ if (len<0) len=-len;
+ if (len>0x02000000) LOG_MSG("Big jump %d",len);
+#endif
+ *(Bit32u*)data=( (*(Bit32u*)data) & 0xff000000 ) | ( ( ((Bit32u)cache.pos - (data+8)) >> 2 ) & 0x00ffffff );
+}
+
+// conditional jump if register is nonzero
+// for isdword==true the 32bit of the register are tested
+// for isdword==false the lowest 8bit of the register are tested
+static Bit32u gen_create_branch_long_nonzero(HostReg reg,bool isdword) {
+ if (isdword) {
+ cache_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0
+ } else {
+ cache_addd( TST_IMM(reg, 0xff, 0) ); // tst reg, #0xff
+ }
+ cache_addd( BNE_FWD(0) ); // bne j
+ return ((Bit32u)cache.pos-4);
+}
+
+// compare 32bit-register against zero and jump if value less/equal than zero
+static Bit32u gen_create_branch_long_leqzero(HostReg reg) {
+ cache_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0
+ cache_addd( BLE_FWD(0) ); // ble j
+ return ((Bit32u)cache.pos-4);
+}
+
+// calculate long relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch_long(Bit32u data) {
+ *(Bit32u*)data=( (*(Bit32u*)data) & 0xff000000 ) | ( ( ((Bit32u)cache.pos - (data+8)) >> 2 ) & 0x00ffffff );
+}
+
+static void gen_run_code(void) {
+#if C_TARGETCPU == ARMV7LE
+ cache_addd(0xe92d4df0); // stmfd sp!, {v1-v5,v7,v8,lr}
+
+ cache_addd( MOVW(FC_SEGS_ADDR, ((Bit32u)&Segs) & 0xffff) ); // movw FC_SEGS_ADDR, #(&Segs & 0xffff)
+ cache_addd( MOVT(FC_SEGS_ADDR, ((Bit32u)&Segs) >> 16) ); // movt FC_SEGS_ADDR, #(&Segs >> 16)
+
+ cache_addd( MOVW(FC_REGS_ADDR, ((Bit32u)&cpu_regs) & 0xffff) ); // movw FC_REGS_ADDR, #(&cpu_regs & 0xffff)
+ cache_addd( MOVT(FC_REGS_ADDR, ((Bit32u)&cpu_regs) >> 16) ); // movt FC_REGS_ADDR, #(&cpu_regs >> 16)
+
+ cache_addd( MOVW(readdata_addr, ((Bitu)&core_dynrec.readdata) & 0xffff) ); // movw readdata_addr, #(&core_dynrec.readdata & 0xffff)
+ cache_addd( MOVT(readdata_addr, ((Bitu)&core_dynrec.readdata) >> 16) ); // movt readdata_addr, #(&core_dynrec.readdata >> 16)
+
+ cache_addd( BX(HOST_r0) ); // bx r0
+#else
+ Bit8u *pos1, *pos2, *pos3;
+
+ cache_addd(0xe92d4df0); // stmfd sp!, {v1-v5,v7,v8,lr}
+
+ pos1 = cache.pos;
+ cache_addd( 0 );
+ pos2 = cache.pos;
+ cache_addd( 0 );
+ pos3 = cache.pos;
+ cache_addd( 0 );
+
+ cache_addd( BX(HOST_r0) ); // bx r0
+
+ // align cache.pos to 32 bytes
+ if ((((Bitu)cache.pos) & 0x1f) != 0) {
+ cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f));
+ }
+
+ *(Bit32u*)pos1 = LDR_IMM(FC_SEGS_ADDR, HOST_pc, cache.pos - (pos1 + 8)); // ldr FC_SEGS_ADDR, [pc, #(&Segs)]
+ cache_addd((Bit32u)&Segs); // address of "Segs"
+
+ *(Bit32u*)pos2 = LDR_IMM(FC_REGS_ADDR, HOST_pc, cache.pos - (pos2 + 8)); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)]
+ cache_addd((Bit32u)&cpu_regs); // address of "cpu_regs"
+
+ *(Bit32u*)pos3 = LDR_IMM(readdata_addr, HOST_pc, cache.pos - (pos3 + 8)); // ldr readdata_addr, [pc, #(&core_dynrec.readdata)]
+ cache_addd((Bit32u)&core_dynrec.readdata); // address of "core_dynrec.readdata"
+
+ // align cache.pos to 32 bytes
+ if ((((Bitu)cache.pos) & 0x1f) != 0) {
+ cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f));
+ }
+#endif
+}
+
+// return from a function
+static void gen_return_function(void) {
+ cache_addd(0xe8bd8df0); // ldmfd sp!, {v1-v5,v7,v8,pc}
+}
+
+#ifdef DRC_FLAGS_INVALIDATION
+
+// called when a call to a function can be replaced by a
+// call to a simpler function
+static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) {
+#ifdef DRC_FLAGS_INVALIDATION_DCODE
+ // try to avoid function calls but rather directly fill in code
+ switch (flags_type) {
+ case t_ADDb:
+ case t_ADDw:
+ case t_ADDd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=ADD_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // add FC_RETOP, a1, a2
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=ORR_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // orr FC_RETOP, a1, a2
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=AND_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // and FC_RETOP, a1, a2
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ case t_SUBb:
+ case t_SUBw:
+ case t_SUBd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=SUB_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // sub FC_RETOP, a1, a2
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=EOR_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // eor FC_RETOP, a1, a2
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ case t_CMPb:
+ case t_CMPw:
+ case t_CMPd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=NOP; // nop
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ case t_INCb:
+ case t_INCw:
+ case t_INCd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=ADD_IMM(FC_RETOP, HOST_a1, 1, 0); // add FC_RETOP, a1, #1
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ case t_DECb:
+ case t_DECw:
+ case t_DECd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=SUB_IMM(FC_RETOP, HOST_a1, 1, 0); // sub FC_RETOP, a1, #1
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ case t_SHLb:
+ case t_SHLw:
+ case t_SHLd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=MOV_REG_LSL_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsl a2
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ case t_SHRb:
+ *(Bit32u*)pos=NOP; // nop
+#if C_TARGETCPU == ARMV7LE
+ *(Bit32u*)(pos+4)=BFC(HOST_a1, 8, 24); // bfc a1, 8, 24
+ *(Bit32u*)(pos+8)=MOV_REG_LSR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsr a2
+#else
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=AND_IMM(FC_RETOP, HOST_a1, 0xff, 0); // and FC_RETOP, a1, #0xff
+ *(Bit32u*)(pos+12)=MOV_REG_LSR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, lsr a2
+#endif
+ break;
+ case t_SHRw:
+ *(Bit32u*)pos=NOP; // nop
+#if C_TARGETCPU == ARMV7LE
+ *(Bit32u*)(pos+4)=BFC(HOST_a1, 16, 16); // bfc a1, 16, 16
+ *(Bit32u*)(pos+8)=MOV_REG_LSR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsr a2
+#else
+ *(Bit32u*)(pos+4)=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16
+ *(Bit32u*)(pos+8)=MOV_REG_LSR_IMM(FC_RETOP, FC_RETOP, 16); // mov FC_RETOP, FC_RETOP, lsr #16
+ *(Bit32u*)(pos+12)=MOV_REG_LSR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, lsr a2
+#endif
+ break;
+ case t_SHRd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=MOV_REG_LSR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsr a2
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ case t_SARb:
+ *(Bit32u*)pos=NOP; // nop
+#if C_TARGETCPU == ARMV7LE
+ *(Bit32u*)(pos+4)=SXTB(FC_RETOP, HOST_a1, 0); // sxtb FC_RETOP, a1
+ *(Bit32u*)(pos+8)=MOV_REG_ASR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, asr a2
+#else
+ *(Bit32u*)(pos+4)=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 24); // mov FC_RETOP, a1, lsl #24
+ *(Bit32u*)(pos+8)=MOV_REG_ASR_IMM(FC_RETOP, FC_RETOP, 24); // mov FC_RETOP, FC_RETOP, asr #24
+ *(Bit32u*)(pos+12)=MOV_REG_ASR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, asr a2
+#endif
+ break;
+ case t_SARw:
+ *(Bit32u*)pos=NOP; // nop
+#if C_TARGETCPU == ARMV7LE
+ *(Bit32u*)(pos+4)=SXTH(FC_RETOP, HOST_a1, 0); // sxth FC_RETOP, a1
+ *(Bit32u*)(pos+8)=MOV_REG_ASR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, asr a2
+#else
+ *(Bit32u*)(pos+4)=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16
+ *(Bit32u*)(pos+8)=MOV_REG_ASR_IMM(FC_RETOP, FC_RETOP, 16); // mov FC_RETOP, FC_RETOP, asr #16
+ *(Bit32u*)(pos+12)=MOV_REG_ASR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, asr a2
+#endif
+ break;
+ case t_SARd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=MOV_REG_ASR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, asr a2
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ case t_RORb:
+#if C_TARGETCPU == ARMV7LE
+ *(Bit32u*)pos=BFI(HOST_a1, HOST_a1, 8, 8); // bfi a1, a1, 8, 8
+ *(Bit32u*)(pos+4)=BFI(HOST_a1, HOST_a1, 16, 16); // bfi a1, a1, 16, 16
+ *(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2
+#else
+ *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 24); // mov FC_RETOP, a1, lsl #24
+ *(Bit32u*)(pos+4)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 8); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #8
+ *(Bit32u*)(pos+8)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16
+ *(Bit32u*)(pos+12)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2
+#endif
+ break;
+ case t_RORw:
+ *(Bit32u*)pos=NOP; // nop
+#if C_TARGETCPU == ARMV7LE
+ *(Bit32u*)(pos+4)=BFI(HOST_a1, HOST_a1, 16, 16); // bfi a1, a1, 16, 16
+ *(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2
+#else
+ *(Bit32u*)(pos+4)=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16
+ *(Bit32u*)(pos+8)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16
+ *(Bit32u*)(pos+12)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2
+#endif
+ break;
+ case t_RORd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ case t_ROLw:
+#if C_TARGETCPU == ARMV7LE
+ *(Bit32u*)pos=BFI(HOST_a1, HOST_a1, 16, 16); // bfi a1, a1, 16, 16
+ *(Bit32u*)(pos+4)=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32
+ *(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2
+#else
+ *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16
+ *(Bit32u*)(pos+4)=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32
+ *(Bit32u*)(pos+8)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16
+ *(Bit32u*)(pos+12)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2
+#endif
+ break;
+ case t_ROLd:
+ *(Bit32u*)pos=NOP; // nop
+#if C_TARGETCPU == ARMV7LE
+ *(Bit32u*)(pos+4)=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32
+ *(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2
+#else
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32
+ *(Bit32u*)(pos+12)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2
+#endif
+ break;
+ case t_NEGb:
+ case t_NEGw:
+ case t_NEGd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=RSB_IMM(FC_RETOP, HOST_a1, 0, 0); // rsb FC_RETOP, a1, #0
+#if C_TARGETCPU != ARMV7LE
+ *(Bit32u*)(pos+12)=NOP; // nop
+#endif
+ break;
+ default:
+#if C_TARGETCPU == ARMV7LE
+ *(Bit32u*)pos=MOVW(temp1, ((Bit32u)fct_ptr) & 0xffff); // movw temp1, #(fct_ptr & 0xffff)
+ *(Bit32u*)(pos+4)=MOVT(temp1, ((Bit32u)fct_ptr) >> 16); // movt temp1, #(fct_ptr >> 16)
+#else
+ *(Bit32u*)(pos+12)=(Bit32u)fct_ptr; // simple_func
+#endif
+ break;
+
+ }
+#else
+#if C_TARGETCPU == ARMV7LE
+ *(Bit32u*)pos=MOVW(temp1, ((Bit32u)fct_ptr) & 0xffff); // movw temp1, #(fct_ptr & 0xffff)
+ *(Bit32u*)(pos+4)=MOVT(temp1, ((Bit32u)fct_ptr) >> 16); // movt temp1, #(fct_ptr >> 16)
+#else
+ *(Bit32u*)(pos+12)=(Bit32u)fct_ptr; // simple_func
+#endif
+#endif
+}
+#endif
+
+static void cache_block_before_close(void) { }
+
+#ifdef DRC_USE_SEGS_ADDR
+
+// mov 16bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_seg16_to_reg(HostReg dest_reg,Bitu index) {
+ cache_addd( LDRH_IMM(dest_reg, FC_SEGS_ADDR, index) ); // ldrh dest_reg, [FC_SEGS_ADDR, #index]
+}
+
+// mov 32bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_seg32_to_reg(HostReg dest_reg,Bitu index) {
+ cache_addd( LDR_IMM(dest_reg, FC_SEGS_ADDR, index) ); // ldr dest_reg, [FC_SEGS_ADDR, #index]
+}
+
+// add a 32bit value from Segs[index] to a full register using FC_SEGS_ADDR (index modulo 4 must be zero)
+static void gen_add_seg32_to_reg(HostReg reg,Bitu index) {
+ cache_addd( LDR_IMM(temp1, FC_SEGS_ADDR, index) ); // ldr temp1, [FC_SEGS_ADDR, #index]
+ cache_addd( ADD_REG_LSL_IMM(reg, reg, temp1, 0) ); // add reg, reg, temp1
+}
+
+#endif
+
+#ifdef DRC_USE_REGS_ADDR
+
+// mov 16bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_regval16_to_reg(HostReg dest_reg,Bitu index) {
+ cache_addd( LDRH_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrh dest_reg, [FC_REGS_ADDR, #index]
+}
+
+// mov 32bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_regval32_to_reg(HostReg dest_reg,Bitu index) {
+ cache_addd( LDR_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldr dest_reg, [FC_REGS_ADDR, #index]
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_regword_to_reg(HostReg dest_reg,Bitu index,bool dword) {
+ if (dword) {
+ cache_addd( LDR_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldr dest_reg, [FC_REGS_ADDR, #index]
+ } else {
+ cache_addd( LDRH_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrh dest_reg, [FC_REGS_ADDR, #index]
+ }
+}
+
+// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_regbyte_to_reg_low(HostReg dest_reg,Bitu index) {
+ cache_addd( LDRB_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrb dest_reg, [FC_REGS_ADDR, #index]
+}
+
+// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void gen_mov_regbyte_to_reg_low_canuseword(HostReg dest_reg,Bitu index) {
+ cache_addd( LDRB_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrb dest_reg, [FC_REGS_ADDR, #index]
+}
+
+
+// add a 32bit value from cpu_regs[index] to a full register using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_add_regval32_to_reg(HostReg reg,Bitu index) {
+ cache_addd( LDR_IMM(temp2, FC_REGS_ADDR, index) ); // ldr temp2, [FC_REGS_ADDR, #index]
+ cache_addd( ADD_REG_LSL_IMM(reg, reg, temp2, 0) ); // add reg, reg, temp2
+}
+
+
+// move 16bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 2 must be zero)
+static void gen_mov_regval16_from_reg(HostReg src_reg,Bitu index) {
+ cache_addd( STRH_IMM(src_reg, FC_REGS_ADDR, index) ); // strh src_reg, [FC_REGS_ADDR, #index]
+}
+
+// move 32bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_regval32_from_reg(HostReg src_reg,Bitu index) {
+ cache_addd( STR_IMM(src_reg, FC_REGS_ADDR, index) ); // str src_reg, [FC_REGS_ADDR, #index]
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into cpu_regs[index] using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
+static void gen_mov_regword_from_reg(HostReg src_reg,Bitu index,bool dword) {
+ if (dword) {
+ cache_addd( STR_IMM(src_reg, FC_REGS_ADDR, index) ); // str src_reg, [FC_REGS_ADDR, #index]
+ } else {
+ cache_addd( STRH_IMM(src_reg, FC_REGS_ADDR, index) ); // strh src_reg, [FC_REGS_ADDR, #index]
+ }
+}
+
+// move the lowest 8bit of a register into cpu_regs[index] using FC_REGS_ADDR
+static void gen_mov_regbyte_from_reg_low(HostReg src_reg,Bitu index) {
+ cache_addd( STRB_IMM(src_reg, FC_REGS_ADDR, index) ); // strb src_reg, [FC_REGS_ADDR, #index]
+}
+
+#endif
diff --git a/src/cpu/core_dynrec/risc_armv4le-thumb-iw.h b/src/cpu/core_dynrec/risc_armv4le-thumb-iw.h
new file mode 100644
index 000000000..400ae9bed
--- /dev/null
+++ b/src/cpu/core_dynrec/risc_armv4le-thumb-iw.h
@@ -0,0 +1,1505 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+/* ARMv4 (little endian) backend by M-HT (thumb version with data pool, requires -mthumb-interwork switch when compiling dosbox) */
+
+
+// temporary "lo" registers
+#define templo1 HOST_v3
+#define templo2 HOST_v4
+#define templo3 HOST_v2
+
+// register that holds function return values
+#define FC_RETOP HOST_a1
+
+// register used for address calculations,
+#define FC_ADDR HOST_v1 // has to be saved across calls, see DRC_PROTECT_ADDR_REG
+
+// register that holds the first parameter
+#define FC_OP1 HOST_a1
+
+// register that holds the second parameter
+#define FC_OP2 HOST_a2
+
+// special register that holds the third parameter for _R3 calls (byte accessible)
+#define FC_OP3 HOST_a4
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA1 HOST_a1
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA2 HOST_a2
+
+// temporary register for LEA
+#define TEMP_REG_DRC HOST_a4
+
+// used to hold the address of "cpu_regs" - preferably filled in function gen_run_code
+#define FC_REGS_ADDR HOST_v7
+
+// used to hold the address of "Segs" - preferably filled in function gen_run_code
+#define FC_SEGS_ADDR HOST_v8
+
+// used to hold the address of "core_dynrec.readdata" - filled in function gen_run_code
+#define readdata_addr HOST_v5
+
+
+// instruction encodings
+
+// move
+// mov dst, #imm @ 0 <= imm <= 255
+#define MOV_IMM(dst, imm) (0x2000 + ((dst) << 8) + (imm) )
+// mov dst, src
+#define MOV_REG(dst, src) ADD_IMM3(dst, src, 0)
+// mov dst, src
+#define MOV_LO_HI(dst, src) (0x4640 + (dst) + (((src) - HOST_r8) << 3) )
+// mov dst, src
+#define MOV_HI_LO(dst, src) (0x4680 + ((dst) - HOST_r8) + ((src) << 3) )
+
+// arithmetic
+// add dst, src, #imm @ 0 <= imm <= 7
+#define ADD_IMM3(dst, src, imm) (0x1c00 + (dst) + ((src) << 3) + ((imm) << 6) )
+// add dst, #imm @ 0 <= imm <= 255
+#define ADD_IMM8(dst, imm) (0x3000 + ((dst) << 8) + (imm) )
+// add dst, src1, src2
+#define ADD_REG(dst, src1, src2) (0x1800 + (dst) + ((src1) << 3) + ((src2) << 6) )
+// add dst, pc, #imm @ 0 <= imm < 1024 & imm mod 4 = 0
+#define ADD_LO_PC_IMM(dst, imm) (0xa000 + ((dst) << 8) + ((imm) >> 2) )
+// sub dst, src1, src2
+#define SUB_REG(dst, src1, src2) (0x1a00 + (dst) + ((src1) << 3) + ((src2) << 6) )
+// sub dst, src, #imm @ 0 <= imm <= 7
+#define SUB_IMM3(dst, src, imm) (0x1e00 + (dst) + ((src) << 3) + ((imm) << 6) )
+// sub dst, #imm @ 0 <= imm <= 255
+#define SUB_IMM8(dst, imm) (0x3800 + ((dst) << 8) + (imm) )
+// neg dst, src
+#define NEG(dst, src) (0x4240 + (dst) + ((src) << 3) )
+// cmp dst, #imm @ 0 <= imm <= 255
+#define CMP_IMM(dst, imm) (0x2800 + ((dst) << 8) + (imm) )
+// nop
+#define NOP (0x46c0)
+
+// logical
+// and dst, src
+#define AND(dst, src) (0x4000 + (dst) + ((src) << 3) )
+// bic dst, src
+#define BIC(dst, src) (0x4380 + (dst) + ((src) << 3) )
+// eor dst, src
+#define EOR(dst, src) (0x4040 + (dst) + ((src) << 3) )
+// orr dst, src
+#define ORR(dst, src) (0x4300 + (dst) + ((src) << 3) )
+// mvn dst, src
+#define MVN(dst, src) (0x43c0 + (dst) + ((src) << 3) )
+
+// shift/rotate
+// lsl dst, src, #imm
+#define LSL_IMM(dst, src, imm) (0x0000 + (dst) + ((src) << 3) + ((imm) << 6) )
+// lsl dst, reg
+#define LSL_REG(dst, reg) (0x4080 + (dst) + ((reg) << 3) )
+// lsr dst, src, #imm
+#define LSR_IMM(dst, src, imm) (0x0800 + (dst) + ((src) << 3) + ((imm) << 6) )
+// lsr dst, reg
+#define LSR_REG(dst, reg) (0x40c0 + (dst) + ((reg) << 3) )
+// asr dst, src, #imm
+#define ASR_IMM(dst, src, imm) (0x1000 + (dst) + ((src) << 3) + ((imm) << 6) )
+// asr dst, reg
+#define ASR_REG(dst, reg) (0x4100 + (dst) + ((reg) << 3) )
+// ror dst, reg
+#define ROR_REG(dst, reg) (0x41c0 + (dst) + ((reg) << 3) )
+
+// load
+// ldr reg, [addr, #imm] @ 0 <= imm < 128 & imm mod 4 = 0
+#define LDR_IMM(reg, addr, imm) (0x6800 + (reg) + ((addr) << 3) + ((imm) << 4) )
+// ldrh reg, [addr, #imm] @ 0 <= imm < 64 & imm mod 2 = 0
+#define LDRH_IMM(reg, addr, imm) (0x8800 + (reg) + ((addr) << 3) + ((imm) << 5) )
+// ldrb reg, [addr, #imm] @ 0 <= imm < 32
+#define LDRB_IMM(reg, addr, imm) (0x7800 + (reg) + ((addr) << 3) + ((imm) << 6) )
+// ldr reg, [pc, #imm] @ 0 <= imm < 1024 & imm mod 4 = 0
+#define LDR_PC_IMM(reg, imm) (0x4800 + ((reg) << 8) + ((imm) >> 2) )
+// ldr reg, [addr1, addr2]
+#define LDR_REG(reg, addr1, addr2) (0x5800 + (reg) + ((addr1) << 3) + ((addr2) << 6) )
+
+// store
+// str reg, [addr, #imm] @ 0 <= imm < 128 & imm mod 4 = 0
+#define STR_IMM(reg, addr, imm) (0x6000 + (reg) + ((addr) << 3) + ((imm) << 4) )
+// strh reg, [addr, #imm] @ 0 <= imm < 64 & imm mod 2 = 0
+#define STRH_IMM(reg, addr, imm) (0x8000 + (reg) + ((addr) << 3) + ((imm) << 5) )
+// strb reg, [addr, #imm] @ 0 <= imm < 32
+#define STRB_IMM(reg, addr, imm) (0x7000 + (reg) + ((addr) << 3) + ((imm) << 6) )
+
+// branch
+// beq pc+imm @ 0 <= imm < 256 & imm mod 2 = 0
+#define BEQ_FWD(imm) (0xd000 + ((imm) >> 1) )
+// bne pc+imm @ 0 <= imm < 256 & imm mod 2 = 0
+#define BNE_FWD(imm) (0xd100 + ((imm) >> 1) )
+// bgt pc+imm @ 0 <= imm < 256 & imm mod 2 = 0
+#define BGT_FWD(imm) (0xdc00 + ((imm) >> 1) )
+// b pc+imm @ 0 <= imm < 2048 & imm mod 2 = 0
+#define B_FWD(imm) (0xe000 + ((imm) >> 1) )
+// bx reg
+#define BX(reg) (0x4700 + ((reg) << 3) )
+
+
+// arm instructions
+
+// arithmetic
+// add dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define ARM_ADD_IMM(dst, src, imm, rimm) (0xe2800000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
+
+// load
+// ldr reg, [addr, #imm] @ 0 <= imm < 4096
+#define ARM_LDR_IMM(reg, addr, imm) (0xe5900000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+
+// store
+// str reg, [addr, #-(imm)]! @ 0 <= imm < 4096
+#define ARM_STR_IMM_M_W(reg, addr, imm) (0xe5200000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+
+// branch
+// bx reg
+#define ARM_BX(reg) (0xe12fff10 + (reg) )
+
+
+// data pool defines
+#define CACHE_DATA_JUMP (2)
+#define CACHE_DATA_ALIGN (32)
+#define CACHE_DATA_MIN (32)
+#define CACHE_DATA_MAX (288)
+
+// data pool variables
+static Bit8u * cache_datapos = NULL; // position of data pool in the cache block
+static Bit32u cache_datasize = 0; // total size of data pool
+static Bit32u cache_dataindex = 0; // used size of data pool = index of free data item (in bytes) in data pool
+
+
+// forwarded function
+static void INLINE gen_create_branch_short(void * func);
+
+// function to check distance to data pool
+// if too close, then generate jump after data pool
+static void cache_checkinstr(Bit32u size) {
+ if (cache_datasize == 0) {
+ if (cache_datapos != NULL) {
+ if (cache.pos + size + CACHE_DATA_JUMP >= cache_datapos) {
+ cache_datapos = NULL;
+ }
+ }
+ return;
+ }
+
+ if (cache.pos + size + CACHE_DATA_JUMP <= cache_datapos) return;
+
+ {
+ register Bit8u * newcachepos;
+
+ newcachepos = cache_datapos + cache_datasize;
+ gen_create_branch_short(newcachepos);
+ cache.pos = newcachepos;
+ }
+
+ if (cache.pos + CACHE_DATA_MAX + CACHE_DATA_ALIGN >= cache.block.active->cache.start + cache.block.active->cache.size &&
+ cache.pos + CACHE_DATA_MIN + CACHE_DATA_ALIGN + (CACHE_DATA_ALIGN - CACHE_ALIGN) < cache.block.active->cache.start + cache.block.active->cache.size)
+ {
+ cache_datapos = (Bit8u *) (((Bitu)cache.block.active->cache.start + cache.block.active->cache.size - CACHE_DATA_ALIGN) & ~(CACHE_DATA_ALIGN - 1));
+ } else {
+ register Bit32u cachemodsize;
+
+ cachemodsize = (cache.pos - cache.block.active->cache.start) & (CACHE_MAXSIZE - 1);
+
+ if (cachemodsize + CACHE_DATA_MAX + CACHE_DATA_ALIGN <= CACHE_MAXSIZE ||
+ cachemodsize + CACHE_DATA_MIN + CACHE_DATA_ALIGN + (CACHE_DATA_ALIGN - CACHE_ALIGN) > CACHE_MAXSIZE)
+ {
+ cache_datapos = (Bit8u *) (((Bitu)cache.pos + CACHE_DATA_MAX) & ~(CACHE_DATA_ALIGN - 1));
+ } else {
+ cache_datapos = (Bit8u *) (((Bitu)cache.pos + (CACHE_MAXSIZE - CACHE_DATA_ALIGN) - cachemodsize) & ~(CACHE_DATA_ALIGN - 1));
+ }
+ }
+
+ cache_datasize = 0;
+ cache_dataindex = 0;
+}
+
+// function to reserve item in data pool
+// returns address of item
+static Bit8u * cache_reservedata(void) {
+ // if data pool not yet initialized, then initialize data pool
+ if (GCC_UNLIKELY(cache_datapos == NULL)) {
+ if (cache.pos + CACHE_DATA_MIN + CACHE_DATA_ALIGN < cache.block.active->cache.start + CACHE_DATA_MAX) {
+ cache_datapos = (Bit8u *) (((Bitu)cache.block.active->cache.start + CACHE_DATA_MAX) & ~(CACHE_DATA_ALIGN - 1));
+ }
+ }
+
+ // if data pool not yet used, then set data pool
+ if (cache_datasize == 0) {
+ // set data pool address is too close (or behind) cache.pos then set new data pool size
+ if (cache.pos + CACHE_DATA_MIN + CACHE_DATA_JUMP /*+ CACHE_DATA_ALIGN*/ > cache_datapos) {
+ if (cache.pos + CACHE_DATA_MAX + CACHE_DATA_ALIGN >= cache.block.active->cache.start + cache.block.active->cache.size &&
+ cache.pos + CACHE_DATA_MIN + CACHE_DATA_ALIGN + (CACHE_DATA_ALIGN - CACHE_ALIGN) < cache.block.active->cache.start + cache.block.active->cache.size)
+ {
+ cache_datapos = (Bit8u *) (((Bitu)cache.block.active->cache.start + cache.block.active->cache.size - CACHE_DATA_ALIGN) & ~(CACHE_DATA_ALIGN - 1));
+ } else {
+ register Bit32u cachemodsize;
+
+ cachemodsize = (cache.pos - cache.block.active->cache.start) & (CACHE_MAXSIZE - 1);
+
+ if (cachemodsize + CACHE_DATA_MAX + CACHE_DATA_ALIGN <= CACHE_MAXSIZE ||
+ cachemodsize + CACHE_DATA_MIN + CACHE_DATA_ALIGN + (CACHE_DATA_ALIGN - CACHE_ALIGN) > CACHE_MAXSIZE)
+ {
+ cache_datapos = (Bit8u *) (((Bitu)cache.pos + CACHE_DATA_MAX) & ~(CACHE_DATA_ALIGN - 1));
+ } else {
+ cache_datapos = (Bit8u *) (((Bitu)cache.pos + (CACHE_MAXSIZE - CACHE_DATA_ALIGN) - cachemodsize) & ~(CACHE_DATA_ALIGN - 1));
+ }
+ }
+ }
+ // set initial data pool size
+ cache_datasize = CACHE_DATA_ALIGN;
+ }
+
+ // if data pool is full, then enlarge data pool
+ if (cache_dataindex == cache_datasize) {
+ cache_datasize += CACHE_DATA_ALIGN;
+ }
+
+ cache_dataindex += 4;
+ return (cache_datapos + (cache_dataindex - 4));
+}
+
+static void cache_block_before_close(void) {
+ // if data pool in use, then resize cache block to include the data pool
+ if (cache_datasize != 0)
+ {
+ cache.pos = cache_datapos + cache_dataindex;
+ }
+
+ // clear the values before next use
+ cache_datapos = NULL;
+ cache_datasize = 0;
+ cache_dataindex = 0;
+}
+
+
+// move a full register from reg_src to reg_dst
+static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) {
+ if(reg_src == reg_dst) return;
+ cache_checkinstr(2);
+ cache_addw( MOV_REG(reg_dst, reg_src) ); // mov reg_dst, reg_src
+}
+
+// helper function
+static bool val_single_shift(Bit32u value, Bit32u *val_shift) {
+ Bit32u shift;
+
+ if (GCC_UNLIKELY(value == 0)) {
+ *val_shift = 0;
+ return true;
+ }
+
+ shift = 0;
+ while ((value & 1) == 0) {
+ value>>=1;
+ shift+=1;
+ }
+
+ if ((value >> 8) != 0) return false;
+
+ *val_shift = shift;
+ return true;
+}
+
+// move a 32bit constant value into dest_reg
+static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) {
+ Bit32u scale;
+
+ if (imm < 256) {
+ cache_checkinstr(2);
+ cache_addw( MOV_IMM(dest_reg, imm) ); // mov dest_reg, #(imm)
+ } else if ((~imm) < 256) {
+ cache_checkinstr(4);
+ cache_addw( MOV_IMM(dest_reg, ~imm) ); // mov dest_reg, #(~imm)
+ cache_addw( MVN(dest_reg, dest_reg) ); // mvn dest_reg, dest_reg
+ } else if (val_single_shift(imm, &scale)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_IMM(dest_reg, imm >> scale) ); // mov dest_reg, #(imm >> scale)
+ cache_addw( LSL_IMM(dest_reg, dest_reg, scale) ); // lsl dest_reg, dest_reg, #scale
+ } else {
+ Bit32u diff;
+
+ cache_checkinstr(4);
+
+ diff = imm - ((Bit32u)cache.pos+4);
+
+ if ((diff < 1024) && ((imm & 0x03) == 0)) {
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( ADD_LO_PC_IMM(dest_reg, diff >> 2) ); // add dest_reg, pc, #(diff >> 2)
+ } else {
+ cache_addw( NOP ); // nop
+ cache_addw( ADD_LO_PC_IMM(dest_reg, (diff - 2) >> 2) ); // add dest_reg, pc, #((diff - 2) >> 2)
+ }
+ } else {
+ Bit8u *datapos;
+
+ datapos = cache_reservedata();
+ *(Bit32u*)datapos=imm;
+
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( LDR_PC_IMM(dest_reg, datapos - (cache.pos + 4)) ); // ldr dest_reg, [pc, datapos]
+ } else {
+ cache_addw( LDR_PC_IMM(dest_reg, datapos - (cache.pos + 2)) ); // ldr dest_reg, [pc, datapos]
+ }
+ }
+ }
+}
+
+// helper function
+static bool gen_mov_memval_to_reg_helper(HostReg dest_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) {
+ switch (size) {
+ case 4:
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((data & 3) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 128) && (((data - addr_data) & 3) == 0)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( LDR_IMM(dest_reg, templo2, data - addr_data) ); // ldr dest_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ }
+ break;
+ case 2:
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((data & 1) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 64) && (((data - addr_data) & 1) == 0)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( LDRH_IMM(dest_reg, templo2, data - addr_data) ); // ldrh dest_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ }
+ break;
+ case 1:
+ if ((data >= addr_data) && (data < addr_data + 32)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( LDRB_IMM(dest_reg, templo2, data - addr_data) ); // ldrb dest_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+// helper function
+static bool gen_mov_memval_to_reg(HostReg dest_reg, void *data, Bitu size) {
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true;
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true;
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true;
+ return false;
+}
+
+// helper function for gen_mov_word_to_reg
+static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,HostReg data_reg) {
+ // alignment....
+ if (dword) {
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((Bit32u)data & 3) {
+ if ( ((Bit32u)data & 3) == 2 ) {
+ cache_checkinstr(8);
+ cache_addw( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg]
+ cache_addw( LDRH_IMM(templo1, data_reg, 2) ); // ldrh templo1, [data_reg, #2]
+ cache_addw( LSL_IMM(templo1, templo1, 16) ); // lsl templo1, templo1, #16
+ cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1
+ } else {
+ cache_checkinstr(16);
+ cache_addw( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg]
+ cache_addw( ADD_IMM3(templo1, data_reg, 1) ); // add templo1, data_reg, #1
+ cache_addw( LDRH_IMM(templo1, templo1, 0) ); // ldrh templo1, [templo1]
+ cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8
+ cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1
+ cache_addw( LDRB_IMM(templo1, data_reg, 3) ); // ldrb templo1, [data_reg, #3]
+ cache_addw( LSL_IMM(templo1, templo1, 24) ); // lsl templo1, templo1, #24
+ cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1
+ }
+ } else
+#endif
+ {
+ cache_checkinstr(2);
+ cache_addw( LDR_IMM(dest_reg, data_reg, 0) ); // ldr dest_reg, [data_reg]
+ }
+ } else {
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((Bit32u)data & 1) {
+ cache_checkinstr(8);
+ cache_addw( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg]
+ cache_addw( LDRB_IMM(templo1, data_reg, 1) ); // ldrb templo1, [data_reg, #1]
+ cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8
+ cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1
+ } else
+#endif
+ {
+ cache_checkinstr(2);
+ cache_addw( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg]
+ }
+ }
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) {
+ if (!gen_mov_memval_to_reg(dest_reg, data, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(templo2, (Bit32u)data);
+ gen_mov_word_to_reg_helper(dest_reg, data, dword, templo2);
+ }
+}
+
+// move a 16bit constant value into dest_reg
+// the upper 16bit of the destination register may be destroyed
+static void INLINE gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) {
+ gen_mov_dword_to_reg_imm(dest_reg, (Bit32u)imm);
+}
+
+// helper function
+static bool gen_mov_memval_from_reg_helper(HostReg src_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) {
+ switch (size) {
+ case 4:
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((data & 3) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 128) && (((data - addr_data) & 3) == 0)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( STR_IMM(src_reg, templo2, data - addr_data) ); // str src_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ }
+ break;
+ case 2:
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((data & 1) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 64) && (((data - addr_data) & 1) == 0)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( STRH_IMM(src_reg, templo2, data - addr_data) ); // strh src_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ }
+ break;
+ case 1:
+ if ((data >= addr_data) && (data < addr_data + 32)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( STRB_IMM(src_reg, templo2, data - addr_data) ); // strb src_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+// helper function
+static bool gen_mov_memval_from_reg(HostReg src_reg, void *dest, Bitu size) {
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true;
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true;
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true;
+ return false;
+}
+
+// helper function for gen_mov_word_from_reg
+static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, HostReg data_reg) {
+ // alignment....
+ if (dword) {
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((Bit32u)dest & 3) {
+ if ( ((Bit32u)dest & 3) == 2 ) {
+ cache_checkinstr(8);
+ cache_addw( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 16) ); // lsr templo1, templo1, #16
+ cache_addw( STRH_IMM(templo1, data_reg, 2) ); // strh templo1, [data_reg, #2]
+ } else {
+ cache_checkinstr(20);
+ cache_addw( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 8) ); // lsr templo1, templo1, #8
+ cache_addw( STRB_IMM(templo1, data_reg, 1) ); // strb templo1, [data_reg, #1]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 16) ); // lsr templo1, templo1, #16
+ cache_addw( STRB_IMM(templo1, data_reg, 2) ); // strb templo1, [data_reg, #2]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 24) ); // lsr templo1, templo1, #24
+ cache_addw( STRB_IMM(templo1, data_reg, 3) ); // strb templo1, [data_reg, #3]
+ }
+ } else
+#endif
+ {
+ cache_checkinstr(2);
+ cache_addw( STR_IMM(src_reg, data_reg, 0) ); // str src_reg, [data_reg]
+ }
+ } else {
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((Bit32u)dest & 1) {
+ cache_checkinstr(8);
+ cache_addw( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 8) ); // lsr templo1, templo1, #8
+ cache_addw( STRB_IMM(templo1, data_reg, 1) ); // strb templo1, [data_reg, #1]
+ } else
+#endif
+ {
+ cache_checkinstr(2);
+ cache_addw( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg]
+ }
+ }
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into memory
+static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) {
+ if (!gen_mov_memval_from_reg(src_reg, dest, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest);
+ gen_mov_word_from_reg_helper(src_reg, dest, dword, templo2);
+ }
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) {
+ if (!gen_mov_memval_to_reg(dest_reg, data, 1)) {
+ gen_mov_dword_to_reg_imm(templo1, (Bit32u)data);
+ cache_checkinstr(2);
+ cache_addw( LDRB_IMM(dest_reg, templo1, 0) ); // ldrb dest_reg, [templo1]
+ }
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) {
+ gen_mov_byte_to_reg_low(dest_reg, data);
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) {
+ cache_checkinstr(2);
+ cache_addw( MOV_IMM(dest_reg, imm) ); // mov dest_reg, #(imm)
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) {
+ gen_mov_byte_to_reg_low_imm(dest_reg, imm);
+}
+
+// move the lowest 8bit of a register into memory
+static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) {
+ if (!gen_mov_memval_from_reg(src_reg, dest, 1)) {
+ gen_mov_dword_to_reg_imm(templo1, (Bit32u)dest);
+ cache_checkinstr(2);
+ cache_addw( STRB_IMM(src_reg, templo1, 0) ); // strb src_reg, [templo1]
+ }
+}
+
+
+
+// convert an 8bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_byte(bool sign,HostReg reg) {
+ cache_checkinstr(4);
+ cache_addw( LSL_IMM(reg, reg, 24) ); // lsl reg, reg, #24
+
+ if (sign) {
+ cache_addw( ASR_IMM(reg, reg, 24) ); // asr reg, reg, #24
+ } else {
+ cache_addw( LSR_IMM(reg, reg, 24) ); // lsr reg, reg, #24
+ }
+}
+
+// convert a 16bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_word(bool sign,HostReg reg) {
+ cache_checkinstr(4);
+ cache_addw( LSL_IMM(reg, reg, 16) ); // lsl reg, reg, #16
+
+ if (sign) {
+ cache_addw( ASR_IMM(reg, reg, 16) ); // asr reg, reg, #16
+ } else {
+ cache_addw( LSR_IMM(reg, reg, 16) ); // lsr reg, reg, #16
+ }
+}
+
+// add a 32bit value from memory to a full register
+static void gen_add(HostReg reg,void* op) {
+ gen_mov_word_to_reg(templo3, op, 1);
+ cache_checkinstr(2);
+ cache_addw( ADD_REG(reg, reg, templo3) ); // add reg, reg, templo3
+}
+
+// add a 32bit constant value to a full register
+static void gen_add_imm(HostReg reg,Bit32u imm) {
+ Bit32u imm2, scale;
+
+ if(!imm) return;
+
+ imm2 = (Bit32u) (-((Bit32s)imm));
+
+ if (imm <= 255) {
+ cache_checkinstr(2);
+ cache_addw( ADD_IMM8(reg, imm) ); // add reg, #imm
+ } else if (imm2 <= 255) {
+ cache_checkinstr(2);
+ cache_addw( SUB_IMM8(reg, imm2) ); // sub reg, #(-imm)
+ } else {
+ if (val_single_shift(imm2, &scale)) {
+ cache_checkinstr((scale)?6:4);
+ cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale)
+ if (scale) {
+ cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale
+ }
+ cache_addw( SUB_REG(reg, reg, templo1) ); // sub reg, reg, templo1
+ } else {
+ gen_mov_dword_to_reg_imm(templo1, imm);
+ cache_checkinstr(2);
+ cache_addw( ADD_REG(reg, reg, templo1) ); // add reg, reg, templo1
+ }
+ }
+}
+
+// and a 32bit constant value with a full register
+static void gen_and_imm(HostReg reg,Bit32u imm) {
+ Bit32u imm2, scale;
+
+ imm2 = ~imm;
+ if(!imm2) return;
+
+ if (!imm) {
+ cache_checkinstr(2);
+ cache_addw( MOV_IMM(reg, 0) ); // mov reg, #0
+ } else {
+ if (val_single_shift(imm2, &scale)) {
+ cache_checkinstr((scale)?6:4);
+ cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale)
+ if (scale) {
+ cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale
+ }
+ cache_addw( BIC(reg, templo1) ); // bic reg, templo1
+ } else {
+ gen_mov_dword_to_reg_imm(templo1, imm);
+ cache_checkinstr(2);
+ cache_addw( AND(reg, templo1) ); // and reg, templo1
+ }
+ }
+}
+
+
+// move a 32bit constant value into memory
+static void gen_mov_direct_dword(void* dest,Bit32u imm) {
+ gen_mov_dword_to_reg_imm(templo3, imm);
+ gen_mov_word_from_reg(templo3, dest, 1);
+}
+
+// move an address into memory
+static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) {
+ gen_mov_direct_dword(dest,(Bit32u)imm);
+}
+
+// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value
+static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) {
+ if (!dword) imm &= 0xffff;
+ if(!imm) return;
+
+ if (!gen_mov_memval_to_reg(templo3, dest, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest);
+ gen_mov_word_to_reg_helper(templo3, dest, dword, templo2);
+ }
+ gen_add_imm(templo3, imm);
+ if (!gen_mov_memval_from_reg(templo3, dest, (dword)?4:2)) {
+ gen_mov_word_from_reg_helper(templo3, dest, dword, templo2);
+ }
+}
+
+// add an 8bit constant value to a dword memory value
+static void gen_add_direct_byte(void* dest,Bit8s imm) {
+ gen_add_direct_word(dest, (Bit32s)imm, 1);
+}
+
+// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value
+static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) {
+ Bit32u imm2, scale;
+
+ if (!dword) imm &= 0xffff;
+ if(!imm) return;
+
+ if (!gen_mov_memval_to_reg(templo3, dest, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest);
+ gen_mov_word_to_reg_helper(templo3, dest, dword, templo2);
+ }
+
+ imm2 = (Bit32u) (-((Bit32s)imm));
+
+ if (imm <= 255) {
+ cache_checkinstr(2);
+ cache_addw( SUB_IMM8(templo3, imm) ); // sub templo3, #imm
+ } else if (imm2 <= 255) {
+ cache_checkinstr(2);
+ cache_addw( ADD_IMM8(templo3, imm2) ); // add templo3, #(-imm)
+ } else {
+ if (val_single_shift(imm2, &scale)) {
+ cache_checkinstr((scale)?6:4);
+ cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale)
+ if (scale) {
+ cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale
+ }
+ cache_addw( ADD_REG(templo3, templo3, templo1) ); // add templo3, templo3, templo1
+ } else {
+ gen_mov_dword_to_reg_imm(templo1, imm);
+ cache_checkinstr(2);
+ cache_addw( SUB_REG(templo3, templo3, templo1) ); // sub templo3, templo3, templo1
+ }
+ }
+
+ if (!gen_mov_memval_from_reg(templo3, dest, (dword)?4:2)) {
+ gen_mov_word_from_reg_helper(templo3, dest, dword, templo2);
+ }
+}
+
+// subtract an 8bit constant value from a dword memory value
+static void gen_sub_direct_byte(void* dest,Bit8s imm) {
+ gen_sub_direct_word(dest, (Bit32s)imm, 1);
+}
+
+// effective address calculation, destination is dest_reg
+// scale_reg is scaled by scale (scale_reg*(2^scale)) and
+// added to dest_reg, then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) {
+ if (scale) {
+ cache_checkinstr(4);
+ cache_addw( LSL_IMM(templo1, scale_reg, scale) ); // lsl templo1, scale_reg, #(scale)
+ cache_addw( ADD_REG(dest_reg, dest_reg, templo1) ); // add dest_reg, dest_reg, templo1
+ } else {
+ cache_checkinstr(2);
+ cache_addw( ADD_REG(dest_reg, dest_reg, scale_reg) ); // add dest_reg, dest_reg, scale_reg
+ }
+ gen_add_imm(dest_reg, imm);
+}
+
+// effective address calculation, destination is dest_reg
+// dest_reg is scaled by scale (dest_reg*(2^scale)),
+// then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) {
+ if (scale) {
+ cache_checkinstr(2);
+ cache_addw( LSL_IMM(dest_reg, dest_reg, scale) ); // lsl dest_reg, dest_reg, #(scale)
+ }
+ gen_add_imm(dest_reg, imm);
+}
+
+// helper function for gen_call_function_raw and gen_call_function_setup
+static void gen_call_function_helper(void * func) {
+ Bit8u *datapos;
+
+ datapos = cache_reservedata();
+ *(Bit32u*)datapos=(Bit32u)func;
+
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( LDR_PC_IMM(templo1, datapos - (cache.pos + 4)) ); // ldr templo1, [pc, datapos]
+ cache_addw( ADD_LO_PC_IMM(templo2, 8) ); // adr templo2, after_call (add templo2, pc, #8)
+ cache_addw( ADD_IMM8(templo2, 1) ); // add templo2, #1
+ cache_addw( MOV_HI_LO(HOST_lr, templo2) ); // mov lr, templo2
+ cache_addw( BX(templo1) ); // bx templo1 --- switch to arm state
+ cache_addw( NOP ); // nop
+ } else {
+ cache_addw( LDR_PC_IMM(templo1, datapos - (cache.pos + 2)) ); // ldr templo1, [pc, datapos]
+ cache_addw( ADD_LO_PC_IMM(templo2, 4) ); // adr templo2, after_call (add templo2, pc, #4)
+ cache_addw( ADD_IMM8(templo2, 1) ); // add templo2, #1
+ cache_addw( MOV_HI_LO(HOST_lr, templo2) ); // mov lr, templo2
+ cache_addw( BX(templo1) ); // bx templo1 --- switch to arm state
+ }
+ // after_call:
+
+ // thumb state from now on
+}
+
+// generate a call to a parameterless function
+static void INLINE gen_call_function_raw(void * func) {
+ cache_checkinstr(12);
+ gen_call_function_helper(func);
+}
+
+// generate a call to a function with paramcount parameters
+// note: the parameters are loaded in the architecture specific way
+// using the gen_load_param_ functions below
+static Bit32u INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) {
+ cache_checkinstr(12);
+ Bit32u proc_addr = (Bit32u)cache.pos;
+ gen_call_function_helper(func);
+ return proc_addr;
+ // if proc_addr is on word boundary ((proc_addr & 0x03) == 0)
+ // then length of generated code is 12 bytes
+ // otherwise length of generated code is 10 bytes
+}
+
+#if (1)
+// max of 4 parameters in a1-a4
+
+// load an immediate value as param'th function parameter
+static void INLINE gen_load_param_imm(Bitu imm,Bitu param) {
+ gen_mov_dword_to_reg_imm(param, imm);
+}
+
+// load an address as param'th function parameter
+static void INLINE gen_load_param_addr(Bitu addr,Bitu param) {
+ gen_mov_dword_to_reg_imm(param, addr);
+}
+
+// load a host-register as param'th function parameter
+static void INLINE gen_load_param_reg(Bitu reg,Bitu param) {
+ gen_mov_regs(param, reg);
+}
+
+// load a value from memory as param'th function parameter
+static void INLINE gen_load_param_mem(Bitu mem,Bitu param) {
+ gen_mov_word_to_reg(param, (void *)mem, 1);
+}
+#else
+ other arm abis
+#endif
+
+// jump to an address pointed at by ptr, offset is in imm
+static void gen_jmp_ptr(void * ptr,Bits imm=0) {
+ gen_mov_word_to_reg(templo3, ptr, 1);
+
+#if !defined(C_UNALIGNED_MEMORY)
+// (*ptr) should be word aligned
+ if ((imm & 0x03) == 0) {
+#endif
+ if ((imm >= 0) && (imm < 128) && ((imm & 3) == 0)) {
+ cache_checkinstr(6);
+ cache_addw( LDR_IMM(templo2, templo3, imm) ); // ldr templo2, [templo3, #imm]
+ } else {
+ gen_mov_dword_to_reg_imm(templo2, imm);
+ cache_checkinstr(6);
+ cache_addw( LDR_REG(templo2, templo3, templo2) ); // ldr templo2, [templo3, templo2]
+ }
+#if !defined(C_UNALIGNED_MEMORY)
+ } else {
+ gen_add_imm(templo3, imm);
+
+ cache_checkinstr(24);
+ cache_addw( LDRB_IMM(templo2, templo3, 0) ); // ldrb templo2, [templo3]
+ cache_addw( LDRB_IMM(templo1, templo3, 1) ); // ldrb templo1, [templo3, #1]
+ cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8
+ cache_addw( ORR(templo2, templo1) ); // orr templo2, templo1
+ cache_addw( LDRB_IMM(templo1, templo3, 2) ); // ldrb templo1, [templo3, #2]
+ cache_addw( LSL_IMM(templo1, templo1, 16) ); // lsl templo1, templo1, #16
+ cache_addw( ORR(templo2, templo1) ); // orr templo2, templo1
+ cache_addw( LDRB_IMM(templo1, templo3, 3) ); // ldrb templo1, [templo3, #3]
+ cache_addw( LSL_IMM(templo1, templo1, 24) ); // lsl templo1, templo1, #24
+ cache_addw( ORR(templo2, templo1) ); // orr templo2, templo1
+ }
+#endif
+
+ // increase jmp address to keep thumb state
+ cache_addw( ADD_IMM3(templo2, templo2, 1) ); // add templo2, templo2, #1
+
+ cache_addw( BX(templo2) ); // bx templo2
+}
+
+// short conditional jump (+-127 bytes) if register is zero
+// the destination is set by gen_fill_branch() later
+static Bit32u gen_create_branch_on_zero(HostReg reg,bool dword) {
+ cache_checkinstr(4);
+ if (dword) {
+ cache_addw( CMP_IMM(reg, 0) ); // cmp reg, #0
+ } else {
+ cache_addw( LSL_IMM(templo1, reg, 16) ); // lsl templo1, reg, #16
+ }
+ cache_addw( BEQ_FWD(0) ); // beq j
+ return ((Bit32u)cache.pos-2);
+}
+
+// short conditional jump (+-127 bytes) if register is nonzero
+// the destination is set by gen_fill_branch() later
+static Bit32u gen_create_branch_on_nonzero(HostReg reg,bool dword) {
+ cache_checkinstr(4);
+ if (dword) {
+ cache_addw( CMP_IMM(reg, 0) ); // cmp reg, #0
+ } else {
+ cache_addw( LSL_IMM(templo1, reg, 16) ); // lsl templo1, reg, #16
+ }
+ cache_addw( BNE_FWD(0) ); // bne j
+ return ((Bit32u)cache.pos-2);
+}
+
+// calculate relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch(DRC_PTR_SIZE_IM data) {
+#if C_DEBUG
+ Bits len=(Bit32u)cache.pos-(data+4);
+ if (len<0) len=-len;
+ if (len>252) LOG_MSG("Big jump %d",len);
+#endif
+ *(Bit8u*)data=(Bit8u)( ((Bit32u)cache.pos-(data+4)) >> 1 );
+}
+
+
+// conditional jump if register is nonzero
+// for isdword==true the 32bit of the register are tested
+// for isdword==false the lowest 8bit of the register are tested
+static Bit32u gen_create_branch_long_nonzero(HostReg reg,bool isdword) {
+ Bit8u *datapos;
+
+ cache_checkinstr(8);
+ datapos = cache_reservedata();
+
+ if (isdword) {
+ cache_addw( CMP_IMM(reg, 0) ); // cmp reg, #0
+ } else {
+ cache_addw( LSL_IMM(templo2, reg, 24) ); // lsl templo2, reg, #24
+ }
+ cache_addw( BEQ_FWD(2) ); // beq nobranch (pc+2)
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( LDR_PC_IMM(templo1, datapos - (cache.pos + 4)) ); // ldr templo1, [pc, datapos]
+ } else {
+ cache_addw( LDR_PC_IMM(templo1, datapos - (cache.pos + 2)) ); // ldr templo1, [pc, datapos]
+ }
+ cache_addw( BX(templo1) ); // bx templo1
+ // nobranch:
+ return ((Bit32u)datapos);
+}
+
+// compare 32bit-register against zero and jump if value less/equal than zero
+static Bit32u gen_create_branch_long_leqzero(HostReg reg) {
+ Bit8u *datapos;
+
+ cache_checkinstr(8);
+ datapos = cache_reservedata();
+
+ cache_addw( CMP_IMM(reg, 0) ); // cmp reg, #0
+ cache_addw( BGT_FWD(2) ); // bgt nobranch (pc+2)
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( LDR_PC_IMM(templo1, datapos - (cache.pos + 4)) ); // ldr templo1, [pc, datapos]
+ } else {
+ cache_addw( LDR_PC_IMM(templo1, datapos - (cache.pos + 2)) ); // ldr templo1, [pc, datapos]
+ }
+ cache_addw( BX(templo1) ); // bx templo1
+ // nobranch:
+ return ((Bit32u)datapos);
+}
+
+// calculate long relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch_long(Bit32u data) {
+ // this is an absolute branch
+ *(Bit32u*)data=((Bit32u)cache.pos) + 1; // add 1 to keep processor in thumb state
+}
+
+static void gen_run_code(void) {
+ Bit8u *pos1, *pos2, *pos3;
+
+#if (__ARM_EABI__)
+ // 8-byte stack alignment
+ cache_addd(0xe92d4ff0); // stmfd sp!, {v1-v8,lr}
+#else
+ cache_addd(0xe92d4df0); // stmfd sp!, {v1-v5,v7,v8,lr}
+#endif
+
+ cache_addd( ARM_ADD_IMM(HOST_r0, HOST_r0, 1, 0) ); // add r0, r0, #1
+
+ pos1 = cache.pos;
+ cache_addd( 0 );
+ pos2 = cache.pos;
+ cache_addd( 0 );
+ pos3 = cache.pos;
+ cache_addd( 0 );
+
+ cache_addd( ARM_ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4
+ cache_addd( ARM_STR_IMM_M_W(HOST_lr, HOST_sp, 4) ); // str lr, [sp, #-4]!
+ cache_addd( ARM_BX(HOST_r0) ); // bx r0
+
+#if (__ARM_EABI__)
+ cache_addd(0xe8bd4ff0); // ldmfd sp!, {v1-v8,lr}
+#else
+ cache_addd(0xe8bd4df0); // ldmfd sp!, {v1-v5,v7,v8,lr}
+#endif
+ cache_addd( ARM_BX(HOST_lr) ); // bx lr
+
+ // align cache.pos to 32 bytes
+ if ((((Bitu)cache.pos) & 0x1f) != 0) {
+ cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f));
+ }
+
+ *(Bit32u*)pos1 = ARM_LDR_IMM(FC_SEGS_ADDR, HOST_pc, cache.pos - (pos1 + 8)); // ldr FC_SEGS_ADDR, [pc, #(&Segs)]
+ cache_addd((Bit32u)&Segs); // address of "Segs"
+
+ *(Bit32u*)pos2 = ARM_LDR_IMM(FC_REGS_ADDR, HOST_pc, cache.pos - (pos2 + 8)); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)]
+ cache_addd((Bit32u)&cpu_regs); // address of "cpu_regs"
+
+ *(Bit32u*)pos3 = ARM_LDR_IMM(readdata_addr, HOST_pc, cache.pos - (pos3 + 8)); // ldr readdata_addr, [pc, #(&core_dynrec.readdata)]
+ cache_addd((Bit32u)&core_dynrec.readdata); // address of "core_dynrec.readdata"
+
+ // align cache.pos to 32 bytes
+ if ((((Bitu)cache.pos) & 0x1f) != 0) {
+ cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f));
+ }
+}
+
+// return from a function
+static void gen_return_function(void) {
+ cache_checkinstr(4);
+ cache_addw(0xbc08); // pop {r3}
+ cache_addw( BX(HOST_r3) ); // bx r3
+}
+
+
+// short unconditional jump (over data pool)
+// must emit at most CACHE_DATA_JUMP bytes
+static void INLINE gen_create_branch_short(void * func) {
+ cache_addw( B_FWD((Bit32u)func - ((Bit32u)cache.pos + 4)) ); // b func
+}
+
+
+#ifdef DRC_FLAGS_INVALIDATION
+
+// called when a call to a function can be replaced by a
+// call to a simpler function
+static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) {
+ if ((*(Bit16u*)pos & 0xf000) == 0xe000) {
+ if ((*(Bit16u*)pos & 0x0fff) >= ((CACHE_DATA_ALIGN / 2) - 1) &&
+ (*(Bit16u*)pos & 0x0fff) < 0x0800)
+ {
+ pos = (Bit8u *) ( ( ( (Bit32u)(*(Bit16u*)pos & 0x0fff) ) << 1 ) + ((Bit32u)pos + 4) );
+ }
+ }
+
+#ifdef DRC_FLAGS_INVALIDATION_DCODE
+ if (((Bit32u)pos & 0x03) == 0)
+ {
+ // try to avoid function calls but rather directly fill in code
+ switch (flags_type) {
+ case t_ADDb:
+ case t_ADDw:
+ case t_ADDd:
+ *(Bit16u*)pos=ADD_REG(HOST_a1, HOST_a1, HOST_a2); // add a1, a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ *(Bit16u*)pos=ORR(HOST_a1, HOST_a2); // orr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ *(Bit16u*)pos=AND(HOST_a1, HOST_a2); // and a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_SUBb:
+ case t_SUBw:
+ case t_SUBd:
+ *(Bit16u*)pos=SUB_REG(HOST_a1, HOST_a1, HOST_a2); // sub a1, a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ *(Bit16u*)pos=EOR(HOST_a1, HOST_a2); // eor a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_CMPb:
+ case t_CMPw:
+ case t_CMPd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ *(Bit16u*)pos=B_FWD(8); // b after_call (pc+8)
+ break;
+ case t_INCb:
+ case t_INCw:
+ case t_INCd:
+ *(Bit16u*)pos=ADD_IMM3(HOST_a1, HOST_a1, 1); // add a1, a1, #1
+ *(Bit16u*)(pos+2)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_DECb:
+ case t_DECw:
+ case t_DECd:
+ *(Bit16u*)pos=SUB_IMM3(HOST_a1, HOST_a1, 1); // sub a1, a1, #1
+ *(Bit16u*)(pos+2)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_SHLb:
+ case t_SHLw:
+ case t_SHLd:
+ *(Bit16u*)pos=LSL_REG(HOST_a1, HOST_a2); // lsl a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_SHRb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=NOP; // nop
+ *(Bit16u*)(pos+4)=LSR_IMM(HOST_a1, HOST_a1, 24); // lsr a1, a1, #24
+ *(Bit16u*)(pos+6)=NOP; // nop
+ *(Bit16u*)(pos+8)=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+10)=NOP; // nop
+ break;
+ case t_SHRw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=NOP; // nop
+ *(Bit16u*)(pos+4)=LSR_IMM(HOST_a1, HOST_a1, 16); // lsr a1, a1, #16
+ *(Bit16u*)(pos+6)=NOP; // nop
+ *(Bit16u*)(pos+8)=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+10)=NOP; // nop
+ break;
+ case t_SHRd:
+ *(Bit16u*)pos=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_SARb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=NOP; // nop
+ *(Bit16u*)(pos+4)=ASR_IMM(HOST_a1, HOST_a1, 24); // asr a1, a1, #24
+ *(Bit16u*)(pos+6)=NOP; // nop
+ *(Bit16u*)(pos+8)=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+10)=NOP; // nop
+ break;
+ case t_SARw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=NOP; // nop
+ *(Bit16u*)(pos+4)=ASR_IMM(HOST_a1, HOST_a1, 16); // asr a1, a1, #16
+ *(Bit16u*)(pos+6)=NOP; // nop
+ *(Bit16u*)(pos+8)=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+10)=NOP; // nop
+ break;
+ case t_SARd:
+ *(Bit16u*)pos=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_RORb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=LSR_IMM(templo1, HOST_a1, 8); // lsr templo1, a1, #8
+ *(Bit16u*)(pos+4)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+6)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+8)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+10)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ break;
+ case t_RORw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+4)=NOP; // nop
+ *(Bit16u*)(pos+6)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+8)=NOP; // nop
+ *(Bit16u*)(pos+10)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ break;
+ case t_RORd:
+ *(Bit16u*)pos=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_ROLw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+4)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+6)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+8)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+10)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ break;
+ case t_ROLd:
+ *(Bit16u*)pos=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+2)=NOP; // nop
+ *(Bit16u*)(pos+4)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+6)=NOP; // nop
+ *(Bit16u*)(pos+8)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+10)=NOP; // nop
+ break;
+ case t_NEGb:
+ case t_NEGw:
+ case t_NEGd:
+ *(Bit16u*)pos=NEG(HOST_a1, HOST_a1); // neg a1, a1
+ *(Bit16u*)(pos+2)=B_FWD(6); // b after_call (pc+6)
+ break;
+ default:
+ *(Bit32u*)( ( ((Bit32u) (*pos)) << 2 ) + ((Bit32u)pos + 4) ) = (Bit32u)fct_ptr; // simple_func
+ break;
+ }
+ }
+ else
+ {
+ // try to avoid function calls but rather directly fill in code
+ switch (flags_type) {
+ case t_ADDb:
+ case t_ADDw:
+ case t_ADDd:
+ *(Bit16u*)pos=ADD_REG(HOST_a1, HOST_a1, HOST_a2); // add a1, a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ *(Bit16u*)pos=ORR(HOST_a1, HOST_a2); // orr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ *(Bit16u*)pos=AND(HOST_a1, HOST_a2); // and a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_SUBb:
+ case t_SUBw:
+ case t_SUBd:
+ *(Bit16u*)pos=SUB_REG(HOST_a1, HOST_a1, HOST_a2); // sub a1, a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ *(Bit16u*)pos=EOR(HOST_a1, HOST_a2); // eor a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_CMPb:
+ case t_CMPw:
+ case t_CMPd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ *(Bit16u*)pos=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_INCb:
+ case t_INCw:
+ case t_INCd:
+ *(Bit16u*)pos=ADD_IMM3(HOST_a1, HOST_a1, 1); // add a1, a1, #1
+ *(Bit16u*)(pos+2)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_DECb:
+ case t_DECw:
+ case t_DECd:
+ *(Bit16u*)pos=SUB_IMM3(HOST_a1, HOST_a1, 1); // sub a1, a1, #1
+ *(Bit16u*)(pos+2)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_SHLb:
+ case t_SHLw:
+ case t_SHLd:
+ *(Bit16u*)pos=LSL_REG(HOST_a1, HOST_a2); // lsl a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_SHRb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=NOP; // nop
+ *(Bit16u*)(pos+4)=LSR_IMM(HOST_a1, HOST_a1, 24); // lsr a1, a1, #24
+ *(Bit16u*)(pos+6)=NOP; // nop
+ *(Bit16u*)(pos+8)=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ break;
+ case t_SHRw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=NOP; // nop
+ *(Bit16u*)(pos+4)=LSR_IMM(HOST_a1, HOST_a1, 16); // lsr a1, a1, #16
+ *(Bit16u*)(pos+6)=NOP; // nop
+ *(Bit16u*)(pos+8)=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ break;
+ case t_SHRd:
+ *(Bit16u*)pos=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_SARb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=NOP; // nop
+ *(Bit16u*)(pos+4)=ASR_IMM(HOST_a1, HOST_a1, 24); // asr a1, a1, #24
+ *(Bit16u*)(pos+6)=NOP; // nop
+ *(Bit16u*)(pos+8)=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ break;
+ case t_SARw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=NOP; // nop
+ *(Bit16u*)(pos+4)=ASR_IMM(HOST_a1, HOST_a1, 16); // asr a1, a1, #16
+ *(Bit16u*)(pos+6)=NOP; // nop
+ *(Bit16u*)(pos+8)=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ break;
+ case t_SARd:
+ *(Bit16u*)pos=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_RORw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+4)=NOP; // nop
+ *(Bit16u*)(pos+6)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+8)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ break;
+ case t_RORd:
+ *(Bit16u*)pos=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_ROLd:
+ *(Bit16u*)pos=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+2)=NOP; // nop
+ *(Bit16u*)(pos+4)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+6)=NOP; // nop
+ *(Bit16u*)(pos+8)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ break;
+ case t_NEGb:
+ case t_NEGw:
+ case t_NEGd:
+ *(Bit16u*)pos=NEG(HOST_a1, HOST_a1); // neg a1, a1
+ *(Bit16u*)(pos+2)=B_FWD(4); // b after_call (pc+4)
+ break;
+ default:
+ *(Bit32u*)( ( ((Bit32u) (*pos)) << 2 ) + ((Bit32u)pos + 2) ) = (Bit32u)fct_ptr; // simple_func
+ break;
+ }
+
+ }
+#else
+ if (((Bit32u)pos & 0x03) == 0)
+ {
+ *(Bit32u*)( ( ((Bit32u) (*pos)) << 2 ) + ((Bit32u)pos + 4) ) = (Bit32u)fct_ptr; // simple_func
+ }
+ else
+ {
+ *(Bit32u*)( ( ((Bit32u) (*pos)) << 2 ) + ((Bit32u)pos + 2) ) = (Bit32u)fct_ptr; // simple_func
+ }
+#endif
+}
+#endif
+
+#ifdef DRC_USE_SEGS_ADDR
+
+// mov 16bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_seg16_to_reg(HostReg dest_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo1, FC_SEGS_ADDR) ); // mov templo1, FC_SEGS_ADDR
+ cache_addw( LDRH_IMM(dest_reg, templo1, index) ); // ldrh dest_reg, [templo1, #index]
+}
+
+// mov 32bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_seg32_to_reg(HostReg dest_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo1, FC_SEGS_ADDR) ); // mov templo1, FC_SEGS_ADDR
+ cache_addw( LDR_IMM(dest_reg, templo1, index) ); // ldr dest_reg, [templo1, #index]
+}
+
+// add a 32bit value from Segs[index] to a full register using FC_SEGS_ADDR (index modulo 4 must be zero)
+static void gen_add_seg32_to_reg(HostReg reg,Bitu index) {
+ cache_checkinstr(6);
+ cache_addw( MOV_LO_HI(templo1, FC_SEGS_ADDR) ); // mov templo1, FC_SEGS_ADDR
+ cache_addw( LDR_IMM(templo2, templo1, index) ); // ldr templo2, [templo1, #index]
+ cache_addw( ADD_REG(reg, reg, templo2) ); // add reg, reg, templo2
+}
+
+#endif
+
+#ifdef DRC_USE_REGS_ADDR
+
+// mov 16bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_regval16_to_reg(HostReg dest_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDRH_IMM(dest_reg, templo2, index) ); // ldrh dest_reg, [templo2, #index]
+}
+
+// mov 32bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_regval32_to_reg(HostReg dest_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDR_IMM(dest_reg, templo2, index) ); // ldr dest_reg, [templo2, #index]
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_regword_to_reg(HostReg dest_reg,Bitu index,bool dword) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ if (dword) {
+ cache_addw( LDR_IMM(dest_reg, templo2, index) ); // ldr dest_reg, [templo2, #index]
+ } else {
+ cache_addw( LDRH_IMM(dest_reg, templo2, index) ); // ldrh dest_reg, [templo2, #index]
+ }
+}
+
+// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_regbyte_to_reg_low(HostReg dest_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDRB_IMM(dest_reg, templo2, index) ); // ldrb dest_reg, [templo2, #index]
+}
+
+// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_regbyte_to_reg_low_canuseword(HostReg dest_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDRB_IMM(dest_reg, templo2, index) ); // ldrb dest_reg, [templo2, #index]
+}
+
+
+// add a 32bit value from cpu_regs[index] to a full register using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_add_regval32_to_reg(HostReg reg,Bitu index) {
+ cache_checkinstr(6);
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDR_IMM(templo1, templo2, index) ); // ldr templo1, [templo2, #index]
+ cache_addw( ADD_REG(reg, reg, templo1) ); // add reg, reg, templo1
+}
+
+
+// move 16bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 2 must be zero)
+static void gen_mov_regval16_from_reg(HostReg src_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo1, FC_REGS_ADDR) ); // mov templo1, FC_REGS_ADDR
+ cache_addw( STRH_IMM(src_reg, templo1, index) ); // strh src_reg, [templo1, #index]
+}
+
+// move 32bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_regval32_from_reg(HostReg src_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo1, FC_REGS_ADDR) ); // mov templo1, FC_REGS_ADDR
+ cache_addw( STR_IMM(src_reg, templo1, index) ); // str src_reg, [templo1, #index]
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into cpu_regs[index] using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
+static void gen_mov_regword_from_reg(HostReg src_reg,Bitu index,bool dword) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo1, FC_REGS_ADDR) ); // mov templo1, FC_REGS_ADDR
+ if (dword) {
+ cache_addw( STR_IMM(src_reg, templo1, index) ); // str src_reg, [templo1, #index]
+ } else {
+ cache_addw( STRH_IMM(src_reg, templo1, index) ); // strh src_reg, [templo1, #index]
+ }
+}
+
+// move the lowest 8bit of a register into cpu_regs[index] using FC_REGS_ADDR
+static void gen_mov_regbyte_from_reg_low(HostReg src_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo1, FC_REGS_ADDR) ); // mov templo1, FC_REGS_ADDR
+ cache_addw( STRB_IMM(src_reg, templo1, index) ); // strb src_reg, [templo1, #index]
+}
+
+#endif
diff --git a/src/cpu/core_dynrec/risc_armv4le-thumb-niw.h b/src/cpu/core_dynrec/risc_armv4le-thumb-niw.h
new file mode 100644
index 000000000..c6e0c9f25
--- /dev/null
+++ b/src/cpu/core_dynrec/risc_armv4le-thumb-niw.h
@@ -0,0 +1,1538 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+/* ARMv4 (little endian) backend by M-HT (thumb version with data pool) */
+
+
+// temporary "lo" registers
+#define templo1 HOST_v3
+#define templo2 HOST_v4
+#define templo3 HOST_v2
+
+// register that holds function return values
+#define FC_RETOP HOST_a1
+
+// register used for address calculations,
+#define FC_ADDR HOST_v1 // has to be saved across calls, see DRC_PROTECT_ADDR_REG
+
+// register that holds the first parameter
+#define FC_OP1 HOST_a1
+
+// register that holds the second parameter
+#define FC_OP2 HOST_a2
+
+// special register that holds the third parameter for _R3 calls (byte accessible)
+#define FC_OP3 HOST_a4
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA1 HOST_a1
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA2 HOST_a2
+
+// temporary register for LEA
+#define TEMP_REG_DRC HOST_a4
+
+// used to hold the address of "cpu_regs" - preferably filled in function gen_run_code
+#define FC_REGS_ADDR HOST_v7
+
+// used to hold the address of "Segs" - preferably filled in function gen_run_code
+#define FC_SEGS_ADDR HOST_v8
+
+// used to hold the address of "core_dynrec.readdata" - filled in function gen_run_code
+#define readdata_addr HOST_v5
+
+
+// instruction encodings
+
+// move
+// mov dst, #imm @ 0 <= imm <= 255
+#define MOV_IMM(dst, imm) (0x2000 + ((dst) << 8) + (imm) )
+// mov dst, src
+#define MOV_REG(dst, src) ADD_IMM3(dst, src, 0)
+// mov dst, src
+#define MOV_LO_HI(dst, src) (0x4640 + (dst) + (((src) - HOST_r8) << 3) )
+// mov dst, src
+#define MOV_HI_LO(dst, src) (0x4680 + ((dst) - HOST_r8) + ((src) << 3) )
+
+// arithmetic
+// add dst, src, #imm @ 0 <= imm <= 7
+#define ADD_IMM3(dst, src, imm) (0x1c00 + (dst) + ((src) << 3) + ((imm) << 6) )
+// add dst, #imm @ 0 <= imm <= 255
+#define ADD_IMM8(dst, imm) (0x3000 + ((dst) << 8) + (imm) )
+// add dst, src1, src2
+#define ADD_REG(dst, src1, src2) (0x1800 + (dst) + ((src1) << 3) + ((src2) << 6) )
+// add dst, pc, #imm @ 0 <= imm < 1024 & imm mod 4 = 0
+#define ADD_LO_PC_IMM(dst, imm) (0xa000 + ((dst) << 8) + ((imm) >> 2) )
+// sub dst, src1, src2
+#define SUB_REG(dst, src1, src2) (0x1a00 + (dst) + ((src1) << 3) + ((src2) << 6) )
+// sub dst, src, #imm @ 0 <= imm <= 7
+#define SUB_IMM3(dst, src, imm) (0x1e00 + (dst) + ((src) << 3) + ((imm) << 6) )
+// sub dst, #imm @ 0 <= imm <= 255
+#define SUB_IMM8(dst, imm) (0x3800 + ((dst) << 8) + (imm) )
+// neg dst, src
+#define NEG(dst, src) (0x4240 + (dst) + ((src) << 3) )
+// cmp dst, #imm @ 0 <= imm <= 255
+#define CMP_IMM(dst, imm) (0x2800 + ((dst) << 8) + (imm) )
+// nop
+#define NOP (0x46c0)
+
+// logical
+// and dst, src
+#define AND(dst, src) (0x4000 + (dst) + ((src) << 3) )
+// bic dst, src
+#define BIC(dst, src) (0x4380 + (dst) + ((src) << 3) )
+// eor dst, src
+#define EOR(dst, src) (0x4040 + (dst) + ((src) << 3) )
+// orr dst, src
+#define ORR(dst, src) (0x4300 + (dst) + ((src) << 3) )
+// mvn dst, src
+#define MVN(dst, src) (0x43c0 + (dst) + ((src) << 3) )
+
+// shift/rotate
+// lsl dst, src, #imm
+#define LSL_IMM(dst, src, imm) (0x0000 + (dst) + ((src) << 3) + ((imm) << 6) )
+// lsl dst, reg
+#define LSL_REG(dst, reg) (0x4080 + (dst) + ((reg) << 3) )
+// lsr dst, src, #imm
+#define LSR_IMM(dst, src, imm) (0x0800 + (dst) + ((src) << 3) + ((imm) << 6) )
+// lsr dst, reg
+#define LSR_REG(dst, reg) (0x40c0 + (dst) + ((reg) << 3) )
+// asr dst, src, #imm
+#define ASR_IMM(dst, src, imm) (0x1000 + (dst) + ((src) << 3) + ((imm) << 6) )
+// asr dst, reg
+#define ASR_REG(dst, reg) (0x4100 + (dst) + ((reg) << 3) )
+// ror dst, reg
+#define ROR_REG(dst, reg) (0x41c0 + (dst) + ((reg) << 3) )
+
+// load
+// ldr reg, [addr, #imm] @ 0 <= imm < 128 & imm mod 4 = 0
+#define LDR_IMM(reg, addr, imm) (0x6800 + (reg) + ((addr) << 3) + ((imm) << 4) )
+// ldrh reg, [addr, #imm] @ 0 <= imm < 64 & imm mod 2 = 0
+#define LDRH_IMM(reg, addr, imm) (0x8800 + (reg) + ((addr) << 3) + ((imm) << 5) )
+// ldrb reg, [addr, #imm] @ 0 <= imm < 32
+#define LDRB_IMM(reg, addr, imm) (0x7800 + (reg) + ((addr) << 3) + ((imm) << 6) )
+// ldr reg, [pc, #imm] @ 0 <= imm < 1024 & imm mod 4 = 0
+#define LDR_PC_IMM(reg, imm) (0x4800 + ((reg) << 8) + ((imm) >> 2) )
+// ldr reg, [addr1, addr2]
+#define LDR_REG(reg, addr1, addr2) (0x5800 + (reg) + ((addr1) << 3) + ((addr2) << 6) )
+
+// store
+// str reg, [addr, #imm] @ 0 <= imm < 128 & imm mod 4 = 0
+#define STR_IMM(reg, addr, imm) (0x6000 + (reg) + ((addr) << 3) + ((imm) << 4) )
+// strh reg, [addr, #imm] @ 0 <= imm < 64 & imm mod 2 = 0
+#define STRH_IMM(reg, addr, imm) (0x8000 + (reg) + ((addr) << 3) + ((imm) << 5) )
+// strb reg, [addr, #imm] @ 0 <= imm < 32
+#define STRB_IMM(reg, addr, imm) (0x7000 + (reg) + ((addr) << 3) + ((imm) << 6) )
+
+// branch
+// beq pc+imm @ 0 <= imm < 256 & imm mod 2 = 0
+#define BEQ_FWD(imm) (0xd000 + ((imm) >> 1) )
+// bne pc+imm @ 0 <= imm < 256 & imm mod 2 = 0
+#define BNE_FWD(imm) (0xd100 + ((imm) >> 1) )
+// bgt pc+imm @ 0 <= imm < 256 & imm mod 2 = 0
+#define BGT_FWD(imm) (0xdc00 + ((imm) >> 1) )
+// b pc+imm @ 0 <= imm < 2048 & imm mod 2 = 0
+#define B_FWD(imm) (0xe000 + ((imm) >> 1) )
+// bx reg
+#define BX(reg) (0x4700 + ((reg) << 3) )
+
+
+// arm instructions
+
+// arithmetic
+// add dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define ARM_ADD_IMM(dst, src, imm, rimm) (0xe2800000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
+
+// load
+// ldr reg, [addr, #imm] @ 0 <= imm < 4096
+#define ARM_LDR_IMM(reg, addr, imm) (0xe5900000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+
+// store
+// str reg, [addr, #-(imm)]! @ 0 <= imm < 4096
+#define ARM_STR_IMM_M_W(reg, addr, imm) (0xe5200000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+
+// branch
+// bx reg
+#define ARM_BX(reg) (0xe12fff10 + (reg) )
+
+
+// data pool defines
+#define CACHE_DATA_JUMP (2)
+#define CACHE_DATA_ALIGN (32)
+#define CACHE_DATA_MIN (32)
+#define CACHE_DATA_MAX (288)
+
+// data pool variables
+static Bit8u * cache_datapos = NULL; // position of data pool in the cache block
+static Bit32u cache_datasize = 0; // total size of data pool
+static Bit32u cache_dataindex = 0; // used size of data pool = index of free data item (in bytes) in data pool
+
+
+// forwarded function
+static void INLINE gen_create_branch_short(void * func);
+
+// function to check distance to data pool
+// if too close, then generate jump after data pool
+static void cache_checkinstr(Bit32u size) {
+ if (cache_datasize == 0) {
+ if (cache_datapos != NULL) {
+ if (cache.pos + size + CACHE_DATA_JUMP >= cache_datapos) {
+ cache_datapos = NULL;
+ }
+ }
+ return;
+ }
+
+ if (cache.pos + size + CACHE_DATA_JUMP <= cache_datapos) return;
+
+ {
+ register Bit8u * newcachepos;
+
+ newcachepos = cache_datapos + cache_datasize;
+ gen_create_branch_short(newcachepos);
+ cache.pos = newcachepos;
+ }
+
+ if (cache.pos + CACHE_DATA_MAX + CACHE_DATA_ALIGN >= cache.block.active->cache.start + cache.block.active->cache.size &&
+ cache.pos + CACHE_DATA_MIN + CACHE_DATA_ALIGN + (CACHE_DATA_ALIGN - CACHE_ALIGN) < cache.block.active->cache.start + cache.block.active->cache.size)
+ {
+ cache_datapos = (Bit8u *) (((Bitu)cache.block.active->cache.start + cache.block.active->cache.size - CACHE_DATA_ALIGN) & ~(CACHE_DATA_ALIGN - 1));
+ } else {
+ register Bit32u cachemodsize;
+
+ cachemodsize = (cache.pos - cache.block.active->cache.start) & (CACHE_MAXSIZE - 1);
+
+ if (cachemodsize + CACHE_DATA_MAX + CACHE_DATA_ALIGN <= CACHE_MAXSIZE ||
+ cachemodsize + CACHE_DATA_MIN + CACHE_DATA_ALIGN + (CACHE_DATA_ALIGN - CACHE_ALIGN) > CACHE_MAXSIZE)
+ {
+ cache_datapos = (Bit8u *) (((Bitu)cache.pos + CACHE_DATA_MAX) & ~(CACHE_DATA_ALIGN - 1));
+ } else {
+ cache_datapos = (Bit8u *) (((Bitu)cache.pos + (CACHE_MAXSIZE - CACHE_DATA_ALIGN) - cachemodsize) & ~(CACHE_DATA_ALIGN - 1));
+ }
+ }
+
+ cache_datasize = 0;
+ cache_dataindex = 0;
+}
+
+// function to reserve item in data pool
+// returns address of item
+static Bit8u * cache_reservedata(void) {
+ // if data pool not yet initialized, then initialize data pool
+ if (GCC_UNLIKELY(cache_datapos == NULL)) {
+ if (cache.pos + CACHE_DATA_MIN + CACHE_DATA_ALIGN < cache.block.active->cache.start + CACHE_DATA_MAX) {
+ cache_datapos = (Bit8u *) (((Bitu)cache.block.active->cache.start + CACHE_DATA_MAX) & ~(CACHE_DATA_ALIGN - 1));
+ }
+ }
+
+ // if data pool not yet used, then set data pool
+ if (cache_datasize == 0) {
+ // set data pool address is too close (or behind) cache.pos then set new data pool size
+ if (cache.pos + CACHE_DATA_MIN + CACHE_DATA_JUMP /*+ CACHE_DATA_ALIGN*/ > cache_datapos) {
+ if (cache.pos + CACHE_DATA_MAX + CACHE_DATA_ALIGN >= cache.block.active->cache.start + cache.block.active->cache.size &&
+ cache.pos + CACHE_DATA_MIN + CACHE_DATA_ALIGN + (CACHE_DATA_ALIGN - CACHE_ALIGN) < cache.block.active->cache.start + cache.block.active->cache.size)
+ {
+ cache_datapos = (Bit8u *) (((Bitu)cache.block.active->cache.start + cache.block.active->cache.size - CACHE_DATA_ALIGN) & ~(CACHE_DATA_ALIGN - 1));
+ } else {
+ register Bit32u cachemodsize;
+
+ cachemodsize = (cache.pos - cache.block.active->cache.start) & (CACHE_MAXSIZE - 1);
+
+ if (cachemodsize + CACHE_DATA_MAX + CACHE_DATA_ALIGN <= CACHE_MAXSIZE ||
+ cachemodsize + CACHE_DATA_MIN + CACHE_DATA_ALIGN + (CACHE_DATA_ALIGN - CACHE_ALIGN) > CACHE_MAXSIZE)
+ {
+ cache_datapos = (Bit8u *) (((Bitu)cache.pos + CACHE_DATA_MAX) & ~(CACHE_DATA_ALIGN - 1));
+ } else {
+ cache_datapos = (Bit8u *) (((Bitu)cache.pos + (CACHE_MAXSIZE - CACHE_DATA_ALIGN) - cachemodsize) & ~(CACHE_DATA_ALIGN - 1));
+ }
+ }
+ }
+ // set initial data pool size
+ cache_datasize = CACHE_DATA_ALIGN;
+ }
+
+ // if data pool is full, then enlarge data pool
+ if (cache_dataindex == cache_datasize) {
+ cache_datasize += CACHE_DATA_ALIGN;
+ }
+
+ cache_dataindex += 4;
+ return (cache_datapos + (cache_dataindex - 4));
+}
+
+static void cache_block_before_close(void) {
+ // if data pool in use, then resize cache block to include the data pool
+ if (cache_datasize != 0)
+ {
+ cache.pos = cache_datapos + cache_dataindex;
+ }
+
+ // clear the values before next use
+ cache_datapos = NULL;
+ cache_datasize = 0;
+ cache_dataindex = 0;
+}
+
+
+// move a full register from reg_src to reg_dst
+static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) {
+ if(reg_src == reg_dst) return;
+ cache_checkinstr(2);
+ cache_addw( MOV_REG(reg_dst, reg_src) ); // mov reg_dst, reg_src
+}
+
+// helper function
+static bool val_single_shift(Bit32u value, Bit32u *val_shift) {
+ Bit32u shift;
+
+ if (GCC_UNLIKELY(value == 0)) {
+ *val_shift = 0;
+ return true;
+ }
+
+ shift = 0;
+ while ((value & 1) == 0) {
+ value>>=1;
+ shift+=1;
+ }
+
+ if ((value >> 8) != 0) return false;
+
+ *val_shift = shift;
+ return true;
+}
+
+// move a 32bit constant value into dest_reg
+static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) {
+ Bit32u scale;
+
+ if (imm < 256) {
+ cache_checkinstr(2);
+ cache_addw( MOV_IMM(dest_reg, imm) ); // mov dest_reg, #(imm)
+ } else if ((~imm) < 256) {
+ cache_checkinstr(4);
+ cache_addw( MOV_IMM(dest_reg, ~imm) ); // mov dest_reg, #(~imm)
+ cache_addw( MVN(dest_reg, dest_reg) ); // mvn dest_reg, dest_reg
+ } else if (val_single_shift(imm, &scale)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_IMM(dest_reg, imm >> scale) ); // mov dest_reg, #(imm >> scale)
+ cache_addw( LSL_IMM(dest_reg, dest_reg, scale) ); // lsl dest_reg, dest_reg, #scale
+ } else {
+ Bit32u diff;
+
+ cache_checkinstr(4);
+
+ diff = imm - ((Bit32u)cache.pos+4);
+
+ if ((diff < 1024) && ((imm & 0x03) == 0)) {
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( ADD_LO_PC_IMM(dest_reg, diff >> 2) ); // add dest_reg, pc, #(diff >> 2)
+ } else {
+ cache_addw( NOP ); // nop
+ cache_addw( ADD_LO_PC_IMM(dest_reg, (diff - 2) >> 2) ); // add dest_reg, pc, #((diff - 2) >> 2)
+ }
+ } else {
+ Bit8u *datapos;
+
+ datapos = cache_reservedata();
+ *(Bit32u*)datapos=imm;
+
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( LDR_PC_IMM(dest_reg, datapos - (cache.pos + 4)) ); // ldr dest_reg, [pc, datapos]
+ } else {
+ cache_addw( LDR_PC_IMM(dest_reg, datapos - (cache.pos + 2)) ); // ldr dest_reg, [pc, datapos]
+ }
+ }
+ }
+}
+
+// helper function
+static bool gen_mov_memval_to_reg_helper(HostReg dest_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) {
+ switch (size) {
+ case 4:
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((data & 3) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 128) && (((data - addr_data) & 3) == 0)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( LDR_IMM(dest_reg, templo2, data - addr_data) ); // ldr dest_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ }
+ break;
+ case 2:
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((data & 1) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 64) && (((data - addr_data) & 1) == 0)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( LDRH_IMM(dest_reg, templo2, data - addr_data) ); // ldrh dest_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ }
+ break;
+ case 1:
+ if ((data >= addr_data) && (data < addr_data + 32)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( LDRB_IMM(dest_reg, templo2, data - addr_data) ); // ldrb dest_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+// helper function
+static bool gen_mov_memval_to_reg(HostReg dest_reg, void *data, Bitu size) {
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true;
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true;
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true;
+ return false;
+}
+
+// helper function for gen_mov_word_to_reg
+static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,HostReg data_reg) {
+ // alignment....
+ if (dword) {
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((Bit32u)data & 3) {
+ if ( ((Bit32u)data & 3) == 2 ) {
+ cache_checkinstr(8);
+ cache_addw( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg]
+ cache_addw( LDRH_IMM(templo1, data_reg, 2) ); // ldrh templo1, [data_reg, #2]
+ cache_addw( LSL_IMM(templo1, templo1, 16) ); // lsl templo1, templo1, #16
+ cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1
+ } else {
+ cache_checkinstr(16);
+ cache_addw( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg]
+ cache_addw( ADD_IMM3(templo1, data_reg, 1) ); // add templo1, data_reg, #1
+ cache_addw( LDRH_IMM(templo1, templo1, 0) ); // ldrh templo1, [templo1]
+ cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8
+ cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1
+ cache_addw( LDRB_IMM(templo1, data_reg, 3) ); // ldrb templo1, [data_reg, #3]
+ cache_addw( LSL_IMM(templo1, templo1, 24) ); // lsl templo1, templo1, #24
+ cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1
+ }
+ } else
+#endif
+ {
+ cache_checkinstr(2);
+ cache_addw( LDR_IMM(dest_reg, data_reg, 0) ); // ldr dest_reg, [data_reg]
+ }
+ } else {
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((Bit32u)data & 1) {
+ cache_checkinstr(8);
+ cache_addw( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg]
+ cache_addw( LDRB_IMM(templo1, data_reg, 1) ); // ldrb templo1, [data_reg, #1]
+ cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8
+ cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1
+ } else
+#endif
+ {
+ cache_checkinstr(2);
+ cache_addw( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg]
+ }
+ }
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) {
+ if (!gen_mov_memval_to_reg(dest_reg, data, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(templo2, (Bit32u)data);
+ gen_mov_word_to_reg_helper(dest_reg, data, dword, templo2);
+ }
+}
+
+// move a 16bit constant value into dest_reg
+// the upper 16bit of the destination register may be destroyed
+static void INLINE gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) {
+ gen_mov_dword_to_reg_imm(dest_reg, (Bit32u)imm);
+}
+
+// helper function
+static bool gen_mov_memval_from_reg_helper(HostReg src_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) {
+ switch (size) {
+ case 4:
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((data & 3) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 128) && (((data - addr_data) & 3) == 0)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( STR_IMM(src_reg, templo2, data - addr_data) ); // str src_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ }
+ break;
+ case 2:
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((data & 1) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 64) && (((data - addr_data) & 1) == 0)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( STRH_IMM(src_reg, templo2, data - addr_data) ); // strh src_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ }
+ break;
+ case 1:
+ if ((data >= addr_data) && (data < addr_data + 32)) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( STRB_IMM(src_reg, templo2, data - addr_data) ); // strb src_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+// helper function
+static bool gen_mov_memval_from_reg(HostReg src_reg, void *dest, Bitu size) {
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true;
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true;
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true;
+ return false;
+}
+
+// helper function for gen_mov_word_from_reg
+static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, HostReg data_reg) {
+ // alignment....
+ if (dword) {
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((Bit32u)dest & 3) {
+ if ( ((Bit32u)dest & 3) == 2 ) {
+ cache_checkinstr(8);
+ cache_addw( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 16) ); // lsr templo1, templo1, #16
+ cache_addw( STRH_IMM(templo1, data_reg, 2) ); // strh templo1, [data_reg, #2]
+ } else {
+ cache_checkinstr(20);
+ cache_addw( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 8) ); // lsr templo1, templo1, #8
+ cache_addw( STRB_IMM(templo1, data_reg, 1) ); // strb templo1, [data_reg, #1]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 16) ); // lsr templo1, templo1, #16
+ cache_addw( STRB_IMM(templo1, data_reg, 2) ); // strb templo1, [data_reg, #2]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 24) ); // lsr templo1, templo1, #24
+ cache_addw( STRB_IMM(templo1, data_reg, 3) ); // strb templo1, [data_reg, #3]
+ }
+ } else
+#endif
+ {
+ cache_checkinstr(2);
+ cache_addw( STR_IMM(src_reg, data_reg, 0) ); // str src_reg, [data_reg]
+ }
+ } else {
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((Bit32u)dest & 1) {
+ cache_checkinstr(8);
+ cache_addw( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 8) ); // lsr templo1, templo1, #8
+ cache_addw( STRB_IMM(templo1, data_reg, 1) ); // strb templo1, [data_reg, #1]
+ } else
+#endif
+ {
+ cache_checkinstr(2);
+ cache_addw( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg]
+ }
+ }
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into memory
+static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) {
+ if (!gen_mov_memval_from_reg(src_reg, dest, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest);
+ gen_mov_word_from_reg_helper(src_reg, dest, dword, templo2);
+ }
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) {
+ if (!gen_mov_memval_to_reg(dest_reg, data, 1)) {
+ gen_mov_dword_to_reg_imm(templo1, (Bit32u)data);
+ cache_checkinstr(2);
+ cache_addw( LDRB_IMM(dest_reg, templo1, 0) ); // ldrb dest_reg, [templo1]
+ }
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) {
+ gen_mov_byte_to_reg_low(dest_reg, data);
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) {
+ cache_checkinstr(2);
+ cache_addw( MOV_IMM(dest_reg, imm) ); // mov dest_reg, #(imm)
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) {
+ gen_mov_byte_to_reg_low_imm(dest_reg, imm);
+}
+
+// move the lowest 8bit of a register into memory
+static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) {
+ if (!gen_mov_memval_from_reg(src_reg, dest, 1)) {
+ gen_mov_dword_to_reg_imm(templo1, (Bit32u)dest);
+ cache_checkinstr(2);
+ cache_addw( STRB_IMM(src_reg, templo1, 0) ); // strb src_reg, [templo1]
+ }
+}
+
+
+
+// convert an 8bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_byte(bool sign,HostReg reg) {
+ cache_checkinstr(4);
+ cache_addw( LSL_IMM(reg, reg, 24) ); // lsl reg, reg, #24
+
+ if (sign) {
+ cache_addw( ASR_IMM(reg, reg, 24) ); // asr reg, reg, #24
+ } else {
+ cache_addw( LSR_IMM(reg, reg, 24) ); // lsr reg, reg, #24
+ }
+}
+
+// convert a 16bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_word(bool sign,HostReg reg) {
+ cache_checkinstr(4);
+ cache_addw( LSL_IMM(reg, reg, 16) ); // lsl reg, reg, #16
+
+ if (sign) {
+ cache_addw( ASR_IMM(reg, reg, 16) ); // asr reg, reg, #16
+ } else {
+ cache_addw( LSR_IMM(reg, reg, 16) ); // lsr reg, reg, #16
+ }
+}
+
+// add a 32bit value from memory to a full register
+static void gen_add(HostReg reg,void* op) {
+ gen_mov_word_to_reg(templo3, op, 1);
+ cache_checkinstr(2);
+ cache_addw( ADD_REG(reg, reg, templo3) ); // add reg, reg, templo3
+}
+
+// add a 32bit constant value to a full register
+static void gen_add_imm(HostReg reg,Bit32u imm) {
+ Bit32u imm2, scale;
+
+ if(!imm) return;
+
+ imm2 = (Bit32u) (-((Bit32s)imm));
+
+ if (imm <= 255) {
+ cache_checkinstr(2);
+ cache_addw( ADD_IMM8(reg, imm) ); // add reg, #imm
+ } else if (imm2 <= 255) {
+ cache_checkinstr(2);
+ cache_addw( SUB_IMM8(reg, imm2) ); // sub reg, #(-imm)
+ } else {
+ if (val_single_shift(imm2, &scale)) {
+ cache_checkinstr((scale)?6:4);
+ cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale)
+ if (scale) {
+ cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale
+ }
+ cache_addw( SUB_REG(reg, reg, templo1) ); // sub reg, reg, templo1
+ } else {
+ gen_mov_dword_to_reg_imm(templo1, imm);
+ cache_checkinstr(2);
+ cache_addw( ADD_REG(reg, reg, templo1) ); // add reg, reg, templo1
+ }
+ }
+}
+
+// and a 32bit constant value with a full register
+static void gen_and_imm(HostReg reg,Bit32u imm) {
+ Bit32u imm2, scale;
+
+ imm2 = ~imm;
+ if(!imm2) return;
+
+ if (!imm) {
+ cache_checkinstr(2);
+ cache_addw( MOV_IMM(reg, 0) ); // mov reg, #0
+ } else {
+ if (val_single_shift(imm2, &scale)) {
+ cache_checkinstr((scale)?6:4);
+ cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale)
+ if (scale) {
+ cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale
+ }
+ cache_addw( BIC(reg, templo1) ); // bic reg, templo1
+ } else {
+ gen_mov_dword_to_reg_imm(templo1, imm);
+ cache_checkinstr(2);
+ cache_addw( AND(reg, templo1) ); // and reg, templo1
+ }
+ }
+}
+
+
+// move a 32bit constant value into memory
+static void gen_mov_direct_dword(void* dest,Bit32u imm) {
+ gen_mov_dword_to_reg_imm(templo3, imm);
+ gen_mov_word_from_reg(templo3, dest, 1);
+}
+
+// move an address into memory
+static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) {
+ gen_mov_direct_dword(dest,(Bit32u)imm);
+}
+
+// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value
+static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) {
+ if (!dword) imm &= 0xffff;
+ if(!imm) return;
+
+ if (!gen_mov_memval_to_reg(templo3, dest, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest);
+ gen_mov_word_to_reg_helper(templo3, dest, dword, templo2);
+ }
+ gen_add_imm(templo3, imm);
+ if (!gen_mov_memval_from_reg(templo3, dest, (dword)?4:2)) {
+ gen_mov_word_from_reg_helper(templo3, dest, dword, templo2);
+ }
+}
+
+// add an 8bit constant value to a dword memory value
+static void gen_add_direct_byte(void* dest,Bit8s imm) {
+ gen_add_direct_word(dest, (Bit32s)imm, 1);
+}
+
+// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value
+static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) {
+ Bit32u imm2, scale;
+
+ if (!dword) imm &= 0xffff;
+ if(!imm) return;
+
+ if (!gen_mov_memval_to_reg(templo3, dest, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest);
+ gen_mov_word_to_reg_helper(templo3, dest, dword, templo2);
+ }
+
+ imm2 = (Bit32u) (-((Bit32s)imm));
+
+ if (imm <= 255) {
+ cache_checkinstr(2);
+ cache_addw( SUB_IMM8(templo3, imm) ); // sub templo3, #imm
+ } else if (imm2 <= 255) {
+ cache_checkinstr(2);
+ cache_addw( ADD_IMM8(templo3, imm2) ); // add templo3, #(-imm)
+ } else {
+ if (val_single_shift(imm2, &scale)) {
+ cache_checkinstr((scale)?6:4);
+ cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale)
+ if (scale) {
+ cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale
+ }
+ cache_addw( ADD_REG(templo3, templo3, templo1) ); // add templo3, templo3, templo1
+ } else {
+ gen_mov_dword_to_reg_imm(templo1, imm);
+ cache_checkinstr(2);
+ cache_addw( SUB_REG(templo3, templo3, templo1) ); // sub templo3, templo3, templo1
+ }
+ }
+
+ if (!gen_mov_memval_from_reg(templo3, dest, (dword)?4:2)) {
+ gen_mov_word_from_reg_helper(templo3, dest, dword, templo2);
+ }
+}
+
+// subtract an 8bit constant value from a dword memory value
+static void gen_sub_direct_byte(void* dest,Bit8s imm) {
+ gen_sub_direct_word(dest, (Bit32s)imm, 1);
+}
+
+// effective address calculation, destination is dest_reg
+// scale_reg is scaled by scale (scale_reg*(2^scale)) and
+// added to dest_reg, then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) {
+ if (scale) {
+ cache_checkinstr(4);
+ cache_addw( LSL_IMM(templo1, scale_reg, scale) ); // lsl templo1, scale_reg, #(scale)
+ cache_addw( ADD_REG(dest_reg, dest_reg, templo1) ); // add dest_reg, dest_reg, templo1
+ } else {
+ cache_checkinstr(2);
+ cache_addw( ADD_REG(dest_reg, dest_reg, scale_reg) ); // add dest_reg, dest_reg, scale_reg
+ }
+ gen_add_imm(dest_reg, imm);
+}
+
+// effective address calculation, destination is dest_reg
+// dest_reg is scaled by scale (dest_reg*(2^scale)),
+// then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) {
+ if (scale) {
+ cache_checkinstr(2);
+ cache_addw( LSL_IMM(dest_reg, dest_reg, scale) ); // lsl dest_reg, dest_reg, #(scale)
+ }
+ gen_add_imm(dest_reg, imm);
+}
+
+// helper function for gen_call_function_raw and gen_call_function_setup
+static void gen_call_function_helper(void * func) {
+ Bit8u *datapos;
+
+ datapos = cache_reservedata();
+ *(Bit32u*)datapos=(Bit32u)func;
+
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( LDR_PC_IMM(templo1, datapos - (cache.pos + 4)) ); // ldr templo1, [pc, datapos]
+ cache_addw( ADD_LO_PC_IMM(templo2, 4) ); // adr templo2, after_call (add templo2, pc, #4)
+ cache_addw( MOV_HI_LO(HOST_lr, templo2) ); // mov lr, templo2
+ cache_addw( BX(templo1) ); // bx templo1 --- switch to arm state
+ } else {
+ cache_addw( LDR_PC_IMM(templo1, datapos - (cache.pos + 2)) ); // ldr templo1, [pc, datapos]
+ cache_addw( ADD_LO_PC_IMM(templo2, 4) ); // adr templo2, after_call (add templo2, pc, #4)
+ cache_addw( MOV_HI_LO(HOST_lr, templo2) ); // mov lr, templo2
+ cache_addw( BX(templo1) ); // bx templo1 --- switch to arm state
+ cache_addw( NOP ); // nop
+ }
+ // after_call:
+
+ // switch from arm to thumb state
+ cache_addd(0xe2800000 + (templo1 << 12) + (HOST_pc << 16) + (1)); // add templo1, pc, #1
+ cache_addd(0xe12fff10 + (templo1)); // bx templo1
+
+ // thumb state from now on
+}
+
+// generate a call to a parameterless function
+static void INLINE gen_call_function_raw(void * func) {
+ cache_checkinstr(18);
+ gen_call_function_helper(func);
+}
+
+// generate a call to a function with paramcount parameters
+// note: the parameters are loaded in the architecture specific way
+// using the gen_load_param_ functions below
+static Bit32u INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) {
+ cache_checkinstr(18);
+ Bit32u proc_addr = (Bit32u)cache.pos;
+ gen_call_function_helper(func);
+ return proc_addr;
+ // if proc_addr is on word boundary ((proc_addr & 0x03) == 0)
+ // then length of generated code is 16 bytes
+ // otherwise length of generated code is 18 bytes
+}
+
+#if (1)
+// max of 4 parameters in a1-a4
+
+// load an immediate value as param'th function parameter
+static void INLINE gen_load_param_imm(Bitu imm,Bitu param) {
+ gen_mov_dword_to_reg_imm(param, imm);
+}
+
+// load an address as param'th function parameter
+static void INLINE gen_load_param_addr(Bitu addr,Bitu param) {
+ gen_mov_dword_to_reg_imm(param, addr);
+}
+
+// load a host-register as param'th function parameter
+static void INLINE gen_load_param_reg(Bitu reg,Bitu param) {
+ gen_mov_regs(param, reg);
+}
+
+// load a value from memory as param'th function parameter
+static void INLINE gen_load_param_mem(Bitu mem,Bitu param) {
+ gen_mov_word_to_reg(param, (void *)mem, 1);
+}
+#else
+ other arm abis
+#endif
+
+// jump to an address pointed at by ptr, offset is in imm
+static void gen_jmp_ptr(void * ptr,Bits imm=0) {
+ gen_mov_word_to_reg(templo3, ptr, 1);
+
+#if !defined(C_UNALIGNED_MEMORY)
+// (*ptr) should be word aligned
+ if ((imm & 0x03) == 0) {
+#endif
+ if ((imm >= 0) && (imm < 128) && ((imm & 3) == 0)) {
+ cache_checkinstr(6);
+ cache_addw( LDR_IMM(templo2, templo3, imm) ); // ldr templo2, [templo3, #imm]
+ } else {
+ gen_mov_dword_to_reg_imm(templo2, imm);
+ cache_checkinstr(6);
+ cache_addw( LDR_REG(templo2, templo3, templo2) ); // ldr templo2, [templo3, templo2]
+ }
+#if !defined(C_UNALIGNED_MEMORY)
+ } else {
+ gen_add_imm(templo3, imm);
+
+ cache_checkinstr(24);
+ cache_addw( LDRB_IMM(templo2, templo3, 0) ); // ldrb templo2, [templo3]
+ cache_addw( LDRB_IMM(templo1, templo3, 1) ); // ldrb templo1, [templo3, #1]
+ cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8
+ cache_addw( ORR(templo2, templo1) ); // orr templo2, templo1
+ cache_addw( LDRB_IMM(templo1, templo3, 2) ); // ldrb templo1, [templo3, #2]
+ cache_addw( LSL_IMM(templo1, templo1, 16) ); // lsl templo1, templo1, #16
+ cache_addw( ORR(templo2, templo1) ); // orr templo2, templo1
+ cache_addw( LDRB_IMM(templo1, templo3, 3) ); // ldrb templo1, [templo3, #3]
+ cache_addw( LSL_IMM(templo1, templo1, 24) ); // lsl templo1, templo1, #24
+ cache_addw( ORR(templo2, templo1) ); // orr templo2, templo1
+ }
+#endif
+
+ // increase jmp address to keep thumb state
+ cache_addw( ADD_IMM3(templo2, templo2, 1) ); // add templo2, templo2, #1
+
+ cache_addw( BX(templo2) ); // bx templo2
+}
+
+// short conditional jump (+-127 bytes) if register is zero
+// the destination is set by gen_fill_branch() later
+static Bit32u gen_create_branch_on_zero(HostReg reg,bool dword) {
+ cache_checkinstr(4);
+ if (dword) {
+ cache_addw( CMP_IMM(reg, 0) ); // cmp reg, #0
+ } else {
+ cache_addw( LSL_IMM(templo1, reg, 16) ); // lsl templo1, reg, #16
+ }
+ cache_addw( BEQ_FWD(0) ); // beq j
+ return ((Bit32u)cache.pos-2);
+}
+
+// short conditional jump (+-127 bytes) if register is nonzero
+// the destination is set by gen_fill_branch() later
+static Bit32u gen_create_branch_on_nonzero(HostReg reg,bool dword) {
+ cache_checkinstr(4);
+ if (dword) {
+ cache_addw( CMP_IMM(reg, 0) ); // cmp reg, #0
+ } else {
+ cache_addw( LSL_IMM(templo1, reg, 16) ); // lsl templo1, reg, #16
+ }
+ cache_addw( BNE_FWD(0) ); // bne j
+ return ((Bit32u)cache.pos-2);
+}
+
+// calculate relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch(DRC_PTR_SIZE_IM data) {
+#if C_DEBUG
+ Bits len=(Bit32u)cache.pos-(data+4);
+ if (len<0) len=-len;
+ if (len>252) LOG_MSG("Big jump %d",len);
+#endif
+ *(Bit8u*)data=(Bit8u)( ((Bit32u)cache.pos-(data+4)) >> 1 );
+}
+
+
+// conditional jump if register is nonzero
+// for isdword==true the 32bit of the register are tested
+// for isdword==false the lowest 8bit of the register are tested
+static Bit32u gen_create_branch_long_nonzero(HostReg reg,bool isdword) {
+ Bit8u *datapos;
+
+ cache_checkinstr(8);
+ datapos = cache_reservedata();
+
+ if (isdword) {
+ cache_addw( CMP_IMM(reg, 0) ); // cmp reg, #0
+ } else {
+ cache_addw( LSL_IMM(templo2, reg, 24) ); // lsl templo2, reg, #24
+ }
+ cache_addw( BEQ_FWD(2) ); // beq nobranch (pc+2)
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( LDR_PC_IMM(templo1, datapos - (cache.pos + 4)) ); // ldr templo1, [pc, datapos]
+ } else {
+ cache_addw( LDR_PC_IMM(templo1, datapos - (cache.pos + 2)) ); // ldr templo1, [pc, datapos]
+ }
+ cache_addw( BX(templo1) ); // bx templo1
+ // nobranch:
+ return ((Bit32u)datapos);
+}
+
+// compare 32bit-register against zero and jump if value less/equal than zero
+static Bit32u gen_create_branch_long_leqzero(HostReg reg) {
+ Bit8u *datapos;
+
+ cache_checkinstr(8);
+ datapos = cache_reservedata();
+
+ cache_addw( CMP_IMM(reg, 0) ); // cmp reg, #0
+ cache_addw( BGT_FWD(2) ); // bgt nobranch (pc+2)
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( LDR_PC_IMM(templo1, datapos - (cache.pos + 4)) ); // ldr templo1, [pc, datapos]
+ } else {
+ cache_addw( LDR_PC_IMM(templo1, datapos - (cache.pos + 2)) ); // ldr templo1, [pc, datapos]
+ }
+ cache_addw( BX(templo1) ); // bx templo1
+ // nobranch:
+ return ((Bit32u)datapos);
+}
+
+// calculate long relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch_long(Bit32u data) {
+ // this is an absolute branch
+ *(Bit32u*)data=((Bit32u)cache.pos) + 1; // add 1 to keep processor in thumb state
+}
+
+static void gen_run_code(void) {
+ Bit8u *pos1, *pos2, *pos3;
+
+#if (__ARM_EABI__)
+ // 8-byte stack alignment
+ cache_addd(0xe92d4ff0); // stmfd sp!, {v1-v8,lr}
+#else
+ cache_addd(0xe92d4df0); // stmfd sp!, {v1-v5,v7,v8,lr}
+#endif
+
+ cache_addd( ARM_ADD_IMM(HOST_r0, HOST_r0, 1, 0) ); // add r0, r0, #1
+
+ pos1 = cache.pos;
+ cache_addd( 0 );
+ pos2 = cache.pos;
+ cache_addd( 0 );
+ pos3 = cache.pos;
+ cache_addd( 0 );
+
+ cache_addd( ARM_ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4
+ cache_addd( ARM_STR_IMM_M_W(HOST_lr, HOST_sp, 4) ); // str lr, [sp, #-4]!
+ cache_addd( ARM_BX(HOST_r0) ); // bx r0
+
+#if (__ARM_EABI__)
+ cache_addd(0xe8bd4ff0); // ldmfd sp!, {v1-v8,lr}
+#else
+ cache_addd(0xe8bd4df0); // ldmfd sp!, {v1-v5,v7,v8,lr}
+#endif
+ cache_addd( ARM_BX(HOST_lr) ); // bx lr
+
+ // align cache.pos to 32 bytes
+ if ((((Bitu)cache.pos) & 0x1f) != 0) {
+ cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f));
+ }
+
+ *(Bit32u*)pos1 = ARM_LDR_IMM(FC_SEGS_ADDR, HOST_pc, cache.pos - (pos1 + 8)); // ldr FC_SEGS_ADDR, [pc, #(&Segs)]
+ cache_addd((Bit32u)&Segs); // address of "Segs"
+
+ *(Bit32u*)pos2 = ARM_LDR_IMM(FC_REGS_ADDR, HOST_pc, cache.pos - (pos2 + 8)); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)]
+ cache_addd((Bit32u)&cpu_regs); // address of "cpu_regs"
+
+ *(Bit32u*)pos3 = ARM_LDR_IMM(readdata_addr, HOST_pc, cache.pos - (pos3 + 8)); // ldr readdata_addr, [pc, #(&core_dynrec.readdata)]
+ cache_addd((Bit32u)&core_dynrec.readdata); // address of "core_dynrec.readdata"
+
+ // align cache.pos to 32 bytes
+ if ((((Bitu)cache.pos) & 0x1f) != 0) {
+ cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f));
+ }
+}
+
+// return from a function
+static void gen_return_function(void) {
+ cache_checkinstr(4);
+ cache_addw(0xbc08); // pop {r3}
+ cache_addw( BX(HOST_r3) ); // bx r3
+}
+
+
+// short unconditional jump (over data pool)
+// must emit at most CACHE_DATA_JUMP bytes
+static void INLINE gen_create_branch_short(void * func) {
+ cache_addw( B_FWD((Bit32u)func - ((Bit32u)cache.pos + 4)) ); // b func
+}
+
+
+#ifdef DRC_FLAGS_INVALIDATION
+
+// called when a call to a function can be replaced by a
+// call to a simpler function
+static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) {
+ if ((*(Bit16u*)pos & 0xf000) == 0xe000) {
+ if ((*(Bit16u*)pos & 0x0fff) >= ((CACHE_DATA_ALIGN / 2) - 1) &&
+ (*(Bit16u*)pos & 0x0fff) < 0x0800)
+ {
+ pos = (Bit8u *) ( ( ( (Bit32u)(*(Bit16u*)pos & 0x0fff) ) << 1 ) + ((Bit32u)pos + 4) );
+ }
+ }
+
+#ifdef DRC_FLAGS_INVALIDATION_DCODE
+ if (((Bit32u)pos & 0x03) == 0)
+ {
+ // try to avoid function calls but rather directly fill in code
+ switch (flags_type) {
+ case t_ADDb:
+ case t_ADDw:
+ case t_ADDd:
+ *(Bit16u*)pos=ADD_REG(HOST_a1, HOST_a1, HOST_a2); // add a1, a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ *(Bit16u*)pos=ORR(HOST_a1, HOST_a2); // orr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ *(Bit16u*)pos=AND(HOST_a1, HOST_a2); // and a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_SUBb:
+ case t_SUBw:
+ case t_SUBd:
+ *(Bit16u*)pos=SUB_REG(HOST_a1, HOST_a1, HOST_a2); // sub a1, a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ *(Bit16u*)pos=EOR(HOST_a1, HOST_a2); // eor a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_CMPb:
+ case t_CMPw:
+ case t_CMPd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ *(Bit16u*)pos=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_INCb:
+ case t_INCw:
+ case t_INCd:
+ *(Bit16u*)pos=ADD_IMM3(HOST_a1, HOST_a1, 1); // add a1, a1, #1
+ *(Bit16u*)(pos+2)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_DECb:
+ case t_DECw:
+ case t_DECd:
+ *(Bit16u*)pos=SUB_IMM3(HOST_a1, HOST_a1, 1); // sub a1, a1, #1
+ *(Bit16u*)(pos+2)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_SHLb:
+ case t_SHLw:
+ case t_SHLd:
+ *(Bit16u*)pos=LSL_REG(HOST_a1, HOST_a2); // lsl a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_SHRb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=LSR_IMM(HOST_a1, HOST_a1, 24); // lsr a1, a1, #24
+ *(Bit16u*)(pos+4)=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_SHRw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=LSR_IMM(HOST_a1, HOST_a1, 16); // lsr a1, a1, #16
+ *(Bit16u*)(pos+4)=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_SHRd:
+ *(Bit16u*)pos=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_SARb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=ASR_IMM(HOST_a1, HOST_a1, 24); // asr a1, a1, #24
+ *(Bit16u*)(pos+4)=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_SARw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=ASR_IMM(HOST_a1, HOST_a1, 16); // asr a1, a1, #16
+ *(Bit16u*)(pos+4)=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_SARd:
+ *(Bit16u*)pos=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_RORb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=LSR_IMM(templo1, HOST_a1, 8); // lsr templo1, a1, #8
+ *(Bit16u*)(pos+4)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+6)=NOP; // nop
+ *(Bit16u*)(pos+8)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+10)=NOP; // nop
+ *(Bit16u*)(pos+12)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+14)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ break;
+ case t_RORw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+4)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+6)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+8)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_RORd:
+ *(Bit16u*)pos=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_ROLb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+4)=LSR_IMM(templo1, HOST_a1, 8); // lsr templo1, a1, #8
+ *(Bit16u*)(pos+6)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+8)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+10)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+12)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+14)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ break;
+ case t_ROLw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+4)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+6)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+8)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+10)=NOP; // nop
+ *(Bit16u*)(pos+12)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+14)=NOP; // nop
+ break;
+ case t_ROLd:
+ *(Bit16u*)pos=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+2)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+4)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_NEGb:
+ case t_NEGw:
+ case t_NEGd:
+ *(Bit16u*)pos=NEG(HOST_a1, HOST_a1); // neg a1, a1
+ *(Bit16u*)(pos+2)=B_FWD(10); // b after_call (pc+10)
+ break;
+ default:
+ *(Bit32u*)( ( ((Bit32u) (*pos)) << 2 ) + ((Bit32u)pos + 4) ) = (Bit32u)fct_ptr; // simple_func
+ break;
+ }
+ }
+ else
+ {
+ // try to avoid function calls but rather directly fill in code
+ switch (flags_type) {
+ case t_ADDb:
+ case t_ADDw:
+ case t_ADDd:
+ *(Bit16u*)pos=ADD_REG(HOST_a1, HOST_a1, HOST_a2); // add a1, a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ *(Bit16u*)pos=ORR(HOST_a1, HOST_a2); // orr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ *(Bit16u*)pos=AND(HOST_a1, HOST_a2); // and a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_SUBb:
+ case t_SUBw:
+ case t_SUBd:
+ *(Bit16u*)pos=SUB_REG(HOST_a1, HOST_a1, HOST_a2); // sub a1, a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ *(Bit16u*)pos=EOR(HOST_a1, HOST_a2); // eor a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_CMPb:
+ case t_CMPw:
+ case t_CMPd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ *(Bit16u*)pos=B_FWD(14); // b after_call (pc+14)
+ break;
+ case t_INCb:
+ case t_INCw:
+ case t_INCd:
+ *(Bit16u*)pos=ADD_IMM3(HOST_a1, HOST_a1, 1); // add a1, a1, #1
+ *(Bit16u*)(pos+2)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_DECb:
+ case t_DECw:
+ case t_DECd:
+ *(Bit16u*)pos=SUB_IMM3(HOST_a1, HOST_a1, 1); // sub a1, a1, #1
+ *(Bit16u*)(pos+2)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_SHLb:
+ case t_SHLw:
+ case t_SHLd:
+ *(Bit16u*)pos=LSL_REG(HOST_a1, HOST_a2); // lsl a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_SHRb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=LSR_IMM(HOST_a1, HOST_a1, 24); // lsr a1, a1, #24
+ *(Bit16u*)(pos+4)=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(8); // b after_call (pc+8)
+ break;
+ case t_SHRw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=LSR_IMM(HOST_a1, HOST_a1, 16); // lsr a1, a1, #16
+ *(Bit16u*)(pos+4)=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(8); // b after_call (pc+8)
+ break;
+ case t_SHRd:
+ *(Bit16u*)pos=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_SARb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=ASR_IMM(HOST_a1, HOST_a1, 24); // asr a1, a1, #24
+ *(Bit16u*)(pos+4)=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(8); // b after_call (pc+8)
+ break;
+ case t_SARw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=ASR_IMM(HOST_a1, HOST_a1, 16); // asr a1, a1, #16
+ *(Bit16u*)(pos+4)=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(8); // b after_call (pc+8)
+ break;
+ case t_SARd:
+ *(Bit16u*)pos=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_RORb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=LSR_IMM(templo1, HOST_a1, 8); // lsr templo1, a1, #8
+ *(Bit16u*)(pos+4)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+6)=NOP; // nop
+ *(Bit16u*)(pos+8)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+10)=NOP; // nop
+ *(Bit16u*)(pos+12)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+14)=NOP; // nop
+ *(Bit16u*)(pos+16)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ break;
+ case t_RORw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+4)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+6)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+8)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_RORd:
+ *(Bit16u*)pos=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_ROLb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+4)=LSR_IMM(templo1, HOST_a1, 8); // lsr templo1, a1, #8
+ *(Bit16u*)(pos+6)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+8)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+10)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+12)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+14)=NOP; // nop
+ *(Bit16u*)(pos+16)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ break;
+ case t_ROLw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+4)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+6)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+8)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+10)=NOP; // nop
+ *(Bit16u*)(pos+12)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+14)=NOP; // nop
+ *(Bit16u*)(pos+16)=NOP; // nop
+ break;
+ case t_ROLd:
+ *(Bit16u*)pos=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+2)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+4)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(8); // b after_call (pc+8)
+ break;
+ case t_NEGb:
+ case t_NEGw:
+ case t_NEGd:
+ *(Bit16u*)pos=NEG(HOST_a1, HOST_a1); // neg a1, a1
+ *(Bit16u*)(pos+2)=B_FWD(12); // b after_call (pc+12)
+ break;
+ default:
+ *(Bit32u*)( ( ((Bit32u) (*pos)) << 2 ) + ((Bit32u)pos + 2) ) = (Bit32u)fct_ptr; // simple_func
+ break;
+ }
+
+ }
+#else
+ if (((Bit32u)pos & 0x03) == 0)
+ {
+ *(Bit32u*)( ( ((Bit32u) (*pos)) << 2 ) + ((Bit32u)pos + 4) ) = (Bit32u)fct_ptr; // simple_func
+ }
+ else
+ {
+ *(Bit32u*)( ( ((Bit32u) (*pos)) << 2 ) + ((Bit32u)pos + 2) ) = (Bit32u)fct_ptr; // simple_func
+ }
+#endif
+}
+#endif
+
+#ifdef DRC_USE_SEGS_ADDR
+
+// mov 16bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_seg16_to_reg(HostReg dest_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo1, FC_SEGS_ADDR) ); // mov templo1, FC_SEGS_ADDR
+ cache_addw( LDRH_IMM(dest_reg, templo1, index) ); // ldrh dest_reg, [templo1, #index]
+}
+
+// mov 32bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_seg32_to_reg(HostReg dest_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo1, FC_SEGS_ADDR) ); // mov templo1, FC_SEGS_ADDR
+ cache_addw( LDR_IMM(dest_reg, templo1, index) ); // ldr dest_reg, [templo1, #index]
+}
+
+// add a 32bit value from Segs[index] to a full register using FC_SEGS_ADDR (index modulo 4 must be zero)
+static void gen_add_seg32_to_reg(HostReg reg,Bitu index) {
+ cache_checkinstr(6);
+ cache_addw( MOV_LO_HI(templo1, FC_SEGS_ADDR) ); // mov templo1, FC_SEGS_ADDR
+ cache_addw( LDR_IMM(templo2, templo1, index) ); // ldr templo2, [templo1, #index]
+ cache_addw( ADD_REG(reg, reg, templo2) ); // add reg, reg, templo2
+}
+
+#endif
+
+#ifdef DRC_USE_REGS_ADDR
+
+// mov 16bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_regval16_to_reg(HostReg dest_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDRH_IMM(dest_reg, templo2, index) ); // ldrh dest_reg, [templo2, #index]
+}
+
+// mov 32bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_regval32_to_reg(HostReg dest_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDR_IMM(dest_reg, templo2, index) ); // ldr dest_reg, [templo2, #index]
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_regword_to_reg(HostReg dest_reg,Bitu index,bool dword) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ if (dword) {
+ cache_addw( LDR_IMM(dest_reg, templo2, index) ); // ldr dest_reg, [templo2, #index]
+ } else {
+ cache_addw( LDRH_IMM(dest_reg, templo2, index) ); // ldrh dest_reg, [templo2, #index]
+ }
+}
+
+// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_regbyte_to_reg_low(HostReg dest_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDRB_IMM(dest_reg, templo2, index) ); // ldrb dest_reg, [templo2, #index]
+}
+
+// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_regbyte_to_reg_low_canuseword(HostReg dest_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDRB_IMM(dest_reg, templo2, index) ); // ldrb dest_reg, [templo2, #index]
+}
+
+
+// add a 32bit value from cpu_regs[index] to a full register using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_add_regval32_to_reg(HostReg reg,Bitu index) {
+ cache_checkinstr(6);
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDR_IMM(templo1, templo2, index) ); // ldr templo1, [templo2, #index]
+ cache_addw( ADD_REG(reg, reg, templo1) ); // add reg, reg, templo1
+}
+
+
+// move 16bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 2 must be zero)
+static void gen_mov_regval16_from_reg(HostReg src_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo1, FC_REGS_ADDR) ); // mov templo1, FC_REGS_ADDR
+ cache_addw( STRH_IMM(src_reg, templo1, index) ); // strh src_reg, [templo1, #index]
+}
+
+// move 32bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_regval32_from_reg(HostReg src_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo1, FC_REGS_ADDR) ); // mov templo1, FC_REGS_ADDR
+ cache_addw( STR_IMM(src_reg, templo1, index) ); // str src_reg, [templo1, #index]
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into cpu_regs[index] using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
+static void gen_mov_regword_from_reg(HostReg src_reg,Bitu index,bool dword) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo1, FC_REGS_ADDR) ); // mov templo1, FC_REGS_ADDR
+ if (dword) {
+ cache_addw( STR_IMM(src_reg, templo1, index) ); // str src_reg, [templo1, #index]
+ } else {
+ cache_addw( STRH_IMM(src_reg, templo1, index) ); // strh src_reg, [templo1, #index]
+ }
+}
+
+// move the lowest 8bit of a register into cpu_regs[index] using FC_REGS_ADDR
+static void gen_mov_regbyte_from_reg_low(HostReg src_reg,Bitu index) {
+ cache_checkinstr(4);
+ cache_addw( MOV_LO_HI(templo1, FC_REGS_ADDR) ); // mov templo1, FC_REGS_ADDR
+ cache_addw( STRB_IMM(src_reg, templo1, index) ); // strb src_reg, [templo1, #index]
+}
+
+#endif
diff --git a/src/cpu/core_dynrec/risc_armv4le-thumb.h b/src/cpu/core_dynrec/risc_armv4le-thumb.h
new file mode 100644
index 000000000..8db5f1ea0
--- /dev/null
+++ b/src/cpu/core_dynrec/risc_armv4le-thumb.h
@@ -0,0 +1,1335 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+/* ARMv4 (little endian) backend by M-HT (thumb version) */
+
+
+// temporary "lo" registers
+#define templo1 HOST_v3
+#define templo2 HOST_v4
+#define templo3 HOST_v2
+
+// register that holds function return values
+#define FC_RETOP HOST_a1
+
+// register used for address calculations,
+#define FC_ADDR HOST_v1 // has to be saved across calls, see DRC_PROTECT_ADDR_REG
+
+// register that holds the first parameter
+#define FC_OP1 HOST_a1
+
+// register that holds the second parameter
+#define FC_OP2 HOST_a2
+
+// special register that holds the third parameter for _R3 calls (byte accessible)
+#define FC_OP3 HOST_a4
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA1 HOST_a1
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA2 HOST_a2
+
+// temporary register for LEA
+#define TEMP_REG_DRC HOST_a4
+
+// used to hold the address of "cpu_regs" - preferably filled in function gen_run_code
+#define FC_REGS_ADDR HOST_v7
+
+// used to hold the address of "Segs" - preferably filled in function gen_run_code
+#define FC_SEGS_ADDR HOST_v8
+
+// used to hold the address of "core_dynrec.readdata" - filled in function gen_run_code
+#define readdata_addr HOST_v5
+
+
+// instruction encodings
+
+// move
+// mov dst, #imm @ 0 <= imm <= 255
+#define MOV_IMM(dst, imm) (0x2000 + ((dst) << 8) + (imm) )
+// mov dst, src
+#define MOV_REG(dst, src) ADD_IMM3(dst, src, 0)
+// mov dst, src
+#define MOV_LO_HI(dst, src) (0x4640 + (dst) + (((src) - HOST_r8) << 3) )
+// mov dst, src
+#define MOV_HI_LO(dst, src) (0x4680 + ((dst) - HOST_r8) + ((src) << 3) )
+
+// arithmetic
+// add dst, src, #imm @ 0 <= imm <= 7
+#define ADD_IMM3(dst, src, imm) (0x1c00 + (dst) + ((src) << 3) + ((imm) << 6) )
+// add dst, #imm @ 0 <= imm <= 255
+#define ADD_IMM8(dst, imm) (0x3000 + ((dst) << 8) + (imm) )
+// add dst, src1, src2
+#define ADD_REG(dst, src1, src2) (0x1800 + (dst) + ((src1) << 3) + ((src2) << 6) )
+// add dst, pc, #imm @ 0 <= imm < 1024 & imm mod 4 = 0
+#define ADD_LO_PC_IMM(dst, imm) (0xa000 + ((dst) << 8) + ((imm) >> 2) )
+// sub dst, src1, src2
+#define SUB_REG(dst, src1, src2) (0x1a00 + (dst) + ((src1) << 3) + ((src2) << 6) )
+// sub dst, src, #imm @ 0 <= imm <= 7
+#define SUB_IMM3(dst, src, imm) (0x1e00 + (dst) + ((src) << 3) + ((imm) << 6) )
+// sub dst, #imm @ 0 <= imm <= 255
+#define SUB_IMM8(dst, imm) (0x3800 + ((dst) << 8) + (imm) )
+// neg dst, src
+#define NEG(dst, src) (0x4240 + (dst) + ((src) << 3) )
+// cmp dst, #imm @ 0 <= imm <= 255
+#define CMP_IMM(dst, imm) (0x2800 + ((dst) << 8) + (imm) )
+// nop
+#define NOP (0x46c0)
+
+// logical
+// and dst, src
+#define AND(dst, src) (0x4000 + (dst) + ((src) << 3) )
+// bic dst, src
+#define BIC(dst, src) (0x4380 + (dst) + ((src) << 3) )
+// eor dst, src
+#define EOR(dst, src) (0x4040 + (dst) + ((src) << 3) )
+// orr dst, src
+#define ORR(dst, src) (0x4300 + (dst) + ((src) << 3) )
+// mvn dst, src
+#define MVN(dst, src) (0x43c0 + (dst) + ((src) << 3) )
+
+// shift/rotate
+// lsl dst, src, #imm
+#define LSL_IMM(dst, src, imm) (0x0000 + (dst) + ((src) << 3) + ((imm) << 6) )
+// lsl dst, reg
+#define LSL_REG(dst, reg) (0x4080 + (dst) + ((reg) << 3) )
+// lsr dst, src, #imm
+#define LSR_IMM(dst, src, imm) (0x0800 + (dst) + ((src) << 3) + ((imm) << 6) )
+// lsr dst, reg
+#define LSR_REG(dst, reg) (0x40c0 + (dst) + ((reg) << 3) )
+// asr dst, src, #imm
+#define ASR_IMM(dst, src, imm) (0x1000 + (dst) + ((src) << 3) + ((imm) << 6) )
+// asr dst, reg
+#define ASR_REG(dst, reg) (0x4100 + (dst) + ((reg) << 3) )
+// ror dst, reg
+#define ROR_REG(dst, reg) (0x41c0 + (dst) + ((reg) << 3) )
+
+// load
+// ldr reg, [addr, #imm] @ 0 <= imm < 128 & imm mod 4 = 0
+#define LDR_IMM(reg, addr, imm) (0x6800 + (reg) + ((addr) << 3) + ((imm) << 4) )
+// ldrh reg, [addr, #imm] @ 0 <= imm < 64 & imm mod 2 = 0
+#define LDRH_IMM(reg, addr, imm) (0x8800 + (reg) + ((addr) << 3) + ((imm) << 5) )
+// ldrb reg, [addr, #imm] @ 0 <= imm < 32
+#define LDRB_IMM(reg, addr, imm) (0x7800 + (reg) + ((addr) << 3) + ((imm) << 6) )
+// ldr reg, [pc, #imm] @ 0 <= imm < 1024 & imm mod 4 = 0
+#define LDR_PC_IMM(reg, imm) (0x4800 + ((reg) << 8) + ((imm) >> 2) )
+// ldr reg, [addr1, addr2]
+#define LDR_REG(reg, addr1, addr2) (0x5800 + (reg) + ((addr1) << 3) + ((addr2) << 6) )
+
+// store
+// str reg, [addr, #imm] @ 0 <= imm < 128 & imm mod 4 = 0
+#define STR_IMM(reg, addr, imm) (0x6000 + (reg) + ((addr) << 3) + ((imm) << 4) )
+// strh reg, [addr, #imm] @ 0 <= imm < 64 & imm mod 2 = 0
+#define STRH_IMM(reg, addr, imm) (0x8000 + (reg) + ((addr) << 3) + ((imm) << 5) )
+// strb reg, [addr, #imm] @ 0 <= imm < 32
+#define STRB_IMM(reg, addr, imm) (0x7000 + (reg) + ((addr) << 3) + ((imm) << 6) )
+
+// branch
+// beq pc+imm @ 0 <= imm < 256 & imm mod 2 = 0
+#define BEQ_FWD(imm) (0xd000 + ((imm) >> 1) )
+// bne pc+imm @ 0 <= imm < 256 & imm mod 2 = 0
+#define BNE_FWD(imm) (0xd100 + ((imm) >> 1) )
+// bgt pc+imm @ 0 <= imm < 256 & imm mod 2 = 0
+#define BGT_FWD(imm) (0xdc00 + ((imm) >> 1) )
+// b pc+imm @ 0 <= imm < 2048 & imm mod 2 = 0
+#define B_FWD(imm) (0xe000 + ((imm) >> 1) )
+// bx reg
+#define BX(reg) (0x4700 + ((reg) << 3) )
+
+
+// arm instructions
+
+// arithmetic
+// add dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0
+#define ARM_ADD_IMM(dst, src, imm, rimm) (0xe2800000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) )
+
+// load
+// ldr reg, [addr, #imm] @ 0 <= imm < 4096
+#define ARM_LDR_IMM(reg, addr, imm) (0xe5900000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+
+// store
+// str reg, [addr, #-(imm)]! @ 0 <= imm < 4096
+#define ARM_STR_IMM_M_W(reg, addr, imm) (0xe5200000 + ((reg) << 12) + ((addr) << 16) + (imm) )
+
+// branch
+// bx reg
+#define ARM_BX(reg) (0xe12fff10 + (reg) )
+
+
+// move a full register from reg_src to reg_dst
+static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) {
+ if(reg_src == reg_dst) return;
+ cache_addw( MOV_REG(reg_dst, reg_src) ); // mov reg_dst, reg_src
+}
+
+// helper function
+static bool val_single_shift(Bit32u value, Bit32u *val_shift) {
+ Bit32u shift;
+
+ if (GCC_UNLIKELY(value == 0)) {
+ *val_shift = 0;
+ return true;
+ }
+
+ shift = 0;
+ while ((value & 1) == 0) {
+ value>>=1;
+ shift+=1;
+ }
+
+ if ((value >> 8) != 0) return false;
+
+ *val_shift = shift;
+ return true;
+}
+
+// move a 32bit constant value into dest_reg
+static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) {
+ Bit32u scale;
+
+ if (imm < 256) {
+ cache_addw( MOV_IMM(dest_reg, imm) ); // mov dest_reg, #imm
+ } else if ((~imm) < 256) {
+ cache_addw( MOV_IMM(dest_reg, ~imm) ); // mov dest_reg, #(~imm)
+ cache_addw( MVN(dest_reg, dest_reg) ); // mvn dest_reg, dest_reg
+ } else if (val_single_shift(imm, &scale)) {
+ cache_addw( MOV_IMM(dest_reg, imm >> scale) ); // mov dest_reg, #(imm >> scale)
+ cache_addw( LSL_IMM(dest_reg, dest_reg, scale) ); // lsl dest_reg, dest_reg, #scale
+ } else {
+ Bit32u diff;
+
+ diff = imm - ((Bit32u)cache.pos+4);
+
+ if ((diff < 1024) && ((imm & 0x03) == 0)) {
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( ADD_LO_PC_IMM(dest_reg, diff) ); // add dest_reg, pc, #(diff >> 2)
+ } else {
+ cache_addw( NOP ); // nop
+ cache_addw( ADD_LO_PC_IMM(dest_reg, diff - 2) ); // add dest_reg, pc, #((diff - 2) >> 2)
+ }
+ } else {
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( LDR_PC_IMM(dest_reg, 0) ); // ldr dest_reg, [pc, #0]
+ cache_addw( B_FWD(2) ); // b next_code (pc+2)
+ cache_addd(imm); // .int imm
+ // next_code:
+ } else {
+ cache_addw( LDR_PC_IMM(dest_reg, 4) ); // ldr dest_reg, [pc, #4]
+ cache_addw( B_FWD(4) ); // b next_code (pc+4)
+ cache_addw( NOP ); // nop
+ cache_addd(imm); // .int imm
+ // next_code:
+ }
+ }
+ }
+}
+
+// helper function
+static bool gen_mov_memval_to_reg_helper(HostReg dest_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) {
+ switch (size) {
+ case 4:
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((data & 3) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 128) && (((data - addr_data) & 3) == 0)) {
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( LDR_IMM(dest_reg, templo2, data - addr_data) ); // ldr dest_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ }
+ break;
+ case 2:
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((data & 1) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 64) && (((data - addr_data) & 1) == 0)) {
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( LDRH_IMM(dest_reg, templo2, data - addr_data) ); // ldrh dest_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ }
+ break;
+ case 1:
+ if ((data >= addr_data) && (data < addr_data + 32)) {
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( LDRB_IMM(dest_reg, templo2, data - addr_data) ); // ldrb dest_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+// helper function
+static bool gen_mov_memval_to_reg(HostReg dest_reg, void *data, Bitu size) {
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true;
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true;
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true;
+ return false;
+}
+
+// helper function for gen_mov_word_to_reg
+static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,HostReg data_reg) {
+ // alignment....
+ if (dword) {
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((Bit32u)data & 3) {
+ if ( ((Bit32u)data & 3) == 2 ) {
+ cache_addw( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg]
+ cache_addw( LDRH_IMM(templo1, data_reg, 2) ); // ldrh templo1, [data_reg, #2]
+ cache_addw( LSL_IMM(templo1, templo1, 16) ); // lsl templo1, templo1, #16
+ cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1
+ } else {
+ cache_addw( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg]
+ cache_addw( ADD_IMM3(templo1, data_reg, 1) ); // add templo1, data_reg, #1
+ cache_addw( LDRH_IMM(templo1, templo1, 0) ); // ldrh templo1, [templo1]
+ cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8
+ cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1
+ cache_addw( LDRB_IMM(templo1, data_reg, 3) ); // ldrb templo1, [data_reg, #3]
+ cache_addw( LSL_IMM(templo1, templo1, 24) ); // lsl templo1, templo1, #24
+ cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1
+ }
+ } else
+#endif
+ {
+ cache_addw( LDR_IMM(dest_reg, data_reg, 0) ); // ldr dest_reg, [data_reg]
+ }
+ } else {
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((Bit32u)data & 1) {
+ cache_addw( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg]
+ cache_addw( LDRB_IMM(templo1, data_reg, 1) ); // ldrb templo1, [data_reg, #1]
+ cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8
+ cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1
+ } else
+#endif
+ {
+ cache_addw( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg]
+ }
+ }
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) {
+ if (!gen_mov_memval_to_reg(dest_reg, data, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(templo2, (Bit32u)data);
+ gen_mov_word_to_reg_helper(dest_reg, data, dword, templo2);
+ }
+}
+
+// move a 16bit constant value into dest_reg
+// the upper 16bit of the destination register may be destroyed
+static void INLINE gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) {
+ gen_mov_dword_to_reg_imm(dest_reg, (Bit32u)imm);
+}
+
+// helper function
+static bool gen_mov_memval_from_reg_helper(HostReg src_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) {
+ switch (size) {
+ case 4:
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((data & 3) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 128) && (((data - addr_data) & 3) == 0)) {
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( STR_IMM(src_reg, templo2, data - addr_data) ); // str src_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ }
+ break;
+ case 2:
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((data & 1) == 0)
+#endif
+ {
+ if ((data >= addr_data) && (data < addr_data + 64) && (((data - addr_data) & 1) == 0)) {
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( STRH_IMM(src_reg, templo2, data - addr_data) ); // strh src_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ }
+ break;
+ case 1:
+ if ((data >= addr_data) && (data < addr_data + 32)) {
+ cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg
+ cache_addw( STRB_IMM(src_reg, templo2, data - addr_data) ); // strb src_reg, [templo2, #(data - addr_data)]
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+// helper function
+static bool gen_mov_memval_from_reg(HostReg src_reg, void *dest, Bitu size) {
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true;
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true;
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true;
+ return false;
+}
+
+// helper function for gen_mov_word_from_reg
+static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, HostReg data_reg) {
+ // alignment....
+ if (dword) {
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((Bit32u)dest & 3) {
+ if ( ((Bit32u)dest & 3) == 2 ) {
+ cache_addw( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 16) ); // lsr templo1, templo1, #16
+ cache_addw( STRH_IMM(templo1, data_reg, 2) ); // strh templo1, [data_reg, #2]
+ } else {
+ cache_addw( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 8) ); // lsr templo1, templo1, #8
+ cache_addw( STRB_IMM(templo1, data_reg, 1) ); // strb templo1, [data_reg, #1]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 16) ); // lsr templo1, templo1, #16
+ cache_addw( STRB_IMM(templo1, data_reg, 2) ); // strb templo1, [data_reg, #2]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 24) ); // lsr templo1, templo1, #24
+ cache_addw( STRB_IMM(templo1, data_reg, 3) ); // strb templo1, [data_reg, #3]
+ }
+ } else
+#endif
+ {
+ cache_addw( STR_IMM(src_reg, data_reg, 0) ); // str src_reg, [data_reg]
+ }
+ } else {
+#if !defined(C_UNALIGNED_MEMORY)
+ if ((Bit32u)dest & 1) {
+ cache_addw( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg]
+ cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg
+ cache_addw( LSR_IMM(templo1, templo1, 8) ); // lsr templo1, templo1, #8
+ cache_addw( STRB_IMM(templo1, data_reg, 1) ); // strb templo1, [data_reg, #1]
+ } else
+#endif
+ {
+ cache_addw( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg]
+ }
+ }
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into memory
+static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) {
+ if (!gen_mov_memval_from_reg(src_reg, dest, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest);
+ gen_mov_word_from_reg_helper(src_reg, dest, dword, templo2);
+ }
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) {
+ if (!gen_mov_memval_to_reg(dest_reg, data, 1)) {
+ gen_mov_dword_to_reg_imm(templo1, (Bit32u)data);
+ cache_addw( LDRB_IMM(dest_reg, templo1, 0) ); // ldrb dest_reg, [templo1]
+ }
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) {
+ gen_mov_byte_to_reg_low(dest_reg, data);
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) {
+ cache_addw( MOV_IMM(dest_reg, imm) ); // mov dest_reg, #(imm)
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) {
+ gen_mov_byte_to_reg_low_imm(dest_reg, imm);
+}
+
+// move the lowest 8bit of a register into memory
+static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) {
+ if (!gen_mov_memval_from_reg(src_reg, dest, 1)) {
+ gen_mov_dword_to_reg_imm(templo1, (Bit32u)dest);
+ cache_addw( STRB_IMM(src_reg, templo1, 0) ); // strb src_reg, [templo1]
+ }
+}
+
+
+
+// convert an 8bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_byte(bool sign,HostReg reg) {
+ cache_addw( LSL_IMM(reg, reg, 24) ); // lsl reg, reg, #24
+
+ if (sign) {
+ cache_addw( ASR_IMM(reg, reg, 24) ); // asr reg, reg, #24
+ } else {
+ cache_addw( LSR_IMM(reg, reg, 24) ); // lsr reg, reg, #24
+ }
+}
+
+// convert a 16bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_word(bool sign,HostReg reg) {
+ cache_addw( LSL_IMM(reg, reg, 16) ); // lsl reg, reg, #16
+
+ if (sign) {
+ cache_addw( ASR_IMM(reg, reg, 16) ); // asr reg, reg, #16
+ } else {
+ cache_addw( LSR_IMM(reg, reg, 16) ); // lsr reg, reg, #16
+ }
+}
+
+// add a 32bit value from memory to a full register
+static void gen_add(HostReg reg,void* op) {
+ gen_mov_word_to_reg(templo3, op, 1);
+ cache_addw( ADD_REG(reg, reg, templo3) ); // add reg, reg, templo3
+}
+
+// add a 32bit constant value to a full register
+static void gen_add_imm(HostReg reg,Bit32u imm) {
+ Bit32u imm2, scale;
+
+ if(!imm) return;
+
+ imm2 = (Bit32u) (-((Bit32s)imm));
+
+ if (imm <= 255) {
+ cache_addw( ADD_IMM8(reg, imm) ); // add reg, #imm
+ } else if (imm2 <= 255) {
+ cache_addw( SUB_IMM8(reg, imm2) ); // sub reg, #(-imm)
+ } else {
+ if (val_single_shift(imm2, &scale)) {
+ cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale)
+ if (scale) {
+ cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale
+ }
+ cache_addw( SUB_REG(reg, reg, templo1) ); // sub reg, reg, templo1
+ } else {
+ gen_mov_dword_to_reg_imm(templo1, imm);
+ cache_addw( ADD_REG(reg, reg, templo1) ); // add reg, reg, templo1
+ }
+ }
+}
+
+// and a 32bit constant value with a full register
+static void gen_and_imm(HostReg reg,Bit32u imm) {
+ Bit32u imm2, scale;
+
+ imm2 = ~imm;
+ if(!imm2) return;
+
+ if (!imm) {
+ cache_addw( MOV_IMM(reg, 0) ); // mov reg, #0
+ } else {
+ if (val_single_shift(imm2, &scale)) {
+ cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale)
+ if (scale) {
+ cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale
+ }
+ cache_addw( BIC(reg, templo1) ); // bic reg, templo1
+ } else {
+ gen_mov_dword_to_reg_imm(templo1, imm);
+ cache_addw( AND(reg, templo1) ); // and reg, templo1
+ }
+ }
+}
+
+
+// move a 32bit constant value into memory
+static void gen_mov_direct_dword(void* dest,Bit32u imm) {
+ gen_mov_dword_to_reg_imm(templo3, imm);
+ gen_mov_word_from_reg(templo3, dest, 1);
+}
+
+// move an address into memory
+static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) {
+ gen_mov_direct_dword(dest,(Bit32u)imm);
+}
+
+// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value
+static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) {
+ if (!dword) imm &= 0xffff;
+ if(!imm) return;
+
+ if (!gen_mov_memval_to_reg(templo3, dest, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest);
+ gen_mov_word_to_reg_helper(templo3, dest, dword, templo2);
+ }
+ gen_add_imm(templo3, imm);
+ if (!gen_mov_memval_from_reg(templo3, dest, (dword)?4:2)) {
+ gen_mov_word_from_reg_helper(templo3, dest, dword, templo2);
+ }
+}
+
+// add an 8bit constant value to a dword memory value
+static void gen_add_direct_byte(void* dest,Bit8s imm) {
+ gen_add_direct_word(dest, (Bit32s)imm, 1);
+}
+
+// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value
+static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) {
+ Bit32u imm2, scale;
+
+ if (!dword) imm &= 0xffff;
+ if(!imm) return;
+
+ if (!gen_mov_memval_to_reg(templo3, dest, (dword)?4:2)) {
+ gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest);
+ gen_mov_word_to_reg_helper(templo3, dest, dword, templo2);
+ }
+
+ imm2 = (Bit32u) (-((Bit32s)imm));
+
+ if (imm <= 255) {
+ cache_addw( SUB_IMM8(templo3, imm) ); // sub templo3, #imm
+ } else if (imm2 <= 255) {
+ cache_addw( ADD_IMM8(templo3, imm2) ); // add templo3, #(-imm)
+ } else {
+ if (val_single_shift(imm2, &scale)) {
+ cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale)
+ if (scale) {
+ cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale
+ }
+ cache_addw( ADD_REG(templo3, templo3, templo1) ); // add templo3, templo3, templo1
+ } else {
+ gen_mov_dword_to_reg_imm(templo1, imm);
+ cache_addw( SUB_REG(templo3, templo3, templo1) ); // sub templo3, templo3, templo1
+ }
+ }
+
+ if (!gen_mov_memval_from_reg(templo3, dest, (dword)?4:2)) {
+ gen_mov_word_from_reg_helper(templo3, dest, dword, templo2);
+ }
+}
+
+// subtract an 8bit constant value from a dword memory value
+static void gen_sub_direct_byte(void* dest,Bit8s imm) {
+ gen_sub_direct_word(dest, (Bit32s)imm, 1);
+}
+
+// effective address calculation, destination is dest_reg
+// scale_reg is scaled by scale (scale_reg*(2^scale)) and
+// added to dest_reg, then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) {
+ if (scale) {
+ cache_addw( LSL_IMM(templo1, scale_reg, scale) ); // lsl templo1, scale_reg, #(scale)
+ cache_addw( ADD_REG(dest_reg, dest_reg, templo1) ); // add dest_reg, dest_reg, templo1
+ } else {
+ cache_addw( ADD_REG(dest_reg, dest_reg, scale_reg) ); // add dest_reg, dest_reg, scale_reg
+ }
+ gen_add_imm(dest_reg, imm);
+}
+
+// effective address calculation, destination is dest_reg
+// dest_reg is scaled by scale (dest_reg*(2^scale)),
+// then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) {
+ if (scale) {
+ cache_addw( LSL_IMM(dest_reg, dest_reg, scale) ); // lsl dest_reg, dest_reg, #(scale)
+ }
+ gen_add_imm(dest_reg, imm);
+}
+
+// generate a call to a parameterless function
+static void INLINE gen_call_function_raw(void * func) {
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( LDR_PC_IMM(templo1, 4) ); // ldr templo1, [pc, #4]
+ cache_addw( ADD_LO_PC_IMM(templo2, 8) ); // adr templo2, after_call (add templo2, pc, #8)
+ cache_addw( MOV_HI_LO(HOST_lr, templo2) ); // mov lr, templo2
+ cache_addw( BX(templo1) ); // bx templo1 --- switch to arm state
+ } else {
+ cache_addw( LDR_PC_IMM(templo1, 8) ); // ldr templo1, [pc, #8]
+ cache_addw( ADD_LO_PC_IMM(templo2, 8) ); // adr templo2, after_call (add templo2, pc, #8)
+ cache_addw( MOV_HI_LO(HOST_lr, templo2) ); // mov lr, templo2
+ cache_addw( BX(templo1) ); // bx templo1 --- switch to arm state
+ cache_addw( NOP ); // nop
+ }
+ cache_addd((Bit32u)func); // .int func
+ // after_call:
+
+ // switch from arm to thumb state
+ cache_addd(0xe2800000 + (templo1 << 12) + (HOST_pc << 16) + (1)); // add templo1, pc, #1
+ cache_addd(0xe12fff10 + (templo1)); // bx templo1
+
+ // thumb state from now on
+}
+
+// generate a call to a function with paramcount parameters
+// note: the parameters are loaded in the architecture specific way
+// using the gen_load_param_ functions below
+static Bit32u INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) {
+ Bit32u proc_addr = (Bit32u)cache.pos;
+ gen_call_function_raw(func);
+ return proc_addr;
+ // if proc_addr is on word boundary ((proc_addr & 0x03) == 0)
+ // then length of generated code is 20 bytes
+ // otherwise length of generated code is 22 bytes
+}
+
+#if (1)
+// max of 4 parameters in a1-a4
+
+// load an immediate value as param'th function parameter
+static void INLINE gen_load_param_imm(Bitu imm,Bitu param) {
+ gen_mov_dword_to_reg_imm(param, imm);
+}
+
+// load an address as param'th function parameter
+static void INLINE gen_load_param_addr(Bitu addr,Bitu param) {
+ gen_mov_dword_to_reg_imm(param, addr);
+}
+
+// load a host-register as param'th function parameter
+static void INLINE gen_load_param_reg(Bitu reg,Bitu param) {
+ gen_mov_regs(param, reg);
+}
+
+// load a value from memory as param'th function parameter
+static void INLINE gen_load_param_mem(Bitu mem,Bitu param) {
+ gen_mov_word_to_reg(param, (void *)mem, 1);
+}
+#else
+ other arm abis
+#endif
+
+// jump to an address pointed at by ptr, offset is in imm
+static void gen_jmp_ptr(void * ptr,Bits imm=0) {
+ gen_mov_word_to_reg(templo3, ptr, 1);
+
+#if !defined(C_UNALIGNED_MEMORY)
+// (*ptr) should be word aligned
+ if ((imm & 0x03) == 0) {
+#endif
+ if ((imm >= 0) && (imm < 128) && ((imm & 3) == 0)) {
+ cache_addw( LDR_IMM(templo2, templo3, imm) ); // ldr templo2, [templo3, #imm]
+ } else {
+ gen_mov_dword_to_reg_imm(templo2, imm);
+ cache_addw( LDR_REG(templo2, templo3, templo2) ); // ldr templo2, [templo3, templo2]
+ }
+#if !defined(C_UNALIGNED_MEMORY)
+ } else {
+ gen_add_imm(templo3, imm);
+
+ cache_addw( LDRB_IMM(templo2, templo3, 0) ); // ldrb templo2, [templo3]
+ cache_addw( LDRB_IMM(templo1, templo3, 1) ); // ldrb templo1, [templo3, #1]
+ cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8
+ cache_addw( ORR(templo2, templo1) ); // orr templo2, templo1
+ cache_addw( LDRB_IMM(templo1, templo3, 2) ); // ldrb templo1, [templo3, #2]
+ cache_addw( LSL_IMM(templo1, templo1, 16) ); // lsl templo1, templo1, #16
+ cache_addw( ORR(templo2, templo1) ); // orr templo2, templo1
+ cache_addw( LDRB_IMM(templo1, templo3, 3) ); // ldrb templo1, [templo3, #3]
+ cache_addw( LSL_IMM(templo1, templo1, 24) ); // lsl templo1, templo1, #24
+ cache_addw( ORR(templo2, templo1) ); // orr templo2, templo1
+ }
+#endif
+
+ // increase jmp address to keep thumb state
+ cache_addw( ADD_IMM3(templo2, templo2, 1) ); // add templo2, templo2, #1
+
+ cache_addw( BX(templo2) ); // bx templo2
+}
+
+// short conditional jump (+-127 bytes) if register is zero
+// the destination is set by gen_fill_branch() later
+static Bit32u gen_create_branch_on_zero(HostReg reg,bool dword) {
+ if (dword) {
+ cache_addw( CMP_IMM(reg, 0) ); // cmp reg, #0
+ } else {
+ cache_addw( LSL_IMM(templo1, reg, 16) ); // lsl templo1, reg, #16
+ }
+ cache_addw( BEQ_FWD(0) ); // beq j
+ return ((Bit32u)cache.pos-2);
+}
+
+// short conditional jump (+-127 bytes) if register is nonzero
+// the destination is set by gen_fill_branch() later
+static Bit32u gen_create_branch_on_nonzero(HostReg reg,bool dword) {
+ if (dword) {
+ cache_addw( CMP_IMM(reg, 0) ); // cmp reg, #0
+ } else {
+ cache_addw( LSL_IMM(templo1, reg, 16) ); // lsl templo1, reg, #16
+ }
+ cache_addw( BNE_FWD(0) ); // bne j
+ return ((Bit32u)cache.pos-2);
+}
+
+// calculate relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch(DRC_PTR_SIZE_IM data) {
+#if C_DEBUG
+ Bits len=(Bit32u)cache.pos-(data+4);
+ if (len<0) len=-len;
+ if (len>252) LOG_MSG("Big jump %d",len);
+#endif
+ *(Bit8u*)data=(Bit8u)( ((Bit32u)cache.pos-(data+4)) >> 1 );
+}
+
+// conditional jump if register is nonzero
+// for isdword==true the 32bit of the register are tested
+// for isdword==false the lowest 8bit of the register are tested
+static Bit32u gen_create_branch_long_nonzero(HostReg reg,bool isdword) {
+ if (isdword) {
+ cache_addw( CMP_IMM(reg, 0) ); // cmp reg, #0
+ } else {
+ cache_addw( LSL_IMM(templo2, reg, 24) ); // lsl templo2, reg, #24
+ }
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( BEQ_FWD(8) ); // beq nobranch (pc+8)
+ cache_addw( LDR_PC_IMM(templo1, 4) ); // ldr templo1, [pc, #4]
+ cache_addw( BX(templo1) ); // bx templo1
+ cache_addw( NOP ); // nop
+ } else {
+ cache_addw( BEQ_FWD(6) ); // beq nobranch (pc+6)
+ cache_addw( LDR_PC_IMM(templo1, 0) ); // ldr templo1, [pc, #0]
+ cache_addw( BX(templo1) ); // bx templo1
+ }
+ cache_addd(0); // fill j
+ // nobranch:
+ return ((Bit32u)cache.pos-4);
+}
+
+// compare 32bit-register against zero and jump if value less/equal than zero
+static Bit32u gen_create_branch_long_leqzero(HostReg reg) {
+ cache_addw( CMP_IMM(reg, 0) ); // cmp reg, #0
+ if (((Bit32u)cache.pos & 0x03) == 0) {
+ cache_addw( BGT_FWD(8) ); // bgt nobranch (pc+8)
+ cache_addw( LDR_PC_IMM(templo1, 4) ); // ldr templo1, [pc, #4]
+ cache_addw( BX(templo1) ); // bx templo1
+ cache_addw( NOP ); // nop
+ } else {
+ cache_addw( BGT_FWD(6) ); // bgt nobranch (pc+6)
+ cache_addw( LDR_PC_IMM(templo1, 0) ); // ldr templo1, [pc, #0]
+ cache_addw( BX(templo1) ); // bx templo1
+ }
+ cache_addd(0); // fill j
+ // nobranch:
+ return ((Bit32u)cache.pos-4);
+}
+
+// calculate long relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch_long(Bit32u data) {
+ // this is an absolute branch
+ *(Bit32u*)data=((Bit32u)cache.pos) + 1; // add 1 to keep processor in thumb state
+}
+
+static void gen_run_code(void) {
+ Bit8u *pos1, *pos2, *pos3;
+
+#if (__ARM_EABI__)
+ // 8-byte stack alignment
+ cache_addd(0xe92d4ff0); // stmfd sp!, {v1-v8,lr}
+#else
+ cache_addd(0xe92d4df0); // stmfd sp!, {v1-v5,v7,v8,lr}
+#endif
+
+ cache_addd( ARM_ADD_IMM(HOST_r0, HOST_r0, 1, 0) ); // add r0, r0, #1
+
+ pos1 = cache.pos;
+ cache_addd( 0 );
+ pos2 = cache.pos;
+ cache_addd( 0 );
+ pos3 = cache.pos;
+ cache_addd( 0 );
+
+ cache_addd( ARM_ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4
+ cache_addd( ARM_STR_IMM_M_W(HOST_lr, HOST_sp, 4) ); // str lr, [sp, #-4]!
+ cache_addd( ARM_BX(HOST_r0) ); // bx r0
+
+#if (__ARM_EABI__)
+ cache_addd(0xe8bd4ff0); // ldmfd sp!, {v1-v8,lr}
+#else
+ cache_addd(0xe8bd4df0); // ldmfd sp!, {v1-v5,v7,v8,lr}
+#endif
+ cache_addd( ARM_BX(HOST_lr) ); // bx lr
+
+ // align cache.pos to 32 bytes
+ if ((((Bitu)cache.pos) & 0x1f) != 0) {
+ cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f));
+ }
+
+ *(Bit32u*)pos1 = ARM_LDR_IMM(FC_SEGS_ADDR, HOST_pc, cache.pos - (pos1 + 8)); // ldr FC_SEGS_ADDR, [pc, #(&Segs)]
+ cache_addd((Bit32u)&Segs); // address of "Segs"
+
+ *(Bit32u*)pos2 = ARM_LDR_IMM(FC_REGS_ADDR, HOST_pc, cache.pos - (pos2 + 8)); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)]
+ cache_addd((Bit32u)&cpu_regs); // address of "cpu_regs"
+
+ *(Bit32u*)pos3 = ARM_LDR_IMM(readdata_addr, HOST_pc, cache.pos - (pos3 + 8)); // ldr readdata_addr, [pc, #(&core_dynrec.readdata)]
+ cache_addd((Bit32u)&core_dynrec.readdata); // address of "core_dynrec.readdata"
+
+ // align cache.pos to 32 bytes
+ if ((((Bitu)cache.pos) & 0x1f) != 0) {
+ cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f));
+ }
+}
+
+// return from a function
+static void gen_return_function(void) {
+ cache_addw(0xbc08); // pop {r3}
+ cache_addw( BX(HOST_r3) ); // bx r3
+}
+
+#ifdef DRC_FLAGS_INVALIDATION
+
+// called when a call to a function can be replaced by a
+// call to a simpler function
+static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) {
+#ifdef DRC_FLAGS_INVALIDATION_DCODE
+ if (((Bit32u)pos & 0x03) == 0)
+ {
+ // try to avoid function calls but rather directly fill in code
+ switch (flags_type) {
+ case t_ADDb:
+ case t_ADDw:
+ case t_ADDd:
+ *(Bit16u*)pos=ADD_REG(HOST_a1, HOST_a1, HOST_a2); // add a1, a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(14); // b after_call (pc+14)
+ break;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ *(Bit16u*)pos=ORR(HOST_a1, HOST_a2); // orr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(14); // b after_call (pc+14)
+ break;
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ *(Bit16u*)pos=AND(HOST_a1, HOST_a2); // and a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(14); // b after_call (pc+14)
+ break;
+ case t_SUBb:
+ case t_SUBw:
+ case t_SUBd:
+ *(Bit16u*)pos=SUB_REG(HOST_a1, HOST_a1, HOST_a2); // sub a1, a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(14); // b after_call (pc+14)
+ break;
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ *(Bit16u*)pos=EOR(HOST_a1, HOST_a2); // eor a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(14); // b after_call (pc+14)
+ break;
+ case t_CMPb:
+ case t_CMPw:
+ case t_CMPd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ *(Bit16u*)pos=B_FWD(16); // b after_call (pc+16)
+ break;
+ case t_INCb:
+ case t_INCw:
+ case t_INCd:
+ *(Bit16u*)pos=ADD_IMM3(HOST_a1, HOST_a1, 1); // add a1, a1, #1
+ *(Bit16u*)(pos+2)=B_FWD(14); // b after_call (pc+14)
+ break;
+ case t_DECb:
+ case t_DECw:
+ case t_DECd:
+ *(Bit16u*)pos=SUB_IMM3(HOST_a1, HOST_a1, 1); // sub a1, a1, #1
+ *(Bit16u*)(pos+2)=B_FWD(14); // b after_call (pc+14)
+ break;
+ case t_SHLb:
+ case t_SHLw:
+ case t_SHLd:
+ *(Bit16u*)pos=LSL_REG(HOST_a1, HOST_a2); // lsl a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(14); // b after_call (pc+14)
+ break;
+ case t_SHRb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=LSR_IMM(HOST_a1, HOST_a1, 24); // lsr a1, a1, #24
+ *(Bit16u*)(pos+4)=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_SHRw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=LSR_IMM(HOST_a1, HOST_a1, 16); // lsr a1, a1, #16
+ *(Bit16u*)(pos+4)=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_SHRd:
+ *(Bit16u*)pos=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(14); // b after_call (pc+14)
+ break;
+ case t_SARb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=ASR_IMM(HOST_a1, HOST_a1, 24); // asr a1, a1, #24
+ *(Bit16u*)(pos+4)=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_SARw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=ASR_IMM(HOST_a1, HOST_a1, 16); // asr a1, a1, #16
+ *(Bit16u*)(pos+4)=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_SARd:
+ *(Bit16u*)pos=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(14); // b after_call (pc+14)
+ break;
+ case t_RORb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=LSR_IMM(templo1, HOST_a1, 8); // lsr templo1, a1, #8
+ *(Bit16u*)(pos+4)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+6)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+8)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+10)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+12)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_RORw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+4)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+6)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+8)=B_FWD(8); // b after_call (pc+8)
+ break;
+ case t_RORd:
+ *(Bit16u*)pos=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(14); // b after_call (pc+14)
+ break;
+ case t_ROLb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+4)=LSR_IMM(templo1, HOST_a1, 8); // lsr templo1, a1, #8
+ *(Bit16u*)(pos+6)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+8)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+10)=NOP; // nop
+ *(Bit16u*)(pos+12)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+14)=NOP; // nop
+ *(Bit16u*)(pos+16)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+18)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ break;
+ case t_ROLw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+4)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+6)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+8)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+10)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+12)=B_FWD(4); // b after_call (pc+4)
+ break;
+ case t_ROLd:
+ *(Bit16u*)pos=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+2)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+4)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_NEGb:
+ case t_NEGw:
+ case t_NEGd:
+ *(Bit16u*)pos=NEG(HOST_a1, HOST_a1); // neg a1, a1
+ *(Bit16u*)(pos+2)=B_FWD(14); // b after_call (pc+14)
+ break;
+ default:
+ *(Bit32u*)(pos+8)=(Bit32u)fct_ptr; // simple_func
+ break;
+ }
+ }
+ else
+ {
+ // try to avoid function calls but rather directly fill in code
+ switch (flags_type) {
+ case t_ADDb:
+ case t_ADDw:
+ case t_ADDd:
+ *(Bit16u*)pos=ADD_REG(HOST_a1, HOST_a1, HOST_a2); // add a1, a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(16); // b after_call (pc+16)
+ break;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ *(Bit16u*)pos=ORR(HOST_a1, HOST_a2); // orr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(16); // b after_call (pc+16)
+ break;
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ *(Bit16u*)pos=AND(HOST_a1, HOST_a2); // and a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(16); // b after_call (pc+16)
+ break;
+ case t_SUBb:
+ case t_SUBw:
+ case t_SUBd:
+ *(Bit16u*)pos=SUB_REG(HOST_a1, HOST_a1, HOST_a2); // sub a1, a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(16); // b after_call (pc+16)
+ break;
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ *(Bit16u*)pos=EOR(HOST_a1, HOST_a2); // eor a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(16); // b after_call (pc+16)
+ break;
+ case t_CMPb:
+ case t_CMPw:
+ case t_CMPd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ *(Bit16u*)pos=B_FWD(18); // b after_call (pc+18)
+ break;
+ case t_INCb:
+ case t_INCw:
+ case t_INCd:
+ *(Bit16u*)pos=ADD_IMM3(HOST_a1, HOST_a1, 1); // add a1, a1, #1
+ *(Bit16u*)(pos+2)=B_FWD(16); // b after_call (pc+16)
+ break;
+ case t_DECb:
+ case t_DECw:
+ case t_DECd:
+ *(Bit16u*)pos=SUB_IMM3(HOST_a1, HOST_a1, 1); // sub a1, a1, #1
+ *(Bit16u*)(pos+2)=B_FWD(16); // b after_call (pc+16)
+ break;
+ case t_SHLb:
+ case t_SHLw:
+ case t_SHLd:
+ *(Bit16u*)pos=LSL_REG(HOST_a1, HOST_a2); // lsl a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(16); // b after_call (pc+16)
+ break;
+ case t_SHRb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=LSR_IMM(HOST_a1, HOST_a1, 24); // lsr a1, a1, #24
+ *(Bit16u*)(pos+4)=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_SHRw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=LSR_IMM(HOST_a1, HOST_a1, 16); // lsr a1, a1, #16
+ *(Bit16u*)(pos+4)=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_SHRd:
+ *(Bit16u*)pos=LSR_REG(HOST_a1, HOST_a2); // lsr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(16); // b after_call (pc+16)
+ break;
+ case t_SARb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=ASR_IMM(HOST_a1, HOST_a1, 24); // asr a1, a1, #24
+ *(Bit16u*)(pos+4)=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_SARw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=ASR_IMM(HOST_a1, HOST_a1, 16); // asr a1, a1, #16
+ *(Bit16u*)(pos+4)=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_SARd:
+ *(Bit16u*)pos=ASR_REG(HOST_a1, HOST_a2); // asr a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(16); // b after_call (pc+16)
+ break;
+ case t_RORb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=LSR_IMM(templo1, HOST_a1, 8); // lsr templo1, a1, #8
+ *(Bit16u*)(pos+4)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+6)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+8)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+10)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+12)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_RORw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+4)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+6)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+8)=B_FWD(10); // b after_call (pc+10)
+ break;
+ case t_RORd:
+ *(Bit16u*)pos=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+2)=B_FWD(16); // b after_call (pc+16)
+ break;
+ case t_ROLb:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 24); // lsl a1, a1, #24
+ *(Bit16u*)(pos+2)=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+4)=LSR_IMM(templo1, HOST_a1, 8); // lsr templo1, a1, #8
+ *(Bit16u*)(pos+6)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+8)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+10)=NOP; // nop
+ *(Bit16u*)(pos+12)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+14)=NOP; // nop
+ *(Bit16u*)(pos+16)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+18)=NOP; // nop
+ *(Bit16u*)(pos+20)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ break;
+ case t_ROLw:
+ *(Bit16u*)pos=LSL_IMM(HOST_a1, HOST_a1, 16); // lsl a1, a1, #16
+ *(Bit16u*)(pos+2)=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+4)=LSR_IMM(templo1, HOST_a1, 16); // lsr templo1, a1, #16
+ *(Bit16u*)(pos+6)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+8)=ORR(HOST_a1, templo1); // orr a1, templo1
+ *(Bit16u*)(pos+10)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+12)=B_FWD(6); // b after_call (pc+6)
+ break;
+ case t_ROLd:
+ *(Bit16u*)pos=NEG(HOST_a2, HOST_a2); // neg a2, a2
+ *(Bit16u*)(pos+2)=ADD_IMM8(HOST_a2, 32); // add a2, #32
+ *(Bit16u*)(pos+4)=ROR_REG(HOST_a1, HOST_a2); // ror a1, a2
+ *(Bit16u*)(pos+6)=B_FWD(12); // b after_call (pc+12)
+ break;
+ case t_NEGb:
+ case t_NEGw:
+ case t_NEGd:
+ *(Bit16u*)pos=NEG(HOST_a1, HOST_a1); // neg a1, a1
+ *(Bit16u*)(pos+2)=B_FWD(16); // b after_call (pc+16)
+ break;
+ default:
+ *(Bit32u*)(pos+10)=(Bit32u)fct_ptr; // simple_func
+ break;
+ }
+
+ }
+#else
+ if (((Bit32u)pos & 0x03) == 0)
+ {
+ *(Bit32u*)(pos+8)=(Bit32u)fct_ptr; // simple_func
+ }
+ else
+ {
+ *(Bit32u*)(pos+10)=(Bit32u)fct_ptr; // simple_func
+ }
+#endif
+}
+#endif
+
+static void cache_block_before_close(void) {
+ if ((((Bit32u)cache.pos) & 3) != 0) {
+ cache_addw( NOP ); // nop
+ }
+}
+
+#ifdef DRC_USE_SEGS_ADDR
+
+// mov 16bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_seg16_to_reg(HostReg dest_reg,Bitu index) {
+ cache_addw( MOV_LO_HI(templo1, FC_SEGS_ADDR) ); // mov templo1, FC_SEGS_ADDR
+ cache_addw( LDRH_IMM(dest_reg, templo1, index) ); // ldrh dest_reg, [templo1, #index]
+}
+
+// mov 32bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_seg32_to_reg(HostReg dest_reg,Bitu index) {
+ cache_addw( MOV_LO_HI(templo1, FC_SEGS_ADDR) ); // mov templo1, FC_SEGS_ADDR
+ cache_addw( LDR_IMM(dest_reg, templo1, index) ); // ldr dest_reg, [templo1, #index]
+}
+
+// add a 32bit value from Segs[index] to a full register using FC_SEGS_ADDR (index modulo 4 must be zero)
+static void gen_add_seg32_to_reg(HostReg reg,Bitu index) {
+ cache_addw( MOV_LO_HI(templo1, FC_SEGS_ADDR) ); // mov templo1, FC_SEGS_ADDR
+ cache_addw( LDR_IMM(templo2, templo1, index) ); // ldr templo2, [templo1, #index]
+ cache_addw( ADD_REG(reg, reg, templo2) ); // add reg, reg, templo2
+}
+
+#endif
+
+#ifdef DRC_USE_REGS_ADDR
+
+// mov 16bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_regval16_to_reg(HostReg dest_reg,Bitu index) {
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDRH_IMM(dest_reg, templo2, index) ); // ldrh dest_reg, [templo2, #index]
+}
+
+// mov 32bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_regval32_to_reg(HostReg dest_reg,Bitu index) {
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDR_IMM(dest_reg, templo2, index) ); // ldr dest_reg, [templo2, #index]
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_regword_to_reg(HostReg dest_reg,Bitu index,bool dword) {
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ if (dword) {
+ cache_addw( LDR_IMM(dest_reg, templo2, index) ); // ldr dest_reg, [templo2, #index]
+ } else {
+ cache_addw( LDRH_IMM(dest_reg, templo2, index) ); // ldrh dest_reg, [templo2, #index]
+ }
+}
+
+// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_regbyte_to_reg_low(HostReg dest_reg,Bitu index) {
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDRB_IMM(dest_reg, templo2, index) ); // ldrb dest_reg, [templo2, #index]
+}
+
+// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_regbyte_to_reg_low_canuseword(HostReg dest_reg,Bitu index) {
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDRB_IMM(dest_reg, templo2, index) ); // ldrb dest_reg, [templo2, #index]
+}
+
+
+// add a 32bit value from cpu_regs[index] to a full register using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_add_regval32_to_reg(HostReg reg,Bitu index) {
+ cache_addw( MOV_LO_HI(templo2, FC_REGS_ADDR) ); // mov templo2, FC_REGS_ADDR
+ cache_addw( LDR_IMM(templo1, templo2, index) ); // ldr templo1, [templo2, #index]
+ cache_addw( ADD_REG(reg, reg, templo1) ); // add reg, reg, templo1
+}
+
+
+// move 16bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 2 must be zero)
+static void gen_mov_regval16_from_reg(HostReg src_reg,Bitu index) {
+ cache_addw( MOV_LO_HI(templo1, FC_REGS_ADDR) ); // mov templo1, FC_REGS_ADDR
+ cache_addw( STRH_IMM(src_reg, templo1, index) ); // strh src_reg, [templo1, #index]
+}
+
+// move 32bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_regval32_from_reg(HostReg src_reg,Bitu index) {
+ cache_addw( MOV_LO_HI(templo1, FC_REGS_ADDR) ); // mov templo1, FC_REGS_ADDR
+ cache_addw( STR_IMM(src_reg, templo1, index) ); // str src_reg, [templo1, #index]
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into cpu_regs[index] using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
+static void gen_mov_regword_from_reg(HostReg src_reg,Bitu index,bool dword) {
+ cache_addw( MOV_LO_HI(templo1, FC_REGS_ADDR) ); // mov templo1, FC_REGS_ADDR
+ if (dword) {
+ cache_addw( STR_IMM(src_reg, templo1, index) ); // str src_reg, [templo1, #index]
+ } else {
+ cache_addw( STRH_IMM(src_reg, templo1, index) ); // strh src_reg, [templo1, #index]
+ }
+}
+
+// move the lowest 8bit of a register into cpu_regs[index] using FC_REGS_ADDR
+static void gen_mov_regbyte_from_reg_low(HostReg src_reg,Bitu index) {
+ cache_addw( MOV_LO_HI(templo1, FC_REGS_ADDR) ); // mov templo1, FC_REGS_ADDR
+ cache_addw( STRB_IMM(src_reg, templo1, index) ); // strb src_reg, [templo1, #index]
+}
+
+#endif
diff --git a/src/cpu/core_dynrec/risc_armv4le.h b/src/cpu/core_dynrec/risc_armv4le.h
new file mode 100644
index 000000000..80f106048
--- /dev/null
+++ b/src/cpu/core_dynrec/risc_armv4le.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+/* ARMv4/ARMv7 (little endian) backend (switcher) by M-HT */
+
+#include "risc_armv4le-common.h"
+
+// choose your destiny:
+#if C_TARGETCPU == ARMV7LE
+ #include "risc_armv4le-o3.h"
+#else
+ #if defined(__THUMB_INTERWORK__)
+ #include "risc_armv4le-thumb-iw.h"
+ #else
+ #include "risc_armv4le-o3.h"
+// #include "risc_armv4le-thumb-niw.h"
+// #include "risc_armv4le-thumb.h"
+ #endif
+#endif
diff --git a/src/cpu/core_dynrec/risc_armv8le.h b/src/cpu/core_dynrec/risc_armv8le.h
new file mode 100644
index 000000000..3e455adfa
--- /dev/null
+++ b/src/cpu/core_dynrec/risc_armv8le.h
@@ -0,0 +1,1238 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+/* ARMv8 (little endian, 64-bit) backend by M-HT */
+
+
+// some configuring defines that specify the capabilities of this architecture
+// or aspects of the recompiling
+
+// protect FC_ADDR over function calls if necessaray
+// #define DRC_PROTECT_ADDR_REG
+
+// try to use non-flags generating functions if possible
+#define DRC_FLAGS_INVALIDATION
+// try to replace _simple functions by code
+#define DRC_FLAGS_INVALIDATION_DCODE
+
+// type with the same size as a pointer
+#define DRC_PTR_SIZE_IM Bit64u
+
+// calling convention modifier
+#define DRC_CALL_CONV /* nothing */
+#define DRC_FC /* nothing */
+
+// use FC_REGS_ADDR to hold the address of "cpu_regs" and to access it using FC_REGS_ADDR
+#define DRC_USE_REGS_ADDR
+// use FC_SEGS_ADDR to hold the address of "Segs" and to access it using FC_SEGS_ADDR
+#define DRC_USE_SEGS_ADDR
+
+// register mapping
+typedef Bit8u HostReg;
+
+// registers
+#define HOST_r0 0
+#define HOST_r1 1
+#define HOST_r2 2
+#define HOST_r3 3
+#define HOST_r4 4
+#define HOST_r5 5
+#define HOST_r6 6
+#define HOST_r7 7
+#define HOST_r8 8
+#define HOST_r9 9
+#define HOST_r10 10
+#define HOST_r11 11
+#define HOST_r12 12
+#define HOST_r13 13
+#define HOST_r14 14
+#define HOST_r15 15
+#define HOST_r16 16
+#define HOST_r17 17
+#define HOST_r18 18
+#define HOST_r19 19
+#define HOST_r20 20
+#define HOST_r21 21
+#define HOST_r22 22
+#define HOST_r23 23
+#define HOST_r24 24
+#define HOST_r25 25
+#define HOST_r26 26
+#define HOST_r27 27
+#define HOST_r28 28
+#define HOST_r29 29
+#define HOST_r30 30
+// special registers
+#define HOST_sp 31
+#define HOST_zr 31
+
+// register aliases
+// 32-bit registers
+#define HOST_w0 HOST_r0
+#define HOST_w1 HOST_r1
+#define HOST_w2 HOST_r2
+#define HOST_w3 HOST_r3
+#define HOST_w4 HOST_r4
+#define HOST_w5 HOST_r5
+#define HOST_w6 HOST_r6
+#define HOST_w7 HOST_r7
+#define HOST_w8 HOST_r8
+#define HOST_w9 HOST_r9
+#define HOST_w10 HOST_r10
+#define HOST_w11 HOST_r11
+#define HOST_w12 HOST_r12
+#define HOST_w13 HOST_r13
+#define HOST_w14 HOST_r14
+#define HOST_w15 HOST_r15
+#define HOST_w16 HOST_r16
+#define HOST_w17 HOST_r17
+#define HOST_w18 HOST_r18
+#define HOST_w19 HOST_r19
+#define HOST_w20 HOST_r20
+#define HOST_w21 HOST_r21
+#define HOST_w22 HOST_r22
+#define HOST_w23 HOST_r23
+#define HOST_w24 HOST_r24
+#define HOST_w25 HOST_r25
+#define HOST_w26 HOST_r26
+#define HOST_w27 HOST_r27
+#define HOST_w28 HOST_r28
+#define HOST_w29 HOST_r29
+#define HOST_w30 HOST_r30
+#define HOST_wsp HOST_sp
+#define HOST_wzr HOST_zr
+// 64-bit registers
+#define HOST_x0 HOST_r0
+#define HOST_x1 HOST_r1
+#define HOST_x2 HOST_r2
+#define HOST_x3 HOST_r3
+#define HOST_x4 HOST_r4
+#define HOST_x5 HOST_r5
+#define HOST_x6 HOST_r6
+#define HOST_x7 HOST_r7
+#define HOST_x8 HOST_r8
+#define HOST_x9 HOST_r9
+#define HOST_x10 HOST_r10
+#define HOST_x11 HOST_r11
+#define HOST_x12 HOST_r12
+#define HOST_x13 HOST_r13
+#define HOST_x14 HOST_r14
+#define HOST_x15 HOST_r15
+#define HOST_x16 HOST_r16
+#define HOST_x17 HOST_r17
+#define HOST_x18 HOST_r18
+#define HOST_x19 HOST_r19
+#define HOST_x20 HOST_r20
+#define HOST_x21 HOST_r21
+#define HOST_x22 HOST_r22
+#define HOST_x23 HOST_r23
+#define HOST_x24 HOST_r24
+#define HOST_x25 HOST_r25
+#define HOST_x26 HOST_r26
+#define HOST_x27 HOST_r27
+#define HOST_x28 HOST_r28
+#define HOST_x29 HOST_r29
+#define HOST_x30 HOST_r30
+#define HOST_xzr HOST_zr
+#define HOST_ip0 HOST_r16
+#define HOST_ip1 HOST_r17
+#define HOST_fp HOST_r29
+#define HOST_lr HOST_r30
+
+
+// temporary registers
+#define temp1 HOST_r10
+#define temp2 HOST_r11
+#define temp3 HOST_r12
+
+// register that holds function return values
+#define FC_RETOP HOST_r0
+
+// register used for address calculations,
+#define FC_ADDR HOST_r19 // has to be saved across calls, see DRC_PROTECT_ADDR_REG
+
+// register that holds the first parameter
+#define FC_OP1 HOST_r0
+
+// register that holds the second parameter
+#define FC_OP2 HOST_r1
+
+// special register that holds the third parameter for _R3 calls (byte accessible)
+#define FC_OP3 HOST_r2
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA1 HOST_r0
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA2 HOST_r1
+
+// temporary register for LEA
+#define TEMP_REG_DRC HOST_r9
+
+// used to hold the address of "cpu_regs" - preferably filled in function gen_run_code
+#define FC_REGS_ADDR HOST_r20
+
+// used to hold the address of "Segs" - preferably filled in function gen_run_code
+#define FC_SEGS_ADDR HOST_r21
+
+// used to hold the address of "core_dynrec.readdata" - filled in function gen_run_code
+#define readdata_addr HOST_r22
+
+
+// instruction encodings
+
+// move
+// mov dst, src, lsl #imm
+#define MOV_REG_LSL_IMM(dst, src, imm) ORR_REG_LSL_IMM(dst, HOST_wzr, src, imm)
+// movz dst, #(imm lsl simm) @ 0 <= imm <= 65535 & simm = 0/16
+#define MOVZ(dst, imm, simm) (0x52800000 + (dst) + ((imm) << 5) + ((simm)?0x00200000:0) )
+// movn dst, #(imm lsl simm) @ 0 <= imm <= 65535 & simm = 0/16
+#define MOVN(dst, imm, simm) (0x12800000 + (dst) + ((imm) << 5) + ((simm)?0x00200000:0) )
+// movk dst, #(imm lsl simm) @ 0 <= imm <= 65535 & simm = 0/16
+#define MOVK(dst, imm, simm) (0x72800000 + (dst) + ((imm) << 5) + ((simm)?0x00200000:0) )
+// movz dst, #(imm lsl simm) @ 0 <= imm <= 65535 & simm = 0/16/32/48
+#define MOVZ64(dst, imm, simm) (0xd2800000 + (dst) + ((imm) << 5) + (((simm) >> 4) << 21) )
+// movk dst, #(imm lsl simm) @ 0 <= imm <= 65535 & simm = 0/16/32/48
+#define MOVK64(dst, imm, simm) (0xf2800000 + (dst) + ((imm) << 5) + (((simm) >> 4) << 21) )
+// lslv dst, src, rreg
+#define LSLV(dst, src, rreg) (0x1ac02000 + (dst) + ((src) << 5) + ((rreg) << 16) )
+// lsrv dst, src, rreg
+#define LSRV(dst, src, rreg) (0x1ac02400 + (dst) + ((src) << 5) + ((rreg) << 16) )
+// asrv dst, src, rreg
+#define ASRV(dst, src, rreg) (0x1ac02800 + (dst) + ((src) << 5) + ((rreg) << 16) )
+// rorv dst, src, rreg
+#define RORV(dst, src, rreg) (0x1ac02c00 + (dst) + ((src) << 5) + ((rreg) << 16) )
+// lslv dst, src, rreg
+#define LSLV64(dst, src, rreg) (0x9ac02000 + (dst) + ((src) << 5) + ((rreg) << 16) )
+// lsrv dst, src, rreg
+#define LSRV64(dst, src, rreg) (0x9ac02400 + (dst) + ((src) << 5) + ((rreg) << 16) )
+// lsr dst, src, #imm
+#define LSR64_IMM(dst, src, imm) UBFM64(dst, src, imm, 63)
+
+// arithmetic
+// add dst, src, #(imm lsl simm) @ 0 <= imm <= 4095 & simm = 0/12
+#define ADD_IMM(dst, src, imm, simm) (0x11000000 + (dst) + ((src) << 5) + ((imm) << 10) + ((simm)?0x00400000:0) )
+// add dst, src1, src2, lsl #imm
+#define ADD_REG_LSL_IMM(dst, src1, src2, imm) (0x0b000000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) )
+// sub dst, src, #(imm lsl simm) @ 0 <= imm <= 4095 & simm = 0/12
+#define SUB_IMM(dst, src, imm, simm) (0x51000000 + (dst) + ((src) << 5) + ((imm) << 10) + ((simm)?0x00400000:0) )
+// sub dst, src1, src2, lsl #imm
+#define SUB_REG_LSL_IMM(dst, src1, src2, imm) (0x4b000000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) )
+// cmp src, #(imm lsl simm) @ 0 <= imm <= 4095 & simm = 0/12
+#define CMP_IMM(src, imm, simm) (0x7100001f + ((src) << 5) + ((imm) << 10) + ((simm)?0x00400000:0) )
+// nop
+#define NOP (0xd503201f)
+
+// logical
+// and dst, src1, src2, lsl #imm @ 0 <= imm <= 31
+#define AND_REG_LSL_IMM(dst, src1, src2, imm) (0x0a000000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) )
+// orr dst, src1, src2, lsl #imm @ 0 <= imm <= 31
+#define ORR_REG_LSL_IMM(dst, src1, src2, imm) (0x2a000000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) )
+// eor dst, src1, src2, lsl #imm @ 0 <= imm <= 31
+#define EOR_REG_LSL_IMM(dst, src1, src2, imm) (0x4a000000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) )
+// bic dst, src1, src2, lsl #imm @ 0 <= imm <= 31
+#define BIC_REG_LSL_IMM(dst, src1, src2, imm) (0x0a200000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) )
+// and dst, src1, src2, lsl #imm @ 0 <= imm <= 63
+#define AND64_REG_LSL_IMM(dst, src1, src2, imm) (0x8a000000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) )
+
+// load
+// ldr reg, [pc, #imm] @ -1M <= imm < 1M & imm mod 4 = 0
+#define LDR64_PC(reg, imm) (0x58000000 + (reg) + (((imm) << 3) & 0x00ffffe0) )
+// ldp reg1, reg2 [addr, #imm] @ -512 <= imm < 512 & imm mod 8 = 0
+#define LDP64_IMM(reg1, reg2, addr, imm) (0xa9400000 + (reg1) + ((reg2) << 10) + ((addr) << 5) + ((imm) << 12) )
+// ldr reg, [addr, #imm] @ 0 <= imm < 32768 & imm mod 8 = 0
+#define LDR64_IMM(reg, addr, imm) (0xf9400000 + (reg) + ((addr) << 5) + ((imm) << 7) )
+// ldr reg, [addr, #imm] @ 0 <= imm < 16384 & imm mod 4 = 0
+#define LDR_IMM(reg, addr, imm) (0xb9400000 + (reg) + ((addr) << 5) + ((imm) << 8) )
+// ldrh reg, [addr, #imm] @ 0 <= imm < 8192 & imm mod 2 = 0
+#define LDRH_IMM(reg, addr, imm) (0x79400000 + (reg) + ((addr) << 5) + ((imm) << 9) )
+// ldrb reg, [addr, #imm] @ 0 <= imm < 4096
+#define LDRB_IMM(reg, addr, imm) (0x39400000 + (reg) + ((addr) << 5) + ((imm) << 10) )
+// ldr reg, [addr1, addr2, lsl #imm] @ imm = 0/2
+#define LDR64_REG_LSL_IMM(reg, addr1, addr2, imm) (0xf8606800 + (reg) + ((addr1) << 5) + ((addr2) << 16) + ((imm)?0x00001000:0) )
+// ldur reg, [addr, #imm] @ -256 <= imm < 256
+#define LDUR64_IMM(reg, addr, imm) (0xf8400000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) )
+// ldur reg, [addr, #imm] @ -256 <= imm < 256
+#define LDUR_IMM(reg, addr, imm) (0xb8400000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) )
+// ldurh reg, [addr, #imm] @ -256 <= imm < 256
+#define LDURH_IMM(reg, addr, imm) (0x78400000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) )
+// ldurb reg, [addr, #imm] @ -256 <= imm < 256
+#define LDURB_IMM(reg, addr, imm) (0x38400000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) )
+
+// store
+// stp reg1, reg2 [addr, #imm] @ -512 <= imm < 512 & imm mod 8 = 0
+#define STP64_IMM(reg1, reg2, addr, imm) (0xa9000000 + (reg1) + ((reg2) << 10) + ((addr) << 5) + ((imm) << 12) )
+// str reg, [addr, #imm] @ 0 <= imm < 32768 & imm mod 8 = 0
+#define STR64_IMM(reg, addr, imm) (0xf9000000 + (reg) + ((addr) << 5) + ((imm) << 7) )
+// str reg, [addr, #imm] @ 0 <= imm < 16384 & imm mod 4 = 0
+#define STR_IMM(reg, addr, imm) (0xb9000000 + (reg) + ((addr) << 5) + ((imm) << 8) )
+// strh reg, [addr, #imm] @ 0 <= imm < 8192 & imm mod 2 = 0
+#define STRH_IMM(reg, addr, imm) (0x79000000 + (reg) + ((addr) << 5) + ((imm) << 9) )
+// strb reg, [addr, #imm] @ 0 <= imm < 4096
+#define STRB_IMM(reg, addr, imm) (0x39000000 + (reg) + ((addr) << 5) + ((imm) << 10) )
+// stur reg, [addr, #imm] @ -256 <= imm < 256
+#define STUR64_IMM(reg, addr, imm) (0xf8000000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) )
+// stur reg, [addr, #imm] @ -256 <= imm < 256
+#define STUR_IMM(reg, addr, imm) (0xb8000000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) )
+// sturh reg, [addr, #imm] @ -256 <= imm < 256
+#define STURH_IMM(reg, addr, imm) (0x78000000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) )
+// sturb reg, [addr, #imm] @ -256 <= imm < 256
+#define STURB_IMM(reg, addr, imm) (0x38000000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) )
+
+// branch
+// bgt pc+imm @ 0 <= imm < 1M & imm mod 4 = 0
+#define BGT_FWD(imm) (0x5400000c + ((imm) << 3) )
+// b pc+imm @ 0 <= imm < 128M & imm mod 4 = 0
+#define B_FWD(imm) (0x14000000 + ((imm) >> 2) )
+// br reg
+#define BR(reg) (0xd61f0000 + ((reg) << 5) )
+// blr reg
+#define BLR_REG(reg) (0xd63f0000 + ((reg) << 5) )
+// cbz reg, pc+imm @ 0 <= imm < 1M & imm mod 4 = 0
+#define CBZ_FWD(reg, imm) (0x34000000 + (reg) + ((imm) << 3) )
+// cbnz reg, pc+imm @ 0 <= imm < 1M & imm mod 4 = 0
+#define CBNZ_FWD(reg, imm) (0x35000000 + (reg) + ((imm) << 3) )
+// ret reg
+#define RET_REG(reg) (0xd65f0000 + ((reg) << 5) )
+// ret
+#define RET RET_REG(HOST_x30)
+
+// extend
+// sxth dst, src
+#define SXTH(dst, src) SBFM(dst, src, 0, 15)
+// sxtb dst, src
+#define SXTB(dst, src) SBFM(dst, src, 0, 7)
+// uxth dst, src
+#define UXTH(dst, src) UBFM(dst, src, 0, 15)
+// uxtb dst, src
+#define UXTB(dst, src) UBFM(dst, src, 0, 7)
+
+// bit field
+// bfi dst, src, #lsb, #width @ lsb >= 0, width >= 1, lsb+width <= 32
+#define BFI(dst, src, lsb, width) BFM(dst, src, (32 - (lsb)) & 0x1f, (width) - 1)
+// bfm dst, src, #rimm, #simm @ 0 <= rimm < 32, 0 <= simm < 32
+#define BFM(dst, src, rimm, simm) (0x33000000 + (dst) + ((src) << 5) + ((rimm) << 16) + ((simm) << 10) )
+// sbfm dst, src, #rimm, #simm @ 0 <= rimm < 32, 0 <= simm < 32
+#define SBFM(dst, src, rimm, simm) (0x13000000 + (dst) + ((src) << 5) + ((rimm) << 16) + ((simm) << 10) )
+// ubfm dst, src, #rimm, #simm @ 0 <= rimm < 32, 0 <= simm < 32
+#define UBFM(dst, src, rimm, simm) (0x53000000 + (dst) + ((src) << 5) + ((rimm) << 16) + ((simm) << 10) )
+// bfi dst, src, #lsb, #width @ lsb >= 0, width >= 1, lsb+width <= 64
+#define BFI64(dst, src, lsb, width) BFM64(dst, src, (64 - (lsb)) & 0x3f, (width) - 1)
+// bfm dst, src, #rimm, #simm @ 0 <= rimm < 64, 0 <= simm < 64
+#define BFM64(dst, src, rimm, simm) (0xb3400000 + (dst) + ((src) << 5) + ((rimm) << 16) + ((simm) << 10) )
+// ubfm dst, src, #rimm, #simm @ 0 <= rimm < 64, 0 <= simm < 64
+#define UBFM64(dst, src, rimm, simm) (0xd3400000 + (dst) + ((src) << 5) + ((rimm) << 16) + ((simm) << 10) )
+
+
+// move a full register from reg_src to reg_dst
+static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) {
+ if(reg_src == reg_dst) return;
+ cache_addd( MOV_REG_LSL_IMM(reg_dst, reg_src, 0) ); // mov reg_dst, reg_src
+}
+
+// move a 32bit constant value into dest_reg
+static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) {
+ if ( (imm & 0xffff0000) == 0 ) {
+ cache_addd( MOVZ(dest_reg, imm, 0) ); // movz dest_reg, #imm
+ } else if ( (imm & 0x0000ffff) == 0 ) {
+ cache_addd( MOVZ(dest_reg, imm >> 16, 16) ); // movz dest_reg, #(imm >> 16), lsl #16
+ } else if ( ((~imm) & 0xffff0000) == 0 ) {
+ cache_addd( MOVN(dest_reg, ~imm, 0) ); // movn dest_reg, #(~imm)
+ } else if ( ((~imm) & 0x0000ffff) == 0 ) {
+ cache_addd( MOVN(dest_reg, (~imm) >> 16, 16) ); // movn dest_reg, #(~imm >> 16), lsl #16
+ } else {
+ cache_addd( MOVZ(dest_reg, imm & 0xffff, 0) ); // movz dest_reg, #(imm & 0xffff)
+ cache_addd( MOVK(dest_reg, imm >> 16, 16) ); // movk dest_reg, #(imm >> 16), lsl #16
+ }
+}
+
+// helper function
+static bool gen_mov_memval_to_reg_helper(HostReg dest_reg, Bit64u data, Bitu size, HostReg addr_reg, Bit64u addr_data) {
+ switch (size) {
+ case 8:
+ if (((data & 7) == 0) && (data >= addr_data) && (data < addr_data + 32768)) {
+ cache_addd( LDR64_IMM(dest_reg, addr_reg, data - addr_data) ); // ldr dest_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data + 256) && (data >= addr_data - 256)) {
+ cache_addd( LDUR64_IMM(dest_reg, addr_reg, data - addr_data) ); // ldur dest_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ }
+ break;
+ case 4:
+ if (((data & 3) == 0) && (data >= addr_data) && (data < addr_data + 16384)) {
+ cache_addd( LDR_IMM(dest_reg, addr_reg, data - addr_data) ); // ldr dest_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data + 256) && (data >= addr_data - 256)) {
+ cache_addd( LDUR_IMM(dest_reg, addr_reg, data - addr_data) ); // ldur dest_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ }
+ break;
+ case 2:
+ if (((data & 1) == 0) && (data >= addr_data) && (data < addr_data + 8192)) {
+ cache_addd( LDRH_IMM(dest_reg, addr_reg, data - addr_data) ); // ldrh dest_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data + 256) && (data >= addr_data - 256)) {
+ cache_addd( LDURH_IMM(dest_reg, addr_reg, data - addr_data) ); // ldurh dest_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ }
+ break;
+ case 1:
+ if ((data >= addr_data) && (data < addr_data + 4096)) {
+ cache_addd( LDRB_IMM(dest_reg, addr_reg, data - addr_data) ); // ldrb dest_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data) && (data >= addr_data - 256)) {
+ cache_addd( LDURB_IMM(dest_reg, addr_reg, data - addr_data) ); // ldurb dest_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+// helper function
+static bool gen_mov_memval_to_reg(HostReg dest_reg, void *data, Bitu size) {
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit64u)data, size, FC_REGS_ADDR, (Bit64u)&cpu_regs)) return true;
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit64u)data, size, readdata_addr, (Bit64u)&core_dynrec.readdata)) return true;
+ if (gen_mov_memval_to_reg_helper(dest_reg, (Bit64u)data, size, FC_SEGS_ADDR, (Bit64u)&Segs)) return true;
+ return false;
+}
+
+// helper function - move a 64bit constant value into dest_reg
+static void gen_mov_qword_to_reg_imm(HostReg dest_reg,Bit64u imm) {
+ bool isfirst = true;
+
+ if ( (imm & 0xffff) != 0 ) {
+ cache_addd( MOVZ64(dest_reg, imm & 0xffff, 0) ); // movz dest_reg, #(imm & 0xffff)
+ isfirst = false;
+ }
+ if ( ((imm >> 16) & 0xffff) != 0 ) {
+ if (isfirst) {
+ isfirst = false;
+ cache_addd( MOVZ64(dest_reg, (imm >> 16) & 0xffff, 16) ); // movz dest_reg, #((imm >> 16) & 0xffff), lsl #16
+ } else {
+ cache_addd( MOVK64(dest_reg, (imm >> 16) & 0xffff, 16) ); // movk dest_reg, #((imm >> 16) & 0xffff), lsl #16
+ }
+ }
+ if ( ((imm >> 32) & 0xffff) != 0 ) {
+ if (isfirst) {
+ isfirst = false;
+ cache_addd( MOVZ64(dest_reg, (imm >> 32) & 0xffff, 32) ); // movz dest_reg, #((imm >> 32) & 0xffff), lsl #32
+ } else {
+ cache_addd( MOVK64(dest_reg, (imm >> 32) & 0xffff, 32) ); // movk dest_reg, #((imm >> 32) & 0xffff), lsl #32
+ }
+ }
+ if ( ((imm >> 48) & 0xffff) != 0 ) {
+ if (isfirst) {
+ isfirst = false;
+ cache_addd( MOVZ64(dest_reg, (imm >> 48) & 0xffff, 48) ); // movz dest_reg, #((imm >> 48) & 0xffff), lsl #48
+ } else {
+ cache_addd( MOVK64(dest_reg, (imm >> 48) & 0xffff, 48) ); // movk dest_reg, #((imm >> 48) & 0xffff), lsl #48
+ }
+ }
+ if (isfirst) {
+ cache_addd( MOVZ64(dest_reg, 0, 0) ); // movz dest_reg, #0
+ }
+}
+
+// helper function for gen_mov_word_to_reg
+static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,HostReg data_reg) {
+ if (dword) {
+ cache_addd( LDR_IMM(dest_reg, data_reg, 0) ); // ldr dest_reg, [data_reg]
+ } else {
+ cache_addd( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg]
+ }
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) {
+ if (!gen_mov_memval_to_reg(dest_reg, data, (dword)?4:2)) {
+ gen_mov_qword_to_reg_imm(temp1, (Bit64u)data);
+ gen_mov_word_to_reg_helper(dest_reg, data, dword, temp1);
+ }
+}
+
+// move a 16bit constant value into dest_reg
+// the upper 16bit of the destination register may be destroyed
+static void INLINE gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) {
+ cache_addd( MOVZ(dest_reg, imm, 0) ); // movz dest_reg, #imm
+}
+
+// helper function
+static bool gen_mov_memval_from_reg_helper(HostReg src_reg, Bit64u data, Bitu size, HostReg addr_reg, Bit64u addr_data) {
+ switch (size) {
+ case 8:
+ if (((data & 7) == 0) && (data >= addr_data) && (data < addr_data + 32768)) {
+ cache_addd( STR64_IMM(src_reg, addr_reg, data - addr_data) ); // str src_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data + 256) && (data >= addr_data - 256)) {
+ cache_addd( STUR64_IMM(src_reg, addr_reg, data - addr_data) ); // stur src_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ }
+ break;
+ case 4:
+ if (((data & 3) == 0) && (data >= addr_data) && (data < addr_data + 16384)) {
+ cache_addd( STR_IMM(src_reg, addr_reg, data - addr_data) ); // str src_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data + 256) && (data >= addr_data - 256)) {
+ cache_addd( STUR_IMM(src_reg, addr_reg, data - addr_data) ); // stur src_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ }
+ break;
+ case 2:
+ if (((data & 1) == 0) && (data >= addr_data) && (data < addr_data + 8192)) {
+ cache_addd( STRH_IMM(src_reg, addr_reg, data - addr_data) ); // strh src_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data + 256) && (data >= addr_data - 256)) {
+ cache_addd( STURH_IMM(src_reg, addr_reg, data - addr_data) ); // sturh src_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ }
+ break;
+ case 1:
+ if ((data >= addr_data) && (data < addr_data + 4096)) {
+ cache_addd( STRB_IMM(src_reg, addr_reg, data - addr_data) ); // strb src_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ } else if ((data < addr_data) && (data >= addr_data - 256)) {
+ cache_addd( STURB_IMM(src_reg, addr_reg, data - addr_data) ); // sturb src_reg, [addr_reg, #(data - addr_data)]
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+// helper function
+static bool gen_mov_memval_from_reg(HostReg src_reg, void *dest, Bitu size) {
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit64u)dest, size, FC_REGS_ADDR, (Bit64u)&cpu_regs)) return true;
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit64u)dest, size, readdata_addr, (Bit64u)&core_dynrec.readdata)) return true;
+ if (gen_mov_memval_from_reg_helper(src_reg, (Bit64u)dest, size, FC_SEGS_ADDR, (Bit64u)&Segs)) return true;
+ return false;
+}
+
+// helper function for gen_mov_word_from_reg
+static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, HostReg data_reg) {
+ if (dword) {
+ cache_addd( STR_IMM(src_reg, data_reg, 0) ); // str src_reg, [data_reg]
+ } else {
+ cache_addd( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg]
+ }
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into memory
+static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) {
+ if (!gen_mov_memval_from_reg(src_reg, dest, (dword)?4:2)) {
+ gen_mov_qword_to_reg_imm(temp1, (Bit64u)dest);
+ gen_mov_word_from_reg_helper(src_reg, dest, dword, temp1);
+ }
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) {
+ if (!gen_mov_memval_to_reg(dest_reg, data, 1)) {
+ gen_mov_qword_to_reg_imm(temp1, (Bit64u)data);
+ cache_addd( LDRB_IMM(dest_reg, temp1, 0) ); // ldrb dest_reg, [temp1]
+ }
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) {
+ gen_mov_byte_to_reg_low(dest_reg, data);
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) {
+ cache_addd( MOVZ(dest_reg, imm, 0) ); // movz dest_reg, #imm
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) {
+ gen_mov_byte_to_reg_low_imm(dest_reg, imm);
+}
+
+// move the lowest 8bit of a register into memory
+static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) {
+ if (!gen_mov_memval_from_reg(src_reg, dest, 1)) {
+ gen_mov_qword_to_reg_imm(temp1, (Bit64u)dest);
+ cache_addd( STRB_IMM(src_reg, temp1, 0) ); // strb src_reg, [temp1]
+ }
+}
+
+
+
+// convert an 8bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_byte(bool sign,HostReg reg) {
+ if (sign) {
+ cache_addd( SXTB(reg, reg) ); // sxtb reg, reg
+ } else {
+ cache_addd( UXTB(reg, reg) ); // uxtb reg, reg
+ }
+}
+
+// convert a 16bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_word(bool sign,HostReg reg) {
+ if (sign) {
+ cache_addd( SXTH(reg, reg) ); // sxth reg, reg
+ } else {
+ cache_addd( UXTH(reg, reg) ); // uxth reg, reg
+ }
+}
+
+// add a 32bit value from memory to a full register
+static void gen_add(HostReg reg,void* op) {
+ gen_mov_word_to_reg(temp3, op, 1);
+ cache_addd( ADD_REG_LSL_IMM(reg, reg, temp3, 0) ); // add reg, reg, temp3
+}
+
+// add a 32bit constant value to a full register
+static void gen_add_imm(HostReg reg,Bit32u imm) {
+ Bit32u imm2;
+
+ if(!imm) return;
+
+ imm2 = (Bit32u) (-((Bit32s)imm));
+
+ if (imm < 4096) {
+ cache_addd( ADD_IMM(reg, reg, imm, 0) ); // add reg, reg, #imm
+ } else if ((imm & 0xff000fff) == 0) {
+ cache_addd( ADD_IMM(reg, reg, imm >> 12, 12) ); // add reg, reg, #(imm >> 12), lsl #12
+ } else if (imm2 < 4096) {
+ cache_addd( SUB_IMM(reg, reg, imm2, 0) ); // sub reg, reg, #(-imm)
+ } else if ((imm2 & 0xff000fff) == 0) {
+ cache_addd( SUB_IMM(reg, reg, imm2 >> 12, 12) ); // sub reg, reg, #(-imm >> 12), lsl #12
+ } else if (imm2 < 0x10000) {
+ cache_addd( MOVZ(temp2, imm2, 0) ); // movz temp2, #(-imm)
+ cache_addd( SUB_REG_LSL_IMM(reg, reg, temp2, 0) ); // sub reg, reg, temp2
+ } else {
+ gen_mov_dword_to_reg_imm(temp2, imm);
+ cache_addd( ADD_REG_LSL_IMM(reg, reg, temp2, 0) ); // add reg, reg, temp2
+ }
+}
+
+// and a 32bit constant value with a full register
+static void gen_and_imm(HostReg reg,Bit32u imm) {
+ Bit32u imm2, scale;
+
+ imm2 = ~imm;
+ if(!imm2) return;
+
+ if (!imm) {
+ cache_addd( MOVZ(reg, 0, 0) ); // movz reg, #0
+ } else if (imm2 < 0x10000) {
+ cache_addd( MOVZ(temp2, imm2, 0) ); // movz temp2, #(~imm)
+ cache_addd( BIC_REG_LSL_IMM(reg, reg, temp2, 0) ); // bic reg, reg, temp2
+ } else if ((imm2 & 0xffff) == 0) {
+ cache_addd( MOVZ(temp2, imm2 >> 16, 16) ); // movz temp2, #(~imm >> 16), lsl #16
+ cache_addd( BIC_REG_LSL_IMM(reg, reg, temp2, 0) ); // bic reg, reg, temp2
+ } else {
+ gen_mov_dword_to_reg_imm(temp2, imm);
+ cache_addd( AND_REG_LSL_IMM(reg, reg, temp2, 0) ); // and reg, reg, temp2
+ }
+}
+
+
+// move a 32bit constant value into memory
+static void gen_mov_direct_dword(void* dest,Bit32u imm) {
+ gen_mov_dword_to_reg_imm(temp3, imm);
+ gen_mov_word_from_reg(temp3, dest, 1);
+}
+
+// move an address into memory
+static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) {
+ gen_mov_qword_to_reg_imm(temp3, imm);
+ if (!gen_mov_memval_from_reg(temp3, dest, 8)) {
+ gen_mov_qword_to_reg_imm(temp1, (Bit64u)dest);
+ cache_addd( STR64_IMM(temp3, temp1, 0) ); // str temp3, [temp1]
+ }
+}
+
+// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value
+static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) {
+ if (!dword) imm &= 0xffff;
+ if(!imm) return;
+
+ if (!gen_mov_memval_to_reg(temp3, dest, (dword)?4:2)) {
+ gen_mov_qword_to_reg_imm(temp1, (Bit64u)dest);
+ gen_mov_word_to_reg_helper(temp3, dest, dword, temp1);
+ }
+ gen_add_imm(temp3, imm);
+ if (!gen_mov_memval_from_reg(temp3, dest, (dword)?4:2)) {
+ gen_mov_word_from_reg_helper(temp3, dest, dword, temp1);
+ }
+}
+
+// add an 8bit constant value to a dword memory value
+static void gen_add_direct_byte(void* dest,Bit8s imm) {
+ gen_add_direct_word(dest, (Bit32s)imm, 1);
+}
+
+// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value
+static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) {
+ Bit32u imm2;
+
+ if (!dword) imm &= 0xffff;
+ if(!imm) return;
+
+ if (!gen_mov_memval_to_reg(temp3, dest, (dword)?4:2)) {
+ gen_mov_qword_to_reg_imm(temp1, (Bit64u)dest);
+ gen_mov_word_to_reg_helper(temp3, dest, dword, temp1);
+ }
+
+ imm2 = (Bit32u) (-((Bit32s)imm));
+
+ if (imm < 4096) {
+ cache_addd( SUB_IMM(temp3, temp3, imm, 0) ); // sub temp3, temp3, #imm
+ } else if ((imm & 0xff000fff) == 0) {
+ cache_addd( SUB_IMM(temp3, temp3, imm >> 12, 12) ); // sub temp3, temp3, #(imm >> 12), lsl #12
+ } else if (imm2 < 4096) {
+ cache_addd( ADD_IMM(temp3, temp3, imm2, 0) ); // add temp3, temp3, #(-imm)
+ } else if ((imm2 & 0xff000fff) == 0) {
+ cache_addd( ADD_IMM(temp3, temp3, imm2 >> 12, 12) ); // add temp3, temp3, #(-imm >> 12), lsl #12
+ } else if (imm2 < 0x10000) {
+ cache_addd( MOVZ(temp2, imm2, 0) ); // movz temp2, #(-imm)
+ cache_addd( ADD_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // add temp3, temp3, temp2
+ } else {
+ gen_mov_dword_to_reg_imm(temp2, imm);
+ cache_addd( SUB_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // sub temp3, temp3, temp2
+ }
+
+ if (!gen_mov_memval_from_reg(temp3, dest, (dword)?4:2)) {
+ gen_mov_word_from_reg_helper(temp3, dest, dword, temp1);
+ }
+}
+
+// subtract an 8bit constant value from a dword memory value
+static void gen_sub_direct_byte(void* dest,Bit8s imm) {
+ gen_sub_direct_word(dest, (Bit32s)imm, 1);
+}
+
+// effective address calculation, destination is dest_reg
+// scale_reg is scaled by scale (scale_reg*(2^scale)) and
+// added to dest_reg, then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) {
+ cache_addd( ADD_REG_LSL_IMM(dest_reg, dest_reg, scale_reg, scale) ); // add dest_reg, dest_reg, scale_reg, lsl #scale
+ gen_add_imm(dest_reg, imm);
+}
+
+// effective address calculation, destination is dest_reg
+// dest_reg is scaled by scale (dest_reg*(2^scale)),
+// then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) {
+ if (scale) {
+ cache_addd( MOV_REG_LSL_IMM(dest_reg, dest_reg, scale) ); // mov dest_reg, dest_reg, lsl #scale
+ }
+ gen_add_imm(dest_reg, imm);
+}
+
+// generate a call to a parameterless function
+static void INLINE gen_call_function_raw(void * func) {
+ cache_addd( MOVZ64(temp1, ((Bit64u)func) & 0xffff, 0) ); // movz dest_reg, #(func & 0xffff)
+ cache_addd( MOVK64(temp1, (((Bit64u)func) >> 16) & 0xffff, 16) ); // movk dest_reg, #((func >> 16) & 0xffff), lsl #16
+ cache_addd( MOVK64(temp1, (((Bit64u)func) >> 32) & 0xffff, 32) ); // movk dest_reg, #((func >> 32) & 0xffff), lsl #32
+ cache_addd( MOVK64(temp1, (((Bit64u)func) >> 48) & 0xffff, 48) ); // movk dest_reg, #((func >> 48) & 0xffff), lsl #48
+ cache_addd( BLR_REG(temp1) ); // blr temp1
+}
+
+// generate a call to a function with paramcount parameters
+// note: the parameters are loaded in the architecture specific way
+// using the gen_load_param_ functions below
+static DRC_PTR_SIZE_IM INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) {
+ DRC_PTR_SIZE_IM proc_addr = (DRC_PTR_SIZE_IM)cache.pos;
+ gen_call_function_raw(func);
+ return proc_addr;
+}
+
+// load an immediate value as param'th function parameter
+static void INLINE gen_load_param_imm(Bitu imm,Bitu param) {
+ gen_mov_qword_to_reg_imm(param, imm);
+}
+
+// load an address as param'th function parameter
+static void INLINE gen_load_param_addr(DRC_PTR_SIZE_IM addr,Bitu param) {
+ gen_mov_qword_to_reg_imm(param, addr);
+}
+
+// load a host-register as param'th function parameter
+static void INLINE gen_load_param_reg(Bitu reg,Bitu param) {
+ gen_mov_regs(param, reg);
+}
+
+// load a value from memory as param'th function parameter
+static void INLINE gen_load_param_mem(Bitu mem,Bitu param) {
+ gen_mov_word_to_reg(param, (void *)mem, 1);
+}
+
+// jump to an address pointed at by ptr, offset is in imm
+static void gen_jmp_ptr(void * ptr,Bits imm=0) {
+ if (!gen_mov_memval_to_reg(temp3, ptr, 8)) {
+ gen_mov_qword_to_reg_imm(temp1, (Bit64u)ptr);
+ cache_addd( LDR64_IMM(temp3, temp1, 0) ); // ldr temp3, [temp1]
+ }
+
+ if (((imm & 7) == 0) && (imm >= 0) && (imm < 32768)) {
+ cache_addd( LDR64_IMM(temp1, temp3, imm) ); // ldr temp1, [temp3, #imm]
+ } else if ((imm < 256) && (imm >= -256)) {
+ cache_addd( LDUR64_IMM(temp1, temp3, imm) ); // ldur temp1, [temp3, #imm]
+ } else {
+ gen_mov_qword_to_reg_imm(temp2, imm);
+ cache_addd( LDR64_REG_LSL_IMM(temp1, temp3, temp2, 0) ); // ldr temp1, [temp3, temp2]
+ }
+
+ cache_addd( BR(temp1) ); // br temp1
+}
+
+// short conditional jump (+-127 bytes) if register is zero
+// the destination is set by gen_fill_branch() later
+static DRC_PTR_SIZE_IM gen_create_branch_on_zero(HostReg reg,bool dword) {
+ if (dword) {
+ cache_addd( CBZ_FWD(reg, 0) ); // cbz reg, j
+ } else {
+ cache_addd( UXTH(temp1, reg) ); // uxth temp1, reg
+ cache_addd( CBZ_FWD(temp1, 0) ); // cbz temp1, j
+ }
+ return ((DRC_PTR_SIZE_IM)cache.pos-4);
+}
+
+// short conditional jump (+-127 bytes) if register is nonzero
+// the destination is set by gen_fill_branch() later
+static DRC_PTR_SIZE_IM gen_create_branch_on_nonzero(HostReg reg,bool dword) {
+ if (dword) {
+ cache_addd( CBNZ_FWD(reg, 0) ); // cbnz reg, j
+ } else {
+ cache_addd( UXTH(temp1, reg) ); // uxth temp1, reg
+ cache_addd( CBNZ_FWD(temp1, 0) ); // cbnz temp1, j
+ }
+ return ((DRC_PTR_SIZE_IM)cache.pos-4);
+}
+
+// calculate relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch(DRC_PTR_SIZE_IM data) {
+#if C_DEBUG
+ Bits len=(Bit64u)cache.pos-data;
+ if (len<0) len=-len;
+ if (len>=0x00100000) LOG_MSG("Big jump %d",len);
+#endif
+ *(Bit32u*)data=( (*(Bit32u*)data) & 0xff00001f ) | ( ( ((Bit64u)cache.pos - data) << 3 ) & 0x00ffffe0 );
+}
+
+// conditional jump if register is nonzero
+// for isdword==true the 32bit of the register are tested
+// for isdword==false the lowest 8bit of the register are tested
+static DRC_PTR_SIZE_IM gen_create_branch_long_nonzero(HostReg reg,bool isdword) {
+ if (isdword) {
+ cache_addd( CBZ_FWD(reg, 8) ); // cbz reg, pc+8 // skip next instruction
+ } else {
+ cache_addd( UXTB(temp1, reg) ); // uxtb temp1, reg
+ cache_addd( CBZ_FWD(temp1, 8) ); // cbz temp1, pc+8 // skip next instruction
+ }
+ cache_addd( B_FWD(0) ); // b j
+ return ((DRC_PTR_SIZE_IM)cache.pos-4);
+}
+
+// compare 32bit-register against zero and jump if value less/equal than zero
+static DRC_PTR_SIZE_IM gen_create_branch_long_leqzero(HostReg reg) {
+ cache_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0
+ cache_addd( BGT_FWD(8) ); // bgt pc+8 // skip next instruction
+ cache_addd( B_FWD(0) ); // b j
+ return ((DRC_PTR_SIZE_IM)cache.pos-4);
+}
+
+// calculate long relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch_long(DRC_PTR_SIZE_IM data) {
+ // optimize for shorter branches ?
+ *(Bit32u*)data=( (*(Bit32u*)data) & 0xfc000000 ) | ( ( ((Bit64u)cache.pos - data) >> 2 ) & 0x03ffffff );
+}
+
+static void gen_run_code(void) {
+ Bit8u *pos1, *pos2, *pos3;
+
+ cache_addd( 0xa9bd7bfd ); // stp fp, lr, [sp, #-48]!
+ cache_addd( 0x910003fd ); // mov fp, sp
+ cache_addd( STP64_IMM(FC_ADDR, FC_REGS_ADDR, HOST_sp, 16) ); // stp FC_ADDR, FC_REGS_ADDR, [sp, #16]
+ cache_addd( STP64_IMM(FC_SEGS_ADDR, readdata_addr, HOST_sp, 32) ); // stp FC_SEGS_ADDR, readdata_addr, [sp, #32]
+
+ pos1 = cache.pos;
+ cache_addd( 0 );
+ pos2 = cache.pos;
+ cache_addd( 0 );
+ pos3 = cache.pos;
+ cache_addd( 0 );
+
+ cache_addd( BR(HOST_x0) ); // br x0
+
+ // align cache.pos to 32 bytes
+ if ((((Bitu)cache.pos) & 0x1f) != 0) {
+ cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f));
+ }
+
+ *(Bit32u *)pos1 = LDR64_PC(FC_SEGS_ADDR, cache.pos - pos1); // ldr FC_SEGS_ADDR, [pc, #(&Segs)]
+ cache_addq((Bit64u)&Segs); // address of "Segs"
+
+ *(Bit32u *)pos2 = LDR64_PC(FC_REGS_ADDR, cache.pos - pos2); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)]
+ cache_addq((Bit64u)&cpu_regs); // address of "cpu_regs"
+
+ *(Bit32u *)pos3 = LDR64_PC(readdata_addr, cache.pos - pos3); // ldr readdata_addr, [pc, #(&core_dynrec.readdata)]
+ cache_addq((Bit64u)&core_dynrec.readdata); // address of "core_dynrec.readdata"
+
+ // align cache.pos to 32 bytes
+ if ((((Bitu)cache.pos) & 0x1f) != 0) {
+ cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f));
+ }
+}
+
+// return from a function
+static void gen_return_function(void) {
+ cache_addd( LDP64_IMM(FC_ADDR, FC_REGS_ADDR, HOST_sp, 16) ); // ldp FC_ADDR, FC_REGS_ADDR, [sp, #16]
+ cache_addd( LDP64_IMM(FC_SEGS_ADDR, readdata_addr, HOST_sp, 32) ); // ldp FC_SEGS_ADDR, readdata_addr, [sp, #32]
+ cache_addd( 0xa8c37bfd ); // ldp fp, lr, [sp], #48
+ cache_addd( RET ); // ret
+}
+
+#ifdef DRC_FLAGS_INVALIDATION
+
+// called when a call to a function can be replaced by a
+// call to a simpler function
+static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) {
+#ifdef DRC_FLAGS_INVALIDATION_DCODE
+ // try to avoid function calls but rather directly fill in code
+ switch (flags_type) {
+ case t_ADDb:
+ case t_ADDw:
+ case t_ADDd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=ADD_REG_LSL_IMM(FC_RETOP, HOST_w0, HOST_w1, 0); // add FC_RETOP, w0, w1
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=ORR_REG_LSL_IMM(FC_RETOP, HOST_w0, HOST_w1, 0); // orr FC_RETOP, w0, w1
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=AND_REG_LSL_IMM(FC_RETOP, HOST_w0, HOST_w1, 0); // and FC_RETOP, w0, w1
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_SUBb:
+ case t_SUBw:
+ case t_SUBd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=SUB_REG_LSL_IMM(FC_RETOP, HOST_w0, HOST_w1, 0); // sub FC_RETOP, w0, w1
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=EOR_REG_LSL_IMM(FC_RETOP, HOST_w0, HOST_w1, 0); // eor FC_RETOP, w0, w1
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_CMPb:
+ case t_CMPw:
+ case t_CMPd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=NOP; // nop
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_INCb:
+ case t_INCw:
+ case t_INCd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=ADD_IMM(FC_RETOP, HOST_w0, 1, 0); // add FC_RETOP, w0, #1
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_DECb:
+ case t_DECw:
+ case t_DECd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=SUB_IMM(FC_RETOP, HOST_w0, 1, 0); // sub FC_RETOP, w0, #1
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_SHLb:
+ case t_SHLw:
+ case t_SHLd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=LSLV(FC_RETOP, HOST_w0, HOST_w1); // lslv FC_RETOP, w0, w1
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_SHRb:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=UXTB(FC_RETOP, HOST_w0); // uxtb FC_RETOP, w0
+ *(Bit32u*)(pos+8)=NOP; // nop
+ *(Bit32u*)(pos+12)=LSRV(FC_RETOP, FC_RETOP, HOST_w1); // lsrv FC_RETOP, FC_RETOP, w1
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_SHRw:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=UXTH(FC_RETOP, HOST_w0); // uxth FC_RETOP, w0
+ *(Bit32u*)(pos+8)=NOP; // nop
+ *(Bit32u*)(pos+12)=LSRV(FC_RETOP, FC_RETOP, HOST_w1); // lsrv FC_RETOP, FC_RETOP, w1
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_SHRd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=LSRV(FC_RETOP, HOST_w0, HOST_w1); // lsrv FC_RETOP, w0, w1
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_SARb:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=SXTB(FC_RETOP, HOST_w0); // sxtb FC_RETOP, w0
+ *(Bit32u*)(pos+8)=NOP; // nop
+ *(Bit32u*)(pos+12)=ASRV(FC_RETOP, FC_RETOP, HOST_w1); // asrv FC_RETOP, FC_RETOP, w1
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_SARw:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=SXTH(FC_RETOP, HOST_w0); // sxth FC_RETOP, w0
+ *(Bit32u*)(pos+8)=NOP; // nop
+ *(Bit32u*)(pos+12)=ASRV(FC_RETOP, FC_RETOP, HOST_w1); // asrv FC_RETOP, FC_RETOP, w1
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_SARd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=ASRV(FC_RETOP, HOST_w0, HOST_w1); // asrv FC_RETOP, w0, w1
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_RORb:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=BFI(HOST_w0, HOST_w0, 8, 8); // bfi w0, w0, 8, 8
+ *(Bit32u*)(pos+8)=BFI(HOST_w0, HOST_w0, 16, 16); // bfi w0, w0, 16, 16
+ *(Bit32u*)(pos+12)=RORV(FC_RETOP, HOST_w0, HOST_w1); // rorv FC_RETOP, w0, w1
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_RORw:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=BFI(HOST_w0, HOST_w0, 16, 16); // bfi w0, w0, 16, 16
+ *(Bit32u*)(pos+8)=NOP; // nop
+ *(Bit32u*)(pos+12)=RORV(FC_RETOP, HOST_w0, HOST_w1); // rorv FC_RETOP, w0, w1
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_RORd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=RORV(FC_RETOP, HOST_w0, HOST_w1); // rorv FC_RETOP, w0, w1
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_ROLb:
+ *(Bit32u*)pos=MOVZ(HOST_w2, 32, 0); // movz w2, #32
+ *(Bit32u*)(pos+4)=BFI(HOST_w0, HOST_w0, 8, 8); // bfi w0, w0, 8, 8
+ *(Bit32u*)(pos+8)=SUB_REG_LSL_IMM(HOST_w2, HOST_w2, HOST_w1, 0); // sub w2, w2, w1
+ *(Bit32u*)(pos+12)=BFI(HOST_w0, HOST_w0, 16, 16); // bfi w0, w0, 16, 16
+ *(Bit32u*)(pos+16)=RORV(FC_RETOP, HOST_w0, HOST_w2); // rorv FC_RETOP, w0, w2
+ break;
+ case t_ROLw:
+ *(Bit32u*)pos=MOVZ(HOST_w2, 32, 0); // movz w2, #32
+ *(Bit32u*)(pos+4)=BFI(HOST_w0, HOST_w0, 16, 16); // bfi w0, w0, 16, 16
+ *(Bit32u*)(pos+8)=SUB_REG_LSL_IMM(HOST_w2, HOST_w2, HOST_w1, 0); // sub w2, w2, w1
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=RORV(FC_RETOP, HOST_w0, HOST_w2); // rorv FC_RETOP, w0, w2
+ break;
+ case t_ROLd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=MOVZ(HOST_w2, 32, 0); // movz w2, #32
+ *(Bit32u*)(pos+8)=SUB_REG_LSL_IMM(HOST_w2, HOST_w2, HOST_w1, 0); // sub w2, w2, w1
+ *(Bit32u*)(pos+12)=RORV(FC_RETOP, HOST_w0, HOST_w2); // rorv FC_RETOP, w0, w2
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_NEGb:
+ case t_NEGw:
+ case t_NEGd:
+ *(Bit32u*)pos=NOP; // nop
+ *(Bit32u*)(pos+4)=NOP; // nop
+ *(Bit32u*)(pos+8)=SUB_REG_LSL_IMM(FC_RETOP, HOST_wzr, HOST_w0, 0); // sub FC_RETOP, wzr, w0
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=NOP; // nop
+ break;
+ case t_DSHLd:
+ *(Bit32u*)pos=MOVZ64(HOST_x3, 0x1f, 0); // movz x3, #0x1f
+ *(Bit32u*)(pos+4)=BFI64(HOST_x1, HOST_x0, 32, 32); // bfi x1, x0, 32, 32
+ *(Bit32u*)(pos+8)=AND64_REG_LSL_IMM(HOST_x2, HOST_x2, HOST_x3, 0); // and x2, x2, x3
+ *(Bit32u*)(pos+12)=LSLV64(FC_RETOP, HOST_x1, HOST_x2); // lslv FC_RETOP, x1, x2
+ *(Bit32u*)(pos+16)=LSR64_IMM(FC_RETOP, FC_RETOP, 32); // lsr FC_RETOP, FC_RETOP, #32
+ break;
+ case t_DSHRd:
+ *(Bit32u*)pos=MOVZ64(HOST_x3, 0x1f, 0); // movz x3, #0x1f
+ *(Bit32u*)(pos+4)=BFI64(HOST_x0, HOST_x1, 32, 32); // bfi x0, x1, 32, 32
+ *(Bit32u*)(pos+8)=AND64_REG_LSL_IMM(HOST_x2, HOST_x2, HOST_x3, 0); // and x2, x2, x3
+ *(Bit32u*)(pos+12)=NOP; // nop
+ *(Bit32u*)(pos+16)=LSRV64(FC_RETOP, HOST_x0, HOST_x2); // lsrv FC_RETOP, x0, x2
+ break;
+ default:
+ *(Bit32u*)pos=MOVZ64(temp1, ((Bit64u)fct_ptr) & 0xffff, 0); // movz temp1, #(fct_ptr & 0xffff)
+ *(Bit32u*)(pos+4)=MOVK64(temp1, (((Bit64u)fct_ptr) >> 16) & 0xffff, 16); // movk temp1, #((fct_ptr >> 16) & 0xffff), lsl #16
+ *(Bit32u*)(pos+8)=MOVK64(temp1, (((Bit64u)fct_ptr) >> 32) & 0xffff, 32); // movk temp1, #((fct_ptr >> 32) & 0xffff), lsl #32
+ *(Bit32u*)(pos+12)=MOVK64(temp1, (((Bit64u)fct_ptr) >> 48) & 0xffff, 48); // movk temp1, #((fct_ptr >> 48) & 0xffff), lsl #48
+ break;
+
+ }
+#else
+ *(Bit32u*)pos=MOVZ64(temp1, ((Bit64u)fct_ptr) & 0xffff, 0); // movz temp1, #(fct_ptr & 0xffff)
+ *(Bit32u*)(pos+4)=MOVK64(temp1, (((Bit64u)fct_ptr) >> 16) & 0xffff, 16); // movk temp1, #((fct_ptr >> 16) & 0xffff), lsl #16
+ *(Bit32u*)(pos+8)=MOVK64(temp1, (((Bit64u)fct_ptr) >> 32) & 0xffff, 32); // movk temp1, #((fct_ptr >> 32) & 0xffff), lsl #32
+ *(Bit32u*)(pos+12)=MOVK64(temp1, (((Bit64u)fct_ptr) >> 48) & 0xffff, 48); // movk temp1, #((fct_ptr >> 48) & 0xffff), lsl #48
+#endif
+}
+#endif
+
+static void cache_block_closing(Bit8u* block_start,Bitu block_size) {
+ //flush cache - GCC/LLVM builtin
+ __builtin___clear_cache((char *)block_start, (char *)(block_start+block_size));
+}
+
+static void cache_block_before_close(void) { }
+
+#ifdef DRC_USE_SEGS_ADDR
+
+// mov 16bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_seg16_to_reg(HostReg dest_reg,Bitu index) {
+ cache_addd( LDRH_IMM(dest_reg, FC_SEGS_ADDR, index) ); // ldrh dest_reg, [FC_SEGS_ADDR, #index]
+}
+
+// mov 32bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_seg32_to_reg(HostReg dest_reg,Bitu index) {
+ cache_addd( LDR_IMM(dest_reg, FC_SEGS_ADDR, index) ); // ldr dest_reg, [FC_SEGS_ADDR, #index]
+}
+
+// add a 32bit value from Segs[index] to a full register using FC_SEGS_ADDR (index modulo 4 must be zero)
+static void gen_add_seg32_to_reg(HostReg reg,Bitu index) {
+ cache_addd( LDR_IMM(temp1, FC_SEGS_ADDR, index) ); // ldr temp1, [FC_SEGS_ADDR, #index]
+ cache_addd( ADD_REG_LSL_IMM(reg, reg, temp1, 0) ); // add reg, reg, temp1
+}
+
+#endif
+
+#ifdef DRC_USE_REGS_ADDR
+
+// mov 16bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_regval16_to_reg(HostReg dest_reg,Bitu index) {
+ cache_addd( LDRH_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrh dest_reg, [FC_REGS_ADDR, #index]
+}
+
+// mov 32bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_regval32_to_reg(HostReg dest_reg,Bitu index) {
+ cache_addd( LDR_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldr dest_reg, [FC_REGS_ADDR, #index]
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_regword_to_reg(HostReg dest_reg,Bitu index,bool dword) {
+ if (dword) {
+ cache_addd( LDR_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldr dest_reg, [FC_REGS_ADDR, #index]
+ } else {
+ cache_addd( LDRH_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrh dest_reg, [FC_REGS_ADDR, #index]
+ }
+}
+
+// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_regbyte_to_reg_low(HostReg dest_reg,Bitu index) {
+ cache_addd( LDRB_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrb dest_reg, [FC_REGS_ADDR, #index]
+}
+
+// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void gen_mov_regbyte_to_reg_low_canuseword(HostReg dest_reg,Bitu index) {
+ cache_addd( LDRB_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrb dest_reg, [FC_REGS_ADDR, #index]
+}
+
+
+// add a 32bit value from cpu_regs[index] to a full register using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_add_regval32_to_reg(HostReg reg,Bitu index) {
+ cache_addd( LDR_IMM(temp2, FC_REGS_ADDR, index) ); // ldr temp2, [FC_REGS_ADDR, #index]
+ cache_addd( ADD_REG_LSL_IMM(reg, reg, temp2, 0) ); // add reg, reg, temp2
+}
+
+
+// move 16bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 2 must be zero)
+static void gen_mov_regval16_from_reg(HostReg src_reg,Bitu index) {
+ cache_addd( STRH_IMM(src_reg, FC_REGS_ADDR, index) ); // strh src_reg, [FC_REGS_ADDR, #index]
+}
+
+// move 32bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_regval32_from_reg(HostReg src_reg,Bitu index) {
+ cache_addd( STR_IMM(src_reg, FC_REGS_ADDR, index) ); // str src_reg, [FC_REGS_ADDR, #index]
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into cpu_regs[index] using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
+static void gen_mov_regword_from_reg(HostReg src_reg,Bitu index,bool dword) {
+ if (dword) {
+ cache_addd( STR_IMM(src_reg, FC_REGS_ADDR, index) ); // str src_reg, [FC_REGS_ADDR, #index]
+ } else {
+ cache_addd( STRH_IMM(src_reg, FC_REGS_ADDR, index) ); // strh src_reg, [FC_REGS_ADDR, #index]
+ }
+}
+
+// move the lowest 8bit of a register into cpu_regs[index] using FC_REGS_ADDR
+static void gen_mov_regbyte_from_reg_low(HostReg src_reg,Bitu index) {
+ cache_addd( STRB_IMM(src_reg, FC_REGS_ADDR, index) ); // strb src_reg, [FC_REGS_ADDR, #index]
+}
+
+#endif
diff --git a/src/cpu/core_dynrec/risc_mipsel32.h b/src/cpu/core_dynrec/risc_mipsel32.h
new file mode 100644
index 000000000..a36e451c5
--- /dev/null
+++ b/src/cpu/core_dynrec/risc_mipsel32.h
@@ -0,0 +1,750 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+/* MIPS32 (little endian) backend by crazyc */
+
+
+// some configuring defines that specify the capabilities of this architecture
+// or aspects of the recompiling
+
+// protect FC_ADDR over function calls if necessaray
+// #define DRC_PROTECT_ADDR_REG
+
+// try to use non-flags generating functions if possible
+#define DRC_FLAGS_INVALIDATION
+// try to replace _simple functions by code
+#define DRC_FLAGS_INVALIDATION_DCODE
+
+// type with the same size as a pointer
+#define DRC_PTR_SIZE_IM Bit32u
+
+// calling convention modifier
+#define DRC_CALL_CONV /* nothing */
+#define DRC_FC /* nothing */
+
+// use FC_REGS_ADDR to hold the address of "cpu_regs" and to access it using FC_REGS_ADDR
+//#define DRC_USE_REGS_ADDR
+// use FC_SEGS_ADDR to hold the address of "Segs" and to access it using FC_SEGS_ADDR
+//#define DRC_USE_SEGS_ADDR
+
+// register mapping
+typedef Bit8u HostReg;
+
+#define HOST_v0 2
+#define HOST_v1 3
+#define HOST_a0 4
+#define HOST_a1 5
+#define HOST_t4 12
+#define HOST_t5 13
+#define HOST_t6 14
+#define HOST_t7 15
+#define HOST_s0 16
+#define HOST_t8 24
+#define HOST_t9 25
+#define temp1 HOST_v1
+#define temp2 HOST_t9
+
+// register that holds function return values
+#define FC_RETOP HOST_v0
+
+// register used for address calculations,
+#define FC_ADDR HOST_s0 // has to be saved across calls, see DRC_PROTECT_ADDR_REG
+
+// register that holds the first parameter
+#define FC_OP1 HOST_a0
+
+// register that holds the second parameter
+#define FC_OP2 HOST_a1
+
+// special register that holds the third parameter for _R3 calls (byte accessible)
+#define FC_OP3 HOST_???
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA1 HOST_t5
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA2 HOST_t6
+
+// temporary register for LEA
+#define TEMP_REG_DRC HOST_t7
+
+#ifdef DRC_USE_REGS_ADDR
+// used to hold the address of "cpu_regs" - preferably filled in function gen_run_code
+#define FC_REGS_ADDR HOST_???
+#endif
+
+#ifdef DRC_USE_SEGS_ADDR
+// used to hold the address of "Segs" - preferably filled in function gen_run_code
+#define FC_SEGS_ADDR HOST_???
+#endif
+
+// save some state to improve code gen
+static bool temp1_valid = false;
+static Bit32u temp1_value;
+
+// move a full register from reg_src to reg_dst
+static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) {
+ if(reg_src == reg_dst) return;
+ cache_addw((reg_dst<<11)+0x21); // addu reg_dst, $0, reg_src
+ cache_addw(reg_src);
+}
+
+// move a 32bit constant value into dest_reg
+static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) {
+ if(imm < 65536) {
+ cache_addw((Bit16u)imm); // ori dest_reg, $0, imm
+ cache_addw(0x3400+dest_reg);
+ } else if(((Bit32s)imm < 0) && ((Bit32s)imm >= -32768)) {
+ cache_addw((Bit16u)imm); // addiu dest_reg, $0, imm
+ cache_addw(0x2400+dest_reg);
+ } else if(!(imm & 0xffff)) {
+ cache_addw((Bit16u)(imm >> 16)); // lui dest_reg, %hi(imm)
+ cache_addw(0x3c00+dest_reg);
+ } else {
+ cache_addw((Bit16u)(imm >> 16)); // lui dest_reg, %hi(imm)
+ cache_addw(0x3c00+dest_reg);
+ cache_addw((Bit16u)imm); // ori dest_reg, dest_reg, %lo(imm)
+ cache_addw(0x3400+(dest_reg<<5)+dest_reg);
+ }
+}
+
+// this is the only place temp1 should be modified
+static void INLINE mov_imm_to_temp1(Bit32u imm) {
+ if (temp1_valid && (temp1_value == imm)) return;
+ gen_mov_dword_to_reg_imm(temp1, imm);
+ temp1_valid = true;
+ temp1_value = imm;
+}
+
+static Bit16s gen_addr_temp1(Bit32u addr) {
+ Bit32u hihalf = addr & 0xffff0000;
+ Bit16s lohalf = addr & 0xffff;
+ if (lohalf > 32764) { // [l,s]wl will overflow
+ hihalf = addr;
+ lohalf = 0;
+ } else if(lohalf < 0) hihalf += 0x10000;
+ mov_imm_to_temp1(hihalf);
+ return lohalf;
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) {
+ Bit16s lohalf = gen_addr_temp1((Bit32u)data);
+ // alignment....
+ if (dword) {
+ if ((Bit32u)data & 3) {
+ cache_addw(lohalf+3); // lwl dest_reg, 3(temp1)
+ cache_addw(0x8800+(temp1<<5)+dest_reg);
+ cache_addw(lohalf); // lwr dest_reg, 0(temp1)
+ cache_addw(0x9800+(temp1<<5)+dest_reg);
+ } else {
+ cache_addw(lohalf); // lw dest_reg, 0(temp1)
+ cache_addw(0x8C00+(temp1<<5)+dest_reg);
+ }
+ } else {
+ if ((Bit32u)data & 1) {
+ cache_addw(lohalf); // lbu dest_reg, 0(temp1)
+ cache_addw(0x9000+(temp1<<5)+dest_reg);
+ cache_addw(lohalf+1); // lbu temp2, 1(temp1)
+ cache_addw(0x9000+(temp1<<5)+temp2);
+#if (_MIPS_ISA==MIPS32R2) || defined(PSP)
+ cache_addw(0x7a04); // ins dest_reg, temp2, 8, 8
+ cache_addw(0x7c00+(temp2<<5)+dest_reg);
+#else
+ cache_addw((temp2<<11)+0x200); // sll temp2, temp2, 8
+ cache_addw(temp2);
+ cache_addw((dest_reg<<11)+0x25); // or dest_reg, temp2, dest_reg
+ cache_addw((temp2<<5)+dest_reg);
+#endif
+ } else {
+ cache_addw(lohalf); // lhu dest_reg, 0(temp1);
+ cache_addw(0x9400+(temp1<<5)+dest_reg);
+ }
+ }
+}
+
+// move a 16bit constant value into dest_reg
+// the upper 16bit of the destination register may be destroyed
+static void gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) {
+ cache_addw(imm); // ori dest_reg, $0, imm
+ cache_addw(0x3400+dest_reg);
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into memory
+static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) {
+ Bit16s lohalf = gen_addr_temp1((Bit32u)dest);
+ // alignment....
+ if (dword) {
+ if ((Bit32u)dest & 3) {
+ cache_addw(lohalf+3); // swl src_reg, 3(temp1)
+ cache_addw(0xA800+(temp1<<5)+src_reg);
+ cache_addw(lohalf); // swr src_reg, 0(temp1)
+ cache_addw(0xB800+(temp1<<5)+src_reg);
+ } else {
+ cache_addw(lohalf); // sw src_reg, 0(temp1)
+ cache_addw(0xAC00+(temp1<<5)+src_reg);
+ }
+ } else {
+ if((Bit32u)dest & 1) {
+ cache_addw(lohalf); // sb src_reg, 0(temp1)
+ cache_addw(0xA000+(temp1<<5)+src_reg);
+ cache_addw((temp2<<11)+0x202); // srl temp2, src_reg, 8
+ cache_addw(src_reg);
+ cache_addw(lohalf+1); // sb temp2, 1(temp1)
+ cache_addw(0xA000+(temp1<<5)+temp2);
+ } else {
+ cache_addw(lohalf); // sh src_reg, 0(temp1);
+ cache_addw(0xA400+(temp1<<5)+src_reg);
+ }
+ }
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) {
+ Bit16s lohalf = gen_addr_temp1((Bit32u)data);
+ cache_addw(lohalf); // lbu dest_reg, 0(temp1)
+ cache_addw(0x9000+(temp1<<5)+dest_reg);
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) {
+ gen_mov_byte_to_reg_low(dest_reg, data);
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) {
+ gen_mov_word_to_reg_imm(dest_reg, imm);
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) {
+ gen_mov_byte_to_reg_low_imm(dest_reg, imm);
+}
+
+// move the lowest 8bit of a register into memory
+static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) {
+ Bit16s lohalf = gen_addr_temp1((Bit32u)dest);
+ cache_addw(lohalf); // sb src_reg, 0(temp1)
+ cache_addw(0xA000+(temp1<<5)+src_reg);
+}
+
+
+
+// convert an 8bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_byte(bool sign,HostReg reg) {
+ if (sign) {
+#if (_MIPS_ISA==MIPS32R2) || defined(PSP)
+ cache_addw((reg<<11)+0x420); // seb reg, reg
+ cache_addw(0x7c00+reg);
+#else
+ arch that lacks seb
+#endif
+ } else {
+ cache_addw(0xff); // andi reg, reg, 0xff
+ cache_addw(0x3000+(reg<<5)+reg);
+ }
+}
+
+// convert a 16bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_word(bool sign,HostReg reg) {
+ if (sign) {
+#if (_MIPS_ISA==MIPS32R2) || defined(PSP)
+ cache_addw((reg<<11)+0x620); // seh reg, reg
+ cache_addw(0x7c00+reg);
+#else
+ arch that lacks seh
+#endif
+ } else {
+ cache_addw(0xffff); // andi reg, reg, 0xffff
+ cache_addw(0x3000+(reg<<5)+reg);
+ }
+}
+
+// add a 32bit value from memory to a full register
+static void gen_add(HostReg reg,void* op) {
+ gen_mov_word_to_reg(temp2, op, 1);
+ cache_addw((reg<<11)+0x21); // addu reg, reg, temp2
+ cache_addw((reg<<5)+temp2);
+}
+
+// add a 32bit constant value to a full register
+static void gen_add_imm(HostReg reg,Bit32u imm) {
+ if(!imm) return;
+ if(((Bit32s)imm >= -32768) && ((Bit32s)imm < 32768)) {
+ cache_addw((Bit16u)imm); // addiu reg, reg, imm
+ cache_addw(0x2400+(reg<<5)+reg);
+ } else {
+ mov_imm_to_temp1(imm);
+ cache_addw((reg<<11)+0x21); // addu reg, reg, temp1
+ cache_addw((reg<<5)+temp1);
+ }
+}
+
+// and a 32bit constant value with a full register
+static void gen_and_imm(HostReg reg,Bit32u imm) {
+ if(imm < 65536) {
+ cache_addw((Bit16u)imm); // andi reg, reg, imm
+ cache_addw(0x3000+(reg<<5)+reg);
+ } else {
+ mov_imm_to_temp1((Bit32u)imm);
+ cache_addw((reg<<11)+0x24); // and reg, temp1, reg
+ cache_addw((temp1<<5)+reg);
+ }
+}
+
+
+// move a 32bit constant value into memory
+static void INLINE gen_mov_direct_dword(void* dest,Bit32u imm) {
+ gen_mov_dword_to_reg_imm(temp2, imm);
+ gen_mov_word_from_reg(temp2, dest, 1);
+}
+
+// move an address into memory
+static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) {
+ gen_mov_direct_dword(dest,(Bit32u)imm);
+}
+
+// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value
+static void INLINE gen_add_direct_word(void* dest,Bit32u imm,bool dword) {
+ if(!imm) return;
+ gen_mov_word_to_reg(temp2, dest, dword);
+ gen_add_imm(temp2, imm);
+ gen_mov_word_from_reg(temp2, dest, dword);
+}
+
+// add an 8bit constant value to a dword memory value
+static void INLINE gen_add_direct_byte(void* dest,Bit8s imm) {
+ gen_add_direct_word(dest, (Bit32s)imm, 1);
+}
+
+// subtract an 8bit constant value from a dword memory value
+static void INLINE gen_sub_direct_byte(void* dest,Bit8s imm) {
+ gen_add_direct_word(dest, -((Bit32s)imm), 1);
+}
+
+// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value
+static void INLINE gen_sub_direct_word(void* dest,Bit32u imm,bool dword) {
+ gen_add_direct_word(dest, -(Bit32s)imm, dword);
+}
+
+// effective address calculation, destination is dest_reg
+// scale_reg is scaled by scale (scale_reg*(2^scale)) and
+// added to dest_reg, then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) {
+ if (scale) {
+ cache_addw((scale_reg<<11)+(scale<<6)); // sll scale_reg, scale_reg, scale
+ cache_addw(scale_reg);
+ }
+ cache_addw((dest_reg<<11)+0x21); // addu dest_reg, dest_reg, scale_reg
+ cache_addw((dest_reg<<5)+scale_reg);
+ gen_add_imm(dest_reg, imm);
+}
+
+// effective address calculation, destination is dest_reg
+// dest_reg is scaled by scale (dest_reg*(2^scale)),
+// then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) {
+ if (scale) {
+ cache_addw((dest_reg<<11)+(scale<<6)); // sll dest_reg, dest_reg, scale
+ cache_addw(dest_reg);
+ }
+ gen_add_imm(dest_reg, imm);
+}
+
+#define DELAY cache_addd(0) // nop
+
+// generate a call to a parameterless function
+static void INLINE gen_call_function_raw(void * func) {
+#if C_DEBUG
+ if ((cache.pos ^ func) & 0xf0000000) LOG_MSG("jump overflow\n");
+#endif
+ temp1_valid = false;
+ cache_addd(0x0c000000+(((Bit32u)func>>2)&0x3ffffff)); // jal func
+ DELAY;
+}
+
+// generate a call to a function with paramcount parameters
+// note: the parameters are loaded in the architecture specific way
+// using the gen_load_param_ functions below
+static Bit32u INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) {
+ Bit32u proc_addr = (Bit32u)cache.pos;
+ gen_call_function_raw(func);
+ return proc_addr;
+}
+
+#ifdef __mips_eabi
+// max of 8 parameters in $a0-$a3 and $t0-$t3
+
+// load an immediate value as param'th function parameter
+static void INLINE gen_load_param_imm(Bitu imm,Bitu param) {
+ gen_mov_dword_to_reg_imm(param+4, imm);
+}
+
+// load an address as param'th function parameter
+static void INLINE gen_load_param_addr(Bitu addr,Bitu param) {
+ gen_mov_dword_to_reg_imm(param+4, addr);
+}
+
+// load a host-register as param'th function parameter
+static void INLINE gen_load_param_reg(Bitu reg,Bitu param) {
+ gen_mov_regs(param+4, reg);
+}
+
+// load a value from memory as param'th function parameter
+static void INLINE gen_load_param_mem(Bitu mem,Bitu param) {
+ gen_mov_word_to_reg(param+4, (void *)mem, 1);
+}
+#else
+ other mips abis
+#endif
+
+// jump to an address pointed at by ptr, offset is in imm
+static void INLINE gen_jmp_ptr(void * ptr,Bits imm=0) {
+ gen_mov_word_to_reg(temp2, ptr, 1);
+ if((imm < -32768) || (imm >= 32768)) {
+ gen_add_imm(temp2, imm);
+ imm = 0;
+ }
+ temp1_valid = false;
+ cache_addw((Bit16u)imm); // lw temp2, imm(temp2)
+ cache_addw(0x8C00+(temp2<<5)+temp2);
+ cache_addd((temp2<<21)+8); // jr temp2
+ DELAY;
+}
+
+// short conditional jump (+-127 bytes) if register is zero
+// the destination is set by gen_fill_branch() later
+static Bit32u INLINE gen_create_branch_on_zero(HostReg reg,bool dword) {
+ temp1_valid = false;
+ if(!dword) {
+ cache_addw(0xffff); // andi temp1, reg, 0xffff
+ cache_addw(0x3000+(reg<<5)+temp1);
+ }
+ cache_addw(0); // beq $0, reg, 0
+ cache_addw(0x1000+(dword?reg:temp1));
+ DELAY;
+ return ((Bit32u)cache.pos-8);
+}
+
+// short conditional jump (+-127 bytes) if register is nonzero
+// the destination is set by gen_fill_branch() later
+static Bit32u INLINE gen_create_branch_on_nonzero(HostReg reg,bool dword) {
+ temp1_valid = false;
+ if(!dword) {
+ cache_addw(0xffff); // andi temp1, reg, 0xffff
+ cache_addw(0x3000+(reg<<5)+temp1);
+ }
+ cache_addw(0); // bne $0, reg, 0
+ cache_addw(0x1400+(dword?reg:temp1));
+ DELAY;
+ return ((Bit32u)cache.pos-8);
+}
+
+// calculate relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch(DRC_PTR_SIZE_IM data) {
+#if C_DEBUG
+ Bits len=(Bit32u)cache.pos-data;
+ if (len<0) len=-len;
+ if (len>126) LOG_MSG("Big jump %d",len);
+#endif
+ temp1_valid = false; // this is a branch target
+ *(Bit16u*)data=((Bit16u)((Bit32u)cache.pos-data-4)>>2);
+}
+
+#if 0 // assume for the moment no branch will go farther then +/- 128KB
+
+// conditional jump if register is nonzero
+// for isdword==true the 32bit of the register are tested
+// for isdword==false the lowest 8bit of the register are tested
+static Bit32u gen_create_branch_long_nonzero(HostReg reg,bool isdword) {
+ temp1_valid = false;
+ if (!isdword) {
+ cache_addw(0xff); // andi temp1, reg, 0xff
+ cache_addw(0x3000+(reg<<5)+temp1);
+ }
+ cache_addw(3); // beq $0, reg, +12
+ cache_addw(0x1000+(isdword?reg:temp1));
+ DELAY;
+ cache_addd(0x00000000); // fill j
+ DELAY;
+ return ((Bit32u)cache.pos-8);
+}
+
+// compare 32bit-register against zero and jump if value less/equal than zero
+static Bit32u INLINE gen_create_branch_long_leqzero(HostReg reg) {
+ temp1_valid = false;
+ cache_addw(3); // bgtz reg, +12
+ cache_addw(0x1c00+(reg<<5));
+ DELAY;
+ cache_addd(0x00000000); // fill j
+ DELAY;
+ return ((Bit32u)cache.pos-8);
+}
+
+// calculate long relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch_long(Bit32u data) {
+ temp1_valid = false;
+ // this is an absolute branch
+ *(Bit32u*)data=0x08000000+(((Bit32u)cache.pos>>2)&0x3ffffff);
+}
+#else
+// conditional jump if register is nonzero
+// for isdword==true the 32bit of the register are tested
+// for isdword==false the lowest 8bit of the register are tested
+static Bit32u gen_create_branch_long_nonzero(HostReg reg,bool isdword) {
+ temp1_valid = false;
+ if (!isdword) {
+ cache_addw(0xff); // andi temp1, reg, 0xff
+ cache_addw(0x3000+(reg<<5)+temp1);
+ }
+ cache_addw(0); // bne $0, reg, 0
+ cache_addw(0x1400+(isdword?reg:temp1));
+ DELAY;
+ return ((Bit32u)cache.pos-8);
+}
+
+// compare 32bit-register against zero and jump if value less/equal than zero
+static Bit32u INLINE gen_create_branch_long_leqzero(HostReg reg) {
+ temp1_valid = false;
+ cache_addw(0); // blez reg, 0
+ cache_addw(0x1800+(reg<<5));
+ DELAY;
+ return ((Bit32u)cache.pos-8);
+}
+
+// calculate long relative offset and fill it into the location pointed to by data
+static void INLINE gen_fill_branch_long(Bit32u data) {
+ gen_fill_branch(data);
+}
+#endif
+
+static void gen_run_code(void) {
+ temp1_valid = false;
+ cache_addd(0x27bdfff0); // addiu $sp, $sp, -16
+ cache_addd(0xafb00004); // sw $s0, 4($sp)
+ cache_addd(0x00800008); // jr $a0
+ cache_addd(0xafbf0000); // sw $ra, 0($sp)
+}
+
+// return from a function
+static void gen_return_function(void) {
+ temp1_valid = false;
+ cache_addd(0x8fbf0000); // lw $ra, 0($sp)
+ cache_addd(0x8fb00004); // lw $s0, 4($sp)
+ cache_addd(0x03e00008); // jr $ra
+ cache_addd(0x27bd0010); // addiu $sp, $sp, 16
+}
+
+#ifdef DRC_FLAGS_INVALIDATION
+// called when a call to a function can be replaced by a
+// call to a simpler function
+static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) {
+#ifdef DRC_FLAGS_INVALIDATION_DCODE
+ // try to avoid function calls but rather directly fill in code
+ switch (flags_type) {
+ case t_ADDb:
+ case t_ADDw:
+ case t_ADDd:
+ *(Bit32u*)pos=0x00851021; // addu $v0, $a0, $a1
+ break;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ *(Bit32u*)pos=0x00851025; // or $v0, $a0, $a1
+ break;
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ *(Bit32u*)pos=0x00851024; // and $v0, $a0, $a1
+ break;
+ case t_SUBb:
+ case t_SUBw:
+ case t_SUBd:
+ *(Bit32u*)pos=0x00851023; // subu $v0, $a0, $a1
+ break;
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ *(Bit32u*)pos=0x00851026; // xor $v0, $a0, $a1
+ break;
+ case t_CMPb:
+ case t_CMPw:
+ case t_CMPd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ *(Bit32u*)pos=0; // nop
+ break;
+ case t_INCb:
+ case t_INCw:
+ case t_INCd:
+ *(Bit32u*)pos=0x24820001; // addiu $v0, $a0, 1
+ break;
+ case t_DECb:
+ case t_DECw:
+ case t_DECd:
+ *(Bit32u*)pos=0x2482ffff; // addiu $v0, $a0, -1
+ break;
+ case t_SHLb:
+ case t_SHLw:
+ case t_SHLd:
+ *(Bit32u*)pos=0x00a41004; // sllv $v0, $a0, $a1
+ break;
+ case t_SHRb:
+ case t_SHRw:
+ case t_SHRd:
+ *(Bit32u*)pos=0x00a41006; // srlv $v0, $a0, $a1
+ break;
+ case t_SARd:
+ *(Bit32u*)pos=0x00a41007; // srav $v0, $a0, $a1
+ break;
+#if (_MIPS_ISA==MIPS32R2) || defined(PSP)
+ case t_RORd:
+ *(Bit32u*)pos=0x00a41046; // rotr $v0, $a0, $a1
+ break;
+#endif
+ case t_NEGb:
+ case t_NEGw:
+ case t_NEGd:
+ *(Bit32u*)pos=0x00041023; // subu $v0, $0, $a0
+ break;
+ default:
+ *(Bit32u*)pos=0x0c000000+((((Bit32u)fct_ptr)>>2)&0x3ffffff); // jal simple_func
+ break;
+ }
+#else
+ *(Bit32u*)pos=0x0c000000+(((Bit32u)fct_ptr)>>2)&0x3ffffff); // jal simple_func
+#endif
+}
+#endif
+
+static void cache_block_closing(Bit8u* block_start,Bitu block_size) {
+#ifdef PSP
+// writeback dcache and invalidate icache
+ Bit32u inval_start = ((Bit32u)block_start) & ~63;
+ Bit32u inval_end = (((Bit32u)block_start) + block_size + 64) & ~63;
+ for (;inval_start < inval_end; inval_start+=64) {
+ __builtin_allegrex_cache(0x1a, inval_start);
+ __builtin_allegrex_cache(0x08, inval_start);
+ }
+#endif
+}
+
+static void cache_block_before_close(void) { }
+
+
+#ifdef DRC_USE_SEGS_ADDR
+
+// mov 16bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_seg16_to_reg(HostReg dest_reg,Bitu index) {
+// stub
+}
+
+// mov 32bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_seg32_to_reg(HostReg dest_reg,Bitu index) {
+// stub
+}
+
+// add a 32bit value from Segs[index] to a full register using FC_SEGS_ADDR (index modulo 4 must be zero)
+static void gen_add_seg32_to_reg(HostReg reg,Bitu index) {
+// stub
+}
+
+#endif
+
+#ifdef DRC_USE_REGS_ADDR
+
+// mov 16bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_regval16_to_reg(HostReg dest_reg,Bitu index) {
+// stub
+}
+
+// mov 32bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_regval32_to_reg(HostReg dest_reg,Bitu index) {
+// stub
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_regword_to_reg(HostReg dest_reg,Bitu index,bool dword) {
+// stub
+}
+
+// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_regbyte_to_reg_low(HostReg dest_reg,Bitu index) {
+// stub
+}
+
+// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void INLINE gen_mov_regbyte_to_reg_low_canuseword(HostReg dest_reg,Bitu index) {
+// stub
+}
+
+
+// add a 32bit value from cpu_regs[index] to a full register using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_add_regval32_to_reg(HostReg reg,Bitu index) {
+// stub
+}
+
+
+// move 16bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 2 must be zero)
+static void gen_mov_regval16_from_reg(HostReg src_reg,Bitu index) {
+// stub
+}
+
+// move 32bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 4 must be zero)
+static void gen_mov_regval32_from_reg(HostReg src_reg,Bitu index) {
+// stub
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into cpu_regs[index] using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero)
+static void gen_mov_regword_from_reg(HostReg src_reg,Bitu index,bool dword) {
+// stub
+}
+
+// move the lowest 8bit of a register into cpu_regs[index] using FC_REGS_ADDR
+static void gen_mov_regbyte_from_reg_low(HostReg src_reg,Bitu index) {
+// stub
+}
+
+#endif
diff --git a/src/cpu/core_dynrec/risc_x64.h b/src/cpu/core_dynrec/risc_x64.h
new file mode 100644
index 000000000..f00e56f9c
--- /dev/null
+++ b/src/cpu/core_dynrec/risc_x64.h
@@ -0,0 +1,707 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+// some configuring defines that specify the capabilities of this architecture
+// or aspects of the recompiling
+
+// protect FC_ADDR over function calls if necessaray
+// #define DRC_PROTECT_ADDR_REG
+
+// try to use non-flags generating functions if possible
+#define DRC_FLAGS_INVALIDATION
+// try to replace _simple functions by code
+#define DRC_FLAGS_INVALIDATION_DCODE
+
+// type with the same size as a pointer
+#define DRC_PTR_SIZE_IM Bit64u
+
+// calling convention modifier
+#define DRC_CALL_CONV /* nothing */
+#define DRC_FC /* nothing */
+
+
+// register mapping
+typedef Bit8u HostReg;
+
+#define HOST_EAX 0
+#define HOST_ECX 1
+#define HOST_EDX 2
+#define HOST_EBX 3
+#define HOST_ESI 6
+#define HOST_EDI 7
+
+
+// register that holds function return values
+#define FC_RETOP HOST_EAX
+
+// register used for address calculations, if the ABI does not
+// state that this register is preserved across function calls
+// then define DRC_PROTECT_ADDR_REG above
+#define FC_ADDR HOST_EBX
+
+#if defined (_WIN64)
+#define FC_OP1 HOST_ECX
+#define FC_OP2 HOST_EDX
+#else
+// register that holds the first parameter
+#define FC_OP1 HOST_EDI
+
+// register that holds the second parameter
+#define FC_OP2 HOST_ESI
+#endif
+
+// special register that holds the third parameter for _R3 calls (byte accessible)
+#define FC_OP3 HOST_EAX
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA1 HOST_ECX
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA2 HOST_EDX
+
+
+// temporary register for LEA
+#define TEMP_REG_DRC HOST_ESI
+
+
+// move a full register from reg_src to reg_dst
+static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) {
+ if (reg_dst==reg_src) return;
+ cache_addb(0x8b); // mov reg_dst,reg_src
+ cache_addb(0xc0+(reg_dst<<3)+reg_src);
+}
+
+static void gen_mov_reg_qword(HostReg dest_reg,Bit64u imm);
+
+// This function generates an instruction with register addressing and a memory location
+static INLINE void gen_reg_memaddr(HostReg reg,void* data,Bit8u op,Bit8u prefix=0) {
+ Bit64s diff = (Bit64s)data-((Bit64s)cache.pos+(prefix?7:6));
+// if ((diff<0x80000000LL) && (diff>-0x80000000LL)) { //clang messes itself up on this...
+ if ( (diff>>63) == (diff>>31) ) { //signed bit extend, test to see if value fits in a Bit32s
+ // mov reg,[rip+diff] (or similar, depending on the op) to fetch *data
+ if(prefix) cache_addb(prefix);
+ cache_addb(op);
+ cache_addb(0x05+(reg<<3));
+ // RIP-relative addressing is offset after the instruction
+ cache_addd((Bit32u)(((Bit64u)diff)&0xffffffffLL));
+ } else if ((Bit64u)data<0x100000000LL) {
+ // mov reg,[data] (or similar, depending on the op) when absolute address of data is <4GB
+ if(prefix) cache_addb(prefix);
+ cache_addb(op);
+ cache_addw(0x2504+(reg<<3));
+ cache_addd((Bit32u)(((Bit64u)data)&0xffffffffLL));
+ } else {
+ // load 64-bit data into tmp_reg and do mov reg,[tmp_reg] (or similar, depending on the op)
+ HostReg tmp_reg = HOST_EAX;
+ if(reg == HOST_EAX) tmp_reg = HOST_ECX;
+
+ cache_addb(0x50+tmp_reg); // push rax/rcx
+ gen_mov_reg_qword(tmp_reg,(Bit64u)data);
+
+ if(prefix) cache_addb(prefix);
+ cache_addb(op);
+ cache_addb(tmp_reg+(reg<<3));
+
+ cache_addb(0x58+tmp_reg); // pop rax/rcx
+ }
+}
+
+// Same as above, but with immediate addressing and a memory location
+static INLINE void gen_memaddr(Bitu modreg,void* data,Bitu off,Bitu imm,Bit8u op,Bit8u prefix=0) {
+ Bit64s diff = (Bit64s)data-((Bit64s)cache.pos+off+(prefix?7:6));
+// if ((diff<0x80000000LL) && (diff>-0x80000000LL)) {
+ if ( (diff>>63) == (diff>>31) ) {
+ // RIP-relative addressing is offset after the instruction
+ if(prefix) cache_addb(prefix);
+ cache_addw(op+((modreg+1)<<8));
+ cache_addd((Bit32u)(((Bit64u)diff)&0xffffffffLL));
+
+ switch(off) {
+ case 1: cache_addb(((Bit8u)imm&0xff)); break;
+ case 2: cache_addw(((Bit16u)imm&0xffff)); break;
+ case 4: cache_addd(((Bit32u)imm&0xffffffff)); break;
+ }
+
+ } else if ((Bit64u)data<0x100000000LL) {
+ if(prefix) cache_addb(prefix);
+ cache_addw(op+(modreg<<8));
+ cache_addb(0x25);
+ cache_addd((Bit32u)(((Bit64u)data)&0xffffffffLL));
+
+ switch(off) {
+ case 1: cache_addb(((Bit8u)imm&0xff)); break;
+ case 2: cache_addw(((Bit16u)imm&0xffff)); break;
+ case 4: cache_addd(((Bit32u)imm&0xffffffff)); break;
+ }
+
+ } else {
+ HostReg tmp_reg = HOST_EAX;
+
+ cache_addb(0x50+tmp_reg); // push rax
+ gen_mov_reg_qword(tmp_reg,(Bit64u)data);
+
+ if(prefix) cache_addb(prefix);
+ cache_addw(op+((modreg-4+tmp_reg)<<8));
+
+ switch(off) {
+ case 1: cache_addb(((Bit8u)imm&0xff)); break;
+ case 2: cache_addw(((Bit16u)imm&0xffff)); break;
+ case 4: cache_addd(((Bit32u)imm&0xffffffff)); break;
+ }
+
+ cache_addb(0x58+tmp_reg); // pop rax
+ }
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword,Bit8u prefix=0) {
+ if (!dword) gen_reg_memaddr(dest_reg,data,0xb7,0x0f); // movzx reg,[data] - zero extend data, fixes LLVM compile where the called function does not extend the parameters
+ else gen_reg_memaddr(dest_reg,data,0x8b,prefix); // mov reg,[data]
+}
+
+// move a 16bit constant value into dest_reg
+// the upper 16bit of the destination register may be destroyed
+static void gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) {
+ cache_addb(0xb8+dest_reg); // mov reg,imm
+ cache_addd((Bit32u)imm);
+}
+
+// move a 32bit constant value into dest_reg
+static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) {
+ cache_addb(0xb8+dest_reg); // mov reg,imm
+ cache_addd(imm);
+}
+
+// move a 64bit constant value into a full register
+static void gen_mov_reg_qword(HostReg dest_reg,Bit64u imm) {
+ if (imm==(Bit32u)imm) {
+ gen_mov_dword_to_reg_imm(dest_reg, (Bit32u)imm);
+ return;
+ }
+ cache_addb(0x48);
+ cache_addb(0xb8+dest_reg); // mov dest_reg,imm
+ cache_addq(imm);
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into memory
+static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword,Bit8u prefix=0) {
+ gen_reg_memaddr(src_reg,dest,0x89,(dword?prefix:0x66)); // mov [data],reg
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) {
+ gen_reg_memaddr(dest_reg,data,0xb6,0x0f); // movzx reg,[data]
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) {
+ gen_reg_memaddr(dest_reg,data,0xb6,0x0f); // movzx reg,[data]
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) {
+ cache_addb(0xb8+dest_reg); // mov reg,imm
+ cache_addd((Bit32u)imm);
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) {
+ cache_addb(0xb8+dest_reg); // mov reg,imm
+ cache_addd((Bit32u)imm);
+}
+
+// move the lowest 8bit of a register into memory
+static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) {
+ gen_reg_memaddr(src_reg,dest,0x88); // mov byte [data],reg
+}
+
+
+
+// convert an 8bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_byte(bool sign,HostReg reg) {
+ cache_addw(0xb60f+(sign?0x800:0)); // movsx/movzx
+ cache_addb(0xc0+(reg<<3)+reg);
+}
+
+// convert a 16bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_word(bool sign,HostReg reg) {
+ cache_addw(0xb70f+(sign?0x800:0)); // movsx/movzx
+ cache_addb(0xc0+(reg<<3)+reg);
+}
+
+
+
+// add a 32bit value from memory to a full register
+static void gen_add(HostReg reg,void* op) {
+ gen_reg_memaddr(reg,op,0x03); // add reg,[data]
+}
+
+// add a 32bit constant value to a full register
+static void gen_add_imm(HostReg reg,Bit32u imm) {
+ if (!imm) return;
+ cache_addw(0xc081+(reg<<8)); // add reg,imm
+ cache_addd(imm);
+}
+
+// and a 32bit constant value with a full register
+static void gen_and_imm(HostReg reg,Bit32u imm) {
+ cache_addw(0xe081+(reg<<8)); // and reg,imm
+ cache_addd(imm);
+}
+
+
+
+// move a 32bit constant value into memory
+static void gen_mov_direct_dword(void* dest,Bit32u imm) {
+ gen_memaddr(0x4,dest,4,imm,0xc7); // mov [data],imm
+}
+
+
+// move an address into memory
+static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) {
+ gen_mov_reg_qword(HOST_EAX,imm);
+ gen_mov_word_from_reg(HOST_EAX,dest,true,0x48); // 0x48 prefixes full 64-bit mov
+}
+
+
+// add an 8bit constant value to a memory value
+static void gen_add_direct_byte(void* dest,Bit8s imm) {
+ if (!imm) return;
+ gen_memaddr(0x4,dest,1,imm,0x83); // add [data],imm
+}
+
+// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value
+static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) {
+ if (!imm) return;
+ if ((imm<128) && dword) {
+ gen_add_direct_byte(dest,(Bit8s)imm);
+ return;
+ }
+ gen_memaddr(0x4,dest,(dword?4:2),imm,0x81,(dword?0:0x66)); // add [data],imm
+}
+
+// subtract an 8bit constant value from a memory value
+static void gen_sub_direct_byte(void* dest,Bit8s imm) {
+ if (!imm) return;
+ gen_memaddr(0x2c,dest,1,imm,0x83);
+}
+
+// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value
+static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) {
+ if (!imm) return;
+ if ((imm<128) && dword) {
+ gen_sub_direct_byte(dest,(Bit8s)imm);
+ return;
+ }
+ gen_memaddr(0x2c,dest,(dword?4:2),imm,0x81,(dword?0:0x66)); // sub [data],imm
+}
+
+
+
+// effective address calculation, destination is dest_reg
+// scale_reg is scaled by scale (scale_reg*(2^scale)) and
+// added to dest_reg, then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) {
+ Bit8u rm_base;
+ Bitu imm_size;
+ if (!imm) {
+ imm_size=0; rm_base=0x0; //no imm
+ } else if ((imm>=-128 && imm<=127)) {
+ imm_size=1; rm_base=0x40; //Signed byte imm
+ } else {
+ imm_size=4; rm_base=0x80; //Signed dword imm
+ }
+
+ // ea_reg := ea_reg+scale_reg*(2^scale)+imm
+ cache_addb(0x48);
+ cache_addb(0x8d); //LEA
+ cache_addb(0x04+(dest_reg << 3)+rm_base); //The sib indicator
+ cache_addb(dest_reg+(scale_reg<<3)+(scale<<6));
+
+ switch (imm_size) {
+ case 0: break;
+ case 1:cache_addb(imm);break;
+ case 4:cache_addd(imm);break;
+ }
+}
+
+// effective address calculation, destination is dest_reg
+// dest_reg is scaled by scale (dest_reg*(2^scale)),
+// then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) {
+ // ea_reg := ea_reg*(2^scale)+imm
+ // ea_reg := op2 *(2^scale)+imm
+ cache_addb(0x48);
+ cache_addb(0x8d); //LEA
+ cache_addb(0x04+(dest_reg<<3));
+ cache_addb(0x05+(dest_reg<<3)+(scale<<6));
+
+ cache_addd(imm); // always add dword immediate
+}
+
+
+
+// generate a call to a parameterless function
+static void INLINE gen_call_function_raw(void * func) {
+ cache_addw(0xb848);
+ cache_addq((Bit64u)func);
+ cache_addw(0xd0ff);
+}
+
+// generate a call to a function with paramcount parameters
+// note: the parameters are loaded in the architecture specific way
+// using the gen_load_param_ functions below
+static Bit64u INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) {
+ Bit64u proc_addr = (Bit64u)cache.pos;
+ gen_call_function_raw(func);
+ return proc_addr;
+}
+
+
+// load an immediate value as param'th function parameter
+static void INLINE gen_load_param_imm(Bitu imm,Bitu param) {
+ // move an immediate 32bit value into a 64bit param reg
+ switch (param) {
+ case 0: // mov param1,imm32
+ gen_mov_dword_to_reg_imm(FC_OP1,(Bit32u)imm);
+ break;
+ case 1: // mov param2,imm32
+ gen_mov_dword_to_reg_imm(FC_OP2,(Bit32u)imm);
+ break;
+#if defined (_WIN64)
+ case 2: // mov r8d,imm32
+ cache_addw(0xb841);
+ cache_addd((Bit32u)imm);
+ break;
+ case 3: // mov r9d,imm32
+ cache_addw(0xb941);
+ cache_addd((Bit32u)imm);
+ break;
+#else
+ case 2: // mov rdx,imm32
+ gen_mov_dword_to_reg_imm(HOST_EDX,(Bit32u)imm);
+ break;
+ case 3: // mov rcx,imm32
+ gen_mov_dword_to_reg_imm(HOST_ECX,(Bit32u)imm);
+ break;
+#endif
+ default:
+ E_Exit("I(mm) >4 params unsupported");
+ break;
+ }
+}
+
+// load an address as param'th function parameter
+static void INLINE gen_load_param_addr(DRC_PTR_SIZE_IM addr,Bitu param) {
+ // move an immediate 64bit value into a 64bit param reg
+ switch (param) {
+ case 0: // mov param1,addr64
+ gen_mov_reg_qword(FC_OP1,addr);
+ break;
+ case 1: // mov param2,addr64
+ gen_mov_reg_qword(FC_OP2,addr);
+ break;
+#if defined (_WIN64)
+ case 2: // mov r8,addr64
+ cache_addw(0xb849);
+ cache_addq(addr);
+ break;
+ case 3: // mov r9,addr64
+ cache_addw(0xb949);
+ cache_addq(addr);
+ break;
+#else
+ case 2: // mov rdx,addr64
+ gen_mov_reg_qword(HOST_EDX,addr);
+ break;
+ case 3: // mov rcx,addr64
+ gen_mov_reg_qword(HOST_ECX,addr);
+ break;
+#endif
+ default:
+ E_Exit("A(ddr) >4 params unsupported");
+ break;
+ }
+}
+
+// load a host-register as param'th function parameter
+static void INLINE gen_load_param_reg(Bitu reg,Bitu param) {
+ // move a register into a 64bit param reg, {inputregs}!={outputregs}
+ switch (param) {
+ case 0: // mov param1,reg&7
+ gen_mov_regs(FC_OP1,reg&7);
+ break;
+ case 1: // mov param2,reg&7
+ gen_mov_regs(FC_OP2,reg&7);
+ break;
+#if defined (_WIN64)
+ case 2: // mov r8,reg&7
+ cache_addw(0x8949);
+ cache_addb(0xc0 + ((reg & 7) << 3));
+ break;
+ case 3: // mov r9,reg&7
+ cache_addw(0x8949);
+ cache_addb(0xc1 + ((reg & 7) << 3));
+ break;
+#else
+ case 2: // mov rdx,reg&7
+ gen_mov_regs(HOST_EDX,reg&7);
+ break;
+ case 3: // mov rcx,reg&7
+ gen_mov_regs(HOST_ECX,reg&7);
+ break;
+#endif
+ default:
+ E_Exit("R(eg) >4 params unsupported");
+ break;
+ }
+}
+
+// load a value from memory as param'th function parameter
+static void INLINE gen_load_param_mem(Bitu mem,Bitu param) {
+ // move memory content into a 64bit param reg
+ switch (param) {
+ case 0: // mov param1,[mem]
+ gen_mov_word_to_reg(FC_OP1,(void*)mem,true);
+ break;
+ case 1: // mov param2,[mem]
+ gen_mov_word_to_reg(FC_OP2,(void*)mem,true);
+ break;
+#if defined (_WIN64)
+ case 2: // mov r8d,[mem]
+ gen_mov_word_to_reg(0,(void*)mem,true,0x44); // 0x44, use x64 rXd regs
+ break;
+ case 3: // mov r9d,[mem]
+ gen_mov_word_to_reg(1,(void*)mem,true,0x44); // 0x44, use x64 rXd regs
+ break;
+#else
+ case 2: // mov edx,[mem]
+ gen_mov_word_to_reg(HOST_EDX,(void*)mem,true);
+ break;
+ case 3: // mov ecx,[mem]
+ gen_mov_word_to_reg(HOST_ECX,(void*)mem,true);
+ break;
+#endif
+ default:
+ E_Exit("R(eg) >4 params unsupported");
+ break;
+ }
+}
+
+
+
+// jump to an address pointed at by ptr, offset is in imm
+static void gen_jmp_ptr(void * ptr,Bits imm=0) {
+ cache_addw(0xa148); // mov rax,[data]
+ cache_addq((Bit64u)ptr);
+
+ cache_addb(0xff); // jmp [rax+imm]
+ if (!imm) {
+ cache_addb(0x20);
+ } else if ((imm>=-128 && imm<=127)) {
+ cache_addb(0x60);
+ cache_addb(imm);
+ } else {
+ cache_addb(0xa0);
+ cache_addd(imm);
+ }
+}
+
+
+// short conditional jump (+-127 bytes) if register is zero
+// the destination is set by gen_fill_branch() later
+static Bit64u gen_create_branch_on_zero(HostReg reg,bool dword) {
+ if (!dword) cache_addb(0x66);
+ cache_addb(0x0b); // or reg,reg
+ cache_addb(0xc0+reg+(reg<<3));
+
+ cache_addw(0x0074); // jz addr
+ return ((Bit64u)cache.pos-1);
+}
+
+// short conditional jump (+-127 bytes) if register is nonzero
+// the destination is set by gen_fill_branch() later
+static Bit64u gen_create_branch_on_nonzero(HostReg reg,bool dword) {
+ if (!dword) cache_addb(0x66);
+ cache_addb(0x0b); // or reg,reg
+ cache_addb(0xc0+reg+(reg<<3));
+
+ cache_addw(0x0075); // jnz addr
+ return ((Bit64u)cache.pos-1);
+}
+
+// calculate relative offset and fill it into the location pointed to by data
+static void gen_fill_branch(DRC_PTR_SIZE_IM data) {
+#if C_DEBUG
+ Bit64s len=(Bit64u)cache.pos-data;
+ if (len<0) len=-len;
+ if (len>126) LOG_MSG("Big jump %d",len);
+#endif
+ *(Bit8u*)data=(Bit8u)((Bit64u)cache.pos-data-1);
+}
+
+// conditional jump if register is nonzero
+// for isdword==true the 32bit of the register are tested
+// for isdword==false the lowest 8bit of the register are tested
+static Bit64u gen_create_branch_long_nonzero(HostReg reg,bool isdword) {
+ // isdword: cmp reg32,0
+ // not isdword: cmp reg8,0
+ cache_addb(0x0a+(isdword?1:0)); // or reg,reg
+ cache_addb(0xc0+reg+(reg<<3));
+
+ cache_addw(0x850f); // jnz
+ cache_addd(0);
+ return ((Bit64u)cache.pos-4);
+}
+
+// compare 32bit-register against zero and jump if value less/equal than zero
+static Bit64u gen_create_branch_long_leqzero(HostReg reg) {
+ cache_addw(0xf883+(reg<<8));
+ cache_addb(0x00); // cmp reg,0
+
+ cache_addw(0x8e0f); // jle
+ cache_addd(0);
+ return ((Bit64u)cache.pos-4);
+}
+
+// calculate long relative offset and fill it into the location pointed to by data
+static void gen_fill_branch_long(Bit64u data) {
+ *(Bit32u*)data=(Bit32u)((Bit64u)cache.pos-data-4);
+}
+
+static void gen_run_code(void) {
+ cache_addw(0x5355); // push rbp,rbx
+ cache_addb(0x56); // push rsi
+ cache_addd(0x20EC8348); // sub rsp, 32
+ cache_addb(0x48);cache_addw(0x2D8D);cache_addd(2); // lea rbp, [rip+2]
+ cache_addw(0xE0FF+(FC_OP1<<8)); // jmp FC_OP1
+ cache_addd(0x20C48348); // add rsp, 32
+ cache_addd(0xC35D5B5E); // pop rsi,rbx,rbp;ret
+}
+
+// return from a function
+static void gen_return_function(void) {
+ cache_addw(0xE5FF); // jmp rbp
+}
+
+#ifdef DRC_FLAGS_INVALIDATION
+// called when a call to a function can be replaced by a
+// call to a simpler function
+// check gen_call_function_raw and gen_call_function_setup
+// for the targeted code
+static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) {
+#ifdef DRC_FLAGS_INVALIDATION_DCODE
+ // try to avoid function calls but rather directly fill in code
+ switch (flags_type) {
+ case t_ADDb:
+ case t_ADDw:
+ case t_ADDd:
+ // mov eax,FC_OP1; add eax,FC_OP2
+ *(Bit32u*)(pos+0)=0xc001c089+(FC_OP1<<11)+(FC_OP2<<27);
+ *(Bit32u*)(pos+4)=0x909006eb; // skip
+ *(Bit32u*)(pos+8)=0x90909090;
+ return;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ // mov eax,FC_OP1; or eax,FC_OP2
+ *(Bit32u*)(pos+0)=0xc009c089+(FC_OP1<<11)+(FC_OP2<<27);
+ *(Bit32u*)(pos+4)=0x909006eb; // skip
+ *(Bit32u*)(pos+8)=0x90909090;
+ return;
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ // mov eax,FC_OP1; and eax,FC_OP2
+ *(Bit32u*)(pos+0)=0xc021c089+(FC_OP1<<11)+(FC_OP2<<27);
+ *(Bit32u*)(pos+4)=0x909006eb; // skip
+ *(Bit32u*)(pos+8)=0x90909090;
+ return;
+ case t_SUBb:
+ case t_SUBw:
+ case t_SUBd:
+ // mov eax,FC_OP1; sub eax,FC_OP2
+ *(Bit32u*)(pos+0)=0xc029c089+(FC_OP1<<11)+(FC_OP2<<27);
+ *(Bit32u*)(pos+4)=0x909006eb; // skip
+ *(Bit32u*)(pos+8)=0x90909090;
+ return;
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ // mov eax,FC_OP1; xor eax,FC_OP2
+ *(Bit32u*)(pos+0)=0xc031c089+(FC_OP1<<11)+(FC_OP2<<27);
+ *(Bit32u*)(pos+4)=0x909006eb; // skip
+ *(Bit32u*)(pos+8)=0x90909090;
+ return;
+ case t_CMPb:
+ case t_CMPw:
+ case t_CMPd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ *(Bit32u*)(pos+0)=0x90900aeb; // skip
+ *(Bit32u*)(pos+4)=0x90909090;
+ *(Bit32u*)(pos+8)=0x90909090;
+ return;
+ case t_INCb:
+ case t_INCw:
+ case t_INCd:
+ *(Bit32u*)(pos+0)=0xc0ffc089+(FC_OP1<<11); // mov eax,ecx; inc eax
+ *(Bit32u*)(pos+4)=0x909006eb; // skip
+ *(Bit32u*)(pos+8)=0x90909090;
+ return;
+ case t_DECb:
+ case t_DECw:
+ case t_DECd:
+ *(Bit32u*)(pos+0)=0xc8ffc089+(FC_OP1<<11); // mov eax, FC_OP1; dec eax
+ *(Bit32u*)(pos+4)=0x909006eb; // skip
+ *(Bit32u*)(pos+8)=0x90909090;
+ return;
+ case t_NEGb:
+ case t_NEGw:
+ case t_NEGd:
+ *(Bit32u*)(pos+0)=0xd8f7c089+(FC_OP1<<11); // mov eax, FC_OP1; neg eax
+ *(Bit32u*)(pos+4)=0x909006eb; // skip
+ *(Bit32u*)(pos+8)=0x90909090;
+ return;
+ }
+#endif
+ *(Bit64u*)(pos+2)=(Bit64u)fct_ptr; // fill function pointer
+}
+#endif
+
+static void cache_block_closing(Bit8u* block_start,Bitu block_size) { }
+
+static void cache_block_before_close(void) { }
diff --git a/src/cpu/core_dynrec/risc_x86.h b/src/cpu/core_dynrec/risc_x86.h
new file mode 100644
index 000000000..82b0013fd
--- /dev/null
+++ b/src/cpu/core_dynrec/risc_x86.h
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+// some configuring defines that specify the capabilities of this architecture
+// or aspects of the recompiling
+
+// protect FC_ADDR over function calls if necessaray
+// #define DRC_PROTECT_ADDR_REG
+
+// try to use non-flags generating functions if possible
+#define DRC_FLAGS_INVALIDATION
+// try to replace _simple functions by code
+#define DRC_FLAGS_INVALIDATION_DCODE
+
+// type with the same size as a pointer
+#define DRC_PTR_SIZE_IM Bit32u
+
+// calling convention modifier
+#if defined (WIN32)
+#define DRC_CALL_CONV _fastcall
+#define DRC_FC /* nothing */
+#else
+#define DRC_CALL_CONV /* nothing */
+#define DRC_FC GCC_ATTRIBUTE(fastcall)
+#endif
+
+
+// register mapping
+enum HostReg {
+ HOST_EAX=0,
+ HOST_ECX,
+ HOST_EDX,
+ HOST_EBX,
+ HOST_ESP,
+ HOST_EBP,
+ HOST_ESI,
+ HOST_EDI
+};
+
+
+// register that holds function return values
+#define FC_RETOP HOST_EAX
+
+// register used for address calculations, if the ABI does not
+// state that this register is preserved across function calls
+// then define DRC_PROTECT_ADDR_REG above
+#define FC_ADDR HOST_EBX
+
+// register that holds the first parameter
+#define FC_OP1 HOST_ECX
+
+// register that holds the second parameter
+#define FC_OP2 HOST_EDX
+
+// special register that holds the third parameter for _R3 calls (byte accessible)
+#define FC_OP3 HOST_EAX
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA1 HOST_ECX
+
+// register that holds byte-accessible temporary values
+#define FC_TMP_BA2 HOST_EDX
+
+
+// temporary register for LEA
+#define TEMP_REG_DRC HOST_ESI
+
+
+// move a full register from reg_src to reg_dst
+static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) {
+ cache_addb(0x8b); // mov reg_dst,reg_src
+ cache_addb(0xc0+(reg_dst<<3)+reg_src);
+}
+
+// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg
+// 16bit moves may destroy the upper 16bit of the destination register
+static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) {
+ if (!dword) cache_addb(0x66);
+ cache_addw(0x058b+(dest_reg<<11)); // mov reg,[data]
+ cache_addd((Bit32u)data);
+}
+
+// move a 16bit constant value into dest_reg
+// the upper 16bit of the destination register may be destroyed
+static void gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) {
+ cache_addb(0x66);
+ cache_addb(0xb8+dest_reg); // mov reg,imm
+ cache_addw(imm);
+}
+
+// move a 32bit constant value into dest_reg
+static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) {
+ cache_addb(0xb8+dest_reg); // mov reg,imm
+ cache_addd(imm);
+}
+
+// move 32bit (dword==true) or 16bit (dword==false) of a register into memory
+static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) {
+ if (!dword) cache_addb(0x66);
+ cache_addw(0x0589+(src_reg<<11)); // mov [data],reg
+ cache_addd((Bit32u)dest);
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) {
+ cache_addw(0x058a+(dest_reg<<11)); // mov reg,[data]
+ cache_addd((Bit32u)data);
+}
+
+// move an 8bit value from memory into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) {
+ cache_addb(0x66);
+ cache_addw(0x058b+(dest_reg<<11)); // mov reg,[data]
+ cache_addd((Bit32u)data);
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function does not use FC_OP1/FC_OP2 as dest_reg as these
+// registers might not be directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) {
+ cache_addb(0xb0+dest_reg); // mov reg,imm
+ cache_addb(imm);
+}
+
+// move an 8bit constant value into dest_reg
+// the upper 24bit of the destination register can be destroyed
+// this function can use FC_OP1/FC_OP2 as dest_reg which are
+// not directly byte-accessible on some architectures
+static void gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) {
+ cache_addb(0x66);
+ cache_addb(0xb8+dest_reg); // mov reg,imm
+ cache_addw(imm);
+}
+
+// move the lowest 8bit of a register into memory
+static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) {
+ cache_addw(0x0588+(src_reg<<11)); // mov [data],reg
+ cache_addd((Bit32u)dest);
+}
+
+
+
+// convert an 8bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_byte(bool sign,HostReg reg) {
+ cache_addw(0xb60f+(sign?0x800:0)); // movsx/movzx
+ cache_addb(0xc0+(reg<<3)+reg);
+}
+
+// convert a 16bit word to a 32bit dword
+// the register is zero-extended (sign==false) or sign-extended (sign==true)
+static void gen_extend_word(bool sign,HostReg reg) {
+ cache_addw(0xb70f+(sign?0x800:0)); // movsx/movzx
+ cache_addb(0xc0+(reg<<3)+reg);
+}
+
+
+
+// add a 32bit value from memory to a full register
+static void gen_add(HostReg reg,void* op) {
+ cache_addw(0x0503+(reg<<11)); // add reg,[data]
+ cache_addd((Bit32u)op);
+}
+
+// add a 32bit constant value to a full register
+static void gen_add_imm(HostReg reg,Bit32u imm) {
+ cache_addw(0xc081+(reg<<8)); // add reg,imm
+ cache_addd(imm);
+}
+
+// and a 32bit constant value with a full register
+static void gen_and_imm(HostReg reg,Bit32u imm) {
+ cache_addw(0xe081+(reg<<8)); // and reg,imm
+ cache_addd(imm);
+}
+
+
+
+// move a 32bit constant value into memory
+static void gen_mov_direct_dword(void* dest,Bit32u imm) {
+ cache_addw(0x05c7); // mov [data],imm
+ cache_addd((Bit32u)dest);
+ cache_addd(imm);
+}
+
+// move an address into memory
+static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) {
+ gen_mov_direct_dword(dest,(Bit32u)imm);
+}
+
+
+// add an 8bit constant value to a memory value
+static void gen_add_direct_byte(void* dest,Bit8s imm) {
+ cache_addw(0x0583); // add [data],imm
+ cache_addd((Bit32u)dest);
+ cache_addb(imm);
+}
+
+// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value
+static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) {
+ if ((imm<128) && dword) {
+ gen_add_direct_byte(dest,(Bit8s)imm);
+ return;
+ }
+ if (!dword) cache_addb(0x66);
+ cache_addw(0x0581); // add [data],imm
+ cache_addd((Bit32u)dest);
+ if (dword) cache_addd((Bit32u)imm);
+ else cache_addw((Bit16u)imm);
+}
+
+// subtract an 8bit constant value from a memory value
+static void gen_sub_direct_byte(void* dest,Bit8s imm) {
+ cache_addw(0x2d83); // sub [data],imm
+ cache_addd((Bit32u)dest);
+ cache_addb(imm);
+}
+
+// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value
+static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) {
+ if ((imm<128) && dword) {
+ gen_sub_direct_byte(dest,(Bit8s)imm);
+ return;
+ }
+ if (!dword) cache_addb(0x66);
+ cache_addw(0x2d81); // sub [data],imm
+ cache_addd((Bit32u)dest);
+ if (dword) cache_addd((Bit32u)imm);
+ else cache_addw((Bit16u)imm);
+}
+
+
+
+// effective address calculation, destination is dest_reg
+// scale_reg is scaled by scale (scale_reg*(2^scale)) and
+// added to dest_reg, then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) {
+ Bit8u rm_base;
+ Bitu imm_size;
+ if (!imm) {
+ imm_size=0; rm_base=0x0; //no imm
+ } else if ((imm>=-128 && imm<=127)) {
+ imm_size=1; rm_base=0x40; //Signed byte imm
+ } else {
+ imm_size=4; rm_base=0x80; //Signed dword imm
+ }
+
+ // ea_reg := ea_reg+scale_reg*(2^scale)+imm
+ cache_addb(0x8d); //LEA
+ cache_addb(0x04+(dest_reg << 3)+rm_base); //The sib indicator
+ cache_addb(dest_reg+(scale_reg<<3)+(scale<<6));
+
+ switch (imm_size) {
+ case 0: break;
+ case 1:cache_addb(imm);break;
+ case 4:cache_addd(imm);break;
+ }
+}
+
+// effective address calculation, destination is dest_reg
+// dest_reg is scaled by scale (dest_reg*(2^scale)),
+// then the immediate value is added
+static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) {
+ // ea_reg := ea_reg*(2^scale)+imm
+ // ea_reg := op2 *(2^scale)+imm
+ cache_addb(0x8d); //LEA
+ cache_addb(0x04+(dest_reg<<3));
+ cache_addb(0x05+(dest_reg<<3)+(scale<<6));
+
+ cache_addd(imm); // always add dword immediate
+}
+
+
+
+// generate a call to a parameterless function
+static void INLINE gen_call_function_raw(void * func) {
+ cache_addb(0xe8);
+ cache_addd((Bit32u)func - (Bit32u)cache.pos-4);
+}
+
+// generate a call to a function with paramcount parameters
+// note: the parameters are loaded in the architecture specific way
+// using the gen_load_param_ functions below
+static Bit32u INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) {
+ Bit32u proc_addr=(Bit32u)cache.pos;
+ // Do the actual call to the procedure
+ cache_addb(0xe8);
+ cache_addd((Bit32u)func - (Bit32u)cache.pos-4);
+
+ // Restore the params of the stack
+ if (paramcount) {
+ cache_addw(0xc483); //add ESP,imm byte
+ cache_addb((!fastcall)?paramcount*4:0);
+ }
+ return proc_addr;
+}
+
+
+// load an immediate value as param'th function parameter
+static void INLINE gen_load_param_imm(Bitu imm,Bitu param) {
+ cache_addb(0x68); // push immediate
+ cache_addd(imm);
+}
+
+// load an address as param'th function parameter
+static void INLINE gen_load_param_addr(Bitu addr,Bitu param) {
+ cache_addb(0x68); // push immediate (address)
+ cache_addd(addr);
+}
+
+// load a host-register as param'th function parameter
+static void INLINE gen_load_param_reg(Bitu reg,Bitu param) {
+ cache_addb(0x50+(reg&7)); // push reg
+}
+
+// load a value from memory as param'th function parameter
+static void INLINE gen_load_param_mem(Bitu mem,Bitu param) {
+ cache_addw(0x35ff); // push []
+ cache_addd(mem);
+}
+
+
+
+// jump to an address pointed at by ptr, offset is in imm
+static void gen_jmp_ptr(void * ptr,Bits imm=0) {
+ gen_mov_word_to_reg(HOST_EAX,ptr,true);
+ cache_addb(0xff); // jmp [eax+imm]
+ if (!imm) {
+ cache_addb(0x20);
+ } else if ((imm>=-128 && imm<=127)) {
+ cache_addb(0x60);
+ cache_addb(imm);
+ } else {
+ cache_addb(0xa0);
+ cache_addd(imm);
+ }
+}
+
+
+// short conditional jump (+-127 bytes) if register is zero
+// the destination is set by gen_fill_branch() later
+static Bit32u gen_create_branch_on_zero(HostReg reg,bool dword) {
+ if (!dword) cache_addb(0x66);
+ cache_addb(0x0b); // or reg,reg
+ cache_addb(0xc0+reg+(reg<<3));
+
+ cache_addw(0x0074); // jz addr
+ return ((Bit32u)cache.pos-1);
+}
+
+// short conditional jump (+-127 bytes) if register is nonzero
+// the destination is set by gen_fill_branch() later
+static Bit32u gen_create_branch_on_nonzero(HostReg reg,bool dword) {
+ if (!dword) cache_addb(0x66);
+ cache_addb(0x0b); // or reg,reg
+ cache_addb(0xc0+reg+(reg<<3));
+
+ cache_addw(0x0075); // jnz addr
+ return ((Bit32u)cache.pos-1);
+}
+
+// calculate relative offset and fill it into the location pointed to by data
+static void gen_fill_branch(DRC_PTR_SIZE_IM data) {
+#if C_DEBUG
+ Bits len=(Bit32u)cache.pos-data;
+ if (len<0) len=-len;
+ if (len>126) LOG_MSG("Big jump %d",len);
+#endif
+ *(Bit8u*)data=(Bit8u)((Bit32u)cache.pos-data-1);
+}
+
+// conditional jump if register is nonzero
+// for isdword==true the 32bit of the register are tested
+// for isdword==false the lowest 8bit of the register are tested
+static Bit32u gen_create_branch_long_nonzero(HostReg reg,bool isdword) {
+ // isdword: cmp reg32,0
+ // not isdword: cmp reg8,0
+ cache_addb(0x0a+(isdword?1:0)); // or reg,reg
+ cache_addb(0xc0+reg+(reg<<3));
+
+ cache_addw(0x850f); // jnz
+ cache_addd(0);
+ return ((Bit32u)cache.pos-4);
+}
+
+// compare 32bit-register against zero and jump if value less/equal than zero
+static Bit32u gen_create_branch_long_leqzero(HostReg reg) {
+ cache_addw(0xf883+(reg<<8));
+ cache_addb(0x00); // cmp reg,0
+
+ cache_addw(0x8e0f); // jle
+ cache_addd(0);
+ return ((Bit32u)cache.pos-4);
+}
+
+// calculate long relative offset and fill it into the location pointed to by data
+static void gen_fill_branch_long(Bit32u data) {
+ *(Bit32u*)data=((Bit32u)cache.pos-data-4);
+}
+
+
+static void gen_run_code(void) {
+ cache_addd(0x0424448b); // mov eax,[esp+4]
+ cache_addb(0x53); // push ebx
+ cache_addb(0x56); // push esi
+ cache_addw(0xd0ff); // call eax
+ cache_addb(0x5e); // pop esi
+ cache_addb(0x5b); // pop ebx
+}
+
+// return from a function
+static void gen_return_function(void) {
+ cache_addb(0xc3); // ret
+}
+
+#ifdef DRC_FLAGS_INVALIDATION
+// called when a call to a function can be replaced by a
+// call to a simpler function
+static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) {
+#ifdef DRC_FLAGS_INVALIDATION_DCODE
+ // try to avoid function calls but rather directly fill in code
+ switch (flags_type) {
+ case t_ADDb:
+ case t_ADDw:
+ case t_ADDd:
+ *(Bit32u*)pos=0xc203c18b; // mov eax,ecx; add eax,edx
+ *(pos+4)=0x90;
+ break;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ *(Bit32u*)pos=0xc20bc18b; // mov eax,ecx; or eax,edx
+ *(pos+4)=0x90;
+ break;
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ *(Bit32u*)pos=0xc223c18b; // mov eax,ecx; and eax,edx
+ *(pos+4)=0x90;
+ break;
+ case t_SUBb:
+ case t_SUBw:
+ case t_SUBd:
+ *(Bit32u*)pos=0xc22bc18b; // mov eax,ecx; sub eax,edx
+ *(pos+4)=0x90;
+ break;
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ *(Bit32u*)pos=0xc233c18b; // mov eax,ecx; xor eax,edx
+ *(pos+4)=0x90;
+ break;
+ case t_CMPb:
+ case t_CMPw:
+ case t_CMPd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ *(Bit32u*)pos=0x909003eb; // skip
+ *(pos+4)=0x90;
+ break;
+ case t_INCb:
+ case t_INCw:
+ case t_INCd:
+ *(Bit32u*)pos=0x9040c18b; // mov eax,ecx; inc eax
+ *(pos+4)=0x90;
+ break;
+ case t_DECb:
+ case t_DECw:
+ case t_DECd:
+ *(Bit32u*)pos=0x9048c18b; // mov eax,ecx; dec eax
+ *(pos+4)=0x90;
+ break;
+ case t_NEGb:
+ case t_NEGw:
+ case t_NEGd:
+ *(Bit32u*)pos=0xd8f7c18b; // mov eax,ecx; neg eax
+ *(pos+4)=0x90;
+ break;
+ default:
+ *(Bit32u*)(pos+1)=(Bit32u)((Bit8u*)fct_ptr - (pos+1+4)); // fill function pointer
+ break;
+ }
+#else
+ *(Bit32u*)(pos+1)=(Bit32u)((Bit8u*)fct_ptr - (pos+1+4)); // fill function pointer
+#endif
+}
+#endif
+
+static void cache_block_closing(Bit8u* block_start,Bitu block_size) { }
+
+static void cache_block_before_close(void) { }
diff --git a/src/cpu/core_full.cpp b/src/cpu/core_full.cpp
new file mode 100644
index 000000000..20a62d1ba
--- /dev/null
+++ b/src/cpu/core_full.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include "dosbox.h"
+
+#include "pic.h"
+#include "regs.h"
+#include "cpu.h"
+#include "lazyflags.h"
+#include "paging.h"
+#include "fpu.h"
+#include "debug.h"
+#include "inout.h"
+#include "callback.h"
+
+
+typedef PhysPt EAPoint;
+#define SegBase(c) SegPhys(c)
+
+#define LoadMb(off) mem_readb_inline(off)
+#define LoadMw(off) mem_readw_inline(off)
+#define LoadMd(off) mem_readd_inline(off)
+
+#define LoadMbs(off) (Bit8s)(LoadMb(off))
+#define LoadMws(off) (Bit16s)(LoadMw(off))
+#define LoadMds(off) (Bit32s)(LoadMd(off))
+
+#define SaveMb(off,val) mem_writeb_inline(off,val)
+#define SaveMw(off,val) mem_writew_inline(off,val)
+#define SaveMd(off,val) mem_writed_inline(off,val)
+
+#define LoadD(reg) reg
+#define SaveD(reg,val) reg=val
+
+
+
+#include "core_full/loadwrite.h"
+#include "core_full/support.h"
+#include "core_full/optable.h"
+#include "instructions.h"
+
+#define EXCEPTION(blah) \
+ { \
+ Bit8u new_num=blah; \
+ CPU_Exception(new_num,0); \
+ continue; \
+ }
+
+Bits CPU_Core_Full_Run(void) {
+ FullData inst;
+ while (CPU_Cycles-->0) {
+#if C_DEBUG
+ cycle_count++;
+#if C_HEAVY_DEBUG
+ if (DEBUG_HeavyIsBreakpoint()) {
+ FillFlags();
+ return debugCallback;
+ };
+#endif
+#endif
+ LoadIP();
+ inst.entry=cpu.code.big*0x200;
+ inst.prefix=cpu.code.big;
+restartopcode:
+ inst.entry=(inst.entry & 0xffffff00) | Fetchb();
+ inst.code=OpCodeTable[inst.entry];
+ #include "core_full/load.h"
+ #include "core_full/op.h"
+ #include "core_full/save.h"
+nextopcode:;
+ SaveIP();
+ continue;
+illegalopcode:
+ LOG(LOG_CPU,LOG_NORMAL)("Illegal opcode");
+ CPU_Exception(0x6,0);
+ }
+ FillFlags();
+ return CBRET_NONE;
+}
+
+
+void CPU_Core_Full_Init(void) {
+
+}
diff --git a/src/cpu/core_full/Makefile.am b/src/cpu/core_full/Makefile.am
new file mode 100644
index 000000000..41fbe6a2b
--- /dev/null
+++ b/src/cpu/core_full/Makefile.am
@@ -0,0 +1,3 @@
+
+noinst_HEADERS = ea_lookup.h load.h loadwrite.h op.h optable.h save.h \
+ string.h support.h
diff --git a/src/cpu/core_full/ea_lookup.h b/src/cpu/core_full/ea_lookup.h
new file mode 100644
index 000000000..b7c146a1c
--- /dev/null
+++ b/src/cpu/core_full/ea_lookup.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+{
+ EAPoint seg_base;
+ Bit16u off;
+ switch ((inst.rm_mod<<3)|inst.rm_eai) {
+ case 0x00:
+ off=reg_bx+reg_si;
+ seg_base=SegBase(ds);
+ break;
+ case 0x01:
+ off=reg_bx+reg_di;
+ seg_base=SegBase(ds);
+ break;
+ case 0x02:
+ off=reg_bp+reg_si;
+ seg_base=SegBase(ss);
+ break;
+ case 0x03:
+ off=reg_bp+reg_di;
+ seg_base=SegBase(ss);
+ break;
+ case 0x04:
+ off=reg_si;
+ seg_base=SegBase(ds);
+ break;
+ case 0x05:
+ off=reg_di;
+ seg_base=SegBase(ds);
+ break;
+ case 0x06:
+ off=Fetchw();
+ seg_base=SegBase(ds);
+ break;
+ case 0x07:
+ off=reg_bx;
+ seg_base=SegBase(ds);
+ break;
+
+ case 0x08:
+ off=reg_bx+reg_si+Fetchbs();
+ seg_base=SegBase(ds);
+ break;
+ case 0x09:
+ off=reg_bx+reg_di+Fetchbs();
+ seg_base=SegBase(ds);
+ break;
+ case 0x0a:
+ off=reg_bp+reg_si+Fetchbs();
+ seg_base=SegBase(ss);
+ break;
+ case 0x0b:
+ off=reg_bp+reg_di+Fetchbs();
+ seg_base=SegBase(ss);
+ break;
+ case 0x0c:
+ off=reg_si+Fetchbs();
+ seg_base=SegBase(ds);
+ break;
+ case 0x0d:
+ off=reg_di+Fetchbs();
+ seg_base=SegBase(ds);
+ break;
+ case 0x0e:
+ off=reg_bp+Fetchbs();
+ seg_base=SegBase(ss);
+ break;
+ case 0x0f:
+ off=reg_bx+Fetchbs();
+ seg_base=SegBase(ds);
+ break;
+
+ case 0x10:
+ off=reg_bx+reg_si+Fetchws();
+ seg_base=SegBase(ds);
+ break;
+ case 0x11:
+ off=reg_bx+reg_di+Fetchws();
+ seg_base=SegBase(ds);
+ break;
+ case 0x12:
+ off=reg_bp+reg_si+Fetchws();
+ seg_base=SegBase(ss);
+ break;
+ case 0x13:
+ off=reg_bp+reg_di+Fetchws();
+ seg_base=SegBase(ss);
+ break;
+ case 0x14:
+ off=reg_si+Fetchws();
+ seg_base=SegBase(ds);
+ break;
+ case 0x15:
+ off=reg_di+Fetchws();
+ seg_base=SegBase(ds);
+ break;
+ case 0x16:
+ off=reg_bp+Fetchws();
+ seg_base=SegBase(ss);
+ break;
+ case 0x17:
+ off=reg_bx+Fetchws();
+ seg_base=SegBase(ds);
+ break;
+ }
+ inst.rm_off=off;
+ if (inst.prefix & PREFIX_SEG) {
+ inst.rm_eaa=inst.seg.base+off;
+ } else {
+ inst.rm_eaa=seg_base+off;
+ }
+} else {
+
+
+#define SIB(MODE) { \
+ Bitu sib=Fetchb(); \
+ switch (sib&7) { \
+ case 0:seg_base=SegBase(ds);off=reg_eax;break; \
+ case 1:seg_base=SegBase(ds);off=reg_ecx;break; \
+ case 2:seg_base=SegBase(ds);off=reg_edx;break; \
+ case 3:seg_base=SegBase(ds);off=reg_ebx;break; \
+ case 4:seg_base=SegBase(ss);off=reg_esp;break; \
+ case 5:if (!MODE) { seg_base=SegBase(ds);off=Fetchd();break; \
+ } else { seg_base=SegBase(ss);off=reg_ebp;break;} \
+ case 6:seg_base=SegBase(ds);off=reg_esi;break; \
+ case 7:seg_base=SegBase(ds);off=reg_edi;break; \
+ } \
+ off+=*SIBIndex[(sib >> 3) &7] << (sib >> 6); \
+};
+ static Bit32u SIBZero=0;
+ static Bit32u * SIBIndex[8]= { &reg_eax,&reg_ecx,&reg_edx,&reg_ebx,&SIBZero,&reg_ebp,&reg_esi,&reg_edi };
+ EAPoint seg_base;
+ Bit32u off;
+ switch ((inst.rm_mod<<3)|inst.rm_eai) {
+ case 0x00:
+ off=reg_eax;
+ seg_base=SegBase(ds);
+ break;
+ case 0x01:
+ off=reg_ecx;
+ seg_base=SegBase(ds);
+ break;
+ case 0x02:
+ off=reg_edx;
+ seg_base=SegBase(ds);
+ break;
+ case 0x03:
+ off=reg_ebx;
+ seg_base=SegBase(ds);
+ break;
+ case 0x04:
+ SIB(0);
+ break;
+ case 0x05:
+ off=Fetchd();
+ seg_base=SegBase(ds);
+ break;
+ case 0x06:
+ off=reg_esi;
+ seg_base=SegBase(ds);
+ break;
+ case 0x07:
+ off=reg_edi;
+ seg_base=SegBase(ds);
+ break;
+
+ case 0x08:
+ off=reg_eax+Fetchbs();
+ seg_base=SegBase(ds);
+ break;
+ case 0x09:
+ off=reg_ecx+Fetchbs();
+ seg_base=SegBase(ds);
+ break;
+ case 0x0a:
+ off=reg_edx+Fetchbs();
+ seg_base=SegBase(ds);
+ break;
+ case 0x0b:
+ off=reg_ebx+Fetchbs();
+ seg_base=SegBase(ds);
+ break;
+ case 0x0c:
+ SIB(1);
+ off+=Fetchbs();
+ break;
+ case 0x0d:
+ off=reg_ebp+Fetchbs();
+ seg_base=SegBase(ss);
+ break;
+ case 0x0e:
+ off=reg_esi+Fetchbs();
+ seg_base=SegBase(ds);
+ break;
+ case 0x0f:
+ off=reg_edi+Fetchbs();
+ seg_base=SegBase(ds);
+ break;
+
+ case 0x10:
+ off=reg_eax+Fetchds();
+ seg_base=SegBase(ds);
+ break;
+ case 0x11:
+ off=reg_ecx+Fetchds();
+ seg_base=SegBase(ds);
+ break;
+ case 0x12:
+ off=reg_edx+Fetchds();
+ seg_base=SegBase(ds);
+ break;
+ case 0x13:
+ off=reg_ebx+Fetchds();
+ seg_base=SegBase(ds);
+ break;
+ case 0x14:
+ SIB(1);
+ off+=Fetchds();
+ break;
+ case 0x15:
+ off=reg_ebp+Fetchds();
+ seg_base=SegBase(ss);
+ break;
+ case 0x16:
+ off=reg_esi+Fetchds();
+ seg_base=SegBase(ds);
+ break;
+ case 0x17:
+ off=reg_edi+Fetchds();
+ seg_base=SegBase(ds);
+ break;
+ }
+ inst.rm_off=off;
+ if (inst.prefix & PREFIX_SEG) {
+ inst.rm_eaa=inst.seg.base+off;
+ } else {
+ inst.rm_eaa=seg_base+off;
+ }
+}
diff --git a/src/cpu/core_full/load.h b/src/cpu/core_full/load.h
new file mode 100644
index 000000000..d029def4c
--- /dev/null
+++ b/src/cpu/core_full/load.h
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+switch (inst.code.load) {
+/* General loading */
+ case L_POPwRM:
+ inst_op1_w = Pop_16();
+ goto case_L_MODRM;
+ case L_POPdRM:
+ inst_op1_d = Pop_32();
+ goto case_L_MODRM;
+ case L_MODRM_NVM:
+ if ((reg_flags & FLAG_VM) || !cpu.pmode) goto illegalopcode;
+ goto case_L_MODRM;
+case_L_MODRM:
+ case L_MODRM:
+ inst.rm=Fetchb();
+ inst.rm_index=(inst.rm >> 3) & 7;
+ inst.rm_eai=inst.rm&07;
+ inst.rm_mod=inst.rm>>6;
+ /* Decode address of mod/rm if needed */
+ if (inst.rm<0xc0) {
+ if (!(inst.prefix & PREFIX_ADDR))
+ #include "ea_lookup.h"
+ }
+l_MODRMswitch:
+ switch (inst.code.extra) {
+/* Byte */
+ case M_Ib:
+ inst_op1_d=Fetchb();
+ break;
+ case M_Ebx:
+ if (inst.rm<0xc0) inst_op1_ds=(Bit8s)LoadMb(inst.rm_eaa);
+ else inst_op1_ds=(Bit8s)reg_8(inst.rm_eai);
+ break;
+ case M_EbIb:
+ inst_op2_d=Fetchb();
+ case M_Eb:
+ if (inst.rm<0xc0) inst_op1_d=LoadMb(inst.rm_eaa);
+ else inst_op1_d=reg_8(inst.rm_eai);
+ break;
+ case M_EbGb:
+ if (inst.rm<0xc0) inst_op1_d=LoadMb(inst.rm_eaa);
+ else inst_op1_d=reg_8(inst.rm_eai);
+ inst_op2_d=reg_8(inst.rm_index);
+ break;
+ case M_GbEb:
+ if (inst.rm<0xc0) inst_op2_d=LoadMb(inst.rm_eaa);
+ else inst_op2_d=reg_8(inst.rm_eai);
+ case M_Gb:
+ inst_op1_d=reg_8(inst.rm_index);;
+ break;
+/* Word */
+ case M_Iw:
+ inst_op1_d=Fetchw();
+ break;
+ case M_EwxGwx:
+ inst_op2_ds=(Bit16s)reg_16(inst.rm_index);
+ goto l_M_Ewx;
+ case M_EwxIbx:
+ inst_op2_ds=Fetchbs();
+ goto l_M_Ewx;
+ case M_EwxIwx:
+ inst_op2_ds=Fetchws();
+l_M_Ewx:
+ case M_Ewx:
+ if (inst.rm<0xc0) inst_op1_ds=(Bit16s)LoadMw(inst.rm_eaa);
+ else inst_op1_ds=(Bit16s)reg_16(inst.rm_eai);
+ break;
+ case M_EwIb:
+ inst_op2_d=Fetchb();
+ goto l_M_Ew;
+ case M_EwIbx:
+ inst_op2_ds=Fetchbs();
+ goto l_M_Ew;
+ case M_EwIw:
+ inst_op2_d=Fetchw();
+ goto l_M_Ew;
+ case M_EwGwCL:
+ inst_imm_d=reg_cl;
+ goto l_M_EwGw;
+ case M_EwGwIb:
+ inst_imm_d=Fetchb();
+ goto l_M_EwGw;
+ case M_EwGwt:
+ inst_op2_d=reg_16(inst.rm_index);
+ inst.rm_eaa+=((Bit16s)inst_op2_d >> 4) * 2;
+ goto l_M_Ew;
+l_M_EwGw:
+ case M_EwGw:
+ inst_op2_d=reg_16(inst.rm_index);
+l_M_Ew:
+ case M_Ew:
+ if (inst.rm<0xc0) inst_op1_d=LoadMw(inst.rm_eaa);
+ else inst_op1_d=reg_16(inst.rm_eai);
+ break;
+ case M_GwEw:
+ if (inst.rm<0xc0) inst_op2_d=LoadMw(inst.rm_eaa);
+ else inst_op2_d=reg_16(inst.rm_eai);
+ case M_Gw:
+ inst_op1_d=reg_16(inst.rm_index);;
+ break;
+/* DWord */
+ case M_Id:
+ inst_op1_d=Fetchd();
+ break;
+ case M_EdxGdx:
+ inst_op2_ds=(Bit32s)reg_32(inst.rm_index);
+ case M_Edx:
+ if (inst.rm<0xc0) inst_op1_d=(Bit32s)LoadMd(inst.rm_eaa);
+ else inst_op1_d=(Bit32s)reg_32(inst.rm_eai);
+ break;
+ case M_EdIb:
+ inst_op2_d=Fetchb();
+ goto l_M_Ed;
+ case M_EdIbx:
+ inst_op2_ds=Fetchbs();
+ goto l_M_Ed;
+ case M_EdId:
+ inst_op2_d=Fetchd();
+ goto l_M_Ed;
+ case M_EdGdCL:
+ inst_imm_d=reg_cl;
+ goto l_M_EdGd;
+ case M_EdGdt:
+ inst_op2_d=reg_32(inst.rm_index);
+ inst.rm_eaa+=((Bit32s)inst_op2_d >> 5) * 4;
+ goto l_M_Ed;
+ case M_EdGdIb:
+ inst_imm_d=Fetchb();
+ goto l_M_EdGd;
+l_M_EdGd:
+ case M_EdGd:
+ inst_op2_d=reg_32(inst.rm_index);
+l_M_Ed:
+ case M_Ed:
+ if (inst.rm<0xc0) inst_op1_d=LoadMd(inst.rm_eaa);
+ else inst_op1_d=reg_32(inst.rm_eai);
+ break;
+ case M_GdEd:
+ if (inst.rm<0xc0) inst_op2_d=LoadMd(inst.rm_eaa);
+ else inst_op2_d=reg_32(inst.rm_eai);
+ case M_Gd:
+ inst_op1_d=reg_32(inst.rm_index);
+ break;
+/* Others */
+
+ case M_SEG:
+ //TODO Check for limit
+ inst_op1_d=SegValue((SegNames)inst.rm_index);
+ break;
+ case M_Efw:
+ if (inst.rm>=0xc0) goto illegalopcode;
+ inst_op1_d=LoadMw(inst.rm_eaa);
+ inst_op2_d=LoadMw(inst.rm_eaa+2);
+ break;
+ case M_Efd:
+ if (inst.rm>=0xc0) goto illegalopcode;
+ inst_op1_d=LoadMd(inst.rm_eaa);
+ inst_op2_d=LoadMw(inst.rm_eaa+4);
+ break;
+ case M_EA:
+ inst_op1_d=inst.rm_off;
+ break;
+ case M_POPw:
+ inst_op1_d = Pop_16();
+ break;
+ case M_POPd:
+ inst_op1_d = Pop_32();
+ break;
+ case M_GRP:
+ inst.code=Groups[inst.code.op][inst.rm_index];
+ goto l_MODRMswitch;
+ case M_SHIFT_Ib:
+ inst_op2_d=Fetchb() & 0x1f;
+ if (!inst_op2_d)
+ break;
+ inst.code=Groups[inst.code.op][inst.rm_index];
+ goto l_MODRMswitch;
+ case M_SHIFT_CL:
+ inst_op2_d=reg_cl & 0x1f;
+ if (!inst_op2_d)
+ break;
+ inst.code=Groups[inst.code.op][inst.rm_index];
+ goto l_MODRMswitch;
+ case M_SHIFT_1:
+ inst_op2_d=1;
+ inst.code=Groups[inst.code.op][inst.rm_index];
+ goto l_MODRMswitch;
+ case 0:
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("MODRM:Unhandled load %d entry %x",inst.code.extra,inst.entry);
+ break;
+ }
+ break;
+ case L_POPw:
+ inst_op1_d = Pop_16();
+ break;
+ case L_POPd:
+ inst_op1_d = Pop_32();
+ break;
+ case L_POPfw:
+ inst_op1_d = Pop_16();
+ inst_op2_d = Pop_16();
+ break;
+ case L_POPfd:
+ inst_op1_d = Pop_32();
+ inst_op2_d = Pop_16();
+ break;
+ case L_Ib:
+ inst_op1_d=Fetchb();
+ break;
+ case L_Ibx:
+ inst_op1_ds=Fetchbs();
+ break;
+ case L_Iw:
+ inst_op1_d=Fetchw();
+ break;
+ case L_Iwx:
+ inst_op1_ds=Fetchws();
+ break;
+ case L_Idx:
+ case L_Id:
+ inst_op1_d=Fetchd();
+ break;
+ case L_Ifw:
+ inst_op1_d=Fetchw();
+ inst_op2_d=Fetchw();
+ break;
+ case L_Ifd:
+ inst_op1_d=Fetchd();
+ inst_op2_d=Fetchw();
+ break;
+/* Direct load of registers */
+ case L_REGbIb:
+ inst_op2_d=Fetchb();
+ case L_REGb:
+ inst_op1_d=reg_8(inst.code.extra);
+ break;
+ case L_REGwIw:
+ inst_op2_d=Fetchw();
+ case L_REGw:
+ inst_op1_d=reg_16(inst.code.extra);
+ break;
+ case L_REGdId:
+ inst_op2_d=Fetchd();
+ case L_REGd:
+ inst_op1_d=reg_32(inst.code.extra);
+ break;
+ case L_SEG:
+ inst_op1_d=SegValue((SegNames)inst.code.extra);
+ break;
+/* Depending on addressize */
+ case L_OP:
+ if (inst.prefix & PREFIX_ADDR) {
+ inst.rm_eaa=Fetchd();
+ } else {
+ inst.rm_eaa=Fetchw();
+ }
+ if (inst.prefix & PREFIX_SEG) {
+ inst.rm_eaa+=inst.seg.base;
+ } else {
+ inst.rm_eaa+=SegBase(ds);
+ }
+ break;
+ /* Special cases */
+ case L_DOUBLE:
+ inst.entry|=0x100;
+ goto restartopcode;
+ case L_PRESEG:
+ inst.prefix|=PREFIX_SEG;
+ inst.seg.base=SegBase((SegNames)inst.code.extra);
+ goto restartopcode;
+ case L_PREREPNE:
+ inst.prefix|=PREFIX_REP;
+ inst.repz=false;
+ goto restartopcode;
+ case L_PREREP:
+ inst.prefix|=PREFIX_REP;
+ inst.repz=true;
+ goto restartopcode;
+ case L_PREOP:
+ inst.entry=(cpu.code.big ^1) * 0x200;
+ goto restartopcode;
+ case L_PREADD:
+ inst.prefix=(inst.prefix & ~1) | (cpu.code.big ^ 1);
+ goto restartopcode;
+ case L_VAL:
+ inst_op1_d=inst.code.extra;
+ break;
+ case L_INTO:
+ if (!get_OF()) goto nextopcode;
+ inst_op1_d=4;
+ break;
+ case D_IRETw:
+ CPU_IRET(false,GetIP());
+ if (GETFLAG(IF) && PIC_IRQCheck) {
+ return CBRET_NONE;
+ }
+ continue;
+ case D_IRETd:
+ CPU_IRET(true,GetIP());
+ if (GETFLAG(IF) && PIC_IRQCheck)
+ return CBRET_NONE;
+ continue;
+ case D_RETFwIw:
+ {
+ Bitu words=Fetchw();
+ FillFlags();
+ CPU_RET(false,words,GetIP());
+ continue;
+ }
+ case D_RETFw:
+ FillFlags();
+ CPU_RET(false,0,GetIP());
+ continue;
+ case D_RETFdIw:
+ {
+ Bitu words=Fetchw();
+ FillFlags();
+ CPU_RET(true,words,GetIP());
+ continue;
+ }
+ case D_RETFd:
+ FillFlags();
+ CPU_RET(true,0,GetIP());
+ continue;
+/* Direct operations */
+ case L_STRING:
+ #include "string.h"
+ goto nextopcode;
+ case D_PUSHAw:
+ {
+ Bit16u old_sp=reg_sp;
+ Push_16(reg_ax);Push_16(reg_cx);Push_16(reg_dx);Push_16(reg_bx);
+ Push_16(old_sp);Push_16(reg_bp);Push_16(reg_si);Push_16(reg_di);
+ }
+ goto nextopcode;
+ case D_PUSHAd:
+ {
+ Bit32u old_esp=reg_esp;
+ Push_32(reg_eax);Push_32(reg_ecx);Push_32(reg_edx);Push_32(reg_ebx);
+ Push_32(old_esp);Push_32(reg_ebp);Push_32(reg_esi);Push_32(reg_edi);
+ }
+ goto nextopcode;
+ case D_POPAw:
+ reg_di=Pop_16();reg_si=Pop_16();reg_bp=Pop_16();Pop_16();//Don't save SP
+ reg_bx=Pop_16();reg_dx=Pop_16();reg_cx=Pop_16();reg_ax=Pop_16();
+ goto nextopcode;
+ case D_POPAd:
+ reg_edi=Pop_32();reg_esi=Pop_32();reg_ebp=Pop_32();Pop_32();//Don't save ESP
+ reg_ebx=Pop_32();reg_edx=Pop_32();reg_ecx=Pop_32();reg_eax=Pop_32();
+ goto nextopcode;
+ case D_POPSEGw:
+ if (CPU_PopSeg((SegNames)inst.code.extra,false)) RunException();
+ goto nextopcode;
+ case D_POPSEGd:
+ if (CPU_PopSeg((SegNames)inst.code.extra,true)) RunException();
+ goto nextopcode;
+ case D_SETALC:
+ reg_al = get_CF() ? 0xFF : 0;
+ goto nextopcode;
+ case D_XLAT:
+ if (inst.prefix & PREFIX_SEG) {
+ if (inst.prefix & PREFIX_ADDR) {
+ reg_al=LoadMb(inst.seg.base+(Bit32u)(reg_ebx+reg_al));
+ } else {
+ reg_al=LoadMb(inst.seg.base+(Bit16u)(reg_bx+reg_al));
+ }
+ } else {
+ if (inst.prefix & PREFIX_ADDR) {
+ reg_al=LoadMb(SegBase(ds)+(Bit32u)(reg_ebx+reg_al));
+ } else {
+ reg_al=LoadMb(SegBase(ds)+(Bit16u)(reg_bx+reg_al));
+ }
+ }
+ goto nextopcode;
+ case D_CBW:
+ reg_ax=(Bit8s)reg_al;
+ goto nextopcode;
+ case D_CWDE:
+ reg_eax=(Bit16s)reg_ax;
+ goto nextopcode;
+ case D_CWD:
+ if (reg_ax & 0x8000) reg_dx=0xffff;
+ else reg_dx=0;
+ goto nextopcode;
+ case D_CDQ:
+ if (reg_eax & 0x80000000) reg_edx=0xffffffff;
+ else reg_edx=0;
+ goto nextopcode;
+ case D_CLI:
+ if (CPU_CLI()) RunException();
+ goto nextopcode;
+ case D_STI:
+ if (CPU_STI()) RunException();
+ goto nextopcode;
+ case D_STC:
+ FillFlags();SETFLAGBIT(CF,true);
+ goto nextopcode;
+ case D_CLC:
+ FillFlags();SETFLAGBIT(CF,false);
+ goto nextopcode;
+ case D_CMC:
+ FillFlags();
+ SETFLAGBIT(CF,!(reg_flags & FLAG_CF));
+ goto nextopcode;
+ case D_CLD:
+ SETFLAGBIT(DF,false);
+ cpu.direction=1;
+ goto nextopcode;
+ case D_STD:
+ SETFLAGBIT(DF,true);
+ cpu.direction=-1;
+ goto nextopcode;
+ case D_PUSHF:
+ if (CPU_PUSHF(inst.code.extra)) RunException();
+ goto nextopcode;
+ case D_POPF:
+ if (CPU_POPF(inst.code.extra)) RunException();
+ if (GETFLAG(IF) && PIC_IRQCheck) {
+ SaveIP();
+ return CBRET_NONE;
+ }
+ goto nextopcode;
+ case D_SAHF:
+ SETFLAGSb(reg_ah);
+ goto nextopcode;
+ case D_LAHF:
+ FillFlags();
+ reg_ah=reg_flags&0xff;
+ goto nextopcode;
+ case D_WAIT:
+ case D_NOP:
+ goto nextopcode;
+ case D_LOCK: /* FIXME: according to intel, LOCK should raise an exception if it's not followed by one of a small set of instructions;
+ probably doesn't matter for our purposes as it is a pentium prefix anyhow */
+ LOG(LOG_CPU,LOG_NORMAL)("CPU:LOCK");
+ goto nextopcode;
+ case D_ENTERw:
+ {
+ Bitu bytes=Fetchw();
+ Bitu level=Fetchb();
+ CPU_ENTER(false,bytes,level);
+ goto nextopcode;
+ }
+ case D_ENTERd:
+ {
+ Bitu bytes=Fetchw();
+ Bitu level=Fetchb();
+ CPU_ENTER(true,bytes,level);
+ goto nextopcode;
+ }
+ case D_LEAVEw:
+ reg_esp&=cpu.stack.notmask;
+ reg_esp|=(reg_ebp&cpu.stack.mask);
+ reg_bp=Pop_16();
+ goto nextopcode;
+ case D_LEAVEd:
+ reg_esp&=cpu.stack.notmask;
+ reg_esp|=(reg_ebp&cpu.stack.mask);
+ reg_ebp=Pop_32();
+ goto nextopcode;
+ case D_DAA:
+ DAA();
+ goto nextopcode;
+ case D_DAS:
+ DAS();
+ goto nextopcode;
+ case D_AAA:
+ AAA();
+ goto nextopcode;
+ case D_AAS:
+ AAS();
+ goto nextopcode;
+ case D_CPUID:
+ if (!CPU_CPUID()) goto illegalopcode;
+ goto nextopcode;
+ case D_HLT:
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ FillFlags();
+ CPU_HLT(GetIP());
+ return CBRET_NONE;
+ case D_CLTS:
+ if (cpu.pmode && cpu.cpl) goto illegalopcode;
+ cpu.cr0&=(~CR0_TASKSWITCH);
+ goto nextopcode;
+ case D_ICEBP:
+ CPU_SW_Interrupt_NoIOPLCheck(1,GetIP());
+ continue;
+ case D_RDTSC: {
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_PENTIUMSLOW) goto illegalopcode;
+ Bit64s tsc=(Bit64s)(PIC_FullIndex()*(double)(CPU_CycleAutoAdjust?70000:CPU_CycleMax));
+ reg_edx=(Bit32u)(tsc>>32);
+ reg_eax=(Bit32u)(tsc&0xffffffff);
+ break;
+ }
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("LOAD:Unhandled code %d opcode %X",inst.code.load,inst.entry);
+ goto illegalopcode;
+}
+
diff --git a/src/cpu/core_full/loadwrite.h b/src/cpu/core_full/loadwrite.h
new file mode 100644
index 000000000..e432a1f05
--- /dev/null
+++ b/src/cpu/core_full/loadwrite.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#define SaveIP() reg_eip=(Bit32u)(inst.cseip-SegBase(cs));
+#define LoadIP() inst.cseip=SegBase(cs)+reg_eip;
+#define GetIP() (inst.cseip-SegBase(cs))
+
+#define RunException() { \
+ CPU_Exception(cpu.exception.which,cpu.exception.error); \
+ continue; \
+}
+
+static INLINE Bit8u the_Fetchb(EAPoint & loc) {
+ Bit8u temp=LoadMb(loc);
+ loc+=1;
+ return temp;
+}
+
+static INLINE Bit16u the_Fetchw(EAPoint & loc) {
+ Bit16u temp=LoadMw(loc);
+ loc+=2;
+ return temp;
+}
+static INLINE Bit32u the_Fetchd(EAPoint & loc) {
+ Bit32u temp=LoadMd(loc);
+ loc+=4;
+ return temp;
+}
+
+#define Fetchb() the_Fetchb(inst.cseip)
+#define Fetchw() the_Fetchw(inst.cseip)
+#define Fetchd() the_Fetchd(inst.cseip)
+
+#define Fetchbs() (Bit8s)the_Fetchb(inst.cseip)
+#define Fetchws() (Bit16s)the_Fetchw(inst.cseip)
+#define Fetchds() (Bit32s)the_Fetchd(inst.cseip)
+
+#define Push_16 CPU_Push16
+#define Push_32 CPU_Push32
+#define Pop_16 CPU_Pop16
+#define Pop_32 CPU_Pop32
+
diff --git a/src/cpu/core_full/op.h b/src/cpu/core_full/op.h
new file mode 100644
index 000000000..cbbf19a85
--- /dev/null
+++ b/src/cpu/core_full/op.h
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+/* Do the actual opcode */
+switch (inst.code.op) {
+ case t_ADDb: case t_ADDw: case t_ADDd:
+ lf_var1d=inst_op1_d;
+ lf_var2d=inst_op2_d;
+ inst_op1_d=lf_resd=lf_var1d + lf_var2d;
+ lflags.type=inst.code.op;
+ break;
+ case t_CMPb: case t_CMPw: case t_CMPd:
+ case t_SUBb: case t_SUBw: case t_SUBd:
+ lf_var1d=inst_op1_d;
+ lf_var2d=inst_op2_d;
+ inst_op1_d=lf_resd=lf_var1d - lf_var2d;
+ lflags.type=inst.code.op;
+ break;
+ case t_ORb: case t_ORw: case t_ORd:
+ lf_var1d=inst_op1_d;
+ lf_var2d=inst_op2_d;
+ inst_op1_d=lf_resd=lf_var1d | lf_var2d;
+ lflags.type=inst.code.op;
+ break;
+ case t_XORb: case t_XORw: case t_XORd:
+ lf_var1d=inst_op1_d;
+ lf_var2d=inst_op2_d;
+ inst_op1_d=lf_resd=lf_var1d ^ lf_var2d;
+ lflags.type=inst.code.op;
+ break;
+ case t_TESTb: case t_TESTw: case t_TESTd:
+ case t_ANDb: case t_ANDw: case t_ANDd:
+ lf_var1d=inst_op1_d;
+ lf_var2d=inst_op2_d;
+ inst_op1_d=lf_resd=lf_var1d & lf_var2d;
+ lflags.type=inst.code.op;
+ break;
+ case t_ADCb: case t_ADCw: case t_ADCd:
+ lflags.oldcf=(get_CF()!=0);
+ lf_var1d=inst_op1_d;
+ lf_var2d=inst_op2_d;
+ inst_op1_d=lf_resd=lf_var1d + lf_var2d + lflags.oldcf;
+ lflags.type=inst.code.op;
+ break;
+ case t_SBBb: case t_SBBw: case t_SBBd:
+ lflags.oldcf=(get_CF()!=0);
+ lf_var1d=inst_op1_d;
+ lf_var2d=inst_op2_d;
+ inst_op1_d=lf_resd=lf_var1d - lf_var2d - lflags.oldcf;
+ lflags.type=inst.code.op;
+ break;
+ case t_INCb: case t_INCw: case t_INCd:
+ LoadCF;
+ lf_var1d=inst_op1_d;
+ inst_op1_d=lf_resd=inst_op1_d+1;
+ lflags.type=inst.code.op;
+ break;
+ case t_DECb: case t_DECw: case t_DECd:
+ LoadCF;
+ lf_var1d=inst_op1_d;
+ inst_op1_d=lf_resd=inst_op1_d-1;
+ lflags.type=inst.code.op;
+ break;
+/* Using the instructions.h defines */
+ case t_ROLb:
+ ROLB(inst_op1_b,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_ROLw:
+ ROLW(inst_op1_w,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_ROLd:
+ ROLD(inst_op1_d,inst_op2_b,LoadD,SaveD);
+ break;
+
+ case t_RORb:
+ RORB(inst_op1_b,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_RORw:
+ RORW(inst_op1_w,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_RORd:
+ RORD(inst_op1_d,inst_op2_b,LoadD,SaveD);
+ break;
+
+ case t_RCLb:
+ RCLB(inst_op1_b,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_RCLw:
+ RCLW(inst_op1_w,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_RCLd:
+ RCLD(inst_op1_d,inst_op2_b,LoadD,SaveD);
+ break;
+
+ case t_RCRb:
+ RCRB(inst_op1_b,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_RCRw:
+ RCRW(inst_op1_w,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_RCRd:
+ RCRD(inst_op1_d,inst_op2_b,LoadD,SaveD);
+ break;
+
+ case t_SHLb:
+ SHLB(inst_op1_b,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_SHLw:
+ SHLW(inst_op1_w,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_SHLd:
+ SHLD(inst_op1_d,inst_op2_b,LoadD,SaveD);
+ break;
+
+ case t_SHRb:
+ SHRB(inst_op1_b,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_SHRw:
+ SHRW(inst_op1_w,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_SHRd:
+ SHRD(inst_op1_d,inst_op2_b,LoadD,SaveD);
+ break;
+
+ case t_SARb:
+ SARB(inst_op1_b,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_SARw:
+ SARW(inst_op1_w,inst_op2_b,LoadD,SaveD);
+ break;
+ case t_SARd:
+ SARD(inst_op1_d,inst_op2_b,LoadD,SaveD);
+ break;
+
+ case O_DSHLw:
+ {
+ DSHLW(inst_op1_w,inst_op2_w,inst_imm_b,LoadD,SaveD);
+ break;
+ }
+ case O_DSHRw:
+ {
+ DSHRW(inst_op1_w,inst_op2_w,inst_imm_b,LoadD,SaveD);
+ break;
+ }
+ case O_DSHLd:
+ {
+ DSHLD(inst_op1_d,inst_op2_d,inst_imm_b,LoadD,SaveD);
+ break;
+ }
+ case O_DSHRd:
+ {
+ DSHRD(inst_op1_d,inst_op2_d,inst_imm_b,LoadD,SaveD);
+ break;
+ }
+
+ case t_NEGb:
+ lf_var1b=inst_op1_b;
+ inst_op1_b=lf_resb=0-inst_op1_b;
+ lflags.type=t_NEGb;
+ break;
+ case t_NEGw:
+ lf_var1w=inst_op1_w;
+ inst_op1_w=lf_resw=0-inst_op1_w;
+ lflags.type=t_NEGw;
+ break;
+ case t_NEGd:
+ lf_var1d=inst_op1_d;
+ inst_op1_d=lf_resd=0-inst_op1_d;
+ lflags.type=t_NEGd;
+ break;
+
+ case O_NOT:
+ inst_op1_d=~inst_op1_d;
+ break;
+
+ /* Special instructions */
+ case O_IMULRw:
+ DIMULW(inst_op1_ws,inst_op1_ws,inst_op2_ws,LoadD,SaveD);
+ break;
+ case O_IMULRd:
+ DIMULD(inst_op1_ds,inst_op1_ds,inst_op2_ds,LoadD,SaveD);
+ break;
+ case O_MULb:
+ MULB(inst_op1_b,LoadD,0);
+ goto nextopcode;
+ case O_MULw:
+ MULW(inst_op1_w,LoadD,0);
+ goto nextopcode;
+ case O_MULd:
+ MULD(inst_op1_d,LoadD,0);
+ goto nextopcode;
+ case O_IMULb:
+ IMULB(inst_op1_b,LoadD,0);
+ goto nextopcode;
+ case O_IMULw:
+ IMULW(inst_op1_w,LoadD,0);
+ goto nextopcode;
+ case O_IMULd:
+ IMULD(inst_op1_d,LoadD,0);
+ goto nextopcode;
+ case O_DIVb:
+ DIVB(inst_op1_b,LoadD,0);
+ goto nextopcode;
+ case O_DIVw:
+ DIVW(inst_op1_w,LoadD,0);
+ goto nextopcode;
+ case O_DIVd:
+ DIVD(inst_op1_d,LoadD,0);
+ goto nextopcode;
+ case O_IDIVb:
+ IDIVB(inst_op1_b,LoadD,0);
+ goto nextopcode;
+ case O_IDIVw:
+ IDIVW(inst_op1_w,LoadD,0);
+ goto nextopcode;
+ case O_IDIVd:
+ IDIVD(inst_op1_d,LoadD,0);
+ goto nextopcode;
+ case O_AAM:
+ AAM(inst_op1_b);
+ goto nextopcode;
+ case O_AAD:
+ AAD(inst_op1_b);
+ goto nextopcode;
+
+ case O_C_O: inst.cond=TFLG_O; break;
+ case O_C_NO: inst.cond=TFLG_NO; break;
+ case O_C_B: inst.cond=TFLG_B; break;
+ case O_C_NB: inst.cond=TFLG_NB; break;
+ case O_C_Z: inst.cond=TFLG_Z; break;
+ case O_C_NZ: inst.cond=TFLG_NZ; break;
+ case O_C_BE: inst.cond=TFLG_BE; break;
+ case O_C_NBE: inst.cond=TFLG_NBE; break;
+ case O_C_S: inst.cond=TFLG_S; break;
+ case O_C_NS: inst.cond=TFLG_NS; break;
+ case O_C_P: inst.cond=TFLG_P; break;
+ case O_C_NP: inst.cond=TFLG_NP; break;
+ case O_C_L: inst.cond=TFLG_L; break;
+ case O_C_NL: inst.cond=TFLG_NL; break;
+ case O_C_LE: inst.cond=TFLG_LE; break;
+ case O_C_NLE: inst.cond=TFLG_NLE; break;
+
+ case O_ALOP:
+ reg_al=LoadMb(inst.rm_eaa);
+ goto nextopcode;
+ case O_AXOP:
+ reg_ax=LoadMw(inst.rm_eaa);
+ goto nextopcode;
+ case O_EAXOP:
+ reg_eax=LoadMd(inst.rm_eaa);
+ goto nextopcode;
+ case O_OPAL:
+ SaveMb(inst.rm_eaa,reg_al);
+ goto nextopcode;
+ case O_OPAX:
+ SaveMw(inst.rm_eaa,reg_ax);
+ goto nextopcode;
+ case O_OPEAX:
+ SaveMd(inst.rm_eaa,reg_eax);
+ goto nextopcode;
+ case O_SEGDS:
+ inst.code.extra=ds;
+ break;
+ case O_SEGES:
+ inst.code.extra=es;
+ break;
+ case O_SEGFS:
+ inst.code.extra=fs;
+ break;
+ case O_SEGGS:
+ inst.code.extra=gs;
+ break;
+ case O_SEGSS:
+ inst.code.extra=ss;
+ break;
+
+ case O_LOOP:
+ if (inst.prefix & PREFIX_ADDR) {
+ if (--reg_ecx) break;
+ } else {
+ if (--reg_cx) break;
+ }
+ goto nextopcode;
+ case O_LOOPZ:
+ if (inst.prefix & PREFIX_ADDR) {
+ if (--reg_ecx && get_ZF()) break;
+ } else {
+ if (--reg_cx && get_ZF()) break;
+ }
+ goto nextopcode;
+ case O_LOOPNZ:
+ if (inst.prefix & PREFIX_ADDR) {
+ if (--reg_ecx && !get_ZF()) break;
+ } else {
+ if (--reg_cx && !get_ZF()) break;
+ }
+ goto nextopcode;
+ case O_JCXZ:
+ if (inst.prefix & PREFIX_ADDR) {
+ if (reg_ecx) goto nextopcode;
+ } else {
+ if (reg_cx) goto nextopcode;
+ }
+ break;
+ case O_XCHG_AX:
+ {
+ Bit16u temp=reg_ax;
+ reg_ax=inst_op1_w;
+ inst_op1_w=temp;
+ break;
+ }
+ case O_XCHG_EAX:
+ {
+ Bit32u temp=reg_eax;
+ reg_eax=inst_op1_d;
+ inst_op1_d=temp;
+ break;
+ }
+ case O_CALLNw:
+ SaveIP();
+ Push_16(reg_ip);
+ break;
+ case O_CALLNd:
+ SaveIP();
+ Push_32(reg_eip);
+ break;
+ case O_CALLFw:
+ FillFlags();
+ CPU_CALL(false,inst_op2_d,inst_op1_d,GetIP());
+ continue;
+ case O_CALLFd:
+ FillFlags();
+ CPU_CALL(true,inst_op2_d,inst_op1_d,GetIP());
+ continue;
+ case O_JMPFw:
+ FillFlags();
+ CPU_JMP(false,inst_op2_d,inst_op1_d,GetIP());
+ continue;
+ case O_JMPFd:
+ FillFlags();
+ CPU_JMP(true,inst_op2_d,inst_op1_d,GetIP());
+ continue;
+ case O_INT:
+#if C_DEBUG
+ FillFlags();
+ if (((inst.entry & 0xFF)==0xcc) && DEBUG_Breakpoint())
+ return debugCallback;
+ else if (DEBUG_IntBreakpoint(inst_op1_b))
+ return debugCallback;
+#endif
+ CPU_SW_Interrupt(inst_op1_b,GetIP());
+ continue;
+ case O_INb:
+ if (CPU_IO_Exception(inst_op1_d,1)) RunException();
+ reg_al=IO_ReadB(inst_op1_d);
+ goto nextopcode;
+ case O_INw:
+ if (CPU_IO_Exception(inst_op1_d,2)) RunException();
+ reg_ax=IO_ReadW(inst_op1_d);
+ goto nextopcode;
+ case O_INd:
+ if (CPU_IO_Exception(inst_op1_d,4)) RunException();
+ reg_eax=IO_ReadD(inst_op1_d);
+ goto nextopcode;
+ case O_OUTb:
+ if (CPU_IO_Exception(inst_op1_d,1)) RunException();
+ IO_WriteB(inst_op1_d,reg_al);
+ goto nextopcode;
+ case O_OUTw:
+ if (CPU_IO_Exception(inst_op1_d,2)) RunException();
+ IO_WriteW(inst_op1_d,reg_ax);
+ goto nextopcode;
+ case O_OUTd:
+ if (CPU_IO_Exception(inst_op1_d,4)) RunException();
+ IO_WriteD(inst_op1_d,reg_eax);
+ goto nextopcode;
+ case O_CBACK:
+ FillFlags();SaveIP();
+ return inst_op1_d;
+ case O_GRP6w:
+ case O_GRP6d:
+ if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegalopcode;
+ switch (inst.rm_index) {
+ case 0x00: /* SLDT */
+ inst_op1_d=(Bit32u)CPU_SLDT();
+ break;
+ case 0x01: /* STR */
+ inst_op1_d=(Bit32u)CPU_STR();
+ break;
+ case 0x02: /* LLDT */
+ if (cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ if (CPU_LLDT(inst_op1_d)) RunException();
+ goto nextopcode; /* Else value will saved */
+ case 0x03: /* LTR */
+ if (cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ if (CPU_LTR(inst_op1_d)) RunException();
+ goto nextopcode; /* Else value will saved */
+ case 0x04: /* VERR */
+ CPU_VERR(inst_op1_d);
+ goto nextopcode; /* Else value will saved */
+ case 0x05: /* VERW */
+ CPU_VERW(inst_op1_d);
+ goto nextopcode; /* Else value will saved */
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Group 6 Illegal subfunction %X",inst.rm_index);
+ goto illegalopcode;
+ }
+ break;
+ case O_GRP7w:
+ case O_GRP7d:
+ switch (inst.rm_index) {
+ case 0: /* SGDT */
+ SaveMw(inst.rm_eaa,CPU_SGDT_limit());
+ SaveMd(inst.rm_eaa+2,CPU_SGDT_base());
+ goto nextopcode;
+ case 1: /* SIDT */
+ SaveMw(inst.rm_eaa,CPU_SIDT_limit());
+ SaveMd(inst.rm_eaa+2,CPU_SIDT_base());
+ goto nextopcode;
+ case 2: /* LGDT */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ CPU_LGDT(LoadMw(inst.rm_eaa),LoadMd(inst.rm_eaa+2)&((inst.code.op == O_GRP7w) ? 0xFFFFFF : 0xFFFFFFFF));
+ goto nextopcode;
+ case 3: /* LIDT */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ CPU_LIDT(LoadMw(inst.rm_eaa),LoadMd(inst.rm_eaa+2)&((inst.code.op == O_GRP7w) ? 0xFFFFFF : 0xFFFFFFFF));
+ goto nextopcode;
+ case 4: /* SMSW */
+ inst_op1_d=CPU_SMSW();
+ break;
+ case 6: /* LMSW */
+ FillFlags();
+ if (CPU_LMSW(inst_op1_w)) RunException();
+ goto nextopcode;
+ case 7: /* INVLPG */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ FillFlags();
+ PAGING_ClearTLB();
+ goto nextopcode;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Group 7 Illegal subfunction %X",inst.rm_index);
+ goto illegalopcode;
+ }
+ break;
+ case O_M_CRx_Rd:
+ if (CPU_WRITE_CRX(inst.rm_index,inst_op1_d)) RunException();
+ break;
+ case O_M_Rd_CRx:
+ if (CPU_READ_CRX(inst.rm_index,inst_op1_d)) RunException();
+ break;
+ case O_M_DRx_Rd:
+ if (CPU_WRITE_DRX(inst.rm_index,inst_op1_d)) RunException();
+ break;
+ case O_M_Rd_DRx:
+ if (CPU_READ_DRX(inst.rm_index,inst_op1_d)) RunException();
+ break;
+ case O_M_TRx_Rd:
+ if (CPU_WRITE_TRX(inst.rm_index,inst_op1_d)) RunException();
+ break;
+ case O_M_Rd_TRx:
+ if (CPU_READ_TRX(inst.rm_index,inst_op1_d)) RunException();
+ break;
+ case O_LAR:
+ {
+ Bitu ar=inst_op2_d;
+ CPU_LAR(inst_op1_w,ar);
+ inst_op1_d=(Bit32u)ar;
+ }
+ break;
+ case O_LSL:
+ {
+ Bitu limit=inst_op2_d;
+ CPU_LSL(inst_op1_w,limit);
+ inst_op1_d=(Bit32u)limit;
+ }
+ break;
+ case O_ARPL:
+ {
+ Bitu new_sel=inst_op1_d;
+ CPU_ARPL(new_sel,inst_op2_d);
+ inst_op1_d=(Bit32u)new_sel;
+ }
+ break;
+ case O_BSFw:
+ {
+ FillFlags();
+ if (!inst_op1_w) {
+ SETFLAGBIT(ZF,true);
+ goto nextopcode;
+ } else {
+ Bitu count=0;
+ while (1) {
+ if (inst_op1_w & 0x1) break;
+ count++;inst_op1_w>>=1;
+ }
+ inst_op1_d=count;
+ SETFLAGBIT(ZF,false);
+ }
+ }
+ break;
+ case O_BSFd:
+ {
+ FillFlags();
+ if (!inst_op1_d) {
+ SETFLAGBIT(ZF,true);
+ goto nextopcode;
+ } else {
+ Bitu count=0;
+ while (1) {
+ if (inst_op1_d & 0x1) break;
+ count++;inst_op1_d>>=1;
+ }
+ inst_op1_d=count;
+ SETFLAGBIT(ZF,false);
+ }
+ }
+ break;
+ case O_BSRw:
+ {
+ FillFlags();
+ if (!inst_op1_w) {
+ SETFLAGBIT(ZF,true);
+ goto nextopcode;
+ } else {
+ Bitu count=15;
+ while (1) {
+ if (inst_op1_w & 0x8000) break;
+ count--;inst_op1_w<<=1;
+ }
+ inst_op1_d=count;
+ SETFLAGBIT(ZF,false);
+ }
+ }
+ break;
+ case O_BSRd:
+ {
+ FillFlags();
+ if (!inst_op1_d) {
+ SETFLAGBIT(ZF,true);
+ goto nextopcode;
+ } else {
+ Bitu count=31;
+ while (1) {
+ if (inst_op1_d & 0x80000000) break;
+ count--;inst_op1_d<<=1;
+ }
+ inst_op1_d=count;
+ SETFLAGBIT(ZF,false);
+ }
+ }
+ break;
+ case O_BTw:
+ FillFlags();
+ SETFLAGBIT(CF,(inst_op1_d & (1 << (inst_op2_d & 15))));
+ break;
+ case O_BTSw:
+ FillFlags();
+ SETFLAGBIT(CF,(inst_op1_d & (1 << (inst_op2_d & 15))));
+ inst_op1_d|=(1 << (inst_op2_d & 15));
+ break;
+ case O_BTCw:
+ FillFlags();
+ SETFLAGBIT(CF,(inst_op1_d & (1 << (inst_op2_d & 15))));
+ inst_op1_d^=(1 << (inst_op2_d & 15));
+ break;
+ case O_BTRw:
+ FillFlags();
+ SETFLAGBIT(CF,(inst_op1_d & (1 << (inst_op2_d & 15))));
+ inst_op1_d&=~(1 << (inst_op2_d & 15));
+ break;
+ case O_BTd:
+ FillFlags();
+ SETFLAGBIT(CF,(inst_op1_d & (1 << (inst_op2_d & 31))));
+ break;
+ case O_BTSd:
+ FillFlags();
+ SETFLAGBIT(CF,(inst_op1_d & (1 << (inst_op2_d & 31))));
+ inst_op1_d|=(1 << (inst_op2_d & 31));
+ break;
+ case O_BTCd:
+ FillFlags();
+ SETFLAGBIT(CF,(inst_op1_d & (1 << (inst_op2_d & 31))));
+ inst_op1_d^=(1 << (inst_op2_d & 31));
+ break;
+ case O_BTRd:
+ FillFlags();
+ SETFLAGBIT(CF,(inst_op1_d & (1 << (inst_op2_d & 31))));
+ inst_op1_d&=~(1 << (inst_op2_d & 31));
+ break;
+ case O_BSWAPw:
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegalopcode;
+ BSWAPW(inst_op1_w);
+ break;
+ case O_BSWAPd:
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegalopcode;
+ BSWAPD(inst_op1_d);
+ break;
+ case O_CMPXCHG:
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486NEWSLOW) goto illegalopcode;
+ FillFlags();
+ if (inst_op1_d==reg_eax) {
+ inst_op1_d=reg_32(inst.rm_index);
+ if (inst.rm<0xc0) SaveMd(inst.rm_eaa,inst_op1_d); // early write-pf
+ SETFLAGBIT(ZF,1);
+ } else {
+ if (inst.rm<0xc0) SaveMd(inst.rm_eaa,inst_op1_d); // early write-pf
+ reg_eax=inst_op1_d;
+ SETFLAGBIT(ZF,0);
+ }
+ break;
+ case O_FPU:
+#if C_FPU
+ switch (((inst.rm>=0xc0) << 3) | inst.code.save) {
+ case 0x00: FPU_ESC0_EA(inst.rm,inst.rm_eaa);break;
+ case 0x01: FPU_ESC1_EA(inst.rm,inst.rm_eaa);break;
+ case 0x02: FPU_ESC2_EA(inst.rm,inst.rm_eaa);break;
+ case 0x03: FPU_ESC3_EA(inst.rm,inst.rm_eaa);break;
+ case 0x04: FPU_ESC4_EA(inst.rm,inst.rm_eaa);break;
+ case 0x05: FPU_ESC5_EA(inst.rm,inst.rm_eaa);break;
+ case 0x06: FPU_ESC6_EA(inst.rm,inst.rm_eaa);break;
+ case 0x07: FPU_ESC7_EA(inst.rm,inst.rm_eaa);break;
+
+ case 0x08: FPU_ESC0_Normal(inst.rm);break;
+ case 0x09: FPU_ESC1_Normal(inst.rm);break;
+ case 0x0a: FPU_ESC2_Normal(inst.rm);break;
+ case 0x0b: FPU_ESC3_Normal(inst.rm);break;
+ case 0x0c: FPU_ESC4_Normal(inst.rm);break;
+ case 0x0d: FPU_ESC5_Normal(inst.rm);break;
+ case 0x0e: FPU_ESC6_Normal(inst.rm);break;
+ case 0x0f: FPU_ESC7_Normal(inst.rm);break;
+ }
+ goto nextopcode;
+#else
+ LOG(LOG_CPU,LOG_ERROR)("Unhandled FPU ESCAPE %d",inst.code.save);
+ goto nextopcode;
+#endif
+ case O_BOUNDw:
+ {
+ Bit16s bound_min, bound_max;
+ bound_min=LoadMw(inst.rm_eaa);
+ bound_max=LoadMw(inst.rm_eaa+2);
+ if ( (((Bit16s)inst_op1_w) < bound_min) || (((Bit16s)inst_op1_w) > bound_max) ) {
+ EXCEPTION(5);
+ }
+ }
+ break;
+ case 0:
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("OP:Unhandled code %d entry %X",inst.code.op,inst.entry);
+
+}
diff --git a/src/cpu/core_full/optable.h b/src/cpu/core_full/optable.h
new file mode 100644
index 000000000..1ea95db4c
--- /dev/null
+++ b/src/cpu/core_full/optable.h
@@ -0,0 +1,832 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+/* Big ass opcode table normal,double, 66 normal, 66 double */
+static OpCode OpCodeTable[1024]={
+/* 0x00 - 0x07 */
+{L_MODRM ,t_ADDb ,S_Eb ,M_EbGb },{L_MODRM ,t_ADDw ,S_Ew ,M_EwGw },
+{L_MODRM ,t_ADDb ,S_Gb ,M_GbEb },{L_MODRM ,t_ADDw ,S_Gw ,M_GwEw },
+{L_REGbIb ,t_ADDb ,S_REGb ,REGI_AL },{L_REGwIw ,t_ADDw ,S_REGw ,REGI_AX },
+{L_SEG ,0 ,S_PUSHw,es },{D_POPSEGw,0 ,0 ,es },
+/* 0x08 - 0x0f */
+{L_MODRM ,t_ORb ,S_Eb ,M_EbGb },{L_MODRM ,t_ORw ,S_Ew ,M_EwGw },
+{L_MODRM ,t_ORb ,S_Gb ,M_GbEb },{L_MODRM ,t_ORw ,S_Gw ,M_GwEw },
+{L_REGbIb ,t_ORb ,S_REGb ,REGI_AL },{L_REGwIw ,t_ORw ,S_REGw ,REGI_AX },
+{L_SEG ,0 ,S_PUSHw,cs },{L_DOUBLE ,0 ,0 ,0 },
+
+/* 0x10 - 0x17 */
+{L_MODRM ,t_ADCb ,S_Eb ,M_EbGb },{L_MODRM ,t_ADCw ,S_Ew ,M_EwGw },
+{L_MODRM ,t_ADCb ,S_Gb ,M_GbEb },{L_MODRM ,t_ADCw ,S_Gw ,M_GwEw },
+{L_REGbIb ,t_ADCb ,S_REGb ,REGI_AL },{L_REGwIw ,t_ADCw ,S_REGw ,REGI_AX },
+{L_SEG ,0 ,S_PUSHw,ss },{D_POPSEGw,0 ,0 ,ss },
+/* 0x18 - 0x1f */
+{L_MODRM ,t_SBBb ,S_Eb ,M_EbGb },{L_MODRM ,t_SBBw ,S_Ew ,M_EwGw },
+{L_MODRM ,t_SBBb ,S_Gb ,M_GbEb },{L_MODRM ,t_SBBw ,S_Gw ,M_GwEw },
+{L_REGbIb ,t_SBBb ,S_REGb ,REGI_AL },{L_REGwIw ,t_SBBw ,S_REGw ,REGI_AX },
+{L_SEG ,0 ,S_PUSHw,ds },{D_POPSEGw,0 ,0 ,ds },
+
+/* 0x20 - 0x27 */
+{L_MODRM ,t_ANDb ,S_Eb ,M_EbGb },{L_MODRM ,t_ANDw ,S_Ew ,M_EwGw },
+{L_MODRM ,t_ANDb ,S_Gb ,M_GbEb },{L_MODRM ,t_ANDw ,S_Gw ,M_GwEw },
+{L_REGbIb ,t_ANDb ,S_REGb ,REGI_AL },{L_REGwIw ,t_ANDw ,S_REGw ,REGI_AX },
+{L_PRESEG ,0 ,0 ,es },{D_DAA ,0 ,0 ,0 },
+/* 0x28 - 0x2f */
+{L_MODRM ,t_SUBb ,S_Eb ,M_EbGb },{L_MODRM ,t_SUBw ,S_Ew ,M_EwGw },
+{L_MODRM ,t_SUBb ,S_Gb ,M_GbEb },{L_MODRM ,t_SUBw ,S_Gw ,M_GwEw },
+{L_REGbIb ,t_SUBb ,S_REGb ,REGI_AL },{L_REGwIw ,t_SUBw ,S_REGw ,REGI_AX },
+{L_PRESEG ,0 ,0 ,cs },{D_DAS ,0 ,0 ,0 },
+
+/* 0x30 - 0x37 */
+{L_MODRM ,t_XORb ,S_Eb ,M_EbGb },{L_MODRM ,t_XORw ,S_Ew ,M_EwGw },
+{L_MODRM ,t_XORb ,S_Gb ,M_GbEb },{L_MODRM ,t_XORw ,S_Gw ,M_GwEw },
+{L_REGbIb ,t_XORb ,S_REGb ,REGI_AL },{L_REGwIw ,t_XORw ,S_REGw ,REGI_AX },
+{L_PRESEG ,0 ,0 ,ss },{D_AAA ,0 ,0 ,0 },
+/* 0x38 - 0x3f */
+{L_MODRM ,t_CMPb ,0 ,M_EbGb },{L_MODRM ,t_CMPw ,0 ,M_EwGw },
+{L_MODRM ,t_CMPb ,0 ,M_GbEb },{L_MODRM ,t_CMPw ,0 ,M_GwEw },
+{L_REGbIb ,t_CMPb ,0 ,REGI_AL },{L_REGwIw ,t_CMPw ,0 ,REGI_AX },
+{L_PRESEG ,0 ,0 ,ds },{D_AAS ,0 ,0 ,0 },
+
+/* 0x40 - 0x47 */
+{L_REGw ,t_INCw ,S_REGw ,REGI_AX},{L_REGw ,t_INCw ,S_REGw ,REGI_CX},
+{L_REGw ,t_INCw ,S_REGw ,REGI_DX},{L_REGw ,t_INCw ,S_REGw ,REGI_BX},
+{L_REGw ,t_INCw ,S_REGw ,REGI_SP},{L_REGw ,t_INCw ,S_REGw ,REGI_BP},
+{L_REGw ,t_INCw ,S_REGw ,REGI_SI},{L_REGw ,t_INCw ,S_REGw ,REGI_DI},
+/* 0x48 - 0x4f */
+{L_REGw ,t_DECw ,S_REGw ,REGI_AX},{L_REGw ,t_DECw ,S_REGw ,REGI_CX},
+{L_REGw ,t_DECw ,S_REGw ,REGI_DX},{L_REGw ,t_DECw ,S_REGw ,REGI_BX},
+{L_REGw ,t_DECw ,S_REGw ,REGI_SP},{L_REGw ,t_DECw ,S_REGw ,REGI_BP},
+{L_REGw ,t_DECw ,S_REGw ,REGI_SI},{L_REGw ,t_DECw ,S_REGw ,REGI_DI},
+
+/* 0x50 - 0x57 */
+{L_REGw ,0 ,S_PUSHw,REGI_AX},{L_REGw ,0 ,S_PUSHw,REGI_CX},
+{L_REGw ,0 ,S_PUSHw,REGI_DX},{L_REGw ,0 ,S_PUSHw,REGI_BX},
+{L_REGw ,0 ,S_PUSHw,REGI_SP},{L_REGw ,0 ,S_PUSHw,REGI_BP},
+{L_REGw ,0 ,S_PUSHw,REGI_SI},{L_REGw ,0 ,S_PUSHw,REGI_DI},
+/* 0x58 - 0x5f */
+{L_POPw ,0 ,S_REGw ,REGI_AX},{L_POPw ,0 ,S_REGw ,REGI_CX},
+{L_POPw ,0 ,S_REGw ,REGI_DX},{L_POPw ,0 ,S_REGw ,REGI_BX},
+{L_POPw ,0 ,S_REGw ,REGI_SP},{L_POPw ,0 ,S_REGw ,REGI_BP},
+{L_POPw ,0 ,S_REGw ,REGI_SI},{L_POPw ,0 ,S_REGw ,REGI_DI},
+
+
+/* 0x60 - 0x67 */
+{D_PUSHAw ,0 ,0 ,0 },{D_POPAw ,0 ,0 ,0 },
+{L_MODRM ,O_BOUNDw ,0 ,M_Gw },{L_MODRM_NVM ,O_ARPL ,S_Ew ,M_EwGw },
+{L_PRESEG ,0 ,0 ,fs },{L_PRESEG ,0 ,0 ,gs },
+{L_PREOP ,0 ,0 ,0 },{L_PREADD ,0 ,0 ,0 },
+/* 0x68 - 0x6f */
+{L_Iw ,0 ,S_PUSHw,0 },{L_MODRM ,O_IMULRw ,S_Gw ,M_EwxIwx},
+{L_Ibx ,0 ,S_PUSHw,0 },{L_MODRM ,O_IMULRw ,S_Gw ,M_EwxIbx},
+{L_STRING ,R_INSB ,0 ,0 },{L_STRING ,R_INSW ,0 ,0 },
+{L_STRING ,R_OUTSB ,0 ,0 },{L_STRING ,R_OUTSW ,0 ,0 },
+
+
+/* 0x70 - 0x77 */
+{L_Ibx ,O_C_O ,S_C_AIPw,0 },{L_Ibx ,O_C_NO ,S_C_AIPw,0 },
+{L_Ibx ,O_C_B ,S_C_AIPw,0 },{L_Ibx ,O_C_NB ,S_C_AIPw,0 },
+{L_Ibx ,O_C_Z ,S_C_AIPw,0 },{L_Ibx ,O_C_NZ ,S_C_AIPw,0 },
+{L_Ibx ,O_C_BE ,S_C_AIPw,0 },{L_Ibx ,O_C_NBE ,S_C_AIPw,0 },
+/* 0x78 - 0x7f */
+{L_Ibx ,O_C_S ,S_C_AIPw,0 },{L_Ibx ,O_C_NS ,S_C_AIPw,0 },
+{L_Ibx ,O_C_P ,S_C_AIPw,0 },{L_Ibx ,O_C_NP ,S_C_AIPw,0 },
+{L_Ibx ,O_C_L ,S_C_AIPw,0 },{L_Ibx ,O_C_NL ,S_C_AIPw,0 },
+{L_Ibx ,O_C_LE ,S_C_AIPw,0 },{L_Ibx ,O_C_NLE ,S_C_AIPw,0 },
+
+
+/* 0x80 - 0x87 */
+{L_MODRM ,0 ,0 ,M_GRP },{L_MODRM ,1 ,0 ,M_GRP },
+{L_MODRM ,0 ,0 ,M_GRP },{L_MODRM ,3 ,0 ,M_GRP },
+{L_MODRM ,t_TESTb ,0 ,M_EbGb },{L_MODRM ,t_TESTw ,0 ,M_EwGw },
+{L_MODRM ,0 ,S_EbGb ,M_GbEb },{L_MODRM ,0 ,S_EwGw ,M_GwEw },
+/* 0x88 - 0x8f */
+{L_MODRM ,0 ,S_Eb ,M_Gb },{L_MODRM ,0 ,S_Ew ,M_Gw },
+{L_MODRM ,0 ,S_Gb ,M_Eb },{L_MODRM ,0 ,S_Gw ,M_Ew },
+{L_MODRM ,0 ,S_Ew ,M_SEG },{L_MODRM ,0 ,S_Gw ,M_EA },
+{L_MODRM ,0 ,S_SEGm ,M_Ew },{L_POPwRM ,0 ,S_Ew ,M_None },
+
+/* 0x90 - 0x97 */
+{D_NOP ,0 ,0 ,0 },{L_REGw ,O_XCHG_AX ,S_REGw ,REGI_CX},
+{L_REGw ,O_XCHG_AX ,S_REGw ,REGI_DX},{L_REGw ,O_XCHG_AX ,S_REGw ,REGI_BX},
+{L_REGw ,O_XCHG_AX ,S_REGw ,REGI_SP},{L_REGw ,O_XCHG_AX ,S_REGw ,REGI_BP},
+{L_REGw ,O_XCHG_AX ,S_REGw ,REGI_SI},{L_REGw ,O_XCHG_AX ,S_REGw ,REGI_DI},
+/* 0x98 - 0x9f */
+{D_CBW ,0 ,0 ,0 },{D_CWD ,0 ,0 ,0 },
+{L_Ifw ,O_CALLFw ,0 ,0 },{D_WAIT ,0 ,0 ,0 },
+{D_PUSHF ,0 ,0 ,0 },{D_POPF ,0 ,0 ,0 },
+{D_SAHF ,0 ,0 ,0 },{D_LAHF ,0 ,0 ,0 },
+
+
+/* 0xa0 - 0xa7 */
+{L_OP ,O_ALOP ,0 ,0 },{L_OP ,O_AXOP ,0 ,0 },
+{L_OP ,O_OPAL ,0 ,0 },{L_OP ,O_OPAX ,0 ,0 },
+{L_STRING ,R_MOVSB ,0 ,0 },{L_STRING ,R_MOVSW ,0 ,0 },
+{L_STRING ,R_CMPSB ,0 ,0 },{L_STRING ,R_CMPSW ,0 ,0 },
+/* 0xa8 - 0xaf */
+{L_REGbIb ,t_TESTb ,0 ,REGI_AL},{L_REGwIw ,t_TESTw ,0 ,REGI_AX},
+{L_STRING ,R_STOSB ,0 ,0 },{L_STRING ,R_STOSW ,0 ,0 },
+{L_STRING ,R_LODSB ,0 ,0 },{L_STRING ,R_LODSW ,0 ,0 },
+{L_STRING ,R_SCASB ,0 ,0 },{L_STRING ,R_SCASW ,0 ,0 },
+
+/* 0xb0 - 0xb7 */
+{L_Ib ,0 ,S_REGb ,REGI_AL},{L_Ib ,0 ,S_REGb ,REGI_CL},
+{L_Ib ,0 ,S_REGb ,REGI_DL},{L_Ib ,0 ,S_REGb ,REGI_BL},
+{L_Ib ,0 ,S_REGb ,REGI_AH},{L_Ib ,0 ,S_REGb ,REGI_CH},
+{L_Ib ,0 ,S_REGb ,REGI_DH},{L_Ib ,0 ,S_REGb ,REGI_BH},
+/* 0xb8 - 0xbf */
+{L_Iw ,0 ,S_REGw ,REGI_AX},{L_Iw ,0 ,S_REGw ,REGI_CX},
+{L_Iw ,0 ,S_REGw ,REGI_DX},{L_Iw ,0 ,S_REGw ,REGI_BX},
+{L_Iw ,0 ,S_REGw ,REGI_SP},{L_Iw ,0 ,S_REGw ,REGI_BP},
+{L_Iw ,0 ,S_REGw ,REGI_SI},{L_Iw ,0 ,S_REGw ,REGI_DI},
+
+/* 0xc0 - 0xc7 */
+{L_MODRM ,5 ,0 ,M_SHIFT_Ib },{L_MODRM ,6 ,0 ,M_SHIFT_Ib },
+{L_POPw ,0 ,S_IPIw ,0 },{L_POPw ,0 ,S_IP ,0 },
+{L_MODRM ,O_SEGES ,S_SEGGw,M_Efw },{L_MODRM ,O_SEGDS ,S_SEGGw,M_Efw },
+{L_MODRM ,0 ,S_Eb ,M_Ib },{L_MODRM ,0 ,S_Ew ,M_Iw },
+/* 0xc8 - 0xcf */
+{D_ENTERw ,0 ,0 ,0 },{D_LEAVEw ,0 ,0 ,0 },
+{D_RETFwIw ,0 ,0 ,0 },{D_RETFw ,0 ,0 ,0 },
+{L_VAL ,O_INT ,0 ,3 },{L_Ib ,O_INT ,0 ,0 },
+{L_INTO ,O_INT ,0 ,0 },{D_IRETw ,0 ,0 ,0 },
+
+/* 0xd0 - 0xd7 */
+{L_MODRM ,5 ,0 ,M_SHIFT_1 },{L_MODRM ,6 ,0 ,M_SHIFT_1 },
+{L_MODRM ,5 ,0 ,M_SHIFT_CL },{L_MODRM ,6 ,0 ,M_SHIFT_CL },
+{L_Ib ,O_AAM ,0 ,0 },{L_Ib ,O_AAD ,0 ,0 },
+{D_SETALC ,0 ,0 ,0 },{D_XLAT ,0 ,0 ,0 },
+//TODO FPU
+/* 0xd8 - 0xdf */
+{L_MODRM ,O_FPU ,0 ,0 },{L_MODRM ,O_FPU ,1 ,0 },
+{L_MODRM ,O_FPU ,2 ,0 },{L_MODRM ,O_FPU ,3 ,0 },
+{L_MODRM ,O_FPU ,4 ,0 },{L_MODRM ,O_FPU ,5 ,0 },
+{L_MODRM ,O_FPU ,6 ,0 },{L_MODRM ,O_FPU ,7 ,0 },
+
+/* 0xe0 - 0xe7 */
+{L_Ibx ,O_LOOPNZ ,S_AIPw ,0 },{L_Ibx ,O_LOOPZ ,S_AIPw ,0 },
+{L_Ibx ,O_LOOP ,S_AIPw ,0 },{L_Ibx ,O_JCXZ ,S_AIPw ,0 },
+{L_Ib ,O_INb ,0 ,0 },{L_Ib ,O_INw ,0 ,0 },
+{L_Ib ,O_OUTb ,0 ,0 },{L_Ib ,O_OUTw ,0 ,0 },
+/* 0xe8 - 0xef */
+{L_Iw ,O_CALLNw ,S_AIPw ,0 },{L_Iwx ,0 ,S_AIPw ,0 },
+{L_Ifw ,O_JMPFw ,0 ,0 },{L_Ibx ,0 ,S_AIPw ,0 },
+{L_REGw ,O_INb ,0 ,REGI_DX},{L_REGw ,O_INw ,0 ,REGI_DX},
+{L_REGw ,O_OUTb ,0 ,REGI_DX},{L_REGw ,O_OUTw ,0 ,REGI_DX},
+
+/* 0xf0 - 0xf7 */
+{D_LOCK ,0 ,0 ,0 },{D_ICEBP ,0 ,0 ,0 },
+{L_PREREPNE ,0 ,0 ,0 },{L_PREREP ,0 ,0 ,0 },
+{D_HLT ,0 ,0 ,0 },{D_CMC ,0 ,0 ,0 },
+{L_MODRM ,8 ,0 ,M_GRP },{L_MODRM ,9 ,0 ,M_GRP },
+/* 0xf8 - 0xff */
+{D_CLC ,0 ,0 ,0 },{D_STC ,0 ,0 ,0 },
+{D_CLI ,0 ,0 ,0 },{D_STI ,0 ,0 ,0 },
+{D_CLD ,0 ,0 ,0 },{D_STD ,0 ,0 ,0 },
+{L_MODRM ,0xb ,0 ,M_GRP },{L_MODRM ,0xc ,0 ,M_GRP },
+
+/* 0x100 - 0x107 */
+{L_MODRM ,O_GRP6w ,S_Ew ,M_Ew },{L_MODRM ,O_GRP7w ,S_Ew ,M_Ew },
+{L_MODRM_NVM ,O_LAR ,S_Gw ,M_EwGw },{L_MODRM_NVM ,O_LSL ,S_Gw ,M_EwGw },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{D_CLTS ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+/* 0x108 - 0x10f */
+{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 },
+
+/* 0x110 - 0x117 */
+{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 },
+/* 0x118 - 0x11f */
+{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 },
+
+/* 0x120 - 0x127 */
+{L_MODRM ,O_M_Rd_CRx ,S_Ed ,0 },{L_MODRM ,O_M_Rd_DRx ,S_Ed ,0 },
+{L_MODRM ,O_M_CRx_Rd ,0 ,M_Ed },{L_MODRM ,O_M_DRx_Rd ,0 ,M_Ed },
+{L_MODRM ,O_M_Rd_TRx ,S_Ed ,0 },{0 ,0 ,0 ,0 },
+{L_MODRM ,O_M_TRx_Rd ,0 ,M_Ed },{0 ,0 ,0 ,0 },
+
+/* 0x128 - 0x12f */
+{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 },
+
+/* 0x130 - 0x137 */
+{0 ,0 ,0 ,0 },{D_RDTSC ,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 },
+/* 0x138 - 0x13f */
+{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 },
+
+/* 0x140 - 0x147 */
+{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 },
+/* 0x148 - 0x14f */
+{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 },
+
+/* 0x150 - 0x157 */
+{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 },
+/* 0x158 - 0x15f */
+{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 },
+
+/* 0x160 - 0x167 */
+{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 },
+/* 0x168 - 0x16f */
+{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 },
+
+
+/* 0x170 - 0x177 */
+{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 },
+/* 0x178 - 0x17f */
+{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 },
+
+/* 0x180 - 0x187 */
+{L_Iwx ,O_C_O ,S_C_AIPw,0 },{L_Iwx ,O_C_NO ,S_C_AIPw,0 },
+{L_Iwx ,O_C_B ,S_C_AIPw,0 },{L_Iwx ,O_C_NB ,S_C_AIPw,0 },
+{L_Iwx ,O_C_Z ,S_C_AIPw,0 },{L_Iwx ,O_C_NZ ,S_C_AIPw,0 },
+{L_Iwx ,O_C_BE ,S_C_AIPw,0 },{L_Iwx ,O_C_NBE ,S_C_AIPw,0 },
+/* 0x188 - 0x18f */
+{L_Iwx ,O_C_S ,S_C_AIPw,0 },{L_Iwx ,O_C_NS ,S_C_AIPw,0 },
+{L_Iwx ,O_C_P ,S_C_AIPw,0 },{L_Iwx ,O_C_NP ,S_C_AIPw,0 },
+{L_Iwx ,O_C_L ,S_C_AIPw,0 },{L_Iwx ,O_C_NL ,S_C_AIPw,0 },
+{L_Iwx ,O_C_LE ,S_C_AIPw,0 },{L_Iwx ,O_C_NLE ,S_C_AIPw,0 },
+
+/* 0x190 - 0x197 */
+{L_MODRM ,O_C_O ,S_C_Eb,0 },{L_MODRM ,O_C_NO ,S_C_Eb,0 },
+{L_MODRM ,O_C_B ,S_C_Eb,0 },{L_MODRM ,O_C_NB ,S_C_Eb,0 },
+{L_MODRM ,O_C_Z ,S_C_Eb,0 },{L_MODRM ,O_C_NZ ,S_C_Eb,0 },
+{L_MODRM ,O_C_BE ,S_C_Eb,0 },{L_MODRM ,O_C_NBE ,S_C_Eb,0 },
+/* 0x198 - 0x19f */
+{L_MODRM ,O_C_S ,S_C_Eb,0 },{L_MODRM ,O_C_NS ,S_C_Eb,0 },
+{L_MODRM ,O_C_P ,S_C_Eb,0 },{L_MODRM ,O_C_NP ,S_C_Eb,0 },
+{L_MODRM ,O_C_L ,S_C_Eb,0 },{L_MODRM ,O_C_NL ,S_C_Eb,0 },
+{L_MODRM ,O_C_LE ,S_C_Eb,0 },{L_MODRM ,O_C_NLE ,S_C_Eb,0 },
+
+/* 0x1a0 - 0x1a7 */
+{L_SEG ,0 ,S_PUSHw ,fs },{D_POPSEGw,0 ,0 ,fs },
+{D_CPUID ,0 ,0 ,0 },{L_MODRM ,O_BTw ,S_Ew ,M_EwGwt },
+{L_MODRM ,O_DSHLw ,S_Ew,M_EwGwIb },{L_MODRM ,O_DSHLw ,S_Ew ,M_EwGwCL },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+/* 0x1a8 - 0x1af */
+{L_SEG ,0 ,S_PUSHw ,gs },{D_POPSEGw,0 ,0 ,gs },
+{0 ,0 ,0 ,0 },{L_MODRM ,O_BTSw ,S_Ew ,M_EwGwt },
+{L_MODRM ,O_DSHRw ,S_Ew,M_EwGwIb },{L_MODRM ,O_DSHRw ,S_Ew ,M_EwGwCL },
+{0 ,0 ,0 ,0 },{L_MODRM ,O_IMULRw ,S_Gw ,M_EwxGwx },
+
+/* 0x1b0 - 0x1b7 */
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{L_MODRM ,O_SEGSS ,S_SEGGw,M_Efw },{L_MODRM ,O_BTRw ,S_Ew ,M_EwGwt },
+{L_MODRM ,O_SEGFS ,S_SEGGw,M_Efw },{L_MODRM ,O_SEGGS ,S_SEGGw,M_Efw },
+{L_MODRM ,0 ,S_Gw ,M_Eb },{L_MODRM ,0 ,S_Gw ,M_Ew },
+/* 0x1b8 - 0x1bf */
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{L_MODRM ,0xe ,0 ,M_GRP },{L_MODRM ,O_BTCw ,S_Ew ,M_EwGwt },
+{L_MODRM ,O_BSFw ,S_Gw ,M_Ew },{L_MODRM ,O_BSRw ,S_Gw ,M_Ew },
+{L_MODRM ,0 ,S_Gw ,M_Ebx },{L_MODRM ,0 ,S_Gw ,M_Ewx },
+
+/* 0x1c0 - 0x1cc */
+{L_MODRM ,t_ADDb ,S_EbGb ,M_GbEb },{L_MODRM ,t_ADDw ,S_EwGw ,M_GwEw },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+/* 0x1c8 - 0x1cf */
+{L_REGw ,O_BSWAPw ,S_REGw ,REGI_AX},{L_REGw ,O_BSWAPw ,S_REGw ,REGI_CX},
+{L_REGw ,O_BSWAPw ,S_REGw ,REGI_DX},{L_REGw ,O_BSWAPw ,S_REGw ,REGI_BX},
+{L_REGw ,O_BSWAPw ,S_REGw ,REGI_SP},{L_REGw ,O_BSWAPw ,S_REGw ,REGI_BP},
+{L_REGw ,O_BSWAPw ,S_REGw ,REGI_SI},{L_REGw ,O_BSWAPw ,S_REGw ,REGI_DI},
+
+/* 0x1d0 - 0x1d7 */
+{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 },
+/* 0x1d8 - 0x1df */
+{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 },
+
+/* 0x1e0 - 0x1ee */
+{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 },
+/* 0x1e8 - 0x1ef */
+{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 },
+
+/* 0x1f0 - 0x1fc */
+{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 },
+/* 0x1f8 - 0x1ff */
+{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 },
+
+
+/* 0x200 - 0x207 */
+{L_MODRM ,t_ADDb ,S_Eb ,M_EbGb },{L_MODRM ,t_ADDd ,S_Ed ,M_EdGd },
+{L_MODRM ,t_ADDb ,S_Gb ,M_GbEb },{L_MODRM ,t_ADDd ,S_Gd ,M_GdEd },
+{L_REGbIb ,t_ADDb ,S_REGb ,REGI_AL },{L_REGdId ,t_ADDd ,S_REGd ,REGI_AX },
+{L_SEG ,0 ,S_PUSHd,es },{D_POPSEGd,0 ,0 ,es },
+/* 0x208 - 0x20f */
+{L_MODRM ,t_ORb ,S_Eb ,M_EbGb },{L_MODRM ,t_ORd ,S_Ed ,M_EdGd },
+{L_MODRM ,t_ORb ,S_Gb ,M_GbEb },{L_MODRM ,t_ORd ,S_Gd ,M_GdEd },
+{L_REGbIb ,t_ORb ,S_REGb ,REGI_AL },{L_REGdId ,t_ORd ,S_REGd ,REGI_AX },
+{L_SEG ,0 ,S_PUSHd,cs },{L_DOUBLE ,0 ,0 ,0 },
+
+/* 0x210 - 0x217 */
+{L_MODRM ,t_ADCb ,S_Eb ,M_EbGb },{L_MODRM ,t_ADCd ,S_Ed ,M_EdGd },
+{L_MODRM ,t_ADCb ,S_Gb ,M_GbEb },{L_MODRM ,t_ADCd ,S_Gd ,M_GdEd },
+{L_REGbIb ,t_ADCb ,S_REGb ,REGI_AL },{L_REGdId ,t_ADCd ,S_REGd ,REGI_AX },
+{L_SEG ,0 ,S_PUSHd,ss },{D_POPSEGd,0 ,0 ,ss },
+/* 0x218 - 0x21f */
+{L_MODRM ,t_SBBb ,S_Eb ,M_EbGb },{L_MODRM ,t_SBBd ,S_Ed ,M_EdGd },
+{L_MODRM ,t_SBBb ,S_Gb ,M_GbEb },{L_MODRM ,t_SBBd ,S_Gd ,M_GdEd },
+{L_REGbIb ,t_SBBb ,S_REGb ,REGI_AL },{L_REGdId ,t_SBBd ,S_REGd ,REGI_AX },
+{L_SEG ,0 ,S_PUSHd,ds },{D_POPSEGd,0 ,0 ,ds },
+
+/* 0x220 - 0x227 */
+{L_MODRM ,t_ANDb ,S_Eb ,M_EbGb },{L_MODRM ,t_ANDd ,S_Ed ,M_EdGd },
+{L_MODRM ,t_ANDb ,S_Gb ,M_GbEb },{L_MODRM ,t_ANDd ,S_Gd ,M_GdEd },
+{L_REGbIb ,t_ANDb ,S_REGb ,REGI_AL },{L_REGdId ,t_ANDd ,S_REGd ,REGI_AX },
+{L_PRESEG ,0 ,0 ,es },{D_DAA ,0 ,0 ,0 },
+/* 0x228 - 0x22f */
+{L_MODRM ,t_SUBb ,S_Eb ,M_EbGb },{L_MODRM ,t_SUBd ,S_Ed ,M_EdGd },
+{L_MODRM ,t_SUBb ,S_Gb ,M_GbEb },{L_MODRM ,t_SUBd ,S_Gd ,M_GdEd },
+{L_REGbIb ,t_SUBb ,S_REGb ,REGI_AL },{L_REGdId ,t_SUBd ,S_REGd ,REGI_AX },
+{L_PRESEG ,0 ,0 ,cs },{D_DAS ,0 ,0 ,0 },
+
+/* 0x230 - 0x237 */
+{L_MODRM ,t_XORb ,S_Eb ,M_EbGb },{L_MODRM ,t_XORd ,S_Ed ,M_EdGd },
+{L_MODRM ,t_XORb ,S_Gb ,M_GbEb },{L_MODRM ,t_XORd ,S_Gd ,M_GdEd },
+{L_REGbIb ,t_XORb ,S_REGb ,REGI_AL },{L_REGdId ,t_XORd ,S_REGd ,REGI_AX },
+{L_PRESEG ,0 ,0 ,ss },{D_AAA ,0 ,0 ,0 },
+/* 0x238 - 0x23f */
+{L_MODRM ,t_CMPb ,0 ,M_EbGb },{L_MODRM ,t_CMPd ,0 ,M_EdGd },
+{L_MODRM ,t_CMPb ,0 ,M_GbEb },{L_MODRM ,t_CMPd ,0 ,M_GdEd },
+{L_REGbIb ,t_CMPb ,0 ,REGI_AL },{L_REGdId ,t_CMPd ,0 ,REGI_AX },
+{L_PRESEG ,0 ,0 ,ds },{D_AAS ,0 ,0 ,0 },
+
+/* 0x240 - 0x247 */
+{L_REGd ,t_INCd ,S_REGd ,REGI_AX},{L_REGd ,t_INCd ,S_REGd ,REGI_CX},
+{L_REGd ,t_INCd ,S_REGd ,REGI_DX},{L_REGd ,t_INCd ,S_REGd ,REGI_BX},
+{L_REGd ,t_INCd ,S_REGd ,REGI_SP},{L_REGd ,t_INCd ,S_REGd ,REGI_BP},
+{L_REGd ,t_INCd ,S_REGd ,REGI_SI},{L_REGd ,t_INCd ,S_REGd ,REGI_DI},
+/* 0x248 - 0x24f */
+{L_REGd ,t_DECd ,S_REGd ,REGI_AX},{L_REGd ,t_DECd ,S_REGd ,REGI_CX},
+{L_REGd ,t_DECd ,S_REGd ,REGI_DX},{L_REGd ,t_DECd ,S_REGd ,REGI_BX},
+{L_REGd ,t_DECd ,S_REGd ,REGI_SP},{L_REGd ,t_DECd ,S_REGd ,REGI_BP},
+{L_REGd ,t_DECd ,S_REGd ,REGI_SI},{L_REGd ,t_DECd ,S_REGd ,REGI_DI},
+
+/* 0x250 - 0x257 */
+{L_REGd ,0 ,S_PUSHd,REGI_AX},{L_REGd ,0 ,S_PUSHd,REGI_CX},
+{L_REGd ,0 ,S_PUSHd,REGI_DX},{L_REGd ,0 ,S_PUSHd,REGI_BX},
+{L_REGd ,0 ,S_PUSHd,REGI_SP},{L_REGd ,0 ,S_PUSHd,REGI_BP},
+{L_REGd ,0 ,S_PUSHd,REGI_SI},{L_REGd ,0 ,S_PUSHd,REGI_DI},
+/* 0x258 - 0x25f */
+{L_POPd ,0 ,S_REGd ,REGI_AX},{L_POPd ,0 ,S_REGd ,REGI_CX},
+{L_POPd ,0 ,S_REGd ,REGI_DX},{L_POPd ,0 ,S_REGd ,REGI_BX},
+{L_POPd ,0 ,S_REGd ,REGI_SP},{L_POPd ,0 ,S_REGd ,REGI_BP},
+{L_POPd ,0 ,S_REGd ,REGI_SI},{L_POPd ,0 ,S_REGd ,REGI_DI},
+
+/* 0x260 - 0x267 */
+{D_PUSHAd ,0 ,0 ,0 },{D_POPAd ,0 ,0 ,0 },
+{L_MODRM ,O_BOUNDd ,0 ,0 },{0 ,0 ,0 ,0 },
+{L_PRESEG ,0 ,0 ,fs },{L_PRESEG ,0 ,0 ,gs },
+{L_PREOP ,0 ,0 ,0 },{L_PREADD ,0 ,0 ,0 },
+/* 0x268 - 0x26f */
+{L_Id ,0 ,S_PUSHd,0 },{L_MODRM ,O_IMULRd ,S_Gd ,M_EdId},
+{L_Ibx ,0 ,S_PUSHd,0 },{L_MODRM ,O_IMULRd ,S_Gd ,M_EdIbx},
+{L_STRING ,R_INSB ,0 ,0 },{L_STRING ,R_INSD ,0 ,0 },
+{L_STRING ,R_OUTSB ,0 ,0 },{L_STRING ,R_OUTSD ,0 ,0 },
+
+/* 0x270 - 0x277 */
+{L_Ibx ,O_C_O ,S_C_AIPd,0 },{L_Ibx ,O_C_NO ,S_C_AIPd,0 },
+{L_Ibx ,O_C_B ,S_C_AIPd,0 },{L_Ibx ,O_C_NB ,S_C_AIPd,0 },
+{L_Ibx ,O_C_Z ,S_C_AIPd,0 },{L_Ibx ,O_C_NZ ,S_C_AIPd,0 },
+{L_Ibx ,O_C_BE ,S_C_AIPd,0 },{L_Ibx ,O_C_NBE ,S_C_AIPd,0 },
+/* 0x278 - 0x27f */
+{L_Ibx ,O_C_S ,S_C_AIPd,0 },{L_Ibx ,O_C_NS ,S_C_AIPd,0 },
+{L_Ibx ,O_C_P ,S_C_AIPd,0 },{L_Ibx ,O_C_NP ,S_C_AIPd,0 },
+{L_Ibx ,O_C_L ,S_C_AIPd,0 },{L_Ibx ,O_C_NL ,S_C_AIPd,0 },
+{L_Ibx ,O_C_LE ,S_C_AIPd,0 },{L_Ibx ,O_C_NLE ,S_C_AIPd,0 },
+
+/* 0x280 - 0x287 */
+{L_MODRM ,0 ,0 ,M_GRP },{L_MODRM ,2 ,0 ,M_GRP },
+{L_MODRM ,0 ,0 ,M_GRP },{L_MODRM ,4 ,0 ,M_GRP },
+{L_MODRM ,t_TESTb ,0 ,M_EbGb },{L_MODRM ,t_TESTd ,0 ,M_EdGd },
+{L_MODRM ,0 ,S_EbGb ,M_GbEb },{L_MODRM ,0 ,S_EdGd ,M_GdEd },
+/* 0x288 - 0x28f */
+{L_MODRM ,0 ,S_Eb ,M_Gb },{L_MODRM ,0 ,S_Ed ,M_Gd },
+{L_MODRM ,0 ,S_Gb ,M_Eb },{L_MODRM ,0 ,S_Gd ,M_Ed },
+{L_MODRM ,0 ,S_EdMw ,M_SEG },{L_MODRM ,0 ,S_Gd ,M_EA },
+{L_MODRM ,0 ,S_SEGm ,M_Ew },{L_POPdRM ,0 ,S_Ed ,M_None },
+
+/* 0x290 - 0x297 */
+{D_NOP ,0 ,0 ,0 },{L_REGd ,O_XCHG_EAX ,S_REGd ,REGI_CX},
+{L_REGd ,O_XCHG_EAX ,S_REGd ,REGI_DX},{L_REGd ,O_XCHG_EAX ,S_REGd ,REGI_BX},
+{L_REGd ,O_XCHG_EAX ,S_REGd ,REGI_SP},{L_REGd ,O_XCHG_EAX ,S_REGd ,REGI_BP},
+{L_REGd ,O_XCHG_EAX ,S_REGd ,REGI_SI},{L_REGd ,O_XCHG_EAX ,S_REGd ,REGI_DI},
+/* 0x298 - 0x29f */
+{D_CWDE ,0 ,0 ,0 },{D_CDQ ,0 ,0 ,0 },
+{L_Ifd ,O_CALLFd ,0 ,0 },{D_WAIT ,0 ,0 ,0 },
+{D_PUSHF ,0 ,0 ,true },{D_POPF ,0 ,0 ,true },
+{D_SAHF ,0 ,0 ,0 },{D_LAHF ,0 ,0 ,0 },
+
+/* 0x2a0 - 0x2a7 */
+{L_OP ,O_ALOP ,0 ,0 },{L_OP ,O_EAXOP ,0 ,0 },
+{L_OP ,O_OPAL ,0 ,0 },{L_OP ,O_OPEAX ,0 ,0 },
+{L_STRING ,R_MOVSB ,0 ,0 },{L_STRING ,R_MOVSD ,0 ,0 },
+{L_STRING ,R_CMPSB ,0 ,0 },{L_STRING ,R_CMPSD ,0 ,0 },
+/* 0x2a8 - 0x2af */
+{L_REGbIb ,t_TESTb ,0 ,REGI_AL},{L_REGdId ,t_TESTd ,0 ,REGI_AX},
+{L_STRING ,R_STOSB ,0 ,0 },{L_STRING ,R_STOSD ,0 ,0 },
+{L_STRING ,R_LODSB ,0 ,0 },{L_STRING ,R_LODSD ,0 ,0 },
+{L_STRING ,R_SCASB ,0 ,0 },{L_STRING ,R_SCASD ,0 ,0 },
+
+/* 0x2b0 - 0x2b7 */
+{L_Ib ,0 ,S_REGb ,REGI_AL},{L_Ib ,0 ,S_REGb ,REGI_CL},
+{L_Ib ,0 ,S_REGb ,REGI_DL},{L_Ib ,0 ,S_REGb ,REGI_BL},
+{L_Ib ,0 ,S_REGb ,REGI_AH},{L_Ib ,0 ,S_REGb ,REGI_CH},
+{L_Ib ,0 ,S_REGb ,REGI_DH},{L_Ib ,0 ,S_REGb ,REGI_BH},
+/* 0x2b8 - 0x2bf */
+{L_Id ,0 ,S_REGd ,REGI_AX},{L_Id ,0 ,S_REGd ,REGI_CX},
+{L_Id ,0 ,S_REGd ,REGI_DX},{L_Id ,0 ,S_REGd ,REGI_BX},
+{L_Id ,0 ,S_REGd ,REGI_SP},{L_Id ,0 ,S_REGd ,REGI_BP},
+{L_Id ,0 ,S_REGd ,REGI_SI},{L_Id ,0 ,S_REGd ,REGI_DI},
+
+/* 0x2c0 - 0x2c7 */
+{L_MODRM ,5 ,0 ,M_SHIFT_Ib },{L_MODRM ,7 ,0 ,M_SHIFT_Ib },
+{L_POPd ,0 ,S_IPIw ,0 },{L_POPd ,0 ,S_IP ,0 },
+{L_MODRM ,O_SEGES ,S_SEGGd,M_Efd },{L_MODRM ,O_SEGDS ,S_SEGGd,M_Efd },
+{L_MODRM ,0 ,S_Eb ,M_Ib },{L_MODRM ,0 ,S_Ed ,M_Id },
+/* 0x2c8 - 0x2cf */
+{D_ENTERd ,0 ,0 ,0 },{D_LEAVEd ,0 ,0 ,0 },
+{D_RETFdIw ,0 ,0 ,0 },{D_RETFd ,0 ,0 ,0 },
+{L_VAL ,O_INT ,0 ,3 },{L_Ib ,O_INT ,0 ,0 },
+{L_INTO ,O_INT ,0 ,0 },{D_IRETd ,0 ,0 ,0 },
+
+/* 0x2d0 - 0x2d7 */
+{L_MODRM ,5 ,0 ,M_SHIFT_1 },{L_MODRM ,7 ,0 ,M_SHIFT_1 },
+{L_MODRM ,5 ,0 ,M_SHIFT_CL },{L_MODRM ,7 ,0 ,M_SHIFT_CL },
+{L_Ib ,O_AAM ,0 ,0 },{L_Ib ,O_AAD ,0 ,0 },
+{D_SETALC ,0 ,0 ,0 },{D_XLAT ,0 ,0 ,0 },
+/* 0x2d8 - 0x2df */
+{L_MODRM ,O_FPU ,0 ,0 },{L_MODRM ,O_FPU ,1 ,0 },
+{L_MODRM ,O_FPU ,2 ,0 },{L_MODRM ,O_FPU ,3 ,0 },
+{L_MODRM ,O_FPU ,4 ,0 },{L_MODRM ,O_FPU ,5 ,0 },
+{L_MODRM ,O_FPU ,6 ,0 },{L_MODRM ,O_FPU ,7 ,0 },
+
+/* 0x2e0 - 0x2e7 */
+{L_Ibx ,O_LOOPNZ ,S_AIPd ,0 },{L_Ibx ,O_LOOPZ ,S_AIPd ,0 },
+{L_Ibx ,O_LOOP ,S_AIPd ,0 },{L_Ibx ,O_JCXZ ,S_AIPd ,0 },
+{L_Ib ,O_INb ,0 ,0 },{L_Ib ,O_INd ,0 ,0 },
+{L_Ib ,O_OUTb ,0 ,0 },{L_Ib ,O_OUTd ,0 ,0 },
+/* 0x2e8 - 0x2ef */
+{L_Id ,O_CALLNd ,S_AIPd ,0 },{L_Idx ,0 ,S_AIPd ,0 },
+{L_Ifd ,O_JMPFd ,0 ,0 },{L_Ibx ,0 ,S_AIPd ,0 },
+{L_REGw ,O_INb ,0 ,REGI_DX},{L_REGw ,O_INd ,0 ,REGI_DX},
+{L_REGw ,O_OUTb ,0 ,REGI_DX},{L_REGw ,O_OUTd ,0 ,REGI_DX},
+
+/* 0x2f0 - 0x2f7 */
+{D_LOCK ,0 ,0 ,0 },{D_ICEBP ,0 ,0 ,0 },
+{L_PREREPNE ,0 ,0 ,0 },{L_PREREP ,0 ,0 ,0 },
+{D_HLT ,0 ,0 ,0 },{D_CMC ,0 ,0 ,0 },
+{L_MODRM ,8 ,0 ,M_GRP },{L_MODRM ,0xa ,0 ,M_GRP },
+/* 0x2f8 - 0x2ff */
+{D_CLC ,0 ,0 ,0 },{D_STC ,0 ,0 ,0 },
+{D_CLI ,0 ,0 ,0 },{D_STI ,0 ,0 ,0 },
+{D_CLD ,0 ,0 ,0 },{D_STD ,0 ,0 ,0 },
+{L_MODRM ,0xb ,0 ,M_GRP },{L_MODRM ,0xd ,0 ,M_GRP },
+
+
+/* 0x300 - 0x307 */
+{L_MODRM ,O_GRP6d ,S_Ew ,M_Ew },{L_MODRM ,O_GRP7d ,S_Ew ,M_Ew },
+{L_MODRM_NVM ,O_LAR ,S_Gd ,M_EdGd },{L_MODRM_NVM ,O_LSL ,S_Gd ,M_EdGd },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{D_CLTS ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+/* 0x308 - 0x30f */
+{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 },
+
+/* 0x310 - 0x317 */
+{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 },
+/* 0x318 - 0x31f */
+{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 },
+
+/* 0x320 - 0x327 */
+{L_MODRM ,O_M_Rd_CRx ,S_Ed ,0 },{L_MODRM ,O_M_Rd_DRx ,S_Ed ,0 },
+{L_MODRM ,O_M_CRx_Rd ,0 ,M_Ed },{L_MODRM ,O_M_DRx_Rd ,0 ,M_Ed },
+{L_MODRM ,O_M_Rd_TRx ,S_Ed ,0 },{0 ,0 ,0 ,0 },
+{L_MODRM ,O_M_TRx_Rd ,0 ,M_Ed },{0 ,0 ,0 ,0 },
+
+/* 0x328 - 0x32f */
+{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 },
+
+/* 0x330 - 0x337 */
+{0 ,0 ,0 ,0 },{D_RDTSC ,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 },
+/* 0x338 - 0x33f */
+{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 },
+
+/* 0x340 - 0x347 */
+{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 },
+/* 0x348 - 0x34f */
+{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 },
+
+/* 0x350 - 0x357 */
+{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 },
+/* 0x358 - 0x35f */
+{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 },
+
+/* 0x360 - 0x367 */
+{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 },
+/* 0x368 - 0x36f */
+{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 },
+
+
+/* 0x370 - 0x377 */
+{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 },
+/* 0x378 - 0x37f */
+{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 },
+
+/* 0x380 - 0x387 */
+{L_Idx ,O_C_O ,S_C_AIPd,0 },{L_Idx ,O_C_NO ,S_C_AIPd,0 },
+{L_Idx ,O_C_B ,S_C_AIPd,0 },{L_Idx ,O_C_NB ,S_C_AIPd,0 },
+{L_Idx ,O_C_Z ,S_C_AIPd,0 },{L_Idx ,O_C_NZ ,S_C_AIPd,0 },
+{L_Idx ,O_C_BE ,S_C_AIPd,0 },{L_Idx ,O_C_NBE ,S_C_AIPd,0 },
+/* 0x388 - 0x38f */
+{L_Idx ,O_C_S ,S_C_AIPd,0 },{L_Idx ,O_C_NS ,S_C_AIPd,0 },
+{L_Idx ,O_C_P ,S_C_AIPd,0 },{L_Idx ,O_C_NP ,S_C_AIPd,0 },
+{L_Idx ,O_C_L ,S_C_AIPd,0 },{L_Idx ,O_C_NL ,S_C_AIPd,0 },
+{L_Idx ,O_C_LE ,S_C_AIPd,0 },{L_Idx ,O_C_NLE ,S_C_AIPd,0 },
+
+/* 0x390 - 0x397 */
+{L_MODRM ,O_C_O ,S_C_Eb,0 },{L_MODRM ,O_C_NO ,S_C_Eb,0 },
+{L_MODRM ,O_C_B ,S_C_Eb,0 },{L_MODRM ,O_C_NB ,S_C_Eb,0 },
+{L_MODRM ,O_C_Z ,S_C_Eb,0 },{L_MODRM ,O_C_NZ ,S_C_Eb,0 },
+{L_MODRM ,O_C_BE ,S_C_Eb,0 },{L_MODRM ,O_C_NBE ,S_C_Eb,0 },
+/* 0x398 - 0x39f */
+{L_MODRM ,O_C_S ,S_C_Eb,0 },{L_MODRM ,O_C_NS ,S_C_Eb,0 },
+{L_MODRM ,O_C_P ,S_C_Eb,0 },{L_MODRM ,O_C_NP ,S_C_Eb,0 },
+{L_MODRM ,O_C_L ,S_C_Eb,0 },{L_MODRM ,O_C_NL ,S_C_Eb,0 },
+{L_MODRM ,O_C_LE ,S_C_Eb,0 },{L_MODRM ,O_C_NLE ,S_C_Eb,0 },
+
+/* 0x3a0 - 0x3a7 */
+{L_SEG ,0 ,S_PUSHd ,fs },{D_POPSEGd,0 ,0 ,fs },
+{D_CPUID ,0 ,0 ,0 },{L_MODRM ,O_BTd ,S_Ed ,M_EdGdt },
+{L_MODRM ,O_DSHLd ,S_Ed,M_EdGdIb },{L_MODRM ,O_DSHLd ,S_Ed ,M_EdGdCL },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+/* 0x3a8 - 0x3af */
+{L_SEG ,0 ,S_PUSHd ,gs },{D_POPSEGd,0 ,0 ,gs },
+{0 ,0 ,0 ,0 },{L_MODRM ,O_BTSd ,S_Ed ,M_EdGdt },
+{L_MODRM ,O_DSHRd ,S_Ed,M_EdGdIb },{L_MODRM ,O_DSHRd ,S_Ed ,M_EdGdCL },
+{0 ,0 ,0 ,0 },{L_MODRM ,O_IMULRd ,S_Gd ,M_EdxGdx },
+
+/* 0x3b0 - 0x3b7 */
+{0 ,0 ,0 ,0 },{L_MODRM ,O_CMPXCHG ,S_Ed ,M_Ed },
+{L_MODRM ,O_SEGSS ,S_SEGGd,M_Efd },{L_MODRM ,O_BTRd ,S_Ed ,M_EdGdt },
+{L_MODRM ,O_SEGFS ,S_SEGGd,M_Efd },{L_MODRM ,O_SEGGS ,S_SEGGd,M_Efd },
+{L_MODRM ,0 ,S_Gd ,M_Eb },{L_MODRM ,0 ,S_Gd ,M_Ew },
+/* 0x3b8 - 0x3bf */
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{L_MODRM ,0xf ,0 ,M_GRP },{L_MODRM ,O_BTCd ,S_Ed ,M_EdGdt },
+{L_MODRM ,O_BSFd ,S_Gd ,M_Ed },{L_MODRM ,O_BSRd ,S_Gd ,M_Ed },
+{L_MODRM ,0 ,S_Gd ,M_Ebx },{L_MODRM ,0 ,S_Gd ,M_Ewx },
+
+/* 0x3c0 - 0x3cc */
+{L_MODRM ,t_ADDb ,S_EbGb ,M_GbEb },{L_MODRM ,t_ADDd ,S_EdGd ,M_GdEd },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+/* 0x3c8 - 0x3cf */
+{L_REGd ,O_BSWAPd ,S_REGd ,REGI_AX},{L_REGd ,O_BSWAPd ,S_REGd ,REGI_CX},
+{L_REGd ,O_BSWAPd ,S_REGd ,REGI_DX},{L_REGd ,O_BSWAPd ,S_REGd ,REGI_BX},
+{L_REGd ,O_BSWAPd ,S_REGd ,REGI_SP},{L_REGd ,O_BSWAPd ,S_REGd ,REGI_BP},
+{L_REGd ,O_BSWAPd ,S_REGd ,REGI_SI},{L_REGd ,O_BSWAPd ,S_REGd ,REGI_DI},
+
+/* 0x3d0 - 0x3d7 */
+{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 },
+/* 0x3d8 - 0x3df */
+{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 },
+
+/* 0x3e0 - 0x3ee */
+{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 },
+/* 0x3e8 - 0x3ef */
+{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 },
+
+/* 0x3f0 - 0x3fc */
+{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 },
+/* 0x3f8 - 0x3ff */
+{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 },
+
+};
+
+static OpCode Groups[16][8]={
+{ /* 0x00 Group 1 Eb,Ib */
+{0 ,t_ADDb ,S_Eb ,M_EbIb },{0 ,t_ORb ,S_Eb ,M_EbIb },
+{0 ,t_ADCb ,S_Eb ,M_EbIb },{0 ,t_SBBb ,S_Eb ,M_EbIb },
+{0 ,t_ANDb ,S_Eb ,M_EbIb },{0 ,t_SUBb ,S_Eb ,M_EbIb },
+{0 ,t_XORb ,S_Eb ,M_EbIb },{0 ,t_CMPb ,0 ,M_EbIb },
+},{ /* 0x01 Group 1 Ew,Iw */
+{0 ,t_ADDw ,S_Ew ,M_EwIw },{0 ,t_ORw ,S_Ew ,M_EwIw },
+{0 ,t_ADCw ,S_Ew ,M_EwIw },{0 ,t_SBBw ,S_Ew ,M_EwIw },
+{0 ,t_ANDw ,S_Ew ,M_EwIw },{0 ,t_SUBw ,S_Ew ,M_EwIw },
+{0 ,t_XORw ,S_Ew ,M_EwIw },{0 ,t_CMPw ,0 ,M_EwIw },
+},{ /* 0x02 Group 1 Ed,Id */
+{0 ,t_ADDd ,S_Ed ,M_EdId },{0 ,t_ORd ,S_Ed ,M_EdId },
+{0 ,t_ADCd ,S_Ed ,M_EdId },{0 ,t_SBBd ,S_Ed ,M_EdId },
+{0 ,t_ANDd ,S_Ed ,M_EdId },{0 ,t_SUBd ,S_Ed ,M_EdId },
+{0 ,t_XORd ,S_Ed ,M_EdId },{0 ,t_CMPd ,0 ,M_EdId },
+},{ /* 0x03 Group 1 Ew,Ibx */
+{0 ,t_ADDw ,S_Ew ,M_EwIbx },{0 ,t_ORw ,S_Ew ,M_EwIbx },
+{0 ,t_ADCw ,S_Ew ,M_EwIbx },{0 ,t_SBBw ,S_Ew ,M_EwIbx },
+{0 ,t_ANDw ,S_Ew ,M_EwIbx },{0 ,t_SUBw ,S_Ew ,M_EwIbx },
+{0 ,t_XORw ,S_Ew ,M_EwIbx },{0 ,t_CMPw ,0 ,M_EwIbx },
+},{ /* 0x04 Group 1 Ed,Ibx */
+{0 ,t_ADDd ,S_Ed ,M_EdIbx },{0 ,t_ORd ,S_Ed ,M_EdIbx },
+{0 ,t_ADCd ,S_Ed ,M_EdIbx },{0 ,t_SBBd ,S_Ed ,M_EdIbx },
+{0 ,t_ANDd ,S_Ed ,M_EdIbx },{0 ,t_SUBd ,S_Ed ,M_EdIbx },
+{0 ,t_XORd ,S_Ed ,M_EdIbx },{0 ,t_CMPd ,0 ,M_EdIbx },
+
+},{ /* 0x05 Group 2 Eb,XXX */
+{0 ,t_ROLb ,S_Eb ,M_Eb },{0 ,t_RORb ,S_Eb ,M_Eb },
+{0 ,t_RCLb ,S_Eb ,M_Eb },{0 ,t_RCRb ,S_Eb ,M_Eb },
+{0 ,t_SHLb ,S_Eb ,M_Eb },{0 ,t_SHRb ,S_Eb ,M_Eb },
+{0 ,t_SHLb ,S_Eb ,M_Eb },{0 ,t_SARb ,S_Eb ,M_Eb },
+},{ /* 0x06 Group 2 Ew,XXX */
+{0 ,t_ROLw ,S_Ew ,M_Ew },{0 ,t_RORw ,S_Ew ,M_Ew },
+{0 ,t_RCLw ,S_Ew ,M_Ew },{0 ,t_RCRw ,S_Ew ,M_Ew },
+{0 ,t_SHLw ,S_Ew ,M_Ew },{0 ,t_SHRw ,S_Ew ,M_Ew },
+{0 ,t_SHLw ,S_Ew ,M_Ew },{0 ,t_SARw ,S_Ew ,M_Ew },
+},{ /* 0x07 Group 2 Ed,XXX */
+{0 ,t_ROLd ,S_Ed ,M_Ed },{0 ,t_RORd ,S_Ed ,M_Ed },
+{0 ,t_RCLd ,S_Ed ,M_Ed },{0 ,t_RCRd ,S_Ed ,M_Ed },
+{0 ,t_SHLd ,S_Ed ,M_Ed },{0 ,t_SHRd ,S_Ed ,M_Ed },
+{0 ,t_SHLd ,S_Ed ,M_Ed },{0 ,t_SARd ,S_Ed ,M_Ed },
+
+
+},{ /* 0x08 Group 3 Eb */
+{0 ,t_TESTb ,0 ,M_EbIb },{0 ,t_TESTb ,0 ,M_EbIb },
+{0 ,O_NOT ,S_Eb ,M_Eb },{0 ,t_NEGb ,S_Eb ,M_Eb },
+{0 ,O_MULb ,0 ,M_Eb },{0 ,O_IMULb ,0 ,M_Eb },
+{0 ,O_DIVb ,0 ,M_Eb },{0 ,O_IDIVb ,0 ,M_Eb },
+},{ /* 0x09 Group 3 Ew */
+{0 ,t_TESTw ,0 ,M_EwIw },{0 ,t_TESTw ,0 ,M_EwIw },
+{0 ,O_NOT ,S_Ew ,M_Ew },{0 ,t_NEGw ,S_Ew ,M_Ew },
+{0 ,O_MULw ,0 ,M_Ew },{0 ,O_IMULw ,0 ,M_Ew },
+{0 ,O_DIVw ,0 ,M_Ew },{0 ,O_IDIVw ,0 ,M_Ew },
+},{ /* 0x0a Group 3 Ed */
+{0 ,t_TESTd ,0 ,M_EdId },{0 ,t_TESTd ,0 ,M_EdId },
+{0 ,O_NOT ,S_Ed ,M_Ed },{0 ,t_NEGd ,S_Ed ,M_Ed },
+{0 ,O_MULd ,0 ,M_Ed },{0 ,O_IMULd ,0 ,M_Ed },
+{0 ,O_DIVd ,0 ,M_Ed },{0 ,O_IDIVd ,0 ,M_Ed },
+
+},{ /* 0x0b Group 4 Eb */
+{0 ,t_INCb ,S_Eb ,M_Eb },{0 ,t_DECb ,S_Eb ,M_Eb },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{0 ,0 ,0 ,0 },{0 ,O_CBACK ,0 ,M_Iw },
+},{ /* 0x0c Group 5 Ew */
+{0 ,t_INCw ,S_Ew ,M_Ew },{0 ,t_DECw ,S_Ew ,M_Ew },
+{0 ,O_CALLNw ,S_IP ,M_Ew },{0 ,O_CALLFw ,0 ,M_Efw },
+{0 ,0 ,S_IP ,M_Ew },{0 ,O_JMPFw ,0 ,M_Efw },
+{0 ,0 ,S_PUSHw,M_Ew },{0 ,0 ,0 ,0 },
+},{ /* 0x0d Group 5 Ed */
+{0 ,t_INCd ,S_Ed ,M_Ed },{0 ,t_DECd ,S_Ed ,M_Ed },
+{0 ,O_CALLNd ,S_IP ,M_Ed },{0 ,O_CALLFd ,0 ,M_Efd },
+{0 ,0 ,S_IP ,M_Ed },{0 ,O_JMPFd ,0 ,M_Efd },
+{0 ,0 ,S_PUSHd,M_Ed },{0 ,0 ,0 ,0 },
+
+
+},{ /* 0x0e Group 8 Ew */
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{0 ,O_BTw ,S_Ew ,M_EwIb },{0 ,O_BTSw ,S_Ew ,M_EwIb },
+{0 ,O_BTRw ,S_Ew ,M_EwIb },{0 ,O_BTCw ,S_Ew ,M_EwIb },
+},{ /* 0x0f Group 8 Ed */
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },
+{0 ,O_BTd ,S_Ed ,M_EdIb },{0 ,O_BTSd ,S_Ed ,M_EdIb },
+{0 ,O_BTRd ,S_Ed ,M_EdIb },{0 ,O_BTCd ,S_Ed ,M_EdIb },
+
+
+
+}
+};
+
+
+
diff --git a/src/cpu/core_full/save.h b/src/cpu/core_full/save.h
new file mode 100644
index 000000000..c494f4ba4
--- /dev/null
+++ b/src/cpu/core_full/save.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+/* Write the data from the opcode */
+switch (inst.code.save) {
+/* Byte */
+ case S_C_Eb:
+ inst_op1_b=inst.cond ? 1 : 0;
+ case S_Eb:
+ if (inst.rm<0xc0) SaveMb(inst.rm_eaa,inst_op1_b);
+ else reg_8(inst.rm_eai)=inst_op1_b;
+ break;
+ case S_Gb:
+ reg_8(inst.rm_index)=inst_op1_b;
+ break;
+ case S_EbGb:
+ if (inst.rm<0xc0) SaveMb(inst.rm_eaa,inst_op1_b);
+ else reg_8(inst.rm_eai)=inst_op1_b;
+ reg_8(inst.rm_index)=inst_op2_b;
+ break;
+/* Word */
+ case S_Ew:
+ if (inst.rm<0xc0) SaveMw(inst.rm_eaa,inst_op1_w);
+ else reg_16(inst.rm_eai)=inst_op1_w;
+ break;
+ case S_Gw:
+ reg_16(inst.rm_index)=inst_op1_w;
+ break;
+ case S_EwGw:
+ if (inst.rm<0xc0) SaveMw(inst.rm_eaa,inst_op1_w);
+ else reg_16(inst.rm_eai)=inst_op1_w;
+ reg_16(inst.rm_index)=inst_op2_w;
+ break;
+/* Dword */
+ case S_Ed:
+ if (inst.rm<0xc0) SaveMd(inst.rm_eaa,inst_op1_d);
+ else reg_32(inst.rm_eai)=inst_op1_d;
+ break;
+ case S_EdMw: /* Special one 16 to memory, 32 zero extend to reg */
+ if (inst.rm<0xc0) SaveMw(inst.rm_eaa,inst_op1_w);
+ else reg_32(inst.rm_eai)=inst_op1_d;
+ break;
+ case S_Gd:
+ reg_32(inst.rm_index)=inst_op1_d;
+ break;
+ case S_EdGd:
+ if (inst.rm<0xc0) SaveMd(inst.rm_eaa,inst_op1_d);
+ else reg_32(inst.rm_eai)=inst_op1_d;
+ reg_32(inst.rm_index)=inst_op2_d;
+ break;
+
+ case S_REGb:
+ reg_8(inst.code.extra)=inst_op1_b;
+ break;
+ case S_REGw:
+ reg_16(inst.code.extra)=inst_op1_w;
+ break;
+ case S_REGd:
+ reg_32(inst.code.extra)=inst_op1_d;
+ break;
+ case S_SEGm:
+ if (CPU_SetSegGeneral((SegNames)inst.rm_index,inst_op1_w)) RunException();
+ break;
+ case S_SEGGw:
+ if (CPU_SetSegGeneral((SegNames)inst.code.extra,inst_op2_w)) RunException();
+ reg_16(inst.rm_index)=inst_op1_w;
+ break;
+ case S_SEGGd:
+ if (CPU_SetSegGeneral((SegNames)inst.code.extra,inst_op2_w)) RunException();
+ reg_32(inst.rm_index)=inst_op1_d;
+ break;
+ case S_PUSHw:
+ Push_16(inst_op1_w);
+ break;
+ case S_PUSHd:
+ Push_32(inst_op1_d);
+ break;
+
+ case S_C_AIPw:
+ if (!inst.cond) goto nextopcode;
+ case S_AIPw:
+ SaveIP();
+ reg_eip+=inst_op1_d;
+ reg_eip&=0xffff;
+ continue;
+ case S_C_AIPd:
+ if (!inst.cond) goto nextopcode;
+ case S_AIPd:
+ SaveIP();
+ reg_eip+=inst_op1_d;
+ continue;
+ case S_IPIw:
+ reg_esp+=Fetchw();
+ case S_IP:
+ SaveIP();
+ reg_eip=inst_op1_d;
+ continue;
+ case 0:
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("SAVE:Unhandled code %d entry %X",inst.code.save,inst.entry);
+}
diff --git a/src/cpu/core_full/string.h b/src/cpu/core_full/string.h
new file mode 100644
index 000000000..943c7f753
--- /dev/null
+++ b/src/cpu/core_full/string.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+{
+ EAPoint si_base,di_base;
+ Bitu si_index,di_index;
+ Bitu add_mask;
+ Bitu count,count_left;
+ Bits add_index;
+
+ if (inst.prefix & PREFIX_SEG) si_base=inst.seg.base;
+ else si_base=SegBase(ds);
+ di_base=SegBase(es);
+ if (inst.prefix & PREFIX_ADDR) {
+ add_mask=0xFFFFFFFF;
+ si_index=reg_esi;
+ di_index=reg_edi;
+ count=reg_ecx;
+ } else {
+ add_mask=0xFFFF;
+ si_index=reg_si;
+ di_index=reg_di;
+ count=reg_cx;
+ }
+ if (!(inst.prefix & PREFIX_REP)) {
+ count=1;
+ } else {
+ /* Calculate amount of ops to do before cycles run out */
+ CPU_Cycles++;
+ if ((count>(Bitu)CPU_Cycles) && (inst.code.op<R_SCASB)) {
+ count_left=count-CPU_Cycles;
+ count=CPU_Cycles;
+ CPU_Cycles=0;
+ LoadIP();
+ } else {
+ /* Won't interrupt scas and cmps instruction since they can interrupt themselves */
+ if (inst.code.op<R_SCASB) CPU_Cycles-=count;
+ count_left=0;
+ }
+ }
+ add_index=cpu.direction;
+ if (count) switch (inst.code.op) {
+ case R_OUTSB:
+ for (;count>0;count--) {
+ IO_WriteB(reg_dx,LoadMb(si_base+si_index));
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_OUTSW:
+ add_index<<=1;
+ for (;count>0;count--) {
+ IO_WriteW(reg_dx,LoadMw(si_base+si_index));
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_OUTSD:
+ add_index<<=2;
+ for (;count>0;count--) {
+ IO_WriteD(reg_dx,LoadMd(si_base+si_index));
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_INSB:
+ for (;count>0;count--) {
+ SaveMb(di_base+di_index,IO_ReadB(reg_dx));
+ di_index=(di_index+add_index) & add_mask;
+ }
+ break;
+ case R_INSW:
+ add_index<<=1;
+ for (;count>0;count--) {
+ SaveMw(di_base+di_index,IO_ReadW(reg_dx));
+ di_index=(di_index+add_index) & add_mask;
+ }
+ break;
+ case R_INSD:
+ add_index<<=2;
+ for (;count>0;count--) {
+ SaveMd(di_base+di_index,IO_ReadD(reg_dx));
+ di_index=(di_index+add_index) & add_mask;
+ }
+ break;
+ case R_STOSB:
+ for (;count>0;count--) {
+ SaveMb(di_base+di_index,reg_al);
+ di_index=(di_index+add_index) & add_mask;
+ }
+ break;
+ case R_STOSW:
+ add_index<<=1;
+ for (;count>0;count--) {
+ SaveMw(di_base+di_index,reg_ax);
+ di_index=(di_index+add_index) & add_mask;
+ }
+ break;
+ case R_STOSD:
+ add_index<<=2;
+ for (;count>0;count--) {
+ SaveMd(di_base+di_index,reg_eax);
+ di_index=(di_index+add_index) & add_mask;
+ }
+ break;
+ case R_MOVSB:
+ for (;count>0;count--) {
+ SaveMb(di_base+di_index,LoadMb(si_base+si_index));
+ di_index=(di_index+add_index) & add_mask;
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_MOVSW:
+ add_index<<=1;
+ for (;count>0;count--) {
+ SaveMw(di_base+di_index,LoadMw(si_base+si_index));
+ di_index=(di_index+add_index) & add_mask;
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_MOVSD:
+ add_index<<=2;
+ for (;count>0;count--) {
+ SaveMd(di_base+di_index,LoadMd(si_base+si_index));
+ di_index=(di_index+add_index) & add_mask;
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_LODSB:
+ for (;count>0;count--) {
+ reg_al=LoadMb(si_base+si_index);
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_LODSW:
+ add_index<<=1;
+ for (;count>0;count--) {
+ reg_ax=LoadMw(si_base+si_index);
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_LODSD:
+ add_index<<=2;
+ for (;count>0;count--) {
+ reg_eax=LoadMd(si_base+si_index);
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_SCASB:
+ {
+ Bit8u val2;
+ for (;count>0;) {
+ count--;CPU_Cycles--;
+ val2=LoadMb(di_base+di_index);
+ di_index=(di_index+add_index) & add_mask;
+ if ((reg_al==val2)!=inst.repz) break;
+ }
+ CMPB(reg_al,val2,LoadD,0);
+ }
+ break;
+ case R_SCASW:
+ {
+ add_index<<=1;Bit16u val2;
+ for (;count>0;) {
+ count--;CPU_Cycles--;
+ val2=LoadMw(di_base+di_index);
+ di_index=(di_index+add_index) & add_mask;
+ if ((reg_ax==val2)!=inst.repz) break;
+ }
+ CMPW(reg_ax,val2,LoadD,0);
+ }
+ break;
+ case R_SCASD:
+ {
+ add_index<<=2;Bit32u val2;
+ for (;count>0;) {
+ count--;CPU_Cycles--;
+ val2=LoadMd(di_base+di_index);
+ di_index=(di_index+add_index) & add_mask;
+ if ((reg_eax==val2)!=inst.repz) break;
+ }
+ CMPD(reg_eax,val2,LoadD,0);
+ }
+ break;
+ case R_CMPSB:
+ {
+ Bit8u val1,val2;
+ for (;count>0;) {
+ count--;CPU_Cycles--;
+ val1=LoadMb(si_base+si_index);
+ val2=LoadMb(di_base+di_index);
+ si_index=(si_index+add_index) & add_mask;
+ di_index=(di_index+add_index) & add_mask;
+ if ((val1==val2)!=inst.repz) break;
+ }
+ CMPB(val1,val2,LoadD,0);
+ }
+ break;
+ case R_CMPSW:
+ {
+ add_index<<=1;Bit16u val1,val2;
+ for (;count>0;) {
+ count--;CPU_Cycles--;
+ val1=LoadMw(si_base+si_index);
+ val2=LoadMw(di_base+di_index);
+ si_index=(si_index+add_index) & add_mask;
+ di_index=(di_index+add_index) & add_mask;
+ if ((val1==val2)!=inst.repz) break;
+ }
+ CMPW(val1,val2,LoadD,0);
+ }
+ break;
+ case R_CMPSD:
+ {
+ add_index<<=2;Bit32u val1,val2;
+ for (;count>0;) {
+ count--;CPU_Cycles--;
+ val1=LoadMd(si_base+si_index);
+ val2=LoadMd(di_base+di_index);
+ si_index=(si_index+add_index) & add_mask;
+ di_index=(di_index+add_index) & add_mask;
+ if ((val1==val2)!=inst.repz) break;
+ }
+ CMPD(val1,val2,LoadD,0);
+ }
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Unhandled string %d entry %X",inst.code.op,inst.entry);
+ }
+ /* Clean up after certain amount of instructions */
+ reg_esi&=(~add_mask);
+ reg_esi|=(si_index & add_mask);
+ reg_edi&=(~add_mask);
+ reg_edi|=(di_index & add_mask);
+ if (inst.prefix & PREFIX_REP) {
+ count+=count_left;
+ reg_ecx&=(~add_mask);
+ reg_ecx|=(count & add_mask);
+ }
+}
diff --git a/src/cpu/core_full/support.h b/src/cpu/core_full/support.h
new file mode 100644
index 000000000..f8d8b332f
--- /dev/null
+++ b/src/cpu/core_full/support.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+enum {
+ L_N=0,
+ L_SKIP,
+ /* Grouped ones using MOD/RM */
+ L_MODRM,L_MODRM_NVM,L_POPwRM,L_POPdRM,
+
+ L_Ib,L_Iw,L_Id,
+ L_Ibx,L_Iwx,L_Idx, //Sign extend
+ L_Ifw,L_Ifd,
+ L_OP,
+
+ L_REGb,L_REGw,L_REGd,
+ L_REGbIb,L_REGwIw,L_REGdId,
+ L_POPw,L_POPd,
+ L_POPfw,L_POPfd,
+ L_SEG,
+
+ L_INTO,
+
+ L_VAL,
+ L_PRESEG,
+ L_DOUBLE,
+ L_PREOP,L_PREADD,L_PREREP,L_PREREPNE,
+ L_STRING,
+
+/* Direct ones */
+ D_IRETw,D_IRETd,
+ D_PUSHAw,D_PUSHAd,
+ D_POPAw,D_POPAd,
+ D_POPSEGw,D_POPSEGd,
+ D_DAA,D_DAS,
+ D_AAA,D_AAS,
+ D_CBW,D_CWDE,
+ D_CWD,D_CDQ,
+ D_SETALC,
+ D_XLAT,
+ D_CLI,D_STI,D_STC,D_CLC,D_CMC,D_CLD,D_STD,
+ D_NOP,D_WAIT,
+ D_ENTERw,D_ENTERd,
+ D_LEAVEw,D_LEAVEd,
+
+ D_RETFw,D_RETFd,
+ D_RETFwIw,D_RETFdIw,
+ D_POPF,D_PUSHF,
+ D_SAHF,D_LAHF,
+ D_CPUID,
+ D_HLT,D_CLTS,
+ D_LOCK,D_ICEBP,
+ D_RDTSC,
+ L_ERROR
+};
+
+
+enum {
+ O_N=t_LASTFLAG,
+ O_COND,
+ O_XCHG_AX,O_XCHG_EAX,
+ O_IMULRw,O_IMULRd,
+ O_BOUNDw,O_BOUNDd,
+ O_CALLNw,O_CALLNd,
+ O_CALLFw,O_CALLFd,
+ O_JMPFw,O_JMPFd,
+
+ O_OPAL,O_ALOP,
+ O_OPAX,O_AXOP,
+ O_OPEAX,O_EAXOP,
+ O_INT,
+ O_SEGDS,O_SEGES,O_SEGFS,O_SEGGS,O_SEGSS,
+ O_LOOP,O_LOOPZ,O_LOOPNZ,O_JCXZ,
+ O_INb,O_INw,O_INd,
+ O_OUTb,O_OUTw,O_OUTd,
+
+ O_NOT,O_AAM,O_AAD,
+ O_MULb,O_MULw,O_MULd,
+ O_IMULb,O_IMULw,O_IMULd,
+ O_DIVb,O_DIVw,O_DIVd,
+ O_IDIVb,O_IDIVw,O_IDIVd,
+ O_CBACK,
+
+
+ O_DSHLw,O_DSHLd,
+ O_DSHRw,O_DSHRd,
+ O_C_O ,O_C_NO ,O_C_B ,O_C_NB ,O_C_Z ,O_C_NZ ,O_C_BE ,O_C_NBE,
+ O_C_S ,O_C_NS ,O_C_P ,O_C_NP ,O_C_L ,O_C_NL ,O_C_LE ,O_C_NLE,
+
+ O_GRP6w,O_GRP6d,
+ O_GRP7w,O_GRP7d,
+ O_M_CRx_Rd,O_M_Rd_CRx,
+ O_M_DRx_Rd,O_M_Rd_DRx,
+ O_M_TRx_Rd,O_M_Rd_TRx,
+ O_LAR,O_LSL,
+ O_ARPL,
+
+ O_BTw,O_BTSw,O_BTRw,O_BTCw,
+ O_BTd,O_BTSd,O_BTRd,O_BTCd,
+ O_BSFw,O_BSRw,O_BSFd,O_BSRd,
+
+ O_BSWAPw, O_BSWAPd,
+ O_CMPXCHG,
+ O_FPU
+
+
+};
+
+enum {
+ S_N=0,
+ S_C_Eb,
+ S_Eb,S_Gb,S_EbGb,
+ S_Ew,S_Gw,S_EwGw,
+ S_Ed,S_Gd,S_EdGd,S_EdMw,
+
+
+ S_REGb,S_REGw,S_REGd,
+ S_PUSHw,S_PUSHd,
+ S_SEGm,
+ S_SEGGw,S_SEGGd,
+
+
+ S_AIPw,S_C_AIPw,
+ S_AIPd,S_C_AIPd,
+
+ S_IP,S_IPIw
+};
+
+enum {
+ R_OUTSB,R_OUTSW,R_OUTSD,
+ R_INSB,R_INSW,R_INSD,
+ R_MOVSB,R_MOVSW,R_MOVSD,
+ R_LODSB,R_LODSW,R_LODSD,
+ R_STOSB,R_STOSW,R_STOSD,
+ R_SCASB,R_SCASW,R_SCASD,
+ R_CMPSB,R_CMPSW,R_CMPSD
+};
+
+enum {
+ M_None=0,
+ M_Ebx,M_Eb,M_Gb,M_EbGb,M_GbEb,
+ M_Ewx,M_Ew,M_Gw,M_EwGw,M_GwEw,M_EwxGwx,M_EwGwt,
+ M_Edx,M_Ed,M_Gd,M_EdGd,M_GdEd,M_EdxGdx,M_EdGdt,
+
+ M_EbIb,M_EwIb,M_EdIb,
+ M_EwIw,M_EwIbx,M_EwxIbx,M_EwxIwx,M_EwGwIb,M_EwGwCL,
+ M_EdId,M_EdIbx,M_EdGdIb,M_EdGdCL,
+
+ M_Efw,M_Efd,
+
+ M_Ib,M_Iw,M_Id,
+
+
+ M_SEG,M_EA,
+ M_GRP,
+ //Special shift groups
+ M_SHIFT_1, M_SHIFT_Ib,M_SHIFT_CL,
+
+ M_POPw,M_POPd
+};
+
+struct OpCode {
+ Bit8u load,op,save,extra;
+};
+
+struct FullData {
+ Bitu entry;
+ Bitu rm;
+ EAPoint rm_eaa;
+ Bitu rm_off;
+ Bitu rm_eai;
+ Bitu rm_index;
+ Bitu rm_mod;
+ OpCode code;
+ EAPoint cseip;
+#ifdef WORDS_BIGENDIAN
+ union {
+ Bit32u dword[1];
+ Bit32s dwords[1];
+ Bit16u word[2];
+ Bit16s words[2];
+ Bit8u byte[4];
+ Bit8s bytes[4];
+ } blah1,blah2,blah_imm;
+#else
+ union {
+ Bit8u b;Bit8s bs;
+ Bit16u w;Bit16s ws;
+ Bit32u d;Bit32s ds;
+ } op1,op2,imm;
+#endif
+ Bitu new_flags;
+ struct {
+ EAPoint base;
+ } seg;
+ Bitu cond;
+ bool repz;
+ Bitu prefix;
+};
+
+/* Some defines to get the names correct. */
+#ifdef WORDS_BIGENDIAN
+
+#define inst_op1_b inst.blah1.byte[3]
+#define inst_op1_bs inst.blah1.bytes[3]
+#define inst_op1_w inst.blah1.word[1]
+#define inst_op1_ws inst.blah1.words[1]
+#define inst_op1_d inst.blah1.dword[0]
+#define inst_op1_ds inst.blah1.dwords[0]
+
+#define inst_op2_b inst.blah2.byte[3]
+#define inst_op2_bs inst.blah2.bytes[3]
+#define inst_op2_w inst.blah2.word[1]
+#define inst_op2_ws inst.blah2.words[1]
+#define inst_op2_d inst.blah2.dword[0]
+#define inst_op2_ds inst.blah2.dwords[0]
+
+#define inst_imm_b inst.blah_imm.byte[3]
+#define inst_imm_bs inst.blah_imm.bytes[3]
+#define inst_imm_w inst.blah_imm.word[1]
+#define inst_imm_ws inst.blah_imm.words[1]
+#define inst_imm_d inst.blah_imm.dword[0]
+#define inst_imm_ds inst.blah_imm.dwords[0]
+
+#else
+
+#define inst_op1_b inst.op1.b
+#define inst_op1_bs inst.op1.bs
+#define inst_op1_w inst.op1.w
+#define inst_op1_ws inst.op1.ws
+#define inst_op1_d inst.op1.d
+#define inst_op1_ds inst.op1.ds
+
+#define inst_op2_b inst.op2.b
+#define inst_op2_bs inst.op2.bs
+#define inst_op2_w inst.op2.w
+#define inst_op2_ws inst.op2.ws
+#define inst_op2_d inst.op2.d
+#define inst_op2_ds inst.op2.ds
+
+#define inst_imm_b inst.imm.b
+#define inst_imm_bs inst.imm.bs
+#define inst_imm_w inst.imm.w
+#define inst_imm_ws inst.imm.ws
+#define inst_imm_d inst.imm.d
+#define inst_imm_ds inst.imm.ds
+
+#endif
+
+
+#define PREFIX_NONE 0x0
+#define PREFIX_ADDR 0x1
+#define PREFIX_SEG 0x2
+#define PREFIX_REP 0x4
+
diff --git a/src/cpu/core_normal.cpp b/src/cpu/core_normal.cpp
new file mode 100644
index 000000000..0d9cfc398
--- /dev/null
+++ b/src/cpu/core_normal.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+
+#include "dosbox.h"
+#include "mem.h"
+#include "cpu.h"
+#include "lazyflags.h"
+#include "inout.h"
+#include "callback.h"
+#include "pic.h"
+#include "fpu.h"
+#include "paging.h"
+
+#if C_DEBUG
+#include "debug.h"
+#endif
+
+#if (!C_CORE_INLINE)
+#define LoadMb(off) mem_readb(off)
+#define LoadMw(off) mem_readw(off)
+#define LoadMd(off) mem_readd(off)
+#define SaveMb(off,val) mem_writeb(off,val)
+#define SaveMw(off,val) mem_writew(off,val)
+#define SaveMd(off,val) mem_writed(off,val)
+#else
+#include "paging.h"
+#define LoadMb(off) mem_readb_inline(off)
+#define LoadMw(off) mem_readw_inline(off)
+#define LoadMd(off) mem_readd_inline(off)
+#define SaveMb(off,val) mem_writeb_inline(off,val)
+#define SaveMw(off,val) mem_writew_inline(off,val)
+#define SaveMd(off,val) mem_writed_inline(off,val)
+#endif
+
+extern Bitu cycle_count;
+
+#if C_FPU
+#define CPU_FPU 1 //Enable FPU escape instructions
+#endif
+
+#define CPU_PIC_CHECK 1
+#define CPU_TRAP_CHECK 1
+
+#define CPU_TRAP_DECODER CPU_Core_Normal_Trap_Run
+
+#define OPCODE_NONE 0x000
+#define OPCODE_0F 0x100
+#define OPCODE_SIZE 0x200
+
+#define PREFIX_ADDR 0x1
+#define PREFIX_REP 0x2
+
+#define TEST_PREFIX_ADDR (core.prefixes & PREFIX_ADDR)
+#define TEST_PREFIX_REP (core.prefixes & PREFIX_REP)
+
+#define DO_PREFIX_SEG(_SEG) \
+ BaseDS=SegBase(_SEG); \
+ BaseSS=SegBase(_SEG); \
+ core.base_val_ds=_SEG; \
+ goto restart_opcode;
+
+#define DO_PREFIX_ADDR() \
+ core.prefixes=(core.prefixes & ~PREFIX_ADDR) | \
+ (cpu.code.big ^ PREFIX_ADDR); \
+ core.ea_table=&EATable[(core.prefixes&1) * 256]; \
+ goto restart_opcode;
+
+#define DO_PREFIX_REP(_ZERO) \
+ core.prefixes|=PREFIX_REP; \
+ core.rep_zero=_ZERO; \
+ goto restart_opcode;
+
+typedef PhysPt (*GetEAHandler)(void);
+
+static const Bit32u AddrMaskTable[2]={0x0000ffff,0xffffffff};
+
+static struct {
+ Bitu opcode_index;
+ PhysPt cseip;
+ PhysPt base_ds,base_ss;
+ SegNames base_val_ds;
+ bool rep_zero;
+ Bitu prefixes;
+ GetEAHandler * ea_table;
+} core;
+
+#define GETIP (core.cseip-SegBase(cs))
+#define SAVEIP reg_eip=GETIP;
+#define LOADIP core.cseip=(SegBase(cs)+reg_eip);
+
+#define SegBase(c) SegPhys(c)
+#define BaseDS core.base_ds
+#define BaseSS core.base_ss
+
+static INLINE Bit8u Fetchb() {
+ Bit8u temp=LoadMb(core.cseip);
+ core.cseip+=1;
+ return temp;
+}
+
+static INLINE Bit16u Fetchw() {
+ Bit16u temp=LoadMw(core.cseip);
+ core.cseip+=2;
+ return temp;
+}
+static INLINE Bit32u Fetchd() {
+ Bit32u temp=LoadMd(core.cseip);
+ core.cseip+=4;
+ return temp;
+}
+
+#define Push_16 CPU_Push16
+#define Push_32 CPU_Push32
+#define Pop_16 CPU_Pop16
+#define Pop_32 CPU_Pop32
+
+#include "instructions.h"
+#include "core_normal/support.h"
+#include "core_normal/string.h"
+
+
+#define EALookupTable (core.ea_table)
+
+Bits CPU_Core_Normal_Run(void) {
+ while (CPU_Cycles-->0) {
+ LOADIP;
+ core.opcode_index=cpu.code.big*0x200;
+ core.prefixes=cpu.code.big;
+ core.ea_table=&EATable[cpu.code.big*256];
+ BaseDS=SegBase(ds);
+ BaseSS=SegBase(ss);
+ core.base_val_ds=ds;
+#if C_DEBUG
+#if C_HEAVY_DEBUG
+ if (DEBUG_HeavyIsBreakpoint()) {
+ FillFlags();
+ return debugCallback;
+ };
+#endif
+ cycle_count++;
+#endif
+restart_opcode:
+ switch (core.opcode_index+Fetchb()) {
+ #include "core_normal/prefix_none.h"
+ #include "core_normal/prefix_0f.h"
+ #include "core_normal/prefix_66.h"
+ #include "core_normal/prefix_66_0f.h"
+ default:
+ illegal_opcode:
+#if C_DEBUG
+ {
+ Bitu len=(GETIP-reg_eip);
+ LOADIP;
+ if (len>16) len=16;
+ char tempcode[16*2+1];char * writecode=tempcode;
+ for (;len>0;len--) {
+ sprintf(writecode,"%02X",mem_readb(core.cseip++));
+ writecode+=2;
+ }
+ LOG(LOG_CPU,LOG_NORMAL)("Illegal/Unhandled opcode %s",tempcode);
+ }
+#endif
+ CPU_Exception(6,0);
+ continue;
+ }
+ SAVEIP;
+ }
+ FillFlags();
+ return CBRET_NONE;
+decode_end:
+ SAVEIP;
+ FillFlags();
+ return CBRET_NONE;
+}
+
+Bits CPU_Core_Normal_Trap_Run(void) {
+ Bits oldCycles = CPU_Cycles;
+ CPU_Cycles = 1;
+ cpu.trap_skip = false;
+
+ Bits ret=CPU_Core_Normal_Run();
+ if (!cpu.trap_skip) CPU_HW_Interrupt(1);
+ CPU_Cycles = oldCycles-1;
+ cpudecoder = &CPU_Core_Normal_Run;
+
+ return ret;
+}
+
+
+
+void CPU_Core_Normal_Init(void) {
+
+}
+
diff --git a/src/cpu/core_normal/Makefile.am b/src/cpu/core_normal/Makefile.am
new file mode 100644
index 000000000..99f76f3d2
--- /dev/null
+++ b/src/cpu/core_normal/Makefile.am
@@ -0,0 +1,3 @@
+
+noinst_HEADERS = helpers.h prefix_none.h prefix_66.h prefix_0f.h support.h table_ea.h \
+ prefix_66_0f.h string.h
diff --git a/src/cpu/core_normal/helpers.h b/src/cpu/core_normal/helpers.h
new file mode 100644
index 000000000..bd4d72e57
--- /dev/null
+++ b/src/cpu/core_normal/helpers.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#define GetEAa \
+ PhysPt eaa=EALookupTable[rm]();
+
+#define GetRMEAa \
+ GetRM; \
+ GetEAa;
+
+
+#define RMEbGb(inst) \
+ { \
+ GetRMrb; \
+ if (rm >= 0xc0 ) {GetEArb;inst(*earb,*rmrb,LoadRb,SaveRb);} \
+ else {GetEAa;inst(eaa,*rmrb,LoadMb,SaveMb);} \
+ }
+
+#define RMGbEb(inst) \
+ { \
+ GetRMrb; \
+ if (rm >= 0xc0 ) {GetEArb;inst(*rmrb,*earb,LoadRb,SaveRb);} \
+ else {GetEAa;inst(*rmrb,LoadMb(eaa),LoadRb,SaveRb);} \
+ }
+
+#define RMEb(inst) \
+ { \
+ if (rm >= 0xc0 ) {GetEArb;inst(*earb,LoadRb,SaveRb);} \
+ else {GetEAa;inst(eaa,LoadMb,SaveMb);} \
+ }
+
+#define RMEwGw(inst) \
+ { \
+ GetRMrw; \
+ if (rm >= 0xc0 ) {GetEArw;inst(*earw,*rmrw,LoadRw,SaveRw);} \
+ else {GetEAa;inst(eaa,*rmrw,LoadMw,SaveMw);} \
+ }
+
+#define RMEwGwOp3(inst,op3) \
+ { \
+ GetRMrw; \
+ if (rm >= 0xc0 ) {GetEArw;inst(*earw,*rmrw,op3,LoadRw,SaveRw);} \
+ else {GetEAa;inst(eaa,*rmrw,op3,LoadMw,SaveMw);} \
+ }
+
+#define RMGwEw(inst) \
+ { \
+ GetRMrw; \
+ if (rm >= 0xc0 ) {GetEArw;inst(*rmrw,*earw,LoadRw,SaveRw);} \
+ else {GetEAa;inst(*rmrw,LoadMw(eaa),LoadRw,SaveRw);} \
+ }
+
+#define RMGwEwOp3(inst,op3) \
+ { \
+ GetRMrw; \
+ if (rm >= 0xc0 ) {GetEArw;inst(*rmrw,*earw,op3,LoadRw,SaveRw);} \
+ else {GetEAa;inst(*rmrw,LoadMw(eaa),op3,LoadRw,SaveRw);} \
+ }
+
+#define RMEw(inst) \
+ { \
+ if (rm >= 0xc0 ) {GetEArw;inst(*earw,LoadRw,SaveRw);} \
+ else {GetEAa;inst(eaa,LoadMw,SaveMw);} \
+ }
+
+#define RMEdGd(inst) \
+ { \
+ GetRMrd; \
+ if (rm >= 0xc0 ) {GetEArd;inst(*eard,*rmrd,LoadRd,SaveRd);} \
+ else {GetEAa;inst(eaa,*rmrd,LoadMd,SaveMd);} \
+ }
+
+#define RMEdGdOp3(inst,op3) \
+ { \
+ GetRMrd; \
+ if (rm >= 0xc0 ) {GetEArd;inst(*eard,*rmrd,op3,LoadRd,SaveRd);} \
+ else {GetEAa;inst(eaa,*rmrd,op3,LoadMd,SaveMd);} \
+ }
+
+
+#define RMGdEd(inst) \
+ { \
+ GetRMrd; \
+ if (rm >= 0xc0 ) {GetEArd;inst(*rmrd,*eard,LoadRd,SaveRd);} \
+ else {GetEAa;inst(*rmrd,LoadMd(eaa),LoadRd,SaveRd);} \
+ }
+
+#define RMGdEdOp3(inst,op3) \
+ { \
+ GetRMrd; \
+ if (rm >= 0xc0 ) {GetEArd;inst(*rmrd,*eard,op3,LoadRd,SaveRd);} \
+ else {GetEAa;inst(*rmrd,LoadMd(eaa),op3,LoadRd,SaveRd);} \
+ }
+
+
+
+
+#define RMEw(inst) \
+ { \
+ if (rm >= 0xc0 ) {GetEArw;inst(*earw,LoadRw,SaveRw);} \
+ else {GetEAa;inst(eaa,LoadMw,SaveMw);} \
+ }
+
+#define RMEd(inst) \
+ { \
+ if (rm >= 0xc0 ) {GetEArd;inst(*eard,LoadRd,SaveRd);} \
+ else {GetEAa;inst(eaa,LoadMd,SaveMd);} \
+ }
+
+#define ALIb(inst) \
+ { inst(reg_al,Fetchb(),LoadRb,SaveRb)}
+
+#define AXIw(inst) \
+ { inst(reg_ax,Fetchw(),LoadRw,SaveRw);}
+
+#define EAXId(inst) \
+ { inst(reg_eax,Fetchd(),LoadRd,SaveRd);}
+
+#define FPU_ESC(code) { \
+ Bit8u rm=Fetchb(); \
+ if (rm >= 0xc0) { \
+ FPU_ESC ## code ## _Normal(rm); \
+ } else { \
+ GetEAa;FPU_ESC ## code ## _EA(rm,eaa); \
+ } \
+}
+
+#define CASE_W(_WHICH) \
+ case (OPCODE_NONE+_WHICH):
+
+#define CASE_D(_WHICH) \
+ case (OPCODE_SIZE+_WHICH):
+
+#define CASE_B(_WHICH) \
+ CASE_W(_WHICH) \
+ CASE_D(_WHICH)
+
+#define CASE_0F_W(_WHICH) \
+ case ((OPCODE_0F|OPCODE_NONE)+_WHICH):
+
+#define CASE_0F_D(_WHICH) \
+ case ((OPCODE_0F|OPCODE_SIZE)+_WHICH):
+
+#define CASE_0F_B(_WHICH) \
+ CASE_0F_W(_WHICH) \
+ CASE_0F_D(_WHICH)
diff --git a/src/cpu/core_normal/prefix_0f.h b/src/cpu/core_normal/prefix_0f.h
new file mode 100644
index 000000000..c19ac7552
--- /dev/null
+++ b/src/cpu/core_normal/prefix_0f.h
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+ CASE_0F_W(0x00) /* GRP 6 Exxx */
+ {
+ if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegal_opcode;
+ GetRM;Bitu which=(rm>>3)&7;
+ switch (which) {
+ case 0x00: /* SLDT */
+ case 0x01: /* STR */
+ {
+ Bitu saveval;
+ if (!which) saveval=CPU_SLDT();
+ else saveval=CPU_STR();
+ if (rm >= 0xc0) {GetEArw;*earw=saveval;}
+ else {GetEAa;SaveMw(eaa,saveval);}
+ }
+ break;
+ case 0x02:case 0x03:case 0x04:case 0x05:
+ {
+ Bitu loadval;
+ if (rm >= 0xc0 ) {GetEArw;loadval=*earw;}
+ else {GetEAa;loadval=LoadMw(eaa);}
+ switch (which) {
+ case 0x02:
+ if (cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ if (CPU_LLDT(loadval)) RUNEXCEPTION();
+ break;
+ case 0x03:
+ if (cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ if (CPU_LTR(loadval)) RUNEXCEPTION();
+ break;
+ case 0x04:
+ CPU_VERR(loadval);
+ break;
+ case 0x05:
+ CPU_VERW(loadval);
+ break;
+ }
+ }
+ break;
+ default:
+ goto illegal_opcode;
+ }
+ }
+ break;
+ CASE_0F_W(0x01) /* Group 7 Ew */
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ if (rm < 0xc0) { //First ones all use EA
+ GetEAa;Bitu limit;
+ switch (which) {
+ case 0x00: /* SGDT */
+ SaveMw(eaa,CPU_SGDT_limit());
+ SaveMd(eaa+2,CPU_SGDT_base());
+ break;
+ case 0x01: /* SIDT */
+ SaveMw(eaa,CPU_SIDT_limit());
+ SaveMd(eaa+2,CPU_SIDT_base());
+ break;
+ case 0x02: /* LGDT */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ CPU_LGDT(LoadMw(eaa),LoadMd(eaa+2) & 0xFFFFFF);
+ break;
+ case 0x03: /* LIDT */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ CPU_LIDT(LoadMw(eaa),LoadMd(eaa+2) & 0xFFFFFF);
+ break;
+ case 0x04: /* SMSW */
+ SaveMw(eaa,CPU_SMSW());
+ break;
+ case 0x06: /* LMSW */
+ limit=LoadMw(eaa);
+ if (CPU_LMSW(limit)) RUNEXCEPTION();
+ break;
+ case 0x07: /* INVLPG */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ PAGING_ClearTLB();
+ break;
+ }
+ } else {
+ GetEArw;
+ switch (which) {
+ case 0x02: /* LGDT */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ goto illegal_opcode;
+ case 0x03: /* LIDT */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ goto illegal_opcode;
+ case 0x04: /* SMSW */
+ *earw=CPU_SMSW();
+ break;
+ case 0x06: /* LMSW */
+ if (CPU_LMSW(*earw)) RUNEXCEPTION();
+ break;
+ default:
+ goto illegal_opcode;
+ }
+ }
+ }
+ break;
+ CASE_0F_W(0x02) /* LAR Gw,Ew */
+ {
+ if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegal_opcode;
+ GetRMrw;Bitu ar=*rmrw;
+ if (rm >= 0xc0) {
+ GetEArw;CPU_LAR(*earw,ar);
+ } else {
+ GetEAa;CPU_LAR(LoadMw(eaa),ar);
+ }
+ *rmrw=(Bit16u)ar;
+ }
+ break;
+ CASE_0F_W(0x03) /* LSL Gw,Ew */
+ {
+ if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegal_opcode;
+ GetRMrw;Bitu limit=*rmrw;
+ if (rm >= 0xc0) {
+ GetEArw;CPU_LSL(*earw,limit);
+ } else {
+ GetEAa;CPU_LSL(LoadMw(eaa),limit);
+ }
+ *rmrw=(Bit16u)limit;
+ }
+ break;
+ CASE_0F_B(0x06) /* CLTS */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ cpu.cr0&=(~CR0_TASKSWITCH);
+ break;
+ CASE_0F_B(0x08) /* INVD */
+ CASE_0F_B(0x09) /* WBINVD */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ break;
+ CASE_0F_B(0x20) /* MOV Rd.CRx */
+ {
+ GetRM;
+ Bitu which=(rm >> 3) & 7;
+ if (rm < 0xc0 ) {
+ rm |= 0xc0;
+ LOG(LOG_CPU,LOG_ERROR)("MOV XXX,CR%u with non-register",which);
+ }
+ GetEArd;
+ Bit32u crx_value;
+ if (CPU_READ_CRX(which,crx_value)) RUNEXCEPTION();
+ *eard=crx_value;
+ }
+ break;
+ CASE_0F_B(0x21) /* MOV Rd,DRx */
+ {
+ GetRM;
+ Bitu which=(rm >> 3) & 7;
+ if (rm < 0xc0 ) {
+ rm |= 0xc0;
+ LOG(LOG_CPU,LOG_ERROR)("MOV XXX,DR%u with non-register",which);
+ }
+ GetEArd;
+ Bit32u drx_value;
+ if (CPU_READ_DRX(which,drx_value)) RUNEXCEPTION();
+ *eard=drx_value;
+ }
+ break;
+ CASE_0F_B(0x22) /* MOV CRx,Rd */
+ {
+ GetRM;
+ Bitu which=(rm >> 3) & 7;
+ if (rm < 0xc0 ) {
+ rm |= 0xc0;
+ LOG(LOG_CPU,LOG_ERROR)("MOV XXX,CR%u with non-register",which);
+ }
+ GetEArd;
+ if (CPU_WRITE_CRX(which,*eard)) RUNEXCEPTION();
+ }
+ break;
+ CASE_0F_B(0x23) /* MOV DRx,Rd */
+ {
+ GetRM;
+ Bitu which=(rm >> 3) & 7;
+ if (rm < 0xc0 ) {
+ rm |= 0xc0;
+ LOG(LOG_CPU,LOG_ERROR)("MOV DR%u,XXX with non-register",which);
+ }
+ GetEArd;
+ if (CPU_WRITE_DRX(which,*eard)) RUNEXCEPTION();
+ }
+ break;
+ CASE_0F_B(0x24) /* MOV Rd,TRx */
+ {
+ GetRM;
+ Bitu which=(rm >> 3) & 7;
+ if (rm < 0xc0 ) {
+ rm |= 0xc0;
+ LOG(LOG_CPU,LOG_ERROR)("MOV XXX,TR%u with non-register",which);
+ }
+ GetEArd;
+ Bit32u trx_value;
+ if (CPU_READ_TRX(which,trx_value)) RUNEXCEPTION();
+ *eard=trx_value;
+ }
+ break;
+ CASE_0F_B(0x26) /* MOV TRx,Rd */
+ {
+ GetRM;
+ Bitu which=(rm >> 3) & 7;
+ if (rm < 0xc0 ) {
+ rm |= 0xc0;
+ LOG(LOG_CPU,LOG_ERROR)("MOV TR%u,XXX with non-register",which);
+ }
+ GetEArd;
+ if (CPU_WRITE_TRX(which,*eard)) RUNEXCEPTION();
+ }
+ break;
+ CASE_0F_B(0x31) /* RDTSC */
+ {
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_PENTIUMSLOW) goto illegal_opcode;
+ /* Use a fixed number when in auto cycles mode as else the reported value changes constantly */
+ Bit64s tsc=(Bit64s)(PIC_FullIndex()*(double) (CPU_CycleAutoAdjust?70000:CPU_CycleMax));
+ reg_edx=(Bit32u)(tsc>>32);
+ reg_eax=(Bit32u)(tsc&0xffffffff);
+ }
+ break;
+ CASE_0F_W(0x80) /* JO */
+ JumpCond16_w(TFLG_O);break;
+ CASE_0F_W(0x81) /* JNO */
+ JumpCond16_w(TFLG_NO);break;
+ CASE_0F_W(0x82) /* JB */
+ JumpCond16_w(TFLG_B);break;
+ CASE_0F_W(0x83) /* JNB */
+ JumpCond16_w(TFLG_NB);break;
+ CASE_0F_W(0x84) /* JZ */
+ JumpCond16_w(TFLG_Z);break;
+ CASE_0F_W(0x85) /* JNZ */
+ JumpCond16_w(TFLG_NZ);break;
+ CASE_0F_W(0x86) /* JBE */
+ JumpCond16_w(TFLG_BE);break;
+ CASE_0F_W(0x87) /* JNBE */
+ JumpCond16_w(TFLG_NBE);break;
+ CASE_0F_W(0x88) /* JS */
+ JumpCond16_w(TFLG_S);break;
+ CASE_0F_W(0x89) /* JNS */
+ JumpCond16_w(TFLG_NS);break;
+ CASE_0F_W(0x8a) /* JP */
+ JumpCond16_w(TFLG_P);break;
+ CASE_0F_W(0x8b) /* JNP */
+ JumpCond16_w(TFLG_NP);break;
+ CASE_0F_W(0x8c) /* JL */
+ JumpCond16_w(TFLG_L);break;
+ CASE_0F_W(0x8d) /* JNL */
+ JumpCond16_w(TFLG_NL);break;
+ CASE_0F_W(0x8e) /* JLE */
+ JumpCond16_w(TFLG_LE);break;
+ CASE_0F_W(0x8f) /* JNLE */
+ JumpCond16_w(TFLG_NLE);break;
+ CASE_0F_B(0x90) /* SETO */
+ SETcc(TFLG_O);break;
+ CASE_0F_B(0x91) /* SETNO */
+ SETcc(TFLG_NO);break;
+ CASE_0F_B(0x92) /* SETB */
+ SETcc(TFLG_B);break;
+ CASE_0F_B(0x93) /* SETNB */
+ SETcc(TFLG_NB);break;
+ CASE_0F_B(0x94) /* SETZ */
+ SETcc(TFLG_Z);break;
+ CASE_0F_B(0x95) /* SETNZ */
+ SETcc(TFLG_NZ); break;
+ CASE_0F_B(0x96) /* SETBE */
+ SETcc(TFLG_BE);break;
+ CASE_0F_B(0x97) /* SETNBE */
+ SETcc(TFLG_NBE);break;
+ CASE_0F_B(0x98) /* SETS */
+ SETcc(TFLG_S);break;
+ CASE_0F_B(0x99) /* SETNS */
+ SETcc(TFLG_NS);break;
+ CASE_0F_B(0x9a) /* SETP */
+ SETcc(TFLG_P);break;
+ CASE_0F_B(0x9b) /* SETNP */
+ SETcc(TFLG_NP);break;
+ CASE_0F_B(0x9c) /* SETL */
+ SETcc(TFLG_L);break;
+ CASE_0F_B(0x9d) /* SETNL */
+ SETcc(TFLG_NL);break;
+ CASE_0F_B(0x9e) /* SETLE */
+ SETcc(TFLG_LE);break;
+ CASE_0F_B(0x9f) /* SETNLE */
+ SETcc(TFLG_NLE);break;
+
+ CASE_0F_W(0xa0) /* PUSH FS */
+ Push_16(SegValue(fs));break;
+ CASE_0F_W(0xa1) /* POP FS */
+ if (CPU_PopSeg(fs,false)) RUNEXCEPTION();
+ break;
+ CASE_0F_B(0xa2) /* CPUID */
+ if (!CPU_CPUID()) goto illegal_opcode;
+ break;
+ CASE_0F_W(0xa3) /* BT Ew,Gw */
+ {
+ FillFlags();GetRMrw;
+ Bit16u mask=1 << (*rmrw & 15);
+ if (rm >= 0xc0 ) {
+ GetEArw;
+ SETFLAGBIT(CF,(*earw & mask));
+ } else {
+ GetEAa;eaa+=(((Bit16s)*rmrw)>>4)*2;
+ Bit16u old=LoadMw(eaa);
+ SETFLAGBIT(CF,(old & mask));
+ }
+ break;
+ }
+ CASE_0F_W(0xa4) /* SHLD Ew,Gw,Ib */
+ RMEwGwOp3(DSHLW,Fetchb());
+ break;
+ CASE_0F_W(0xa5) /* SHLD Ew,Gw,CL */
+ RMEwGwOp3(DSHLW,reg_cl);
+ break;
+ CASE_0F_W(0xa8) /* PUSH GS */
+ Push_16(SegValue(gs));break;
+ CASE_0F_W(0xa9) /* POP GS */
+ if (CPU_PopSeg(gs,false)) RUNEXCEPTION();
+ break;
+ CASE_0F_W(0xab) /* BTS Ew,Gw */
+ {
+ FillFlags();GetRMrw;
+ Bit16u mask=1 << (*rmrw & 15);
+ if (rm >= 0xc0 ) {
+ GetEArw;
+ SETFLAGBIT(CF,(*earw & mask));
+ *earw|=mask;
+ } else {
+ GetEAa;eaa+=(((Bit16s)*rmrw)>>4)*2;
+ Bit16u old=LoadMw(eaa);
+ SETFLAGBIT(CF,(old & mask));
+ SaveMw(eaa,old | mask);
+ }
+ break;
+ }
+ CASE_0F_W(0xac) /* SHRD Ew,Gw,Ib */
+ RMEwGwOp3(DSHRW,Fetchb());
+ break;
+ CASE_0F_W(0xad) /* SHRD Ew,Gw,CL */
+ RMEwGwOp3(DSHRW,reg_cl);
+ break;
+ CASE_0F_W(0xaf) /* IMUL Gw,Ew */
+ RMGwEwOp3(DIMULW,*rmrw);
+ break;
+ CASE_0F_B(0xb0) /* cmpxchg Eb,Gb */
+ {
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ FillFlags();
+ GetRMrb;
+ if (rm >= 0xc0 ) {
+ GetEArb;
+ if (reg_al == *earb) {
+ *earb=*rmrb;
+ SETFLAGBIT(ZF,1);
+ } else {
+ reg_al = *earb;
+ SETFLAGBIT(ZF,0);
+ }
+ } else {
+ GetEAa;
+ Bit8u val = LoadMb(eaa);
+ if (reg_al == val) {
+ SaveMb(eaa,*rmrb);
+ SETFLAGBIT(ZF,1);
+ } else {
+ SaveMb(eaa,val); // cmpxchg always issues a write
+ reg_al = val;
+ SETFLAGBIT(ZF,0);
+ }
+ }
+ break;
+ }
+ CASE_0F_W(0xb1) /* cmpxchg Ew,Gw */
+ {
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ FillFlags();
+ GetRMrw;
+ if (rm >= 0xc0 ) {
+ GetEArw;
+ if(reg_ax == *earw) {
+ *earw = *rmrw;
+ SETFLAGBIT(ZF,1);
+ } else {
+ reg_ax = *earw;
+ SETFLAGBIT(ZF,0);
+ }
+ } else {
+ GetEAa;
+ Bit16u val = LoadMw(eaa);
+ if(reg_ax == val) {
+ SaveMw(eaa,*rmrw);
+ SETFLAGBIT(ZF,1);
+ } else {
+ SaveMw(eaa,val); // cmpxchg always issues a write
+ reg_ax = val;
+ SETFLAGBIT(ZF,0);
+ }
+ }
+ break;
+ }
+
+ CASE_0F_W(0xb2) /* LSS Ew */
+ {
+ GetRMrw;
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ if (CPU_SetSegGeneral(ss,LoadMw(eaa+2))) RUNEXCEPTION();
+ *rmrw=LoadMw(eaa);
+ break;
+ }
+ CASE_0F_W(0xb3) /* BTR Ew,Gw */
+ {
+ FillFlags();GetRMrw;
+ Bit16u mask=1 << (*rmrw & 15);
+ if (rm >= 0xc0 ) {
+ GetEArw;
+ SETFLAGBIT(CF,(*earw & mask));
+ *earw&= ~mask;
+ } else {
+ GetEAa;eaa+=(((Bit16s)*rmrw)>>4)*2;
+ Bit16u old=LoadMw(eaa);
+ SETFLAGBIT(CF,(old & mask));
+ SaveMw(eaa,old & ~mask);
+ }
+ break;
+ }
+ CASE_0F_W(0xb4) /* LFS Ew */
+ {
+ GetRMrw;
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ if (CPU_SetSegGeneral(fs,LoadMw(eaa+2))) RUNEXCEPTION();
+ *rmrw=LoadMw(eaa);
+ break;
+ }
+ CASE_0F_W(0xb5) /* LGS Ew */
+ {
+ GetRMrw;
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ if (CPU_SetSegGeneral(gs,LoadMw(eaa+2))) RUNEXCEPTION();
+ *rmrw=LoadMw(eaa);
+ break;
+ }
+ CASE_0F_W(0xb6) /* MOVZX Gw,Eb */
+ {
+ GetRMrw;
+ if (rm >= 0xc0 ) {GetEArb;*rmrw=*earb;}
+ else {GetEAa;*rmrw=LoadMb(eaa);}
+ break;
+ }
+ CASE_0F_W(0xb7) /* MOVZX Gw,Ew */
+ CASE_0F_W(0xbf) /* MOVSX Gw,Ew */
+ {
+ GetRMrw;
+ if (rm >= 0xc0 ) {GetEArw;*rmrw=*earw;}
+ else {GetEAa;*rmrw=LoadMw(eaa);}
+ break;
+ }
+ CASE_0F_W(0xba) /* GRP8 Ew,Ib */
+ {
+ FillFlags();GetRM;
+ if (rm >= 0xc0 ) {
+ GetEArw;
+ Bit16u mask=1 << (Fetchb() & 15);
+ SETFLAGBIT(CF,(*earw & mask));
+ switch (rm & 0x38) {
+ case 0x20: /* BT */
+ break;
+ case 0x28: /* BTS */
+ *earw|=mask;
+ break;
+ case 0x30: /* BTR */
+ *earw&= ~mask;
+ break;
+ case 0x38: /* BTC */
+ *earw^=mask;
+ break;
+ default:
+ E_Exit("CPU:0F:BA:Illegal subfunction %X",rm & 0x38);
+ }
+ } else {
+ GetEAa;Bit16u old=LoadMw(eaa);
+ Bit16u mask=1 << (Fetchb() & 15);
+ SETFLAGBIT(CF,(old & mask));
+ switch (rm & 0x38) {
+ case 0x20: /* BT */
+ break;
+ case 0x28: /* BTS */
+ SaveMw(eaa,old|mask);
+ break;
+ case 0x30: /* BTR */
+ SaveMw(eaa,old & ~mask);
+ break;
+ case 0x38: /* BTC */
+ SaveMw(eaa,old ^ mask);
+ break;
+ default:
+ E_Exit("CPU:0F:BA:Illegal subfunction %X",rm & 0x38);
+ }
+ }
+ break;
+ }
+ CASE_0F_W(0xbb) /* BTC Ew,Gw */
+ {
+ FillFlags();GetRMrw;
+ Bit16u mask=1 << (*rmrw & 15);
+ if (rm >= 0xc0 ) {
+ GetEArw;
+ SETFLAGBIT(CF,(*earw & mask));
+ *earw^=mask;
+ } else {
+ GetEAa;eaa+=(((Bit16s)*rmrw)>>4)*2;
+ Bit16u old=LoadMw(eaa);
+ SETFLAGBIT(CF,(old & mask));
+ SaveMw(eaa,old ^ mask);
+ }
+ break;
+ }
+ CASE_0F_W(0xbc) /* BSF Gw,Ew */
+ {
+ GetRMrw;
+ Bit16u result,value;
+ if (rm >= 0xc0) { GetEArw; value=*earw; }
+ else { GetEAa; value=LoadMw(eaa); }
+ if (value==0) {
+ SETFLAGBIT(ZF,true);
+ } else {
+ result = 0;
+ while ((value & 0x01)==0) { result++; value>>=1; }
+ SETFLAGBIT(ZF,false);
+ *rmrw = result;
+ }
+ lflags.type=t_UNKNOWN;
+ break;
+ }
+ CASE_0F_W(0xbd) /* BSR Gw,Ew */
+ {
+ GetRMrw;
+ Bit16u result,value;
+ if (rm >= 0xc0) { GetEArw; value=*earw; }
+ else { GetEAa; value=LoadMw(eaa); }
+ if (value==0) {
+ SETFLAGBIT(ZF,true);
+ } else {
+ result = 15; // Operandsize-1
+ while ((value & 0x8000)==0) { result--; value<<=1; }
+ SETFLAGBIT(ZF,false);
+ *rmrw = result;
+ }
+ lflags.type=t_UNKNOWN;
+ break;
+ }
+ CASE_0F_W(0xbe) /* MOVSX Gw,Eb */
+ {
+ GetRMrw;
+ if (rm >= 0xc0 ) {GetEArb;*rmrw=*(Bit8s *)earb;}
+ else {GetEAa;*rmrw=LoadMbs(eaa);}
+ break;
+ }
+ CASE_0F_B(0xc0) /* XADD Gb,Eb */
+ {
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ GetRMrb;Bit8u oldrmrb=*rmrb;
+ if (rm >= 0xc0 ) {GetEArb;*rmrb=*earb;*earb+=oldrmrb;}
+ else {GetEAa;*rmrb=LoadMb(eaa);SaveMb(eaa,LoadMb(eaa)+oldrmrb);}
+ break;
+ }
+ CASE_0F_W(0xc1) /* XADD Gw,Ew */
+ {
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ GetRMrw;Bit16u oldrmrw=*rmrw;
+ if (rm >= 0xc0 ) {GetEArw;*rmrw=*earw;*earw+=oldrmrw;}
+ else {GetEAa;*rmrw=LoadMw(eaa);SaveMw(eaa,LoadMw(eaa)+oldrmrw);}
+ break;
+ }
+ CASE_0F_W(0xc8) /* BSWAP AX */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPW(reg_ax);break;
+ CASE_0F_W(0xc9) /* BSWAP CX */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPW(reg_cx);break;
+ CASE_0F_W(0xca) /* BSWAP DX */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPW(reg_dx);break;
+ CASE_0F_W(0xcb) /* BSWAP BX */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPW(reg_bx);break;
+ CASE_0F_W(0xcc) /* BSWAP SP */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPW(reg_sp);break;
+ CASE_0F_W(0xcd) /* BSWAP BP */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPW(reg_bp);break;
+ CASE_0F_W(0xce) /* BSWAP SI */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPW(reg_si);break;
+ CASE_0F_W(0xcf) /* BSWAP DI */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPW(reg_di);break;
+
diff --git a/src/cpu/core_normal/prefix_66.h b/src/cpu/core_normal/prefix_66.h
new file mode 100644
index 000000000..0c9218c71
--- /dev/null
+++ b/src/cpu/core_normal/prefix_66.h
@@ -0,0 +1,718 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+ CASE_D(0x01) /* ADD Ed,Gd */
+ RMEdGd(ADDD);break;
+ CASE_D(0x03) /* ADD Gd,Ed */
+ RMGdEd(ADDD);break;
+ CASE_D(0x05) /* ADD EAX,Id */
+ EAXId(ADDD);break;
+ CASE_D(0x06) /* PUSH ES */
+ Push_32(SegValue(es));break;
+ CASE_D(0x07) /* POP ES */
+ if (CPU_PopSeg(es,true)) RUNEXCEPTION();
+ break;
+ CASE_D(0x09) /* OR Ed,Gd */
+ RMEdGd(ORD);break;
+ CASE_D(0x0b) /* OR Gd,Ed */
+ RMGdEd(ORD);break;
+ CASE_D(0x0d) /* OR EAX,Id */
+ EAXId(ORD);break;
+ CASE_D(0x0e) /* PUSH CS */
+ Push_32(SegValue(cs));break;
+ CASE_D(0x11) /* ADC Ed,Gd */
+ RMEdGd(ADCD);break;
+ CASE_D(0x13) /* ADC Gd,Ed */
+ RMGdEd(ADCD);break;
+ CASE_D(0x15) /* ADC EAX,Id */
+ EAXId(ADCD);break;
+ CASE_D(0x16) /* PUSH SS */
+ Push_32(SegValue(ss));break;
+ CASE_D(0x17) /* POP SS */
+ if (CPU_PopSeg(ss,true)) RUNEXCEPTION();
+ CPU_Cycles++;
+ break;
+ CASE_D(0x19) /* SBB Ed,Gd */
+ RMEdGd(SBBD);break;
+ CASE_D(0x1b) /* SBB Gd,Ed */
+ RMGdEd(SBBD);break;
+ CASE_D(0x1d) /* SBB EAX,Id */
+ EAXId(SBBD);break;
+ CASE_D(0x1e) /* PUSH DS */
+ Push_32(SegValue(ds));break;
+ CASE_D(0x1f) /* POP DS */
+ if (CPU_PopSeg(ds,true)) RUNEXCEPTION();
+ break;
+ CASE_D(0x21) /* AND Ed,Gd */
+ RMEdGd(ANDD);break;
+ CASE_D(0x23) /* AND Gd,Ed */
+ RMGdEd(ANDD);break;
+ CASE_D(0x25) /* AND EAX,Id */
+ EAXId(ANDD);break;
+ CASE_D(0x29) /* SUB Ed,Gd */
+ RMEdGd(SUBD);break;
+ CASE_D(0x2b) /* SUB Gd,Ed */
+ RMGdEd(SUBD);break;
+ CASE_D(0x2d) /* SUB EAX,Id */
+ EAXId(SUBD);break;
+ CASE_D(0x31) /* XOR Ed,Gd */
+ RMEdGd(XORD);break;
+ CASE_D(0x33) /* XOR Gd,Ed */
+ RMGdEd(XORD);break;
+ CASE_D(0x35) /* XOR EAX,Id */
+ EAXId(XORD);break;
+ CASE_D(0x39) /* CMP Ed,Gd */
+ RMEdGd(CMPD);break;
+ CASE_D(0x3b) /* CMP Gd,Ed */
+ RMGdEd(CMPD);break;
+ CASE_D(0x3d) /* CMP EAX,Id */
+ EAXId(CMPD);break;
+ CASE_D(0x40) /* INC EAX */
+ INCD(reg_eax,LoadRd,SaveRd);break;
+ CASE_D(0x41) /* INC ECX */
+ INCD(reg_ecx,LoadRd,SaveRd);break;
+ CASE_D(0x42) /* INC EDX */
+ INCD(reg_edx,LoadRd,SaveRd);break;
+ CASE_D(0x43) /* INC EBX */
+ INCD(reg_ebx,LoadRd,SaveRd);break;
+ CASE_D(0x44) /* INC ESP */
+ INCD(reg_esp,LoadRd,SaveRd);break;
+ CASE_D(0x45) /* INC EBP */
+ INCD(reg_ebp,LoadRd,SaveRd);break;
+ CASE_D(0x46) /* INC ESI */
+ INCD(reg_esi,LoadRd,SaveRd);break;
+ CASE_D(0x47) /* INC EDI */
+ INCD(reg_edi,LoadRd,SaveRd);break;
+ CASE_D(0x48) /* DEC EAX */
+ DECD(reg_eax,LoadRd,SaveRd);break;
+ CASE_D(0x49) /* DEC ECX */
+ DECD(reg_ecx,LoadRd,SaveRd);break;
+ CASE_D(0x4a) /* DEC EDX */
+ DECD(reg_edx,LoadRd,SaveRd);break;
+ CASE_D(0x4b) /* DEC EBX */
+ DECD(reg_ebx,LoadRd,SaveRd);break;
+ CASE_D(0x4c) /* DEC ESP */
+ DECD(reg_esp,LoadRd,SaveRd);break;
+ CASE_D(0x4d) /* DEC EBP */
+ DECD(reg_ebp,LoadRd,SaveRd);break;
+ CASE_D(0x4e) /* DEC ESI */
+ DECD(reg_esi,LoadRd,SaveRd);break;
+ CASE_D(0x4f) /* DEC EDI */
+ DECD(reg_edi,LoadRd,SaveRd);break;
+ CASE_D(0x50) /* PUSH EAX */
+ Push_32(reg_eax);break;
+ CASE_D(0x51) /* PUSH ECX */
+ Push_32(reg_ecx);break;
+ CASE_D(0x52) /* PUSH EDX */
+ Push_32(reg_edx);break;
+ CASE_D(0x53) /* PUSH EBX */
+ Push_32(reg_ebx);break;
+ CASE_D(0x54) /* PUSH ESP */
+ Push_32(reg_esp);break;
+ CASE_D(0x55) /* PUSH EBP */
+ Push_32(reg_ebp);break;
+ CASE_D(0x56) /* PUSH ESI */
+ Push_32(reg_esi);break;
+ CASE_D(0x57) /* PUSH EDI */
+ Push_32(reg_edi);break;
+ CASE_D(0x58) /* POP EAX */
+ reg_eax=Pop_32();break;
+ CASE_D(0x59) /* POP ECX */
+ reg_ecx=Pop_32();break;
+ CASE_D(0x5a) /* POP EDX */
+ reg_edx=Pop_32();break;
+ CASE_D(0x5b) /* POP EBX */
+ reg_ebx=Pop_32();break;
+ CASE_D(0x5c) /* POP ESP */
+ reg_esp=Pop_32();break;
+ CASE_D(0x5d) /* POP EBP */
+ reg_ebp=Pop_32();break;
+ CASE_D(0x5e) /* POP ESI */
+ reg_esi=Pop_32();break;
+ CASE_D(0x5f) /* POP EDI */
+ reg_edi=Pop_32();break;
+ CASE_D(0x60) /* PUSHAD */
+ {
+ Bitu tmpesp = reg_esp;
+ Push_32(reg_eax);Push_32(reg_ecx);Push_32(reg_edx);Push_32(reg_ebx);
+ Push_32(tmpesp);Push_32(reg_ebp);Push_32(reg_esi);Push_32(reg_edi);
+ }; break;
+ CASE_D(0x61) /* POPAD */
+ reg_edi=Pop_32();reg_esi=Pop_32();reg_ebp=Pop_32();Pop_32();//Don't save ESP
+ reg_ebx=Pop_32();reg_edx=Pop_32();reg_ecx=Pop_32();reg_eax=Pop_32();
+ break;
+ CASE_D(0x62) /* BOUND Ed */
+ {
+ Bit32s bound_min, bound_max;
+ GetRMrd;GetEAa;
+ bound_min=LoadMd(eaa);
+ bound_max=LoadMd(eaa+4);
+ if ( (((Bit32s)*rmrd) < bound_min) || (((Bit32s)*rmrd) > bound_max) ) {
+ EXCEPTION(5);
+ }
+ }
+ break;
+ CASE_D(0x63) /* ARPL Ed,Rd */
+ {
+ if (((cpu.pmode) && (reg_flags & FLAG_VM)) || (!cpu.pmode)) goto illegal_opcode;
+ GetRMrw;
+ if (rm >= 0xc0 ) {
+ GetEArd;Bitu new_sel=(Bit16u)*eard;
+ CPU_ARPL(new_sel,*rmrw);
+ *eard=(Bit32u)new_sel;
+ } else {
+ GetEAa;Bitu new_sel=LoadMw(eaa);
+ CPU_ARPL(new_sel,*rmrw);
+ SaveMd(eaa,(Bit32u)new_sel);
+ }
+ }
+ break;
+ CASE_D(0x68) /* PUSH Id */
+ Push_32(Fetchd());break;
+ CASE_D(0x69) /* IMUL Gd,Ed,Id */
+ RMGdEdOp3(DIMULD,Fetchds());
+ break;
+ CASE_D(0x6a) /* PUSH Ib */
+ Push_32(Fetchbs());break;
+ CASE_D(0x6b) /* IMUL Gd,Ed,Ib */
+ RMGdEdOp3(DIMULD,Fetchbs());
+ break;
+ CASE_D(0x6d) /* INSD */
+ if (CPU_IO_Exception(reg_dx,4)) RUNEXCEPTION();
+ DoString(R_INSD);break;
+ CASE_D(0x6f) /* OUTSD */
+ if (CPU_IO_Exception(reg_dx,4)) RUNEXCEPTION();
+ DoString(R_OUTSD);break;
+ CASE_D(0x70) /* JO */
+ JumpCond32_b(TFLG_O);break;
+ CASE_D(0x71) /* JNO */
+ JumpCond32_b(TFLG_NO);break;
+ CASE_D(0x72) /* JB */
+ JumpCond32_b(TFLG_B);break;
+ CASE_D(0x73) /* JNB */
+ JumpCond32_b(TFLG_NB);break;
+ CASE_D(0x74) /* JZ */
+ JumpCond32_b(TFLG_Z);break;
+ CASE_D(0x75) /* JNZ */
+ JumpCond32_b(TFLG_NZ);break;
+ CASE_D(0x76) /* JBE */
+ JumpCond32_b(TFLG_BE);break;
+ CASE_D(0x77) /* JNBE */
+ JumpCond32_b(TFLG_NBE);break;
+ CASE_D(0x78) /* JS */
+ JumpCond32_b(TFLG_S);break;
+ CASE_D(0x79) /* JNS */
+ JumpCond32_b(TFLG_NS);break;
+ CASE_D(0x7a) /* JP */
+ JumpCond32_b(TFLG_P);break;
+ CASE_D(0x7b) /* JNP */
+ JumpCond32_b(TFLG_NP);break;
+ CASE_D(0x7c) /* JL */
+ JumpCond32_b(TFLG_L);break;
+ CASE_D(0x7d) /* JNL */
+ JumpCond32_b(TFLG_NL);break;
+ CASE_D(0x7e) /* JLE */
+ JumpCond32_b(TFLG_LE);break;
+ CASE_D(0x7f) /* JNLE */
+ JumpCond32_b(TFLG_NLE);break;
+ CASE_D(0x81) /* Grpl Ed,Id */
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ if (rm >= 0xc0) {
+ GetEArd;Bit32u id=Fetchd();
+ switch (which) {
+ case 0x00:ADDD(*eard,id,LoadRd,SaveRd);break;
+ case 0x01: ORD(*eard,id,LoadRd,SaveRd);break;
+ case 0x02:ADCD(*eard,id,LoadRd,SaveRd);break;
+ case 0x03:SBBD(*eard,id,LoadRd,SaveRd);break;
+ case 0x04:ANDD(*eard,id,LoadRd,SaveRd);break;
+ case 0x05:SUBD(*eard,id,LoadRd,SaveRd);break;
+ case 0x06:XORD(*eard,id,LoadRd,SaveRd);break;
+ case 0x07:CMPD(*eard,id,LoadRd,SaveRd);break;
+ }
+ } else {
+ GetEAa;Bit32u id=Fetchd();
+ switch (which) {
+ case 0x00:ADDD(eaa,id,LoadMd,SaveMd);break;
+ case 0x01: ORD(eaa,id,LoadMd,SaveMd);break;
+ case 0x02:ADCD(eaa,id,LoadMd,SaveMd);break;
+ case 0x03:SBBD(eaa,id,LoadMd,SaveMd);break;
+ case 0x04:ANDD(eaa,id,LoadMd,SaveMd);break;
+ case 0x05:SUBD(eaa,id,LoadMd,SaveMd);break;
+ case 0x06:XORD(eaa,id,LoadMd,SaveMd);break;
+ case 0x07:CMPD(eaa,id,LoadMd,SaveMd);break;
+ }
+ }
+ }
+ break;
+ CASE_D(0x83) /* Grpl Ed,Ix */
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ if (rm >= 0xc0) {
+ GetEArd;Bit32u id=(Bit32s)Fetchbs();
+ switch (which) {
+ case 0x00:ADDD(*eard,id,LoadRd,SaveRd);break;
+ case 0x01: ORD(*eard,id,LoadRd,SaveRd);break;
+ case 0x02:ADCD(*eard,id,LoadRd,SaveRd);break;
+ case 0x03:SBBD(*eard,id,LoadRd,SaveRd);break;
+ case 0x04:ANDD(*eard,id,LoadRd,SaveRd);break;
+ case 0x05:SUBD(*eard,id,LoadRd,SaveRd);break;
+ case 0x06:XORD(*eard,id,LoadRd,SaveRd);break;
+ case 0x07:CMPD(*eard,id,LoadRd,SaveRd);break;
+ }
+ } else {
+ GetEAa;Bit32u id=(Bit32s)Fetchbs();
+ switch (which) {
+ case 0x00:ADDD(eaa,id,LoadMd,SaveMd);break;
+ case 0x01: ORD(eaa,id,LoadMd,SaveMd);break;
+ case 0x02:ADCD(eaa,id,LoadMd,SaveMd);break;
+ case 0x03:SBBD(eaa,id,LoadMd,SaveMd);break;
+ case 0x04:ANDD(eaa,id,LoadMd,SaveMd);break;
+ case 0x05:SUBD(eaa,id,LoadMd,SaveMd);break;
+ case 0x06:XORD(eaa,id,LoadMd,SaveMd);break;
+ case 0x07:CMPD(eaa,id,LoadMd,SaveMd);break;
+ }
+ }
+ }
+ break;
+ CASE_D(0x85) /* TEST Ed,Gd */
+ RMEdGd(TESTD);break;
+ CASE_D(0x87) /* XCHG Ed,Gd */
+ {
+ GetRMrd;Bit32u oldrmrd=*rmrd;
+ if (rm >= 0xc0 ) {GetEArd;*rmrd=*eard;*eard=oldrmrd;}
+ else {GetEAa;*rmrd=LoadMd(eaa);SaveMd(eaa,oldrmrd);}
+ break;
+ }
+ CASE_D(0x89) /* MOV Ed,Gd */
+ {
+ GetRMrd;
+ if (rm >= 0xc0 ) {GetEArd;*eard=*rmrd;}
+ else {GetEAa;SaveMd(eaa,*rmrd);}
+ break;
+ }
+ CASE_D(0x8b) /* MOV Gd,Ed */
+ {
+ GetRMrd;
+ if (rm >= 0xc0 ) {GetEArd;*rmrd=*eard;}
+ else {GetEAa;*rmrd=LoadMd(eaa);}
+ break;
+ }
+ CASE_D(0x8c) /* Mov Ew,Sw */
+ {
+ GetRM;Bit16u val;Bitu which=(rm>>3)&7;
+ switch (which) {
+ case 0x00: /* MOV Ew,ES */
+ val=SegValue(es);break;
+ case 0x01: /* MOV Ew,CS */
+ val=SegValue(cs);break;
+ case 0x02: /* MOV Ew,SS */
+ val=SegValue(ss);break;
+ case 0x03: /* MOV Ew,DS */
+ val=SegValue(ds);break;
+ case 0x04: /* MOV Ew,FS */
+ val=SegValue(fs);break;
+ case 0x05: /* MOV Ew,GS */
+ val=SegValue(gs);break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("CPU:8c:Illegal RM Byte");
+ goto illegal_opcode;
+ }
+ if (rm >= 0xc0 ) {GetEArd;*eard=val;}
+ else {GetEAa;SaveMw(eaa,val);}
+ break;
+ }
+ CASE_D(0x8d) /* LEA Gd */
+ {
+ //Little hack to always use segprefixed version
+ GetRMrd;
+ BaseDS=BaseSS=0;
+ if (TEST_PREFIX_ADDR) {
+ *rmrd=(Bit32u)(*EATable[256+rm])();
+ } else {
+ *rmrd=(Bit32u)(*EATable[rm])();
+ }
+ break;
+ }
+ CASE_D(0x8f) /* POP Ed */
+ {
+ Bit32u val=Pop_32();
+ GetRM;
+ if (rm >= 0xc0 ) {GetEArd;*eard=val;}
+ else {GetEAa;SaveMd(eaa,val);}
+ break;
+ }
+ CASE_D(0x91) /* XCHG ECX,EAX */
+ { Bit32u temp=reg_eax;reg_eax=reg_ecx;reg_ecx=temp;break;}
+ CASE_D(0x92) /* XCHG EDX,EAX */
+ { Bit32u temp=reg_eax;reg_eax=reg_edx;reg_edx=temp;break;}
+ break;
+ CASE_D(0x93) /* XCHG EBX,EAX */
+ { Bit32u temp=reg_eax;reg_eax=reg_ebx;reg_ebx=temp;break;}
+ break;
+ CASE_D(0x94) /* XCHG ESP,EAX */
+ { Bit32u temp=reg_eax;reg_eax=reg_esp;reg_esp=temp;break;}
+ break;
+ CASE_D(0x95) /* XCHG EBP,EAX */
+ { Bit32u temp=reg_eax;reg_eax=reg_ebp;reg_ebp=temp;break;}
+ break;
+ CASE_D(0x96) /* XCHG ESI,EAX */
+ { Bit32u temp=reg_eax;reg_eax=reg_esi;reg_esi=temp;break;}
+ break;
+ CASE_D(0x97) /* XCHG EDI,EAX */
+ { Bit32u temp=reg_eax;reg_eax=reg_edi;reg_edi=temp;break;}
+ break;
+ CASE_D(0x98) /* CWDE */
+ reg_eax=(Bit16s)reg_ax;break;
+ CASE_D(0x99) /* CDQ */
+ if (reg_eax & 0x80000000) reg_edx=0xffffffff;
+ else reg_edx=0;
+ break;
+ CASE_D(0x9a) /* CALL FAR Ad */
+ {
+ Bit32u newip=Fetchd();Bit16u newcs=Fetchw();
+ FillFlags();
+ CPU_CALL(true,newcs,newip,GETIP);
+#if CPU_TRAP_CHECK
+ if (GETFLAG(TF)) {
+ cpudecoder=CPU_TRAP_DECODER;
+ return CBRET_NONE;
+ }
+#endif
+ continue;
+ }
+ CASE_D(0x9c) /* PUSHFD */
+ if (CPU_PUSHF(true)) RUNEXCEPTION();
+ break;
+ CASE_D(0x9d) /* POPFD */
+ if (CPU_POPF(true)) RUNEXCEPTION();
+#if CPU_TRAP_CHECK
+ if (GETFLAG(TF)) {
+ cpudecoder=CPU_TRAP_DECODER;
+ goto decode_end;
+ }
+#endif
+#if CPU_PIC_CHECK
+ if (GETFLAG(IF) && PIC_IRQCheck) goto decode_end;
+#endif
+ break;
+ CASE_D(0xa1) /* MOV EAX,Od */
+ {
+ GetEADirect;
+ reg_eax=LoadMd(eaa);
+ }
+ break;
+ CASE_D(0xa3) /* MOV Od,EAX */
+ {
+ GetEADirect;
+ SaveMd(eaa,reg_eax);
+ }
+ break;
+ CASE_D(0xa5) /* MOVSD */
+ DoString(R_MOVSD);break;
+ CASE_D(0xa7) /* CMPSD */
+ DoString(R_CMPSD);break;
+ CASE_D(0xa9) /* TEST EAX,Id */
+ EAXId(TESTD);break;
+ CASE_D(0xab) /* STOSD */
+ DoString(R_STOSD);break;
+ CASE_D(0xad) /* LODSD */
+ DoString(R_LODSD);break;
+ CASE_D(0xaf) /* SCASD */
+ DoString(R_SCASD);break;
+ CASE_D(0xb8) /* MOV EAX,Id */
+ reg_eax=Fetchd();break;
+ CASE_D(0xb9) /* MOV ECX,Id */
+ reg_ecx=Fetchd();break;
+ CASE_D(0xba) /* MOV EDX,Iw */
+ reg_edx=Fetchd();break;
+ CASE_D(0xbb) /* MOV EBX,Id */
+ reg_ebx=Fetchd();break;
+ CASE_D(0xbc) /* MOV ESP,Id */
+ reg_esp=Fetchd();break;
+ CASE_D(0xbd) /* MOV EBP.Id */
+ reg_ebp=Fetchd();break;
+ CASE_D(0xbe) /* MOV ESI,Id */
+ reg_esi=Fetchd();break;
+ CASE_D(0xbf) /* MOV EDI,Id */
+ reg_edi=Fetchd();break;
+ CASE_D(0xc1) /* GRP2 Ed,Ib */
+ GRP2D(Fetchb());break;
+ CASE_D(0xc2) /* RETN Iw */
+ reg_eip=Pop_32();
+ reg_esp+=Fetchw();
+ continue;
+ CASE_D(0xc3) /* RETN */
+ reg_eip=Pop_32();
+ continue;
+ CASE_D(0xc4) /* LES */
+ {
+ GetRMrd;
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ if (CPU_SetSegGeneral(es,LoadMw(eaa+4))) RUNEXCEPTION();
+ *rmrd=LoadMd(eaa);
+ break;
+ }
+ CASE_D(0xc5) /* LDS */
+ {
+ GetRMrd;
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ if (CPU_SetSegGeneral(ds,LoadMw(eaa+4))) RUNEXCEPTION();
+ *rmrd=LoadMd(eaa);
+ break;
+ }
+ CASE_D(0xc7) /* MOV Ed,Id */
+ {
+ GetRM;
+ if (rm >= 0xc0) {GetEArd;*eard=Fetchd();}
+ else {GetEAa;SaveMd(eaa,Fetchd());}
+ break;
+ }
+ CASE_D(0xc8) /* ENTER Iw,Ib */
+ {
+ Bitu bytes=Fetchw();
+ Bitu level=Fetchb();
+ CPU_ENTER(true,bytes,level);
+ }
+ break;
+ CASE_D(0xc9) /* LEAVE */
+ reg_esp&=cpu.stack.notmask;
+ reg_esp|=(reg_ebp&cpu.stack.mask);
+ reg_ebp=Pop_32();
+ break;
+ CASE_D(0xca) /* RETF Iw */
+ {
+ Bitu words=Fetchw();
+ FillFlags();
+ CPU_RET(true,words,GETIP);
+ continue;
+ }
+ CASE_D(0xcb) /* RETF */
+ {
+ FillFlags();
+ CPU_RET(true,0,GETIP);
+ continue;
+ }
+ CASE_D(0xcf) /* IRET */
+ {
+ CPU_IRET(true,GETIP);
+#if CPU_TRAP_CHECK
+ if (GETFLAG(TF)) {
+ cpudecoder=CPU_TRAP_DECODER;
+ return CBRET_NONE;
+ }
+#endif
+#if CPU_PIC_CHECK
+ if (GETFLAG(IF) && PIC_IRQCheck) return CBRET_NONE;
+#endif
+ continue;
+ }
+ CASE_D(0xd1) /* GRP2 Ed,1 */
+ GRP2D(1);break;
+ CASE_D(0xd3) /* GRP2 Ed,CL */
+ GRP2D(reg_cl);break;
+ CASE_D(0xe0) /* LOOPNZ */
+ if (TEST_PREFIX_ADDR) {
+ JumpCond32_b(--reg_ecx && !get_ZF());
+ } else {
+ JumpCond32_b(--reg_cx && !get_ZF());
+ }
+ break;
+ CASE_D(0xe1) /* LOOPZ */
+ if (TEST_PREFIX_ADDR) {
+ JumpCond32_b(--reg_ecx && get_ZF());
+ } else {
+ JumpCond32_b(--reg_cx && get_ZF());
+ }
+ break;
+ CASE_D(0xe2) /* LOOP */
+ if (TEST_PREFIX_ADDR) {
+ JumpCond32_b(--reg_ecx);
+ } else {
+ JumpCond32_b(--reg_cx);
+ }
+ break;
+ CASE_D(0xe3) /* JCXZ */
+ JumpCond32_b(!(reg_ecx & AddrMaskTable[core.prefixes& PREFIX_ADDR]));
+ break;
+ CASE_D(0xe5) /* IN EAX,Ib */
+ {
+ Bitu port=Fetchb();
+ if (CPU_IO_Exception(port,4)) RUNEXCEPTION();
+ reg_eax=IO_ReadD(port);
+ break;
+ }
+ CASE_D(0xe7) /* OUT Ib,EAX */
+ {
+ Bitu port=Fetchb();
+ if (CPU_IO_Exception(port,4)) RUNEXCEPTION();
+ IO_WriteD(port,reg_eax);
+ break;
+ }
+ CASE_D(0xe8) /* CALL Jd */
+ {
+ Bit32s addip=Fetchds();
+ SAVEIP;
+ Push_32(reg_eip);
+ reg_eip+=addip;
+ continue;
+ }
+ CASE_D(0xe9) /* JMP Jd */
+ {
+ Bit32s addip=Fetchds();
+ SAVEIP;
+ reg_eip+=addip;
+ continue;
+ }
+ CASE_D(0xea) /* JMP Ad */
+ {
+ Bit32u newip=Fetchd();
+ Bit16u newcs=Fetchw();
+ FillFlags();
+ CPU_JMP(true,newcs,newip,GETIP);
+#if CPU_TRAP_CHECK
+ if (GETFLAG(TF)) {
+ cpudecoder=CPU_TRAP_DECODER;
+ return CBRET_NONE;
+ }
+#endif
+ continue;
+ }
+ CASE_D(0xeb) /* JMP Jb */
+ {
+ Bit32s addip=Fetchbs();
+ SAVEIP;
+ reg_eip+=addip;
+ continue;
+ }
+ CASE_D(0xed) /* IN EAX,DX */
+ reg_eax=IO_ReadD(reg_dx);
+ break;
+ CASE_D(0xef) /* OUT DX,EAX */
+ IO_WriteD(reg_dx,reg_eax);
+ break;
+ CASE_D(0xf7) /* GRP3 Ed(,Id) */
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ switch (which) {
+ case 0x00: /* TEST Ed,Id */
+ case 0x01: /* TEST Ed,Id Undocumented*/
+ {
+ if (rm >= 0xc0 ) {GetEArd;TESTD(*eard,Fetchd(),LoadRd,SaveRd);}
+ else {GetEAa;TESTD(eaa,Fetchd(),LoadMd,SaveMd);}
+ break;
+ }
+ case 0x02: /* NOT Ed */
+ {
+ if (rm >= 0xc0 ) {GetEArd;*eard=~*eard;}
+ else {GetEAa;SaveMd(eaa,~LoadMd(eaa));}
+ break;
+ }
+ case 0x03: /* NEG Ed */
+ {
+ lflags.type=t_NEGd;
+ if (rm >= 0xc0 ) {
+ GetEArd;lf_var1d=*eard;lf_resd=0-lf_var1d;
+ *eard=lf_resd;
+ } else {
+ GetEAa;lf_var1d=LoadMd(eaa);lf_resd=0-lf_var1d;
+ SaveMd(eaa,lf_resd);
+ }
+ break;
+ }
+ case 0x04: /* MUL EAX,Ed */
+ RMEd(MULD);
+ break;
+ case 0x05: /* IMUL EAX,Ed */
+ RMEd(IMULD);
+ break;
+ case 0x06: /* DIV Ed */
+ RMEd(DIVD);
+ break;
+ case 0x07: /* IDIV Ed */
+ RMEd(IDIVD);
+ break;
+ }
+ break;
+ }
+ CASE_D(0xff) /* GRP 5 Ed */
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ switch (which) {
+ case 0x00: /* INC Ed */
+ RMEd(INCD);
+ break;
+ case 0x01: /* DEC Ed */
+ RMEd(DECD);
+ break;
+ case 0x02: /* CALL NEAR Ed */
+ if (rm >= 0xc0 ) {GetEArd;reg_eip=*eard;}
+ else {GetEAa;reg_eip=LoadMd(eaa);}
+ Push_32(GETIP);
+ continue;
+ case 0x03: /* CALL FAR Ed */
+ {
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ Bit32u newip=LoadMd(eaa);
+ Bit16u newcs=LoadMw(eaa+4);
+ FillFlags();
+ CPU_CALL(true,newcs,newip,GETIP);
+#if CPU_TRAP_CHECK
+ if (GETFLAG(TF)) {
+ cpudecoder=CPU_TRAP_DECODER;
+ return CBRET_NONE;
+ }
+#endif
+ continue;
+ }
+ case 0x04: /* JMP NEAR Ed */
+ if (rm >= 0xc0 ) {GetEArd;reg_eip=*eard;}
+ else {GetEAa;reg_eip=LoadMd(eaa);}
+ continue;
+ case 0x05: /* JMP FAR Ed */
+ {
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ Bit32u newip=LoadMd(eaa);
+ Bit16u newcs=LoadMw(eaa+4);
+ FillFlags();
+ CPU_JMP(true,newcs,newip,GETIP);
+#if CPU_TRAP_CHECK
+ if (GETFLAG(TF)) {
+ cpudecoder=CPU_TRAP_DECODER;
+ return CBRET_NONE;
+ }
+#endif
+ continue;
+ }
+ break;
+ case 0x06: /* Push Ed */
+ if (rm >= 0xc0 ) {GetEArd;Push_32(*eard);}
+ else {GetEAa;Push_32(LoadMd(eaa));}
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("CPU:66:GRP5:Illegal call %2X",which);
+ goto illegal_opcode;
+ }
+ break;
+ }
+
+
diff --git a/src/cpu/core_normal/prefix_66_0f.h b/src/cpu/core_normal/prefix_66_0f.h
new file mode 100644
index 000000000..38966aa7d
--- /dev/null
+++ b/src/cpu/core_normal/prefix_66_0f.h
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+ CASE_0F_D(0x00) /* GRP 6 Exxx */
+ {
+ if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegal_opcode;
+ GetRM;Bitu which=(rm>>3)&7;
+ switch (which) {
+ case 0x00: /* SLDT */
+ case 0x01: /* STR */
+ {
+ Bitu saveval;
+ if (!which) saveval=CPU_SLDT();
+ else saveval=CPU_STR();
+ if (rm >= 0xc0) {GetEArw;*earw=(Bit16u)saveval;}
+ else {GetEAa;SaveMw(eaa,saveval);}
+ }
+ break;
+ case 0x02:case 0x03:case 0x04:case 0x05:
+ {
+ /* Just use 16-bit loads since were only using selectors */
+ Bitu loadval;
+ if (rm >= 0xc0 ) {GetEArw;loadval=*earw;}
+ else {GetEAa;loadval=LoadMw(eaa);}
+ switch (which) {
+ case 0x02:
+ if (cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ if (CPU_LLDT(loadval)) RUNEXCEPTION();
+ break;
+ case 0x03:
+ if (cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ if (CPU_LTR(loadval)) RUNEXCEPTION();
+ break;
+ case 0x04:
+ CPU_VERR(loadval);
+ break;
+ case 0x05:
+ CPU_VERW(loadval);
+ break;
+ }
+ }
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("GRP6:Illegal call %2X",which);
+ goto illegal_opcode;
+ }
+ }
+ break;
+ CASE_0F_D(0x01) /* Group 7 Ed */
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ if (rm < 0xc0) { //First ones all use EA
+ GetEAa;Bitu limit;
+ switch (which) {
+ case 0x00: /* SGDT */
+ SaveMw(eaa,(Bit16u)CPU_SGDT_limit());
+ SaveMd(eaa+2,(Bit32u)CPU_SGDT_base());
+ break;
+ case 0x01: /* SIDT */
+ SaveMw(eaa,(Bit16u)CPU_SIDT_limit());
+ SaveMd(eaa+2,(Bit32u)CPU_SIDT_base());
+ break;
+ case 0x02: /* LGDT */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ CPU_LGDT(LoadMw(eaa),LoadMd(eaa+2));
+ break;
+ case 0x03: /* LIDT */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ CPU_LIDT(LoadMw(eaa),LoadMd(eaa+2));
+ break;
+ case 0x04: /* SMSW */
+ SaveMw(eaa,(Bit16u)CPU_SMSW());
+ break;
+ case 0x06: /* LMSW */
+ limit=LoadMw(eaa);
+ if (CPU_LMSW((Bit16u)limit)) RUNEXCEPTION();
+ break;
+ case 0x07: /* INVLPG */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ PAGING_ClearTLB();
+ break;
+ }
+ } else {
+ GetEArd;
+ switch (which) {
+ case 0x02: /* LGDT */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ goto illegal_opcode;
+ case 0x03: /* LIDT */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ goto illegal_opcode;
+ case 0x04: /* SMSW */
+ *eard=(Bit32u)CPU_SMSW();
+ break;
+ case 0x06: /* LMSW */
+ if (CPU_LMSW(*eard)) RUNEXCEPTION();
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Illegal group 7 RM subfunction %d",which);
+ goto illegal_opcode;
+ break;
+ }
+
+ }
+ }
+ break;
+ CASE_0F_D(0x02) /* LAR Gd,Ed */
+ {
+ if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegal_opcode;
+ GetRMrd;Bitu ar=*rmrd;
+ if (rm >= 0xc0) {
+ GetEArw;CPU_LAR(*earw,ar);
+ } else {
+ GetEAa;CPU_LAR(LoadMw(eaa),ar);
+ }
+ *rmrd=(Bit32u)ar;
+ }
+ break;
+ CASE_0F_D(0x03) /* LSL Gd,Ew */
+ {
+ if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegal_opcode;
+ GetRMrd;Bitu limit=*rmrd;
+ /* Just load 16-bit values for selectors */
+ if (rm >= 0xc0) {
+ GetEArw;CPU_LSL(*earw,limit);
+ } else {
+ GetEAa;CPU_LSL(LoadMw(eaa),limit);
+ }
+ *rmrd=(Bit32u)limit;
+ }
+ break;
+ CASE_0F_D(0x80) /* JO */
+ JumpCond32_d(TFLG_O);break;
+ CASE_0F_D(0x81) /* JNO */
+ JumpCond32_d(TFLG_NO);break;
+ CASE_0F_D(0x82) /* JB */
+ JumpCond32_d(TFLG_B);break;
+ CASE_0F_D(0x83) /* JNB */
+ JumpCond32_d(TFLG_NB);break;
+ CASE_0F_D(0x84) /* JZ */
+ JumpCond32_d(TFLG_Z);break;
+ CASE_0F_D(0x85) /* JNZ */
+ JumpCond32_d(TFLG_NZ);break;
+ CASE_0F_D(0x86) /* JBE */
+ JumpCond32_d(TFLG_BE);break;
+ CASE_0F_D(0x87) /* JNBE */
+ JumpCond32_d(TFLG_NBE);break;
+ CASE_0F_D(0x88) /* JS */
+ JumpCond32_d(TFLG_S);break;
+ CASE_0F_D(0x89) /* JNS */
+ JumpCond32_d(TFLG_NS);break;
+ CASE_0F_D(0x8a) /* JP */
+ JumpCond32_d(TFLG_P);break;
+ CASE_0F_D(0x8b) /* JNP */
+ JumpCond32_d(TFLG_NP);break;
+ CASE_0F_D(0x8c) /* JL */
+ JumpCond32_d(TFLG_L);break;
+ CASE_0F_D(0x8d) /* JNL */
+ JumpCond32_d(TFLG_NL);break;
+ CASE_0F_D(0x8e) /* JLE */
+ JumpCond32_d(TFLG_LE);break;
+ CASE_0F_D(0x8f) /* JNLE */
+ JumpCond32_d(TFLG_NLE);break;
+
+ CASE_0F_D(0xa0) /* PUSH FS */
+ Push_32(SegValue(fs));break;
+ CASE_0F_D(0xa1) /* POP FS */
+ if (CPU_PopSeg(fs,true)) RUNEXCEPTION();
+ break;
+ CASE_0F_D(0xa3) /* BT Ed,Gd */
+ {
+ FillFlags();GetRMrd;
+ Bit32u mask=1 << (*rmrd & 31);
+ if (rm >= 0xc0 ) {
+ GetEArd;
+ SETFLAGBIT(CF,(*eard & mask));
+ } else {
+ GetEAa;eaa+=(((Bit32s)*rmrd)>>5)*4;
+ Bit32u old=LoadMd(eaa);
+ SETFLAGBIT(CF,(old & mask));
+ }
+ break;
+ }
+ CASE_0F_D(0xa4) /* SHLD Ed,Gd,Ib */
+ RMEdGdOp3(DSHLD,Fetchb());
+ break;
+ CASE_0F_D(0xa5) /* SHLD Ed,Gd,CL */
+ RMEdGdOp3(DSHLD,reg_cl);
+ break;
+ CASE_0F_D(0xa8) /* PUSH GS */
+ Push_32(SegValue(gs));break;
+ CASE_0F_D(0xa9) /* POP GS */
+ if (CPU_PopSeg(gs,true)) RUNEXCEPTION();
+ break;
+ CASE_0F_D(0xab) /* BTS Ed,Gd */
+ {
+ FillFlags();GetRMrd;
+ Bit32u mask=1 << (*rmrd & 31);
+ if (rm >= 0xc0 ) {
+ GetEArd;
+ SETFLAGBIT(CF,(*eard & mask));
+ *eard|=mask;
+ } else {
+ GetEAa;eaa+=(((Bit32s)*rmrd)>>5)*4;
+ Bit32u old=LoadMd(eaa);
+ SETFLAGBIT(CF,(old & mask));
+ SaveMd(eaa,old | mask);
+ }
+ break;
+ }
+
+ CASE_0F_D(0xac) /* SHRD Ed,Gd,Ib */
+ RMEdGdOp3(DSHRD,Fetchb());
+ break;
+ CASE_0F_D(0xad) /* SHRD Ed,Gd,CL */
+ RMEdGdOp3(DSHRD,reg_cl);
+ break;
+ CASE_0F_D(0xaf) /* IMUL Gd,Ed */
+ {
+ RMGdEdOp3(DIMULD,*rmrd);
+ break;
+ }
+ CASE_0F_D(0xb1) /* CMPXCHG Ed,Gd */
+ {
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486NEWSLOW) goto illegal_opcode;
+ FillFlags();
+ GetRMrd;
+ if (rm >= 0xc0) {
+ GetEArd;
+ if (*eard==reg_eax) {
+ *eard=*rmrd;
+ SETFLAGBIT(ZF,1);
+ } else {
+ reg_eax=*eard;
+ SETFLAGBIT(ZF,0);
+ }
+ } else {
+ GetEAa;
+ Bit32u val=LoadMd(eaa);
+ if (val==reg_eax) {
+ SaveMd(eaa,*rmrd);
+ SETFLAGBIT(ZF,1);
+ } else {
+ SaveMd(eaa,val); // cmpxchg always issues a write
+ reg_eax=val;
+ SETFLAGBIT(ZF,0);
+ }
+ }
+ break;
+ }
+ CASE_0F_D(0xb2) /* LSS Ed */
+ {
+ GetRMrd;
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ if (CPU_SetSegGeneral(ss,LoadMw(eaa+4))) RUNEXCEPTION();
+ *rmrd=LoadMd(eaa);
+ break;
+ }
+ CASE_0F_D(0xb3) /* BTR Ed,Gd */
+ {
+ FillFlags();GetRMrd;
+ Bit32u mask=1 << (*rmrd & 31);
+ if (rm >= 0xc0 ) {
+ GetEArd;
+ SETFLAGBIT(CF,(*eard & mask));
+ *eard&= ~mask;
+ } else {
+ GetEAa;eaa+=(((Bit32s)*rmrd)>>5)*4;
+ Bit32u old=LoadMd(eaa);
+ SETFLAGBIT(CF,(old & mask));
+ SaveMd(eaa,old & ~mask);
+ }
+ break;
+ }
+ CASE_0F_D(0xb4) /* LFS Ed */
+ {
+ GetRMrd;
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ if (CPU_SetSegGeneral(fs,LoadMw(eaa+4))) RUNEXCEPTION();
+ *rmrd=LoadMd(eaa);
+ break;
+ }
+ CASE_0F_D(0xb5) /* LGS Ed */
+ {
+ GetRMrd;
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ if (CPU_SetSegGeneral(gs,LoadMw(eaa+4))) RUNEXCEPTION();
+ *rmrd=LoadMd(eaa);
+ break;
+ }
+ CASE_0F_D(0xb6) /* MOVZX Gd,Eb */
+ {
+ GetRMrd;
+ if (rm >= 0xc0 ) {GetEArb;*rmrd=*earb;}
+ else {GetEAa;*rmrd=LoadMb(eaa);}
+ break;
+ }
+ CASE_0F_D(0xb7) /* MOVXZ Gd,Ew */
+ {
+ GetRMrd;
+ if (rm >= 0xc0 ) {GetEArw;*rmrd=*earw;}
+ else {GetEAa;*rmrd=LoadMw(eaa);}
+ break;
+ }
+ CASE_0F_D(0xba) /* GRP8 Ed,Ib */
+ {
+ FillFlags();GetRM;
+ if (rm >= 0xc0 ) {
+ GetEArd;
+ Bit32u mask=1 << (Fetchb() & 31);
+ SETFLAGBIT(CF,(*eard & mask));
+ switch (rm & 0x38) {
+ case 0x20: /* BT */
+ break;
+ case 0x28: /* BTS */
+ *eard|=mask;
+ break;
+ case 0x30: /* BTR */
+ *eard&=~mask;
+ break;
+ case 0x38: /* BTC */
+ if (GETFLAG(CF)) *eard&=~mask;
+ else *eard|=mask;
+ break;
+ default:
+ E_Exit("CPU:66:0F:BA:Illegal subfunction %X",rm & 0x38);
+ }
+ } else {
+ GetEAa;Bit32u old=LoadMd(eaa);
+ Bit32u mask=1 << (Fetchb() & 31);
+ SETFLAGBIT(CF,(old & mask));
+ switch (rm & 0x38) {
+ case 0x20: /* BT */
+ break;
+ case 0x28: /* BTS */
+ SaveMd(eaa,old|mask);
+ break;
+ case 0x30: /* BTR */
+ SaveMd(eaa,old & ~mask);
+ break;
+ case 0x38: /* BTC */
+ if (GETFLAG(CF)) old&=~mask;
+ else old|=mask;
+ SaveMd(eaa,old);
+ break;
+ default:
+ E_Exit("CPU:66:0F:BA:Illegal subfunction %X",rm & 0x38);
+ }
+ }
+ break;
+ }
+ CASE_0F_D(0xbb) /* BTC Ed,Gd */
+ {
+ FillFlags();GetRMrd;
+ Bit32u mask=1 << (*rmrd & 31);
+ if (rm >= 0xc0 ) {
+ GetEArd;
+ SETFLAGBIT(CF,(*eard & mask));
+ *eard^=mask;
+ } else {
+ GetEAa;eaa+=(((Bit32s)*rmrd)>>5)*4;
+ Bit32u old=LoadMd(eaa);
+ SETFLAGBIT(CF,(old & mask));
+ SaveMd(eaa,old ^ mask);
+ }
+ break;
+ }
+ CASE_0F_D(0xbc) /* BSF Gd,Ed */
+ {
+ GetRMrd;
+ Bit32u result,value;
+ if (rm >= 0xc0) { GetEArd; value=*eard; }
+ else { GetEAa; value=LoadMd(eaa); }
+ if (value==0) {
+ SETFLAGBIT(ZF,true);
+ } else {
+ result = 0;
+ while ((value & 0x01)==0) { result++; value>>=1; }
+ SETFLAGBIT(ZF,false);
+ *rmrd = result;
+ }
+ lflags.type=t_UNKNOWN;
+ break;
+ }
+ CASE_0F_D(0xbd) /* BSR Gd,Ed */
+ {
+ GetRMrd;
+ Bit32u result,value;
+ if (rm >= 0xc0) { GetEArd; value=*eard; }
+ else { GetEAa; value=LoadMd(eaa); }
+ if (value==0) {
+ SETFLAGBIT(ZF,true);
+ } else {
+ result = 31; // Operandsize-1
+ while ((value & 0x80000000)==0) { result--; value<<=1; }
+ SETFLAGBIT(ZF,false);
+ *rmrd = result;
+ }
+ lflags.type=t_UNKNOWN;
+ break;
+ }
+ CASE_0F_D(0xbe) /* MOVSX Gd,Eb */
+ {
+ GetRMrd;
+ if (rm >= 0xc0 ) {GetEArb;*rmrd=*(Bit8s *)earb;}
+ else {GetEAa;*rmrd=LoadMbs(eaa);}
+ break;
+ }
+ CASE_0F_D(0xbf) /* MOVSX Gd,Ew */
+ {
+ GetRMrd;
+ if (rm >= 0xc0 ) {GetEArw;*rmrd=*(Bit16s *)earw;}
+ else {GetEAa;*rmrd=LoadMws(eaa);}
+ break;
+ }
+ CASE_0F_D(0xc1) /* XADD Gd,Ed */
+ {
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ GetRMrd;Bit32u oldrmrd=*rmrd;
+ if (rm >= 0xc0 ) {GetEArd;*rmrd=*eard;*eard+=oldrmrd;}
+ else {GetEAa;*rmrd=LoadMd(eaa);SaveMd(eaa,LoadMd(eaa)+oldrmrd);}
+ break;
+ }
+ CASE_0F_D(0xc8) /* BSWAP EAX */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPD(reg_eax);break;
+ CASE_0F_D(0xc9) /* BSWAP ECX */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPD(reg_ecx);break;
+ CASE_0F_D(0xca) /* BSWAP EDX */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPD(reg_edx);break;
+ CASE_0F_D(0xcb) /* BSWAP EBX */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPD(reg_ebx);break;
+ CASE_0F_D(0xcc) /* BSWAP ESP */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPD(reg_esp);break;
+ CASE_0F_D(0xcd) /* BSWAP EBP */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPD(reg_ebp);break;
+ CASE_0F_D(0xce) /* BSWAP ESI */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPD(reg_esi);break;
+ CASE_0F_D(0xcf) /* BSWAP EDI */
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) goto illegal_opcode;
+ BSWAPD(reg_edi);break;
diff --git a/src/cpu/core_normal/prefix_none.h b/src/cpu/core_normal/prefix_none.h
new file mode 100644
index 000000000..5d59e80c9
--- /dev/null
+++ b/src/cpu/core_normal/prefix_none.h
@@ -0,0 +1,1176 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+ CASE_B(0x00) /* ADD Eb,Gb */
+ RMEbGb(ADDB);break;
+ CASE_W(0x01) /* ADD Ew,Gw */
+ RMEwGw(ADDW);break;
+ CASE_B(0x02) /* ADD Gb,Eb */
+ RMGbEb(ADDB);break;
+ CASE_W(0x03) /* ADD Gw,Ew */
+ RMGwEw(ADDW);break;
+ CASE_B(0x04) /* ADD AL,Ib */
+ ALIb(ADDB);break;
+ CASE_W(0x05) /* ADD AX,Iw */
+ AXIw(ADDW);break;
+ CASE_W(0x06) /* PUSH ES */
+ Push_16(SegValue(es));break;
+ CASE_W(0x07) /* POP ES */
+ if (CPU_PopSeg(es,false)) RUNEXCEPTION();
+ break;
+ CASE_B(0x08) /* OR Eb,Gb */
+ RMEbGb(ORB);break;
+ CASE_W(0x09) /* OR Ew,Gw */
+ RMEwGw(ORW);break;
+ CASE_B(0x0a) /* OR Gb,Eb */
+ RMGbEb(ORB);break;
+ CASE_W(0x0b) /* OR Gw,Ew */
+ RMGwEw(ORW);break;
+ CASE_B(0x0c) /* OR AL,Ib */
+ ALIb(ORB);break;
+ CASE_W(0x0d) /* OR AX,Iw */
+ AXIw(ORW);break;
+ CASE_W(0x0e) /* PUSH CS */
+ Push_16(SegValue(cs));break;
+ CASE_B(0x0f) /* 2 byte opcodes*/
+ core.opcode_index|=OPCODE_0F;
+ goto restart_opcode;
+ break;
+ CASE_B(0x10) /* ADC Eb,Gb */
+ RMEbGb(ADCB);break;
+ CASE_W(0x11) /* ADC Ew,Gw */
+ RMEwGw(ADCW);break;
+ CASE_B(0x12) /* ADC Gb,Eb */
+ RMGbEb(ADCB);break;
+ CASE_W(0x13) /* ADC Gw,Ew */
+ RMGwEw(ADCW);break;
+ CASE_B(0x14) /* ADC AL,Ib */
+ ALIb(ADCB);break;
+ CASE_W(0x15) /* ADC AX,Iw */
+ AXIw(ADCW);break;
+ CASE_W(0x16) /* PUSH SS */
+ Push_16(SegValue(ss));break;
+ CASE_W(0x17) /* POP SS */
+ if (CPU_PopSeg(ss,false)) RUNEXCEPTION();
+ CPU_Cycles++; //Always do another instruction
+ break;
+ CASE_B(0x18) /* SBB Eb,Gb */
+ RMEbGb(SBBB);break;
+ CASE_W(0x19) /* SBB Ew,Gw */
+ RMEwGw(SBBW);break;
+ CASE_B(0x1a) /* SBB Gb,Eb */
+ RMGbEb(SBBB);break;
+ CASE_W(0x1b) /* SBB Gw,Ew */
+ RMGwEw(SBBW);break;
+ CASE_B(0x1c) /* SBB AL,Ib */
+ ALIb(SBBB);break;
+ CASE_W(0x1d) /* SBB AX,Iw */
+ AXIw(SBBW);break;
+ CASE_W(0x1e) /* PUSH DS */
+ Push_16(SegValue(ds));break;
+ CASE_W(0x1f) /* POP DS */
+ if (CPU_PopSeg(ds,false)) RUNEXCEPTION();
+ break;
+ CASE_B(0x20) /* AND Eb,Gb */
+ RMEbGb(ANDB);break;
+ CASE_W(0x21) /* AND Ew,Gw */
+ RMEwGw(ANDW);break;
+ CASE_B(0x22) /* AND Gb,Eb */
+ RMGbEb(ANDB);break;
+ CASE_W(0x23) /* AND Gw,Ew */
+ RMGwEw(ANDW);break;
+ CASE_B(0x24) /* AND AL,Ib */
+ ALIb(ANDB);break;
+ CASE_W(0x25) /* AND AX,Iw */
+ AXIw(ANDW);break;
+ CASE_B(0x26) /* SEG ES: */
+ DO_PREFIX_SEG(es);break;
+ CASE_B(0x27) /* DAA */
+ DAA();break;
+ CASE_B(0x28) /* SUB Eb,Gb */
+ RMEbGb(SUBB);break;
+ CASE_W(0x29) /* SUB Ew,Gw */
+ RMEwGw(SUBW);break;
+ CASE_B(0x2a) /* SUB Gb,Eb */
+ RMGbEb(SUBB);break;
+ CASE_W(0x2b) /* SUB Gw,Ew */
+ RMGwEw(SUBW);break;
+ CASE_B(0x2c) /* SUB AL,Ib */
+ ALIb(SUBB);break;
+ CASE_W(0x2d) /* SUB AX,Iw */
+ AXIw(SUBW);break;
+ CASE_B(0x2e) /* SEG CS: */
+ DO_PREFIX_SEG(cs);break;
+ CASE_B(0x2f) /* DAS */
+ DAS();break;
+ CASE_B(0x30) /* XOR Eb,Gb */
+ RMEbGb(XORB);break;
+ CASE_W(0x31) /* XOR Ew,Gw */
+ RMEwGw(XORW);break;
+ CASE_B(0x32) /* XOR Gb,Eb */
+ RMGbEb(XORB);break;
+ CASE_W(0x33) /* XOR Gw,Ew */
+ RMGwEw(XORW);break;
+ CASE_B(0x34) /* XOR AL,Ib */
+ ALIb(XORB);break;
+ CASE_W(0x35) /* XOR AX,Iw */
+ AXIw(XORW);break;
+ CASE_B(0x36) /* SEG SS: */
+ DO_PREFIX_SEG(ss);break;
+ CASE_B(0x37) /* AAA */
+ AAA();break;
+ CASE_B(0x38) /* CMP Eb,Gb */
+ RMEbGb(CMPB);break;
+ CASE_W(0x39) /* CMP Ew,Gw */
+ RMEwGw(CMPW);break;
+ CASE_B(0x3a) /* CMP Gb,Eb */
+ RMGbEb(CMPB);break;
+ CASE_W(0x3b) /* CMP Gw,Ew */
+ RMGwEw(CMPW);break;
+ CASE_B(0x3c) /* CMP AL,Ib */
+ ALIb(CMPB);break;
+ CASE_W(0x3d) /* CMP AX,Iw */
+ AXIw(CMPW);break;
+ CASE_B(0x3e) /* SEG DS: */
+ DO_PREFIX_SEG(ds);break;
+ CASE_B(0x3f) /* AAS */
+ AAS();break;
+ CASE_W(0x40) /* INC AX */
+ INCW(reg_ax,LoadRw,SaveRw);break;
+ CASE_W(0x41) /* INC CX */
+ INCW(reg_cx,LoadRw,SaveRw);break;
+ CASE_W(0x42) /* INC DX */
+ INCW(reg_dx,LoadRw,SaveRw);break;
+ CASE_W(0x43) /* INC BX */
+ INCW(reg_bx,LoadRw,SaveRw);break;
+ CASE_W(0x44) /* INC SP */
+ INCW(reg_sp,LoadRw,SaveRw);break;
+ CASE_W(0x45) /* INC BP */
+ INCW(reg_bp,LoadRw,SaveRw);break;
+ CASE_W(0x46) /* INC SI */
+ INCW(reg_si,LoadRw,SaveRw);break;
+ CASE_W(0x47) /* INC DI */
+ INCW(reg_di,LoadRw,SaveRw);break;
+ CASE_W(0x48) /* DEC AX */
+ DECW(reg_ax,LoadRw,SaveRw);break;
+ CASE_W(0x49) /* DEC CX */
+ DECW(reg_cx,LoadRw,SaveRw);break;
+ CASE_W(0x4a) /* DEC DX */
+ DECW(reg_dx,LoadRw,SaveRw);break;
+ CASE_W(0x4b) /* DEC BX */
+ DECW(reg_bx,LoadRw,SaveRw);break;
+ CASE_W(0x4c) /* DEC SP */
+ DECW(reg_sp,LoadRw,SaveRw);break;
+ CASE_W(0x4d) /* DEC BP */
+ DECW(reg_bp,LoadRw,SaveRw);break;
+ CASE_W(0x4e) /* DEC SI */
+ DECW(reg_si,LoadRw,SaveRw);break;
+ CASE_W(0x4f) /* DEC DI */
+ DECW(reg_di,LoadRw,SaveRw);break;
+ CASE_W(0x50) /* PUSH AX */
+ Push_16(reg_ax);break;
+ CASE_W(0x51) /* PUSH CX */
+ Push_16(reg_cx);break;
+ CASE_W(0x52) /* PUSH DX */
+ Push_16(reg_dx);break;
+ CASE_W(0x53) /* PUSH BX */
+ Push_16(reg_bx);break;
+ CASE_W(0x54) /* PUSH SP */
+ Push_16(reg_sp);break;
+ CASE_W(0x55) /* PUSH BP */
+ Push_16(reg_bp);break;
+ CASE_W(0x56) /* PUSH SI */
+ Push_16(reg_si);break;
+ CASE_W(0x57) /* PUSH DI */
+ Push_16(reg_di);break;
+ CASE_W(0x58) /* POP AX */
+ reg_ax=Pop_16();break;
+ CASE_W(0x59) /* POP CX */
+ reg_cx=Pop_16();break;
+ CASE_W(0x5a) /* POP DX */
+ reg_dx=Pop_16();break;
+ CASE_W(0x5b) /* POP BX */
+ reg_bx=Pop_16();break;
+ CASE_W(0x5c) /* POP SP */
+ reg_sp=Pop_16();break;
+ CASE_W(0x5d) /* POP BP */
+ reg_bp=Pop_16();break;
+ CASE_W(0x5e) /* POP SI */
+ reg_si=Pop_16();break;
+ CASE_W(0x5f) /* POP DI */
+ reg_di=Pop_16();break;
+ CASE_W(0x60) /* PUSHA */
+ {
+ Bit16u old_sp=reg_sp;
+ Push_16(reg_ax);Push_16(reg_cx);Push_16(reg_dx);Push_16(reg_bx);
+ Push_16(old_sp);Push_16(reg_bp);Push_16(reg_si);Push_16(reg_di);
+ }
+ break;
+ CASE_W(0x61) /* POPA */
+ reg_di=Pop_16();reg_si=Pop_16();reg_bp=Pop_16();Pop_16();//Don't save SP
+ reg_bx=Pop_16();reg_dx=Pop_16();reg_cx=Pop_16();reg_ax=Pop_16();
+ break;
+ CASE_W(0x62) /* BOUND */
+ {
+ Bit16s bound_min, bound_max;
+ GetRMrw;GetEAa;
+ bound_min=LoadMw(eaa);
+ bound_max=LoadMw(eaa+2);
+ if ( (((Bit16s)*rmrw) < bound_min) || (((Bit16s)*rmrw) > bound_max) ) {
+ EXCEPTION(5);
+ }
+ }
+ break;
+ CASE_W(0x63) /* ARPL Ew,Rw */
+ {
+ if ((reg_flags & FLAG_VM) || (!cpu.pmode)) goto illegal_opcode;
+ GetRMrw;
+ if (rm >= 0xc0 ) {
+ GetEArw;Bitu new_sel=*earw;
+ CPU_ARPL(new_sel,*rmrw);
+ *earw=(Bit16u)new_sel;
+ } else {
+ GetEAa;Bitu new_sel=LoadMw(eaa);
+ CPU_ARPL(new_sel,*rmrw);
+ SaveMw(eaa,(Bit16u)new_sel);
+ }
+ }
+ break;
+ CASE_B(0x64) /* SEG FS: */
+ DO_PREFIX_SEG(fs);break;
+ CASE_B(0x65) /* SEG GS: */
+ DO_PREFIX_SEG(gs);break;
+ CASE_B(0x66) /* Operand Size Prefix */
+ core.opcode_index=(cpu.code.big^0x1)*0x200;
+ goto restart_opcode;
+ CASE_B(0x67) /* Address Size Prefix */
+ DO_PREFIX_ADDR();
+ CASE_W(0x68) /* PUSH Iw */
+ Push_16(Fetchw());break;
+ CASE_W(0x69) /* IMUL Gw,Ew,Iw */
+ RMGwEwOp3(DIMULW,Fetchws());
+ break;
+ CASE_W(0x6a) /* PUSH Ib */
+ Push_16(Fetchbs());
+ break;
+ CASE_W(0x6b) /* IMUL Gw,Ew,Ib */
+ RMGwEwOp3(DIMULW,Fetchbs());
+ break;
+ CASE_B(0x6c) /* INSB */
+ if (CPU_IO_Exception(reg_dx,1)) RUNEXCEPTION();
+ DoString(R_INSB);break;
+ CASE_W(0x6d) /* INSW */
+ if (CPU_IO_Exception(reg_dx,2)) RUNEXCEPTION();
+ DoString(R_INSW);break;
+ CASE_B(0x6e) /* OUTSB */
+ if (CPU_IO_Exception(reg_dx,1)) RUNEXCEPTION();
+ DoString(R_OUTSB);break;
+ CASE_W(0x6f) /* OUTSW */
+ if (CPU_IO_Exception(reg_dx,2)) RUNEXCEPTION();
+ DoString(R_OUTSW);break;
+ CASE_W(0x70) /* JO */
+ JumpCond16_b(TFLG_O);break;
+ CASE_W(0x71) /* JNO */
+ JumpCond16_b(TFLG_NO);break;
+ CASE_W(0x72) /* JB */
+ JumpCond16_b(TFLG_B);break;
+ CASE_W(0x73) /* JNB */
+ JumpCond16_b(TFLG_NB);break;
+ CASE_W(0x74) /* JZ */
+ JumpCond16_b(TFLG_Z);break;
+ CASE_W(0x75) /* JNZ */
+ JumpCond16_b(TFLG_NZ);break;
+ CASE_W(0x76) /* JBE */
+ JumpCond16_b(TFLG_BE);break;
+ CASE_W(0x77) /* JNBE */
+ JumpCond16_b(TFLG_NBE);break;
+ CASE_W(0x78) /* JS */
+ JumpCond16_b(TFLG_S);break;
+ CASE_W(0x79) /* JNS */
+ JumpCond16_b(TFLG_NS);break;
+ CASE_W(0x7a) /* JP */
+ JumpCond16_b(TFLG_P);break;
+ CASE_W(0x7b) /* JNP */
+ JumpCond16_b(TFLG_NP);break;
+ CASE_W(0x7c) /* JL */
+ JumpCond16_b(TFLG_L);break;
+ CASE_W(0x7d) /* JNL */
+ JumpCond16_b(TFLG_NL);break;
+ CASE_W(0x7e) /* JLE */
+ JumpCond16_b(TFLG_LE);break;
+ CASE_W(0x7f) /* JNLE */
+ JumpCond16_b(TFLG_NLE);break;
+ CASE_B(0x80) /* Grpl Eb,Ib */
+ CASE_B(0x82) /* Grpl Eb,Ib Mirror instruction*/
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ if (rm>= 0xc0) {
+ GetEArb;Bit8u ib=Fetchb();
+ switch (which) {
+ case 0x00:ADDB(*earb,ib,LoadRb,SaveRb);break;
+ case 0x01: ORB(*earb,ib,LoadRb,SaveRb);break;
+ case 0x02:ADCB(*earb,ib,LoadRb,SaveRb);break;
+ case 0x03:SBBB(*earb,ib,LoadRb,SaveRb);break;
+ case 0x04:ANDB(*earb,ib,LoadRb,SaveRb);break;
+ case 0x05:SUBB(*earb,ib,LoadRb,SaveRb);break;
+ case 0x06:XORB(*earb,ib,LoadRb,SaveRb);break;
+ case 0x07:CMPB(*earb,ib,LoadRb,SaveRb);break;
+ }
+ } else {
+ GetEAa;Bit8u ib=Fetchb();
+ switch (which) {
+ case 0x00:ADDB(eaa,ib,LoadMb,SaveMb);break;
+ case 0x01: ORB(eaa,ib,LoadMb,SaveMb);break;
+ case 0x02:ADCB(eaa,ib,LoadMb,SaveMb);break;
+ case 0x03:SBBB(eaa,ib,LoadMb,SaveMb);break;
+ case 0x04:ANDB(eaa,ib,LoadMb,SaveMb);break;
+ case 0x05:SUBB(eaa,ib,LoadMb,SaveMb);break;
+ case 0x06:XORB(eaa,ib,LoadMb,SaveMb);break;
+ case 0x07:CMPB(eaa,ib,LoadMb,SaveMb);break;
+ }
+ }
+ break;
+ }
+ CASE_W(0x81) /* Grpl Ew,Iw */
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ if (rm>= 0xc0) {
+ GetEArw;Bit16u iw=Fetchw();
+ switch (which) {
+ case 0x00:ADDW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x01: ORW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x02:ADCW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x03:SBBW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x04:ANDW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x05:SUBW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x06:XORW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x07:CMPW(*earw,iw,LoadRw,SaveRw);break;
+ }
+ } else {
+ GetEAa;Bit16u iw=Fetchw();
+ switch (which) {
+ case 0x00:ADDW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x01: ORW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x02:ADCW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x03:SBBW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x04:ANDW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x05:SUBW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x06:XORW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x07:CMPW(eaa,iw,LoadMw,SaveMw);break;
+ }
+ }
+ break;
+ }
+ CASE_W(0x83) /* Grpl Ew,Ix */
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ if (rm>= 0xc0) {
+ GetEArw;Bit16u iw=(Bit16s)Fetchbs();
+ switch (which) {
+ case 0x00:ADDW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x01: ORW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x02:ADCW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x03:SBBW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x04:ANDW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x05:SUBW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x06:XORW(*earw,iw,LoadRw,SaveRw);break;
+ case 0x07:CMPW(*earw,iw,LoadRw,SaveRw);break;
+ }
+ } else {
+ GetEAa;Bit16u iw=(Bit16s)Fetchbs();
+ switch (which) {
+ case 0x00:ADDW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x01: ORW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x02:ADCW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x03:SBBW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x04:ANDW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x05:SUBW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x06:XORW(eaa,iw,LoadMw,SaveMw);break;
+ case 0x07:CMPW(eaa,iw,LoadMw,SaveMw);break;
+ }
+ }
+ break;
+ }
+ CASE_B(0x84) /* TEST Eb,Gb */
+ RMEbGb(TESTB);
+ break;
+ CASE_W(0x85) /* TEST Ew,Gw */
+ RMEwGw(TESTW);
+ break;
+ CASE_B(0x86) /* XCHG Eb,Gb */
+ {
+ GetRMrb;Bit8u oldrmrb=*rmrb;
+ if (rm >= 0xc0 ) {GetEArb;*rmrb=*earb;*earb=oldrmrb;}
+ else {GetEAa;*rmrb=LoadMb(eaa);SaveMb(eaa,oldrmrb);}
+ break;
+ }
+ CASE_W(0x87) /* XCHG Ew,Gw */
+ {
+ GetRMrw;Bit16u oldrmrw=*rmrw;
+ if (rm >= 0xc0 ) {GetEArw;*rmrw=*earw;*earw=oldrmrw;}
+ else {GetEAa;*rmrw=LoadMw(eaa);SaveMw(eaa,oldrmrw);}
+ break;
+ }
+ CASE_B(0x88) /* MOV Eb,Gb */
+ {
+ GetRMrb;
+ if (rm >= 0xc0 ) {GetEArb;*earb=*rmrb;}
+ else {
+ if (cpu.pmode) {
+ if (GCC_UNLIKELY((rm==0x05) && (!cpu.code.big))) {
+ Descriptor desc;
+ cpu.gdt.GetDescriptor(SegValue(core.base_val_ds),desc);
+ if ((desc.Type()==DESC_CODE_R_NC_A) || (desc.Type()==DESC_CODE_R_NC_NA)) {
+ CPU_Exception(EXCEPTION_GP,SegValue(core.base_val_ds) & 0xfffc);
+ continue;
+ }
+ }
+ }
+ GetEAa;SaveMb(eaa,*rmrb);
+ }
+ break;
+ }
+ CASE_W(0x89) /* MOV Ew,Gw */
+ {
+ GetRMrw;
+ if (rm >= 0xc0 ) {GetEArw;*earw=*rmrw;}
+ else {GetEAa;SaveMw(eaa,*rmrw);}
+ break;
+ }
+ CASE_B(0x8a) /* MOV Gb,Eb */
+ {
+ GetRMrb;
+ if (rm >= 0xc0 ) {GetEArb;*rmrb=*earb;}
+ else {GetEAa;*rmrb=LoadMb(eaa);}
+ break;
+ }
+ CASE_W(0x8b) /* MOV Gw,Ew */
+ {
+ GetRMrw;
+ if (rm >= 0xc0 ) {GetEArw;*rmrw=*earw;}
+ else {GetEAa;*rmrw=LoadMw(eaa);}
+ break;
+ }
+ CASE_W(0x8c) /* Mov Ew,Sw */
+ {
+ GetRM;Bit16u val;Bitu which=(rm>>3)&7;
+ switch (which) {
+ case 0x00: /* MOV Ew,ES */
+ val=SegValue(es);break;
+ case 0x01: /* MOV Ew,CS */
+ val=SegValue(cs);break;
+ case 0x02: /* MOV Ew,SS */
+ val=SegValue(ss);break;
+ case 0x03: /* MOV Ew,DS */
+ val=SegValue(ds);break;
+ case 0x04: /* MOV Ew,FS */
+ val=SegValue(fs);break;
+ case 0x05: /* MOV Ew,GS */
+ val=SegValue(gs);break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("CPU:8c:Illegal RM Byte");
+ goto illegal_opcode;
+ }
+ if (rm >= 0xc0 ) {GetEArw;*earw=val;}
+ else {GetEAa;SaveMw(eaa,val);}
+ break;
+ }
+ CASE_W(0x8d) /* LEA Gw */
+ {
+ //Little hack to always use segprefixed version
+ BaseDS=BaseSS=0;
+ GetRMrw;
+ if (TEST_PREFIX_ADDR) {
+ *rmrw=(Bit16u)(*EATable[256+rm])();
+ } else {
+ *rmrw=(Bit16u)(*EATable[rm])();
+ }
+ break;
+ }
+ CASE_B(0x8e) /* MOV Sw,Ew */
+ {
+ GetRM;Bit16u val;Bitu which=(rm>>3)&7;
+ if (rm >= 0xc0 ) {GetEArw;val=*earw;}
+ else {GetEAa;val=LoadMw(eaa);}
+ switch (which) {
+ case 0x02: /* MOV SS,Ew */
+ CPU_Cycles++; //Always do another instruction
+ case 0x00: /* MOV ES,Ew */
+ case 0x03: /* MOV DS,Ew */
+ case 0x05: /* MOV GS,Ew */
+ case 0x04: /* MOV FS,Ew */
+ if (CPU_SetSegGeneral((SegNames)which,val)) RUNEXCEPTION();
+ break;
+ default:
+ goto illegal_opcode;
+ }
+ break;
+ }
+ CASE_W(0x8f) /* POP Ew */
+ {
+ Bit16u val=Pop_16();
+ GetRM;
+ if (rm >= 0xc0 ) {GetEArw;*earw=val;}
+ else {GetEAa;SaveMw(eaa,val);}
+ break;
+ }
+ CASE_B(0x90) /* NOP */
+ break;
+ CASE_W(0x91) /* XCHG CX,AX */
+ { Bit16u temp=reg_ax;reg_ax=reg_cx;reg_cx=temp; }
+ break;
+ CASE_W(0x92) /* XCHG DX,AX */
+ { Bit16u temp=reg_ax;reg_ax=reg_dx;reg_dx=temp; }
+ break;
+ CASE_W(0x93) /* XCHG BX,AX */
+ { Bit16u temp=reg_ax;reg_ax=reg_bx;reg_bx=temp; }
+ break;
+ CASE_W(0x94) /* XCHG SP,AX */
+ { Bit16u temp=reg_ax;reg_ax=reg_sp;reg_sp=temp; }
+ break;
+ CASE_W(0x95) /* XCHG BP,AX */
+ { Bit16u temp=reg_ax;reg_ax=reg_bp;reg_bp=temp; }
+ break;
+ CASE_W(0x96) /* XCHG SI,AX */
+ { Bit16u temp=reg_ax;reg_ax=reg_si;reg_si=temp; }
+ break;
+ CASE_W(0x97) /* XCHG DI,AX */
+ { Bit16u temp=reg_ax;reg_ax=reg_di;reg_di=temp; }
+ break;
+ CASE_W(0x98) /* CBW */
+ reg_ax=(Bit8s)reg_al;break;
+ CASE_W(0x99) /* CWD */
+ if (reg_ax & 0x8000) reg_dx=0xffff;else reg_dx=0;
+ break;
+ CASE_W(0x9a) /* CALL Ap */
+ {
+ FillFlags();
+ Bit16u newip=Fetchw();Bit16u newcs=Fetchw();
+ CPU_CALL(false,newcs,newip,GETIP);
+#if CPU_TRAP_CHECK
+ if (GETFLAG(TF)) {
+ cpudecoder=CPU_TRAP_DECODER;
+ return CBRET_NONE;
+ }
+#endif
+ continue;
+ }
+ CASE_B(0x9b) /* WAIT */
+ break; /* No waiting here */
+ CASE_W(0x9c) /* PUSHF */
+ if (CPU_PUSHF(false)) RUNEXCEPTION();
+ break;
+ CASE_W(0x9d) /* POPF */
+ if (CPU_POPF(false)) RUNEXCEPTION();
+#if CPU_TRAP_CHECK
+ if (GETFLAG(TF)) {
+ cpudecoder=CPU_TRAP_DECODER;
+ goto decode_end;
+ }
+#endif
+#if CPU_PIC_CHECK
+ if (GETFLAG(IF) && PIC_IRQCheck) goto decode_end;
+#endif
+ break;
+ CASE_B(0x9e) /* SAHF */
+ SETFLAGSb(reg_ah);
+ break;
+ CASE_B(0x9f) /* LAHF */
+ FillFlags();
+ reg_ah=reg_flags&0xff;
+ break;
+ CASE_B(0xa0) /* MOV AL,Ob */
+ {
+ GetEADirect;
+ reg_al=LoadMb(eaa);
+ }
+ break;
+ CASE_W(0xa1) /* MOV AX,Ow */
+ {
+ GetEADirect;
+ reg_ax=LoadMw(eaa);
+ }
+ break;
+ CASE_B(0xa2) /* MOV Ob,AL */
+ {
+ GetEADirect;
+ SaveMb(eaa,reg_al);
+ }
+ break;
+ CASE_W(0xa3) /* MOV Ow,AX */
+ {
+ GetEADirect;
+ SaveMw(eaa,reg_ax);
+ }
+ break;
+ CASE_B(0xa4) /* MOVSB */
+ DoString(R_MOVSB);break;
+ CASE_W(0xa5) /* MOVSW */
+ DoString(R_MOVSW);break;
+ CASE_B(0xa6) /* CMPSB */
+ DoString(R_CMPSB);break;
+ CASE_W(0xa7) /* CMPSW */
+ DoString(R_CMPSW);break;
+ CASE_B(0xa8) /* TEST AL,Ib */
+ ALIb(TESTB);break;
+ CASE_W(0xa9) /* TEST AX,Iw */
+ AXIw(TESTW);break;
+ CASE_B(0xaa) /* STOSB */
+ DoString(R_STOSB);break;
+ CASE_W(0xab) /* STOSW */
+ DoString(R_STOSW);break;
+ CASE_B(0xac) /* LODSB */
+ DoString(R_LODSB);break;
+ CASE_W(0xad) /* LODSW */
+ DoString(R_LODSW);break;
+ CASE_B(0xae) /* SCASB */
+ DoString(R_SCASB);break;
+ CASE_W(0xaf) /* SCASW */
+ DoString(R_SCASW);break;
+ CASE_B(0xb0) /* MOV AL,Ib */
+ reg_al=Fetchb();break;
+ CASE_B(0xb1) /* MOV CL,Ib */
+ reg_cl=Fetchb();break;
+ CASE_B(0xb2) /* MOV DL,Ib */
+ reg_dl=Fetchb();break;
+ CASE_B(0xb3) /* MOV BL,Ib */
+ reg_bl=Fetchb();break;
+ CASE_B(0xb4) /* MOV AH,Ib */
+ reg_ah=Fetchb();break;
+ CASE_B(0xb5) /* MOV CH,Ib */
+ reg_ch=Fetchb();break;
+ CASE_B(0xb6) /* MOV DH,Ib */
+ reg_dh=Fetchb();break;
+ CASE_B(0xb7) /* MOV BH,Ib */
+ reg_bh=Fetchb();break;
+ CASE_W(0xb8) /* MOV AX,Iw */
+ reg_ax=Fetchw();break;
+ CASE_W(0xb9) /* MOV CX,Iw */
+ reg_cx=Fetchw();break;
+ CASE_W(0xba) /* MOV DX,Iw */
+ reg_dx=Fetchw();break;
+ CASE_W(0xbb) /* MOV BX,Iw */
+ reg_bx=Fetchw();break;
+ CASE_W(0xbc) /* MOV SP,Iw */
+ reg_sp=Fetchw();break;
+ CASE_W(0xbd) /* MOV BP.Iw */
+ reg_bp=Fetchw();break;
+ CASE_W(0xbe) /* MOV SI,Iw */
+ reg_si=Fetchw();break;
+ CASE_W(0xbf) /* MOV DI,Iw */
+ reg_di=Fetchw();break;
+ CASE_B(0xc0) /* GRP2 Eb,Ib */
+ GRP2B(Fetchb());break;
+ CASE_W(0xc1) /* GRP2 Ew,Ib */
+ GRP2W(Fetchb());break;
+ CASE_W(0xc2) /* RETN Iw */
+ reg_eip=Pop_16();
+ reg_esp+=Fetchw();
+ continue;
+ CASE_W(0xc3) /* RETN */
+ reg_eip=Pop_16();
+ continue;
+ CASE_W(0xc4) /* LES */
+ {
+ GetRMrw;
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ if (CPU_SetSegGeneral(es,LoadMw(eaa+2))) RUNEXCEPTION();
+ *rmrw=LoadMw(eaa);
+ break;
+ }
+ CASE_W(0xc5) /* LDS */
+ {
+ GetRMrw;
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ if (CPU_SetSegGeneral(ds,LoadMw(eaa+2))) RUNEXCEPTION();
+ *rmrw=LoadMw(eaa);
+ break;
+ }
+ CASE_B(0xc6) /* MOV Eb,Ib */
+ {
+ GetRM;
+ if (rm >= 0xc0) {GetEArb;*earb=Fetchb();}
+ else {GetEAa;SaveMb(eaa,Fetchb());}
+ break;
+ }
+ CASE_W(0xc7) /* MOV EW,Iw */
+ {
+ GetRM;
+ if (rm >= 0xc0) {GetEArw;*earw=Fetchw();}
+ else {GetEAa;SaveMw(eaa,Fetchw());}
+ break;
+ }
+ CASE_W(0xc8) /* ENTER Iw,Ib */
+ {
+ Bitu bytes=Fetchw();
+ Bitu level=Fetchb();
+ CPU_ENTER(false,bytes,level);
+ }
+ break;
+ CASE_W(0xc9) /* LEAVE */
+ reg_esp&=cpu.stack.notmask;
+ reg_esp|=(reg_ebp&cpu.stack.mask);
+ reg_bp=Pop_16();
+ break;
+ CASE_W(0xca) /* RETF Iw */
+ {
+ Bitu words=Fetchw();
+ FillFlags();
+ CPU_RET(false,words,GETIP);
+ continue;
+ }
+ CASE_W(0xcb) /* RETF */
+ FillFlags();
+ CPU_RET(false,0,GETIP);
+ continue;
+ CASE_B(0xcc) /* INT3 */
+#if C_DEBUG
+ FillFlags();
+ if (DEBUG_Breakpoint())
+ return debugCallback;
+#endif
+ CPU_SW_Interrupt_NoIOPLCheck(3,GETIP);
+#if CPU_TRAP_CHECK
+ cpu.trap_skip=true;
+#endif
+ continue;
+ CASE_B(0xcd) /* INT Ib */
+ {
+ Bit8u num=Fetchb();
+#if C_DEBUG
+ FillFlags();
+ if (DEBUG_IntBreakpoint(num)) {
+ return debugCallback;
+ }
+#endif
+ CPU_SW_Interrupt(num,GETIP);
+#if CPU_TRAP_CHECK
+ cpu.trap_skip=true;
+#endif
+ continue;
+ }
+ CASE_B(0xce) /* INTO */
+ if (get_OF()) {
+ CPU_SW_Interrupt(4,GETIP);
+#if CPU_TRAP_CHECK
+ cpu.trap_skip=true;
+#endif
+ continue;
+ }
+ break;
+ CASE_W(0xcf) /* IRET */
+ {
+ CPU_IRET(false,GETIP);
+#if CPU_TRAP_CHECK
+ if (GETFLAG(TF)) {
+ cpudecoder=CPU_TRAP_DECODER;
+ return CBRET_NONE;
+ }
+#endif
+#if CPU_PIC_CHECK
+ if (GETFLAG(IF) && PIC_IRQCheck) return CBRET_NONE;
+#endif
+ continue;
+ }
+ CASE_B(0xd0) /* GRP2 Eb,1 */
+ GRP2B(1);break;
+ CASE_W(0xd1) /* GRP2 Ew,1 */
+ GRP2W(1);break;
+ CASE_B(0xd2) /* GRP2 Eb,CL */
+ GRP2B(reg_cl);break;
+ CASE_W(0xd3) /* GRP2 Ew,CL */
+ GRP2W(reg_cl);break;
+ CASE_B(0xd4) /* AAM Ib */
+ AAM(Fetchb());break;
+ CASE_B(0xd5) /* AAD Ib */
+ AAD(Fetchb());break;
+ CASE_B(0xd6) /* SALC */
+ reg_al = get_CF() ? 0xFF : 0;
+ break;
+ CASE_B(0xd7) /* XLAT */
+ if (TEST_PREFIX_ADDR) {
+ reg_al=LoadMb(BaseDS+(Bit32u)(reg_ebx+reg_al));
+ } else {
+ reg_al=LoadMb(BaseDS+(Bit16u)(reg_bx+reg_al));
+ }
+ break;
+#ifdef CPU_FPU
+ CASE_B(0xd8) /* FPU ESC 0 */
+ FPU_ESC(0);break;
+ CASE_B(0xd9) /* FPU ESC 1 */
+ FPU_ESC(1);break;
+ CASE_B(0xda) /* FPU ESC 2 */
+ FPU_ESC(2);break;
+ CASE_B(0xdb) /* FPU ESC 3 */
+ FPU_ESC(3);break;
+ CASE_B(0xdc) /* FPU ESC 4 */
+ FPU_ESC(4);break;
+ CASE_B(0xdd) /* FPU ESC 5 */
+ FPU_ESC(5);break;
+ CASE_B(0xde) /* FPU ESC 6 */
+ FPU_ESC(6);break;
+ CASE_B(0xdf) /* FPU ESC 7 */
+ FPU_ESC(7);break;
+#else
+ CASE_B(0xd8) /* FPU ESC 0 */
+ CASE_B(0xd9) /* FPU ESC 1 */
+ CASE_B(0xda) /* FPU ESC 2 */
+ CASE_B(0xdb) /* FPU ESC 3 */
+ CASE_B(0xdc) /* FPU ESC 4 */
+ CASE_B(0xdd) /* FPU ESC 5 */
+ CASE_B(0xde) /* FPU ESC 6 */
+ CASE_B(0xdf) /* FPU ESC 7 */
+ {
+ LOG(LOG_CPU,LOG_NORMAL)("FPU used");
+ Bit8u rm=Fetchb();
+ if (rm<0xc0) GetEAa;
+ }
+ break;
+#endif
+ CASE_W(0xe0) /* LOOPNZ */
+ if (TEST_PREFIX_ADDR) {
+ JumpCond16_b(--reg_ecx && !get_ZF());
+ } else {
+ JumpCond16_b(--reg_cx && !get_ZF());
+ }
+ break;
+ CASE_W(0xe1) /* LOOPZ */
+ if (TEST_PREFIX_ADDR) {
+ JumpCond16_b(--reg_ecx && get_ZF());
+ } else {
+ JumpCond16_b(--reg_cx && get_ZF());
+ }
+ break;
+ CASE_W(0xe2) /* LOOP */
+ if (TEST_PREFIX_ADDR) {
+ JumpCond16_b(--reg_ecx);
+ } else {
+ JumpCond16_b(--reg_cx);
+ }
+ break;
+ CASE_W(0xe3) /* JCXZ */
+ JumpCond16_b(!(reg_ecx & AddrMaskTable[core.prefixes& PREFIX_ADDR]));
+ break;
+ CASE_B(0xe4) /* IN AL,Ib */
+ {
+ Bitu port=Fetchb();
+ if (CPU_IO_Exception(port,1)) RUNEXCEPTION();
+ reg_al=IO_ReadB(port);
+ break;
+ }
+ CASE_W(0xe5) /* IN AX,Ib */
+ {
+ Bitu port=Fetchb();
+ if (CPU_IO_Exception(port,2)) RUNEXCEPTION();
+ reg_ax=IO_ReadW(port);
+ break;
+ }
+ CASE_B(0xe6) /* OUT Ib,AL */
+ {
+ Bitu port=Fetchb();
+ if (CPU_IO_Exception(port,1)) RUNEXCEPTION();
+ IO_WriteB(port,reg_al);
+ break;
+ }
+ CASE_W(0xe7) /* OUT Ib,AX */
+ {
+ Bitu port=Fetchb();
+ if (CPU_IO_Exception(port,2)) RUNEXCEPTION();
+ IO_WriteW(port,reg_ax);
+ break;
+ }
+ CASE_W(0xe8) /* CALL Jw */
+ {
+ Bit16u addip=Fetchws();
+ SAVEIP;
+ Push_16(reg_eip);
+ reg_eip=(Bit16u)(reg_eip+addip);
+ continue;
+ }
+ CASE_W(0xe9) /* JMP Jw */
+ {
+ Bit16u addip=Fetchws();
+ SAVEIP;
+ reg_eip=(Bit16u)(reg_eip+addip);
+ continue;
+ }
+ CASE_W(0xea) /* JMP Ap */
+ {
+ Bit16u newip=Fetchw();
+ Bit16u newcs=Fetchw();
+ FillFlags();
+ CPU_JMP(false,newcs,newip,GETIP);
+#if CPU_TRAP_CHECK
+ if (GETFLAG(TF)) {
+ cpudecoder=CPU_TRAP_DECODER;
+ return CBRET_NONE;
+ }
+#endif
+ continue;
+ }
+ CASE_W(0xeb) /* JMP Jb */
+ {
+ Bit16s addip=Fetchbs();
+ SAVEIP;
+ reg_eip=(Bit16u)(reg_eip+addip);
+ continue;
+ }
+ CASE_B(0xec) /* IN AL,DX */
+ if (CPU_IO_Exception(reg_dx,1)) RUNEXCEPTION();
+ reg_al=IO_ReadB(reg_dx);
+ break;
+ CASE_W(0xed) /* IN AX,DX */
+ if (CPU_IO_Exception(reg_dx,2)) RUNEXCEPTION();
+ reg_ax=IO_ReadW(reg_dx);
+ break;
+ CASE_B(0xee) /* OUT DX,AL */
+ if (CPU_IO_Exception(reg_dx,1)) RUNEXCEPTION();
+ IO_WriteB(reg_dx,reg_al);
+ break;
+ CASE_W(0xef) /* OUT DX,AX */
+ if (CPU_IO_Exception(reg_dx,2)) RUNEXCEPTION();
+ IO_WriteW(reg_dx,reg_ax);
+ break;
+ CASE_B(0xf0) /* LOCK */
+ LOG(LOG_CPU,LOG_NORMAL)("CPU:LOCK"); /* FIXME: see case D_LOCK in core_full/load.h */
+ break;
+ CASE_B(0xf1) /* ICEBP */
+ CPU_SW_Interrupt_NoIOPLCheck(1,GETIP);
+#if CPU_TRAP_CHECK
+ cpu.trap_skip=true;
+#endif
+ continue;
+ CASE_B(0xf2) /* REPNZ */
+ DO_PREFIX_REP(false);
+ break;
+ CASE_B(0xf3) /* REPZ */
+ DO_PREFIX_REP(true);
+ break;
+ CASE_B(0xf4) /* HLT */
+ if (cpu.pmode && cpu.cpl) EXCEPTION(EXCEPTION_GP);
+ FillFlags();
+ CPU_HLT(GETIP);
+ return CBRET_NONE; //Needs to return for hlt cpu core
+ CASE_B(0xf5) /* CMC */
+ FillFlags();
+ SETFLAGBIT(CF,!(reg_flags & FLAG_CF));
+ break;
+ CASE_B(0xf6) /* GRP3 Eb(,Ib) */
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ switch (which) {
+ case 0x00: /* TEST Eb,Ib */
+ case 0x01: /* TEST Eb,Ib Undocumented*/
+ {
+ if (rm >= 0xc0 ) {GetEArb;TESTB(*earb,Fetchb(),LoadRb,0)}
+ else {GetEAa;TESTB(eaa,Fetchb(),LoadMb,0);}
+ break;
+ }
+ case 0x02: /* NOT Eb */
+ {
+ if (rm >= 0xc0 ) {GetEArb;*earb=~*earb;}
+ else {GetEAa;SaveMb(eaa,~LoadMb(eaa));}
+ break;
+ }
+ case 0x03: /* NEG Eb */
+ {
+ lflags.type=t_NEGb;
+ if (rm >= 0xc0 ) {
+ GetEArb;lf_var1b=*earb;lf_resb=0-lf_var1b;
+ *earb=lf_resb;
+ } else {
+ GetEAa;lf_var1b=LoadMb(eaa);lf_resb=0-lf_var1b;
+ SaveMb(eaa,lf_resb);
+ }
+ break;
+ }
+ case 0x04: /* MUL AL,Eb */
+ RMEb(MULB);
+ break;
+ case 0x05: /* IMUL AL,Eb */
+ RMEb(IMULB);
+ break;
+ case 0x06: /* DIV Eb */
+ RMEb(DIVB);
+ break;
+ case 0x07: /* IDIV Eb */
+ RMEb(IDIVB);
+ break;
+ }
+ break;
+ }
+ CASE_W(0xf7) /* GRP3 Ew(,Iw) */
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ switch (which) {
+ case 0x00: /* TEST Ew,Iw */
+ case 0x01: /* TEST Ew,Iw Undocumented*/
+ {
+ if (rm >= 0xc0 ) {GetEArw;TESTW(*earw,Fetchw(),LoadRw,SaveRw);}
+ else {GetEAa;TESTW(eaa,Fetchw(),LoadMw,SaveMw);}
+ break;
+ }
+ case 0x02: /* NOT Ew */
+ {
+ if (rm >= 0xc0 ) {GetEArw;*earw=~*earw;}
+ else {GetEAa;SaveMw(eaa,~LoadMw(eaa));}
+ break;
+ }
+ case 0x03: /* NEG Ew */
+ {
+ lflags.type=t_NEGw;
+ if (rm >= 0xc0 ) {
+ GetEArw;lf_var1w=*earw;lf_resw=0-lf_var1w;
+ *earw=lf_resw;
+ } else {
+ GetEAa;lf_var1w=LoadMw(eaa);lf_resw=0-lf_var1w;
+ SaveMw(eaa,lf_resw);
+ }
+ break;
+ }
+ case 0x04: /* MUL AX,Ew */
+ RMEw(MULW);
+ break;
+ case 0x05: /* IMUL AX,Ew */
+ RMEw(IMULW)
+ break;
+ case 0x06: /* DIV Ew */
+ RMEw(DIVW)
+ break;
+ case 0x07: /* IDIV Ew */
+ RMEw(IDIVW)
+ break;
+ }
+ break;
+ }
+ CASE_B(0xf8) /* CLC */
+ FillFlags();
+ SETFLAGBIT(CF,false);
+ break;
+ CASE_B(0xf9) /* STC */
+ FillFlags();
+ SETFLAGBIT(CF,true);
+ break;
+ CASE_B(0xfa) /* CLI */
+ if (CPU_CLI()) RUNEXCEPTION();
+ break;
+ CASE_B(0xfb) /* STI */
+ if (CPU_STI()) RUNEXCEPTION();
+#if CPU_PIC_CHECK
+ if (GETFLAG(IF) && PIC_IRQCheck) goto decode_end;
+#endif
+ break;
+ CASE_B(0xfc) /* CLD */
+ SETFLAGBIT(DF,false);
+ cpu.direction=1;
+ break;
+ CASE_B(0xfd) /* STD */
+ SETFLAGBIT(DF,true);
+ cpu.direction=-1;
+ break;
+ CASE_B(0xfe) /* GRP4 Eb */
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ switch (which) {
+ case 0x00: /* INC Eb */
+ RMEb(INCB);
+ break;
+ case 0x01: /* DEC Eb */
+ RMEb(DECB);
+ break;
+ case 0x07: /* CallBack */
+ {
+ Bitu cb=Fetchw();
+ FillFlags();SAVEIP;
+ return cb;
+ }
+ default:
+ E_Exit("Illegal GRP4 Call %d",(rm>>3) & 7);
+ break;
+ }
+ break;
+ }
+ CASE_W(0xff) /* GRP5 Ew */
+ {
+ GetRM;Bitu which=(rm>>3)&7;
+ switch (which) {
+ case 0x00: /* INC Ew */
+ RMEw(INCW);
+ break;
+ case 0x01: /* DEC Ew */
+ RMEw(DECW);
+ break;
+ case 0x02: /* CALL Ev */
+ if (rm >= 0xc0 ) {GetEArw;reg_eip=*earw;}
+ else {GetEAa;reg_eip=LoadMw(eaa);}
+ Push_16(GETIP);
+ continue;
+ case 0x03: /* CALL Ep */
+ {
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ Bit16u newip=LoadMw(eaa);
+ Bit16u newcs=LoadMw(eaa+2);
+ FillFlags();
+ CPU_CALL(false,newcs,newip,GETIP);
+#if CPU_TRAP_CHECK
+ if (GETFLAG(TF)) {
+ cpudecoder=CPU_TRAP_DECODER;
+ return CBRET_NONE;
+ }
+#endif
+ continue;
+ }
+ break;
+ case 0x04: /* JMP Ev */
+ if (rm >= 0xc0 ) {GetEArw;reg_eip=*earw;}
+ else {GetEAa;reg_eip=LoadMw(eaa);}
+ continue;
+ case 0x05: /* JMP Ep */
+ {
+ if (rm >= 0xc0) goto illegal_opcode;
+ GetEAa;
+ Bit16u newip=LoadMw(eaa);
+ Bit16u newcs=LoadMw(eaa+2);
+ FillFlags();
+ CPU_JMP(false,newcs,newip,GETIP);
+#if CPU_TRAP_CHECK
+ if (GETFLAG(TF)) {
+ cpudecoder=CPU_TRAP_DECODER;
+ return CBRET_NONE;
+ }
+#endif
+ continue;
+ }
+ break;
+ case 0x06: /* PUSH Ev */
+ if (rm >= 0xc0 ) {GetEArw;Push_16(*earw);}
+ else {GetEAa;Push_16(LoadMw(eaa));}
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("CPU:GRP5:Illegal Call %2X",which);
+ goto illegal_opcode;
+ }
+ break;
+ }
+
+
+
+
diff --git a/src/cpu/core_normal/string.h b/src/cpu/core_normal/string.h
new file mode 100644
index 000000000..5fdbe1077
--- /dev/null
+++ b/src/cpu/core_normal/string.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+enum STRING_OP {
+ R_OUTSB,R_OUTSW,R_OUTSD,
+ R_INSB,R_INSW,R_INSD,
+ R_MOVSB,R_MOVSW,R_MOVSD,
+ R_LODSB,R_LODSW,R_LODSD,
+ R_STOSB,R_STOSW,R_STOSD,
+ R_SCASB,R_SCASW,R_SCASD,
+ R_CMPSB,R_CMPSW,R_CMPSD
+};
+
+#define LoadD(_BLAH) _BLAH
+
+static void DoString(STRING_OP type) {
+ PhysPt si_base,di_base;
+ Bitu si_index,di_index;
+ Bitu add_mask;
+ Bitu count,count_left;
+ Bits add_index;
+
+ si_base=BaseDS;
+ di_base=SegBase(es);
+ add_mask=AddrMaskTable[core.prefixes & PREFIX_ADDR];
+ si_index=reg_esi & add_mask;
+ di_index=reg_edi & add_mask;
+ count=reg_ecx & add_mask;
+ if (!TEST_PREFIX_REP) {
+ count=1;
+ } else {
+ CPU_Cycles++;
+ /* Calculate amount of ops to do before cycles run out */
+ if ((count>(Bitu)CPU_Cycles) && (type<R_SCASB)) {
+ count_left=count-CPU_Cycles;
+ count=CPU_Cycles;
+ CPU_Cycles=0;
+ LOADIP; //RESET IP to the start
+ } else {
+ /* Won't interrupt scas and cmps instruction since they can interrupt themselves */
+ if ((count<=1) && (CPU_Cycles<=1)) CPU_Cycles--;
+ else if (type<R_SCASB) CPU_Cycles-=count;
+ count_left=0;
+ }
+ }
+ add_index=cpu.direction;
+ if (count) switch (type) {
+ case R_OUTSB:
+ for (;count>0;count--) {
+ IO_WriteB(reg_dx,LoadMb(si_base+si_index));
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_OUTSW:
+ add_index<<=1;
+ for (;count>0;count--) {
+ IO_WriteW(reg_dx,LoadMw(si_base+si_index));
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_OUTSD:
+ add_index<<=2;
+ for (;count>0;count--) {
+ IO_WriteD(reg_dx,LoadMd(si_base+si_index));
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_INSB:
+ for (;count>0;count--) {
+ SaveMb(di_base+di_index,IO_ReadB(reg_dx));
+ di_index=(di_index+add_index) & add_mask;
+ }
+ break;
+ case R_INSW:
+ add_index<<=1;
+ for (;count>0;count--) {
+ SaveMw(di_base+di_index,IO_ReadW(reg_dx));
+ di_index=(di_index+add_index) & add_mask;
+ }
+ break;
+ case R_INSD:
+ add_index<<=2;
+ for (;count>0;count--) {
+ SaveMd(di_base+di_index,IO_ReadD(reg_dx));
+ di_index=(di_index+add_index) & add_mask;
+ }
+ break;
+ case R_STOSB:
+ for (;count>0;count--) {
+ SaveMb(di_base+di_index,reg_al);
+ di_index=(di_index+add_index) & add_mask;
+ }
+ break;
+ case R_STOSW:
+ add_index<<=1;
+ for (;count>0;count--) {
+ SaveMw(di_base+di_index,reg_ax);
+ di_index=(di_index+add_index) & add_mask;
+ }
+ break;
+ case R_STOSD:
+ add_index<<=2;
+ for (;count>0;count--) {
+ SaveMd(di_base+di_index,reg_eax);
+ di_index=(di_index+add_index) & add_mask;
+ }
+ break;
+ case R_MOVSB:
+ for (;count>0;count--) {
+ SaveMb(di_base+di_index,LoadMb(si_base+si_index));
+ di_index=(di_index+add_index) & add_mask;
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_MOVSW:
+ add_index<<=1;
+ for (;count>0;count--) {
+ SaveMw(di_base+di_index,LoadMw(si_base+si_index));
+ di_index=(di_index+add_index) & add_mask;
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_MOVSD:
+ add_index<<=2;
+ for (;count>0;count--) {
+ SaveMd(di_base+di_index,LoadMd(si_base+si_index));
+ di_index=(di_index+add_index) & add_mask;
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_LODSB:
+ for (;count>0;count--) {
+ reg_al=LoadMb(si_base+si_index);
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_LODSW:
+ add_index<<=1;
+ for (;count>0;count--) {
+ reg_ax=LoadMw(si_base+si_index);
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_LODSD:
+ add_index<<=2;
+ for (;count>0;count--) {
+ reg_eax=LoadMd(si_base+si_index);
+ si_index=(si_index+add_index) & add_mask;
+ }
+ break;
+ case R_SCASB:
+ {
+ Bit8u val2;
+ for (;count>0;) {
+ count--;CPU_Cycles--;
+ val2=LoadMb(di_base+di_index);
+ di_index=(di_index+add_index) & add_mask;
+ if ((reg_al==val2)!=core.rep_zero) break;
+ }
+ CMPB(reg_al,val2,LoadD,0);
+ }
+ break;
+ case R_SCASW:
+ {
+ add_index<<=1;Bit16u val2;
+ for (;count>0;) {
+ count--;CPU_Cycles--;
+ val2=LoadMw(di_base+di_index);
+ di_index=(di_index+add_index) & add_mask;
+ if ((reg_ax==val2)!=core.rep_zero) break;
+ }
+ CMPW(reg_ax,val2,LoadD,0);
+ }
+ break;
+ case R_SCASD:
+ {
+ add_index<<=2;Bit32u val2;
+ for (;count>0;) {
+ count--;CPU_Cycles--;
+ val2=LoadMd(di_base+di_index);
+ di_index=(di_index+add_index) & add_mask;
+ if ((reg_eax==val2)!=core.rep_zero) break;
+ }
+ CMPD(reg_eax,val2,LoadD,0);
+ }
+ break;
+ case R_CMPSB:
+ {
+ Bit8u val1,val2;
+ for (;count>0;) {
+ count--;CPU_Cycles--;
+ val1=LoadMb(si_base+si_index);
+ val2=LoadMb(di_base+di_index);
+ si_index=(si_index+add_index) & add_mask;
+ di_index=(di_index+add_index) & add_mask;
+ if ((val1==val2)!=core.rep_zero) break;
+ }
+ CMPB(val1,val2,LoadD,0);
+ }
+ break;
+ case R_CMPSW:
+ {
+ add_index<<=1;Bit16u val1,val2;
+ for (;count>0;) {
+ count--;CPU_Cycles--;
+ val1=LoadMw(si_base+si_index);
+ val2=LoadMw(di_base+di_index);
+ si_index=(si_index+add_index) & add_mask;
+ di_index=(di_index+add_index) & add_mask;
+ if ((val1==val2)!=core.rep_zero) break;
+ }
+ CMPW(val1,val2,LoadD,0);
+ }
+ break;
+ case R_CMPSD:
+ {
+ add_index<<=2;Bit32u val1,val2;
+ for (;count>0;) {
+ count--;CPU_Cycles--;
+ val1=LoadMd(si_base+si_index);
+ val2=LoadMd(di_base+di_index);
+ si_index=(si_index+add_index) & add_mask;
+ di_index=(di_index+add_index) & add_mask;
+ if ((val1==val2)!=core.rep_zero) break;
+ }
+ CMPD(val1,val2,LoadD,0);
+ }
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Unhandled string op %d",type);
+ }
+ /* Clean up after certain amount of instructions */
+ reg_esi&=(~add_mask);
+ reg_esi|=(si_index & add_mask);
+ reg_edi&=(~add_mask);
+ reg_edi|=(di_index & add_mask);
+ if (TEST_PREFIX_REP) {
+ count+=count_left;
+ reg_ecx&=(~add_mask);
+ reg_ecx|=(count & add_mask);
+ }
+}
diff --git a/src/cpu/core_normal/support.h b/src/cpu/core_normal/support.h
new file mode 100644
index 000000000..9b94b7836
--- /dev/null
+++ b/src/cpu/core_normal/support.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#define LoadMbs(off) (Bit8s)(LoadMb(off))
+#define LoadMws(off) (Bit16s)(LoadMw(off))
+#define LoadMds(off) (Bit32s)(LoadMd(off))
+
+#define LoadRb(reg) reg
+#define LoadRw(reg) reg
+#define LoadRd(reg) reg
+
+#define SaveRb(reg,val) reg=val
+#define SaveRw(reg,val) reg=val
+#define SaveRd(reg,val) reg=val
+
+static INLINE Bit8s Fetchbs() {
+ return Fetchb();
+}
+static INLINE Bit16s Fetchws() {
+ return Fetchw();
+}
+
+static INLINE Bit32s Fetchds() {
+ return Fetchd();
+}
+
+
+#define RUNEXCEPTION() { \
+ CPU_Exception(cpu.exception.which,cpu.exception.error); \
+ continue; \
+}
+
+#define EXCEPTION(blah) \
+ { \
+ CPU_Exception(blah); \
+ continue; \
+ }
+
+//TODO Could probably make all byte operands fast?
+#define JumpCond16_b(COND) { \
+ SAVEIP; \
+ if (COND) reg_ip+=Fetchbs(); \
+ reg_ip+=1; \
+ continue; \
+}
+
+#define JumpCond16_w(COND) { \
+ SAVEIP; \
+ if (COND) reg_ip+=Fetchws(); \
+ reg_ip+=2; \
+ continue; \
+}
+
+#define JumpCond32_b(COND) { \
+ SAVEIP; \
+ if (COND) reg_eip+=Fetchbs(); \
+ reg_eip+=1; \
+ continue; \
+}
+
+#define JumpCond32_d(COND) { \
+ SAVEIP; \
+ if (COND) reg_eip+=Fetchds(); \
+ reg_eip+=4; \
+ continue; \
+}
+
+
+#define SETcc(cc) \
+ { \
+ GetRM; \
+ if (rm >= 0xc0 ) {GetEArb;*earb=(cc) ? 1 : 0;} \
+ else {GetEAa;SaveMb(eaa,(cc) ? 1 : 0);} \
+ }
+
+#include "helpers.h"
+#include "table_ea.h"
+#include "../modrm.h"
+
+
diff --git a/src/cpu/core_normal/table_ea.h b/src/cpu/core_normal/table_ea.h
new file mode 100644
index 000000000..f4a764a76
--- /dev/null
+++ b/src/cpu/core_normal/table_ea.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+typedef PhysPt (*EA_LookupHandler)(void);
+
+/* The MOD/RM Decoder for EA for this decoder's addressing modes */
+static PhysPt EA_16_00_n(void) { return BaseDS+(Bit16u)(reg_bx+(Bit16s)reg_si); }
+static PhysPt EA_16_01_n(void) { return BaseDS+(Bit16u)(reg_bx+(Bit16s)reg_di); }
+static PhysPt EA_16_02_n(void) { return BaseSS+(Bit16u)(reg_bp+(Bit16s)reg_si); }
+static PhysPt EA_16_03_n(void) { return BaseSS+(Bit16u)(reg_bp+(Bit16s)reg_di); }
+static PhysPt EA_16_04_n(void) { return BaseDS+(Bit16u)(reg_si); }
+static PhysPt EA_16_05_n(void) { return BaseDS+(Bit16u)(reg_di); }
+static PhysPt EA_16_06_n(void) { return BaseDS+(Bit16u)(Fetchw());}
+static PhysPt EA_16_07_n(void) { return BaseDS+(Bit16u)(reg_bx); }
+
+static PhysPt EA_16_40_n(void) { return BaseDS+(Bit16u)(reg_bx+(Bit16s)reg_si+Fetchbs()); }
+static PhysPt EA_16_41_n(void) { return BaseDS+(Bit16u)(reg_bx+(Bit16s)reg_di+Fetchbs()); }
+static PhysPt EA_16_42_n(void) { return BaseSS+(Bit16u)(reg_bp+(Bit16s)reg_si+Fetchbs()); }
+static PhysPt EA_16_43_n(void) { return BaseSS+(Bit16u)(reg_bp+(Bit16s)reg_di+Fetchbs()); }
+static PhysPt EA_16_44_n(void) { return BaseDS+(Bit16u)(reg_si+Fetchbs()); }
+static PhysPt EA_16_45_n(void) { return BaseDS+(Bit16u)(reg_di+Fetchbs()); }
+static PhysPt EA_16_46_n(void) { return BaseSS+(Bit16u)(reg_bp+Fetchbs()); }
+static PhysPt EA_16_47_n(void) { return BaseDS+(Bit16u)(reg_bx+Fetchbs()); }
+
+static PhysPt EA_16_80_n(void) { return BaseDS+(Bit16u)(reg_bx+(Bit16s)reg_si+Fetchws()); }
+static PhysPt EA_16_81_n(void) { return BaseDS+(Bit16u)(reg_bx+(Bit16s)reg_di+Fetchws()); }
+static PhysPt EA_16_82_n(void) { return BaseSS+(Bit16u)(reg_bp+(Bit16s)reg_si+Fetchws()); }
+static PhysPt EA_16_83_n(void) { return BaseSS+(Bit16u)(reg_bp+(Bit16s)reg_di+Fetchws()); }
+static PhysPt EA_16_84_n(void) { return BaseDS+(Bit16u)(reg_si+Fetchws()); }
+static PhysPt EA_16_85_n(void) { return BaseDS+(Bit16u)(reg_di+Fetchws()); }
+static PhysPt EA_16_86_n(void) { return BaseSS+(Bit16u)(reg_bp+Fetchws()); }
+static PhysPt EA_16_87_n(void) { return BaseDS+(Bit16u)(reg_bx+Fetchws()); }
+
+static Bit32u SIBZero=0;
+static Bit32u * SIBIndex[8]= { &reg_eax,&reg_ecx,&reg_edx,&reg_ebx,&SIBZero,&reg_ebp,&reg_esi,&reg_edi };
+
+static INLINE PhysPt Sib(Bitu mode) {
+ Bit8u sib=Fetchb();
+ PhysPt base;
+ switch (sib&7) {
+ case 0: /* EAX Base */
+ base=BaseDS+reg_eax;break;
+ case 1: /* ECX Base */
+ base=BaseDS+reg_ecx;break;
+ case 2: /* EDX Base */
+ base=BaseDS+reg_edx;break;
+ case 3: /* EBX Base */
+ base=BaseDS+reg_ebx;break;
+ case 4: /* ESP Base */
+ base=BaseSS+reg_esp;break;
+ case 5: /* #1 Base */
+ if (!mode) {
+ base=BaseDS+Fetchd();break;
+ } else {
+ base=BaseSS+reg_ebp;break;
+ }
+ case 6: /* ESI Base */
+ base=BaseDS+reg_esi;break;
+ case 7: /* EDI Base */
+ base=BaseDS+reg_edi;break;
+ }
+ base+=*SIBIndex[(sib >> 3) &7] << (sib >> 6);
+ return base;
+}
+
+static PhysPt EA_32_00_n(void) { return BaseDS+reg_eax; }
+static PhysPt EA_32_01_n(void) { return BaseDS+reg_ecx; }
+static PhysPt EA_32_02_n(void) { return BaseDS+reg_edx; }
+static PhysPt EA_32_03_n(void) { return BaseDS+reg_ebx; }
+static PhysPt EA_32_04_n(void) { return Sib(0);}
+static PhysPt EA_32_05_n(void) { return BaseDS+Fetchd(); }
+static PhysPt EA_32_06_n(void) { return BaseDS+reg_esi; }
+static PhysPt EA_32_07_n(void) { return BaseDS+reg_edi; }
+
+static PhysPt EA_32_40_n(void) { return BaseDS+reg_eax+Fetchbs(); }
+static PhysPt EA_32_41_n(void) { return BaseDS+reg_ecx+Fetchbs(); }
+static PhysPt EA_32_42_n(void) { return BaseDS+reg_edx+Fetchbs(); }
+static PhysPt EA_32_43_n(void) { return BaseDS+reg_ebx+Fetchbs(); }
+static PhysPt EA_32_44_n(void) { PhysPt temp=Sib(1);return temp+Fetchbs();}
+//static PhysPt EA_32_44_n(void) { return Sib(1)+Fetchbs();}
+static PhysPt EA_32_45_n(void) { return BaseSS+reg_ebp+Fetchbs(); }
+static PhysPt EA_32_46_n(void) { return BaseDS+reg_esi+Fetchbs(); }
+static PhysPt EA_32_47_n(void) { return BaseDS+reg_edi+Fetchbs(); }
+
+static PhysPt EA_32_80_n(void) { return BaseDS+reg_eax+Fetchds(); }
+static PhysPt EA_32_81_n(void) { return BaseDS+reg_ecx+Fetchds(); }
+static PhysPt EA_32_82_n(void) { return BaseDS+reg_edx+Fetchds(); }
+static PhysPt EA_32_83_n(void) { return BaseDS+reg_ebx+Fetchds(); }
+static PhysPt EA_32_84_n(void) { PhysPt temp=Sib(2);return temp+Fetchds();}
+//static PhysPt EA_32_84_n(void) { return Sib(2)+Fetchds();}
+static PhysPt EA_32_85_n(void) { return BaseSS+reg_ebp+Fetchds(); }
+static PhysPt EA_32_86_n(void) { return BaseDS+reg_esi+Fetchds(); }
+static PhysPt EA_32_87_n(void) { return BaseDS+reg_edi+Fetchds(); }
+
+static GetEAHandler EATable[512]={
+/* 00 */
+ EA_16_00_n,EA_16_01_n,EA_16_02_n,EA_16_03_n,EA_16_04_n,EA_16_05_n,EA_16_06_n,EA_16_07_n,
+ EA_16_00_n,EA_16_01_n,EA_16_02_n,EA_16_03_n,EA_16_04_n,EA_16_05_n,EA_16_06_n,EA_16_07_n,
+ EA_16_00_n,EA_16_01_n,EA_16_02_n,EA_16_03_n,EA_16_04_n,EA_16_05_n,EA_16_06_n,EA_16_07_n,
+ EA_16_00_n,EA_16_01_n,EA_16_02_n,EA_16_03_n,EA_16_04_n,EA_16_05_n,EA_16_06_n,EA_16_07_n,
+ EA_16_00_n,EA_16_01_n,EA_16_02_n,EA_16_03_n,EA_16_04_n,EA_16_05_n,EA_16_06_n,EA_16_07_n,
+ EA_16_00_n,EA_16_01_n,EA_16_02_n,EA_16_03_n,EA_16_04_n,EA_16_05_n,EA_16_06_n,EA_16_07_n,
+ EA_16_00_n,EA_16_01_n,EA_16_02_n,EA_16_03_n,EA_16_04_n,EA_16_05_n,EA_16_06_n,EA_16_07_n,
+ EA_16_00_n,EA_16_01_n,EA_16_02_n,EA_16_03_n,EA_16_04_n,EA_16_05_n,EA_16_06_n,EA_16_07_n,
+/* 01 */
+ EA_16_40_n,EA_16_41_n,EA_16_42_n,EA_16_43_n,EA_16_44_n,EA_16_45_n,EA_16_46_n,EA_16_47_n,
+ EA_16_40_n,EA_16_41_n,EA_16_42_n,EA_16_43_n,EA_16_44_n,EA_16_45_n,EA_16_46_n,EA_16_47_n,
+ EA_16_40_n,EA_16_41_n,EA_16_42_n,EA_16_43_n,EA_16_44_n,EA_16_45_n,EA_16_46_n,EA_16_47_n,
+ EA_16_40_n,EA_16_41_n,EA_16_42_n,EA_16_43_n,EA_16_44_n,EA_16_45_n,EA_16_46_n,EA_16_47_n,
+ EA_16_40_n,EA_16_41_n,EA_16_42_n,EA_16_43_n,EA_16_44_n,EA_16_45_n,EA_16_46_n,EA_16_47_n,
+ EA_16_40_n,EA_16_41_n,EA_16_42_n,EA_16_43_n,EA_16_44_n,EA_16_45_n,EA_16_46_n,EA_16_47_n,
+ EA_16_40_n,EA_16_41_n,EA_16_42_n,EA_16_43_n,EA_16_44_n,EA_16_45_n,EA_16_46_n,EA_16_47_n,
+ EA_16_40_n,EA_16_41_n,EA_16_42_n,EA_16_43_n,EA_16_44_n,EA_16_45_n,EA_16_46_n,EA_16_47_n,
+/* 10 */
+ EA_16_80_n,EA_16_81_n,EA_16_82_n,EA_16_83_n,EA_16_84_n,EA_16_85_n,EA_16_86_n,EA_16_87_n,
+ EA_16_80_n,EA_16_81_n,EA_16_82_n,EA_16_83_n,EA_16_84_n,EA_16_85_n,EA_16_86_n,EA_16_87_n,
+ EA_16_80_n,EA_16_81_n,EA_16_82_n,EA_16_83_n,EA_16_84_n,EA_16_85_n,EA_16_86_n,EA_16_87_n,
+ EA_16_80_n,EA_16_81_n,EA_16_82_n,EA_16_83_n,EA_16_84_n,EA_16_85_n,EA_16_86_n,EA_16_87_n,
+ EA_16_80_n,EA_16_81_n,EA_16_82_n,EA_16_83_n,EA_16_84_n,EA_16_85_n,EA_16_86_n,EA_16_87_n,
+ EA_16_80_n,EA_16_81_n,EA_16_82_n,EA_16_83_n,EA_16_84_n,EA_16_85_n,EA_16_86_n,EA_16_87_n,
+ EA_16_80_n,EA_16_81_n,EA_16_82_n,EA_16_83_n,EA_16_84_n,EA_16_85_n,EA_16_86_n,EA_16_87_n,
+ EA_16_80_n,EA_16_81_n,EA_16_82_n,EA_16_83_n,EA_16_84_n,EA_16_85_n,EA_16_86_n,EA_16_87_n,
+/* 11 These are illegal so make em 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,
+/* 00 */
+ EA_32_00_n,EA_32_01_n,EA_32_02_n,EA_32_03_n,EA_32_04_n,EA_32_05_n,EA_32_06_n,EA_32_07_n,
+ EA_32_00_n,EA_32_01_n,EA_32_02_n,EA_32_03_n,EA_32_04_n,EA_32_05_n,EA_32_06_n,EA_32_07_n,
+ EA_32_00_n,EA_32_01_n,EA_32_02_n,EA_32_03_n,EA_32_04_n,EA_32_05_n,EA_32_06_n,EA_32_07_n,
+ EA_32_00_n,EA_32_01_n,EA_32_02_n,EA_32_03_n,EA_32_04_n,EA_32_05_n,EA_32_06_n,EA_32_07_n,
+ EA_32_00_n,EA_32_01_n,EA_32_02_n,EA_32_03_n,EA_32_04_n,EA_32_05_n,EA_32_06_n,EA_32_07_n,
+ EA_32_00_n,EA_32_01_n,EA_32_02_n,EA_32_03_n,EA_32_04_n,EA_32_05_n,EA_32_06_n,EA_32_07_n,
+ EA_32_00_n,EA_32_01_n,EA_32_02_n,EA_32_03_n,EA_32_04_n,EA_32_05_n,EA_32_06_n,EA_32_07_n,
+ EA_32_00_n,EA_32_01_n,EA_32_02_n,EA_32_03_n,EA_32_04_n,EA_32_05_n,EA_32_06_n,EA_32_07_n,
+/* 01 */
+ EA_32_40_n,EA_32_41_n,EA_32_42_n,EA_32_43_n,EA_32_44_n,EA_32_45_n,EA_32_46_n,EA_32_47_n,
+ EA_32_40_n,EA_32_41_n,EA_32_42_n,EA_32_43_n,EA_32_44_n,EA_32_45_n,EA_32_46_n,EA_32_47_n,
+ EA_32_40_n,EA_32_41_n,EA_32_42_n,EA_32_43_n,EA_32_44_n,EA_32_45_n,EA_32_46_n,EA_32_47_n,
+ EA_32_40_n,EA_32_41_n,EA_32_42_n,EA_32_43_n,EA_32_44_n,EA_32_45_n,EA_32_46_n,EA_32_47_n,
+ EA_32_40_n,EA_32_41_n,EA_32_42_n,EA_32_43_n,EA_32_44_n,EA_32_45_n,EA_32_46_n,EA_32_47_n,
+ EA_32_40_n,EA_32_41_n,EA_32_42_n,EA_32_43_n,EA_32_44_n,EA_32_45_n,EA_32_46_n,EA_32_47_n,
+ EA_32_40_n,EA_32_41_n,EA_32_42_n,EA_32_43_n,EA_32_44_n,EA_32_45_n,EA_32_46_n,EA_32_47_n,
+ EA_32_40_n,EA_32_41_n,EA_32_42_n,EA_32_43_n,EA_32_44_n,EA_32_45_n,EA_32_46_n,EA_32_47_n,
+/* 10 */
+ EA_32_80_n,EA_32_81_n,EA_32_82_n,EA_32_83_n,EA_32_84_n,EA_32_85_n,EA_32_86_n,EA_32_87_n,
+ EA_32_80_n,EA_32_81_n,EA_32_82_n,EA_32_83_n,EA_32_84_n,EA_32_85_n,EA_32_86_n,EA_32_87_n,
+ EA_32_80_n,EA_32_81_n,EA_32_82_n,EA_32_83_n,EA_32_84_n,EA_32_85_n,EA_32_86_n,EA_32_87_n,
+ EA_32_80_n,EA_32_81_n,EA_32_82_n,EA_32_83_n,EA_32_84_n,EA_32_85_n,EA_32_86_n,EA_32_87_n,
+ EA_32_80_n,EA_32_81_n,EA_32_82_n,EA_32_83_n,EA_32_84_n,EA_32_85_n,EA_32_86_n,EA_32_87_n,
+ EA_32_80_n,EA_32_81_n,EA_32_82_n,EA_32_83_n,EA_32_84_n,EA_32_85_n,EA_32_86_n,EA_32_87_n,
+ EA_32_80_n,EA_32_81_n,EA_32_82_n,EA_32_83_n,EA_32_84_n,EA_32_85_n,EA_32_86_n,EA_32_87_n,
+ EA_32_80_n,EA_32_81_n,EA_32_82_n,EA_32_83_n,EA_32_84_n,EA_32_85_n,EA_32_86_n,EA_32_87_n,
+/* 11 These are illegal so make em 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
+};
+
+#define GetEADirect \
+ PhysPt eaa; \
+ if (TEST_PREFIX_ADDR) { \
+ eaa=BaseDS+Fetchd(); \
+ } else { \
+ eaa=BaseDS+Fetchw(); \
+ } \
+
+
diff --git a/src/cpu/core_prefetch.cpp b/src/cpu/core_prefetch.cpp
new file mode 100644
index 000000000..f718cd561
--- /dev/null
+++ b/src/cpu/core_prefetch.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdio.h>
+
+#include "dosbox.h"
+#include "mem.h"
+#include "cpu.h"
+#include "lazyflags.h"
+#include "inout.h"
+#include "callback.h"
+#include "pic.h"
+#include "fpu.h"
+#include "paging.h"
+
+#if C_DEBUG
+#include "debug.h"
+#endif
+
+#if (!C_CORE_INLINE)
+#define LoadMb(off) mem_readb(off)
+#define LoadMw(off) mem_readw(off)
+#define LoadMd(off) mem_readd(off)
+#define SaveMb(off,val) mem_writeb(off,val)
+#define SaveMw(off,val) mem_writew(off,val)
+#define SaveMd(off,val) mem_writed(off,val)
+#else
+#include "paging.h"
+#define LoadMb(off) mem_readb_inline(off)
+#define LoadMw(off) mem_readw_inline(off)
+#define LoadMd(off) mem_readd_inline(off)
+#define SaveMb(off,val) mem_writeb_inline(off,val)
+#define SaveMw(off,val) mem_writew_inline(off,val)
+#define SaveMd(off,val) mem_writed_inline(off,val)
+#endif
+
+extern Bitu cycle_count;
+
+#if C_FPU
+#define CPU_FPU 1 //Enable FPU escape instructions
+#endif
+
+#define CPU_PIC_CHECK 1
+#define CPU_TRAP_CHECK 1
+
+#define CPU_TRAP_DECODER CPU_Core_Prefetch_Trap_Run
+
+#define OPCODE_NONE 0x000
+#define OPCODE_0F 0x100
+#define OPCODE_SIZE 0x200
+
+#define PREFIX_ADDR 0x1
+#define PREFIX_REP 0x2
+
+#define TEST_PREFIX_ADDR (core.prefixes & PREFIX_ADDR)
+#define TEST_PREFIX_REP (core.prefixes & PREFIX_REP)
+
+#define DO_PREFIX_SEG(_SEG) \
+ BaseDS=SegBase(_SEG); \
+ BaseSS=SegBase(_SEG); \
+ core.base_val_ds=_SEG; \
+ goto restart_opcode;
+
+#define DO_PREFIX_ADDR() \
+ core.prefixes=(core.prefixes & ~PREFIX_ADDR) | \
+ (cpu.code.big ^ PREFIX_ADDR); \
+ core.ea_table=&EATable[(core.prefixes&1) * 256]; \
+ goto restart_opcode;
+
+#define DO_PREFIX_REP(_ZERO) \
+ core.prefixes|=PREFIX_REP; \
+ core.rep_zero=_ZERO; \
+ goto restart_opcode;
+
+typedef PhysPt (*GetEAHandler)(void);
+
+static const Bit32u AddrMaskTable[2]={0x0000ffff,0xffffffff};
+
+static struct {
+ Bitu opcode_index;
+ PhysPt cseip;
+ PhysPt base_ds,base_ss;
+ SegNames base_val_ds;
+ bool rep_zero;
+ Bitu prefixes;
+ GetEAHandler * ea_table;
+} core;
+
+#define GETIP (core.cseip-SegBase(cs))
+#define SAVEIP reg_eip=GETIP;
+#define LOADIP core.cseip=(SegBase(cs)+reg_eip);
+
+#define SegBase(c) SegPhys(c)
+#define BaseDS core.base_ds
+#define BaseSS core.base_ss
+
+
+#define MAX_PQ_SIZE 32
+static Bit8u prefetch_buffer[MAX_PQ_SIZE];
+static bool pq_valid=false;
+static Bitu pq_start;
+
+static Bit8u Fetchb() {
+ Bit8u temp;
+ if (pq_valid && (core.cseip>=pq_start) && (core.cseip<pq_start+CPU_PrefetchQueueSize)) {
+ temp=prefetch_buffer[core.cseip-pq_start];
+ if ((core.cseip+1>=pq_start+CPU_PrefetchQueueSize-4) &&
+ (core.cseip+1<pq_start+CPU_PrefetchQueueSize)) {
+ Bitu remaining_bytes=pq_start+CPU_PrefetchQueueSize-(core.cseip+1);
+ for (Bitu i=0; i<remaining_bytes; i++) prefetch_buffer[i]=prefetch_buffer[core.cseip+1-pq_start+i];
+ for (Bitu i=remaining_bytes; i<CPU_PrefetchQueueSize; i++) prefetch_buffer[i]=LoadMb(core.cseip+1+i);
+ pq_start=core.cseip+1;
+ pq_valid=true;
+ }
+ } else {
+ for (Bitu i=0; i<CPU_PrefetchQueueSize; i++) prefetch_buffer[i]=LoadMb(core.cseip+i);
+ pq_start=core.cseip;
+ pq_valid=true;
+ temp=prefetch_buffer[0];
+ }
+/* if (temp!=LoadMb(core.cseip)) {
+ LOG_MSG("prefetch queue content!=memory at %x:%x",SegValue(cs),reg_eip);
+ } */
+ core.cseip+=1;
+ return temp;
+}
+
+static Bit16u Fetchw() {
+ Bit16u temp;
+ if (pq_valid && (core.cseip>=pq_start) && (core.cseip+2<pq_start+CPU_PrefetchQueueSize)) {
+ temp=prefetch_buffer[core.cseip-pq_start]|
+ (prefetch_buffer[core.cseip-pq_start+1]<<8);
+ if ((core.cseip+2>=pq_start+CPU_PrefetchQueueSize-4) &&
+ (core.cseip+2<pq_start+CPU_PrefetchQueueSize)) {
+ Bitu remaining_bytes=pq_start+CPU_PrefetchQueueSize-(core.cseip+2);
+ for (Bitu i=0; i<remaining_bytes; i++) prefetch_buffer[i]=prefetch_buffer[core.cseip+2-pq_start+i];
+ for (Bitu i=remaining_bytes; i<CPU_PrefetchQueueSize; i++) prefetch_buffer[i]=LoadMb(core.cseip+2+i);
+ pq_start=core.cseip+2;
+ pq_valid=true;
+ }
+ } else {
+ for (Bitu i=0; i<CPU_PrefetchQueueSize; i++) prefetch_buffer[i]=LoadMb(core.cseip+i);
+ pq_start=core.cseip;
+ pq_valid=true;
+ temp=prefetch_buffer[0] | (prefetch_buffer[1]<<8);
+ }
+/* if (temp!=LoadMw(core.cseip)) {
+ LOG_MSG("prefetch queue content!=memory at %x:%x",SegValue(cs),reg_eip);
+ } */
+ core.cseip+=2;
+ return temp;
+}
+
+static Bit32u Fetchd() {
+ Bit32u temp;
+ if (pq_valid && (core.cseip>=pq_start) && (core.cseip+4<pq_start+CPU_PrefetchQueueSize)) {
+ temp=prefetch_buffer[core.cseip-pq_start]|
+ (prefetch_buffer[core.cseip-pq_start+1]<<8)|
+ (prefetch_buffer[core.cseip-pq_start+2]<<16)|
+ (prefetch_buffer[core.cseip-pq_start+3]<<24);
+ if ((core.cseip+4>=pq_start+CPU_PrefetchQueueSize-4) &&
+ (core.cseip+4<pq_start+CPU_PrefetchQueueSize)) {
+ Bitu remaining_bytes=pq_start+CPU_PrefetchQueueSize-(core.cseip+4);
+ for (Bitu i=0; i<remaining_bytes; i++) prefetch_buffer[i]=prefetch_buffer[core.cseip+4-pq_start+i];
+ for (Bitu i=remaining_bytes; i<CPU_PrefetchQueueSize; i++) prefetch_buffer[i]=LoadMb(core.cseip+4+i);
+ pq_start=core.cseip+4;
+ pq_valid=true;
+ }
+ } else {
+ for (Bitu i=0; i<CPU_PrefetchQueueSize; i++) prefetch_buffer[i]=LoadMb(core.cseip+i);
+ pq_start=core.cseip;
+ pq_valid=true;
+ temp=prefetch_buffer[0] | (prefetch_buffer[1]<<8) |
+ (prefetch_buffer[2]<<16) | (prefetch_buffer[3]<<24);
+ }
+/* if (temp!=LoadMd(core.cseip)) {
+ LOG_MSG("prefetch queue content!=memory at %x:%x",SegValue(cs),reg_eip);
+ } */
+ core.cseip+=4;
+ return temp;
+}
+
+#define Push_16 CPU_Push16
+#define Push_32 CPU_Push32
+#define Pop_16 CPU_Pop16
+#define Pop_32 CPU_Pop32
+
+#include "instructions.h"
+#include "core_normal/support.h"
+#include "core_normal/string.h"
+
+
+#define EALookupTable (core.ea_table)
+
+Bits CPU_Core_Prefetch_Run(void) {
+ bool invalidate_pq=false;
+ while (CPU_Cycles-->0) {
+ if (invalidate_pq) {
+ pq_valid=false;
+ invalidate_pq=false;
+ }
+ LOADIP;
+ core.opcode_index=cpu.code.big*0x200;
+ core.prefixes=cpu.code.big;
+ core.ea_table=&EATable[cpu.code.big*256];
+ BaseDS=SegBase(ds);
+ BaseSS=SegBase(ss);
+ core.base_val_ds=ds;
+#if C_DEBUG
+#if C_HEAVY_DEBUG
+ if (DEBUG_HeavyIsBreakpoint()) {
+ FillFlags();
+ return debugCallback;
+ };
+#endif
+ cycle_count++;
+#endif
+restart_opcode:
+ Bit8u next_opcode=Fetchb();
+ invalidate_pq=false;
+ if (core.opcode_index&OPCODE_0F) invalidate_pq=true;
+ else switch (next_opcode) {
+ case 0x70: case 0x71: case 0x72: case 0x73:
+ case 0x74: case 0x75: case 0x76: case 0x77:
+ case 0x78: case 0x79: case 0x7a: case 0x7b:
+ case 0x7c: case 0x7d: case 0x7e: case 0x7f: // jcc
+ case 0x9a: // call
+ case 0xc2: case 0xc3: // retn
+ case 0xc8: // enter
+ case 0xc9: // leave
+ case 0xca: case 0xcb: // retf
+ case 0xcc: // int3
+ case 0xcd: // int
+ case 0xce: // into
+ case 0xcf: // iret
+ case 0xe0: // loopnz
+ case 0xe1: // loopz
+ case 0xe2: // loop
+ case 0xe3: // jcxz
+ case 0xe8: // call
+ case 0xe9: case 0xea: case 0xeb: // jmp
+ case 0xff:
+ invalidate_pq=true;
+ break;
+ default:
+ break;
+ }
+ switch (core.opcode_index+next_opcode) {
+ #include "core_normal/prefix_none.h"
+ #include "core_normal/prefix_0f.h"
+ #include "core_normal/prefix_66.h"
+ #include "core_normal/prefix_66_0f.h"
+ default:
+ illegal_opcode:
+#if C_DEBUG
+ {
+ Bitu len=(GETIP-reg_eip);
+ LOADIP;
+ if (len>16) len=16;
+ char tempcode[16*2+1];char * writecode=tempcode;
+ for (;len>0;len--) {
+ sprintf(writecode,"%02X",mem_readb(core.cseip++));
+ writecode+=2;
+ }
+ LOG(LOG_CPU,LOG_NORMAL)("Illegal/Unhandled opcode %s",tempcode);
+ }
+#endif
+ CPU_Exception(6,0);
+ invalidate_pq=true;
+ continue;
+ }
+ SAVEIP;
+ }
+ FillFlags();
+ return CBRET_NONE;
+decode_end:
+ SAVEIP;
+ FillFlags();
+ return CBRET_NONE;
+}
+
+Bits CPU_Core_Prefetch_Trap_Run(void) {
+ Bits oldCycles = CPU_Cycles;
+ CPU_Cycles = 1;
+ cpu.trap_skip = false;
+
+ Bits ret=CPU_Core_Prefetch_Run();
+ if (!cpu.trap_skip) CPU_HW_Interrupt(1);
+ CPU_Cycles = oldCycles-1;
+ cpudecoder = &CPU_Core_Prefetch_Run;
+
+ return ret;
+}
+
+
+
+void CPU_Core_Prefetch_Init(void) {
+
+}
+
diff --git a/src/cpu/core_simple.cpp b/src/cpu/core_simple.cpp
new file mode 100644
index 000000000..9f18c3bc7
--- /dev/null
+++ b/src/cpu/core_simple.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+
+#include "dosbox.h"
+#include "mem.h"
+#include "cpu.h"
+#include "lazyflags.h"
+#include "inout.h"
+#include "callback.h"
+#include "pic.h"
+#include "fpu.h"
+
+#if C_DEBUG
+#include "debug.h"
+#endif
+
+#include "paging.h"
+#define SegBase(c) SegPhys(c)
+#define LoadMb(off) mem_readb(off)
+#define LoadMw(off) mem_readw(off)
+#define LoadMd(off) mem_readd(off)
+
+#define SaveMb(off,val) mem_writeb(off,val)
+#define SaveMw(off,val) mem_writew(off,val)
+#define SaveMd(off,val) mem_writed(off,val)
+
+extern Bitu cycle_count;
+
+#if C_FPU
+#define CPU_FPU 1 //Enable FPU escape instructions
+#endif
+
+#define CPU_PIC_CHECK 1
+#define CPU_TRAP_CHECK 1
+
+#define CPU_TRAP_DECODER CPU_Core_Simple_Trap_Run
+
+#define OPCODE_NONE 0x000
+#define OPCODE_0F 0x100
+#define OPCODE_SIZE 0x200
+
+#define PREFIX_ADDR 0x1
+#define PREFIX_REP 0x2
+
+#define TEST_PREFIX_ADDR (core.prefixes & PREFIX_ADDR)
+#define TEST_PREFIX_REP (core.prefixes & PREFIX_REP)
+
+#define DO_PREFIX_SEG(_SEG) \
+ BaseDS=SegBase(_SEG); \
+ BaseSS=SegBase(_SEG); \
+ core.base_val_ds=_SEG; \
+ goto restart_opcode;
+
+#define DO_PREFIX_ADDR() \
+ core.prefixes=(core.prefixes & ~PREFIX_ADDR) | \
+ (cpu.code.big ^ PREFIX_ADDR); \
+ core.ea_table=&EATable[(core.prefixes&1) * 256]; \
+ goto restart_opcode;
+
+#define DO_PREFIX_REP(_ZERO) \
+ core.prefixes|=PREFIX_REP; \
+ core.rep_zero=_ZERO; \
+ goto restart_opcode;
+
+typedef PhysPt (*GetEAHandler)(void);
+
+static const Bit32u AddrMaskTable[2]={0x0000ffff,0xffffffff};
+
+static struct {
+ Bitu opcode_index;
+#if defined (_MSC_VER)
+ volatile HostPt cseip;
+#else
+ HostPt cseip;
+#endif
+ PhysPt base_ds,base_ss;
+ SegNames base_val_ds;
+ bool rep_zero;
+ Bitu prefixes;
+ GetEAHandler * ea_table;
+} core;
+
+#define GETIP (core.cseip-SegBase(cs)-MemBase)
+#define SAVEIP reg_eip=GETIP;
+#define LOADIP core.cseip=(MemBase+SegBase(cs)+reg_eip);
+
+#define SegBase(c) SegPhys(c)
+#define BaseDS core.base_ds
+#define BaseSS core.base_ss
+
+static INLINE Bit8u Fetchb() {
+ Bit8u temp=host_readb(core.cseip);
+ core.cseip+=1;
+ return temp;
+}
+
+static INLINE Bit16u Fetchw() {
+ Bit16u temp=host_readw(core.cseip);
+ core.cseip+=2;
+ return temp;
+}
+static INLINE Bit32u Fetchd() {
+ Bit32u temp=host_readd(core.cseip);
+ core.cseip+=4;
+ return temp;
+}
+
+#define Push_16 CPU_Push16
+#define Push_32 CPU_Push32
+#define Pop_16 CPU_Pop16
+#define Pop_32 CPU_Pop32
+
+#include "instructions.h"
+#include "core_normal/support.h"
+#include "core_normal/string.h"
+
+
+#define EALookupTable (core.ea_table)
+
+Bits CPU_Core_Simple_Run(void) {
+ while (CPU_Cycles-->0) {
+ LOADIP;
+ core.opcode_index=cpu.code.big*0x200;
+ core.prefixes=cpu.code.big;
+ core.ea_table=&EATable[cpu.code.big*256];
+ BaseDS=SegBase(ds);
+ BaseSS=SegBase(ss);
+ core.base_val_ds=ds;
+#if C_DEBUG
+#if C_HEAVY_DEBUG
+ if (DEBUG_HeavyIsBreakpoint()) {
+ FillFlags();
+ return debugCallback;
+ };
+#endif
+ cycle_count++;
+#endif
+restart_opcode:
+ switch (core.opcode_index+Fetchb()) {
+
+ #include "core_normal/prefix_none.h"
+ #include "core_normal/prefix_0f.h"
+ #include "core_normal/prefix_66.h"
+ #include "core_normal/prefix_66_0f.h"
+ default:
+ illegal_opcode:
+#if C_DEBUG
+ {
+ Bitu len=(GETIP-reg_eip);
+ LOADIP;
+ if (len>16) len=16;
+ char tempcode[16*2+1];char * writecode=tempcode;
+ for (;len>0;len--) {
+// sprintf(writecode,"%X",mem_readb(core.cseip++));
+ writecode+=2;
+ }
+ LOG(LOG_CPU,LOG_NORMAL)("Illegal/Unhandled opcode %s",tempcode);
+ }
+#endif
+ CPU_Exception(6,0);
+ continue;
+ }
+ SAVEIP;
+ }
+ FillFlags();
+ return CBRET_NONE;
+decode_end:
+ SAVEIP;
+ FillFlags();
+ return CBRET_NONE;
+}
+
+Bits CPU_Core_Simple_Trap_Run(void) {
+ Bits oldCycles = CPU_Cycles;
+ CPU_Cycles = 1;
+ cpu.trap_skip = false;
+
+ Bits ret=CPU_Core_Simple_Run();
+ if (!cpu.trap_skip) CPU_HW_Interrupt(1);
+ CPU_Cycles = oldCycles-1;
+ cpudecoder = &CPU_Core_Simple_Run;
+
+ return ret;
+}
+
+
+
+void CPU_Core_Simple_Init(void) {
+
+}
diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp
new file mode 100644
index 000000000..bc1c576e2
--- /dev/null
+++ b/src/cpu/cpu.cpp
@@ -0,0 +1,2430 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <assert.h>
+#include <sstream>
+#include <stddef.h>
+#include "dosbox.h"
+#include "cpu.h"
+#include "memory.h"
+#include "debug.h"
+#include "mapper.h"
+#include "setup.h"
+#include "programs.h"
+#include "paging.h"
+#include "lazyflags.h"
+#include "support.h"
+
+Bitu DEBUG_EnableDebugger(void);
+extern void GFX_SetTitle(Bit32s cycles ,Bits frameskip,bool paused);
+
+#if 1
+#undef LOG
+#if defined (_MSC_VER)
+#define LOG(X,Y)
+#else
+#define LOG(X,Y) CPU_LOG
+#define CPU_LOG(...)
+#endif
+#endif
+
+CPU_Regs cpu_regs;
+CPUBlock cpu;
+Segments Segs;
+
+Bit32s CPU_Cycles = 0;
+Bit32s CPU_CycleLeft = 3000;
+Bit32s CPU_CycleMax = 3000;
+Bit32s CPU_OldCycleMax = 3000;
+Bit32s CPU_CyclePercUsed = 100;
+Bit32s CPU_CycleLimit = -1;
+Bit32s CPU_CycleUp = 0;
+Bit32s CPU_CycleDown = 0;
+Bit64s CPU_IODelayRemoved = 0;
+CPU_Decoder * cpudecoder;
+bool CPU_CycleAutoAdjust = false;
+bool CPU_SkipCycleAutoAdjust = false;
+Bitu CPU_AutoDetermineMode = 0;
+
+Bitu CPU_ArchitectureType = CPU_ARCHTYPE_MIXED;
+
+Bitu CPU_extflags_toggle=0; // ID and AC flags may be toggled depending on emulated CPU architecture
+
+Bitu CPU_PrefetchQueueSize=0;
+
+void CPU_Core_Full_Init(void);
+void CPU_Core_Normal_Init(void);
+void CPU_Core_Simple_Init(void);
+#if (C_DYNAMIC_X86)
+void CPU_Core_Dyn_X86_Init(void);
+void CPU_Core_Dyn_X86_Cache_Init(bool enable_cache);
+void CPU_Core_Dyn_X86_Cache_Close(void);
+void CPU_Core_Dyn_X86_SetFPUMode(bool dh_fpu);
+#elif (C_DYNREC)
+void CPU_Core_Dynrec_Init(void);
+void CPU_Core_Dynrec_Cache_Init(bool enable_cache);
+void CPU_Core_Dynrec_Cache_Close(void);
+#endif
+
+/* In debug mode exceptions are tested and dosbox exits when
+ * a unhandled exception state is detected.
+ * USE CHECK_EXCEPT to raise an exception in that case to see if that exception
+ * solves the problem.
+ *
+ * In non-debug mode dosbox doesn't do detection (and hence doesn't crash at
+ * that point). (game might crash later due to the unhandled exception) */
+
+#if C_DEBUG
+// #define CPU_CHECK_EXCEPT 1
+// #define CPU_CHECK_IGNORE 1
+ /* Use CHECK_EXCEPT when something doesn't work to see if a exception is
+ * needed that isn't enabled by default.*/
+#else
+/* NORMAL NO CHECKING => More Speed */
+#define CPU_CHECK_IGNORE 1
+#endif /* C_DEBUG */
+
+#if defined(CPU_CHECK_IGNORE)
+#define CPU_CHECK_COND(cond,msg,exc,sel) { \
+ if (cond) do {} while (0); \
+}
+#elif defined(CPU_CHECK_EXCEPT)
+#define CPU_CHECK_COND(cond,msg,exc,sel) { \
+ if (cond) { \
+ CPU_Exception(exc,sel); \
+ return; \
+ } \
+}
+#else
+#define CPU_CHECK_COND(cond,msg,exc,sel) { \
+ if (cond) E_Exit(msg); \
+}
+#endif
+
+
+void Descriptor::Load(PhysPt address) {
+ cpu.mpl=0;
+ Bit32u* data = (Bit32u*)&saved;
+ *data = mem_readd(address);
+ *(data+1) = mem_readd(address+4);
+ cpu.mpl=3;
+}
+void Descriptor:: Save(PhysPt address) {
+ cpu.mpl=0;
+ Bit32u* data = (Bit32u*)&saved;
+ mem_writed(address,*data);
+ mem_writed(address+4,*(data+1));
+ cpu.mpl=03;
+}
+
+
+void CPU_Push16(Bitu value) {
+ Bit32u new_esp=(reg_esp&cpu.stack.notmask)|((reg_esp-2)&cpu.stack.mask);
+ mem_writew(SegPhys(ss) + (new_esp & cpu.stack.mask) ,value);
+ reg_esp=new_esp;
+}
+
+void CPU_Push32(Bitu value) {
+ Bit32u new_esp=(reg_esp&cpu.stack.notmask)|((reg_esp-4)&cpu.stack.mask);
+ mem_writed(SegPhys(ss) + (new_esp & cpu.stack.mask) ,value);
+ reg_esp=new_esp;
+}
+
+Bitu CPU_Pop16(void) {
+ Bitu val=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask));
+ reg_esp=(reg_esp&cpu.stack.notmask)|((reg_esp+2)&cpu.stack.mask);
+ return val;
+}
+
+Bitu CPU_Pop32(void) {
+ Bitu val=mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask));
+ reg_esp=(reg_esp&cpu.stack.notmask)|((reg_esp+4)&cpu.stack.mask);
+ return val;
+}
+
+PhysPt SelBase(Bitu sel) {
+ if (cpu.cr0 & CR0_PROTECTION) {
+ Descriptor desc;
+ cpu.gdt.GetDescriptor(sel,desc);
+ return desc.GetBase();
+ } else {
+ return sel<<4;
+ }
+}
+
+
+void CPU_SetFlags(Bitu word,Bitu mask) {
+ mask|=CPU_extflags_toggle; // ID-flag and AC-flag can be toggled on CPUID-supporting CPUs
+ reg_flags=(reg_flags & ~mask)|(word & mask)|2;
+ cpu.direction=1-((reg_flags & FLAG_DF) >> 9);
+}
+
+bool CPU_PrepareException(Bitu which,Bitu error) {
+ cpu.exception.which=which;
+ cpu.exception.error=error;
+ return true;
+}
+
+bool CPU_CLI(void) {
+ if (cpu.pmode && ((!GETFLAG(VM) && (GETFLAG_IOPL<cpu.cpl)) || (GETFLAG(VM) && (GETFLAG_IOPL<3)))) {
+ return CPU_PrepareException(EXCEPTION_GP,0);
+ } else {
+ SETFLAGBIT(IF,false);
+ return false;
+ }
+}
+
+bool CPU_STI(void) {
+ if (cpu.pmode && ((!GETFLAG(VM) && (GETFLAG_IOPL<cpu.cpl)) || (GETFLAG(VM) && (GETFLAG_IOPL<3)))) {
+ return CPU_PrepareException(EXCEPTION_GP,0);
+ } else {
+ SETFLAGBIT(IF,true);
+ return false;
+ }
+}
+
+bool CPU_POPF(Bitu use32) {
+ if (cpu.pmode && GETFLAG(VM) && (GETFLAG(IOPL)!=FLAG_IOPL)) {
+ /* Not enough privileges to execute POPF */
+ return CPU_PrepareException(EXCEPTION_GP,0);
+ }
+ Bitu mask=FMASK_ALL;
+ /* IOPL field can only be modified when CPL=0 or in real mode: */
+ if (cpu.pmode && (cpu.cpl>0)) mask &= (~FLAG_IOPL);
+ if (cpu.pmode && !GETFLAG(VM) && (GETFLAG_IOPL<cpu.cpl)) mask &= (~FLAG_IF);
+ if (use32)
+ CPU_SetFlags(CPU_Pop32(),mask);
+ else CPU_SetFlags(CPU_Pop16(),mask & 0xffff);
+ DestroyConditionFlags();
+ return false;
+}
+
+bool CPU_PUSHF(Bitu use32) {
+ if (cpu.pmode && GETFLAG(VM) && (GETFLAG(IOPL)!=FLAG_IOPL)) {
+ /* Not enough privileges to execute PUSHF */
+ return CPU_PrepareException(EXCEPTION_GP,0);
+ }
+ FillFlags();
+ if (use32)
+ CPU_Push32(reg_flags & 0xfcffff);
+ else CPU_Push16(reg_flags);
+ return false;
+}
+
+void CPU_CheckSegments(void) {
+ bool needs_invalidation = false;
+ Descriptor desc;
+ if (!cpu.gdt.GetDescriptor(SegValue(es),desc)) needs_invalidation = true;
+ else switch (desc.Type()) {
+ case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+ case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA:
+ if (cpu.cpl > desc.DPL()) needs_invalidation = true;
+ break;
+ default: break; }
+ if (needs_invalidation) CPU_SetSegGeneral(es,0);
+
+ needs_invalidation = false;
+ if (!cpu.gdt.GetDescriptor(SegValue(ds),desc)) needs_invalidation = true;
+ else switch (desc.Type()) {
+ case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+ case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA:
+ if (cpu.cpl > desc.DPL()) needs_invalidation = true;
+ break;
+ default: break; }
+ if (needs_invalidation) CPU_SetSegGeneral(ds,0);
+
+ needs_invalidation = false;
+ if (!cpu.gdt.GetDescriptor(SegValue(fs),desc)) needs_invalidation = true;
+ else switch (desc.Type()) {
+ case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+ case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA:
+ if (cpu.cpl > desc.DPL()) needs_invalidation = true;
+ break;
+ default: break; }
+ if (needs_invalidation) CPU_SetSegGeneral(fs,0);
+
+ needs_invalidation = false;
+ if (!cpu.gdt.GetDescriptor(SegValue(gs),desc)) needs_invalidation = true;
+ else switch (desc.Type()) {
+ case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+ case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA:
+ if (cpu.cpl > desc.DPL()) needs_invalidation = true;
+ break;
+ default: break; }
+ if (needs_invalidation) CPU_SetSegGeneral(gs,0);
+}
+
+
+class TaskStateSegment {
+public:
+ TaskStateSegment() {
+ valid=false;
+ }
+ bool IsValid(void) {
+ return valid;
+ }
+ Bitu Get_back(void) {
+ cpu.mpl=0;
+ Bit16u backlink=mem_readw(base);
+ cpu.mpl=3;
+ return backlink;
+ }
+ void SaveSelector(void) {
+ cpu.gdt.SetDescriptor(selector,desc);
+ }
+ void Get_SSx_ESPx(Bitu level,Bitu & _ss,Bitu & _esp) {
+ cpu.mpl=0;
+ if (is386) {
+ PhysPt where=base+offsetof(TSS_32,esp0)+level*8;
+ _esp=mem_readd(where);
+ _ss=mem_readw(where+4);
+ } else {
+ PhysPt where=base+offsetof(TSS_16,sp0)+level*4;
+ _esp=mem_readw(where);
+ _ss=mem_readw(where+2);
+ }
+ cpu.mpl=3;
+ }
+ bool SetSelector(Bitu new_sel) {
+ valid=false;
+ if ((new_sel & 0xfffc)==0) {
+ selector=0;
+ base=0;
+ limit=0;
+ is386=1;
+ return true;
+ }
+ if (new_sel&4) return false;
+ if (!cpu.gdt.GetDescriptor(new_sel,desc)) return false;
+ switch (desc.Type()) {
+ case DESC_286_TSS_A: case DESC_286_TSS_B:
+ case DESC_386_TSS_A: case DESC_386_TSS_B:
+ break;
+ default:
+ return false;
+ }
+ if (!desc.saved.seg.p) return false;
+ selector=new_sel;
+ valid=true;
+ base=desc.GetBase();
+ limit=desc.GetLimit();
+ is386=desc.Is386();
+ return true;
+ }
+ TSS_Descriptor desc;
+ Bitu selector;
+ PhysPt base;
+ Bitu limit;
+ Bitu is386;
+ bool valid;
+};
+
+TaskStateSegment cpu_tss;
+
+enum TSwitchType {
+ TSwitch_JMP,TSwitch_CALL_INT,TSwitch_IRET
+};
+
+bool CPU_SwitchTask(Bitu new_tss_selector,TSwitchType tstype,Bitu old_eip) {
+ FillFlags();
+ TaskStateSegment new_tss;
+ if (!new_tss.SetSelector(new_tss_selector))
+ E_Exit("Illegal TSS for switch, selector=%x, switchtype=%x",new_tss_selector,tstype);
+ if (tstype==TSwitch_IRET) {
+ if (!new_tss.desc.IsBusy())
+ E_Exit("TSS not busy for IRET");
+ } else {
+ if (new_tss.desc.IsBusy())
+ E_Exit("TSS busy for JMP/CALL/INT");
+ }
+ Bitu new_cr3=0;
+ Bitu new_eax,new_ebx,new_ecx,new_edx,new_esp,new_ebp,new_esi,new_edi;
+ Bitu new_es,new_cs,new_ss,new_ds,new_fs,new_gs;
+ Bitu new_ldt,new_eip,new_eflags;
+ /* Read new context from new TSS */
+ if (new_tss.is386) {
+ new_cr3=mem_readd(new_tss.base+offsetof(TSS_32,cr3));
+ new_eip=mem_readd(new_tss.base+offsetof(TSS_32,eip));
+ new_eflags=mem_readd(new_tss.base+offsetof(TSS_32,eflags));
+ new_eax=mem_readd(new_tss.base+offsetof(TSS_32,eax));
+ new_ecx=mem_readd(new_tss.base+offsetof(TSS_32,ecx));
+ new_edx=mem_readd(new_tss.base+offsetof(TSS_32,edx));
+ new_ebx=mem_readd(new_tss.base+offsetof(TSS_32,ebx));
+ new_esp=mem_readd(new_tss.base+offsetof(TSS_32,esp));
+ new_ebp=mem_readd(new_tss.base+offsetof(TSS_32,ebp));
+ new_edi=mem_readd(new_tss.base+offsetof(TSS_32,edi));
+ new_esi=mem_readd(new_tss.base+offsetof(TSS_32,esi));
+
+ new_es=mem_readw(new_tss.base+offsetof(TSS_32,es));
+ new_cs=mem_readw(new_tss.base+offsetof(TSS_32,cs));
+ new_ss=mem_readw(new_tss.base+offsetof(TSS_32,ss));
+ new_ds=mem_readw(new_tss.base+offsetof(TSS_32,ds));
+ new_fs=mem_readw(new_tss.base+offsetof(TSS_32,fs));
+ new_gs=mem_readw(new_tss.base+offsetof(TSS_32,gs));
+ new_ldt=mem_readw(new_tss.base+offsetof(TSS_32,ldt));
+ } else {
+ E_Exit("286 task switch");
+ new_cr3=0;
+ new_eip=0;
+ new_eflags=0;
+ new_eax=0; new_ecx=0; new_edx=0; new_ebx=0;
+ new_esp=0; new_ebp=0; new_edi=0; new_esi=0;
+
+ new_es=0; new_cs=0; new_ss=0; new_ds=0; new_fs=0; new_gs=0;
+ new_ldt=0;
+ }
+
+ /* Check if we need to clear busy bit of old TASK */
+ if (tstype==TSwitch_JMP || tstype==TSwitch_IRET) {
+ cpu_tss.desc.SetBusy(false);
+ cpu_tss.SaveSelector();
+ }
+ Bit32u old_flags = reg_flags;
+ if (tstype==TSwitch_IRET) old_flags &= (~FLAG_NT);
+
+ /* Save current context in current TSS */
+ if (cpu_tss.is386) {
+ mem_writed(cpu_tss.base+offsetof(TSS_32,eflags),old_flags);
+ mem_writed(cpu_tss.base+offsetof(TSS_32,eip),old_eip);
+
+ mem_writed(cpu_tss.base+offsetof(TSS_32,eax),reg_eax);
+ mem_writed(cpu_tss.base+offsetof(TSS_32,ecx),reg_ecx);
+ mem_writed(cpu_tss.base+offsetof(TSS_32,edx),reg_edx);
+ mem_writed(cpu_tss.base+offsetof(TSS_32,ebx),reg_ebx);
+ mem_writed(cpu_tss.base+offsetof(TSS_32,esp),reg_esp);
+ mem_writed(cpu_tss.base+offsetof(TSS_32,ebp),reg_ebp);
+ mem_writed(cpu_tss.base+offsetof(TSS_32,esi),reg_esi);
+ mem_writed(cpu_tss.base+offsetof(TSS_32,edi),reg_edi);
+
+ mem_writed(cpu_tss.base+offsetof(TSS_32,es),SegValue(es));
+ mem_writed(cpu_tss.base+offsetof(TSS_32,cs),SegValue(cs));
+ mem_writed(cpu_tss.base+offsetof(TSS_32,ss),SegValue(ss));
+ mem_writed(cpu_tss.base+offsetof(TSS_32,ds),SegValue(ds));
+ mem_writed(cpu_tss.base+offsetof(TSS_32,fs),SegValue(fs));
+ mem_writed(cpu_tss.base+offsetof(TSS_32,gs),SegValue(gs));
+ } else {
+ E_Exit("286 task switch");
+ }
+
+ /* Setup a back link to the old TSS in new TSS */
+ if (tstype==TSwitch_CALL_INT) {
+ if (new_tss.is386) {
+ mem_writed(new_tss.base+offsetof(TSS_32,back),cpu_tss.selector);
+ } else {
+ mem_writew(new_tss.base+offsetof(TSS_16,back),cpu_tss.selector);
+ }
+ /* And make the new task's eflag have the nested task bit */
+ new_eflags|=FLAG_NT;
+ }
+ /* Set the busy bit in the new task */
+ if (tstype==TSwitch_JMP || tstype==TSwitch_CALL_INT) {
+ new_tss.desc.SetBusy(true);
+ new_tss.SaveSelector();
+ }
+
+// cpu.cr0|=CR0_TASKSWITCHED;
+ if (new_tss_selector == cpu_tss.selector) {
+ reg_eip = old_eip;
+ new_cs = SegValue(cs);
+ new_ss = SegValue(ss);
+ new_ds = SegValue(ds);
+ new_es = SegValue(es);
+ new_fs = SegValue(fs);
+ new_gs = SegValue(gs);
+ } else {
+
+ /* Setup the new cr3 */
+ PAGING_SetDirBase(new_cr3);
+
+ /* Load new context */
+ if (new_tss.is386) {
+ reg_eip=new_eip;
+ CPU_SetFlags(new_eflags,FMASK_ALL | FLAG_VM);
+ reg_eax=new_eax;
+ reg_ecx=new_ecx;
+ reg_edx=new_edx;
+ reg_ebx=new_ebx;
+ reg_esp=new_esp;
+ reg_ebp=new_ebp;
+ reg_edi=new_edi;
+ reg_esi=new_esi;
+
+// new_cs=mem_readw(new_tss.base+offsetof(TSS_32,cs));
+ } else {
+ E_Exit("286 task switch");
+ }
+ }
+ /* Load the new selectors */
+ if (reg_flags & FLAG_VM) {
+ SegSet16(cs,new_cs);
+ cpu.code.big=false;
+ cpu.cpl=3; //We don't have segment caches so this will do
+ } else {
+ /* Protected mode task */
+ if (new_ldt!=0) CPU_LLDT(new_ldt);
+ /* Load the new CS*/
+ Descriptor cs_desc;
+ cpu.cpl=new_cs & 3;
+ if (!cpu.gdt.GetDescriptor(new_cs,cs_desc))
+ E_Exit("Task switch with CS beyond limits");
+ if (!cs_desc.saved.seg.p)
+ E_Exit("Task switch with non present code-segment");
+ switch (cs_desc.Type()) {
+ case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA:
+ case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA:
+ if (cpu.cpl != cs_desc.DPL()) E_Exit("Task CS RPL != DPL");
+ goto doconforming;
+ case DESC_CODE_N_C_A: case DESC_CODE_N_C_NA:
+ case DESC_CODE_R_C_A: case DESC_CODE_R_C_NA:
+ if (cpu.cpl < cs_desc.DPL()) E_Exit("Task CS RPL < DPL");
+doconforming:
+ Segs.phys[cs]=cs_desc.GetBase();
+ cpu.code.big=cs_desc.Big()>0;
+ Segs.val[cs]=new_cs;
+ break;
+ default:
+ E_Exit("Task switch CS Type %d",cs_desc.Type());
+ }
+ }
+ CPU_SetSegGeneral(es,new_es);
+ CPU_SetSegGeneral(ss,new_ss);
+ CPU_SetSegGeneral(ds,new_ds);
+ CPU_SetSegGeneral(fs,new_fs);
+ CPU_SetSegGeneral(gs,new_gs);
+ if (!cpu_tss.SetSelector(new_tss_selector)) {
+ LOG(LOG_CPU,LOG_NORMAL)("TaskSwitch: set tss selector %X failed",new_tss_selector);
+ }
+// cpu_tss.desc.SetBusy(true);
+// cpu_tss.SaveSelector();
+// LOG_MSG("Task CPL %X CS:%X IP:%X SS:%X SP:%X eflags %x",cpu.cpl,SegValue(cs),reg_eip,SegValue(ss),reg_esp,reg_flags);
+ return true;
+}
+
+bool CPU_IO_Exception(Bitu port,Bitu size) {
+ if (cpu.pmode && ((GETFLAG_IOPL<cpu.cpl) || GETFLAG(VM))) {
+ cpu.mpl=0;
+ if (!cpu_tss.is386) goto doexception;
+ PhysPt bwhere=cpu_tss.base+0x66;
+ Bitu ofs=mem_readw(bwhere);
+ if (ofs>cpu_tss.limit) goto doexception;
+ bwhere=cpu_tss.base+ofs+(port/8);
+ Bitu map=mem_readw(bwhere);
+ Bitu mask=(0xffff>>(16-size)) << (port&7);
+ if (map & mask) goto doexception;
+ cpu.mpl=3;
+ }
+ return false;
+doexception:
+ cpu.mpl=3;
+ LOG(LOG_CPU,LOG_NORMAL)("IO Exception port %X",port);
+ return CPU_PrepareException(EXCEPTION_GP,0);
+}
+
+void CPU_Exception(Bitu which,Bitu error ) {
+// LOG_MSG("Exception %d error %x",which,error);
+ cpu.exception.error=error;
+ CPU_Interrupt(which,CPU_INT_EXCEPTION | ((which>=8) ? CPU_INT_HAS_ERROR : 0),reg_eip);
+}
+
+Bit8u lastint;
+void CPU_Interrupt(Bitu num,Bitu type,Bitu oldeip) {
+ lastint=num;
+ FillFlags();
+#if C_DEBUG
+ switch (num) {
+ case 0xcd:
+#if C_HEAVY_DEBUG
+ LOG(LOG_CPU,LOG_ERROR)("Call to interrupt 0xCD this is BAD");
+// DEBUG_HeavyWriteLogInstruction();
+// E_Exit("Call to interrupt 0xCD this is BAD");
+#endif
+ break;
+ case 0x03:
+ if (DEBUG_Breakpoint()) {
+ CPU_Cycles=0;
+ return;
+ }
+ };
+#endif
+ if (!cpu.pmode) {
+ /* Save everything on a 16-bit stack */
+ CPU_Push16(reg_flags & 0xffff);
+ CPU_Push16(SegValue(cs));
+ CPU_Push16(oldeip);
+ SETFLAGBIT(IF,false);
+ SETFLAGBIT(TF,false);
+ /* Get the new CS:IP from vector table */
+ PhysPt base=cpu.idt.GetBase();
+ reg_eip=mem_readw(base+(num << 2));
+ Segs.val[cs]=mem_readw(base+(num << 2)+2);
+ Segs.phys[cs]=Segs.val[cs]<<4;
+ cpu.code.big=false;
+ return;
+ } else {
+ /* Protected Mode Interrupt */
+ if ((reg_flags & FLAG_VM) && (type&CPU_INT_SOFTWARE) && !(type&CPU_INT_NOIOPLCHECK)) {
+// LOG_MSG("Software int in v86, AH %X IOPL %x",reg_ah,(reg_flags & FLAG_IOPL) >>12);
+ if ((reg_flags & FLAG_IOPL)!=FLAG_IOPL) {
+ CPU_Exception(EXCEPTION_GP,0);
+ return;
+ }
+ }
+
+ Descriptor gate;
+ if (!cpu.idt.GetDescriptor(num<<3,gate)) {
+ // zone66
+ CPU_Exception(EXCEPTION_GP,num*8+2+(type&CPU_INT_SOFTWARE)?0:1);
+ return;
+ }
+
+ if ((type&CPU_INT_SOFTWARE) && (gate.DPL()<cpu.cpl)) {
+ // zone66, win3.x e
+ CPU_Exception(EXCEPTION_GP,num*8+2);
+ return;
+ }
+
+
+ switch (gate.Type()) {
+ case DESC_286_INT_GATE: case DESC_386_INT_GATE:
+ case DESC_286_TRAP_GATE: case DESC_386_TRAP_GATE:
+ {
+ CPU_CHECK_COND(!gate.saved.seg.p,
+ "INT:Gate segment not present",
+ EXCEPTION_NP,num*8+2+(type&CPU_INT_SOFTWARE)?0:1)
+
+ Descriptor cs_desc;
+ Bitu gate_sel=gate.GetSelector();
+ Bitu gate_off=gate.GetOffset();
+ CPU_CHECK_COND((gate_sel & 0xfffc)==0,
+ "INT:Gate with CS zero selector",
+ EXCEPTION_GP,(type&CPU_INT_SOFTWARE)?0:1)
+ CPU_CHECK_COND(!cpu.gdt.GetDescriptor(gate_sel,cs_desc),
+ "INT:Gate with CS beyond limit",
+ EXCEPTION_GP,(gate_sel & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
+
+ Bitu cs_dpl=cs_desc.DPL();
+ CPU_CHECK_COND(cs_dpl>cpu.cpl,
+ "Interrupt to higher privilege",
+ EXCEPTION_GP,(gate_sel & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
+ switch (cs_desc.Type()) {
+ case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA:
+ case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA:
+ if (cs_dpl<cpu.cpl) {
+ /* Prepare for gate to inner level */
+ CPU_CHECK_COND(!cs_desc.saved.seg.p,
+ "INT:Inner level:CS segment not present",
+ EXCEPTION_NP,(gate_sel & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
+ CPU_CHECK_COND((reg_flags & FLAG_VM) && (cs_dpl!=0),
+ "V86 interrupt calling codesegment with DPL>0",
+ EXCEPTION_GP,gate_sel & 0xfffc)
+
+ Bitu n_ss,n_esp;
+ Bitu o_ss,o_esp;
+ o_ss=SegValue(ss);
+ o_esp=reg_esp;
+ cpu_tss.Get_SSx_ESPx(cs_dpl,n_ss,n_esp);
+ CPU_CHECK_COND((n_ss & 0xfffc)==0,
+ "INT:Gate with SS zero selector",
+ EXCEPTION_TS,(type&CPU_INT_SOFTWARE)?0:1)
+ Descriptor n_ss_desc;
+ CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_ss,n_ss_desc),
+ "INT:Gate with SS beyond limit",
+ EXCEPTION_TS,(n_ss & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
+ CPU_CHECK_COND(((n_ss & 3)!=cs_dpl) || (n_ss_desc.DPL()!=cs_dpl),
+ "INT:Inner level with CS_DPL!=SS_DPL and SS_RPL",
+ EXCEPTION_TS,(n_ss & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
+
+ // check if stack segment is a writable data segment
+ switch (n_ss_desc.Type()) {
+ case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+ break;
+ default:
+ E_Exit("INT:Inner level:Stack segment not writable."); // or #TS(ss_sel+EXT)
+ }
+ CPU_CHECK_COND(!n_ss_desc.saved.seg.p,
+ "INT:Inner level with nonpresent SS",
+ EXCEPTION_SS,(n_ss & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
+
+ // commit point
+ Segs.phys[ss]=n_ss_desc.GetBase();
+ Segs.val[ss]=n_ss;
+ if (n_ss_desc.Big()) {
+ cpu.stack.big=true;
+ cpu.stack.mask=0xffffffff;
+ cpu.stack.notmask=0;
+ reg_esp=n_esp;
+ } else {
+ cpu.stack.big=false;
+ cpu.stack.mask=0xffff;
+ cpu.stack.notmask=0xffff0000;
+ reg_sp=n_esp & 0xffff;
+ }
+
+ cpu.cpl=cs_dpl;
+ if (gate.Type() & 0x8) { /* 32-bit Gate */
+ if (reg_flags & FLAG_VM) {
+ CPU_Push32(SegValue(gs));SegSet16(gs,0x0);
+ CPU_Push32(SegValue(fs));SegSet16(fs,0x0);
+ CPU_Push32(SegValue(ds));SegSet16(ds,0x0);
+ CPU_Push32(SegValue(es));SegSet16(es,0x0);
+ }
+ CPU_Push32(o_ss);
+ CPU_Push32(o_esp);
+ } else { /* 16-bit Gate */
+ if (reg_flags & FLAG_VM) E_Exit("V86 to 16-bit gate");
+ CPU_Push16(o_ss);
+ CPU_Push16(o_esp);
+ }
+// LOG_MSG("INT:Gate to inner level SS:%X SP:%X",n_ss,n_esp);
+ goto do_interrupt;
+ }
+ if (cs_dpl!=cpu.cpl)
+ E_Exit("Non-conforming intra privilege INT with DPL!=CPL");
+ case DESC_CODE_N_C_A: case DESC_CODE_N_C_NA:
+ case DESC_CODE_R_C_A: case DESC_CODE_R_C_NA:
+ /* Prepare stack for gate to same priviledge */
+ CPU_CHECK_COND(!cs_desc.saved.seg.p,
+ "INT:Same level:CS segment not present",
+ EXCEPTION_NP,(gate_sel & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
+ if ((reg_flags & FLAG_VM) && (cs_dpl<cpu.cpl))
+ E_Exit("V86 interrupt doesn't change to pl0"); // or #GP(cs_sel)
+
+ // commit point
+do_interrupt:
+ if (gate.Type() & 0x8) { /* 32-bit Gate */
+ CPU_Push32(reg_flags);
+ CPU_Push32(SegValue(cs));
+ CPU_Push32(oldeip);
+ if (type & CPU_INT_HAS_ERROR) CPU_Push32(cpu.exception.error);
+ } else { /* 16-bit gate */
+ CPU_Push16(reg_flags & 0xffff);
+ CPU_Push16(SegValue(cs));
+ CPU_Push16(oldeip);
+ if (type & CPU_INT_HAS_ERROR) CPU_Push16(cpu.exception.error);
+ }
+ break;
+ default:
+ E_Exit("INT:Gate Selector points to illegal descriptor with type %x",cs_desc.Type());
+ }
+
+ Segs.val[cs]=(gate_sel&0xfffc) | cpu.cpl;
+ Segs.phys[cs]=cs_desc.GetBase();
+ cpu.code.big=cs_desc.Big()>0;
+ reg_eip=gate_off;
+
+ if (!(gate.Type()&1)) {
+ SETFLAGBIT(IF,false);
+ }
+ SETFLAGBIT(TF,false);
+ SETFLAGBIT(NT,false);
+ SETFLAGBIT(VM,false);
+ LOG(LOG_CPU,LOG_NORMAL)("INT:Gate to %X:%X big %d %s",gate_sel,gate_off,cs_desc.Big(),gate.Type() & 0x8 ? "386" : "286");
+ return;
+ }
+ case DESC_TASK_GATE:
+ CPU_CHECK_COND(!gate.saved.seg.p,
+ "INT:Gate segment not present",
+ EXCEPTION_NP,num*8+2+(type&CPU_INT_SOFTWARE)?0:1)
+
+ CPU_SwitchTask(gate.GetSelector(),TSwitch_CALL_INT,oldeip);
+ if (type & CPU_INT_HAS_ERROR) {
+ //TODO Be sure about this, seems somewhat unclear
+ if (cpu_tss.is386) CPU_Push32(cpu.exception.error);
+ else CPU_Push16(cpu.exception.error);
+ }
+ return;
+ default:
+ E_Exit("Illegal descriptor type %X for int %X",gate.Type(),num);
+ }
+ }
+ assert(1);
+ return ; // make compiler happy
+}
+
+
+void CPU_IRET(bool use32,Bitu oldeip) {
+ if (!cpu.pmode) { /* RealMode IRET */
+ if (use32) {
+ reg_eip=CPU_Pop32();
+ SegSet16(cs,CPU_Pop32());
+ CPU_SetFlags(CPU_Pop32(),FMASK_ALL);
+ } else {
+ reg_eip=CPU_Pop16();
+ SegSet16(cs,CPU_Pop16());
+ CPU_SetFlags(CPU_Pop16(),FMASK_ALL & 0xffff);
+ }
+ cpu.code.big=false;
+ DestroyConditionFlags();
+ return;
+ } else { /* Protected mode IRET */
+ if (reg_flags & FLAG_VM) {
+ if ((reg_flags & FLAG_IOPL)!=FLAG_IOPL) {
+ // win3.x e
+ CPU_Exception(EXCEPTION_GP,0);
+ return;
+ } else {
+ if (use32) {
+ Bit32u new_eip=mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask));
+ Bit32u tempesp=(reg_esp&cpu.stack.notmask)|((reg_esp+4)&cpu.stack.mask);
+ Bit32u new_cs=mem_readd(SegPhys(ss) + (tempesp & cpu.stack.mask));
+ tempesp=(tempesp&cpu.stack.notmask)|((tempesp+4)&cpu.stack.mask);
+ Bit32u new_flags=mem_readd(SegPhys(ss) + (tempesp & cpu.stack.mask));
+ reg_esp=(tempesp&cpu.stack.notmask)|((tempesp+4)&cpu.stack.mask);
+
+ reg_eip=new_eip;
+ SegSet16(cs,(Bit16u)(new_cs&0xffff));
+ /* IOPL can not be modified in v86 mode by IRET */
+ CPU_SetFlags(new_flags,FMASK_NORMAL|FLAG_NT);
+ } else {
+ Bit16u new_eip=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask));
+ Bit32u tempesp=(reg_esp&cpu.stack.notmask)|((reg_esp+2)&cpu.stack.mask);
+ Bit16u new_cs=mem_readw(SegPhys(ss) + (tempesp & cpu.stack.mask));
+ tempesp=(tempesp&cpu.stack.notmask)|((tempesp+2)&cpu.stack.mask);
+ Bit16u new_flags=mem_readw(SegPhys(ss) + (tempesp & cpu.stack.mask));
+ reg_esp=(tempesp&cpu.stack.notmask)|((tempesp+2)&cpu.stack.mask);
+
+ reg_eip=(Bit32u)new_eip;
+ SegSet16(cs,new_cs);
+ /* IOPL can not be modified in v86 mode by IRET */
+ CPU_SetFlags(new_flags,FMASK_NORMAL|FLAG_NT);
+ }
+ cpu.code.big=false;
+ DestroyConditionFlags();
+ return;
+ }
+ }
+ /* Check if this is task IRET */
+ if (GETFLAG(NT)) {
+ if (GETFLAG(VM)) E_Exit("Pmode IRET with VM bit set");
+ CPU_CHECK_COND(!cpu_tss.IsValid(),
+ "TASK Iret without valid TSS",
+ EXCEPTION_TS,cpu_tss.selector & 0xfffc)
+ if (!cpu_tss.desc.IsBusy()) {
+ LOG(LOG_CPU,LOG_ERROR)("TASK Iret:TSS not busy");
+ }
+ Bitu back_link=cpu_tss.Get_back();
+ CPU_SwitchTask(back_link,TSwitch_IRET,oldeip);
+ return;
+ }
+ Bitu n_cs_sel,n_eip,n_flags;
+ Bit32u tempesp;
+ if (use32) {
+ n_eip=mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask));
+ tempesp=(reg_esp&cpu.stack.notmask)|((reg_esp+4)&cpu.stack.mask);
+ n_cs_sel=mem_readd(SegPhys(ss) + (tempesp & cpu.stack.mask)) & 0xffff;
+ tempesp=(tempesp&cpu.stack.notmask)|((tempesp+4)&cpu.stack.mask);
+ n_flags=mem_readd(SegPhys(ss) + (tempesp & cpu.stack.mask));
+ tempesp=(tempesp&cpu.stack.notmask)|((tempesp+4)&cpu.stack.mask);
+
+ if ((n_flags & FLAG_VM) && (cpu.cpl==0)) {
+ // commit point
+ reg_esp=tempesp;
+ reg_eip=n_eip & 0xffff;
+ Bitu n_ss,n_esp,n_es,n_ds,n_fs,n_gs;
+ n_esp=CPU_Pop32();
+ n_ss=CPU_Pop32() & 0xffff;
+ n_es=CPU_Pop32() & 0xffff;
+ n_ds=CPU_Pop32() & 0xffff;
+ n_fs=CPU_Pop32() & 0xffff;
+ n_gs=CPU_Pop32() & 0xffff;
+
+ CPU_SetFlags(n_flags,FMASK_ALL | FLAG_VM);
+ DestroyConditionFlags();
+ cpu.cpl=3;
+
+ CPU_SetSegGeneral(ss,n_ss);
+ CPU_SetSegGeneral(es,n_es);
+ CPU_SetSegGeneral(ds,n_ds);
+ CPU_SetSegGeneral(fs,n_fs);
+ CPU_SetSegGeneral(gs,n_gs);
+ reg_esp=n_esp;
+ cpu.code.big=false;
+ SegSet16(cs,n_cs_sel);
+ LOG(LOG_CPU,LOG_NORMAL)("IRET:Back to V86: CS:%X IP %X SS:%X SP %X FLAGS:%X",SegValue(cs),reg_eip,SegValue(ss),reg_esp,reg_flags);
+ return;
+ }
+ if (n_flags & FLAG_VM) E_Exit("IRET from pmode to v86 with CPL!=0");
+ } else {
+ n_eip=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask));
+ tempesp=(reg_esp&cpu.stack.notmask)|((reg_esp+2)&cpu.stack.mask);
+ n_cs_sel=mem_readw(SegPhys(ss) + (tempesp & cpu.stack.mask));
+ tempesp=(tempesp&cpu.stack.notmask)|((tempesp+2)&cpu.stack.mask);
+ n_flags=mem_readw(SegPhys(ss) + (tempesp & cpu.stack.mask));
+ n_flags|=(reg_flags & 0xffff0000);
+ tempesp=(tempesp&cpu.stack.notmask)|((tempesp+2)&cpu.stack.mask);
+
+ if (n_flags & FLAG_VM) E_Exit("VM Flag in 16-bit iret");
+ }
+ CPU_CHECK_COND((n_cs_sel & 0xfffc)==0,
+ "IRET:CS selector zero",
+ EXCEPTION_GP,0)
+ Bitu n_cs_rpl=n_cs_sel & 3;
+ Descriptor n_cs_desc;
+ CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_cs_sel,n_cs_desc),
+ "IRET:CS selector beyond limits",
+ EXCEPTION_GP,n_cs_sel & 0xfffc)
+ CPU_CHECK_COND(n_cs_rpl<cpu.cpl,
+ "IRET to lower privilege",
+ EXCEPTION_GP,n_cs_sel & 0xfffc)
+
+ switch (n_cs_desc.Type()) {
+ case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA:
+ case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA:
+ CPU_CHECK_COND(n_cs_rpl!=n_cs_desc.DPL(),
+ "IRET:NC:DPL!=RPL",
+ EXCEPTION_GP,n_cs_sel & 0xfffc)
+ break;
+ case DESC_CODE_N_C_A: case DESC_CODE_N_C_NA:
+ case DESC_CODE_R_C_A: case DESC_CODE_R_C_NA:
+ CPU_CHECK_COND(n_cs_desc.DPL()>n_cs_rpl,
+ "IRET:C:DPL>RPL",
+ EXCEPTION_GP,n_cs_sel & 0xfffc)
+ break;
+ default:
+ E_Exit("IRET:Illegal descriptor type %X",n_cs_desc.Type());
+ }
+ CPU_CHECK_COND(!n_cs_desc.saved.seg.p,
+ "IRET with nonpresent code segment",
+ EXCEPTION_NP,n_cs_sel & 0xfffc)
+
+ if (n_cs_rpl==cpu.cpl) {
+ /* Return to same level */
+
+ // commit point
+ reg_esp=tempesp;
+ Segs.phys[cs]=n_cs_desc.GetBase();
+ cpu.code.big=n_cs_desc.Big()>0;
+ Segs.val[cs]=n_cs_sel;
+ reg_eip=n_eip;
+
+ Bitu mask=cpu.cpl ? (FMASK_NORMAL | FLAG_NT) : FMASK_ALL;
+ if (GETFLAG_IOPL<cpu.cpl) mask &= (~FLAG_IF);
+ CPU_SetFlags(n_flags,mask);
+ DestroyConditionFlags();
+ LOG(LOG_CPU,LOG_NORMAL)("IRET:Same level:%X:%X big %d",n_cs_sel,n_eip,cpu.code.big);
+ } else {
+ /* Return to outer level */
+ Bitu n_ss,n_esp;
+ if (use32) {
+ n_esp=mem_readd(SegPhys(ss) + (tempesp & cpu.stack.mask));
+ tempesp=(tempesp&cpu.stack.notmask)|((tempesp+4)&cpu.stack.mask);
+ n_ss=mem_readd(SegPhys(ss) + (tempesp & cpu.stack.mask)) & 0xffff;
+ } else {
+ n_esp=mem_readw(SegPhys(ss) + (tempesp & cpu.stack.mask));
+ tempesp=(tempesp&cpu.stack.notmask)|((tempesp+2)&cpu.stack.mask);
+ n_ss=mem_readw(SegPhys(ss) + (tempesp & cpu.stack.mask));
+ }
+ CPU_CHECK_COND((n_ss & 0xfffc)==0,
+ "IRET:Outer level:SS selector zero",
+ EXCEPTION_GP,0)
+ CPU_CHECK_COND((n_ss & 3)!=n_cs_rpl,
+ "IRET:Outer level:SS rpl!=CS rpl",
+ EXCEPTION_GP,n_ss & 0xfffc)
+ Descriptor n_ss_desc;
+ CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_ss,n_ss_desc),
+ "IRET:Outer level:SS beyond limit",
+ EXCEPTION_GP,n_ss & 0xfffc)
+ CPU_CHECK_COND(n_ss_desc.DPL()!=n_cs_rpl,
+ "IRET:Outer level:SS dpl!=CS rpl",
+ EXCEPTION_GP,n_ss & 0xfffc)
+
+ // check if stack segment is a writable data segment
+ switch (n_ss_desc.Type()) {
+ case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+ break;
+ default:
+ E_Exit("IRET:Outer level:Stack segment not writable"); // or #GP(ss_sel)
+ }
+ CPU_CHECK_COND(!n_ss_desc.saved.seg.p,
+ "IRET:Outer level:Stack segment not present",
+ EXCEPTION_NP,n_ss & 0xfffc)
+
+ // commit point
+
+ Segs.phys[cs]=n_cs_desc.GetBase();
+ cpu.code.big=n_cs_desc.Big()>0;
+ Segs.val[cs]=n_cs_sel;
+
+ Bitu mask=cpu.cpl ? (FMASK_NORMAL | FLAG_NT) : FMASK_ALL;
+ if (GETFLAG_IOPL<cpu.cpl) mask &= (~FLAG_IF);
+ CPU_SetFlags(n_flags,mask);
+ DestroyConditionFlags();
+
+ cpu.cpl=n_cs_rpl;
+ reg_eip=n_eip;
+
+ Segs.val[ss]=n_ss;
+ Segs.phys[ss]=n_ss_desc.GetBase();
+ if (n_ss_desc.Big()) {
+ cpu.stack.big=true;
+ cpu.stack.mask=0xffffffff;
+ cpu.stack.notmask=0;
+ reg_esp=n_esp;
+ } else {
+ cpu.stack.big=false;
+ cpu.stack.mask=0xffff;
+ cpu.stack.notmask=0xffff0000;
+ reg_sp=n_esp & 0xffff;
+ }
+
+ // borland extender, zrdx
+ CPU_CheckSegments();
+
+ LOG(LOG_CPU,LOG_NORMAL)("IRET:Outer level:%X:%X big %d",n_cs_sel,n_eip,cpu.code.big);
+ }
+ return;
+ }
+}
+
+
+void CPU_JMP(bool use32,Bitu selector,Bitu offset,Bitu oldeip) {
+ if (!cpu.pmode || (reg_flags & FLAG_VM)) {
+ if (!use32) {
+ reg_eip=offset&0xffff;
+ } else {
+ reg_eip=offset;
+ }
+ SegSet16(cs,selector);
+ cpu.code.big=false;
+ return;
+ } else {
+ CPU_CHECK_COND((selector & 0xfffc)==0,
+ "JMP:CS selector zero",
+ EXCEPTION_GP,0)
+ Bitu rpl=selector & 3;
+ Descriptor desc;
+ CPU_CHECK_COND(!cpu.gdt.GetDescriptor(selector,desc),
+ "JMP:CS beyond limits",
+ EXCEPTION_GP,selector & 0xfffc)
+ switch (desc.Type()) {
+ case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA:
+ case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA:
+ CPU_CHECK_COND(rpl>cpu.cpl,
+ "JMP:NC:RPL>CPL",
+ EXCEPTION_GP,selector & 0xfffc)
+ CPU_CHECK_COND(cpu.cpl!=desc.DPL(),
+ "JMP:NC:RPL != DPL",
+ EXCEPTION_GP,selector & 0xfffc)
+ LOG(LOG_CPU,LOG_NORMAL)("JMP:Code:NC to %X:%X big %d",selector,offset,desc.Big());
+ goto CODE_jmp;
+ case DESC_CODE_N_C_A: case DESC_CODE_N_C_NA:
+ case DESC_CODE_R_C_A: case DESC_CODE_R_C_NA:
+ LOG(LOG_CPU,LOG_NORMAL)("JMP:Code:C to %X:%X big %d",selector,offset,desc.Big());
+ CPU_CHECK_COND(cpu.cpl<desc.DPL(),
+ "JMP:C:CPL < DPL",
+ EXCEPTION_GP,selector & 0xfffc)
+CODE_jmp:
+ if (!desc.saved.seg.p) {
+ // win
+ CPU_Exception(EXCEPTION_NP,selector & 0xfffc);
+ return;
+ }
+
+ /* Normal jump to another selector:offset */
+ Segs.phys[cs]=desc.GetBase();
+ cpu.code.big=desc.Big()>0;
+ Segs.val[cs]=(selector & 0xfffc) | cpu.cpl;
+ reg_eip=offset;
+ return;
+ case DESC_386_TSS_A:
+ CPU_CHECK_COND(desc.DPL()<cpu.cpl,
+ "JMP:TSS:dpl<cpl",
+ EXCEPTION_GP,selector & 0xfffc)
+ CPU_CHECK_COND(desc.DPL()<rpl,
+ "JMP:TSS:dpl<rpl",
+ EXCEPTION_GP,selector & 0xfffc)
+ LOG(LOG_CPU,LOG_NORMAL)("JMP:TSS to %X",selector);
+ CPU_SwitchTask(selector,TSwitch_JMP,oldeip);
+ break;
+ default:
+ E_Exit("JMP Illegal descriptor type %X",desc.Type());
+ }
+ }
+ assert(1);
+}
+
+
+void CPU_CALL(bool use32,Bitu selector,Bitu offset,Bitu oldeip) {
+ if (!cpu.pmode || (reg_flags & FLAG_VM)) {
+ if (!use32) {
+ CPU_Push16(SegValue(cs));
+ CPU_Push16(oldeip);
+ reg_eip=offset&0xffff;
+ } else {
+ CPU_Push32(SegValue(cs));
+ CPU_Push32(oldeip);
+ reg_eip=offset;
+ }
+ cpu.code.big=false;
+ SegSet16(cs,selector);
+ return;
+ } else {
+ CPU_CHECK_COND((selector & 0xfffc)==0,
+ "CALL:CS selector zero",
+ EXCEPTION_GP,0)
+ Bitu rpl=selector & 3;
+ Descriptor call;
+ CPU_CHECK_COND(!cpu.gdt.GetDescriptor(selector,call),
+ "CALL:CS beyond limits",
+ EXCEPTION_GP,selector & 0xfffc)
+ /* Check for type of far call */
+ switch (call.Type()) {
+ case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA:
+ case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA:
+ CPU_CHECK_COND(rpl>cpu.cpl,
+ "CALL:CODE:NC:RPL>CPL",
+ EXCEPTION_GP,selector & 0xfffc)
+ CPU_CHECK_COND(call.DPL()!=cpu.cpl,
+ "CALL:CODE:NC:DPL!=CPL",
+ EXCEPTION_GP,selector & 0xfffc)
+ LOG(LOG_CPU,LOG_NORMAL)("CALL:CODE:NC to %X:%X",selector,offset);
+ goto call_code;
+ case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA:
+ case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA:
+ CPU_CHECK_COND(call.DPL()>cpu.cpl,
+ "CALL:CODE:C:DPL>CPL",
+ EXCEPTION_GP,selector & 0xfffc)
+ LOG(LOG_CPU,LOG_NORMAL)("CALL:CODE:C to %X:%X",selector,offset);
+call_code:
+ if (!call.saved.seg.p) {
+ // borland extender (RTM)
+ CPU_Exception(EXCEPTION_NP,selector & 0xfffc);
+ return;
+ }
+ // commit point
+ if (!use32) {
+ CPU_Push16(SegValue(cs));
+ CPU_Push16(oldeip);
+ reg_eip=offset & 0xffff;
+ } else {
+ CPU_Push32(SegValue(cs));
+ CPU_Push32(oldeip);
+ reg_eip=offset;
+ }
+ Segs.phys[cs]=call.GetBase();
+ cpu.code.big=call.Big()>0;
+ Segs.val[cs]=(selector & 0xfffc) | cpu.cpl;
+ return;
+ case DESC_386_CALL_GATE:
+ case DESC_286_CALL_GATE:
+ {
+ CPU_CHECK_COND(call.DPL()<cpu.cpl,
+ "CALL:Gate:Gate DPL<CPL",
+ EXCEPTION_GP,selector & 0xfffc)
+ CPU_CHECK_COND(call.DPL()<rpl,
+ "CALL:Gate:Gate DPL<RPL",
+ EXCEPTION_GP,selector & 0xfffc)
+ CPU_CHECK_COND(!call.saved.seg.p,
+ "CALL:Gate:Segment not present",
+ EXCEPTION_NP,selector & 0xfffc)
+ Descriptor n_cs_desc;
+ Bitu n_cs_sel=call.GetSelector();
+
+ CPU_CHECK_COND((n_cs_sel & 0xfffc)==0,
+ "CALL:Gate:CS selector zero",
+ EXCEPTION_GP,0)
+ CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_cs_sel,n_cs_desc),
+ "CALL:Gate:CS beyond limits",
+ EXCEPTION_GP,n_cs_sel & 0xfffc)
+ Bitu n_cs_dpl = n_cs_desc.DPL();
+ CPU_CHECK_COND(n_cs_dpl>cpu.cpl,
+ "CALL:Gate:CS DPL>CPL",
+ EXCEPTION_GP,n_cs_sel & 0xfffc)
+
+ CPU_CHECK_COND(!n_cs_desc.saved.seg.p,
+ "CALL:Gate:CS not present",
+ EXCEPTION_NP,n_cs_sel & 0xfffc)
+
+ Bitu n_eip = call.GetOffset();
+ switch (n_cs_desc.Type()) {
+ case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA:
+ case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA:
+ /* Check if we goto inner priviledge */
+ if (n_cs_dpl < cpu.cpl) {
+ /* Get new SS:ESP out of TSS */
+ Bitu n_ss_sel,n_esp;
+ Descriptor n_ss_desc;
+ cpu_tss.Get_SSx_ESPx(n_cs_dpl,n_ss_sel,n_esp);
+ CPU_CHECK_COND((n_ss_sel & 0xfffc)==0,
+ "CALL:Gate:NC:SS selector zero",
+ EXCEPTION_TS,0)
+ CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_ss_sel,n_ss_desc),
+ "CALL:Gate:Invalid SS selector",
+ EXCEPTION_TS,n_ss_sel & 0xfffc)
+ CPU_CHECK_COND(((n_ss_sel & 3)!=n_cs_desc.DPL()) || (n_ss_desc.DPL()!=n_cs_desc.DPL()),
+ "CALL:Gate:Invalid SS selector privileges",
+ EXCEPTION_TS,n_ss_sel & 0xfffc)
+
+ switch (n_ss_desc.Type()) {
+ case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+ // writable data segment
+ break;
+ default:
+ E_Exit("Call:Gate:SS no writable data segment"); // or #TS(ss_sel)
+ }
+ CPU_CHECK_COND(!n_ss_desc.saved.seg.p,
+ "CALL:Gate:Stack segment not present",
+ EXCEPTION_SS,n_ss_sel & 0xfffc)
+
+ /* Load the new SS:ESP and save data on it */
+ Bitu o_esp = reg_esp;
+ Bitu o_ss = SegValue(ss);
+ PhysPt o_stack = SegPhys(ss)+(reg_esp & cpu.stack.mask);
+
+
+ // catch pagefaults
+ if (call.saved.gate.paramcount&31) {
+ if (call.Type()==DESC_386_CALL_GATE) {
+ for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--)
+ mem_readd(o_stack+i*4);
+ } else {
+ for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--)
+ mem_readw(o_stack+i*2);
+ }
+ }
+
+ // commit point
+ Segs.val[ss]=n_ss_sel;
+ Segs.phys[ss]=n_ss_desc.GetBase();
+ if (n_ss_desc.Big()) {
+ cpu.stack.big=true;
+ cpu.stack.mask=0xffffffff;
+ cpu.stack.notmask=0;
+ reg_esp=n_esp;
+ } else {
+ cpu.stack.big=false;
+ cpu.stack.mask=0xffff;
+ cpu.stack.notmask=0xffff0000;
+ reg_sp=n_esp & 0xffff;
+ }
+
+ cpu.cpl = n_cs_desc.DPL();
+ Bit16u oldcs = SegValue(cs);
+ /* Switch to new CS:EIP */
+ Segs.phys[cs] = n_cs_desc.GetBase();
+ Segs.val[cs] = (n_cs_sel & 0xfffc) | cpu.cpl;
+ cpu.code.big = n_cs_desc.Big()>0;
+ reg_eip = n_eip;
+ if (!use32) reg_eip&=0xffff;
+
+ if (call.Type()==DESC_386_CALL_GATE) {
+ CPU_Push32(o_ss); //save old stack
+ CPU_Push32(o_esp);
+ if (call.saved.gate.paramcount&31)
+ for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--)
+ CPU_Push32(mem_readd(o_stack+i*4));
+ CPU_Push32(oldcs);
+ CPU_Push32(oldeip);
+ } else {
+ CPU_Push16(o_ss); //save old stack
+ CPU_Push16(o_esp);
+ if (call.saved.gate.paramcount&31)
+ for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--)
+ CPU_Push16(mem_readw(o_stack+i*2));
+ CPU_Push16(oldcs);
+ CPU_Push16(oldeip);
+ }
+
+ break;
+ } else if (n_cs_dpl > cpu.cpl)
+ E_Exit("CALL:GATE:CS DPL>CPL"); // or #GP(sel)
+ case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA:
+ case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA:
+ // zrdx extender
+
+ if (call.Type()==DESC_386_CALL_GATE) {
+ CPU_Push32(SegValue(cs));
+ CPU_Push32(oldeip);
+ } else {
+ CPU_Push16(SegValue(cs));
+ CPU_Push16(oldeip);
+ }
+
+ /* Switch to new CS:EIP */
+ Segs.phys[cs] = n_cs_desc.GetBase();
+ Segs.val[cs] = (n_cs_sel & 0xfffc) | cpu.cpl;
+ cpu.code.big = n_cs_desc.Big()>0;
+ reg_eip = n_eip;
+ if (!use32) reg_eip&=0xffff;
+ break;
+ default:
+ E_Exit("CALL:GATE:CS no executable segment");
+ }
+ } /* Call Gates */
+ break;
+ case DESC_386_TSS_A:
+ CPU_CHECK_COND(call.DPL()<cpu.cpl,
+ "CALL:TSS:dpl<cpl",
+ EXCEPTION_GP,selector & 0xfffc)
+ CPU_CHECK_COND(call.DPL()<rpl,
+ "CALL:TSS:dpl<rpl",
+ EXCEPTION_GP,selector & 0xfffc)
+
+ CPU_CHECK_COND(!call.saved.seg.p,
+ "CALL:TSS:Segment not present",
+ EXCEPTION_NP,selector & 0xfffc)
+
+ LOG(LOG_CPU,LOG_NORMAL)("CALL:TSS to %X",selector);
+ CPU_SwitchTask(selector,TSwitch_CALL_INT,oldeip);
+ break;
+ case DESC_DATA_EU_RW_NA: // vbdos
+ case DESC_INVALID: // used by some installers
+ CPU_Exception(EXCEPTION_GP,selector & 0xfffc);
+ return;
+ default:
+ E_Exit("CALL:Descriptor type %x unsupported",call.Type());
+ }
+ }
+ assert(1);
+}
+
+
+void CPU_RET(bool use32,Bitu bytes,Bitu oldeip) {
+ if (!cpu.pmode || (reg_flags & FLAG_VM)) {
+ Bitu new_ip,new_cs;
+ if (!use32) {
+ new_ip=CPU_Pop16();
+ new_cs=CPU_Pop16();
+ } else {
+ new_ip=CPU_Pop32();
+ new_cs=CPU_Pop32() & 0xffff;
+ }
+ reg_esp+=bytes;
+ SegSet16(cs,new_cs);
+ reg_eip=new_ip;
+ cpu.code.big=false;
+ return;
+ } else {
+ Bitu offset,selector;
+ if (!use32) selector = mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask) + 2);
+ else selector = mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask) + 4) & 0xffff;
+
+ Descriptor desc;
+ Bitu rpl=selector & 3;
+ if(rpl < cpu.cpl) {
+ // win setup
+ CPU_Exception(EXCEPTION_GP,selector & 0xfffc);
+ return;
+ }
+
+ CPU_CHECK_COND((selector & 0xfffc)==0,
+ "RET:CS selector zero",
+ EXCEPTION_GP,0)
+ CPU_CHECK_COND(!cpu.gdt.GetDescriptor(selector,desc),
+ "RET:CS beyond limits",
+ EXCEPTION_GP,selector & 0xfffc)
+
+ if (cpu.cpl==rpl) {
+ /* Return to same level */
+ switch (desc.Type()) {
+ case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA:
+ case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA:
+ CPU_CHECK_COND(cpu.cpl!=desc.DPL(),
+ "RET to NC segment of other privilege",
+ EXCEPTION_GP,selector & 0xfffc)
+ goto RET_same_level;
+ case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA:
+ case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA:
+ CPU_CHECK_COND(desc.DPL()>cpu.cpl,
+ "RET to C segment of higher privilege",
+ EXCEPTION_GP,selector & 0xfffc)
+ break;
+ default:
+ E_Exit("RET from illegal descriptor type %X",desc.Type());
+ }
+RET_same_level:
+ if (!desc.saved.seg.p) {
+ // borland extender (RTM)
+ CPU_Exception(EXCEPTION_NP,selector & 0xfffc);
+ return;
+ }
+
+ // commit point
+ if (!use32) {
+ offset=CPU_Pop16();
+ selector=CPU_Pop16();
+ } else {
+ offset=CPU_Pop32();
+ selector=CPU_Pop32() & 0xffff;
+ }
+
+ Segs.phys[cs]=desc.GetBase();
+ cpu.code.big=desc.Big()>0;
+ Segs.val[cs]=selector;
+ reg_eip=offset;
+ if (cpu.stack.big) {
+ reg_esp+=bytes;
+ } else {
+ reg_sp+=bytes;
+ }
+ LOG(LOG_CPU,LOG_NORMAL)("RET - Same level to %X:%X RPL %X DPL %X",selector,offset,rpl,desc.DPL());
+ return;
+ } else {
+ /* Return to outer level */
+ switch (desc.Type()) {
+ case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA:
+ case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA:
+ CPU_CHECK_COND(desc.DPL()!=rpl,
+ "RET to outer NC segment with DPL!=RPL",
+ EXCEPTION_GP,selector & 0xfffc)
+ break;
+ case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA:
+ case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA:
+ CPU_CHECK_COND(desc.DPL()>rpl,
+ "RET to outer C segment with DPL>RPL",
+ EXCEPTION_GP,selector & 0xfffc)
+ break;
+ default:
+ E_Exit("RET from illegal descriptor type %X",desc.Type()); // or #GP(selector)
+ }
+
+ CPU_CHECK_COND(!desc.saved.seg.p,
+ "RET:Outer level:CS not present",
+ EXCEPTION_NP,selector & 0xfffc)
+
+ // commit point
+ Bitu n_esp,n_ss;
+ if (use32) {
+ offset=CPU_Pop32();
+ selector=CPU_Pop32() & 0xffff;
+ reg_esp+=bytes;
+ n_esp = CPU_Pop32();
+ n_ss = CPU_Pop32() & 0xffff;
+ } else {
+ offset=CPU_Pop16();
+ selector=CPU_Pop16();
+ reg_esp+=bytes;
+ n_esp = CPU_Pop16();
+ n_ss = CPU_Pop16();
+ }
+
+ CPU_CHECK_COND((n_ss & 0xfffc)==0,
+ "RET to outer level with SS selector zero",
+ EXCEPTION_GP,0)
+
+ Descriptor n_ss_desc;
+ CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_ss,n_ss_desc),
+ "RET:SS beyond limits",
+ EXCEPTION_GP,n_ss & 0xfffc)
+
+ CPU_CHECK_COND(((n_ss & 3)!=rpl) || (n_ss_desc.DPL()!=rpl),
+ "RET to outer segment with invalid SS privileges",
+ EXCEPTION_GP,n_ss & 0xfffc)
+ switch (n_ss_desc.Type()) {
+ case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+ break;
+ default:
+ E_Exit("RET:SS selector type no writable data segment"); // or #GP(selector)
+ }
+ CPU_CHECK_COND(!n_ss_desc.saved.seg.p,
+ "RET:Stack segment not present",
+ EXCEPTION_SS,n_ss & 0xfffc)
+
+ cpu.cpl = rpl;
+ Segs.phys[cs]=desc.GetBase();
+ cpu.code.big=desc.Big()>0;
+ Segs.val[cs]=(selector&0xfffc) | cpu.cpl;
+ reg_eip=offset;
+
+ Segs.val[ss]=n_ss;
+ Segs.phys[ss]=n_ss_desc.GetBase();
+ if (n_ss_desc.Big()) {
+ cpu.stack.big=true;
+ cpu.stack.mask=0xffffffff;
+ cpu.stack.notmask=0;
+ reg_esp=n_esp+bytes;
+ } else {
+ cpu.stack.big=false;
+ cpu.stack.mask=0xffff;
+ cpu.stack.notmask=0xffff0000;
+ reg_sp=(n_esp & 0xffff)+bytes;
+ }
+
+ CPU_CheckSegments();
+
+// LOG(LOG_MISC,LOG_ERROR)("RET - Higher level to %X:%X RPL %X DPL %X",selector,offset,rpl,desc.DPL());
+ return;
+ }
+ LOG(LOG_CPU,LOG_NORMAL)("Prot ret %X:%X",selector,offset);
+ return;
+ }
+ assert(1);
+}
+
+
+Bitu CPU_SLDT(void) {
+ return cpu.gdt.SLDT();
+}
+
+bool CPU_LLDT(Bitu selector) {
+ if (!cpu.gdt.LLDT(selector)) {
+ LOG(LOG_CPU,LOG_ERROR)("LLDT failed, selector=%X",selector);
+ return true;
+ }
+ LOG(LOG_CPU,LOG_NORMAL)("LDT Set to %X",selector);
+ return false;
+}
+
+Bitu CPU_STR(void) {
+ return cpu_tss.selector;
+}
+
+bool CPU_LTR(Bitu selector) {
+ if ((selector & 0xfffc)==0) {
+ cpu_tss.SetSelector(selector);
+ return false;
+ }
+ TSS_Descriptor desc;
+ if ((selector & 4) || (!cpu.gdt.GetDescriptor(selector,desc))) {
+ LOG(LOG_CPU,LOG_ERROR)("LTR failed, selector=%X",selector);
+ return CPU_PrepareException(EXCEPTION_GP,selector);
+ }
+
+ if ((desc.Type()==DESC_286_TSS_A) || (desc.Type()==DESC_386_TSS_A)) {
+ if (!desc.saved.seg.p) {
+ LOG(LOG_CPU,LOG_ERROR)("LTR failed, selector=%X (not present)",selector);
+ return CPU_PrepareException(EXCEPTION_NP,selector);
+ }
+ if (!cpu_tss.SetSelector(selector)) E_Exit("LTR failed, selector=%X",selector);
+ cpu_tss.desc.SetBusy(true);
+ cpu_tss.SaveSelector();
+ } else {
+ /* Descriptor was no available TSS descriptor */
+ LOG(LOG_CPU,LOG_NORMAL)("LTR failed, selector=%X (type=%X)",selector,desc.Type());
+ return CPU_PrepareException(EXCEPTION_GP,selector);
+ }
+ return false;
+}
+
+void CPU_LGDT(Bitu limit,Bitu base) {
+ LOG(LOG_CPU,LOG_NORMAL)("GDT Set to base:%X limit:%X",base,limit);
+ cpu.gdt.SetLimit(limit);
+ cpu.gdt.SetBase(base);
+}
+
+void CPU_LIDT(Bitu limit,Bitu base) {
+ LOG(LOG_CPU,LOG_NORMAL)("IDT Set to base:%X limit:%X",base,limit);
+ cpu.idt.SetLimit(limit);
+ cpu.idt.SetBase(base);
+}
+
+Bitu CPU_SGDT_base(void) {
+ return cpu.gdt.GetBase();
+}
+Bitu CPU_SGDT_limit(void) {
+ return cpu.gdt.GetLimit();
+}
+
+Bitu CPU_SIDT_base(void) {
+ return cpu.idt.GetBase();
+}
+Bitu CPU_SIDT_limit(void) {
+ return cpu.idt.GetLimit();
+}
+
+static bool printed_cycles_auto_info = false;
+void CPU_SET_CRX(Bitu cr,Bitu value) {
+ switch (cr) {
+ case 0:
+ {
+ value|=CR0_FPUPRESENT;
+ Bitu changed=cpu.cr0 ^ value;
+ if (!changed) return;
+ cpu.cr0=value;
+ if (value & CR0_PROTECTION) {
+ cpu.pmode=true;
+ LOG(LOG_CPU,LOG_NORMAL)("Protected mode");
+ PAGING_Enable((value & CR0_PAGING)>0);
+
+ if (!(CPU_AutoDetermineMode&CPU_AUTODETERMINE_MASK)) break;
+
+ if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CYCLES) {
+ CPU_CycleAutoAdjust=true;
+ CPU_CycleLeft=0;
+ CPU_Cycles=0;
+ CPU_OldCycleMax=CPU_CycleMax;
+ GFX_SetTitle(CPU_CyclePercUsed,-1,false);
+ if(!printed_cycles_auto_info) {
+ printed_cycles_auto_info = true;
+ LOG_MSG("DOSBox has switched to max cycles, because of the setting: cycles=auto.\nIf the game runs too fast, try a fixed cycles amount in DOSBox's options.");
+ }
+ } else {
+ GFX_SetTitle(-1,-1,false);
+ }
+#if (C_DYNAMIC_X86)
+ if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CORE) {
+ CPU_Core_Dyn_X86_Cache_Init(true);
+ cpudecoder=&CPU_Core_Dyn_X86_Run;
+ }
+#elif (C_DYNREC)
+ if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CORE) {
+ CPU_Core_Dynrec_Cache_Init(true);
+ cpudecoder=&CPU_Core_Dynrec_Run;
+ }
+#endif
+ CPU_AutoDetermineMode<<=CPU_AUTODETERMINE_SHIFT;
+ } else {
+ cpu.pmode=false;
+ if (value & CR0_PAGING) LOG_MSG("Paging requested without PE=1");
+ PAGING_Enable(false);
+ LOG(LOG_CPU,LOG_NORMAL)("Real mode");
+ }
+ break;
+ }
+ case 2:
+ paging.cr2=value;
+ break;
+ case 3:
+ PAGING_SetDirBase(value);
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV CR%d,%X",cr,value);
+ break;
+ }
+}
+
+bool CPU_WRITE_CRX(Bitu cr,Bitu value) {
+ /* Check if privileged to access control registers */
+ if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
+ if ((cr==1) || (cr>4)) return CPU_PrepareException(EXCEPTION_UD,0);
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLDSLOW) {
+ if (cr==4) return CPU_PrepareException(EXCEPTION_UD,0);
+ }
+ CPU_SET_CRX(cr,value);
+ return false;
+}
+
+Bitu CPU_GET_CRX(Bitu cr) {
+ switch (cr) {
+ case 0:
+ if (CPU_ArchitectureType>=CPU_ARCHTYPE_PENTIUMSLOW) return cpu.cr0;
+ else if (CPU_ArchitectureType>=CPU_ARCHTYPE_486OLDSLOW) return (cpu.cr0 & 0xe005003f);
+ else return (cpu.cr0 | 0x7ffffff0);
+ case 2:
+ return paging.cr2;
+ case 3:
+ return PAGING_GetDirBase() & 0xfffff000;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV XXX, CR%d",cr);
+ break;
+ }
+ return 0;
+}
+
+bool CPU_READ_CRX(Bitu cr,Bit32u & retvalue) {
+ /* Check if privileged to access control registers */
+ if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
+ if ((cr==1) || (cr>4)) return CPU_PrepareException(EXCEPTION_UD,0);
+ retvalue=CPU_GET_CRX(cr);
+ return false;
+}
+
+
+bool CPU_WRITE_DRX(Bitu dr,Bitu value) {
+ /* Check if privileged to access control registers */
+ if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
+ switch (dr) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ cpu.drx[dr]=value;
+ break;
+ case 4:
+ case 6:
+ cpu.drx[6]=(value|0xffff0ff0) & 0xffffefff;
+ break;
+ case 5:
+ case 7:
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_PENTIUMSLOW) {
+ cpu.drx[7]=(value|0x400) & 0xffff2fff;
+ } else {
+ cpu.drx[7]=(value|0x400);
+ }
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV DR%d,%X",dr,value);
+ break;
+ }
+ return false;
+}
+
+bool CPU_READ_DRX(Bitu dr,Bit32u & retvalue) {
+ /* Check if privileged to access control registers */
+ if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
+ switch (dr) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 6:
+ case 7:
+ retvalue=cpu.drx[dr];
+ break;
+ case 4:
+ retvalue=cpu.drx[6];
+ break;
+ case 5:
+ retvalue=cpu.drx[7];
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV XXX, DR%d",dr);
+ retvalue=0;
+ break;
+ }
+ return false;
+}
+
+bool CPU_WRITE_TRX(Bitu tr,Bitu value) {
+ /* Check if privileged to access control registers */
+ if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
+ switch (tr) {
+// case 3:
+ case 6:
+ case 7:
+ cpu.trx[tr]=value;
+ return false;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV TR%d,%X",tr,value);
+ break;
+ }
+ return CPU_PrepareException(EXCEPTION_UD,0);
+}
+
+bool CPU_READ_TRX(Bitu tr,Bit32u & retvalue) {
+ /* Check if privileged to access control registers */
+ if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
+ switch (tr) {
+// case 3:
+ case 6:
+ case 7:
+ retvalue=cpu.trx[tr];
+ return false;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV XXX, TR%d",tr);
+ break;
+ }
+ return CPU_PrepareException(EXCEPTION_UD,0);
+}
+
+
+Bitu CPU_SMSW(void) {
+ return cpu.cr0;
+}
+
+bool CPU_LMSW(Bitu word) {
+ if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
+ word&=0xf;
+ if (cpu.cr0 & 1) word|=1;
+ word|=(cpu.cr0&0xfffffff0);
+ CPU_SET_CRX(0,word);
+ return false;
+}
+
+void CPU_ARPL(Bitu & dest_sel,Bitu src_sel) {
+ FillFlags();
+ if ((dest_sel & 3) < (src_sel & 3)) {
+ dest_sel=(dest_sel & 0xfffc) + (src_sel & 3);
+// dest_sel|=0xff3f0000;
+ SETFLAGBIT(ZF,true);
+ } else {
+ SETFLAGBIT(ZF,false);
+ }
+}
+
+void CPU_LAR(Bitu selector,Bitu & ar) {
+ FillFlags();
+ if (selector == 0) {
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ Descriptor desc;Bitu rpl=selector & 3;
+ if (!cpu.gdt.GetDescriptor(selector,desc)){
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ switch (desc.Type()){
+ case DESC_CODE_N_C_A: case DESC_CODE_N_C_NA:
+ case DESC_CODE_R_C_A: case DESC_CODE_R_C_NA:
+ break;
+
+ case DESC_286_INT_GATE: case DESC_286_TRAP_GATE: {
+ case DESC_386_INT_GATE: case DESC_386_TRAP_GATE:
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+
+ case DESC_LDT:
+ case DESC_TASK_GATE:
+
+ case DESC_286_TSS_A: case DESC_286_TSS_B:
+ case DESC_286_CALL_GATE:
+
+ case DESC_386_TSS_A: case DESC_386_TSS_B:
+ case DESC_386_CALL_GATE:
+
+
+ case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A:
+ case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A:
+ case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+ case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA:
+ case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA:
+ if (desc.DPL()<cpu.cpl || desc.DPL() < rpl) {
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ break;
+ default:
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ /* Valid descriptor */
+ ar=desc.saved.fill[1] & 0x00ffff00;
+ SETFLAGBIT(ZF,true);
+}
+
+void CPU_LSL(Bitu selector,Bitu & limit) {
+ FillFlags();
+ if (selector == 0) {
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ Descriptor desc;Bitu rpl=selector & 3;
+ if (!cpu.gdt.GetDescriptor(selector,desc)){
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ switch (desc.Type()){
+ case DESC_CODE_N_C_A: case DESC_CODE_N_C_NA:
+ case DESC_CODE_R_C_A: case DESC_CODE_R_C_NA:
+ break;
+
+ case DESC_LDT:
+ case DESC_286_TSS_A:
+ case DESC_286_TSS_B:
+
+ case DESC_386_TSS_A:
+ case DESC_386_TSS_B:
+
+ case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A:
+ case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A:
+ case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+
+ case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA:
+ case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA:
+ if (desc.DPL()<cpu.cpl || desc.DPL() < rpl) {
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ break;
+ default:
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ limit=desc.GetLimit();
+ SETFLAGBIT(ZF,true);
+}
+
+void CPU_VERR(Bitu selector) {
+ FillFlags();
+ if (selector == 0) {
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ Descriptor desc;Bitu rpl=selector & 3;
+ if (!cpu.gdt.GetDescriptor(selector,desc)){
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ switch (desc.Type()){
+ case DESC_CODE_R_C_A: case DESC_CODE_R_C_NA:
+ //Conforming readable code segments can be always read
+ break;
+ case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A:
+ case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A:
+ case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+
+ case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA:
+ if (desc.DPL()<cpu.cpl || desc.DPL() < rpl) {
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ break;
+ default:
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ SETFLAGBIT(ZF,true);
+}
+
+void CPU_VERW(Bitu selector) {
+ FillFlags();
+ if (selector == 0) {
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ Descriptor desc;Bitu rpl=selector & 3;
+ if (!cpu.gdt.GetDescriptor(selector,desc)){
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ switch (desc.Type()){
+ case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+ if (desc.DPL()<cpu.cpl || desc.DPL() < rpl) {
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ break;
+ default:
+ SETFLAGBIT(ZF,false);
+ return;
+ }
+ SETFLAGBIT(ZF,true);
+}
+
+bool CPU_SetSegGeneral(SegNames seg,Bitu value) {
+ value &= 0xffff;
+ if (!cpu.pmode || (reg_flags & FLAG_VM)) {
+ Segs.val[seg]=value;
+ Segs.phys[seg]=value << 4;
+ if (seg==ss) {
+ cpu.stack.big=false;
+ cpu.stack.mask=0xffff;
+ cpu.stack.notmask=0xffff0000;
+ }
+ return false;
+ } else {
+ if (seg==ss) {
+ // Stack needs to be non-zero
+ if ((value & 0xfffc)==0) {
+// E_Exit("CPU_SetSegGeneral: Stack segment zero");
+ return CPU_PrepareException(EXCEPTION_GP,0);
+ }
+ Descriptor desc;
+ if (!cpu.gdt.GetDescriptor(value,desc)) {
+// E_Exit("CPU_SetSegGeneral: Stack segment beyond limits");
+ return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc);
+ }
+ if (((value & 3)!=cpu.cpl) || (desc.DPL()!=cpu.cpl)) {
+// E_Exit("CPU_SetSegGeneral: Stack segment with invalid privileges");
+ return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc);
+ }
+
+ switch (desc.Type()) {
+ case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+ break;
+ default:
+ //Earth Siege 1
+ return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc);
+ }
+
+ if (!desc.saved.seg.p) {
+// E_Exit("CPU_SetSegGeneral: Stack segment not present"); // or #SS(sel)
+ return CPU_PrepareException(EXCEPTION_SS,value & 0xfffc);
+ }
+
+ Segs.val[seg]=value;
+ Segs.phys[seg]=desc.GetBase();
+ if (desc.Big()) {
+ cpu.stack.big=true;
+ cpu.stack.mask=0xffffffff;
+ cpu.stack.notmask=0;
+ } else {
+ cpu.stack.big=false;
+ cpu.stack.mask=0xffff;
+ cpu.stack.notmask=0xffff0000;
+ }
+ } else {
+ if ((value & 0xfffc)==0) {
+ Segs.val[seg]=value;
+ Segs.phys[seg]=0; // ??
+ return false;
+ }
+ Descriptor desc;
+ if (!cpu.gdt.GetDescriptor(value,desc)) {
+ return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc);
+ }
+ switch (desc.Type()) {
+ case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A:
+ case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A:
+ case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A:
+ case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A:
+ case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA:
+ if (((value & 3)>desc.DPL()) || (cpu.cpl>desc.DPL())) {
+ // extreme pinball
+ return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc);
+ }
+ break;
+ case DESC_CODE_R_C_A: case DESC_CODE_R_C_NA:
+ break;
+ default:
+ // gabriel knight
+ return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc);
+
+ }
+ if (!desc.saved.seg.p) {
+ // win
+ return CPU_PrepareException(EXCEPTION_NP,value & 0xfffc);
+ }
+
+ Segs.val[seg]=value;
+ Segs.phys[seg]=desc.GetBase();
+ }
+
+ return false;
+ }
+}
+
+bool CPU_PopSeg(SegNames seg,bool use32) {
+ Bitu val=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask));
+ Bitu addsp = use32 ? 0x04 : 0x02;
+ //Calcullate this beforehande since the stack mask might change
+ Bit32u new_esp = (reg_esp&cpu.stack.notmask) | ((reg_esp + addsp)&cpu.stack.mask);
+ if (CPU_SetSegGeneral(seg,val)) return true;
+ reg_esp = new_esp;
+ return false;
+}
+
+bool CPU_CPUID(void) {
+ if (CPU_ArchitectureType<CPU_ARCHTYPE_486NEWSLOW) return false;
+ switch (reg_eax) {
+ case 0: /* Vendor ID String and maximum level? */
+ reg_eax=1; /* Maximum level */
+ reg_ebx='G' | ('e' << 8) | ('n' << 16) | ('u'<< 24);
+ reg_edx='i' | ('n' << 8) | ('e' << 16) | ('I'<< 24);
+ reg_ecx='n' | ('t' << 8) | ('e' << 16) | ('l'<< 24);
+ break;
+ case 1: /* get processor type/family/model/stepping and feature flags */
+ if ((CPU_ArchitectureType==CPU_ARCHTYPE_486NEWSLOW) ||
+ (CPU_ArchitectureType==CPU_ARCHTYPE_MIXED)) {
+ reg_eax=0x402; /* intel 486dx */
+ reg_ebx=0; /* Not Supported */
+ reg_ecx=0; /* No features */
+ reg_edx=0x00000001; /* FPU */
+ } else if (CPU_ArchitectureType==CPU_ARCHTYPE_PENTIUMSLOW) {
+ reg_eax=0x513; /* intel pentium */
+ reg_ebx=0; /* Not Supported */
+ reg_ecx=0; /* No features */
+ reg_edx=0x00000011; /* FPU+TimeStamp/RDTSC */
+ } else {
+ return false;
+ }
+ break;
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Unhandled CPUID Function %x",reg_eax);
+ reg_eax=0;
+ reg_ebx=0;
+ reg_ecx=0;
+ reg_edx=0;
+ break;
+ }
+ return true;
+}
+
+static Bits HLT_Decode(void) {
+ /* Once an interrupt occurs, it should change cpu core */
+ if (reg_eip!=cpu.hlt.eip || SegValue(cs) != cpu.hlt.cs) {
+ cpudecoder=cpu.hlt.old_decoder;
+ } else {
+ CPU_IODelayRemoved += CPU_Cycles;
+ CPU_Cycles=0;
+ }
+ return 0;
+}
+
+void CPU_HLT(Bitu oldeip) {
+ reg_eip=oldeip;
+ CPU_IODelayRemoved += CPU_Cycles;
+ CPU_Cycles=0;
+ cpu.hlt.cs=SegValue(cs);
+ cpu.hlt.eip=reg_eip;
+ cpu.hlt.old_decoder=cpudecoder;
+ cpudecoder=&HLT_Decode;
+}
+
+void CPU_ENTER(bool use32,Bitu bytes,Bitu level) {
+ level&=0x1f;
+ Bitu sp_index=reg_esp&cpu.stack.mask;
+ Bitu bp_index=reg_ebp&cpu.stack.mask;
+ if (!use32) {
+ sp_index-=2;
+ mem_writew(SegPhys(ss)+sp_index,reg_bp);
+ reg_bp=(Bit16u)(reg_esp-2);
+ if (level) {
+ for (Bitu i=1;i<level;i++) {
+ sp_index-=2;bp_index-=2;
+ mem_writew(SegPhys(ss)+sp_index,mem_readw(SegPhys(ss)+bp_index));
+ }
+ sp_index-=2;
+ mem_writew(SegPhys(ss)+sp_index,reg_bp);
+ }
+ } else {
+ sp_index-=4;
+ mem_writed(SegPhys(ss)+sp_index,reg_ebp);
+ reg_ebp=(reg_esp-4);
+ if (level) {
+ for (Bitu i=1;i<level;i++) {
+ sp_index-=4;bp_index-=4;
+ mem_writed(SegPhys(ss)+sp_index,mem_readd(SegPhys(ss)+bp_index));
+ }
+ sp_index-=4;
+ mem_writed(SegPhys(ss)+sp_index,reg_ebp);
+ }
+ }
+ sp_index-=bytes;
+ reg_esp=(reg_esp&cpu.stack.notmask)|((sp_index)&cpu.stack.mask);
+}
+
+static void CPU_CycleIncrease(bool pressed) {
+ if (!pressed) return;
+ if (CPU_CycleAutoAdjust) {
+ CPU_CyclePercUsed+=5;
+ if (CPU_CyclePercUsed>105) CPU_CyclePercUsed=105;
+ LOG_MSG("CPU speed: max %d percent.",CPU_CyclePercUsed);
+ GFX_SetTitle(CPU_CyclePercUsed,-1,false);
+ } else {
+ Bit32s old_cycles=CPU_CycleMax;
+ if (CPU_CycleUp < 100) {
+ CPU_CycleMax = (Bit32s)(CPU_CycleMax * (1 + (float)CPU_CycleUp / 100.0));
+ } else {
+ CPU_CycleMax = (Bit32s)(CPU_CycleMax + CPU_CycleUp);
+ }
+
+ CPU_CycleLeft=0;CPU_Cycles=0;
+ if (CPU_CycleMax==old_cycles) CPU_CycleMax++;
+ if(CPU_CycleMax > 15000 )
+ LOG_MSG("CPU speed: fixed %d cycles. If you need more than 20000, try core=dynamic in DOSBox's options.",CPU_CycleMax);
+ else
+ LOG_MSG("CPU speed: fixed %d cycles.",CPU_CycleMax);
+ GFX_SetTitle(CPU_CycleMax,-1,false);
+ }
+}
+
+static void CPU_CycleDecrease(bool pressed) {
+ if (!pressed) return;
+ if (CPU_CycleAutoAdjust) {
+ CPU_CyclePercUsed-=5;
+ if (CPU_CyclePercUsed<=0) CPU_CyclePercUsed=1;
+ if(CPU_CyclePercUsed <=70)
+ LOG_MSG("CPU speed: max %d percent. If the game runs too fast, try a fixed cycles amount in DOSBox's options.",CPU_CyclePercUsed);
+ else
+ LOG_MSG("CPU speed: max %d percent.",CPU_CyclePercUsed);
+ GFX_SetTitle(CPU_CyclePercUsed,-1,false);
+ } else {
+ if (CPU_CycleDown < 100) {
+ CPU_CycleMax = (Bit32s)(CPU_CycleMax / (1 + (float)CPU_CycleDown / 100.0));
+ } else {
+ CPU_CycleMax = (Bit32s)(CPU_CycleMax - CPU_CycleDown);
+ }
+ CPU_CycleLeft=0;CPU_Cycles=0;
+ if (CPU_CycleMax <= 0) CPU_CycleMax=1;
+ LOG_MSG("CPU speed: fixed %d cycles.",CPU_CycleMax);
+ GFX_SetTitle(CPU_CycleMax,-1,false);
+ }
+}
+
+void CPU_Enable_SkipAutoAdjust(void) {
+ if (CPU_CycleAutoAdjust) {
+ CPU_CycleMax /= 2;
+ if (CPU_CycleMax < CPU_CYCLES_LOWER_LIMIT)
+ CPU_CycleMax = CPU_CYCLES_LOWER_LIMIT;
+ }
+ CPU_SkipCycleAutoAdjust=true;
+}
+
+void CPU_Disable_SkipAutoAdjust(void) {
+ CPU_SkipCycleAutoAdjust=false;
+}
+
+
+extern Bit32s ticksDone;
+extern Bit32u ticksScheduled;
+
+void CPU_Reset_AutoAdjust(void) {
+ CPU_IODelayRemoved = 0;
+ ticksDone = 0;
+ ticksScheduled = 0;
+}
+
+class CPU: public Module_base {
+private:
+ static bool inited;
+public:
+ CPU(Section* configuration):Module_base(configuration) {
+ if(inited) {
+ Change_Config(configuration);
+ return;
+ }
+// Section_prop * section=static_cast<Section_prop *>(configuration);
+ inited=true;
+ reg_eax=0;
+ reg_ebx=0;
+ reg_ecx=0;
+ reg_edx=0;
+ reg_edi=0;
+ reg_esi=0;
+ reg_ebp=0;
+ reg_esp=0;
+
+ SegSet16(cs,0);
+ SegSet16(ds,0);
+ SegSet16(es,0);
+ SegSet16(fs,0);
+ SegSet16(gs,0);
+ SegSet16(ss,0);
+
+ CPU_SetFlags(FLAG_IF,FMASK_ALL); //Enable interrupts
+ cpu.cr0=0xffffffff;
+ CPU_SET_CRX(0,0); //Initialize
+ cpu.code.big=false;
+ cpu.stack.mask=0xffff;
+ cpu.stack.notmask=0xffff0000;
+ cpu.stack.big=false;
+ cpu.trap_skip=false;
+ cpu.idt.SetBase(0);
+ cpu.idt.SetLimit(1023);
+
+ for (Bitu i=0; i<7; i++) {
+ cpu.drx[i]=0;
+ cpu.trx[i]=0;
+ }
+ if (CPU_ArchitectureType==CPU_ARCHTYPE_PENTIUMSLOW) {
+ cpu.drx[6]=0xffff0ff0;
+ } else {
+ cpu.drx[6]=0xffff1ff0;
+ }
+ cpu.drx[7]=0x00000400;
+
+ /* Init the cpu cores */
+ CPU_Core_Normal_Init();
+ CPU_Core_Simple_Init();
+ CPU_Core_Full_Init();
+#if (C_DYNAMIC_X86)
+ CPU_Core_Dyn_X86_Init();
+#elif (C_DYNREC)
+ CPU_Core_Dynrec_Init();
+#endif
+ MAPPER_AddHandler(CPU_CycleDecrease,MK_f11,MMOD1,"cycledown","Dec Cycles");
+ MAPPER_AddHandler(CPU_CycleIncrease,MK_f12,MMOD1,"cycleup" ,"Inc Cycles");
+ Change_Config(configuration);
+ CPU_JMP(false,0,0,0); //Setup the first cpu core
+ }
+ bool Change_Config(Section* newconfig){
+ Section_prop * section=static_cast<Section_prop *>(newconfig);
+ CPU_AutoDetermineMode=CPU_AUTODETERMINE_NONE;
+ //CPU_CycleLeft=0;//needed ?
+ CPU_Cycles=0;
+ CPU_SkipCycleAutoAdjust=false;
+
+ Prop_multival* p = section->Get_multival("cycles");
+ std::string type = p->GetSection()->Get_string("type");
+ std::string str ;
+ CommandLine cmd(0,p->GetSection()->Get_string("parameters"));
+ if (type=="max") {
+ CPU_CycleMax=0;
+ CPU_CyclePercUsed=100;
+ CPU_CycleAutoAdjust=true;
+ CPU_CycleLimit=-1;
+ for (Bitu cmdnum=1; cmdnum<=cmd.GetCount(); cmdnum++) {
+ if (cmd.FindCommand(cmdnum,str)) {
+ if (str.find('%')==str.length()-1) {
+ str.erase(str.find('%'));
+ int percval=0;
+ std::istringstream stream(str);
+ stream >> percval;
+ if ((percval>0) && (percval<=105)) CPU_CyclePercUsed=(Bit32s)percval;
+ } else if (str=="limit") {
+ cmdnum++;
+ if (cmd.FindCommand(cmdnum,str)) {
+ int cyclimit=0;
+ std::istringstream stream(str);
+ stream >> cyclimit;
+ if (cyclimit>0) CPU_CycleLimit=cyclimit;
+ }
+ }
+ }
+ }
+ } else {
+ if (type=="auto") {
+ CPU_AutoDetermineMode|=CPU_AUTODETERMINE_CYCLES;
+ CPU_CycleMax=3000;
+ CPU_OldCycleMax=3000;
+ CPU_CyclePercUsed=100;
+ for (Bitu cmdnum=0; cmdnum<=cmd.GetCount(); cmdnum++) {
+ if (cmd.FindCommand(cmdnum,str)) {
+ if (str.find('%')==str.length()-1) {
+ str.erase(str.find('%'));
+ int percval=0;
+ std::istringstream stream(str);
+ stream >> percval;
+ if ((percval>0) && (percval<=105)) CPU_CyclePercUsed=(Bit32s)percval;
+ } else if (str=="limit") {
+ cmdnum++;
+ if (cmd.FindCommand(cmdnum,str)) {
+ int cyclimit=0;
+ std::istringstream stream(str);
+ stream >> cyclimit;
+ if (cyclimit>0) CPU_CycleLimit=cyclimit;
+ }
+ } else {
+ int rmdval=0;
+ std::istringstream stream(str);
+ stream >> rmdval;
+ if (rmdval>0) {
+ CPU_CycleMax=(Bit32s)rmdval;
+ CPU_OldCycleMax=(Bit32s)rmdval;
+ }
+ }
+ }
+ }
+ } else if(type =="fixed") {
+ cmd.FindCommand(1,str);
+ int rmdval=0;
+ std::istringstream stream(str);
+ stream >> rmdval;
+ CPU_CycleMax=(Bit32s)rmdval;
+ } else {
+ std::istringstream stream(type);
+ int rmdval=0;
+ stream >> rmdval;
+ if(rmdval) CPU_CycleMax=(Bit32s)rmdval;
+ }
+ CPU_CycleAutoAdjust=false;
+ }
+
+ CPU_CycleUp=section->Get_int("cycleup");
+ CPU_CycleDown=section->Get_int("cycledown");
+ std::string core(section->Get_string("core"));
+ cpudecoder=&CPU_Core_Normal_Run;
+ if (core == "normal") {
+ cpudecoder=&CPU_Core_Normal_Run;
+ } else if (core =="simple") {
+ cpudecoder=&CPU_Core_Simple_Run;
+ } else if (core == "full") {
+ cpudecoder=&CPU_Core_Full_Run;
+ } else if (core == "auto") {
+ cpudecoder=&CPU_Core_Normal_Run;
+#if (C_DYNAMIC_X86)
+ CPU_AutoDetermineMode|=CPU_AUTODETERMINE_CORE;
+ }
+ else if (core == "dynamic") {
+ cpudecoder=&CPU_Core_Dyn_X86_Run;
+ CPU_Core_Dyn_X86_SetFPUMode(true);
+ } else if (core == "dynamic_nodhfpu") {
+ cpudecoder=&CPU_Core_Dyn_X86_Run;
+ CPU_Core_Dyn_X86_SetFPUMode(false);
+#elif (C_DYNREC)
+ CPU_AutoDetermineMode|=CPU_AUTODETERMINE_CORE;
+ }
+ else if (core == "dynamic") {
+ cpudecoder=&CPU_Core_Dynrec_Run;
+#else
+
+#endif
+ }
+
+#if (C_DYNAMIC_X86)
+ CPU_Core_Dyn_X86_Cache_Init((core == "dynamic") || (core == "dynamic_nodhfpu"));
+#elif (C_DYNREC)
+ CPU_Core_Dynrec_Cache_Init( core == "dynamic" );
+#endif
+
+ CPU_ArchitectureType = CPU_ARCHTYPE_MIXED;
+ std::string cputype(section->Get_string("cputype"));
+ if (cputype == "auto") {
+ CPU_ArchitectureType = CPU_ARCHTYPE_MIXED;
+ } else if (cputype == "386") {
+ CPU_ArchitectureType = CPU_ARCHTYPE_386FAST;
+ } else if (cputype == "386_prefetch") {
+ CPU_ArchitectureType = CPU_ARCHTYPE_386FAST;
+ if (core == "normal") {
+ cpudecoder=&CPU_Core_Prefetch_Run;
+ CPU_PrefetchQueueSize = 16;
+ } else if (core == "auto") {
+ cpudecoder=&CPU_Core_Prefetch_Run;
+ CPU_PrefetchQueueSize = 16;
+ CPU_AutoDetermineMode&=(~CPU_AUTODETERMINE_CORE);
+ } else {
+ E_Exit("prefetch queue emulation requires the normal core setting.");
+ }
+ } else if (cputype == "386_slow") {
+ CPU_ArchitectureType = CPU_ARCHTYPE_386SLOW;
+ } else if (cputype == "486_slow") {
+ CPU_ArchitectureType = CPU_ARCHTYPE_486NEWSLOW;
+ } else if (cputype == "486_prefetch") {
+ CPU_ArchitectureType = CPU_ARCHTYPE_486NEWSLOW;
+ if (core == "normal") {
+ cpudecoder=&CPU_Core_Prefetch_Run;
+ CPU_PrefetchQueueSize = 32;
+ } else if (core == "auto") {
+ cpudecoder=&CPU_Core_Prefetch_Run;
+ CPU_PrefetchQueueSize = 32;
+ CPU_AutoDetermineMode&=(~CPU_AUTODETERMINE_CORE);
+ } else {
+ E_Exit("prefetch queue emulation requires the normal core setting.");
+ }
+ } else if (cputype == "pentium_slow") {
+ CPU_ArchitectureType = CPU_ARCHTYPE_PENTIUMSLOW;
+ }
+
+ if (CPU_ArchitectureType>=CPU_ARCHTYPE_486NEWSLOW) CPU_extflags_toggle=(FLAG_ID|FLAG_AC);
+ else if (CPU_ArchitectureType>=CPU_ARCHTYPE_486OLDSLOW) CPU_extflags_toggle=(FLAG_AC);
+ else CPU_extflags_toggle=0;
+
+
+ if(CPU_CycleMax <= 0) CPU_CycleMax = 3000;
+ if(CPU_CycleUp <= 0) CPU_CycleUp = 500;
+ if(CPU_CycleDown <= 0) CPU_CycleDown = 20;
+ if (CPU_CycleAutoAdjust) GFX_SetTitle(CPU_CyclePercUsed,-1,false);
+ else GFX_SetTitle(CPU_CycleMax,-1,false);
+ return true;
+ }
+ ~CPU(){ /* empty */};
+};
+
+static CPU * test;
+
+void CPU_ShutDown(Section* sec) {
+#if (C_DYNAMIC_X86)
+ CPU_Core_Dyn_X86_Cache_Close();
+#elif (C_DYNREC)
+ CPU_Core_Dynrec_Cache_Close();
+#endif
+ delete test;
+}
+
+void CPU_Init(Section* sec) {
+ test = new CPU(sec);
+ sec->AddDestroyFunction(&CPU_ShutDown,true);
+}
+//initialize static members
+bool CPU::inited=false;
diff --git a/src/cpu/flags.cpp b/src/cpu/flags.cpp
new file mode 100644
index 000000000..55c6fa86f
--- /dev/null
+++ b/src/cpu/flags.cpp
@@ -0,0 +1,1188 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+/*
+ Lazy flag system was designed after the same kind of system used in Bochs.
+ Probably still some bugs left in here.
+*/
+
+#include "dosbox.h"
+#include "cpu.h"
+#include "lazyflags.h"
+#include "pic.h"
+
+LazyFlags lflags;
+
+/* CF Carry Flag -- Set on high-order bit carry or borrow; cleared
+ otherwise.
+*/
+Bit32u get_CF(void) {
+
+ switch (lflags.type) {
+ case t_UNKNOWN:
+ case t_INCb:
+ case t_INCw:
+ case t_INCd:
+ case t_DECb:
+ case t_DECw:
+ case t_DECd:
+ case t_MUL:
+ return GETFLAG(CF);
+ case t_ADDb:
+ return (lf_resb<lf_var1b);
+ case t_ADDw:
+ return (lf_resw<lf_var1w);
+ case t_ADDd:
+ return (lf_resd<lf_var1d);
+ case t_ADCb:
+ return (lf_resb < lf_var1b) || (lflags.oldcf && (lf_resb == lf_var1b));
+ case t_ADCw:
+ return (lf_resw < lf_var1w) || (lflags.oldcf && (lf_resw == lf_var1w));
+ case t_ADCd:
+ return (lf_resd < lf_var1d) || (lflags.oldcf && (lf_resd == lf_var1d));
+ case t_SBBb:
+ return (lf_var1b < lf_resb) || (lflags.oldcf && (lf_var2b==0xff));
+ case t_SBBw:
+ return (lf_var1w < lf_resw) || (lflags.oldcf && (lf_var2w==0xffff));
+ case t_SBBd:
+ return (lf_var1d < lf_resd) || (lflags.oldcf && (lf_var2d==0xffffffff));
+ case t_SUBb:
+ case t_CMPb:
+ return (lf_var1b<lf_var2b);
+ case t_SUBw:
+ case t_CMPw:
+ return (lf_var1w<lf_var2w);
+ case t_SUBd:
+ case t_CMPd:
+ return (lf_var1d<lf_var2d);
+ case t_SHLb:
+ if (lf_var2b>8) return false;
+ else return (lf_var1b >> (8-lf_var2b)) & 1;
+ case t_SHLw:
+ if (lf_var2b>16) return false;
+ else return (lf_var1w >> (16-lf_var2b)) & 1;
+ case t_SHLd:
+ case t_DSHLw: /* Hmm this is not correct for shift higher than 16 */
+ case t_DSHLd:
+ return (lf_var1d >> (32 - lf_var2b)) & 1;
+ case t_RCRb:
+ case t_SHRb:
+ return (lf_var1b >> (lf_var2b - 1)) & 1;
+ case t_RCRw:
+ case t_SHRw:
+ return (lf_var1w >> (lf_var2b - 1)) & 1;
+ case t_RCRd:
+ case t_SHRd:
+ case t_DSHRw: /* Hmm this is not correct for shift higher than 16 */
+ case t_DSHRd:
+ return (lf_var1d >> (lf_var2b - 1)) & 1;
+ case t_SARb:
+ return (((Bit8s) lf_var1b) >> (lf_var2b - 1)) & 1;
+ case t_SARw:
+ return (((Bit16s) lf_var1w) >> (lf_var2b - 1)) & 1;
+ case t_SARd:
+ return (((Bit32s) lf_var1d) >> (lf_var2b - 1)) & 1;
+ case t_NEGb:
+ return lf_var1b;
+ case t_NEGw:
+ return lf_var1w;
+ case t_NEGd:
+ return lf_var1d;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ return false; /* Set to false */
+ case t_DIV:
+ return false; /* Unkown */
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("get_CF Unknown %d",lflags.type);
+ }
+ return 0;
+}
+
+/* AF Adjust flag -- Set on carry from or borrow to the low order
+ four bits of AL; cleared otherwise. Used for decimal
+ arithmetic.
+*/
+Bit32u get_AF(void) {
+ Bitu type=lflags.type;
+ switch (type) {
+ case t_UNKNOWN:
+ return GETFLAG(AF);
+ case t_ADDb:
+ case t_ADCb:
+ case t_SBBb:
+ case t_SUBb:
+ case t_CMPb:
+ return ((lf_var1b ^ lf_var2b) ^ lf_resb) & 0x10;
+ case t_ADDw:
+ case t_ADCw:
+ case t_SBBw:
+ case t_SUBw:
+ case t_CMPw:
+ return ((lf_var1w ^ lf_var2w) ^ lf_resw) & 0x10;
+ case t_ADCd:
+ case t_ADDd:
+ case t_SBBd:
+ case t_SUBd:
+ case t_CMPd:
+ return ((lf_var1d ^ lf_var2d) ^ lf_resd) & 0x10;
+ case t_INCb:
+ return (lf_resb & 0x0f) == 0;
+ case t_INCw:
+ return (lf_resw & 0x0f) == 0;
+ case t_INCd:
+ return (lf_resd & 0x0f) == 0;
+ case t_DECb:
+ return (lf_resb & 0x0f) == 0x0f;
+ case t_DECw:
+ return (lf_resw & 0x0f) == 0x0f;
+ case t_DECd:
+ return (lf_resd & 0x0f) == 0x0f;
+ case t_NEGb:
+ return lf_var1b & 0x0f;
+ case t_NEGw:
+ return lf_var1w & 0x0f;
+ case t_NEGd:
+ return lf_var1d & 0x0f;
+ case t_SHLb:
+ case t_SHRb:
+ case t_SARb:
+ return lf_var2b & 0x1f;
+ case t_SHLw:
+ case t_SHRw:
+ case t_SARw:
+ return lf_var2w & 0x1f;
+ case t_SHLd:
+ case t_SHRd:
+ case t_SARd:
+ return lf_var2d & 0x1f;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ case t_DSHLw:
+ case t_DSHLd:
+ case t_DSHRw:
+ case t_DSHRd:
+ case t_DIV:
+ case t_MUL:
+ return false; /* Unkown */
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("get_AF Unknown %d",lflags.type);
+ }
+ return 0;
+}
+
+/* ZF Zero Flag -- Set if result is zero; cleared otherwise.
+*/
+
+Bit32u get_ZF(void) {
+ Bitu type=lflags.type;
+ switch (type) {
+ case t_UNKNOWN:
+ return GETFLAG(ZF);
+ case t_ADDb:
+ case t_ORb:
+ case t_ADCb:
+ case t_SBBb:
+ case t_ANDb:
+ case t_XORb:
+ case t_SUBb:
+ case t_CMPb:
+ case t_INCb:
+ case t_DECb:
+ case t_TESTb:
+ case t_SHLb:
+ case t_SHRb:
+ case t_SARb:
+ case t_NEGb:
+ return (lf_resb==0);
+ case t_ADDw:
+ case t_ORw:
+ case t_ADCw:
+ case t_SBBw:
+ case t_ANDw:
+ case t_XORw:
+ case t_SUBw:
+ case t_CMPw:
+ case t_INCw:
+ case t_DECw:
+ case t_TESTw:
+ case t_SHLw:
+ case t_SHRw:
+ case t_SARw:
+ case t_DSHLw:
+ case t_DSHRw:
+ case t_NEGw:
+ return (lf_resw==0);
+ case t_ADDd:
+ case t_ORd:
+ case t_ADCd:
+ case t_SBBd:
+ case t_ANDd:
+ case t_XORd:
+ case t_SUBd:
+ case t_CMPd:
+ case t_INCd:
+ case t_DECd:
+ case t_TESTd:
+ case t_SHLd:
+ case t_SHRd:
+ case t_SARd:
+ case t_DSHLd:
+ case t_DSHRd:
+ case t_NEGd:
+ return (lf_resd==0);
+ case t_DIV:
+ case t_MUL:
+ return false; /* Unkown */
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("get_ZF Unknown %d",lflags.type);
+ }
+ return false;
+}
+/* SF Sign Flag -- Set equal to high-order bit of result (0 is
+ positive, 1 if negative).
+*/
+Bit32u get_SF(void) {
+ Bitu type=lflags.type;
+ switch (type) {
+ case t_UNKNOWN:
+ return GETFLAG(SF);
+ case t_ADDb:
+ case t_ORb:
+ case t_ADCb:
+ case t_SBBb:
+ case t_ANDb:
+ case t_XORb:
+ case t_SUBb:
+ case t_CMPb:
+ case t_INCb:
+ case t_DECb:
+ case t_TESTb:
+ case t_SHLb:
+ case t_SHRb:
+ case t_SARb:
+ case t_NEGb:
+ return (lf_resb&0x80);
+ case t_ADDw:
+ case t_ORw:
+ case t_ADCw:
+ case t_SBBw:
+ case t_ANDw:
+ case t_XORw:
+ case t_SUBw:
+ case t_CMPw:
+ case t_INCw:
+ case t_DECw:
+ case t_TESTw:
+ case t_SHLw:
+ case t_SHRw:
+ case t_SARw:
+ case t_DSHLw:
+ case t_DSHRw:
+ case t_NEGw:
+ return (lf_resw&0x8000);
+ case t_ADDd:
+ case t_ORd:
+ case t_ADCd:
+ case t_SBBd:
+ case t_ANDd:
+ case t_XORd:
+ case t_SUBd:
+ case t_CMPd:
+ case t_INCd:
+ case t_DECd:
+ case t_TESTd:
+ case t_SHLd:
+ case t_SHRd:
+ case t_SARd:
+ case t_DSHLd:
+ case t_DSHRd:
+ case t_NEGd:
+ return (lf_resd&0x80000000);
+ case t_DIV:
+ case t_MUL:
+ return false; /* Unkown */
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("get_SF Unkown %d",lflags.type);
+ }
+ return false;
+
+}
+Bit32u get_OF(void) {
+ Bitu type=lflags.type;
+ switch (type) {
+ case t_UNKNOWN:
+ case t_MUL:
+ return GETFLAG(OF);
+ case t_ADDb:
+ case t_ADCb:
+ return ((lf_var1b ^ lf_var2b ^ 0x80) & (lf_resb ^ lf_var2b)) & 0x80;
+ case t_ADDw:
+ case t_ADCw:
+ return ((lf_var1w ^ lf_var2w ^ 0x8000) & (lf_resw ^ lf_var2w)) & 0x8000;
+ case t_ADDd:
+ case t_ADCd:
+ return ((lf_var1d ^ lf_var2d ^ 0x80000000) & (lf_resd ^ lf_var2d)) & 0x80000000;
+ case t_SBBb:
+ case t_SUBb:
+ case t_CMPb:
+ return ((lf_var1b ^ lf_var2b) & (lf_var1b ^ lf_resb)) & 0x80;
+ case t_SBBw:
+ case t_SUBw:
+ case t_CMPw:
+ return ((lf_var1w ^ lf_var2w) & (lf_var1w ^ lf_resw)) & 0x8000;
+ case t_SBBd:
+ case t_SUBd:
+ case t_CMPd:
+ return ((lf_var1d ^ lf_var2d) & (lf_var1d ^ lf_resd)) & 0x80000000;
+ case t_INCb:
+ return (lf_resb == 0x80);
+ case t_INCw:
+ return (lf_resw == 0x8000);
+ case t_INCd:
+ return (lf_resd == 0x80000000);
+ case t_DECb:
+ return (lf_resb == 0x7f);
+ case t_DECw:
+ return (lf_resw == 0x7fff);
+ case t_DECd:
+ return (lf_resd == 0x7fffffff);
+ case t_NEGb:
+ return (lf_var1b == 0x80);
+ case t_NEGw:
+ return (lf_var1w == 0x8000);
+ case t_NEGd:
+ return (lf_var1d == 0x80000000);
+ case t_SHLb:
+ return (lf_resb ^ lf_var1b) & 0x80;
+ case t_SHLw:
+ case t_DSHRw:
+ case t_DSHLw:
+ return (lf_resw ^ lf_var1w) & 0x8000;
+ case t_SHLd:
+ case t_DSHRd:
+ case t_DSHLd:
+ return (lf_resd ^ lf_var1d) & 0x80000000;
+ case t_SHRb:
+ if ((lf_var2b&0x1f)==1) return (lf_var1b > 0x80);
+ else return false;
+ case t_SHRw:
+ if ((lf_var2b&0x1f)==1) return (lf_var1w > 0x8000);
+ else return false;
+ case t_SHRd:
+ if ((lf_var2b&0x1f)==1) return (lf_var1d > 0x80000000);
+ else return false;
+ case t_ORb:
+ case t_ORw:
+ case t_ORd:
+ case t_ANDb:
+ case t_ANDw:
+ case t_ANDd:
+ case t_XORb:
+ case t_XORw:
+ case t_XORd:
+ case t_TESTb:
+ case t_TESTw:
+ case t_TESTd:
+ case t_SARb:
+ case t_SARw:
+ case t_SARd:
+ return false; /* Return false */
+ case t_DIV:
+ return false; /* Unkown */
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("get_OF Unkown %d",lflags.type);
+ }
+ return false;
+}
+
+Bit16u parity_lookup[256] = {
+ FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF,
+ 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0,
+ 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0,
+ FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF,
+ 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0,
+ FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF,
+ FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF,
+ 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0,
+ 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0,
+ FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF,
+ FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF,
+ 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0,
+ FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF,
+ 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0,
+ 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0,
+ FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF
+ };
+
+Bit32u get_PF(void) {
+ switch (lflags.type) {
+ case t_UNKNOWN:
+ return GETFLAG(PF);
+ default:
+ return (parity_lookup[lf_resb]);
+ };
+ return 0;
+}
+
+
+#if 0
+
+Bitu FillFlags(void) {
+// if (lflags.type==t_UNKNOWN) return reg_flags;
+ Bitu new_word=(reg_flags & ~FLAG_MASK);
+ if (get_CF()) new_word|=FLAG_CF;
+ if (get_PF()) new_word|=FLAG_PF;
+ if (get_AF()) new_word|=FLAG_AF;
+ if (get_ZF()) new_word|=FLAG_ZF;
+ if (get_SF()) new_word|=FLAG_SF;
+ if (get_OF()) new_word|=FLAG_OF;
+ reg_flags=new_word;
+ lflags.type=t_UNKNOWN;
+ return reg_flags;
+}
+
+#else
+
+#define DOFLAG_PF reg_flags=(reg_flags & ~FLAG_PF) | parity_lookup[lf_resb];
+
+#define DOFLAG_AF reg_flags=(reg_flags & ~FLAG_AF) | (((lf_var1b ^ lf_var2b) ^ lf_resb) & 0x10);
+
+#define DOFLAG_ZFb SETFLAGBIT(ZF,lf_resb==0);
+#define DOFLAG_ZFw SETFLAGBIT(ZF,lf_resw==0);
+#define DOFLAG_ZFd SETFLAGBIT(ZF,lf_resd==0);
+
+#define DOFLAG_SFb reg_flags=(reg_flags & ~FLAG_SF) | ((lf_resb & 0x80) >> 0);
+#define DOFLAG_SFw reg_flags=(reg_flags & ~FLAG_SF) | ((lf_resw & 0x8000) >> 8);
+#define DOFLAG_SFd reg_flags=(reg_flags & ~FLAG_SF) | ((lf_resd & 0x80000000) >> 24);
+
+#define SETCF(NEWBIT) reg_flags=(reg_flags & ~FLAG_CF)|(NEWBIT);
+
+#define SET_FLAG SETFLAGBIT
+
+Bitu FillFlags(void) {
+ switch (lflags.type) {
+ case t_UNKNOWN:
+ break;
+ case t_ADDb:
+ SET_FLAG(CF,(lf_resb<lf_var1b));
+ DOFLAG_AF;
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ SET_FLAG(OF,((lf_var1b ^ lf_var2b ^ 0x80) & (lf_resb ^ lf_var1b)) & 0x80);
+ DOFLAG_PF;
+ break;
+ case t_ADDw:
+ SET_FLAG(CF,(lf_resw<lf_var1w));
+ DOFLAG_AF;
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,((lf_var1w ^ lf_var2w ^ 0x8000) & (lf_resw ^ lf_var1w)) & 0x8000);
+ DOFLAG_PF;
+ break;
+ case t_ADDd:
+ SET_FLAG(CF,(lf_resd<lf_var1d));
+ DOFLAG_AF;
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,((lf_var1d ^ lf_var2d ^ 0x80000000) & (lf_resd ^ lf_var1d)) & 0x80000000);
+ DOFLAG_PF;
+ break;
+ case t_ADCb:
+ SET_FLAG(CF,(lf_resb < lf_var1b) || (lflags.oldcf && (lf_resb == lf_var1b)));
+ DOFLAG_AF;
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ SET_FLAG(OF,((lf_var1b ^ lf_var2b ^ 0x80) & (lf_resb ^ lf_var1b)) & 0x80);
+ DOFLAG_PF;
+ break;
+ case t_ADCw:
+ SET_FLAG(CF,(lf_resw < lf_var1w) || (lflags.oldcf && (lf_resw == lf_var1w)));
+ DOFLAG_AF;
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,((lf_var1w ^ lf_var2w ^ 0x8000) & (lf_resw ^ lf_var1w)) & 0x8000);
+ DOFLAG_PF;
+ break;
+ case t_ADCd:
+ SET_FLAG(CF,(lf_resd < lf_var1d) || (lflags.oldcf && (lf_resd == lf_var1d)));
+ DOFLAG_AF;
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,((lf_var1d ^ lf_var2d ^ 0x80000000) & (lf_resd ^ lf_var1d)) & 0x80000000);
+ DOFLAG_PF;
+ break;
+
+
+ case t_SBBb:
+ SET_FLAG(CF,(lf_var1b < lf_resb) || (lflags.oldcf && (lf_var2b==0xff)));
+ DOFLAG_AF;
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ SET_FLAG(OF,(lf_var1b ^ lf_var2b) & (lf_var1b ^ lf_resb) & 0x80);
+ DOFLAG_PF;
+ break;
+ case t_SBBw:
+ SET_FLAG(CF,(lf_var1w < lf_resw) || (lflags.oldcf && (lf_var2w==0xffff)));
+ DOFLAG_AF;
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,(lf_var1w ^ lf_var2w) & (lf_var1w ^ lf_resw) & 0x8000);
+ DOFLAG_PF;
+ break;
+ case t_SBBd:
+ SET_FLAG(CF,(lf_var1d < lf_resd) || (lflags.oldcf && (lf_var2d==0xffffffff)));
+ DOFLAG_AF;
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,(lf_var1d ^ lf_var2d) & (lf_var1d ^ lf_resd) & 0x80000000);
+ DOFLAG_PF;
+ break;
+
+
+ case t_SUBb:
+ case t_CMPb:
+ SET_FLAG(CF,(lf_var1b<lf_var2b));
+ DOFLAG_AF;
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ SET_FLAG(OF,(lf_var1b ^ lf_var2b) & (lf_var1b ^ lf_resb) & 0x80);
+ DOFLAG_PF;
+ break;
+ case t_SUBw:
+ case t_CMPw:
+ SET_FLAG(CF,(lf_var1w<lf_var2w));
+ DOFLAG_AF;
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,(lf_var1w ^ lf_var2w) & (lf_var1w ^ lf_resw) & 0x8000);
+ DOFLAG_PF;
+ break;
+ case t_SUBd:
+ case t_CMPd:
+ SET_FLAG(CF,(lf_var1d<lf_var2d));
+ DOFLAG_AF;
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,(lf_var1d ^ lf_var2d) & (lf_var1d ^ lf_resd) & 0x80000000);
+ DOFLAG_PF;
+ break;
+
+
+ case t_ORb:
+ SET_FLAG(CF,false);
+ SET_FLAG(AF,false);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ SET_FLAG(OF,false);
+ DOFLAG_PF;
+ break;
+ case t_ORw:
+ SET_FLAG(CF,false);
+ SET_FLAG(AF,false);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,false);
+ DOFLAG_PF;
+ break;
+ case t_ORd:
+ SET_FLAG(CF,false);
+ SET_FLAG(AF,false);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,false);
+ DOFLAG_PF;
+ break;
+
+
+ case t_TESTb:
+ case t_ANDb:
+ SET_FLAG(CF,false);
+ SET_FLAG(AF,false);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ SET_FLAG(OF,false);
+ DOFLAG_PF;
+ break;
+ case t_TESTw:
+ case t_ANDw:
+ SET_FLAG(CF,false);
+ SET_FLAG(AF,false);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,false);
+ DOFLAG_PF;
+ break;
+ case t_TESTd:
+ case t_ANDd:
+ SET_FLAG(CF,false);
+ SET_FLAG(AF,false);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,false);
+ DOFLAG_PF;
+ break;
+
+
+ case t_XORb:
+ SET_FLAG(CF,false);
+ SET_FLAG(AF,false);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ SET_FLAG(OF,false);
+ DOFLAG_PF;
+ break;
+ case t_XORw:
+ SET_FLAG(CF,false);
+ SET_FLAG(AF,false);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,false);
+ DOFLAG_PF;
+ break;
+ case t_XORd:
+ SET_FLAG(CF,false);
+ SET_FLAG(AF,false);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,false);
+ DOFLAG_PF;
+ break;
+
+
+ case t_SHLb:
+ if (lf_var2b>8) SET_FLAG(CF,false);
+ else SET_FLAG(CF,(lf_var1b >> (8-lf_var2b)) & 1);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ SET_FLAG(OF,(lf_resb ^ lf_var1b) & 0x80);
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2b&0x1f));
+ break;
+ case t_SHLw:
+ if (lf_var2b>16) SET_FLAG(CF,false);
+ else SET_FLAG(CF,(lf_var1w >> (16-lf_var2b)) & 1);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,(lf_resw ^ lf_var1w) & 0x8000);
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2w&0x1f));
+ break;
+ case t_SHLd:
+ SET_FLAG(CF,(lf_var1d >> (32 - lf_var2b)) & 1);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,(lf_resd ^ lf_var1d) & 0x80000000);
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2d&0x1f));
+ break;
+
+
+ case t_DSHLw:
+ SET_FLAG(CF,(lf_var1d >> (32 - lf_var2b)) & 1);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,(lf_resw ^ lf_var1w) & 0x8000);
+ DOFLAG_PF;
+ break;
+ case t_DSHLd:
+ SET_FLAG(CF,(lf_var1d >> (32 - lf_var2b)) & 1);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,(lf_resd ^ lf_var1d) & 0x80000000);
+ DOFLAG_PF;
+ break;
+
+
+ case t_SHRb:
+ SET_FLAG(CF,(lf_var1b >> (lf_var2b - 1)) & 1);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ if ((lf_var2b&0x1f)==1) SET_FLAG(OF,(lf_var1b >= 0x80));
+ else SET_FLAG(OF,false);
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2b&0x1f));
+ break;
+ case t_SHRw:
+ SET_FLAG(CF,(lf_var1w >> (lf_var2b - 1)) & 1);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ if ((lf_var2w&0x1f)==1) SET_FLAG(OF,(lf_var1w >= 0x8000));
+ else SET_FLAG(OF,false);
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2w&0x1f));
+ break;
+ case t_SHRd:
+ SET_FLAG(CF,(lf_var1d >> (lf_var2b - 1)) & 1);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ if ((lf_var2d&0x1f)==1) SET_FLAG(OF,(lf_var1d >= 0x80000000));
+ else SET_FLAG(OF,false);
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2d&0x1f));
+ break;
+
+
+ case t_DSHRw: /* Hmm this is not correct for shift higher than 16 */
+ SET_FLAG(CF,(lf_var1d >> (lf_var2b - 1)) & 1);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,(lf_resw ^ lf_var1w) & 0x8000);
+ DOFLAG_PF;
+ break;
+ case t_DSHRd:
+ SET_FLAG(CF,(lf_var1d >> (lf_var2b - 1)) & 1);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,(lf_resd ^ lf_var1d) & 0x80000000);
+ DOFLAG_PF;
+ break;
+
+
+ case t_SARb:
+ SET_FLAG(CF,(((Bit8s) lf_var1b) >> (lf_var2b - 1)) & 1);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ SET_FLAG(OF,false);
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2b&0x1f));
+ break;
+ case t_SARw:
+ SET_FLAG(CF,(((Bit16s) lf_var1w) >> (lf_var2b - 1)) & 1);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,false);
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2w&0x1f));
+ break;
+ case t_SARd:
+ SET_FLAG(CF,(((Bit32s) lf_var1d) >> (lf_var2b - 1)) & 1);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,false);
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2d&0x1f));
+ break;
+
+ case t_INCb:
+ SET_FLAG(AF,(lf_resb & 0x0f) == 0);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ SET_FLAG(OF,(lf_resb == 0x80));
+ DOFLAG_PF;
+ break;
+ case t_INCw:
+ SET_FLAG(AF,(lf_resw & 0x0f) == 0);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,(lf_resw == 0x8000));
+ DOFLAG_PF;
+ break;
+ case t_INCd:
+ SET_FLAG(AF,(lf_resd & 0x0f) == 0);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,(lf_resd == 0x80000000));
+ DOFLAG_PF;
+ break;
+
+ case t_DECb:
+ SET_FLAG(AF,(lf_resb & 0x0f) == 0x0f);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ SET_FLAG(OF,(lf_resb == 0x7f));
+ DOFLAG_PF;
+ break;
+ case t_DECw:
+ SET_FLAG(AF,(lf_resw & 0x0f) == 0x0f);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,(lf_resw == 0x7fff));
+ DOFLAG_PF;
+ break;
+ case t_DECd:
+ SET_FLAG(AF,(lf_resd & 0x0f) == 0x0f);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,(lf_resd == 0x7fffffff));
+ DOFLAG_PF;
+ break;
+
+ case t_NEGb:
+ SET_FLAG(CF,(lf_var1b!=0));
+ SET_FLAG(AF,(lf_resb & 0x0f) != 0);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ SET_FLAG(OF,(lf_var1b == 0x80));
+ DOFLAG_PF;
+ break;
+ case t_NEGw:
+ SET_FLAG(CF,(lf_var1w!=0));
+ SET_FLAG(AF,(lf_resw & 0x0f) != 0);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ SET_FLAG(OF,(lf_var1w == 0x8000));
+ DOFLAG_PF;
+ break;
+ case t_NEGd:
+ SET_FLAG(CF,(lf_var1d!=0));
+ SET_FLAG(AF,(lf_resd & 0x0f) != 0);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ SET_FLAG(OF,(lf_var1d == 0x80000000));
+ DOFLAG_PF;
+ break;
+
+
+ case t_DIV:
+ case t_MUL:
+ break;
+
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Unhandled flag type %d",lflags.type);
+ return 0;
+ }
+ lflags.type=t_UNKNOWN;
+ return reg_flags;
+}
+
+void FillFlagsNoCFOF(void) {
+ switch (lflags.type) {
+ case t_UNKNOWN:
+ return;
+ case t_ADDb:
+ DOFLAG_AF;
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ break;
+ case t_ADDw:
+ DOFLAG_AF;
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ break;
+ case t_ADDd:
+ DOFLAG_AF;
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ break;
+ case t_ADCb:
+ DOFLAG_AF;
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ break;
+ case t_ADCw:
+ DOFLAG_AF;
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ break;
+ case t_ADCd:
+ DOFLAG_AF;
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ break;
+
+
+ case t_SBBb:
+ DOFLAG_AF;
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ break;
+ case t_SBBw:
+ DOFLAG_AF;
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ break;
+ case t_SBBd:
+ DOFLAG_AF;
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ break;
+
+
+ case t_SUBb:
+ case t_CMPb:
+ DOFLAG_AF;
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ break;
+ case t_SUBw:
+ case t_CMPw:
+ DOFLAG_AF;
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ break;
+ case t_SUBd:
+ case t_CMPd:
+ DOFLAG_AF;
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ break;
+
+
+ case t_ORb:
+ SET_FLAG(AF,false);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ break;
+ case t_ORw:
+ SET_FLAG(AF,false);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ break;
+ case t_ORd:
+ SET_FLAG(AF,false);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ break;
+
+
+ case t_TESTb:
+ case t_ANDb:
+ SET_FLAG(AF,false);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ break;
+ case t_TESTw:
+ case t_ANDw:
+ SET_FLAG(AF,false);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ break;
+ case t_TESTd:
+ case t_ANDd:
+ SET_FLAG(AF,false);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ break;
+
+
+ case t_XORb:
+ SET_FLAG(AF,false);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ break;
+ case t_XORw:
+ SET_FLAG(AF,false);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ break;
+ case t_XORd:
+ SET_FLAG(AF,false);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ break;
+
+
+ case t_SHLb:
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2b&0x1f));
+ break;
+ case t_SHLw:
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2w&0x1f));
+ break;
+ case t_SHLd:
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2d&0x1f));
+ break;
+
+
+ case t_DSHLw:
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ break;
+ case t_DSHLd:
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ break;
+
+
+ case t_SHRb:
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2b&0x1f));
+ break;
+ case t_SHRw:
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2w&0x1f));
+ break;
+ case t_SHRd:
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2d&0x1f));
+ break;
+
+
+ case t_DSHRw: /* Hmm this is not correct for shift higher than 16 */
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ break;
+ case t_DSHRd:
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ break;
+
+
+ case t_SARb:
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2b&0x1f));
+ break;
+ case t_SARw:
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2w&0x1f));
+ break;
+ case t_SARd:
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ SET_FLAG(AF,(lf_var2d&0x1f));
+ break;
+
+ case t_INCb:
+ SET_FLAG(AF,(lf_resb & 0x0f) == 0);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ break;
+ case t_INCw:
+ SET_FLAG(AF,(lf_resw & 0x0f) == 0);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ break;
+ case t_INCd:
+ SET_FLAG(AF,(lf_resd & 0x0f) == 0);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ break;
+
+ case t_DECb:
+ SET_FLAG(AF,(lf_resb & 0x0f) == 0x0f);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ break;
+ case t_DECw:
+ SET_FLAG(AF,(lf_resw & 0x0f) == 0x0f);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ break;
+ case t_DECd:
+ SET_FLAG(AF,(lf_resd & 0x0f) == 0x0f);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ break;
+
+ case t_NEGb:
+ SET_FLAG(AF,(lf_resb & 0x0f) != 0);
+ DOFLAG_ZFb;
+ DOFLAG_SFb;
+ DOFLAG_PF;
+ break;
+ case t_NEGw:
+ SET_FLAG(AF,(lf_resw & 0x0f) != 0);
+ DOFLAG_ZFw;
+ DOFLAG_SFw;
+ DOFLAG_PF;
+ break;
+ case t_NEGd:
+ SET_FLAG(AF,(lf_resd & 0x0f) != 0);
+ DOFLAG_ZFd;
+ DOFLAG_SFd;
+ DOFLAG_PF;
+ break;
+
+
+ case t_DIV:
+ case t_MUL:
+ break;
+
+ default:
+ LOG(LOG_CPU,LOG_ERROR)("Unhandled flag type %d",lflags.type);
+ break;
+ }
+ lflags.type=t_UNKNOWN;
+}
+
+void DestroyConditionFlags(void) {
+ lflags.type=t_UNKNOWN;
+}
+
+#endif
diff --git a/src/cpu/instructions.h b/src/cpu/instructions.h
new file mode 100644
index 000000000..404ab68b2
--- /dev/null
+++ b/src/cpu/instructions.h
@@ -0,0 +1,936 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+/* Jumps */
+
+/* All Byte general instructions */
+#define ADDB(op1,op2,load,save) \
+ lf_var1b=load(op1);lf_var2b=op2; \
+ lf_resb=lf_var1b+lf_var2b; \
+ save(op1,lf_resb); \
+ lflags.type=t_ADDb;
+
+#define ADCB(op1,op2,load,save) \
+ lflags.oldcf=get_CF()!=0; \
+ lf_var1b=load(op1);lf_var2b=op2; \
+ lf_resb=lf_var1b+lf_var2b+lflags.oldcf; \
+ save(op1,lf_resb); \
+ lflags.type=t_ADCb;
+
+#define SBBB(op1,op2,load,save) \
+ lflags.oldcf=get_CF()!=0; \
+ lf_var1b=load(op1);lf_var2b=op2; \
+ lf_resb=lf_var1b-(lf_var2b+lflags.oldcf); \
+ save(op1,lf_resb); \
+ lflags.type=t_SBBb;
+
+#define SUBB(op1,op2,load,save) \
+ lf_var1b=load(op1);lf_var2b=op2; \
+ lf_resb=lf_var1b-lf_var2b; \
+ save(op1,lf_resb); \
+ lflags.type=t_SUBb;
+
+#define ORB(op1,op2,load,save) \
+ lf_var1b=load(op1);lf_var2b=op2; \
+ lf_resb=lf_var1b | lf_var2b; \
+ save(op1,lf_resb); \
+ lflags.type=t_ORb;
+
+#define XORB(op1,op2,load,save) \
+ lf_var1b=load(op1);lf_var2b=op2; \
+ lf_resb=lf_var1b ^ lf_var2b; \
+ save(op1,lf_resb); \
+ lflags.type=t_XORb;
+
+#define ANDB(op1,op2,load,save) \
+ lf_var1b=load(op1);lf_var2b=op2; \
+ lf_resb=lf_var1b & lf_var2b; \
+ save(op1,lf_resb); \
+ lflags.type=t_ANDb;
+
+#define CMPB(op1,op2,load,save) \
+ lf_var1b=load(op1);lf_var2b=op2; \
+ lf_resb=lf_var1b-lf_var2b; \
+ lflags.type=t_CMPb;
+
+#define TESTB(op1,op2,load,save) \
+ lf_var1b=load(op1);lf_var2b=op2; \
+ lf_resb=lf_var1b & lf_var2b; \
+ lflags.type=t_TESTb;
+
+/* All Word General instructions */
+
+#define ADDW(op1,op2,load,save) \
+ lf_var1w=load(op1);lf_var2w=op2; \
+ lf_resw=lf_var1w+lf_var2w; \
+ save(op1,lf_resw); \
+ lflags.type=t_ADDw;
+
+#define ADCW(op1,op2,load,save) \
+ lflags.oldcf=get_CF()!=0; \
+ lf_var1w=load(op1);lf_var2w=op2; \
+ lf_resw=lf_var1w+lf_var2w+lflags.oldcf; \
+ save(op1,lf_resw); \
+ lflags.type=t_ADCw;
+
+#define SBBW(op1,op2,load,save) \
+ lflags.oldcf=get_CF()!=0; \
+ lf_var1w=load(op1);lf_var2w=op2; \
+ lf_resw=lf_var1w-(lf_var2w+lflags.oldcf); \
+ save(op1,lf_resw); \
+ lflags.type=t_SBBw;
+
+#define SUBW(op1,op2,load,save) \
+ lf_var1w=load(op1);lf_var2w=op2; \
+ lf_resw=lf_var1w-lf_var2w; \
+ save(op1,lf_resw); \
+ lflags.type=t_SUBw;
+
+#define ORW(op1,op2,load,save) \
+ lf_var1w=load(op1);lf_var2w=op2; \
+ lf_resw=lf_var1w | lf_var2w; \
+ save(op1,lf_resw); \
+ lflags.type=t_ORw;
+
+#define XORW(op1,op2,load,save) \
+ lf_var1w=load(op1);lf_var2w=op2; \
+ lf_resw=lf_var1w ^ lf_var2w; \
+ save(op1,lf_resw); \
+ lflags.type=t_XORw;
+
+#define ANDW(op1,op2,load,save) \
+ lf_var1w=load(op1);lf_var2w=op2; \
+ lf_resw=lf_var1w & lf_var2w; \
+ save(op1,lf_resw); \
+ lflags.type=t_ANDw;
+
+#define CMPW(op1,op2,load,save) \
+ lf_var1w=load(op1);lf_var2w=op2; \
+ lf_resw=lf_var1w-lf_var2w; \
+ lflags.type=t_CMPw;
+
+#define TESTW(op1,op2,load,save) \
+ lf_var1w=load(op1);lf_var2w=op2; \
+ lf_resw=lf_var1w & lf_var2w; \
+ lflags.type=t_TESTw;
+
+/* All DWORD General Instructions */
+
+#define ADDD(op1,op2,load,save) \
+ lf_var1d=load(op1);lf_var2d=op2; \
+ lf_resd=lf_var1d+lf_var2d; \
+ save(op1,lf_resd); \
+ lflags.type=t_ADDd;
+
+#define ADCD(op1,op2,load,save) \
+ lflags.oldcf=get_CF()!=0; \
+ lf_var1d=load(op1);lf_var2d=op2; \
+ lf_resd=lf_var1d+lf_var2d+lflags.oldcf; \
+ save(op1,lf_resd); \
+ lflags.type=t_ADCd;
+
+#define SBBD(op1,op2,load,save) \
+ lflags.oldcf=get_CF()!=0; \
+ lf_var1d=load(op1);lf_var2d=op2; \
+ lf_resd=lf_var1d-(lf_var2d+lflags.oldcf); \
+ save(op1,lf_resd); \
+ lflags.type=t_SBBd;
+
+#define SUBD(op1,op2,load,save) \
+ lf_var1d=load(op1);lf_var2d=op2; \
+ lf_resd=lf_var1d-lf_var2d; \
+ save(op1,lf_resd); \
+ lflags.type=t_SUBd;
+
+#define ORD(op1,op2,load,save) \
+ lf_var1d=load(op1);lf_var2d=op2; \
+ lf_resd=lf_var1d | lf_var2d; \
+ save(op1,lf_resd); \
+ lflags.type=t_ORd;
+
+#define XORD(op1,op2,load,save) \
+ lf_var1d=load(op1);lf_var2d=op2; \
+ lf_resd=lf_var1d ^ lf_var2d; \
+ save(op1,lf_resd); \
+ lflags.type=t_XORd;
+
+#define ANDD(op1,op2,load,save) \
+ lf_var1d=load(op1);lf_var2d=op2; \
+ lf_resd=lf_var1d & lf_var2d; \
+ save(op1,lf_resd); \
+ lflags.type=t_ANDd;
+
+#define CMPD(op1,op2,load,save) \
+ lf_var1d=load(op1);lf_var2d=op2; \
+ lf_resd=lf_var1d-lf_var2d; \
+ lflags.type=t_CMPd;
+
+
+#define TESTD(op1,op2,load,save) \
+ lf_var1d=load(op1);lf_var2d=op2; \
+ lf_resd=lf_var1d & lf_var2d; \
+ lflags.type=t_TESTd;
+
+
+
+
+#define INCB(op1,load,save) \
+ LoadCF;lf_var1b=load(op1); \
+ lf_resb=lf_var1b+1; \
+ save(op1,lf_resb); \
+ lflags.type=t_INCb; \
+
+#define INCW(op1,load,save) \
+ LoadCF;lf_var1w=load(op1); \
+ lf_resw=lf_var1w+1; \
+ save(op1,lf_resw); \
+ lflags.type=t_INCw;
+
+#define INCD(op1,load,save) \
+ LoadCF;lf_var1d=load(op1); \
+ lf_resd=lf_var1d+1; \
+ save(op1,lf_resd); \
+ lflags.type=t_INCd;
+
+#define DECB(op1,load,save) \
+ LoadCF;lf_var1b=load(op1); \
+ lf_resb=lf_var1b-1; \
+ save(op1,lf_resb); \
+ lflags.type=t_DECb;
+
+#define DECW(op1,load,save) \
+ LoadCF;lf_var1w=load(op1); \
+ lf_resw=lf_var1w-1; \
+ save(op1,lf_resw); \
+ lflags.type=t_DECw;
+
+#define DECD(op1,load,save) \
+ LoadCF;lf_var1d=load(op1); \
+ lf_resd=lf_var1d-1; \
+ save(op1,lf_resd); \
+ lflags.type=t_DECd;
+
+
+
+#define ROLB(op1,op2,load,save) \
+ FillFlagsNoCFOF(); \
+ lf_var1b=load(op1); \
+ lf_var2b=op2&0x07; \
+ lf_resb=(lf_var1b << lf_var2b) | \
+ (lf_var1b >> (8-lf_var2b)); \
+ save(op1,lf_resb); \
+ SETFLAGBIT(CF,lf_resb & 1); \
+ SETFLAGBIT(OF,(lf_resb & 1) ^ (lf_resb >> 7));
+
+#define ROLW(op1,op2,load,save) \
+ FillFlagsNoCFOF(); \
+ lf_var1w=load(op1); \
+ lf_var2b=op2&0xf; \
+ lf_resw=(lf_var1w << lf_var2b) | \
+ (lf_var1w >> (16-lf_var2b)); \
+ save(op1,lf_resw); \
+ SETFLAGBIT(CF,lf_resw & 1); \
+ SETFLAGBIT(OF,(lf_resw & 1) ^ (lf_resw >> 15));
+
+#define ROLD(op1,op2,load,save) \
+ FillFlagsNoCFOF(); \
+ lf_var1d=load(op1); \
+ lf_var2b=op2; \
+ lf_resd=(lf_var1d << lf_var2b) | \
+ (lf_var1d >> (32-lf_var2b)); \
+ save(op1,lf_resd); \
+ SETFLAGBIT(CF,lf_resd & 1); \
+ SETFLAGBIT(OF,(lf_resd & 1) ^ (lf_resd >> 31));
+
+
+#define RORB(op1,op2,load,save) \
+ FillFlagsNoCFOF(); \
+ lf_var1b=load(op1); \
+ lf_var2b=op2&0x07; \
+ lf_resb=(lf_var1b >> lf_var2b) | \
+ (lf_var1b << (8-lf_var2b)); \
+ save(op1,lf_resb); \
+ SETFLAGBIT(CF,lf_resb & 0x80); \
+ SETFLAGBIT(OF,(lf_resb ^ (lf_resb<<1)) & 0x80);
+
+#define RORW(op1,op2,load,save) \
+ FillFlagsNoCFOF(); \
+ lf_var1w=load(op1); \
+ lf_var2b=op2&0xf; \
+ lf_resw=(lf_var1w >> lf_var2b) | \
+ (lf_var1w << (16-lf_var2b)); \
+ save(op1,lf_resw); \
+ SETFLAGBIT(CF,lf_resw & 0x8000); \
+ SETFLAGBIT(OF,(lf_resw ^ (lf_resw<<1)) & 0x8000);
+
+#define RORD(op1,op2,load,save) \
+ FillFlagsNoCFOF(); \
+ lf_var1d=load(op1); \
+ lf_var2b=op2; \
+ lf_resd=(lf_var1d >> lf_var2b) | \
+ (lf_var1d << (32-lf_var2b)); \
+ save(op1,lf_resd); \
+ SETFLAGBIT(CF,lf_resd & 0x80000000); \
+ SETFLAGBIT(OF,(lf_resd ^ (lf_resd<<1)) & 0x80000000);
+
+
+#define RCLB(op1,op2,load,save) \
+ if (!(op2%9)) break; \
+{ Bit8u cf=(Bit8u)FillFlags()&0x1; \
+ lf_var1b=load(op1); \
+ lf_var2b=op2%9; \
+ lf_resb=(lf_var1b << lf_var2b) | \
+ (cf << (lf_var2b-1)) | \
+ (lf_var1b >> (9-lf_var2b)); \
+ save(op1,lf_resb); \
+ SETFLAGBIT(CF,((lf_var1b >> (8-lf_var2b)) & 1)); \
+ SETFLAGBIT(OF,(reg_flags & 1) ^ (lf_resb >> 7)); \
+}
+
+#define RCLW(op1,op2,load,save) \
+ if (!(op2%17)) break; \
+{ Bit16u cf=(Bit16u)FillFlags()&0x1; \
+ lf_var1w=load(op1); \
+ lf_var2b=op2%17; \
+ lf_resw=(lf_var1w << lf_var2b) | \
+ (cf << (lf_var2b-1)) | \
+ (lf_var1w >> (17-lf_var2b)); \
+ save(op1,lf_resw); \
+ SETFLAGBIT(CF,((lf_var1w >> (16-lf_var2b)) & 1)); \
+ SETFLAGBIT(OF,(reg_flags & 1) ^ (lf_resw >> 15)); \
+}
+
+#define RCLD(op1,op2,load,save) \
+ if (!op2) break; \
+{ Bit32u cf=(Bit32u)FillFlags()&0x1; \
+ lf_var1d=load(op1); \
+ lf_var2b=op2; \
+ if (lf_var2b==1) { \
+ lf_resd=(lf_var1d << 1) | cf; \
+ } else { \
+ lf_resd=(lf_var1d << lf_var2b) | \
+ (cf << (lf_var2b-1)) | \
+ (lf_var1d >> (33-lf_var2b)); \
+ } \
+ save(op1,lf_resd); \
+ SETFLAGBIT(CF,((lf_var1d >> (32-lf_var2b)) & 1)); \
+ SETFLAGBIT(OF,(reg_flags & 1) ^ (lf_resd >> 31)); \
+}
+
+#define RCRB(op1,op2,load,save) \
+ if (op2%9) { \
+ Bit8u cf=(Bit8u)FillFlags()&0x1; \
+ lf_var1b=load(op1); \
+ lf_var2b=op2%9; \
+ lf_resb=(lf_var1b >> lf_var2b) | \
+ (cf << (8-lf_var2b)) | \
+ (lf_var1b << (9-lf_var2b)); \
+ save(op1,lf_resb); \
+ SETFLAGBIT(CF,(lf_var1b >> (lf_var2b - 1)) & 1); \
+ SETFLAGBIT(OF,(lf_resb ^ (lf_resb<<1)) & 0x80); \
+ }
+
+#define RCRW(op1,op2,load,save) \
+ if (op2%17) { \
+ Bit16u cf=(Bit16u)FillFlags()&0x1; \
+ lf_var1w=load(op1); \
+ lf_var2b=op2%17; \
+ lf_resw=(lf_var1w >> lf_var2b) | \
+ (cf << (16-lf_var2b)) | \
+ (lf_var1w << (17-lf_var2b)); \
+ save(op1,lf_resw); \
+ SETFLAGBIT(CF,(lf_var1w >> (lf_var2b - 1)) & 1); \
+ SETFLAGBIT(OF,(lf_resw ^ (lf_resw<<1)) & 0x8000); \
+ }
+
+#define RCRD(op1,op2,load,save) \
+ if (op2) { \
+ Bit32u cf=(Bit32u)FillFlags()&0x1; \
+ lf_var1d=load(op1); \
+ lf_var2b=op2; \
+ if (lf_var2b==1) { \
+ lf_resd=lf_var1d >> 1 | cf << 31; \
+ } else { \
+ lf_resd=(lf_var1d >> lf_var2b) | \
+ (cf << (32-lf_var2b)) | \
+ (lf_var1d << (33-lf_var2b)); \
+ } \
+ save(op1,lf_resd); \
+ SETFLAGBIT(CF,(lf_var1d >> (lf_var2b - 1)) & 1); \
+ SETFLAGBIT(OF,(lf_resd ^ (lf_resd<<1)) & 0x80000000); \
+ }
+
+
+#define SHLB(op1,op2,load,save) \
+ lf_var1b=load(op1);lf_var2b=op2; \
+ lf_resb=lf_var1b << lf_var2b; \
+ save(op1,lf_resb); \
+ lflags.type=t_SHLb;
+
+#define SHLW(op1,op2,load,save) \
+ lf_var1w=load(op1);lf_var2b=op2 ; \
+ lf_resw=lf_var1w << lf_var2b; \
+ save(op1,lf_resw); \
+ lflags.type=t_SHLw;
+
+#define SHLD(op1,op2,load,save) \
+ lf_var1d=load(op1);lf_var2b=op2; \
+ lf_resd=lf_var1d << lf_var2b; \
+ save(op1,lf_resd); \
+ lflags.type=t_SHLd;
+
+
+#define SHRB(op1,op2,load,save) \
+ lf_var1b=load(op1);lf_var2b=op2; \
+ lf_resb=lf_var1b >> lf_var2b; \
+ save(op1,lf_resb); \
+ lflags.type=t_SHRb;
+
+#define SHRW(op1,op2,load,save) \
+ lf_var1w=load(op1);lf_var2b=op2; \
+ lf_resw=lf_var1w >> lf_var2b; \
+ save(op1,lf_resw); \
+ lflags.type=t_SHRw;
+
+#define SHRD(op1,op2,load,save) \
+ lf_var1d=load(op1);lf_var2b=op2; \
+ lf_resd=lf_var1d >> lf_var2b; \
+ save(op1,lf_resd); \
+ lflags.type=t_SHRd;
+
+
+#define SARB(op1,op2,load,save) \
+ lf_var1b=load(op1);lf_var2b=op2; \
+ if (lf_var2b>8) lf_var2b=8; \
+ if (lf_var1b & 0x80) { \
+ lf_resb=(lf_var1b >> lf_var2b)| \
+ (0xff << (8 - lf_var2b)); \
+ } else { \
+ lf_resb=lf_var1b >> lf_var2b; \
+ } \
+ save(op1,lf_resb); \
+ lflags.type=t_SARb;
+
+#define SARW(op1,op2,load,save) \
+ lf_var1w=load(op1);lf_var2b=op2; \
+ if (lf_var2b>16) lf_var2b=16; \
+ if (lf_var1w & 0x8000) { \
+ lf_resw=(lf_var1w >> lf_var2b)| \
+ (0xffff << (16 - lf_var2b)); \
+ } else { \
+ lf_resw=lf_var1w >> lf_var2b; \
+ } \
+ save(op1,lf_resw); \
+ lflags.type=t_SARw;
+
+#define SARD(op1,op2,load,save) \
+ lf_var2b=op2;lf_var1d=load(op1); \
+ if (lf_var1d & 0x80000000) { \
+ lf_resd=(lf_var1d >> lf_var2b)| \
+ (0xffffffff << (32 - lf_var2b)); \
+ } else { \
+ lf_resd=lf_var1d >> lf_var2b; \
+ } \
+ save(op1,lf_resd); \
+ lflags.type=t_SARd;
+
+
+
+#define DAA() \
+ if (((reg_al & 0x0F)>0x09) || get_AF()) { \
+ if ((reg_al > 0x99) || get_CF()) { \
+ reg_al+=0x60; \
+ SETFLAGBIT(CF,true); \
+ } else { \
+ SETFLAGBIT(CF,false); \
+ } \
+ reg_al+=0x06; \
+ SETFLAGBIT(AF,true); \
+ } else { \
+ if ((reg_al > 0x99) || get_CF()) { \
+ reg_al+=0x60; \
+ SETFLAGBIT(CF,true); \
+ } else { \
+ SETFLAGBIT(CF,false); \
+ } \
+ SETFLAGBIT(AF,false); \
+ } \
+ SETFLAGBIT(SF,(reg_al&0x80)); \
+ SETFLAGBIT(ZF,(reg_al==0)); \
+ SETFLAGBIT(PF,parity_lookup[reg_al]); \
+ lflags.type=t_UNKNOWN;
+
+
+#define DAS() \
+{ \
+ Bit8u osigned=reg_al & 0x80; \
+ if (((reg_al & 0x0f) > 9) || get_AF()) { \
+ if ((reg_al>0x99) || get_CF()) { \
+ reg_al-=0x60; \
+ SETFLAGBIT(CF,true); \
+ } else { \
+ SETFLAGBIT(CF,(reg_al<=0x05)); \
+ } \
+ reg_al-=6; \
+ SETFLAGBIT(AF,true); \
+ } else { \
+ if ((reg_al>0x99) || get_CF()) { \
+ reg_al-=0x60; \
+ SETFLAGBIT(CF,true); \
+ } else { \
+ SETFLAGBIT(CF,false); \
+ } \
+ SETFLAGBIT(AF,false); \
+ } \
+ SETFLAGBIT(OF,osigned && ((reg_al&0x80)==0)); \
+ SETFLAGBIT(SF,(reg_al&0x80)); \
+ SETFLAGBIT(ZF,(reg_al==0)); \
+ SETFLAGBIT(PF,parity_lookup[reg_al]); \
+ lflags.type=t_UNKNOWN; \
+}
+
+
+#define AAA() \
+ SETFLAGBIT(SF,((reg_al>=0x7a) && (reg_al<=0xf9))); \
+ if ((reg_al & 0xf) > 9) { \
+ SETFLAGBIT(OF,(reg_al&0xf0)==0x70); \
+ reg_ax += 0x106; \
+ SETFLAGBIT(CF,true); \
+ SETFLAGBIT(ZF,(reg_al == 0)); \
+ SETFLAGBIT(AF,true); \
+ } else if (get_AF()) { \
+ reg_ax += 0x106; \
+ SETFLAGBIT(OF,false); \
+ SETFLAGBIT(CF,true); \
+ SETFLAGBIT(ZF,false); \
+ SETFLAGBIT(AF,true); \
+ } else { \
+ SETFLAGBIT(OF,false); \
+ SETFLAGBIT(CF,false); \
+ SETFLAGBIT(ZF,(reg_al == 0)); \
+ SETFLAGBIT(AF,false); \
+ } \
+ SETFLAGBIT(PF,parity_lookup[reg_al]); \
+ reg_al &= 0x0F; \
+ lflags.type=t_UNKNOWN;
+
+#define AAS() \
+ if ((reg_al & 0x0f)>9) { \
+ SETFLAGBIT(SF,(reg_al>0x85)); \
+ reg_ax -= 0x106; \
+ SETFLAGBIT(OF,false); \
+ SETFLAGBIT(CF,true); \
+ SETFLAGBIT(AF,true); \
+ } else if (get_AF()) { \
+ SETFLAGBIT(OF,((reg_al>=0x80) && (reg_al<=0x85))); \
+ SETFLAGBIT(SF,(reg_al<0x06) || (reg_al>0x85)); \
+ reg_ax -= 0x106; \
+ SETFLAGBIT(CF,true); \
+ SETFLAGBIT(AF,true); \
+ } else { \
+ SETFLAGBIT(SF,(reg_al>=0x80)); \
+ SETFLAGBIT(OF,false); \
+ SETFLAGBIT(CF,false); \
+ SETFLAGBIT(AF,false); \
+ } \
+ SETFLAGBIT(ZF,(reg_al == 0)); \
+ SETFLAGBIT(PF,parity_lookup[reg_al]); \
+ reg_al &= 0x0F; \
+ lflags.type=t_UNKNOWN;
+
+#define AAM(op1) \
+{ \
+ Bit8u dv=op1; \
+ if (dv!=0) { \
+ reg_ah=reg_al / dv; \
+ reg_al=reg_al % dv; \
+ SETFLAGBIT(SF,(reg_al & 0x80)); \
+ SETFLAGBIT(ZF,(reg_al == 0)); \
+ SETFLAGBIT(PF,parity_lookup[reg_al]); \
+ SETFLAGBIT(CF,false); \
+ SETFLAGBIT(OF,false); \
+ SETFLAGBIT(AF,false); \
+ lflags.type=t_UNKNOWN; \
+ } else EXCEPTION(0); \
+}
+
+
+//Took this from bochs, i seriously hate these weird bcd opcodes
+#define AAD(op1) \
+ { \
+ Bit16u ax1 = reg_ah * op1; \
+ Bit16u ax2 = ax1 + reg_al; \
+ reg_al = (Bit8u) ax2; \
+ reg_ah = 0; \
+ SETFLAGBIT(CF,false); \
+ SETFLAGBIT(OF,false); \
+ SETFLAGBIT(AF,false); \
+ SETFLAGBIT(SF,reg_al >= 0x80); \
+ SETFLAGBIT(ZF,reg_al == 0); \
+ SETFLAGBIT(PF,parity_lookup[reg_al]); \
+ lflags.type=t_UNKNOWN; \
+ }
+
+#define MULB(op1,load,save) \
+ reg_ax=reg_al*load(op1); \
+ FillFlagsNoCFOF(); \
+ SETFLAGBIT(ZF,reg_al == 0); \
+ if (reg_ax & 0xff00) { \
+ SETFLAGBIT(CF,true);SETFLAGBIT(OF,true); \
+ } else { \
+ SETFLAGBIT(CF,false);SETFLAGBIT(OF,false); \
+ }
+
+#define MULW(op1,load,save) \
+{ \
+ Bitu tempu=(Bitu)reg_ax*(Bitu)(load(op1)); \
+ reg_ax=(Bit16u)(tempu); \
+ reg_dx=(Bit16u)(tempu >> 16); \
+ FillFlagsNoCFOF(); \
+ SETFLAGBIT(ZF,reg_ax == 0); \
+ if (reg_dx) { \
+ SETFLAGBIT(CF,true);SETFLAGBIT(OF,true); \
+ } else { \
+ SETFLAGBIT(CF,false);SETFLAGBIT(OF,false); \
+ } \
+}
+
+#define MULD(op1,load,save) \
+{ \
+ Bit64u tempu=(Bit64u)reg_eax*(Bit64u)(load(op1)); \
+ reg_eax=(Bit32u)(tempu); \
+ reg_edx=(Bit32u)(tempu >> 32); \
+ FillFlagsNoCFOF(); \
+ SETFLAGBIT(ZF,reg_eax == 0); \
+ if (reg_edx) { \
+ SETFLAGBIT(CF,true);SETFLAGBIT(OF,true); \
+ } else { \
+ SETFLAGBIT(CF,false);SETFLAGBIT(OF,false); \
+ } \
+}
+
+#define DIVB(op1,load,save) \
+{ \
+ Bitu val=load(op1); \
+ if (val==0) EXCEPTION(0); \
+ Bitu quo=reg_ax / val; \
+ Bit8u rem=(Bit8u)(reg_ax % val); \
+ Bit8u quo8=(Bit8u)(quo&0xff); \
+ if (quo>0xff) EXCEPTION(0); \
+ reg_ah=rem; \
+ reg_al=quo8; \
+ SETFLAGBIT(OF,false); \
+}
+
+
+#define DIVW(op1,load,save) \
+{ \
+ Bitu val=load(op1); \
+ if (val==0) EXCEPTION(0); \
+ Bitu num=((Bit32u)reg_dx<<16)|reg_ax; \
+ Bitu quo=num/val; \
+ Bit16u rem=(Bit16u)(num % val); \
+ Bit16u quo16=(Bit16u)(quo&0xffff); \
+ if (quo!=(Bit32u)quo16) EXCEPTION(0); \
+ reg_dx=rem; \
+ reg_ax=quo16; \
+ SETFLAGBIT(OF,false); \
+}
+
+#define DIVD(op1,load,save) \
+{ \
+ Bitu val=load(op1); \
+ if (val==0) EXCEPTION(0); \
+ Bit64u num=(((Bit64u)reg_edx)<<32)|reg_eax; \
+ Bit64u quo=num/val; \
+ Bit32u rem=(Bit32u)(num % val); \
+ Bit32u quo32=(Bit32u)(quo&0xffffffff); \
+ if (quo!=(Bit64u)quo32) EXCEPTION(0); \
+ reg_edx=rem; \
+ reg_eax=quo32; \
+ SETFLAGBIT(OF,false); \
+}
+
+
+#define IDIVB(op1,load,save) \
+{ \
+ Bits val=(Bit8s)(load(op1)); \
+ if (val==0) EXCEPTION(0); \
+ Bits quo=((Bit16s)reg_ax) / val; \
+ Bit8s rem=(Bit8s)((Bit16s)reg_ax % val); \
+ Bit8s quo8s=(Bit8s)(quo&0xff); \
+ if (quo!=(Bit16s)quo8s) EXCEPTION(0); \
+ reg_ah=rem; \
+ reg_al=quo8s; \
+ SETFLAGBIT(OF,false); \
+}
+
+
+#define IDIVW(op1,load,save) \
+{ \
+ Bits val=(Bit16s)(load(op1)); \
+ if (val==0) EXCEPTION(0); \
+ Bits num=(Bit32s)((reg_dx<<16)|reg_ax); \
+ Bits quo=num/val; \
+ Bit16s rem=(Bit16s)(num % val); \
+ Bit16s quo16s=(Bit16s)quo; \
+ if (quo!=(Bit32s)quo16s) EXCEPTION(0); \
+ reg_dx=rem; \
+ reg_ax=quo16s; \
+ SETFLAGBIT(OF,false); \
+}
+
+#define IDIVD(op1,load,save) \
+{ \
+ Bits val=(Bit32s)(load(op1)); \
+ if (val==0) EXCEPTION(0); \
+ Bit64s num=(((Bit64u)reg_edx)<<32)|reg_eax; \
+ Bit64s quo=num/val; \
+ Bit32s rem=(Bit32s)(num % val); \
+ Bit32s quo32s=(Bit32s)(quo&0xffffffff); \
+ if (quo!=(Bit64s)quo32s) EXCEPTION(0); \
+ reg_edx=rem; \
+ reg_eax=quo32s; \
+ SETFLAGBIT(OF,false); \
+}
+
+#define IMULB(op1,load,save) \
+{ \
+ reg_ax=((Bit8s)reg_al) * ((Bit8s)(load(op1))); \
+ FillFlagsNoCFOF(); \
+ SETFLAGBIT(ZF,reg_al == 0); \
+ SETFLAGBIT(SF,reg_al & 0x80); \
+ if ((reg_ax & 0xff80)==0xff80 || \
+ (reg_ax & 0xff80)==0x0000) { \
+ SETFLAGBIT(CF,false);SETFLAGBIT(OF,false); \
+ } else { \
+ SETFLAGBIT(CF,true);SETFLAGBIT(OF,true); \
+ } \
+}
+
+
+#define IMULW(op1,load,save) \
+{ \
+ Bits temps=((Bit16s)reg_ax)*((Bit16s)(load(op1))); \
+ reg_ax=(Bit16s)(temps); \
+ reg_dx=(Bit16s)(temps >> 16); \
+ FillFlagsNoCFOF(); \
+ SETFLAGBIT(ZF,reg_ax == 0); \
+ SETFLAGBIT(SF,reg_ax & 0x8000); \
+ if (((temps & 0xffff8000)==0xffff8000 || \
+ (temps & 0xffff8000)==0x0000)) { \
+ SETFLAGBIT(CF,false);SETFLAGBIT(OF,false); \
+ } else { \
+ SETFLAGBIT(CF,true);SETFLAGBIT(OF,true); \
+ } \
+}
+
+#define IMULD(op1,load,save) \
+{ \
+ Bit64s temps=((Bit64s)((Bit32s)reg_eax))* \
+ ((Bit64s)((Bit32s)(load(op1)))); \
+ reg_eax=(Bit32u)(temps); \
+ reg_edx=(Bit32u)(temps >> 32); \
+ FillFlagsNoCFOF(); \
+ SETFLAGBIT(ZF,reg_eax == 0); \
+ SETFLAGBIT(SF,reg_eax & 0x80000000); \
+ if ((reg_edx==0xffffffff) && \
+ (reg_eax & 0x80000000) ) { \
+ SETFLAGBIT(CF,false);SETFLAGBIT(OF,false); \
+ } else if ( (reg_edx==0x00000000) && \
+ (reg_eax< 0x80000000) ) { \
+ SETFLAGBIT(CF,false);SETFLAGBIT(OF,false); \
+ } else { \
+ SETFLAGBIT(CF,true);SETFLAGBIT(OF,true); \
+ } \
+}
+
+#define DIMULW(op1,op2,op3,load,save) \
+{ \
+ Bits res=((Bit16s)op2) * ((Bit16s)op3); \
+ save(op1,res & 0xffff); \
+ FillFlagsNoCFOF(); \
+ if ((res>= -32768) && (res<=32767)) { \
+ SETFLAGBIT(CF,false);SETFLAGBIT(OF,false); \
+ } else { \
+ SETFLAGBIT(CF,true);SETFLAGBIT(OF,true); \
+ } \
+}
+
+#define DIMULD(op1,op2,op3,load,save) \
+{ \
+ Bit64s res=((Bit64s)((Bit32s)op2))*((Bit64s)((Bit32s)op3)); \
+ save(op1,(Bit32s)res); \
+ FillFlagsNoCFOF(); \
+ if ((res>=-((Bit64s)(2147483647)+1)) && \
+ (res<=(Bit64s)2147483647)) { \
+ SETFLAGBIT(CF,false);SETFLAGBIT(OF,false); \
+ } else { \
+ SETFLAGBIT(CF,true);SETFLAGBIT(OF,true); \
+ } \
+}
+
+#define GRP2B(blah) \
+{ \
+ GetRM;Bitu which=(rm>>3)&7; \
+ if (rm >= 0xc0) { \
+ GetEArb; \
+ Bit8u val=blah & 0x1f; \
+ if (!val) break; \
+ switch (which) { \
+ case 0x00:ROLB(*earb,val,LoadRb,SaveRb);break; \
+ case 0x01:RORB(*earb,val,LoadRb,SaveRb);break; \
+ case 0x02:RCLB(*earb,val,LoadRb,SaveRb);break; \
+ case 0x03:RCRB(*earb,val,LoadRb,SaveRb);break; \
+ case 0x04:/* SHL and SAL are the same */ \
+ case 0x06:SHLB(*earb,val,LoadRb,SaveRb);break; \
+ case 0x05:SHRB(*earb,val,LoadRb,SaveRb);break; \
+ case 0x07:SARB(*earb,val,LoadRb,SaveRb);break; \
+ } \
+ } else { \
+ GetEAa; \
+ Bit8u val=blah & 0x1f; \
+ if (!val) break; \
+ switch (which) { \
+ case 0x00:ROLB(eaa,val,LoadMb,SaveMb);break; \
+ case 0x01:RORB(eaa,val,LoadMb,SaveMb);break; \
+ case 0x02:RCLB(eaa,val,LoadMb,SaveMb);break; \
+ case 0x03:RCRB(eaa,val,LoadMb,SaveMb);break; \
+ case 0x04:/* SHL and SAL are the same */ \
+ case 0x06:SHLB(eaa,val,LoadMb,SaveMb);break; \
+ case 0x05:SHRB(eaa,val,LoadMb,SaveMb);break; \
+ case 0x07:SARB(eaa,val,LoadMb,SaveMb);break; \
+ } \
+ } \
+}
+
+
+
+#define GRP2W(blah) \
+{ \
+ GetRM;Bitu which=(rm>>3)&7; \
+ if (rm >= 0xc0) { \
+ GetEArw; \
+ Bit8u val=blah & 0x1f; \
+ if (!val) break; \
+ switch (which) { \
+ case 0x00:ROLW(*earw,val,LoadRw,SaveRw);break; \
+ case 0x01:RORW(*earw,val,LoadRw,SaveRw);break; \
+ case 0x02:RCLW(*earw,val,LoadRw,SaveRw);break; \
+ case 0x03:RCRW(*earw,val,LoadRw,SaveRw);break; \
+ case 0x04:/* SHL and SAL are the same */ \
+ case 0x06:SHLW(*earw,val,LoadRw,SaveRw);break; \
+ case 0x05:SHRW(*earw,val,LoadRw,SaveRw);break; \
+ case 0x07:SARW(*earw,val,LoadRw,SaveRw);break; \
+ } \
+ } else { \
+ GetEAa; \
+ Bit8u val=blah & 0x1f; \
+ if (!val) break; \
+ switch (which) { \
+ case 0x00:ROLW(eaa,val,LoadMw,SaveMw);break; \
+ case 0x01:RORW(eaa,val,LoadMw,SaveMw);break; \
+ case 0x02:RCLW(eaa,val,LoadMw,SaveMw);break; \
+ case 0x03:RCRW(eaa,val,LoadMw,SaveMw);break; \
+ case 0x04:/* SHL and SAL are the same */ \
+ case 0x06:SHLW(eaa,val,LoadMw,SaveMw);break; \
+ case 0x05:SHRW(eaa,val,LoadMw,SaveMw);break; \
+ case 0x07:SARW(eaa,val,LoadMw,SaveMw);break; \
+ } \
+ } \
+}
+
+
+#define GRP2D(blah) \
+{ \
+ GetRM;Bitu which=(rm>>3)&7; \
+ if (rm >= 0xc0) { \
+ GetEArd; \
+ Bit8u val=blah & 0x1f; \
+ if (!val) break; \
+ switch (which) { \
+ case 0x00:ROLD(*eard,val,LoadRd,SaveRd);break; \
+ case 0x01:RORD(*eard,val,LoadRd,SaveRd);break; \
+ case 0x02:RCLD(*eard,val,LoadRd,SaveRd);break; \
+ case 0x03:RCRD(*eard,val,LoadRd,SaveRd);break; \
+ case 0x04:/* SHL and SAL are the same */ \
+ case 0x06:SHLD(*eard,val,LoadRd,SaveRd);break; \
+ case 0x05:SHRD(*eard,val,LoadRd,SaveRd);break; \
+ case 0x07:SARD(*eard,val,LoadRd,SaveRd);break; \
+ } \
+ } else { \
+ GetEAa; \
+ Bit8u val=blah & 0x1f; \
+ if (!val) break; \
+ switch (which) { \
+ case 0x00:ROLD(eaa,val,LoadMd,SaveMd);break; \
+ case 0x01:RORD(eaa,val,LoadMd,SaveMd);break; \
+ case 0x02:RCLD(eaa,val,LoadMd,SaveMd);break; \
+ case 0x03:RCRD(eaa,val,LoadMd,SaveMd);break; \
+ case 0x04:/* SHL and SAL are the same */ \
+ case 0x06:SHLD(eaa,val,LoadMd,SaveMd);break; \
+ case 0x05:SHRD(eaa,val,LoadMd,SaveMd);break; \
+ case 0x07:SARD(eaa,val,LoadMd,SaveMd);break; \
+ } \
+ } \
+}
+
+/* let's hope bochs has it correct with the higher than 16 shifts */
+/* double-precision shift left has low bits in second argument */
+#define DSHLW(op1,op2,op3,load,save) \
+ Bit8u val=op3 & 0x1F; \
+ if (!val) break; \
+ lf_var2b=val;lf_var1d=(load(op1)<<16)|op2; \
+ Bit32u tempd=lf_var1d << lf_var2b; \
+ if (lf_var2b>16) tempd |= (op2 << (lf_var2b - 16)); \
+ lf_resw=(Bit16u)(tempd >> 16); \
+ save(op1,lf_resw); \
+ lflags.type=t_DSHLw;
+
+#define DSHLD(op1,op2,op3,load,save) \
+ Bit8u val=op3 & 0x1F; \
+ if (!val) break; \
+ lf_var2b=val;lf_var1d=load(op1); \
+ lf_resd=(lf_var1d << lf_var2b) | (op2 >> (32-lf_var2b)); \
+ save(op1,lf_resd); \
+ lflags.type=t_DSHLd;
+
+/* double-precision shift right has high bits in second argument */
+#define DSHRW(op1,op2,op3,load,save) \
+ Bit8u val=op3 & 0x1F; \
+ if (!val) break; \
+ lf_var2b=val;lf_var1d=(op2<<16)|load(op1); \
+ Bit32u tempd=lf_var1d >> lf_var2b; \
+ if (lf_var2b>16) tempd |= (op2 << (32-lf_var2b )); \
+ lf_resw=(Bit16u)(tempd); \
+ save(op1,lf_resw); \
+ lflags.type=t_DSHRw;
+
+#define DSHRD(op1,op2,op3,load,save) \
+ Bit8u val=op3 & 0x1F; \
+ if (!val) break; \
+ lf_var2b=val;lf_var1d=load(op1); \
+ lf_resd=(lf_var1d >> lf_var2b) | (op2 << (32-lf_var2b)); \
+ save(op1,lf_resd); \
+ lflags.type=t_DSHRd;
+
+#define BSWAPW(op1) \
+ op1 = 0;
+
+#define BSWAPD(op1) \
+ op1 = (op1>>24)|((op1>>8)&0xFF00)|((op1<<8)&0xFF0000)|((op1<<24)&0xFF000000);
diff --git a/src/cpu/lazyflags.h b/src/cpu/lazyflags.h
new file mode 100644
index 000000000..c110681f6
--- /dev/null
+++ b/src/cpu/lazyflags.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_LAZYFLAGS_H
+#define DOSBOX_LAZYFLAGS_H
+
+//Flag Handling
+Bit32u get_CF(void);
+Bit32u get_AF(void);
+Bit32u get_ZF(void);
+Bit32u get_SF(void);
+Bit32u get_OF(void);
+Bit32u get_PF(void);
+
+Bitu FillFlags(void);
+void FillFlagsNoCFOF(void);
+void DestroyConditionFlags(void);
+
+#ifndef DOSBOX_REGS_H
+#include "regs.h"
+#endif
+
+struct LazyFlags {
+ GenReg32 var1,var2,res;
+ Bitu type;
+ Bitu prev_type;
+ Bitu oldcf;
+};
+
+extern LazyFlags lfags;
+
+#define lf_var1b lflags.var1.byte[BL_INDEX]
+#define lf_var2b lflags.var2.byte[BL_INDEX]
+#define lf_resb lflags.res.byte[BL_INDEX]
+
+#define lf_var1w lflags.var1.word[W_INDEX]
+#define lf_var2w lflags.var2.word[W_INDEX]
+#define lf_resw lflags.res.word[W_INDEX]
+
+#define lf_var1d lflags.var1.dword[DW_INDEX]
+#define lf_var2d lflags.var2.dword[DW_INDEX]
+#define lf_resd lflags.res.dword[DW_INDEX]
+
+
+extern LazyFlags lflags;
+
+#define SETFLAGSb(FLAGB) \
+{ \
+ SETFLAGBIT(OF,get_OF()); \
+ lflags.type=t_UNKNOWN; \
+ CPU_SetFlags(FLAGB,FMASK_NORMAL & 0xff); \
+}
+
+#define SETFLAGSw(FLAGW) \
+{ \
+ lflags.type=t_UNKNOWN; \
+ CPU_SetFlagsw(FLAGW); \
+}
+
+#define SETFLAGSd(FLAGD) \
+{ \
+ lflags.type=t_UNKNOWN; \
+ CPU_SetFlagsd(FLAGD); \
+}
+
+#define LoadCF SETFLAGBIT(CF,get_CF());
+#define LoadZF SETFLAGBIT(ZF,get_ZF());
+#define LoadSF SETFLAGBIT(SF,get_SF());
+#define LoadOF SETFLAGBIT(OF,get_OF());
+#define LoadAF SETFLAGBIT(AF,get_AF());
+
+#define TFLG_O (get_OF())
+#define TFLG_NO (!get_OF())
+#define TFLG_B (get_CF())
+#define TFLG_NB (!get_CF())
+#define TFLG_Z (get_ZF())
+#define TFLG_NZ (!get_ZF())
+#define TFLG_BE (get_CF() || get_ZF())
+#define TFLG_NBE (!get_CF() && !get_ZF())
+#define TFLG_S (get_SF())
+#define TFLG_NS (!get_SF())
+#define TFLG_P (get_PF())
+#define TFLG_NP (!get_PF())
+#define TFLG_L ((get_SF()!=0) != (get_OF()!=0))
+#define TFLG_NL ((get_SF()!=0) == (get_OF()!=0))
+#define TFLG_LE (get_ZF() || ((get_SF()!=0) != (get_OF()!=0)))
+#define TFLG_NLE (!get_ZF() && ((get_SF()!=0) == (get_OF()!=0)))
+
+//Types of Flag changing instructions
+enum {
+ t_UNKNOWN=0,
+ t_ADDb,t_ADDw,t_ADDd,
+ t_ORb,t_ORw,t_ORd,
+ t_ADCb,t_ADCw,t_ADCd,
+ t_SBBb,t_SBBw,t_SBBd,
+ t_ANDb,t_ANDw,t_ANDd,
+ t_SUBb,t_SUBw,t_SUBd,
+ t_XORb,t_XORw,t_XORd,
+ t_CMPb,t_CMPw,t_CMPd,
+ t_INCb,t_INCw,t_INCd,
+ t_DECb,t_DECw,t_DECd,
+ t_TESTb,t_TESTw,t_TESTd,
+ t_SHLb,t_SHLw,t_SHLd,
+ t_SHRb,t_SHRw,t_SHRd,
+ t_SARb,t_SARw,t_SARd,
+ t_ROLb,t_ROLw,t_ROLd,
+ t_RORb,t_RORw,t_RORd,
+ t_RCLb,t_RCLw,t_RCLd,
+ t_RCRb,t_RCRw,t_RCRd,
+ t_NEGb,t_NEGw,t_NEGd,
+
+ t_DSHLw,t_DSHLd,
+ t_DSHRw,t_DSHRd,
+ t_MUL,t_DIV,
+ t_NOTDONE,
+ t_LASTFLAG
+};
+
+#endif
diff --git a/src/cpu/modrm.cpp b/src/cpu/modrm.cpp
new file mode 100644
index 000000000..79538bcad
--- /dev/null
+++ b/src/cpu/modrm.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include "cpu.h"
+
+
+Bit8u * lookupRMregb[]=
+{
+ &reg_al,&reg_al,&reg_al,&reg_al,&reg_al,&reg_al,&reg_al,&reg_al,
+ &reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,
+ &reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,
+ &reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,
+ &reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,
+ &reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,
+ &reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,
+ &reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,
+
+ &reg_al,&reg_al,&reg_al,&reg_al,&reg_al,&reg_al,&reg_al,&reg_al,
+ &reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,
+ &reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,
+ &reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,
+ &reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,
+ &reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,
+ &reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,
+ &reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,
+
+ &reg_al,&reg_al,&reg_al,&reg_al,&reg_al,&reg_al,&reg_al,&reg_al,
+ &reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,
+ &reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,
+ &reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,
+ &reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,
+ &reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,
+ &reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,
+ &reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,
+
+ &reg_al,&reg_al,&reg_al,&reg_al,&reg_al,&reg_al,&reg_al,&reg_al,
+ &reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,&reg_cl,
+ &reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,&reg_dl,
+ &reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,&reg_bl,
+ &reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,&reg_ah,
+ &reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,&reg_ch,
+ &reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,&reg_dh,
+ &reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh,&reg_bh
+};
+
+Bit16u * lookupRMregw[]={
+ &reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,
+ &reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,
+ &reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,
+ &reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,
+ &reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,
+ &reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,
+ &reg_si,&reg_si,&reg_si,&reg_si,&reg_si,&reg_si,&reg_si,&reg_si,
+ &reg_di,&reg_di,&reg_di,&reg_di,&reg_di,&reg_di,&reg_di,&reg_di,
+
+ &reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,
+ &reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,
+ &reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,
+ &reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,
+ &reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,
+ &reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,
+ &reg_si,&reg_si,&reg_si,&reg_si,&reg_si,&reg_si,&reg_si,&reg_si,
+ &reg_di,&reg_di,&reg_di,&reg_di,&reg_di,&reg_di,&reg_di,&reg_di,
+
+ &reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,
+ &reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,
+ &reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,
+ &reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,
+ &reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,
+ &reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,
+ &reg_si,&reg_si,&reg_si,&reg_si,&reg_si,&reg_si,&reg_si,&reg_si,
+ &reg_di,&reg_di,&reg_di,&reg_di,&reg_di,&reg_di,&reg_di,&reg_di,
+
+ &reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,&reg_ax,
+ &reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,&reg_cx,
+ &reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,&reg_dx,
+ &reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,&reg_bx,
+ &reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,&reg_sp,
+ &reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,&reg_bp,
+ &reg_si,&reg_si,&reg_si,&reg_si,&reg_si,&reg_si,&reg_si,&reg_si,
+ &reg_di,&reg_di,&reg_di,&reg_di,&reg_di,&reg_di,&reg_di,&reg_di
+};
+
+Bit32u * lookupRMregd[256]={
+ &reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,
+ &reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,
+ &reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,
+ &reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,
+ &reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,
+ &reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,
+ &reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,
+ &reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,
+
+ &reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,
+ &reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,
+ &reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,
+ &reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,
+ &reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,
+ &reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,
+ &reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,
+ &reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,
+
+ &reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,
+ &reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,
+ &reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,
+ &reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,
+ &reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,
+ &reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,
+ &reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,
+ &reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,
+
+ &reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,&reg_eax,
+ &reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,&reg_ecx,
+ &reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,&reg_edx,
+ &reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,&reg_ebx,
+ &reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,&reg_esp,
+ &reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,&reg_ebp,
+ &reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,&reg_esi,
+ &reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi,&reg_edi
+};
+
+
+Bit8u * lookupRMEAregb[256]={
+/* 12 lines of 16*0 should give nice errors when used */
+ 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,
+ 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,
+ &reg_al,&reg_cl,&reg_dl,&reg_bl,&reg_ah,&reg_ch,&reg_dh,&reg_bh,
+ &reg_al,&reg_cl,&reg_dl,&reg_bl,&reg_ah,&reg_ch,&reg_dh,&reg_bh,
+ &reg_al,&reg_cl,&reg_dl,&reg_bl,&reg_ah,&reg_ch,&reg_dh,&reg_bh,
+ &reg_al,&reg_cl,&reg_dl,&reg_bl,&reg_ah,&reg_ch,&reg_dh,&reg_bh,
+ &reg_al,&reg_cl,&reg_dl,&reg_bl,&reg_ah,&reg_ch,&reg_dh,&reg_bh,
+ &reg_al,&reg_cl,&reg_dl,&reg_bl,&reg_ah,&reg_ch,&reg_dh,&reg_bh,
+ &reg_al,&reg_cl,&reg_dl,&reg_bl,&reg_ah,&reg_ch,&reg_dh,&reg_bh,
+ &reg_al,&reg_cl,&reg_dl,&reg_bl,&reg_ah,&reg_ch,&reg_dh,&reg_bh
+};
+
+Bit16u * lookupRMEAregw[256]={
+/* 12 lines of 16*0 should give nice errors when used */
+ 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,
+ 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,
+ &reg_ax,&reg_cx,&reg_dx,&reg_bx,&reg_sp,&reg_bp,&reg_si,&reg_di,
+ &reg_ax,&reg_cx,&reg_dx,&reg_bx,&reg_sp,&reg_bp,&reg_si,&reg_di,
+ &reg_ax,&reg_cx,&reg_dx,&reg_bx,&reg_sp,&reg_bp,&reg_si,&reg_di,
+ &reg_ax,&reg_cx,&reg_dx,&reg_bx,&reg_sp,&reg_bp,&reg_si,&reg_di,
+ &reg_ax,&reg_cx,&reg_dx,&reg_bx,&reg_sp,&reg_bp,&reg_si,&reg_di,
+ &reg_ax,&reg_cx,&reg_dx,&reg_bx,&reg_sp,&reg_bp,&reg_si,&reg_di,
+ &reg_ax,&reg_cx,&reg_dx,&reg_bx,&reg_sp,&reg_bp,&reg_si,&reg_di,
+ &reg_ax,&reg_cx,&reg_dx,&reg_bx,&reg_sp,&reg_bp,&reg_si,&reg_di
+};
+
+Bit32u * lookupRMEAregd[256]={
+/* 12 lines of 16*0 should give nice errors when used */
+ 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,
+ 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,
+ &reg_eax,&reg_ecx,&reg_edx,&reg_ebx,&reg_esp,&reg_ebp,&reg_esi,&reg_edi,
+ &reg_eax,&reg_ecx,&reg_edx,&reg_ebx,&reg_esp,&reg_ebp,&reg_esi,&reg_edi,
+ &reg_eax,&reg_ecx,&reg_edx,&reg_ebx,&reg_esp,&reg_ebp,&reg_esi,&reg_edi,
+ &reg_eax,&reg_ecx,&reg_edx,&reg_ebx,&reg_esp,&reg_ebp,&reg_esi,&reg_edi,
+ &reg_eax,&reg_ecx,&reg_edx,&reg_ebx,&reg_esp,&reg_ebp,&reg_esi,&reg_edi,
+ &reg_eax,&reg_ecx,&reg_edx,&reg_ebx,&reg_esp,&reg_ebp,&reg_esi,&reg_edi,
+ &reg_eax,&reg_ecx,&reg_edx,&reg_ebx,&reg_esp,&reg_ebp,&reg_esi,&reg_edi,
+ &reg_eax,&reg_ecx,&reg_edx,&reg_ebx,&reg_esp,&reg_ebp,&reg_esi,&reg_edi
+};
+
+
diff --git a/src/cpu/modrm.h b/src/cpu/modrm.h
new file mode 100644
index 000000000..f6d1a2a56
--- /dev/null
+++ b/src/cpu/modrm.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+extern Bit8u * lookupRMregb[];
+extern Bit16u * lookupRMregw[];
+extern Bit32u * lookupRMregd[];
+extern Bit8u * lookupRMEAregb[256];
+extern Bit16u * lookupRMEAregw[256];
+extern Bit32u * lookupRMEAregd[256];
+
+#define GetRM \
+ Bit8u rm=Fetchb();
+
+#define Getrb \
+ Bit8u * rmrb; \
+ rmrb=lookupRMregb[rm];
+
+#define Getrw \
+ Bit16u * rmrw; \
+ rmrw=lookupRMregw[rm];
+
+#define Getrd \
+ Bit32u * rmrd; \
+ rmrd=lookupRMregd[rm];
+
+
+#define GetRMrb \
+ GetRM; \
+ Getrb;
+
+#define GetRMrw \
+ GetRM; \
+ Getrw;
+
+#define GetRMrd \
+ GetRM; \
+ Getrd;
+
+
+#define GetEArb \
+ Bit8u * earb=lookupRMEAregb[rm];
+
+#define GetEArw \
+ Bit16u * earw=lookupRMEAregw[rm];
+
+#define GetEArd \
+ Bit32u * eard=lookupRMEAregd[rm];
+
+
diff --git a/src/cpu/paging.cpp b/src/cpu/paging.cpp
new file mode 100644
index 000000000..b91cdfaf0
--- /dev/null
+++ b/src/cpu/paging.cpp
@@ -0,0 +1,890 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "dosbox.h"
+#include "mem.h"
+#include "paging.h"
+#include "regs.h"
+#include "lazyflags.h"
+#include "cpu.h"
+#include "debug.h"
+#include "setup.h"
+
+#define LINK_TOTAL (64*1024)
+
+#define USERWRITE_PROHIBITED ((cpu.cpl&cpu.mpl)==3)
+
+
+PagingBlock paging;
+
+
+Bitu PageHandler::readb(PhysPt addr) {
+ E_Exit("No byte handler for read from %d",addr);
+ return 0;
+}
+Bitu PageHandler::readw(PhysPt addr) {
+ Bitu ret = (readb(addr+0) << 0);
+ ret |= (readb(addr+1) << 8);
+ return ret;
+}
+Bitu PageHandler::readd(PhysPt addr) {
+ Bitu ret = (readb(addr+0) << 0);
+ ret |= (readb(addr+1) << 8);
+ ret |= (readb(addr+2) << 16);
+ ret |= (readb(addr+3) << 24);
+ return ret;
+}
+
+void PageHandler::writeb(PhysPt addr,Bitu /*val*/) {
+ E_Exit("No byte handler for write to %d",addr);
+}
+
+void PageHandler::writew(PhysPt addr,Bitu val) {
+ writeb(addr+0,(Bit8u) (val >> 0));
+ writeb(addr+1,(Bit8u) (val >> 8));
+}
+void PageHandler::writed(PhysPt addr,Bitu val) {
+ writeb(addr+0,(Bit8u) (val >> 0));
+ writeb(addr+1,(Bit8u) (val >> 8));
+ writeb(addr+2,(Bit8u) (val >> 16));
+ writeb(addr+3,(Bit8u) (val >> 24));
+}
+
+HostPt PageHandler::GetHostReadPt(Bitu /*phys_page*/) {
+ return 0;
+}
+
+HostPt PageHandler::GetHostWritePt(Bitu /*phys_page*/) {
+ return 0;
+}
+
+bool PageHandler::readb_checked(PhysPt addr, Bit8u * val) {
+ *val=(Bit8u)readb(addr); return false;
+}
+bool PageHandler::readw_checked(PhysPt addr, Bit16u * val) {
+ *val=(Bit16u)readw(addr); return false;
+}
+bool PageHandler::readd_checked(PhysPt addr, Bit32u * val) {
+ *val=(Bit32u)readd(addr); return false;
+}
+bool PageHandler::writeb_checked(PhysPt addr,Bitu val) {
+ writeb(addr,val); return false;
+}
+bool PageHandler::writew_checked(PhysPt addr,Bitu val) {
+ writew(addr,val); return false;
+}
+bool PageHandler::writed_checked(PhysPt addr,Bitu val) {
+ writed(addr,val); return false;
+}
+
+
+
+struct PF_Entry {
+ Bitu cs;
+ Bitu eip;
+ Bitu page_addr;
+ Bitu mpl;
+};
+
+#define PF_QUEUESIZE 16
+static struct {
+ Bitu used;
+ PF_Entry entries[PF_QUEUESIZE];
+} pf_queue;
+
+static Bits PageFaultCore(void) {
+ CPU_CycleLeft+=CPU_Cycles;
+ CPU_Cycles=1;
+ Bits ret=CPU_Core_Full_Run();
+ CPU_CycleLeft+=CPU_Cycles;
+ if (ret<0) E_Exit("Got a dosbox close machine in pagefault core?");
+ if (ret)
+ return ret;
+ if (!pf_queue.used) E_Exit("PF Core without PF");
+ PF_Entry * entry=&pf_queue.entries[pf_queue.used-1];
+ X86PageEntry pentry;
+ pentry.load=phys_readd(entry->page_addr);
+ if (pentry.block.p && entry->cs == SegValue(cs) && entry->eip==reg_eip) {
+ cpu.mpl=entry->mpl;
+ return -1;
+ }
+ return 0;
+}
+#if C_DEBUG
+Bitu DEBUG_EnableDebugger(void);
+#endif
+
+bool first=false;
+
+void PAGING_PageFault(PhysPt lin_addr,Bitu page_addr,Bitu faultcode) {
+ /* Save the state of the cpu cores */
+ LazyFlags old_lflags;
+ memcpy(&old_lflags,&lflags,sizeof(LazyFlags));
+ CPU_Decoder * old_cpudecoder;
+ old_cpudecoder=cpudecoder;
+ cpudecoder=&PageFaultCore;
+ paging.cr2=lin_addr;
+ PF_Entry * entry=&pf_queue.entries[pf_queue.used++];
+ LOG(LOG_PAGING,LOG_NORMAL)("PageFault at %X type [%x] queue %d",lin_addr,faultcode,pf_queue.used);
+// LOG_MSG("EAX:%04X ECX:%04X EDX:%04X EBX:%04X",reg_eax,reg_ecx,reg_edx,reg_ebx);
+// LOG_MSG("CS:%04X EIP:%08X SS:%04x SP:%08X",SegValue(cs),reg_eip,SegValue(ss),reg_esp);
+ entry->cs=SegValue(cs);
+ entry->eip=reg_eip;
+ entry->page_addr=page_addr;
+ entry->mpl=cpu.mpl;
+ cpu.mpl=3;
+
+ CPU_Exception(EXCEPTION_PF,faultcode);
+#if C_DEBUG
+// DEBUG_EnableDebugger();
+#endif
+ DOSBOX_RunMachine();
+ pf_queue.used--;
+ LOG(LOG_PAGING,LOG_NORMAL)("Left PageFault for %x queue %d",lin_addr,pf_queue.used);
+ memcpy(&lflags,&old_lflags,sizeof(LazyFlags));
+ cpudecoder=old_cpudecoder;
+// LOG_MSG("SS:%04x SP:%08X",SegValue(ss),reg_esp);
+}
+
+static INLINE void InitPageUpdateLink(Bitu relink,PhysPt addr) {
+ if (relink==0) return;
+ if (paging.links.used) {
+ if (paging.links.entries[paging.links.used-1]==(addr>>12)) {
+ paging.links.used--;
+ PAGING_UnlinkPages(addr>>12,1);
+ }
+ }
+ if (relink>1) PAGING_LinkPage_ReadOnly(addr>>12,relink);
+}
+
+static INLINE void InitPageCheckPresence(PhysPt lin_addr,bool writing,X86PageEntry& table,X86PageEntry& entry) {
+ Bitu lin_page=lin_addr >> 12;
+ Bitu d_index=lin_page >> 10;
+ Bitu t_index=lin_page & 0x3ff;
+ Bitu table_addr=(paging.base.page<<12)+d_index*4;
+ table.load=phys_readd(table_addr);
+ if (!table.block.p) {
+ LOG(LOG_PAGING,LOG_NORMAL)("NP Table");
+ PAGING_PageFault(lin_addr,table_addr,
+ (writing?0x02:0x00) | (((cpu.cpl&cpu.mpl)==0)?0x00:0x04));
+ table.load=phys_readd(table_addr);
+ if (GCC_UNLIKELY(!table.block.p))
+ E_Exit("Pagefault didn't correct table");
+ }
+ Bitu entry_addr=(table.block.base<<12)+t_index*4;
+ entry.load=phys_readd(entry_addr);
+ if (!entry.block.p) {
+// LOG(LOG_PAGING,LOG_NORMAL)("NP Page");
+ PAGING_PageFault(lin_addr,entry_addr,
+ (writing?0x02:0x00) | (((cpu.cpl&cpu.mpl)==0)?0x00:0x04));
+ entry.load=phys_readd(entry_addr);
+ if (GCC_UNLIKELY(!entry.block.p))
+ E_Exit("Pagefault didn't correct page");
+ }
+}
+
+static INLINE bool InitPageCheckPresence_CheckOnly(PhysPt lin_addr,bool writing,X86PageEntry& table,X86PageEntry& entry) {
+ Bitu lin_page=lin_addr >> 12;
+ Bitu d_index=lin_page >> 10;
+ Bitu t_index=lin_page & 0x3ff;
+ Bitu table_addr=(paging.base.page<<12)+d_index*4;
+ table.load=phys_readd(table_addr);
+ if (!table.block.p) {
+ paging.cr2=lin_addr;
+ cpu.exception.which=EXCEPTION_PF;
+ cpu.exception.error=(writing?0x02:0x00) | (((cpu.cpl&cpu.mpl)==0)?0x00:0x04);
+ return false;
+ }
+ Bitu entry_addr=(table.block.base<<12)+t_index*4;
+ entry.load=phys_readd(entry_addr);
+ if (!entry.block.p) {
+ paging.cr2=lin_addr;
+ cpu.exception.which=EXCEPTION_PF;
+ cpu.exception.error=(writing?0x02:0x00) | (((cpu.cpl&cpu.mpl)==0)?0x00:0x04);
+ return false;
+ }
+ return true;
+}
+
+// check if a user-level memory access would trigger a privilege page fault
+static INLINE bool InitPage_CheckUseraccess(Bitu u1,Bitu u2) {
+ switch (CPU_ArchitectureType) {
+ case CPU_ARCHTYPE_MIXED:
+ case CPU_ARCHTYPE_386SLOW:
+ case CPU_ARCHTYPE_386FAST:
+ default:
+ return ((u1)==0) && ((u2)==0);
+ case CPU_ARCHTYPE_486OLDSLOW:
+ case CPU_ARCHTYPE_486NEWSLOW:
+ case CPU_ARCHTYPE_PENTIUMSLOW:
+ return ((u1)==0) || ((u2)==0);
+ }
+}
+
+
+class InitPageHandler : public PageHandler {
+public:
+ InitPageHandler() {
+ flags=PFLAG_INIT|PFLAG_NOCODE;
+ }
+ Bitu readb(PhysPt addr) {
+ Bitu needs_reset=InitPage(addr,false);
+ Bit8u val=mem_readb(addr);
+ InitPageUpdateLink(needs_reset,addr);
+ return val;
+ }
+ Bitu readw(PhysPt addr) {
+ Bitu needs_reset=InitPage(addr,false);
+ Bit16u val=mem_readw(addr);
+ InitPageUpdateLink(needs_reset,addr);
+ return val;
+ }
+ Bitu readd(PhysPt addr) {
+ Bitu needs_reset=InitPage(addr,false);
+ Bit32u val=mem_readd(addr);
+ InitPageUpdateLink(needs_reset,addr);
+ return val;
+ }
+ void writeb(PhysPt addr,Bitu val) {
+ Bitu needs_reset=InitPage(addr,true);
+ mem_writeb(addr,val);
+ InitPageUpdateLink(needs_reset,addr);
+ }
+ void writew(PhysPt addr,Bitu val) {
+ Bitu needs_reset=InitPage(addr,true);
+ mem_writew(addr,val);
+ InitPageUpdateLink(needs_reset,addr);
+ }
+ void writed(PhysPt addr,Bitu val) {
+ Bitu needs_reset=InitPage(addr,true);
+ mem_writed(addr,val);
+ InitPageUpdateLink(needs_reset,addr);
+ }
+ bool readb_checked(PhysPt addr, Bit8u * val) {
+ if (InitPageCheckOnly(addr,false)) {
+ *val=mem_readb(addr);
+ return false;
+ } else return true;
+ }
+ bool readw_checked(PhysPt addr, Bit16u * val) {
+ if (InitPageCheckOnly(addr,false)){
+ *val=mem_readw(addr);
+ return false;
+ } else return true;
+ }
+ bool readd_checked(PhysPt addr, Bit32u * val) {
+ if (InitPageCheckOnly(addr,false)) {
+ *val=mem_readd(addr);
+ return false;
+ } else return true;
+ }
+ bool writeb_checked(PhysPt addr,Bitu val) {
+ if (InitPageCheckOnly(addr,true)) {
+ mem_writeb(addr,val);
+ return false;
+ } else return true;
+ }
+ bool writew_checked(PhysPt addr,Bitu val) {
+ if (InitPageCheckOnly(addr,true)) {
+ mem_writew(addr,val);
+ return false;
+ } else return true;
+ }
+ bool writed_checked(PhysPt addr,Bitu val) {
+ if (InitPageCheckOnly(addr,true)) {
+ mem_writed(addr,val);
+ return false;
+ } else return true;
+ }
+ Bitu InitPage(Bitu lin_addr,bool writing) {
+ Bitu lin_page=lin_addr >> 12;
+ Bitu phys_page;
+ if (paging.enabled) {
+ X86PageEntry table;
+ X86PageEntry entry;
+ InitPageCheckPresence(lin_addr,writing,table,entry);
+
+ // 0: no action
+ // 1: can (but currently does not) fail a user-level access privilege check
+ // 2: can (but currently does not) fail a write privilege check
+ // 3: fails a privilege check
+ Bitu priv_check=0;
+ if (InitPage_CheckUseraccess(entry.block.us,table.block.us)) {
+ if ((cpu.cpl&cpu.mpl)==3) priv_check=3;
+ else {
+ switch (CPU_ArchitectureType) {
+ case CPU_ARCHTYPE_MIXED:
+ case CPU_ARCHTYPE_386FAST:
+ default:
+// priv_check=0; // default
+ break;
+ case CPU_ARCHTYPE_386SLOW:
+ case CPU_ARCHTYPE_486OLDSLOW:
+ case CPU_ARCHTYPE_486NEWSLOW:
+ case CPU_ARCHTYPE_PENTIUMSLOW:
+ priv_check=1;
+ break;
+ }
+ }
+ }
+ if ((entry.block.wr==0) || (table.block.wr==0)) {
+ // page is write-protected for user mode
+ if (priv_check==0) {
+ switch (CPU_ArchitectureType) {
+ case CPU_ARCHTYPE_MIXED:
+ case CPU_ARCHTYPE_386FAST:
+ default:
+// priv_check=0; // default
+ break;
+ case CPU_ARCHTYPE_386SLOW:
+ case CPU_ARCHTYPE_486OLDSLOW:
+ case CPU_ARCHTYPE_486NEWSLOW:
+ case CPU_ARCHTYPE_PENTIUMSLOW:
+ priv_check=2;
+ break;
+ }
+ }
+ // check if actually failing the write-protected check
+ if (writing && USERWRITE_PROHIBITED) priv_check=3;
+ }
+ if (priv_check==3) {
+ LOG(LOG_PAGING,LOG_NORMAL)("Page access denied: cpl=%i, %x:%x:%x:%x",
+ cpu.cpl,entry.block.us,table.block.us,entry.block.wr,table.block.wr);
+ PAGING_PageFault(lin_addr,(table.block.base<<12)+(lin_page & 0x3ff)*4,0x05 | (writing?0x02:0x00));
+ priv_check=0;
+ }
+
+ if (!table.block.a) {
+ table.block.a=1; // set page table accessed
+ phys_writed((paging.base.page<<12)+(lin_page >> 10)*4,table.load);
+ }
+ if ((!entry.block.a) || (!entry.block.d)) {
+ entry.block.a=1; // set page accessed
+
+ // page is dirty if we're writing to it, or if we're reading but the
+ // page will be fully linked so we can't track later writes
+ if (writing || (priv_check==0)) entry.block.d=1; // mark page as dirty
+
+ phys_writed((table.block.base<<12)+(lin_page & 0x3ff)*4,entry.load);
+ }
+
+ phys_page=entry.block.base;
+
+ // now see how the page should be linked best, if we need to catch privilege
+ // checks later on it should be linked as read-only page
+ if (priv_check==0) {
+ // if reading we could link the page as read-only to later cacth writes,
+ // will slow down pretty much but allows catching all dirty events
+ PAGING_LinkPage(lin_page,phys_page);
+ } else {
+ if (priv_check==1) {
+ PAGING_LinkPage(lin_page,phys_page);
+ return 1;
+ } else if (writing) {
+ PageHandler * handler=MEM_GetPageHandler(phys_page);
+ PAGING_LinkPage(lin_page,phys_page);
+ if (!(handler->flags & PFLAG_READABLE)) return 1;
+ if (!(handler->flags & PFLAG_WRITEABLE)) return 1;
+ if (get_tlb_read(lin_addr)!=get_tlb_write(lin_addr)) return 1;
+ if (phys_page>1) return phys_page;
+ else return 1;
+ } else {
+ PAGING_LinkPage_ReadOnly(lin_page,phys_page);
+ }
+ }
+ } else {
+ if (lin_page<LINK_START) phys_page=paging.firstmb[lin_page];
+ else phys_page=lin_page;
+ PAGING_LinkPage(lin_page,phys_page);
+ }
+ return 0;
+ }
+ bool InitPageCheckOnly(Bitu lin_addr,bool writing) {
+ Bitu lin_page=lin_addr >> 12;
+ if (paging.enabled) {
+ X86PageEntry table;
+ X86PageEntry entry;
+ if (!InitPageCheckPresence_CheckOnly(lin_addr,writing,table,entry)) return false;
+
+ if (!USERWRITE_PROHIBITED) return true;
+
+ if (InitPage_CheckUseraccess(entry.block.us,table.block.us) ||
+ (((entry.block.wr==0) || (table.block.wr==0)) && writing)) {
+ LOG(LOG_PAGING,LOG_NORMAL)("Page access denied: cpl=%i, %x:%x:%x:%x",
+ cpu.cpl,entry.block.us,table.block.us,entry.block.wr,table.block.wr);
+ paging.cr2=lin_addr;
+ cpu.exception.which=EXCEPTION_PF;
+ cpu.exception.error=0x05 | (writing?0x02:0x00);
+ return false;
+ }
+ } else {
+ Bitu phys_page;
+ if (lin_page<LINK_START) phys_page=paging.firstmb[lin_page];
+ else phys_page=lin_page;
+ PAGING_LinkPage(lin_page,phys_page);
+ }
+ return true;
+ }
+ void InitPageForced(Bitu lin_addr) {
+ Bitu lin_page=lin_addr >> 12;
+ Bitu phys_page;
+ if (paging.enabled) {
+ X86PageEntry table;
+ X86PageEntry entry;
+ InitPageCheckPresence(lin_addr,false,table,entry);
+
+ if (!table.block.a) {
+ table.block.a=1; //Set access
+ phys_writed((paging.base.page<<12)+(lin_page >> 10)*4,table.load);
+ }
+ if (!entry.block.a) {
+ entry.block.a=1; //Set access
+ phys_writed((table.block.base<<12)+(lin_page & 0x3ff)*4,entry.load);
+ }
+ phys_page=entry.block.base;
+ // maybe use read-only page here if possible
+ } else {
+ if (lin_page<LINK_START) phys_page=paging.firstmb[lin_page];
+ else phys_page=lin_page;
+ }
+ PAGING_LinkPage(lin_page,phys_page);
+ }
+};
+
+class InitPageUserROHandler : public PageHandler {
+public:
+ InitPageUserROHandler() {
+ flags=PFLAG_INIT|PFLAG_NOCODE;
+ }
+ void writeb(PhysPt addr,Bitu val) {
+ InitPage(addr,(Bit8u)(val&0xff));
+ host_writeb(get_tlb_read(addr)+addr,(Bit8u)(val&0xff));
+ }
+ void writew(PhysPt addr,Bitu val) {
+ InitPage(addr,(Bit16u)(val&0xffff));
+ host_writew(get_tlb_read(addr)+addr,(Bit16u)(val&0xffff));
+ }
+ void writed(PhysPt addr,Bitu val) {
+ InitPage(addr,(Bit32u)val);
+ host_writed(get_tlb_read(addr)+addr,(Bit32u)val);
+ }
+ bool writeb_checked(PhysPt addr,Bitu val) {
+ Bitu writecode=InitPageCheckOnly(addr,(Bit8u)(val&0xff));
+ if (writecode) {
+ HostPt tlb_addr;
+ if (writecode>1) tlb_addr=get_tlb_read(addr);
+ else tlb_addr=get_tlb_write(addr);
+ host_writeb(tlb_addr+addr,(Bit8u)(val&0xff));
+ return false;
+ }
+ return true;
+ }
+ bool writew_checked(PhysPt addr,Bitu val) {
+ Bitu writecode=InitPageCheckOnly(addr,(Bit16u)(val&0xffff));
+ if (writecode) {
+ HostPt tlb_addr;
+ if (writecode>1) tlb_addr=get_tlb_read(addr);
+ else tlb_addr=get_tlb_write(addr);
+ host_writew(tlb_addr+addr,(Bit16u)(val&0xffff));
+ return false;
+ }
+ return true;
+ }
+ bool writed_checked(PhysPt addr,Bitu val) {
+ Bitu writecode=InitPageCheckOnly(addr,(Bit32u)val);
+ if (writecode) {
+ HostPt tlb_addr;
+ if (writecode>1) tlb_addr=get_tlb_read(addr);
+ else tlb_addr=get_tlb_write(addr);
+ host_writed(tlb_addr+addr,(Bit32u)val);
+ return false;
+ }
+ return true;
+ }
+ void InitPage(Bitu lin_addr,Bitu val) {
+ Bitu lin_page=lin_addr >> 12;
+ Bitu phys_page;
+ if (paging.enabled) {
+ if (!USERWRITE_PROHIBITED) return;
+
+ X86PageEntry table;
+ X86PageEntry entry;
+ InitPageCheckPresence(lin_addr,true,table,entry);
+
+ LOG(LOG_PAGING,LOG_NORMAL)("Page access denied: cpl=%i, %x:%x:%x:%x",
+ cpu.cpl,entry.block.us,table.block.us,entry.block.wr,table.block.wr);
+ PAGING_PageFault(lin_addr,(table.block.base<<12)+(lin_page & 0x3ff)*4,0x07);
+
+ if (!table.block.a) {
+ table.block.a=1; //Set access
+ phys_writed((paging.base.page<<12)+(lin_page >> 10)*4,table.load);
+ }
+ if ((!entry.block.a) || (!entry.block.d)) {
+ entry.block.a=1; //Set access
+ entry.block.d=1; //Set dirty
+ phys_writed((table.block.base<<12)+(lin_page & 0x3ff)*4,entry.load);
+ }
+ phys_page=entry.block.base;
+ PAGING_LinkPage(lin_page,phys_page);
+ } else {
+ if (lin_page<LINK_START) phys_page=paging.firstmb[lin_page];
+ else phys_page=lin_page;
+ PAGING_LinkPage(lin_page,phys_page);
+ }
+ }
+ Bitu InitPageCheckOnly(Bitu lin_addr,Bitu val) {
+ Bitu lin_page=lin_addr >> 12;
+ if (paging.enabled) {
+ if (!USERWRITE_PROHIBITED) return 2;
+
+ X86PageEntry table;
+ X86PageEntry entry;
+ if (!InitPageCheckPresence_CheckOnly(lin_addr,true,table,entry)) return 0;
+
+ if (InitPage_CheckUseraccess(entry.block.us,table.block.us) || (((entry.block.wr==0) || (table.block.wr==0)))) {
+ LOG(LOG_PAGING,LOG_NORMAL)("Page access denied: cpl=%i, %x:%x:%x:%x",
+ cpu.cpl,entry.block.us,table.block.us,entry.block.wr,table.block.wr);
+ paging.cr2=lin_addr;
+ cpu.exception.which=EXCEPTION_PF;
+ cpu.exception.error=0x07;
+ return 0;
+ }
+ PAGING_LinkPage(lin_page,entry.block.base);
+ } else {
+ Bitu phys_page;
+ if (lin_page<LINK_START) phys_page=paging.firstmb[lin_page];
+ else phys_page=lin_page;
+ PAGING_LinkPage(lin_page,phys_page);
+ }
+ return 1;
+ }
+ void InitPageForced(Bitu lin_addr) {
+ Bitu lin_page=lin_addr >> 12;
+ Bitu phys_page;
+ if (paging.enabled) {
+ X86PageEntry table;
+ X86PageEntry entry;
+ InitPageCheckPresence(lin_addr,true,table,entry);
+
+ if (!table.block.a) {
+ table.block.a=1; //Set access
+ phys_writed((paging.base.page<<12)+(lin_page >> 10)*4,table.load);
+ }
+ if (!entry.block.a) {
+ entry.block.a=1; //Set access
+ phys_writed((table.block.base<<12)+(lin_page & 0x3ff)*4,entry.load);
+ }
+ phys_page=entry.block.base;
+ } else {
+ if (lin_page<LINK_START) phys_page=paging.firstmb[lin_page];
+ else phys_page=lin_page;
+ }
+ PAGING_LinkPage(lin_page,phys_page);
+ }
+};
+
+
+bool PAGING_MakePhysPage(Bitu & page) {
+ if (paging.enabled) {
+ Bitu d_index=page >> 10;
+ Bitu t_index=page & 0x3ff;
+ X86PageEntry table;
+ table.load=phys_readd((paging.base.page<<12)+d_index*4);
+ if (!table.block.p) return false;
+ X86PageEntry entry;
+ entry.load=phys_readd((table.block.base<<12)+t_index*4);
+ if (!entry.block.p) return false;
+ page=entry.block.base;
+ } else {
+ if (page<LINK_START) page=paging.firstmb[page];
+ //Else keep it the same
+ }
+ return true;
+}
+
+static InitPageHandler init_page_handler;
+static InitPageUserROHandler init_page_handler_userro;
+
+
+Bitu PAGING_GetDirBase(void) {
+ return paging.cr3;
+}
+
+bool PAGING_ForcePageInit(Bitu lin_addr) {
+ PageHandler * handler=get_tlb_readhandler(lin_addr);
+ if (handler==&init_page_handler) {
+ init_page_handler.InitPageForced(lin_addr);
+ return true;
+ } else if (handler==&init_page_handler_userro) {
+ PAGING_UnlinkPages(lin_addr>>12,1);
+ init_page_handler_userro.InitPageForced(lin_addr);
+ return true;
+ }
+ return false;
+}
+
+#if defined(USE_FULL_TLB)
+void PAGING_InitTLB(void) {
+ for (Bitu i=0;i<TLB_SIZE;i++) {
+ paging.tlb.read[i]=0;
+ paging.tlb.write[i]=0;
+ paging.tlb.readhandler[i]=&init_page_handler;
+ paging.tlb.writehandler[i]=&init_page_handler;
+ }
+ paging.links.used=0;
+}
+
+void PAGING_ClearTLB(void) {
+ Bit32u * entries=&paging.links.entries[0];
+ for (;paging.links.used>0;paging.links.used--) {
+ Bitu page=*entries++;
+ paging.tlb.read[page]=0;
+ paging.tlb.write[page]=0;
+ paging.tlb.readhandler[page]=&init_page_handler;
+ paging.tlb.writehandler[page]=&init_page_handler;
+ }
+ paging.links.used=0;
+}
+
+void PAGING_UnlinkPages(Bitu lin_page,Bitu pages) {
+ for (;pages>0;pages--) {
+ paging.tlb.read[lin_page]=0;
+ paging.tlb.write[lin_page]=0;
+ paging.tlb.readhandler[lin_page]=&init_page_handler;
+ paging.tlb.writehandler[lin_page]=&init_page_handler;
+ lin_page++;
+ }
+}
+
+void PAGING_MapPage(Bitu lin_page,Bitu phys_page) {
+ if (lin_page<LINK_START) {
+ paging.firstmb[lin_page]=phys_page;
+ paging.tlb.read[lin_page]=0;
+ paging.tlb.write[lin_page]=0;
+ paging.tlb.readhandler[lin_page]=&init_page_handler;
+ paging.tlb.writehandler[lin_page]=&init_page_handler;
+ } else {
+ PAGING_LinkPage(lin_page,phys_page);
+ }
+}
+
+void PAGING_LinkPage(Bitu lin_page,Bitu phys_page) {
+ PageHandler * handler=MEM_GetPageHandler(phys_page);
+ Bitu lin_base=lin_page << 12;
+ if (lin_page>=TLB_SIZE || phys_page>=TLB_SIZE)
+ E_Exit("Illegal page");
+
+ if (paging.links.used>=PAGING_LINKS) {
+ LOG(LOG_PAGING,LOG_NORMAL)("Not enough paging links, resetting cache");
+ PAGING_ClearTLB();
+ }
+
+ paging.tlb.phys_page[lin_page]=phys_page;
+ if (handler->flags & PFLAG_READABLE) paging.tlb.read[lin_page]=handler->GetHostReadPt(phys_page)-lin_base;
+ else paging.tlb.read[lin_page]=0;
+ if (handler->flags & PFLAG_WRITEABLE) paging.tlb.write[lin_page]=handler->GetHostWritePt(phys_page)-lin_base;
+ else paging.tlb.write[lin_page]=0;
+
+ paging.links.entries[paging.links.used++]=lin_page;
+ paging.tlb.readhandler[lin_page]=handler;
+ paging.tlb.writehandler[lin_page]=handler;
+}
+
+void PAGING_LinkPage_ReadOnly(Bitu lin_page,Bitu phys_page) {
+ PageHandler * handler=MEM_GetPageHandler(phys_page);
+ Bitu lin_base=lin_page << 12;
+ if (lin_page>=TLB_SIZE || phys_page>=TLB_SIZE)
+ E_Exit("Illegal page");
+
+ if (paging.links.used>=PAGING_LINKS) {
+ LOG(LOG_PAGING,LOG_NORMAL)("Not enough paging links, resetting cache");
+ PAGING_ClearTLB();
+ }
+
+ paging.tlb.phys_page[lin_page]=phys_page;
+ if (handler->flags & PFLAG_READABLE) paging.tlb.read[lin_page]=handler->GetHostReadPt(phys_page)-lin_base;
+ else paging.tlb.read[lin_page]=0;
+ paging.tlb.write[lin_page]=0;
+
+ paging.links.entries[paging.links.used++]=lin_page;
+ paging.tlb.readhandler[lin_page]=handler;
+ paging.tlb.writehandler[lin_page]=&init_page_handler_userro;
+}
+
+#else
+
+static INLINE void InitTLBInt(tlb_entry *bank) {
+ for (Bitu i=0;i<TLB_SIZE;i++) {
+ bank[i].read=0;
+ bank[i].write=0;
+ bank[i].readhandler=&init_page_handler;
+ bank[i].writehandler=&init_page_handler;
+ }
+}
+
+void PAGING_InitTLBBank(tlb_entry **bank) {
+ *bank = (tlb_entry *)malloc(sizeof(tlb_entry)*TLB_SIZE);
+ if(!*bank) E_Exit("Out of Memory");
+ InitTLBInt(*bank);
+}
+
+void PAGING_InitTLB(void) {
+ InitTLBInt(paging.tlbh);
+ paging.links.used=0;
+}
+
+void PAGING_ClearTLB(void) {
+ Bit32u * entries=&paging.links.entries[0];
+ for (;paging.links.used>0;paging.links.used--) {
+ Bitu page=*entries++;
+ tlb_entry *entry = get_tlb_entry(page<<12);
+ entry->read=0;
+ entry->write=0;
+ entry->readhandler=&init_page_handler;
+ entry->writehandler=&init_page_handler;
+ }
+ paging.links.used=0;
+}
+
+void PAGING_UnlinkPages(Bitu lin_page,Bitu pages) {
+ for (;pages>0;pages--) {
+ tlb_entry *entry = get_tlb_entry(lin_page<<12);
+ entry->read=0;
+ entry->write=0;
+ entry->readhandler=&init_page_handler;
+ entry->writehandler=&init_page_handler;
+ lin_page++;
+ }
+}
+
+void PAGING_MapPage(Bitu lin_page,Bitu phys_page) {
+ if (lin_page<LINK_START) {
+ paging.firstmb[lin_page]=phys_page;
+ paging.tlbh[lin_page].read=0;
+ paging.tlbh[lin_page].write=0;
+ paging.tlbh[lin_page].readhandler=&init_page_handler;
+ paging.tlbh[lin_page].writehandler=&init_page_handler;
+ } else {
+ PAGING_LinkPage(lin_page,phys_page);
+ }
+}
+
+void PAGING_LinkPage(Bitu lin_page,Bitu phys_page) {
+ PageHandler * handler=MEM_GetPageHandler(phys_page);
+ Bitu lin_base=lin_page << 12;
+ if (lin_page>=(TLB_SIZE*(TLB_BANKS+1)) || phys_page>=(TLB_SIZE*(TLB_BANKS+1)))
+ E_Exit("Illegal page");
+
+ if (paging.links.used>=PAGING_LINKS) {
+ LOG(LOG_PAGING,LOG_NORMAL)("Not enough paging links, resetting cache");
+ PAGING_ClearTLB();
+ }
+
+ tlb_entry *entry = get_tlb_entry(lin_base);
+ entry->phys_page=phys_page;
+ if (handler->flags & PFLAG_READABLE) entry->read=handler->GetHostReadPt(phys_page)-lin_base;
+ else entry->read=0;
+ if (handler->flags & PFLAG_WRITEABLE) entry->write=handler->GetHostWritePt(phys_page)-lin_base;
+ else entry->write=0;
+
+ paging.links.entries[paging.links.used++]=lin_page;
+ entry->readhandler=handler;
+ entry->writehandler=handler;
+}
+
+void PAGING_LinkPage_ReadOnly(Bitu lin_page,Bitu phys_page) {
+ PageHandler * handler=MEM_GetPageHandler(phys_page);
+ Bitu lin_base=lin_page << 12;
+ if (lin_page>=(TLB_SIZE*(TLB_BANKS+1)) || phys_page>=(TLB_SIZE*(TLB_BANKS+1)))
+ E_Exit("Illegal page");
+
+ if (paging.links.used>=PAGING_LINKS) {
+ LOG(LOG_PAGING,LOG_NORMAL)("Not enough paging links, resetting cache");
+ PAGING_ClearTLB();
+ }
+
+ tlb_entry *entry = get_tlb_entry(lin_base);
+ entry->phys_page=phys_page;
+ if (handler->flags & PFLAG_READABLE) entry->read=handler->GetHostReadPt(phys_page)-lin_base;
+ else entry->read=0;
+ entry->write=0;
+
+ paging.links.entries[paging.links.used++]=lin_page;
+ entry->readhandler=handler;
+ entry->writehandler=&init_page_handler_userro;
+}
+
+#endif
+
+
+void PAGING_SetDirBase(Bitu cr3) {
+ paging.cr3=cr3;
+
+ paging.base.page=cr3 >> 12;
+ paging.base.addr=cr3 & ~4095;
+// LOG(LOG_PAGING,LOG_NORMAL)("CR3:%X Base %X",cr3,paging.base.page);
+ if (paging.enabled) {
+ PAGING_ClearTLB();
+ }
+}
+
+void PAGING_Enable(bool enabled) {
+ /* If paging is disabled, we work from a default paging table */
+ if (paging.enabled==enabled) return;
+ paging.enabled=enabled;
+ if (enabled) {
+ if (GCC_UNLIKELY(cpudecoder==CPU_Core_Simple_Run)) {
+// LOG_MSG("CPU core simple won't run this game,switching to normal");
+ cpudecoder=CPU_Core_Normal_Run;
+ CPU_CycleLeft+=CPU_Cycles;
+ CPU_Cycles=0;
+ }
+// LOG(LOG_PAGING,LOG_NORMAL)("Enabled");
+ PAGING_SetDirBase(paging.cr3);
+ }
+ PAGING_ClearTLB();
+}
+
+bool PAGING_Enabled(void) {
+ return paging.enabled;
+}
+
+class PAGING:public Module_base{
+public:
+ PAGING(Section* configuration):Module_base(configuration){
+ /* Setup default Page Directory, force it to update */
+ paging.enabled=false;
+ PAGING_InitTLB();
+ Bitu i;
+ for (i=0;i<LINK_START;i++) {
+ paging.firstmb[i]=i;
+ }
+ pf_queue.used=0;
+ }
+ ~PAGING(){}
+};
+
+static PAGING* test;
+void PAGING_Init(Section * sec) {
+ test = new PAGING(sec);
+}
diff --git a/src/debug/Makefile.am b/src/debug/Makefile.am
new file mode 100644
index 000000000..dfee79248
--- /dev/null
+++ b/src/debug/Makefile.am
@@ -0,0 +1,4 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libdebug.a
+libdebug_a_SOURCES = debug.cpp debug_gui.cpp debug_disasm.cpp debug_inc.h disasm_tables.h debug_win32.cpp \ No newline at end of file
diff --git a/src/debug/debug.cpp b/src/debug/debug.cpp
new file mode 100644
index 000000000..dcf0b8007
--- /dev/null
+++ b/src/debug/debug.cpp
@@ -0,0 +1,2588 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#if C_DEBUG
+
+#include <string.h>
+#include <list>
+#include <vector>
+#include <ctype.h>
+#include <fstream>
+#include <iomanip>
+#include <string>
+#include <sstream>
+using namespace std;
+
+#include "debug.h"
+#include "cross.h" //snprintf
+#include "cpu.h"
+#include "video.h"
+#include "pic.h"
+#include "mapper.h"
+#include "cpu.h"
+#include "callback.h"
+#include "inout.h"
+#include "mixer.h"
+#include "timer.h"
+#include "paging.h"
+#include "support.h"
+#include "shell.h"
+#include "programs.h"
+#include "debug_inc.h"
+#include "../cpu/lazyflags.h"
+#include "keyboard.h"
+#include "setup.h"
+
+#ifdef WIN32
+void WIN32_Console();
+#else
+#include <termios.h>
+#include <unistd.h>
+static struct termios consolesettings;
+#endif
+int old_cursor_state;
+
+// Forwards
+static void DrawCode(void);
+static void DEBUG_RaiseTimerIrq(void);
+static void SaveMemory(Bit16u seg, Bit32u ofs1, Bit32u num);
+static void SaveMemoryBin(Bit16u seg, Bit32u ofs1, Bit32u num);
+static void LogMCBS(void);
+static void LogGDT(void);
+static void LogLDT(void);
+static void LogIDT(void);
+static void LogPages(char* selname);
+static void LogCPUInfo(void);
+static void OutputVecTable(char* filename);
+static void DrawVariables(void);
+
+char* AnalyzeInstruction(char* inst, bool saveSelector);
+Bit32u GetHexValue(char* str, char*& hex);
+
+#if 0
+class DebugPageHandler : public PageHandler {
+public:
+ Bitu readb(PhysPt /*addr*/) {
+ }
+ Bitu readw(PhysPt /*addr*/) {
+ }
+ Bitu readd(PhysPt /*addr*/) {
+ }
+ void writeb(PhysPt /*addr*/,Bitu /*val*/) {
+ }
+ void writew(PhysPt /*addr*/,Bitu /*val*/) {
+ }
+ void writed(PhysPt /*addr*/,Bitu /*val*/) {
+ }
+};
+#endif
+
+
+class DEBUG;
+
+DEBUG* pDebugcom = 0;
+bool exitLoop = false;
+
+
+// Heavy Debugging Vars for logging
+#if C_HEAVY_DEBUG
+static ofstream cpuLogFile;
+static bool cpuLog = false;
+static int cpuLogCounter = 0;
+static int cpuLogType = 1; // log detail
+static bool zeroProtect = false;
+bool logHeavy = false;
+#endif
+
+
+
+static struct {
+ Bit32u eax,ebx,ecx,edx,esi,edi,ebp,esp,eip;
+} oldregs;
+
+static char curSelectorName[3] = { 0,0,0 };
+
+static Segment oldsegs[6];
+static Bitu oldflags,oldcpucpl;
+DBGBlock dbg;
+Bitu cycle_count;
+static bool debugging;
+
+
+static void SetColor(Bitu test) {
+ if (test) {
+ if (has_colors()) { wattrset(dbg.win_reg,COLOR_PAIR(PAIR_BYELLOW_BLACK));}
+ } else {
+ if (has_colors()) { wattrset(dbg.win_reg,0);}
+ }
+}
+
+#define MAXCMDLEN 254
+struct SCodeViewData {
+ int cursorPos;
+ Bit16u firstInstSize;
+ Bit16u useCS;
+ Bit32u useEIPlast, useEIPmid;
+ Bit32u useEIP;
+ Bit16u cursorSeg;
+ Bit32u cursorOfs;
+ bool ovrMode;
+ char inputStr[MAXCMDLEN+1];
+ char suspInputStr[MAXCMDLEN+1];
+ int inputPos;
+} codeViewData;
+
+static Bit16u dataSeg;
+static Bit32u dataOfs;
+static bool showExtend = true;
+
+static void ClearInputLine(void) {
+ codeViewData.inputStr[0] = 0;
+ codeViewData.inputPos = 0;
+}
+
+// History stuff
+#define MAX_HIST_BUFFER 50
+static list<string> histBuff;
+static list<string>::iterator histBuffPos = histBuff.end();
+
+/***********/
+/* Helpers */
+/***********/
+
+Bit32u PhysMakeProt(Bit16u selector, Bit32u offset)
+{
+ Descriptor desc;
+ if (cpu.gdt.GetDescriptor(selector,desc)) return desc.GetBase()+offset;
+ return 0;
+};
+
+Bit32u GetAddress(Bit16u seg, Bit32u offset)
+{
+ if (seg==SegValue(cs)) return SegPhys(cs)+offset;
+ if (cpu.pmode && !(reg_flags & FLAG_VM)) {
+ Descriptor desc;
+ if (cpu.gdt.GetDescriptor(seg,desc)) return PhysMakeProt(seg,offset);
+ }
+ return (seg<<4)+offset;
+}
+
+static char empty_sel[] = { ' ',' ',0 };
+
+bool GetDescriptorInfo(char* selname, char* out1, char* out2)
+{
+ Bitu sel;
+ Descriptor desc;
+
+ if (strstr(selname,"cs") || strstr(selname,"CS")) sel = SegValue(cs);
+ else if (strstr(selname,"ds") || strstr(selname,"DS")) sel = SegValue(ds);
+ else if (strstr(selname,"es") || strstr(selname,"ES")) sel = SegValue(es);
+ else if (strstr(selname,"fs") || strstr(selname,"FS")) sel = SegValue(fs);
+ else if (strstr(selname,"gs") || strstr(selname,"GS")) sel = SegValue(gs);
+ else if (strstr(selname,"ss") || strstr(selname,"SS")) sel = SegValue(ss);
+ else {
+ sel = GetHexValue(selname,selname);
+ if (*selname==0) selname=empty_sel;
+ }
+ if (cpu.gdt.GetDescriptor(sel,desc)) {
+ switch (desc.Type()) {
+ case DESC_TASK_GATE:
+ sprintf(out1,"%s: s:%08X type:%02X p",selname,desc.GetSelector(),desc.saved.gate.type);
+ sprintf(out2," TaskGate dpl : %01X %1X",desc.saved.gate.dpl,desc.saved.gate.p);
+ return true;
+ case DESC_LDT:
+ case DESC_286_TSS_A:
+ case DESC_286_TSS_B:
+ case DESC_386_TSS_A:
+ case DESC_386_TSS_B:
+ sprintf(out1,"%s: b:%08X type:%02X pag",selname,desc.GetBase(),desc.saved.seg.type);
+ sprintf(out2," l:%08X dpl : %01X %1X%1X%1X",desc.GetLimit(),desc.saved.seg.dpl,desc.saved.seg.p,desc.saved.seg.avl,desc.saved.seg.g);
+ return true;
+ case DESC_286_CALL_GATE:
+ case DESC_386_CALL_GATE:
+ sprintf(out1,"%s: s:%08X type:%02X p params: %02X",selname,desc.GetSelector(),desc.saved.gate.type,desc.saved.gate.paramcount);
+ sprintf(out2," o:%08X dpl : %01X %1X",desc.GetOffset(),desc.saved.gate.dpl,desc.saved.gate.p);
+ return true;
+ case DESC_286_INT_GATE:
+ case DESC_286_TRAP_GATE:
+ case DESC_386_INT_GATE:
+ case DESC_386_TRAP_GATE:
+ sprintf(out1,"%s: s:%08X type:%02X p",selname,desc.GetSelector(),desc.saved.gate.type);
+ sprintf(out2," o:%08X dpl : %01X %1X",desc.GetOffset(),desc.saved.gate.dpl,desc.saved.gate.p);
+ return true;
+ }
+ sprintf(out1,"%s: b:%08X type:%02X parbg",selname,desc.GetBase(),desc.saved.seg.type);
+ sprintf(out2," l:%08X dpl : %01X %1X%1X%1X%1X%1X",desc.GetLimit(),desc.saved.seg.dpl,desc.saved.seg.p,desc.saved.seg.avl,desc.saved.seg.r,desc.saved.seg.big,desc.saved.seg.g);
+ return true;
+ } else {
+ strcpy(out1," ");
+ strcpy(out2," ");
+ }
+ return false;
+};
+
+/********************/
+/* DebugVar stuff */
+/********************/
+
+class CDebugVar
+{
+public:
+ CDebugVar(char* _name, PhysPt _adr) { adr=_adr; safe_strncpy(name,_name,16); hasvalue = false; value = 0; };
+
+ char* GetName (void) { return name; };
+ PhysPt GetAdr (void) { return adr; };
+ void SetValue(bool has, Bit16u val) { hasvalue = has; value=val; };
+ Bit16u GetValue(void) { return value; };
+ bool HasValue(void) { return hasvalue; };
+
+private:
+ PhysPt adr;
+ char name[16];
+ bool hasvalue;
+ Bit16u value;
+
+public:
+ static void InsertVariable(char* name, PhysPt adr);
+ static CDebugVar* FindVar (PhysPt adr);
+ static void DeleteAll ();
+ static bool SaveVars (char* name);
+ static bool LoadVars (char* name);
+
+ static std::vector<CDebugVar*> varList;
+};
+
+std::vector<CDebugVar*> CDebugVar::varList;
+
+
+/********************/
+/* Breakpoint stuff */
+/********************/
+
+bool skipFirstInstruction = false;
+
+enum EBreakpoint { BKPNT_UNKNOWN, BKPNT_PHYSICAL, BKPNT_INTERRUPT, BKPNT_MEMORY, BKPNT_MEMORY_PROT, BKPNT_MEMORY_LINEAR };
+
+#define BPINT_ALL 0x100
+
+class CBreakpoint
+{
+public:
+
+ CBreakpoint(void);
+ void SetAddress (Bit16u seg, Bit32u off) { location = GetAddress(seg,off); type = BKPNT_PHYSICAL; segment = seg; offset = off; };
+ void SetAddress (PhysPt adr) { location = adr; type = BKPNT_PHYSICAL; };
+ void SetInt (Bit8u _intNr, Bit16u ah, Bit16u al) { intNr = _intNr, ahValue = ah; alValue = al; type = BKPNT_INTERRUPT; };
+ void SetOnce (bool _once) { once = _once; };
+ void SetType (EBreakpoint _type) { type = _type; };
+ void SetValue (Bit8u value) { ahValue = value; };
+ void SetOther (Bit8u other) { alValue = other; };
+
+ bool IsActive (void) { return active; };
+ void Activate (bool _active);
+
+ EBreakpoint GetType (void) { return type; };
+ bool GetOnce (void) { return once; };
+ PhysPt GetLocation (void) { return location; };
+ Bit16u GetSegment (void) { return segment; };
+ Bit32u GetOffset (void) { return offset; };
+ Bit8u GetIntNr (void) { return intNr; };
+ Bit16u GetValue (void) { return ahValue; };
+ Bit16u GetOther (void) { return alValue; };
+
+ // statics
+ static CBreakpoint* AddBreakpoint (Bit16u seg, Bit32u off, bool once);
+ static CBreakpoint* AddIntBreakpoint (Bit8u intNum, Bit16u ah, Bit16u al, bool once);
+ static CBreakpoint* AddMemBreakpoint (Bit16u seg, Bit32u off);
+ static void DeactivateBreakpoints();
+ static void ActivateBreakpoints ();
+ static void ActivateBreakpointsExceptAt(PhysPt adr);
+ static bool CheckBreakpoint (PhysPt adr);
+ static bool CheckBreakpoint (Bitu seg, Bitu off);
+ static bool CheckIntBreakpoint (PhysPt adr, Bit8u intNr, Bit16u ahValue, Bit16u alValue);
+ static CBreakpoint* FindPhysBreakpoint (Bit16u seg, Bit32u off, bool once);
+ static CBreakpoint* FindOtherActiveBreakpoint(PhysPt adr, CBreakpoint* skip);
+ static bool IsBreakpoint (Bit16u seg, Bit32u off);
+ static bool DeleteBreakpoint (Bit16u seg, Bit32u off);
+ static bool DeleteByIndex (Bit16u index);
+ static void DeleteAll (void);
+ static void ShowList (void);
+
+
+private:
+ EBreakpoint type;
+ // Physical
+ PhysPt location;
+ Bit8u oldData;
+ Bit16u segment;
+ Bit32u offset;
+ // Int
+ Bit8u intNr;
+ Bit16u ahValue;
+ Bit16u alValue;
+ // Shared
+ bool active;
+ bool once;
+
+ static std::list<CBreakpoint*> BPoints;
+};
+
+CBreakpoint::CBreakpoint(void):
+location(0),oldData(0xCC),
+active(false),once(false),
+segment(0),offset(0),intNr(0),ahValue(0),alValue(0),
+type(BKPNT_UNKNOWN) { };
+
+void CBreakpoint::Activate(bool _active)
+{
+#if !C_HEAVY_DEBUG
+ if (GetType() == BKPNT_PHYSICAL) {
+ if (_active) {
+ // Set 0xCC and save old value
+ Bit8u data = mem_readb(location);
+ if (data != 0xCC) {
+ oldData = data;
+ mem_writeb(location,0xCC);
+ } else if (!active) {
+ // Another activate breakpoint is already here.
+ // Find it, and copy its oldData value
+ CBreakpoint *bp = FindOtherActiveBreakpoint(location, this);
+
+ if (!bp || bp->oldData == 0xCC) {
+ // This might also happen if there is a real 0xCC instruction here
+ DEBUG_ShowMsg("DEBUG: Internal error while activating breakpoint.\n");
+ oldData = 0xCC;
+ } else
+ oldData = bp->oldData;
+ };
+ } else {
+ if (mem_readb(location) == 0xCC) {
+ if (oldData == 0xCC)
+ DEBUG_ShowMsg("DEBUG: Internal error while deactivating breakpoint.\n");
+
+ // Check if we are the last active breakpoint at this location
+ bool otherActive = (FindOtherActiveBreakpoint(location, this) != 0);
+
+ // If so, remove 0xCC and set old value
+ if (!otherActive)
+ mem_writeb(location, oldData);
+ };
+ }
+ }
+#endif
+ active = _active;
+};
+
+// Statics
+std::list<CBreakpoint*> CBreakpoint::BPoints;
+
+CBreakpoint* CBreakpoint::AddBreakpoint(Bit16u seg, Bit32u off, bool once)
+{
+ CBreakpoint* bp = new CBreakpoint();
+ bp->SetAddress (seg,off);
+ bp->SetOnce (once);
+ BPoints.push_front (bp);
+ return bp;
+};
+
+CBreakpoint* CBreakpoint::AddIntBreakpoint(Bit8u intNum, Bit16u ah, Bit16u al, bool once)
+{
+ CBreakpoint* bp = new CBreakpoint();
+ bp->SetInt (intNum,ah,al);
+ bp->SetOnce (once);
+ BPoints.push_front (bp);
+ return bp;
+};
+
+CBreakpoint* CBreakpoint::AddMemBreakpoint(Bit16u seg, Bit32u off)
+{
+ CBreakpoint* bp = new CBreakpoint();
+ bp->SetAddress (seg,off);
+ bp->SetOnce (false);
+ bp->SetType (BKPNT_MEMORY);
+ BPoints.push_front (bp);
+ return bp;
+};
+
+void CBreakpoint::ActivateBreakpoints()
+{
+ // activate all breakpoints
+ std::list<CBreakpoint*>::iterator i;
+ for (i = BPoints.begin(); i != BPoints.end(); ++i)
+ (*i)->Activate(true);
+}
+
+void CBreakpoint::DeactivateBreakpoints()
+{
+ // deactivate all breakpoints
+ std::list<CBreakpoint*>::iterator i;
+ for (i = BPoints.begin(); i != BPoints.end(); ++i)
+ (*i)->Activate(false);
+}
+
+void CBreakpoint::ActivateBreakpointsExceptAt(PhysPt adr)
+{
+ // activate all breakpoints, except those at adr
+ std::list<CBreakpoint*>::iterator i;
+ for (i = BPoints.begin(); i != BPoints.end(); ++i) {
+ CBreakpoint* bp = (*i);
+ // Do not activate breakpoints at adr
+ if (bp->GetType() == BKPNT_PHYSICAL && bp->GetLocation() == adr)
+ continue;
+ bp->Activate(true);
+ };
+};
+
+bool CBreakpoint::CheckBreakpoint(Bitu seg, Bitu off)
+// Checks if breakpoint is valid and should stop execution
+{
+ // Quick exit if there are no breakpoints
+ if (BPoints.empty()) return false;
+
+ // Search matching breakpoint
+ std::list<CBreakpoint*>::iterator i;
+ CBreakpoint* bp;
+ for(i=BPoints.begin(); i != BPoints.end(); ++i) {
+ bp = (*i);
+ if ((bp->GetType()==BKPNT_PHYSICAL) && bp->IsActive() && (bp->GetSegment()==seg) && (bp->GetOffset()==off)) {
+ // Found,
+ if (bp->GetOnce()) {
+ // delete it, if it should only be used once
+ (BPoints.erase)(i);
+ bp->Activate(false);
+ delete bp;
+ } else {
+ // Also look for once-only breakpoints at this address
+ bp = FindPhysBreakpoint(seg, off, true);
+ if (bp) {
+ BPoints.remove(bp);
+ bp->Activate(false);
+ delete bp;
+ }
+ }
+ return true;
+ }
+#if C_HEAVY_DEBUG
+ // Memory breakpoint support
+ else if (bp->IsActive()) {
+ if ((bp->GetType()==BKPNT_MEMORY) || (bp->GetType()==BKPNT_MEMORY_PROT) || (bp->GetType()==BKPNT_MEMORY_LINEAR)) {
+ // Watch Protected Mode Memoryonly in pmode
+ if (bp->GetType()==BKPNT_MEMORY_PROT) {
+ // Check if pmode is active
+ if (!cpu.pmode) return false;
+ // Check if descriptor is valid
+ Descriptor desc;
+ if (!cpu.gdt.GetDescriptor(bp->GetSegment(),desc)) return false;
+ if (desc.GetLimit()==0) return false;
+ }
+
+ Bitu address;
+ if (bp->GetType()==BKPNT_MEMORY_LINEAR) address = bp->GetOffset();
+ else address = GetAddress(bp->GetSegment(),bp->GetOffset());
+ Bit8u value=0;
+ if (mem_readb_checked(address,&value)) return false;
+ if (bp->GetValue() != value) {
+ // Yup, memory value changed
+ DEBUG_ShowMsg("DEBUG: Memory breakpoint %s: %04X:%04X - %02X -> %02X\n",(bp->GetType()==BKPNT_MEMORY_PROT)?"(Prot)":"",bp->GetSegment(),bp->GetOffset(),bp->GetValue(),value);
+ bp->SetValue(value);
+ return true;
+ };
+ }
+ };
+#endif
+ };
+ return false;
+};
+
+bool CBreakpoint::CheckIntBreakpoint(PhysPt adr, Bit8u intNr, Bit16u ahValue, Bit16u alValue)
+// Checks if interrupt breakpoint is valid and should stop execution
+{
+ if (BPoints.empty()) return false;
+
+ // Search matching breakpoint
+ std::list<CBreakpoint*>::iterator i;
+ CBreakpoint* bp;
+ for(i=BPoints.begin(); i != BPoints.end(); ++i) {
+ bp = (*i);
+ if ((bp->GetType()==BKPNT_INTERRUPT) && bp->IsActive() && (bp->GetIntNr()==intNr)) {
+ if (((bp->GetValue()==BPINT_ALL) || (bp->GetValue()==ahValue)) && ((bp->GetOther()==BPINT_ALL) || (bp->GetOther()==alValue))) {
+ // Ignore it once ?
+ // Found
+ if (bp->GetOnce()) {
+ // delete it, if it should only be used once
+ (BPoints.erase)(i);
+ bp->Activate(false);
+ delete bp;
+ }
+ return true;
+ }
+ };
+ };
+ return false;
+};
+
+void CBreakpoint::DeleteAll()
+{
+ std::list<CBreakpoint*>::iterator i;
+ CBreakpoint* bp;
+ for(i=BPoints.begin(); i != BPoints.end(); ++i) {
+ bp = (*i);
+ bp->Activate(false);
+ delete bp;
+ };
+ (BPoints.clear)();
+};
+
+
+bool CBreakpoint::DeleteByIndex(Bit16u index)
+{
+ // Search matching breakpoint
+ int nr = 0;
+ std::list<CBreakpoint*>::iterator i;
+ CBreakpoint* bp;
+ for(i=BPoints.begin(); i != BPoints.end(); ++i) {
+ if (nr==index) {
+ bp = (*i);
+ (BPoints.erase)(i);
+ bp->Activate(false);
+ delete bp;
+ return true;
+ }
+ nr++;
+ };
+ return false;
+};
+
+CBreakpoint* CBreakpoint::FindPhysBreakpoint(Bit16u seg, Bit32u off, bool once)
+{
+ if (BPoints.empty()) return 0;
+#if !C_HEAVY_DEBUG
+ PhysPt adr = GetAddress(seg, off);
+#endif
+ // Search for matching breakpoint
+ std::list<CBreakpoint*>::iterator i;
+ CBreakpoint* bp;
+ for(i=BPoints.begin(); i != BPoints.end(); ++i) {
+ bp = (*i);
+#if C_HEAVY_DEBUG
+ // Heavy debugging breakpoints are triggered by matching seg:off
+ bool atLocation = bp->GetSegment() == seg && bp->GetOffset() == off;
+#else
+ // Normal debugging breakpoints are triggered at an address
+ bool atLocation = bp->GetLocation() == adr;
+#endif
+
+ if (bp->GetType() == BKPNT_PHYSICAL && atLocation && bp->GetOnce() == once)
+ return bp;
+ }
+
+ return 0;
+}
+
+CBreakpoint* CBreakpoint::FindOtherActiveBreakpoint(PhysPt adr, CBreakpoint* skip)
+{
+ std::list<CBreakpoint*>::iterator i;
+ for (i = BPoints.begin(); i != BPoints.end(); ++i) {
+ CBreakpoint* bp = (*i);
+ if (bp != skip && bp->GetType() == BKPNT_PHYSICAL && bp->GetLocation() == adr && bp->IsActive())
+ return bp;
+ }
+ return 0;
+}
+
+// is there a permanent breakpoint at address ?
+bool CBreakpoint::IsBreakpoint(Bit16u seg, Bit32u off)
+{
+ return FindPhysBreakpoint(seg, off, false) != 0;
+}
+
+bool CBreakpoint::DeleteBreakpoint(Bit16u seg, Bit32u off)
+{
+ CBreakpoint* bp = FindPhysBreakpoint(seg, off, false);
+ if (bp) {
+ BPoints.remove(bp);
+ delete bp;
+ return true;
+ }
+
+ return false;
+}
+
+
+void CBreakpoint::ShowList(void)
+{
+ // iterate list
+ int nr = 0;
+ std::list<CBreakpoint*>::iterator i;
+ for(i=BPoints.begin(); i != BPoints.end(); ++i) {
+ CBreakpoint* bp = (*i);
+ if (bp->GetType()==BKPNT_PHYSICAL) {
+ DEBUG_ShowMsg("%02X. BP %04X:%04X\n",nr,bp->GetSegment(),bp->GetOffset());
+ } else if (bp->GetType()==BKPNT_INTERRUPT) {
+ if (bp->GetValue()==BPINT_ALL) DEBUG_ShowMsg("%02X. BPINT %02X\n",nr,bp->GetIntNr());
+ else if (bp->GetOther()==BPINT_ALL) DEBUG_ShowMsg("%02X. BPINT %02X AH=%02X\n",nr,bp->GetIntNr(),bp->GetValue());
+ else DEBUG_ShowMsg("%02X. BPINT %02X AH=%02X AL=%02X\n",nr,bp->GetIntNr(),bp->GetValue(),bp->GetOther());
+ } else if (bp->GetType()==BKPNT_MEMORY) {
+ DEBUG_ShowMsg("%02X. BPMEM %04X:%04X (%02X)\n",nr,bp->GetSegment(),bp->GetOffset(),bp->GetValue());
+ } else if (bp->GetType()==BKPNT_MEMORY_PROT) {
+ DEBUG_ShowMsg("%02X. BPPM %04X:%08X (%02X)\n",nr,bp->GetSegment(),bp->GetOffset(),bp->GetValue());
+ } else if (bp->GetType()==BKPNT_MEMORY_LINEAR ) {
+ DEBUG_ShowMsg("%02X. BPLM %08X (%02X)\n",nr,bp->GetOffset(),bp->GetValue());
+ };
+ nr++;
+ }
+};
+
+bool DEBUG_Breakpoint(void)
+{
+ /* First get the physical address and check for a set Breakpoint */
+ if (!CBreakpoint::CheckBreakpoint(SegValue(cs),reg_eip)) return false;
+ // Found. Breakpoint is valid
+ PhysPt where=GetAddress(SegValue(cs),reg_eip);
+ CBreakpoint::DeactivateBreakpoints(); // Deactivate all breakpoints
+ return true;
+};
+
+bool DEBUG_IntBreakpoint(Bit8u intNum)
+{
+ /* First get the physical address and check for a set Breakpoint */
+ PhysPt where=GetAddress(SegValue(cs),reg_eip);
+ if (!CBreakpoint::CheckIntBreakpoint(where,intNum,reg_ah,reg_al)) return false;
+ // Found. Breakpoint is valid
+ CBreakpoint::DeactivateBreakpoints(); // Deactivate all breakpoints
+ return true;
+};
+
+static bool StepOver()
+{
+ exitLoop = false;
+ PhysPt start=GetAddress(SegValue(cs),reg_eip);
+ char dline[200];Bitu size;
+ size=DasmI386(dline, start, reg_eip, cpu.code.big);
+
+ if (strstr(dline,"call") || strstr(dline,"int") || strstr(dline,"loop") || strstr(dline,"rep")) {
+ // Don't add a temporary breakpoint if there's already one here
+ if (!CBreakpoint::FindPhysBreakpoint(SegValue(cs), reg_eip+size, true))
+ CBreakpoint::AddBreakpoint(SegValue(cs),reg_eip+size, true);
+ debugging=false;
+ DrawCode();
+ return true;
+ }
+ return false;
+};
+
+bool DEBUG_ExitLoop(void)
+{
+#if C_HEAVY_DEBUG
+ DrawVariables();
+#endif
+
+ if (exitLoop) {
+ exitLoop = false;
+ return true;
+ }
+ return false;
+};
+
+/********************/
+/* Draw windows */
+/********************/
+
+static void DrawData(void) {
+
+ Bit8u ch;
+ Bit32u add = dataOfs;
+ Bit32u address;
+ /* Data win */
+ for (int y=0; y<8; y++) {
+ // Address
+ if (add<0x10000) mvwprintw (dbg.win_data,y,0,"%04X:%04X ",dataSeg,add);
+ else mvwprintw (dbg.win_data,y,0,"%04X:%08X ",dataSeg,add);
+ for (int x=0; x<16; x++) {
+ address = GetAddress(dataSeg,add);
+ if (mem_readb_checked(address,&ch)) ch=0;
+ mvwprintw (dbg.win_data,y,14+3*x,"%02X",ch);
+ if (ch<32 || !isprint(*reinterpret_cast<unsigned char*>(&ch))) ch='.';
+ mvwprintw (dbg.win_data,y,63+x,"%c",ch);
+ add++;
+ };
+ }
+ wrefresh(dbg.win_data);
+};
+
+static void DrawRegisters(void) {
+ /* Main Registers */
+ SetColor(reg_eax!=oldregs.eax);oldregs.eax=reg_eax;mvwprintw (dbg.win_reg,0,4,"%08X",reg_eax);
+ SetColor(reg_ebx!=oldregs.ebx);oldregs.ebx=reg_ebx;mvwprintw (dbg.win_reg,1,4,"%08X",reg_ebx);
+ SetColor(reg_ecx!=oldregs.ecx);oldregs.ecx=reg_ecx;mvwprintw (dbg.win_reg,2,4,"%08X",reg_ecx);
+ SetColor(reg_edx!=oldregs.edx);oldregs.edx=reg_edx;mvwprintw (dbg.win_reg,3,4,"%08X",reg_edx);
+
+ SetColor(reg_esi!=oldregs.esi);oldregs.esi=reg_esi;mvwprintw (dbg.win_reg,0,18,"%08X",reg_esi);
+ SetColor(reg_edi!=oldregs.edi);oldregs.edi=reg_edi;mvwprintw (dbg.win_reg,1,18,"%08X",reg_edi);
+ SetColor(reg_ebp!=oldregs.ebp);oldregs.ebp=reg_ebp;mvwprintw (dbg.win_reg,2,18,"%08X",reg_ebp);
+ SetColor(reg_esp!=oldregs.esp);oldregs.esp=reg_esp;mvwprintw (dbg.win_reg,3,18,"%08X",reg_esp);
+ SetColor(reg_eip!=oldregs.eip);oldregs.eip=reg_eip;mvwprintw (dbg.win_reg,1,42,"%08X",reg_eip);
+
+ SetColor(SegValue(ds)!=oldsegs[ds].val);oldsegs[ds].val=SegValue(ds);mvwprintw (dbg.win_reg,0,31,"%04X",SegValue(ds));
+ SetColor(SegValue(es)!=oldsegs[es].val);oldsegs[es].val=SegValue(es);mvwprintw (dbg.win_reg,0,41,"%04X",SegValue(es));
+ SetColor(SegValue(fs)!=oldsegs[fs].val);oldsegs[fs].val=SegValue(fs);mvwprintw (dbg.win_reg,0,51,"%04X",SegValue(fs));
+ SetColor(SegValue(gs)!=oldsegs[gs].val);oldsegs[gs].val=SegValue(gs);mvwprintw (dbg.win_reg,0,61,"%04X",SegValue(gs));
+ SetColor(SegValue(ss)!=oldsegs[ss].val);oldsegs[ss].val=SegValue(ss);mvwprintw (dbg.win_reg,0,71,"%04X",SegValue(ss));
+ SetColor(SegValue(cs)!=oldsegs[cs].val);oldsegs[cs].val=SegValue(cs);mvwprintw (dbg.win_reg,1,31,"%04X",SegValue(cs));
+
+ /*Individual flags*/
+ Bitu changed_flags = reg_flags ^ oldflags;
+ oldflags = reg_flags;
+
+ SetColor(changed_flags&FLAG_CF);
+ mvwprintw (dbg.win_reg,1,53,"%01X",GETFLAG(CF) ? 1:0);
+ SetColor(changed_flags&FLAG_ZF);
+ mvwprintw (dbg.win_reg,1,56,"%01X",GETFLAG(ZF) ? 1:0);
+ SetColor(changed_flags&FLAG_SF);
+ mvwprintw (dbg.win_reg,1,59,"%01X",GETFLAG(SF) ? 1:0);
+ SetColor(changed_flags&FLAG_OF);
+ mvwprintw (dbg.win_reg,1,62,"%01X",GETFLAG(OF) ? 1:0);
+ SetColor(changed_flags&FLAG_AF);
+ mvwprintw (dbg.win_reg,1,65,"%01X",GETFLAG(AF) ? 1:0);
+ SetColor(changed_flags&FLAG_PF);
+ mvwprintw (dbg.win_reg,1,68,"%01X",GETFLAG(PF) ? 1:0);
+
+
+ SetColor(changed_flags&FLAG_DF);
+ mvwprintw (dbg.win_reg,1,71,"%01X",GETFLAG(DF) ? 1:0);
+ SetColor(changed_flags&FLAG_IF);
+ mvwprintw (dbg.win_reg,1,74,"%01X",GETFLAG(IF) ? 1:0);
+ SetColor(changed_flags&FLAG_TF);
+ mvwprintw (dbg.win_reg,1,77,"%01X",GETFLAG(TF) ? 1:0);
+
+ SetColor(changed_flags&FLAG_IOPL);
+ mvwprintw (dbg.win_reg,2,72,"%01X",GETFLAG(IOPL)>>12);
+
+
+ SetColor(cpu.cpl ^ oldcpucpl);
+ mvwprintw (dbg.win_reg,2,78,"%01X",cpu.cpl);
+ oldcpucpl=cpu.cpl;
+
+ if (cpu.pmode) {
+ if (reg_flags & FLAG_VM) mvwprintw(dbg.win_reg,0,76,"VM86");
+ else if (cpu.code.big) mvwprintw(dbg.win_reg,0,76,"Pr32");
+ else mvwprintw(dbg.win_reg,0,76,"Pr16");
+ } else
+ mvwprintw(dbg.win_reg,0,76,"Real");
+
+ // Selector info, if available
+ if ((cpu.pmode) && curSelectorName[0]) {
+ char out1[200], out2[200];
+ GetDescriptorInfo(curSelectorName,out1,out2);
+ mvwprintw(dbg.win_reg,2,28,out1);
+ mvwprintw(dbg.win_reg,3,28,out2);
+ }
+
+ wattrset(dbg.win_reg,0);
+ mvwprintw(dbg.win_reg,3,60,"%u ",cycle_count);
+ wrefresh(dbg.win_reg);
+};
+
+static void DrawCode(void) {
+ bool saveSel;
+ Bit32u disEIP = codeViewData.useEIP;
+ PhysPt start = GetAddress(codeViewData.useCS,codeViewData.useEIP);
+ char dline[200];Bitu size;Bitu c;
+ static char line20[21] = " ";
+
+ for (int i=0;i<10;i++) {
+ saveSel = false;
+ if (has_colors()) {
+ if ((codeViewData.useCS==SegValue(cs)) && (disEIP == reg_eip)) {
+ wattrset(dbg.win_code,COLOR_PAIR(PAIR_GREEN_BLACK));
+ if (codeViewData.cursorPos==-1) {
+ codeViewData.cursorPos = i; // Set Cursor
+ }
+ if (i == codeViewData.cursorPos) {
+ codeViewData.cursorSeg = SegValue(cs);
+ codeViewData.cursorOfs = disEIP;
+ }
+ saveSel = (i == codeViewData.cursorPos);
+ } else if (i == codeViewData.cursorPos) {
+ wattrset(dbg.win_code,COLOR_PAIR(PAIR_BLACK_GREY));
+ codeViewData.cursorSeg = codeViewData.useCS;
+ codeViewData.cursorOfs = disEIP;
+ saveSel = true;
+ } else if (CBreakpoint::IsBreakpoint(codeViewData.useCS, disEIP)) {
+ wattrset(dbg.win_code,COLOR_PAIR(PAIR_GREY_RED));
+ } else {
+ wattrset(dbg.win_code,0);
+ }
+ }
+
+
+ Bitu drawsize=size=DasmI386(dline, start, disEIP, cpu.code.big);
+ bool toolarge = false;
+ mvwprintw(dbg.win_code,i,0,"%04X:%04X ",codeViewData.useCS,disEIP);
+
+ if (drawsize>10) { toolarge = true; drawsize = 9; };
+ for (c=0;c<drawsize;c++) {
+ Bit8u value;
+ if (mem_readb_checked(start+c,&value)) value=0;
+ wprintw(dbg.win_code,"%02X",value);
+ }
+ if (toolarge) { waddstr(dbg.win_code,".."); drawsize++; };
+ // Spacepad up to 20 characters
+ if(drawsize && (drawsize < 11)) {
+ line20[20 - drawsize*2] = 0;
+ waddstr(dbg.win_code,line20);
+ line20[20 - drawsize*2] = ' ';
+ } else waddstr(dbg.win_code,line20);
+
+ char empty_res[] = { 0 };
+ char* res = empty_res;
+ if (showExtend) res = AnalyzeInstruction(dline, saveSel);
+ // Spacepad it up to 28 characters
+ size_t dline_len = strlen(dline);
+ if(dline_len < 28) for (c = dline_len; c < 28;c++) dline[c] = ' '; dline[28] = 0;
+ waddstr(dbg.win_code,dline);
+ // Spacepad it up to 20 characters
+ size_t res_len = strlen(res);
+ if(res_len && (res_len < 21)) {
+ waddstr(dbg.win_code,res);
+ line20[20-res_len] = 0;
+ waddstr(dbg.win_code,line20);
+ line20[20-res_len] = ' ';
+ } else waddstr(dbg.win_code,line20);
+
+ start+=size;
+ disEIP+=size;
+
+ if (i==0) codeViewData.firstInstSize = size;
+ if (i==4) codeViewData.useEIPmid = disEIP;
+ }
+
+ codeViewData.useEIPlast = disEIP;
+
+ wattrset(dbg.win_code,0);
+ if (!debugging) {
+ if (has_colors()) wattrset(dbg.win_code,COLOR_PAIR(PAIR_GREEN_BLACK));
+ mvwprintw(dbg.win_code,10,0,"%s","(Running)");
+ wclrtoeol(dbg.win_code);
+ } else {
+ //TODO long lines
+ char* dispPtr = codeViewData.inputStr;
+ char* curPtr = &codeViewData.inputStr[codeViewData.inputPos];
+ mvwprintw(dbg.win_code,10,0,"%c-> %s%c",
+ (codeViewData.ovrMode?'O':'I'),dispPtr,(*curPtr?' ':'_'));
+ wclrtoeol(dbg.win_code); // not correct in pdcurses if full line
+ mvwchgat(dbg.win_code,10,0,3,0,(PAIR_BLACK_GREY),NULL);
+ if (*curPtr) {
+ mvwchgat(dbg.win_code,10,(curPtr-dispPtr+4),1,0,(PAIR_BLACK_GREY),NULL);
+ }
+ }
+
+ wattrset(dbg.win_code,0);
+ wrefresh(dbg.win_code);
+}
+
+static void SetCodeWinStart()
+{
+ if ((SegValue(cs)==codeViewData.useCS) && (reg_eip>=codeViewData.useEIP) && (reg_eip<=codeViewData.useEIPlast)) {
+ // in valid window - scroll ?
+ if (reg_eip>=codeViewData.useEIPmid) codeViewData.useEIP += codeViewData.firstInstSize;
+
+ } else {
+ // totally out of range.
+ codeViewData.useCS = SegValue(cs);
+ codeViewData.useEIP = reg_eip;
+ }
+ codeViewData.cursorPos = -1; // Recalc Cursor position
+};
+
+/********************/
+/* User input */
+/********************/
+
+Bit32u GetHexValue(char* str, char*& hex)
+{
+ Bit32u value = 0;
+ Bit32u regval = 0;
+ hex = str;
+ while (*hex == ' ') hex++;
+ if (strncmp(hex,"EAX",3) == 0) { hex+=3; regval = reg_eax; } else
+ if (strncmp(hex,"EBX",3) == 0) { hex+=3; regval = reg_ebx; } else
+ if (strncmp(hex,"ECX",3) == 0) { hex+=3; regval = reg_ecx; } else
+ if (strncmp(hex,"EDX",3) == 0) { hex+=3; regval = reg_edx; } else
+ if (strncmp(hex,"ESI",3) == 0) { hex+=3; regval = reg_esi; } else
+ if (strncmp(hex,"EDI",3) == 0) { hex+=3; regval = reg_edi; } else
+ if (strncmp(hex,"EBP",3) == 0) { hex+=3; regval = reg_ebp; } else
+ if (strncmp(hex,"ESP",3) == 0) { hex+=3; regval = reg_esp; } else
+ if (strncmp(hex,"EIP",3) == 0) { hex+=3; regval = reg_eip; } else
+ if (strncmp(hex,"AX",2) == 0) { hex+=2; regval = reg_ax; } else
+ if (strncmp(hex,"BX",2) == 0) { hex+=2; regval = reg_bx; } else
+ if (strncmp(hex,"CX",2) == 0) { hex+=2; regval = reg_cx; } else
+ if (strncmp(hex,"DX",2) == 0) { hex+=2; regval = reg_dx; } else
+ if (strncmp(hex,"SI",2) == 0) { hex+=2; regval = reg_si; } else
+ if (strncmp(hex,"DI",2) == 0) { hex+=2; regval = reg_di; } else
+ if (strncmp(hex,"BP",2) == 0) { hex+=2; regval = reg_bp; } else
+ if (strncmp(hex,"SP",2) == 0) { hex+=2; regval = reg_sp; } else
+ if (strncmp(hex,"IP",2) == 0) { hex+=2; regval = reg_ip; } else
+ if (strncmp(hex,"CS",2) == 0) { hex+=2; regval = SegValue(cs); } else
+ if (strncmp(hex,"DS",2) == 0) { hex+=2; regval = SegValue(ds); } else
+ if (strncmp(hex,"ES",2) == 0) { hex+=2; regval = SegValue(es); } else
+ if (strncmp(hex,"FS",2) == 0) { hex+=2; regval = SegValue(fs); } else
+ if (strncmp(hex,"GS",2) == 0) { hex+=2; regval = SegValue(gs); } else
+ if (strncmp(hex,"SS",2) == 0) { hex+=2; regval = SegValue(ss); };
+
+ while (*hex) {
+ if ((*hex >= '0') && (*hex <= '9')) value = (value<<4) + *hex - '0';
+ else if ((*hex >= 'A') && (*hex <= 'F')) value = (value<<4) + *hex - 'A' + 10;
+ else {
+ if (*hex == '+') {hex++;return regval + value + GetHexValue(hex,hex); } else
+ if (*hex == '-') {hex++;return regval + value - GetHexValue(hex,hex); }
+ else break; // No valid char
+ }
+ hex++;
+ };
+ return regval + value;
+};
+
+bool ChangeRegister(char* str)
+{
+ char* hex = str;
+ while (*hex==' ') hex++;
+ if (strncmp(hex,"EAX",3) == 0) { hex+=3; reg_eax = GetHexValue(hex,hex); } else
+ if (strncmp(hex,"EBX",3) == 0) { hex+=3; reg_ebx = GetHexValue(hex,hex); } else
+ if (strncmp(hex,"ECX",3) == 0) { hex+=3; reg_ecx = GetHexValue(hex,hex); } else
+ if (strncmp(hex,"EDX",3) == 0) { hex+=3; reg_edx = GetHexValue(hex,hex); } else
+ if (strncmp(hex,"ESI",3) == 0) { hex+=3; reg_esi = GetHexValue(hex,hex); } else
+ if (strncmp(hex,"EDI",3) == 0) { hex+=3; reg_edi = GetHexValue(hex,hex); } else
+ if (strncmp(hex,"EBP",3) == 0) { hex+=3; reg_ebp = GetHexValue(hex,hex); } else
+ if (strncmp(hex,"ESP",3) == 0) { hex+=3; reg_esp = GetHexValue(hex,hex); } else
+ if (strncmp(hex,"EIP",3) == 0) { hex+=3; reg_eip = GetHexValue(hex,hex); } else
+ if (strncmp(hex,"AX",2) == 0) { hex+=2; reg_ax = (Bit16u)GetHexValue(hex,hex); } else
+ if (strncmp(hex,"BX",2) == 0) { hex+=2; reg_bx = (Bit16u)GetHexValue(hex,hex); } else
+ if (strncmp(hex,"CX",2) == 0) { hex+=2; reg_cx = (Bit16u)GetHexValue(hex,hex); } else
+ if (strncmp(hex,"DX",2) == 0) { hex+=2; reg_dx = (Bit16u)GetHexValue(hex,hex); } else
+ if (strncmp(hex,"SI",2) == 0) { hex+=2; reg_si = (Bit16u)GetHexValue(hex,hex); } else
+ if (strncmp(hex,"DI",2) == 0) { hex+=2; reg_di = (Bit16u)GetHexValue(hex,hex); } else
+ if (strncmp(hex,"BP",2) == 0) { hex+=2; reg_bp = (Bit16u)GetHexValue(hex,hex); } else
+ if (strncmp(hex,"SP",2) == 0) { hex+=2; reg_sp = (Bit16u)GetHexValue(hex,hex); } else
+ if (strncmp(hex,"IP",2) == 0) { hex+=2; reg_ip = (Bit16u)GetHexValue(hex,hex); } else
+ if (strncmp(hex,"CS",2) == 0) { hex+=2; SegSet16(cs,(Bit16u)GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"DS",2) == 0) { hex+=2; SegSet16(ds,(Bit16u)GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"ES",2) == 0) { hex+=2; SegSet16(es,(Bit16u)GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"FS",2) == 0) { hex+=2; SegSet16(fs,(Bit16u)GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"GS",2) == 0) { hex+=2; SegSet16(gs,(Bit16u)GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"SS",2) == 0) { hex+=2; SegSet16(ss,(Bit16u)GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"AF",2) == 0) { hex+=2; SETFLAGBIT(AF,GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"CF",2) == 0) { hex+=2; SETFLAGBIT(CF,GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"DF",2) == 0) { hex+=2; SETFLAGBIT(DF,GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"IF",2) == 0) { hex+=2; SETFLAGBIT(IF,GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"OF",2) == 0) { hex+=2; SETFLAGBIT(OF,GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"ZF",2) == 0) { hex+=2; SETFLAGBIT(ZF,GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"PF",2) == 0) { hex+=2; SETFLAGBIT(PF,GetHexValue(hex,hex)); } else
+ if (strncmp(hex,"SF",2) == 0) { hex+=2; SETFLAGBIT(SF,GetHexValue(hex,hex)); } else
+ { return false; };
+ return true;
+};
+
+bool ParseCommand(char* str) {
+ char* found = str;
+ for(char* idx = found;*idx != 0; idx++)
+ *idx = toupper(*idx);
+
+ found = trim(found);
+ string s_found(found);
+ istringstream stream(s_found);
+ string command;
+ stream >> command;
+ string::size_type next = s_found.find_first_not_of(' ',command.size());
+ if(next == string::npos) next = command.size();
+ (s_found.erase)(0,next);
+ found = const_cast<char*>(s_found.c_str());
+
+ if (command == "MEMDUMP") { // Dump memory to file
+ Bit16u seg = (Bit16u)GetHexValue(found,found); found++;
+ Bit32u ofs = GetHexValue(found,found); found++;
+ Bit32u num = GetHexValue(found,found); found++;
+ SaveMemory(seg,ofs,num);
+ return true;
+ };
+
+ if (command == "MEMDUMPBIN") { // Dump memory to file binary
+ Bit16u seg = (Bit16u)GetHexValue(found,found); found++;
+ Bit32u ofs = GetHexValue(found,found); found++;
+ Bit32u num = GetHexValue(found,found); found++;
+ SaveMemoryBin(seg,ofs,num);
+ return true;
+ };
+
+ if (command == "IV") { // Insert variable
+ Bit16u seg = (Bit16u)GetHexValue(found,found); found++;
+ Bit32u ofs = (Bit16u)GetHexValue(found,found); found++;
+ char name[16];
+ for (int i=0; i<16; i++) {
+ if (found[i] && (found[i]!=' ')) name[i] = found[i];
+ else { name[i] = 0; break; };
+ };
+ name[15] = 0;
+
+ if(!name[0]) return false;
+ DEBUG_ShowMsg("DEBUG: Created debug var %s at %04X:%04X\n",name,seg,ofs);
+ CDebugVar::InsertVariable(name,GetAddress(seg,ofs));
+ return true;
+ };
+
+ if (command == "SV") { // Save variables
+ char name[13];
+ for (int i=0; i<12; i++) {
+ if (found[i] && (found[i]!=' ')) name[i] = found[i];
+ else { name[i] = 0; break; };
+ };
+ name[12] = 0;
+ if(!name[0]) return false;
+ DEBUG_ShowMsg("DEBUG: Variable list save (%s) : %s.\n",name,(CDebugVar::SaveVars(name)?"ok":"failure"));
+ return true;
+ };
+
+ if (command == "LV") { // load variables
+ char name[13];
+ for (int i=0; i<12; i++) {
+ if (found[i] && (found[i]!=' ')) name[i] = found[i];
+ else { name[i] = 0; break; };
+ };
+ name[12] = 0;
+ if(!name[0]) return false;
+ DEBUG_ShowMsg("DEBUG: Variable list load (%s) : %s.\n",name,(CDebugVar::LoadVars(name)?"ok":"failure"));
+ return true;
+ };
+
+ if (command == "ADDLOG") {
+ if(found && *found) DEBUG_ShowMsg("NOTICE: %s\n",found);
+ return true;
+ };
+
+ if (command == "SR") { // Set register value
+ DEBUG_ShowMsg("DEBUG: Set Register %s.\n",(ChangeRegister(found)?"success":"failure"));
+ return true;
+ };
+
+ if (command == "SM") { // Set memory with following values
+ Bit16u seg = (Bit16u)GetHexValue(found,found); found++;
+ Bit32u ofs = GetHexValue(found,found); found++;
+ Bit16u count = 0;
+ while (*found) {
+ while (*found==' ') found++;
+ if (*found) {
+ Bit8u value = (Bit8u)GetHexValue(found,found);
+ if(*found) found++;
+ mem_writeb_checked(GetAddress(seg,ofs+count),value);
+ count++;
+ }
+ };
+ DEBUG_ShowMsg("DEBUG: Memory changed.\n");
+ return true;
+ };
+
+ if (command == "BP") { // Add new breakpoint
+ Bit16u seg = (Bit16u)GetHexValue(found,found);found++; // skip ":"
+ Bit32u ofs = GetHexValue(found,found);
+ CBreakpoint::AddBreakpoint(seg,ofs,false);
+ DEBUG_ShowMsg("DEBUG: Set breakpoint at %04X:%04X\n",seg,ofs);
+ return true;
+ };
+
+#if C_HEAVY_DEBUG
+
+ if (command == "BPM") { // Add new breakpoint
+ Bit16u seg = (Bit16u)GetHexValue(found,found);found++; // skip ":"
+ Bit32u ofs = GetHexValue(found,found);
+ CBreakpoint::AddMemBreakpoint(seg,ofs);
+ DEBUG_ShowMsg("DEBUG: Set memory breakpoint at %04X:%04X\n",seg,ofs);
+ return true;
+ };
+
+ if (command == "BPPM") { // Add new breakpoint
+ Bit16u seg = (Bit16u)GetHexValue(found,found);found++; // skip ":"
+ Bit32u ofs = GetHexValue(found,found);
+ CBreakpoint* bp = CBreakpoint::AddMemBreakpoint(seg,ofs);
+ if (bp) {
+ bp->SetType(BKPNT_MEMORY_PROT);
+ DEBUG_ShowMsg("DEBUG: Set prot-mode memory breakpoint at %04X:%08X\n",seg,ofs);
+ }
+ return true;
+ };
+
+ if (command == "BPLM") { // Add new breakpoint
+ Bit32u ofs = GetHexValue(found,found);
+ CBreakpoint* bp = CBreakpoint::AddMemBreakpoint(0,ofs);
+ if (bp) bp->SetType(BKPNT_MEMORY_LINEAR);
+ DEBUG_ShowMsg("DEBUG: Set linear memory breakpoint at %08X\n",ofs);
+ return true;
+ };
+
+#endif
+
+ if (command == "BPINT") { // Add Interrupt Breakpoint
+ Bit8u intNr = (Bit8u)GetHexValue(found,found);
+ bool all = !(*found);
+ Bit8u valAH = (Bit8u)GetHexValue(found,found);
+ if ((valAH==0x00) && (*found=='*' || all)) {
+ CBreakpoint::AddIntBreakpoint(intNr,BPINT_ALL,BPINT_ALL,false);
+ DEBUG_ShowMsg("DEBUG: Set interrupt breakpoint at INT %02X\n",intNr);
+ } else {
+ all = !(*found);
+ Bit8u valAL = (Bit8u)GetHexValue(found,found);
+ if ((valAL==0x00) && (*found=='*' || all)) {
+ CBreakpoint::AddIntBreakpoint(intNr,valAH,BPINT_ALL,false);
+ DEBUG_ShowMsg("DEBUG: Set interrupt breakpoint at INT %02X AH=%02X\n",intNr,valAH);
+ } else {
+ CBreakpoint::AddIntBreakpoint(intNr,valAH,valAL,false);
+ DEBUG_ShowMsg("DEBUG: Set interrupt breakpoint at INT %02X AH=%02X AL=%02X\n",intNr,valAH,valAL);
+ }
+ }
+ return true;
+ };
+
+ if (command == "BPLIST") {
+ DEBUG_ShowMsg("Breakpoint list:\n");
+ DEBUG_ShowMsg("-------------------------------------------------------------------------\n");
+ CBreakpoint::ShowList();
+ return true;
+ };
+
+ if (command == "BPDEL") { // Delete Breakpoints
+ Bit8u bpNr = (Bit8u)GetHexValue(found,found);
+ if ((bpNr==0x00) && (*found=='*')) { // Delete all
+ CBreakpoint::DeleteAll();
+ DEBUG_ShowMsg("DEBUG: Breakpoints deleted.\n");
+ } else {
+ // delete single breakpoint
+ DEBUG_ShowMsg("DEBUG: Breakpoint deletion %s.\n",(CBreakpoint::DeleteByIndex(bpNr)?"success":"failure"));
+ }
+ return true;
+ };
+
+ if (command == "C") { // Set code overview
+ Bit16u codeSeg = (Bit16u)GetHexValue(found,found); found++;
+ Bit32u codeOfs = GetHexValue(found,found);
+ DEBUG_ShowMsg("DEBUG: Set code overview to %04X:%04X\n",codeSeg,codeOfs);
+ codeViewData.useCS = codeSeg;
+ codeViewData.useEIP = codeOfs;
+ codeViewData.cursorPos = 0;
+ return true;
+ };
+
+ if (command == "D") { // Set data overview
+ dataSeg = (Bit16u)GetHexValue(found,found); found++;
+ dataOfs = GetHexValue(found,found);
+ DEBUG_ShowMsg("DEBUG: Set data overview to %04X:%04X\n",dataSeg,dataOfs);
+ return true;
+ };
+
+#if C_HEAVY_DEBUG
+
+ if (command == "LOG") { // Create Cpu normal log file
+ cpuLogType = 1;
+ command = "logcode";
+ }
+
+ if (command == "LOGS") { // Create Cpu short log file
+ cpuLogType = 0;
+ command = "logcode";
+ }
+
+ if (command == "LOGL") { // Create Cpu long log file
+ cpuLogType = 2;
+ command = "logcode";
+ }
+
+ if (command == "logcode") { //Shared code between all logs
+ DEBUG_ShowMsg("DEBUG: Starting log\n");
+ cpuLogFile.open("LOGCPU.TXT");
+ if (!cpuLogFile.is_open()) {
+ DEBUG_ShowMsg("DEBUG: Logfile couldn't be created.\n");
+ return false;
+ }
+ //Initialize log object
+ cpuLogFile << hex << noshowbase << setfill('0') << uppercase;
+ cpuLog = true;
+ cpuLogCounter = GetHexValue(found,found);
+
+ debugging = false;
+ CBreakpoint::ActivateBreakpointsExceptAt(SegPhys(cs)+reg_eip);
+ DOSBOX_SetNormalLoop();
+ return true;
+ };
+
+#endif
+
+ if (command == "INTT") { //trace int.
+ Bit8u intNr = (Bit8u)GetHexValue(found,found);
+ DEBUG_ShowMsg("DEBUG: Tracing INT %02X\n",intNr);
+ CPU_HW_Interrupt(intNr);
+ SetCodeWinStart();
+ return true;
+ };
+
+ if (command == "INT") { // start int.
+ Bit8u intNr = (Bit8u)GetHexValue(found,found);
+ DEBUG_ShowMsg("DEBUG: Starting INT %02X\n",intNr);
+ CBreakpoint::AddBreakpoint(SegValue(cs),reg_eip, true);
+ CBreakpoint::ActivateBreakpointsExceptAt(SegPhys(cs)+reg_eip-1);
+ debugging = false;
+ DrawCode();
+ DOSBOX_SetNormalLoop();
+ CPU_HW_Interrupt(intNr);
+ return true;
+ };
+
+ if (command == "SELINFO") {
+ while (found[0] == ' ') found++;
+ char out1[200],out2[200];
+ GetDescriptorInfo(found,out1,out2);
+ DEBUG_ShowMsg("SelectorInfo %s:\n%s\n%s\n",found,out1,out2);
+ return true;
+ };
+
+ if (command == "DOS") {
+ stream >> command;
+ if (command == "MCBS") LogMCBS();
+ return true;
+ }
+
+ if (command == "GDT") {LogGDT(); return true;}
+
+ if (command == "LDT") {LogLDT(); return true;}
+
+ if (command == "IDT") {LogIDT(); return true;}
+
+ if (command == "PAGING") {LogPages(found); return true;}
+
+ if (command == "CPU") {LogCPUInfo(); return true;}
+
+ if (command == "INTVEC") {
+ if (found[0] != 0) {
+ OutputVecTable(found);
+ return true;
+ }
+ };
+
+ if (command == "INTHAND") {
+ if (found[0] != 0) {
+ Bit8u intNr = (Bit8u)GetHexValue(found,found);
+ DEBUG_ShowMsg("DEBUG: Set code overview to interrupt handler %X\n",intNr);
+ codeViewData.useCS = mem_readw(intNr*4+2);
+ codeViewData.useEIP = mem_readw(intNr*4);
+ codeViewData.cursorPos = 0;
+ return true;
+ }
+ };
+
+ if(command == "EXTEND") { //Toggle additional data.
+ showExtend = !showExtend;
+ return true;
+ };
+
+ if(command == "TIMERIRQ") { //Start a timer irq
+ DEBUG_RaiseTimerIrq();
+ DEBUG_ShowMsg("Debug: Timer Int started.\n");
+ return true;
+ };
+
+
+#if C_HEAVY_DEBUG
+ if (command == "HEAVYLOG") { // Create Cpu log file
+ logHeavy = !logHeavy;
+ DEBUG_ShowMsg("DEBUG: Heavy cpu logging %s.\n",logHeavy?"on":"off");
+ return true;
+ };
+
+ if (command == "ZEROPROTECT") { //toggle zero protection
+ zeroProtect = !zeroProtect;
+ DEBUG_ShowMsg("DEBUG: Zero code execution protection %s.\n",zeroProtect?"on":"off");
+ return true;
+ };
+
+#endif
+ if (command == "HELP" || command == "?") {
+ DEBUG_ShowMsg("Debugger commands (enter all values in hex or as register):\n");
+ DEBUG_ShowMsg("Commands------------------------------------------------\n");
+ DEBUG_ShowMsg("BP [segment]:[offset] - Set breakpoint.\n");
+ DEBUG_ShowMsg("BPINT [intNr] * - Set interrupt breakpoint.\n");
+ DEBUG_ShowMsg("BPINT [intNr] [ah] * - Set interrupt breakpoint with ah.\n");
+ DEBUG_ShowMsg("BPINT [intNr] [ah] [al] - Set interrupt breakpoint with ah and al.\n");
+#if C_HEAVY_DEBUG
+ DEBUG_ShowMsg("BPM [segment]:[offset] - Set memory breakpoint (memory change).\n");
+ DEBUG_ShowMsg("BPPM [selector]:[offset]- Set pmode-memory breakpoint (memory change).\n");
+ DEBUG_ShowMsg("BPLM [linear address] - Set linear memory breakpoint (memory change).\n");
+#endif
+ DEBUG_ShowMsg("BPLIST - List breakpoints.\n");
+ DEBUG_ShowMsg("BPDEL [bpNr] / * - Delete breakpoint nr / all.\n");
+ DEBUG_ShowMsg("C / D [segment]:[offset] - Set code / data view address.\n");
+ DEBUG_ShowMsg("DOS MCBS - Show Memory Control Block chain.\n");
+ DEBUG_ShowMsg("INT [nr] / INTT [nr] - Execute / Trace into interrupt.\n");
+#if C_HEAVY_DEBUG
+ DEBUG_ShowMsg("LOG [num] - Write cpu log file.\n");
+ DEBUG_ShowMsg("LOGS/LOGL [num] - Write short/long cpu log file.\n");
+ DEBUG_ShowMsg("HEAVYLOG - Enable/Disable automatic cpu log when dosbox exits.\n");
+ DEBUG_ShowMsg("ZEROPROTECT - Enable/Disable zero code execution detection.\n");
+#endif
+ DEBUG_ShowMsg("SR [reg] [value] - Set register value.\n");
+ DEBUG_ShowMsg("SM [seg]:[off] [val] [.]..- Set memory with following values.\n");
+
+ DEBUG_ShowMsg("IV [seg]:[off] [name] - Create var name for memory address.\n");
+ DEBUG_ShowMsg("SV [filename] - Save var list in file.\n");
+ DEBUG_ShowMsg("LV [filename] - Load var list from file.\n");
+
+ DEBUG_ShowMsg("ADDLOG [message] - Add message to the log file.\n");
+
+ DEBUG_ShowMsg("MEMDUMP [seg]:[off] [len] - Write memory to file memdump.txt.\n");
+ DEBUG_ShowMsg("MEMDUMPBIN [s]:[o] [len] - Write memory to file memdump.bin.\n");
+ DEBUG_ShowMsg("SELINFO [segName] - Show selector info.\n");
+
+ DEBUG_ShowMsg("INTVEC [filename] - Writes interrupt vector table to file.\n");
+ DEBUG_ShowMsg("INTHAND [intNum] - Set code view to interrupt handler.\n");
+
+ DEBUG_ShowMsg("CPU - Display CPU status information.\n");
+ DEBUG_ShowMsg("GDT - Lists descriptors of the GDT.\n");
+ DEBUG_ShowMsg("LDT - Lists descriptors of the LDT.\n");
+ DEBUG_ShowMsg("IDT - Lists descriptors of the IDT.\n");
+ DEBUG_ShowMsg("PAGING [page] - Display content of page table.\n");
+ DEBUG_ShowMsg("EXTEND - Toggle additional info.\n");
+ DEBUG_ShowMsg("TIMERIRQ - Run the system timer.\n");
+
+ DEBUG_ShowMsg("HELP - Help\n");
+ DEBUG_ShowMsg("Keys------------------------------------------------\n");
+ DEBUG_ShowMsg("F3/F6 - Previous command in history.\n");
+ DEBUG_ShowMsg("F4/F7 - Next command in history.\n");
+ DEBUG_ShowMsg("F5 - Run.\n");
+ DEBUG_ShowMsg("F9 - Set/Remove breakpoint.\n");
+ DEBUG_ShowMsg("F10/F11 - Step over / trace into instruction.\n");
+ DEBUG_ShowMsg("ALT + D/E/S/X/B - Set data view to DS:SI/ES:DI/SS:SP/DS:DX/ES:BX.\n");
+ DEBUG_ShowMsg("Escape - Clear input line.");
+ DEBUG_ShowMsg("Up/Down - Move code view cursor.\n");
+ DEBUG_ShowMsg("Page Up/Down - Scroll data view.\n");
+ DEBUG_ShowMsg("Home/End - Scroll log messages.\n");
+
+ return true;
+ };
+ return false;
+};
+
+char* AnalyzeInstruction(char* inst, bool saveSelector) {
+ static char result[256];
+
+ char instu[256];
+ char prefix[3];
+ Bit16u seg;
+
+ strcpy(instu,inst);
+ upcase(instu);
+
+ result[0] = 0;
+ char* pos = strchr(instu,'[');
+ if (pos) {
+ // Segment prefix ?
+ if (*(pos-1)==':') {
+ char* segpos = pos-3;
+ prefix[0] = tolower(*segpos);
+ prefix[1] = tolower(*(segpos+1));
+ prefix[2] = 0;
+ seg = (Bit16u)GetHexValue(segpos,segpos);
+ } else {
+ if (strstr(pos,"SP") || strstr(pos,"BP")) {
+ seg = SegValue(ss);
+ strcpy(prefix,"ss");
+ } else {
+ seg = SegValue(ds);
+ strcpy(prefix,"ds");
+ };
+ };
+
+ pos++;
+ Bit32u adr = GetHexValue(pos,pos);
+ while (*pos!=']') {
+ if (*pos=='+') {
+ pos++;
+ adr += GetHexValue(pos,pos);
+ } else if (*pos=='-') {
+ pos++;
+ adr -= GetHexValue(pos,pos);
+ } else
+ pos++;
+ };
+ Bit32u address = GetAddress(seg,adr);
+ if (!(get_tlb_readhandler(address)->flags & PFLAG_INIT)) {
+ static char outmask[] = "%s:[%04X]=%02X";
+
+ if (cpu.pmode) outmask[6] = '8';
+ switch (DasmLastOperandSize()) {
+ case 8 : { Bit8u val = mem_readb(address);
+ outmask[12] = '2';
+ sprintf(result,outmask,prefix,adr,val);
+ } break;
+ case 16: { Bit16u val = mem_readw(address);
+ outmask[12] = '4';
+ sprintf(result,outmask,prefix,adr,val);
+ } break;
+ case 32: { Bit32u val = mem_readd(address);
+ outmask[12] = '8';
+ sprintf(result,outmask,prefix,adr,val);
+ } break;
+ }
+ } else {
+ sprintf(result,"[illegal]");
+ }
+ // Variable found ?
+ CDebugVar* var = CDebugVar::FindVar(address);
+ if (var) {
+ // Replace occurrence
+ char* pos1 = strchr(inst,'[');
+ char* pos2 = strchr(inst,']');
+ if (pos1 && pos2) {
+ char temp[256];
+ strcpy(temp,pos2); // save end
+ pos1++; *pos1 = 0; // cut after '['
+ strcat(inst,var->GetName()); // add var name
+ strcat(inst,temp); // add end
+ };
+ };
+ // show descriptor info, if available
+ if ((cpu.pmode) && saveSelector) {
+ strcpy(curSelectorName,prefix);
+ };
+ };
+ // If it is a callback add additional info
+ pos = strstr(inst,"callback");
+ if (pos) {
+ pos += 9;
+ Bitu nr = GetHexValue(pos,pos);
+ const char* descr = CALLBACK_GetDescription(nr);
+ if (descr) {
+ strcat(inst," ("); strcat(inst,descr); strcat(inst,")");
+ }
+ };
+ // Must be a jump
+ if (instu[0] == 'J')
+ {
+ bool jmp = false;
+ switch (instu[1]) {
+ case 'A' : { jmp = (get_CF()?false:true) && (get_ZF()?false:true); // JA
+ } break;
+ case 'B' : { if (instu[2] == 'E') {
+ jmp = (get_CF()?true:false) || (get_ZF()?true:false); // JBE
+ } else {
+ jmp = get_CF()?true:false; // JB
+ }
+ } break;
+ case 'C' : { if (instu[2] == 'X') {
+ jmp = reg_cx == 0; // JCXZ
+ } else {
+ jmp = get_CF()?true:false; // JC
+ }
+ } break;
+ case 'E' : { jmp = get_ZF()?true:false; // JE
+ } break;
+ case 'G' : { if (instu[2] == 'E') {
+ jmp = (get_SF()?true:false)==(get_OF()?true:false); // JGE
+ } else {
+ jmp = (get_ZF()?false:true) && ((get_SF()?true:false)==(get_OF()?true:false)); // JG
+ }
+ } break;
+ case 'L' : { if (instu[2] == 'E') {
+ jmp = (get_ZF()?true:false) || ((get_SF()?true:false)!=(get_OF()?true:false)); // JLE
+ } else {
+ jmp = (get_SF()?true:false)!=(get_OF()?true:false); // JL
+ }
+ } break;
+ case 'M' : { jmp = true; // JMP
+ } break;
+ case 'N' : { switch (instu[2]) {
+ case 'B' :
+ case 'C' : { jmp = get_CF()?false:true; // JNB / JNC
+ } break;
+ case 'E' : { jmp = get_ZF()?false:true; // JNE
+ } break;
+ case 'O' : { jmp = get_OF()?false:true; // JNO
+ } break;
+ case 'P' : { jmp = get_PF()?false:true; // JNP
+ } break;
+ case 'S' : { jmp = get_SF()?false:true; // JNS
+ } break;
+ case 'Z' : { jmp = get_ZF()?false:true; // JNZ
+ } break;
+ }
+ } break;
+ case 'O' : { jmp = get_OF()?true:false; // JO
+ } break;
+ case 'P' : { if (instu[2] == 'O') {
+ jmp = get_PF()?false:true; // JPO
+ } else {
+ jmp = get_SF()?true:false; // JP / JPE
+ }
+ } break;
+ case 'S' : { jmp = get_SF()?true:false; // JS
+ } break;
+ case 'Z' : { jmp = get_ZF()?true:false; // JZ
+ } break;
+ }
+ if (jmp) {
+ pos = strchr(instu,'$');
+ if (pos) {
+ pos = strchr(instu,'+');
+ if (pos) {
+ strcpy(result,"(down)");
+ } else {
+ strcpy(result,"(up)");
+ }
+ }
+ } else {
+ sprintf(result,"(no jmp)");
+ }
+ }
+ return result;
+};
+
+
+Bit32u DEBUG_CheckKeys(void) {
+ Bits ret=0;
+ bool numberrun = false;
+ bool skipDraw = false;
+ int key=getch();
+
+ if (key >='1' && key <='5' && strlen(codeViewData.inputStr) == 0) {
+ const Bit32s v[] ={5,500,1000,5000,10000};
+ CPU_Cycles= v[key - '1'];
+
+ skipFirstInstruction = true;
+
+ ret = (*cpudecoder)();
+ SetCodeWinStart();
+
+ /* Setup variables so we end up at the proper ret processing */
+ numberrun = true;
+
+ // Don't redraw the screen if it's going to get redrawn immediately
+ // afterwards, to avoid resetting oldregs.
+ if (ret == debugCallback)
+ skipDraw = true;
+ key = -1;
+ }
+
+ if (key>0 || numberrun) {
+#if defined(WIN32) && defined(__PDCURSES__)
+ switch (key) {
+ case PADENTER: key=0x0A; break;
+ case PADSLASH: key='/'; break;
+ case PADSTAR: key='*'; break;
+ case PADMINUS: key='-'; break;
+ case PADPLUS: key='+'; break;
+ case ALT_D:
+ if (ungetch('D') != ERR) key=27;
+ break;
+ case ALT_E:
+ if (ungetch('E') != ERR) key=27;
+ break;
+ case ALT_X:
+ if (ungetch('X') != ERR) key=27;
+ break;
+ case ALT_B:
+ if (ungetch('B') != ERR) key=27;
+ break;
+ case ALT_S:
+ if (ungetch('S') != ERR) key=27;
+ break;
+ }
+#endif
+ switch (toupper(key)) {
+ case 27: // escape (a bit slow): Clears line. and processes alt commands.
+ key=getch();
+ if(key < 0) { //Purely escape Clear line
+ ClearInputLine();
+ break;
+ }
+
+ switch(toupper(key)) {
+ case 'D' : // ALT - D: DS:SI
+ dataSeg = SegValue(ds);
+ if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_esi;
+ else dataOfs = reg_si;
+ break;
+ case 'E' : //ALT - E: es:di
+ dataSeg = SegValue(es);
+ if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_edi;
+ else dataOfs = reg_di;
+ break;
+ case 'X': //ALT - X: ds:dx
+ dataSeg = SegValue(ds);
+ if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_edx;
+ else dataOfs = reg_dx;
+ break;
+ case 'B' : //ALT -B: es:bx
+ dataSeg = SegValue(es);
+ if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_ebx;
+ else dataOfs = reg_bx;
+ break;
+ case 'S': //ALT - S: ss:sp
+ dataSeg = SegValue(ss);
+ if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_esp;
+ else dataOfs = reg_sp;
+ break;
+ default:
+ break;
+ }
+ break;
+ case KEY_PPAGE : dataOfs -= 16; break;
+ case KEY_NPAGE : dataOfs += 16; break;
+
+ case KEY_DOWN: // down
+ if (codeViewData.cursorPos<9) codeViewData.cursorPos++;
+ else codeViewData.useEIP += codeViewData.firstInstSize;
+ break;
+ case KEY_UP: // up
+ if (codeViewData.cursorPos>0) codeViewData.cursorPos--;
+ else {
+ Bitu bytes = 0;
+ char dline[200];
+ Bitu size = 0;
+ Bit32u newEIP = codeViewData.useEIP - 1;
+ if(codeViewData.useEIP) {
+ for (; bytes < 10; bytes++) {
+ PhysPt start = GetAddress(codeViewData.useCS,newEIP);
+ size = DasmI386(dline, start, newEIP, cpu.code.big);
+ if(codeViewData.useEIP == newEIP+size) break;
+ newEIP--;
+ }
+ if (bytes>=10) newEIP = codeViewData.useEIP - 1;
+ }
+ codeViewData.useEIP = newEIP;
+ }
+ break;
+ case KEY_HOME: // Home: scroll log page up
+ DEBUG_RefreshPage(-1);
+ break;
+ case KEY_END: // End: scroll log page down
+ DEBUG_RefreshPage(1);
+ break;
+ case KEY_IC: // Insert: toggle insert/overwrite
+ codeViewData.ovrMode = !codeViewData.ovrMode;
+ break;
+ case KEY_LEFT: // move to the left in command line
+ if (codeViewData.inputPos > 0) codeViewData.inputPos--;
+ break;
+ case KEY_RIGHT: // move to the right in command line
+ if (codeViewData.inputStr[codeViewData.inputPos]) codeViewData.inputPos++;
+ break;
+ case KEY_F(6): // previous command (f1-f4 generate rubbish at my place)
+ case KEY_F(3): // previous command
+ if (histBuffPos == histBuff.begin()) break;
+ if (histBuffPos == histBuff.end()) {
+ // copy inputStr to suspInputStr so we can restore it
+ safe_strncpy(codeViewData.suspInputStr, codeViewData.inputStr, sizeof(codeViewData.suspInputStr));
+ }
+ safe_strncpy(codeViewData.inputStr,(*--histBuffPos).c_str(),sizeof(codeViewData.inputStr));
+ codeViewData.inputPos = strlen(codeViewData.inputStr);
+ break;
+ case KEY_F(7): // next command (f1-f4 generate rubbish at my place)
+ case KEY_F(4): // next command
+ if (histBuffPos == histBuff.end()) break;
+ if (++histBuffPos != histBuff.end()) {
+ safe_strncpy(codeViewData.inputStr,(*histBuffPos).c_str(),sizeof(codeViewData.inputStr));
+ } else {
+ // copy suspInputStr back into inputStr
+ safe_strncpy(codeViewData.inputStr, codeViewData.suspInputStr, sizeof(codeViewData.inputStr));
+ }
+ codeViewData.inputPos = strlen(codeViewData.inputStr);
+ break;
+ case KEY_F(5): // Run Program
+ debugging=false;
+ DrawCode(); // update code window to show "running" status
+
+ skipFirstInstruction = true; // for heavy debugger
+ CPU_Cycles = 1;
+ ret=(*cpudecoder)();
+
+ // ensure all breakpoints are activated
+ CBreakpoint::ActivateBreakpoints();
+
+ skipDraw = true; // don't update screen after this instruction
+
+ DOSBOX_SetNormalLoop();
+ break;
+ case KEY_F(9): // Set/Remove Breakpoint
+ if (CBreakpoint::IsBreakpoint(codeViewData.cursorSeg, codeViewData.cursorOfs)) {
+ if (CBreakpoint::DeleteBreakpoint(codeViewData.cursorSeg, codeViewData.cursorOfs))
+ DEBUG_ShowMsg("DEBUG: Breakpoint deletion success.\n");
+ else
+ DEBUG_ShowMsg("DEBUG: Failed to delete breakpoint.\n");
+ }
+ else {
+ CBreakpoint::AddBreakpoint(codeViewData.cursorSeg, codeViewData.cursorOfs, false);
+ DEBUG_ShowMsg("DEBUG: Set breakpoint at %04X:%04X\n",codeViewData.cursorSeg,codeViewData.cursorOfs);
+ }
+ break;
+ case KEY_F(10): // Step over inst
+ if (StepOver()) {
+ skipFirstInstruction = true; // for heavy debugger
+ CPU_Cycles = 1;
+ ret=(*cpudecoder)();
+
+ DOSBOX_SetNormalLoop();
+
+ // ensure all breakpoints are activated
+ CBreakpoint::ActivateBreakpoints();
+ skipDraw = true;
+ break;
+ }
+ // If we aren't stepping over something, do a normal step.
+ // NB: Fall-through
+ case KEY_F(11): // trace into
+ exitLoop = false;
+ skipFirstInstruction = true; // for heavy debugger
+ CPU_Cycles = 1;
+ ret = (*cpudecoder)();
+ SetCodeWinStart();
+ break;
+ case 0x0A: //Parse typed Command
+ codeViewData.inputStr[MAXCMDLEN] = '\0';
+ if(ParseCommand(codeViewData.inputStr)) {
+ char* cmd = ltrim(codeViewData.inputStr);
+ if (histBuff.empty() || *--histBuff.end()!=cmd)
+ histBuff.push_back(cmd);
+ if (histBuff.size() > MAX_HIST_BUFFER) histBuff.pop_front();
+ histBuffPos = histBuff.end();
+ ClearInputLine();
+ } else {
+ codeViewData.inputPos = strlen(codeViewData.inputStr);
+ }
+ break;
+ case KEY_BACKSPACE: //backspace (linux)
+ case 0x7f: // backspace in some terminal emulators (linux)
+ case 0x08: // delete
+ if (codeViewData.inputPos == 0) break;
+ codeViewData.inputPos--;
+ // fallthrough
+ case KEY_DC: // delete character
+ if ((codeViewData.inputPos<0) || (codeViewData.inputPos>=MAXCMDLEN)) break;
+ if (codeViewData.inputStr[codeViewData.inputPos] != 0) {
+ codeViewData.inputStr[MAXCMDLEN] = '\0';
+ for(char* p=&codeViewData.inputStr[codeViewData.inputPos];(*p=*(p+1));p++) {}
+ }
+ break;
+ default:
+ if ((key>=32) && (key<127)) {
+ if ((codeViewData.inputPos<0) || (codeViewData.inputPos>=MAXCMDLEN)) break;
+ codeViewData.inputStr[MAXCMDLEN] = '\0';
+ if (codeViewData.inputStr[codeViewData.inputPos] == 0) {
+ codeViewData.inputStr[codeViewData.inputPos++] = char(key);
+ codeViewData.inputStr[codeViewData.inputPos] = '\0';
+ } else if (!codeViewData.ovrMode) {
+ int len = (int) strlen(codeViewData.inputStr);
+ if (len < MAXCMDLEN) {
+ for(len++;len>codeViewData.inputPos;len--)
+ codeViewData.inputStr[len]=codeViewData.inputStr[len-1];
+ codeViewData.inputStr[codeViewData.inputPos++] = char(key);
+ }
+ } else {
+ codeViewData.inputStr[codeViewData.inputPos++] = char(key);
+ }
+ } else if (key==killchar()) {
+ ClearInputLine();
+ }
+ break;
+ }
+ if (ret<0) return ret;
+ if (ret>0) {
+ if (GCC_UNLIKELY(ret >= CB_MAX))
+ ret = 0;
+ else
+ ret = (*CallBack_Handlers[ret])();
+ if (ret) {
+ exitLoop=true;
+ CPU_Cycles=CPU_CycleLeft=0;
+ return ret;
+ }
+ }
+ ret=0;
+ if (!skipDraw)
+ DEBUG_DrawScreen();
+ }
+ return ret;
+};
+
+Bitu DEBUG_Loop(void) {
+//TODO Disable sound
+ GFX_Events();
+ // Interrupt started ? - then skip it
+ Bit16u oldCS = SegValue(cs);
+ Bit32u oldEIP = reg_eip;
+ PIC_runIRQs();
+ SDL_Delay(1);
+ if ((oldCS!=SegValue(cs)) || (oldEIP!=reg_eip)) {
+ CBreakpoint::AddBreakpoint(oldCS,oldEIP,true);
+ CBreakpoint::ActivateBreakpointsExceptAt(SegPhys(cs)+reg_eip);
+ debugging=false;
+ DOSBOX_SetNormalLoop();
+ return 0;
+ }
+ return DEBUG_CheckKeys();
+}
+
+void DEBUG_Enable(bool pressed) {
+ if (!pressed)
+ return;
+ static bool showhelp=false;
+ debugging=true;
+ SetCodeWinStart();
+ DEBUG_DrawScreen();
+ DOSBOX_SetLoop(&DEBUG_Loop);
+ if(!showhelp) {
+ showhelp=true;
+ DEBUG_ShowMsg("***| TYPE HELP (+ENTER) TO GET AN OVERVIEW OF ALL COMMANDS |***\n");
+ }
+ KEYBOARD_ClrBuffer();
+}
+
+void DEBUG_DrawScreen(void) {
+ DrawData();
+ DrawCode();
+ DrawRegisters();
+ DrawVariables();
+}
+
+static void DEBUG_RaiseTimerIrq(void) {
+ PIC_ActivateIRQ(0);
+}
+
+// Display the content of the MCB chain starting with the MCB at the specified segment.
+static void LogMCBChain(Bit16u mcb_segment) {
+ DOS_MCB mcb(mcb_segment);
+ char filename[9]; // 8 characters plus a terminating NUL
+ const char *psp_seg_note;
+ Bit16u DOS_dataOfs = static_cast<Bit16u>(dataOfs); //Realmode addressing only
+ PhysPt dataAddr = PhysMake(dataSeg,DOS_dataOfs);// location being viewed in the "Data Overview"
+
+ // loop forever, breaking out of the loop once we've processed the last MCB
+ while (true) {
+ // verify that the type field is valid
+ if (mcb.GetType()!=0x4d && mcb.GetType()!=0x5a) {
+ LOG(LOG_MISC,LOG_ERROR)("MCB chain broken at %04X:0000!",mcb_segment);
+ return;
+ }
+
+ mcb.GetFileName(filename);
+
+ // some PSP segment values have special meanings
+ switch (mcb.GetPSPSeg()) {
+ case MCB_FREE:
+ psp_seg_note = "(free)";
+ break;
+ case MCB_DOS:
+ psp_seg_note = "(DOS)";
+ break;
+ default:
+ psp_seg_note = "";
+ }
+
+ LOG(LOG_MISC,LOG_ERROR)(" %04X %12u %04X %-7s %s",mcb_segment,mcb.GetSize() << 4,mcb.GetPSPSeg(), psp_seg_note, filename);
+
+ // print a message if dataAddr is within this MCB's memory range
+ PhysPt mcbStartAddr = PhysMake(mcb_segment+1,0);
+ PhysPt mcbEndAddr = PhysMake(mcb_segment+1+mcb.GetSize(),0);
+ if (dataAddr >= mcbStartAddr && dataAddr < mcbEndAddr) {
+ LOG(LOG_MISC,LOG_ERROR)(" (data addr %04hX:%04X is %u bytes past this MCB)",dataSeg,DOS_dataOfs,dataAddr - mcbStartAddr);
+ }
+
+ // if we've just processed the last MCB in the chain, break out of the loop
+ if (mcb.GetType()==0x5a) {
+ break;
+ }
+ // else, move to the next MCB in the chain
+ mcb_segment+=mcb.GetSize()+1;
+ mcb.SetPt(mcb_segment);
+ }
+}
+
+// Display the content of all Memory Control Blocks.
+static void LogMCBS(void)
+{
+ LOG(LOG_MISC,LOG_ERROR)("MCB Seg Size (bytes) PSP Seg (notes) Filename");
+ LOG(LOG_MISC,LOG_ERROR)("Conventional memory:");
+ LogMCBChain(dos.firstMCB);
+
+ LOG(LOG_MISC,LOG_ERROR)("Upper memory:");
+ LogMCBChain(dos_infoblock.GetStartOfUMBChain());
+}
+
+static void LogGDT(void)
+{
+ char out1[512];
+ Descriptor desc;
+ Bitu length = cpu.gdt.GetLimit();
+ PhysPt address = cpu.gdt.GetBase();
+ PhysPt max = address + length;
+ Bitu i = 0;
+ LOG(LOG_MISC,LOG_ERROR)("GDT Base:%08X Limit:%08X",address,length);
+ while (address<max) {
+ desc.Load(address);
+ sprintf(out1,"%04X: b:%08X type: %02X parbg",(i<<3),desc.GetBase(),desc.saved.seg.type);
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ sprintf(out1," l:%08X dpl : %01X %1X%1X%1X%1X%1X",desc.GetLimit(),desc.saved.seg.dpl,desc.saved.seg.p,desc.saved.seg.avl,desc.saved.seg.r,desc.saved.seg.big,desc.saved.seg.g);
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ address+=8; i++;
+ };
+};
+
+static void LogLDT(void) {
+ char out1[512];
+ Descriptor desc;
+ Bitu ldtSelector = cpu.gdt.SLDT();
+ if (!cpu.gdt.GetDescriptor(ldtSelector,desc)) return;
+ Bitu length = desc.GetLimit();
+ PhysPt address = desc.GetBase();
+ PhysPt max = address + length;
+ Bitu i = 0;
+ LOG(LOG_MISC,LOG_ERROR)("LDT Base:%08X Limit:%08X",address,length);
+ while (address<max) {
+ desc.Load(address);
+ sprintf(out1,"%04X: b:%08X type: %02X parbg",(i<<3)|4,desc.GetBase(),desc.saved.seg.type);
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ sprintf(out1," l:%08X dpl : %01X %1X%1X%1X%1X%1X",desc.GetLimit(),desc.saved.seg.dpl,desc.saved.seg.p,desc.saved.seg.avl,desc.saved.seg.r,desc.saved.seg.big,desc.saved.seg.g);
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ address+=8; i++;
+ };
+};
+
+static void LogIDT(void) {
+ char out1[512];
+ Descriptor desc;
+ Bitu address = 0;
+ while (address<256*8) {
+ if (cpu.idt.GetDescriptor(address,desc)) {
+ sprintf(out1,"%04X: sel:%04X off:%02X",address/8,desc.GetSelector(),desc.GetOffset());
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ }
+ address+=8;
+ };
+};
+
+void LogPages(char* selname) {
+ char out1[512];
+ if (paging.enabled) {
+ Bitu sel = GetHexValue(selname,selname);
+ if ((sel==0x00) && ((*selname==0) || (*selname=='*'))) {
+ for (int i=0; i<0xfffff; i++) {
+ Bitu table_addr=(paging.base.page<<12)+(i >> 10)*4;
+ X86PageEntry table;
+ table.load=phys_readd(table_addr);
+ if (table.block.p) {
+ X86PageEntry entry;
+ Bitu entry_addr=(table.block.base<<12)+(i & 0x3ff)*4;
+ entry.load=phys_readd(entry_addr);
+ if (entry.block.p) {
+ sprintf(out1,"page %05Xxxx -> %04Xxxx flags [uw] %x:%x::%x:%x [d=%x|a=%x]",
+ i,entry.block.base,entry.block.us,table.block.us,
+ entry.block.wr,table.block.wr,entry.block.d,entry.block.a);
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ }
+ }
+ }
+ } else {
+ Bitu table_addr=(paging.base.page<<12)+(sel >> 10)*4;
+ X86PageEntry table;
+ table.load=phys_readd(table_addr);
+ if (table.block.p) {
+ X86PageEntry entry;
+ Bitu entry_addr=(table.block.base<<12)+(sel & 0x3ff)*4;
+ entry.load=phys_readd(entry_addr);
+ sprintf(out1,"page %05Xxxx -> %04Xxxx flags [puw] %x:%x::%x:%x::%x:%x",sel,entry.block.base,entry.block.p,table.block.p,entry.block.us,table.block.us,entry.block.wr,table.block.wr);
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ } else {
+ sprintf(out1,"pagetable %03X not present, flags [puw] %x::%x::%x",(sel >> 10),table.block.p,table.block.us,table.block.wr);
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ }
+ }
+ }
+};
+
+static void LogCPUInfo(void) {
+ char out1[512];
+ sprintf(out1,"cr0:%08X cr2:%08X cr3:%08X cpl=%x",cpu.cr0,paging.cr2,paging.cr3,cpu.cpl);
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ sprintf(out1,"eflags:%08X [vm=%x iopl=%x nt=%x]",reg_flags,GETFLAG(VM)>>17,GETFLAG(IOPL)>>12,GETFLAG(NT)>>14);
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ sprintf(out1,"GDT base=%08X limit=%08X",cpu.gdt.GetBase(),cpu.gdt.GetLimit());
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ sprintf(out1,"IDT base=%08X limit=%08X",cpu.idt.GetBase(),cpu.idt.GetLimit());
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+
+ Bitu sel=CPU_STR();
+ Descriptor desc;
+ if (cpu.gdt.GetDescriptor(sel,desc)) {
+ sprintf(out1,"TR selector=%04X, base=%08X limit=%08X*%X",sel,desc.GetBase(),desc.GetLimit(),desc.saved.seg.g?0x4000:1);
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ }
+ sel=CPU_SLDT();
+ if (cpu.gdt.GetDescriptor(sel,desc)) {
+ sprintf(out1,"LDT selector=%04X, base=%08X limit=%08X*%X",sel,desc.GetBase(),desc.GetLimit(),desc.saved.seg.g?0x4000:1);
+ LOG(LOG_MISC,LOG_ERROR)("%s",out1);
+ }
+};
+
+#if C_HEAVY_DEBUG
+static void LogInstruction(Bit16u segValue, Bit32u eipValue, ofstream& out) {
+ static char empty[23] = { 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0 };
+
+ PhysPt start = GetAddress(segValue,eipValue);
+ char dline[200];Bitu size;
+ size = DasmI386(dline, start, reg_eip, cpu.code.big);
+ char* res = empty;
+ if (showExtend && (cpuLogType > 0) ) {
+ res = AnalyzeInstruction(dline,false);
+ if (!res || !(*res)) res = empty;
+ Bitu reslen = strlen(res);
+ if (reslen<22) for (Bitu i=0; i<22-reslen; i++) res[reslen+i] = ' '; res[22] = 0;
+ };
+ Bitu len = strlen(dline);
+ if (len<30) for (Bitu i=0; i<30-len; i++) dline[len + i] = ' '; dline[30] = 0;
+
+ // Get register values
+
+ if(cpuLogType == 0) {
+ out << setw(4) << SegValue(cs) << ":" << setw(4) << reg_eip << " " << dline;
+ } else if (cpuLogType == 1) {
+ out << setw(4) << SegValue(cs) << ":" << setw(8) << reg_eip << " " << dline << " " << res;
+ } else if (cpuLogType == 2) {
+ char ibytes[200]=""; char tmpc[200];
+ for (Bitu i=0; i<size; i++) {
+ Bit8u value;
+ if (mem_readb_checked(start+i,&value)) sprintf(tmpc,"%s","?? ");
+ else sprintf(tmpc,"%02X ",value);
+ strcat(ibytes,tmpc);
+ }
+ len = strlen(ibytes);
+ if (len<21) { for (Bitu i=0; i<21-len; i++) ibytes[len + i] =' '; ibytes[21]=0;} //NOTE THE BRACKETS
+ out << setw(4) << SegValue(cs) << ":" << setw(8) << reg_eip << " " << dline << " " << res << " " << ibytes;
+ }
+
+ out << " EAX:" << setw(8) << reg_eax << " EBX:" << setw(8) << reg_ebx
+ << " ECX:" << setw(8) << reg_ecx << " EDX:" << setw(8) << reg_edx
+ << " ESI:" << setw(8) << reg_esi << " EDI:" << setw(8) << reg_edi
+ << " EBP:" << setw(8) << reg_ebp << " ESP:" << setw(8) << reg_esp
+ << " DS:" << setw(4) << SegValue(ds)<< " ES:" << setw(4) << SegValue(es);
+
+ if(cpuLogType == 0) {
+ out << " SS:" << setw(4) << SegValue(ss) << " C" << (get_CF()>0) << " Z" << (get_ZF()>0)
+ << " S" << (get_SF()>0) << " O" << (get_OF()>0) << " I" << GETFLAGBOOL(IF);
+ } else {
+ out << " FS:" << setw(4) << SegValue(fs) << " GS:" << setw(4) << SegValue(gs)
+ << " SS:" << setw(4) << SegValue(ss)
+ << " CF:" << (get_CF()>0) << " ZF:" << (get_ZF()>0) << " SF:" << (get_SF()>0)
+ << " OF:" << (get_OF()>0) << " AF:" << (get_AF()>0) << " PF:" << (get_PF()>0)
+ << " IF:" << GETFLAGBOOL(IF);
+ }
+ if(cpuLogType == 2) {
+ out << " TF:" << GETFLAGBOOL(TF) << " VM:" << GETFLAGBOOL(VM) <<" FLG:" << setw(8) << reg_flags
+ << " CR0:" << setw(8) << cpu.cr0;
+ }
+ out << endl;
+};
+#endif
+
+// DEBUG.COM stuff
+
+class DEBUG : public Program {
+public:
+ DEBUG() { pDebugcom = this; active = false; };
+ ~DEBUG() { pDebugcom = 0; };
+
+ bool IsActive() { return active; };
+
+ void Run(void)
+ {
+ if(cmd->FindExist("/NOMOUSE",false)) {
+ real_writed(0,0x33<<2,0);
+ return;
+ }
+
+ char filename[128];
+ char args[256+1];
+
+ cmd->FindCommand(1,temp_line);
+ safe_strncpy(filename,temp_line.c_str(),128);
+ // Read commandline
+ Bit16u i =2;
+ args[0] = 0;
+ for (;cmd->FindCommand(i++,temp_line)==true;) {
+ strncat(args,temp_line.c_str(),256);
+ strncat(args," ",256);
+ }
+ // Start new shell and execute prog
+ active = true;
+ // Save cpu state....
+ Bit16u oldcs = SegValue(cs);
+ Bit32u oldeip = reg_eip;
+ Bit16u oldss = SegValue(ss);
+ Bit32u oldesp = reg_esp;
+
+ // Start shell
+ DOS_Shell shell;
+ shell.Execute(filename,args);
+
+ // set old reg values
+ SegSet16(ss,oldss);
+ reg_esp = oldesp;
+ SegSet16(cs,oldcs);
+ reg_eip = oldeip;
+ };
+
+private:
+ bool active;
+};
+
+void DEBUG_CheckExecuteBreakpoint(Bit16u seg, Bit32u off)
+{
+ if (pDebugcom && pDebugcom->IsActive()) {
+ CBreakpoint::AddBreakpoint(seg,off,true);
+ CBreakpoint::ActivateBreakpointsExceptAt(SegPhys(cs)+reg_eip);
+ pDebugcom = 0;
+ };
+};
+
+Bitu DEBUG_EnableDebugger(void)
+{
+ exitLoop = true;
+ DEBUG_Enable(true);
+ CPU_Cycles=CPU_CycleLeft=0;
+ return 0;
+};
+
+static void DEBUG_ProgramStart(Program * * make) {
+ *make=new DEBUG;
+}
+
+// INIT
+
+void DEBUG_SetupConsole(void) {
+ #ifdef WIN32
+ WIN32_Console();
+ #else
+ tcgetattr(0,&consolesettings);
+ //curses must be inited first in order to catch the resize (is an event)
+// printf("\e[8;50;80t"); //resize terminal
+// fflush(NULL);
+ #endif
+ memset((void *)&dbg,0,sizeof(dbg));
+ debugging=false;
+// dbg.active_win=3;
+ /* Start the Debug Gui */
+ DBGUI_StartUp();
+}
+
+void DEBUG_ShutDown(Section * /*sec*/) {
+ CBreakpoint::DeleteAll();
+ CDebugVar::DeleteAll();
+ curs_set(old_cursor_state);
+ endwin();
+ #ifndef WIN32
+ tcsetattr(0, TCSANOW,&consolesettings);
+// printf("\e[0m\e[2J"); //Seems to destroy scrolling
+// printf("\ec"); //Doesn't seem to be needed anymore
+// fflush(NULL);
+ #endif
+}
+
+Bitu debugCallback;
+
+void DEBUG_Init(Section* sec) {
+
+// MSG_Add("DEBUG_CONFIGFILE_HELP","Debugger related options.\n");
+ DEBUG_DrawScreen();
+ /* Add some keyhandlers */
+ MAPPER_AddHandler(DEBUG_Enable,MK_pause,MMOD2,"debugger","Debugger");
+ /* Reset code overview and input line */
+ memset((void*)&codeViewData,0,sizeof(codeViewData));
+ /* setup debug.com */
+ PROGRAMS_MakeFile("DEBUG.COM",DEBUG_ProgramStart);
+ /* Setup callback */
+ debugCallback=CALLBACK_Allocate();
+ CALLBACK_Setup(debugCallback,DEBUG_EnableDebugger,CB_RETF,"debugger");
+ /* shutdown function */
+ sec->AddDestroyFunction(&DEBUG_ShutDown);
+}
+
+// DEBUGGING VAR STUFF
+
+void CDebugVar::InsertVariable(char* name, PhysPt adr)
+{
+ varList.push_back(new CDebugVar(name,adr));
+};
+
+void CDebugVar::DeleteAll(void)
+{
+ std::vector<CDebugVar*>::iterator i;
+ CDebugVar* bp;
+ for(i=varList.begin(); i != varList.end(); i++) {
+ bp = static_cast<CDebugVar*>(*i);
+ delete bp;
+ };
+ (varList.clear)();
+};
+
+CDebugVar* CDebugVar::FindVar(PhysPt pt)
+{
+ if (varList.empty()) return 0;
+
+ std::vector<CDebugVar*>::size_type s = varList.size();
+ CDebugVar* bp;
+ for(std::vector<CDebugVar*>::size_type i = 0; i != s; i++) {
+ bp = static_cast<CDebugVar*>(varList[i]);
+ if (bp->GetAdr() == pt) return bp;
+ };
+ return 0;
+};
+
+bool CDebugVar::SaveVars(char* name) {
+ if (varList.size() > 65535) return false;
+
+ FILE* f = fopen(name,"wb+");
+ if (!f) return false;
+
+ // write number of vars
+ Bit16u num = (Bit16u)varList.size();
+ fwrite(&num,1,sizeof(num),f);
+
+ std::vector<CDebugVar*>::iterator i;
+ CDebugVar* bp;
+ for(i=varList.begin(); i != varList.end(); i++) {
+ bp = static_cast<CDebugVar*>(*i);
+ // name
+ fwrite(bp->GetName(),1,16,f);
+ // adr
+ PhysPt adr = bp->GetAdr();
+ fwrite(&adr,1,sizeof(adr),f);
+ };
+ fclose(f);
+ return true;
+};
+
+bool CDebugVar::LoadVars(char* name)
+{
+ FILE* f = fopen(name,"rb");
+ if (!f) return false;
+
+ // read number of vars
+ Bit16u num;
+ if (fread(&num,sizeof(num),1,f) != 1) return false;
+
+ for (Bit16u i=0; i<num; i++) {
+ char name[16];
+ // name
+ if (fread(name,16,1,f) != 1) break;
+ // adr
+ PhysPt adr;
+ if (fread(&adr,sizeof(adr),1,f) != 1) break;
+ // insert
+ InsertVariable(name,adr);
+ };
+ fclose(f);
+ return true;
+};
+
+static void SaveMemory(Bit16u seg, Bit32u ofs1, Bit32u num) {
+ FILE* f = fopen("MEMDUMP.TXT","wt");
+ if (!f) {
+ DEBUG_ShowMsg("DEBUG: Memory dump failed.\n");
+ return;
+ }
+
+ char buffer[128];
+ char temp[16];
+
+ while (num>16) {
+ sprintf(buffer,"%04X:%04X ",seg,ofs1);
+ for (Bit16u x=0; x<16; x++) {
+ Bit8u value;
+ if (mem_readb_checked(GetAddress(seg,ofs1+x),&value)) sprintf(temp,"%s","?? ");
+ else sprintf(temp,"%02X ",value);
+ strcat(buffer,temp);
+ }
+ ofs1+=16;
+ num-=16;
+
+ fprintf(f,"%s\n",buffer);
+ }
+ if (num>0) {
+ sprintf(buffer,"%04X:%04X ",seg,ofs1);
+ for (Bit16u x=0; x<num; x++) {
+ Bit8u value;
+ if (mem_readb_checked(GetAddress(seg,ofs1+x),&value)) sprintf(temp,"%s","?? ");
+ else sprintf(temp,"%02X ",value);
+ strcat(buffer,temp);
+ }
+ fprintf(f,"%s\n",buffer);
+ }
+ fclose(f);
+ DEBUG_ShowMsg("DEBUG: Memory dump success.\n");
+}
+
+static void SaveMemoryBin(Bit16u seg, Bit32u ofs1, Bit32u num) {
+ FILE* f = fopen("MEMDUMP.BIN","wb");
+ if (!f) {
+ DEBUG_ShowMsg("DEBUG: Memory binary dump failed.\n");
+ return;
+ }
+
+ for (Bitu x = 0; x < num;x++) {
+ Bit8u val;
+ if (mem_readb_checked(GetAddress(seg,ofs1+x),&val)) val=0;
+ fwrite(&val,1,1,f);
+ }
+
+ fclose(f);
+ DEBUG_ShowMsg("DEBUG: Memory dump binary success.\n");
+}
+
+static void OutputVecTable(char* filename) {
+ FILE* f = fopen(filename, "wt");
+ if (!f)
+ {
+ DEBUG_ShowMsg("DEBUG: Output of interrupt vector table failed.\n");
+ return;
+ }
+
+ for (int i=0; i<256; i++)
+ fprintf(f,"INT %02X: %04X:%04X\n", i, mem_readw(i*4+2), mem_readw(i*4));
+
+ fclose(f);
+ DEBUG_ShowMsg("DEBUG: Interrupt vector table written to %s.\n", filename);
+}
+
+#define DEBUG_VAR_BUF_LEN 16
+static void DrawVariables(void) {
+ if (CDebugVar::varList.empty()) return;
+
+ CDebugVar *dv;
+ char buffer[DEBUG_VAR_BUF_LEN];
+ std::vector<CDebugVar*>::size_type s = CDebugVar::varList.size();
+ bool windowchanges = false;
+
+ for(std::vector<CDebugVar*>::size_type i = 0; i != s; i++) {
+
+ if (i == 4*3) {
+ /* too many variables */
+ break;
+ }
+
+ dv = static_cast<CDebugVar*>(CDebugVar::varList[i]);
+ Bit16u value;
+ bool varchanges = false;
+ bool has_no_value = mem_readw_checked(dv->GetAdr(),&value);
+ if (has_no_value) {
+ snprintf(buffer,DEBUG_VAR_BUF_LEN, "%s", "??????");
+ dv->SetValue(false,0);
+ varchanges = true;
+ } else {
+ if ( dv->HasValue() && dv->GetValue() == value) {
+ ; //It already had a value and it didn't change (most likely case)
+ } else {
+ dv->SetValue(true,value);
+ snprintf(buffer,DEBUG_VAR_BUF_LEN, "0x%04x", value);
+ varchanges = true;
+ }
+ }
+
+ if (varchanges) {
+ int y = i / 3;
+ int x = (i % 3) * 26;
+ mvwprintw(dbg.win_var, y, x, dv->GetName());
+ mvwprintw(dbg.win_var, y, (x + DEBUG_VAR_BUF_LEN + 1) , buffer);
+ windowchanges = true; //Something has changed in this window
+ }
+ }
+
+ if (windowchanges) wrefresh(dbg.win_var);
+};
+#undef DEBUG_VAR_BUF_LEN
+// HEAVY DEBUGGING STUFF
+
+#if C_HEAVY_DEBUG
+
+const Bit32u LOGCPUMAX = 20000;
+
+static Bit32u logCount = 0;
+
+struct TLogInst {
+ Bit16u s_cs;
+ Bit32u eip;
+ Bit32u eax;
+ Bit32u ebx;
+ Bit32u ecx;
+ Bit32u edx;
+ Bit32u esi;
+ Bit32u edi;
+ Bit32u ebp;
+ Bit32u esp;
+ Bit16u s_ds;
+ Bit16u s_es;
+ Bit16u s_fs;
+ Bit16u s_gs;
+ Bit16u s_ss;
+ bool c;
+ bool z;
+ bool s;
+ bool o;
+ bool a;
+ bool p;
+ bool i;
+ char dline[31];
+ char res[23];
+};
+
+TLogInst logInst[LOGCPUMAX];
+
+void DEBUG_HeavyLogInstruction(void) {
+
+ static char empty[23] = { 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0 };
+
+ PhysPt start = GetAddress(SegValue(cs),reg_eip);
+ char dline[200];
+ DasmI386(dline, start, reg_eip, cpu.code.big);
+ char* res = empty;
+ if (showExtend) {
+ res = AnalyzeInstruction(dline,false);
+ if (!res || !(*res)) res = empty;
+ Bitu reslen = strlen(res);
+ if (reslen<22) for (Bitu i=0; i<22-reslen; i++) res[reslen+i] = ' '; res[22] = 0;
+ };
+
+ Bitu len = strlen(dline);
+ if (len < 30) for (Bitu i=0; i < 30-len; i++) dline[len+i] = ' ';
+ dline[30] = 0;
+
+ TLogInst & inst = logInst[logCount];
+ strcpy(inst.dline,dline);
+ inst.s_cs = SegValue(cs);
+ inst.eip = reg_eip;
+ strcpy(inst.res,res);
+ inst.eax = reg_eax;
+ inst.ebx = reg_ebx;
+ inst.ecx = reg_ecx;
+ inst.edx = reg_edx;
+ inst.esi = reg_esi;
+ inst.edi = reg_edi;
+ inst.ebp = reg_ebp;
+ inst.esp = reg_esp;
+ inst.s_ds = SegValue(ds);
+ inst.s_es = SegValue(es);
+ inst.s_fs = SegValue(fs);
+ inst.s_gs = SegValue(gs);
+ inst.s_ss = SegValue(ss);
+ inst.c = get_CF()>0;
+ inst.z = get_ZF()>0;
+ inst.s = get_SF()>0;
+ inst.o = get_OF()>0;
+ inst.a = get_AF()>0;
+ inst.p = get_PF()>0;
+ inst.i = GETFLAGBOOL(IF);
+
+ if (++logCount >= LOGCPUMAX) logCount = 0;
+};
+
+void DEBUG_HeavyWriteLogInstruction(void) {
+ if (!logHeavy) return;
+ logHeavy = false;
+
+ DEBUG_ShowMsg("DEBUG: Creating cpu log LOGCPU_INT_CD.TXT\n");
+
+ ofstream out("LOGCPU_INT_CD.TXT");
+ if (!out.is_open()) {
+ DEBUG_ShowMsg("DEBUG: Failed.\n");
+ return;
+ }
+ out << hex << noshowbase << setfill('0') << uppercase;
+ Bit32u startLog = logCount;
+ do {
+ // Write Instructions
+ TLogInst & inst = logInst[startLog];
+ out << setw(4) << inst.s_cs << ":" << setw(8) << inst.eip << " "
+ << inst.dline << " " << inst.res << " EAX:" << setw(8)<< inst.eax
+ << " EBX:" << setw(8) << inst.ebx << " ECX:" << setw(8) << inst.ecx
+ << " EDX:" << setw(8) << inst.edx << " ESI:" << setw(8) << inst.esi
+ << " EDI:" << setw(8) << inst.edi << " EBP:" << setw(8) << inst.ebp
+ << " ESP:" << setw(8) << inst.esp << " DS:" << setw(4) << inst.s_ds
+ << " ES:" << setw(4) << inst.s_es<< " FS:" << setw(4) << inst.s_fs
+ << " GS:" << setw(4) << inst.s_gs<< " SS:" << setw(4) << inst.s_ss
+ << " CF:" << inst.c << " ZF:" << inst.z << " SF:" << inst.s
+ << " OF:" << inst.o << " AF:" << inst.a << " PF:" << inst.p
+ << " IF:" << inst.i << endl;
+
+/* fprintf(f,"%04X:%08X %s %s EAX:%08X EBX:%08X ECX:%08X EDX:%08X ESI:%08X EDI:%08X EBP:%08X ESP:%08X DS:%04X ES:%04X FS:%04X GS:%04X SS:%04X CF:%01X ZF:%01X SF:%01X OF:%01X AF:%01X PF:%01X IF:%01X\n",
+ logInst[startLog].s_cs,logInst[startLog].eip,logInst[startLog].dline,logInst[startLog].res,logInst[startLog].eax,logInst[startLog].ebx,logInst[startLog].ecx,logInst[startLog].edx,logInst[startLog].esi,logInst[startLog].edi,logInst[startLog].ebp,logInst[startLog].esp,
+ logInst[startLog].s_ds,logInst[startLog].s_es,logInst[startLog].s_fs,logInst[startLog].s_gs,logInst[startLog].s_ss,
+ logInst[startLog].c,logInst[startLog].z,logInst[startLog].s,logInst[startLog].o,logInst[startLog].a,logInst[startLog].p,logInst[startLog].i);*/
+ if (++startLog >= LOGCPUMAX) startLog = 0;
+ } while (startLog != logCount);
+
+ out.close();
+ DEBUG_ShowMsg("DEBUG: Done.\n");
+};
+
+bool DEBUG_HeavyIsBreakpoint(void) {
+ static Bitu zero_count = 0;
+ if (cpuLog) {
+ if (cpuLogCounter>0) {
+ LogInstruction(SegValue(cs),reg_eip,cpuLogFile);
+ cpuLogCounter--;
+ }
+ if (cpuLogCounter<=0) {
+ cpuLogFile.flush();
+ cpuLogFile.close();
+ DEBUG_ShowMsg("DEBUG: cpu log LOGCPU.TXT created\n");
+ cpuLog = false;
+ DEBUG_EnableDebugger();
+ return true;
+ }
+ }
+ // LogInstruction
+ if (logHeavy) DEBUG_HeavyLogInstruction();
+ if (zeroProtect) {
+ Bit32u value=0;
+ if (!mem_readd_checked(SegPhys(cs)+reg_eip,&value)) {
+ if (value == 0) zero_count++;
+ else zero_count = 0;
+ }
+ if (GCC_UNLIKELY(zero_count == 10)) E_Exit("running zeroed code");
+ }
+
+ if (skipFirstInstruction) {
+ skipFirstInstruction = false;
+ return false;
+ }
+ if (CBreakpoint::CheckBreakpoint(SegValue(cs),reg_eip)) {
+ return true;
+ }
+ return false;
+}
+
+#endif // HEAVY DEBUG
+
+
+#endif // DEBUG
+
+
diff --git a/src/debug/debug_disasm.cpp b/src/debug/debug_disasm.cpp
new file mode 100644
index 000000000..9b905f8a6
--- /dev/null
+++ b/src/debug/debug_disasm.cpp
@@ -0,0 +1,1123 @@
+
+/*
+ Ripped out some stuff from the mame releae to only make it for 386's
+ Changed some variables to use the standard DOSBox data types
+ Added my callback opcode
+
+*/
+
+/*
+ * 2asm: Convert binary files to 80*86 assembler. Version 1.00
+ * Adapted by Andrea Mazzoleni for use with MAME
+ * HJB 990321:
+ * Changed output of hex values from 0xxxxh to $xxxx format
+ * Removed "ptr" from "byte ptr", "word ptr" and "dword ptr"
+*/
+
+/* 2asm comments
+
+License:
+
+ 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.
+
+ 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.
+
+Comments:
+
+ The code was originally snaffled from the GNU C++ debugger, as ported
+ to DOS by DJ Delorie and Kent Williams (williams@herky.cs.uiowa.edu).
+ Extensively modified by Robin Hilliard in Jan and May 1992.
+
+ This source compiles under Turbo C v2.01. The disassembler is entirely
+ table driven so it's fairly easy to change to suit your own tastes.
+
+ The instruction table has been modified to correspond with that in
+ `Programmer's Technical Reference: The Processor and Coprocessor',
+ Robert L. Hummel, Ziff-Davis Press, 1992. Missing (read "undocumented")
+ instructions were added and many mistakes and omissions corrected.
+
+
+Health warning:
+
+ When writing and degbugging this code, I didn't have (and still don't have)
+ a 32-bit disassembler to compare this guy's output with. It's therefore
+ quite likely that bugs will appear when disassembling instructions which use
+ the 386 and 486's native 32 bit mode. It seems to work fine in 16 bit mode.
+
+Any comments/updates/bug reports to:
+
+ Robin Hilliard, Lough Guitane, Killarney, Co. Kerry, Ireland.
+ Tel: [+353] 64-54014
+ Internet: softloft@iruccvax.ucc.ie
+ Compu$erve: 100042, 1237
+
+ If you feel like registering, and possibly get notices of updates and
+ other items of software, then send me a post card of your home town.
+
+ Thanks and enjoy!
+
+*/
+#include "dosbox.h"
+#if C_DEBUG
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "mem.h"
+
+typedef Bit8u UINT8;
+typedef Bit16u UINT16;
+typedef Bit32u UINT32;
+
+typedef Bit8s INT8;
+typedef Bit16s INT16;
+typedef Bit32s INT32;
+
+
+/* Little endian uint read */
+#define le_uint8(ptr) (*(UINT8*)ptr)
+
+INLINE UINT16 le_uint16(const void* ptr) {
+ const UINT8* ptr8 = (const UINT8*)ptr;
+ return (UINT16)ptr8[0] | (UINT16)ptr8[1] << 8;
+}
+INLINE UINT32 le_uint32(const void* ptr) {
+ const UINT8* ptr8 = (const UINT8*)ptr;
+ return (UINT32)ptr8[0] | (UINT32)ptr8[1] << 8 | (UINT32)ptr8[2] << 16 | (UINT32)ptr8[3] << 24;
+}
+
+/* Little endian int read */
+#define le_int8(ptr) ((INT8)le_uint8(ptr))
+#define le_int16(ptr) ((INT16)le_uint16(ptr))
+#define le_int32(ptr) ((INT32)le_uint32(ptr))
+
+#define fp_segment(dw) ((dw >> 16) & 0xFFFFU)
+#define fp_offset(dw) (dw & 0xFFFFU)
+#define fp_addr(seg, off) ( (seg<<4)+off )
+
+static UINT8 must_do_size; /* used with size of operand */
+static int wordop; /* dealing with word or byte operand */
+
+static int instruction_offset;
+//static UINT16 instruction_segment;
+
+static char* ubufs; /* start of buffer */
+static char* ubufp; /* last position of buffer */
+static int invalid_opcode = 0;
+static int first_space = 1;
+
+static int prefix; /* segment override prefix byte */
+static int modrmv; /* flag for getting modrm byte */
+static int sibv; /* flag for getting sib byte */
+static int opsize; /* just like it says ... */
+static int addrsize;
+static int addr32bit=0;
+
+/* some defines for extracting instruction bit fields from bytes */
+
+#define MOD(a) (((a)>>6)&7)
+#define REG(a) (((a)>>3)&7)
+#define RM(a) ((a)&7)
+#define SCALE(a) (((a)>>6)&7)
+#define INDEX(a) (((a)>>3)&7)
+#define BASE(a) ((a)&7)
+
+/* Percent tokens in strings:
+ First char after '%':
+ A - direct address
+ C - reg of r/m picks control register
+ D - reg of r/m picks debug register
+ E - r/m picks operand
+ F - flags register
+ G - reg of r/m picks general register
+ I - immediate data
+ J - relative IP offset
++ K - call/jmp distance
+ M - r/m picks memory
+ O - no r/m, offset only
+ R - mod of r/m picks register only
+ S - reg of r/m picks segment register
+ T - reg of r/m picks test register
+ X - DS:ESI
+ Y - ES:EDI
+ 2 - prefix of two-byte opcode
++ e - put in 'e' if use32 (second char is part of reg name)
++ put in 'w' for use16 or 'd' for use32 (second char is 'w')
++ j - put in 'e' in jcxz if prefix==0x66
+ f - floating point (second char is esc value)
+ g - do r/m group 'n', n==0..7
+ p - prefix
+ s - size override (second char is a,o)
++ d - put d if double arg, nothing otherwise (pushfd, popfd &c)
++ w - put w if word, d if double arg, nothing otherwise (lodsw/lodsd)
++ P - simple prefix
+
+ Second char after '%':
+ a - two words in memory (BOUND)
+ b - byte
+ c - byte or word
+ d - dword
++ f - far call/jmp
++ n - near call/jmp
+ p - 32 or 48 bit pointer
++ q - byte/word thingy
+ s - six byte pseudo-descriptor
+ v - word or dword
+ w - word
++ x - sign extended byte
+ F - use floating regs in mod/rm
+ 1-8 - group number, esc value, etc
+*/
+
+/* watch out for aad && aam with odd operands */
+
+static char const* (*opmap1)[256];
+
+static char const * op386map1[256] = {
+/* 0 */
+ "add %Eb,%Gb", "add %Ev,%Gv", "add %Gb,%Eb", "add %Gv,%Ev",
+ "add al,%Ib", "add %eax,%Iv", "push es", "pop es",
+ "or %Eb,%Gb", "or %Ev,%Gv", "or %Gb,%Eb", "or %Gv,%Ev",
+ "or al,%Ib", "or %eax,%Iv", "push cs", "%2 ",
+/* 1 */
+ "adc %Eb,%Gb", "adc %Ev,%Gv", "adc %Gb,%Eb", "adc %Gv,%Ev",
+ "adc al,%Ib", "adc %eax,%Iv", "push ss", "pop ss",
+ "sbb %Eb,%Gb", "sbb %Ev,%Gv", "sbb %Gb,%Eb", "sbb %Gv,%Ev",
+ "sbb al,%Ib", "sbb %eax,%Iv", "push ds", "pop ds",
+/* 2 */
+ "and %Eb,%Gb", "and %Ev,%Gv", "and %Gb,%Eb", "and %Gv,%Ev",
+ "and al,%Ib", "and %eax,%Iv", "%pe", "daa",
+ "sub %Eb,%Gb", "sub %Ev,%Gv", "sub %Gb,%Eb", "sub %Gv,%Ev",
+ "sub al,%Ib", "sub %eax,%Iv", "%pc", "das",
+/* 3 */
+ "xor %Eb,%Gb", "xor %Ev,%Gv", "xor %Gb,%Eb", "xor %Gv,%Ev",
+ "xor al,%Ib", "xor %eax,%Iv", "%ps", "aaa",
+ "cmp %Eb,%Gb", "cmp %Ev,%Gv", "cmp %Gb,%Eb", "cmp %Gv,%Ev",
+ "cmp al,%Ib", "cmp %eax,%Iv", "%pd", "aas",
+/* 4 */
+ "inc %eax", "inc %ecx", "inc %edx", "inc %ebx",
+ "inc %esp", "inc %ebp", "inc %esi", "inc %edi",
+ "dec %eax", "dec %ecx", "dec %edx", "dec %ebx",
+ "dec %esp", "dec %ebp", "dec %esi", "dec %edi",
+/* 5 */
+ "push %eax", "push %ecx", "push %edx", "push %ebx",
+ "push %esp", "push %ebp", "push %esi", "push %edi",
+ "pop %eax", "pop %ecx", "pop %edx", "pop %ebx",
+ "pop %esp", "pop %ebp", "pop %esi", "pop %edi",
+/* 6 */
+ "pusha%d ", "popa%d ", "bound %Gv,%Ma", "arpl %Ew,%Rw",
+ "%pf", "%pg", "%so", "%sa",
+ "push %Iv", "imul %Gv,%Ev,%Iv","push %Ix", "imul %Gv,%Ev,%Ib",
+ "insb", "ins%ew", "outsb", "outs%ew",
+/* 7 */
+ "jo %Jb", "jno %Jb", "jc %Jb", "jnc %Jb",
+ "je %Jb", "jne %Jb", "jbe %Jb", "ja %Jb",
+ "js %Jb", "jns %Jb", "jpe %Jb", "jpo %Jb",
+ "jl %Jb", "jge %Jb", "jle %Jb", "jg %Jb",
+/* 8 */
+ "%g0 %Eb,%Ib", "%g0 %Ev,%Iv", "%g0 %Eb,%Ib", "%g0 %Ev,%Ix",
+ "test %Eb,%Gb", "test %Ev,%Gv", "xchg %Eb,%Gb", "xchg %Ev,%Gv",
+ "mov %Eb,%Gb", "mov %Ev,%Gv", "mov %Gb,%Eb", "mov %Gv,%Ev",
+ "mov %Ew,%Sw", "lea %Gv,%M ", "mov %Sw,%Ew", "pop %Ev",
+/* 9 */
+ "nop", "xchg %ecx,%eax", "xchg %edx,%eax", "xchg %ebx,%eax",
+ "xchg %esp,%eax", "xchg %ebp,%eax", "xchg %esi,%eax", "xchg %edi,%eax",
+ "cbw", "cwd", "call %Ap", "fwait",
+ "pushf%d ", "popf%d ", "sahf", "lahf",
+/* a */
+ "mov al,%Oc", "mov %eax,%Ov", "mov %Oc,al", "mov %Ov,%eax",
+ "%P movsb", "%P movs%w", "%P cmpsb", "%P cmps%w ",
+ "test al,%Ib", "test %eax,%Iv", "%P stosb", "%P stos%w ",
+ "%P lodsb", "%P lods%w ", "%P scasb", "%P scas%w ",
+/* b */
+ "mov al,%Ib", "mov cl,%Ib", "mov dl,%Ib", "mov bl,%Ib",
+ "mov ah,%Ib", "mov ch,%Ib", "mov dh,%Ib", "mov bh,%Ib",
+ "mov %eax,%Iv", "mov %ecx,%Iv", "mov %edx,%Iv", "mov %ebx,%Iv",
+ "mov %esp,%Iv", "mov %ebp,%Iv", "mov %esi,%Iv", "mov %edi,%Iv",
+/* c */
+ "%g1 %Eb,%Ib", "%g1 %Ev,%Ib", "ret %Iw", "ret",
+ "les %Gv,%Mp", "lds %Gv,%Mp", "mov %Eb,%Ib", "mov %Ev,%Iv",
+ "enter %Iw,%Ib", "leave", "retf %Iw", "retf",
+ "int 03", "int %Ib", "into", "iret",
+/* d */
+ "%g1 %Eb,1", "%g1 %Ev,1", "%g1 %Eb,cl", "%g1 %Ev,cl",
+ "aam ; %Ib", "aad ; %Ib", "setalc", "%P xlat",
+#if 0
+ "esc 0,%Ib", "esc 1,%Ib", "esc 2,%Ib", "esc 3,%Ib",
+ "esc 4,%Ib", "esc 5,%Ib", "esc 6,%Ib", "esc 7,%Ib",
+#else
+ "%f0", "%f1", "%f2", "%f3",
+ "%f4", "%f5", "%f6", "%f7",
+#endif
+/* e */
+ "loopne %Jb", "loope %Jb", "loop %Jb", "j%j cxz %Jb",
+ "in al,%Ib", "in %eax,%Ib", "out %Ib,al", "out %Ib,%eax",
+ "call %Jv", "jmp %Jv", "jmp %Ap", "jmp %Ks%Jb",
+ "in al,dx", "in %eax,dx", "out dx,al", "out dx,%eax",
+/* f */
+ "lock %p ", "icebp", "repne %p ", "repe %p ",
+ "hlt", "cmc", "%g2", "%g2",
+ "clc", "stc", "cli", "sti",
+ "cld", "std", "%g3", "%g4"
+};
+
+static char const *second[] = {
+/* 0 */
+ "%g5", "%g6", "lar %Gv,%Ew", "lsl %Gv,%Ew",
+ 0, "[loadall]", "clts", "[loadall]",
+ "invd", "wbinvd", 0, "UD2",
+ 0, 0, 0, 0,
+/* 1 */
+ "mov %Eb,%Gb", "mov %Ev,%Gv", "mov %Gb,%Eb", "mov %Gv,%Ev",
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+/* 2 */
+ "mov %Rd,%Cd", "mov %Rd,%Dd", "mov %Cd,%Rd", "mov %Dd,%Rd",
+ "mov %Rd,%Td", 0, "mov %Td,%Rd", 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+/* 3 */
+ 0, "rdtsc", 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+/* 4 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 5 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 6 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 7 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 8 */
+ "jo %Jv", "jno %Jv", "jb %Jv", "jnb %Jv",
+ "jz %Jv", "jnz %Jv", "jbe %Jv", "ja %Jv",
+ "js %Jv", "jns %Jv", "jp %Jv", "jnp %Jv",
+ "jl %Jv", "jge %Jv", "jle %Jv", "jg %Jv",
+/* 9 */
+ "seto %Eb", "setno %Eb", "setc %Eb", "setnc %Eb",
+ "setz %Eb", "setnz %Eb", "setbe %Eb", "setnbe %Eb",
+ "sets %Eb", "setns %Eb", "setp %Eb", "setnp %Eb",
+ "setl %Eb", "setge %Eb", "setle %Eb", "setg %Eb",
+/* a */
+ "push fs", "pop fs", "cpuid", "bt %Ev,%Gv",
+ "shld %Ev,%Gv,%Ib", "shld %Ev,%Gv,cl", 0, 0,
+ "push gs", "pop gs", 0, "bts %Ev,%Gv",
+ "shrd %Ev,%Gv,%Ib", "shrd %Ev,%Gv,cl", 0, "imul %Gv,%Ev",
+/* b */
+ "cmpxchg %Eb,%Gb", "cmpxchg %Ev,%Gv", "lss %Mp", "btr %Ev,%Gv",
+ "lfs %Mp", "lgs %Mp", "movzx %Gv,%Eb", "movzx %Gv,%Ew",
+ 0, 0, "%g7 %Ev,%Ib", "btc %Ev,%Gv",
+ "bsf %Gv,%Ev", "bsr %Gv,%Ev", "movsx %Gv,%Eb", "movsx %Gv,%Ew",
+/* c */
+ "xadd %Eb,%Gb", "xadd %Ev,%Gv", 0, 0,
+ 0, 0, 0, 0,
+ "bswap eax", "bswap ecx", "bswap edx", "bswap ebx",
+ "bswap esp", "bswap ebp", "bswap esi", "bswap edi",
+/* d */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* e */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* f */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static char const *groups[][8] = { /* group 0 is group 3 for %Ev set */
+/* 0 */
+ { "add", "or", "adc", "sbb",
+ "and", "sub", "xor", "cmp" },
+/* 1 */
+ { "rol", "ror", "rcl", "rcr",
+ "shl", "shr", "shl", "sar" },
+/* 2 */ /* v v*/
+ { "test %Eq,%Iq", "test %Eq,%Iq", "not %Ec", "neg %Ec",
+ "mul %Ec", "imul %Ec", "div %Ec", "idiv %Ec" },
+/* 3 */
+ { "inc %Eb", "dec %Eb", 0, 0,
+ 0, 0, 0, "callback %Iw" },
+/* 4 */
+ { "inc %Ev", "dec %Ev", "call %Kn%Ev", "call %Kf%Ep",
+ "jmp %Kn%Ev", "jmp %Kf%Ep", "push %Ev", 0 },
+/* 5 */
+ { "sldt %Ew", "str %Ew", "lldt %Ew", "ltr %Ew",
+ "verr %Ew", "verw %Ew", 0, 0 },
+/* 6 */
+ { "sgdt %Ms", "sidt %Ms", "lgdt %Ms", "lidt %Ms",
+ "smsw %Ew", 0, "lmsw %Ew", "invlpg" },
+/* 7 */
+ { 0, 0, 0, 0,
+ "bt", "bts", "btr", "btc" }
+};
+
+/* zero here means invalid. If first entry starts with '*', use st(i) */
+/* no assumed %EFs here. Indexed by RM(modrm()) */
+static char const *f0[] = { 0, 0, 0, 0, 0, 0, 0, 0};
+static char const *fop_8[] = { "*fld st,%GF" };
+static char const *fop_9[] = { "*fxch st,%GF" };
+static char const *fop_10[] = { "fnop", 0, 0, 0, 0, 0, 0, 0 };
+static char const *fop_11[] = { "*fst st,%GF" };
+static char const *fop_12[] = { "fchs", "fabs", 0, 0, "ftst", "fxam", 0, 0 };
+static char const *fop_13[] = { "fld1", "fldl2t", "fldl2e", "fldpi",
+ "fldlg2", "fldln2", "fldz", 0 };
+static char const *fop_14[] = { "f2xm1", "fyl2x", "fptan", "fpatan",
+ "fxtract", "fprem1", "fdecstp", "fincstp" };
+static char const *fop_15[] = { "fprem", "fyl2xp1", "fsqrt", "fsincos",
+ "frndint", "fscale", "fsin", "fcos" };
+static char const *fop_21[] = { 0, "fucompp", 0, 0, 0, 0, 0, 0 };
+static char const *fop_28[] = { "[fneni]", "[fndis]", "fclex", "finit", "[fnsetpm]", "[frstpm]", 0, 0 };
+static char const *fop_32[] = { "*fadd %GF,st" };
+static char const *fop_33[] = { "*fmul %GF,st" };
+static char const *fop_34[] = { "*fcom %GF,st" };
+static char const *fop_35[] = { "*fcomp %GF,st" };
+static char const *fop_36[] = { "*fsubr %GF,st" };
+static char const *fop_37[] = { "*fsub %GF,st" };
+static char const *fop_38[] = { "*fdivr %GF,st" };
+static char const *fop_39[] = { "*fdiv %GF,st" };
+static char const *fop_40[] = { "*ffree %GF" };
+static char const *fop_41[] = { "*fxch %GF" };
+static char const *fop_42[] = { "*fst %GF" };
+static char const *fop_43[] = { "*fstp %GF" };
+static char const *fop_44[] = { "*fucom %GF" };
+static char const *fop_45[] = { "*fucomp %GF" };
+static char const *fop_48[] = { "*faddp %GF,st" };
+static char const *fop_49[] = { "*fmulp %GF,st" };
+static char const *fop_50[] = { "*fcomp %GF,st" };
+static char const *fop_51[] = { 0, "fcompp", 0, 0, 0, 0, 0, 0 };
+static char const *fop_52[] = { "*fsubrp %GF,st" };
+static char const *fop_53[] = { "*fsubp %GF,st" };
+static char const *fop_54[] = { "*fdivrp %GF,st" };
+static char const *fop_55[] = { "*fdivp %GF,st" };
+static char const *fop_56[] = { "*ffreep %GF" };
+static char const *fop_60[] = { "fstsw ax", 0, 0, 0, 0, 0, 0, 0 };
+
+static char const **fspecial[] = { /* 0=use st(i), 1=undefined 0 in fop_* means undefined */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ fop_8, fop_9, fop_10, fop_11, fop_12, fop_13, fop_14, fop_15,
+ f0, f0, f0, f0, f0, fop_21, f0, f0,
+ f0, f0, f0, f0, fop_28, f0, f0, f0,
+ fop_32, fop_33, fop_34, fop_35, fop_36, fop_37, fop_38, fop_39,
+ fop_40, fop_41, fop_42, fop_43, fop_44, fop_45, f0, f0,
+ fop_48, fop_49, fop_50, fop_51, fop_52, fop_53, fop_54, fop_55,
+ fop_56, f0, f0, f0, fop_60, f0, f0, f0,
+};
+
+static const char *floatops[] = { /* assumed " %EF" at end of each. mod != 3 only */
+/*00*/ "fadd", "fmul", "fcom", "fcomp",
+ "fsub", "fsubr", "fdiv", "fdivr",
+/*08*/ "fld", 0, "fst", "fstp",
+ "fldenv", "fldcw", "fstenv", "fstcw",
+/*16*/ "fiadd", "fimul", "ficomw", "ficompw",
+ "fisub", "fisubr", "fidiv", "fidivr",
+/*24*/ "fild", 0, "fist", "fistp",
+ "frstor", "fldt", 0, "fstpt",
+/*32*/ "faddq", "fmulq", "fcomq", "fcompq",
+ "fsubq", "fsubrq", "fdivq", "fdivrq",
+/*40*/ "fldq", 0, "fstq", "fstpq",
+ 0, 0, "fsave", "fstsw",
+/*48*/ "fiaddw", "fimulw", "ficomw", "ficompw",
+ "fisubw", "fisubrw", "fidivw", "fidivr",
+/*56*/ "fildw", 0, "fistw", "fistpw",
+ "fbldt", "fildq", "fbstpt", "fistpq"
+};
+
+static char *addr_to_hex(UINT32 addr, int splitup) {
+ static char buffer[11];
+
+ if (splitup) {
+ if (fp_segment(addr)==0 || fp_offset(addr)==0xffff) /* 'coz of wraparound */
+ sprintf(buffer, "%04X", (unsigned)fp_offset(addr) );
+ else
+ sprintf(buffer, "%04X:%04X", (unsigned)fp_segment(addr), (unsigned)fp_offset(addr) );
+ } else {
+#if 0
+ /* Pet outcommented, reducing address size to 4
+ when segment is 0 or 0xffff */
+ if (fp_segment(addr)==0 || fp_segment(addr)==0xffff) /* 'coz of wraparound */
+ sprintf(buffer, "%04X", (unsigned)fp_offset(addr) );
+ else
+#endif
+
+ sprintf(buffer, "%08X", addr );
+
+ }
+
+ return buffer;
+}
+
+static PhysPt getbyte_mac;
+static PhysPt startPtr;
+
+static UINT8 getbyte(void) {
+ return mem_readb(getbyte_mac++);
+}
+
+/*
+ only one modrm or sib byte per instruction, tho' they need to be
+ returned a few times...
+*/
+
+static int modrm(void)
+{
+ if (modrmv == -1)
+ modrmv = getbyte();
+ return modrmv;
+}
+
+static int sib(void)
+{
+ if (sibv == -1)
+ sibv = getbyte();
+ return sibv;
+}
+
+/*------------------------------------------------------------------------*/
+
+static void uprintf(char const *s, ...)
+{
+ va_list arg_ptr;
+ va_start (arg_ptr, s);
+ vsprintf(ubufp, s, arg_ptr);
+ va_end(arg_ptr);
+ while (*ubufp)
+ ubufp++;
+}
+
+static void uputchar(char c)
+{
+ *ubufp++ = c;
+ *ubufp = 0;
+}
+
+/*------------------------------------------------------------------------*/
+
+static int bytes(char c)
+{
+ switch (c) {
+ case 'b':
+ return 1;
+ case 'w':
+ return 2;
+ case 'd':
+ return 4;
+ case 'v':
+ if (opsize == 32)
+ return 4;
+ else
+ return 2;
+ }
+ return 0;
+}
+
+/*------------------------------------------------------------------------*/
+static void outhex(char subtype, int extend, int optional, int defsize, int sign)
+{
+ int n=0, s=0, i;
+ INT32 delta = 0;
+ unsigned char buff[6];
+ char *name;
+ char signchar;
+
+ switch (subtype) {
+ case 'q':
+ if (wordop) {
+ if (opsize==16) {
+ n = 2;
+ } else {
+ n = 4;
+ }
+ } else {
+ n = 1;
+ }
+ break;
+
+ case 'a':
+ break;
+ case 'x':
+ extend = 2;
+ n = 1;
+ break;
+ case 'b':
+ n = 1;
+ break;
+ case 'w':
+ n = 2;
+ break;
+ case 'd':
+ n = 4;
+ break;
+ case 's':
+ n = 6;
+ break;
+ case 'c':
+ case 'v':
+ if (defsize == 32)
+ n = 4;
+ else
+ n = 2;
+ break;
+ case 'p':
+ if (defsize == 32)
+ n = 6;
+ else
+ n = 4;
+ s = 1;
+ break;
+ }
+ for (i=0; i<n; i++)
+ buff[i] = getbyte();
+ for (; i<extend; i++)
+ buff[i] = (buff[i-1] & 0x80) ? 0xff : 0;
+ if (s) {
+ uprintf("%02X%02X:", (unsigned)buff[n-1], (unsigned)buff[n-2]);
+ n -= 2;
+ }
+ switch (n) {
+ case 1:
+ delta = le_int8(buff);
+ break;
+ case 2:
+ delta = le_int16(buff);
+ break;
+ case 4:
+ delta = le_int32(buff);
+ break;
+ }
+ if (extend > n) {
+ if (subtype!='x') {
+ if (delta<0) {
+ delta = -delta;
+ signchar = '-';
+ } else
+ signchar = '+';
+ if (delta || !optional)
+ uprintf("%c%0*lX", (char)signchar, (int)(extend), (long)delta);
+ } else {
+ if (extend==2)
+ delta = (UINT16)delta;
+ uprintf("%0.*lX", (int)(2*extend), (long)delta );
+ }
+ return;
+ }
+ if ((n == 4) && !sign) {
+ name = addr_to_hex(delta, 0);
+ uprintf("%s", name);
+ return;
+ }
+ switch (n) {
+ case 1:
+ if (sign && (char)delta<0) {
+ delta = -delta;
+ signchar = '-';
+ } else
+ signchar = '+';
+ if (sign)
+ uprintf("%c%02lX", (char)signchar, delta & 0xFFL);
+ else
+ uprintf("%02lX", delta & 0xFFL);
+ break;
+
+ case 2:
+ if (sign && delta<0) {
+ signchar = '-';
+ delta = -delta;
+ } else
+ signchar = '+';
+ if (sign)
+ uprintf("%c%04lX", (char)signchar, delta & 0xFFFFL);
+ else
+ uprintf("%04lX", delta & 0xFFFFL);
+ break;
+
+ case 4:
+ if (sign && delta<0) {
+ delta = -delta;
+ signchar = '-';
+ } else
+ signchar = '+';
+ if (sign)
+ uprintf("%c%08lX", (char)signchar, delta & 0xFFFFFFFFL);
+ else
+ uprintf("%08lX", delta & 0xFFFFFFFFL);
+ break;
+ }
+}
+
+
+/*------------------------------------------------------------------------*/
+
+static void reg_name(int regnum, char size)
+{
+ if (size == 'F') { /* floating point register? */
+ uprintf("st(%d)", regnum);
+ return;
+ }
+ if ((((size == 'c') || (size == 'v')) && (opsize == 32)) || (size == 'd'))
+ uputchar('e');
+ if ((size=='q' || size == 'b' || size=='c') && !wordop) {
+ uputchar("acdbacdb"[regnum]);
+ uputchar("llllhhhh"[regnum]);
+ } else {
+ uputchar("acdbsbsd"[regnum]);
+ uputchar("xxxxppii"[regnum]);
+ }
+}
+
+
+/*------------------------------------------------------------------------*/
+
+static void ua_str(char const *str);
+
+static void do_sib(int m)
+{
+ int s, i, b;
+
+ s = SCALE(sib());
+ i = INDEX(sib());
+ b = BASE(sib());
+ switch (b) { /* pick base */
+ case 0: ua_str("%p:[eax"); break;
+ case 1: ua_str("%p:[ecx"); break;
+ case 2: ua_str("%p:[edx"); break;
+ case 3: ua_str("%p:[ebx"); break;
+ case 4: ua_str("%p:[esp"); break;
+ case 5:
+ if (m == 0) {
+ ua_str("%p:[");
+ outhex('d', 4, 0, addrsize, 0);
+ } else {
+ ua_str("%p:[ebp");
+ }
+ break;
+ case 6: ua_str("%p:[esi"); break;
+ case 7: ua_str("%p:[edi"); break;
+ }
+ switch (i) { /* and index */
+ case 0: uprintf("+eax"); break;
+ case 1: uprintf("+ecx"); break;
+ case 2: uprintf("+edx"); break;
+ case 3: uprintf("+ebx"); break;
+ case 4: break;
+ case 5: uprintf("+ebp"); break;
+ case 6: uprintf("+esi"); break;
+ case 7: uprintf("+edi"); break;
+ }
+ if (i != 4) {
+ switch (s) { /* and scale */
+ case 0: /*uprintf("");*/ break;
+ case 1: uprintf("*2"); break;
+ case 2: uprintf("*4"); break;
+ case 3: uprintf("*8"); break;
+ }
+ }
+}
+
+
+
+/*------------------------------------------------------------------------*/
+static void do_modrm(char subtype)
+{
+ int mod = MOD(modrm());
+ int rm = RM(modrm());
+ int extend = (addrsize == 32) ? 4 : 2;
+
+ if (mod == 3) { /* specifies two registers */
+ reg_name(rm, subtype);
+ return;
+ }
+ if (must_do_size) {
+ if (wordop) {
+ if (addrsize==32 || opsize==32) { /* then must specify size */
+ ua_str("dword ");
+ } else {
+ ua_str("word ");
+ }
+ } else {
+ ua_str("byte ");
+ }
+ }
+ if ((mod == 0) && (rm == 5) && (addrsize == 32)) {/* mem operand with 32 bit ofs */
+ ua_str("%p:[");
+ outhex('d', extend, 0, addrsize, 0);
+ uputchar(']');
+ return;
+ }
+ if ((mod == 0) && (rm == 6) && (addrsize == 16)) { /* 16 bit dsplcmnt */
+ ua_str("%p:[");
+ outhex('w', extend, 0, addrsize, 0);
+ uputchar(']');
+ return;
+ }
+ if ((addrsize != 32) || (rm != 4))
+ ua_str("%p:[");
+ if (addrsize == 16) {
+ switch (rm) {
+ case 0: uprintf("bx+si"); break;
+ case 1: uprintf("bx+di"); break;
+ case 2: uprintf("bp+si"); break;
+ case 3: uprintf("bp+di"); break;
+ case 4: uprintf("si"); break;
+ case 5: uprintf("di"); break;
+ case 6: uprintf("bp"); break;
+ case 7: uprintf("bx"); break;
+ }
+ } else {
+ switch (rm) {
+ case 0: uprintf("eax"); break;
+ case 1: uprintf("ecx"); break;
+ case 2: uprintf("edx"); break;
+ case 3: uprintf("ebx"); break;
+ case 4: do_sib(mod); break;
+ case 5: uprintf("ebp"); break;
+ case 6: uprintf("esi"); break;
+ case 7: uprintf("edi"); break;
+ }
+ }
+ switch (mod) {
+ case 1:
+ outhex('b', extend, 1, addrsize, 0);
+ break;
+ case 2:
+ outhex('v', extend, 1, addrsize, 1);
+ break;
+ }
+ uputchar(']');
+}
+
+
+
+/*------------------------------------------------------------------------*/
+static void floating_point(int e1)
+{
+ int esc = e1*8 + REG(modrm());
+
+ if ((MOD(modrm()) == 3)&&fspecial[esc]) {
+ if (fspecial[esc][0]) {
+ if (fspecial[esc][0][0] == '*') {
+ ua_str(fspecial[esc][0]+1);
+ } else {
+ ua_str(fspecial[esc][RM(modrm())]);
+ }
+ } else {
+ ua_str(floatops[esc]);
+ ua_str(" %EF");
+ }
+ } else {
+ ua_str(floatops[esc]);
+ ua_str(" %EF");
+ }
+}
+
+
+/*------------------------------------------------------------------------*/
+/* Main table driver */
+
+#define INSTRUCTION_SIZE (int)getbyte_mac - (int)startPtr
+
+static void percent(char type, char subtype)
+{
+ INT32 vofs = 0;
+ char *name=NULL;
+ int extend = (addrsize == 32) ? 4 : 2;
+ UINT8 c;
+
+ switch (type) {
+ case 'A': /* direct address */
+ outhex(subtype, extend, 0, addrsize, 0);
+ break;
+
+ case 'C': /* reg(r/m) picks control reg */
+ uprintf("CR%d", REG(modrm()));
+ must_do_size = 0;
+ break;
+
+ case 'D': /* reg(r/m) picks debug reg */
+ uprintf("DR%d", REG(modrm()));
+ must_do_size = 0;
+ break;
+
+ case 'E': /* r/m picks operand */
+ do_modrm(subtype);
+ break;
+
+ case 'G': /* reg(r/m) picks register */
+ if (subtype == 'F') /* 80*87 operand? */
+ reg_name(RM(modrm()), subtype);
+ else
+ reg_name(REG(modrm()), subtype);
+ must_do_size = 0;
+ break;
+
+ case 'I': /* immed data */
+ outhex(subtype, 0, 0, opsize, 0);
+ break;
+
+ case 'J': /* relative IP offset */
+ switch (bytes(subtype)) { /* sizeof offset value */
+ case 1:
+ vofs = (INT8)getbyte();
+ name = addr_to_hex(vofs+instruction_offset+INSTRUCTION_SIZE,0);
+ break;
+ case 2:
+ vofs = getbyte();
+ vofs += getbyte()<<8;
+ vofs = (INT16)vofs;
+ name = addr_to_hex(vofs+instruction_offset+INSTRUCTION_SIZE,0);
+ break;
+ /* i386 */
+ case 4:
+ vofs = (UINT32)getbyte(); /* yuk! */
+ vofs |= (UINT32)getbyte() << 8;
+ vofs |= (UINT32)getbyte() << 16;
+ vofs |= (UINT32)getbyte() << 24;
+ name = addr_to_hex(vofs+instruction_offset+INSTRUCTION_SIZE,(addrsize == 32)?0:1);
+ break;
+ }
+ if (vofs<0)
+ uprintf("%s ($-%x)", name, -vofs);
+ else
+ uprintf("%s ($+%x)", name, vofs);
+ break;
+
+ case 'K':
+ switch (subtype) {
+ case 'f':
+ ua_str("far ");
+ break;
+ case 'n':
+ ua_str("near ");
+ break;
+ case 's':
+ ua_str("short ");
+ break;
+ }
+ break;
+
+ case 'M': /* r/m picks memory */
+ do_modrm(subtype);
+ break;
+
+ case 'O': /* offset only */
+ ua_str("%p:[");
+ outhex(subtype, extend, 0, addrsize, 0);
+ uputchar(']');
+ break;
+
+ case 'P': /* prefix byte (rh) */
+ ua_str("%p:");
+ break;
+
+ case 'R': /* mod(r/m) picks register */
+ reg_name(RM(modrm()), subtype); /* rh */
+ must_do_size = 0;
+ break;
+
+ case 'S': /* reg(r/m) picks segment reg */
+ uputchar("ecsdfg"[REG(modrm())]);
+ uputchar('s');
+ must_do_size = 0;
+ break;
+
+ case 'T': /* reg(r/m) picks T reg */
+ uprintf("tr%d", REG(modrm()));
+ must_do_size = 0;
+ break;
+
+ case 'X': /* ds:si type operator */
+ uprintf("ds:[");
+ if (addrsize == 32)
+ uputchar('e');
+ uprintf("si]");
+ break;
+
+ case 'Y': /* es:di type operator */
+ uprintf("es:[");
+ if (addrsize == 32)
+ uputchar('e');
+ uprintf("di]");
+ break;
+
+ case '2': /* old [pop cs]! now indexes */
+ c = getbyte();
+ wordop = c & 1;
+ ua_str(second[c]); /* instructions in 386/486 */
+ break;
+
+ case 'g': /* modrm group `subtype' (0--7) */
+ ua_str(groups[subtype-'0'][REG(modrm())]);
+ break;
+
+ case 'd': /* sizeof operand==dword? */
+ if (opsize == 32)
+ uputchar('d');
+ uputchar(subtype);
+ break;
+
+ case 'w': /* insert explicit size specifier */
+ if (opsize == 32)
+ uputchar('d');
+ else
+ uputchar('w');
+ uputchar(subtype);
+ break;
+
+ case 'e': /* extended reg name */
+ if (opsize == 32) {
+ if (subtype == 'w')
+ uputchar('d');
+ else {
+ uputchar('e');
+ uputchar(subtype);
+ }
+ } else
+ uputchar(subtype);
+ break;
+
+ case 'f': /* '87 opcode */
+ floating_point(subtype-'0');
+ break;
+
+ case 'j':
+ if (addrsize==32 || opsize==32) /* both of them?! */
+ uputchar('e');
+ break;
+
+ case 'p': /* prefix byte */
+ switch (subtype) {
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 's':
+ prefix = subtype;
+ c = getbyte();
+ wordop = c & 1;
+ ua_str((*opmap1)[c]);
+ break;
+ case ':':
+ if (prefix)
+ uprintf("%cs:", prefix);
+ break;
+ case ' ':
+ c = getbyte();
+ wordop = c & 1;
+ ua_str((*opmap1)[c]);
+ break;
+ }
+ break;
+
+ case 's': /* size override */
+ switch (subtype) {
+ case 'a':
+ addrsize = 48 - addrsize;
+ c = getbyte();
+ wordop = c & 1;
+ ua_str((*opmap1)[c]);
+/* ua_str(opmap1[getbyte()]); */
+ break;
+ case 'o':
+ opsize = 48 - opsize;
+ c = getbyte();
+ wordop = c & 1;
+ ua_str((*opmap1)[c]);
+/* ua_str(opmap1[getbyte()]); */
+ break;
+ }
+ break;
+ }
+}
+
+
+static void ua_str(char const *str)
+{
+ char c;
+
+ if (str == 0) {
+ invalid_opcode = 1;
+ uprintf("?");
+ return;
+ }
+
+ if (strpbrk(str, "CDFGRST")) /* specifiers for registers=>no size 2b specified */
+ must_do_size = 0;
+
+ while ((c = *str++) != 0) {
+ if (c == ' ' && first_space)
+ {
+ first_space = 0;
+ do
+ {
+ uputchar(' ');
+ } while ( (int)(ubufp - ubufs) < 5 );
+ }
+ else
+ if (c == '%') {
+ c = *str++;
+ percent(c, *str++);
+ } else {
+ uputchar(c);
+ }
+ }
+}
+
+
+Bitu DasmI386(char* buffer, PhysPt pc, Bitu cur_ip, bool bit32)
+{
+ Bitu c;
+
+
+ instruction_offset = cur_ip;
+ /* input buffer */
+ startPtr = pc;
+ getbyte_mac = pc;
+
+ /* output buffer */
+ ubufs = buffer;
+ ubufp = buffer;
+ first_space = 1;
+
+ addr32bit=1;
+
+ prefix = 0;
+ modrmv = sibv = -1; /* set modrm and sib flags */
+ if (bit32) opsize = addrsize = 32;
+ else opsize = addrsize = 16;
+ c = getbyte();
+ wordop = c & 1;
+ must_do_size = 1;
+ invalid_opcode = 0;
+ opmap1=&op386map1;
+ ua_str(op386map1[c]);
+
+ if (invalid_opcode) {
+ /* restart output buffer */
+ ubufp = buffer;
+ /* invalid instruction, use db xx */
+ uprintf("db %02X", (unsigned)c);
+ return 1;
+ }
+
+ return getbyte_mac-pc;
+}
+
+int DasmLastOperandSize()
+{
+ return opsize;
+};
+
+
+#endif
+
diff --git a/src/debug/debug_gui.cpp b/src/debug/debug_gui.cpp
new file mode 100644
index 000000000..a9ab9da98
--- /dev/null
+++ b/src/debug/debug_gui.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+
+#if C_DEBUG
+#include "control.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <curses.h>
+#include <string.h>
+
+#include "support.h"
+#include "regs.h"
+#include "debug.h"
+#include "debug_inc.h"
+
+struct _LogGroup {
+ char const* front;
+ bool enabled;
+};
+#include <list>
+#include <string>
+using namespace std;
+
+#define MAX_LOG_BUFFER 500
+static list<string> logBuff;
+static list<string>::iterator logBuffPos = logBuff.end();
+
+static _LogGroup loggrp[LOG_MAX]={{"",true},{0,false}};
+static FILE* debuglog;
+
+extern int old_cursor_state;
+
+
+
+void DEBUG_ShowMsg(char const* format,...) {
+
+ char buf[512];
+ va_list msg;
+ va_start(msg,format);
+ vsprintf(buf,format,msg);
+ va_end(msg);
+
+ /* Add newline if not present */
+ Bitu len=strlen(buf);
+ if(buf[len-1]!='\n') strcat(buf,"\n");
+
+ if(debuglog) fprintf(debuglog,"%s",buf);
+
+ if (logBuffPos!=logBuff.end()) {
+ logBuffPos=logBuff.end();
+ DEBUG_RefreshPage(0);
+// mvwprintw(dbg.win_out,dbg.win_out->_maxy-1, 0, "");
+ }
+ logBuff.push_back(buf);
+ if (logBuff.size() > MAX_LOG_BUFFER)
+ logBuff.pop_front();
+
+ logBuffPos = logBuff.end();
+ wprintw(dbg.win_out,"%s",buf);
+ wrefresh(dbg.win_out);
+}
+
+void DEBUG_RefreshPage(char scroll) {
+ if (scroll==-1 && logBuffPos!=logBuff.begin()) logBuffPos--;
+ else if (scroll==1 && logBuffPos!=logBuff.end()) logBuffPos++;
+
+ list<string>::iterator i = logBuffPos;
+ int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx);
+ int rem_lines = maxy;
+ if(rem_lines == -1) return;
+
+ wclear(dbg.win_out);
+
+ while (rem_lines > 0 && i!=logBuff.begin()) {
+ --i;
+ for (string::size_type posf=0, posl; (posl=(*i).find('\n',posf)) != string::npos ;posf=posl+1)
+ rem_lines -= (int) ((posl-posf) / maxx) + 1; // len=(posl+1)-posf-1
+ /* Const cast is needed for pdcurses which has no const char in mvwprintw (bug maybe) */
+ mvwprintw(dbg.win_out,rem_lines-1, 0, const_cast<char*>((*i).c_str()));
+ }
+ mvwprintw(dbg.win_out,maxy-1, 0, "");
+ wrefresh(dbg.win_out);
+}
+
+void LOG::operator() (char const* format, ...){
+ char buf[512];
+ va_list msg;
+ va_start(msg,format);
+ vsprintf(buf,format,msg);
+ va_end(msg);
+
+ if (d_type>=LOG_MAX) return;
+ if ((d_severity!=LOG_ERROR) && (!loggrp[d_type].enabled)) return;
+ DEBUG_ShowMsg("%10u: %s:%s\n",static_cast<Bit32u>(cycle_count),loggrp[d_type].front,buf);
+}
+
+
+static void Draw_RegisterLayout(void) {
+ mvwaddstr(dbg.win_reg,0,0,"EAX=");
+ mvwaddstr(dbg.win_reg,1,0,"EBX=");
+ mvwaddstr(dbg.win_reg,2,0,"ECX=");
+ mvwaddstr(dbg.win_reg,3,0,"EDX=");
+
+ mvwaddstr(dbg.win_reg,0,14,"ESI=");
+ mvwaddstr(dbg.win_reg,1,14,"EDI=");
+ mvwaddstr(dbg.win_reg,2,14,"EBP=");
+ mvwaddstr(dbg.win_reg,3,14,"ESP=");
+
+ mvwaddstr(dbg.win_reg,0,28,"DS=");
+ mvwaddstr(dbg.win_reg,0,38,"ES=");
+ mvwaddstr(dbg.win_reg,0,48,"FS=");
+ mvwaddstr(dbg.win_reg,0,58,"GS=");
+ mvwaddstr(dbg.win_reg,0,68,"SS=");
+
+ mvwaddstr(dbg.win_reg,1,28,"CS=");
+ mvwaddstr(dbg.win_reg,1,38,"EIP=");
+
+ mvwaddstr(dbg.win_reg,2,75,"CPL");
+ mvwaddstr(dbg.win_reg,2,68,"IOPL");
+
+ mvwaddstr(dbg.win_reg,1,52,"C Z S O A P D I T ");
+}
+
+
+static void DrawBars(void) {
+ if (has_colors()) {
+ attrset(COLOR_PAIR(PAIR_BLACK_BLUE));
+ }
+ /* Show the Register bar */
+ mvaddstr(1-1,0, "-----(Register Overview )----- ");
+ /* Show the Data Overview bar perhaps with more special stuff in the end */
+ mvaddstr(6-1,0, "-----(Data Overview Scroll: page up/down)----- ");
+ /* Show the Code Overview perhaps with special stuff in bar too */
+ mvaddstr(15-1,0,"-----(Code Overview Scroll: up/down )----- ");
+ /* Show the Variable Overview bar */
+ mvaddstr(27-1,0,"-----(Variable Overview )----- ");
+ /* Show the Output OverView */
+ mvaddstr(32-1,0,"-----(Output Scroll: home/end )----- ");
+ attrset(0);
+ //Match values with below. So we don't need to touch the internal window structures
+}
+
+
+
+static void MakeSubWindows(void) {
+ /* The Std output win should go at the bottom */
+ /* Make all the subwindows */
+ int win_main_maxy, win_main_maxx; getmaxyx(dbg.win_main,win_main_maxy,win_main_maxx);
+ int outy=1; //Match values with above
+ /* The Register window */
+ dbg.win_reg=subwin(dbg.win_main,4,win_main_maxx,outy,0);
+ outy+=5; // 6
+ /* The Data Window */
+ dbg.win_data=subwin(dbg.win_main,8,win_main_maxx,outy,0);
+ outy+=9; // 15
+ /* The Code Window */
+ dbg.win_code=subwin(dbg.win_main,11,win_main_maxx,outy,0);
+ outy+=12; // 27
+ /* The Variable Window */
+ dbg.win_var=subwin(dbg.win_main,4,win_main_maxx,outy,0);
+ outy+=5; // 32
+ /* The Output Window */
+ dbg.win_out=subwin(dbg.win_main,win_main_maxy-outy,win_main_maxx,outy,0);
+ if(!dbg.win_reg ||!dbg.win_data || !dbg.win_code || !dbg.win_var || !dbg.win_out) E_Exit("Setting up windows failed");
+// dbg.input_y=win_main_maxy-1;
+ scrollok(dbg.win_out,TRUE);
+ DrawBars();
+ Draw_RegisterLayout();
+ refresh();
+}
+
+static void MakePairs(void) {
+ init_pair(PAIR_BLACK_BLUE, COLOR_BLACK, COLOR_CYAN);
+ init_pair(PAIR_BYELLOW_BLACK, COLOR_YELLOW /*| FOREGROUND_INTENSITY */, COLOR_BLACK);
+ init_pair(PAIR_GREEN_BLACK, COLOR_GREEN /*| FOREGROUND_INTENSITY */, COLOR_BLACK);
+ init_pair(PAIR_BLACK_GREY, COLOR_BLACK /*| FOREGROUND_INTENSITY */, COLOR_WHITE);
+ init_pair(PAIR_GREY_RED, COLOR_WHITE/*| FOREGROUND_INTENSITY */, COLOR_RED);
+}
+static void LOG_Destroy(Section*) {
+ if(debuglog) fclose(debuglog);
+ debuglog = 0;
+}
+
+static void LOG_Init(Section * sec) {
+ Section_prop * sect = static_cast<Section_prop *>(sec);
+ const char * blah = sect->Get_string("logfile");
+ if(blah && blah[0] && (debuglog = fopen(blah,"wt+"))){
+ ;
+ } else {
+ debuglog = 0;
+ }
+ sect->AddDestroyFunction(&LOG_Destroy);
+ char buf[64];
+ for (Bitu i = LOG_ALL + 1;i < LOG_MAX;i++) { //Skip LOG_ALL, it is always enabled
+ safe_strncpy(buf,loggrp[i].front,sizeof(buf));
+ lowcase(buf);
+ loggrp[i].enabled=sect->Get_bool(buf);
+ }
+}
+
+
+void LOG_StartUp(void) {
+ /* Setup logging groups */
+ loggrp[LOG_ALL].front="ALL";
+ loggrp[LOG_VGA].front="VGA";
+ loggrp[LOG_VGAGFX].front="VGAGFX";
+ loggrp[LOG_VGAMISC].front="VGAMISC";
+ loggrp[LOG_INT10].front="INT10";
+ loggrp[LOG_SB].front="SBLASTER";
+ loggrp[LOG_DMACONTROL].front="DMA_CONTROL";
+
+ loggrp[LOG_FPU].front="FPU";
+ loggrp[LOG_CPU].front="CPU";
+ loggrp[LOG_PAGING].front="PAGING";
+
+ loggrp[LOG_FCB].front="FCB";
+ loggrp[LOG_FILES].front="FILES";
+ loggrp[LOG_IOCTL].front="IOCTL";
+ loggrp[LOG_EXEC].front="EXEC";
+ loggrp[LOG_DOSMISC].front="DOSMISC";
+
+ loggrp[LOG_PIT].front="PIT";
+ loggrp[LOG_KEYBOARD].front="KEYBOARD";
+ loggrp[LOG_PIC].front="PIC";
+
+ loggrp[LOG_MOUSE].front="MOUSE";
+ loggrp[LOG_BIOS].front="BIOS";
+ loggrp[LOG_GUI].front="GUI";
+ loggrp[LOG_MISC].front="MISC";
+
+ loggrp[LOG_IO].front="IO";
+ loggrp[LOG_PCI].front="PCI";
+
+ /* Register the log section */
+ Section_prop * sect=control->AddSection_prop("log",LOG_Init);
+ Prop_string* Pstring = sect->Add_string("logfile",Property::Changeable::Always,"");
+ Pstring->Set_help("file where the log messages will be saved to");
+ char buf[64];
+ for (Bitu i = LOG_ALL + 1;i < LOG_MAX;i++) {
+ safe_strncpy(buf,loggrp[i].front, sizeof(buf));
+ lowcase(buf);
+ Prop_bool* Pbool = sect->Add_bool(buf,Property::Changeable::Always,true);
+ Pbool->Set_help("Enable/Disable logging of this type.");
+ }
+// MSG_Add("LOG_CONFIGFILE_HELP","Logging related options for the debugger.\n");
+}
+
+
+
+
+void DBGUI_StartUp(void) {
+ /* Start the main window */
+ dbg.win_main=initscr();
+ cbreak(); /* take input chars one at a time, no wait for \n */
+ noecho(); /* don't echo input */
+ nodelay(dbg.win_main,true);
+ keypad(dbg.win_main,true);
+ #ifndef WIN32
+ printf("\e[8;50;80t");
+ fflush(NULL);
+ resizeterm(50,80);
+ touchwin(dbg.win_main);
+ #endif
+ old_cursor_state = curs_set(0);
+ start_color();
+ cycle_count=0;
+ MakePairs();
+ MakeSubWindows();
+
+}
+
+#endif
diff --git a/src/debug/debug_inc.h b/src/debug/debug_inc.h
new file mode 100644
index 000000000..0c8f638de
--- /dev/null
+++ b/src/debug/debug_inc.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+/* Local Debug Function */
+
+
+#include <curses.h>
+#include "mem.h"
+
+#define PAIR_BLACK_BLUE 1
+#define PAIR_BYELLOW_BLACK 2
+#define PAIR_GREEN_BLACK 3
+#define PAIR_BLACK_GREY 4
+#define PAIR_GREY_RED 5
+
+
+void DBGUI_StartUp(void);
+
+struct DBGBlock {
+ WINDOW * win_main; /* The Main Window */
+ WINDOW * win_reg; /* Register Window */
+ WINDOW * win_data; /* Data Output window */
+ WINDOW * win_code; /* Disassembly/Debug point Window */
+ WINDOW * win_var; /* Variable Window */
+ WINDOW * win_out; /* Text Output Window */
+ Bit32u active_win; /* Current active window */
+ Bit32u input_y;
+ Bit32u global_mask; /* Current msgmask */
+};
+
+
+struct DASMLine {
+ Bit32u pc;
+ char dasm[80];
+ PhysPt ea;
+ Bit16u easeg;
+ Bit32u eaoff;
+};
+
+extern DBGBlock dbg;
+
+/* Local Debug Stuff */
+Bitu DasmI386(char* buffer, PhysPt pc, Bitu cur_ip, bool bit32);
+int DasmLastOperandSize(void);
+
diff --git a/src/debug/debug_win32.cpp b/src/debug/debug_win32.cpp
new file mode 100644
index 000000000..090ab693c
--- /dev/null
+++ b/src/debug/debug_win32.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include "dosbox.h"
+#if C_DEBUG
+#ifdef WIN32
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifndef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+
+/*
+ Have to remember where i ripped this code sometime ago.
+
+*/
+static void ResizeConsole( HANDLE hConsole, SHORT xSize, SHORT ySize ) {
+ CONSOLE_SCREEN_BUFFER_INFO csbi; // Hold Current Console Buffer Info
+ BOOL bSuccess;
+ SMALL_RECT srWindowRect; // Hold the New Console Size
+ COORD coordScreen;
+
+ bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi );
+
+ // Get the Largest Size we can size the Console Window to
+ coordScreen = GetLargestConsoleWindowSize( hConsole );
+
+ // Define the New Console Window Size and Scroll Position
+ srWindowRect.Right = (SHORT)(min(xSize, coordScreen.X) - 1);
+ srWindowRect.Bottom = (SHORT)(min(ySize, coordScreen.Y) - 1);
+ srWindowRect.Left = srWindowRect.Top = (SHORT)0;
+
+ // Define the New Console Buffer Size
+ coordScreen.X = xSize;
+ coordScreen.Y = ySize;
+
+ // If the Current Buffer is Larger than what we want, Resize the
+ // Console Window First, then the Buffer
+ if( (DWORD)csbi.dwSize.X * csbi.dwSize.Y > (DWORD) xSize * ySize)
+ {
+ bSuccess = SetConsoleWindowInfo( hConsole, TRUE, &srWindowRect );
+ bSuccess = SetConsoleScreenBufferSize( hConsole, coordScreen );
+ }
+
+ // If the Current Buffer is Smaller than what we want, Resize the
+ // Buffer First, then the Console Window
+ if( (DWORD)csbi.dwSize.X * csbi.dwSize.Y < (DWORD) xSize * ySize )
+ {
+ bSuccess = SetConsoleScreenBufferSize( hConsole, coordScreen );
+ bSuccess = SetConsoleWindowInfo( hConsole, TRUE, &srWindowRect );
+ }
+
+ // If the Current Buffer *is* the Size we want, Don't do anything!
+ return;
+ }
+
+
+void WIN32_Console() {
+ AllocConsole();
+ SetConsoleTitle("DOSBox Debugger");
+ ResizeConsole(GetStdHandle(STD_OUTPUT_HANDLE),80,50);
+}
+#endif
+#endif
diff --git a/src/debug/disasm_tables.h b/src/debug/disasm_tables.h
new file mode 100644
index 000000000..25453a312
--- /dev/null
+++ b/src/debug/disasm_tables.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+char * RegTable_16[8]= {"ax","cx","dx","bx","sp","bp","si","di"};
+char * RegTable_8[8]= {"al","cl","dl","bl","ah","ch","dh","bh"};
+char * SegTable[8]= {"es","cs","ss","ds","fs","gs","iseg","iseg"};
+
+
+#define MAX_INFO 3
+
+enum {
+ Eb,Ev,Ew,Ep,
+ Gb,Gv,
+ Rb,Rw,Rv,
+ Ob,Ov,
+ Sw,
+ Mp,
+ Ib,Ibs,Iw,Iv,Ap,
+ Jb,Jv,
+ Bd,Bw,
+ XBnd,Xlea,
+/* specials */
+ b2,p_es,p_ss,p_cs,p_ds,p_fs,p_gs,p_size,p_addr,p_rep,
+ s_ax,s_cx,s_dx,s_bx,s_sp,s_bp,s_si,s_di,
+ s_al,s_cl,s_dl,s_bl,s_ah,s_ch,s_dh,s_bh,
+ s_1,
+ Cj,
+ G1,G2,G3b,G3v,G4,G5,
+ no=0xff
+};
+
+enum {
+ s_jo, s_jno, s_jc, s_jnc, s_je, s_jne, s_jbe, s_jnbe,
+ s_js, s_jns, s_jp, s_jnp, s_jl, s_jnl, s_jle, s_jnle
+};
+
+
+struct Dentry {
+ char * start;
+ Bit8u info[MAX_INFO];
+};
+
+static char * G1_Table[8]={"add ","or ","adc ","sbb ","and ","sub ","xor ","cmp "};
+static char * G2_Table[8]={"rol ","ror ","rcl ","rcr ","shl ","shr ","sal ","sar "};
+
+static Dentry G3b_Table[8]={
+ "test ",Eb,Ib,no,
+ "test ",Eb,Ib,no,
+ "not ",Eb,no,no,
+ "neg ",Eb,no,no,
+ "mul al,",Eb,no,no,
+ "imul al,",Eb,no,no,
+ "div ax,",Eb,no,no,
+ "idiv ax,",Eb,no,no
+};
+
+static Dentry G3v_Table[8]={
+ "test ",Ev,Iv,no,
+ "test ",Ev,Iv,no,
+ "not ",Ev,no,no,
+ "neg ",Ev,no,no,
+ "mul ax,",Ev,no,no,
+ "imul ax,",Ev,no,no,
+ "div dx:ax,",Ev,no,no,
+ "idiv dx:ax,",Ev,no,no
+};
+
+static char * G4_Table[8]={
+ "inc ",
+ "dec ",
+ "illegal",
+ "illegal",
+ "illegal",
+ "illegal",
+ "illegal",
+ "illegal"
+};
+
+static Dentry G5_Table[8]={
+ "inc ",Ev,no,no,
+ "dec ",Ev,no,no,
+ "call ",Ev,no,no,
+ "call ",Ep,no,no,
+ "jmp ",Ev,no,no,
+ "jmp ",Ep,no,no,
+ "push ,",Ev,no,no,
+ "illegal",no,no,no
+};
+
+
+
+static Dentry DTable[256]={
+/* 0 */
+ "add ",Eb,Gb,no, "add ",Ev,Gv,no, "add ",Gb,Eb,no, "add ",Gv,Ev,no,
+ "add ",s_al,Ib,no, "add ",s_ax,Iv,no, "push es",no,no,no, "pop es",no,no,no,
+ "or ",Eb,Gb,no, "or ",Ev,Gv,no, "or ",Gb,Eb,no, "or ",Gv,Ev,no,
+ "or ",s_al,Ib,no, "or ",s_ax,Iv,no, "push cs",no,no,no, "",b2,no,no,
+/* 1 */
+ "adc ",Eb,Gb,no, "adc ",Ev,Gv,no, "adc ",Gb,Eb,no, "adc ",Gv,Ev,no,
+ "adc ",s_al,Ib,no, "adc ",s_ax,Iv,no, "push ss",no,no,no, "pop ss",no,no,no,
+ "sbb ",Eb,Gb,no, "sbb ",Ev,Gv,no, "sbb ",Gb,Eb,no, "sbb ",Gv,Ev,no,
+ "sbb ",s_al,Ib,no, "sbb ",s_ax,Iv,no, "push ds",no,no,no, "pop ds",no,no,no,
+/* 2 */
+ "and ",Eb,Gb,no, "and ",Ev,Gv,no, "and ",Gb,Eb,no, "and ",Gv,Ev,no,
+ "and ",s_al,Ib,no, "and ",s_ax,Iv,no, "",p_es,no,no, "daa",no,no,no,
+ "sub ",Eb,Gb,no, "sub ",Ev,Gv,no, "sub ",Gb,Eb,no, "sub ",Gv,Ev,no,
+ "sub ",s_al,Ib,no, "sub ",s_ax,Iv,no, "",p_ss,no,no, "das",no,no,no,
+/* 3 */
+ "xor ",Eb,Gb,no, "xor ",Ev,Gv,no, "xor ",Gb,Eb,no, "xor ",Gv,Ev,no,
+ "xor ",s_al,Ib,no, "xor ",s_ax,Iv,no, "",p_ss,no,no, "aaa",no,no,no,
+ "cmp ",Eb,Gb,no, "cmp ",Ev,Gv,no, "cmp ",Gb,Eb,no, "cmp ",Gv,Ev,no,
+ "cmp ",s_al,Ib,no, "cmp ",s_ax,Iv,no, "",p_ds,no,no, "aas",no,no,no,
+/* 4 */
+ "inc ",s_ax,no,no, "inc ",s_cx,no,no, "inc ",s_dx,no,no, "inc ",s_bx,no,no,
+ "inc ",s_sp,no,no, "inc ",s_bp,no,no, "inc ",s_si,no,no, "inc ",s_di,no,no,
+ "dec ",s_ax,no,no, "dec ",s_cx,no,no, "dec ",s_dx,no,no, "dec ",s_bx,no,no,
+ "dec ",s_sp,no,no, "dec ",s_bp,no,no, "dec ",s_si,no,no, "dec ",s_di,no,no,
+/* 5 */
+ "push ",s_ax,no,no, "push ",s_cx,no,no, "push ",s_dx,no,no, "push ",s_bx,no,no,
+ "push ",s_sp,no,no, "push ",s_bp,no,no, "push ",s_si,no,no, "push ",s_di,no,no,
+ "pop ",s_ax,no,no, "pop ",s_cx,no,no, "pop ",s_dx,no,no, "pop ",s_bx,no,no,
+ "pop ",s_sp,no,no, "pop ",s_bp,no,no, "pop ",s_si,no,no, "pop ",s_di,no,no,
+/* 6 */
+ "pusha",Bd,no,no, "popa",Bd,no,no, "bound",XBnd,no,no, "arpl",Ew,Rw,no,
+ "",p_fs,no,no, "",p_gs,no,no, "",p_size,no,no, "",p_addr,no,no,
+ "push ",Iv,no,no, "imul ",Gv,Ev,Iv, "push ",Ibs,no,no, "imul ",Gv,Ev,Ib,
+ "insb",no,no,no, "ins",Bw,no,no, "oustb",no,no,no, "outs",Bw,no,no,
+/* 7 */
+ "jo ",Cj,s_jo,no, "jno ",Cj,s_jno,no, "jc ",Cj,s_jc,no, "jnc ",Cj,s_jnc,no,
+ "je ",Cj,s_je,no, "jne ",Cj,s_jne,no, "jbe ",Cj,s_jbe,no, "jnbe ",Cj,s_jnbe,no,
+ "js ",Cj,s_js,no, "jns ",Cj,s_jns,no, "jp ",Cj,s_jp,no, "jnp ",Cj,s_jnp,no,
+ "jl ",Cj,s_jl,no, "jnl ",Cj,s_jnl,no, "jle ",Cj,s_jle,no, "jnle ",Cj,s_jnle,no,
+/* 8 */
+ "",G1,Eb,Ib, "",G1,Ev,Iv, "",G1,Eb,Ib, "",G1,Ev,Ibs,
+ "test ",Eb,Gb,no, "test ",Ev,Gv,no, "xchg ",Eb,Gb,no, "xchg ",Ev,Gv,no,
+ "mov ",Eb,Gb,no, "mov ",Ev,Gv,no, "mov ",Gb,Eb,no, "mov ",Gv,Ev,no,
+ "mov ",Ew,Sw,no, "lea ",Gv,Xlea,no, "mov ",Sw,Ew,no, "pop ",Ev,no,no,
+/* 9 */
+ "nop",no,no,no, "xchg ",s_ax,s_cx,no,"xchg ",s_ax,s_dx,no,"xchg ",s_ax,s_bx,no,
+ "xchg ",s_ax,s_sp,no,"xchg ",s_ax,s_bp,no,"xchg ",s_ax,s_si,no,"xchg ",s_ax,s_di,no,
+ "cbw",no,no,no, "cwd",no,no,no, "call ",Ap,no,no, "fwait",no,no,no,
+ "pushf",Bd,no,no, "popf",Bd,no,no, "sahf",no,no,no, "lahf",no,no,no,
+/* a */
+ "mov ",s_al,Ob,no, "mov ",s_ax,Ov,no, "mov ",Ob,s_al,no, "mov ",Ov,s_ax,no,
+ "movsb",no,no,no, "movs",Bw,no,no, "cmpsb",no,no,no, "cmps",Bw,no,no,
+ "test ",s_al,Ib,no, "test ",s_ax,Iv,no, "stosb",no,no,no, "stos",Bw,no,no,
+ "lodsb",no,no,no, "lods",Bw,no,no, "scasb",no,no,no, "scas",Bw,no,no,
+/* b */
+ "mov ",s_al,Ib,no, "mov ",s_cl,Ib,no, "mov ",s_dl,Ib,no, "mov ",s_bl,Ib,no,
+ "mov ",s_ah,Ib,no, "mov ",s_ch,Ib,no, "mov ",s_dh,Ib,no, "mov ",s_bh,Ib,no,
+ "mov ",s_ax,Iv,no, "mov ",s_cx,Iv,no, "mov ",s_dx,Iv,no, "mov ",s_bx,Iv,no,
+ "mov ",s_sp,Iv,no, "mov ",s_bp,Iv,no, "mov ",s_si,Iv,no, "mov ",s_di,Iv,no,
+/* c */
+ "",G2,Eb,Ib, "",G2,Ev,Ib, "ret ",Iw,no,no, "ret",no,no,no,
+ "les ",Gv,Mp,no, "lds ",Gv,Mp,no, "mov ",Eb,Ib,no, "mov ",Ev,Iv,no,
+ "enter ",Iw,Ib,no, "leave",no,no,no, "retf ",Iw,no,no, "retf",no,no,no,
+ "int 03",no,no,no, "int ",Ib,no,no, "into",no,no,no, "iret",Bd,no,no,
+/* d */
+ "",G2,Eb,s_1, "",G2,Ev,s_1, "",G2,Eb,s_cl, "",G2,Ev,s_cl,
+ "aam",no,no,no, "aad",no,no,no, "setalc",no,no,no, "xlat",no,no,no,
+ "esc 0",Ib,no,no, "esc 1",Ib,no,no, "esc 2",Ib,no,no, "esc 3",Ib,no,no,
+ "esc 4",Ib,no,no, "esc 5",Ib,no,no, "esc 6",Ib,no,no, "esc 7",Ib,no,no,
+/* e */
+ "loopne ",Jb,no,no, "loope ",Jb,no,no, "loop ",Jb,no,no, "jcxz ",Jb,no,no,
+ "in ",s_al,Ib,no, "in ",s_ax,Ib,no, "out ",Ib,s_al,no, "out ",Ib,s_ax,no,
+ "call ",Jv,no,no, "jmp ",Jv,no,no, "jmp",Ap,no,no, "jmp ",Jb,no,no,
+ "in ",s_al,s_dx,no, "in ",s_ax,s_dx,no, "out ",s_dx,s_al,no,"out ",s_dx,s_ax,no,
+/* f */
+ "lock",no,no,no, "cb ",Iw,no,no, "repne ",p_rep,no,no,"repe ",p_rep,no,no,
+ "hlt",no,no,no, "cmc",no,no,no, "",G3b,no,no, "",G3v,no,no,
+ "clc",no,no,no, "stc",no,no,no, "cli",no,no,no, "sti",no,no,no,
+ "cld",no,no,no, "std",no,no,no, "",G4,Eb,no, "",G5,no,no,
+};
+
+
+
diff --git a/src/dos/Makefile.am b/src/dos/Makefile.am
new file mode 100644
index 000000000..36a2d99b0
--- /dev/null
+++ b/src/dos/Makefile.am
@@ -0,0 +1,10 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libdos.a
+EXTRA_DIST = scsidefs.h wnaspi32.h dos_codepages.h dos_keyboard_layout_data.h
+libdos_a_SOURCES = dos.cpp dos_devices.cpp dos_execute.cpp dos_files.cpp dos_ioctl.cpp dos_memory.cpp \
+ dos_misc.cpp dos_classes.cpp dos_programs.cpp dos_tables.cpp \
+ drives.cpp drives.h drive_virtual.cpp drive_local.cpp drive_cache.cpp drive_fat.cpp \
+ drive_iso.cpp dev_con.h dos_mscdex.cpp dos_keyboard_layout.cpp \
+ cdrom.h cdrom.cpp cdrom_ioctl_win32.cpp cdrom_aspi_win32.cpp cdrom_ioctl_linux.cpp cdrom_image.cpp \
+ cdrom_ioctl_os2.cpp drive_overlay.cpp
diff --git a/src/dos/cdrom.cpp b/src/dos/cdrom.cpp
new file mode 100644
index 000000000..06d47de16
--- /dev/null
+++ b/src/dos/cdrom.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+// ******************************************************
+// SDL CDROM
+// ******************************************************
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "dosbox.h"
+#include "SDL.h"
+#include "support.h"
+#include "cdrom.h"
+
+CDROM_Interface_SDL::CDROM_Interface_SDL(void) {
+ driveID = 0;
+ oldLeadOut = 0;
+ cd = 0;
+}
+
+CDROM_Interface_SDL::~CDROM_Interface_SDL(void) {
+ StopAudio();
+ SDL_CDClose(cd);
+ cd = 0;
+}
+
+bool CDROM_Interface_SDL::SetDevice(char* path, int forceCD) {
+ char buffer[512];
+ strcpy(buffer,path);
+ upcase(buffer);
+
+ int num = SDL_CDNumDrives();
+ if ((forceCD>=0) && (forceCD<num)) {
+ driveID = forceCD;
+ cd = SDL_CDOpen(driveID);
+ SDL_CDStatus(cd);
+ return true;
+ };
+
+ const char* cdname = 0;
+ for (int i=0; i<num; i++) {
+ cdname = SDL_CDName(i);
+ if (strcmp(buffer,cdname)==0) {
+ cd = SDL_CDOpen(i);
+ SDL_CDStatus(cd);
+ driveID = i;
+ return true;
+ };
+ };
+ return false;
+}
+
+bool CDROM_Interface_SDL::GetAudioTracks(int& stTrack, int& end, TMSF& leadOut) {
+
+ if (CD_INDRIVE(SDL_CDStatus(cd))) {
+ stTrack = 1;
+ end = cd->numtracks;
+ FRAMES_TO_MSF(cd->track[cd->numtracks].offset,&leadOut.min,&leadOut.sec,&leadOut.fr);
+ }
+ return CD_INDRIVE(SDL_CDStatus(cd));
+}
+
+bool CDROM_Interface_SDL::GetAudioTrackInfo(int track, TMSF& start, unsigned char& attr) {
+ if (CD_INDRIVE(SDL_CDStatus(cd))) {
+ FRAMES_TO_MSF(cd->track[track-1].offset,&start.min,&start.sec,&start.fr);
+ attr = cd->track[track-1].type<<4;//sdl uses 0 for audio and 4 for data. instead of 0x00 and 0x40
+ }
+ return CD_INDRIVE(SDL_CDStatus(cd));
+}
+
+bool CDROM_Interface_SDL::GetAudioSub(unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos) {
+ if (CD_INDRIVE(SDL_CDStatus(cd))) {
+ track = cd->cur_track;
+ index = cd->cur_track;
+ attr = cd->track[track].type<<4;
+ FRAMES_TO_MSF(cd->cur_frame,&relPos.min,&relPos.sec,&relPos.fr);
+ FRAMES_TO_MSF(cd->cur_frame+cd->track[track].offset,&absPos.min,&absPos.sec,&absPos.fr);
+ }
+ return CD_INDRIVE(SDL_CDStatus(cd));
+}
+
+bool CDROM_Interface_SDL::GetAudioStatus(bool& playing, bool& pause){
+ if (CD_INDRIVE(SDL_CDStatus(cd))) {
+ playing = (cd->status==CD_PLAYING);
+ pause = (cd->status==CD_PAUSED);
+ }
+ return CD_INDRIVE(SDL_CDStatus(cd));
+}
+
+bool CDROM_Interface_SDL::GetMediaTrayStatus(bool& mediaPresent, bool& mediaChanged, bool& trayOpen) {
+ SDL_CDStatus(cd);
+ mediaPresent = (cd->status!=CD_TRAYEMPTY) && (cd->status!=CD_ERROR);
+ mediaChanged = (oldLeadOut!=cd->track[cd->numtracks].offset);
+ trayOpen = !mediaPresent;
+ oldLeadOut = cd->track[cd->numtracks].offset;
+ if (mediaChanged) SDL_CDStatus(cd);
+ return true;
+}
+
+bool CDROM_Interface_SDL::PlayAudioSector(unsigned long start,unsigned long len) {
+ // Has to be there, otherwise wrong cd status report (dunno why, sdl bug ?)
+ SDL_CDClose(cd);
+ cd = SDL_CDOpen(driveID);
+ bool success = (SDL_CDPlay(cd,start+150,len)==0);
+ return success;
+}
+
+bool CDROM_Interface_SDL::PauseAudio(bool resume) {
+ bool success;
+ if (resume) success = (SDL_CDResume(cd)==0);
+ else success = (SDL_CDPause (cd)==0);
+ return success;
+}
+
+bool CDROM_Interface_SDL::StopAudio(void) {
+ // Has to be there, otherwise wrong cd status report (dunno why, sdl bug ?)
+ SDL_CDClose(cd);
+ cd = SDL_CDOpen(driveID);
+ bool success = (SDL_CDStop(cd)==0);
+ return success;
+}
+
+bool CDROM_Interface_SDL::LoadUnloadMedia(bool unload) {
+ bool success = (SDL_CDEject(cd)==0);
+ return success;
+}
+
+int CDROM_GetMountType(char* path, int forceCD) {
+// 0 - physical CDROM
+// 1 - Iso file
+// 2 - subdirectory
+ // 1. Smells like a real cdrom
+ // if ((strlen(path)<=3) && (path[2]=='\\') && (strchr(path,'\\')==strrchr(path,'\\')) && (GetDriveType(path)==DRIVE_CDROM)) return 0;
+
+ const char* cdName;
+ char buffer[512];
+ strcpy(buffer,path);
+#if defined (WIN32) || defined(OS2)
+ upcase(buffer);
+#endif
+
+ int num = SDL_CDNumDrives();
+ // If cd drive is forced then check if its in range and return 0
+ if ((forceCD>=0) && (forceCD<num)) {
+ LOG(LOG_ALL,LOG_ERROR)("CDROM: Using drive %d",forceCD);
+ return 0;
+ }
+
+ // compare names
+ for (int i=0; i<num; i++) {
+ cdName = SDL_CDName(i);
+ if (strcmp(buffer,cdName)==0) return 0;
+ };
+
+ // Detect ISO
+ struct stat file_stat;
+ if ((stat(path, &file_stat) == 0) && (file_stat.st_mode & S_IFREG)) return 1;
+ return 2;
+}
+
+// ******************************************************
+// Fake CDROM
+// ******************************************************
+
+bool CDROM_Interface_Fake :: GetAudioTracks(int& stTrack, int& end, TMSF& leadOut) {
+ stTrack = end = 1;
+ leadOut.min = 60;
+ leadOut.sec = leadOut.fr = 0;
+ return true;
+}
+
+bool CDROM_Interface_Fake :: GetAudioTrackInfo(int track, TMSF& start, unsigned char& attr) {
+ if (track>1) return false;
+ start.min = start.fr = 0;
+ start.sec = 2;
+ attr = 0x60; // data / permitted
+ return true;
+}
+
+bool CDROM_Interface_Fake :: GetAudioSub(unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos){
+ attr = 0;
+ track = index = 1;
+ relPos.min = relPos.fr = 0; relPos.sec = 2;
+ absPos.min = absPos.fr = 0; absPos.sec = 2;
+ return true;
+}
+
+bool CDROM_Interface_Fake :: GetAudioStatus(bool& playing, bool& pause) {
+ playing = pause = false;
+ return true;
+}
+
+bool CDROM_Interface_Fake :: GetMediaTrayStatus(bool& mediaPresent, bool& mediaChanged, bool& trayOpen) {
+ mediaPresent = true;
+ mediaChanged = false;
+ trayOpen = false;
+ return true;
+}
+
+
diff --git a/src/dos/cdrom.h b/src/dos/cdrom.h
new file mode 100644
index 000000000..c1219c7a9
--- /dev/null
+++ b/src/dos/cdrom.h
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef __CDROM_INTERFACE__
+#define __CDROM_INTERFACE__
+
+#define MAX_ASPI_CDROM 5
+
+#include <string.h>
+#include <string>
+#include <iostream>
+#include <vector>
+#include <fstream>
+#include <sstream>
+#include "dosbox.h"
+#include "mem.h"
+#include "mixer.h"
+#include "SDL.h"
+#include "SDL_thread.h"
+
+#if defined(C_SDL_SOUND)
+#include "SDL_sound.h"
+#endif
+
+#define RAW_SECTOR_SIZE 2352
+#define COOKED_SECTOR_SIZE 2048
+
+enum { CDROM_USE_SDL, CDROM_USE_ASPI, CDROM_USE_IOCTL_DIO, CDROM_USE_IOCTL_DX, CDROM_USE_IOCTL_MCI };
+
+typedef struct SMSF {
+ unsigned char min;
+ unsigned char sec;
+ unsigned char fr;
+} TMSF;
+
+typedef struct SCtrl {
+ Bit8u out[4]; // output channel
+ Bit8u vol[4]; // channel volume
+} TCtrl;
+
+extern int CDROM_GetMountType(char* path, int force);
+
+class CDROM_Interface
+{
+public:
+// CDROM_Interface (void);
+ virtual ~CDROM_Interface (void) {};
+
+ virtual bool SetDevice (char* path, int forceCD) = 0;
+
+ virtual bool GetUPC (unsigned char& attr, char* upc) = 0;
+
+ virtual bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut) = 0;
+ virtual bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr) = 0;
+ virtual bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos) = 0;
+ virtual bool GetAudioStatus (bool& playing, bool& pause) = 0;
+ virtual bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen) = 0;
+
+ virtual bool PlayAudioSector (unsigned long start,unsigned long len) = 0;
+ virtual bool PauseAudio (bool resume) = 0;
+ virtual bool StopAudio (void) = 0;
+ virtual void ChannelControl (TCtrl ctrl) = 0;
+
+ virtual bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num) = 0;
+
+ virtual bool LoadUnloadMedia (bool unload) = 0;
+
+ virtual void InitNewMedia (void) {};
+};
+
+class CDROM_Interface_SDL : public CDROM_Interface
+{
+public:
+ CDROM_Interface_SDL (void);
+ virtual ~CDROM_Interface_SDL(void);
+
+ virtual bool SetDevice (char* path, int forceCD);
+ virtual bool GetUPC (unsigned char& attr, char* upc) { attr = 0; strcpy(upc,"UPC"); return true; };
+ virtual bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut);
+ virtual bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr);
+ virtual bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos);
+ virtual bool GetAudioStatus (bool& playing, bool& pause);
+ virtual bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen);
+ virtual bool PlayAudioSector (unsigned long start,unsigned long len);
+ virtual bool PauseAudio (bool resume);
+ virtual bool StopAudio (void);
+ virtual void ChannelControl (TCtrl ctrl) { return; };
+ virtual bool ReadSectors (PhysPt /*buffer*/, bool /*raw*/, unsigned long /*sector*/, unsigned long /*num*/) { return false; };
+ virtual bool LoadUnloadMedia (bool unload);
+
+private:
+ bool Open (void);
+ void Close (void);
+
+ SDL_CD* cd;
+ int driveID;
+ Uint32 oldLeadOut;
+};
+
+class CDROM_Interface_Fake : public CDROM_Interface
+{
+public:
+ bool SetDevice (char* /*path*/, int /*forceCD*/) { return true; };
+ bool GetUPC (unsigned char& attr, char* upc) { attr = 0; strcpy(upc,"UPC"); return true; };
+ bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut);
+ bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr);
+ bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos);
+ bool GetAudioStatus (bool& playing, bool& pause);
+ bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen);
+ bool PlayAudioSector (unsigned long /*start*/,unsigned long /*len*/) { return true; };
+ bool PauseAudio (bool /*resume*/) { return true; };
+ bool StopAudio (void) { return true; };
+ void ChannelControl (TCtrl ctrl) { return; };
+ bool ReadSectors (PhysPt /*buffer*/, bool /*raw*/, unsigned long /*sector*/, unsigned long /*num*/) { return true; };
+ bool LoadUnloadMedia (bool /*unload*/) { return true; };
+};
+
+class CDROM_Interface_Image : public CDROM_Interface
+{
+private:
+ class TrackFile {
+ public:
+ virtual bool read(Bit8u *buffer, int seek, int count) = 0;
+ virtual int getLength() = 0;
+ virtual ~TrackFile() { };
+ };
+
+ class BinaryFile : public TrackFile {
+ public:
+ BinaryFile(const char *filename, bool &error);
+ ~BinaryFile();
+ bool read(Bit8u *buffer, int seek, int count);
+ int getLength();
+ private:
+ BinaryFile();
+ std::ifstream *file;
+ };
+
+ #if defined(C_SDL_SOUND)
+ class AudioFile : public TrackFile {
+ public:
+ AudioFile(const char *filename, bool &error);
+ ~AudioFile();
+ bool read(Bit8u *buffer, int seek, int count);
+ int getLength();
+ private:
+ AudioFile();
+ Sound_Sample *sample;
+ int lastCount;
+ int lastSeek;
+ };
+ #endif
+
+ struct Track {
+ int number;
+ int attr;
+ int start;
+ int length;
+ int skip;
+ int sectorSize;
+ bool mode2;
+ TrackFile *file;
+ };
+
+public:
+ CDROM_Interface_Image (Bit8u subUnit);
+ virtual ~CDROM_Interface_Image (void);
+ void InitNewMedia (void);
+ bool SetDevice (char* path, int forceCD);
+ bool GetUPC (unsigned char& attr, char* upc);
+ bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut);
+ bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr);
+ bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos);
+ bool GetAudioStatus (bool& playing, bool& pause);
+ bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen);
+ bool PlayAudioSector (unsigned long start,unsigned long len);
+ bool PauseAudio (bool resume);
+ bool StopAudio (void);
+ void ChannelControl (TCtrl ctrl);
+ bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num);
+ bool LoadUnloadMedia (bool unload);
+ bool ReadSector (Bit8u *buffer, bool raw, unsigned long sector);
+ bool HasDataTrack (void);
+
+static CDROM_Interface_Image* images[26];
+
+private:
+ // player
+static void CDAudioCallBack(Bitu len);
+ int GetTrack(int sector);
+
+static struct imagePlayer {
+ CDROM_Interface_Image *cd;
+ MixerChannel *channel;
+ SDL_mutex *mutex;
+ Bit8u buffer[8192];
+ int bufLen;
+ int currFrame;
+ int targetFrame;
+ bool isPlaying;
+ bool isPaused;
+ bool ctrlUsed;
+ TCtrl ctrlData;
+ } player;
+
+ void ClearTracks();
+ bool LoadIsoFile(char *filename);
+ bool CanReadPVD(TrackFile *file, int sectorSize, bool mode2);
+ // cue sheet processing
+ bool LoadCueSheet(char *cuefile);
+ bool GetRealFileName(std::string& filename, std::string& pathname);
+ bool GetCueKeyword(std::string &keyword, std::istream &in);
+ bool GetCueFrame(int &frames, std::istream &in);
+ bool GetCueString(std::string &str, std::istream &in);
+ bool AddTrack(Track &curr, int &shift, int prestart, int &totalPregap, int currPregap);
+
+static int refCount;
+ std::vector<Track> tracks;
+typedef std::vector<Track>::iterator track_it;
+ std::string mcn;
+ Bit8u subUnit;
+};
+
+#if defined (WIN32) /* Win 32 */
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+#include <windows.h>
+#include "wnaspi32.h" // Aspi stuff
+
+class CDROM_Interface_Aspi : public CDROM_Interface
+{
+public:
+ CDROM_Interface_Aspi (void);
+ virtual ~CDROM_Interface_Aspi(void);
+
+ bool SetDevice (char* path, int forceCD);
+
+ bool GetUPC (unsigned char& attr, char* upc);
+
+ bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut);
+ bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr);
+ bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos);
+ bool GetAudioStatus (bool& playing, bool& pause);
+ bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen);
+
+ bool PlayAudioSector (unsigned long start,unsigned long len);
+ bool PauseAudio (bool resume);
+ bool StopAudio (void);
+ void ChannelControl (TCtrl ctrl) { return; };
+
+ bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num);
+
+ bool LoadUnloadMedia (bool unload);
+
+private:
+ DWORD GetTOC (LPTOC toc);
+ HANDLE OpenIOCTLFile (char cLetter, BOOL bAsync);
+ void GetIOCTLAdapter (HANDLE hF,int * iDA,int * iDT,int * iDL);
+ bool ScanRegistryFindKey (HKEY& hKeyBase);
+ bool ScanRegistry (HKEY& hKeyBase);
+ BYTE GetHostAdapter (char* hardwareID);
+ bool GetVendor (BYTE HA_num, BYTE SCSI_Id, BYTE SCSI_Lun, char* szBuffer);
+
+ // ASPI stuff
+ BYTE haId;
+ BYTE target;
+ BYTE lun;
+ char letter;
+
+ // Windows stuff
+ HINSTANCE hASPI;
+ HANDLE hEvent; // global event
+ DWORD (*pGetASPI32SupportInfo) (void); // ptrs to aspi funcs
+ DWORD (*pSendASPI32Command) (LPSRB);
+ TMSF oldLeadOut;
+};
+
+class CDROM_Interface_Ioctl : public CDROM_Interface
+{
+public:
+ enum cdioctl_cdatype { CDIOCTL_CDA_DIO, CDIOCTL_CDA_MCI, CDIOCTL_CDA_DX };
+ cdioctl_cdatype cdioctl_cda_selected;
+
+ CDROM_Interface_Ioctl (cdioctl_cdatype ioctl_cda);
+ virtual ~CDROM_Interface_Ioctl(void);
+
+ bool SetDevice (char* path, int forceCD);
+
+ bool GetUPC (unsigned char& attr, char* upc);
+
+ bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut);
+ bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr);
+ bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos);
+ bool GetAudioStatus (bool& playing, bool& pause);
+ bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen);
+
+ bool PlayAudioSector (unsigned long start,unsigned long len);
+ bool PauseAudio (bool resume);
+ bool StopAudio (void);
+ void ChannelControl (TCtrl ctrl);
+
+ bool ReadSector (Bit8u *buffer, bool raw, unsigned long sector);
+ bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num);
+
+ bool LoadUnloadMedia (bool unload);
+
+ void InitNewMedia (void) { Close(); Open(); };
+private:
+
+ bool Open (void);
+ void Close (void);
+
+ char pathname[32];
+ HANDLE hIOCTL;
+ TMSF oldLeadOut;
+
+
+ /* track start/length data */
+ bool track_start_valid;
+ int track_start_first,track_start_last;
+ int track_start[128];
+
+ bool GetAudioTracksAll (void);
+
+
+ /* mci audio cd interface */
+ bool use_mciplay;
+ int mci_devid;
+
+ bool mci_CDioctl (UINT msg, DWORD flags, void *arg);
+ bool mci_CDOpen (char drive);
+ bool mci_CDClose (void);
+ bool mci_CDPlay (int start, int length);
+ bool mci_CDPause (void);
+ bool mci_CDResume (void);
+ bool mci_CDStop (void);
+ int mci_CDStatus (void);
+ bool mci_CDPosition (int *position);
+
+
+ /* digital audio extraction cd interface */
+ static void dx_CDAudioCallBack(Bitu len);
+
+ bool use_dxplay;
+ static struct dxPlayer {
+ CDROM_Interface_Ioctl *cd;
+ MixerChannel *channel;
+ SDL_mutex *mutex;
+ Bit8u buffer[8192];
+ int bufLen;
+ int currFrame;
+ int targetFrame;
+ bool isPlaying;
+ bool isPaused;
+ bool ctrlUsed;
+ TCtrl ctrlData;
+ } player;
+
+};
+
+#endif /* WIN 32 */
+
+#if defined (LINUX) || defined(OS2)
+
+class CDROM_Interface_Ioctl : public CDROM_Interface_SDL
+{
+public:
+ CDROM_Interface_Ioctl (void);
+
+ bool SetDevice (char* path, int forceCD);
+ bool GetUPC (unsigned char& attr, char* upc);
+ bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num);
+
+private:
+ char device_name[512];
+};
+
+#endif /* LINUX */
+
+#endif /* __CDROM_INTERFACE__ */
diff --git a/src/dos/cdrom_aspi_win32.cpp b/src/dos/cdrom_aspi_win32.cpp
new file mode 100644
index 000000000..aeed5ea38
--- /dev/null
+++ b/src/dos/cdrom_aspi_win32.cpp
@@ -0,0 +1,768 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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 (WIN32)
+
+#include <ctype.h>
+
+#include "dosbox.h"
+#include "cdrom.h"
+#include "support.h"
+
+//Are actually system includes but leave for now
+#include "wnaspi32.h"
+
+#if defined (_MSC_VER)
+#include <ntddcdrm.h> // Ioctl stuff
+#include <ntddscsi.h>
+#include <winioctl.h> // Ioctl stuff
+#elif (defined __MINGW64_VERSION_MAJOR)
+#include <winioctl.h> // Ioctl stuff
+#include <ntddcdrm.h> // Ioctl stuff
+#include <ntddscsi.h>
+#else
+#include "ddk/ntddcdrm.h" // Ioctl stuff
+#include "ddk/ntddscsi.h"
+#endif
+
+#include "scsidefs.h"
+
+// always use a buffer of the maximum struct size (like the union of all 'SRB_*' struct types)
+// Thanx SaPu
+typedef union {
+ SRB_HAInquiry hainquiry;
+ SRB_GDEVBlock gdevblock;
+ SRB_ExecSCSICmd execscsicmd;
+ SRB_Abort abort;
+ SRB_BusDeviceReset busdevicereset;
+ SRB_GetDiskInfo getdiskinfo;
+ SRB_RescanPort rescanport;
+ SRB_GetSetTimeouts getsettimeouts;
+} ASPI_SRB;
+
+// *****************************************************************
+// Windows ASPI functions (should work for all WIN with ASPI layer)
+// *****************************************************************
+
+CDROM_Interface_Aspi::CDROM_Interface_Aspi(void)
+{
+ hASPI = NULL;
+ hEvent = NULL;
+ pGetASPI32SupportInfo = NULL;
+ pSendASPI32Command = NULL;
+ memset(&oldLeadOut,0,sizeof(oldLeadOut));
+};
+
+CDROM_Interface_Aspi::~CDROM_Interface_Aspi(void)
+{
+ // Stop Audio
+ StopAudio();
+
+ pGetASPI32SupportInfo = NULL; // clear funcs
+ pSendASPI32Command = NULL;
+
+ if (hASPI) { // free aspi
+ FreeLibrary(hASPI);
+ hASPI=NULL;
+ }
+};
+
+bool GetRegistryValue(HKEY& hKey,char* valueName, char* buffer, ULONG bufferSize)
+// hKey has to be open
+{
+ // Read subkey
+ ULONG valType;
+ ULONG result;
+ result = RegQueryValueEx(hKey,valueName,NULL,&valType,(unsigned char*)&buffer[0],&bufferSize);
+ return (result == ERROR_SUCCESS);
+};
+
+BYTE CDROM_Interface_Aspi::GetHostAdapter(char* hardwareID)
+{
+ ASPI_SRB sh;
+ ASPI_SRB sd;
+ DWORD d = pGetASPI32SupportInfo();
+ int cnt = LOBYTE(LOWORD(d));
+ int i,j,k,max;
+
+ for(i=0; i<cnt; i++) {
+ memset(&sh, 0, sizeof(sh));
+ sh.hainquiry.SRB_Cmd = SC_HA_INQUIRY;
+ sh.hainquiry.SRB_HaId = i;
+ pSendASPI32Command((LPSRB)&sh);
+ if (sh.hainquiry.SRB_Status!=SS_COMP) continue;
+
+ // Indicates the maximum number of targets the adapter supports
+ // If the value is not 8 or 16, then it should be assumed max target is 8
+ max = (int)sh.hainquiry.HA_Unique[3];
+ if ((max!=8) && (max!=16)) max = 8;
+
+ for(j=0; j<max; j++) {
+ for(k=0; k<8; k++) {
+ memset(&sd, 0, sizeof(sd));
+ sd.gdevblock.SRB_Cmd = SC_GET_DEV_TYPE;
+ sd.gdevblock.SRB_HaId = i;
+ sd.gdevblock.SRB_Target = j;
+ sd.gdevblock.SRB_Lun = k;
+ pSendASPI32Command((LPSRB)&sd);
+ if (sd.gdevblock.SRB_Status == SS_COMP) {
+ if (sd.gdevblock.SRB_DeviceType == DTYPE_CDROM) {
+ if ((target==j) && (lun==k)) {
+ LOG(LOG_MISC,LOG_NORMAL)("SCSI: Getting Hardware vendor.");
+ // "Hardware ID = vendor" match ?
+ char vendor[64];
+ if (GetVendor(i,target,lun,vendor)) {
+ LOG(LOG_MISC,LOG_NORMAL)("SCSI: Vendor : %s",vendor);
+ if (strstr(strupr(hardwareID),strupr(vendor))) {
+ LOG(LOG_MISC,LOG_NORMAL)("SCSI: Host Adapter found: %d",i);
+ return i;
+ }
+ };
+ }
+ }
+ }
+ }
+ }
+ }
+ LOG(LOG_MISC,LOG_ERROR)("SCSI: Host Adapter not found: %d",i);
+ return 0;
+};
+
+bool CDROM_Interface_Aspi::ScanRegistryFindKey(HKEY& hKeyBase)
+// hKey has to be open
+{
+ FILETIME time;
+ ULONG result,newKeyResult;
+ char subKey[256];
+ char buffer[256];
+ ULONG subKeySize = 256;
+ HKEY hNewKey;
+
+ ULONG index = 0;
+ do {
+ result = RegEnumKeyEx (hKeyBase,index,&subKey[0],&subKeySize,NULL,NULL,0,&time);
+ if (result==ERROR_SUCCESS) {
+ // Open Key...
+ newKeyResult = RegOpenKeyEx (hKeyBase,subKey,0,KEY_READ,&hNewKey);
+ if (newKeyResult==ERROR_SUCCESS) {
+ static const char drive_letter_assignment[] = "CurrentDriveLetterAssignment";
+ if (GetRegistryValue(hNewKey,(char*)&drive_letter_assignment,buffer,256)) {
+ LOG(LOG_MISC,LOG_NORMAL)("SCSI: Drive Letter found: %s",buffer);
+ // aha, something suspicious...
+ if (buffer[0]==letter) {
+ char hardwareID[256];
+ // found it... lets see if we can get the scsi values
+ static const char SCSI_LUN[] = "SCSILUN";
+ bool v1 = GetRegistryValue(hNewKey,(char*)SCSI_LUN,buffer,256);
+ LOG(LOG_MISC,LOG_NORMAL)("SCSI: SCSILUN found: %s",buffer);
+ lun = buffer[0]-'0';
+ static const char SCSI_TargetID[] = "SCSITargetID";
+ bool v2 = GetRegistryValue(hNewKey,(char*)SCSI_TargetID,buffer,256);
+ LOG(LOG_MISC,LOG_NORMAL)("SCSI: SCSITargetID found: %s",buffer);
+ target = buffer[0]-'0';
+ static const char Hardware_ID[] = "HardwareID";
+ bool v3 = GetRegistryValue(hNewKey,(char*)Hardware_ID,hardwareID,256);
+ RegCloseKey(hNewKey);
+ if (v1 && v2 && v3) {
+ haId = GetHostAdapter(hardwareID);
+ return true;
+ };
+ }
+ };
+ };
+ RegCloseKey(hNewKey);
+ };
+ index++;
+ } while ((result==ERROR_SUCCESS) || (result==ERROR_MORE_DATA));
+ return false;
+};
+
+bool CDROM_Interface_Aspi::GetVendor(BYTE HA_num, BYTE SCSI_Id, BYTE SCSI_Lun, char* szBuffer)
+{
+ ASPI_SRB srbExec;
+ // SRB_ExecSCSICmd srbExec;
+ memset ( &srbExec, 0, sizeof ( SRB_ExecSCSICmd ) );
+
+ hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+
+ srbExec.execscsicmd.SRB_Cmd = SC_EXEC_SCSI_CMD ;
+ srbExec.execscsicmd.SRB_HaId = HA_num;
+ srbExec.execscsicmd.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
+ srbExec.execscsicmd.SRB_Target = SCSI_Id;
+ srbExec.execscsicmd.SRB_Lun = SCSI_Lun;
+ srbExec.execscsicmd.SRB_BufLen = 36;
+ srbExec.execscsicmd.SRB_BufPointer = (unsigned char*)szBuffer;
+ srbExec.execscsicmd.SRB_SenseLen = SENSE_LEN;
+ srbExec.execscsicmd.SRB_CDBLen = 6;
+ srbExec.execscsicmd.SRB_PostProc = (LPVOID)hEvent;
+ srbExec.execscsicmd.CDBByte [ 0 ] = SCSI_INQUIRY;
+ srbExec.execscsicmd.CDBByte [ 4 ] = 36; // allocation length per szBuffer [ ]
+
+ ResetEvent(hEvent);
+ int dwStatus = pSendASPI32Command ((LPSRB)&srbExec);
+// LOG(LOG_MISC|LOG_ERROR,"SCSI: Get vendor command send");
+
+ if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,30000);
+// LOG(LOG_MISC|LOG_ERROR,"SCSI: Pending done.");
+
+ CloseHandle(hEvent);
+ if (srbExec.execscsicmd.SRB_Status != SS_COMP) {
+ strcpy (szBuffer, "error" );
+ return false;
+ } else {
+ safe_strncpy(szBuffer,szBuffer+8,26);
+ size_t len = strlen(szBuffer);
+ for (size_t i=0; i<len; i++) if (szBuffer[i]<=32) szBuffer[i]='_';
+ }
+ return true;
+}
+
+bool CDROM_Interface_Aspi::ScanRegistry(HKEY& hKeyBase)
+// hKey has to be open
+{
+ FILETIME time;
+ ULONG result,newKeyResult;
+ char subKey[256];
+ ULONG subKeySize= 256;
+ HKEY hNewKey;
+
+ ULONG index = 0;
+ do {
+ result = RegEnumKeyEx (hKeyBase,index,&subKey[0],&subKeySize,NULL,NULL,0,&time);
+ if ((result==ERROR_SUCCESS) || (result==ERROR_MORE_DATA)) {
+ // Open Key...
+ newKeyResult = RegOpenKeyEx (hKeyBase,subKey,0,KEY_READ,&hNewKey);
+ if (newKeyResult==ERROR_SUCCESS) {
+ bool found = ScanRegistryFindKey(hNewKey);
+ RegCloseKey(hNewKey);
+ if (found) return true;
+ };
+ RegCloseKey(hNewKey);
+ };
+ index++;
+ } while ((result==ERROR_SUCCESS) || (result==ERROR_MORE_DATA));
+ return false;
+};
+
+bool CDROM_Interface_Aspi::SetDevice(char* path, int forceCD)
+{
+ // load WNASPI32.DLL
+ hASPI = LoadLibrary ( "WNASPI32.DLL" );
+ if (!hASPI) return false;
+ // Get Pointer to ASPI funcs
+ pGetASPI32SupportInfo = (DWORD(*)(void))GetProcAddress(hASPI,"GetASPI32SupportInfo");
+ pSendASPI32Command = (DWORD(*)(LPSRB))GetProcAddress(hASPI,"SendASPI32Command");
+ if (!pGetASPI32SupportInfo || !pSendASPI32Command) return false;
+ // Letter
+ letter = toupper(path[0]);
+
+ // Check OS
+ OSVERSIONINFO osi;
+ osi.dwOSVersionInfoSize = sizeof(osi);
+ GetVersionEx(&osi);
+ if ((osi.dwPlatformId==VER_PLATFORM_WIN32_NT) && (osi.dwMajorVersion>4)) {
+ if (GetDriveType(path)==DRIVE_CDROM) {
+ // WIN XP/NT/2000
+ int iDA,iDT,iDL;
+ letter = path[0];
+ HANDLE hF = OpenIOCTLFile(letter,FALSE);
+ GetIOCTLAdapter(hF,&iDA,&iDT,&iDL);
+ CloseHandle(hF);
+ // Set SCSI IDs
+ haId = iDA;
+ target = iDT;
+ lun = iDL;
+ return true;
+ }
+ } else {
+ // win 95/98/ME have to scan the registry...
+ // lets hope the layout is always the same... i dunno...
+ char key[2048];
+ HKEY hKeyBase;
+ bool found = false;
+ strcpy(key,"ENUM\\SCSI");
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,key,0,KEY_READ,&hKeyBase)==ERROR_SUCCESS) {
+ found = ScanRegistry(hKeyBase);
+ };
+ RegCloseKey(hKeyBase);
+ return found;
+ }
+ return false;
+};
+
+bool CDROM_Interface_Aspi::GetAudioTracks(int& stTrack, int& endTrack, TMSF& leadOut)
+{
+ TOC toc;
+ if (GetTOC((LPTOC)&toc) == SS_COMP) {
+ stTrack = toc.cFirstTrack;
+ endTrack = toc.cLastTrack;
+ leadOut.min = (unsigned char)(toc.tracks[endTrack].lAddr >> 8) &0xFF;
+ leadOut.sec = (unsigned char)(toc.tracks[endTrack].lAddr >> 16) &0xFF;
+ leadOut.fr = (unsigned char)(toc.tracks[endTrack].lAddr >> 24) &0xFF;
+ return true;
+ }
+ return false;
+};
+
+bool CDROM_Interface_Aspi::GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr)
+{
+ TOC toc;
+ if (GetTOC((LPTOC)&toc) == SS_COMP) {
+ start.min = (unsigned char)(toc.tracks[track-1].lAddr >> 8) &0xFF;
+ start.sec = (unsigned char)(toc.tracks[track-1].lAddr >> 16) &0xFF;
+ start.fr = (unsigned char)(toc.tracks[track-1].lAddr >> 24) &0xFF;
+ attr = (toc.tracks[track-1].cAdrCtrl << 4) & 0xEF;
+ return true;
+ };
+ return false;
+};
+
+HANDLE CDROM_Interface_Aspi::OpenIOCTLFile(char cLetter,BOOL bAsync)
+{
+ HANDLE hF;
+ char szFName[16];
+ OSVERSIONINFO ov;
+ DWORD dwFlags;
+ DWORD dwIOCTLAttr;
+// if(bAsync) dwIOCTLAttr=FILE_FLAG_OVERLAPPED;
+// else
+ dwIOCTLAttr=0;
+
+ memset(&ov,0,sizeof(OSVERSIONINFO));
+ ov.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
+ GetVersionEx(&ov);
+
+ if ((ov.dwPlatformId==VER_PLATFORM_WIN32_NT) && (ov.dwMajorVersion>4))
+ dwFlags = GENERIC_READ|GENERIC_WRITE; // add gen write on W2k/XP
+ else
+ dwFlags = GENERIC_READ;
+
+ wsprintf(szFName, "\\\\.\\%c:",cLetter);
+
+ hF=CreateFile(szFName,dwFlags,FILE_SHARE_READ, // open drive
+ NULL,OPEN_EXISTING,dwIOCTLAttr,NULL);
+
+ if (hF==INVALID_HANDLE_VALUE) {
+ dwFlags^=GENERIC_WRITE; // mmm... no success
+ hF=CreateFile(szFName,dwFlags,FILE_SHARE_READ, // -> open drive again
+ NULL,OPEN_EXISTING,dwIOCTLAttr,NULL);
+ if (hF==INVALID_HANDLE_VALUE) return NULL;
+ }
+ return hF;
+}
+
+void CDROM_Interface_Aspi::GetIOCTLAdapter(HANDLE hF,int * iDA,int * iDT,int * iDL)
+{
+ char szBuf[1024];
+ PSCSI_ADDRESS pSA;
+ DWORD dwRet;
+
+ *iDA=*iDT=*iDL=-1;
+ if(hF==NULL) return;
+
+ memset(szBuf,0,1024);
+
+ pSA=(PSCSI_ADDRESS)szBuf;
+ pSA->Length=sizeof(SCSI_ADDRESS);
+
+ if(!DeviceIoControl(hF,IOCTL_SCSI_GET_ADDRESS,NULL,
+ 0,pSA,sizeof(SCSI_ADDRESS),
+ &dwRet,NULL))
+ return;
+
+ *iDA = pSA->PortNumber;
+ *iDT = pSA->TargetId;
+ *iDL = pSA->Lun;
+}
+
+DWORD CDROM_Interface_Aspi::GetTOC(LPTOC toc)
+{
+// SRB_ExecSCSICmd s;
+ ASPI_SRB s;
+ DWORD dwStatus;
+
+ hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+
+ memset(&s,0,sizeof(s));
+
+ s.execscsicmd.SRB_Cmd = SC_EXEC_SCSI_CMD;
+ s.execscsicmd.SRB_HaId = haId;
+ s.execscsicmd.SRB_Target = target;
+ s.execscsicmd.SRB_Lun = lun;
+ s.execscsicmd.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
+ s.execscsicmd.SRB_BufLen = sizeof(*toc);
+ s.execscsicmd.SRB_BufPointer = (BYTE FAR *)toc;
+ s.execscsicmd.SRB_SenseLen = SENSE_LEN;
+ s.execscsicmd.SRB_CDBLen = 0x0A;
+ s.execscsicmd.SRB_PostProc = (LPVOID)hEvent;
+ s.execscsicmd.CDBByte[0] = SCSI_READ_TOC;
+ s.execscsicmd.CDBByte[1] = 0x02; // 0x02 for MSF
+ s.execscsicmd.CDBByte[7] = 0x03;
+ s.execscsicmd.CDBByte[8] = 0x24;
+
+ ResetEvent(hEvent);
+ dwStatus=pSendASPI32Command((LPSRB)&s);
+
+ if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,30000);
+
+ CloseHandle(hEvent);
+
+ return (s.execscsicmd.SRB_Status==SS_COMP);
+}
+
+bool CDROM_Interface_Aspi::PlayAudioSector(unsigned long start,unsigned long len)
+{
+// SRB_ExecSCSICmd s;
+ ASPI_SRB s;
+ DWORD dwStatus;
+
+ hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+
+ memset(&s,0,sizeof(s));
+ s.execscsicmd.SRB_Cmd = SC_EXEC_SCSI_CMD;
+ s.execscsicmd.SRB_HaId = haId;
+ s.execscsicmd.SRB_Target = target;
+ s.execscsicmd.SRB_Lun = lun;
+ s.execscsicmd.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
+ s.execscsicmd.SRB_BufLen = 0;
+ s.execscsicmd.SRB_BufPointer = 0;
+ s.execscsicmd.SRB_SenseLen = SENSE_LEN;
+ s.execscsicmd.SRB_CDBLen = 12;
+ s.execscsicmd.SRB_PostProc = (LPVOID)hEvent;
+
+ s.execscsicmd.CDBByte[0] = SCSI_PLAYAUD_12;
+ s.execscsicmd.CDBByte[1] = lun << 5;
+ s.execscsicmd.CDBByte[2] = (unsigned char)((start >> 24) & 0xFF);
+ s.execscsicmd.CDBByte[3] = (unsigned char)((start >> 16) & 0xFF);
+ s.execscsicmd.CDBByte[4] = (unsigned char)((start >> 8) & 0xFF);
+ s.execscsicmd.CDBByte[5] = (unsigned char)((start & 0xFF));
+ s.execscsicmd.CDBByte[6] = (unsigned char)((len >> 24) & 0xFF);
+ s.execscsicmd.CDBByte[7] = (unsigned char)((len >> 16) & 0xFF);
+ s.execscsicmd.CDBByte[8] = (unsigned char)((len >> 8) & 0xFF);
+ s.execscsicmd.CDBByte[9] = (unsigned char)(len & 0xFF);
+
+ ResetEvent(hEvent);
+
+ dwStatus = pSendASPI32Command((LPSRB)&s);
+
+ if(dwStatus==SS_PENDING) WaitForSingleObject(hEvent,10000);
+
+ CloseHandle(hEvent);
+
+ return s.execscsicmd.SRB_Status==SS_COMP;
+}
+
+bool CDROM_Interface_Aspi::StopAudio(void)
+{
+ return PauseAudio(false);
+};
+
+bool CDROM_Interface_Aspi::PauseAudio(bool resume)
+{
+ //SRB_ExecSCSICmd s;
+ ASPI_SRB s;
+ DWORD dwStatus;
+
+ hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+
+ memset(&s,0,sizeof(s));
+
+ s.execscsicmd.SRB_Cmd = SC_EXEC_SCSI_CMD;
+ s.execscsicmd.SRB_HaId = haId;
+ s.execscsicmd.SRB_Target = target;
+ s.execscsicmd.SRB_Lun = lun;
+ s.execscsicmd.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
+ s.execscsicmd.SRB_BufLen = 0x00;
+ s.execscsicmd.SRB_SenseLen = SENSE_LEN;
+ s.execscsicmd.SRB_CDBLen = 0x0A;
+ s.execscsicmd.SRB_PostProc = (LPVOID)hEvent;
+ s.execscsicmd.CDBByte[0] = 0x4B;
+ s.execscsicmd.CDBByte[8] = (unsigned char)resume; // Pause
+
+ ResetEvent(hEvent);
+ dwStatus=pSendASPI32Command((LPSRB)&s);
+
+ if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,30000);
+
+ CloseHandle(hEvent);
+
+ return (s.execscsicmd.SRB_Status==SS_COMP);
+};
+
+bool CDROM_Interface_Aspi::GetAudioSub(unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos)
+{
+ SUB_Q_CURRENT_POSITION pos;
+// SRB_ExecSCSICmd s;
+ ASPI_SRB s;
+ DWORD dwStatus;
+
+ hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+
+ memset(&s,0,sizeof(s));
+
+ s.execscsicmd.SRB_Cmd = SC_EXEC_SCSI_CMD;
+ s.execscsicmd.SRB_HaId = haId;
+ s.execscsicmd.SRB_Target = target;
+ s.execscsicmd.SRB_Lun = lun;
+ s.execscsicmd.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
+ s.execscsicmd.SRB_SenseLen = SENSE_LEN;
+
+ s.execscsicmd.SRB_BufLen = sizeof(pos);
+ s.execscsicmd.SRB_BufPointer = (BYTE FAR *)&pos;
+ s.execscsicmd.SRB_CDBLen = 10;
+ s.execscsicmd.SRB_PostProc = (LPVOID)hEvent;
+
+ s.execscsicmd.CDBByte[0] = SCSI_SUBCHANNEL;
+ s.execscsicmd.CDBByte[1] = (lun<<5)|2; // lun & msf
+ s.execscsicmd.CDBByte[2] = 0x40; // subq
+ s.execscsicmd.CDBByte[3] = 0x01; // curr pos info
+ s.execscsicmd.CDBByte[6] = 0; // track number (only in isrc mode, ignored)
+ s.execscsicmd.CDBByte[7] = 0; // alloc len
+ s.execscsicmd.CDBByte[8] = sizeof(pos);
+
+ ResetEvent(hEvent);
+
+ dwStatus = pSendASPI32Command((LPSRB)&s);
+
+ if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,0xFFFFFFFF);
+
+ CloseHandle(hEvent);
+
+ if (s.execscsicmd.SRB_Status!=SS_COMP) return false;
+
+ attr = (pos.Control<<4) &0xEF;
+ track = pos.TrackNumber;
+ index = pos.IndexNumber;
+ absPos.min = pos.AbsoluteAddress[1];
+ absPos.sec = pos.AbsoluteAddress[2];
+ absPos.fr = pos.AbsoluteAddress[3];
+ relPos.min = pos.TrackRelativeAddress[1];
+ relPos.sec = pos.TrackRelativeAddress[2];
+ relPos.fr = pos.TrackRelativeAddress[3];
+
+ return true;
+};
+
+bool CDROM_Interface_Aspi::GetUPC(unsigned char& attr, char* upcdata)
+{
+ SUB_Q_MEDIA_CATALOG_NUMBER upc;
+ ASPI_SRB s;
+ //SRB_ExecSCSICmd s;
+ DWORD dwStatus;
+
+ hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+
+ memset(&s,0,sizeof(s));
+
+ s.execscsicmd.SRB_Cmd = SC_EXEC_SCSI_CMD;
+ s.execscsicmd.SRB_HaId = haId;
+ s.execscsicmd.SRB_Target = target;
+ s.execscsicmd.SRB_Lun = lun;
+ s.execscsicmd.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
+ s.execscsicmd.SRB_SenseLen = SENSE_LEN;
+
+ s.execscsicmd.SRB_BufLen = sizeof(upc);
+ s.execscsicmd.SRB_BufPointer = (BYTE FAR *)&upc;
+ s.execscsicmd.SRB_CDBLen = 10;
+ s.execscsicmd.SRB_PostProc = (LPVOID)hEvent;
+
+ s.execscsicmd.CDBByte[0] = SCSI_SUBCHANNEL;
+ s.execscsicmd.CDBByte[1] = (lun<<5)|2; // lun & msf
+ s.execscsicmd.CDBByte[2] = 0x40; // subq
+ s.execscsicmd.CDBByte[3] = 0x02; // get upc
+ s.execscsicmd.CDBByte[6] = 0; // track number (only in isrc mode, ignored)
+ s.execscsicmd.CDBByte[7] = 0; // alloc len
+ s.execscsicmd.CDBByte[8] = sizeof(upc);
+
+ ResetEvent(hEvent);
+
+ dwStatus = pSendASPI32Command((LPSRB)&s);
+
+ if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,0xFFFFFFFF);
+
+ CloseHandle(hEvent);
+
+ if (s.execscsicmd.SRB_Status!=SS_COMP) return false;
+
+// attr = (upc.ADR<<4) | upc.Control;
+ attr = 0;
+ // Convert to mscdex format
+ for (int i=0; i<7; i++) upcdata[i] = upc.MediaCatalog[i];
+ for (int i=0; i<7; i++) upcdata[i] = (upc.MediaCatalog[i*2] << 4) | (upc.MediaCatalog[i*2+1] & 0x0F);
+
+ return true;
+};
+
+bool CDROM_Interface_Aspi::GetAudioStatus(bool& playing, bool& pause)
+{
+ playing = pause = false;
+
+ SUB_Q_HEADER sub;
+// SRB_ExecSCSICmd s;
+ ASPI_SRB s;
+ DWORD dwStatus;
+
+ hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+
+ memset(&s,0,sizeof(s));
+
+ s.execscsicmd.SRB_Cmd = SC_EXEC_SCSI_CMD;
+ s.execscsicmd.SRB_HaId = haId;
+ s.execscsicmd.SRB_Target = target;
+ s.execscsicmd.SRB_Lun = lun;
+ s.execscsicmd.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
+ s.execscsicmd.SRB_SenseLen = SENSE_LEN;
+
+ s.execscsicmd.SRB_BufLen = sizeof(sub);
+ s.execscsicmd.SRB_BufPointer = (BYTE FAR *)&sub;
+ s.execscsicmd.SRB_CDBLen = 10;
+ s.execscsicmd.SRB_PostProc = (LPVOID)hEvent;
+
+ s.execscsicmd.CDBByte[0] = SCSI_SUBCHANNEL;
+ s.execscsicmd.CDBByte[1] = (lun<<5)|2; // lun & msf
+ s.execscsicmd.CDBByte[2] = 0x00; // no subq
+ s.execscsicmd.CDBByte[3] = 0x00; // dont care
+ s.execscsicmd.CDBByte[6] = 0; // track number (only in isrc mode, ignored)
+ s.execscsicmd.CDBByte[7] = 0; // alloc len
+ s.execscsicmd.CDBByte[8] = sizeof(sub);
+
+ ResetEvent(hEvent);
+
+ dwStatus = pSendASPI32Command((LPSRB)&s);
+
+ if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,0xFFFFFFFF);
+
+ CloseHandle(hEvent);
+
+ if (s.execscsicmd.SRB_Status!=SS_COMP) return false;
+
+ playing = (sub.AudioStatus==0x11);
+ pause = (sub.AudioStatus==0x12);
+
+ return true;
+};
+
+bool CDROM_Interface_Aspi::LoadUnloadMedia(bool unload)
+{
+ //SRB_ExecSCSICmd s;
+ ASPI_SRB s;
+ DWORD dwStatus;
+
+ hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+
+ memset(&s,0,sizeof(s));
+
+ s.execscsicmd.SRB_Cmd = SC_EXEC_SCSI_CMD;
+ s.execscsicmd.SRB_HaId = haId;
+ s.execscsicmd.SRB_Target = target;
+ s.execscsicmd.SRB_Lun = lun;
+ s.execscsicmd.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
+ s.execscsicmd.SRB_SenseLen = SENSE_LEN;
+
+ s.execscsicmd.SRB_BufLen = 0;
+ s.execscsicmd.SRB_BufPointer = 0;
+ s.execscsicmd.SRB_CDBLen = 6; // 14;
+ s.execscsicmd.SRB_PostProc = (LPVOID)hEvent;
+
+ s.execscsicmd.CDBByte[0] = SCSI_LOAD_UN;
+ s.execscsicmd.CDBByte[1] = (lun<<5)|1; // lun & immediate
+ s.execscsicmd.CDBByte[4] = (unload ? 0x02:0x03); // unload/load media
+
+ ResetEvent(hEvent);
+
+ dwStatus = pSendASPI32Command((LPSRB)&s);
+
+ if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,0xFFFFFFFF);
+
+ CloseHandle(hEvent);
+
+ if (s.execscsicmd.SRB_Status!=SS_COMP) return false;
+
+ return true;
+};
+
+bool CDROM_Interface_Aspi::GetMediaTrayStatus(bool& mediaPresent, bool& mediaChanged, bool& trayOpen)
+{
+ // Seems not possible to get this values using ioctl...
+ int track1,track2;
+ TMSF leadOut;
+ // If we can read, there's a media
+ mediaPresent = GetAudioTracks(track1, track2, leadOut),
+ trayOpen = !mediaPresent;
+ mediaChanged = (oldLeadOut.min!=leadOut.min) || (oldLeadOut.sec!=leadOut.sec) || (oldLeadOut.fr!=leadOut.fr);
+ // Save old values
+ oldLeadOut.min = leadOut.min;
+ oldLeadOut.sec = leadOut.sec;
+ oldLeadOut.fr = leadOut.fr;
+ // always success
+ return true;
+};
+
+bool CDROM_Interface_Aspi::ReadSectors(PhysPt buffer, bool raw, unsigned long sector, unsigned long num)
+{
+ //SRB_ExecSCSICmd s;
+ ASPI_SRB s;
+ DWORD dwStatus;
+
+ hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+
+ memset(&s,0,sizeof(s));
+
+ Bitu buflen = raw?2352*num:2048*num;
+ Bit8u* bufdata = new Bit8u[buflen];
+
+ s.execscsicmd.SRB_Cmd = SC_EXEC_SCSI_CMD;
+ s.execscsicmd.SRB_HaId = haId;
+ s.execscsicmd.SRB_Target = target;
+ s.execscsicmd.SRB_Lun = lun;
+ s.execscsicmd.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
+ s.execscsicmd.SRB_SenseLen = SENSE_LEN;
+
+ s.execscsicmd.SRB_BufLen = buflen;
+ s.execscsicmd.SRB_BufPointer = (BYTE FAR*)bufdata;
+ s.execscsicmd.SRB_CDBLen = 12;
+ s.execscsicmd.SRB_PostProc = (LPVOID)hEvent;
+
+ s.execscsicmd.CDBByte[0] = 0xBE;
+ s.execscsicmd.CDBByte[2] = (unsigned char)((sector >> 24) & 0xFF);
+ s.execscsicmd.CDBByte[3] = (unsigned char)((sector >> 16) & 0xFF);
+ s.execscsicmd.CDBByte[4] = (unsigned char)((sector >> 8) & 0xFF);
+ s.execscsicmd.CDBByte[5] = (unsigned char)((sector & 0xFF));
+ s.execscsicmd.CDBByte[6] = (unsigned char)((num >> 16) & 0xFF);
+ s.execscsicmd.CDBByte[7] = (unsigned char)((num >> 8) & 0xFF);
+ s.execscsicmd.CDBByte[8] = (unsigned char) (num & 0xFF);
+ s.execscsicmd.CDBByte[9] = (raw?0xF0:0x10);
+
+ ResetEvent(hEvent);
+
+ dwStatus = pSendASPI32Command((LPSRB)&s);
+
+ if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,0xFFFFFFFF);
+
+ CloseHandle(hEvent);
+
+ // Copy to PhysPt
+ MEM_BlockWrite(buffer,bufdata,buflen);
+
+ delete[] bufdata;
+
+ return (s.execscsicmd.SRB_Status==SS_COMP);
+};
+
+#endif
diff --git a/src/dos/cdrom_image.cpp b/src/dos/cdrom_image.cpp
new file mode 100644
index 000000000..68d1806d1
--- /dev/null
+++ b/src/dos/cdrom_image.cpp
@@ -0,0 +1,758 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <cctype>
+#include <cmath>
+#include <cstdio>
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <limits.h> //GCC 2.95
+#include <sstream>
+#include <vector>
+#include <sys/stat.h>
+#include "cdrom.h"
+#include "drives.h"
+#include "support.h"
+#include "setup.h"
+
+#if !defined(WIN32)
+#include <libgen.h>
+#else
+#include <string.h>
+#endif
+
+using namespace std;
+
+#define MAX_LINE_LENGTH 512
+#define MAX_FILENAME_LENGTH 256
+
+CDROM_Interface_Image::BinaryFile::BinaryFile(const char *filename, bool &error)
+{
+ file = new ifstream(filename, ios::in | ios::binary);
+ error = (file == NULL) || (file->fail());
+}
+
+CDROM_Interface_Image::BinaryFile::~BinaryFile()
+{
+ delete file;
+ file = NULL;
+}
+
+bool CDROM_Interface_Image::BinaryFile::read(Bit8u *buffer, int seek, int count)
+{
+ file->seekg(seek, ios::beg);
+ file->read((char*)buffer, count);
+ return !(file->fail());
+}
+
+int CDROM_Interface_Image::BinaryFile::getLength()
+{
+ file->seekg(0, ios::end);
+ int length = (int)file->tellg();
+ if (file->fail()) return -1;
+ return length;
+}
+
+#if defined(C_SDL_SOUND)
+CDROM_Interface_Image::AudioFile::AudioFile(const char *filename, bool &error)
+{
+ Sound_AudioInfo desired = {AUDIO_S16, 2, 44100};
+ sample = Sound_NewSampleFromFile(filename, &desired, RAW_SECTOR_SIZE);
+ lastCount = RAW_SECTOR_SIZE;
+ lastSeek = 0;
+ error = (sample == NULL);
+}
+
+CDROM_Interface_Image::AudioFile::~AudioFile()
+{
+ Sound_FreeSample(sample);
+}
+
+bool CDROM_Interface_Image::AudioFile::read(Bit8u *buffer, int seek, int count)
+{
+ if (lastCount != count) {
+ int success = Sound_SetBufferSize(sample, count);
+ if (!success) return false;
+ }
+ if (lastSeek != (seek - count)) {
+ int success = Sound_Seek(sample, (int)((double)(seek) / 176.4f));
+ if (!success) return false;
+ }
+ lastSeek = seek;
+ int bytes = Sound_Decode(sample);
+ if (bytes < count) {
+ memcpy(buffer, sample->buffer, bytes);
+ memset(buffer + bytes, 0, count - bytes);
+ } else {
+ memcpy(buffer, sample->buffer, count);
+ }
+
+ return !(sample->flags & SOUND_SAMPLEFLAG_ERROR);
+}
+
+int CDROM_Interface_Image::AudioFile::getLength()
+{
+ int time = 1;
+ int shift = 0;
+ if (!(sample->flags & SOUND_SAMPLEFLAG_CANSEEK)) return -1;
+
+ while (true) {
+ int success = Sound_Seek(sample, (unsigned int)(shift + time));
+ if (!success) {
+ if (time == 1) return lround((double)shift * 176.4f);
+ shift += time >> 1;
+ time = 1;
+ } else {
+ if (time > ((numeric_limits<int>::max() - shift) / 2)) return -1;
+ time = time << 1;
+ }
+ }
+}
+#endif
+
+// initialize static members
+int CDROM_Interface_Image::refCount = 0;
+CDROM_Interface_Image* CDROM_Interface_Image::images[26] = {};
+CDROM_Interface_Image::imagePlayer CDROM_Interface_Image::player = {
+ NULL, NULL, NULL, {0}, 0, 0, 0, false, false, false, {0} };
+
+
+CDROM_Interface_Image::CDROM_Interface_Image(Bit8u subUnit)
+ :subUnit(subUnit)
+{
+ images[subUnit] = this;
+ if (refCount == 0) {
+ player.mutex = SDL_CreateMutex();
+ if (!player.channel) {
+ player.channel = MIXER_AddChannel(&CDAudioCallBack, 44100, "CDAUDIO");
+ }
+ player.channel->Enable(true);
+ }
+ refCount++;
+}
+
+CDROM_Interface_Image::~CDROM_Interface_Image()
+{
+ refCount--;
+ if (player.cd == this) player.cd = NULL;
+ ClearTracks();
+ if (refCount == 0) {
+ SDL_DestroyMutex(player.mutex);
+ player.channel->Enable(false);
+ }
+}
+
+void CDROM_Interface_Image::InitNewMedia()
+{
+}
+
+bool CDROM_Interface_Image::SetDevice(char* path, int forceCD)
+{
+ if (LoadCueSheet(path)) return true;
+ if (LoadIsoFile(path)) return true;
+
+ // print error message on dosbox console
+ char buf[MAX_LINE_LENGTH];
+ snprintf(buf, MAX_LINE_LENGTH, "Could not load image file: %s\n", path);
+ Bit16u size = (Bit16u)strlen(buf);
+ DOS_WriteFile(STDOUT, (Bit8u*)buf, &size);
+ return false;
+}
+
+bool CDROM_Interface_Image::GetUPC(unsigned char& attr, char* upc)
+{
+ attr = 0;
+ strcpy(upc, this->mcn.c_str());
+ return true;
+}
+
+bool CDROM_Interface_Image::GetAudioTracks(int& stTrack, int& end, TMSF& leadOut)
+{
+ stTrack = 1;
+ end = (int)(tracks.size() - 1);
+ FRAMES_TO_MSF(tracks[tracks.size() - 1].start + 150, &leadOut.min, &leadOut.sec, &leadOut.fr);
+ return true;
+}
+
+bool CDROM_Interface_Image::GetAudioTrackInfo(int track, TMSF& start, unsigned char& attr)
+{
+ if (track < 1 || track > (int)tracks.size()) return false;
+ FRAMES_TO_MSF(tracks[track - 1].start + 150, &start.min, &start.sec, &start.fr);
+ attr = tracks[track - 1].attr;
+ return true;
+}
+
+bool CDROM_Interface_Image::GetAudioSub(unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos)
+{
+ int cur_track = GetTrack(player.currFrame);
+ if (cur_track < 1) return false;
+ track = (unsigned char)cur_track;
+ attr = tracks[track - 1].attr;
+ index = 1;
+ FRAMES_TO_MSF(player.currFrame + 150, &absPos.min, &absPos.sec, &absPos.fr);
+ FRAMES_TO_MSF(player.currFrame - tracks[track - 1].start, &relPos.min, &relPos.sec, &relPos.fr);
+ return true;
+}
+
+bool CDROM_Interface_Image::GetAudioStatus(bool& playing, bool& pause)
+{
+ playing = player.isPlaying;
+ pause = player.isPaused;
+ return true;
+}
+
+bool CDROM_Interface_Image::GetMediaTrayStatus(bool& mediaPresent, bool& mediaChanged, bool& trayOpen)
+{
+ mediaPresent = true;
+ mediaChanged = false;
+ trayOpen = false;
+ return true;
+}
+
+bool CDROM_Interface_Image::PlayAudioSector(unsigned long start,unsigned long len)
+{
+ // We might want to do some more checks. E.g valid start and length
+ SDL_mutexP(player.mutex);
+ player.cd = this;
+ player.currFrame = start;
+ player.targetFrame = start + len;
+ int track = GetTrack(start) - 1;
+ if(track >= 0 && tracks[track].attr == 0x40) {
+ LOG(LOG_MISC,LOG_WARN)("Game tries to play the data track. Not doing this");
+ player.isPlaying = false;
+ //Unclear wether return false should be here.
+ //specs say that this function returns at once and games should check the status wether the audio is actually playing
+ //Real drives either fail or succeed as well
+ } else player.isPlaying = true;
+ player.isPaused = false;
+ SDL_mutexV(player.mutex);
+ return true;
+}
+
+bool CDROM_Interface_Image::PauseAudio(bool resume)
+{
+ player.isPaused = !resume;
+ return true;
+}
+
+bool CDROM_Interface_Image::StopAudio(void)
+{
+ player.isPlaying = false;
+ player.isPaused = false;
+ return true;
+}
+
+void CDROM_Interface_Image::ChannelControl(TCtrl ctrl)
+{
+ player.ctrlUsed = (ctrl.out[0]!=0 || ctrl.out[1]!=1 || ctrl.vol[0]<0xfe || ctrl.vol[1]<0xfe);
+ player.ctrlData = ctrl;
+}
+
+bool CDROM_Interface_Image::ReadSectors(PhysPt buffer, bool raw, unsigned long sector, unsigned long num)
+{
+ int sectorSize = raw ? RAW_SECTOR_SIZE : COOKED_SECTOR_SIZE;
+ Bitu buflen = num * sectorSize;
+ Bit8u* buf = new Bit8u[buflen];
+
+ bool success = true; //Gobliiins reads 0 sectors
+ for(unsigned long i = 0; i < num; i++) {
+ success = ReadSector(&buf[i * sectorSize], raw, sector + i);
+ if (!success) break;
+ }
+
+ MEM_BlockWrite(buffer, buf, buflen);
+ delete[] buf;
+
+ return success;
+}
+
+bool CDROM_Interface_Image::LoadUnloadMedia(bool unload)
+{
+ return true;
+}
+
+int CDROM_Interface_Image::GetTrack(int sector)
+{
+ vector<Track>::iterator i = tracks.begin();
+ vector<Track>::iterator end = tracks.end() - 1;
+
+ while(i != end) {
+ Track &curr = *i;
+ Track &next = *(i + 1);
+ if (curr.start <= sector && sector < next.start) return curr.number;
+ i++;
+ }
+ return -1;
+}
+
+bool CDROM_Interface_Image::ReadSector(Bit8u *buffer, bool raw, unsigned long sector)
+{
+ int track = GetTrack(sector) - 1;
+ if (track < 0) return false;
+
+ int seek = tracks[track].skip + (sector - tracks[track].start) * tracks[track].sectorSize;
+ int length = (raw ? RAW_SECTOR_SIZE : COOKED_SECTOR_SIZE);
+ if (tracks[track].sectorSize != RAW_SECTOR_SIZE && raw) return false;
+ if (tracks[track].sectorSize == RAW_SECTOR_SIZE && !tracks[track].mode2 && !raw) seek += 16;
+ if (tracks[track].mode2 && !raw) seek += 24;
+
+ return tracks[track].file->read(buffer, seek, length);
+}
+
+void CDROM_Interface_Image::CDAudioCallBack(Bitu len)
+{
+ len *= 4; // 16 bit, stereo
+ if (!len) return;
+ if (!player.isPlaying || player.isPaused) {
+ player.channel->AddSilence();
+ return;
+ }
+
+ SDL_mutexP(player.mutex);
+ while (player.bufLen < (Bits)len) {
+ bool success;
+ if (player.targetFrame > player.currFrame)
+ success = player.cd->ReadSector(&player.buffer[player.bufLen], true, player.currFrame);
+ else success = false;
+
+ if (success) {
+ player.currFrame++;
+ player.bufLen += RAW_SECTOR_SIZE;
+ } else {
+ memset(&player.buffer[player.bufLen], 0, len - player.bufLen);
+ player.bufLen = len;
+ player.isPlaying = false;
+ }
+ }
+ SDL_mutexV(player.mutex);
+ if (player.ctrlUsed) {
+ Bit16s sample0,sample1;
+ Bit16s * samples=(Bit16s *)&player.buffer;
+ for (Bitu pos=0;pos<len/4;pos++) {
+#if defined(WORDS_BIGENDIAN)
+ sample0=(Bit16s)host_readw((HostPt)&samples[pos*2+player.ctrlData.out[0]]);
+ sample1=(Bit16s)host_readw((HostPt)&samples[pos*2+player.ctrlData.out[1]]);
+#else
+ sample0=samples[pos*2+player.ctrlData.out[0]];
+ sample1=samples[pos*2+player.ctrlData.out[1]];
+#endif
+ samples[pos*2+0]=(Bit16s)(sample0*player.ctrlData.vol[0]/255.0);
+ samples[pos*2+1]=(Bit16s)(sample1*player.ctrlData.vol[1]/255.0);
+ }
+#if defined(WORDS_BIGENDIAN)
+ player.channel->AddSamples_s16(len/4,(Bit16s *)player.buffer);
+ } else player.channel->AddSamples_s16_nonnative(len/4,(Bit16s *)player.buffer);
+#else
+ }
+ player.channel->AddSamples_s16(len/4,(Bit16s *)player.buffer);
+#endif
+ memmove(player.buffer, &player.buffer[len], player.bufLen - len);
+ player.bufLen -= len;
+}
+
+bool CDROM_Interface_Image::LoadIsoFile(char* filename)
+{
+ tracks.clear();
+
+ // data track
+ Track track = {0, 0, 0, 0, 0, 0, false, NULL};
+ bool error;
+ track.file = new BinaryFile(filename, error);
+ if (error) {
+ delete track.file;
+ track.file = NULL;
+ return false;
+ }
+ track.number = 1;
+ track.attr = 0x40;//data
+
+ // try to detect iso type
+ if (CanReadPVD(track.file, COOKED_SECTOR_SIZE, false)) {
+ track.sectorSize = COOKED_SECTOR_SIZE;
+ track.mode2 = false;
+ } else if (CanReadPVD(track.file, RAW_SECTOR_SIZE, false)) {
+ track.sectorSize = RAW_SECTOR_SIZE;
+ track.mode2 = false;
+ } else if (CanReadPVD(track.file, 2336, true)) {
+ track.sectorSize = 2336;
+ track.mode2 = true;
+ } else if (CanReadPVD(track.file, RAW_SECTOR_SIZE, true)) {
+ track.sectorSize = RAW_SECTOR_SIZE;
+ track.mode2 = true;
+ } else return false;
+
+ track.length = track.file->getLength() / track.sectorSize;
+ tracks.push_back(track);
+
+ // leadout track
+ track.number = 2;
+ track.attr = 0;
+ track.start = track.length;
+ track.length = 0;
+ track.file = NULL;
+ tracks.push_back(track);
+
+ return true;
+}
+
+bool CDROM_Interface_Image::CanReadPVD(TrackFile *file, int sectorSize, bool mode2)
+{
+ Bit8u pvd[COOKED_SECTOR_SIZE];
+ int seek = 16 * sectorSize; // first vd is located at sector 16
+ if (sectorSize == RAW_SECTOR_SIZE && !mode2) seek += 16;
+ if (mode2) seek += 24;
+ file->read(pvd, seek, COOKED_SECTOR_SIZE);
+ // pvd[0] = descriptor type, pvd[1..5] = standard identifier, pvd[6] = iso version (+8 for High Sierra)
+ return ((pvd[0] == 1 && !strncmp((char*)(&pvd[1]), "CD001", 5) && pvd[6] == 1) ||
+ (pvd[8] == 1 && !strncmp((char*)(&pvd[9]), "CDROM", 5) && pvd[14] == 1));
+}
+
+#if defined(WIN32)
+static string dirname(char * file) {
+ char * sep = strrchr(file, '\\');
+ if (sep == NULL)
+ sep = strrchr(file, '/');
+ if (sep == NULL)
+ return "";
+ else {
+ int len = (int)(sep - file);
+ char tmp[MAX_FILENAME_LENGTH];
+ safe_strncpy(tmp, file, len+1);
+ return tmp;
+ }
+}
+#endif
+
+bool CDROM_Interface_Image::LoadCueSheet(char *cuefile)
+{
+ Track track = {0, 0, 0, 0, 0, 0, false, NULL};
+ tracks.clear();
+ int shift = 0;
+ int currPregap = 0;
+ int totalPregap = 0;
+ int prestart = 0;
+ bool success;
+ bool canAddTrack = false;
+ char tmp[MAX_FILENAME_LENGTH]; // dirname can change its argument
+ safe_strncpy(tmp, cuefile, MAX_FILENAME_LENGTH);
+ string pathname(dirname(tmp));
+ ifstream in;
+ in.open(cuefile, ios::in);
+ if (in.fail()) return false;
+
+ while(!in.eof()) {
+ // get next line
+ char buf[MAX_LINE_LENGTH];
+ in.getline(buf, MAX_LINE_LENGTH);
+ if (in.fail() && !in.eof()) return false; // probably a binary file
+ istringstream line(buf);
+
+ string command;
+ GetCueKeyword(command, line);
+
+ if (command == "TRACK") {
+ if (canAddTrack) success = AddTrack(track, shift, prestart, totalPregap, currPregap);
+ else success = true;
+
+ track.start = 0;
+ track.skip = 0;
+ currPregap = 0;
+ prestart = 0;
+
+ line >> track.number;
+ string type;
+ GetCueKeyword(type, line);
+
+ if (type == "AUDIO") {
+ track.sectorSize = RAW_SECTOR_SIZE;
+ track.attr = 0;
+ track.mode2 = false;
+ } else if (type == "MODE1/2048") {
+ track.sectorSize = COOKED_SECTOR_SIZE;
+ track.attr = 0x40;
+ track.mode2 = false;
+ } else if (type == "MODE1/2352") {
+ track.sectorSize = RAW_SECTOR_SIZE;
+ track.attr = 0x40;
+ track.mode2 = false;
+ } else if (type == "MODE2/2336") {
+ track.sectorSize = 2336;
+ track.attr = 0x40;
+ track.mode2 = true;
+ } else if (type == "MODE2/2352") {
+ track.sectorSize = RAW_SECTOR_SIZE;
+ track.attr = 0x40;
+ track.mode2 = true;
+ } else success = false;
+
+ canAddTrack = true;
+ }
+ else if (command == "INDEX") {
+ int index;
+ line >> index;
+ int frame;
+ success = GetCueFrame(frame, line);
+
+ if (index == 1) track.start = frame;
+ else if (index == 0) prestart = frame;
+ // ignore other indices
+ }
+ else if (command == "FILE") {
+ if (canAddTrack) success = AddTrack(track, shift, prestart, totalPregap, currPregap);
+ else success = true;
+ canAddTrack = false;
+
+ string filename;
+ GetCueString(filename, line);
+ GetRealFileName(filename, pathname);
+ string type;
+ GetCueKeyword(type, line);
+
+ track.file = NULL;
+ bool error = true;
+ if (type == "BINARY") {
+ track.file = new BinaryFile(filename.c_str(), error);
+ }
+#if defined(C_SDL_SOUND)
+ //The next if has been surpassed by the else, but leaving it in as not
+ //to break existing cue sheets that depend on this.(mine with OGG tracks specifying MP3 as type)
+ else if (type == "WAVE" || type == "AIFF" || type == "MP3") {
+ track.file = new AudioFile(filename.c_str(), error);
+ } else {
+ const Sound_DecoderInfo **i;
+ for (i = Sound_AvailableDecoders(); *i != NULL; i++) {
+ if (*(*i)->extensions == type) {
+ track.file = new AudioFile(filename.c_str(), error);
+ break;
+ }
+ }
+ }
+#endif
+ if (error) {
+ delete track.file;
+ track.file = NULL;
+ success = false;
+ }
+ }
+ else if (command == "PREGAP") success = GetCueFrame(currPregap, line);
+ else if (command == "CATALOG") success = GetCueString(mcn, line);
+ // ignored commands
+ else if (command == "CDTEXTFILE" || command == "FLAGS" || command == "ISRC"
+ || command == "PERFORMER" || command == "POSTGAP" || command == "REM"
+ || command == "SONGWRITER" || command == "TITLE" || command == "") success = true;
+ // failure
+ else success = false;
+
+ if (!success) return false;
+ }
+ // add last track
+ if (!AddTrack(track, shift, prestart, totalPregap, currPregap)) return false;
+
+ // add leadout track
+ track.number++;
+ track.attr = 0;//sync with load iso
+ track.start = 0;
+ track.length = 0;
+ track.file = NULL;
+ if(!AddTrack(track, shift, 0, totalPregap, 0)) return false;
+
+ return true;
+}
+
+bool CDROM_Interface_Image::AddTrack(Track &curr, int &shift, int prestart, int &totalPregap, int currPregap)
+{
+ // frames between index 0(prestart) and 1(curr.start) must be skipped
+ int skip;
+ if (prestart > 0) {
+ if (prestart > curr.start) return false;
+ skip = curr.start - prestart;
+ } else skip = 0;
+
+ // first track (track number must be 1)
+ if (tracks.empty()) {
+ if (curr.number != 1) return false;
+ curr.skip = skip * curr.sectorSize;
+ curr.start += currPregap;
+ totalPregap = currPregap;
+ tracks.push_back(curr);
+ return true;
+ }
+
+ Track &prev = *(tracks.end() - 1);
+
+ // current track consumes data from the same file as the previous
+ if (prev.file == curr.file) {
+ curr.start += shift;
+ prev.length = curr.start + totalPregap - prev.start - skip;
+ curr.skip += prev.skip + prev.length * prev.sectorSize + skip * curr.sectorSize;
+ totalPregap += currPregap;
+ curr.start += totalPregap;
+ // current track uses a different file as the previous track
+ } else {
+ int tmp = prev.file->getLength() - prev.skip;
+ prev.length = tmp / prev.sectorSize;
+ if (tmp % prev.sectorSize != 0) prev.length++; // padding
+
+ curr.start += prev.start + prev.length + currPregap;
+ curr.skip = skip * curr.sectorSize;
+ shift += prev.start + prev.length;
+ totalPregap = currPregap;
+ }
+
+ // error checks
+ if (curr.number <= 1) return false;
+ if (prev.number + 1 != curr.number) return false;
+ if (curr.start < prev.start + prev.length) return false;
+ if (curr.length < 0) return false;
+
+ tracks.push_back(curr);
+ return true;
+}
+
+bool CDROM_Interface_Image::HasDataTrack(void)
+{
+ //Data track has attribute 0x40
+ for(track_it it = tracks.begin(); it != tracks.end(); it++) {
+ if ((*it).attr == 0x40) return true;
+ }
+ return false;
+}
+
+
+bool CDROM_Interface_Image::GetRealFileName(string &filename, string &pathname)
+{
+ // check if file exists
+ struct stat test;
+ if (stat(filename.c_str(), &test) == 0) return true;
+
+ // check if file with path relative to cue file exists
+ string tmpstr(pathname + "/" + filename);
+ if (stat(tmpstr.c_str(), &test) == 0) {
+ filename = tmpstr;
+ return true;
+ }
+ // finally check if file is in a dosbox local drive
+ char fullname[CROSS_LEN];
+ char tmp[CROSS_LEN];
+ safe_strncpy(tmp, filename.c_str(), CROSS_LEN);
+ Bit8u drive;
+ if (!DOS_MakeName(tmp, fullname, &drive)) return false;
+
+ localDrive *ldp = dynamic_cast<localDrive*>(Drives[drive]);
+ if (ldp) {
+ ldp->GetSystemFilename(tmp, fullname);
+ if (stat(tmp, &test) == 0) {
+ filename = tmp;
+ return true;
+ }
+ }
+#if defined (WIN32) || defined(OS2)
+ //Nothing
+#else
+ //Consider the possibility that the filename has a windows directory seperator (inside the CUE file)
+ //which is common for some commercial rereleases of DOS games using DOSBox
+
+ string copy = filename;
+ size_t l = copy.size();
+ for (size_t i = 0; i < l;i++) {
+ if(copy[i] == '\\') copy[i] = '/';
+ }
+
+ if (stat(copy.c_str(), &test) == 0) {
+ filename = copy;
+ return true;
+ }
+
+ tmpstr = pathname + "/" + copy;
+ if (stat(tmpstr.c_str(), &test) == 0) {
+ filename = tmpstr;
+ return true;
+ }
+
+#endif
+ return false;
+}
+
+bool CDROM_Interface_Image::GetCueKeyword(string &keyword, istream &in)
+{
+ in >> keyword;
+ for(Bitu i = 0; i < keyword.size(); i++) keyword[i] = toupper(keyword[i]);
+
+ return true;
+}
+
+bool CDROM_Interface_Image::GetCueFrame(int &frames, istream &in)
+{
+ string msf;
+ in >> msf;
+ int min, sec, fr;
+ bool success = sscanf(msf.c_str(), "%d:%d:%d", &min, &sec, &fr) == 3;
+ frames = MSF_TO_FRAMES(min, sec, fr);
+
+ return success;
+}
+
+bool CDROM_Interface_Image::GetCueString(string &str, istream &in)
+{
+ int pos = (int)in.tellg();
+ in >> str;
+ if (str[0] == '\"') {
+ if (str[str.size() - 1] == '\"') {
+ str.assign(str, 1, str.size() - 2);
+ } else {
+ in.seekg(pos, ios::beg);
+ char buffer[MAX_FILENAME_LENGTH];
+ in.getline(buffer, MAX_FILENAME_LENGTH, '\"'); // skip
+ in.getline(buffer, MAX_FILENAME_LENGTH, '\"');
+ str = buffer;
+ }
+ }
+ return true;
+}
+
+void CDROM_Interface_Image::ClearTracks()
+{
+ vector<Track>::iterator i = tracks.begin();
+ vector<Track>::iterator end = tracks.end();
+
+ TrackFile* last = NULL;
+ while(i != end) {
+ Track &curr = *i;
+ if (curr.file != last) {
+ delete curr.file;
+ last = curr.file;
+ }
+ i++;
+ }
+ tracks.clear();
+}
+
+void CDROM_Image_Destroy(Section*) {
+#if defined(C_SDL_SOUND)
+ Sound_Quit();
+#endif
+}
+
+void CDROM_Image_Init(Section* section) {
+#if defined(C_SDL_SOUND)
+ Sound_Init();
+ section->AddDestroyFunction(CDROM_Image_Destroy, false);
+#endif
+}
diff --git a/src/dos/cdrom_ioctl_linux.cpp b/src/dos/cdrom_ioctl_linux.cpp
new file mode 100644
index 000000000..846db8b7d
--- /dev/null
+++ b/src/dos/cdrom_ioctl_linux.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include "cdrom.h"
+#include "support.h"
+
+#if defined (LINUX)
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/cdrom.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+CDROM_Interface_Ioctl::CDROM_Interface_Ioctl(void) : CDROM_Interface_SDL()
+{
+ strcpy(device_name, "");
+}
+
+bool CDROM_Interface_Ioctl::GetUPC(unsigned char& attr, char* upc)
+{
+ int cdrom_fd = open(device_name, O_RDONLY | O_NONBLOCK);
+ if (cdrom_fd == -1) return false;
+
+ struct cdrom_mcn cdrom_mcn;
+ int ret = ioctl(cdrom_fd, CDROM_GET_MCN, &cdrom_mcn);
+
+ close(cdrom_fd);
+
+ if (ret > 0) {
+ attr = 0;
+ safe_strncpy(upc, (char*)cdrom_mcn.medium_catalog_number, 14);
+ }
+
+ return (ret > 0);
+}
+
+bool CDROM_Interface_Ioctl::ReadSectors(PhysPt buffer, bool raw, unsigned long sector, unsigned long num)
+{
+ int cdrom_fd = open(device_name, O_RDONLY | O_NONBLOCK);
+ if (cdrom_fd == -1) return false;
+
+ Bits buflen = raw ? num * CD_FRAMESIZE_RAW : num * CD_FRAMESIZE;
+ Bit8u* buf = new Bit8u[buflen];
+ int ret;
+
+ if (raw) {
+ struct cdrom_read cdrom_read;
+ cdrom_read.cdread_lba = sector;
+ cdrom_read.cdread_bufaddr = (char*)buf;
+ cdrom_read.cdread_buflen = buflen;
+
+ ret = ioctl(cdrom_fd, CDROMREADRAW, &cdrom_read);
+ } else {
+ ret = lseek(cdrom_fd, sector * CD_FRAMESIZE, SEEK_SET);
+ if (ret >= 0) ret = read(cdrom_fd, buf, buflen);
+ if (ret != buflen) ret = -1;
+ }
+ close(cdrom_fd);
+
+ MEM_BlockWrite(buffer, buf, buflen);
+ delete[] buf;
+
+ return (ret > 0);
+}
+
+bool CDROM_Interface_Ioctl::SetDevice(char* path, int forceCD)
+{
+ bool success = CDROM_Interface_SDL::SetDevice(path, forceCD);
+
+ if (success) {
+ const char* tmp = SDL_CDName(forceCD);
+ if (tmp) safe_strncpy(device_name, tmp, 512);
+ else success = false;
+ }
+
+ return success;
+}
+
+#endif
diff --git a/src/dos/cdrom_ioctl_os2.cpp b/src/dos/cdrom_ioctl_os2.cpp
new file mode 100644
index 000000000..926a85a35
--- /dev/null
+++ b/src/dos/cdrom_ioctl_os2.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include "dosbox.h"
+#include "cdrom.h"
+
+#if defined (OS2)
+#define INCL_DOSFILEMGR
+#define INCL_DOSERRORS
+#define INCL_DOSDEVICES
+#define INCL_DOSDEVIOCTL
+#include "os2.h"
+
+// Ripped from linux/cdrom.h
+#define CD_FRAMESIZE_RAW 2352
+#define CD_FRAMESIZE 2048
+
+CDROM_Interface_Ioctl::CDROM_Interface_Ioctl(void) : CDROM_Interface_SDL(){
+ strcpy(device_name, "");
+}
+
+bool CDROM_Interface_Ioctl::GetUPC(unsigned char& attr, char* upc){
+ HFILE cdrom_fd = 0;
+ ULONG ulAction = 0;
+ APIRET rc = DosOpen((unsigned char*)device_name, &cdrom_fd, &ulAction, 0L, FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS,
+ OPEN_FLAGS_DASD | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY, 0L);
+ if (rc != NO_ERROR) {
+ return false;
+ }
+ char data[50];
+ ULONG len = sizeof(data);
+ char sig[] = {'C', 'D', '0', '1'};
+ ULONG sigsize = 4;
+ rc = DosDevIOCtl(cdrom_fd, IOCTL_CDROMDISK, CDROMDISK_GETUPC, sig, sigsize, &sigsize,
+ data, len, &len);
+ if (rc != NO_ERROR) {
+ return false;
+ }
+ rc = DosClose(cdrom_fd);
+ return rc == NO_ERROR;
+}
+
+bool CDROM_Interface_Ioctl::ReadSectors(PhysPt buffer, bool raw, unsigned long sector, unsigned long num){
+ HFILE cdrom_fd = 0;
+ ULONG ulAction = 0;
+ APIRET rc = DosOpen((unsigned char*)device_name, &cdrom_fd, &ulAction, 0L, FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS,
+ OPEN_FLAGS_DASD | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY, 0L);
+ if (rc != NO_ERROR) {
+ return false;
+ }
+
+ Bitu buflen = raw ? num * CD_FRAMESIZE_RAW : num * CD_FRAMESIZE;
+ Bit8u* buf = new Bit8u[buflen];
+ int ret = NO_ERROR;
+
+ if (raw) {
+ struct paramseek {
+ UCHAR sig[4];
+ UCHAR mode;
+ ULONG sec;
+
+ paramseek(ULONG sector)
+ {
+ sig[0] = 'C'; sig[1] = 'D'; sig[2] = '0'; sig[3] = '1';
+ sec = sector;
+ }
+ } param_seek(sector);
+ ULONG paramsize = sizeof (paramseek);
+ rc = DosDevIOCtl(cdrom_fd, IOCTL_CDROMDISK, CDROMDISK_SEEK, &param_seek, paramsize, &paramsize,
+ 0, 0, 0);
+ if (rc != NO_ERROR) {
+ return false;
+ }
+
+ struct paramread {
+ UCHAR sig[4];
+ UCHAR mode;
+ USHORT number;
+ BYTE sec;
+ BYTE reserved;
+ BYTE interleave;
+
+ paramread(USHORT num)
+ {
+ sig[0] = 'C'; sig[1] = 'D'; sig[2] = '0'; sig[3] = '1';
+ mode = 0; number = num;
+ sec = interleave = 0;
+ }
+ } param_read(num);
+ paramsize = sizeof (paramread);
+ ULONG len = buflen;
+ rc = DosDevIOCtl(cdrom_fd, IOCTL_CDROMDISK, CDROMDISK_READLONG, &param_read, paramsize, &paramsize,
+ buf, len, &len);
+ if (rc != NO_ERROR) {
+ return false;
+ }
+ } else {
+ ULONG pos = 0;
+ rc = DosSetFilePtr(cdrom_fd, sector * CD_FRAMESIZE, FILE_BEGIN, &pos);
+ if (rc != NO_ERROR) {
+ return false;
+ }
+ ULONG read = 0;
+ rc = DosRead(cdrom_fd, buf, buflen, &read);
+ if (rc != NO_ERROR || read != buflen) {
+ return false;
+ }
+ }
+ rc = DosClose(cdrom_fd);
+ MEM_BlockWrite(buffer, buf, buflen);
+ delete[] buf;
+
+ return (ret == NO_ERROR);
+}
+
+bool CDROM_Interface_Ioctl::SetDevice(char* path, int forceCD) {
+ bool success = CDROM_Interface_SDL::SetDevice(path, forceCD);
+
+ if (success) {
+ char temp[3] = {0, 0, 0};
+ if (path[1] == ':') {
+ temp[0] = path[0];
+ temp[1] = path[1];
+ temp[2] = 0;
+ }
+ strncpy(device_name, temp, 512);
+ } else {
+ strcpy(device_name, "");
+ success = false;
+ }
+
+ return success;
+}
+
+#endif
diff --git a/src/dos/cdrom_ioctl_win32.cpp b/src/dos/cdrom_ioctl_win32.cpp
new file mode 100644
index 000000000..74b568b12
--- /dev/null
+++ b/src/dos/cdrom_ioctl_win32.cpp
@@ -0,0 +1,623 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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 (WIN32)
+
+// *****************************************************************
+// Windows IOCTL functions (not suitable for 95/98/Me)
+// *****************************************************************
+
+#include <windows.h>
+#include <io.h>
+
+#if (defined (_MSC_VER)) || (defined __MINGW64_VERSION_MAJOR)
+#include <winioctl.h> // Ioctl stuff
+#include <ntddcdrm.h> // Ioctl stuff
+#else
+#include "ddk/ntddcdrm.h" // Ioctl stuff
+#endif
+
+#include <mmsystem.h>
+
+#include "cdrom.h"
+
+// for a more sophisticated implementation of the mci cdda functionality
+// see the SDL sources, which the mci_ functions are based on
+
+/* General ioctl() CD-ROM command function */
+bool CDROM_Interface_Ioctl::mci_CDioctl(UINT msg, DWORD flags, void *arg) {
+ MCIERROR mci_error = mciSendCommand(mci_devid, msg, flags, (DWORD_PTR)arg);
+ if (mci_error!=MMSYSERR_NOERROR) {
+ char error[256];
+ mciGetErrorString(mci_error, error, 256);
+ LOG_MSG("mciSendCommand() error: %s", error);
+ return true;
+ }
+ return false;
+}
+
+bool CDROM_Interface_Ioctl::mci_CDOpen(char drive) {
+ MCI_OPEN_PARMS mci_open;
+ MCI_SET_PARMS mci_set;
+ char device[3];
+ DWORD flags;
+
+ /* Open the requested device */
+ mci_open.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO;
+ device[0] = drive;
+ device[1] = ':';
+ device[2] = '\0';
+ mci_open.lpstrElementName = device;
+ flags = (MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID|MCI_OPEN_SHAREABLE|MCI_OPEN_ELEMENT);
+ if (mci_CDioctl(MCI_OPEN, flags, &mci_open)) {
+ flags &= ~MCI_OPEN_SHAREABLE;
+ if (mci_CDioctl(MCI_OPEN, flags, &mci_open)) {
+ return true;
+ }
+ }
+ mci_devid = mci_open.wDeviceID;
+
+ /* Set the minute-second-frame time format */
+ mci_set.dwTimeFormat = MCI_FORMAT_MSF;
+ mci_CDioctl(MCI_SET, MCI_SET_TIME_FORMAT, &mci_set);
+
+ return false;
+}
+
+bool CDROM_Interface_Ioctl::mci_CDClose(void) {
+ return mci_CDioctl(MCI_CLOSE, MCI_WAIT, NULL);
+}
+
+bool CDROM_Interface_Ioctl::mci_CDPlay(int start, int length) {
+ DWORD flags = MCI_FROM | MCI_TO | MCI_NOTIFY;
+ MCI_PLAY_PARMS mci_play;
+ mci_play.dwCallback = 0;
+
+ int m, s, f;
+ FRAMES_TO_MSF(start, &m, &s, &f);
+ mci_play.dwFrom = MCI_MAKE_MSF(m, s, f);
+
+ FRAMES_TO_MSF(start+length, &m, &s, &f);
+ mci_play.dwTo = MCI_MAKE_MSF(m, s, f);
+
+ return mci_CDioctl(MCI_PLAY, flags, &mci_play);
+}
+
+bool CDROM_Interface_Ioctl::mci_CDPause(void) {
+ return mci_CDioctl(MCI_PAUSE, MCI_WAIT, NULL);
+}
+
+bool CDROM_Interface_Ioctl::mci_CDResume(void) {
+ return mci_CDioctl(MCI_RESUME, MCI_WAIT, NULL);
+}
+
+bool CDROM_Interface_Ioctl::mci_CDStop(void) {
+ return mci_CDioctl(MCI_STOP, MCI_WAIT, NULL);
+}
+
+int CDROM_Interface_Ioctl::mci_CDStatus(void) {
+ int status;
+ MCI_STATUS_PARMS mci_status;
+
+ DWORD flags = MCI_STATUS_ITEM | MCI_WAIT;
+ mci_status.dwItem = MCI_STATUS_MODE;
+ if (mci_CDioctl(MCI_STATUS, flags, &mci_status)) {
+ status = -1;
+ } else {
+ switch (mci_status.dwReturn) {
+ case MCI_MODE_NOT_READY:
+ case MCI_MODE_OPEN:
+ status = 0;
+ break;
+ case MCI_MODE_STOP:
+ status = 1;
+ break;
+ case MCI_MODE_PLAY:
+ status = 2;
+ break;
+ case MCI_MODE_PAUSE:
+ status = 3;
+ break;
+ default:
+ status = -1;
+ break;
+ }
+ }
+
+ return status;
+}
+
+bool CDROM_Interface_Ioctl::mci_CDPosition(int *position) {
+ *position = 0;
+
+ DWORD flags = MCI_STATUS_ITEM | MCI_WAIT;
+
+ MCI_STATUS_PARMS mci_status;
+ mci_status.dwItem = MCI_STATUS_MODE;
+ if (mci_CDioctl(MCI_STATUS, flags, &mci_status)) return true;
+ switch (mci_status.dwReturn) {
+ case MCI_MODE_NOT_READY:
+ case MCI_MODE_OPEN:
+ case MCI_MODE_STOP:
+ return true; // not ready/undefined status
+ case MCI_MODE_PLAY:
+ case MCI_MODE_PAUSE:
+ mci_status.dwItem = MCI_STATUS_POSITION;
+ if (!mci_CDioctl(MCI_STATUS, flags, &mci_status)) {
+ *position = MSF_TO_FRAMES(
+ MCI_MSF_MINUTE(mci_status.dwReturn),
+ MCI_MSF_SECOND(mci_status.dwReturn),
+ MCI_MSF_FRAME(mci_status.dwReturn));
+ }
+ return false; // no error, position read
+ default:
+ break;
+ }
+ return false;
+}
+
+
+CDROM_Interface_Ioctl::dxPlayer CDROM_Interface_Ioctl::player = {
+ NULL, NULL, NULL, {0}, 0, 0, 0, false, false, false, {0} };
+
+CDROM_Interface_Ioctl::CDROM_Interface_Ioctl(cdioctl_cdatype ioctl_cda) {
+ pathname[0] = 0;
+ hIOCTL = INVALID_HANDLE_VALUE;
+ memset(&oldLeadOut,0,sizeof(oldLeadOut));
+ cdioctl_cda_selected = ioctl_cda;
+}
+
+CDROM_Interface_Ioctl::~CDROM_Interface_Ioctl() {
+ StopAudio();
+ if (use_mciplay) mci_CDStop();
+ Close();
+ if (use_mciplay) mci_CDClose();
+}
+
+bool CDROM_Interface_Ioctl::GetUPC(unsigned char& attr, char* upc) {
+ // FIXME : To Do
+ return true;
+}
+
+bool CDROM_Interface_Ioctl::GetAudioTracks(int& stTrack, int& endTrack, TMSF& leadOut) {
+ CDROM_TOC toc;
+ DWORD byteCount;
+ BOOL bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &byteCount,NULL);
+ if (!bStat) return false;
+
+ stTrack = toc.FirstTrack;
+ endTrack = toc.LastTrack;
+ leadOut.min = toc.TrackData[endTrack].Address[1];
+ leadOut.sec = toc.TrackData[endTrack].Address[2];
+ leadOut.fr = toc.TrackData[endTrack].Address[3];
+
+ if ((use_mciplay || use_dxplay) && (!track_start_valid)) {
+ Bits track_num = 0;
+ // get track start address of all tracks
+ for (Bits i=toc.FirstTrack; i<=toc.LastTrack+1; i++) {
+ if (((toc.TrackData[i].Control&1)==0) || (i==toc.LastTrack+1)) {
+ track_start[track_num] = MSF_TO_FRAMES(toc.TrackData[track_num].Address[1],toc.TrackData[track_num].Address[2],toc.TrackData[track_num].Address[3])-150;
+ track_start[track_num] += 150;
+ track_num++;
+ }
+ }
+ track_start_first = 0;
+ track_start_last = track_num-1;
+ track_start_valid = true;
+ }
+
+ return true;
+}
+
+bool CDROM_Interface_Ioctl::GetAudioTrackInfo(int track, TMSF& start, unsigned char& attr) {
+ CDROM_TOC toc;
+ DWORD byteCount;
+ BOOL bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &byteCount,NULL);
+ if (!bStat) return false;
+
+ attr = (toc.TrackData[track-1].Control << 4) & 0xEF;
+ start.min = toc.TrackData[track-1].Address[1];
+ start.sec = toc.TrackData[track-1].Address[2];
+ start.fr = toc.TrackData[track-1].Address[3];
+ return true;
+}
+
+bool CDROM_Interface_Ioctl::GetAudioTracksAll(void) {
+ if (track_start_valid) return true;
+
+ CDROM_TOC toc;
+ DWORD byteCount;
+ BOOL bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_TOC, NULL, 0,
+ &toc, sizeof(toc), &byteCount,NULL);
+ if (!bStat) return false;
+
+ Bits track_num = 0;
+ // get track start address of all tracks
+ for (Bits i=toc.FirstTrack; i<=toc.LastTrack+1; i++) {
+ if (((toc.TrackData[i].Control&1)==0) || (i==toc.LastTrack+1)) {
+ track_start[track_num] = MSF_TO_FRAMES(toc.TrackData[track_num].Address[1],toc.TrackData[track_num].Address[2],toc.TrackData[track_num].Address[3])-150;
+ track_start[track_num] += 150;
+ track_num++;
+ }
+ }
+ track_start_first = 0;
+ track_start_last = track_num-1;
+ track_start_valid = true;
+ return true;
+}
+
+bool CDROM_Interface_Ioctl::GetAudioSub(unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos) {
+ if (use_dxplay) {
+ track = 1;
+ FRAMES_TO_MSF(player.currFrame + 150, &absPos.min, &absPos.sec, &absPos.fr);
+ FRAMES_TO_MSF(player.currFrame + 150, &relPos.min, &relPos.sec, &relPos.fr);
+
+ if (GetAudioTracksAll()) {
+ // get track number from current frame
+ for (int i=track_start_first; i<=track_start_last; i++) {
+ if ((player.currFrame + 150<track_start[i+1]) && (player.currFrame + 150>=track_start[i])) {
+ // track found, calculate relative position
+ track = i;
+ FRAMES_TO_MSF(player.currFrame + 150-track_start[i],&relPos.min,&relPos.sec,&relPos.fr);
+ break;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ CDROM_SUB_Q_DATA_FORMAT insub;
+ SUB_Q_CHANNEL_DATA sub;
+ DWORD byteCount;
+
+ insub.Format = IOCTL_CDROM_CURRENT_POSITION;
+
+ BOOL bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_Q_CHANNEL, &insub, sizeof(insub),
+ &sub, sizeof(sub), &byteCount,NULL);
+ if (!bStat) return false;
+
+ attr = (sub.CurrentPosition.Control << 4) & 0xEF;
+ track = sub.CurrentPosition.TrackNumber;
+ index = sub.CurrentPosition.IndexNumber;
+ relPos.min = sub.CurrentPosition.TrackRelativeAddress[1];
+ relPos.sec = sub.CurrentPosition.TrackRelativeAddress[2];
+ relPos.fr = sub.CurrentPosition.TrackRelativeAddress[3];
+ absPos.min = sub.CurrentPosition.AbsoluteAddress[1];
+ absPos.sec = sub.CurrentPosition.AbsoluteAddress[2];
+ absPos.fr = sub.CurrentPosition.AbsoluteAddress[3];
+
+ if (use_mciplay) {
+ int cur_pos;
+ if (!mci_CDPosition(&cur_pos)) {
+ // absolute position read, try to calculate the track-relative position
+ if (GetAudioTracksAll()) {
+ for (int i=track_start_first; i<=track_start_last; i++) {
+ if ((cur_pos<track_start[i+1]) && (cur_pos>=track_start[i])) {
+ // track found, calculate relative position
+ FRAMES_TO_MSF(cur_pos-track_start[i],&relPos.min,&relPos.sec,&relPos.fr);
+ break;
+ }
+ }
+ }
+ FRAMES_TO_MSF(cur_pos,&absPos.min,&absPos.sec,&absPos.fr);
+ }
+ }
+
+ return true;
+}
+
+bool CDROM_Interface_Ioctl::GetAudioStatus(bool& playing, bool& pause) {
+ if (use_mciplay) {
+ int status = mci_CDStatus();
+ if (status<0) return false;
+ playing = (status==2);
+ pause = (status==3);
+ return true;
+ }
+ if (use_dxplay) {
+ playing = player.isPlaying;
+ pause = player.isPaused;
+ return true;
+ }
+
+ CDROM_SUB_Q_DATA_FORMAT insub;
+ SUB_Q_CHANNEL_DATA sub;
+ DWORD byteCount;
+
+ insub.Format = IOCTL_CDROM_CURRENT_POSITION;
+
+ BOOL bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_Q_CHANNEL, &insub, sizeof(insub),
+ &sub, sizeof(sub), &byteCount,NULL);
+ if (!bStat) return false;
+
+ playing = (sub.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS);
+ pause = (sub.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_PAUSED);
+
+ return true;
+}
+
+bool CDROM_Interface_Ioctl::GetMediaTrayStatus(bool& mediaPresent, bool& mediaChanged, bool& trayOpen) {
+ // Seems not possible to get this values using ioctl...
+ int track1,track2;
+ TMSF leadOut;
+ // If we can read, there's a media
+ mediaPresent = GetAudioTracks(track1, track2, leadOut),
+ trayOpen = !mediaPresent;
+ mediaChanged = (oldLeadOut.min!=leadOut.min) || (oldLeadOut.sec!=leadOut.sec) || (oldLeadOut.fr!=leadOut.fr);
+ if (mediaChanged) {
+ Close();
+ if (use_mciplay) mci_CDClose();
+ // Open new medium
+ Open();
+
+ if (cdioctl_cda_selected == CDIOCTL_CDA_MCI) {
+ // check this (what to do if cd is ejected):
+ use_mciplay = false;
+ if (!mci_CDOpen(pathname[4])) use_mciplay = true;
+ }
+ track_start_valid = false;
+ }
+ // Save old values
+ oldLeadOut.min = leadOut.min;
+ oldLeadOut.sec = leadOut.sec;
+ oldLeadOut.fr = leadOut.fr;
+ // always success
+ return true;
+}
+
+bool CDROM_Interface_Ioctl::PlayAudioSector (unsigned long start,unsigned long len) {
+ if (use_mciplay) {
+ if (!mci_CDPlay(start+150, len)) return true;
+ if (!mci_CDPlay(start+150, len-1)) return true;
+ return false;
+ }
+ if (use_dxplay) {
+ SDL_mutexP(player.mutex);
+ player.cd = this;
+ player.currFrame = start;
+ player.targetFrame = start + len;
+ player.isPlaying = true;
+ player.isPaused = false;
+ SDL_mutexV(player.mutex);
+ return true;
+ }
+
+ CDROM_PLAY_AUDIO_MSF audio;
+ DWORD byteCount;
+ // Start
+ unsigned long addr = start + 150;
+ audio.StartingF = (UCHAR)(addr%75); addr/=75;
+ audio.StartingS = (UCHAR)(addr%60);
+ audio.StartingM = (UCHAR)(addr/60);
+ // End
+ addr = start + len + 150;
+ audio.EndingF = (UCHAR)(addr%75); addr/=75;
+ audio.EndingS = (UCHAR)(addr%60);
+ audio.EndingM = (UCHAR)(addr/60);
+
+ BOOL bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_PLAY_AUDIO_MSF, &audio, sizeof(audio),
+ NULL, 0, &byteCount,NULL);
+ return bStat>0;
+}
+
+bool CDROM_Interface_Ioctl::PauseAudio(bool resume) {
+ if (use_mciplay) {
+ if (resume) {
+ if (!mci_CDResume()) return true;
+ } else {
+ if (!mci_CDPause()) return true;
+ }
+ return false;
+ }
+ if (use_dxplay) {
+ player.isPaused = !resume;
+ return true;
+ }
+
+ BOOL bStat;
+ DWORD byteCount;
+ if (resume) bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_RESUME_AUDIO, NULL, 0,
+ NULL, 0, &byteCount,NULL);
+ else bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_PAUSE_AUDIO, NULL, 0,
+ NULL, 0, &byteCount,NULL);
+ return bStat>0;
+}
+
+bool CDROM_Interface_Ioctl::StopAudio(void) {
+ if (use_mciplay) {
+ if (!mci_CDStop()) return true;
+ return false;
+ }
+ if (use_dxplay) {
+ player.isPlaying = false;
+ player.isPaused = false;
+ return true;
+ }
+
+ BOOL bStat;
+ DWORD byteCount;
+ bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_STOP_AUDIO, NULL, 0,
+ NULL, 0, &byteCount,NULL);
+ return bStat>0;
+}
+
+void CDROM_Interface_Ioctl::ChannelControl(TCtrl ctrl)
+{
+ player.ctrlUsed = (ctrl.out[0]!=0 || ctrl.out[1]!=1 || ctrl.vol[0]<0xfe || ctrl.vol[1]<0xfe);
+ player.ctrlData = ctrl;
+}
+
+bool CDROM_Interface_Ioctl::LoadUnloadMedia(bool unload) {
+ BOOL bStat;
+ DWORD byteCount;
+ if (unload) bStat = DeviceIoControl(hIOCTL,IOCTL_STORAGE_EJECT_MEDIA, NULL, 0,
+ NULL, 0, &byteCount,NULL);
+ else bStat = DeviceIoControl(hIOCTL,IOCTL_STORAGE_LOAD_MEDIA, NULL, 0,
+ NULL, 0, &byteCount,NULL);
+ track_start_valid = false;
+ return bStat>0;
+}
+
+bool CDROM_Interface_Ioctl::ReadSector(Bit8u *buffer, bool raw, unsigned long sector) {
+ BOOL bStat;
+ DWORD byteCount = 0;
+
+ Bitu buflen = raw ? RAW_SECTOR_SIZE : COOKED_SECTOR_SIZE;
+
+ if (!raw) {
+ // Cooked
+ int success = 0;
+ DWORD newPos = SetFilePointer(hIOCTL, sector*COOKED_SECTOR_SIZE, 0, FILE_BEGIN);
+ if (newPos != 0xFFFFFFFF) success = ReadFile(hIOCTL, buffer, buflen, &byteCount, NULL);
+ bStat = (success!=0);
+ } else {
+ // Raw
+ RAW_READ_INFO in;
+ in.DiskOffset.LowPart = sector*COOKED_SECTOR_SIZE;
+ in.DiskOffset.HighPart = 0;
+ in.SectorCount = 1;
+ in.TrackMode = CDDA;
+ bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_RAW_READ, &in, sizeof(in),
+ buffer, buflen, &byteCount,NULL);
+ }
+
+ return (byteCount==buflen) && (bStat>0);
+}
+
+bool CDROM_Interface_Ioctl::ReadSectors(PhysPt buffer, bool raw, unsigned long sector, unsigned long num) {
+ BOOL bStat;
+ DWORD byteCount = 0;
+
+ Bitu buflen = raw ? num*RAW_SECTOR_SIZE : num*COOKED_SECTOR_SIZE;
+ Bit8u* bufdata = new Bit8u[buflen];
+
+ if (!raw) {
+ // Cooked
+ int success = 0;
+ DWORD newPos = SetFilePointer(hIOCTL, sector*COOKED_SECTOR_SIZE, 0, FILE_BEGIN);
+ if (newPos != 0xFFFFFFFF) success = ReadFile(hIOCTL, bufdata, buflen, &byteCount, NULL);
+ bStat = (success!=0);
+ } else {
+ // Raw
+ RAW_READ_INFO in;
+ in.DiskOffset.LowPart = sector*COOKED_SECTOR_SIZE;
+ in.DiskOffset.HighPart = 0;
+ in.SectorCount = num;
+ in.TrackMode = CDDA;
+ bStat = DeviceIoControl(hIOCTL,IOCTL_CDROM_RAW_READ, &in, sizeof(in),
+ bufdata, buflen, &byteCount,NULL);
+ }
+
+ MEM_BlockWrite(buffer,bufdata,buflen);
+ delete[] bufdata;
+
+ return (byteCount==buflen) && (bStat>0);
+}
+
+void CDROM_Interface_Ioctl::dx_CDAudioCallBack(Bitu len) {
+ len *= 4; // 16 bit, stereo
+ if (!len) return;
+ if (!player.isPlaying || player.isPaused) {
+ player.channel->AddSilence();
+ return;
+ }
+ SDL_mutexP(player.mutex);
+ while (player.bufLen < (Bits)len) {
+ bool success;
+ if (player.targetFrame > player.currFrame)
+ success = player.cd->ReadSector(&player.buffer[player.bufLen], true, player.currFrame);
+ else success = false;
+
+ if (success) {
+ player.currFrame++;
+ player.bufLen += RAW_SECTOR_SIZE;
+ } else {
+ memset(&player.buffer[player.bufLen], 0, len - player.bufLen);
+ player.bufLen = len;
+ player.isPlaying = false;
+ }
+ }
+ SDL_mutexV(player.mutex);
+ if (player.ctrlUsed) {
+ Bit16s sample0,sample1;
+ Bit16s * samples=(Bit16s *)&player.buffer;
+ for (Bitu pos=0;pos<len/4;pos++) {
+ sample0=samples[pos*2+player.ctrlData.out[0]];
+ sample1=samples[pos*2+player.ctrlData.out[1]];
+ samples[pos*2+0]=(Bit16s)(sample0*player.ctrlData.vol[0]/255.0);
+ samples[pos*2+1]=(Bit16s)(sample1*player.ctrlData.vol[1]/255.0);
+ }
+ }
+ player.channel->AddSamples_s16(len/4,(Bit16s *)player.buffer);
+ memmove(player.buffer, &player.buffer[len], player.bufLen - len);
+ player.bufLen -= len;
+}
+
+bool CDROM_Interface_Ioctl::SetDevice(char* path, int forceCD) {
+ mci_devid = 0;
+ use_mciplay = false;
+ use_dxplay = false;
+ track_start_valid = false;
+ if (GetDriveType(path)==DRIVE_CDROM) {
+ char letter [3] = { 0, ':', 0 };
+ letter[0] = path[0];
+ strcpy(pathname,"\\\\.\\");
+ strcat(pathname,letter);
+ if (Open()) {
+ if (cdioctl_cda_selected == CDIOCTL_CDA_MCI) {
+ // check if MCI-interface can be used for cd audio
+ if (!mci_CDOpen(path[0])) use_mciplay = true;
+ }
+ if (!use_mciplay) {
+ if (cdioctl_cda_selected == CDIOCTL_CDA_DX) {
+ // use direct sector access for cd audio routines
+ player.mutex = SDL_CreateMutex();
+ if (!player.channel) {
+ player.channel = MIXER_AddChannel(&dx_CDAudioCallBack, 44100, "CDAUDIO");
+ }
+ player.channel->Enable(true);
+ use_dxplay = true;
+ }
+ }
+ return true;
+ };
+ }
+ return false;
+}
+
+bool CDROM_Interface_Ioctl::Open(void) {
+ hIOCTL = CreateFile(pathname, // drive to open
+ GENERIC_READ, // read access
+ FILE_SHARE_READ | // share mode
+ FILE_SHARE_WRITE,
+ NULL, // default security attributes
+ OPEN_EXISTING, // disposition
+ 0, // file attributes
+ NULL); // do not copy file attributes
+ return (hIOCTL!=INVALID_HANDLE_VALUE);
+}
+
+void CDROM_Interface_Ioctl::Close(void) {
+ CloseHandle(hIOCTL);
+}
+
+#endif
diff --git a/src/dos/dev_con.h b/src/dos/dev_con.h
new file mode 100644
index 000000000..330b56f2c
--- /dev/null
+++ b/src/dos/dev_con.h
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dos_inc.h"
+#include "../ints/int10.h"
+#include <string.h>
+
+#define NUMBER_ANSI_DATA 10
+
+class device_CON : public DOS_Device {
+public:
+ device_CON();
+ bool Read(Bit8u * data,Bit16u * size);
+ bool Write(Bit8u * data,Bit16u * size);
+ bool Seek(Bit32u * pos,Bit32u type);
+ bool Close();
+ Bit16u GetInformation(void);
+ bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode){return false;}
+ bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode){return false;}
+private:
+ void ClearAnsi(void);
+ void Output(Bit8u chr);
+ Bit8u readcache;
+ struct ansi { /* should create a constructor, which would fill them with the appropriate values */
+ bool esc;
+ bool sci;
+ bool enabled;
+ Bit8u attr;
+ Bit8u data[NUMBER_ANSI_DATA];
+ Bit8u numberofarg;
+ Bit8s savecol;
+ Bit8s saverow;
+ bool warned;
+ } ansi;
+};
+
+bool device_CON::Read(Bit8u * data,Bit16u * size) {
+ Bit16u oldax=reg_ax;
+ Bit16u count=0;
+ INT10_SetCurMode();
+ if ((readcache) && (*size)) {
+ data[count++]=readcache;
+ if(dos.echo) INT10_TeletypeOutput(readcache,7);
+ readcache=0;
+ }
+ while (*size>count) {
+ reg_ah=(IS_EGAVGA_ARCH)?0x10:0x0;
+ CALLBACK_RunRealInt(0x16);
+ switch(reg_al) {
+ case 13:
+ data[count++]=0x0D;
+ if (*size>count) data[count++]=0x0A; // it's only expanded if there is room for it. (NO cache)
+ *size=count;
+ reg_ax=oldax;
+ if(dos.echo) {
+ INT10_TeletypeOutput(13,7); //maybe don't do this ( no need for it actually ) (but it's compatible)
+ INT10_TeletypeOutput(10,7);
+ }
+ return true;
+ break;
+ case 8:
+ if(*size==1) data[count++]=reg_al; //one char at the time so give back that BS
+ else if(count) { //Remove data if it exists (extended keys don't go right)
+ data[count--]=0;
+ INT10_TeletypeOutput(8,7);
+ INT10_TeletypeOutput(' ',7);
+ } else {
+ continue; //no data read yet so restart whileloop.
+ }
+ break;
+ case 0xe0: /* Extended keys in the int 16 0x10 case */
+ if(!reg_ah) { /*extended key if reg_ah isn't 0 */
+ data[count++] = reg_al;
+ } else {
+ data[count++] = 0;
+ if (*size>count) data[count++] = reg_ah;
+ else readcache = reg_ah;
+ }
+ break;
+ case 0: /* Extended keys in the int 16 0x0 case */
+ data[count++]=reg_al;
+ if (*size>count) data[count++]=reg_ah;
+ else readcache=reg_ah;
+ break;
+ default:
+ data[count++]=reg_al;
+ break;
+ }
+ if(dos.echo) { //what to do if *size==1 and character is BS ?????
+ INT10_TeletypeOutput(reg_al,7);
+ }
+ }
+ *size=count;
+ reg_ax=oldax;
+ return true;
+}
+
+
+bool device_CON::Write(Bit8u * data,Bit16u * size) {
+ Bit16u count=0;
+ Bitu i;
+ Bit8u col,row,page;
+ Bit16u ncols,nrows;
+ Bit8u tempdata;
+ INT10_SetCurMode();
+ while (*size>count) {
+ if (!ansi.esc){
+ if(data[count]=='\033') {
+ /*clear the datastructure */
+ ClearAnsi();
+ /* start the sequence */
+ ansi.esc=true;
+ count++;
+ continue;
+ } else if(data[count] == '\t' && !dos.direct_output) {
+ /* expand tab if not direct output */
+ page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+ do {
+ Output(' ');
+ col=CURSOR_POS_COL(page);
+ } while(col%8);
+ count++;
+ continue;
+ } else {
+ Output(data[count]);
+ count++;
+ continue;
+ }
+ }
+
+ if(!ansi.sci){
+
+ switch(data[count]){
+ case '[':
+ ansi.sci=true;
+ break;
+ case '7': /* save cursor pos + attr */
+ case '8': /* restore this (Wonder if this is actually used) */
+ case 'D':/* scrolling DOWN*/
+ case 'M':/* scrolling UP*/
+ default:
+ LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unknown char %c after a esc",data[count]); /*prob () */
+ ClearAnsi();
+ break;
+ }
+ count++;
+ continue;
+ }
+ /*ansi.esc and ansi.sci are true */
+ if (!dos.internal_output) ansi.enabled=true;
+ page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+ switch(data[count]){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ansi.data[ansi.numberofarg]=10*ansi.data[ansi.numberofarg]+(data[count]-'0');
+ break;
+ case ';': /* till a max of NUMBER_ANSI_DATA */
+ ansi.numberofarg++;
+ break;
+ case 'm': /* SGR */
+ for(i=0;i<=ansi.numberofarg;i++){
+ switch(ansi.data[i]){
+ case 0: /* normal */
+ ansi.attr=0x07;//Real ansi does this as well. (should do current defaults)
+ break;
+ case 1: /* bold mode on*/
+ ansi.attr|=0x08;
+ break;
+ case 4: /* underline */
+ LOG(LOG_IOCTL,LOG_NORMAL)("ANSI:no support for underline yet");
+ break;
+ case 5: /* blinking */
+ ansi.attr|=0x80;
+ break;
+ case 7: /* reverse */
+ ansi.attr=0x70;//Just like real ansi. (should do use current colors reversed)
+ break;
+ case 30: /* fg color black */
+ ansi.attr&=0xf8;
+ ansi.attr|=0x0;
+ break;
+ case 31: /* fg color red */
+ ansi.attr&=0xf8;
+ ansi.attr|=0x4;
+ break;
+ case 32: /* fg color green */
+ ansi.attr&=0xf8;
+ ansi.attr|=0x2;
+ break;
+ case 33: /* fg color yellow */
+ ansi.attr&=0xf8;
+ ansi.attr|=0x6;
+ break;
+ case 34: /* fg color blue */
+ ansi.attr&=0xf8;
+ ansi.attr|=0x1;
+ break;
+ case 35: /* fg color magenta */
+ ansi.attr&=0xf8;
+ ansi.attr|=0x5;
+ break;
+ case 36: /* fg color cyan */
+ ansi.attr&=0xf8;
+ ansi.attr|=0x3;
+ break;
+ case 37: /* fg color white */
+ ansi.attr&=0xf8;
+ ansi.attr|=0x7;
+ break;
+ case 40:
+ ansi.attr&=0x8f;
+ ansi.attr|=0x0;
+ break;
+ case 41:
+ ansi.attr&=0x8f;
+ ansi.attr|=0x40;
+ break;
+ case 42:
+ ansi.attr&=0x8f;
+ ansi.attr|=0x20;
+ break;
+ case 43:
+ ansi.attr&=0x8f;
+ ansi.attr|=0x60;
+ break;
+ case 44:
+ ansi.attr&=0x8f;
+ ansi.attr|=0x10;
+ break;
+ case 45:
+ ansi.attr&=0x8f;
+ ansi.attr|=0x50;
+ break;
+ case 46:
+ ansi.attr&=0x8f;
+ ansi.attr|=0x30;
+ break;
+ case 47:
+ ansi.attr&=0x8f;
+ ansi.attr|=0x70;
+ break;
+ default:
+ break;
+ }
+ }
+ ClearAnsi();
+ break;
+ case 'f':
+ case 'H':/* Cursor Pos*/
+ if(!ansi.warned) { //Inform the debugger that ansi is used.
+ ansi.warned = true;
+ LOG(LOG_IOCTL,LOG_WARN)("ANSI SEQUENCES USED");
+ }
+ ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ nrows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1;
+ /* Turn them into positions that are on the screen */
+ if(ansi.data[0] == 0) ansi.data[0] = 1;
+ if(ansi.data[1] == 0) ansi.data[1] = 1;
+ if(ansi.data[0] > nrows) ansi.data[0] = (Bit8u)nrows;
+ if(ansi.data[1] > ncols) ansi.data[1] = (Bit8u)ncols;
+ INT10_SetCursorPos(--(ansi.data[0]),--(ansi.data[1]),page); /*ansi=1 based, int10 is 0 based */
+ ClearAnsi();
+ break;
+ /* cursor up down and forward and backward only change the row or the col not both */
+ case 'A': /* cursor up*/
+ col=CURSOR_POS_COL(page) ;
+ row=CURSOR_POS_ROW(page) ;
+ tempdata = (ansi.data[0]? ansi.data[0] : 1);
+ if(tempdata > row) { row=0; }
+ else { row-=tempdata;}
+ INT10_SetCursorPos(row,col,page);
+ ClearAnsi();
+ break;
+ case 'B': /*cursor Down */
+ col=CURSOR_POS_COL(page) ;
+ row=CURSOR_POS_ROW(page) ;
+ nrows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1;
+ tempdata = (ansi.data[0]? ansi.data[0] : 1);
+ if(tempdata + static_cast<Bitu>(row) >= nrows)
+ { row = nrows - 1;}
+ else { row += tempdata; }
+ INT10_SetCursorPos(row,col,page);
+ ClearAnsi();
+ break;
+ case 'C': /*cursor forward */
+ col=CURSOR_POS_COL(page);
+ row=CURSOR_POS_ROW(page);
+ ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ tempdata=(ansi.data[0]? ansi.data[0] : 1);
+ if(tempdata + static_cast<Bitu>(col) >= ncols)
+ { col = ncols - 1;}
+ else { col += tempdata;}
+ INT10_SetCursorPos(row,col,page);
+ ClearAnsi();
+ break;
+ case 'D': /*Cursor Backward */
+ col=CURSOR_POS_COL(page);
+ row=CURSOR_POS_ROW(page);
+ tempdata=(ansi.data[0]? ansi.data[0] : 1);
+ if(tempdata > col) {col = 0;}
+ else { col -= tempdata;}
+ INT10_SetCursorPos(row,col,page);
+ ClearAnsi();
+ break;
+ case 'J': /*erase screen and move cursor home*/
+ if(ansi.data[0]==0) ansi.data[0]=2;
+ if(ansi.data[0]!=2) {/* every version behaves like type 2 */
+ LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: esc[%dJ called : not supported handling as 2",ansi.data[0]);
+ }
+ INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
+ ClearAnsi();
+ INT10_SetCursorPos(0,0,page);
+ break;
+ case 'h': /* SET MODE (if code =7 enable linewrap) */
+ case 'I': /* RESET MODE */
+ LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: set/reset mode called(not supported)");
+ ClearAnsi();
+ break;
+ case 'u': /* Restore Cursor Pos */
+ INT10_SetCursorPos(ansi.saverow,ansi.savecol,page);
+ ClearAnsi();
+ break;
+ case 's': /* SAVE CURSOR POS */
+ ansi.savecol=CURSOR_POS_COL(page);
+ ansi.saverow=CURSOR_POS_ROW(page);
+ ClearAnsi();
+ break;
+ case 'K': /* erase till end of line (don't touch cursor) */
+ col = CURSOR_POS_COL(page);
+ row = CURSOR_POS_ROW(page);
+ ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ INT10_WriteChar(' ',ansi.attr,page,ncols-col,true); //Use this one to prevent scrolling when end of screen is reached
+ //for(i = col;i<(Bitu) ncols; i++) INT10_TeletypeOutputAttr(' ',ansi.attr,true);
+ INT10_SetCursorPos(row,col,page);
+ ClearAnsi();
+ break;
+ case 'M': /* delete line (NANSI) */
+ row = CURSOR_POS_ROW(page);
+ ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ nrows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1;
+ INT10_ScrollWindow(row,0,nrows-1,ncols-1,ansi.data[0]? -ansi.data[0] : -1,ansi.attr,0xFF);
+ ClearAnsi();
+ break;
+ case 'l':/* (if code =7) disable linewrap */
+ case 'p':/* reassign keys (needs strings) */
+ case 'i':/* printer stuff */
+ default:
+ LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled char %c in esc[",data[count]);
+ ClearAnsi();
+ break;
+ }
+ count++;
+ }
+ *size=count;
+ return true;
+}
+
+bool device_CON::Seek(Bit32u * pos,Bit32u type) {
+ // seek is valid
+ *pos = 0;
+ return true;
+}
+
+bool device_CON::Close() {
+ return true;
+}
+
+Bit16u device_CON::GetInformation(void) {
+ Bit16u head=mem_readw(BIOS_KEYBOARD_BUFFER_HEAD);
+ Bit16u tail=mem_readw(BIOS_KEYBOARD_BUFFER_TAIL);
+
+ if ((head==tail) && !readcache) return 0x80D3; /* No Key Available */
+ if (readcache || real_readw(0x40,head)) return 0x8093; /* Key Available */
+
+ /* remove the zero from keyboard buffer */
+ Bit16u start=mem_readw(BIOS_KEYBOARD_BUFFER_START);
+ Bit16u end =mem_readw(BIOS_KEYBOARD_BUFFER_END);
+ head+=2;
+ if (head>=end) head=start;
+ mem_writew(BIOS_KEYBOARD_BUFFER_HEAD,head);
+ return 0x80D3; /* No Key Available */
+}
+
+device_CON::device_CON() {
+ SetName("CON");
+ readcache=0;
+ ansi.enabled=false;
+ ansi.attr=0x7;
+ ansi.saverow=0;
+ ansi.savecol=0;
+ ansi.warned=false;
+ ClearAnsi();
+}
+
+void device_CON::ClearAnsi(void){
+ for(Bit8u i=0; i<NUMBER_ANSI_DATA;i++) ansi.data[i]=0;
+ ansi.esc=false;
+ ansi.sci=false;
+ ansi.numberofarg=0;
+}
+
+void device_CON::Output(Bit8u chr) {
+ if (dos.internal_output || ansi.enabled) {
+ if (CurMode->type==M_TEXT) {
+ Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+ Bit8u col=CURSOR_POS_COL(page);
+ Bit8u row=CURSOR_POS_ROW(page);
+ BIOS_NCOLS;BIOS_NROWS;
+ if (nrows==row+1 && (chr=='\n' || (ncols==col+1 && chr!='\r' && chr!=8 && chr!=7))) {
+ INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,ansi.attr,page);
+ INT10_SetCursorPos(row-1,col,page);
+ }
+ }
+ INT10_TeletypeOutputAttr(chr,ansi.attr,true);
+ } else INT10_TeletypeOutput(chr,7);
+ }
diff --git a/src/dos/dos.cpp b/src/dos/dos.cpp
new file mode 100644
index 000000000..07e39f1d5
--- /dev/null
+++ b/src/dos/dos.cpp
@@ -0,0 +1,1274 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "dosbox.h"
+#include "bios.h"
+#include "mem.h"
+#include "callback.h"
+#include "regs.h"
+#include "dos_inc.h"
+#include "setup.h"
+#include "support.h"
+#include "serialport.h"
+
+DOS_Block dos;
+DOS_InfoBlock dos_infoblock;
+
+#define DOS_COPYBUFSIZE 0x10000
+Bit8u dos_copybuf[DOS_COPYBUFSIZE];
+
+void DOS_SetError(Bit16u code) {
+ dos.errorcode=code;
+}
+
+const Bit8u DOS_DATE_months[] = {
+ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static void DOS_AddDays(Bitu days) {
+ dos.date.day += days;
+ Bit8u monthlimit = DOS_DATE_months[dos.date.month];
+
+ if(dos.date.day > monthlimit) {
+ if((dos.date.year %4 == 0) && (dos.date.month==2)) {
+ // leap year
+ if(dos.date.day > 29) {
+ dos.date.month++;
+ dos.date.day -= 29;
+ }
+ } else {
+ //not leap year
+ dos.date.month++;
+ dos.date.day -= monthlimit;
+ }
+ if(dos.date.month > 12) {
+ // year over
+ dos.date.month = 1;
+ dos.date.year++;
+ }
+ }
+}
+
+#define DATA_TRANSFERS_TAKE_CYCLES 1
+#ifdef DATA_TRANSFERS_TAKE_CYCLES
+
+#ifndef DOSBOX_CPU_H
+#include "cpu.h"
+#endif
+static inline void modify_cycles(Bits value) {
+ if((4*value+5) < CPU_Cycles) {
+ CPU_Cycles -= 4*value;
+ CPU_IODelayRemoved += 4*value;
+ } else {
+ CPU_IODelayRemoved += CPU_Cycles/*-5*/; //don't want to mess with negative
+ CPU_Cycles = 5;
+ }
+}
+#else
+static inline void modify_cycles(Bits /* value */) {
+ return;
+}
+#endif
+#define DOS_OVERHEAD 1
+#ifdef DOS_OVERHEAD
+#ifndef DOSBOX_CPU_H
+#include "cpu.h"
+#endif
+
+static inline void overhead() {
+ reg_ip += 2;
+}
+#else
+static inline void overhead() {
+ return;
+}
+#endif
+
+#define DOSNAMEBUF 256
+static Bitu DOS_21Handler(void) {
+ if (((reg_ah != 0x50) && (reg_ah != 0x51) && (reg_ah != 0x62) && (reg_ah != 0x64)) && (reg_ah<0x6c)) {
+ DOS_PSP psp(dos.psp());
+ psp.SetStack(RealMake(SegValue(ss),reg_sp-18));
+ }
+
+ char name1[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII];
+ char name2[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII];
+
+ static Bitu time_start = 0; //For emulating temporary time changes.
+
+ switch (reg_ah) {
+ case 0x00: /* Terminate Program */
+ DOS_Terminate(mem_readw(SegPhys(ss)+reg_sp+2),false,0);
+ break;
+ case 0x01: /* Read character from STDIN, with echo */
+ {
+ Bit8u c;Bit16u n=1;
+ dos.echo=true;
+ DOS_ReadFile(STDIN,&c,&n);
+ reg_al=c;
+ dos.echo=false;
+ }
+ break;
+ case 0x02: /* Write character to STDOUT */
+ {
+ Bit8u c=reg_dl;Bit16u n=1;
+ DOS_WriteFile(STDOUT,&c,&n);
+ //Not in the official specs, but happens nonetheless. (last written character)
+ reg_al=(c==9)?0x20:c; //strangely, tab conversion to spaces is reflected here
+ }
+ break;
+ case 0x03: /* Read character from STDAUX */
+ {
+ Bit16u port = real_readw(0x40,0);
+ if(port!=0 && serialports[0]) {
+ Bit8u status;
+ // RTS/DTR on
+ IO_WriteB(port+4,0x3);
+ serialports[0]->Getchar(&reg_al, &status, true, 0xFFFFFFFF);
+ }
+ }
+ break;
+ case 0x04: /* Write Character to STDAUX */
+ {
+ Bit16u port = real_readw(0x40,0);
+ if(port!=0 && serialports[0]) {
+ // RTS/DTR on
+ IO_WriteB(port+4,0x3);
+ serialports[0]->Putchar(reg_dl,true,true, 0xFFFFFFFF);
+ // RTS off
+ IO_WriteB(port+4,0x1);
+ }
+ }
+ break;
+ case 0x05: /* Write Character to PRINTER */
+ E_Exit("DOS:Unhandled call %02X",reg_ah);
+ break;
+ case 0x06: /* Direct Console Output / Input */
+ switch (reg_dl) {
+ case 0xFF: /* Input */
+ {
+ //Simulate DOS overhead for timing sensitive games
+ //MM1
+ overhead();
+ //TODO Make this better according to standards
+ if (!DOS_GetSTDINStatus()) {
+ reg_al=0;
+ CALLBACK_SZF(true);
+ break;
+ }
+ Bit8u c;Bit16u n=1;
+ DOS_ReadFile(STDIN,&c,&n);
+ reg_al=c;
+ CALLBACK_SZF(false);
+ break;
+ }
+ default:
+ {
+ Bit8u c = reg_dl;Bit16u n = 1;
+ dos.direct_output=true;
+ DOS_WriteFile(STDOUT,&c,&n);
+ dos.direct_output=false;
+ reg_al=c;
+ }
+ break;
+ };
+ break;
+ case 0x07: /* Character Input, without echo */
+ {
+ Bit8u c;Bit16u n=1;
+ DOS_ReadFile (STDIN,&c,&n);
+ reg_al=c;
+ break;
+ };
+ case 0x08: /* Direct Character Input, without echo (checks for breaks officially :)*/
+ {
+ Bit8u c;Bit16u n=1;
+ DOS_ReadFile (STDIN,&c,&n);
+ reg_al=c;
+ break;
+ };
+ case 0x09: /* Write string to STDOUT */
+ {
+ Bit8u c;Bit16u n=1;
+ PhysPt buf=SegPhys(ds)+reg_dx;
+ while ((c=mem_readb(buf++))!='$') {
+ DOS_WriteFile(STDOUT,&c,&n);
+ }
+ reg_al=c;
+ }
+ break;
+ case 0x0a: /* Buffered Input */
+ {
+ //TODO ADD Break checkin in STDIN but can't care that much for it
+ PhysPt data=SegPhys(ds)+reg_dx;
+ Bit8u free=mem_readb(data);
+ Bit8u read=0;Bit8u c;Bit16u n=1;
+ if (!free) break;
+ free--;
+ for(;;) {
+ DOS_ReadFile(STDIN,&c,&n);
+ if (n == 0) // End of file
+ E_Exit("DOS:0x0a:Redirected input reached EOF");
+ if (c == 10) // Line feed
+ continue;
+ if (c == 8) { // Backspace
+ if (read) { //Something to backspace.
+ // STDOUT treats backspace as non-destructive.
+ DOS_WriteFile(STDOUT,&c,&n);
+ c = ' '; DOS_WriteFile(STDOUT,&c,&n);
+ c = 8; DOS_WriteFile(STDOUT,&c,&n);
+ --read;
+ }
+ continue;
+ }
+ if (read == free && c != 13) { // Keyboard buffer full
+ Bit8u bell = 7;
+ DOS_WriteFile(STDOUT, &bell, &n);
+ continue;
+ }
+ DOS_WriteFile(STDOUT,&c,&n);
+ mem_writeb(data+read+2,c);
+ if (c==13)
+ break;
+ read++;
+ };
+ mem_writeb(data+1,read);
+ break;
+ };
+ case 0x0b: /* Get STDIN Status */
+ if (!DOS_GetSTDINStatus()) {reg_al=0x00;}
+ else {reg_al=0xFF;}
+ //Simulate some overhead for timing issues
+ //Tankwar menu (needs maybe even more)
+ overhead();
+ break;
+ case 0x0c: /* Flush Buffer and read STDIN call */
+ {
+ /* flush buffer if STDIN is CON */
+ Bit8u handle=RealHandle(STDIN);
+ if (handle!=0xFF && Files[handle] && Files[handle]->IsName("CON")) {
+ Bit8u c;Bit16u n;
+ while (DOS_GetSTDINStatus()) {
+ n=1; DOS_ReadFile(STDIN,&c,&n);
+ }
+ }
+ switch (reg_al) {
+ case 0x1:
+ case 0x6:
+ case 0x7:
+ case 0x8:
+ case 0xa:
+ {
+ Bit8u oldah=reg_ah;
+ reg_ah=reg_al;
+ DOS_21Handler();
+ reg_ah=oldah;
+ }
+ break;
+ default:
+// LOG_ERROR("DOS:0C:Illegal Flush STDIN Buffer call %d",reg_al);
+ reg_al=0;
+ break;
+ }
+ }
+ break;
+//TODO Find out the values for when reg_al!=0
+//TODO Hope this doesn't do anything special
+ case 0x0d: /* Disk Reset */
+//Sure let's reset a virtual disk
+ break;
+ case 0x0e: /* Select Default Drive */
+ DOS_SetDefaultDrive(reg_dl);
+ reg_al=DOS_DRIVES;
+ break;
+ case 0x0f: /* Open File using FCB */
+ if(DOS_FCBOpen(SegValue(ds),reg_dx)){
+ reg_al=0;
+ }else{
+ reg_al=0xff;
+ }
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:0x0f FCB-fileopen used, result:al=%d",reg_al);
+ break;
+ case 0x10: /* Close File using FCB */
+ if(DOS_FCBClose(SegValue(ds),reg_dx)){
+ reg_al=0;
+ }else{
+ reg_al=0xff;
+ }
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:0x10 FCB-fileclose used, result:al=%d",reg_al);
+ break;
+ case 0x11: /* Find First Matching File using FCB */
+ if(DOS_FCBFindFirst(SegValue(ds),reg_dx)) reg_al = 0x00;
+ else reg_al = 0xFF;
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:0x11 FCB-FindFirst used, result:al=%d",reg_al);
+ break;
+ case 0x12: /* Find Next Matching File using FCB */
+ if(DOS_FCBFindNext(SegValue(ds),reg_dx)) reg_al = 0x00;
+ else reg_al = 0xFF;
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:0x12 FCB-FindNext used, result:al=%d",reg_al);
+ break;
+ case 0x13: /* Delete File using FCB */
+ if (DOS_FCBDeleteFile(SegValue(ds),reg_dx)) reg_al = 0x00;
+ else reg_al = 0xFF;
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:0x16 FCB-Delete used, result:al=%d",reg_al);
+ break;
+ case 0x14: /* Sequential read from FCB */
+ reg_al = DOS_FCBRead(SegValue(ds),reg_dx,0);
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:0x14 FCB-Read used, result:al=%d",reg_al);
+ break;
+ case 0x15: /* Sequential write to FCB */
+ reg_al=DOS_FCBWrite(SegValue(ds),reg_dx,0);
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:0x15 FCB-Write used, result:al=%d",reg_al);
+ break;
+ case 0x16: /* Create or truncate file using FCB */
+ if (DOS_FCBCreate(SegValue(ds),reg_dx)) reg_al = 0x00;
+ else reg_al = 0xFF;
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:0x16 FCB-Create used, result:al=%d",reg_al);
+ break;
+ case 0x17: /* Rename file using FCB */
+ if (DOS_FCBRenameFile(SegValue(ds),reg_dx)) reg_al = 0x00;
+ else reg_al = 0xFF;
+ break;
+ case 0x1b: /* Get allocation info for default drive */
+ if (!DOS_GetAllocationInfo(0,&reg_cx,&reg_al,&reg_dx)) reg_al=0xff;
+ break;
+ case 0x1c: /* Get allocation info for specific drive */
+ if (!DOS_GetAllocationInfo(reg_dl,&reg_cx,&reg_al,&reg_dx)) reg_al=0xff;
+ break;
+ case 0x21: /* Read random record from FCB */
+ {
+ Bit16u toread=1;
+ reg_al = DOS_FCBRandomRead(SegValue(ds),reg_dx,&toread,true);
+ }
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:0x21 FCB-Random read used, result:al=%d",reg_al);
+ break;
+ case 0x22: /* Write random record to FCB */
+ {
+ Bit16u towrite=1;
+ reg_al=DOS_FCBRandomWrite(SegValue(ds),reg_dx,&towrite,true);
+ }
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:0x22 FCB-Random write used, result:al=%d",reg_al);
+ break;
+ case 0x23: /* Get file size for FCB */
+ if (DOS_FCBGetFileSize(SegValue(ds),reg_dx)) reg_al = 0x00;
+ else reg_al = 0xFF;
+ break;
+ case 0x24: /* Set Random Record number for FCB */
+ DOS_FCBSetRandomRecord(SegValue(ds),reg_dx);
+ break;
+ case 0x27: /* Random block read from FCB */
+ reg_al = DOS_FCBRandomRead(SegValue(ds),reg_dx,&reg_cx,false);
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:0x27 FCB-Random(block) read used, result:al=%d",reg_al);
+ break;
+ case 0x28: /* Random Block write to FCB */
+ reg_al=DOS_FCBRandomWrite(SegValue(ds),reg_dx,&reg_cx,false);
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:0x28 FCB-Random(block) write used, result:al=%d",reg_al);
+ break;
+ case 0x29: /* Parse filename into FCB */
+ {
+ Bit8u difference;
+ char string[1024];
+ MEM_StrCopy(SegPhys(ds)+reg_si,string,1023); // 1024 toasts the stack
+ reg_al=FCB_Parsename(SegValue(es),reg_di,reg_al ,string, &difference);
+ reg_si+=difference;
+ }
+ LOG(LOG_FCB,LOG_NORMAL)("DOS:29:FCB Parse Filename, result:al=%d",reg_al);
+ break;
+ case 0x19: /* Get current default drive */
+ reg_al=DOS_GetDefaultDrive();
+ break;
+ case 0x1a: /* Set Disk Transfer Area Address */
+ dos.dta(RealMakeSeg(ds,reg_dx));
+ break;
+ case 0x25: /* Set Interrupt Vector */
+ RealSetVec(reg_al,RealMakeSeg(ds,reg_dx));
+ break;
+ case 0x26: /* Create new PSP */
+ DOS_NewPSP(reg_dx,DOS_PSP(dos.psp()).GetSize());
+ reg_al=0xf0; /* al destroyed */
+ break;
+ case 0x2a: /* Get System Date */
+ {
+ reg_ax=0; // get time
+ CALLBACK_RunRealInt(0x1a);
+ if(reg_al) DOS_AddDays(reg_al);
+ int a = (14 - dos.date.month)/12;
+ int y = dos.date.year - a;
+ int m = dos.date.month + 12*a - 2;
+ reg_al=(dos.date.day+y+(y/4)-(y/100)+(y/400)+(31*m)/12) % 7;
+ reg_cx=dos.date.year;
+ reg_dh=dos.date.month;
+ reg_dl=dos.date.day;
+ }
+ break;
+ case 0x2b: /* Set System Date */
+ if (reg_cx<1980) { reg_al=0xff;break;}
+ if ((reg_dh>12) || (reg_dh==0)) { reg_al=0xff;break;}
+ if (reg_dl==0) { reg_al=0xff;break;}
+ if (reg_dl>DOS_DATE_months[reg_dh]) {
+ if(!((reg_dh==2)&&(reg_cx%4 == 0)&&(reg_dl==29))) // february pass
+ { reg_al=0xff;break; }
+ }
+ dos.date.year=reg_cx;
+ dos.date.month=reg_dh;
+ dos.date.day=reg_dl;
+ reg_al=0;
+ break;
+ case 0x2c: { /* Get System Time */
+ reg_ax=0; // get time
+ CALLBACK_RunRealInt(0x1a);
+ if(reg_al) DOS_AddDays(reg_al);
+ reg_ah=0x2c;
+
+ Bitu ticks=((Bitu)reg_cx<<16)|reg_dx;
+ if(time_start<=ticks) ticks-=time_start;
+ Bitu time=(Bitu)((100.0/((double)PIT_TICK_RATE/65536.0)) * (double)ticks);
+
+ reg_dl=(Bit8u)((Bitu)time % 100); // 1/100 seconds
+ time/=100;
+ reg_dh=(Bit8u)((Bitu)time % 60); // seconds
+ time/=60;
+ reg_cl=(Bit8u)((Bitu)time % 60); // minutes
+ time/=60;
+ reg_ch=(Bit8u)((Bitu)time % 24); // hours
+
+ //Simulate DOS overhead for timing-sensitive games
+ //Robomaze 2
+ overhead();
+ break;
+ }
+ case 0x2d: /* Set System Time */
+ LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Set System Time not supported");
+ //Check input parameters nonetheless
+ if( reg_ch > 23 || reg_cl > 59 || reg_dh > 59 || reg_dl > 99 )
+ reg_al = 0xff;
+ else { //Allow time to be set to zero. Restore the orginal time for all other parameters. (QuickBasic)
+ if (reg_cx == 0 && reg_dx == 0) {time_start = mem_readd(BIOS_TIMER);LOG_MSG("Warning: game messes with DOS time!");}
+ else time_start = 0;
+ reg_al = 0;
+ }
+ break;
+ case 0x2e: /* Set Verify flag */
+ dos.verify=(reg_al==1);
+ break;
+ case 0x2f: /* Get Disk Transfer Area */
+ SegSet16(es,RealSeg(dos.dta()));
+ reg_bx=RealOff(dos.dta());
+ break;
+ case 0x30: /* Get DOS Version */
+ if (reg_al==0) reg_bh=0xFF; /* Fake Microsoft DOS */
+ if (reg_al==1) reg_bh=0x10; /* DOS is in HMA */
+ reg_al=dos.version.major;
+ reg_ah=dos.version.minor;
+ /* Serialnumber */
+ reg_bl=0x00;
+ reg_cx=0x0000;
+ break;
+ case 0x31: /* Terminate and stay resident */
+ // Important: This service does not set the carry flag!
+ DOS_ResizeMemory(dos.psp(),&reg_dx);
+ DOS_Terminate(dos.psp(),true,reg_al);
+ break;
+ case 0x1f: /* Get drive parameter block for default drive */
+ case 0x32: /* Get drive parameter block for specific drive */
+ { /* Officially a dpb should be returned as well. The disk detection part is implemented */
+ Bit8u drive=reg_dl;
+ if (!drive || reg_ah==0x1f) drive = DOS_GetDefaultDrive();
+ else drive--;
+ if (drive < DOS_DRIVES && Drives[drive] && !Drives[drive]->isRemovable()) {
+ reg_al = 0x00;
+ SegSet16(ds,dos.tables.dpb);
+ reg_bx = drive*9;
+ LOG(LOG_DOSMISC,LOG_ERROR)("Get drive parameter block.");
+ } else {
+ reg_al=0xff;
+ }
+ }
+ break;
+ case 0x33: /* Extended Break Checking */
+ switch (reg_al) {
+ case 0:reg_dl=dos.breakcheck;break; /* Get the breakcheck flag */
+ case 1:dos.breakcheck=(reg_dl>0);break; /* Set the breakcheck flag */
+ case 2:{bool old=dos.breakcheck;dos.breakcheck=(reg_dl>0);reg_dl=old;}break;
+ case 3: /* Get cpsw */
+ /* Fallthrough */
+ case 4: /* Set cpsw */
+ LOG(LOG_DOSMISC,LOG_ERROR)("Someone playing with cpsw %x",reg_ax);
+ break;
+ case 5:reg_dl=3;break;//TODO should be z /* Always boot from c: :) */
+ case 6: /* Get true version number */
+ reg_bl=dos.version.major;
+ reg_bh=dos.version.minor;
+ reg_dl=dos.version.revision;
+ reg_dh=0x10; /* Dos in HMA */
+ break;
+ default:
+ LOG(LOG_DOSMISC,LOG_ERROR)("Weird 0x33 call %2X",reg_al);
+ reg_al =0xff;
+ break;
+ }
+ break;
+ case 0x34: /* Get INDos Flag */
+ SegSet16(es,DOS_SDA_SEG);
+ reg_bx=DOS_SDA_OFS + 0x01;
+ break;
+ case 0x35: /* Get interrupt vector */
+ reg_bx=real_readw(0,((Bit16u)reg_al)*4);
+ SegSet16(es,real_readw(0,((Bit16u)reg_al)*4+2));
+ break;
+ case 0x36: /* Get Free Disk Space */
+ {
+ Bit16u bytes,clusters,free;
+ Bit8u sectors;
+ if (DOS_GetFreeDiskSpace(reg_dl,&bytes,&sectors,&clusters,&free)) {
+ reg_ax=sectors;
+ reg_bx=free;
+ reg_cx=bytes;
+ reg_dx=clusters;
+ } else {
+ Bit8u drive=reg_dl;
+ if (drive==0) drive=DOS_GetDefaultDrive();
+ else drive--;
+ if (drive<2) {
+ // floppy drive, non-present drivesdisks issue floppy check through int24
+ // (critical error handler); needed for Mixed up Mother Goose (hook)
+// CALLBACK_RunRealInt(0x24);
+ }
+ reg_ax=0xffff; // invalid drive specified
+ }
+ }
+ break;
+ case 0x37: /* Get/Set Switch char Get/Set Availdev thing */
+//TODO Give errors for these functions to see if anyone actually uses this shit-
+ switch (reg_al) {
+ case 0:
+ reg_al=0;reg_dl=0x2f;break; /* always return '/' like dos 5.0+ */
+ case 1:
+ reg_al=0;break;
+ case 2:
+ reg_al=0;reg_dl=0x2f;break;
+ case 3:
+ reg_al=0;break;
+ };
+ LOG(LOG_MISC,LOG_ERROR)("DOS:0x37:Call for not supported switchchar");
+ break;
+ case 0x38: /* Set Country Code */
+ if (reg_al==0) { /* Get country specidic information */
+ PhysPt dest = SegPhys(ds)+reg_dx;
+ MEM_BlockWrite(dest,dos.tables.country,0x18);
+ reg_ax = reg_bx = 0x01;
+ CALLBACK_SCF(false);
+ break;
+ } else { /* Set country code */
+ LOG(LOG_MISC,LOG_ERROR)("DOS:Setting country code not supported");
+ }
+ CALLBACK_SCF(true);
+ break;
+ case 0x39: /* MKDIR Create directory */
+ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
+ if (DOS_MakeDir(name1)) {
+ reg_ax=0x05; /* ax destroyed */
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x3a: /* RMDIR Remove directory */
+ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
+ if (DOS_RemoveDir(name1)) {
+ reg_ax=0x05; /* ax destroyed */
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ LOG(LOG_MISC,LOG_NORMAL)("Remove dir failed on %s with error %X",name1,dos.errorcode);
+ }
+ break;
+ case 0x3b: /* CHDIR Set current directory */
+ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
+ if (DOS_ChangeDir(name1)) {
+ reg_ax=0x00; /* ax destroyed */
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x3c: /* CREATE Create of truncate file */
+ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
+ if (DOS_CreateFile(name1,reg_cx,&reg_ax)) {
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x3d: /* OPEN Open existing file */
+ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
+ if (DOS_OpenFile(name1,reg_al,&reg_ax)) {
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x3e: /* CLOSE Close file */
+ if (DOS_CloseFile(reg_bx)) {
+// reg_al=0x01; /* al destroyed. Refcount */
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x3f: /* READ Read from file or device */
+ {
+ Bit16u toread=reg_cx;
+ dos.echo=true;
+ if (DOS_ReadFile(reg_bx,dos_copybuf,&toread)) {
+ MEM_BlockWrite(SegPhys(ds)+reg_dx,dos_copybuf,toread);
+ reg_ax=toread;
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ modify_cycles(reg_ax);
+ dos.echo=false;
+ break;
+ }
+ case 0x40: /* WRITE Write to file or device */
+ {
+ Bit16u towrite=reg_cx;
+ MEM_BlockRead(SegPhys(ds)+reg_dx,dos_copybuf,towrite);
+ if (DOS_WriteFile(reg_bx,dos_copybuf,&towrite)) {
+ reg_ax=towrite;
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ modify_cycles(reg_ax);
+ break;
+ };
+ case 0x41: /* UNLINK Delete file */
+ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
+ if (DOS_UnlinkFile(name1)) {
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x42: /* LSEEK Set current file position */
+ {
+ Bit32u pos=(reg_cx<<16) + reg_dx;
+ if (DOS_SeekFile(reg_bx,&pos,reg_al)) {
+ reg_dx=(Bit16u)(pos >> 16);
+ reg_ax=(Bit16u)(pos & 0xFFFF);
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ }
+ case 0x43: /* Get/Set file attributes */
+ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
+ switch (reg_al) {
+ case 0x00: /* Get */
+ {
+ Bit16u attr_val=reg_cx;
+ if (DOS_GetFileAttr(name1,&attr_val)) {
+ reg_cx=attr_val;
+ reg_ax=attr_val; /* Undocumented */
+ CALLBACK_SCF(false);
+ } else {
+ CALLBACK_SCF(true);
+ reg_ax=dos.errorcode;
+ }
+ break;
+ };
+ case 0x01: /* Set */
+ LOG(LOG_MISC,LOG_ERROR)("DOS:Set File Attributes for %s not supported",name1);
+ if (DOS_SetFileAttr(name1,reg_cx)) {
+ reg_ax=0x202; /* ax destroyed */
+ CALLBACK_SCF(false);
+ } else {
+ CALLBACK_SCF(true);
+ reg_ax=dos.errorcode;
+ }
+ break;
+ default:
+ LOG(LOG_MISC,LOG_ERROR)("DOS:0x43:Illegal subfunction %2X",reg_al);
+ reg_ax=1;
+ CALLBACK_SCF(true);
+ break;
+ }
+ break;
+ case 0x44: /* IOCTL Functions */
+ if (DOS_IOCTL()) {
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x45: /* DUP Duplicate file handle */
+ if (DOS_DuplicateEntry(reg_bx,&reg_ax)) {
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x46: /* DUP2,FORCEDUP Force duplicate file handle */
+ if (DOS_ForceDuplicateEntry(reg_bx,reg_cx)) {
+ reg_ax=reg_cx; //Not all sources agree on it.
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x47: /* CWD Get current directory */
+ if (DOS_GetCurrentDir(reg_dl,name1)) {
+ MEM_BlockWrite(SegPhys(ds)+reg_si,name1,(Bitu)(strlen(name1)+1));
+ reg_ax=0x0100;
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x48: /* Allocate memory */
+ {
+ Bit16u size=reg_bx;Bit16u seg;
+ if (DOS_AllocateMemory(&seg,&size)) {
+ reg_ax=seg;
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ reg_bx=size;
+ CALLBACK_SCF(true);
+ }
+ break;
+ }
+ case 0x49: /* Free memory */
+ if (DOS_FreeMemory(SegValue(es))) {
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x4a: /* Resize memory block */
+ {
+ Bit16u size=reg_bx;
+ if (DOS_ResizeMemory(SegValue(es),&size)) {
+ reg_ax=SegValue(es);
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ reg_bx=size;
+ CALLBACK_SCF(true);
+ }
+ break;
+ }
+ case 0x4b: /* EXEC Load and/or execute program */
+ {
+ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
+ LOG(LOG_EXEC,LOG_ERROR)("Execute %s %d",name1,reg_al);
+ if (!DOS_Execute(name1,SegPhys(es)+reg_bx,reg_al)) {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ }
+ break;
+//TODO Check for use of execution state AL=5
+ case 0x4c: /* EXIT Terminate with return code */
+ DOS_Terminate(dos.psp(),false,reg_al);
+ break;
+ case 0x4d: /* Get Return code */
+ reg_al=dos.return_code;/* Officially read from SDA and clear when read */
+ reg_ah=dos.return_mode;
+ break;
+ case 0x4e: /* FINDFIRST Find first matching file */
+ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
+ if (DOS_FindFirst(name1,reg_cx)) {
+ CALLBACK_SCF(false);
+ reg_ax=0; /* Undocumented */
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ };
+ break;
+ case 0x4f: /* FINDNEXT Find next matching file */
+ if (DOS_FindNext()) {
+ CALLBACK_SCF(false);
+ /* reg_ax=0xffff;*/ /* Undocumented */
+ reg_ax=0; /* Undocumented:Qbix Willy beamish */
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ };
+ break;
+ case 0x50: /* Set current PSP */
+ dos.psp(reg_bx);
+ break;
+ case 0x51: /* Get current PSP */
+ reg_bx=dos.psp();
+ break;
+ case 0x52: { /* Get list of lists */
+ Bit8u count=2; // floppy drives always counted
+ while (count<DOS_DRIVES && Drives[count] && !Drives[count]->isRemovable()) count++;
+ dos_infoblock.SetBlockDevices(count);
+ RealPt addr=dos_infoblock.GetPointer();
+ SegSet16(es,RealSeg(addr));
+ reg_bx=RealOff(addr);
+ LOG(LOG_DOSMISC,LOG_NORMAL)("Call is made for list of lists - let's hope for the best");
+ break; }
+//TODO Think hard how shit this is gonna be
+//And will any game ever use this :)
+ case 0x53: /* Translate BIOS parameter block to drive parameter block */
+ E_Exit("Unhandled Dos 21 call %02X",reg_ah);
+ break;
+ case 0x54: /* Get verify flag */
+ reg_al=dos.verify?1:0;
+ break;
+ case 0x55: /* Create Child PSP*/
+ DOS_ChildPSP(reg_dx,reg_si);
+ dos.psp(reg_dx);
+ reg_al=0xf0; /* al destroyed */
+ break;
+ case 0x56: /* RENAME Rename file */
+ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
+ MEM_StrCopy(SegPhys(es)+reg_di,name2,DOSNAMEBUF);
+ if (DOS_Rename(name1,name2)) {
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x57: /* Get/Set File's Date and Time */
+ if (reg_al==0x00) {
+ if (DOS_GetFileDate(reg_bx,&reg_cx,&reg_dx)) {
+ CALLBACK_SCF(false);
+ } else {
+ CALLBACK_SCF(true);
+ }
+ } else if (reg_al==0x01) {
+ LOG(LOG_DOSMISC,LOG_ERROR)("DOS:57:Set File Date Time Faked");
+ CALLBACK_SCF(false);
+ } else {
+ LOG(LOG_DOSMISC,LOG_ERROR)("DOS:57:Unsupported subtion %X",reg_al);
+ }
+ break;
+ case 0x58: /* Get/Set Memory allocation strategy */
+ switch (reg_al) {
+ case 0: /* Get Strategy */
+ reg_ax=DOS_GetMemAllocStrategy();
+ break;
+ case 1: /* Set Strategy */
+ if (DOS_SetMemAllocStrategy(reg_bx)) CALLBACK_SCF(false);
+ else {
+ reg_ax=1;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 2: /* Get UMB Link Status */
+ reg_al=dos_infoblock.GetUMBChainState()&1;
+ CALLBACK_SCF(false);
+ break;
+ case 3: /* Set UMB Link Status */
+ if (DOS_LinkUMBsToMemChain(reg_bx)) CALLBACK_SCF(false);
+ else {
+ reg_ax=1;
+ CALLBACK_SCF(true);
+ }
+ break;
+ default:
+ LOG(LOG_DOSMISC,LOG_ERROR)("DOS:58:Not Supported Set//Get memory allocation call %X",reg_al);
+ reg_ax=1;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x59: /* Get Extended error information */
+ reg_ax=dos.errorcode;
+ if (dos.errorcode==DOSERR_FILE_NOT_FOUND || dos.errorcode==DOSERR_PATH_NOT_FOUND) {
+ reg_bh=8; //Not Found error class (Road Hog)
+ } else {
+ reg_bh=0; //Unspecified error class
+ }
+ reg_bl=1; //Retry retry retry
+ reg_ch=0; //Unkown error locus
+ break;
+ case 0x5a: /* Create temporary file */
+ {
+ Bit16u handle;
+ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
+ if (DOS_CreateTempFile(name1,&handle)) {
+ reg_ax=handle;
+ MEM_BlockWrite(SegPhys(ds)+reg_dx,name1,(Bitu)(strlen(name1)+1));
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ }
+ break;
+ case 0x5b: /* Create new file */
+ {
+ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF);
+ Bit16u handle;
+ if (DOS_OpenFile(name1,0,&handle)) {
+ DOS_CloseFile(handle);
+ DOS_SetError(DOSERR_FILE_ALREADY_EXISTS);
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ break;
+ }
+ if (DOS_CreateFile(name1,reg_cx,&handle)) {
+ reg_ax=handle;
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ }
+ case 0x5c: /* FLOCK File region locking */
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ reg_ax = dos.errorcode;
+ CALLBACK_SCF(true);
+ break;
+ case 0x5d: /* Network Functions */
+ if(reg_al == 0x06) {
+ SegSet16(ds,DOS_SDA_SEG);
+ reg_si = DOS_SDA_OFS;
+ reg_cx = 0x80; // swap if in dos
+ reg_dx = 0x1a; // swap always
+ LOG(LOG_DOSMISC,LOG_ERROR)("Get SDA, Let's hope for the best!");
+ }
+ break;
+ case 0x5f: /* Network redirection */
+ reg_ax=0x0001; //Failing it
+ CALLBACK_SCF(true);
+ break;
+ case 0x60: /* Canonicalize filename or path */
+ MEM_StrCopy(SegPhys(ds)+reg_si,name1,DOSNAMEBUF);
+ if (DOS_Canonicalize(name1,name2)) {
+ MEM_BlockWrite(SegPhys(es)+reg_di,name2,(Bitu)(strlen(name2)+1));
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x62: /* Get Current PSP Address */
+ reg_bx=dos.psp();
+ break;
+ case 0x63: /* DOUBLE BYTE CHARACTER SET */
+ if(reg_al == 0) {
+ SegSet16(ds,RealSeg(dos.tables.dbcs));
+ reg_si=RealOff(dos.tables.dbcs);
+ reg_al = 0;
+ CALLBACK_SCF(false); //undocumented
+ } else reg_al = 0xff; //Doesn't officially touch carry flag
+ break;
+ case 0x64: /* Set device driver lookahead flag */
+ LOG(LOG_DOSMISC,LOG_NORMAL)("set driver look ahead flag");
+ break;
+ case 0x65: /* Get extented country information and a lot of other useless shit*/
+ { /* Todo maybe fully support this for now we set it standard for USA */
+ LOG(LOG_DOSMISC,LOG_ERROR)("DOS:65:Extended country information call %X",reg_ax);
+ if((reg_al <= 0x07) && (reg_cx < 0x05)) {
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ CALLBACK_SCF(true);
+ break;
+ }
+ Bitu len = 0; /* For 0x21 and 0x22 */
+ PhysPt data=SegPhys(es)+reg_di;
+ switch (reg_al) {
+ case 0x01:
+ mem_writeb(data + 0x00,reg_al);
+ mem_writew(data + 0x01,0x26);
+ mem_writew(data + 0x03,1);
+ if(reg_cx > 0x06 ) mem_writew(data+0x05,dos.loaded_codepage);
+ if(reg_cx > 0x08 ) {
+ Bitu amount = (reg_cx>=0x29)?0x22:(reg_cx-7);
+ MEM_BlockWrite(data + 0x07,dos.tables.country,amount);
+ reg_cx=(reg_cx>=0x29)?0x29:reg_cx;
+ }
+ CALLBACK_SCF(false);
+ break;
+ case 0x05: // Get pointer to filename terminator table
+ mem_writeb(data + 0x00, reg_al);
+ mem_writed(data + 0x01, dos.tables.filenamechar);
+ reg_cx = 5;
+ CALLBACK_SCF(false);
+ break;
+ case 0x02: // Get pointer to uppercase table
+ mem_writeb(data + 0x00, reg_al);
+ mem_writed(data + 0x01, dos.tables.upcase);
+ reg_cx = 5;
+ CALLBACK_SCF(false);
+ break;
+ case 0x06: // Get pointer to collating sequence table
+ mem_writeb(data + 0x00, reg_al);
+ mem_writed(data + 0x01, dos.tables.collatingseq);
+ reg_cx = 5;
+ CALLBACK_SCF(false);
+ break;
+ case 0x03: // Get pointer to lowercase table
+ case 0x04: // Get pointer to filename uppercase table
+ case 0x07: // Get pointer to double byte char set table
+ mem_writeb(data + 0x00, reg_al);
+ mem_writed(data + 0x01, dos.tables.dbcs); //used to be 0
+ reg_cx = 5;
+ CALLBACK_SCF(false);
+ break;
+ case 0x20: /* Capitalize Character */
+ {
+ int in = reg_dl;
+ int out = toupper(in);
+ reg_dl = (Bit8u)out;
+ }
+ CALLBACK_SCF(false);
+ break;
+ case 0x21: /* Capitalize String (cx=length) */
+ case 0x22: /* Capatilize ASCIZ string */
+ data = SegPhys(ds) + reg_dx;
+ if(reg_al == 0x21) len = reg_cx;
+ else len = mem_strlen(data); /* Is limited to 1024 */
+
+ if(len > DOS_COPYBUFSIZE - 1) E_Exit("DOS:0x65 Buffer overflow");
+ if(len) {
+ MEM_BlockRead(data,dos_copybuf,len);
+ dos_copybuf[len] = 0;
+ //No upcase as String(0x21) might be multiple asciz strings
+ for (Bitu count = 0; count < len;count++)
+ dos_copybuf[count] = (Bit8u)toupper(*reinterpret_cast<unsigned char*>(dos_copybuf+count));
+ MEM_BlockWrite(data,dos_copybuf,len);
+ }
+ CALLBACK_SCF(false);
+ break;
+ default:
+ E_Exit("DOS:0x65:Unhandled country information call %2X",reg_al);
+ };
+ break;
+ }
+ case 0x66: /* Get/Set global code page table */
+ if (reg_al==1) {
+ LOG(LOG_DOSMISC,LOG_ERROR)("Getting global code page table");
+ reg_bx=reg_dx=dos.loaded_codepage;
+ CALLBACK_SCF(false);
+ break;
+ }
+ LOG(LOG_DOSMISC,LOG_NORMAL)("DOS:Setting code page table is not supported");
+ break;
+ case 0x67: /* Set handle count */
+ /* Weird call to increase amount of file handles needs to allocate memory if >20 */
+ {
+ DOS_PSP psp(dos.psp());
+ psp.SetNumFiles(reg_bx);
+ CALLBACK_SCF(false);
+ break;
+ };
+ case 0x68: /* FFLUSH Commit file */
+ if(DOS_FlushFile(reg_bl)) {
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax = dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x69: /* Get/Set disk serial number */
+ {
+ Bit16u old_cx=reg_cx;
+ switch(reg_al) {
+ case 0x00: /* Get */
+ LOG(LOG_DOSMISC,LOG_WARN)("DOS:Get Disk serial number");
+ reg_cl=0x66;// IOCTL function
+ break;
+ case 0x01: /* Set */
+ LOG(LOG_DOSMISC,LOG_WARN)("DOS:Set Disk serial number");
+ reg_cl=0x46;// IOCTL function
+ break;
+ default:
+ E_Exit("DOS:Illegal Get Serial Number call %2X",reg_al);
+ }
+ reg_ch=0x08; // IOCTL category: disk drive
+ reg_ax=0x440d; // Generic block device request
+ DOS_21Handler();
+ reg_cx=old_cx;
+ break;
+ }
+ case 0x6c: /* Extended Open/Create */
+ MEM_StrCopy(SegPhys(ds)+reg_si,name1,DOSNAMEBUF);
+ if (DOS_OpenFileExtended(name1,reg_bx,reg_cx,reg_dx,&reg_ax,&reg_cx)) {
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax=dos.errorcode;
+ CALLBACK_SCF(true);
+ }
+ break;
+
+ case 0x71: /* Unknown probably 4dos detection */
+ reg_ax=0x7100;
+ CALLBACK_SCF(true); //Check this! What needs this ? See default case
+ LOG(LOG_DOSMISC,LOG_NORMAL)("DOS:Windows long file name support call %2X",reg_al);
+ break;
+
+ case 0xE0:
+ case 0x18: /* NULL Function for CP/M compatibility or Extended rename FCB */
+ case 0x1d: /* NULL Function for CP/M compatibility or Extended rename FCB */
+ case 0x1e: /* NULL Function for CP/M compatibility or Extended rename FCB */
+ case 0x20: /* NULL Function for CP/M compatibility or Extended rename FCB */
+ case 0x6b: /* NULL Function */
+ case 0x61: /* UNUSED */
+ case 0xEF: /* Used in Ancient Art Of War CGA */
+ case 0x5e: /* More Network Functions */
+ default:
+ if (reg_ah < 0x6d) LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Unhandled call %02X al=%02X. Set al to default of 0",reg_ah,reg_al); //Less errors. above 0x6c the functions are simply always skipped, only al is zeroed, all other registers untouched
+ reg_al=0x00; /* default value */
+ break;
+ };
+ return CBRET_NONE;
+}
+
+
+static Bitu DOS_20Handler(void) {
+ reg_ah=0x00;
+ DOS_21Handler();
+ return CBRET_NONE;
+}
+
+static Bitu DOS_27Handler(void) {
+ // Terminate & stay resident
+ Bit16u para = (reg_dx/16)+((reg_dx % 16)>0);
+ Bit16u psp = dos.psp(); //mem_readw(SegPhys(ss)+reg_sp+2);
+ if (DOS_ResizeMemory(psp,&para)) DOS_Terminate(psp,true,0);
+ return CBRET_NONE;
+}
+
+static Bitu DOS_25Handler(void) {
+ if (reg_al >= DOS_DRIVES || !Drives[reg_al] || Drives[reg_al]->isRemovable()) {
+ reg_ax = 0x8002;
+ SETFLAGBIT(CF,true);
+ } else {
+ if (reg_cx == 1 && reg_dx == 0) {
+ if (reg_al >= 2) {
+ PhysPt ptr = PhysMake(SegValue(ds),reg_bx);
+ // write some BPB data into buffer for MicroProse installers
+ mem_writew(ptr+0x1c,0x3f); // hidden sectors
+ }
+ } else {
+ LOG(LOG_DOSMISC,LOG_NORMAL)("int 25 called but not as disk detection drive %u",reg_al);
+ }
+ SETFLAGBIT(CF,false);
+ reg_ax = 0;
+ }
+ return CBRET_NONE;
+}
+static Bitu DOS_26Handler(void) {
+ LOG(LOG_DOSMISC,LOG_NORMAL)("int 26 called: hope for the best!");
+ if (reg_al >= DOS_DRIVES || !Drives[reg_al] || Drives[reg_al]->isRemovable()) {
+ reg_ax = 0x8002;
+ SETFLAGBIT(CF,true);
+ } else {
+ SETFLAGBIT(CF,false);
+ reg_ax = 0;
+ }
+ return CBRET_NONE;
+}
+
+
+class DOS:public Module_base{
+private:
+ CALLBACK_HandlerObject callback[7];
+public:
+ DOS(Section* configuration):Module_base(configuration){
+ callback[0].Install(DOS_20Handler,CB_IRET,"DOS Int 20");
+ callback[0].Set_RealVec(0x20);
+
+ callback[1].Install(DOS_21Handler,CB_INT21,"DOS Int 21");
+ callback[1].Set_RealVec(0x21);
+ //Pseudo code for int 21
+ // sti
+ // callback
+ // iret
+ // retf <- int 21 4c jumps here to mimic a retf Cyber
+
+ callback[2].Install(DOS_25Handler,CB_RETF_STI,"DOS Int 25");
+ callback[2].Set_RealVec(0x25);
+
+ callback[3].Install(DOS_26Handler,CB_RETF_STI,"DOS Int 26");
+ callback[3].Set_RealVec(0x26);
+
+ callback[4].Install(DOS_27Handler,CB_IRET,"DOS Int 27");
+ callback[4].Set_RealVec(0x27);
+
+ callback[5].Install(NULL,CB_IRET,"DOS Int 28");
+ callback[5].Set_RealVec(0x28);
+
+ callback[6].Install(NULL,CB_INT29,"CON Output Int 29");
+ callback[6].Set_RealVec(0x29);
+ // pseudocode for CB_INT29:
+ // push ax
+ // mov ah, 0x0e
+ // int 0x10
+ // pop ax
+ // iret
+
+ DOS_SetupFiles(); /* Setup system File tables */
+ DOS_SetupDevices(); /* Setup dos devices */
+ DOS_SetupTables();
+ DOS_SetupMemory(); /* Setup first MCB */
+ DOS_SetupPrograms();
+ DOS_SetupMisc(); /* Some additional dos interrupts */
+ DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDrive(25); /* Else the next call gives a warning. */
+ DOS_SetDefaultDrive(25);
+
+ dos.version.major=5;
+ dos.version.minor=0;
+ dos.direct_output=false;
+ dos.internal_output=false;
+ }
+ ~DOS(){
+ for (Bit16u i=0;i<DOS_DRIVES;i++) delete Drives[i];
+ }
+};
+
+static DOS* test;
+
+void DOS_ShutDown(Section* /*sec*/) {
+ delete test;
+}
+
+void DOS_Init(Section* sec) {
+ test = new DOS(sec);
+ /* shutdown function */
+ sec->AddDestroyFunction(&DOS_ShutDown,false);
+}
diff --git a/src/dos/dos_classes.cpp b/src/dos/dos_classes.cpp
new file mode 100644
index 000000000..fd45eda4b
--- /dev/null
+++ b/src/dos/dos_classes.cpp
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <stdlib.h>
+#include "dosbox.h"
+#include "mem.h"
+#include "dos_inc.h"
+#include "support.h"
+
+
+void DOS_ParamBlock::Clear(void) {
+ memset(&exec,0,sizeof(exec));
+ memset(&overlay,0,sizeof(overlay));
+}
+
+void DOS_ParamBlock::LoadData(void) {
+ exec.envseg=(Bit16u)sGet(sExec,envseg);
+ exec.cmdtail=sGet(sExec,cmdtail);
+ exec.fcb1=sGet(sExec,fcb1);
+ exec.fcb2=sGet(sExec,fcb2);
+ exec.initsssp=sGet(sExec,initsssp);
+ exec.initcsip=sGet(sExec,initcsip);
+ overlay.loadseg=(Bit16u)sGet(sOverlay,loadseg);
+ overlay.relocation=(Bit16u)sGet(sOverlay,relocation);
+}
+
+void DOS_ParamBlock::SaveData(void) {
+ sSave(sExec,envseg,exec.envseg);
+ sSave(sExec,cmdtail,exec.cmdtail);
+ sSave(sExec,fcb1,exec.fcb1);
+ sSave(sExec,fcb2,exec.fcb2);
+ sSave(sExec,initsssp,exec.initsssp);
+ sSave(sExec,initcsip,exec.initcsip);
+}
+
+
+void DOS_InfoBlock::SetLocation(Bit16u segment) {
+ seg = segment;
+ pt=PhysMake(seg,0);
+ /* Clear the initial Block */
+ for(Bitu i=0;i<sizeof(sDIB);i++) mem_writeb(pt+i,0xff);
+ for(Bitu i=0;i<14;i++) mem_writeb(pt+i,0);
+
+ sSave(sDIB,regCXfrom5e,(Bit16u)0);
+ sSave(sDIB,countLRUcache,(Bit16u)0);
+ sSave(sDIB,countLRUopens,(Bit16u)0);
+
+ sSave(sDIB,protFCBs,(Bit16u)0);
+ sSave(sDIB,specialCodeSeg,(Bit16u)0);
+ sSave(sDIB,joindedDrives,(Bit8u)0);
+ sSave(sDIB,lastdrive,(Bit8u)0x01);//increase this if you add drives to cds-chain
+
+ sSave(sDIB,diskInfoBuffer,RealMake(segment,offsetof(sDIB,diskBufferHeadPt)));
+ sSave(sDIB,setverPtr,(Bit32u)0);
+
+ sSave(sDIB,a20FixOfs,(Bit16u)0);
+ sSave(sDIB,pspLastIfHMA,(Bit16u)0);
+ sSave(sDIB,blockDevices,(Bit8u)0);
+
+ sSave(sDIB,bootDrive,(Bit8u)0);
+ sSave(sDIB,useDwordMov,(Bit8u)1);
+ sSave(sDIB,extendedSize,(Bit16u)(MEM_TotalPages()*4-1024));
+ sSave(sDIB,magicWord,(Bit16u)0x0001); // dos5+
+
+ sSave(sDIB,sharingCount,(Bit16u)0);
+ sSave(sDIB,sharingDelay,(Bit16u)0);
+ sSave(sDIB,ptrCONinput,(Bit16u)0); // no unread input available
+ sSave(sDIB,maxSectorLength,(Bit16u)0x200);
+
+ sSave(sDIB,dirtyDiskBuffers,(Bit16u)0);
+ sSave(sDIB,lookaheadBufPt,(Bit32u)0);
+ sSave(sDIB,lookaheadBufNumber,(Bit16u)0);
+ sSave(sDIB,bufferLocation,(Bit8u)0); // buffer in base memory, no workspace
+ sSave(sDIB,workspaceBuffer,(Bit32u)0);
+
+ sSave(sDIB,minMemForExec,(Bit16u)0);
+ sSave(sDIB,memAllocScanStart,(Bit16u)DOS_MEM_START);
+ sSave(sDIB,startOfUMBChain,(Bit16u)0xffff);
+ sSave(sDIB,chainingUMB,(Bit8u)0);
+
+ sSave(sDIB,nulNextDriver,(Bit32u)0xffffffff);
+ sSave(sDIB,nulAttributes,(Bit16u)0x8004);
+ sSave(sDIB,nulStrategy,(Bit32u)0x00000000);
+ sSave(sDIB,nulString[0],(Bit8u)0x4e);
+ sSave(sDIB,nulString[1],(Bit8u)0x55);
+ sSave(sDIB,nulString[2],(Bit8u)0x4c);
+ sSave(sDIB,nulString[3],(Bit8u)0x20);
+ sSave(sDIB,nulString[4],(Bit8u)0x20);
+ sSave(sDIB,nulString[5],(Bit8u)0x20);
+ sSave(sDIB,nulString[6],(Bit8u)0x20);
+ sSave(sDIB,nulString[7],(Bit8u)0x20);
+
+ /* Create a fake SFT, so programs think there are 100 file handles */
+ Bit16u sftOffset=offsetof(sDIB,firstFileTable)+0xa2;
+ sSave(sDIB,firstFileTable,RealMake(segment,sftOffset));
+ real_writed(segment,sftOffset+0x00,RealMake(segment+0x26,0)); //Next File Table
+ real_writew(segment,sftOffset+0x04,100); //File Table supports 100 files
+ real_writed(segment+0x26,0x00,0xffffffff); //Last File Table
+ real_writew(segment+0x26,0x04,100); //File Table supports 100 files
+}
+
+void DOS_InfoBlock::SetFirstMCB(Bit16u _firstmcb) {
+ sSave(sDIB,firstMCB,_firstmcb); //c2woody
+}
+
+void DOS_InfoBlock::SetBuffers(Bit16u x,Bit16u y) {
+ sSave(sDIB,buffers_x,x);
+ sSave(sDIB,buffers_y,y);
+}
+
+void DOS_InfoBlock::SetCurDirStruct(Bit32u _curdirstruct) {
+ sSave(sDIB,curDirStructure,_curdirstruct);
+}
+
+void DOS_InfoBlock::SetFCBTable(Bit32u _fcbtable) {
+ sSave(sDIB,fcbTable,_fcbtable);
+}
+
+void DOS_InfoBlock::SetDeviceChainStart(Bit32u _devchain) {
+ sSave(sDIB,nulNextDriver,_devchain);
+}
+
+void DOS_InfoBlock::SetDiskBufferHeadPt(Bit32u _dbheadpt) {
+ sSave(sDIB,diskBufferHeadPt,_dbheadpt);
+}
+
+Bit16u DOS_InfoBlock::GetStartOfUMBChain(void) {
+ return (Bit16u)sGet(sDIB,startOfUMBChain);
+}
+
+void DOS_InfoBlock::SetStartOfUMBChain(Bit16u _umbstartseg) {
+ sSave(sDIB,startOfUMBChain,_umbstartseg);
+}
+
+Bit8u DOS_InfoBlock::GetUMBChainState(void) {
+ return (Bit8u)sGet(sDIB,chainingUMB);
+}
+
+void DOS_InfoBlock::SetUMBChainState(Bit8u _umbchaining) {
+ sSave(sDIB,chainingUMB,_umbchaining);
+}
+
+void DOS_InfoBlock::SetBlockDevices(Bit8u _count) {
+ sSave(sDIB,blockDevices,_count);
+}
+
+RealPt DOS_InfoBlock::GetPointer(void) {
+ return RealMake(seg,offsetof(sDIB,firstDPB));
+}
+
+Bit32u DOS_InfoBlock::GetDeviceChain(void) {
+ return sGet(sDIB,nulNextDriver);
+}
+
+
+/* program Segment prefix */
+
+Bit16u DOS_PSP::rootpsp = 0;
+
+void DOS_PSP::MakeNew(Bit16u mem_size) {
+ /* get previous */
+// DOS_PSP prevpsp(dos.psp());
+ /* Clear it first */
+ Bitu i;
+ for (i=0;i<sizeof(sPSP);i++) mem_writeb(pt+i,0);
+ // Set size
+ sSave(sPSP,next_seg,seg+mem_size);
+ /* far call opcode */
+ sSave(sPSP,far_call,0xea);
+ // far call to interrupt 0x21 - faked for bill & ted
+ // lets hope nobody really uses this address
+ sSave(sPSP,cpm_entry,RealMake(0xDEAD,0xFFFF));
+ /* Standard blocks,int 20 and int21 retf */
+ sSave(sPSP,exit[0],0xcd);
+ sSave(sPSP,exit[1],0x20);
+ sSave(sPSP,service[0],0xcd);
+ sSave(sPSP,service[1],0x21);
+ sSave(sPSP,service[2],0xcb);
+ /* psp and psp-parent */
+ sSave(sPSP,psp_parent,dos.psp());
+ sSave(sPSP,prev_psp,0xffffffff);
+ sSave(sPSP,dos_version,0x0005);
+ /* terminate 22,break 23,crititcal error 24 address stored */
+ SaveVectors();
+
+ /* FCBs are filled with 0 */
+ // ....
+ /* Init file pointer and max_files */
+ sSave(sPSP,file_table,RealMake(seg,offsetof(sPSP,files)));
+ sSave(sPSP,max_files,20);
+ for (Bit16u ct=0;ct<20;ct++) SetFileHandle(ct,0xff);
+
+ /* User Stack pointer */
+// if (prevpsp.GetSegment()!=0) sSave(sPSP,stack,prevpsp.GetStack());
+
+ if (rootpsp==0) rootpsp = seg;
+}
+
+Bit8u DOS_PSP::GetFileHandle(Bit16u index) {
+ if (index>=sGet(sPSP,max_files)) return 0xff;
+ PhysPt files=Real2Phys(sGet(sPSP,file_table));
+ return mem_readb(files+index);
+}
+
+void DOS_PSP::SetFileHandle(Bit16u index, Bit8u handle) {
+ if (index<sGet(sPSP,max_files)) {
+ PhysPt files=Real2Phys(sGet(sPSP,file_table));
+ mem_writeb(files+index,handle);
+ }
+}
+
+Bit16u DOS_PSP::FindFreeFileEntry(void) {
+ PhysPt files=Real2Phys(sGet(sPSP,file_table));
+ for (Bit16u i=0;i<sGet(sPSP,max_files);i++) {
+ if (mem_readb(files+i)==0xff) return i;
+ }
+ return 0xff;
+}
+
+Bit16u DOS_PSP::FindEntryByHandle(Bit8u handle) {
+ PhysPt files=Real2Phys(sGet(sPSP,file_table));
+ for (Bit16u i=0;i<sGet(sPSP,max_files);i++) {
+ if (mem_readb(files+i)==handle) return i;
+ }
+ return 0xFF;
+}
+
+void DOS_PSP::CopyFileTable(DOS_PSP* srcpsp,bool createchildpsp) {
+ /* Copy file table from calling process */
+ for (Bit16u i=0;i<20;i++) {
+ Bit8u handle = srcpsp->GetFileHandle(i);
+ if(createchildpsp)
+ { //copy obeying not inherit flag.(but dont duplicate them)
+ bool allowCopy = true;//(handle==0) || ((handle>0) && (FindEntryByHandle(handle)==0xff));
+ if((handle<DOS_FILES) && Files[handle] && !(Files[handle]->flags & DOS_NOT_INHERIT) && allowCopy)
+ {
+ Files[handle]->AddRef();
+ SetFileHandle(i,handle);
+ }
+ else
+ {
+ SetFileHandle(i,0xff);
+ }
+ }
+ else
+ { //normal copy so don't mind the inheritance
+ SetFileHandle(i,handle);
+ }
+ }
+}
+
+void DOS_PSP::CloseFiles(void) {
+ for (Bit16u i=0;i<sGet(sPSP,max_files);i++) {
+ DOS_CloseFile(i);
+ }
+}
+
+void DOS_PSP::SaveVectors(void) {
+ /* Save interrupt 22,23,24 */
+ sSave(sPSP,int_22,RealGetVec(0x22));
+ sSave(sPSP,int_23,RealGetVec(0x23));
+ sSave(sPSP,int_24,RealGetVec(0x24));
+}
+
+void DOS_PSP::RestoreVectors(void) {
+ /* Restore interrupt 22,23,24 */
+ RealSetVec(0x22,sGet(sPSP,int_22));
+ RealSetVec(0x23,sGet(sPSP,int_23));
+ RealSetVec(0x24,sGet(sPSP,int_24));
+}
+
+void DOS_PSP::SetCommandTail(RealPt src) {
+ if (src) { // valid source
+ MEM_BlockCopy(pt+offsetof(sPSP,cmdtail),Real2Phys(src),128);
+ } else { // empty
+ sSave(sPSP,cmdtail.count,0x00);
+ mem_writeb(pt+offsetof(sPSP,cmdtail.buffer),0x0d);
+ };
+}
+
+void DOS_PSP::SetFCB1(RealPt src) {
+ if (src) MEM_BlockCopy(PhysMake(seg,offsetof(sPSP,fcb1)),Real2Phys(src),16);
+}
+
+void DOS_PSP::SetFCB2(RealPt src) {
+ if (src) MEM_BlockCopy(PhysMake(seg,offsetof(sPSP,fcb2)),Real2Phys(src),16);
+}
+
+bool DOS_PSP::SetNumFiles(Bit16u fileNum) {
+ //20 minimum. clipper program.
+ if (fileNum < 20) fileNum = 20;
+
+ if (fileNum>20) {
+ // Allocate needed paragraphs
+ fileNum+=2; // Add a few more files for safety
+ Bit16u para = (fileNum/16)+((fileNum%16)>0);
+ RealPt data = RealMake(DOS_GetMemory(para),0);
+ sSave(sPSP,file_table,data);
+ sSave(sPSP,max_files,fileNum);
+ Bit16u i;
+ for (i=0; i<20; i++) SetFileHandle(i,(Bit8u)sGet(sPSP,files[i]));
+ for (i=20; i<fileNum; i++) SetFileHandle(i,0xFF);
+ } else {
+ sSave(sPSP,max_files,fileNum);
+ };
+ return true;
+}
+
+
+void DOS_DTA::SetupSearch(Bit8u _sdrive,Bit8u _sattr,char * pattern) {
+ sSave(sDTA,sdrive,_sdrive);
+ sSave(sDTA,sattr,_sattr);
+ /* Fill with spaces */
+ Bitu i;
+ for (i=0;i<11;i++) mem_writeb(pt+offsetof(sDTA,sname)+i,' ');
+ char * find_ext;
+ find_ext=strchr(pattern,'.');
+ if (find_ext) {
+ Bitu size=(Bitu)(find_ext-pattern);
+ if (size>8) size=8;
+ MEM_BlockWrite(pt+offsetof(sDTA,sname),pattern,size);
+ find_ext++;
+ MEM_BlockWrite(pt+offsetof(sDTA,sext),find_ext,(strlen(find_ext)>3) ? 3 : (Bitu)strlen(find_ext));
+ } else {
+ MEM_BlockWrite(pt+offsetof(sDTA,sname),pattern,(strlen(pattern) > 8) ? 8 : (Bitu)strlen(pattern));
+ }
+}
+
+void DOS_DTA::SetResult(const char * _name,Bit32u _size,Bit16u _date,Bit16u _time,Bit8u _attr) {
+ MEM_BlockWrite(pt+offsetof(sDTA,name),(void *)_name,strlen(_name)+1);
+ sSave(sDTA,size,_size);
+ sSave(sDTA,date,_date);
+ sSave(sDTA,time,_time);
+ sSave(sDTA,attr,_attr);
+}
+
+
+void DOS_DTA::GetResult(char * _name,Bit32u & _size,Bit16u & _date,Bit16u & _time,Bit8u & _attr) {
+ MEM_BlockRead(pt+offsetof(sDTA,name),_name,DOS_NAMELENGTH_ASCII);
+ _size=sGet(sDTA,size);
+ _date=(Bit16u)sGet(sDTA,date);
+ _time=(Bit16u)sGet(sDTA,time);
+ _attr=(Bit8u)sGet(sDTA,attr);
+}
+
+Bit8u DOS_DTA::GetSearchDrive(void) {
+ return (Bit8u)sGet(sDTA,sdrive);
+}
+
+void DOS_DTA::GetSearchParams(Bit8u & attr,char * pattern) {
+ attr=(Bit8u)sGet(sDTA,sattr);
+ char temp[11];
+ MEM_BlockRead(pt+offsetof(sDTA,sname),temp,11);
+ memcpy(pattern,temp,8);
+ pattern[8]='.';
+ memcpy(&pattern[9],&temp[8],3);
+ pattern[12]=0;
+
+}
+
+DOS_FCB::DOS_FCB(Bit16u seg,Bit16u off,bool allow_extended) {
+ SetPt(seg,off);
+ real_pt=pt;
+ extended=false;
+ if (allow_extended) {
+ if (sGet(sFCB,drive)==0xff) {
+ pt+=7;
+ extended=true;
+ }
+ }
+}
+
+bool DOS_FCB::Extended(void) {
+ return extended;
+}
+
+void DOS_FCB::Create(bool _extended) {
+ Bitu fill;
+ if (_extended) fill=33+7;
+ else fill=33;
+ Bitu i;
+ for (i=0;i<fill;i++) mem_writeb(real_pt+i,0);
+ pt=real_pt;
+ if (_extended) {
+ mem_writeb(real_pt,0xff);
+ pt+=7;
+ extended=true;
+ } else extended=false;
+}
+
+void DOS_FCB::SetName(Bit8u _drive,char * _fname,char * _ext) {
+ sSave(sFCB,drive,_drive);
+ MEM_BlockWrite(pt+offsetof(sFCB,filename),_fname,8);
+ MEM_BlockWrite(pt+offsetof(sFCB,ext),_ext,3);
+}
+
+void DOS_FCB::SetSizeDateTime(Bit32u _size,Bit16u _date,Bit16u _time) {
+ sSave(sFCB,filesize,_size);
+ sSave(sFCB,date,_date);
+ sSave(sFCB,time,_time);
+}
+
+void DOS_FCB::GetSizeDateTime(Bit32u & _size,Bit16u & _date,Bit16u & _time) {
+ _size=sGet(sFCB,filesize);
+ _date=(Bit16u)sGet(sFCB,date);
+ _time=(Bit16u)sGet(sFCB,time);
+}
+
+void DOS_FCB::GetRecord(Bit16u & _cur_block,Bit8u & _cur_rec) {
+ _cur_block=(Bit16u)sGet(sFCB,cur_block);
+ _cur_rec=(Bit8u)sGet(sFCB,cur_rec);
+
+}
+
+void DOS_FCB::SetRecord(Bit16u _cur_block,Bit8u _cur_rec) {
+ sSave(sFCB,cur_block,_cur_block);
+ sSave(sFCB,cur_rec,_cur_rec);
+}
+
+void DOS_FCB::GetSeqData(Bit8u & _fhandle,Bit16u & _rec_size) {
+ _fhandle=(Bit8u)sGet(sFCB,file_handle);
+ _rec_size=(Bit16u)sGet(sFCB,rec_size);
+}
+
+void DOS_FCB::SetSeqData(Bit8u _fhandle,Bit16u _rec_size) {
+ sSave(sFCB,file_handle,_fhandle);
+ sSave(sFCB,rec_size,_rec_size);
+}
+
+void DOS_FCB::GetRandom(Bit32u & _random) {
+ _random=sGet(sFCB,rndm);
+}
+
+void DOS_FCB::SetRandom(Bit32u _random) {
+ sSave(sFCB,rndm,_random);
+}
+
+void DOS_FCB::ClearBlockRecsize(void) {
+ sSave(sFCB,cur_block,0);
+ sSave(sFCB,rec_size,0);
+}
+void DOS_FCB::FileOpen(Bit8u _fhandle) {
+ sSave(sFCB,drive,GetDrive()+1);
+ sSave(sFCB,file_handle,_fhandle);
+ sSave(sFCB,cur_block,0);
+ sSave(sFCB,rec_size,128);
+// sSave(sFCB,rndm,0); // breaks Jewels of darkness.
+ Bit32u size = 0;
+ Files[_fhandle]->Seek(&size,DOS_SEEK_END);
+ sSave(sFCB,filesize,size);
+ size = 0;
+ Files[_fhandle]->Seek(&size,DOS_SEEK_SET);
+ sSave(sFCB,time,Files[_fhandle]->time);
+ sSave(sFCB,date,Files[_fhandle]->date);
+}
+
+bool DOS_FCB::Valid() {
+ //Very simple check for Oubliette
+ if(sGet(sFCB,filename[0]) == 0 && sGet(sFCB,file_handle) == 0) return false;
+ return true;
+}
+
+void DOS_FCB::FileClose(Bit8u & _fhandle) {
+ _fhandle=(Bit8u)sGet(sFCB,file_handle);
+ sSave(sFCB,file_handle,0xff);
+}
+
+Bit8u DOS_FCB::GetDrive(void) {
+ Bit8u drive=(Bit8u)sGet(sFCB,drive);
+ if (!drive) return DOS_GetDefaultDrive();
+ else return drive-1;
+}
+
+void DOS_FCB::GetName(char * fillname) {
+ fillname[0]=GetDrive()+'A';
+ fillname[1]=':';
+ MEM_BlockRead(pt+offsetof(sFCB,filename),&fillname[2],8);
+ fillname[10]='.';
+ MEM_BlockRead(pt+offsetof(sFCB,ext),&fillname[11],3);
+ fillname[14]=0;
+}
+
+void DOS_FCB::GetAttr(Bit8u& attr) {
+ if(extended) attr=mem_readb(pt - 1);
+}
+
+void DOS_FCB::SetAttr(Bit8u attr) {
+ if(extended) mem_writeb(pt - 1,attr);
+}
+
+void DOS_FCB::SetResult(Bit32u size,Bit16u date,Bit16u time,Bit8u attr) {
+ mem_writed(pt + 0x1d,size);
+ mem_writew(pt + 0x19,date);
+ mem_writew(pt + 0x17,time);
+ mem_writeb(pt + 0x0c,attr);
+}
+
+void DOS_SDA::Init() {
+ /* Clear */
+ for(Bitu i=0;i<sizeof(sSDA);i++) mem_writeb(pt+i,0x00);
+ sSave(sSDA,drive_crit_error,0xff);
+}
diff --git a/src/dos/dos_codepages.h b/src/dos/dos_codepages.h
new file mode 100644
index 000000000..d5b1bbed6
--- /dev/null
+++ b/src/dos/dos_codepages.h
@@ -0,0 +1,1131 @@
+/*
+ * Copyright (C) Henrique Peron
+ *
+ * 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 file contains data of .CPI/.CPX-files. They have been
+ generated by Henrique Peron */
+
+/* The font files have been created/edited with CPIadd V1.20
+ Copyright (C) 1993-1996 by kostis@acm.org (Kosta Kostis) */
+
+/* This file contains data of UPX-compressed CPI files.
+ UPX, The Ultimate Packer for eXecutables
+ Copyright (c) 1996-2002 Markus Oberhumer & Laszlo Molnar
+ http://upx.sourceforge.net */
+
+
+Bit8u font_ega_cpx[6322] = {
+0x81, 0xfc, 0xce, 0xe7, 0x77, 0x02, 0xcd, 0x20, 0xb9, 0xb2, 0x18, 0xbe, 0xb2, 0x19, 0xbf, 0x6e,
+0xe7, 0xbb, 0x00, 0x80, 0xfd, 0xf3, 0xa4, 0xfc, 0x87, 0xf7, 0x83, 0xee, 0xc6, 0x19, 0xed, 0x57,
+0x57, 0xe9, 0xed, 0xe5, 0x55, 0x50, 0x58, 0x21, 0x0b, 0x01, 0x04, 0x08, 0x6c, 0xfa, 0x36, 0x54,
+0x99, 0xe8, 0x0b, 0xa9, 0x00, 0xe6, 0x1a, 0x18, 0x06, 0x74, 0xbb, 0xfc, 0xff, 0x46, 0x4f, 0x4e,
+0x54, 0x20, 0x00, 0x00, 0x01, 0x6e, 0x39, 0x01, 0x17, 0x06, 0xfd, 0xfd, 0x06, 0x00, 0x1c, 0x00,
+0x4d, 0x26, 0x0e, 0x45, 0x47, 0x41, 0xc9, 0xcd, 0x1e, 0x20, 0xb5, 0x01, 0xfe, 0xd8, 0x35, 0x24,
+0x03, 0x00, 0x12, 0x26, 0x10, 0x08, 0x95, 0xc5, 0x0a, 0x00, 0x6d, 0xff, 0x7e, 0x81, 0xa5, 0x81,
+0x81, 0xbd, 0x99, 0x03, 0x7e, 0xfe, 0x83, 0x0f, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff,
+0xe6, 0x77, 0x00, 0x6c, 0xfe, 0x3e, 0xec, 0x7c, 0x38, 0x10, 0x30, 0x10, 0x38, 0x7c, 0xfd, 0xc0,
+0x0e, 0x18, 0x3c, 0x3c, 0xe7, 0xb2, 0xb6, 0x00, 0x18, 0x06, 0x0f, 0x65, 0x6e, 0x7e, 0x3b, 0x0f,
+0xb3, 0xb3, 0x22, 0x18, 0x09, 0xde, 0xb1, 0xff, 0x00, 0xe7, 0xc3, 0x5f, 0x3f, 0xf6, 0x00, 0x1f,
+0x3c, 0x66, 0x42, 0x42, 0x66, 0xff, 0x92, 0x3c, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xfe, 0x21,
+0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xde, 0xeb, 0x00, 0x78, 0x20, 0x2d, 0xb9, 0xe6, 0x00, 0x4f,
+0x61, 0xbb, 0x9f, 0x0f, 0x3f, 0x33, 0x3f, 0x30, 0x00, 0x70, 0xf0, 0xf9, 0xc8, 0xe0, 0x00, 0x7f,
+0x63, 0x7f, 0x63, 0x9e, 0xbd, 0x67, 0xe7, 0xe6, 0xc0, 0x7c, 0x18, 0xdb, 0x87, 0xdd, 0xa0, 0x3c,
+0xdb, 0x2e, 0x80, 0xbb, 0xfd, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0x2d, 0xc0, 0x80, 0xbf, 0xfe,
+0x1f, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x6b, 0x49, 0x2b, 0x5e,
+0x7c, 0x67, 0x3c, 0x00, 0x66, 0x98, 0x9b, 0x07, 0x5f, 0xe7, 0x36, 0xdb, 0x00, 0x7b, 0x1b, 0xff,
+0x9b, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0x06, 0xe4, 0xc6, 0x7c,
+0x00, 0xfe, 0x43, 0x66, 0x4f, 0x7e, 0x6f, 0x60, 0x0f, 0x00, 0xbd, 0x60, 0x0f, 0x00, 0xfe, 0x96,
+0x70, 0x11, 0x0c, 0xfe, 0x0c, 0x77, 0xc9, 0x0c, 0x00, 0x30, 0x60, 0xfe, 0x06, 0xe4, 0x60, 0x30,
+0x00, 0xc0, 0x61, 0x56, 0x5c, 0xa1, 0x76, 0x39, 0x28, 0x6c, 0x28, 0x0e, 0x50, 0x58, 0x9f, 0xb2,
+0xb7, 0xa0, 0x7c, 0x7d, 0x8a, 0x0e, 0x5b, 0x0e, 0x38, 0xaf, 0xbc, 0xc0, 0x00, 0x18, 0x3c, 0x66,
+0xaf, 0x8c, 0x8f, 0x21, 0xeb, 0xe9, 0x24, 0x20, 0x8b, 0xb9, 0x6c, 0x5e, 0x03, 0xad, 0xfe, 0x36,
+0xf1, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0x1e, 0xd6, 0xef, 0xc1, 0xc2, 0xc6, 0x26, 0xdf, 0xae,
+0x30, 0x60, 0xc6, 0x86, 0x2e, 0x14, 0x0e, 0x0c, 0x76, 0xdc, 0xc1, 0x1a, 0xaf, 0x76, 0xbd, 0x81,
+0x6d, 0x00, 0x60, 0x4e, 0x8b, 0xb9, 0x2b, 0x00, 0xe5, 0xb9, 0x59, 0x0f, 0x08, 0x96, 0x8d, 0x00,
+0x18, 0xe2, 0x15, 0x16, 0xde, 0xff, 0xe5, 0x84, 0x81, 0xa2, 0xed, 0x06, 0xb6, 0x14, 0x6c, 0xc0,
+0x31, 0xfe, 0xd7, 0x96, 0xad, 0x31, 0xf9, 0x15, 0xe2, 0x9f, 0xc0, 0x80, 0xf8, 0x30, 0xae, 0xd6,
+0xd6, 0x0e, 0x76, 0xb2, 0x27, 0x38, 0x78, 0x59, 0x10, 0x81, 0x2e, 0xf6, 0xe4, 0x7c, 0xc6, 0xc6,
+0xfe, 0x0f, 0xd8, 0x4e, 0x06, 0x3c, 0x02, 0x06, 0xfd, 0x43, 0xdf, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc,
+0xfe, 0x9c, 0xd0, 0xb0, 0x1e, 0x8d, 0xfe, 0x03, 0xd6, 0xfc, 0x1f, 0x38, 0x90, 0xbb, 0x60, 0x0f,
+0xc6, 0xc6, 0xc6, 0x66, 0xee, 0xfe, 0x3f, 0xf3, 0x73, 0xc3, 0x4f, 0x1a, 0x1f, 0x7d, 0x01, 0x0b,
+0x7e, 0x0b, 0xdb, 0x3f, 0x0c, 0x78, 0xba, 0x0b, 0x7b, 0x04, 0x0f, 0x62, 0x93, 0x30, 0x9e, 0x92,
+0x1a, 0x35, 0x06, 0xc2, 0xc9, 0xde, 0x02, 0x00, 0xf6, 0x62, 0x1b, 0x23, 0x5f, 0x25, 0x4c, 0x0c,
+0xdf, 0xb6, 0xb3, 0x10, 0xde, 0x00, 0xdc, 0x61, 0x14, 0xc4, 0x2e, 0x3b, 0xdb, 0x6c, 0x90, 0xfe,
+0x0f, 0x6c, 0x3c, 0xfc, 0x66, 0xf1, 0x7c, 0x86, 0xf4, 0xfc, 0x6f, 0xd4, 0x15, 0xef, 0x00, 0xc2,
+0xf2, 0xe9, 0xa1, 0x10, 0xf8, 0x6c, 0x3f, 0x7b, 0x6c, 0xf8, 0x0f, 0xfe, 0x66, 0x62, 0x68, 0x84,
+0xfc, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0xc0, 0xb6, 0x00, 0xf0, 0x3f, 0x4d, 0x7e, 0xde, 0xc6,
+0xc6, 0x66, 0x3a, 0x61, 0xef, 0x69, 0x6e, 0x6f, 0x6b, 0xc2, 0x3c, 0x7d, 0x30, 0x31, 0x26, 0x1e,
+0x0d, 0x77, 0x49, 0xef, 0xe6, 0x6a, 0xb3, 0x6e, 0x78, 0x78, 0x74, 0xe6, 0x0f, 0xb3, 0xd8, 0xf0,
+0x60, 0x00, 0x6f, 0x92, 0x7f, 0x4f, 0xee, 0xfe, 0xfe, 0xd6, 0xf2, 0xdb, 0x0f, 0xe6, 0xf6, 0xfe,
+0xde, 0xce, 0x79, 0x58, 0x6f, 0xc6, 0x9b, 0x2d, 0xdf, 0x41, 0x27, 0xec, 0xf0, 0x1f, 0xd6, 0xde,
+0x97, 0xe4, 0x7c, 0x0c, 0x0e, 0xe3, 0x29, 0xd9, 0xe6, 0x1f, 0xe2, 0xb2, 0x42, 0x50, 0x3f, 0x09,
+0x7b, 0x7e, 0x7e, 0x5a, 0xaf, 0x16, 0xb2, 0xc6, 0x5f, 0x0f, 0x25, 0x0f, 0x6c, 0x38, 0x10, 0x43,
+0xfe, 0xd6, 0xd6, 0xd6, 0xfe, 0xee, 0x6c, 0x6b, 0xa6, 0x6c, 0x8e, 0x61, 0x9f, 0x66, 0x0d, 0xce,
+0x0b, 0xcb, 0x4f, 0x1b, 0x86, 0x21, 0x1f, 0x7f, 0xc2, 0x16, 0xec, 0x3c, 0x30, 0x00, 0x1f, 0xfe,
+0x0a, 0xc1, 0x70, 0x38, 0x1c, 0x76, 0xb2, 0xaf, 0x3c, 0x0c, 0x00, 0x62, 0x0c, 0x3c, 0xdc, 0x8d,
+0x90, 0xe6, 0xec, 0x34, 0x6c, 0xff, 0x18, 0xe9, 0x03, 0x78, 0x0c, 0x7c, 0xaf, 0x16, 0x2e, 0xe0,
+0x19, 0x78, 0x66, 0x11, 0xe2, 0xcf, 0x1c, 0x13, 0x02, 0x00, 0xb9, 0xc3, 0xdf, 0x1c, 0x69, 0x2d,
+0x2b, 0x00, 0x2f, 0x80, 0x8c, 0x1f, 0xfe, 0xc6, 0xfb, 0x36, 0x32, 0x30, 0x78, 0xaf, 0xcf, 0xf0,
+0x5d, 0x76, 0x2e, 0xcc, 0xdf, 0xa1, 0x5f, 0xd2, 0x5f, 0x6c, 0x32, 0x9e, 0x76, 0xe6, 0xdc, 0x03,
+0x61, 0x38, 0x4f, 0x6b, 0x4b, 0x09, 0x00, 0xdb, 0x00, 0xcc, 0x10, 0x17, 0x2f, 0x71, 0xc6, 0x00,
+0xe6, 0x90, 0x9c, 0x2f, 0x38, 0x35, 0xba, 0x00, 0xec, 0x00, 0x4c, 0xd6, 0x00, 0xc6, 0x0f, 0xdc,
+0xcb, 0x66, 0xa1, 0x9f, 0x6c, 0x09, 0x9f, 0x1f, 0xf6, 0xb1, 0x04, 0xf0, 0x9f, 0x90, 0x16, 0xd2,
+0x19, 0xed, 0xdc, 0x9f, 0x1f, 0x4c, 0x60, 0xe4, 0x76, 0x09, 0xdf, 0x10, 0xd9, 0xb2, 0xdf, 0xfc,
+0xde, 0x36, 0x1c, 0x1f, 0x10, 0x5e, 0x3d, 0x0f, 0x82, 0x31, 0x00, 0x47, 0xc0, 0x0f, 0x00, 0xb7,
+0x84, 0x9a, 0x1b, 0x30, 0x53, 0x38, 0x75, 0x2f, 0xd4, 0xb5, 0x04, 0x0c, 0x03, 0x87, 0x52, 0xcc,
+0x4f, 0xcd, 0xd9, 0xfe, 0xef, 0x0e, 0x70, 0x64, 0x3d, 0x0e, 0xf7, 0x09, 0x83, 0x3f, 0xda, 0x7b,
+0x1b, 0x23, 0x24, 0x40, 0xf8, 0xc9, 0xdc, 0xdf, 0x8b, 0x34, 0xe2, 0x4f, 0x17, 0xd2, 0xcf, 0x18,
+0xd8, 0x59, 0x72, 0xcc, 0xbf, 0xe0, 0x4a, 0x39, 0xb8, 0x84, 0xcf, 0x3b, 0x6e, 0x61, 0x1f, 0x2f,
+0x7c, 0x60, 0x0f, 0xc0, 0x60, 0x30, 0xb6, 0x84, 0x2e, 0x38, 0x46, 0xc8, 0x2f, 0x3f, 0xde, 0x4b,
+0x6f, 0x4f, 0x5c, 0xc2, 0x5f, 0xfd, 0x57, 0xc2, 0x0f, 0xbd, 0x18, 0x32, 0x1f, 0x66, 0x5a, 0xc2,
+0x1f, 0x06, 0xde, 0xc2, 0x0f, 0x2f, 0x6b, 0xc0, 0x0f, 0xc6, 0xcf, 0x73, 0x0b, 0x8d, 0x0f, 0x72,
+0x09, 0xdc, 0xc7, 0xc8, 0xaf, 0x3f, 0x4e, 0xfe, 0x36, 0x36, 0x7e, 0xd8, 0xd8, 0x6e, 0x3e, 0x1e,
+0x3a, 0xdc, 0xfe, 0x0f, 0xce, 0x18, 0xc9, 0xff, 0x3f, 0x84, 0x5d, 0xc6, 0x0f, 0x65, 0xb3, 0xaf,
+0x0f, 0x08, 0x8d, 0xf6, 0x4f, 0x71, 0x58, 0x1f, 0xc2, 0x85, 0x1f, 0xc6, 0xef, 0x59, 0x07, 0xc6,
+0x9f, 0x65, 0x0d, 0xc6, 0x4f, 0x70, 0x22, 0x71, 0x37, 0xe1, 0x0d, 0xef, 0xd2, 0x70, 0xe0, 0x64,
+0x60, 0x03, 0xa1, 0x21, 0x9f, 0x0c, 0xc9, 0x38, 0x7e, 0x0f, 0xdb, 0xb7, 0xf8, 0x69, 0xf8, 0xc4,
+0xcc, 0xde, 0x05, 0xc6, 0xe5, 0x19, 0x1f, 0x0e, 0x1b, 0x70, 0x9a, 0x7e, 0xd8, 0x0b, 0xe1, 0x01,
+0xaf, 0x42, 0x6a, 0x84, 0x7f, 0xcb, 0xde, 0x0f, 0xcf, 0x21, 0x7b, 0x2f, 0xbf, 0x92, 0x1c, 0x62,
+0x5f, 0x04, 0xdc, 0x0c, 0x6f, 0x6e, 0x8d, 0x1c, 0x6c, 0x36, 0x0c, 0x0c, 0x8e, 0xd6, 0x5e, 0x0f,
+0x51, 0x0f, 0x98, 0x58, 0x07, 0x48, 0x5d, 0x11, 0xc0, 0x8f, 0x7a, 0x1c, 0xfe, 0x90, 0x34, 0x10,
+0x3c, 0xc1, 0xe0, 0xfd, 0xcc, 0xa6, 0x85, 0xe0, 0xa8, 0x6c, 0x0d, 0xac, 0x81, 0xdc, 0x14, 0x3e,
+0x0f, 0x60, 0xbf, 0x66, 0xce, 0x9a, 0x3f, 0x22, 0xe8, 0xb9, 0xf7, 0xbc, 0x63, 0xc9, 0x62, 0x85,
+0x36, 0xcb, 0xcc, 0x6c, 0xd8, 0x00, 0x6d, 0x76, 0x0d, 0x6c, 0xd8, 0x11, 0x44, 0xe4, 0x85, 0x01,
+0x55, 0xaa, 0x21, 0x2f, 0xdd, 0x77, 0x05, 0x76, 0x18, 0x00, 0x0b, 0xd9, 0xf8, 0x0d, 0x0f, 0xcf,
+0x26, 0x36, 0x00, 0xf6, 0x36, 0xb0, 0x20, 0xd0, 0x4b, 0xd8, 0x0f, 0x2f, 0xce, 0x23, 0xf6, 0x06,
+0x96, 0xc0, 0x00, 0x3d, 0x85, 0x6c, 0x1f, 0x25, 0xa3, 0x2f, 0xba, 0xc9, 0x82, 0x11, 0x0f, 0xac,
+0x61, 0x8f, 0x00, 0xc8, 0x86, 0xf8, 0xbf, 0x27, 0x6c, 0x1f, 0x2f, 0x18, 0x18, 0x19, 0x92, 0xff,
+0xff, 0x58, 0x23, 0x72, 0x59, 0x5b, 0x1f, 0x3f, 0x21, 0x1b, 0x2d, 0x86, 0x84, 0x1f, 0x0f, 0x37,
+0x7b, 0xc2, 0xbf, 0x37, 0x30, 0x3f, 0x6d, 0x52, 0x09, 0xbf, 0xbb, 0xb0, 0x1f, 0xf7, 0x5f, 0x83,
+0x2d, 0x6d, 0xf7, 0x2c, 0x64, 0x3f, 0x2f, 0x6c, 0x96, 0x1f, 0x2f, 0x6c, 0xb6, 0x3f, 0x2f, 0x0c,
+0xb6, 0xdd, 0xff, 0x2c, 0x24, 0x2f, 0x5f, 0xc8, 0x8e, 0xff, 0xdf, 0x42, 0xc2, 0x86, 0x61, 0xc9,
+0xaf, 0xdf, 0x97, 0xb0, 0x3f, 0xef, 0xb9, 0xb0, 0x3f, 0x3f, 0x64, 0x21, 0x4f, 0x10, 0x84, 0x2d,
+0x84, 0x91, 0x5f, 0xaf, 0x93, 0x8d, 0x4f, 0xff, 0x2c, 0x64, 0x00, 0x9f, 0x1c, 0x36, 0x00, 0xf0,
+0xc9, 0x90, 0x0f, 0x0b, 0x0b, 0x36, 0xf2, 0x61, 0x00, 0x76, 0xdc, 0xd8, 0x08, 0xd2, 0xdc, 0xcf,
+0xd3, 0x06, 0x5b, 0xd8, 0xcc, 0xbe, 0x86, 0x45, 0xcc, 0x7f, 0x48, 0x07, 0xad, 0x91, 0x9d, 0x4a,
+0xff, 0x35, 0xcc, 0x00, 0x1f, 0xcb, 0xc0, 0xf2, 0x9f, 0x78, 0x7e, 0x00, 0x7e, 0xd8, 0x70, 0x64,
+0x92, 0x5f, 0x66, 0x0c, 0x3b, 0xc0, 0x6f, 0x2b, 0x20, 0x1c, 0x8b, 0x20, 0x40, 0xc1, 0x2f, 0xa9,
+0x8f, 0xfe, 0xc6, 0x83, 0x25, 0xab, 0xd1, 0x17, 0x6e, 0xee, 0xd6, 0x9c, 0xce, 0x1e, 0x3e, 0x31,
+0xb0, 0x2c, 0x6f, 0xb2, 0x0a, 0x82, 0x61, 0x6c, 0xf7, 0x03, 0x06, 0x0f, 0xf3, 0x7e, 0x6c, 0x68,
+0x3b, 0x1c, 0x9a, 0x60, 0x09, 0x7d, 0xdf, 0x30, 0x1c, 0xff, 0x25, 0x0b, 0xcd, 0xb2, 0x9a, 0x02,
+0x5d, 0x4a, 0x36, 0x50, 0x59, 0x90, 0x3e, 0x10, 0x41, 0x2a, 0x6e, 0x07, 0x66, 0x0f, 0x4f, 0x01,
+0xac, 0x1b, 0x11, 0x77, 0x60, 0xff, 0x18, 0x2a, 0x12, 0x42, 0x18, 0x6e, 0x0c, 0x24, 0x97, 0xac,
+0x0e, 0x0f, 0xac, 0x61, 0x81, 0x42, 0xf3, 0xb0, 0x00, 0x0f, 0x0c, 0x60, 0xdb, 0xec, 0x34, 0x3c,
+0xcf, 0x06, 0xd6, 0x6c, 0x58, 0x5b, 0x24, 0xcf, 0xa0, 0x32, 0xbe, 0x64, 0xbc, 0x00, 0x7e, 0x40,
+0x4e, 0x00, 0x2f, 0x18, 0x0e, 0x05, 0x84, 0x05, 0x03, 0x58, 0x70, 0x7e, 0x01, 0x12, 0x1c, 0x7e,
+0xff, 0x82, 0x00, 0x12, 0x4c, 0xfd, 0xfb, 0x09, 0x56, 0xf9, 0x04, 0x29, 0x92, 0xe0, 0xf7, 0xf5,
+0x49, 0x30, 0xf3, 0x92, 0x60, 0xf1, 0x49, 0x30, 0xef, 0x60, 0x76, 0x78, 0x27, 0xc1, 0x92, 0xec,
+0x24, 0x38, 0xeb, 0xea, 0x4e, 0xb0, 0xe9, 0xc1, 0x09, 0xe8, 0x2a, 0x25, 0xe7, 0x4a, 0x60, 0x24,
+0xe5, 0x2c, 0x09, 0xe4, 0xa9, 0x12, 0xe3, 0x32, 0x25, 0xc1, 0xe1, 0x90, 0x20, 0xde, 0x48, 0x90,
+0xdd, 0x90, 0x60, 0xdb, 0x24, 0xc1, 0xda, 0x82, 0x72, 0x00, 0xde, 0x48, 0xd7, 0x0d, 0xc1, 0x32,
+0xe4, 0x20, 0x24, 0xd2, 0x12, 0x9c, 0xd1, 0xcf, 0x82, 0x10, 0x12, 0x4c, 0xcd, 0xcb, 0x09, 0x56,
+0xc9, 0x49, 0x30, 0xc7, 0x09, 0x52, 0xc3, 0x02, 0x17, 0x76, 0x12, 0x4c, 0xc1, 0xbe, 0x82, 0x10,
+0x12, 0x4c, 0xbd, 0xbc, 0x27, 0x58, 0xbb, 0x20, 0xc1, 0x66, 0xc6, 0xb9, 0x49, 0x70, 0x76, 0x5f,
+0x24, 0x48, 0xb5, 0x82, 0xd3, 0x06, 0x64, 0x4b, 0xb3, 0x20, 0x70, 0x82, 0xb0, 0x02, 0x49, 0xae,
+0x63, 0xc0, 0x4b, 0xfe, 0xfb, 0x41, 0x08, 0x6b, 0x25, 0xa5, 0x14, 0x49, 0x30, 0xa3, 0x49, 0x70,
+0x7e, 0xa1, 0xc1, 0x6b, 0x80, 0x87, 0x25, 0x9f, 0xa3, 0x97, 0x04, 0x9d, 0x1e, 0x80, 0x04, 0x9b,
+0x12, 0xbc, 0x1b, 0x99, 0xc1, 0x5b, 0x0d, 0x30, 0x3b, 0x97, 0xd4, 0x1c, 0x48, 0x95, 0x7c, 0x02,
+0x12, 0x93, 0x04, 0x8f, 0xaf, 0x82, 0x95, 0x90, 0x58, 0x40, 0x8e, 0xb3, 0x04, 0xaf, 0x1e, 0x82,
+0x95, 0x8b, 0x41, 0x42, 0x88, 0xf0, 0x22, 0x87, 0x3c, 0x4a, 0x49, 0x84, 0x41, 0x32, 0xa3, 0x75,
+0x20, 0x81, 0xfc, 0x82, 0x25, 0xaf, 0x13, 0x4c, 0x7e, 0x7d, 0x4b, 0x82, 0x7c, 0x68, 0x46, 0xcb,
+0x5a, 0xf6, 0xca, 0x0d, 0x12, 0xbc, 0xb8, 0x77, 0x27, 0x58, 0x76, 0x96, 0x04, 0x75, 0x03, 0x09,
+0x73, 0x40, 0x82, 0x3c, 0x71, 0x93, 0xe0, 0x78, 0x6f, 0x80, 0x1b, 0x06, 0x78, 0x6c, 0x82, 0x41,
+0x6e, 0xf0, 0x04, 0x4b, 0x6c, 0x92, 0xe0, 0x6b, 0x6a, 0x92, 0xd0, 0xf9, 0x26, 0x04, 0x67, 0x7c,
+0x03, 0x09, 0x65, 0xc1, 0xb2, 0xf0, 0x1b, 0x24, 0x30, 0x62, 0x61, 0x09, 0x0e, 0xe6, 0x5f, 0x86,
+0x2d, 0x37, 0xd9, 0x09, 0x5d, 0x99, 0x42, 0x2a, 0x0a, 0x21, 0xc1, 0x58, 0x91, 0x20, 0x57, 0xb1,
+0x19, 0xd7, 0x0d, 0xf0, 0x4a, 0x1c, 0x66, 0x1b, 0x48, 0x53, 0x45, 0xc1, 0x36, 0xf7, 0x8c, 0x82,
+0x25, 0x50, 0x82, 0x41, 0x4f, 0x3c, 0xc1, 0x4a, 0x4c, 0xc1, 0x20, 0x4b, 0x3c, 0x0c, 0x24, 0x49,
+0xff, 0x01, 0x12, 0x45, 0x18, 0xa9, 0x53, 0x76, 0x0d, 0x24, 0x41, 0xa9, 0x64, 0x35, 0xc0, 0x48,
+0x30, 0x3d, 0x97, 0x1d, 0x76, 0x1b, 0xfe, 0x82, 0x24, 0x24, 0x58, 0x39, 0x7c, 0x38, 0x03, 0x06,
+0x76, 0x96, 0x48, 0x10, 0x35, 0x12, 0x0c, 0xe6, 0x33, 0x04, 0x07, 0x3c, 0x09, 0x96, 0x31, 0x2f,
+0x08, 0x09, 0xbf, 0x5e, 0x6c, 0x26, 0x29, 0x09, 0x09, 0x2b, 0x84, 0x04, 0x09, 0xc3, 0xb2, 0x8b,
+0x6b, 0x98, 0xb0, 0x1b, 0x7c, 0x58, 0x76, 0xc1, 0x8b, 0x0e, 0x24, 0x21, 0xf0, 0xc9, 0xcb, 0x37,
+0x70, 0x1c, 0x4c, 0x82, 0x1d, 0x58, 0x12, 0x1c, 0x24, 0x24, 0x1a, 0xb3, 0x25, 0xbf, 0x0d, 0x80,
+0x04, 0x16, 0x12, 0xb8, 0x19, 0x14, 0x04, 0x58, 0x22, 0x92, 0xe0, 0x7c, 0x11, 0x82, 0x47, 0x83,
+0x82, 0x4b, 0x0f, 0x0e, 0x41, 0x42, 0x0d, 0x70, 0x20, 0x0b, 0x70, 0x12, 0x48, 0x09, 0x48, 0x93,
+0x66, 0x55, 0xdc, 0x5b, 0x2b, 0xa5, 0x53, 0xc0, 0xa7, 0xb2, 0xb7, 0x04, 0x95, 0x33, 0x0d, 0x84,
+0xdb, 0xb4, 0xcc, 0x29, 0x0d, 0xb0, 0xcc, 0xf8, 0x0d, 0x30, 0x19, 0xf9, 0x76, 0xbd, 0x85, 0xf7,
+0x61, 0xb7, 0xec, 0x45, 0x53, 0xdd, 0xd7, 0x32, 0x0d, 0x26, 0x98, 0xb0, 0x1b, 0x66, 0x5a, 0x40,
+0xdb, 0x05, 0xde, 0xc0, 0x0d, 0x29, 0x01, 0xc9, 0x0d, 0x03, 0x96, 0xe9, 0xe7, 0x5e, 0x09, 0x94,
+0x10, 0xb2, 0x19, 0x86, 0x1e, 0xe3, 0xf5, 0xc0, 0x80, 0x6e, 0xe1, 0x30, 0xe0, 0xce, 0xdf, 0x03,
+0x36, 0x99, 0xc6, 0x37, 0x90, 0xc6, 0x0d, 0xcb, 0x80, 0xd9, 0x84, 0x65, 0xfb, 0xdb, 0x12, 0x26,
+0xc6, 0xb1, 0x09, 0xeb, 0xc6, 0x0b, 0x84, 0xa4, 0xc6, 0xc5, 0x83, 0x07, 0xcf, 0x7f, 0x06, 0x24,
+0xcd, 0x30, 0x6b, 0xeb, 0x2b, 0x19, 0xcb, 0x43, 0xde, 0x0e, 0x7c, 0xc1, 0x6f, 0x66, 0xf3, 0x01,
+0x03, 0xc7, 0x60, 0x42, 0x66, 0x09, 0x33, 0xc5, 0xb1, 0x2d, 0x61, 0x17, 0xb7, 0xec, 0x0d, 0xb5,
+0x29, 0xc0, 0xb2, 0xa7, 0x80, 0x65, 0xbd, 0x40, 0xc2, 0xbb, 0x60, 0x65, 0xb9, 0x90, 0x30, 0xb7,
+0xb3, 0x18, 0xb5, 0x53, 0x48, 0x06, 0x5a, 0x24, 0x0c, 0xb1, 0x73, 0x06, 0xaf, 0x63, 0xb0, 0x6d,
+0x6e, 0xc3, 0x85, 0x1f, 0x0d, 0x0c, 0xf8, 0x36, 0x6e, 0xda, 0xac, 0x03, 0xdc, 0xd8, 0xaa, 0x01,
+0x0b, 0x32, 0xe0, 0xa8, 0xa6, 0x19, 0x10, 0xa3, 0x03, 0x02, 0x9f, 0xe0, 0x2a, 0xf8, 0xb0, 0x32,
+0x9b, 0x5b, 0x18, 0x99, 0xb6, 0x03, 0xce, 0x95, 0x01, 0x2b, 0x60, 0x90, 0x93, 0xb0, 0x32, 0x8f,
+0x03, 0x1e, 0x8d, 0x8b, 0x01, 0x29, 0x80, 0x95, 0x89, 0x60, 0xc8, 0x87, 0x1f, 0xac, 0x30, 0x83,
+0x42, 0x06, 0x51, 0x2b, 0x03, 0x7d, 0x94, 0x01, 0x7b, 0xd9, 0x80, 0x77, 0x1f, 0xc0, 0x08, 0x03,
+0x66, 0x75, 0x73, 0x01, 0x2b, 0x30, 0xe0, 0x71, 0x6f, 0x07, 0x8c, 0x6d, 0xca, 0x80, 0x6b, 0x65,
+0x40, 0x69, 0xc8, 0x80, 0x67, 0x65, 0xc0, 0x65, 0x01, 0x6e, 0x1b, 0x70, 0x85, 0x61, 0x3f, 0x46,
+0x18, 0x5d, 0x18, 0xf0, 0xd1, 0x59, 0x01, 0x53, 0xff, 0x70, 0x85, 0x55, 0xf8, 0x02, 0x18, 0x51,
+0x20, 0x03, 0x4d, 0x64, 0x40, 0x49, 0x0c, 0x08, 0x47, 0x30, 0xad, 0x3f, 0x38, 0x0c, 0x44, 0x78,
+0xb0, 0x0c, 0x42, 0x60, 0x19, 0x41, 0x90, 0x30, 0x3f, 0x20, 0x19, 0x3d, 0x18, 0x30, 0x3c, 0x7e,
+0x0c, 0x0c, 0x3a, 0x60, 0x06, 0x38, 0x30, 0x30, 0x37, 0x6b, 0x19, 0x35, 0x1d, 0x93, 0x91, 0x1d,
+0xfe, 0x03, 0x26, 0x31, 0x03, 0x2e, 0xee, 0x2f, 0x80, 0x03, 0x3c, 0x80, 0xc9, 0x2c, 0x7e, 0x96,
+0xc0, 0x2a, 0x29, 0x6b, 0xad, 0x78, 0x0b, 0x8f, 0xc2, 0x62, 0x1e, 0xbf, 0x65, 0x40, 0x25, 0x32,
+0x60, 0x22, 0x65, 0xc0, 0x20, 0x32, 0x20, 0x1e, 0x60, 0xc0, 0x1c, 0x0c, 0x48, 0x19, 0x01, 0x23,
+0x03, 0x96, 0x17, 0x15, 0x64, 0xc0, 0x12, 0x06, 0x0c, 0x18, 0x0f, 0x80, 0x43, 0x3c, 0x1c, 0xc0,
+0xca, 0x0d, 0x20, 0x64, 0x0b, 0x2e, 0x30, 0x08, 0x08, 0x2c, 0x03, 0x05, 0x80, 0x03, 0xfc, 0x0c,
+0x70, 0xfb, 0xf6, 0xa0, 0x01, 0xf5, 0x6c, 0x00, 0x13, 0xee, 0xda, 0x82, 0xe9, 0x06, 0x38, 0x6b,
+0x85, 0x61, 0x0d, 0x0f, 0xd0, 0xda, 0x10, 0x05, 0x20, 0x08, 0xd7, 0x40, 0x10, 0xd1, 0x80, 0x20,
+0xcb, 0xf0, 0x41, 0xc5, 0x0f, 0x07, 0x0f, 0x7d, 0x01, 0x0c, 0xbf, 0xba, 0x03, 0x04, 0xb4, 0x50,
+0x80, 0xb3, 0xc0, 0x0f, 0xae, 0x67, 0xe6, 0xc0, 0x06, 0x30, 0xa7, 0xa8, 0x75, 0x80, 0x80, 0xa1,
+0x03, 0x2c, 0xef, 0x02, 0x9b, 0x38, 0xc0, 0x02, 0x42, 0x00, 0x06, 0x33, 0x06, 0x98, 0x8f, 0x8a,
+0xc0, 0x00, 0x89, 0xbb, 0xdb, 0xae, 0x61, 0x49, 0x3c, 0x86, 0x7c, 0xd0, 0xef, 0x05, 0x27, 0x26,
+0x4d, 0x80, 0xff, 0x72, 0xc0, 0x3b, 0x35, 0x0f, 0x80, 0x24, 0x65, 0x09, 0x70, 0x5f, 0x59, 0x03,
+0x7c, 0x24, 0x66, 0xff, 0x12, 0xf1, 0x80, 0x9f, 0xc0, 0x8b, 0xef, 0x2e, 0x6f, 0x20, 0x3d, 0x46,
+0x00, 0x1d, 0x7c, 0x24, 0x8f, 0x86, 0x31, 0x57, 0xdb, 0xea, 0x18, 0x3e, 0x60, 0x58, 0x7c, 0x29,
+0x80, 0x86, 0xd7, 0x07, 0x78, 0x23, 0x38, 0x1e, 0x24, 0xab, 0x76, 0xcc, 0xc3, 0x34, 0xf9, 0x30,
+0x0f, 0x00, 0xe6, 0x0c, 0x01, 0x92, 0x06, 0x00, 0x83, 0x2d, 0x2b, 0x7e, 0x1a, 0x08, 0xd6, 0xc7,
+0xb0, 0xe8, 0xb7, 0xab, 0x47, 0xd6, 0xc3, 0xdd, 0xab, 0x58, 0xc2, 0xc9, 0xf7, 0xe2, 0x1c, 0xe5,
+0x07, 0xea, 0x55, 0x3c, 0xb3, 0x0f, 0xce, 0xca, 0x1e, 0x6a, 0x5c, 0xbd, 0xfc, 0x0f, 0xbf, 0xa6,
+0xae, 0xbe, 0x7c, 0x43, 0x3d, 0x62, 0x79, 0xb1, 0xab, 0xef, 0x02, 0x07, 0x7e, 0xab, 0xab, 0x17,
+0x74, 0x62, 0xbc, 0x37, 0x77, 0x67, 0xb2, 0x7a, 0xaa, 0x94, 0xab, 0x47, 0xa6, 0x8d, 0xcd, 0xea,
+0x7c, 0x88, 0xed, 0xca, 0x07, 0x82, 0xc0, 0x78, 0xc4, 0xe2, 0x87, 0x37, 0xaf, 0x4e, 0x6f, 0x75,
+0x60, 0x68, 0xc3, 0x38, 0xf5, 0xca, 0xd0, 0x6b, 0x58, 0x7c, 0x69, 0xfe, 0x4a, 0xa1, 0xd9, 0xfe,
+0x07, 0xf5, 0x57, 0x9f, 0x1f, 0xce, 0x57, 0x05, 0x2b, 0x52, 0x2d, 0x58, 0x4d, 0x2f, 0x06, 0xab,
+0x1e, 0x45, 0xe6, 0x1a, 0x8c, 0x7f, 0xf0, 0x55, 0xb0, 0x39, 0x27, 0x26, 0xa1, 0x6d, 0xa0, 0x13,
+0x6c, 0x7a, 0xc3, 0x5e, 0x6f, 0xfc, 0x3d, 0x45, 0x0f, 0xce, 0x7c, 0x0e, 0xda, 0x8b, 0x37, 0x5f,
+0x2b, 0xe7, 0xa8, 0x7f, 0x0b, 0x2c, 0xde, 0x57, 0x3d, 0x08, 0x35, 0x56, 0x41, 0x2a, 0xf8, 0x53,
+0xbd, 0x00, 0x0c, 0x46, 0x7a, 0x0f, 0x67, 0x27, 0xea, 0x9e, 0xe7, 0x32, 0x6f, 0x3c, 0x40, 0x90,
+0xdf, 0x99, 0x3a, 0x97, 0xd9, 0xc5, 0x49, 0xd5, 0x3c, 0x40, 0xe0, 0xe9, 0x91, 0xa9, 0xc3, 0xba,
+0xbc, 0xa9, 0x01, 0x76, 0x39, 0x35, 0x80, 0x75, 0xdc, 0x87, 0x09, 0xb8, 0x7f, 0x86, 0x44, 0x26,
+0xbb, 0x87, 0x9e, 0x7c, 0x97, 0x60, 0xf8, 0x75, 0x46, 0xf5, 0x76, 0xca, 0x35, 0x09, 0xf8, 0x2f,
+0x8b, 0x76, 0x92, 0xaf, 0x7c, 0x15, 0xde, 0x7f, 0x69, 0x70, 0x4f, 0x84, 0x3c, 0x17, 0x24, 0x7a,
+0x00, 0xe6, 0x56, 0x22, 0xb5, 0x74, 0x30, 0xa4, 0x52, 0x2c, 0x18, 0x6d, 0x0f, 0x9a, 0x6c, 0x7c,
+0x4f, 0x1e, 0x67, 0x91, 0x56, 0x5f, 0x7e, 0xa1, 0x04, 0xa3, 0x87, 0xea, 0x54, 0x4a, 0x58, 0x4c,
+0x49, 0xbf, 0x90, 0x62, 0x26, 0x86, 0x54, 0x37, 0x63, 0x11, 0x00, 0x17, 0x7e, 0x83, 0x73, 0x2f,
+0x37, 0xdf, 0xf5, 0x54, 0x7e, 0x21, 0x70, 0x90, 0x3a, 0x1f, 0x19, 0xe5, 0xde, 0x0c, 0x12, 0x92,
+0x9a, 0x6b, 0x07, 0xa3, 0xc3, 0xc6, 0xff, 0x36, 0x1b, 0xcc, 0x5f, 0x3b, 0x46, 0xb3, 0xe7, 0xf0,
+0xb3, 0x6e, 0x6f, 0xcc, 0x07, 0xb6, 0xc9, 0x60, 0x30, 0x17, 0x38, 0x09, 0x4d, 0xbf, 0x82, 0xd1,
+0x70, 0xd2, 0x9d, 0x19, 0x97, 0x07, 0x30, 0x8c, 0x82, 0x3f, 0xc0, 0x98, 0x1b, 0x3c, 0x1f, 0x07,
+0x8e, 0xb9, 0x17, 0x07, 0xc6, 0x10, 0x73, 0x33, 0x68, 0x47, 0x27, 0x33, 0x81, 0x0d, 0x2c, 0x3a,
+0x69, 0xf8, 0xa1, 0xec, 0xa3, 0x85, 0x7f, 0x6e, 0x97, 0xd4, 0xd1, 0x96, 0xce, 0xa3, 0x60, 0x90,
+0x47, 0x9b, 0x0d, 0x57, 0x07, 0x82, 0x91, 0x7e, 0x9d, 0xf0, 0x8f, 0x0f, 0xc6, 0x70, 0x9a, 0xf7,
+0x98, 0x7a, 0xef, 0x17, 0x0e, 0x27, 0xa6, 0x66, 0x4a, 0x55, 0x80, 0x23, 0x47, 0x5a, 0x59, 0xc8,
+0x11, 0x1d, 0x42, 0xc5, 0x53, 0x18, 0x1d, 0xfd, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7, 0x48, 0x3c, 0xa3,
+0x47, 0x47, 0x42, 0xb3, 0x37, 0xf7, 0x9f, 0xf6, 0x62, 0xff, 0x3f, 0x5f, 0x30, 0x0a, 0x2a, 0x31,
+0xdc, 0x07, 0xb8, 0x4a, 0x46, 0x1f, 0xd1, 0x30, 0x19, 0x93, 0x9b, 0x12, 0x08, 0xa1, 0x0d, 0xa3,
+0x06, 0xe6, 0xbf, 0x07, 0x63, 0xe6, 0x6c, 0x7e, 0x33, 0x66, 0xcc, 0x0f, 0xa1, 0xf6, 0x7a, 0x36,
+0x6a, 0xdf, 0x11, 0xf4, 0xee, 0x35, 0x10, 0x16, 0xdd, 0xc3, 0x66, 0x33, 0xcf, 0x66, 0x09, 0x22,
+0x42, 0xb3, 0x88, 0x01, 0xd9, 0x10, 0x1a, 0xd4, 0x95, 0xd0, 0xca, 0x34, 0xa1, 0x9a, 0xa1, 0x21,
+0xbe, 0xb8, 0x08, 0x0d, 0xb2, 0x48, 0x68, 0xac, 0x96, 0xd0, 0xa0, 0xd0, 0x84, 0x9a, 0x84, 0x86,
+0x94, 0x8e, 0x84, 0x0e, 0xf8, 0x82, 0x08, 0x05, 0x7c, 0x08, 0x1d, 0xff, 0x70, 0x16, 0x0b, 0x0f,
+0x1f, 0x10, 0x1a, 0x5e, 0x3c, 0x14, 0x52, 0x37, 0xa1, 0x84, 0x4c, 0x25, 0x34, 0x46, 0xd0, 0xa1,
+0x40, 0x84, 0x96, 0x3a, 0x34, 0x21, 0x34, 0x2e, 0x0d, 0xa1, 0x28, 0x6e, 0x09, 0x22, 0x0f, 0xa1,
+0x61, 0x6f, 0x10, 0x42, 0x01, 0x0a, 0x31, 0x5b, 0x77, 0x6f, 0x60, 0xfe, 0x27, 0x06, 0x16, 0xf2,
+0x78, 0xc3, 0xd7, 0x27, 0x31, 0xb0, 0xdd, 0x62, 0x60, 0xd7, 0xc4, 0xc0, 0xcb, 0x17, 0x93, 0x81,
+0xc8, 0xc1, 0x88, 0x45, 0x78, 0xbc, 0xb3, 0xd8, 0x5d, 0xfe, 0xb6, 0x18, 0x16, 0xb0, 0x16, 0xb1,
+0xab, 0xa9, 0x60, 0x71, 0x7e, 0xa3, 0xe2, 0x8b, 0x9c, 0xc0, 0x16, 0xc3, 0x98, 0x92, 0xf1, 0x22,
+0x91, 0xc7, 0xe9, 0xd6, 0x97, 0x86, 0xe2, 0x8b, 0x85, 0x0e, 0x2c, 0xc6, 0x80, 0x3c, 0x7a, 0xef,
+0xd5, 0x8f, 0x07, 0x0b, 0x17, 0x73, 0x6f, 0xf5, 0xa2, 0x7e, 0x6d, 0xde, 0x9b, 0xff, 0x66, 0x01,
+0x36, 0x0c, 0x2e, 0x29, 0x17, 0x81, 0x9e, 0x07, 0x5e, 0x38, 0x05, 0x4f, 0x25, 0x2c, 0x4b, 0xcc,
+0xe2, 0x7f, 0x3e, 0x63, 0x59, 0x38, 0x87, 0x90, 0xc5, 0x2c, 0x2c, 0xbe, 0x06, 0x0f, 0x1f, 0x62,
+0x31, 0x1b, 0xc2, 0xbd, 0xdd, 0x50, 0x5d, 0x45, 0x3d, 0x79, 0x3c, 0x88, 0x24, 0x20, 0x37, 0x33,
+0x81, 0x4c, 0x1c, 0x39, 0x52, 0x03, 0x69, 0x26, 0x11, 0x84, 0x20, 0x90, 0x16, 0x22, 0x00, 0x78,
+0xf9, 0x04, 0x7c, 0xce, 0xd6, 0xe6, 0x7c, 0x40, 0x2b, 0x83, 0x33, 0xda, 0x7b, 0x1c, 0x1d, 0x1e,
+0x50, 0xc3, 0x1f, 0x10, 0xe7, 0x7c, 0x01, 0x61, 0x33, 0x76, 0x3f, 0x7c, 0x82, 0xb2, 0xaa, 0xaa,
+0x02, 0x82, 0x7c, 0xc2, 0x20, 0x33, 0xd4, 0x2a, 0x2c, 0xa2, 0x8d, 0xa3, 0x85, 0x85, 0x0f, 0x2d,
+0xe4, 0x60, 0x30, 0xdd, 0xc5, 0xef, 0x9a, 0xa2, 0x00, 0x9a, 0x20, 0x62, 0xef, 0x84, 0xca, 0x33,
+0x95, 0x45, 0x53, 0xb2, 0x88, 0x43, 0x06, 0x11, 0x33, 0x1a, 0x44, 0x11, 0xb3, 0xd0, 0xb9, 0x64,
+0x93, 0x0c, 0x88, 0x84, 0xff, 0x28, 0x32, 0x33, 0x6a, 0x52, 0x0f, 0x82, 0x31, 0x41, 0xc6, 0xf3,
+0xb1, 0x83, 0x34, 0x18, 0x2c, 0x06, 0x92, 0xa0, 0x03, 0x43, 0xe4, 0xf6, 0xbf, 0x30, 0x8a, 0x53,
+0x0b, 0xbb, 0xc6, 0x0f, 0x30, 0x8a, 0x30, 0x73, 0x46, 0x0c, 0x1c, 0xe4, 0x48, 0x0f, 0x5a, 0x12,
+0x03, 0x61, 0x06, 0xc4, 0x0f, 0x42, 0x72, 0x00, 0xca, 0x20, 0x33, 0x9b, 0x85, 0x40, 0x47, 0xec,
+0x05, 0x9f, 0x7f, 0x85, 0x85, 0x36, 0x04, 0xbd, 0x9f, 0x88, 0x91, 0x43, 0xde, 0x32, 0x33, 0xff,
+0xec, 0xc2, 0x1f, 0x30, 0x2f, 0x30, 0xac, 0xd2, 0x16, 0xa4, 0xf4, 0x0c, 0x31, 0x5b, 0x0f, 0x56,
+0x06, 0x33, 0xfa, 0x00, 0xc3, 0x7c, 0x01, 0x01, 0xa3, 0x14, 0xb3, 0xf0, 0x0e, 0x90, 0xef, 0x2c,
+0x8f, 0xc6, 0x49, 0x37, 0x7f, 0xc6, 0x09, 0x0b, 0x0f, 0xe2, 0x55, 0x11, 0xb7, 0xa4, 0x73, 0x0c,
+0x90, 0x20, 0x73, 0x85, 0x91, 0x39, 0x81, 0xbd, 0x5f, 0x00, 0x2d, 0x21, 0xbb, 0x42, 0x16, 0x10,
+0x09, 0xa1, 0xe7, 0x8a, 0x6f, 0xbd, 0x30, 0x62, 0x36, 0xec, 0xd1, 0x32, 0xa3, 0x56, 0x59, 0x33,
+0xc8, 0x20, 0x33, 0x59, 0xd1, 0x0b, 0x59, 0x34, 0x78, 0x1d, 0xcd, 0x18, 0x94, 0xb0, 0x95, 0x2a,
+0x0a, 0x1e, 0x11, 0xd0, 0x82, 0xcb, 0x0f, 0x7c, 0x20, 0x9a, 0x21, 0x7c, 0x22, 0xc0, 0x33, 0x45,
+0x8a, 0x03, 0x5a, 0x1d, 0xcf, 0x83, 0xb8, 0xe6, 0x7c, 0x80, 0x33, 0x01, 0x23, 0x0c, 0x98, 0xcb,
+0xca, 0x02, 0x90, 0x13, 0x0c, 0x62, 0x33, 0x8c, 0x02, 0x00, 0x07, 0xb3, 0x06, 0xf1, 0xb2, 0x33,
+0x02, 0x05, 0x01, 0x03, 0x9b, 0x84, 0x01, 0x99, 0xc2, 0x80, 0x97, 0x65, 0x40, 0x95, 0xf1, 0x80,
+0x94, 0x44, 0x06, 0x33, 0x2c, 0x22, 0x0f, 0x07, 0x8c, 0x89, 0x41, 0x98, 0x02, 0x90, 0x33, 0x2e,
+0x03, 0x79, 0x76, 0x03, 0x03, 0x77, 0x83, 0x70, 0xdf, 0x33, 0xa4, 0x0a, 0x4b, 0x06, 0x67, 0x03,
+0x60, 0x40, 0x65, 0x04, 0x68, 0xe9, 0x16, 0x99, 0xf6, 0x95, 0x10, 0x8a, 0xcf, 0x03, 0x3b, 0xc6,
+0x0d, 0x12, 0x8a, 0x30, 0xeb, 0x42, 0xb1, 0x0b, 0x32, 0x60, 0x59, 0x90, 0xdf, 0x0d, 0x3c, 0x66,
+0x09, 0x39, 0x00, 0x64, 0x10, 0x33, 0xc4, 0x42, 0x38, 0x80, 0x20, 0x57, 0xec, 0xc0, 0x49, 0x3c,
+0x22, 0x03, 0xce, 0x45, 0x88, 0x23, 0x7c, 0x10, 0x32, 0x33, 0x60, 0x19, 0x41, 0x64, 0x26, 0x56,
+0x29, 0x30, 0x60, 0x30, 0xb7, 0x6c, 0x7c, 0x0a, 0x0d, 0x32, 0x88, 0x33, 0x1e, 0xb0, 0x37, 0xe4,
+0x03, 0x35, 0xe0, 0x1e, 0x30, 0x34, 0x2e, 0x03, 0x33, 0x7c, 0x42, 0xc6, 0x6f, 0xc6, 0x60, 0x59,
+0x0d, 0xc5, 0x32, 0x2d, 0x03, 0x1c, 0xcb, 0x2b, 0x13, 0x2c, 0x1a, 0xca, 0x80, 0x27, 0x56, 0x34,
+0x56, 0x80, 0x14, 0x78, 0xc0, 0x1f, 0x63, 0x36, 0xec, 0x34, 0xa3, 0x15, 0x54, 0x46, 0x73, 0xc0,
+0x80, 0x19, 0x06, 0x3c, 0x18, 0x70, 0x15, 0x32, 0x60, 0x13, 0x03, 0x06, 0x18, 0x0f, 0xc0, 0x02,
+0x20, 0x62, 0x0d, 0x00, 0xc2, 0x33, 0x47, 0x83, 0x5f, 0xc4, 0xa3, 0x5e, 0x3d, 0x18, 0x33, 0x00,
+0x3a, 0x6c, 0x30, 0xda, 0x0f, 0x6c, 0xb8, 0x4e, 0x18, 0xc4, 0x33, 0x28, 0x14, 0x0e, 0xcd, 0x0c,
+0x39, 0xc2, 0x20, 0x33, 0x68, 0x09, 0x97, 0x16, 0x89, 0x6b, 0x9c, 0xc9, 0xb3, 0x07, 0x60, 0x30,
+0xd0, 0xa1, 0xb2, 0x0c, 0xe2, 0xb1, 0x33, 0x11, 0xa1, 0x88, 0x20, 0x43, 0x44, 0x90, 0x3b, 0x10,
+0x19, 0x33, 0x30, 0xa1, 0x5e, 0xc4, 0xec, 0x17, 0x7f, 0x10, 0x19, 0x33, 0xdf, 0xa1, 0x28, 0xcd,
+0x76, 0xe2, 0xd0, 0x18, 0x6c, 0x22, 0x95, 0x8f, 0x9b, 0xf6, 0x22, 0x0e, 0x19, 0xb1, 0xd6, 0x44,
+0x59, 0x06, 0x45, 0x6c, 0x07, 0x30, 0x10, 0x92, 0x53, 0x9f, 0x22, 0x50, 0xe6, 0x16, 0x2c, 0xfb,
+0x06, 0x10, 0x6e, 0x07, 0x0a, 0x65, 0x33, 0x1b, 0x24, 0x20, 0x30, 0x3f, 0x86, 0x05, 0x1b, 0x1c,
+0x11, 0x57, 0x12, 0xc1, 0x20, 0x33, 0xf0, 0x0e, 0x5f, 0x0f, 0x16, 0xef, 0x5f, 0x07, 0x9f, 0x8b,
+0x0d, 0x7c, 0xef, 0x27, 0x19, 0xc4, 0x33, 0x3c, 0x8a, 0x5a, 0x2f, 0x5e, 0x89, 0x93, 0x45, 0x73,
+0x07, 0x47, 0x98, 0x9b, 0x6f, 0xbf, 0x07, 0x23, 0xd8, 0x30, 0x0f, 0x4f, 0x5f, 0x84, 0xd3, 0x0c,
+0xf1, 0x0e, 0x79, 0x77, 0x63, 0x65, 0x6b, 0x4d, 0x19, 0xc4, 0x33, 0xc1, 0x22, 0x53, 0x40, 0xff,
+0xe1, 0x32, 0xe4, 0x3a, 0xf6, 0x2a, 0x5f, 0x86, 0xa0, 0x20, 0x34, 0x45, 0x12, 0x33, 0x8b, 0x81,
+0x3e, 0x35, 0x8b, 0x87, 0x87, 0x2e, 0x8b, 0x21, 0x18, 0x21, 0x20, 0x0e, 0x3c, 0x6c, 0xf1, 0x2b,
+0x7a, 0x78, 0x90, 0x41, 0x33, 0x01, 0x39, 0xb5, 0x72, 0xc1, 0x93, 0x54, 0x9d, 0x4c, 0x44, 0x56,
+0x67, 0x4a, 0x52, 0x09, 0x80, 0x68, 0x44, 0x35, 0xca, 0x2a, 0xa7, 0x05, 0xe6, 0xc0, 0x5a, 0x59,
+0x67, 0x80, 0x1f, 0xff, 0x1a, 0x1e, 0x1c, 0x8b, 0x8d, 0x37, 0x58, 0xd6, 0x56, 0xc1, 0x67, 0xa5,
+0x46, 0x49, 0x64, 0x01, 0x83, 0x4c, 0x36, 0x33, 0x12, 0x7c, 0x65, 0x15, 0x67, 0x10, 0x6e, 0x6c,
+0x60, 0x49, 0x97, 0x29, 0xab, 0x67, 0x88, 0x70, 0x1f, 0x05, 0x4b, 0x27, 0x4b, 0x59, 0x67, 0x37,
+0x16, 0x44, 0xb7, 0xac, 0x12, 0x3f, 0x48, 0x16, 0x5f, 0xca, 0x2a, 0x67, 0x65, 0x9f, 0x00, 0xf6,
+0x62, 0x64, 0x3f, 0xc8, 0xbb, 0x00, 0x76, 0x32, 0x34, 0x30, 0x88, 0xd9, 0x78, 0x4f, 0x2c, 0x2c,
+0xa7, 0x80, 0x38, 0x1f, 0x05, 0xcb, 0xb7, 0x17, 0x59, 0x67, 0x36, 0x2c, 0x88, 0x1c, 0xd7, 0x88,
+0x87, 0x06, 0x12, 0x34, 0x0d, 0x0c, 0xe7, 0x93, 0xbf, 0xbf, 0x64, 0x6c, 0x78, 0x70, 0xe0, 0xe0,
+0x65, 0x10, 0x33, 0x09, 0x20, 0xd6, 0x58, 0x82, 0x8f, 0xca, 0x2a, 0x67, 0x25, 0xac, 0x5f, 0x09,
+0xd5, 0x18, 0xa1, 0xdc, 0xb0, 0x4f, 0x0f, 0x90, 0xd0, 0x6d, 0x8f, 0x41, 0xbc, 0x7f, 0x21, 0x21,
+0x37, 0x7f, 0x92, 0x2d, 0x3f, 0x59, 0x10, 0xff, 0x0f, 0x57, 0x85, 0xaf, 0x29, 0x1e, 0x54, 0xf0,
+0xe8, 0x65, 0xc3, 0x8f, 0x3f, 0x21, 0xb8, 0xf4, 0xcf, 0x0b, 0x22, 0x07, 0x1e, 0x03, 0x5f, 0xc8,
+0x20, 0x33, 0x25, 0x8b, 0x41, 0x0b, 0xfd, 0x18, 0x59, 0x63, 0x70, 0xcb, 0x0f, 0xaf, 0x2a, 0x83,
+0x33, 0xc8, 0x18, 0x18, 0xff, 0x84, 0xd9, 0x12, 0x2f, 0x19, 0xc4, 0x33, 0x6d, 0x15, 0xd4, 0x68,
+0x61, 0xff, 0x61, 0x9c, 0x49, 0x1f, 0x30, 0x88, 0x33, 0x9c, 0x00, 0x0c, 0x7e, 0x21, 0x45, 0x27,
+0x0c, 0x22, 0x33, 0xe2, 0xb0, 0xaf, 0x0b, 0x81, 0x47, 0x91, 0x90, 0xc6, 0x29, 0xc2, 0x7f, 0x67,
+0xe2, 0x2d, 0x2f, 0x2c, 0x09, 0x67, 0x95, 0x41, 0x33, 0x0e, 0x69, 0x8f, 0xfe, 0x06, 0x71, 0x33,
+0x62, 0x55, 0x03, 0x2b, 0xf7, 0x06, 0x1c, 0x4f, 0x8d, 0x62, 0x59, 0x3f, 0xaa, 0x0c, 0x33, 0xc2,
+0xdc, 0x2f, 0xdf, 0x62, 0x52, 0x8f, 0x2d, 0x29, 0xc7, 0xb7, 0xec, 0xcf, 0x0f, 0x0d, 0x8d, 0x2c,
+0xef, 0xcd, 0x9a, 0xef, 0xef, 0x81, 0xe2, 0x4f, 0xc7, 0x20, 0x26, 0xf6, 0xd8, 0xca, 0x33, 0x5f,
+0x29, 0x22, 0xe7, 0x78, 0x30, 0x0f, 0x08, 0x83, 0x33, 0x15, 0xb1, 0x86, 0x80, 0x07, 0x0f, 0x65,
+0x10, 0x33, 0x98, 0x0b, 0x5f, 0x00, 0x96, 0x56, 0x43, 0x3c, 0x12, 0x00, 0xfc, 0x51, 0x10, 0x88,
+0x85, 0x71, 0x28, 0x30, 0x33, 0xa4, 0xec, 0x18, 0x37, 0x04, 0x1e, 0xff, 0x7f, 0x04, 0xb7, 0x3a,
+0x3f, 0x96, 0xb0, 0x7f, 0x05, 0x13, 0x2f, 0x45, 0x59, 0x67, 0x68, 0x14, 0x55, 0x60, 0x6d, 0xcc,
+0x91, 0x9f, 0xc0, 0xb2, 0x0a, 0x67, 0x18, 0x30, 0xf5, 0xf0, 0x22, 0xa4, 0x84, 0x55, 0x67, 0xcb,
+0x80, 0xf1, 0xc0, 0x80, 0x7c, 0xef, 0xab, 0xe0, 0x7c, 0x67, 0x70, 0x21, 0x5e, 0x40, 0x82, 0x31,
+0x65, 0x40, 0xe9, 0x41, 0xbc, 0x1b, 0x82, 0x65, 0x8f, 0x2c, 0xac, 0x67, 0x1b, 0x06, 0xe3, 0x0d,
+0x19, 0x10, 0xe1, 0x55, 0x70, 0x3c, 0x20, 0x94, 0x67, 0x62, 0x33, 0xdb, 0x37, 0x2e, 0x03, 0xd9,
+0x78, 0x2c, 0x03, 0xd7, 0x96, 0x10, 0x9b, 0x30, 0x20, 0x6d, 0x56, 0xc1, 0x67, 0x80, 0x15, 0x80,
+0xcb, 0xcf, 0x3c, 0x40, 0xc0, 0xcd, 0x6f, 0x67, 0xcc, 0x78, 0x08, 0x3c, 0x38, 0x70, 0xa2, 0xd0,
+0xf2, 0x36, 0x3f, 0x8d, 0x0c, 0x33, 0x87, 0x19, 0x47, 0x4f, 0x59, 0x05, 0x67, 0x84, 0x41, 0x33,
+0x00, 0x94, 0xcd, 0x82, 0xb6, 0x45, 0x82, 0xa1, 0x0d, 0xca, 0x5d, 0x0c, 0x58, 0xb7, 0x61, 0xb6,
+0xfb, 0x4f, 0x86, 0x2d, 0x37, 0x62, 0x13, 0xbf, 0x0d, 0x3b, 0x03, 0xb1, 0x66, 0x7d, 0x86, 0x25,
+0x37, 0x96, 0xe0, 0xd5, 0x95, 0x92, 0x20, 0x93, 0x88, 0xc7, 0x13, 0xaa, 0x30, 0x33, 0x02, 0x49,
+0x56, 0xf0, 0xed, 0x9d, 0x12, 0x06, 0xcd, 0x20, 0x5a, 0x99, 0x86, 0xc8, 0x33, 0x18, 0xb6, 0x30,
+0x9f, 0x10, 0x31, 0x61, 0x09, 0x15, 0x06, 0x33, 0x0c, 0x58, 0x79, 0x06, 0x04, 0x77, 0x10, 0x07,
+0x76, 0x01, 0x65, 0x33, 0xc1, 0x80, 0x65, 0x32, 0x88, 0x76, 0x33, 0x19, 0x10, 0x61, 0x10, 0x2f,
+0x0d, 0x20, 0x61, 0x33, 0xe0, 0x32, 0x5d, 0x76, 0x91, 0x30, 0x5b, 0x45, 0x06, 0x33, 0x19, 0xa4,
+0x1d, 0xfe, 0x19, 0xc4, 0x33, 0x88, 0x14, 0xb1, 0xa4, 0xe5, 0x32, 0xa0, 0x05, 0x49, 0x0c, 0xe2,
+0x7c, 0x33, 0x40, 0x8a, 0x20, 0x60, 0x3f, 0x90, 0x30, 0x3d, 0x4b, 0x18, 0x3b, 0x0b, 0x16, 0xd6,
+0x51, 0x66, 0xcd, 0xd1, 0x51, 0x2e, 0x03, 0x35, 0xe6, 0xc8, 0x20, 0x33, 0x65, 0x40, 0x31, 0x78,
+0xc0, 0xf0, 0x2f, 0x02, 0x83, 0x33, 0x14, 0x11, 0x88, 0x97, 0x13, 0x0c, 0x38, 0x04, 0x32, 0x33,
+0x14, 0x06, 0x23, 0x0c, 0xf8, 0x30, 0x1c, 0x6c, 0x38, 0x1d, 0x10, 0x2b, 0x01, 0x60, 0x33, 0xc0,
+0x8c, 0xf5, 0x63, 0x65, 0x0f, 0x21, 0x80, 0x30, 0x17, 0xcb, 0x80, 0x0b, 0xac, 0x82, 0xf0, 0x67,
+0x08, 0x02, 0x70, 0x10, 0x09, 0xc9, 0x2a, 0x87, 0xb1, 0x07, 0x61, 0x05, 0x87, 0x5c, 0xb5, 0xac,
+0xab, 0xe0, 0x30, 0x70, 0xdc, 0x67, 0x0a, 0x06, 0x63, 0x18, 0x4d, 0x73, 0xc0, 0x55, 0x30, 0x7c,
+0xfe, 0x83, 0x67, 0x0c, 0x18, 0xfe, 0x86, 0x1c, 0x70, 0xc2, 0xfe, 0x68, 0x41, 0x0f, 0xc4, 0x2a,
+0xe1, 0x60, 0x15, 0x67, 0x67, 0xe8, 0x0c, 0xd8, 0x21, 0x07, 0xc6, 0x68, 0x06, 0x6c, 0xa5, 0x3c,
+0xb2, 0x0a, 0x67, 0xef, 0x16, 0x07, 0xc2, 0xc4, 0x1f, 0xa3, 0x02, 0x88, 0x8d, 0x60, 0x17, 0x37,
+0x7e, 0xc2, 0x0b, 0xdc, 0x17, 0x86, 0xfc, 0xdb, 0x00, 0x5e, 0x10, 0x08, 0xb2, 0x0a, 0x67, 0x78,
+0x14, 0x63, 0xed, 0xb1, 0x2d, 0x23, 0x61, 0xf2, 0x2e, 0x1b, 0x88, 0x68, 0xd9, 0x3c, 0x78, 0xc3,
+0xe8, 0xf0, 0x30, 0x3e, 0x4e, 0x31, 0x6b, 0x04, 0xc1, 0x50, 0x06, 0x33, 0x1e, 0xbd, 0xad, 0x27,
+0x1e, 0xcd, 0x25, 0x21, 0xe2, 0xcd, 0xc7, 0x5f, 0x99, 0x01, 0xcf, 0xbd, 0x8b, 0xb9, 0x1f, 0xfd,
+0x07, 0x7c, 0x74, 0x7c, 0xc2, 0x05, 0xc2, 0xc5, 0x1e, 0x67, 0x1f, 0xd9, 0xac, 0x27, 0xe7, 0xa5,
+0x10, 0x86, 0x2f, 0x05, 0x60, 0x33, 0x40, 0xb8, 0xbf, 0x3c, 0xe1, 0x5b, 0x05, 0x30, 0xe0, 0x95,
+0x41, 0x33, 0xf2, 0x2c, 0xb7, 0x18, 0x18, 0x0c, 0x62, 0x33, 0x43, 0x88, 0xf9, 0xc9, 0x87, 0x11,
+0xc6, 0x7c, 0x32, 0x88, 0x33, 0x12, 0xa1, 0xc8, 0x98, 0xd0, 0x3e, 0x9f, 0x30, 0x83, 0x33, 0xc4,
+0x60, 0xcd, 0xfc, 0x3f, 0x18, 0x33, 0x00, 0x3b, 0x19, 0x1a, 0x78, 0xc2, 0xc4, 0xd8, 0xd8, 0x18,
+0x11, 0x0b, 0xa0, 0x42, 0x06, 0x33, 0x38, 0x1c, 0xc7, 0x79, 0x28, 0x83, 0x33, 0x4e, 0x6a, 0x7e,
+0x0d, 0x38, 0x18, 0x2c, 0xd1, 0x7c, 0x28, 0x83, 0x33, 0xcc, 0x5a, 0x17, 0x6f, 0xbd, 0x5f, 0x56,
+0xe6, 0xc6, 0x11, 0x13, 0x86, 0x07, 0x71, 0x11, 0x0c, 0xff, 0xd1, 0x6e, 0x6b, 0x1f, 0xaa, 0xfc,
+0x39, 0x32, 0x88, 0x0c, 0x23, 0x63, 0x7b, 0xce, 0xf0, 0xe0, 0xb8, 0x63, 0x88, 0x19, 0xc4, 0x33,
+0xf1, 0x5d, 0x18, 0x88, 0x0e, 0x0c, 0xa2, 0x07, 0x33, 0x58, 0x84, 0x5f, 0xf1, 0x58, 0x67, 0x62,
+0x61, 0x4d, 0x08, 0x0c, 0x33, 0x6f, 0x42, 0xb1, 0x4f, 0xb9, 0x19, 0xbf, 0x1c, 0xb6, 0xf6, 0xa0,
+0xce, 0x07, 0x97, 0xd8, 0x06, 0x61, 0x33, 0xc9, 0x53, 0xe9, 0x74, 0x50, 0x7a, 0x72, 0x55, 0xd1,
+0x72, 0xc8, 0x88, 0x9b, 0x49, 0x8a, 0x01, 0x50, 0x85, 0x24, 0x22, 0x06, 0x83, 0x51, 0x0f, 0x33,
+0x55, 0x19, 0x9b, 0xf1, 0x68, 0x5f, 0x40, 0x06, 0x33, 0x36, 0xa1, 0x6a, 0xaf, 0x98, 0x5b, 0x0c,
+0x2f, 0x88, 0x4a, 0xa1, 0xc0, 0x9b, 0x30, 0x82, 0x18, 0x53, 0x11, 0xb5, 0x2f, 0x95, 0x9b, 0x7f,
+0x09, 0x54, 0xdb, 0xab, 0x60, 0x67, 0x7a, 0x0b, 0xdf, 0x4b, 0xa2, 0xfb, 0x65, 0x10, 0x33, 0x57,
+0x7c, 0x08, 0x1c, 0x36, 0x88, 0x2c, 0x22, 0x7d, 0x46, 0xd4, 0x9b, 0x0b, 0x15, 0x34, 0x1b, 0x99,
+0x8f, 0x12, 0x6e, 0x99, 0xbf, 0xcc, 0x59, 0x54, 0x9b, 0x54, 0x6f, 0xcc, 0x1b, 0x19, 0xab, 0x00,
+0x44, 0x2d, 0x6e, 0x65, 0x9b, 0xcc, 0x5e, 0x54, 0xbb, 0x06, 0x71, 0x33, 0x56, 0x05, 0xb3, 0x0a,
+0x67, 0x20, 0x22, 0xaa, 0xc8, 0x33, 0x0a, 0xda, 0xeb, 0x52, 0xc8, 0x33, 0x3f, 0xaf, 0x60, 0x33,
+0x60, 0x10, 0x33, 0x2c, 0x01, 0x00, 0x28, 0x23, 0xc2, 0x2a, 0x67, 0x59, 0x80, 0xbf, 0x2a, 0x08,
+0xa1, 0xc2, 0x67, 0x4a, 0x6a, 0x55, 0xdf, 0x92, 0x5c, 0x12, 0xdf, 0x56, 0xc1, 0x67, 0xd9, 0x52,
+0x0c, 0xff, 0x0d, 0x6b, 0x7e, 0x2f, 0x25, 0x3d, 0xd1, 0xf8, 0xef, 0xac, 0x82, 0x67, 0xc4, 0x28,
+0x21, 0x73, 0x41, 0xbf, 0xc2, 0x72, 0x74, 0x9f, 0x57, 0x41, 0x67, 0x15, 0xc8, 0x18, 0xa6, 0x8c,
+0x02, 0x6c, 0x55, 0x2a, 0x84, 0x37, 0xbb, 0x77, 0xec, 0xb8, 0xae, 0xc4, 0xb1, 0x2c, 0xef, 0x45,
+0x06, 0x33, 0x04, 0x4b, 0x00, 0x65, 0x15, 0x67, 0x55, 0x41, 0x31, 0xa3, 0xe9, 0xa8, 0x07, 0x33,
+0x8a, 0x8c, 0x9b, 0x08, 0x37, 0x26, 0x8c, 0x30, 0x33, 0x48, 0x06, 0xe3, 0x16, 0x23, 0x51, 0x29,
+0x88, 0x5a, 0x21, 0xc0, 0x9b, 0x97, 0x01, 0xd5, 0x3c, 0x64, 0x10, 0x33, 0x35, 0x4b, 0x6f, 0x96,
+0x40, 0x73, 0xb2, 0x0a, 0x67, 0x64, 0xc0, 0xcb, 0xc1, 0xa3, 0x5a, 0x56, 0x56, 0x67, 0x12, 0xa8,
+0x81, 0x46, 0xd4, 0x9b, 0x4b, 0x05, 0x2d, 0x5b, 0x98, 0x7d, 0x10, 0x6e, 0x98, 0xa7, 0xb2, 0x96,
+0x01, 0xb4, 0x2c, 0x03, 0xb3, 0x52, 0x06, 0xb2, 0x30, 0xa2, 0x9b, 0x19, 0x30, 0xad, 0x06, 0x71,
+0x78, 0x33, 0x12, 0x01, 0x33, 0x03, 0x97, 0x32, 0x88, 0x33, 0x90, 0x00, 0x63, 0x18, 0x79, 0x67,
+0x15, 0x2c, 0x83, 0x98, 0x73, 0x33, 0x0d, 0x28, 0x24, 0x58, 0x5e, 0x9c, 0xc9, 0x60, 0x15, 0x67,
+0x60, 0x50, 0x38, 0x33, 0x49, 0xc2, 0x2a, 0x67, 0x59, 0x03, 0x7b, 0xb7, 0xa4, 0x63, 0x10, 0xb0,
+0xac, 0x0b, 0x85, 0x55, 0x67, 0xc3, 0x80, 0x37, 0x81, 0x01, 0x35, 0x56, 0xc1, 0xe6, 0x67, 0x20,
+0x10, 0xd9, 0x32, 0x2d, 0xa7, 0x01, 0xa6, 0x86, 0xc1, 0x85, 0x2a, 0x18, 0x55, 0x56, 0x67, 0xc0,
+0x80, 0x21, 0x1e, 0xb0, 0x20, 0x2c, 0x03, 0x1f, 0x90, 0x41, 0x33, 0x03, 0x12, 0x0d, 0x55, 0x30,
+0x54, 0x80, 0x67, 0x20, 0x06, 0x44, 0xcd, 0x33, 0x17, 0x65, 0x9b, 0x7e, 0x30, 0x88, 0xc3, 0x33,
+0xcc, 0x95, 0xe1, 0x51, 0x19, 0xc4, 0x4b, 0x8c, 0xa8, 0x9b, 0x51, 0x08, 0x10, 0x83, 0x75, 0x3c,
+0x2e, 0x64, 0x33, 0x3f, 0x5e, 0x51, 0x3b, 0xb0, 0x0a, 0x67, 0xcc, 0x5d, 0x4f, 0x7e, 0xc0, 0xce,
+0x1e, 0x8c, 0x4e, 0x1e, 0x33, 0x00, 0x82, 0x87, 0x93, 0x78, 0x28, 0xac, 0x67, 0xc5, 0x5c, 0x41,
+0x47, 0x51, 0xcc, 0x07, 0x5f, 0xc7, 0xc0, 0xaf, 0xa1, 0x0e, 0xdb, 0xc0, 0x6d, 0xb5, 0xf8, 0x30,
+0x15, 0x4c, 0x92, 0xff, 0x64, 0x67, 0x70, 0xd8, 0x07, 0x03, 0x03, 0x73, 0x63, 0x30, 0x88, 0x3e,
+0x33, 0x41, 0x28, 0x08, 0x56, 0x67, 0x22, 0x83, 0x33, 0xbe, 0x87, 0x7c, 0x82, 0x7c, 0x71, 0x7c,
+0x62, 0x8e, 0xd5, 0xd1, 0xca, 0x20, 0x33, 0x90, 0x8d, 0x00, 0xac, 0x82, 0x67, 0x16, 0x22, 0x5f,
+0x2a, 0x18, 0x85, 0xc2, 0x67, 0xc2, 0xd0, 0xf1, 0xc6, 0xef, 0x09, 0x05, 0xe9, 0xef, 0x8c, 0xa8,
+0x9b, 0x70, 0x31, 0x99, 0x58, 0xbc, 0x98, 0x93, 0x55, 0x30, 0xe6, 0x23, 0x84, 0x67, 0xc1, 0xe4,
+0xc7, 0x4f, 0xcc, 0x78, 0x98, 0xc5, 0x00, 0xca, 0x2a, 0x67, 0x58, 0xc2, 0x00, 0xc5, 0xbf, 0x56,
+0xbc, 0xc0, 0x40, 0x80, 0x83, 0x88, 0xea, 0x33, 0x16, 0x22, 0x00, 0x2a, 0x48, 0x3c, 0xca, 0x67,
+0x1d, 0xbf, 0xc9, 0x80, 0x59, 0xa2, 0xe0, 0x05, 0x99, 0x8a, 0xc8, 0xcf, 0x50, 0x49, 0x24, 0x41,
+0x14, 0x8a, 0xa0, 0x04, 0xbf, 0xc2, 0xa2, 0xcf, 0xc4, 0x0a, 0x54, 0x18, 0x33, 0x8f, 0xa8, 0x9b,
+0x02, 0x21, 0x30, 0x0a, 0xc7, 0x54, 0x30, 0x88, 0x85, 0x87, 0xa8, 0x32, 0x33, 0x46, 0xd4, 0x9b,
+0x40, 0x56, 0x84, 0xa2, 0x49, 0x15, 0x5f, 0xaa, 0x08, 0x41, 0xcc, 0x71, 0x18, 0x91, 0x33, 0x43,
+0x82, 0x17, 0x18, 0x51, 0x9b, 0x16, 0x04, 0xe2, 0xcc, 0x00, 0xad, 0x8c, 0xd3, 0x61, 0x44, 0x9b,
+0x20, 0x52, 0xb1, 0x0c, 0xbf, 0x65, 0x00, 0x1f, 0x23, 0x6a, 0x9b, 0x0d, 0x02, 0x23, 0xca, 0x00,
+0x9b, 0x80, 0x00, 0x47, 0x89, 0xa1, 0x8b, 0x02, 0xcf, 0x03, 0x02, 0x61, 0x10, 0x33, 0xa2, 0x02,
+0x80, 0x38, 0x9b, 0x28, 0x58, 0x05, 0xcb, 0xfb, 0x08, 0x49, 0xa3, 0x22, 0x83, 0x33, 0x44, 0x25,
+0x64, 0x64, 0x9b, 0xaa, 0x00, 0x61, 0x40, 0x65, 0x33, 0x20, 0x63, 0x61, 0x10, 0x33, 0x20, 0x46,
+0x54, 0x30, 0xc1, 0x54, 0x46, 0x9b, 0x03, 0x02, 0x35, 0x01, 0x2b, 0x23, 0xea, 0x34, 0x9b, 0x31,
+0x08, 0x58, 0x52, 0x65, 0x12, 0x80, 0x75, 0x11, 0x15, 0x4b, 0x95, 0x9b, 0x00, 0x46, 0x94, 0x9b,
+0x40, 0x10, 0x42, 0x0e, 0x00, 0x00, 0x10, 0x09, 0x0a, 0x61, 0x33, 0xc2, 0x88, 0x9b, 0x02, 0x11,
+0x63, 0x81, 0x08, 0xc3, 0x41, 0x04, 0x0a, 0x95, 0x33, 0xc0, 0x88, 0x9b, 0x14, 0xaa, 0x74, 0x22,
+0x23, 0x20, 0x26, 0x1d, 0x31, 0xc2, 0x33, 0x82, 0x00, 0xbf, 0x30, 0xa2, 0x9b, 0x11, 0x02, 0x62,
+0x64, 0x92, 0x21, 0x0c, 0x33, 0x20, 0x14, 0x2b, 0xa9, 0x82, 0x77, 0x44, 0x05, 0x06, 0x60, 0x9b,
+0x11, 0xe5, 0x00, 0x53, 0x90, 0x9b, 0x51, 0x30, 0x88, 0xe5, 0x33, 0x3c, 0x19, 0x5a, 0x39, 0xbf,
+0x59, 0x14, 0xcf, 0x59, 0x11, 0xa8, 0x00, 0x25, 0x09, 0x42, 0x05, 0xc0, 0x20, 0x33, 0x10, 0x2a,
+0x64, 0x51, 0xcf, 0x50, 0x41, 0x0c, 0x22, 0x33, 0x04, 0xaa, 0x58, 0x14, 0xcf, 0x01, 0x00, 0x83,
+0x18, 0x33, 0xa1, 0x00, 0x16, 0x05, 0xcf, 0x43, 0x40, 0x0d, 0xdb, 0xff, 0x0a, 0x54, 0x68, 0x69,
+0x73, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x04, 0x69, 0x7f, 0xfb, 0x6c, 0x65, 0x20, 0x68, 0x61, 0x0d,
+0x62, 0x65, 0x65, 0x6e, 0x20, 0x63, 0xdb, 0xfe, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x2f, 0x02,
+0x69, 0x06, 0xfd, 0x9b, 0x13, 0x79, 0x20, 0x43, 0x50, 0x49, 0x61, 0x64, 0x6f, 0xbf, 0x09, 0x56,
+0x31, 0x2e, 0x32, 0x30, 0x37, 0x43, 0x6f, 0x70, 0x7f, 0xf9, 0x79, 0x72, 0x69, 0x67, 0x68, 0x28,
+0x43, 0x29, 0x20, 0x31, 0x39, 0xbf, 0xee, 0x39, 0x33, 0x2d, 0x04, 0x36, 0x28, 0x6b, 0x6f, 0xfe,
+0xdb, 0x73, 0x74, 0x54, 0x40, 0x61, 0x63, 0x6d, 0x2e, 0x6f, 0x72, 0x67, 0xbb, 0xdb, 0x1f, 0x4b,
+0x0f, 0x61, 0x20, 0x4b, 0x15, 0x07, 0x1b, 0x29, 0x71, 0x70, 0x72, 0xbb, 0xff, 0x6f, 0x67, 0x72,
+0x61, 0x6d, 0x20, 0x6d, 0x61, 0x79, 0x6f, 0x20, 0x75, 0xd6, 0xba, 0x73, 0x63, 0x66, 0x72, 0x09,
+0x6b, 0xed, 0x6f, 0x66, 0x7a, 0x84, 0x36, 0xdf, 0x5a, 0x09, 0x7e, 0x20, 0x79, 0x6f, 0x75, 0xd6,
+0xb6, 0x72, 0x11, 0x77, 0x8d, 0x69, 0x00, 0xfc, 0x73, 0x6b, 0x2e, 0x0d, 0x0a, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x12, 0xff, 0xa4, 0xe8, 0x3a, 0x00, 0x72, 0xfa, 0x41, 0xe8, 0x2f, 0x00, 0xe3, 0x3b,
+0x73, 0xf9, 0x83, 0xe9, 0x03, 0x72, 0x06, 0x88, 0xcc, 0xac, 0xf7, 0xd0, 0x95, 0x31, 0xc9, 0xe8,
+0x1b, 0x00, 0x11, 0xc9, 0x75, 0x08, 0x41, 0xe8, 0x13, 0x00, 0x73, 0xfb, 0x41, 0x41, 0x81, 0xfd,
+0x00, 0xf3, 0x83, 0xd1, 0x01, 0x8d, 0x03, 0x96, 0xf3, 0xa4, 0x96, 0xeb, 0xc8, 0xe8, 0x02, 0x00,
+0x11, 0xc9, 0x01, 0xdb, 0x75, 0x04, 0xad, 0x11, 0xc0, 0x93, 0xc3, 0x5e, 0xb9, 0x01, 0x00, 0xac,
+0x2c, 0xe8, 0x3c, 0x01, 0x77, 0xf9, 0x8b, 0x1c, 0x86, 0xdf, 0x29, 0xf3, 0x89, 0x1c, 0xad, 0xe2,
+0xee, 0xc3 };
+
+Bit8u font_ega3_cpx[5455] = {
+0x81, 0xfc, 0xce, 0xe7, 0x77, 0x02, 0xcd, 0x20, 0xb9, 0x4f, 0x15, 0xbe, 0x4f, 0x16, 0xbf, 0x6e,
+0xe7, 0xbb, 0x00, 0x80, 0xfd, 0xf3, 0xa4, 0xfc, 0x87, 0xf7, 0x83, 0xee, 0xc6, 0x19, 0xed, 0x57,
+0x57, 0xe9, 0xed, 0xe5, 0x55, 0x50, 0x58, 0x21, 0x0b, 0x01, 0x04, 0x08, 0x10, 0x40, 0x37, 0xe4,
+0x59, 0xc7, 0x7f, 0x53, 0x00, 0xe6, 0xb7, 0x14, 0x06, 0x45, 0xbb, 0xfc, 0xff, 0x46, 0x4f, 0x4e,
+0x54, 0x20, 0x00, 0x00, 0x01, 0x6e, 0x39, 0x01, 0x17, 0x06, 0xfd, 0xfd, 0x06, 0x00, 0x1c, 0x00,
+0x4d, 0x26, 0x0e, 0x45, 0x47, 0x41, 0xc9, 0xcd, 0x1e, 0x20, 0x03, 0x03, 0xb7, 0xd8, 0x35, 0x24,
+0x0c, 0x12, 0x26, 0xb2, 0xd8, 0x10, 0x08, 0x0a, 0x00, 0xed, 0xbf, 0x7e, 0x81, 0xa5, 0x81, 0x81,
+0xbd, 0x99, 0x03, 0x7f, 0xb0, 0x7e, 0x0f, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xfc, 0xce, 0xff,
+0xff, 0x00, 0x6c, 0xfe, 0x87, 0xdd, 0x7c, 0x38, 0x10, 0x30, 0x10, 0x1f, 0xd8, 0x38, 0x7c, 0x0e,
+0x18, 0x3c, 0x3c, 0xd6, 0xb6, 0xe7, 0x00, 0x18, 0x06, 0x0f, 0xcc, 0x4d, 0x7e, 0x3b, 0x0f, 0x76,
+0xb6, 0x22, 0x18, 0x09, 0x3b, 0x76, 0xff, 0x00, 0xe7, 0xc3, 0x5f, 0xc7, 0xde, 0x00, 0x1f, 0x3c,
+0x66, 0x5f, 0xf2, 0x42, 0x42, 0x66, 0x3c, 0xc3, 0x99, 0xbd, 0x3f, 0xe4, 0xbd, 0x99, 0xc3, 0x1e,
+0x0e, 0x1a, 0x32, 0x7b, 0xdd, 0x78, 0xcc, 0x00, 0x78, 0x20, 0x2d, 0xd7, 0xdc, 0x00, 0x4f, 0x61,
+0xf7, 0x33, 0x0f, 0x3f, 0x33, 0x3f, 0x30, 0x00, 0x1f, 0x79, 0x70, 0xf0, 0xe0, 0x00, 0x7f, 0x63,
+0x7f, 0x63, 0xb3, 0x37, 0x67, 0xe7, 0xe6, 0xc0, 0x7c, 0xb0, 0xdb, 0x18, 0xdb, 0xa0, 0x3c, 0xdb,
+0x2e, 0xb7, 0xff, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0x2d, 0xc0, 0x80, 0xff, 0x63, 0xbf,
+0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x2d, 0xc9, 0x06, 0x02, 0x2b, 0xef, 0x6c,
+0x5e, 0x3c, 0x00, 0x73, 0x93, 0x66, 0x07, 0x5f, 0xdc, 0x06, 0xdb, 0x00, 0x7b, 0x1b, 0x7f, 0xf3,
+0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x80, 0xfc, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00,
+0xc8, 0xcc, 0xfe, 0x4f, 0x0d, 0x6c, 0x7e, 0x0f, 0x17, 0xec, 0x00, 0x0f, 0x00, 0xdf, 0xb2, 0x70,
+0x11, 0x0c, 0x2e, 0xd9, 0xfe, 0x0c, 0x0c, 0x00, 0x80, 0xfc, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00,
+0xcc, 0xca, 0xc0, 0x5c, 0x2e, 0x27, 0xa1, 0x28, 0x6c, 0x28, 0x0a, 0xcb, 0x0e, 0x9f, 0xf6, 0x16,
+0xa0, 0x7c, 0x7d, 0x8a, 0x61, 0x4b, 0x0e, 0x38, 0xaf, 0x17, 0xd8, 0x00, 0x18, 0xec, 0x95, 0x3c,
+0x8c, 0x8f, 0x64, 0xdd, 0xe9, 0x24, 0x20, 0x31, 0x37, 0x6c, 0x5e, 0x03, 0xdf, 0x66, 0xad, 0xf1,
+0xc2, 0xc0, 0x7c, 0x06, 0xc3, 0xda, 0x06, 0x86, 0xef, 0xc1, 0xe4, 0xdb, 0xc2, 0xc6, 0xae, 0x30,
+0x60, 0xc6, 0x86, 0x85, 0xc2, 0x0e, 0x0c, 0x58, 0xc3, 0x76, 0xdc, 0xaf, 0x76, 0xbd, 0xb0, 0x2d,
+0x00, 0x60, 0x4e, 0x31, 0x37, 0x2b, 0x00, 0x37, 0x6b, 0xe5, 0x0f, 0x08, 0xb2, 0x31, 0x00, 0x18,
+0xe2, 0xc2, 0xc2, 0xde, 0xff, 0xe5, 0x30, 0xb0, 0xa2, 0xc0, 0x96, 0xed, 0x14, 0x0d, 0xd8, 0x31,
+0xfe, 0xb2, 0x95, 0xd7, 0x31, 0xbf, 0xc2, 0xe2, 0x9f, 0xc0, 0x80, 0x1f, 0x26, 0xae, 0xd6, 0xd6,
+0xc1, 0x0e, 0xb2, 0x27, 0x0b, 0xc2, 0x38, 0x78, 0x81, 0x2e, 0x9e, 0x3c, 0x7c, 0xc6, 0xc6, 0xfe,
+0xdb, 0xc9, 0x0f, 0x06, 0x3c, 0x02, 0x06, 0x7f, 0x08, 0xdf, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0x13,
+0xba, 0xfe, 0xb0, 0x1e, 0x8d, 0xc0, 0x9a, 0xfe, 0xfc, 0x1f, 0x72, 0x77, 0x38, 0x60, 0x0f, 0xc6,
+0xc6, 0xc6, 0xcc, 0x1d, 0xfe, 0x3f, 0xf3, 0x6e, 0xd8, 0x4f, 0x1a, 0x2f, 0x60, 0x1f, 0x0b, 0x61,
+0xbb, 0x7e, 0x3f, 0x0c, 0x78, 0xba, 0x61, 0x6f, 0x04, 0x0f, 0x6c, 0x72, 0x30, 0x9e, 0x52, 0x43,
+0x35, 0x06, 0xc2, 0xd9, 0x5b, 0x02, 0x00, 0x5e, 0x2c, 0x1b, 0x23, 0x84, 0xc9, 0x5f, 0x0c, 0xdf,
+0x76, 0xb6, 0x10, 0xde, 0x00, 0x8c, 0xc2, 0xdc, 0xc4, 0x2e, 0x67, 0x3b, 0x6c, 0x90, 0xfe, 0x8d,
+0x67, 0x0f, 0xfc, 0x66, 0xf1, 0x90, 0x9e, 0x7c, 0xfc, 0x6f, 0xba, 0xc2, 0xef, 0x00, 0xc2, 0x3e,
+0x9d, 0xa1, 0x10, 0xf8, 0x6c, 0x67, 0x4f, 0x6c, 0xf8, 0x0f, 0xfe, 0x90, 0xff, 0x66, 0x62, 0x68,
+0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0xd8, 0x96, 0x00, 0xf0, 0x3f, 0xc9, 0x0f, 0xde, 0xc6, 0xc6,
+0x66, 0x3a, 0xec, 0xbd, 0x69, 0x6e, 0x6f, 0x4d, 0x38, 0x3c, 0x7d, 0xc6, 0x64, 0x30, 0x1e, 0x0d,
+0x2e, 0x29, 0xef, 0xe6, 0xd6, 0xed, 0x6a, 0x78, 0x78, 0x74, 0xe6, 0x0f, 0x16, 0x7b, 0xf0, 0x60,
+0x00, 0x6f, 0xf2, 0x6f, 0x4f, 0xee, 0xfe, 0xfe, 0xd6, 0x7e, 0x5b, 0x0f, 0xe6, 0xf6, 0xfe, 0xde,
+0xce, 0x0f, 0x4b, 0x6f, 0xc6, 0xb3, 0x25, 0xdf, 0x84, 0x7d, 0x41, 0xf0, 0x1f, 0x92, 0xfc, 0xd6,
+0xde, 0x7c, 0x0c, 0x0e, 0x25, 0xfb, 0xe3, 0xe6, 0x1f, 0x56, 0x28, 0xe2, 0x50, 0x3f, 0x61, 0x4f,
+0x7e, 0x7e, 0x5a, 0xaf, 0x42, 0x36, 0xc6, 0x5f, 0xe4, 0xc1, 0x0f, 0x6c, 0x38, 0x10, 0xc8, 0xbf,
+0xd6, 0xd6, 0xd6, 0xfe, 0xee, 0x6c, 0xcd, 0x74, 0x6c, 0x8e, 0x61, 0xac, 0x61, 0x9f, 0xce, 0x61,
+0xd9, 0x4f, 0x1b, 0xe4, 0x63, 0x86, 0x7f, 0xc2, 0x82, 0x3d, 0x3c, 0x30, 0x00, 0x5f, 0xc1, 0x1f,
+0xc1, 0x70, 0x4e, 0xd6, 0x38, 0x1c, 0xaf, 0x3c, 0x0c, 0x8c, 0xc1, 0x00, 0x3c, 0xdc, 0x11, 0x52,
+0xe6, 0x9d, 0xa6, 0x6c, 0xff, 0x7d, 0x80, 0x18, 0x78, 0x0c, 0x7c, 0xc2, 0x25, 0xaf, 0xe0, 0x19,
+0x2c, 0xc2, 0x78, 0xe2, 0xcf, 0x63, 0xc2, 0x02, 0x00, 0x77, 0x98, 0xdf, 0x1c, 0x69, 0x65, 0x25,
+0x00, 0x2f, 0x90, 0xb1, 0x1f, 0xfe, 0x78, 0x1f, 0x36, 0x32, 0x30, 0x78, 0xaf, 0xbe, 0xcb, 0xcf,
+0x76, 0x2e, 0xcc, 0x3b, 0x14, 0x5f, 0xd2, 0x5f, 0xc6, 0xf3, 0x6c, 0x76, 0xe6, 0xdc, 0x20, 0x4c,
+0x38, 0x4f, 0x6d, 0x69, 0x09, 0x00, 0xdb, 0x19, 0x62, 0x00, 0x17, 0xce, 0x98, 0x2f, 0x00, 0xe6,
+0x92, 0x33, 0x2f, 0x38, 0x46, 0x17, 0x00, 0xec, 0x00, 0xc9, 0xba, 0x00, 0xc6, 0x0f, 0xd9, 0x8c,
+0xdc, 0xa1, 0x9f, 0x2d, 0x61, 0x9f, 0x3e, 0x96, 0x1f, 0x04, 0xf0, 0xd2, 0xc2, 0x9f, 0xd2, 0xa3,
+0x1d, 0xdc, 0x9f, 0x1f, 0x09, 0x2c, 0xe4, 0x2e, 0x81, 0xdf, 0x10, 0xf6, 0xdb, 0xd9, 0xfc, 0xde,
+0x36, 0x1c, 0x1f, 0xc2, 0x4b, 0x3d, 0x0f, 0x30, 0x06, 0x00, 0x08, 0x58, 0x0f, 0x96, 0xf0, 0x00,
+0x9a, 0x66, 0xea, 0x1b, 0x38, 0x75, 0x2f, 0xba, 0x16, 0x04, 0x0c, 0xe0, 0x90, 0x52, 0xcc, 0x39,
+0x7b, 0x4f, 0xfe, 0xef, 0x0e, 0xac, 0xa7, 0x70, 0x0e, 0xf7, 0x61, 0x90, 0x3f, 0x7b, 0x2f, 0x1b,
+0x23, 0x24, 0x08, 0x5f, 0xc9, 0xdc, 0xdf, 0x91, 0x06, 0xe2, 0xf4, 0x61, 0x4f, 0x1e, 0x36, 0x66,
+0xf9, 0x84, 0xef, 0xfe, 0x62, 0x60, 0x60, 0x25, 0x2d, 0xff, 0x92, 0x69, 0xcf, 0x71, 0x4b, 0x76,
+0xf0, 0x3f, 0x00, 0x2d, 0x31, 0xa7, 0x81, 0x1b, 0x20, 0xff, 0xe9, 0x6b, 0x70, 0x54, 0x54, 0x70,
+0x03, 0x04, 0x5a, 0x07, 0x3f, 0x77, 0x0b, 0x78, 0xce, 0xde, 0xf6, 0x73, 0xb3, 0xe6, 0x7f, 0x14,
+0x0f, 0x48, 0x09, 0xef, 0x1d, 0xce, 0x6c, 0xef, 0xd2, 0x85, 0x6f, 0xc6, 0xef, 0xac, 0x10, 0x4f,
+0x14, 0xd2, 0xef, 0x58, 0xb3, 0x1b, 0xdf, 0x52, 0x5b, 0xe0, 0xff, 0xb1, 0x12, 0xdf, 0x13, 0x49,
+0xdf, 0x7e, 0xc7, 0x65, 0xbf, 0x7c, 0x6b, 0xe4, 0xa5, 0xcf, 0x7c, 0x10, 0x38, 0x64, 0x0b, 0x6e,
+0xec, 0xd0, 0xff, 0x84, 0x3f, 0xc8, 0xb6, 0x00, 0x00, 0x76, 0xc8, 0xd6, 0xfe, 0x0f, 0xc1, 0x42,
+0x2f, 0x6e, 0xe9, 0xf8, 0xb0, 0xb0, 0xea, 0x36, 0x00, 0x0e, 0x58, 0x4f, 0xf6, 0xde, 0xcb, 0xea,
+0x00, 0xf6, 0xff, 0x18, 0x08, 0x9f, 0x4b, 0x6e, 0x5f, 0x26, 0x3e, 0x26, 0x7b, 0xba, 0xcc, 0x4f,
+0xf6, 0xcc, 0xcf, 0x7a, 0x04, 0xda, 0x40, 0x7a, 0xb0, 0xce, 0xef, 0x18, 0xbc, 0x06, 0x7c, 0xaf,
+0x4e, 0x58, 0x22, 0x7c, 0x18, 0xc9, 0x5f, 0x22, 0x12, 0xd2, 0x62, 0x0f, 0x3d, 0x1c, 0x92, 0xff,
+0xc3, 0xe4, 0x25, 0x5f, 0xfe, 0xc0, 0xf0, 0xb1, 0x02, 0x7c, 0xcc, 0x92, 0xff, 0xa2, 0x01, 0x6b,
+0x01, 0x1f, 0x2c, 0x08, 0xf1, 0x0f, 0x16, 0xf2, 0xc6, 0x7c, 0x26, 0x3d, 0x02, 0xef, 0x59, 0xc8,
+0x6f, 0x0f, 0xd2, 0x63, 0x02, 0xef, 0x30, 0x06, 0x01, 0x14, 0x52, 0xef, 0x9e, 0xbd, 0x1c, 0x1f,
+0x11, 0x44, 0xf2, 0xc2, 0x01, 0x55, 0xaa, 0x90, 0x17, 0xdd, 0x77, 0x02, 0xbb, 0x18, 0x00, 0x85,
+0xec, 0xf8, 0x0d, 0x67, 0x93, 0x0f, 0x36, 0x00, 0xf6, 0x58, 0x90, 0x36, 0x80, 0x25, 0x6c, 0x0f,
+0xe7, 0x91, 0x2f, 0xf6, 0x06, 0x4b, 0x60, 0x00, 0x3d, 0x42, 0x36, 0x1f, 0x64, 0xd1, 0x2f, 0xee,
+0x59, 0xb0, 0x11, 0x0f, 0x35, 0x2c, 0x8f, 0xd9, 0x90, 0x00, 0xf8, 0xbf, 0x84, 0x0d, 0x1f, 0x2f,
+0x43, 0xf2, 0x18, 0x18, 0xff, 0xff, 0x6b, 0x24, 0x72, 0x6b, 0x0b, 0x1f, 0x3f, 0x64, 0x23, 0x2d,
+0x90, 0x30, 0x1f, 0x0f, 0x4f, 0xd8, 0x37, 0xbf, 0x37, 0x30, 0x3f, 0x2a, 0x61, 0x6d, 0x17, 0x56,
+0xbf, 0x1f, 0xf7, 0xb0, 0x65, 0x5f, 0x6d, 0x85, 0x6c, 0xf7, 0x3f, 0xcd, 0x92, 0x2f, 0x1f, 0xcd,
+0x96, 0x2f, 0x3f, 0xc1, 0x96, 0x2f, 0xdd, 0x85, 0x84, 0xff, 0x2f, 0xd9, 0x91, 0x5f, 0xff, 0xdf,
+0x48, 0x18, 0x86, 0x2c, 0x59, 0xaf, 0xdf, 0x12, 0x36, 0x3f, 0x17, 0xf6, 0xef, 0x3f, 0x3f, 0x2c,
+0x24, 0x4f, 0x82, 0x90, 0x2d, 0x30, 0x12, 0x5f, 0xb2, 0x91, 0xaf, 0x4f, 0x85, 0x6c, 0xff, 0x00,
+0x48, 0x08, 0xaf, 0x90, 0x36, 0x24, 0x1e, 0xcf, 0x03, 0x97, 0x0f, 0x94, 0x90, 0x90, 0xaf, 0xc4,
+0x8b, 0x87, 0x30, 0x98, 0xbf, 0xff, 0x91, 0xad, 0x1f, 0x38, 0x81, 0x01, 0x3c, 0x90, 0x32, 0x9f,
+0x93, 0x27, 0x02, 0x38, 0x10, 0x0c, 0xca, 0x7c, 0x10, 0xcf, 0x59, 0x83, 0xcf, 0x7a, 0xb3, 0x0f,
+0x3d, 0xd6, 0x32, 0xaf, 0xff, 0xcb, 0x12, 0x0f, 0x81, 0x64, 0x2f, 0x02, 0xcc, 0x80, 0x01, 0x8f,
+0x26, 0xb0, 0x01, 0x09, 0xbc, 0xff, 0x02, 0x09, 0x2b, 0x9f, 0x20, 0x5d, 0x5f, 0x3e, 0x23, 0x70,
+0xcc, 0x01, 0xcc, 0x81, 0x65, 0xcf, 0x01, 0x32, 0x1a, 0xce, 0xaf, 0x98, 0x30, 0x3f, 0xdc, 0xb2,
+0xbf, 0x0f, 0x65, 0xd0, 0xf5, 0xcf, 0x60, 0x2e, 0x12, 0x1f, 0x23, 0xa3, 0xaf, 0x2c, 0x66, 0x3f,
+0xbf, 0x05, 0xb7, 0x0f, 0x08, 0x46, 0x46, 0x2f, 0x16, 0x13, 0x7f, 0x3f, 0x32, 0x58, 0x1f, 0x2c,
+0xde, 0x6f, 0x3f, 0xf4, 0x25, 0x0f, 0xfe, 0x6d, 0x64, 0x4f, 0x1f, 0xd8, 0x90, 0xfc, 0x4d, 0x23,
+0xa3, 0x1f, 0xb0, 0x78, 0x5f, 0x2f, 0x33, 0x97, 0xee, 0x00, 0x06, 0x90, 0x00, 0x0e, 0xc1, 0x0b,
+0x05, 0x1c, 0x61, 0x03, 0x7e, 0x07, 0x16, 0x01, 0x7e, 0x80, 0x04, 0xff, 0x93, 0x20, 0xfd, 0x95,
+0x04, 0xfb, 0x4a, 0x82, 0xf9, 0x38, 0x41, 0xf7, 0x8c, 0x24, 0xf5, 0x58, 0x12, 0xf3, 0x8c, 0x24,
+0xf1, 0x5d, 0x12, 0xef, 0x78, 0x24, 0x98, 0x27, 0xec, 0x4e, 0xb0, 0xeb, 0x2c, 0x09, 0xea, 0x82,
+0x13, 0xe9, 0x49, 0x70, 0xe8, 0xe7, 0x98, 0x4a, 0x24, 0x82, 0x12, 0xe5, 0x04, 0x4b, 0xe4, 0x70,
+0xaa, 0xe3, 0x32, 0x48, 0x49, 0xe1, 0x24, 0x24, 0xde, 0x18, 0x12, 0xdd, 0x30, 0x24, 0xdb, 0x1c,
+0x49, 0xda, 0x00, 0x92, 0xa0, 0xd7, 0xac, 0x37, 0x0d, 0x01, 0x49, 0xb0, 0xd2, 0x27, 0x08, 0xd1,
+0x84, 0x04, 0xcf, 0x93, 0x20, 0xcd, 0x95, 0x04, 0xcb, 0x4c, 0x82, 0xc9, 0x54, 0x12, 0xc7, 0x45,
+0x82, 0xc3, 0x93, 0xc0, 0x76, 0xc1, 0x84, 0x04, 0xbe, 0x93, 0x20, 0xbd, 0x96, 0x04, 0xbc, 0xd0,
+0x09, 0xbb, 0x92, 0xe0, 0x5a, 0xb9, 0x92, 0xe0, 0x76, 0x5f, 0x48, 0x90, 0xb5, 0x04, 0x2d, 0xc8,
+0xb2, 0x80, 0xb3, 0x20, 0x38, 0x41, 0xb0, 0x81, 0x24, 0xae, 0x30, 0x60, 0x4b, 0xfe, 0x16, 0x48,
+0xa8, 0x2b, 0x09, 0xa5, 0x60, 0x52, 0x44, 0xe0, 0x92, 0xa3, 0x7e, 0x11, 0x90, 0xa1, 0xcb, 0xe8,
+0xf0, 0x5f, 0x09, 0x0e, 0xa3, 0x9d, 0x09, 0x2e, 0x1e, 0x9b, 0x78, 0x01, 0x1b, 0xb7, 0x24, 0x99,
+0x0d, 0x76, 0x82, 0x97, 0xd4, 0x90, 0x60, 0x95, 0x24, 0x38, 0x7c, 0x93, 0x49, 0xb0, 0x92, 0x93,
+0x20, 0x80, 0x95, 0x04, 0x8e, 0x04, 0xb1, 0xb3, 0x92, 0xc0, 0x71, 0x8b, 0x48, 0xb0, 0x88, 0x24,
+0x48, 0x87, 0x09, 0x5e, 0x3c, 0x84, 0x02, 0x29, 0xd3, 0x90, 0x4a, 0x81, 0x24, 0x98, 0xfc, 0x7f,
+0x21, 0xb0, 0xce, 0x70, 0x82, 0x7d, 0xb2, 0x4e, 0x7c, 0xcb, 0xb2, 0xd7, 0xaa, 0x0d, 0xe0, 0xc0,
+0xd5, 0xf5, 0x92, 0x77, 0x66, 0x3a, 0x6b, 0x21, 0x25, 0xc3, 0x82, 0x09, 0x3c, 0xea, 0x40, 0x71,
+0x78, 0x52, 0x4b, 0x7f, 0x7e, 0x12, 0x4c, 0xf0, 0x6c, 0x1d, 0x5a, 0x78, 0x60, 0x49, 0x7a, 0xd9,
+0x92, 0x69, 0xd1, 0x26, 0xac, 0xb6, 0xfc, 0x25, 0xa4, 0x64, 0x9c, 0xe0, 0x1b, 0x62, 0x07, 0x12,
+0x61, 0xe6, 0x96, 0x04, 0x5f, 0x53, 0xc3, 0x37, 0x7d, 0xc0, 0xb2, 0x99, 0x82, 0x85, 0x0a, 0x41,
+0x42, 0x58, 0x33, 0x22, 0x57, 0xd7, 0x95, 0x62, 0x0d, 0x1c, 0x90, 0xe0, 0x66, 0x53, 0x6d, 0x36,
+0x45, 0xf7, 0x2c, 0x81, 0x8c, 0x30, 0x1c, 0x12, 0x4f, 0x3c, 0x56, 0x12, 0x4c, 0x06, 0x09, 0x4b,
+0x3c, 0x20, 0x09, 0x49, 0x90, 0x60, 0xff, 0x45, 0x48, 0x0c, 0x83, 0x20, 0xc1, 0x76, 0x41, 0x12,
+0x6f, 0xb5, 0x5f, 0xc1, 0x4b, 0xc3, 0xd9, 0x25, 0x3d, 0x76, 0x1b, 0x48, 0x72, 0xfe, 0x82, 0x25,
+0x39, 0x7c, 0x60, 0x40, 0x38, 0x76, 0x04, 0x31, 0x96, 0xc1, 0x80, 0x35, 0xe6, 0x70, 0x20, 0x33,
+0x3c, 0x60, 0x49, 0x31, 0x90, 0x90, 0x2f, 0xc5, 0x86, 0xbf, 0x26, 0x90, 0xe0, 0x29, 0x2b, 0x61,
+0x91, 0x09, 0x42, 0x6a, 0x37, 0x26, 0x6c, 0x7c, 0x1b, 0x7c, 0x96, 0x1d, 0xc1, 0x8b, 0x03, 0x09,
+0x21, 0xf2, 0xb2, 0xf0, 0x37, 0x70, 0x1c, 0x93, 0x60, 0x1d, 0x96, 0x04, 0x1c, 0x09, 0x09, 0x1a,
+0x6c, 0x09, 0xbf, 0x0d, 0x20, 0xc1, 0x16, 0x16, 0x2e, 0x19, 0x44, 0x2c, 0x02, 0x72, 0x24, 0x18,
+0x7c, 0x11, 0xe0, 0x91, 0x83, 0xe0, 0x92, 0x0f, 0x0e, 0x90, 0x90, 0x0d, 0x1c, 0x48, 0x0b, 0x70,
+0x04, 0x12, 0x09, 0xc1, 0xa4, 0x66, 0x2a, 0x21, 0x05, 0x00, 0xc1, 0xe0, 0x4a, 0x9a, 0x52, 0x7f,
+0x61, 0xc0, 0xff, 0xe0, 0xbd, 0xed, 0x37, 0xd2, 0x32, 0xfc, 0x7f, 0xd0, 0x42, 0xcc, 0xac, 0x19,
+0xf8, 0x97, 0x06, 0xac, 0xe5, 0x0c, 0x48, 0xf3, 0x25, 0x25, 0x71, 0x98, 0x6c, 0x61, 0xc6, 0x25,
+0x0c, 0xed, 0x69, 0x25, 0xc5, 0x71, 0x01, 0x21, 0x90, 0x86, 0xe7, 0x7f, 0x72, 0x2d, 0x43, 0xb0,
+0x42, 0x63, 0x28, 0x19, 0xde, 0x80, 0x43, 0xda, 0x38, 0x01, 0xc9, 0xdb, 0x03, 0x86, 0xda, 0xd8,
+0x01, 0x09, 0x03, 0x86, 0xd7, 0xd4, 0x24, 0xa4, 0x00, 0x13, 0x06, 0xd2, 0xf0, 0x32, 0xe0, 0xf0,
+0xb0, 0xbc, 0xd0, 0x61, 0x40, 0xce, 0x82, 0x2b, 0x5f, 0x79, 0x0c, 0x58, 0xcb, 0x80, 0xd9, 0x8b,
+0xa0, 0xf9, 0xc9, 0xf6, 0xcc, 0x3a, 0x30, 0xc7, 0xce, 0x10, 0x40, 0x71, 0x8c, 0x19, 0xc3, 0xbb,
+0x0c, 0xb8, 0xfc, 0xc0, 0x07, 0x0c, 0xbf, 0x65, 0xc0, 0xf0, 0x4d, 0xf4, 0x80, 0xbc, 0xb1, 0x94,
+0x7f, 0xb8, 0x92, 0x0a, 0xc1, 0x1d, 0x92, 0xdb, 0x1b, 0x1c, 0x06, 0x65, 0xc0, 0xa4, 0x65, 0x87,
+0xc2, 0x0d, 0x49, 0x87, 0xc2, 0x71, 0x36, 0x6c, 0x61, 0x0d, 0x60, 0x48, 0xc2, 0x63, 0x19, 0xac,
+0x75, 0x25, 0x2d, 0x71, 0xb0, 0x60, 0xc6, 0x64, 0x40, 0xa3, 0x0c, 0x08, 0x9f, 0x80, 0xab, 0xf8,
+0xc0, 0xca, 0x9b, 0x6f, 0x61, 0x99, 0x70, 0x0c, 0x38, 0x95, 0x06, 0xac, 0x93, 0x80, 0x41, 0xc0,
+0xca, 0x8f, 0x0c, 0x78, 0x8d, 0x8b, 0x06, 0xa4, 0x89, 0x03, 0x56, 0x87, 0x80, 0x21, 0x1f, 0xb0,
+0xc2, 0x83, 0x08, 0x19, 0x51, 0xac, 0x0c, 0x7d, 0x52, 0x06, 0x7b, 0x64, 0x03, 0x77, 0x1f, 0x01,
+0x23, 0x0c, 0x98, 0x75, 0x73, 0x07, 0xac, 0x71, 0xc2, 0x80, 0x6f, 0x1e, 0x30, 0x6d, 0x29, 0x03,
+0x6b, 0x96, 0x01, 0x69, 0x23, 0x03, 0x67, 0x95, 0x01, 0x65, 0x06, 0xb8, 0x1b, 0x61, 0xc0, 0x15,
+0x3f, 0x1b, 0x61, 0x5d, 0xd1, 0x61, 0xc0, 0x59, 0x06, 0x4c, 0xff, 0x55, 0xc0, 0x15, 0xf8, 0x08,
+0x60, 0x51, 0x29, 0x0c, 0x4d, 0xa4, 0x03, 0x4f, 0x55, 0x81, 0xb6, 0x0d, 0xc1, 0x96, 0x85, 0x79,
+0x2c, 0x03, 0x47, 0x46, 0x18, 0x1f, 0xd6, 0xc2, 0x1b, 0x61, 0x24, 0x30, 0x3c, 0xcb, 0x19, 0x30,
+0xfc, 0x3d, 0xc0, 0x6b, 0x61, 0x60, 0x84, 0xf5, 0xc0, 0x30, 0x38, 0x2b, 0x61, 0x36, 0x5f, 0x92,
+0x2d, 0x0d, 0x58, 0xc9, 0x29, 0x61, 0x40, 0xb2, 0x7d, 0x30, 0x61, 0x2e, 0xf0, 0x1c, 0x18, 0x2c,
+0xfc, 0xb3, 0x06, 0x2a, 0x89, 0xae, 0x59, 0x62, 0x61, 0xc0, 0x80, 0xcc, 0x27, 0x42, 0xe8, 0xce,
+0x59, 0xac, 0xc3, 0x17, 0x27, 0xa0, 0xbd, 0x30, 0x1c, 0xd6, 0x5c, 0x48, 0xf5, 0x10, 0x08, 0xcd,
+0x1b, 0x59, 0xc4, 0x5b, 0x37, 0xd6, 0x96, 0xa7, 0x0d, 0xe6, 0x0b, 0x8b, 0xe9, 0x89, 0x09, 0x4f,
+0x37, 0x85, 0xc5, 0xe9, 0x49, 0xbc, 0x37, 0x37, 0xd1, 0x6e, 0x0d, 0xfe, 0x00, 0x2c, 0x64, 0xc0,
+0x1b, 0x8b, 0x17, 0x43, 0xdb, 0x78, 0x0b, 0x53, 0xc0, 0x92, 0x29, 0x80, 0x61, 0x09, 0xe4, 0xc8,
+0x05, 0x08, 0x0e, 0xb0, 0xfc, 0xc0, 0x01, 0xfb, 0x06, 0x30, 0xf6, 0xf5, 0x4c, 0x80, 0x6c, 0xee,
+0x0b, 0x02, 0xe9, 0x06, 0xe6, 0x60, 0x38, 0x83, 0x0d, 0x1c, 0xbe, 0x0f, 0xfe, 0xb7, 0x60, 0x02,
+0xd7, 0x40, 0x10, 0xd1, 0x80, 0x20, 0xcb, 0xf0, 0x41, 0xc5, 0x0f, 0x07, 0x0f, 0x7d, 0x01, 0x0c,
+0xbf, 0xba, 0x03, 0x04, 0xb4, 0x50, 0x80, 0xb3, 0xc0, 0x0f, 0xae, 0x67, 0xe6, 0xc0, 0x06, 0x30,
+0xa7, 0xa8, 0x75, 0x80, 0x80, 0xa1, 0x03, 0x2c, 0xef, 0x02, 0x9b, 0x38, 0xc0, 0x02, 0x42, 0x00,
+0x06, 0x33, 0x06, 0x98, 0x8f, 0x8a, 0xdd, 0x00, 0x89, 0x00, 0x3e, 0x61, 0xde, 0xdb, 0x49, 0x3c,
+0x86, 0xfd, 0xd0, 0xc0, 0xf7, 0x27, 0x26, 0xff, 0x9d, 0x26, 0x72, 0x35, 0x12, 0xe0, 0x0f, 0x65,
+0x38, 0x40, 0x5f, 0xb8, 0x04, 0x59, 0x24, 0x03, 0x14, 0xed, 0x12, 0xf0, 0x80, 0x9f, 0xf0, 0x1e,
+0x72, 0x2e, 0x1b, 0x08, 0x3d, 0x46, 0xf0, 0xda, 0x72, 0x25, 0xa3, 0x09, 0x31, 0x57, 0x94, 0xf8,
+0x18, 0x3e, 0x60, 0xc0, 0x68, 0x28, 0x90, 0x9c, 0xcc, 0x80, 0x07, 0x23, 0x38, 0xb2, 0x7a, 0x1e,
+0x76, 0xcc, 0x8b, 0x55, 0x3e, 0x74, 0x22, 0x40, 0x0d, 0x09, 0xf0, 0x13, 0x06, 0x4b, 0x80, 0x00,
+0x84, 0x61, 0x2b, 0x83, 0x2b, 0xb7, 0x18, 0xe8, 0x74, 0x0c, 0xf7, 0xd6, 0xb9, 0x72, 0xfe, 0xdd,
+0x3e, 0x88, 0xc2, 0xfd, 0x1c, 0x02, 0x1d, 0xbb, 0x2e, 0xb3, 0x0a, 0xca, 0xef, 0xab, 0x1e, 0xc5,
+0xfc, 0x0f, 0x47, 0x57, 0xbf, 0xfc, 0x05, 0x62, 0x85, 0xf3, 0x79, 0xef, 0x3d, 0x4f, 0x02, 0x07,
+0x17, 0xab, 0x7e, 0xab, 0x74, 0x37, 0xab, 0x62, 0x77, 0x5e, 0xbd, 0x67, 0x81, 0x95, 0xac, 0x94,
+0xbe, 0x7a, 0x8f, 0x8d, 0x7c, 0xdc, 0xac, 0x88, 0x07, 0xde, 0x26, 0x86, 0xc0, 0x78, 0x87, 0x02,
+0xbd, 0x01, 0xf6, 0x43, 0x1b, 0xc3, 0x38, 0x57, 0x06, 0xd0, 0x6b, 0xe3, 0xab, 0x69, 0xfe, 0xee,
+0x34, 0x1b, 0xfe, 0x07, 0xab, 0xcf, 0xef, 0x1f, 0xce, 0x57, 0x20, 0x90, 0x76, 0xdb, 0x8c, 0xf3,
+0x2f, 0x1e, 0x0d, 0x56, 0x45, 0xe6, 0x35, 0x18, 0x7f, 0xf0, 0x2c, 0x66, 0x39, 0xc3, 0x7a, 0x35,
+0x2f, 0x02, 0xbd, 0x2e, 0x9e, 0xc4, 0x1b, 0x6f, 0xe4, 0xef, 0x97, 0x0f, 0xce, 0x7c, 0x0e, 0xd7,
+0x5e, 0x37, 0x5f, 0xa8, 0xb0, 0x3a, 0x7f, 0x7e, 0x45, 0x1a, 0x46, 0xfd, 0x15, 0x84, 0x56, 0xde,
+0x20, 0xf8, 0x00, 0x0c, 0x1e, 0x2f, 0x9b, 0x67, 0xf7, 0xf4, 0x27, 0x0b, 0x32, 0x6f, 0x82, 0x54,
+0x3c, 0xdf, 0xd4, 0x01, 0x97, 0x4e, 0xca, 0xd9, 0xd5, 0x3c, 0x18, 0xb8, 0xea, 0x61, 0x71, 0x48,
+0xba, 0x86, 0x65, 0x4a, 0xd7, 0x1a, 0x8b, 0xe0, 0x8a, 0xdc, 0x3a, 0xcc, 0xcc, 0x7f, 0x34, 0x24,
+0x26, 0x62, 0xf7, 0xe0, 0x7c, 0x97, 0x60, 0xf8, 0xa6, 0xce, 0x47, 0x76, 0x09, 0x4e, 0xb9, 0xf8,
+0x2f, 0x92, 0xef, 0xd4, 0xaf, 0xe4, 0x7f, 0xa7, 0x0a, 0x69, 0x84, 0x3d, 0xb8, 0x3c, 0x17, 0x00,
+0xe6, 0x5a, 0x46, 0xd6, 0x02, 0x91, 0x74, 0x32, 0x0c, 0x18, 0x6d, 0x36, 0x16, 0x0f, 0x7c, 0x4f,
+0x48, 0x4d, 0x1e, 0x56, 0xb5, 0xb3, 0x5f, 0x7e, 0x6d, 0x9d, 0x72, 0xa3, 0x4a, 0xa9, 0x49, 0x49,
+0x43, 0x46, 0x0d, 0x56, 0x90, 0x0a, 0x37, 0x2c, 0xc2, 0x00, 0x17, 0x9c, 0x40, 0x5d, 0x7e, 0x4f,
+0x35, 0xdf, 0x7e, 0x21, 0x70, 0xa9, 0x53, 0x1f, 0x19, 0xee, 0x0d, 0x0c, 0x12, 0xa9, 0x53, 0x11,
+0x07, 0xa9, 0x09, 0x3e, 0x02, 0xc0, 0x35, 0xcf, 0x21, 0x4c, 0x21, 0xff, 0x1e, 0xb9, 0x0f, 0x39,
+0x34, 0x3d, 0x7f, 0x0a, 0xc6, 0x81, 0x24, 0xff, 0xb5, 0x71, 0xa8, 0x38, 0x1d, 0x8c, 0x86, 0x9f,
+0xc4, 0x30, 0x3b, 0x7c, 0x07, 0x8c, 0x92, 0xf7, 0xc1, 0x2d, 0xe1, 0xc6, 0xf7, 0x4b, 0x88, 0x66,
+0xf7, 0xc1, 0x78, 0x0c, 0x6f, 0x2b, 0x62, 0xff, 0x7e, 0x58, 0x31, 0x76, 0xb0, 0x62, 0x6b, 0xcc,
+0x60, 0x7c, 0x27, 0xac, 0x24, 0xe7, 0xf6, 0x51, 0x61, 0xfe, 0x65, 0x8c, 0x86, 0x77, 0x71, 0x60,
+0x30, 0x0a, 0xc5, 0xa3, 0x67, 0xac, 0x78, 0x36, 0x2e, 0x59, 0x11, 0x26, 0xc3, 0x8a, 0x1e, 0x98,
+0x15, 0x16, 0xb0, 0xac, 0x0e, 0x54, 0x9a, 0xf7, 0xe3, 0x33, 0x9a, 0x57, 0x36, 0x1c, 0x0e, 0x47,
+0x01, 0x88, 0x3d, 0x87, 0x3c, 0xff, 0xe1, 0x4d, 0xff, 0x1f, 0x00, 0x66, 0x14, 0x18, 0x9a, 0xc5,
+0x2f, 0xf8, 0x51, 0x30, 0x0c, 0xfa, 0x82, 0x06, 0x07, 0x3e, 0x41, 0x10, 0xff, 0x10, 0x3a, 0x2f,
+0xf4, 0x58, 0xc2, 0xf7, 0x66, 0x4f, 0x01, 0x22, 0x88, 0x01, 0x34, 0x84, 0xd9, 0xa1, 0x21, 0xd4,
+0xca, 0x42, 0x2b, 0x9a, 0x43, 0x68, 0xbe, 0x1a, 0x42, 0xb8, 0xd0, 0x10, 0xb2, 0xa1, 0x91, 0xac,
+0xa0, 0x09, 0x2d, 0x9a, 0x0d, 0xa1, 0x94, 0x1d, 0x08, 0x8e, 0xf8, 0x0a, 0x08, 0x82, 0x3a, 0x10,
+0x7c, 0xff, 0x16, 0x10, 0x70, 0x0f, 0x34, 0x2c, 0x1f, 0x28, 0x20, 0x5e, 0x09, 0x79, 0x52, 0x37,
+0x68, 0x42, 0x4c, 0x43, 0x4b, 0x46, 0x40, 0x2d, 0xa1, 0x3a, 0x68, 0x08, 0x34, 0x42, 0x43, 0x2e,
+0x28, 0x12, 0x1a, 0x22, 0xc3, 0xdc, 0x0f, 0x6f, 0x02, 0x42, 0x10, 0xb6, 0x84, 0x0a, 0x77, 0xc0,
+0x62, 0xfe, 0x2c, 0xde, 0x27, 0xf2, 0x87, 0x0d, 0xd7, 0x1f, 0x02, 0x16, 0xe3, 0x4c, 0xb3, 0xd6,
+0x07, 0x17, 0x11, 0xe5, 0xd1, 0x4a, 0xaf, 0xb8, 0x9f, 0x69, 0x24, 0x89, 0xc0, 0x7f, 0x36, 0x19,
+0xfc, 0x0f, 0xf4, 0x48, 0x81, 0x33, 0xe0, 0xaf, 0x79, 0x8d, 0x17, 0xaa, 0x80, 0x08, 0x48, 0x67,
+0x9b, 0xcd, 0x7f, 0x1f, 0x07, 0x86, 0xc5, 0x91, 0x68, 0x33, 0x7f, 0x00, 0x71, 0xf6, 0x80, 0x3f,
+0xf6, 0xce, 0xc2, 0x7f, 0x7a, 0xfc, 0x17, 0x2c, 0x74, 0x07, 0x16, 0x8d, 0x81, 0x6d, 0x78, 0x71,
+0x7e, 0x67, 0x39, 0xd8, 0x52, 0xf8, 0x9f, 0x14, 0xc3, 0x9f, 0x5d, 0x19, 0xec, 0xa2, 0x11, 0x18,
+0xb0, 0x38, 0x3c, 0x39, 0x8b, 0x57, 0x5e, 0x41, 0x5b, 0x3c, 0x7e, 0xc2, 0x2f, 0x86, 0x06, 0xae,
+0xfc, 0x11, 0x98, 0xc9, 0x22, 0x2f, 0x0b, 0x16, 0x29, 0x88, 0x86, 0x40, 0x57, 0xbf, 0xcd, 0x11,
+0x2f, 0xfe, 0x86, 0x1c, 0x70, 0xc2, 0x66, 0x99, 0xfe, 0x17, 0xd2, 0x35, 0xce, 0x00, 0x00, 0x83,
+0x18, 0x33, 0x03, 0x72, 0x81, 0x4c, 0x04, 0x90, 0x27, 0x69, 0x26, 0x32, 0x82, 0x14, 0x11, 0x0a,
+0x06, 0x41, 0x48, 0xc3, 0x48, 0x49, 0xb3, 0x2c, 0x23, 0x83, 0x95, 0x11, 0x93, 0x32, 0x88, 0x33,
+0x15, 0xa1, 0x23, 0x98, 0xa5, 0xb3, 0xc4, 0x0a, 0x11, 0x19, 0x33, 0xa4, 0x20, 0x13, 0x14, 0x8c,
+0x88, 0x85, 0x03, 0x28, 0x32, 0x33, 0x49, 0x41, 0x63, 0x81, 0x48, 0x63, 0x10, 0x09, 0x48, 0x20,
+0x53, 0x24, 0x2b, 0x43, 0x95, 0x15, 0x33, 0xb0, 0x82, 0x23, 0x56, 0x30, 0x13, 0x0a, 0x56, 0x41,
+0xc2, 0x03, 0x58, 0x48, 0xf3, 0x21, 0x29, 0xe3, 0x61, 0x10, 0x33, 0xf9, 0x56, 0x00, 0xff, 0x48,
+0x0e, 0xf0, 0x85, 0x64, 0x0f, 0x88, 0x84, 0x36, 0x00, 0x30, 0x33, 0x90, 0x06, 0x6c, 0x7f, 0x88,
+0xb0, 0x91, 0x96, 0x6c, 0xff, 0xc6, 0xfc, 0x4d, 0x08, 0xe9, 0x1a, 0x02, 0xe7, 0x37, 0x21, 0xa2,
+0x0f, 0x90, 0xa2, 0x4b, 0xad, 0xbd, 0x24, 0x48, 0x01, 0x05, 0x25, 0x05, 0xc1, 0x90, 0x2e, 0x2a,
+0x18, 0x4a, 0x11, 0xac, 0x24, 0x29, 0xaf, 0xe8, 0x14, 0x99, 0x2b, 0x8c, 0xe0, 0xb0, 0x61, 0x42,
+0x00, 0xdb, 0xf3, 0x0f, 0x0c, 0xec, 0x34, 0x3c, 0x46, 0xb0, 0x1c, 0x76, 0xf8, 0x23, 0x58, 0x1b,
+0xd8, 0x36, 0xa0, 0x32, 0x9b, 0x30, 0x88, 0x33, 0x08, 0x80, 0x18, 0xb0, 0x9b, 0x2b, 0x08, 0x03,
+0x05, 0x29, 0x20, 0x20, 0x15, 0x24, 0xa4, 0x07, 0x91, 0x41, 0x33, 0x0c, 0x08, 0x8b, 0x49, 0xc1,
+0x23, 0x83, 0x58, 0x33, 0x04, 0x20, 0x85, 0x14, 0x77, 0x90, 0x82, 0x69, 0x06, 0x31, 0x33, 0x48,
+0x15, 0x21, 0x29, 0xbd, 0x20, 0x10, 0x7d, 0x04, 0x22, 0x6f, 0x15, 0xac, 0x0a, 0x86, 0x81, 0xc1,
+0xc8, 0x73, 0x60, 0x59, 0x65, 0x8c, 0xac, 0x57, 0x80, 0x15, 0x49, 0xb2, 0x82, 0x3b, 0x59, 0xc1,
+0x2d, 0x83, 0x08, 0x33, 0x30, 0x08, 0x60, 0x19, 0x4d, 0x05, 0x32, 0x49, 0x31, 0x99, 0x30, 0x45,
+0x06, 0x33, 0x10, 0x57, 0x6c, 0x79, 0x61, 0x17, 0x00, 0x6c, 0x06, 0x24, 0x20, 0x03, 0x42, 0x1e,
+0x03, 0xce, 0x1b, 0x40, 0x2a, 0x03, 0x66, 0x19, 0x17, 0x01, 0x2b, 0x30, 0xe0, 0x15, 0x12, 0x03,
+0xae, 0x18, 0x0f, 0xc0, 0x21, 0x3c, 0x1c, 0x60, 0x65, 0x0d, 0xc2, 0x3c, 0x0b, 0x2a, 0x0c, 0x33,
+0x10, 0x08, 0x60, 0x56, 0xfb, 0x30, 0xac, 0xf3, 0x60, 0x56, 0x59, 0x31, 0xac, 0x63, 0x50, 0x06,
+0x33, 0x10, 0xa1, 0x94, 0x58, 0x56, 0x73, 0x22, 0x83, 0x33, 0x26, 0x14, 0x4d, 0xc9, 0x0a, 0x9b,
+0x64, 0x10, 0x33, 0x0a, 0x42, 0x10, 0xc1, 0xcb, 0x88, 0x20, 0xcb, 0xc4, 0x10, 0xc3, 0x6c, 0x08,
+0x3b, 0x09, 0x02, 0x11, 0x2b, 0x04, 0x62, 0x23, 0x02, 0x11, 0x1b, 0x81, 0x08, 0x13, 0x40, 0x04,
+0x20, 0x82, 0x0b, 0x11, 0xc2, 0x33, 0x8b, 0x61, 0xda, 0xd1, 0x61, 0x23, 0x1b, 0x32, 0x88, 0x33,
+0x45, 0xa8, 0x50, 0xdc, 0x64, 0x62, 0x78, 0x60, 0x62, 0xb6, 0x8b, 0x6c, 0xef, 0x62, 0xf1, 0x57,
+0x55, 0x64, 0xe1, 0x05, 0x4f, 0x16, 0xef, 0xcc, 0x66, 0x01, 0x3e, 0x2c, 0x42, 0x38, 0x78, 0x11,
+0x33, 0x17, 0xb2, 0x2c, 0x06, 0x86, 0xc5, 0x0f, 0x1f, 0x51, 0x2c, 0x1b, 0x17, 0xee, 0x88, 0x50,
+0xb9, 0x32, 0x88, 0x33, 0x80, 0x3c, 0xb5, 0x72, 0xe0, 0xc9, 0x28, 0x9d, 0x4c, 0x22, 0xab, 0x67,
+0x24, 0x29, 0x41, 0x15, 0x0c, 0x62, 0x33, 0xa1, 0x22, 0xbc, 0x05, 0xbf, 0xc8, 0xf8, 0xc8, 0x85,
+0x45, 0x43, 0x56, 0x88, 0x9c, 0x0f, 0x44, 0x71, 0x66, 0x82, 0x91, 0x17, 0x88, 0x58, 0xf2, 0x38,
+0xc9, 0xc3, 0x97, 0xc6, 0x7c, 0x0d, 0x1b, 0x12, 0xe2, 0x50, 0x3b, 0xaa, 0x0c, 0x33, 0x15, 0xff,
+0xcf, 0xcd, 0xef, 0xec, 0xff, 0xdc, 0x8b, 0x0c, 0xc8, 0x27, 0xd1, 0x05, 0x9c, 0xf0, 0x0a, 0x1c,
+0x4c, 0x80, 0xb0, 0x67, 0xb2, 0x08, 0x00, 0x01, 0x0c, 0x62, 0x33, 0x01, 0x0a, 0x98, 0x01, 0x21,
+0x4b, 0x80, 0x55, 0x34, 0x01, 0xbb, 0x7e, 0x07, 0x06, 0x1d, 0x3c, 0x81, 0x01, 0x1b, 0xc0, 0xb9,
+0x0d, 0x2d, 0x96, 0x30, 0x18, 0x10, 0x3c, 0x60, 0x15, 0x91, 0x41, 0x33, 0x0c, 0x58, 0x0d, 0x19,
+0x30, 0x0b, 0x34, 0x60, 0x0a, 0x61, 0x15, 0x67, 0x40, 0x50, 0x28, 0x90, 0x41, 0x84, 0x51, 0x84,
+0x33, 0xc9, 0xa0, 0x66, 0xc5, 0x17, 0x50, 0x7e, 0x17, 0x83, 0x4c, 0x3c, 0x55, 0xd1, 0x5e, 0x38,
+0x07, 0x19, 0xe7, 0x66, 0x2d, 0x21, 0x08, 0x19, 0xc4, 0x7c, 0x33, 0x16, 0x47, 0x8f, 0x1b, 0xd8,
+0xc5, 0xfc, 0x19, 0x7c, 0xc2, 0x10, 0xbf, 0x13, 0xc2, 0x7c, 0x3c, 0x65, 0x33, 0xe9, 0x74, 0x50,
+0x27, 0x97, 0x57, 0xd1, 0x22, 0x92, 0x72, 0x50, 0x92, 0x5f, 0x50, 0x60, 0x60, 0xf8, 0x18, 0x41,
+0x0f, 0x5d, 0xf0, 0xe6, 0x06, 0x06, 0x77, 0xfc, 0xac, 0xa0, 0x60, 0xf6, 0x83, 0xea, 0xec, 0xf9,
+0x44, 0x05, 0xa2, 0x78, 0xae, 0xea, 0x45, 0x9c, 0x0c, 0x2c, 0x23, 0x9b, 0x25, 0x10, 0x97, 0x81,
+0x20, 0xb7, 0x01, 0x2b, 0x60, 0x20, 0x63, 0x25, 0x27, 0x83, 0x89, 0x20, 0x97, 0x02, 0xd5, 0xfb,
+0xa2, 0x56, 0xb0, 0xb2, 0xab, 0x41, 0x12, 0xa3, 0x93, 0x80, 0xc3, 0x59, 0x51, 0xdb, 0xa8, 0x02,
+0x17, 0x32, 0xeb, 0xf2, 0xd8, 0xf6, 0x58, 0x5c, 0x5a, 0x00, 0xdc, 0x0c, 0xb2, 0x77, 0x58, 0x0e,
+0x0f, 0xe5, 0x5d, 0x00, 0xd8, 0xd8, 0xfc, 0xda, 0xde, 0xb0, 0xdc, 0x0c, 0x0e, 0x09, 0xcb, 0x0f,
+0x3e, 0x8c, 0xa0, 0x7c, 0xec, 0x90, 0x3f, 0x0f, 0xea, 0xb0, 0x3f, 0x2d, 0x21, 0x5b, 0x29, 0xea,
+0x0c, 0x6b, 0x05, 0x2c, 0x80, 0x91, 0x43, 0xb7, 0xb2, 0x63, 0x00, 0xd9, 0x67, 0xee, 0x6c, 0xfe,
+0x10, 0x0c, 0xd4, 0x6c, 0x0f, 0x56, 0x02, 0x7b, 0x32, 0xa2, 0x8b, 0x00, 0x95, 0xa8, 0x84, 0xdb,
+0x92, 0x8d, 0xeb, 0xf0, 0x46, 0xd4, 0x9b, 0xa2, 0x56, 0x15, 0xb2, 0xab, 0x90, 0x11, 0xab, 0x44,
+0x8d, 0x0c, 0x6a, 0x09, 0xb8, 0x2b, 0x03, 0x7b, 0x49, 0x51, 0x8b, 0x88, 0x0a, 0x54, 0xc8, 0xbb,
+0x42, 0x56, 0xcb, 0x32, 0xa2, 0xcb, 0x94, 0x11, 0x3f, 0x0c, 0xa8, 0xfb, 0x45, 0x2d, 0x2a, 0x64,
+0x0b, 0x21, 0x29, 0x2b, 0x4a, 0x19, 0x9f, 0xfd, 0xab, 0x05, 0x6c, 0xd8, 0x6c, 0x36, 0xb9, 0x80,
+0x0f, 0x0d, 0x23, 0xaa, 0x11, 0x9b, 0xad, 0x02, 0x25, 0x41, 0x9b, 0x2b, 0x6a, 0xab, 0x51, 0x21,
+0x1a, 0x49, 0x8b, 0x4b, 0xa0, 0x8b, 0x59, 0x05, 0x67, 0xa8, 0x55, 0xb5, 0xa4, 0xdb, 0x95, 0x40,
+0xeb, 0xac, 0x82, 0x67, 0xb5, 0x8a, 0x90, 0x15, 0x5b, 0xb0, 0x8c, 0xff, 0x91, 0x55, 0x67, 0x10,
+0x56, 0x21, 0x21, 0x46, 0xce, 0x0f, 0x66, 0x2c, 0x92, 0x32, 0xa8, 0xfb, 0x15, 0xb5, 0xaa, 0x95,
+0xfb, 0x8d, 0x24, 0x0b, 0x24, 0x50, 0x0b, 0x8b, 0x28, 0x2f, 0x40, 0x35, 0x6a, 0x84, 0x1b, 0x35,
+0x89, 0x2b, 0x95, 0x40, 0x2b, 0xac, 0x82, 0x67, 0xd5, 0xaa, 0x56, 0x12, 0x7b, 0x93, 0x82, 0x63,
+0xa7, 0xa0, 0x3c, 0x64, 0x93, 0x60, 0x36, 0x18, 0x54, 0xab, 0xa2, 0x46, 0xd5, 0x32, 0xab, 0x52,
+0x12, 0xbb, 0x3c, 0xa2, 0xbb, 0x89, 0xea, 0xcb, 0x51, 0x2b, 0x5a, 0x19, 0xcb, 0x75, 0xa2, 0xdb,
+0x85, 0x11, 0xdb, 0x2b, 0x6b, 0xef, 0x19, 0x54, 0xcb, 0x8b, 0x6a, 0xdb, 0x54, 0x2b, 0xaa, 0x49,
+0x2b, 0x23, 0x84, 0x3b, 0x19, 0x51, 0xab, 0xa2, 0x0a, 0x44, 0x48, 0xbb, 0x0a, 0x09, 0x63, 0x4a,
+0x82, 0x20, 0x06, 0xd0, 0xcc, 0x8b, 0x6a, 0x0b, 0x54, 0x2b, 0x5a, 0x49, 0x5b, 0x4d, 0xa8, 0x6b,
+0xaa, 0x85, 0xbf, 0x01, 0x2c, 0x6b, 0xa4, 0xa8, 0xfe, 0x3b, 0x51, 0xad, 0x6a, 0x65, 0x4b, 0x21,
+0x29, 0x9b, 0xea, 0x65, 0x3f, 0x07, 0x07, 0x2b, 0x29, 0xdb, 0x58, 0x54, 0xeb, 0x02, 0x58, 0x38,
+0x13, 0x1b, 0x28, 0x83, 0x33, 0x04, 0x22, 0x48, 0x12, 0x04, 0x04, 0x27, 0x03, 0x09, 0x96, 0x02,
+0x01, 0x03, 0x26, 0xf0, 0xff, 0x82, 0x03, 0xf0, 0x8c, 0x0c, 0x71, 0x64, 0x10, 0x8d, 0x2c, 0x60,
+0x3d, 0x16, 0xb0, 0x59, 0x51, 0x67, 0x48, 0x49, 0xc1, 0x4b, 0x28, 0x81, 0x59, 0x51, 0x69, 0x0b,
+0x18, 0x75, 0xc0, 0x08, 0xa2, 0x66, 0xeb, 0x85, 0xb0, 0x93, 0x65, 0x50, 0x61, 0xcb, 0x80, 0xe5,
+0xc0, 0x80, 0xdc, 0xe3, 0x30, 0xe0, 0xdc, 0xe1, 0x0c, 0x38, 0xdc, 0xdf, 0x12, 0x0e, 0xdc, 0x17,
+0x03, 0xce, 0xdb, 0x80, 0x03, 0xe6, 0x53, 0xcb, 0xd9, 0xc8, 0xc3, 0x80, 0xd7, 0x82, 0x53, 0xd6,
+0x10, 0x12, 0x01, 0x46, 0x42, 0x1d, 0x30, 0x03, 0xc4, 0xbd, 0x19, 0xc0, 0x0a, 0x80, 0x9a, 0x0d,
+0x54, 0xca, 0x1f, 0x5a, 0x46, 0x8d, 0xc8, 0x80, 0x73, 0x46, 0xd4, 0xe1, 0x88, 0x5a, 0xc0, 0xc8,
+0x9b, 0x94, 0x79, 0xc3, 0x57, 0x46, 0xa9, 0xfe, 0x90, 0x01, 0xbe, 0xa4, 0xa8, 0x5f, 0x9a, 0x85,
+0x0d, 0xc8, 0x88, 0xb7, 0x56, 0xd4, 0x85, 0x88, 0x5a, 0xc2, 0xc8, 0xc5, 0xf7, 0xa0, 0x42, 0x35,
+0x90, 0x6f, 0x96, 0x14, 0xdd, 0x92, 0xa2, 0x19, 0x90, 0x31, 0x4b, 0xc8, 0x80, 0xa8, 0x7a, 0xc0,
+0xa6, 0xc0, 0x88, 0x9b, 0x50, 0x21, 0x1a, 0x49, 0xfb, 0xcb, 0x8a, 0x69, 0x49, 0x51, 0x6d, 0x0c,
+0x48, 0x35, 0x55, 0x10, 0x1a, 0x94, 0x67, 0x4b, 0x8a, 0xb3, 0x89, 0x01, 0x89, 0x55, 0x10, 0x16,
+0x90, 0x67, 0xb0, 0xa2, 0x23, 0x96, 0x91, 0x9f, 0xb0, 0x0a, 0x67, 0xd8, 0xaa, 0xfd, 0x30, 0x20,
+0x66, 0xea, 0x31, 0x40, 0x2b, 0x83, 0x6f, 0x59, 0x51, 0xaf, 0x0e, 0x08, 0x5f, 0x8a, 0x3a, 0xc0,
+0xca, 0xbd, 0xa2, 0x66, 0x5b, 0x0c, 0xb0, 0xcb, 0xa8, 0x07, 0x57, 0xac, 0xac, 0xd9, 0x64, 0x15,
+0x67, 0x03, 0x42, 0x4b, 0x51, 0x03, 0x0b, 0x09, 0xad, 0x30, 0x66, 0x0b, 0x18, 0xb0, 0x03, 0x63,
+0xcc, 0xce, 0x95, 0x55, 0x75, 0xc1, 0x80, 0x41, 0x84, 0xaa, 0xf0, 0x9b, 0x06, 0xac, 0x3d, 0x11,
+0x35, 0xc2, 0xaa, 0x95, 0xc5, 0x85, 0x24, 0x33, 0x2f, 0x59, 0xf7, 0x2b, 0x5a, 0x59, 0x51, 0xcb,
+0xa0, 0x45, 0x59, 0x54, 0x13, 0x83, 0x6a, 0x99, 0x72, 0x23, 0x8b, 0x44, 0x4d, 0xfd, 0x6a, 0x65,
+0xa9, 0x2c, 0x89, 0x17, 0x91, 0x40, 0x7d, 0xc8, 0x80, 0x24, 0x65, 0xc0, 0x22, 0xc8, 0x80, 0x20,
+0x06, 0xd5, 0xc3, 0x0c, 0x58, 0x1d, 0x6a, 0xd9, 0xa7, 0x2b, 0x89, 0xb1, 0xa4, 0xa8, 0xfe, 0x27,
+0x51, 0xad, 0xa8, 0x25, 0x95, 0x8d, 0xa4, 0x7b, 0x51, 0xe7, 0x37, 0x58, 0x49, 0xb3, 0x90, 0x0c,
+0x0d, 0x26, 0x01, 0x4b, 0x41, 0x4c, 0x10, 0x80, 0x33, 0x05, 0x02, 0x9f, 0xd0, 0x58, 0xfe, 0x0c,
+0xa1, 0xcf, 0xe5, 0x07, 0x6c, 0xd3, 0xf6, 0xa2, 0x7e, 0x9c, 0x07, 0x5e, 0x8b, 0xe7, 0x00, 0xb0,
+0x08, 0xff, 0x0a, 0x5a, 0x06, 0x04, 0x31, 0xcf, 0xa8, 0x60, 0xcb, 0x51, 0x4b, 0xde, 0xa8, 0x48,
+0x43, 0x45, 0x4d, 0xce, 0xa3, 0x21, 0xa3, 0xa5, 0x19, 0xc1, 0xeb, 0x8c, 0x60, 0xfb, 0xa3, 0x36,
+0x69, 0xa0, 0xa8, 0x96, 0x9e, 0x34, 0xac, 0xc3, 0x16, 0x7f, 0x97, 0x5e, 0x5b, 0xde, 0x15, 0x9a,
+0xd1, 0x58, 0xa9, 0xb9, 0x3f, 0x8b, 0xfe, 0xdb, 0xde, 0x05, 0xb0, 0x19, 0x9d, 0xa0, 0xc6, 0x5a,
+0x1f, 0x9f, 0x66, 0x11, 0x65, 0xfc, 0xed, 0xfc, 0x07, 0xc6, 0xcc, 0xf8, 0xf8, 0xcc, 0xc6, 0x82,
+0x14, 0x3b, 0x41, 0x0a, 0x4b, 0x0d, 0x12, 0x2e, 0x38, 0xd4, 0x8b, 0x06, 0x07, 0x6a, 0x16, 0x0b,
+0xd4, 0x12, 0x2e, 0xa3, 0x17, 0x3b, 0x49, 0xf5, 0x91, 0x48, 0x3c, 0x86, 0x11, 0x9b, 0x90, 0xa2,
+0xa3, 0x46, 0x54, 0xa3, 0x29, 0x6a, 0xab, 0xa2, 0x0e, 0x00, 0x7b, 0x91, 0x0b, 0x05, 0x07, 0x46,
+0xd4, 0xab, 0x8a, 0x1a, 0x11, 0x75, 0xb3, 0xa2, 0x96, 0xb3, 0x50, 0x93, 0xbb, 0x6a, 0x58, 0xcb,
+0x35, 0x29, 0x53, 0x86, 0x11, 0xe3, 0x27, 0xf1, 0x05, 0x0d, 0x33, 0x1b, 0x05, 0x33, 0x33, 0xca,
+0x7d, 0x97, 0x66, 0x09, 0x02, 0x23, 0x9b, 0x10, 0x15, 0xa2, 0x86, 0x1b, 0x42, 0x93, 0xa3, 0xb8,
+0x24, 0x1a, 0xf6, 0x55, 0x10, 0x28, 0x94, 0x67, 0xb3, 0x49, 0x94, 0x7c, 0x06, 0x59, 0x05, 0x67,
+0xa8, 0x10, 0x34, 0xa4, 0x7b, 0x82, 0x49, 0xde, 0x88, 0xac, 0x67, 0x84, 0x5e, 0x91, 0x27, 0x53,
+0xd4, 0xc3, 0xac, 0xa8, 0xcb, 0x14, 0x95, 0x10, 0x9a, 0xcb, 0x11, 0x86, 0xd0, 0x0a, 0x2d, 0x89,
+0x6e, 0xa2, 0x08, 0x60, 0x16, 0x4b, 0x5e, 0xaf, 0xab, 0x20, 0x67, 0x51, 0x28, 0xa8, 0x25, 0x6e,
+0x61, 0x8f, 0x13, 0x1b, 0xac, 0xa8, 0xa3, 0x11, 0xb5, 0xf5, 0xe2, 0xa3, 0x2b, 0x81, 0x11, 0xab,
+0xf5, 0xa0, 0xf6, 0x96, 0x11, 0xb3, 0x11, 0xf5, 0x05, 0x8b, 0x80, 0xbb, 0x9d, 0x45, 0x2d, 0xa8,
+0x62, 0xb3, 0x66, 0x13, 0xbb, 0x4f, 0x12, 0xa8, 0xeb, 0x46, 0xd4, 0xa3, 0xb3, 0x08, 0x6f, 0x10,
+0x88, 0x7c, 0xcb, 0xf2, 0x45, 0x5c, 0x7e, 0x06, 0x5d, 0xf6, 0x86, 0x8a, 0x5a, 0x19, 0xf4, 0xfb,
+0x76, 0xcc, 0x62, 0x35, 0x6a, 0x59, 0x3d, 0x1d, 0x23, 0xeb, 0xf8, 0x0c, 0x8b, 0xd7, 0x06, 0x0c,
+0xfa, 0x1c, 0x61, 0x11, 0x32, 0x55, 0xb0, 0x9f, 0x1a, 0x02, 0xc3, 0x49, 0xa8, 0xdb, 0x59, 0x05,
+0x67, 0x40, 0x0e, 0x1d, 0xbf, 0xf2, 0x64, 0x62, 0x05, 0x99, 0x52, 0x44, 0x0a, 0x4a, 0x21, 0x0b,
+0x50, 0x45, 0x00, 0x90, 0x14, 0xa1, 0xc1, 0x39, 0x13, 0x50, 0x56, 0x67, 0x09, 0x44, 0x00, 0x08,
+0x94, 0x84, 0x22, 0x11, 0x30, 0x2f, 0xb7, 0xc0, 0x2a, 0x67, 0x01, 0x08, 0x45, 0x21, 0x40, 0x42,
+0x11, 0x02, 0x48, 0x12, 0xa3, 0x21, 0x83, 0x33, 0x0c, 0xc8, 0x51, 0xe5, 0x68, 0x0a, 0x9e, 0x39,
+0xbf, 0x22, 0xb2, 0x67, 0x50, 0xb2, 0x49, 0x50, 0xa2, 0x02, 0x41, 0x85, 0xc1, 0x39, 0xbb, 0x50,
+0x56, 0x67, 0x12, 0x45, 0xbc, 0xa0, 0x17, 0xab, 0xc0, 0x67, 0x02, 0x02, 0x51, 0xa1, 0x20, 0x08,
+0x2b, 0x0a, 0xab, 0x67, 0x7f, 0xa8, 0x0d, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x7f, 0xfb, 0x20, 0x66,
+0x6f, 0x6e, 0x74, 0x04, 0x69, 0x6c, 0x65, 0x20, 0x68, 0x61, 0xff, 0x6f, 0x0d, 0x62, 0x65, 0x65,
+0x6e, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0xdb, 0x64, 0x2f, 0x02, 0x69, 0x06, 0x13,
+0xb7, 0x7f, 0x79, 0x20, 0x43, 0x50, 0x49, 0x61, 0x64, 0x09, 0x56, 0x31, 0xff, 0xed, 0x2e, 0x32,
+0x30, 0x37, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0xfd, 0x2f, 0x28, 0x43, 0x29, 0x20,
+0x31, 0x39, 0x39, 0x33, 0x2d, 0xfb, 0xd7, 0x04, 0x36, 0x28, 0x6b, 0x6f, 0x73, 0x74, 0x54, 0xdb,
+0x7f, 0x40, 0x61, 0x63, 0x6d, 0x2e, 0x6f, 0x72, 0x67, 0x1f, 0x4b, 0x63, 0x77, 0x0f, 0x61, 0x20,
+0x4b, 0x15, 0x29, 0xff, 0x60, 0x71, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x77, 0xf7, 0x20,
+0x6d, 0x61, 0x79, 0x6f, 0x20, 0x75, 0x73, 0x63, 0xdd, 0x5a, 0x66, 0x72, 0x09, 0x6f, 0x66, 0x6b,
+0xad, 0x7a, 0x84, 0x36, 0x09, 0xf6, 0x5b, 0x7e, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x11, 0xdf, 0xda,
+0x77, 0x8d, 0x69, 0x73, 0x6b, 0x2e, 0x0d, 0x00, 0x80, 0x0a, 0x00, 0x00, 0x02, 0x00, 0x00, 0x40,
+0xff, 0xa4, 0xe8, 0x3a, 0x00, 0x72, 0xfa, 0x41, 0xe8, 0x2f, 0x00, 0xe3, 0x3b, 0x73, 0xf9, 0x83,
+0xe9, 0x03, 0x72, 0x06, 0x88, 0xcc, 0xac, 0xf7, 0xd0, 0x95, 0x31, 0xc9, 0xe8, 0x1b, 0x00, 0x11,
+0xc9, 0x75, 0x08, 0x41, 0xe8, 0x13, 0x00, 0x73, 0xfb, 0x41, 0x41, 0x81, 0xfd, 0x00, 0xf3, 0x83,
+0xd1, 0x01, 0x8d, 0x03, 0x96, 0xf3, 0xa4, 0x96, 0xeb, 0xc8, 0xe8, 0x02, 0x00, 0x11, 0xc9, 0x01,
+0xdb, 0x75, 0x04, 0xad, 0x11, 0xc0, 0x93, 0xc3, 0x5e, 0xb9, 0x01, 0x00, 0xac, 0x2c, 0xe8, 0x3c,
+0x01, 0x77, 0xf9, 0x8b, 0x1c, 0x86, 0xdf, 0x29, 0xf3, 0x89, 0x1c, 0xad, 0xe2, 0xee, 0xc3 };
+
+Bit8u font_ega5_cpx[5720] = {
+0x81, 0xfc, 0x9a, 0xc1, 0x77, 0x02, 0xcd, 0x20, 0xb9, 0x58, 0x16, 0xbe, 0x58, 0x17, 0xbf, 0x3a,
+0xc1, 0xbb, 0x00, 0x80, 0xfd, 0xf3, 0xa4, 0xfc, 0x87, 0xf7, 0x83, 0xee, 0xc6, 0x19, 0xed, 0x57,
+0x57, 0xe9, 0xb9, 0xbf, 0x55, 0x50, 0x58, 0x21, 0x0b, 0x01, 0x04, 0x08, 0xcf, 0xfc, 0xfe, 0x92,
+0xf9, 0xe1, 0xc2, 0x32, 0xcc, 0xbf, 0xc0, 0x15, 0x06, 0xcf, 0xbb, 0xfc, 0xff, 0x46, 0x4f, 0x4e,
+0x54, 0x20, 0x00, 0x00, 0x01, 0x6e, 0x39, 0x01, 0x17, 0x06, 0xfd, 0xfd, 0x05, 0x00, 0x1c, 0x00,
+0x4d, 0x26, 0x0e, 0x45, 0x47, 0x41, 0xc9, 0xcd, 0x1e, 0x20, 0xe1, 0x02, 0xfe, 0xd8, 0x35, 0x24,
+0x03, 0x00, 0x12, 0x26, 0x10, 0x08, 0x95, 0xc5, 0x0a, 0x00, 0x6d, 0xff, 0x7e, 0x81, 0xa5, 0x81,
+0x81, 0xbd, 0x99, 0x03, 0x7e, 0xfe, 0x83, 0x0f, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff,
+0xe6, 0x77, 0x00, 0x6c, 0xfe, 0x3e, 0xec, 0x7c, 0x38, 0x10, 0x30, 0x10, 0x38, 0x7c, 0xfd, 0xc0,
+0x0e, 0x18, 0x3c, 0x3c, 0xe7, 0xb2, 0xb6, 0x00, 0x18, 0x06, 0x0f, 0x65, 0x6e, 0x7e, 0x3b, 0x0f,
+0xb3, 0xb3, 0x22, 0x18, 0x09, 0xde, 0xb1, 0xff, 0x00, 0xe7, 0xc3, 0x5f, 0x3f, 0xf6, 0x00, 0x1f,
+0x3c, 0x66, 0x42, 0x42, 0x66, 0xff, 0x92, 0x3c, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xfe, 0x21,
+0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xde, 0xeb, 0x00, 0x78, 0x20, 0x2d, 0xb9, 0xe6, 0x00, 0x4f,
+0x61, 0xbb, 0x9f, 0x0f, 0x3f, 0x33, 0x3f, 0x30, 0x00, 0x70, 0xf0, 0xf9, 0xc8, 0xe0, 0x00, 0x7f,
+0x63, 0x7f, 0x63, 0x9e, 0xbd, 0x67, 0xe7, 0xe6, 0xc0, 0x7c, 0x18, 0xdb, 0x87, 0xdd, 0xa0, 0x3c,
+0xdb, 0x2e, 0x80, 0xbb, 0xfd, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0x2d, 0xc0, 0x80, 0xbf, 0xfe,
+0x1f, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x6b, 0x49, 0x2b, 0x5e,
+0x7c, 0x67, 0x3c, 0x00, 0x66, 0x98, 0x9b, 0x07, 0x5f, 0xe7, 0x36, 0xdb, 0x00, 0x7b, 0x1b, 0xff,
+0x9b, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0x06, 0xe4, 0xc6, 0x7c,
+0x00, 0xfe, 0x43, 0x66, 0x4f, 0x7e, 0x6f, 0x60, 0x0f, 0x00, 0xbd, 0x60, 0x0f, 0x00, 0xfe, 0x96,
+0x70, 0x11, 0x0c, 0xfe, 0x0c, 0x77, 0xc9, 0x0c, 0x00, 0x30, 0x60, 0xfe, 0x06, 0xe4, 0x60, 0x30,
+0x00, 0xc0, 0x61, 0x56, 0x5c, 0xa1, 0x76, 0x39, 0x28, 0x6c, 0x28, 0x0e, 0x50, 0x58, 0x9f, 0xb2,
+0xb7, 0xa0, 0x7c, 0x7d, 0x8a, 0x0e, 0x5b, 0x0e, 0x38, 0xaf, 0xbc, 0xc0, 0x00, 0x18, 0x3c, 0x66,
+0xaf, 0x8c, 0x8f, 0x21, 0xeb, 0xe9, 0x24, 0x20, 0x8b, 0xb9, 0x6c, 0x5e, 0x03, 0xad, 0xfe, 0x36,
+0xf1, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0x1e, 0xd6, 0xef, 0xc1, 0xc2, 0xc6, 0x26, 0xdf, 0xae,
+0x30, 0x60, 0xc6, 0x86, 0x2e, 0x14, 0x0e, 0x0c, 0x76, 0xdc, 0xc1, 0x1a, 0xaf, 0x76, 0xbd, 0x81,
+0x6d, 0x00, 0x60, 0x4e, 0x8b, 0xb9, 0x2b, 0x00, 0xe5, 0xb9, 0x59, 0x0f, 0x08, 0x96, 0x8d, 0x00,
+0x18, 0xe2, 0x15, 0x16, 0xde, 0xff, 0xe5, 0x84, 0x81, 0xa2, 0xed, 0x06, 0xb6, 0x14, 0x6c, 0xc0,
+0x31, 0xfe, 0xd7, 0x96, 0xad, 0x31, 0xf9, 0x15, 0xe2, 0x9f, 0xc0, 0x80, 0xf8, 0x30, 0xae, 0xd6,
+0xd6, 0x0e, 0x76, 0xb2, 0x27, 0x38, 0x78, 0x59, 0x10, 0x81, 0x2e, 0xf6, 0xe4, 0x7c, 0xc6, 0xc6,
+0xfe, 0x0f, 0xd8, 0x4e, 0x06, 0x3c, 0x02, 0x06, 0xfd, 0x43, 0xdf, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc,
+0xfe, 0x9c, 0xd0, 0xb0, 0x1e, 0x8d, 0xfe, 0x03, 0xd6, 0xfc, 0x1f, 0x38, 0x90, 0xbb, 0x60, 0x0f,
+0xc6, 0xc6, 0xc6, 0x66, 0xee, 0xfe, 0x3f, 0xf3, 0x73, 0xc3, 0x4f, 0x1a, 0x1f, 0x7d, 0x01, 0x0b,
+0x7e, 0x0b, 0xdb, 0x3f, 0x0c, 0x78, 0xba, 0x0b, 0x7b, 0x04, 0x0f, 0x62, 0x93, 0x30, 0x9e, 0x92,
+0x1a, 0x35, 0x06, 0xc2, 0xc9, 0xde, 0x02, 0x00, 0xf6, 0x62, 0x1b, 0x23, 0x5f, 0x25, 0x4c, 0x0c,
+0xdf, 0xb6, 0xb3, 0x10, 0xde, 0x00, 0xdc, 0x61, 0x14, 0xc4, 0x2e, 0x3b, 0xdb, 0x6c, 0x90, 0xfe,
+0x0f, 0x6c, 0x3c, 0xfc, 0x66, 0xf1, 0x7c, 0x86, 0xf4, 0xfc, 0x6f, 0xd4, 0x15, 0xef, 0x00, 0xc2,
+0xf2, 0xe9, 0xa1, 0x10, 0xf8, 0x6c, 0x3f, 0x7b, 0x6c, 0xf8, 0x0f, 0xfe, 0x66, 0x62, 0x68, 0x84,
+0xfc, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0xc0, 0xb6, 0x00, 0xf0, 0x3f, 0x4d, 0x7e, 0xde, 0xc6,
+0xc6, 0x66, 0x3a, 0x61, 0xef, 0x69, 0x6e, 0x6f, 0x6b, 0xc2, 0x3c, 0x7d, 0x30, 0x31, 0x26, 0x1e,
+0x0d, 0x77, 0x49, 0xef, 0xe6, 0x6a, 0xb3, 0x6e, 0x78, 0x78, 0x74, 0xe6, 0x0f, 0xb3, 0xd8, 0xf0,
+0x60, 0x00, 0x6f, 0x92, 0x7f, 0x4f, 0xee, 0xfe, 0xfe, 0xd6, 0xf2, 0xdb, 0x0f, 0xe6, 0xf6, 0xfe,
+0xde, 0xce, 0x79, 0x58, 0x6f, 0xc6, 0x9b, 0x2d, 0xdf, 0x41, 0x27, 0xec, 0xf0, 0x1f, 0xd6, 0xde,
+0x97, 0xe4, 0x7c, 0x0c, 0x0e, 0xe3, 0x29, 0xd9, 0xe6, 0x1f, 0xe2, 0xb2, 0x42, 0x50, 0x3f, 0x09,
+0x7b, 0x7e, 0x7e, 0x5a, 0xaf, 0x16, 0xb2, 0xc6, 0x5f, 0x0f, 0x25, 0x0f, 0x6c, 0x38, 0x10, 0x43,
+0xfe, 0xd6, 0xd6, 0xd6, 0xfe, 0xee, 0x6c, 0x6b, 0xa6, 0x6c, 0x8e, 0x61, 0x9f, 0x66, 0x0d, 0xce,
+0x0b, 0xcb, 0x4f, 0x1b, 0x86, 0x21, 0x1f, 0x7f, 0xc2, 0x16, 0xec, 0x3c, 0x30, 0x00, 0x1f, 0xfe,
+0x0a, 0xc1, 0x70, 0x38, 0x1c, 0x76, 0xb2, 0xaf, 0x3c, 0x0c, 0x00, 0x62, 0x0c, 0x3c, 0xdc, 0x8d,
+0x90, 0xe6, 0xec, 0x34, 0x6c, 0xff, 0x18, 0xe9, 0x03, 0x78, 0x0c, 0x7c, 0xaf, 0x16, 0x2e, 0xe0,
+0x19, 0x78, 0x66, 0x11, 0xe2, 0xcf, 0x1c, 0x13, 0x02, 0x00, 0xb9, 0xc3, 0xdf, 0x1c, 0x69, 0x2d,
+0x2b, 0x00, 0x2f, 0x80, 0x8c, 0x1f, 0xfe, 0xc6, 0xfb, 0x36, 0x32, 0x30, 0x78, 0xaf, 0xcf, 0xf0,
+0x5d, 0x76, 0x2e, 0xcc, 0xdf, 0xa1, 0x5f, 0xd2, 0x5f, 0x6c, 0x32, 0x9e, 0x76, 0xe6, 0xdc, 0x03,
+0x61, 0x38, 0x4f, 0x6b, 0x4b, 0x09, 0x00, 0xdb, 0x00, 0xcc, 0x10, 0x17, 0x2f, 0x71, 0xc6, 0x00,
+0xe6, 0x90, 0x9c, 0x2f, 0x38, 0x35, 0xba, 0x00, 0xec, 0x00, 0x4c, 0xd6, 0x00, 0xc6, 0x0f, 0xdc,
+0xcb, 0x66, 0xa1, 0x9f, 0x6c, 0x09, 0x9f, 0x1f, 0xf6, 0xb1, 0x04, 0xf0, 0x9f, 0x90, 0x16, 0xd2,
+0x19, 0xed, 0xdc, 0x9f, 0x1f, 0x4c, 0x60, 0xe4, 0x76, 0x09, 0xdf, 0x10, 0xd9, 0xb2, 0xdf, 0xfc,
+0xde, 0x36, 0x1c, 0x1f, 0x10, 0x5e, 0x3d, 0x0f, 0x82, 0x31, 0x00, 0x47, 0xc0, 0x0f, 0x00, 0xb7,
+0x84, 0x9a, 0x1b, 0x30, 0x53, 0x38, 0x75, 0x2f, 0xd4, 0xb5, 0x04, 0x0c, 0x03, 0x87, 0x52, 0xcc,
+0x4f, 0xcd, 0xd9, 0xfe, 0xef, 0x0e, 0x70, 0x64, 0x3d, 0x0e, 0xf7, 0x09, 0x83, 0x3f, 0xda, 0x7b,
+0x1b, 0x23, 0x24, 0x40, 0xf8, 0xc9, 0xdc, 0xdf, 0x8b, 0x34, 0xe2, 0x4f, 0x95, 0x34, 0xef, 0x76,
+0x7a, 0xfe, 0x62, 0x1c, 0xff, 0xe6, 0x5e, 0x10, 0x10, 0xab, 0x3f, 0x2b, 0x69, 0xef, 0x5a, 0x19,
+0xaf, 0x69, 0x4a, 0xdf, 0x7c, 0xef, 0x40, 0x0e, 0x7c, 0x91, 0xb4, 0xdf, 0x69, 0xb2, 0x6f, 0xc6,
+0xdf, 0x52, 0x08, 0xe0, 0x30, 0x48, 0xde, 0x6d, 0xed, 0x82, 0x63, 0x5b, 0x82, 0x02, 0xa9, 0x07,
+0xef, 0x0e, 0xe9, 0xfe, 0xff, 0xc6, 0xb6, 0x40, 0x1f, 0x6b, 0xe0, 0x62, 0x30, 0x5b, 0x62, 0xbf,
+0x56, 0xd2, 0xdf, 0xb4, 0x92, 0x9f, 0xf6, 0xb1, 0xd7, 0x7c, 0x6d, 0x7c, 0xa6, 0x0b, 0xa4, 0xcf,
+0xd8, 0x7b, 0x1c, 0x1e, 0x1f, 0x17, 0x61, 0x7b, 0x24, 0x2f, 0x69, 0x28, 0xee, 0x0f, 0xdc, 0xd8,
+0x60, 0x6c, 0x00, 0xdc, 0x2f, 0xe0, 0x8b, 0xdb, 0xd8, 0x3c, 0xf8, 0x30, 0xfc, 0xa1, 0xbe, 0x64,
+0x1f, 0x35, 0x7c, 0xda, 0x04, 0x3f, 0x9e, 0x21, 0xc5, 0xd3, 0x8f, 0x24, 0x3b, 0x78, 0xc0, 0xdf,
+0x61, 0x3a, 0x7d, 0x9f, 0x61, 0x34, 0x94, 0x3c, 0xff, 0x90, 0x69, 0x49, 0x6b, 0x9d, 0x2f, 0x3e,
+0xbd, 0xc4, 0x2b, 0xd9, 0x3f, 0x4f, 0x97, 0x31, 0xbf, 0xc6, 0x6f, 0xf6, 0xcc, 0xf8, 0xf8, 0xcc,
+0xbf, 0x8f, 0x78, 0x5f, 0x4b, 0x6c, 0x0f, 0xee, 0xc2, 0xdf, 0xf6, 0x63, 0x21, 0x09, 0xb2, 0xb3,
+0x06, 0xa1, 0x30, 0x4f, 0x38, 0x63, 0x17, 0x4a, 0x3c, 0x60, 0x7f, 0x1c, 0x42, 0x48, 0x6f, 0x72,
+0x2f, 0x3f, 0x00, 0x6c, 0x48, 0x8f, 0xfc, 0x4f, 0xe3, 0x9d, 0x7e, 0xd8, 0x00, 0xbf, 0x86, 0xc1,
+0x32, 0xb2, 0xe6, 0x1a, 0x54, 0x4f, 0x21, 0x69, 0xfe, 0x6f, 0x03, 0x7b, 0x7f, 0xcf, 0x3a, 0x84,
+0x5c, 0x72, 0x10, 0x9d, 0x40, 0x5f, 0x10, 0xd6, 0xb0, 0x93, 0x1f, 0x11, 0x44, 0x01, 0x85, 0xbc,
+0x55, 0xaa, 0x2e, 0xe4, 0xdd, 0x77, 0x18, 0xbb, 0xc0, 0x00, 0xf8, 0x0d, 0x64, 0x21, 0x0f, 0xe4,
+0xd9, 0x36, 0x00, 0xf6, 0x36, 0x1b, 0x16, 0xc0, 0x0f, 0x64, 0x09, 0x2f, 0xd8, 0x79, 0xf6, 0x06,
+0x00, 0xcd, 0x12, 0x3d, 0xb4, 0x90, 0x1f, 0x2f, 0x2c, 0x59, 0xee, 0x11, 0x4b, 0x16, 0x0f, 0x8f,
+0x64, 0x0d, 0x00, 0x43, 0x36, 0xf8, 0xbf, 0x1f, 0x3c, 0x61, 0x2f, 0x18, 0x18, 0xff, 0xc9, 0x90,
+0xff, 0xc2, 0x1a, 0x72, 0xc8, 0xda, 0x1f, 0x3f, 0x0c, 0xd9, 0x2d, 0x1f, 0x36, 0x24, 0x0f, 0x37,
+0xbf, 0xd8, 0x13, 0x37, 0x30, 0x3f, 0x6d, 0x95, 0x4a, 0xbf, 0xd9, 0x85, 0x1f, 0xf7, 0x5f, 0x1b,
+0x6c, 0x6d, 0xf7, 0x3f, 0x64, 0x21, 0x2f, 0x65, 0xb3, 0x1f, 0x2f, 0x65, 0xb3, 0x3f, 0x2f, 0x61,
+0xb0, 0xdd, 0xff, 0x2f, 0x64, 0x21, 0x5f, 0x46, 0x76, 0xff, 0xdf, 0x16, 0x12, 0x86, 0xaf, 0x0d,
+0x4b, 0xdf, 0xbd, 0x84, 0x3f, 0xef, 0xc9, 0x85, 0x3f, 0x3f, 0x24, 0x0b, 0x4f, 0x84, 0x20, 0x2d,
+0x5f, 0x24, 0x8c, 0xaf, 0x9b, 0x6c, 0x4f, 0xff, 0x00, 0x61, 0x21, 0x9f, 0xe4, 0xb0, 0x00, 0xf0,
+0x48, 0x86, 0x0f, 0x58, 0x58, 0x36, 0xe4, 0x30, 0x8f, 0x44, 0x58, 0x0f, 0x08, 0x10, 0x8f, 0x96,
+0x5b, 0x0f, 0x5f, 0xe5, 0x12, 0x0f, 0x1e, 0x96, 0x4f, 0xf5, 0x66, 0xb2, 0x41, 0x0e, 0x1f, 0x12,
+0x56, 0x4f, 0x81, 0xf4, 0x3f, 0xff, 0x21, 0xe9, 0x04, 0x08, 0xaf, 0xb7, 0xb0, 0x66, 0x0f, 0x2f,
+0x1c, 0x30, 0x8f, 0x40, 0x80, 0x5b, 0x18, 0x9f, 0x0f, 0x68, 0x60, 0x7e, 0xaa, 0x78, 0xcd, 0x1e,
+0xaf, 0x7e, 0x0f, 0x2c, 0xf6, 0x3a, 0x7e, 0x40, 0x0f, 0x85, 0xc1, 0x4f, 0x08, 0xb4, 0x0f, 0xff,
+0x3b, 0x5b, 0x2f, 0x3c, 0x1f, 0x02, 0x01, 0x8f, 0xac, 0x52, 0x5d, 0x96, 0xd4, 0x41, 0x16, 0xa4,
+0x3e, 0x10, 0x90, 0x4a, 0x6e, 0xf6, 0x5e, 0x0f, 0xbc, 0x6f, 0x06, 0x17, 0x66, 0x1f, 0xe7, 0x52,
+0x50, 0x2a, 0x02, 0x0f, 0x09, 0xee, 0x90, 0x58, 0x97, 0x0c, 0x30, 0x1f, 0x81, 0xac, 0xac, 0xb0,
+0x61, 0x42, 0x00, 0xdb, 0xf3, 0x0f, 0x0c, 0xec, 0x34, 0x3c, 0x35, 0x08, 0x5f, 0x6c, 0x81, 0x81,
+0x58, 0x2c, 0xd9, 0x16, 0xa0, 0x32, 0xbc, 0x93, 0x2f, 0x00, 0x7e, 0x06, 0x90, 0x00, 0x0e, 0xc1,
+0x0b, 0x05, 0x1c, 0x61, 0x03, 0x7e, 0x07, 0x16, 0x01, 0x7e, 0x80, 0x04, 0xff, 0x93, 0x20, 0xfd,
+0x95, 0x04, 0xfb, 0x4a, 0x82, 0xf9, 0x38, 0x41, 0xf7, 0x8c, 0x24, 0xf5, 0x58, 0x12, 0xf3, 0x8c,
+0x24, 0xf1, 0x5d, 0x12, 0xef, 0x78, 0x24, 0x98, 0x27, 0xec, 0x4e, 0xb0, 0xeb, 0x2c, 0x09, 0xea,
+0x82, 0x13, 0xe9, 0x49, 0x70, 0xe8, 0xe7, 0x98, 0x4a, 0x24, 0x82, 0x12, 0xe5, 0x04, 0x4b, 0xe4,
+0x70, 0xaa, 0xe3, 0x32, 0x48, 0x49, 0xe1, 0x24, 0x24, 0xde, 0x18, 0x12, 0xdd, 0x30, 0x24, 0xdb,
+0x1c, 0x49, 0xda, 0x00, 0x92, 0xa0, 0xd7, 0xac, 0x37, 0x0d, 0x01, 0x49, 0xb0, 0xd2, 0x27, 0x08,
+0xd1, 0x84, 0x04, 0xcf, 0x93, 0x20, 0xcd, 0x95, 0x04, 0xcb, 0x4c, 0x82, 0xc9, 0x54, 0x12, 0xc7,
+0x45, 0x82, 0xc3, 0x93, 0xc0, 0x76, 0xc1, 0x84, 0x04, 0xbe, 0x93, 0x20, 0xbd, 0x96, 0x04, 0xbc,
+0xf0, 0x09, 0xbb, 0x66, 0xc6, 0x1c, 0x48, 0xb9, 0x76, 0x52, 0x12, 0x5f, 0x34, 0x09, 0xb5, 0x92,
+0xe0, 0x06, 0xb3, 0x20, 0xd9, 0x20, 0x12, 0x9c, 0xb0, 0xae, 0xb0, 0x40, 0x4b, 0xc2, 0x18, 0xfe,
+0xfb, 0x49, 0x10, 0xa5, 0x93, 0x5a, 0x44, 0x97, 0x04, 0xa3, 0x7e, 0x96, 0x04, 0xa1, 0x04, 0xa7,
+0x20, 0x1c, 0x96, 0x9f, 0xa3, 0x5c, 0x12, 0x9d, 0x1e, 0x02, 0x12, 0x9b, 0x49, 0xf0, 0x1b, 0x99,
+0x04, 0x6f, 0x0d, 0xc1, 0xec, 0x97, 0xd4, 0x70, 0x20, 0x95, 0x7c, 0x60, 0x49, 0x93, 0x41, 0x92,
+0x92, 0x09, 0x26, 0x80, 0x8e, 0x62, 0x2b, 0xb3, 0x12, 0xbc, 0x1e, 0x8b, 0x09, 0x56, 0x88, 0x04,
+0x09, 0xc1, 0x8b, 0x87, 0x3c, 0x2b, 0x25, 0x84, 0xf3, 0x48, 0x25, 0x91, 0x12, 0x4c, 0xfc, 0x7f,
+0x24, 0x58, 0x7e, 0x4e, 0x10, 0x7d, 0xd4, 0x09, 0x7c, 0xa7, 0x96, 0x8b, 0x8a, 0xa3, 0x65, 0x0d,
+0xd8, 0x25, 0xc1, 0x77, 0x42, 0xea, 0x66, 0x3a, 0x95, 0x84, 0xd4, 0x83, 0x20, 0xc1, 0x3c, 0x71,
+0xea, 0x6c, 0xed, 0xe6, 0x64, 0x59, 0x8e, 0x6c, 0x6c, 0x24, 0x18, 0xf0, 0x6c, 0x3a, 0xb5, 0x8b,
+0x35, 0x90, 0x8a, 0x75, 0x21, 0x88, 0x7c, 0xc8, 0x20, 0x65, 0x7c, 0x96, 0x4d, 0xf0, 0x1b, 0x81,
+0x09, 0x62, 0x70, 0x20, 0x61, 0xe6, 0x6c, 0x49, 0x5f, 0x37, 0x3b, 0x35, 0x7d, 0x99, 0x48, 0x2d,
+0xca, 0x24, 0x58, 0x58, 0x12, 0x24, 0x57, 0x36, 0x23, 0xd7, 0x0d, 0x5e, 0x29, 0x1c, 0x66, 0x25,
+0xa4, 0xb3, 0x6a, 0x9b, 0x45, 0xf7, 0x8c, 0x04, 0xcb, 0xa0, 0x04, 0x87, 0x4f, 0x3c, 0x82, 0x95,
+0x4c, 0x82, 0x41, 0x4b, 0x3c, 0x18, 0x48, 0x49, 0xff, 0x03, 0x24, 0x45, 0xf0, 0x04, 0x43, 0x76,
+0x1c, 0x48, 0x41, 0x7c, 0x02, 0x12, 0x3f, 0x49, 0xf0, 0xc3, 0x3d, 0x5c, 0x76, 0x76, 0x1b, 0xfe,
+0x09, 0x92, 0x39, 0x90, 0x60, 0x7c, 0x38, 0x0c, 0x18, 0x76, 0x96, 0x20, 0x41, 0x35, 0x48, 0x30,
+0xe6, 0x33, 0x12, 0x1c, 0x3c, 0x31, 0x24, 0x58, 0x2f, 0x21, 0x24, 0xbf, 0x78, 0xb1, 0x26, 0x29,
+0x24, 0x24, 0x2b, 0x12, 0x12, 0x09, 0x26, 0xa4, 0x97, 0x7c, 0x61, 0xc2, 0x1b, 0x7c, 0xc1, 0x60,
+0xd9, 0x8b, 0x3b, 0x90, 0x21, 0xf0, 0x37, 0x26, 0x2f, 0x70, 0x1c, 0xd4, 0x09, 0x1d, 0x82, 0x84,
+0xac, 0x42, 0x42, 0x1a, 0xbf, 0x31, 0x5b, 0x0d, 0xab, 0x65, 0x86, 0xf5, 0x80, 0x0d, 0x29, 0x82,
+0xa7, 0x83, 0x7c, 0x1e, 0x49, 0x11, 0x83, 0x2e, 0x09, 0x0f, 0x0e, 0x09, 0x09, 0x0d, 0x81, 0x04,
+0x0b, 0x20, 0xc1, 0x70, 0x09, 0x4d, 0x4a, 0x66, 0x1b, 0x20, 0x71, 0x08, 0xac, 0x98, 0x03, 0x35,
+0x20, 0x96, 0x00, 0x3b, 0x30, 0x8f, 0xfe, 0x1b, 0x04, 0x26, 0xf0, 0xb1, 0xcd, 0x9a, 0xfe, 0xb3,
+0x29, 0xe9, 0x0d, 0x63, 0x83, 0x01, 0xf7, 0x7c, 0x60, 0x65, 0xa7, 0x0d, 0x16, 0x70, 0xd8, 0xc3,
+0x18, 0xf0, 0xf0, 0xd8, 0xcc, 0xc6, 0xf1, 0x4a, 0x4a, 0x63, 0xbe, 0x97, 0x67, 0x4f, 0xfe, 0xb0,
+0x92, 0x71, 0x2c, 0x19, 0xe7, 0xd1, 0x48, 0xb6, 0xc3, 0x01, 0x0f, 0xe3, 0x61, 0x87, 0xe2, 0x7e,
+0x8b, 0xc9, 0x22, 0xbf, 0x96, 0x01, 0xdd, 0x04, 0x03, 0xdc, 0x76, 0x38, 0xad, 0x94, 0x53, 0x10,
+0x60, 0x65, 0xa1, 0x97, 0x16, 0x46, 0x0c, 0x03, 0x71, 0x91, 0x8c, 0x44, 0x03, 0x3e, 0xbf, 0xf8,
+0xcc, 0xd2, 0xc0, 0x2d, 0x47, 0x06, 0x3c, 0xd0, 0xcf, 0x40, 0x5a, 0x1d, 0xc0, 0x64, 0x70, 0xc7,
+0x64, 0xcb, 0x78, 0xf3, 0xb1, 0x8c, 0x9f, 0x32, 0xc0, 0xe0, 0xc7, 0x4a, 0x46, 0xc9, 0xc0, 0x32,
+0x74, 0xb0, 0x64, 0xc3, 0x7d, 0x61, 0x40, 0xc0, 0x30, 0x60, 0xbe, 0x65, 0x41, 0x70, 0x20, 0xd8,
+0x30, 0x6f, 0x80, 0xd6, 0x13, 0x01, 0x25, 0xcd, 0xba, 0x01, 0x31, 0x60, 0xb7, 0x4c, 0x48, 0x1d,
+0xfc, 0x07, 0x06, 0xb3, 0x70, 0x96, 0x01, 0xb1, 0x2e, 0x09, 0x80, 0xfe, 0x97, 0x90, 0x01, 0x6f,
+0x18, 0x30, 0xb5, 0x5c, 0x18, 0x41, 0x44, 0xf3, 0x08, 0x03, 0x10, 0x60, 0x32, 0x60, 0xa3, 0x06,
+0x04, 0x9f, 0xc0, 0x55, 0xf8, 0x60, 0x65, 0x9b, 0xc0, 0x32, 0x99, 0x20, 0x65, 0x97, 0xc0, 0x32,
+0x95, 0x58, 0x64, 0x93, 0xac, 0x0c, 0x8f, 0x80, 0x07, 0x8d, 0x40, 0xca, 0x8b, 0x60, 0x65, 0x89,
+0x18, 0x32, 0x87, 0x1f, 0x2b, 0x0c, 0x83, 0x90, 0x01, 0x51, 0xca, 0x80, 0x7d, 0x65, 0xc0, 0x7b,
+0x36, 0x20, 0x77, 0x1f, 0x30, 0x42, 0x80, 0x19, 0x75, 0xc0, 0xca, 0x73, 0x0c, 0x78, 0x71, 0x6f,
+0x01, 0x23, 0x32, 0xe0, 0x6d, 0x6b, 0x19, 0x90, 0x69, 0x32, 0x60, 0x67, 0x19, 0x30, 0x65, 0x80,
+0x5b, 0x1b, 0x5c, 0x61, 0x61, 0x3f, 0x11, 0x06, 0x5d, 0x06, 0xbc, 0xd1, 0x59, 0xc0, 0x14, 0xff,
+0x5c, 0x61, 0x55, 0xf8, 0x00, 0x06, 0x51, 0xc2, 0x80, 0x4d, 0x18, 0xb0, 0x4b, 0x0c, 0x58, 0x47,
+0x81, 0xa9, 0x44, 0xbd, 0x49, 0x2f, 0x08, 0x10, 0xfd, 0x49, 0x6f, 0x0d, 0xd3, 0x49, 0x6f, 0x0d,
+0xc5, 0x32, 0x60, 0x3d, 0x30, 0x60, 0x0e, 0x3b, 0x2d, 0x3b, 0x1c, 0x37, 0x03, 0x26, 0x7f, 0x37,
+0x52, 0x2c, 0x86, 0x2d, 0x0b, 0x0d, 0xb0, 0xec, 0x29, 0x7d, 0x60, 0x19, 0x31, 0x38, 0x30, 0x2f,
+0x7e, 0x25, 0x0c, 0x2d, 0x49, 0x88, 0x0d, 0x1c, 0x81, 0x01, 0x29, 0x61, 0xc0, 0x7c, 0x27, 0x56,
+0x2c, 0x0d, 0xdd, 0x0c, 0x58, 0x22, 0x06, 0xa4, 0x20, 0x0c, 0x58, 0x1e, 0xe0, 0x2d, 0xdc, 0x92,
+0x4b, 0x5b, 0x66, 0x19, 0x10, 0x19, 0x0c, 0x08, 0x17, 0x1e, 0xb0, 0x15, 0x20, 0x03, 0x12, 0x32,
+0x60, 0x18, 0x0f, 0x06, 0x1c, 0x3c, 0x1c, 0x0d, 0x03, 0x56, 0x0b, 0x01, 0x21, 0x70, 0x81, 0x08,
+0x08, 0x60, 0x19, 0x05, 0x03, 0x1c, 0xfc, 0xfb, 0x60, 0x80, 0xf6, 0x00, 0x0d, 0xf5, 0x6c, 0x04,
+0x98, 0xee, 0xe9, 0xd0, 0x16, 0x06, 0x38, 0x5f, 0x2b, 0x81, 0x0d, 0x0f, 0xfe, 0x80, 0x07, 0x19,
+0x02, 0x4c, 0xd7, 0xd1, 0x04, 0x08, 0xcb, 0x08, 0x10, 0xc5, 0x01, 0x3e, 0x0f, 0x07, 0x0f, 0x7d,
+0xbf, 0x20, 0x80, 0xba, 0x70, 0x80, 0xb4, 0x01, 0x0a, 0xb3, 0xae, 0x06, 0xf8, 0x67, 0xe6, 0xc0,
+0xa7, 0xd0, 0x00, 0xa8, 0x80, 0x0d, 0x0e, 0xa1, 0x20, 0x78, 0x80, 0x02, 0x9b, 0x02, 0x00, 0x07,
+0x42, 0x13, 0xc0, 0x33, 0x8f, 0xc0, 0x00, 0x8a, 0xbb, 0x1b, 0x89, 0x00, 0x3e, 0x61, 0x49, 0x3c,
+0x86, 0x5e, 0xb0, 0x7c, 0xd0, 0x27, 0x04, 0xf8, 0x26, 0xff, 0x72, 0xbc, 0xc3, 0x72, 0x0f, 0x48,
+0x02, 0x65, 0x00, 0x07, 0x5f, 0xc0, 0x97, 0x59, 0x24, 0x66, 0xff, 0x0f, 0x38, 0x12, 0x9f, 0xbc,
+0x18, 0xef, 0x2e, 0x06, 0x02, 0x3d, 0xd0, 0xf0, 0x46, 0xad, 0x24, 0x68, 0x08, 0x31, 0x56, 0xfc,
+0x57, 0x18, 0x3e, 0x83, 0xf0, 0xe0, 0x7c, 0x18, 0x5b, 0x00, 0x0f, 0x23, 0x38, 0x64, 0xf5, 0x1e,
+0x76, 0xcc, 0x98, 0x86, 0xf9, 0x30, 0xc0, 0x7c, 0x0f, 0x0c, 0x40, 0x12, 0x06, 0xb0, 0x25, 0x00,
+0x2b, 0x03, 0x61, 0x7e, 0xd6, 0x18, 0x56, 0xe8, 0xbd, 0xea, 0x63, 0xd6, 0xe1, 0xc5, 0x5a, 0xdd,
+0xc2, 0x17, 0x5f, 0xc9, 0x1c, 0xe5, 0xaf, 0xba, 0x07, 0x3c, 0xb3, 0x71, 0x56, 0xca, 0x1e, 0xe0,
+0x7d, 0x73, 0xfc, 0x0f, 0xf1, 0xe8, 0xa9, 0xfc, 0xad, 0x53, 0xac, 0xea, 0x79, 0x2c, 0xf5, 0xbd,
+0x02, 0x07, 0x7e, 0xf5, 0x62, 0xab, 0x74, 0xf7, 0x66, 0x62, 0x77, 0x67, 0x56, 0x8f, 0xaa, 0x94,
+0xf5, 0x48, 0xa6, 0x59, 0x7d, 0x8d, 0x7c, 0x88, 0x5c, 0xb9, 0x07, 0x82, 0x7a, 0x25, 0x6b, 0x87,
+0xac, 0x46, 0x3d, 0xfc, 0x76, 0xcc, 0x60, 0xfc, 0xcc, 0xbd, 0x32, 0xd0, 0x6b, 0x56, 0x5f, 0x69,
+0xfe, 0x64, 0x68, 0x36, 0xfe, 0x07, 0xd5, 0x67, 0xf5, 0x1f, 0xce, 0xc1, 0xc2, 0x57, 0xe0, 0x0b,
+0x56, 0x4d, 0x2f, 0xc1, 0x6a, 0x1e, 0x45, 0x06, 0xa3, 0xe6, 0x7f, 0xf0, 0x14, 0xac, 0x39, 0x8b,
+0x49, 0xc1, 0xcb, 0xe2, 0xc5, 0xca, 0xde, 0x30, 0x1e, 0x6f, 0x7f, 0x8f, 0x45, 0x0f, 0xce, 0x7c,
+0x0e, 0xf1, 0x22, 0x37, 0xca, 0x7d, 0x21, 0x0c, 0x7f, 0xa4, 0xf7, 0x0b, 0x57, 0xa5, 0x08, 0x35,
+0x56, 0x41, 0x2a, 0xf8, 0x22, 0x16, 0x9d, 0x9d, 0xf4, 0x9b, 0x64, 0x3c, 0xdd, 0x48, 0xe7, 0x32,
+0x6f, 0x3c, 0x08, 0x52, 0xdf, 0x53, 0x07, 0x97, 0xd9, 0x3a, 0x29, 0xd5, 0x3c, 0x61, 0xe0, 0x26,
+0x61, 0xd4, 0x21, 0xba, 0xd4, 0x80, 0xbc, 0x76, 0x1a, 0xc0, 0x39, 0x75, 0xdc, 0xc3, 0xa4, 0xb0,
+0x7f, 0x43, 0xaa, 0x16, 0xdd, 0x53, 0xa4, 0x7c, 0x97, 0x60, 0x3a, 0xa3, 0xf8, 0xf5, 0x76, 0xe5,
+0x9a, 0x09, 0xf8, 0x2f, 0x54, 0x3b, 0x92, 0xaf, 0x0a, 0xef, 0x8c, 0x7f, 0x69, 0xb8, 0xa7, 0x84,
+0x3c, 0x17, 0x46, 0x3d, 0x00, 0xe6, 0xd6, 0x91, 0x5a, 0x74, 0x18, 0x16, 0xce, 0x16, 0x0c, 0x6d,
+0x0f, 0x4d, 0x36, 0x7c, 0x4f, 0x1e, 0xb3, 0x48, 0x56, 0x5f, 0x82, 0x8b, 0x7e, 0x4b, 0x87, 0xc5,
+0xa9, 0x4a, 0x90, 0x9a, 0x47, 0x43, 0x60, 0xd4, 0x56, 0x30, 0xac, 0xfb, 0x1b, 0x8b, 0x00, 0x17,
+0x7e, 0x2f, 0x1a, 0x9c, 0x37, 0xdf, 0x7e, 0x81, 0xa7, 0x21, 0x70, 0x61, 0x1b, 0xd6, 0x89, 0x0c,
+0xb3, 0xdc, 0x12, 0x6b, 0x58, 0x46, 0xfc, 0x54, 0x4a, 0xf7, 0x91, 0x3b, 0xf9, 0x7f, 0x25, 0x9c,
+0x8e, 0xfe, 0xf7, 0xc2, 0x84, 0x57, 0xef, 0x23, 0x4c, 0x7c, 0xf7, 0x7c, 0x84, 0x24, 0xef, 0x60,
+0x34, 0xc6, 0xa5, 0x84, 0xef, 0xd4, 0x42, 0xa0, 0x38, 0x49, 0x28, 0x9f, 0xf7, 0x08, 0x46, 0xa9,
+0x6c, 0x49, 0xff, 0x0f, 0x60, 0x84, 0x13, 0xcd, 0xef, 0x86, 0x30, 0xcf, 0x10, 0x8c, 0x8a, 0xa1,
+0x92, 0xe7, 0x76, 0xe0, 0x05, 0x0c, 0xa0, 0x4d, 0x99, 0xd2, 0xd1, 0xf6, 0x73, 0x57, 0xfc, 0x37,
+0x8b, 0x69, 0x26, 0x97, 0x53, 0x21, 0xd3, 0x17, 0x37, 0xa3, 0x5a, 0x6f, 0x42, 0xa6, 0x38, 0xe1,
+0xe8, 0x61, 0x7f, 0x06, 0x0c, 0xe8, 0x1f, 0x7e, 0xed, 0x87, 0xc1, 0x7b, 0xc6, 0xa3, 0x47, 0xad,
+0x37, 0xcd, 0xe8, 0x3c, 0xeb, 0xa3, 0x18, 0x2e, 0x2a, 0x47, 0x07, 0x38, 0x7e, 0x23, 0x12, 0x5e,
+0xe0, 0x3f, 0xb7, 0x8b, 0xd1, 0x18, 0x0f, 0x66, 0x14, 0x0f, 0xbd, 0x8f, 0x0c, 0x70, 0x07, 0x08,
+0x83, 0x67, 0xfe, 0xb7, 0x60, 0x30, 0xe6, 0xb1, 0x12, 0xde, 0x5c, 0xb8, 0xaf, 0x76, 0xec, 0x10,
+0xc8, 0x22, 0x88, 0x01, 0x43, 0x68, 0xd9, 0x1a, 0x42, 0xd4, 0xb4, 0x12, 0xca, 0x84, 0x26, 0x9a,
+0xbe, 0x21, 0x34, 0xb8, 0x0d, 0xa1, 0xb2, 0x1a, 0x09, 0xac, 0xd0, 0x12, 0xa0, 0x10, 0x9a, 0x9a,
+0x94, 0x81, 0xd0, 0x8e, 0x80, 0xd0, 0xf8, 0x82, 0x03, 0xa1, 0x7c, 0x01, 0xa1, 0xff, 0x70, 0xc3,
+0x62, 0x0f, 0x1f, 0x02, 0x42, 0x5e, 0x90, 0x87, 0x52, 0x37, 0x26, 0x94, 0x4c, 0xb4, 0x84, 0x46,
+0x12, 0x3a, 0x40, 0x3a, 0x86, 0xd0, 0x34, 0x34, 0x84, 0x2e, 0xa1, 0x21, 0x28, 0x22, 0xcc, 0x2d,
+0x0f, 0x6f, 0x20, 0x34, 0x10, 0x4b, 0x28, 0x0a, 0x2c, 0x66, 0x77, 0xfe, 0xe2, 0x0d, 0x27, 0xd8,
+0xc0, 0xf2, 0xd7, 0x16, 0x6f, 0x27, 0xdd, 0x2c, 0x06, 0xd7, 0x58, 0x0c, 0xcb, 0xb0, 0x18, 0xc2,
+0x8b, 0xd3, 0x47, 0xbc, 0xb8, 0xc7, 0x47, 0x07, 0xe1, 0x62, 0x2f, 0x07, 0x93, 0x98, 0x27, 0xcd,
+0x06, 0x8b, 0x0e, 0xa4, 0x1c, 0x44, 0xc6, 0x1f, 0xc6, 0x66, 0x27, 0xd7, 0x66, 0x07, 0x3a, 0xe6,
+0x0f, 0x47, 0x40, 0xb8, 0x5c, 0x4f, 0x4f, 0x40, 0xbe, 0x7c, 0xd9, 0xde, 0x02, 0x3e, 0x40, 0xb6,
+0xa7, 0x3e, 0x9b, 0xb4, 0x40, 0xbc, 0x27, 0x07, 0x62, 0x50, 0xf6, 0xef, 0xf6, 0x17, 0x4c, 0x08,
+0x1c, 0x2f, 0x83, 0x18, 0xc7, 0x2e, 0x3c, 0x38, 0x7e, 0x47, 0xb8, 0x58, 0x55, 0x05, 0x83, 0x5e,
+0x4f, 0x6a, 0x72, 0x30, 0x51, 0x59, 0x4c, 0x0f, 0x3e, 0xcc, 0x62, 0x38, 0x18, 0x58, 0x33, 0x45,
+0x32, 0x6e, 0x2f, 0x82, 0x21, 0x1f, 0x6b, 0x16, 0x1b, 0xdd, 0xab, 0xdc, 0x50, 0x4a, 0x48, 0x17,
+0x00, 0x00, 0x0d, 0x62, 0x33, 0x0e, 0xc8, 0x81, 0x4c, 0x52, 0x03, 0x21, 0x47, 0x69, 0x26, 0x64,
+0x04, 0x82, 0xa2, 0x46, 0x11, 0x03, 0x02, 0x4d, 0x18, 0x99, 0x06, 0x31, 0xcc, 0xf3, 0x03, 0x06,
+0x68, 0x58, 0x44, 0x03, 0x06, 0x18, 0x69, 0x65, 0x11, 0x53, 0x84, 0xbd, 0x2f, 0x0f, 0x56, 0x80,
+0x98, 0x0f, 0x09, 0x12, 0x58, 0x08, 0x59, 0x2f, 0x0d, 0x8b, 0x73, 0x61, 0xef, 0x6f, 0x4f, 0x5f,
+0x66, 0x2a, 0x48, 0x0f, 0x01, 0x12, 0xe7, 0x0c, 0x59, 0x1f, 0x66, 0x81, 0x45, 0x53, 0x85, 0xb5,
+0x06, 0x0f, 0x80, 0xbd, 0x2f, 0x0f, 0x48, 0xc4, 0xc6, 0x13, 0x98, 0x4b, 0x8d, 0x0f, 0x84, 0x4b,
+0xdc, 0x08, 0x19, 0xf3, 0x42, 0x06, 0xd2, 0x5a, 0xac, 0xa9, 0xf5, 0x6e, 0x0c, 0x16, 0xea, 0x3e,
+0xd2, 0xfe, 0xc9, 0x1e, 0x0f, 0xce, 0xff, 0x21, 0x10, 0x03, 0x32, 0x20, 0xf4, 0x6c, 0x59, 0xaf,
+0x0f, 0x21, 0xbc, 0x30, 0x78, 0x4f, 0xd9, 0x61, 0x1f, 0x0f, 0x2e, 0x62, 0x23, 0xd9, 0x92, 0xc6,
+0x4c, 0xd9, 0x2c, 0x3f, 0x1c, 0x30, 0x68, 0xba, 0xae, 0x1e, 0x04, 0x7c, 0xce, 0x64, 0x07, 0xe1,
+0xe6, 0x7c, 0x40, 0x60, 0x64, 0x60, 0xca, 0x04, 0x8d, 0x73, 0xb3, 0x52, 0x1c, 0x1d, 0xcd, 0xda,
+0x1e, 0x1f, 0x53, 0x42, 0x7c, 0x0e, 0x1a, 0xa3, 0x1b, 0x93, 0x18, 0x0e, 0xb1, 0xd4, 0x01, 0xb0,
+0x10, 0xaf, 0x03, 0xe1, 0xef, 0x4f, 0x2d, 0x7b, 0x0f, 0xcf, 0x85, 0xec, 0x2f, 0xbf, 0x28, 0x1c,
+0xad, 0x70, 0x49, 0x93, 0x0c, 0x21, 0xa9, 0xa3, 0x62, 0xa3, 0x3c, 0xc9, 0x36, 0xd1, 0x12, 0x23,
+0x24, 0x9b, 0xd9, 0xcb, 0x3b, 0x03, 0xa7, 0x02, 0x60, 0xc0, 0x24, 0x40, 0x33, 0xa2, 0xfb, 0x82,
+0xb2, 0xaa, 0xaa, 0x02, 0x82, 0xb5, 0x0c, 0x63, 0x00, 0x4c, 0x01, 0x16, 0x4e, 0x77, 0xe0, 0xa8,
+0x6c, 0x62, 0x0b, 0xe8, 0xdc, 0xb2, 0x2d, 0x98, 0x3e, 0x0f, 0x9a, 0xfd, 0x66, 0xce, 0x9a, 0x3f,
+0x22, 0xe8, 0x9d, 0x2e, 0xbc, 0x36, 0x2c, 0x96, 0x85, 0xcc, 0x3c, 0x36, 0x6c, 0xd8, 0x00, 0x66,
+0xb7, 0x0d, 0x6c, 0xd8, 0x60, 0x10, 0x33, 0xb4, 0x50, 0x94, 0x6c, 0x64, 0x6f, 0x0f, 0x21, 0x2f,
+0x60, 0x30, 0x77, 0x6c, 0xef, 0x9a, 0xa2, 0x00, 0x88, 0x58, 0x9a, 0xef, 0xa1, 0x32, 0x33, 0x9d,
+0x62, 0xff, 0x5e, 0x92, 0x04, 0xc1, 0x10, 0x29, 0xa8, 0x0c, 0xe2, 0xf1, 0x33, 0xb1, 0x8a, 0xcb,
+0x98, 0x12, 0x5f, 0x09, 0x73, 0x0c, 0xff, 0x64, 0x10, 0x33, 0xa4, 0x50, 0x0f, 0x62, 0xd0, 0x89,
+0x7e, 0x10, 0x7f, 0x34, 0x18, 0x2c, 0x06, 0x3e, 0x04, 0x3d, 0xbe, 0x22, 0x97, 0x03, 0xf6, 0x64,
+0x1d, 0xbf, 0x1f, 0x17, 0x36, 0xc6, 0x0f, 0x30, 0x12, 0x58, 0x3f, 0xc7, 0x92, 0x3f, 0x0f, 0xb0,
+0x02, 0x53, 0xcc, 0x35, 0xac, 0x0f, 0x48, 0x26, 0x00, 0x19, 0x44, 0x33, 0xb3, 0x50, 0x40, 0xbd,
+0x60, 0x47, 0x9f, 0xb0, 0x90, 0x7f, 0x36, 0xac, 0xb7, 0x9f, 0x6f, 0x51, 0x84, 0xb6, 0x9c, 0xb3,
+0xcc, 0xcc, 0xff, 0x26, 0x6c, 0x1f, 0x30, 0xc3, 0xca, 0x2f, 0xd2, 0xb6, 0x2c, 0x1f, 0x0c, 0x01,
+0x4a, 0x93, 0x90, 0x03, 0x66, 0x0c, 0x60, 0xc0, 0xc3, 0x04, 0x68, 0x7c, 0xa3, 0xcc, 0x06, 0xf0,
+0x0e, 0xbe, 0x53, 0x2c, 0x8f, 0xc6, 0xdd, 0x40, 0x7f, 0xc6, 0x32, 0x24, 0xc6, 0xbc, 0x16, 0x9f,
+0x6e, 0x09, 0x3f, 0x0c, 0x21, 0x10, 0xb3, 0x0b, 0x23, 0x39, 0x02, 0x7b, 0x5f, 0x00, 0x5a, 0x42,
+0xbb, 0x85, 0x2c, 0x10, 0x12, 0x42, 0xe7, 0x58, 0xdf, 0xbd, 0x30, 0x62, 0x36, 0xec, 0x6f, 0x2b,
+0x4a, 0x33, 0x51, 0x2a, 0xca, 0x66, 0x19, 0x00, 0xe8, 0x41, 0xff, 0x11, 0xb2, 0x1d, 0x2b, 0x9b,
+0x94, 0x2a, 0x22, 0x60, 0x0a, 0x12, 0xb1, 0x90, 0x4c, 0x29, 0x58, 0x21, 0x0c, 0xa2, 0x7c, 0x33,
+0xaa, 0xa0, 0x42, 0x11, 0x89, 0x09, 0x1e, 0x18, 0x70, 0x03, 0x09, 0x2e, 0x76, 0x01, 0x13, 0x01,
+0xd0, 0xcb, 0x80, 0xff, 0xd9, 0x7b, 0x1b, 0x29, 0x0d, 0x32, 0x60, 0xfb, 0x30, 0x60, 0x76, 0xf9,
+0x8b, 0x38, 0x76, 0x2b, 0x7b, 0x09, 0x61, 0x45, 0x62, 0xd9, 0x53, 0x5b, 0x76, 0xf6, 0x0d, 0x44,
+0x60, 0x59, 0x6f, 0xe0, 0x32, 0xef, 0x3c, 0x37, 0x30, 0xed, 0x0d, 0x96, 0xbd, 0x29, 0x0d, 0x2c,
+0x03, 0xe9, 0x12, 0x06, 0xe7, 0x2d, 0x03, 0xe5, 0x60, 0x19, 0xa4, 0x42, 0x35, 0xe3, 0x18, 0x60,
+0xc0, 0x6e, 0xe1, 0x18, 0x70, 0xce, 0xdf, 0x01, 0x1b, 0x99, 0x1b, 0xc8, 0xc6, 0xc6, 0x0d, 0x65,
+0xc0, 0xd9, 0xd9, 0xb2, 0xfb, 0x0d, 0x11, 0x6f, 0x37, 0xb3, 0x24, 0xe5, 0x42, 0xf4, 0x66, 0x37,
+0x18, 0x70, 0x64, 0x20, 0x02, 0xc0, 0x1d, 0xcf, 0xe6, 0x7c, 0x80, 0x80, 0x65, 0xcd, 0x19, 0xf0,
+0xcc, 0xcb, 0xcb, 0x80, 0xca, 0xe0, 0x54, 0x63, 0x9c, 0x30, 0xc7, 0xd8, 0x70, 0x58, 0x06, 0xc5,
+0x09, 0x4b, 0xb1, 0x17, 0x65, 0x6f, 0x0d, 0xb5, 0x96, 0xbd, 0x29, 0xa7, 0x2c, 0x03, 0xbd, 0x12,
+0x06, 0xbb, 0x2b, 0x03, 0xb9, 0x84, 0x01, 0xb7, 0xc5, 0x80, 0xb5, 0x0f, 0x98, 0x53, 0xb3, 0x84,
+0x01, 0xb2, 0xe0, 0x80, 0xb1, 0xcd, 0x19, 0xaf, 0x63, 0x6e, 0xc3, 0xb6, 0xc3, 0x85, 0x1f, 0x0d,
+0xcc, 0xe8, 0x36, 0x6e, 0xda, 0x3c, 0x80, 0x07, 0xab, 0x18, 0xf0, 0xaa, 0xa8, 0x03, 0x16, 0xa6,
+0x06, 0x31, 0x33, 0x60, 0x55, 0xb0, 0x30, 0x9b, 0x48, 0x18, 0x99, 0x24, 0x0c, 0x97, 0x58, 0x06,
+0x95, 0x10, 0x0f, 0x94, 0x42, 0x64, 0x33, 0x30, 0x03, 0x8b, 0x03, 0x1e, 0x8a, 0x89, 0x10, 0x09,
+0x00, 0x64, 0x33, 0xcb, 0x80, 0x79, 0xc0, 0x80, 0x76, 0x77, 0x20, 0xdc, 0xdf, 0xa9, 0xc2, 0x33,
+0x92, 0x01, 0x67, 0x18, 0xd0, 0x03, 0x65, 0x01, 0x1a, 0xe9, 0x45, 0x26, 0xf6, 0x09, 0xa9, 0x95,
+0x9b, 0x3a, 0xb0, 0xc6, 0x0d, 0x30, 0xc6, 0x4a, 0xb7, 0xd7, 0x0c, 0x48, 0x59, 0x64, 0xb7, 0x0d,
+0x3c, 0x66, 0x42, 0x0e, 0x00, 0x19, 0x44, 0x33, 0xb1, 0x10, 0x38, 0x3b, 0x73, 0x0e, 0x06, 0x30,
+0x6f, 0x61, 0x09, 0x30, 0xd2, 0x69, 0x60, 0xd3, 0x01, 0x13, 0x78, 0xbd, 0x92, 0x42, 0x4e, 0x64,
+0x97, 0x1b, 0x30, 0x29, 0x30, 0x60, 0x30, 0xc0, 0x6c, 0x7c, 0x0a, 0x20, 0x64, 0x3a, 0x01, 0x33,
+0x28, 0x03, 0x96, 0x37, 0x35, 0x30, 0x64, 0xe0, 0x03, 0x1e, 0x34, 0x33, 0xc6, 0x2e, 0x7c, 0x6f,
+0xc6, 0x59, 0x42, 0x0d, 0x35, 0x6b, 0x0b, 0x03, 0x92, 0x97, 0x2b, 0x13, 0x2c, 0x1a, 0xca, 0x80,
+0x27, 0x0c, 0x38, 0xfe, 0x22, 0x01, 0x8b, 0xa9, 0x8c, 0x1f, 0x63, 0xe1, 0x46, 0x34, 0x73, 0x80,
+0x54, 0x30, 0xc0, 0x19, 0xbc, 0x62, 0x81, 0x6d, 0x40, 0x06, 0x13, 0x60, 0xc0, 0x18, 0x0f, 0x0c,
+0x58, 0x0d, 0x18, 0x44, 0x33, 0x40, 0x01, 0x26, 0x62, 0xfa, 0x28, 0x46, 0xff, 0x8c, 0x4e, 0xd6,
+0x76, 0xf6, 0x30, 0x1a, 0x7c, 0xf0, 0x63, 0x6e, 0x76, 0x17, 0x07, 0x60, 0x6c, 0x93, 0x30, 0x17,
+0x38, 0x93, 0x98, 0xe3, 0x05, 0xa3, 0x70, 0xd2, 0xba, 0x09, 0xef, 0x07, 0x30, 0x46, 0xc1, 0x3f,
+0xc0, 0xcc, 0x0d, 0x3c, 0x1f, 0x07, 0xc7, 0x5c, 0x17, 0x07, 0xc6, 0x10, 0x62, 0x10, 0xa4, 0x28,
+0xe7, 0x47, 0x27, 0xb1, 0x19, 0x41, 0x2c, 0xf8, 0x35, 0x42, 0xd5, 0xec, 0x47, 0x0b, 0x7f, 0x6e,
+0x97, 0xa0, 0xa3, 0x96, 0xce, 0x46, 0xc1, 0x41, 0x47, 0x37, 0x1b, 0x57, 0x07, 0x37, 0x23, 0x7e,
+0x8f, 0x70, 0xb3, 0x07, 0x1f, 0xda, 0x89, 0x2b, 0x06, 0x17, 0x29, 0x81, 0x0e, 0x1e, 0xbd, 0x27,
+0x5f, 0x47, 0x8f, 0x5e, 0x5a, 0x3d, 0x23, 0x59, 0x3a, 0x6c, 0x30, 0xda, 0x0f, 0x6c, 0xb8, 0x4e,
+0xd4, 0xd1, 0x48, 0x3c, 0x33, 0x7a, 0x47, 0x42, 0x30, 0x7b, 0xf7, 0x9f, 0xe6, 0x66, 0xd5, 0xd9,
+0x5f, 0x30, 0x0a, 0x2a, 0x20, 0xdc, 0x07, 0x64, 0xf4, 0xfc, 0x1f, 0x0d, 0xa3, 0x19, 0xd1, 0x19,
+0x12, 0x7c, 0x31, 0x9a, 0x0c, 0x0b, 0xed, 0x83, 0xfe, 0x04, 0x63, 0x6b, 0xbd, 0xfb, 0x7e, 0x33,
+0x66, 0xcc, 0x0f, 0x07, 0x7a, 0x36, 0x6a, 0xdf, 0x4c, 0xa8, 0x11, 0xf4, 0xed, 0x0e, 0xc1, 0x16,
+0x66, 0x33, 0x70, 0x5f, 0x44, 0x66, 0x09, 0x02, 0x83, 0x33, 0x84, 0x16, 0x97, 0x37, 0xcf, 0xda,
+0x9c, 0x07, 0x60, 0x30, 0x87, 0x26, 0xb2, 0x88, 0x43, 0xb1, 0x84, 0x32, 0x33, 0x14, 0x1c, 0xbc,
+0x65, 0x0e, 0x1c, 0xbf, 0xfa, 0x62, 0x42, 0x8d, 0x82, 0x0c, 0x33, 0x98, 0xd0, 0x5e, 0x62, 0x76,
+0x17, 0x7f, 0x88, 0x0c, 0x33, 0xef, 0x50, 0x28, 0xcd, 0x71, 0xe8, 0x76, 0x18, 0x6c, 0x22, 0xca,
+0x47, 0x9b, 0xf6, 0x7b, 0x18, 0xf7, 0x10, 0x59, 0x38, 0x63, 0x00, 0x17, 0x30, 0x08, 0xcb, 0x1f,
+0x9f, 0x11, 0x28, 0xe6, 0x0b, 0x16, 0xfb, 0x06, 0x08, 0x37, 0x07, 0x85, 0x32, 0x33, 0x0d, 0x12,
+0x20, 0x30, 0xc3, 0x82, 0x3f, 0x1b, 0x3d, 0x0e, 0x57, 0x37, 0xc6, 0xa2, 0xd5, 0xbc, 0xcc, 0xe1,
+0x1d, 0x5f, 0x0f, 0x5f, 0x2c, 0xde, 0x07, 0x9f, 0xc5, 0x1b, 0x7c, 0xef, 0x8b, 0x09, 0xab, 0x82,
+0x8f, 0xe2, 0xc0, 0x5a, 0x8b, 0x17, 0x89, 0x93, 0xd1, 0xdc, 0x07, 0x47, 0xb3, 0x98, 0x77, 0x89,
+0xcd, 0x1e, 0xaf, 0x0f, 0x38, 0xa3, 0x17, 0x9f, 0x0c, 0x8b, 0x77, 0x79, 0x77, 0x6b, 0x62, 0x00,
+0x7e, 0x62, 0xcb, 0x5d, 0x0a, 0xfd, 0x1f, 0x2a, 0xe1, 0x32, 0xe4, 0x3a, 0xf6, 0x2a, 0x5f, 0x86,
+0x82, 0x00, 0x34, 0x49, 0x80, 0x33, 0x06, 0x16, 0x3e, 0x78, 0x98, 0xb7, 0x87, 0x18, 0xb2, 0x2e,
+0x18, 0xe2, 0xb0, 0x21, 0x3c, 0x16, 0x0f, 0x2b, 0x7a, 0x19, 0xc4, 0x78, 0x33, 0x90, 0x03, 0xb5,
+0x72, 0x3c, 0x19, 0x53, 0x9d, 0x4c, 0x14, 0x91, 0x82, 0x92, 0xc8, 0x82, 0x20, 0x51, 0x21, 0x83,
+0x27, 0x61, 0x10, 0x33, 0x82, 0x50, 0x84, 0x0c, 0x87, 0x85, 0x41, 0x33, 0x0c, 0x82, 0x97, 0x64,
+0xa3, 0x00, 0x83, 0x60, 0xa7, 0x10, 0x21, 0x08, 0x65, 0x33, 0xc8, 0x20, 0xc7, 0x19, 0x44, 0x33,
+0x08, 0x42, 0x11, 0x32, 0xe7, 0x54, 0x06, 0x33, 0x2c, 0x82, 0x07, 0x41, 0x84, 0x08, 0x96, 0x33,
+0xac, 0xb2, 0x17, 0x65, 0x11, 0x07, 0x8b, 0x20, 0x27, 0xd9, 0x09, 0x04, 0x08, 0x0f, 0x11, 0x9c,
+0x58, 0x65, 0x27, 0xc8, 0x80, 0xa7, 0x10, 0xa3, 0x22, 0x64, 0x33, 0x2a, 0x03, 0xb7, 0x19, 0x44,
+0x33, 0x62, 0x10, 0x2a, 0x09, 0x27, 0x06, 0x11, 0x33, 0x62, 0x55, 0x28, 0x09, 0x67, 0x19, 0x44,
+0x33, 0x22, 0x14, 0x20, 0x09, 0xd7, 0x19, 0xc4, 0x33, 0x22, 0x14, 0x02, 0x0b, 0x47, 0x10, 0xa1,
+0x46, 0x65, 0x33, 0xb6, 0x20, 0x87, 0x60, 0xb6, 0x42, 0x36, 0x31, 0x09, 0x95, 0x05, 0x97, 0xae,
+0x04, 0xcf, 0x61, 0x10, 0x33, 0x20, 0x42, 0x2b, 0xb0, 0xc7, 0x60, 0x10, 0x33, 0x82, 0x08, 0x10,
+0xcb, 0xd7, 0x1c, 0x65, 0x33, 0x66, 0x88, 0x94, 0xb1, 0x0c, 0x07, 0x95, 0x00, 0x97, 0x20, 0xb6,
+0xce, 0xc6, 0x84, 0x10, 0xa7, 0x94, 0x41, 0x33, 0x02, 0x11, 0xa9, 0x40, 0x96, 0x01, 0xf9, 0xc2,
+0x20, 0x33, 0x04, 0x0b, 0x48, 0x48, 0x43, 0xb0, 0x0c, 0xe9, 0x91, 0x44, 0x51, 0x30, 0x60, 0xe1,
+0x20, 0x86, 0x7c, 0xb0, 0xc8, 0x33, 0x62, 0x19, 0xdb, 0x8b, 0x0c, 0x33, 0x48, 0x04, 0x97, 0x83,
+0x48, 0x33, 0x41, 0x22, 0x12, 0x16, 0x93, 0xcb, 0x20, 0x33, 0x59, 0x04, 0xa1, 0x08, 0x16, 0xc1,
+0xb2, 0x93, 0x42, 0x16, 0xaf, 0x04, 0x87, 0x13, 0x56, 0x59, 0xaf, 0x32, 0x20, 0x5f, 0x44, 0x00,
+0x48, 0x18, 0x33, 0xc8, 0x80, 0x6d, 0x06, 0x91, 0x33, 0x58, 0x14, 0xc8, 0x80, 0xcf, 0x41, 0x84,
+0x58, 0x91, 0x33, 0x48, 0x82, 0x07, 0x06, 0x11, 0x33, 0x48, 0x15, 0x42, 0x82, 0x69, 0x06, 0x31,
+0x33, 0x48, 0x15, 0x40, 0x82, 0xcb, 0x44, 0x20, 0x11, 0x19, 0x33, 0x2c, 0x88, 0x03, 0x66, 0x2b,
+0x30, 0x41, 0x4c, 0x41, 0x64, 0x11, 0x83, 0x78, 0x08, 0x10, 0x33, 0xb1, 0x02, 0x95, 0x05, 0x3b,
+0x0c, 0x62, 0x33, 0x41, 0xac, 0x88, 0x64, 0x49, 0x0c, 0x32, 0x33, 0x56, 0x06, 0x13, 0x12, 0x20,
+0xd3, 0x02, 0xc4, 0x71, 0xf1, 0x5a, 0xdb, 0x81, 0x00, 0x7f, 0x0c, 0x22, 0x33, 0x80, 0x00, 0x14,
+0x12, 0xa1, 0x20, 0x47, 0x41, 0x04, 0x08, 0x85, 0x33, 0x41, 0x28, 0x77, 0x60, 0x10, 0x33, 0x84,
+0x82, 0x7f, 0x19, 0x85, 0x98, 0xa1, 0x20, 0x87, 0x06, 0x31, 0x33, 0x0a, 0x42, 0x44, 0x10, 0x97,
+0x08, 0x19, 0x33, 0x41, 0x28, 0xa7, 0x64, 0x10, 0x33, 0xa3, 0x20, 0x37, 0x41, 0x04, 0x0a, 0x82,
+0x33, 0x10, 0x32, 0x3f, 0x82, 0x51, 0x37, 0x30, 0x0a, 0x47, 0xc1, 0xec, 0xd3, 0x07, 0x46, 0x46,
+0x47, 0x12, 0x20, 0x87, 0x10, 0x0b, 0x88, 0x60, 0x33, 0x8c, 0x04, 0x8f, 0x90, 0x41, 0x33, 0x80,
+0x58, 0x11, 0x4a, 0xc7, 0x50, 0x06, 0x33, 0x04, 0x88, 0xe7, 0x41, 0x8c, 0x58, 0x90, 0x33, 0xc8,
+0x80, 0x1f, 0x19, 0x44, 0x33, 0x88, 0x11, 0x08, 0x0c, 0x57, 0x06, 0xb1, 0x33, 0x88, 0x50, 0x85,
+0x0c, 0x77, 0x20, 0x86, 0x1b, 0x88, 0x32, 0x7f, 0x98, 0x45, 0x68, 0x2b, 0x83, 0x33, 0x18, 0x10,
+0x97, 0x20, 0x16, 0x10, 0xc9, 0x33, 0x88, 0x18, 0x9f, 0x10, 0x32, 0x33, 0x20, 0x0e, 0xf1, 0xc1,
+0x12, 0x37, 0x1c, 0x56, 0xff, 0xd3, 0x21, 0x40, 0x07, 0x0c, 0x22, 0x33, 0x92, 0xa7, 0xe9, 0x74,
+0x50, 0xf0, 0xe4, 0x5a, 0xd1, 0x72, 0x91, 0x55, 0x67, 0x92, 0x14, 0x80, 0xa0, 0x15, 0xc8, 0x22,
+0x15, 0xa3, 0x16, 0x7a, 0xf8, 0x4e, 0xab, 0xa0, 0x66, 0x67, 0x20, 0x28, 0x01, 0xab, 0x15, 0x9c,
+0x5b, 0x66, 0x15, 0x60, 0x67, 0x7a, 0x50, 0x7c, 0xc2, 0x05, 0x1f, 0x03, 0xc2, 0x7c, 0x41, 0x58,
+0x67, 0x90, 0x53, 0x1d, 0xbf, 0x3c, 0x19, 0x65, 0x05, 0x99, 0x91, 0x41, 0x33, 0x92, 0x14, 0x82,
+0x82, 0x42, 0xde, 0x00, 0x05, 0x2b, 0x48, 0x59, 0x67, 0x2d, 0x21, 0x53, 0x25, 0x00, 0x6a, 0x0b,
+0x6a, 0x8b, 0x54, 0x21, 0xb2, 0x60, 0x34, 0x07, 0x5b, 0xf6, 0x10, 0x08, 0x28, 0x0f, 0x49, 0xd0,
+0xa6, 0xab, 0x60, 0x67, 0x54, 0x2b, 0x20, 0x61, 0xad, 0x2a, 0xab, 0x67, 0x45, 0x70, 0x66, 0x00,
+0x4b, 0x24, 0x0f, 0xef, 0xc0, 0x2a, 0x82, 0x03, 0x8b, 0x0a, 0x23, 0xdf, 0xde, 0xff, 0x3c, 0x42,
+0x99, 0xa5, 0xa1, 0xa5, 0x99, 0x42, 0x0e, 0xb2, 0x0a, 0x67, 0x05, 0x90, 0x28, 0x65, 0x83, 0x32,
+0x03, 0x6d, 0xac, 0x82, 0x67, 0x00, 0x2a, 0x01, 0x10, 0xb0, 0xec, 0x09, 0x00, 0x95, 0x55, 0x67,
+0x46, 0x00, 0x0a, 0x03, 0xf9, 0x10, 0x0c, 0x70, 0x92, 0xef, 0x07, 0x18, 0xf4, 0xfe, 0x84, 0x01,
+0xf2, 0x60, 0xc0, 0x00, 0xef, 0x0c, 0x18, 0xed, 0x55, 0xb0, 0x80, 0x91, 0x67, 0x05, 0xcb, 0xe9,
+0x17, 0x59, 0x67, 0x66, 0x29, 0x82, 0x00, 0xc7, 0x40, 0x0a, 0x16, 0x60, 0xdb, 0xa4, 0x08, 0x9d,
+0x50, 0xaf, 0x38, 0x44, 0xb1, 0x5f, 0xcd, 0x44, 0x38, 0x2a, 0xac, 0x82, 0xb8, 0x67, 0x19, 0xb0,
+0xd3, 0x0f, 0x08, 0xd1, 0x2a, 0x30, 0x00, 0xc2, 0x67, 0x01, 0x2b, 0x82, 0x81, 0x17, 0x02, 0xac,
+0x67, 0x94, 0xa0, 0x52, 0x61, 0xe0, 0xb3, 0x0a, 0x67, 0x20, 0x05, 0xc3, 0x01, 0xa3, 0xd2, 0x0e,
+0x51, 0x93, 0x14, 0x8c, 0xbf, 0xa3, 0x64, 0xc1, 0xbb, 0x15, 0x0c, 0xa3, 0x60, 0x67, 0xb0, 0x15,
+0x0c, 0x2a, 0x64, 0x67, 0x14, 0x1c, 0x82, 0x6c, 0x09, 0xef, 0x77, 0x15, 0xa4, 0x0a, 0x66, 0x67,
+0x15, 0x04, 0xd7, 0x82, 0x10, 0x93, 0x0c, 0x0a, 0x0f, 0x04, 0x35, 0xd0, 0x9b, 0x7b, 0x07, 0x02,
+0x13, 0xc9, 0x28, 0xac, 0x67, 0x74, 0xa8, 0x08, 0xd3, 0x2a, 0x98, 0x18, 0x87, 0xca, 0x67, 0x0d,
+0x0a, 0xb7, 0xff, 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x04, 0x69, 0x6c, 0xff,
+0xf6, 0x65, 0x20, 0x68, 0x61, 0x0d, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x63, 0x72, 0xb7, 0xfd, 0x65,
+0x61, 0x74, 0x65, 0x64, 0x2f, 0x02, 0x69, 0x06, 0xfb, 0x37, 0x13, 0x79, 0x20, 0x43, 0x50, 0x49,
+0x61, 0x64, 0x09, 0xdf, 0x7e, 0x56, 0x31, 0x2e, 0x32, 0x30, 0x37, 0x43, 0x6f, 0x70, 0x79, 0xff,
+0xf2, 0x72, 0x69, 0x67, 0x68, 0x28, 0x43, 0x29, 0x20, 0x31, 0x39, 0x39, 0x7f, 0xdd, 0x33, 0x2d,
+0x04, 0x36, 0x28, 0x6b, 0x6f, 0x73, 0xfd, 0xb7, 0x74, 0x54, 0x40, 0x61, 0x63, 0x6d, 0x2e, 0x6f,
+0x72, 0x67, 0x76, 0xb7, 0x1f, 0x4b, 0x0f, 0x61, 0x20, 0x4b, 0x15, 0x0f, 0x36, 0x29, 0x71, 0x70,
+0x72, 0x6f, 0x77, 0xff, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x6d, 0x61, 0x79, 0x6f, 0x20, 0x75, 0x73,
+0xad, 0x75, 0x63, 0x66, 0x72, 0x09, 0xd6, 0xda, 0x6f, 0x66, 0x7a, 0x84, 0x36, 0xbf, 0xb5, 0x09,
+0x7e, 0x20, 0x79, 0x6f, 0x75, 0x72, 0xad, 0x6d, 0x11, 0x77, 0x8d, 0x69, 0x00, 0xf8, 0x73, 0x6b,
+0x2e, 0x0d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xff, 0xa4, 0xe8, 0x3a, 0x00, 0x72, 0xfa,
+0x41, 0xe8, 0x2f, 0x00, 0xe3, 0x3b, 0x73, 0xf9, 0x83, 0xe9, 0x03, 0x72, 0x06, 0x88, 0xcc, 0xac,
+0xf7, 0xd0, 0x95, 0x31, 0xc9, 0xe8, 0x1b, 0x00, 0x11, 0xc9, 0x75, 0x08, 0x41, 0xe8, 0x13, 0x00,
+0x73, 0xfb, 0x41, 0x41, 0x81, 0xfd, 0x00, 0xf3, 0x83, 0xd1, 0x01, 0x8d, 0x03, 0x96, 0xf3, 0xa4,
+0x96, 0xeb, 0xc8, 0xe8, 0x02, 0x00, 0x11, 0xc9, 0x01, 0xdb, 0x75, 0x04, 0xad, 0x11, 0xc0, 0x93,
+0xc3, 0x5e, 0xb9, 0x01, 0x00, 0xac, 0x2c, 0xe8, 0x3c, 0x01, 0x77, 0xf9, 0x8b, 0x1c, 0x86, 0xdf,
+0x29, 0xf3, 0x89, 0x1c, 0xad, 0xe2, 0xee, 0xc3 };
diff --git a/src/dos/dos_devices.cpp b/src/dos/dos_devices.cpp
new file mode 100644
index 000000000..9beb43051
--- /dev/null
+++ b/src/dos/dos_devices.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include "dosbox.h"
+#include "callback.h"
+#include "regs.h"
+#include "mem.h"
+#include "bios.h"
+#include "dos_inc.h"
+#include "support.h"
+#include "drives.h" //Wildcmp
+/* Include all the devices */
+
+#include "dev_con.h"
+
+
+DOS_Device * Devices[DOS_DEVICES];
+
+class device_NUL : public DOS_Device {
+public:
+ device_NUL() { SetName("NUL"); };
+ virtual bool Read(Bit8u * data,Bit16u * size) {
+ *size = 0; //Return success and no data read.
+ LOG(LOG_IOCTL,LOG_NORMAL)("%s:READ",GetName());
+ return true;
+ }
+ virtual bool Write(Bit8u * data,Bit16u * size) {
+ LOG(LOG_IOCTL,LOG_NORMAL)("%s:WRITE",GetName());
+ return true;
+ }
+ virtual bool Seek(Bit32u * pos,Bit32u type) {
+ LOG(LOG_IOCTL,LOG_NORMAL)("%s:SEEK",GetName());
+ return true;
+ }
+ virtual bool Close() { return true; }
+ virtual Bit16u GetInformation(void) { return 0x8084; }
+ virtual bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode){return false;}
+ virtual bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode){return false;}
+};
+
+class device_LPT1 : public device_NUL {
+public:
+ device_LPT1() { SetName("LPT1");}
+ Bit16u GetInformation(void) { return 0x80A0; }
+ bool Read(Bit8u* data,Bit16u * size){
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+};
+
+bool DOS_Device::Read(Bit8u * data,Bit16u * size) {
+ return Devices[devnum]->Read(data,size);
+}
+
+bool DOS_Device::Write(Bit8u * data,Bit16u * size) {
+ return Devices[devnum]->Write(data,size);
+}
+
+bool DOS_Device::Seek(Bit32u * pos,Bit32u type) {
+ return Devices[devnum]->Seek(pos,type);
+}
+
+bool DOS_Device::Close() {
+ return Devices[devnum]->Close();
+}
+
+Bit16u DOS_Device::GetInformation(void) {
+ return Devices[devnum]->GetInformation();
+}
+
+bool DOS_Device::ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) {
+ return Devices[devnum]->ReadFromControlChannel(bufptr,size,retcode);
+}
+
+bool DOS_Device::WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) {
+ return Devices[devnum]->WriteToControlChannel(bufptr,size,retcode);
+}
+
+DOS_File::DOS_File(const DOS_File& orig) {
+ flags=orig.flags;
+ time=orig.time;
+ date=orig.date;
+ attr=orig.attr;
+ refCtr=orig.refCtr;
+ open=orig.open;
+ hdrive=orig.hdrive;
+ name=0;
+ if(orig.name) {
+ name=new char [strlen(orig.name) + 1];strcpy(name,orig.name);
+ }
+}
+
+DOS_File & DOS_File::operator= (const DOS_File & orig) {
+ flags=orig.flags;
+ time=orig.time;
+ date=orig.date;
+ attr=orig.attr;
+ refCtr=orig.refCtr;
+ open=orig.open;
+ hdrive=orig.hdrive;
+ if(name) {
+ delete [] name; name=0;
+ }
+ if(orig.name) {
+ name=new char [strlen(orig.name) + 1];strcpy(name,orig.name);
+ }
+ return *this;
+}
+
+Bit8u DOS_FindDevice(char const * name) {
+ /* should only check for the names before the dot and spacepadded */
+ char fullname[DOS_PATHLENGTH];Bit8u drive;
+// if(!name || !(*name)) return DOS_DEVICES; //important, but makename does it
+ if (!DOS_MakeName(name,fullname,&drive)) return DOS_DEVICES;
+
+ char* name_part = strrchr(fullname,'\\');
+ if(name_part) {
+ *name_part++ = 0;
+ //Check validity of leading directory.
+ if(!Drives[drive]->TestDir(fullname)) return DOS_DEVICES;
+ } else name_part = fullname;
+
+ char* dot = strrchr(name_part,'.');
+ if(dot) *dot = 0; //no ext checking
+
+ static char com[5] = { 'C','O','M','1',0 };
+ static char lpt[5] = { 'L','P','T','1',0 };
+ // AUX is alias for COM1 and PRN for LPT1
+ // A bit of a hack. (but less then before).
+ // no need for casecmp as makename returns uppercase
+ if (strcmp(name_part, "AUX") == 0) name_part = com;
+ if (strcmp(name_part, "PRN") == 0) name_part = lpt;
+
+ /* loop through devices */
+ for(Bit8u index = 0;index < DOS_DEVICES;index++) {
+ if (Devices[index]) {
+ if (WildFileCmp(name_part,Devices[index]->name)) return index;
+ }
+ }
+ return DOS_DEVICES;
+}
+
+
+void DOS_AddDevice(DOS_Device * adddev) {
+//Caller creates the device. We store a pointer to it
+//TODO Give the Device a real handler in low memory that responds to calls
+ for(Bitu i = 0; i < DOS_DEVICES;i++) {
+ if(!Devices[i]){
+ Devices[i] = adddev;
+ Devices[i]->SetDeviceNumber(i);
+ return;
+ }
+ }
+ E_Exit("DOS:Too many devices added");
+}
+
+void DOS_DelDevice(DOS_Device * dev) {
+// We will destroy the device if we find it in our list.
+// TODO:The file table is not checked to see the device is opened somewhere!
+ for (Bitu i = 0; i <DOS_DEVICES;i++) {
+ if(Devices[i] && !strcasecmp(Devices[i]->name,dev->name)){
+ delete Devices[i];
+ Devices[i] = 0;
+ return;
+ }
+ }
+}
+
+void DOS_SetupDevices(void) {
+ DOS_Device * newdev;
+ newdev=new device_CON();
+ DOS_AddDevice(newdev);
+ DOS_Device * newdev2;
+ newdev2=new device_NUL();
+ DOS_AddDevice(newdev2);
+ DOS_Device * newdev3;
+ newdev3=new device_LPT1();
+ DOS_AddDevice(newdev3);
+}
diff --git a/src/dos/dos_execute.cpp b/src/dos/dos_execute.cpp
new file mode 100644
index 000000000..bf180268c
--- /dev/null
+++ b/src/dos/dos_execute.cpp
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <ctype.h>
+#include "dosbox.h"
+#include "mem.h"
+#include "dos_inc.h"
+#include "regs.h"
+#include "callback.h"
+#include "debug.h"
+#include "cpu.h"
+
+const char * RunningProgram="DOSBOX";
+
+#ifdef _MSC_VER
+#pragma pack(1)
+#endif
+struct EXE_Header {
+ Bit16u signature; /* EXE Signature MZ or ZM */
+ Bit16u extrabytes; /* Bytes on the last page */
+ Bit16u pages; /* Pages in file */
+ Bit16u relocations; /* Relocations in file */
+ Bit16u headersize; /* Paragraphs in header */
+ Bit16u minmemory; /* Minimum amount of memory */
+ Bit16u maxmemory; /* Maximum amount of memory */
+ Bit16u initSS;
+ Bit16u initSP;
+ Bit16u checksum;
+ Bit16u initIP;
+ Bit16u initCS;
+ Bit16u reloctable;
+ Bit16u overlay;
+} GCC_ATTRIBUTE(packed);
+#ifdef _MSC_VER
+#pragma pack()
+#endif
+
+#define MAGIC1 0x5a4d
+#define MAGIC2 0x4d5a
+#define MAXENV 32768u
+#define ENV_KEEPFREE 83 /* keep unallocated by environment variables */
+#define LOADNGO 0
+#define LOAD 1
+#define OVERLAY 3
+
+
+
+static void SaveRegisters(void) {
+ reg_sp-=18;
+ mem_writew(SegPhys(ss)+reg_sp+ 0,reg_ax);
+ mem_writew(SegPhys(ss)+reg_sp+ 2,reg_cx);
+ mem_writew(SegPhys(ss)+reg_sp+ 4,reg_dx);
+ mem_writew(SegPhys(ss)+reg_sp+ 6,reg_bx);
+ mem_writew(SegPhys(ss)+reg_sp+ 8,reg_si);
+ mem_writew(SegPhys(ss)+reg_sp+10,reg_di);
+ mem_writew(SegPhys(ss)+reg_sp+12,reg_bp);
+ mem_writew(SegPhys(ss)+reg_sp+14,SegValue(ds));
+ mem_writew(SegPhys(ss)+reg_sp+16,SegValue(es));
+}
+
+static void RestoreRegisters(void) {
+ reg_ax=mem_readw(SegPhys(ss)+reg_sp+ 0);
+ reg_cx=mem_readw(SegPhys(ss)+reg_sp+ 2);
+ reg_dx=mem_readw(SegPhys(ss)+reg_sp+ 4);
+ reg_bx=mem_readw(SegPhys(ss)+reg_sp+ 6);
+ reg_si=mem_readw(SegPhys(ss)+reg_sp+ 8);
+ reg_di=mem_readw(SegPhys(ss)+reg_sp+10);
+ reg_bp=mem_readw(SegPhys(ss)+reg_sp+12);
+ SegSet16(ds,mem_readw(SegPhys(ss)+reg_sp+14));
+ SegSet16(es,mem_readw(SegPhys(ss)+reg_sp+16));
+ reg_sp+=18;
+}
+
+extern void GFX_SetTitle(Bit32s cycles,Bits frameskip,bool paused);
+void DOS_UpdatePSPName(void) {
+ DOS_MCB mcb(dos.psp()-1);
+ static char name[9];
+ mcb.GetFileName(name);
+ name[8] = 0;
+ if (!strlen(name)) strcpy(name,"DOSBOX");
+ for(Bitu i = 0;i < 8;i++) { //Don't put garbage in the title bar. Mac OS X doesn't like it
+ if (name[i] == 0) break;
+ if ( !isprint(*reinterpret_cast<unsigned char*>(&name[i])) ) name[i] = '?';
+ }
+ RunningProgram = name;
+ GFX_SetTitle(-1,-1,false);
+}
+
+void DOS_Terminate(Bit16u pspseg,bool tsr,Bit8u exitcode) {
+
+ dos.return_code=exitcode;
+ dos.return_mode=(tsr)?(Bit8u)RETURN_TSR:(Bit8u)RETURN_EXIT;
+
+ DOS_PSP curpsp(pspseg);
+ if (pspseg==curpsp.GetParent()) return;
+ /* Free Files owned by process */
+ if (!tsr) curpsp.CloseFiles();
+
+ /* Get the termination address */
+ RealPt old22 = curpsp.GetInt22();
+ /* Restore vector 22,23,24 */
+ curpsp.RestoreVectors();
+ /* Set the parent PSP */
+ dos.psp(curpsp.GetParent());
+ DOS_PSP parentpsp(curpsp.GetParent());
+
+ /* Restore the SS:SP to the previous one */
+ SegSet16(ss,RealSeg(parentpsp.GetStack()));
+ reg_sp = RealOff(parentpsp.GetStack());
+ /* Restore the old CS:IP from int 22h */
+ RestoreRegisters();
+ /* Set the CS:IP stored in int 0x22 back on the stack */
+ mem_writew(SegPhys(ss)+reg_sp+0,RealOff(old22));
+ mem_writew(SegPhys(ss)+reg_sp+2,RealSeg(old22));
+ /* set IOPL=3 (Strike Commander), nested task set,
+ interrupts enabled, test flags cleared */
+ mem_writew(SegPhys(ss)+reg_sp+4,0x7202);
+ // Free memory owned by process
+ if (!tsr) DOS_FreeProcessMemory(pspseg);
+ DOS_UpdatePSPName();
+
+ if ((!(CPU_AutoDetermineMode>>CPU_AUTODETERMINE_SHIFT)) || (cpu.pmode)) return;
+
+ CPU_AutoDetermineMode>>=CPU_AUTODETERMINE_SHIFT;
+ if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CYCLES) {
+ CPU_CycleAutoAdjust=false;
+ CPU_CycleLeft=0;
+ CPU_Cycles=0;
+ CPU_CycleMax=CPU_OldCycleMax;
+ GFX_SetTitle(CPU_OldCycleMax,-1,false);
+ } else {
+ GFX_SetTitle(-1,-1,false);
+ }
+#if (C_DYNAMIC_X86) || (C_DYNREC)
+ if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CORE) {
+ cpudecoder=&CPU_Core_Normal_Run;
+ CPU_CycleLeft=0;
+ CPU_Cycles=0;
+ }
+#endif
+
+ return;
+}
+
+static bool MakeEnv(char * name,Bit16u * segment) {
+ /* If segment to copy environment is 0 copy the caller's environment */
+ DOS_PSP psp(dos.psp());
+ PhysPt envread,envwrite;
+ Bit16u envsize=1;
+ bool parentenv=true;
+
+ if (*segment==0) {
+ if (!psp.GetEnvironment()) parentenv=false; //environment seg=0
+ envread=PhysMake(psp.GetEnvironment(),0);
+ } else {
+ if (!*segment) parentenv=false; //environment seg=0
+ envread=PhysMake(*segment,0);
+ }
+
+ if (parentenv) {
+ for (envsize=0; ;envsize++) {
+ if (envsize>=MAXENV - ENV_KEEPFREE) {
+ DOS_SetError(DOSERR_ENVIRONMENT_INVALID);
+ return false;
+ }
+ if (mem_readw(envread+envsize)==0) break;
+ }
+ envsize += 2; /* account for trailing \0\0 */
+ }
+ Bit16u size=long2para(envsize+ENV_KEEPFREE);
+ if (!DOS_AllocateMemory(segment,&size)) return false;
+ envwrite=PhysMake(*segment,0);
+ if (parentenv) {
+ MEM_BlockCopy(envwrite,envread,envsize);
+// mem_memcpy(envwrite,envread,envsize);
+ envwrite+=envsize;
+ } else {
+ mem_writeb(envwrite++,0);
+ }
+ mem_writew(envwrite,1);
+ envwrite+=2;
+ char namebuf[DOS_PATHLENGTH];
+ if (DOS_Canonicalize(name,namebuf)) {
+ MEM_BlockWrite(envwrite,namebuf,(Bitu)(strlen(namebuf)+1));
+ return true;
+ } else return false;
+}
+
+bool DOS_NewPSP(Bit16u segment, Bit16u size) {
+ DOS_PSP psp(segment);
+ psp.MakeNew(size);
+ Bit16u parent_psp_seg=psp.GetParent();
+ DOS_PSP psp_parent(parent_psp_seg);
+ psp.CopyFileTable(&psp_parent,false);
+ // copy command line as well (Kings Quest AGI -cga switch)
+ psp.SetCommandTail(RealMake(parent_psp_seg,0x80));
+ return true;
+}
+
+bool DOS_ChildPSP(Bit16u segment, Bit16u size) {
+ DOS_PSP psp(segment);
+ psp.MakeNew(size);
+ Bit16u parent_psp_seg = psp.GetParent();
+ DOS_PSP psp_parent(parent_psp_seg);
+ psp.CopyFileTable(&psp_parent,true);
+ psp.SetCommandTail(RealMake(parent_psp_seg,0x80));
+ psp.SetFCB1(RealMake(parent_psp_seg,0x5c));
+ psp.SetFCB2(RealMake(parent_psp_seg,0x6c));
+ psp.SetEnvironment(psp_parent.GetEnvironment());
+ psp.SetSize(size);
+ // push registers in case child PSP is terminated
+ SaveRegisters();
+ psp.SetStack(RealMakeSeg(ss,reg_sp));
+ reg_sp+=18;
+ return true;
+}
+
+static void SetupPSP(Bit16u pspseg,Bit16u memsize,Bit16u envseg) {
+ /* Fix the PSP for psp and environment MCB's */
+ DOS_MCB mcb((Bit16u)(pspseg-1));
+ mcb.SetPSPSeg(pspseg);
+ mcb.SetPt((Bit16u)(envseg-1));
+ mcb.SetPSPSeg(pspseg);
+
+ DOS_PSP psp(pspseg);
+ psp.MakeNew(memsize);
+ psp.SetEnvironment(envseg);
+
+ /* Copy file handles */
+ DOS_PSP oldpsp(dos.psp());
+ psp.CopyFileTable(&oldpsp,true);
+
+}
+
+static void SetupCMDLine(Bit16u pspseg,DOS_ParamBlock & block) {
+ DOS_PSP psp(pspseg);
+ // if cmdtail==0 it will inited as empty in SetCommandTail
+ psp.SetCommandTail(block.exec.cmdtail);
+}
+
+bool DOS_Execute(char * name,PhysPt block_pt,Bit8u flags) {
+ EXE_Header head;Bitu i;
+ Bit16u fhandle;Bit16u len;Bit32u pos;
+ Bit16u pspseg,envseg,loadseg,memsize,readsize;
+ PhysPt loadaddress;RealPt relocpt;
+ Bitu headersize=0,imagesize=0;
+ DOS_ParamBlock block(block_pt);
+
+ block.LoadData();
+ //Remove the loadhigh flag for the moment!
+ if(flags&0x80) LOG(LOG_EXEC,LOG_ERROR)("using loadhigh flag!!!!!. dropping it");
+ flags &= 0x7f;
+ if (flags!=LOADNGO && flags!=OVERLAY && flags!=LOAD) {
+ DOS_SetError(DOSERR_FORMAT_INVALID);
+ return false;
+// E_Exit("DOS:Not supported execute mode %d for file %s",flags,name);
+ }
+ /* Check for EXE or COM File */
+ bool iscom=false;
+ if (!DOS_OpenFile(name,OPEN_READ,&fhandle)) {
+ DOS_SetError(DOSERR_FILE_NOT_FOUND);
+ return false;
+ }
+ len=sizeof(EXE_Header);
+ if (!DOS_ReadFile(fhandle,(Bit8u *)&head,&len)) {
+ DOS_CloseFile(fhandle);
+ return false;
+ }
+ if (len<sizeof(EXE_Header)) {
+ if (len==0) {
+ /* Prevent executing zero byte files */
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ DOS_CloseFile(fhandle);
+ return false;
+ }
+ /* Otherwise must be a .com file */
+ iscom=true;
+ } else {
+ /* Convert the header to correct endian, i hope this works */
+ HostPt endian=(HostPt)&head;
+ for (i=0;i<sizeof(EXE_Header)/2;i++) {
+ *((Bit16u *)endian)=host_readw(endian);
+ endian+=2;
+ }
+ if ((head.signature!=MAGIC1) && (head.signature!=MAGIC2)) iscom=true;
+ else {
+ if(head.pages & ~0x07ff) /* 1 MB dos maximum address limit. Fixes TC3 IDE (kippesoep) */
+ LOG(LOG_EXEC,LOG_NORMAL)("Weird header: head.pages > 1 MB");
+ head.pages&=0x07ff;
+ headersize = head.headersize*16;
+ imagesize = head.pages*512-headersize;
+ if (imagesize+headersize<512) imagesize = 512-headersize;
+ }
+ }
+ Bit8u * loadbuf=(Bit8u *)new Bit8u[0x10000];
+ if (flags!=OVERLAY) {
+ /* Create an environment block */
+ envseg=block.exec.envseg;
+ if (!MakeEnv(name,&envseg)) {
+ DOS_CloseFile(fhandle);
+ return false;
+ }
+ /* Get Memory */
+ Bit16u minsize,maxsize;Bit16u maxfree=0xffff;DOS_AllocateMemory(&pspseg,&maxfree);
+ if (iscom) {
+ minsize=0x1000;maxsize=0xffff;
+ if (machine==MCH_PCJR) {
+ /* try to load file into memory below 96k */
+ pos=0;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET);
+ Bit16u dataread=0x1800;
+ DOS_ReadFile(fhandle,loadbuf,&dataread);
+ if (dataread<0x1800) maxsize=dataread;
+ if (minsize>maxsize) minsize=maxsize;
+ }
+ } else { /* Exe size calculated from header */
+ minsize=long2para(imagesize+(head.minmemory<<4)+256);
+ if (head.maxmemory!=0) maxsize=long2para(imagesize+(head.maxmemory<<4)+256);
+ else maxsize=0xffff;
+ }
+ if (maxfree<minsize) {
+ if (iscom) {
+ /* Reduce minimum of needed memory size to filesize */
+ pos=0;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET);
+ Bit16u dataread=0xf800;
+ DOS_ReadFile(fhandle,loadbuf,&dataread);
+ if (dataread<0xf800) minsize=((dataread+0x10)>>4)+0x20;
+ }
+ if (maxfree<minsize) {
+ DOS_CloseFile(fhandle);
+ DOS_SetError(DOSERR_INSUFFICIENT_MEMORY);
+ DOS_FreeMemory(envseg);
+ return false;
+ }
+ }
+ if (maxfree<maxsize) memsize=maxfree;
+ else memsize=maxsize;
+ if (!DOS_AllocateMemory(&pspseg,&memsize)) E_Exit("DOS:Exec error in memory");
+ if (iscom && (machine==MCH_PCJR) && (pspseg<0x2000)) {
+ maxsize=0xffff;
+ /* resize to full extent of memory block */
+ DOS_ResizeMemory(pspseg,&maxsize);
+ }
+ loadseg=pspseg+16;
+ if (!iscom) {
+ /* Check if requested to load program into upper part of allocated memory */
+ if ((head.minmemory == 0) && (head.maxmemory == 0))
+ loadseg = (Bit16u)(((pspseg+memsize)*0x10-imagesize)/0x10);
+ }
+ } else loadseg=block.overlay.loadseg;
+ /* Load the executable */
+ loadaddress=PhysMake(loadseg,0);
+
+ if (iscom) { /* COM Load 64k - 256 bytes max */
+ pos=0;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET);
+ readsize=0xffff-256;
+ DOS_ReadFile(fhandle,loadbuf,&readsize);
+ MEM_BlockWrite(loadaddress,loadbuf,readsize);
+ } else { /* EXE Load in 32kb blocks and then relocate */
+ pos=headersize;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET);
+ while (imagesize>0x7FFF) {
+ readsize=0x8000;DOS_ReadFile(fhandle,loadbuf,&readsize);
+ MEM_BlockWrite(loadaddress,loadbuf,readsize);
+// if (readsize!=0x8000) LOG(LOG_EXEC,LOG_NORMAL)("Illegal header");
+ loadaddress+=0x8000;imagesize-=0x8000;
+ }
+ if (imagesize>0) {
+ readsize=(Bit16u)imagesize;DOS_ReadFile(fhandle,loadbuf,&readsize);
+ MEM_BlockWrite(loadaddress,loadbuf,readsize);
+// if (readsize!=imagesize) LOG(LOG_EXEC,LOG_NORMAL)("Illegal header");
+ }
+ /* Relocate the exe image */
+ Bit16u relocate;
+ if (flags==OVERLAY) relocate=block.overlay.relocation;
+ else relocate=loadseg;
+ pos=head.reloctable;DOS_SeekFile(fhandle,&pos,0);
+ for (i=0;i<head.relocations;i++) {
+ readsize=4;DOS_ReadFile(fhandle,(Bit8u *)&relocpt,&readsize);
+ relocpt=host_readd((HostPt)&relocpt); //Endianize
+ PhysPt address=PhysMake(RealSeg(relocpt)+loadseg,RealOff(relocpt));
+ mem_writew(address,mem_readw(address)+relocate);
+ }
+ }
+ delete[] loadbuf;
+ DOS_CloseFile(fhandle);
+
+ /* Setup a psp */
+ if (flags!=OVERLAY) {
+ // Create psp after closing exe, to avoid dead file handle of exe in copied psp
+ SetupPSP(pspseg,memsize,envseg);
+ SetupCMDLine(pspseg,block);
+ };
+ CALLBACK_SCF(false); /* Carry flag cleared for caller if successfull */
+ if (flags==OVERLAY) return true; /* Everything done for overlays */
+ RealPt csip,sssp;
+ if (iscom) {
+ csip=RealMake(pspseg,0x100);
+ if (memsize<0x1000) {
+ LOG(LOG_EXEC,LOG_WARN)("COM format with only %X paragraphs available",memsize);
+ sssp=RealMake(pspseg,(memsize<<4)-2);
+ } else sssp=RealMake(pspseg,0xfffe);
+ mem_writew(Real2Phys(sssp),0);
+ } else {
+ csip=RealMake(loadseg+head.initCS,head.initIP);
+ sssp=RealMake(loadseg+head.initSS,head.initSP);
+ if (head.initSP<4) LOG(LOG_EXEC,LOG_ERROR)("stack underflow/wrap at EXEC");
+ }
+
+ if ((flags==LOAD) || (flags==LOADNGO)) {
+ /* Get Caller's program CS:IP of the stack and set termination address to that */
+ RealSetVec(0x22,RealMake(mem_readw(SegPhys(ss)+reg_sp+2),mem_readw(SegPhys(ss)+reg_sp)));
+ SaveRegisters();
+ DOS_PSP callpsp(dos.psp());
+ /* Save the SS:SP on the PSP of calling program */
+ callpsp.SetStack(RealMakeSeg(ss,reg_sp));
+ /* Switch the psp's and set new DTA */
+ dos.psp(pspseg);
+ DOS_PSP newpsp(dos.psp());
+ dos.dta(RealMake(newpsp.GetSegment(),0x80));
+ /* save vectors */
+ newpsp.SaveVectors();
+ /* copy fcbs */
+ newpsp.SetFCB1(block.exec.fcb1);
+ newpsp.SetFCB2(block.exec.fcb2);
+ /* Save the SS:SP on the PSP of new program */
+ newpsp.SetStack(RealMakeSeg(ss,reg_sp));
+
+ /* Setup bx, contains a 0xff in bl and bh if the drive in the fcb is not valid */
+ DOS_FCB fcb1(RealSeg(block.exec.fcb1),RealOff(block.exec.fcb1));
+ DOS_FCB fcb2(RealSeg(block.exec.fcb2),RealOff(block.exec.fcb2));
+ Bit8u d1 = fcb1.GetDrive(); //depends on 0 giving the dos.default drive
+ if ( (d1>=DOS_DRIVES) || !Drives[d1] ) reg_bl = 0xFF; else reg_bl = 0;
+ Bit8u d2 = fcb2.GetDrive();
+ if ( (d2>=DOS_DRIVES) || !Drives[d2] ) reg_bh = 0xFF; else reg_bh = 0;
+
+ /* Write filename in new program MCB */
+ char stripname[8]= { 0 };Bitu index=0;
+ while (char chr=*name++) {
+ switch (chr) {
+ case ':':case '\\':case '/':index=0;break;
+ default:if (index<8) stripname[index++]=(char)toupper(chr);
+ }
+ }
+ index=0;
+ while (index<8) {
+ if (stripname[index]=='.') break;
+ if (!stripname[index]) break;
+ index++;
+ }
+ memset(&stripname[index],0,8-index);
+ DOS_MCB pspmcb(dos.psp()-1);
+ pspmcb.SetFileName(stripname);
+ DOS_UpdatePSPName();
+ }
+
+ if (flags==LOAD) {
+ /* First word on the stack is the value ax should contain on startup */
+ real_writew(RealSeg(sssp-2),RealOff(sssp-2),reg_bx);
+ /* Write initial CS:IP and SS:SP in param block */
+ block.exec.initsssp = sssp-2;
+ block.exec.initcsip = csip;
+ block.SaveData();
+ /* Changed registers */
+ reg_sp+=18;
+ reg_ax=RealOff(csip);
+ reg_bx=memsize;
+ reg_dx=0;
+ return true;
+ }
+
+ if (flags==LOADNGO) {
+ if ((reg_sp>0xfffe) || (reg_sp<18)) LOG(LOG_EXEC,LOG_ERROR)("stack underflow/wrap at EXEC");
+ /* Set the stack for new program */
+ SegSet16(ss,RealSeg(sssp));reg_sp=RealOff(sssp);
+ /* Add some flags and CS:IP on the stack for the IRET */
+ CPU_Push16(RealSeg(csip));
+ CPU_Push16(RealOff(csip));
+ /* DOS starts programs with a RETF, so critical flags
+ * should not be modified (IOPL in v86 mode);
+ * interrupt flag is set explicitly, test flags cleared */
+ reg_flags=(reg_flags&(~FMASK_TEST))|FLAG_IF;
+ //Jump to retf so that we only need to store cs:ip on the stack
+ reg_ip++;
+ /* Setup the rest of the registers */
+ reg_ax=reg_bx;
+ reg_cx=0xff;
+ reg_dx=pspseg;
+ reg_si=RealOff(csip);
+ reg_di=RealOff(sssp);
+ reg_bp=0x91c; /* DOS internal stack begin relict */
+ SegSet16(ds,pspseg);SegSet16(es,pspseg);
+#if C_DEBUG
+ /* Started from debug.com, then set breakpoint at start */
+ DEBUG_CheckExecuteBreakpoint(RealSeg(csip),RealOff(csip));
+#endif
+ return true;
+ }
+ return false;
+}
diff --git a/src/dos/dos_files.cpp b/src/dos/dos_files.cpp
new file mode 100644
index 000000000..8d88361da
--- /dev/null
+++ b/src/dos/dos_files.cpp
@@ -0,0 +1,1334 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "dosbox.h"
+#include "bios.h"
+#include "mem.h"
+#include "regs.h"
+#include "dos_inc.h"
+#include "drives.h"
+#include "cross.h"
+
+#define DOS_FILESTART 4
+
+#define FCB_SUCCESS 0
+#define FCB_READ_NODATA 1
+#define FCB_READ_PARTIAL 3
+#define FCB_ERR_NODATA 1
+#define FCB_ERR_EOF 3
+#define FCB_ERR_WRITE 1
+
+
+DOS_File * Files[DOS_FILES];
+DOS_Drive * Drives[DOS_DRIVES];
+
+Bit8u DOS_GetDefaultDrive(void) {
+// return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetDrive();
+ Bit8u d = DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetDrive();
+ if( d != dos.current_drive ) LOG(LOG_DOSMISC,LOG_ERROR)("SDA drive %d not the same as dos.current_drive %d",d,dos.current_drive);
+ return dos.current_drive;
+}
+
+void DOS_SetDefaultDrive(Bit8u drive) {
+// if (drive<=DOS_DRIVES && ((drive<2) || Drives[drive])) DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDrive(drive);
+ if (drive<DOS_DRIVES && ((drive<2) || Drives[drive])) {dos.current_drive = drive; DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDrive(drive);}
+}
+
+bool DOS_MakeName(char const * const name,char * const fullname,Bit8u * drive) {
+ if(!name || *name == 0 || *name == ' ') {
+ /* Both \0 and space are seperators and
+ * empty filenames report file not found */
+ DOS_SetError(DOSERR_FILE_NOT_FOUND);
+ return false;
+ }
+ const char * name_int = name;
+ char tempdir[DOS_PATHLENGTH];
+ char upname[DOS_PATHLENGTH];
+ Bitu r,w;
+ Bit8u c;
+ *drive = DOS_GetDefaultDrive();
+ /* First get the drive */
+ if (name_int[1]==':') {
+ *drive=(name_int[0] | 0x20)-'a';
+ name_int+=2;
+ }
+ if (*drive>=DOS_DRIVES || !Drives[*drive]) {
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ return false;
+ }
+ r=0;w=0;
+ while (name_int[r]!=0 && (r<DOS_PATHLENGTH)) {
+ c=name_int[r++];
+ if ((c>='a') && (c<='z')) c-=32;
+ else if (c==' ') continue; /* should be separator */
+ else if (c=='/') c='\\';
+ upname[w++]=c;
+ }
+ while (r>0 && name_int[r-1]==' ') r--;
+ if (r>=DOS_PATHLENGTH) { DOS_SetError(DOSERR_PATH_NOT_FOUND);return false; }
+ upname[w]=0;
+ /* Now parse the new file name to make the final filename */
+ if (upname[0]!='\\') strcpy(fullname,Drives[*drive]->curdir);
+ else fullname[0]=0;
+ Bit32u lastdir=0;Bit32u t=0;
+ while (fullname[t]!=0) {
+ if ((fullname[t]=='\\') && (fullname[t+1]!=0)) lastdir=t;
+ t++;
+ };
+ r=0;w=0;
+ tempdir[0]=0;
+ bool stop=false;
+ while (!stop) {
+ if (upname[r]==0) stop=true;
+ if ((upname[r]=='\\') || (upname[r]==0)){
+ tempdir[w]=0;
+ if (tempdir[0]==0) { w=0;r++;continue;}
+ if (strcmp(tempdir,".")==0) {
+ tempdir[0]=0;
+ w=0;r++;
+ continue;
+ }
+
+ Bit32s iDown;
+ bool dots = true;
+ Bit32s templen=(Bit32s)strlen(tempdir);
+ for(iDown=0;(iDown < templen) && dots;iDown++)
+ if(tempdir[iDown] != '.')
+ dots = false;
+
+ // only dots?
+ if (dots && (templen > 1)) {
+ Bit32s cDots = templen - 1;
+ for(iDown=(Bit32s)strlen(fullname)-1;iDown>=0;iDown--) {
+ if(fullname[iDown]=='\\' || iDown==0) {
+ lastdir = iDown;
+ cDots--;
+ if(cDots==0)
+ break;
+ }
+ }
+ fullname[lastdir]=0;
+ t=0;lastdir=0;
+ while (fullname[t]!=0) {
+ if ((fullname[t]=='\\') && (fullname[t+1]!=0)) lastdir=t;
+ t++;
+ }
+ tempdir[0]=0;
+ w=0;r++;
+ continue;
+ }
+
+
+ lastdir=(Bit32u)strlen(fullname);
+
+ if (lastdir!=0) strcat(fullname,"\\");
+ char * ext=strchr(tempdir,'.');
+ if (ext) {
+ if(strchr(ext+1,'.')) {
+ //another dot in the extension =>file not found
+ //Or path not found depending on wether
+ //we are still in dir check stage or file stage
+ if(stop)
+ DOS_SetError(DOSERR_FILE_NOT_FOUND);
+ else
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ return false;
+ }
+
+ ext[4] = 0;
+ if((strlen(tempdir) - strlen(ext)) > 8) memmove(tempdir + 8, ext, 5);
+ } else tempdir[8]=0;
+
+ for (Bitu i=0;i<strlen(tempdir);i++) {
+ c=tempdir[i];
+ if ((c>='A') && (c<='Z')) continue;
+ if ((c>='0') && (c<='9')) continue;
+ switch (c) {
+ case '$': case '#': case '@': case '(': case ')':
+ case '!': case '%': case '{': case '}': case '`': case '~':
+ case '_': case '-': case '.': case '*': case '?': case '&':
+ case '\'': case '+': case '^': case 246: case 255: case 0xa0:
+ case 0xe5: case 0xbd: case 0x9d:
+ break;
+ default:
+ LOG(LOG_FILES,LOG_NORMAL)("Makename encountered an illegal char %c hex:%X in %s!",c,c,name);
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);return false;
+ break;
+ }
+ }
+
+ if (strlen(fullname)+strlen(tempdir)>=DOS_PATHLENGTH) {
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);return false;
+ }
+
+ strcat(fullname,tempdir);
+ tempdir[0]=0;
+ w=0;r++;
+ continue;
+ }
+ tempdir[w++]=upname[r++];
+ }
+ return true;
+}
+
+bool DOS_GetCurrentDir(Bit8u drive,char * const buffer) {
+ if (drive==0) drive=DOS_GetDefaultDrive();
+ else drive--;
+ if ((drive>=DOS_DRIVES) || (!Drives[drive])) {
+ DOS_SetError(DOSERR_INVALID_DRIVE);
+ return false;
+ }
+ strcpy(buffer,Drives[drive]->curdir);
+ return true;
+}
+
+bool DOS_ChangeDir(char const * const dir) {
+ Bit8u drive;char fulldir[DOS_PATHLENGTH];
+ const char * testdir=dir;
+ if (strlen(testdir) && testdir[1]==':') testdir+=2;
+ size_t len=strlen(testdir);
+ if (!len) {
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ return false;
+ }
+ if (!DOS_MakeName(dir,fulldir,&drive)) return false;
+ if (strlen(fulldir) && testdir[len-1]=='\\') {
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ return false;
+ }
+
+ if (Drives[drive]->TestDir(fulldir)) {
+ strcpy(Drives[drive]->curdir,fulldir);
+ return true;
+ } else {
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ }
+ return false;
+}
+
+bool DOS_MakeDir(char const * const dir) {
+ Bit8u drive;char fulldir[DOS_PATHLENGTH];
+ size_t len = strlen(dir);
+ if(!len || dir[len-1] == '\\') {
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ return false;
+ }
+ if (!DOS_MakeName(dir,fulldir,&drive)) return false;
+ if(Drives[drive]->MakeDir(fulldir)) return true;
+
+ /* Determine reason for failing */
+ if(Drives[drive]->TestDir(fulldir))
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ else
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ return false;
+}
+
+bool DOS_RemoveDir(char const * const dir) {
+/* We need to do the test before the removal as can not rely on
+ * the host to forbid removal of the current directory.
+ * We never change directory. Everything happens in the drives.
+ */
+ Bit8u drive;char fulldir[DOS_PATHLENGTH];
+ if (!DOS_MakeName(dir,fulldir,&drive)) return false;
+ /* Check if exists */
+ if(!Drives[drive]->TestDir(fulldir)) {
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ return false;
+ }
+ /* See if it's current directory */
+ char currdir[DOS_PATHLENGTH]= { 0 };
+ DOS_GetCurrentDir(drive + 1 ,currdir);
+ if(strcmp(currdir,fulldir) == 0) {
+ DOS_SetError(DOSERR_REMOVE_CURRENT_DIRECTORY);
+ return false;
+ }
+
+ if(Drives[drive]->RemoveDir(fulldir)) return true;
+
+ /* Failed. We know it exists and it's not the current dir */
+ /* Assume non empty */
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+}
+
+bool DOS_Rename(char const * const oldname,char const * const newname) {
+ Bit8u driveold;char fullold[DOS_PATHLENGTH];
+ Bit8u drivenew;char fullnew[DOS_PATHLENGTH];
+ if (!DOS_MakeName(oldname,fullold,&driveold)) return false;
+ if (!DOS_MakeName(newname,fullnew,&drivenew)) return false;
+ /* No tricks with devices */
+ if ( (DOS_FindDevice(oldname) != DOS_DEVICES) ||
+ (DOS_FindDevice(newname) != DOS_DEVICES) ) {
+ DOS_SetError(DOSERR_FILE_NOT_FOUND);
+ return false;
+ }
+ /* Must be on the same drive */
+ if(driveold != drivenew) {
+ DOS_SetError(DOSERR_NOT_SAME_DEVICE);
+ return false;
+ }
+ /*Test if target exists => no access */
+ Bit16u attr;
+ if(Drives[drivenew]->GetFileAttr(fullnew,&attr)) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ /* Source must exist, check for path ? */
+ if (!Drives[driveold]->GetFileAttr( fullold, &attr ) ) {
+ DOS_SetError(DOSERR_FILE_NOT_FOUND);
+ return false;
+ }
+
+ if (Drives[drivenew]->Rename(fullold,fullnew)) return true;
+ /* If it still fails, which error should we give ? PATH NOT FOUND or EACCESS */
+ LOG(LOG_FILES,LOG_NORMAL)("Rename fails for %s to %s, no proper errorcode returned.",oldname,newname);
+ DOS_SetError(DOSERR_FILE_NOT_FOUND);
+ return false;
+}
+
+bool DOS_FindFirst(char * search,Bit16u attr,bool fcb_findfirst) {
+ LOG(LOG_FILES,LOG_NORMAL)("file search attributes %X name %s",attr,search);
+ DOS_DTA dta(dos.dta());
+ Bit8u drive;char fullsearch[DOS_PATHLENGTH];
+ char dir[DOS_PATHLENGTH];char pattern[DOS_PATHLENGTH];
+ size_t len = strlen(search);
+ if(len && search[len - 1] == '\\' && !( (len > 2) && (search[len - 2] == ':') && (attr == DOS_ATTR_VOLUME) )) {
+ //Dark Forces installer, but c:\ is allright for volume labels(exclusively set)
+ DOS_SetError(DOSERR_NO_MORE_FILES);
+ return false;
+ }
+ if (!DOS_MakeName(search,fullsearch,&drive)) return false;
+ //Check for devices. FindDevice checks for leading subdir as well
+ bool device = (DOS_FindDevice(search) != DOS_DEVICES);
+
+ /* Split the search in dir and pattern */
+ char * find_last;
+ find_last=strrchr(fullsearch,'\\');
+ if (!find_last) { /*No dir */
+ strcpy(pattern,fullsearch);
+ dir[0]=0;
+ } else {
+ *find_last=0;
+ strcpy(pattern,find_last+1);
+ strcpy(dir,fullsearch);
+ }
+
+ dta.SetupSearch(drive,(Bit8u)attr,pattern);
+
+ if(device) {
+ find_last = strrchr(pattern,'.');
+ if(find_last) *find_last = 0;
+ //TODO use current date and time
+ dta.SetResult(pattern,0,0,0,DOS_ATTR_DEVICE);
+ LOG(LOG_DOSMISC,LOG_WARN)("finding device %s",pattern);
+ return true;
+ }
+
+ if (Drives[drive]->FindFirst(dir,dta,fcb_findfirst)) return true;
+
+ return false;
+}
+
+bool DOS_FindNext(void) {
+ DOS_DTA dta(dos.dta());
+ Bit8u i = dta.GetSearchDrive();
+ if(i >= DOS_DRIVES || !Drives[i]) {
+ /* Corrupt search. */
+ LOG(LOG_FILES,LOG_ERROR)("Corrupt search!!!!");
+ DOS_SetError(DOSERR_NO_MORE_FILES);
+ return false;
+ }
+ if (Drives[i]->FindNext(dta)) return true;
+ return false;
+}
+
+
+bool DOS_ReadFile(Bit16u entry,Bit8u * data,Bit16u * amount,bool fcb) {
+ Bit32u handle = fcb?entry:RealHandle(entry);
+ if (handle>=DOS_FILES) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ if (!Files[handle] || !Files[handle]->IsOpen()) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+/*
+ if ((Files[handle]->flags & 0x0f) == OPEN_WRITE)) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ }
+*/
+ Bit16u toread=*amount;
+ bool ret=Files[handle]->Read(data,&toread);
+ *amount=toread;
+ return ret;
+}
+
+bool DOS_WriteFile(Bit16u entry,Bit8u * data,Bit16u * amount,bool fcb) {
+ Bit32u handle = fcb?entry:RealHandle(entry);
+ if (handle>=DOS_FILES) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ if (!Files[handle] || !Files[handle]->IsOpen()) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+/*
+ if ((Files[handle]->flags & 0x0f) == OPEN_READ)) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ }
+*/
+ Bit16u towrite=*amount;
+ bool ret=Files[handle]->Write(data,&towrite);
+ *amount=towrite;
+ return ret;
+}
+
+bool DOS_SeekFile(Bit16u entry,Bit32u * pos,Bit32u type,bool fcb) {
+ Bit32u handle = fcb?entry:RealHandle(entry);
+ if (handle>=DOS_FILES) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ if (!Files[handle] || !Files[handle]->IsOpen()) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ return Files[handle]->Seek(pos,type);
+}
+
+bool DOS_CloseFile(Bit16u entry, bool fcb) {
+ Bit32u handle = fcb?entry:RealHandle(entry);
+ if (handle>=DOS_FILES) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ if (!Files[handle]) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ if (Files[handle]->IsOpen()) {
+ Files[handle]->Close();
+ }
+
+ DOS_PSP psp(dos.psp());
+ if (!fcb) psp.SetFileHandle(entry,0xff);
+
+ if (Files[handle]->RemoveRef()<=0) {
+ delete Files[handle];
+ Files[handle]=0;
+ }
+ return true;
+}
+
+bool DOS_FlushFile(Bit16u entry) {
+ Bit32u handle=RealHandle(entry);
+ if (handle>=DOS_FILES) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ if (!Files[handle] || !Files[handle]->IsOpen()) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ LOG(LOG_DOSMISC,LOG_NORMAL)("FFlush used.");
+ return true;
+}
+
+static bool PathExists(char const * const name) {
+ const char* leading = strrchr(name,'\\');
+ if(!leading) return true;
+ char temp[CROSS_LEN];
+ strcpy(temp,name);
+ char * lead = strrchr(temp,'\\');
+ if (lead == temp) return true;
+ *lead = 0;
+ Bit8u drive;char fulldir[DOS_PATHLENGTH];
+ if (!DOS_MakeName(temp,fulldir,&drive)) return false;
+ if(!Drives[drive]->TestDir(fulldir)) return false;
+ return true;
+}
+
+
+bool DOS_CreateFile(char const * name,Bit16u attributes,Bit16u * entry,bool fcb) {
+ // Creation of a device is the same as opening it
+ // Tc201 installer
+ if (DOS_FindDevice(name) != DOS_DEVICES)
+ return DOS_OpenFile(name, OPEN_READ, entry, fcb);
+
+ LOG(LOG_FILES,LOG_NORMAL)("file create attributes %X file %s",attributes,name);
+ char fullname[DOS_PATHLENGTH];Bit8u drive;
+ DOS_PSP psp(dos.psp());
+ if (!DOS_MakeName(name,fullname,&drive)) return false;
+ /* Check for a free file handle */
+ Bit8u handle=DOS_FILES;Bit8u i;
+ for (i=0;i<DOS_FILES;i++) {
+ if (!Files[i]) {
+ handle=i;
+ break;
+ }
+ }
+ if (handle==DOS_FILES) {
+ DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
+ return false;
+ }
+ /* We have a position in the main table now find one in the psp table */
+ *entry = fcb?handle:psp.FindFreeFileEntry();
+ if (*entry==0xff) {
+ DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
+ return false;
+ }
+ /* Don't allow directories to be created */
+ if (attributes&DOS_ATTR_DIRECTORY) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ bool foundit=Drives[drive]->FileCreate(&Files[handle],fullname,attributes);
+ if (foundit) {
+ Files[handle]->SetDrive(drive);
+ Files[handle]->AddRef();
+ if (!fcb) psp.SetFileHandle(*entry,handle);
+ return true;
+ } else {
+ if(!PathExists(name)) DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ else DOS_SetError(DOSERR_FILE_NOT_FOUND);
+ return false;
+ }
+}
+
+bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry,bool fcb) {
+ /* First check for devices */
+ if (flags>2) LOG(LOG_FILES,LOG_ERROR)("Special file open command %X file %s",flags,name);
+ else LOG(LOG_FILES,LOG_NORMAL)("file open command %X file %s",flags,name);
+
+ DOS_PSP psp(dos.psp());
+ Bit16u attr = 0;
+ Bit8u devnum = DOS_FindDevice(name);
+ bool device = (devnum != DOS_DEVICES);
+ if(!device && DOS_GetFileAttr(name,&attr)) {
+ //DON'T ALLOW directories to be openened.(skip test if file is device).
+ if((attr & DOS_ATTR_DIRECTORY) || (attr & DOS_ATTR_VOLUME)){
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ }
+
+ char fullname[DOS_PATHLENGTH];Bit8u drive;Bit8u i;
+ /* First check if the name is correct */
+ if (!DOS_MakeName(name,fullname,&drive)) return false;
+ Bit8u handle=255;
+ /* Check for a free file handle */
+ for (i=0;i<DOS_FILES;i++) {
+ if (!Files[i]) {
+ handle=i;
+ break;
+ }
+ }
+ if (handle==255) {
+ DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
+ return false;
+ }
+ /* We have a position in the main table now find one in the psp table */
+ *entry = fcb?handle:psp.FindFreeFileEntry();
+
+ if (*entry==0xff) {
+ DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
+ return false;
+ }
+ bool exists=false;
+ if (device) {
+ Files[handle]=new DOS_Device(*Devices[devnum]);
+ } else {
+ exists=Drives[drive]->FileOpen(&Files[handle],fullname,flags);
+ if (exists) Files[handle]->SetDrive(drive);
+ }
+ if (exists || device ) {
+ Files[handle]->AddRef();
+ if (!fcb) psp.SetFileHandle(*entry,handle);
+ return true;
+ } else {
+ //Test if file exists, but opened in read-write mode (and writeprotected)
+ if(((flags&3) != OPEN_READ) && Drives[drive]->FileExists(fullname))
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ else {
+ if(!PathExists(name)) DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ else DOS_SetError(DOSERR_FILE_NOT_FOUND);
+ }
+ return false;
+ }
+}
+
+bool DOS_OpenFileExtended(char const * name, Bit16u flags, Bit16u createAttr, Bit16u action, Bit16u *entry, Bit16u* status) {
+// FIXME: Not yet supported : Bit 13 of flags (int 0x24 on critical error)
+ Bit16u result = 0;
+ if (action==0) {
+ // always fail setting
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ } else {
+ if (((action & 0x0f)>2) || ((action & 0xf0)>0x10)) {
+ // invalid action parameter
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ }
+ }
+ if (DOS_OpenFile(name, (Bit8u)(flags&0xff), entry)) {
+ // File already exists
+ switch (action & 0x0f) {
+ case 0x00: // failed
+ DOS_SetError(DOSERR_FILE_ALREADY_EXISTS);
+ return false;
+ case 0x01: // file open (already done)
+ result = 1;
+ break;
+ case 0x02: // replace
+ DOS_CloseFile(*entry);
+ if (!DOS_CreateFile(name, createAttr, entry)) return false;
+ result = 3;
+ break;
+ default:
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ E_Exit("DOS: OpenFileExtended: Unknown action.");
+ break;
+ }
+ } else {
+ // File doesn't exist
+ if ((action & 0xf0)==0) {
+ // uses error code from failed open
+ return false;
+ }
+ // Create File
+ if (!DOS_CreateFile(name, createAttr, entry)) {
+ // uses error code from failed create
+ return false;
+ }
+ result = 2;
+ }
+ // success
+ *status = result;
+ return true;
+}
+
+bool DOS_UnlinkFile(char const * const name) {
+ char fullname[DOS_PATHLENGTH];Bit8u drive;
+ // An existing device returns an access denied error
+ if (DOS_FindDevice(name) != DOS_DEVICES) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ if (!DOS_MakeName(name,fullname,&drive)) return false;
+ if(Drives[drive]->FileUnlink(fullname)){
+ return true;
+ } else {
+ DOS_SetError(DOSERR_FILE_NOT_FOUND);
+ return false;
+ }
+}
+
+bool DOS_GetFileAttr(char const * const name,Bit16u * attr) {
+ char fullname[DOS_PATHLENGTH];Bit8u drive;
+ if (!DOS_MakeName(name,fullname,&drive)) return false;
+ if (Drives[drive]->GetFileAttr(fullname,attr)) {
+ return true;
+ } else {
+ DOS_SetError(DOSERR_FILE_NOT_FOUND);
+ return false;
+ }
+}
+
+bool DOS_SetFileAttr(char const * const name,Bit16u /*attr*/)
+// this function does not change the file attributs
+// it just does some tests if file is available
+// returns false when using on cdrom (stonekeep)
+{
+ Bit16u attrTemp;
+ char fullname[DOS_PATHLENGTH];Bit8u drive;
+ if (!DOS_MakeName(name,fullname,&drive)) return false;
+ if (strncmp(Drives[drive]->GetInfo(),"CDRom ",6)==0 || strncmp(Drives[drive]->GetInfo(),"isoDrive ",9)==0) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ return Drives[drive]->GetFileAttr(fullname,&attrTemp);
+}
+
+bool DOS_Canonicalize(char const * const name,char * const big) {
+//TODO Add Better support for devices and shit but will it be needed i doubt it :)
+ Bit8u drive;
+ char fullname[DOS_PATHLENGTH];
+ if (!DOS_MakeName(name,fullname,&drive)) return false;
+ big[0]=drive+'A';
+ big[1]=':';
+ big[2]='\\';
+ strcpy(&big[3],fullname);
+ return true;
+}
+
+bool DOS_GetFreeDiskSpace(Bit8u drive,Bit16u * bytes,Bit8u * sectors,Bit16u * clusters,Bit16u * free) {
+ if (drive==0) drive=DOS_GetDefaultDrive();
+ else drive--;
+ if ((drive>=DOS_DRIVES) || (!Drives[drive])) {
+ DOS_SetError(DOSERR_INVALID_DRIVE);
+ return false;
+ }
+ return Drives[drive]->AllocationInfo(bytes,sectors,clusters,free);
+}
+
+bool DOS_DuplicateEntry(Bit16u entry,Bit16u * newentry) {
+ // Dont duplicate console handles
+/* if (entry<=STDPRN) {
+ *newentry = entry;
+ return true;
+ };
+*/
+ Bit8u handle=RealHandle(entry);
+ if (handle>=DOS_FILES) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ if (!Files[handle] || !Files[handle]->IsOpen()) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ DOS_PSP psp(dos.psp());
+ *newentry = psp.FindFreeFileEntry();
+ if (*newentry==0xff) {
+ DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
+ return false;
+ }
+ Files[handle]->AddRef();
+ psp.SetFileHandle(*newentry,handle);
+ return true;
+}
+
+bool DOS_ForceDuplicateEntry(Bit16u entry,Bit16u newentry) {
+ if(entry == newentry) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ }
+ Bit8u orig = RealHandle(entry);
+ if (orig >= DOS_FILES) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ if (!Files[orig] || !Files[orig]->IsOpen()) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ Bit8u newone = RealHandle(newentry);
+ if (newone < DOS_FILES && Files[newone]) {
+ DOS_CloseFile(newentry);
+ }
+ DOS_PSP psp(dos.psp());
+ Files[orig]->AddRef();
+ psp.SetFileHandle(newentry,orig);
+ return true;
+}
+
+
+bool DOS_CreateTempFile(char * const name,Bit16u * entry) {
+ size_t namelen=strlen(name);
+ char * tempname=name+namelen;
+ if (namelen==0) {
+ // temp file created in root directory
+ tempname[0]='\\';
+ tempname++;
+ } else {
+ if ((name[namelen-1]!='\\') && (name[namelen-1]!='/')) {
+ tempname[0]='\\';
+ tempname++;
+ }
+ }
+ dos.errorcode=0;
+ /* add random crap to the end of the name and try to open */
+ do {
+ Bit32u i;
+ for (i=0;i<8;i++) {
+ tempname[i]=(rand()%26)+'A';
+ }
+ tempname[8]=0;
+ } while ((!DOS_CreateFile(name,0,entry)) && (dos.errorcode==DOSERR_FILE_ALREADY_EXISTS));
+ if (dos.errorcode) return false;
+ return true;
+}
+
+char DOS_ToUpper(char c) {
+ unsigned char uc = *reinterpret_cast<unsigned char*>(&c);
+ if (uc > 0x60 && uc < 0x7B) uc -= 0x20;
+ else if (uc > 0x7F && uc < 0xA5) {
+ const unsigned char t[0x25] = {
+ 0x00, 0x9a, 0x45, 0x41, 0x8E, 0x41, 0x8F, 0x80, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x00, 0x00,
+ 0x00, 0x92, 0x00, 0x4F, 0x99, 0x4F, 0x55, 0x55, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x41, 0x49, 0x4F, 0x55, 0xA5};
+ if (t[uc - 0x80]) uc = t[uc-0x80];
+ }
+ char sc = *reinterpret_cast<char*>(&uc);
+ return sc;
+}
+
+#define FCB_SEP ":;,=+"
+#define ILLEGAL ":.;,=+ \t/\"[]<>|"
+
+static bool isvalid(const char in){
+ const char ill[]=ILLEGAL;
+ return (Bit8u(in)>0x1F) && (!strchr(ill,in));
+}
+
+#define PARSE_SEP_STOP 0x01
+#define PARSE_DFLT_DRIVE 0x02
+#define PARSE_BLNK_FNAME 0x04
+#define PARSE_BLNK_FEXT 0x08
+
+#define PARSE_RET_NOWILD 0
+#define PARSE_RET_WILD 1
+#define PARSE_RET_BADDRIVE 0xff
+
+Bit8u FCB_Parsename(Bit16u seg,Bit16u offset,Bit8u parser ,char *string, Bit8u *change) {
+ char * string_begin=string;
+ Bit8u ret=0;
+ if (!(parser & PARSE_DFLT_DRIVE)) {
+ // default drive forced, this intentionally invalidates an extended FCB
+ mem_writeb(PhysMake(seg,offset),0);
+ }
+ DOS_FCB fcb(seg,offset,false); // always a non-extended FCB
+ bool hasdrive,hasname,hasext,finished;
+ hasdrive=hasname=hasext=finished=false;
+ Bitu index=0;
+ Bit8u fill=' ';
+/* First get the old data from the fcb */
+#ifdef _MSC_VER
+#pragma pack (1)
+#endif
+ union {
+ struct {
+ char drive[2];
+ char name[9];
+ char ext[4];
+ } GCC_ATTRIBUTE (packed) part;
+ char full[DOS_FCBNAME];
+ } fcb_name;
+#ifdef _MSC_VER
+#pragma pack()
+#endif
+ /* Get the old information from the previous fcb */
+ fcb.GetName(fcb_name.full);
+ fcb_name.part.drive[0]-='A'-1;fcb_name.part.drive[1]=0;
+ fcb_name.part.name[8]=0;fcb_name.part.ext[3]=0;
+ /* strip leading spaces */
+ while((*string==' ')||(*string=='\t')) string++;
+
+ /* Strip of the leading separator */
+ if((parser & PARSE_SEP_STOP) && *string) {
+ char sep[] = FCB_SEP;char a[2];
+ a[0] = *string;a[1] = '\0';
+ if (strcspn(a,sep) == 0) string++;
+ }
+
+ /* Skip following spaces as well */
+ while((*string==' ')||(*string=='\t')) string++;
+
+ /* Check for a drive */
+ if (string[1]==':') {
+ unsigned char d = *reinterpret_cast<unsigned char*>(&string[0]);
+ if (!isvalid(toupper(d))) {string += 2; goto savefcb;} //TODO check (for ret value)
+ fcb_name.part.drive[0]=0;
+ hasdrive=true;
+ if (isalpha(d) && Drives[toupper(d)-'A']) { //Floppies under dos always exist, but don't bother with that at this level
+ ; //THIS* was here
+ } else ret=0xff;
+ fcb_name.part.drive[0]=DOS_ToUpper(string[0])-'A'+1; //Always do THIS* and continue parsing, just return the right code
+ string+=2;
+ }
+
+ /* Check for extension only file names */
+ if (string[0] == '.') {string++;goto checkext;}
+
+ /* do nothing if not a valid name */
+ if(!isvalid(string[0])) goto savefcb;
+
+ hasname=true;finished=false;fill=' ';index=0;
+ /* Copy the name */
+ while (true) {
+ unsigned char nc = *reinterpret_cast<unsigned char*>(&string[0]);
+ char ncs = (char)toupper(nc); //Should use DOS_ToUpper, but then more calls need to be changed.
+ if (ncs == '*') { //Handle *
+ fill = '?';
+ ncs = '?';
+ }
+ if (ncs == '?' && !ret && index < 8) ret = 1; //Don't override bad drive
+ if (!isvalid(ncs)) { //Fill up the name.
+ while(index < 8)
+ fcb_name.part.name[index++] = fill;
+ break;
+ }
+ if (index < 8) {
+ fcb_name.part.name[index++] = (fill == '?')?fill:ncs;
+ }
+ string++;
+ }
+ if (!(string[0]=='.')) goto savefcb;
+ string++;
+checkext:
+ /* Copy the extension */
+ hasext=true;finished=false;fill=' ';index=0;
+ while (true) {
+ unsigned char nc = *reinterpret_cast<unsigned char*>(&string[0]);
+ char ncs = (char)toupper(nc);
+ if (ncs == '*') { //Handle *
+ fill = '?';
+ ncs = '?';
+ }
+ if (ncs == '?' && !ret && index < 3) ret = 1;
+ if (!isvalid(ncs)) { //Fill up the name.
+ while(index < 3)
+ fcb_name.part.ext[index++] = fill;
+ break;
+ }
+ if (index < 3) {
+ fcb_name.part.ext[index++] = (fill=='?')?fill:ncs;
+ }
+ string++;
+ }
+savefcb:
+ if (!hasdrive & !(parser & PARSE_DFLT_DRIVE)) fcb_name.part.drive[0] = 0;
+ if (!hasname & !(parser & PARSE_BLNK_FNAME)) strcpy(fcb_name.part.name," ");
+ if (!hasext & !(parser & PARSE_BLNK_FEXT)) strcpy(fcb_name.part.ext," ");
+ fcb.SetName(fcb_name.part.drive[0],fcb_name.part.name,fcb_name.part.ext);
+ fcb.ClearBlockRecsize(); //Undocumented bonus work.
+ *change=(Bit8u)(string-string_begin);
+ return ret;
+}
+
+static void DTAExtendName(char * const name,char * const filename,char * const ext) {
+ char * find=strchr(name,'.');
+ if (find && find!=name) {
+ strcpy(ext,find+1);
+ *find=0;
+ } else ext[0]=0;
+ strcpy(filename,name);
+ size_t i;
+ for (i=strlen(name);i<8;i++) filename[i]=' ';
+ filename[8]=0;
+ for (i=strlen(ext);i<3;i++) ext[i]=' ';
+ ext[3]=0;
+}
+
+static void SaveFindResult(DOS_FCB & find_fcb) {
+ DOS_DTA find_dta(dos.tables.tempdta);
+ char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u date;Bit16u time;Bit8u attr;Bit8u drive;
+ char file_name[9];char ext[4];
+ find_dta.GetResult(name,size,date,time,attr);
+ drive=find_fcb.GetDrive()+1;
+ Bit8u find_attr = DOS_ATTR_ARCHIVE;
+ find_fcb.GetAttr(find_attr); /* Gets search attributes if extended */
+ /* Create a correct file and extention */
+ DTAExtendName(name,file_name,ext);
+ DOS_FCB fcb(RealSeg(dos.dta()),RealOff(dos.dta()));//TODO
+ fcb.Create(find_fcb.Extended());
+ fcb.SetName(drive,file_name,ext);
+ fcb.SetAttr(find_attr); /* Only adds attribute if fcb is extended */
+ fcb.SetResult(size,date,time,attr);
+}
+
+bool DOS_FCBCreate(Bit16u seg,Bit16u offset) {
+ DOS_FCB fcb(seg,offset);
+ char shortname[DOS_FCBNAME];Bit16u handle;
+ fcb.GetName(shortname);
+ Bit8u attr = DOS_ATTR_ARCHIVE;
+ fcb.GetAttr(attr);
+ if (!attr) attr = DOS_ATTR_ARCHIVE; //Better safe than sorry
+ if (!DOS_CreateFile(shortname,attr,&handle,true)) return false;
+ fcb.FileOpen((Bit8u)handle);
+ return true;
+}
+
+bool DOS_FCBOpen(Bit16u seg,Bit16u offset) {
+ DOS_FCB fcb(seg,offset);
+ char shortname[DOS_FCBNAME];Bit16u handle;
+ fcb.GetName(shortname);
+
+ /* Search for file if name has wildcards */
+ if (strpbrk(shortname,"*?")) {
+ LOG(LOG_FCB,LOG_WARN)("Wildcards in filename");
+ if (!DOS_FCBFindFirst(seg,offset)) return false;
+ DOS_DTA find_dta(dos.tables.tempdta);
+ DOS_FCB find_fcb(RealSeg(dos.tables.tempdta),RealOff(dos.tables.tempdta));
+ char name[DOS_NAMELENGTH_ASCII],file_name[9],ext[4];
+ Bit32u size;Bit16u date,time;Bit8u attr;
+ find_dta.GetResult(name,size,date,time,attr);
+ DTAExtendName(name,file_name,ext);
+ find_fcb.SetName(fcb.GetDrive()+1,file_name,ext);
+ find_fcb.GetName(shortname);
+ }
+
+ /* First check if the name is correct */
+ Bit8u drive;
+ char fullname[DOS_PATHLENGTH];
+ if (!DOS_MakeName(shortname,fullname,&drive)) return false;
+
+ /* Check, if file is already opened */
+ for (Bit8u i = 0;i < DOS_FILES;i++) {
+ if (Files[i] && Files[i]->IsOpen() && Files[i]->IsName(fullname)) {
+ Files[i]->AddRef();
+ fcb.FileOpen(i);
+ return true;
+ }
+ }
+
+ if (!DOS_OpenFile(shortname,OPEN_READWRITE,&handle,true)) return false;
+ fcb.FileOpen((Bit8u)handle);
+ return true;
+}
+
+bool DOS_FCBClose(Bit16u seg,Bit16u offset) {
+ DOS_FCB fcb(seg,offset);
+ if(!fcb.Valid()) return false;
+ Bit8u fhandle;
+ fcb.FileClose(fhandle);
+ DOS_CloseFile(fhandle,true);
+ return true;
+}
+
+bool DOS_FCBFindFirst(Bit16u seg,Bit16u offset) {
+ DOS_FCB fcb(seg,offset);
+ RealPt old_dta=dos.dta();dos.dta(dos.tables.tempdta);
+ char name[DOS_FCBNAME];fcb.GetName(name);
+ Bit8u attr = DOS_ATTR_ARCHIVE;
+ fcb.GetAttr(attr); /* Gets search attributes if extended */
+ bool ret=DOS_FindFirst(name,attr,true);
+ dos.dta(old_dta);
+ if (ret) SaveFindResult(fcb);
+ return ret;
+}
+
+bool DOS_FCBFindNext(Bit16u seg,Bit16u offset) {
+ DOS_FCB fcb(seg,offset);
+ RealPt old_dta=dos.dta();dos.dta(dos.tables.tempdta);
+ bool ret=DOS_FindNext();
+ dos.dta(old_dta);
+ if (ret) SaveFindResult(fcb);
+ return ret;
+}
+
+Bit8u DOS_FCBRead(Bit16u seg,Bit16u offset,Bit16u recno) {
+ DOS_FCB fcb(seg,offset);
+ Bit8u fhandle,cur_rec;Bit16u cur_block,rec_size;
+ fcb.GetSeqData(fhandle,rec_size);
+ if (fhandle==0xff && rec_size!=0) {
+ if (!DOS_FCBOpen(seg,offset)) return FCB_READ_NODATA;
+ LOG(LOG_FCB,LOG_WARN)("Reopened closed FCB");
+ fcb.GetSeqData(fhandle,rec_size);
+ }
+ if (rec_size == 0) {
+ rec_size = 128;
+ fcb.SetSeqData(fhandle,rec_size);
+ }
+ fcb.GetRecord(cur_block,cur_rec);
+ Bit32u pos=((cur_block*128)+cur_rec)*rec_size;
+ if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET,true)) return FCB_READ_NODATA;
+ Bit16u toread=rec_size;
+ if (!DOS_ReadFile(fhandle,dos_copybuf,&toread,true)) return FCB_READ_NODATA;
+ if (toread==0) return FCB_READ_NODATA;
+ if (toread < rec_size) { //Zero pad copybuffer to rec_size
+ Bitu i = toread;
+ while(i < rec_size) dos_copybuf[i++] = 0;
+ }
+ MEM_BlockWrite(Real2Phys(dos.dta())+recno*rec_size,dos_copybuf,rec_size);
+ if (++cur_rec>127) { cur_block++;cur_rec=0; }
+ fcb.SetRecord(cur_block,cur_rec);
+ if (toread==rec_size) return FCB_SUCCESS;
+ if (toread==0) return FCB_READ_NODATA;
+ return FCB_READ_PARTIAL;
+}
+
+Bit8u DOS_FCBWrite(Bit16u seg,Bit16u offset,Bit16u recno) {
+ DOS_FCB fcb(seg,offset);
+ Bit8u fhandle,cur_rec;Bit16u cur_block,rec_size;
+ fcb.GetSeqData(fhandle,rec_size);
+ if (fhandle==0xff && rec_size!=0) {
+ if (!DOS_FCBOpen(seg,offset)) return FCB_READ_NODATA;
+ LOG(LOG_FCB,LOG_WARN)("Reopened closed FCB");
+ fcb.GetSeqData(fhandle,rec_size);
+ }
+ if (rec_size == 0) {
+ rec_size = 128;
+ fcb.SetSeqData(fhandle,rec_size);
+ }
+ fcb.GetRecord(cur_block,cur_rec);
+ Bit32u pos=((cur_block*128)+cur_rec)*rec_size;
+ if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET,true)) return FCB_ERR_WRITE;
+ MEM_BlockRead(Real2Phys(dos.dta())+recno*rec_size,dos_copybuf,rec_size);
+ Bit16u towrite=rec_size;
+ if (!DOS_WriteFile(fhandle,dos_copybuf,&towrite,true)) return FCB_ERR_WRITE;
+ Bit32u size;Bit16u date,time;
+ fcb.GetSizeDateTime(size,date,time);
+ if (pos+towrite>size) size=pos+towrite;
+ //time doesn't keep track of endofday
+ date = DOS_PackDate(dos.date.year,dos.date.month,dos.date.day);
+ Bit32u ticks = mem_readd(BIOS_TIMER);
+ Bit32u seconds = (ticks*10)/182;
+ Bit16u hour = (Bit16u)(seconds/3600);
+ Bit16u min = (Bit16u)((seconds % 3600)/60);
+ Bit16u sec = (Bit16u)(seconds % 60);
+ time = DOS_PackTime(hour,min,sec);
+ Files[fhandle]->time = time;
+ Files[fhandle]->date = date;
+ fcb.SetSizeDateTime(size,date,time);
+ if (++cur_rec>127) { cur_block++;cur_rec=0; }
+ fcb.SetRecord(cur_block,cur_rec);
+ return FCB_SUCCESS;
+}
+
+Bit8u DOS_FCBIncreaseSize(Bit16u seg,Bit16u offset) {
+ DOS_FCB fcb(seg,offset);
+ Bit8u fhandle,cur_rec;Bit16u cur_block,rec_size;
+ fcb.GetSeqData(fhandle,rec_size);
+ fcb.GetRecord(cur_block,cur_rec);
+ Bit32u pos=((cur_block*128)+cur_rec)*rec_size;
+ if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET,true)) return FCB_ERR_WRITE;
+ Bit16u towrite=0;
+ if (!DOS_WriteFile(fhandle,dos_copybuf,&towrite,true)) return FCB_ERR_WRITE;
+ Bit32u size;Bit16u date,time;
+ fcb.GetSizeDateTime(size,date,time);
+ if (pos+towrite>size) size=pos+towrite;
+ //time doesn't keep track of endofday
+ date = DOS_PackDate(dos.date.year,dos.date.month,dos.date.day);
+ Bit32u ticks = mem_readd(BIOS_TIMER);
+ Bit32u seconds = (ticks*10)/182;
+ Bit16u hour = (Bit16u)(seconds/3600);
+ Bit16u min = (Bit16u)((seconds % 3600)/60);
+ Bit16u sec = (Bit16u)(seconds % 60);
+ time = DOS_PackTime(hour,min,sec);
+ Files[fhandle]->time = time;
+ Files[fhandle]->date = date;
+ fcb.SetSizeDateTime(size,date,time);
+ fcb.SetRecord(cur_block,cur_rec);
+ return FCB_SUCCESS;
+}
+
+Bit8u DOS_FCBRandomRead(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore) {
+/* if restore is true :random read else random blok read.
+ * random read updates old block and old record to reflect the random data
+ * before the read!!!!!!!!! and the random data is not updated! (user must do this)
+ * Random block read updates these fields to reflect the state after the read!
+ */
+ DOS_FCB fcb(seg,offset);
+ Bit32u random;
+ Bit16u old_block=0;
+ Bit8u old_rec=0;
+ Bit8u error=0;
+ Bit16u count;
+
+ /* Set the correct record from the random data */
+ fcb.GetRandom(random);
+ fcb.SetRecord((Bit16u)(random / 128),(Bit8u)(random & 127));
+ if (restore) fcb.GetRecord(old_block,old_rec);//store this for after the read.
+ // Read records
+ for (count=0; count<*numRec; count++) {
+ error = DOS_FCBRead(seg,offset,count);
+ if (error!=FCB_SUCCESS) break;
+ }
+ if (error==FCB_READ_PARTIAL) count++; //partial read counts
+ *numRec=count;
+ Bit16u new_block;Bit8u new_rec;
+ fcb.GetRecord(new_block,new_rec);
+ if (restore) fcb.SetRecord(old_block,old_rec);
+ /* Update the random record pointer with new position only when restore is false*/
+ if(!restore) fcb.SetRandom(new_block*128+new_rec);
+ return error;
+}
+
+Bit8u DOS_FCBRandomWrite(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore) {
+/* see FCB_RandomRead */
+ DOS_FCB fcb(seg,offset);
+ Bit32u random;
+ Bit16u old_block=0;
+ Bit8u old_rec=0;
+ Bit8u error=0;
+ Bit16u count;
+
+ /* Set the correct record from the random data */
+ fcb.GetRandom(random);
+ fcb.SetRecord((Bit16u)(random / 128),(Bit8u)(random & 127));
+ if (restore) fcb.GetRecord(old_block,old_rec);
+ if (*numRec > 0) {
+ /* Write records */
+ for (count=0; count<*numRec; count++) {
+ error = DOS_FCBWrite(seg,offset,count);// dos_fcbwrite return 0 false when true...
+ if (error!=FCB_SUCCESS) break;
+ }
+ *numRec=count;
+ } else {
+ DOS_FCBIncreaseSize(seg,offset);
+ }
+ Bit16u new_block;Bit8u new_rec;
+ fcb.GetRecord(new_block,new_rec);
+ if (restore) fcb.SetRecord(old_block,old_rec);
+ /* Update the random record pointer with new position only when restore is false */
+ if (!restore) fcb.SetRandom(new_block*128+new_rec);
+ return error;
+}
+
+bool DOS_FCBGetFileSize(Bit16u seg,Bit16u offset) {
+ char shortname[DOS_PATHLENGTH];Bit16u entry;
+ DOS_FCB fcb(seg,offset);
+ fcb.GetName(shortname);
+ if (!DOS_OpenFile(shortname,OPEN_READ,&entry,true)) return false;
+ Bit32u size = 0;
+ Files[entry]->Seek(&size,DOS_SEEK_END);
+ DOS_CloseFile(entry,true);
+
+ Bit8u handle; Bit16u rec_size;
+ fcb.GetSeqData(handle,rec_size);
+ if (rec_size == 0) rec_size = 128; //Use default if missing.
+
+ Bit32u random=(size/rec_size);
+ if (size % rec_size) random++;
+ fcb.SetRandom(random);
+ return true;
+}
+
+bool DOS_FCBDeleteFile(Bit16u seg,Bit16u offset){
+/* FCB DELETE honours wildcards. it will return true if one or more
+ * files get deleted.
+ * To get this: the dta is set to temporary dta in which found files are
+ * stored. This can not be the tempdta as that one is used by fcbfindfirst
+ */
+ RealPt old_dta=dos.dta();dos.dta(dos.tables.tempdta_fcbdelete);
+ RealPt new_dta=dos.dta();
+ bool nextfile = false;
+ bool return_value = false;
+ nextfile = DOS_FCBFindFirst(seg,offset);
+ DOS_FCB fcb(RealSeg(new_dta),RealOff(new_dta));
+ while(nextfile) {
+ char shortname[DOS_FCBNAME] = { 0 };
+ fcb.GetName(shortname);
+ bool res=DOS_UnlinkFile(shortname);
+ if(!return_value && res) return_value = true; //at least one file deleted
+ nextfile = DOS_FCBFindNext(seg,offset);
+ }
+ dos.dta(old_dta); /*Restore dta */
+ return return_value;
+}
+
+bool DOS_FCBRenameFile(Bit16u seg, Bit16u offset){
+ DOS_FCB fcbold(seg,offset);
+ DOS_FCB fcbnew(seg,offset+16);
+ if(!fcbold.Valid()) return false;
+ char oldname[DOS_FCBNAME];
+ char newname[DOS_FCBNAME];
+ fcbold.GetName(oldname);fcbnew.GetName(newname);
+
+ /* Check, if sourcefile is still open. This was possible in DOS, but modern oses don't like this */
+ Bit8u drive; char fullname[DOS_PATHLENGTH];
+ if (!DOS_MakeName(oldname,fullname,&drive)) return false;
+
+ DOS_PSP psp(dos.psp());
+ for (Bit8u i=0;i<DOS_FILES;i++) {
+ if (Files[i] && Files[i]->IsOpen() && Files[i]->IsName(fullname)) {
+ Bit16u handle = psp.FindEntryByHandle(i);
+ //(more than once maybe)
+ if (handle == 0xFF) {
+ DOS_CloseFile(i,true);
+ } else {
+ DOS_CloseFile(handle);
+ }
+ }
+ }
+
+ /* Rename the file */
+ return DOS_Rename(oldname,newname);
+}
+
+void DOS_FCBSetRandomRecord(Bit16u seg, Bit16u offset) {
+ DOS_FCB fcb(seg,offset);
+ Bit16u block;Bit8u rec;
+ fcb.GetRecord(block,rec);
+ fcb.SetRandom(block*128+rec);
+}
+
+
+bool DOS_FileExists(char const * const name) {
+ char fullname[DOS_PATHLENGTH];Bit8u drive;
+ if (!DOS_MakeName(name,fullname,&drive)) return false;
+ return Drives[drive]->FileExists(fullname);
+}
+
+bool DOS_GetAllocationInfo(Bit8u drive,Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters) {
+ if (!drive) drive = DOS_GetDefaultDrive();
+ else drive--;
+ if (drive >= DOS_DRIVES || !Drives[drive]) {
+ DOS_SetError(DOSERR_INVALID_DRIVE);
+ return false;
+ }
+ Bit16u _free_clusters;
+ Drives[drive]->AllocationInfo(_bytes_sector,_sectors_cluster,_total_clusters,&_free_clusters);
+ SegSet16(ds,RealSeg(dos.tables.mediaid));
+ reg_bx=RealOff(dos.tables.mediaid+drive*9);
+ return true;
+}
+
+bool DOS_SetDrive(Bit8u drive) {
+ if (Drives[drive]) {
+ DOS_SetDefaultDrive(drive);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool DOS_GetFileDate(Bit16u entry, Bit16u* otime, Bit16u* odate) {
+ Bit32u handle=RealHandle(entry);
+ if (handle>=DOS_FILES) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ if (!Files[handle] || !Files[handle]->IsOpen()) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ };
+ if (!Files[handle]->UpdateDateTimeFromHost()) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ }
+ *otime = Files[handle]->time;
+ *odate = Files[handle]->date;
+ return true;
+}
+
+void DOS_SetupFiles (void) {
+ /* Setup the File Handles */
+ Bit32u i;
+ for (i=0;i<DOS_FILES;i++) {
+ Files[i]=0;
+ }
+ /* Setup the Virtual Disk System */
+ for (i=0;i<DOS_DRIVES;i++) {
+ Drives[i]=0;
+ }
+ Drives[25]=new Virtual_Drive();
+}
diff --git a/src/dos/dos_ioctl.cpp b/src/dos/dos_ioctl.cpp
new file mode 100644
index 000000000..90ef826f8
--- /dev/null
+++ b/src/dos/dos_ioctl.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include "dosbox.h"
+#include "callback.h"
+#include "mem.h"
+#include "regs.h"
+#include "dos_inc.h"
+
+bool DOS_IOCTL(void) {
+// LOG(LOG_IOCTL,LOG_WARN)("%X %X %X %X",reg_ax,reg_bx,reg_cx,reg_dx);
+ Bitu handle=0;Bit8u drive=0;
+ /* calls 0-4,6,7,10,12,16 use a file handle */
+ if ((reg_al<4) || (reg_al==0x06) || (reg_al==0x07) || (reg_al==0x0a) || (reg_al==0x0c) || (reg_al==0x10)) {
+ handle=RealHandle(reg_bx);
+ if (handle>=DOS_FILES) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ }
+ if (!Files[handle]) {
+ DOS_SetError(DOSERR_INVALID_HANDLE);
+ return false;
+ }
+ } else if (reg_al<0x12) { /* those use a diskdrive except 0x0b */
+ if (reg_al!=0x0b) {
+ drive=reg_bl;if (!drive) drive = DOS_GetDefaultDrive();else drive--;
+ if( (drive >= 2) && !(( drive < DOS_DRIVES ) && Drives[drive]) ) {
+ DOS_SetError(DOSERR_INVALID_DRIVE);
+ return false;
+ }
+ }
+ } else {
+ LOG(LOG_DOSMISC,LOG_ERROR)("DOS:IOCTL Call %2X unhandled",reg_al);
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ }
+ switch(reg_al) {
+ case 0x00: /* Get Device Information */
+ if (Files[handle]->GetInformation() & 0x8000) { //Check for device
+ reg_dx=Files[handle]->GetInformation();
+ } else {
+ Bit8u hdrive=Files[handle]->GetDrive();
+ if (hdrive==0xff) {
+ LOG(LOG_IOCTL,LOG_NORMAL)("00:No drive set");
+ hdrive=2; // defaulting to C:
+ }
+ /* return drive number in lower 5 bits for block devices */
+ reg_dx=(Files[handle]->GetInformation()&0xffe0)|hdrive;
+ }
+ reg_ax=reg_dx; //Destroyed officially
+ return true;
+ case 0x01: /* Set Device Information */
+ if (reg_dh != 0) {
+ DOS_SetError(DOSERR_DATA_INVALID);
+ return false;
+ } else {
+ if (Files[handle]->GetInformation() & 0x8000) { //Check for device
+ reg_al=(Bit8u)(Files[handle]->GetInformation() & 0xff);
+ } else {
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ }
+ }
+ return true;
+ case 0x02: /* Read from Device Control Channel */
+ if (Files[handle]->GetInformation() & 0xc000) {
+ /* is character device with IOCTL support */
+ PhysPt bufptr=PhysMake(SegValue(ds),reg_dx);
+ Bit16u retcode=0;
+ if (((DOS_Device*)(Files[handle]))->ReadFromControlChannel(bufptr,reg_cx,&retcode)) {
+ reg_ax=retcode;
+ return true;
+ }
+ }
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ case 0x03: /* Write to Device Control Channel */
+ if (Files[handle]->GetInformation() & 0xc000) {
+ /* is character device with IOCTL support */
+ PhysPt bufptr=PhysMake(SegValue(ds),reg_dx);
+ Bit16u retcode=0;
+ if (((DOS_Device*)(Files[handle]))->WriteToControlChannel(bufptr,reg_cx,&retcode)) {
+ reg_ax=retcode;
+ return true;
+ }
+ }
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ case 0x06: /* Get Input Status */
+ if (Files[handle]->GetInformation() & 0x8000) { //Check for device
+ reg_al=(Files[handle]->GetInformation() & 0x40) ? 0x0 : 0xff;
+ } else { // FILE
+ Bit32u oldlocation=0;
+ Files[handle]->Seek(&oldlocation, DOS_SEEK_CUR);
+ Bit32u endlocation=0;
+ Files[handle]->Seek(&endlocation, DOS_SEEK_END);
+ if(oldlocation < endlocation){//Still data available
+ reg_al=0xff;
+ } else {
+ reg_al=0x0; //EOF or beyond
+ }
+ Files[handle]->Seek(&oldlocation, DOS_SEEK_SET); //restore filelocation
+ LOG(LOG_IOCTL,LOG_NORMAL)("06:Used Get Input Status on regular file with handle %d",handle);
+ }
+ return true;
+ case 0x07: /* Get Output Status */
+ LOG(LOG_IOCTL,LOG_NORMAL)("07:Fakes output status is ready for handle %d",handle);
+ reg_al=0xff;
+ return true;
+ case 0x08: /* Check if block device removable */
+ /* cdrom drives and drive a&b are removable */
+ if (drive < 2) reg_ax=0;
+ else if (!Drives[drive]->isRemovable()) reg_ax=1;
+ else {
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ }
+ return true;
+ case 0x09: /* Check if block device remote */
+ if ((drive >= 2) && Drives[drive]->isRemote()) {
+ reg_dx=0x1000; // device is remote
+ // undocumented bits always clear
+ } else {
+ reg_dx=0x0802; // Open/Close supported; 32bit access supported (any use? fixes Fable installer)
+ // undocumented bits from device attribute word
+ // TODO Set bit 9 on drives that don't support direct I/O
+ }
+ reg_ax=0x300;
+ return true;
+ case 0x0B: /* Set sharing retry count */
+ if (reg_dx==0) {
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ }
+ return true;
+ case 0x0D: /* Generic block device request */
+ {
+ if (drive < 2 && !Drives[drive]) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ if (reg_ch != 0x08 || Drives[drive]->isRemovable()) {
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ }
+ PhysPt ptr = SegPhys(ds)+reg_dx;
+ switch (reg_cl) {
+ case 0x60: /* Get Device parameters */
+ //mem_writeb(ptr+0,0); // special functions (call value)
+ mem_writeb(ptr+1,(drive>=2)?0x05:0x07); // type: hard disk(5), 1.44 floppy(7)
+ mem_writew(ptr+2,(drive>=2)?0x01:0x00); // attributes: bit 0 set for nonremovable
+ mem_writew(ptr+4,0x0000); // num of cylinders
+ mem_writeb(ptr+6,0x00); // media type (00=other type)
+ // bios parameter block following
+ mem_writew(ptr+7,0x0200); // bytes per sector (Win3 File Mgr. uses it)
+ break;
+ case 0x46: /* Set volume serial number */
+ break;
+ case 0x66: /* Get volume serial number */
+ {
+ char const* bufin=Drives[drive]->GetLabel();
+ char buffer[11];memset(buffer,' ',11);
+
+ char const* find_ext=strchr(bufin,'.');
+ if (find_ext) {
+ Bitu size=(Bitu)(find_ext-bufin);
+ if (size>8) size=8;
+ memcpy(buffer,bufin,size);
+ find_ext++;
+ memcpy(buffer+8,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext));
+ } else {
+ memcpy(buffer,bufin,(strlen(bufin) > 8) ? 8 : strlen(bufin));
+ }
+
+ char buf2[8]={ 'F','A','T','1','6',' ',' ',' '};
+ if(drive<2) buf2[4] = '2'; //FAT12 for floppies
+
+ //mem_writew(ptr+0,0); //Info level (call value)
+ mem_writed(ptr+2,0x1234); //Serial number
+ MEM_BlockWrite(ptr+6,buffer,11);//volumename
+ MEM_BlockWrite(ptr+0x11,buf2,8);//filesystem
+ }
+ break;
+ default :
+ LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL Call 0D:%2X Drive %2X unhandled",reg_cl,drive);
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ }
+ reg_ax=0;
+ return true;
+ }
+ case 0x0E: /* Get Logical Drive Map */
+ if (drive < 2) {
+ if (Drives[drive]) reg_al=drive+1;
+ else reg_al=1;
+ } else if (Drives[drive]->isRemovable()) {
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ return false;
+ } else reg_al=0; /* Only 1 logical drive assigned */
+ reg_ah=0x07;
+ return true;
+ default:
+ LOG(LOG_DOSMISC,LOG_ERROR)("DOS:IOCTL Call %2X unhandled",reg_al);
+ DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
+ break;
+ }
+ return false;
+}
+
+
+bool DOS_GetSTDINStatus(void) {
+ Bit32u handle=RealHandle(STDIN);
+ if (handle==0xFF) return false;
+ if (Files[handle] && (Files[handle]->GetInformation() & 64)) return false;
+ return true;
+}
diff --git a/src/dos/dos_keyboard_layout.cpp b/src/dos/dos_keyboard_layout.cpp
new file mode 100644
index 000000000..f600fc632
--- /dev/null
+++ b/src/dos/dos_keyboard_layout.cpp
@@ -0,0 +1,1302 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "bios.h"
+#include "bios_disk.h"
+#include "setup.h"
+#include "support.h"
+#include "../ints/int10.h"
+#include "regs.h"
+#include "callback.h"
+#include "mapper.h"
+#include "drives.h"
+#include "dos_inc.h"
+
+#include "dos_codepages.h"
+#include "dos_keyboard_layout_data.h"
+
+#if defined (WIN32)
+#include <windows.h>
+#endif
+
+
+static FILE* OpenDosboxFile(const char* name) {
+ Bit8u drive;
+ char fullname[DOS_PATHLENGTH];
+
+ localDrive* ldp=0;
+ // try to build dos name
+ if (DOS_MakeName(name,fullname,&drive)) {
+ try {
+ // try to open file on mounted drive first
+ ldp=dynamic_cast<localDrive*>(Drives[drive]);
+ if (ldp) {
+ FILE *tmpfile=ldp->GetSystemFilePtr(fullname, "rb");
+ if (tmpfile != NULL) return tmpfile;
+ }
+ }
+ catch(...) {}
+ }
+ FILE *tmpfile=fopen(name, "rb");
+ return tmpfile;
+}
+
+
+class keyboard_layout {
+public:
+ keyboard_layout() {
+ this->reset();
+ language_codes=NULL;
+ use_foreign_layout=false;
+ sprintf(current_keyboard_file_name, "none");
+ };
+
+ ~keyboard_layout();
+
+ // read in a codepage from a .cpi-file
+ Bitu read_codepage_file(const char* codepage_file_name, Bit32s codepage_id);
+ Bit16u extract_codepage(const char* keyboard_file_name);
+ // read in a keyboard layout from a .kl-file
+ Bitu read_keyboard_file(const char* keyboard_file_name, Bit32s req_cp);
+
+ // call layout_key to apply the current language layout
+ bool layout_key(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3);
+
+ Bitu switch_keyboard_layout(const char* new_layout, keyboard_layout* &created_layout, Bit32s& tried_cp);
+ void switch_foreign_layout();
+ const char* get_layout_name();
+ const char* main_language_code();
+
+
+private:
+ static const Bit8u layout_pages=12;
+ Bit16u current_layout[(MAX_SCAN_CODE+1)*layout_pages];
+ struct {
+ Bit16u required_flags,forbidden_flags;
+ Bit16u required_userflags,forbidden_userflags;
+ } current_layout_planes[layout_pages-4];
+ Bit8u additional_planes,used_lock_modifiers;
+
+ // diacritics table
+ Bit8u diacritics[2048];
+ Bit16u diacritics_entries;
+ Bit16u diacritics_character;
+ Bit16u user_keys;
+
+ char current_keyboard_file_name[256];
+ bool use_foreign_layout;
+
+ // language code storage used when switching layouts
+ char** language_codes;
+ Bitu language_code_count;
+
+ void reset();
+ void read_keyboard_file(Bit32s specific_layout);
+ Bitu read_keyboard_file(const char* keyboard_file_name, Bit32s specific_layout, Bit32s requested_codepage);
+ bool map_key(Bitu key, Bit16u layouted_key, bool is_command, bool is_keypair);
+};
+
+
+keyboard_layout::~keyboard_layout() {
+ if (language_codes) {
+ for (Bitu i=0; i<language_code_count; i++)
+ delete[] language_codes[i];
+ delete[] language_codes;
+ language_codes=NULL;
+ }
+}
+
+void keyboard_layout::reset() {
+ for (Bit32u i=0; i<(MAX_SCAN_CODE+1)*layout_pages; i++) current_layout[i]=0;
+ for (Bit32u i=0; i<layout_pages-4; i++) {
+ current_layout_planes[i].required_flags=0;
+ current_layout_planes[i].forbidden_flags=0xffff;
+ current_layout_planes[i].required_userflags=0;
+ current_layout_planes[i].forbidden_userflags=0xffff;
+ }
+ used_lock_modifiers=0x0f;
+ diacritics_entries=0; // no diacritics loaded
+ diacritics_character=0;
+ user_keys=0; // all userkeys off
+ language_code_count=0;
+}
+
+Bitu keyboard_layout::read_keyboard_file(const char* keyboard_file_name, Bit32s req_cp) {
+ return this->read_keyboard_file(keyboard_file_name, -1, req_cp);
+}
+
+// switch to a different layout
+void keyboard_layout::read_keyboard_file(Bit32s specific_layout) {
+ if (strcmp(current_keyboard_file_name,"none"))
+ this->read_keyboard_file(current_keyboard_file_name, specific_layout, dos.loaded_codepage);
+}
+
+static Bit32u read_kcl_file(const char* kcl_file_name, const char* layout_id, bool first_id_only) {
+ FILE* tempfile = OpenDosboxFile(kcl_file_name);
+ if (tempfile==0) return 0;
+
+ static Bit8u rbuf[8192];
+
+ // check ID-bytes of file
+ Bit32u dr=(Bit32u)fread(rbuf, sizeof(Bit8u), 7, tempfile);
+ if ((dr<7) || (rbuf[0]!=0x4b) || (rbuf[1]!=0x43) || (rbuf[2]!=0x46)) {
+ fclose(tempfile);
+ return 0;
+ }
+
+ fseek(tempfile, 7+rbuf[6], SEEK_SET);
+
+ for (;;) {
+ Bit32u cur_pos=(Bit32u)(ftell(tempfile));
+ dr=(Bit32u)fread(rbuf, sizeof(Bit8u), 5, tempfile);
+ if (dr<5) break;
+ Bit16u len=host_readw(&rbuf[0]);
+
+ Bit8u data_len=rbuf[2];
+
+ char lng_codes[258];
+ fseek(tempfile, -2, SEEK_CUR);
+ // get all language codes for this layout
+ for (Bitu i=0; i<data_len;) {
+ fread(rbuf, sizeof(Bit8u), 2, tempfile);
+ Bit16u lcnum=host_readw(&rbuf[0]);
+ i+=2;
+ Bitu lcpos=0;
+ for (;i<data_len;) {
+ fread(rbuf, sizeof(Bit8u), 1, tempfile);
+ i++;
+ if (((char)rbuf[0])==',') break;
+ lng_codes[lcpos++]=(char)rbuf[0];
+ }
+ lng_codes[lcpos]=0;
+ if (strcasecmp(lng_codes, layout_id)==0) {
+ // language ID found in file, return file position
+ fclose(tempfile);
+ return cur_pos;
+ }
+ if (first_id_only) break;
+ if (lcnum) {
+ sprintf(&lng_codes[lcpos],"%d",lcnum);
+ if (strcasecmp(lng_codes, layout_id)==0) {
+ // language ID found in file, return file position
+ return cur_pos;
+ }
+ }
+ }
+ fseek(tempfile, cur_pos+3+len, SEEK_SET);
+ }
+
+ fclose(tempfile);
+ return 0;
+}
+
+static Bit32u read_kcl_data(Bit8u * kcl_data, Bit32u kcl_data_size, const char* layout_id, bool first_id_only) {
+ // check ID-bytes
+ if ((kcl_data[0]!=0x4b) || (kcl_data[1]!=0x43) || (kcl_data[2]!=0x46)) {
+ return 0;
+ }
+
+ Bit32u dpos=7+kcl_data[6];
+
+ for (;;) {
+ if (dpos+5>kcl_data_size) break;
+ Bit32u cur_pos=dpos;
+ Bit16u len=host_readw(&kcl_data[dpos]);
+ Bit8u data_len=kcl_data[dpos+2];
+ dpos+=5;
+
+ char lng_codes[258];
+ // get all language codes for this layout
+ for (Bitu i=0; i<data_len;) {
+ Bit16u lcnum=host_readw(&kcl_data[dpos-2]);
+ i+=2;
+ Bitu lcpos=0;
+ for (;i<data_len;) {
+ if (dpos+1>kcl_data_size) break;
+ char lc=(char)kcl_data[dpos];
+ dpos++;
+ i++;
+ if (lc==',') break;
+ lng_codes[lcpos++]=lc;
+ }
+ lng_codes[lcpos]=0;
+ if (strcasecmp(lng_codes, layout_id)==0) {
+ // language ID found, return position
+ return cur_pos;
+ }
+ if (first_id_only) break;
+ if (lcnum) {
+ sprintf(&lng_codes[lcpos],"%d",lcnum);
+ if (strcasecmp(lng_codes, layout_id)==0) {
+ // language ID found, return position
+ return cur_pos;
+ }
+ }
+ dpos+=2;
+ }
+ dpos=cur_pos+3+len;
+ }
+ return 0;
+}
+
+Bitu keyboard_layout::read_keyboard_file(const char* keyboard_file_name, Bit32s specific_layout, Bit32s requested_codepage) {
+ this->reset();
+
+ if (specific_layout==-1) strcpy(current_keyboard_file_name, keyboard_file_name);
+ if (!strcmp(keyboard_file_name,"none")) return KEYB_NOERROR;
+
+ static Bit8u read_buf[65535];
+ Bit32u read_buf_size, read_buf_pos, bytes_read;
+ Bit32u start_pos=5;
+
+ char nbuf[512];
+ read_buf_size = 0;
+ sprintf(nbuf, "%s.kl", keyboard_file_name);
+ FILE* tempfile = OpenDosboxFile(nbuf);
+ if (tempfile==NULL) {
+ // try keyboard layout libraries next
+ if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,true))) {
+ tempfile = OpenDosboxFile("keyboard.sys");
+ } else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,true))) {
+ tempfile = OpenDosboxFile("keybrd2.sys");
+ } else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,true))) {
+ tempfile = OpenDosboxFile("keybrd3.sys");
+ } else if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,false))) {
+ tempfile = OpenDosboxFile("keyboard.sys");
+ } else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,false))) {
+ tempfile = OpenDosboxFile("keybrd2.sys");
+ } else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,false))) {
+ tempfile = OpenDosboxFile("keybrd3.sys");
+ } else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,true))) {
+ read_buf_size=0;
+ for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
+ } else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,true))) {
+ read_buf_size=0;
+ for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
+ } else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,true))) {
+ read_buf_size=0;
+ for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
+ } else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,false))) {
+ read_buf_size=0;
+ for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
+ } else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,false))) {
+ read_buf_size=0;
+ for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
+ } else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,false))) {
+ read_buf_size=0;
+ for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
+ } else {
+ LOG(LOG_BIOS,LOG_ERROR)("Keyboard layout file %s not found",keyboard_file_name);
+ return KEYB_FILENOTFOUND;
+ }
+ if (tempfile) {
+ fseek(tempfile, start_pos+2, SEEK_SET);
+ read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
+ fclose(tempfile);
+ }
+ start_pos=0;
+ } else {
+ // check ID-bytes of file
+ Bit32u dr=(Bit32u)fread(read_buf, sizeof(Bit8u), 4, tempfile);
+ if ((dr<4) || (read_buf[0]!=0x4b) || (read_buf[1]!=0x4c) || (read_buf[2]!=0x46)) {
+ LOG(LOG_BIOS,LOG_ERROR)("Invalid keyboard layout file %s",keyboard_file_name);
+ return KEYB_INVALIDFILE;
+ }
+
+ fseek(tempfile, 0, SEEK_SET);
+ read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
+ fclose(tempfile);
+ }
+
+ Bit8u data_len,submappings;
+ data_len=read_buf[start_pos++];
+
+ language_codes=new char*[data_len];
+ language_code_count=0;
+ // get all language codes for this layout
+ for (Bitu i=0; i<data_len;) {
+ language_codes[language_code_count]=new char[256];
+ i+=2;
+ Bitu lcpos=0;
+ for (;i<data_len;) {
+ char lcode=char(read_buf[start_pos+i]);
+ i++;
+ if (lcode==',') break;
+ language_codes[language_code_count][lcpos++]=lcode;
+ }
+ language_codes[language_code_count][lcpos]=0;
+ language_code_count++;
+ }
+
+ start_pos+=data_len; // start_pos==absolute position of KeybCB block
+
+ submappings=read_buf[start_pos];
+ additional_planes=read_buf[start_pos+1];
+
+ // four pages always occupied by normal,shift,flags,commandbits
+ if (additional_planes>(layout_pages-4)) additional_planes=(layout_pages-4);
+
+ // seek to plane descriptor
+ read_buf_pos=start_pos+0x14+submappings*8;
+ for (Bit16u cplane=0; cplane<additional_planes; cplane++) {
+ Bit16u plane_flags;
+
+ // get required-flags (shift/alt/ctrl-states etc.)
+ plane_flags=host_readw(&read_buf[read_buf_pos]);
+ read_buf_pos+=2;
+ current_layout_planes[cplane].required_flags=plane_flags;
+ used_lock_modifiers |= (plane_flags&0x70);
+ // get forbidden-flags
+ plane_flags=host_readw(&read_buf[read_buf_pos]);
+ read_buf_pos+=2;
+ current_layout_planes[cplane].forbidden_flags=plane_flags;
+
+ // get required-userflags
+ plane_flags=host_readw(&read_buf[read_buf_pos]);
+ read_buf_pos+=2;
+ current_layout_planes[cplane].required_userflags=plane_flags;
+ // get forbidden-userflags
+ plane_flags=host_readw(&read_buf[read_buf_pos]);
+ read_buf_pos+=2;
+ current_layout_planes[cplane].forbidden_userflags=plane_flags;
+ }
+
+ bool found_matching_layout=false;
+
+ // check all submappings and use them if general submapping or same codepage submapping
+ for (Bit16u sub_map=0; (sub_map<submappings) && (!found_matching_layout); sub_map++) {
+ Bit16u submap_cp, table_offset;
+
+ if ((sub_map!=0) && (specific_layout!=-1)) sub_map=(Bit16u)(specific_layout&0xffff);
+
+ // read codepage of submapping
+ submap_cp=host_readw(&read_buf[start_pos+0x14+sub_map*8]);
+ if ((submap_cp!=0) && (submap_cp!=requested_codepage) && (specific_layout==-1))
+ continue; // skip nonfitting submappings
+
+ if (submap_cp==requested_codepage) found_matching_layout=true;
+
+ // get table offset
+ table_offset=host_readw(&read_buf[start_pos+0x18+sub_map*8]);
+ diacritics_entries=0;
+ if (table_offset!=0) {
+ // process table
+ Bit16u i,j;
+ for (i=0; i<2048;) {
+ if (read_buf[start_pos+table_offset+i]==0) break; // end of table
+ diacritics_entries++;
+ i+=read_buf[start_pos+table_offset+i+1]*2+2;
+ }
+ // copy diacritics table
+ for (j=0; j<=i; j++) diacritics[j]=read_buf[start_pos+table_offset+j];
+ }
+
+
+ // get table offset
+ table_offset=host_readw(&read_buf[start_pos+0x16+sub_map*8]);
+ if (table_offset==0) continue; // non-present table
+
+ read_buf_pos=start_pos+table_offset;
+
+ bytes_read=read_buf_size-read_buf_pos;
+
+ // process submapping table
+ for (Bit32u i=0; i<bytes_read;) {
+ Bit8u scan=read_buf[read_buf_pos++];
+ if (scan==0) break;
+ Bit8u scan_length=(read_buf[read_buf_pos]&7)+1; // length of data struct
+ read_buf_pos+=2;
+ i+=3;
+ if (((scan&0x7f)<=MAX_SCAN_CODE) && (scan_length>0)) {
+ // add all available mappings
+ for (Bit16u addmap=0; addmap<scan_length; addmap++) {
+ if (addmap>additional_planes+2) break;
+ Bitu charptr=read_buf_pos+addmap*((read_buf[read_buf_pos-2]&0x80)?2:1);
+ Bit16u kchar=read_buf[charptr];
+
+ if (kchar!=0) { // key remapped
+ if (read_buf[read_buf_pos-2]&0x80) kchar|=read_buf[charptr+1]<<8; // scancode/char pair
+ // overwrite mapping
+ current_layout[scan*layout_pages+addmap]=kchar;
+ // clear command bit
+ current_layout[scan*layout_pages+layout_pages-2]&=~(1<<addmap);
+ // add command bit
+ current_layout[scan*layout_pages+layout_pages-2]|=(read_buf[read_buf_pos-1] & (1<<addmap));
+ }
+ }
+
+ // calculate max length of entries, taking into account old number of entries
+ Bit8u new_flags=current_layout[scan*layout_pages+layout_pages-1]&0x7;
+ if ((read_buf[read_buf_pos-2]&0x7) > new_flags) new_flags = read_buf[read_buf_pos-2]&0x7;
+
+ // merge flag bits in as well
+ new_flags |= (read_buf[read_buf_pos-2] | current_layout[scan*layout_pages+layout_pages-1]) & 0xf0;
+
+ current_layout[scan*layout_pages+layout_pages-1]=new_flags;
+ if (read_buf[read_buf_pos-2]&0x80) scan_length*=2; // granularity flag (S)
+ }
+ i+=scan_length; // advance pointer
+ read_buf_pos+=scan_length;
+ }
+ if (specific_layout==sub_map) break;
+ }
+
+ if (found_matching_layout) {
+ if (specific_layout==-1) LOG(LOG_BIOS,LOG_NORMAL)("Keyboard layout %s successfully loaded",keyboard_file_name);
+ else LOG(LOG_BIOS,LOG_NORMAL)("Keyboard layout %s (%i) successfully loaded",keyboard_file_name,specific_layout);
+ this->use_foreign_layout=true;
+ return KEYB_NOERROR;
+ }
+
+ LOG(LOG_BIOS,LOG_ERROR)("No matching keyboard layout found in %s",keyboard_file_name);
+
+ // reset layout data (might have been changed by general layout)
+ this->reset();
+
+ return KEYB_LAYOUTNOTFOUND;
+}
+
+bool keyboard_layout::layout_key(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3) {
+ if (key>MAX_SCAN_CODE) return false;
+ if (!this->use_foreign_layout) return false;
+
+ bool is_special_pair=(current_layout[key*layout_pages+layout_pages-1] & 0x80)==0x80;
+
+ if ((((flags1&used_lock_modifiers)&0x7c)==0) && ((flags3&2)==0)) {
+ // check if shift/caps is active:
+ // (left_shift OR right_shift) XOR (key_affected_by_caps AND caps_locked)
+ if ((((flags1&2)>>1) | (flags1&1)) ^ (((current_layout[key*layout_pages+layout_pages-1] & 0x40) & (flags1 & 0x40))>>6)) {
+ // shift plane
+ if (current_layout[key*layout_pages+1]!=0) {
+ // check if command-bit is set for shift plane
+ bool is_command=(current_layout[key*layout_pages+layout_pages-2]&2)!=0;
+ if (this->map_key(key, current_layout[key*layout_pages+1],
+ is_command, is_special_pair)) return true;
+ }
+ } else {
+ // normal plane
+ if (current_layout[key*layout_pages]!=0) {
+ // check if command-bit is set for normal plane
+ bool is_command=(current_layout[key*layout_pages+layout_pages-2]&1)!=0;
+ if (this->map_key(key, current_layout[key*layout_pages],
+ is_command, is_special_pair)) return true;
+ }
+ }
+ }
+
+ // calculate current flags
+ Bit16u current_flags=(flags1&0x7f) | (((flags2&3) | (flags3&0xc))<<8);
+ if (flags1&3) current_flags|=0x4000; // either shift key active
+ if (flags3&2) current_flags|=0x1000; // e0 prefixed
+
+ // check all planes if flags fit
+ for (Bit16u cplane=0; cplane<additional_planes; cplane++) {
+ Bit16u req_flags=current_layout_planes[cplane].required_flags;
+ Bit16u req_userflags=current_layout_planes[cplane].required_userflags;
+ // test flags
+ if (((current_flags & req_flags)==req_flags) &&
+ ((user_keys & req_userflags)==req_userflags) &&
+ ((current_flags & current_layout_planes[cplane].forbidden_flags)==0) &&
+ ((user_keys & current_layout_planes[cplane].forbidden_userflags)==0)) {
+ // remap key
+ if (current_layout[key*layout_pages+2+cplane]!=0) {
+ // check if command-bit is set for this plane
+ bool is_command=((current_layout[key*layout_pages+layout_pages-2]>>(cplane+2))&1)!=0;
+ if (this->map_key(key, current_layout[key*layout_pages+2+cplane],
+ is_command, is_special_pair)) return true;
+ } else break; // abort plane checking
+ }
+ }
+
+ if (diacritics_character>0) {
+ // ignore state-changing keys
+ switch(key) {
+ case 0x1d: /* Ctrl Pressed */
+ case 0x2a: /* Left Shift Pressed */
+ case 0x36: /* Right Shift Pressed */
+ case 0x38: /* Alt Pressed */
+ case 0x3a: /* Caps Lock */
+ case 0x45: /* Num Lock */
+ case 0x46: /* Scroll Lock */
+ break;
+ default:
+ if (diacritics_character-200>=diacritics_entries) {
+ diacritics_character=0;
+ return true;
+ }
+ Bit16u diacritics_start=0;
+ // search start of subtable
+ for (Bit16u i=0; i<diacritics_character-200; i++)
+ diacritics_start+=diacritics[diacritics_start+1]*2+2;
+
+ BIOS_AddKeyToBuffer((Bit16u)(key<<8) | diacritics[diacritics_start]);
+ diacritics_character=0;
+ }
+ }
+
+ return false;
+}
+
+bool keyboard_layout::map_key(Bitu key, Bit16u layouted_key, bool is_command, bool is_keypair) {
+ if (is_command) {
+ Bit8u key_command=(Bit8u)(layouted_key&0xff);
+ // check if diacritics-command
+ if ((key_command>=200) && (key_command<235)) {
+ // diacritics command
+ diacritics_character=key_command;
+ if (diacritics_character-200>=diacritics_entries) diacritics_character=0;
+ return true;
+ } else if ((key_command>=120) && (key_command<140)) {
+ // switch layout command
+ this->read_keyboard_file(key_command-119);
+ return true;
+ } else if ((key_command>=180) && (key_command<188)) {
+ // switch user key off
+ user_keys&=~(1<<(key_command-180));
+ return true;
+ } else if ((key_command>=188) && (key_command<196)) {
+ // switch user key on
+ user_keys|=(1<<(key_command-188));
+ return true;
+ } else if (key_command==160) return true; // nop command
+ } else {
+ // non-command
+ if (diacritics_character>0) {
+ if (diacritics_character-200>=diacritics_entries) diacritics_character = 0;
+ else {
+ Bit16u diacritics_start=0;
+ // search start of subtable
+ for (Bit16u i=0; i<diacritics_character-200; i++)
+ diacritics_start+=diacritics[diacritics_start+1]*2+2;
+
+ Bit8u diacritics_length=diacritics[diacritics_start+1];
+ diacritics_start+=2;
+ diacritics_character=0; // reset
+
+ // search scancode
+ for (Bit16u i=0; i<diacritics_length; i++) {
+ if (diacritics[diacritics_start+i*2]==(layouted_key&0xff)) {
+ // add diacritics to keybuf
+ BIOS_AddKeyToBuffer((Bit16u)(key<<8) | diacritics[diacritics_start+i*2+1]);
+ return true;
+ }
+ }
+ // add standard-diacritics to keybuf
+ BIOS_AddKeyToBuffer((Bit16u)(key<<8) | diacritics[diacritics_start-2]);
+ }
+ }
+
+ // add remapped key to keybuf
+ if (is_keypair) BIOS_AddKeyToBuffer(layouted_key);
+ else BIOS_AddKeyToBuffer((Bit16u)(key<<8) | (layouted_key&0xff));
+
+ return true;
+ }
+ return false;
+}
+
+Bit16u keyboard_layout::extract_codepage(const char* keyboard_file_name) {
+ if (!strcmp(keyboard_file_name,"none")) return 437;
+
+ Bit32u read_buf_size;
+ static Bit8u read_buf[65535];
+ Bit32u start_pos=5;
+
+ char nbuf[512];
+ sprintf(nbuf, "%s.kl", keyboard_file_name);
+ FILE* tempfile = OpenDosboxFile(nbuf);
+ if (tempfile==NULL) {
+ // try keyboard layout libraries next
+ if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,true))) {
+ tempfile = OpenDosboxFile("keyboard.sys");
+ } else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,true))) {
+ tempfile = OpenDosboxFile("keybrd2.sys");
+ } else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,true))) {
+ tempfile = OpenDosboxFile("keybrd3.sys");
+ } else if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,false))) {
+ tempfile = OpenDosboxFile("keyboard.sys");
+ } else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,false))) {
+ tempfile = OpenDosboxFile("keybrd2.sys");
+ } else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,false))) {
+ tempfile = OpenDosboxFile("keybrd3.sys");
+ } else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,true))) {
+ read_buf_size=0;
+ for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
+ } else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,true))) {
+ read_buf_size=0;
+ for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
+ } else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,true))) {
+ read_buf_size=0;
+ for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
+ } else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,false))) {
+ read_buf_size=0;
+ for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
+ } else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,false))) {
+ read_buf_size=0;
+ for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
+ } else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,false))) {
+ read_buf_size=0;
+ for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
+ } else {
+ start_pos=0;
+ LOG(LOG_BIOS,LOG_ERROR)("Keyboard layout file %s not found",keyboard_file_name);
+ return 437;
+ }
+ if (tempfile) {
+ fseek(tempfile, start_pos+2, SEEK_SET);
+ read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
+ fclose(tempfile);
+ }
+ start_pos=0;
+ } else {
+ // check ID-bytes of file
+ Bit32u dr=(Bit32u)fread(read_buf, sizeof(Bit8u), 4, tempfile);
+ if ((dr<4) || (read_buf[0]!=0x4b) || (read_buf[1]!=0x4c) || (read_buf[2]!=0x46)) {
+ LOG(LOG_BIOS,LOG_ERROR)("Invalid keyboard layout file %s",keyboard_file_name);
+ return 437;
+ }
+
+ fseek(tempfile, 0, SEEK_SET);
+ read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
+ fclose(tempfile);
+ }
+
+ Bit8u data_len,submappings;
+ data_len=read_buf[start_pos++];
+
+ start_pos+=data_len; // start_pos==absolute position of KeybCB block
+
+ submappings=read_buf[start_pos];
+
+ // check all submappings and use them if general submapping or same codepage submapping
+ for (Bit16u sub_map=0; (sub_map<submappings); sub_map++) {
+ Bit16u submap_cp;
+
+ // read codepage of submapping
+ submap_cp=host_readw(&read_buf[start_pos+0x14+sub_map*8]);
+
+ if (submap_cp!=0) return submap_cp;
+ }
+ return 437;
+}
+
+Bitu keyboard_layout::read_codepage_file(const char* codepage_file_name, Bit32s codepage_id) {
+ char cp_filename[512];
+ strcpy(cp_filename, codepage_file_name);
+ if (!strcmp(cp_filename,"none")) return KEYB_NOERROR;
+
+ if (codepage_id==dos.loaded_codepage) return KEYB_NOERROR;
+
+ if (!strcmp(cp_filename,"auto")) {
+ // select matching .cpi-file for specified codepage
+ switch (codepage_id) {
+ case 437: case 850: case 852: case 853: case 857: case 858:
+ sprintf(cp_filename, "EGA.CPI"); break;
+ case 775: case 859: case 1116: case 1117:
+ sprintf(cp_filename, "EGA2.CPI"); break;
+ case 771: case 772: case 808: case 855: case 866: case 872:
+ sprintf(cp_filename, "EGA3.CPI"); break;
+ case 848: case 849: case 1125: case 1131: case 61282:
+ sprintf(cp_filename, "EGA4.CPI"); break;
+ case 737: case 851: case 869:
+ sprintf(cp_filename, "EGA5.CPI"); break;
+ case 113: case 899: case 59829: case 60853:
+ sprintf(cp_filename, "EGA6.CPI"); break;
+ case 58152: case 58210: case 59234: case 60258: case 62306:
+ sprintf(cp_filename, "EGA7.CPI"); break;
+ case 770: case 773: case 774: case 777: case 778:
+ sprintf(cp_filename, "EGA8.CPI"); break;
+ case 860: case 861: case 863: case 865:
+ sprintf(cp_filename, "EGA9.CPI"); break;
+ case 667: case 668: case 790: case 867: case 991: case 57781:
+ sprintf(cp_filename, "EGA10.CPI"); break;
+ default:
+ LOG_MSG("No matching cpi file for codepage %i",codepage_id);
+ return KEYB_INVALIDCPFILE;
+ }
+ }
+
+ Bit32u start_pos;
+ Bit16u number_of_codepages;
+
+ char nbuf[512];
+ sprintf(nbuf, "%s", cp_filename);
+ FILE* tempfile=OpenDosboxFile(nbuf);
+ if (tempfile==NULL) {
+ size_t strsz=strlen(nbuf);
+ if (strsz) {
+ char plc=(char)toupper(*reinterpret_cast<unsigned char*>(&nbuf[strsz-1]));
+ if (plc=='I') {
+ // try CPX-extension as well
+ nbuf[strsz-1]='X';
+ tempfile=OpenDosboxFile(nbuf);
+ } else if (plc=='X') {
+ // try CPI-extension as well
+ nbuf[strsz-1]='I';
+ tempfile=OpenDosboxFile(nbuf);
+ }
+ }
+ }
+
+ static Bit8u cpi_buf[65536];
+ Bit32u cpi_buf_size=0,size_of_cpxdata=0;;
+ bool upxfound=false;
+ Bit16u found_at_pos=5;
+ if (tempfile==NULL) {
+ // check if build-in codepage is available
+ switch (codepage_id) {
+ case 437: case 850: case 852: case 853: case 857: case 858:
+ for (Bitu bct=0; bct<6322; bct++) cpi_buf[bct]=font_ega_cpx[bct];
+ cpi_buf_size=6322;
+ break;
+ case 771: case 772: case 808: case 855: case 866: case 872:
+ for (Bitu bct=0; bct<5455; bct++) cpi_buf[bct]=font_ega3_cpx[bct];
+ cpi_buf_size=5455;
+ break;
+ case 737: case 851: case 869:
+ for (Bitu bct=0; bct<5720; bct++) cpi_buf[bct]=font_ega5_cpx[bct];
+ cpi_buf_size=5720;
+ break;
+ default:
+ return KEYB_INVALIDCPFILE;
+ break;
+ }
+ upxfound=true;
+ found_at_pos=0x29;
+ size_of_cpxdata=cpi_buf_size;
+ } else {
+ Bit32u dr=(Bit32u)fread(cpi_buf, sizeof(Bit8u), 5, tempfile);
+ // check if file is valid
+ if (dr<5) {
+ LOG(LOG_BIOS,LOG_ERROR)("Codepage file %s invalid",cp_filename);
+ return KEYB_INVALIDCPFILE;
+ }
+ // check if non-compressed cpi file
+ if ((cpi_buf[0]!=0xff) || (cpi_buf[1]!=0x46) || (cpi_buf[2]!=0x4f) ||
+ (cpi_buf[3]!=0x4e) || (cpi_buf[4]!=0x54)) {
+ // check if dr-dos custom cpi file
+ if ((cpi_buf[0]==0x7f) && (cpi_buf[1]!=0x44) && (cpi_buf[2]!=0x52) &&
+ (cpi_buf[3]!=0x46) && (cpi_buf[4]!=0x5f)) {
+ LOG(LOG_BIOS,LOG_ERROR)("Codepage file %s has unsupported DR-DOS format",cp_filename);
+ return KEYB_INVALIDCPFILE;
+ }
+ // check if compressed cpi file
+ Bit8u next_byte=0;
+ for (Bitu i=0; i<100; i++) {
+ fread(&next_byte, sizeof(Bit8u), 1, tempfile); found_at_pos++;
+ while (next_byte==0x55) {
+ fread(&next_byte, sizeof(Bit8u), 1, tempfile); found_at_pos++;
+ if (next_byte==0x50) {
+ fread(&next_byte, sizeof(Bit8u), 1, tempfile); found_at_pos++;
+ if (next_byte==0x58) {
+ fread(&next_byte, sizeof(Bit8u), 1, tempfile); found_at_pos++;
+ if (next_byte==0x21) {
+ // read version ID
+ fread(&next_byte, sizeof(Bit8u), 1, tempfile);
+ found_at_pos++;
+ upxfound=true;
+ break;
+ }
+ }
+ }
+ }
+ if (upxfound) break;
+ }
+ if (!upxfound) {
+ LOG(LOG_BIOS,LOG_ERROR)("Codepage file %s invalid: %x",cp_filename,cpi_buf[0]);
+ return KEYB_INVALIDCPFILE;
+ } else {
+ if (next_byte<10) E_Exit("UPX-compressed cpi file, but upx-version too old");
+
+ // read in compressed CPX-file
+ fseek(tempfile, 0, SEEK_SET);
+ size_of_cpxdata=(Bitu)fread(cpi_buf, sizeof(Bit8u), 65536, tempfile);
+ }
+ } else {
+ // standard uncompressed cpi-file
+ fseek(tempfile, 0, SEEK_SET);
+ cpi_buf_size=(Bit32u)fread(cpi_buf, sizeof(Bit8u), 65536, tempfile);
+ }
+ }
+
+ if (upxfound) {
+ if (size_of_cpxdata>0xfe00) E_Exit("Size of cpx-compressed data too big");
+
+ found_at_pos+=19;
+ // prepare for direct decompression
+ cpi_buf[found_at_pos]=0xcb;
+
+ Bit16u seg=0;
+ Bit16u size=0x1500;
+ if (!DOS_AllocateMemory(&seg,&size)) E_Exit("Not enough free low memory to unpack data");
+ MEM_BlockWrite((seg<<4)+0x100,cpi_buf,size_of_cpxdata);
+
+ // setup segments
+ Bit16u save_ds=SegValue(ds);
+ Bit16u save_es=SegValue(es);
+ Bit16u save_ss=SegValue(ss);
+ Bit32u save_esp=reg_esp;
+ SegSet16(ds,seg);
+ SegSet16(es,seg);
+ SegSet16(ss,seg+0x1000);
+ reg_esp=0xfffe;
+
+ // let UPX unpack the file
+ CALLBACK_RunRealFar(seg,0x100);
+
+ SegSet16(ds,save_ds);
+ SegSet16(es,save_es);
+ SegSet16(ss,save_ss);
+ reg_esp=save_esp;
+
+ // get unpacked content
+ MEM_BlockRead((seg<<4)+0x100,cpi_buf,65536);
+ cpi_buf_size=65536;
+
+ DOS_FreeMemory(seg);
+ }
+
+
+ start_pos=host_readd(&cpi_buf[0x13]);
+ number_of_codepages=host_readw(&cpi_buf[start_pos]);
+ start_pos+=4;
+
+ // search if codepage is provided by file
+ for (Bit16u test_codepage=0; test_codepage<number_of_codepages; test_codepage++) {
+ Bit16u device_type, font_codepage, font_type;
+
+ // device type can be display/printer (only the first is supported)
+ device_type=host_readw(&cpi_buf[start_pos+0x04]);
+ font_codepage=host_readw(&cpi_buf[start_pos+0x0e]);
+
+ Bit32u font_data_header_pt;
+ font_data_header_pt=host_readd(&cpi_buf[start_pos+0x16]);
+
+ font_type=host_readw(&cpi_buf[font_data_header_pt]);
+
+ if ((device_type==0x0001) && (font_type==0x0001) && (font_codepage==codepage_id)) {
+ // valid/matching codepage found
+
+ Bit16u number_of_fonts,font_data_length;
+ number_of_fonts=host_readw(&cpi_buf[font_data_header_pt+0x02]);
+ font_data_length=host_readw(&cpi_buf[font_data_header_pt+0x04]);
+
+ bool font_changed=false;
+ Bit32u font_data_start=font_data_header_pt+0x06;
+
+ // load all fonts if possible
+ for (Bit16u current_font=0; current_font<number_of_fonts; current_font++) {
+ Bit8u font_height=cpi_buf[font_data_start];
+ font_data_start+=6;
+ if (font_height==0x10) {
+ // 16x8 font
+ PhysPt font16pt=Real2Phys(int10.rom.font_16);
+ for (Bitu i=0;i<256*16;i++) {
+ phys_writeb(font16pt+i,cpi_buf[font_data_start+i]);
+ }
+ // terminate alternate list to prevent loading
+ phys_writeb(Real2Phys(int10.rom.font_16_alternate),0);
+ font_changed=true;
+ } else if (font_height==0x0e) {
+ // 14x8 font
+ PhysPt font14pt=Real2Phys(int10.rom.font_14);
+ for (Bitu i=0;i<256*14;i++) {
+ phys_writeb(font14pt+i,cpi_buf[font_data_start+i]);
+ }
+ // terminate alternate list to prevent loading
+ phys_writeb(Real2Phys(int10.rom.font_14_alternate),0);
+ font_changed=true;
+ } else if (font_height==0x08) {
+ // 8x8 fonts
+ PhysPt font8pt=Real2Phys(int10.rom.font_8_first);
+ for (Bitu i=0;i<128*8;i++) {
+ phys_writeb(font8pt+i,cpi_buf[font_data_start+i]);
+ }
+ font8pt=Real2Phys(int10.rom.font_8_second);
+ for (Bitu i=0;i<128*8;i++) {
+ phys_writeb(font8pt+i,cpi_buf[font_data_start+i+128*8]);
+ }
+ font_changed=true;
+ }
+ font_data_start+=font_height*256;
+ }
+
+ LOG(LOG_BIOS,LOG_NORMAL)("Codepage %i successfully loaded",codepage_id);
+
+ // set codepage entries
+ dos.loaded_codepage=(Bit16u)(codepage_id&0xffff);
+
+ // update font if necessary
+ if (font_changed && (CurMode->type==M_TEXT) && (IS_EGAVGA_ARCH)) {
+ INT10_ReloadFont();
+ }
+ INT10_SetupRomMemoryChecksum();
+
+ return KEYB_NOERROR;
+ }
+
+ start_pos=host_readd(&cpi_buf[start_pos]);
+ start_pos+=2;
+ }
+
+ LOG(LOG_BIOS,LOG_ERROR)("Codepage %i not found",codepage_id);
+
+ return KEYB_INVALIDCPFILE;
+}
+
+Bitu keyboard_layout::switch_keyboard_layout(const char* new_layout, keyboard_layout*& created_layout, Bit32s& tried_cp) {
+ if (strncasecmp(new_layout,"US",2)) {
+ // switch to a foreign layout
+ char tbuf[256];
+ strcpy(tbuf, new_layout);
+ size_t newlen=strlen(tbuf);
+
+ bool language_code_found=false;
+ // check if language code is present in loaded foreign layout
+ for (Bitu i=0; i<language_code_count; i++) {
+ if (!strncasecmp(tbuf,language_codes[i],newlen)) {
+ language_code_found=true;
+ break;
+ }
+ }
+
+ if (language_code_found) {
+ if (!this->use_foreign_layout) {
+ // switch to foreign layout
+ this->use_foreign_layout=true;
+ diacritics_character=0;
+ LOG(LOG_BIOS,LOG_NORMAL)("Switched to layout %s",tbuf);
+ }
+ } else {
+ keyboard_layout * temp_layout=new keyboard_layout();
+ Bitu req_codepage=temp_layout->extract_codepage(new_layout);
+ tried_cp = req_codepage;
+ Bitu kerrcode=temp_layout->read_keyboard_file(new_layout, req_codepage);
+ if (kerrcode) {
+ delete temp_layout;
+ return kerrcode;
+ }
+ // ...else keyboard layout loaded successfully, change codepage accordingly
+ kerrcode=temp_layout->read_codepage_file("auto", req_codepage);
+ if (kerrcode) {
+ delete temp_layout;
+ return kerrcode;
+ }
+ // Everything went fine, switch to new layout
+ created_layout=temp_layout;
+ }
+ } else if (this->use_foreign_layout) {
+ // switch to the US layout
+ this->use_foreign_layout=false;
+ diacritics_character=0;
+ LOG(LOG_BIOS,LOG_NORMAL)("Switched to US layout");
+ }
+ return KEYB_NOERROR;
+}
+
+void keyboard_layout::switch_foreign_layout() {
+ this->use_foreign_layout=!this->use_foreign_layout;
+ diacritics_character=0;
+ if (this->use_foreign_layout) LOG(LOG_BIOS,LOG_NORMAL)("Switched to foreign layout");
+ else LOG(LOG_BIOS,LOG_NORMAL)("Switched to US layout");
+}
+
+const char* keyboard_layout::get_layout_name() {
+ // get layout name (language ID or NULL if default layout)
+ if (use_foreign_layout) {
+ if (strcmp(current_keyboard_file_name,"none") != 0) {
+ return (const char*)&current_keyboard_file_name;
+ }
+ }
+ return NULL;
+}
+
+const char* keyboard_layout::main_language_code() {
+ if (language_codes) {
+ return language_codes[0];
+ }
+ return NULL;
+}
+
+
+static keyboard_layout* loaded_layout=NULL;
+
+// CTRL-ALT-F2 switches between foreign and US-layout using this function
+/* static void switch_keyboard_layout(bool pressed) {
+ if (!pressed)
+ return;
+ if (loaded_layout) loaded_layout->switch_foreign_layout();
+} */
+
+// called by int9-handler
+bool DOS_LayoutKey(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3) {
+ if (loaded_layout) return loaded_layout->layout_key(key, flags1, flags2, flags3);
+ else return false;
+}
+
+Bitu DOS_LoadKeyboardLayout(const char * layoutname, Bit32s codepage, const char * codepagefile) {
+ keyboard_layout * temp_layout=new keyboard_layout();
+ // try to read the layout for the specified codepage
+ Bitu kerrcode=temp_layout->read_keyboard_file(layoutname, codepage);
+ if (kerrcode) {
+ delete temp_layout;
+ return kerrcode;
+ }
+ // ...else keyboard layout loaded successfully, change codepage accordingly
+ kerrcode=temp_layout->read_codepage_file(codepagefile, codepage);
+ if (kerrcode) {
+ delete temp_layout;
+ return kerrcode;
+ }
+ // Everything went fine, switch to new layout
+ loaded_layout=temp_layout;
+ return KEYB_NOERROR;
+}
+
+Bitu DOS_SwitchKeyboardLayout(const char* new_layout, Bit32s& tried_cp) {
+ if (loaded_layout) {
+ keyboard_layout* changed_layout=NULL;
+ Bitu ret_code=loaded_layout->switch_keyboard_layout(new_layout, changed_layout, tried_cp);
+ if (changed_layout) {
+ // Remove old layout, activate new layout
+ delete loaded_layout;
+ loaded_layout=changed_layout;
+ }
+ return ret_code;
+ } else return 0xff;
+}
+
+// get currently loaded layout name (NULL if no layout is loaded)
+const char* DOS_GetLoadedLayout(void) {
+ if (loaded_layout) {
+ return loaded_layout->get_layout_name();
+ }
+ return NULL;
+}
+
+
+class DOS_KeyboardLayout: public Module_base {
+public:
+ DOS_KeyboardLayout(Section* configuration):Module_base(configuration){
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+ dos.loaded_codepage=437; // US codepage already initialized
+ loaded_layout=new keyboard_layout();
+
+ const char * layoutname=section->Get_string("keyboardlayout");
+
+ Bits wants_dos_codepage = -1;
+ if (!strncmp(layoutname,"auto",4)) {
+#if defined (WIN32)
+ WORD cur_kb_layout = LOWORD(GetKeyboardLayout(0));
+ WORD cur_kb_subID = 0;
+ char layoutID_string[KL_NAMELENGTH];
+ if (GetKeyboardLayoutName(layoutID_string)) {
+ if (strlen(layoutID_string) == 8) {
+ int cur_kb_layout_by_name = ConvHexWord((char*)&layoutID_string[4]);
+ layoutID_string[4] = 0;
+ int subID = ConvHexWord((char*)&layoutID_string[0]);
+ if ((cur_kb_layout_by_name>0) && (cur_kb_layout_by_name<65536)) {
+ // use layout ID extracted from the layout string
+ cur_kb_layout = (WORD)cur_kb_layout_by_name;
+ }
+ if ((subID>=0) && (subID<100)) {
+ // use sublanguage ID extracted from the layout string
+ cur_kb_subID = (WORD)subID;
+ }
+ }
+ }
+ // try to match emulated keyboard layout with host-keyboardlayout
+ // codepage 437 (standard) is preferred
+ switch (cur_kb_layout) {
+/* case 1026:
+ layoutname = "bg241";
+ break; */
+ case 1029:
+ layoutname = "cz243";
+ break;
+ case 1030:
+ layoutname = "dk";
+ break;
+ case 1031:
+ layoutname = "gr";
+ wants_dos_codepage = 437;
+ break;
+ case 1033:
+ // US
+ return;
+ case 1032:
+ layoutname = "gk";
+ break;
+ case 1034:
+ layoutname = "sp";
+ wants_dos_codepage = 437;
+ break;
+ case 1035:
+ layoutname = "su";
+ wants_dos_codepage = 437;
+ break;
+ case 1036:
+ layoutname = "fr";
+ wants_dos_codepage = 437;
+ break;
+ case 1038:
+ if (cur_kb_subID==1) layoutname = "hu";
+ else layoutname = "hu208";
+ break;
+ case 1039:
+ layoutname = "is161";
+ break;
+ case 1040:
+ layoutname = "it";
+ wants_dos_codepage = 437;
+ break;
+ case 1043:
+ layoutname = "nl";
+ wants_dos_codepage = 437;
+ break;
+ case 1044:
+ layoutname = "no";
+ break;
+ case 1045:
+ layoutname = "pl";
+ break;
+ case 1046:
+ layoutname = "br";
+ wants_dos_codepage = 437;
+ break;
+/* case 1048:
+ layoutname = "ro446";
+ break; */
+ case 1049:
+ layoutname = "ru";
+ wants_dos_codepage = 437;
+ break;
+ case 1050:
+ layoutname = "hr";
+ break;
+ case 1051:
+ layoutname = "sk";
+ break;
+/* case 1052:
+ layoutname = "sq448";
+ break; */
+ case 1053:
+ layoutname = "sv";
+ wants_dos_codepage = 437;
+ break;
+ case 1055:
+ layoutname = "tr";
+ break;
+ case 1058:
+ layoutname = "ur";
+ wants_dos_codepage = 437;
+ break;
+ case 1059:
+ layoutname = "bl";
+ break;
+ case 1060:
+ layoutname = "si";
+ break;
+ case 1061:
+ layoutname = "et";
+ break;
+/* case 1062:
+ layoutname = "lv";
+ break; */
+/* case 1063:
+ layoutname = "lt221";
+ break; */
+/* case 1064:
+ layoutname = "tj";
+ break;
+ case 1066:
+ layoutname = "vi";
+ break;
+ case 1067:
+ layoutname = "hy";
+ break; */
+ case 2055:
+ layoutname = "sg";
+ wants_dos_codepage = 437;
+ break;
+ case 2070:
+ layoutname = "po";
+ break;
+ case 4108:
+ layoutname = "sf";
+ wants_dos_codepage = 437;
+ break;
+ default:
+ break;
+ }
+#endif
+ }
+
+ bool extract_codepage = true;
+ if (wants_dos_codepage>0) {
+ if ((loaded_layout->read_codepage_file("auto", (Bitu)wants_dos_codepage)) == KEYB_NOERROR) {
+ // preselected codepage was successfully loaded
+ extract_codepage = false;
+ }
+ }
+ if (extract_codepage) {
+ // try to find a good codepage for the requested layout
+ Bitu req_codepage = loaded_layout->extract_codepage(layoutname);
+ loaded_layout->read_codepage_file("auto", req_codepage);
+ }
+
+/* if (strncmp(layoutname,"auto",4) && strncmp(layoutname,"none",4)) {
+ LOG_MSG("Loading DOS keyboard layout %s ...",layoutname);
+ } */
+ if (loaded_layout->read_keyboard_file(layoutname, dos.loaded_codepage)) {
+ if (strncmp(layoutname,"auto",4)) {
+ LOG_MSG("Error loading keyboard layout %s",layoutname);
+ }
+ } else {
+ const char* lcode = loaded_layout->main_language_code();
+ if (lcode) {
+ LOG_MSG("DOS keyboard layout loaded with main language code %s for layout %s",lcode,layoutname);
+ }
+ }
+ }
+
+ ~DOS_KeyboardLayout(){
+ if ((dos.loaded_codepage!=437) && (CurMode->type==M_TEXT)) {
+ INT10_ReloadRomFonts();
+ dos.loaded_codepage=437; // US codepage
+ }
+ if (loaded_layout) {
+ delete loaded_layout;
+ loaded_layout=NULL;
+ }
+ }
+};
+
+static DOS_KeyboardLayout* test;
+
+void DOS_KeyboardLayout_ShutDown(Section* /*sec*/) {
+ delete test;
+}
+
+void DOS_KeyboardLayout_Init(Section* sec) {
+ test = new DOS_KeyboardLayout(sec);
+ sec->AddDestroyFunction(&DOS_KeyboardLayout_ShutDown,true);
+// MAPPER_AddHandler(switch_keyboard_layout,MK_f2,MMOD1|MMOD2,"sw_layout","Switch Layout");
+}
diff --git a/src/dos/dos_keyboard_layout_data.h b/src/dos/dos_keyboard_layout_data.h
new file mode 100644
index 000000000..e850a6c35
--- /dev/null
+++ b/src/dos/dos_keyboard_layout_data.h
@@ -0,0 +1,5393 @@
+/*
+ * Copyright (C) Henrique Peron
+ *
+ * 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 file contains data of .KL-files. They have been generated
+ by Henrique Peron using FreeDOS KC and are combined into .SYS files. */
+
+/* KC: compiles keyboard descriptors in KEY language to a KeybCB,
+ wrapped in a KL file (for use of FD-KEYB 2.X)
+ Copyright (C) 2004 by Aitor SANTAMARIA_MERINO */
+
+
+Bit8u layout_keyboardsys[33196] = {
+0x4b, 0x43, 0x46, 0x00, 0x01, 0x01, 0x2e, 0x48, 0x65, 0x6e, 0x72, 0x69, 0x71, 0x75, 0x65, 0x20,
+0x50, 0x65, 0x72, 0x6f, 0x6e, 0xff, 0x44, 0x4f, 0x53, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x70, 0x61,
+0x67, 0x65, 0x73, 0x20, 0x2d, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x20, 0x23, 0x31,
+0x20, 0x6f, 0x66, 0x20, 0x33, 0xa0, 0x03, 0x0e, 0x00, 0x00, 0x42, 0x45, 0x2c, 0x78, 0x00, 0x42,
+0x45, 0x2c, 0x78, 0x00, 0x46, 0x52, 0x06, 0x04, 0x2c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x5a, 0x03, 0x0f, 0x03, 0x16, 0x03, 0x00, 0x00, 0x5b, 0x03, 0x5e, 0x01, 0x96, 0x01,
+0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, 0xb5, 0x01, 0x13, 0x02, 0x1e, 0x02,
+0x00, 0x00, 0x37, 0x75, 0x6d, 0x02, 0xba, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x42, 0x00, 0x26, 0x31, 0x7c,
+0x03, 0x42, 0x00, 0x82, 0x32, 0x40, 0x04, 0x42, 0x00, 0x22, 0x33, 0x23, 0x05, 0x41, 0x00, 0x27,
+0x34, 0x06, 0x41, 0x00, 0x28, 0x35, 0x07, 0x42, 0x00, 0xf5, 0x36, 0x5e, 0x08, 0x41, 0x00, 0x8a,
+0x37, 0x09, 0x41, 0x00, 0x21, 0x38, 0x0a, 0x42, 0x00, 0x87, 0x39, 0x7b, 0x0b, 0x42, 0x00, 0x85,
+0x30, 0x7d, 0x0c, 0x41, 0x00, 0x29, 0xf8, 0x0d, 0x44, 0x0c, 0x2d, 0x5f, 0x00, 0x00, 0x1f, 0x10,
+0xc5, 0x00, 0x61, 0x1e, 0x41, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x01, 0x1e, 0x00, 0x1e, 0x11, 0xc5,
+0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x12, 0x41, 0x00,
+0x65, 0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69,
+0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x42, 0x03, 0xca, 0xcc, 0x5b, 0x1b, 0x42, 0x00, 0x24,
+0x2a, 0x5d, 0x1e, 0xc5, 0x00, 0x71, 0x10, 0x51, 0x10, 0x00, 0x10, 0x00, 0x10, 0x11, 0x10, 0x00,
+0x10, 0x27, 0xc5, 0x00, 0x6d, 0x32, 0x4d, 0x32, 0x00, 0x32, 0x00, 0x32, 0x0d, 0x32, 0x00, 0x32,
+0x28, 0x43, 0x0c, 0x97, 0x25, 0xcb, 0xcb, 0x29, 0x41, 0x00, 0xfd, 0xfc, 0x2b, 0x43, 0x0c, 0xe6,
+0x9c, 0xc8, 0xc8, 0x2c, 0xc5, 0x00, 0x77, 0x11, 0x57, 0x11, 0x00, 0x11, 0x00, 0x11, 0x17, 0x11,
+0x00, 0x11, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32, 0x44, 0x08, 0x2c,
+0x3f, 0x5c, 0x00, 0x1c, 0x33, 0x42, 0x00, 0x3b, 0x2e, 0x3c, 0x34, 0x42, 0x00, 0x3a, 0x2f, 0x3e,
+0x35, 0x43, 0x0c, 0x3d, 0x2b, 0xc9, 0xc9, 0x39, 0x00, 0x00, 0x20, 0x56, 0x44, 0x08, 0x3c, 0x3e,
+0x5c, 0x00, 0x1c, 0x00, 0x10, 0xc5, 0x00, 0x61, 0x1e, 0x41, 0x1e, 0x91, 0x1e, 0x92, 0x1e, 0x01,
+0x1e, 0x00, 0x1e, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x18, 0x43, 0x03, 0x00, 0x00, 0xab, 0xac,
+0x1f, 0x42, 0x03, 0x00, 0x00, 0xe1, 0x2c, 0xc5, 0x00, 0x77, 0x11, 0x57, 0x11, 0xae, 0x11, 0x00,
+0x11, 0x17, 0x11, 0x00, 0x11, 0x2d, 0x42, 0x03, 0x00, 0x00, 0xaf, 0x00, 0x60, 0x0b, 0x61, 0x85,
+0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3,
+0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5,
+0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96,
+0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x27, 0x0f, 0x61, 0xa0,
+0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x43, 0x80,
+0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0x27, 0x22, 0x0d, 0x61, 0x84,
+0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8,
+0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x00, 0x07, 0x41, 0x02, 0x15, 0x00, 0x29, 0x41,
+0x03, 0x00, 0xa0, 0x00, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97,
+0x20, 0x60, 0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83, 0x65, 0x88,
+0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x27, 0x09, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82,
+0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x43, 0x80, 0x45, 0x90, 0x20, 0x27, 0x22, 0x0a, 0x61, 0x84,
+0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a,
+0x20, 0x22, 0x00, 0x05, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x10, 0xc5, 0x00, 0x61, 0x1e, 0x41, 0x1e,
+0x86, 0x1e, 0x8f, 0x1e, 0x01, 0x1e, 0x00, 0x1e, 0x11, 0xc5, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x91,
+0x2c, 0x92, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x12, 0x43, 0x03, 0x00, 0x00, 0xe4, 0xe5, 0x18, 0x43,
+0x03, 0x00, 0x00, 0xab, 0xac, 0x2b, 0x41, 0x03, 0x00, 0xa0, 0x2c, 0xc5, 0x00, 0x77, 0x11, 0x57,
+0x11, 0xae, 0x11, 0x00, 0x11, 0x17, 0x11, 0x00, 0x11, 0x2d, 0x42, 0x03, 0x00, 0x00, 0xaf, 0x00,
+0x60, 0x07, 0x61, 0x85, 0x65, 0x8a, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x55, 0xeb, 0x20, 0x60,
+0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c,
+0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e,
+0x27, 0x05, 0x63, 0x87, 0x65, 0x82, 0x43, 0x80, 0x45, 0x90, 0x20, 0x27, 0x22, 0x0b, 0x61, 0x84,
+0x65, 0x89, 0x69, 0x8b, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x55, 0x9a,
+0x59, 0xf3, 0x20, 0x22, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x60, 0x0b, 0x61, 0x85,
+0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3,
+0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5,
+0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96,
+0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0f, 0x61, 0xa0,
+0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x43, 0x80,
+0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84,
+0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8,
+0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00, 0xf7, 0xe7, 0x01, 0x09, 0x00, 0x00, 0x42, 0x52, 0x2c,
+0x13, 0x01, 0x42, 0x52, 0x04, 0x03, 0x2c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x5a, 0x03, 0x5d, 0x01, 0x66, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x66, 0x01, 0x00, 0x00,
+0xb5, 0x01, 0xf9, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x02, 0x02, 0x03, 0x00, 0x00, 0xfb, 0x03, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x04, 0x02, 0x03, 0x00,
+0x00, 0xfc, 0x05, 0x02, 0x03, 0x00, 0x00, 0x9c, 0x06, 0x02, 0x03, 0x00, 0x00, 0xbd, 0x07, 0x02,
+0x03, 0x00, 0xcc, 0xaa, 0x08, 0x02, 0x03, 0x00, 0x00, 0x2f, 0x09, 0x02, 0x03, 0x00, 0x00, 0x7c,
+0x0a, 0x02, 0x03, 0x00, 0x00, 0x3f, 0x0b, 0x02, 0x03, 0x00, 0x00, 0xf8, 0x0d, 0x02, 0x03, 0x00,
+0x00, 0xf5, 0x10, 0x42, 0x03, 0x00, 0x00, 0x2f, 0x11, 0x42, 0x03, 0x00, 0x00, 0x3f, 0x12, 0x42,
+0x00, 0x65, 0x45, 0xf8, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41,
+0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x01, 0x03, 0xcb, 0xc8, 0x1b, 0x03, 0x00,
+0x5b, 0x7b, 0xa6, 0x1b, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x41, 0x00, 0x87, 0x80, 0x28, 0x03,
+0x07, 0xc9, 0xca, 0x00, 0x1e, 0x29, 0x01, 0x00, 0x27, 0x22, 0x2b, 0x03, 0x00, 0x5d, 0x7d, 0xa7,
+0x1d, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x35, 0x03, 0x00, 0x3b, 0x3a, 0x5c, 0x1c, 0x39, 0x00, 0x00,
+0x20, 0x73, 0x02, 0x00, 0x2f, 0x3f, 0xf8, 0x7e, 0x01, 0x00, 0x2e, 0x2e, 0x00, 0x02, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00, 0x00, 0x9b, 0x0d,
+0x02, 0x03, 0x00, 0x00, 0x15, 0x00, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95,
+0x75, 0x97, 0x20, 0x60, 0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83,
+0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x27, 0x07, 0x61, 0xa0, 0x65, 0x82,
+0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x45, 0x90, 0x20, 0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89,
+0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22,
+0x00, 0x12, 0x44, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xd5, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a,
+0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb,
+0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5,
+0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6,
+0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82,
+0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0,
+0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94,
+0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9,
+0x00, 0xfa, 0x56, 0x01, 0x04, 0x12, 0x01, 0x42, 0x52, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xcf, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00,
+0x00, 0xd6, 0x00, 0x00, 0x00, 0xb5, 0x01, 0x79, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x03, 0x00, 0xca, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15,
+0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41,
+0x00, 0x6f, 0x4f, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x28, 0x02, 0x03, 0xcb, 0xcc, 0xf9, 0x29, 0x01,
+0x03, 0xc8, 0xc9, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x39, 0x00, 0x00,
+0x20, 0x00, 0x28, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x00, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69,
+0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60, 0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e,
+0x06, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x27, 0x09, 0x61,
+0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x43, 0x80, 0x45, 0x90, 0x20,
+0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41,
+0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x60,
+0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49,
+0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41,
+0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f,
+0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x27,
+0x0f, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41,
+0xb5, 0x43, 0x80, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0x27, 0x22,
+0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45,
+0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0xc6, 0x08, 0x04, 0x13, 0x00, 0x00,
+0x43, 0x46, 0x2c, 0x00, 0x00, 0x43, 0x41, 0x2c, 0x3a, 0x00, 0x43, 0x46, 0x2c, 0x3a, 0x00, 0x43,
+0x41, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0x2a,
+0x01, 0x31, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, 0x5f, 0x03, 0x70,
+0x03, 0xa7, 0x03, 0x00, 0x00, 0x5b, 0x03, 0xa0, 0x01, 0xd4, 0x01, 0x00, 0x00, 0x46, 0x75, 0x45,
+0x02, 0xaf, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03,
+0x00, 0x00, 0xf1, 0x03, 0x02, 0x01, 0x00, 0x22, 0x40, 0x04, 0x02, 0x01, 0x00, 0x2f, 0x9c, 0x05,
+0x02, 0x03, 0x00, 0x00, 0xbd, 0x06, 0x02, 0x03, 0x00, 0x00, 0xcf, 0x07, 0x02, 0x01, 0x00, 0x3f,
+0xaa, 0x08, 0x02, 0x03, 0x00, 0x00, 0xdd, 0x09, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x0a, 0x02, 0x03,
+0x00, 0x00, 0xfc, 0x0b, 0x02, 0x03, 0x00, 0x00, 0xac, 0x0c, 0x02, 0x03, 0x00, 0x00, 0xab, 0x0d,
+0x02, 0x03, 0x00, 0x00, 0xf3, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16,
+0x41, 0x00, 0x75, 0x55, 0x17, 0x42, 0x00, 0x69, 0x49, 0xfb, 0x18, 0x42, 0x00, 0x6f, 0x4f, 0xf5,
+0x19, 0x42, 0x03, 0x00, 0x00, 0xf4, 0x1a, 0x02, 0x03, 0xc9, 0xc9, 0x5b, 0x1b, 0x02, 0x03, 0xcc,
+0xcb, 0x5d, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x02, 0x03, 0x00, 0x00, 0x7e, 0x28, 0x02, 0x03,
+0xc8, 0xc8, 0x7b, 0x29, 0x04, 0x08, 0x23, 0x7c, 0x5c, 0x00, 0x1c, 0x2b, 0x02, 0x00, 0x3c, 0x3e,
+0x7d, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x2f, 0x42, 0x03, 0x00, 0x00, 0xae, 0x30, 0x42, 0x03, 0x00,
+0x00, 0xaf, 0x31, 0x42, 0x03, 0x00, 0x00, 0xf8, 0x32, 0x42, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x02,
+0x01, 0x00, 0x27, 0xee, 0x34, 0x02, 0x01, 0x00, 0x2e, 0x2d, 0x35, 0x42, 0x04, 0x82, 0x90, 0xca,
+0x39, 0x00, 0x00, 0x20, 0x56, 0x02, 0x00, 0xae, 0xaf, 0xf8, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00,
+0xd5, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7,
+0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88,
+0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea,
+0x20, 0x5e, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec,
+0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c,
+0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3,
+0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0xf7, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0xf7,
+0x00, 0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x1e,
+0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x24, 0x03, 0x03, 0x00, 0x00, 0xa4, 0xa5, 0x26, 0x03, 0x03,
+0x00, 0x00, 0xab, 0xac, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75,
+0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x5e, 0x0b, 0x61,
+0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f,
+0xe2, 0x55, 0xea, 0x20, 0x5e, 0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75,
+0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20,
+0x27, 0x22, 0x0d, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41,
+0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x2c, 0x03, 0x63,
+0x87, 0x43, 0x80, 0x20, 0x2c, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x0b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x0d, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x15, 0x03, 0x03, 0x00,
+0x00, 0xe7, 0xe8, 0x17, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x19, 0x03, 0x03, 0x00, 0x00, 0xab, 0xac,
+0x1e, 0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x26, 0x03, 0x03, 0x00, 0x00, 0xd0, 0xd1, 0x2b, 0x01,
+0x03, 0xce, 0xcf, 0x33, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x34, 0x02, 0x07, 0x00, 0x00, 0xcd, 0x00,
+0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4,
+0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c,
+0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e,
+0x27, 0x0b, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90,
+0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x20, 0x27, 0x22, 0x09, 0x65, 0x89, 0x69, 0x8b, 0x75, 0x81,
+0x79, 0x98, 0x45, 0xd3, 0x49, 0xd8, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x2c, 0x0d, 0x61, 0x84,
+0x65, 0x9e, 0x69, 0xa6, 0x6f, 0x94, 0x75, 0xa8, 0x41, 0x8e, 0x45, 0x9f, 0x49, 0xa7, 0x4f, 0x99,
+0x55, 0xa9, 0x63, 0x87, 0x43, 0x80, 0x20, 0x2c, 0x2d, 0x0b, 0x61, 0xc6, 0x65, 0xec, 0x69, 0xf4,
+0x6f, 0xe4, 0x6e, 0xa4, 0x41, 0xc7, 0x45, 0xed, 0x49, 0xf6, 0x4f, 0xe5, 0x4e, 0xa5, 0x20, 0x2d,
+0x3c, 0x0b, 0x61, 0xfa, 0x65, 0xf1, 0x69, 0xe1, 0x6f, 0xfc, 0x75, 0xf7, 0x41, 0xfb, 0x45, 0xf2,
+0x49, 0xee, 0x4f, 0xfd, 0x55, 0xef, 0x20, 0x3c, 0x3e, 0x0b, 0x61, 0x86, 0x65, 0xbd, 0x69, 0xf9,
+0x6f, 0x9b, 0x75, 0xb8, 0x41, 0x8f, 0x45, 0xbe, 0x49, 0xdd, 0x4f, 0x9d, 0x55, 0xad, 0x20, 0x3e,
+0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0x9b, 0x06, 0x02, 0x03, 0x00, 0x00, 0x98, 0x08, 0x02, 0x03,
+0x00, 0x00, 0xa0, 0x0a, 0x02, 0x03, 0x00, 0x00, 0xa6, 0x0d, 0x02, 0x03, 0x00, 0x00, 0xad, 0x17,
+0x42, 0x07, 0x00, 0x00, 0xa0, 0x18, 0x42, 0x03, 0x00, 0x00, 0x8f, 0x19, 0x42, 0x03, 0x00, 0x00,
+0x86, 0x33, 0x02, 0x03, 0x00, 0x00, 0xa7, 0x00, 0x60, 0x07, 0x61, 0x85, 0x65, 0x8a, 0x75, 0x97,
+0x41, 0x8e, 0x45, 0x91, 0x55, 0x9d, 0x20, 0x60, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c,
+0x6f, 0x93, 0x75, 0x96, 0x41, 0x84, 0x45, 0x92, 0x49, 0xa8, 0x4f, 0x99, 0x55, 0x9e, 0x20, 0x5e,
+0xa1, 0x05, 0x65, 0x82, 0x6f, 0xa2, 0x75, 0xa3, 0x45, 0x90, 0x20, 0xa1, 0xa4, 0x07, 0x65, 0x89,
+0x69, 0x8b, 0x75, 0x81, 0x45, 0x94, 0x49, 0x95, 0x55, 0x9a, 0x20, 0xa4, 0xa5, 0x03, 0x63, 0x87,
+0x43, 0x80, 0x20, 0xa5, 0x00, 0x9a, 0x44, 0x07, 0x09, 0xbd, 0x01, 0x43, 0x46, 0x2c, 0xbd, 0x01,
+0x43, 0x41, 0x0a, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03,
+0x1b, 0x01, 0xfc, 0x02, 0x00, 0x00, 0x5a, 0x03, 0x2a, 0x01, 0xfc, 0x02, 0x00, 0x00, 0x52, 0x03,
+0x0e, 0x02, 0xfc, 0x02, 0x00, 0x00, 0x52, 0x03, 0x17, 0x02, 0xfc, 0x02, 0x00, 0x00, 0x5f, 0x03,
+0x83, 0x03, 0x7b, 0x04, 0x00, 0x00, 0x5f, 0x03, 0xa0, 0x03, 0x7b, 0x04, 0x00, 0x00, 0x5b, 0x03,
+0xba, 0x05, 0xb1, 0x06, 0x00, 0x00, 0x5b, 0x03, 0xd0, 0x05, 0xb1, 0x06, 0x00, 0x00, 0x46, 0x75,
+0xc8, 0x04, 0xfd, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01,
+0x01, 0x00, 0x3f, 0x08, 0x02, 0x03, 0x00, 0x00, 0x7b, 0x09, 0x02, 0x03, 0x00, 0x00, 0x7d, 0x0a,
+0x04, 0x0b, 0x00, 0x00, 0x5b, 0x00, 0x1b, 0x0b, 0x04, 0x0b, 0x00, 0x00, 0x5d, 0x00, 0x1d, 0x0d,
+0x02, 0x03, 0x00, 0x00, 0xaa, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16,
+0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x04,
+0x0f, 0xca, 0xcc, 0xc8, 0x00, 0x1e, 0x1b, 0x42, 0x00, 0x87, 0x80, 0x7e, 0x1e, 0x41, 0x00, 0x61,
+0x41, 0x27, 0x02, 0x03, 0x00, 0x00, 0xf8, 0x28, 0x41, 0x00, 0x8a, 0xd4, 0x29, 0x04, 0x08, 0x2f,
+0x5c, 0x7c, 0x00, 0x1c, 0x2b, 0x41, 0x00, 0x85, 0xb7, 0x2c, 0x42, 0x03, 0x00, 0x00, 0xae, 0x2d,
+0x42, 0x03, 0x00, 0x00, 0xaf, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x33,
+0x02, 0x01, 0x00, 0x27, 0x3c, 0x34, 0x02, 0x01, 0x00, 0x22, 0x3e, 0x35, 0x41, 0x00, 0x82, 0x90,
+0x39, 0x00, 0x00, 0x20, 0x56, 0x44, 0x0c, 0x97, 0xeb, 0x00, 0x00, 0x1c, 0x00, 0x12, 0x42, 0x03,
+0x00, 0x00, 0xd5, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x02, 0x01, 0x00, 0xfb,
+0xad, 0x03, 0x01, 0x02, 0xfd, 0xa0, 0x04, 0x01, 0x00, 0xfc, 0x9c, 0x05, 0x01, 0x00, 0xac, 0xcf,
+0x06, 0x01, 0x02, 0xab, 0xa0, 0x07, 0x01, 0x02, 0xf3, 0xa0, 0x08, 0x10, 0x00, 0x00, 0x09, 0x10,
+0x00, 0x00, 0x0a, 0x01, 0x00, 0xf1, 0xf1, 0x0b, 0x10, 0x00, 0x00, 0x0c, 0x01, 0x00, 0xa8, 0xa8,
+0x0d, 0x01, 0x03, 0xce, 0xa0, 0x10, 0x10, 0x00, 0x00, 0x11, 0x10, 0x00, 0x00, 0x12, 0x10, 0x00,
+0x00, 0x13, 0x01, 0x00, 0xf4, 0xa9, 0x14, 0x10, 0x00, 0x00, 0x15, 0x01, 0x00, 0xbe, 0xbe, 0x16,
+0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x18, 0x41, 0x00, 0x9b, 0x9d, 0x19, 0x41, 0x00, 0xe7,
+0xe8, 0x1a, 0x01, 0x03, 0xa0, 0xcd, 0x1b, 0x01, 0x01, 0xc9, 0xee, 0x1e, 0x41, 0x00, 0x91, 0x92,
+0x1f, 0x01, 0x00, 0xe1, 0xf5, 0x20, 0x41, 0x00, 0xd0, 0xd1, 0x21, 0x01, 0x00, 0xa6, 0xa6, 0x22,
+0x10, 0x00, 0x00, 0x23, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, 0x00, 0x25, 0x10, 0x00, 0x00, 0x26,
+0x10, 0x00, 0x00, 0x27, 0x01, 0x03, 0xcb, 0xa0, 0x28, 0x10, 0x00, 0x00, 0x29, 0x01, 0x00, 0x2d,
+0xc4, 0x2b, 0x10, 0x00, 0x00, 0x2c, 0x10, 0x00, 0x00, 0x2d, 0x10, 0x00, 0x00, 0x2e, 0x01, 0x00,
+0xbd, 0xb8, 0x2f, 0x10, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x31, 0x10, 0x00, 0x00, 0x32, 0x01,
+0x00, 0xe6, 0xa7, 0x33, 0x01, 0x00, 0x9e, 0x9e, 0x34, 0x01, 0x00, 0xfa, 0xf6, 0x35, 0x01, 0x02,
+0x2f, 0xa0, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x01, 0x02, 0xdd, 0xa0, 0x00,
+0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x02, 0x01, 0x00, 0xfb, 0xad, 0x03, 0x01,
+0x02, 0xfd, 0xa0, 0x04, 0x01, 0x00, 0xfc, 0x9c, 0x05, 0x01, 0x00, 0xac, 0xcf, 0x06, 0x01, 0x02,
+0xab, 0xa0, 0x07, 0x01, 0x02, 0xf3, 0xa0, 0x08, 0x10, 0x00, 0x00, 0x09, 0x10, 0x00, 0x00, 0x0a,
+0x01, 0x00, 0xf1, 0xf1, 0x0b, 0x10, 0x00, 0x00, 0x0c, 0x01, 0x00, 0xa8, 0xa8, 0x0d, 0x01, 0x03,
+0xce, 0xa0, 0x10, 0x10, 0x00, 0x00, 0x11, 0x10, 0x00, 0x00, 0x12, 0x10, 0x00, 0x00, 0x13, 0x01,
+0x00, 0xf4, 0xa9, 0x14, 0x10, 0x00, 0x00, 0x15, 0x01, 0x00, 0xbe, 0xbe, 0x16, 0x10, 0x00, 0x00,
+0x17, 0x01, 0x01, 0xa0, 0xd5, 0x18, 0x41, 0x00, 0x9b, 0x9d, 0x19, 0x41, 0x00, 0xe7, 0xe8, 0x1a,
+0x01, 0x03, 0xa0, 0xcd, 0x1b, 0x01, 0x01, 0xc9, 0xee, 0x1e, 0x41, 0x00, 0x91, 0x92, 0x1f, 0x01,
+0x00, 0xe1, 0xf5, 0x20, 0x41, 0x00, 0xd0, 0xd1, 0x21, 0x01, 0x00, 0xa6, 0xa6, 0x22, 0x10, 0x00,
+0x00, 0x23, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, 0x00, 0x25, 0x10, 0x00, 0x00, 0x26, 0x10, 0x00,
+0x00, 0x27, 0x01, 0x03, 0xcb, 0xa0, 0x28, 0x10, 0x00, 0x00, 0x29, 0x01, 0x00, 0x2d, 0xc4, 0x2b,
+0x10, 0x00, 0x00, 0x2c, 0x10, 0x00, 0x00, 0x2d, 0x10, 0x00, 0x00, 0x2e, 0x01, 0x00, 0xbd, 0xb8,
+0x2f, 0x10, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x31, 0x10, 0x00, 0x00, 0x32, 0x01, 0x00, 0xe6,
+0xa7, 0x33, 0x01, 0x00, 0x9e, 0x9e, 0x34, 0x01, 0x00, 0xfa, 0xf6, 0x35, 0x01, 0x02, 0x2f, 0xa0,
+0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x56, 0x01, 0x02, 0xdd, 0xa0, 0x00, 0x60, 0x0b,
+0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde,
+0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7,
+0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93,
+0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0d,
+0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90,
+0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89,
+0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99,
+0x55, 0x9a, 0x20, 0xf9, 0xf8, 0x03, 0x61, 0x86, 0x41, 0x8f, 0x20, 0xf8, 0xf7, 0x03, 0x63, 0x87,
+0x43, 0x80, 0x20, 0xf7, 0x00, 0x1a, 0x01, 0x03, 0x00, 0xc9, 0x28, 0x41, 0x00, 0x8a, 0x91, 0x2a,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x2b, 0x41, 0x00, 0x85, 0x8e, 0x56, 0x41, 0x00, 0x97,
+0x9d, 0x00, 0x02, 0x10, 0x00, 0x00, 0x03, 0x01, 0x02, 0xfd, 0xa0, 0x04, 0x01, 0x00, 0xa6, 0x9c,
+0x05, 0x01, 0x00, 0xac, 0x98, 0x06, 0x01, 0x02, 0xab, 0xa0, 0x07, 0x01, 0x02, 0xad, 0xa0, 0x08,
+0x10, 0x00, 0x00, 0x09, 0x10, 0x00, 0x00, 0x0a, 0x01, 0x00, 0xf1, 0xf1, 0x0b, 0x10, 0x00, 0x00,
+0x0c, 0x10, 0x00, 0x00, 0x0d, 0x01, 0x03, 0xcc, 0xa0, 0x10, 0x10, 0x00, 0x00, 0x11, 0x10, 0x00,
+0x00, 0x12, 0x10, 0x00, 0x00, 0x13, 0x01, 0x02, 0x86, 0xa0, 0x14, 0x10, 0x00, 0x00, 0x15, 0x10,
+0x00, 0x00, 0x16, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x18, 0x10, 0x00, 0x00, 0x19, 0x10,
+0x00, 0x00, 0x1a, 0x01, 0x01, 0xa0, 0xf8, 0x1b, 0x01, 0x00, 0x7e, 0xa7, 0x1e, 0x10, 0x00, 0x00,
+0x1f, 0x01, 0x00, 0xe1, 0x8f, 0x20, 0x10, 0x00, 0x00, 0x21, 0x10, 0x00, 0x00, 0x22, 0x10, 0x00,
+0x00, 0x23, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, 0x00, 0x25, 0x10, 0x00, 0x00, 0x26, 0x10, 0x00,
+0x00, 0x27, 0x01, 0x03, 0xcb, 0xa0, 0x28, 0x10, 0x00, 0x00, 0x29, 0x01, 0x00, 0x2d, 0xc4, 0x2b,
+0x10, 0x00, 0x00, 0x2c, 0x10, 0x00, 0x00, 0x2d, 0x10, 0x00, 0x00, 0x2e, 0x01, 0x02, 0x9b, 0xa0,
+0x2f, 0x10, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x31, 0x10, 0x00, 0x00, 0x32, 0x01, 0x02, 0xe6,
+0xa0, 0x33, 0x10, 0x00, 0x00, 0x34, 0x01, 0x00, 0xfa, 0xf6, 0x35, 0x01, 0x02, 0x2f, 0xa0, 0x36,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x56, 0x01, 0x02, 0xa0, 0xa0, 0x00, 0x60, 0x07, 0x61,
+0x85, 0x65, 0x8a, 0x75, 0x97, 0x41, 0x8e, 0x45, 0x91, 0x55, 0x9d, 0x20, 0x60, 0xa4, 0x07, 0x65,
+0x89, 0x69, 0x8b, 0x75, 0x81, 0x45, 0x94, 0x49, 0x95, 0x55, 0x9a, 0x20, 0xa4, 0x5e, 0x0b, 0x61,
+0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0x84, 0x45, 0x92, 0x49, 0xa8, 0x4f,
+0x99, 0x55, 0x9e, 0x20, 0x5e, 0xa1, 0x05, 0x65, 0x82, 0x6f, 0xa2, 0x75, 0xa3, 0x45, 0x90, 0x20,
+0xa1, 0xa5, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0xa5, 0x00, 0x07, 0x03, 0x03, 0x00, 0x00, 0xe7,
+0xe8, 0x12, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x18, 0x03, 0x03, 0x00, 0x00, 0xab, 0xac, 0x1b, 0x02,
+0x07, 0x00, 0x00, 0xc9, 0x1e, 0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x26, 0x03, 0x03, 0x00, 0x00,
+0xd0, 0xd1, 0x33, 0x02, 0x07, 0x00, 0xcb, 0xcd, 0x34, 0x02, 0x07, 0x00, 0xce, 0xcf, 0x00, 0x60,
+0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49,
+0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x0b, 0x61, 0xc6, 0x65, 0xec, 0x69, 0xf4, 0x6f,
+0xe4, 0x6e, 0xa4, 0x41, 0xc7, 0x45, 0xed, 0x49, 0xf6, 0x4f, 0xe5, 0x4e, 0xa5, 0x20, 0x7e, 0x5e,
+0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49,
+0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x27, 0x0b, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f,
+0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x20, 0x27, 0x22,
+0x09, 0x65, 0x89, 0x69, 0x8b, 0x75, 0x81, 0x79, 0x98, 0x45, 0xd3, 0x49, 0xd8, 0x55, 0x9a, 0x59,
+0xf3, 0x20, 0x22, 0x3c, 0x0b, 0x61, 0xfa, 0x65, 0xf1, 0x69, 0xe1, 0x6f, 0xfc, 0x75, 0xf7, 0x41,
+0xfb, 0x45, 0xf2, 0x49, 0xee, 0x4f, 0xfd, 0x55, 0xef, 0x20, 0x3c, 0x22, 0x0b, 0x61, 0x84, 0x65,
+0x9e, 0x69, 0xa6, 0x6f, 0x94, 0x75, 0xa8, 0x41, 0x8e, 0x45, 0x9f, 0x49, 0xa7, 0x4f, 0x99, 0x55,
+0xa9, 0x20, 0x22, 0x3e, 0x0b, 0x61, 0x86, 0x65, 0xbd, 0x69, 0xf9, 0x6f, 0x9b, 0x75, 0xb8, 0x41,
+0x8f, 0x45, 0xbe, 0x49, 0xdd, 0x4f, 0x9d, 0x55, 0xad, 0x20, 0x3e, 0x00, 0x12, 0x42, 0x03, 0x00,
+0x00, 0xd5, 0x18, 0x43, 0x03, 0x00, 0x00, 0xab, 0xac, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x7f, 0x00, 0x02, 0x01, 0x00, 0xfb, 0xad, 0x03, 0x01, 0x02, 0xfd, 0xa0, 0x04, 0x01, 0x00, 0xfc,
+0x9c, 0x05, 0x01, 0x01, 0xa0, 0xcf, 0x06, 0x10, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x08, 0x10,
+0x00, 0x00, 0x09, 0x10, 0x00, 0x00, 0x0a, 0x01, 0x00, 0xf1, 0xf1, 0x0b, 0x10, 0x00, 0x00, 0x0c,
+0x01, 0x00, 0xa8, 0xa8, 0x0d, 0x01, 0x03, 0xce, 0xa0, 0x10, 0x10, 0x00, 0x00, 0x11, 0x10, 0x00,
+0x00, 0x12, 0x10, 0x00, 0x00, 0x13, 0x01, 0x00, 0xf4, 0xa9, 0x14, 0x10, 0x00, 0x00, 0x15, 0x01,
+0x00, 0xbe, 0xbe, 0x16, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x18, 0x41, 0x00, 0x9b, 0x9d,
+0x19, 0x41, 0x00, 0xe7, 0xe8, 0x1a, 0x01, 0x03, 0xa0, 0xcd, 0x1b, 0x01, 0x01, 0xc9, 0xee, 0x1e,
+0x41, 0x00, 0x91, 0x92, 0x1f, 0x01, 0x00, 0xe1, 0xf5, 0x20, 0x41, 0x00, 0xd0, 0xd1, 0x21, 0x01,
+0x00, 0xa6, 0xa6, 0x22, 0x10, 0x00, 0x00, 0x23, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, 0x00, 0x25,
+0x10, 0x00, 0x00, 0x26, 0x10, 0x00, 0x00, 0x27, 0x01, 0x03, 0xcb, 0xa0, 0x28, 0x10, 0x00, 0x00,
+0x29, 0x01, 0x00, 0x2d, 0xc4, 0x2b, 0x10, 0x00, 0x00, 0x2c, 0x10, 0x00, 0x00, 0x2d, 0x10, 0x00,
+0x00, 0x2e, 0x01, 0x00, 0xbd, 0xb8, 0x2f, 0x10, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x31, 0x10,
+0x00, 0x00, 0x32, 0x01, 0x00, 0xe6, 0xa7, 0x33, 0x01, 0x00, 0x9e, 0x9e, 0x34, 0x01, 0x00, 0xfa,
+0xf6, 0x35, 0x01, 0x02, 0x2f, 0xa0, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x56, 0x10,
+0x00, 0x00, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41,
+0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e,
+0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65,
+0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55,
+0xea, 0x20, 0x5e, 0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79,
+0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0x27, 0x22,
+0x0d, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45,
+0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0xf8, 0x03, 0x61, 0x86, 0x41,
+0x8f, 0x20, 0xf8, 0x2c, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0x2c, 0x00, 0x50, 0xaa, 0x04, 0x04,
+0xf5, 0x01, 0x43, 0x46, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x5a, 0x03, 0x58, 0x01, 0x5f, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00,
+0x5f, 0x03, 0xd8, 0x03, 0x58, 0x04, 0x00, 0x00, 0x5b, 0x03, 0xde, 0x01, 0xfe, 0x01, 0x00, 0x00,
+0x46, 0x75, 0x7f, 0x02, 0x17, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x02, 0x03, 0x03, 0x00, 0x00, 0xfb, 0xad, 0x03, 0x03, 0x01, 0x00, 0x22, 0x40, 0xfd, 0x04, 0x03,
+0x03, 0x00, 0x00, 0xfc, 0x9c, 0x05, 0x03, 0x03, 0x00, 0x00, 0xac, 0xcf, 0x06, 0x02, 0x03, 0x00,
+0x00, 0xab, 0x07, 0x02, 0x01, 0x00, 0x3f, 0xf3, 0x08, 0x02, 0x03, 0x00, 0x00, 0x7b, 0x09, 0x04,
+0x03, 0x00, 0x00, 0x5b, 0xf1, 0x1b, 0x0a, 0x04, 0x0b, 0x00, 0x00, 0x5d, 0x00, 0x1d, 0x0b, 0x02,
+0x03, 0x00, 0x00, 0x7d, 0x0c, 0x03, 0x03, 0x00, 0x00, 0x7c, 0xa8, 0x0d, 0x02, 0x07, 0x00, 0x00,
+0xcc, 0x12, 0x41, 0x00, 0x65, 0x45, 0x13, 0x43, 0x03, 0x00, 0x00, 0xf4, 0xa9, 0x15, 0x42, 0x00,
+0x79, 0x59, 0xbe, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x43, 0x00,
+0x6f, 0x4f, 0x9b, 0x9d, 0x19, 0x43, 0x03, 0x00, 0x00, 0xe7, 0xe8, 0x1a, 0x04, 0x0b, 0xca, 0xca,
+0xf8, 0x00, 0x1e, 0x1b, 0x43, 0x0c, 0x87, 0x80, 0xc9, 0xcd, 0x1e, 0x43, 0x00, 0x61, 0x41, 0x91,
+0x92, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xe1, 0xf5, 0x20, 0x43, 0x03, 0x00, 0x00, 0xd0, 0xd1, 0x21,
+0x42, 0x03, 0x00, 0x00, 0xa6, 0x27, 0x03, 0x0f, 0x00, 0x00, 0xcb, 0xcb, 0x28, 0x41, 0x00, 0x8a,
+0xd4, 0x29, 0x02, 0x00, 0xf8, 0xf8, 0xaa, 0x2b, 0x43, 0x0c, 0x85, 0xb7, 0xc8, 0xc8, 0x2c, 0x42,
+0x03, 0x00, 0x00, 0xae, 0x2d, 0x42, 0x03, 0x00, 0x00, 0xaf, 0x2e, 0x43, 0x00, 0x63, 0x43, 0xbd,
+0xb8, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32, 0x43, 0x03, 0x00, 0x00, 0xe6, 0xa7, 0x33, 0x02, 0x01,
+0x00, 0x27, 0x3c, 0x34, 0x02, 0x01, 0x00, 0x2e, 0x3e, 0x35, 0x42, 0x00, 0x82, 0x90, 0x2f, 0x39,
+0x00, 0x00, 0x20, 0x56, 0x44, 0x00, 0x97, 0xeb, 0x5c, 0x7c, 0x1c, 0x00, 0x12, 0x42, 0x03, 0x00,
+0x00, 0xd5, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41,
+0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e,
+0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65,
+0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55,
+0xea, 0x20, 0x5e, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79,
+0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf7,
+0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0xf7, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f,
+0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20,
+0xf9, 0x00, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x07, 0x02,
+0x05, 0x00, 0x3f, 0xa0, 0x12, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x17, 0x03, 0x03, 0x00, 0x00, 0xab,
+0xac, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7,
+0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4,
+0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88,
+0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea,
+0x20, 0x5e, 0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec,
+0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0x27, 0x2c, 0x03,
+0x63, 0x87, 0x43, 0x80, 0x20, 0x2c, 0x22, 0x0d, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94,
+0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3,
+0x20, 0x22, 0x00, 0x02, 0x03, 0x0f, 0x00, 0x00, 0xa0, 0xa0, 0x03, 0x03, 0x0f, 0x00, 0x00, 0x00,
+0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x07, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x03, 0x0f, 0x00, 0x00, 0x00, 0xa0,
+0x0c, 0x03, 0x0f, 0x00, 0x00, 0x00, 0xa0, 0x12, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x13, 0x03, 0x0f,
+0x00, 0x00, 0xa0, 0xa0, 0x14, 0x03, 0x03, 0x00, 0x00, 0xe7, 0xe8, 0x15, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x17, 0x03, 0x03, 0x00, 0x00, 0xab, 0xac, 0x18, 0x03, 0x0f, 0x00, 0x00, 0xa0, 0xa0, 0x19,
+0x03, 0x0f, 0x00, 0x00, 0xa0, 0xa0, 0x1f, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x20, 0x03, 0x0f, 0x00,
+0x00, 0xa0, 0xa0, 0x21, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x26, 0x03, 0x03, 0x00, 0x00, 0xd0, 0xd1,
+0x2e, 0x03, 0x0f, 0x00, 0x00, 0xa0, 0xa0, 0x32, 0x03, 0x0f, 0x00, 0x00, 0x00, 0xa0, 0x33, 0x02,
+0x07, 0x00, 0x00, 0xce, 0x34, 0x02, 0x07, 0x00, 0x00, 0xcf, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65,
+0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55,
+0xeb, 0x20, 0x60, 0x7e, 0x0b, 0x61, 0xc6, 0x65, 0xec, 0x69, 0xf4, 0x6f, 0xe4, 0x6e, 0xa4, 0x41,
+0xc7, 0x45, 0xed, 0x49, 0xf6, 0x4f, 0xe5, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65,
+0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55,
+0xea, 0x20, 0x5e, 0x27, 0x0b, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x41,
+0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x20, 0x27, 0x2c, 0x0d, 0x61, 0x84, 0x65,
+0x9e, 0x69, 0xa6, 0x6f, 0x94, 0x75, 0xa8, 0x41, 0x8e, 0x45, 0x9f, 0x49, 0xa7, 0x4f, 0x99, 0x55,
+0xa9, 0x63, 0x87, 0x43, 0x80, 0x20, 0x2c, 0x22, 0x09, 0x65, 0x89, 0x69, 0x8b, 0x75, 0x81, 0x79,
+0x98, 0x45, 0xd3, 0x49, 0xd8, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x3c, 0x0b, 0x61, 0xfa, 0x65,
+0xf1, 0x69, 0xe1, 0x6f, 0xfc, 0x75, 0xf7, 0x41, 0xfb, 0x45, 0xf2, 0x49, 0xee, 0x4f, 0xfd, 0x55,
+0xef, 0x20, 0x3c, 0x3e, 0x0b, 0x61, 0x86, 0x65, 0xbd, 0x69, 0xf9, 0x6f, 0x9b, 0x75, 0xb8, 0x41,
+0x8f, 0x45, 0xbe, 0x49, 0xdd, 0x4f, 0x9d, 0x55, 0xad, 0x20, 0x3e, 0x00, 0x02, 0x03, 0x0f, 0x00,
+0x00, 0xa0, 0xa0, 0x04, 0x02, 0x03, 0x00, 0x00, 0xa6, 0x05, 0x03, 0x07, 0x00, 0x00, 0x00, 0x98,
+0x07, 0x02, 0x03, 0x00, 0x00, 0xad, 0x0c, 0x03, 0x0f, 0x00, 0x00, 0x00, 0xa0, 0x13, 0x43, 0x0b,
+0x00, 0x00, 0x86, 0xa0, 0x15, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x18, 0x43, 0x0f, 0x00, 0x00, 0xa0,
+0xa0, 0x19, 0x43, 0x0f, 0x00, 0x00, 0xa0, 0xa0, 0x1b, 0x43, 0x0b, 0x00, 0x00, 0x7e, 0xc9, 0x1e,
+0x43, 0x0f, 0x00, 0x00, 0xa0, 0xa0, 0x1f, 0x43, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x20, 0x43, 0x0f,
+0x00, 0x00, 0xa0, 0xa0, 0x21, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x28, 0x41, 0x00, 0x8a, 0x91, 0x2b,
+0x41, 0x00, 0x85, 0x8e, 0x2e, 0x43, 0x0b, 0x00, 0x00, 0x9b, 0xa0, 0x32, 0x43, 0x0f, 0x00, 0x00,
+0x00, 0xa0, 0x39, 0x00, 0x00, 0x20, 0x56, 0x41, 0x00, 0x97, 0x9d, 0x00, 0x60, 0x07, 0x61, 0x85,
+0x65, 0x8a, 0x75, 0x97, 0x41, 0x8e, 0x45, 0x91, 0x55, 0x9d, 0x20, 0x60, 0xa4, 0x07, 0x65, 0x89,
+0x69, 0x8b, 0x75, 0x81, 0x45, 0x94, 0x49, 0x95, 0x55, 0x9a, 0x20, 0xa4, 0x5e, 0x0b, 0x61, 0x83,
+0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0x84, 0x45, 0x92, 0x49, 0xa8, 0x4f, 0x99,
+0x55, 0x9e, 0x20, 0x5e, 0xa1, 0x05, 0x65, 0x82, 0x6f, 0xa2, 0x75, 0xa3, 0x45, 0x90, 0x20, 0xa1,
+0xa5, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0xa5, 0x00, 0x8c, 0x0b, 0x03, 0x04, 0x00, 0x00, 0x43,
+0x5a, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x03, 0x66,
+0x01, 0xb6, 0x01, 0x00, 0x00, 0x5a, 0x03, 0x08, 0x01, 0x0f, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00,
+0x00, 0x0f, 0x01, 0x00, 0x00, 0x63, 0x03, 0x4b, 0x02, 0x99, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00,
+0x2b, 0x31, 0x21, 0x03, 0x02, 0x01, 0xa0, 0x32, 0x40, 0x04, 0x02, 0x01, 0xa0, 0x33, 0x23, 0x05,
+0x02, 0x01, 0xa0, 0x34, 0x24, 0x06, 0x02, 0x01, 0xa0, 0x35, 0x25, 0x07, 0x02, 0x01, 0xa0, 0x36,
+0x5e, 0x08, 0x02, 0x00, 0xec, 0x37, 0x26, 0x09, 0x02, 0x00, 0xa0, 0x38, 0x2a, 0x0a, 0x02, 0x00,
+0xa1, 0x39, 0x28, 0x0b, 0x02, 0x00, 0x82, 0x30, 0x29, 0x0c, 0x03, 0x00, 0x3d, 0x25, 0x2d, 0x5f,
+0x0d, 0x03, 0x03, 0xc8, 0xa0, 0x3d, 0x2b, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00, 0x79,
+0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f,
+0x1a, 0x03, 0x00, 0xa3, 0x2f, 0x5b, 0x7b, 0x1b, 0x03, 0x00, 0x29, 0x28, 0x5d, 0x7d, 0x1e, 0x41,
+0x00, 0x61, 0x41, 0x27, 0x03, 0x01, 0xa0, 0x22, 0x3b, 0x3a, 0x28, 0x03, 0x08, 0xf5, 0x21, 0xcf,
+0xcb, 0x29, 0x03, 0x02, 0x3b, 0xca, 0x60, 0x7e, 0x2b, 0x03, 0x01, 0xc9, 0x27, 0x5c, 0x7c, 0x2e,
+0x41, 0x00, 0x63, 0x43, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x33, 0x03, 0x01, 0x00, 0x3f, 0x3c, 0x9e,
+0x34, 0x03, 0x01, 0x00, 0x3a, 0x3e, 0xf6, 0x35, 0x03, 0x00, 0x2d, 0x5f, 0x2f, 0x3f, 0x39, 0x00,
+0x00, 0x20, 0x56, 0x02, 0x00, 0x26, 0x2a, 0xe1, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00,
+0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5,
+0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84,
+0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8,
+0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0xf8, 0x03, 0x61, 0x86, 0x41, 0x8f, 0x20, 0xf8, 0x5e, 0x0b,
+0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7,
+0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x00, 0x03, 0x00, 0x00, 0xd8, 0x04, 0x00, 0x00, 0xe7, 0x05,
+0x00, 0x00, 0x9f, 0x06, 0x00, 0x00, 0xfd, 0x07, 0x00, 0x00, 0xa7, 0x0d, 0x01, 0x03, 0x00, 0xcb,
+0x12, 0x42, 0x03, 0x00, 0x00, 0xaa, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41, 0x00, 0x74, 0x54,
+0x1f, 0x41, 0x00, 0x73, 0x53, 0x20, 0x41, 0x00, 0x64, 0x44, 0x26, 0x41, 0x00, 0x6c, 0x4c, 0x27,
+0x00, 0x00, 0x85, 0x28, 0x03, 0x0f, 0x00, 0x00, 0x00, 0xcc, 0x2c, 0x41, 0x00, 0x7a, 0x5a, 0x56,
+0x03, 0x0f, 0x00, 0x00, 0x00, 0xcd, 0x00, 0x27, 0x19, 0x61, 0xa0, 0x63, 0x86, 0x65, 0x82, 0x69,
+0xa1, 0x6c, 0x92, 0x6e, 0xe4, 0x6f, 0xa2, 0x72, 0xea, 0x73, 0x98, 0x75, 0xa3, 0x79, 0xec, 0x7a,
+0xab, 0x41, 0xb5, 0x43, 0x8f, 0x45, 0x90, 0x49, 0xd6, 0x4c, 0x91, 0x4e, 0xe3, 0x4f, 0xe0, 0x52,
+0xe8, 0x53, 0x97, 0x55, 0xe9, 0x59, 0xed, 0x5a, 0x8d, 0x20, 0xef, 0xf9, 0x09, 0x61, 0x84, 0x65,
+0x89, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x45, 0xd3, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0xf8,
+0x03, 0x75, 0x85, 0x55, 0xde, 0x20, 0xf8, 0xf3, 0x13, 0x63, 0x9f, 0x64, 0xd4, 0x65, 0xd8, 0x6c,
+0x96, 0x6e, 0xe5, 0x72, 0xfd, 0x73, 0xe7, 0x74, 0x9c, 0x7a, 0xa7, 0x43, 0xac, 0x44, 0xd2, 0x45,
+0xb7, 0x4c, 0x95, 0x4e, 0xd5, 0x52, 0xfc, 0x53, 0xe6, 0x54, 0x9b, 0x5a, 0xa6, 0x20, 0xf3, 0x5e,
+0x07, 0x61, 0x83, 0x69, 0x8c, 0x6f, 0x93, 0x41, 0xb6, 0x49, 0xd7, 0x4f, 0xe2, 0x20, 0x5e, 0xf1,
+0x05, 0x6f, 0x8b, 0x75, 0xfb, 0x4f, 0x8a, 0x55, 0xeb, 0x20, 0xf1, 0x00, 0x03, 0x00, 0x00, 0x88,
+0x04, 0x00, 0x00, 0xa8, 0x05, 0x00, 0x00, 0x87, 0x06, 0x00, 0x00, 0xa9, 0x07, 0x00, 0x00, 0x91,
+0x08, 0x00, 0x00, 0x98, 0x0d, 0x01, 0x03, 0x00, 0xcb, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41,
+0x00, 0x74, 0x54, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x20, 0x41, 0x00, 0x64, 0x44, 0x26, 0x41, 0x00,
+0x6c, 0x4c, 0x27, 0x00, 0x00, 0x96, 0x28, 0x03, 0x0e, 0xad, 0x00, 0xa0, 0xcc, 0x2c, 0x41, 0x00,
+0x7a, 0x5a, 0x33, 0x03, 0x0f, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x27, 0x11, 0x61, 0xa0, 0x65, 0x82,
+0x69, 0xa1, 0x6c, 0x8d, 0x6f, 0xa2, 0x72, 0xaa, 0x75, 0xa3, 0x79, 0x98, 0x41, 0x8f, 0x45, 0x90,
+0x49, 0x8b, 0x4c, 0x8a, 0x4f, 0x95, 0x52, 0xab, 0x55, 0x97, 0x59, 0x9d, 0x20, 0x27, 0x22, 0x07,
+0x61, 0x84, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0xf8, 0x03,
+0x75, 0x96, 0x55, 0xa6, 0x20, 0xf8, 0x76, 0x13, 0x63, 0x87, 0x64, 0x83, 0x65, 0x88, 0x6c, 0x8c,
+0x6e, 0xa4, 0x72, 0xa9, 0x73, 0xa8, 0x74, 0x9f, 0x7a, 0x91, 0x43, 0x80, 0x44, 0x85, 0x45, 0x89,
+0x4c, 0x9c, 0x4e, 0xa5, 0x52, 0x9e, 0x53, 0x9b, 0x54, 0x86, 0x5a, 0x92, 0x20, 0x76, 0x5e, 0x03,
+0x6f, 0x93, 0x4f, 0xa7, 0x20, 0x5e, 0x00, 0x19, 0xfa, 0x03, 0x04, 0xf3, 0x00, 0x43, 0x5a, 0x05,
+0x03, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x03, 0x4a, 0x01, 0xcc,
+0x01, 0x00, 0x00, 0x5a, 0x03, 0x8d, 0x02, 0x94, 0x02, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x94,
+0x02, 0x00, 0x00, 0x63, 0x03, 0x1b, 0x03, 0x88, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x2b, 0x31, 0xc8, 0x03, 0x01, 0x01, 0xa0, 0x32, 0x04, 0x03,
+0x05, 0xa0, 0x33, 0xc9, 0x1e, 0x05, 0x01, 0x01, 0xa0, 0x34, 0x06, 0x02, 0x05, 0xa0, 0x35, 0xca,
+0x07, 0x01, 0x01, 0xa0, 0x36, 0x08, 0x02, 0x04, 0xec, 0x37, 0xcb, 0x09, 0x02, 0x00, 0xa0, 0x38,
+0xfa, 0x0a, 0x02, 0x04, 0xa1, 0x39, 0xcc, 0x0b, 0x01, 0x00, 0x82, 0x30, 0x0c, 0x02, 0x04, 0x3d,
+0x25, 0xcd, 0x0d, 0x02, 0x07, 0xcc, 0xa0, 0xce, 0x10, 0x42, 0x03, 0x00, 0x00, 0x5c, 0x11, 0x42,
+0x03, 0x00, 0x00, 0x7c, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0xc4, 0x00, 0x7a, 0x2c, 0x5a, 0x2c,
+0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49,
+0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x02, 0x00, 0xa3, 0x2f, 0xf6, 0x1b, 0x02, 0x00, 0x29, 0x28,
+0x9e, 0x1e, 0x42, 0x00, 0x61, 0x41, 0x3c, 0x21, 0x42, 0x03, 0x00, 0x00, 0x5b, 0x22, 0x42, 0x03,
+0x00, 0x00, 0x5d, 0x27, 0x02, 0x01, 0xa0, 0x22, 0x24, 0x28, 0x02, 0x00, 0xf5, 0x21, 0xe1, 0x29,
+0x01, 0x02, 0x3b, 0xca, 0x2b, 0x02, 0x01, 0xcd, 0x27, 0xcf, 0x2c, 0xc4, 0x00, 0x79, 0x15, 0x59,
+0x15, 0x3e, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x42, 0x03, 0x00, 0x00, 0x23, 0x2e, 0x42, 0x00,
+0x63, 0x43, 0x26, 0x2f, 0x42, 0x03, 0x00, 0x00, 0x40, 0x30, 0x42, 0x03, 0x00, 0x00, 0x7b, 0x31,
+0x42, 0x00, 0x6e, 0x4e, 0x7d, 0x32, 0x42, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x01, 0x01, 0x00, 0x3f,
+0x34, 0x02, 0x01, 0x00, 0x3a, 0x2a, 0x35, 0x03, 0x04, 0x2d, 0x5f, 0x00, 0x1f, 0x39, 0x00, 0x00,
+0x20, 0x56, 0x03, 0x00, 0x26, 0x2a, 0x3c, 0x1c, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7e, 0x03,
+0x02, 0x06, 0xd8, 0x00, 0xc8, 0x04, 0x00, 0x00, 0xe7, 0x05, 0x02, 0x06, 0x9f, 0x00, 0xca, 0x06,
+0x02, 0x06, 0xfd, 0x00, 0xcb, 0x07, 0x02, 0x06, 0xa7, 0x00, 0xcc, 0x08, 0x02, 0x03, 0x00, 0x00,
+0x60, 0x09, 0x02, 0x07, 0x00, 0x00, 0xcd, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xce, 0x0b, 0x02, 0x07,
+0x00, 0x00, 0xcf, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xd0, 0x0d, 0x02, 0x07, 0xce, 0xc8, 0xd1, 0x12,
+0x42, 0x03, 0x00, 0x00, 0xaa, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41, 0x00, 0x74, 0x54, 0x1f,
+0x42, 0x00, 0x73, 0x53, 0xd0, 0x20, 0x42, 0x00, 0x64, 0x44, 0xd1, 0x25, 0x42, 0x03, 0x00, 0x00,
+0x88, 0x26, 0x42, 0x00, 0x6c, 0x4c, 0x9d, 0x27, 0x00, 0x00, 0x85, 0x29, 0x01, 0x03, 0x00, 0xcb,
+0x2b, 0x00, 0x01, 0xd0, 0x32, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x00, 0xf3, 0x13, 0x63, 0x9f, 0x64,
+0xd4, 0x65, 0xd8, 0x6c, 0x96, 0x6e, 0xe5, 0x72, 0xfd, 0x73, 0xe7, 0x74, 0x9c, 0x7a, 0xa7, 0x43,
+0xac, 0x44, 0xd2, 0x45, 0xb7, 0x4c, 0x95, 0x4e, 0xd5, 0x52, 0xfc, 0x53, 0xe6, 0x54, 0x9b, 0x5a,
+0xa6, 0x20, 0xf3, 0x5e, 0x07, 0x61, 0x83, 0x69, 0x8c, 0x6f, 0x93, 0x41, 0xb6, 0x49, 0xd7, 0x4f,
+0xe2, 0x20, 0x5e, 0xf4, 0x03, 0x61, 0xc7, 0x41, 0xc6, 0x20, 0xf4, 0xf8, 0x03, 0x75, 0x85, 0x55,
+0xde, 0x20, 0xf8, 0xf2, 0x05, 0x61, 0xa5, 0x65, 0xa9, 0x41, 0xa4, 0x45, 0xa8, 0x20, 0xf2, 0xfa,
+0x03, 0x7a, 0xbe, 0x5a, 0xbd, 0x20, 0xfa, 0x27, 0x19, 0x61, 0xa0, 0x63, 0x86, 0x65, 0x82, 0x69,
+0xa1, 0x6c, 0x92, 0x6e, 0xe4, 0x6f, 0xa2, 0x72, 0xea, 0x73, 0x98, 0x75, 0xa3, 0x79, 0xec, 0x7a,
+0xab, 0x41, 0xb5, 0x43, 0x8f, 0x45, 0x90, 0x49, 0xd6, 0x4c, 0x91, 0x4e, 0xe3, 0x4f, 0xe0, 0x52,
+0xe8, 0x53, 0x97, 0x55, 0xe9, 0x59, 0xed, 0x5a, 0x8d, 0x20, 0xef, 0xf1, 0x05, 0x6f, 0x8b, 0x75,
+0xfb, 0x4f, 0x8a, 0x55, 0xeb, 0x20, 0xf1, 0xf9, 0x09, 0x61, 0x84, 0x65, 0x89, 0x6f, 0x94, 0x75,
+0x81, 0x41, 0x8e, 0x45, 0xd3, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0xf7, 0x07, 0x63, 0x87, 0x73,
+0xad, 0x74, 0xee, 0x43, 0x80, 0x53, 0xb8, 0x54, 0xdd, 0x20, 0xf7, 0x00, 0x12, 0x42, 0x03, 0x00,
+0x00, 0xd5, 0x00, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f,
+0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41,
+0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xf8, 0x03, 0x61, 0x86, 0x41,
+0x8f, 0x20, 0xf8, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41,
+0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0xef, 0x0d, 0x61, 0xa0, 0x65,
+0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f,
+0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f,
+0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20,
+0xf9, 0xf7, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0xf7, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7e,
+0x03, 0x02, 0x06, 0x88, 0x00, 0xc8, 0x04, 0x00, 0x00, 0xa8, 0x05, 0x00, 0x00, 0x87, 0x06, 0x00,
+0x00, 0xa9, 0x07, 0x00, 0x00, 0x91, 0x08, 0x02, 0x02, 0x98, 0x00, 0x60, 0x09, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xcb, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xcc, 0x0d, 0x02,
+0x07, 0xcb, 0xc8, 0xa0, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41, 0x00, 0x74, 0x54, 0x1b, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x20, 0x41, 0x00, 0x64, 0x44, 0x26, 0x41,
+0x00, 0x6c, 0x4c, 0x27, 0x00, 0x00, 0x96, 0x28, 0x00, 0x00, 0xad, 0x29, 0x01, 0x03, 0x00, 0xca,
+0x2b, 0x02, 0x07, 0xcc, 0x00, 0xa0, 0x00, 0x76, 0x13, 0x63, 0x87, 0x64, 0x83, 0x65, 0x88, 0x6c,
+0x8c, 0x6e, 0xa4, 0x72, 0xa9, 0x73, 0xa8, 0x74, 0x9f, 0x7a, 0x91, 0x43, 0x80, 0x44, 0x85, 0x45,
+0x89, 0x4c, 0x9c, 0x4e, 0xa5, 0x52, 0x9e, 0x53, 0x9b, 0x54, 0x86, 0x5a, 0x92, 0x20, 0x76, 0x5e,
+0x03, 0x6f, 0x93, 0x4f, 0xa7, 0x20, 0x5e, 0xf8, 0x03, 0x75, 0x96, 0x55, 0xa6, 0x20, 0xf8, 0x27,
+0x11, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6c, 0x8d, 0x6f, 0xa2, 0x72, 0xaa, 0x75, 0xa3, 0x79,
+0x98, 0x41, 0x8f, 0x45, 0x90, 0x49, 0x8b, 0x4c, 0x8a, 0x4f, 0x95, 0x52, 0xab, 0x55, 0x97, 0x59,
+0x9d, 0x20, 0x27, 0x22, 0x07, 0x61, 0x84, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x4f, 0x99, 0x55,
+0x9a, 0x20, 0x22, 0x00, 0xe5, 0x77, 0x02, 0x09, 0x00, 0x00, 0x44, 0x4b, 0x2c, 0x9f, 0x00, 0x44,
+0x4b, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xfd,
+0x00, 0x0a, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x61, 0x03, 0x85,
+0x01, 0x90, 0x01, 0x00, 0x00, 0x34, 0x75, 0xdf, 0x01, 0x18, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x22, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00,
+0x9c, 0x05, 0x02, 0x01, 0x00, 0xcf, 0x24, 0x07, 0x01, 0x01, 0x00, 0x26, 0x08, 0x02, 0x01, 0x00,
+0x2f, 0x7b, 0x09, 0x03, 0x01, 0x00, 0x28, 0x5b, 0x1b, 0x0a, 0x03, 0x01, 0x00, 0x29, 0x5d, 0x1d,
+0x0b, 0x02, 0x01, 0x00, 0x3d, 0x7d, 0x0c, 0x01, 0x00, 0x2b, 0x3f, 0x0d, 0x02, 0x03, 0xcb, 0xc8,
+0x7c, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55,
+0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x41, 0x00, 0x86, 0x8f, 0x1b,
+0x03, 0x07, 0xcc, 0xca, 0xc9, 0x1e, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x41, 0x00, 0x91, 0x92,
+0x28, 0x41, 0x00, 0x9b, 0x9d, 0x29, 0x01, 0x00, 0xab, 0xf5, 0x2b, 0x01, 0x00, 0x27, 0x2a, 0x2e,
+0x41, 0x00, 0x63, 0x43, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32, 0x42, 0x03, 0x00, 0x00, 0xe6, 0x33,
+0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34, 0x02, 0x01, 0x00, 0x3a, 0x3e, 0x35, 0x03, 0x00, 0x2d, 0x5f,
+0x5c, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56, 0x03, 0x00, 0x3c, 0x3e, 0x5c, 0x1c, 0x00, 0x06, 0x02,
+0x03, 0x00, 0x00, 0xd5, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65,
+0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55,
+0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f,
+0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41,
+0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0f, 0x61, 0xa0, 0x63,
+0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x43, 0x80, 0x45,
+0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65,
+0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f,
+0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00, 0x05, 0x01, 0x01, 0x00, 0xaf, 0x29, 0x01, 0x01, 0x00, 0x15,
+0x00, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60, 0x7e,
+0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f,
+0x93, 0x75, 0x96, 0x20, 0x5e, 0x27, 0x09, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f,
+0xa2, 0x75, 0xa3, 0x43, 0x80, 0x45, 0x90, 0x20, 0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89, 0x69,
+0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00,
+0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x0d, 0x01, 0x03, 0xca, 0xa0, 0x12, 0x02, 0x03, 0x00, 0x00,
+0xd5, 0x1b, 0x03, 0x07, 0xcb, 0xc9, 0xc8, 0x1e, 0x1f, 0x02, 0x03, 0x00, 0x00, 0xe1, 0x20, 0x04,
+0x0b, 0x00, 0x00, 0xd0, 0x00, 0xd1, 0x25, 0x02, 0x03, 0x00, 0x00, 0xf2, 0x2c, 0x02, 0x03, 0x00,
+0x00, 0xae, 0x2d, 0x02, 0x03, 0x00, 0x00, 0xaf, 0x00, 0x7e, 0x07, 0x61, 0xc6, 0x69, 0x8d, 0x75,
+0x97, 0x41, 0xc7, 0x49, 0xde, 0x55, 0xeb, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69,
+0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20,
+0x5e, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41,
+0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x07, 0x61,
+0x84, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00, 0x8d, 0x20,
+0x02, 0x17, 0x00, 0x00, 0x44, 0x56, 0x2c, 0x00, 0x00, 0x55, 0x53, 0x44, 0x56, 0x2c, 0x67, 0x00,
+0x44, 0x56, 0x2c, 0x67, 0x00, 0x55, 0x53, 0x44, 0x56, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xfa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x84, 0x00, 0x5b, 0x1a, 0x7b, 0x1a, 0x1b, 0x1a, 0x00, 0x1a,
+0x00, 0x1a, 0x0d, 0x84, 0x00, 0x5d, 0x1b, 0x7d, 0x1b, 0x1d, 0x1b, 0x00, 0x1b, 0x00, 0x1b, 0x10,
+0x84, 0x00, 0x27, 0x28, 0x22, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x11, 0x84, 0x00, 0x2c,
+0x33, 0x3c, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x12, 0x84, 0x00, 0x2e, 0x34, 0x3e, 0x34,
+0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x13, 0xc4, 0x00, 0x70, 0x19, 0x50, 0x19, 0x10, 0x19, 0x00,
+0x19, 0x00, 0x19, 0x14, 0xc4, 0x00, 0x79, 0x15, 0x59, 0x15, 0x19, 0x15, 0x00, 0x15, 0x00, 0x15,
+0x15, 0xc4, 0x00, 0x66, 0x21, 0x46, 0x21, 0x06, 0x21, 0x00, 0x21, 0x00, 0x21, 0x16, 0xc4, 0x00,
+0x67, 0x22, 0x47, 0x22, 0x07, 0x22, 0x00, 0x22, 0x00, 0x22, 0x17, 0xc4, 0x00, 0x63, 0x2e, 0x43,
+0x2e, 0x03, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x18, 0xc4, 0x00, 0x72, 0x13, 0x52, 0x13, 0x12, 0x13,
+0x00, 0x13, 0x00, 0x13, 0x19, 0xc4, 0x00, 0x6c, 0x26, 0x4c, 0x26, 0x0c, 0x26, 0x00, 0x26, 0x00,
+0x26, 0x1a, 0x84, 0x00, 0x2f, 0x35, 0x3f, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x1b, 0x84,
+0x00, 0x3d, 0x0d, 0x2b, 0x0d, 0x00, 0x0d, 0x00, 0x83, 0x00, 0x83, 0x1f, 0xc4, 0x00, 0x6f, 0x18,
+0x4f, 0x18, 0x0f, 0x18, 0x00, 0x18, 0x00, 0x18, 0x20, 0xc4, 0x00, 0x65, 0x12, 0x45, 0x12, 0x05,
+0x12, 0x00, 0x12, 0x00, 0x12, 0x21, 0xc4, 0x00, 0x75, 0x16, 0x55, 0x16, 0x15, 0x16, 0x00, 0x16,
+0x00, 0x16, 0x22, 0xc4, 0x00, 0x69, 0x17, 0x49, 0x17, 0x09, 0x17, 0x00, 0x17, 0x00, 0x17, 0x23,
+0xc4, 0x00, 0x64, 0x20, 0x44, 0x20, 0x04, 0x20, 0x00, 0x20, 0x00, 0x20, 0x24, 0xc4, 0x00, 0x68,
+0x23, 0x48, 0x23, 0x08, 0x23, 0x00, 0x23, 0x00, 0x23, 0x25, 0xc4, 0x00, 0x74, 0x14, 0x54, 0x14,
+0x14, 0x14, 0x00, 0x14, 0x00, 0x14, 0x26, 0xc4, 0x00, 0x6e, 0x31, 0x4e, 0x31, 0x0e, 0x31, 0x00,
+0x31, 0x00, 0x31, 0x27, 0xc4, 0x00, 0x73, 0x1f, 0x53, 0x1f, 0x13, 0x1f, 0x00, 0x1f, 0x00, 0x1f,
+0x28, 0x84, 0x00, 0x2d, 0x0c, 0x5f, 0x0c, 0x1f, 0x0c, 0x00, 0x82, 0x00, 0x82, 0x2c, 0x84, 0x00,
+0x3b, 0x27, 0x3a, 0x27, 0x00, 0x27, 0x00, 0x27, 0x00, 0x27, 0x2d, 0xc4, 0x00, 0x71, 0x10, 0x51,
+0x10, 0x11, 0x10, 0x00, 0x10, 0x00, 0x10, 0x2e, 0xc4, 0x00, 0x6a, 0x24, 0x4a, 0x24, 0x0a, 0x24,
+0x00, 0x24, 0x00, 0x24, 0x2f, 0xc4, 0x00, 0x6b, 0x25, 0x4b, 0x25, 0x0b, 0x25, 0x00, 0x25, 0x00,
+0x25, 0x30, 0xc4, 0x00, 0x78, 0x2d, 0x58, 0x2d, 0x18, 0x2d, 0x00, 0x2d, 0x00, 0x2d, 0x31, 0xc4,
+0x00, 0x62, 0x30, 0x42, 0x30, 0x02, 0x30, 0x00, 0x30, 0x00, 0x30, 0x33, 0xc4, 0x00, 0x77, 0x11,
+0x57, 0x11, 0x17, 0x11, 0x00, 0x11, 0x00, 0x11, 0x34, 0xc4, 0x00, 0x76, 0x2f, 0x56, 0x2f, 0x16,
+0x2f, 0x00, 0x2f, 0x00, 0x2f, 0x35, 0xc4, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x1a, 0x2c, 0x00, 0x2c,
+0x00, 0x2c, 0x00, 0x20, 0xc4, 0x00, 0x65, 0x12, 0x45, 0x12, 0x05, 0x12, 0xd5, 0x12, 0x00, 0x12,
+0x00, 0x9d, 0xdb, 0x02, 0x09, 0x00, 0x00, 0x46, 0x52, 0x2c, 0xbd, 0x00, 0x46, 0x52, 0x06, 0x04,
+0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xa0, 0x01, 0xa7, 0x01,
+0x00, 0x00, 0x5b, 0x03, 0x02, 0x02, 0x3f, 0x02, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0xa7, 0x01,
+0x00, 0x00, 0xb5, 0x01, 0x54, 0x01, 0x65, 0x01, 0x00, 0x00, 0x45, 0x75, 0x9c, 0x02, 0xb8, 0x02,
+0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x02, 0x41, 0x00, 0x26, 0x31, 0x03, 0x42, 0x04, 0x82, 0x32, 0xc9, 0x04, 0x42, 0x00,
+0x22, 0x33, 0x23, 0x05, 0x42, 0x00, 0x27, 0x34, 0x7b, 0x06, 0x43, 0x00, 0x28, 0x35, 0x5b, 0x1b,
+0x07, 0x43, 0x00, 0x2d, 0x36, 0x7c, 0x1f, 0x08, 0x42, 0x04, 0x8a, 0x37, 0xc8, 0x09, 0x43, 0x00,
+0x5f, 0x38, 0x5c, 0x1c, 0x0a, 0x42, 0x00, 0x87, 0x39, 0x5e, 0x0b, 0x42, 0x00, 0x85, 0x30, 0x40,
+0x0c, 0x43, 0x00, 0x29, 0xf8, 0x5d, 0x1d, 0x0d, 0x42, 0x03, 0x00, 0x00, 0x7d, 0x10, 0xc5, 0x00,
+0x61, 0x1e, 0x41, 0x1e, 0x00, 0x1e, 0x01, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x11, 0xc5, 0x00, 0x7a,
+0x2c, 0x5a, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x12, 0x41, 0x00, 0x65, 0x45,
+0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18,
+0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x43, 0x07, 0xca, 0xcb, 0x00, 0x1e, 0x1b, 0x42, 0x00, 0x24, 0x9c,
+0xcf, 0x1e, 0xc5, 0x00, 0x71, 0x10, 0x51, 0x10, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x00, 0x10,
+0x27, 0xc5, 0x00, 0x6d, 0x32, 0x4d, 0x32, 0x00, 0x32, 0x0d, 0x32, 0x00, 0x32, 0x00, 0x32, 0x28,
+0x41, 0x00, 0x97, 0x25, 0x29, 0x41, 0x00, 0xfd, 0xfc, 0x2b, 0x41, 0x00, 0x2a, 0xe6, 0x2c, 0xc5,
+0x00, 0x77, 0x11, 0x57, 0x11, 0x00, 0x11, 0x17, 0x11, 0x00, 0x11, 0x00, 0x11, 0x31, 0x41, 0x00,
+0x6e, 0x4e, 0x32, 0x41, 0x00, 0x2c, 0x3f, 0x33, 0x42, 0x00, 0x3b, 0x2e, 0x3c, 0x34, 0x42, 0x00,
+0x3a, 0x2f, 0x3e, 0x35, 0x41, 0x00, 0x21, 0xf5, 0x39, 0x00, 0x00, 0x20, 0x56, 0x41, 0x00, 0x3c,
+0x3e, 0x00, 0x1b, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x29, 0x41, 0x03, 0x00, 0xa0, 0x35, 0x41, 0x01,
+0x00, 0x15, 0x00, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20,
+0x60, 0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83, 0x65, 0x88, 0x69,
+0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f,
+0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0x12, 0x42,
+0x03, 0x00, 0x00, 0xd5, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75,
+0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61,
+0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61,
+0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f,
+0xe2, 0x55, 0xea, 0x20, 0x5e, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75,
+0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00,
+0x10, 0xc5, 0x00, 0x61, 0x1e, 0x41, 0x1e, 0x91, 0x1e, 0x01, 0x1e, 0x92, 0x1e, 0x00, 0x1e, 0x12,
+0x44, 0x0b, 0x00, 0x00, 0xd5, 0x00, 0x90, 0x18, 0x44, 0x0b, 0x00, 0x00, 0xab, 0x00, 0xac, 0x2c,
+0xc5, 0x00, 0x77, 0x11, 0x57, 0x11, 0xae, 0x11, 0x17, 0x11, 0x00, 0x11, 0x00, 0x11, 0x2d, 0x42,
+0x03, 0x00, 0x00, 0xaf, 0x2e, 0x44, 0x0b, 0x00, 0x00, 0x87, 0x00, 0x80, 0x00, 0x60, 0x0b, 0x61,
+0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f,
+0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e,
+0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75,
+0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x22, 0x0d, 0x61,
+0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49,
+0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x00, 0x03, 0x42, 0x07, 0x00, 0x00, 0xa0,
+0x08, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x1a, 0x41, 0x03, 0xc8,
+0xa0, 0x29, 0x10, 0x00, 0x00, 0x00, 0x5e, 0x0b, 0x61, 0xee, 0x65, 0xd0, 0x69, 0xf6, 0x6f, 0xfa,
+0x75, 0xfc, 0x41, 0xef, 0x45, 0xd1, 0x49, 0xf7, 0x4f, 0xfb, 0x55, 0xfd, 0x20, 0x5e, 0x00, 0xff,
+0x3c, 0x05, 0x13, 0x00, 0x00, 0x47, 0x52, 0x2c, 0x00, 0x00, 0x44, 0x45, 0x2c, 0x81, 0x00, 0x47,
+0x52, 0x2c, 0x81, 0x00, 0x44, 0x45, 0x09, 0x04, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x5a, 0x03, 0x4f, 0x01, 0x56, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x56, 0x01,
+0x00, 0x00, 0x5b, 0x03, 0xa7, 0x01, 0xeb, 0x01, 0x00, 0x00, 0xb5, 0x01, 0x40, 0x02, 0x47, 0x02,
+0x00, 0x00, 0x54, 0x03, 0x78, 0x02, 0xfc, 0x02, 0x00, 0x00, 0x55, 0x03, 0x5d, 0x03, 0x7a, 0x03,
+0x00, 0x00, 0x39, 0x75, 0x97, 0x03, 0x03, 0x04, 0x00, 0x00, 0x44, 0x75, 0x58, 0x04, 0x93, 0x04,
+0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7c, 0x03, 0x02, 0x01, 0x00, 0x22, 0xfd, 0x04, 0x02,
+0x01, 0x00, 0xf5, 0xfc, 0x07, 0x01, 0x01, 0x00, 0x26, 0x08, 0x02, 0x01, 0x00, 0x2f, 0x7b, 0x09,
+0x04, 0x09, 0x00, 0x28, 0x5b, 0x00, 0x1b, 0x0a, 0x04, 0x09, 0x00, 0x29, 0x5d, 0x00, 0x1d, 0x0b,
+0x02, 0x01, 0x00, 0x3d, 0x7d, 0x0c, 0x04, 0x08, 0xe1, 0x3f, 0x5c, 0x00, 0x1c, 0x0d, 0x01, 0x03,
+0xca, 0xc8, 0x10, 0x42, 0x03, 0x00, 0x00, 0x40, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0xc5, 0x00,
+0x7a, 0x2c, 0x5a, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0x75,
+0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x41, 0x00, 0x81, 0x9a,
+0x1b, 0x02, 0x00, 0x2b, 0x2a, 0x7e, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x41, 0x00, 0x94, 0x99,
+0x28, 0x41, 0x00, 0x84, 0x8e, 0x29, 0x04, 0x0d, 0xc9, 0xf8, 0x00, 0x00, 0x1e, 0x2b, 0x01, 0x00,
+0x23, 0x27, 0x2c, 0xc5, 0x00, 0x79, 0x15, 0x59, 0x15, 0x3c, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00,
+0x15, 0x2d, 0x42, 0x03, 0x00, 0x00, 0x3e, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x42, 0x03, 0x00,
+0x00, 0x23, 0x32, 0x42, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x01, 0x01, 0x00, 0x3b, 0x34, 0x01, 0x01,
+0x00, 0x3a, 0x35, 0x04, 0x0c, 0x2d, 0x5f, 0x00, 0x00, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56, 0x02,
+0x00, 0x3c, 0x3e, 0x7c, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x60, 0x0b, 0x61, 0x85,
+0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3,
+0x55, 0xeb, 0x20, 0x60, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96,
+0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0f, 0x61, 0xa0,
+0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x43, 0x80,
+0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0x00, 0x03, 0x01, 0x03,
+0x00, 0xcb, 0x09, 0x03, 0x07, 0x00, 0x00, 0x00, 0xae, 0x0a, 0x03, 0x07, 0x00, 0x00, 0x00, 0xaf,
+0x12, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x18, 0x03, 0x03, 0x00, 0x00, 0xab, 0xac, 0x1e, 0x03, 0x03,
+0x00, 0x00, 0x86, 0x8f, 0x27, 0x03, 0x03, 0x00, 0x00, 0x9b, 0x9d, 0x28, 0x03, 0x03, 0x00, 0x00,
+0x91, 0x92, 0x2e, 0x03, 0x03, 0x00, 0x00, 0x87, 0x80, 0x31, 0x03, 0x03, 0x00, 0x00, 0xa4, 0xa5,
+0x00, 0x60, 0x07, 0x61, 0x85, 0x65, 0x8a, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x55, 0xeb, 0x20,
+0x60, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45,
+0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x27, 0x07, 0x61, 0xa0, 0x65, 0x82, 0x6f,
+0xa2, 0x41, 0xb5, 0x45, 0x90, 0x4f, 0xe0, 0x20, 0x27, 0x22, 0x0d, 0x61, 0x84, 0x65, 0x89, 0x69,
+0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55,
+0x9a, 0x59, 0xf3, 0x20, 0x22, 0x00, 0x04, 0x02, 0x05, 0x00, 0x15, 0xa0, 0x00, 0x60, 0x06, 0x61,
+0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60, 0x5e, 0x06, 0x61, 0x83, 0x65,
+0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x27, 0x09, 0x61, 0xa0, 0x63, 0x87, 0x65,
+0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x43, 0x80, 0x45, 0x90, 0x20, 0x27, 0x00, 0x03, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x02, 0x03, 0x00, 0x00, 0xaa,
+0x09, 0x03, 0x07, 0x00, 0x00, 0x00, 0xae, 0x0a, 0x03, 0x07, 0x00, 0x00, 0x00, 0xaf, 0x0d, 0x01,
+0x03, 0xc8, 0xa0, 0x12, 0x03, 0x03, 0x00, 0x00, 0xa9, 0xa8, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14,
+0x41, 0x00, 0x74, 0x54, 0x15, 0xc5, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0xbe, 0x2c, 0xbd, 0x2c, 0x1a,
+0x2c, 0x00, 0x2c, 0x16, 0x03, 0x03, 0x00, 0x00, 0xfb, 0xeb, 0x17, 0x03, 0x03, 0x00, 0x00, 0x85,
+0xde, 0x18, 0x03, 0x03, 0x00, 0x00, 0x8b, 0x8a, 0x1e, 0x03, 0x03, 0x00, 0x00, 0xa5, 0xa4, 0x1f,
+0x41, 0x00, 0x73, 0x53, 0x20, 0x43, 0x00, 0x64, 0x44, 0xd0, 0xd1, 0x26, 0x43, 0x00, 0x6c, 0x4c,
+0x88, 0x9d, 0x29, 0x00, 0x01, 0xc9, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x00, 0xef, 0x19, 0x61, 0xa0, 0x63, 0x86, 0x65, 0x82, 0x69, 0xa1, 0x6c, 0x92, 0x6e, 0xe4,
+0x6f, 0xa2, 0x72, 0xea, 0x73, 0x98, 0x75, 0xa3, 0x79, 0xec, 0x7a, 0xab, 0x41, 0xb5, 0x43, 0x8f,
+0x45, 0x90, 0x49, 0xd6, 0x4c, 0x91, 0x4e, 0xe3, 0x4f, 0xe0, 0x52, 0xe8, 0x53, 0x97, 0x55, 0xe9,
+0x59, 0xed, 0x5a, 0x8d, 0x20, 0xef, 0x5e, 0x15, 0x63, 0x9f, 0x64, 0xd4, 0x65, 0xd8, 0x6c, 0x96,
+0x6e, 0xe5, 0x6f, 0x93, 0x72, 0xfd, 0x73, 0xe7, 0x74, 0x9c, 0x7a, 0xa7, 0x43, 0xac, 0x44, 0xd2,
+0x45, 0xb7, 0x4c, 0x95, 0x4e, 0xd5, 0x4f, 0xe2, 0x52, 0xfc, 0x53, 0xe6, 0x54, 0x9b, 0x5a, 0xa6,
+0x20, 0x5e, 0x00, 0x0d, 0x10, 0x00, 0x00, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x22, 0x41, 0x00, 0x67,
+0x47, 0x23, 0x41, 0x00, 0x68, 0x48, 0x24, 0x41, 0x00, 0x6a, 0x4a, 0x29, 0x00, 0x01, 0xc8, 0x00,
+0x5e, 0x0d, 0x63, 0x86, 0x67, 0x9b, 0x68, 0xa9, 0x6a, 0x9f, 0x73, 0xc7, 0x75, 0xed, 0x43, 0x8f,
+0x47, 0x9d, 0x48, 0xa8, 0x4a, 0xac, 0x53, 0xc6, 0x55, 0xec, 0x20, 0x5e, 0x00, 0x03, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x00, 0x01, 0xa0, 0x0d, 0x00, 0x01,
+0xc8, 0x12, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41, 0x00, 0x74,
+0x54, 0x17, 0x03, 0x03, 0x00, 0x00, 0x8b, 0xd8, 0x19, 0x41, 0x00, 0x70, 0x50, 0x1e, 0x03, 0x03,
+0x00, 0x00, 0xc6, 0xc7, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x20, 0x41, 0x00, 0x64, 0x44, 0x22, 0x41,
+0x00, 0x67, 0x47, 0x23, 0x41, 0x00, 0x68, 0x48, 0x25, 0x41, 0x00, 0x6b, 0x4b, 0x26, 0x41, 0x00,
+0x6c, 0x4c, 0x28, 0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x29, 0x00, 0x01, 0xc9, 0x31, 0x41, 0x00,
+0x6e, 0x4e, 0x32, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x00, 0xef, 0x0b, 0x63, 0x86, 0x67, 0xd0, 0x72,
+0xe8, 0x73, 0xfa, 0x7a, 0xaa, 0x43, 0x8f, 0x47, 0xd1, 0x52, 0xf6, 0x53, 0xfb, 0x5a, 0xab, 0x20,
+0xef, 0x5e, 0x1d, 0x63, 0xac, 0x64, 0x8a, 0x65, 0x88, 0x67, 0xdd, 0x68, 0xbd, 0x69, 0x8c, 0x6b,
+0x85, 0x6c, 0xf2, 0x6e, 0xe4, 0x70, 0x9d, 0x72, 0xfd, 0x73, 0xe7, 0x74, 0x9c, 0x7a, 0xa8, 0x43,
+0xad, 0x44, 0xd4, 0x45, 0xd2, 0x47, 0xde, 0x48, 0xbe, 0x49, 0xd7, 0x4b, 0xb7, 0x4c, 0xf1, 0x4e,
+0xe5, 0x50, 0xe1, 0x52, 0xfc, 0x53, 0xe6, 0x54, 0x9b, 0x5a, 0xa9, 0x20, 0x5e, 0x00, 0x02, 0x02,
+0x07, 0x00, 0x00, 0xcc, 0x03, 0x01, 0x03, 0x00, 0xcb, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x17,
+0x43, 0x03, 0x00, 0x00, 0x9e, 0x9f, 0x18, 0x43, 0x03, 0x00, 0x00, 0xab, 0xac, 0x1b, 0x02, 0x07,
+0x00, 0x00, 0xcd, 0x1e, 0x43, 0x03, 0x00, 0x00, 0x86, 0x8f, 0x27, 0x43, 0x03, 0x00, 0x00, 0x9b,
+0x9d, 0x28, 0x43, 0x03, 0x00, 0x00, 0x91, 0x92, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69,
+0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20,
+0x60, 0x5e, 0x0d, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x79, 0xbd, 0x41,
+0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x59, 0xbe, 0x20, 0x5e, 0x27, 0x0d, 0x61,
+0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49,
+0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0x27, 0x22, 0x0d, 0x61, 0x84, 0x65, 0x89, 0x69,
+0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55,
+0x9a, 0x59, 0xf3, 0x20, 0x22, 0x7c, 0x0b, 0x61, 0x87, 0x65, 0xd0, 0x69, 0xe7, 0x6f, 0xf9, 0x75,
+0xf7, 0x41, 0x80, 0x45, 0xd1, 0x49, 0xe8, 0x4f, 0xdd, 0x55, 0xef, 0x20, 0x7c, 0x7e, 0x07, 0x65,
+0xc6, 0x6f, 0xa4, 0x94, 0xe4, 0x45, 0xc7, 0x4f, 0xa5, 0x99, 0xe5, 0x20, 0x7e, 0x00, 0xc8, 0xc6,
+0x08, 0x09, 0xc5, 0x01, 0x47, 0x52, 0x2c, 0xc5, 0x01, 0x44, 0x45, 0x0c, 0x04, 0x00, 0x00, 0x00,
+0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0x5d, 0x01, 0x75, 0x03, 0x00, 0x00, 0x5a,
+0x03, 0x6c, 0x01, 0x75, 0x03, 0x00, 0x00, 0x52, 0x03, 0x6c, 0x02, 0x75, 0x03, 0x00, 0x00, 0x52,
+0x03, 0x75, 0x02, 0x75, 0x03, 0x00, 0x00, 0xb5, 0x01, 0xfc, 0x03, 0x05, 0x05, 0x00, 0x00, 0xb5,
+0x01, 0x0a, 0x04, 0x05, 0x05, 0x00, 0x00, 0x54, 0x03, 0x01, 0x06, 0x7b, 0x06, 0x00, 0x00, 0x55,
+0x03, 0xdc, 0x06, 0x03, 0x07, 0x00, 0x00, 0x5b, 0x03, 0x60, 0x05, 0x9c, 0x05, 0x00, 0x00, 0x39,
+0x75, 0x20, 0x07, 0x91, 0x07, 0x00, 0x00, 0x44, 0x75, 0xe6, 0x07, 0x27, 0x08, 0x00, 0x00, 0x00,
+0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+0x02, 0x03, 0x00, 0x00, 0x7c, 0x03, 0x02, 0x01, 0x00, 0x22, 0xfd, 0x04, 0x02, 0x01, 0x00, 0xf5,
+0xfc, 0x07, 0x01, 0x01, 0x00, 0x26, 0x08, 0x02, 0x01, 0x00, 0x2f, 0x7b, 0x09, 0x04, 0x09, 0x00,
+0x28, 0x5b, 0x00, 0x1b, 0x0a, 0x04, 0x09, 0x00, 0x29, 0x5d, 0x00, 0x1d, 0x0b, 0x02, 0x01, 0x00,
+0x3d, 0x7d, 0x0c, 0x04, 0x08, 0xe1, 0x3f, 0x5c, 0x00, 0x1c, 0x0d, 0x02, 0x07, 0xcb, 0xc8, 0xce,
+0x10, 0x42, 0x03, 0x00, 0x00, 0x40, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0xc5, 0x00, 0x7a, 0x2c,
+0x5a, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17,
+0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x41, 0x00, 0x81, 0x9a, 0x1b, 0x02,
+0x04, 0x2b, 0x2a, 0xc9, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x41, 0x00, 0x94, 0x99, 0x28, 0x41,
+0x00, 0x84, 0x8e, 0x29, 0x04, 0x0c, 0x27, 0xf8, 0xca, 0x00, 0x1e, 0x2c, 0xc5, 0x00, 0x79, 0x15,
+0x59, 0x15, 0x3c, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x42, 0x03, 0x00, 0x00, 0x3e,
+0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x42, 0x00, 0x6e, 0x4e, 0x23, 0x32, 0x42, 0x03, 0x00, 0x00,
+0xe6, 0x33, 0x01, 0x01, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x00, 0x3a, 0x35, 0x04, 0x0c, 0x2d, 0x5f,
+0x00, 0x00, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x2a, 0x04,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x02, 0x01, 0x00, 0xfb, 0xad, 0x03, 0x01, 0x02, 0xfd,
+0xa0, 0x04, 0x01, 0x00, 0xfc, 0x9c, 0x05, 0x01, 0x00, 0xac, 0xcf, 0x06, 0x01, 0x02, 0xab, 0xa0,
+0x07, 0x01, 0x02, 0xf3, 0xa0, 0x08, 0x01, 0x02, 0x7b, 0xa0, 0x09, 0x01, 0x02, 0x5b, 0xa0, 0x0a,
+0x01, 0x00, 0x5d, 0xf1, 0x0b, 0x01, 0x00, 0x7d, 0xf8, 0x0c, 0x01, 0x00, 0x5c, 0xa8, 0x0d, 0x01,
+0x03, 0xce, 0xa0, 0x10, 0x01, 0x02, 0x40, 0xa0, 0x11, 0x10, 0x00, 0x00, 0x12, 0x10, 0x00, 0x00,
+0x13, 0x01, 0x00, 0xf4, 0xa9, 0x14, 0x41, 0x00, 0xe7, 0xe8, 0x15, 0x85, 0x02, 0xbe, 0x2c, 0xa0,
+0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00,
+0x00, 0x18, 0x41, 0x00, 0x9b, 0x9d, 0x19, 0x10, 0x00, 0x00, 0x1a, 0x01, 0x03, 0xcc, 0xcd, 0x1b,
+0x01, 0x01, 0xc9, 0xee, 0x1e, 0x41, 0x00, 0x91, 0x92, 0x1f, 0x01, 0x00, 0xe1, 0xf5, 0x20, 0x41,
+0x00, 0xd0, 0xd1, 0x21, 0x01, 0x02, 0xa6, 0xa0, 0x22, 0x10, 0x00, 0x00, 0x23, 0x10, 0x00, 0x00,
+0x24, 0x10, 0x00, 0x00, 0x25, 0x01, 0x02, 0x26, 0xa0, 0x26, 0x01, 0x03, 0xc8, 0xa0, 0x27, 0x01,
+0x03, 0xcb, 0xa0, 0x28, 0x01, 0x03, 0xca, 0xa0, 0x29, 0x01, 0x00, 0xaa, 0xdd, 0x2b, 0x10, 0x00,
+0x00, 0x2c, 0x85, 0x00, 0xae, 0x15, 0x3c, 0x15, 0x3c, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15,
+0x2d, 0x01, 0x00, 0xaf, 0x3e, 0x2e, 0x01, 0x00, 0xbd, 0xb8, 0x2f, 0x10, 0x00, 0x00, 0x30, 0x10,
+0x00, 0x00, 0x31, 0x01, 0x02, 0x23, 0xa0, 0x32, 0x01, 0x00, 0xe6, 0xa7, 0x33, 0x01, 0x02, 0x9e,
+0xa0, 0x34, 0x01, 0x00, 0xfa, 0xf6, 0x35, 0x10, 0x00, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x78, 0x56, 0x10, 0x00, 0x00, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00,
+0x02, 0x01, 0x00, 0xfb, 0xad, 0x03, 0x01, 0x02, 0xfd, 0xa0, 0x04, 0x01, 0x00, 0xfc, 0x9c, 0x05,
+0x01, 0x00, 0xac, 0xcf, 0x06, 0x01, 0x02, 0xab, 0xa0, 0x07, 0x01, 0x02, 0xf3, 0xa0, 0x08, 0x01,
+0x02, 0x7b, 0xa0, 0x09, 0x01, 0x02, 0x5b, 0xa0, 0x0a, 0x01, 0x00, 0x5d, 0xf1, 0x0b, 0x01, 0x00,
+0x7d, 0xf8, 0x0c, 0x01, 0x00, 0x5c, 0xa8, 0x0d, 0x01, 0x03, 0xce, 0xa0, 0x10, 0x01, 0x02, 0x40,
+0xa0, 0x11, 0x10, 0x00, 0x00, 0x12, 0x10, 0x00, 0x00, 0x13, 0x01, 0x00, 0xf4, 0xa9, 0x14, 0x41,
+0x00, 0xe7, 0xe8, 0x15, 0x85, 0x02, 0xbe, 0x2c, 0xa0, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x1a, 0x2c,
+0x00, 0x2c, 0x16, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x18, 0x41, 0x00, 0x9b, 0x9d, 0x19,
+0x10, 0x00, 0x00, 0x1a, 0x01, 0x03, 0xcc, 0xcd, 0x1b, 0x01, 0x01, 0xc9, 0xee, 0x1e, 0x41, 0x00,
+0x91, 0x92, 0x1f, 0x01, 0x00, 0xe1, 0xf5, 0x20, 0x41, 0x00, 0xd0, 0xd1, 0x21, 0x01, 0x02, 0xa6,
+0xa0, 0x22, 0x10, 0x00, 0x00, 0x23, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, 0x00, 0x25, 0x01, 0x02,
+0x26, 0xa0, 0x26, 0x01, 0x03, 0xc8, 0xa0, 0x27, 0x01, 0x03, 0xcb, 0xa0, 0x28, 0x01, 0x03, 0xca,
+0xa0, 0x29, 0x01, 0x00, 0xaa, 0xdd, 0x2b, 0x10, 0x00, 0x00, 0x2c, 0x85, 0x00, 0xae, 0x15, 0x3c,
+0x15, 0x3c, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x01, 0x00, 0xaf, 0x3e, 0x2e, 0x01,
+0x00, 0xbd, 0xb8, 0x2f, 0x10, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x31, 0x01, 0x02, 0x23, 0xa0,
+0x32, 0x01, 0x00, 0xe6, 0xa7, 0x33, 0x01, 0x02, 0x9e, 0xa0, 0x34, 0x01, 0x00, 0xfa, 0xf6, 0x35,
+0x10, 0x00, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x56, 0x10, 0x00, 0x00, 0x00,
+0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4,
+0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4,
+0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c,
+0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e,
+0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5,
+0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84,
+0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8,
+0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0xf8, 0x03, 0x61, 0x86, 0x41, 0x8f, 0x20, 0xf8, 0xf7, 0x03,
+0x63, 0x87, 0x43, 0x80, 0x20, 0xf7, 0x00, 0x04, 0x01, 0x01, 0x00, 0x15, 0x2a, 0x04, 0x1f, 0x00,
+0x00, 0x00, 0x00, 0x7d, 0x00, 0x02, 0x01, 0x01, 0xa0, 0xad, 0x03, 0x01, 0x02, 0xfd, 0xa0, 0x04,
+0x01, 0x01, 0xa0, 0x9c, 0x05, 0x01, 0x02, 0xac, 0xa0, 0x06, 0x01, 0x02, 0xab, 0xa0, 0x07, 0x10,
+0x00, 0x00, 0x08, 0x01, 0x02, 0x7b, 0xa0, 0x09, 0x01, 0x02, 0x5b, 0xa0, 0x0a, 0x01, 0x00, 0x5d,
+0xf1, 0x0b, 0x01, 0x00, 0x7d, 0xf8, 0x0c, 0x01, 0x00, 0x5c, 0xa8, 0x0d, 0x01, 0x03, 0xce, 0xa0,
+0x10, 0x01, 0x02, 0x40, 0xa0, 0x11, 0x10, 0x00, 0x00, 0x12, 0x10, 0x00, 0x00, 0x13, 0x01, 0x02,
+0x14, 0xa0, 0x14, 0x10, 0x00, 0x00, 0x15, 0x85, 0x02, 0x9d, 0x2c, 0xa0, 0x2c, 0x00, 0x2c, 0x00,
+0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x18, 0x10, 0x00,
+0x00, 0x19, 0x10, 0x00, 0x00, 0x1a, 0x01, 0x03, 0xcc, 0xcd, 0x1b, 0x01, 0x03, 0xc9, 0xa0, 0x1e,
+0x41, 0x00, 0x91, 0x92, 0x1f, 0x01, 0x00, 0xe1, 0x15, 0x20, 0x10, 0x00, 0x00, 0x21, 0x01, 0x02,
+0xa6, 0xa0, 0x22, 0x10, 0x00, 0x00, 0x23, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, 0x00, 0x25, 0x01,
+0x02, 0x26, 0xa0, 0x26, 0x01, 0x03, 0xc8, 0xa0, 0x27, 0x01, 0x03, 0xcb, 0xa0, 0x28, 0x01, 0x03,
+0xca, 0xa0, 0x29, 0x01, 0x02, 0xaa, 0xa0, 0x2b, 0x10, 0x00, 0x00, 0x2c, 0x85, 0x00, 0xae, 0x15,
+0x3c, 0x15, 0x3c, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x01, 0x00, 0xaf, 0x3e, 0x2e,
+0x01, 0x02, 0x9b, 0xa0, 0x2f, 0x10, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x31, 0x01, 0x02, 0x23,
+0xa0, 0x32, 0x01, 0x00, 0xe6, 0xa7, 0x33, 0x10, 0x00, 0x00, 0x34, 0x01, 0x00, 0xfa, 0xf6, 0x35,
+0x10, 0x00, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x56, 0x10, 0x00, 0x00, 0x00,
+0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60, 0x7e, 0x03,
+0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93,
+0x75, 0x96, 0x20, 0x5e, 0x27, 0x07, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3,
+0x45, 0x90, 0x20, 0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81,
+0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0xf8, 0x03, 0x61, 0x86, 0x41, 0x8f,
+0x20, 0xf8, 0x2c, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0x2c, 0x00, 0x03, 0x01, 0x03, 0x00, 0xcc,
+0x09, 0x03, 0x07, 0x00, 0x00, 0x00, 0xae, 0x0a, 0x03, 0x07, 0x00, 0x00, 0x00, 0xaf, 0x0d, 0x02,
+0x07, 0x00, 0x00, 0xcd, 0x12, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x18, 0x03, 0x03, 0x00, 0x00, 0xab,
+0xac, 0x1e, 0x03, 0x03, 0x00, 0x00, 0x86, 0x8f, 0x27, 0x03, 0x03, 0x00, 0x00, 0x9b, 0x9d, 0x28,
+0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x00, 0x60, 0x07, 0x61, 0x85, 0x65, 0x8a, 0x75, 0x97, 0x41,
+0xb7, 0x45, 0xd4, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e,
+0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49,
+0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x27, 0x07, 0x61, 0xa0, 0x65, 0x82, 0x6f, 0xa2, 0x41,
+0xb5, 0x45, 0x90, 0x4f, 0xe0, 0x20, 0x27, 0x22, 0x0d, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f,
+0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59,
+0xf3, 0x20, 0x22, 0x2c, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0x2c, 0x00, 0x03, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x02, 0x03, 0x00, 0x00, 0xaa, 0x0d, 0x02,
+0x07, 0xc8, 0xc9, 0xa0, 0x12, 0x03, 0x03, 0x00, 0x00, 0xa9, 0xa8, 0x13, 0x41, 0x00, 0x72, 0x52,
+0x14, 0x41, 0x00, 0x74, 0x54, 0x15, 0xc5, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0xbe, 0x2c, 0xbd, 0x2c,
+0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x03, 0x03, 0x00, 0x00, 0xfb, 0xeb, 0x17, 0x03, 0x03, 0x00, 0x00,
+0x85, 0xde, 0x18, 0x03, 0x03, 0x00, 0x00, 0x8b, 0x8a, 0x1b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x1e,
+0x03, 0x03, 0x00, 0x00, 0xa5, 0xa4, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x20, 0x43, 0x00, 0x64, 0x44,
+0xd0, 0xd1, 0x26, 0x43, 0x00, 0x6c, 0x4c, 0x88, 0x9d, 0x29, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x32,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x00, 0xef, 0x19, 0x61, 0xa0, 0x63, 0x86, 0x65, 0x82, 0x69, 0xa1,
+0x6c, 0x92, 0x6e, 0xe4, 0x6f, 0xa2, 0x72, 0xea, 0x73, 0x98, 0x75, 0xa3, 0x79, 0xec, 0x7a, 0xab,
+0x41, 0xb5, 0x43, 0x8f, 0x45, 0x90, 0x49, 0xd6, 0x4c, 0x91, 0x4e, 0xe3, 0x4f, 0xe0, 0x52, 0xe8,
+0x53, 0x97, 0x55, 0xe9, 0x59, 0xed, 0x5a, 0x8d, 0x20, 0xef, 0x60, 0x15, 0x63, 0x9f, 0x64, 0xd4,
+0x65, 0xd8, 0x6c, 0x96, 0x6e, 0xe5, 0x6f, 0x93, 0x72, 0xfd, 0x73, 0xe7, 0x74, 0x9c, 0x7a, 0xa7,
+0x43, 0xac, 0x44, 0xd2, 0x45, 0xb7, 0x4c, 0x95, 0x4e, 0xd5, 0x4f, 0xe2, 0x52, 0xfc, 0x53, 0xe6,
+0x54, 0x9b, 0x5a, 0xa6, 0x20, 0x60, 0x00, 0x0d, 0x02, 0x07, 0xc8, 0xa0, 0xa0, 0x1b, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x22, 0x41, 0x00, 0x67, 0x47, 0x23, 0x41, 0x00,
+0x68, 0x48, 0x24, 0x41, 0x00, 0x6a, 0x4a, 0x29, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x00, 0x5e, 0x0d,
+0x63, 0x86, 0x67, 0x9b, 0x68, 0xa9, 0x6a, 0x9f, 0x73, 0xc7, 0x75, 0xed, 0x43, 0x8f, 0x47, 0x9d,
+0x48, 0xa8, 0x4a, 0xac, 0x53, 0xc6, 0x55, 0xec, 0x20, 0x5e, 0x00, 0x03, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x00, 0x01, 0xa0, 0x0d, 0x02, 0x07, 0xc8, 0xc9,
+0xa0, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41, 0x00, 0x74,
+0x54, 0x17, 0x43, 0x03, 0x00, 0x00, 0x8b, 0xd8, 0x19, 0x41, 0x00, 0x70, 0x50, 0x1b, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x1e, 0x43, 0x03, 0x00, 0x00, 0xc6, 0xc7, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x20,
+0x41, 0x00, 0x64, 0x44, 0x22, 0x41, 0x00, 0x67, 0x47, 0x23, 0x41, 0x00, 0x68, 0x48, 0x25, 0x41,
+0x00, 0x6b, 0x4b, 0x26, 0x41, 0x00, 0x6c, 0x4c, 0x28, 0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x29,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x32, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x00, 0xef, 0x0b, 0x63, 0x86,
+0x67, 0xd0, 0x72, 0xe8, 0x73, 0xfa, 0x7a, 0xaa, 0x43, 0x8f, 0x47, 0xd1, 0x52, 0xf6, 0x53, 0xfb,
+0x5a, 0xab, 0x20, 0xef, 0x60, 0x1d, 0x63, 0xac, 0x64, 0x8a, 0x65, 0x88, 0x67, 0xdd, 0x68, 0xbd,
+0x69, 0x8c, 0x6b, 0x85, 0x6c, 0xf2, 0x6e, 0xe4, 0x70, 0x9d, 0x72, 0xfd, 0x73, 0xe7, 0x74, 0x9c,
+0x7a, 0xa8, 0x43, 0xad, 0x44, 0xd4, 0x45, 0xd2, 0x47, 0xde, 0x48, 0xbe, 0x49, 0xd7, 0x4b, 0xb7,
+0x4c, 0xf1, 0x4e, 0xe5, 0x50, 0xe1, 0x52, 0xfc, 0x53, 0xe6, 0x54, 0x9b, 0x5a, 0xa9, 0x20, 0x60,
+0x00, 0x03, 0x01, 0x03, 0x00, 0xcb, 0x0d, 0x02, 0x07, 0xca, 0x00, 0xcd, 0x12, 0x02, 0x03, 0x00,
+0x00, 0xd5, 0x17, 0x43, 0x03, 0x00, 0x00, 0x9e, 0x9f, 0x18, 0x43, 0x03, 0x00, 0x00, 0xab, 0xac,
+0x1b, 0x02, 0x07, 0x00, 0x00, 0xcc, 0x1e, 0x43, 0x03, 0x00, 0x00, 0x86, 0x8f, 0x27, 0x43, 0x03,
+0x00, 0x00, 0x9b, 0x9d, 0x28, 0x43, 0x03, 0x00, 0x00, 0x91, 0x92, 0x29, 0x02, 0x07, 0x00, 0x00,
+0xc9, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7,
+0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x5e, 0x0d, 0x61, 0x83, 0x65, 0x88,
+0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x79, 0xbd, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2,
+0x55, 0xea, 0x59, 0xbe, 0x20, 0x5e, 0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2,
+0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed,
+0x20, 0x27, 0x22, 0x0d, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98,
+0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x7e, 0x0b,
+0x61, 0x87, 0x65, 0xd0, 0x69, 0xe7, 0x6f, 0xf9, 0x75, 0xf7, 0x41, 0x80, 0x45, 0xd1, 0x49, 0xe8,
+0x4f, 0xdd, 0x55, 0xef, 0x20, 0x7e, 0x2c, 0x07, 0x65, 0xc6, 0x6f, 0xa4, 0x94, 0xe4, 0x45, 0xc7,
+0x4f, 0xa5, 0x99, 0xe5, 0x20, 0x2c, 0x00, 0xf4, 0xc5, 0x03, 0x04, 0x00, 0x00, 0x48, 0x55, 0x05,
+0x02, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x03, 0xd3, 0x01, 0x44,
+0x02, 0x00, 0x00, 0x5a, 0x03, 0x45, 0x01, 0x4c, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x4c,
+0x01, 0x00, 0x00, 0xb5, 0xe1, 0x05, 0x03, 0x5f, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x07, 0x00, 0xcc,
+0xc8, 0x03, 0x01, 0x03, 0x00, 0xcd, 0x04, 0x03, 0x05, 0x00, 0x2b, 0xc9, 0x1e, 0x05, 0x01, 0x01,
+0x00, 0x21, 0x06, 0x02, 0x07, 0x00, 0x00, 0xca, 0x07, 0x01, 0x01, 0x00, 0x2f, 0x08, 0x02, 0x05,
+0x00, 0x3d, 0xcb, 0x09, 0x02, 0x01, 0x00, 0x28, 0xfa, 0x0a, 0x02, 0x01, 0x00, 0x29, 0xef, 0x0b,
+0x41, 0x00, 0x94, 0x99, 0x0c, 0x42, 0x00, 0x81, 0x9a, 0xf9, 0x0d, 0xc2, 0x04, 0xa2, 0x0d, 0xe0,
+0x00, 0xce, 0x83, 0x10, 0x42, 0x03, 0x00, 0x00, 0x5c, 0x11, 0x42, 0x03, 0x00, 0x00, 0x7c, 0x12,
+0x42, 0x00, 0x65, 0x45, 0x8e, 0x13, 0x42, 0x03, 0x00, 0x00, 0xf5, 0x14, 0x42, 0x03, 0x00, 0x00,
+0xcf, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x42, 0x00, 0x69, 0x49,
+0xd6, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x42, 0x03, 0xa0, 0xa0, 0xf6, 0x1b, 0x42, 0x00, 0xa3,
+0xe9, 0x9e, 0x1e, 0x42, 0x00, 0x61, 0x41, 0x84, 0x21, 0x42, 0x03, 0x00, 0x00, 0x5b, 0x22, 0x42,
+0x03, 0x00, 0x00, 0x5d, 0x24, 0x42, 0x03, 0x00, 0x00, 0xa1, 0x27, 0x42, 0x00, 0x82, 0x90, 0x24,
+0x28, 0x42, 0x00, 0xa0, 0xb5, 0xe1, 0x29, 0x42, 0x00, 0xa1, 0xd6, 0x30, 0x2b, 0x42, 0x03, 0xa0,
+0xa0, 0x5c, 0x2c, 0x42, 0x00, 0x7a, 0x5a, 0x3e, 0x2d, 0x42, 0x03, 0x00, 0x00, 0x23, 0x2e, 0x42,
+0x00, 0x63, 0x43, 0x26, 0x2f, 0x42, 0x03, 0x00, 0x00, 0x40, 0x30, 0x42, 0x03, 0x00, 0x00, 0x7b,
+0x31, 0x42, 0x00, 0x6e, 0x4e, 0x7d, 0x32, 0x42, 0x03, 0x00, 0x00, 0x3c, 0x33, 0x02, 0x00, 0x2c,
+0x3f, 0x3b, 0x34, 0x02, 0x00, 0x2e, 0x3a, 0x3e, 0x35, 0x03, 0x00, 0x2d, 0x5f, 0x2a, 0x1f, 0x39,
+0x00, 0x00, 0x20, 0x00, 0x16, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x7e, 0x07, 0x61, 0xc6, 0x6e,
+0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65,
+0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55,
+0xea, 0x20, 0x5e, 0xf8, 0x03, 0x61, 0x86, 0x41, 0x8f, 0x20, 0xf8, 0x60, 0x0b, 0x61, 0x85, 0x65,
+0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55,
+0xeb, 0x20, 0x60, 0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79,
+0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0x27, 0x22,
+0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45,
+0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0xf7, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20,
+0xf7, 0x00, 0x02, 0x02, 0x03, 0x00, 0xce, 0x7e, 0x03, 0x02, 0x07, 0x00, 0xd0, 0xc8, 0x04, 0x01,
+0x03, 0x00, 0xcf, 0x05, 0x02, 0x07, 0x00, 0x00, 0xca, 0x06, 0x02, 0x07, 0x00, 0x00, 0xcb, 0x07,
+0x02, 0x07, 0x00, 0x00, 0xcc, 0x08, 0x02, 0x03, 0x00, 0x00, 0x60, 0x09, 0x02, 0x07, 0x00, 0x00,
+0xcd, 0x0b, 0x42, 0x03, 0x00, 0x00, 0xf1, 0x0d, 0xc2, 0x05, 0x00, 0x0d, 0xe0, 0x00, 0xd1, 0x83,
+0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41, 0x00, 0x74, 0x54, 0x16, 0x42, 0x03, 0x00, 0x00, 0xaa,
+0x1a, 0x41, 0x00, 0x8b, 0x8a, 0x1f, 0x42, 0x00, 0x73, 0x53, 0xd0, 0x20, 0x42, 0x00, 0x64, 0x44,
+0xd1, 0x25, 0x42, 0x03, 0x00, 0x00, 0x88, 0x26, 0x42, 0x00, 0x6c, 0x4c, 0x9d, 0x2b, 0x41, 0x00,
+0xfb, 0xeb, 0x00, 0xf3, 0x13, 0x63, 0x9f, 0x64, 0xd4, 0x65, 0xd8, 0x6c, 0x96, 0x6e, 0xe5, 0x72,
+0xfd, 0x73, 0xe7, 0x74, 0x9c, 0x7a, 0xa7, 0x43, 0xac, 0x44, 0xd2, 0x45, 0xb7, 0x4c, 0x95, 0x4e,
+0xd5, 0x52, 0xfc, 0x53, 0xe6, 0x54, 0x9b, 0x5a, 0xa6, 0x20, 0xf3, 0x5e, 0x07, 0x61, 0x83, 0x69,
+0x8c, 0x6f, 0x93, 0x41, 0xb6, 0x49, 0xd7, 0x4f, 0xe2, 0x20, 0x5e, 0xf4, 0x03, 0x61, 0xc7, 0x41,
+0xc6, 0x20, 0xf4, 0xf8, 0x03, 0x75, 0x85, 0x55, 0xde, 0x20, 0xf8, 0xf2, 0x05, 0x61, 0xa5, 0x65,
+0xa9, 0x41, 0xa4, 0x45, 0xa8, 0x20, 0xf2, 0xfa, 0x03, 0x7a, 0xbe, 0x5a, 0xbd, 0x20, 0xfa, 0x27,
+0x19, 0x61, 0xa0, 0x63, 0x86, 0x65, 0x82, 0x69, 0xa1, 0x6c, 0x92, 0x6e, 0xe4, 0x6f, 0xa2, 0x72,
+0xea, 0x73, 0x98, 0x75, 0xa3, 0x79, 0xec, 0x7a, 0xab, 0x41, 0xb5, 0x43, 0x8f, 0x45, 0x90, 0x49,
+0xd6, 0x4c, 0x91, 0x4e, 0xe3, 0x4f, 0xe0, 0x52, 0xe8, 0x53, 0x97, 0x55, 0xe9, 0x59, 0xed, 0x5a,
+0x8d, 0x20, 0x27, 0x2b, 0x05, 0x6f, 0x8b, 0x75, 0xfb, 0x4f, 0x8a, 0x55, 0xeb, 0x20, 0x2b, 0x22,
+0x09, 0x61, 0x84, 0x65, 0x89, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x45, 0xd3, 0x4f, 0x99, 0x55,
+0x9a, 0x20, 0x22, 0xf7, 0x07, 0x63, 0x87, 0x73, 0xad, 0x74, 0xee, 0x43, 0x80, 0x53, 0xb8, 0x54,
+0xdd, 0x20, 0xf7, 0x00, 0x02, 0x01, 0x03, 0x00, 0xcc, 0x03, 0x01, 0x03, 0x00, 0xce, 0x04, 0x01,
+0x03, 0x00, 0xcd, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xcc, 0x0b, 0x42, 0x07, 0x00, 0x00, 0xcd, 0x0c,
+0x42, 0x07, 0x00, 0x00, 0xce, 0x0d, 0x42, 0x05, 0x00, 0x95, 0xcf, 0x13, 0x42, 0x03, 0x00, 0x00,
+0x15, 0x14, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x17, 0x42, 0x03, 0x00, 0x00, 0x8d, 0x19, 0x42, 0x03,
+0x00, 0x00, 0x9f, 0x1a, 0x41, 0x00, 0x93, 0xa7, 0x1b, 0x42, 0x05, 0x00, 0x97, 0xa0, 0x28, 0x41,
+0x01, 0x00, 0x8f, 0x29, 0x41, 0x01, 0x00, 0x8d, 0x2b, 0x41, 0x00, 0x96, 0x98, 0x00, 0x7e, 0x03,
+0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x04, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x20, 0x5e,
+0xf8, 0x02, 0x61, 0x86, 0x20, 0xf8, 0x60, 0x03, 0x61, 0x85, 0x65, 0x8a, 0x20, 0x60, 0x27, 0x0b,
+0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x41, 0x8f, 0x45, 0x90, 0x49, 0x8d,
+0x4f, 0x95, 0x55, 0x97, 0x20, 0x27, 0x2b, 0x05, 0x6f, 0x93, 0x75, 0x96, 0x4f, 0xa7, 0x55, 0x98,
+0x20, 0x2b, 0x22, 0x09, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e,
+0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x2c, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0x2c, 0x00, 0x37,
+0xcf, 0x03, 0x04, 0xd0, 0x00, 0x48, 0x55, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x54, 0x03, 0xe3, 0x01, 0x54, 0x02, 0x00, 0x00, 0x5a, 0x03, 0x55, 0x01, 0x5c,
+0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00, 0xb5, 0xe1, 0x15, 0x03, 0x69,
+0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x07, 0x00, 0xcc,
+0xc8, 0x03, 0x01, 0x03, 0x00, 0xcd, 0x04, 0x03, 0x05, 0x00, 0x2b, 0xc9, 0x1e, 0x05, 0x01, 0x01,
+0x00, 0x21, 0x06, 0x02, 0x07, 0x00, 0x00, 0xca, 0x07, 0x01, 0x01, 0x00, 0x2f, 0x08, 0x02, 0x05,
+0x00, 0x3d, 0xcb, 0x09, 0x02, 0x01, 0x00, 0x28, 0xfa, 0x0a, 0x02, 0x01, 0x00, 0x29, 0xef, 0x0b,
+0x41, 0x00, 0x94, 0x99, 0x0c, 0x42, 0x00, 0x81, 0x9a, 0xf9, 0x0d, 0xc2, 0x04, 0xa2, 0x0d, 0xe0,
+0x00, 0xce, 0x83, 0x10, 0x42, 0x03, 0x00, 0x00, 0x5c, 0x11, 0x42, 0x03, 0x00, 0x00, 0x7c, 0x12,
+0x42, 0x00, 0x65, 0x45, 0x8e, 0x15, 0xc4, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x00, 0x2c, 0x1a, 0x2c,
+0x00, 0x2c, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x42, 0x00, 0x69, 0x49, 0xd6, 0x18, 0x41, 0x00,
+0x6f, 0x4f, 0x1a, 0x42, 0x03, 0xa0, 0xa0, 0xf6, 0x1b, 0x42, 0x00, 0xa3, 0xe9, 0x9e, 0x1e, 0x42,
+0x00, 0x61, 0x41, 0x84, 0x21, 0x42, 0x03, 0x00, 0x00, 0x5b, 0x22, 0x42, 0x03, 0x00, 0x00, 0x5d,
+0x24, 0x42, 0x03, 0x00, 0x00, 0xa1, 0x27, 0x42, 0x00, 0x82, 0x90, 0x24, 0x28, 0x42, 0x00, 0xa0,
+0xb5, 0xe1, 0x29, 0x01, 0x00, 0x30, 0xf5, 0x2b, 0x42, 0x03, 0xa0, 0xa0, 0xcf, 0x2c, 0xc4, 0x00,
+0x79, 0x15, 0x59, 0x15, 0x3e, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x42, 0x03, 0x00, 0x00, 0x23,
+0x2e, 0x42, 0x00, 0x63, 0x43, 0x26, 0x2f, 0x42, 0x03, 0x00, 0x00, 0x40, 0x30, 0x42, 0x03, 0x00,
+0x00, 0x7b, 0x31, 0x42, 0x00, 0x6e, 0x4e, 0x7d, 0x32, 0x42, 0x03, 0x00, 0x00, 0x3c, 0x33, 0x02,
+0x00, 0x2c, 0x3f, 0x3b, 0x34, 0x02, 0x00, 0x2e, 0x3a, 0x3e, 0x35, 0x03, 0x00, 0x2d, 0x5f, 0x2a,
+0x1f, 0x39, 0x00, 0x00, 0x20, 0x56, 0x42, 0x00, 0xa1, 0xd6, 0x3c, 0x00, 0x16, 0x42, 0x03, 0x00,
+0x00, 0xd5, 0x00, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f,
+0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41,
+0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xf8, 0x03, 0x61, 0x86, 0x41,
+0x8f, 0x20, 0xf8, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41,
+0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x27, 0x0d, 0x61, 0xa0, 0x65,
+0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f,
+0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0x27, 0x22, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f,
+0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20,
+0x22, 0xf7, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0xf7, 0x00, 0x02, 0x02, 0x03, 0x00, 0xce, 0x7e,
+0x03, 0x02, 0x07, 0x00, 0xd0, 0xc8, 0x04, 0x01, 0x03, 0x00, 0xcf, 0x05, 0x02, 0x07, 0x00, 0x00,
+0xca, 0x06, 0x02, 0x07, 0x00, 0x00, 0xcb, 0x07, 0x02, 0x07, 0x00, 0x00, 0xcc, 0x08, 0x02, 0x03,
+0x00, 0x00, 0x60, 0x09, 0x02, 0x07, 0x00, 0x00, 0xcd, 0x0b, 0x42, 0x03, 0x00, 0x00, 0xf1, 0x0d,
+0xc2, 0x05, 0x00, 0x0d, 0xe0, 0x00, 0xd1, 0x83, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41, 0x00,
+0x74, 0x54, 0x16, 0x42, 0x03, 0x00, 0x00, 0xaa, 0x1a, 0x41, 0x00, 0x8b, 0x8a, 0x1f, 0x42, 0x00,
+0x73, 0x53, 0xd0, 0x20, 0x42, 0x00, 0x64, 0x44, 0xd1, 0x25, 0x42, 0x03, 0x00, 0x00, 0x88, 0x26,
+0x42, 0x00, 0x6c, 0x4c, 0x9d, 0x2b, 0x41, 0x00, 0xfb, 0xeb, 0x00, 0xf3, 0x13, 0x63, 0x9f, 0x64,
+0xd4, 0x65, 0xd8, 0x6c, 0x96, 0x6e, 0xe5, 0x72, 0xfd, 0x73, 0xe7, 0x74, 0x9c, 0x7a, 0xa7, 0x43,
+0xac, 0x44, 0xd2, 0x45, 0xb7, 0x4c, 0x95, 0x4e, 0xd5, 0x52, 0xfc, 0x53, 0xe6, 0x54, 0x9b, 0x5a,
+0xa6, 0x20, 0xf3, 0x5e, 0x07, 0x61, 0x83, 0x69, 0x8c, 0x6f, 0x93, 0x41, 0xb6, 0x49, 0xd7, 0x4f,
+0xe2, 0x20, 0x5e, 0xf4, 0x03, 0x61, 0xc7, 0x41, 0xc6, 0x20, 0xf4, 0xf8, 0x03, 0x75, 0x85, 0x55,
+0xde, 0x20, 0xf8, 0xf2, 0x05, 0x61, 0xa5, 0x65, 0xa9, 0x41, 0xa4, 0x45, 0xa8, 0x20, 0xf2, 0xfa,
+0x03, 0x7a, 0xbe, 0x5a, 0xbd, 0x20, 0xfa, 0x27, 0x19, 0x61, 0xa0, 0x63, 0x86, 0x65, 0x82, 0x69,
+0xa1, 0x6c, 0x92, 0x6e, 0xe4, 0x6f, 0xa2, 0x72, 0xea, 0x73, 0x98, 0x75, 0xa3, 0x79, 0xec, 0x7a,
+0xab, 0x41, 0xb5, 0x43, 0x8f, 0x45, 0x90, 0x49, 0xd6, 0x4c, 0x91, 0x4e, 0xe3, 0x4f, 0xe0, 0x52,
+0xe8, 0x53, 0x97, 0x55, 0xe9, 0x59, 0xed, 0x5a, 0x8d, 0x20, 0x27, 0x2b, 0x05, 0x6f, 0x8b, 0x75,
+0xfb, 0x4f, 0x8a, 0x55, 0xeb, 0x20, 0x2b, 0x22, 0x09, 0x61, 0x84, 0x65, 0x89, 0x6f, 0x94, 0x75,
+0x81, 0x41, 0x8e, 0x45, 0xd3, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0xf7, 0x07, 0x63, 0x87, 0x73,
+0xad, 0x74, 0xee, 0x43, 0x80, 0x53, 0xb8, 0x54, 0xdd, 0x20, 0xf7, 0x00, 0x02, 0x01, 0x03, 0x00,
+0xcc, 0x03, 0x01, 0x03, 0x00, 0xce, 0x04, 0x01, 0x03, 0x00, 0xcd, 0x0a, 0x02, 0x07, 0x00, 0x00,
+0xcc, 0x0b, 0x42, 0x07, 0x00, 0x00, 0xcd, 0x0c, 0x42, 0x07, 0x00, 0x00, 0xce, 0x0d, 0x42, 0x05,
+0x00, 0x95, 0xcf, 0x17, 0x42, 0x03, 0x00, 0x00, 0x8d, 0x19, 0x42, 0x03, 0x00, 0x00, 0x9f, 0x1a,
+0x41, 0x00, 0x93, 0xa7, 0x1b, 0x42, 0x05, 0x00, 0x97, 0xa0, 0x28, 0x41, 0x01, 0x00, 0x8f, 0x29,
+0x01, 0x01, 0x00, 0x15, 0x2b, 0x42, 0x04, 0x96, 0x98, 0xa0, 0x56, 0x41, 0x01, 0x00, 0x8d, 0x00,
+0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x04, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c,
+0x20, 0x5e, 0xf8, 0x02, 0x61, 0x86, 0x20, 0xf8, 0x60, 0x03, 0x61, 0x85, 0x65, 0x8a, 0x20, 0x60,
+0x27, 0x0b, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x41, 0x8f, 0x45, 0x90,
+0x49, 0x8d, 0x4f, 0x95, 0x55, 0x97, 0x20, 0x27, 0x2b, 0x05, 0x6f, 0x93, 0x75, 0x96, 0x4f, 0xa7,
+0x55, 0x98, 0x20, 0x2b, 0x22, 0x09, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81,
+0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x2c, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0x2c,
+0x00, 0x2d, 0x83, 0x03, 0x09, 0x00, 0x00, 0x49, 0x54, 0x2c, 0x8d, 0x00, 0x49, 0x54, 0x08, 0x04,
+0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xfb, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x01, 0xf5, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x54, 0x03, 0x16, 0x02, 0x59, 0x02, 0x00, 0x00, 0x65, 0x03, 0x6a, 0x02, 0x00, 0x00,
+0x00, 0x00, 0x65, 0x03, 0x93, 0x02, 0x44, 0x03, 0x00, 0x00, 0x37, 0x75, 0x08, 0x01, 0x81, 0x01,
+0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x02, 0x01, 0x00, 0x9c, 0x23, 0x07, 0x01, 0x01,
+0x00, 0x26, 0x08, 0x02, 0x01, 0x00, 0x2f, 0x7b, 0x09, 0x04, 0x09, 0x00, 0x28, 0x5b, 0x00, 0x1b,
+0x0a, 0x04, 0x09, 0x00, 0x29, 0x5d, 0x00, 0x1d, 0x0b, 0x02, 0x01, 0x00, 0x3d, 0x7d, 0x0c, 0x01,
+0x00, 0x27, 0x3f, 0x0d, 0x04, 0x08, 0x8d, 0x5e, 0x7e, 0x00, 0x1e, 0x10, 0x42, 0x03, 0x00, 0x00,
+0x40, 0x1a, 0x03, 0x00, 0x8a, 0x82, 0x5b, 0x7b, 0x1b, 0x03, 0x00, 0x2b, 0x2a, 0x5d, 0x7d, 0x27,
+0x02, 0x00, 0x95, 0x87, 0x40, 0x28, 0x02, 0x00, 0x85, 0xf8, 0x23, 0x29, 0x04, 0x0c, 0x5c, 0x7c,
+0x00, 0x00, 0x1c, 0x2b, 0x02, 0x00, 0x97, 0xf5, 0x60, 0x33, 0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34,
+0x02, 0x01, 0x00, 0x3a, 0x3e, 0x35, 0x04, 0x0c, 0x2d, 0x5f, 0x00, 0x00, 0x1f, 0x56, 0x01, 0x00,
+0x3c, 0x3e, 0x00, 0x2b, 0x01, 0x01, 0x00, 0x15, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x12,
+0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x03, 0x01, 0x03, 0x00, 0xc8, 0x04, 0x01, 0x03, 0x00, 0xa0,
+0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x0c, 0x00, 0x01, 0xc9, 0x0d, 0x01, 0x03, 0x00, 0xca, 0x12,
+0x42, 0x00, 0x65, 0x45, 0xd5, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17,
+0x41, 0x00, 0x69, 0x49, 0x18, 0x43, 0x00, 0x6f, 0x4f, 0xab, 0xac, 0x1e, 0x43, 0x00, 0x61, 0x41,
+0x91, 0x92, 0x1f, 0x42, 0x03, 0x00, 0x00, 0xe1, 0x26, 0x43, 0x03, 0x00, 0x00, 0xf2, 0xf6, 0x28,
+0x01, 0x03, 0x00, 0xcc, 0x29, 0x02, 0x03, 0x00, 0x00, 0xfa, 0x2b, 0x02, 0x07, 0x00, 0xcd, 0xcb,
+0x2c, 0x42, 0x03, 0x00, 0x00, 0xae, 0x2d, 0x42, 0x03, 0x00, 0x00, 0xaf, 0x2e, 0x43, 0x03, 0x00,
+0x00, 0x87, 0x80, 0x31, 0x43, 0x03, 0x00, 0x00, 0xa4, 0xa5, 0x39, 0x00, 0x00, 0x20, 0x00, 0x22,
+0x0d, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45,
+0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x27, 0x0b, 0x61, 0xa0, 0x65,
+0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55,
+0xe9, 0x20, 0x27, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41,
+0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x60, 0x0b, 0x61, 0x85, 0x65,
+0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55,
+0xeb, 0x20, 0x60, 0xf8, 0x0b, 0x61, 0xc6, 0x65, 0xbd, 0x69, 0x9b, 0x6f, 0x9e, 0x75, 0xa9, 0x41,
+0xc7, 0x45, 0xbe, 0x49, 0x9d, 0x4f, 0x9f, 0x55, 0xb8, 0x20, 0xf8, 0xf5, 0x0b, 0x61, 0xd0, 0x65,
+0xdd, 0x69, 0x9c, 0x6f, 0xe7, 0x75, 0xef, 0x41, 0xd1, 0x45, 0xf9, 0x49, 0xcf, 0x4f, 0xe8, 0x55,
+0xf7, 0x20, 0xf5, 0x00, 0x04, 0x01, 0x03, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00, 0x00, 0xaa, 0x0d,
+0x01, 0x03, 0xa0, 0xc8, 0x12, 0x42, 0x03, 0x00, 0x00, 0xaa, 0x1a, 0x00, 0x01, 0xa0, 0x1f, 0x41,
+0x00, 0x73, 0x53, 0x20, 0x43, 0x00, 0x64, 0x44, 0xd0, 0xd1, 0x27, 0x00, 0x01, 0xa0, 0x28, 0x00,
+0x01, 0xa0, 0x2b, 0x00, 0x01, 0xa0, 0x2c, 0x41, 0x00, 0x7a, 0x5a, 0x2e, 0x43, 0x00, 0x63, 0x43,
+0x86, 0x8f, 0x39, 0x00, 0x00, 0x20, 0x00, 0x5e, 0x07, 0x63, 0x9f, 0x73, 0xe7, 0x7a, 0xa7, 0x43,
+0xac, 0x53, 0xe6, 0x5a, 0xa6, 0x20, 0x5e, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0x87, 0x0d, 0x00,
+0x01, 0xa0, 0x12, 0x02, 0x03, 0x00, 0x00, 0x87, 0x27, 0x01, 0x03, 0xa0, 0xa0, 0x28, 0x01, 0x03,
+0xa0, 0xa0, 0x2b, 0x01, 0x03, 0xa0, 0xa0, 0x36, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d,
+0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0x87, 0x0d, 0x00, 0x01, 0xa0, 0x10, 0x01, 0x00, 0x8b, 0x8c,
+0x11, 0x01, 0x02, 0xed, 0xa0, 0x12, 0x42, 0x00, 0xde, 0xa8, 0x87, 0x13, 0x41, 0x00, 0xeb, 0xc7,
+0x14, 0x41, 0x00, 0xee, 0xd0, 0x15, 0x41, 0x00, 0xf2, 0xd1, 0x16, 0x41, 0x00, 0xe2, 0xac, 0x17,
+0x41, 0x00, 0xe3, 0xad, 0x18, 0x41, 0x00, 0xe9, 0xbe, 0x19, 0x41, 0x00, 0xea, 0xc6, 0x1a, 0x01,
+0x03, 0xa0, 0xa0, 0x1e, 0x41, 0x00, 0xd6, 0xa4, 0x1f, 0x41, 0x00, 0xec, 0xcf, 0x20, 0x41, 0x00,
+0xdd, 0xa7, 0x21, 0x41, 0x00, 0xf3, 0xd2, 0x22, 0x41, 0x00, 0xd8, 0xa6, 0x23, 0x41, 0x00, 0xe1,
+0xaa, 0x24, 0x41, 0x00, 0xe8, 0xbd, 0x25, 0x41, 0x00, 0xe4, 0xb5, 0x26, 0x41, 0x00, 0xe5, 0xb6,
+0x27, 0x01, 0x03, 0xc8, 0xa0, 0x28, 0x01, 0x03, 0xc9, 0xa0, 0x2a, 0x05, 0x3f, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0x2b, 0x01, 0x03, 0xca, 0xa0, 0x2c, 0xc1, 0x00, 0xe0, 0x00, 0xa9, 0x2c, 0x2d,
+0x41, 0x00, 0xf4, 0xd3, 0x2e, 0x41, 0x00, 0xf6, 0xd4, 0x2f, 0x41, 0x00, 0xfa, 0xd5, 0x30, 0x41,
+0x00, 0xd7, 0xa5, 0x31, 0x41, 0x00, 0xe7, 0xb8, 0x32, 0x41, 0x00, 0xe6, 0xb7, 0x39, 0x00, 0x00,
+0x20, 0x00, 0xef, 0x0f, 0xd6, 0x9b, 0xde, 0x9d, 0xe1, 0x9e, 0xe3, 0x9f, 0xe9, 0xa2, 0xf2, 0xa3,
+0xfa, 0xfd, 0xa4, 0x86, 0xa8, 0x8d, 0xaa, 0x8f, 0xad, 0x90, 0xbe, 0x92, 0xd1, 0x95, 0xd5, 0x98,
+0x20, 0xef, 0xf9, 0x05, 0xe3, 0xa0, 0xf2, 0xfb, 0xad, 0x91, 0xd1, 0x96, 0x20, 0xf9, 0xf7, 0x03,
+0xe3, 0xa1, 0xf2, 0xfc, 0x20, 0xf7, 0x00, 0x6e, 0x79, 0x03, 0x04, 0x8e, 0x00, 0x49, 0x54, 0x08,
+0x04, 0x2c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xf6, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x01, 0xf0, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x54, 0x03, 0x11, 0x02, 0x54, 0x02, 0x00, 0x00, 0x65, 0x03, 0x65, 0x02, 0x00,
+0x00, 0x00, 0x00, 0x65, 0x03, 0x8e, 0x02, 0x3f, 0x03, 0x00, 0x00, 0x37, 0x75, 0x03, 0x01, 0x7c,
+0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x02, 0x01, 0x00, 0x9c, 0x23, 0x07, 0x01,
+0x01, 0x00, 0x26, 0x08, 0x02, 0x01, 0x00, 0x2f, 0x7b, 0x09, 0x04, 0x09, 0x00, 0x28, 0x5b, 0x00,
+0x1b, 0x0a, 0x04, 0x09, 0x00, 0x29, 0x5d, 0x00, 0x1d, 0x0b, 0x02, 0x01, 0x00, 0x3d, 0x7d, 0x0c,
+0x01, 0x00, 0x27, 0x3f, 0x0d, 0x04, 0x0c, 0x8d, 0x5e, 0x00, 0x00, 0x1e, 0x10, 0x42, 0x03, 0x00,
+0x00, 0x40, 0x1a, 0x01, 0x00, 0x8a, 0x82, 0x1b, 0x02, 0x00, 0x2b, 0x2a, 0x7e, 0x27, 0x01, 0x00,
+0x95, 0x87, 0x28, 0x01, 0x00, 0x85, 0xf8, 0x29, 0x04, 0x0c, 0x5c, 0x7c, 0x00, 0x00, 0x1c, 0x2b,
+0x02, 0x00, 0x97, 0xf5, 0x60, 0x33, 0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34, 0x02, 0x01, 0x00, 0x3a,
+0x3e, 0x35, 0x04, 0x0c, 0x2d, 0x5f, 0x00, 0x00, 0x1f, 0x56, 0x01, 0x00, 0x3c, 0x3e, 0x00, 0x2b,
+0x01, 0x01, 0x00, 0x15, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x12, 0x42, 0x03, 0x00, 0x00,
+0xd5, 0x00, 0x03, 0x01, 0x03, 0x00, 0xc8, 0x04, 0x01, 0x03, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00,
+0x00, 0xd5, 0x0c, 0x00, 0x01, 0xc9, 0x0d, 0x01, 0x03, 0x00, 0xca, 0x12, 0x42, 0x00, 0x65, 0x45,
+0xd5, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49,
+0x18, 0x43, 0x00, 0x6f, 0x4f, 0xab, 0xac, 0x1e, 0x43, 0x00, 0x61, 0x41, 0x91, 0x92, 0x1f, 0x42,
+0x03, 0x00, 0x00, 0xe1, 0x26, 0x43, 0x03, 0x00, 0x00, 0xf2, 0xf6, 0x28, 0x01, 0x03, 0x00, 0xcc,
+0x29, 0x02, 0x03, 0x00, 0x00, 0xfa, 0x2b, 0x02, 0x07, 0x00, 0xcd, 0xcb, 0x2c, 0x42, 0x03, 0x00,
+0x00, 0xae, 0x2d, 0x42, 0x03, 0x00, 0x00, 0xaf, 0x2e, 0x43, 0x03, 0x00, 0x00, 0x87, 0x80, 0x31,
+0x43, 0x03, 0x00, 0x00, 0xa4, 0xa5, 0x39, 0x00, 0x00, 0x20, 0x00, 0x22, 0x0d, 0x61, 0x84, 0x65,
+0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f,
+0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x27, 0x0b, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f,
+0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x20, 0x27, 0x5e,
+0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49,
+0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f,
+0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0xf8,
+0x0b, 0x61, 0xc6, 0x65, 0xbd, 0x69, 0x9b, 0x6f, 0x9e, 0x75, 0xa9, 0x41, 0xc7, 0x45, 0xbe, 0x49,
+0x9d, 0x4f, 0x9f, 0x55, 0xb8, 0x20, 0xf8, 0xf5, 0x0b, 0x61, 0xd0, 0x65, 0xdd, 0x69, 0x9c, 0x6f,
+0xe7, 0x75, 0xef, 0x41, 0xd1, 0x45, 0xf9, 0x49, 0xcf, 0x4f, 0xe8, 0x55, 0xf7, 0x20, 0xf5, 0x00,
+0x04, 0x01, 0x03, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00, 0x00, 0xaa, 0x0d, 0x01, 0x03, 0xa0, 0xc8,
+0x12, 0x42, 0x03, 0x00, 0x00, 0xaa, 0x1a, 0x00, 0x01, 0xa0, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x20,
+0x43, 0x00, 0x64, 0x44, 0xd0, 0xd1, 0x27, 0x00, 0x01, 0xa0, 0x28, 0x00, 0x01, 0xa0, 0x2b, 0x00,
+0x01, 0xa0, 0x2c, 0x41, 0x00, 0x7a, 0x5a, 0x2e, 0x43, 0x00, 0x63, 0x43, 0x86, 0x8f, 0x39, 0x00,
+0x00, 0x20, 0x00, 0x5e, 0x07, 0x63, 0x9f, 0x73, 0xe7, 0x7a, 0xa7, 0x43, 0xac, 0x53, 0xe6, 0x5a,
+0xa6, 0x20, 0x5e, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0x87, 0x0d, 0x00, 0x01, 0xa0, 0x12, 0x02,
+0x03, 0x00, 0x00, 0x87, 0x27, 0x01, 0x03, 0xa0, 0xa0, 0x28, 0x01, 0x03, 0xa0, 0xa0, 0x2b, 0x01,
+0x03, 0xa0, 0xa0, 0x36, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x06, 0x02, 0x03,
+0x00, 0x00, 0x87, 0x0d, 0x00, 0x01, 0xa0, 0x10, 0x01, 0x00, 0x8b, 0x8c, 0x11, 0x01, 0x02, 0xed,
+0xa0, 0x12, 0x42, 0x00, 0xde, 0xa8, 0x87, 0x13, 0x41, 0x00, 0xeb, 0xc7, 0x14, 0x41, 0x00, 0xee,
+0xd0, 0x15, 0x41, 0x00, 0xf2, 0xd1, 0x16, 0x41, 0x00, 0xe2, 0xac, 0x17, 0x41, 0x00, 0xe3, 0xad,
+0x18, 0x41, 0x00, 0xe9, 0xbe, 0x19, 0x41, 0x00, 0xea, 0xc6, 0x1a, 0x01, 0x03, 0xa0, 0xa0, 0x1e,
+0x41, 0x00, 0xd6, 0xa4, 0x1f, 0x41, 0x00, 0xec, 0xcf, 0x20, 0x41, 0x00, 0xdd, 0xa7, 0x21, 0x41,
+0x00, 0xf3, 0xd2, 0x22, 0x41, 0x00, 0xd8, 0xa6, 0x23, 0x41, 0x00, 0xe1, 0xaa, 0x24, 0x41, 0x00,
+0xe8, 0xbd, 0x25, 0x41, 0x00, 0xe4, 0xb5, 0x26, 0x41, 0x00, 0xe5, 0xb6, 0x27, 0x01, 0x03, 0xc8,
+0xa0, 0x28, 0x01, 0x03, 0xc9, 0xa0, 0x2a, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x2b,
+0x01, 0x03, 0xca, 0xa0, 0x2c, 0xc1, 0x00, 0xe0, 0x00, 0xa9, 0x2c, 0x2d, 0x41, 0x00, 0xf4, 0xd3,
+0x2e, 0x41, 0x00, 0xf6, 0xd4, 0x2f, 0x41, 0x00, 0xfa, 0xd5, 0x30, 0x41, 0x00, 0xd7, 0xa5, 0x31,
+0x41, 0x00, 0xe7, 0xb8, 0x32, 0x41, 0x00, 0xe6, 0xb7, 0x39, 0x00, 0x00, 0x20, 0x00, 0xef, 0x0f,
+0xd6, 0x9b, 0xde, 0x9d, 0xe1, 0x9e, 0xe3, 0x9f, 0xe9, 0xa2, 0xf2, 0xa3, 0xfa, 0xfd, 0xa4, 0x86,
+0xa8, 0x8d, 0xaa, 0x8f, 0xad, 0x90, 0xbe, 0x92, 0xd1, 0x95, 0xd5, 0x98, 0x20, 0xef, 0xf9, 0x05,
+0xe3, 0xa0, 0xf2, 0xfb, 0xad, 0x91, 0xd1, 0x96, 0x20, 0xf9, 0xf7, 0x03, 0xe3, 0xa1, 0xf2, 0xfc,
+0x20, 0xf7, 0x00, 0x8c, 0x23, 0x02, 0x0c, 0x00, 0x00, 0x4a, 0x50, 0x2c, 0x00, 0x00, 0x4a, 0x50,
+0x31, 0x30, 0x36, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4,
+0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x0c, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0c, 0x00, 0x80, 0x00, 0x00, 0x00, 0x04,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x08,
+0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0b, 0x00, 0x00, 0xc7, 0xa0, 0x03, 0x03,
+0x09, 0x00, 0x22, 0xcc, 0xa0, 0x04, 0x03, 0x03, 0x00, 0x00, 0xb1, 0xa7, 0x05, 0x03, 0x03, 0x00,
+0x00, 0xb3, 0xa9, 0x06, 0x03, 0x03, 0x00, 0x00, 0xb4, 0xaa, 0x07, 0x03, 0x01, 0x00, 0x26, 0xb5,
+0xab, 0x08, 0x03, 0x01, 0x00, 0x27, 0xd4, 0xac, 0x09, 0x03, 0x01, 0x00, 0x28, 0xd5, 0xad, 0x0a,
+0x03, 0x01, 0x00, 0x29, 0xd6, 0xae, 0x0b, 0x03, 0x01, 0x00, 0x7e, 0xdc, 0xa6, 0x0c, 0x03, 0x09,
+0x00, 0x3d, 0xce, 0xa0, 0x0d, 0x04, 0x08, 0x5e, 0x7e, 0xcd, 0xa0, 0x1e, 0x10, 0x43, 0x0b, 0x00,
+0x00, 0xc0, 0xa0, 0x11, 0x43, 0x0b, 0x00, 0x00, 0xc3, 0xa0, 0x12, 0x43, 0x03, 0x00, 0x00, 0xb2,
+0xa8, 0x13, 0x43, 0x0b, 0x00, 0x00, 0xbd, 0xa0, 0x14, 0x43, 0x0b, 0x00, 0x00, 0xb6, 0xa0, 0x15,
+0x43, 0x0b, 0x00, 0x00, 0xdd, 0xa0, 0x16, 0x43, 0x0b, 0x00, 0x00, 0xc5, 0xa0, 0x17, 0x43, 0x0b,
+0x00, 0x00, 0xc6, 0xa0, 0x18, 0x43, 0x0b, 0x00, 0x00, 0xd7, 0xa0, 0x19, 0x43, 0x0b, 0x00, 0x00,
+0xbe, 0xa0, 0x1a, 0x84, 0x08, 0x40, 0x1a, 0x60, 0x1a, 0xde, 0x1a, 0xa0, 0x1a, 0x00, 0x03, 0x1b,
+0x04, 0x00, 0x5b, 0x7b, 0xdf, 0xa2, 0x1b, 0x1e, 0x43, 0x0b, 0x00, 0x00, 0xc1, 0xa0, 0x1f, 0x43,
+0x0b, 0x00, 0x00, 0xc4, 0xa0, 0x20, 0x43, 0x0b, 0x00, 0x00, 0xbc, 0xa0, 0x21, 0x43, 0x0b, 0x00,
+0x00, 0xca, 0xa0, 0x22, 0x43, 0x0b, 0x00, 0x00, 0xb7, 0xa0, 0x23, 0x43, 0x0b, 0x00, 0x00, 0xb8,
+0xa0, 0x24, 0x43, 0x0b, 0x00, 0x00, 0xcf, 0xa0, 0x25, 0x43, 0x0b, 0x00, 0x00, 0xc9, 0xa0, 0x26,
+0x43, 0x0b, 0x00, 0x00, 0xd8, 0xa0, 0x27, 0x03, 0x09, 0x00, 0x2b, 0xda, 0xa0, 0x28, 0x03, 0x08,
+0x3a, 0x2a, 0xb9, 0xa0, 0x29, 0x86, 0x00, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xb0, 0x00,
+0xb1, 0x00, 0xb2, 0x00, 0xb2, 0x2b, 0x04, 0x00, 0x5d, 0x7d, 0xd1, 0xa3, 0x1d, 0x2c, 0x43, 0x03,
+0x00, 0x00, 0xc2, 0xaf, 0x2d, 0x43, 0x0b, 0x00, 0x00, 0xbb, 0xa0, 0x2e, 0x43, 0x0b, 0x00, 0x00,
+0xbf, 0xa0, 0x2f, 0x43, 0x0b, 0x00, 0x00, 0xcb, 0xa0, 0x30, 0x43, 0x0b, 0x00, 0x00, 0xba, 0xa0,
+0x31, 0x43, 0x0b, 0x00, 0x00, 0xd0, 0xa0, 0x32, 0x43, 0x0b, 0x00, 0x00, 0xd3, 0xa0, 0x33, 0x03,
+0x03, 0x00, 0x00, 0xc8, 0xa4, 0x34, 0x03, 0x03, 0x00, 0x00, 0xd9, 0xa1, 0x35, 0x03, 0x03, 0x00,
+0x00, 0xd2, 0xa5, 0x3a, 0x86, 0x0a, 0x00, 0xb3, 0x00, 0x3a, 0x00, 0xb3, 0x00, 0x3a, 0x00, 0xb4,
+0x00, 0xb5, 0x00, 0xb5, 0x70, 0x86, 0x60, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb6, 0x00, 0xb7, 0x00,
+0xb8, 0xc3, 0x00, 0xbb, 0x00, 0x73, 0x04, 0x00, 0x5c, 0x5f, 0xdb, 0x7c, 0x1c, 0x79, 0x86, 0x00,
+0x00, 0xa7, 0x00, 0xa8, 0x00, 0xa7, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x7b, 0x86,
+0x00, 0x00, 0xab, 0x00, 0xac, 0x00, 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x7d,
+0x04, 0x08, 0x5c, 0x7c, 0xb0, 0xa0, 0x1c, 0xba, 0x86, 0x7f, 0x00, 0xb3, 0x00, 0x3a, 0xbb, 0x00,
+0x00, 0x3a, 0x00, 0xb4, 0x00, 0xb5, 0x00, 0xb5, 0x00, 0xd5, 0xb3, 0x04, 0x09, 0x00, 0x00, 0x4c,
+0x41, 0x2c, 0xab, 0x00, 0x4c, 0x41, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x5a, 0x03, 0x43, 0x01, 0x4a, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x4a, 0x01,
+0x00, 0x00, 0xb5, 0x01, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x33, 0x75, 0xb5, 0x01, 0x09, 0x02,
+0x00, 0x00, 0x4d, 0x75, 0xaa, 0x02, 0x15, 0x03, 0x00, 0x00, 0x4e, 0x75, 0xaa, 0x03, 0x02, 0x04,
+0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x07,
+0x01, 0x01, 0x00, 0x26, 0x08, 0x01, 0x01, 0x00, 0x2f, 0x09, 0x01, 0x01, 0x00, 0x28, 0x0a, 0x01,
+0x01, 0x00, 0x29, 0x0b, 0x01, 0x01, 0x00, 0x3d, 0x0c, 0x04, 0x08, 0x27, 0x3f, 0x5c, 0x00, 0x1c,
+0x0d, 0x01, 0x00, 0xa8, 0xad, 0x10, 0x02, 0x03, 0x00, 0x00, 0x40, 0x12, 0x41, 0x00, 0x65, 0x45,
+0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18,
+0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x01, 0x03, 0xca, 0xcb, 0x1b, 0x02, 0x00, 0x2b, 0x2a, 0x7e, 0x1e,
+0x41, 0x00, 0x61, 0x41, 0x27, 0x41, 0x00, 0xa4, 0xa5, 0x28, 0x04, 0x0c, 0x7b, 0x5b, 0xc9, 0x00,
+0x1b, 0x29, 0x02, 0x00, 0x7c, 0xf8, 0xaa, 0x2b, 0x04, 0x0c, 0x7d, 0x5d, 0xc8, 0x00, 0x1d, 0x2e,
+0x41, 0x00, 0x63, 0x43, 0x33, 0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34, 0x02, 0x01, 0x00, 0x3a, 0x3e,
+0x35, 0x04, 0x0c, 0x2d, 0x5f, 0x00, 0x00, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56, 0x01, 0x00, 0x3c,
+0x3e, 0x00, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60,
+0x5e, 0x06, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x27, 0x09,
+0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x43, 0x80, 0x45, 0x90,
+0x20, 0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98,
+0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00,
+0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4,
+0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c,
+0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e,
+0xef, 0x0f, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec,
+0x41, 0xb5, 0x43, 0x80, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef,
+0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e,
+0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00,
+0xcf, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x0d, 0x02, 0x03, 0x00, 0x00, 0x9c, 0x12, 0x03, 0x03,
+0x00, 0x00, 0xbd, 0xbe, 0x17, 0x03, 0x03, 0x00, 0x00, 0x9e, 0x9f, 0x1b, 0x02, 0x07, 0x00, 0x00,
+0xcc, 0x1e, 0x03, 0x03, 0x00, 0x00, 0xf4, 0xf6, 0x1f, 0x43, 0x00, 0x73, 0x53, 0xfb, 0xe6, 0x22,
+0x41, 0x00, 0x67, 0x47, 0x26, 0x03, 0x03, 0x00, 0x00, 0xfc, 0xfd, 0x2c, 0x42, 0x00, 0x7a, 0x5a,
+0xae, 0x2d, 0x42, 0x00, 0x78, 0x58, 0xaf, 0x31, 0x43, 0x00, 0x6e, 0x4e, 0xe1, 0xee, 0x00, 0x60,
+0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49,
+0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x5e, 0x13, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f,
+0x93, 0x75, 0x96, 0x63, 0xab, 0x73, 0xf9, 0x7a, 0xf7, 0x78, 0xb8, 0x41, 0xb6, 0x45, 0xd2, 0x49,
+0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x43, 0xac, 0x53, 0xdd, 0x5a, 0xef, 0x58, 0xa9, 0x20, 0x5e, 0x27,
+0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45,
+0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0x27, 0x22, 0x0f, 0x61, 0x84, 0x65,
+0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x6e, 0xa6, 0x78, 0xf1, 0x41, 0x8e, 0x45, 0xd3, 0x49,
+0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x4e, 0xa7, 0x58, 0xf2, 0x20, 0x22, 0x7e, 0x11, 0x61, 0xc6, 0x65,
+0x86, 0x69, 0x91, 0x6f, 0xe4, 0x75, 0x9b, 0x67, 0xd0, 0x6e, 0xa4, 0x79, 0xe7, 0x41, 0xc7, 0x45,
+0x8f, 0x49, 0x92, 0x4f, 0xe5, 0x55, 0x9d, 0x47, 0xd1, 0x4e, 0xa5, 0x59, 0xe8, 0x20, 0x7e, 0x00,
+0x04, 0x02, 0x03, 0x00, 0x00, 0xfa, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x11, 0x03, 0x03, 0x00,
+0x00, 0xf7, 0xef, 0x12, 0x03, 0x03, 0x00, 0x00, 0x87, 0x80, 0x13, 0x03, 0x03, 0x00, 0x00, 0xe6,
+0xfb, 0x15, 0x03, 0x03, 0x00, 0x00, 0xf4, 0xf6, 0x16, 0x03, 0x03, 0x00, 0x00, 0xe7, 0xe8, 0x17,
+0x03, 0x03, 0x00, 0x00, 0xf1, 0xf2, 0x18, 0x03, 0x03, 0x00, 0x00, 0x9b, 0x9d, 0x1f, 0x43, 0x00,
+0x73, 0x53, 0xe4, 0xe5, 0x20, 0x03, 0x03, 0x00, 0x00, 0xfc, 0xfd, 0x21, 0x03, 0x03, 0x00, 0x00,
+0xc6, 0xc7, 0x29, 0x01, 0x03, 0xcc, 0xcd, 0x2c, 0x02, 0x03, 0x00, 0x00, 0xae, 0x2d, 0x02, 0x03,
+0x00, 0x00, 0xaf, 0x31, 0x03, 0x03, 0x00, 0x00, 0x9c, 0xcf, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65,
+0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55,
+0xeb, 0x20, 0x60, 0x5e, 0x0b, 0x61, 0x86, 0x65, 0x91, 0x69, 0x98, 0x6f, 0xe1, 0x75, 0x9e, 0x41,
+0x8f, 0x45, 0x92, 0x49, 0xf3, 0x4f, 0xee, 0x55, 0x9f, 0x20, 0x5e, 0x27, 0x0b, 0x61, 0xa0, 0x65,
+0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55,
+0xe9, 0x20, 0x27, 0x22, 0x0d, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x73,
+0xf9, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x53, 0xdd, 0x20, 0x22, 0x7c,
+0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49,
+0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x7c, 0xf8, 0x0b, 0x61, 0xa6, 0x65, 0xab, 0x69, 0xa9, 0x6f,
+0xbd, 0x75, 0xd0, 0x41, 0xa7, 0x45, 0xac, 0x49, 0xb8, 0x4f, 0xbe, 0x55, 0xd1, 0x20, 0xf8, 0x00,
+0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x0c, 0x00, 0x01, 0xcc, 0x0d, 0x02, 0x03, 0x00, 0x00, 0x9c,
+0x12, 0x02, 0x03, 0x00, 0x00, 0x9f, 0x14, 0x02, 0x03, 0x00, 0x00, 0xcf, 0x16, 0x02, 0x03, 0x00,
+0x00, 0xfd, 0x17, 0x02, 0x03, 0x00, 0x00, 0xfb, 0x18, 0x02, 0x03, 0x00, 0x00, 0xfc, 0x19, 0x02,
+0x03, 0x00, 0x00, 0xbe, 0x1e, 0x02, 0x03, 0x00, 0x00, 0x9e, 0x1f, 0x42, 0x00, 0x73, 0x53, 0xe1,
+0x29, 0x01, 0x03, 0xcd, 0xce, 0x2c, 0x42, 0x00, 0x7a, 0x5a, 0xae, 0x2d, 0x02, 0x03, 0x00, 0x00,
+0xaf, 0x2e, 0x02, 0x03, 0x00, 0x00, 0xbd, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d,
+0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60,
+0x5e, 0x0c, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x63, 0x87, 0x73, 0xf9,
+0x7a, 0xf7, 0x43, 0x80, 0x53, 0xdd, 0x5a, 0xef, 0x20, 0x5e, 0xef, 0x0b, 0x61, 0xa0, 0x65, 0x82,
+0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9,
+0x20, 0x27, 0x22, 0x09, 0x61, 0x84, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x49, 0xd8,
+0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x27, 0x0b, 0x61, 0xb6, 0x65, 0xd2, 0x69, 0xd7, 0x6f, 0xe2,
+0x75, 0xea, 0x41, 0xc6, 0x45, 0xc7, 0x49, 0xe4, 0x4f, 0xe5, 0x55, 0xe6, 0x20, 0x27, 0x7c, 0x0b,
+0x61, 0xd0, 0x65, 0x89, 0x69, 0xec, 0x6f, 0xe7, 0x75, 0x98, 0x41, 0xd1, 0x45, 0xd3, 0x49, 0xed,
+0x4f, 0xe8, 0x55, 0xf3, 0x20, 0x7c, 0xf8, 0x0b, 0x61, 0x86, 0x65, 0x91, 0x69, 0x9b, 0x6f, 0xab,
+0x75, 0xa6, 0x41, 0x8f, 0x45, 0x92, 0x49, 0x9d, 0x4f, 0xac, 0x55, 0xa7, 0x20, 0xf8, 0x00, 0xc3,
+0x7d, 0x02, 0x19, 0x00, 0x00, 0x4c, 0x48, 0x2c, 0x00, 0x00, 0x55, 0x53, 0x44, 0x56, 0x4c, 0x2c,
+0x67, 0x00, 0x4c, 0x48, 0x2c, 0x67, 0x00, 0x55, 0x53, 0x44, 0x56, 0x4c, 0x04, 0x03, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0x55, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x52, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x84, 0x00, 0x5b, 0x1a, 0x7b, 0x1a, 0x1b,
+0x1a, 0x00, 0x1a, 0x00, 0x1a, 0x03, 0x84, 0x00, 0x5d, 0x1b, 0x7d, 0x1b, 0x1d, 0x1b, 0x00, 0x1b,
+0x00, 0x1b, 0x04, 0x84, 0x00, 0x2f, 0x35, 0x3f, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x05,
+0xc4, 0x00, 0x70, 0x19, 0x50, 0x19, 0x10, 0x19, 0x00, 0x19, 0x00, 0x19, 0x06, 0xc4, 0x00, 0x66,
+0x21, 0x46, 0x21, 0x06, 0x21, 0x00, 0x21, 0x00, 0x21, 0x07, 0xc4, 0x00, 0x6d, 0x32, 0x4d, 0x32,
+0x0d, 0x32, 0x00, 0x32, 0x00, 0x32, 0x08, 0xc4, 0x00, 0x6c, 0x26, 0x4c, 0x26, 0x0c, 0x26, 0x00,
+0x26, 0x00, 0x26, 0x09, 0xc4, 0x00, 0x6a, 0x24, 0x4a, 0x24, 0x0a, 0x24, 0x00, 0x24, 0x00, 0x24,
+0x0a, 0x84, 0x00, 0x34, 0x05, 0x24, 0x05, 0x00, 0x05, 0x00, 0x7b, 0x00, 0x7b, 0x0b, 0x84, 0x00,
+0x33, 0x04, 0x23, 0x04, 0x00, 0x04, 0x00, 0x7a, 0x00, 0x7a, 0x0c, 0x84, 0x00, 0x32, 0x03, 0x40,
+0x03, 0x00, 0x03, 0x00, 0x79, 0x00, 0x79, 0x0d, 0x84, 0x00, 0x31, 0x02, 0x21, 0x02, 0x00, 0x02,
+0x00, 0x78, 0x00, 0x78, 0x10, 0x84, 0x00, 0x3b, 0x27, 0x3a, 0x27, 0x00, 0x27, 0x00, 0x27, 0x00,
+0x27, 0x11, 0xc4, 0x00, 0x71, 0x10, 0x51, 0x10, 0x11, 0x10, 0x00, 0x10, 0x00, 0x10, 0x12, 0xc4,
+0x00, 0x62, 0x30, 0x42, 0x30, 0x02, 0x30, 0x00, 0x30, 0x00, 0x30, 0x13, 0xc4, 0x00, 0x79, 0x15,
+0x59, 0x15, 0x19, 0x15, 0x00, 0x15, 0x00, 0x15, 0x14, 0xc4, 0x00, 0x75, 0x16, 0x55, 0x16, 0x15,
+0x16, 0x00, 0x16, 0x00, 0x16, 0x15, 0xc4, 0x00, 0x72, 0x13, 0x52, 0x13, 0x12, 0x13, 0x00, 0x13,
+0x00, 0x13, 0x16, 0xc4, 0x00, 0x73, 0x1f, 0x53, 0x1f, 0x13, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x17,
+0xc4, 0x00, 0x6f, 0x18, 0x4f, 0x18, 0x0f, 0x18, 0x00, 0x18, 0x00, 0x18, 0x18, 0x84, 0x00, 0x2e,
+0x34, 0x3e, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x19, 0x84, 0x00, 0x36, 0x07, 0x5e, 0x07,
+0x1e, 0x07, 0x00, 0x7d, 0x00, 0x7d, 0x1a, 0x84, 0x00, 0x35, 0x06, 0x25, 0x06, 0x00, 0x06, 0x00,
+0x7c, 0x00, 0x7c, 0x1b, 0x84, 0x00, 0x3d, 0x0d, 0x2b, 0x0d, 0x00, 0x0d, 0x00, 0x83, 0x00, 0x83,
+0x1e, 0x84, 0x00, 0x2d, 0x0c, 0x5f, 0x0c, 0x1f, 0x0c, 0x00, 0x82, 0x00, 0x82, 0x1f, 0xc4, 0x00,
+0x6b, 0x25, 0x4b, 0x25, 0x0b, 0x25, 0x00, 0x25, 0x00, 0x25, 0x20, 0xc4, 0x00, 0x63, 0x2e, 0x43,
+0x2e, 0x03, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x21, 0xc4, 0x00, 0x64, 0x20, 0x44, 0x20, 0x04, 0x20,
+0x00, 0x20, 0x00, 0x20, 0x22, 0xc4, 0x00, 0x74, 0x14, 0x54, 0x14, 0x14, 0x14, 0x00, 0x14, 0x00,
+0x14, 0x24, 0xc4, 0x00, 0x65, 0x12, 0x45, 0x12, 0x05, 0x12, 0x00, 0x12, 0x00, 0x12, 0x25, 0xc4,
+0x00, 0x61, 0x1e, 0x41, 0x1e, 0x01, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x26, 0xc4, 0x00, 0x7a, 0x2c,
+0x5a, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x27, 0x84, 0x00, 0x38, 0x09, 0x2a, 0x09, 0x00,
+0x09, 0x00, 0x7f, 0x00, 0x7f, 0x28, 0x84, 0x00, 0x37, 0x08, 0x26, 0x08, 0x00, 0x08, 0x00, 0x7e,
+0x00, 0x7e, 0x2c, 0x84, 0x00, 0x27, 0x28, 0x22, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x2e,
+0xc4, 0x00, 0x67, 0x22, 0x47, 0x22, 0x07, 0x22, 0x00, 0x22, 0x00, 0x22, 0x30, 0xc4, 0x00, 0x77,
+0x11, 0x57, 0x11, 0x17, 0x11, 0x00, 0x11, 0x00, 0x11, 0x32, 0xc4, 0x00, 0x69, 0x17, 0x49, 0x17,
+0x09, 0x17, 0x00, 0x17, 0x00, 0x17, 0x34, 0x84, 0x00, 0x30, 0x0b, 0x29, 0x0b, 0x00, 0x0b, 0x00,
+0x81, 0x00, 0x81, 0x35, 0x84, 0x00, 0x39, 0x0a, 0x28, 0x0a, 0x00, 0x0a, 0x00, 0x80, 0x00, 0x80,
+0x00, 0x24, 0xc4, 0x00, 0x65, 0x12, 0x45, 0x12, 0x05, 0x12, 0xd5, 0x12, 0x00, 0x12, 0x00, 0xa0,
+0x19, 0x03, 0x09, 0x00, 0x00, 0x4e, 0x4c, 0x2c, 0x8f, 0x00, 0x4e, 0x4c, 0x05, 0x03, 0x00, 0x00,
+0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xa9, 0x01, 0xb0, 0x01, 0x00, 0x00,
+0x52, 0x03, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb5, 0x01, 0x2c, 0x01, 0x56, 0x01, 0x00, 0x00,
+0x44, 0x75, 0x2f, 0x02, 0x7e, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x02, 0x02, 0x03, 0x00, 0x00, 0xfb, 0x03, 0x02, 0x01, 0x00, 0x22, 0xfd, 0x04, 0x02, 0x03, 0x00,
+0x00, 0xfc, 0x05, 0x02, 0x03, 0x00, 0x00, 0xac, 0x06, 0x02, 0x03, 0x00, 0x00, 0xab, 0x07, 0x02,
+0x01, 0x00, 0x26, 0xf3, 0x08, 0x04, 0x09, 0x00, 0x5f, 0x9c, 0x00, 0x1f, 0x09, 0x02, 0x01, 0x00,
+0x28, 0x7b, 0x0a, 0x02, 0x01, 0x00, 0x29, 0x7d, 0x0b, 0x01, 0x01, 0x00, 0x27, 0x0c, 0x04, 0x08,
+0x2f, 0x3f, 0x5c, 0x00, 0x1c, 0x0d, 0x02, 0x06, 0xf8, 0xc9, 0xcd, 0x12, 0x41, 0x00, 0x65, 0x45,
+0x13, 0x42, 0x03, 0x00, 0x00, 0xf4, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55,
+0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x04, 0x0f, 0xcc, 0xca, 0x00,
+0x00, 0x1e, 0x1b, 0x02, 0x00, 0x2a, 0x7c, 0xdd, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x1f, 0x42, 0x03,
+0x00, 0x00, 0xe1, 0x27, 0x01, 0x00, 0x2b, 0xf1, 0x28, 0x01, 0x03, 0xcb, 0xc8, 0x29, 0x02, 0x00,
+0x40, 0xf5, 0xaa, 0x2b, 0x01, 0x00, 0x3c, 0x3e, 0x2c, 0x42, 0x03, 0x00, 0x00, 0xae, 0x2d, 0x42,
+0x03, 0x00, 0x00, 0xaf, 0x2e, 0x42, 0x00, 0x63, 0x43, 0xbd, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32,
+0x42, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x04, 0x09, 0x00, 0x3b, 0x5b, 0x00, 0x1b, 0x34, 0x02, 0x01,
+0x00, 0x3a, 0xfa, 0x35, 0x04, 0x08, 0x2d, 0x3d, 0x5d, 0x00, 0x1d, 0x39, 0x00, 0x00, 0x20, 0x56,
+0x04, 0x08, 0x5d, 0x5b, 0x7c, 0x00, 0x1d, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x07, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x13, 0x42, 0x03, 0x00, 0x00, 0x14,
+0x1b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x29, 0x01, 0x01, 0x00, 0x15, 0x2e, 0x42, 0x03, 0x00, 0x00,
+0x9b, 0x00, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60,
+0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c,
+0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x27, 0x07, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2,
+0x75, 0xa3, 0x45, 0x90, 0x20, 0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94,
+0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x2c, 0x03, 0x63, 0x87,
+0x43, 0x80, 0x20, 0x2c, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x60, 0x0b, 0x61, 0x85,
+0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3,
+0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5,
+0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96,
+0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0d, 0x61, 0xa0,
+0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6,
+0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b,
+0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a,
+0x20, 0xf9, 0xf7, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0xf7, 0x00, 0x05, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x07, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x10, 0x43, 0x03,
+0x00, 0x00, 0x91, 0x92, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x17, 0x43, 0x03, 0x00, 0x00, 0x9e,
+0x9f, 0x18, 0x43, 0x03, 0x00, 0x00, 0xab, 0xac, 0x19, 0x43, 0x03, 0x00, 0x00, 0x9b, 0x9d, 0x1b,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x1e, 0x43, 0x03, 0x00, 0x00, 0x86, 0x8f, 0x26, 0x43, 0x03, 0x00,
+0x00, 0xe4, 0xe5, 0x2e, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a,
+0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb,
+0x20, 0x60, 0x7e, 0x0b, 0x61, 0x87, 0x65, 0xd0, 0x69, 0xe7, 0x6f, 0xf9, 0x75, 0xf7, 0x41, 0x80,
+0x45, 0xd1, 0x49, 0xe8, 0x4f, 0xdd, 0x55, 0xef, 0x20, 0x7e, 0x5e, 0x0d, 0x61, 0x83, 0x65, 0x88,
+0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x79, 0xbd, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2,
+0x55, 0xea, 0x59, 0xbe, 0x20, 0x5e, 0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2,
+0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed,
+0x20, 0x27, 0x22, 0x0d, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98,
+0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x2c, 0x05,
+0x65, 0xc6, 0x6f, 0xa4, 0x45, 0xc7, 0x4f, 0xa5, 0x20, 0x2c, 0x00, 0x78, 0x50, 0x03, 0x09, 0x00,
+0x00, 0x4e, 0x4f, 0x2c, 0x9b, 0x00, 0x4e, 0x4f, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0x11, 0x01, 0x1e, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00,
+0x1e, 0x01, 0x00, 0x00, 0x61, 0x03, 0x99, 0x01, 0xa4, 0x01, 0x00, 0x00, 0x30, 0x75, 0xf3, 0x01,
+0x00, 0x00, 0x00, 0x00, 0x30, 0x75, 0xa7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00,
+0x22, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00, 0x9c, 0x05, 0x02, 0x01, 0x00, 0xcf, 0x24, 0x07, 0x01,
+0x01, 0x00, 0x26, 0x08, 0x02, 0x01, 0x00, 0x2f, 0x7b, 0x09, 0x04, 0x09, 0x00, 0x28, 0x5b, 0x00,
+0x1b, 0x0a, 0x04, 0x09, 0x00, 0x29, 0x5d, 0x00, 0x1d, 0x0b, 0x02, 0x01, 0x00, 0x3d, 0x7d, 0x0c,
+0x01, 0x00, 0x2b, 0x3f, 0x0d, 0x04, 0x0e, 0x5c, 0xc8, 0xcb, 0x00, 0x1c, 0x12, 0x41, 0x00, 0x65,
+0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49,
+0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x41, 0x00, 0x86, 0x8f, 0x1b, 0x04, 0x0f, 0xcc, 0xca, 0xc9,
+0x00, 0x1e, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x41, 0x00, 0x9b, 0x9d, 0x28, 0x41, 0x00, 0x91,
+0x92, 0x29, 0x01, 0x00, 0x7c, 0xf5, 0x2b, 0x01, 0x00, 0x27, 0x2a, 0x2e, 0x41, 0x00, 0x63, 0x43,
+0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32, 0x42, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x02, 0x01, 0x00, 0x3b,
+0x3c, 0x34, 0x02, 0x01, 0x00, 0x3a, 0x3e, 0x35, 0x04, 0x0c, 0x2d, 0x5f, 0x00, 0x00, 0x1f, 0x39,
+0x00, 0x00, 0x20, 0x56, 0x01, 0x00, 0x3c, 0x3e, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x12,
+0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95,
+0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07,
+0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b,
+0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7,
+0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0f, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1,
+0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x43, 0x80, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0,
+0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94,
+0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9,
+0x00, 0x05, 0x01, 0x01, 0x00, 0xaf, 0x29, 0x01, 0x01, 0x00, 0x15, 0x00, 0x60, 0x06, 0x61, 0x85,
+0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60, 0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5,
+0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e,
+0x27, 0x09, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x43, 0x80,
+0x45, 0x90, 0x20, 0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81,
+0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0x04, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x09, 0x03, 0x07, 0x00, 0x00, 0x00, 0xae, 0x0a, 0x03,
+0x07, 0x00, 0x00, 0x00, 0xaf, 0x0d, 0x02, 0x07, 0x00, 0xa0, 0xa0, 0x10, 0x43, 0x03, 0x00, 0x00,
+0x83, 0xb6, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x14, 0x43, 0x03, 0x00, 0x00, 0xab, 0x97, 0x17,
+0x43, 0x03, 0x00, 0x00, 0x8b, 0xd8, 0x18, 0x43, 0x03, 0x00, 0x00, 0xe4, 0xe5, 0x1b, 0x02, 0x07,
+0xa0, 0xa0, 0xa0, 0x1e, 0x43, 0x03, 0x00, 0x00, 0xa0, 0xb5, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xaa,
+0x96, 0x20, 0x43, 0x03, 0x00, 0x00, 0x9f, 0x8d, 0x21, 0x43, 0x03, 0x00, 0x00, 0x90, 0x80, 0x22,
+0x43, 0x03, 0x00, 0x00, 0xa1, 0xa2, 0x23, 0x43, 0x03, 0x00, 0x00, 0x85, 0x82, 0x25, 0x43, 0x03,
+0x00, 0x00, 0xa6, 0xa7, 0x27, 0x43, 0x03, 0x00, 0x00, 0x94, 0x99, 0x28, 0x43, 0x03, 0x00, 0x00,
+0x84, 0x8e, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xac, 0x98, 0x2e, 0x43, 0x03, 0x00, 0x00, 0x9e, 0x8c,
+0x2f, 0x43, 0x03, 0x00, 0x00, 0x89, 0x8a, 0x30, 0x43, 0x03, 0x00, 0x00, 0x87, 0x88, 0x31, 0x43,
+0x03, 0x00, 0x00, 0xa9, 0x95, 0x36, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x04,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x09, 0x03, 0x07, 0x00, 0x00,
+0x00, 0xae, 0x0a, 0x03, 0x07, 0x00, 0x00, 0x00, 0xaf, 0x0d, 0x02, 0x07, 0x00, 0xa0, 0xa0, 0x10,
+0x43, 0x00, 0xa0, 0xb5, 0x71, 0x51, 0x11, 0x43, 0x00, 0xaa, 0x96, 0x77, 0x57, 0x12, 0x42, 0x03,
+0x00, 0x00, 0xd5, 0x15, 0x43, 0x00, 0xab, 0x97, 0x79, 0x59, 0x17, 0x43, 0x03, 0x00, 0x00, 0x8b,
+0xd8, 0x18, 0x43, 0x03, 0x00, 0x00, 0xe4, 0xe5, 0x1b, 0x42, 0x04, 0xa9, 0x95, 0xa0, 0x1e, 0x43,
+0x03, 0x00, 0x00, 0x83, 0xb6, 0x22, 0x43, 0x03, 0x00, 0x00, 0xa1, 0xa2, 0x23, 0x43, 0x03, 0x00,
+0x00, 0x90, 0x80, 0x25, 0x43, 0x03, 0x00, 0x00, 0xa6, 0xa7, 0x27, 0x43, 0x03, 0x00, 0x00, 0x94,
+0x99, 0x28, 0x43, 0x03, 0x00, 0x00, 0x84, 0x8e, 0x2a, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x7b, 0x2b, 0x43, 0x00, 0x9f, 0x8d, 0x27, 0x2a, 0x2c, 0x43, 0x03, 0x00, 0x00, 0x87, 0x88, 0x2d,
+0x43, 0x00, 0x9e, 0x8c, 0x78, 0x58, 0x56, 0x43, 0x00, 0xac, 0x98, 0x89, 0x8a, 0x00, 0x69, 0x5b,
+0x03, 0x09, 0x00, 0x00, 0x50, 0x4c, 0x2c, 0xc9, 0x01, 0x50, 0x4c, 0x0d, 0x03, 0x00, 0x00, 0x00,
+0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x03, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9c,
+0x02, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0xdf, 0x03, 0x4f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16,
+0x03, 0x4f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x02, 0x4f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x50,
+0x03, 0x69, 0x02, 0x00, 0x00, 0x00, 0x00, 0x50, 0x03, 0x9c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x51,
+0x03, 0x42, 0x03, 0x00, 0x00, 0x00, 0x00, 0x51, 0x03, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x5a,
+0x03, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf,
+0xe3, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+0xc3, 0x00, 0x6f, 0x18, 0x4f, 0x18, 0xa2, 0x18, 0xe0, 0x00, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00,
+0x9b, 0x10, 0x43, 0x03, 0x00, 0x00, 0x84, 0x8e, 0x11, 0x43, 0x03, 0x00, 0x00, 0x82, 0x8a, 0x12,
+0x43, 0x03, 0x00, 0x00, 0x91, 0x90, 0x13, 0x43, 0x03, 0x00, 0x00, 0x89, 0x83, 0x16, 0x43, 0x03,
+0x00, 0x00, 0x81, 0x9a, 0x17, 0x43, 0x03, 0x00, 0x00, 0x94, 0x99, 0x18, 0x43, 0x07, 0x00, 0x00,
+0x00, 0xa3, 0x19, 0x43, 0x03, 0x00, 0x00, 0x93, 0x96, 0x1e, 0x43, 0x03, 0x00, 0x00, 0x86, 0x8f,
+0x1f, 0x43, 0x03, 0x00, 0x00, 0x9e, 0x98, 0x26, 0x43, 0x03, 0x00, 0x00, 0x92, 0x9c, 0x2c, 0x43,
+0x03, 0x00, 0x00, 0xa7, 0xa1, 0x2d, 0x43, 0x03, 0x00, 0x00, 0xa6, 0xa0, 0x2e, 0x43, 0x03, 0x00,
+0x00, 0x8d, 0x95, 0x31, 0x43, 0x03, 0x00, 0x00, 0xa4, 0xa5, 0x00, 0x12, 0x43, 0x03, 0x00, 0x00,
+0xa9, 0xa8, 0x16, 0x42, 0x03, 0x00, 0x00, 0xaa, 0x1e, 0x43, 0x03, 0x00, 0x00, 0xa5, 0xa4, 0x1f,
+0x43, 0x03, 0x00, 0x00, 0x98, 0x97, 0x26, 0x43, 0x03, 0x00, 0x00, 0x88, 0x9d, 0x2c, 0x43, 0x03,
+0x00, 0x00, 0xbe, 0xbd, 0x2d, 0x43, 0x03, 0x00, 0x00, 0xab, 0x8d, 0x2e, 0x43, 0x03, 0x00, 0x00,
+0x86, 0x8f, 0x31, 0x43, 0x03, 0x00, 0x00, 0xe4, 0xe3, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0x9b,
+0x12, 0x43, 0x03, 0x00, 0x00, 0x91, 0x90, 0x18, 0x43, 0x07, 0x00, 0x00, 0x00, 0xa3, 0x1e, 0x43,
+0x03, 0x00, 0x00, 0x86, 0x8f, 0x1f, 0x43, 0x03, 0x00, 0x00, 0x9e, 0x98, 0x26, 0x43, 0x03, 0x00,
+0x00, 0x92, 0x9c, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xa7, 0xa1, 0x2d, 0x43, 0x03, 0x00, 0x00, 0xa6,
+0xa0, 0x2e, 0x43, 0x03, 0x00, 0x00, 0x8d, 0x95, 0x31, 0x43, 0x03, 0x00, 0x00, 0xa4, 0xa5, 0x00,
+0x16, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x0d, 0x43, 0x00, 0xec, 0x9c, 0x3d, 0x2b, 0x10, 0x41,
+0x00, 0xef, 0x9f, 0x11, 0x41, 0x00, 0xe8, 0x98, 0x12, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x13, 0xc1,
+0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xa7, 0x87, 0x16,
+0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xf7, 0xf6, 0x18, 0x43, 0x0c, 0xae, 0x8e, 0xa0, 0xa0,
+0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x43, 0x00, 0xa6, 0x86, 0x5b, 0x7b, 0x1b, 0x43, 0x00, 0xe7,
+0x97, 0x5d, 0x7d, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00,
+0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00, 0xf3, 0xf2, 0x23, 0x41, 0x00, 0xa3,
+0x83, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b,
+0x27, 0x43, 0x00, 0xf9, 0xf8, 0x3b, 0x3a, 0x28, 0x43, 0x00, 0xe9, 0x99, 0x27, 0x22, 0x29, 0x43,
+0x00, 0xf5, 0xf4, 0x60, 0x7e, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x2b, 0x43, 0x00,
+0xee, 0x9e, 0x5c, 0x7c, 0x2c, 0x41, 0x00, 0xa8, 0x88, 0x2d, 0x41, 0x00, 0xe5, 0x95, 0x2e, 0x41,
+0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00,
+0xad, 0x8d, 0x32, 0x41, 0x00, 0xac, 0x8c, 0x33, 0x02, 0x03, 0x00, 0x00, 0x3b, 0x34, 0x02, 0x03,
+0x00, 0x00, 0x3a, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x7e, 0x00, 0x10, 0x41, 0x00, 0xef, 0x9f, 0x11, 0x41, 0x00, 0xe8, 0x98, 0x12, 0x42, 0x00,
+0xa5, 0x85, 0xfb, 0x13, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15,
+0x41, 0x00, 0xa7, 0x87, 0x16, 0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xf9, 0xf8, 0x18, 0x43,
+0x0c, 0xae, 0x8e, 0xa0, 0xa0, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x43, 0x00, 0xa6, 0x86, 0x5b,
+0x7b, 0x1b, 0x43, 0x00, 0xe7, 0x97, 0x5d, 0x7d, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00,
+0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00, 0xec,
+0x9c, 0x23, 0x41, 0x00, 0xa3, 0x83, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41, 0x00, 0xaa, 0x8a,
+0x26, 0x41, 0x00, 0xab, 0x8b, 0x27, 0x43, 0x00, 0xf7, 0xf6, 0x3b, 0x3a, 0x28, 0x43, 0x00, 0xed,
+0x9d, 0x27, 0x22, 0x29, 0xc3, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0x60, 0x29, 0x7e, 0x29, 0x2a, 0x04,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x2b, 0x43, 0x00, 0xee, 0x9e, 0x5c, 0x7c, 0x2c, 0x41, 0x00,
+0xeb, 0x9b, 0x2d, 0x41, 0x00, 0xe5, 0x95, 0x2e, 0x41, 0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2,
+0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x41, 0x00, 0xac, 0x8c,
+0x33, 0x02, 0x03, 0x00, 0x00, 0x3b, 0x34, 0x02, 0x03, 0x00, 0x00, 0x3a, 0x00, 0x12, 0x42, 0x03,
+0x00, 0x00, 0xfb, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xa4, 0xf5, 0x08, 0x04,
+0xd6, 0x00, 0x50, 0x4c, 0x0d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x54, 0x03, 0x95, 0x03, 0x17, 0x04, 0x00, 0x00, 0x9c, 0x02, 0xd8, 0x04, 0x3e, 0x05, 0x00, 0x00,
+0x5a, 0x03, 0xa7, 0x05, 0xae, 0x05, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0xae, 0x05, 0x00, 0x00,
+0x9b, 0x02, 0x81, 0x01, 0x5a, 0x02, 0x00, 0x00, 0x16, 0x03, 0x81, 0x01, 0x5a, 0x02, 0x00, 0x00,
+0xdf, 0x03, 0xe7, 0x01, 0x5a, 0x02, 0x00, 0x00, 0x50, 0x03, 0x2f, 0x07, 0x00, 0x00, 0x00, 0x00,
+0x50, 0x03, 0x35, 0x06, 0x00, 0x00, 0x00, 0x00, 0x51, 0x03, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x51, 0x03, 0x94, 0x07, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xe3, 0xbd, 0x02, 0x30, 0x03, 0x00, 0x00,
+0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x03, 0x01,
+0x01, 0x00, 0x22, 0x04, 0x03, 0x07, 0x00, 0x00, 0xc9, 0x1e, 0x05, 0x01, 0x01, 0x00, 0xcf, 0x06,
+0x02, 0x07, 0x00, 0x00, 0xca, 0x07, 0x01, 0x01, 0x00, 0x26, 0x08, 0x02, 0x05, 0x00, 0x2f, 0xcb,
+0x09, 0x02, 0x01, 0x00, 0x28, 0xfa, 0x0a, 0x02, 0x05, 0x00, 0x29, 0xcc, 0x0b, 0x01, 0x01, 0x00,
+0x3d, 0x0c, 0x02, 0x04, 0x2b, 0x3f, 0xcd, 0x0d, 0x02, 0x04, 0x27, 0x2a, 0xce, 0x10, 0x42, 0x03,
+0x00, 0x00, 0x5c, 0x11, 0x42, 0x03, 0x00, 0x00, 0x7c, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0xc4,
+0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0x75, 0x55,
+0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x42, 0x03, 0xa0, 0xa0, 0xf6,
+0x1b, 0x43, 0x03, 0xa0, 0xa0, 0x9e, 0x1b, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x21, 0x42, 0x03, 0x00,
+0x00, 0x5b, 0x22, 0x42, 0x03, 0x00, 0x00, 0x5d, 0x27, 0x43, 0x03, 0xa0, 0xa0, 0x24, 0x1d, 0x28,
+0x42, 0x03, 0xa0, 0xa0, 0xe1, 0x29, 0x10, 0x00, 0x00, 0x2b, 0x41, 0x02, 0xa2, 0xa0, 0x2c, 0xc4,
+0x00, 0x79, 0x15, 0x59, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2e, 0x41, 0x00, 0x63, 0x43,
+0x2f, 0x42, 0x03, 0x00, 0x00, 0x40, 0x30, 0x42, 0x03, 0x00, 0x00, 0x7b, 0x31, 0x42, 0x00, 0x6e,
+0x4e, 0x7d, 0x32, 0x42, 0x03, 0x00, 0x00, 0xf5, 0x33, 0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34, 0x02,
+0x01, 0x00, 0x3a, 0x3e, 0x35, 0x03, 0x04, 0x2d, 0x5f, 0x00, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56,
+0x01, 0x00, 0x3c, 0x3e, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7e, 0x04, 0x02, 0x07, 0x00, 0x00,
+0xc8, 0x05, 0x01, 0x03, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00, 0x00, 0xf8, 0x07, 0x02, 0x07, 0x00,
+0x00, 0xc9, 0x08, 0x02, 0x07, 0x00, 0x00, 0xca, 0x09, 0x02, 0x07, 0x00, 0x00, 0xcb, 0x0a, 0x02,
+0x07, 0x00, 0x00, 0xcc, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xcd, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xce,
+0x1a, 0x41, 0x00, 0xa7, 0xa4, 0x1b, 0x42, 0x04, 0x9e, 0x8d, 0xa0, 0x1f, 0x41, 0x00, 0x73, 0x53,
+0x27, 0x41, 0x00, 0x92, 0x9c, 0x28, 0x41, 0x00, 0x86, 0x91, 0x29, 0x01, 0x03, 0xc9, 0xcb, 0x2b,
+0x41, 0x01, 0x00, 0xa6, 0x32, 0x42, 0x03, 0x00, 0x00, 0x15, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00,
+0x7e, 0x04, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x05, 0x01, 0x03, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00,
+0x00, 0xf8, 0x07, 0x02, 0x07, 0x00, 0x00, 0xc9, 0x08, 0x02, 0x07, 0x00, 0x00, 0xca, 0x09, 0x02,
+0x07, 0x00, 0x00, 0xcb, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xcc, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xcd,
+0x0d, 0x02, 0x07, 0x00, 0x00, 0xce, 0x15, 0xc4, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x9b, 0x2c, 0x1a,
+0x2c, 0x00, 0x2c, 0x1a, 0x41, 0x00, 0xa7, 0xa4, 0x1b, 0x42, 0x04, 0x9e, 0x8d, 0xa0, 0x1f, 0x41,
+0x00, 0x73, 0x53, 0x27, 0x41, 0x00, 0x92, 0x9c, 0x28, 0x41, 0x00, 0x86, 0x91, 0x29, 0x01, 0x03,
+0xc9, 0xcb, 0x2b, 0x41, 0x01, 0x00, 0xa6, 0x32, 0x42, 0x03, 0x00, 0x00, 0xa8, 0x00, 0x5e, 0x06,
+0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x7e, 0x05, 0x61, 0x86,
+0x65, 0x91, 0x41, 0x8f, 0x45, 0x90, 0x20, 0x7e, 0x60, 0x04, 0x61, 0x85, 0x65, 0x8a, 0x75, 0x97,
+0x20, 0x60, 0xfa, 0x03, 0x7a, 0xa7, 0x5a, 0xa1, 0x20, 0xfa, 0x27, 0x0c, 0x63, 0x8d, 0x65, 0x82,
+0x6e, 0xa4, 0x6f, 0xa2, 0x73, 0x9e, 0x7a, 0xa6, 0x43, 0x95, 0x4e, 0xa5, 0x4f, 0xa3, 0x53, 0x98,
+0x5a, 0xa0, 0x20, 0x27, 0x22, 0x09, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81,
+0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x2c, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0x2c,
+0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x04, 0x02, 0x07, 0x00, 0x00, 0xc9, 0x05, 0x01, 0x03,
+0x00, 0xa0, 0x06, 0x02, 0x03, 0x00, 0x00, 0xf8, 0x07, 0x02, 0x07, 0x00, 0x00, 0xca, 0x08, 0x02,
+0x07, 0x00, 0x00, 0xcb, 0x09, 0x02, 0x07, 0x00, 0x00, 0xcc, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xcd,
+0x0c, 0x02, 0x07, 0x00, 0x00, 0xce, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xcf, 0x15, 0xc4, 0x00, 0x7a,
+0x2c, 0x5a, 0x2c, 0x9b, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x1a, 0x41, 0x00, 0xa7, 0xa4, 0x1b, 0x42,
+0x04, 0x9e, 0x8d, 0xa0, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x27, 0x41, 0x00, 0x92, 0x9c, 0x28, 0x41,
+0x00, 0x86, 0x91, 0x29, 0x01, 0x03, 0xca, 0xcc, 0x2b, 0x41, 0x01, 0x00, 0xa6, 0x32, 0x42, 0x03,
+0x00, 0x00, 0xa8, 0x00, 0x7e, 0x03, 0x61, 0x84, 0x41, 0x8e, 0x20, 0x7e, 0x5e, 0x05, 0x65, 0x88,
+0x69, 0x8c, 0x6f, 0x93, 0x4f, 0x96, 0x20, 0x5e, 0x2c, 0x05, 0x61, 0x86, 0x65, 0x91, 0x41, 0x8f,
+0x45, 0x90, 0x20, 0x2c, 0x60, 0x06, 0x61, 0x85, 0x6f, 0x94, 0x75, 0x97, 0x4f, 0x99, 0x55, 0x9a,
+0x20, 0x60, 0xfa, 0x03, 0x7a, 0xa7, 0x5a, 0xa1, 0x20, 0xfa, 0x27, 0x0d, 0x63, 0x8d, 0x65, 0x82,
+0x6e, 0xa4, 0x6f, 0xa2, 0x73, 0x9e, 0x7a, 0xa6, 0x43, 0x95, 0x45, 0x8a, 0x4e, 0xa5, 0x4f, 0xa3,
+0x53, 0x98, 0x5a, 0xa0, 0x20, 0x27, 0x22, 0x04, 0x65, 0x89, 0x69, 0x8b, 0x45, 0x83, 0x20, 0x22,
+0x2d, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0x2c, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7e, 0x03,
+0x02, 0x07, 0x00, 0x00, 0xc8, 0x05, 0x02, 0x07, 0x00, 0x00, 0xca, 0x06, 0x02, 0x07, 0x00, 0x00,
+0xcb, 0x07, 0x02, 0x07, 0x00, 0x00, 0xcc, 0x08, 0x02, 0x03, 0x00, 0x00, 0x60, 0x09, 0x02, 0x07,
+0x00, 0x00, 0xcd, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xce, 0x0b, 0x02, 0x07, 0x00, 0x00, 0xcf, 0x0c,
+0x02, 0x07, 0x00, 0x00, 0xd0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xd1, 0x13, 0x41, 0x00, 0x72, 0x52,
+0x14, 0x41, 0x00, 0x74, 0x54, 0x16, 0x42, 0x03, 0x00, 0x00, 0xaa, 0x1a, 0x41, 0x00, 0xbe, 0xe4,
+0x1b, 0x41, 0x00, 0x98, 0x86, 0x1f, 0x42, 0x00, 0x73, 0x53, 0xd0, 0x20, 0x42, 0x00, 0x64, 0x44,
+0xd1, 0x26, 0x41, 0x00, 0x6c, 0x4c, 0x27, 0x41, 0x00, 0x88, 0x9d, 0x28, 0x41, 0x00, 0xa5, 0xa9,
+0x29, 0x01, 0x03, 0xcc, 0xcd, 0x2b, 0x41, 0x01, 0x00, 0xab, 0x00, 0xf3, 0x13, 0x63, 0x9f, 0x64,
+0xd4, 0x65, 0xd8, 0x6c, 0x96, 0x6e, 0xe5, 0x72, 0xfd, 0x73, 0xe7, 0x74, 0x9c, 0x7a, 0xa7, 0x43,
+0xac, 0x44, 0xd2, 0x45, 0xb7, 0x4c, 0x95, 0x4e, 0xd5, 0x52, 0xfc, 0x53, 0xe6, 0x54, 0x9b, 0x5a,
+0xa6, 0x20, 0xf3, 0x5e, 0x07, 0x61, 0x83, 0x69, 0x8c, 0x6f, 0x93, 0x41, 0xb6, 0x49, 0xd7, 0x4f,
+0xe2, 0x20, 0x5e, 0xf4, 0x03, 0x61, 0xc7, 0x41, 0xc6, 0x20, 0xf4, 0xf8, 0x03, 0x75, 0x85, 0x55,
+0xde, 0x20, 0xf8, 0xf2, 0x05, 0x61, 0xa5, 0x65, 0xa9, 0x41, 0xa4, 0x45, 0xa8, 0x20, 0xf2, 0xfa,
+0x03, 0x7a, 0xbe, 0x5a, 0xbd, 0x20, 0xfa, 0xef, 0x19, 0x61, 0xa0, 0x63, 0x86, 0x65, 0x82, 0x69,
+0xa1, 0x6c, 0x92, 0x6e, 0xe4, 0x6f, 0xa2, 0x72, 0xea, 0x73, 0x98, 0x75, 0xa3, 0x79, 0xec, 0x7a,
+0xab, 0x41, 0xb5, 0x43, 0x8f, 0x45, 0x90, 0x49, 0xd6, 0x4c, 0x91, 0x4e, 0xe3, 0x4f, 0xe0, 0x52,
+0xe8, 0x53, 0x97, 0x55, 0xe9, 0x59, 0xed, 0x5a, 0x8d, 0x20, 0xef, 0xf1, 0x05, 0x6f, 0x8b, 0x75,
+0xfb, 0x4f, 0x8a, 0x55, 0xeb, 0x20, 0xf1, 0xf9, 0x09, 0x61, 0x84, 0x65, 0x89, 0x6f, 0x94, 0x75,
+0x81, 0x41, 0x8e, 0x45, 0xd3, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0xf7, 0x07, 0x63, 0x87, 0x73,
+0xad, 0x74, 0xee, 0x43, 0x80, 0x53, 0xb8, 0x54, 0xdd, 0x20, 0xf7, 0x00, 0x02, 0x02, 0x03, 0x00,
+0x00, 0x7e, 0x04, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x05, 0x01, 0x03, 0x00, 0xa0, 0x06, 0x02, 0x03,
+0x00, 0x00, 0xf8, 0x07, 0x02, 0x07, 0x00, 0x00, 0xc9, 0x08, 0x02, 0x07, 0x00, 0x00, 0xca, 0x09,
+0x02, 0x07, 0x00, 0x00, 0xcb, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xcc, 0x0c, 0x02, 0x07, 0x00, 0x00,
+0xcd, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xce, 0x1a, 0x41, 0x00, 0xbe, 0xe4, 0x1b, 0x42, 0x04, 0x98,
+0x86, 0xa0, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x27, 0x41, 0x00, 0x88, 0x9d, 0x28, 0x41, 0x00, 0xa5,
+0xa9, 0x29, 0x01, 0x03, 0xc9, 0xcb, 0x2b, 0x41, 0x01, 0x00, 0xab, 0x32, 0x42, 0x03, 0x00, 0x00,
+0x15, 0x00, 0x5e, 0x05, 0x61, 0x83, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x7e, 0x05,
+0x61, 0xa5, 0x65, 0xa9, 0x41, 0xa4, 0x45, 0xa8, 0x20, 0x7e, 0x60, 0x04, 0x61, 0x85, 0x65, 0x8a,
+0x6f, 0x95, 0x20, 0x60, 0xfa, 0x03, 0x7a, 0xbe, 0x5a, 0xbd, 0x20, 0xfa, 0x27, 0x10, 0x61, 0xa0,
+0x63, 0x86, 0x65, 0x82, 0x69, 0xa1, 0x6e, 0xe4, 0x6f, 0xa2, 0x73, 0x98, 0x75, 0xa3, 0x7a, 0xab,
+0x43, 0x8f, 0x45, 0x90, 0x4e, 0xe3, 0x4f, 0xe0, 0x53, 0x97, 0x5a, 0x8d, 0x20, 0x27, 0x22, 0x09,
+0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a,
+0x20, 0x22, 0x2c, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0x2c, 0x00, 0x16, 0x42, 0x03, 0x00, 0x00,
+0xd5, 0x00, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5,
+0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6,
+0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xf8, 0x03, 0x61, 0x86, 0x41, 0x8f,
+0x20, 0xf8, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7,
+0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82,
+0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0,
+0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94,
+0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9,
+0xf7, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0xf7, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x01, 0x03, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x10, 0x41,
+0x00, 0xef, 0x9f, 0x11, 0x41, 0x00, 0xe8, 0x98, 0x12, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x13, 0xc1,
+0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0xc4, 0x00, 0xa7, 0x2c, 0x87,
+0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xf7,
+0xf6, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x42, 0x04, 0xa6, 0x86,
+0xa0, 0x1b, 0x42, 0x04, 0xe7, 0x97, 0xa0, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1,
+0x91, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00, 0xf3, 0xf2,
+0x23, 0x41, 0x00, 0xa3, 0x83, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26,
+0x41, 0x00, 0xab, 0x8b, 0x27, 0x41, 0x00, 0xf9, 0xf8, 0x28, 0x42, 0x04, 0xe9, 0x99, 0xa0, 0x29,
+0x41, 0x00, 0xf5, 0xf4, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x2b, 0x41, 0x00, 0xee,
+0x9e, 0x2c, 0xc4, 0x00, 0xa8, 0x15, 0x88, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x41,
+0x00, 0xe5, 0x95, 0x2e, 0x41, 0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00,
+0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x42, 0x04, 0xac, 0x8c, 0xa0, 0x56, 0x41, 0x00,
+0xec, 0x9c, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05,
+0x01, 0x03, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x08, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x1a, 0x10,
+0x00, 0x00, 0x1b, 0x02, 0x07, 0xa0, 0xa0, 0xa0, 0x27, 0x01, 0x03, 0xa0, 0xa0, 0x28, 0x10, 0x00,
+0x00, 0x29, 0x10, 0x00, 0x00, 0x2b, 0x10, 0x00, 0x00, 0x32, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x36,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x05, 0x01, 0x03, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x08,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x10, 0x41, 0x00,
+0xef, 0x9f, 0x11, 0x41, 0x00, 0xe8, 0x98, 0x12, 0x42, 0x00, 0xa5, 0x85, 0xfb, 0x13, 0xc1, 0x00,
+0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0xc4, 0x00, 0xa7, 0x2c, 0x87, 0x2c,
+0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xf9, 0xf8,
+0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x42, 0x04, 0xa6, 0x86, 0xa0,
+0x1b, 0x42, 0x04, 0xe7, 0x97, 0xa0, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91,
+0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00, 0xec, 0x9c, 0x23,
+0x41, 0x00, 0xa3, 0x83, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41,
+0x00, 0xab, 0x8b, 0x27, 0x41, 0x00, 0xf7, 0xf6, 0x28, 0x42, 0x04, 0xed, 0x9d, 0xa0, 0x29, 0xc1,
+0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x81, 0x2b, 0x41, 0x00,
+0xee, 0x9e, 0x2c, 0xc4, 0x00, 0xeb, 0x15, 0x9b, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d,
+0x41, 0x00, 0xe5, 0x95, 0x2e, 0x41, 0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41,
+0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x42, 0x04, 0xac, 0x8c, 0xa0, 0x00, 0x02,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x01, 0x03, 0x00, 0xa0,
+0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfb, 0x1a, 0x10, 0x00, 0x00, 0x1b, 0x02,
+0x07, 0xa0, 0xa0, 0xa0, 0x27, 0x01, 0x03, 0xa0, 0xa0, 0x28, 0x10, 0x00, 0x00, 0x29, 0x10, 0x00,
+0x00, 0x2b, 0x10, 0x00, 0x00, 0x32, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x36, 0x04, 0x1f, 0x00, 0x00,
+0x00, 0x00, 0x82, 0x00, 0xb4, 0xd1, 0x01, 0x09, 0x00, 0x00, 0x50, 0x4f, 0x2c, 0xa3, 0x00, 0x50,
+0x4f, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xe3,
+0x00, 0xf0, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x5c, 0x03, 0x67,
+0x01, 0x6e, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x22, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00,
+0x9c, 0x05, 0x02, 0x03, 0x00, 0x00, 0xf5, 0x07, 0x01, 0x01, 0x00, 0x26, 0x08, 0x02, 0x01, 0x00,
+0x2f, 0x7b, 0x09, 0x03, 0x01, 0x00, 0x28, 0x5b, 0x1b, 0x0a, 0x03, 0x01, 0x00, 0x29, 0x5d, 0x1d,
+0x0b, 0x02, 0x01, 0x00, 0x3d, 0x7d, 0x0c, 0x01, 0x00, 0x27, 0x3f, 0x0d, 0x01, 0x00, 0xae, 0xaf,
+0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17,
+0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x02, 0x04, 0x2b, 0x2a, 0xcc, 0x1b,
+0x01, 0x03, 0xcb, 0xc8, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x41, 0x00, 0x87, 0x80, 0x28, 0x01,
+0x00, 0xa7, 0xa6, 0x29, 0x03, 0x04, 0x5c, 0x7c, 0x00, 0x1c, 0x2b, 0x03, 0x07, 0xc9, 0xca, 0x00,
+0x1e, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x33, 0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34, 0x02, 0x01, 0x00,
+0x3a, 0x3e, 0x35, 0x03, 0x04, 0x2d, 0x5f, 0x00, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56, 0x02, 0x04,
+0x3c, 0x3e, 0xcc, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5,
+0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45,
+0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f,
+0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69,
+0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20,
+0x5e, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41,
+0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61,
+0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49,
+0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0x15, 0x00, 0x60,
+0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0x91, 0x45, 0x92, 0x49,
+0x98, 0x4f, 0xa9, 0x55, 0x9d, 0x20, 0x60, 0x7e, 0x07, 0x61, 0x84, 0x6e, 0xa4, 0x6f, 0x94, 0x41,
+0x8e, 0x4e, 0xa5, 0x4f, 0x99, 0x20, 0x7e, 0x5e, 0x07, 0x61, 0x83, 0x65, 0x88, 0x6f, 0x93, 0x41,
+0x8f, 0x45, 0x89, 0x4f, 0x8c, 0x20, 0x5e, 0x27, 0x0b, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f,
+0xa2, 0x75, 0xa3, 0x41, 0x86, 0x45, 0x90, 0x49, 0x8b, 0x4f, 0x9f, 0x55, 0x96, 0x20, 0x27, 0x22,
+0x03, 0x75, 0x81, 0x55, 0x9a, 0x20, 0x22, 0x00, 0xe0, 0x70, 0x02, 0x19, 0x00, 0x00, 0x52, 0x48,
+0x2c, 0x00, 0x00, 0x55, 0x53, 0x44, 0x56, 0x52, 0x2c, 0x67, 0x00, 0x52, 0x48, 0x2c, 0x67, 0x00,
+0x55, 0x53, 0x44, 0x56, 0x52, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x5a, 0x03, 0x48, 0x02, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xb5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x06, 0xc4, 0x00, 0x6a, 0x24, 0x4a, 0x24, 0x0a, 0x24, 0x00, 0x24, 0x00, 0x24, 0x07, 0xc4,
+0x00, 0x6c, 0x26, 0x4c, 0x26, 0x0c, 0x26, 0x00, 0x26, 0x00, 0x26, 0x08, 0xc4, 0x00, 0x6d, 0x32,
+0x4d, 0x32, 0x0d, 0x32, 0x00, 0x32, 0x00, 0x32, 0x09, 0xc4, 0x00, 0x66, 0x21, 0x46, 0x21, 0x06,
+0x21, 0x00, 0x21, 0x00, 0x21, 0x0a, 0xc4, 0x00, 0x70, 0x19, 0x50, 0x19, 0x10, 0x19, 0x00, 0x19,
+0x00, 0x19, 0x0b, 0x84, 0x00, 0x2f, 0x35, 0x3f, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x0c,
+0x84, 0x00, 0x5b, 0x1a, 0x7b, 0x1a, 0x1b, 0x1a, 0x00, 0x1a, 0x00, 0x1a, 0x0d, 0x84, 0x00, 0x5d,
+0x1b, 0x7d, 0x1b, 0x1d, 0x1b, 0x00, 0x1b, 0x00, 0x1b, 0x10, 0x84, 0x00, 0x35, 0x06, 0x25, 0x06,
+0x00, 0x06, 0x00, 0x7d, 0x00, 0x7d, 0x11, 0x84, 0x00, 0x36, 0x07, 0x5e, 0x07, 0x1e, 0x07, 0x00,
+0x7e, 0x00, 0x7e, 0x12, 0xc4, 0x00, 0x71, 0x10, 0x51, 0x10, 0x11, 0x10, 0x00, 0x10, 0x00, 0x10,
+0x13, 0x84, 0x00, 0x2e, 0x34, 0x3e, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x14, 0xc4, 0x00,
+0x6f, 0x18, 0x4f, 0x18, 0x0f, 0x18, 0x00, 0x18, 0x00, 0x18, 0x15, 0xc4, 0x00, 0x72, 0x13, 0x52,
+0x13, 0x12, 0x13, 0x00, 0x13, 0x00, 0x13, 0x16, 0xc4, 0x00, 0x73, 0x1f, 0x53, 0x1f, 0x13, 0x1f,
+0x00, 0x1f, 0x00, 0x1f, 0x17, 0xc4, 0x00, 0x75, 0x16, 0x55, 0x16, 0x15, 0x16, 0x00, 0x16, 0x00,
+0x16, 0x18, 0xc4, 0x00, 0x79, 0x15, 0x59, 0x15, 0x19, 0x15, 0x00, 0x15, 0x00, 0x15, 0x19, 0xc4,
+0x00, 0x62, 0x30, 0x42, 0x30, 0x02, 0x30, 0x00, 0x30, 0x00, 0x30, 0x1a, 0x84, 0x00, 0x3b, 0x27,
+0x3a, 0x27, 0x00, 0x27, 0x00, 0x27, 0x00, 0x27, 0x1b, 0x84, 0x00, 0x3d, 0x0d, 0x2b, 0x0d, 0x00,
+0x0d, 0x00, 0x83, 0x00, 0x83, 0x1e, 0x84, 0x00, 0x37, 0x08, 0x26, 0x08, 0x00, 0x08, 0x00, 0x7f,
+0x00, 0x7f, 0x1f, 0x84, 0x00, 0x38, 0x09, 0x2a, 0x09, 0x00, 0x09, 0x00, 0x80, 0x00, 0x80, 0x20,
+0xc4, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x21, 0xc4, 0x00, 0x61,
+0x1e, 0x41, 0x1e, 0x01, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x22, 0xc4, 0x00, 0x65, 0x12, 0x45, 0x12,
+0x05, 0x12, 0x00, 0x12, 0x00, 0x12, 0x24, 0xc4, 0x00, 0x74, 0x14, 0x54, 0x14, 0x14, 0x14, 0x00,
+0x14, 0x00, 0x14, 0x25, 0xc4, 0x00, 0x64, 0x20, 0x44, 0x20, 0x04, 0x20, 0x00, 0x20, 0x00, 0x20,
+0x26, 0xc4, 0x00, 0x63, 0x2e, 0x43, 0x2e, 0x03, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x27, 0xc4, 0x00,
+0x6b, 0x25, 0x4b, 0x25, 0x0b, 0x25, 0x00, 0x25, 0x00, 0x25, 0x28, 0x84, 0x00, 0x2d, 0x0c, 0x5f,
+0x0c, 0x1f, 0x0c, 0x00, 0x82, 0x00, 0x82, 0x2c, 0x84, 0x00, 0x39, 0x0a, 0x28, 0x0a, 0x00, 0x0a,
+0x00, 0x80, 0x00, 0x80, 0x2d, 0x84, 0x00, 0x30, 0x0b, 0x29, 0x0b, 0x00, 0x0b, 0x00, 0x81, 0x00,
+0x81, 0x2e, 0xc4, 0x00, 0x78, 0x2d, 0x58, 0x2d, 0x18, 0x2d, 0x00, 0x2d, 0x00, 0x2d, 0x2f, 0x84,
+0x00, 0x2c, 0x33, 0x3c, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x30, 0xc4, 0x00, 0x69, 0x17,
+0x49, 0x17, 0x09, 0x17, 0x00, 0x17, 0x00, 0x17, 0x32, 0xc4, 0x00, 0x77, 0x11, 0x57, 0x11, 0x17,
+0x11, 0x00, 0x11, 0x00, 0x11, 0x33, 0xc4, 0x00, 0x76, 0x2f, 0x56, 0x2f, 0x16, 0x2f, 0x00, 0x2f,
+0x00, 0x2f, 0x34, 0xc4, 0x00, 0x67, 0x22, 0x47, 0x22, 0x07, 0x22, 0x00, 0x22, 0x00, 0x22, 0x35,
+0x84, 0x00, 0x27, 0x28, 0x22, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x22, 0xc4, 0x00,
+0x65, 0x12, 0x45, 0x12, 0x05, 0x12, 0xd5, 0x12, 0x00, 0x12, 0x00, 0xc3, 0x40, 0x03, 0x09, 0x00,
+0x00, 0x53, 0x46, 0x2c, 0x96, 0x00, 0x53, 0x46, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x05, 0x40,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xeb, 0x01, 0xf4, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00,
+0xf4, 0x01, 0x00, 0x00, 0xb5, 0x01, 0x7c, 0x01, 0x9c, 0x01, 0x00, 0x00, 0x5b, 0x03, 0x6f, 0x02,
+0xb9, 0x02, 0x00, 0x00, 0x40, 0x40, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x0c, 0x40,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x09, 0x00,
+0x2b, 0x2b, 0x00, 0x7c, 0x03, 0x04, 0x09, 0x00, 0x22, 0x22, 0x00, 0x40, 0x04, 0x04, 0x09, 0x00,
+0x2a, 0x2a, 0x00, 0x23, 0x05, 0x04, 0x09, 0x00, 0x87, 0x87, 0x00, 0xf8, 0x06, 0x04, 0x0f, 0x00,
+0x00, 0x00, 0x00, 0xf5, 0x07, 0x04, 0x09, 0x00, 0x26, 0x26, 0x00, 0xaa, 0x08, 0x04, 0x09, 0x00,
+0x2f, 0x2f, 0x00, 0xdd, 0x09, 0x04, 0x09, 0x00, 0x28, 0x28, 0x00, 0xbd, 0x0a, 0x02, 0x01, 0x00,
+0x29, 0x29, 0x0b, 0x02, 0x01, 0x00, 0x3d, 0x3d, 0x0c, 0x04, 0x10, 0x27, 0x3f, 0x3f, 0x27, 0xcb,
+0x0d, 0x05, 0x1f, 0xca, 0xc8, 0xc8, 0xca, 0xc9, 0x1e, 0x12, 0x03, 0x00, 0x65, 0x45, 0x65, 0x45,
+0x15, 0x86, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x7a, 0x2c, 0x5a, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00,
+0x2c, 0x16, 0x03, 0x00, 0x75, 0x55, 0x75, 0x55, 0x17, 0x03, 0x00, 0x69, 0x49, 0x69, 0x49, 0x18,
+0x03, 0x00, 0x6f, 0x4f, 0x6f, 0x4f, 0x1a, 0x04, 0x00, 0x8a, 0x81, 0x9a, 0xd4, 0x5b, 0x1b, 0x04,
+0x09, 0xcc, 0x21, 0x21, 0xcc, 0x5d, 0x1e, 0x03, 0x00, 0x61, 0x41, 0x61, 0x41, 0x1f, 0x04, 0x0f,
+0x00, 0x00, 0x00, 0x00, 0xe1, 0x27, 0x03, 0x00, 0x82, 0x94, 0x99, 0x90, 0x28, 0x04, 0x00, 0x85,
+0x84, 0x8e, 0xb7, 0x7b, 0x29, 0x03, 0x00, 0xf5, 0xf8, 0xf8, 0xf5, 0x2b, 0x04, 0x00, 0x24, 0x9c,
+0x9c, 0x24, 0x7d, 0x2c, 0x86, 0x00, 0x79, 0x15, 0x59, 0x15, 0x79, 0x15, 0x59, 0x15, 0x00, 0x15,
+0x19, 0x15, 0x00, 0x15, 0x2e, 0x03, 0x00, 0x63, 0x43, 0x63, 0x43, 0x31, 0x03, 0x00, 0x6e, 0x4e,
+0x6e, 0x4e, 0x32, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x33, 0x04, 0x09, 0x00, 0x3b, 0x3b,
+0x00, 0x3c, 0x34, 0x04, 0x09, 0x00, 0x3a, 0x3a, 0x00, 0x3e, 0x35, 0x05, 0x00, 0x2d, 0x5f, 0x5f,
+0x2d, 0x5c, 0x1f, 0x39, 0x03, 0x06, 0x20, 0x00, 0x00, 0x20, 0x56, 0x05, 0x00, 0x3c, 0x3e, 0x3e,
+0x3c, 0x5c, 0x1c, 0x00, 0x06, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x15, 0x08, 0x04, 0x1f, 0x00,
+0x00, 0x00, 0x00, 0xa0, 0x09, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x29, 0x03, 0x06, 0x15,
+0x00, 0x00, 0x15, 0x00, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97,
+0x20, 0x60, 0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83, 0x65, 0x88,
+0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x27, 0x09, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82,
+0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x43, 0x80, 0x45, 0x90, 0x20, 0x27, 0x22, 0x0a, 0x61, 0x84,
+0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a,
+0x20, 0x22, 0x00, 0x12, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xd5, 0x00, 0x60, 0x0b, 0x61, 0x85,
+0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3,
+0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5,
+0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96,
+0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0f, 0x61, 0xa0,
+0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x43, 0x80,
+0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84,
+0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8,
+0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00, 0x08, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x10,
+0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x92, 0x12, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xd5, 0x17,
+0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xac, 0x18, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xab, 0x1e,
+0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x91, 0x2c, 0x86, 0x00, 0x79, 0x15, 0x59, 0x15, 0x79, 0x15,
+0x59, 0x15, 0xae, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xaf,
+0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45,
+0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f,
+0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69,
+0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20,
+0x5e, 0x27, 0x0f, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79,
+0xec, 0x41, 0xb5, 0x43, 0x80, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20,
+0x27, 0x22, 0x0d, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41,
+0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x00, 0x63, 0x7b,
+0x02, 0x13, 0x00, 0x00, 0x53, 0x47, 0x2c, 0x00, 0x00, 0x53, 0x44, 0x2c, 0x96, 0x00, 0x53, 0x47,
+0x2c, 0x96, 0x00, 0x53, 0x44, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x5a, 0x03, 0xe3, 0x01, 0xec, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0xec, 0x01, 0x00,
+0x00, 0xb5, 0x01, 0x74, 0x01, 0x94, 0x01, 0x00, 0x00, 0x40, 0x40, 0x0c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x40, 0x00, 0x0c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x02, 0x04, 0x09, 0x00, 0x2b, 0x2b, 0x00, 0x7c, 0x03, 0x04, 0x09, 0x00, 0x22, 0x22, 0x00,
+0x40, 0x04, 0x04, 0x09, 0x00, 0x2a, 0x2a, 0x00, 0x23, 0x05, 0x04, 0x09, 0x00, 0x87, 0x87, 0x00,
+0xf8, 0x06, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x07, 0x04, 0x09, 0x00, 0x26, 0x26, 0x00,
+0xaa, 0x08, 0x04, 0x09, 0x00, 0x2f, 0x2f, 0x00, 0xdd, 0x09, 0x04, 0x09, 0x00, 0x28, 0x28, 0x00,
+0xbd, 0x0a, 0x02, 0x01, 0x00, 0x29, 0x29, 0x0b, 0x02, 0x01, 0x00, 0x3d, 0x3d, 0x0c, 0x04, 0x10,
+0x27, 0x3f, 0x3f, 0x27, 0xcb, 0x0d, 0x05, 0x1f, 0xca, 0xc8, 0xc8, 0xca, 0xc9, 0x1e, 0x12, 0x03,
+0x00, 0x65, 0x45, 0x65, 0x45, 0x15, 0x86, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x7a, 0x2c, 0x5a, 0x2c,
+0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x03, 0x00, 0x75, 0x55, 0x75, 0x55, 0x17, 0x03, 0x00,
+0x69, 0x49, 0x69, 0x49, 0x18, 0x03, 0x00, 0x6f, 0x4f, 0x6f, 0x4f, 0x1a, 0x04, 0x00, 0x81, 0x8a,
+0xd4, 0x9a, 0x5b, 0x1b, 0x04, 0x09, 0xcc, 0x21, 0x21, 0xcc, 0x5d, 0x1e, 0x03, 0x00, 0x61, 0x41,
+0x61, 0x41, 0x1f, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x27, 0x03, 0x00, 0x94, 0x82, 0x90,
+0x99, 0x28, 0x04, 0x00, 0x84, 0x85, 0xb7, 0x8e, 0x7b, 0x29, 0x03, 0x00, 0xf5, 0xf8, 0xf8, 0xf5,
+0x2b, 0x04, 0x00, 0x24, 0x9c, 0x9c, 0x24, 0x7d, 0x2c, 0x86, 0x00, 0x79, 0x15, 0x59, 0x15, 0x79,
+0x15, 0x59, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2e, 0x03, 0x00, 0x63, 0x43, 0x63, 0x43,
+0x31, 0x03, 0x00, 0x6e, 0x4e, 0x6e, 0x4e, 0x32, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x33,
+0x04, 0x09, 0x00, 0x3b, 0x3b, 0x00, 0x3c, 0x34, 0x04, 0x09, 0x00, 0x3a, 0x3a, 0x00, 0x3e, 0x35,
+0x05, 0x00, 0x2d, 0x5f, 0x5f, 0x2d, 0x5c, 0x1f, 0x39, 0x03, 0x06, 0x20, 0x00, 0x00, 0x20, 0x56,
+0x05, 0x00, 0x3c, 0x3e, 0x3e, 0x3c, 0x5c, 0x1c, 0x00, 0x06, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00,
+0x15, 0x08, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x09, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00,
+0x9b, 0x29, 0x03, 0x06, 0x15, 0x00, 0x00, 0x15, 0x00, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69,
+0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60, 0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e,
+0x06, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x27, 0x09, 0x61,
+0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x43, 0x80, 0x45, 0x90, 0x20,
+0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41,
+0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0x12, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xd5,
+0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45,
+0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f,
+0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69,
+0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20,
+0x5e, 0xef, 0x0f, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79,
+0xec, 0x41, 0xb5, 0x43, 0x80, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20,
+0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41,
+0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00, 0xef, 0xf0, 0x03, 0x09,
+0x00, 0x00, 0x53, 0x4b, 0x2c, 0xf5, 0x00, 0x53, 0x4b, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x03, 0x49, 0x01, 0xc1, 0x01, 0x00, 0x00, 0x5a, 0x03, 0x82,
+0x02, 0x89, 0x02, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x89, 0x02, 0x00, 0x00, 0x63, 0x03, 0x10,
+0x03, 0x79, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04,
+0x2b, 0x31, 0xc8, 0x03, 0x01, 0x01, 0xa0, 0x32, 0x04, 0x03, 0x05, 0xa0, 0x33, 0xc9, 0x1e, 0x05,
+0x01, 0x01, 0xa0, 0x34, 0x06, 0x02, 0x05, 0xa0, 0x35, 0xca, 0x07, 0x01, 0x01, 0xa0, 0x36, 0x08,
+0x02, 0x04, 0xec, 0x37, 0xcb, 0x09, 0x02, 0x00, 0xa0, 0x38, 0xfa, 0x0a, 0x02, 0x04, 0xa1, 0x39,
+0xcc, 0x0b, 0x01, 0x00, 0x82, 0x30, 0x0c, 0x02, 0x04, 0x3d, 0x25, 0xcd, 0x0d, 0x02, 0x07, 0xcc,
+0xa0, 0xce, 0x10, 0x42, 0x03, 0x00, 0x00, 0x5c, 0x11, 0x42, 0x03, 0x00, 0x00, 0x7c, 0x12, 0x41,
+0x00, 0x65, 0x45, 0x15, 0xc4, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c,
+0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x19,
+0x42, 0x03, 0x00, 0x00, 0x27, 0x1a, 0x02, 0x00, 0xa3, 0x2f, 0xf6, 0x1b, 0x02, 0x00, 0x84, 0x28,
+0x9e, 0x1e, 0x42, 0x00, 0x61, 0x41, 0x3c, 0x21, 0x42, 0x03, 0x00, 0x00, 0x5b, 0x22, 0x42, 0x03,
+0x00, 0x00, 0x5d, 0x27, 0x02, 0x00, 0x93, 0x22, 0x24, 0x28, 0x02, 0x00, 0xf5, 0x21, 0xe1, 0x29,
+0x01, 0x02, 0x3b, 0xca, 0x2b, 0x02, 0x01, 0xa0, 0x29, 0xcf, 0x2c, 0xc4, 0x00, 0x79, 0x15, 0x59,
+0x15, 0x3e, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x42, 0x03, 0x00, 0x00, 0x23, 0x2e, 0x42, 0x00,
+0x63, 0x43, 0x26, 0x2f, 0x42, 0x03, 0x00, 0x00, 0x40, 0x30, 0x42, 0x03, 0x00, 0x00, 0x7b, 0x31,
+0x42, 0x00, 0x6e, 0x4e, 0x7d, 0x33, 0x01, 0x01, 0x00, 0x3f, 0x34, 0x02, 0x01, 0x00, 0x3a, 0x2a,
+0x35, 0x03, 0x04, 0x2d, 0x5f, 0x00, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56, 0x02, 0x00, 0x26, 0x2a,
+0x3c, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7e, 0x03, 0x02, 0x06, 0x96, 0x00, 0xc8, 0x04, 0x00,
+0x00, 0xe7, 0x05, 0x02, 0x06, 0x9f, 0x00, 0xca, 0x06, 0x02, 0x06, 0x9c, 0x00, 0xcb, 0x07, 0x02,
+0x06, 0xa7, 0x00, 0xcc, 0x08, 0x02, 0x03, 0x00, 0x00, 0x60, 0x09, 0x02, 0x07, 0x00, 0x00, 0xcd,
+0x0a, 0x02, 0x07, 0x00, 0x00, 0xce, 0x0b, 0x02, 0x07, 0x00, 0x00, 0xcf, 0x0c, 0x02, 0x07, 0x00,
+0x00, 0xd0, 0x0d, 0x02, 0x07, 0xce, 0xc8, 0xd1, 0x12, 0x42, 0x03, 0x00, 0x00, 0xaa, 0x13, 0x41,
+0x00, 0x72, 0x52, 0x14, 0x41, 0x00, 0x74, 0x54, 0x1f, 0x42, 0x00, 0x73, 0x53, 0xd0, 0x20, 0x42,
+0x00, 0x64, 0x44, 0xd1, 0x25, 0x42, 0x03, 0x00, 0x00, 0x88, 0x26, 0x42, 0x00, 0x6c, 0x4c, 0x9d,
+0x29, 0x01, 0x03, 0x00, 0xcb, 0x2b, 0x00, 0x00, 0xe5, 0x00, 0xf3, 0x13, 0x63, 0x9f, 0x64, 0xd4,
+0x65, 0xd8, 0x6c, 0x96, 0x6e, 0xe5, 0x72, 0xfd, 0x73, 0xe7, 0x74, 0x9c, 0x7a, 0xa7, 0x43, 0xac,
+0x44, 0xd2, 0x45, 0xb7, 0x4c, 0x95, 0x4e, 0xd5, 0x52, 0xfc, 0x53, 0xe6, 0x54, 0x9b, 0x5a, 0xa6,
+0x20, 0xf3, 0x5e, 0x07, 0x61, 0x83, 0x69, 0x8c, 0x6f, 0x93, 0x41, 0xb6, 0x49, 0xd7, 0x4f, 0xe2,
+0x20, 0x5e, 0xf4, 0x03, 0x61, 0xc7, 0x41, 0xc6, 0x20, 0xf4, 0xf8, 0x03, 0x75, 0x85, 0x55, 0xde,
+0x20, 0xf8, 0xf2, 0x05, 0x61, 0xa5, 0x65, 0xa9, 0x41, 0xa4, 0x45, 0xa8, 0x20, 0xf2, 0xfa, 0x03,
+0x7a, 0xbe, 0x5a, 0xbd, 0x20, 0xfa, 0xef, 0x19, 0x61, 0xa0, 0x63, 0x86, 0x65, 0x82, 0x69, 0xa1,
+0x6c, 0x92, 0x6e, 0xe4, 0x6f, 0xa2, 0x72, 0xea, 0x73, 0x98, 0x75, 0xa3, 0x79, 0xec, 0x7a, 0xab,
+0x41, 0xb5, 0x43, 0x8f, 0x45, 0x90, 0x49, 0xd6, 0x4c, 0x91, 0x4e, 0xe3, 0x4f, 0xe0, 0x52, 0xe8,
+0x53, 0x97, 0x55, 0xe9, 0x59, 0xed, 0x5a, 0x8d, 0x20, 0xef, 0xf1, 0x05, 0x6f, 0x8b, 0x75, 0xfb,
+0x4f, 0x8a, 0x55, 0xeb, 0x20, 0xf1, 0xf9, 0x09, 0x61, 0x84, 0x65, 0x89, 0x6f, 0x94, 0x75, 0x81,
+0x41, 0x8e, 0x45, 0xd3, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0xf7, 0x07, 0x63, 0x87, 0x73, 0xad,
+0x74, 0xee, 0x43, 0x80, 0x53, 0xb8, 0x54, 0xdd, 0x20, 0xf7, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00,
+0xd5, 0x00, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5,
+0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6,
+0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xf8, 0x03, 0x61, 0x86, 0x41, 0x8f,
+0x20, 0xf8, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7,
+0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82,
+0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0,
+0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94,
+0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9,
+0xf7, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0xf7, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7e, 0x03,
+0x02, 0x06, 0x8c, 0x00, 0xc8, 0x04, 0x00, 0x00, 0xa8, 0x05, 0x00, 0x00, 0x87, 0x06, 0x00, 0x00,
+0x9f, 0x07, 0x00, 0x00, 0x91, 0x08, 0x02, 0x02, 0x98, 0x00, 0x60, 0x09, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xcb, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xcc, 0x0d, 0x02, 0x07,
+0xcb, 0xc8, 0xa0, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41, 0x00, 0x74, 0x54, 0x1b, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x20, 0x41, 0x00, 0x64, 0x44, 0x26, 0x41, 0x00,
+0x6c, 0x4c, 0x28, 0x00, 0x00, 0xad, 0x29, 0x01, 0x03, 0x00, 0xca, 0x2b, 0x02, 0x06, 0xa4, 0x00,
+0xa0, 0x00, 0x76, 0x13, 0x63, 0x87, 0x64, 0x83, 0x65, 0x88, 0x6c, 0x8c, 0x6e, 0xa4, 0x72, 0xa9,
+0x73, 0xa8, 0x74, 0x9f, 0x7a, 0x91, 0x43, 0x80, 0x44, 0x85, 0x45, 0x89, 0x4c, 0x9c, 0x4e, 0xa5,
+0x52, 0x9e, 0x53, 0x9b, 0x54, 0x86, 0x5a, 0x92, 0x20, 0x76, 0x5e, 0x03, 0x6f, 0x93, 0x4f, 0xa7,
+0x20, 0x5e, 0xf8, 0x03, 0x75, 0x96, 0x55, 0xa6, 0x20, 0xf8, 0x27, 0x11, 0x61, 0xa0, 0x65, 0x82,
+0x69, 0xa1, 0x6c, 0x8d, 0x6f, 0xa2, 0x72, 0xaa, 0x75, 0xa3, 0x79, 0x98, 0x41, 0x8f, 0x45, 0x90,
+0x49, 0x8b, 0x4c, 0x8a, 0x4f, 0x95, 0x52, 0xab, 0x55, 0x97, 0x59, 0x9d, 0x20, 0x27, 0x22, 0x07,
+0x61, 0x84, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0x9b,
+0x44, 0x02, 0x1d, 0x00, 0x00, 0x53, 0x50, 0x2c, 0x00, 0x00, 0x45, 0x53, 0x2c, 0xac, 0x00, 0x53,
+0x50, 0x2c, 0xac, 0x00, 0x45, 0x53, 0x2c, 0xad, 0x00, 0x53, 0x50, 0x2c, 0xad, 0x00, 0x45, 0x53,
+0x05, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0x40, 0x01,
+0x4d, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x4d, 0x01, 0x00, 0x00, 0xb5, 0x01, 0x00, 0x00,
+0xf5, 0x00, 0x00, 0x00, 0x37, 0x75, 0xc4, 0x01, 0xf5, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40,
+0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7c, 0x03, 0x02, 0x01, 0x00, 0x22, 0x40,
+0x04, 0x02, 0x01, 0x00, 0xfa, 0x23, 0x05, 0x02, 0x07, 0x00, 0x00, 0xc9, 0x07, 0x02, 0x01, 0x00,
+0x26, 0xaa, 0x08, 0x01, 0x01, 0x00, 0x2f, 0x09, 0x01, 0x01, 0x00, 0x28, 0x0a, 0x01, 0x01, 0x00,
+0x29, 0x0b, 0x01, 0x01, 0x00, 0x3d, 0x0c, 0x01, 0x00, 0x27, 0x3f, 0x0d, 0x01, 0x00, 0xad, 0xa8,
+0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17,
+0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x02, 0x03, 0xc8, 0xca, 0x5b, 0x1b,
+0x02, 0x00, 0x2b, 0x2a, 0x5d, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x42, 0x00, 0xa4, 0xa5, 0x7e,
+0x28, 0x02, 0x03, 0xcb, 0xcc, 0x7b, 0x29, 0x03, 0x00, 0xa7, 0xa6, 0x5c, 0x1c, 0x2b, 0x42, 0x00,
+0x87, 0x80, 0x7d, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x33, 0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34, 0x02,
+0x01, 0x00, 0x3a, 0x3e, 0x35, 0x03, 0x00, 0x2d, 0x5f, 0x5c, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56,
+0x01, 0x00, 0x3c, 0x3e, 0x00, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75,
+0x97, 0x20, 0x60, 0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83, 0x65,
+0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x27, 0x07, 0x61, 0xa0, 0x65, 0x82, 0x69,
+0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x45, 0x90, 0x20, 0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89, 0x69,
+0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00,
+0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x60, 0x0b, 0x61,
+0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f,
+0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e,
+0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75,
+0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0d, 0x61,
+0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49,
+0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69,
+0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55,
+0x9a, 0x20, 0xf9, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0x7e, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5,
+0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x1a, 0x01, 0x03, 0x00, 0xa0, 0x26, 0x04, 0x0b, 0x00, 0x00,
+0xf2, 0x00, 0xf6, 0x28, 0x01, 0x03, 0xc9, 0xca, 0x2c, 0x02, 0x03, 0x00, 0x00, 0xae, 0x2d, 0x02,
+0x03, 0x00, 0x00, 0xaf, 0x00, 0x60, 0x07, 0x61, 0x85, 0x65, 0x8a, 0x6f, 0x95, 0x41, 0xb7, 0x45,
+0xd4, 0x4f, 0xe3, 0x20, 0x60, 0x27, 0x09, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x45,
+0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x20, 0x27, 0x22, 0x05, 0x69, 0x8b, 0x75, 0x81, 0x49,
+0xd8, 0x55, 0x9a, 0x20, 0x22, 0x00, 0xcc, 0x27, 0x04, 0x1d, 0x00, 0x00, 0x53, 0x55, 0x2c, 0x00,
+0x00, 0x53, 0x56, 0x2c, 0x00, 0x00, 0x46, 0x49, 0x2c, 0x99, 0x00, 0x53, 0x55, 0x2c, 0x99, 0x00,
+0x53, 0x56, 0x2c, 0x99, 0x00, 0x46, 0x49, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x5a, 0x03, 0x7a, 0x01, 0x87, 0x01, 0x00, 0x00, 0x5b, 0x03, 0x02, 0x02, 0x25,
+0x02, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x87, 0x01, 0x00, 0x00, 0xb5, 0x01, 0x21, 0x01, 0x2b,
+0x01, 0x00, 0x00, 0x30, 0x75, 0xae, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30, 0x75, 0x66, 0x03, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x22, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00, 0x9c, 0x05,
+0x02, 0x01, 0x00, 0xcf, 0x24, 0x07, 0x01, 0x01, 0x00, 0x26, 0x08, 0x02, 0x01, 0x00, 0x2f, 0x7b,
+0x09, 0x04, 0x09, 0x00, 0x28, 0x5b, 0x00, 0x1b, 0x0a, 0x04, 0x09, 0x00, 0x29, 0x5d, 0x00, 0x1d,
+0x0b, 0x02, 0x01, 0x00, 0x3d, 0x7d, 0x0c, 0x04, 0x08, 0x2b, 0x3f, 0x5c, 0x00, 0x1c, 0x0d, 0x02,
+0x03, 0xcb, 0xc8, 0x7c, 0x10, 0x42, 0x03, 0x00, 0x00, 0x40, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15,
+0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41,
+0x00, 0x6f, 0x4f, 0x1a, 0x41, 0x00, 0x86, 0x8f, 0x1b, 0x04, 0x0f, 0xcc, 0xca, 0xc9, 0x00, 0x1e,
+0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x41, 0x00, 0x94, 0x99, 0x28, 0x41, 0x00, 0x84, 0x8e, 0x29,
+0x01, 0x00, 0xf5, 0xab, 0x2b, 0x01, 0x00, 0x27, 0x2a, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41,
+0x00, 0x6e, 0x4e, 0x32, 0x42, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34,
+0x02, 0x01, 0x00, 0x3a, 0x3e, 0x35, 0x04, 0x0c, 0x2d, 0x5f, 0x00, 0x00, 0x1f, 0x39, 0x00, 0x00,
+0x20, 0x56, 0x02, 0x00, 0x3c, 0x3e, 0x7c, 0x00, 0x05, 0x01, 0x03, 0x00, 0xa0, 0x29, 0x00, 0x00,
+0x15, 0x00, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60,
+0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c,
+0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x27, 0x09, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1,
+0x6f, 0xa2, 0x75, 0xa3, 0x43, 0x80, 0x45, 0x90, 0x20, 0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89,
+0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22,
+0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x60, 0x0b,
+0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde,
+0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7,
+0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93,
+0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0f,
+0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5,
+0x43, 0x80, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c,
+0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3,
+0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x12,
+0x42, 0x03, 0x00, 0x00, 0xd5, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x28, 0x42, 0x07, 0x00, 0x00, 0xcd,
+0x29, 0x01, 0x03, 0x00, 0xa0, 0x2c, 0x42, 0x00, 0x7a, 0x5a, 0xaf, 0x00, 0x60, 0x0b, 0x61, 0x85,
+0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3,
+0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5,
+0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96,
+0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x27, 0x0f, 0x61, 0xa0,
+0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x43, 0x80,
+0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0x27, 0x22, 0x0d, 0x61, 0x84,
+0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8,
+0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x76, 0x05, 0x73, 0xf9, 0x7a, 0xf7, 0x53, 0xdd,
+0x5a, 0xef, 0x20, 0x76, 0x00, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00, 0x00,
+0xd5, 0x09, 0x03, 0x07, 0x00, 0x00, 0x00, 0xae, 0x0a, 0x03, 0x07, 0x00, 0x00, 0x00, 0xaf, 0x0d,
+0x01, 0x03, 0xa0, 0xa0, 0x10, 0x43, 0x03, 0x00, 0x00, 0x83, 0xb6, 0x12, 0x42, 0x03, 0x00, 0x00,
+0xd5, 0x14, 0x43, 0x03, 0x00, 0x00, 0xab, 0x97, 0x17, 0x43, 0x03, 0x00, 0x00, 0x8b, 0xd8, 0x18,
+0x43, 0x03, 0x00, 0x00, 0xe4, 0xe5, 0x1b, 0x02, 0x07, 0xa0, 0xa0, 0xa0, 0x1e, 0x43, 0x03, 0x00,
+0x00, 0xa0, 0xb5, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xaa, 0x96, 0x20, 0x43, 0x03, 0x00, 0x00, 0x9f,
+0x8d, 0x21, 0x43, 0x03, 0x00, 0x00, 0x90, 0x80, 0x22, 0x43, 0x03, 0x00, 0x00, 0xa1, 0xa2, 0x23,
+0x43, 0x03, 0x00, 0x00, 0x85, 0x82, 0x25, 0x43, 0x03, 0x00, 0x00, 0xa6, 0xa7, 0x27, 0x43, 0x03,
+0x00, 0x00, 0x9b, 0x9d, 0x28, 0x43, 0x03, 0x00, 0x00, 0x91, 0x92, 0x29, 0x01, 0x03, 0x00, 0xa0,
+0x2c, 0x43, 0x03, 0x00, 0x00, 0xac, 0x98, 0x2e, 0x43, 0x03, 0x00, 0x00, 0x9e, 0x8c, 0x2f, 0x43,
+0x03, 0x00, 0x00, 0x89, 0x8a, 0x30, 0x43, 0x03, 0x00, 0x00, 0x87, 0x88, 0x31, 0x43, 0x03, 0x00,
+0x00, 0xa9, 0x95, 0x36, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x04, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x09, 0x03, 0x07, 0x00, 0x00, 0x00, 0xae,
+0x0a, 0x03, 0x07, 0x00, 0x00, 0x00, 0xaf, 0x0d, 0x01, 0x03, 0xa0, 0xa0, 0x10, 0x43, 0x00, 0xa0,
+0xb5, 0x71, 0x51, 0x11, 0x43, 0x00, 0xaa, 0x96, 0x77, 0x57, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5,
+0x15, 0x43, 0x00, 0xab, 0x97, 0x79, 0x59, 0x17, 0x43, 0x03, 0x00, 0x00, 0x8b, 0xd8, 0x18, 0x43,
+0x03, 0x00, 0x00, 0xe4, 0xe5, 0x1b, 0x42, 0x04, 0xa9, 0x95, 0xa0, 0x1e, 0x43, 0x03, 0x00, 0x00,
+0x83, 0xb6, 0x22, 0x43, 0x03, 0x00, 0x00, 0xa1, 0xa2, 0x23, 0x43, 0x03, 0x00, 0x00, 0x90, 0x80,
+0x25, 0x43, 0x03, 0x00, 0x00, 0xa6, 0xa7, 0x27, 0x43, 0x03, 0x00, 0x00, 0x9b, 0x9d, 0x28, 0x43,
+0x03, 0x00, 0x00, 0x91, 0x92, 0x29, 0x01, 0x03, 0x00, 0xa0, 0x2a, 0x05, 0x3f, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0x2b, 0x43, 0x00, 0x9f, 0x8d, 0x27, 0x2a, 0x2c, 0x43, 0x03, 0x00, 0x00, 0x87,
+0x88, 0x2d, 0x43, 0x00, 0x9e, 0x8c, 0x78, 0x58, 0x56, 0x43, 0x00, 0xac, 0x98, 0x89, 0x8a, 0x00,
+0xd6, 0xb8, 0x03, 0x09, 0x00, 0x00, 0x55, 0x4b, 0x2c, 0xa6, 0x00, 0x55, 0x4b, 0x08, 0x03, 0x00,
+0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xe7, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x52, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x01, 0xc4, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x31, 0x75, 0x84, 0x01, 0xfe, 0x01, 0x00, 0x00, 0x5b, 0x03, 0xee, 0x00, 0x43, 0x01, 0x00,
+0x00, 0x55, 0x03, 0x04, 0x03, 0x61, 0x03, 0x00, 0x00, 0x54, 0x03, 0xa7, 0x02, 0xdb, 0x02, 0x00,
+0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7c, 0x03,
+0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0x9c, 0x12, 0x43, 0x00, 0x65, 0x45, 0x82, 0x90,
+0x16, 0x43, 0x00, 0x75, 0x55, 0xa3, 0xe9, 0x17, 0x43, 0x00, 0x69, 0x49, 0xa1, 0xd6, 0x18, 0xc3,
+0x00, 0x6f, 0x18, 0x4f, 0x18, 0xa2, 0x18, 0xe0, 0x00, 0x1e, 0x43, 0x00, 0x61, 0x41, 0xa0, 0xb5,
+0x28, 0x01, 0x01, 0x00, 0x40, 0x29, 0x02, 0x01, 0x00, 0xaa, 0xdd, 0x2b, 0x01, 0x00, 0x23, 0x7e,
+0x35, 0x04, 0x0b, 0x00, 0x00, 0x5c, 0x00, 0x1c, 0x56, 0x04, 0x0c, 0x5c, 0x7c, 0x00, 0x00, 0x1c,
+0x00, 0x16, 0x43, 0x0f, 0x00, 0x00, 0x00, 0xa0, 0x17, 0x43, 0x0f, 0x00, 0x00, 0x00, 0xa0, 0x18,
+0x43, 0x0f, 0x00, 0x00, 0x00, 0xa0, 0x1e, 0x43, 0x0f, 0x00, 0x00, 0x00, 0xa0, 0x29, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00,
+0xad, 0x03, 0x01, 0x03, 0x00, 0xca, 0x05, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x07, 0x01, 0x03, 0x00,
+0xc8, 0x10, 0x43, 0x03, 0x00, 0x00, 0x91, 0x92, 0x15, 0x41, 0x00, 0x79, 0x59, 0x19, 0x43, 0x03,
+0x00, 0x00, 0xab, 0xac, 0x29, 0x02, 0x03, 0xc9, 0x00, 0x7c, 0x2e, 0x43, 0x03, 0x00, 0x00, 0x87,
+0x80, 0x31, 0x43, 0x03, 0x00, 0x00, 0xa4, 0xa5, 0x33, 0x02, 0x03, 0x00, 0x00, 0xae, 0x34, 0x02,
+0x03, 0x00, 0x00, 0xaf, 0x35, 0x03, 0x07, 0x00, 0x00, 0x00, 0xa8, 0x39, 0x00, 0x00, 0x20, 0x00,
+0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2,
+0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x60, 0x07, 0x61, 0x85, 0x65, 0x8a, 0x75, 0x97,
+0x41, 0xb7, 0x45, 0xd4, 0x55, 0xeb, 0x20, 0x60, 0x22, 0x0b, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94,
+0x75, 0x81, 0x79, 0x98, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22,
+0x00, 0x03, 0x01, 0x03, 0x00, 0xc9, 0x05, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x07, 0x02, 0x03, 0x00,
+0xca, 0x5e, 0x11, 0x43, 0x00, 0x77, 0x57, 0x86, 0x8f, 0x14, 0x41, 0x00, 0x74, 0x54, 0x15, 0x43,
+0x00, 0x79, 0x59, 0xec, 0xed, 0x19, 0x41, 0x00, 0x70, 0x50, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x20,
+0x41, 0x00, 0x64, 0x44, 0x21, 0x41, 0x00, 0x66, 0x46, 0x22, 0x41, 0x00, 0x67, 0x47, 0x28, 0x00,
+0x01, 0xcb, 0x29, 0x00, 0x01, 0xc8, 0x2b, 0x01, 0x03, 0x00, 0xcc, 0x2c, 0x43, 0x03, 0x00, 0x00,
+0xf9, 0xfa, 0x2e, 0x43, 0x00, 0x63, 0x43, 0x87, 0x80, 0x30, 0x41, 0x00, 0x62, 0x42, 0x31, 0x43,
+0x03, 0x00, 0x00, 0xa4, 0xa5, 0x32, 0x41, 0x00, 0x6d, 0x4d, 0x33, 0x02, 0x03, 0x00, 0x00, 0xae,
+0x34, 0x02, 0x03, 0x00, 0x00, 0xaf, 0x39, 0x00, 0x00, 0x20, 0x00, 0x60, 0x0f, 0x61, 0x85, 0x65,
+0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x77, 0x91, 0x79, 0xc6, 0x41, 0xb7, 0x45, 0xd4, 0x49,
+0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x57, 0x92, 0x59, 0xc7, 0x20, 0x60, 0x22, 0x0f, 0x61, 0x84, 0x65,
+0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x77, 0xa6, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49,
+0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x57, 0xa7, 0x59, 0xf3, 0x20, 0x22, 0x5e, 0x0f, 0x61, 0x83, 0x65,
+0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x77, 0x9b, 0x79, 0xe4, 0x41, 0xb6, 0x45, 0xd2, 0x49,
+0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x57, 0x9d, 0x59, 0xe5, 0x20, 0x5e, 0x27, 0x0f, 0x61, 0xa0, 0x65,
+0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x77, 0x86, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49,
+0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x57, 0x8f, 0x59, 0xed, 0x20, 0x27, 0x7e, 0x13, 0x62, 0xf2, 0x63,
+0xbd, 0x64, 0xd0, 0x66, 0x9f, 0x67, 0x9e, 0x6d, 0xac, 0x70, 0xa8, 0x73, 0xf4, 0x74, 0xe7, 0x42,
+0xa9, 0x43, 0xb8, 0x44, 0xd1, 0x46, 0xbe, 0x47, 0xf6, 0x4d, 0xfb, 0x50, 0xad, 0x53, 0xe1, 0x54,
+0xe8, 0x20, 0x7e, 0x00, 0x04, 0x01, 0x03, 0x00, 0xa0, 0x05, 0x02, 0x03, 0x00, 0x00, 0xaa, 0x1f,
+0x41, 0x00, 0x73, 0x53, 0x26, 0x41, 0x00, 0x6c, 0x4c, 0x29, 0x02, 0x07, 0xc8, 0xa0, 0xa0, 0x2c,
+0x41, 0x00, 0x7a, 0x5a, 0x2d, 0x41, 0x00, 0x78, 0x58, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41,
+0x00, 0x6e, 0x4e, 0x39, 0x00, 0x00, 0x20, 0x00, 0x2d, 0x13, 0x61, 0xa5, 0x63, 0x86, 0x65, 0xa9,
+0x6c, 0x88, 0x6e, 0xe4, 0x6f, 0xa2, 0x73, 0x98, 0x78, 0xab, 0x7a, 0xbe, 0x41, 0xa4, 0x43, 0x8f,
+0x45, 0xa8, 0x4c, 0x9d, 0x4e, 0xe3, 0x4f, 0xe0, 0x53, 0x97, 0x58, 0x8d, 0x5a, 0xbd, 0x20, 0x2d,
+0x00, 0x03, 0x01, 0x03, 0x00, 0xca, 0x07, 0x01, 0x03, 0x00, 0xc9, 0x16, 0x43, 0x03, 0x00, 0x00,
+0xed, 0xec, 0x17, 0x43, 0x0f, 0x00, 0x00, 0xa0, 0xa0, 0x18, 0x43, 0x0c, 0x6f, 0x4f, 0xa0, 0xa0,
+0x1e, 0x43, 0x0f, 0x00, 0x00, 0xa0, 0xa0, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x22, 0x43, 0x00, 0x67,
+0x47, 0xe5, 0xe4, 0x23, 0x43, 0x00, 0x68, 0x48, 0xe8, 0xe7, 0x24, 0x41, 0x00, 0x6a, 0x4a, 0x29,
+0x02, 0x03, 0xc8, 0xa0, 0x7c, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xbe, 0xbd, 0x2e, 0x43, 0x00, 0x63,
+0x43, 0x91, 0x92, 0x31, 0x42, 0x03, 0x00, 0x00, 0xf3, 0x39, 0x00, 0x00, 0x20, 0x00, 0x60, 0x0b,
+0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde,
+0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x5e, 0x13, 0x63, 0x86, 0x65, 0x88, 0x67, 0x9b, 0x68, 0xa9,
+0x69, 0x8c, 0x6a, 0x9f, 0x6f, 0x93, 0x73, 0xc7, 0x75, 0x96, 0x43, 0x8f, 0x45, 0xd2, 0x47, 0x9d,
+0x48, 0xa8, 0x49, 0xd7, 0x4a, 0xac, 0x4f, 0xe2, 0x53, 0xc6, 0x55, 0xea, 0x20, 0x5e, 0x22, 0x05,
+0x65, 0x89, 0x69, 0x8b, 0x45, 0xd3, 0x49, 0xd8, 0x20, 0x22, 0x00, 0xfc, 0x7f, 0x04, 0x04, 0xa8,
+0x00, 0x55, 0x4b, 0x08, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a,
+0x03, 0x0b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5,
+0x01, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x75, 0xe2, 0x01, 0x77, 0x02, 0x00, 0x00, 0x5b,
+0x03, 0x12, 0x01, 0x89, 0x01, 0x00, 0x00, 0x55, 0x03, 0x20, 0x03, 0x9b, 0x03, 0x00, 0x00, 0x54,
+0x03, 0xe8, 0x03, 0x51, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+0x02, 0x03, 0x00, 0x00, 0xfb, 0x03, 0x02, 0x01, 0x00, 0x22, 0xfd, 0x04, 0x02, 0x01, 0x00, 0x9c,
+0xfc, 0x05, 0x02, 0x03, 0x00, 0x00, 0xac, 0x06, 0x02, 0x03, 0x00, 0x00, 0xab, 0x07, 0x02, 0x01,
+0x00, 0x26, 0xf3, 0x08, 0x01, 0x01, 0x00, 0x27, 0x09, 0x01, 0x01, 0x00, 0x28, 0x0a, 0x02, 0x01,
+0x00, 0x29, 0xf1, 0x0b, 0x02, 0x01, 0x00, 0x23, 0xf8, 0x0c, 0x02, 0x03, 0x00, 0x00, 0x5f, 0x0d,
+0x01, 0x00, 0xaa, 0xee, 0x1a, 0x01, 0x00, 0x40, 0x60, 0x1b, 0x04, 0x08, 0x5b, 0x7b, 0x7e, 0x00,
+0x1b, 0x27, 0x01, 0x01, 0x00, 0x2b, 0x28, 0x04, 0x08, 0x3a, 0x2a, 0x5e, 0x00, 0x1e, 0x29, 0x04,
+0x08, 0x5c, 0xdd, 0x7c, 0x00, 0x1c, 0x2b, 0x04, 0x0c, 0x5d, 0x7d, 0x00, 0x00, 0x1d, 0x32, 0x42,
+0x03, 0x00, 0x00, 0xe6, 0x39, 0x00, 0x00, 0x20, 0x56, 0x04, 0x0c, 0x7c, 0x5f, 0x00, 0x00, 0x1f,
+0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x07, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x0d, 0x01, 0x03, 0x00, 0xa0, 0x29, 0x01, 0x03, 0x00, 0xa0, 0x00, 0x12, 0x42,
+0x03, 0x00, 0x00, 0xd5, 0x00, 0x02, 0x03, 0x07, 0x00, 0x00, 0x00, 0xad, 0x03, 0x01, 0x03, 0x00,
+0xca, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x07, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x08, 0x01, 0x03, 0x00, 0xcb, 0x12, 0x42, 0x00, 0x65, 0x45, 0xd5, 0x15, 0x41,
+0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x43, 0x00,
+0x6f, 0x4f, 0xab, 0xac, 0x1a, 0x01, 0x03, 0x00, 0xc9, 0x1e, 0x43, 0x00, 0x61, 0x41, 0x91, 0x92,
+0x28, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x29, 0x01, 0x03, 0x00, 0xa0, 0x2e, 0x43, 0x03, 0x00, 0x00,
+0x87, 0x80, 0x31, 0x43, 0x03, 0x00, 0x00, 0xa4, 0xa5, 0x33, 0x02, 0x03, 0x00, 0x00, 0xae, 0x34,
+0x02, 0x03, 0x00, 0x00, 0xaf, 0x35, 0x02, 0x03, 0x00, 0x00, 0xa8, 0x00, 0x5e, 0x0b, 0x61, 0x83,
+0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2,
+0x55, 0xea, 0x20, 0x5e, 0x60, 0x07, 0x61, 0x85, 0x65, 0x8a, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4,
+0x55, 0xeb, 0x20, 0x60, 0x22, 0x0b, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98,
+0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x27, 0x0b, 0x61, 0xa0,
+0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0,
+0x55, 0xe9, 0x20, 0x27, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x03, 0x02, 0x03, 0x00, 0xc9,
+0xfd, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x07, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x08, 0x01, 0x03,
+0x00, 0xcb, 0x0b, 0x01, 0x03, 0x00, 0xca, 0x0d, 0x01, 0x03, 0x00, 0xcc, 0x11, 0x41, 0x00, 0x77,
+0x57, 0x12, 0x42, 0x00, 0x65, 0x45, 0xd5, 0x14, 0x41, 0x00, 0x74, 0x54, 0x15, 0x43, 0x00, 0x79,
+0x59, 0xf9, 0xfa, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00,
+0x6f, 0x4f, 0x19, 0x41, 0x00, 0x70, 0x50, 0x1a, 0x01, 0x03, 0x00, 0xc8, 0x1e, 0x41, 0x00, 0x61,
+0x41, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x20, 0x41, 0x00, 0x64, 0x44, 0x21, 0x41, 0x00, 0x66, 0x46,
+0x22, 0x41, 0x00, 0x67, 0x47, 0x2e, 0x43, 0x00, 0x63, 0x43, 0x87, 0x80, 0x30, 0x41, 0x00, 0x62,
+0x42, 0x31, 0x43, 0x03, 0x00, 0x00, 0xa4, 0xa5, 0x32, 0x41, 0x00, 0x6d, 0x4d, 0x33, 0x02, 0x03,
+0x00, 0x00, 0xae, 0x34, 0x02, 0x03, 0x00, 0x00, 0xaf, 0x00, 0x60, 0x0f, 0x61, 0x85, 0x65, 0x8a,
+0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x77, 0x91, 0x79, 0xc6, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde,
+0x4f, 0xe3, 0x55, 0xeb, 0x57, 0x92, 0x59, 0xc7, 0x20, 0x60, 0x22, 0x0f, 0x61, 0x84, 0x65, 0x89,
+0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x77, 0xa6, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8,
+0x4f, 0x99, 0x55, 0x9a, 0x57, 0xa7, 0x59, 0xf3, 0x20, 0x22, 0x23, 0x0f, 0x61, 0x83, 0x65, 0x88,
+0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x77, 0x9b, 0x79, 0xe4, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7,
+0x4f, 0xe2, 0x55, 0xea, 0x57, 0x9d, 0x59, 0xe5, 0x20, 0x23, 0x27, 0x0f, 0x61, 0xa0, 0x65, 0x82,
+0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x77, 0x86, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6,
+0x4f, 0xe0, 0x55, 0xe9, 0x57, 0x8f, 0x59, 0xed, 0x20, 0x27, 0xee, 0x13, 0x62, 0xf2, 0x63, 0xbd,
+0x64, 0xd0, 0x66, 0x9f, 0x67, 0x9e, 0x6d, 0xac, 0x70, 0xa8, 0x73, 0xf4, 0x74, 0xe7, 0x42, 0xa9,
+0x43, 0xb8, 0x44, 0xd1, 0x46, 0xbe, 0x47, 0xf6, 0x4d, 0xfb, 0x50, 0xad, 0x53, 0xe1, 0x54, 0xe8,
+0x20, 0xee, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x03, 0x01, 0x03, 0x00, 0xc8, 0x05, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x07, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x0d, 0x10, 0x00, 0x00, 0x12, 0x43, 0x00, 0x65, 0x45, 0x82, 0x90, 0x16, 0x43, 0x00, 0x75, 0x55,
+0xed, 0xec, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x01, 0x03, 0x00,
+0xc9, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x22, 0x43, 0x00, 0x67, 0x47,
+0xe5, 0xe4, 0x23, 0x43, 0x00, 0x68, 0x48, 0xe8, 0xe7, 0x24, 0x41, 0x00, 0x6a, 0x4a, 0x28, 0x02,
+0x07, 0x00, 0x00, 0xca, 0x29, 0x01, 0x01, 0x00, 0x7c, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xbe, 0xbd,
+0x2e, 0x43, 0x00, 0x63, 0x43, 0x91, 0x92, 0x31, 0x42, 0x03, 0x00, 0x00, 0xf3, 0x00, 0x22, 0x05,
+0x65, 0x89, 0x69, 0x8b, 0x45, 0xd3, 0x49, 0xd8, 0x20, 0x22, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a,
+0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb,
+0x20, 0x60, 0x5e, 0x13, 0x63, 0x86, 0x65, 0x88, 0x67, 0x9b, 0x68, 0xa9, 0x69, 0x8c, 0x6a, 0x9f,
+0x6f, 0x93, 0x73, 0xc7, 0x75, 0x96, 0x43, 0x8f, 0x45, 0xd2, 0x47, 0x9d, 0x48, 0xa8, 0x49, 0xd7,
+0x4a, 0xac, 0x4f, 0xe2, 0x53, 0xc6, 0x55, 0xea, 0x20, 0x5e, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x03, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0xa0, 0xa0, 0x05, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x07, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x01, 0x03, 0xc8, 0xa0, 0x12, 0x42, 0x00, 0x65, 0x45, 0xaa,
+0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x26,
+0x41, 0x00, 0x6c, 0x4c, 0x29, 0x01, 0x03, 0x00, 0xa0, 0x2c, 0x41, 0x00, 0x7a, 0x5a, 0x2d, 0x41,
+0x00, 0x78, 0x58, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32, 0x42, 0x07,
+0x00, 0x00, 0xa0, 0x00, 0x60, 0x13, 0x61, 0xa5, 0x63, 0x86, 0x65, 0xa9, 0x6c, 0x88, 0x6e, 0xe4,
+0x6f, 0xa2, 0x73, 0x98, 0x78, 0xab, 0x7a, 0xbe, 0x41, 0xa4, 0x43, 0x8f, 0x45, 0xa8, 0x4c, 0x9d,
+0x4e, 0xe3, 0x4f, 0xe0, 0x53, 0x97, 0x58, 0x8d, 0x5a, 0xbd, 0x20, 0x60, 0x00, 0x88, 0xa6, 0x02,
+0x09, 0x00, 0x00, 0x55, 0x53, 0x2c, 0x67, 0x00, 0x55, 0x53, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x03,
+0xdb, 0x01, 0x37, 0x02, 0x00, 0x00, 0x55, 0x03, 0x8f, 0x00, 0xce, 0x00, 0x00, 0x00, 0x52, 0x03,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x75,
+0x03, 0x01, 0x08, 0x01, 0x00, 0x00, 0x47, 0x75, 0x21, 0x01, 0x66, 0x01, 0x00, 0x00, 0x00, 0x08,
+0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x41,
+0x00, 0x65, 0x45, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00,
+0x6f, 0x4f, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x39, 0x00, 0x00, 0x20, 0x00, 0x06, 0x02, 0x03, 0x00,
+0x00, 0xd5, 0x12, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x07, 0x01, 0x03, 0x00, 0xc8, 0x12, 0x03,
+0x03, 0x00, 0x00, 0x82, 0x90, 0x16, 0x03, 0x03, 0x00, 0x00, 0xed, 0xec, 0x1f, 0x41, 0x00, 0x73,
+0x53, 0x22, 0x41, 0x00, 0x67, 0x47, 0x23, 0x41, 0x00, 0x68, 0x48, 0x24, 0x41, 0x00, 0x6a, 0x4a,
+0x28, 0x01, 0x03, 0x00, 0xc9, 0x29, 0x03, 0x03, 0x00, 0x00, 0x8a, 0xd4, 0x2e, 0x41, 0x00, 0x63,
+0x43, 0x31, 0x02, 0x03, 0x00, 0x00, 0xf3, 0x00, 0x5e, 0x13, 0x63, 0x86, 0x67, 0x9b, 0x68, 0xa9,
+0x6a, 0x9f, 0x73, 0xc7, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x43, 0x8f, 0x47, 0x9d,
+0x48, 0xa8, 0x4a, 0xac, 0x53, 0xc6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e,
+0x22, 0x05, 0x65, 0x89, 0x69, 0x8b, 0x45, 0xd3, 0x49, 0xd8, 0x20, 0x22, 0x00, 0x29, 0x00, 0x01,
+0xc8, 0x00, 0x60, 0x0b, 0x61, 0xee, 0x65, 0xd0, 0x69, 0xf6, 0x6f, 0xfa, 0x75, 0xfc, 0x41, 0xef,
+0x45, 0xd1, 0x49, 0xf7, 0x4f, 0xfb, 0x55, 0xfd, 0x20, 0x60, 0x00, 0x07, 0x01, 0x03, 0x00, 0xca,
+0x12, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x14, 0x41, 0x00, 0x74, 0x54, 0x18, 0x03, 0x03, 0x00, 0x00,
+0xab, 0xac, 0x1e, 0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x20, 0x41,
+0x00, 0x64, 0x44, 0x26, 0x41, 0x00, 0x6c, 0x4c, 0x28, 0x01, 0x03, 0xcb, 0xcc, 0x29, 0x01, 0x03,
+0xc8, 0xc9, 0x2e, 0x03, 0x03, 0x00, 0x00, 0x87, 0x80, 0x31, 0x42, 0x00, 0x6e, 0x4e, 0xf2, 0x00,
+0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4,
+0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x03, 0x6e, 0xbd, 0x4e, 0xbe, 0x20, 0x7e,
+0x5e, 0x15, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x73, 0xf9, 0x64, 0xd0,
+0x6c, 0x9b, 0x6e, 0x9e, 0x74, 0xe7, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea,
+0x53, 0xdd, 0x44, 0xd1, 0x4c, 0x9d, 0x4e, 0x9f, 0x54, 0xe8, 0x20, 0x5e, 0x27, 0x0b, 0x61, 0xa0,
+0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0,
+0x55, 0xe9, 0x20, 0x27, 0x22, 0x07, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x45, 0xd3, 0x49, 0xd8,
+0x4f, 0x99, 0x20, 0x22, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0xad, 0x06, 0x02, 0x03, 0x00, 0x00,
+0xd5, 0x07, 0x01, 0x03, 0x00, 0xc8, 0x12, 0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x15, 0x41, 0x00,
+0x79, 0x59, 0x18, 0x03, 0x03, 0x00, 0x00, 0xab, 0xac, 0x1e, 0x03, 0x03, 0x00, 0x00, 0x86, 0x8f,
+0x1f, 0x02, 0x03, 0x00, 0x00, 0xe1, 0x28, 0x01, 0x03, 0xc9, 0xca, 0x29, 0x01, 0x03, 0xcb, 0xcc,
+0x2e, 0x03, 0x03, 0x00, 0x00, 0x87, 0x80, 0x31, 0x43, 0x00, 0x6e, 0x4e, 0xa4, 0xa5, 0x33, 0x02,
+0x03, 0x00, 0x00, 0xae, 0x34, 0x02, 0x03, 0x00, 0x00, 0xaf, 0x35, 0x02, 0x03, 0x00, 0x00, 0xa8,
+0x00, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45,
+0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x27, 0x0b, 0x61, 0xa0, 0x65, 0x82, 0x69,
+0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x20,
+0x27, 0x22, 0x0d, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41,
+0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x60, 0x07, 0x61,
+0x85, 0x65, 0x8a, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x03, 0x6e,
+0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x00, 0x3c, 0xf0, 0x03, 0x27, 0x00, 0x00, 0x59, 0x55, 0x2c, 0x00,
+0x00, 0x42, 0x41, 0x2c, 0x00, 0x00, 0x48, 0x52, 0x2c, 0x00, 0x00, 0x53, 0x49, 0x2c, 0xea, 0x00,
+0x59, 0x55, 0x2c, 0xea, 0x00, 0x42, 0x41, 0x2c, 0xea, 0x00, 0x48, 0x52, 0x2c, 0xea, 0x00, 0x53,
+0x49, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x03, 0xf2,
+0x01, 0x79, 0x02, 0x00, 0x00, 0x71, 0x00, 0x3d, 0x01, 0x93, 0x01, 0x00, 0x00, 0x5a, 0x03, 0x3a,
+0x03, 0x41, 0x03, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x41, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x03, 0x01, 0x01, 0x00, 0x22,
+0x04, 0x03, 0x07, 0x00, 0x00, 0xc9, 0x1e, 0x06, 0x02, 0x07, 0x00, 0x00, 0xca, 0x07, 0x01, 0x01,
+0x00, 0x26, 0x08, 0x02, 0x05, 0x00, 0x2f, 0xcb, 0x09, 0x02, 0x01, 0x00, 0x28, 0xfa, 0x0a, 0x02,
+0x05, 0x00, 0x29, 0xcc, 0x0b, 0x01, 0x01, 0x00, 0x3d, 0x0c, 0x02, 0x04, 0x27, 0x3f, 0xcd, 0x0d,
+0x02, 0x04, 0x2b, 0x2a, 0xce, 0x10, 0x42, 0x03, 0x00, 0x00, 0x5c, 0x11, 0x42, 0x03, 0x00, 0x00,
+0x7c, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0xc4, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x00, 0x2c, 0x1a,
+0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00,
+0x6f, 0x4f, 0x1a, 0x42, 0x03, 0xa0, 0xa0, 0xf6, 0x1b, 0x42, 0x03, 0xa0, 0xa0, 0x9e, 0x1e, 0x41,
+0x00, 0x61, 0x41, 0x21, 0x42, 0x03, 0x00, 0x00, 0x5b, 0x22, 0x42, 0x03, 0x00, 0x00, 0x5d, 0x27,
+0x43, 0x07, 0xa0, 0xa0, 0x00, 0x1d, 0x28, 0x42, 0x03, 0xa0, 0xa0, 0xe1, 0x29, 0x01, 0x03, 0xce,
+0xcd, 0x2b, 0x42, 0x03, 0xa0, 0xa0, 0xcf, 0x2c, 0xc4, 0x00, 0x79, 0x15, 0x59, 0x15, 0x00, 0x15,
+0x19, 0x15, 0x00, 0x15, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x2f, 0x42, 0x03, 0x00, 0x00, 0x40, 0x30,
+0x42, 0x03, 0x00, 0x00, 0x7b, 0x31, 0x42, 0x00, 0x6e, 0x4e, 0x7d, 0x32, 0x42, 0x03, 0x00, 0x00,
+0xf5, 0x33, 0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34, 0x02, 0x01, 0x00, 0x3a, 0x3e, 0x35, 0x03, 0x04,
+0x2d, 0x5f, 0x00, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56, 0x01, 0x00, 0x3c, 0x3e, 0x00, 0x02, 0x02,
+0x03, 0x00, 0x00, 0x7e, 0x03, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x06, 0x02, 0x03, 0x00, 0x00, 0xf8,
+0x08, 0x02, 0x07, 0x00, 0x00, 0xca, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xcb, 0x0c, 0x02, 0x07, 0x00,
+0x00, 0xcc, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xcd, 0x1a, 0x41, 0x00, 0xa4, 0xa5, 0x1b, 0x42, 0x04,
+0x9b, 0x9d, 0xa0, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x27, 0x41, 0x00, 0x91, 0x92, 0x28, 0x41, 0x00,
+0x86, 0x8f, 0x29, 0x01, 0x03, 0xcd, 0xcc, 0x2b, 0x42, 0x04, 0xa6, 0xa7, 0xa0, 0x32, 0x42, 0x03,
+0x00, 0x00, 0x15, 0x00, 0x76, 0x07, 0x63, 0x91, 0x73, 0xa4, 0x7a, 0xa6, 0x43, 0x92, 0x53, 0xa5,
+0x5a, 0xa7, 0x20, 0x76, 0x5e, 0x06, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96,
+0x20, 0x5e, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60,
+0x27, 0x09, 0x61, 0xa0, 0x63, 0x86, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x43, 0x8f,
+0x45, 0x90, 0x20, 0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81,
+0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x2c, 0x03, 0x63, 0x87, 0x43, 0x80,
+0x20, 0x2c, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7e, 0x03, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x05,
+0x02, 0x07, 0x00, 0x00, 0xca, 0x06, 0x02, 0x07, 0x00, 0x00, 0xcb, 0x07, 0x02, 0x07, 0x00, 0x00,
+0xcc, 0x08, 0x02, 0x03, 0x00, 0x00, 0x60, 0x09, 0x02, 0x07, 0x00, 0x00, 0xcd, 0x0a, 0x02, 0x07,
+0x00, 0x00, 0xce, 0x0b, 0x02, 0x07, 0x00, 0x00, 0xcf, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xd0, 0x0d,
+0x02, 0x07, 0x00, 0x00, 0xd1, 0x12, 0x42, 0x03, 0x00, 0x00, 0xaa, 0x13, 0x41, 0x00, 0x72, 0x52,
+0x14, 0x41, 0x00, 0x74, 0x54, 0x1a, 0x41, 0x00, 0xe7, 0xe6, 0x1b, 0x41, 0x00, 0xd0, 0xd1, 0x1f,
+0x41, 0x00, 0x73, 0x53, 0x20, 0x41, 0x00, 0x64, 0x44, 0x25, 0x42, 0x03, 0x00, 0x00, 0x88, 0x26,
+0x42, 0x00, 0x6c, 0x4c, 0x9d, 0x27, 0x41, 0x00, 0x9f, 0xac, 0x28, 0x41, 0x00, 0x86, 0x8f, 0x29,
+0x01, 0x03, 0xd1, 0xd0, 0x2b, 0x41, 0x00, 0xa7, 0xa6, 0x00, 0xf3, 0x13, 0x63, 0x9f, 0x64, 0xd4,
+0x65, 0xd8, 0x6c, 0x96, 0x6e, 0xe5, 0x72, 0xfd, 0x73, 0xe7, 0x74, 0x9c, 0x7a, 0xa7, 0x43, 0xac,
+0x44, 0xd2, 0x45, 0xb7, 0x4c, 0x95, 0x4e, 0xd5, 0x52, 0xfc, 0x53, 0xe6, 0x54, 0x9b, 0x5a, 0xa6,
+0x20, 0xf3, 0x5e, 0x07, 0x61, 0x83, 0x69, 0x8c, 0x6f, 0x93, 0x41, 0xb6, 0x49, 0xd7, 0x4f, 0xe2,
+0x20, 0x5e, 0xf4, 0x03, 0x61, 0xc7, 0x41, 0xc6, 0x20, 0xf4, 0xf8, 0x03, 0x75, 0x85, 0x55, 0xde,
+0x20, 0xf8, 0xf2, 0x05, 0x61, 0xa5, 0x65, 0xa9, 0x41, 0xa4, 0x45, 0xa8, 0x20, 0xf2, 0xfa, 0x03,
+0x7a, 0xbe, 0x5a, 0xbd, 0x20, 0xfa, 0xef, 0x19, 0x61, 0xa0, 0x63, 0x86, 0x65, 0x82, 0x69, 0xa1,
+0x6c, 0x92, 0x6e, 0xe4, 0x6f, 0xa2, 0x72, 0xea, 0x73, 0x98, 0x75, 0xa3, 0x79, 0xec, 0x7a, 0xab,
+0x41, 0xb5, 0x43, 0x8f, 0x45, 0x90, 0x49, 0xd6, 0x4c, 0x91, 0x4e, 0xe3, 0x4f, 0xe0, 0x52, 0xe8,
+0x53, 0x97, 0x55, 0xe9, 0x59, 0xed, 0x5a, 0x8d, 0x20, 0xef, 0xf1, 0x05, 0x6f, 0x8b, 0x75, 0xfb,
+0x4f, 0x8a, 0x55, 0xeb, 0x20, 0xf1, 0xf9, 0x09, 0x61, 0x84, 0x65, 0x89, 0x6f, 0x94, 0x75, 0x81,
+0x41, 0x8e, 0x45, 0xd3, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0xf7, 0x07, 0x63, 0x87, 0x73, 0xad,
+0x74, 0xee, 0x43, 0x80, 0x53, 0xb8, 0x54, 0xdd, 0x20, 0xf7, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00,
+0xd5, 0x00, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5,
+0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6,
+0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xf8, 0x03, 0x61, 0x86, 0x41, 0x8f,
+0x20, 0xf8, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7,
+0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82,
+0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0,
+0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94,
+0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9,
+0xf7, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0xf7, 0x00, 0xad, 0x00, 0x00 };
+
+Bit8u layout_keybrd2sys[25431] = {
+0x4b, 0x43, 0x46, 0x00, 0x01, 0x01, 0x2e, 0x48, 0x65, 0x6e, 0x72, 0x69, 0x71, 0x75, 0x65, 0x20,
+0x50, 0x65, 0x72, 0x6f, 0x6e, 0xff, 0x44, 0x4f, 0x53, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x70, 0x61,
+0x67, 0x65, 0x73, 0x20, 0x2d, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x20, 0x23, 0x32,
+0x20, 0x6f, 0x66, 0x20, 0x33, 0xaa, 0x02, 0x09, 0x00, 0x00, 0x42, 0x47, 0x2c, 0xba, 0x01, 0x42,
+0x47, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x03, 0x99,
+0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x03, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x97,
+0x02, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0xae, 0x01, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xa7,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x03, 0x00, 0x00, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00,
+0x23, 0x05, 0x02, 0x03, 0x00, 0x00, 0xcf, 0x07, 0x02, 0x03, 0x00, 0x00, 0x5e, 0x08, 0x02, 0x03,
+0x00, 0x00, 0x26, 0x09, 0x02, 0x03, 0x00, 0x00, 0x24, 0x1a, 0x02, 0x03, 0x00, 0x00, 0x5b, 0x1b,
+0x02, 0x03, 0x00, 0x00, 0x5d, 0x2b, 0x02, 0x03, 0x00, 0x00, 0x7c, 0x33, 0x02, 0x03, 0x00, 0x00,
+0x3c, 0x34, 0x02, 0x03, 0x00, 0x00, 0x3e, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x03,
+0x01, 0x01, 0x00, 0x3f, 0x04, 0x01, 0x01, 0x00, 0x2b, 0x05, 0x02, 0x05, 0x00, 0x22, 0xa0, 0x07,
+0x01, 0x01, 0x00, 0x3d, 0x08, 0x01, 0x01, 0x00, 0x3a, 0x09, 0x01, 0x01, 0x00, 0x2f, 0x0a, 0x03,
+0x05, 0x00, 0x5f, 0x00, 0x1f, 0x0b, 0x01, 0x01, 0x00, 0xef, 0x0c, 0x01, 0x01, 0x00, 0x49, 0x0d,
+0x01, 0x00, 0x2e, 0x56, 0x10, 0x01, 0x00, 0x2c, 0xf2, 0x11, 0x41, 0x00, 0xe7, 0xe8, 0x12, 0x42,
+0x00, 0xa8, 0xa9, 0xcf, 0x13, 0x41, 0x00, 0xb7, 0xb8, 0x14, 0x41, 0x00, 0xf5, 0xf6, 0x15, 0x41,
+0x00, 0xf9, 0xfa, 0x16, 0x41, 0x00, 0xc6, 0xc7, 0x17, 0x41, 0x00, 0xe3, 0xe4, 0x18, 0x41, 0x00,
+0xa6, 0xa7, 0x19, 0x41, 0x00, 0xf3, 0xf4, 0x1a, 0x41, 0x00, 0xa4, 0xa5, 0x1b, 0x01, 0x00, 0x3b,
+0xfd, 0x1e, 0x41, 0x00, 0xed, 0xee, 0x1f, 0xc1, 0x00, 0xde, 0x1f, 0xe0, 0x00, 0x20, 0x41, 0x00,
+0xa0, 0xa1, 0x21, 0x41, 0x00, 0xd6, 0xd7, 0x22, 0x41, 0x00, 0xe9, 0xea, 0x23, 0x41, 0x00, 0xac,
+0xad, 0x24, 0x41, 0x00, 0xe5, 0xe6, 0x25, 0x41, 0x00, 0xd4, 0xd5, 0x26, 0x41, 0x00, 0xeb, 0xec,
+0x27, 0x41, 0x00, 0xd2, 0xd3, 0x28, 0x41, 0x00, 0xfb, 0xfc, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x78, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0x9c, 0x9d, 0x2d, 0x41, 0x00, 0xbd,
+0xbe, 0x2e, 0x41, 0x00, 0x9e, 0x9f, 0x2f, 0x41, 0x00, 0xf7, 0xf8, 0x30, 0x41, 0x00, 0xaa, 0xab,
+0x31, 0x41, 0x00, 0xb5, 0xb6, 0x32, 0x41, 0x00, 0xd8, 0xdd, 0x33, 0x41, 0x00, 0xe1, 0xe2, 0x34,
+0x41, 0x00, 0xd0, 0xd1, 0x35, 0x41, 0x00, 0xa2, 0xa3, 0x00, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x12, 0x42, 0x03, 0x00, 0x00, 0xcf, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x03,
+0x01, 0x01, 0x00, 0x3f, 0x04, 0x01, 0x01, 0x00, 0x2b, 0x05, 0x01, 0x01, 0x00, 0x22, 0x07, 0x01,
+0x01, 0x00, 0x3d, 0x08, 0x01, 0x01, 0x00, 0x3a, 0x09, 0x01, 0x01, 0x00, 0x2f, 0x0a, 0x03, 0x05,
+0x00, 0x5f, 0x00, 0x1f, 0x0b, 0x01, 0x01, 0x00, 0xef, 0x0c, 0x01, 0x01, 0x00, 0x49, 0x0d, 0x01,
+0x00, 0x2e, 0x56, 0x10, 0x01, 0x00, 0x2c, 0xf2, 0x11, 0x41, 0x00, 0xe7, 0xe8, 0x12, 0x41, 0x00,
+0xa8, 0xa9, 0x13, 0x41, 0x00, 0xb7, 0xb8, 0x14, 0x41, 0x00, 0xf5, 0xf6, 0x15, 0x41, 0x00, 0xf9,
+0xfa, 0x16, 0x41, 0x00, 0xc6, 0xc7, 0x17, 0x41, 0x00, 0xe3, 0xe4, 0x18, 0x41, 0x00, 0xa6, 0xa7,
+0x19, 0x41, 0x00, 0xf3, 0xf4, 0x1a, 0x41, 0x00, 0xa4, 0xa5, 0x1b, 0x01, 0x00, 0x3b, 0xfd, 0x1e,
+0x41, 0x00, 0xed, 0xee, 0x1f, 0xc1, 0x00, 0xde, 0x1f, 0xe0, 0x00, 0x20, 0x41, 0x00, 0xa0, 0xa1,
+0x21, 0x41, 0x00, 0xd6, 0xd7, 0x22, 0x41, 0x00, 0xe9, 0xea, 0x23, 0x41, 0x00, 0xac, 0xad, 0x24,
+0x41, 0x00, 0xe5, 0xe6, 0x25, 0x41, 0x00, 0xd4, 0xd5, 0x26, 0x41, 0x00, 0xeb, 0xec, 0x27, 0x41,
+0x00, 0xd2, 0xd3, 0x28, 0x41, 0x00, 0xfb, 0xfc, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a,
+0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0x9c, 0x9d, 0x2d, 0x41, 0x00, 0xbd, 0xbe, 0x2e,
+0x41, 0x00, 0x9e, 0x9f, 0x2f, 0x41, 0x00, 0xf7, 0xf8, 0x30, 0x41, 0x00, 0xaa, 0xab, 0x31, 0x41,
+0x00, 0xb5, 0xb6, 0x32, 0x41, 0x00, 0xd8, 0xdd, 0x33, 0x41, 0x00, 0xe1, 0xe2, 0x34, 0x41, 0x00,
+0xd0, 0xd1, 0x35, 0x41, 0x00, 0xa2, 0xa3, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b,
+0x00, 0x24, 0x72, 0x06, 0x04, 0xf1, 0x00, 0x42, 0x47, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x03, 0x61, 0x04, 0x00, 0x00, 0x00, 0x00, 0x68, 0x03, 0x78,
+0x02, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x63, 0x06, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x7e,
+0x04, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0x68, 0x02, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x21, 0x31, 0x03, 0x01, 0x00, 0x22, 0x32, 0x04,
+0x01, 0x00, 0x23, 0x33, 0x05, 0x01, 0x00, 0xcf, 0x34, 0x06, 0x01, 0x00, 0x25, 0x35, 0x07, 0x01,
+0x00, 0x26, 0x36, 0x08, 0x01, 0x00, 0x27, 0x37, 0x09, 0x01, 0x00, 0x28, 0x38, 0x0a, 0x01, 0x00,
+0x29, 0x39, 0x0b, 0x01, 0x00, 0x24, 0x30, 0x0c, 0x01, 0x00, 0x3d, 0x2d, 0x0d, 0x02, 0x00, 0x5f,
+0x5f, 0x1f, 0x10, 0xc5, 0x00, 0x6a, 0x24, 0x4a, 0x24, 0x0a, 0x24, 0x00, 0x24, 0x00, 0x24, 0x00,
+0x24, 0x11, 0xc5, 0x00, 0x63, 0x2e, 0x43, 0x2e, 0x03, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x2e,
+0x12, 0xc5, 0x00, 0x75, 0x16, 0x55, 0x16, 0x15, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x13,
+0xc5, 0x00, 0x6b, 0x25, 0x4b, 0x25, 0x0b, 0x25, 0x00, 0x25, 0x00, 0x25, 0x00, 0x25, 0x14, 0xc5,
+0x00, 0x65, 0x12, 0x45, 0x12, 0x05, 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, 0x12, 0x15, 0xc5, 0x00,
+0x6e, 0x31, 0x4e, 0x31, 0x0e, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x16, 0xc5, 0x00, 0x67,
+0x22, 0x47, 0x22, 0x07, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x17, 0x02, 0x00, 0x5b, 0x5b,
+0x1b, 0x18, 0x02, 0x00, 0x5d, 0x5d, 0x1d, 0x19, 0xc5, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x1a, 0x2c,
+0x00, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x1a, 0xc5, 0x00, 0x68, 0x23, 0x48, 0x23, 0x08, 0x23, 0x00,
+0x23, 0x00, 0x23, 0x00, 0x23, 0x1b, 0x04, 0x04, 0x7d, 0x7b, 0x00, 0x2a, 0x3a, 0x1e, 0xc5, 0x00,
+0x66, 0x21, 0x46, 0x21, 0x06, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x1f, 0xc5, 0x00, 0x79,
+0x15, 0x59, 0x15, 0x19, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x20, 0xc5, 0x00, 0x77, 0x11,
+0x57, 0x11, 0x17, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x21, 0xc5, 0x00, 0x61, 0x1e, 0x41,
+0x1e, 0x01, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x22, 0xc5, 0x00, 0x70, 0x19, 0x50, 0x19,
+0x10, 0x19, 0x00, 0x19, 0x00, 0x19, 0x00, 0x19, 0x23, 0xc5, 0x00, 0x72, 0x13, 0x52, 0x13, 0x12,
+0x13, 0x00, 0x13, 0x00, 0x13, 0x00, 0x13, 0x24, 0xc5, 0x00, 0x6f, 0x18, 0x4f, 0x18, 0x0f, 0x18,
+0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x25, 0xc5, 0x00, 0x6c, 0x26, 0x4c, 0x26, 0x0c, 0x26, 0x00,
+0x26, 0x00, 0x26, 0x00, 0x26, 0x26, 0xc5, 0x00, 0x64, 0x20, 0x44, 0x20, 0x04, 0x20, 0x00, 0x20,
+0x00, 0x20, 0x00, 0x20, 0x27, 0xc5, 0x00, 0x76, 0x2f, 0x56, 0x2f, 0x16, 0x2f, 0x00, 0x2f, 0x00,
+0x2f, 0x00, 0x2f, 0x28, 0x02, 0x00, 0x5c, 0x7c, 0x1c, 0x29, 0x01, 0x00, 0x2b, 0x3b, 0x2b, 0x01,
+0x00, 0x3e, 0x2e, 0x2c, 0x02, 0x00, 0x40, 0x5e, 0x1e, 0x2d, 0xc5, 0x00, 0x73, 0x1f, 0x53, 0x1f,
+0x13, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x2e, 0xc5, 0x00, 0x6d, 0x32, 0x4d, 0x32, 0x0d,
+0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x2f, 0xc5, 0x00, 0x69, 0x17, 0x49, 0x17, 0x09, 0x17,
+0x00, 0x17, 0x00, 0x17, 0x00, 0x17, 0x30, 0xc5, 0x00, 0x74, 0x14, 0x54, 0x14, 0x14, 0x14, 0x00,
+0x14, 0x00, 0x14, 0x00, 0x14, 0x31, 0xc5, 0x00, 0x78, 0x2d, 0x58, 0x2d, 0x18, 0x2d, 0x00, 0x2d,
+0x00, 0x2d, 0x00, 0x2d, 0x32, 0xc5, 0x00, 0x62, 0x30, 0x42, 0x30, 0x02, 0x30, 0x00, 0x30, 0x00,
+0x30, 0x00, 0x30, 0x33, 0x10, 0x00, 0x00, 0x34, 0x01, 0x00, 0x3c, 0x2c, 0x35, 0x01, 0x00, 0x3f,
+0x2f, 0x56, 0xc5, 0x00, 0x71, 0x10, 0x51, 0x10, 0x11, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10,
+0x00, 0x14, 0xc5, 0x00, 0x65, 0x12, 0x45, 0x12, 0x05, 0x12, 0xd5, 0x12, 0x00, 0x12, 0x00, 0x12,
+0x00, 0x05, 0x00, 0x01, 0xa0, 0x0d, 0x43, 0x04, 0x9e, 0x9f, 0x00, 0x5f, 0x10, 0xc5, 0x00, 0xbd,
+0x24, 0xbe, 0x24, 0x0a, 0x24, 0x00, 0x24, 0x00, 0x24, 0x00, 0x24, 0x11, 0xc5, 0x00, 0xa4, 0x2e,
+0xa5, 0x2e, 0x03, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x12, 0xc5, 0x00, 0xe7, 0x16, 0xe8,
+0x16, 0x15, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x13, 0xc5, 0x00, 0xc6, 0x25, 0xc7, 0x25,
+0x0b, 0x25, 0x00, 0x25, 0x00, 0x25, 0x00, 0x25, 0x14, 0xc5, 0x00, 0xa8, 0x12, 0xa9, 0x12, 0x05,
+0x12, 0xcf, 0x12, 0x00, 0x12, 0x00, 0x12, 0x15, 0xc5, 0x00, 0xd4, 0x31, 0xd5, 0x31, 0x0e, 0x31,
+0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x16, 0xc5, 0x00, 0xac, 0x22, 0xad, 0x22, 0x07, 0x22, 0x00,
+0x22, 0x00, 0x22, 0x00, 0x22, 0x17, 0x43, 0x04, 0xf5, 0xf6, 0x00, 0x5b, 0x18, 0x43, 0x04, 0xf9,
+0xfa, 0x00, 0x5d, 0x19, 0xc5, 0x00, 0xf3, 0x2c, 0xf4, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x00, 0x2c,
+0x00, 0x2c, 0x1a, 0xc5, 0x00, 0xb5, 0x23, 0xb6, 0x23, 0x08, 0x23, 0x00, 0x23, 0x00, 0x23, 0x00,
+0x23, 0x1b, 0x04, 0x04, 0x2a, 0x3a, 0x00, 0x7d, 0x7b, 0x1e, 0xc5, 0x00, 0xaa, 0x21, 0xab, 0x21,
+0x06, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x1f, 0xc5, 0x00, 0xf1, 0x15, 0xf2, 0x15, 0x19,
+0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x20, 0xc5, 0x00, 0xeb, 0x11, 0xec, 0x11, 0x17, 0x11,
+0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x21, 0xc5, 0x00, 0xa0, 0x1e, 0xa1, 0x1e, 0x01, 0x1e, 0x00,
+0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x22, 0xc5, 0x00, 0xd8, 0x19, 0xdd, 0x19, 0x10, 0x19, 0x00, 0x19,
+0x00, 0x19, 0x00, 0x19, 0x23, 0xc5, 0x00, 0xe1, 0x13, 0xe2, 0x13, 0x12, 0x13, 0x00, 0x13, 0x00,
+0x13, 0x00, 0x13, 0x24, 0xc5, 0x00, 0xd6, 0x18, 0xd7, 0x18, 0x0f, 0x18, 0x00, 0x18, 0x00, 0x18,
+0x00, 0x18, 0x25, 0xc5, 0x00, 0xd0, 0x26, 0xd1, 0x26, 0x0c, 0x26, 0x00, 0x26, 0x00, 0x26, 0x00,
+0x26, 0x26, 0xc5, 0x00, 0xa6, 0x20, 0xa7, 0x20, 0x04, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20,
+0x27, 0xc5, 0x00, 0xe9, 0x2f, 0xea, 0x2f, 0x16, 0x2f, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x2f, 0x28,
+0x44, 0x04, 0xf7, 0xf8, 0x00, 0x5c, 0x7c, 0x29, 0x04, 0x07, 0x00, 0x00, 0x00, 0x98, 0x99, 0x2a,
+0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x04, 0x07, 0x00, 0x00, 0x00, 0x84, 0x85,
+0x2c, 0x44, 0x04, 0xfb, 0xfc, 0x00, 0x40, 0x5e, 0x2d, 0xc5, 0x00, 0xe3, 0x1f, 0xe4, 0x1f, 0x13,
+0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x2e, 0xc5, 0x00, 0xd2, 0x32, 0xd3, 0x32, 0x0d, 0x32,
+0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x2f, 0xc5, 0x00, 0xb7, 0x17, 0xb8, 0x17, 0x09, 0x17, 0x00,
+0x17, 0x00, 0x17, 0x00, 0x17, 0x30, 0xc5, 0x00, 0xe5, 0x14, 0xe6, 0x14, 0x14, 0x14, 0x00, 0x14,
+0x00, 0x14, 0x00, 0x14, 0x31, 0xc5, 0x00, 0xed, 0x2d, 0xee, 0x2d, 0x18, 0x2d, 0x00, 0x2d, 0x00,
+0x2d, 0x00, 0x2d, 0x32, 0xc5, 0x00, 0xa2, 0x30, 0xa3, 0x30, 0x02, 0x30, 0x00, 0x30, 0x00, 0x30,
+0x00, 0x30, 0x33, 0x44, 0x04, 0x9c, 0x9d, 0x00, 0x86, 0x87, 0x34, 0x04, 0x07, 0x00, 0x00, 0x00,
+0x8a, 0x8b, 0x35, 0x04, 0x07, 0x00, 0x00, 0x00, 0x8c, 0x8d, 0x56, 0xc5, 0x00, 0xde, 0x10, 0xe0,
+0x00, 0x11, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x05, 0x00, 0x01, 0xa0, 0x14, 0xc5,
+0x00, 0x65, 0x12, 0x45, 0x12, 0x05, 0x12, 0xcf, 0x12, 0x00, 0x12, 0x00, 0x12, 0x36, 0x05, 0x3f,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x0d, 0x43, 0x04, 0x9e, 0x9f, 0x00, 0x5f, 0x10, 0xc5,
+0x00, 0xbd, 0x24, 0xbe, 0x24, 0x0a, 0x24, 0x00, 0x24, 0x00, 0x24, 0x00, 0x24, 0x11, 0xc5, 0x00,
+0xa4, 0x2e, 0xa5, 0x2e, 0x03, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x12, 0xc5, 0x00, 0xe7,
+0x16, 0xe8, 0x16, 0x15, 0x16, 0x00, 0x16, 0x00, 0x16, 0x00, 0x16, 0x13, 0xc5, 0x00, 0xc6, 0x25,
+0xc7, 0x25, 0x0b, 0x25, 0x00, 0x25, 0x00, 0x25, 0x00, 0x25, 0x14, 0xc5, 0x00, 0xa8, 0x12, 0xa9,
+0x12, 0x05, 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, 0x12, 0x15, 0xc5, 0x00, 0xd4, 0x31, 0xd5, 0x31,
+0x0e, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x16, 0xc5, 0x00, 0xac, 0x22, 0xad, 0x22, 0x07,
+0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x17, 0x43, 0x04, 0xf5, 0xf6, 0x00, 0x5b, 0x18, 0x43,
+0x04, 0xf9, 0xfa, 0x00, 0x5d, 0x19, 0xc5, 0x00, 0xf3, 0x2c, 0xf4, 0x2c, 0x1a, 0x2c, 0x00, 0x2c,
+0x00, 0x2c, 0x00, 0x2c, 0x1a, 0xc5, 0x00, 0xb5, 0x23, 0xb6, 0x23, 0x08, 0x23, 0x00, 0x23, 0x00,
+0x23, 0x00, 0x23, 0x1b, 0x04, 0x04, 0x2a, 0x3a, 0x00, 0x7d, 0x7b, 0x1e, 0xc5, 0x00, 0xaa, 0x21,
+0xab, 0x21, 0x06, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x1f, 0xc5, 0x00, 0xf1, 0x15, 0xf2,
+0x15, 0x19, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x20, 0xc5, 0x00, 0xeb, 0x11, 0xec, 0x11,
+0x17, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x21, 0xc5, 0x00, 0xa0, 0x1e, 0xa1, 0x1e, 0x01,
+0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x22, 0xc5, 0x00, 0xd8, 0x19, 0xdd, 0x19, 0x10, 0x19,
+0x00, 0x19, 0x00, 0x19, 0x00, 0x19, 0x23, 0xc5, 0x00, 0xe1, 0x13, 0xe2, 0x13, 0x12, 0x13, 0x00,
+0x13, 0x00, 0x13, 0x00, 0x13, 0x24, 0xc5, 0x00, 0xd6, 0x18, 0xd7, 0x18, 0x0f, 0x18, 0x00, 0x18,
+0x00, 0x18, 0x00, 0x18, 0x25, 0xc5, 0x00, 0xd0, 0x26, 0xd1, 0x26, 0x0c, 0x26, 0x00, 0x26, 0x00,
+0x26, 0x00, 0x26, 0x26, 0xc5, 0x00, 0xa6, 0x20, 0xa7, 0x20, 0x04, 0x20, 0x00, 0x20, 0x00, 0x20,
+0x00, 0x20, 0x27, 0xc5, 0x00, 0xe9, 0x2f, 0xea, 0x2f, 0x16, 0x2f, 0x00, 0x2f, 0x00, 0x2f, 0x00,
+0x2f, 0x28, 0x44, 0x04, 0xf7, 0xf8, 0x00, 0x5c, 0x7c, 0x29, 0x04, 0x07, 0x00, 0x00, 0x00, 0x98,
+0x99, 0x2a, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x04, 0x07, 0x00, 0x00, 0x00,
+0x84, 0x85, 0x2c, 0x44, 0x04, 0xfb, 0xfc, 0x00, 0x40, 0x5e, 0x2d, 0xc5, 0x00, 0xe3, 0x1f, 0xe4,
+0x1f, 0x13, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x2e, 0xc5, 0x00, 0xd2, 0x32, 0xd3, 0x32,
+0x0d, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x2f, 0xc5, 0x00, 0xb7, 0x17, 0xb8, 0x17, 0x09,
+0x17, 0x00, 0x17, 0x00, 0x17, 0x00, 0x17, 0x30, 0xc5, 0x00, 0xe5, 0x14, 0xe6, 0x14, 0x14, 0x14,
+0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x31, 0xc5, 0x00, 0xed, 0x2d, 0xee, 0x2d, 0x18, 0x2d, 0x00,
+0x2d, 0x00, 0x2d, 0x00, 0x2d, 0x32, 0xc5, 0x00, 0xa2, 0x30, 0xa3, 0x30, 0x02, 0x30, 0x00, 0x30,
+0x00, 0x30, 0x00, 0x30, 0x33, 0x44, 0x04, 0x9c, 0x9d, 0x00, 0x86, 0x87, 0x34, 0x04, 0x07, 0x00,
+0x00, 0x00, 0x8a, 0x8b, 0x35, 0x04, 0x07, 0x00, 0x00, 0x00, 0x8c, 0x8d, 0x56, 0xc5, 0x00, 0xde,
+0x10, 0xe0, 0x00, 0x11, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x36, 0x05, 0x3f, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x12, 0xe7, 0x02, 0x09, 0x00, 0x00, 0x43, 0x45, 0x2c, 0xb9,
+0x01, 0x43, 0x45, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43,
+0x75, 0x72, 0x01, 0xe2, 0x01, 0x00, 0x00, 0x43, 0x75, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b,
+0x75, 0xce, 0x02, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x75, 0xf3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x03, 0x00, 0x00, 0x40, 0x04, 0x02, 0x03,
+0x00, 0x00, 0x23, 0x07, 0x02, 0x03, 0x00, 0x00, 0x5e, 0x08, 0x02, 0x03, 0x00, 0x00, 0x26, 0x09,
+0x02, 0x03, 0x00, 0x00, 0x24, 0x0d, 0x02, 0x03, 0x00, 0x00, 0x3d, 0x1a, 0x02, 0x03, 0x00, 0x00,
+0x5b, 0x1b, 0x02, 0x03, 0x00, 0x00, 0x5d, 0x2b, 0x02, 0x03, 0x00, 0x00, 0x7c, 0x33, 0x02, 0x03,
+0x00, 0x00, 0x3c, 0x34, 0x02, 0x03, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04,
+0x01, 0x01, 0x00, 0xfc, 0x05, 0x01, 0x01, 0x00, 0x3b, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01,
+0x01, 0x00, 0x3f, 0x0d, 0x01, 0x00, 0xfe, 0x2b, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00,
+0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5,
+0x85, 0xfd, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8,
+0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95,
+0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20,
+0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1,
+0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26,
+0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0xc1,
+0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x01, 0x01,
+0x00, 0x2f, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1,
+0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92,
+0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35,
+0x01, 0x00, 0x2e, 0x2c, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x10, 0x43, 0x03, 0x00, 0x00,
+0xd6, 0xd5, 0x12, 0x43, 0x03, 0x00, 0x00, 0xbe, 0xbd, 0x16, 0x41, 0x00, 0x75, 0x55, 0x18, 0x43,
+0x00, 0x6f, 0x4f, 0xde, 0xdd, 0x1a, 0x43, 0x00, 0xf9, 0xf8, 0x5b, 0x7b, 0x1b, 0x43, 0x00, 0xd2,
+0xd1, 0x5d, 0x7d, 0x1e, 0x43, 0x00, 0x61, 0x41, 0xc7, 0xc6, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xfb,
+0xfa, 0x22, 0x43, 0x03, 0x00, 0x00, 0xb6, 0xb5, 0x29, 0x00, 0x01, 0xc8, 0x2c, 0x43, 0x03, 0x00,
+0x00, 0xd8, 0xd7, 0x2d, 0x43, 0x03, 0x00, 0x00, 0xd4, 0xd3, 0x2e, 0x43, 0x03, 0x00, 0x00, 0xd0,
+0xcf, 0x31, 0x43, 0x03, 0x00, 0x00, 0xb8, 0xb7, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79,
+0x39, 0x00, 0x00, 0x20, 0x00, 0x60, 0x07, 0x61, 0xf3, 0x6f, 0xf5, 0x75, 0xf7, 0x41, 0xf2, 0x4f,
+0xf4, 0x55, 0xf6, 0x20, 0x60, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xfc,
+0x05, 0x01, 0x01, 0x00, 0x3b, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x0d,
+0x01, 0x00, 0xfe, 0x2b, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41,
+0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15, 0x41,
+0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00,
+0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea,
+0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82,
+0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90,
+0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84,
+0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0,
+0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0x41,
+0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00,
+0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec,
+0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01, 0x00, 0x2e, 0x2c,
+0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00,
+0xc2, 0x19, 0x03, 0x04, 0xbb, 0x01, 0x43, 0x45, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x43, 0x75, 0x8a, 0x01, 0xfa, 0x01, 0x00, 0x00, 0x43, 0x75, 0x91, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x3b, 0x75, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x75, 0x0b, 0x02,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x03, 0x00,
+0x00, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00, 0x23, 0x05, 0x02, 0x03, 0x00, 0x00, 0x24, 0x07, 0x02,
+0x03, 0x00, 0x00, 0x5e, 0x08, 0x02, 0x03, 0x00, 0x00, 0x26, 0x09, 0x02, 0x03, 0x00, 0x00, 0x2a,
+0x1a, 0x02, 0x03, 0x00, 0x00, 0x5b, 0x1b, 0x02, 0x03, 0x00, 0x00, 0x5d, 0x33, 0x02, 0x03, 0x00,
+0x00, 0x3c, 0x34, 0x02, 0x03, 0x00, 0x00, 0x3e, 0x00, 0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01,
+0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x01, 0x00, 0x22, 0x34, 0x06, 0x01, 0x00,
+0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f,
+0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d,
+0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12,
+0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15,
+0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41,
+0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00,
+0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2,
+0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00,
+0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4,
+0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0x01, 0x00, 0x7c, 0x2b,
+0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00,
+0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac,
+0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c,
+0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0,
+0x00, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x10, 0x43, 0x03, 0x00, 0x00, 0xd6, 0xd5, 0x12,
+0x43, 0x03, 0x00, 0x00, 0xbe, 0xbd, 0x16, 0x41, 0x00, 0x75, 0x55, 0x18, 0x43, 0x00, 0x6f, 0x4f,
+0xde, 0xdd, 0x1a, 0x43, 0x00, 0xf9, 0xf8, 0x5b, 0x7b, 0x1b, 0x43, 0x00, 0xd2, 0xd1, 0x5d, 0x7d,
+0x1e, 0x43, 0x00, 0x61, 0x41, 0xc7, 0xc6, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xfb, 0xfa, 0x22, 0x43,
+0x03, 0x00, 0x00, 0xb6, 0xb5, 0x29, 0x00, 0x01, 0xc8, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xd8, 0xd7,
+0x2d, 0x43, 0x03, 0x00, 0x00, 0xd4, 0xd3, 0x2e, 0x43, 0x03, 0x00, 0x00, 0xd0, 0xcf, 0x31, 0x43,
+0x03, 0x00, 0x00, 0xb8, 0xb7, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x39, 0x00, 0x00,
+0x20, 0x00, 0x60, 0x07, 0x61, 0xf3, 0x6f, 0xf5, 0x75, 0xf7, 0x41, 0xf2, 0x4f, 0xf4, 0x55, 0xf6,
+0x20, 0x60, 0x00, 0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00,
+0x2f, 0x33, 0x05, 0x01, 0x00, 0x22, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c,
+0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39,
+0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10,
+0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41,
+0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41,
+0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00,
+0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4,
+0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80,
+0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae,
+0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86,
+0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0x02, 0x00, 0xfe, 0x2b, 0x7c, 0x2a, 0x04, 0x1f, 0x00, 0x00,
+0x00, 0x00, 0x7a, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00,
+0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8,
+0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81,
+0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0x00, 0x12, 0x42, 0x03,
+0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0xc8, 0x58, 0x04, 0x13,
+0x00, 0x00, 0x47, 0x4b, 0x2c, 0x00, 0x00, 0x45, 0x4c, 0x2c, 0x3f, 0x01, 0x47, 0x4b, 0x2c, 0x3f,
+0x01, 0x45, 0x4c, 0x09, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+0x03, 0x3a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x65, 0x03, 0x79, 0x01, 0x49, 0x02, 0x00, 0x00, 0xe1,
+0x02, 0x24, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x02, 0x7e, 0x02, 0x2d, 0x03, 0x00, 0x00, 0x53,
+0x03, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x00, 0x53, 0x03, 0x5a, 0x03, 0x13, 0x04, 0x00, 0x00, 0x5a,
+0x03, 0xf7, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00,
+0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x07, 0x01, 0x01, 0x00,
+0x26, 0x08, 0x01, 0x01, 0x00, 0x2f, 0x09, 0x01, 0x01, 0x00, 0x28, 0x0a, 0x01, 0x01, 0x00, 0x29,
+0x0b, 0x01, 0x01, 0x00, 0x3d, 0x0c, 0x01, 0x00, 0x27, 0x3f, 0x0d, 0x01, 0x00, 0x2b, 0x2a, 0x12,
+0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41,
+0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x01, 0x03,
+0xcb, 0xcc, 0x28, 0x01, 0x03, 0xca, 0xc9, 0x29, 0x03, 0x04, 0x5c, 0x7c, 0x00, 0x1c, 0x2b, 0x01,
+0x01, 0xc8, 0x40, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x33, 0x02, 0x00,
+0x2c, 0x3b, 0x3c, 0x34, 0x02, 0x00, 0x2e, 0x3a, 0x3e, 0x35, 0x03, 0x04, 0x2d, 0x5f, 0x00, 0x1f,
+0x39, 0x00, 0x00, 0x20, 0x56, 0x01, 0x00, 0x3c, 0x3e, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5,
+0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45,
+0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f,
+0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69,
+0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20,
+0x5e, 0xef, 0x0f, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79,
+0xec, 0x41, 0xb5, 0x43, 0x80, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20,
+0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41,
+0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00, 0x04, 0x01, 0x01, 0x00,
+0x9c, 0x07, 0x01, 0x01, 0x00, 0x89, 0x0c, 0x01, 0x01, 0x00, 0xf8, 0x10, 0x01, 0x00, 0x88, 0x8e,
+0x11, 0x01, 0x00, 0xed, 0x8a, 0x12, 0x42, 0x00, 0xde, 0xa8, 0x87, 0x13, 0x41, 0x00, 0xeb, 0xc7,
+0x14, 0x41, 0x00, 0xee, 0xd0, 0x15, 0x41, 0x00, 0xf2, 0xd1, 0x16, 0x41, 0x00, 0xe2, 0xac, 0x17,
+0x41, 0x00, 0xe3, 0xad, 0x18, 0x41, 0x00, 0xe9, 0xbe, 0x19, 0x41, 0x00, 0xea, 0xc6, 0x1a, 0x01,
+0x01, 0x00, 0xae, 0x1b, 0x01, 0x01, 0x00, 0xaf, 0x1e, 0x41, 0x00, 0xd6, 0xa4, 0x1f, 0x41, 0x00,
+0xec, 0xcf, 0x20, 0x41, 0x00, 0xdd, 0xa7, 0x21, 0x41, 0x00, 0xf3, 0xd2, 0x22, 0x41, 0x00, 0xd8,
+0xa6, 0x23, 0x41, 0x00, 0xe1, 0xaa, 0x24, 0x41, 0x00, 0xe8, 0xbd, 0x25, 0x41, 0x00, 0xe4, 0xb5,
+0x26, 0x41, 0x00, 0xe5, 0xb6, 0x27, 0x02, 0x07, 0xc8, 0xc9, 0xca, 0x28, 0x01, 0x00, 0x8c, 0x8b,
+0x29, 0x01, 0x00, 0xab, 0xf1, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x01, 0x00,
+0x99, 0x9a, 0x2c, 0xc1, 0x00, 0xe0, 0x00, 0xa9, 0x2c, 0x2d, 0x41, 0x00, 0xf4, 0xd3, 0x2e, 0x41,
+0x00, 0xf6, 0xd4, 0x2f, 0x41, 0x00, 0xfa, 0xd5, 0x30, 0x41, 0x00, 0xd7, 0xa5, 0x31, 0x41, 0x00,
+0xe7, 0xb8, 0x32, 0x41, 0x00, 0xe6, 0xb7, 0x56, 0x01, 0x00, 0xf5, 0x97, 0x00, 0x12, 0x42, 0x03,
+0x00, 0x00, 0x87, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0xef, 0x0f, 0xd6, 0x9b,
+0xde, 0x9d, 0xe1, 0x9e, 0xe3, 0x9f, 0xe9, 0xa2, 0xf2, 0xa3, 0xfa, 0xfd, 0xa4, 0x86, 0xa8, 0x8d,
+0xaa, 0x8f, 0xad, 0x90, 0xbe, 0x92, 0xd1, 0x95, 0xd5, 0x98, 0x20, 0xef, 0xf9, 0x05, 0xe3, 0xa0,
+0xf2, 0xfb, 0xad, 0x91, 0xd1, 0x96, 0x20, 0xf9, 0xf7, 0x03, 0xe3, 0xa1, 0xf2, 0xfc, 0x20, 0xf7,
+0x00, 0x0c, 0x01, 0x01, 0x00, 0xf8, 0x10, 0x01, 0x00, 0xf9, 0x51, 0x11, 0x01, 0x00, 0xaa, 0x57,
+0x12, 0x41, 0x00, 0x9c, 0x84, 0x13, 0x41, 0x00, 0xa8, 0x90, 0x14, 0x41, 0x00, 0xab, 0x92, 0x15,
+0x41, 0x00, 0xac, 0x93, 0x16, 0x41, 0x00, 0x9f, 0x87, 0x17, 0x41, 0x00, 0xa0, 0x88, 0x18, 0x41,
+0x00, 0xa6, 0x8e, 0x19, 0x41, 0x00, 0xa7, 0x8f, 0x1e, 0x41, 0x00, 0x98, 0x80, 0x1f, 0x41, 0x00,
+0xa9, 0x91, 0x20, 0x41, 0x00, 0x9b, 0x83, 0x21, 0x41, 0x00, 0xad, 0x94, 0x22, 0x41, 0x00, 0x9a,
+0x82, 0x23, 0x41, 0x00, 0x9e, 0x86, 0x24, 0x41, 0x00, 0xa5, 0x8d, 0x25, 0x41, 0x00, 0xa1, 0x89,
+0x26, 0x41, 0x00, 0xa2, 0x8a, 0x27, 0x01, 0x03, 0xc8, 0xc9, 0x29, 0x01, 0x00, 0x60, 0xf1, 0x2a,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x01, 0x00, 0xfd, 0x7c, 0x2c, 0x41, 0x00, 0x9d,
+0x85, 0x2d, 0x41, 0x00, 0xae, 0x95, 0x2e, 0x41, 0x00, 0xaf, 0x96, 0x2f, 0xc1, 0x00, 0xe0, 0x00,
+0x97, 0x2f, 0x30, 0x41, 0x00, 0x99, 0x81, 0x31, 0x41, 0x00, 0xa4, 0x8c, 0x32, 0x41, 0x00, 0xa3,
+0x8b, 0x56, 0x01, 0x00, 0x15, 0x7c, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00,
+0x27, 0x0f, 0x98, 0xe1, 0x9c, 0xe2, 0x9e, 0xe3, 0xa0, 0xe5, 0xa6, 0xe6, 0xac, 0xe7, 0xe0, 0xe9,
+0x80, 0xea, 0x84, 0xeb, 0x86, 0xec, 0x88, 0xed, 0x8e, 0xee, 0x93, 0xef, 0x97, 0xf0, 0x20, 0x27,
+0x22, 0x05, 0xa0, 0xe4, 0xac, 0xe8, 0x88, 0xf4, 0x93, 0xf5, 0x20, 0x22, 0x00, 0x0c, 0x01, 0x01,
+0x00, 0xf8, 0x10, 0x10, 0x00, 0x00, 0x11, 0x01, 0x00, 0xed, 0x7c, 0x12, 0x41, 0x00, 0xde, 0xa8,
+0x13, 0x41, 0x00, 0xeb, 0xc7, 0x14, 0x41, 0x00, 0xee, 0xd0, 0x15, 0x41, 0x00, 0xf2, 0xd1, 0x16,
+0x41, 0x00, 0xe2, 0xac, 0x17, 0x41, 0x00, 0xe3, 0xad, 0x18, 0x41, 0x00, 0xe9, 0xbe, 0x19, 0x41,
+0x00, 0xea, 0xc6, 0x1a, 0x01, 0x01, 0x00, 0xae, 0x1b, 0x01, 0x01, 0x00, 0xaf, 0x1e, 0x41, 0x00,
+0xd6, 0xa4, 0x1f, 0x41, 0x00, 0xec, 0xcf, 0x20, 0x41, 0x00, 0xdd, 0xa7, 0x21, 0x41, 0x00, 0xf3,
+0xd2, 0x22, 0x41, 0x00, 0xd8, 0xa6, 0x23, 0x41, 0x00, 0xe1, 0xaa, 0x24, 0x41, 0x00, 0xe8, 0xbd,
+0x25, 0x41, 0x00, 0xe4, 0xb5, 0x26, 0x41, 0x00, 0xe5, 0xb6, 0x27, 0x02, 0x07, 0xc8, 0xc9, 0xca,
+0x29, 0x01, 0x00, 0xab, 0xf1, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x2b, 0x01, 0x00,
+0x5c, 0x7c, 0x2c, 0xc1, 0x00, 0xe0, 0x00, 0xa9, 0x2c, 0x2d, 0x41, 0x00, 0xf4, 0xd3, 0x2e, 0x41,
+0x00, 0xf6, 0xd4, 0x2f, 0x41, 0x00, 0xfa, 0xd5, 0x30, 0x41, 0x00, 0xd7, 0xa5, 0x31, 0x41, 0x00,
+0xe7, 0xb8, 0x32, 0x41, 0x00, 0xe6, 0xb7, 0x56, 0x01, 0x00, 0xf5, 0x7c, 0x00, 0x36, 0x04, 0x1f,
+0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0xef, 0x0f, 0xd6, 0x9b, 0xde, 0x9d, 0xe1, 0x9e, 0xe3, 0x9f,
+0xe9, 0xa2, 0xf2, 0xa3, 0xfa, 0xfd, 0xa4, 0x86, 0xa8, 0x8d, 0xaa, 0x8f, 0xad, 0x90, 0xbe, 0x92,
+0xd1, 0x95, 0xd5, 0x98, 0x20, 0xef, 0xf9, 0x03, 0xe3, 0xa0, 0xf2, 0xfb, 0x20, 0xf9, 0x27, 0x03,
+0xe3, 0xa1, 0xf2, 0xfc, 0x20, 0x27, 0x00, 0x42, 0xbe, 0x04, 0x09, 0xdc, 0x00, 0x47, 0x4b, 0x2c,
+0xdc, 0x00, 0x45, 0x4c, 0x09, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x65, 0x03, 0xdd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x65, 0x03, 0xfb, 0x00, 0x34, 0x02, 0x00, 0x00,
+0xe1, 0x02, 0x3a, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x02, 0x69, 0x02, 0x79, 0x03, 0x00, 0x00,
+0x53, 0x03, 0x5c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x53, 0x03, 0xa6, 0x03, 0x83, 0x04, 0x00, 0x00,
+0x5a, 0x03, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x22, 0xfd, 0x04, 0x02,
+0x01, 0x00, 0x7e, 0xfc, 0x05, 0x02, 0x03, 0x00, 0x00, 0x9c, 0x06, 0x02, 0x03, 0x00, 0x00, 0xf5,
+0x07, 0x02, 0x01, 0x00, 0x26, 0x14, 0x08, 0x01, 0x01, 0x00, 0x2f, 0x09, 0x02, 0x01, 0x00, 0x28,
+0xcf, 0x0a, 0x02, 0x01, 0x00, 0x29, 0xdd, 0x0b, 0x02, 0x01, 0x00, 0x3d, 0xf8, 0x0c, 0x02, 0x00,
+0x27, 0x3f, 0xf1, 0x0d, 0x02, 0x00, 0x5d, 0x5b, 0xab, 0x1a, 0x02, 0x00, 0x2b, 0x2a, 0xae, 0x1b,
+0x02, 0x00, 0x7d, 0x7b, 0xaf, 0x27, 0x01, 0x00, 0x27, 0x60, 0x28, 0x10, 0x00, 0x00, 0x29, 0x03,
+0x04, 0x5c, 0x7c, 0x00, 0x1c, 0x2b, 0x02, 0x00, 0x23, 0x40, 0xaa, 0x33, 0x02, 0x00, 0x2c, 0x3b,
+0x3c, 0x34, 0x02, 0x00, 0x2e, 0x3a, 0x3e, 0x35, 0x03, 0x04, 0x2d, 0x5f, 0x00, 0x1f, 0x39, 0x00,
+0x00, 0x20, 0x56, 0x01, 0x00, 0x3c, 0x3e, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x03,
+0x02, 0x03, 0x00, 0x00, 0x99, 0x04, 0x02, 0x01, 0x00, 0x9c, 0x9a, 0x05, 0x02, 0x03, 0x00, 0x00,
+0x9c, 0x06, 0x02, 0x03, 0x00, 0x00, 0xf5, 0x07, 0x02, 0x03, 0x00, 0x00, 0x14, 0x09, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x0a, 0x02, 0x03, 0x00, 0x00, 0x8a, 0x0b, 0x02, 0x03, 0x00, 0x00, 0xf8, 0x0c,
+0x02, 0x03, 0x00, 0x00, 0xf1, 0x0d, 0x02, 0x03, 0x00, 0x00, 0xab, 0x10, 0x01, 0x00, 0x3b, 0x3a,
+0x11, 0x01, 0x00, 0xed, 0x7e, 0x12, 0x42, 0x00, 0xde, 0xa8, 0x87, 0x13, 0x41, 0x00, 0xeb, 0xc7,
+0x14, 0x41, 0x00, 0xee, 0xd0, 0x15, 0x41, 0x00, 0xf2, 0xd1, 0x16, 0x41, 0x00, 0xe2, 0xac, 0x17,
+0x41, 0x00, 0xe3, 0xad, 0x18, 0x41, 0x00, 0xe9, 0xbe, 0x19, 0x41, 0x00, 0xea, 0xc6, 0x1e, 0x41,
+0x00, 0xd6, 0xa4, 0x1f, 0x41, 0x00, 0xec, 0xcf, 0x20, 0x41, 0x00, 0xdd, 0xa7, 0x21, 0x41, 0x00,
+0xf3, 0xd2, 0x22, 0x41, 0x00, 0xd8, 0xa6, 0x23, 0x41, 0x00, 0xe1, 0xaa, 0x24, 0x41, 0x00, 0xe8,
+0xbd, 0x25, 0x41, 0x00, 0xe4, 0xb5, 0x26, 0x41, 0x00, 0xe5, 0xb6, 0x27, 0x02, 0x07, 0xc8, 0xc9,
+0xca, 0x28, 0x02, 0x07, 0xc9, 0xca, 0xca, 0x29, 0x01, 0x00, 0xab, 0xf1, 0x2a, 0x04, 0x1f, 0x00,
+0x00, 0x00, 0x00, 0x78, 0x2b, 0x02, 0x03, 0x00, 0x00, 0x89, 0x2c, 0xc1, 0x00, 0xe0, 0x00, 0xa9,
+0x2c, 0x2d, 0x41, 0x00, 0xf4, 0xd3, 0x2e, 0x41, 0x00, 0xf6, 0xd4, 0x2f, 0x42, 0x00, 0xfa, 0xd5,
+0x97, 0x30, 0x41, 0x00, 0xd7, 0xa5, 0x31, 0x41, 0x00, 0xe7, 0xb8, 0x32, 0x41, 0x00, 0xe6, 0xb7,
+0x00, 0x03, 0x02, 0x03, 0x00, 0x00, 0x99, 0x04, 0x02, 0x03, 0x00, 0x00, 0x9a, 0x05, 0x02, 0x03,
+0x00, 0x00, 0x9c, 0x06, 0x02, 0x03, 0x00, 0x00, 0xf5, 0x07, 0x02, 0x03, 0x00, 0x00, 0x14, 0x09,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x03, 0x00, 0x00, 0x8a, 0x0b, 0x02, 0x03, 0x00, 0x00,
+0xf8, 0x0c, 0x02, 0x03, 0x00, 0x00, 0xf1, 0x0d, 0x02, 0x03, 0x00, 0x00, 0xab, 0x12, 0x42, 0x03,
+0x00, 0x00, 0x87, 0x2b, 0x02, 0x03, 0x00, 0x00, 0x89, 0x2f, 0x42, 0x00, 0xfa, 0xd5, 0x97, 0x36,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0xef, 0x0f, 0xd6, 0x9b, 0xde, 0x9d, 0xe1, 0x9e,
+0xe3, 0x9f, 0xe9, 0xa2, 0xf2, 0xa3, 0xfa, 0xfd, 0xa4, 0x86, 0xa8, 0x8d, 0xaa, 0x8f, 0xad, 0x90,
+0xbe, 0x92, 0xd1, 0x95, 0xd5, 0x98, 0x20, 0xef, 0xf9, 0x05, 0xe3, 0xa0, 0xf2, 0xfb, 0xad, 0x91,
+0xd1, 0x96, 0x20, 0xf9, 0xf7, 0x03, 0xe3, 0xa1, 0xf2, 0xfc, 0x20, 0xf7, 0x00, 0x04, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00, 0x00, 0x15, 0x09,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x10, 0x10, 0x00, 0x00, 0x11, 0x01, 0x00, 0xaa, 0x7e, 0x12, 0x41, 0x00, 0x9c, 0x84, 0x13,
+0x41, 0x00, 0xa8, 0x90, 0x14, 0x41, 0x00, 0xab, 0x92, 0x15, 0x41, 0x00, 0xac, 0x93, 0x16, 0x41,
+0x00, 0x9f, 0x87, 0x17, 0x41, 0x00, 0xa0, 0x88, 0x18, 0x41, 0x00, 0xa6, 0x8e, 0x19, 0x41, 0x00,
+0xa7, 0x8f, 0x1a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x1b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x1e, 0x41,
+0x00, 0x98, 0x80, 0x1f, 0x41, 0x00, 0xa9, 0x91, 0x20, 0x41, 0x00, 0x9b, 0x83, 0x21, 0x41, 0x00,
+0xad, 0x94, 0x22, 0x41, 0x00, 0x9a, 0x82, 0x23, 0x41, 0x00, 0x9e, 0x86, 0x24, 0x41, 0x00, 0xa5,
+0x8d, 0x25, 0x41, 0x00, 0xa1, 0x89, 0x26, 0x41, 0x00, 0xa2, 0x8a, 0x27, 0x01, 0x03, 0xc8, 0xc9,
+0x28, 0x01, 0x03, 0xc9, 0xc9, 0x29, 0x01, 0x00, 0x5c, 0xf1, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x7a, 0x2b, 0x02, 0x05, 0x00, 0xf8, 0xa0, 0x2c, 0x41, 0x00, 0x9d, 0x85, 0x2d, 0x41, 0x00,
+0xae, 0x95, 0x2e, 0x41, 0x00, 0xaf, 0x96, 0x2f, 0xc1, 0x00, 0xe0, 0x00, 0x97, 0x2f, 0x30, 0x41,
+0x00, 0x99, 0x81, 0x31, 0x41, 0x00, 0xa4, 0x8c, 0x32, 0x41, 0x00, 0xa3, 0x8b, 0x00, 0x04, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00, 0x00, 0x15,
+0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x1a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x1b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2b, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x27, 0x0f, 0x98,
+0xe1, 0x9c, 0xe2, 0x9e, 0xe3, 0xa0, 0xe5, 0xa6, 0xe6, 0xac, 0xe7, 0xe0, 0xe9, 0x80, 0xea, 0x84,
+0xeb, 0x86, 0xec, 0x88, 0xed, 0x8e, 0xee, 0x93, 0xef, 0x97, 0xf0, 0x20, 0x27, 0x22, 0x05, 0xa0,
+0xe4, 0xac, 0xe8, 0x88, 0xf4, 0x93, 0xf5, 0x20, 0x22, 0x00, 0x03, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x10, 0x10, 0x00, 0x00, 0x11, 0x01, 0x02, 0xed, 0xa0, 0x12, 0x41, 0x00, 0xde, 0xa8,
+0x13, 0x41, 0x00, 0xeb, 0xc7, 0x14, 0x41, 0x00, 0xee, 0xd0, 0x15, 0x41, 0x00, 0xf2, 0xd1, 0x16,
+0x41, 0x00, 0xe2, 0xac, 0x17, 0x41, 0x00, 0xe3, 0xad, 0x18, 0x41, 0x00, 0xe9, 0xbe, 0x19, 0x41,
+0x00, 0xea, 0xc6, 0x1e, 0x41, 0x00, 0xd6, 0xa4, 0x1f, 0x41, 0x00, 0xec, 0xcf, 0x20, 0x41, 0x00,
+0xdd, 0xa7, 0x21, 0x41, 0x00, 0xf3, 0xd2, 0x22, 0x41, 0x00, 0xd8, 0xa6, 0x23, 0x41, 0x00, 0xe1,
+0xaa, 0x24, 0x41, 0x00, 0xe8, 0xbd, 0x25, 0x41, 0x00, 0xe4, 0xb5, 0x26, 0x41, 0x00, 0xe5, 0xb6,
+0x27, 0x02, 0x07, 0xc8, 0xc9, 0xca, 0x28, 0x02, 0x07, 0xc9, 0xca, 0xca, 0x2a, 0x04, 0x1f, 0x00,
+0x00, 0x00, 0x00, 0x7c, 0x2b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2c, 0xc1, 0x00, 0xe0, 0x00, 0xa9,
+0x2c, 0x2d, 0x41, 0x00, 0xf4, 0xd3, 0x2e, 0x41, 0x00, 0xf6, 0xd4, 0x2f, 0x41, 0x00, 0xfa, 0xd5,
+0x30, 0x41, 0x00, 0xd7, 0xa5, 0x31, 0x41, 0x00, 0xe7, 0xb8, 0x32, 0x41, 0x00, 0xe6, 0xb7, 0x00,
+0x03, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x36, 0x04,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0xef, 0x0f, 0xd6, 0x9b, 0xde, 0x9d, 0xe1, 0x9e, 0xe3,
+0x9f, 0xe9, 0xa2, 0xf2, 0xa3, 0xfa, 0xfd, 0xa4, 0x86, 0xa8, 0x8d, 0xaa, 0x8f, 0xad, 0x90, 0xbe,
+0x92, 0xd1, 0x95, 0xd5, 0x98, 0x20, 0xef, 0xf9, 0x03, 0xe3, 0xa0, 0xf2, 0xfb, 0x20, 0xf9, 0x27,
+0x03, 0xe3, 0xa1, 0xf2, 0xfc, 0x20, 0x27, 0x00, 0x70, 0x24, 0x06, 0x09, 0xcb, 0x01, 0x47, 0x4b,
+0x2c, 0xcb, 0x01, 0x45, 0x4c, 0x09, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x65, 0x03, 0xcc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x65, 0x03, 0x12, 0x03, 0x0b, 0x04, 0x00,
+0x00, 0xe1, 0x02, 0x07, 0x05, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x02, 0x40, 0x04, 0xda, 0x04, 0x00,
+0x00, 0x53, 0x03, 0xf9, 0x05, 0x00, 0x00, 0x00, 0x00, 0x53, 0x03, 0x22, 0x05, 0xc8, 0x05, 0x00,
+0x00, 0x5a, 0x03, 0xb0, 0x01, 0x97, 0x02, 0x00, 0x00, 0x52, 0x03, 0xcf, 0x00, 0x97, 0x02, 0x00,
+0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x07,
+0x02, 0x03, 0x00, 0x00, 0x14, 0x0b, 0x02, 0x03, 0x00, 0x00, 0xf8, 0x0c, 0x02, 0x03, 0x00, 0x00,
+0xf1, 0x10, 0x01, 0x00, 0x3b, 0x3a, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00, 0x79, 0x59,
+0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a,
+0x02, 0x03, 0x00, 0x00, 0xae, 0x1b, 0x02, 0x03, 0x00, 0x00, 0xaf, 0x1e, 0x41, 0x00, 0x61, 0x41,
+0x27, 0x01, 0x03, 0xc8, 0xc9, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x39,
+0x00, 0x00, 0x20, 0x00, 0x02, 0x03, 0x03, 0x00, 0x00, 0xad, 0xfb, 0x04, 0x02, 0x03, 0x00, 0x00,
+0xfc, 0x05, 0x03, 0x03, 0x00, 0x00, 0xcf, 0x9c, 0x07, 0x02, 0x03, 0x00, 0xca, 0xac, 0x08, 0x02,
+0x03, 0x00, 0x00, 0xab, 0x09, 0x02, 0x03, 0x00, 0x00, 0xf3, 0x0b, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x0c, 0x02, 0x03, 0x00, 0x00, 0xbe, 0x0d, 0x03, 0x03, 0x00, 0x00, 0x9e, 0xf6, 0x10, 0x43, 0x00,
+0x71, 0x51, 0x84, 0x8e, 0x11, 0x43, 0x03, 0x00, 0x00, 0x86, 0x8f, 0x12, 0x43, 0x00, 0x65, 0x45,
+0x82, 0x90, 0x13, 0x42, 0x03, 0x00, 0x00, 0xa9, 0x14, 0x43, 0x03, 0x00, 0x00, 0xe7, 0xe8, 0x15,
+0x43, 0x00, 0x79, 0x59, 0x81, 0x9a, 0x16, 0x43, 0x00, 0x75, 0x55, 0xa3, 0xe9, 0x17, 0x43, 0x00,
+0x69, 0x49, 0xa1, 0xd6, 0x18, 0xc3, 0x00, 0x6f, 0x18, 0x4f, 0x18, 0xa2, 0x18, 0xe0, 0x00, 0x19,
+0x43, 0x03, 0x00, 0x00, 0x94, 0x99, 0x1e, 0x43, 0x00, 0x61, 0x41, 0xa0, 0xb5, 0x1f, 0x43, 0x03,
+0x00, 0x00, 0xe1, 0xf5, 0x20, 0x43, 0x03, 0x00, 0x00, 0xd0, 0xd1, 0x26, 0x43, 0x03, 0x00, 0x00,
+0x9b, 0x9d, 0x27, 0x03, 0x00, 0x3b, 0x3a, 0xf4, 0xf8, 0x28, 0x03, 0x0f, 0x00, 0x00, 0xcb, 0xcc,
+0x29, 0x01, 0x03, 0xc8, 0xc9, 0x2b, 0x03, 0x03, 0x00, 0x00, 0xaa, 0xdd, 0x2c, 0x43, 0x03, 0x00,
+0x00, 0x91, 0x92, 0x2e, 0x43, 0x00, 0x63, 0x43, 0xb8, 0xbd, 0x31, 0x43, 0x00, 0x6e, 0x4e, 0xa4,
+0xa5, 0x32, 0x42, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x03, 0x03, 0x00, 0x00, 0x87, 0x80, 0x35, 0x02,
+0x03, 0x00, 0x00, 0xa8, 0x00, 0x02, 0x03, 0x03, 0x00, 0x00, 0xad, 0xfb, 0x04, 0x02, 0x03, 0x00,
+0x00, 0xfc, 0x05, 0x03, 0x03, 0x00, 0x00, 0xcf, 0x9c, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x07,
+0x02, 0x03, 0x00, 0xca, 0xac, 0x08, 0x02, 0x03, 0x00, 0x00, 0xab, 0x09, 0x02, 0x03, 0x00, 0x00,
+0xf3, 0x0b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x03, 0x00, 0x00, 0xbe, 0x0d, 0x03, 0x03,
+0x00, 0x00, 0x9e, 0xf6, 0x10, 0x43, 0x00, 0x71, 0x51, 0x84, 0x8e, 0x11, 0x43, 0x03, 0x00, 0x00,
+0x86, 0x8f, 0x12, 0x43, 0x00, 0x65, 0x45, 0x82, 0x90, 0x13, 0x42, 0x03, 0x00, 0x00, 0xa9, 0x14,
+0x43, 0x03, 0x00, 0x00, 0xe7, 0xe8, 0x15, 0x43, 0x00, 0x79, 0x59, 0x81, 0x9a, 0x16, 0x43, 0x00,
+0x75, 0x55, 0xa3, 0xe9, 0x17, 0x43, 0x00, 0x69, 0x49, 0xa1, 0xd6, 0x18, 0xc3, 0x00, 0x6f, 0x18,
+0x4f, 0x18, 0xa2, 0x18, 0xe0, 0x00, 0x19, 0x43, 0x03, 0x00, 0x00, 0x94, 0x99, 0x1e, 0x43, 0x00,
+0x61, 0x41, 0xa0, 0xb5, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xe1, 0xf5, 0x20, 0x43, 0x03, 0x00, 0x00,
+0xd0, 0xd1, 0x26, 0x43, 0x03, 0x00, 0x00, 0x9b, 0x9d, 0x27, 0x03, 0x00, 0x3b, 0x3a, 0xf4, 0xf8,
+0x28, 0x03, 0x0f, 0x00, 0x00, 0xcb, 0xcc, 0x29, 0x01, 0x03, 0xc8, 0xc9, 0x2b, 0x03, 0x03, 0x00,
+0x00, 0xaa, 0xdd, 0x2c, 0x43, 0x03, 0x00, 0x00, 0x91, 0x92, 0x2e, 0x43, 0x00, 0x63, 0x43, 0xb8,
+0xbd, 0x31, 0x43, 0x00, 0x6e, 0x4e, 0xa4, 0xa5, 0x32, 0x42, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x03,
+0x03, 0x00, 0x00, 0x87, 0x80, 0x35, 0x02, 0x03, 0x00, 0x00, 0xa8, 0x00, 0x60, 0x0b, 0x61, 0x85,
+0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3,
+0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5,
+0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96,
+0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0f, 0x61, 0xa0,
+0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x43, 0x80,
+0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84,
+0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8,
+0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00, 0x03, 0x02, 0x03, 0x00, 0x00, 0x99, 0x04, 0x02, 0x03,
+0x00, 0x00, 0x9a, 0x05, 0x02, 0x03, 0x00, 0x00, 0x9c, 0x06, 0x02, 0x03, 0x00, 0x00, 0xf5, 0x0a,
+0x02, 0x03, 0x00, 0x00, 0x8a, 0x0d, 0x02, 0x03, 0x00, 0x00, 0xab, 0x11, 0x01, 0x02, 0xed, 0xca,
+0x12, 0x42, 0x00, 0xde, 0xa8, 0x87, 0x13, 0x41, 0x00, 0xeb, 0xc7, 0x14, 0x41, 0x00, 0xee, 0xd0,
+0x15, 0x41, 0x00, 0xf2, 0xd1, 0x16, 0x41, 0x00, 0xe2, 0xac, 0x17, 0x41, 0x00, 0xe3, 0xad, 0x18,
+0x41, 0x00, 0xe9, 0xbe, 0x19, 0x41, 0x00, 0xea, 0xc6, 0x1e, 0x41, 0x00, 0xd6, 0xa4, 0x1f, 0x41,
+0x00, 0xec, 0xcf, 0x20, 0x41, 0x00, 0xdd, 0xa7, 0x21, 0x41, 0x00, 0xf3, 0xd2, 0x22, 0x41, 0x00,
+0xd8, 0xa6, 0x23, 0x41, 0x00, 0xe1, 0xaa, 0x24, 0x41, 0x00, 0xe8, 0xbd, 0x25, 0x41, 0x00, 0xe4,
+0xb5, 0x26, 0x41, 0x00, 0xe5, 0xb6, 0x27, 0x02, 0x07, 0x00, 0x00, 0xca, 0x2a, 0x04, 0x1f, 0x00,
+0x00, 0x00, 0x00, 0x78, 0x2b, 0x02, 0x03, 0x00, 0x00, 0x89, 0x2c, 0xc1, 0x00, 0xe0, 0x00, 0xa9,
+0x2c, 0x2d, 0x41, 0x00, 0xf4, 0xd3, 0x2e, 0x42, 0x00, 0xf6, 0xd4, 0x97, 0x2f, 0x41, 0x00, 0xfa,
+0xd5, 0x30, 0x41, 0x00, 0xd7, 0xa5, 0x31, 0x41, 0x00, 0xe7, 0xb8, 0x32, 0x41, 0x00, 0xe6, 0xb7,
+0x00, 0x03, 0x02, 0x03, 0x00, 0x00, 0x99, 0x04, 0x02, 0x03, 0x00, 0x00, 0x9a, 0x05, 0x02, 0x03,
+0x00, 0x00, 0x9c, 0x06, 0x02, 0x03, 0x00, 0x00, 0xf5, 0x0a, 0x02, 0x03, 0x00, 0x00, 0x8a, 0x0d,
+0x02, 0x03, 0x00, 0x00, 0xab, 0x12, 0x42, 0x03, 0x00, 0x00, 0x87, 0x2b, 0x02, 0x03, 0x00, 0x00,
+0x89, 0x2e, 0x42, 0x03, 0x00, 0x00, 0x97, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00,
+0xef, 0x0f, 0xd6, 0x9b, 0xde, 0x9d, 0xe1, 0x9e, 0xe3, 0x9f, 0xe9, 0xa2, 0xf2, 0xa3, 0xfa, 0xfd,
+0xa4, 0x86, 0xa8, 0x8d, 0xaa, 0x8f, 0xad, 0x90, 0xbe, 0x92, 0xd1, 0x95, 0xd5, 0x98, 0x20, 0xef,
+0xf9, 0x05, 0xe3, 0xa0, 0xf2, 0xfb, 0xad, 0x91, 0xd1, 0x96, 0x20, 0xf9, 0xf7, 0x03, 0xe3, 0xa1,
+0xf2, 0xfc, 0x20, 0xf7, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0x15, 0x11, 0x01, 0x02, 0xaa, 0x00,
+0x12, 0x41, 0x00, 0x9c, 0x84, 0x13, 0x41, 0x00, 0xa8, 0x90, 0x14, 0x41, 0x00, 0xab, 0x92, 0x15,
+0x41, 0x00, 0xac, 0x93, 0x16, 0x41, 0x00, 0x9f, 0x87, 0x17, 0x41, 0x00, 0xa0, 0x88, 0x18, 0x41,
+0x00, 0xa6, 0x8e, 0x19, 0x41, 0x00, 0xa7, 0x8f, 0x1a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x1b, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x1e, 0x41, 0x00, 0x98, 0x80, 0x1f, 0x41, 0x00, 0xa9, 0x91, 0x20, 0x41,
+0x00, 0x9b, 0x83, 0x21, 0x41, 0x00, 0xad, 0x94, 0x22, 0x41, 0x00, 0x9a, 0x82, 0x23, 0x41, 0x00,
+0x9e, 0x86, 0x24, 0x41, 0x00, 0xa5, 0x8d, 0x25, 0x41, 0x00, 0xa1, 0x89, 0x26, 0x41, 0x00, 0xa2,
+0x8a, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2c, 0x41, 0x00, 0x9d, 0x85, 0x2d, 0x41,
+0x00, 0xae, 0x95, 0x2e, 0x41, 0x00, 0xaf, 0x96, 0x2f, 0xc1, 0x00, 0xe0, 0x00, 0x97, 0x2f, 0x30,
+0x41, 0x00, 0x99, 0x81, 0x31, 0x41, 0x00, 0xa4, 0x8c, 0x32, 0x41, 0x00, 0xa3, 0x8b, 0x00, 0x27,
+0x0f, 0x98, 0xe1, 0x9c, 0xe2, 0x9e, 0xe3, 0xa0, 0xe5, 0xa6, 0xe6, 0xac, 0xe7, 0xe0, 0xe9, 0x80,
+0xea, 0x84, 0xeb, 0x86, 0xec, 0x88, 0xed, 0x8e, 0xee, 0x93, 0xef, 0x97, 0xf0, 0x20, 0x27, 0x22,
+0x05, 0xa0, 0xe4, 0xac, 0xe8, 0x88, 0xf4, 0x93, 0xf5, 0x20, 0x22, 0x00, 0x06, 0x02, 0x03, 0x00,
+0x00, 0x15, 0x1a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x1b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x36, 0x04,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x03, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x02, 0x03,
+0x00, 0x00, 0x9c, 0x06, 0x02, 0x03, 0x00, 0x00, 0xf5, 0x0d, 0x02, 0x03, 0x00, 0x00, 0xab, 0x11,
+0x01, 0x02, 0xed, 0xca, 0x12, 0x41, 0x00, 0xde, 0xa8, 0x13, 0x41, 0x00, 0xeb, 0xc7, 0x14, 0x41,
+0x00, 0xee, 0xd0, 0x15, 0x41, 0x00, 0xf2, 0xd1, 0x16, 0x41, 0x00, 0xe2, 0xac, 0x17, 0x41, 0x00,
+0xe3, 0xad, 0x18, 0x41, 0x00, 0xe9, 0xbe, 0x19, 0x41, 0x00, 0xea, 0xc6, 0x1e, 0x41, 0x00, 0xd6,
+0xa4, 0x1f, 0x41, 0x00, 0xec, 0xcf, 0x20, 0x41, 0x00, 0xdd, 0xa7, 0x21, 0x41, 0x00, 0xf3, 0xd2,
+0x22, 0x41, 0x00, 0xd8, 0xa6, 0x23, 0x41, 0x00, 0xe1, 0xaa, 0x24, 0x41, 0x00, 0xe8, 0xbd, 0x25,
+0x41, 0x00, 0xe4, 0xb5, 0x26, 0x41, 0x00, 0xe5, 0xb6, 0x27, 0x02, 0x07, 0x00, 0x00, 0xca, 0x2a,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x2c, 0xc1, 0x00, 0xe0, 0x00, 0xa9, 0x2c, 0x2d, 0x41,
+0x00, 0xf4, 0xd3, 0x2e, 0x41, 0x00, 0xf6, 0xd4, 0x2f, 0x41, 0x00, 0xfa, 0xd5, 0x30, 0x41, 0x00,
+0xd7, 0xa5, 0x31, 0x41, 0x00, 0xe7, 0xb8, 0x32, 0x41, 0x00, 0xe6, 0xb7, 0x00, 0xef, 0x0f, 0xd6,
+0x9b, 0xde, 0x9d, 0xe1, 0x9e, 0xe3, 0x9f, 0xe9, 0xa2, 0xf2, 0xa3, 0xfa, 0xfd, 0xa4, 0x86, 0xa8,
+0x8d, 0xaa, 0x8f, 0xad, 0x90, 0xbe, 0x92, 0xd1, 0x95, 0xd5, 0x98, 0x20, 0xef, 0xf9, 0x03, 0xe3,
+0xa0, 0xf2, 0xfb, 0x20, 0xf9, 0x27, 0x03, 0xe3, 0xa1, 0xf2, 0xfc, 0x20, 0x27, 0x00, 0x03, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x05, 0x02, 0x03, 0x00, 0x00, 0x9c, 0x06, 0x02, 0x03, 0x00, 0x00, 0xf5,
+0x0d, 0x02, 0x03, 0x00, 0x00, 0xab, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x78,
+0x34, 0x01, 0x09, 0x00, 0x00, 0x49, 0x53, 0x2c, 0xca, 0x01, 0x49, 0x53, 0x04, 0x02, 0x00, 0x00,
+0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xd8, 0x00, 0xe5, 0x00, 0x00, 0x00,
+0x52, 0x03, 0x00, 0x00, 0xe5, 0x00, 0x00, 0x00, 0x5d, 0x03, 0x02, 0x01, 0x0d, 0x01, 0x00, 0x00,
+0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x03, 0x02, 0x01, 0x00, 0x22, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00, 0x9c, 0x07, 0x01, 0x01, 0x00,
+0x26, 0x08, 0x02, 0x01, 0x00, 0x2f, 0x7b, 0x09, 0x03, 0x01, 0x00, 0x28, 0x5b, 0x1b, 0x0a, 0x03,
+0x01, 0x00, 0x29, 0x5d, 0x1d, 0x0b, 0x02, 0x01, 0x00, 0x3d, 0x7d, 0x0c, 0x43, 0x00, 0x94, 0x99,
+0x5c, 0x1c, 0x0d, 0x03, 0x04, 0x2d, 0x5f, 0x00, 0x1f, 0x10, 0x42, 0x03, 0x00, 0x00, 0x40, 0x12,
+0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41,
+0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x41, 0x00, 0xd0, 0xd1, 0x1b, 0x02, 0x00,
+0x27, 0x3f, 0x7e, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x41, 0x00, 0x91, 0x92, 0x28, 0x03, 0x03,
+0xc8, 0xc8, 0x5e, 0x1e, 0x29, 0x02, 0x00, 0x3c, 0x3e, 0x7c, 0x2b, 0x02, 0x00, 0x2b, 0x2a, 0x60,
+0x33, 0x01, 0x01, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x00, 0x3a, 0x35, 0x41, 0x00, 0xe7, 0xe8, 0x39,
+0x00, 0x00, 0x20, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5,
+0x00, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41,
+0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0x00, 0x1a, 0x41,
+0x00, 0x8c, 0x8b, 0x35, 0x41, 0x00, 0x95, 0x8d, 0x00, 0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69,
+0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0x98, 0x41, 0xa4, 0x45, 0x90, 0x49, 0xa5, 0x4f, 0xa6, 0x55,
+0xa7, 0x59, 0x97, 0x20, 0x27, 0x00, 0xca, 0xee, 0x01, 0x09, 0xa1, 0x00, 0x49, 0x53, 0x2c, 0xc5,
+0x00, 0x49, 0x53, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a,
+0x03, 0xf7, 0x00, 0x04, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x5d,
+0x03, 0x83, 0x01, 0x99, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x22, 0x40, 0x04, 0x02, 0x03,
+0x00, 0x00, 0x9c, 0x05, 0x02, 0x01, 0x00, 0xcf, 0x24, 0x07, 0x01, 0x01, 0x00, 0x26, 0x08, 0x02,
+0x01, 0x00, 0x2f, 0x7b, 0x09, 0x03, 0x01, 0x00, 0x28, 0x5b, 0x1b, 0x0a, 0x03, 0x01, 0x00, 0x29,
+0x5d, 0x1d, 0x0b, 0x02, 0x01, 0x00, 0x3d, 0x7d, 0x0c, 0x43, 0x00, 0x94, 0x99, 0x5c, 0x1c, 0x0d,
+0x03, 0x04, 0x2d, 0x5f, 0x00, 0x1f, 0x10, 0x42, 0x03, 0x00, 0x00, 0x40, 0x12, 0x41, 0x00, 0x65,
+0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49,
+0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x41, 0x00, 0xd0, 0xd1, 0x1b, 0x02, 0x04, 0x27, 0x3f, 0xcd,
+0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x41, 0x00, 0x91, 0x92, 0x28, 0x03, 0x07, 0xca, 0xca, 0xc9,
+0x1e, 0x29, 0x02, 0x03, 0xcc, 0xcb, 0xf8, 0x2b, 0x02, 0x04, 0x2b, 0x2a, 0xc8, 0x2e, 0x41, 0x00,
+0x63, 0x43, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32, 0x42, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x02, 0x01,
+0x00, 0x3b, 0x3c, 0x34, 0x02, 0x01, 0x00, 0x3a, 0x3e, 0x35, 0x42, 0x00, 0xe7, 0xe8, 0x7c, 0x39,
+0x00, 0x00, 0x20, 0x56, 0x02, 0x00, 0x3c, 0x3e, 0x7c, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5,
+0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f,
+0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x5e,
+0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49,
+0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f,
+0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59,
+0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79,
+0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0xf8, 0x03, 0x61,
+0x86, 0x41, 0x8f, 0x20, 0xf8, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e,
+0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x00, 0x05, 0x01, 0x03, 0x00, 0xa0, 0x1a, 0x41, 0x00, 0x8c, 0x8b,
+0x1b, 0x02, 0x03, 0x00, 0x00, 0x7e, 0x35, 0x41, 0x00, 0x95, 0x8d, 0x00, 0x60, 0x03, 0x61, 0x85,
+0x65, 0x8a, 0x20, 0x60, 0x5e, 0x05, 0x61, 0x83, 0x65, 0x88, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e,
+0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0x98, 0x41, 0xa4,
+0x45, 0x90, 0x49, 0xa5, 0x4f, 0xa6, 0x55, 0xa7, 0x59, 0x97, 0x20, 0x27, 0x22, 0x08, 0x61, 0x84,
+0x65, 0x89, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0xf8, 0x03,
+0x61, 0x86, 0x41, 0x8f, 0x20, 0xf8, 0x00, 0x29, 0x62, 0x08, 0x09, 0x00, 0x00, 0x52, 0x4f, 0x2c,
+0x4d, 0x01, 0x52, 0x4f, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x54, 0x03, 0x0f, 0x02, 0x89, 0x02, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00,
+0x5a, 0x03, 0x81, 0x01, 0x88, 0x01, 0x00, 0x00, 0x50, 0x03, 0x4a, 0x04, 0x00, 0x00, 0x00, 0x00,
+0x50, 0x03, 0x4a, 0x03, 0x00, 0x00, 0x00, 0x00, 0x65, 0x04, 0xc3, 0x05, 0x00, 0x00, 0x00, 0x00,
+0x65, 0x04, 0xc4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x39, 0x75, 0x37, 0x06, 0x00, 0x00, 0x00, 0x00,
+0x3a, 0x75, 0xde, 0x07, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x75, 0xc0, 0x06, 0xc9, 0x07, 0x00, 0x00,
+0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x02, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x04, 0x0f, 0x00, 0x00,
+0xc9, 0x00, 0x1e, 0x05, 0x01, 0x01, 0x00, 0xcf, 0x06, 0x02, 0x07, 0x00, 0x00, 0xca, 0x07, 0x01,
+0x01, 0x00, 0x26, 0x08, 0x02, 0x05, 0x00, 0x2f, 0xcb, 0x09, 0x02, 0x01, 0x00, 0x28, 0xfa, 0x0a,
+0x02, 0x05, 0x00, 0x29, 0xcc, 0x0b, 0x01, 0x01, 0x00, 0x3d, 0x0c, 0x02, 0x04, 0x2b, 0x3f, 0xcd,
+0x0d, 0x02, 0x04, 0x27, 0x2a, 0xce, 0x10, 0x42, 0x03, 0x00, 0x00, 0x5c, 0x11, 0x42, 0x03, 0x00,
+0x00, 0x7c, 0x12, 0x42, 0x00, 0x65, 0x45, 0xf6, 0x13, 0x42, 0x03, 0x00, 0x00, 0x9e, 0x14, 0x42,
+0x03, 0x00, 0x00, 0x24, 0x15, 0xc5, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0xe1, 0x2c, 0x00, 0x2c, 0x1a,
+0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x42, 0x00, 0x69, 0x49, 0x3c, 0x18, 0x42,
+0x00, 0x6f, 0x4f, 0x3e, 0x1a, 0x02, 0x03, 0xa0, 0xa0, 0xf6, 0x1b, 0x42, 0x00, 0x8c, 0xd7, 0x9e,
+0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x02, 0x03, 0xa0, 0xa0, 0x24, 0x28, 0x02, 0x03, 0xa0, 0xa0,
+0xe1, 0x29, 0x01, 0x00, 0x5d, 0x5b, 0x2b, 0x41, 0x00, 0x83, 0xb6, 0x2c, 0xc5, 0x00, 0x79, 0x15,
+0x59, 0x15, 0x00, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x2f,
+0x42, 0x03, 0x00, 0x00, 0x40, 0x30, 0x42, 0x03, 0x00, 0x00, 0x7b, 0x31, 0x42, 0x00, 0x6e, 0x4e,
+0x7d, 0x32, 0x42, 0x03, 0x00, 0x00, 0xf5, 0x33, 0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34, 0x02, 0x01,
+0x00, 0x3a, 0x3e, 0x35, 0x04, 0x0c, 0x2d, 0x5f, 0x00, 0x00, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56,
+0x01, 0x00, 0x3c, 0x3e, 0x00, 0x16, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x7e, 0x07, 0x61, 0xc6,
+0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83,
+0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2,
+0x55, 0xea, 0x20, 0x5e, 0xf8, 0x03, 0x61, 0x86, 0x41, 0x8f, 0x20, 0xf8, 0x60, 0x0b, 0x61, 0x85,
+0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3,
+0x55, 0xeb, 0x20, 0x60, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3,
+0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef,
+0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e,
+0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0xf7, 0x03, 0x63, 0x87, 0x43, 0x80,
+0x20, 0xf7, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7e, 0x03, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x05,
+0x02, 0x07, 0x00, 0x00, 0xca, 0x06, 0x02, 0x07, 0x00, 0x00, 0xcb, 0x07, 0x02, 0x07, 0x00, 0x00,
+0xcc, 0x08, 0x02, 0x03, 0x00, 0x00, 0x60, 0x09, 0x02, 0x07, 0x00, 0x00, 0xcd, 0x0a, 0x02, 0x07,
+0x00, 0x00, 0xce, 0x0b, 0x02, 0x07, 0x00, 0x00, 0xcf, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xd0, 0x0d,
+0x02, 0x07, 0x00, 0x00, 0xd1, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41, 0x00, 0x74, 0x54, 0x16,
+0x42, 0x03, 0x00, 0x00, 0xaa, 0x1a, 0x41, 0x00, 0xc7, 0xc6, 0x1f, 0x42, 0x00, 0x73, 0x53, 0xd0,
+0x20, 0x42, 0x00, 0x64, 0x44, 0xd1, 0x25, 0x42, 0x03, 0x00, 0x00, 0x88, 0x26, 0x42, 0x00, 0x6c,
+0x4c, 0x9d, 0x27, 0x41, 0x00, 0xad, 0xb8, 0x28, 0x41, 0x00, 0xee, 0xdd, 0x00, 0xf3, 0x13, 0x63,
+0x9f, 0x64, 0xd4, 0x65, 0xd8, 0x6c, 0x96, 0x6e, 0xe5, 0x72, 0xfd, 0x73, 0xe7, 0x74, 0x9c, 0x7a,
+0xa7, 0x43, 0xac, 0x44, 0xd2, 0x45, 0xb7, 0x4c, 0x95, 0x4e, 0xd5, 0x52, 0xfc, 0x53, 0xe6, 0x54,
+0x9b, 0x5a, 0xa6, 0x20, 0xf3, 0x5e, 0x07, 0x61, 0x83, 0x69, 0x8c, 0x6f, 0x93, 0x41, 0xb6, 0x49,
+0xd7, 0x4f, 0xe2, 0x20, 0x5e, 0xf4, 0x03, 0x61, 0xc7, 0x41, 0xc6, 0x20, 0xf4, 0xf8, 0x03, 0x75,
+0x85, 0x55, 0xde, 0x20, 0xf8, 0xf2, 0x05, 0x61, 0xa5, 0x65, 0xa9, 0x41, 0xa4, 0x45, 0xa8, 0x20,
+0xf2, 0xfa, 0x03, 0x7a, 0xbe, 0x5a, 0xbd, 0x20, 0xfa, 0xef, 0x19, 0x61, 0xa0, 0x63, 0x86, 0x65,
+0x82, 0x69, 0xa1, 0x6c, 0x92, 0x6e, 0xe4, 0x6f, 0xa2, 0x72, 0xea, 0x73, 0x98, 0x75, 0xa3, 0x79,
+0xec, 0x7a, 0xab, 0x41, 0xb5, 0x43, 0x8f, 0x45, 0x90, 0x49, 0xd6, 0x4c, 0x91, 0x4e, 0xe3, 0x4f,
+0xe0, 0x52, 0xe8, 0x53, 0x97, 0x55, 0xe9, 0x59, 0xed, 0x5a, 0x8d, 0x20, 0xef, 0xf1, 0x05, 0x6f,
+0x8b, 0x75, 0xfb, 0x4f, 0x8a, 0x55, 0xeb, 0x20, 0xf1, 0xf9, 0x09, 0x61, 0x84, 0x65, 0x89, 0x6f,
+0x94, 0x75, 0x81, 0x41, 0x8e, 0x45, 0xd3, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0xf7, 0x07, 0x63,
+0x87, 0x73, 0xad, 0x74, 0xee, 0x43, 0x80, 0x53, 0xb8, 0x54, 0xdd, 0x20, 0xf7, 0x00, 0x02, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x01, 0x03, 0x00, 0xa0, 0x06,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x43, 0x00,
+0xec, 0x9c, 0x27, 0x2a, 0x10, 0x41, 0x00, 0xee, 0x9e, 0x11, 0x41, 0x00, 0xe9, 0x99, 0x12, 0x42,
+0x00, 0xa5, 0x85, 0xfa, 0x13, 0xc2, 0x04, 0xe0, 0x00, 0x90, 0x13, 0xa0, 0x13, 0x14, 0x41, 0x00,
+0xe2, 0x92, 0x15, 0xc5, 0x04, 0xa7, 0x2c, 0x87, 0x2c, 0xa0, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00,
+0x2c, 0x16, 0x42, 0x00, 0xe3, 0x93, 0xfd, 0x17, 0x41, 0x00, 0xf7, 0xf6, 0x18, 0x41, 0x00, 0xae,
+0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x42, 0x00, 0xf9, 0xf8, 0xfa, 0x1b, 0x42, 0x04, 0xa8,
+0x88, 0xa0, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4,
+0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00, 0xf3, 0xf2, 0x23, 0x41, 0x00, 0xa3, 0x83,
+0x24, 0x41, 0x00, 0xa6, 0x86, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x27,
+0x41, 0x00, 0xe8, 0x98, 0x28, 0x42, 0x04, 0xe6, 0x96, 0xa0, 0x29, 0x43, 0x00, 0xf5, 0xf4, 0x5d,
+0x5b, 0x2a, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x2b, 0x41, 0x00, 0xef, 0x9f, 0x2c,
+0xc5, 0x00, 0xa9, 0x15, 0x89, 0x15, 0x00, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x41,
+0x00, 0xe5, 0x95, 0x2e, 0x41, 0x00, 0xe7, 0x97, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00,
+0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x42, 0x04, 0xac, 0x8c, 0xa0, 0x00, 0x02, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x01, 0x03, 0x00, 0xa0, 0x06,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x12, 0x02, 0x03, 0x00, 0x00, 0xfa, 0x13, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x15,
+0xc5, 0x04, 0x7a, 0x2c, 0x5a, 0x2c, 0xa0, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x02,
+0x03, 0x00, 0x00, 0xfd, 0x1a, 0x02, 0x03, 0x00, 0x00, 0xfa, 0x1b, 0x10, 0x00, 0x00, 0x28, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x2b, 0x10, 0x00, 0x00, 0x32, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x36, 0x05,
+0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x05, 0x01, 0x01, 0x00, 0xfd, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x08,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x43, 0x00, 0xec, 0x9c, 0x27, 0x2a, 0x10, 0x41,
+0x00, 0xee, 0x9e, 0x11, 0x41, 0x00, 0xe9, 0x99, 0x12, 0x42, 0x00, 0xa5, 0x85, 0xfa, 0x13, 0xc2,
+0x04, 0xe0, 0x00, 0x90, 0x13, 0xa0, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0xc5, 0x04, 0xa7,
+0x2c, 0x87, 0x2c, 0xa0, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0xe3, 0x93,
+0x17, 0x41, 0x00, 0xf7, 0xf6, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a,
+0x42, 0x00, 0xf9, 0xf8, 0xfa, 0x1b, 0x42, 0x04, 0xa8, 0x88, 0xa0, 0x1e, 0x41, 0x00, 0xa0, 0x80,
+0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22,
+0x41, 0x00, 0xf3, 0xf2, 0x23, 0x41, 0x00, 0xa3, 0x83, 0x24, 0x41, 0x00, 0xa6, 0x86, 0x25, 0x41,
+0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x27, 0x41, 0x00, 0xe8, 0x98, 0x28, 0x42, 0x04,
+0xe6, 0x96, 0xa0, 0x29, 0x43, 0x00, 0xf5, 0xf4, 0x5d, 0x5b, 0x2a, 0x05, 0x3f, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7d, 0x2b, 0x41, 0x00, 0xef, 0x9f, 0x2c, 0xc5, 0x00, 0xa9, 0x15, 0x89, 0x15, 0x00,
+0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x41, 0x00, 0xe5, 0x95, 0x2e, 0x41, 0x00, 0xe7,
+0x97, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d,
+0x32, 0x42, 0x04, 0xac, 0x8c, 0xa0, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x05, 0x01, 0x01, 0x00, 0xfd, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x08, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x02, 0x03, 0x00,
+0x00, 0xfa, 0x13, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x15, 0xc5, 0x04, 0x7a, 0x2c, 0x5a, 0x2c, 0xa0,
+0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x1a, 0x02, 0x03, 0x00, 0x00, 0xfa, 0x1b, 0x10, 0x00,
+0x00, 0x28, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2b, 0x10, 0x00, 0x00, 0x32, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x36, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x06, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x12, 0x03, 0x03, 0x00, 0x00, 0x88, 0xd2, 0x13, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x15, 0xc5,
+0x04, 0x7a, 0x2c, 0x5a, 0x2c, 0xa0, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x43, 0x03,
+0x00, 0x00, 0x81, 0x9a, 0x17, 0x41, 0x01, 0x00, 0x98, 0x18, 0x43, 0x03, 0x00, 0x00, 0x94, 0x99,
+0x1a, 0x42, 0x04, 0x84, 0x8e, 0xa0, 0x1b, 0x42, 0x04, 0x8d, 0x49, 0xa0, 0x22, 0x43, 0x03, 0x00,
+0x00, 0xa7, 0xa6, 0x27, 0x41, 0x00, 0x9f, 0x9e, 0x28, 0x42, 0x04, 0xf3, 0xf4, 0xa0, 0x2b, 0x41,
+0x00, 0x87, 0x80, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x03, 0x01, 0x03, 0x00, 0xc8, 0x04,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x01, 0x03, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x43, 0x00, 0xec, 0x9c, 0x27, 0x2a, 0x10,
+0x41, 0x00, 0xee, 0x9e, 0x11, 0x41, 0x00, 0xe9, 0x99, 0x12, 0x42, 0x04, 0xa5, 0x85, 0xa0, 0x13,
+0xc2, 0x04, 0xe0, 0x00, 0x90, 0x13, 0xa0, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0xc5, 0x04,
+0xa7, 0x2c, 0x87, 0x2c, 0xa0, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x42, 0x00, 0xe3,
+0x93, 0xfd, 0x17, 0x41, 0x00, 0xa8, 0x88, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf,
+0x8f, 0x1a, 0x42, 0x04, 0xed, 0x9d, 0xa0, 0x1b, 0x42, 0x04, 0xeb, 0x9b, 0xa0, 0x1e, 0x41, 0x00,
+0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4,
+0x94, 0x22, 0x41, 0x00, 0xa3, 0x83, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24, 0x41, 0x00, 0xa6, 0x86,
+0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x27, 0x41, 0x00, 0xe8, 0x98, 0x28,
+0x42, 0x04, 0xe6, 0x96, 0xa0, 0x29, 0x43, 0x00, 0xea, 0x9a, 0x5d, 0x5b, 0x2a, 0x05, 0x3f, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x80, 0x2b, 0x41, 0x00, 0xef, 0x9f, 0x2c, 0xc5, 0x00, 0xa9, 0x15, 0x89,
+0x15, 0x00, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x41, 0x00, 0xfb, 0xfa, 0x2e, 0x41,
+0x00, 0xe7, 0x97, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00,
+0xad, 0x8d, 0x32, 0x42, 0x04, 0xac, 0x8c, 0xa0, 0x39, 0x00, 0x00, 0x20, 0x00, 0x22, 0x09, 0xa0,
+0xf3, 0xa5, 0xf1, 0xae, 0xf5, 0xe3, 0xf7, 0x80, 0xf2, 0x85, 0xf0, 0x8e, 0xf4, 0x93, 0xf6, 0x20,
+0x22, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x01,
+0x03, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x13, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x15, 0xc5, 0x04, 0x7a, 0x2c, 0x5a, 0x2c, 0xa0, 0x2c, 0x00, 0x2c, 0x1a, 0x2c,
+0x00, 0x2c, 0x16, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x1a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x1b, 0x10,
+0x00, 0x00, 0x28, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2b, 0x10, 0x00, 0x00, 0x32, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x36, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0xb7, 0xbf, 0x07, 0x04,
+0xbe, 0x01, 0x52, 0x4f, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x54, 0x03, 0xd7, 0x01, 0x4d, 0x02, 0x00, 0x00, 0x5a, 0x03, 0x49, 0x01, 0x50, 0x01, 0x00, 0x00,
+0x52, 0x03, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x50, 0x03, 0xfb, 0x03, 0x00, 0x00, 0x00, 0x00,
+0x50, 0x03, 0x0e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x65, 0x04, 0x55, 0x05, 0x00, 0x00, 0x00, 0x00,
+0x65, 0x04, 0x69, 0x04, 0x00, 0x00, 0x00, 0x00, 0x39, 0x75, 0xbd, 0x05, 0x00, 0x00, 0x00, 0x00,
+0x3a, 0x75, 0x4c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x75, 0x45, 0x06, 0x37, 0x07, 0x00, 0x00,
+0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x02, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x04, 0x0f, 0x00, 0x00,
+0xc9, 0x00, 0x1e, 0x06, 0x02, 0x07, 0x00, 0x00, 0xca, 0x07, 0x01, 0x01, 0x00, 0x26, 0x08, 0x02,
+0x05, 0x00, 0x2f, 0xcb, 0x09, 0x02, 0x01, 0x00, 0x28, 0xfa, 0x0a, 0x02, 0x05, 0x00, 0x29, 0xcc,
+0x0b, 0x01, 0x01, 0x00, 0x3d, 0x0c, 0x02, 0x04, 0x2b, 0x3f, 0xcd, 0x0d, 0x02, 0x04, 0x27, 0x2a,
+0xce, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55,
+0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x42, 0x03, 0xa0, 0xa0, 0x7b,
+0x1b, 0x42, 0x00, 0x8c, 0xd7, 0x7d, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x27, 0x44, 0x0b, 0xa0, 0xa0,
+0x5b, 0x00, 0x1b, 0x28, 0x44, 0x0b, 0xa0, 0xa0, 0x5d, 0x00, 0x1d, 0x29, 0x42, 0x00, 0x83, 0xb6,
+0x2d, 0x2b, 0x02, 0x03, 0x00, 0x00, 0xe1, 0x2c, 0x42, 0x03, 0x00, 0x00, 0x9e, 0x2d, 0x42, 0x03,
+0x00, 0x00, 0xf6, 0x2e, 0x42, 0x00, 0x63, 0x43, 0xf5, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x33, 0x02,
+0x01, 0x00, 0x3b, 0x3c, 0x34, 0x02, 0x01, 0x00, 0x3a, 0x3e, 0x35, 0x04, 0x08, 0x2d, 0x5f, 0x40,
+0x00, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56, 0x02, 0x00, 0x9e, 0xf6, 0xf5, 0x00, 0x12, 0x42, 0x03,
+0x00, 0x00, 0xd5, 0x00, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5,
+0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96,
+0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xf8, 0x03, 0x61, 0x86,
+0x41, 0x8f, 0x20, 0xf8, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97,
+0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0xef, 0x0d, 0x61, 0xa0,
+0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6,
+0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b,
+0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a,
+0x20, 0xf9, 0xf7, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0xf7, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00,
+0x7e, 0x03, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x05, 0x02, 0x07, 0x00, 0x00, 0xca, 0x06, 0x02, 0x07,
+0x00, 0x00, 0xcb, 0x07, 0x02, 0x07, 0x00, 0x00, 0xcc, 0x08, 0x02, 0x03, 0x00, 0x00, 0x60, 0x09,
+0x02, 0x07, 0x00, 0x00, 0xcd, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xce, 0x0b, 0x02, 0x07, 0x00, 0x00,
+0xcf, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xd0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xd1, 0x12, 0x42, 0x03,
+0x00, 0x00, 0xaa, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41, 0x00, 0x74, 0x54, 0x1a, 0x41, 0x00,
+0xc7, 0xc6, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x20, 0x41, 0x00, 0x64, 0x44, 0x26, 0x41, 0x00, 0x6c,
+0x4c, 0x27, 0x41, 0x00, 0xad, 0xb8, 0x28, 0x41, 0x00, 0xee, 0xdd, 0x2c, 0x41, 0x00, 0x7a, 0x5a,
+0x00, 0xf3, 0x13, 0x63, 0x9f, 0x64, 0xd4, 0x65, 0xd8, 0x6c, 0x96, 0x6e, 0xe5, 0x72, 0xfd, 0x73,
+0xe7, 0x74, 0x9c, 0x7a, 0xa7, 0x43, 0xac, 0x44, 0xd2, 0x45, 0xb7, 0x4c, 0x95, 0x4e, 0xd5, 0x52,
+0xfc, 0x53, 0xe6, 0x54, 0x9b, 0x5a, 0xa6, 0x20, 0xf3, 0x5e, 0x07, 0x61, 0x83, 0x69, 0x8c, 0x6f,
+0x93, 0x41, 0xb6, 0x49, 0xd7, 0x4f, 0xe2, 0x20, 0x5e, 0xf4, 0x03, 0x61, 0xc7, 0x41, 0xc6, 0x20,
+0xf4, 0xf8, 0x03, 0x75, 0x85, 0x55, 0xde, 0x20, 0xf8, 0xf2, 0x05, 0x61, 0xa5, 0x65, 0xa9, 0x41,
+0xa4, 0x45, 0xa8, 0x20, 0xf2, 0xfa, 0x03, 0x7a, 0xbe, 0x5a, 0xbd, 0x20, 0xfa, 0xef, 0x19, 0x61,
+0xa0, 0x63, 0x86, 0x65, 0x82, 0x69, 0xa1, 0x6c, 0x92, 0x6e, 0xe4, 0x6f, 0xa2, 0x72, 0xea, 0x73,
+0x98, 0x75, 0xa3, 0x79, 0xec, 0x7a, 0xab, 0x41, 0xb5, 0x43, 0x8f, 0x45, 0x90, 0x49, 0xd6, 0x4c,
+0x91, 0x4e, 0xe3, 0x4f, 0xe0, 0x52, 0xe8, 0x53, 0x97, 0x55, 0xe9, 0x59, 0xed, 0x5a, 0x8d, 0x20,
+0xef, 0xf1, 0x05, 0x6f, 0x8b, 0x75, 0xfb, 0x4f, 0x8a, 0x55, 0xeb, 0x20, 0xf1, 0xf9, 0x09, 0x61,
+0x84, 0x65, 0x89, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x45, 0xd3, 0x4f, 0x99, 0x55, 0x9a, 0x20,
+0xf9, 0xf7, 0x07, 0x63, 0x87, 0x73, 0xad, 0x74, 0xee, 0x43, 0x80, 0x53, 0xb8, 0x54, 0xdd, 0x20,
+0xf7, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x43, 0x00, 0xec,
+0x9c, 0x27, 0x2a, 0x10, 0x41, 0x00, 0xee, 0x9e, 0x11, 0x41, 0x00, 0xe9, 0x99, 0x12, 0x42, 0x00,
+0xa5, 0x85, 0xfd, 0x13, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15,
+0x41, 0x00, 0xa9, 0x89, 0x16, 0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xf7, 0xf6, 0x18, 0x41,
+0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x41, 0x00, 0xf9, 0xf8, 0x1b, 0x41, 0x00,
+0xa8, 0x88, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4,
+0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00, 0xf3, 0xf2, 0x23, 0x41, 0x00, 0xa3, 0x83,
+0x24, 0x41, 0x00, 0xa6, 0x86, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x27,
+0x41, 0x00, 0xe8, 0x98, 0x28, 0x41, 0x00, 0xe6, 0x96, 0x29, 0x41, 0x00, 0xef, 0x9f, 0x2a, 0x05,
+0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x2b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2c, 0x42, 0x04,
+0xa7, 0x87, 0xa0, 0x2d, 0x42, 0x04, 0xe5, 0x95, 0xa0, 0x2e, 0x42, 0x00, 0xe7, 0x97, 0x15, 0x2f,
+0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x42,
+0x04, 0xac, 0x8c, 0xa0, 0x39, 0x00, 0x00, 0x20, 0x56, 0x42, 0x00, 0xf5, 0xf4, 0x15, 0x00, 0x02,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12,
+0x02, 0x03, 0x00, 0x00, 0xfd, 0x1b, 0x01, 0x03, 0xa0, 0xa0, 0x29, 0x01, 0x03, 0xa0, 0xa0, 0x2b,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x2c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2d, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x2e, 0x02, 0x03, 0x00, 0x00, 0x15, 0x32, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x36, 0x05, 0x3f,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x56, 0x02, 0x03, 0xa0, 0xa0, 0x15, 0x00, 0x02, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x08,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x43, 0x00, 0xec, 0x9c, 0x27, 0x2a, 0x10, 0x41,
+0x00, 0xee, 0x9e, 0x11, 0x41, 0x00, 0xe9, 0x99, 0x12, 0x41, 0x00, 0xa5, 0x85, 0x13, 0xc1, 0x00,
+0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xa9, 0x89, 0x16, 0x41,
+0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xf7, 0xf6, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00,
+0xaf, 0x8f, 0x1a, 0x41, 0x00, 0xf9, 0xf8, 0x1b, 0x41, 0x00, 0xa8, 0x88, 0x1e, 0x41, 0x00, 0xa0,
+0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94,
+0x22, 0x41, 0x00, 0xf3, 0xf2, 0x23, 0x41, 0x00, 0xa3, 0x83, 0x24, 0x41, 0x00, 0xa6, 0x86, 0x25,
+0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x27, 0x41, 0x00, 0xe8, 0x98, 0x28, 0x41,
+0x00, 0xe6, 0x96, 0x29, 0x41, 0x00, 0xef, 0x9f, 0x2a, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x7d, 0x2b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2c, 0x42, 0x04, 0xa7, 0x87, 0xa0, 0x2d, 0x42, 0x04,
+0xe5, 0x95, 0xa0, 0x2e, 0x42, 0x00, 0xe7, 0x97, 0x15, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41,
+0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x42, 0x04, 0xac, 0x8c, 0xa0, 0x39, 0x00,
+0x00, 0x20, 0x56, 0x42, 0x00, 0xf5, 0xf4, 0x15, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x08, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x1b, 0x01, 0x03, 0xa0, 0xa0, 0x29, 0x01,
+0x03, 0xa0, 0xa0, 0x2b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2d,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x2e, 0x02, 0x03, 0x00, 0x00, 0x15, 0x32, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x36, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x56, 0x02, 0x03, 0xa0, 0xa0, 0x15,
+0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x02, 0x03,
+0x00, 0x00, 0xd5, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x03, 0x03, 0x00, 0x00, 0x88, 0xd2, 0x16, 0x43,
+0x03, 0x00, 0x00, 0x81, 0x9a, 0x17, 0x41, 0x01, 0x00, 0x98, 0x18, 0x43, 0x03, 0x00, 0x00, 0x94,
+0x99, 0x1a, 0x41, 0x00, 0x84, 0x8e, 0x1b, 0x41, 0x00, 0x8d, 0x49, 0x22, 0x43, 0x03, 0x00, 0x00,
+0xa7, 0xa6, 0x27, 0x41, 0x00, 0x9f, 0x9e, 0x28, 0x41, 0x00, 0xf3, 0xf4, 0x29, 0x01, 0x03, 0xa0,
+0xa0, 0x2b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2d, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x56, 0x41, 0x00, 0x87, 0x80, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x03,
+0x01, 0x03, 0x00, 0xc8, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x43, 0x00, 0xec, 0x9c, 0x27, 0x2a, 0x10,
+0x41, 0x00, 0xee, 0x9e, 0x11, 0x41, 0x00, 0xe9, 0x99, 0x12, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x13,
+0xc1, 0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xa9, 0x89,
+0x16, 0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xa8, 0x88, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19,
+0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x41, 0x00, 0xed, 0x9d, 0x1b, 0x41, 0x00, 0xeb, 0x9b, 0x1e, 0x41,
+0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00,
+0xe4, 0x94, 0x22, 0x41, 0x00, 0xa3, 0x83, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24, 0x41, 0x00, 0xa6,
+0x86, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x27, 0x41, 0x00, 0xe8, 0x98,
+0x28, 0x41, 0x00, 0xe6, 0x96, 0x29, 0x41, 0x00, 0xef, 0x9f, 0x2a, 0x05, 0x3f, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x80, 0x2b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2c, 0x42, 0x04, 0xa7, 0x87, 0xa0, 0x2d,
+0x42, 0x04, 0xfb, 0xfa, 0xa0, 0x2e, 0x42, 0x00, 0xe7, 0x97, 0x15, 0x2f, 0x41, 0x00, 0xa2, 0x82,
+0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x42, 0x04, 0xac, 0x8c, 0xa0,
+0x39, 0x00, 0x00, 0x20, 0x56, 0x42, 0x00, 0xea, 0x9a, 0x15, 0x00, 0x22, 0x09, 0xa0, 0xf3, 0xa5,
+0xf1, 0xae, 0xf5, 0xe3, 0xf7, 0x80, 0xf2, 0x85, 0xf0, 0x8e, 0xf4, 0x93, 0xf6, 0x20, 0x22, 0x00,
+0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x08, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x12, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x1b, 0x01, 0x03, 0xa0, 0xa0, 0x29, 0x01, 0x03, 0xa0, 0xa0,
+0x2b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2c, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2d, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x2e, 0x02, 0x03, 0x00, 0x00, 0x15, 0x32, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x36, 0x05,
+0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x56, 0x02, 0x03, 0xa0, 0xa0, 0x15, 0x00, 0xd5, 0x6e,
+0x04, 0x09, 0x00, 0x00, 0x52, 0x55, 0x2c, 0xb9, 0x01, 0x52, 0x55, 0x0c, 0x02, 0x00, 0x00, 0x00,
+0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x03, 0xab, 0x01, 0x00, 0x00, 0x00, 0x00, 0x28,
+0x03, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x03, 0x94, 0x02, 0x00, 0x00, 0x00, 0x00, 0x62,
+0x03, 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x03, 0x76, 0x03, 0x00, 0x00, 0x00, 0x00, 0x68,
+0x03, 0xa2, 0x02, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x5c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x57,
+0x03, 0x8a, 0x03, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52,
+0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x01, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+0x02, 0x03, 0x00, 0x00, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00, 0x23, 0x05, 0x02, 0x03, 0x00, 0x00,
+0xcf, 0x07, 0x02, 0x03, 0x00, 0x00, 0x5e, 0x08, 0x02, 0x03, 0x00, 0x00, 0x26, 0x09, 0x02, 0x03,
+0x00, 0x00, 0x24, 0x1a, 0x02, 0x03, 0x00, 0x00, 0x5b, 0x1b, 0x02, 0x03, 0x00, 0x00, 0x5d, 0x2b,
+0x02, 0x03, 0x00, 0x00, 0x7c, 0x33, 0x02, 0x03, 0x00, 0x00, 0x3c, 0x34, 0x02, 0x03, 0x00, 0x00,
+0x3e, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x00,
+0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xfc, 0x05, 0x02, 0x05, 0x00, 0x3b, 0xa0,
+0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11,
+0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42,
+0x00, 0xa5, 0x85, 0xfd, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41,
+0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00,
+0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb,
+0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f,
+0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab,
+0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d,
+0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x01,
+0x01, 0x00, 0x2f, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00,
+0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2,
+0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e,
+0x35, 0x01, 0x00, 0x2e, 0x2c, 0x00, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x42, 0x03, 0x00,
+0x00, 0xfd, 0x36, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x79, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04,
+0x01, 0x01, 0x00, 0xfc, 0x05, 0x02, 0x01, 0x00, 0x3b, 0xfd, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08,
+0x01, 0x01, 0x00, 0x3f, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41,
+0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x41, 0x00, 0xa5, 0x85, 0x15, 0x41, 0x00,
+0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9,
+0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a,
+0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21,
+0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23,
+0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27,
+0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00,
+0x2a, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0x41, 0x00, 0xef,
+0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c,
+0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33,
+0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x00, 0x05,
+0x02, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x03, 0x01, 0x01,
+0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xef, 0x05, 0x02, 0x05, 0x00, 0x3b, 0xa0, 0x07, 0x01, 0x01,
+0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41, 0x00, 0xbd, 0xbe, 0x11, 0x41, 0x00, 0xa4,
+0xa5, 0x12, 0x41, 0x00, 0xe7, 0xe8, 0x13, 0x41, 0x00, 0xc6, 0xc7, 0x14, 0x42, 0x00, 0xa8, 0xa9,
+0xcf, 0x15, 0x41, 0x00, 0xd4, 0xd5, 0x16, 0x41, 0x00, 0xac, 0xad, 0x17, 0x41, 0x00, 0xf5, 0xf6,
+0x18, 0x41, 0x00, 0xf9, 0xfa, 0x19, 0x41, 0x00, 0xf3, 0xf4, 0x1a, 0x41, 0x00, 0xb5, 0xb6, 0x1b,
+0x41, 0x00, 0x9e, 0x9f, 0x1e, 0x41, 0x00, 0xaa, 0xab, 0x1f, 0x41, 0x00, 0xf1, 0xf2, 0x20, 0x41,
+0x00, 0xeb, 0xec, 0x21, 0x41, 0x00, 0xa0, 0xa1, 0x22, 0x41, 0x00, 0xd8, 0xdd, 0x23, 0x41, 0x00,
+0xe1, 0xe2, 0x24, 0x41, 0x00, 0xd6, 0xd7, 0x25, 0x41, 0x00, 0xd0, 0xd1, 0x26, 0x41, 0x00, 0xa6,
+0xa7, 0x27, 0x41, 0x00, 0xe9, 0xea, 0x28, 0x41, 0x00, 0xf7, 0xf8, 0x29, 0x41, 0x00, 0x84, 0x85,
+0x2a, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x7c, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0xc1, 0x00, 0xde,
+0x2c, 0xe0, 0x00, 0x2d, 0x41, 0x00, 0xfb, 0xfc, 0x2e, 0x41, 0x00, 0xe3, 0xe4, 0x2f, 0x41, 0x00,
+0xd2, 0xd3, 0x30, 0x41, 0x00, 0xb7, 0xb8, 0x31, 0x41, 0x00, 0xe5, 0xe6, 0x32, 0x41, 0x00, 0xed,
+0xee, 0x33, 0x41, 0x00, 0xa2, 0xa3, 0x34, 0x41, 0x00, 0x9c, 0x9d, 0x35, 0x01, 0x00, 0x2e, 0x2c,
+0x00, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x42, 0x03, 0x00, 0x00, 0xcf, 0x36, 0x03, 0x0f,
+0x00, 0x00, 0x00, 0x7d, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xef, 0x05,
+0x01, 0x01, 0x00, 0x3b, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41,
+0x00, 0xbd, 0xbe, 0x11, 0x41, 0x00, 0xa4, 0xa5, 0x12, 0x41, 0x00, 0xe7, 0xe8, 0x13, 0x41, 0x00,
+0xc6, 0xc7, 0x14, 0x41, 0x00, 0xa8, 0xa9, 0x15, 0x41, 0x00, 0xd4, 0xd5, 0x16, 0x41, 0x00, 0xac,
+0xad, 0x17, 0x41, 0x00, 0xf5, 0xf6, 0x18, 0x41, 0x00, 0xf9, 0xfa, 0x19, 0x41, 0x00, 0xf3, 0xf4,
+0x1a, 0x41, 0x00, 0xb5, 0xb6, 0x1b, 0x41, 0x00, 0x9e, 0x9f, 0x1e, 0x41, 0x00, 0xaa, 0xab, 0x1f,
+0x41, 0x00, 0xf1, 0xf2, 0x20, 0x41, 0x00, 0xeb, 0xec, 0x21, 0x41, 0x00, 0xa0, 0xa1, 0x22, 0x41,
+0x00, 0xd8, 0xdd, 0x23, 0x41, 0x00, 0xe1, 0xe2, 0x24, 0x41, 0x00, 0xd6, 0xd7, 0x25, 0x41, 0x00,
+0xd0, 0xd1, 0x26, 0x41, 0x00, 0xa6, 0xa7, 0x27, 0x41, 0x00, 0xe9, 0xea, 0x28, 0x41, 0x00, 0xf7,
+0xf8, 0x29, 0x41, 0x00, 0x84, 0x85, 0x2a, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x7e, 0x2b, 0x01, 0x01,
+0x00, 0x2f, 0x2c, 0xc1, 0x00, 0xde, 0x2c, 0xe0, 0x00, 0x2d, 0x41, 0x00, 0xfb, 0xfc, 0x2e, 0x41,
+0x00, 0xe3, 0xe4, 0x2f, 0x41, 0x00, 0xd2, 0xd3, 0x30, 0x41, 0x00, 0xb7, 0xb8, 0x31, 0x41, 0x00,
+0xe5, 0xe6, 0x32, 0x41, 0x00, 0xed, 0xee, 0x33, 0x41, 0x00, 0xa2, 0xa3, 0x34, 0x41, 0x00, 0x9c,
+0x9d, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x00, 0x36, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x32,
+0xeb, 0x04, 0x04, 0xbb, 0x01, 0x52, 0x55, 0x0c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x28, 0x03, 0xd1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x28, 0x03, 0xd8, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x62, 0x03, 0xd8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x62, 0x03, 0xe0, 0x01, 0x00,
+0x00, 0x00, 0x00, 0x68, 0x03, 0xd8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x68, 0x03, 0xe1, 0x02, 0x00,
+0x00, 0x00, 0x00, 0x57, 0x03, 0xdd, 0x04, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0xe7, 0x03, 0x00,
+0x00, 0x00, 0x00, 0x5a, 0x03, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xb5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x03, 0x02, 0x03, 0x00, 0x00, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00, 0x23, 0x05,
+0x02, 0x03, 0x00, 0x00, 0x24, 0x07, 0x02, 0x03, 0x00, 0x00, 0x5e, 0x08, 0x02, 0x03, 0x00, 0x00,
+0x26, 0x09, 0x03, 0x03, 0x00, 0x00, 0x2a, 0x1f, 0x0d, 0x03, 0x07, 0x00, 0x00, 0x00, 0x1c, 0x1a,
+0x02, 0x03, 0x00, 0x00, 0x5b, 0x1b, 0x02, 0x03, 0x00, 0x00, 0x5d, 0x33, 0x02, 0x03, 0x00, 0x00,
+0x3c, 0x34, 0x02, 0x03, 0x00, 0x00, 0x3e, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x02,
+0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x01,
+0x00, 0x22, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00,
+0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25,
+0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9, 0x89,
+0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14,
+0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17,
+0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41,
+0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00,
+0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf,
+0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00,
+0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xed,
+0x9d, 0x29, 0x01, 0x00, 0x7c, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x01,
+0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00,
+0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2,
+0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e,
+0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00, 0x2d,
+0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x01, 0x00, 0x22, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35,
+0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a,
+0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01,
+0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00,
+0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x41, 0x00, 0xa5, 0x85, 0x15, 0x41, 0x00, 0xad,
+0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99,
+0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e,
+0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41,
+0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24,
+0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41,
+0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0x01, 0x00, 0x7c, 0x2b, 0x2a, 0x04, 0x1f,
+0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d,
+0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41,
+0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00,
+0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0x00, 0x36,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x02, 0x01, 0x00, 0xef, 0x31, 0x03, 0x01, 0x00,
+0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x01, 0x00, 0x22, 0x34, 0x06, 0x01, 0x00, 0x3a,
+0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38,
+0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d,
+0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xbd, 0xbe, 0x11, 0x41, 0x00, 0xa4, 0xa5, 0x12, 0x41,
+0x00, 0xe7, 0xe8, 0x13, 0x41, 0x00, 0xc6, 0xc7, 0x14, 0x42, 0x00, 0xa8, 0xa9, 0xcf, 0x15, 0x41,
+0x00, 0xd4, 0xd5, 0x16, 0x41, 0x00, 0xac, 0xad, 0x17, 0x41, 0x00, 0xf5, 0xf6, 0x18, 0x41, 0x00,
+0xf9, 0xfa, 0x19, 0x41, 0x00, 0xf3, 0xf4, 0x1a, 0x41, 0x00, 0xb5, 0xb6, 0x1b, 0x41, 0x00, 0x9e,
+0x9f, 0x1e, 0x41, 0x00, 0xaa, 0xab, 0x1f, 0x41, 0x00, 0xf1, 0xf2, 0x20, 0x41, 0x00, 0xeb, 0xec,
+0x21, 0x41, 0x00, 0xa0, 0xa1, 0x22, 0x41, 0x00, 0xd8, 0xdd, 0x23, 0x41, 0x00, 0xe1, 0xe2, 0x24,
+0x41, 0x00, 0xd6, 0xd7, 0x25, 0x41, 0x00, 0xd0, 0xd1, 0x26, 0x41, 0x00, 0xa6, 0xa7, 0x27, 0x41,
+0x00, 0xe9, 0xea, 0x28, 0x41, 0x00, 0xf7, 0xf8, 0x29, 0x01, 0x00, 0x7c, 0x2b, 0x2a, 0x04, 0x1f,
+0x00, 0x00, 0x00, 0x00, 0x7c, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0xc1, 0x00, 0xde, 0x2c, 0xe0,
+0x00, 0x2d, 0x41, 0x00, 0xfb, 0xfc, 0x2e, 0x41, 0x00, 0xe3, 0xe4, 0x2f, 0x41, 0x00, 0xd2, 0xd3,
+0x30, 0x41, 0x00, 0xb7, 0xb8, 0x31, 0x41, 0x00, 0xe5, 0xe6, 0x32, 0x41, 0x00, 0xed, 0xee, 0x33,
+0x41, 0x00, 0xa2, 0xa3, 0x34, 0x41, 0x00, 0x9c, 0x9d, 0x35, 0x41, 0x00, 0x84, 0x85, 0x00, 0x12,
+0x42, 0x03, 0x00, 0x00, 0xcf, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x02, 0x01,
+0x00, 0xef, 0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x01, 0x00,
+0x22, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e,
+0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25, 0x30,
+0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xbd, 0xbe, 0x11,
+0x41, 0x00, 0xa4, 0xa5, 0x12, 0x41, 0x00, 0xe7, 0xe8, 0x13, 0x41, 0x00, 0xc6, 0xc7, 0x14, 0x41,
+0x00, 0xa8, 0xa9, 0x15, 0x41, 0x00, 0xd4, 0xd5, 0x16, 0x41, 0x00, 0xac, 0xad, 0x17, 0x41, 0x00,
+0xf5, 0xf6, 0x18, 0x41, 0x00, 0xf9, 0xfa, 0x19, 0x41, 0x00, 0xf3, 0xf4, 0x1a, 0x41, 0x00, 0xb5,
+0xb6, 0x1b, 0x41, 0x00, 0x9e, 0x9f, 0x1e, 0x41, 0x00, 0xaa, 0xab, 0x1f, 0x41, 0x00, 0xf1, 0xf2,
+0x20, 0x41, 0x00, 0xeb, 0xec, 0x21, 0x41, 0x00, 0xa0, 0xa1, 0x22, 0x41, 0x00, 0xd8, 0xdd, 0x23,
+0x41, 0x00, 0xe1, 0xe2, 0x24, 0x41, 0x00, 0xd6, 0xd7, 0x25, 0x41, 0x00, 0xd0, 0xd1, 0x26, 0x41,
+0x00, 0xa6, 0xa7, 0x27, 0x41, 0x00, 0xe9, 0xea, 0x28, 0x41, 0x00, 0xf7, 0xf8, 0x29, 0x01, 0x00,
+0x7c, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c,
+0xc1, 0x00, 0xde, 0x2c, 0xe0, 0x00, 0x2d, 0x41, 0x00, 0xfb, 0xfc, 0x2e, 0x41, 0x00, 0xe3, 0xe4,
+0x2f, 0x41, 0x00, 0xd2, 0xd3, 0x30, 0x41, 0x00, 0xb7, 0xb8, 0x31, 0x41, 0x00, 0xe5, 0xe6, 0x32,
+0x41, 0x00, 0xed, 0xee, 0x33, 0x41, 0x00, 0xa2, 0xa3, 0x34, 0x41, 0x00, 0x9c, 0x9d, 0x35, 0x41,
+0x00, 0x84, 0x85, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xbb, 0xfe, 0x07,
+0x09, 0x00, 0x00, 0x52, 0x58, 0x2c, 0xb9, 0x01, 0x52, 0x58, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x00,
+0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x75, 0xd8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x75,
+0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x75, 0xdb, 0x02, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x75,
+0xe7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x75, 0xda, 0x03, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x75,
+0xea, 0x02, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x75, 0xd1, 0x04, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x75,
+0xe9, 0x03, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x75, 0xca, 0x05, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x75,
+0xe0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x40, 0x75, 0xc7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x40, 0x75,
+0xd9, 0x05, 0x00, 0x00, 0x00, 0x00, 0x41, 0x75, 0xb4, 0x07, 0x00, 0x00, 0x00, 0x00, 0x41, 0x75,
+0xd6, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02,
+0x03, 0x00, 0x00, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00, 0x23, 0x07, 0x02, 0x03, 0x00, 0x00, 0x5e,
+0x08, 0x02, 0x03, 0x00, 0x00, 0x26, 0x09, 0x02, 0x03, 0x00, 0x00, 0x24, 0x0c, 0x03, 0x03, 0x00,
+0x00, 0x5b, 0x7b, 0x1a, 0x03, 0x03, 0x00, 0x00, 0x5b, 0x7b, 0x1b, 0x03, 0x03, 0x00, 0x00, 0x5d,
+0x7d, 0x2b, 0x02, 0x03, 0x00, 0x00, 0x7c, 0x33, 0x02, 0x03, 0x00, 0x00, 0x3c, 0x34, 0x02, 0x03,
+0x00, 0x00, 0x3e, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xfc, 0x05, 0x01,
+0x01, 0x00, 0x3b, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x0d, 0x02, 0x02,
+0xfe, 0x00, 0x3d, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x43, 0x00, 0xe6, 0x96, 0xf7, 0xf6, 0x12,
+0x43, 0x00, 0xe3, 0x93, 0xb8, 0xb5, 0x13, 0x43, 0x00, 0xaa, 0x8a, 0xd1, 0xcf, 0x14, 0x42, 0x00,
+0xa5, 0x85, 0xfd, 0x15, 0x43, 0x00, 0xad, 0x8d, 0xfb, 0xfa, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17,
+0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x43,
+0x00, 0xe5, 0x95, 0xc6, 0xbe, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f,
+0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x43, 0x00, 0xa0, 0x80, 0xd5, 0xd4,
+0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x43, 0x00, 0xae,
+0x8e, 0xf5, 0xf4, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x43, 0x00,
+0xa6, 0x86, 0xf9, 0xf8, 0x28, 0x43, 0x00, 0xed, 0x9d, 0xf3, 0xf2, 0x29, 0xc1, 0x00, 0xf1, 0x29,
+0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c,
+0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41,
+0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00,
+0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01, 0x00, 0x2e,
+0x2c, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79,
+0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xfc, 0x05, 0x02, 0x01, 0x00, 0x3b,
+0xfd, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x43, 0x00, 0xa9, 0x89,
+0xd2, 0xd1, 0x11, 0x43, 0x00, 0xe6, 0x96, 0xb6, 0xb5, 0x12, 0x43, 0x00, 0xe3, 0x93, 0xf7, 0xf6,
+0x13, 0x43, 0x00, 0xaa, 0x8a, 0xd6, 0xd5, 0x14, 0x43, 0x00, 0xa5, 0x85, 0xfb, 0xfa, 0x15, 0x43,
+0x00, 0xad, 0x8d, 0xd4, 0xd3, 0x16, 0x43, 0x00, 0xa3, 0x83, 0xd8, 0xd7, 0x17, 0x43, 0x00, 0xe8,
+0x98, 0xb8, 0xb7, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x43, 0x00,
+0xe5, 0x95, 0xbe, 0xbd, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41,
+0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00,
+0xaf, 0x8f, 0x23, 0xc3, 0x00, 0xe0, 0x00, 0x90, 0x23, 0xf9, 0x23, 0xf8, 0x23, 0x24, 0x43, 0x00,
+0xae, 0x8e, 0xf3, 0xf2, 0x25, 0x43, 0x00, 0xab, 0x8b, 0xf5, 0xf4, 0x26, 0x41, 0x00, 0xa4, 0x84,
+0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0,
+0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0x41,
+0x00, 0xef, 0x9f, 0x2d, 0x43, 0x00, 0xe7, 0x97, 0xc7, 0xc6, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f,
+0x41, 0x00, 0xac, 0x8c, 0x30, 0x43, 0x00, 0xa8, 0x88, 0xd0, 0xcf, 0x31, 0x41, 0x00, 0xe2, 0x92,
+0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35,
+0x01, 0x00, 0x2e, 0x2c, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00,
+0x00, 0x00, 0x7b, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xfc, 0x05, 0x02,
+0x01, 0x00, 0x3b, 0xfd, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41,
+0x00, 0xa9, 0x89, 0x11, 0x43, 0x00, 0xe6, 0x96, 0xd4, 0xd3, 0x12, 0x43, 0x00, 0xe3, 0x93, 0xb8,
+0xb5, 0x13, 0x43, 0x00, 0xaa, 0x8a, 0xc7, 0xbd, 0x14, 0x43, 0x00, 0xa5, 0x85, 0xf7, 0xf6, 0x15,
+0x43, 0x00, 0xad, 0x8d, 0xfb, 0xfa, 0x16, 0x43, 0x00, 0xa3, 0x83, 0xb7, 0xb6, 0x17, 0x41, 0x00,
+0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x43, 0x00, 0xa7, 0x87, 0xd6, 0xd5, 0x1a, 0x43,
+0x00, 0xe5, 0x95, 0xc6, 0xbe, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f,
+0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x43, 0x00, 0xa0, 0x80, 0xf3, 0xf2,
+0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x43, 0x00, 0xae,
+0x8e, 0xf5, 0xf4, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x43, 0x00,
+0xa6, 0x86, 0xd2, 0xd1, 0x28, 0x43, 0x00, 0xed, 0x9d, 0xd0, 0xcf, 0x29, 0xc1, 0x00, 0xf1, 0x29,
+0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c,
+0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x43, 0x00, 0xe1, 0x91, 0xd8, 0xd7,
+0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32,
+0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01,
+0x00, 0x2e, 0x2c, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x7d, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xfc, 0x05, 0x01, 0x01,
+0x00, 0x3b, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41, 0x00, 0xa9,
+0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x43, 0x00, 0xe3, 0x93, 0xf7, 0xf6, 0x13, 0x41, 0x00,
+0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15, 0x43, 0x00, 0xad, 0x8d, 0xd5, 0xd4, 0x16,
+0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x43,
+0x00, 0xa7, 0x87, 0xc6, 0xbe, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e,
+0x41, 0x00, 0xe4, 0x94, 0x1f, 0x43, 0x00, 0xeb, 0x9b, 0xfb, 0xfa, 0x20, 0x41, 0x00, 0xa2, 0x82,
+0x21, 0x43, 0x00, 0xa0, 0x80, 0xf3, 0xf2, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0,
+0x00, 0x90, 0x23, 0x24, 0x43, 0x00, 0xae, 0x8e, 0xf5, 0xf4, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26,
+0x41, 0x00, 0xa4, 0x84, 0x27, 0x43, 0x00, 0xa6, 0x86, 0xb8, 0xb5, 0x28, 0x41, 0x00, 0xed, 0x9d,
+0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x2b,
+0x01, 0x01, 0x00, 0x2f, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x43, 0x00, 0xe7, 0x97, 0xd1, 0xcf,
+0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x43, 0x00, 0xa8, 0x88, 0xf9,
+0xf8, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81,
+0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00,
+0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04,
+0x01, 0x01, 0x00, 0xfc, 0x05, 0x01, 0x01, 0x00, 0x3b, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01,
+0x01, 0x00, 0x3f, 0x10, 0x43, 0x00, 0xa9, 0x89, 0xde, 0xdd, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12,
+0x43, 0x00, 0xe3, 0x93, 0xf7, 0xf6, 0x13, 0x43, 0x00, 0xaa, 0x8a, 0xd5, 0xd4, 0x14, 0x42, 0x00,
+0xa5, 0x85, 0xfd, 0x15, 0x43, 0x00, 0xad, 0x8d, 0xd1, 0xcf, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17,
+0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41,
+0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00,
+0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x43, 0x00, 0xa0, 0x80, 0xf3, 0xf2, 0x22, 0x41,
+0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x43, 0x00, 0xae, 0x8e, 0xf5,
+0xf4, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x43, 0x00, 0xa6, 0x86,
+0xfb, 0xfa, 0x28, 0x43, 0x00, 0xed, 0x9d, 0xc6, 0xbe, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00,
+0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0x43, 0x00,
+0xef, 0x9f, 0xb8, 0xb5, 0x2d, 0x43, 0x00, 0xe7, 0x97, 0xf9, 0xf8, 0x2e, 0x41, 0x00, 0xe1, 0x91,
+0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32,
+0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01,
+0x00, 0x2e, 0x2c, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x81, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xfc, 0x05, 0x02, 0x01,
+0x00, 0x3b, 0xfd, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41, 0x00,
+0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x43, 0x00, 0xe3, 0x93, 0xf7, 0xf6, 0x13, 0x41,
+0x00, 0xaa, 0x8a, 0x14, 0x43, 0x00, 0xa5, 0x85, 0xdd, 0xdc, 0x15, 0x43, 0x00, 0xad, 0x8d, 0xdf,
+0xde, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99,
+0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e,
+0x41, 0x00, 0xe4, 0x94, 0x1f, 0x43, 0x00, 0xeb, 0x9b, 0xfb, 0xfa, 0x20, 0x41, 0x00, 0xa2, 0x82,
+0x21, 0x43, 0x00, 0xa0, 0x80, 0xf3, 0xf2, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0,
+0x00, 0x90, 0x23, 0x24, 0x43, 0x00, 0xae, 0x8e, 0xf5, 0xf4, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26,
+0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x43, 0x00, 0xed, 0x9d, 0xc6, 0xbe,
+0x29, 0xc3, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0xb8, 0x29, 0xb5, 0x29, 0x2a, 0x04, 0x1f, 0x00, 0x00,
+0x00, 0x00, 0x82, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0x43, 0x00, 0xef, 0x9f, 0xd5, 0xd4, 0x2d,
+0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x43,
+0x00, 0xa8, 0x88, 0xf9, 0xf8, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33,
+0x41, 0x00, 0xa1, 0x81, 0x34, 0x43, 0x00, 0xee, 0x9e, 0xd1, 0xcf, 0x35, 0x01, 0x00, 0x2e, 0x2c,
+0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00,
+0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xfc, 0x05, 0x01, 0x01, 0x00, 0x3b, 0x07,
+0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41,
+0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00,
+0xa5, 0x85, 0xfd, 0x15, 0x43, 0x00, 0xad, 0x8d, 0xb8, 0xb5, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17,
+0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41,
+0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00,
+0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf,
+0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x43, 0x00, 0xae, 0x8e, 0xf5, 0xf4, 0x25,
+0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x43,
+0x00, 0xed, 0x9d, 0xc6, 0xbe, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00,
+0x00, 0x00, 0x00, 0x84, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41,
+0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x43, 0x00,
+0xa8, 0x88, 0xd1, 0xcf, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41,
+0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x00, 0x12, 0x42,
+0x03, 0x00, 0x00, 0xfd, 0x16, 0x43, 0x03, 0x00, 0x00, 0xf7, 0xf6, 0x18, 0x43, 0x03, 0x00, 0x00,
+0xf5, 0xf4, 0x1e, 0x43, 0x03, 0x00, 0x00, 0xf3, 0xf2, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xfb, 0xfa,
+0x2c, 0x43, 0x03, 0x00, 0x00, 0xd5, 0xd4, 0x2d, 0x43, 0x03, 0x00, 0x00, 0xde, 0xdd, 0x2e, 0x43,
+0x03, 0x00, 0x00, 0xf9, 0xf8, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x92, 0xe0,
+0x08, 0x04, 0xbb, 0x01, 0x52, 0x58, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3b, 0x75, 0xed, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x75, 0xe1, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3c, 0x75, 0x13, 0x03, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x75, 0xfc, 0x01, 0x00, 0x00,
+0x00, 0x00, 0x3d, 0x75, 0x35, 0x04, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x75, 0x22, 0x03, 0x00, 0x00,
+0x00, 0x00, 0x3e, 0x75, 0x4f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x75, 0x44, 0x04, 0x00, 0x00,
+0x00, 0x00, 0x3f, 0x75, 0x6b, 0x06, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x75, 0x5e, 0x05, 0x00, 0x00,
+0x00, 0x00, 0x40, 0x75, 0x8b, 0x07, 0x00, 0x00, 0x00, 0x00, 0x40, 0x75, 0x7a, 0x06, 0x00, 0x00,
+0x00, 0x00, 0x41, 0x75, 0x9b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x41, 0x75, 0x9a, 0x07, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x03, 0x00, 0x00, 0x40,
+0x04, 0x02, 0x03, 0x00, 0x00, 0x23, 0x05, 0x02, 0x03, 0x00, 0x00, 0x24, 0x07, 0x02, 0x03, 0x00,
+0x00, 0x5e, 0x08, 0x02, 0x03, 0x00, 0x00, 0x26, 0x09, 0x02, 0x03, 0x00, 0x00, 0x2a, 0x1a, 0x02,
+0x03, 0x00, 0x00, 0x5b, 0x1b, 0x02, 0x03, 0x00, 0x00, 0x5d, 0x33, 0x02, 0x03, 0x00, 0x00, 0x3c,
+0x34, 0x02, 0x03, 0x00, 0x00, 0x3e, 0x00, 0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00, 0x2d,
+0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x01, 0x00, 0x22, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35,
+0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a,
+0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01,
+0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x43, 0x00, 0xe6, 0x96, 0xf7, 0xf6, 0x12,
+0x43, 0x00, 0xe3, 0x93, 0xb8, 0xb5, 0x13, 0x43, 0x00, 0xaa, 0x8a, 0xd1, 0xcf, 0x14, 0x42, 0x00,
+0xa5, 0x85, 0xfd, 0x15, 0x43, 0x00, 0xad, 0x8d, 0xfb, 0xfa, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17,
+0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x43,
+0x00, 0xe5, 0x95, 0xc6, 0xbe, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f,
+0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x43, 0x00, 0xa0, 0x80, 0xd5, 0xd4,
+0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x43, 0x00, 0xae,
+0x8e, 0xf5, 0xf4, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x43, 0x00,
+0xa6, 0x86, 0xf9, 0xf8, 0x28, 0x43, 0x00, 0xed, 0x9d, 0xf3, 0xf2, 0x29, 0x02, 0x00, 0xfe, 0x2b,
+0x7c, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0x41,
+0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00,
+0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec,
+0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0xc1, 0x00, 0xf1, 0x35,
+0xf0, 0x00, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x79, 0x00, 0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f,
+0x33, 0x05, 0x02, 0x00, 0x22, 0x34, 0xfd, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c,
+0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39,
+0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10,
+0x43, 0x00, 0xa9, 0x89, 0xd2, 0xd1, 0x11, 0x43, 0x00, 0xe6, 0x96, 0xb6, 0xb5, 0x12, 0x43, 0x00,
+0xe3, 0x93, 0xf7, 0xf6, 0x13, 0x43, 0x00, 0xaa, 0x8a, 0xd6, 0xd5, 0x14, 0x43, 0x00, 0xa5, 0x85,
+0xfb, 0xfa, 0x15, 0x43, 0x00, 0xad, 0x8d, 0xd4, 0xd3, 0x16, 0x43, 0x00, 0xa3, 0x83, 0xd8, 0xd7,
+0x17, 0x43, 0x00, 0xe8, 0x98, 0xb8, 0xb7, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7,
+0x87, 0x1a, 0x43, 0x00, 0xe5, 0x95, 0xbe, 0xbd, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00,
+0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0,
+0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc3, 0x00, 0xe0, 0x00, 0x90, 0x23, 0xf9, 0x23, 0xf8,
+0x23, 0x24, 0x43, 0x00, 0xae, 0x8e, 0xf3, 0xf2, 0x25, 0x43, 0x00, 0xab, 0x8b, 0xf5, 0xf4, 0x26,
+0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0x01,
+0x00, 0x7c, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x01, 0x00, 0x29, 0x28,
+0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x43, 0x00, 0xe7, 0x97, 0xc7, 0xc6, 0x2e, 0x41, 0x00, 0xe1,
+0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x43, 0x00, 0xa8, 0x88, 0xd0, 0xcf, 0x31, 0x41, 0x00,
+0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee,
+0x9e, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00,
+0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x02, 0x00, 0x22, 0x34, 0xfd, 0x06, 0x01, 0x00,
+0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f,
+0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d,
+0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x43, 0x00, 0xe6, 0x96, 0xd4,
+0xd3, 0x12, 0x43, 0x00, 0xe3, 0x93, 0xb8, 0xb5, 0x13, 0x43, 0x00, 0xaa, 0x8a, 0xc7, 0xbd, 0x14,
+0x43, 0x00, 0xa5, 0x85, 0xf7, 0xf6, 0x15, 0x43, 0x00, 0xad, 0x8d, 0xfb, 0xfa, 0x16, 0x43, 0x00,
+0xa3, 0x83, 0xb7, 0xb6, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x43,
+0x00, 0xa7, 0x87, 0xd6, 0xd5, 0x1a, 0x43, 0x00, 0xe5, 0x95, 0xc6, 0xbe, 0x1b, 0x41, 0x00, 0xea,
+0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82,
+0x21, 0x43, 0x00, 0xa0, 0x80, 0xf3, 0xf2, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0,
+0x00, 0x90, 0x23, 0x24, 0x43, 0x00, 0xae, 0x8e, 0xf5, 0xf4, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26,
+0x41, 0x00, 0xa4, 0x84, 0x27, 0x43, 0x00, 0xa6, 0x86, 0xd2, 0xd1, 0x28, 0x43, 0x00, 0xed, 0x9d,
+0xd0, 0xcf, 0x29, 0x01, 0x00, 0x7c, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x2b,
+0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x43,
+0x00, 0xe1, 0x91, 0xd8, 0xd7, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31,
+0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41,
+0x00, 0xee, 0x9e, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00,
+0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x02, 0x01, 0x00, 0xfc, 0x31, 0x03,
+0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x01, 0x00, 0x22, 0x34, 0x06, 0x01,
+0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00,
+0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21,
+0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96,
+0x12, 0x43, 0x00, 0xe3, 0x93, 0xf7, 0xf6, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5,
+0x85, 0xfd, 0x15, 0x43, 0x00, 0xad, 0x8d, 0xd5, 0xd4, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41,
+0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x43, 0x00, 0xa7, 0x87, 0xc6, 0xbe, 0x1a,
+0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x43,
+0x00, 0xeb, 0x9b, 0xfb, 0xfa, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x43, 0x00, 0xa0, 0x80, 0xf3,
+0xf2, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x43, 0x00,
+0xae, 0x8e, 0xf5, 0xf4, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x43,
+0x00, 0xa6, 0x86, 0xb8, 0xb5, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0x01, 0x00, 0x7c, 0x2b, 0x2a,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0xef,
+0x9f, 0x2d, 0x43, 0x00, 0xe7, 0x97, 0xd1, 0xcf, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00,
+0xac, 0x8c, 0x30, 0x43, 0x00, 0xa8, 0x88, 0xf9, 0xf8, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41,
+0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0xc1, 0x00,
+0xf1, 0x35, 0xf0, 0x00, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00,
+0x00, 0x00, 0x7f, 0x00, 0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01,
+0x00, 0x2f, 0x33, 0x05, 0x01, 0x00, 0x22, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00,
+0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f,
+0x39, 0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c,
+0x10, 0x43, 0x00, 0xa9, 0x89, 0xde, 0xdd, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x43, 0x00, 0xe3,
+0x93, 0xf7, 0xf6, 0x13, 0x43, 0x00, 0xaa, 0x8a, 0xd5, 0xd4, 0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd,
+0x15, 0x43, 0x00, 0xad, 0x8d, 0xd1, 0xcf, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8,
+0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95,
+0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20,
+0x41, 0x00, 0xa2, 0x82, 0x21, 0x43, 0x00, 0xa0, 0x80, 0xf3, 0xf2, 0x22, 0x41, 0x00, 0xaf, 0x8f,
+0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x43, 0x00, 0xae, 0x8e, 0xf5, 0xf4, 0x25, 0x41,
+0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x43, 0x00, 0xa6, 0x86, 0xfb, 0xfa, 0x28,
+0x43, 0x00, 0xed, 0x9d, 0xc6, 0xbe, 0x29, 0x01, 0x00, 0x7c, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00,
+0x00, 0x00, 0x80, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0x43, 0x00, 0xef, 0x9f, 0xb8, 0xb5, 0x2d,
+0x43, 0x00, 0xe7, 0x97, 0xf9, 0xf8, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c,
+0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33,
+0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00,
+0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00,
+0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05,
+0x02, 0x00, 0x22, 0x34, 0xfd, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08,
+0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01,
+0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00,
+0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x43, 0x00, 0xe3, 0x93, 0xf7, 0xf6, 0x13, 0x41,
+0x00, 0xaa, 0x8a, 0x14, 0x43, 0x00, 0xa5, 0x85, 0xdd, 0xdc, 0x15, 0x43, 0x00, 0xad, 0x8d, 0xdf,
+0xde, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99,
+0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e,
+0x41, 0x00, 0xe4, 0x94, 0x1f, 0x43, 0x00, 0xeb, 0x9b, 0xfb, 0xfa, 0x20, 0x41, 0x00, 0xa2, 0x82,
+0x21, 0x43, 0x00, 0xa0, 0x80, 0xf3, 0xf2, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0,
+0x00, 0x90, 0x23, 0x24, 0x43, 0x00, 0xae, 0x8e, 0xf5, 0xf4, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26,
+0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x43, 0x00, 0xed, 0x9d, 0xc6, 0xbe,
+0x29, 0x01, 0x00, 0x7c, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x82, 0x2b, 0x01, 0x00,
+0x29, 0x28, 0x2c, 0x43, 0x00, 0xef, 0x9f, 0xd5, 0xd4, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41,
+0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x43, 0x00, 0xa8, 0x88, 0xf9, 0xf8, 0x31,
+0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x43,
+0x00, 0xee, 0x9e, 0xd1, 0xcf, 0x35, 0xc3, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0xb8, 0x35, 0xb5, 0x35,
+0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00,
+0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05,
+0x01, 0x00, 0x22, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01,
+0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00,
+0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9,
+0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a,
+0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15, 0x43, 0x00, 0xad, 0x8d, 0xb8, 0xb5, 0x16, 0x41, 0x00,
+0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7,
+0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94,
+0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22,
+0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x43, 0x00, 0xae, 0x8e,
+0xf5, 0xf4, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6,
+0x86, 0x28, 0x43, 0x00, 0xed, 0x9d, 0xc6, 0xbe, 0x29, 0x01, 0x00, 0x7c, 0x2b, 0x2a, 0x04, 0x1f,
+0x00, 0x00, 0x00, 0x00, 0x84, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d,
+0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x43,
+0x00, 0xa8, 0x88, 0xd1, 0xcf, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33,
+0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00,
+0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x16, 0x43, 0x03, 0x00, 0x00, 0xf7, 0xf6, 0x18, 0x43,
+0x03, 0x00, 0x00, 0xf5, 0xf4, 0x1e, 0x43, 0x03, 0x00, 0x00, 0xf3, 0xf2, 0x1f, 0x43, 0x03, 0x00,
+0x00, 0xfb, 0xfa, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xd5, 0xd4, 0x2d, 0x43, 0x03, 0x00, 0x00, 0xde,
+0xdd, 0x2e, 0x43, 0x03, 0x00, 0x00, 0xf9, 0xf8, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x85,
+0x00, 0x4d, 0x4e, 0x03, 0x09, 0x00, 0x00, 0x54, 0x52, 0x2c, 0xb3, 0x00, 0x54, 0x52, 0x05, 0x04,
+0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x03, 0xbb, 0x01, 0xdd, 0x01,
+0x00, 0x00, 0x55, 0x03, 0x60, 0x02, 0xb7, 0x02, 0x00, 0x00, 0x5a, 0x03, 0x00, 0x00, 0x44, 0x01,
+0x00, 0x00, 0x52, 0x03, 0x3d, 0x01, 0x44, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00,
+0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x3e,
+0x03, 0x02, 0x01, 0x00, 0x27, 0x9c, 0x04, 0x03, 0x03, 0x00, 0xc8, 0x23, 0x1e, 0x05, 0x02, 0x01,
+0x00, 0x2b, 0x24, 0x06, 0x02, 0x03, 0x00, 0x00, 0xab, 0x07, 0x01, 0x01, 0x00, 0x26, 0x08, 0x02,
+0x01, 0x00, 0x2f, 0x7b, 0x09, 0x03, 0x01, 0x00, 0x28, 0x5b, 0x1b, 0x0a, 0x03, 0x01, 0x00, 0x29,
+0x5d, 0x1d, 0x0b, 0x02, 0x01, 0x00, 0x3d, 0x7d, 0x0c, 0x03, 0x00, 0x2a, 0x3f, 0x5c, 0x1c, 0x0d,
+0x03, 0x00, 0x2d, 0x5f, 0x7c, 0x1f, 0x10, 0x42, 0x03, 0x00, 0x00, 0x40, 0x12, 0x42, 0x00, 0x65,
+0x45, 0xd5, 0x13, 0x42, 0x03, 0x00, 0x00, 0xf4, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00,
+0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x19, 0x42, 0x03, 0x00,
+0x00, 0x9c, 0x1a, 0x02, 0x07, 0xa0, 0xa0, 0xc9, 0x1b, 0x42, 0x04, 0x81, 0x9a, 0xca, 0x1e, 0x44,
+0x08, 0x61, 0x41, 0x91, 0x00, 0x92, 0x1f, 0x42, 0x03, 0x00, 0x00, 0xe1, 0x27, 0x02, 0x07, 0xa0,
+0xa0, 0xcb, 0x28, 0xc5, 0x00, 0x69, 0x17, 0x49, 0x17, 0x00, 0x17, 0x09, 0x17, 0x00, 0x17, 0x00,
+0x17, 0x29, 0x02, 0x00, 0x22, 0x82, 0x3c, 0x2b, 0x02, 0x04, 0x2c, 0x3b, 0xcc, 0x2c, 0x42, 0x03,
+0x00, 0x00, 0xae, 0x2d, 0x42, 0x03, 0x00, 0x00, 0xaf, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x33, 0x42,
+0x00, 0x94, 0x99, 0x3c, 0x34, 0x42, 0x00, 0x87, 0x80, 0x3e, 0x35, 0x02, 0x00, 0x2e, 0x3a, 0x7c,
+0x39, 0x00, 0x00, 0x20, 0x56, 0x02, 0x00, 0x3c, 0x3e, 0x7c, 0x00, 0x12, 0x42, 0x07, 0x00, 0x00,
+0xa0, 0x00, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6,
+0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89,
+0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99,
+0x55, 0x9a, 0x20, 0xf9, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5,
+0x4f, 0xe5, 0x20, 0x7e, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3,
+0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef,
+0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4,
+0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x00, 0x17, 0x44, 0x0a, 0x8d, 0x00, 0x69, 0x00,
+0x98, 0x1a, 0x41, 0x00, 0xa7, 0xa6, 0x27, 0x41, 0x00, 0x9f, 0x9e, 0x28, 0xc5, 0x00, 0x69, 0x17,
+0x98, 0x17, 0x00, 0x17, 0x09, 0x17, 0x00, 0x17, 0x00, 0x17, 0x00, 0x5e, 0x0d, 0x61, 0x83, 0x65,
+0x88, 0x8d, 0x8c, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x98,
+0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xf9, 0x0e, 0x61, 0x84, 0x65, 0x89, 0x8d, 0x8b, 0x69,
+0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0xed, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x98, 0xd8, 0x4f,
+0x99, 0x55, 0x9a, 0x20, 0xf9, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e,
+0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x8d, 0xa1, 0x69, 0xa1, 0x6f,
+0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x98, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x20,
+0xef, 0x60, 0x0d, 0x61, 0x85, 0x65, 0x8a, 0x8d, 0xec, 0x69, 0xec, 0x6f, 0x95, 0x75, 0x97, 0x41,
+0xb7, 0x45, 0xd4, 0x49, 0xde, 0x98, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x00, 0x12, 0x42,
+0x07, 0x00, 0x00, 0xa0, 0x13, 0x42, 0x03, 0x00, 0x00, 0x14, 0x16, 0x44, 0x0b, 0x00, 0x00, 0xed,
+0x00, 0xec, 0x17, 0x44, 0x0a, 0xd5, 0x00, 0x69, 0x00, 0x98, 0x1a, 0x41, 0x00, 0xa7, 0xa6, 0x1e,
+0x44, 0x1f, 0x00, 0x00, 0xa0, 0x00, 0xa0, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x22, 0x41, 0x00, 0x67,
+0x47, 0x23, 0x41, 0x00, 0x68, 0x48, 0x24, 0x41, 0x00, 0x6a, 0x4a, 0x27, 0x41, 0x00, 0xad, 0xb8,
+0x28, 0xc5, 0x00, 0x69, 0x17, 0x98, 0x17, 0x00, 0x17, 0x09, 0x17, 0x00, 0x17, 0x00, 0x17, 0x2e,
+0x41, 0x00, 0x63, 0x43, 0x00, 0x5e, 0x17, 0x61, 0x83, 0x63, 0x86, 0x65, 0x88, 0x67, 0x9b, 0x68,
+0xa9, 0xd5, 0x8c, 0x69, 0x8c, 0x6a, 0x9f, 0x6f, 0x93, 0x73, 0xc7, 0x75, 0x96, 0x41, 0xb6, 0x43,
+0x8f, 0x45, 0xd2, 0x47, 0x9d, 0x48, 0xa8, 0x49, 0xd7, 0x98, 0xd7, 0x4a, 0xac, 0x4f, 0xe2, 0x53,
+0xc6, 0x55, 0xea, 0x20, 0x5e, 0xf9, 0x0d, 0x61, 0x84, 0x65, 0x89, 0xd5, 0x8b, 0x69, 0x8b, 0x6f,
+0x94, 0x75, 0x81, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x98, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20,
+0xf9, 0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0xd5,
+0xa1, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x98, 0xd6, 0x4f,
+0xe0, 0x55, 0xe9, 0x20, 0xef, 0x60, 0x0d, 0x61, 0x85, 0x65, 0x8a, 0xd5, 0x8d, 0x69, 0x8d, 0x6f,
+0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x98, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20,
+0x60, 0x00, 0x50, 0x0b, 0x05, 0x04, 0xb8, 0x01, 0x54, 0x52, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00,
+0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x03, 0xec, 0x02, 0x42, 0x03, 0x00, 0x00, 0x55, 0x03,
+0xc5, 0x03, 0x79, 0x04, 0x00, 0x00, 0x52, 0x03, 0x65, 0x02, 0x75, 0x02, 0x00, 0x00, 0x5a, 0x03,
+0x00, 0x00, 0x75, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x0b, 0x00, 0x00, 0xfb, 0x00, 0xad, 0x03, 0x02,
+0x01, 0x00, 0x22, 0xfd, 0x04, 0x04, 0x03, 0x00, 0xc8, 0x23, 0x1e, 0xfc, 0x05, 0x04, 0x0b, 0x00,
+0x00, 0xac, 0x00, 0xcf, 0x06, 0x02, 0x03, 0x00, 0x00, 0xab, 0x07, 0x02, 0x01, 0x00, 0x26, 0xf3,
+0x08, 0x02, 0x01, 0x00, 0x27, 0x7b, 0x09, 0x03, 0x01, 0x00, 0x28, 0x5b, 0x1b, 0x0a, 0x03, 0x01,
+0x00, 0x29, 0x5d, 0x1d, 0x0b, 0x02, 0x01, 0x00, 0x3d, 0x7d, 0x0c, 0x04, 0x00, 0x2f, 0x3f, 0x5c,
+0x1c, 0xa8, 0x0d, 0x03, 0x00, 0x2d, 0x5f, 0x7c, 0x1f, 0x10, 0xc5, 0x00, 0x66, 0x21, 0x46, 0x21,
+0x40, 0x21, 0x06, 0x21, 0x00, 0x21, 0x00, 0x21, 0x11, 0xc5, 0x00, 0x67, 0x22, 0x47, 0x22, 0x00,
+0x22, 0x07, 0x22, 0x00, 0x22, 0x00, 0x22, 0x12, 0x10, 0x00, 0x00, 0x13, 0xc5, 0x00, 0x69, 0x17,
+0x49, 0x17, 0xf4, 0x17, 0x09, 0x17, 0xa9, 0x17, 0x00, 0x17, 0x14, 0xc5, 0x00, 0x6f, 0x18, 0x4f,
+0x18, 0x00, 0x18, 0x0f, 0x18, 0x00, 0x18, 0x00, 0x18, 0x15, 0xc5, 0x00, 0x64, 0x20, 0x44, 0x20,
+0xbe, 0x20, 0x04, 0x20, 0x00, 0x20, 0x00, 0x20, 0x16, 0xc5, 0x00, 0x72, 0x13, 0x52, 0x13, 0x00,
+0x13, 0x12, 0x13, 0x00, 0x13, 0x00, 0x13, 0x17, 0xc5, 0x00, 0x6e, 0x31, 0x4e, 0x31, 0x00, 0x31,
+0x0e, 0x31, 0x00, 0x31, 0x00, 0x31, 0x18, 0xc5, 0x00, 0x68, 0x23, 0x48, 0x23, 0x9b, 0x23, 0x08,
+0x23, 0x9d, 0x23, 0x00, 0x23, 0x19, 0x42, 0x03, 0x00, 0x00, 0x9c, 0x1a, 0xc5, 0x04, 0x71, 0x10,
+0x51, 0x10, 0xc9, 0x10, 0x11, 0x10, 0x00, 0x10, 0x00, 0x10, 0x1b, 0xc5, 0x04, 0x77, 0x11, 0x57,
+0x11, 0xca, 0x11, 0x17, 0x11, 0x00, 0x11, 0x00, 0x11, 0x1e, 0xc5, 0x00, 0x75, 0x16, 0x55, 0x16,
+0x91, 0x16, 0x15, 0x16, 0x92, 0x16, 0x00, 0x16, 0x1f, 0xc5, 0x00, 0x69, 0x17, 0x49, 0x17, 0xe1,
+0x17, 0x09, 0x17, 0xf5, 0x17, 0x00, 0x17, 0x20, 0xc5, 0x00, 0x65, 0x12, 0x45, 0x12, 0xd5, 0x12,
+0x05, 0x12, 0x00, 0x12, 0x00, 0x12, 0x21, 0xc5, 0x00, 0x61, 0x1e, 0x41, 0x1e, 0x00, 0x1e, 0x01,
+0x1e, 0xa6, 0x1e, 0x00, 0x1e, 0x22, 0x41, 0x00, 0x81, 0x9a, 0x23, 0xc5, 0x00, 0x74, 0x14, 0x54,
+0x14, 0x00, 0x14, 0x14, 0x14, 0x00, 0x14, 0x00, 0x14, 0x24, 0xc5, 0x00, 0x6b, 0x25, 0x4b, 0x25,
+0x00, 0x25, 0x0b, 0x25, 0x00, 0x25, 0x00, 0x25, 0x25, 0xc5, 0x00, 0x6d, 0x32, 0x4d, 0x32, 0x00,
+0x32, 0x0d, 0x32, 0x00, 0x32, 0x00, 0x32, 0x27, 0xc5, 0x04, 0x79, 0x15, 0x59, 0x15, 0xcb, 0x15,
+0x19, 0x15, 0x00, 0x15, 0x00, 0x15, 0x28, 0x10, 0x00, 0x00, 0x29, 0x02, 0x00, 0x2b, 0x2a, 0xaa,
+0x2b, 0xc5, 0x04, 0x78, 0x2d, 0x58, 0x2d, 0xcc, 0x2d, 0x18, 0x2d, 0x00, 0x2d, 0x00, 0x2d, 0x2c,
+0xc5, 0x00, 0x6a, 0x24, 0x4a, 0x24, 0xae, 0x24, 0x0a, 0x24, 0x3c, 0x24, 0x00, 0x24, 0x2d, 0x44,
+0x08, 0x94, 0x99, 0xaf, 0x00, 0x3e, 0x2e, 0xc5, 0x00, 0x76, 0x2f, 0x56, 0x2f, 0xbd, 0x2f, 0x16,
+0x2f, 0xb8, 0x2f, 0x00, 0x2f, 0x2f, 0xc5, 0x00, 0x63, 0x2e, 0x43, 0x2e, 0x3c, 0x2e, 0x03, 0x2e,
+0x00, 0x2e, 0x00, 0x2e, 0x30, 0x42, 0x00, 0x87, 0x80, 0x3e, 0x31, 0xc5, 0x00, 0x7a, 0x2c, 0x5a,
+0x2c, 0x7c, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x32, 0xc5, 0x00, 0x73, 0x1f, 0x53, 0x1f,
+0xe6, 0x1f, 0x13, 0x1f, 0xa7, 0x1f, 0x00, 0x1f, 0x33, 0xc5, 0x00, 0x62, 0x30, 0x42, 0x30, 0x9e,
+0x30, 0x02, 0x30, 0x00, 0x30, 0x00, 0x30, 0x34, 0x02, 0x01, 0x00, 0x3a, 0xf6, 0x35, 0x02, 0x00,
+0x2c, 0x3b, 0x2d, 0x39, 0x00, 0x00, 0x20, 0x56, 0x03, 0x00, 0x3c, 0x3e, 0x7c, 0x1c, 0x00, 0x20,
+0xc5, 0x04, 0x65, 0x12, 0x45, 0x12, 0xa0, 0x12, 0x05, 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, 0x5e,
+0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49,
+0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f,
+0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20,
+0xf9, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20,
+0x7e, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41,
+0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0x60, 0x0b, 0x61,
+0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f,
+0xe3, 0x55, 0xeb, 0x20, 0x60, 0x00, 0x12, 0x41, 0x00, 0xa7, 0xa6, 0x13, 0xc5, 0x00, 0x8d, 0x17,
+0x49, 0x17, 0xf4, 0x17, 0x09, 0x17, 0xa9, 0x17, 0x00, 0x17, 0x1f, 0xc5, 0x00, 0x69, 0x17, 0x98,
+0x17, 0xe1, 0x17, 0x09, 0x17, 0xf5, 0x17, 0x00, 0x17, 0x21, 0xc5, 0x10, 0x61, 0x1e, 0x41, 0x1e,
+0x00, 0x1e, 0x01, 0x1e, 0xa0, 0x1e, 0x00, 0x1e, 0x28, 0x41, 0x00, 0x9f, 0x9e, 0x32, 0xc5, 0x10,
+0x73, 0x1f, 0x53, 0x1f, 0xe6, 0x1f, 0x13, 0x1f, 0xa0, 0x1f, 0x00, 0x1f, 0x33, 0xc5, 0x00, 0x62,
+0x30, 0x42, 0x30, 0xe8, 0x30, 0x02, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x5e, 0x0d, 0x61, 0x83,
+0x65, 0x88, 0x8d, 0x8c, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7,
+0x98, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xf9, 0x0e, 0x61, 0x84, 0x65, 0x89, 0x8d, 0x8b,
+0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0xed, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x98, 0xd8,
+0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7,
+0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x8d, 0xa1, 0x69, 0xa1,
+0x6f, 0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x98, 0xd6, 0x4f, 0xe0, 0x55, 0xe9,
+0x20, 0xef, 0x60, 0x0d, 0x61, 0x85, 0x65, 0x8a, 0x8d, 0xec, 0x69, 0xec, 0x6f, 0x95, 0x75, 0x97,
+0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x98, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x00, 0x02,
+0x04, 0x1f, 0x00, 0x00, 0xa0, 0x00, 0xa0, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x07, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x0c, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x12, 0x41, 0x00, 0xa7, 0xa6,
+0x13, 0xc5, 0x14, 0xd5, 0x17, 0x49, 0x17, 0xa0, 0x17, 0x09, 0x17, 0xa0, 0x17, 0x00, 0x17, 0x15,
+0xc5, 0x04, 0x64, 0x20, 0x44, 0x20, 0xa0, 0x20, 0x04, 0x20, 0x00, 0x20, 0x00, 0x20, 0x18, 0xc5,
+0x14, 0x68, 0x23, 0x48, 0x23, 0xa0, 0x23, 0x08, 0x23, 0xa0, 0x23, 0x00, 0x23, 0x1e, 0xc5, 0x00,
+0x75, 0x16, 0x55, 0x16, 0xed, 0x16, 0x15, 0x16, 0xec, 0x16, 0x00, 0x16, 0x1f, 0xc5, 0x00, 0x69,
+0x17, 0x98, 0x17, 0xe1, 0x17, 0x09, 0x17, 0xf5, 0x17, 0x00, 0x17, 0x20, 0xc5, 0x04, 0x65, 0x12,
+0x45, 0x12, 0xa0, 0x12, 0x05, 0x12, 0x00, 0x12, 0x00, 0x12, 0x21, 0xc5, 0x10, 0x61, 0x1e, 0x41,
+0x1e, 0x00, 0x1e, 0x01, 0x1e, 0xa0, 0x1e, 0x00, 0x1e, 0x28, 0x41, 0x00, 0xad, 0xb8, 0x29, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x2e, 0xc5, 0x14, 0x76, 0x2f, 0x56, 0x2f, 0xa0, 0x2f, 0x16, 0x2f, 0xa0,
+0x2f, 0x00, 0x2f, 0x32, 0xc5, 0x10, 0x73, 0x1f, 0x53, 0x1f, 0xe6, 0x1f, 0x13, 0x1f, 0xa0, 0x1f,
+0x00, 0x1f, 0x00, 0x5e, 0x17, 0x61, 0x83, 0x63, 0x86, 0x65, 0x88, 0x67, 0x9b, 0x68, 0xa9, 0xd5,
+0x8c, 0x69, 0x8c, 0x6a, 0x9f, 0x6f, 0x93, 0x73, 0xc7, 0x75, 0x96, 0x41, 0xb6, 0x43, 0x8f, 0x45,
+0xd2, 0x47, 0x9d, 0x48, 0xa8, 0x49, 0xd7, 0x98, 0xd7, 0x4a, 0xac, 0x4f, 0xe2, 0x53, 0xc6, 0x55,
+0xea, 0x20, 0x5e, 0xf9, 0x0d, 0x61, 0x84, 0x65, 0x89, 0xd5, 0x8b, 0x69, 0x8b, 0x6f, 0x94, 0x75,
+0x81, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x98, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x7e,
+0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0xef, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0xd5, 0xa1, 0x69,
+0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x98, 0xd6, 0x4f, 0xe0, 0x55,
+0xe9, 0x20, 0xef, 0x60, 0x0d, 0x61, 0x85, 0x65, 0x8a, 0xd5, 0x8d, 0x69, 0x8d, 0x6f, 0x95, 0x75,
+0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x98, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x00,
+0x2f, 0xc4, 0x02, 0x04, 0x00, 0x00, 0x54, 0x54, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x3d, 0x75, 0x6b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x75, 0x8b, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x42, 0x75, 0x4c, 0x02, 0xa2, 0x02, 0x00, 0x00, 0x42, 0x75, 0x7a, 0x01,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x03, 0x00,
+0x00, 0x40, 0x05, 0x02, 0x03, 0x00, 0x00, 0x24, 0x07, 0x02, 0x03, 0x00, 0x00, 0x5e, 0x08, 0x02,
+0x03, 0x00, 0x00, 0x5b, 0x09, 0x02, 0x03, 0x00, 0x00, 0x5d, 0x0a, 0x02, 0x03, 0x00, 0x00, 0x7b,
+0x0b, 0x02, 0x03, 0x00, 0x00, 0x7d, 0x33, 0x02, 0x03, 0x00, 0x00, 0x3c, 0x34, 0x02, 0x03, 0x00,
+0x00, 0x3e, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x05, 0x01, 0x01, 0x00, 0x3b, 0x07, 0x01, 0x01,
+0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x43, 0x00, 0xf5,
+0xf4, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00,
+0xa5, 0x85, 0xfd, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00,
+0xe8, 0x98, 0x18, 0x43, 0x00, 0xd0, 0xcf, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41,
+0x00, 0xe5, 0x95, 0x1b, 0x43, 0x00, 0xb8, 0xb5, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f,
+0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41,
+0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25,
+0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x43, 0x00, 0xfb, 0xfa, 0xa6, 0x86,
+0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0xc3, 0x00, 0xc6, 0x29, 0xbe, 0x29, 0xf1, 0x29, 0xf0, 0x00,
+0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x02, 0x01, 0x00, 0x2f, 0x7c, 0x2c, 0x41,
+0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00,
+0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x43, 0x00, 0xd2,
+0xd1, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01, 0x00,
+0x2e, 0x2c, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x79, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x05, 0x01, 0x01, 0x00, 0x3b, 0x07, 0x01, 0x01, 0x00,
+0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96,
+0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd,
+0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18,
+0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41,
+0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00,
+0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0,
+0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00,
+0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0xc1, 0x00, 0xf1,
+0x29, 0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x02, 0x01, 0x00, 0x2f,
+0x7c, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91,
+0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32,
+0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01,
+0x00, 0x2e, 0x2c, 0x00, 0x07, 0x03, 0x03, 0x00, 0x00, 0xc7, 0xc6, 0x12, 0x42, 0x00, 0x65, 0x45,
+0xfd, 0x16, 0x43, 0x00, 0x75, 0x55, 0xf7, 0xf6, 0x17, 0x43, 0x00, 0x69, 0xbd, 0xbe, 0x49, 0x18,
+0x43, 0x00, 0x6f, 0x4f, 0xf5, 0xf4, 0x1e, 0x43, 0x00, 0x61, 0x41, 0xf3, 0xf2, 0x1f, 0x43, 0x03,
+0x00, 0x00, 0xfb, 0xfa, 0x22, 0x43, 0x03, 0x00, 0x00, 0xb6, 0xb5, 0x28, 0x00, 0x01, 0xc8, 0x2e,
+0x43, 0x03, 0x00, 0x00, 0xf9, 0xf8, 0x31, 0x43, 0x03, 0x00, 0x00, 0xb8, 0xb7, 0x36, 0x04, 0x1f,
+0x00, 0x00, 0x00, 0x00, 0x7b, 0x39, 0x00, 0x00, 0x20, 0x00, 0x27, 0x0d, 0x61, 0xd0, 0x65, 0xd2,
+0xbe, 0xd4, 0x69, 0xd4, 0x6f, 0xd6, 0x75, 0xd8, 0x41, 0xcf, 0x45, 0xd1, 0x49, 0xd3, 0xbd, 0xd3,
+0x4f, 0xd5, 0x55, 0xd7, 0x20, 0x27, 0x00, 0x17, 0x18, 0x03, 0x04, 0xbb, 0x01, 0x54, 0x54, 0x05,
+0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x75, 0x8a, 0x01, 0xe0,
+0x01, 0x00, 0x00, 0x42, 0x75, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x75, 0x04, 0x03, 0x00,
+0x00, 0x00, 0x00, 0x3d, 0x75, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x03, 0x02, 0x03, 0x00, 0x00, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00, 0x23, 0x05,
+0x02, 0x03, 0x00, 0x00, 0x24, 0x07, 0x02, 0x03, 0x00, 0x00, 0x5e, 0x08, 0x02, 0x03, 0x00, 0x00,
+0x26, 0x09, 0x02, 0x03, 0x00, 0x00, 0x2a, 0x1a, 0x02, 0x03, 0x00, 0x00, 0x5b, 0x1b, 0x02, 0x03,
+0x00, 0x00, 0x5d, 0x33, 0x02, 0x03, 0x00, 0x00, 0x3c, 0x34, 0x02, 0x03, 0x00, 0x00, 0x3e, 0x00,
+0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05,
+0x01, 0x00, 0x22, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01,
+0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00,
+0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9,
+0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a,
+0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83,
+0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a,
+0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41,
+0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00,
+0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41,
+0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00,
+0xed, 0x9d, 0x29, 0x01, 0x00, 0x7c, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b,
+0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41,
+0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00,
+0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee,
+0x9e, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0x00, 0x07, 0x03, 0x03, 0x00, 0x00, 0xc7, 0xc6,
+0x12, 0x42, 0x00, 0x65, 0x45, 0xfd, 0x16, 0x43, 0x00, 0x75, 0x55, 0xf7, 0xf6, 0x17, 0x43, 0x00,
+0x69, 0xbd, 0xbe, 0x49, 0x18, 0x43, 0x00, 0x6f, 0x4f, 0xf5, 0xf4, 0x1e, 0x43, 0x00, 0x61, 0x41,
+0xf3, 0xf2, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xfb, 0xfa, 0x22, 0x43, 0x03, 0x00, 0x00, 0xb6, 0xb5,
+0x28, 0x00, 0x01, 0xc8, 0x2e, 0x43, 0x03, 0x00, 0x00, 0xf9, 0xf8, 0x31, 0x43, 0x03, 0x00, 0x00,
+0xb8, 0xb7, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x39, 0x00, 0x00, 0x20, 0x00, 0x27,
+0x0d, 0x61, 0xd0, 0x65, 0xd2, 0xbe, 0xd4, 0x69, 0xd4, 0x6f, 0xd6, 0x75, 0xd8, 0x41, 0xcf, 0x45,
+0xd1, 0x49, 0xd3, 0xbd, 0xd3, 0x4f, 0xd5, 0x55, 0xd7, 0x20, 0x27, 0x00, 0x02, 0x01, 0x00, 0xfc,
+0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x01, 0x00, 0x22, 0x34,
+0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09,
+0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01,
+0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x43, 0x00,
+0xf5, 0xf4, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42,
+0x00, 0xa5, 0x85, 0xfd, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41,
+0x00, 0xe8, 0x98, 0x18, 0x43, 0x00, 0xd0, 0xcf, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a,
+0x41, 0x00, 0xe5, 0x95, 0x1b, 0x43, 0x00, 0xb8, 0xb5, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94,
+0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22,
+0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e,
+0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x43, 0x00, 0xfb, 0xfa, 0xa6,
+0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0x01, 0x00, 0x7c, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00,
+0x00, 0x00, 0x7a, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00,
+0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8,
+0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x43, 0x00, 0xd2, 0xd1, 0xec, 0x9c, 0x33, 0x41, 0x00,
+0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0xc3, 0x00, 0xc6, 0x35, 0xbe, 0x35, 0xf1, 0x35,
+0xf0, 0x00, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x7b, 0x00, 0xaf, 0x72, 0x04, 0x13, 0x00, 0x00, 0x59, 0x43, 0x2c, 0x00, 0x00, 0x53, 0x52, 0x2c,
+0x76, 0x00, 0x59, 0x43, 0x2c, 0x76, 0x00, 0x53, 0x52, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0d,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x03, 0xda, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x03, 0x1b,
+0x01, 0xb0, 0x02, 0x00, 0x00, 0x57, 0x03, 0xa7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0xef,
+0x01, 0xb0, 0x02, 0x00, 0x00, 0x50, 0x03, 0x79, 0x03, 0x00, 0x00, 0x00, 0x00, 0x50, 0x03, 0xbd,
+0x02, 0x00, 0x00, 0x00, 0x00, 0x65, 0x04, 0x4f, 0x04, 0x00, 0x00, 0x00, 0x00, 0x65, 0x04, 0x8e,
+0x03, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x01,
+0x00, 0x22, 0x40, 0x05, 0x02, 0x03, 0x00, 0x00, 0xcf, 0x07, 0x02, 0x01, 0x00, 0x26, 0x5e, 0x08,
+0x02, 0x01, 0x00, 0x2f, 0x26, 0x09, 0x02, 0x01, 0x00, 0x28, 0x24, 0x0a, 0x02, 0x01, 0x00, 0x29,
+0x3c, 0x0b, 0x02, 0x01, 0x00, 0x3d, 0x3e, 0x0c, 0x01, 0x00, 0x27, 0x3f, 0x0d, 0x01, 0x00, 0x2b,
+0x2a, 0x15, 0xc4, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x1a, 0x02,
+0x03, 0x00, 0x00, 0x5b, 0x1b, 0x02, 0x00, 0x5c, 0x5c, 0x5d, 0x27, 0x03, 0x04, 0x5e, 0x5e, 0x00,
+0x1e, 0x28, 0x03, 0x04, 0x5d, 0x7d, 0x00, 0x1d, 0x29, 0x01, 0x00, 0x49, 0x56, 0x2b, 0x02, 0x00,
+0x40, 0x40, 0x7c, 0x2c, 0xc4, 0x00, 0x79, 0x15, 0x59, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15,
+0x33, 0x02, 0x00, 0x2c, 0x3b, 0x3c, 0x34, 0x02, 0x00, 0x2e, 0x3a, 0x3e, 0x35, 0x03, 0x00, 0x2d,
+0x5f, 0x2f, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56, 0x01, 0x00, 0x3c, 0x3e, 0x00, 0x12, 0x42, 0x03,
+0x00, 0x00, 0xd5, 0x00, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0c, 0x00, 0x01, 0xc8, 0x10, 0x41,
+0x00, 0x90, 0x91, 0x11, 0x41, 0x00, 0x92, 0x93, 0x12, 0x42, 0x00, 0xa8, 0xa9, 0xcf, 0x13, 0x41,
+0x00, 0xe1, 0xe2, 0x14, 0x41, 0x00, 0xe5, 0xe6, 0x15, 0xc4, 0x00, 0xf3, 0x2c, 0xf4, 0x2c, 0x00,
+0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0xe7, 0xe8, 0x17, 0x41, 0x00, 0xb7, 0xb8, 0x18,
+0x41, 0x00, 0xd6, 0xd7, 0x19, 0x41, 0x00, 0xd8, 0xdd, 0x1a, 0x41, 0x00, 0xf5, 0xf6, 0x1b, 0x41,
+0x00, 0x80, 0x81, 0x1e, 0x41, 0x00, 0xa0, 0xa1, 0x1f, 0x41, 0x00, 0xe3, 0xe4, 0x20, 0x41, 0x00,
+0xa6, 0xa7, 0x21, 0x41, 0x00, 0xaa, 0xab, 0x22, 0x41, 0x00, 0xac, 0xad, 0x23, 0x41, 0x00, 0xb5,
+0xb6, 0x24, 0x41, 0x00, 0x8e, 0x8f, 0x25, 0x41, 0x00, 0xc6, 0xc7, 0x26, 0x41, 0x00, 0xd0, 0xd1,
+0x27, 0x41, 0x00, 0xfb, 0xfc, 0x28, 0x41, 0x00, 0x94, 0x95, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x78, 0x2b, 0x41, 0x00, 0xe9, 0xea, 0x2c, 0xc4, 0x00, 0x88, 0x15, 0x89, 0x15, 0x00, 0x15,
+0x19, 0x15, 0x00, 0x15, 0x2d, 0x41, 0x00, 0x9a, 0x9b, 0x2e, 0x41, 0x00, 0xa4, 0xa5, 0x2f, 0x41,
+0x00, 0xeb, 0xec, 0x30, 0x41, 0x00, 0xa2, 0xa3, 0x31, 0x41, 0x00, 0xd4, 0xd5, 0x32, 0x41, 0x00,
+0xd2, 0xd3, 0x00, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x42, 0x03, 0x00, 0x00, 0xcf, 0x36,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x0c, 0x00, 0x01, 0xc8, 0x10, 0x41, 0x00, 0x90,
+0x91, 0x11, 0x41, 0x00, 0x92, 0x93, 0x12, 0x41, 0x00, 0xa8, 0xa9, 0x13, 0x41, 0x00, 0xe1, 0xe2,
+0x14, 0x41, 0x00, 0xe5, 0xe6, 0x15, 0xc4, 0x00, 0xf3, 0x2c, 0xf4, 0x2c, 0x00, 0x2c, 0x1a, 0x2c,
+0x00, 0x2c, 0x16, 0x41, 0x00, 0xe7, 0xe8, 0x17, 0x41, 0x00, 0xb7, 0xb8, 0x18, 0x41, 0x00, 0xd6,
+0xd7, 0x19, 0x41, 0x00, 0xd8, 0xdd, 0x1a, 0x41, 0x00, 0xf5, 0xf6, 0x1b, 0x41, 0x00, 0x80, 0x81,
+0x1e, 0x41, 0x00, 0xa0, 0xa1, 0x1f, 0x41, 0x00, 0xe3, 0xe4, 0x20, 0x41, 0x00, 0xa6, 0xa7, 0x21,
+0x41, 0x00, 0xaa, 0xab, 0x22, 0x41, 0x00, 0xac, 0xad, 0x23, 0x41, 0x00, 0xb5, 0xb6, 0x24, 0x41,
+0x00, 0x8e, 0x8f, 0x25, 0x41, 0x00, 0xc6, 0xc7, 0x26, 0x41, 0x00, 0xd0, 0xd1, 0x27, 0x41, 0x00,
+0xfb, 0xfc, 0x28, 0x41, 0x00, 0x94, 0x95, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b,
+0x41, 0x00, 0xe9, 0xea, 0x2c, 0xc4, 0x00, 0x88, 0x15, 0x89, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00,
+0x15, 0x2d, 0x41, 0x00, 0x9a, 0x9b, 0x2e, 0x41, 0x00, 0xa4, 0xa5, 0x2f, 0x41, 0x00, 0xeb, 0xec,
+0x30, 0x41, 0x00, 0xa2, 0xa3, 0x31, 0x41, 0x00, 0xd4, 0xd5, 0x32, 0x41, 0x00, 0xd2, 0xd3, 0x00,
+0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x27, 0x05, 0xac, 0x82, 0xc6, 0x96, 0xad,
+0x83, 0xc7, 0x97, 0x20, 0x27, 0x00, 0x10, 0x41, 0x00, 0xef, 0x9f, 0x11, 0x41, 0x00, 0xf9, 0xf8,
+0x12, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x13, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00,
+0xe2, 0x92, 0x15, 0xc4, 0x00, 0xa7, 0x2c, 0x87, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16,
+0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xf7, 0xf6, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41,
+0x00, 0xaf, 0x8f, 0x1a, 0x41, 0x00, 0xe8, 0x98, 0x1b, 0x41, 0x00, 0xeb, 0x9b, 0x1e, 0x41, 0x00,
+0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4,
+0x94, 0x22, 0x41, 0x00, 0xf3, 0xf2, 0x23, 0x41, 0x00, 0xa3, 0x83, 0x24, 0x41, 0x00, 0xa9, 0x89,
+0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x27, 0x41, 0x00, 0xe7, 0x97, 0x28,
+0x41, 0x00, 0xee, 0x9e, 0x29, 0x41, 0x00, 0xec, 0x9c, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x7c, 0x2b, 0x41, 0x00, 0xa6, 0x86, 0x2c, 0xc4, 0x00, 0xa8, 0x15, 0x88, 0x15, 0x00, 0x15, 0x19,
+0x15, 0x00, 0x15, 0x2d, 0x41, 0x00, 0xe5, 0x95, 0x2e, 0x41, 0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00,
+0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x41, 0x00, 0xac,
+0x8c, 0x00, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x10, 0x41, 0x00,
+0xef, 0x9f, 0x11, 0x41, 0x00, 0xf9, 0xf8, 0x12, 0x41, 0x00, 0xa5, 0x85, 0x13, 0xc1, 0x00, 0xe0,
+0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0xc4, 0x00, 0xa7, 0x2c, 0x87, 0x2c, 0x00,
+0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xf7, 0xf6, 0x18,
+0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x41, 0x00, 0xe8, 0x98, 0x1b, 0x41,
+0x00, 0xeb, 0x9b, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00,
+0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00, 0xf3, 0xf2, 0x23, 0x41, 0x00, 0xa3,
+0x83, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b,
+0x27, 0x41, 0x00, 0xe7, 0x97, 0x28, 0x41, 0x00, 0xee, 0x9e, 0x29, 0x41, 0x00, 0xec, 0x9c, 0x2a,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x2b, 0x41, 0x00, 0xa6, 0x86, 0x2c, 0xc4, 0x00, 0xa8,
+0x15, 0x88, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x41, 0x00, 0xe5, 0x95, 0x2e, 0x41,
+0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00,
+0xad, 0x8d, 0x32, 0x41, 0x00, 0xac, 0x8c, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xae, 0x6a, 0x04, 0x09, 0xc2, 0x01, 0x59, 0x43, 0x2c,
+0xc2, 0x01, 0x53, 0x52, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x68, 0x03, 0xee, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x03, 0x34, 0x01, 0x00, 0x00, 0x00, 0x00,
+0x57, 0x03, 0xbb, 0x02, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x50, 0x03, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x50, 0x03, 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x65, 0x04, 0x53, 0x04, 0x00, 0x00, 0x00, 0x00, 0x65, 0x04, 0x98, 0x03, 0x00, 0x00, 0x00, 0x00,
+0x5a, 0x03, 0x2d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7e, 0x03, 0x01,
+0x01, 0x00, 0x22, 0x04, 0x03, 0x01, 0x00, 0x23, 0x5e, 0x1e, 0x07, 0x01, 0x01, 0x00, 0x26, 0x08,
+0x02, 0x01, 0x00, 0x2f, 0x60, 0x09, 0x01, 0x01, 0x00, 0x28, 0x0a, 0x01, 0x01, 0x00, 0x29, 0x0b,
+0x01, 0x01, 0x00, 0x3d, 0x0c, 0x01, 0x00, 0x27, 0x3f, 0x0d, 0x01, 0x00, 0x2b, 0x2a, 0x10, 0x42,
+0x03, 0x00, 0x00, 0x5c, 0x11, 0x42, 0x03, 0x00, 0x00, 0x7c, 0x15, 0xc4, 0x00, 0x7a, 0x2c, 0x5a,
+0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x21, 0x42, 0x03, 0x00, 0x00, 0x5b, 0x22, 0x42, 0x03,
+0x00, 0x00, 0x5d, 0x27, 0x03, 0x04, 0x5e, 0x5e, 0x00, 0x1e, 0x28, 0x01, 0x00, 0xf5, 0xf5, 0x29,
+0x03, 0x04, 0x5c, 0x7c, 0x00, 0x1c, 0x2b, 0x01, 0x00, 0x40, 0x40, 0x2c, 0xc4, 0x00, 0x79, 0x15,
+0x59, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2f, 0x42, 0x03, 0x00, 0x00, 0x40, 0x30, 0x42,
+0x03, 0x00, 0x00, 0x7b, 0x31, 0x42, 0x03, 0x00, 0x00, 0x7d, 0x33, 0x01, 0x00, 0x2c, 0x3b, 0x34,
+0x01, 0x00, 0x2e, 0x3a, 0x35, 0x03, 0x04, 0x2d, 0x5f, 0x00, 0x1f, 0x56, 0x01, 0x00, 0x3c, 0x3e,
+0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x10, 0x41, 0x00, 0x90, 0x91, 0x11, 0x41, 0x00,
+0x92, 0x93, 0x12, 0x42, 0x00, 0xa8, 0xa9, 0xcf, 0x13, 0x41, 0x00, 0xe1, 0xe2, 0x14, 0x41, 0x00,
+0xe5, 0xe6, 0x15, 0xc4, 0x00, 0xf3, 0x2c, 0xf4, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16,
+0x41, 0x00, 0xe7, 0xe8, 0x17, 0x41, 0x00, 0xb7, 0xb8, 0x18, 0x41, 0x00, 0xd6, 0xd7, 0x19, 0x41,
+0x00, 0xd8, 0xdd, 0x1a, 0x42, 0x00, 0xf5, 0xf6, 0x83, 0x1b, 0x42, 0x00, 0x80, 0x81, 0x82, 0x1e,
+0x41, 0x00, 0xa0, 0xa1, 0x1f, 0x41, 0x00, 0xe3, 0xe4, 0x20, 0x41, 0x00, 0xa6, 0xa7, 0x21, 0x41,
+0x00, 0xaa, 0xab, 0x22, 0x41, 0x00, 0xac, 0xad, 0x23, 0x41, 0x00, 0xb5, 0xb6, 0x24, 0x41, 0x00,
+0x8e, 0x8f, 0x25, 0x41, 0x00, 0xc6, 0xc7, 0x26, 0x41, 0x00, 0xd0, 0xd1, 0x27, 0x42, 0x00, 0xfb,
+0xfc, 0x97, 0x28, 0x42, 0x00, 0x94, 0x95, 0x96, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78,
+0x2b, 0x41, 0x00, 0xe9, 0xea, 0x2c, 0xc4, 0x00, 0x88, 0x15, 0x89, 0x15, 0x00, 0x15, 0x19, 0x15,
+0x00, 0x15, 0x2d, 0x41, 0x00, 0x9a, 0x9b, 0x2e, 0x41, 0x00, 0xa4, 0xa5, 0x2f, 0x41, 0x00, 0xeb,
+0xec, 0x30, 0x41, 0x00, 0xa2, 0xa3, 0x31, 0x41, 0x00, 0xd4, 0xd5, 0x32, 0x42, 0x00, 0xd2, 0xd3,
+0xfd, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xcf, 0x28, 0x01, 0x00, 0xfd, 0xfd, 0x36, 0x04, 0x1f,
+0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x10, 0x41, 0x00, 0x90, 0x91, 0x11, 0x41, 0x00, 0x92, 0x93,
+0x12, 0x41, 0x00, 0xa8, 0xa9, 0x13, 0x41, 0x00, 0xe1, 0xe2, 0x14, 0x41, 0x00, 0xe5, 0xe6, 0x15,
+0xc4, 0x00, 0xf3, 0x2c, 0xf4, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0xe7,
+0xe8, 0x17, 0x41, 0x00, 0xb7, 0xb8, 0x18, 0x41, 0x00, 0xd6, 0xd7, 0x19, 0x41, 0x00, 0xd8, 0xdd,
+0x1a, 0x42, 0x00, 0xf5, 0xf6, 0x83, 0x1b, 0x42, 0x00, 0x80, 0x81, 0x82, 0x1e, 0x41, 0x00, 0xa0,
+0xa1, 0x1f, 0x41, 0x00, 0xe3, 0xe4, 0x20, 0x41, 0x00, 0xa6, 0xa7, 0x21, 0x41, 0x00, 0xaa, 0xab,
+0x22, 0x41, 0x00, 0xac, 0xad, 0x23, 0x41, 0x00, 0xb5, 0xb6, 0x24, 0x41, 0x00, 0x8e, 0x8f, 0x25,
+0x41, 0x00, 0xc6, 0xc7, 0x26, 0x41, 0x00, 0xd0, 0xd1, 0x27, 0x42, 0x00, 0xfb, 0xfc, 0x97, 0x28,
+0x42, 0x00, 0x94, 0x95, 0x96, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x41, 0x00,
+0xe9, 0xea, 0x2c, 0xc4, 0x00, 0x88, 0x15, 0x89, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d,
+0x41, 0x00, 0x9a, 0x9b, 0x2e, 0x41, 0x00, 0xa4, 0xa5, 0x2f, 0x41, 0x00, 0xeb, 0xec, 0x30, 0x41,
+0x00, 0xa2, 0xa3, 0x31, 0x41, 0x00, 0xd4, 0xd5, 0x32, 0x42, 0x00, 0xd2, 0xd3, 0xfd, 0x00, 0x28,
+0x01, 0x00, 0xfd, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x10, 0x41, 0x00,
+0xef, 0x9f, 0x11, 0x41, 0x00, 0xf9, 0xf8, 0x12, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x13, 0xc1, 0x00,
+0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0xc4, 0x00, 0xa7, 0x2c, 0x87, 0x2c,
+0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xf7, 0xf6,
+0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x41, 0x00, 0xe8, 0x98, 0x1b,
+0x41, 0x00, 0xeb, 0x9b, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41,
+0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00, 0xf3, 0xf2, 0x23, 0x41, 0x00,
+0xa3, 0x83, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab,
+0x8b, 0x27, 0x41, 0x00, 0xe7, 0x97, 0x28, 0x41, 0x00, 0xee, 0x9e, 0x29, 0x41, 0x00, 0xec, 0x9c,
+0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x2b, 0x41, 0x00, 0xa6, 0x86, 0x2c, 0xc4, 0x00,
+0xa8, 0x15, 0x88, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x41, 0x00, 0xe5, 0x95, 0x2e,
+0x41, 0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41,
+0x00, 0xad, 0x8d, 0x32, 0x41, 0x00, 0xac, 0x8c, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x28,
+0x10, 0x00, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x10, 0x41, 0x00, 0xef,
+0x9f, 0x11, 0x41, 0x00, 0xf9, 0xf8, 0x12, 0x41, 0x00, 0xa5, 0x85, 0x13, 0xc1, 0x00, 0xe0, 0x00,
+0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0xc4, 0x00, 0xa7, 0x2c, 0x87, 0x2c, 0x00, 0x2c,
+0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xf7, 0xf6, 0x18, 0x41,
+0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x41, 0x00, 0xe8, 0x98, 0x1b, 0x41, 0x00,
+0xeb, 0x9b, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4,
+0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00, 0xf3, 0xf2, 0x23, 0x41, 0x00, 0xa3, 0x83,
+0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x27,
+0x41, 0x00, 0xe7, 0x97, 0x28, 0x41, 0x00, 0xee, 0x9e, 0x29, 0x41, 0x00, 0xec, 0x9c, 0x2a, 0x04,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x2b, 0x41, 0x00, 0xa6, 0x86, 0x2c, 0xc4, 0x00, 0xa8, 0x15,
+0x88, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x41, 0x00, 0xe5, 0x95, 0x2e, 0x41, 0x00,
+0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad,
+0x8d, 0x32, 0x41, 0x00, 0xac, 0x8c, 0x00, 0x28, 0x10, 0x00, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00,
+0x00, 0x00, 0x7f, 0x00, 0xaf, 0x00, 0x00 };
+
+Bit8u layout_keybrd3sys[27122] = {
+0x4b, 0x43, 0x46, 0x00, 0x01, 0x01, 0x2e, 0x48, 0x65, 0x6e, 0x72, 0x69, 0x71, 0x75, 0x65, 0x20,
+0x50, 0x65, 0x72, 0x6f, 0x6e, 0xff, 0x44, 0x4f, 0x53, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x70, 0x61,
+0x67, 0x65, 0x73, 0x20, 0x2d, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x20, 0x23, 0x33,
+0x20, 0x6f, 0x66, 0x20, 0x33, 0x6c, 0x02, 0x04, 0x00, 0x00, 0x41, 0x5a, 0x05, 0x03, 0x00, 0x00,
+0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0xeb, 0x59, 0x01, 0x00, 0x00, 0x00, 0x00,
+0x62, 0xeb, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0xe3, 0x5e, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x62, 0xe3, 0x93, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x03, 0x02, 0x01, 0x00, 0x22, 0x40, 0x04, 0x02, 0x01, 0x00, 0x27, 0x23, 0x05, 0x03, 0x03, 0x00,
+0xa0, 0x24, 0x4e, 0x06, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x02, 0x03, 0x00, 0x00, 0x26, 0x09, 0x02,
+0x01, 0x00, 0x3b, 0x2a, 0x0a, 0x02, 0x01, 0x00, 0x3f, 0x7b, 0x0b, 0x02, 0x01, 0x00, 0x25, 0x7d,
+0x29, 0x03, 0x00, 0x29, 0x28, 0x5d, 0x5b, 0x2b, 0x02, 0x00, 0x2c, 0x7c, 0x5c, 0x33, 0x02, 0x03,
+0x00, 0x00, 0x3c, 0x34, 0x02, 0x03, 0x00, 0x00, 0x3e, 0x35, 0x02, 0x00, 0x2e, 0x3f, 0x2f, 0x00,
+0x07, 0x01, 0x01, 0x00, 0x2c, 0x08, 0x01, 0x01, 0x00, 0x2e, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11,
+0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x41,
+0x00, 0xa5, 0x85, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00,
+0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5,
+0x95, 0x1b, 0x41, 0x00, 0xea, 0x9a, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b,
+0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23,
+0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b,
+0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x2a,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7,
+0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88,
+0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34,
+0x41, 0x00, 0xee, 0x9e, 0x00, 0x07, 0x01, 0x01, 0x00, 0xf3, 0x08, 0x01, 0x01, 0x00, 0xf1, 0x11,
+0x43, 0x00, 0xf5, 0xf4, 0x77, 0x57, 0x1a, 0x41, 0x00, 0xfc, 0xfb, 0x1b, 0x41, 0x00, 0xf7, 0xf6,
+0x27, 0x41, 0x00, 0xfe, 0xfd, 0x28, 0x41, 0x00, 0xf9, 0xf8, 0x33, 0x41, 0x00, 0xf3, 0xf2, 0x34,
+0xc1, 0x00, 0xf1, 0x34, 0xf0, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x07,
+0x01, 0x01, 0x00, 0x2c, 0x08, 0x01, 0x01, 0x00, 0x2e, 0x10, 0x43, 0x00, 0x6a, 0x4a, 0xa9, 0x89,
+0x11, 0x43, 0x00, 0xfd, 0x59, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa,
+0x8a, 0x14, 0x41, 0x00, 0xa5, 0x85, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83,
+0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x43, 0x00, 0x68, 0xfc, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7,
+0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x43, 0x00, 0xf3, 0xf2, 0xea, 0x9a, 0x1e, 0x41, 0x00,
+0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0,
+0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00,
+0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6,
+0x86, 0x28, 0x43, 0x00, 0xf5, 0xf4, 0xed, 0x9d, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a,
+0x2c, 0x43, 0x00, 0xf7, 0xf6, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1,
+0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92,
+0x32, 0x43, 0x00, 0xf9, 0xf8, 0xec, 0x9c, 0x33, 0x43, 0x04, 0xa1, 0x81, 0x00, 0x3e, 0x34, 0xc3,
+0x00, 0xf1, 0x34, 0xf0, 0x00, 0xee, 0x34, 0x9e, 0x34, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x7b, 0x00, 0xec, 0xc1, 0x04, 0x13, 0x00, 0x00, 0x42, 0x4c, 0x2c, 0x00, 0x00, 0x42, 0x59,
+0x2c, 0xcf, 0x01, 0x42, 0x4c, 0x2c, 0xcf, 0x01, 0x42, 0x59, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x00,
+0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x03, 0xca, 0x01, 0x00, 0x00, 0x00, 0x00, 0x51, 0x03,
+0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x04, 0xc8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x04,
+0xdf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x03, 0xb4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x68, 0x03,
+0xd7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0xa4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03,
+0xc9, 0x03, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02,
+0x03, 0x00, 0x00, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00, 0x23, 0x05, 0x02, 0x03, 0x00, 0x00, 0xcf,
+0x07, 0x02, 0x03, 0x00, 0x00, 0x5e, 0x08, 0x02, 0x03, 0x00, 0x00, 0x26, 0x09, 0x02, 0x03, 0x00,
+0x00, 0x24, 0x0a, 0x02, 0x03, 0x00, 0x00, 0xae, 0x0b, 0x02, 0x03, 0x00, 0x00, 0xaf, 0x1a, 0x02,
+0x03, 0x00, 0x00, 0x5b, 0x1b, 0x02, 0x03, 0x00, 0x00, 0x5d, 0x2b, 0x02, 0x03, 0x00, 0x00, 0x7c,
+0x33, 0x02, 0x03, 0x00, 0x00, 0x3c, 0x34, 0x02, 0x03, 0x00, 0x00, 0x3e, 0x35, 0x02, 0x03, 0x00,
+0x00, 0x2f, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x05,
+0x02, 0x05, 0x00, 0x3b, 0xa0, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x0a,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x0b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x10, 0x41, 0x00, 0xa9, 0x89,
+0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x42, 0x00, 0xe3, 0x93, 0xfb, 0x13, 0x41, 0x00, 0xaa, 0x8a,
+0x14, 0x43, 0x00, 0xa5, 0x85, 0xf3, 0xf2, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x43, 0x00, 0xa3,
+0x83, 0xfd, 0xfc, 0x17, 0x43, 0x00, 0xe8, 0x98, 0xe9, 0x99, 0x18, 0x41, 0x00, 0xf7, 0xf6, 0x19,
+0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x01, 0x00, 0x27, 0x27, 0x1e, 0x41,
+0x00, 0xe4, 0x94, 0x1f, 0x43, 0x00, 0xeb, 0x9b, 0xea, 0x9a, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21,
+0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23,
+0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27,
+0x41, 0x00, 0xa6, 0x86, 0x28, 0x43, 0x00, 0xed, 0x9d, 0xf5, 0xf4, 0x29, 0xc1, 0x00, 0xf1, 0x29,
+0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c,
+0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41,
+0x00, 0xac, 0x8c, 0x30, 0x43, 0x00, 0xf9, 0xf8, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32,
+0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01,
+0x00, 0x2e, 0x2c, 0x00, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfb,
+0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x05, 0x02,
+0x01, 0x00, 0x3b, 0xfb, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x0a, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x0b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11,
+0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x43,
+0x00, 0xa5, 0x85, 0xf3, 0xf2, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x43, 0x00, 0xa3, 0x83, 0xfd,
+0xfc, 0x17, 0x43, 0x00, 0xe8, 0x98, 0xe9, 0x99, 0x18, 0x41, 0x00, 0xf7, 0xf6, 0x19, 0x41, 0x00,
+0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x01, 0x00, 0x27, 0x27, 0x1e, 0x41, 0x00, 0xe4,
+0x94, 0x1f, 0x43, 0x00, 0xeb, 0x9b, 0xea, 0x9a, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00,
+0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41,
+0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00,
+0xa6, 0x86, 0x28, 0x43, 0x00, 0xed, 0x9d, 0xf5, 0xf4, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00,
+0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0x41, 0x00,
+0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac,
+0x8c, 0x30, 0x43, 0x00, 0xf9, 0xf8, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00,
+0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01, 0x00, 0x2e,
+0x2c, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0xfb, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b,
+0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xef, 0x05, 0x02, 0x05, 0x00, 0x3b,
+0xa0, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41, 0x00, 0xbd, 0xbe,
+0x11, 0x41, 0x00, 0xa4, 0xa5, 0x12, 0x42, 0x00, 0xe7, 0xe8, 0xcf, 0x13, 0x41, 0x00, 0xc6, 0xc7,
+0x14, 0x43, 0x00, 0xa8, 0xa9, 0x86, 0x87, 0x15, 0x41, 0x00, 0xd4, 0xd5, 0x16, 0x41, 0x00, 0xac,
+0xad, 0x17, 0x43, 0x00, 0xf5, 0xf6, 0xf9, 0xfa, 0x18, 0x41, 0x00, 0x98, 0x99, 0x19, 0x41, 0x00,
+0xf3, 0xf4, 0x1a, 0x41, 0x00, 0xb5, 0xb6, 0x1b, 0x01, 0x00, 0x27, 0x27, 0x1e, 0x41, 0x00, 0xaa,
+0xab, 0x1f, 0x43, 0x00, 0xf1, 0xf2, 0x9e, 0x9f, 0x20, 0x41, 0x00, 0xeb, 0xec, 0x21, 0x41, 0x00,
+0xa0, 0xa1, 0x22, 0x41, 0x00, 0xd8, 0xdd, 0x23, 0x41, 0x00, 0xe1, 0xe2, 0x24, 0x41, 0x00, 0xd6,
+0xd7, 0x25, 0x41, 0x00, 0xd0, 0xd1, 0x26, 0x41, 0x00, 0xa6, 0xa7, 0x27, 0x41, 0x00, 0xe9, 0xea,
+0x28, 0x41, 0x00, 0xf7, 0xf8, 0x29, 0x41, 0x00, 0x84, 0x85, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x7c, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0xc1, 0x00, 0xde, 0x2c, 0xe0, 0x00, 0x2d, 0x41,
+0x00, 0xfb, 0xfc, 0x2e, 0x41, 0x00, 0xe3, 0xe4, 0x2f, 0x41, 0x00, 0xd2, 0xd3, 0x30, 0x43, 0x00,
+0x8a, 0x8b, 0xb7, 0xb8, 0x31, 0x41, 0x00, 0xe5, 0xe6, 0x32, 0x41, 0x00, 0xed, 0xee, 0x33, 0x41,
+0x00, 0xa2, 0xa3, 0x34, 0x41, 0x00, 0x9c, 0x9d, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x00, 0x05, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x12, 0x42, 0x03, 0x00, 0x00, 0xcf, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x7d, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xef, 0x05, 0x01, 0x01,
+0x00, 0x3b, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41, 0x00, 0xbd,
+0xbe, 0x11, 0x41, 0x00, 0xa4, 0xa5, 0x12, 0x41, 0x00, 0xe7, 0xe8, 0x13, 0x41, 0x00, 0xc6, 0xc7,
+0x14, 0x43, 0x00, 0xa8, 0xa9, 0x86, 0x87, 0x15, 0x41, 0x00, 0xd4, 0xd5, 0x16, 0x41, 0x00, 0xac,
+0xad, 0x17, 0x43, 0x00, 0xf5, 0xf6, 0xf9, 0xfa, 0x18, 0x41, 0x00, 0x98, 0x99, 0x19, 0x41, 0x00,
+0xf3, 0xf4, 0x1a, 0x41, 0x00, 0xb5, 0xb6, 0x1b, 0x01, 0x00, 0x27, 0x27, 0x1e, 0x41, 0x00, 0xaa,
+0xab, 0x1f, 0x43, 0x00, 0xf1, 0xf2, 0x9e, 0x9f, 0x20, 0x41, 0x00, 0xeb, 0xec, 0x21, 0x41, 0x00,
+0xa0, 0xa1, 0x22, 0x41, 0x00, 0xd8, 0xdd, 0x23, 0x41, 0x00, 0xe1, 0xe2, 0x24, 0x41, 0x00, 0xd6,
+0xd7, 0x25, 0x41, 0x00, 0xd0, 0xd1, 0x26, 0x41, 0x00, 0xa6, 0xa7, 0x27, 0x41, 0x00, 0xe9, 0xea,
+0x28, 0x41, 0x00, 0xf7, 0xf8, 0x29, 0x41, 0x00, 0x84, 0x85, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x7e, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0xc1, 0x00, 0xde, 0x2c, 0xe0, 0x00, 0x2d, 0x41,
+0x00, 0xfb, 0xfc, 0x2e, 0x41, 0x00, 0xe3, 0xe4, 0x2f, 0x41, 0x00, 0xd2, 0xd3, 0x30, 0x43, 0x00,
+0x8a, 0x8b, 0xb7, 0xb8, 0x31, 0x41, 0x00, 0xe5, 0xe6, 0x32, 0x41, 0x00, 0xed, 0xee, 0x33, 0x41,
+0x00, 0xa2, 0xa3, 0x34, 0x41, 0x00, 0x9c, 0x9d, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x00, 0x36, 0x04,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x43, 0x08, 0x02, 0x04, 0x00, 0x00, 0x42, 0x4e, 0x02,
+0x04, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x75, 0x44, 0x00, 0x6e,
+0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x41, 0x00, 0x26, 0x31, 0x03, 0x42, 0x00, 0x82, 0x32, 0x7e, 0x04, 0x42,
+0x00, 0x22, 0x33, 0x23, 0x05, 0x42, 0x01, 0xcb, 0x34, 0x7b, 0x06, 0x44, 0x08, 0x28, 0x35, 0x5b,
+0x00, 0x1b, 0x07, 0x44, 0x08, 0x2d, 0x36, 0x7c, 0x00, 0x1f, 0x08, 0x42, 0x04, 0x8a, 0x37, 0xc8,
+0x09, 0x44, 0x08, 0x5f, 0x38, 0x5c, 0x00, 0x1c, 0x0a, 0x42, 0x00, 0x87, 0x39, 0x5e, 0x0b, 0x42,
+0x00, 0x85, 0x30, 0x40, 0x0c, 0x44, 0x08, 0x29, 0xf8, 0x5d, 0x00, 0x1d, 0x0d, 0x42, 0x03, 0x00,
+0x00, 0x7d, 0x10, 0xc5, 0x00, 0x61, 0x1e, 0x41, 0x1e, 0x91, 0x1e, 0x92, 0x1e, 0x01, 0x1e, 0x00,
+0x1e, 0x11, 0xc5, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x84, 0x2c, 0x8e, 0x2c, 0x1a, 0x2c, 0x00, 0x2c,
+0x12, 0x43, 0x00, 0x65, 0x45, 0xc6, 0xc7, 0x13, 0x03, 0x03, 0x00, 0x00, 0x94, 0x99, 0x15, 0x41,
+0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x43, 0x00, 0x69, 0x49, 0xe1, 0xee, 0x18,
+0x43, 0x00, 0x6f, 0x4f, 0xe4, 0xe5, 0x19, 0x03, 0x03, 0x00, 0x00, 0xf7, 0xef, 0x1a, 0x44, 0x0f,
+0xc9, 0xca, 0x00, 0x00, 0x1e, 0x1b, 0x43, 0x06, 0x24, 0xa0, 0x00, 0xd5, 0x1e, 0xc5, 0x00, 0x71,
+0x10, 0x51, 0x10, 0x00, 0x10, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x1f, 0x03, 0x03, 0x00, 0x00,
+0xf9, 0xdd, 0x20, 0x03, 0x03, 0x00, 0x00, 0xa8, 0xad, 0x26, 0x03, 0x03, 0x00, 0x00, 0xab, 0xac,
+0x27, 0xc5, 0x00, 0x6d, 0x32, 0x4d, 0x32, 0x00, 0x32, 0x00, 0x32, 0x0d, 0x32, 0x00, 0x32, 0x28,
+0x41, 0x02, 0x97, 0xcc, 0x29, 0x41, 0x00, 0x9e, 0x9f, 0x2b, 0x43, 0x00, 0x9b, 0x9d, 0x2a, 0xe6,
+0x2c, 0xc5, 0x00, 0x77, 0x11, 0x57, 0x11, 0x00, 0x11, 0x00, 0x11, 0x17, 0x11, 0x00, 0x11, 0x2e,
+0x41, 0x00, 0x63, 0x43, 0x31, 0x43, 0x00, 0x6e, 0x4e, 0xa4, 0xa5, 0x32, 0x41, 0x00, 0x2c, 0x3f,
+0x33, 0x43, 0x00, 0x3b, 0x2e, 0x3c, 0xae, 0x34, 0x43, 0x00, 0x3a, 0x2f, 0x3e, 0xaf, 0x35, 0x41,
+0x00, 0x21, 0xf5, 0x39, 0x00, 0x00, 0x20, 0x56, 0x41, 0x00, 0x3c, 0x3e, 0x00, 0x60, 0x0f, 0x61,
+0x85, 0x65, 0x8a, 0x9e, 0xd0, 0x69, 0x8d, 0x6f, 0x95, 0x9b, 0xfa, 0x75, 0x97, 0x41, 0xb7, 0x45,
+0xd4, 0x9f, 0xd1, 0x49, 0xde, 0x4f, 0xe3, 0x9d, 0xfb, 0x55, 0xeb, 0x20, 0x60, 0x5e, 0x0b, 0x61,
+0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f,
+0xe2, 0x55, 0xea, 0x20, 0x5e, 0x22, 0x09, 0x65, 0x89, 0x69, 0x8b, 0x75, 0x81, 0x79, 0x98, 0x45,
+0xd3, 0x49, 0xd8, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x27, 0x13, 0x61, 0xa0, 0x65, 0x82, 0x9e,
+0xf1, 0x69, 0xa1, 0x6f, 0xa2, 0x9b, 0xec, 0x75, 0xa3, 0x6e, 0x9c, 0x63, 0x87, 0x41, 0xb5, 0x45,
+0x90, 0x9f, 0xf2, 0x49, 0xd6, 0x4f, 0xe0, 0x9d, 0xed, 0x55, 0xe9, 0x4e, 0xcf, 0x43, 0x80, 0x20,
+0x27, 0x25, 0x0f, 0x61, 0x86, 0x65, 0xa6, 0x9e, 0xe7, 0x69, 0xa9, 0x6f, 0xbd, 0x9b, 0xfc, 0x75,
+0xf4, 0x41, 0x8f, 0x45, 0xa7, 0x9f, 0xe8, 0x49, 0xb8, 0x4f, 0xbe, 0x9d, 0xfd, 0x55, 0xf6, 0x20,
+0x25, 0x00, 0xcd, 0x4b, 0x03, 0x13, 0x00, 0x00, 0x45, 0x54, 0x2c, 0x00, 0x00, 0x45, 0x45, 0x2c,
+0xc6, 0x01, 0x45, 0x54, 0x2c, 0xc6, 0x01, 0x45, 0x45, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x18, 0x01, 0x40, 0x01, 0x00, 0x00, 0x52, 0x03, 0xd1,
+0x02, 0xd6, 0x02, 0x00, 0x00, 0x5a, 0x03, 0xc0, 0x02, 0xd6, 0x02, 0x00, 0x00, 0x5b, 0x03, 0x31,
+0x02, 0x53, 0x02, 0x00, 0x00, 0x5c, 0x04, 0xb5, 0x01, 0xc4, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x01, 0x00, 0x22, 0x40, 0x40, 0x04, 0x03, 0x03, 0x00,
+0x00, 0x9c, 0x9c, 0x05, 0x03, 0x01, 0x00, 0xcf, 0x24, 0x24, 0x07, 0x01, 0x01, 0x00, 0x26, 0x08,
+0x03, 0x01, 0x00, 0x2f, 0x7b, 0x7b, 0x09, 0x04, 0x01, 0x00, 0x28, 0x5b, 0x5b, 0x1b, 0x0a, 0x04,
+0x01, 0x00, 0x29, 0x5d, 0x5d, 0x1d, 0x0b, 0x03, 0x01, 0x00, 0x3d, 0x7d, 0x7d, 0x0c, 0x04, 0x00,
+0x2b, 0x3f, 0x5c, 0x5c, 0x1c, 0x0d, 0x01, 0x03, 0xcb, 0xc8, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15,
+0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41,
+0x00, 0x6f, 0x4f, 0x1a, 0x41, 0x00, 0x81, 0x9a, 0x1b, 0x43, 0x00, 0xe4, 0xe5, 0xf5, 0xf5, 0x1e,
+0x41, 0x00, 0x61, 0x41, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x27, 0x41, 0x00, 0x94, 0x99, 0x28, 0x44,
+0x04, 0x84, 0x8e, 0xca, 0x5e, 0x1e, 0x29, 0x01, 0x03, 0xcc, 0xc9, 0x2b, 0x03, 0x00, 0x27, 0x2a,
+0xab, 0xab, 0x2c, 0x41, 0x00, 0x7a, 0x5a, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41, 0x00, 0x6e,
+0x4e, 0x33, 0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34, 0x02, 0x01, 0x00, 0x3a, 0x3e, 0x35, 0x04, 0x00,
+0x2d, 0x5f, 0x7c, 0x7c, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56, 0x03, 0x00, 0x3c, 0x3e, 0x7c, 0x7c,
+0x00, 0x05, 0x01, 0x01, 0x00, 0x9f, 0x13, 0x41, 0x00, 0x72, 0x52, 0x1f, 0x43, 0x03, 0x00, 0x00,
+0xd5, 0xbe, 0x22, 0x41, 0x00, 0x67, 0x47, 0x25, 0x41, 0x00, 0x6b, 0x4b, 0x26, 0x41, 0x00, 0x6c,
+0x4c, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xd8, 0xcf, 0x00, 0x60, 0x0b, 0x61, 0x83, 0x65, 0x89, 0x69,
+0x8c, 0x6f, 0x93, 0x75, 0xd7, 0x41, 0xa0, 0x45, 0xed, 0x49, 0xa1, 0x4f, 0xe2, 0x55, 0xc7, 0x20,
+0x60, 0x7e, 0x03, 0x6f, 0xe4, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x13, 0x61, 0xd0, 0x65, 0xd2, 0x69,
+0xd4, 0x75, 0xd6, 0x67, 0x85, 0x6b, 0xe9, 0x6c, 0xeb, 0x6e, 0xec, 0x72, 0x8b, 0x41, 0xb5, 0x45,
+0xb7, 0x49, 0xbd, 0x55, 0xc6, 0x47, 0x95, 0x4b, 0xe8, 0x4c, 0xea, 0x4e, 0xee, 0x52, 0x8a, 0x20,
+0x5e, 0xef, 0x0d, 0x63, 0x87, 0x65, 0x82, 0x6e, 0xe7, 0x6f, 0xa2, 0x73, 0x98, 0x7a, 0xa5, 0x43,
+0x80, 0x45, 0x90, 0x4e, 0xe3, 0x4f, 0xe0, 0x53, 0x97, 0x5a, 0x8d, 0x20, 0xef, 0x27, 0x07, 0x63,
+0xd1, 0x73, 0xd5, 0x7a, 0xd8, 0x43, 0xb6, 0x53, 0xbe, 0x5a, 0xcf, 0x20, 0x27, 0x00, 0x1f, 0x43,
+0x03, 0x00, 0x00, 0xd0, 0xd1, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xe7, 0xe8, 0x00, 0x60, 0x0b, 0x61,
+0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f,
+0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e,
+0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75,
+0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0f, 0x61,
+0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x43,
+0x80, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0x76, 0x05, 0x73,
+0xd0, 0x7a, 0xe7, 0x53, 0xd1, 0x5a, 0xe8, 0x20, 0x76, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5,
+0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xf9, 0xdd, 0x2b, 0x03, 0x0f,
+0x00, 0x00, 0xa0, 0xa0, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xf7, 0xef, 0x00, 0x60, 0x0b, 0x61, 0x85,
+0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3,
+0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5,
+0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96,
+0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x27, 0x0f, 0x61, 0xa0,
+0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x43, 0x80,
+0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0x27, 0x76, 0x05, 0x73, 0xf9,
+0x7a, 0xf7, 0x53, 0xdd, 0x5a, 0xef, 0x20, 0x76, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x12,
+0x42, 0x03, 0x00, 0x00, 0xd5, 0x29, 0x00, 0x01, 0xa0, 0x00, 0x29, 0x00, 0x01, 0xa0, 0x00, 0x60,
+0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49,
+0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41,
+0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f,
+0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef,
+0x0f, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41,
+0xb5, 0x43, 0x80, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0x00,
+0x86, 0xe0, 0x01, 0x04, 0x00, 0x00, 0x46, 0x4f, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xef, 0x00,
+0xfc, 0x00, 0x00, 0x00, 0x5d, 0x03, 0x77, 0x01, 0x98, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00,
+0x22, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00, 0x9c, 0x05, 0x02, 0x01, 0x00, 0xcf, 0x24, 0x07, 0x01,
+0x01, 0x00, 0x26, 0x08, 0x02, 0x01, 0x00, 0x2f, 0x7b, 0x09, 0x03, 0x01, 0x00, 0x28, 0x5b, 0x1b,
+0x0a, 0x03, 0x01, 0x00, 0x29, 0x5d, 0x1d, 0x0b, 0x02, 0x01, 0x00, 0x3d, 0x7d, 0x0c, 0x01, 0x00,
+0x2b, 0x3f, 0x0d, 0x02, 0x03, 0xcb, 0xc8, 0x7c, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00,
+0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f,
+0x4f, 0x1a, 0x42, 0x04, 0x86, 0x8f, 0xcc, 0x1b, 0x42, 0x04, 0xd0, 0xd1, 0xc9, 0x1e, 0x41, 0x00,
+0x61, 0x41, 0x27, 0x41, 0x00, 0x91, 0x92, 0x28, 0x43, 0x04, 0x9b, 0x9d, 0xca, 0x1e, 0x29, 0x01,
+0x00, 0xab, 0xf5, 0x2b, 0x01, 0x00, 0x27, 0x2a, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41, 0x00,
+0x6e, 0x4e, 0x32, 0x42, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34, 0x02,
+0x01, 0x00, 0x3a, 0x3e, 0x35, 0x03, 0x00, 0x2d, 0x5f, 0x5c, 0x1f, 0x39, 0x00, 0x00, 0x20, 0x56,
+0x03, 0x00, 0x3c, 0x3e, 0x5c, 0x1c, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x12, 0x42, 0x03,
+0x00, 0x00, 0xd5, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97,
+0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6,
+0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83,
+0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2,
+0x55, 0xea, 0x20, 0x5e, 0xef, 0x0f, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2,
+0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x43, 0x80, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9,
+0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81,
+0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00, 0x05,
+0x01, 0x03, 0x00, 0xa0, 0x0d, 0x00, 0x01, 0xca, 0x1a, 0x42, 0x07, 0x00, 0x00, 0xcb, 0x1b, 0x42,
+0x00, 0x8c, 0x8b, 0x7e, 0x28, 0x42, 0x07, 0x00, 0x00, 0xc9, 0x29, 0x01, 0x00, 0xab, 0x15, 0x00,
+0x60, 0x03, 0x61, 0x85, 0x65, 0x8a, 0x20, 0x60, 0x5e, 0x05, 0x61, 0x83, 0x65, 0x88, 0x6f, 0x93,
+0x75, 0x96, 0x20, 0x5e, 0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3,
+0x79, 0x98, 0x41, 0xa4, 0x45, 0x90, 0x49, 0xa5, 0x4f, 0xa6, 0x55, 0xa7, 0x59, 0x97, 0x20, 0x27,
+0x22, 0x08, 0x61, 0x84, 0x65, 0x89, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a,
+0x20, 0x22, 0x00, 0xfb, 0x5e, 0x01, 0x04, 0x00, 0x00, 0x48, 0x59, 0x03, 0x03, 0x00, 0x00, 0x00,
+0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x03, 0x50, 0x01, 0x00, 0x00, 0x00, 0x00, 0x83,
+0x03, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+0x43, 0x00, 0xa5, 0xa4, 0x31, 0x21, 0x03, 0x43, 0x00, 0xe7, 0xe6, 0x32, 0x40, 0x04, 0x43, 0x00,
+0x93, 0x92, 0x33, 0x23, 0x05, 0x43, 0x00, 0xa1, 0xa0, 0x34, 0x24, 0x06, 0x43, 0x00, 0xa9, 0xa8,
+0x35, 0x25, 0x07, 0x42, 0x00, 0xe9, 0xe8, 0x36, 0x08, 0xc3, 0x00, 0xf1, 0x08, 0xf0, 0x00, 0x37,
+0x7e, 0x26, 0x7e, 0x09, 0x43, 0x00, 0xf3, 0xf2, 0x38, 0x2a, 0x0a, 0x42, 0x00, 0x8d, 0x8c, 0x39,
+0x0b, 0x42, 0x00, 0xa3, 0xa2, 0x30, 0x0d, 0x03, 0x00, 0x28, 0x29, 0x3d, 0x2b, 0x10, 0x41, 0x00,
+0xf7, 0xf6, 0x11, 0x41, 0x00, 0x83, 0x82, 0x12, 0x41, 0x00, 0xeb, 0xea, 0x13, 0x41, 0x00, 0xa7,
+0xa6, 0x14, 0xc1, 0x00, 0xe1, 0x14, 0xe0, 0x00, 0x15, 0x41, 0x00, 0xf5, 0xf4, 0x16, 0x41, 0x00,
+0x9d, 0x9c, 0x17, 0x41, 0x00, 0x8f, 0x8e, 0x18, 0x41, 0x00, 0x91, 0x90, 0x19, 0x41, 0x00, 0x9b,
+0x9a, 0x1a, 0x43, 0x00, 0xfb, 0xfa, 0x5b, 0x7b, 0x1b, 0x43, 0x00, 0xfd, 0xfc, 0x5d, 0x7d, 0x1e,
+0x41, 0x00, 0xed, 0xec, 0x1f, 0x41, 0x00, 0x85, 0x84, 0x20, 0x41, 0x00, 0x89, 0x88, 0x21, 0x41,
+0x00, 0x81, 0x80, 0x22, 0x41, 0x00, 0xab, 0xaa, 0x23, 0x41, 0x00, 0x95, 0x94, 0x24, 0x41, 0x00,
+0xef, 0xee, 0x25, 0x41, 0x00, 0x9f, 0x9e, 0x26, 0x41, 0x00, 0xe5, 0xe4, 0x27, 0x01, 0x00, 0x3a,
+0xdd, 0x28, 0x02, 0x00, 0x27, 0xfe, 0x22, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b,
+0x01, 0x00, 0xde, 0x15, 0x2c, 0x41, 0x00, 0x87, 0x86, 0x2d, 0x41, 0x00, 0xe3, 0xe2, 0x2e, 0x41,
+0x00, 0x8b, 0x8a, 0x2f, 0x41, 0x00, 0x97, 0x96, 0x30, 0x41, 0x00, 0xf9, 0xf8, 0x31, 0x41, 0x00,
+0x99, 0x98, 0x32, 0x41, 0x00, 0xad, 0xac, 0x33, 0x02, 0x01, 0x00, 0xae, 0x3c, 0x34, 0x02, 0x01,
+0x00, 0xaf, 0x3e, 0x35, 0x03, 0x00, 0xdf, 0xdc, 0x2f, 0x3f, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00,
+0x00, 0x00, 0x79, 0x00, 0x2d, 0x60, 0x03, 0x04, 0x00, 0x00, 0x4b, 0x41, 0x07, 0x03, 0x00, 0x00,
+0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xe9, 0x78, 0x01, 0x00, 0x00, 0x00, 0x00,
+0xb5, 0xe9, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xed, 0x55, 0x02, 0x00, 0x00, 0x00, 0x00,
+0xb5, 0xed, 0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x38, 0x75, 0x4c, 0x03, 0x00, 0x00, 0x00, 0x00,
+0x38, 0x75, 0x5e, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x03, 0x02, 0x03, 0x00, 0x00, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00, 0x23, 0x07, 0x02, 0x03, 0x00,
+0x00, 0x5e, 0x08, 0x02, 0x03, 0x00, 0x00, 0x26, 0x09, 0x02, 0x03, 0x00, 0x00, 0x24, 0x1a, 0x02,
+0x03, 0x00, 0x00, 0x5b, 0x1b, 0x02, 0x03, 0x00, 0x00, 0x5d, 0x2b, 0x02, 0x03, 0x00, 0x00, 0x7c,
+0x33, 0x02, 0x03, 0x00, 0x00, 0x3c, 0x34, 0x02, 0x03, 0x00, 0x00, 0x3e, 0x35, 0x02, 0x03, 0x00,
+0x00, 0x2f, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0x25, 0x05, 0x01, 0x01,
+0x00, 0x3b, 0x06, 0x01, 0x01, 0x00, 0x3a, 0x07, 0x01, 0x01, 0x00, 0x2c, 0x08, 0x01, 0x01, 0x00,
+0x2e, 0x10, 0x01, 0x00, 0x96, 0x96, 0x11, 0x01, 0x00, 0x9a, 0x9a, 0x12, 0x01, 0x00, 0x93, 0x93,
+0x13, 0x01, 0x00, 0x89, 0x89, 0x14, 0x01, 0x00, 0x84, 0x84, 0x15, 0x01, 0x00, 0x8c, 0x8c, 0x16,
+0x01, 0x00, 0x82, 0x82, 0x17, 0x01, 0x00, 0x98, 0x98, 0x18, 0x01, 0x00, 0x9c, 0x9c, 0x19, 0x01,
+0x00, 0x86, 0x86, 0x1a, 0x01, 0x00, 0x9e, 0x9e, 0x1b, 0x01, 0x00, 0x9b, 0x9b, 0x1e, 0x01, 0x00,
+0x94, 0x94, 0x1f, 0x01, 0x00, 0x87, 0x87, 0x20, 0x01, 0x00, 0x85, 0x85, 0x21, 0x01, 0x00, 0x80,
+0x80, 0x22, 0x01, 0x00, 0x8e, 0x8e, 0x23, 0x01, 0x00, 0x90, 0x90, 0x24, 0x01, 0x00, 0x8d, 0x8d,
+0x25, 0x01, 0x00, 0x8a, 0x8a, 0x26, 0x01, 0x00, 0x83, 0x83, 0x27, 0x01, 0x00, 0x8f, 0x8f, 0x28,
+0x01, 0x00, 0x97, 0x97, 0x29, 0x01, 0x00, 0xa2, 0xa2, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x78, 0x2b, 0x01, 0x00, 0xa0, 0xa0, 0x2c, 0x01, 0x00, 0x9d, 0x9d, 0x2d, 0x01, 0x00, 0x99, 0x99,
+0x2e, 0x01, 0x00, 0x91, 0x91, 0x2f, 0x01, 0x00, 0x8b, 0x8b, 0x30, 0x01, 0x00, 0x88, 0x88, 0x31,
+0x01, 0x00, 0x92, 0x92, 0x32, 0x01, 0x00, 0x95, 0x95, 0x33, 0x01, 0x00, 0x81, 0x81, 0x34, 0x01,
+0x00, 0x9f, 0x9f, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x03, 0x01, 0x01,
+0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0x25, 0x05, 0x01, 0x01, 0x00, 0x3b, 0x06, 0x01, 0x01, 0x00,
+0x3a, 0x07, 0x01, 0x01, 0x00, 0x2c, 0x08, 0x01, 0x01, 0x00, 0x2e, 0x10, 0x41, 0x00, 0xe6, 0x96,
+0x11, 0x41, 0x00, 0xea, 0x9a, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xa9, 0x89, 0x14,
+0x41, 0x00, 0xa4, 0x84, 0x15, 0x41, 0x00, 0xac, 0x8c, 0x16, 0x41, 0x00, 0xa2, 0x82, 0x17, 0x41,
+0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xec, 0x9c, 0x19, 0x41, 0x00, 0xa6, 0x86, 0x1a, 0x41, 0x00,
+0xee, 0x9e, 0x1b, 0x41, 0x00, 0xeb, 0x9b, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xa7,
+0x87, 0x20, 0x41, 0x00, 0xa5, 0x85, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xae, 0x8e,
+0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xad, 0x8d, 0x25, 0x41, 0x00, 0xaa,
+0x8a, 0x26, 0x41, 0x00, 0xa3, 0x83, 0x27, 0x41, 0x00, 0xaf, 0x8f, 0x28, 0x41, 0x00, 0xe7, 0x97,
+0x29, 0x10, 0x00, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0xc1, 0x00, 0xf0,
+0x2b, 0xf0, 0x00, 0x2c, 0x41, 0x00, 0xed, 0x9d, 0x2d, 0x41, 0x00, 0xe9, 0x99, 0x2e, 0x41, 0x00,
+0xe1, 0x91, 0x2f, 0x41, 0x00, 0xab, 0x8b, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2,
+0x92, 0x32, 0x41, 0x00, 0xe5, 0x95, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xef, 0x9f,
+0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04,
+0x01, 0x01, 0x00, 0x25, 0x05, 0x01, 0x01, 0x00, 0x3b, 0x06, 0x01, 0x01, 0x00, 0x3a, 0x07, 0x01,
+0x01, 0x00, 0x2c, 0x08, 0x01, 0x01, 0x00, 0x2e, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x43, 0x00,
+0xe6, 0x96, 0xf6, 0xf7, 0x12, 0x43, 0x00, 0xe3, 0x93, 0xd3, 0xd4, 0x13, 0x43, 0x00, 0xaa, 0x8a,
+0xd1, 0xd2, 0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x43, 0x00,
+0xa3, 0x83, 0xb5, 0xb6, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x43,
+0x00, 0xa7, 0x87, 0xcf, 0xd0, 0x1a, 0x43, 0x00, 0xe5, 0x95, 0xd5, 0xd6, 0x1b, 0x43, 0x00, 0xea,
+0x9a, 0xf4, 0xf5, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00,
+0xa2, 0x82, 0x21, 0x43, 0x00, 0xa0, 0x80, 0xfa, 0xfb, 0x22, 0x43, 0x00, 0xaf, 0x8f, 0xd7, 0xd8,
+0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab,
+0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x43, 0x00, 0xa6, 0x86, 0xb7, 0xb8, 0x28, 0x42, 0x00,
+0xed, 0x9d, 0xfe, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x7c, 0x2c, 0x43, 0x00, 0xef, 0x9f, 0xc6, 0xc7, 0x2d, 0x43, 0x00, 0xe7, 0x97, 0xbd, 0xbe,
+0x2e, 0x43, 0x00, 0xe1, 0x91, 0xf8, 0xf9, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8,
+0x88, 0x31, 0x43, 0x00, 0xe2, 0x92, 0xf2, 0xf3, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00,
+0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0xbc, 0x6f, 0x01, 0x04, 0x00, 0x00, 0x4b, 0x4b, 0x03,
+0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xe3, 0x32, 0x01, 0x00,
+0x00, 0x00, 0x00, 0x28, 0xe3, 0x44, 0x00, 0x41, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x22, 0x40, 0x04, 0x02, 0x01, 0x00, 0xfc, 0x23, 0x05,
+0x02, 0x05, 0x00, 0x3b, 0xa0, 0x07, 0x02, 0x01, 0x00, 0x3a, 0x5e, 0x08, 0x02, 0x01, 0x00, 0x3f,
+0x26, 0x09, 0x02, 0x03, 0x00, 0x00, 0x24, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6,
+0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5, 0x85,
+0xfd, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98,
+0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x42, 0x00, 0xe5, 0x95, 0x5b,
+0x1b, 0x42, 0x00, 0xea, 0x9a, 0x5d, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b,
+0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23,
+0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b,
+0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29,
+0x83, 0x03, 0xc8, 0x29, 0x00, 0x29, 0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x78, 0x2b, 0x02, 0x01, 0x00, 0x2f, 0x7c, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00,
+0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8,
+0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x42, 0x00, 0xa1, 0x81,
+0x3c, 0x34, 0x42, 0x00, 0xee, 0x9e, 0x3e, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x39, 0x00, 0x00, 0x20,
+0x00, 0x12, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00,
+0x60, 0x13, 0xa0, 0xd1, 0xa3, 0xd3, 0xa8, 0xf5, 0xa9, 0xf7, 0xaa, 0xd5, 0xad, 0xd7, 0xae, 0xf3,
+0xe3, 0xdd, 0xe5, 0xdf, 0x80, 0xd0, 0x83, 0xd2, 0x88, 0xf4, 0x89, 0xf6, 0x8a, 0xd4, 0x8d, 0xd6,
+0x8e, 0xf2, 0x93, 0xdc, 0x95, 0xde, 0x20, 0x60, 0x00, 0x19, 0x44, 0x01, 0x04, 0x00, 0x00, 0x4b,
+0x59, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xe3, 0x30,
+0x01, 0x00, 0x00, 0x00, 0x00, 0x28, 0xe3, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x22, 0x40, 0x04, 0x02, 0x01, 0x00, 0xfc,
+0x23, 0x05, 0x02, 0x05, 0x00, 0x3b, 0xa0, 0x07, 0x02, 0x01, 0x00, 0x3a, 0x5e, 0x08, 0x02, 0x01,
+0x00, 0x3f, 0x26, 0x09, 0x02, 0x03, 0x00, 0x00, 0x24, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41,
+0x00, 0xe6, 0x96, 0x12, 0x43, 0x00, 0xe3, 0x93, 0xdd, 0xdc, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14,
+0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15, 0x43, 0x00, 0xad, 0x8d, 0xd7, 0xd6, 0x16, 0x41, 0x00, 0xa3,
+0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87,
+0x1a, 0x42, 0x00, 0xe5, 0x95, 0x5b, 0x1b, 0x42, 0x00, 0xea, 0x9a, 0x5d, 0x1e, 0x41, 0x00, 0xe4,
+0x94, 0x1f, 0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80,
+0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x43, 0x00, 0xae,
+0x8e, 0xf3, 0xf2, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00,
+0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x04,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x02, 0x01, 0x00, 0x2f, 0x7c, 0x2c, 0x41, 0x00, 0xef,
+0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c,
+0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33,
+0x42, 0x00, 0xa1, 0x81, 0x3c, 0x34, 0x42, 0x00, 0xee, 0x9e, 0x3e, 0x35, 0x01, 0x00, 0x2e, 0x2c,
+0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00,
+0x39, 0x60, 0x05, 0x09, 0x00, 0x00, 0x4c, 0x54, 0x2c, 0xd4, 0x00, 0x4c, 0x54, 0x0b, 0x03, 0x00,
+0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0xc4, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x02, 0x03, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x20, 0x01, 0x00, 0x00, 0x00,
+0x00, 0x06, 0x03, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x03, 0x50, 0x01, 0xc4, 0x01, 0x00,
+0x00, 0x0a, 0x03, 0x55, 0x02, 0xc7, 0x02, 0x00, 0x00, 0x03, 0x03, 0x58, 0x03, 0x00, 0x00, 0x00,
+0x00, 0x03, 0x03, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x57, 0x04, 0x00, 0x00, 0x00,
+0x00, 0x04, 0x03, 0x8d, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x02, 0x03, 0x03, 0x00, 0x00, 0x31, 0x21, 0x03, 0x03, 0x03, 0x00, 0x00, 0x32, 0x40, 0x04,
+0x03, 0x03, 0x00, 0x00, 0x33, 0x23, 0x05, 0x03, 0x03, 0x00, 0x00, 0x34, 0x24, 0x06, 0x03, 0x03,
+0x00, 0x00, 0x35, 0x25, 0x07, 0x03, 0x03, 0x00, 0x00, 0x36, 0x5e, 0x08, 0x03, 0x03, 0x00, 0x00,
+0x37, 0x26, 0x09, 0x03, 0x03, 0x00, 0x00, 0x38, 0x2a, 0x0d, 0x03, 0x03, 0x00, 0x00, 0x3d, 0x2b,
+0x00, 0x02, 0x41, 0x00, 0xd0, 0xb5, 0x03, 0x41, 0x00, 0xd1, 0xb6, 0x04, 0x41, 0x00, 0xd2, 0xb7,
+0x05, 0x41, 0x00, 0xd3, 0xb8, 0x06, 0x41, 0x00, 0xd4, 0xbd, 0x07, 0x41, 0x00, 0xd5, 0xbe, 0x08,
+0x41, 0x00, 0xd6, 0xc6, 0x09, 0x41, 0x00, 0xd7, 0xc7, 0x0d, 0x41, 0x00, 0xd8, 0xcf, 0x00, 0x02,
+0x41, 0x00, 0x85, 0x8f, 0x03, 0x41, 0x00, 0x87, 0x80, 0x04, 0x41, 0x00, 0x8a, 0x8b, 0x05, 0x41,
+0x00, 0x82, 0x90, 0x06, 0x41, 0x00, 0x8d, 0xad, 0x07, 0x41, 0x00, 0x9e, 0x9f, 0x08, 0x41, 0x00,
+0x97, 0xa7, 0x09, 0x41, 0x00, 0x96, 0xa6, 0x0d, 0x41, 0x00, 0x91, 0x92, 0x00, 0x02, 0x41, 0x00,
+0xdd, 0xdc, 0x03, 0x41, 0x00, 0xdf, 0xde, 0x04, 0xc1, 0x00, 0xf1, 0x04, 0xf0, 0x00, 0x05, 0x41,
+0x00, 0xf3, 0xf2, 0x06, 0x41, 0x00, 0xf5, 0xf4, 0x07, 0x41, 0x00, 0xf7, 0xf6, 0x08, 0x41, 0x00,
+0xf9, 0xf8, 0x09, 0x41, 0x00, 0xfb, 0xfa, 0x0d, 0x41, 0x00, 0xfd, 0xfc, 0x00, 0x02, 0x41, 0x00,
+0xdd, 0xdc, 0x03, 0x41, 0x00, 0xdf, 0xde, 0x04, 0xc1, 0x00, 0xf1, 0x04, 0xf0, 0x00, 0x05, 0x41,
+0x00, 0xf3, 0xf2, 0x06, 0x41, 0x00, 0xf5, 0xf4, 0x07, 0x41, 0x00, 0xf7, 0xf6, 0x08, 0x41, 0x00,
+0xf9, 0xf8, 0x09, 0x41, 0x00, 0xfb, 0xfa, 0x0d, 0x41, 0x00, 0xfd, 0xfc, 0x12, 0x41, 0x00, 0x65,
+0x45, 0x13, 0x41, 0x00, 0x72, 0x52, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55,
+0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x24,
+0x41, 0x02, 0x6a, 0x00, 0x26, 0x41, 0x00, 0x6c, 0x4c, 0x28, 0x00, 0x01, 0xca, 0x29, 0x01, 0x03,
+0xc8, 0xc9, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32, 0x41, 0x00, 0x6d, 0x4d, 0x39, 0x00, 0x00, 0x20,
+0x00, 0x60, 0x0b, 0x61, 0x83, 0x65, 0xa5, 0x69, 0xe9, 0x6f, 0xec, 0x75, 0x88, 0x41, 0xa0, 0x45,
+0x8d, 0x49, 0xe8, 0x4f, 0xee, 0x55, 0xad, 0x20, 0x60, 0x7e, 0x21, 0x61, 0x86, 0xdd, 0xb5, 0x65,
+0x85, 0xf1, 0xd6, 0xf3, 0x8b, 0x69, 0x9f, 0xf5, 0xe7, 0x6c, 0xe6, 0x6d, 0xbe, 0x6e, 0xd4, 0x6f,
+0xe4, 0x72, 0xaf, 0x75, 0xc6, 0xf9, 0xef, 0xfb, 0xb8, 0x79, 0xa4, 0x41, 0x8f, 0xdc, 0xbd, 0x45,
+0x95, 0xf0, 0x9c, 0xf2, 0x8a, 0x49, 0xd0, 0xf4, 0xe3, 0x4c, 0xa6, 0x4d, 0xa7, 0x4e, 0xa8, 0x4f,
+0xe5, 0x52, 0xae, 0x55, 0x9e, 0xf8, 0xab, 0xfa, 0xa9, 0x59, 0xa3, 0x20, 0x7e, 0x27, 0x19, 0x61,
+0x87, 0xdd, 0x89, 0x65, 0x82, 0xf1, 0xd8, 0xf3, 0x9b, 0x69, 0x8c, 0xf5, 0xeb, 0x6f, 0xa2, 0x75,
+0x98, 0xf9, 0xac, 0xfb, 0x91, 0x79, 0x93, 0x41, 0x80, 0xdc, 0xed, 0x45, 0x90, 0xf0, 0x96, 0xf2,
+0x9d, 0x49, 0xa1, 0xf4, 0xea, 0x4f, 0xe0, 0x55, 0x97, 0xf8, 0xaa, 0xfa, 0x92, 0x59, 0xe2, 0x20,
+0x27, 0x00, 0x02, 0x41, 0x00, 0xd0, 0xb5, 0x03, 0x41, 0x00, 0xd1, 0xb6, 0x04, 0x41, 0x00, 0xd2,
+0xb7, 0x05, 0x41, 0x00, 0xd3, 0xb8, 0x06, 0x41, 0x00, 0xd4, 0xbd, 0x07, 0x41, 0x00, 0xd5, 0xbe,
+0x08, 0x41, 0x00, 0xd6, 0xc6, 0x09, 0x41, 0x00, 0xd7, 0xc7, 0x0d, 0x41, 0x00, 0xd8, 0xcf, 0x12,
+0x41, 0x00, 0x65, 0x45, 0x13, 0x41, 0x00, 0x72, 0x52, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41,
+0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1e, 0x41, 0x00,
+0x61, 0x41, 0x24, 0x41, 0x02, 0x6a, 0x00, 0x26, 0x41, 0x00, 0x6c, 0x4c, 0x28, 0x00, 0x01, 0xca,
+0x29, 0x01, 0x03, 0xc8, 0xc9, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32, 0x41, 0x00, 0x6d, 0x4d, 0x39,
+0x00, 0x00, 0x20, 0x00, 0x60, 0x0b, 0x61, 0x83, 0x65, 0xa5, 0x69, 0xe9, 0x6f, 0xec, 0x75, 0x88,
+0x41, 0xa0, 0x45, 0x8d, 0x49, 0xe8, 0x4f, 0xee, 0x55, 0xad, 0x20, 0x60, 0x7e, 0x21, 0x61, 0x86,
+0xd0, 0xf1, 0x65, 0x85, 0xd2, 0xfc, 0xd3, 0x8b, 0x69, 0x9f, 0xd4, 0xe7, 0x6c, 0xe6, 0x6d, 0xf5,
+0x6e, 0xfb, 0x6f, 0xe4, 0x72, 0xaf, 0x75, 0xf6, 0xd6, 0xef, 0xd7, 0xf3, 0x79, 0xa4, 0x41, 0x8f,
+0xb5, 0xf4, 0x45, 0x95, 0xb7, 0x9c, 0xb8, 0x8a, 0x49, 0xf8, 0xbd, 0xe3, 0x4c, 0xa6, 0x4d, 0xa7,
+0x4e, 0xa8, 0x4f, 0xe5, 0x52, 0xae, 0x55, 0x9e, 0xc6, 0xab, 0xc7, 0xa9, 0x59, 0xa3, 0x20, 0x7e,
+0x27, 0x19, 0x61, 0x87, 0xd0, 0x89, 0x65, 0x82, 0xd2, 0xfd, 0xd3, 0x9b, 0x69, 0x8c, 0xd4, 0xeb,
+0x6f, 0xa2, 0x75, 0x98, 0xd6, 0xac, 0xd7, 0x91, 0x79, 0x93, 0x41, 0x80, 0xb5, 0xed, 0x45, 0x90,
+0xb7, 0x96, 0xb8, 0x9d, 0x49, 0xa1, 0xbd, 0xea, 0x4f, 0xe0, 0x55, 0x97, 0xc6, 0xaa, 0xc7, 0x92,
+0x59, 0xe2, 0x20, 0x27, 0x00, 0x02, 0x41, 0x00, 0xdd, 0xdc, 0x03, 0x41, 0x00, 0xdf, 0xde, 0x04,
+0xc1, 0x00, 0xf1, 0x04, 0xf0, 0x00, 0x05, 0x41, 0x00, 0xf3, 0xf2, 0x06, 0x41, 0x00, 0xf5, 0xf4,
+0x07, 0x41, 0x00, 0xf7, 0xf6, 0x08, 0x41, 0x00, 0xf9, 0xf8, 0x09, 0x41, 0x00, 0xfb, 0xfa, 0x0d,
+0x41, 0x00, 0xfd, 0xfc, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x02, 0x01, 0x03,
+0xa0, 0xa0, 0x03, 0x41, 0x00, 0xe7, 0x97, 0x04, 0x01, 0x03, 0xa0, 0xa0, 0x05, 0x41, 0x00, 0xed,
+0x9d, 0x06, 0x01, 0x03, 0xa0, 0xa0, 0x07, 0x41, 0x00, 0xe8, 0x98, 0x08, 0x41, 0x00, 0xee, 0x9e,
+0x09, 0x01, 0x03, 0xa0, 0xa0, 0x0d, 0x41, 0x00, 0xa6, 0x86, 0x10, 0x41, 0x00, 0xef, 0x9f, 0x11,
+0x41, 0x00, 0xe9, 0x99, 0x12, 0x41, 0x00, 0xa5, 0x85, 0x13, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x13,
+0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xeb, 0x9b, 0x16, 0x41, 0x00, 0xe3, 0x93, 0x17,
+0x41, 0x00, 0xa8, 0x88, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x43,
+0x00, 0xea, 0x9a, 0x5b, 0x7b, 0x1b, 0x43, 0x00, 0xec, 0x9c, 0x5d, 0x7d, 0x1e, 0x41, 0x00, 0xa0,
+0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94,
+0x22, 0x41, 0x00, 0xa3, 0x83, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25,
+0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x7e, 0x2c, 0x41, 0x00, 0xa7, 0x87, 0x2d, 0x10, 0x00, 0x00, 0x2e, 0x41, 0x00, 0xe6, 0x96, 0x2f,
+0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x41,
+0x00, 0xac, 0x8c, 0x00, 0x02, 0x41, 0x00, 0xd0, 0xb5, 0x03, 0x41, 0x00, 0xd1, 0xb6, 0x04, 0x41,
+0x00, 0xd2, 0xb7, 0x05, 0x41, 0x00, 0xd3, 0xb8, 0x06, 0x41, 0x00, 0xd4, 0xbd, 0x07, 0x41, 0x00,
+0xd5, 0xbe, 0x08, 0x41, 0x00, 0xd6, 0xc6, 0x09, 0x41, 0x00, 0xd7, 0xc7, 0x0d, 0x41, 0x00, 0xd8,
+0xcf, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x02, 0x01, 0x03, 0xa0, 0xa0, 0x03,
+0x41, 0x00, 0xe7, 0x97, 0x04, 0xc1, 0x00, 0xf1, 0x04, 0xf0, 0x00, 0x05, 0x41, 0x00, 0xed, 0x9d,
+0x06, 0x01, 0x03, 0xa0, 0xa0, 0x07, 0x41, 0x00, 0xe8, 0x98, 0x08, 0x41, 0x00, 0xee, 0x9e, 0x09,
+0x01, 0x03, 0xa0, 0xa0, 0x0d, 0x41, 0x00, 0xa6, 0x86, 0x10, 0x41, 0x00, 0xef, 0x9f, 0x11, 0x41,
+0x00, 0xe9, 0x99, 0x12, 0x41, 0x00, 0xa5, 0x85, 0x13, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x13, 0x14,
+0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xeb, 0x9b, 0x16, 0x41, 0x00, 0xe3, 0x93, 0x17, 0x41,
+0x00, 0xa8, 0x88, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x43, 0x00,
+0xea, 0x9a, 0x5b, 0x7b, 0x1b, 0x43, 0x00, 0xec, 0x9c, 0x5d, 0x7d, 0x1e, 0x41, 0x00, 0xa0, 0x80,
+0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22,
+0x41, 0x00, 0xa3, 0x83, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41,
+0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x80,
+0x2c, 0x41, 0x00, 0xa7, 0x87, 0x2d, 0x10, 0x00, 0x00, 0x2e, 0x41, 0x00, 0xe6, 0x96, 0x2f, 0x41,
+0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x41, 0x00,
+0xac, 0x8c, 0x00, 0xc1, 0x9a, 0x03, 0x04, 0xd2, 0x00, 0x4c, 0x54, 0x09, 0x03, 0x00, 0x00, 0x00,
+0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+0x03, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06,
+0x03, 0x4c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x54, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03,
+0x03, 0xa7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04,
+0x03, 0xc0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+0x43, 0x03, 0x00, 0x00, 0xd7, 0xc7, 0x11, 0x43, 0x03, 0x00, 0x00, 0xd3, 0xb8, 0x12, 0x43, 0x03,
+0x00, 0x00, 0xd2, 0xb7, 0x16, 0x43, 0x03, 0x00, 0x00, 0xd6, 0xc6, 0x17, 0x43, 0x03, 0x00, 0x00,
+0xd4, 0xbd, 0x1e, 0x43, 0x03, 0x00, 0x00, 0xd0, 0xb5, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xd5, 0xbe,
+0x28, 0x03, 0x03, 0x00, 0x00, 0xf2, 0xa6, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xd8, 0xcf, 0x2e, 0x43,
+0x03, 0x00, 0x00, 0xd1, 0xb6, 0x00, 0x10, 0x43, 0x03, 0x00, 0x00, 0x96, 0xa6, 0x11, 0x43, 0x03,
+0x00, 0x00, 0x82, 0x90, 0x12, 0x43, 0x03, 0x00, 0x00, 0x8a, 0x8b, 0x16, 0x43, 0x03, 0x00, 0x00,
+0x97, 0xa7, 0x17, 0x43, 0x03, 0x00, 0x00, 0x8d, 0xad, 0x1e, 0x43, 0x03, 0x00, 0x00, 0x85, 0x8f,
+0x1f, 0x43, 0x03, 0x00, 0x00, 0x9e, 0x9f, 0x28, 0x03, 0x03, 0x00, 0x00, 0xa9, 0x9d, 0x2c, 0x43,
+0x03, 0x00, 0x00, 0x91, 0x92, 0x2e, 0x43, 0x03, 0x00, 0x00, 0x87, 0x80, 0x00, 0x10, 0x43, 0x03,
+0x00, 0x00, 0xfb, 0xfa, 0x11, 0x43, 0x03, 0x00, 0x00, 0xf3, 0xf2, 0x12, 0xc3, 0x00, 0x65, 0x12,
+0x45, 0x12, 0xf1, 0x12, 0xf0, 0x00, 0x16, 0x43, 0x03, 0x00, 0x00, 0xf9, 0xf8, 0x17, 0x43, 0x03,
+0x00, 0x00, 0xf5, 0xf4, 0x1e, 0x43, 0x03, 0x00, 0x00, 0xdd, 0xdc, 0x1f, 0x43, 0x03, 0x00, 0x00,
+0xf7, 0xf6, 0x28, 0x02, 0x03, 0x00, 0x00, 0xb7, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xfd, 0xfc, 0x2e,
+0x43, 0x03, 0x00, 0x00, 0xdf, 0xde, 0x00, 0x28, 0x03, 0x0f, 0x00, 0x00, 0xa0, 0xa0, 0x00, 0x10,
+0x43, 0x03, 0x00, 0x00, 0xfb, 0xfa, 0x11, 0x43, 0x03, 0x00, 0x00, 0xf3, 0xf2, 0x12, 0xc3, 0x00,
+0x65, 0x12, 0x45, 0x12, 0xf1, 0x12, 0xf0, 0x00, 0x16, 0x43, 0x03, 0x00, 0x00, 0xf9, 0xf8, 0x17,
+0x43, 0x03, 0x00, 0x00, 0xf5, 0xf4, 0x1e, 0x43, 0x03, 0x00, 0x00, 0xdd, 0xdc, 0x1f, 0x43, 0x03,
+0x00, 0x00, 0xf7, 0xf6, 0x28, 0x03, 0x0f, 0x00, 0x00, 0xa0, 0xa0, 0x2c, 0x43, 0x03, 0x00, 0x00,
+0xfd, 0xfc, 0x2e, 0x43, 0x03, 0x00, 0x00, 0xdf, 0xde, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x7d, 0x00, 0x0c, 0x43, 0x00, 0xea, 0x9a, 0x2d, 0x5f, 0x0d, 0x43, 0x00, 0xec, 0x9c, 0x3d, 0x2b,
+0x10, 0x43, 0x0c, 0xee, 0x9e, 0xa0, 0xa0, 0x11, 0x43, 0x0c, 0xed, 0x9d, 0xa0, 0xa0, 0x12, 0x43,
+0x0c, 0xa5, 0x85, 0xa0, 0xa0, 0x13, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2,
+0x92, 0x15, 0x41, 0x00, 0xeb, 0x9b, 0x16, 0x43, 0x0c, 0xe3, 0x93, 0xa0, 0xa0, 0x17, 0x43, 0x0c,
+0xa8, 0x88, 0xa0, 0xa0, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x43,
+0x00, 0xe7, 0x97, 0x5b, 0x7b, 0x1b, 0x43, 0x00, 0xe8, 0x98, 0x5d, 0x7d, 0x1e, 0x43, 0x0c, 0xa0,
+0x80, 0xa0, 0xa0, 0x1f, 0x43, 0x0c, 0xe1, 0x91, 0xa0, 0xa0, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21,
+0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00, 0xa3, 0x83, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24, 0x41,
+0x00, 0xa9, 0x89, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x28, 0x43, 0x00,
+0xef, 0x9f, 0x27, 0x22, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x2b, 0x43, 0x00, 0xa6,
+0x86, 0x5c, 0x7c, 0x2c, 0x43, 0x0c, 0xa7, 0x87, 0xa0, 0xa0, 0x2d, 0x41, 0x00, 0xe9, 0x99, 0x2e,
+0x44, 0x1c, 0xe6, 0x96, 0xa0, 0x00, 0x3c, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1,
+0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x41, 0x00, 0xac, 0x8c, 0x00, 0x10, 0x43, 0x03, 0x00,
+0x00, 0xd7, 0xc7, 0x11, 0x43, 0x03, 0x00, 0x00, 0xd3, 0xb8, 0x12, 0x43, 0x03, 0x00, 0x00, 0xd2,
+0xb7, 0x16, 0x43, 0x03, 0x00, 0x00, 0xd6, 0xc6, 0x17, 0x43, 0x03, 0x00, 0x00, 0xd4, 0xbd, 0x1e,
+0x43, 0x03, 0x00, 0x00, 0xd0, 0xb5, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xd5, 0xbe, 0x28, 0x03, 0x03,
+0x00, 0x00, 0xf4, 0xf5, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xd8, 0xcf, 0x2e, 0x43, 0x03, 0x00, 0x00,
+0xd1, 0xb6, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x0c, 0x43, 0x00, 0xea, 0x9a,
+0x2d, 0x5f, 0x0d, 0x43, 0x00, 0xec, 0x9c, 0x3d, 0x2b, 0x10, 0x43, 0x0c, 0xee, 0x9e, 0xa0, 0xa0,
+0x11, 0x43, 0x0c, 0xed, 0x9d, 0xa0, 0xa0, 0x12, 0x43, 0x0c, 0xa5, 0x85, 0xa0, 0xa0, 0x13, 0xc1,
+0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xeb, 0x9b, 0x16,
+0x43, 0x0c, 0xe3, 0x93, 0xa0, 0xa0, 0x17, 0x43, 0x0c, 0xa8, 0x88, 0xa0, 0xa0, 0x18, 0x41, 0x00,
+0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x43, 0x00, 0xe7, 0x97, 0x5b, 0x7b, 0x1b, 0x43,
+0x00, 0xe8, 0x98, 0x5d, 0x7d, 0x1e, 0x43, 0x0c, 0xa0, 0x80, 0xa0, 0xa0, 0x1f, 0x43, 0x0c, 0xe1,
+0x91, 0xa0, 0xa0, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00,
+0xa3, 0x83, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41, 0x00, 0xaa,
+0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x28, 0x43, 0x00, 0xef, 0x9f, 0x27, 0x22, 0x29, 0xc3, 0x00,
+0xf1, 0x29, 0xf0, 0x00, 0x60, 0x29, 0x7e, 0x29, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7e,
+0x2b, 0x43, 0x00, 0xa6, 0x86, 0x5c, 0x7c, 0x2c, 0x43, 0x0c, 0xa7, 0x87, 0xa0, 0xa0, 0x2d, 0x41,
+0x00, 0xe9, 0x99, 0x2e, 0x44, 0x1c, 0xe6, 0x96, 0xa0, 0x00, 0x3c, 0x2f, 0x41, 0x00, 0xa2, 0x82,
+0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x41, 0x00, 0xac, 0x8c, 0x00,
+0x6f, 0x88, 0x05, 0x04, 0xd3, 0x00, 0x4c, 0x54, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x13, 0x01,
+0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x7b, 0x01,
+0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xc2, 0x01,
+0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x7c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0xb7, 0x02,
+0x00, 0x00, 0x00, 0x00, 0x09, 0x03, 0x73, 0x03, 0xed, 0x03, 0x00, 0x00, 0x0a, 0x03, 0x7e, 0x04,
+0xf2, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x21,
+0x31, 0x03, 0x02, 0x00, 0x22, 0x32, 0x40, 0x04, 0x02, 0x00, 0x2f, 0x33, 0x23, 0x05, 0x02, 0x00,
+0x3b, 0x34, 0x24, 0x06, 0x02, 0x00, 0x3a, 0x35, 0x25, 0x07, 0x02, 0x00, 0x2c, 0x36, 0x5e, 0x08,
+0x02, 0x00, 0x2e, 0x37, 0x26, 0x09, 0x02, 0x00, 0x3f, 0x38, 0x2a, 0x0a, 0x01, 0x00, 0x28, 0x39,
+0x0b, 0x01, 0x00, 0x29, 0x30, 0x0c, 0x01, 0x00, 0x5f, 0x2d, 0x0d, 0x01, 0x00, 0x2b, 0x3d, 0x10,
+0x43, 0x00, 0xd0, 0xb5, 0x71, 0x51, 0x11, 0x43, 0x00, 0xd8, 0xcf, 0x77, 0x57, 0x1a, 0x43, 0x00,
+0xd4, 0xbd, 0x5b, 0x7b, 0x1b, 0x03, 0x00, 0xf2, 0xa6, 0x5d, 0x7d, 0x27, 0x43, 0x00, 0xd6, 0xc6,
+0x3b, 0x3a, 0x28, 0x43, 0x00, 0xd3, 0xb8, 0x27, 0x22, 0x2b, 0x01, 0x00, 0x7c, 0x5c, 0x2d, 0x43,
+0x00, 0xd7, 0xc7, 0x78, 0x58, 0x33, 0x43, 0x00, 0xd1, 0xb6, 0x2c, 0x3c, 0x34, 0x43, 0x00, 0xd5,
+0xbe, 0x2e, 0x3e, 0x35, 0x43, 0x00, 0xd2, 0xb7, 0x2f, 0x3f, 0x00, 0x10, 0x41, 0x00, 0x85, 0x8f,
+0x11, 0x41, 0x00, 0x91, 0x92, 0x1a, 0x41, 0x00, 0x8d, 0xad, 0x1b, 0x01, 0x00, 0xa9, 0x9d, 0x27,
+0x41, 0x00, 0x97, 0xa7, 0x28, 0x41, 0x00, 0x82, 0x90, 0x2d, 0x41, 0x00, 0x96, 0xa6, 0x33, 0x41,
+0x00, 0x87, 0x80, 0x34, 0x41, 0x00, 0x9e, 0x9f, 0x35, 0x41, 0x00, 0x8a, 0x8b, 0x00, 0x10, 0x41,
+0x00, 0xdd, 0xdc, 0x11, 0x41, 0x00, 0xfd, 0xfc, 0x1a, 0x41, 0x00, 0xf5, 0xf4, 0x1b, 0x01, 0x00,
+0xb7, 0xa6, 0x27, 0x41, 0x00, 0xf9, 0xf8, 0x28, 0x41, 0x00, 0xf3, 0xf2, 0x2d, 0x41, 0x00, 0xfb,
+0xfa, 0x33, 0x41, 0x00, 0xdf, 0xde, 0x34, 0x41, 0x00, 0xf7, 0xf6, 0x35, 0xc1, 0x00, 0xf1, 0x35,
+0xf0, 0x00, 0x00, 0x1b, 0x01, 0x03, 0xa0, 0xa0, 0x00, 0x10, 0x41, 0x00, 0xdd, 0xdc, 0x11, 0x41,
+0x00, 0xfd, 0xfc, 0x1a, 0x41, 0x00, 0xf5, 0xf4, 0x1b, 0x01, 0x03, 0xa0, 0xa0, 0x27, 0x41, 0x00,
+0xf9, 0xf8, 0x28, 0x41, 0x00, 0xf3, 0xf2, 0x2d, 0x41, 0x00, 0xfb, 0xfa, 0x33, 0x41, 0x00, 0xdf,
+0xde, 0x34, 0x41, 0x00, 0xf7, 0xf6, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0x36, 0x04, 0x1f,
+0x00, 0x00, 0x00, 0x00, 0x7d, 0x56, 0x10, 0x00, 0x00, 0x00, 0x10, 0x43, 0x0c, 0xef, 0x9f, 0xa0,
+0xa0, 0x11, 0x43, 0x0c, 0xa6, 0x86, 0xa0, 0xa0, 0x12, 0x41, 0x00, 0xa5, 0x85, 0x13, 0xc1, 0x00,
+0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xeb, 0x9b, 0x16, 0x41,
+0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xa8, 0x88, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00,
+0xaf, 0x8f, 0x1a, 0x41, 0x00, 0xea, 0x9a, 0x1b, 0x41, 0x00, 0xec, 0x9c, 0x1e, 0x41, 0x00, 0xa0,
+0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94,
+0x22, 0x41, 0x00, 0xa3, 0x83, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25,
+0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x27, 0x41, 0x00, 0xee, 0x9e, 0x28, 0x41,
+0x00, 0xed, 0x9d, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x2c, 0x41, 0x00, 0xa7, 0x87,
+0x2d, 0x43, 0x0c, 0xe9, 0x99, 0xa0, 0xa0, 0x2e, 0x41, 0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2,
+0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x41, 0x00, 0xac, 0x8c,
+0x33, 0x41, 0x00, 0xe7, 0x97, 0x34, 0x41, 0x00, 0xe8, 0x98, 0x35, 0x01, 0x03, 0xa0, 0xa0, 0x56,
+0x10, 0x00, 0x00, 0x00, 0x10, 0x41, 0x00, 0xd0, 0xb5, 0x11, 0x41, 0x00, 0xd8, 0xcf, 0x1a, 0x41,
+0x00, 0xd4, 0xbd, 0x1b, 0x01, 0x00, 0xf4, 0xf5, 0x27, 0x41, 0x00, 0xd6, 0xc6, 0x28, 0x41, 0x00,
+0xd3, 0xb8, 0x2d, 0x41, 0x00, 0xd7, 0xc7, 0x33, 0x41, 0x00, 0xd1, 0xb6, 0x34, 0x41, 0x00, 0xd5,
+0xbe, 0x35, 0x41, 0x00, 0xd2, 0xb7, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x10,
+0x43, 0x0c, 0xef, 0x9f, 0xa0, 0xa0, 0x11, 0x43, 0x0c, 0xa6, 0x86, 0xa0, 0xa0, 0x12, 0x41, 0x00,
+0xa5, 0x85, 0x13, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41,
+0x00, 0xeb, 0x9b, 0x16, 0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xa8, 0x88, 0x18, 0x41, 0x00,
+0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x41, 0x00, 0xea, 0x9a, 0x1b, 0x41, 0x00, 0xec,
+0x9c, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4, 0x84,
+0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00, 0xa3, 0x83, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24,
+0x41, 0x00, 0xa9, 0x89, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x27, 0x41,
+0x00, 0xee, 0x9e, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7e,
+0x2c, 0x41, 0x00, 0xa7, 0x87, 0x2d, 0x43, 0x0c, 0xe9, 0x99, 0xa0, 0xa0, 0x2e, 0x41, 0x00, 0xe6,
+0x96, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d,
+0x32, 0x41, 0x00, 0xac, 0x8c, 0x33, 0x41, 0x00, 0xe7, 0x97, 0x34, 0x41, 0x00, 0xe8, 0x98, 0x35,
+0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0x56, 0x10, 0x00, 0x00, 0x00, 0x10, 0x41, 0x00, 0xdd, 0xdc,
+0x11, 0x41, 0x00, 0xfd, 0xfc, 0x12, 0x41, 0x00, 0x65, 0x45, 0x13, 0x41, 0x00, 0x72, 0x52, 0x15,
+0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41,
+0x00, 0x6f, 0x4f, 0x1a, 0x41, 0x00, 0xf5, 0xf4, 0x1b, 0x01, 0x00, 0xd5, 0xb7, 0x1e, 0x41, 0x00,
+0x61, 0x41, 0x24, 0x41, 0x02, 0x6a, 0x00, 0x26, 0x41, 0x00, 0x6c, 0x4c, 0x27, 0x41, 0x00, 0xf9,
+0xf8, 0x28, 0x41, 0x00, 0xf3, 0xf2, 0x29, 0x02, 0x07, 0xc8, 0xc9, 0xca, 0x2d, 0x41, 0x00, 0xfb,
+0xfa, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32, 0x41, 0x00, 0x6d, 0x4d, 0x33, 0x41, 0x00, 0xdf, 0xde,
+0x34, 0x41, 0x00, 0xf7, 0xf6, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0x39, 0x00, 0x00, 0x20,
+0x56, 0x10, 0x00, 0x00, 0x00, 0x60, 0x0b, 0x61, 0x83, 0x65, 0xa5, 0x69, 0xe9, 0x6f, 0xec, 0x75,
+0x88, 0x41, 0xa0, 0x45, 0x8d, 0x49, 0xe8, 0x4f, 0xee, 0x55, 0xad, 0x20, 0x60, 0x7e, 0x21, 0x61,
+0x86, 0xdd, 0xb5, 0x65, 0x85, 0xf1, 0xd6, 0xf3, 0x8b, 0x69, 0x9f, 0xf5, 0xe7, 0x6c, 0xe6, 0x6d,
+0xbe, 0x6e, 0xd4, 0x6f, 0xe4, 0x72, 0xaf, 0x75, 0xc6, 0xf9, 0xef, 0xfb, 0xb8, 0x79, 0xa4, 0x41,
+0x8f, 0xdc, 0xbd, 0x45, 0x95, 0xf0, 0x9c, 0xf2, 0x8a, 0x49, 0xd0, 0xf4, 0xe3, 0x4c, 0xa6, 0x4d,
+0xa7, 0x4e, 0xa8, 0x4f, 0xe5, 0x52, 0xae, 0x55, 0x9e, 0xf8, 0xab, 0xfa, 0xa9, 0x59, 0xa3, 0x20,
+0x7e, 0x27, 0x19, 0x61, 0x87, 0xdd, 0x89, 0x65, 0x82, 0xf1, 0xd8, 0xf3, 0x9b, 0x69, 0x8c, 0xf5,
+0xeb, 0x6f, 0xa2, 0x75, 0x98, 0xf9, 0xac, 0xfb, 0x91, 0x79, 0x93, 0x41, 0x80, 0xdc, 0xed, 0x45,
+0x90, 0xf0, 0x96, 0xf2, 0x9d, 0x49, 0xa1, 0xf4, 0xea, 0x4f, 0xe0, 0x55, 0x97, 0xf8, 0xaa, 0xfa,
+0x92, 0x59, 0xe2, 0x20, 0x27, 0x00, 0x10, 0x41, 0x00, 0xd0, 0xb5, 0x11, 0x41, 0x00, 0xd8, 0xcf,
+0x12, 0x41, 0x00, 0x65, 0x45, 0x13, 0x41, 0x00, 0x72, 0x52, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16,
+0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x41,
+0x00, 0xd4, 0xbd, 0x1b, 0x01, 0x00, 0xf7, 0xf2, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x24, 0x41, 0x02,
+0x6a, 0x00, 0x26, 0x41, 0x00, 0x6c, 0x4c, 0x27, 0x41, 0x00, 0xd6, 0xc6, 0x28, 0x41, 0x00, 0xd3,
+0xb8, 0x29, 0x02, 0x07, 0xc8, 0xc9, 0xca, 0x2d, 0x41, 0x00, 0xd7, 0xc7, 0x31, 0x41, 0x00, 0x6e,
+0x4e, 0x32, 0x41, 0x00, 0x6d, 0x4d, 0x33, 0x41, 0x00, 0xd1, 0xb6, 0x34, 0x41, 0x00, 0xd5, 0xbe,
+0x35, 0x41, 0x00, 0xd2, 0xb7, 0x39, 0x00, 0x00, 0x20, 0x00, 0x60, 0x0b, 0x61, 0x83, 0x65, 0xa5,
+0x69, 0xe9, 0x6f, 0xec, 0x75, 0x88, 0x41, 0xa0, 0x45, 0x8d, 0x49, 0xe8, 0x4f, 0xee, 0x55, 0xad,
+0x20, 0x60, 0x7e, 0x21, 0x61, 0x86, 0xd0, 0xf1, 0x65, 0x85, 0xd2, 0xfc, 0xd3, 0x8b, 0x69, 0x9f,
+0xd4, 0xe7, 0x6c, 0xe6, 0x6d, 0xf5, 0x6e, 0xfb, 0x6f, 0xe4, 0x72, 0xaf, 0x75, 0xf6, 0xd6, 0xef,
+0xd7, 0xf3, 0x79, 0xa4, 0x41, 0x8f, 0xb5, 0xf4, 0x45, 0x95, 0xb7, 0x9c, 0xb8, 0x8a, 0x49, 0xf8,
+0xbd, 0xe3, 0x4c, 0xa6, 0x4d, 0xa7, 0x4e, 0xa8, 0x4f, 0xe5, 0x52, 0xae, 0x55, 0x9e, 0xc6, 0xab,
+0xc7, 0xa9, 0x59, 0xa3, 0x20, 0x7e, 0x27, 0x19, 0x61, 0x87, 0xd0, 0x89, 0x65, 0x82, 0xd2, 0xfd,
+0xd3, 0x9b, 0x69, 0x8c, 0xd4, 0xeb, 0x6f, 0xa2, 0x75, 0x98, 0xd6, 0xac, 0xd7, 0x91, 0x79, 0x93,
+0x41, 0x80, 0xb5, 0xed, 0x45, 0x90, 0xb7, 0x96, 0xb8, 0x9d, 0x49, 0xa1, 0xbd, 0xea, 0x4f, 0xe0,
+0x55, 0x97, 0xc6, 0xaa, 0xc7, 0x92, 0x59, 0xe2, 0x20, 0x27, 0x00, 0x19, 0x22, 0x06, 0x04, 0xdd,
+0x00, 0x4c, 0x54, 0x0b, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03,
+0x03, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xda, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04,
+0x03, 0xc1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0xe9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x05,
+0x03, 0xd2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x16, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09,
+0x03, 0x36, 0x04, 0xb7, 0x04, 0x00, 0x00, 0x0a, 0x03, 0x48, 0x05, 0x8c, 0x05, 0x00, 0x00, 0x00,
+0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x21, 0x31, 0x40, 0x03, 0x03, 0x00,
+0x2d, 0x32, 0x5f, 0x1f, 0x04, 0x02, 0x00, 0x2f, 0x33, 0x23, 0x05, 0x02, 0x00, 0x3b, 0x34, 0x24,
+0x06, 0x02, 0x00, 0x3a, 0x35, 0xf5, 0x07, 0x02, 0x00, 0x2c, 0x36, 0x5e, 0x08, 0x02, 0x00, 0x2e,
+0x37, 0x26, 0x09, 0x02, 0x00, 0x3d, 0x38, 0x2a, 0x0a, 0x03, 0x00, 0x28, 0x39, 0x5b, 0x1b, 0x0b,
+0x03, 0x00, 0x29, 0x30, 0x5d, 0x1d, 0x0c, 0x02, 0x00, 0x3f, 0x2b, 0x27, 0x0d, 0xc4, 0x00, 0x78,
+0x2d, 0x58, 0x2d, 0x25, 0x83, 0x18, 0x2d, 0x00, 0x83, 0x10, 0x41, 0x00, 0xd0, 0xb5, 0x11, 0x41,
+0x00, 0xd8, 0xcf, 0x1a, 0x42, 0x00, 0xd4, 0xbd, 0x7b, 0x1b, 0xc4, 0x00, 0x77, 0x11, 0x57, 0x11,
+0x7d, 0x11, 0x17, 0x11, 0x00, 0x11, 0x21, 0x41, 0x00, 0xd5, 0xbe, 0x27, 0x41, 0x00, 0xd6, 0xc6,
+0x28, 0x42, 0x00, 0xd3, 0xb8, 0x22, 0x29, 0x02, 0x00, 0x60, 0x7e, 0xef, 0x2b, 0xc4, 0x00, 0x71,
+0x10, 0x51, 0x10, 0x7c, 0x10, 0x11, 0x10, 0x00, 0x10, 0x2d, 0x41, 0x00, 0xd7, 0xc7, 0x33, 0x42,
+0x00, 0xd1, 0xb6, 0xf7, 0x34, 0xc4, 0x00, 0x66, 0x21, 0x46, 0x21, 0xf2, 0x21, 0x06, 0x21, 0x00,
+0x21, 0x35, 0x43, 0x00, 0xd2, 0xb7, 0x5c, 0x1c, 0x39, 0x00, 0x00, 0x20, 0x56, 0x02, 0x00, 0x3c,
+0x3e, 0x2d, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0x15, 0x10, 0x41, 0x00, 0x85, 0x8f, 0x11, 0x41,
+0x00, 0x91, 0x92, 0x1a, 0x41, 0x00, 0x8d, 0xad, 0x21, 0x41, 0x00, 0x9e, 0x9f, 0x27, 0x41, 0x00,
+0x97, 0xa7, 0x28, 0x41, 0x00, 0x82, 0x90, 0x29, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x2d, 0x41, 0x00,
+0x96, 0xa6, 0x33, 0x42, 0x00, 0x87, 0x80, 0x9d, 0x34, 0xc4, 0x00, 0x66, 0x21, 0x46, 0x21, 0xa9,
+0x21, 0x06, 0x21, 0x00, 0x21, 0x35, 0x41, 0x00, 0x8a, 0x8b, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00,
+0x15, 0x10, 0x41, 0x00, 0xdd, 0xdc, 0x11, 0x41, 0x00, 0xfd, 0xfc, 0x1a, 0x41, 0x00, 0xf5, 0xf4,
+0x21, 0x41, 0x00, 0xf7, 0xf6, 0x27, 0x41, 0x00, 0xf9, 0xf8, 0x28, 0x41, 0x00, 0xf3, 0xf2, 0x29,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x2d, 0x41, 0x00, 0xfb, 0xfa, 0x33, 0x42, 0x04, 0xdf, 0xde, 0xa0,
+0x34, 0xc4, 0x04, 0x66, 0x21, 0x46, 0x21, 0xa0, 0x21, 0x06, 0x21, 0x00, 0x21, 0x35, 0xc1, 0x00,
+0xf1, 0x35, 0xf0, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x06, 0x02, 0x03,
+0x00, 0x00, 0x15, 0x0d, 0x84, 0x03, 0xa0, 0x2d, 0xa0, 0x2d, 0x25, 0x83, 0x18, 0x2d, 0x00, 0x83,
+0x10, 0x41, 0x00, 0xef, 0x9f, 0x11, 0x41, 0x00, 0xa6, 0x86, 0x12, 0x41, 0x00, 0xa5, 0x85, 0x13,
+0xc1, 0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xeb, 0x9b,
+0x16, 0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xa8, 0x88, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19,
+0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x41, 0x00, 0xea, 0x9a, 0x1b, 0xc4, 0x00, 0xec, 0x11, 0x9c, 0x11,
+0x7d, 0x11, 0x17, 0x11, 0x00, 0x11, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91,
+0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe8, 0x98, 0x22, 0x41, 0x00, 0xa3, 0x83, 0x23,
+0x41, 0x00, 0xe5, 0x95, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41,
+0x00, 0xab, 0x8b, 0x27, 0x41, 0x00, 0xee, 0x9e, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x84, 0x03, 0xa0, 0x10,
+0xa0, 0x10, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x2c, 0x41, 0x00, 0xa7, 0x87, 0x2d, 0x41, 0x00,
+0xe9, 0x99, 0x2e, 0x41, 0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1,
+0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x41, 0x00, 0xac, 0x8c, 0x33, 0x42, 0x04, 0xe7, 0x97,
+0xa0, 0x34, 0xc4, 0x04, 0xe4, 0x21, 0x94, 0x21, 0xa0, 0x21, 0x06, 0x21, 0x00, 0x21, 0x35, 0x01,
+0x03, 0xa0, 0xa0, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0x15, 0x29, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x33, 0x42, 0x03, 0x00, 0x00, 0xf4, 0x34, 0xc4, 0x00, 0x66, 0x21, 0x46, 0x21, 0xf5, 0x21, 0x06,
+0x21, 0x00, 0x21, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x06, 0x02, 0x03, 0x00,
+0x00, 0x15, 0x0d, 0x84, 0x03, 0xa0, 0x2d, 0xa0, 0x2d, 0x25, 0x83, 0x18, 0x2d, 0x00, 0x83, 0x10,
+0x41, 0x00, 0xef, 0x9f, 0x11, 0x41, 0x00, 0xa6, 0x86, 0x12, 0x41, 0x00, 0xa5, 0x85, 0x13, 0xc1,
+0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xeb, 0x9b, 0x16,
+0x41, 0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xa8, 0x88, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41,
+0x00, 0xaf, 0x8f, 0x1a, 0x41, 0x00, 0xea, 0x9a, 0x1b, 0xc4, 0x00, 0xec, 0x11, 0x9c, 0x11, 0x7d,
+0x11, 0x17, 0x11, 0x00, 0x11, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41, 0x00, 0xe1, 0x91, 0x20,
+0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe8, 0x98, 0x22, 0x41, 0x00, 0xa3, 0x83, 0x23, 0x41,
+0x00, 0xe5, 0x95, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00,
+0xab, 0x8b, 0x27, 0x41, 0x00, 0xee, 0x9e, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x2b, 0x84, 0x03, 0xa0, 0x10, 0xa0,
+0x10, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x2c, 0x41, 0x00, 0xa7, 0x87, 0x2d, 0x41, 0x00, 0xe9,
+0x99, 0x2e, 0x41, 0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41, 0x00, 0xa1, 0x81,
+0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x41, 0x00, 0xac, 0x8c, 0x33, 0x42, 0x00, 0xe7, 0x97, 0xf4,
+0x34, 0xc4, 0x00, 0xe4, 0x21, 0x94, 0x21, 0xf5, 0x21, 0x06, 0x21, 0x00, 0x21, 0x35, 0xc1, 0x00,
+0xf1, 0x35, 0xf0, 0x00, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0xbe, 0x10, 0x41, 0x00, 0xdd, 0xdc,
+0x11, 0x41, 0x00, 0xfd, 0xfc, 0x1a, 0x41, 0x00, 0xf5, 0xf4, 0x21, 0x41, 0x00, 0xf7, 0xf6, 0x27,
+0x41, 0x00, 0xf9, 0xf8, 0x28, 0x41, 0x00, 0xf3, 0xf2, 0x2d, 0x41, 0x00, 0xfb, 0xfa, 0x33, 0x42,
+0x00, 0xdf, 0xde, 0xd5, 0x34, 0xc4, 0x00, 0x66, 0x21, 0x46, 0x21, 0xb7, 0x21, 0x06, 0x21, 0x00,
+0x21, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0x15, 0x29,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x33, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x34, 0xc4, 0x04, 0x66, 0x21,
+0x46, 0x21, 0xa0, 0x21, 0x06, 0x21, 0x00, 0x21, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00, 0x15, 0x10,
+0x41, 0x00, 0xdd, 0xdc, 0x11, 0x41, 0x00, 0xfd, 0xfc, 0x12, 0x41, 0x00, 0x65, 0x45, 0x13, 0x41,
+0x00, 0x72, 0x52, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00,
+0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x41, 0x00, 0xf5, 0xf4, 0x1e, 0x41, 0x00, 0x61,
+0x41, 0x21, 0x41, 0x00, 0xf7, 0xf6, 0x24, 0x41, 0x02, 0x6a, 0x00, 0x26, 0x41, 0x00, 0x6c, 0x4c,
+0x27, 0x41, 0x00, 0xf9, 0xf8, 0x28, 0x41, 0x00, 0xf3, 0xf2, 0x29, 0x02, 0x07, 0xc8, 0xc9, 0xca,
+0x2d, 0x41, 0x00, 0xfb, 0xfa, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32, 0x41, 0x00, 0x6d, 0x4d, 0x33,
+0x42, 0x00, 0xdf, 0xde, 0xd5, 0x34, 0xc4, 0x00, 0x66, 0x21, 0x46, 0x21, 0xb7, 0x21, 0x06, 0x21,
+0x00, 0x21, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0x00, 0x60, 0x0b, 0x61, 0x83, 0x65, 0xa5,
+0x69, 0xe9, 0x6f, 0xec, 0x75, 0x88, 0x41, 0xa0, 0x45, 0x8d, 0x49, 0xe8, 0x4f, 0xee, 0x55, 0xad,
+0x20, 0x60, 0x7e, 0x21, 0x61, 0x86, 0xdd, 0xb5, 0x65, 0x85, 0xf1, 0xd6, 0xf3, 0x8b, 0x69, 0x9f,
+0xf5, 0xe7, 0x6c, 0xe6, 0x6d, 0xbe, 0x6e, 0xd4, 0x6f, 0xe4, 0x72, 0xaf, 0x75, 0xc6, 0xf9, 0xef,
+0xfb, 0xb8, 0x79, 0xa4, 0x41, 0x8f, 0xdc, 0xbd, 0x45, 0x95, 0xf0, 0x9c, 0xf2, 0x8a, 0x49, 0xd0,
+0xf4, 0xe3, 0x4c, 0xa6, 0x4d, 0xa7, 0x4e, 0xa8, 0x4f, 0xe5, 0x52, 0xae, 0x55, 0x9e, 0xf8, 0xab,
+0xfa, 0xa9, 0x59, 0xa3, 0x20, 0x7e, 0x27, 0x19, 0x61, 0x87, 0xdd, 0x89, 0x65, 0x82, 0xf1, 0xd8,
+0xf3, 0x9b, 0x69, 0x8c, 0xf5, 0xeb, 0x6f, 0xa2, 0x75, 0x98, 0xf9, 0xac, 0xfb, 0x91, 0x79, 0x93,
+0x41, 0x80, 0xdc, 0xed, 0x45, 0x90, 0xf0, 0x96, 0xf2, 0x9d, 0x49, 0xa1, 0xf4, 0xea, 0x4f, 0xe0,
+0x55, 0x97, 0xf8, 0xaa, 0xfa, 0x92, 0x59, 0xe2, 0x20, 0x27, 0x00, 0x06, 0x02, 0x03, 0x00, 0x00,
+0x15, 0x12, 0x41, 0x00, 0x65, 0x45, 0x13, 0x41, 0x00, 0x72, 0x52, 0x15, 0x41, 0x00, 0x79, 0x59,
+0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1e,
+0x41, 0x00, 0x61, 0x41, 0x24, 0x41, 0x02, 0x6a, 0x00, 0x26, 0x41, 0x00, 0x6c, 0x4c, 0x29, 0x02,
+0x07, 0xc8, 0xc9, 0xca, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x32, 0x41, 0x00, 0x6d, 0x4d, 0x00, 0x60,
+0x0b, 0x61, 0x83, 0x65, 0xa5, 0x69, 0xe9, 0x6f, 0xec, 0x75, 0x88, 0x41, 0xa0, 0x45, 0x8d, 0x49,
+0xe8, 0x4f, 0xee, 0x55, 0xad, 0x20, 0x60, 0x7e, 0x21, 0x61, 0x86, 0xd0, 0xf1, 0x65, 0x85, 0xd2,
+0xfc, 0xd3, 0x8b, 0x69, 0x9f, 0xd4, 0xe7, 0x6c, 0xe6, 0x6d, 0xf5, 0x6e, 0xfb, 0x6f, 0xe4, 0x72,
+0xaf, 0x75, 0xf6, 0xd6, 0xef, 0xd7, 0xf3, 0x79, 0xa4, 0x41, 0x8f, 0xb5, 0xf4, 0x45, 0x95, 0xb7,
+0x9c, 0xb8, 0x8a, 0x49, 0xf8, 0xbd, 0xe3, 0x4c, 0xa6, 0x4d, 0xa7, 0x4e, 0xa8, 0x4f, 0xe5, 0x52,
+0xae, 0x55, 0x9e, 0xc6, 0xab, 0xc7, 0xa9, 0x59, 0xa3, 0x20, 0x7e, 0x27, 0x19, 0x61, 0x87, 0xd0,
+0x89, 0x65, 0x82, 0xd2, 0xfd, 0xd3, 0x9b, 0x69, 0x8c, 0xd4, 0xeb, 0x6f, 0xa2, 0x75, 0x98, 0xd6,
+0xac, 0xd7, 0x91, 0x79, 0x93, 0x41, 0x80, 0xb5, 0xed, 0x45, 0x90, 0xb7, 0x96, 0xb8, 0x9d, 0x49,
+0xa1, 0xbd, 0xea, 0x4f, 0xe0, 0x55, 0x97, 0xc6, 0xaa, 0xc7, 0x92, 0x59, 0xe2, 0x20, 0x27, 0x00,
+0x06, 0x5a, 0x08, 0x04, 0xc8, 0x01, 0x4c, 0x54, 0x0f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x1b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x23, 0x01,
+0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x94, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0xd8, 0x01,
+0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x60, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xa4, 0x02,
+0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x33, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x71, 0x03,
+0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xf9, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x84, 0x04,
+0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x2f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x33, 0x06,
+0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0xa8, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x4f, 0x07,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0xac, 0x03, 0x02, 0x03, 0x00, 0x00, 0xab,
+0x04, 0x02, 0x03, 0x00, 0x00, 0xf3, 0x05, 0x02, 0x03, 0x00, 0x00, 0x9f, 0x06, 0x02, 0x03, 0x00,
+0x00, 0x96, 0x07, 0x02, 0x03, 0x00, 0x00, 0xf8, 0x08, 0x02, 0x03, 0x00, 0x00, 0xf5, 0x09, 0x02,
+0x03, 0x00, 0x00, 0x9e, 0x0a, 0x02, 0x03, 0x00, 0x00, 0xae, 0x0b, 0x02, 0x03, 0x00, 0x00, 0xaf,
+0x0d, 0x02, 0x03, 0x00, 0x00, 0xf1, 0x10, 0x42, 0x03, 0x00, 0x00, 0xfb, 0x11, 0x42, 0x03, 0x00,
+0x00, 0xfd, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfc, 0x13, 0x42, 0x03, 0x00, 0x00, 0xa9, 0x1f, 0x42,
+0x03, 0x00, 0x00, 0xe1, 0x26, 0x42, 0x03, 0x00, 0x00, 0x9c, 0x31, 0x42, 0x03, 0x00, 0x00, 0xaa,
+0x34, 0x02, 0x03, 0x00, 0x00, 0xfa, 0x35, 0x02, 0x03, 0x00, 0x00, 0xf6, 0x56, 0x02, 0x00, 0xfd,
+0xfc, 0xfb, 0x00, 0x36, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x79, 0x00, 0x02, 0x01, 0x00, 0x21, 0x31,
+0x03, 0x01, 0x00, 0x22, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x01, 0x00, 0x3b, 0x34, 0x06,
+0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01,
+0x00, 0x3f, 0x38, 0x0a, 0x01, 0x00, 0x28, 0x39, 0x0b, 0x01, 0x00, 0x29, 0x30, 0x0d, 0x01, 0x00,
+0x2b, 0x3d, 0x10, 0x41, 0x00, 0xd0, 0xb5, 0x11, 0x41, 0x00, 0xd8, 0xcf, 0x1a, 0x41, 0x00, 0xd4,
+0xbd, 0x1b, 0x01, 0x00, 0xf7, 0xf2, 0x27, 0x41, 0x00, 0xd6, 0xc6, 0x28, 0x41, 0x00, 0xd3, 0xb8,
+0x2a, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x78, 0x2d, 0x41, 0x00, 0xd7, 0xc7, 0x33, 0x41, 0x00, 0xd1,
+0xb6, 0x34, 0x41, 0x00, 0xd5, 0xbe, 0x35, 0x41, 0x00, 0xd2, 0xb7, 0x00, 0x04, 0x02, 0x03, 0x00,
+0x00, 0xb8, 0x07, 0x02, 0x03, 0x00, 0x00, 0xd0, 0x08, 0x02, 0x03, 0x00, 0x00, 0xbe, 0x0d, 0x02,
+0x03, 0x00, 0x00, 0xb5, 0x10, 0x42, 0x03, 0x00, 0x00, 0xd4, 0x11, 0x42, 0x03, 0x00, 0x00, 0xd8,
+0x12, 0x42, 0x03, 0x00, 0x00, 0xd6, 0x34, 0x02, 0x03, 0x00, 0x00, 0xd3, 0x35, 0x02, 0x03, 0x00,
+0x00, 0xc6, 0x36, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x7b, 0x56, 0x02, 0x00, 0xd8, 0xd6, 0xd4, 0x00,
+0x02, 0x01, 0x00, 0x21, 0x31, 0x03, 0x01, 0x00, 0x22, 0x32, 0x04, 0x02, 0x00, 0x2f, 0x33, 0xb8,
+0x05, 0x01, 0x00, 0x3b, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x02, 0x00, 0x2c, 0x36, 0xd0,
+0x08, 0x02, 0x00, 0x2e, 0x37, 0xbe, 0x09, 0x01, 0x00, 0x3f, 0x38, 0x0a, 0x01, 0x00, 0x28, 0x39,
+0x0b, 0x01, 0x00, 0x29, 0x30, 0x0d, 0x02, 0x00, 0x2b, 0x3d, 0xb5, 0x10, 0x42, 0x00, 0xdd, 0xdc,
+0xd4, 0x11, 0x42, 0x00, 0xfd, 0xfc, 0xd8, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd6, 0x1a, 0x41, 0x00,
+0xf5, 0xf4, 0x1b, 0x01, 0x00, 0xd5, 0xb7, 0x27, 0x41, 0x00, 0xf9, 0xf8, 0x28, 0x41, 0x00, 0xf3,
+0xf2, 0x2a, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x7a, 0x2d, 0x41, 0x00, 0xfb, 0xfa, 0x33, 0x41, 0x00,
+0xdf, 0xde, 0x34, 0x42, 0x00, 0xf7, 0xf6, 0xd3, 0x35, 0xc2, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0xc6,
+0x35, 0x56, 0x02, 0x00, 0xd8, 0xd6, 0xd4, 0x00, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x02,
+0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00, 0x00, 0x9b, 0x08, 0x02, 0x03, 0x00, 0x00, 0x15,
+0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x10, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x42, 0x07, 0x00,
+0x00, 0xa0, 0x13, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x26, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x36, 0x03,
+0x0f, 0x00, 0x00, 0x00, 0x7d, 0x56, 0x02, 0x07, 0x00, 0xa0, 0xa0, 0x00, 0x02, 0x01, 0x00, 0x21,
+0x31, 0x03, 0x01, 0x00, 0x22, 0x32, 0x04, 0x02, 0x04, 0x2f, 0x33, 0xa0, 0x05, 0x02, 0x04, 0x3b,
+0x34, 0xa0, 0x06, 0x02, 0x00, 0x3a, 0x35, 0x9b, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x02, 0x00,
+0x2e, 0x37, 0x15, 0x09, 0x02, 0x04, 0x3f, 0x38, 0xa0, 0x0a, 0x01, 0x00, 0x28, 0x39, 0x0b, 0x01,
+0x00, 0x29, 0x30, 0x0d, 0x01, 0x00, 0x2b, 0x3d, 0x10, 0x42, 0x04, 0x85, 0x8f, 0xa0, 0x11, 0x41,
+0x00, 0x91, 0x92, 0x12, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x13, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x1a,
+0x41, 0x00, 0x8d, 0xad, 0x1b, 0x01, 0x00, 0x9d, 0xa9, 0x26, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x27,
+0x41, 0x00, 0x97, 0xa7, 0x28, 0x41, 0x00, 0x82, 0x90, 0x2a, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x7c,
+0x2d, 0x41, 0x00, 0x96, 0xa6, 0x33, 0x41, 0x00, 0x87, 0x80, 0x34, 0x41, 0x00, 0x9e, 0x9f, 0x35,
+0x41, 0x00, 0x8a, 0x8b, 0x56, 0x02, 0x07, 0x00, 0xa0, 0xa0, 0x00, 0x04, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x03, 0x00, 0x00, 0x9b, 0x08, 0x02, 0x03,
+0x00, 0x00, 0x15, 0x09, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x10, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x12,
+0x42, 0x07, 0x00, 0x00, 0xa0, 0x13, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x36, 0x03, 0x0f, 0x00, 0x00,
+0x00, 0x7f, 0x56, 0x02, 0x06, 0xfd, 0xa0, 0xa0, 0x00, 0x02, 0x01, 0x00, 0x21, 0x31, 0x03, 0x01,
+0x00, 0x22, 0x32, 0x04, 0x02, 0x04, 0x2f, 0x33, 0xa0, 0x05, 0x02, 0x04, 0x3b, 0x34, 0xa0, 0x06,
+0x02, 0x00, 0x3a, 0x35, 0x9b, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x02, 0x00, 0x2e, 0x37, 0x15,
+0x09, 0x02, 0x04, 0x3f, 0x38, 0xa0, 0x0a, 0x01, 0x00, 0x28, 0x39, 0x0b, 0x01, 0x00, 0x29, 0x30,
+0x0d, 0x01, 0x00, 0x2b, 0x3d, 0x10, 0x42, 0x04, 0xd0, 0xb5, 0xa0, 0x11, 0x41, 0x00, 0xd8, 0xcf,
+0x12, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x13, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x1a, 0x41, 0x00, 0xd4,
+0xbd, 0x1b, 0x10, 0x00, 0x00, 0x27, 0x41, 0x00, 0xd6, 0xc6, 0x28, 0x41, 0x00, 0xd3, 0xb8, 0x2a,
+0x03, 0x0f, 0x00, 0x00, 0x00, 0x7e, 0x2d, 0x41, 0x00, 0xd7, 0xc7, 0x33, 0x41, 0x00, 0xd1, 0xb6,
+0x34, 0x41, 0x00, 0xd5, 0xbe, 0x35, 0x41, 0x00, 0xd2, 0xb7, 0x56, 0x02, 0x07, 0x00, 0xa0, 0xa0,
+0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x03, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x07,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x08, 0x02, 0x03, 0x00, 0x00, 0x15, 0x09, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0d, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x10, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x11, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x12,
+0x42, 0x07, 0x00, 0x00, 0xa0, 0x13, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x1d, 0x03, 0x0f, 0x00, 0x00,
+0x00, 0x82, 0x1f, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x26, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x31, 0x42,
+0x07, 0x00, 0x00, 0xa0, 0x34, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x35, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x36, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x81, 0x56, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x21,
+0x31, 0xa0, 0x03, 0x02, 0x04, 0x22, 0x32, 0xa0, 0x04, 0x02, 0x04, 0x2f, 0x33, 0xa0, 0x05, 0x02,
+0x04, 0x3b, 0x34, 0xa0, 0x06, 0x02, 0x04, 0x3a, 0x35, 0xa0, 0x07, 0x02, 0x04, 0x2c, 0x36, 0xa0,
+0x08, 0x02, 0x00, 0x2e, 0x37, 0x15, 0x09, 0x02, 0x04, 0x3f, 0x38, 0xa0, 0x0a, 0x02, 0x04, 0x28,
+0x39, 0xa0, 0x0b, 0x02, 0x04, 0x29, 0x30, 0xa0, 0x0d, 0x02, 0x04, 0x2b, 0x3d, 0xa0, 0x10, 0x42,
+0x04, 0xdd, 0xdc, 0xa0, 0x11, 0x42, 0x04, 0xfd, 0xfc, 0xa0, 0x12, 0x42, 0x07, 0x00, 0x00, 0xa0,
+0x13, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x1a, 0x41, 0x00, 0xf5, 0xf4, 0x1b, 0x10, 0x00, 0x00, 0x1d,
+0x03, 0x0f, 0x00, 0x00, 0x00, 0x82, 0x1f, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x26, 0x42, 0x07, 0x00,
+0x00, 0xa0, 0x27, 0x41, 0x00, 0xf9, 0xf8, 0x28, 0x41, 0x00, 0xf3, 0xf2, 0x2a, 0x03, 0x0f, 0x00,
+0x00, 0x00, 0x80, 0x2d, 0x41, 0x00, 0xfb, 0xfa, 0x31, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x33, 0x41,
+0x00, 0xdf, 0xde, 0x34, 0x42, 0x04, 0xf7, 0xf6, 0xa0, 0x35, 0xc2, 0x04, 0xf1, 0x35, 0xf0, 0x00,
+0xa0, 0x35, 0x56, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x21, 0x31, 0xa0, 0x03, 0x02, 0x04,
+0x22, 0x32, 0xa0, 0x04, 0x02, 0x04, 0x2f, 0x33, 0xa0, 0x05, 0x02, 0x04, 0x3b, 0x34, 0xa0, 0x06,
+0x02, 0x04, 0x3a, 0x35, 0xa0, 0x07, 0x02, 0x04, 0x2c, 0x36, 0xa0, 0x08, 0x02, 0x00, 0x2e, 0x37,
+0x15, 0x09, 0x02, 0x04, 0x3f, 0x38, 0xa0, 0x0a, 0x02, 0x04, 0x28, 0x39, 0xa0, 0x0b, 0x02, 0x04,
+0x29, 0x30, 0xa0, 0x0d, 0x02, 0x04, 0x2b, 0x3d, 0xa0, 0x10, 0x42, 0x04, 0xef, 0x9f, 0xa0, 0x11,
+0x42, 0x04, 0xa6, 0x86, 0xa0, 0x12, 0x42, 0x04, 0xa5, 0x85, 0xa0, 0x13, 0xc2, 0x04, 0xe0, 0x00,
+0x90, 0x13, 0xa0, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xeb, 0x9b, 0x16, 0x41,
+0x00, 0xe3, 0x93, 0x17, 0x41, 0x00, 0xa8, 0x88, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00,
+0xaf, 0x8f, 0x1a, 0x41, 0x00, 0xea, 0x9a, 0x1b, 0x41, 0x00, 0xec, 0x9c, 0x1e, 0x41, 0x00, 0xa0,
+0x80, 0x1f, 0x42, 0x04, 0xe1, 0x91, 0xa0, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4,
+0x94, 0x22, 0x41, 0x00, 0xa3, 0x83, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24, 0x41, 0x00, 0xa9, 0x89,
+0x25, 0x41, 0x00, 0xaa, 0x8a, 0x26, 0x42, 0x04, 0xab, 0x8b, 0xa0, 0x27, 0x41, 0x00, 0xee, 0x9e,
+0x28, 0x41, 0x00, 0xed, 0x9d, 0x2a, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x80, 0x2c, 0x41, 0x00, 0xa7,
+0x87, 0x2d, 0x41, 0x00, 0xe9, 0x99, 0x2e, 0x41, 0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2, 0x82,
+0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x42, 0x04, 0xad, 0x8d, 0xa0, 0x32, 0x41, 0x00, 0xac, 0x8c,
+0x33, 0x41, 0x00, 0xe7, 0x97, 0x34, 0x42, 0x04, 0xe8, 0x98, 0xa0, 0x35, 0x10, 0x00, 0x00, 0x36,
+0x03, 0x0f, 0x00, 0x00, 0x00, 0x81, 0x56, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x03, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x02, 0x07,
+0x00, 0x00, 0xa0, 0x06, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x08, 0x02, 0x03, 0x00, 0x00, 0x15, 0x09,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x0b, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x10, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x42, 0x07,
+0x00, 0x00, 0xa0, 0x13, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x1d, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x85,
+0x1f, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x26, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x31, 0x42, 0x07, 0x00,
+0x00, 0xa0, 0x36, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x84, 0x56, 0x02, 0x07, 0x00, 0xa0, 0xa0, 0x00,
+0x02, 0x02, 0x04, 0x21, 0x31, 0xa0, 0x03, 0x02, 0x04, 0x22, 0x32, 0xa0, 0x04, 0x02, 0x04, 0x2f,
+0x33, 0xa0, 0x05, 0x02, 0x04, 0x3b, 0x34, 0xa0, 0x06, 0x02, 0x04, 0x3a, 0x35, 0xa0, 0x07, 0x01,
+0x00, 0x2c, 0x36, 0x08, 0x02, 0x00, 0x2e, 0x37, 0x15, 0x09, 0x02, 0x04, 0x3f, 0x38, 0xa0, 0x0a,
+0x02, 0x04, 0x28, 0x39, 0xa0, 0x0b, 0x02, 0x04, 0x29, 0x30, 0xa0, 0x0d, 0x02, 0x04, 0x2b, 0x3d,
+0xa0, 0x10, 0x42, 0x04, 0xd0, 0xb5, 0xa0, 0x11, 0x41, 0x00, 0xd8, 0xcf, 0x12, 0x42, 0x07, 0x00,
+0x00, 0xa0, 0x13, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x1a, 0x41, 0x00, 0xd4, 0xbd, 0x1b, 0x01, 0x00,
+0xf4, 0xf5, 0x1d, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x85, 0x1f, 0x42, 0x07, 0x00, 0x00, 0xa0, 0x26,
+0x42, 0x07, 0x00, 0x00, 0xa0, 0x27, 0x41, 0x00, 0xd6, 0xc6, 0x28, 0x41, 0x00, 0xd3, 0xb8, 0x2a,
+0x03, 0x0f, 0x00, 0x00, 0x00, 0x83, 0x2d, 0x41, 0x00, 0xd7, 0xc7, 0x31, 0x42, 0x07, 0x00, 0x00,
+0xa0, 0x33, 0x41, 0x00, 0xd1, 0xb6, 0x34, 0x41, 0x00, 0xd5, 0xbe, 0x35, 0x41, 0x00, 0xd2, 0xb7,
+0x56, 0x02, 0x07, 0x00, 0xa0, 0xa0, 0x00, 0x02, 0x02, 0x04, 0x21, 0x31, 0xa0, 0x03, 0x02, 0x04,
+0x22, 0x32, 0xa0, 0x04, 0x02, 0x04, 0x2f, 0x33, 0xa0, 0x05, 0x02, 0x04, 0x3b, 0x34, 0xa0, 0x06,
+0x02, 0x04, 0x3a, 0x35, 0xa0, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x02, 0x00, 0x2e, 0x37, 0x15,
+0x09, 0x02, 0x04, 0x3f, 0x38, 0xa0, 0x0a, 0x02, 0x04, 0x28, 0x39, 0xa0, 0x0b, 0x02, 0x04, 0x29,
+0x30, 0xa0, 0x0d, 0x02, 0x04, 0x2b, 0x3d, 0xa0, 0x10, 0x42, 0x04, 0xef, 0x9f, 0xa0, 0x11, 0x41,
+0x00, 0xa6, 0x86, 0x12, 0x42, 0x04, 0xa5, 0x85, 0xa0, 0x13, 0xc2, 0x04, 0xe0, 0x00, 0x90, 0x13,
+0xa0, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xeb, 0x9b, 0x16, 0x41, 0x00, 0xe3,
+0x93, 0x17, 0x41, 0x00, 0xa8, 0x88, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f,
+0x1a, 0x41, 0x00, 0xea, 0x9a, 0x1b, 0x41, 0x00, 0xec, 0x9c, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f,
+0x42, 0x04, 0xe1, 0x91, 0xa0, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22,
+0x41, 0x00, 0xa3, 0x83, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41,
+0x00, 0xaa, 0x8a, 0x26, 0x42, 0x04, 0xab, 0x8b, 0xa0, 0x27, 0x41, 0x00, 0xee, 0x9e, 0x28, 0x41,
+0x00, 0xed, 0x9d, 0x2a, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x83, 0x2c, 0x41, 0x00, 0xa7, 0x87, 0x2d,
+0x41, 0x00, 0xe9, 0x99, 0x2e, 0x41, 0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41,
+0x00, 0xa1, 0x81, 0x31, 0x42, 0x04, 0xad, 0x8d, 0xa0, 0x32, 0x41, 0x00, 0xac, 0x8c, 0x33, 0x41,
+0x00, 0xe7, 0x97, 0x34, 0x41, 0x00, 0xe8, 0x98, 0x35, 0xc1, 0x00, 0xf1, 0x35, 0xf0, 0x00, 0x36,
+0x03, 0x0f, 0x00, 0x00, 0x00, 0x84, 0x56, 0x02, 0x07, 0x00, 0xa0, 0xa0, 0x00, 0x80, 0x8a, 0x04,
+0x04, 0x00, 0x00, 0x4c, 0x56, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x07, 0x03, 0xd1, 0x00, 0x1f, 0x01, 0x00, 0x00, 0x02, 0x03, 0x64, 0x01, 0xce, 0x01, 0x00,
+0x00, 0x05, 0x03, 0xef, 0x01, 0x3d, 0x02, 0x00, 0x00, 0x5d, 0x04, 0x82, 0x02, 0xd0, 0x02, 0x00,
+0x00, 0x62, 0xef, 0x15, 0x03, 0x00, 0x00, 0x00, 0x00, 0x62, 0xef, 0xa0, 0x03, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0xff, 0x03,
+0x02, 0x03, 0x00, 0x00, 0xae, 0x04, 0x02, 0x03, 0x00, 0x00, 0xaf, 0x09, 0x03, 0x07, 0x00, 0x00,
+0x00, 0x9e, 0x12, 0x43, 0x00, 0x65, 0x45, 0x89, 0xed, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x43,
+0x03, 0x00, 0x00, 0x8c, 0xa1, 0x18, 0x43, 0x00, 0x6f, 0x4f, 0xe4, 0xe5, 0x1e, 0x43, 0x00, 0x61,
+0x41, 0x83, 0xa0, 0x1f, 0x41, 0x00, 0x73, 0x53, 0x22, 0x43, 0x03, 0x00, 0x00, 0x85, 0x95, 0x28,
+0x03, 0x0f, 0x00, 0x00, 0xca, 0xcb, 0x29, 0x02, 0x03, 0x00, 0xc9, 0x2d, 0x2b, 0x02, 0x01, 0xc8,
+0x7c, 0x5c, 0x2c, 0x41, 0x00, 0x7a, 0x5a, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41, 0x00, 0x6e,
+0x4e, 0x39, 0x00, 0x00, 0x20, 0x00, 0x05, 0x03, 0x07, 0x00, 0x00, 0x00, 0xf5, 0x06, 0x03, 0x07,
+0x00, 0x00, 0x00, 0xf8, 0x08, 0x03, 0x07, 0x00, 0x00, 0x00, 0xf1, 0x13, 0x43, 0x03, 0x00, 0x00,
+0x8b, 0x8a, 0x16, 0x43, 0x03, 0x00, 0x00, 0xd7, 0xc7, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xd5, 0xbe,
+0x25, 0x43, 0x03, 0x00, 0x00, 0xe9, 0xe8, 0x26, 0x43, 0x03, 0x00, 0x00, 0xeb, 0xea, 0x2c, 0x43,
+0x03, 0x00, 0x00, 0xd8, 0xcf, 0x2e, 0x43, 0x03, 0x00, 0x00, 0xd1, 0xb6, 0x31, 0x43, 0x03, 0x00,
+0x00, 0xec, 0xee, 0x00, 0xf8, 0x07, 0x61, 0x86, 0x65, 0xd3, 0x7a, 0xa4, 0x41, 0x8f, 0x45, 0xb8,
+0x5a, 0xa3, 0x20, 0xf8, 0x7e, 0x03, 0x6f, 0xe4, 0x4f, 0xe5, 0x20, 0x7e, 0xef, 0x0d, 0x63, 0x87,
+0x65, 0x82, 0x6e, 0xe7, 0x6f, 0xa2, 0x73, 0x98, 0x7a, 0xa5, 0x43, 0x80, 0x45, 0x90, 0x4e, 0xe3,
+0x4f, 0xe0, 0x53, 0x97, 0x5a, 0x8d, 0x20, 0xef, 0x22, 0x07, 0x61, 0x84, 0x6f, 0x94, 0x75, 0x81,
+0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0x05, 0x03, 0x07, 0x00, 0x00, 0x00, 0x15,
+0x06, 0x03, 0x07, 0x00, 0x00, 0x00, 0xf8, 0x08, 0x03, 0x07, 0x00, 0x00, 0x00, 0xf1, 0x09, 0x03,
+0x0f, 0x00, 0x00, 0x00, 0xa0, 0x12, 0x43, 0x03, 0x00, 0x00, 0x88, 0x89, 0x16, 0x43, 0x03, 0x00,
+0x00, 0x96, 0xa6, 0x18, 0x43, 0x03, 0x00, 0x00, 0x93, 0x95, 0x1f, 0x43, 0x03, 0x00, 0x00, 0x9e,
+0x9f, 0x22, 0x43, 0x03, 0x00, 0x00, 0x98, 0xa8, 0x25, 0x43, 0x03, 0x00, 0x00, 0xa2, 0xa3, 0x26,
+0x43, 0x03, 0x00, 0x00, 0x86, 0x9c, 0x28, 0x03, 0x0f, 0x00, 0x00, 0xa0, 0xca, 0x2c, 0x43, 0x03,
+0x00, 0x00, 0x91, 0x92, 0x2e, 0x43, 0x03, 0x00, 0x00, 0x87, 0x80, 0x31, 0x43, 0x03, 0x00, 0x00,
+0xa4, 0xa5, 0x00, 0xfa, 0x03, 0x65, 0x82, 0x45, 0x90, 0x20, 0xfa, 0x7e, 0x03, 0x6f, 0x93, 0x4f,
+0x95, 0x20, 0x7e, 0x22, 0x07, 0x61, 0x84, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x4f, 0x99, 0x55,
+0x9a, 0x20, 0x22, 0x00, 0x05, 0x03, 0x07, 0x00, 0x00, 0x00, 0xbe, 0x06, 0x03, 0x07, 0x00, 0x00,
+0x00, 0xd0, 0x08, 0x03, 0x07, 0x00, 0x00, 0x00, 0xb5, 0x13, 0x43, 0x03, 0x00, 0x00, 0x8b, 0x8a,
+0x16, 0x43, 0x03, 0x00, 0x00, 0xfb, 0xfa, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xf7, 0xf6, 0x25, 0x43,
+0x03, 0x00, 0x00, 0xe9, 0xe8, 0x26, 0x43, 0x03, 0x00, 0x00, 0xeb, 0xea, 0x2c, 0x43, 0x03, 0x00,
+0x00, 0xfd, 0xfc, 0x2e, 0x43, 0x03, 0x00, 0x00, 0xdf, 0xde, 0x31, 0x43, 0x03, 0x00, 0x00, 0xec,
+0xee, 0x00, 0xd3, 0x07, 0x61, 0x86, 0x65, 0xf3, 0x7a, 0xa4, 0x41, 0x8f, 0x45, 0xf2, 0x5a, 0xa3,
+0x20, 0xd3, 0x7e, 0x03, 0x6f, 0xe4, 0x4f, 0xe5, 0x20, 0x7e, 0xef, 0x0d, 0x63, 0x87, 0x65, 0x82,
+0x6e, 0xe7, 0x6f, 0xa2, 0x73, 0x98, 0x7a, 0xa5, 0x43, 0x80, 0x45, 0x90, 0x4e, 0xe3, 0x4f, 0xe0,
+0x53, 0x97, 0x5a, 0x8d, 0x20, 0xef, 0x22, 0x07, 0x61, 0x84, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e,
+0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0x05, 0x03, 0x07, 0x00, 0x00, 0x00, 0x15, 0x06, 0x03,
+0x07, 0x00, 0x00, 0x00, 0xf8, 0x08, 0x03, 0x07, 0x00, 0x00, 0x00, 0xf1, 0x13, 0x43, 0x03, 0x00,
+0x00, 0xfb, 0xfc, 0x16, 0x43, 0x03, 0x00, 0x00, 0x96, 0xea, 0x1f, 0x43, 0x03, 0x00, 0x00, 0xe7,
+0xe6, 0x25, 0x43, 0x03, 0x00, 0x00, 0xe9, 0xe8, 0x26, 0x43, 0x03, 0x00, 0x00, 0xec, 0x9c, 0x2c,
+0x43, 0x03, 0x00, 0x00, 0xa7, 0xa6, 0x2e, 0x43, 0x03, 0x00, 0x00, 0x9f, 0xac, 0x31, 0x43, 0x03,
+0x00, 0x00, 0xef, 0xee, 0x00, 0xf8, 0x07, 0x61, 0x86, 0x65, 0x8a, 0x7a, 0x92, 0x41, 0x8f, 0x45,
+0xaa, 0x5a, 0x91, 0x20, 0xf8, 0x7e, 0x03, 0x6f, 0xe4, 0x4f, 0xe5, 0x20, 0x7e, 0x27, 0x0d, 0x63,
+0x87, 0x65, 0x82, 0x6e, 0x9b, 0x6f, 0xa2, 0x73, 0x98, 0x7a, 0xab, 0x43, 0x80, 0x45, 0x90, 0x4e,
+0xe3, 0x4f, 0xe0, 0x53, 0x97, 0x5a, 0x8d, 0x20, 0x27, 0x22, 0x07, 0x61, 0x84, 0x6f, 0x94, 0x75,
+0x81, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0x03, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x04, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x05, 0x03, 0x07, 0x00, 0x00, 0x00, 0x15, 0x09, 0x03, 0x0f,
+0x00, 0x00, 0x00, 0xa0, 0x12, 0xc3, 0x00, 0x65, 0x12, 0x45, 0x12, 0xf1, 0x12, 0xf0, 0x00, 0x16,
+0x43, 0x03, 0x00, 0x00, 0xdd, 0xde, 0x17, 0x43, 0x03, 0x00, 0x00, 0xd8, 0xd7, 0x18, 0x43, 0x0f,
+0x00, 0x00, 0xa0, 0xa0, 0x1e, 0x43, 0x03, 0x00, 0x00, 0xc6, 0xb5, 0x1f, 0x43, 0x03, 0x00, 0x00,
+0xfd, 0xd0, 0x22, 0x43, 0x03, 0x00, 0x00, 0xd6, 0xf2, 0x25, 0x43, 0x03, 0x00, 0x00, 0xf3, 0xf4,
+0x26, 0x43, 0x03, 0x00, 0x00, 0xf5, 0xf6, 0x28, 0x03, 0x0f, 0x00, 0x00, 0xa0, 0xa0, 0x29, 0x01,
+0x01, 0x00, 0x7e, 0x2b, 0x00, 0x01, 0xa0, 0x2c, 0x43, 0x03, 0x00, 0x00, 0xf7, 0xf8, 0x2e, 0x43,
+0x03, 0x00, 0x00, 0xd2, 0xd3, 0x31, 0x43, 0x03, 0x00, 0x00, 0xb7, 0xfc, 0x36, 0x04, 0x1f, 0x00,
+0x00, 0x00, 0x00, 0x7d, 0x00, 0x03, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x04, 0x02, 0x07, 0x00, 0x00,
+0xa0, 0x05, 0x03, 0x07, 0x00, 0x00, 0x00, 0x15, 0x09, 0x03, 0x0f, 0x00, 0x00, 0x00, 0xa0, 0x10,
+0x41, 0x00, 0xef, 0x9f, 0x11, 0x41, 0x00, 0xe8, 0x98, 0x12, 0x43, 0x0c, 0xa5, 0x85, 0xa0, 0xa0,
+0x13, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xeb,
+0x9b, 0x16, 0x41, 0x00, 0xe3, 0x93, 0x17, 0x43, 0x0c, 0xa8, 0x88, 0xa0, 0xa0, 0x18, 0x43, 0x0c,
+0xae, 0x8e, 0xa0, 0xa0, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a, 0x43, 0x00, 0xed, 0x9d, 0x5b, 0x7b,
+0x1b, 0x43, 0x00, 0xee, 0x9e, 0x5d, 0x7d, 0x1e, 0x43, 0x0c, 0xa0, 0x80, 0xa0, 0xa0, 0x1f, 0x41,
+0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x43, 0x0c,
+0xa3, 0x83, 0xa0, 0xa0, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24, 0x41, 0x00, 0xa9, 0x89, 0x25, 0x41,
+0x00, 0xaa, 0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x27, 0x41, 0x00, 0xe7, 0x97, 0x28, 0x43, 0x00,
+0xec, 0x9c, 0x27, 0x22, 0x29, 0x43, 0x00, 0xea, 0x9a, 0x60, 0x7e, 0x2a, 0x04, 0x1f, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0x2b, 0x43, 0x00, 0xe9, 0x99, 0x5c, 0x7c, 0x2c, 0x41, 0x00, 0xa7, 0x87, 0x2d,
+0x41, 0x00, 0xa6, 0x86, 0x2e, 0x41, 0x00, 0xe6, 0x96, 0x2f, 0x41, 0x00, 0xa2, 0x82, 0x30, 0x41,
+0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x41, 0x00, 0xac, 0x8c, 0x33, 0x02, 0x03,
+0x00, 0x00, 0x3b, 0x34, 0x02, 0x03, 0x00, 0x00, 0x3a, 0x00, 0x0c, 0xfd, 0x05, 0x04, 0xc7, 0x01,
+0x4c, 0x56, 0x0c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03,
+0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03,
+0x7a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03,
+0x5e, 0x02, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x04,
+0x4a, 0x03, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x04, 0x58, 0x03, 0x00, 0x00, 0x00, 0x00, 0x62, 0xef,
+0x37, 0x04, 0x00, 0x00, 0x00, 0x00, 0x62, 0xef, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x62, 0xef,
+0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x01,
+0x00, 0xf8, 0xa7, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x03, 0x01, 0x01,
+0x00, 0xae, 0x04, 0x01, 0x01, 0x00, 0xaf, 0x05, 0x01, 0x01, 0x00, 0xf5, 0x07, 0x01, 0x01, 0x00,
+0x2f, 0x09, 0x01, 0x01, 0x00, 0x9e, 0x0d, 0x41, 0x00, 0x66, 0x46, 0x10, 0x41, 0x00, 0xd7, 0xc7,
+0x11, 0x41, 0x00, 0x67, 0x47, 0x12, 0x41, 0x00, 0x6a, 0x4a, 0x13, 0x43, 0x03, 0x00, 0x00, 0x8b,
+0x8a, 0x14, 0x41, 0x00, 0x6d, 0x4d, 0x15, 0x41, 0x00, 0x76, 0x56, 0x16, 0x41, 0x00, 0x6e, 0x4e,
+0x17, 0x41, 0x00, 0x7a, 0x5a, 0x18, 0x41, 0x00, 0x89, 0xed, 0x19, 0x41, 0x00, 0xd1, 0xb6, 0x1a,
+0x41, 0x00, 0xd8, 0xcf, 0x1b, 0x41, 0x00, 0x68, 0x48, 0x1e, 0x41, 0x00, 0xd5, 0xbe, 0x1f, 0x41,
+0x00, 0x75, 0x55, 0x20, 0x41, 0x00, 0x73, 0x53, 0x21, 0x41, 0x00, 0x69, 0x49, 0x22, 0x41, 0x00,
+0x6c, 0x4c, 0x23, 0x41, 0x00, 0x64, 0x44, 0x24, 0x41, 0x00, 0x61, 0x41, 0x25, 0x41, 0x00, 0x74,
+0x54, 0x26, 0x41, 0x00, 0x65, 0x45, 0x27, 0x41, 0x00, 0x63, 0x43, 0x28, 0x01, 0x00, 0x27, 0xf8,
+0x29, 0x01, 0x00, 0x2d, 0x3f, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x41, 0x00,
+0xe9, 0xe8, 0x2c, 0x41, 0x00, 0xec, 0xee, 0x2d, 0x41, 0x00, 0x62, 0x42, 0x2e, 0x41, 0x00, 0x8c,
+0xa1, 0x2f, 0x41, 0x00, 0x6b, 0x4b, 0x30, 0x41, 0x00, 0x70, 0x50, 0x31, 0x43, 0x00, 0x6f, 0x4f,
+0x93, 0xe2, 0x32, 0x41, 0x00, 0x83, 0xa0, 0x33, 0x01, 0x01, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x00,
+0x3a, 0x35, 0x41, 0x00, 0xeb, 0xea, 0x56, 0x41, 0x00, 0x85, 0x95, 0x00, 0x2b, 0x01, 0x03, 0x00,
+0xa0, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x03, 0x01, 0x01, 0x00, 0xae, 0x04,
+0x01, 0x01, 0x00, 0xaf, 0x05, 0x01, 0x01, 0x00, 0x15, 0x07, 0x01, 0x01, 0x00, 0x2f, 0x09, 0x01,
+0x03, 0x00, 0xa0, 0x0d, 0x41, 0x00, 0x66, 0x46, 0x10, 0x41, 0x00, 0x96, 0xa6, 0x11, 0x41, 0x00,
+0x67, 0x47, 0x12, 0x41, 0x00, 0x6a, 0x4a, 0x14, 0x41, 0x00, 0x6d, 0x4d, 0x15, 0x41, 0x00, 0x76,
+0x56, 0x16, 0x41, 0x00, 0x6e, 0x4e, 0x17, 0x41, 0x00, 0x7a, 0x5a, 0x18, 0x41, 0x00, 0x88, 0x89,
+0x19, 0x41, 0x00, 0x87, 0x80, 0x1a, 0x41, 0x00, 0x91, 0x92, 0x1b, 0x41, 0x00, 0x68, 0x48, 0x1e,
+0x41, 0x00, 0x9e, 0x9f, 0x1f, 0x41, 0x00, 0x75, 0x55, 0x20, 0x41, 0x00, 0x73, 0x53, 0x21, 0x41,
+0x00, 0x69, 0x49, 0x22, 0x41, 0x00, 0x6c, 0x4c, 0x23, 0x41, 0x00, 0x64, 0x44, 0x24, 0x41, 0x00,
+0x61, 0x41, 0x25, 0x41, 0x00, 0x74, 0x54, 0x26, 0x41, 0x00, 0x65, 0x45, 0x27, 0x41, 0x00, 0x63,
+0x43, 0x28, 0x01, 0x00, 0x27, 0xf8, 0x29, 0x01, 0x00, 0x2d, 0x3f, 0x2a, 0x04, 0x1f, 0x00, 0x00,
+0x00, 0x00, 0x7a, 0x2b, 0x41, 0x00, 0xa2, 0xa3, 0x2c, 0x41, 0x00, 0xa4, 0xa5, 0x2d, 0x41, 0x00,
+0x62, 0x42, 0x2e, 0x41, 0x00, 0x8c, 0xa1, 0x2f, 0x41, 0x00, 0x6b, 0x4b, 0x30, 0x41, 0x00, 0x70,
+0x50, 0x31, 0x41, 0x00, 0x6f, 0x4f, 0x32, 0x41, 0x00, 0x83, 0xa0, 0x33, 0x01, 0x01, 0x00, 0x3b,
+0x34, 0x01, 0x01, 0x00, 0x3a, 0x35, 0x41, 0x00, 0x86, 0x9c, 0x56, 0x41, 0x00, 0x98, 0xa8, 0x00,
+0x2b, 0x00, 0x00, 0xd0, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x03, 0x01, 0x01,
+0x00, 0xae, 0x04, 0x01, 0x01, 0x00, 0xaf, 0x05, 0x01, 0x01, 0x00, 0xbe, 0x07, 0x01, 0x01, 0x00,
+0x2f, 0x09, 0x01, 0x01, 0x00, 0x9e, 0x0d, 0x41, 0x00, 0x66, 0x46, 0x10, 0x41, 0x00, 0xfb, 0xfa,
+0x11, 0x41, 0x00, 0x67, 0x47, 0x12, 0x41, 0x00, 0x6a, 0x4a, 0x13, 0x43, 0x03, 0x00, 0x00, 0x8b,
+0x8a, 0x14, 0x41, 0x00, 0x6d, 0x4d, 0x15, 0x41, 0x00, 0x76, 0x56, 0x16, 0x41, 0x00, 0x6e, 0x4e,
+0x17, 0x41, 0x00, 0x7a, 0x5a, 0x18, 0x41, 0x00, 0x89, 0xed, 0x19, 0x41, 0x00, 0xdf, 0xde, 0x1a,
+0x41, 0x00, 0xfd, 0xfc, 0x1b, 0x41, 0x00, 0x68, 0x48, 0x1e, 0x41, 0x00, 0xf7, 0xf6, 0x1f, 0x41,
+0x00, 0x75, 0x55, 0x20, 0x41, 0x00, 0x73, 0x53, 0x21, 0x41, 0x00, 0x69, 0x49, 0x22, 0x41, 0x00,
+0x6c, 0x4c, 0x23, 0x41, 0x00, 0x64, 0x44, 0x24, 0x41, 0x00, 0x61, 0x41, 0x25, 0x41, 0x00, 0x74,
+0x54, 0x26, 0x41, 0x00, 0x65, 0x45, 0x27, 0x41, 0x00, 0x63, 0x43, 0x28, 0x01, 0x00, 0x27, 0xd0,
+0x29, 0x01, 0x00, 0x2d, 0x3f, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x2b, 0x41, 0x00,
+0xe9, 0xe8, 0x2c, 0x41, 0x00, 0xec, 0xee, 0x2d, 0x41, 0x00, 0x62, 0x42, 0x2e, 0x41, 0x00, 0x8c,
+0xa1, 0x2f, 0x41, 0x00, 0x6b, 0x4b, 0x30, 0x41, 0x00, 0x70, 0x50, 0x31, 0x43, 0x00, 0x6f, 0x4f,
+0x93, 0xe2, 0x32, 0x41, 0x00, 0x83, 0xa0, 0x33, 0x01, 0x01, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x00,
+0x3a, 0x35, 0x41, 0x00, 0xeb, 0xea, 0x56, 0x41, 0x00, 0x85, 0x95, 0x00, 0x2b, 0x01, 0x03, 0x00,
+0xa0, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x03, 0x01, 0x01, 0x00, 0xae, 0x04,
+0x01, 0x01, 0x00, 0xaf, 0x05, 0x01, 0x01, 0x00, 0x15, 0x07, 0x01, 0x01, 0x00, 0x2f, 0x09, 0x01,
+0x01, 0x00, 0x9e, 0x0d, 0x41, 0x00, 0x66, 0x46, 0x10, 0x41, 0x00, 0x96, 0xea, 0x11, 0x41, 0x00,
+0x67, 0x47, 0x12, 0x41, 0x00, 0x6a, 0x4a, 0x13, 0x43, 0x00, 0x72, 0x52, 0xfb, 0xfc, 0x14, 0x41,
+0x00, 0x6d, 0x4d, 0x15, 0x41, 0x00, 0x76, 0x56, 0x16, 0x41, 0x00, 0x6e, 0x4e, 0x17, 0x41, 0x00,
+0x7a, 0x5a, 0x18, 0x41, 0x00, 0x89, 0xed, 0x19, 0x41, 0x00, 0x9f, 0xac, 0x1a, 0x41, 0x00, 0xa7,
+0xa6, 0x1b, 0x41, 0x00, 0x68, 0x48, 0x1e, 0x41, 0x00, 0xe7, 0xe6, 0x1f, 0x41, 0x00, 0x75, 0x55,
+0x20, 0x41, 0x00, 0x73, 0x53, 0x21, 0x41, 0x00, 0x69, 0x49, 0x22, 0x41, 0x00, 0x6c, 0x4c, 0x23,
+0x41, 0x00, 0x64, 0x44, 0x24, 0x41, 0x00, 0x61, 0x41, 0x25, 0x41, 0x00, 0x74, 0x54, 0x26, 0x41,
+0x00, 0x65, 0x45, 0x27, 0x41, 0x00, 0x63, 0x43, 0x28, 0x01, 0x00, 0x27, 0xf8, 0x29, 0x01, 0x00,
+0x2d, 0x3f, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x2b, 0x41, 0x00, 0xe9, 0xe8, 0x2c,
+0x41, 0x00, 0xef, 0xee, 0x2d, 0x41, 0x00, 0x62, 0x42, 0x2e, 0x41, 0x00, 0x8c, 0xa1, 0x2f, 0x41,
+0x00, 0x6b, 0x4b, 0x30, 0x41, 0x00, 0x70, 0x50, 0x31, 0x43, 0x00, 0x6f, 0x4f, 0x93, 0xe2, 0x32,
+0x41, 0x00, 0x83, 0xa0, 0x33, 0x01, 0x01, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x00, 0x3a, 0x35, 0x41,
+0x00, 0xec, 0x9c, 0x56, 0x41, 0x00, 0x85, 0x95, 0x00, 0x1d, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x82, 0x28, 0x01, 0x03, 0x00, 0xa0, 0x2b, 0x10, 0x00, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x81, 0x00, 0x05, 0x01, 0x01, 0x00, 0x15, 0x07, 0x01, 0x01, 0x00, 0x2f, 0x0d, 0x41, 0x00,
+0x66, 0x46, 0x10, 0x41, 0x00, 0xdd, 0xde, 0x11, 0x41, 0x00, 0x67, 0x47, 0x12, 0x41, 0x00, 0x6a,
+0x4a, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41, 0x00, 0x6d, 0x4d, 0x15, 0x41, 0x00, 0x76, 0x56,
+0x16, 0x41, 0x00, 0x6e, 0x4e, 0x17, 0x41, 0x00, 0x7a, 0x5a, 0x18, 0xc1, 0x00, 0xf1, 0x18, 0xf0,
+0x00, 0x19, 0x41, 0x00, 0xd2, 0xd3, 0x1a, 0x41, 0x00, 0xf7, 0xf8, 0x1b, 0x41, 0x00, 0x68, 0x48,
+0x1d, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x82, 0x1e, 0x41, 0x00, 0xfd, 0xd0, 0x1f, 0x41, 0x00,
+0x75, 0x55, 0x20, 0x41, 0x00, 0x73, 0x53, 0x21, 0x41, 0x00, 0x69, 0x49, 0x22, 0x41, 0x00, 0x6c,
+0x4c, 0x23, 0x41, 0x00, 0x64, 0x44, 0x24, 0x41, 0x00, 0x61, 0x41, 0x25, 0x41, 0x00, 0x74, 0x54,
+0x26, 0x41, 0x00, 0x65, 0x45, 0x27, 0x41, 0x00, 0x63, 0x43, 0x28, 0x01, 0x03, 0x00, 0xa0, 0x29,
+0x01, 0x00, 0x2d, 0x3f, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2b, 0x41, 0x00, 0xf3,
+0xf4, 0x2c, 0x41, 0x00, 0xb7, 0xfc, 0x2d, 0x41, 0x00, 0x62, 0x42, 0x2e, 0x41, 0x00, 0xd8, 0xd7,
+0x2f, 0x41, 0x00, 0x6b, 0x4b, 0x30, 0x41, 0x00, 0x70, 0x50, 0x31, 0x41, 0x00, 0x6f, 0x4f, 0x32,
+0x41, 0x00, 0xc6, 0xb5, 0x33, 0x01, 0x01, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x00, 0x3a, 0x35, 0x41,
+0x00, 0xf5, 0xf6, 0x56, 0x41, 0x00, 0xd6, 0xf2, 0x00, 0x05, 0x01, 0x01, 0x00, 0x15, 0x07, 0x01,
+0x01, 0x00, 0x2f, 0x0d, 0x41, 0x00, 0xe4, 0x94, 0x10, 0x41, 0x00, 0xee, 0x9e, 0x11, 0x41, 0x00,
+0xa3, 0x83, 0x12, 0x41, 0x00, 0xa9, 0x89, 0x13, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x13, 0x14, 0x41,
+0x00, 0xac, 0x8c, 0x15, 0x41, 0x00, 0xa2, 0x82, 0x16, 0x41, 0x00, 0xad, 0x8d, 0x17, 0x41, 0x00,
+0xa7, 0x87, 0x18, 0x41, 0x00, 0xed, 0x9d, 0x19, 0x41, 0x00, 0xe7, 0x97, 0x1a, 0x41, 0x00, 0xa6,
+0x86, 0x1b, 0x41, 0x00, 0xe5, 0x95, 0x1e, 0x41, 0x00, 0xe8, 0x98, 0x1f, 0x41, 0x00, 0xe3, 0x93,
+0x20, 0x41, 0x00, 0xe1, 0x91, 0x21, 0x41, 0x00, 0xa8, 0x88, 0x22, 0x41, 0x00, 0xab, 0x8b, 0x23,
+0x41, 0x00, 0xa4, 0x84, 0x24, 0x41, 0x00, 0xa0, 0x80, 0x25, 0x41, 0x00, 0xe2, 0x92, 0x26, 0x41,
+0x00, 0xa5, 0x85, 0x27, 0x41, 0x00, 0xe6, 0x96, 0x28, 0x01, 0x03, 0x00, 0xa0, 0x29, 0x01, 0x00,
+0x2d, 0x3f, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2b, 0x41, 0x00, 0xea, 0x9a, 0x2c,
+0x41, 0x00, 0xe9, 0x99, 0x2d, 0x41, 0x00, 0xa1, 0x81, 0x2e, 0x41, 0x00, 0xeb, 0x9b, 0x2f, 0x41,
+0x00, 0xaa, 0x8a, 0x30, 0x41, 0x00, 0xaf, 0x8f, 0x31, 0x41, 0x00, 0xae, 0x8e, 0x32, 0x41, 0x00,
+0xef, 0x9f, 0x33, 0x01, 0x01, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x00, 0x3a, 0x35, 0x41, 0x00, 0xec,
+0x9c, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0xec, 0x67, 0x03, 0x09, 0x00, 0x00,
+0x4d, 0x4b, 0x2c, 0xc1, 0x01, 0x4d, 0x4b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x68, 0x03, 0xeb, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x03, 0x2c, 0x01, 0x00,
+0x00, 0x00, 0x00, 0x57, 0x03, 0x4e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x90, 0x02, 0x00,
+0x00, 0x00, 0x00, 0x5a, 0x03, 0x25, 0x01, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x39, 0x75, 0x00, 0x02, 0x53, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00,
+0x7e, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x04, 0x09, 0x00, 0x23, 0x5e, 0x00, 0x1e, 0x07, 0x01,
+0x01, 0x00, 0x26, 0x08, 0x02, 0x01, 0x00, 0x2f, 0x60, 0x09, 0x01, 0x01, 0x00, 0x28, 0x0a, 0x01,
+0x01, 0x00, 0x29, 0x0b, 0x01, 0x01, 0x00, 0x3d, 0x0c, 0x01, 0x00, 0x27, 0x3f, 0x0d, 0x01, 0x00,
+0x2b, 0x2a, 0x10, 0x42, 0x03, 0x00, 0x00, 0x5c, 0x11, 0x42, 0x03, 0x00, 0x00, 0x7c, 0x15, 0xc5,
+0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x21, 0x42, 0x03,
+0x00, 0x00, 0x5b, 0x22, 0x42, 0x03, 0x00, 0x00, 0x5d, 0x27, 0x04, 0x0c, 0x5e, 0x5e, 0x00, 0x00,
+0x1e, 0x28, 0x01, 0x00, 0xf5, 0xf5, 0x29, 0x04, 0x0c, 0x5c, 0x7c, 0x00, 0x00, 0x1c, 0x2b, 0x01,
+0x00, 0x40, 0x40, 0x2c, 0xc5, 0x00, 0x79, 0x15, 0x59, 0x15, 0x00, 0x15, 0x00, 0x15, 0x19, 0x15,
+0x00, 0x15, 0x2f, 0x42, 0x03, 0x00, 0x00, 0x40, 0x30, 0x42, 0x03, 0x00, 0x00, 0x7b, 0x31, 0x42,
+0x03, 0x00, 0x00, 0x7d, 0x33, 0x01, 0x00, 0x2c, 0x3b, 0x34, 0x01, 0x00, 0x2e, 0x3a, 0x35, 0x04,
+0x0c, 0x2d, 0x5f, 0x00, 0x00, 0x1f, 0x56, 0x01, 0x00, 0x3c, 0x3e, 0x00, 0x12, 0x42, 0x03, 0x00,
+0x00, 0xd5, 0x00, 0x10, 0x41, 0x00, 0x90, 0x91, 0x11, 0x41, 0x00, 0x92, 0x93, 0x12, 0x42, 0x00,
+0xa8, 0xa9, 0xcf, 0x13, 0x41, 0x00, 0xe1, 0xe2, 0x14, 0x41, 0x00, 0xe5, 0xe6, 0x15, 0xc5, 0x00,
+0xf3, 0x2c, 0xf4, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0xe7,
+0xe8, 0x17, 0x41, 0x00, 0xb7, 0xb8, 0x18, 0x41, 0x00, 0xd6, 0xd7, 0x19, 0x41, 0x00, 0xd8, 0xdd,
+0x1a, 0x42, 0x00, 0xf5, 0xf6, 0x81, 0x1b, 0x42, 0x00, 0x82, 0x83, 0x80, 0x1e, 0x41, 0x00, 0xa0,
+0xa1, 0x1f, 0x41, 0x00, 0xe3, 0xe4, 0x20, 0x41, 0x00, 0xa6, 0xa7, 0x21, 0x41, 0x00, 0xaa, 0xab,
+0x22, 0x41, 0x00, 0xac, 0xad, 0x23, 0x41, 0x00, 0xb5, 0xb6, 0x24, 0x41, 0x00, 0x8e, 0x8f, 0x25,
+0x41, 0x00, 0xc6, 0xc7, 0x26, 0x41, 0x00, 0xd0, 0xd1, 0x27, 0x42, 0x00, 0xfb, 0xfc, 0x95, 0x28,
+0x42, 0x00, 0x96, 0x97, 0x94, 0x2a, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x41,
+0x00, 0xe9, 0xea, 0x2c, 0xc5, 0x00, 0x88, 0x15, 0x89, 0x15, 0x00, 0x15, 0x00, 0x15, 0x19, 0x15,
+0x00, 0x15, 0x2d, 0x41, 0x00, 0x9a, 0x9b, 0x2e, 0x41, 0x00, 0xa4, 0xa5, 0x2f, 0x41, 0x00, 0xeb,
+0xec, 0x30, 0x41, 0x00, 0xa2, 0xa3, 0x31, 0x41, 0x00, 0xd4, 0xd5, 0x32, 0x42, 0x00, 0xd2, 0xd3,
+0xfd, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xcf, 0x28, 0x01, 0x00, 0xfd, 0xfd, 0x36, 0x05, 0x3f,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x03, 0x01, 0x03, 0x00, 0xc8, 0x0c, 0x00, 0x01, 0xc9,
+0x12, 0x42, 0x00, 0x65, 0x45, 0xd5, 0x15, 0xc5, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x00, 0x2c, 0x00,
+0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x43, 0x00, 0x69, 0x49, 0x8d,
+0x98, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x1f, 0x43, 0x00, 0x73, 0x53,
+0x9f, 0x9e, 0x22, 0x43, 0x00, 0x67, 0x47, 0xa7, 0xa6, 0x27, 0x01, 0x03, 0xca, 0xca, 0x2e, 0x43,
+0x00, 0x63, 0x43, 0x87, 0x80, 0x39, 0x00, 0x00, 0x20, 0x00, 0x22, 0x09, 0x61, 0x84, 0x65, 0x89,
+0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e, 0x45, 0xd3, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x27, 0x05,
+0x63, 0x86, 0x67, 0xd0, 0x43, 0x8f, 0x47, 0xd1, 0x20, 0x27, 0x5e, 0x0d, 0x61, 0x83, 0x63, 0xac,
+0x69, 0x8c, 0x73, 0xe7, 0x75, 0x96, 0x7a, 0xa8, 0x41, 0xb6, 0x43, 0xad, 0x49, 0xd7, 0x53, 0xe6,
+0x55, 0xea, 0x5a, 0xa9, 0x20, 0x5e, 0x00, 0x10, 0x41, 0x00, 0x90, 0x91, 0x11, 0x41, 0x00, 0x92,
+0x93, 0x12, 0x41, 0x00, 0xa8, 0xa9, 0x13, 0x41, 0x00, 0xe1, 0xe2, 0x14, 0x41, 0x00, 0xe5, 0xe6,
+0x15, 0xc5, 0x00, 0xf3, 0x2c, 0xf4, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16,
+0x41, 0x00, 0xe7, 0xe8, 0x17, 0x41, 0x00, 0xb7, 0xb8, 0x18, 0x41, 0x00, 0xd6, 0xd7, 0x19, 0x41,
+0x00, 0xd8, 0xdd, 0x1a, 0x42, 0x00, 0xf5, 0xf6, 0x81, 0x1b, 0x42, 0x00, 0x82, 0x83, 0x80, 0x1e,
+0x41, 0x00, 0xa0, 0xa1, 0x1f, 0x41, 0x00, 0xe3, 0xe4, 0x20, 0x41, 0x00, 0xa6, 0xa7, 0x21, 0x41,
+0x00, 0xaa, 0xab, 0x22, 0x41, 0x00, 0xac, 0xad, 0x23, 0x41, 0x00, 0xb5, 0xb6, 0x24, 0x41, 0x00,
+0x8e, 0x8f, 0x25, 0x41, 0x00, 0xc6, 0xc7, 0x26, 0x41, 0x00, 0xd0, 0xd1, 0x27, 0x42, 0x00, 0xfb,
+0xfc, 0x95, 0x28, 0x42, 0x00, 0x96, 0x97, 0x94, 0x2a, 0x05, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x7a, 0x2b, 0x41, 0x00, 0xe9, 0xea, 0x2c, 0xc5, 0x00, 0x88, 0x15, 0x89, 0x15, 0x00, 0x15, 0x00,
+0x15, 0x19, 0x15, 0x00, 0x15, 0x2d, 0x41, 0x00, 0x9a, 0x9b, 0x2e, 0x41, 0x00, 0xa4, 0xa5, 0x2f,
+0x41, 0x00, 0xeb, 0xec, 0x30, 0x41, 0x00, 0xa2, 0xa3, 0x31, 0x41, 0x00, 0xd4, 0xd5, 0x32, 0x42,
+0x00, 0xd2, 0xd3, 0xfd, 0x00, 0x28, 0x01, 0x00, 0xfd, 0xfd, 0x36, 0x05, 0x3f, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7b, 0x00, 0x48, 0x61, 0x01, 0x09, 0x00, 0x00, 0x4d, 0x4e, 0x2c, 0x00, 0x00, 0x4d,
+0x4f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xe3, 0x48,
+0x01, 0x00, 0x00, 0x00, 0x00, 0x28, 0xe3, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0xfc, 0x31, 0x21, 0x03, 0x02, 0x00, 0x2d, 0x32,
+0x40, 0x04, 0x02, 0x00, 0x22, 0x33, 0x23, 0x05, 0x02, 0x00, 0x24, 0x34, 0x3b, 0x06, 0x01, 0x00,
+0x3a, 0x35, 0x07, 0x02, 0x00, 0x2e, 0x36, 0x5e, 0x08, 0x03, 0x00, 0x5f, 0x37, 0x26, 0x1f, 0x09,
+0x02, 0x00, 0x2c, 0x38, 0x2a, 0x0a, 0x02, 0x00, 0x25, 0x39, 0x28, 0x0b, 0x02, 0x00, 0x3f, 0x30,
+0x29, 0x0c, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x0d, 0x41, 0x00, 0xe9, 0x99, 0x10, 0x41, 0x00, 0xe4,
+0x94, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xa6, 0x86,
+0x14, 0x41, 0x00, 0xed, 0x9d, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17,
+0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xdd, 0xdc, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x42,
+0x00, 0xaa, 0x8a, 0x5b, 0x1b, 0x42, 0x00, 0xea, 0x9a, 0x5d, 0x1e, 0x41, 0x00, 0xa9, 0x89, 0x1f,
+0x41, 0x00, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa1, 0x81, 0x21, 0x41, 0x00, 0xf3, 0xf2, 0x22, 0x41,
+0x00, 0xa0, 0x80, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x25,
+0x41, 0x00, 0xae, 0x8e, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x27, 0x41, 0x00, 0xa4, 0x84, 0x28, 0x42,
+0x00, 0xaf, 0x8f, 0x27, 0x29, 0x01, 0x00, 0x3d, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x78, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0xc1, 0x00, 0xf1, 0x2e,
+0xf0, 0x00, 0x2f, 0x41, 0x00, 0xe1, 0x91, 0x30, 0x41, 0x00, 0xac, 0x8c, 0x31, 0x41, 0x00, 0xa8,
+0x88, 0x32, 0x41, 0x00, 0xe2, 0x92, 0x33, 0x42, 0x00, 0xec, 0x9c, 0x3c, 0x34, 0x42, 0x00, 0xa2,
+0x82, 0x3e, 0x35, 0x42, 0x00, 0xee, 0x9e, 0x2f, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x57, 0xa8, 0x00, 0x09, 0x00, 0x00, 0x4d, 0x54,
+0x2c, 0x00, 0x00, 0x4d, 0x4c, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x55, 0x03, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x7c, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x02, 0x03, 0x00,
+0xa0, 0x9c, 0x12, 0x43, 0x03, 0x00, 0x00, 0x8a, 0xd4, 0x16, 0x43, 0x03, 0x00, 0x00, 0x97, 0xeb,
+0x17, 0x43, 0x03, 0x00, 0x00, 0x8d, 0xde, 0x18, 0x43, 0x03, 0x00, 0x00, 0x95, 0xe3, 0x1a, 0x43,
+0x00, 0xe5, 0xe4, 0x5b, 0x7b, 0x1b, 0x43, 0x00, 0xe8, 0xe7, 0x5d, 0x7d, 0x1e, 0x43, 0x03, 0x00,
+0x00, 0x85, 0xb7, 0x28, 0x01, 0x01, 0x00, 0x40, 0x29, 0x42, 0x00, 0x91, 0x92, 0x60, 0x2b, 0x01,
+0x00, 0x23, 0x7e, 0x35, 0x04, 0x0b, 0x00, 0x00, 0x5c, 0x00, 0x1c, 0x56, 0x43, 0x00, 0xbe, 0xbd,
+0x5c, 0x7c, 0x00, 0x31, 0x7e, 0x00, 0x09, 0x2f, 0x00, 0x4d, 0x54, 0x2c, 0x2f, 0x00, 0x4d, 0x4c,
+0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x34, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x12, 0x03, 0x03, 0x00, 0x00, 0x8a, 0xd4, 0x16, 0x03, 0x03, 0x00, 0x00,
+0x97, 0xeb, 0x17, 0x03, 0x03, 0x00, 0x00, 0x8d, 0xde, 0x18, 0x03, 0x03, 0x00, 0x00, 0x95, 0xe3,
+0x1a, 0x43, 0x00, 0xe5, 0xe4, 0x5b, 0x7b, 0x1b, 0x43, 0x00, 0xe8, 0xe7, 0x5d, 0x7d, 0x1e, 0x03,
+0x03, 0x00, 0x00, 0x85, 0xb7, 0x29, 0x43, 0x00, 0x91, 0x92, 0x60, 0x7e, 0x2b, 0x43, 0x00, 0xbe,
+0xbd, 0x5c, 0x7c, 0x00, 0xb0, 0xf4, 0x01, 0x04, 0x00, 0x00, 0x4e, 0x45, 0x02, 0x04, 0x00, 0x00,
+0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x75, 0x44, 0x00, 0x8a, 0x01, 0x00, 0x00,
+0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x02, 0x41, 0x00, 0x26, 0x31, 0x03, 0x42, 0x04, 0x82, 0x32, 0xc9, 0x04, 0x42, 0x00, 0x22, 0x33,
+0x23, 0x05, 0x42, 0x00, 0x27, 0x34, 0x7b, 0x06, 0x44, 0x08, 0x28, 0x35, 0x5b, 0x00, 0x1b, 0x07,
+0x44, 0x08, 0x2d, 0x36, 0x7c, 0x00, 0x1f, 0x08, 0x42, 0x04, 0x8a, 0x37, 0xc8, 0x09, 0x44, 0x08,
+0x5f, 0x38, 0x5c, 0x00, 0x1c, 0x0a, 0x42, 0x00, 0x87, 0x39, 0x5e, 0x0b, 0x42, 0x00, 0x85, 0x30,
+0x40, 0x0c, 0x44, 0x08, 0x29, 0xf8, 0x5d, 0x00, 0x1d, 0x0d, 0x42, 0x03, 0x00, 0x00, 0x7d, 0x10,
+0xc5, 0x00, 0x61, 0x1e, 0x41, 0x1e, 0x91, 0x1e, 0x92, 0x1e, 0x01, 0x1e, 0x00, 0x1e, 0x11, 0xc5,
+0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0xf7, 0x2c, 0xef, 0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x12, 0x43, 0x00,
+0x65, 0x45, 0xe1, 0xee, 0x14, 0x03, 0x03, 0x00, 0x00, 0x9e, 0x9f, 0x15, 0x43, 0x00, 0x79, 0x59,
+0xec, 0xed, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x43, 0x00, 0x6f,
+0x4f, 0xab, 0xac, 0x1a, 0x44, 0x0f, 0xca, 0xcb, 0x00, 0x00, 0x1e, 0x1b, 0x43, 0x00, 0x24, 0x9c,
+0xcf, 0xd5, 0x1e, 0xc5, 0x00, 0x71, 0x10, 0x51, 0x10, 0x84, 0x10, 0x8e, 0x10, 0x11, 0x10, 0x00,
+0x10, 0x1f, 0x43, 0x00, 0x73, 0x53, 0x8d, 0xde, 0x20, 0x03, 0x03, 0x00, 0x00, 0x95, 0xe3, 0x21,
+0x03, 0x03, 0x00, 0x00, 0xd0, 0xd1, 0x22, 0x43, 0x00, 0x67, 0x47, 0xa8, 0xad, 0x24, 0x41, 0x00,
+0x6a, 0x4a, 0x25, 0x03, 0x03, 0x00, 0x00, 0xe7, 0xe8, 0x26, 0x03, 0x03, 0x00, 0x00, 0x9b, 0x9d,
+0x27, 0xc5, 0x00, 0x6d, 0x32, 0x4d, 0x32, 0x00, 0x32, 0x00, 0x32, 0x0d, 0x32, 0x00, 0x32, 0x28,
+0x41, 0x00, 0x97, 0x25, 0x29, 0x41, 0x00, 0x82, 0x90, 0x2b, 0x41, 0x00, 0x2a, 0xe6, 0x2c, 0xc5,
+0x00, 0x77, 0x11, 0x57, 0x11, 0x00, 0x11, 0x00, 0x11, 0x17, 0x11, 0x00, 0x11, 0x2e, 0x03, 0x03,
+0x00, 0x00, 0x87, 0x80, 0x30, 0x03, 0x03, 0x00, 0x00, 0xf1, 0xf2, 0x31, 0x43, 0x00, 0x6e, 0x4e,
+0xfa, 0xfb, 0x32, 0x43, 0x00, 0x2c, 0x3f, 0xfc, 0xfd, 0x33, 0x43, 0x00, 0x3b, 0x2e, 0x3c, 0xae,
+0x34, 0x43, 0x00, 0x3a, 0x2f, 0x3e, 0xaf, 0x35, 0x41, 0x00, 0x21, 0xf5, 0x39, 0x00, 0x00, 0x20,
+0x56, 0x41, 0x00, 0x3c, 0x3e, 0x00, 0x60, 0x07, 0x61, 0x85, 0x65, 0x8a, 0x75, 0x97, 0x41, 0xb7,
+0x45, 0xd4, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x0d, 0x61, 0xc6, 0x65, 0xbd, 0x69, 0xa9, 0x6f, 0xe4,
+0x75, 0xf4, 0x6e, 0xa4, 0x41, 0xc7, 0x45, 0xbe, 0x49, 0xb8, 0x4f, 0xe5, 0x55, 0xf6, 0x4e, 0xa5,
+0x20, 0x7e, 0x5e, 0x11, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x67, 0x86,
+0x6a, 0xa6, 0x73, 0xf9, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x47, 0x8f,
+0x4a, 0xa7, 0x53, 0xdd, 0x20, 0x5e, 0x22, 0x09, 0x65, 0x89, 0x69, 0x8b, 0x75, 0x81, 0x79, 0x98,
+0x45, 0xd3, 0x49, 0xd8, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x00, 0x51, 0x20, 0x01, 0x04, 0x00,
+0x00, 0x4e, 0x47, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35,
+0x75, 0x34, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0xe1, 0x24, 0x9c, 0x06, 0x02,
+0x03, 0x00, 0x00, 0xd5, 0x07, 0x01, 0x03, 0x00, 0xca, 0x11, 0x43, 0x03, 0x00, 0x00, 0xbd, 0xbe,
+0x12, 0x43, 0x00, 0x65, 0x45, 0xf1, 0xf2, 0x15, 0x43, 0x03, 0x00, 0x00, 0xf7, 0xef, 0x16, 0x43,
+0x00, 0x75, 0x55, 0x9e, 0x9f, 0x17, 0x43, 0x00, 0x69, 0x49, 0x9b, 0x9d, 0x18, 0x43, 0x00, 0x6f,
+0x4f, 0xfc, 0xfd, 0x19, 0x43, 0x03, 0x00, 0x00, 0xfa, 0xfb, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x1f,
+0x41, 0x00, 0x73, 0x53, 0x20, 0x43, 0x03, 0x00, 0x00, 0xd0, 0xd1, 0x24, 0x43, 0x03, 0x00, 0x00,
+0x94, 0x99, 0x25, 0x43, 0x03, 0x00, 0x00, 0xe7, 0xe8, 0x28, 0x00, 0x01, 0xcb, 0x29, 0x01, 0x03,
+0xc8, 0xc9, 0x30, 0x43, 0x03, 0x00, 0x00, 0xc6, 0xc7, 0x31, 0x43, 0x00, 0x6e, 0x4e, 0xa4, 0xa5,
+0x39, 0x00, 0x00, 0x20, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75,
+0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x0d, 0x65,
+0xa6, 0x69, 0x84, 0x6e, 0xe4, 0x6f, 0xa8, 0x73, 0xf9, 0x75, 0x86, 0x45, 0xa7, 0x49, 0x8e, 0x4e,
+0xe5, 0x4f, 0xa9, 0x53, 0xdd, 0x55, 0x8f, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69,
+0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20,
+0x5e, 0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6e, 0xec, 0x6f, 0xa2, 0x75, 0xa3, 0x41,
+0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4e, 0xed, 0x4f, 0xe0, 0x55, 0xe9, 0x20, 0x27, 0x00, 0xdf, 0xfc,
+0x01, 0x04, 0x00, 0x00, 0x50, 0x48, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x5a, 0x03, 0x22, 0x01, 0x29, 0x01, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x29, 0x01,
+0x00, 0x00, 0xb5, 0x01, 0xa0, 0x01, 0xac, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x00, 0xa6, 0x7c,
+0x03, 0x02, 0x01, 0x00, 0x22, 0x40, 0x04, 0x02, 0x01, 0x00, 0x2f, 0x23, 0x05, 0x02, 0x01, 0x00,
+0x28, 0xac, 0x06, 0x02, 0x01, 0x00, 0x29, 0xab, 0x07, 0x02, 0x01, 0x00, 0xad, 0xaa, 0x08, 0x03,
+0x01, 0x00, 0x21, 0x5f, 0x1f, 0x09, 0x02, 0x01, 0x00, 0xa8, 0x23, 0x0a, 0x02, 0x01, 0x00, 0x3f,
+0xf5, 0x0b, 0x03, 0x03, 0x00, 0xa0, 0x5c, 0x1c, 0x0c, 0x02, 0x00, 0x2d, 0x2b, 0x2a, 0x0d, 0x02,
+0x07, 0xcc, 0xcc, 0xc9, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41,
+0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x02, 0x00,
+0xf6, 0x9e, 0x5b, 0x1b, 0x02, 0x03, 0xc8, 0xc8, 0x5d, 0x1e, 0x42, 0x00, 0x61, 0x41, 0x24, 0x1f,
+0x42, 0x03, 0x00, 0x00, 0x26, 0x20, 0x42, 0x03, 0x00, 0x00, 0x40, 0x21, 0x42, 0x03, 0x00, 0x00,
+0x5b, 0x22, 0x42, 0x03, 0x00, 0x00, 0x5d, 0x23, 0x42, 0x03, 0x00, 0x00, 0x7c, 0x24, 0x42, 0x03,
+0x00, 0x00, 0x9c, 0x25, 0x42, 0x03, 0x00, 0x00, 0xf1, 0x27, 0x42, 0x00, 0xa4, 0xa5, 0x7e, 0x28,
+0x42, 0x00, 0x87, 0x80, 0x7b, 0x29, 0x03, 0x00, 0x27, 0xfa, 0x5c, 0x1c, 0x2b, 0x42, 0x03, 0xcb,
+0xcb, 0x7d, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41, 0x00, 0x6e, 0x4e, 0x33, 0x02, 0x01, 0x00,
+0x3b, 0x3c, 0x34, 0x02, 0x05, 0x00, 0x3a, 0xca, 0x35, 0x02, 0x00, 0x3d, 0x25, 0x3e, 0x39, 0x00,
+0x00, 0x20, 0x56, 0x01, 0x00, 0x3c, 0x3e, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x60,
+0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49,
+0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41,
+0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f,
+0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef,
+0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45,
+0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65,
+0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f,
+0x99, 0x55, 0x9a, 0x20, 0xf9, 0x00, 0x0a, 0x02, 0x03, 0x00, 0x00, 0x15, 0x1a, 0x01, 0x03, 0x00,
+0xa0, 0x00, 0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60,
+0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c,
+0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0x27, 0x07, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2,
+0x75, 0xa3, 0x45, 0x90, 0x20, 0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94,
+0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0xd8, 0xb5, 0x00,
+0x09, 0x00, 0x00, 0x53, 0x51, 0x2c, 0xc4, 0x01, 0x53, 0x51, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x03, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03,
+0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x01,
+0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xf5,
+0x08, 0x02, 0x03, 0x00, 0x00, 0x7b, 0x09, 0x02, 0x03, 0x00, 0x00, 0x7d, 0x0a, 0x03, 0x03, 0x00,
+0x00, 0x5b, 0x1b, 0x0b, 0x03, 0x03, 0x00, 0x00, 0x5d, 0x1d, 0x1a, 0x41, 0x00, 0x89, 0xd3, 0x1b,
+0x01, 0x00, 0x27, 0x40, 0x28, 0x41, 0x00, 0x87, 0x80, 0x29, 0x02, 0x00, 0x60, 0xaa, 0x7c, 0x2b,
+0x03, 0x00, 0x23, 0x7e, 0x5c, 0x1c, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x12, 0x42,
+0x03, 0x00, 0x00, 0xaa, 0x29, 0x01, 0x03, 0x00, 0xa0, 0x00, 0x04, 0x01, 0x01, 0x00, 0x15, 0x1a,
+0x41, 0x02, 0x89, 0xa0, 0x00, 0xa8, 0x61, 0x03, 0x04, 0xc0, 0x01, 0x53, 0x51, 0x05, 0x03, 0x00,
+0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x03, 0x30, 0x02, 0x9b, 0x02, 0x00,
+0x00, 0x52, 0x03, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x5a, 0x03, 0x2f, 0x01, 0x36, 0x01, 0x00,
+0x00, 0xb5, 0x01, 0xbd, 0x01, 0xd5, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x02, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x03, 0x07, 0x00,
+0x00, 0xc9, 0x1e, 0x06, 0x02, 0x07, 0x00, 0x00, 0xca, 0x08, 0x02, 0x07, 0x00, 0x00, 0xcb, 0x09,
+0x02, 0x03, 0x00, 0x00, 0xfa, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xcc, 0x0c, 0x02, 0x07, 0x00, 0x00,
+0xcd, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xce, 0x10, 0x42, 0x03, 0x00, 0x00, 0x5c, 0x11, 0x42, 0x03,
+0x00, 0x00, 0x7c, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0xc4, 0x00, 0x7a, 0x2c, 0x5a, 0x2c, 0x00,
+0x2c, 0x1a, 0x2c, 0x00, 0x2c, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18,
+0x41, 0x00, 0x6f, 0x4f, 0x1a, 0x42, 0x00, 0x87, 0x80, 0xf6, 0x1b, 0x02, 0x00, 0x40, 0x27, 0x9e,
+0x1e, 0x41, 0x00, 0x61, 0x41, 0x21, 0x42, 0x03, 0x00, 0x00, 0x5b, 0x22, 0x42, 0x03, 0x00, 0x00,
+0x5d, 0x27, 0x42, 0x00, 0x89, 0xd3, 0x24, 0x28, 0x03, 0x00, 0x5b, 0x7b, 0xe1, 0x1b, 0x29, 0x03,
+0x04, 0x5c, 0x7c, 0x00, 0x1c, 0x2b, 0x03, 0x00, 0x5d, 0x7d, 0xcf, 0x1d, 0x2c, 0xc4, 0x00, 0x79,
+0x15, 0x59, 0x15, 0x00, 0x15, 0x19, 0x15, 0x00, 0x15, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x2f, 0x42,
+0x03, 0x00, 0x00, 0x40, 0x30, 0x42, 0x03, 0x00, 0x00, 0x7b, 0x31, 0x42, 0x00, 0x6e, 0x4e, 0x7d,
+0x32, 0x42, 0x03, 0x00, 0x00, 0xf5, 0x33, 0x02, 0x01, 0x00, 0x3b, 0x3c, 0x34, 0x02, 0x01, 0x00,
+0x3a, 0x3e, 0x39, 0x00, 0x00, 0x20, 0x56, 0x01, 0x00, 0x3c, 0x3e, 0x00, 0x12, 0x42, 0x03, 0x00,
+0x00, 0xd5, 0x00, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f,
+0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41,
+0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xf8, 0x03, 0x61, 0x86, 0x41,
+0x8f, 0x20, 0xf8, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41,
+0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0xef, 0x0d, 0x61, 0xa0, 0x65,
+0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f,
+0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0xef, 0xf9, 0x0c, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f,
+0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20,
+0xf9, 0xf7, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0xf7, 0x00, 0x1b, 0x02, 0x07, 0x00, 0x00, 0xa0,
+0x27, 0x41, 0x02, 0x89, 0xa0, 0x2b, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x32, 0x42, 0x03, 0x00, 0x00,
+0x15, 0x00, 0x7e, 0x03, 0x6e, 0xa4, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83, 0x65, 0x88,
+0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x20, 0x5e, 0xf8, 0x03, 0x61, 0x86, 0x41, 0x8f, 0x20, 0xf8,
+0x60, 0x06, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60, 0x27, 0x07,
+0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x45, 0x90, 0x20, 0x27, 0x22, 0x0a,
+0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99,
+0x55, 0x9a, 0x20, 0x22, 0x2c, 0x03, 0x63, 0x87, 0x43, 0x80, 0x20, 0x2c, 0x00, 0x02, 0x02, 0x03,
+0x00, 0x00, 0x7e, 0x03, 0x02, 0x07, 0x00, 0x00, 0xc8, 0x05, 0x02, 0x07, 0x00, 0x00, 0xca, 0x06,
+0x02, 0x07, 0x00, 0x00, 0xcb, 0x07, 0x02, 0x07, 0x00, 0x00, 0xcc, 0x08, 0x02, 0x03, 0x00, 0x00,
+0x60, 0x09, 0x02, 0x07, 0x00, 0x00, 0xcd, 0x0a, 0x02, 0x07, 0x00, 0x00, 0xce, 0x0b, 0x02, 0x07,
+0x00, 0x00, 0xcf, 0x0c, 0x02, 0x07, 0x00, 0x00, 0xd0, 0x0d, 0x02, 0x07, 0x00, 0x00, 0xd1, 0x12,
+0x42, 0x03, 0x00, 0x00, 0xaa, 0x13, 0x41, 0x00, 0x72, 0x52, 0x14, 0x41, 0x00, 0x74, 0x54, 0x1f,
+0x42, 0x00, 0x73, 0x53, 0xd0, 0x20, 0x42, 0x00, 0x64, 0x44, 0xd1, 0x25, 0x42, 0x03, 0x00, 0x00,
+0x88, 0x26, 0x42, 0x00, 0x6c, 0x4c, 0x9d, 0x00, 0xf3, 0x13, 0x63, 0x9f, 0x64, 0xd4, 0x65, 0xd8,
+0x6c, 0x96, 0x6e, 0xe5, 0x72, 0xfd, 0x73, 0xe7, 0x74, 0x9c, 0x7a, 0xa7, 0x43, 0xac, 0x44, 0xd2,
+0x45, 0xb7, 0x4c, 0x95, 0x4e, 0xd5, 0x52, 0xfc, 0x53, 0xe6, 0x54, 0x9b, 0x5a, 0xa6, 0x20, 0xf3,
+0x5e, 0x07, 0x61, 0x83, 0x69, 0x8c, 0x6f, 0x93, 0x41, 0xb6, 0x49, 0xd7, 0x4f, 0xe2, 0x20, 0x5e,
+0xf4, 0x03, 0x61, 0xc7, 0x41, 0xc6, 0x20, 0xf4, 0xf8, 0x03, 0x75, 0x85, 0x55, 0xde, 0x20, 0xf8,
+0xf2, 0x05, 0x61, 0xa5, 0x65, 0xa9, 0x41, 0xa4, 0x45, 0xa8, 0x20, 0xf2, 0xfa, 0x03, 0x7a, 0xbe,
+0x5a, 0xbd, 0x20, 0xfa, 0xef, 0x19, 0x61, 0xa0, 0x63, 0x86, 0x65, 0x82, 0x69, 0xa1, 0x6c, 0x92,
+0x6e, 0xe4, 0x6f, 0xa2, 0x72, 0xea, 0x73, 0x98, 0x75, 0xa3, 0x79, 0xec, 0x7a, 0xab, 0x41, 0xb5,
+0x43, 0x8f, 0x45, 0x90, 0x49, 0xd6, 0x4c, 0x91, 0x4e, 0xe3, 0x4f, 0xe0, 0x52, 0xe8, 0x53, 0x97,
+0x55, 0xe9, 0x59, 0xed, 0x5a, 0x8d, 0x20, 0xef, 0xf1, 0x05, 0x6f, 0x8b, 0x75, 0xfb, 0x4f, 0x8a,
+0x55, 0xeb, 0x20, 0xf1, 0xf9, 0x09, 0x61, 0x84, 0x65, 0x89, 0x6f, 0x94, 0x75, 0x81, 0x41, 0x8e,
+0x45, 0xd3, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0xf9, 0xf7, 0x07, 0x63, 0x87, 0x73, 0xad, 0x74, 0xee,
+0x43, 0x80, 0x53, 0xb8, 0x54, 0xdd, 0x20, 0xf7, 0x00, 0x07, 0x55, 0x01, 0x04, 0x00, 0x00, 0x54,
+0x4a, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x75, 0x41,
+0x01, 0x00, 0x00, 0x00, 0x00, 0x32, 0x75, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x22, 0x40, 0x04, 0x02, 0x01, 0x00, 0xfc,
+0x23, 0x05, 0x02, 0x05, 0x00, 0x3b, 0xa0, 0x07, 0x02, 0x01, 0x00, 0x3a, 0x5e, 0x08, 0x02, 0x01,
+0x00, 0x3f, 0x26, 0x09, 0x02, 0x03, 0x00, 0x00, 0x24, 0x0c, 0x43, 0x00, 0xf3, 0xf2, 0x2d, 0x5f,
+0x0d, 0x43, 0x00, 0xf7, 0xf6, 0x3d, 0x2b, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x43, 0x00, 0xf9,
+0xf8, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00,
+0xa5, 0x85, 0xfd, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00,
+0xe8, 0x98, 0x18, 0x43, 0x00, 0xfb, 0xfa, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x42,
+0x00, 0xe5, 0x95, 0x5b, 0x1b, 0x42, 0x00, 0xea, 0x9a, 0x5d, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f,
+0x43, 0x00, 0xde, 0xdd, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80,
+0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae,
+0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86,
+0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00,
+0x00, 0x00, 0x00, 0x78, 0x2b, 0x02, 0x01, 0x00, 0x2f, 0x7c, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d,
+0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41,
+0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x43, 0x00, 0xf5, 0xf4, 0xec, 0x9c, 0x33,
+0x42, 0x00, 0xa1, 0x81, 0x3c, 0x34, 0x42, 0x00, 0xee, 0x9e, 0x3e, 0x35, 0x02, 0x00, 0x2e, 0x2c,
+0x2f, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79,
+0x00, 0x52, 0x6f, 0x01, 0x04, 0x00, 0x00, 0x54, 0x4d, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x03, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0xe7, 0x61,
+0x01, 0x00, 0x00, 0x00, 0x00, 0x62, 0xe7, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x03, 0x03, 0x00, 0x00, 0x5b, 0x7b, 0x1b, 0x03, 0x03, 0x00,
+0x00, 0x5d, 0x7d, 0x29, 0x03, 0x03, 0x00, 0x00, 0x60, 0x7e, 0x2b, 0x03, 0x03, 0x00, 0x00, 0x5c,
+0x7c, 0x00, 0x10, 0x43, 0x00, 0x84, 0x8e, 0x71, 0x51, 0x12, 0x42, 0x03, 0x00, 0x00, 0xaa, 0x1a,
+0x41, 0x00, 0xe5, 0xd5, 0x1b, 0x41, 0x00, 0x94, 0x99, 0x29, 0x41, 0x00, 0xa7, 0xa6, 0x2b, 0x41,
+0x00, 0xad, 0xb8, 0x2d, 0x43, 0x00, 0xec, 0xed, 0x78, 0x58, 0x2e, 0x43, 0x00, 0x87, 0x80, 0x63,
+0x43, 0x2f, 0x43, 0x00, 0x81, 0x9a, 0x76, 0x56, 0x00, 0x0c, 0xc3, 0x00, 0xf1, 0x0c, 0xf0, 0x00,
+0x2d, 0x82, 0x5f, 0x82, 0x0d, 0x43, 0x00, 0xeb, 0x9b, 0x3d, 0x2b, 0x10, 0x41, 0x00, 0xf3, 0xf2,
+0x11, 0x41, 0x00, 0xa2, 0x82, 0x12, 0x41, 0x00, 0xa5, 0x85, 0x13, 0xc1, 0x00, 0xe0, 0x00, 0x90,
+0x13, 0x14, 0x41, 0x00, 0xe2, 0x92, 0x15, 0x41, 0x00, 0xa9, 0x89, 0x16, 0x41, 0x00, 0xe3, 0x93,
+0x17, 0x41, 0x00, 0xa8, 0x88, 0x18, 0x41, 0x00, 0xae, 0x8e, 0x19, 0x41, 0x00, 0xaf, 0x8f, 0x1a,
+0x41, 0x00, 0xfb, 0xfa, 0x1b, 0x41, 0x00, 0xf5, 0xf4, 0x1e, 0x41, 0x00, 0xa0, 0x80, 0x1f, 0x41,
+0x00, 0xe1, 0x91, 0x20, 0x41, 0x00, 0xa4, 0x84, 0x21, 0x41, 0x00, 0xe4, 0x94, 0x22, 0x41, 0x00,
+0xa3, 0x83, 0x23, 0x41, 0x00, 0xe5, 0x95, 0x24, 0x41, 0x00, 0xa6, 0x86, 0x25, 0x41, 0x00, 0xaa,
+0x8a, 0x26, 0x41, 0x00, 0xab, 0x8b, 0x28, 0x43, 0x00, 0xed, 0x9d, 0x27, 0x22, 0x29, 0x41, 0x00,
+0xf9, 0xf8, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x2b, 0x41, 0x00, 0xe8, 0x98, 0x2c,
+0x41, 0x00, 0xa7, 0x87, 0x2d, 0x41, 0x00, 0xef, 0x9f, 0x2e, 0x41, 0x00, 0xe7, 0x97, 0x2f, 0x41,
+0x00, 0xf7, 0xf6, 0x30, 0x41, 0x00, 0xa1, 0x81, 0x31, 0x41, 0x00, 0xad, 0x8d, 0x32, 0x41, 0x00,
+0xac, 0x8c, 0x35, 0x43, 0x00, 0xee, 0x9e, 0x2f, 0x3f, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x7a, 0x00, 0x85, 0x83, 0x06, 0x09, 0x00, 0x00, 0x55, 0x52, 0x2c, 0x00, 0x00, 0x55, 0x41,
+0x0f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x03, 0xd2, 0x01,
+0x00, 0x00, 0x00, 0x00, 0x50, 0x03, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x04, 0xc4, 0x02,
+0x00, 0x00, 0x00, 0x00, 0x65, 0x04, 0xe7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x51, 0x03, 0xac, 0x03,
+0x00, 0x00, 0x00, 0x00, 0x51, 0x03, 0xd3, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x04, 0x99, 0x04,
+0x00, 0x00, 0x00, 0x00, 0x6b, 0x04, 0xc1, 0x03, 0x00, 0x00, 0x00, 0x00, 0x28, 0x03, 0x7f, 0x05,
+0x00, 0x00, 0x00, 0x00, 0x28, 0x03, 0xa8, 0x04, 0x00, 0x00, 0x00, 0x00, 0x62, 0x03, 0x6a, 0x06,
+0x00, 0x00, 0x00, 0x00, 0x62, 0x03, 0x94, 0x05, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xed, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x03, 0x00, 0x00, 0x40, 0x04, 0x02, 0x03, 0x00, 0x00, 0x23,
+0x05, 0x02, 0x03, 0x00, 0x00, 0xcf, 0x07, 0x02, 0x03, 0x00, 0x00, 0x5e, 0x08, 0x02, 0x03, 0x00,
+0x00, 0x26, 0x09, 0x02, 0x03, 0x00, 0x00, 0x24, 0x1a, 0x02, 0x03, 0x00, 0x00, 0x5b, 0x1b, 0x02,
+0x03, 0x00, 0x00, 0x5d, 0x2b, 0x02, 0x03, 0x00, 0x00, 0x7c, 0x33, 0x02, 0x03, 0x00, 0x00, 0x3c,
+0x34, 0x02, 0x03, 0x00, 0x00, 0x3e, 0x35, 0x02, 0x03, 0x00, 0x00, 0x2f, 0x00, 0x12, 0x42, 0x03,
+0x00, 0x00, 0xd5, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xfc, 0x05, 0x02,
+0x05, 0x00, 0x3b, 0xa0, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41,
+0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00,
+0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x43, 0x00,
+0xa3, 0x83, 0xf3, 0xf2, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41,
+0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xf9, 0xf8, 0x1e, 0x41, 0x00,
+0xe4, 0x94, 0x1f, 0x41, 0x00, 0xf7, 0xf6, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0,
+0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00,
+0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6,
+0x86, 0x28, 0x41, 0x00, 0xf5, 0xf4, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x04, 0x1f,
+0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d,
+0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41,
+0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00,
+0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x56, 0x41, 0x00, 0xf3,
+0xf2, 0x00, 0x05, 0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00,
+0xfc, 0x05, 0x02, 0x01, 0x00, 0x3b, 0xfd, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00,
+0x3f, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93,
+0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x41, 0x00, 0xa5, 0x85, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16,
+0x43, 0x00, 0xa3, 0x83, 0xf3, 0xf2, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99,
+0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xf9, 0xf8, 0x1e,
+0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xf7, 0xf6, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41,
+0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24,
+0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41,
+0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xf5, 0xf4, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0x41, 0x00, 0xef,
+0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c,
+0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33,
+0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x56, 0x41,
+0x00, 0xf3, 0xf2, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x7b, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x05, 0x02, 0x05, 0x00, 0x3b, 0xa0, 0x07, 0x01,
+0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00,
+0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5,
+0x85, 0xfb, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x43, 0x00, 0xa3, 0x83, 0xfd, 0xfc, 0x17, 0x41,
+0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00,
+0xe5, 0x95, 0x1b, 0x41, 0x00, 0xf5, 0xf4, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xf9,
+0xf8, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f,
+0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab,
+0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xf3, 0xf2,
+0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x2b,
+0x01, 0x01, 0x00, 0x2f, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41,
+0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00,
+0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee,
+0x9e, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x56, 0x41, 0x00, 0xfd, 0xfc, 0x00, 0x05, 0x02, 0x07, 0x00,
+0x00, 0xa0, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfb, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d,
+0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x05, 0x02, 0x01, 0x00, 0x3b, 0xfb, 0x07, 0x01, 0x01, 0x00,
+0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96,
+0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x41, 0x00, 0xa5, 0x85, 0x15,
+0x41, 0x00, 0xad, 0x8d, 0x16, 0x43, 0x00, 0xa3, 0x83, 0xfd, 0xfc, 0x17, 0x41, 0x00, 0xe8, 0x98,
+0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b,
+0x41, 0x00, 0xf5, 0xf4, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xf9, 0xf8, 0x20, 0x41,
+0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00,
+0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41,
+0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xf3, 0xf2, 0x29, 0xc1, 0x00,
+0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x2b, 0x01, 0x01, 0x00,
+0x2f, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91,
+0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32,
+0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01,
+0x00, 0x2e, 0x2c, 0x56, 0x41, 0x00, 0xfd, 0xfc, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0xfb, 0x36,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01,
+0x00, 0xfc, 0x05, 0x02, 0x05, 0x00, 0x3b, 0xa0, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01,
+0x00, 0x3f, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3,
+0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15, 0x41, 0x00, 0xad,
+0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99,
+0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xf5, 0xf4, 0x1e,
+0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0x69, 0x49, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41,
+0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24,
+0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41,
+0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xf3, 0xf2, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a,
+0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0x41, 0x00, 0xef,
+0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c,
+0x30, 0x41, 0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33,
+0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x00, 0x05,
+0x02, 0x07, 0x00, 0x00, 0xa0, 0x12, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00,
+0x00, 0x00, 0x81, 0x00, 0x03, 0x01, 0x01, 0x00, 0x22, 0x04, 0x01, 0x01, 0x00, 0xfc, 0x05, 0x02,
+0x01, 0x00, 0x3b, 0xfd, 0x07, 0x01, 0x01, 0x00, 0x3a, 0x08, 0x01, 0x01, 0x00, 0x3f, 0x10, 0x41,
+0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00,
+0xaa, 0x8a, 0x14, 0x41, 0x00, 0xa5, 0x85, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3,
+0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87,
+0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xf5, 0xf4, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f,
+0x41, 0x00, 0x69, 0x49, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41,
+0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25,
+0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41,
+0x00, 0xf3, 0xf2, 0x29, 0xc1, 0x00, 0xf1, 0x29, 0xf0, 0x00, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x82, 0x2b, 0x01, 0x01, 0x00, 0x2f, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7,
+0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xa8, 0x88,
+0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34,
+0x41, 0x00, 0xee, 0x9e, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x00, 0x05, 0x02, 0x03, 0x00, 0x00, 0xfd,
+0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x51, 0x07, 0x07, 0x09, 0xd1, 0x01, 0x55,
+0x52, 0x2c, 0xd1, 0x01, 0x55, 0x41, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x50, 0x03, 0xe7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x50, 0x03, 0xf0, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x65, 0x04, 0xec, 0x02, 0x00, 0x00, 0x00, 0x00, 0x65, 0x04, 0xf6, 0x01, 0x00, 0x00,
+0x00, 0x00, 0x51, 0x03, 0xec, 0x03, 0x00, 0x00, 0x00, 0x00, 0x51, 0x03, 0xf5, 0x02, 0x00, 0x00,
+0x00, 0x00, 0x6b, 0x04, 0xf1, 0x04, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x04, 0xfb, 0x03, 0x00, 0x00,
+0x00, 0x00, 0x28, 0x03, 0xf0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x28, 0x03, 0xfa, 0x04, 0x00, 0x00,
+0x00, 0x00, 0x62, 0x03, 0xf4, 0x06, 0x00, 0x00, 0x00, 0x00, 0x62, 0x03, 0xff, 0x05, 0x00, 0x00,
+0x00, 0x00, 0x5a, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x03, 0x00, 0x00, 0x40,
+0x04, 0x02, 0x03, 0x00, 0x00, 0x23, 0x05, 0x02, 0x03, 0x00, 0x00, 0x24, 0x07, 0x02, 0x03, 0x00,
+0x00, 0x5e, 0x08, 0x02, 0x03, 0x00, 0x00, 0x26, 0x09, 0x03, 0x03, 0x00, 0x00, 0x2a, 0x1f, 0x0d,
+0x03, 0x07, 0x00, 0x00, 0x00, 0x1c, 0x1a, 0x02, 0x03, 0x00, 0x00, 0x5b, 0x1b, 0x02, 0x03, 0x00,
+0x00, 0x5d, 0x33, 0x02, 0x03, 0x00, 0x00, 0x3c, 0x34, 0x02, 0x03, 0x00, 0x00, 0x3e, 0x00, 0x12,
+0x42, 0x03, 0x00, 0x00, 0xd5, 0x00, 0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00, 0x2d, 0x32,
+0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x01, 0x00, 0x22, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07,
+0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01,
+0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00,
+0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3,
+0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15, 0x41, 0x00, 0xad,
+0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99,
+0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xf3, 0xf2, 0x1e,
+0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xa8, 0x88, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41,
+0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24,
+0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41,
+0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xf5, 0xf4, 0x29, 0x01, 0x00, 0x60, 0x2b, 0x2a, 0x04, 0x1f,
+0x00, 0x00, 0x00, 0x00, 0x78, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d,
+0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41,
+0x00, 0xf7, 0xf6, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00,
+0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x41, 0x00, 0xf9, 0xf8, 0x00, 0x12, 0x42, 0x03,
+0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x02, 0x01, 0x00, 0xfc,
+0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x01, 0x00, 0x22, 0x34,
+0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09,
+0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01,
+0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00,
+0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x41, 0x00, 0xa5,
+0x85, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98,
+0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b,
+0x41, 0x00, 0xf3, 0xf2, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xa8, 0x88, 0x20, 0x41,
+0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00,
+0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41,
+0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xf5, 0xf4, 0x29, 0x01, 0x00,
+0x60, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c,
+0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41,
+0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xf7, 0xf6, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00,
+0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x41, 0x00, 0xf9,
+0xf8, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x02, 0x01, 0x00, 0x21, 0x31,
+0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05, 0x01, 0x00, 0x22, 0x34, 0x06,
+0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01,
+0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01, 0x00,
+0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6,
+0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x42, 0x00, 0xa5, 0x85,
+0xfb, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98,
+0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b,
+0x41, 0x00, 0xfd, 0xfc, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00, 0xa8, 0x88, 0x20, 0x41,
+0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00,
+0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41,
+0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xf3, 0xf2, 0x29, 0x01, 0x00,
+0x60, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c,
+0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41,
+0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xf9, 0xf8, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00,
+0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e, 0x35, 0x41, 0x00, 0xf5,
+0xf4, 0x00, 0x12, 0x42, 0x03, 0x00, 0x00, 0xfb, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7d,
+0x00, 0x02, 0x01, 0x00, 0x21, 0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33,
+0x05, 0x01, 0x00, 0x22, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08,
+0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01,
+0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00,
+0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa,
+0x8a, 0x14, 0x41, 0x00, 0xa5, 0x85, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83,
+0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a,
+0x41, 0x00, 0xe5, 0x95, 0x1b, 0x41, 0x00, 0xfd, 0xfc, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41,
+0x00, 0xa8, 0x88, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00,
+0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41,
+0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00,
+0xf3, 0xf2, 0x29, 0x01, 0x00, 0x60, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x2b,
+0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41,
+0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0xf9, 0xf8, 0x31, 0x41, 0x00,
+0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee,
+0x9e, 0x35, 0x41, 0x00, 0xf5, 0xf4, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00,
+0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04, 0x01, 0x00, 0x2f, 0x33, 0x05,
+0x01, 0x00, 0x22, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01, 0x00, 0x2c, 0x36, 0x08, 0x01,
+0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00, 0x3f, 0x39, 0x0b, 0x01, 0x00,
+0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b, 0x5c, 0x10, 0x41, 0x00, 0xa9,
+0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a,
+0x14, 0x42, 0x00, 0xa5, 0x85, 0xfd, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83,
+0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87, 0x1a,
+0x41, 0x00, 0xe5, 0x95, 0x1b, 0x10, 0x00, 0x00, 0x1e, 0x41, 0x00, 0xe4, 0x94, 0x1f, 0x41, 0x00,
+0xa8, 0x88, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf,
+0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae, 0x8e, 0x25, 0x41, 0x00,
+0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86, 0x28, 0x41, 0x00, 0xf3,
+0xf2, 0x29, 0x01, 0x00, 0x60, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2b, 0x01,
+0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00,
+0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0x69, 0x49, 0x31, 0x41, 0x00, 0xe2,
+0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34, 0x41, 0x00, 0xee, 0x9e,
+0x35, 0x41, 0x00, 0xf5, 0xf4, 0x00, 0x12, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00,
+0x00, 0x00, 0x00, 0x81, 0x00, 0x02, 0x01, 0x00, 0xfc, 0x31, 0x03, 0x01, 0x00, 0x2d, 0x32, 0x04,
+0x01, 0x00, 0x2f, 0x33, 0x05, 0x01, 0x00, 0x22, 0x34, 0x06, 0x01, 0x00, 0x3a, 0x35, 0x07, 0x01,
+0x00, 0x2c, 0x36, 0x08, 0x01, 0x00, 0x2e, 0x37, 0x09, 0x01, 0x00, 0x5f, 0x38, 0x0a, 0x01, 0x00,
+0x3f, 0x39, 0x0b, 0x01, 0x00, 0x25, 0x30, 0x0c, 0x01, 0x00, 0x21, 0x3d, 0x0d, 0x01, 0x00, 0x3b,
+0x5c, 0x10, 0x41, 0x00, 0xa9, 0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93,
+0x13, 0x41, 0x00, 0xaa, 0x8a, 0x14, 0x41, 0x00, 0xa5, 0x85, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16,
+0x41, 0x00, 0xa3, 0x83, 0x17, 0x41, 0x00, 0xe8, 0x98, 0x18, 0x41, 0x00, 0xe9, 0x99, 0x19, 0x41,
+0x00, 0xa7, 0x87, 0x1a, 0x41, 0x00, 0xe5, 0x95, 0x1b, 0x10, 0x00, 0x00, 0x1e, 0x41, 0x00, 0xe4,
+0x94, 0x1f, 0x41, 0x00, 0xa8, 0x88, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00, 0xa0, 0x80,
+0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41, 0x00, 0xae,
+0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00, 0xa6, 0x86,
+0x28, 0x41, 0x00, 0xf3, 0xf2, 0x29, 0x01, 0x00, 0x60, 0x2b, 0x2a, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x82, 0x2b, 0x01, 0x00, 0x29, 0x28, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d, 0x41, 0x00, 0xe7,
+0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41, 0x00, 0x69, 0x49,
+0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x41, 0x00, 0xa1, 0x81, 0x34,
+0x41, 0x00, 0xee, 0x9e, 0x35, 0x41, 0x00, 0xf5, 0xf4, 0x00, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00,
+0x00, 0x83, 0x00, 0xfe, 0x98, 0x09, 0x09, 0x00, 0x00, 0x55, 0x58, 0x2c, 0x67, 0x00, 0x55, 0x58,
+0x09, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x03, 0xa8, 0x00,
+0xad, 0x03, 0x00, 0x00, 0x52, 0x03, 0xa8, 0x01, 0xad, 0x03, 0x00, 0x00, 0xb5, 0x01, 0xa2, 0x02,
+0x5e, 0x03, 0x00, 0x00, 0x45, 0x75, 0x28, 0x04, 0x8f, 0x04, 0x00, 0x00, 0x47, 0x75, 0x14, 0x05,
+0xa3, 0x05, 0x00, 0x00, 0x48, 0x75, 0x38, 0x06, 0xbe, 0x06, 0x00, 0x00, 0x49, 0x75, 0x4f, 0x07,
+0x06, 0x08, 0x00, 0x00, 0x4a, 0x75, 0x83, 0x08, 0xf1, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x03, 0x00,
+0xca, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0x41, 0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55,
+0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00, 0x6f, 0x4f, 0x1e, 0x41, 0x00, 0x61, 0x41, 0x28,
+0x01, 0x03, 0xcb, 0xcc, 0x29, 0x01, 0x03, 0xc8, 0xc9, 0x2e, 0x41, 0x00, 0x63, 0x43, 0x31, 0x41,
+0x00, 0x6e, 0x4e, 0x39, 0x00, 0x00, 0x20, 0x00, 0x02, 0x03, 0x03, 0x00, 0x00, 0xad, 0xfb, 0x03,
+0x02, 0x03, 0x00, 0x00, 0xfd, 0x04, 0x02, 0x03, 0x00, 0x00, 0xfc, 0x05, 0x03, 0x03, 0x00, 0x00,
+0xcf, 0x9c, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x07, 0x02, 0x03, 0x00, 0x00, 0xac, 0x08, 0x02,
+0x03, 0x00, 0x00, 0xab, 0x09, 0x02, 0x03, 0x00, 0x00, 0xf3, 0x0c, 0x02, 0x03, 0x00, 0x00, 0xbe,
+0x0d, 0x03, 0x03, 0x00, 0x00, 0x9e, 0xf6, 0x10, 0x03, 0x03, 0x00, 0x00, 0x84, 0x8e, 0x11, 0x03,
+0x03, 0x00, 0x00, 0x86, 0x8f, 0x12, 0x03, 0x03, 0x00, 0x00, 0x82, 0x90, 0x13, 0x02, 0x03, 0x00,
+0x00, 0xa9, 0x14, 0x03, 0x03, 0x00, 0x00, 0xe7, 0xe8, 0x15, 0x03, 0x03, 0x00, 0x00, 0x81, 0x9a,
+0x16, 0x03, 0x03, 0x00, 0x00, 0xa3, 0xe9, 0x17, 0x03, 0x03, 0x00, 0x00, 0xa1, 0xd6, 0x18, 0x83,
+0x03, 0x00, 0x18, 0x00, 0x18, 0xa2, 0x18, 0xe0, 0x00, 0x19, 0x03, 0x03, 0x00, 0x00, 0x94, 0x99,
+0x1a, 0x02, 0x03, 0x00, 0x00, 0xae, 0x1b, 0x02, 0x03, 0x00, 0x00, 0xaf, 0x1e, 0x03, 0x03, 0x00,
+0x00, 0xa0, 0xb5, 0x1f, 0x03, 0x03, 0x00, 0x00, 0xe1, 0xf5, 0x20, 0x03, 0x03, 0x00, 0x00, 0xd0,
+0xd1, 0x21, 0x02, 0x03, 0x00, 0x00, 0x9f, 0x26, 0x03, 0x03, 0x00, 0x00, 0x9b, 0x9d, 0x27, 0x03,
+0x03, 0x00, 0x00, 0xf4, 0xf8, 0x28, 0x03, 0x03, 0x00, 0x00, 0xef, 0xf9, 0x29, 0x02, 0x03, 0x00,
+0x00, 0xee, 0x2b, 0x03, 0x03, 0x00, 0x00, 0xaa, 0xdd, 0x2c, 0x03, 0x03, 0x00, 0x00, 0x91, 0x92,
+0x2e, 0x03, 0x03, 0x00, 0x00, 0xb8, 0xbd, 0x31, 0x03, 0x03, 0x00, 0x00, 0xa4, 0xa5, 0x32, 0x02,
+0x03, 0x00, 0x00, 0xe6, 0x33, 0x03, 0x03, 0x00, 0x00, 0x87, 0x80, 0x34, 0x02, 0x03, 0x00, 0x00,
+0xfa, 0x35, 0x02, 0x03, 0x00, 0x00, 0xa8, 0x00, 0x02, 0x03, 0x03, 0x00, 0x00, 0xad, 0xfb, 0x03,
+0x02, 0x03, 0x00, 0x00, 0xfd, 0x04, 0x02, 0x03, 0x00, 0x00, 0xfc, 0x05, 0x03, 0x03, 0x00, 0x00,
+0xcf, 0x9c, 0x07, 0x02, 0x03, 0x00, 0x00, 0xac, 0x08, 0x02, 0x03, 0x00, 0x00, 0xab, 0x09, 0x02,
+0x03, 0x00, 0x00, 0xf3, 0x0c, 0x02, 0x03, 0x00, 0x00, 0xbe, 0x0d, 0x03, 0x03, 0x00, 0x00, 0x9e,
+0xf6, 0x10, 0x03, 0x03, 0x00, 0x00, 0x84, 0x8e, 0x11, 0x03, 0x03, 0x00, 0x00, 0x86, 0x8f, 0x12,
+0x03, 0x03, 0x00, 0x00, 0x82, 0x90, 0x13, 0x02, 0x03, 0x00, 0x00, 0xa9, 0x14, 0x03, 0x03, 0x00,
+0x00, 0xe7, 0xe8, 0x15, 0x03, 0x03, 0x00, 0x00, 0x81, 0x9a, 0x16, 0x03, 0x03, 0x00, 0x00, 0xa3,
+0xe9, 0x17, 0x03, 0x03, 0x00, 0x00, 0xa1, 0xd6, 0x18, 0x83, 0x03, 0x00, 0x18, 0x00, 0x18, 0xa2,
+0x18, 0xe0, 0x00, 0x19, 0x03, 0x03, 0x00, 0x00, 0x94, 0x99, 0x1a, 0x02, 0x03, 0x00, 0x00, 0xae,
+0x1b, 0x02, 0x03, 0x00, 0x00, 0xaf, 0x1e, 0x03, 0x03, 0x00, 0x00, 0xa0, 0xb5, 0x1f, 0x03, 0x03,
+0x00, 0x00, 0xe1, 0xf5, 0x20, 0x03, 0x03, 0x00, 0x00, 0xd0, 0xd1, 0x21, 0x02, 0x03, 0x00, 0x00,
+0x9f, 0x26, 0x03, 0x03, 0x00, 0x00, 0x9b, 0x9d, 0x27, 0x03, 0x03, 0x00, 0x00, 0xf4, 0xf8, 0x28,
+0x03, 0x03, 0x00, 0x00, 0xef, 0xf9, 0x29, 0x02, 0x03, 0x00, 0x00, 0xee, 0x2b, 0x03, 0x03, 0x00,
+0x00, 0xaa, 0xdd, 0x2c, 0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x2e, 0x03, 0x03, 0x00, 0x00, 0xb8,
+0xbd, 0x31, 0x03, 0x03, 0x00, 0x00, 0xa4, 0xa5, 0x32, 0x02, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x03,
+0x03, 0x00, 0x00, 0x87, 0x80, 0x34, 0x02, 0x03, 0x00, 0x00, 0xfa, 0x35, 0x02, 0x03, 0x00, 0x00,
+0xa8, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0xad, 0x03, 0x02, 0x03, 0x00, 0x00, 0xfd, 0x05, 0x03,
+0x07, 0x00, 0x00, 0x00, 0x9c, 0x07, 0x02, 0x03, 0x00, 0x00, 0xac, 0x08, 0x02, 0x03, 0x00, 0x00,
+0xab, 0x0c, 0x02, 0x03, 0x00, 0x00, 0x9d, 0x0d, 0x03, 0x07, 0x00, 0x00, 0x00, 0xf6, 0x10, 0x03,
+0x03, 0x00, 0x00, 0x84, 0x8e, 0x11, 0x03, 0x03, 0x00, 0x00, 0x86, 0x8f, 0x12, 0x03, 0x03, 0x00,
+0x00, 0x82, 0x90, 0x15, 0x03, 0x03, 0x00, 0x00, 0x81, 0x9a, 0x16, 0x02, 0x03, 0x00, 0x00, 0xa3,
+0x17, 0x02, 0x03, 0x00, 0x00, 0xa1, 0x18, 0x02, 0x03, 0x00, 0x00, 0xa2, 0x19, 0x03, 0x03, 0x00,
+0x00, 0x94, 0x99, 0x1a, 0x02, 0x03, 0x00, 0x00, 0xae, 0x1b, 0x02, 0x03, 0x00, 0x00, 0xaf, 0x1e,
+0x02, 0x03, 0x00, 0x00, 0xa0, 0x1f, 0x03, 0x03, 0x00, 0x00, 0xe1, 0x15, 0x21, 0x02, 0x03, 0x00,
+0x00, 0x9f, 0x27, 0x03, 0x03, 0x00, 0x00, 0x14, 0xf8, 0x2b, 0x02, 0x03, 0x00, 0x00, 0xaa, 0x2c,
+0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x2e, 0x03, 0x07, 0x00, 0x00, 0x00, 0x9b, 0x31, 0x03, 0x03,
+0x00, 0x00, 0xa4, 0xa5, 0x32, 0x02, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x03, 0x03, 0x00, 0x00, 0x87,
+0x80, 0x34, 0x02, 0x03, 0x00, 0x00, 0xfa, 0x35, 0x02, 0x03, 0x00, 0x00, 0xa8, 0x00, 0x60, 0x06,
+0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x20, 0x60, 0x7e, 0x03, 0x6e, 0xa4,
+0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x06, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96,
+0x20, 0x5e, 0x27, 0x09, 0x61, 0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3,
+0x43, 0x80, 0x45, 0x90, 0x20, 0x27, 0x22, 0x0a, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94,
+0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0x60, 0x0b, 0x61,
+0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f,
+0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e,
+0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75,
+0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0xef, 0x0f, 0x61,
+0xa0, 0x63, 0x87, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x79, 0xec, 0x41, 0xb5, 0x43,
+0x80, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x59, 0xed, 0x20, 0x27, 0xf9, 0x0c, 0x61,
+0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49,
+0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x20, 0x22, 0x00, 0x05, 0x03, 0x03, 0x00, 0x00, 0xcf, 0x9c, 0x06,
+0x02, 0x03, 0x00, 0x00, 0xd5, 0x0c, 0x02, 0x03, 0x00, 0x00, 0xbe, 0x11, 0x03, 0x03, 0x00, 0x00,
+0x86, 0x8f, 0x13, 0x02, 0x03, 0x00, 0x00, 0xa9, 0x18, 0x03, 0x03, 0x00, 0x00, 0xab, 0xac, 0x1a,
+0x02, 0x03, 0xcc, 0x00, 0xae, 0x1b, 0x02, 0x03, 0xcd, 0x00, 0xaf, 0x1e, 0x03, 0x03, 0x00, 0x00,
+0x91, 0x92, 0x1f, 0x03, 0x07, 0x00, 0x00, 0x00, 0xf5, 0x26, 0x41, 0x00, 0x6c, 0x4c, 0x27, 0x03,
+0x03, 0x00, 0x00, 0xf4, 0xf8, 0x28, 0x01, 0x02, 0x27, 0xcb, 0x2b, 0x03, 0x03, 0x00, 0x00, 0xaa,
+0xdd, 0x2e, 0x03, 0x03, 0x00, 0x00, 0xb8, 0xbd, 0x32, 0x42, 0x00, 0x6d, 0x4d, 0xe6, 0x00, 0x60,
+0x07, 0x61, 0x85, 0x65, 0x8a, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x55, 0xeb, 0x20, 0x60, 0x7e,
+0x09, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x75, 0xe7, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x55,
+0xe8, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41,
+0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x22, 0x09, 0x65, 0x89, 0x69,
+0x8b, 0x75, 0x81, 0x79, 0x98, 0x45, 0xd3, 0x49, 0xd8, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x27,
+0x0d, 0x61, 0xee, 0x65, 0xd0, 0x69, 0xf6, 0x6e, 0xec, 0x6f, 0xfa, 0x75, 0xfc, 0x41, 0xef, 0x45,
+0xd1, 0x49, 0xf7, 0x4e, 0xed, 0x4f, 0xfb, 0x55, 0xfd, 0x20, 0x5b, 0x2d, 0x0b, 0x63, 0x87, 0x6c,
+0x9b, 0x6d, 0x9e, 0x6e, 0xa6, 0x6f, 0xf1, 0x43, 0x80, 0x4c, 0x9d, 0x4d, 0x9f, 0x4e, 0xa7, 0x4f,
+0xf2, 0x20, 0x5d, 0x00, 0x02, 0x03, 0x07, 0x00, 0x00, 0x00, 0xfb, 0x03, 0x02, 0x03, 0x00, 0x00,
+0xfd, 0x04, 0x02, 0x03, 0x00, 0x00, 0xfc, 0x05, 0x03, 0x03, 0x00, 0x00, 0xcf, 0x9c, 0x06, 0x02,
+0x03, 0x00, 0x00, 0xd5, 0x0d, 0x03, 0x07, 0x00, 0x00, 0x00, 0xf6, 0x11, 0x41, 0x00, 0x77, 0x57,
+0x14, 0x41, 0x00, 0x74, 0x54, 0x18, 0x03, 0x03, 0x00, 0x00, 0xab, 0xac, 0x1a, 0x02, 0x03, 0x00,
+0x00, 0xa6, 0x1b, 0x02, 0x03, 0x00, 0x00, 0xa7, 0x1e, 0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x1f,
+0x43, 0x00, 0x73, 0x53, 0xe1, 0xf5, 0x20, 0x41, 0x00, 0x64, 0x44, 0x24, 0x02, 0x03, 0x00, 0x00,
+0xf2, 0x26, 0x41, 0x00, 0x6c, 0x4c, 0x27, 0x03, 0x03, 0x00, 0x00, 0xf4, 0xf8, 0x29, 0x02, 0x03,
+0x00, 0x00, 0xee, 0x2b, 0x02, 0x03, 0x00, 0x00, 0xaa, 0x31, 0x03, 0x03, 0x00, 0x00, 0xbd, 0xbe,
+0x32, 0x02, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x02, 0x03, 0x00, 0x00, 0xae, 0x34, 0x02, 0x03, 0x00,
+0x00, 0xaf, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41,
+0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e,
+0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20, 0x7e, 0x5e, 0x17, 0x61, 0x83, 0x65,
+0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x77, 0xf7, 0x73, 0xf9, 0x64, 0xd0, 0x6c, 0x9b, 0x6e,
+0x9e, 0x74, 0xe7, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x53, 0xdd, 0x57,
+0xef, 0x44, 0xd1, 0x4c, 0x9d, 0x4e, 0x9f, 0x54, 0xe8, 0x20, 0x5e, 0x27, 0x0d, 0x61, 0xa0, 0x65,
+0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x63, 0x87, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f,
+0xe0, 0x55, 0xe9, 0x43, 0x80, 0x20, 0x27, 0x22, 0x0f, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6e,
+0xa9, 0x6f, 0x94, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4e, 0xb8, 0x4f,
+0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0xad, 0x06, 0x02,
+0x03, 0x00, 0x00, 0xd5, 0x12, 0x03, 0x03, 0x00, 0x00, 0xfa, 0xfb, 0x15, 0x03, 0x03, 0x00, 0x00,
+0xec, 0xed, 0x18, 0x03, 0x03, 0x00, 0x00, 0xab, 0xac, 0x1a, 0x43, 0x00, 0x86, 0x8f, 0x5b, 0x7b,
+0x1b, 0x43, 0x00, 0x9b, 0x9d, 0x5d, 0x7d, 0x1e, 0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x1f, 0x03,
+0x07, 0x00, 0x00, 0x00, 0xf5, 0x20, 0x03, 0x03, 0x00, 0x00, 0xd0, 0xd1, 0x22, 0x03, 0x03, 0x00,
+0x00, 0xa9, 0xb8, 0x24, 0x03, 0x03, 0x00, 0x00, 0xbd, 0xbe, 0x27, 0x03, 0x07, 0x00, 0x00, 0x00,
+0xf8, 0x2b, 0x02, 0x07, 0x00, 0x00, 0xcd, 0x30, 0x03, 0x03, 0x00, 0x00, 0xe7, 0xe8, 0x31, 0x03,
+0x03, 0x00, 0x00, 0xa6, 0xa7, 0x32, 0x02, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x02, 0x03, 0x00, 0x00,
+0xae, 0x34, 0x02, 0x03, 0x00, 0x00, 0xaf, 0x35, 0x02, 0x03, 0x00, 0x00, 0xa8, 0x00, 0x60, 0x0b,
+0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde,
+0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x05, 0x61, 0xc6, 0x6e, 0xa4, 0x41, 0xc7, 0x4e, 0xa5,
+0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88, 0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6,
+0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea, 0x20, 0x5e, 0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82,
+0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x63, 0x87, 0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0,
+0x55, 0xe9, 0x43, 0x80, 0x20, 0x27, 0x22, 0x11, 0x61, 0x84, 0x65, 0x89, 0x86, 0xf1, 0x69, 0x8b,
+0x6f, 0x94, 0x9b, 0x9e, 0x75, 0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x8f, 0xf2, 0x49, 0xd8,
+0x4f, 0x99, 0x9d, 0x9f, 0x55, 0x9a, 0x59, 0xf3, 0x20, 0x22, 0xaa, 0x09, 0x61, 0x9c, 0x69, 0xf4,
+0x6f, 0xe1, 0x9b, 0xfc, 0x41, 0xcf, 0x49, 0xf6, 0x4f, 0xee, 0x9d, 0xfd, 0x20, 0xaa, 0x00, 0x05,
+0x02, 0x03, 0x00, 0x00, 0xcf, 0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x11, 0x03, 0x03, 0x00, 0x00,
+0x94, 0x99, 0x12, 0x03, 0x03, 0x00, 0x00, 0x86, 0x8f, 0x13, 0x03, 0x03, 0x00, 0x00, 0x9c, 0xe6,
+0x15, 0x03, 0x03, 0x00, 0x00, 0xec, 0xed, 0x16, 0x03, 0x03, 0x00, 0x00, 0xe1, 0xee, 0x17, 0x03,
+0x03, 0x00, 0x00, 0x84, 0x8e, 0x18, 0x03, 0x03, 0x00, 0x00, 0xab, 0xac, 0x19, 0x03, 0x03, 0x00,
+0x00, 0x9b, 0x9d, 0x1a, 0x02, 0x03, 0x00, 0x00, 0xa6, 0x1b, 0x02, 0x03, 0x00, 0x00, 0xa7, 0x1e,
+0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x1f, 0x03, 0x07, 0x00, 0x00, 0x00, 0xf5, 0x20, 0x03, 0x03,
+0x00, 0x00, 0xd0, 0xd1, 0x21, 0x03, 0x03, 0x00, 0x00, 0x9f, 0x9e, 0x22, 0x03, 0x03, 0x00, 0x00,
+0xa9, 0xb8, 0x23, 0x03, 0x03, 0x00, 0x00, 0xbd, 0xbe, 0x24, 0x03, 0x03, 0x00, 0x00, 0xf7, 0xef,
+0x27, 0x03, 0x07, 0x00, 0x00, 0x00, 0xf8, 0x2b, 0x02, 0x03, 0x00, 0x00, 0xaa, 0x2e, 0x03, 0x03,
+0x00, 0x00, 0xfa, 0xfb, 0x2f, 0x03, 0x03, 0x00, 0x00, 0xf9, 0xdd, 0x30, 0x03, 0x03, 0x00, 0x00,
+0xe7, 0xe8, 0x31, 0x03, 0x03, 0x00, 0x00, 0xa8, 0xad, 0x33, 0x02, 0x03, 0x00, 0x00, 0xae, 0x34,
+0x02, 0x03, 0x00, 0x00, 0xaf, 0x00, 0x60, 0x0b, 0x61, 0x85, 0x65, 0x8a, 0x69, 0x8d, 0x6f, 0x95,
+0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x49, 0xde, 0x4f, 0xe3, 0x55, 0xeb, 0x20, 0x60, 0x7e, 0x0d,
+0x61, 0xc6, 0x65, 0xfc, 0x69, 0xf1, 0x6f, 0xe4, 0x75, 0xf4, 0x6e, 0xa4, 0x41, 0xc7, 0x45, 0xfd,
+0x49, 0xf2, 0x4f, 0xe5, 0x55, 0xf6, 0x4e, 0xa5, 0x20, 0x7e, 0x5e, 0x0b, 0x61, 0x83, 0x65, 0x88,
+0x69, 0x8c, 0x6f, 0x93, 0x75, 0x96, 0x41, 0xb6, 0x45, 0xd2, 0x49, 0xd7, 0x4f, 0xe2, 0x55, 0xea,
+0x20, 0x5e, 0x27, 0x0d, 0x61, 0xa0, 0x65, 0x82, 0x69, 0xa1, 0x6f, 0xa2, 0x75, 0xa3, 0x63, 0x87,
+0x41, 0xb5, 0x45, 0x90, 0x49, 0xd6, 0x4f, 0xe0, 0x55, 0xe9, 0x43, 0x80, 0x20, 0x27, 0x22, 0x09,
+0x65, 0x89, 0x69, 0x8b, 0x75, 0x81, 0x79, 0x98, 0x45, 0xd3, 0x49, 0xd8, 0x55, 0x9a, 0x59, 0xf3,
+0x20, 0x22, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0xad, 0x05, 0x03, 0x03, 0x00, 0x00, 0xcf, 0x9c,
+0x06, 0x02, 0x03, 0x00, 0x00, 0xd5, 0x10, 0x02, 0x03, 0x00, 0x00, 0xa6, 0x18, 0x03, 0x03, 0x00,
+0x00, 0xab, 0xac, 0x19, 0x02, 0x03, 0x00, 0x00, 0xa7, 0x1a, 0x43, 0x00, 0x9e, 0x9f, 0x5b, 0x7b,
+0x1b, 0x43, 0x00, 0x9b, 0x9d, 0x5d, 0x7d, 0x1e, 0x03, 0x03, 0x00, 0x00, 0x91, 0x92, 0x1f, 0x03,
+0x07, 0x00, 0x00, 0x00, 0xf5, 0x27, 0x03, 0x07, 0x00, 0x00, 0x00, 0xf8, 0x29, 0x02, 0x07, 0x00,
+0x00, 0xcd, 0x2b, 0x02, 0x03, 0x00, 0x00, 0xaa, 0x32, 0x02, 0x03, 0x00, 0x00, 0xe6, 0x33, 0x02,
+0x03, 0x00, 0x00, 0xae, 0x34, 0x02, 0x03, 0x00, 0x00, 0xaf, 0x35, 0x02, 0x03, 0x00, 0x00, 0xa8,
+0x00, 0x60, 0x07, 0x61, 0x85, 0x65, 0x8a, 0x75, 0x97, 0x41, 0xb7, 0x45, 0xd4, 0x55, 0xeb, 0x20,
+0x60, 0x7e, 0x07, 0x61, 0xc6, 0x6e, 0xa4, 0x6f, 0xe4, 0x41, 0xc7, 0x4e, 0xa5, 0x4f, 0xe5, 0x20,
+0x7e, 0x5e, 0x0f, 0x61, 0x83, 0x65, 0x88, 0x9e, 0xd0, 0x69, 0x8c, 0x6f, 0x93, 0x9b, 0xfa, 0x75,
+0x96, 0x41, 0xb6, 0x45, 0xd2, 0x9f, 0xd1, 0x49, 0xd7, 0x4f, 0xe2, 0x9d, 0xfb, 0x55, 0xea, 0x20,
+0x5e, 0x27, 0x11, 0x61, 0xa0, 0x65, 0x82, 0x9e, 0xf1, 0x69, 0xa1, 0x6f, 0xa2, 0x9b, 0xec, 0x75,
+0xa3, 0x63, 0x87, 0x41, 0xb5, 0x45, 0x90, 0x9f, 0xf2, 0x49, 0xd6, 0x4f, 0xe0, 0x9d, 0xed, 0x55,
+0xe9, 0x43, 0x80, 0x20, 0x27, 0x22, 0x0d, 0x61, 0x84, 0x65, 0x89, 0x69, 0x8b, 0x6f, 0x94, 0x75,
+0x81, 0x79, 0x98, 0x41, 0x8e, 0x45, 0xd3, 0x49, 0xd8, 0x4f, 0x99, 0x55, 0x9a, 0x59, 0xf3, 0x20,
+0x22, 0xee, 0x0d, 0x61, 0x86, 0x65, 0xbd, 0x9e, 0xe7, 0x69, 0xa9, 0x6f, 0xf4, 0x9b, 0xfc, 0x41,
+0x8f, 0x45, 0xbe, 0x9f, 0xe8, 0x49, 0xb8, 0x4f, 0xf6, 0x9d, 0xfd, 0x20, 0xee, 0x00, 0x49, 0x4d,
+0x01, 0x04, 0x00, 0x00, 0x55, 0x5a, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x62, 0xf3, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x62, 0xf3, 0x44, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x22, 0x40,
+0x04, 0x02, 0x01, 0x00, 0xfc, 0x23, 0x05, 0x02, 0x01, 0x00, 0x3b, 0xfd, 0x07, 0x02, 0x01, 0x00,
+0x3a, 0x5e, 0x08, 0x02, 0x01, 0x00, 0x3f, 0x26, 0x09, 0x02, 0x03, 0x00, 0x00, 0x24, 0x0c, 0x43,
+0x00, 0xf3, 0xf2, 0x2d, 0x5f, 0x0d, 0x43, 0x00, 0xf9, 0xf8, 0x3d, 0x2b, 0x10, 0x41, 0x00, 0xa9,
+0x89, 0x11, 0x41, 0x00, 0xe6, 0x96, 0x12, 0x41, 0x00, 0xe3, 0x93, 0x13, 0x41, 0x00, 0xaa, 0x8a,
+0x14, 0x41, 0x00, 0xa5, 0x85, 0x15, 0x41, 0x00, 0xad, 0x8d, 0x16, 0x41, 0x00, 0xa3, 0x83, 0x17,
+0x41, 0x00, 0xe8, 0x98, 0x18, 0x43, 0x00, 0xf7, 0xf6, 0xe9, 0x99, 0x19, 0x41, 0x00, 0xa7, 0x87,
+0x1a, 0x42, 0x00, 0xe5, 0x95, 0x5b, 0x1b, 0x42, 0x00, 0xea, 0x9a, 0x5d, 0x1e, 0x41, 0x00, 0xe4,
+0x94, 0x1f, 0x43, 0x00, 0xf5, 0xf4, 0xeb, 0x9b, 0x20, 0x41, 0x00, 0xa2, 0x82, 0x21, 0x41, 0x00,
+0xa0, 0x80, 0x22, 0x41, 0x00, 0xaf, 0x8f, 0x23, 0xc1, 0x00, 0xe0, 0x00, 0x90, 0x23, 0x24, 0x41,
+0x00, 0xae, 0x8e, 0x25, 0x41, 0x00, 0xab, 0x8b, 0x26, 0x41, 0x00, 0xa4, 0x84, 0x27, 0x41, 0x00,
+0xa6, 0x86, 0x28, 0x41, 0x00, 0xed, 0x9d, 0x29, 0x41, 0x00, 0xf1, 0xf0, 0x2a, 0x04, 0x1f, 0x00,
+0x00, 0x00, 0x00, 0x78, 0x2b, 0x02, 0x01, 0x00, 0x2f, 0x7c, 0x2c, 0x41, 0x00, 0xef, 0x9f, 0x2d,
+0x41, 0x00, 0xe7, 0x97, 0x2e, 0x41, 0x00, 0xe1, 0x91, 0x2f, 0x41, 0x00, 0xac, 0x8c, 0x30, 0x41,
+0x00, 0xa8, 0x88, 0x31, 0x41, 0x00, 0xe2, 0x92, 0x32, 0x41, 0x00, 0xec, 0x9c, 0x33, 0x42, 0x00,
+0xa1, 0x81, 0x3c, 0x34, 0x42, 0x00, 0xee, 0x9e, 0x3e, 0x35, 0x01, 0x00, 0x2e, 0x2c, 0x00, 0x05,
+0x02, 0x03, 0x00, 0x00, 0xfd, 0x36, 0x04, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x38, 0xbe,
+0x01, 0x09, 0x00, 0x00, 0x56, 0x49, 0x2c, 0xcd, 0x01, 0x56, 0x49, 0x02, 0x02, 0x00, 0x00, 0x00,
+0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x75, 0x34, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00,
+0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+0x43, 0x00, 0xe5, 0xc5, 0x31, 0x21, 0x03, 0x43, 0x00, 0xe2, 0xc2, 0x32, 0x40, 0x04, 0x43, 0x00,
+0xea, 0xca, 0x33, 0x23, 0x05, 0x43, 0x00, 0xf4, 0xd4, 0x34, 0x24, 0x06, 0x03, 0x03, 0xc8, 0xc8,
+0x35, 0x25, 0x07, 0x03, 0x03, 0xc9, 0xc9, 0x36, 0x5e, 0x08, 0x03, 0x03, 0xca, 0xca, 0x37, 0x26,
+0x09, 0x03, 0x03, 0xcb, 0xcb, 0x38, 0x2a, 0x0a, 0x03, 0x03, 0xcc, 0xcc, 0x39, 0x28, 0x0b, 0xc3,
+0x00, 0xf0, 0x00, 0xd0, 0x0b, 0x30, 0x81, 0x29, 0x81, 0x12, 0x41, 0x00, 0x65, 0x45, 0x15, 0x41,
+0x00, 0x79, 0x59, 0x16, 0x41, 0x00, 0x75, 0x55, 0x17, 0x41, 0x00, 0x69, 0x49, 0x18, 0x41, 0x00,
+0x6f, 0x4f, 0x1a, 0x43, 0x00, 0xdf, 0xbf, 0x5b, 0x7b, 0x1b, 0x43, 0x00, 0xbd, 0xb4, 0x5d, 0x7d,
+0x1e, 0x41, 0x00, 0x61, 0x41, 0x39, 0x00, 0x00, 0x20, 0x00, 0x60, 0x19, 0x61, 0xe0, 0xe5, 0xa2,
+0xe2, 0xa5, 0x65, 0xe8, 0xea, 0xab, 0x69, 0xec, 0x6f, 0xf2, 0xf4, 0xb0, 0xbd, 0xb6, 0x75, 0xf9,
+0xdf, 0xd7, 0x79, 0xcf, 0x41, 0xc0, 0xc5, 0x82, 0xc2, 0x85, 0x45, 0xc8, 0xca, 0x8b, 0x49, 0xcc,
+0x4f, 0xd2, 0xd4, 0x90, 0xb4, 0x96, 0x55, 0xd9, 0xbf, 0xbb, 0x59, 0x9f, 0x20, 0x60, 0x22, 0x19,
+0x61, 0xe4, 0xe5, 0xc6, 0xe2, 0xa6, 0x65, 0xeb, 0xea, 0xac, 0x69, 0xef, 0x6f, 0xf6, 0xf4, 0xb1,
+0xbd, 0xb7, 0x75, 0xfc, 0xdf, 0xd8, 0x79, 0xd6, 0x41, 0xc4, 0xc5, 0x02, 0xc2, 0x86, 0x45, 0xcb,
+0xca, 0x8c, 0x49, 0x9b, 0x4f, 0x99, 0xd4, 0x91, 0xb4, 0x97, 0x55, 0x9c, 0xbf, 0xbc, 0x59, 0x14,
+0x20, 0x22, 0x7e, 0x19, 0x61, 0xe3, 0xe5, 0xc7, 0xe2, 0xe7, 0x65, 0xa8, 0xea, 0xad, 0x69, 0xee,
+0x6f, 0xf5, 0xf4, 0xb2, 0xbd, 0xde, 0x75, 0xfb, 0xdf, 0xe6, 0x79, 0xdb, 0x41, 0xc3, 0xc5, 0x05,
+0xc2, 0x06, 0x45, 0x88, 0xca, 0x8d, 0x49, 0xce, 0x4f, 0xa0, 0xd4, 0x92, 0xb4, 0xb3, 0x55, 0x9d,
+0xbf, 0xff, 0x59, 0x19, 0x20, 0x7e, 0x27, 0x19, 0x61, 0xe1, 0xe5, 0xa1, 0xe2, 0xa4, 0x65, 0xe9,
+0xea, 0xaa, 0x69, 0xed, 0x6f, 0xf3, 0xf4, 0xaf, 0xbd, 0xbe, 0x75, 0xfa, 0xdf, 0xd1, 0x79, 0xfd,
+0x41, 0xc1, 0xc5, 0x81, 0xc2, 0x84, 0x45, 0xc9, 0xca, 0x8a, 0x49, 0xcd, 0x4f, 0xd3, 0xd4, 0x8f,
+0xb4, 0x95, 0x55, 0xda, 0xbf, 0xba, 0x59, 0xdd, 0x20, 0x27, 0x2e, 0x19, 0x61, 0xd5, 0xe5, 0xa3,
+0xe2, 0xa7, 0x65, 0xa9, 0xea, 0xae, 0x69, 0xb8, 0x6f, 0xf7, 0xf4, 0xb5, 0xbd, 0xfe, 0x75, 0xf8,
+0xdf, 0xf1, 0x79, 0xdc, 0x41, 0x80, 0xc5, 0x83, 0xc2, 0x87, 0x45, 0x89, 0xca, 0x8e, 0x49, 0x98,
+0x4f, 0x9a, 0xd4, 0x93, 0xb4, 0x94, 0x55, 0x9e, 0xbf, 0xb9, 0x59, 0x1e, 0x20, 0x2e, 0x00, 0x5c,
+0x00, 0x00 };
diff --git a/src/dos/dos_memory.cpp b/src/dos/dos_memory.cpp
new file mode 100644
index 000000000..6037eec0b
--- /dev/null
+++ b/src/dos/dos_memory.cpp
@@ -0,0 +1,448 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "mem.h"
+#include "dos_inc.h"
+
+#define UMB_START_SEG 0x9fff
+
+static Bit16u memAllocStrategy = 0x00;
+
+static void DOS_CompressMemory(void) {
+ Bit16u mcb_segment=dos.firstMCB;
+ DOS_MCB mcb(mcb_segment);
+ DOS_MCB mcb_next(0);
+
+ while (mcb.GetType()!=0x5a) {
+ mcb_next.SetPt((Bit16u)(mcb_segment+mcb.GetSize()+1));
+ if (GCC_UNLIKELY((mcb_next.GetType()!=0x4d) && (mcb_next.GetType()!=0x5a))) E_Exit("Corrupt MCB chain");
+ if ((mcb.GetPSPSeg()==MCB_FREE) && (mcb_next.GetPSPSeg()==MCB_FREE)) {
+ mcb.SetSize(mcb.GetSize()+mcb_next.GetSize()+1);
+ mcb.SetType(mcb_next.GetType());
+ } else {
+ mcb_segment+=mcb.GetSize()+1;
+ mcb.SetPt(mcb_segment);
+ }
+ }
+}
+
+void DOS_FreeProcessMemory(Bit16u pspseg) {
+ Bit16u mcb_segment=dos.firstMCB;
+ DOS_MCB mcb(mcb_segment);
+ for (;;) {
+ if (mcb.GetPSPSeg()==pspseg) {
+ mcb.SetPSPSeg(MCB_FREE);
+ }
+ if (mcb.GetType()==0x5a) break;
+ if (GCC_UNLIKELY(mcb.GetType()!=0x4d)) E_Exit("Corrupt MCB chain");
+ mcb_segment+=mcb.GetSize()+1;
+ mcb.SetPt(mcb_segment);
+ }
+
+ Bit16u umb_start=dos_infoblock.GetStartOfUMBChain();
+ if (umb_start==UMB_START_SEG) {
+ DOS_MCB umb_mcb(umb_start);
+ for (;;) {
+ if (umb_mcb.GetPSPSeg()==pspseg) {
+ umb_mcb.SetPSPSeg(MCB_FREE);
+ }
+ if (umb_mcb.GetType()!=0x4d) break;
+ umb_start+=umb_mcb.GetSize()+1;
+ umb_mcb.SetPt(umb_start);
+ }
+ } else if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start);
+
+ DOS_CompressMemory();
+}
+
+Bit16u DOS_GetMemAllocStrategy() {
+ return memAllocStrategy;
+}
+
+bool DOS_SetMemAllocStrategy(Bit16u strat) {
+ if ((strat&0x3f)<3) {
+ memAllocStrategy = strat;
+ return true;
+ }
+ /* otherwise an invalid allocation strategy was specified */
+ return false;
+}
+
+bool DOS_AllocateMemory(Bit16u * segment,Bit16u * blocks) {
+ DOS_CompressMemory();
+ Bit16u bigsize=0;
+ Bit16u mem_strat=memAllocStrategy;
+ Bit16u mcb_segment=dos.firstMCB;
+
+ Bit16u umb_start=dos_infoblock.GetStartOfUMBChain();
+ if (umb_start==UMB_START_SEG) {
+ /* start with UMBs if requested (bits 7 or 6 set) */
+ if (mem_strat&0xc0) mcb_segment=umb_start;
+ } else if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start);
+
+ DOS_MCB mcb(0);
+ DOS_MCB mcb_next(0);
+ DOS_MCB psp_mcb(dos.psp()-1);
+ char psp_name[9];
+ psp_mcb.GetFileName(psp_name);
+ Bit16u found_seg=0,found_seg_size=0;
+ for (;;) {
+ mcb.SetPt(mcb_segment);
+ if (mcb.GetPSPSeg()==MCB_FREE) {
+ /* Check for enough free memory in current block */
+ Bit16u block_size=mcb.GetSize();
+ if (block_size<(*blocks)) {
+ if (bigsize<block_size) {
+ /* current block is largest block that was found,
+ but still not as big as requested */
+ bigsize=block_size;
+ }
+ } else if ((block_size==*blocks) && ((mem_strat & 0x3f)<2)) {
+ /* MCB fits precisely, use it if search strategy is firstfit or bestfit */
+ mcb.SetPSPSeg(dos.psp());
+ *segment=mcb_segment+1;
+ return true;
+ } else {
+ switch (mem_strat & 0x3f) {
+ case 0: /* firstfit */
+ mcb_next.SetPt((Bit16u)(mcb_segment+*blocks+1));
+ mcb_next.SetPSPSeg(MCB_FREE);
+ mcb_next.SetType(mcb.GetType());
+ mcb_next.SetSize(block_size-*blocks-1);
+ mcb.SetSize(*blocks);
+ mcb.SetType(0x4d);
+ mcb.SetPSPSeg(dos.psp());
+ mcb.SetFileName(psp_name);
+ //TODO Filename
+ *segment=mcb_segment+1;
+ return true;
+ case 1: /* bestfit */
+ if ((found_seg_size==0) || (block_size<found_seg_size)) {
+ /* first fitting MCB, or smaller than the last that was found */
+ found_seg=mcb_segment;
+ found_seg_size=block_size;
+ }
+ break;
+ default: /* everything else is handled as lastfit by dos */
+ /* MCB is large enough, note it down */
+ found_seg=mcb_segment;
+ found_seg_size=block_size;
+ break;
+ }
+ }
+ }
+ /* Onward to the next MCB if there is one */
+ if (mcb.GetType()==0x5a) {
+ if ((mem_strat&0x80) && (umb_start==UMB_START_SEG)) {
+ /* bit 7 set: try high memory first, then low */
+ mcb_segment=dos.firstMCB;
+ mem_strat&=(~0xc0);
+ } else {
+ /* finished searching all requested MCB chains */
+ if (found_seg) {
+ /* a matching MCB was found (cannot occur for firstfit) */
+ if ((mem_strat & 0x3f)==0x01) {
+ /* bestfit, allocate block at the beginning of the MCB */
+ mcb.SetPt(found_seg);
+
+ mcb_next.SetPt((Bit16u)(found_seg+*blocks+1));
+ mcb_next.SetPSPSeg(MCB_FREE);
+ mcb_next.SetType(mcb.GetType());
+ mcb_next.SetSize(found_seg_size-*blocks-1);
+
+ mcb.SetSize(*blocks);
+ mcb.SetType(0x4d);
+ mcb.SetPSPSeg(dos.psp());
+ mcb.SetFileName(psp_name);
+ //TODO Filename
+ *segment=found_seg+1;
+ } else {
+ /* lastfit, allocate block at the end of the MCB */
+ mcb.SetPt(found_seg);
+ if (found_seg_size==*blocks) {
+ /* use the whole block */
+ mcb.SetPSPSeg(dos.psp());
+ //Not consistent with line 124. But how many application will use this information ?
+ mcb.SetFileName(psp_name);
+ *segment = found_seg+1;
+ return true;
+ }
+ *segment = found_seg+1+found_seg_size - *blocks;
+ mcb_next.SetPt((Bit16u)(*segment-1));
+ mcb_next.SetSize(*blocks);
+ mcb_next.SetType(mcb.GetType());
+ mcb_next.SetPSPSeg(dos.psp());
+ mcb_next.SetFileName(psp_name);
+ // Old Block
+ mcb.SetSize(found_seg_size-*blocks-1);
+ mcb.SetPSPSeg(MCB_FREE);
+ mcb.SetType(0x4D);
+ }
+ return true;
+ }
+ /* no fitting MCB found, return size of largest block */
+ *blocks=bigsize;
+ DOS_SetError(DOSERR_INSUFFICIENT_MEMORY);
+ return false;
+ }
+ } else mcb_segment+=mcb.GetSize()+1;
+ }
+ return false;
+}
+
+
+bool DOS_ResizeMemory(Bit16u segment,Bit16u * blocks) {
+ if (segment < DOS_MEM_START+1) {
+ LOG(LOG_DOSMISC,LOG_ERROR)("Program resizes %X, take care",segment);
+ }
+
+ DOS_MCB mcb(segment-1);
+ if ((mcb.GetType()!=0x4d) && (mcb.GetType()!=0x5a)) {
+ DOS_SetError(DOSERR_MCB_DESTROYED);
+ return false;
+ }
+
+ DOS_CompressMemory();
+ Bit16u total=mcb.GetSize();
+ DOS_MCB mcb_next(segment+total);
+ if (*blocks<=total) {
+ if (GCC_UNLIKELY(*blocks==total)) {
+ /* Nothing to do */
+ return true;
+ }
+ /* Shrinking MCB */
+ DOS_MCB mcb_new_next(segment+(*blocks));
+ mcb.SetSize(*blocks);
+ mcb_new_next.SetType(mcb.GetType());
+ if (mcb.GetType()==0x5a) {
+ /* Further blocks follow */
+ mcb.SetType(0x4d);
+ }
+
+ mcb_new_next.SetSize(total-*blocks-1);
+ mcb_new_next.SetPSPSeg(MCB_FREE);
+ mcb.SetPSPSeg(dos.psp());
+ DOS_CompressMemory();
+ return true;
+ }
+ /* MCB will grow, try to join with following MCB */
+ if (mcb.GetType()!=0x5a) {
+ if (mcb_next.GetPSPSeg()==MCB_FREE) {
+ total+=mcb_next.GetSize()+1;
+ }
+ }
+ if (*blocks<total) {
+ if (mcb.GetType()!=0x5a) {
+ /* save type of following MCB */
+ mcb.SetType(mcb_next.GetType());
+ }
+ mcb.SetSize(*blocks);
+ mcb_next.SetPt((Bit16u)(segment+*blocks));
+ mcb_next.SetSize(total-*blocks-1);
+ mcb_next.SetType(mcb.GetType());
+ mcb_next.SetPSPSeg(MCB_FREE);
+ mcb.SetType(0x4d);
+ mcb.SetPSPSeg(dos.psp());
+ return true;
+ }
+
+ /* at this point: *blocks==total (fits) or *blocks>total,
+ in the second case resize block to maximum */
+
+ if ((mcb_next.GetPSPSeg()==MCB_FREE) && (mcb.GetType()!=0x5a)) {
+ /* adjust type of joined MCB */
+ mcb.SetType(mcb_next.GetType());
+ }
+ mcb.SetSize(total);
+ mcb.SetPSPSeg(dos.psp());
+ if (*blocks==total) return true; /* block fit exactly */
+
+ *blocks=total; /* return maximum */
+ DOS_SetError(DOSERR_INSUFFICIENT_MEMORY);
+ return false;
+}
+
+
+bool DOS_FreeMemory(Bit16u segment) {
+//TODO Check if allowed to free this segment
+ if (segment < DOS_MEM_START+1) {
+ LOG(LOG_DOSMISC,LOG_ERROR)("Program tried to free %X ---ERROR",segment);
+ DOS_SetError(DOSERR_MB_ADDRESS_INVALID);
+ return false;
+ }
+
+ DOS_MCB mcb(segment-1);
+ if ((mcb.GetType()!=0x4d) && (mcb.GetType()!=0x5a)) {
+ DOS_SetError(DOSERR_MB_ADDRESS_INVALID);
+ return false;
+ }
+ mcb.SetPSPSeg(MCB_FREE);
+// DOS_CompressMemory();
+ return true;
+}
+
+
+void DOS_BuildUMBChain(bool umb_active,bool ems_active) {
+ if (umb_active && (!IS_TANDY_ARCH)) {
+ Bit16u first_umb_seg = 0xd000;
+ Bit16u first_umb_size = 0x2000;
+ if(ems_active) first_umb_size = 0x1000;
+
+ dos_infoblock.SetStartOfUMBChain(UMB_START_SEG);
+ dos_infoblock.SetUMBChainState(0); // UMBs not linked yet
+
+ DOS_MCB umb_mcb(first_umb_seg);
+ umb_mcb.SetPSPSeg(0); // currently free
+ umb_mcb.SetSize(first_umb_size-1);
+ umb_mcb.SetType(0x5a);
+
+ /* Scan MCB-chain for last block */
+ Bit16u mcb_segment=dos.firstMCB;
+ DOS_MCB mcb(mcb_segment);
+ while (mcb.GetType()!=0x5a) {
+ mcb_segment+=mcb.GetSize()+1;
+ mcb.SetPt(mcb_segment);
+ }
+
+ /* A system MCB has to cover the space between the
+ regular MCB-chain and the UMBs */
+ Bit16u cover_mcb=(Bit16u)(mcb_segment+mcb.GetSize()+1);
+ mcb.SetPt(cover_mcb);
+ mcb.SetType(0x4d);
+ mcb.SetPSPSeg(0x0008);
+ mcb.SetSize(first_umb_seg-cover_mcb-1);
+ mcb.SetFileName("SC ");
+
+ } else {
+ dos_infoblock.SetStartOfUMBChain(0xffff);
+ dos_infoblock.SetUMBChainState(0);
+ }
+}
+
+bool DOS_LinkUMBsToMemChain(Bit16u linkstate) {
+ /* Get start of UMB-chain */
+ Bit16u umb_start=dos_infoblock.GetStartOfUMBChain();
+ if (umb_start!=UMB_START_SEG) {
+ if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start);
+ return false;
+ }
+
+ if ((linkstate&1)==(dos_infoblock.GetUMBChainState()&1)) return true;
+
+ /* Scan MCB-chain for last block before UMB-chain */
+ Bit16u mcb_segment=dos.firstMCB;
+ Bit16u prev_mcb_segment=dos.firstMCB;
+ DOS_MCB mcb(mcb_segment);
+ while ((mcb_segment!=umb_start) && (mcb.GetType()!=0x5a)) {
+ prev_mcb_segment=mcb_segment;
+ mcb_segment+=mcb.GetSize()+1;
+ mcb.SetPt(mcb_segment);
+ }
+ DOS_MCB prev_mcb(prev_mcb_segment);
+
+ switch (linkstate) {
+ case 0x0000: // unlink
+ if ((prev_mcb.GetType()==0x4d) && (mcb_segment==umb_start)) {
+ prev_mcb.SetType(0x5a);
+ }
+ dos_infoblock.SetUMBChainState(0);
+ break;
+ case 0x0001: // link
+ if (mcb.GetType()==0x5a) {
+ mcb.SetType(0x4d);
+ dos_infoblock.SetUMBChainState(1);
+ }
+ break;
+ default:
+ LOG_MSG("Invalid link state %x when reconfiguring MCB chain",linkstate);
+ return false;
+ }
+
+ return true;
+}
+
+
+void DOS_SetupMemory(void) {
+ /* Let dos claim a few bios interrupts. Makes DOSBox more compatible with
+ * buggy games, which compare against the interrupt table. (probably a
+ * broken linked list implementation) */
+ Bit16u ihseg = 0x70;
+ Bit16u ihofs = 0xF4;
+ real_writeb(ihseg,ihofs,(Bit8u)0xCF); //An IRET Instruction
+ RealSetVec(0x01,RealMake(ihseg,ihofs)); //BioMenace (offset!=4)
+ RealSetVec(0x02,RealMake(ihseg,ihofs)); //BioMenace (segment<0x8000)
+ RealSetVec(0x03,RealMake(ihseg,ihofs)); //Alien Incident (offset!=0)
+ RealSetVec(0x04,RealMake(ihseg,ihofs)); //Shadow President (lower byte of segment!=0)
+ RealSetVec(0x0f,RealMake(ihseg,ihofs)); //Always a tricky one (soundblaster irq)
+
+ // Create a dummy device MCB with PSPSeg=0x0008
+ DOS_MCB mcb_devicedummy((Bit16u)DOS_MEM_START);
+ mcb_devicedummy.SetPSPSeg(MCB_DOS); // Devices
+ mcb_devicedummy.SetSize(1);
+ mcb_devicedummy.SetType(0x4d); // More blocks will follow
+// mcb_devicedummy.SetFileName("SD ");
+
+ Bit16u mcb_sizes=2;
+ // Create a small empty MCB (result from a growing environment block)
+ DOS_MCB tempmcb((Bit16u)DOS_MEM_START+mcb_sizes);
+ tempmcb.SetPSPSeg(MCB_FREE);
+ tempmcb.SetSize(4);
+ mcb_sizes+=5;
+ tempmcb.SetType(0x4d);
+
+ // Lock the previous empty MCB
+ DOS_MCB tempmcb2((Bit16u)DOS_MEM_START+mcb_sizes);
+ tempmcb2.SetPSPSeg(0x40); // can be removed by loadfix
+ tempmcb2.SetSize(16);
+ mcb_sizes+=17;
+ tempmcb2.SetType(0x4d);
+
+ DOS_MCB mcb((Bit16u)DOS_MEM_START+mcb_sizes);
+ mcb.SetPSPSeg(MCB_FREE); //Free
+ mcb.SetType(0x5a); //Last Block
+ if (machine==MCH_TANDY) {
+ /* memory up to 608k available, the rest (to 640k) is used by
+ the tandy graphics system's variable mapping of 0xb800 */
+ mcb.SetSize(0x9BFF - DOS_MEM_START - mcb_sizes);
+ } else if (machine==MCH_PCJR) {
+ /* memory from 128k to 640k is available */
+ mcb_devicedummy.SetPt((Bit16u)0x2000);
+ mcb_devicedummy.SetPSPSeg(MCB_FREE);
+ mcb_devicedummy.SetSize(0x9FFF - 0x2000);
+ mcb_devicedummy.SetType(0x5a);
+
+ /* exclude PCJr graphics region */
+ mcb_devicedummy.SetPt((Bit16u)0x17ff);
+ mcb_devicedummy.SetPSPSeg(MCB_DOS);
+ mcb_devicedummy.SetSize(0x800);
+ mcb_devicedummy.SetType(0x4d);
+
+ /* memory below 96k */
+ mcb.SetSize(0x1800 - DOS_MEM_START - (2+mcb_sizes));
+ mcb.SetType(0x4d);
+ } else {
+ /* complete memory up to 640k available */
+ /* last paragraph used to add UMB chain to low-memory MCB chain */
+ mcb.SetSize(0x9FFE - DOS_MEM_START - mcb_sizes);
+ }
+
+ dos.firstMCB=DOS_MEM_START;
+ dos_infoblock.SetFirstMCB(DOS_MEM_START);
+}
diff --git a/src/dos/dos_misc.cpp b/src/dos/dos_misc.cpp
new file mode 100644
index 000000000..c1a7d2e92
--- /dev/null
+++ b/src/dos/dos_misc.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "callback.h"
+#include "mem.h"
+#include "regs.h"
+#include "dos_inc.h"
+#include <list>
+
+
+static Bitu call_int2f,call_int2a;
+
+static std::list<MultiplexHandler*> Multiplex;
+typedef std::list<MultiplexHandler*>::iterator Multiplex_it;
+
+void DOS_AddMultiplexHandler(MultiplexHandler * handler) {
+ Multiplex.push_front(handler);
+}
+
+void DOS_DelMultiplexHandler(MultiplexHandler * handler) {
+ for(Multiplex_it it =Multiplex.begin();it != Multiplex.end();it++) {
+ if(*it == handler) {
+ Multiplex.erase(it);
+ return;
+ }
+ }
+}
+
+static Bitu INT2F_Handler(void) {
+ for(Multiplex_it it = Multiplex.begin();it != Multiplex.end();it++)
+ if( (*it)() ) return CBRET_NONE;
+
+ LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Multiplex Unhandled call %4X",reg_ax);
+ return CBRET_NONE;
+}
+
+
+static Bitu INT2A_Handler(void) {
+ return CBRET_NONE;
+}
+
+static bool DOS_MultiplexFunctions(void) {
+ switch (reg_ax) {
+ case 0x1216: /* GET ADDRESS OF SYSTEM FILE TABLE ENTRY */
+ // reg_bx is a system file table entry, should coincide with
+ // the file handle so just use that
+ LOG(LOG_DOSMISC,LOG_ERROR)("Some BAD filetable call used bx=%X",reg_bx);
+ if(reg_bx <= DOS_FILES) CALLBACK_SCF(false);
+ else CALLBACK_SCF(true);
+ if (reg_bx<16) {
+ RealPt sftrealpt=mem_readd(Real2Phys(dos_infoblock.GetPointer())+4);
+ PhysPt sftptr=Real2Phys(sftrealpt);
+ Bitu sftofs=0x06+reg_bx*0x3b;
+
+ if (Files[reg_bx]) mem_writeb(sftptr+sftofs,Files[reg_bx]->refCtr);
+ else mem_writeb(sftptr+sftofs,0);
+
+ if (!Files[reg_bx]) return true;
+
+ Bit32u handle=RealHandle(reg_bx);
+ if (handle>=DOS_FILES) {
+ mem_writew(sftptr+sftofs+0x02,0x02); // file open mode
+ mem_writeb(sftptr+sftofs+0x04,0x00); // file attribute
+ mem_writew(sftptr+sftofs+0x05,Files[reg_bx]->GetInformation()); // device info word
+ mem_writed(sftptr+sftofs+0x07,0); // device driver header
+ mem_writew(sftptr+sftofs+0x0d,0); // packed time
+ mem_writew(sftptr+sftofs+0x0f,0); // packed date
+ mem_writew(sftptr+sftofs+0x11,0); // size
+ mem_writew(sftptr+sftofs+0x15,0); // current position
+ } else {
+ Bit8u drive=Files[reg_bx]->GetDrive();
+
+ mem_writew(sftptr+sftofs+0x02,(Bit16u)(Files[reg_bx]->flags&3)); // file open mode
+ mem_writeb(sftptr+sftofs+0x04,(Bit8u)(Files[reg_bx]->attr)); // file attribute
+ mem_writew(sftptr+sftofs+0x05,0x40|drive); // device info word
+ mem_writed(sftptr+sftofs+0x07,RealMake(dos.tables.dpb,drive*9)); // dpb of the drive
+ mem_writew(sftptr+sftofs+0x0d,Files[reg_bx]->time); // packed file time
+ mem_writew(sftptr+sftofs+0x0f,Files[reg_bx]->date); // packed file date
+ Bit32u curpos=0;
+ Files[reg_bx]->Seek(&curpos,DOS_SEEK_CUR);
+ Bit32u endpos=0;
+ Files[reg_bx]->Seek(&endpos,DOS_SEEK_END);
+ mem_writed(sftptr+sftofs+0x11,endpos); // size
+ mem_writed(sftptr+sftofs+0x15,curpos); // current position
+ Files[reg_bx]->Seek(&curpos,DOS_SEEK_SET);
+ }
+
+ // fill in filename in fcb style
+ // (space-padded name (8 chars)+space-padded extension (3 chars))
+ const char* filename=(const char*)Files[reg_bx]->GetName();
+ if (strrchr(filename,'\\')) filename=strrchr(filename,'\\')+1;
+ if (strrchr(filename,'/')) filename=strrchr(filename,'/')+1;
+ if (!filename) return true;
+ const char* dotpos=strrchr(filename,'.');
+ if (dotpos) {
+ dotpos++;
+ size_t nlen=strlen(filename);
+ size_t extlen=strlen(dotpos);
+ Bits nmelen=(Bits)nlen-(Bits)extlen;
+ if (nmelen<1) return true;
+ nlen-=(extlen+1);
+
+ if (nlen>8) nlen=8;
+ size_t i;
+
+ for (i=0; i<nlen; i++)
+ mem_writeb((PhysPt)(sftptr+sftofs+0x20+i),filename[i]);
+ for (i=nlen; i<8; i++)
+ mem_writeb((PhysPt)(sftptr+sftofs+0x20+i),' ');
+
+ if (extlen>3) extlen=3;
+ for (i=0; i<extlen; i++)
+ mem_writeb((PhysPt)(sftptr+sftofs+0x28+i),dotpos[i]);
+ for (i=extlen; i<3; i++)
+ mem_writeb((PhysPt)(sftptr+sftofs+0x28+i),' ');
+ } else {
+ size_t i;
+ size_t nlen=strlen(filename);
+ if (nlen>8) nlen=8;
+ for (i=0; i<nlen; i++)
+ mem_writeb((PhysPt)(sftptr+sftofs+0x20+i),filename[i]);
+ for (i=nlen; i<11; i++)
+ mem_writeb((PhysPt)(sftptr+sftofs+0x20+i),' ');
+ }
+
+ SegSet16(es,RealSeg(sftrealpt));
+ reg_di=RealOff(sftrealpt+sftofs);
+ reg_ax=0xc000;
+
+ }
+ return true;
+ case 0x1607:
+ if (reg_bx == 0x15) {
+ switch (reg_cx) {
+ case 0x0000: // query instance
+ reg_cx = 0x0001;
+ reg_dx = 0x50; // dos driver segment
+ SegSet16(es,0x50); // patch table seg
+ reg_bx = 0x60; // patch table ofs
+ return true;
+ case 0x0001: // set patches
+ reg_ax = 0xb97c;
+ reg_bx = (reg_dx & 0x16);
+ reg_dx = 0xa2ab;
+ return true;
+ case 0x0003: // get size of data struc
+ if (reg_dx==0x0001) {
+ // CDS size requested
+ reg_ax = 0xb97c;
+ reg_dx = 0xa2ab;
+ reg_cx = 0x000e; // size
+ }
+ return true;
+ case 0x0004: // instanced data
+ reg_dx = 0; // none
+ return true;
+ case 0x0005: // get device driver size
+ reg_ax = 0;
+ reg_dx = 0;
+ return true;
+ default:
+ return false;
+ }
+ }
+ else if (reg_bx == 0x18) return true; // idle callout
+ else return false;
+ case 0x1680: /* RELEASE CURRENT VIRTUAL MACHINE TIME-SLICE */
+ //TODO Maybe do some idling but could screw up other systems :)
+ return true; //So no warning in the debugger anymore
+ case 0x1689: /* Kernel IDLE CALL */
+ case 0x168f: /* Close awareness crap */
+ /* Removing warning */
+ return true;
+ case 0x4a01: /* Query free hma space */
+ case 0x4a02: /* ALLOCATE HMA SPACE */
+ LOG(LOG_DOSMISC,LOG_WARN)("INT 2f:4a HMA. DOSBox reports none available.");
+ reg_bx=0; //number of bytes available in HMA or amount successfully allocated
+ //ESDI=ffff:ffff Location of HMA/Allocated memory
+ SegSet16(es,0xffff);
+ reg_di=0xffff;
+ return true;
+ }
+
+ return false;
+}
+
+void DOS_SetupMisc(void) {
+ /* Setup the dos multiplex interrupt */
+ call_int2f=CALLBACK_Allocate();
+ CALLBACK_Setup(call_int2f,&INT2F_Handler,CB_IRET,"DOS Int 2f");
+ RealSetVec(0x2f,CALLBACK_RealPointer(call_int2f));
+ DOS_AddMultiplexHandler(DOS_MultiplexFunctions);
+ /* Setup the dos network interrupt */
+ call_int2a=CALLBACK_Allocate();
+ CALLBACK_Setup(call_int2a,&INT2A_Handler,CB_IRET,"DOS Int 2a");
+ RealSetVec(0x2A,CALLBACK_RealPointer(call_int2a));
+}
diff --git a/src/dos/dos_mscdex.cpp b/src/dos/dos_mscdex.cpp
new file mode 100644
index 000000000..c211bcc6c
--- /dev/null
+++ b/src/dos/dos_mscdex.cpp
@@ -0,0 +1,1359 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <ctype.h>
+#include "regs.h"
+#include "callback.h"
+#include "dos_system.h"
+#include "dos_inc.h"
+#include "setup.h"
+#include "support.h"
+#include "bios_disk.h"
+#include "cpu.h"
+
+#include "cdrom.h"
+
+#define MSCDEX_LOG LOG(LOG_MISC,LOG_ERROR)
+//#define MSCDEX_LOG
+
+#define MSCDEX_VERSION_HIGH 2
+#define MSCDEX_VERSION_LOW 23
+#define MSCDEX_MAX_DRIVES 8
+
+// Error Codes
+#define MSCDEX_ERROR_INVALID_FUNCTION 1
+#define MSCDEX_ERROR_BAD_FORMAT 11
+#define MSCDEX_ERROR_UNKNOWN_DRIVE 15
+#define MSCDEX_ERROR_DRIVE_NOT_READY 21
+
+// Request Status
+#define REQUEST_STATUS_DONE 0x0100
+#define REQUEST_STATUS_ERROR 0x8000
+
+// Use cdrom Interface
+int useCdromInterface = CDROM_USE_SDL;
+int forceCD = -1;
+
+static Bitu MSCDEX_Strategy_Handler(void);
+static Bitu MSCDEX_Interrupt_Handler(void);
+
+class DOS_DeviceHeader:public MemStruct {
+public:
+ DOS_DeviceHeader(PhysPt ptr) { pt = ptr; };
+
+ void SetNextDeviceHeader (RealPt ptr) { sSave(sDeviceHeader,nextDeviceHeader,ptr); };
+ RealPt GetNextDeviceHeader (void) { return sGet(sDeviceHeader,nextDeviceHeader); };
+ void SetAttribute (Bit16u atr) { sSave(sDeviceHeader,devAttributes,atr); };
+ void SetDriveLetter (Bit8u letter) { sSave(sDeviceHeader,driveLetter,letter); };
+ void SetNumSubUnits (Bit8u num) { sSave(sDeviceHeader,numSubUnits,num); };
+ Bit8u GetNumSubUnits (void) { return sGet(sDeviceHeader,numSubUnits); };
+ void SetName (char const* _name) { MEM_BlockWrite(pt+offsetof(sDeviceHeader,name),_name,8); };
+ void SetInterrupt (Bit16u ofs) { sSave(sDeviceHeader,interrupt,ofs); };
+ void SetStrategy (Bit16u ofs) { sSave(sDeviceHeader,strategy,ofs); };
+
+public:
+ #ifdef _MSC_VER
+ #pragma pack(1)
+ #endif
+ struct sDeviceHeader{
+ RealPt nextDeviceHeader;
+ Bit16u devAttributes;
+ Bit16u strategy;
+ Bit16u interrupt;
+ Bit8u name[8];
+ Bit16u wReserved;
+ Bit8u driveLetter;
+ Bit8u numSubUnits;
+ } GCC_ATTRIBUTE(packed) TDeviceHeader;
+ #ifdef _MSC_VER
+ #pragma pack()
+ #endif
+};
+
+class CMscdex {
+public:
+ CMscdex (void);
+ ~CMscdex (void);
+
+ Bit16u GetVersion (void) { return (MSCDEX_VERSION_HIGH<<8)+MSCDEX_VERSION_LOW; };
+ Bit16u GetNumDrives (void) { return numDrives; };
+ Bit16u GetFirstDrive (void) { return dinfo[0].drive; };
+ Bit8u GetSubUnit (Bit16u _drive);
+ bool GetUPC (Bit8u subUnit, Bit8u& attr, char* upc);
+
+ void InitNewMedia (Bit8u subUnit);
+ bool PlayAudioSector (Bit8u subUnit, Bit32u start, Bit32u length);
+ bool PlayAudioMSF (Bit8u subUnit, Bit32u start, Bit32u length);
+ bool StopAudio (Bit8u subUnit);
+ bool GetAudioStatus (Bit8u subUnit, bool& playing, bool& pause, TMSF& start, TMSF& end);
+
+ bool GetSubChannelData (Bit8u subUnit, Bit8u& attr, Bit8u& track, Bit8u &index, TMSF& rel, TMSF& abs);
+
+ int RemoveDrive (Bit16u _drive);
+ int AddDrive (Bit16u _drive, char* physicalPath, Bit8u& subUnit);
+ bool HasDrive (Bit16u drive);
+ void ReplaceDrive (CDROM_Interface* newCdrom, Bit8u subUnit);
+ void GetDrives (PhysPt data);
+ void GetDriverInfo (PhysPt data);
+ bool GetVolumeName (Bit8u subUnit, char* name);
+ bool GetFileName (Bit16u drive, Bit16u pos, PhysPt data);
+ bool GetDirectoryEntry (Bit16u drive, bool copyFlag, PhysPt pathname, PhysPt buffer, Bit16u& error);
+ bool ReadVTOC (Bit16u drive, Bit16u volume, PhysPt data, Bit16u& offset, Bit16u& error);
+ bool ReadSectors (Bit16u drive, Bit32u sector, Bit16u num, PhysPt data);
+ bool ReadSectors (Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data);
+ bool ReadSectorsMSF (Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data);
+ bool SendDriverRequest (Bit16u drive, PhysPt data);
+ bool IsValidDrive (Bit16u drive);
+ bool GetCDInfo (Bit8u subUnit, Bit8u& tr1, Bit8u& tr2, TMSF& leadOut);
+ Bit32u GetVolumeSize (Bit8u subUnit);
+ bool GetTrackInfo (Bit8u subUnit, Bit8u track, Bit8u& attr, TMSF& start);
+ Bit16u GetStatusWord (Bit8u subUnit,Bit16u status);
+ bool GetCurrentPos (Bit8u subUnit, TMSF& pos);
+ Bit32u GetDeviceStatus (Bit8u subUnit);
+ bool GetMediaStatus (Bit8u subUnit, Bit8u& status);
+ bool LoadUnloadMedia (Bit8u subUnit, bool unload);
+ bool ResumeAudio (Bit8u subUnit);
+ bool GetMediaStatus (Bit8u subUnit, bool& media, bool& changed, bool& trayOpen);
+
+private:
+
+ PhysPt GetDefaultBuffer (void);
+ PhysPt GetTempBuffer (void);
+
+ Bit16u numDrives;
+
+ typedef struct SDriveInfo {
+ Bit8u drive; // drive letter in dosbox
+ Bit8u physDrive; // drive letter in system
+ bool audioPlay; // audio playing active
+ bool audioPaused; // audio playing paused
+ Bit32u audioStart; // StartLoc for resume
+ Bit32u audioEnd; // EndLoc for resume
+ bool locked; // drive locked ?
+ bool lastResult; // last operation success ?
+ Bit32u volumeSize; // for media change
+ TCtrl audioCtrl; // audio channel control
+ } TDriveInfo;
+
+ Bit16u defaultBufSeg;
+ TDriveInfo dinfo[MSCDEX_MAX_DRIVES];
+ CDROM_Interface* cdrom[MSCDEX_MAX_DRIVES];
+
+public:
+ Bit16u rootDriverHeaderSeg;
+
+ bool ChannelControl (Bit8u subUnit, TCtrl ctrl);
+ bool GetChannelControl (Bit8u subUnit, TCtrl& ctrl);
+};
+
+CMscdex::CMscdex(void) {
+ numDrives = 0;
+ rootDriverHeaderSeg = 0;
+ defaultBufSeg = 0;
+
+ memset(dinfo,0,sizeof(dinfo));
+ for (Bit32u i=0; i<MSCDEX_MAX_DRIVES; i++) cdrom[i] = 0;
+}
+
+CMscdex::~CMscdex(void) {
+ defaultBufSeg = 0;
+ for (Bit16u i=0; i<GetNumDrives(); i++) {
+ delete (cdrom)[i];
+ cdrom[i] = 0;
+ };
+}
+
+void CMscdex::GetDrives(PhysPt data)
+{
+ for (Bit16u i=0; i<GetNumDrives(); i++) mem_writeb(data+i,dinfo[i].drive);
+}
+
+bool CMscdex::IsValidDrive(Bit16u _drive)
+{
+ _drive &= 0xff; //Only lowerpart (Ultimate domain)
+ for (Bit16u i=0; i<GetNumDrives(); i++) if (dinfo[i].drive==_drive) return true;
+ return false;
+}
+
+Bit8u CMscdex::GetSubUnit(Bit16u _drive)
+{
+ _drive &= 0xff; //Only lowerpart (Ultimate domain)
+ for (Bit16u i=0; i<GetNumDrives(); i++) if (dinfo[i].drive==_drive) return (Bit8u)i;
+ return 0xff;
+}
+
+int CMscdex::RemoveDrive(Bit16u _drive)
+{
+ Bit16u idx = MSCDEX_MAX_DRIVES;
+ for (Bit16u i=0; i<GetNumDrives(); i++) {
+ if (dinfo[i].drive == _drive) {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx == MSCDEX_MAX_DRIVES || (idx!=0 && idx!=GetNumDrives()-1)) return 0;
+ delete (cdrom)[idx];
+ if (idx==0) {
+ for (Bit16u i=0; i<GetNumDrives(); i++) {
+ if (i == MSCDEX_MAX_DRIVES-1) {
+ cdrom[i] = 0;
+ memset(&dinfo[i],0,sizeof(TDriveInfo));
+ } else {
+ dinfo[i] = dinfo[i+1];
+ cdrom[i] = cdrom[i+1];
+ }
+ }
+ } else {
+ cdrom[idx] = 0;
+ memset(&dinfo[idx],0,sizeof(TDriveInfo));
+ }
+ numDrives--;
+
+ if (GetNumDrives() == 0) {
+ DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0));
+ Bit16u off = sizeof(DOS_DeviceHeader::sDeviceHeader);
+ devHeader.SetStrategy(off+4); // point to the RETF (To deactivate MSCDEX)
+ devHeader.SetInterrupt(off+4); // point to the RETF (To deactivate MSCDEX)
+ devHeader.SetDriveLetter(0);
+ } else if (idx==0) {
+ DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0));
+ devHeader.SetDriveLetter(GetFirstDrive()+1);
+ }
+ return 1;
+}
+
+int CMscdex::AddDrive(Bit16u _drive, char* physicalPath, Bit8u& subUnit)
+{
+ subUnit = 0;
+ if ((Bitu)GetNumDrives()+1>=MSCDEX_MAX_DRIVES) return 4;
+ if (GetNumDrives()) {
+ // Error check, driveletter have to be in a row
+ if (dinfo[0].drive-1!=_drive && dinfo[numDrives-1].drive+1!=_drive)
+ return 1;
+ }
+ // Set return type to ok
+ int result = 0;
+ // Get Mounttype and init needed cdrom interface
+ switch (CDROM_GetMountType(physicalPath,forceCD)) {
+ case 0x00: {
+ LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting physical cdrom: %s" ,physicalPath);
+#if defined (WIN32)
+ // Check OS
+ OSVERSIONINFO osi;
+ osi.dwOSVersionInfoSize = sizeof(osi);
+ GetVersionEx(&osi);
+ if ((osi.dwPlatformId==VER_PLATFORM_WIN32_NT) && (osi.dwMajorVersion>4)) {
+ // only WIN NT/200/XP
+ if (useCdromInterface==CDROM_USE_IOCTL_DIO) {
+ cdrom[numDrives] = new CDROM_Interface_Ioctl(CDROM_Interface_Ioctl::CDIOCTL_CDA_DIO);
+ LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface.");
+ break;
+ }
+ if (useCdromInterface==CDROM_USE_IOCTL_DX) {
+ cdrom[numDrives] = new CDROM_Interface_Ioctl(CDROM_Interface_Ioctl::CDIOCTL_CDA_DX);
+ LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface (digital audio extraction).");
+ break;
+ }
+ if (useCdromInterface==CDROM_USE_IOCTL_MCI) {
+ cdrom[numDrives] = new CDROM_Interface_Ioctl(CDROM_Interface_Ioctl::CDIOCTL_CDA_MCI);
+ LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface (media control interface).");
+ break;
+ }
+ }
+ if (useCdromInterface==CDROM_USE_ASPI) {
+ // all Wins - ASPI
+ cdrom[numDrives] = new CDROM_Interface_Aspi();
+ LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: ASPI Interface.");
+ break;
+ }
+#endif
+#if defined (LINUX) || defined(OS2)
+ // Always use IOCTL in Linux or OS/2
+ cdrom[numDrives] = new CDROM_Interface_Ioctl();
+ LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface.");
+#else
+ // Default case windows and other oses
+ cdrom[numDrives] = new CDROM_Interface_SDL();
+ LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: SDL Interface.");
+#endif
+ } break;
+ case 0x01: // iso cdrom interface
+ LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting iso file as cdrom: %s", physicalPath);
+ cdrom[numDrives] = new CDROM_Interface_Image((Bit8u)numDrives);
+ break;
+ case 0x02: // fake cdrom interface (directories)
+ cdrom[numDrives] = new CDROM_Interface_Fake;
+ LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting directory as cdrom: %s",physicalPath);
+ LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: You wont have full MSCDEX support !");
+ result = 5;
+ break;
+ default : // weird result
+ return 6;
+ };
+
+ if (!cdrom[numDrives]->SetDevice(physicalPath,forceCD)) {
+// delete cdrom[numDrives] ; mount seems to delete it
+ return 3;
+ }
+
+
+ if (rootDriverHeaderSeg==0) {
+
+ Bit16u driverSize = sizeof(DOS_DeviceHeader::sDeviceHeader) + 10; // 10 = Bytes for 3 callbacks
+
+ // Create Device Header
+ Bit16u seg = DOS_GetMemory(driverSize/16+((driverSize%16)>0));
+ DOS_DeviceHeader devHeader(PhysMake(seg,0));
+ devHeader.SetNextDeviceHeader (0xFFFFFFFF);
+ devHeader.SetAttribute(0xc800);
+ devHeader.SetDriveLetter (_drive+1);
+ devHeader.SetNumSubUnits (1);
+ devHeader.SetName ("MSCD001 ");
+
+ //Link it in the device chain
+ Bit32u start = dos_infoblock.GetDeviceChain();
+ Bit16u segm = (Bit16u)(start>>16);
+ Bit16u offm = (Bit16u)(start&0xFFFF);
+ while(start != 0xFFFFFFFF) {
+ segm = (Bit16u)(start>>16);
+ offm = (Bit16u)(start&0xFFFF);
+ start = real_readd(segm,offm);
+ }
+ real_writed(segm,offm,seg<<16);
+
+ // Create Callback Strategy
+ Bit16u off = sizeof(DOS_DeviceHeader::sDeviceHeader);
+ Bit16u call_strategy=(Bit16u)CALLBACK_Allocate();
+ CallBack_Handlers[call_strategy]=MSCDEX_Strategy_Handler;
+ real_writeb(seg,off+0,(Bit8u)0xFE); //GRP 4
+ real_writeb(seg,off+1,(Bit8u)0x38); //Extra Callback instruction
+ real_writew(seg,off+2,call_strategy); //The immediate word
+ real_writeb(seg,off+4,(Bit8u)0xCB); //A RETF Instruction
+ devHeader.SetStrategy(off);
+
+ // Create Callback Interrupt
+ off += 5;
+ Bit16u call_interrupt=(Bit16u)CALLBACK_Allocate();
+ CallBack_Handlers[call_interrupt]=MSCDEX_Interrupt_Handler;
+ real_writeb(seg,off+0,(Bit8u)0xFE); //GRP 4
+ real_writeb(seg,off+1,(Bit8u)0x38); //Extra Callback instruction
+ real_writew(seg,off+2,call_interrupt); //The immediate word
+ real_writeb(seg,off+4,(Bit8u)0xCB); //A RETF Instruction
+ devHeader.SetInterrupt(off);
+
+ rootDriverHeaderSeg = seg;
+
+ } else if (GetNumDrives() == 0) {
+ DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0));
+ Bit16u off = sizeof(DOS_DeviceHeader::sDeviceHeader);
+ devHeader.SetDriveLetter(_drive+1);
+ devHeader.SetStrategy(off);
+ devHeader.SetInterrupt(off+5);
+ }
+
+ // Set drive
+ DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0));
+ devHeader.SetNumSubUnits(devHeader.GetNumSubUnits()+1);
+
+ if (dinfo[0].drive-1==_drive) {
+ CDROM_Interface *_cdrom = cdrom[numDrives];
+ CDROM_Interface_Image *_cdimg = CDROM_Interface_Image::images[numDrives];
+ for (Bit16u i=GetNumDrives(); i>0; i--) {
+ dinfo[i] = dinfo[i-1];
+ cdrom[i] = cdrom[i-1];
+ CDROM_Interface_Image::images[i] = CDROM_Interface_Image::images[i-1];
+ }
+ cdrom[0] = _cdrom;
+ CDROM_Interface_Image::images[0] = _cdimg;
+ dinfo[0].drive = (Bit8u)_drive;
+ dinfo[0].physDrive = (Bit8u)toupper(physicalPath[0]);
+ subUnit = 0;
+ } else {
+ dinfo[numDrives].drive = (Bit8u)_drive;
+ dinfo[numDrives].physDrive = (Bit8u)toupper(physicalPath[0]);
+ subUnit = (Bit8u)numDrives;
+ }
+ numDrives++;
+ // init channel control
+ for (Bit8u chan=0;chan<4;chan++) {
+ dinfo[subUnit].audioCtrl.out[chan]=chan;
+ dinfo[subUnit].audioCtrl.vol[chan]=0xff;
+ }
+ // stop audio
+ StopAudio(subUnit);
+ return result;
+}
+
+bool CMscdex::HasDrive(Bit16u drive) {
+ return (GetSubUnit(drive) != 0xff);
+}
+
+void CMscdex::ReplaceDrive(CDROM_Interface* newCdrom, Bit8u subUnit) {
+ if (cdrom[subUnit] != NULL) {
+ StopAudio(subUnit);
+ delete cdrom[subUnit];
+ }
+ cdrom[subUnit] = newCdrom;
+}
+
+PhysPt CMscdex::GetDefaultBuffer(void) {
+ if (defaultBufSeg==0) {
+ Bit16u size = (2352*2+15)/16;
+ defaultBufSeg = DOS_GetMemory(size);
+ };
+ return PhysMake(defaultBufSeg,2352);
+}
+
+PhysPt CMscdex::GetTempBuffer(void) {
+ if (defaultBufSeg==0) {
+ Bit16u size = (2352*2+15)/16;
+ defaultBufSeg = DOS_GetMemory(size);
+ };
+ return PhysMake(defaultBufSeg,0);
+}
+
+void CMscdex::GetDriverInfo (PhysPt data) {
+ for (Bit16u i=0; i<GetNumDrives(); i++) {
+ mem_writeb(data ,(Bit8u)i); // subunit
+ mem_writed(data+1,RealMake(rootDriverHeaderSeg,0));
+ data+=5;
+ };
+}
+
+bool CMscdex::GetCDInfo(Bit8u subUnit, Bit8u& tr1, Bit8u& tr2, TMSF& leadOut) {
+ if (subUnit>=numDrives) return false;
+ int tr1i,tr2i;
+ // Assume Media change
+ cdrom[subUnit]->InitNewMedia();
+ dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioTracks(tr1i,tr2i,leadOut);
+ if (!dinfo[subUnit].lastResult) {
+ tr1 = tr2 = 0;
+ memset(&leadOut,0,sizeof(leadOut));
+ } else {
+ tr1 = (Bit8u) tr1i;
+ tr2 = (Bit8u) tr2i;
+ }
+ return dinfo[subUnit].lastResult;
+}
+
+bool CMscdex::GetTrackInfo(Bit8u subUnit, Bit8u track, Bit8u& attr, TMSF& start) {
+ if (subUnit>=numDrives) return false;
+ dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioTrackInfo(track,start,attr);
+ if (!dinfo[subUnit].lastResult) {
+ attr = 0;
+ memset(&start,0,sizeof(start));
+ }
+ return dinfo[subUnit].lastResult;
+}
+
+bool CMscdex::PlayAudioSector(Bit8u subUnit, Bit32u sector, Bit32u length) {
+ if (subUnit>=numDrives) return false;
+ // If value from last stop is used, this is meant as a resume
+ // better start using resume command
+ if (dinfo[subUnit].audioPaused && (sector==dinfo[subUnit].audioStart) && (dinfo[subUnit].audioEnd!=0)) {
+ dinfo[subUnit].lastResult = cdrom[subUnit]->PauseAudio(true);
+ } else
+ dinfo[subUnit].lastResult = cdrom[subUnit]->PlayAudioSector(sector,length);
+
+ if (dinfo[subUnit].lastResult) {
+ dinfo[subUnit].audioPlay = true;
+ dinfo[subUnit].audioPaused = false;
+ dinfo[subUnit].audioStart = sector;
+ dinfo[subUnit].audioEnd = length;
+ }
+ return dinfo[subUnit].lastResult;
+}
+
+bool CMscdex::PlayAudioMSF(Bit8u subUnit, Bit32u start, Bit32u length) {
+ if (subUnit>=numDrives) return false;
+ Bit8u min = (Bit8u)(start>>16) & 0xFF;
+ Bit8u sec = (Bit8u)(start>> 8) & 0xFF;
+ Bit8u fr = (Bit8u)(start>> 0) & 0xFF;
+ Bit32u sector = min*60*75+sec*75+fr - 150;
+ return dinfo[subUnit].lastResult = PlayAudioSector(subUnit,sector,length);
+}
+
+bool CMscdex::GetSubChannelData(Bit8u subUnit, Bit8u& attr, Bit8u& track, Bit8u &index, TMSF& rel, TMSF& abs) {
+ if (subUnit>=numDrives) return false;
+ dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioSub(attr,track,index,rel,abs);
+ if (!dinfo[subUnit].lastResult) {
+ attr = track = index = 0;
+ memset(&rel,0,sizeof(rel));
+ memset(&abs,0,sizeof(abs));
+ }
+ return dinfo[subUnit].lastResult;
+}
+
+bool CMscdex::GetAudioStatus(Bit8u subUnit, bool& playing, bool& pause, TMSF& start, TMSF& end) {
+ if (subUnit>=numDrives) return false;
+ dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioStatus(playing,pause);
+ if (dinfo[subUnit].lastResult) {
+ if (playing) {
+ // Start
+ Bit32u addr = dinfo[subUnit].audioStart + 150;
+ start.fr = (Bit8u)(addr%75); addr/=75;
+ start.sec = (Bit8u)(addr%60);
+ start.min = (Bit8u)(addr/60);
+ // End
+ addr = dinfo[subUnit].audioEnd + 150;
+ end.fr = (Bit8u)(addr%75); addr/=75;
+ end.sec = (Bit8u)(addr%60);
+ end.min = (Bit8u)(addr/60);
+ } else {
+ memset(&start,0,sizeof(start));
+ memset(&end,0,sizeof(end));
+ }
+ } else {
+ playing = false;
+ pause = false;
+ memset(&start,0,sizeof(start));
+ memset(&end,0,sizeof(end));
+ }
+
+ return dinfo[subUnit].lastResult;
+}
+
+bool CMscdex::StopAudio(Bit8u subUnit) {
+ if (subUnit>=numDrives) return false;
+ if (dinfo[subUnit].audioPlay) {
+ // Check if audio is still playing....
+ TMSF start,end;
+ bool playing,pause;
+ if (GetAudioStatus(subUnit,playing,pause,start,end))
+ dinfo[subUnit].audioPlay = playing;
+ else
+ dinfo[subUnit].audioPlay = false;
+ }
+ if (dinfo[subUnit].audioPlay)
+ dinfo[subUnit].lastResult = cdrom[subUnit]->PauseAudio(false);
+ else
+ dinfo[subUnit].lastResult = cdrom[subUnit]->StopAudio();
+
+ if (dinfo[subUnit].lastResult) {
+ if (dinfo[subUnit].audioPlay) {
+ TMSF pos;
+ GetCurrentPos(subUnit,pos);
+ dinfo[subUnit].audioStart = pos.min*60*75+pos.sec*75+pos.fr - 150;
+ dinfo[subUnit].audioPaused = true;
+ } else {
+ dinfo[subUnit].audioPaused = false;
+ dinfo[subUnit].audioStart = 0;
+ dinfo[subUnit].audioEnd = 0;
+ }
+ dinfo[subUnit].audioPlay = false;
+ }
+ return dinfo[subUnit].lastResult;
+}
+
+bool CMscdex::ResumeAudio(Bit8u subUnit) {
+ if (subUnit>=numDrives) return false;
+ return dinfo[subUnit].lastResult = PlayAudioSector(subUnit,dinfo[subUnit].audioStart,dinfo[subUnit].audioEnd);
+}
+
+Bit32u CMscdex::GetVolumeSize(Bit8u subUnit) {
+ if (subUnit>=numDrives) return false;
+ Bit8u tr1,tr2;
+ TMSF leadOut;
+ dinfo[subUnit].lastResult = GetCDInfo(subUnit,tr1,tr2,leadOut);
+ if (dinfo[subUnit].lastResult) return (leadOut.min*60*75)+(leadOut.sec*75)+leadOut.fr;
+ return 0;
+}
+
+bool CMscdex::ReadVTOC(Bit16u drive, Bit16u volume, PhysPt data, Bit16u& offset, Bit16u& error) {
+ Bit8u subunit = GetSubUnit(drive);
+/* if (subunit>=numDrives) {
+ error=MSCDEX_ERROR_UNKNOWN_DRIVE;
+ return false;
+ } */
+ if (!ReadSectors(subunit,false,16+volume,1,data)) {
+ error=MSCDEX_ERROR_DRIVE_NOT_READY;
+ return false;
+ }
+ char id[5];
+ MEM_BlockRead(data + 1, id, 5);
+ if (strncmp("CD001", id, 5)==0) offset = 0;
+ else {
+ MEM_BlockRead(data + 9, id, 5);
+ if (strncmp("CDROM", id, 5)==0) offset = 8;
+ else {
+ error = MSCDEX_ERROR_BAD_FORMAT;
+ return false;
+ }
+ }
+ Bit8u type = mem_readb(data + offset);
+ error = (type == 1) ? 1 : (type == 0xFF) ? 0xFF : 0;
+ return true;
+}
+
+bool CMscdex::GetVolumeName(Bit8u subUnit, char* data) {
+ if (subUnit>=numDrives) return false;
+ Bit16u drive = dinfo[subUnit].drive;
+
+ Bit16u offset = 0, error;
+ bool success = false;
+ PhysPt ptoc = GetTempBuffer();
+ success = ReadVTOC(drive,0x00,ptoc,offset,error);
+ if (success) {
+ MEM_StrCopy(ptoc+offset+40,data,31);
+ data[31] = 0;
+ rtrim(data);
+ };
+
+ return success;
+}
+
+bool CMscdex::GetFileName(Bit16u drive, Bit16u pos, PhysPt data) {
+ Bit16u offset = 0, error;
+ bool success = false;
+ PhysPt ptoc = GetTempBuffer();
+ success = ReadVTOC(drive,0x00,ptoc,offset,error);
+ if (success) {
+ Bitu len;
+ for (len=0;len<37;len++) {
+ Bit8u c=mem_readb(ptoc+offset+pos+len);
+ if (c==0 || c==0x20) break;
+ }
+ MEM_BlockCopy(data,ptoc+offset+pos,len);
+ mem_writeb(data+len,0);
+ };
+ return success;
+}
+
+bool CMscdex::GetUPC(Bit8u subUnit, Bit8u& attr, char* upc)
+{
+ if (subUnit>=numDrives) return false;
+ return dinfo[subUnit].lastResult = cdrom[subUnit]->GetUPC(attr,&upc[0]);
+}
+
+bool CMscdex::ReadSectors(Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data) {
+ if (subUnit>=numDrives) return false;
+ if ((4*num*2048+5) < CPU_Cycles) CPU_Cycles -= 4*num*2048;
+ else CPU_Cycles = 5;
+ dinfo[subUnit].lastResult = cdrom[subUnit]->ReadSectors(data,raw,sector,num);
+ return dinfo[subUnit].lastResult;
+}
+
+bool CMscdex::ReadSectorsMSF(Bit8u subUnit, bool raw, Bit32u start, Bit16u num, PhysPt data) {
+ if (subUnit>=numDrives) return false;
+ Bit8u min = (Bit8u)(start>>16) & 0xFF;
+ Bit8u sec = (Bit8u)(start>> 8) & 0xFF;
+ Bit8u fr = (Bit8u)(start>> 0) & 0xFF;
+ Bit32u sector = min*60*75+sec*75+fr - 150;
+ return ReadSectors(subUnit,raw,sector,num,data);
+}
+
+// Called from INT 2F
+bool CMscdex::ReadSectors(Bit16u drive, Bit32u sector, Bit16u num, PhysPt data) {
+ return ReadSectors(GetSubUnit(drive),false,sector,num,data);
+}
+
+bool CMscdex::GetDirectoryEntry(Bit16u drive, bool copyFlag, PhysPt pathname, PhysPt buffer, Bit16u& error) {
+ char volumeID[6] = {0};
+ char searchName[256];
+ char entryName[256];
+ bool foundComplete = false;
+ bool foundName;
+ bool nextPart = true;
+ char* useName = 0;
+ Bitu entryLength,nameLength;
+ // clear error
+ error = 0;
+ MEM_StrCopy(pathname+1,searchName,mem_readb(pathname));
+ upcase(searchName);
+ char* searchPos = searchName;
+
+ //strip of tailing . (XCOM APOCALYPSE)
+ size_t searchlen = strlen(searchName);
+ if (searchlen > 1 && strcmp(searchName,".."))
+ if (searchName[searchlen-1] =='.') searchName[searchlen-1] = 0;
+
+ //LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Get DirEntry : Find : %s",searchName);
+ // read vtoc
+ PhysPt defBuffer = GetDefaultBuffer();
+ if (!ReadSectors(GetSubUnit(drive),false,16,1,defBuffer)) return false;
+ MEM_StrCopy(defBuffer+1,volumeID,5); volumeID[5] = 0;
+ bool iso = (strcmp("CD001",volumeID)==0);
+ if (!iso) {
+ MEM_StrCopy(defBuffer+9,volumeID,5);
+ if (strcmp("CDROM",volumeID)!=0) E_Exit("MSCDEX: GetDirEntry: Not an ISO 9660 or HSF CD.");
+ }
+ Bit16u offset = iso ? 156:180;
+ // get directory position
+ Bitu dirEntrySector = mem_readd(defBuffer+offset+2);
+ Bits dirSize = mem_readd(defBuffer+offset+10);
+ Bitu index;
+ while (dirSize>0) {
+ index = 0;
+ if (!ReadSectors(GetSubUnit(drive),false,dirEntrySector,1,defBuffer)) return false;
+ // Get string part
+ foundName = false;
+ if (nextPart) {
+ if (searchPos) {
+ useName = searchPos;
+ searchPos = strchr(searchPos,'\\');
+ }
+ if (searchPos) { *searchPos = 0; searchPos++; }
+ else foundComplete = true;
+ }
+
+ do {
+ entryLength = mem_readb(defBuffer+index);
+ if (entryLength==0) break;
+ if (mem_readb(defBuffer + index + (iso?0x19:0x18) ) & 4) {
+ // skip associated files
+ index += entryLength;
+ continue;
+ }
+ nameLength = mem_readb(defBuffer+index+32);
+ MEM_StrCopy(defBuffer+index+33,entryName,nameLength);
+ // strip separator and file version number
+ char* separator = strchr(entryName,';');
+ if (separator) *separator = 0;
+ // strip trailing period
+ size_t entrylen = strlen(entryName);
+ if (entrylen>0 && entryName[entrylen-1]=='.') entryName[entrylen-1] = 0;
+
+ if (strcmp(entryName,useName)==0) {
+ //LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Get DirEntry : Found : %s",useName);
+ foundName = true;
+ break;
+ }
+ index += entryLength;
+ } while (index+33<=2048);
+
+ if (foundName) {
+ if (foundComplete) {
+ if (copyFlag) {
+ LOG(LOG_MISC,LOG_WARN)("MSCDEX: GetDirEntry: Copyflag structure not entirely accurate maybe");
+ Bit8u readBuf[256];
+ Bit8u writeBuf[256];
+ if (entryLength > 256)
+ return false;
+ MEM_BlockRead( defBuffer+index, readBuf, entryLength );
+ writeBuf[0] = readBuf[1]; // 00h BYTE length of XAR in Logical Block Numbers
+ memcpy( &writeBuf[1], &readBuf[0x2], 4); // 01h DWORD Logical Block Number of file start
+ writeBuf[5] = 0;writeBuf[6] = 8; // 05h WORD size of disk in logical blocks
+ memcpy( &writeBuf[7], &readBuf[0xa], 4); // 07h DWORD file length in bytes
+ memcpy( &writeBuf[0xb], &readBuf[0x12], 6); // 0bh BYTEs date and time
+ writeBuf[0x11] = iso ? readBuf[0x18]:0; // 11h BYTE time zone
+ writeBuf[0x12] = readBuf[iso ? 0x19:0x18]; // 12h BYTE bit flags
+ writeBuf[0x13] = readBuf[0x1a]; // 13h BYTE interleave size
+ writeBuf[0x14] = readBuf[0x1b]; // 14h BYTE interleave skip factor
+ memcpy( &writeBuf[0x15], &readBuf[0x1c], 2); // 15h WORD volume set sequence number
+ writeBuf[0x17] = readBuf[0x20];
+ memcpy( &writeBuf[0x18], &readBuf[21], readBuf[0x20] <= 38 ? readBuf[0x20] : 38 );
+ MEM_BlockWrite( buffer, writeBuf, 0x18 + 40 );
+ } else {
+ // Direct copy
+ MEM_BlockCopy(buffer,defBuffer+index,entryLength);
+ }
+ error = iso ? 1:0;
+ return true;
+ }
+ // change directory
+ dirEntrySector = mem_readd(defBuffer+index+2);
+ dirSize = mem_readd(defBuffer+index+10);
+ nextPart = true;
+ } else {
+ // continue search in next sector
+ dirSize -= 2048;
+ dirEntrySector++;
+ nextPart = false;
+ }
+ }
+ error = 2; // file not found
+ return false; // not found
+}
+
+bool CMscdex::GetCurrentPos(Bit8u subUnit, TMSF& pos) {
+ if (subUnit>=numDrives) return false;
+ TMSF rel;
+ Bit8u attr,track,index;
+ dinfo[subUnit].lastResult = GetSubChannelData(subUnit, attr, track, index, rel, pos);
+ if (!dinfo[subUnit].lastResult) memset(&pos,0,sizeof(pos));
+ return dinfo[subUnit].lastResult;
+}
+
+bool CMscdex::GetMediaStatus(Bit8u subUnit, bool& media, bool& changed, bool& trayOpen) {
+ if (subUnit>=numDrives) return false;
+ dinfo[subUnit].lastResult = cdrom[subUnit]->GetMediaTrayStatus(media,changed,trayOpen);
+ return dinfo[subUnit].lastResult;
+}
+
+Bit32u CMscdex::GetDeviceStatus(Bit8u subUnit) {
+ if (subUnit>=numDrives) return false;
+ bool media,changed,trayOpen;
+
+ dinfo[subUnit].lastResult = GetMediaStatus(subUnit,media,changed,trayOpen);
+ if (dinfo[subUnit].audioPlay) {
+ // Check if audio is still playing....
+ TMSF start,end;
+ bool playing,pause;
+ if (GetAudioStatus(subUnit,playing,pause,start,end))
+ dinfo[subUnit].audioPlay = playing;
+ else
+ dinfo[subUnit].audioPlay = false;
+ }
+
+ Bit32u status = ((trayOpen?1:0) << 0) | // Drive is open ?
+ ((dinfo[subUnit].locked?1:0) << 1) | // Drive is locked ?
+ (1<<2) | // raw + cooked sectors
+ (1<<4) | // Can read sudio
+ (1<<8) | // Can control audio
+ (1<<9) | // Red book & HSG
+ ((dinfo[subUnit].audioPlay?1:0) << 10) | // Audio is playing ?
+ ((media?0:1) << 11); // Drive is empty ?
+ return status;
+}
+
+bool CMscdex::GetMediaStatus(Bit8u subUnit, Bit8u& status) {
+ if (subUnit>=numDrives) return false;
+/* bool media,changed,open,result;
+ result = GetMediaStatus(subUnit,media,changed,open);
+ status = changed ? 0xFF : 0x01;
+ return result; */
+ status = getSwapRequest() ? 0xFF : 0x01;
+ return true;
+}
+
+bool CMscdex::LoadUnloadMedia(Bit8u subUnit, bool unload) {
+ if (subUnit>=numDrives) return false;
+ dinfo[subUnit].lastResult = cdrom[subUnit]->LoadUnloadMedia(unload);
+ return dinfo[subUnit].lastResult;
+}
+
+bool CMscdex::SendDriverRequest(Bit16u drive, PhysPt data) {
+ Bit8u subUnit = GetSubUnit(drive);
+ if (subUnit>=numDrives) return false;
+ // Get SubUnit
+ mem_writeb(data+1,subUnit);
+ // Call Strategy / Interrupt
+ MSCDEX_Strategy_Handler();
+ MSCDEX_Interrupt_Handler();
+ return true;
+}
+
+Bit16u CMscdex::GetStatusWord(Bit8u subUnit,Bit16u status) {
+ if (subUnit>=numDrives) return REQUEST_STATUS_ERROR | 0x02; // error : Drive not ready
+
+ if (dinfo[subUnit].lastResult) status |= REQUEST_STATUS_DONE; // ok
+ else status |= REQUEST_STATUS_ERROR;
+
+ if (dinfo[subUnit].audioPlay) {
+ // Check if audio is still playing....
+ TMSF start,end;
+ bool playing,pause;
+ if (GetAudioStatus(subUnit,playing,pause,start,end)) {
+ dinfo[subUnit].audioPlay = playing;
+ } else
+ dinfo[subUnit].audioPlay = false;
+
+ status |= (dinfo[subUnit].audioPlay<<9);
+ }
+ dinfo[subUnit].lastResult = true;
+ return status;
+}
+
+void CMscdex::InitNewMedia(Bit8u subUnit) {
+ if (subUnit<numDrives) {
+ // Reopen new media
+ cdrom[subUnit]->InitNewMedia();
+ }
+}
+
+bool CMscdex::ChannelControl(Bit8u subUnit, TCtrl ctrl) {
+ if (subUnit>=numDrives) return false;
+ // adjust strange channel mapping
+ if (ctrl.out[0]>1) ctrl.out[0]=0;
+ if (ctrl.out[1]>1) ctrl.out[1]=1;
+ dinfo[subUnit].audioCtrl=ctrl;
+ cdrom[subUnit]->ChannelControl(ctrl);
+ return true;
+}
+
+bool CMscdex::GetChannelControl(Bit8u subUnit, TCtrl& ctrl) {
+ if (subUnit>=numDrives) return false;
+ ctrl=dinfo[subUnit].audioCtrl;
+ return true;
+}
+
+static CMscdex* mscdex = 0;
+static PhysPt curReqheaderPtr = 0;
+
+static Bit16u MSCDEX_IOCTL_Input(PhysPt buffer,Bit8u drive_unit) {
+ Bit8u ioctl_fct = mem_readb(buffer);
+ MSCDEX_LOG("MSCDEX: IOCTL INPUT Subfunction %02X",ioctl_fct);
+ switch (ioctl_fct) {
+ case 0x00 : /* Get Device Header address */
+ mem_writed(buffer+1,RealMake(mscdex->rootDriverHeaderSeg,0));
+ break;
+ case 0x01 :{/* Get current position */
+ TMSF pos;
+ mscdex->GetCurrentPos(drive_unit,pos);
+ Bit8u addr_mode = mem_readb(buffer+1);
+ if (addr_mode==0) { // HSG
+ Bit32u frames=MSF_TO_FRAMES(pos.min, pos.sec, pos.fr);
+ if (frames<150) MSCDEX_LOG("MSCDEX: Get position: invalid position %d:%d:%d", pos.min, pos.sec, pos.fr);
+ else frames-=150;
+ mem_writed(buffer+2,frames);
+ } else if (addr_mode==1) { // Red book
+ mem_writeb(buffer+2,pos.fr);
+ mem_writeb(buffer+3,pos.sec);
+ mem_writeb(buffer+4,pos.min);
+ mem_writeb(buffer+5,0x00);
+ } else {
+ MSCDEX_LOG("MSCDEX: Get position: invalid address mode %x",addr_mode);
+ return 0x03; // invalid function
+ }
+ }break;
+ case 0x04 : /* Audio Channel control */
+ TCtrl ctrl;
+ if (!mscdex->GetChannelControl(drive_unit,ctrl)) return 0x01;
+ for (Bit8u chan=0;chan<4;chan++) {
+ mem_writeb(buffer+chan*2+1,ctrl.out[chan]);
+ mem_writeb(buffer+chan*2+2,ctrl.vol[chan]);
+ }
+ break;
+ case 0x06 : /* Get Device status */
+ mem_writed(buffer+1,mscdex->GetDeviceStatus(drive_unit));
+ break;
+ case 0x07 : /* Get sector size */
+ if (mem_readb(buffer+1)==0) mem_writed(buffer+2,2048);
+ else if (mem_readb(buffer+1)==1) mem_writed(buffer+2,2352);
+ else return 0x03; // invalid function
+ break;
+ case 0x08 : /* Get size of current volume */
+ mem_writed(buffer+1,mscdex->GetVolumeSize(drive_unit));
+ break;
+ case 0x09 : /* Media change ? */
+ Bit8u status;
+ if (!mscdex->GetMediaStatus(drive_unit,status)) {
+ status = 0; // state unknown
+ }
+ mem_writeb(buffer+1,status);
+ break;
+ case 0x0A : /* Get Audio Disk info */
+ Bit8u tr1,tr2; TMSF leadOut;
+ if (!mscdex->GetCDInfo(drive_unit,tr1,tr2,leadOut)) return 0x05;
+ mem_writeb(buffer+1,tr1);
+ mem_writeb(buffer+2,tr2);
+ mem_writeb(buffer+3,leadOut.fr);
+ mem_writeb(buffer+4,leadOut.sec);
+ mem_writeb(buffer+5,leadOut.min);
+ mem_writeb(buffer+6,0x00);
+ break;
+ case 0x0B :{/* Audio Track Info */
+ Bit8u attr; TMSF start;
+ Bit8u track = mem_readb(buffer+1);
+ mscdex->GetTrackInfo(drive_unit,track,attr,start);
+ mem_writeb(buffer+2,start.fr);
+ mem_writeb(buffer+3,start.sec);
+ mem_writeb(buffer+4,start.min);
+ mem_writeb(buffer+5,0x00);
+ mem_writeb(buffer+6,attr);
+ break; };
+ case 0x0C :{/* Get Audio Sub Channel data */
+ Bit8u attr,track,index;
+ TMSF abs,rel;
+ mscdex->GetSubChannelData(drive_unit,attr,track,index,rel,abs);
+ mem_writeb(buffer+1,attr);
+ mem_writeb(buffer+2,track);
+ mem_writeb(buffer+3,index);
+ mem_writeb(buffer+4,rel.min);
+ mem_writeb(buffer+5,rel.sec);
+ mem_writeb(buffer+6,rel.fr);
+ mem_writeb(buffer+7,0x00);
+ mem_writeb(buffer+8,abs.min);
+ mem_writeb(buffer+9,abs.sec);
+ mem_writeb(buffer+10,abs.fr);
+ break;
+ };
+ case 0x0E :{ /* Get UPC */
+ Bit8u attr; char upc[8];
+ mscdex->GetUPC(drive_unit,attr,&upc[0]);
+ mem_writeb(buffer+1,attr);
+ for (int i=0; i<7; i++) mem_writeb(buffer+2+i,upc[i]);
+ mem_writeb(buffer+9,0x00);
+ break;
+ };
+ case 0x0F :{ /* Get Audio Status */
+ bool playing,pause;
+ TMSF resStart,resEnd;
+ mscdex->GetAudioStatus(drive_unit,playing,pause,resStart,resEnd);
+ mem_writeb(buffer+1,pause);
+ mem_writeb(buffer+3,resStart.min);
+ mem_writeb(buffer+4,resStart.sec);
+ mem_writeb(buffer+5,resStart.fr);
+ mem_writeb(buffer+6,0x00);
+ mem_writeb(buffer+7,resEnd.min);
+ mem_writeb(buffer+8,resEnd.sec);
+ mem_writeb(buffer+9,resEnd.fr);
+ mem_writeb(buffer+10,0x00);
+ break;
+ };
+ default : LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unsupported IOCTL INPUT Subfunction %02X",ioctl_fct);
+ return 0x03; // invalid function
+ }
+ return 0x00; // success
+}
+
+static Bit16u MSCDEX_IOCTL_Optput(PhysPt buffer,Bit8u drive_unit) {
+ Bit8u ioctl_fct = mem_readb(buffer);
+// MSCDEX_LOG("MSCDEX: IOCTL OUTPUT Subfunction %02X",ioctl_fct);
+ switch (ioctl_fct) {
+ case 0x00 : // Unload /eject media
+ if (!mscdex->LoadUnloadMedia(drive_unit,true)) return 0x02;
+ break;
+ case 0x03: //Audio Channel control
+ TCtrl ctrl;
+ for (Bit8u chan=0;chan<4;chan++) {
+ ctrl.out[chan]=mem_readb(buffer+chan*2+1);
+ ctrl.vol[chan]=mem_readb(buffer+chan*2+2);
+ }
+ if (!mscdex->ChannelControl(drive_unit,ctrl)) return 0x01;
+ break;
+ case 0x01 : // (un)Lock door
+ // do nothing -> report as success
+ break;
+ case 0x02 : // Reset Drive
+ LOG(LOG_MISC,LOG_WARN)("cdromDrive reset");
+ if (!mscdex->StopAudio(drive_unit)) return 0x02;
+ break;
+ case 0x05 : // load media
+ if (!mscdex->LoadUnloadMedia(drive_unit,false)) return 0x02;
+ break;
+ default : LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unsupported IOCTL OUTPUT Subfunction %02X",ioctl_fct);
+ return 0x03; // invalid function
+ }
+ return 0x00; // success
+}
+
+static Bitu MSCDEX_Strategy_Handler(void) {
+ curReqheaderPtr = PhysMake(SegValue(es),reg_bx);
+// MSCDEX_LOG("MSCDEX: Device Strategy Routine called, request header at %x",curReqheaderPtr);
+ return CBRET_NONE;
+}
+
+static Bitu MSCDEX_Interrupt_Handler(void) {
+ if (curReqheaderPtr==0) {
+ MSCDEX_LOG("MSCDEX: invalid call to interrupt handler");
+ return CBRET_NONE;
+ }
+ Bit8u subUnit = mem_readb(curReqheaderPtr+1);
+ Bit8u funcNr = mem_readb(curReqheaderPtr+2);
+ Bit16u errcode = 0;
+ PhysPt buffer = 0;
+
+ MSCDEX_LOG("MSCDEX: Driver Function %02X",funcNr);
+
+ if ((funcNr==0x03) || (funcNr==0x0c) || (funcNr==0x80) || (funcNr==0x82)) {
+ buffer = PhysMake(mem_readw(curReqheaderPtr+0x10),mem_readw(curReqheaderPtr+0x0E));
+ }
+
+ switch (funcNr) {
+ case 0x03 : { /* IOCTL INPUT */
+ Bit16u error=MSCDEX_IOCTL_Input(buffer,subUnit);
+ if (error) errcode = error;
+ break;
+ };
+ case 0x0C : { /* IOCTL OUTPUT */
+ Bit16u error=MSCDEX_IOCTL_Optput(buffer,subUnit);
+ if (error) errcode = error;
+ break;
+ };
+ case 0x0D : // device open
+ case 0x0E : // device close - dont care :)
+ break;
+ case 0x80 : // Read long
+ case 0x82 : { // Read long prefetch -> both the same here :)
+ Bit32u start = mem_readd(curReqheaderPtr+0x14);
+ Bit16u len = mem_readw(curReqheaderPtr+0x12);
+ bool raw = (mem_readb(curReqheaderPtr+0x18)==1);
+ if (mem_readb(curReqheaderPtr+0x0D)==0x00) // HSG
+ mscdex->ReadSectors(subUnit,raw,start,len,buffer);
+ else
+ mscdex->ReadSectorsMSF(subUnit,raw,start,len,buffer);
+ break;
+ };
+ case 0x83 : // Seek - dont care :)
+ break;
+ case 0x84 : { /* Play Audio Sectors */
+ Bit32u start = mem_readd(curReqheaderPtr+0x0E);
+ Bit32u len = mem_readd(curReqheaderPtr+0x12);
+ if (mem_readb(curReqheaderPtr+0x0D)==0x00) // HSG
+ mscdex->PlayAudioSector(subUnit,start,len);
+ else // RED BOOK
+ mscdex->PlayAudioMSF(subUnit,start,len);
+ break;
+ };
+ case 0x85 : /* Stop Audio */
+ mscdex->StopAudio(subUnit);
+ break;
+ case 0x88 : /* Resume Audio */
+ mscdex->ResumeAudio(subUnit);
+ break;
+ default : LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unsupported Driver Request %02X",funcNr);
+ break;
+
+ };
+
+ // Set Statusword
+ mem_writew(curReqheaderPtr+3,mscdex->GetStatusWord(subUnit,errcode));
+ MSCDEX_LOG("MSCDEX: Status : %04X",mem_readw(curReqheaderPtr+3));
+ return CBRET_NONE;
+}
+
+static bool MSCDEX_Handler(void) {
+ if(reg_ah == 0x11) {
+ if(reg_al == 0x00) {
+ if (mscdex->rootDriverHeaderSeg==0) return false;
+ PhysPt check = PhysMake(SegValue(ss),reg_sp);
+ if(mem_readw(check+6) == 0xDADA) {
+ //MSCDEX sets word on stack to ADAD if it DADA on entry.
+ mem_writew(check+6,0xADAD);
+ }
+ reg_al = 0xff;
+ return true;
+ } else {
+ LOG(LOG_MISC,LOG_ERROR)("NETWORK REDIRECTOR USED!!!");
+ reg_ax = 0x49;//NETWERK SOFTWARE NOT INSTALLED
+ CALLBACK_SCF(true);
+ return true;
+ }
+ }
+
+ if (reg_ah!=0x15) return false; // not handled here, continue chain
+ if (mscdex->rootDriverHeaderSeg==0) return false; // not handled if MSCDEX not installed
+
+ PhysPt data = PhysMake(SegValue(es),reg_bx);
+ MSCDEX_LOG("MSCDEX: INT 2F %04X BX= %04X CX=%04X",reg_ax,reg_bx,reg_cx);
+ switch (reg_ax) {
+
+ case 0x1500: /* Install check */
+ reg_bx = mscdex->GetNumDrives();
+ if (reg_bx>0) reg_cx = mscdex->GetFirstDrive();
+ reg_al = 0xff;
+ return true;
+ case 0x1501: /* Get cdrom driver info */
+ mscdex->GetDriverInfo(data);
+ return true;
+ case 0x1502: /* Get Copyright filename */
+ case 0x1503: /* Get Abstract filename */
+ case 0x1504: /* Get Documentation filename */
+ if (mscdex->GetFileName(reg_cx,702+(reg_al-2)*37,data)) {
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
+ CALLBACK_SCF(true);
+ };
+ return true;
+ case 0x1505: { // read vtoc
+ Bit16u offset = 0, error = 0;
+ if (mscdex->ReadVTOC(reg_cx,reg_dx,data,offset,error)) {
+// reg_ax = error; // return code
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax = error;
+ CALLBACK_SCF(true);
+ };
+ };
+ return true;
+ case 0x1508: { // read sectors
+ Bit32u sector = (reg_si<<16)+reg_di;
+ if (mscdex->ReadSectors(reg_cx,sector,reg_dx,data)) {
+ reg_ax = 0;
+ CALLBACK_SCF(false);
+ } else {
+ // possibly: MSCDEX_ERROR_DRIVE_NOT_READY if sector is beyond total length
+ reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
+ CALLBACK_SCF(true);
+ };
+ return true;
+ };
+ case 0x1509: // write sectors - not supported
+ reg_ax = MSCDEX_ERROR_INVALID_FUNCTION;
+ CALLBACK_SCF(true);
+ return true;
+ case 0x150B: /* Valid CDROM drive ? */
+ reg_ax = (mscdex->IsValidDrive(reg_cx) ? 0x5ad8 : 0x0000);
+ reg_bx = 0xADAD;
+ return true;
+ case 0x150C: /* Get MSCDEX Version */
+ reg_bx = mscdex->GetVersion();
+ return true;
+ case 0x150D: /* Get drives */
+ mscdex->GetDrives(data);
+ return true;
+ case 0x150E: /* Get/Set Volume Descriptor Preference */
+ if (mscdex->IsValidDrive(reg_cx)) {
+ if (reg_bx == 0) {
+ // get preference
+ reg_dx = 0x100; // preference?
+ CALLBACK_SCF(false);
+ } else if (reg_bx == 1) {
+ // set preference
+ if (reg_dh == 1) {
+ // valid
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax = MSCDEX_ERROR_INVALID_FUNCTION;
+ CALLBACK_SCF(true);
+ }
+ } else {
+ reg_ax = MSCDEX_ERROR_INVALID_FUNCTION;
+ CALLBACK_SCF(true);
+ }
+ } else {
+ reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
+ CALLBACK_SCF(true);
+ }
+ return true;
+ case 0x150F: { // Get directory entry
+ Bit16u error;
+ bool success = mscdex->GetDirectoryEntry(reg_cl,reg_ch&1,data,PhysMake(reg_si,reg_di),error);
+ reg_ax = error;
+ CALLBACK_SCF(!success);
+ }; return true;
+ case 0x1510: /* Device driver request */
+ if (mscdex->SendDriverRequest(reg_cx,data)) {
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
+ CALLBACK_SCF(true);
+ }
+ return true;
+ };
+ LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unknown call : %04X",reg_ax);
+ return true;
+}
+
+class device_MSCDEX : public DOS_Device {
+public:
+ device_MSCDEX() { SetName("MSCD001"); }
+ bool Read (Bit8u * /*data*/,Bit16u * /*size*/) { return false;}
+ bool Write(Bit8u * /*data*/,Bit16u * /*size*/) {
+ LOG(LOG_ALL,LOG_NORMAL)("Write to mscdex device");
+ return false;
+ }
+ bool Seek(Bit32u * /*pos*/,Bit32u /*type*/){return false;}
+ bool Close(){return false;}
+ Bit16u GetInformation(void){return 0xc880;}
+ bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode);
+ bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode);
+private:
+// Bit8u cache;
+};
+
+bool device_MSCDEX::ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) {
+ if (MSCDEX_IOCTL_Input(bufptr,0)==0) {
+ *retcode=size;
+ return true;
+ }
+ return false;
+}
+
+bool device_MSCDEX::WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) {
+ if (MSCDEX_IOCTL_Optput(bufptr,0)==0) {
+ *retcode=size;
+ return true;
+ }
+ return false;
+}
+
+int MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit)
+{
+ int result = mscdex->AddDrive(driveLetter-'A',(char*)physicalPath,subUnit);
+ return result;
+}
+
+int MSCDEX_RemoveDrive(char driveLetter)
+{
+ if(!mscdex) return 0;
+ return mscdex->RemoveDrive(driveLetter-'A');
+}
+
+bool MSCDEX_HasDrive(char driveLetter)
+{
+ return mscdex->HasDrive(driveLetter-'A');
+}
+
+void MSCDEX_ReplaceDrive(CDROM_Interface* cdrom, Bit8u subUnit)
+{
+ mscdex->ReplaceDrive(cdrom, subUnit);
+}
+
+Bit8u MSCDEX_GetSubUnit(char driveLetter)
+{
+ return mscdex->GetSubUnit(driveLetter-'A');
+}
+
+bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name)
+{
+ return mscdex->GetVolumeName(subUnit,name);
+}
+
+bool MSCDEX_HasMediaChanged(Bit8u subUnit)
+{
+ static TMSF leadOut[MSCDEX_MAX_DRIVES];
+
+ TMSF leadnew;
+ Bit8u tr1,tr2;
+ if (mscdex->GetCDInfo(subUnit,tr1,tr2,leadnew)) {
+ bool changed = (leadOut[subUnit].min!=leadnew.min) || (leadOut[subUnit].sec!=leadnew.sec) || (leadOut[subUnit].fr!=leadnew.fr);
+ if (changed) {
+ leadOut[subUnit].min = leadnew.min;
+ leadOut[subUnit].sec = leadnew.sec;
+ leadOut[subUnit].fr = leadnew.fr;
+ mscdex->InitNewMedia(subUnit);
+ }
+ return changed;
+ };
+ if (subUnit<MSCDEX_MAX_DRIVES) {
+ leadOut[subUnit].min = 0;
+ leadOut[subUnit].sec = 0;
+ leadOut[subUnit].fr = 0;
+ }
+ return true;
+}
+
+void MSCDEX_SetCDInterface(int intNr, int numCD) {
+ useCdromInterface = intNr;
+ forceCD = numCD;
+}
+
+void MSCDEX_ShutDown(Section* /*sec*/) {
+ delete mscdex;
+ mscdex = 0;
+ curReqheaderPtr = 0;
+}
+
+void MSCDEX_Init(Section* sec) {
+ // AddDestroy func
+ sec->AddDestroyFunction(&MSCDEX_ShutDown);
+ /* Register the mscdex device */
+ DOS_Device * newdev = new device_MSCDEX();
+ DOS_AddDevice(newdev);
+ curReqheaderPtr = 0;
+ /* Add Multiplexer */
+ DOS_AddMultiplexHandler(MSCDEX_Handler);
+ /* Create MSCDEX */
+ mscdex = new CMscdex;
+}
diff --git a/src/dos/dos_programs.cpp b/src/dos/dos_programs.cpp
new file mode 100644
index 000000000..7e17f11ef
--- /dev/null
+++ b/src/dos/dos_programs.cpp
@@ -0,0 +1,1835 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <string>
+#include <vector>
+#include "programs.h"
+#include "support.h"
+#include "drives.h"
+#include "cross.h"
+#include "regs.h"
+#include "callback.h"
+#include "cdrom.h"
+#include "dos_system.h"
+#include "dos_inc.h"
+#include "bios.h"
+#include "bios_disk.h"
+#include "setup.h"
+#include "control.h"
+#include "inout.h"
+#include "dma.h"
+
+
+#if defined(OS2)
+#define INCL DOSFILEMGR
+#define INCL_DOSERRORS
+#include "os2.h"
+#endif
+
+#if defined(WIN32)
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m)&S_IFMT)==S_IFDIR)
+#endif
+#endif
+
+#if C_DEBUG
+Bitu DEBUG_EnableDebugger(void);
+#endif
+
+void MSCDEX_SetCDInterface(int intNr, int forceCD);
+static Bitu ZDRIVE_NUM = 25;
+
+static const char* UnmountHelper(char umount) {
+ int i_drive;
+ if (umount < '0' || umount > 3+'0')
+ i_drive = toupper(umount) - 'A';
+ else
+ i_drive = umount - '0';
+
+ if (i_drive >= DOS_DRIVES || i_drive < 0)
+ return MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED");
+
+ if (i_drive < MAX_DISK_IMAGES && Drives[i_drive] == NULL && imageDiskList[i_drive] == NULL)
+ return MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED");
+
+ if (i_drive >= MAX_DISK_IMAGES && Drives[i_drive] == NULL)
+ return MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED");
+
+ if (Drives[i_drive]) {
+ switch (DriveManager::UnmountDrive(i_drive)) {
+ case 1: return MSG_Get("PROGRAM_MOUNT_UMOUNT_NO_VIRTUAL");
+ case 2: return MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS");
+ }
+ Drives[i_drive] = 0;
+ mem_writeb(Real2Phys(dos.tables.mediaid)+i_drive*9,0);
+ if (i_drive == DOS_GetDefaultDrive()) {
+ DOS_SetDrive(ZDRIVE_NUM);
+ }
+
+ }
+
+ if (i_drive < MAX_DISK_IMAGES && imageDiskList[i_drive]) {
+ delete imageDiskList[i_drive];
+ imageDiskList[i_drive] = NULL;
+ }
+
+ return MSG_Get("PROGRAM_MOUNT_UMOUNT_SUCCESS");
+}
+
+class MOUNT : public Program {
+public:
+ void ListMounts(void) {
+ char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u date;Bit16u time;Bit8u attr;
+ /* Command uses dta so set it to our internal dta */
+ RealPt save_dta = dos.dta();
+ dos.dta(dos.tables.tempdta);
+ DOS_DTA dta(dos.dta());
+
+ WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_1"));
+ WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_FORMAT"),"Drive","Type","Label");
+ for(int p = 0;p < 8;p++) WriteOut("----------");
+
+ for (int d = 0;d < DOS_DRIVES;d++) {
+ if (!Drives[d]) continue;
+
+ char root[7] = {static_cast<char>('A'+d),':','\\','*','.','*',0};
+ bool ret = DOS_FindFirst(root,DOS_ATTR_VOLUME);
+ if (ret) {
+ dta.GetResult(name,size,date,time,attr);
+ DOS_FindNext(); //Mark entry as invalid
+ } else name[0] = 0;
+
+ /* Change 8.3 to 11.0 */
+ char* dot = strchr(name,'.');
+ if(dot && (dot - name == 8) ) {
+ name[8] = name[9];name[9] = name[10];name[10] = name[11];name[11] = 0;
+ }
+
+ root[1] = 0; //This way, the format string can be reused.
+ WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_FORMAT"),root, Drives[d]->GetInfo(),name);
+ }
+ dos.dta(save_dta);
+ }
+
+ void Run(void) {
+ DOS_Drive * newdrive;char drive;
+ std::string label;
+ std::string umount;
+ std::string newz;
+
+ //Hack To allow long commandlines
+ ChangeToLongCmd();
+ /* Parse the command line */
+ /* if the command line is empty show current mounts */
+ if (!cmd->GetCount()) {
+ ListMounts();
+ return;
+ }
+
+ /* In secure mode don't allow people to change mount points.
+ * Neither mount nor unmount */
+ if(control->SecureMode()) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
+ return;
+ }
+ bool path_relative_to_last_config = false;
+ if (cmd->FindExist("-pr",true)) path_relative_to_last_config = true;
+
+ /* Check for unmounting */
+ if (cmd->FindString("-u",umount,false)) {
+ WriteOut(UnmountHelper(umount[0]), toupper(umount[0]));
+ return;
+ }
+
+ /* Check for moving Z: */
+ /* Only allowing moving it once. It is merely a convenience added for the wine team */
+ if (ZDRIVE_NUM == 25 && cmd->FindString("-z", newz,false)) {
+ newz[0] = toupper(newz[0]);
+ int i_newz = newz[0] - 'A';
+ if (i_newz >= 0 && i_newz < DOS_DRIVES-1 && !Drives[i_newz]) {
+ ZDRIVE_NUM = i_newz;
+ /* remap drives */
+ Drives[i_newz] = Drives[25];
+ Drives[25] = 0;
+ if (!first_shell) return; //Should not be possible
+ /* Update environment */
+ std::string line = "";
+ char ppp[2] = {newz[0],0};
+ std::string tempenv = ppp; tempenv += ":\\";
+ if (first_shell->GetEnvStr("PATH",line)){
+ std::string::size_type idx = line.find('=');
+ std::string value = line.substr(idx +1 , std::string::npos);
+ while ( (idx = value.find("Z:\\")) != std::string::npos ||
+ (idx = value.find("z:\\")) != std::string::npos )
+ value.replace(idx,3,tempenv);
+ line = value;
+ }
+ if (!line.size()) line = tempenv;
+ first_shell->SetEnv("PATH",line.c_str());
+ tempenv += "COMMAND.COM";
+ first_shell->SetEnv("COMSPEC",tempenv.c_str());
+
+ /* Update batch file if running from Z: (very likely: autoexec) */
+ if(first_shell->bf) {
+ std::string &name = first_shell->bf->filename;
+ if(name.length() >2 && name[0] == 'Z' && name[1] == ':') name[0] = newz[0];
+ }
+ /* Change the active drive */
+ if (DOS_GetDefaultDrive() == 25) DOS_SetDrive(i_newz);
+ }
+ return;
+ }
+ /* Show list of cdroms */
+ if (cmd->FindExist("-cd",false)) {
+ int num = SDL_CDNumDrives();
+ WriteOut(MSG_Get("PROGRAM_MOUNT_CDROMS_FOUND"),num);
+ for (int i=0; i<num; i++) {
+ WriteOut("%2d. %s\n",i,SDL_CDName(i));
+ };
+ return;
+ }
+
+ std::string type="dir";
+ cmd->FindString("-t",type,true);
+ bool iscdrom = (type =="cdrom"); //Used for mscdex bug cdrom label name emulation
+ if (type=="floppy" || type=="dir" || type=="cdrom" || type =="overlay") {
+ Bit16u sizes[4];
+ Bit8u mediaid;
+ std::string str_size;
+ if (type=="floppy") {
+ str_size="512,1,2880,2880";/* All space free */
+ mediaid=0xF0; /* Floppy 1.44 media */
+ } else if (type=="dir" || type == "overlay") {
+ // 512*32*32765==~500MB total size
+ // 512*32*16000==~250MB total free size
+ str_size="512,32,32765,16000";
+ mediaid=0xF8; /* Hard Disk */
+ } else if (type=="cdrom") {
+ str_size="2048,1,65535,0";
+ mediaid=0xF8; /* Hard Disk */
+ } else {
+ WriteOut(MSG_Get("PROGAM_MOUNT_ILL_TYPE"),type.c_str());
+ return;
+ }
+ /* Parse the free space in mb's (kb's for floppies) */
+ std::string mb_size;
+ if(cmd->FindString("-freesize",mb_size,true)) {
+ char teststr[1024];
+ Bit16u freesize = static_cast<Bit16u>(atoi(mb_size.c_str()));
+ if (type=="floppy") {
+ // freesize in kb
+ sprintf(teststr,"512,1,2880,%d",freesize*1024/(512*1));
+ } else {
+ Bit32u total_size_cyl=32765;
+ Bit32u free_size_cyl=(Bit32u)freesize*1024*1024/(512*32);
+ if (free_size_cyl>65534) free_size_cyl=65534;
+ if (total_size_cyl<free_size_cyl) total_size_cyl=free_size_cyl+10;
+ if (total_size_cyl>65534) total_size_cyl=65534;
+ sprintf(teststr,"512,32,%d,%d",total_size_cyl,free_size_cyl);
+ }
+ str_size=teststr;
+ }
+
+ cmd->FindString("-size",str_size,true);
+ char number[21] = { 0 };const char * scan = str_size.c_str();
+ Bitu index = 0;Bitu count = 0;
+ /* Parse the str_size string */
+ while (*scan && index < 20 && count < 4) {
+ if (*scan==',') {
+ number[index] = 0;
+ sizes[count++] = atoi(number);
+ index = 0;
+ } else number[index++] = *scan;
+ scan++;
+ }
+ if (count < 4) {
+ number[index] = 0; //always goes correct as index is max 20 at this point.
+ sizes[count] = atoi(number);
+ }
+
+ // get the drive letter
+ cmd->FindCommand(1,temp_line);
+ if ((temp_line.size() > 2) || ((temp_line.size()>1) && (temp_line[1]!=':'))) goto showusage;
+ int i_drive = toupper(temp_line[0]);
+ if (!isalpha(i_drive)) goto showusage;
+ if ((i_drive - 'A') >= DOS_DRIVES || (i_drive-'A') < 0 ) goto showusage;
+ drive = static_cast<char>(i_drive);
+
+ if (!cmd->FindCommand(2,temp_line)) goto showusage;
+ if (!temp_line.size()) goto showusage;
+ if(path_relative_to_last_config && control->configfiles.size() && !Cross::IsPathAbsolute(temp_line)) {
+ std::string lastconfigdir(control->configfiles[control->configfiles.size()-1]);
+ std::string::size_type pos = lastconfigdir.rfind(CROSS_FILESPLIT);
+ if(pos == std::string::npos) pos = 0; //No directory then erase string
+ lastconfigdir.erase(pos);
+ if (lastconfigdir.length()) temp_line = lastconfigdir + CROSS_FILESPLIT + temp_line;
+ }
+ struct stat test;
+ //Win32 : strip tailing backslashes
+ //os2: some special drive check
+ //rest: substitute ~ for home
+ bool failed = false;
+#if defined (WIN32) || defined(OS2)
+ /* Removing trailing backslash if not root dir so stat will succeed */
+ if(temp_line.size() > 3 && temp_line[temp_line.size()-1]=='\\') temp_line.erase(temp_line.size()-1,1);
+ if (stat(temp_line.c_str(),&test)) {
+#endif
+#if defined(WIN32)
+// Nothing to do here.
+#elif defined (OS2)
+ if (temp_line.size() <= 2) // Seems to be a drive.
+ {
+ failed = true;
+ HFILE cdrom_fd = 0;
+ ULONG ulAction = 0;
+
+ APIRET rc = DosOpen((unsigned char*)temp_line.c_str(), &cdrom_fd, &ulAction, 0L, FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS,
+ OPEN_FLAGS_DASD | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY, 0L);
+ DosClose(cdrom_fd);
+ if (rc != NO_ERROR && rc != ERROR_NOT_READY)
+ {
+ failed = true;
+ } else {
+ failed = false;
+ }
+ }
+ }
+ if (failed) {
+#else
+ if (stat(temp_line.c_str(),&test)) {
+ failed = true;
+ Cross::ResolveHomedir(temp_line);
+ //Try again after resolving ~
+ if(!stat(temp_line.c_str(),&test)) failed = false;
+ }
+ if(failed) {
+#endif
+ WriteOut(MSG_Get("PROGRAM_MOUNT_ERROR_1"),temp_line.c_str());
+ return;
+ }
+ /* Not a switch so a normal directory/file */
+ if (!S_ISDIR(test.st_mode)) {
+#ifdef OS2
+ HFILE cdrom_fd = 0;
+ ULONG ulAction = 0;
+
+ APIRET rc = DosOpen((unsigned char*)temp_line.c_str(), &cdrom_fd, &ulAction, 0L, FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS,
+ OPEN_FLAGS_DASD | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY, 0L);
+ DosClose(cdrom_fd);
+ if (rc != NO_ERROR && rc != ERROR_NOT_READY) {
+ WriteOut(MSG_Get("PROGRAM_MOUNT_ERROR_2"),temp_line.c_str());
+ return;
+ }
+#else
+ WriteOut(MSG_Get("PROGRAM_MOUNT_ERROR_2"),temp_line.c_str());
+ return;
+#endif
+ }
+
+ if (temp_line[temp_line.size()-1]!=CROSS_FILESPLIT) temp_line+=CROSS_FILESPLIT;
+ Bit8u bit8size=(Bit8u) sizes[1];
+ if (type=="cdrom") {
+ int num = -1;
+ cmd->FindInt("-usecd",num,true);
+ int error = 0;
+ if (cmd->FindExist("-aspi",false)) {
+ MSCDEX_SetCDInterface(CDROM_USE_ASPI, num);
+ } else if (cmd->FindExist("-ioctl_dio",false)) {
+ MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num);
+ } else if (cmd->FindExist("-ioctl_dx",false)) {
+ MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DX, num);
+#if defined (WIN32)
+ } else if (cmd->FindExist("-ioctl_mci",false)) {
+ MSCDEX_SetCDInterface(CDROM_USE_IOCTL_MCI, num);
+#endif
+ } else if (cmd->FindExist("-noioctl",false)) {
+ MSCDEX_SetCDInterface(CDROM_USE_SDL, num);
+ } else {
+#if defined (WIN32)
+ // Check OS
+ OSVERSIONINFO osi;
+ osi.dwOSVersionInfoSize = sizeof(osi);
+ GetVersionEx(&osi);
+ if ((osi.dwPlatformId==VER_PLATFORM_WIN32_NT) && (osi.dwMajorVersion>5)) {
+ // Vista/above
+ MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DX, num);
+ } else {
+ MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num);
+ }
+#else
+ MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num);
+#endif
+ }
+ newdrive = new cdromDrive(drive,temp_line.c_str(),sizes[0],bit8size,sizes[2],0,mediaid,error);
+ // Check Mscdex, if it worked out...
+ switch (error) {
+ case 0 : WriteOut(MSG_Get("MSCDEX_SUCCESS")); break;
+ case 1 : WriteOut(MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS")); break;
+ case 2 : WriteOut(MSG_Get("MSCDEX_ERROR_NOT_SUPPORTED")); break;
+ case 3 : WriteOut(MSG_Get("MSCDEX_ERROR_PATH")); break;
+ case 4 : WriteOut(MSG_Get("MSCDEX_TOO_MANY_DRIVES")); break;
+ case 5 : WriteOut(MSG_Get("MSCDEX_LIMITED_SUPPORT")); break;
+ default : WriteOut(MSG_Get("MSCDEX_UNKNOWN_ERROR")); break;
+ };
+ if (error && error!=5) {
+ delete newdrive;
+ return;
+ }
+ } else {
+ /* Give a warning when mount c:\ or the / */
+#if defined (WIN32) || defined(OS2)
+ if( (temp_line == "c:\\") || (temp_line == "C:\\") ||
+ (temp_line == "c:/") || (temp_line == "C:/") )
+ WriteOut(MSG_Get("PROGRAM_MOUNT_WARNING_WIN"));
+#else
+ if(temp_line == "/") WriteOut(MSG_Get("PROGRAM_MOUNT_WARNING_OTHER"));
+#endif
+ if(type == "overlay") {
+ //Ensure that the base drive exists:
+ if (!Drives[drive-'A']) {
+ WriteOut("No basedrive mounted yet!");
+ return;
+ }
+ localDrive* ldp = dynamic_cast<localDrive*>(Drives[drive-'A']);
+ cdromDrive* cdp = dynamic_cast<cdromDrive*>(Drives[drive-'A']);
+ if (!ldp || cdp) {
+ WriteOut("Basedrive not compatible");
+ return;
+ }
+ std::string base = ldp->getBasedir();
+ Bit8u o_error = 0;
+ newdrive = new Overlay_Drive(base.c_str(),temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid,o_error);
+ //Erase old drive on success
+ if (newdrive) {
+ if (o_error) {
+ if (o_error == 1) WriteOut("No mixing of relative and absolute paths. Overlay failed.");
+ else if (o_error == 2) WriteOut("overlay directory can not be the same as underlying file system.");
+ else WriteOut("Something went wrong");
+ delete newdrive;
+ return;
+ }
+ delete Drives[drive-'A'];
+ Drives[drive-'A'] = 0;
+ } else {
+ WriteOut("overlaydrive construction failed.");
+ return;
+ }
+ } else {
+ newdrive=new localDrive(temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid);
+ }
+ }
+ } else {
+ WriteOut(MSG_Get("PROGRAM_MOUNT_ILL_TYPE"),type.c_str());
+ return;
+ }
+ if (Drives[drive-'A']) {
+ WriteOut(MSG_Get("PROGRAM_MOUNT_ALREADY_MOUNTED"),drive,Drives[drive-'A']->GetInfo());
+ if (newdrive) delete newdrive;
+ return;
+ }
+ if (!newdrive) E_Exit("DOS:Can't create drive");
+ Drives[drive-'A']=newdrive;
+ /* Set the correct media byte in the table */
+ mem_writeb(Real2Phys(dos.tables.mediaid)+(drive-'A')*9,newdrive->GetMediaByte());
+ if (type != "overlay") WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"),drive,newdrive->GetInfo());
+ else WriteOut("Overlay %s on drive %c mounted.\n",temp_line.c_str(),drive);
+ /* check if volume label is given and don't allow it to updated in the future */
+ if (cmd->FindString("-label",label,true)) newdrive->dirCache.SetLabel(label.c_str(),iscdrom,false);
+ /* For hard drives set the label to DRIVELETTER_Drive.
+ * For floppy drives set the label to DRIVELETTER_Floppy.
+ * This way every drive except cdroms should get a label.*/
+ else if(type == "dir" || type == "overlay") {
+ label = drive; label += "_DRIVE";
+ newdrive->dirCache.SetLabel(label.c_str(),iscdrom,false);
+ } else if(type == "floppy") {
+ label = drive; label += "_FLOPPY";
+ newdrive->dirCache.SetLabel(label.c_str(),iscdrom,true);
+ }
+ if(type == "floppy") incrementFDD();
+ return;
+showusage:
+#if defined (WIN32) || defined(OS2)
+ WriteOut(MSG_Get("PROGRAM_MOUNT_USAGE"),"d:\\dosprogs","d:\\dosprogs");
+#else
+ WriteOut(MSG_Get("PROGRAM_MOUNT_USAGE"),"~/dosprogs","~/dosprogs");
+#endif
+ return;
+ }
+};
+
+static void MOUNT_ProgramStart(Program * * make) {
+ *make=new MOUNT;
+}
+
+class MEM : public Program {
+public:
+ void Run(void) {
+ /* Show conventional Memory */
+ WriteOut("\n");
+
+ Bit16u umb_start=dos_infoblock.GetStartOfUMBChain();
+ Bit8u umb_flag=dos_infoblock.GetUMBChainState();
+ Bit8u old_memstrat=DOS_GetMemAllocStrategy()&0xff;
+ if (umb_start!=0xffff) {
+ if ((umb_flag&1)==1) DOS_LinkUMBsToMemChain(0);
+ DOS_SetMemAllocStrategy(0);
+ }
+
+ Bit16u seg,blocks;blocks=0xffff;
+ DOS_AllocateMemory(&seg,&blocks);
+ WriteOut(MSG_Get("PROGRAM_MEM_CONVEN"),blocks*16/1024);
+
+ if (umb_start!=0xffff) {
+ DOS_LinkUMBsToMemChain(1);
+ DOS_SetMemAllocStrategy(0x40); // search in UMBs only
+
+ Bit16u largest_block=0,total_blocks=0,block_count=0;
+ for (;; block_count++) {
+ blocks=0xffff;
+ DOS_AllocateMemory(&seg,&blocks);
+ if (blocks==0) break;
+ total_blocks+=blocks;
+ if (blocks>largest_block) largest_block=blocks;
+ DOS_AllocateMemory(&seg,&blocks);
+ }
+
+ Bit8u current_umb_flag=dos_infoblock.GetUMBChainState();
+ if ((current_umb_flag&1)!=(umb_flag&1)) DOS_LinkUMBsToMemChain(umb_flag);
+ DOS_SetMemAllocStrategy(old_memstrat); // restore strategy
+
+ if (block_count>0) WriteOut(MSG_Get("PROGRAM_MEM_UPPER"),total_blocks*16/1024,block_count,largest_block*16/1024);
+ }
+
+ /* Test for and show free XMS */
+ reg_ax=0x4300;CALLBACK_RunRealInt(0x2f);
+ if (reg_al==0x80) {
+ reg_ax=0x4310;CALLBACK_RunRealInt(0x2f);
+ Bit16u xms_seg=SegValue(es);Bit16u xms_off=reg_bx;
+ reg_ah=8;
+ CALLBACK_RunRealFar(xms_seg,xms_off);
+ if (!reg_bl) {
+ WriteOut(MSG_Get("PROGRAM_MEM_EXTEND"),reg_dx);
+ }
+ }
+ /* Test for and show free EMS */
+ Bit16u handle;
+ char emm[9] = { 'E','M','M','X','X','X','X','0',0 };
+ if (DOS_OpenFile(emm,0,&handle)) {
+ DOS_CloseFile(handle);
+ reg_ah=0x42;
+ CALLBACK_RunRealInt(0x67);
+ WriteOut(MSG_Get("PROGRAM_MEM_EXPAND"),reg_bx*16);
+ }
+ }
+};
+
+
+static void MEM_ProgramStart(Program * * make) {
+ *make=new MEM;
+}
+
+extern Bit32u floppytype;
+
+
+class BOOT : public Program {
+private:
+
+ FILE *getFSFile_mounted(char const* filename, Bit32u *ksize, Bit32u *bsize, Bit8u *error) {
+ //if return NULL then put in error the errormessage code if an error was requested
+ bool tryload = (*error)?true:false;
+ *error = 0;
+ Bit8u drive;
+ FILE *tmpfile;
+ char fullname[DOS_PATHLENGTH];
+
+ localDrive* ldp=0;
+ if (!DOS_MakeName(const_cast<char*>(filename),fullname,&drive)) return NULL;
+
+ try {
+ ldp=dynamic_cast<localDrive*>(Drives[drive]);
+ if(!ldp) return NULL;
+
+ tmpfile = ldp->GetSystemFilePtr(fullname, "rb");
+ if(tmpfile == NULL) {
+ if (!tryload) *error=1;
+ return NULL;
+ }
+
+ // get file size
+ fseek(tmpfile,0L, SEEK_END);
+ *ksize = (ftell(tmpfile) / 1024);
+ *bsize = ftell(tmpfile);
+ fclose(tmpfile);
+
+ tmpfile = ldp->GetSystemFilePtr(fullname, "rb+");
+ if(tmpfile == NULL) {
+// if (!tryload) *error=2;
+// return NULL;
+ WriteOut(MSG_Get("PROGRAM_BOOT_WRITE_PROTECTED"));
+ tmpfile = ldp->GetSystemFilePtr(fullname, "rb");
+ if(tmpfile == NULL) {
+ if (!tryload) *error=1;
+ return NULL;
+ }
+ }
+
+ return tmpfile;
+ }
+ catch(...) {
+ return NULL;
+ }
+ }
+
+ FILE *getFSFile(char const * filename, Bit32u *ksize, Bit32u *bsize,bool tryload=false) {
+ Bit8u error = tryload?1:0;
+ FILE* tmpfile = getFSFile_mounted(filename,ksize,bsize,&error);
+ if(tmpfile) return tmpfile;
+ //File not found on mounted filesystem. Try regular filesystem
+ std::string filename_s(filename);
+ Cross::ResolveHomedir(filename_s);
+ tmpfile = fopen_wrap(filename_s.c_str(),"rb+");
+ if(!tmpfile) {
+ if( (tmpfile = fopen_wrap(filename_s.c_str(),"rb")) ) {
+ //File exists; So can't be opened in correct mode => error 2
+// fclose(tmpfile);
+// if(tryload) error = 2;
+ WriteOut(MSG_Get("PROGRAM_BOOT_WRITE_PROTECTED"));
+ fseek(tmpfile,0L, SEEK_END);
+ *ksize = (ftell(tmpfile) / 1024);
+ *bsize = ftell(tmpfile);
+ return tmpfile;
+ }
+ // Give the delayed errormessages from the mounted variant (or from above)
+ if(error == 1) WriteOut(MSG_Get("PROGRAM_BOOT_NOT_EXIST"));
+ if(error == 2) WriteOut(MSG_Get("PROGRAM_BOOT_NOT_OPEN"));
+ return NULL;
+ }
+ fseek(tmpfile,0L, SEEK_END);
+ *ksize = (ftell(tmpfile) / 1024);
+ *bsize = ftell(tmpfile);
+ return tmpfile;
+ }
+
+ void printError(void) {
+ WriteOut(MSG_Get("PROGRAM_BOOT_PRINT_ERROR"));
+ }
+
+ void disable_umb_ems_xms(void) {
+ Section* dos_sec = control->GetSection("dos");
+ dos_sec->ExecuteDestroy(false);
+ char test[20];
+ strcpy(test,"umb=false");
+ dos_sec->HandleInputline(test);
+ strcpy(test,"xms=false");
+ dos_sec->HandleInputline(test);
+ strcpy(test,"ems=false");
+ dos_sec->HandleInputline(test);
+ dos_sec->ExecuteInit(false);
+ }
+
+public:
+
+ void Run(void) {
+ //Hack To allow long commandlines
+ ChangeToLongCmd();
+ /* In secure mode don't allow people to boot stuff.
+ * They might try to corrupt the data on it */
+ if(control->SecureMode()) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
+ return;
+ }
+
+ FILE *usefile_1=NULL;
+ FILE *usefile_2=NULL;
+ Bitu i=0;
+ Bit32u floppysize=0;
+ Bit32u rombytesize_1=0;
+ Bit32u rombytesize_2=0;
+ Bit8u drive = 'A';
+ std::string cart_cmd="";
+
+ if(!cmd->GetCount()) {
+ printError();
+ return;
+ }
+ while(i<cmd->GetCount()) {
+ if(cmd->FindCommand(i+1, temp_line)) {
+ if((temp_line == "-l") || (temp_line == "-L")) {
+ /* Specifying drive... next argument then is the drive */
+ i++;
+ if(cmd->FindCommand(i+1, temp_line)) {
+ drive=toupper(temp_line[0]);
+ if ((drive != 'A') && (drive != 'C') && (drive != 'D')) {
+ printError();
+ return;
+ }
+
+ } else {
+ printError();
+ return;
+ }
+ i++;
+ continue;
+ }
+
+ if((temp_line == "-e") || (temp_line == "-E")) {
+ /* Command mode for PCJr cartridges */
+ i++;
+ if(cmd->FindCommand(i + 1, temp_line)) {
+ for(size_t ct = 0;ct < temp_line.size();ct++) temp_line[ct] = toupper(temp_line[ct]);
+ cart_cmd = temp_line;
+ } else {
+ printError();
+ return;
+ }
+ i++;
+ continue;
+ }
+
+ if ( i >= MAX_SWAPPABLE_DISKS ) {
+ return; //TODO give a warning.
+ }
+ WriteOut(MSG_Get("PROGRAM_BOOT_IMAGE_OPEN"), temp_line.c_str());
+ Bit32u rombytesize;
+ FILE *usefile = getFSFile(temp_line.c_str(), &floppysize, &rombytesize);
+ if(usefile != NULL) {
+ if(diskSwap[i] != NULL) delete diskSwap[i];
+ diskSwap[i] = new imageDisk(usefile, (Bit8u *)temp_line.c_str(), floppysize, false);
+ if (usefile_1==NULL) {
+ usefile_1=usefile;
+ rombytesize_1=rombytesize;
+ } else {
+ usefile_2=usefile;
+ rombytesize_2=rombytesize;
+ }
+ } else {
+ WriteOut(MSG_Get("PROGRAM_BOOT_IMAGE_NOT_OPEN"), temp_line.c_str());
+ return;
+ }
+
+ }
+ i++;
+ }
+
+ swapPosition = 0;
+
+ swapInDisks();
+
+ if(imageDiskList[drive-65]==NULL) {
+ WriteOut(MSG_Get("PROGRAM_BOOT_UNABLE"), drive);
+ return;
+ }
+
+ bootSector bootarea;
+ imageDiskList[drive-65]->Read_Sector(0,0,1,(Bit8u *)&bootarea);
+ if ((bootarea.rawdata[0]==0x50) && (bootarea.rawdata[1]==0x43) && (bootarea.rawdata[2]==0x6a) && (bootarea.rawdata[3]==0x72)) {
+ if (machine!=MCH_PCJR) WriteOut(MSG_Get("PROGRAM_BOOT_CART_WO_PCJR"));
+ else {
+ Bit8u rombuf[65536];
+ Bits cfound_at=-1;
+ if (cart_cmd!="") {
+ /* read cartridge data into buffer */
+ fseek(usefile_1,0x200L, SEEK_SET);
+ fread(rombuf, 1, rombytesize_1-0x200, usefile_1);
+
+ char cmdlist[1024];
+ cmdlist[0]=0;
+ Bitu ct=6;
+ Bits clen=rombuf[ct];
+ char buf[257];
+ if (cart_cmd=="?") {
+ while (clen!=0) {
+ strncpy(buf,(char*)&rombuf[ct+1],clen);
+ buf[clen]=0;
+ upcase(buf);
+ strcat(cmdlist," ");
+ strcat(cmdlist,buf);
+ ct+=1+clen+3;
+ if (ct>sizeof(cmdlist)) break;
+ clen=rombuf[ct];
+ }
+ if (ct>6) {
+ WriteOut(MSG_Get("PROGRAM_BOOT_CART_LIST_CMDS"),cmdlist);
+ } else {
+ WriteOut(MSG_Get("PROGRAM_BOOT_CART_NO_CMDS"));
+ }
+ for(Bitu dct=0;dct<MAX_SWAPPABLE_DISKS;dct++) {
+ if(diskSwap[dct]!=NULL) {
+ delete diskSwap[dct];
+ diskSwap[dct]=NULL;
+ }
+ }
+ //fclose(usefile_1); //delete diskSwap closes the file
+ return;
+ } else {
+ while (clen!=0) {
+ strncpy(buf,(char*)&rombuf[ct+1],clen);
+ buf[clen]=0;
+ upcase(buf);
+ strcat(cmdlist," ");
+ strcat(cmdlist,buf);
+ ct+=1+clen;
+
+ if (cart_cmd==buf) {
+ cfound_at=ct;
+ break;
+ }
+
+ ct+=3;
+ if (ct>sizeof(cmdlist)) break;
+ clen=rombuf[ct];
+ }
+ if (cfound_at<=0) {
+ if (ct>6) {
+ WriteOut(MSG_Get("PROGRAM_BOOT_CART_LIST_CMDS"),cmdlist);
+ } else {
+ WriteOut(MSG_Get("PROGRAM_BOOT_CART_NO_CMDS"));
+ }
+ for(Bitu dct=0;dct<MAX_SWAPPABLE_DISKS;dct++) {
+ if(diskSwap[dct]!=NULL) {
+ delete diskSwap[dct];
+ diskSwap[dct]=NULL;
+ }
+ }
+ //fclose(usefile_1); //Delete diskSwap closes the file
+ return;
+ }
+ }
+ }
+
+ disable_umb_ems_xms();
+ void PreparePCJRCartRom(void);
+ PreparePCJRCartRom();
+
+ if (usefile_1==NULL) return;
+
+ Bit32u sz1,sz2;
+ FILE *tfile = getFSFile("system.rom", &sz1, &sz2, true);
+ if (tfile!=NULL) {
+ fseek(tfile, 0x3000L, SEEK_SET);
+ Bit32u drd=(Bit32u)fread(rombuf, 1, 0xb000, tfile);
+ if (drd==0xb000) {
+ for(i=0;i<0xb000;i++) phys_writeb(0xf3000+i,rombuf[i]);
+ }
+ fclose(tfile);
+ }
+
+ if (usefile_2!=NULL) {
+ fseek(usefile_2, 0x0L, SEEK_SET);
+ fread(rombuf, 1, 0x200, usefile_2);
+ PhysPt romseg_pt=host_readw(&rombuf[0x1ce])<<4;
+
+ /* read cartridge data into buffer */
+ fseek(usefile_2, 0x200L, SEEK_SET);
+ fread(rombuf, 1, rombytesize_2-0x200, usefile_2);
+ //fclose(usefile_2); //usefile_2 is in diskSwap structure which should be deleted to close the file
+
+ /* write cartridge data into ROM */
+ for(i=0;i<rombytesize_2-0x200;i++) phys_writeb(romseg_pt+i,rombuf[i]);
+ }
+
+ fseek(usefile_1, 0x0L, SEEK_SET);
+ fread(rombuf, 1, 0x200, usefile_1);
+ Bit16u romseg=host_readw(&rombuf[0x1ce]);
+
+ /* read cartridge data into buffer */
+ fseek(usefile_1,0x200L, SEEK_SET);
+ fread(rombuf, 1, rombytesize_1-0x200, usefile_1);
+ //fclose(usefile_1); //usefile_1 is in diskSwap structure which should be deleted to close the file
+
+ /* write cartridge data into ROM */
+ for(i=0;i<rombytesize_1-0x200;i++) phys_writeb((romseg<<4)+i,rombuf[i]);
+
+ //Close cardridges
+ for(Bitu dct=0;dct<MAX_SWAPPABLE_DISKS;dct++) {
+ if(diskSwap[dct]!=NULL) {
+ delete diskSwap[dct];
+ diskSwap[dct]=NULL;
+ }
+ }
+
+
+ if (cart_cmd=="") {
+ Bit32u old_int18=mem_readd(0x60);
+ /* run cartridge setup */
+ SegSet16(ds,romseg);
+ SegSet16(es,romseg);
+ SegSet16(ss,0x8000);
+ reg_esp=0xfffe;
+ CALLBACK_RunRealFar(romseg,0x0003);
+
+ Bit32u new_int18=mem_readd(0x60);
+ if (old_int18!=new_int18) {
+ /* boot cartridge (int18) */
+ SegSet16(cs,RealSeg(new_int18));
+ reg_ip = RealOff(new_int18);
+ }
+ } else {
+ if (cfound_at>0) {
+ /* run cartridge setup */
+ SegSet16(ds,dos.psp());
+ SegSet16(es,dos.psp());
+ CALLBACK_RunRealFar(romseg,cfound_at);
+ }
+ }
+ }
+ } else {
+ disable_umb_ems_xms();
+ void RemoveEMSPageFrame(void);
+ RemoveEMSPageFrame();
+ WriteOut(MSG_Get("PROGRAM_BOOT_BOOT"), drive);
+ for(i=0;i<512;i++) real_writeb(0, 0x7c00 + i, bootarea.rawdata[i]);
+
+ /* create appearance of floppy drive DMA usage (Demon's Forge) */
+ if (!IS_TANDY_ARCH && floppysize!=0) GetDMAChannel(2)->tcount=true;
+
+ /* revector some dos-allocated interrupts */
+ real_writed(0,0x01*4,0xf000ff53);
+ real_writed(0,0x03*4,0xf000ff53);
+
+ SegSet16(cs, 0);
+ reg_ip = 0x7c00;
+ SegSet16(ds, 0);
+ SegSet16(es, 0);
+ /* set up stack at a safe place */
+ SegSet16(ss, 0x7000);
+ reg_esp = 0x100;
+ reg_esi = 0;
+ reg_ecx = 1;
+ reg_ebp = 0;
+ reg_eax = 0;
+ reg_edx = 0; //Head 0 drive 0
+ reg_ebx= 0x7c00; //Real code probably uses bx to load the image
+ }
+ }
+};
+
+static void BOOT_ProgramStart(Program * * make) {
+ *make=new BOOT;
+}
+
+
+class LOADROM : public Program {
+public:
+ void Run(void) {
+ if (!(cmd->FindCommand(1, temp_line))) {
+ WriteOut(MSG_Get("PROGRAM_LOADROM_SPECIFY_FILE"));
+ return;
+ }
+
+ Bit8u drive;
+ char fullname[DOS_PATHLENGTH];
+ localDrive* ldp=0;
+ if (!DOS_MakeName((char *)temp_line.c_str(),fullname,&drive)) return;
+
+ try {
+ /* try to read ROM file into buffer */
+ ldp=dynamic_cast<localDrive*>(Drives[drive]);
+ if(!ldp) return;
+
+ FILE *tmpfile = ldp->GetSystemFilePtr(fullname, "rb");
+ if(tmpfile == NULL) {
+ WriteOut(MSG_Get("PROGRAM_LOADROM_CANT_OPEN"));
+ return;
+ }
+ fseek(tmpfile, 0L, SEEK_END);
+ if (ftell(tmpfile)>0x8000) {
+ WriteOut(MSG_Get("PROGRAM_LOADROM_TOO_LARGE"));
+ fclose(tmpfile);
+ return;
+ }
+ fseek(tmpfile, 0L, SEEK_SET);
+ Bit8u rom_buffer[0x8000];
+ Bitu data_read = fread(rom_buffer, 1, 0x8000, tmpfile);
+ fclose(tmpfile);
+
+ /* try to identify ROM type */
+ PhysPt rom_base = 0;
+ if (data_read >= 0x4000 && rom_buffer[0] == 0x55 && rom_buffer[1] == 0xaa &&
+ (rom_buffer[3] & 0xfc) == 0xe8 && strncmp((char*)(&rom_buffer[0x1e]), "IBM", 3) == 0) {
+
+ if (!IS_EGAVGA_ARCH) {
+ WriteOut(MSG_Get("PROGRAM_LOADROM_INCOMPATIBLE"));
+ return;
+ }
+ rom_base = PhysMake(0xc000, 0); // video BIOS
+ }
+ else if (data_read == 0x8000 && rom_buffer[0] == 0xe9 && rom_buffer[1] == 0x8f &&
+ rom_buffer[2] == 0x7e && strncmp((char*)(&rom_buffer[0x4cd4]), "IBM", 3) == 0) {
+
+ rom_base = PhysMake(0xf600, 0); // BASIC
+ }
+
+ if (rom_base) {
+ /* write buffer into ROM */
+ for (Bitu i=0; i<data_read; i++) phys_writeb(rom_base + i, rom_buffer[i]);
+
+ if (rom_base == 0xc0000) {
+ /* initialize video BIOS */
+ phys_writeb(PhysMake(0xf000, 0xf065), 0xcf);
+ reg_flags &= ~FLAG_IF;
+ CALLBACK_RunRealFar(0xc000, 0x0003);
+ LOG_MSG("Video BIOS ROM loaded and initialized.");
+ }
+ else WriteOut(MSG_Get("PROGRAM_LOADROM_BASIC_LOADED"));
+ }
+ else WriteOut(MSG_Get("PROGRAM_LOADROM_UNRECOGNIZED"));
+ }
+ catch(...) {
+ return;
+ }
+ }
+};
+
+static void LOADROM_ProgramStart(Program * * make) {
+ *make=new LOADROM;
+}
+
+#if C_DEBUG
+class BIOSTEST : public Program {
+public:
+ void Run(void) {
+ if (!(cmd->FindCommand(1, temp_line))) {
+ WriteOut("Must specify BIOS file to load.\n");
+ return;
+ }
+
+ Bit8u drive;
+ char fullname[DOS_PATHLENGTH];
+ localDrive* ldp = 0;
+ if (!DOS_MakeName((char *)temp_line.c_str(), fullname, &drive)) return;
+
+ try {
+ /* try to read ROM file into buffer */
+ ldp = dynamic_cast<localDrive*>(Drives[drive]);
+ if (!ldp) return;
+
+ FILE *tmpfile = ldp->GetSystemFilePtr(fullname, "rb");
+ if (tmpfile == NULL) {
+ WriteOut("Can't open a file");
+ return;
+ }
+ fseek(tmpfile, 0L, SEEK_END);
+ if (ftell(tmpfile) > 64 * 1024) {
+ WriteOut("BIOS File too large");
+ fclose(tmpfile);
+ return;
+ }
+ fseek(tmpfile, 0L, SEEK_SET);
+ Bit8u buffer[64*1024];
+ Bitu data_read = fread(buffer, 1, sizeof( buffer), tmpfile);
+ fclose(tmpfile);
+
+ Bit32u rom_base = PhysMake(0xf000, 0); // override regular dosbox bios
+ /* write buffer into ROM */
+ for (Bitu i = 0; i < data_read; i++) phys_writeb(rom_base + i, buffer[i]);
+
+ //Start executing this bios
+ memset(&cpu_regs, 0, sizeof(cpu_regs));
+ memset(&Segs, 0, sizeof(Segs));
+
+
+ SegSet16(cs, 0xf000);
+ reg_eip = 0xfff0;
+ }
+ catch (...) {
+ return;
+ }
+ }
+};
+
+static void BIOSTEST_ProgramStart(Program * * make) {
+ *make = new BIOSTEST;
+}
+
+#endif
+
+// LOADFIX
+
+class LOADFIX : public Program {
+public:
+ void Run(void);
+};
+
+void LOADFIX::Run(void)
+{
+ Bit16u commandNr = 1;
+ Bit16u kb = 64;
+ if (cmd->FindCommand(commandNr,temp_line)) {
+ if (temp_line[0]=='-') {
+ char ch = temp_line[1];
+ if ((*upcase(&ch)=='D') || (*upcase(&ch)=='F')) {
+ // Deallocate all
+ DOS_FreeProcessMemory(0x40);
+ WriteOut(MSG_Get("PROGRAM_LOADFIX_DEALLOCALL"),kb);
+ return;
+ } else {
+ // Set mem amount to allocate
+ kb = atoi(temp_line.c_str()+1);
+ if (kb==0) kb=64;
+ commandNr++;
+ }
+ }
+ }
+ // Allocate Memory
+ Bit16u segment;
+ Bit16u blocks = kb*1024/16;
+ if (DOS_AllocateMemory(&segment,&blocks)) {
+ DOS_MCB mcb((Bit16u)(segment-1));
+ mcb.SetPSPSeg(0x40); // use fake segment
+ WriteOut(MSG_Get("PROGRAM_LOADFIX_ALLOC"),kb);
+ // Prepare commandline...
+ if (cmd->FindCommand(commandNr++,temp_line)) {
+ // get Filename
+ char filename[128];
+ safe_strncpy(filename,temp_line.c_str(),128);
+ // Setup commandline
+ bool ok;
+ char args[256];
+ args[0] = 0;
+ do {
+ ok = cmd->FindCommand(commandNr++,temp_line);
+ if(sizeof(args)-strlen(args)-1 < temp_line.length()+1)
+ break;
+ strcat(args,temp_line.c_str());
+ strcat(args," ");
+ } while (ok);
+ // Use shell to start program
+ DOS_Shell shell;
+ shell.Execute(filename,args);
+ DOS_FreeMemory(segment);
+ WriteOut(MSG_Get("PROGRAM_LOADFIX_DEALLOC"),kb);
+ }
+ } else {
+ WriteOut(MSG_Get("PROGRAM_LOADFIX_ERROR"),kb);
+ }
+}
+
+static void LOADFIX_ProgramStart(Program * * make) {
+ *make=new LOADFIX;
+}
+
+// RESCAN
+
+class RESCAN : public Program {
+public:
+ void Run(void);
+};
+
+void RESCAN::Run(void)
+{
+ bool all = false;
+
+ Bit8u drive = DOS_GetDefaultDrive();
+
+ if(cmd->FindCommand(1,temp_line)) {
+ //-A -All /A /All
+ if(temp_line.size() >= 2 && (temp_line[0] == '-' ||temp_line[0] =='/')&& (temp_line[1] == 'a' || temp_line[1] =='A') ) all = true;
+ else if(temp_line.size() == 2 && temp_line[1] == ':') {
+ lowcase(temp_line);
+ drive = temp_line[0] - 'a';
+ }
+ }
+ // Get current drive
+ if (all) {
+ for(Bitu i =0; i<DOS_DRIVES;i++) {
+ if (Drives[i]) Drives[i]->EmptyCache();
+ }
+ WriteOut(MSG_Get("PROGRAM_RESCAN_SUCCESS"));
+ } else {
+ if (drive < DOS_DRIVES && Drives[drive]) {
+ Drives[drive]->EmptyCache();
+ WriteOut(MSG_Get("PROGRAM_RESCAN_SUCCESS"));
+ }
+ }
+}
+
+static void RESCAN_ProgramStart(Program * * make) {
+ *make=new RESCAN;
+}
+
+class INTRO : public Program {
+public:
+ void DisplayMount(void) {
+ /* Basic mounting has a version for each operating system.
+ * This is done this way so both messages appear in the language file*/
+ WriteOut(MSG_Get("PROGRAM_INTRO_MOUNT_START"));
+#if (WIN32)
+ WriteOut(MSG_Get("PROGRAM_INTRO_MOUNT_WINDOWS"));
+#else
+ WriteOut(MSG_Get("PROGRAM_INTRO_MOUNT_OTHER"));
+#endif
+ WriteOut(MSG_Get("PROGRAM_INTRO_MOUNT_END"));
+ }
+
+ void Run(void) {
+ /* Only run if called from the first shell (Xcom TFTD runs any intro file in the path) */
+ if(DOS_PSP(dos.psp()).GetParent() != DOS_PSP(DOS_PSP(dos.psp()).GetParent()).GetParent()) return;
+ if(cmd->FindExist("cdrom",false)) {
+ WriteOut(MSG_Get("PROGRAM_INTRO_CDROM"));
+ return;
+ }
+ if(cmd->FindExist("mount",false)) {
+ WriteOut("\033[2J");//Clear screen before printing
+ DisplayMount();
+ return;
+ }
+ if(cmd->FindExist("special",false)) {
+ WriteOut(MSG_Get("PROGRAM_INTRO_SPECIAL"));
+ return;
+ }
+ /* Default action is to show all pages */
+ WriteOut(MSG_Get("PROGRAM_INTRO"));
+ Bit8u c;Bit16u n=1;
+ DOS_ReadFile (STDIN,&c,&n);
+ DisplayMount();
+ DOS_ReadFile (STDIN,&c,&n);
+ WriteOut(MSG_Get("PROGRAM_INTRO_CDROM"));
+ DOS_ReadFile (STDIN,&c,&n);
+ WriteOut(MSG_Get("PROGRAM_INTRO_SPECIAL"));
+ }
+};
+
+static void INTRO_ProgramStart(Program * * make) {
+ *make=new INTRO;
+}
+
+class IMGMOUNT : public Program {
+public:
+ void Run(void) {
+ //Hack To allow long commandlines
+ ChangeToLongCmd();
+ /* In secure mode don't allow people to change imgmount points.
+ * Neither mount nor unmount */
+ if(control->SecureMode()) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
+ return;
+ }
+ DOS_Drive * newdrive = NULL;
+ imageDisk * newImage = NULL;
+ Bit32u imagesize;
+ char drive;
+ std::string label;
+ std::vector<std::string> paths;
+ std::string umount;
+ /* Check for unmounting */
+ if (cmd->FindString("-u",umount,false)) {
+ WriteOut(UnmountHelper(umount[0]), toupper(umount[0]));
+ return;
+ }
+
+
+ std::string type="hdd";
+ std::string fstype="fat";
+ cmd->FindString("-t",type,true);
+ cmd->FindString("-fs",fstype,true);
+ if(type == "cdrom") type = "iso"; //Tiny hack for people who like to type -t cdrom
+ Bit8u mediaid;
+ if (type=="floppy" || type=="hdd" || type=="iso") {
+ Bit16u sizes[4];
+ bool imgsizedetect=false;
+
+ std::string str_size;
+ mediaid=0xF8;
+
+ if (type=="floppy") {
+ mediaid=0xF0;
+ } else if (type=="iso") {
+ //str_size="2048,1,65535,0"; // ignored, see drive_iso.cpp (AllocationInfo)
+ mediaid=0xF8;
+ fstype = "iso";
+ }
+ cmd->FindString("-size",str_size,true);
+ if ((type=="hdd") && (str_size.size()==0)) {
+ imgsizedetect=true;
+ } else {
+ char number[21] = { 0 };const char * scan = str_size.c_str();
+ Bitu index = 0;Bitu count = 0;
+ /* Parse the str_size string */
+ while (*scan && index < 20 && count < 4) {
+ if (*scan==',') {
+ number[index] = 0;
+ sizes[count++] = atoi(number);
+ index = 0;
+ } else number[index++] = *scan;
+ scan++;
+ }
+ if (count < 4) {
+ number[index] = 0; //always goes correct as index is max 20 at this point.
+ sizes[count] = atoi(number);
+ }
+ }
+
+ if(fstype=="fat" || fstype=="iso") {
+ // get the drive letter
+ if (!cmd->FindCommand(1,temp_line) || (temp_line.size() > 2) || ((temp_line.size()>1) && (temp_line[1]!=':'))) {
+ WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_DRIVE"));
+ return;
+ }
+ int i_drive = toupper(temp_line[0]);
+ if (!isalpha(i_drive) || (i_drive - 'A') >= DOS_DRIVES || (i_drive - 'A') <0) {
+ WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_DRIVE"));
+ return;
+ }
+ drive = static_cast<char>(i_drive);
+ } else if (fstype=="none") {
+ cmd->FindCommand(1,temp_line);
+ if ((temp_line.size() > 1) || (!isdigit(temp_line[0]))) {
+ WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY2"));
+ return;
+ }
+ drive=temp_line[0];
+ if ((drive<'0') || (drive>=(MAX_DISK_IMAGES+'0'))) {
+ WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY2"));
+ return;
+ }
+ } else {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FORMAT_UNSUPPORTED"),fstype.c_str());
+ return;
+ }
+
+ // find all file parameters, assuming that all option parameters have been removed
+ while(cmd->FindCommand((unsigned int)(paths.size() + 2), temp_line) && temp_line.size()) {
+
+ struct stat test;
+ if (stat(temp_line.c_str(),&test)) {
+ //See if it works if the ~ are written out
+ std::string homedir(temp_line);
+ Cross::ResolveHomedir(homedir);
+ if(!stat(homedir.c_str(),&test)) {
+ temp_line = homedir;
+ } else {
+ // convert dosbox filename to system filename
+ char fullname[CROSS_LEN];
+ char tmp[CROSS_LEN];
+ safe_strncpy(tmp, temp_line.c_str(), CROSS_LEN);
+
+ Bit8u dummy;
+ if (!DOS_MakeName(tmp, fullname, &dummy) || strncmp(Drives[dummy]->GetInfo(),"local directory",15)) {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_NON_LOCAL_DRIVE"));
+ return;
+ }
+
+ localDrive *ldp = dynamic_cast<localDrive*>(Drives[dummy]);
+ if (ldp==NULL) {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FILE_NOT_FOUND"));
+ return;
+ }
+ ldp->GetSystemFilename(tmp, fullname);
+ temp_line = tmp;
+
+ if (stat(temp_line.c_str(),&test)) {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FILE_NOT_FOUND"));
+ return;
+ }
+ }
+ }
+ if (S_ISDIR(test.st_mode)) {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_MOUNT"));
+ return;
+ }
+ paths.push_back(temp_line);
+ }
+ if (paths.size() == 0) {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_FILE"));
+ return;
+ }
+ if (paths.size() == 1)
+ temp_line = paths[0];
+
+ if(fstype=="fat") {
+ if (imgsizedetect) {
+ FILE * diskfile = fopen_wrap(temp_line.c_str(), "rb+");
+ if (!diskfile) {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE"));
+ return;
+ }
+ fseek(diskfile, 0L, SEEK_END);
+ Bit32u fcsize = (Bit32u)(ftell(diskfile) / 512L);
+ Bit8u buf[512];
+ fseek(diskfile, 0L, SEEK_SET);
+ if (fread(buf,sizeof(Bit8u),512,diskfile)<512) {
+ fclose(diskfile);
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE"));
+ return;
+ }
+ fclose(diskfile);
+ if ((buf[510]!=0x55) || (buf[511]!=0xaa)) {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_GEOMETRY"));
+ return;
+ }
+ Bitu sectors=(Bitu)(fcsize/(16*63));
+ if (sectors*16*63!=fcsize) {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_GEOMETRY"));
+ return;
+ }
+ sizes[0]=512; sizes[1]=63; sizes[2]=16; sizes[3]=sectors;
+ LOG_MSG("autosized image file: %d:%d:%d:%d",sizes[0],sizes[1],sizes[2],sizes[3]);
+ }
+
+ if (Drives[drive-'A']) {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED"));
+ return;
+ }
+
+ std::vector<DOS_Drive*> imgDisks;
+ std::vector<std::string>::size_type i;
+ std::vector<DOS_Drive*>::size_type ct;
+
+ for (i = 0; i < paths.size(); i++) {
+ DOS_Drive* newDrive = new fatDrive(paths[i].c_str(),sizes[0],sizes[1],sizes[2],sizes[3],0);
+ imgDisks.push_back(newDrive);
+ if(!(dynamic_cast<fatDrive*>(newDrive))->created_successfully) {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE"));
+ for(ct = 0; ct < imgDisks.size(); ct++) {
+ delete imgDisks[ct];
+ }
+ return;
+ }
+ }
+
+ // Update DriveManager
+ for(ct = 0; ct < imgDisks.size(); ct++) {
+ DriveManager::AppendDisk(drive - 'A', imgDisks[ct]);
+ }
+ DriveManager::InitializeDrive(drive - 'A');
+
+ // Set the correct media byte in the table
+ mem_writeb(Real2Phys(dos.tables.mediaid) + (drive - 'A') * 9, mediaid);
+
+ /* Command uses dta so set it to our internal dta */
+ RealPt save_dta = dos.dta();
+ dos.dta(dos.tables.tempdta);
+
+ for(ct = 0; ct < imgDisks.size(); ct++) {
+ DriveManager::CycleDisks(drive - 'A', (ct == (imgDisks.size() - 1)));
+
+ char root[7] = {drive,':','\\','*','.','*',0};
+ DOS_FindFirst(root, DOS_ATTR_VOLUME); // force obtaining the label and saving it in dirCache
+ }
+ dos.dta(save_dta);
+
+ std::string tmp(paths[0]);
+ for (i = 1; i < paths.size(); i++) {
+ tmp += "; " + paths[i];
+ }
+ WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"), drive, tmp.c_str());
+
+ if (paths.size() == 1) {
+ newdrive = imgDisks[0];
+ switch (drive - 'A') {
+ case 0:
+ case 1:
+ if(!((fatDrive *)newdrive)->loadedDisk->hardDrive) {
+ if(imageDiskList[drive - 'A'] != NULL) delete imageDiskList[drive - 'A'];
+ imageDiskList[drive - 'A'] = ((fatDrive *)newdrive)->loadedDisk;
+ }
+ break;
+ case 2:
+ case 3:
+ if(((fatDrive *)newdrive)->loadedDisk->hardDrive) {
+ if(imageDiskList[drive - 'A'] != NULL) delete imageDiskList[drive - 'A'];
+ imageDiskList[drive - 'A'] = ((fatDrive *)newdrive)->loadedDisk;
+ updateDPT();
+ }
+ break;
+ }
+ }
+ } else if (fstype=="iso") {
+
+ if (Drives[drive-'A']) {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED"));
+ return;
+ }
+ MSCDEX_SetCDInterface(CDROM_USE_SDL, -1);
+ // create new drives for all images
+ std::vector<DOS_Drive*> isoDisks;
+ std::vector<std::string>::size_type i;
+ std::vector<DOS_Drive*>::size_type ct;
+ for (i = 0; i < paths.size(); i++) {
+ int error = -1;
+ DOS_Drive* newDrive = new isoDrive(drive, paths[i].c_str(), mediaid, error);
+ isoDisks.push_back(newDrive);
+ switch (error) {
+ case 0 : break;
+ case 1 : WriteOut(MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS")); break;
+ case 2 : WriteOut(MSG_Get("MSCDEX_ERROR_NOT_SUPPORTED")); break;
+ case 3 : WriteOut(MSG_Get("MSCDEX_ERROR_OPEN")); break;
+ case 4 : WriteOut(MSG_Get("MSCDEX_TOO_MANY_DRIVES")); break;
+ case 5 : WriteOut(MSG_Get("MSCDEX_LIMITED_SUPPORT")); break;
+ case 6 : WriteOut(MSG_Get("MSCDEX_INVALID_FILEFORMAT")); break;
+ default : WriteOut(MSG_Get("MSCDEX_UNKNOWN_ERROR")); break;
+ }
+ // error: clean up and leave
+ if (error) {
+ for(ct = 0; ct < isoDisks.size(); ct++) {
+ delete isoDisks[ct];
+ }
+ return;
+ }
+ }
+ // Update DriveManager
+ for(ct = 0; ct < isoDisks.size(); ct++) {
+ DriveManager::AppendDisk(drive - 'A', isoDisks[ct]);
+ }
+ DriveManager::InitializeDrive(drive - 'A');
+
+ // Set the correct media byte in the table
+ mem_writeb(Real2Phys(dos.tables.mediaid) + (drive - 'A') * 9, mediaid);
+
+ // Print status message (success)
+ WriteOut(MSG_Get("MSCDEX_SUCCESS"));
+ std::string tmp(paths[0]);
+ for (i = 1; i < paths.size(); i++) {
+ tmp += "; " + paths[i];
+ }
+ WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"), drive, tmp.c_str());
+
+ } else {
+ FILE *newDisk = fopen_wrap(temp_line.c_str(), "rb+");
+ if (!newDisk) {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE"));
+ return;
+ }
+ fseek(newDisk,0L, SEEK_END);
+ imagesize = (ftell(newDisk) / 1024);
+
+ newImage = new imageDisk(newDisk, (Bit8u *)temp_line.c_str(), imagesize, (imagesize > 2880));
+ if(imagesize>2880) newImage->Set_Geometry(sizes[2],sizes[3],sizes[1],sizes[0]);
+ }
+ } else {
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_TYPE_UNSUPPORTED"),type.c_str());
+ return;
+ }
+
+ if (fstype=="none") {
+ if(imageDiskList[drive-'0'] != NULL) delete imageDiskList[drive-'0'];
+ imageDiskList[drive-'0'] = newImage;
+ updateDPT();
+ WriteOut(MSG_Get("PROGRAM_IMGMOUNT_MOUNT_NUMBER"),drive-'0',temp_line.c_str());
+ }
+
+ // check if volume label is given. be careful for cdrom
+ //if (cmd->FindString("-label",label,true)) newdrive->dirCache.SetLabel(label.c_str());
+ return;
+ }
+};
+
+void IMGMOUNT_ProgramStart(Program * * make) {
+ *make=new IMGMOUNT;
+}
+
+
+Bitu DOS_SwitchKeyboardLayout(const char* new_layout, Bit32s& tried_cp);
+Bitu DOS_LoadKeyboardLayout(const char * layoutname, Bit32s codepage, const char * codepagefile);
+const char* DOS_GetLoadedLayout(void);
+
+class KEYB : public Program {
+public:
+ void Run(void);
+};
+
+void KEYB::Run(void) {
+ if (cmd->FindCommand(1,temp_line)) {
+ if (cmd->FindString("?",temp_line,false)) {
+ WriteOut(MSG_Get("PROGRAM_KEYB_SHOWHELP"));
+ } else {
+ /* first parameter is layout ID */
+ Bitu keyb_error=0;
+ std::string cp_string;
+ Bit32s tried_cp = -1;
+ if (cmd->FindCommand(2,cp_string)) {
+ /* second parameter is codepage number */
+ tried_cp=atoi(cp_string.c_str());
+ char cp_file_name[256];
+ if (cmd->FindCommand(3,cp_string)) {
+ /* third parameter is codepage file */
+ strcpy(cp_file_name, cp_string.c_str());
+ } else {
+ /* no codepage file specified, use automatic selection */
+ strcpy(cp_file_name, "auto");
+ }
+
+ keyb_error=DOS_LoadKeyboardLayout(temp_line.c_str(), tried_cp, cp_file_name);
+ } else {
+ keyb_error=DOS_SwitchKeyboardLayout(temp_line.c_str(), tried_cp);
+ }
+ switch (keyb_error) {
+ case KEYB_NOERROR:
+ WriteOut(MSG_Get("PROGRAM_KEYB_NOERROR"),temp_line.c_str(),dos.loaded_codepage);
+ break;
+ case KEYB_FILENOTFOUND:
+ WriteOut(MSG_Get("PROGRAM_KEYB_FILENOTFOUND"),temp_line.c_str());
+ WriteOut(MSG_Get("PROGRAM_KEYB_SHOWHELP"));
+ break;
+ case KEYB_INVALIDFILE:
+ WriteOut(MSG_Get("PROGRAM_KEYB_INVALIDFILE"),temp_line.c_str());
+ break;
+ case KEYB_LAYOUTNOTFOUND:
+ WriteOut(MSG_Get("PROGRAM_KEYB_LAYOUTNOTFOUND"),temp_line.c_str(),tried_cp);
+ break;
+ case KEYB_INVALIDCPFILE:
+ WriteOut(MSG_Get("PROGRAM_KEYB_INVCPFILE"),temp_line.c_str());
+ WriteOut(MSG_Get("PROGRAM_KEYB_SHOWHELP"));
+ break;
+ default:
+ LOG(LOG_DOSMISC,LOG_ERROR)("KEYB:Invalid returncode %x",keyb_error);
+ break;
+ }
+ }
+ } else {
+ /* no parameter in the command line, just output codepage info and possibly loaded layout ID */
+ const char* layout_name = DOS_GetLoadedLayout();
+ if (layout_name==NULL) {
+ WriteOut(MSG_Get("PROGRAM_KEYB_INFO"),dos.loaded_codepage);
+ } else {
+ WriteOut(MSG_Get("PROGRAM_KEYB_INFO_LAYOUT"),dos.loaded_codepage,layout_name);
+ }
+ }
+}
+
+static void KEYB_ProgramStart(Program * * make) {
+ *make=new KEYB;
+}
+
+
+void DOS_SetupPrograms(void) {
+ /*Add Messages */
+
+ MSG_Add("PROGRAM_MOUNT_CDROMS_FOUND","CDROMs found: %d\n");
+ MSG_Add("PROGRAM_MOUNT_STATUS_FORMAT","%-5s %-58s %-12s\n");
+ MSG_Add("PROGRAM_MOUNT_STATUS_2","Drive %c is mounted as %s\n");
+ MSG_Add("PROGRAM_MOUNT_STATUS_1","The currently mounted drives are:\n");
+ MSG_Add("PROGRAM_MOUNT_ERROR_1","Directory %s doesn't exist.\n");
+ MSG_Add("PROGRAM_MOUNT_ERROR_2","%s isn't a directory\n");
+ MSG_Add("PROGRAM_MOUNT_ILL_TYPE","Illegal type %s\n");
+ MSG_Add("PROGRAM_MOUNT_ALREADY_MOUNTED","Drive %c already mounted with %s\n");
+ MSG_Add("PROGRAM_MOUNT_USAGE",
+ "Usage \033[34;1mMOUNT Drive-Letter Local-Directory\033[0m\n"
+ "For example: MOUNT c %s\n"
+ "This makes the directory %s act as the C: drive inside DOSBox.\n"
+ "The directory has to exist.\n");
+ MSG_Add("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED","Drive %c isn't mounted.\n");
+ MSG_Add("PROGRAM_MOUNT_UMOUNT_SUCCESS","Drive %c has successfully been removed.\n");
+ MSG_Add("PROGRAM_MOUNT_UMOUNT_NO_VIRTUAL","Virtual Drives can not be unMOUNTed.\n");
+ MSG_Add("PROGRAM_MOUNT_WARNING_WIN","\033[31;1mMounting c:\\ is NOT recommended. Please mount a (sub)directory next time.\033[0m\n");
+ MSG_Add("PROGRAM_MOUNT_WARNING_OTHER","\033[31;1mMounting / is NOT recommended. Please mount a (sub)directory next time.\033[0m\n");
+
+ MSG_Add("PROGRAM_MEM_CONVEN","%10d Kb free conventional memory\n");
+ MSG_Add("PROGRAM_MEM_EXTEND","%10d Kb free extended memory\n");
+ MSG_Add("PROGRAM_MEM_EXPAND","%10d Kb free expanded memory\n");
+ MSG_Add("PROGRAM_MEM_UPPER","%10d Kb free upper memory in %d blocks (largest UMB %d Kb)\n");
+
+ MSG_Add("PROGRAM_LOADFIX_ALLOC","%d kb allocated.\n");
+ MSG_Add("PROGRAM_LOADFIX_DEALLOC","%d kb freed.\n");
+ MSG_Add("PROGRAM_LOADFIX_DEALLOCALL","Used memory freed.\n");
+ MSG_Add("PROGRAM_LOADFIX_ERROR","Memory allocation error.\n");
+
+ MSG_Add("MSCDEX_SUCCESS","MSCDEX installed.\n");
+ MSG_Add("MSCDEX_ERROR_MULTIPLE_CDROMS","MSCDEX: Failure: Drive-letters of multiple CD-ROM drives have to be continuous.\n");
+ MSG_Add("MSCDEX_ERROR_NOT_SUPPORTED","MSCDEX: Failure: Not yet supported.\n");
+ MSG_Add("MSCDEX_ERROR_PATH","MSCDEX: Specified location is not a CD-ROM drive.\n");
+ MSG_Add("MSCDEX_ERROR_OPEN","MSCDEX: Failure: Invalid file or unable to open.\n");
+ MSG_Add("MSCDEX_TOO_MANY_DRIVES","MSCDEX: Failure: Too many CD-ROM drives (max: 5). MSCDEX Installation failed.\n");
+ MSG_Add("MSCDEX_LIMITED_SUPPORT","MSCDEX: Mounted subdirectory: limited support.\n");
+ MSG_Add("MSCDEX_INVALID_FILEFORMAT","MSCDEX: Failure: File is either no ISO/CUE image or contains errors.\n");
+ MSG_Add("MSCDEX_UNKNOWN_ERROR","MSCDEX: Failure: Unknown error.\n");
+
+ MSG_Add("PROGRAM_RESCAN_SUCCESS","Drive cache cleared.\n");
+
+ MSG_Add("PROGRAM_INTRO",
+ "\033[2J\033[32;1mWelcome to DOSBox\033[0m, an x86 emulator with sound and graphics.\n"
+ "DOSBox creates a shell for you which looks like old plain DOS.\n"
+ "\n"
+ "For information about basic mount type \033[34;1mintro mount\033[0m\n"
+ "For information about CD-ROM support type \033[34;1mintro cdrom\033[0m\n"
+ "For information about special keys type \033[34;1mintro special\033[0m\n"
+ "For more information about DOSBox, go to \033[34;1mhttp://www.dosbox.com/wiki\033[0m\n"
+ "\n"
+ "\033[31;1mDOSBox will stop/exit without a warning if an error occurred!\033[0m\n"
+ "\n"
+ "\n"
+ );
+ MSG_Add("PROGRAM_INTRO_MOUNT_START",
+ "\033[32;1mHere are some commands to get you started:\033[0m\n"
+ "Before you can use the files located on your own filesystem,\n"
+ "You have to mount the directory containing the files.\n"
+ "\n"
+ );
+ MSG_Add("PROGRAM_INTRO_MOUNT_WINDOWS",
+ "\033[44;1m\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\n"
+ "\xBA \033[32mmount c c:\\dosgames\\\033[37m will create a C drive with c:\\dosgames as contents.\xBA\n"
+ "\xBA \xBA\n"
+ "\xBA \033[32mc:\\dosgames\\\033[37m is an example. Replace it with your own games directory. \033[37m \xBA\n"
+ "\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\033[0m\n"
+ );
+ MSG_Add("PROGRAM_INTRO_MOUNT_OTHER",
+ "\033[44;1m\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\n"
+ "\xBA \033[32mmount c ~/dosgames\033[37m will create a C drive with ~/dosgames as contents.\xBA\n"
+ "\xBA \xBA\n"
+ "\xBA \033[32m~/dosgames\033[37m is an example. Replace it with your own games directory.\033[37m \xBA\n"
+ "\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\033[0m\n"
+ );
+ MSG_Add("PROGRAM_INTRO_MOUNT_END",
+ "When the mount has successfully completed you can type \033[34;1mc:\033[0m to go to your freshly\n"
+ "mounted C-drive. Typing \033[34;1mdir\033[0m there will show its contents."
+ " \033[34;1mcd\033[0m will allow you to\n"
+ "enter a directory (recognised by the \033[33;1m[]\033[0m in a directory listing).\n"
+ "You can run programs/files which end with \033[31m.exe .bat\033[0m and \033[31m.com\033[0m.\n"
+ );
+ MSG_Add("PROGRAM_INTRO_CDROM",
+ "\033[2J\033[32;1mHow to mount a Real/Virtual CD-ROM Drive in DOSBox:\033[0m\n"
+ "DOSBox provides CD-ROM emulation on several levels.\n"
+ "\n"
+ "The \033[33mbasic\033[0m level works on all CD-ROM drives and normal directories.\n"
+ "It installs MSCDEX and marks the files read-only.\n"
+ "Usually this is enough for most games:\n"
+ "\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom\033[0m or \033[34;1mmount d C:\\example -t cdrom\033[0m\n"
+ "If it doesn't work you might have to tell DOSBox the label of the CD-ROM:\n"
+ "\033[34;1mmount d C:\\example -t cdrom -label CDLABEL\033[0m\n"
+ "\n"
+ "The \033[33mnext\033[0m level adds some low-level support.\n"
+ "Therefore only works on CD-ROM drives:\n"
+ "\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom -usecd \033[33m0\033[0m\n"
+ "\n"
+ "The \033[33mlast\033[0m level of support depends on your Operating System:\n"
+ "For \033[1mWindows 2000\033[0m, \033[1mWindows XP\033[0m and \033[1mLinux\033[0m:\n"
+ "\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom -usecd \033[33m0 \033[34m-ioctl\033[0m\n"
+ "For \033[1mWindows 9x\033[0m with a ASPI layer installed:\n"
+ "\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom -usecd \033[33m0 \033[34m-aspi\033[0m\n"
+ "\n"
+ "Replace \033[0;31mD:\\\033[0m with the location of your CD-ROM.\n"
+ "Replace the \033[33;1m0\033[0m in \033[34;1m-usecd \033[33m0\033[0m with the number reported for your CD-ROM if you type:\n"
+ "\033[34;1mmount -cd\033[0m\n"
+ );
+ MSG_Add("PROGRAM_INTRO_SPECIAL",
+ "\033[2J\033[32;1mSpecial keys:\033[0m\n"
+ "These are the default keybindings.\n"
+ "They can be changed in the \033[33mkeymapper\033[0m.\n"
+ "\n"
+ "\033[33;1mALT-ENTER\033[0m : Go full screen and back.\n"
+ "\033[33;1mALT-PAUSE\033[0m : Pause DOSBox.\n"
+ "\033[33;1mCTRL-F1\033[0m : Start the \033[33mkeymapper\033[0m.\n"
+ "\033[33;1mCTRL-F4\033[0m : Update directory cache for all drives! Swap mounted disk-image.\n"
+ "\033[33;1mCTRL-ALT-F5\033[0m : Start/Stop creating a movie of the screen.\n"
+ "\033[33;1mCTRL-F5\033[0m : Save a screenshot.\n"
+ "\033[33;1mCTRL-F6\033[0m : Start/Stop recording sound output to a wave file.\n"
+ "\033[33;1mCTRL-ALT-F7\033[0m : Start/Stop recording of OPL commands.\n"
+ "\033[33;1mCTRL-ALT-F8\033[0m : Start/Stop the recording of raw MIDI commands.\n"
+ "\033[33;1mCTRL-F7\033[0m : Decrease frameskip.\n"
+ "\033[33;1mCTRL-F8\033[0m : Increase frameskip.\n"
+ "\033[33;1mCTRL-F9\033[0m : Kill DOSBox.\n"
+ "\033[33;1mCTRL-F10\033[0m : Capture/Release the mouse.\n"
+ "\033[33;1mCTRL-F11\033[0m : Slow down emulation (Decrease DOSBox Cycles).\n"
+ "\033[33;1mCTRL-F12\033[0m : Speed up emulation (Increase DOSBox Cycles).\n"
+ "\033[33;1mALT-F12\033[0m : Unlock speed (turbo button/fast forward).\n"
+ );
+ MSG_Add("PROGRAM_BOOT_NOT_EXIST","Bootdisk file does not exist. Failing.\n");
+ MSG_Add("PROGRAM_BOOT_NOT_OPEN","Cannot open bootdisk file. Failing.\n");
+ MSG_Add("PROGRAM_BOOT_WRITE_PROTECTED","Image file is read-only! Might create problems.\n");
+ MSG_Add("PROGRAM_BOOT_PRINT_ERROR","This command boots DOSBox from either a floppy or hard disk image.\n\n"
+ "For this command, one can specify a succession of floppy disks swappable\n"
+ "by pressing Ctrl-F4, and -l specifies the mounted drive to boot from. If\n"
+ "no drive letter is specified, this defaults to booting from the A drive.\n"
+ "The only bootable drive letters are A, C, and D. For booting from a hard\n"
+ "drive (C or D), the image should have already been mounted using the\n"
+ "\033[34;1mIMGMOUNT\033[0m command.\n\n"
+ "The syntax of this command is:\n\n"
+ "\033[34;1mBOOT [diskimg1.img diskimg2.img] [-l driveletter]\033[0m\n"
+ );
+ MSG_Add("PROGRAM_BOOT_UNABLE","Unable to boot off of drive %c");
+ MSG_Add("PROGRAM_BOOT_IMAGE_OPEN","Opening image file: %s\n");
+ MSG_Add("PROGRAM_BOOT_IMAGE_NOT_OPEN","Cannot open %s");
+ MSG_Add("PROGRAM_BOOT_BOOT","Booting from drive %c...\n");
+ MSG_Add("PROGRAM_BOOT_CART_WO_PCJR","PCjr cartridge found, but machine is not PCjr");
+ MSG_Add("PROGRAM_BOOT_CART_LIST_CMDS","Available PCjr cartridge commandos:%s");
+ MSG_Add("PROGRAM_BOOT_CART_NO_CMDS","No PCjr cartridge commandos found");
+
+ MSG_Add("PROGRAM_LOADROM_SPECIFY_FILE","Must specify ROM file to load.\n");
+ MSG_Add("PROGRAM_LOADROM_CANT_OPEN","ROM file not accessible.\n");
+ MSG_Add("PROGRAM_LOADROM_TOO_LARGE","ROM file too large.\n");
+ MSG_Add("PROGRAM_LOADROM_INCOMPATIBLE","Video BIOS not supported by machine type.\n");
+ MSG_Add("PROGRAM_LOADROM_UNRECOGNIZED","ROM file not recognized.\n");
+ MSG_Add("PROGRAM_LOADROM_BASIC_LOADED","BASIC ROM loaded.\n");
+
+ MSG_Add("PROGRAM_IMGMOUNT_SPECIFY_DRIVE","Must specify drive letter to mount image at.\n");
+ MSG_Add("PROGRAM_IMGMOUNT_SPECIFY2","Must specify drive number (0 or 3) to mount image at (0,1=fda,fdb;2,3=hda,hdb).\n");
+ MSG_Add("PROGRAM_IMGMOUNT_SPECIFY_GEOMETRY",
+ "For \033[33mCD-ROM\033[0m images: \033[34;1mIMGMOUNT drive-letter location-of-image -t iso\033[0m\n"
+ "\n"
+ "For \033[33mhardrive\033[0m images: Must specify drive geometry for hard drives:\n"
+ "bytes_per_sector, sectors_per_cylinder, heads_per_cylinder, cylinder_count.\n"
+ "\033[34;1mIMGMOUNT drive-letter location-of-image -size bps,spc,hpc,cyl\033[0m\n");
+ MSG_Add("PROGRAM_IMGMOUNT_INVALID_IMAGE","Could not load image file.\n"
+ "Check that the path is correct and the image is accessible.\n");
+ MSG_Add("PROGRAM_IMGMOUNT_INVALID_GEOMETRY","Could not extract drive geometry from image.\n"
+ "Use parameter -size bps,spc,hpc,cyl to specify the geometry.\n");
+ MSG_Add("PROGRAM_IMGMOUNT_TYPE_UNSUPPORTED","Type \"%s\" is unsupported. Specify \"hdd\" or \"floppy\" or\"iso\".\n");
+ MSG_Add("PROGRAM_IMGMOUNT_FORMAT_UNSUPPORTED","Format \"%s\" is unsupported. Specify \"fat\" or \"iso\" or \"none\".\n");
+ MSG_Add("PROGRAM_IMGMOUNT_SPECIFY_FILE","Must specify file-image to mount.\n");
+ MSG_Add("PROGRAM_IMGMOUNT_FILE_NOT_FOUND","Image file not found.\n");
+ MSG_Add("PROGRAM_IMGMOUNT_MOUNT","To mount directories, use the \033[34;1mMOUNT\033[0m command, not the \033[34;1mIMGMOUNT\033[0m command.\n");
+ MSG_Add("PROGRAM_IMGMOUNT_ALREADY_MOUNTED","Drive already mounted at that letter.\n");
+ MSG_Add("PROGRAM_IMGMOUNT_CANT_CREATE","Can't create drive from file.\n");
+ MSG_Add("PROGRAM_IMGMOUNT_MOUNT_NUMBER","Drive number %d mounted as %s\n");
+ MSG_Add("PROGRAM_IMGMOUNT_NON_LOCAL_DRIVE", "The image must be on a host or local drive.\n");
+ MSG_Add("PROGRAM_IMGMOUNT_MULTIPLE_NON_CUEISO_FILES", "Using multiple files is only supported for cue/iso images.\n");
+
+ MSG_Add("PROGRAM_KEYB_INFO","Codepage %i has been loaded\n");
+ MSG_Add("PROGRAM_KEYB_INFO_LAYOUT","Codepage %i has been loaded for layout %s\n");
+ MSG_Add("PROGRAM_KEYB_SHOWHELP",
+ "\033[32;1mKEYB\033[0m [keyboard layout ID[ codepage number[ codepage file]]]\n\n"
+ "Some examples:\n"
+ " \033[32;1mKEYB\033[0m: Display currently loaded codepage.\n"
+ " \033[32;1mKEYB\033[0m sp: Load the spanish (SP) layout, use an appropriate codepage.\n"
+ " \033[32;1mKEYB\033[0m sp 850: Load the spanish (SP) layout, use codepage 850.\n"
+ " \033[32;1mKEYB\033[0m sp 850 mycp.cpi: Same as above, but use file mycp.cpi.\n");
+ MSG_Add("PROGRAM_KEYB_NOERROR","Keyboard layout %s loaded for codepage %i\n");
+ MSG_Add("PROGRAM_KEYB_FILENOTFOUND","Keyboard file %s not found\n\n");
+ MSG_Add("PROGRAM_KEYB_INVALIDFILE","Keyboard file %s invalid\n");
+ MSG_Add("PROGRAM_KEYB_LAYOUTNOTFOUND","No layout in %s for codepage %i\n");
+ MSG_Add("PROGRAM_KEYB_INVCPFILE","None or invalid codepage file for layout %s\n\n");
+
+ /*regular setup*/
+ PROGRAMS_MakeFile("MOUNT.COM",MOUNT_ProgramStart);
+ PROGRAMS_MakeFile("MEM.COM",MEM_ProgramStart);
+ PROGRAMS_MakeFile("LOADFIX.COM",LOADFIX_ProgramStart);
+ PROGRAMS_MakeFile("RESCAN.COM",RESCAN_ProgramStart);
+ PROGRAMS_MakeFile("INTRO.COM",INTRO_ProgramStart);
+ PROGRAMS_MakeFile("BOOT.COM",BOOT_ProgramStart);
+#if C_DEBUG
+ PROGRAMS_MakeFile("BIOSTEST.COM", BIOSTEST_ProgramStart);
+#endif
+ PROGRAMS_MakeFile("LOADROM.COM", LOADROM_ProgramStart);
+ PROGRAMS_MakeFile("IMGMOUNT.COM", IMGMOUNT_ProgramStart);
+ PROGRAMS_MakeFile("KEYB.COM", KEYB_ProgramStart);
+
+}
diff --git a/src/dos/dos_tables.cpp b/src/dos/dos_tables.cpp
new file mode 100644
index 000000000..6ed87c46b
--- /dev/null
+++ b/src/dos/dos_tables.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "mem.h"
+#include "dos_inc.h"
+#include "callback.h"
+
+#ifdef _MSC_VER
+#pragma pack(1)
+#endif
+struct DOS_TableCase {
+ Bit16u size;
+ Bit8u chars[256];
+}
+GCC_ATTRIBUTE (packed);
+#ifdef _MSC_VER
+#pragma pack ()
+#endif
+
+RealPt DOS_TableUpCase;
+RealPt DOS_TableLowCase;
+
+static Bitu call_casemap;
+
+static Bit16u dos_memseg=DOS_PRIVATE_SEGMENT;
+
+Bit16u DOS_GetMemory(Bit16u pages) {
+ if ((Bitu)pages+(Bitu)dos_memseg>=DOS_PRIVATE_SEGMENT_END) {
+ E_Exit("DOS:Not enough memory for internal tables");
+ }
+ Bit16u page=dos_memseg;
+ dos_memseg+=pages;
+ return page;
+}
+
+static Bitu DOS_CaseMapFunc(void) {
+ //LOG(LOG_DOSMISC,LOG_ERROR)("Case map routine called : %c",reg_al);
+ return CBRET_NONE;
+}
+
+static Bit8u country_info[0x22] = {
+/* Date format */ 0x00, 0x00,
+/* Currencystring */ 0x24, 0x00, 0x00, 0x00, 0x00,
+/* Thousands sep */ 0x2c, 0x00,
+/* Decimal sep */ 0x2e, 0x00,
+/* Date sep */ 0x2d, 0x00,
+/* time sep */ 0x3a, 0x00,
+/* currency form */ 0x00,
+/* digits after dec */ 0x02,
+/* Time format */ 0x00,
+/* Casemap */ 0x00, 0x00, 0x00, 0x00,
+/* Data sep */ 0x2c, 0x00,
+/* Reservered 5 */ 0x00, 0x00, 0x00, 0x00, 0x00,
+/* Reservered 5 */ 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+void DOS_SetupTables(void) {
+ Bit16u seg;Bitu i;
+ dos.tables.tempdta=RealMake(DOS_GetMemory(4),0);
+ dos.tables.tempdta_fcbdelete=RealMake(DOS_GetMemory(4),0);
+ /* Create the DOS Info Block */
+ dos_infoblock.SetLocation(DOS_INFOBLOCK_SEG); //c2woody
+
+ /* create SDA */
+ DOS_SDA(DOS_SDA_SEG,0).Init();
+
+ /* Some weird files >20 detection routine */
+ /* Possibly obselete when SFT is properly handled */
+ real_writed(DOS_CONSTRING_SEG,0x0a,0x204e4f43);
+ real_writed(DOS_CONSTRING_SEG,0x1a,0x204e4f43);
+ real_writed(DOS_CONSTRING_SEG,0x2a,0x204e4f43);
+
+ /* create a CON device driver */
+ seg=DOS_CONDRV_SEG;
+ real_writed(seg,0x00,0xffffffff); // next ptr
+ real_writew(seg,0x04,0x8013); // attributes
+ real_writed(seg,0x06,0xffffffff); // strategy routine
+ real_writed(seg,0x0a,0x204e4f43); // driver name
+ real_writed(seg,0x0e,0x20202020); // driver name
+ dos_infoblock.SetDeviceChainStart(RealMake(seg,0));
+
+ /* Create a fake Current Directory Structure */
+ seg=DOS_CDS_SEG;
+ real_writed(seg,0x00,0x005c3a43);
+ dos_infoblock.SetCurDirStruct(RealMake(seg,0));
+
+
+
+ /* Allocate DCBS DOUBLE BYTE CHARACTER SET LEAD-BYTE TABLE */
+ dos.tables.dbcs=RealMake(DOS_GetMemory(12),0);
+ mem_writed(Real2Phys(dos.tables.dbcs),0); //empty table
+ /* FILENAME CHARACTER TABLE */
+ dos.tables.filenamechar=RealMake(DOS_GetMemory(2),0);
+ mem_writew(Real2Phys(dos.tables.filenamechar)+0x00,0x16);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x02,0x01);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x03,0x00); // allowed chars from
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x04,0xff); // ...to
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x05,0x00);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x06,0x00); // excluded chars from
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x07,0x20); // ...to
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x08,0x02);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x09,0x0e); // number of illegal separators
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x0a,0x2e);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x0b,0x22);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x0c,0x2f);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x0d,0x5c);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x0e,0x5b);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x0f,0x5d);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x10,0x3a);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x11,0x7c);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x12,0x3c);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x13,0x3e);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x14,0x2b);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x15,0x3d);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x16,0x3b);
+ mem_writeb(Real2Phys(dos.tables.filenamechar)+0x17,0x2c);
+ /* COLLATING SEQUENCE TABLE + UPCASE TABLE*/
+ // 256 bytes for col table, 128 for upcase, 4 for number of entries
+ dos.tables.collatingseq=RealMake(DOS_GetMemory(25),0);
+ mem_writew(Real2Phys(dos.tables.collatingseq),0x100);
+ for (i=0; i<256; i++) mem_writeb(Real2Phys(dos.tables.collatingseq)+i+2,i);
+ dos.tables.upcase=dos.tables.collatingseq+258;
+ mem_writew(Real2Phys(dos.tables.upcase),0x80);
+ for (i=0; i<128; i++) mem_writeb(Real2Phys(dos.tables.upcase)+i+2,0x80+i);
+
+
+ /* Create a fake FCB SFT */
+ seg=DOS_GetMemory(4);
+ real_writed(seg,0,0xffffffff); //Last File Table
+ real_writew(seg,4,100); //File Table supports 100 files
+ dos_infoblock.SetFCBTable(RealMake(seg,0));
+
+ /* Create a fake DPB */
+ dos.tables.dpb=DOS_GetMemory(16);
+ dos.tables.mediaid=RealMake(dos.tables.dpb,0x17); //Media ID offset in DPB
+ for (i=0;i<DOS_DRIVES;i++) {
+ real_writeb(dos.tables.dpb,i*9,i); // drive number
+ real_writeb(dos.tables.dpb,i*9+1,i); // unit number
+ real_writew(dos.tables.dpb,i*9+2,0x0200); // bytes per sector
+ mem_writew(Real2Phys(dos.tables.mediaid)+i*9,0);
+ }
+
+ /* Create a fake disk buffer head */
+ seg=DOS_GetMemory(6);
+ for (Bitu ct=0; ct<0x20; ct++) real_writeb(seg,ct,0);
+ real_writew(seg,0x00,0xffff); // forward ptr
+ real_writew(seg,0x02,0xffff); // backward ptr
+ real_writeb(seg,0x04,0xff); // not in use
+ real_writeb(seg,0x0a,0x01); // number of FATs
+ real_writed(seg,0x0d,0xffffffff); // pointer to DPB
+ dos_infoblock.SetDiskBufferHeadPt(RealMake(seg,0));
+
+ /* Set buffers to a nice value */
+ dos_infoblock.SetBuffers(50,50);
+
+ /* case map routine INT 0x21 0x38 */
+ call_casemap = CALLBACK_Allocate();
+ CALLBACK_Setup(call_casemap,DOS_CaseMapFunc,CB_RETF,"DOS CaseMap");
+ /* Add it to country structure */
+ host_writed(country_info + 0x12, CALLBACK_RealPointer(call_casemap));
+ dos.tables.country=country_info;
+}
diff --git a/src/dos/drive_cache.cpp b/src/dos/drive_cache.cpp
new file mode 100644
index 000000000..e0500d30f
--- /dev/null
+++ b/src/dos/drive_cache.cpp
@@ -0,0 +1,947 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "drives.h"
+#include "dos_inc.h"
+#include "support.h"
+#include "cross.h"
+
+// STL stuff
+#include <vector>
+#include <iterator>
+#include <algorithm>
+
+#if defined (WIN32) /* Win 32 */
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from
+#include <windows.h>
+#endif
+
+#if defined (OS2)
+#define INCL_DOSERRORS
+#define INCL_DOSFILEMGR
+#include <os2.h>
+#endif
+
+int fileInfoCounter = 0;
+
+bool SortByName(DOS_Drive_Cache::CFileInfo* const &a, DOS_Drive_Cache::CFileInfo* const &b) {
+ return strcmp(a->shortname,b->shortname)<0;
+}
+
+bool SortByNameRev(DOS_Drive_Cache::CFileInfo* const &a, DOS_Drive_Cache::CFileInfo* const &b) {
+ return strcmp(a->shortname,b->shortname)>0;
+}
+
+bool SortByDirName(DOS_Drive_Cache::CFileInfo* const &a, DOS_Drive_Cache::CFileInfo* const &b) {
+ // Directories first...
+ if (a->isDir!=b->isDir) return (a->isDir>b->isDir);
+ return strcmp(a->shortname,b->shortname)<0;
+}
+
+bool SortByDirNameRev(DOS_Drive_Cache::CFileInfo* const &a, DOS_Drive_Cache::CFileInfo* const &b) {
+ // Directories first...
+ if (a->isDir!=b->isDir) return (a->isDir>b->isDir);
+ return strcmp(a->shortname,b->shortname)>0;
+}
+
+DOS_Drive_Cache::DOS_Drive_Cache(void) {
+ dirBase = new CFileInfo;
+ save_dir = 0;
+ srchNr = 0;
+ label[0] = 0;
+ nextFreeFindFirst = 0;
+ for (Bit32u i=0; i<MAX_OPENDIRS; i++) { dirSearch[i] = 0; dirFindFirst[i] = 0; };
+ SetDirSort(DIRALPHABETICAL);
+ updatelabel = true;
+}
+
+DOS_Drive_Cache::DOS_Drive_Cache(const char* path) {
+ dirBase = new CFileInfo;
+ save_dir = 0;
+ srchNr = 0;
+ label[0] = 0;
+ nextFreeFindFirst = 0;
+ for (Bit32u i=0; i<MAX_OPENDIRS; i++) { dirSearch[i] = 0; dirFindFirst[i] = 0; };
+ SetDirSort(DIRALPHABETICAL);
+ SetBaseDir(path);
+ updatelabel = true;
+}
+
+DOS_Drive_Cache::~DOS_Drive_Cache(void) {
+ Clear();
+ for (Bit32u i=0; i<MAX_OPENDIRS; i++) { DeleteFileInfo(dirFindFirst[i]); dirFindFirst[i]=0; };
+}
+
+void DOS_Drive_Cache::Clear(void) {
+ DeleteFileInfo(dirBase); dirBase = 0;
+ nextFreeFindFirst = 0;
+ for (Bit32u i=0; i<MAX_OPENDIRS; i++) dirSearch[i] = 0;
+}
+
+void DOS_Drive_Cache::EmptyCache(void) {
+ // Empty Cache and reinit
+ Clear();
+ dirBase = new CFileInfo;
+ save_dir = 0;
+ srchNr = 0;
+ SetBaseDir(basePath);
+}
+
+void DOS_Drive_Cache::SetLabel(const char* vname,bool cdrom,bool allowupdate) {
+/* allowupdate defaults to true. if mount sets a label then allowupdate is
+ * false and will this function return at once after the first call.
+ * The label will be set at the first call. */
+
+ if(!this->updatelabel) return;
+ this->updatelabel = allowupdate;
+ Set_Label(vname,label,cdrom);
+ LOG(LOG_DOSMISC,LOG_NORMAL)("DIRCACHE: Set volume label to %s",label);
+}
+
+Bit16u DOS_Drive_Cache::GetFreeID(CFileInfo* dir) {
+ if (dir->id != MAX_OPENDIRS)
+ return dir->id;
+ for (Bit16u i=0; i<MAX_OPENDIRS; i++) {
+ if (!dirSearch[i]) {
+ dir->id = i;
+ return i;
+ }
+ }
+ LOG(LOG_FILES,LOG_NORMAL)("DIRCACHE: Too many open directories!");
+ dir->id=0;
+ return 0;
+}
+
+void DOS_Drive_Cache::SetBaseDir(const char* baseDir) {
+ Bit16u id;
+ strcpy(basePath,baseDir);
+ if (OpenDir(baseDir,id)) {
+ char* result = 0;
+ ReadDir(id,result);
+ };
+ // Get Volume Label
+#if defined (WIN32) || defined (OS2)
+ bool cdrom = false;
+ char labellocal[256]={ 0 };
+ char drive[4] = "C:\\";
+ drive[0] = basePath[0];
+#if defined (WIN32)
+ if (GetVolumeInformation(drive,labellocal,256,NULL,NULL,NULL,NULL,0)) {
+ UINT test = GetDriveType(drive);
+ if(test == DRIVE_CDROM) cdrom = true;
+#else // OS2
+ //TODO determine wether cdrom or not!
+ FSINFO fsinfo;
+ ULONG drivenumber = drive[0];
+ if (drivenumber > 26) { // drive letter was lowercase
+ drivenumber = drive[0] - 'a' + 1;
+ }
+ APIRET rc = DosQueryFSInfo(drivenumber, FSIL_VOLSER, &fsinfo, sizeof(FSINFO));
+ if (rc == NO_ERROR) {
+#endif
+ /* Set label and allow being updated */
+ SetLabel(labellocal,cdrom,true);
+ }
+#endif
+}
+
+void DOS_Drive_Cache::ExpandName(char* path) {
+ strcpy(path,GetExpandName(path));
+}
+
+char* DOS_Drive_Cache::GetExpandName(const char* path) {
+ static char work [CROSS_LEN] = { 0 };
+ char dir [CROSS_LEN];
+
+ work[0] = 0;
+ strcpy (dir,path);
+
+ const char* pos = strrchr(path,CROSS_FILESPLIT);
+
+ if (pos) dir[pos-path+1] = 0;
+ CFileInfo* dirInfo = FindDirInfo(dir, work);
+
+ if (pos) {
+ // Last Entry = File
+ strcpy(dir,pos+1);
+ GetLongName(dirInfo, dir);
+ strcat(work,dir);
+ }
+
+ if (*work) {
+ size_t len = strlen(work);
+#if defined (WIN32)
+ //What about OS/2
+ if((work[len-1] == CROSS_FILESPLIT ) && (len >= 2) && (work[len-2] != ':')) {
+#else
+ if((len > 1) && (work[len-1] == CROSS_FILESPLIT )) {
+#endif
+ work[len-1] = 0; // Remove trailing slashes except when in root
+ }
+ }
+ return work;
+}
+
+void DOS_Drive_Cache::AddEntry(const char* path, bool checkExists) {
+ // Get Last part...
+ char file [CROSS_LEN];
+ char expand [CROSS_LEN];
+
+ CFileInfo* dir = FindDirInfo(path,expand);
+ const char* pos = strrchr(path,CROSS_FILESPLIT);
+
+ if (pos) {
+ strcpy(file,pos+1);
+ // Check if file already exists, then don't add new entry...
+ if (checkExists) {
+ if (GetLongName(dir,file)>=0) return;
+ }
+
+ CreateEntry(dir,file,false);
+
+ Bits index = GetLongName(dir,file);
+ if (index>=0) {
+ Bit32u i;
+ // Check if there are any open search dir that are affected by this...
+ if (dir) for (i=0; i<MAX_OPENDIRS; i++) {
+ if ((dirSearch[i]==dir) && ((Bit32u)index<=dirSearch[i]->nextEntry))
+ dirSearch[i]->nextEntry++;
+ }
+ }
+ // LOG_DEBUG("DIR: Added Entry %s",path);
+ } else {
+// LOG_DEBUG("DIR: Error: Failed to add %s",path);
+ }
+}
+void DOS_Drive_Cache::AddEntryDirOverlay(const char* path, bool checkExists) {
+ // Get Last part...
+ char file [CROSS_LEN];
+ char expand [CROSS_LEN];
+ char dironly[CROSS_LEN + 1];
+
+ //When adding a directory, the directory we want to operate inside in is the above it. (which can go wrong if the directory already exists.)
+ strcpy(dironly,path);
+ char* post = strrchr(dironly,CROSS_FILESPLIT);
+
+ if (post) {
+#if defined (WIN32)
+ //OS2 ?
+ if (post > dironly && *(post - 1) == ':' && (post - dironly) == 2)
+ post++; //move away from X: as need to end up with x:\
+#else
+ //Lets hope this is not really used.. (root folder specified as overlay)
+ if (post == dironly)
+ post++; //move away from /
+#endif
+ *post = 0; //TODO take care of AddEntryDIR D:\\piet) (so mount d d:\ as base)
+ *(post + 1) = 0; //As FindDirInfo is skipping over the base directory
+ }
+ CFileInfo* dir = FindDirInfo(dironly,expand);
+ const char* pos = strrchr(path,CROSS_FILESPLIT);
+
+ if (pos) {
+ strcpy(file,pos + 1);
+ // Check if directory already exists, then don't add new entry...
+ if (checkExists) {
+ Bits index = GetLongName(dir,file);
+ if (index >= 0) {
+ //directory already exists, but most likely empty.
+ dir = dir->fileList[index];
+ if (dir->isOverlayDir && dir->fileList.empty()) {
+ //maybe care about searches ? but this function should only run on cache inits/refreshes.
+ //add dot entries
+ CreateEntry(dir,".",true);
+ CreateEntry(dir,"..",true);
+ }
+ return;
+ }
+ }
+
+ CreateEntry(dir,file,true);
+
+
+ Bits index = GetLongName(dir,file);
+ if (index>=0) {
+ Bit32u i;
+ // Check if there are any open search dir that are affected by this...
+ if (dir) for (i=0; i<MAX_OPENDIRS; i++) {
+ if ((dirSearch[i]==dir) && ((Bit32u)index<=dirSearch[i]->nextEntry))
+ dirSearch[i]->nextEntry++;
+ }
+
+ dir = dir->fileList[index];
+ dir->isOverlayDir = true;
+ CreateEntry(dir,".",true);
+ CreateEntry(dir,"..",true);
+ }
+ // LOG_DEBUG("DIR: Added Entry %s",path);
+ } else {
+ // LOG_DEBUG("DIR: Error: Failed to add %s",path);
+ }
+}
+
+void DOS_Drive_Cache::DeleteEntry(const char* path, bool ignoreLastDir) {
+ CacheOut(path,ignoreLastDir);
+ if (dirSearch[srchNr] && (dirSearch[srchNr]->nextEntry>0)) dirSearch[srchNr]->nextEntry--;
+
+ if (!ignoreLastDir) {
+ // Check if there are any open search dir that are affected by this...
+ Bit32u i;
+ char expand [CROSS_LEN];
+ CFileInfo* dir = FindDirInfo(path,expand);
+ if (dir) for (i=0; i<MAX_OPENDIRS; i++) {
+ if ((dirSearch[i]==dir) && (dirSearch[i]->nextEntry>0))
+ dirSearch[i]->nextEntry--;
+ }
+ }
+}
+
+void DOS_Drive_Cache::CacheOut(const char* path, bool ignoreLastDir) {
+ char expand[CROSS_LEN] = { 0 };
+ CFileInfo* dir;
+
+ if (ignoreLastDir) {
+ char tmp[CROSS_LEN] = { 0 };
+ Bit32s len=0;
+ const char* pos = strrchr(path,CROSS_FILESPLIT);
+ if (pos) len = (Bit32s)(pos - path);
+ if (len>0) {
+ safe_strncpy(tmp,path,len+1);
+ } else {
+ strcpy(tmp,path);
+ }
+ dir = FindDirInfo(tmp,expand);
+ } else {
+ dir = FindDirInfo(path,expand);
+ }
+
+// LOG_DEBUG("DIR: Caching out %s : dir %s",expand,dir->orgname);
+ // delete file objects...
+ //Maybe check if it is a file and then only delete the file and possibly the long name. instead of all objects in the dir.
+ for(Bit32u i=0; i<dir->fileList.size(); i++) {
+ if (dirSearch[srchNr]==dir->fileList[i]) dirSearch[srchNr] = 0;
+ DeleteFileInfo(dir->fileList[i]); dir->fileList[i] = 0;
+ }
+ // clear lists
+ dir->fileList.clear();
+ dir->longNameList.clear();
+ save_dir = 0;
+}
+
+bool DOS_Drive_Cache::IsCachedIn(CFileInfo* curDir) {
+ return (curDir->isOverlayDir || curDir->fileList.size()>0);
+}
+
+
+bool DOS_Drive_Cache::GetShortName(const char* fullname, char* shortname) {
+ // Get Dir Info
+ char expand[CROSS_LEN] = {0};
+ CFileInfo* curDir = FindDirInfo(fullname,expand);
+
+ const char* pos = strrchr(fullname,CROSS_FILESPLIT);
+ if (pos) pos++; else return false;
+
+ std::vector<CFileInfo*>::size_type filelist_size = curDir->longNameList.size();
+ if (GCC_UNLIKELY(filelist_size<=0)) return false;
+
+ // The orgname part of the list is not sorted (shortname is)! So we can only walk through it.
+ for(Bitu i = 0; i < filelist_size; i++) {
+#if defined (WIN32) || defined (OS2) /* Win 32 & OS/2*/
+ if (strcasecmp(pos,curDir->longNameList[i]->orgname) == 0) {
+#else
+ if (strcmp(pos,curDir->longNameList[i]->orgname) == 0) {
+#endif
+ strcpy(shortname,curDir->longNameList[i]->shortname);
+ return true;
+ }
+ }
+ return false;
+}
+
+int DOS_Drive_Cache::CompareShortname(const char* compareName, const char* shortName) {
+ char const* cpos = strchr(shortName,'~');
+ if (cpos) {
+/* the following code is replaced as it's not safe when char* is 64 bits */
+/* Bits compareCount1 = (int)cpos - (int)shortName;
+ char* endPos = strchr(cpos,'.');
+ Bitu numberSize = endPos ? int(endPos)-int(cpos) : strlen(cpos);
+
+ char* lpos = strchr(compareName,'.');
+ Bits compareCount2 = lpos ? int(lpos)-int(compareName) : strlen(compareName);
+ if (compareCount2>8) compareCount2 = 8;
+
+ compareCount2 -= numberSize;
+ if (compareCount2>compareCount1) compareCount1 = compareCount2;
+*/
+ size_t compareCount1 = strcspn(shortName,"~");
+ size_t numberSize = strcspn(cpos,".");
+ size_t compareCount2 = strcspn(compareName,".");
+ if(compareCount2 > 8) compareCount2 = 8;
+ /* We want
+ * compareCount2 -= numberSize;
+ * if (compareCount2>compareCount1) compareCount1 = compareCount2;
+ * but to prevent negative numbers:
+ */
+ if(compareCount2 > compareCount1 + numberSize)
+ compareCount1 = compareCount2 - numberSize;
+ return strncmp(compareName,shortName,compareCount1);
+ }
+ return strcmp(compareName,shortName);
+}
+
+Bitu DOS_Drive_Cache::CreateShortNameID(CFileInfo* curDir, const char* name) {
+ std::vector<CFileInfo*>::size_type filelist_size = curDir->longNameList.size();
+ if (GCC_UNLIKELY(filelist_size<=0)) return 1; // shortener IDs start with 1
+
+ Bitu foundNr = 0;
+ Bits low = 0;
+ Bits high = (Bits)(filelist_size-1);
+ Bits mid, res;
+
+ while (low<=high) {
+ mid = (low+high)/2;
+ res = CompareShortname(name,curDir->longNameList[mid]->shortname);
+
+ if (res>0) low = mid+1; else
+ if (res<0) high = mid-1;
+ else {
+ // any more same x chars in next entries ?
+ do {
+ foundNr = curDir->longNameList[mid]->shortNr;
+ mid++;
+ } while((Bitu)mid<curDir->longNameList.size() && (CompareShortname(name,curDir->longNameList[mid]->shortname)==0));
+ break;
+ };
+ }
+ return foundNr+1;
+}
+
+bool DOS_Drive_Cache::RemoveTrailingDot(char* shortname) {
+// remove trailing '.' if no extension is available (Linux compatibility)
+ size_t len = strlen(shortname);
+ if (len && (shortname[len-1]=='.')) {
+ if (len==1) return false;
+ if ((len==2) && (shortname[0]=='.')) return false;
+ shortname[len-1] = 0;
+ return true;
+ }
+ return false;
+}
+
+#define WINE_DRIVE_SUPPORT 1
+#if WINE_DRIVE_SUPPORT
+//Changes to interact with WINE by supporting their namemangling.
+//The code is rather slow, because orglist is unordered, so it needs to be avoided if possible.
+//Hence the tests in GetLongFileName
+
+
+// From the Wine project
+static Bits wine_hash_short_file_name( char* name, char* buffer )
+{
+ static const char hash_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
+ static const char invalid_chars[] = { '*','?','<','>','|','"','+','=',',',';','[',']',' ','\345','~','.',0 };
+ char* p;
+ char* ext;
+ char* end = name + strlen(name);
+ char* dst;
+ unsigned short hash;
+ int i;
+
+ // Compute the hash code of the file name
+ for (p = name, hash = 0xbeef; p < end - 1; p++)
+ hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
+ hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); // Last character
+
+
+ // Find last dot for start of the extension
+ for (p = name + 1, ext = NULL; p < end - 1; p++) if (*p == '.') ext = p;
+
+ // Copy first 4 chars, replacing invalid chars with '_'
+ for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
+ {
+ if (p == end || p == ext) break;
+ *dst++ = (*p < 0 || strchr( invalid_chars, *p ) != NULL) ? '_' : toupper(*p);
+ }
+ // Pad to 5 chars with '~'
+ while (i-- >= 0) *dst++ = '~';
+
+ // Insert hash code converted to 3 ASCII chars
+ *dst++ = hash_chars[(hash >> 10) & 0x1f];
+ *dst++ = hash_chars[(hash >> 5) & 0x1f];
+ *dst++ = hash_chars[hash & 0x1f];
+
+ // Copy the first 3 chars of the extension (if any)
+ if (ext)
+ {
+ *dst++ = '.';
+ for (i = 3, ext++; (i > 0) && ext < end; i--, ext++)
+ *dst++ = (*ext < 0 || strchr( invalid_chars, *ext ) != NULL) ? '_' : toupper(*ext);
+ }
+
+ return dst - buffer;
+}
+#endif
+
+Bits DOS_Drive_Cache::GetLongName(CFileInfo* curDir, char* shortName) {
+ std::vector<CFileInfo*>::size_type filelist_size = curDir->fileList.size();
+ if (GCC_UNLIKELY(filelist_size<=0)) return -1;
+
+ // Remove dot, if no extension...
+ RemoveTrailingDot(shortName);
+ // Search long name and return array number of element
+ Bits low = 0;
+ Bits high = (Bits)(filelist_size-1);
+ Bits mid,res;
+ while (low<=high) {
+ mid = (low+high)/2;
+ res = strcmp(shortName,curDir->fileList[mid]->shortname);
+ if (res>0) low = mid+1; else
+ if (res<0) high = mid-1; else
+ { // Found
+ strcpy(shortName,curDir->fileList[mid]->orgname);
+ return mid;
+ };
+ }
+#ifdef WINE_DRIVE_SUPPORT
+ if (strlen(shortName) < 8 || shortName[4] != '~' || shortName[5] == '.' || shortName[6] == '.' || shortName[7] == '.') return -1; // not available
+ // else it's most likely a Wine style short name ABCD~###, # = not dot (length at least 8)
+ // The above test is rather strict as the following loop can be really slow if filelist_size is large.
+ char buff[CROSS_LEN];
+ for (Bitu i = 0; i < filelist_size; i++) {
+ res = wine_hash_short_file_name(curDir->fileList[i]->orgname,buff);
+ buff[res] = 0;
+ if (!strcmp(shortName,buff)) {
+ // Found
+ strcpy(shortName,curDir->fileList[i]->orgname);
+ return (Bits)i;
+ }
+ }
+#endif
+ // not available
+ return -1;
+}
+
+bool DOS_Drive_Cache::RemoveSpaces(char* str) {
+// Removes all spaces
+ char* curpos = str;
+ char* chkpos = str;
+ while (*chkpos!=0) {
+ if (*chkpos==' ') chkpos++; else *curpos++ = *chkpos++;
+ }
+ *curpos = 0;
+ return (curpos!=chkpos);
+}
+
+void DOS_Drive_Cache::CreateShortName(CFileInfo* curDir, CFileInfo* info) {
+ Bits len = 0;
+ bool createShort = false;
+
+ char tmpNameBuffer[CROSS_LEN];
+
+ char* tmpName = tmpNameBuffer;
+
+ // Remove Spaces
+ strcpy(tmpName,info->orgname);
+ upcase(tmpName);
+ createShort = RemoveSpaces(tmpName);
+
+ // Get Length of filename
+ char* pos = strchr(tmpName,'.');
+ if (pos) {
+ // ignore preceding '.' if extension is longer than "3"
+ if (strlen(pos)>4) {
+ while (*tmpName=='.') tmpName++;
+ createShort = true;
+ }
+ pos = strchr(tmpName,'.');
+ if (pos) len = (Bits)(pos - tmpName);
+ else len = (Bits)strlen(tmpName);
+ } else {
+ len = (Bits)strlen(tmpName);
+ }
+
+ // Should shortname version be created ?
+ createShort = createShort || (len>8);
+ if (!createShort) {
+ char buffer[CROSS_LEN];
+ strcpy(buffer,tmpName);
+ createShort = (GetLongName(curDir,buffer)>=0);
+ }
+
+ if (createShort) {
+ // Create number
+ char buffer[8];
+ info->shortNr = CreateShortNameID(curDir,tmpName);
+ sprintf(buffer,"%d",info->shortNr);
+ // Copy first letters
+ Bits tocopy = 0;
+ size_t buflen = strlen(buffer);
+ if (len+buflen+1>8) tocopy = (Bits)(8 - buflen - 1);
+ else tocopy = len;
+ safe_strncpy(info->shortname,tmpName,tocopy+1);
+ // Copy number
+ strcat(info->shortname,"~");
+ strcat(info->shortname,buffer);
+ // Add (and cut) Extension, if available
+ if (pos) {
+ // Step to last extension...
+ pos = strrchr(tmpName, '.');
+ // add extension
+ strncat(info->shortname,pos,4);
+ info->shortname[DOS_NAMELENGTH] = 0;
+ }
+
+ // keep list sorted for CreateShortNameID to work correctly
+ if (curDir->longNameList.size()>0) {
+ if (!(strcmp(info->shortname,curDir->longNameList.back()->shortname)<0)) {
+ // append at end of list
+ curDir->longNameList.push_back(info);
+ } else {
+ // look for position where to insert this element
+ bool found=false;
+ std::vector<CFileInfo*>::iterator it;
+ for (it=curDir->longNameList.begin(); it!=curDir->longNameList.end(); ++it) {
+ if (strcmp(info->shortname,(*it)->shortname)<0) {
+ found = true;
+ break;
+ }
+ }
+ // Put it in longname list...
+ if (found) curDir->longNameList.insert(it,info);
+ else curDir->longNameList.push_back(info);
+ }
+ } else {
+ // empty file list, append
+ curDir->longNameList.push_back(info);
+ }
+ } else {
+ strcpy(info->shortname,tmpName);
+ }
+ RemoveTrailingDot(info->shortname);
+}
+
+DOS_Drive_Cache::CFileInfo* DOS_Drive_Cache::FindDirInfo(const char* path, char* expandedPath) {
+ // statics
+ static char split[2] = { CROSS_FILESPLIT,0 };
+
+ char dir [CROSS_LEN];
+ char work [CROSS_LEN];
+ const char* start = path;
+ const char* pos;
+ CFileInfo* curDir = dirBase;
+ Bit16u id;
+
+ if (save_dir && (strcmp(path,save_path)==0)) {
+ strcpy(expandedPath,save_expanded);
+ return save_dir;
+ };
+
+// LOG_DEBUG("DIR: Find %s",path);
+
+ // Remove base dir path
+ start += strlen(basePath);
+ strcpy(expandedPath,basePath);
+
+ // hehe, baseDir should be cached in...
+ if (!IsCachedIn(curDir)) {
+ strcpy(work,basePath);
+ if (OpenDir(curDir,work,id)) {
+ char buffer[CROSS_LEN];
+ char* result = 0;
+ strcpy(buffer,dirPath);
+ ReadDir(id,result);
+ strcpy(dirPath,buffer);
+ if (dirSearch[id]) {
+ dirSearch[id]->id = MAX_OPENDIRS;
+ dirSearch[id] = 0;
+ }
+ };
+ };
+
+ do {
+// bool errorcheck = false;
+ pos = strchr(start,CROSS_FILESPLIT);
+ if (pos) { safe_strncpy(dir,start,pos-start+1); /*errorcheck = true;*/ }
+ else { strcpy(dir,start); };
+
+ // Path found
+ Bits nextDir = GetLongName(curDir,dir);
+ strcat(expandedPath,dir);
+
+ // Error check
+/* if ((errorcheck) && (nextDir<0)) {
+ LOG_DEBUG("DIR: Error: %s not found.",expandedPath);
+ };
+*/
+ // Follow Directory
+ if ((nextDir>=0) && curDir->fileList[nextDir]->isDir) {
+ curDir = curDir->fileList[nextDir];
+ strcpy (curDir->orgname,dir);
+ if (!IsCachedIn(curDir)) {
+ if (OpenDir(curDir,expandedPath,id)) {
+ char buffer[CROSS_LEN];
+ char* result = 0;
+ strcpy(buffer,dirPath);
+ ReadDir(id,result);
+ strcpy(dirPath,buffer);
+ if (dirSearch[id]) {
+ dirSearch[id]->id = MAX_OPENDIRS;
+ dirSearch[id] = 0;
+ }
+ };
+ }
+ };
+ if (pos) {
+ strcat(expandedPath,split);
+ start = pos+1;
+ }
+ } while (pos);
+
+ // Save last result for faster access next time
+ strcpy(save_path,path);
+ strcpy(save_expanded,expandedPath);
+ save_dir = curDir;
+
+ return curDir;
+}
+
+bool DOS_Drive_Cache::OpenDir(const char* path, Bit16u& id) {
+ char expand[CROSS_LEN] = {0};
+ CFileInfo* dir = FindDirInfo(path,expand);
+ if (OpenDir(dir,expand,id)) {
+ dirSearch[id]->nextEntry = 0;
+ return true;
+ }
+ return false;
+}
+
+bool DOS_Drive_Cache::OpenDir(CFileInfo* dir, const char* expand, Bit16u& id) {
+ id = GetFreeID(dir);
+ dirSearch[id] = dir;
+ char expandcopy [CROSS_LEN];
+ strcpy(expandcopy,expand);
+ // Add "/"
+ char end[2]={CROSS_FILESPLIT,0};
+ if (expandcopy[strlen(expandcopy)-1]!=CROSS_FILESPLIT) strcat(expandcopy,end);
+ // open dir
+ if (dirSearch[id]) {
+ // open dir
+ dir_information* dirp = open_directory(expandcopy);
+ if (dirp || dir->isOverlayDir) {
+ // Reset it..
+ if (dirp) close_directory(dirp);
+ strcpy(dirPath,expandcopy);
+ return true;
+ }
+ if (dirSearch[id]) {
+ dirSearch[id]->id = MAX_OPENDIRS;
+ dirSearch[id] = 0;
+ }
+ };
+ return false;
+}
+
+void DOS_Drive_Cache::CreateEntry(CFileInfo* dir, const char* name, bool is_directory) {
+ CFileInfo* info = new CFileInfo;
+ strcpy(info->orgname, name);
+ info->shortNr = 0;
+ info->isDir = is_directory;
+
+ // Check for long filenames...
+ CreateShortName(dir, info);
+
+ bool found = false;
+
+ // keep list sorted (so GetLongName works correctly, used by CreateShortName in this routine)
+ if (dir->fileList.size()>0) {
+ if (!(strcmp(info->shortname,dir->fileList.back()->shortname)<0)) {
+ // append at end of list
+ dir->fileList.push_back(info);
+ } else {
+ // look for position where to insert this element
+ std::vector<CFileInfo*>::iterator it;
+ for (it=dir->fileList.begin(); it!=dir->fileList.end(); ++it) {
+ if (strcmp(info->shortname,(*it)->shortname)<0) {
+ found = true;
+ break;
+ }
+ }
+ // Put file in lists
+ if (found) dir->fileList.insert(it,info);
+ else dir->fileList.push_back(info);
+ }
+ } else {
+ // empty file list, append
+ dir->fileList.push_back(info);
+ }
+}
+
+void DOS_Drive_Cache::CopyEntry(CFileInfo* dir, CFileInfo* from) {
+ CFileInfo* info = new CFileInfo;
+ // just copy things into new fileinfo
+ strcpy(info->orgname, from->orgname);
+ strcpy(info->shortname, from->shortname);
+ info->shortNr = from->shortNr;
+ info->isDir = from->isDir;
+
+ dir->fileList.push_back(info);
+}
+
+bool DOS_Drive_Cache::ReadDir(Bit16u id, char* &result) {
+ // shouldnt happen...
+ if (id>MAX_OPENDIRS) return false;
+
+ if (!IsCachedIn(dirSearch[id])) {
+ // Try to open directory
+ dir_information* dirp = open_directory(dirPath);
+ if (!dirp) {
+ if (dirSearch[id]) {
+ dirSearch[id]->id = MAX_OPENDIRS;
+ dirSearch[id] = 0;
+ }
+ return false;
+ }
+ // Read complete directory
+ char dir_name[CROSS_LEN];
+ bool is_directory;
+ if (read_directory_first(dirp, dir_name, is_directory)) {
+ CreateEntry(dirSearch[id], dir_name, is_directory);
+ while (read_directory_next(dirp, dir_name, is_directory)) {
+ CreateEntry(dirSearch[id], dir_name, is_directory);
+ }
+ }
+
+ // close dir
+ close_directory(dirp);
+
+ // Info
+/* if (!dirp) {
+ LOG_DEBUG("DIR: Error Caching in %s",dirPath);
+ return false;
+ } else {
+ char buffer[128];
+ sprintf(buffer,"DIR: Caching in %s (%d Files)",dirPath,dirSearch[srchNr]->fileList.size());
+ LOG_DEBUG(buffer);
+ };*/
+ };
+ if (SetResult(dirSearch[id], result, dirSearch[id]->nextEntry)) return true;
+ if (dirSearch[id]) {
+ dirSearch[id]->id = MAX_OPENDIRS;
+ dirSearch[id] = 0;
+ }
+ return false;
+}
+
+bool DOS_Drive_Cache::SetResult(CFileInfo* dir, char* &result, Bitu entryNr)
+{
+ static char res[CROSS_LEN] = { 0 };
+
+ result = res;
+ if (entryNr>=dir->fileList.size()) return false;
+ CFileInfo* info = dir->fileList[entryNr];
+ // copy filename, short version
+ strcpy(res,info->shortname);
+ // Set to next Entry
+ dir->nextEntry = entryNr+1;
+ return true;
+}
+
+// FindFirst / FindNext
+bool DOS_Drive_Cache::FindFirst(char* path, Bit16u& id) {
+ Bit16u dirID;
+ // Cache directory in
+ if (!OpenDir(path,dirID)) return false;
+
+ //Find a free slot.
+ //If the next one isn't free, move on to the next, if none is free => reset and assume the worst
+ Bit16u local_findcounter = 0;
+ while ( local_findcounter < MAX_OPENDIRS ) {
+ if (dirFindFirst[this->nextFreeFindFirst] == 0) break;
+ if (++this->nextFreeFindFirst >= MAX_OPENDIRS) this->nextFreeFindFirst = 0; //Wrap around
+ local_findcounter++;
+ }
+
+ Bit16u dirFindFirstID = this->nextFreeFindFirst++;
+ if (this->nextFreeFindFirst >= MAX_OPENDIRS) this->nextFreeFindFirst = 0; //Increase and wrap around for the next search.
+
+ if (local_findcounter == MAX_OPENDIRS) { //Here is the reset from above.
+ // no free slot found...
+ LOG(LOG_MISC,LOG_ERROR)("DIRCACHE: FindFirst/Next: All slots full. Resetting");
+ // Clear the internal list then.
+ dirFindFirstID = 0;
+ this->nextFreeFindFirst = 1; //the next free one after this search
+ for(Bitu n=0; n<MAX_OPENDIRS;n++) {
+ // Clear and reuse slot
+ DeleteFileInfo(dirFindFirst[n]);
+ dirFindFirst[n]=0;
+ }
+
+ }
+ dirFindFirst[dirFindFirstID] = new CFileInfo();
+ dirFindFirst[dirFindFirstID]-> nextEntry = 0;
+
+ // Copy entries to use with FindNext
+ for (Bitu i=0; i<dirSearch[dirID]->fileList.size(); i++) {
+ CopyEntry(dirFindFirst[dirFindFirstID],dirSearch[dirID]->fileList[i]);
+ }
+ // Now re-sort the fileList accordingly to output
+ switch (sortDirType) {
+ case ALPHABETICAL : break;
+// case ALPHABETICAL : std::sort(dirFindFirst[dirFindFirstID]->fileList.begin(), dirFindFirst[dirFindFirstID]->fileList.end(), SortByName); break;
+ case DIRALPHABETICAL : std::sort(dirFindFirst[dirFindFirstID]->fileList.begin(), dirFindFirst[dirFindFirstID]->fileList.end(), SortByDirName); break;
+ case ALPHABETICALREV : std::sort(dirFindFirst[dirFindFirstID]->fileList.begin(), dirFindFirst[dirFindFirstID]->fileList.end(), SortByNameRev); break;
+ case DIRALPHABETICALREV : std::sort(dirFindFirst[dirFindFirstID]->fileList.begin(), dirFindFirst[dirFindFirstID]->fileList.end(), SortByDirNameRev); break;
+ case NOSORT : break;
+ }
+
+// LOG(LOG_MISC,LOG_ERROR)("DIRCACHE: FindFirst : %s (ID:%02X)",path,dirFindFirstID);
+ id = dirFindFirstID;
+ return true;
+}
+
+bool DOS_Drive_Cache::FindNext(Bit16u id, char* &result) {
+ // out of range ?
+ if ((id>=MAX_OPENDIRS) || !dirFindFirst[id]) {
+ LOG(LOG_MISC,LOG_ERROR)("DIRCACHE: FindFirst/Next failure : ID out of range: %04X",id);
+ return false;
+ }
+ if (!SetResult(dirFindFirst[id], result, dirFindFirst[id]->nextEntry)) {
+ // free slot
+ DeleteFileInfo(dirFindFirst[id]); dirFindFirst[id] = 0;
+ return false;
+ }
+ return true;
+}
+
+void DOS_Drive_Cache::ClearFileInfo(CFileInfo *dir) {
+ for(Bit32u i=0; i<dir->fileList.size(); i++) {
+ if (CFileInfo *info = dir->fileList[i])
+ ClearFileInfo(info);
+ }
+ if (dir->id != MAX_OPENDIRS) {
+ dirSearch[dir->id] = 0;
+ dir->id = MAX_OPENDIRS;
+ }
+}
+
+void DOS_Drive_Cache::DeleteFileInfo(CFileInfo *dir) {
+ if (dir)
+ ClearFileInfo(dir);
+ delete dir;
+}
diff --git a/src/dos/drive_fat.cpp b/src/dos/drive_fat.cpp
new file mode 100644
index 000000000..e7ffdb70d
--- /dev/null
+++ b/src/dos/drive_fat.cpp
@@ -0,0 +1,1435 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "dosbox.h"
+#include "dos_inc.h"
+#include "drives.h"
+#include "support.h"
+#include "cross.h"
+#include "bios.h"
+#include "bios_disk.h"
+
+#define IMGTYPE_FLOPPY 0
+#define IMGTYPE_ISO 1
+#define IMGTYPE_HDD 2
+
+#define FAT12 0
+#define FAT16 1
+#define FAT32 2
+
+class fatFile : public DOS_File {
+public:
+ fatFile(const char* name, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive);
+ bool Read(Bit8u * data,Bit16u * size);
+ bool Write(Bit8u * data,Bit16u * size);
+ bool Seek(Bit32u * pos,Bit32u type);
+ bool Close();
+ Bit16u GetInformation(void);
+ bool UpdateDateTimeFromHost(void);
+public:
+ Bit32u firstCluster;
+ Bit32u seekpos;
+ Bit32u filelength;
+ Bit32u currentSector;
+ Bit32u curSectOff;
+ Bit8u sectorBuffer[512];
+ /* Record of where in the directory structure this file is located */
+ Bit32u dirCluster;
+ Bit32u dirIndex;
+
+ bool loadedSector;
+ fatDrive *myDrive;
+private:
+ enum { NONE,READ,WRITE } last_action;
+ Bit16u info;
+};
+
+
+/* IN - char * filename: Name in regular filename format, e.g. bob.txt */
+/* OUT - char * filearray: Name in DOS directory format, eleven char, e.g. bob txt */
+static void convToDirFile(char *filename, char *filearray) {
+ Bit32u charidx = 0;
+ Bit32u flen,i;
+ flen = (Bit32u)strlen(filename);
+ memset(filearray, 32, 11);
+ for(i=0;i<flen;i++) {
+ if(charidx >= 11) break;
+ if(filename[i] != '.') {
+ filearray[charidx] = filename[i];
+ charidx++;
+ } else {
+ charidx = 8;
+ }
+ }
+}
+
+fatFile::fatFile(const char* /*name*/, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive) {
+ Bit32u seekto = 0;
+ firstCluster = startCluster;
+ myDrive = useDrive;
+ filelength = fileLen;
+ open = true;
+ loadedSector = false;
+ curSectOff = 0;
+ seekpos = 0;
+ memset(&sectorBuffer[0], 0, sizeof(sectorBuffer));
+
+ if(filelength > 0) {
+ Seek(&seekto, DOS_SEEK_SET);
+ }
+}
+
+bool fatFile::Read(Bit8u * data, Bit16u *size) {
+ if ((this->flags & 0xf) == OPEN_WRITE) { // check if file opened in write-only mode
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ Bit16u sizedec, sizecount;
+ if(seekpos >= filelength) {
+ *size = 0;
+ return true;
+ }
+
+ if (!loadedSector) {
+ currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
+ if(currentSector == 0) {
+ /* EOC reached before EOF */
+ *size = 0;
+ loadedSector = false;
+ return true;
+ }
+ curSectOff = seekpos % myDrive->getSectorSize();
+ myDrive->readSector(currentSector, sectorBuffer);
+ loadedSector = true;
+ }
+
+ sizedec = *size;
+ sizecount = 0;
+ while(sizedec != 0) {
+ if(seekpos >= filelength) {
+ *size = sizecount;
+ return true;
+ }
+ data[sizecount++] = sectorBuffer[curSectOff++];
+ seekpos++;
+ if(curSectOff >= myDrive->getSectorSize()) {
+ currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
+ if(currentSector == 0) {
+ /* EOC reached before EOF */
+ //LOG_MSG("EOC reached before EOF, seekpos %d, filelen %d", seekpos, filelength);
+ *size = sizecount;
+ loadedSector = false;
+ return true;
+ }
+ curSectOff = 0;
+ myDrive->readSector(currentSector, sectorBuffer);
+ loadedSector = true;
+ //LOG_MSG("Reading absolute sector at %d for seekpos %d", currentSector, seekpos);
+ }
+ --sizedec;
+ }
+ *size =sizecount;
+ return true;
+}
+
+bool fatFile::Write(Bit8u * data, Bit16u *size) {
+ if ((this->flags & 0xf) == OPEN_READ) { // check if file opened in read-only mode
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+
+ direntry tmpentry;
+ Bit16u sizedec, sizecount;
+ sizedec = *size;
+ sizecount = 0;
+
+ if(seekpos < filelength && *size == 0) {
+ /* Truncate file to current position */
+ myDrive->deleteClustChain(firstCluster, seekpos);
+ filelength = seekpos;
+ goto finalizeWrite;
+ }
+
+ if(seekpos > filelength) {
+ /* Extend file to current position */
+ Bit32u clustSize = myDrive->getClusterSize();
+ if(filelength == 0) {
+ firstCluster = myDrive->getFirstFreeClust();
+ if(firstCluster == 0) goto finalizeWrite; // out of space
+ myDrive->allocateCluster(firstCluster, 0);
+ filelength = clustSize;
+ }
+ filelength = ((filelength - 1) / clustSize + 1) * clustSize;
+ while(filelength < seekpos) {
+ if(myDrive->appendCluster(firstCluster) == 0) goto finalizeWrite; // out of space
+ filelength += clustSize;
+ }
+ if(filelength > seekpos) filelength = seekpos;
+ if(*size == 0) goto finalizeWrite;
+ }
+
+ while(sizedec != 0) {
+ /* Increase filesize if necessary */
+ if(seekpos >= filelength) {
+ if(filelength == 0) {
+ firstCluster = myDrive->getFirstFreeClust();
+ if(firstCluster == 0) goto finalizeWrite; // out of space
+ myDrive->allocateCluster(firstCluster, 0);
+ currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
+ myDrive->readSector(currentSector, sectorBuffer);
+ loadedSector = true;
+ }
+ if (!loadedSector) {
+ currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
+ if(currentSector == 0) {
+ /* EOC reached before EOF - try to increase file allocation */
+ myDrive->appendCluster(firstCluster);
+ /* Try getting sector again */
+ currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
+ if(currentSector == 0) {
+ /* No can do. lets give up and go home. We must be out of room */
+ goto finalizeWrite;
+ }
+ }
+ curSectOff = seekpos % myDrive->getSectorSize();
+ myDrive->readSector(currentSector, sectorBuffer);
+ loadedSector = true;
+ }
+ filelength = seekpos+1;
+ }
+ sectorBuffer[curSectOff++] = data[sizecount++];
+ seekpos++;
+ if(curSectOff >= myDrive->getSectorSize()) {
+ if(loadedSector) myDrive->writeSector(currentSector, sectorBuffer);
+
+ currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
+ if(currentSector == 0) {
+ /* EOC reached before EOF - try to increase file allocation */
+ myDrive->appendCluster(firstCluster);
+ /* Try getting sector again */
+ currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
+ if(currentSector == 0) {
+ /* No can do. lets give up and go home. We must be out of room */
+ loadedSector = false;
+ goto finalizeWrite;
+ }
+ }
+ curSectOff = 0;
+ myDrive->readSector(currentSector, sectorBuffer);
+ loadedSector = true;
+ }
+ --sizedec;
+ }
+ if(curSectOff>0 && loadedSector) myDrive->writeSector(currentSector, sectorBuffer);
+
+finalizeWrite:
+ myDrive->directoryBrowse(dirCluster, &tmpentry, dirIndex);
+ tmpentry.entrysize = filelength;
+ tmpentry.loFirstClust = (Bit16u)firstCluster;
+ myDrive->directoryChange(dirCluster, &tmpentry, dirIndex);
+
+ *size =sizecount;
+ return true;
+}
+
+bool fatFile::Seek(Bit32u *pos, Bit32u type) {
+ Bit32s seekto=0;
+
+ switch(type) {
+ case DOS_SEEK_SET:
+ seekto = (Bit32s)*pos;
+ break;
+ case DOS_SEEK_CUR:
+ /* Is this relative seek signed? */
+ seekto = (Bit32s)*pos + (Bit32s)seekpos;
+ break;
+ case DOS_SEEK_END:
+ seekto = (Bit32s)filelength + (Bit32s)*pos;
+ break;
+ }
+// LOG_MSG("Seek to %d with type %d (absolute value %d)", *pos, type, seekto);
+
+ if(seekto<0) seekto = 0;
+ seekpos = (Bit32u)seekto;
+ currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
+ if (currentSector == 0) {
+ /* not within file size, thus no sector is available */
+ loadedSector = false;
+ } else {
+ curSectOff = seekpos % myDrive->getSectorSize();
+ myDrive->readSector(currentSector, sectorBuffer);
+ loadedSector = true;
+ }
+ *pos = seekpos;
+ return true;
+}
+
+bool fatFile::Close() {
+ /* Flush buffer */
+ if (loadedSector) myDrive->writeSector(currentSector, sectorBuffer);
+
+ return false;
+}
+
+Bit16u fatFile::GetInformation(void) {
+ return 0;
+}
+
+bool fatFile::UpdateDateTimeFromHost(void) {
+ return true;
+}
+
+Bit32u fatDrive::getClustFirstSect(Bit32u clustNum) {
+ return ((clustNum - 2) * bootbuffer.sectorspercluster) + firstDataSector;
+}
+
+Bit32u fatDrive::getClusterValue(Bit32u clustNum) {
+ Bit32u fatoffset=0;
+ Bit32u fatsectnum;
+ Bit32u fatentoff;
+ Bit32u clustValue=0;
+
+ switch(fattype) {
+ case FAT12:
+ fatoffset = clustNum + (clustNum / 2);
+ break;
+ case FAT16:
+ fatoffset = clustNum * 2;
+ break;
+ case FAT32:
+ fatoffset = clustNum * 4;
+ break;
+ }
+ fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff;
+ fatentoff = fatoffset % bootbuffer.bytespersector;
+
+ if(curFatSect != fatsectnum) {
+ /* Load two sectors at once for FAT12 */
+ readSector(fatsectnum, &fatSectBuffer[0]);
+ if (fattype==FAT12)
+ readSector(fatsectnum+1, &fatSectBuffer[512]);
+ curFatSect = fatsectnum;
+ }
+
+ switch(fattype) {
+ case FAT12:
+ clustValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
+ if(clustNum & 0x1) {
+ clustValue >>= 4;
+ } else {
+ clustValue &= 0xfff;
+ }
+ break;
+ case FAT16:
+ clustValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
+ break;
+ case FAT32:
+ clustValue = *((Bit32u *)&fatSectBuffer[fatentoff]);
+ break;
+ }
+
+ return clustValue;
+}
+
+void fatDrive::setClusterValue(Bit32u clustNum, Bit32u clustValue) {
+ Bit32u fatoffset=0;
+ Bit32u fatsectnum;
+ Bit32u fatentoff;
+
+ switch(fattype) {
+ case FAT12:
+ fatoffset = clustNum + (clustNum / 2);
+ break;
+ case FAT16:
+ fatoffset = clustNum * 2;
+ break;
+ case FAT32:
+ fatoffset = clustNum * 4;
+ break;
+ }
+ fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff;
+ fatentoff = fatoffset % bootbuffer.bytespersector;
+
+ if(curFatSect != fatsectnum) {
+ /* Load two sectors at once for FAT12 */
+ readSector(fatsectnum, &fatSectBuffer[0]);
+ if (fattype==FAT12)
+ readSector(fatsectnum+1, &fatSectBuffer[512]);
+ curFatSect = fatsectnum;
+ }
+
+ switch(fattype) {
+ case FAT12: {
+ Bit16u tmpValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
+ if(clustNum & 0x1) {
+ clustValue &= 0xfff;
+ clustValue <<= 4;
+ tmpValue &= 0xf;
+ tmpValue |= (Bit16u)clustValue;
+
+ } else {
+ clustValue &= 0xfff;
+ tmpValue &= 0xf000;
+ tmpValue |= (Bit16u)clustValue;
+ }
+ *((Bit16u *)&fatSectBuffer[fatentoff]) = tmpValue;
+ break;
+ }
+ case FAT16:
+ *((Bit16u *)&fatSectBuffer[fatentoff]) = (Bit16u)clustValue;
+ break;
+ case FAT32:
+ *((Bit32u *)&fatSectBuffer[fatentoff]) = clustValue;
+ break;
+ }
+ for(int fc=0;fc<bootbuffer.fatcopies;fc++) {
+ writeSector(fatsectnum + (fc * bootbuffer.sectorsperfat), &fatSectBuffer[0]);
+ if (fattype==FAT12) {
+ if (fatentoff>=511)
+ writeSector(fatsectnum+1+(fc * bootbuffer.sectorsperfat), &fatSectBuffer[512]);
+ }
+ }
+}
+
+bool fatDrive::getEntryName(char *fullname, char *entname) {
+ char dirtoken[DOS_PATHLENGTH];
+
+ char * findDir;
+ char * findFile;
+ strcpy(dirtoken,fullname);
+
+ //LOG_MSG("Testing for filename %s", fullname);
+ findDir = strtok(dirtoken,"\\");
+ if (findDir==NULL) {
+ return true; // root always exists
+ }
+ findFile = findDir;
+ while(findDir != NULL) {
+ findFile = findDir;
+ findDir = strtok(NULL,"\\");
+ }
+ strcpy(entname, findFile);
+ return true;
+}
+
+bool fatDrive::getFileDirEntry(char const * const filename, direntry * useEntry, Bit32u * dirClust, Bit32u * subEntry) {
+ size_t len = strlen(filename);
+ char dirtoken[DOS_PATHLENGTH];
+ Bit32u currentClust = 0;
+
+ direntry foundEntry;
+ char * findDir;
+ char * findFile;
+ strcpy(dirtoken,filename);
+ findFile=dirtoken;
+
+ /* Skip if testing in root directory */
+ if ((len>0) && (filename[len-1]!='\\')) {
+ //LOG_MSG("Testing for filename %s", filename);
+ findDir = strtok(dirtoken,"\\");
+ findFile = findDir;
+ while(findDir != NULL) {
+ imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir);
+ imgDTA->SetDirID(0);
+
+ findFile = findDir;
+ if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) break;
+ else {
+ //Found something. See if it's a directory (findfirst always finds regular files)
+ char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr;
+ imgDTA->GetResult(find_name,find_size,find_date,find_time,find_attr);
+ if(!(find_attr & DOS_ATTR_DIRECTORY)) break;
+ }
+
+ currentClust = foundEntry.loFirstClust;
+ findDir = strtok(NULL,"\\");
+ }
+ } else {
+ /* Set to root directory */
+ }
+
+ /* Search found directory for our file */
+ imgDTA->SetupSearch(0,0x7,findFile);
+ imgDTA->SetDirID(0);
+ if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) return false;
+
+ memcpy(useEntry, &foundEntry, sizeof(direntry));
+ *dirClust = (Bit32u)currentClust;
+ *subEntry = ((Bit32u)imgDTA->GetDirID()-1);
+ return true;
+}
+
+bool fatDrive::getDirClustNum(char *dir, Bit32u *clustNum, bool parDir) {
+ Bit32u len = (Bit32u)strlen(dir);
+ char dirtoken[DOS_PATHLENGTH];
+ Bit32u currentClust = 0;
+ direntry foundEntry;
+ char * findDir;
+ strcpy(dirtoken,dir);
+
+ /* Skip if testing for root directory */
+ if ((len>0) && (dir[len-1]!='\\')) {
+ //LOG_MSG("Testing for dir %s", dir);
+ findDir = strtok(dirtoken,"\\");
+ while(findDir != NULL) {
+ imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir);
+ imgDTA->SetDirID(0);
+ findDir = strtok(NULL,"\\");
+ if(parDir && (findDir == NULL)) break;
+
+ char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr;
+ if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) {
+ return false;
+ } else {
+ imgDTA->GetResult(find_name,find_size,find_date,find_time,find_attr);
+ if(!(find_attr &DOS_ATTR_DIRECTORY)) return false;
+ }
+ currentClust = foundEntry.loFirstClust;
+
+ }
+ *clustNum = currentClust;
+ } else {
+ /* Set to root directory */
+ *clustNum = 0;
+ }
+ return true;
+}
+
+Bit8u fatDrive::readSector(Bit32u sectnum, void * data) {
+ if (absolute) return loadedDisk->Read_AbsoluteSector(sectnum, data);
+ Bit32u cylindersize = bootbuffer.headcount * bootbuffer.sectorspertrack;
+ Bit32u cylinder = sectnum / cylindersize;
+ sectnum %= cylindersize;
+ Bit32u head = sectnum / bootbuffer.sectorspertrack;
+ Bit32u sector = sectnum % bootbuffer.sectorspertrack + 1L;
+ return loadedDisk->Read_Sector(head, cylinder, sector, data);
+}
+
+Bit8u fatDrive::writeSector(Bit32u sectnum, void * data) {
+ if (absolute) return loadedDisk->Write_AbsoluteSector(sectnum, data);
+ Bit32u cylindersize = bootbuffer.headcount * bootbuffer.sectorspertrack;
+ Bit32u cylinder = sectnum / cylindersize;
+ sectnum %= cylindersize;
+ Bit32u head = sectnum / bootbuffer.sectorspertrack;
+ Bit32u sector = sectnum % bootbuffer.sectorspertrack + 1L;
+ return loadedDisk->Write_Sector(head, cylinder, sector, data);
+}
+
+Bit32u fatDrive::getSectorSize(void) {
+ return bootbuffer.bytespersector;
+}
+
+Bit32u fatDrive::getClusterSize(void) {
+ return bootbuffer.sectorspercluster * bootbuffer.bytespersector;
+}
+
+Bit32u fatDrive::getAbsoluteSectFromBytePos(Bit32u startClustNum, Bit32u bytePos) {
+ return getAbsoluteSectFromChain(startClustNum, bytePos / bootbuffer.bytespersector);
+}
+
+Bit32u fatDrive::getAbsoluteSectFromChain(Bit32u startClustNum, Bit32u logicalSector) {
+ Bit32s skipClust = logicalSector / bootbuffer.sectorspercluster;
+ Bit32u sectClust = logicalSector % bootbuffer.sectorspercluster;
+
+ Bit32u currentClust = startClustNum;
+ Bit32u testvalue;
+
+ while(skipClust!=0) {
+ bool isEOF = false;
+ testvalue = getClusterValue(currentClust);
+ switch(fattype) {
+ case FAT12:
+ if(testvalue >= 0xff8) isEOF = true;
+ break;
+ case FAT16:
+ if(testvalue >= 0xfff8) isEOF = true;
+ break;
+ case FAT32:
+ if(testvalue >= 0xfffffff8) isEOF = true;
+ break;
+ }
+ if((isEOF) && (skipClust>=1)) {
+ //LOG_MSG("End of cluster chain reached before end of logical sector seek!");
+ if (skipClust == 1 && fattype == FAT12) {
+ //break;
+ LOG(LOG_DOSMISC,LOG_ERROR)("End of cluster chain reached, but maybe good afterall ?");
+ }
+ return 0;
+ }
+ currentClust = testvalue;
+ --skipClust;
+ }
+
+ return (getClustFirstSect(currentClust) + sectClust);
+}
+
+void fatDrive::deleteClustChain(Bit32u startCluster, Bit32u bytePos) {
+ Bit32u clustSize = getClusterSize();
+ Bit32u endClust = (bytePos + clustSize - 1) / clustSize;
+ Bit32u countClust = 1;
+
+ Bit32u testvalue;
+ Bit32u currentClust = startCluster;
+ bool isEOF = false;
+ while(!isEOF) {
+ testvalue = getClusterValue(currentClust);
+ if(testvalue == 0) {
+ /* What the crap? Cluster is already empty - BAIL! */
+ break;
+ }
+ switch(fattype) {
+ case FAT12:
+ if(testvalue >= 0xff8) isEOF = true;
+ break;
+ case FAT16:
+ if(testvalue >= 0xfff8) isEOF = true;
+ break;
+ case FAT32:
+ if(testvalue >= 0xfffffff8) isEOF = true;
+ break;
+ }
+ if(countClust == endClust && !isEOF) {
+ /* Mark cluster as end */
+ switch(fattype) {
+ case FAT12:
+ setClusterValue(currentClust, 0xfff);
+ break;
+ case FAT16:
+ setClusterValue(currentClust, 0xffff);
+ break;
+ case FAT32:
+ setClusterValue(currentClust, 0xffffffff);
+ break;
+ }
+ } else if(countClust > endClust) {
+ /* Mark cluster as empty */
+ setClusterValue(currentClust, 0);
+ }
+ if(isEOF) break;
+ currentClust = testvalue;
+ countClust++;
+ }
+}
+
+Bit32u fatDrive::appendCluster(Bit32u startCluster) {
+ Bit32u testvalue;
+ Bit32u currentClust = startCluster;
+ bool isEOF = false;
+
+ while(!isEOF) {
+ testvalue = getClusterValue(currentClust);
+ switch(fattype) {
+ case FAT12:
+ if(testvalue >= 0xff8) isEOF = true;
+ break;
+ case FAT16:
+ if(testvalue >= 0xfff8) isEOF = true;
+ break;
+ case FAT32:
+ if(testvalue >= 0xfffffff8) isEOF = true;
+ break;
+ }
+ if(isEOF) break;
+ currentClust = testvalue;
+ }
+
+ Bit32u newClust = getFirstFreeClust();
+ /* Drive is full */
+ if(newClust == 0) return 0;
+
+ if(!allocateCluster(newClust, currentClust)) return 0;
+
+ zeroOutCluster(newClust);
+
+ return newClust;
+}
+
+bool fatDrive::allocateCluster(Bit32u useCluster, Bit32u prevCluster) {
+
+ /* Can't allocate cluster #0 */
+ if(useCluster == 0) return false;
+
+ if(prevCluster != 0) {
+ /* Refuse to allocate cluster if previous cluster value is zero (unallocated) */
+ if(!getClusterValue(prevCluster)) return false;
+
+ /* Point cluster to new cluster in chain */
+ setClusterValue(prevCluster, useCluster);
+ //LOG_MSG("Chaining cluser %d to %d", prevCluster, useCluster);
+ }
+
+ switch(fattype) {
+ case FAT12:
+ setClusterValue(useCluster, 0xfff);
+ break;
+ case FAT16:
+ setClusterValue(useCluster, 0xffff);
+ break;
+ case FAT32:
+ setClusterValue(useCluster, 0xffffffff);
+ break;
+ }
+ return true;
+}
+
+fatDrive::fatDrive(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders, Bit32u startSector) {
+ created_successfully = true;
+ FILE *diskfile;
+ Bit32u filesize;
+ bool is_hdd;
+ struct partTable mbrData;
+
+ if(imgDTASeg == 0) {
+ imgDTASeg = DOS_GetMemory(2);
+ imgDTAPtr = RealMake(imgDTASeg, 0);
+ imgDTA = new DOS_DTA(imgDTAPtr);
+ }
+
+ diskfile = fopen_wrap(sysFilename, "rb+");
+ if(!diskfile) {created_successfully = false;return;}
+ fseek(diskfile, 0L, SEEK_END);
+ filesize = (Bit32u)ftell(diskfile) / 1024L;
+ is_hdd = (filesize > 2880);
+
+ /* Load disk image */
+ loadedDisk = new imageDisk(diskfile, (Bit8u *)sysFilename, filesize, is_hdd);
+ if(!loadedDisk) {
+ created_successfully = false;
+ return;
+ }
+
+ if(is_hdd) {
+ /* Set user specified harddrive parameters */
+ loadedDisk->Set_Geometry(headscyl, cylinders,cylsector, bytesector);
+
+ loadedDisk->Read_Sector(0,0,1,&mbrData);
+
+ if(mbrData.magic1!= 0x55 || mbrData.magic2!= 0xaa) LOG_MSG("Possibly invalid partition table in disk image.");
+
+ startSector = 63;
+ int m;
+ for(m=0;m<4;m++) {
+ /* Pick the first available partition */
+ if(mbrData.pentry[m].partSize != 0x00) {
+ LOG_MSG("Using partition %d on drive; skipping %d sectors", m, mbrData.pentry[m].absSectStart);
+ startSector = mbrData.pentry[m].absSectStart;
+ break;
+ }
+ }
+
+ if(m==4) LOG_MSG("No good partition found in image.");
+
+ partSectOff = startSector;
+ } else {
+ /* Get floppy disk parameters based on image size */
+ loadedDisk->Get_Geometry(&headscyl, &cylinders, &cylsector, &bytesector);
+ /* Floppy disks don't have partitions */
+ partSectOff = 0;
+ }
+
+ if (bytesector != 512) {
+ /* Non-standard sector sizes not implemented */
+ created_successfully = false;
+ return;
+ }
+
+ loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer);
+
+ if (!is_hdd) {
+ /* Identify floppy format */
+ if ((bootbuffer.nearjmp[0] == 0x69 || bootbuffer.nearjmp[0] == 0xe9 ||
+ (bootbuffer.nearjmp[0] == 0xeb && bootbuffer.nearjmp[2] == 0x90)) &&
+ (bootbuffer.mediadescriptor & 0xf0) == 0xf0) {
+ /* DOS 2.x or later format, BPB assumed valid */
+
+ if ((bootbuffer.mediadescriptor != 0xf0 && !(bootbuffer.mediadescriptor & 0x1)) &&
+ (bootbuffer.oemname[5] != '3' || bootbuffer.oemname[6] != '.' || bootbuffer.oemname[7] < '2')) {
+ /* Fix pre-DOS 3.2 single-sided floppy */
+ bootbuffer.sectorspercluster = 1;
+ }
+ } else {
+ /* Read media descriptor in FAT */
+ Bit8u sectorBuffer[512];
+ loadedDisk->Read_AbsoluteSector(1,&sectorBuffer);
+ Bit8u mdesc = sectorBuffer[0];
+
+ if (mdesc >= 0xf8) {
+ /* DOS 1.x format, create BPB for 160kB floppy */
+ bootbuffer.bytespersector = 512;
+ bootbuffer.sectorspercluster = 1;
+ bootbuffer.reservedsectors = 1;
+ bootbuffer.fatcopies = 2;
+ bootbuffer.rootdirentries = 64;
+ bootbuffer.totalsectorcount = 320;
+ bootbuffer.mediadescriptor = mdesc;
+ bootbuffer.sectorsperfat = 1;
+ bootbuffer.sectorspertrack = 8;
+ bootbuffer.headcount = 1;
+ bootbuffer.magic1 = 0x55; // to silence warning
+ bootbuffer.magic2 = 0xaa;
+ if (!(mdesc & 0x2)) {
+ /* Adjust for 9 sectors per track */
+ bootbuffer.totalsectorcount = 360;
+ bootbuffer.sectorsperfat = 2;
+ bootbuffer.sectorspertrack = 9;
+ }
+ if (mdesc & 0x1) {
+ /* Adjust for 2 sides */
+ bootbuffer.sectorspercluster = 2;
+ bootbuffer.rootdirentries = 112;
+ bootbuffer.totalsectorcount *= 2;
+ bootbuffer.headcount = 2;
+ }
+ } else {
+ /* Unknown format */
+ created_successfully = false;
+ return;
+ }
+ }
+ }
+
+ if ((bootbuffer.magic1 != 0x55) || (bootbuffer.magic2 != 0xaa)) {
+ /* Not a FAT filesystem */
+ LOG_MSG("Loaded image has no valid magicnumbers at the end!");
+ }
+
+ /* Sanity checks */
+ if ((bootbuffer.sectorsperfat == 0) || // FAT32 not implemented yet
+ (bootbuffer.bytespersector != 512) || // non-standard sector sizes not implemented
+ (bootbuffer.sectorspercluster == 0) ||
+ (bootbuffer.rootdirentries == 0) ||
+ (bootbuffer.fatcopies == 0) ||
+ (bootbuffer.headcount == 0) ||
+ (bootbuffer.headcount > headscyl) ||
+ (bootbuffer.sectorspertrack == 0) ||
+ (bootbuffer.sectorspertrack > cylsector)) {
+ created_successfully = false;
+ return;
+ }
+
+ /* Filesystem must be contiguous to use absolute sectors, otherwise CHS will be used */
+ absolute = ((bootbuffer.headcount == headscyl) && (bootbuffer.sectorspertrack == cylsector));
+
+ /* Determine FAT format, 12, 16 or 32 */
+
+ /* Get size of root dir in sectors */
+ Bit32u RootDirSectors = ((bootbuffer.rootdirentries * 32) + (bootbuffer.bytespersector - 1)) / bootbuffer.bytespersector;
+ Bit32u DataSectors;
+ if(bootbuffer.totalsectorcount != 0) {
+ DataSectors = bootbuffer.totalsectorcount - (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors);
+ } else {
+ DataSectors = bootbuffer.totalsecdword - (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors);
+
+ }
+ CountOfClusters = DataSectors / bootbuffer.sectorspercluster;
+
+ firstDataSector = (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors) + partSectOff;
+ firstRootDirSect = bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + partSectOff;
+
+ if(CountOfClusters < 4085) {
+ /* Volume is FAT12 */
+ LOG_MSG("Mounted FAT volume is FAT12 with %d clusters", CountOfClusters);
+ fattype = FAT12;
+ } else if (CountOfClusters < 65525) {
+ LOG_MSG("Mounted FAT volume is FAT16 with %d clusters", CountOfClusters);
+ fattype = FAT16;
+ } else {
+ LOG_MSG("Mounted FAT volume is FAT32 with %d clusters", CountOfClusters);
+ fattype = FAT32;
+ }
+
+ /* There is no cluster 0, this means we are in the root directory */
+ cwdDirCluster = 0;
+
+ memset(fatSectBuffer,0,1024);
+ curFatSect = 0xffffffff;
+
+ strcpy(info, "fatDrive ");
+ strcat(info, sysFilename);
+}
+
+bool fatDrive::AllocationInfo(Bit16u *_bytes_sector, Bit8u *_sectors_cluster, Bit16u *_total_clusters, Bit16u *_free_clusters) {
+ Bit32u hs, cy, sect,sectsize;
+ Bit32u countFree = 0;
+ Bit32u i;
+
+ loadedDisk->Get_Geometry(&hs, &cy, &sect, &sectsize);
+ *_bytes_sector = (Bit16u)sectsize;
+ *_sectors_cluster = bootbuffer.sectorspercluster;
+ if (CountOfClusters<65536) *_total_clusters = (Bit16u)CountOfClusters;
+ else {
+ // maybe some special handling needed for fat32
+ *_total_clusters = 65535;
+ }
+ for(i=0;i<CountOfClusters;i++) if(!getClusterValue(i+2)) countFree++;
+ if (countFree<65536) *_free_clusters = (Bit16u)countFree;
+ else {
+ // maybe some special handling needed for fat32
+ *_free_clusters = 65535;
+ }
+
+ return true;
+}
+
+Bit32u fatDrive::getFirstFreeClust(void) {
+ Bit32u i;
+ for(i=0;i<CountOfClusters;i++) {
+ if(!getClusterValue(i+2)) return (i+2);
+ }
+
+ /* No free cluster found */
+ return 0;
+}
+
+bool fatDrive::isRemote(void) { return false; }
+bool fatDrive::isRemovable(void) { return false; }
+
+Bits fatDrive::UnMount(void) {
+ delete this;
+ return 0;
+}
+
+Bit8u fatDrive::GetMediaByte(void) { return loadedDisk->GetBiosType(); }
+
+bool fatDrive::FileCreate(DOS_File **file, char *name, Bit16u attributes) {
+ direntry fileEntry;
+ Bit32u dirClust, subEntry;
+ char dirName[DOS_NAMELENGTH_ASCII];
+ char pathName[11];
+
+ Bit16u save_errorcode=dos.errorcode;
+
+ /* Check if file already exists */
+ if(getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
+ /* Truncate file */
+ fileEntry.entrysize=0;
+ directoryChange(dirClust, &fileEntry, subEntry);
+ if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust, 0);
+ } else {
+ /* Can we even get the name of the file itself? */
+ if(!getEntryName(name, &dirName[0])) return false;
+ convToDirFile(&dirName[0], &pathName[0]);
+
+ /* Can we find the base directory? */
+ if(!getDirClustNum(name, &dirClust, true)) return false;
+ memset(&fileEntry, 0, sizeof(direntry));
+ memcpy(&fileEntry.entryname, &pathName[0], 11);
+ fileEntry.attrib = (Bit8u)(attributes & 0xff);
+ addDirectoryEntry(dirClust, fileEntry);
+
+ /* Check if file exists now */
+ if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
+ }
+
+ /* Empty file created, now lets open it */
+ /* TODO: check for read-only flag and requested write access */
+ *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
+ (*file)->flags=OPEN_READWRITE;
+ ((fatFile *)(*file))->dirCluster = dirClust;
+ ((fatFile *)(*file))->dirIndex = subEntry;
+ /* Maybe modTime and date should be used ? (crt matches findnext) */
+ ((fatFile *)(*file))->time = fileEntry.crtTime;
+ ((fatFile *)(*file))->date = fileEntry.crtDate;
+
+ dos.errorcode=save_errorcode;
+ return true;
+}
+
+bool fatDrive::FileExists(const char *name) {
+ direntry fileEntry;
+ Bit32u dummy1, dummy2;
+ if(!getFileDirEntry(name, &fileEntry, &dummy1, &dummy2)) return false;
+ return true;
+}
+
+bool fatDrive::FileOpen(DOS_File **file, char *name, Bit32u flags) {
+ direntry fileEntry;
+ Bit32u dirClust, subEntry;
+ if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
+ /* TODO: check for read-only flag and requested write access */
+ *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
+ (*file)->flags = flags;
+ ((fatFile *)(*file))->dirCluster = dirClust;
+ ((fatFile *)(*file))->dirIndex = subEntry;
+ /* Maybe modTime and date should be used ? (crt matches findnext) */
+ ((fatFile *)(*file))->time = fileEntry.crtTime;
+ ((fatFile *)(*file))->date = fileEntry.crtDate;
+ return true;
+}
+
+bool fatDrive::FileStat(const char * /*name*/, FileStat_Block *const /*stat_block*/) {
+ /* TODO: Stub */
+ return false;
+}
+
+bool fatDrive::FileUnlink(char * name) {
+ direntry fileEntry;
+ Bit32u dirClust, subEntry;
+
+ if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
+
+ fileEntry.entryname[0] = 0xe5;
+ directoryChange(dirClust, &fileEntry, subEntry);
+
+ if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust, 0);
+
+ return true;
+}
+
+bool fatDrive::FindFirst(char *_dir, DOS_DTA &dta,bool /*fcb_findfirst*/) {
+ direntry dummyClust;
+#if 0
+ Bit8u attr;char pattern[DOS_NAMELENGTH_ASCII];
+ dta.GetSearchParams(attr,pattern);
+ if(attr==DOS_ATTR_VOLUME) {
+ if (strcmp(GetLabel(), "") == 0 ) {
+ DOS_SetError(DOSERR_NO_MORE_FILES);
+ return false;
+ }
+ dta.SetResult(GetLabel(),0,0,0,DOS_ATTR_VOLUME);
+ return true;
+ }
+ if(attr & DOS_ATTR_VOLUME) //check for root dir or fcb_findfirst
+ LOG(LOG_DOSMISC,LOG_WARN)("findfirst for volumelabel used on fatDrive. Unhandled!!!!!");
+#endif
+ if(!getDirClustNum(_dir, &cwdDirCluster, false)) {
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ return false;
+ }
+ dta.SetDirID(0);
+ dta.SetDirIDCluster((Bit16u)(cwdDirCluster&0xffff));
+ return FindNextInternal(cwdDirCluster, dta, &dummyClust);
+}
+
+char* removeTrailingSpaces(char* str) {
+ char* end = str + strlen(str);
+ while((*--end == ' ') && (end > str)) {};
+ *++end = '\0';
+ return str;
+}
+
+char* removeLeadingSpaces(char* str) {
+ size_t len = strlen(str);
+ size_t pos = strspn(str," ");
+ memmove(str,str + pos,len - pos + 1);
+ return str;
+}
+
+char* trimString(char* str) {
+ return removeTrailingSpaces(removeLeadingSpaces(str));
+}
+
+bool fatDrive::FindNextInternal(Bit32u dirClustNumber, DOS_DTA &dta, direntry *foundEntry) {
+ direntry sectbuf[16]; /* 16 directory entries per sector */
+ Bit32u logentsector; /* Logical entry sector */
+ Bit32u entryoffset; /* Index offset within sector */
+ Bit32u tmpsector;
+ Bit8u attrs;
+ Bit16u dirPos;
+ char srch_pattern[DOS_NAMELENGTH_ASCII];
+ char find_name[DOS_NAMELENGTH_ASCII];
+ char extension[4];
+
+ dta.GetSearchParams(attrs, srch_pattern);
+ dirPos = dta.GetDirID();
+
+nextfile:
+ logentsector = dirPos / 16;
+ entryoffset = dirPos % 16;
+
+ if(dirClustNumber==0) {
+ if(dirPos >= bootbuffer.rootdirentries) {
+ DOS_SetError(DOSERR_NO_MORE_FILES);
+ return false;
+ }
+ readSector(firstRootDirSect+logentsector,sectbuf);
+ } else {
+ tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
+ /* A zero sector number can't happen */
+ if(tmpsector == 0) {
+ DOS_SetError(DOSERR_NO_MORE_FILES);
+ return false;
+ }
+ readSector(tmpsector,sectbuf);
+ }
+ dirPos++;
+ dta.SetDirID(dirPos);
+
+ /* Deleted file entry */
+ if (sectbuf[entryoffset].entryname[0] == 0xe5) goto nextfile;
+
+ /* End of directory list */
+ if (sectbuf[entryoffset].entryname[0] == 0x00) {
+ DOS_SetError(DOSERR_NO_MORE_FILES);
+ return false;
+ }
+ memset(find_name,0,DOS_NAMELENGTH_ASCII);
+ memset(extension,0,4);
+ memcpy(find_name,&sectbuf[entryoffset].entryname[0],8);
+ memcpy(extension,&sectbuf[entryoffset].entryname[8],3);
+ trimString(&find_name[0]);
+ trimString(&extension[0]);
+
+ //if(!(sectbuf[entryoffset].attrib & DOS_ATTR_DIRECTORY))
+ if (extension[0]!=0) {
+ strcat(find_name, ".");
+ strcat(find_name, extension);
+ }
+
+ /* Compare attributes to search attributes */
+
+ //TODO What about attrs = DOS_ATTR_VOLUME|DOS_ATTR_DIRECTORY ?
+ if (attrs == DOS_ATTR_VOLUME) {
+ if (!(sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME)) goto nextfile;
+ dirCache.SetLabel(find_name, false, true);
+ } else {
+ if (~attrs & sectbuf[entryoffset].attrib & (DOS_ATTR_DIRECTORY | DOS_ATTR_VOLUME | DOS_ATTR_SYSTEM | DOS_ATTR_HIDDEN) ) goto nextfile;
+ }
+
+
+ /* Compare name to search pattern */
+ if(!WildFileCmp(find_name,srch_pattern)) goto nextfile;
+
+ //dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].crtDate, sectbuf[entryoffset].crtTime, sectbuf[entryoffset].attrib);
+
+ dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].modDate, sectbuf[entryoffset].modTime, sectbuf[entryoffset].attrib);
+
+ memcpy(foundEntry, &sectbuf[entryoffset], sizeof(direntry));
+
+ return true;
+}
+
+bool fatDrive::FindNext(DOS_DTA &dta) {
+ direntry dummyClust;
+
+ return FindNextInternal(dta.GetDirIDCluster(), dta, &dummyClust);
+}
+
+bool fatDrive::GetFileAttr(char *name, Bit16u *attr) {
+ direntry fileEntry;
+ Bit32u dirClust, subEntry;
+ if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
+ char dirName[DOS_NAMELENGTH_ASCII];
+ char pathName[11];
+
+ /* Can we even get the name of the directory itself? */
+ if(!getEntryName(name, &dirName[0])) return false;
+ convToDirFile(&dirName[0], &pathName[0]);
+
+ /* Get parent directory starting cluster */
+ if(!getDirClustNum(name, &dirClust, true)) return false;
+
+ /* Find directory entry in parent directory */
+ Bit32s fileidx = 2;
+ if (dirClust==0) fileidx = 0; // root directory
+ Bit32s last_idx=0;
+ while(directoryBrowse(dirClust, &fileEntry, fileidx, last_idx)) {
+ if(memcmp(&fileEntry.entryname, &pathName[0], 11) == 0) {
+ *attr=fileEntry.attrib;
+ return true;
+ }
+ last_idx=fileidx;
+ fileidx++;
+ }
+ return false;
+ } else *attr=fileEntry.attrib;
+ return true;
+}
+
+bool fatDrive::directoryBrowse(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum, Bit32s start/*=0*/) {
+ direntry sectbuf[16]; /* 16 directory entries per sector */
+ Bit32u logentsector; /* Logical entry sector */
+ Bit32u entryoffset = 0; /* Index offset within sector */
+ Bit32u tmpsector;
+ if ((start<0) || (start>65535)) return false;
+ Bit16u dirPos = (Bit16u)start;
+ if (entNum<start) return false;
+ entNum-=start;
+
+ while(entNum>=0) {
+
+ logentsector = dirPos / 16;
+ entryoffset = dirPos % 16;
+
+ if(dirClustNumber==0) {
+ if(dirPos >= bootbuffer.rootdirentries) return false;
+ tmpsector = firstRootDirSect+logentsector;
+ readSector(tmpsector,sectbuf);
+ } else {
+ tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
+ /* A zero sector number can't happen */
+ if(tmpsector == 0) return false;
+ readSector(tmpsector,sectbuf);
+ }
+ dirPos++;
+
+
+ /* End of directory list */
+ if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
+ --entNum;
+ }
+
+ memcpy(useEntry, &sectbuf[entryoffset],sizeof(direntry));
+ return true;
+}
+
+bool fatDrive::directoryChange(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum) {
+ direntry sectbuf[16]; /* 16 directory entries per sector */
+ Bit32u logentsector; /* Logical entry sector */
+ Bit32u entryoffset = 0; /* Index offset within sector */
+ Bit32u tmpsector = 0;
+ Bit16u dirPos = 0;
+
+ while(entNum>=0) {
+
+ logentsector = dirPos / 16;
+ entryoffset = dirPos % 16;
+
+ if(dirClustNumber==0) {
+ if(dirPos >= bootbuffer.rootdirentries) return false;
+ tmpsector = firstRootDirSect+logentsector;
+ readSector(tmpsector,sectbuf);
+ } else {
+ tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
+ /* A zero sector number can't happen */
+ if(tmpsector == 0) return false;
+ readSector(tmpsector,sectbuf);
+ }
+ dirPos++;
+
+
+ /* End of directory list */
+ if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
+ --entNum;
+ }
+ if(tmpsector != 0) {
+ memcpy(&sectbuf[entryoffset], useEntry, sizeof(direntry));
+ writeSector(tmpsector, sectbuf);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool fatDrive::addDirectoryEntry(Bit32u dirClustNumber, direntry useEntry) {
+ direntry sectbuf[16]; /* 16 directory entries per sector */
+ Bit32u logentsector; /* Logical entry sector */
+ Bit32u entryoffset; /* Index offset within sector */
+ Bit32u tmpsector;
+ Bit16u dirPos = 0;
+
+ for(;;) {
+
+ logentsector = dirPos / 16;
+ entryoffset = dirPos % 16;
+
+ if(dirClustNumber==0) {
+ if(dirPos >= bootbuffer.rootdirentries) return false;
+ tmpsector = firstRootDirSect+logentsector;
+ readSector(tmpsector,sectbuf);
+ } else {
+ tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
+ /* A zero sector number can't happen - we need to allocate more room for this directory*/
+ if(tmpsector == 0) {
+ Bit32u newClust;
+ newClust = appendCluster(dirClustNumber);
+ if(newClust == 0) return false;
+ /* Try again to get tmpsector */
+ tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
+ if(tmpsector == 0) return false; /* Give up if still can't get more room for directory */
+ }
+ readSector(tmpsector,sectbuf);
+ }
+ dirPos++;
+
+ /* Deleted file entry or end of directory list */
+ if ((sectbuf[entryoffset].entryname[0] == 0xe5) || (sectbuf[entryoffset].entryname[0] == 0x00)) {
+ sectbuf[entryoffset] = useEntry;
+ writeSector(tmpsector,sectbuf);
+ break;
+ }
+ }
+
+ return true;
+}
+
+void fatDrive::zeroOutCluster(Bit32u clustNumber) {
+ Bit8u secBuffer[512];
+
+ memset(&secBuffer[0], 0, 512);
+
+ int i;
+ for(i=0;i<bootbuffer.sectorspercluster;i++) {
+ writeSector(getAbsoluteSectFromChain(clustNumber,i), &secBuffer[0]);
+ }
+}
+
+bool fatDrive::MakeDir(char *dir) {
+ Bit32u dummyClust, dirClust;
+ direntry tmpentry;
+ char dirName[DOS_NAMELENGTH_ASCII];
+ char pathName[11];
+
+ /* Can we even get the name of the directory itself? */
+ if(!getEntryName(dir, &dirName[0])) return false;
+ convToDirFile(&dirName[0], &pathName[0]);
+
+ /* Fail to make directory if already exists */
+ if(getDirClustNum(dir, &dummyClust, false)) return false;
+
+ dummyClust = getFirstFreeClust();
+ /* No more space */
+ if(dummyClust == 0) return false;
+
+ if(!allocateCluster(dummyClust, 0)) return false;
+
+ zeroOutCluster(dummyClust);
+
+ /* Can we find the base directory? */
+ if(!getDirClustNum(dir, &dirClust, true)) return false;
+
+ /* Add the new directory to the base directory */
+ memset(&tmpentry,0, sizeof(direntry));
+ memcpy(&tmpentry.entryname, &pathName[0], 11);
+ tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
+ tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
+ tmpentry.attrib = DOS_ATTR_DIRECTORY;
+ addDirectoryEntry(dirClust, tmpentry);
+
+ /* Add the [.] and [..] entries to our new directory*/
+ /* [.] entry */
+ memset(&tmpentry,0, sizeof(direntry));
+ memcpy(&tmpentry.entryname, ". ", 11);
+ tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
+ tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
+ tmpentry.attrib = DOS_ATTR_DIRECTORY;
+ addDirectoryEntry(dummyClust, tmpentry);
+
+ /* [..] entry */
+ memset(&tmpentry,0, sizeof(direntry));
+ memcpy(&tmpentry.entryname, ".. ", 11);
+ tmpentry.loFirstClust = (Bit16u)(dirClust & 0xffff);
+ tmpentry.hiFirstClust = (Bit16u)(dirClust >> 16);
+ tmpentry.attrib = DOS_ATTR_DIRECTORY;
+ addDirectoryEntry(dummyClust, tmpentry);
+
+ return true;
+}
+
+bool fatDrive::RemoveDir(char *dir) {
+ Bit32u dummyClust, dirClust;
+ direntry tmpentry;
+ char dirName[DOS_NAMELENGTH_ASCII];
+ char pathName[11];
+
+ /* Can we even get the name of the directory itself? */
+ if(!getEntryName(dir, &dirName[0])) return false;
+ convToDirFile(&dirName[0], &pathName[0]);
+
+ /* Get directory starting cluster */
+ if(!getDirClustNum(dir, &dummyClust, false)) return false;
+
+ /* Can't remove root directory */
+ if(dummyClust == 0) return false;
+
+ /* Get parent directory starting cluster */
+ if(!getDirClustNum(dir, &dirClust, true)) return false;
+
+ /* Check to make sure directory is empty */
+ Bit32u filecount = 0;
+ /* Set to 2 to skip first 2 entries, [.] and [..] */
+ Bit32s fileidx = 2;
+ while(directoryBrowse(dummyClust, &tmpentry, fileidx)) {
+ /* Check for non-deleted files */
+ if(tmpentry.entryname[0] != 0xe5) filecount++;
+ fileidx++;
+ }
+
+ /* Return if directory is not empty */
+ if(filecount > 0) return false;
+
+ /* Find directory entry in parent directory */
+ if (dirClust==0) fileidx = 0; // root directory
+ else fileidx = 2;
+ bool found = false;
+ while(directoryBrowse(dirClust, &tmpentry, fileidx)) {
+ if(memcmp(&tmpentry.entryname, &pathName[0], 11) == 0) {
+ found = true;
+ tmpentry.entryname[0] = 0xe5;
+ directoryChange(dirClust, &tmpentry, fileidx);
+ deleteClustChain(dummyClust, 0);
+
+ break;
+ }
+ fileidx++;
+ }
+
+ if(!found) return false;
+
+ return true;
+}
+
+bool fatDrive::Rename(char * oldname, char * newname) {
+ direntry fileEntry1;
+ Bit32u dirClust1, subEntry1;
+ if(!getFileDirEntry(oldname, &fileEntry1, &dirClust1, &subEntry1)) return false;
+ /* File to be renamed really exists */
+
+ direntry fileEntry2;
+ Bit32u dirClust2, subEntry2;
+
+ /* Check if file already exists */
+ if(!getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2)) {
+ /* Target doesn't exist, can rename */
+
+ char dirName2[DOS_NAMELENGTH_ASCII];
+ char pathName2[11];
+ /* Can we even get the name of the file itself? */
+ if(!getEntryName(newname, &dirName2[0])) return false;
+ convToDirFile(&dirName2[0], &pathName2[0]);
+
+ /* Can we find the base directory? */
+ if(!getDirClustNum(newname, &dirClust2, true)) return false;
+ memcpy(&fileEntry2, &fileEntry1, sizeof(direntry));
+ memcpy(&fileEntry2.entryname, &pathName2[0], 11);
+ addDirectoryEntry(dirClust2, fileEntry2);
+
+ /* Check if file exists now */
+ if(!getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2)) return false;
+
+ /* Remove old entry */
+ fileEntry1.entryname[0] = 0xe5;
+ directoryChange(dirClust1, &fileEntry1, subEntry1);
+
+ return true;
+ }
+
+ /* Target already exists, fail */
+ return false;
+}
+
+bool fatDrive::TestDir(char *dir) {
+ Bit32u dummyClust;
+ return getDirClustNum(dir, &dummyClust, false);
+}
+
diff --git a/src/dos/drive_iso.cpp b/src/dos/drive_iso.cpp
new file mode 100644
index 000000000..64e95ef31
--- /dev/null
+++ b/src/dos/drive_iso.cpp
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <cctype>
+#include <cstring>
+#include "cdrom.h"
+#include "dosbox.h"
+#include "dos_system.h"
+#include "support.h"
+#include "drives.h"
+
+#define FLAGS1 ((iso) ? de.fileFlags : de.timeZone)
+#define FLAGS2 ((iso) ? de->fileFlags : de->timeZone)
+
+using namespace std;
+
+class isoFile : public DOS_File {
+public:
+ isoFile(isoDrive *drive, const char *name, FileStat_Block *stat, Bit32u offset);
+ bool Read(Bit8u *data, Bit16u *size);
+ bool Write(Bit8u *data, Bit16u *size);
+ bool Seek(Bit32u *pos, Bit32u type);
+ bool Close();
+ Bit16u GetInformation(void);
+private:
+ isoDrive *drive;
+ Bit8u buffer[ISO_FRAMESIZE];
+ int cachedSector;
+ Bit32u fileBegin;
+ Bit32u filePos;
+ Bit32u fileEnd;
+ Bit16u info;
+};
+
+isoFile::isoFile(isoDrive *drive, const char *name, FileStat_Block *stat, Bit32u offset) {
+ this->drive = drive;
+ time = stat->time;
+ date = stat->date;
+ attr = stat->attr;
+ fileBegin = offset;
+ filePos = fileBegin;
+ fileEnd = fileBegin + stat->size;
+ cachedSector = -1;
+ open = true;
+ this->name = NULL;
+ SetName(name);
+}
+
+bool isoFile::Read(Bit8u *data, Bit16u *size) {
+ if (filePos + *size > fileEnd)
+ *size = (Bit16u)(fileEnd - filePos);
+
+ Bit16u nowSize = 0;
+ int sector = filePos / ISO_FRAMESIZE;
+ Bit16u sectorPos = (Bit16u)(filePos % ISO_FRAMESIZE);
+
+ if (sector != cachedSector) {
+ if (drive->readSector(buffer, sector)) cachedSector = sector;
+ else { *size = 0; cachedSector = -1; }
+ }
+ while (nowSize < *size) {
+ Bit16u remSector = ISO_FRAMESIZE - sectorPos;
+ Bit16u remSize = *size - nowSize;
+ if(remSector < remSize) {
+ memcpy(&data[nowSize], &buffer[sectorPos], remSector);
+ nowSize += remSector;
+ sectorPos = 0;
+ sector++;
+ cachedSector++;
+ if (!drive->readSector(buffer, sector)) {
+ *size = nowSize;
+ cachedSector = -1;
+ }
+ } else {
+ memcpy(&data[nowSize], &buffer[sectorPos], remSize);
+ nowSize += remSize;
+ }
+
+ }
+
+ *size = nowSize;
+ filePos += *size;
+ return true;
+}
+
+bool isoFile::Write(Bit8u* /*data*/, Bit16u* /*size*/) {
+ return false;
+}
+
+bool isoFile::Seek(Bit32u *pos, Bit32u type) {
+ switch (type) {
+ case DOS_SEEK_SET:
+ filePos = fileBegin + *pos;
+ break;
+ case DOS_SEEK_CUR:
+ filePos += *pos;
+ break;
+ case DOS_SEEK_END:
+ filePos = fileEnd + *pos;
+ break;
+ default:
+ return false;
+ }
+ if (filePos > fileEnd || filePos < fileBegin)
+ filePos = fileEnd;
+
+ *pos = filePos - fileBegin;
+ return true;
+}
+
+bool isoFile::Close() {
+ if (refCtr == 1) open = false;
+ return true;
+}
+
+Bit16u isoFile::GetInformation(void) {
+ return 0x40; // read-only drive
+}
+
+int MSCDEX_RemoveDrive(char driveLetter);
+int MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit);
+void MSCDEX_ReplaceDrive(CDROM_Interface* cdrom, Bit8u subUnit);
+bool MSCDEX_HasDrive(char driveLetter);
+bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name);
+Bit8u MSCDEX_GetSubUnit(char driveLetter);
+
+isoDrive::isoDrive(char driveLetter, const char *fileName, Bit8u mediaid, int &error)
+ :iso(false),
+ dataCD(false),
+ mediaid(0),
+ subUnit(0),
+ driveLetter('\0')
+ {
+ this->fileName[0] = '\0';
+ this->discLabel[0] = '\0';
+ nextFreeDirIterator = 0;
+ memset(dirIterators, 0, sizeof(dirIterators));
+ memset(sectorHashEntries, 0, sizeof(sectorHashEntries));
+ memset(&rootEntry, 0, sizeof(isoDirEntry));
+
+ safe_strncpy(this->fileName, fileName, CROSS_LEN);
+ error = UpdateMscdex(driveLetter, fileName, subUnit);
+
+ if (!error) {
+ if (loadImage()) {
+ strcpy(info, "isoDrive ");
+ strcat(info, fileName);
+ this->driveLetter = driveLetter;
+ this->mediaid = mediaid;
+ char buffer[32] = { 0 };
+ if (!MSCDEX_GetVolumeName(subUnit, buffer)) strcpy(buffer, "");
+ Set_Label(buffer,discLabel,true);
+
+ } else if (CDROM_Interface_Image::images[subUnit]->HasDataTrack() == false) { //Audio only cdrom
+ strcpy(info, "isoDrive ");
+ strcat(info, fileName);
+ this->driveLetter = driveLetter;
+ this->mediaid = mediaid;
+ char buffer[32] = { 0 };
+ strcpy(buffer, "Audio_CD");
+ Set_Label(buffer,discLabel,true);
+ } else error = 6; //Corrupt image
+ }
+}
+
+isoDrive::~isoDrive() { }
+
+int isoDrive::UpdateMscdex(char driveLetter, const char* path, Bit8u& subUnit) {
+ if (MSCDEX_HasDrive(driveLetter)) {
+ subUnit = MSCDEX_GetSubUnit(driveLetter);
+ CDROM_Interface_Image* oldCdrom = CDROM_Interface_Image::images[subUnit];
+ CDROM_Interface* cdrom = new CDROM_Interface_Image(subUnit);
+ char pathCopy[CROSS_LEN];
+ safe_strncpy(pathCopy, path, CROSS_LEN);
+ if (!cdrom->SetDevice(pathCopy, 0)) {
+ CDROM_Interface_Image::images[subUnit] = oldCdrom;
+ delete cdrom;
+ return 3;
+ }
+ MSCDEX_ReplaceDrive(cdrom, subUnit);
+ return 0;
+ } else {
+ return MSCDEX_AddDrive(driveLetter, path, subUnit);
+ }
+}
+
+void isoDrive::Activate(void) {
+ UpdateMscdex(driveLetter, fileName, subUnit);
+}
+
+bool isoDrive::FileOpen(DOS_File **file, char *name, Bit32u flags) {
+ if ((flags & 0x0f) == OPEN_WRITE) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+
+ isoDirEntry de;
+ bool success = lookup(&de, name) && !IS_DIR(FLAGS1);
+
+ if (success) {
+ FileStat_Block file_stat;
+ file_stat.size = DATA_LENGTH(de);
+ file_stat.attr = DOS_ATTR_ARCHIVE | DOS_ATTR_READ_ONLY;
+ file_stat.date = DOS_PackDate(1900 + de.dateYear, de.dateMonth, de.dateDay);
+ file_stat.time = DOS_PackTime(de.timeHour, de.timeMin, de.timeSec);
+ *file = new isoFile(this, name, &file_stat, EXTENT_LOCATION(de) * ISO_FRAMESIZE);
+ (*file)->flags = flags;
+ }
+ return success;
+}
+
+bool isoDrive::FileCreate(DOS_File** /*file*/, char* /*name*/, Bit16u /*attributes*/) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+}
+
+bool isoDrive::FileUnlink(char* /*name*/) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+}
+
+bool isoDrive::RemoveDir(char* /*dir*/) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+}
+
+bool isoDrive::MakeDir(char* /*dir*/) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+}
+
+bool isoDrive::TestDir(char *dir) {
+ isoDirEntry de;
+ return (lookup(&de, dir) && IS_DIR(FLAGS1));
+}
+
+bool isoDrive::FindFirst(char *dir, DOS_DTA &dta, bool fcb_findfirst) {
+ isoDirEntry de;
+ if (!lookup(&de, dir)) {
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ return false;
+ }
+
+ // get a directory iterator and save its id in the dta
+ int dirIterator = GetDirIterator(&de);
+ bool isRoot = (*dir == 0);
+ dirIterators[dirIterator].root = isRoot;
+ dta.SetDirID((Bit16u)dirIterator);
+
+ Bit8u attr;
+ char pattern[ISO_MAXPATHNAME];
+ dta.GetSearchParams(attr, pattern);
+
+ if (attr == DOS_ATTR_VOLUME) {
+ dta.SetResult(discLabel, 0, 0, 0, DOS_ATTR_VOLUME);
+ return true;
+ } else if ((attr & DOS_ATTR_VOLUME) && isRoot && !fcb_findfirst) {
+ if (WildFileCmp(discLabel,pattern)) {
+ // Get Volume Label (DOS_ATTR_VOLUME) and only in basedir and if it matches the searchstring
+ dta.SetResult(discLabel, 0, 0, 0, DOS_ATTR_VOLUME);
+ return true;
+ }
+ }
+
+ return FindNext(dta);
+}
+
+bool isoDrive::FindNext(DOS_DTA &dta) {
+ Bit8u attr;
+ char pattern[DOS_NAMELENGTH_ASCII];
+ dta.GetSearchParams(attr, pattern);
+
+ int dirIterator = dta.GetDirID();
+ bool isRoot = dirIterators[dirIterator].root;
+
+ isoDirEntry de;
+ while (GetNextDirEntry(dirIterator, &de)) {
+ Bit8u findAttr = 0;
+ if (IS_DIR(FLAGS1)) findAttr |= DOS_ATTR_DIRECTORY;
+ else findAttr |= DOS_ATTR_ARCHIVE;
+ if (IS_HIDDEN(FLAGS1)) findAttr |= DOS_ATTR_HIDDEN;
+
+ if (!IS_ASSOC(FLAGS1) && !(isRoot && de.ident[0]=='.') && WildFileCmp((char*)de.ident, pattern)
+ && !(~attr & findAttr & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM))) {
+
+ /* file is okay, setup everything to be copied in DTA Block */
+ char findName[DOS_NAMELENGTH_ASCII];
+ findName[0] = 0;
+ if(strlen((char*)de.ident) < DOS_NAMELENGTH_ASCII) {
+ strcpy(findName, (char*)de.ident);
+ upcase(findName);
+ }
+ Bit32u findSize = DATA_LENGTH(de);
+ Bit16u findDate = DOS_PackDate(1900 + de.dateYear, de.dateMonth, de.dateDay);
+ Bit16u findTime = DOS_PackTime(de.timeHour, de.timeMin, de.timeSec);
+ dta.SetResult(findName, findSize, findDate, findTime, findAttr);
+ return true;
+ }
+ }
+ // after searching the directory, free the iterator
+ FreeDirIterator(dirIterator);
+
+ DOS_SetError(DOSERR_NO_MORE_FILES);
+ return false;
+}
+
+bool isoDrive::Rename(char* /*oldname*/, char* /*newname*/) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+}
+
+bool isoDrive::GetFileAttr(char *name, Bit16u *attr) {
+ *attr = 0;
+ isoDirEntry de;
+ bool success = lookup(&de, name);
+ if (success) {
+ *attr = DOS_ATTR_ARCHIVE | DOS_ATTR_READ_ONLY;
+ if (IS_HIDDEN(FLAGS1)) *attr |= DOS_ATTR_HIDDEN;
+ if (IS_DIR(FLAGS1)) *attr |= DOS_ATTR_DIRECTORY;
+ }
+ return success;
+}
+
+bool isoDrive::AllocationInfo(Bit16u *bytes_sector, Bit8u *sectors_cluster, Bit16u *total_clusters, Bit16u *free_clusters) {
+ *bytes_sector = 2048;
+ *sectors_cluster = 1; // cluster size for cdroms ?
+ *total_clusters = 65535;
+ *free_clusters = 0;
+ return true;
+}
+
+bool isoDrive::FileExists(const char *name) {
+ isoDirEntry de;
+ return (lookup(&de, name) && !IS_DIR(FLAGS1));
+}
+
+bool isoDrive::FileStat(const char *name, FileStat_Block *const stat_block) {
+ isoDirEntry de;
+ bool success = lookup(&de, name);
+
+ if (success) {
+ stat_block->date = DOS_PackDate(1900 + de.dateYear, de.dateMonth, de.dateDay);
+ stat_block->time = DOS_PackTime(de.timeHour, de.timeMin, de.timeSec);
+ stat_block->size = DATA_LENGTH(de);
+ stat_block->attr = DOS_ATTR_ARCHIVE | DOS_ATTR_READ_ONLY;
+ if (IS_DIR(FLAGS1)) stat_block->attr |= DOS_ATTR_DIRECTORY;
+ }
+
+ return success;
+}
+
+Bit8u isoDrive::GetMediaByte(void) {
+ return mediaid;
+}
+
+bool isoDrive::isRemote(void) {
+ return true;
+}
+
+bool isoDrive::isRemovable(void) {
+ return true;
+}
+
+Bits isoDrive::UnMount(void) {
+ if(MSCDEX_RemoveDrive(driveLetter)) {
+ delete this;
+ return 0;
+ }
+ return 2;
+}
+
+int isoDrive::GetDirIterator(const isoDirEntry* de) {
+ int dirIterator = nextFreeDirIterator;
+
+ // get start and end sector of the directory entry (pad end sector if necessary)
+ dirIterators[dirIterator].currentSector = EXTENT_LOCATION(*de);
+ dirIterators[dirIterator].endSector =
+ EXTENT_LOCATION(*de) + DATA_LENGTH(*de) / ISO_FRAMESIZE - 1;
+ if (DATA_LENGTH(*de) % ISO_FRAMESIZE != 0)
+ dirIterators[dirIterator].endSector++;
+
+ // reset position and mark as valid
+ dirIterators[dirIterator].pos = 0;
+ dirIterators[dirIterator].valid = true;
+
+ // advance to next directory iterator (wrap around if necessary)
+ nextFreeDirIterator = (nextFreeDirIterator + 1) % MAX_OPENDIRS;
+
+ return dirIterator;
+}
+
+bool isoDrive::GetNextDirEntry(const int dirIteratorHandle, isoDirEntry* de) {
+ bool result = false;
+ Bit8u* buffer = NULL;
+ DirIterator& dirIterator = dirIterators[dirIteratorHandle];
+
+ // check if the directory entry is valid
+ if (dirIterator.valid && ReadCachedSector(&buffer, dirIterator.currentSector)) {
+ // check if the next sector has to be read
+ if ((dirIterator.pos >= ISO_FRAMESIZE)
+ || (buffer[dirIterator.pos] == 0)
+ || (dirIterator.pos + buffer[dirIterator.pos] > ISO_FRAMESIZE)) {
+
+ // check if there is another sector available
+ if (dirIterator.currentSector < dirIterator.endSector) {
+ dirIterator.pos = 0;
+ dirIterator.currentSector++;
+ if (!ReadCachedSector(&buffer, dirIterator.currentSector)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ // read sector and advance sector pointer
+ int length = readDirEntry(de, &buffer[dirIterator.pos]);
+ result = length >= 0;
+ dirIterator.pos += length;
+ }
+ return result;
+}
+
+void isoDrive::FreeDirIterator(const int dirIterator) {
+ dirIterators[dirIterator].valid = false;
+
+ // if this was the last aquired iterator decrement nextFreeIterator
+ if ((dirIterator + 1) % MAX_OPENDIRS == nextFreeDirIterator) {
+ if (nextFreeDirIterator>0) {
+ nextFreeDirIterator--;
+ } else {
+ nextFreeDirIterator = MAX_OPENDIRS-1;
+ }
+ }
+}
+
+bool isoDrive::ReadCachedSector(Bit8u** buffer, const Bit32u sector) {
+ // get hash table entry
+ int pos = sector % ISO_MAX_HASH_TABLE_SIZE;
+ SectorHashEntry& he = sectorHashEntries[pos];
+
+ // check if the entry is valid and contains the correct sector
+ if (!he.valid || he.sector != sector) {
+ if (!CDROM_Interface_Image::images[subUnit]->ReadSector(he.data, false, sector)) {
+ return false;
+ }
+ he.valid = true;
+ he.sector = sector;
+ }
+
+ *buffer = he.data;
+ return true;
+}
+
+inline bool isoDrive :: readSector(Bit8u *buffer, Bit32u sector) {
+ return CDROM_Interface_Image::images[subUnit]->ReadSector(buffer, false, sector);
+}
+
+int isoDrive :: readDirEntry(isoDirEntry *de, Bit8u *data) {
+ // copy data into isoDirEntry struct, data[0] = length of DirEntry
+// if (data[0] > sizeof(isoDirEntry)) return -1;//check disabled as isoDirentry is currently 258 bytes large. So it always fits
+ memcpy(de, data, data[0]);//Perharps care about a zero at the end.
+
+ // xa not supported
+ if (de->extAttrLength != 0) return -1;
+ // interleaved mode not supported
+ if (de->fileUnitSize != 0 || de->interleaveGapSize != 0) return -1;
+
+ // modify file identifier for use with dosbox
+ if ((de->length < 33 + de->fileIdentLength)) return -1;
+ if (IS_DIR(FLAGS2)) {
+ if (de->fileIdentLength == 1 && de->ident[0] == 0) strcpy((char*)de->ident, ".");
+ else if (de->fileIdentLength == 1 && de->ident[0] == 1) strcpy((char*)de->ident, "..");
+ else {
+ if (de->fileIdentLength > 200) return -1;
+ de->ident[de->fileIdentLength] = 0;
+ }
+ } else {
+ if (de->fileIdentLength > 200) return -1;
+ de->ident[de->fileIdentLength] = 0;
+ // remove any file version identifiers as there are some cdroms that don't have them
+ strreplace((char*)de->ident, ';', 0);
+ // if file has no extension remove the trailing dot
+ size_t tmp = strlen((char*)de->ident);
+ if (tmp > 0) {
+ if (de->ident[tmp - 1] == '.') de->ident[tmp - 1] = 0;
+ }
+ }
+ char* dotpos = strchr((char*)de->ident, '.');
+ if (dotpos!=NULL) {
+ if (strlen(dotpos)>4) dotpos[4]=0;
+ if (dotpos-(char*)de->ident>8) {
+ strcpy((char*)(&de->ident[8]),dotpos);
+ }
+ } else if (strlen((char*)de->ident)>8) de->ident[8]=0;
+ return de->length;
+}
+
+bool isoDrive :: loadImage() {
+ Bit8u pvd[COOKED_SECTOR_SIZE];
+ dataCD = false;
+ readSector(pvd, ISO_FIRST_VD);
+ if (pvd[0] == 1 && !strncmp((char*)(&pvd[1]), "CD001", 5) && pvd[6] == 1) iso = true;
+ else if (pvd[8] == 1 && !strncmp((char*)(&pvd[9]), "CDROM", 5) && pvd[14] == 1) iso = false;
+ else return false;
+ Bit16u offset = iso ? 156 : 180;
+ if (readDirEntry(&this->rootEntry, &pvd[offset])>0) {
+ dataCD = true;
+ return true;
+ }
+ return false;
+}
+
+bool isoDrive :: lookup(isoDirEntry *de, const char *path) {
+ if (!dataCD) return false;
+ *de = this->rootEntry;
+ if (!strcmp(path, "")) return true;
+
+ char isoPath[ISO_MAXPATHNAME];
+ safe_strncpy(isoPath, path, ISO_MAXPATHNAME);
+ strreplace(isoPath, '\\', '/');
+
+ // iterate over all path elements (name), and search each of them in the current de
+ for(char* name = strtok(isoPath, "/"); NULL != name; name = strtok(NULL, "/")) {
+
+ bool found = false;
+ // current entry must be a directory, abort otherwise
+ if (IS_DIR(FLAGS2)) {
+
+ // remove the trailing dot if present
+ size_t nameLength = strlen(name);
+ if (nameLength > 0) {
+ if (name[nameLength - 1] == '.') name[nameLength - 1] = 0;
+ }
+
+ // look for the current path element
+ int dirIterator = GetDirIterator(de);
+ while (!found && GetNextDirEntry(dirIterator, de)) {
+ if (!IS_ASSOC(FLAGS2) && (0 == strncasecmp((char*) de->ident, name, ISO_MAX_FILENAME_LENGTH))) {
+ found = true;
+ }
+ }
+ FreeDirIterator(dirIterator);
+ }
+ if (!found) return false;
+ }
+ return true;
+}
diff --git a/src/dos/drive_local.cpp b/src/dos/drive_local.cpp
new file mode 100644
index 000000000..4b9013b2a
--- /dev/null
+++ b/src/dos/drive_local.cpp
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include "dosbox.h"
+#include "dos_inc.h"
+#include "drives.h"
+#include "support.h"
+#include "cross.h"
+#include "inout.h"
+
+
+bool localDrive::FileCreate(DOS_File * * file,char * name,Bit16u /*attributes*/) {
+//TODO Maybe care for attributes but not likely
+ char newname[CROSS_LEN];
+ strcpy(newname,basedir);
+ strcat(newname,name);
+ CROSS_FILENAME(newname);
+ char* temp_name = dirCache.GetExpandName(newname); //Can only be used in till a new drive_cache action is preformed */
+ /* Test if file exists (so we need to truncate it). don't add to dirCache then */
+ bool existing_file = false;
+
+ FILE * test = fopen_wrap(temp_name,"rb+");
+ if(test) {
+ fclose(test);
+ existing_file=true;
+
+ }
+
+ FILE * hand = fopen_wrap(temp_name,"wb+");
+ if (!hand){
+ LOG_MSG("Warning: file creation failed: %s",newname);
+ return false;
+ }
+
+ if(!existing_file) dirCache.AddEntry(newname, true);
+ /* Make the 16 bit device information */
+ *file=new localFile(name,hand);
+ (*file)->flags=OPEN_READWRITE;
+
+ return true;
+}
+
+bool localDrive::FileOpen(DOS_File * * file,char * name,Bit32u flags) {
+ const char* type;
+ switch (flags&0xf) {
+ case OPEN_READ: type = "rb" ; break;
+ case OPEN_WRITE: type = "rb+"; break;
+ case OPEN_READWRITE: type = "rb+"; break;
+ case OPEN_READ_NO_MOD: type = "rb" ; break; //No modification of dates. LORD4.07 uses this
+ default:
+ DOS_SetError(DOSERR_ACCESS_CODE_INVALID);
+ return false;
+ }
+ char newname[CROSS_LEN];
+ strcpy(newname,basedir);
+ strcat(newname,name);
+ CROSS_FILENAME(newname);
+ dirCache.ExpandName(newname);
+
+ //Flush the buffer of handles for the same file. (Betrayal in Antara)
+ Bit8u i,drive=DOS_DRIVES;
+ localFile *lfp;
+ for (i=0;i<DOS_DRIVES;i++) {
+ if (Drives[i]==this) {
+ drive=i;
+ break;
+ }
+ }
+ for (i=0;i<DOS_FILES;i++) {
+ if (Files[i] && Files[i]->IsOpen() && Files[i]->GetDrive()==drive && Files[i]->IsName(name)) {
+ lfp=dynamic_cast<localFile*>(Files[i]);
+ if (lfp) lfp->Flush();
+ }
+ }
+
+ FILE * hand = fopen_wrap(newname,type);
+// Bit32u err=errno;
+ if (!hand) {
+ if((flags&0xf) != OPEN_READ) {
+ FILE * hmm = fopen_wrap(newname,"rb");
+ if (hmm) {
+ fclose(hmm);
+ LOG_MSG("Warning: file %s exists and failed to open in write mode.\nPlease Remove write-protection",newname);
+ }
+ }
+ return false;
+ }
+
+ *file=new localFile(name,hand);
+ (*file)->flags=flags; //for the inheritance flag and maybe check for others.
+// (*file)->SetFileName(newname);
+ return true;
+}
+
+FILE * localDrive::GetSystemFilePtr(char const * const name, char const * const type) {
+
+ char newname[CROSS_LEN];
+ strcpy(newname,basedir);
+ strcat(newname,name);
+ CROSS_FILENAME(newname);
+ dirCache.ExpandName(newname);
+
+ return fopen_wrap(newname,type);
+}
+
+bool localDrive::GetSystemFilename(char *sysName, char const * const dosName) {
+
+ strcpy(sysName, basedir);
+ strcat(sysName, dosName);
+ CROSS_FILENAME(sysName);
+ dirCache.ExpandName(sysName);
+ return true;
+}
+
+bool localDrive::FileUnlink(char * name) {
+ char newname[CROSS_LEN];
+ strcpy(newname,basedir);
+ strcat(newname,name);
+ CROSS_FILENAME(newname);
+ char *fullname = dirCache.GetExpandName(newname);
+ if (unlink(fullname)) {
+ //Unlink failed for some reason try finding it.
+ struct stat buffer;
+ if(stat(fullname,&buffer)) return false; // File not found.
+
+ FILE* file_writable = fopen_wrap(fullname,"rb+");
+ if(!file_writable) return false; //No acces ? ERROR MESSAGE NOT SET. FIXME ?
+ fclose(file_writable);
+
+ //File exists and can technically be deleted, nevertheless it failed.
+ //This means that the file is probably open by some process.
+ //See if We have it open.
+ bool found_file = false;
+ for(Bitu i = 0;i < DOS_FILES;i++){
+ if(Files[i] && Files[i]->IsName(name)) {
+ Bitu max = DOS_FILES;
+ while(Files[i]->IsOpen() && max--) {
+ Files[i]->Close();
+ if (Files[i]->RemoveRef()<=0) break;
+ }
+ found_file=true;
+ }
+ }
+ if(!found_file) return false;
+ if (!unlink(fullname)) {
+ dirCache.DeleteEntry(newname);
+ return true;
+ }
+ return false;
+ } else {
+ dirCache.DeleteEntry(newname);
+ return true;
+ }
+}
+
+bool localDrive::FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst) {
+ char tempDir[CROSS_LEN];
+ strcpy(tempDir,basedir);
+ strcat(tempDir,_dir);
+ CROSS_FILENAME(tempDir);
+
+ if (allocation.mediaid==0xF0 ) {
+ EmptyCache(); //rescan floppie-content on each findfirst
+ }
+
+ char end[2]={CROSS_FILESPLIT,0};
+ if (tempDir[strlen(tempDir)-1]!=CROSS_FILESPLIT) strcat(tempDir,end);
+
+ Bit16u id;
+ if (!dirCache.FindFirst(tempDir,id)) {
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ return false;
+ }
+ strcpy(srchInfo[id].srch_dir,tempDir);
+ dta.SetDirID(id);
+
+ Bit8u sAttr;
+ dta.GetSearchParams(sAttr,tempDir);
+
+ if (this->isRemote() && this->isRemovable()) {
+ // cdroms behave a bit different than regular drives
+ if (sAttr == DOS_ATTR_VOLUME) {
+ dta.SetResult(dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME);
+ return true;
+ }
+ } else {
+ if (sAttr == DOS_ATTR_VOLUME) {
+ if ( strcmp(dirCache.GetLabel(), "") == 0 ) {
+// LOG(LOG_DOSMISC,LOG_ERROR)("DRIVELABEL REQUESTED: none present, returned NOLABEL");
+// dta.SetResult("NO_LABEL",0,0,0,DOS_ATTR_VOLUME);
+// return true;
+ DOS_SetError(DOSERR_NO_MORE_FILES);
+ return false;
+ }
+ dta.SetResult(dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME);
+ return true;
+ } else if ((sAttr & DOS_ATTR_VOLUME) && (*_dir == 0) && !fcb_findfirst) {
+ //should check for a valid leading directory instead of 0
+ //exists==true if the volume label matches the searchmask and the path is valid
+ if (WildFileCmp(dirCache.GetLabel(),tempDir)) {
+ dta.SetResult(dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME);
+ return true;
+ }
+ }
+ }
+ return FindNext(dta);
+}
+
+bool localDrive::FindNext(DOS_DTA & dta) {
+
+ char * dir_ent;
+ struct stat stat_block;
+ char full_name[CROSS_LEN];
+ char dir_entcopy[CROSS_LEN];
+
+ Bit8u srch_attr;char srch_pattern[DOS_NAMELENGTH_ASCII];
+ Bit8u find_attr;
+
+ dta.GetSearchParams(srch_attr,srch_pattern);
+ Bit16u id = dta.GetDirID();
+
+again:
+ if (!dirCache.FindNext(id,dir_ent)) {
+ DOS_SetError(DOSERR_NO_MORE_FILES);
+ return false;
+ }
+ if(!WildFileCmp(dir_ent,srch_pattern)) goto again;
+
+ strcpy(full_name,srchInfo[id].srch_dir);
+ strcat(full_name,dir_ent);
+
+ //GetExpandName might indirectly destroy dir_ent (by caching in a new directory
+ //and due to its design dir_ent might be lost.)
+ //Copying dir_ent first
+ strcpy(dir_entcopy,dir_ent);
+ if (stat(dirCache.GetExpandName(full_name),&stat_block)!=0) {
+ goto again;//No symlinks and such
+ }
+
+ if(stat_block.st_mode & S_IFDIR) find_attr=DOS_ATTR_DIRECTORY;
+ else find_attr=DOS_ATTR_ARCHIVE;
+ if (~srch_attr & find_attr & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM)) goto again;
+
+ /*file is okay, setup everything to be copied in DTA Block */
+ char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;
+
+ if(strlen(dir_entcopy)<DOS_NAMELENGTH_ASCII){
+ strcpy(find_name,dir_entcopy);
+ upcase(find_name);
+ }
+
+ find_size=(Bit32u) stat_block.st_size;
+ struct tm *time;
+ if((time=localtime(&stat_block.st_mtime))!=0){
+ find_date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday);
+ find_time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec);
+ } else {
+ find_time=6;
+ find_date=4;
+ }
+ dta.SetResult(find_name,find_size,find_date,find_time,find_attr);
+ return true;
+}
+
+bool localDrive::GetFileAttr(char * name,Bit16u * attr) {
+ char newname[CROSS_LEN];
+ strcpy(newname,basedir);
+ strcat(newname,name);
+ CROSS_FILENAME(newname);
+ dirCache.ExpandName(newname);
+
+ struct stat status;
+ if (stat(newname,&status)==0) {
+ *attr=DOS_ATTR_ARCHIVE;
+ if(status.st_mode & S_IFDIR) *attr|=DOS_ATTR_DIRECTORY;
+ return true;
+ }
+ *attr=0;
+ return false;
+}
+
+bool localDrive::MakeDir(char * dir) {
+ char newdir[CROSS_LEN];
+ strcpy(newdir,basedir);
+ strcat(newdir,dir);
+ CROSS_FILENAME(newdir);
+#if defined (WIN32) /* MS Visual C++ */
+ int temp=mkdir(dirCache.GetExpandName(newdir));
+#else
+ int temp=mkdir(dirCache.GetExpandName(newdir),0700);
+#endif
+ if (temp==0) dirCache.CacheOut(newdir,true);
+
+ return (temp==0);// || ((temp!=0) && (errno==EEXIST));
+}
+
+bool localDrive::RemoveDir(char * dir) {
+ char newdir[CROSS_LEN];
+ strcpy(newdir,basedir);
+ strcat(newdir,dir);
+ CROSS_FILENAME(newdir);
+ int temp=rmdir(dirCache.GetExpandName(newdir));
+ if (temp==0) dirCache.DeleteEntry(newdir,true);
+ return (temp==0);
+}
+
+bool localDrive::TestDir(char * dir) {
+ char newdir[CROSS_LEN];
+ strcpy(newdir,basedir);
+ strcat(newdir,dir);
+ CROSS_FILENAME(newdir);
+ dirCache.ExpandName(newdir);
+ // Skip directory test, if "\"
+ size_t len = strlen(newdir);
+ if (len && (newdir[len-1]!='\\')) {
+ // It has to be a directory !
+ struct stat test;
+ if (stat(newdir,&test)) return false;
+ if ((test.st_mode & S_IFDIR)==0) return false;
+ };
+ int temp=access(newdir,F_OK);
+ return (temp==0);
+}
+
+bool localDrive::Rename(char * oldname,char * newname) {
+ char newold[CROSS_LEN];
+ strcpy(newold,basedir);
+ strcat(newold,oldname);
+ CROSS_FILENAME(newold);
+ dirCache.ExpandName(newold);
+
+ char newnew[CROSS_LEN];
+ strcpy(newnew,basedir);
+ strcat(newnew,newname);
+ CROSS_FILENAME(newnew);
+ int temp=rename(newold,dirCache.GetExpandName(newnew));
+ if (temp==0) dirCache.CacheOut(newnew);
+ return (temp==0);
+
+}
+
+bool localDrive::AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters) {
+ *_bytes_sector=allocation.bytes_sector;
+ *_sectors_cluster=allocation.sectors_cluster;
+ *_total_clusters=allocation.total_clusters;
+ *_free_clusters=allocation.free_clusters;
+ return true;
+}
+
+bool localDrive::FileExists(const char* name) {
+ char newname[CROSS_LEN];
+ strcpy(newname,basedir);
+ strcat(newname,name);
+ CROSS_FILENAME(newname);
+ dirCache.ExpandName(newname);
+ struct stat temp_stat;
+ if(stat(newname,&temp_stat)!=0) return false;
+ if(temp_stat.st_mode & S_IFDIR) return false;
+ return true;
+}
+
+bool localDrive::FileStat(const char* name, FileStat_Block * const stat_block) {
+ char newname[CROSS_LEN];
+ strcpy(newname,basedir);
+ strcat(newname,name);
+ CROSS_FILENAME(newname);
+ dirCache.ExpandName(newname);
+ struct stat temp_stat;
+ if(stat(newname,&temp_stat)!=0) return false;
+ /* Convert the stat to a FileStat */
+ struct tm *time;
+ if((time=localtime(&temp_stat.st_mtime))!=0) {
+ stat_block->time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec);
+ stat_block->date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday);
+ } else {
+
+ }
+ stat_block->size=(Bit32u)temp_stat.st_size;
+ return true;
+}
+
+
+Bit8u localDrive::GetMediaByte(void) {
+ return allocation.mediaid;
+}
+
+bool localDrive::isRemote(void) {
+ return false;
+}
+
+bool localDrive::isRemovable(void) {
+ return false;
+}
+
+Bits localDrive::UnMount(void) {
+ delete this;
+ return 0;
+}
+
+localDrive::localDrive(const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid) {
+ strcpy(basedir,startdir);
+ sprintf(info,"local directory %s",startdir);
+ allocation.bytes_sector=_bytes_sector;
+ allocation.sectors_cluster=_sectors_cluster;
+ allocation.total_clusters=_total_clusters;
+ allocation.free_clusters=_free_clusters;
+ allocation.mediaid=_mediaid;
+
+ dirCache.SetBaseDir(basedir);
+}
+
+
+//TODO Maybe use fflush, but that seemed to fuck up in visual c
+bool localFile::Read(Bit8u * data,Bit16u * size) {
+ if ((this->flags & 0xf) == OPEN_WRITE) { // check if file opened in write-only mode
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ if (last_action==WRITE) fseek(fhandle,ftell(fhandle),SEEK_SET);
+ last_action=READ;
+ *size=(Bit16u)fread(data,1,*size,fhandle);
+ /* Fake harddrive motion. Inspector Gadget with soundblaster compatible */
+ /* Same for Igor */
+ /* hardrive motion => unmask irq 2. Only do it when it's masked as unmasking is realitively heavy to emulate */
+ Bit8u mask = IO_Read(0x21);
+ if(mask & 0x4 ) IO_Write(0x21,mask&0xfb);
+ return true;
+}
+
+bool localFile::Write(Bit8u * data,Bit16u * size) {
+ Bit32u lastflags = this->flags & 0xf;
+ if (lastflags == OPEN_READ || lastflags == OPEN_READ_NO_MOD) { // check if file opened in read-only mode
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ if (last_action==READ) fseek(fhandle,ftell(fhandle),SEEK_SET);
+ last_action=WRITE;
+ if(*size==0){
+ return (!ftruncate(fileno(fhandle),ftell(fhandle)));
+ }
+ else
+ {
+ *size=(Bit16u)fwrite(data,1,*size,fhandle);
+ return true;
+ }
+}
+
+bool localFile::Seek(Bit32u * pos,Bit32u type) {
+ int seektype;
+ switch (type) {
+ case DOS_SEEK_SET:seektype=SEEK_SET;break;
+ case DOS_SEEK_CUR:seektype=SEEK_CUR;break;
+ case DOS_SEEK_END:seektype=SEEK_END;break;
+ default:
+ //TODO Give some doserrorcode;
+ return false;//ERROR
+ }
+ int ret=fseek(fhandle,*reinterpret_cast<Bit32s*>(pos),seektype);
+ if (ret!=0) {
+ // Out of file range, pretend everythings ok
+ // and move file pointer top end of file... ?! (Black Thorne)
+ fseek(fhandle,0,SEEK_END);
+ };
+#if 0
+ fpos_t temppos;
+ fgetpos(fhandle,&temppos);
+ Bit32u * fake_pos=(Bit32u*)&temppos;
+ *pos=*fake_pos;
+#endif
+ *pos=(Bit32u)ftell(fhandle);
+ last_action=NONE;
+ return true;
+}
+
+bool localFile::Close() {
+ // only close if one reference left
+ if (refCtr==1) {
+ if(fhandle) fclose(fhandle);
+ fhandle = 0;
+ open = false;
+ };
+ return true;
+}
+
+Bit16u localFile::GetInformation(void) {
+ return read_only_medium?0x40:0;
+}
+
+
+localFile::localFile(const char* _name, FILE * handle) {
+ fhandle=handle;
+ open=true;
+ UpdateDateTimeFromHost();
+
+ attr=DOS_ATTR_ARCHIVE;
+ last_action=NONE;
+ read_only_medium=false;
+
+ name=0;
+ SetName(_name);
+}
+
+void localFile::FlagReadOnlyMedium(void) {
+ read_only_medium = true;
+}
+
+bool localFile::UpdateDateTimeFromHost(void) {
+ if(!open) return false;
+ struct stat temp_stat;
+ fstat(fileno(fhandle),&temp_stat);
+ struct tm * ltime;
+ if((ltime=localtime(&temp_stat.st_mtime))!=0) {
+ time=DOS_PackTime((Bit16u)ltime->tm_hour,(Bit16u)ltime->tm_min,(Bit16u)ltime->tm_sec);
+ date=DOS_PackDate((Bit16u)(ltime->tm_year+1900),(Bit16u)(ltime->tm_mon+1),(Bit16u)ltime->tm_mday);
+ } else {
+ time=1;date=1;
+ }
+ return true;
+}
+
+void localFile::Flush(void) {
+ if (last_action==WRITE) {
+ fseek(fhandle,ftell(fhandle),SEEK_SET);
+ last_action=NONE;
+ }
+}
+
+
+// ********************************************
+// CDROM DRIVE
+// ********************************************
+
+int MSCDEX_RemoveDrive(char driveLetter);
+int MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit);
+bool MSCDEX_HasMediaChanged(Bit8u subUnit);
+bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name);
+
+
+cdromDrive::cdromDrive(const char driveLetter, const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid, int& error)
+ :localDrive(startdir,_bytes_sector,_sectors_cluster,_total_clusters,_free_clusters,_mediaid),
+ subUnit(0),
+ driveLetter('\0')
+{
+ // Init mscdex
+ error = MSCDEX_AddDrive(driveLetter,startdir,subUnit);
+ strcpy(info, "CDRom ");
+ strcat(info, startdir);
+ this->driveLetter = driveLetter;
+ // Get Volume Label
+ char name[32];
+ if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true);
+}
+
+bool cdromDrive::FileOpen(DOS_File * * file,char * name,Bit32u flags) {
+ if ((flags&0xf)==OPEN_READWRITE) {
+ flags &= ~OPEN_READWRITE;
+ } else if ((flags&0xf)==OPEN_WRITE) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+ }
+ bool retcode = localDrive::FileOpen(file,name,flags);
+ if(retcode) (dynamic_cast<localFile*>(*file))->FlagReadOnlyMedium();
+ return retcode;
+}
+
+bool cdromDrive::FileCreate(DOS_File * * /*file*/,char * /*name*/,Bit16u /*attributes*/) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+}
+
+bool cdromDrive::FileUnlink(char * /*name*/) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+}
+
+bool cdromDrive::RemoveDir(char * /*dir*/) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+}
+
+bool cdromDrive::MakeDir(char * /*dir*/) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+}
+
+bool cdromDrive::Rename(char * /*oldname*/,char * /*newname*/) {
+ DOS_SetError(DOSERR_ACCESS_DENIED);
+ return false;
+}
+
+bool cdromDrive::GetFileAttr(char * name,Bit16u * attr) {
+ bool result = localDrive::GetFileAttr(name,attr);
+ if (result) *attr |= DOS_ATTR_READ_ONLY;
+ return result;
+}
+
+bool cdromDrive::FindFirst(char * _dir,DOS_DTA & dta,bool /*fcb_findfirst*/) {
+ // If media has changed, reInit drivecache.
+ if (MSCDEX_HasMediaChanged(subUnit)) {
+ dirCache.EmptyCache();
+ // Get Volume Label
+ char name[32];
+ if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true);
+ }
+ return localDrive::FindFirst(_dir,dta);
+}
+
+void cdromDrive::SetDir(const char* path) {
+ // If media has changed, reInit drivecache.
+ if (MSCDEX_HasMediaChanged(subUnit)) {
+ dirCache.EmptyCache();
+ // Get Volume Label
+ char name[32];
+ if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true);
+ }
+ localDrive::SetDir(path);
+}
+
+bool cdromDrive::isRemote(void) {
+ return true;
+}
+
+bool cdromDrive::isRemovable(void) {
+ return true;
+}
+
+Bits cdromDrive::UnMount(void) {
+ if(MSCDEX_RemoveDrive(driveLetter)) {
+ delete this;
+ return 0;
+ }
+ return 2;
+}
diff --git a/src/dos/drive_overlay.cpp b/src/dos/drive_overlay.cpp
new file mode 100644
index 000000000..73f2f2d7e
--- /dev/null
+++ b/src/dos/drive_overlay.cpp
@@ -0,0 +1,1182 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include "dosbox.h"
+#include "dos_inc.h"
+#include "drives.h"
+#include "support.h"
+#include "cross.h"
+#include "inout.h"
+#include "timer.h"
+
+#include <vector>
+#include <string>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#define OVERLAY_DIR 1
+bool logoverlay = false;
+using namespace std;
+
+#if defined (WIN32) || defined (OS2) /* Win 32 & OS/2*/
+#define CROSS_DOSFILENAME(blah)
+#else
+//Convert back to DOS PATH
+#define CROSS_DOSFILENAME(blah) strreplace(blah,'/','\\')
+#endif
+
+
+/*
+ * design principles/limitations/requirements:
+ * 1) All filenames inside the overlay directories are UPPERCASE and conform to the 8.3 standard except for the special DBOVERLAY files.
+ * 2) Renaming directories is currently not supported.
+ *
+ * Point 2 is still being worked on.
+ */
+
+/* New rename for base directories:
+ * Alter shortname in the drive_cache: take care of order and long names.
+ * update stored deleted files list in overlay.
+ */
+
+//TODO recheck directories under linux with the filename_cache (as one adds the dos name (and runs cross_filename on the other))
+
+
+//TODO Check: Maybe handle file redirection in ccc (opening the new file), (call update datetime host there ?)
+
+
+/* For rename/delete(unlink)/makedir/removedir we need to rebuild the cache. (shouldn't be needed,
+ * but cacheout/delete entry currently throw away the cached folder and rebuild it on read.
+ * so we have to ensure the rebuilding is controlled through the overlay.
+ * In order to not reread the overlay directory contents, the information in there is cached and updated when
+ * it changes (when deleting a file or adding one)
+ */
+
+
+//directories that exist only in overlay can not be added to the drive_cache currently.
+//Either upgrade addentry to support directories. (without actually caching stuff in! (code in testing))
+//Or create an empty directory in local drive base.
+
+bool Overlay_Drive::RemoveDir(char * dir) {
+ //DOS_RemoveDir checks if directory exists.
+#if OVERLAY_DIR
+ if (logoverlay) LOG_MSG("Overlay: trying to remove directory: %s",dir);
+#else
+ E_Exit("Overlay: trying to remove directory: %s",dir);
+#endif
+ /* Overlay: Check if folder is empty (findfirst/next, skipping . and .. and breaking on first file found ?), if so, then it is not too tricky. */
+ if (is_dir_only_in_overlay(dir)) {
+ //The simple case
+ char odir[CROSS_LEN];
+ strcpy(odir,overlaydir);
+ strcat(odir,dir);
+ CROSS_FILENAME(odir);
+ int temp=rmdir(odir);
+ if (temp==0) {
+ remove_DOSdir_from_cache(dir);
+ char newdir[CROSS_LEN];
+ strcpy(newdir,basedir);
+ strcat(newdir,dir);
+ CROSS_FILENAME(newdir);
+ dirCache.DeleteEntry(newdir,true);
+ update_cache(false);
+ }
+ return (temp==0);
+ } else {
+ Bit16u olderror = dos.errorcode; //FindFirst/Next always set an errorcode, while RemoveDir itself shouldn't touch it if successful
+ DOS_DTA dta(dos.tables.tempdta);
+ char stardotstar[4] = {'*', '.', '*', 0};
+ dta.SetupSearch(0,(0xff & ~DOS_ATTR_VOLUME),stardotstar); //Fake drive as we don't use it.
+ bool ret = this->FindFirst(dir,dta,false);// DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME);
+ if (!ret) {
+ //Path not found. Should not be possible due to removedir doing a testdir, but lets be correct
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ return false;
+ }
+ bool empty = true;
+ do {
+ char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u date;Bit16u time;Bit8u attr;
+ dta.GetResult(name,size,date,time,attr);
+ if (logoverlay) LOG_MSG("RemoveDir found %s",name);
+ if (empty && strcmp(".",name ) && strcmp("..",name))
+ empty = false; //Neither . or .. so directory not empty.
+ } while ( (ret=this->FindNext(dta)) );
+ //Always exhaust list, so drive_cache entry gets invalidated/reused.
+ //FindNext is done, restore error code to old value. DOS_RemoveDir will set the right one if needed.
+ dos.errorcode = olderror;
+
+ if (!empty) return false;
+ if (logoverlay) LOG_MSG("directory empty! Hide it.");
+ //Directory is empty, mark it as deleted and create DBOVERLAY file.
+ //Ensure that overlap folder can not be created.
+ add_deleted_path(dir,true);
+ return true;
+ }
+}
+
+bool Overlay_Drive::MakeDir(char * dir) {
+ //DOS_MakeDir tries first, before checking if the directory already exists, so doing it here as well, so that case is handled.
+ if (TestDir(dir)) return false;
+ if (overlap_folder == dir) return false; //TODO Test
+#if OVERLAY_DIR
+ if (logoverlay) LOG_MSG("Overlay trying to make directory: %s",dir);
+#else
+ E_Exit("Overlay trying to make directory: %s",dir);
+#endif
+ /* Overlay: Create in Overlay only and add it to drive_cache + some entries else the drive_cache will try to access it. Needs an AddEntry for directories. */
+
+ if (is_deleted_path(dir) && localDrive::TestDir(dir)) {
+ //Was deleted before and exists (last one is safety check)
+ remove_deleted_path(dir,true);
+ return true;
+ }
+ char newdir[CROSS_LEN];
+ strcpy(newdir,overlaydir);
+ strcat(newdir,dir);
+ CROSS_FILENAME(newdir);
+#if defined (WIN32) /* MS Visual C++ */
+ int temp=mkdir(newdir);
+#else
+ int temp=mkdir(newdir,0700);
+#endif
+ if (temp==0) {
+ char fakename[CROSS_LEN];
+ strcpy(fakename,basedir);
+ strcat(fakename,dir);
+ CROSS_FILENAME(fakename);
+ dirCache.AddEntryDirOverlay(fakename,true);
+ add_DOSdir_to_cache(dir);
+ }
+
+ return (temp==0);// || ((temp!=0) && (errno==EEXIST));
+}
+
+bool Overlay_Drive::TestDir(char * dir) {
+ //First check if directory exist exclusively in the overlay.
+ //Currently using the update_cache cache, alternatively access the directory itself.
+
+ //Directories are stored without a trailing backslash
+ char tempdir[CROSS_LEN];
+ strcpy(tempdir,dir);
+ size_t templen = strlen(dir);
+ if (templen && tempdir[templen-1] == '\\') tempdir[templen-1] = 0;
+
+#if OVERLAY_DIR
+ if (is_dir_only_in_overlay(tempdir)) return true;
+#endif
+
+ //Next Check if the directory is marked as deleted or one of its leading directories is.
+ //(it still might exists in the localDrive)
+
+ if (is_deleted_path(tempdir)) return false;
+
+ // Not exclusive to overlay nor marked as deleted. Pass on to LocalDrive
+ return localDrive::TestDir(dir);
+}
+
+
+class OverlayFile: public localFile {
+public:
+ OverlayFile(const char* name, FILE * handle):localFile(name,handle){
+ if (logoverlay) LOG_MSG("constructing OverlayFile: %s",name);
+ }
+ bool Write(Bit8u * data,Bit16u * size) {
+ Bit32u f = real_flags&0xf;
+ if (!overlay_active && (f == OPEN_READWRITE || f == OPEN_WRITE)) {
+ if (logoverlay) LOG_MSG("write detected, switching file for %s",GetName());
+ if (*data == 0) {
+ if (logoverlay) LOG_MSG("OPTIMISE: truncate on switch!!!!");
+ }
+ Bitu a = GetTicks();
+ bool r = create_copy();
+ if (GetTicks()-a >2) {
+ if (logoverlay) LOG_MSG("OPTIMISE: switching took %d",GetTicks()-a);
+ }
+ if (!r) return false;
+ overlay_active = true;
+ flags = real_flags;
+
+ }
+ return localFile::Write(data,size);
+ }
+ bool create_copy();
+//private:
+ Bit32u real_flags;
+ bool overlay_active;
+};
+
+//Create leading directories of a file being overlayed if they exist in the original (localDrive).
+//This function is used to create copies of existing files, so all leading directories exist in the original.
+
+FILE* Overlay_Drive::create_file_in_overlay(char* dos_filename, char const* mode) {
+
+ if (logoverlay) LOG_MSG("create_file_in_overlay called %s %s",dos_filename,mode);
+ char newname[CROSS_LEN];
+ strcpy(newname,overlaydir); //TODO GOG make part of class and join in
+ strcat(newname,dos_filename); //HERE we need to convert it to Linux TODO
+ CROSS_FILENAME(newname);
+
+ FILE* f = fopen_wrap(newname,mode);
+ //Check if a directories are part of the name:
+ char* dir = strrchr(dos_filename,'\\');
+ if (!f && dir && *dir) {
+ if (logoverlay) LOG_MSG("Overlay: warning creating a file inside a directory %s",dos_filename);
+ //ensure they exist, else make them in the overlay if they exist in the original....
+ Sync_leading_dirs(dos_filename);
+ //try again
+ f = fopen_wrap(newname,mode);
+ }
+
+ return f;
+}
+
+#ifndef BUFSIZ
+#define BUFSIZ 2048
+#endif
+
+//bool OverlayFile::create_copy(DOS_File * file, char* newname)
+bool OverlayFile::create_copy() {
+ //test if open/valid/etc
+ //ensure file position
+ if (logoverlay) LOG_MSG("create_copy called %s",GetName());
+
+ FILE* lhandle = this->fhandle;
+ fseek(lhandle,ftell(lhandle),SEEK_SET);
+ int location_in_old_file = ftell(lhandle);
+ fseek(lhandle,0L,SEEK_SET);
+
+ FILE* newhandle = NULL;
+ Bit8u drive_set = GetDrive();
+ if (drive_set != 0xff && drive_set < DOS_DRIVES && Drives[drive_set]){
+ Overlay_Drive* od = dynamic_cast<Overlay_Drive*>(Drives[drive_set]);
+ if (od) {
+ newhandle = od->create_file_in_overlay(GetName(),"wb+"); //todo check wb+
+ }
+ }
+// newhandle = create_file(newname,"wb+");
+ if (!newhandle) return false;
+ char buffer[BUFSIZ];
+ size_t s;
+ while ( (s = fread(buffer,1,BUFSIZ,lhandle)) ) fwrite(buffer, 1, s, newhandle);
+ fclose(lhandle);
+ //Set copied file handle to position of the old one
+ fseek(newhandle,location_in_old_file,SEEK_SET);
+ this->fhandle = newhandle;
+ //Flags ?
+ if (logoverlay) LOG_MSG("success");
+ return true;
+}
+
+
+
+static OverlayFile* ccc(DOS_File* file) {
+ localFile* l = dynamic_cast<localFile*>(file);
+ if (!l) E_Exit("overlay input file is not a localFile");
+ //Create an overlayFile
+ OverlayFile* ret = new OverlayFile(l->GetName(),l->fhandle);
+ ret->flags = l->flags;
+ delete l;
+ return ret;
+}
+
+Overlay_Drive::Overlay_Drive(const char * startdir,const char* overlay, Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid,Bit8u &error)
+:localDrive(startdir,_bytes_sector,_sectors_cluster,_total_clusters,_free_clusters,_mediaid),special_prefix("DBOVERLAY") {
+ optimize_cache_v1 = true; //Try to not reread overlay files on deletes. Ideally drive_cache should be improved to handle deletes properly.
+ //Currently this flag does nothing, as the current behavior is to not reread due to caching everything.
+#if defined (WIN32)
+ if (strcasecmp(startdir,overlay) == 0) {
+#else
+ if (strcmp(startdir,overlay) == 0) {
+#endif
+ //overlay directory can not be the base directory
+ error = 2;
+ return;
+ }
+
+ std::string s(startdir);
+ std::string o(overlay);
+ bool s_absolute = Cross::IsPathAbsolute(s);
+ bool o_absolute = Cross::IsPathAbsolute(o);
+ error = 0;
+ if (s_absolute != o_absolute) {
+ error = 1;
+ return;
+ }
+ strcpy(overlaydir,overlay);
+ char dirname[CROSS_LEN] = { 0 };
+ //Determine if overlaydir is part of the startdir.
+ convert_overlay_to_DOSname_in_base(dirname);
+
+
+ if(strlen(dirname) && dirname[strlen(dirname)-1] == '\\') dirname[strlen(dirname)-1] = 0;
+
+ //add_deleted_path(dirname); //update_cache will add the overlap_folder
+ overlap_folder = dirname;
+
+ update_cache(true);
+}
+
+void Overlay_Drive::convert_overlay_to_DOSname_in_base(char* dirname )
+{
+ dirname[0] = 0;//ensure good return string
+ if (strlen(overlaydir) >= strlen(basedir) ) {
+ //Needs to be longer at least.
+#if defined (WIN32)
+//OS2 ?
+ if (strncasecmp(overlaydir,basedir,strlen(basedir)) == 0) {
+#else
+ if (strncmp(overlaydir,basedir,strlen(basedir)) == 0) {
+#endif
+ //Beginning is the same.
+ char t[CROSS_LEN];
+ strcpy(t,overlaydir+strlen(basedir));
+
+ char* p = t;
+ char* b = t;
+
+ while ( (p =strchr(p,CROSS_FILESPLIT)) ) {
+ char directoryname[CROSS_LEN]={0};
+ char dosboxdirname[CROSS_LEN]={0};
+ strcpy(directoryname,dirname);
+ strncat(directoryname,b,p-b);
+
+ char d[CROSS_LEN];
+ strcpy(d,basedir);
+ strcat(d,directoryname);
+ CROSS_FILENAME(d);
+ //Try to find the corresponding directoryname in DOSBox.
+ if(!dirCache.GetShortName(d,dosboxdirname) ) {
+ //Not a long name, assume it is a short name instead
+ strncpy(dosboxdirname,b,p-b);
+ upcase(dosboxdirname);
+ }
+
+
+ strcat(dirname,dosboxdirname);
+ strcat(dirname,"\\");
+
+ if (logoverlay) LOG_MSG("HIDE directory: %s",dirname);
+
+
+ b=++p;
+
+ }
+ }
+ }
+}
+
+bool Overlay_Drive::FileOpen(DOS_File * * file,char * name,Bit32u flags) {
+ const char* type;
+ switch (flags&0xf) {
+ case OPEN_READ: type = "rb" ; break;
+ case OPEN_WRITE: type = "rb+"; break;
+ case OPEN_READWRITE: type = "rb+"; break;
+ case OPEN_READ_NO_MOD: type = "rb" ; break; //No modification of dates. LORD4.07 uses this
+ default:
+ DOS_SetError(DOSERR_ACCESS_CODE_INVALID);
+ return false;
+ }
+
+ //Flush the buffer of handles for the same file. (Betrayal in Antara)
+ Bit8u i,drive=DOS_DRIVES;
+ localFile *lfp;
+ for (i=0;i<DOS_DRIVES;i++) {
+ if (Drives[i]==this) {
+ drive=i;
+ break;
+ }
+ }
+ for (i=0;i<DOS_FILES;i++) {
+ if (Files[i] && Files[i]->IsOpen() && Files[i]->GetDrive()==drive && Files[i]->IsName(name)) {
+ lfp=dynamic_cast<localFile*>(Files[i]);
+ if (lfp) lfp->Flush();
+ }
+ }
+
+
+ //Todo check name first against local tree
+ //if name exists, use that one instead!
+ //overlay file.
+ char newname[CROSS_LEN];
+ strcpy(newname,overlaydir);
+ strcat(newname,name);
+ CROSS_FILENAME(newname);
+
+ FILE * hand = fopen_wrap(newname,type);
+ bool fileopened = false;
+ if (hand) {
+ if (logoverlay) LOG_MSG("overlay file opened %s",newname);
+ *file=new localFile(name,hand);
+ (*file)->flags=flags;
+ fileopened = true;
+ } else {
+ ; //TODO error handling!!!! (maybe check if it exists and read only (should not happen with overlays)
+ }
+ bool overlayed = fileopened;
+
+ //File not present in overlay, try normal drive
+ //TODO take care of file being marked deleted.
+
+ if (!fileopened && !is_deleted_file(name)) fileopened = localDrive::FileOpen(file,name, OPEN_READ);
+
+
+ if (fileopened) {
+ if (logoverlay) LOG_MSG("file opened %s",name);
+ //Convert file to OverlayFile
+ OverlayFile* f = ccc(*file);
+ //Store original flags, as with overlay the files are opened read only and they switch on write
+ f->real_flags = flags;
+ f->overlay_active = overlayed; //No need to switch if already in overlayed.
+ *file = f;
+ }
+ return fileopened;
+}
+
+
+bool Overlay_Drive::FileCreate(DOS_File * * file,char * name,Bit16u /*attributes*/) {
+
+ //TODO Check if it exists in the dirCache ? // fix addentry ? or just double check (ld and overlay)
+ //AddEntry looks sound to me..
+
+ FILE* f = create_file_in_overlay(name,"wb+");
+ if(!f) {
+ if (logoverlay) LOG_MSG("File creation in overlay system failed %s",name);
+ return false;
+ }
+ *file = new localFile(name,f);
+ (*file)->flags = OPEN_READWRITE;
+ OverlayFile* of = ccc(*file);
+ of->overlay_active = true;
+ of->real_flags = OPEN_READWRITE;
+ *file = of;
+ //create fake name for the drive cache
+ char fakename[CROSS_LEN];
+ strcpy(fakename,basedir);
+ strcat(fakename,name);
+ CROSS_FILENAME(fakename);
+ dirCache.AddEntry(fakename,true); //add it.
+ add_DOSname_to_cache(name);
+ remove_deleted_file(name,true);
+ return true;
+}
+void Overlay_Drive::add_DOSname_to_cache(const char* name) {
+ for (std::vector<std::string>::const_iterator itc = DOSnames_cache.begin(); itc != DOSnames_cache.end();itc++){
+ if (name == (*itc)) return;
+ }
+ DOSnames_cache.push_back(name);
+}
+void Overlay_Drive::remove_DOSname_from_cache(const char* name) {
+ for (std::vector<std::string>::iterator it = DOSnames_cache.begin(); it != DOSnames_cache.end();it++) {
+ if (name == (*it)) { DOSnames_cache.erase(it); return;}
+ }
+
+}
+
+bool Overlay_Drive::Sync_leading_dirs(const char* dos_filename){
+ const char* lastdir = strrchr(dos_filename,'\\');
+ //If there are no directories, return success.
+ if (!lastdir) return true;
+
+ const char* leaddir = dos_filename;
+ while ( (leaddir=strchr(leaddir,'\\')) != 0) {
+ char dirname[CROSS_LEN] = {0};
+ strncpy(dirname,dos_filename,leaddir-dos_filename);
+
+ if (logoverlay) LOG_MSG("syncdir: %s",dirname);
+ //Test if directory exist in base.
+ char dirnamebase[CROSS_LEN] ={0};
+ strcpy(dirnamebase,basedir);
+ strcat(dirnamebase,dirname);
+ CROSS_FILENAME(dirnamebase);
+ struct stat basetest;
+ if (stat(dirCache.GetExpandName(dirnamebase),&basetest) == 0 && basetest.st_mode & S_IFDIR) {
+ if (logoverlay) LOG_MSG("base exists: %s",dirnamebase);
+ //Directory exists in base folder.
+ //Ensure it exists in overlay as well
+
+ struct stat overlaytest;
+ char dirnameoverlay[CROSS_LEN] ={0};
+ strcpy(dirnameoverlay,overlaydir);
+ strcat(dirnameoverlay,dirname);
+ CROSS_FILENAME(dirnameoverlay);
+ if (stat(dirnameoverlay,&overlaytest) == 0 ) {
+ //item exist. Check if it is a folder, if not a folder =>fail!
+ if ((overlaytest.st_mode & S_IFDIR) ==0) return false;
+ } else {
+ //folder does not exist, make it
+ if (logoverlay) LOG_MSG("creating %s",dirnameoverlay);
+#if defined (WIN32) /* MS Visual C++ */
+ int temp = mkdir(dirnameoverlay);
+#else
+ int temp = mkdir(dirnameoverlay,0700);
+#endif
+ if (temp != 0) return false;
+ }
+ }
+ leaddir = leaddir + 1; //Move to next
+ }
+
+ return true;
+}
+void Overlay_Drive::update_cache(bool read_directory_contents) {
+ Bitu a = GetTicks();
+ std::vector<std::string> specials;
+ std::vector<std::string> dirnames;
+ std::vector<std::string> filenames;
+ if (read_directory_contents) {
+ //Clear all lists
+ DOSnames_cache.clear();
+ DOSdirs_cache.clear();
+ deleted_files_in_base.clear();
+ deleted_paths_in_base.clear();
+ //Ensure hiding of the folder that contains the overlay, if it is part of the base folder.
+ add_deleted_path(overlap_folder.c_str(), false);
+ }
+
+ //Needs later to support stored renames and removals of files existing in the localDrive plane.
+ //and by taking in account if the file names are actually already renamed.
+ //and taking in account that a file could have gotten an overlay version and then both need to be removed.
+ //
+ //Also what about sequences were a base file gets copied to a working save game and then removed/renamed...
+ //copy should be safe as then the link with the original doesn't exist.
+ //however the working safe can be rather complicated after a rename and delete..
+
+ //Currently directories existing only in the overlay can not be added to drive cache:
+ //1. possible workaround create empty directory in base. Drawback would break the no-touching-of-base.
+ //2. double up Addentry to support directories, (and adding . and .. to the newly directory so it counts as cachedin.. and won't be recached, as otherwise
+ // cache will realize we are faking it.
+ //Working on solution 2.
+
+ //Random TODO: Does the root drive under DOS have . and .. ?
+
+ //This function needs to be called after any localDrive function calling cacheout/deleteentry, as those throw away directories.
+ //either do this with a parameter stating the part that needs to be rebuild,(directory) or clear the cache by default and do it all.
+
+ std::vector<std::string>::iterator i;
+ std::string::size_type const prefix_lengh = special_prefix.length();
+ if (read_directory_contents) {
+ dir_information* dirp = open_directory(overlaydir);
+ if (dirp == NULL) return;
+ // Read complete directory
+ char dir_name[CROSS_LEN];
+ bool is_directory;
+ if (read_directory_first(dirp, dir_name, is_directory)) {
+ if ((strlen(dir_name) > prefix_lengh+5) && strncmp(dir_name,special_prefix.c_str(),prefix_lengh) == 0) specials.push_back(dir_name);
+ else if (is_directory) dirnames.push_back(dir_name);
+ else filenames.push_back(dir_name);
+ while (read_directory_next(dirp, dir_name, is_directory)) {
+ if ((strlen(dir_name) > prefix_lengh+5) && strncmp(dir_name,special_prefix.c_str(),prefix_lengh) == 0) specials.push_back(dir_name);
+ else if (is_directory) dirnames.push_back(dir_name);
+ else filenames.push_back(dir_name);
+ }
+ }
+ close_directory(dirp);
+ //parse directories to add them.
+
+
+
+ for (i = dirnames.begin(); i != dirnames.end();i++) {
+ if ((*i) == ".") continue;
+ if ((*i) == "..") continue;
+ std::string testi(*i);
+ std::string::size_type ll = testi.length();
+ //TODO: Use the dirname\. and dirname\.. for creating fake directories in the driveCache.
+ if( ll >2 && testi[ll-1] == '.' && testi[ll-2] == CROSS_FILESPLIT) continue;
+ if( ll >3 && testi[ll-1] == '.' && testi[ll-2] == '.' && testi[ll-3] == CROSS_FILESPLIT) continue;
+
+#if OVERLAY_DIR
+ char tdir[CROSS_LEN];
+ strcpy(tdir,(*i).c_str());
+ CROSS_DOSFILENAME(tdir);
+ bool dir_exists_in_base = localDrive::TestDir(tdir);
+#endif
+
+ char dir[CROSS_LEN];
+ strcpy(dir,overlaydir);
+ strcat(dir,(*i).c_str());
+ char dirpush[CROSS_LEN];
+ strcpy(dirpush,(*i).c_str());
+ static char end[2] = {CROSS_FILESPLIT,0};
+ strcat(dirpush,end); //Linux ?
+ dir_information* dirp = open_directory(dir);
+ if (dirp == NULL) continue;
+
+#if OVERLAY_DIR
+ //Good directory, add to DOSdirs_cache if not existing in localDrive. tested earlier to prevent problems with opendir
+ if (!dir_exists_in_base) add_DOSdir_to_cache(tdir);
+#endif
+
+ std::string backupi(*i);
+ // Read complete directory
+ char dir_name[CROSS_LEN];
+ bool is_directory;
+ if (read_directory_first(dirp, dir_name, is_directory)) {
+ if ((strlen(dir_name) > prefix_lengh+5) && strncmp(dir_name,special_prefix.c_str(),prefix_lengh) == 0) specials.push_back(string(dirpush)+dir_name);
+ else if (is_directory) dirnames.push_back(string(dirpush)+dir_name);
+ else filenames.push_back(string(dirpush)+dir_name);
+ while (read_directory_next(dirp, dir_name, is_directory)) {
+ if ((strlen(dir_name) > prefix_lengh+5) && strncmp(dir_name,special_prefix.c_str(),prefix_lengh) == 0) specials.push_back(string(dirpush)+dir_name);
+ else if (is_directory) dirnames.push_back(string(dirpush)+dir_name);
+ else filenames.push_back(string(dirpush)+dir_name);
+ }
+ }
+ close_directory(dirp);
+ for(i = dirnames.begin(); i != dirnames.end();i++) {
+ if ( (*i) == backupi) break; //find current directory again, for the next round.
+ }
+ }
+ }
+
+
+ if (read_directory_contents) {
+ for( i = filenames.begin(); i != filenames.end(); i++) {
+ char dosname[CROSS_LEN];
+ strcpy(dosname,(*i).c_str());
+ upcase(dosname); //Should not be really needed, as uppercase in the overlay is a requirement...
+ CROSS_DOSFILENAME(dosname);
+ if (logoverlay) LOG_MSG("update cache add dosname %s",dosname);
+ DOSnames_cache.push_back(dosname);
+ }
+ }
+
+#if OVERLAY_DIR
+ for (i = DOSdirs_cache.begin(); i !=DOSdirs_cache.end(); i++) {
+ char fakename[CROSS_LEN];
+ strcpy(fakename,basedir);
+ strcat(fakename,(*i).c_str());
+ CROSS_FILENAME(fakename);
+ dirCache.AddEntryDirOverlay(fakename,true);
+ }
+#endif
+
+ for (i = DOSnames_cache.begin(); i != DOSnames_cache.end(); i++) {
+ char fakename[CROSS_LEN];
+ strcpy(fakename,basedir);
+ strcat(fakename,(*i).c_str());
+ CROSS_FILENAME(fakename);
+ dirCache.AddEntry(fakename,true);
+ }
+
+ if (read_directory_contents) {
+ for (i = specials.begin(); i != specials.end();i++) {
+ //Specials look like this DBOVERLAY_YYY_FILENAME.EXT or DIRNAME[\/]DBOVERLAY_YYY_FILENAME.EXT where
+ //YYY is the operation involved. Currently only DEL is supported.
+ //DEL = file marked as deleted, (but exists in localDrive!)
+ std::string name(*i);
+ std::string special_dir("");
+ std::string special_file("");
+ std::string special_operation("");
+ std::string::size_type s = name.find(special_prefix);
+ if (s == std::string::npos) continue;
+ if (s) {
+ special_dir = name.substr(0,s);
+ name.erase(0,s);
+ }
+ name.erase(0,special_prefix.length()+1); //Erase DBOVERLAY_
+ s = name.find("_");
+ if (s == std::string::npos ||s == 0) continue;
+ special_operation = name.substr(0,s);
+ name.erase(0,s + 1);
+ special_file = name;
+ if (special_file.length() == 0) continue;
+ if (special_operation == "DEL") {
+ name = special_dir + special_file;
+ //CROSS_DOSFILENAME for strings:
+ while ( (s = name.find('/')) != std::string::npos) name.replace(s,1,"\\");
+
+ add_deleted_file(name.c_str(),false);
+ } else if (special_operation == "RMD") {
+ name = special_dir + special_file;
+ //CROSS_DOSFILENAME for strings:
+ while ( (s = name.find('/')) != std::string::npos) name.replace(s,1,"\\");
+ add_deleted_path(name.c_str(),false);
+
+ } else {
+ if (logoverlay) LOG_MSG("unsupported operation %s on %s",special_operation.c_str(),(*i).c_str());
+ }
+
+ }
+ }
+ if (logoverlay) LOG_MSG("OPTIMISE: update cache took %d",GetTicks()-a);
+}
+
+bool Overlay_Drive::FindNext(DOS_DTA & dta) {
+
+ char * dir_ent;
+ struct stat stat_block;
+ char full_name[CROSS_LEN];
+ char dir_entcopy[CROSS_LEN];
+
+ Bit8u srch_attr;char srch_pattern[DOS_NAMELENGTH_ASCII];
+ Bit8u find_attr;
+
+ dta.GetSearchParams(srch_attr,srch_pattern);
+ Bit16u id = dta.GetDirID();
+
+again:
+ if (!dirCache.FindNext(id,dir_ent)) {
+ DOS_SetError(DOSERR_NO_MORE_FILES);
+ return false;
+ }
+ if(!WildFileCmp(dir_ent,srch_pattern)) goto again;
+
+ strcpy(full_name,srchInfo[id].srch_dir);
+ strcat(full_name,dir_ent);
+
+ //GetExpandName might indirectly destroy dir_ent (by caching in a new directory
+ //and due to its design dir_ent might be lost.)
+ //Copying dir_ent first
+ strcpy(dir_entcopy,dir_ent);
+
+ //First try overlay:
+ char ovname[CROSS_LEN];
+ char relativename[CROSS_LEN];
+ strcpy(relativename,srchInfo[id].srch_dir);
+ //strip off basedir: //TODO cleanup
+ char* prel = relativename+strlen(basedir);
+ strcpy(ovname,overlaydir);
+ prel =full_name+strlen(basedir);
+
+
+
+#if 0
+ //Check hidden/deleted directories first. TODO is this really needed. If the directory exist in the overlay things are weird anyway.
+ //the deleted paths are added to the deleted_files list.
+ if (is_deleted_dir(prel)) {
+ LOG_MSG("skipping early out deleted dir %s",prel);
+ goto again;
+ }
+#endif
+
+ strcat(ovname,full_name+strlen(basedir));
+ bool statok = ( stat(ovname,&stat_block)==0);
+
+ if (logoverlay) LOG_MSG("listing %s",dir_entcopy);
+ if (statok) {
+ if (logoverlay) LOG_MSG("using overlay data for %s : %s",full_name, ovname);
+ } else {
+ char preldos[CROSS_LEN];
+ strcpy(preldos,prel);
+ CROSS_DOSFILENAME(preldos);
+ if (is_deleted_file(preldos)) { //dir.. maybe lower or keep it as is TODO
+ if (logoverlay) LOG_MSG("skipping deleted file %s %s %s",preldos,full_name,ovname);
+ goto again;
+ }
+ if (stat(dirCache.GetExpandName(full_name),&stat_block)!=0) {
+ if (logoverlay) LOG_MSG("stat failed for %s . This should not happen.",dirCache.GetExpandName(full_name));
+ goto again;//No symlinks and such
+ }
+ }
+
+ if(stat_block.st_mode & S_IFDIR) find_attr=DOS_ATTR_DIRECTORY;
+ else find_attr=DOS_ATTR_ARCHIVE;
+ if (~srch_attr & find_attr & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM)) goto again;
+
+
+ /* file is okay, setup everything to be copied in DTA Block */
+ char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;
+
+ if(strlen(dir_entcopy)<DOS_NAMELENGTH_ASCII){
+ strcpy(find_name,dir_entcopy);
+ upcase(find_name);
+ }
+
+ find_size=(Bit32u) stat_block.st_size;
+ struct tm *time;
+ if((time=localtime(&stat_block.st_mtime))!=0){
+ find_date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday);
+ find_time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec);
+ } else {
+ find_time=6;
+ find_date=4;
+ }
+ dta.SetResult(find_name,find_size,find_date,find_time,find_attr);
+ return true;
+}
+
+
+
+bool Overlay_Drive::FileUnlink(char * name) {
+//TODO check the basedir for file existence in order if we need to add the file to deleted file list.
+ Bitu a = GetTicks();
+ if (logoverlay) LOG_MSG("calling unlink on %s",name);
+ char basename[CROSS_LEN];
+ strcpy(basename,basedir);
+ strcat(basename,name);
+ CROSS_FILENAME(basename);
+
+
+ char overlayname[CROSS_LEN];
+ strcpy(overlayname,overlaydir);
+ strcat(overlayname,name);
+ CROSS_FILENAME(overlayname);
+// char *fullname = dirCache.GetExpandName(newname);
+ if (unlink(overlayname)) {
+ //Unlink failed for some reason try finding it.
+ struct stat buffer;
+ if(stat(overlayname,&buffer)) {
+ //file not found in overlay, check the basedrive
+ //Check if file not already deleted
+ if (is_deleted_file(name)) return false;
+
+
+ char *fullname = dirCache.GetExpandName(basename);
+ if (stat(fullname,&buffer)) return false; // File not found in either, return file false.
+ //File does exist in normal drive.
+ //Maybe do something with the drive_cache.
+ add_deleted_file(name,true);
+ return true;
+// E_Exit("trying to remove existing non-overlay file %s",name);
+ }
+ FILE* file_writable = fopen_wrap(overlayname,"rb+");
+ if(!file_writable) return false; //No access ? ERROR MESSAGE NOT SET. FIXME ?
+ fclose(file_writable);
+
+ //File exists and can technically be deleted, nevertheless it failed.
+ //This means that the file is probably open by some process.
+ //See if We have it open.
+ bool found_file = false;
+ for(Bitu i = 0;i < DOS_FILES;i++){
+ if(Files[i] && Files[i]->IsName(name)) {
+ Bitu max = DOS_FILES;
+ while(Files[i]->IsOpen() && max--) {
+ Files[i]->Close();
+ if (Files[i]->RemoveRef()<=0) break;
+ }
+ found_file=true;
+ }
+ }
+ if(!found_file) return false;
+ if (unlink(overlayname) == 0) { //Overlay file removed
+ //Mark basefile as deleted if it exists:
+ if (localDrive::FileExists(name)) add_deleted_file(name,true);
+ remove_DOSname_from_cache(name); //Should be an else ? although better safe than sorry.
+ //Handle this better
+ dirCache.DeleteEntry(basename);
+ update_cache(false);
+ //Check if it exists in the base dir as well
+
+ return true;
+ }
+ return false;
+ } else { //Removed from overlay.
+ //TODO IF it exists in the basedir: and more locations above.
+ if (localDrive::FileExists(name)) add_deleted_file(name,true);
+ remove_DOSname_from_cache(name);
+ //TODODO remove from the update_cache cache as well
+ //Handle this better
+ //Check if it exists in the base dir as well
+ dirCache.DeleteEntry(basename);
+
+ update_cache(false);
+ if (logoverlay) LOG_MSG("OPTIMISE: unlink took %d",GetTicks()-a);
+ return true;
+ }
+}
+
+
+bool Overlay_Drive::GetFileAttr(char * name,Bit16u * attr) {
+ char overlayname[CROSS_LEN];
+ strcpy(overlayname,overlaydir);
+ strcat(overlayname,name);
+ CROSS_FILENAME(overlayname);
+
+ struct stat status;
+ if (stat(overlayname,&status)==0) {
+ *attr=DOS_ATTR_ARCHIVE;
+ if(status.st_mode & S_IFDIR) *attr|=DOS_ATTR_DIRECTORY;
+ return true;
+ }
+ //Maybe check for deleted path as well
+ if (is_deleted_file(name)) {
+ *attr = 0;
+ return false;
+ }
+ return localDrive::GetFileAttr(name,attr);
+
+}
+
+
+void Overlay_Drive::add_deleted_file(const char* name,bool create_on_disk) {
+ if (logoverlay) LOG_MSG("add del file %s",name);
+ if (!is_deleted_file(name)) {
+ deleted_files_in_base.push_back(name);
+ if (create_on_disk) add_special_file_to_disk(name, "DEL");
+
+ }
+}
+
+void Overlay_Drive::add_special_file_to_disk(const char* dosname, const char* operation) {
+ std::string name = create_filename_of_special_operation(dosname, operation);
+ char overlayname[CROSS_LEN];
+ strcpy(overlayname,overlaydir);
+ strcat(overlayname,name.c_str());
+ CROSS_FILENAME(overlayname);
+ FILE* f = fopen_wrap(overlayname,"wb+");
+ if (!f) {
+ Sync_leading_dirs(dosname);
+ f = fopen_wrap(overlayname,"wb+");
+ }
+ if (!f) E_Exit("Failed creation of %s",overlayname);
+ char buf[5] = {'e','m','p','t','y'};
+ fwrite(buf,5,1,f);
+ fclose(f);
+
+}
+void Overlay_Drive::remove_special_file_from_disk(const char* dosname, const char* operation) {
+ std::string name = create_filename_of_special_operation(dosname,operation);
+ char overlayname[CROSS_LEN];
+ strcpy(overlayname,overlaydir);
+ strcat(overlayname,name.c_str());
+ CROSS_FILENAME(overlayname);
+ if(unlink(overlayname) != 0) E_Exit("Failed removal of %s",overlayname);
+}
+
+std::string Overlay_Drive::create_filename_of_special_operation(const char* dosname, const char* operation) {
+
+ std::string res(dosname);
+ std::string::size_type s = res.rfind("\\"); //CHECK DOS or host endings.... on update_cache
+ if (s == std::string::npos) s = 0; else s++;
+ std::string oper = special_prefix +"_" +operation +"_";
+ res.insert(s,oper);
+ return res;
+
+
+}
+
+
+bool Overlay_Drive::is_dir_only_in_overlay(const char* name) {
+ if (!name || !*name) return false;
+ if (DOSdirs_cache.empty()) return false;
+ for(std::vector<std::string>::iterator it = DOSdirs_cache.begin(); it != DOSdirs_cache.end(); it++) {
+ if (*it == name) return true;
+ }
+ return false;
+}
+
+bool Overlay_Drive::is_deleted_file(const char* name) {
+ if (!name || !*name) return false;
+ if (deleted_files_in_base.empty()) return false;
+ for(std::vector<std::string>::iterator it = deleted_files_in_base.begin(); it != deleted_files_in_base.end(); it++) {
+ if (*it == name) return true;
+ }
+ return false;
+}
+
+void Overlay_Drive::add_DOSdir_to_cache(const char* name) {
+ if (!name || !*name ) return; //Skip empty file.
+ LOG_MSG("Adding name to overlay_only_dir_cache %s",name);
+ if (!is_dir_only_in_overlay(name)) {
+ DOSdirs_cache.push_back(name);
+ }
+}
+
+void Overlay_Drive::remove_DOSdir_from_cache(const char* name) {
+ for(std::vector<std::string>::iterator it = DOSdirs_cache.begin(); it != DOSdirs_cache.end(); it++) {
+ if ( *it == name) {
+ DOSdirs_cache.erase(it);
+ return;
+ }
+ }
+}
+
+void Overlay_Drive::remove_deleted_file(const char* name,bool create_on_disk) {
+ for(std::vector<std::string>::iterator it = deleted_files_in_base.begin(); it != deleted_files_in_base.end(); it++) {
+ if (*it == name) {
+ deleted_files_in_base.erase(it);
+ if (create_on_disk) remove_special_file_from_disk(name, "DEL");
+ return;
+ }
+ }
+}
+void Overlay_Drive::add_deleted_path(const char* name, bool create_on_disk) {
+ if (!name || !*name ) return; //Skip empty file.
+ if (logoverlay) LOG_MSG("add del path %s",name);
+ if (!is_deleted_path(name)) {
+ deleted_paths_in_base.push_back(name);
+ //Add it to deleted files as well, so it gets skipped in FindNext.
+ //Maybe revise that.
+ if (create_on_disk) add_special_file_to_disk(name,"RMD");
+ add_deleted_file(name,false);
+ }
+}
+bool Overlay_Drive::is_deleted_path(const char* name) {
+ if (!name || !*name) return false;
+ if (deleted_paths_in_base.empty()) return false;
+ std::string sname(name);
+ std::string::size_type namelen = sname.length();;
+ for(std::vector<std::string>::iterator it = deleted_paths_in_base.begin(); it != deleted_paths_in_base.end(); it++) {
+ std::string::size_type blockedlen = (*it).length();
+ if (namelen < blockedlen) continue;
+ //See if input starts with name.
+ std::string::size_type n = sname.find(*it);
+ if (n == 0 && (namelen == blockedlen) || *(name+blockedlen) =='\\' ) return true;
+ }
+ return false;
+}
+
+void Overlay_Drive::remove_deleted_path(const char* name, bool create_on_disk) {
+ for(std::vector<std::string>::iterator it = deleted_paths_in_base.begin(); it != deleted_paths_in_base.end(); it++) {
+ if (*it == name) {
+ deleted_paths_in_base.erase(it);
+ remove_deleted_file(name,false); //Rethink maybe.
+ if (create_on_disk) remove_special_file_from_disk(name,"RMD");
+ break;
+ }
+ }
+}
+
+bool Overlay_Drive::FileExists(const char* name) {
+ char overlayname[CROSS_LEN];
+ strcpy(overlayname,overlaydir);
+ strcat(overlayname,name);
+ CROSS_FILENAME(overlayname);
+ struct stat temp_stat;
+ if(stat(overlayname,&temp_stat)==0 && (temp_stat.st_mode & S_IFDIR)==0) return true;
+
+ if (is_deleted_file(name)) return false;
+
+ return localDrive::FileExists(name);
+}
+
+#if 1
+bool Overlay_Drive::Rename(char * oldname,char * newname) {
+ //TODO with cache function!
+//Tricky function.
+//Renaming directories is currently not supported, due the drive_cache not handling that smoothly.
+//So oldname is directory => Exit!
+//If oldname is on overlay => simple rename.
+//if oldname is on base => copy file to overlay with new name and mark old file as deleted.
+//More advanced version. keep track of the file being renamed in order to detect that the file is being renamed back.
+ Bit16u attr=0;
+ if (!GetFileAttr(oldname,&attr)) E_Exit("rename, but source doesn't exist, should not happen %s",oldname);
+ if (attr&DOS_ATTR_DIRECTORY) {
+ //See if the directory exists only in the overlay, then it should be possible.
+#if OVERLAY_DIR
+ if (localDrive::TestDir(oldname)) E_Exit("Overlay: renaming base directory %s to %s not yet supported", oldname,newname);
+#endif
+ E_Exit("renaming directory %s to %s . Not yet supported in Overlay",oldname,newname); //TODO
+ }
+
+ Bitu a = GetTicks();
+ //First generate overlay names.
+ char overlaynameold[CROSS_LEN];
+ strcpy(overlaynameold,overlaydir);
+ strcat(overlaynameold,oldname);
+ CROSS_FILENAME(overlaynameold);
+
+ char overlaynamenew[CROSS_LEN];
+ strcpy(overlaynamenew,overlaydir);
+ strcat(overlaynamenew,newname);
+ CROSS_FILENAME(overlaynamenew);
+
+ //No need to check if the original is marked as deleted, as GetFileAttr would fail if it did.
+
+ //Check if overlay source file exists
+ struct stat tempstat;
+ int temp = -1;
+ if (stat(overlaynameold,&tempstat) ==0) {
+ //Simple rename
+ temp = rename(overlaynameold,overlaynamenew);
+ //TODO CHECK if base has a file with same oldname!!!!! if it does mark it as deleted!!
+ if (localDrive::FileExists(oldname)) add_deleted_file(oldname,true);
+ } else {
+ Bitu aa = GetTicks();
+ //File exists in the basedrive. Make a copy and mark old one as deleted.
+ char newold[CROSS_LEN];
+ strcpy(newold,basedir);
+ strcat(newold,oldname);
+ CROSS_FILENAME(newold);
+ dirCache.ExpandName(newold);
+ FILE* o = fopen_wrap(newold,"rb");
+ if (!o) return false;
+ FILE* n = create_file_in_overlay(newname,"wb+");
+ if (!n) {fclose(o); return false;}
+ char buffer[BUFSIZ];
+ size_t s;
+ while ( (s = fread(buffer,1,BUFSIZ,o)) ) fwrite(buffer, 1, s, n);
+ fclose(o); fclose(n);
+
+ //File copied.
+ //Mark old file as deleted
+ add_deleted_file(oldname,true);
+ temp =0; //success
+ if (logoverlay) LOG_MSG("OPTIMISE: update rename with copy took %d",GetTicks()-aa);
+
+ }
+ if (temp ==0) {
+ //handle the drive_cache (a bit better)
+ //Ensure that the file is not marked as deleted anymore.
+ if (is_deleted_file(newname)) remove_deleted_file(newname,true);
+ dirCache.EmptyCache();
+ update_cache(true);
+ if (logoverlay) LOG_MSG("OPTIMISE: rename took %d",GetTicks()-a);
+
+ }
+ return (temp==0);
+
+}
+#endif
+
+bool Overlay_Drive::FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst) {
+ if (logoverlay) LOG_MSG("FindFirst in %s",_dir);
+
+ if (is_deleted_path(_dir)) {
+ //No accidental listing of files in there.
+ DOS_SetError(DOSERR_PATH_NOT_FOUND);
+ return false;
+ }
+
+ return localDrive::FindFirst(_dir,dta,fcb_findfirst);
+}
+
+bool Overlay_Drive::FileStat(const char* name, FileStat_Block * const stat_block) {
+ char overlayname[CROSS_LEN];
+ strcpy(overlayname,overlaydir);
+ strcat(overlayname,name);
+ CROSS_FILENAME(overlayname);
+ struct stat temp_stat;
+ if(stat(overlayname,&temp_stat) != 0) {
+ if (is_deleted_file(name)) return false;
+ return localDrive::FileStat(name,stat_block);
+ }
+ /* Convert the stat to a FileStat */
+ struct tm *time;
+ if((time=localtime(&temp_stat.st_mtime))!=0) {
+ stat_block->time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec);
+ stat_block->date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday);
+ } else {
+ // ... But this function is not used at the moment.
+ }
+ stat_block->size=(Bit32u)temp_stat.st_size;
+ return true;
+}
+
+Bits Overlay_Drive::UnMount(void) {
+ delete this;
+ return 0;
+}
+void Overlay_Drive::EmptyCache(void){
+ localDrive::EmptyCache();
+ update_cache(true);//lets rebuild it.
+}
+
diff --git a/src/dos/drive_virtual.cpp b/src/dos/drive_virtual.cpp
new file mode 100644
index 000000000..b0261289c
--- /dev/null
+++ b/src/dos/drive_virtual.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "dosbox.h"
+#include "dos_inc.h"
+#include "drives.h"
+#include "support.h"
+#include "cross.h"
+
+struct VFILE_Block {
+ const char * name;
+ Bit8u * data;
+ Bit32u size;
+ Bit16u date;
+ Bit16u time;
+ VFILE_Block * next;
+};
+
+
+static VFILE_Block * first_file;
+
+void VFILE_Register(const char * name,Bit8u * data,Bit32u size) {
+ VFILE_Block * new_file=new VFILE_Block;
+ new_file->name=name;
+ new_file->data=data;
+ new_file->size=size;
+ new_file->date=DOS_PackDate(2002,10,1);
+ new_file->time=DOS_PackTime(12,34,56);
+ new_file->next=first_file;
+ first_file=new_file;
+}
+
+void VFILE_Remove(const char *name) {
+ VFILE_Block * chan=first_file;
+ VFILE_Block * * where=&first_file;
+ while (chan) {
+ if (strcmp(name,chan->name) == 0) {
+ *where = chan->next;
+ if(chan == first_file) first_file = chan->next;
+ delete chan;
+ return;
+ }
+ where=&chan->next;
+ chan=chan->next;
+ }
+}
+
+class Virtual_File : public DOS_File {
+public:
+ Virtual_File(Bit8u * in_data,Bit32u in_size);
+ bool Read(Bit8u * data,Bit16u * size);
+ bool Write(Bit8u * data,Bit16u * size);
+ bool Seek(Bit32u * pos,Bit32u type);
+ bool Close();
+ Bit16u GetInformation(void);
+private:
+ Bit32u file_size;
+ Bit32u file_pos;
+ Bit8u * file_data;
+};
+
+
+Virtual_File::Virtual_File(Bit8u * in_data,Bit32u in_size) {
+ file_size=in_size;
+ file_data=in_data;
+ file_pos=0;
+ date=DOS_PackDate(2002,10,1);
+ time=DOS_PackTime(12,34,56);
+ open=true;
+}
+
+bool Virtual_File::Read(Bit8u * data,Bit16u * size) {
+ Bit32u left=file_size-file_pos;
+ if (left<=*size) {
+ memcpy(data,&file_data[file_pos],left);
+ *size=(Bit16u)left;
+ } else {
+ memcpy(data,&file_data[file_pos],*size);
+ }
+ file_pos+=*size;
+ return true;
+}
+
+bool Virtual_File::Write(Bit8u * data,Bit16u * size){
+ /* Not really writable */
+ return false;
+}
+
+bool Virtual_File::Seek(Bit32u * new_pos,Bit32u type){
+ switch (type) {
+ case DOS_SEEK_SET:
+ if (*new_pos<=file_size) file_pos=*new_pos;
+ else return false;
+ break;
+ case DOS_SEEK_CUR:
+ if ((*new_pos+file_pos)<=file_size) file_pos=*new_pos+file_pos;
+ else return false;
+ break;
+ case DOS_SEEK_END:
+ if (*new_pos<=file_size) file_pos=file_size-*new_pos;
+ else return false;
+ break;
+ }
+ *new_pos=file_pos;
+ return true;
+}
+
+bool Virtual_File::Close(){
+ return true;
+}
+
+
+Bit16u Virtual_File::GetInformation(void) {
+ return 0x40; // read-only drive
+}
+
+
+Virtual_Drive::Virtual_Drive() {
+ strcpy(info,"Internal Virtual Drive");
+ search_file=0;
+}
+
+
+bool Virtual_Drive::FileOpen(DOS_File * * file,char * name,Bit32u flags) {
+/* Scan through the internal list of files */
+ VFILE_Block * cur_file=first_file;
+ while (cur_file) {
+ if (strcasecmp(name,cur_file->name)==0) {
+ /* We have a match */
+ *file=new Virtual_File(cur_file->data,cur_file->size);
+ (*file)->flags=flags;
+ return true;
+ }
+ cur_file=cur_file->next;
+ }
+ return false;
+}
+
+bool Virtual_Drive::FileCreate(DOS_File * * file,char * name,Bit16u attributes) {
+ return false;
+}
+
+bool Virtual_Drive::FileUnlink(char * name) {
+ return false;
+}
+
+bool Virtual_Drive::RemoveDir(char * dir) {
+ return false;
+}
+
+bool Virtual_Drive::MakeDir(char * dir) {
+ return false;
+}
+
+bool Virtual_Drive::TestDir(char * dir) {
+ if (!dir[0]) return true; //only valid dir is the empty dir
+ return false;
+}
+
+bool Virtual_Drive::FileStat(const char* name, FileStat_Block * const stat_block){
+ VFILE_Block * cur_file=first_file;
+ while (cur_file) {
+ if (strcasecmp(name,cur_file->name)==0) {
+ stat_block->attr=DOS_ATTR_ARCHIVE;
+ stat_block->size=cur_file->size;
+ stat_block->date=DOS_PackDate(2002,10,1);
+ stat_block->time=DOS_PackTime(12,34,56);
+ return true;
+ }
+ cur_file=cur_file->next;
+ }
+ return false;
+}
+
+bool Virtual_Drive::FileExists(const char* name){
+ VFILE_Block * cur_file=first_file;
+ while (cur_file) {
+ if (strcasecmp(name,cur_file->name)==0) return true;
+ cur_file=cur_file->next;
+ }
+ return false;
+}
+
+bool Virtual_Drive::FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst) {
+ search_file=first_file;
+ Bit8u attr;char pattern[DOS_NAMELENGTH_ASCII];
+ dta.GetSearchParams(attr,pattern);
+ if (attr == DOS_ATTR_VOLUME) {
+ dta.SetResult(GetLabel(),0,0,0,DOS_ATTR_VOLUME);
+ return true;
+ } else if ((attr & DOS_ATTR_VOLUME) && !fcb_findfirst) {
+ if (WildFileCmp(GetLabel(),pattern)) {
+ dta.SetResult(GetLabel(),0,0,0,DOS_ATTR_VOLUME);
+ return true;
+ }
+ }
+ return FindNext(dta);
+}
+
+bool Virtual_Drive::FindNext(DOS_DTA & dta) {
+ Bit8u attr;char pattern[DOS_NAMELENGTH_ASCII];
+ dta.GetSearchParams(attr,pattern);
+ while (search_file) {
+ if (WildFileCmp(search_file->name,pattern)) {
+ dta.SetResult(search_file->name,search_file->size,search_file->date,search_file->time,DOS_ATTR_ARCHIVE);
+ search_file=search_file->next;
+ return true;
+ }
+ search_file=search_file->next;
+ }
+ DOS_SetError(DOSERR_NO_MORE_FILES);
+ return false;
+}
+
+bool Virtual_Drive::GetFileAttr(char * name,Bit16u * attr) {
+ VFILE_Block * cur_file=first_file;
+ while (cur_file) {
+ if (strcasecmp(name,cur_file->name)==0) {
+ *attr = DOS_ATTR_ARCHIVE; //Maybe readonly ?
+ return true;
+ }
+ cur_file=cur_file->next;
+ }
+ return false;
+}
+
+bool Virtual_Drive::Rename(char * oldname,char * newname) {
+ return false;
+}
+
+bool Virtual_Drive::AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters) {
+ *_bytes_sector=512;
+ *_sectors_cluster=32;
+ *_total_clusters=32765; // total size is always 500 mb
+ *_free_clusters=0; // nothing free here
+ return true;
+}
+
+Bit8u Virtual_Drive::GetMediaByte(void) {
+ return 0xF8;
+}
+
+bool Virtual_Drive::isRemote(void) {
+ return false;
+}
+
+bool Virtual_Drive::isRemovable(void) {
+ return false;
+}
+
+Bits Virtual_Drive::UnMount(void) {
+ return 1;
+}
+
+char const* Virtual_Drive::GetLabel(void) {
+ return "DOSBOX";
+}
diff --git a/src/dos/drives.cpp b/src/dos/drives.cpp
new file mode 100644
index 000000000..6b312bd63
--- /dev/null
+++ b/src/dos/drives.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "dos_system.h"
+#include "drives.h"
+#include "mapper.h"
+#include "support.h"
+
+bool WildFileCmp(const char * file, const char * wild)
+{
+ char file_name[9];
+ char file_ext[4];
+ char wild_name[9];
+ char wild_ext[4];
+ const char * find_ext;
+ Bitu r;
+
+ strcpy(file_name," ");
+ strcpy(file_ext," ");
+ strcpy(wild_name," ");
+ strcpy(wild_ext," ");
+
+ find_ext=strrchr(file,'.');
+ if (find_ext) {
+ Bitu size=(Bitu)(find_ext-file);
+ if (size>8) size=8;
+ memcpy(file_name,file,size);
+ find_ext++;
+ memcpy(file_ext,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext));
+ } else {
+ memcpy(file_name,file,(strlen(file) > 8) ? 8 : strlen(file));
+ }
+ upcase(file_name);upcase(file_ext);
+ find_ext=strrchr(wild,'.');
+ if (find_ext) {
+ Bitu size=(Bitu)(find_ext-wild);
+ if (size>8) size=8;
+ memcpy(wild_name,wild,size);
+ find_ext++;
+ memcpy(wild_ext,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext));
+ } else {
+ memcpy(wild_name,wild,(strlen(wild) > 8) ? 8 : strlen(wild));
+ }
+ upcase(wild_name);upcase(wild_ext);
+ /* Names are right do some checking */
+ r=0;
+ while (r<8) {
+ if (wild_name[r]=='*') goto checkext;
+ if (wild_name[r]!='?' && wild_name[r]!=file_name[r]) return false;
+ r++;
+ }
+checkext:
+ r=0;
+ while (r<3) {
+ if (wild_ext[r]=='*') return true;
+ if (wild_ext[r]!='?' && wild_ext[r]!=file_ext[r]) return false;
+ r++;
+ }
+ return true;
+}
+
+void Set_Label(char const * const input, char * const output, bool cdrom) {
+ Bitu togo = 8;
+ Bitu vnamePos = 0;
+ Bitu labelPos = 0;
+ bool point = false;
+
+ //spacepadding the filenamepart to include spaces after the terminating zero is more closely to the specs. (not doing this now)
+ // HELLO\0' '' '
+
+ while (togo > 0) {
+ if (input[vnamePos]==0) break;
+ if (!point && (input[vnamePos]=='.')) { togo=4; point=true; }
+
+ //another mscdex quirk. Label is not always uppercase. (Daggerfall)
+ output[labelPos] = (cdrom?input[vnamePos]:toupper(input[vnamePos]));
+
+ labelPos++; vnamePos++;
+ togo--;
+ if ((togo==0) && !point) {
+ if (input[vnamePos]=='.') vnamePos++;
+ output[labelPos]='.'; labelPos++; point=true; togo=3;
+ }
+ };
+ output[labelPos]=0;
+
+ //Remove trailing dot. except when on cdrom and filename is exactly 8 (9 including the dot) letters. MSCDEX feature/bug (fifa96 cdrom detection)
+ if((labelPos > 0) && (output[labelPos-1] == '.') && !(cdrom && labelPos ==9))
+ output[labelPos-1] = 0;
+}
+
+
+
+DOS_Drive::DOS_Drive() {
+ curdir[0]=0;
+ info[0]=0;
+}
+
+char * DOS_Drive::GetInfo(void) {
+ return info;
+}
+
+// static members variables
+int DriveManager::currentDrive;
+DriveManager::DriveInfo DriveManager::driveInfos[26];
+
+void DriveManager::AppendDisk(int drive, DOS_Drive* disk) {
+ driveInfos[drive].disks.push_back(disk);
+}
+
+void DriveManager::InitializeDrive(int drive) {
+ currentDrive = drive;
+ DriveInfo& driveInfo = driveInfos[currentDrive];
+ if (driveInfo.disks.size() > 0) {
+ driveInfo.currentDisk = 0;
+ DOS_Drive* disk = driveInfo.disks[driveInfo.currentDisk];
+ Drives[currentDrive] = disk;
+ if (driveInfo.disks.size() > 1) disk->Activate();
+ }
+}
+
+/*
+void DriveManager::CycleDrive(bool pressed) {
+ if (!pressed) return;
+
+ // do one round through all drives or stop at the next drive with multiple disks
+ int oldDrive = currentDrive;
+ do {
+ currentDrive = (currentDrive + 1) % DOS_DRIVES;
+ int numDisks = driveInfos[currentDrive].disks.size();
+ if (numDisks > 1) break;
+ } while (currentDrive != oldDrive);
+}
+
+void DriveManager::CycleDisk(bool pressed) {
+ if (!pressed) return;
+
+ int numDisks = driveInfos[currentDrive].disks.size();
+ if (numDisks > 1) {
+ // cycle disk
+ int currentDisk = driveInfos[currentDrive].currentDisk;
+ DOS_Drive* oldDisk = driveInfos[currentDrive].disks[currentDisk];
+ currentDisk = (currentDisk + 1) % numDisks;
+ DOS_Drive* newDisk = driveInfos[currentDrive].disks[currentDisk];
+ driveInfos[currentDrive].currentDisk = currentDisk;
+
+ // copy working directory, acquire system resources and finally switch to next drive
+ strcpy(newDisk->curdir, oldDisk->curdir);
+ newDisk->Activate();
+ Drives[currentDrive] = newDisk;
+ }
+}
+*/
+
+void DriveManager::CycleDisks(int drive, bool notify) {
+ int numDisks = (int)driveInfos[drive].disks.size();
+ if (numDisks > 1) {
+ // cycle disk
+ int currentDisk = driveInfos[drive].currentDisk;
+ DOS_Drive* oldDisk = driveInfos[drive].disks[currentDisk];
+ currentDisk = (currentDisk + 1) % numDisks;
+ DOS_Drive* newDisk = driveInfos[drive].disks[currentDisk];
+ driveInfos[drive].currentDisk = currentDisk;
+
+ // copy working directory, acquire system resources and finally switch to next drive
+ strcpy(newDisk->curdir, oldDisk->curdir);
+ newDisk->Activate();
+ Drives[drive] = newDisk;
+ if (notify) LOG_MSG("Drive %c: disk %d of %d now active", 'A'+drive, currentDisk+1, numDisks);
+ }
+}
+
+void DriveManager::CycleAllDisks(void) {
+ for (int idrive=0; idrive<DOS_DRIVES; idrive++) CycleDisks(idrive, true);
+}
+
+int DriveManager::UnmountDrive(int drive) {
+ int result = 0;
+ // unmanaged drive
+ if (driveInfos[drive].disks.size() == 0) {
+ result = Drives[drive]->UnMount();
+ } else {
+ // managed drive
+ int currentDisk = driveInfos[drive].currentDisk;
+ result = driveInfos[drive].disks[currentDisk]->UnMount();
+ // only delete on success, current disk set to NULL because of UnMount
+ if (result == 0) {
+ driveInfos[drive].disks[currentDisk] = NULL;
+ for (int i = 0; i < (int)driveInfos[drive].disks.size(); i++) {
+ delete driveInfos[drive].disks[i];
+ }
+ driveInfos[drive].disks.clear();
+ }
+ }
+
+ return result;
+}
+
+void DriveManager::Init(Section* /* sec */) {
+
+ // setup driveInfos structure
+ currentDrive = 0;
+ for(int i = 0; i < DOS_DRIVES; i++) {
+ driveInfos[i].currentDisk = 0;
+ }
+
+// MAPPER_AddHandler(&CycleDisk, MK_f3, MMOD1, "cycledisk", "Cycle Disk");
+// MAPPER_AddHandler(&CycleDrive, MK_f3, MMOD2, "cycledrive", "Cycle Drv");
+}
+
+void DRIVES_Init(Section* sec) {
+ DriveManager::Init(sec);
+}
diff --git a/src/dos/drives.h b/src/dos/drives.h
new file mode 100644
index 000000000..1bf665cda
--- /dev/null
+++ b/src/dos/drives.h
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef _DRIVES_H__
+#define _DRIVES_H__
+
+#include <vector>
+#include <string>
+#include <sys/types.h>
+#include "dos_system.h"
+#include "shell.h" /* for DOS_Shell */
+
+bool WildFileCmp(const char * file, const char * wild);
+void Set_Label(char const * const input, char * const output, bool cdrom);
+
+class DriveManager {
+public:
+ static void AppendDisk(int drive, DOS_Drive* disk);
+ static void InitializeDrive(int drive);
+ static int UnmountDrive(int drive);
+// static void CycleDrive(bool pressed);
+// static void CycleDisk(bool pressed);
+ static void CycleDisks(int drive, bool notify);
+ static void CycleAllDisks(void);
+ static void Init(Section* sec);
+
+private:
+ static struct DriveInfo {
+ std::vector<DOS_Drive*> disks;
+ Bit32u currentDisk;
+ } driveInfos[DOS_DRIVES];
+
+ static int currentDrive;
+};
+
+class localDrive : public DOS_Drive {
+public:
+ localDrive(const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid);
+ virtual bool FileOpen(DOS_File * * file,char * name,Bit32u flags);
+ virtual FILE *GetSystemFilePtr(char const * const name, char const * const type);
+ virtual bool GetSystemFilename(char* sysName, char const * const dosName);
+ virtual bool FileCreate(DOS_File * * file,char * name,Bit16u attributes);
+ virtual bool FileUnlink(char * name);
+ virtual bool RemoveDir(char * dir);
+ virtual bool MakeDir(char * dir);
+ virtual bool TestDir(char * dir);
+ virtual bool FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst=false);
+ virtual bool FindNext(DOS_DTA & dta);
+ virtual bool GetFileAttr(char * name,Bit16u * attr);
+ virtual bool Rename(char * oldname,char * newname);
+ virtual bool AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters);
+ virtual bool FileExists(const char* name);
+ virtual bool FileStat(const char* name, FileStat_Block * const stat_block);
+ virtual Bit8u GetMediaByte(void);
+ virtual bool isRemote(void);
+ virtual bool isRemovable(void);
+ virtual Bits UnMount(void);
+ const char* getBasedir() {return basedir;};
+protected:
+ char basedir[CROSS_LEN];
+private:
+ friend void DOS_Shell::CMD_SUBST(char* args);
+protected:
+ struct {
+ char srch_dir[CROSS_LEN];
+ } srchInfo[MAX_OPENDIRS];
+
+private:
+ struct {
+ Bit16u bytes_sector;
+ Bit8u sectors_cluster;
+ Bit16u total_clusters;
+ Bit16u free_clusters;
+ Bit8u mediaid;
+ } allocation;
+};
+
+#ifdef _MSC_VER
+#pragma pack (1)
+#endif
+struct bootstrap {
+ Bit8u nearjmp[3];
+ Bit8u oemname[8];
+ Bit16u bytespersector;
+ Bit8u sectorspercluster;
+ Bit16u reservedsectors;
+ Bit8u fatcopies;
+ Bit16u rootdirentries;
+ Bit16u totalsectorcount;
+ Bit8u mediadescriptor;
+ Bit16u sectorsperfat;
+ Bit16u sectorspertrack;
+ Bit16u headcount;
+ /* 32-bit FAT extensions */
+ Bit32u hiddensectorcount;
+ Bit32u totalsecdword;
+ Bit8u bootcode[474];
+ Bit8u magic1; /* 0x55 */
+ Bit8u magic2; /* 0xaa */
+} GCC_ATTRIBUTE(packed);
+
+struct direntry {
+ Bit8u entryname[11];
+ Bit8u attrib;
+ Bit8u NTRes;
+ Bit8u milliSecondStamp;
+ Bit16u crtTime;
+ Bit16u crtDate;
+ Bit16u accessDate;
+ Bit16u hiFirstClust;
+ Bit16u modTime;
+ Bit16u modDate;
+ Bit16u loFirstClust;
+ Bit32u entrysize;
+} GCC_ATTRIBUTE(packed);
+
+struct partTable {
+ Bit8u booter[446];
+ struct {
+ Bit8u bootflag;
+ Bit8u beginchs[3];
+ Bit8u parttype;
+ Bit8u endchs[3];
+ Bit32u absSectStart;
+ Bit32u partSize;
+ } pentry[4];
+ Bit8u magic1; /* 0x55 */
+ Bit8u magic2; /* 0xaa */
+} GCC_ATTRIBUTE(packed);
+
+#ifdef _MSC_VER
+#pragma pack ()
+#endif
+//Forward
+class imageDisk;
+class fatDrive : public DOS_Drive {
+public:
+ fatDrive(const char * sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders, Bit32u startSector);
+ virtual bool FileOpen(DOS_File * * file,char * name,Bit32u flags);
+ virtual bool FileCreate(DOS_File * * file,char * name,Bit16u attributes);
+ virtual bool FileUnlink(char * name);
+ virtual bool RemoveDir(char * dir);
+ virtual bool MakeDir(char * dir);
+ virtual bool TestDir(char * dir);
+ virtual bool FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst=false);
+ virtual bool FindNext(DOS_DTA & dta);
+ virtual bool GetFileAttr(char * name,Bit16u * attr);
+ virtual bool Rename(char * oldname,char * newname);
+ virtual bool AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters);
+ virtual bool FileExists(const char* name);
+ virtual bool FileStat(const char* name, FileStat_Block * const stat_block);
+ virtual Bit8u GetMediaByte(void);
+ virtual bool isRemote(void);
+ virtual bool isRemovable(void);
+ virtual Bits UnMount(void);
+public:
+ Bit8u readSector(Bit32u sectnum, void * data);
+ Bit8u writeSector(Bit32u sectnum, void * data);
+ Bit32u getAbsoluteSectFromBytePos(Bit32u startClustNum, Bit32u bytePos);
+ Bit32u getSectorSize(void);
+ Bit32u getClusterSize(void);
+ Bit32u getAbsoluteSectFromChain(Bit32u startClustNum, Bit32u logicalSector);
+ bool allocateCluster(Bit32u useCluster, Bit32u prevCluster);
+ Bit32u appendCluster(Bit32u startCluster);
+ void deleteClustChain(Bit32u startCluster, Bit32u bytePos);
+ Bit32u getFirstFreeClust(void);
+ bool directoryBrowse(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum, Bit32s start=0);
+ bool directoryChange(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum);
+ imageDisk *loadedDisk;
+ bool created_successfully;
+private:
+ Bit32u getClusterValue(Bit32u clustNum);
+ void setClusterValue(Bit32u clustNum, Bit32u clustValue);
+ Bit32u getClustFirstSect(Bit32u clustNum);
+ bool FindNextInternal(Bit32u dirClustNumber, DOS_DTA & dta, direntry *foundEntry);
+ bool getDirClustNum(char * dir, Bit32u * clustNum, bool parDir);
+ bool getFileDirEntry(char const * const filename, direntry * useEntry, Bit32u * dirClust, Bit32u * subEntry);
+ bool addDirectoryEntry(Bit32u dirClustNumber, direntry useEntry);
+ void zeroOutCluster(Bit32u clustNumber);
+ bool getEntryName(char *fullname, char *entname);
+ friend void DOS_Shell::CMD_SUBST(char* args);
+ struct {
+ char srch_dir[CROSS_LEN];
+ } srchInfo[MAX_OPENDIRS];
+
+ struct {
+ Bit16u bytes_sector;
+ Bit8u sectors_cluster;
+ Bit16u total_clusters;
+ Bit16u free_clusters;
+ Bit8u mediaid;
+ } allocation;
+
+ bootstrap bootbuffer;
+ bool absolute;
+ Bit8u fattype;
+ Bit32u CountOfClusters;
+ Bit32u partSectOff;
+ Bit32u firstDataSector;
+ Bit32u firstRootDirSect;
+
+ Bit32u cwdDirCluster;
+ Bit32u dirPosition; /* Position in directory search */
+
+ Bit8u fatSectBuffer[1024];
+ Bit32u curFatSect;
+};
+
+
+class cdromDrive : public localDrive
+{
+public:
+ cdromDrive(const char driveLetter, const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid, int& error);
+ virtual bool FileOpen(DOS_File * * file,char * name,Bit32u flags);
+ virtual bool FileCreate(DOS_File * * file,char * name,Bit16u attributes);
+ virtual bool FileUnlink(char * name);
+ virtual bool RemoveDir(char * dir);
+ virtual bool MakeDir(char * dir);
+ virtual bool Rename(char * oldname,char * newname);
+ virtual bool GetFileAttr(char * name,Bit16u * attr);
+ virtual bool FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst=false);
+ virtual void SetDir(const char* path);
+ virtual bool isRemote(void);
+ virtual bool isRemovable(void);
+ virtual Bits UnMount(void);
+private:
+ Bit8u subUnit;
+ char driveLetter;
+};
+
+#ifdef _MSC_VER
+#pragma pack (1)
+#endif
+struct isoPVD {
+ Bit8u type;
+ Bit8u standardIdent[5];
+ Bit8u version;
+ Bit8u unused1;
+ Bit8u systemIdent[32];
+ Bit8u volumeIdent[32];
+ Bit8u unused2[8];
+ Bit32u volumeSpaceSizeL;
+ Bit32u volumeSpaceSizeM;
+ Bit8u unused3[32];
+ Bit16u volumeSetSizeL;
+ Bit16u volumeSetSizeM;
+ Bit16u volumeSeqNumberL;
+ Bit16u volumeSeqNumberM;
+ Bit16u logicBlockSizeL;
+ Bit16u logicBlockSizeM;
+ Bit32u pathTableSizeL;
+ Bit32u pathTableSizeM;
+ Bit32u locationPathTableL;
+ Bit32u locationOptPathTableL;
+ Bit32u locationPathTableM;
+ Bit32u locationOptPathTableM;
+ Bit8u rootEntry[34];
+ Bit32u unused4[1858];
+} GCC_ATTRIBUTE(packed);
+
+struct isoDirEntry {
+ Bit8u length;
+ Bit8u extAttrLength;
+ Bit32u extentLocationL;
+ Bit32u extentLocationM;
+ Bit32u dataLengthL;
+ Bit32u dataLengthM;
+ Bit8u dateYear;
+ Bit8u dateMonth;
+ Bit8u dateDay;
+ Bit8u timeHour;
+ Bit8u timeMin;
+ Bit8u timeSec;
+ Bit8u timeZone;
+ Bit8u fileFlags;
+ Bit8u fileUnitSize;
+ Bit8u interleaveGapSize;
+ Bit16u VolumeSeqNumberL;
+ Bit16u VolumeSeqNumberM;
+ Bit8u fileIdentLength;
+ Bit8u ident[222];
+} GCC_ATTRIBUTE(packed);
+
+#ifdef _MSC_VER
+#pragma pack ()
+#endif
+
+#if defined (WORDS_BIGENDIAN)
+#define EXTENT_LOCATION(de) ((de).extentLocationM)
+#define DATA_LENGTH(de) ((de).dataLengthM)
+#else
+#define EXTENT_LOCATION(de) ((de).extentLocationL)
+#define DATA_LENGTH(de) ((de).dataLengthL)
+#endif
+
+#define ISO_FRAMESIZE 2048
+#define ISO_ASSOCIATED 4
+#define ISO_DIRECTORY 2
+#define ISO_HIDDEN 1
+#define ISO_MAX_FILENAME_LENGTH 37
+#define ISO_MAXPATHNAME 256
+#define ISO_FIRST_VD 16
+#define IS_ASSOC(fileFlags) (fileFlags & ISO_ASSOCIATED)
+#define IS_DIR(fileFlags) (fileFlags & ISO_DIRECTORY)
+#define IS_HIDDEN(fileFlags) (fileFlags & ISO_HIDDEN)
+#define ISO_MAX_HASH_TABLE_SIZE 100
+
+class isoDrive : public DOS_Drive {
+public:
+ isoDrive(char driveLetter, const char* device_name, Bit8u mediaid, int &error);
+ ~isoDrive();
+ virtual bool FileOpen(DOS_File **file, char *name, Bit32u flags);
+ virtual bool FileCreate(DOS_File **file, char *name, Bit16u attributes);
+ virtual bool FileUnlink(char *name);
+ virtual bool RemoveDir(char *dir);
+ virtual bool MakeDir(char *dir);
+ virtual bool TestDir(char *dir);
+ virtual bool FindFirst(char *_dir, DOS_DTA &dta, bool fcb_findfirst);
+ virtual bool FindNext(DOS_DTA &dta);
+ virtual bool GetFileAttr(char *name, Bit16u *attr);
+ virtual bool Rename(char * oldname,char * newname);
+ virtual bool AllocationInfo(Bit16u *bytes_sector, Bit8u *sectors_cluster, Bit16u *total_clusters, Bit16u *free_clusters);
+ virtual bool FileExists(const char *name);
+ virtual bool FileStat(const char *name, FileStat_Block *const stat_block);
+ virtual Bit8u GetMediaByte(void);
+ virtual void EmptyCache(void){}
+ virtual bool isRemote(void);
+ virtual bool isRemovable(void);
+ virtual Bits UnMount(void);
+ bool readSector(Bit8u *buffer, Bit32u sector);
+ virtual char const* GetLabel(void) {return discLabel;};
+ virtual void Activate(void);
+private:
+ int readDirEntry(isoDirEntry *de, Bit8u *data);
+ bool loadImage();
+ bool lookupSingle(isoDirEntry *de, const char *name, Bit32u sectorStart, Bit32u length);
+ bool lookup(isoDirEntry *de, const char *path);
+ int UpdateMscdex(char driveLetter, const char* physicalPath, Bit8u& subUnit);
+ int GetDirIterator(const isoDirEntry* de);
+ bool GetNextDirEntry(const int dirIterator, isoDirEntry* de);
+ void FreeDirIterator(const int dirIterator);
+ bool ReadCachedSector(Bit8u** buffer, const Bit32u sector);
+
+ struct DirIterator {
+ bool valid;
+ bool root;
+ Bit32u currentSector;
+ Bit32u endSector;
+ Bit32u pos;
+ } dirIterators[MAX_OPENDIRS];
+
+ int nextFreeDirIterator;
+
+ struct SectorHashEntry {
+ bool valid;
+ Bit32u sector;
+ Bit8u data[ISO_FRAMESIZE];
+ } sectorHashEntries[ISO_MAX_HASH_TABLE_SIZE];
+
+ bool iso;
+ bool dataCD;
+ isoDirEntry rootEntry;
+ Bit8u mediaid;
+ char fileName[CROSS_LEN];
+ Bit8u subUnit;
+ char driveLetter;
+ char discLabel[32];
+};
+
+struct VFILE_Block;
+
+class Virtual_Drive: public DOS_Drive {
+public:
+ Virtual_Drive();
+ bool FileOpen(DOS_File * * file,char * name,Bit32u flags);
+ bool FileCreate(DOS_File * * file,char * name,Bit16u attributes);
+ bool FileUnlink(char * name);
+ bool RemoveDir(char * dir);
+ bool MakeDir(char * dir);
+ bool TestDir(char * dir);
+ bool FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst);
+ bool FindNext(DOS_DTA & dta);
+ bool GetFileAttr(char * name,Bit16u * attr);
+ bool Rename(char * oldname,char * newname);
+ bool AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters);
+ bool FileExists(const char* name);
+ bool FileStat(const char* name, FileStat_Block* const stat_block);
+ Bit8u GetMediaByte(void);
+ void EmptyCache(void){}
+ bool isRemote(void);
+ virtual bool isRemovable(void);
+ virtual Bits UnMount(void);
+ virtual char const* GetLabel(void);
+private:
+ VFILE_Block * search_file;
+};
+
+class Overlay_Drive: public localDrive {
+public:
+ Overlay_Drive(const char * startdir,const char* overlay, Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid,Bit8u &error);
+
+ virtual bool FileOpen(DOS_File * * file,char * name,Bit32u flags);
+ virtual bool FileCreate(DOS_File * * file,char * name,Bit16u /*attributes*/);
+ virtual bool FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst);
+ virtual bool FindNext(DOS_DTA & dta);
+ virtual bool FileUnlink(char * name);
+ virtual bool GetFileAttr(char * name,Bit16u * attr);
+ virtual bool FileExists(const char* name);
+ virtual bool Rename(char * oldname,char * newname);
+ virtual bool FileStat(const char* name, FileStat_Block * const stat_block);
+ virtual void EmptyCache(void);
+
+ bool Sync_leading_dirs(const char* dos_filename);
+ FILE* create_file_in_overlay(char* dos_filename, char const* mode);
+ virtual Bits UnMount(void);
+ virtual bool TestDir(char * dir);
+ virtual bool RemoveDir(char * dir);
+ virtual bool MakeDir(char * dir);
+private:
+ char overlaydir[CROSS_LEN];
+ bool optimize_cache_v1;
+ void add_DOSname_to_cache(const char* name);
+ void remove_DOSname_from_cache(const char* name);
+ void add_DOSdir_to_cache(const char* name);
+ void remove_DOSdir_from_cache(const char* name);
+ void update_cache(bool read_directory_contents = false);
+
+ std::vector<std::string> deleted_files_in_base; //Set is probably better, or some other solution (involving the disk).
+ std::vector<std::string> deleted_paths_in_base; //Currently only used to hide the overlay folder.
+ std::string overlap_folder;
+ void add_deleted_file(const char* name, bool create_on_disk);
+ void remove_deleted_file(const char* name, bool create_on_disk);
+ bool is_deleted_file(const char* name);
+ void add_deleted_path(const char* name, bool create_on_disk);
+ void remove_deleted_path(const char* name, bool create_on_disk);
+ bool is_deleted_path(const char* name);
+
+ bool is_dir_only_in_overlay(const char* name); //cached
+
+
+ void remove_special_file_from_disk(const char* dosname, const char* operation);
+ void add_special_file_to_disk(const char* dosname, const char* operation);
+ std::string create_filename_of_special_operation(const char* dosname, const char* operation);
+ void convert_overlay_to_DOSname_in_base(char* dirname );
+ //For caching the update_cache routine.
+ std::vector<std::string> DOSnames_cache; //Also set is probably better.
+ std::vector<std::string> DOSdirs_cache; //Can not blindly change its type. it is important that subdirs come after the parent directory.
+ const std::string special_prefix;
+};
+
+#endif
diff --git a/src/dos/scsidefs.h b/src/dos/scsidefs.h
new file mode 100644
index 000000000..a8b996a41
--- /dev/null
+++ b/src/dos/scsidefs.h
@@ -0,0 +1,283 @@
+/* Got it from Bochs */
+
+//
+// iodev/scsidefs.h
+//
+// This file was copied from ... ?
+//
+
+//***************************************************************************
+//
+// Name: SCSIDEFS.H
+//
+// Description: SCSI definitions ('C' Language)
+//
+//***************************************************************************
+
+//***************************************************************************
+// %%% TARGET STATUS VALUES %%%
+//***************************************************************************
+#define STATUS_GOOD 0x00 // Status Good
+#define STATUS_CHKCOND 0x02 // Check Condition
+#define STATUS_CONDMET 0x04 // Condition Met
+#define STATUS_BUSY 0x08 // Busy
+#define STATUS_INTERM 0x10 // Intermediate
+#define STATUS_INTCDMET 0x14 // Intermediate-condition met
+#define STATUS_RESCONF 0x18 // Reservation conflict
+#define STATUS_COMTERM 0x22 // Command Terminated
+#define STATUS_QFULL 0x28 // Queue full
+
+//***************************************************************************
+// %%% SCSI MISCELLANEOUS EQUATES %%%
+//***************************************************************************
+#define MAXLUN 7 // Maximum Logical Unit Id
+#define MAXTARG 7 // Maximum Target Id
+#define MAX_SCSI_LUNS 64 // Maximum Number of SCSI LUNs
+#define MAX_NUM_HA 8 // Maximum Number of SCSI HA's
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+//
+// %%% SCSI COMMAND OPCODES %%%
+//
+///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+//***************************************************************************
+// %%% Commands for all Device Types %%%
+//***************************************************************************
+#define SCSI_CHANGE_DEF 0x40 // Change Definition (Optional)
+#define SCSI_COMPARE 0x39 // Compare (O)
+#define SCSI_COPY 0x18 // Copy (O)
+#define SCSI_COP_VERIFY 0x3A // Copy and Verify (O)
+#define SCSI_INQUIRY 0x12 // Inquiry (MANDATORY)
+#define SCSI_LOG_SELECT 0x4C // Log Select (O)
+#define SCSI_LOG_SENSE 0x4D // Log Sense (O)
+#define SCSI_MODE_SEL6 0x15 // Mode Select 6-byte (Device Specific)
+#define SCSI_MODE_SEL10 0x55 // Mode Select 10-byte (Device Specific)
+#define SCSI_MODE_SEN6 0x1A // Mode Sense 6-byte (Device Specific)
+#define SCSI_MODE_SEN10 0x5A // Mode Sense 10-byte (Device Specific)
+#define SCSI_READ_BUFF 0x3C // Read Buffer (O)
+#define SCSI_REQ_SENSE 0x03 // Request Sense (MANDATORY)
+#define SCSI_SEND_DIAG 0x1D // Send Diagnostic (O)
+#define SCSI_TST_U_RDY 0x00 // Test Unit Ready (MANDATORY)
+#define SCSI_WRITE_BUFF 0x3B // Write Buffer (O)
+
+//***************************************************************************
+// %%% Commands Unique to Direct Access Devices %%%
+//***************************************************************************
+#define SCSI_COMPARE 0x39 // Compare (O)
+#define SCSI_FORMAT 0x04 // Format Unit (MANDATORY)
+#define SCSI_LCK_UN_CAC 0x36 // Lock Unlock Cache (O)
+#define SCSI_PREFETCH 0x34 // Prefetch (O)
+#define SCSI_MED_REMOVL 0x1E // Prevent/Allow medium Removal (O)
+#define SCSI_READ6 0x08 // Read 6-byte (MANDATORY)
+#define SCSI_READ10 0x28 // Read 10-byte (MANDATORY)
+#define SCSI_RD_CAPAC 0x25 // Read Capacity (MANDATORY)
+#define SCSI_RD_DEFECT 0x37 // Read Defect Data (O)
+#define SCSI_READ_LONG 0x3E // Read Long (O)
+#define SCSI_REASS_BLK 0x07 // Reassign Blocks (O)
+#define SCSI_RCV_DIAG 0x1C // Receive Diagnostic Results (O)
+#define SCSI_RELEASE 0x17 // Release Unit (MANDATORY)
+#define SCSI_REZERO 0x01 // Rezero Unit (O)
+#define SCSI_SRCH_DAT_E 0x31 // Search Data Equal (O)
+#define SCSI_SRCH_DAT_H 0x30 // Search Data High (O)
+#define SCSI_SRCH_DAT_L 0x32 // Search Data Low (O)
+#define SCSI_SEEK6 0x0B // Seek 6-Byte (O)
+#define SCSI_SEEK10 0x2B // Seek 10-Byte (O)
+#define SCSI_SEND_DIAG 0x1D // Send Diagnostics (MANDATORY)
+#define SCSI_SET_LIMIT 0x33 // Set Limits (O)
+#define SCSI_START_STP 0x1B // Start/Stop Unit (O)
+#define SCSI_SYNC_CACHE 0x35 // Synchronize Cache (O)
+#define SCSI_VERIFY 0x2F // Verify (O)
+#define SCSI_WRITE6 0x0A // Write 6-Byte (MANDATORY)
+#define SCSI_WRITE10 0x2A // Write 10-Byte (MANDATORY)
+#define SCSI_WRT_VERIFY 0x2E // Write and Verify (O)
+#define SCSI_WRITE_LONG 0x3F // Write Long (O)
+#define SCSI_WRITE_SAME 0x41 // Write Same (O)
+
+//***************************************************************************
+// %%% Commands Unique to Sequential Access Devices %%%
+//***************************************************************************
+#define SCSI_ERASE 0x19 // Erase (MANDATORY)
+#define SCSI_LOAD_UN 0x1B // Load/Unload (O)
+#define SCSI_LOCATE 0x2B // Locate (O)
+#define SCSI_RD_BLK_LIM 0x05 // Read Block Limits (MANDATORY)
+#define SCSI_READ_POS 0x34 // Read Position (O)
+#define SCSI_READ_REV 0x0F // Read Reverse (O)
+#define SCSI_REC_BF_DAT 0x14 // Recover Buffer Data (O)
+#define SCSI_RESERVE 0x16 // Reserve Unit (MANDATORY)
+#define SCSI_REWIND 0x01 // Rewind (MANDATORY)
+#define SCSI_SPACE 0x11 // Space (MANDATORY)
+#define SCSI_VERIFY_T 0x13 // Verify (Tape) (O)
+#define SCSI_WRT_FILE 0x10 // Write Filemarks (MANDATORY)
+
+//***************************************************************************
+// %%% Commands Unique to Printer Devices %%%
+//***************************************************************************
+#define SCSI_PRINT 0x0A // Print (MANDATORY)
+#define SCSI_SLEW_PNT 0x0B // Slew and Print (O)
+#define SCSI_STOP_PNT 0x1B // Stop Print (O)
+#define SCSI_SYNC_BUFF 0x10 // Synchronize Buffer (O)
+
+//***************************************************************************
+// %%% Commands Unique to Processor Devices %%%
+//***************************************************************************
+#define SCSI_RECEIVE 0x08 // Receive (O)
+#define SCSI_SEND 0x0A // Send (O)
+
+//***************************************************************************
+// %%% Commands Unique to Write-Once Devices %%%
+//***************************************************************************
+#define SCSI_MEDIUM_SCN 0x38 // Medium Scan (O)
+#define SCSI_SRCHDATE10 0x31 // Search Data Equal 10-Byte (O)
+#define SCSI_SRCHDATE12 0xB1 // Search Data Equal 12-Byte (O)
+#define SCSI_SRCHDATH10 0x30 // Search Data High 10-Byte (O)
+#define SCSI_SRCHDATH12 0xB0 // Search Data High 12-Byte (O)
+#define SCSI_SRCHDATL10 0x32 // Search Data Low 10-Byte (O)
+#define SCSI_SRCHDATL12 0xB2 // Search Data Low 12-Byte (O)
+#define SCSI_SET_LIM_10 0x33 // Set Limits 10-Byte (O)
+#define SCSI_SET_LIM_12 0xB3 // Set Limits 10-Byte (O)
+#define SCSI_VERIFY10 0x2F // Verify 10-Byte (O)
+#define SCSI_VERIFY12 0xAF // Verify 12-Byte (O)
+#define SCSI_WRITE12 0xAA // Write 12-Byte (O)
+#define SCSI_WRT_VER10 0x2E // Write and Verify 10-Byte (O)
+#define SCSI_WRT_VER12 0xAE // Write and Verify 12-Byte (O)
+
+//***************************************************************************
+// %%% Commands Unique to CD-ROM Devices %%%
+//***************************************************************************
+#define SCSI_PLAYAUD_10 0x45 // Play Audio 10-Byte (O)
+#define SCSI_PLAYAUD_12 0xA5 // Play Audio 12-Byte 12-Byte (O)
+#define SCSI_PLAYAUDMSF 0x47 // Play Audio MSF (O)
+#define SCSI_PLAYA_TKIN 0x48 // Play Audio Track/Index (O)
+#define SCSI_PLYTKREL10 0x49 // Play Track Relative 10-Byte (O)
+#define SCSI_PLYTKREL12 0xA9 // Play Track Relative 12-Byte (O)
+#define SCSI_READCDCAP 0x25 // Read CD-ROM Capacity (MANDATORY)
+#define SCSI_READHEADER 0x44 // Read Header (O)
+#define SCSI_SUBCHANNEL 0x42 // Read Subchannel (O)
+#define SCSI_READ_TOC 0x43 // Read TOC (O)
+
+//***************************************************************************
+// %%% Commands Unique to Scanner Devices %%%
+//***************************************************************************
+#define SCSI_GETDBSTAT 0x34 // Get Data Buffer Status (O)
+#define SCSI_GETWINDOW 0x25 // Get Window (O)
+#define SCSI_OBJECTPOS 0x31 // Object Postion (O)
+#define SCSI_SCAN 0x1B // Scan (O)
+#define SCSI_SETWINDOW 0x24 // Set Window (MANDATORY)
+
+//***************************************************************************
+// %%% Commands Unique to Optical Memory Devices %%%
+//***************************************************************************
+#define SCSI_UpdateBlk 0x3D // Update Block (O)
+
+//***************************************************************************
+// %%% Commands Unique to Medium Changer Devices %%%
+//***************************************************************************
+#define SCSI_EXCHMEDIUM 0xA6 // Exchange Medium (O)
+#define SCSI_INITELSTAT 0x07 // Initialize Element Status (O)
+#define SCSI_POSTOELEM 0x2B // Position to Element (O)
+#define SCSI_REQ_VE_ADD 0xB5 // Request Volume Element Address (O)
+#define SCSI_SENDVOLTAG 0xB6 // Send Volume Tag (O)
+
+//***************************************************************************
+// %%% Commands Unique to Communication Devices %%%
+//***************************************************************************
+#define SCSI_GET_MSG_6 0x08 // Get Message 6-Byte (MANDATORY)
+#define SCSI_GET_MSG_10 0x28 // Get Message 10-Byte (O)
+#define SCSI_GET_MSG_12 0xA8 // Get Message 12-Byte (O)
+#define SCSI_SND_MSG_6 0x0A // Send Message 6-Byte (MANDATORY)
+#define SCSI_SND_MSG_10 0x2A // Send Message 10-Byte (O)
+#define SCSI_SND_MSG_12 0xAA // Send Message 12-Byte (O)
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+//
+// %%% END OF SCSI COMMAND OPCODES %%%
+//
+///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+//***************************************************************************
+// %%% Request Sense Data Format %%%
+//***************************************************************************
+typedef struct {
+
+ BYTE ErrorCode; // Error Code (70H or 71H)
+ BYTE SegmentNum; // Number of current segment descriptor
+ BYTE SenseKey; // Sense Key(See bit definitions too)
+ BYTE InfoByte0; // Information MSB
+ BYTE InfoByte1; // Information MID
+ BYTE InfoByte2; // Information MID
+ BYTE InfoByte3; // Information LSB
+ BYTE AddSenLen; // Additional Sense Length
+ BYTE ComSpecInf0; // Command Specific Information MSB
+ BYTE ComSpecInf1; // Command Specific Information MID
+ BYTE ComSpecInf2; // Command Specific Information MID
+ BYTE ComSpecInf3; // Command Specific Information LSB
+ BYTE AddSenseCode; // Additional Sense Code
+ BYTE AddSenQual; // Additional Sense Code Qualifier
+ BYTE FieldRepUCode; // Field Replaceable Unit Code
+ BYTE SenKeySpec15; // Sense Key Specific 15th byte
+ BYTE SenKeySpec16; // Sense Key Specific 16th byte
+ BYTE SenKeySpec17; // Sense Key Specific 17th byte
+ BYTE AddSenseBytes; // Additional Sense Bytes
+
+} SENSE_DATA_FMT;
+
+//***************************************************************************
+// %%% REQUEST SENSE ERROR CODE %%%
+//***************************************************************************
+#define SERROR_CURRENT 0x70 // Current Errors
+#define SERROR_DEFERED 0x71 // Deferred Errors
+
+//***************************************************************************
+// %%% REQUEST SENSE BIT DEFINITIONS %%%
+//***************************************************************************
+#define SENSE_VALID 0x80 // Byte 0 Bit 7
+#define SENSE_FILEMRK 0x80 // Byte 2 Bit 7
+#define SENSE_EOM 0x40 // Byte 2 Bit 6
+#define SENSE_ILI 0x20 // Byte 2 Bit 5
+
+//***************************************************************************
+// %%% REQUEST SENSE SENSE KEY DEFINITIONS %%%
+//***************************************************************************
+#define KEY_NOSENSE 0x00 // No Sense
+#define KEY_RECERROR 0x01 // Recovered Error
+#define KEY_NOTREADY 0x02 // Not Ready
+#define KEY_MEDIUMERR 0x03 // Medium Error
+#define KEY_HARDERROR 0x04 // Hardware Error
+#define KEY_ILLGLREQ 0x05 // Illegal Request
+#define KEY_UNITATT 0x06 // Unit Attention
+#define KEY_DATAPROT 0x07 // Data Protect
+#define KEY_BLANKCHK 0x08 // Blank Check
+#define KEY_VENDSPEC 0x09 // Vendor Specific
+#define KEY_COPYABORT 0x0A // Copy Abort
+#define KEY_EQUAL 0x0C // Equal (Search)
+#define KEY_VOLOVRFLW 0x0D // Volume Overflow
+#define KEY_MISCOMP 0x0E // Miscompare (Search)
+#define KEY_RESERVED 0x0F // Reserved
+
+//***************************************************************************
+// %%% PERIPHERAL DEVICE TYPE DEFINITIONS %%%
+//***************************************************************************
+#define DTYPE_DASD 0x00 // Disk Device
+#define DTYPE_SEQD 0x01 // Tape Device
+#define DTYPE_PRNT 0x02 // Printer
+#define DTYPE_PROC 0x03 // Processor
+#define DTYPE_WORM 0x04 // Write-once read-multiple
+#define DTYPE_CROM 0x05 // CD-ROM device
+#define DTYPE_CDROM 0x05 // CD-ROM device
+#define DTYPE_SCAN 0x06 // Scanner device
+#define DTYPE_OPTI 0x07 // Optical memory device
+#define DTYPE_JUKE 0x08 // Medium Changer device
+#define DTYPE_COMM 0x09 // Communications device
+#define DTYPE_RESL 0x0A // Reserved (low)
+#define DTYPE_RESH 0x1E // Reserved (high)
+#define DTYPE_UNKNOWN 0x1F // Unknown or no device type
+
+//***************************************************************************
+// %%% ANSI APPROVED VERSION DEFINITIONS %%%
+//***************************************************************************
+#define ANSI_MAYBE 0x0 // Device may or may not be ANSI approved stand
+#define ANSI_SCSI1 0x1 // Device complies to ANSI X3.131-1986 (SCSI-1)
+#define ANSI_SCSI2 0x2 // Device complies to SCSI-2
+#define ANSI_RESLO 0x3 // Reserved (low)
+#define ANSI_RESHI 0x7 // Reserved (high)
diff --git a/src/dos/wnaspi32.h b/src/dos/wnaspi32.h
new file mode 100644
index 000000000..92b9921bf
--- /dev/null
+++ b/src/dos/wnaspi32.h
@@ -0,0 +1,354 @@
+/******************************************************************************
+**
+** Module Name: wnaspi32.h
+**
+** Description: Header file for ASPI for Win32. This header includes
+** macro and type declarations, and can be included without
+** modification when using Borland C++ or Microsoft Visual
+** C++ with 32-bit compilation. If you are using a different
+** compiler then you MUST ensure that structures are packed
+** onto byte alignments, and that C++ name mangling is turned
+** off.
+**
+** Notes: This file created using 4 spaces per tab.
+**
+******************************************************************************/
+
+#ifndef __WNASPI32_H__
+#define __WNASPI32_H__
+
+/*
+** Make sure structures are packed and undecorated.
+*/
+
+#ifdef __BORLANDC__
+#pragma option -a1
+#endif //__BORLANDC__
+
+#ifdef _MSC_VER
+#pragma pack(1)
+#endif //__MSC_VER
+
+#ifdef __cplusplus
+extern "C" {
+#endif //__cplusplus
+
+//*****************************************************************************
+// %%% SCSI MISCELLANEOUS EQUATES %%%
+//*****************************************************************************
+
+#define SENSE_LEN 14 // Default sense buffer length
+#define SRB_DIR_SCSI 0x00 // Direction determined by SCSI
+#define SRB_POSTING 0x01 // Enable ASPI posting
+#define SRB_ENABLE_RESIDUAL_COUNT 0x04 // Enable residual byte count reporting
+#define SRB_DIR_IN 0x08 // Transfer from SCSI target to host
+#define SRB_DIR_OUT 0x10 // Transfer from host to SCSI target
+#define SRB_EVENT_NOTIFY 0x40 // Enable ASPI event notification
+
+#define RESIDUAL_COUNT_SUPPORTED 0x02 // Extended buffer flag
+#define MAX_SRB_TIMEOUT 108000lu // 30 hour maximum timeout in s
+#define DEFAULT_SRB_TIMEOUT 108000lu // Max timeout by default
+
+
+//*****************************************************************************
+// %%% ASPI Command Definitions %%%
+//*****************************************************************************
+
+#define SC_HA_INQUIRY 0x00 // Host adapter inquiry
+#define SC_GET_DEV_TYPE 0x01 // Get device type
+#define SC_EXEC_SCSI_CMD 0x02 // Execute SCSI command
+#define SC_ABORT_SRB 0x03 // Abort an SRB
+#define SC_RESET_DEV 0x04 // SCSI bus device reset
+#define SC_SET_HA_PARMS 0x05 // Set HA parameters
+#define SC_GET_DISK_INFO 0x06 // Get Disk information
+#define SC_RESCAN_SCSI_BUS 0x07 // ReBuild SCSI device map
+#define SC_GETSET_TIMEOUTS 0x08 // Get/Set target timeouts
+
+//*****************************************************************************
+// %%% SRB Status %%%
+//*****************************************************************************
+
+#define SS_PENDING 0x00 // SRB being processed
+#define SS_COMP 0x01 // SRB completed without error
+#define SS_ABORTED 0x02 // SRB aborted
+#define SS_ABORT_FAIL 0x03 // Unable to abort SRB
+#define SS_ERR 0x04 // SRB completed with error
+
+#define SS_INVALID_CMD 0x80 // Invalid ASPI command
+#define SS_INVALID_HA 0x81 // Invalid host adapter number
+#define SS_NO_DEVICE 0x82 // SCSI device not installed
+
+#define SS_INVALID_SRB 0xE0 // Invalid parameter set in SRB
+#define SS_OLD_MANAGER 0xE1 // ASPI manager doesn't support Windows
+#define SS_BUFFER_ALIGN 0xE1 // Buffer not aligned (replaces OLD_MANAGER in Win32)
+#define SS_ILLEGAL_MODE 0xE2 // Unsupported Windows mode
+#define SS_NO_ASPI 0xE3 // No ASPI managers resident
+#define SS_FAILED_INIT 0xE4 // ASPI for windows failed init
+#define SS_ASPI_IS_BUSY 0xE5 // No resources available to execute cmd
+#define SS_BUFFER_TO_BIG 0xE6 // Buffer size to big to handle!
+#define SS_MISMATCHED_COMPONENTS 0xE7 // The DLLs/EXEs of ASPI don't version check
+#define SS_NO_ADAPTERS 0xE8 // No host adapters to manage
+#define SS_INSUFFICIENT_RESOURCES 0xE9 // Couldn't allocate resources needed to init
+#define SS_ASPI_IS_SHUTDOWN 0xEA // Call came to ASPI after PROCESS_DETACH
+#define SS_BAD_INSTALL 0xEB // The DLL or other components are installed wrong
+
+//*****************************************************************************
+// %%% Host Adapter Status %%%
+//*****************************************************************************
+
+#define HASTAT_OK 0x00 // Host adapter did not detect an // error
+#define HASTAT_SEL_TO 0x11 // Selection Timeout
+#define HASTAT_DO_DU 0x12 // Data overrun data underrun
+#define HASTAT_BUS_FREE 0x13 // Unexpected bus free
+#define HASTAT_PHASE_ERR 0x14 // Target bus phase sequence // failure
+#define HASTAT_TIMEOUT 0x09 // Timed out while SRB was waiting to beprocessed.
+#define HASTAT_COMMAND_TIMEOUT 0x0B // Adapter timed out processing SRB.
+#define HASTAT_MESSAGE_REJECT 0x0D // While processing SRB, the // adapter received a MESSAGE
+#define HASTAT_BUS_RESET 0x0E // A bus reset was detected.
+#define HASTAT_PARITY_ERROR 0x0F // A parity error was detected.
+#define HASTAT_REQUEST_SENSE_FAILED 0x10 // The adapter failed in issuing
+
+//*****************************************************************************
+// %%% SRB - HOST ADAPTER INQUIRY - SC_HA_INQUIRY (0) %%%
+//*****************************************************************************
+
+typedef struct // Offset
+{ // HX/DEC
+ BYTE SRB_Cmd; // 00/000 ASPI command code = SC_HA_INQUIRY
+ BYTE SRB_Status; // 01/001 ASPI command status byte
+ BYTE SRB_HaId; // 02/002 ASPI host adapter number
+ BYTE SRB_Flags; // 03/003 ASPI request flags
+ DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0
+ BYTE HA_Count; // 08/008 Number of host adapters present
+ BYTE HA_SCSI_ID; // 09/009 SCSI ID of host adapter
+ BYTE HA_ManagerId[16]; // 0A/010 String describing the manager
+ BYTE HA_Identifier[16]; // 1A/026 String describing the host adapter
+ BYTE HA_Unique[16]; // 2A/042 Host Adapter Unique parameters
+ WORD HA_Rsvd1; // 3A/058 Reserved, MUST = 0
+}
+SRB_HAInquiry, *PSRB_HAInquiry, FAR *LPSRB_HAInquiry;
+
+//*****************************************************************************
+// %%% SRB - GET DEVICE TYPE - SC_GET_DEV_TYPE (1) %%%
+//*****************************************************************************
+
+typedef struct // Offset
+{ // HX/DEC
+ BYTE SRB_Cmd; // 00/000 ASPI command code = SC_GET_DEV_TYPE
+ BYTE SRB_Status; // 01/001 ASPI command status byte
+ BYTE SRB_HaId; // 02/002 ASPI host adapter number
+ BYTE SRB_Flags; // 03/003 Reserved, MUST = 0
+ DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0
+ BYTE SRB_Target; // 08/008 Target's SCSI ID
+ BYTE SRB_Lun; // 09/009 Target's LUN number
+ BYTE SRB_DeviceType; // 0A/010 Target's peripheral device type
+ BYTE SRB_Rsvd1; // 0B/011 Reserved, MUST = 0
+}
+SRB_GDEVBlock, *PSRB_GDEVBlock, FAR *LPSRB_GDEVBlock;
+
+//*****************************************************************************
+// %%% SRB - EXECUTE SCSI COMMAND - SC_EXEC_SCSI_CMD (2) %%%
+//*****************************************************************************
+
+typedef struct // Offset
+{ // HX/DEC
+ BYTE SRB_Cmd; // 00/000 ASPI command code = SC_EXEC_SCSI_CMD
+ BYTE SRB_Status; // 01/001 ASPI command status byte
+ BYTE SRB_HaId; // 02/002 ASPI host adapter number
+ BYTE SRB_Flags; // 03/003 ASPI request flags
+ DWORD SRB_Hdr_Rsvd; // 04/004 Reserved
+ BYTE SRB_Target; // 08/008 Target's SCSI ID
+ BYTE SRB_Lun; // 09/009 Target's LUN number
+ WORD SRB_Rsvd1; // 0A/010 Reserved for Alignment
+ DWORD SRB_BufLen; // 0C/012 Data Allocation Length
+ BYTE FAR *SRB_BufPointer; // 10/016 Data Buffer Pointer
+ BYTE SRB_SenseLen; // 14/020 Sense Allocation Length
+ BYTE SRB_CDBLen; // 15/021 CDB Length
+ BYTE SRB_HaStat; // 16/022 Host Adapter Status
+ BYTE SRB_TargStat; // 17/023 Target Status
+ VOID FAR *SRB_PostProc; // 18/024 Post routine
+ BYTE SRB_Rsvd2[20]; // 1C/028 Reserved, MUST = 0
+ BYTE CDBByte[16]; // 30/048 SCSI CDB
+ BYTE SenseArea[SENSE_LEN+2]; // 50/064 Request Sense buffer
+}
+SRB_ExecSCSICmd, *PSRB_ExecSCSICmd, FAR *LPSRB_ExecSCSICmd;
+
+//*****************************************************************************
+// %%% SRB - ABORT AN SRB - SC_ABORT_SRB (3) %%%
+//*****************************************************************************
+
+typedef struct // Offset
+{ // HX/DEC
+ BYTE SRB_Cmd; // 00/000 ASPI command code = SC_ABORT_SRB
+ BYTE SRB_Status; // 01/001 ASPI command status byte
+ BYTE SRB_HaId; // 02/002 ASPI host adapter number
+ BYTE SRB_Flags; // 03/003 Reserved
+ DWORD SRB_Hdr_Rsvd; // 04/004 Reserved
+ VOID FAR *SRB_ToAbort; // 08/008 Pointer to SRB to abort
+}
+SRB_Abort, *PSRB_Abort, FAR *LPSRB_Abort;
+
+//*****************************************************************************
+// %%% SRB - BUS DEVICE RESET - SC_RESET_DEV (4) %%%
+//*****************************************************************************
+
+typedef struct // Offset
+{ // HX/DEC
+ BYTE SRB_Cmd; // 00/000 ASPI command code = SC_RESET_DEV
+ BYTE SRB_Status; // 01/001 ASPI command status byte
+ BYTE SRB_HaId; // 02/002 ASPI host adapter number
+ BYTE SRB_Flags; // 03/003 ASPI request flags
+ DWORD SRB_Hdr_Rsvd; // 04/004 Reserved
+ BYTE SRB_Target; // 08/008 Target's SCSI ID
+ BYTE SRB_Lun; // 09/009 Target's LUN number
+ BYTE SRB_Rsvd1[12]; // 0A/010 Reserved for Alignment
+ BYTE SRB_HaStat; // 16/022 Host Adapter Status
+ BYTE SRB_TargStat; // 17/023 Target Status
+ VOID FAR *SRB_PostProc; // 18/024 Post routine
+ BYTE SRB_Rsvd2[36]; // 1C/028 Reserved, MUST = 0
+}
+SRB_BusDeviceReset, *PSRB_BusDeviceReset, FAR *LPSRB_BusDeviceReset;
+
+//*****************************************************************************
+// %%% SRB - GET DISK INFORMATION - SC_GET_DISK_INFO %%%
+//*****************************************************************************
+
+typedef struct // Offset
+{ // HX/DEC
+ BYTE SRB_Cmd; // 00/000 ASPI command code = SC_GET_DISK_INFO
+ BYTE SRB_Status; // 01/001 ASPI command status byte
+ BYTE SRB_HaId; // 02/002 ASPI host adapter number
+ BYTE SRB_Flags; // 03/003 Reserved, MUST = 0
+ DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0
+ BYTE SRB_Target; // 08/008 Target's SCSI ID
+ BYTE SRB_Lun; // 09/009 Target's LUN number
+ BYTE SRB_DriveFlags; // 0A/010 Driver flags
+ BYTE SRB_Int13HDriveInfo; // 0B/011 Host Adapter Status
+ BYTE SRB_Heads; // 0C/012 Preferred number of heads translation
+ BYTE SRB_Sectors; // 0D/013 Preferred number of sectors translation
+ BYTE SRB_Rsvd1[10]; // 0E/014 Reserved, MUST = 0
+}
+SRB_GetDiskInfo, *PSRB_GetDiskInfo, FAR *LPSRB_GetDiskInfo;
+
+//*****************************************************************************
+// %%% SRB - RESCAN SCSI BUS(ES) ON SCSIPORT %%%
+//*****************************************************************************
+
+typedef struct // Offset
+{ // HX/DEC
+ BYTE SRB_Cmd; // 00/000 ASPI command code = SC_RESCAN_SCSI_BUS
+ BYTE SRB_Status; // 01/001 ASPI command status byte
+ BYTE SRB_HaId; // 02/002 ASPI host adapter number
+ BYTE SRB_Flags; // 03/003 Reserved, MUST = 0
+ DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0
+}
+SRB_RescanPort, *PSRB_RescanPort, FAR *LPSRB_RescanPort;
+
+//*****************************************************************************
+// %%% SRB - GET/SET TARGET TIMEOUTS %%%
+//*****************************************************************************
+
+typedef struct // Offset
+{ // HX/DEC
+ BYTE SRB_Cmd; // 00/000 ASPI command code = SC_GETSET_TIMEOUTS
+ BYTE SRB_Status; // 01/001 ASPI command status byte
+ BYTE SRB_HaId; // 02/002 ASPI host adapter number
+ BYTE SRB_Flags; // 03/003 ASPI request flags
+ DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0
+ BYTE SRB_Target; // 08/008 Target's SCSI ID
+ BYTE SRB_Lun; // 09/009 Target's LUN number
+ DWORD SRB_Timeout; // 0A/010 Timeout in half seconds
+}
+SRB_GetSetTimeouts, *PSRB_GetSetTimeouts, FAR *LPSRB_GetSetTimeouts;
+
+//*****************************************************************************
+// %%% ASPIBUFF - Structure For Controllng I/O Buffers %%%
+//*****************************************************************************
+
+typedef struct tag_ASPI32BUFF // Offset
+{ // HX/DEC
+ PBYTE AB_BufPointer; // 00/000 Pointer to the ASPI allocated buffer
+ DWORD AB_BufLen; // 04/004 Length in bytes of the buffer
+ DWORD AB_ZeroFill; // 08/008 Flag set to 1 if buffer should be zeroed
+ DWORD AB_Reserved; // 0C/012 Reserved
+}
+ASPI32BUFF, *PASPI32BUFF, FAR *LPASPI32BUFF;
+
+//*****************************************************************************
+// %%% TOC structures %%%
+//*****************************************************************************
+
+typedef struct
+{
+ unsigned char reserved1;
+ unsigned char cAdrCtrl;
+ unsigned char cTrackNum;
+ unsigned char reserved2;
+ unsigned long lAddr;
+} TOC_TRACK;
+
+typedef struct
+{
+ unsigned short usTocDataLen;
+ unsigned char cFirstTrack;
+ unsigned char cLastTrack;
+ TOC_TRACK tracks[100];
+} TOC, *PTOC, FAR *LPTOC;
+
+//*****************************************************************************
+// %%% PROTOTYPES - User Callable ASPI for Win32 Functions %%%
+//*****************************************************************************
+
+typedef struct
+{
+ BYTE SRB_Cmd;
+ BYTE SRB_Status;
+ BYTE SRB_HaId;
+ BYTE SRB_Flags;
+ DWORD SRB_Hdr_Rsvd;
+} SRB, *PSRB, FAR *LPSRB;
+
+
+#if defined(__BORLANDC__)
+
+DWORD _import GetASPI32SupportInfo( void );
+DWORD _import SendASPI32Command( LPSRB );
+BOOL _import GetASPI32Buffer( PASPI32BUFF );
+BOOL _import FreeASPI32Buffer( PASPI32BUFF );
+BOOL _import TranslateASPI32Address( PDWORD, PDWORD );
+
+#elif defined(_MSC_VER)
+
+__declspec(dllimport) DWORD GetASPI32SupportInfo( void );
+__declspec(dllimport) DWORD SendASPI32Command( LPSRB );
+__declspec(dllimport) BOOL GetASPI32Buffer( PASPI32BUFF );
+__declspec(dllimport) BOOL FreeASPI32Buffer( PASPI32BUFF );
+__declspec(dllimport) BOOL TranslateASPI32Address( PDWORD, PDWORD );
+
+#else
+
+extern DWORD GetASPI32SupportInfo( void );
+extern DWORD GetASPI32Command( LPSRB );
+extern BOOL GetASPI32Buffer( PASPI32BUFF );
+extern BOOL FreeASPI32Buffer( PASPI32BUFF );
+extern BOOL TranslateASPI32Address( PDWORD, PDWORD );
+
+#endif
+
+/*
+** Restore compiler default packing and close off the C declarations.
+*/
+
+#ifdef __BORLANDC__
+#pragma option -a.
+#endif //__BORLANDC__
+
+#ifdef _MSC_VER
+#pragma pack()
+#endif //_MSC_VER
+
+#ifdef __cplusplus
+}
+#endif //__cplusplus
+
+#endif //__WNASPI32_H__
diff --git a/src/dosbox.cpp b/src/dosbox.cpp
new file mode 100644
index 000000000..1e48915c3
--- /dev/null
+++ b/src/dosbox.cpp
@@ -0,0 +1,793 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "dosbox.h"
+#include "debug.h"
+#include "cpu.h"
+#include "video.h"
+#include "pic.h"
+#include "cpu.h"
+#include "callback.h"
+#include "inout.h"
+#include "mixer.h"
+#include "timer.h"
+#include "dos_inc.h"
+#include "setup.h"
+#include "control.h"
+#include "cross.h"
+#include "programs.h"
+#include "support.h"
+#include "mapper.h"
+#include "ints/int10.h"
+#include "render.h"
+#include "pci_bus.h"
+
+Config * control;
+MachineType machine;
+SVGACards svgaCard;
+
+/* The whole load of startups for all the subfunctions */
+void MSG_Init(Section_prop *);
+void LOG_StartUp(void);
+void MEM_Init(Section *);
+void PAGING_Init(Section *);
+void IO_Init(Section * );
+void CALLBACK_Init(Section*);
+void PROGRAMS_Init(Section*);
+//void CREDITS_Init(Section*);
+void RENDER_Init(Section*);
+void VGA_Init(Section*);
+
+void DOS_Init(Section*);
+
+
+void CPU_Init(Section*);
+
+#if C_FPU
+void FPU_Init(Section*);
+#endif
+
+void DMA_Init(Section*);
+
+void MIXER_Init(Section*);
+void MIDI_Init(Section*);
+void HARDWARE_Init(Section*);
+
+#if defined(PCI_FUNCTIONALITY_ENABLED)
+void PCI_Init(Section*);
+#endif
+
+
+void KEYBOARD_Init(Section*); //TODO This should setup INT 16 too but ok ;)
+void JOYSTICK_Init(Section*);
+void MOUSE_Init(Section*);
+void SBLASTER_Init(Section*);
+void GUS_Init(Section*);
+void MPU401_Init(Section*);
+void PCSPEAKER_Init(Section*);
+void TANDYSOUND_Init(Section*);
+void DISNEY_Init(Section*);
+void SERIAL_Init(Section*);
+
+
+#if C_IPX
+void IPX_Init(Section*);
+#endif
+
+void SID_Init(Section* sec);
+
+void PIC_Init(Section*);
+void TIMER_Init(Section*);
+void BIOS_Init(Section*);
+void DEBUG_Init(Section*);
+void CMOS_Init(Section*);
+
+void MSCDEX_Init(Section*);
+void DRIVES_Init(Section*);
+void CDROM_Image_Init(Section*);
+
+/* Dos Internal mostly */
+void EMS_Init(Section*);
+void XMS_Init(Section*);
+
+void DOS_KeyboardLayout_Init(Section*);
+
+void AUTOEXEC_Init(Section*);
+void SHELL_Init(void);
+
+void INT10_Init(Section*);
+
+static LoopHandler * loop;
+
+bool SDLNetInited;
+
+static Bit32u ticksRemain;
+static Bit32u ticksLast;
+static Bit32u ticksAdded;
+Bit32s ticksDone;
+Bit32u ticksScheduled;
+bool ticksLocked;
+void increaseticks();
+
+static Bitu Normal_Loop(void) {
+ Bits ret;
+ while (1) {
+ if (PIC_RunQueue()) {
+ ret = (*cpudecoder)();
+ if (GCC_UNLIKELY(ret<0)) return 1;
+ if (ret>0) {
+ if (GCC_UNLIKELY(ret >= CB_MAX)) return 0;
+ Bitu blah = (*CallBack_Handlers[ret])();
+ if (GCC_UNLIKELY(blah)) return blah;
+ }
+#if C_DEBUG
+ if (DEBUG_ExitLoop()) return 0;
+#endif
+ } else {
+ GFX_Events();
+ if (ticksRemain>0) {
+ TIMER_AddTick();
+ ticksRemain--;
+ } else {increaseticks();return 0;}
+ }
+ }
+}
+
+//For trying other delays
+#define wrap_delay(a) SDL_Delay(a)
+
+void increaseticks() { //Make it return ticksRemain and set it in the function above to remove the global variable.
+ if (GCC_UNLIKELY(ticksLocked)) { // For Fast Forward Mode
+ ticksRemain=5;
+ /* Reset any auto cycle guessing for this frame */
+ ticksLast = GetTicks();
+ ticksAdded = 0;
+ ticksDone = 0;
+ ticksScheduled = 0;
+ return;
+ }
+
+ static Bit32s lastsleepDone = -1;
+ static Bitu sleep1count = 0;
+
+ Bit32u ticksNew;
+ ticksNew = GetTicks();
+ ticksScheduled += ticksAdded;
+ if (ticksNew <= ticksLast) { //lower should not be possible, only equal.
+ ticksAdded = 0;
+
+ if (!CPU_CycleAutoAdjust || CPU_SkipCycleAutoAdjust || sleep1count < 3) {
+ wrap_delay(1);
+ } else {
+ /* Certain configurations always give an exact sleepingtime of 1, this causes problems due to the fact that
+ dosbox keeps track of full blocks.
+ This code introduces some randomness to the time slept, which improves stability on those configurations
+ */
+ static const Bit32u sleeppattern[] = { 2, 2, 3, 2, 2, 4, 2};
+ static Bit32u sleepindex = 0;
+ if (ticksDone != lastsleepDone) sleepindex = 0;
+ wrap_delay(sleeppattern[sleepindex++]);
+ sleepindex %= sizeof(sleeppattern) / sizeof(sleeppattern[0]);
+ }
+ Bit32s timeslept = GetTicks() - ticksNew;
+ // Count how many times in the current block (of 250 ms) the time slept was 1 ms
+ if (CPU_CycleAutoAdjust && !CPU_SkipCycleAutoAdjust && timeslept == 1) sleep1count++;
+ lastsleepDone = ticksDone;
+
+ // Update ticksDone with the time spent sleeping
+ ticksDone -= timeslept;
+ if (ticksDone < 0)
+ ticksDone = 0;
+ return; //0
+
+ // If we do work this tick and sleep till the next tick, then ticksDone is decreased,
+ // despite the fact that work was done as well in this tick. Maybe make it depend on an extra parameter.
+ // What do we know: ticksRemain = 0 (condition to enter this function)
+ // ticksNew = time before sleeping
+
+ // maybe keep track of sleeped time in this frame, and use sleeped and done as indicators. (and take care of the fact there
+ // are frames that have both.
+ }
+
+ //TicksNew > ticksLast
+ ticksRemain = ticksNew-ticksLast;
+ ticksLast = ticksNew;
+ ticksDone += ticksRemain;
+ if ( ticksRemain > 20 ) {
+// LOG(LOG_MISC,LOG_ERROR)("large remain %d",ticksRemain);
+ ticksRemain = 20;
+ }
+ ticksAdded = ticksRemain;
+
+ // Is the system in auto cycle mode guessing ? If not just exit. (It can be temporary disabled)
+ if (!CPU_CycleAutoAdjust || CPU_SkipCycleAutoAdjust) return;
+
+ if (ticksScheduled >= 250 || ticksDone >= 250 || (ticksAdded > 15 && ticksScheduled >= 5) ) {
+ if(ticksDone < 1) ticksDone = 1; // Protect against div by zero
+ /* ratio we are aiming for is around 90% usage*/
+ Bit32s ratio = (ticksScheduled * (CPU_CyclePercUsed*90*1024/100/100)) / ticksDone;
+ Bit32s new_cmax = CPU_CycleMax;
+ Bit64s cproc = (Bit64s)CPU_CycleMax * (Bit64s)ticksScheduled;
+ double ratioremoved = 0.0; //increase scope for logging
+ if (cproc > 0) {
+ /* ignore the cycles added due to the IO delay code in order
+ to have smoother auto cycle adjustments */
+ ratioremoved = (double) CPU_IODelayRemoved / (double) cproc;
+ if (ratioremoved < 1.0) {
+ double ratio_not_removed = 1 - ratioremoved;
+ ratio = (Bit32s)((double)ratio * ratio_not_removed);
+
+ /* Don't allow very high ratio which can cause us to lock as we don't scale down
+ * for very low ratios. High ratio might result because of timing resolution */
+ if (ticksScheduled >= 250 && ticksDone < 10 && ratio > 16384)
+ ratio = 16384;
+
+ // Limit the ratio even more when the cycles are already way above the realmode default.
+ if (ticksScheduled >= 250 && ticksDone < 10 && ratio > 5120 && CPU_CycleMax > 50000)
+ ratio = 5120;
+
+ // When downscaling multiple times in a row, ensure a minimum amount of downscaling
+ if (ticksAdded > 15 && ticksScheduled >= 5 && ticksScheduled <= 20 && ratio > 800)
+ ratio = 800;
+
+ if (ratio <= 1024) {
+ // ratio_not_removed = 1.0; //enabling this restores the old formula
+ double r = (1.0 + ratio_not_removed) /(ratio_not_removed + 1024.0/(static_cast<double>(ratio)));
+ new_cmax = 1 + static_cast<Bit32s>(CPU_CycleMax * r);
+ } else {
+ Bit64s ratio_with_removed = (Bit64s) ((((double)ratio - 1024.0) * ratio_not_removed) + 1024.0);
+ Bit64s cmax_scaled = (Bit64s)CPU_CycleMax * ratio_with_removed;
+ new_cmax = (Bit32s)(1 + (CPU_CycleMax >> 1) + cmax_scaled / (Bit64s)2048);
+ }
+ }
+ }
+
+ if (new_cmax < CPU_CYCLES_LOWER_LIMIT)
+ new_cmax = CPU_CYCLES_LOWER_LIMIT;
+ /*
+ LOG(LOG_MISC,LOG_ERROR)("cyclelog: current %06d cmax %06d ratio %05d done %03d sched %03d Add %d rr %4.2f",
+ CPU_CycleMax,
+ new_cmax,
+ ratio,
+ ticksDone,
+ ticksScheduled,
+ ticksAdded,
+ ratioremoved);
+ */
+
+ /* ratios below 1% are considered to be dropouts due to
+ temporary load imbalance, the cycles adjusting is skipped */
+ if (ratio > 10) {
+ /* ratios below 12% along with a large time since the last update
+ has taken place are most likely caused by heavy load through a
+ different application, the cycles adjusting is skipped as well */
+ if ((ratio > 120) || (ticksDone < 700)) {
+ CPU_CycleMax = new_cmax;
+ if (CPU_CycleLimit > 0) {
+ if (CPU_CycleMax > CPU_CycleLimit) CPU_CycleMax = CPU_CycleLimit;
+ } else if (CPU_CycleMax > 2000000) CPU_CycleMax = 2000000; //Hardcoded limit, if no limit was specified.
+ }
+ }
+
+ //Reset cycleguessing parameters.
+ CPU_IODelayRemoved = 0;
+ ticksDone = 0;
+ ticksScheduled = 0;
+ lastsleepDone = -1;
+ sleep1count = 0;
+ } else if (ticksAdded > 15) {
+ /* ticksAdded > 15 but ticksScheduled < 5, lower the cycles
+ but do not reset the scheduled/done ticks to take them into
+ account during the next auto cycle adjustment */
+ CPU_CycleMax /= 3;
+ if (CPU_CycleMax < CPU_CYCLES_LOWER_LIMIT)
+ CPU_CycleMax = CPU_CYCLES_LOWER_LIMIT;
+ } //if (ticksScheduled >= 250 || ticksDone >= 250 || (ticksAdded > 15 && ticksScheduled >= 5) )
+}
+
+void DOSBOX_SetLoop(LoopHandler * handler) {
+ loop=handler;
+}
+
+void DOSBOX_SetNormalLoop() {
+ loop=Normal_Loop;
+}
+
+void DOSBOX_RunMachine(void){
+ Bitu ret;
+ do {
+ ret=(*loop)();
+ } while (!ret);
+}
+
+static void DOSBOX_UnlockSpeed( bool pressed ) {
+ static bool autoadjust = false;
+ if (pressed) {
+ LOG_MSG("Fast Forward ON");
+ ticksLocked = true;
+ if (CPU_CycleAutoAdjust) {
+ autoadjust = true;
+ CPU_CycleAutoAdjust = false;
+ CPU_CycleMax /= 3;
+ if (CPU_CycleMax<1000) CPU_CycleMax=1000;
+ }
+ } else {
+ LOG_MSG("Fast Forward OFF");
+ ticksLocked = false;
+ if (autoadjust) {
+ autoadjust = false;
+ CPU_CycleAutoAdjust = true;
+ }
+ }
+}
+
+static void DOSBOX_RealInit(Section * sec) {
+ Section_prop * section=static_cast<Section_prop *>(sec);
+ /* Initialize some dosbox internals */
+
+ ticksRemain=0;
+ ticksLast=GetTicks();
+ ticksLocked = false;
+ DOSBOX_SetLoop(&Normal_Loop);
+ MSG_Init(section);
+
+ MAPPER_AddHandler(DOSBOX_UnlockSpeed, MK_f12, MMOD2,"speedlock","Speedlock");
+ std::string cmd_machine;
+ if (control->cmdline->FindString("-machine",cmd_machine,true)){
+ //update value in config (else no matching against suggested values
+ section->HandleInputline(std::string("machine=") + cmd_machine);
+ }
+
+ std::string mtype(section->Get_string("machine"));
+ svgaCard = SVGA_None;
+ machine = MCH_VGA;
+ int10.vesa_nolfb = false;
+ int10.vesa_oldvbe = false;
+ if (mtype == "cga") { machine = MCH_CGA; }
+ else if (mtype == "tandy") { machine = MCH_TANDY; }
+ else if (mtype == "pcjr") { machine = MCH_PCJR; }
+ else if (mtype == "hercules") { machine = MCH_HERC; }
+ else if (mtype == "ega") { machine = MCH_EGA; }
+// else if (mtype == "vga") { svgaCard = SVGA_S3Trio; }
+ else if (mtype == "svga_s3") { svgaCard = SVGA_S3Trio; }
+ else if (mtype == "vesa_nolfb") { svgaCard = SVGA_S3Trio; int10.vesa_nolfb = true;}
+ else if (mtype == "vesa_oldvbe") { svgaCard = SVGA_S3Trio; int10.vesa_oldvbe = true;}
+ else if (mtype == "svga_et4000") { svgaCard = SVGA_TsengET4K; }
+ else if (mtype == "svga_et3000") { svgaCard = SVGA_TsengET3K; }
+// else if (mtype == "vga_pvga1a") { svgaCard = SVGA_ParadisePVGA1A; }
+ else if (mtype == "svga_paradise") { svgaCard = SVGA_ParadisePVGA1A; }
+ else if (mtype == "vgaonly") { svgaCard = SVGA_None; }
+ else E_Exit("DOSBOX:Unknown machine type %s",mtype.c_str());
+}
+
+
+void DOSBOX_Init(void) {
+ Section_prop * secprop;
+ Section_line * secline;
+ Prop_int* Pint;
+ Prop_hex* Phex;
+ Prop_string* Pstring;
+ Prop_bool* Pbool;
+ Prop_multival* Pmulti;
+ Prop_multival_remain* Pmulti_remain;
+
+ SDLNetInited = false;
+
+ // Some frequently used option sets
+ const char *rates[] = { "44100", "48000", "32000","22050", "16000", "11025", "8000", "49716", 0 };
+ const char *oplrates[] = { "44100", "49716", "48000", "32000","22050", "16000", "11025", "8000", 0 };
+ const char *ios[] = { "220", "240", "260", "280", "2a0", "2c0", "2e0", "300", 0 };
+ const char *irqssb[] = { "7", "5", "3", "9", "10", "11", "12", 0 };
+ const char *dmassb[] = { "1", "5", "0", "3", "6", "7", 0 };
+ const char *iosgus[] = { "240", "220", "260", "280", "2a0", "2c0", "2e0", "300", 0 };
+ const char *irqsgus[] = { "5", "3", "7", "9", "10", "11", "12", 0 };
+ const char *dmasgus[] = { "3", "0", "1", "5", "6", "7", 0 };
+
+
+ /* Setup all the different modules making up DOSBox */
+ const char* machines[] = {
+ "hercules", "cga", "tandy", "pcjr", "ega",
+ "vgaonly", "svga_s3", "svga_et3000", "svga_et4000",
+ "svga_paradise", "vesa_nolfb", "vesa_oldvbe", 0 };
+ secprop=control->AddSection_prop("dosbox",&DOSBOX_RealInit);
+ Pstring = secprop->Add_path("language",Property::Changeable::Always,"");
+ Pstring->Set_help("Select another language file.");
+
+ Pstring = secprop->Add_string("machine",Property::Changeable::OnlyAtStart,"svga_s3");
+ Pstring->Set_values(machines);
+ Pstring->Set_help("The type of machine DOSBox tries to emulate.");
+
+ Pstring = secprop->Add_path("captures",Property::Changeable::Always,"capture");
+ Pstring->Set_help("Directory where things like wave, midi, screenshot get captured.");
+
+#if C_DEBUG
+ LOG_StartUp();
+#endif
+
+ secprop->AddInitFunction(&IO_Init);//done
+ secprop->AddInitFunction(&PAGING_Init);//done
+ secprop->AddInitFunction(&MEM_Init);//done
+ secprop->AddInitFunction(&HARDWARE_Init);//done
+ Pint = secprop->Add_int("memsize", Property::Changeable::WhenIdle,16);
+ Pint->SetMinMax(1,63);
+ Pint->Set_help(
+ "Amount of memory DOSBox has in megabytes.\n"
+ "This value is best left at its default to avoid problems with some games,\n"
+ "though few games might require a higher value.\n"
+ "There is generally no speed advantage when raising this value.");
+ secprop->AddInitFunction(&CALLBACK_Init);
+ secprop->AddInitFunction(&PIC_Init);//done
+ secprop->AddInitFunction(&PROGRAMS_Init);
+ secprop->AddInitFunction(&TIMER_Init);//done
+ secprop->AddInitFunction(&CMOS_Init);//done
+
+ secprop=control->AddSection_prop("render",&RENDER_Init,true);
+ Pint = secprop->Add_int("frameskip",Property::Changeable::Always,0);
+ Pint->SetMinMax(0,10);
+ Pint->Set_help("How many frames DOSBox skips before drawing one.");
+
+ Pbool = secprop->Add_bool("aspect",Property::Changeable::Always,false);
+ Pbool->Set_help("Do aspect correction, if your output method doesn't support scaling this can slow things down!");
+
+ Pmulti = secprop->Add_multi("scaler",Property::Changeable::Always," ");
+ Pmulti->SetValue("normal2x");
+ Pmulti->Set_help("Scaler used to enlarge/enhance low resolution modes. If 'forced' is appended,\n"
+ "then the scaler will be used even if the result might not be desired.\n"
+ "To fit a scaler in the resolution used at full screen may require a border or side bars,\n"
+ "to fill the screen entirely, depending on your hardware, a different scaler/fullresolution might work.");
+ Pstring = Pmulti->GetSection()->Add_string("type",Property::Changeable::Always,"normal2x");
+
+ const char *scalers[] = {
+ "none", "normal2x", "normal3x",
+#if RENDER_USE_ADVANCED_SCALERS>2
+ "advmame2x", "advmame3x", "advinterp2x", "advinterp3x", "hq2x", "hq3x", "2xsai", "super2xsai", "supereagle",
+#endif
+#if RENDER_USE_ADVANCED_SCALERS>0
+ "tv2x", "tv3x", "rgb2x", "rgb3x", "scan2x", "scan3x",
+#endif
+ 0 };
+ Pstring->Set_values(scalers);
+
+ const char* force[] = { "", "forced", 0 };
+ Pstring = Pmulti->GetSection()->Add_string("force",Property::Changeable::Always,"");
+ Pstring->Set_values(force);
+
+ secprop=control->AddSection_prop("cpu",&CPU_Init,true);//done
+ const char* cores[] = { "auto",
+#if (C_DYNAMIC_X86) || (C_DYNREC)
+ "dynamic",
+#endif
+ "normal", "simple",0 };
+ Pstring = secprop->Add_string("core",Property::Changeable::WhenIdle,"auto");
+ Pstring->Set_values(cores);
+ Pstring->Set_help("CPU Core used in emulation. auto will switch to dynamic if available and\n"
+ "appropriate.");
+
+ const char* cputype_values[] = { "auto", "386", "386_slow", "486_slow", "pentium_slow", "386_prefetch", 0};
+ Pstring = secprop->Add_string("cputype",Property::Changeable::Always,"auto");
+ Pstring->Set_values(cputype_values);
+ Pstring->Set_help("CPU Type used in emulation. auto is the fastest choice.");
+
+
+ Pmulti_remain = secprop->Add_multiremain("cycles",Property::Changeable::Always," ");
+ Pmulti_remain->Set_help(
+ "Amount of instructions DOSBox tries to emulate each millisecond.\n"
+ "Setting this value too high results in sound dropouts and lags.\n"
+ "Cycles can be set in 3 ways:\n"
+ " 'auto' tries to guess what a game needs.\n"
+ " It usually works, but can fail for certain games.\n"
+ " 'fixed #number' will set a fixed amount of cycles. This is what you usually\n"
+ " need if 'auto' fails (Example: fixed 4000).\n"
+ " 'max' will allocate as much cycles as your computer is able to\n"
+ " handle.");
+
+ const char* cyclest[] = { "auto","fixed","max","%u",0 };
+ Pstring = Pmulti_remain->GetSection()->Add_string("type",Property::Changeable::Always,"auto");
+ Pmulti_remain->SetValue("auto");
+ Pstring->Set_values(cyclest);
+
+ Pstring = Pmulti_remain->GetSection()->Add_string("parameters",Property::Changeable::Always,"");
+
+ Pint = secprop->Add_int("cycleup",Property::Changeable::Always,10);
+ Pint->SetMinMax(1,1000000);
+ Pint->Set_help("Amount of cycles to decrease/increase with keycombos.(CTRL-F11/CTRL-F12)");
+
+ Pint = secprop->Add_int("cycledown",Property::Changeable::Always,20);
+ Pint->SetMinMax(1,1000000);
+ Pint->Set_help("Setting it lower than 100 will be a percentage.");
+
+#if C_FPU
+ secprop->AddInitFunction(&FPU_Init);
+#endif
+ secprop->AddInitFunction(&DMA_Init);//done
+ secprop->AddInitFunction(&VGA_Init);
+ secprop->AddInitFunction(&KEYBOARD_Init);
+
+
+#if defined(PCI_FUNCTIONALITY_ENABLED)
+ secprop=control->AddSection_prop("pci",&PCI_Init,false); //PCI bus
+#endif
+
+
+ secprop=control->AddSection_prop("mixer",&MIXER_Init);
+ Pbool = secprop->Add_bool("nosound",Property::Changeable::OnlyAtStart,false);
+ Pbool->Set_help("Enable silent mode, sound is still emulated though.");
+
+ Pint = secprop->Add_int("rate",Property::Changeable::OnlyAtStart,44100);
+ Pint->Set_values(rates);
+ Pint->Set_help("Mixer sample rate, setting any device's rate higher than this will probably lower their sound quality.");
+
+ const char *blocksizes[] = {
+ "1024", "2048", "4096", "8192", "512", "256", 0};
+ Pint = secprop->Add_int("blocksize",Property::Changeable::OnlyAtStart,1024);
+ Pint->Set_values(blocksizes);
+ Pint->Set_help("Mixer block size, larger blocks might help sound stuttering but sound will also be more lagged.");
+
+ Pint = secprop->Add_int("prebuffer",Property::Changeable::OnlyAtStart,25);
+ Pint->SetMinMax(0,100);
+ Pint->Set_help("How many milliseconds of data to keep on top of the blocksize.");
+
+ secprop=control->AddSection_prop("midi",&MIDI_Init,true);//done
+ secprop->AddInitFunction(&MPU401_Init,true);//done
+
+ const char* mputypes[] = { "intelligent", "uart", "none",0};
+ // FIXME: add some way to offer the actually available choices.
+ const char *devices[] = { "default", "win32", "alsa", "oss", "coreaudio", "coremidi","none", 0};
+ Pstring = secprop->Add_string("mpu401",Property::Changeable::WhenIdle,"intelligent");
+ Pstring->Set_values(mputypes);
+ Pstring->Set_help("Type of MPU-401 to emulate.");
+
+ Pstring = secprop->Add_string("mididevice",Property::Changeable::WhenIdle,"default");
+ Pstring->Set_values(devices);
+ Pstring->Set_help("Device that will receive the MIDI data from MPU-401.");
+
+ Pstring = secprop->Add_string("midiconfig",Property::Changeable::WhenIdle,"");
+ Pstring->Set_help("Special configuration options for the device driver. This is usually the id or part of the name of the device you want to use (find the id/name with mixer/listmidi).\n"
+ "Or in the case of coreaudio, you can specify a soundfont here.\n"
+ "When using a Roland MT-32 rev. 0 as midi output device, some games may require a delay in order to prevent 'buffer overflow' issues.\n"
+ "In that case, add 'delaysysex', for example: midiconfig=2 delaysysex\n"
+ "See the README/Manual for more details.");
+
+#if C_DEBUG
+ secprop=control->AddSection_prop("debug",&DEBUG_Init);
+#endif
+
+ secprop=control->AddSection_prop("sblaster",&SBLASTER_Init,true);//done
+
+ const char* sbtypes[] = { "sb1", "sb2", "sbpro1", "sbpro2", "sb16", "gb", "none", 0 };
+ Pstring = secprop->Add_string("sbtype",Property::Changeable::WhenIdle,"sb16");
+ Pstring->Set_values(sbtypes);
+ Pstring->Set_help("Type of Soundblaster to emulate. gb is Gameblaster.");
+
+ Phex = secprop->Add_hex("sbbase",Property::Changeable::WhenIdle,0x220);
+ Phex->Set_values(ios);
+ Phex->Set_help("The IO address of the soundblaster.");
+
+ Pint = secprop->Add_int("irq",Property::Changeable::WhenIdle,7);
+ Pint->Set_values(irqssb);
+ Pint->Set_help("The IRQ number of the soundblaster.");
+
+ Pint = secprop->Add_int("dma",Property::Changeable::WhenIdle,1);
+ Pint->Set_values(dmassb);
+ Pint->Set_help("The DMA number of the soundblaster.");
+
+ Pint = secprop->Add_int("hdma",Property::Changeable::WhenIdle,5);
+ Pint->Set_values(dmassb);
+ Pint->Set_help("The High DMA number of the soundblaster.");
+
+ Pbool = secprop->Add_bool("sbmixer",Property::Changeable::WhenIdle,true);
+ Pbool->Set_help("Allow the soundblaster mixer to modify the DOSBox mixer.");
+
+ const char* oplmodes[]={ "auto", "cms", "opl2", "dualopl2", "opl3", "opl3gold", "none", 0};
+ Pstring = secprop->Add_string("oplmode",Property::Changeable::WhenIdle,"auto");
+ Pstring->Set_values(oplmodes);
+ Pstring->Set_help("Type of OPL emulation. On 'auto' the mode is determined by sblaster type. All OPL modes are Adlib-compatible, except for 'cms'.");
+
+ const char* oplemus[]={ "default", "compat", "fast", "mame", 0};
+ Pstring = secprop->Add_string("oplemu",Property::Changeable::WhenIdle,"default");
+ Pstring->Set_values(oplemus);
+ Pstring->Set_help("Provider for the OPL emulation. compat might provide better quality (see oplrate as well).");
+
+ Pint = secprop->Add_int("oplrate",Property::Changeable::WhenIdle,44100);
+ Pint->Set_values(oplrates);
+ Pint->Set_help("Sample rate of OPL music emulation. Use 49716 for highest quality (set the mixer rate accordingly).");
+
+
+ secprop=control->AddSection_prop("gus",&GUS_Init,true); //done
+ Pbool = secprop->Add_bool("gus",Property::Changeable::WhenIdle,false);
+ Pbool->Set_help("Enable the Gravis Ultrasound emulation.");
+
+ Pint = secprop->Add_int("gusrate",Property::Changeable::WhenIdle,44100);
+ Pint->Set_values(rates);
+ Pint->Set_help("Sample rate of Ultrasound emulation.");
+
+ Phex = secprop->Add_hex("gusbase",Property::Changeable::WhenIdle,0x240);
+ Phex->Set_values(iosgus);
+ Phex->Set_help("The IO base address of the Gravis Ultrasound.");
+
+ Pint = secprop->Add_int("gusirq",Property::Changeable::WhenIdle,5);
+ Pint->Set_values(irqsgus);
+ Pint->Set_help("The IRQ number of the Gravis Ultrasound.");
+
+ Pint = secprop->Add_int("gusdma",Property::Changeable::WhenIdle,3);
+ Pint->Set_values(dmasgus);
+ Pint->Set_help("The DMA channel of the Gravis Ultrasound.");
+
+ Pstring = secprop->Add_string("ultradir",Property::Changeable::WhenIdle,"C:\\ULTRASND");
+ Pstring->Set_help(
+ "Path to Ultrasound directory. In this directory\n"
+ "there should be a MIDI directory that contains\n"
+ "the patch files for GUS playback. Patch sets used\n"
+ "with Timidity should work fine.");
+
+ secprop = control->AddSection_prop("speaker",&PCSPEAKER_Init,true);//done
+ Pbool = secprop->Add_bool("pcspeaker",Property::Changeable::WhenIdle,true);
+ Pbool->Set_help("Enable PC-Speaker emulation.");
+
+ Pint = secprop->Add_int("pcrate",Property::Changeable::WhenIdle,44100);
+ Pint->Set_values(rates);
+ Pint->Set_help("Sample rate of the PC-Speaker sound generation.");
+
+ secprop->AddInitFunction(&TANDYSOUND_Init,true);//done
+ const char* tandys[] = { "auto", "on", "off", 0};
+ Pstring = secprop->Add_string("tandy",Property::Changeable::WhenIdle,"auto");
+ Pstring->Set_values(tandys);
+ Pstring->Set_help("Enable Tandy Sound System emulation. For 'auto', emulation is present only if machine is set to 'tandy'.");
+
+ Pint = secprop->Add_int("tandyrate",Property::Changeable::WhenIdle,44100);
+ Pint->Set_values(rates);
+ Pint->Set_help("Sample rate of the Tandy 3-Voice generation.");
+
+ secprop->AddInitFunction(&DISNEY_Init,true);//done
+
+ Pbool = secprop->Add_bool("disney",Property::Changeable::WhenIdle,true);
+ Pbool->Set_help("Enable Disney Sound Source emulation. (Covox Voice Master and Speech Thing compatible).");
+
+ secprop=control->AddSection_prop("joystick",&BIOS_Init,false);//done
+ secprop->AddInitFunction(&INT10_Init);
+ secprop->AddInitFunction(&MOUSE_Init); //Must be after int10 as it uses CurMode
+ secprop->AddInitFunction(&JOYSTICK_Init,true);
+ const char* joytypes[] = { "auto", "2axis", "4axis", "4axis_2", "fcs", "ch", "none",0};
+ Pstring = secprop->Add_string("joysticktype",Property::Changeable::WhenIdle,"auto");
+ Pstring->Set_values(joytypes);
+ Pstring->Set_help(
+ "Type of joystick to emulate: auto (default), none,\n"
+ "2axis (supports two joysticks),\n"
+ "4axis (supports one joystick, first joystick used),\n"
+ "4axis_2 (supports one joystick, second joystick used),\n"
+ "fcs (Thrustmaster), ch (CH Flightstick).\n"
+ "none disables joystick emulation.\n"
+ "auto chooses emulation depending on real joystick(s).\n"
+ "(Remember to reset dosbox's mapperfile if you saved it earlier)");
+
+ Pbool = secprop->Add_bool("timed",Property::Changeable::WhenIdle,true);
+ Pbool->Set_help("enable timed intervals for axis. Experiment with this option, if your joystick drifts (away).");
+
+ Pbool = secprop->Add_bool("autofire",Property::Changeable::WhenIdle,false);
+ Pbool->Set_help("continuously fires as long as you keep the button pressed.");
+
+ Pbool = secprop->Add_bool("swap34",Property::Changeable::WhenIdle,false);
+ Pbool->Set_help("swap the 3rd and the 4th axis. Can be useful for certain joysticks.");
+
+ Pbool = secprop->Add_bool("buttonwrap",Property::Changeable::WhenIdle,false);
+ Pbool->Set_help("enable button wrapping at the number of emulated buttons.");
+
+ Pbool = secprop->Add_bool("circularinput",Property::Changeable::WhenIdle,false);
+ Pbool->Set_help("enable translation of circular input to square output.\n"
+ "Try enabling this if your left analog stick can only move in a circle.");
+
+ Pint = secprop->Add_int("deadzone",Property::Changeable::WhenIdle,10);
+ Pint->SetMinMax(0,100);
+ Pint->Set_help("the percentage of motion to ignore. 100 turns the stick into a digital one.");
+
+ secprop=control->AddSection_prop("serial",&SERIAL_Init,true);
+ const char* serials[] = { "dummy", "disabled", "modem", "nullmodem",
+ "directserial",0 };
+
+ Pmulti_remain = secprop->Add_multiremain("serial1",Property::Changeable::WhenIdle," ");
+ Pstring = Pmulti_remain->GetSection()->Add_string("type",Property::Changeable::WhenIdle,"dummy");
+ Pmulti_remain->SetValue("dummy");
+ Pstring->Set_values(serials);
+ Pstring = Pmulti_remain->GetSection()->Add_string("parameters",Property::Changeable::WhenIdle,"");
+ Pmulti_remain->Set_help(
+ "set type of device connected to com port.\n"
+ "Can be disabled, dummy, modem, nullmodem, directserial.\n"
+ "Additional parameters must be in the same line in the form of\n"
+ "parameter:value. Parameter for all types is irq (optional).\n"
+ "for directserial: realport (required), rxdelay (optional).\n"
+ " (realport:COM1 realport:ttyS0).\n"
+ "for modem: listenport (optional).\n"
+ "for nullmodem: server, rxdelay, txdelay, telnet, usedtr,\n"
+ " transparent, port, inhsocket (all optional).\n"
+ "Example: serial1=modem listenport:5000");
+
+ Pmulti_remain = secprop->Add_multiremain("serial2",Property::Changeable::WhenIdle," ");
+ Pstring = Pmulti_remain->GetSection()->Add_string("type",Property::Changeable::WhenIdle,"dummy");
+ Pmulti_remain->SetValue("dummy");
+ Pstring->Set_values(serials);
+ Pstring = Pmulti_remain->GetSection()->Add_string("parameters",Property::Changeable::WhenIdle,"");
+ Pmulti_remain->Set_help("see serial1");
+
+ Pmulti_remain = secprop->Add_multiremain("serial3",Property::Changeable::WhenIdle," ");
+ Pstring = Pmulti_remain->GetSection()->Add_string("type",Property::Changeable::WhenIdle,"disabled");
+ Pmulti_remain->SetValue("disabled");
+ Pstring->Set_values(serials);
+ Pstring = Pmulti_remain->GetSection()->Add_string("parameters",Property::Changeable::WhenIdle,"");
+ Pmulti_remain->Set_help("see serial1");
+
+ Pmulti_remain = secprop->Add_multiremain("serial4",Property::Changeable::WhenIdle," ");
+ Pstring = Pmulti_remain->GetSection()->Add_string("type",Property::Changeable::WhenIdle,"disabled");
+ Pmulti_remain->SetValue("disabled");
+ Pstring->Set_values(serials);
+ Pstring = Pmulti_remain->GetSection()->Add_string("parameters",Property::Changeable::WhenIdle,"");
+ Pmulti_remain->Set_help("see serial1");
+
+
+ /* All the DOS Related stuff, which will eventually start up in the shell */
+ secprop=control->AddSection_prop("dos",&DOS_Init,false);//done
+ secprop->AddInitFunction(&XMS_Init,true);//done
+ Pbool = secprop->Add_bool("xms",Property::Changeable::WhenIdle,true);
+ Pbool->Set_help("Enable XMS support.");
+
+ secprop->AddInitFunction(&EMS_Init,true);//done
+ const char* ems_settings[] = { "true", "emsboard", "emm386", "false", 0};
+ Pstring = secprop->Add_string("ems",Property::Changeable::WhenIdle,"true");
+ Pstring->Set_values(ems_settings);
+ Pstring->Set_help("Enable EMS support. The default (=true) provides the best\n"
+ "compatibility but certain applications may run better with\n"
+ "other choices, or require EMS support to be disabled (=false)\n"
+ "to work at all.");
+
+ Pbool = secprop->Add_bool("umb",Property::Changeable::WhenIdle,true);
+ Pbool->Set_help("Enable UMB support.");
+
+ secprop->AddInitFunction(&DOS_KeyboardLayout_Init,true);
+ Pstring = secprop->Add_string("keyboardlayout",Property::Changeable::WhenIdle, "auto");
+ Pstring->Set_help("Language code of the keyboard layout (or none).");
+
+ // Mscdex
+ secprop->AddInitFunction(&MSCDEX_Init);
+ secprop->AddInitFunction(&DRIVES_Init);
+ secprop->AddInitFunction(&CDROM_Image_Init);
+#if C_IPX
+ secprop=control->AddSection_prop("ipx",&IPX_Init,true);
+ Pbool = secprop->Add_bool("ipx",Property::Changeable::WhenIdle, false);
+ Pbool->Set_help("Enable ipx over UDP/IP emulation.");
+#endif
+// secprop->AddInitFunction(&CREDITS_Init);
+
+ //TODO ?
+ secline=control->AddSection_line("autoexec",&AUTOEXEC_Init);
+ MSG_Add("AUTOEXEC_CONFIGFILE_HELP",
+ "Lines in this section will be run at startup.\n"
+ "You can put your MOUNT lines here.\n"
+ );
+ MSG_Add("CONFIGFILE_INTRO",
+ "# This is the configuration file for DOSBox %s. (Please use the latest version of DOSBox)\n"
+ "# Lines starting with a # are comment lines and are ignored by DOSBox.\n"
+ "# They are used to (briefly) document the effect of each option.\n");
+ MSG_Add("CONFIG_SUGGESTED_VALUES", "Possible values");
+
+ control->SetStartUp(&SHELL_Init);
+}
diff --git a/src/dosbox.ico b/src/dosbox.ico
new file mode 100644
index 000000000..7467ae5e7
--- /dev/null
+++ b/src/dosbox.ico
Binary files differ
diff --git a/src/fpu/Makefile.am b/src/fpu/Makefile.am
new file mode 100644
index 000000000..541d5a466
--- /dev/null
+++ b/src/fpu/Makefile.am
@@ -0,0 +1,5 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libfpu.a
+libfpu_a_SOURCES = fpu.cpp fpu_instructions.h \
+ fpu_instructions_x86.h
diff --git a/src/fpu/fpu.cpp b/src/fpu/fpu.cpp
new file mode 100644
index 000000000..aa8f062f9
--- /dev/null
+++ b/src/fpu/fpu.cpp
@@ -0,0 +1,630 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#if C_FPU
+
+#include <math.h>
+#include <float.h>
+#include "cross.h"
+#include "mem.h"
+#include "fpu.h"
+#include "cpu.h"
+
+FPU_rec fpu;
+
+void FPU_FLDCW(PhysPt addr){
+ Bit16u temp = mem_readw(addr);
+ FPU_SetCW(temp);
+}
+
+Bit16u FPU_GetTag(void){
+ Bit16u tag=0;
+ for(Bitu i=0;i<8;i++)
+ tag |= ( (fpu.tags[i]&3) <<(2*i));
+ return tag;
+}
+
+#if C_FPU_X86
+#include "fpu_instructions_x86.h"
+#else
+#include "fpu_instructions.h"
+#endif
+
+/* WATCHIT : ALWAYS UPDATE REGISTERS BEFORE AND AFTER USING THEM
+ STATUS WORD => FPU_SET_TOP(TOP) BEFORE a read
+ TOP=FPU_GET_TOP() after a write;
+ */
+
+static void EATREE(Bitu _rm){
+ Bitu group=(_rm >> 3) & 7;
+ switch(group){
+ case 0x00: /* FADD */
+ FPU_FADD_EA(TOP);
+ break;
+ case 0x01: /* FMUL */
+ FPU_FMUL_EA(TOP);
+ break;
+ case 0x02: /* FCOM */
+ FPU_FCOM_EA(TOP);
+ break;
+ case 0x03: /* FCOMP */
+ FPU_FCOM_EA(TOP);
+ FPU_FPOP();
+ break;
+ case 0x04: /* FSUB */
+ FPU_FSUB_EA(TOP);
+ break;
+ case 0x05: /* FSUBR */
+ FPU_FSUBR_EA(TOP);
+ break;
+ case 0x06: /* FDIV */
+ FPU_FDIV_EA(TOP);
+ break;
+ case 0x07: /* FDIVR */
+ FPU_FDIVR_EA(TOP);
+ break;
+ default:
+ break;
+ }
+}
+
+void FPU_ESC0_EA(Bitu rm,PhysPt addr) {
+ /* REGULAR TREE WITH 32 BITS REALS */
+ FPU_FLD_F32_EA(addr);
+ EATREE(rm);
+}
+
+void FPU_ESC0_Normal(Bitu rm) {
+ Bitu group=(rm >> 3) & 7;
+ Bitu sub=(rm & 7);
+ switch (group){
+ case 0x00: /* FADD ST,STi */
+ FPU_FADD(TOP,STV(sub));
+ break;
+ case 0x01: /* FMUL ST,STi */
+ FPU_FMUL(TOP,STV(sub));
+ break;
+ case 0x02: /* FCOM STi */
+ FPU_FCOM(TOP,STV(sub));
+ break;
+ case 0x03: /* FCOMP STi */
+ FPU_FCOM(TOP,STV(sub));
+ FPU_FPOP();
+ break;
+ case 0x04: /* FSUB ST,STi */
+ FPU_FSUB(TOP,STV(sub));
+ break;
+ case 0x05: /* FSUBR ST,STi */
+ FPU_FSUBR(TOP,STV(sub));
+ break;
+ case 0x06: /* FDIV ST,STi */
+ FPU_FDIV(TOP,STV(sub));
+ break;
+ case 0x07: /* FDIVR ST,STi */
+ FPU_FDIVR(TOP,STV(sub));
+ break;
+ default:
+ break;
+ }
+}
+
+void FPU_ESC1_EA(Bitu rm,PhysPt addr) {
+// floats
+ Bitu group=(rm >> 3) & 7;
+ Bitu sub=(rm & 7);
+ switch(group){
+ case 0x00: /* FLD float*/
+ FPU_PREP_PUSH();
+ FPU_FLD_F32(addr,TOP);
+ break;
+ case 0x01: /* UNKNOWN */
+ LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",group,sub);
+ break;
+ case 0x02: /* FST float*/
+ FPU_FST_F32(addr);
+ break;
+ case 0x03: /* FSTP float*/
+ FPU_FST_F32(addr);
+ FPU_FPOP();
+ break;
+ case 0x04: /* FLDENV */
+ FPU_FLDENV(addr);
+ break;
+ case 0x05: /* FLDCW */
+ FPU_FLDCW(addr);
+ break;
+ case 0x06: /* FSTENV */
+ FPU_FSTENV(addr);
+ break;
+ case 0x07: /* FNSTCW*/
+ mem_writew(addr,fpu.cw);
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+}
+
+void FPU_ESC1_Normal(Bitu rm) {
+ Bitu group=(rm >> 3) & 7;
+ Bitu sub=(rm & 7);
+ switch (group){
+ case 0x00: /* FLD STi */
+ {
+ Bitu reg_from=STV(sub);
+ FPU_PREP_PUSH();
+ FPU_FST(reg_from, TOP);
+ break;
+ }
+ case 0x01: /* FXCH STi */
+ FPU_FXCH(TOP,STV(sub));
+ break;
+ case 0x02: /* FNOP */
+ FPU_FNOP();
+ break;
+ case 0x03: /* FSTP STi */
+ FPU_FST(TOP,STV(sub));
+ FPU_FPOP();
+ break;
+ case 0x04:
+ switch(sub){
+ case 0x00: /* FCHS */
+ FPU_FCHS();
+ break;
+ case 0x01: /* FABS */
+ FPU_FABS();
+ break;
+ case 0x02: /* UNKNOWN */
+ case 0x03: /* ILLEGAL */
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",group,sub);
+ break;
+ case 0x04: /* FTST */
+ FPU_FTST();
+ break;
+ case 0x05: /* FXAM */
+ FPU_FXAM();
+ break;
+ case 0x06: /* FTSTP (cyrix)*/
+ case 0x07: /* UNKNOWN */
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",group,sub);
+ break;
+ }
+ break;
+ case 0x05:
+ switch(sub){
+ case 0x00: /* FLD1 */
+ FPU_FLD1();
+ break;
+ case 0x01: /* FLDL2T */
+ FPU_FLDL2T();
+ break;
+ case 0x02: /* FLDL2E */
+ FPU_FLDL2E();
+ break;
+ case 0x03: /* FLDPI */
+ FPU_FLDPI();
+ break;
+ case 0x04: /* FLDLG2 */
+ FPU_FLDLG2();
+ break;
+ case 0x05: /* FLDLN2 */
+ FPU_FLDLN2();
+ break;
+ case 0x06: /* FLDZ*/
+ FPU_FLDZ();
+ break;
+ case 0x07: /* ILLEGAL */
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",group,sub);
+ break;
+ }
+ break;
+ case 0x06:
+ switch(sub){
+ case 0x00: /* F2XM1 */
+ FPU_F2XM1();
+ break;
+ case 0x01: /* FYL2X */
+ FPU_FYL2X();
+ break;
+ case 0x02: /* FPTAN */
+ FPU_FPTAN();
+ break;
+ case 0x03: /* FPATAN */
+ FPU_FPATAN();
+ break;
+ case 0x04: /* FXTRACT */
+ FPU_FXTRACT();
+ break;
+ case 0x05: /* FPREM1 */
+ FPU_FPREM1();
+ break;
+ case 0x06: /* FDECSTP */
+ TOP = (TOP - 1) & 7;
+ break;
+ case 0x07: /* FINCSTP */
+ TOP = (TOP + 1) & 7;
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",group,sub);
+ break;
+ }
+ break;
+ case 0x07:
+ switch(sub){
+ case 0x00: /* FPREM */
+ FPU_FPREM();
+ break;
+ case 0x01: /* FYL2XP1 */
+ FPU_FYL2XP1();
+ break;
+ case 0x02: /* FSQRT */
+ FPU_FSQRT();
+ break;
+ case 0x03: /* FSINCOS */
+ FPU_FSINCOS();
+ break;
+ case 0x04: /* FRNDINT */
+ FPU_FRNDINT();
+ break;
+ case 0x05: /* FSCALE */
+ FPU_FSCALE();
+ break;
+ case 0x06: /* FSIN */
+ FPU_FSIN();
+ break;
+ case 0x07: /* FCOS */
+ FPU_FCOS();
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",group,sub);
+ break;
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",group,sub);
+ }
+}
+
+
+void FPU_ESC2_EA(Bitu rm,PhysPt addr) {
+ /* 32 bits integer operants */
+ FPU_FLD_I32_EA(addr);
+ EATREE(rm);
+}
+
+void FPU_ESC2_Normal(Bitu rm) {
+ Bitu group=(rm >> 3) & 7;
+ Bitu sub=(rm & 7);
+ switch(group){
+ case 0x05:
+ switch(sub){
+ case 0x01: /* FUCOMPP */
+ FPU_FUCOM(TOP,STV(1));
+ FPU_FPOP();
+ FPU_FPOP();
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 2:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 2:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+}
+
+
+void FPU_ESC3_EA(Bitu rm,PhysPt addr) {
+ Bitu group=(rm >> 3) & 7;
+ Bitu sub=(rm & 7);
+ switch(group){
+ case 0x00: /* FILD */
+ FPU_PREP_PUSH();
+ FPU_FLD_I32(addr,TOP);
+ break;
+ case 0x01: /* FISTTP */
+ LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",group,sub);
+ break;
+ case 0x02: /* FIST */
+ FPU_FST_I32(addr);
+ break;
+ case 0x03: /* FISTP */
+ FPU_FST_I32(addr);
+ FPU_FPOP();
+ break;
+ case 0x05: /* FLD 80 Bits Real */
+ FPU_PREP_PUSH();
+ FPU_FLD_F80(addr);
+ break;
+ case 0x07: /* FSTP 80 Bits Real */
+ FPU_FST_F80(addr);
+ FPU_FPOP();
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",group,sub);
+ }
+}
+
+void FPU_ESC3_Normal(Bitu rm) {
+ Bitu group=(rm >> 3) & 7;
+ Bitu sub=(rm & 7);
+ switch (group) {
+ case 0x04:
+ switch (sub) {
+ case 0x00: //FNENI
+ case 0x01: //FNDIS
+ LOG(LOG_FPU,LOG_ERROR)("8087 only fpu code used esc 3: group 4: subfuntion :%d",sub);
+ break;
+ case 0x02: //FNCLEX FCLEX
+ FPU_FCLEX();
+ break;
+ case 0x03: //FNINIT FINIT
+ FPU_FINIT();
+ break;
+ case 0x04: //FNSETPM
+ case 0x05: //FRSTPM
+// LOG(LOG_FPU,LOG_ERROR)("80267 protected mode (un)set. Nothing done");
+ FPU_FNOP();
+ break;
+ default:
+ E_Exit("ESC 3:ILLEGAL OPCODE group %d subfunction %d",group,sub);
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 3:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ return;
+}
+
+
+void FPU_ESC4_EA(Bitu rm,PhysPt addr) {
+ /* REGULAR TREE WITH 64 BITS REALS */
+ FPU_FLD_F64_EA(addr);
+ EATREE(rm);
+}
+
+void FPU_ESC4_Normal(Bitu rm) {
+ /* LOOKS LIKE number 6 without popping */
+ Bitu group=(rm >> 3) & 7;
+ Bitu sub=(rm & 7);
+ switch(group){
+ case 0x00: /* FADD STi,ST*/
+ FPU_FADD(STV(sub),TOP);
+ break;
+ case 0x01: /* FMUL STi,ST*/
+ FPU_FMUL(STV(sub),TOP);
+ break;
+ case 0x02: /* FCOM*/
+ FPU_FCOM(TOP,STV(sub));
+ break;
+ case 0x03: /* FCOMP*/
+ FPU_FCOM(TOP,STV(sub));
+ FPU_FPOP();
+ break;
+ case 0x04: /* FSUBR STi,ST*/
+ FPU_FSUBR(STV(sub),TOP);
+ break;
+ case 0x05: /* FSUB STi,ST*/
+ FPU_FSUB(STV(sub),TOP);
+ break;
+ case 0x06: /* FDIVR STi,ST*/
+ FPU_FDIVR(STV(sub),TOP);
+ break;
+ case 0x07: /* FDIV STi,ST*/
+ FPU_FDIV(STV(sub),TOP);
+ break;
+ default:
+ break;
+ }
+}
+
+void FPU_ESC5_EA(Bitu rm,PhysPt addr) {
+ Bitu group=(rm >> 3) & 7;
+ Bitu sub=(rm & 7);
+ switch(group){
+ case 0x00: /* FLD double real*/
+ FPU_PREP_PUSH();
+ FPU_FLD_F64(addr,TOP);
+ break;
+ case 0x01: /* FISTTP longint*/
+ LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",group,sub);
+ break;
+ case 0x02: /* FST double real*/
+ FPU_FST_F64(addr);
+ break;
+ case 0x03: /* FSTP double real*/
+ FPU_FST_F64(addr);
+ FPU_FPOP();
+ break;
+ case 0x04: /* FRSTOR */
+ FPU_FRSTOR(addr);
+ break;
+ case 0x06: /* FSAVE */
+ FPU_FSAVE(addr);
+ break;
+ case 0x07: /*FNSTSW NG DISAGREES ON THIS*/
+ FPU_SET_TOP(TOP);
+ mem_writew(addr,fpu.sw);
+ //seems to break all dos4gw games :)
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",group,sub);
+ }
+}
+
+void FPU_ESC5_Normal(Bitu rm) {
+ Bitu group=(rm >> 3) & 7;
+ Bitu sub=(rm & 7);
+ switch(group){
+ case 0x00: /* FFREE STi */
+ fpu.tags[STV(sub)]=TAG_Empty;
+ break;
+ case 0x01: /* FXCH STi*/
+ FPU_FXCH(TOP,STV(sub));
+ break;
+ case 0x02: /* FST STi */
+ FPU_FST(TOP,STV(sub));
+ break;
+ case 0x03: /* FSTP STi*/
+ FPU_FST(TOP,STV(sub));
+ FPU_FPOP();
+ break;
+ case 0x04: /* FUCOM STi */
+ FPU_FUCOM(TOP,STV(sub));
+ break;
+ case 0x05: /*FUCOMP STi */
+ FPU_FUCOM(TOP,STV(sub));
+ FPU_FPOP();
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 5:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+}
+
+void FPU_ESC6_EA(Bitu rm,PhysPt addr) {
+ /* 16 bit (word integer) operants */
+ FPU_FLD_I16_EA(addr);
+ EATREE(rm);
+}
+
+void FPU_ESC6_Normal(Bitu rm) {
+ /* all P variants working only on registers */
+ /* get top before switch and pop afterwards */
+ Bitu group=(rm >> 3) & 7;
+ Bitu sub=(rm & 7);
+ switch(group){
+ case 0x00: /*FADDP STi,ST*/
+ FPU_FADD(STV(sub),TOP);
+ break;
+ case 0x01: /* FMULP STi,ST*/
+ FPU_FMUL(STV(sub),TOP);
+ break;
+ case 0x02: /* FCOMP5*/
+ FPU_FCOM(TOP,STV(sub));
+ break; /* TODO IS THIS ALLRIGHT ????????? */
+ case 0x03: /*FCOMPP*/
+ if(sub != 1) {
+ LOG(LOG_FPU,LOG_WARN)("ESC 6:Unhandled group %d subfunction %d",group,sub);
+ return;
+ }
+ FPU_FCOM(TOP,STV(1));
+ FPU_FPOP(); /* extra pop at the bottom*/
+ break;
+ case 0x04: /* FSUBRP STi,ST*/
+ FPU_FSUBR(STV(sub),TOP);
+ break;
+ case 0x05: /* FSUBP STi,ST*/
+ FPU_FSUB(STV(sub),TOP);
+ break;
+ case 0x06: /* FDIVRP STi,ST*/
+ FPU_FDIVR(STV(sub),TOP);
+ break;
+ case 0x07: /* FDIVP STi,ST*/
+ FPU_FDIV(STV(sub),TOP);
+ break;
+ default:
+ break;
+ }
+ FPU_FPOP();
+}
+
+
+void FPU_ESC7_EA(Bitu rm,PhysPt addr) {
+ Bitu group=(rm >> 3) & 7;
+ Bitu sub=(rm & 7);
+ switch(group){
+ case 0x00: /* FILD Bit16s */
+ FPU_PREP_PUSH();
+ FPU_FLD_I16(addr,TOP);
+ break;
+ case 0x01:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",group,sub);
+ break;
+ case 0x02: /* FIST Bit16s */
+ FPU_FST_I16(addr);
+ break;
+ case 0x03: /* FISTP Bit16s */
+ FPU_FST_I16(addr);
+ FPU_FPOP();
+ break;
+ case 0x04: /* FBLD packed BCD */
+ FPU_PREP_PUSH();
+ FPU_FBLD(addr,TOP);
+ break;
+ case 0x05: /* FILD Bit64s */
+ FPU_PREP_PUSH();
+ FPU_FLD_I64(addr,TOP);
+ break;
+ case 0x06: /* FBSTP packed BCD */
+ FPU_FBST(addr);
+ FPU_FPOP();
+ break;
+ case 0x07: /* FISTP Bit64s */
+ FPU_FST_I64(addr);
+ FPU_FPOP();
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+}
+
+void FPU_ESC7_Normal(Bitu rm) {
+ Bitu group=(rm >> 3) & 7;
+ Bitu sub=(rm & 7);
+ switch (group){
+ case 0x00: /* FFREEP STi*/
+ fpu.tags[STV(sub)]=TAG_Empty;
+ FPU_FPOP();
+ break;
+ case 0x01: /* FXCH STi*/
+ FPU_FXCH(TOP,STV(sub));
+ break;
+ case 0x02: /* FSTP STi*/
+ case 0x03: /* FSTP STi*/
+ FPU_FST(TOP,STV(sub));
+ FPU_FPOP();
+ break;
+ case 0x04:
+ switch(sub){
+ case 0x00: /* FNSTSW AX*/
+ FPU_SET_TOP(TOP);
+ reg_ax = fpu.sw;
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+ break;
+ default:
+ LOG(LOG_FPU,LOG_WARN)("ESC 7:Unhandled group %d subfunction %d",group,sub);
+ break;
+ }
+}
+
+
+void FPU_Init(Section*) {
+ FPU_FINIT();
+}
+
+#endif
diff --git a/src/fpu/fpu_instructions.h b/src/fpu/fpu_instructions.h
new file mode 100644
index 000000000..9dc97c760
--- /dev/null
+++ b/src/fpu/fpu_instructions.h
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_FPU_H
+#include "fpu.h"
+#endif
+
+
+static void FPU_FINIT(void) {
+ FPU_SetCW(0x37F);
+ fpu.sw = 0;
+ TOP=FPU_GET_TOP();
+ fpu.tags[0] = TAG_Empty;
+ fpu.tags[1] = TAG_Empty;
+ fpu.tags[2] = TAG_Empty;
+ fpu.tags[3] = TAG_Empty;
+ fpu.tags[4] = TAG_Empty;
+ fpu.tags[5] = TAG_Empty;
+ fpu.tags[6] = TAG_Empty;
+ fpu.tags[7] = TAG_Empty;
+ fpu.tags[8] = TAG_Valid; // is only used by us
+}
+
+static void FPU_FCLEX(void){
+ fpu.sw &= 0x7f00; //should clear exceptions
+}
+
+static void FPU_FNOP(void){
+ return;
+}
+
+static void FPU_PREP_PUSH(void){
+ TOP = (TOP - 1) &7;
+#if DB_FPU_STACK_CHECK_PUSH > DB_FPU_STACK_CHECK_NONE
+ if (GCC_UNLIKELY(fpu.tags[TOP] != TAG_Empty)) {
+#if DB_FPU_STACK_CHECK_PUSH == DB_FPU_STACK_CHECK_EXIT
+ E_Exit("FPU stack overflow");
+#else
+ if (fpu.cw&1) { // Masked ?
+ fpu.sw &= 0x1; //Invalid Operation
+ fpu.sw &= 0x40; //Stack Fault
+ FPU_SET_C1(1); //Register is used.
+ //No need to set 0x80 as the exception is masked.
+ LOG(LOG_FPU,LOG_ERROR)("Masked stack overflow encountered!");
+ } else {
+ E_Exit("FPU stack overflow"); //Exit as this is bad
+ }
+#endif
+ }
+#endif
+ fpu.tags[TOP] = TAG_Valid;
+}
+
+static void FPU_PUSH(double in){
+ FPU_PREP_PUSH();
+ fpu.regs[TOP].d = in;
+// LOG(LOG_FPU,LOG_ERROR)("Pushed at %d %g to the stack",newtop,in);
+ return;
+}
+
+
+static void FPU_FPOP(void){
+#if DB_FPU_STACK_CHECK_POP > DB_FPU_STACK_CHECK_NONE
+ if (GCC_UNLIKELY(fpu.tags[TOP] != TAG_Empty)) {
+#if DB_FPU_STACK_CHECK_POP == DB_FPU_STACK_CHECK_EXIT
+ E_Exit("FPU stack underflow");
+#else
+ if (fpu.cw&1) { // Masked ?
+ fpu.sw &= 0x1; //Invalid Operation
+ fpu.sw &= 0x40; //Stack Fault
+ FPU_SET_C1(0); //Register is free.
+ //No need to set 0x80 as the exception is masked.
+ LOG(LOG_FPU,LOG_ERROR)("Masked stack underflow encountered!");
+ } else {
+ LOG_MSG("Unmasked Stack underflow!"); //Also log in release mode
+ }
+#endif
+ }
+#endif
+ fpu.tags[TOP]=TAG_Empty;
+ //maybe set zero in it as well
+ TOP = ((TOP+1)&7);
+// LOG(LOG_FPU,LOG_ERROR)("popped from %d %g off the stack",top,fpu.regs[top].d);
+ return;
+}
+
+static double FROUND(double in){
+ switch(fpu.round){
+ case ROUND_Nearest:
+ if (in-floor(in)>0.5) return (floor(in)+1);
+ else if (in-floor(in)<0.5) return (floor(in));
+ else return (((static_cast<Bit64s>(floor(in)))&1)!=0)?(floor(in)+1):(floor(in));
+ break;
+ case ROUND_Down:
+ return (floor(in));
+ break;
+ case ROUND_Up:
+ return (ceil(in));
+ break;
+ case ROUND_Chop:
+ return in; //the cast afterwards will do it right maybe cast here
+ break;
+ default:
+ return in;
+ break;
+ }
+}
+
+#define BIAS80 16383
+#define BIAS64 1023
+
+static Real64 FPU_FLD80(PhysPt addr) {
+ struct {
+ Bit16s begin;
+ FPU_Reg eind;
+ } test;
+ test.eind.l.lower = mem_readd(addr);
+ test.eind.l.upper = mem_readd(addr+4);
+ test.begin = mem_readw(addr+8);
+
+ Bit64s exp64 = (((test.begin&0x7fff) - BIAS80));
+ Bit64s blah = ((exp64 >0)?exp64:-exp64)&0x3ff;
+ Bit64s exp64final = ((exp64 >0)?blah:-blah) +BIAS64;
+
+ Bit64s mant64 = (test.eind.ll >> 11) & LONGTYPE(0xfffffffffffff);
+ Bit64s sign = (test.begin&0x8000)?1:0;
+ FPU_Reg result;
+ result.ll = (sign <<63)|(exp64final << 52)| mant64;
+
+ if(test.eind.l.lower == 0 && test.eind.l.upper == 0x80000000 && (test.begin&0x7fff) == 0x7fff) {
+ //Detect INF and -INF (score 3.11 when drawing a slur.)
+ result.d = sign?-HUGE_VAL:HUGE_VAL;
+ }
+ return result.d;
+
+ //mant64= test.mant80/2***64 * 2 **53
+}
+
+static void FPU_ST80(PhysPt addr,Bitu reg) {
+ struct {
+ Bit16s begin;
+ FPU_Reg eind;
+ } test;
+ Bit64s sign80 = (fpu.regs[reg].ll&LONGTYPE(0x8000000000000000))?1:0;
+ Bit64s exp80 = fpu.regs[reg].ll&LONGTYPE(0x7ff0000000000000);
+ Bit64s exp80final = (exp80>>52);
+ Bit64s mant80 = fpu.regs[reg].ll&LONGTYPE(0x000fffffffffffff);
+ Bit64s mant80final = (mant80 << 11);
+ if(fpu.regs[reg].d != 0){ //Zero is a special case
+ // Elvira wants the 8 and tcalc doesn't
+ mant80final |= LONGTYPE(0x8000000000000000);
+ //Ca-cyber doesn't like this when result is zero.
+ exp80final += (BIAS80 - BIAS64);
+ }
+ test.begin = (static_cast<Bit16s>(sign80)<<15)| static_cast<Bit16s>(exp80final);
+ test.eind.ll = mant80final;
+ mem_writed(addr,test.eind.l.lower);
+ mem_writed(addr+4,test.eind.l.upper);
+ mem_writew(addr+8,test.begin);
+}
+
+
+static void FPU_FLD_F32(PhysPt addr,Bitu store_to) {
+ union {
+ float f;
+ Bit32u l;
+ } blah;
+ blah.l = mem_readd(addr);
+ fpu.regs[store_to].d = static_cast<Real64>(blah.f);
+}
+
+static void FPU_FLD_F64(PhysPt addr,Bitu store_to) {
+ fpu.regs[store_to].l.lower = mem_readd(addr);
+ fpu.regs[store_to].l.upper = mem_readd(addr+4);
+}
+
+static void FPU_FLD_F80(PhysPt addr) {
+ fpu.regs[TOP].d = FPU_FLD80(addr);
+}
+
+static void FPU_FLD_I16(PhysPt addr,Bitu store_to) {
+ Bit16s blah = mem_readw(addr);
+ fpu.regs[store_to].d = static_cast<Real64>(blah);
+}
+
+static void FPU_FLD_I32(PhysPt addr,Bitu store_to) {
+ Bit32s blah = mem_readd(addr);
+ fpu.regs[store_to].d = static_cast<Real64>(blah);
+}
+
+static void FPU_FLD_I64(PhysPt addr,Bitu store_to) {
+ FPU_Reg blah;
+ blah.l.lower = mem_readd(addr);
+ blah.l.upper = mem_readd(addr+4);
+ fpu.regs[store_to].d = static_cast<Real64>(blah.ll);
+}
+
+static void FPU_FBLD(PhysPt addr,Bitu store_to) {
+ Bit64u val = 0;
+ Bitu in = 0;
+ Bit64u base = 1;
+ for(Bitu i = 0;i < 9;i++){
+ in = mem_readb(addr + i);
+ val += ( (in&0xf) * base); //in&0xf shouldn't be higher then 9
+ base *= 10;
+ val += ((( in>>4)&0xf) * base);
+ base *= 10;
+ }
+
+ //last number, only now convert to float in order to get
+ //the best signification
+ Real64 temp = static_cast<Real64>(val);
+ in = mem_readb(addr + 9);
+ temp += ( (in&0xf) * base );
+ if(in&0x80) temp *= -1.0;
+ fpu.regs[store_to].d = temp;
+}
+
+
+static INLINE void FPU_FLD_F32_EA(PhysPt addr) {
+ FPU_FLD_F32(addr,8);
+}
+static INLINE void FPU_FLD_F64_EA(PhysPt addr) {
+ FPU_FLD_F64(addr,8);
+}
+static INLINE void FPU_FLD_I32_EA(PhysPt addr) {
+ FPU_FLD_I32(addr,8);
+}
+static INLINE void FPU_FLD_I16_EA(PhysPt addr) {
+ FPU_FLD_I16(addr,8);
+}
+
+
+static void FPU_FST_F32(PhysPt addr) {
+ union {
+ float f;
+ Bit32u l;
+ } blah;
+ //should depend on rounding method
+ blah.f = static_cast<float>(fpu.regs[TOP].d);
+ mem_writed(addr,blah.l);
+}
+
+static void FPU_FST_F64(PhysPt addr) {
+ mem_writed(addr,fpu.regs[TOP].l.lower);
+ mem_writed(addr+4,fpu.regs[TOP].l.upper);
+}
+
+static void FPU_FST_F80(PhysPt addr) {
+ FPU_ST80(addr,TOP);
+}
+
+static void FPU_FST_I16(PhysPt addr) {
+ mem_writew(addr,static_cast<Bit16s>(FROUND(fpu.regs[TOP].d)));
+}
+
+static void FPU_FST_I32(PhysPt addr) {
+ mem_writed(addr,static_cast<Bit32s>(FROUND(fpu.regs[TOP].d)));
+}
+
+static void FPU_FST_I64(PhysPt addr) {
+ FPU_Reg blah;
+ blah.ll = static_cast<Bit64s>(FROUND(fpu.regs[TOP].d));
+ mem_writed(addr,blah.l.lower);
+ mem_writed(addr+4,blah.l.upper);
+}
+
+static void FPU_FBST(PhysPt addr) {
+ FPU_Reg val = fpu.regs[TOP];
+ bool sign = false;
+ if(fpu.regs[TOP].ll & LONGTYPE(0x8000000000000000)) { //sign
+ sign=true;
+ val.d=-val.d;
+ }
+ //numbers from back to front
+ Real64 temp=val.d;
+ Bitu p;
+ for(Bitu i=0;i<9;i++){
+ val.d=temp;
+ temp = static_cast<Real64>(static_cast<Bit64s>(floor(val.d/10.0)));
+ p = static_cast<Bitu>(val.d - 10.0*temp);
+ val.d=temp;
+ temp = static_cast<Real64>(static_cast<Bit64s>(floor(val.d/10.0)));
+ p |= (static_cast<Bitu>(val.d - 10.0*temp)<<4);
+
+ mem_writeb(addr+i,p);
+ }
+ val.d=temp;
+ temp = static_cast<Real64>(static_cast<Bit64s>(floor(val.d/10.0)));
+ p = static_cast<Bitu>(val.d - 10.0*temp);
+ if(sign)
+ p|=0x80;
+ mem_writeb(addr+9,p);
+}
+
+static void FPU_FADD(Bitu op1, Bitu op2){
+ fpu.regs[op1].d+=fpu.regs[op2].d;
+ //flags and such :)
+ return;
+}
+
+static void FPU_FSIN(void){
+ fpu.regs[TOP].d = sin(fpu.regs[TOP].d);
+ FPU_SET_C2(0);
+ //flags and such :)
+ return;
+}
+
+static void FPU_FSINCOS(void){
+ Real64 temp = fpu.regs[TOP].d;
+ fpu.regs[TOP].d = sin(temp);
+ FPU_PUSH(cos(temp));
+ FPU_SET_C2(0);
+ //flags and such :)
+ return;
+}
+
+static void FPU_FCOS(void){
+ fpu.regs[TOP].d = cos(fpu.regs[TOP].d);
+ FPU_SET_C2(0);
+ //flags and such :)
+ return;
+}
+
+static void FPU_FSQRT(void){
+ fpu.regs[TOP].d = sqrt(fpu.regs[TOP].d);
+ //flags and such :)
+ return;
+}
+static void FPU_FPATAN(void){
+ fpu.regs[STV(1)].d = atan2(fpu.regs[STV(1)].d,fpu.regs[TOP].d);
+ FPU_FPOP();
+ //flags and such :)
+ return;
+}
+static void FPU_FPTAN(void){
+ fpu.regs[TOP].d = tan(fpu.regs[TOP].d);
+ FPU_PUSH(1.0);
+ FPU_SET_C2(0);
+ //flags and such :)
+ return;
+}
+static void FPU_FDIV(Bitu st, Bitu other){
+ fpu.regs[st].d= fpu.regs[st].d/fpu.regs[other].d;
+ //flags and such :)
+ return;
+}
+
+static void FPU_FDIVR(Bitu st, Bitu other){
+ fpu.regs[st].d= fpu.regs[other].d/fpu.regs[st].d;
+ // flags and such :)
+ return;
+}
+
+static void FPU_FMUL(Bitu st, Bitu other){
+ fpu.regs[st].d*=fpu.regs[other].d;
+ //flags and such :)
+ return;
+}
+
+static void FPU_FSUB(Bitu st, Bitu other){
+ fpu.regs[st].d = fpu.regs[st].d - fpu.regs[other].d;
+ //flags and such :)
+ return;
+}
+
+static void FPU_FSUBR(Bitu st, Bitu other){
+ fpu.regs[st].d= fpu.regs[other].d - fpu.regs[st].d;
+ //flags and such :)
+ return;
+}
+
+static void FPU_FXCH(Bitu st, Bitu other){
+ FPU_Tag tag = fpu.tags[other];
+ FPU_Reg reg = fpu.regs[other];
+ fpu.tags[other] = fpu.tags[st];
+ fpu.regs[other] = fpu.regs[st];
+ fpu.tags[st] = tag;
+ fpu.regs[st] = reg;
+}
+
+static void FPU_FST(Bitu st, Bitu other){
+ fpu.tags[other] = fpu.tags[st];
+ fpu.regs[other] = fpu.regs[st];
+}
+
+
+static void FPU_FCOM(Bitu st, Bitu other){
+ if(((fpu.tags[st] != TAG_Valid) && (fpu.tags[st] != TAG_Zero)) ||
+ ((fpu.tags[other] != TAG_Valid) && (fpu.tags[other] != TAG_Zero))){
+ FPU_SET_C3(1);FPU_SET_C2(1);FPU_SET_C0(1);return;
+ }
+ if(fpu.regs[st].d == fpu.regs[other].d){
+ FPU_SET_C3(1);FPU_SET_C2(0);FPU_SET_C0(0);return;
+ }
+ if(fpu.regs[st].d < fpu.regs[other].d){
+ FPU_SET_C3(0);FPU_SET_C2(0);FPU_SET_C0(1);return;
+ }
+ // st > other
+ FPU_SET_C3(0);FPU_SET_C2(0);FPU_SET_C0(0);return;
+}
+
+static void FPU_FUCOM(Bitu st, Bitu other){
+ //does atm the same as fcom
+ FPU_FCOM(st,other);
+}
+
+static void FPU_FRNDINT(void){
+ Bit64s temp= static_cast<Bit64s>(FROUND(fpu.regs[TOP].d));
+ fpu.regs[TOP].d=static_cast<double>(temp);
+}
+
+static void FPU_FPREM(void){
+ Real64 valtop = fpu.regs[TOP].d;
+ Real64 valdiv = fpu.regs[STV(1)].d;
+ Bit64s ressaved = static_cast<Bit64s>( (valtop/valdiv) );
+// Some backups
+// Real64 res=valtop - ressaved*valdiv;
+// res= fmod(valtop,valdiv);
+ fpu.regs[TOP].d = valtop - ressaved*valdiv;
+ FPU_SET_C0(static_cast<Bitu>(ressaved&4));
+ FPU_SET_C3(static_cast<Bitu>(ressaved&2));
+ FPU_SET_C1(static_cast<Bitu>(ressaved&1));
+ FPU_SET_C2(0);
+}
+
+static void FPU_FPREM1(void){
+ Real64 valtop = fpu.regs[TOP].d;
+ Real64 valdiv = fpu.regs[STV(1)].d;
+ double quot = valtop/valdiv;
+ double quotf = floor(quot);
+ Bit64s ressaved;
+ if (quot-quotf>0.5) ressaved = static_cast<Bit64s>(quotf+1);
+ else if (quot-quotf<0.5) ressaved = static_cast<Bit64s>(quotf);
+ else ressaved = static_cast<Bit64s>((((static_cast<Bit64s>(quotf))&1)!=0)?(quotf+1):(quotf));
+ fpu.regs[TOP].d = valtop - ressaved*valdiv;
+ FPU_SET_C0(static_cast<Bitu>(ressaved&4));
+ FPU_SET_C3(static_cast<Bitu>(ressaved&2));
+ FPU_SET_C1(static_cast<Bitu>(ressaved&1));
+ FPU_SET_C2(0);
+}
+
+static void FPU_FXAM(void){
+ if(fpu.regs[TOP].ll & LONGTYPE(0x8000000000000000)) //sign
+ {
+ FPU_SET_C1(1);
+ }
+ else
+ {
+ FPU_SET_C1(0);
+ }
+ if(fpu.tags[TOP] == TAG_Empty)
+ {
+ FPU_SET_C3(1);FPU_SET_C2(0);FPU_SET_C0(1);
+ return;
+ }
+ if(fpu.regs[TOP].d == 0.0) //zero or normalized number.
+ {
+ FPU_SET_C3(1);FPU_SET_C2(0);FPU_SET_C0(0);
+ }
+ else
+ {
+ FPU_SET_C3(0);FPU_SET_C2(1);FPU_SET_C0(0);
+ }
+}
+
+
+static void FPU_F2XM1(void){
+ fpu.regs[TOP].d = pow(2.0,fpu.regs[TOP].d) - 1;
+ return;
+}
+
+static void FPU_FYL2X(void){
+ fpu.regs[STV(1)].d*=log(fpu.regs[TOP].d)/log(static_cast<Real64>(2.0));
+ FPU_FPOP();
+ return;
+}
+
+static void FPU_FYL2XP1(void){
+ fpu.regs[STV(1)].d*=log(fpu.regs[TOP].d+1.0)/log(static_cast<Real64>(2.0));
+ FPU_FPOP();
+ return;
+}
+
+static void FPU_FSCALE(void){
+ fpu.regs[TOP].d *= pow(2.0,static_cast<Real64>(static_cast<Bit64s>(fpu.regs[STV(1)].d)));
+ return; //2^x where x is chopped.
+}
+
+static void FPU_FSTENV(PhysPt addr){
+ FPU_SET_TOP(TOP);
+ if(!cpu.code.big) {
+ mem_writew(addr+0,static_cast<Bit16u>(fpu.cw));
+ mem_writew(addr+2,static_cast<Bit16u>(fpu.sw));
+ mem_writew(addr+4,static_cast<Bit16u>(FPU_GetTag()));
+ } else {
+ mem_writed(addr+0,static_cast<Bit32u>(fpu.cw));
+ mem_writed(addr+4,static_cast<Bit32u>(fpu.sw));
+ mem_writed(addr+8,static_cast<Bit32u>(FPU_GetTag()));
+ }
+}
+
+static void FPU_FLDENV(PhysPt addr){
+ Bit16u tag;
+ Bit32u tagbig;
+ Bitu cw;
+ if(!cpu.code.big) {
+ cw = mem_readw(addr+0);
+ fpu.sw = mem_readw(addr+2);
+ tag = mem_readw(addr+4);
+ } else {
+ cw = mem_readd(addr+0);
+ fpu.sw = (Bit16u)mem_readd(addr+4);
+ tagbig = mem_readd(addr+8);
+ tag = static_cast<Bit16u>(tagbig);
+ }
+ FPU_SetTag(tag);
+ FPU_SetCW(cw);
+ TOP = FPU_GET_TOP();
+}
+
+static void FPU_FSAVE(PhysPt addr){
+ FPU_FSTENV(addr);
+ Bitu start = (cpu.code.big?28:14);
+ for(Bitu i = 0;i < 8;i++){
+ FPU_ST80(addr+start,STV(i));
+ start += 10;
+ }
+ FPU_FINIT();
+}
+
+static void FPU_FRSTOR(PhysPt addr){
+ FPU_FLDENV(addr);
+ Bitu start = (cpu.code.big?28:14);
+ for(Bitu i = 0;i < 8;i++){
+ fpu.regs[STV(i)].d = FPU_FLD80(addr+start);
+ start += 10;
+ }
+}
+
+static void FPU_FXTRACT(void) {
+ // function stores real bias in st and
+ // pushes the significant number onto the stack
+ // if double ever uses a different base please correct this function
+
+ FPU_Reg test = fpu.regs[TOP];
+ Bit64s exp80 = test.ll&LONGTYPE(0x7ff0000000000000);
+ Bit64s exp80final = (exp80>>52) - BIAS64;
+ Real64 mant = test.d / (pow(2.0,static_cast<Real64>(exp80final)));
+ fpu.regs[TOP].d = static_cast<Real64>(exp80final);
+ FPU_PUSH(mant);
+}
+
+static void FPU_FCHS(void){
+ fpu.regs[TOP].d = -1.0*(fpu.regs[TOP].d);
+}
+
+static void FPU_FABS(void){
+ fpu.regs[TOP].d = fabs(fpu.regs[TOP].d);
+}
+
+static void FPU_FTST(void){
+ fpu.regs[8].d = 0.0;
+ FPU_FCOM(TOP,8);
+}
+
+static void FPU_FLD1(void){
+ FPU_PREP_PUSH();
+ fpu.regs[TOP].d = 1.0;
+}
+
+static void FPU_FLDL2T(void){
+ FPU_PREP_PUSH();
+ fpu.regs[TOP].d = L2T;
+}
+
+static void FPU_FLDL2E(void){
+ FPU_PREP_PUSH();
+ fpu.regs[TOP].d = L2E;
+}
+
+static void FPU_FLDPI(void){
+ FPU_PREP_PUSH();
+ fpu.regs[TOP].d = PI;
+}
+
+static void FPU_FLDLG2(void){
+ FPU_PREP_PUSH();
+ fpu.regs[TOP].d = LG2;
+}
+
+static void FPU_FLDLN2(void){
+ FPU_PREP_PUSH();
+ fpu.regs[TOP].d = LN2;
+}
+
+static void FPU_FLDZ(void){
+ FPU_PREP_PUSH();
+ fpu.regs[TOP].d = 0.0;
+ fpu.tags[TOP] = TAG_Zero;
+}
+
+
+static INLINE void FPU_FADD_EA(Bitu op1){
+ FPU_FADD(op1,8);
+}
+static INLINE void FPU_FMUL_EA(Bitu op1){
+ FPU_FMUL(op1,8);
+}
+static INLINE void FPU_FSUB_EA(Bitu op1){
+ FPU_FSUB(op1,8);
+}
+static INLINE void FPU_FSUBR_EA(Bitu op1){
+ FPU_FSUBR(op1,8);
+}
+static INLINE void FPU_FDIV_EA(Bitu op1){
+ FPU_FDIV(op1,8);
+}
+static INLINE void FPU_FDIVR_EA(Bitu op1){
+ FPU_FDIVR(op1,8);
+}
+static INLINE void FPU_FCOM_EA(Bitu op1){
+ FPU_FCOM(op1,8);
+}
+
diff --git a/src/fpu/fpu_instructions_x86.h b/src/fpu/fpu_instructions_x86.h
new file mode 100644
index 000000000..31e51fc47
--- /dev/null
+++ b/src/fpu/fpu_instructions_x86.h
@@ -0,0 +1,1359 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_FPU_H
+#include "fpu.h"
+#endif
+
+// #define WEAK_EXCEPTIONS
+
+
+#if defined (_MSC_VER)
+
+#ifdef WEAK_EXCEPTIONS
+#define clx
+#else
+#define clx fclex
+#endif
+
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_LOAD(op,szI,szA) \
+ __asm { \
+ __asm mov ebx, store_to \
+ __asm shl ebx, 4 \
+ __asm op szI PTR fpu.p_regs[128].m1 \
+ __asm fstp TBYTE PTR fpu.p_regs[ebx].m1 \
+ }
+#else
+#define FPUD_LOAD(op,szI,szA) \
+ Bit16u new_sw; \
+ __asm { \
+ __asm mov eax, 8 \
+ __asm shl eax, 4 \
+ __asm mov ebx, store_to \
+ __asm shl ebx, 4 \
+ __asm fclex \
+ __asm op szI PTR fpu.p_regs[eax].m1 \
+ __asm fnstsw new_sw \
+ __asm fstp TBYTE PTR fpu.p_regs[ebx].m1 \
+ } \
+ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff);
+#endif
+
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_LOAD_EA(op,szI,szA) \
+ __asm { \
+ __asm op szI PTR fpu.p_regs[128].m1 \
+ }
+#else
+#define FPUD_LOAD_EA(op,szI,szA) \
+ Bit16u new_sw; \
+ __asm { \
+ __asm mov eax, 8 \
+ __asm shl eax, 4 \
+ __asm fclex \
+ __asm op szI PTR fpu.p_regs[eax].m1 \
+ __asm fnstsw new_sw \
+ } \
+ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff);
+#endif
+
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_STORE(op,szI,szA) \
+ Bit16u save_cw; \
+ __asm { \
+ __asm fnstcw save_cw \
+ __asm mov eax, TOP \
+ __asm fldcw fpu.cw_mask_all \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm op szI PTR fpu.p_regs[128].m1 \
+ __asm fldcw save_cw \
+ }
+#else
+#define FPUD_STORE(op,szI,szA) \
+ Bit16u new_sw,save_cw; \
+ __asm { \
+ __asm fnstcw save_cw \
+ __asm fldcw fpu.cw_mask_all \
+ __asm mov eax, TOP \
+ __asm shl eax, 4 \
+ __asm mov ebx, 8 \
+ __asm shl ebx, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm clx \
+ __asm op szI PTR fpu.p_regs[ebx].m1 \
+ __asm fnstsw new_sw \
+ __asm fldcw save_cw \
+ } \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+#endif
+
+// handles fsin,fcos,f2xm1,fchs,fabs
+#define FPUD_TRIG(op) \
+ Bit16u new_sw; \
+ __asm { \
+ __asm mov eax, TOP \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm clx \
+ __asm op \
+ __asm fnstsw new_sw \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ } \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+
+// handles fsincos
+#define FPUD_SINCOS() \
+ Bit16u new_sw; \
+ __asm { \
+ __asm mov eax, TOP \
+ __asm mov ebx, eax \
+ __asm dec ebx \
+ __asm and ebx, 7 \
+ __asm shl eax, 4 \
+ __asm shl ebx, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm clx \
+ __asm fsincos \
+ __asm fnstsw new_sw \
+ __asm mov cx, new_sw \
+ __asm and ch, 0x04 \
+ __asm jnz argument_too_large1 \
+ __asm fstp TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm jmp end_sincos \
+ __asm argument_too_large1: \
+ __asm fstp st(0) \
+ __asm end_sincos: \
+ } \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); \
+ if ((new_sw&0x0400)==0) FPU_PREP_PUSH();
+
+// handles fptan
+#define FPUD_PTAN() \
+ Bit16u new_sw; \
+ __asm { \
+ __asm mov eax, TOP \
+ __asm mov ebx, eax \
+ __asm dec ebx \
+ __asm and ebx, 7 \
+ __asm shl eax, 4 \
+ __asm shl ebx, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm clx \
+ __asm fptan \
+ __asm fnstsw new_sw \
+ __asm mov cx, new_sw \
+ __asm and ch, 0x04 \
+ __asm jnz argument_too_large2 \
+ __asm fstp TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm jmp end_ptan \
+ __asm argument_too_large2: \
+ __asm fstp st(0) \
+ __asm end_ptan: \
+ } \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); \
+ if ((new_sw&0x0400)==0) FPU_PREP_PUSH();
+
+// handles fxtract
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_XTRACT \
+ __asm { \
+ __asm mov eax, TOP \
+ __asm mov ebx, eax \
+ __asm dec ebx \
+ __asm and ebx, 7 \
+ __asm shl eax, 4 \
+ __asm shl ebx, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fxtract \
+ __asm fstp TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ } \
+ FPU_PREP_PUSH();
+#else
+#define FPUD_XTRACT \
+ Bit16u new_sw; \
+ __asm { \
+ __asm mov eax, TOP \
+ __asm mov ebx, eax \
+ __asm dec ebx \
+ __asm and ebx, 7 \
+ __asm shl eax, 4 \
+ __asm shl ebx, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fclex \
+ __asm fxtract \
+ __asm fnstsw new_sw \
+ __asm fstp TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ } \
+ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); \
+ FPU_PREP_PUSH();
+#endif
+
+// handles fadd,fmul,fsub,fsubr
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_ARITH1(op) \
+ Bit16u save_cw; \
+ __asm { \
+ __asm fnstcw save_cw \
+ __asm mov eax, op1 \
+ __asm shl eax, 4 \
+ __asm fldcw fpu.cw_mask_all \
+ __asm mov ebx, op2 \
+ __asm shl ebx, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fld TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm op st(1), st(0) \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fldcw save_cw \
+ }
+#else
+#define FPUD_ARITH1(op) \
+ Bit16u new_sw,save_cw; \
+ __asm { \
+ __asm fnstcw save_cw \
+ __asm fldcw fpu.cw_mask_all \
+ __asm mov eax, op1 \
+ __asm shl eax, 4 \
+ __asm mov ebx, op2 \
+ __asm shl ebx, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fld TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm clx \
+ __asm op st(1), st(0) \
+ __asm fnstsw new_sw \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fldcw save_cw \
+ } \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+#endif
+
+// handles fadd,fmul,fsub,fsubr
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_ARITH1_EA(op) \
+ Bit16u save_cw; \
+ __asm { \
+ __asm fnstcw save_cw \
+ __asm mov eax, op1 \
+ __asm fldcw fpu.cw_mask_all \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fxch \
+ __asm op st(1), st(0) \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fldcw save_cw \
+ }
+#else
+#define FPUD_ARITH1_EA(op) \
+ Bit16u new_sw,save_cw; \
+ __asm { \
+ __asm fnstcw save_cw \
+ __asm fldcw fpu.cw_mask_all \
+ __asm mov eax, op1 \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fxch \
+ __asm clx \
+ __asm op st(1), st(0) \
+ __asm fnstsw new_sw \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fldcw save_cw \
+ } \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+#endif
+
+// handles fsqrt,frndint
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_ARITH2(op) \
+ Bit16u save_cw; \
+ __asm { \
+ __asm fnstcw save_cw \
+ __asm mov eax, TOP \
+ __asm fldcw fpu.cw_mask_all \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm op \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fldcw save_cw \
+ }
+#else
+#define FPUD_ARITH2(op) \
+ Bit16u new_sw,save_cw; \
+ __asm { \
+ __asm fnstcw save_cw \
+ __asm fldcw fpu.cw_mask_all \
+ __asm mov eax, TOP \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm clx \
+ __asm op \
+ __asm fnstsw new_sw \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fldcw save_cw \
+ } \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+#endif
+
+// handles fdiv,fdivr
+// (This is identical to FPUD_ARITH1 but without a WEAK_EXCEPTIONS variant)
+#define FPUD_ARITH3(op) \
+ Bit16u new_sw,save_cw; \
+ __asm { \
+ __asm fnstcw save_cw \
+ __asm fldcw fpu.cw_mask_all \
+ __asm mov eax, op1 \
+ __asm shl eax, 4 \
+ __asm mov ebx, op2 \
+ __asm shl ebx, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fld TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm fclex \
+ __asm op st(1), st(0) \
+ __asm fnstsw new_sw \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fldcw save_cw \
+ } \
+ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff);
+
+// handles fdiv,fdivr
+// (This is identical to FPUD_ARITH1_EA but without a WEAK_EXCEPTIONS variant)
+#define FPUD_ARITH3_EA(op) \
+ Bit16u new_sw,save_cw; \
+ __asm { \
+ __asm fnstcw save_cw \
+ __asm mov eax, op1 \
+ __asm fldcw fpu.cw_mask_all \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fxch \
+ __asm fclex \
+ __asm op st(1), st(0) \
+ __asm fnstsw new_sw \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fldcw save_cw \
+ } \
+ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff);
+
+// handles fprem,fprem1,fscale
+#define FPUD_REMAINDER(op) \
+ Bit16u new_sw; \
+ __asm { \
+ __asm mov eax, TOP \
+ __asm mov ebx, eax \
+ __asm inc ebx \
+ __asm and ebx, 7 \
+ __asm shl ebx, 4 \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fclex \
+ __asm op \
+ __asm fnstsw new_sw \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fstp st(0) \
+ } \
+ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff);
+
+// handles fcom,fucom
+#define FPUD_COMPARE(op) \
+ Bit16u new_sw; \
+ __asm { \
+ __asm mov ebx, op2 \
+ __asm mov eax, op1 \
+ __asm shl ebx, 4 \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm clx \
+ __asm op \
+ __asm fnstsw new_sw \
+ } \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+
+#define FPUD_COMPARE_EA(op) \
+ Bit16u new_sw; \
+ __asm { \
+ __asm mov eax, op1 \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm clx \
+ __asm op \
+ __asm fnstsw new_sw \
+ } \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+
+// handles fxam,ftst
+#define FPUD_EXAMINE(op) \
+ Bit16u new_sw; \
+ __asm { \
+ __asm mov eax, TOP \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm clx \
+ __asm op \
+ __asm fnstsw new_sw \
+ __asm fstp st(0) \
+ } \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+
+// handles fpatan,fyl2xp1
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_WITH_POP(op) \
+ __asm { \
+ __asm mov eax, TOP \
+ __asm mov ebx, eax \
+ __asm inc ebx \
+ __asm and ebx, 7 \
+ __asm shl ebx, 4 \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm op \
+ __asm fstp TBYTE PTR fpu.p_regs[ebx].m1 \
+ } \
+ FPU_FPOP();
+#else
+#define FPUD_WITH_POP(op) \
+ Bit16u new_sw; \
+ __asm { \
+ __asm mov eax, TOP \
+ __asm mov ebx, eax \
+ __asm inc ebx \
+ __asm and ebx, 7 \
+ __asm shl ebx, 4 \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fclex \
+ __asm op \
+ __asm fnstsw new_sw \
+ __asm fstp TBYTE PTR fpu.p_regs[ebx].m1 \
+ } \
+ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); \
+ FPU_FPOP();
+#endif
+
+// handles fyl2x
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_FYL2X(op) \
+ __asm { \
+ __asm mov eax, TOP \
+ __asm mov ebx, eax \
+ __asm inc ebx \
+ __asm and ebx, 7 \
+ __asm shl ebx, 4 \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm op \
+ __asm fstp TBYTE PTR fpu.p_regs[ebx].m1 \
+ } \
+ FPU_FPOP();
+#else
+#define FPUD_FYL2X(op) \
+ Bit16u new_sw; \
+ __asm { \
+ __asm mov eax, TOP \
+ __asm mov ebx, eax \
+ __asm inc ebx \
+ __asm and ebx, 7 \
+ __asm shl ebx, 4 \
+ __asm shl eax, 4 \
+ __asm fld TBYTE PTR fpu.p_regs[ebx].m1 \
+ __asm fld TBYTE PTR fpu.p_regs[eax].m1 \
+ __asm fclex \
+ __asm op \
+ __asm fnstsw new_sw \
+ __asm fstp TBYTE PTR fpu.p_regs[ebx].m1 \
+ } \
+ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); \
+ FPU_FPOP();
+#endif
+
+// load math constants
+#define FPUD_LOAD_CONST(op) \
+ FPU_PREP_PUSH(); \
+ __asm { \
+ __asm mov eax, TOP \
+ __asm shl eax, 4 \
+ __asm clx \
+ __asm op \
+ __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \
+ } \
+
+#else
+
+// !defined _MSC_VER
+
+#ifdef WEAK_EXCEPTIONS
+#define clx
+#else
+#define clx "fclex"
+#endif
+
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_LOAD(op,szI,szA) \
+ __asm__ volatile ( \
+ #op #szA " %1 \n" \
+ "fstpt %0 " \
+ : "=m" (fpu.p_regs[store_to]) \
+ : "m" (fpu.p_regs[8]) \
+ );
+#else
+#define FPUD_LOAD(op,szI,szA) \
+ Bit16u new_sw; \
+ __asm__ volatile ( \
+ "fclex \n" \
+ #op #szA " %2 \n" \
+ "fnstsw %0 \n" \
+ "fstpt %1 " \
+ : "=&am" (new_sw), "=m" (fpu.p_regs[store_to]) \
+ : "m" (fpu.p_regs[8]) \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+#endif
+
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_LOAD_EA(op,szI,szA) \
+ __asm__ volatile ( \
+ #op #szA " %0 \n" \
+ : \
+ : "m" (fpu.p_regs[8]) \
+ );
+#else
+#define FPUD_LOAD_EA(op,szI,szA) \
+ Bit16u new_sw; \
+ __asm__ volatile ( \
+ "fclex \n" \
+ #op #szA " %1 \n" \
+ "fnstsw %0 \n" \
+ : "=&am" (new_sw) \
+ : "m" (fpu.p_regs[8]) \
+ : \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+#endif
+
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_STORE(op,szI,szA) \
+ Bit16u save_cw; \
+ __asm__ volatile ( \
+ "fnstcw %0 \n" \
+ "fldcw %3 \n" \
+ "fldt %2 \n" \
+ #op #szA " %1 \n" \
+ "fldcw %0 " \
+ : "=m" (save_cw), "=m" (fpu.p_regs[8]) \
+ : "m" (fpu.p_regs[TOP]), "m" (fpu.cw_mask_all) \
+ );
+#else
+#define FPUD_STORE(op,szI,szA) \
+ Bit16u new_sw,save_cw; \
+ __asm__ volatile ( \
+ "fnstcw %1 \n" \
+ "fldcw %4 \n" \
+ "fldt %3 \n" \
+ "fclex \n" \
+ #op #szA " %2 \n" \
+ "fnstsw %0 \n" \
+ "fldcw %1 " \
+ : "=&am" (new_sw), "=m" (save_cw), "=m" (fpu.p_regs[8]) \
+ : "m" (fpu.p_regs[TOP]), "m" (fpu.cw_mask_all) \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+#endif
+
+// handles fsin,fcos,f2xm1,fchs,fabs
+#define FPUD_TRIG(op) \
+ Bit16u new_sw; \
+ __asm__ volatile ( \
+ "fldt %1 \n" \
+ clx" \n" \
+ #op" \n" \
+ "fnstsw %0 \n" \
+ "fstpt %1 " \
+ : "=&am" (new_sw), "+m" (fpu.p_regs[TOP]) \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+
+// handles fsincos
+#define FPUD_SINCOS() \
+ Bit16u new_sw; \
+ __asm__ volatile ( \
+ "fldt %1 \n" \
+ clx" \n" \
+ "fsincos \n" \
+ "fnstsw %0 \n" \
+ "fstpt %2 \n" \
+ "movw %0, %%ax \n" \
+ "sahf \n" \
+ "jp 1f \n" \
+ "fstpt %1 \n" \
+ "1: " \
+ : "=m" (new_sw), "+m" (fpu.p_regs[TOP]), \
+ "=m" (fpu.p_regs[(TOP-1)&7]) \
+ : \
+ : "ax", "cc" \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); \
+ if ((new_sw&0x0400)==0) FPU_PREP_PUSH();
+
+// handles fptan
+#define FPUD_PTAN() \
+ Bit16u new_sw; \
+ __asm__ volatile ( \
+ "fldt %1 \n" \
+ clx" \n" \
+ "fptan \n" \
+ "fnstsw %0 \n" \
+ "fstpt %2 \n" \
+ "movw %0, %%ax \n" \
+ "sahf \n" \
+ "jp 1f \n" \
+ "fstpt %1 \n" \
+ "1: " \
+ : "=m" (new_sw), "+m" (fpu.p_regs[TOP]), \
+ "=m" (fpu.p_regs[(TOP-1)&7]) \
+ : \
+ : "ax", "cc" \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); \
+ if ((new_sw&0x0400)==0) FPU_PREP_PUSH();
+
+// handles fxtract
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_XTRACT \
+ __asm__ volatile ( \
+ "fldt %0 \n" \
+ "fxtract \n" \
+ "fstpt %1 \n" \
+ "fstpt %0 " \
+ : "+m" (fpu.p_regs[TOP]), "=m" (fpu.p_regs[(TOP-1)&7]) \
+ ); \
+ FPU_PREP_PUSH();
+#else
+#define FPUD_XTRACT \
+ Bit16u new_sw; \
+ __asm__ volatile ( \
+ "fldt %1 \n" \
+ "fclex \n" \
+ "fxtract \n" \
+ "fnstsw %0 \n" \
+ "fstpt %2 \n" \
+ "fstpt %1 " \
+ : "=&am" (new_sw), "+m" (fpu.p_regs[TOP]), \
+ "=m" (fpu.p_regs[(TOP-1)&7]) \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); \
+ FPU_PREP_PUSH();
+#endif
+
+// handles fadd,fmul,fsub,fsubr
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_ARITH1(op) \
+ Bit16u save_cw; \
+ __asm__ volatile ( \
+ "fnstcw %0 \n" \
+ "fldcw %3 \n" \
+ "fldt %2 \n" \
+ "fldt %1 \n" \
+ #op" \n" \
+ "fstpt %1 \n" \
+ "fldcw %0 " \
+ : "=m" (save_cw), "+m" (fpu.p_regs[op1]) \
+ : "m" (fpu.p_regs[op2]), "m" (fpu.cw_mask_all) \
+ );
+#else
+#define FPUD_ARITH1(op) \
+ Bit16u new_sw,save_cw; \
+ __asm__ volatile ( \
+ "fnstcw %1 \n" \
+ "fldcw %4 \n" \
+ "fldt %3 \n" \
+ "fldt %2 \n" \
+ "fclex \n" \
+ #op" \n" \
+ "fnstsw %0 \n" \
+ "fstpt %2 \n" \
+ "fldcw %1 " \
+ : "=&am" (new_sw), "=m" (save_cw), "+m" (fpu.p_regs[op1]) \
+ : "m" (fpu.p_regs[op2]), "m" (fpu.cw_mask_all) \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+#endif
+
+// handles fadd,fmul,fsub,fsubr
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_ARITH1_EA(op) \
+ Bit16u save_cw; \
+ __asm__ volatile ( \
+ "fnstcw %0 \n" \
+ "fldcw %2 \n" \
+ "fldt %1 \n" \
+ #op" \n" \
+ "fstpt %1 \n" \
+ "fldcw %0 " \
+ : "=m" (save_cw), "+m" (fpu.p_regs[op1]) \
+ : "m" (fpu.cw_mask_all) \
+ );
+#else
+#define FPUD_ARITH1_EA(op) \
+ Bit16u new_sw,save_cw; \
+ __asm__ volatile ( \
+ "fnstcw %1 \n" \
+ "fldcw %3 \n" \
+ "fldt %2 \n" \
+ "fclex \n" \
+ #op" \n" \
+ "fnstsw %0 \n" \
+ "fstpt %2 \n" \
+ "fldcw %1 " \
+ : "=&am" (new_sw), "=m" (save_cw), "+m" (fpu.p_regs[op1]) \
+ : "m" (fpu.cw_mask_all) \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+#endif
+
+// handles fsqrt,frndint
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_ARITH2(op) \
+ Bit16u save_cw; \
+ __asm__ volatile ( \
+ "fnstcw %0 \n" \
+ "fldcw %2 \n" \
+ "fldt %1 \n" \
+ #op" \n" \
+ "fstpt %1 \n" \
+ "fldcw %0 " \
+ : "=m" (save_cw), "+m" (fpu.p_regs[TOP]) \
+ : "m" (fpu.cw_mask_all) \
+ );
+#else
+#define FPUD_ARITH2(op) \
+ Bit16u new_sw,save_cw; \
+ __asm__ volatile ( \
+ "fnstcw %1 \n" \
+ "fldcw %3 \n" \
+ "fldt %2 \n" \
+ "fclex \n" \
+ #op" \n" \
+ "fnstsw %0 \n" \
+ "fstpt %2 \n" \
+ "fldcw %1 " \
+ : "=&am" (new_sw), "=m" (save_cw), "+m" (fpu.p_regs[TOP]) \
+ : "m" (fpu.cw_mask_all) \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+#endif
+
+// handles fdiv,fdivr
+// (This is identical to FPUD_ARITH1 but without a WEAK_EXCEPTIONS variant)
+#define FPUD_ARITH3(op) \
+ Bit16u new_sw,save_cw; \
+ __asm__ volatile ( \
+ "fnstcw %1 \n" \
+ "fldcw %4 \n" \
+ "fldt %3 \n" \
+ "fldt %2 \n" \
+ "fclex \n" \
+ #op" \n" \
+ "fnstsw %0 \n" \
+ "fstpt %2 \n" \
+ "fldcw %1 " \
+ : "=&am" (new_sw), "=m" (save_cw), "+m" (fpu.p_regs[op1]) \
+ : "m" (fpu.p_regs[op2]), "m" (fpu.cw_mask_all) \
+ ); \
+ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff);
+
+// handles fdiv,fdivr
+// (This is identical to FPUD_ARITH1_EA but without a WEAK_EXCEPTIONS variant)
+#define FPUD_ARITH3_EA(op) \
+ Bit16u new_sw,save_cw; \
+ __asm__ volatile ( \
+ "fnstcw %1 \n" \
+ "fldcw %3 \n" \
+ "fldt %2 \n" \
+ "fclex \n" \
+ #op" \n" \
+ "fnstsw %0 \n" \
+ "fstpt %2 \n" \
+ "fldcw %1 " \
+ : "=&am" (new_sw), "=m" (save_cw), "+m" (fpu.p_regs[op1]) \
+ : "m" (fpu.cw_mask_all) \
+ ); \
+ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff);
+
+// handles fprem,fprem1,fscale
+#define FPUD_REMAINDER(op) \
+ Bit16u new_sw; \
+ __asm__ volatile ( \
+ "fldt %2 \n" \
+ "fldt %1 \n" \
+ "fclex \n" \
+ #op" \n" \
+ "fnstsw %0 \n" \
+ "fstpt %1 \n" \
+ "fstp %%st(0) " \
+ : "=&am" (new_sw), "+m" (fpu.p_regs[TOP]) \
+ : "m" (fpu.p_regs[(TOP+1)&7]) \
+ ); \
+ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff);
+
+// handles fcom,fucom
+#define FPUD_COMPARE(op) \
+ Bit16u new_sw; \
+ __asm__ volatile ( \
+ "fldt %2 \n" \
+ "fldt %1 \n" \
+ clx" \n" \
+ #op" \n" \
+ "fnstsw %0 " \
+ : "=&am" (new_sw) \
+ : "m" (fpu.p_regs[op1]), "m" (fpu.p_regs[op2]) \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+
+// handles fcom,fucom
+#define FPUD_COMPARE_EA(op) \
+ Bit16u new_sw; \
+ __asm__ volatile ( \
+ "fldt %1 \n" \
+ clx" \n" \
+ #op" \n" \
+ "fnstsw %0 " \
+ : "=&am" (new_sw) \
+ : "m" (fpu.p_regs[op1]) \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+
+// handles fxam,ftst
+#define FPUD_EXAMINE(op) \
+ Bit16u new_sw; \
+ __asm__ volatile ( \
+ "fldt %1 \n" \
+ clx" \n" \
+ #op" \n" \
+ "fnstsw %0 \n" \
+ "fstp %%st(0) " \
+ : "=&am" (new_sw) \
+ : "m" (fpu.p_regs[TOP]) \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff);
+
+// handles fpatan,fyl2xp1
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_WITH_POP(op) \
+ __asm__ volatile ( \
+ "fldt %0 \n" \
+ "fldt %1 \n" \
+ #op" \n" \
+ "fstpt %0 \n" \
+ : "+m" (fpu.p_regs[(TOP+1)&7]) \
+ : "m" (fpu.p_regs[TOP]) \
+ ); \
+ FPU_FPOP();
+#else
+#define FPUD_WITH_POP(op) \
+ Bit16u new_sw; \
+ __asm__ volatile ( \
+ "fldt %1 \n" \
+ "fldt %2 \n" \
+ "fclex \n" \
+ #op" \n" \
+ "fnstsw %0 \n" \
+ "fstpt %1 \n" \
+ : "=&am" (new_sw), "+m" (fpu.p_regs[(TOP+1)&7]) \
+ : "m" (fpu.p_regs[TOP]) \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); \
+ FPU_FPOP();
+#endif
+
+// handles fyl2x
+#ifdef WEAK_EXCEPTIONS
+#define FPUD_FYL2X(op) \
+ __asm__ volatile ( \
+ "fldt %0 \n" \
+ "fldt %1 \n" \
+ #op" \n" \
+ "fstpt %0 \n" \
+ : "+m" (fpu.p_regs[(TOP+1)&7]) \
+ : "m" (fpu.p_regs[TOP]) \
+ ); \
+ FPU_FPOP();
+#else
+#define FPUD_FYL2X(op) \
+ Bit16u new_sw; \
+ __asm__ volatile ( \
+ "fldt %1 \n" \
+ "fldt %2 \n" \
+ "fclex \n" \
+ #op" \n" \
+ "fnstsw %0 \n" \
+ "fstpt %1 \n" \
+ : "=&am" (new_sw), "+m" (fpu.p_regs[(TOP+1)&7]) \
+ : "m" (fpu.p_regs[TOP]) \
+ ); \
+ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); \
+ FPU_FPOP();
+#endif
+
+// load math constants
+#define FPUD_LOAD_CONST(op) \
+ FPU_PREP_PUSH(); \
+ __asm__ volatile ( \
+ clx" \n" \
+ #op" \n" \
+ "fstpt %0 \n" \
+ : "=m" (fpu.p_regs[TOP]) \
+ );
+
+#endif
+
+#ifdef WEAK_EXCEPTIONS
+const Bit16u exc_mask=0x7f00;
+#else
+const Bit16u exc_mask=0xffbf;
+#endif
+
+static void FPU_FINIT(void) {
+ FPU_SetCW(0x37F);
+ fpu.sw=0;
+ TOP=FPU_GET_TOP();
+ fpu.tags[0]=TAG_Empty;
+ fpu.tags[1]=TAG_Empty;
+ fpu.tags[2]=TAG_Empty;
+ fpu.tags[3]=TAG_Empty;
+ fpu.tags[4]=TAG_Empty;
+ fpu.tags[5]=TAG_Empty;
+ fpu.tags[6]=TAG_Empty;
+ fpu.tags[7]=TAG_Empty;
+ fpu.tags[8]=TAG_Valid; // is only used by us
+}
+
+static void FPU_FCLEX(void){
+ fpu.sw&=0x7f00; //should clear exceptions
+}
+
+static void FPU_FNOP(void){
+}
+
+static void FPU_PREP_PUSH(void){
+ TOP = (TOP - 1) &7;
+#if DB_FPU_STACK_CHECK_PUSH > DB_FPU_STACK_CHECK_NONE
+ if (GCC_UNLIKELY(fpu.tags[TOP] != TAG_Empty)) {
+#if DB_FPU_STACK_CHECK_PUSH == DB_FPU_STACK_CHECK_EXIT
+ E_Exit("FPU stack overflow");
+#else
+ if (fpu.cw&1) { // Masked ?
+ fpu.sw &= 0x1; //Invalid Operation
+ fpu.sw &= 0x40; //Stack Fault
+ FPU_SET_C1(1); //Register is used.
+ //No need to set 0x80 as the exception is masked.
+ LOG(LOG_FPU,LOG_ERROR)("Masked stack overflow encountered!");
+ } else {
+ E_Exit("FPU stack overflow"); //Exit as this is bad
+ }
+#endif
+ }
+#endif
+ fpu.tags[TOP] = TAG_Valid;
+}
+
+static void FPU_FPOP(void){
+#if DB_FPU_STACK_CHECK_POP > DB_FPU_STACK_CHECK_NONE
+ if (GCC_UNLIKELY(fpu.tags[TOP] == TAG_Empty)) {
+#if DB_FPU_STACK_CHECK_POP == DB_FPU_STACK_CHECK_EXIT
+ E_Exit("FPU stack underflow");
+#else
+ if (fpu.cw&1) { // Masked ?
+ fpu.sw &= 0x1; //Invalid Operation
+ fpu.sw &= 0x40; //Stack Fault
+ FPU_SET_C1(0); //Register is free.
+ //No need to set 0x80 as the exception is masked.
+ LOG(LOG_FPU,LOG_ERROR)("Masked stack underflow encountered!");
+ } else {
+ LOG_MSG("Unmasked Stack underflow!");
+ }
+#endif
+ }
+#endif
+ fpu.tags[TOP] = TAG_Empty;
+ TOP = ((TOP+1)&7);
+}
+
+static void FPU_FLD_F32(PhysPt addr,Bitu store_to) {
+ fpu.p_regs[8].m1 = mem_readd(addr);
+ FPUD_LOAD(fld,DWORD,s)
+}
+
+static void FPU_FLD_F32_EA(PhysPt addr) {
+ fpu.p_regs[8].m1 = mem_readd(addr);
+ FPUD_LOAD_EA(fld,DWORD,s)
+}
+
+static void FPU_FLD_F64(PhysPt addr,Bitu store_to) {
+ fpu.p_regs[8].m1 = mem_readd(addr);
+ fpu.p_regs[8].m2 = mem_readd(addr+4);
+ FPUD_LOAD(fld,QWORD,l)
+}
+
+static void FPU_FLD_F64_EA(PhysPt addr) {
+ fpu.p_regs[8].m1 = mem_readd(addr);
+ fpu.p_regs[8].m2 = mem_readd(addr+4);
+ FPUD_LOAD_EA(fld,QWORD,l)
+}
+
+static void FPU_FLD_F80(PhysPt addr) {
+ fpu.p_regs[TOP].m1 = mem_readd(addr);
+ fpu.p_regs[TOP].m2 = mem_readd(addr+4);
+ fpu.p_regs[TOP].m3 = mem_readw(addr+8);
+ FPU_SET_C1(0);
+}
+
+static void FPU_FLD_I16(PhysPt addr,Bitu store_to) {
+ fpu.p_regs[8].m1 = (Bit32u)mem_readw(addr);
+ FPUD_LOAD(fild,WORD,s)
+}
+
+static void FPU_FLD_I16_EA(PhysPt addr) {
+ fpu.p_regs[8].m1 = (Bit32u)mem_readw(addr);
+ FPUD_LOAD_EA(fild,WORD,s)
+}
+
+static void FPU_FLD_I32(PhysPt addr,Bitu store_to) {
+ fpu.p_regs[8].m1 = mem_readd(addr);
+ FPUD_LOAD(fild,DWORD,l)
+}
+
+static void FPU_FLD_I32_EA(PhysPt addr) {
+ fpu.p_regs[8].m1 = mem_readd(addr);
+ FPUD_LOAD_EA(fild,DWORD,l)
+}
+
+static void FPU_FLD_I64(PhysPt addr,Bitu store_to) {
+ fpu.p_regs[8].m1 = mem_readd(addr);
+ fpu.p_regs[8].m2 = mem_readd(addr+4);
+ FPUD_LOAD(fild,QWORD,q)
+}
+
+static void FPU_FBLD(PhysPt addr,Bitu store_to) {
+ fpu.p_regs[8].m1 = mem_readd(addr);
+ fpu.p_regs[8].m2 = mem_readd(addr+4);
+ fpu.p_regs[8].m3 = mem_readw(addr+8);
+ FPUD_LOAD(fbld,TBYTE,)
+}
+
+static void FPU_FST_F32(PhysPt addr) {
+ FPUD_STORE(fstp,DWORD,s)
+ mem_writed(addr,fpu.p_regs[8].m1);
+}
+
+static void FPU_FST_F64(PhysPt addr) {
+ FPUD_STORE(fstp,QWORD,l)
+ mem_writed(addr,fpu.p_regs[8].m1);
+ mem_writed(addr+4,fpu.p_regs[8].m2);
+}
+
+static void FPU_FST_F80(PhysPt addr) {
+ mem_writed(addr,fpu.p_regs[TOP].m1);
+ mem_writed(addr+4,fpu.p_regs[TOP].m2);
+ mem_writew(addr+8,fpu.p_regs[TOP].m3);
+ FPU_SET_C1(0);
+}
+
+static void FPU_FST_I16(PhysPt addr) {
+ FPUD_STORE(fistp,WORD,s)
+ mem_writew(addr,(Bit16u)fpu.p_regs[8].m1);
+}
+
+static void FPU_FST_I32(PhysPt addr) {
+ FPUD_STORE(fistp,DWORD,l)
+ mem_writed(addr,fpu.p_regs[8].m1);
+}
+
+static void FPU_FST_I64(PhysPt addr) {
+ FPUD_STORE(fistp,QWORD,q)
+ mem_writed(addr,fpu.p_regs[8].m1);
+ mem_writed(addr+4,fpu.p_regs[8].m2);
+}
+
+static void FPU_FBST(PhysPt addr) {
+ FPUD_STORE(fbstp,TBYTE,)
+ mem_writed(addr,fpu.p_regs[8].m1);
+ mem_writed(addr+4,fpu.p_regs[8].m2);
+ mem_writew(addr+8,fpu.p_regs[8].m3);
+}
+
+
+static void FPU_FSIN(void){
+ FPUD_TRIG(fsin)
+}
+
+static void FPU_FSINCOS(void){
+ FPUD_SINCOS()
+}
+
+static void FPU_FCOS(void){
+ FPUD_TRIG(fcos)
+}
+
+static void FPU_FSQRT(void){
+ FPUD_ARITH2(fsqrt)
+}
+
+static void FPU_FPATAN(void){
+ FPUD_WITH_POP(fpatan)
+}
+
+static void FPU_FPTAN(void){
+ FPUD_PTAN()
+}
+
+
+static void FPU_FADD(Bitu op1, Bitu op2){
+ FPUD_ARITH1(faddp)
+}
+
+static void FPU_FADD_EA(Bitu op1){
+ FPUD_ARITH1_EA(faddp)
+}
+
+static void FPU_FDIV(Bitu op1, Bitu op2){
+ FPUD_ARITH3(fdivp)
+}
+
+static void FPU_FDIV_EA(Bitu op1){
+ FPUD_ARITH3_EA(fdivp)
+}
+
+static void FPU_FDIVR(Bitu op1, Bitu op2){
+ FPUD_ARITH3(fdivrp)
+}
+
+static void FPU_FDIVR_EA(Bitu op1){
+ FPUD_ARITH3_EA(fdivrp)
+}
+
+static void FPU_FMUL(Bitu op1, Bitu op2){
+ FPUD_ARITH1(fmulp)
+}
+
+static void FPU_FMUL_EA(Bitu op1){
+ FPUD_ARITH1_EA(fmulp)
+}
+
+static void FPU_FSUB(Bitu op1, Bitu op2){
+ FPUD_ARITH1(fsubp)
+}
+
+static void FPU_FSUB_EA(Bitu op1){
+ FPUD_ARITH1_EA(fsubp)
+}
+
+static void FPU_FSUBR(Bitu op1, Bitu op2){
+ FPUD_ARITH1(fsubrp)
+}
+
+static void FPU_FSUBR_EA(Bitu op1){
+ FPUD_ARITH1_EA(fsubrp)
+}
+
+static void FPU_FXCH(Bitu stv, Bitu other){
+ FPU_Tag tag = fpu.tags[other];
+ fpu.tags[other] = fpu.tags[stv];
+ fpu.tags[stv] = tag;
+
+ Bit32u m1s = fpu.p_regs[other].m1;
+ Bit32u m2s = fpu.p_regs[other].m2;
+ Bit16u m3s = fpu.p_regs[other].m3;
+ fpu.p_regs[other].m1 = fpu.p_regs[stv].m1;
+ fpu.p_regs[other].m2 = fpu.p_regs[stv].m2;
+ fpu.p_regs[other].m3 = fpu.p_regs[stv].m3;
+ fpu.p_regs[stv].m1 = m1s;
+ fpu.p_regs[stv].m2 = m2s;
+ fpu.p_regs[stv].m3 = m3s;
+
+ FPU_SET_C1(0);
+}
+
+static void FPU_FST(Bitu stv, Bitu other){
+ fpu.tags[other] = fpu.tags[stv];
+
+ fpu.p_regs[other].m1 = fpu.p_regs[stv].m1;
+ fpu.p_regs[other].m2 = fpu.p_regs[stv].m2;
+ fpu.p_regs[other].m3 = fpu.p_regs[stv].m3;
+
+ FPU_SET_C1(0);
+}
+
+
+static void FPU_FCOM(Bitu op1, Bitu op2){
+ FPUD_COMPARE(fcompp)
+}
+
+static void FPU_FCOM_EA(Bitu op1){
+ FPUD_COMPARE_EA(fcompp)
+}
+
+static void FPU_FUCOM(Bitu op1, Bitu op2){
+ FPUD_COMPARE(fucompp)
+}
+
+static void FPU_FRNDINT(void){
+ FPUD_ARITH2(frndint)
+}
+
+static void FPU_FPREM(void){
+ FPUD_REMAINDER(fprem)
+}
+
+static void FPU_FPREM1(void){
+ FPUD_REMAINDER(fprem1)
+}
+
+static void FPU_FXAM(void){
+ FPUD_EXAMINE(fxam)
+ // handle empty registers (C1 set to sign in any way!)
+ if(fpu.tags[TOP] == TAG_Empty) {
+ FPU_SET_C3(1);FPU_SET_C2(0);FPU_SET_C0(1);
+ return;
+ }
+}
+
+static void FPU_F2XM1(void){
+ FPUD_TRIG(f2xm1)
+}
+
+static void FPU_FYL2X(void){
+ FPUD_FYL2X(fyl2x)
+}
+
+static void FPU_FYL2XP1(void){
+ FPUD_WITH_POP(fyl2xp1)
+}
+
+static void FPU_FSCALE(void){
+ FPUD_REMAINDER(fscale)
+}
+
+
+static void FPU_FSTENV(PhysPt addr){
+ FPU_SET_TOP(TOP);
+ if(!cpu.code.big) {
+ mem_writew(addr+0,static_cast<Bit16u>(fpu.cw));
+ mem_writew(addr+2,static_cast<Bit16u>(fpu.sw));
+ mem_writew(addr+4,static_cast<Bit16u>(FPU_GetTag()));
+ } else {
+ mem_writed(addr+0,static_cast<Bit32u>(fpu.cw));
+ mem_writed(addr+4,static_cast<Bit32u>(fpu.sw));
+ mem_writed(addr+8,static_cast<Bit32u>(FPU_GetTag()));
+ }
+}
+
+static void FPU_FLDENV(PhysPt addr){
+ Bit16u tag;
+ Bit32u tagbig;
+ Bitu cw;
+ if(!cpu.code.big) {
+ cw = mem_readw(addr+0);
+ fpu.sw = mem_readw(addr+2);
+ tag = mem_readw(addr+4);
+ } else {
+ cw = mem_readd(addr+0);
+ fpu.sw = (Bit16u)mem_readd(addr+4);
+ tagbig = mem_readd(addr+8);
+ tag = static_cast<Bit16u>(tagbig);
+ }
+ FPU_SetTag(tag);
+ FPU_SetCW(cw);
+ TOP=FPU_GET_TOP();
+}
+
+static void FPU_FSAVE(PhysPt addr){
+ FPU_FSTENV(addr);
+ Bitu start=(cpu.code.big?28:14);
+ for(Bitu i=0;i<8;i++){
+ mem_writed(addr+start,fpu.p_regs[STV(i)].m1);
+ mem_writed(addr+start+4,fpu.p_regs[STV(i)].m2);
+ mem_writew(addr+start+8,fpu.p_regs[STV(i)].m3);
+ start+=10;
+ }
+ FPU_FINIT();
+}
+
+static void FPU_FRSTOR(PhysPt addr){
+ FPU_FLDENV(addr);
+ Bitu start=(cpu.code.big?28:14);
+ for(Bitu i=0;i<8;i++){
+ fpu.p_regs[STV(i)].m1 = mem_readd(addr+start);
+ fpu.p_regs[STV(i)].m2 = mem_readd(addr+start+4);
+ fpu.p_regs[STV(i)].m3 = mem_readw(addr+start+8);
+ start+=10;
+ }
+}
+
+
+static void FPU_FXTRACT(void) {
+ FPUD_XTRACT
+}
+
+static void FPU_FCHS(void){
+ FPUD_TRIG(fchs)
+}
+
+static void FPU_FABS(void){
+ FPUD_TRIG(fabs)
+}
+
+static void FPU_FTST(void){
+ FPUD_EXAMINE(ftst)
+}
+
+static void FPU_FLD1(void){
+ FPUD_LOAD_CONST(fld1)
+}
+
+static void FPU_FLDL2T(void){
+ FPUD_LOAD_CONST(fldl2t)
+}
+
+static void FPU_FLDL2E(void){
+ FPUD_LOAD_CONST(fldl2e)
+}
+
+static void FPU_FLDPI(void){
+ FPUD_LOAD_CONST(fldpi)
+}
+
+static void FPU_FLDLG2(void){
+ FPUD_LOAD_CONST(fldlg2)
+}
+
+static void FPU_FLDLN2(void){
+ FPUD_LOAD_CONST(fldln2)
+}
+
+static void FPU_FLDZ(void){
+ FPUD_LOAD_CONST(fldz)
+ fpu.tags[TOP]=TAG_Zero;
+}
diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am
new file mode 100644
index 000000000..3fed5e68a
--- /dev/null
+++ b/src/gui/Makefile.am
@@ -0,0 +1,11 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libgui.a
+libgui_a_SOURCES = sdlmain.cpp sdl_mapper.cpp dosbox_logo.h \
+ render.cpp render_scalers.cpp render_scalers.h \
+ render_templates.h render_loops.h render_simple.h \
+ render_templates_sai.h render_templates_hq.h \
+ render_templates_hq2x.h render_templates_hq3x.h \
+ midi.cpp midi_win32.h midi_oss.h midi_coreaudio.h midi_alsa.h \
+ midi_coremidi.h sdl_gui.cpp dosbox_splash.h
+
diff --git a/src/gui/dosbox_logo.h b/src/gui/dosbox_logo.h
new file mode 100644
index 000000000..73925ffae
--- /dev/null
+++ b/src/gui/dosbox_logo.h
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+/* DOSBox icon designed by Ido Beeri */
+
+/* Select only one logo at the time */
+#define LOGO_1 1
+//#define LOGO_2 1
+#ifdef LOGO_1
+ 65, 44, 25,0, 65, 44, 25,0, 93, 58, 30,0, 93, 58, 30,0
+, 92, 57, 30,0, 92, 57, 30,0, 98, 59, 28,0, 98, 59, 28,0
+, 97, 58, 29,0, 97, 58, 29,0, 97, 60, 30,0, 97, 60, 30,0
+, 97, 60, 30,0, 97, 60, 30,0, 96, 59, 29,0, 96, 59, 29,0
+, 91, 55, 26,0, 91, 55, 26,0, 96, 60, 29,0, 96, 60, 29,0
+, 93, 58, 28,0, 93, 58, 28,0, 93, 57, 27,0, 93, 57, 27,0
+, 93, 56, 27,0, 93, 56, 27,0, 85, 52, 25,0, 85, 52, 25,0
+, 89, 53, 24,0, 89, 53, 24,0, 63, 39, 22,0, 63, 39, 22,0
+, 65, 44, 25,0, 65, 44, 25,0, 93, 58, 30,0, 93, 58, 30,0
+, 92, 57, 30,0, 92, 57, 30,0, 98, 59, 28,0, 98, 59, 28,0
+, 97, 58, 29,0, 97, 58, 29,0, 97, 60, 30,0, 97, 60, 30,0
+, 97, 60, 30,0, 97, 60, 30,0, 96, 59, 29,0, 96, 59, 29,0
+, 91, 55, 26,0, 91, 55, 26,0, 96, 60, 29,0, 96, 60, 29,0
+, 93, 58, 28,0, 93, 58, 28,0, 93, 57, 27,0, 93, 57, 27,0
+, 93, 56, 27,0, 93, 56, 27,0, 85, 52, 25,0, 85, 52, 25,0
+, 89, 53, 24,0, 89, 53, 24,0, 63, 39, 22,0, 63, 39, 22,0
+, 81, 50, 26,0, 81, 50, 26,0,145, 89, 46,0,145, 89, 46,0
+,145, 76, 29,0,145, 76, 29,0,134, 72, 26,0,134, 72, 26,0
+,136, 76, 30,0,136, 76, 30,0,148, 85, 36,0,148, 85, 36,0
+,137, 77, 31,0,137, 77, 31,0,134, 72, 27,0,134, 72, 27,0
+,117, 64, 26,0,117, 64, 26,0,141, 80, 32,0,141, 80, 32,0
+,141, 81, 34,0,141, 81, 34,0,123, 70, 27,0,123, 70, 27,0
+,123, 68, 27,0,123, 68, 27,0,130, 72, 27,0,130, 72, 27,0
+,135, 80, 37,0,135, 80, 37,0, 85, 50, 24,0, 85, 50, 24,0
+, 81, 50, 26,0, 81, 50, 26,0,145, 89, 46,0,145, 89, 46,0
+,145, 76, 29,0,145, 76, 29,0,134, 72, 26,0,134, 72, 26,0
+,136, 76, 30,0,136, 76, 30,0,148, 85, 36,0,148, 85, 36,0
+,137, 77, 31,0,137, 77, 31,0,134, 72, 27,0,134, 72, 27,0
+,117, 64, 26,0,117, 64, 26,0,141, 80, 32,0,141, 80, 32,0
+,141, 81, 34,0,141, 81, 34,0,123, 70, 27,0,123, 70, 27,0
+,123, 68, 27,0,123, 68, 27,0,130, 72, 27,0,130, 72, 27,0
+,135, 80, 37,0,135, 80, 37,0, 85, 50, 24,0, 85, 50, 24,0
+, 80, 48, 24,0, 80, 48, 24,0,146, 76, 29,0,146, 76, 29,0
+,255,227, 30,0,255,227, 30,0,255,237, 75,0,255,237, 75,0
+,125, 98, 52,0,125, 98, 52,0,145,120, 69,0,145,120, 69,0
+,133,105, 55,0,133,105, 55,0,255,231, 48,0,255,231, 48,0
+,255,237, 75,0,255,237, 75,0,127,100, 53,0,127,100, 53,0
+,143,117, 67,0,143,117, 67,0,132,105, 55,0,132,105, 55,0
+,255,232, 54,0,255,232, 54,0,255,236, 76,0,255,236, 76,0
+, 85, 44, 15,0, 85, 44, 15,0, 83, 47, 20,0, 83, 47, 20,0
+, 80, 48, 24,0, 80, 48, 24,0,146, 76, 29,0,146, 76, 29,0
+,255,227, 30,0,255,227, 30,0,255,237, 75,0,255,237, 75,0
+,125, 98, 52,0,125, 98, 52,0,145,120, 69,0,145,120, 69,0
+,133,105, 55,0,133,105, 55,0,255,231, 48,0,255,231, 48,0
+,255,237, 75,0,255,237, 75,0,127,100, 53,0,127,100, 53,0
+,143,117, 67,0,143,117, 67,0,132,105, 55,0,132,105, 55,0
+,255,232, 54,0,255,232, 54,0,255,236, 76,0,255,236, 76,0
+, 85, 44, 15,0, 85, 44, 15,0, 83, 47, 20,0, 83, 47, 20,0
+, 79, 48, 23,0, 79, 48, 23,0,149, 72, 24,0,149, 72, 24,0
+,255,227, 31,0,255,227, 31,0, 82, 56, 21,0, 82, 56, 21,0
+,255,236, 76,0,255,236, 76,0, 97, 72, 30,0, 97, 72, 30,0
+,255,231, 48,0,255,231, 48,0, 16, 12, 5,0, 16, 12, 5,0
+,102, 75, 32,0,102, 75, 32,0,255,237, 75,0,255,237, 75,0
+,107, 80, 36,0,107, 80, 36,0,255,221, 2,0,255,221, 2,0
+, 65, 45, 19,0, 65, 45, 19,0, 48, 38, 19,0, 48, 38, 19,0
+, 66, 32, 6,0, 66, 32, 6,0, 80, 43, 14,0, 80, 43, 14,0
+, 79, 48, 23,0, 79, 48, 23,0,149, 72, 24,0,149, 72, 24,0
+,255,227, 31,0,255,227, 31,0, 82, 56, 21,0, 82, 56, 21,0
+,255,236, 76,0,255,236, 76,0, 97, 72, 30,0, 97, 72, 30,0
+,255,231, 48,0,255,231, 48,0, 16, 12, 5,0, 16, 12, 5,0
+,102, 75, 32,0,102, 75, 32,0,255,237, 75,0,255,237, 75,0
+,107, 80, 36,0,107, 80, 36,0,255,221, 2,0,255,221, 2,0
+, 65, 45, 19,0, 65, 45, 19,0, 48, 38, 19,0, 48, 38, 19,0
+, 66, 32, 6,0, 66, 32, 6,0, 80, 43, 14,0, 80, 43, 14,0
+, 85, 52, 25,0, 85, 52, 25,0,149, 70, 21,0,149, 70, 21,0
+,255,229, 37,0,255,229, 37,0, 16, 12, 3,0, 16, 12, 3,0
+,255,236, 76,0,255,236, 76,0, 62, 48, 12,0, 62, 48, 12,0
+,255,236, 76,0,255,236, 76,0, 27, 21, 8,0, 27, 21, 8,0
+,107, 84, 34,0,107, 84, 34,0,255,237, 75,0,255,237, 75,0
+, 11, 8, 3,0, 11, 8, 3,0,106, 84, 35,0,106, 84, 35,0
+,255,237, 74,0,255,237, 74,0, 98, 75, 35,0, 98, 75, 35,0
+,105, 50, 7,0,105, 50, 7,0, 83, 46, 15,0, 83, 46, 15,0
+, 85, 52, 25,0, 85, 52, 25,0,149, 70, 21,0,149, 70, 21,0
+,255,229, 37,0,255,229, 37,0, 16, 12, 3,0, 16, 12, 3,0
+,255,236, 76,0,255,236, 76,0, 62, 48, 12,0, 62, 48, 12,0
+,255,236, 76,0,255,236, 76,0, 27, 21, 8,0, 27, 21, 8,0
+,107, 84, 34,0,107, 84, 34,0,255,237, 75,0,255,237, 75,0
+, 11, 8, 3,0, 11, 8, 3,0,106, 84, 35,0,106, 84, 35,0
+,255,237, 74,0,255,237, 74,0, 98, 75, 35,0, 98, 75, 35,0
+,105, 50, 7,0,105, 50, 7,0, 83, 46, 15,0, 83, 46, 15,0
+, 81, 49, 23,0, 81, 49, 23,0,142, 67, 20,0,142, 67, 20,0
+,255,230, 45,0,255,230, 45,0, 2, 1, 0,0, 2, 1, 0,0
+,255,236, 76,0,255,236, 76,0, 75, 58, 13,0, 75, 58, 13,0
+,255,236, 76,0,255,236, 76,0, 80, 62, 19,0, 80, 62, 19,0
+, 81, 64, 21,0, 81, 64, 21,0,255,236, 76,0,255,236, 76,0
+, 2, 2, 1,0, 2, 2, 1,0,111, 86, 36,0,111, 86, 36,0
+, 88, 63, 23,0, 88, 63, 23,0,255,236, 76,0,255,236, 76,0
+, 66, 31, 4,0, 66, 31, 4,0, 87, 46, 15,0, 87, 46, 15,0
+, 81, 49, 23,0, 81, 49, 23,0,142, 67, 20,0,142, 67, 20,0
+,255,230, 45,0,255,230, 45,0, 2, 1, 0,0, 2, 1, 0,0
+,255,236, 76,0,255,236, 76,0, 75, 58, 13,0, 75, 58, 13,0
+,255,236, 76,0,255,236, 76,0, 80, 62, 19,0, 80, 62, 19,0
+, 81, 64, 21,0, 81, 64, 21,0,255,236, 76,0,255,236, 76,0
+, 2, 2, 1,0, 2, 2, 1,0,111, 86, 36,0,111, 86, 36,0
+, 88, 63, 23,0, 88, 63, 23,0,255,236, 76,0,255,236, 76,0
+, 66, 31, 4,0, 66, 31, 4,0, 87, 46, 15,0, 87, 46, 15,0
+, 78, 46, 21,0, 78, 46, 21,0,150, 76, 25,0,150, 76, 25,0
+,255,237, 75,0,255,237, 75,0,255,236, 76,0,255,236, 76,0
+, 57, 44, 14,0, 57, 44, 14,0,111, 87, 27,0,111, 87, 27,0
+,104, 80, 30,0,104, 80, 30,0,255,236, 76,0,255,236, 76,0
+,255,236, 76,0,255,236, 76,0, 47, 37, 16,0, 47, 37, 16,0
+,116, 91, 38,0,116, 91, 38,0,255,237, 75,0,255,237, 75,0
+,255,236, 76,0,255,236, 76,0, 38, 30, 13,0, 38, 30, 13,0
+, 56, 26, 4,0, 56, 26, 4,0, 82, 43, 14,0, 82, 43, 14,0
+, 78, 46, 21,0, 78, 46, 21,0,150, 76, 25,0,150, 76, 25,0
+,255,237, 75,0,255,237, 75,0,255,236, 76,0,255,236, 76,0
+, 57, 44, 14,0, 57, 44, 14,0,111, 87, 27,0,111, 87, 27,0
+,104, 80, 30,0,104, 80, 30,0,255,236, 76,0,255,236, 76,0
+,255,236, 76,0,255,236, 76,0, 47, 37, 16,0, 47, 37, 16,0
+,116, 91, 38,0,116, 91, 38,0,255,237, 75,0,255,237, 75,0
+,255,236, 76,0,255,236, 76,0, 38, 30, 13,0, 38, 30, 13,0
+, 56, 26, 4,0, 56, 26, 4,0, 82, 43, 14,0, 82, 43, 14,0
+, 82, 50, 25,0, 82, 50, 25,0,159, 84, 31,0,159, 84, 31,0
+,125,101, 52,0,125,101, 52,0, 57, 46, 20,0, 57, 46, 20,0
+, 66, 51, 23,0, 66, 51, 23,0,140,115, 53,0,140,115, 53,0
+,134,109, 51,0,134,109, 51,0,118, 94, 41,0,118, 94, 41,0
+, 47, 38, 16,0, 47, 38, 16,0, 63, 50, 23,0, 63, 50, 23,0
+,129,105, 46,0,129,105, 46,0,114, 92, 41,0,114, 92, 41,0
+, 71, 57, 25,0, 71, 57, 25,0, 68, 52, 26,0, 68, 52, 26,0
+,111, 51, 7,0,111, 51, 7,0, 84, 47, 17,0, 84, 47, 17,0
+, 82, 50, 25,0, 82, 50, 25,0,159, 84, 31,0,159, 84, 31,0
+,125,101, 52,0,125,101, 52,0, 57, 46, 20,0, 57, 46, 20,0
+, 66, 51, 23,0, 66, 51, 23,0,140,115, 53,0,140,115, 53,0
+,134,109, 51,0,134,109, 51,0,118, 94, 41,0,118, 94, 41,0
+, 47, 38, 16,0, 47, 38, 16,0, 63, 50, 23,0, 63, 50, 23,0
+,129,105, 46,0,129,105, 46,0,114, 92, 41,0,114, 92, 41,0
+, 71, 57, 25,0, 71, 57, 25,0, 68, 52, 26,0, 68, 52, 26,0
+,111, 51, 7,0,111, 51, 7,0, 84, 47, 17,0, 84, 47, 17,0
+, 81, 50, 24,0, 81, 50, 24,0,164, 89, 35,0,164, 89, 35,0
+,130,104, 57,0,130,104, 57,0,121, 97, 46,0,121, 97, 46,0
+,131,109, 52,0,131,109, 52,0,139,116, 57,0,139,116, 57,0
+,137,114, 58,0,137,114, 58,0,123, 98, 47,0,123, 98, 47,0
+,125,101, 49,0,125,101, 49,0,139,115, 59,0,139,115, 59,0
+,141,118, 62,0,141,118, 62,0,137,110, 58,0,137,110, 58,0
+,130,106, 52,0,130,106, 52,0,131,108, 55,0,131,108, 55,0
+,113, 54, 9,0,113, 54, 9,0, 85, 48, 18,0, 85, 48, 18,0
+, 81, 50, 24,0, 81, 50, 24,0,164, 89, 35,0,164, 89, 35,0
+,130,104, 57,0,130,104, 57,0,121, 97, 46,0,121, 97, 46,0
+,131,109, 52,0,131,109, 52,0,139,116, 57,0,139,116, 57,0
+,137,114, 58,0,137,114, 58,0,123, 98, 47,0,123, 98, 47,0
+,125,101, 49,0,125,101, 49,0,139,115, 59,0,139,115, 59,0
+,141,118, 62,0,141,118, 62,0,137,110, 58,0,137,110, 58,0
+,130,106, 52,0,130,106, 52,0,131,108, 55,0,131,108, 55,0
+,113, 54, 9,0,113, 54, 9,0, 85, 48, 18,0, 85, 48, 18,0
+, 76, 45, 21,0, 76, 45, 21,0,150, 75, 26,0,150, 75, 26,0
+,255,228, 34,0,255,228, 34,0,255,237, 75,0,255,237, 75,0
+,118, 90, 42,0,118, 90, 42,0,138,111, 58,0,138,111, 58,0
+,120, 93, 46,0,120, 93, 46,0,255,231, 48,0,255,231, 48,0
+,255,237, 75,0,255,237, 75,0,118, 92, 43,0,118, 92, 43,0
+,118, 91, 42,0,118, 91, 42,0,255,233, 60,0,255,233, 60,0
+,100, 73, 31,0,100, 73, 31,0,255,236, 76,0,255,236, 76,0
+,102, 51, 9,0,102, 51, 9,0, 82, 43, 14,0, 82, 43, 14,0
+, 76, 45, 21,0, 76, 45, 21,0,150, 75, 26,0,150, 75, 26,0
+,255,228, 34,0,255,228, 34,0,255,237, 75,0,255,237, 75,0
+,118, 90, 42,0,118, 90, 42,0,138,111, 58,0,138,111, 58,0
+,120, 93, 46,0,120, 93, 46,0,255,231, 48,0,255,231, 48,0
+,255,237, 75,0,255,237, 75,0,118, 92, 43,0,118, 92, 43,0
+,118, 91, 42,0,118, 91, 42,0,255,233, 60,0,255,233, 60,0
+,100, 73, 31,0,100, 73, 31,0,255,236, 76,0,255,236, 76,0
+,102, 51, 9,0,102, 51, 9,0, 82, 43, 14,0, 82, 43, 14,0
+, 78, 47, 22,0, 78, 47, 22,0,142, 67, 20,0,142, 67, 20,0
+,255,226, 24,0,255,226, 24,0, 82, 56, 20,0, 82, 56, 20,0
+,255,236, 76,0,255,236, 76,0,106, 80, 33,0,106, 80, 33,0
+,255,220, 0,0,255,220, 0,0, 15, 11, 4,0, 15, 11, 4,0
+, 99, 72, 30,0, 99, 72, 30,0,255,237, 74,0,255,237, 74,0
+, 93, 70, 27,0, 93, 70, 27,0,247,212, 0,0,247,212, 0,0
+, 66, 50, 15,0, 66, 50, 15,0,255,236, 76,0,255,236, 76,0
+, 66, 30, 1,0, 66, 30, 1,0, 77, 40, 11,0, 77, 40, 11,0
+, 78, 47, 22,0, 78, 47, 22,0,142, 67, 20,0,142, 67, 20,0
+,255,226, 24,0,255,226, 24,0, 82, 56, 20,0, 82, 56, 20,0
+,255,236, 76,0,255,236, 76,0,106, 80, 33,0,106, 80, 33,0
+,255,220, 0,0,255,220, 0,0, 15, 11, 4,0, 15, 11, 4,0
+, 99, 72, 30,0, 99, 72, 30,0,255,237, 74,0,255,237, 74,0
+, 93, 70, 27,0, 93, 70, 27,0,247,212, 0,0,247,212, 0,0
+, 66, 50, 15,0, 66, 50, 15,0,255,236, 76,0,255,236, 76,0
+, 66, 30, 1,0, 66, 30, 1,0, 77, 40, 11,0, 77, 40, 11,0
+, 86, 52, 25,0, 86, 52, 25,0,136, 64, 18,0,136, 64, 18,0
+,255,230, 45,0,255,230, 45,0,255,236, 76,0,255,236, 76,0
+, 42, 33, 8,0, 42, 33, 8,0, 82, 64, 20,0, 82, 64, 20,0
+,255,237, 74,0,255,237, 74,0, 28, 22, 8,0, 28, 22, 8,0
+,109, 84, 35,0,109, 84, 35,0,255,225, 26,0,255,225, 26,0
+, 35, 26, 9,0, 35, 26, 9,0, 82, 64, 22,0, 82, 64, 22,0
+,255,237, 74,0,255,237, 74,0, 49, 39, 13,0, 49, 39, 13,0
+, 67, 30, 0,0, 67, 30, 0,0, 80, 40, 8,0, 80, 40, 8,0
+, 86, 52, 25,0, 86, 52, 25,0,136, 64, 18,0,136, 64, 18,0
+,255,230, 45,0,255,230, 45,0,255,236, 76,0,255,236, 76,0
+, 42, 33, 8,0, 42, 33, 8,0, 82, 64, 20,0, 82, 64, 20,0
+,255,237, 74,0,255,237, 74,0, 28, 22, 8,0, 28, 22, 8,0
+,109, 84, 35,0,109, 84, 35,0,255,225, 26,0,255,225, 26,0
+, 35, 26, 9,0, 35, 26, 9,0, 82, 64, 22,0, 82, 64, 22,0
+,255,237, 74,0,255,237, 74,0, 49, 39, 13,0, 49, 39, 13,0
+, 67, 30, 0,0, 67, 30, 0,0, 80, 40, 8,0, 80, 40, 8,0
+, 74, 43, 19,0, 74, 43, 19,0,140, 63, 17,0,140, 63, 17,0
+,255,237, 74,0,255,237, 74,0, 14, 11, 3,0, 14, 11, 3,0
+,255,236, 76,0,255,236, 76,0, 82, 63, 15,0, 82, 63, 15,0
+,255,237, 75,0,255,237, 75,0, 80, 63, 19,0, 80, 63, 19,0
+, 86, 67, 22,0, 86, 67, 22,0,255,237, 75,0,255,237, 75,0
+, 67, 52, 16,0, 67, 52, 16,0,255,237, 74,0,255,237, 74,0
+, 43, 34, 8,0, 43, 34, 8,0,255,236, 76,0,255,236, 76,0
+, 66, 29, 0,0, 66, 29, 0,0, 71, 34, 4,0, 71, 34, 4,0
+, 74, 43, 19,0, 74, 43, 19,0,140, 63, 17,0,140, 63, 17,0
+,255,237, 74,0,255,237, 74,0, 14, 11, 3,0, 14, 11, 3,0
+,255,236, 76,0,255,236, 76,0, 82, 63, 15,0, 82, 63, 15,0
+,255,237, 75,0,255,237, 75,0, 80, 63, 19,0, 80, 63, 19,0
+, 86, 67, 22,0, 86, 67, 22,0,255,237, 75,0,255,237, 75,0
+, 67, 52, 16,0, 67, 52, 16,0,255,237, 74,0,255,237, 74,0
+, 43, 34, 8,0, 43, 34, 8,0,255,236, 76,0,255,236, 76,0
+, 66, 29, 0,0, 66, 29, 0,0, 71, 34, 4,0, 71, 34, 4,0
+, 77, 45, 20,0, 77, 45, 20,0,145, 71, 22,0,145, 71, 22,0
+,255,237, 75,0,255,237, 75,0,255,236, 76,0,255,236, 76,0
+, 47, 37, 11,0, 47, 37, 11,0,108, 84, 26,0,108, 84, 26,0
+, 95, 76, 27,0, 95, 76, 27,0,255,236, 76,0,255,236, 76,0
+,255,236, 76,0,255,236, 76,0, 71, 55, 20,0, 71, 55, 20,0
+, 98, 77, 26,0, 98, 77, 26,0,255,236, 76,0,255,236, 76,0
+, 78, 62, 19,0, 78, 62, 19,0,255,236, 76,0,255,236, 76,0
+, 32, 14, 0,0, 32, 14, 0,0, 75, 37, 5,0, 75, 37, 5,0
+, 77, 45, 20,0, 77, 45, 20,0,145, 71, 22,0,145, 71, 22,0
+,255,237, 75,0,255,237, 75,0,255,236, 76,0,255,236, 76,0
+, 47, 37, 11,0, 47, 37, 11,0,108, 84, 26,0,108, 84, 26,0
+, 95, 76, 27,0, 95, 76, 27,0,255,236, 76,0,255,236, 76,0
+,255,236, 76,0,255,236, 76,0, 71, 55, 20,0, 71, 55, 20,0
+, 98, 77, 26,0, 98, 77, 26,0,255,236, 76,0,255,236, 76,0
+, 78, 62, 19,0, 78, 62, 19,0,255,236, 76,0,255,236, 76,0
+, 32, 14, 0,0, 32, 14, 0,0, 75, 37, 5,0, 75, 37, 5,0
+, 79, 46, 24,0, 79, 46, 24,0,132, 76, 31,0,132, 76, 31,0
+,124, 59, 13,0,124, 59, 13,0, 96, 43, 0,0, 96, 43, 0,0
+, 52, 23, 0,0, 52, 23, 0,0,123, 55, 0,0,123, 55, 0,0
+,136, 59, 1,0,136, 59, 1,0,124, 54, 2,0,124, 54, 2,0
+, 96, 43, 0,0, 96, 43, 0,0, 95, 42, 0,0, 95, 42, 0,0
+,124, 57, 0,0,124, 57, 0,0,121, 55, 0,0,121, 55, 0,0
+, 93, 41, 0,0, 93, 41, 0,0, 98, 43, 0,0, 98, 43, 0,0
+, 57, 28, 5,0, 57, 28, 5,0, 79, 43, 12,0, 79, 43, 12,0
+, 79, 46, 24,0, 79, 46, 24,0,132, 76, 31,0,132, 76, 31,0
+,124, 59, 13,0,124, 59, 13,0, 96, 43, 0,0, 96, 43, 0,0
+, 52, 23, 0,0, 52, 23, 0,0,123, 55, 0,0,123, 55, 0,0
+,136, 59, 1,0,136, 59, 1,0,124, 54, 2,0,124, 54, 2,0
+, 96, 43, 0,0, 96, 43, 0,0, 95, 42, 0,0, 95, 42, 0,0
+,124, 57, 0,0,124, 57, 0,0,121, 55, 0,0,121, 55, 0,0
+, 93, 41, 0,0, 93, 41, 0,0, 98, 43, 0,0, 98, 43, 0,0
+, 57, 28, 5,0, 57, 28, 5,0, 79, 43, 12,0, 79, 43, 12,0
+, 66, 43, 24,0, 66, 43, 24,0, 78, 46, 20,0, 78, 46, 20,0
+, 69, 38, 13,0, 69, 38, 13,0, 70, 36, 8,0, 70, 36, 8,0
+, 70, 35, 7,0, 70, 35, 7,0, 69, 35, 7,0, 69, 35, 7,0
+, 71, 39, 10,0, 71, 39, 10,0, 71, 36, 9,0, 71, 36, 9,0
+, 71, 36, 9,0, 71, 36, 9,0, 72, 36, 9,0, 72, 36, 9,0
+, 68, 36, 7,0, 68, 36, 7,0, 67, 33, 7,0, 67, 33, 7,0
+, 67, 33, 6,0, 67, 33, 6,0, 64, 33, 7,0, 64, 33, 7,0
+, 74, 38, 12,0, 74, 38, 12,0, 61, 35, 14,0, 61, 35, 14,0
+, 66, 43, 24,0, 66, 43, 24,0, 78, 46, 20,0, 78, 46, 20,0
+, 69, 38, 13,0, 69, 38, 13,0, 70, 36, 8,0, 70, 36, 8,0
+, 70, 35, 7,0, 70, 35, 7,0, 69, 35, 7,0, 69, 35, 7,0
+, 71, 39, 10,0, 71, 39, 10,0, 71, 36, 9,0, 71, 36, 9,0
+, 71, 36, 9,0, 71, 36, 9,0, 72, 36, 9,0, 72, 36, 9,0
+, 68, 36, 7,0, 68, 36, 7,0, 67, 33, 7,0, 67, 33, 7,0
+, 67, 33, 6,0, 67, 33, 6,0, 64, 33, 7,0, 64, 33, 7,0
+, 74, 38, 12,0, 74, 38, 12,0, 61, 35, 14,0, 61, 35, 14,0
+#endif
+#ifdef LOGO_2
+ 61, 39, 23,0, 91, 57, 30,0, 85, 53, 28,0, 82, 51, 26,0
+, 83, 52, 28,0, 91, 57, 30,0, 91, 57, 30,0, 91, 57, 30,0
+, 88, 55, 29,0, 91, 57, 30,0, 91, 57, 30,0, 91, 57, 30,0
+, 91, 57, 30,0, 91, 57, 30,0, 91, 57, 30,0, 88, 55, 29,0
+, 80, 50, 25,0, 85, 53, 28,0, 91, 57, 30,0, 91, 57, 30,0
+, 85, 53, 28,0, 88, 55, 29,0, 88, 55, 29,0, 85, 53, 28,0
+, 85, 53, 28,0, 85, 53, 28,0, 82, 51, 26,0, 78, 48, 25,0
+, 78, 48, 25,0, 82, 51, 26,0, 88, 55, 29,0, 61, 39, 23,0
+, 91, 57, 30,0,140, 89, 49,0,166,101, 49,0,161, 97, 49,0
+,157, 96, 49,0,165, 99, 50,0,165, 99, 50,0,161, 99, 50,0
+,170,104, 52,0,170,104, 52,0,170,104, 52,0,157, 96, 49,0
+,161, 97, 49,0,162, 99, 50,0,161, 97, 49,0,148, 89, 46,0
+,156, 95, 48,0,153, 93, 47,0,156, 95, 48,0,156, 95, 47,0
+,166,101, 49,0,166,101, 49,0,159, 98, 50,0,157, 96, 49,0
+,160, 96, 49,0,166,101, 49,0,150, 92, 47,0,148, 89, 46,0
+,148, 89, 46,0,160, 96, 49,0,156, 99, 53,0, 91, 57, 30,0
+, 75, 46, 24,0,150, 92, 47,0,151, 99, 53,0,172,106, 53,0
+,162, 99, 50,0,162, 99, 50,0,159, 98, 50,0,147, 91, 46,0
+,148, 89, 46,0,146, 89, 47,0,150, 92, 47,0,147, 91, 46,0
+,140, 84, 42,0,153, 91, 44,0,153, 91, 44,0,145, 86, 41,0
+,145, 86, 41,0,145, 86, 41,0,150, 92, 47,0,157, 96, 49,0
+,150, 92, 47,0,143, 87, 44,0,144, 89, 45,0,136, 84, 42,0
+,148, 89, 46,0,148, 89, 46,0,156, 95, 48,0,150, 92, 47,0
+,159, 98, 50,0,145, 93, 52,0,170,104, 52,0, 80, 50, 25,0
+, 71, 45, 23,0,140, 84, 42,0,172,106, 53,0,137, 87, 53,0
+,132, 92, 57,0,132, 92, 57,0,140, 97, 61,0,141,101, 64,0
+,141,101, 64,0,141,101, 64,0,140, 97, 61,0,141,101, 64,0
+,132, 88, 51,0,231,192, 61,0,245,209, 66,0,245,209, 66,0
+,245,209, 66,0,246,210, 68,0,243,209, 68,0,136, 97, 62,0
+,141,101, 64,0,136, 97, 62,0,136, 97, 62,0,136, 97, 62,0
+,136, 97, 62,0,132, 92, 57,0,122, 85, 52,0,125, 88, 54,0
+,122, 85, 52,0,168,102, 50,0,161, 97, 49,0, 78, 48, 25,0
+, 75, 46, 24,0,150, 92, 47,0,165, 99, 50,0,122, 85, 52,0
+,132,112, 74,0,130,111, 74,0,130,111, 74,0,154,135, 91,0
+,167,148,101,0,167,148,101,0,168,149,102,0,166,147, 99,0
+,147,124, 78,0,132,107, 61,0,224,185, 54,0,242,206, 67,0
+,156,136, 86,0,151,129, 80,0,242,208, 64,0,244,207, 66,0
+,164,145, 99,0,165,146, 99,0,165,146, 99,0,167,148,101,0
+,154,135, 91,0,132,112, 74,0,131,113, 76,0,134,115, 78,0
+,113, 84, 53,0,156, 95, 48,0,171,103, 50,0, 80, 50, 25,0
+, 72, 44, 22,0,163,102, 51,0,168,102, 50,0,125, 88, 54,0
+,128,110, 73,0,131,113, 76,0,131,113, 76,0,128,110, 73,0
+,148,130, 87,0,164,145, 99,0,167,148,101,0,166,147, 98,0
+,162,143, 92,0,132,107, 61,0,220,180, 55,0,247,212, 70,0
+,155,135, 82,0,152,130, 81,0,243,209, 65,0,246,212, 67,0
+,163,144, 96,0,168,149,102,0,168,149,102,0,150,132, 89,0
+,127,109, 71,0,130,111, 74,0,132,112, 74,0,129,112, 75,0
+,113, 84, 53,0,156, 95, 48,0,178,109, 54,0, 75, 46, 24,0
+, 75, 46, 24,0,150, 92, 47,0,172,106, 53,0,140, 97, 61,0
+,128,111, 74,0,129,112, 75,0,133,113, 75,0,133,113, 75,0
+,132,112, 74,0,150,132, 89,0,166,147, 98,0,163,144, 96,0
+,158,138, 88,0,132,107, 61,0,217,177, 51,0,243,207, 69,0
+,243,207, 69,0,231,192, 61,0,240,205, 65,0,159,140, 90,0
+,163,144, 96,0,167,148,101,0,150,132, 89,0,130,111, 74,0
+,133,114, 76,0,133,113, 75,0,130,111, 74,0,129,112, 75,0
+,123, 93, 58,0,154, 94, 48,0,165, 99, 50,0, 78, 48, 25,0
+, 78, 48, 25,0,144, 88, 45,0,174,108, 52,0,142,101, 62,0
+,161,141, 95,0,128,111, 74,0,130,111, 74,0,133,113, 75,0
+,133,113, 75,0,128,110, 73,0,150,132, 89,0,165,146, 98,0
+,161,142, 93,0,135,112, 64,0,223,183, 55,0,247,212, 70,0
+,155,135, 82,0,144,122, 72,0,240,205, 65,0,245,209, 66,0
+,163,143, 95,0,148,130, 87,0,127,109, 71,0,133,113, 75,0
+,133,113, 75,0,130,111, 74,0,129,112, 75,0,164,145, 99,0
+,126, 96, 62,0,154, 94, 48,0,161, 97, 49,0, 83, 52, 28,0
+, 80, 50, 25,0,147, 91, 46,0,178,109, 54,0,148,103, 65,0
+,166,147,100,0,161,142, 95,0,128,111, 74,0,128,109, 72,0
+,132,112, 74,0,132,112, 74,0,130,110, 73,0,147,128, 85,0
+,153,132, 84,0,137,114, 66,0,224,185, 54,0,245,209, 66,0
+,155,135, 82,0,149,129, 75,0,235,199, 63,0,245,209, 66,0
+,148,130, 87,0,130,111, 74,0,132,112, 74,0,130,111, 74,0
+,128,110, 73,0,128,111, 74,0,161,142, 95,0,168,149,102,0
+,127, 97, 63,0,153, 93, 47,0,162, 99, 50,0, 83, 52, 28,0
+, 82, 51, 26,0,148, 89, 46,0,178,109, 54,0,151,106, 65,0
+,166,147,100,0,167,148,101,0,160,143, 95,0,129,112, 75,0
+,129,112, 75,0,135,115, 77,0,135,115, 77,0,130,109, 69,0
+,129,106, 61,0,210,166, 47,0,210,169, 51,0,231,192, 61,0
+,234,197, 61,0,224,185, 56,0,225,189, 55,0,141,120, 75,0
+,130,111, 74,0,134,116, 78,0,133,114, 76,0,128,110, 73,0
+,132,114, 76,0,163,144, 98,0,168,149,102,0,167,148,101,0
+,127, 97, 63,0,153, 93, 47,0,163,102, 51,0, 84, 52, 27,0
+, 75, 46, 24,0,150, 92, 47,0,170,104, 52,0,147,105, 69,0
+,170,151,105,0,170,151,105,0,166,147,100,0,164,145, 99,0
+,134,116, 78,0,133,114, 76,0,133,114, 76,0,131,110, 71,0
+,116, 94, 54,0,116, 94, 54,0,137,114, 66,0,144,122, 72,0
+,146,125, 75,0,148,127, 78,0,137,117, 73,0,130,110, 73,0
+,134,116, 78,0,134,116, 78,0,133,113, 75,0,134,116, 78,0
+,164,145, 99,0,168,149,102,0,170,151,105,0,167,148,101,0
+,126, 96, 62,0,150, 92, 47,0,172,106, 53,0, 85, 53, 28,0
+, 75, 46, 24,0,147, 91, 46,0,166,101, 49,0,151,106, 65,0
+,165,146, 98,0,165,146, 98,0,165,146, 99,0,165,146, 99,0
+,160,143, 95,0,134,116, 78,0,130,110, 73,0,132,112, 74,0
+,130,109, 69,0,117, 98, 59,0,137,117, 73,0,158,138, 88,0
+,156,136, 86,0,137,117, 73,0,125,105, 65,0,133,114, 76,0
+,135,115, 77,0,130,111, 74,0,134,116, 78,0,164,145, 99,0
+,166,147, 99,0,167,148,101,0,166,147,100,0,166,147,100,0
+,134, 97, 62,0,150, 92, 47,0,162, 99, 50,0, 84, 52, 27,0
+, 75, 46, 24,0,146, 89, 43,0,162, 95, 42,0,234,197, 61,0
+,248,214, 73,0,248,214, 73,0,248,211, 69,0,246,210, 68,0
+,159,140, 93,0,163,144, 96,0,131,113, 76,0,126,107, 69,0
+,125,105, 65,0,125,105, 65,0,247,212, 70,0,248,211, 69,0
+,246,210, 68,0,248,211, 69,0,245,211, 67,0,132,112, 74,0
+,132,112, 74,0,134,116, 78,0,161,141, 95,0,162,142, 94,0
+,247,212, 70,0,247,212, 70,0,248,211, 69,0,247,212, 70,0
+,123, 93, 58,0,148, 89, 46,0,157, 96, 49,0, 84, 52, 27,0
+, 72, 44, 22,0,145, 86, 41,0,158, 87, 33,0,110, 68, 32,0
+,224,185, 56,0,243,209, 68,0,158,138, 88,0,240,205, 65,0
+,244,208, 66,0,167,148,101,0,165,146, 99,0,134,116, 76,0
+,114, 93, 56,0,231,192, 61,0,244,208, 66,0,122,100, 60,0
+,112, 90, 51,0,109, 87, 49,0,241,207, 67,0,244,208, 66,0
+,134,116, 78,0,162,142, 94,0,148,127, 78,0,225,187, 56,0
+,243,209, 65,0,152,130, 81,0,156,136, 86,0,240,205, 65,0
+,245,209, 66,0,153, 93, 47,0,159, 98, 50,0, 80, 50, 25,0
+, 75, 46, 24,0,142, 84, 38,0,157, 88, 35,0,117, 67, 29,0
+,214,174, 48,0,241,206, 62,0,153,133, 80,0,141,118, 71,0
+,239,202, 64,0,243,208, 67,0,166,147, 99,0,163,143, 95,0
+,109, 87, 49,0,221,179, 53,0,241,206, 62,0,124,103, 62,0
+,121, 99, 57,0, 97, 75, 39,0,223,183, 53,0,241,206, 62,0
+,159,140, 93,0,157,139, 89,0,135,112, 64,0,202,161, 42,0
+,232,194, 57,0,241,206, 62,0,153,133, 80,0,159,139, 90,0
+,123, 93, 58,0,127, 79, 39,0,172,106, 53,0, 80, 50, 25,0
+, 78, 48, 25,0,142, 84, 38,0,166, 93, 38,0,117, 67, 29,0
+,221,183, 53,0,245,209, 66,0,149,129, 75,0,140,117, 70,0
+,231,192, 61,0,246,212, 67,0,164,145, 96,0,161,141, 92,0
+,129,106, 61,0,222,184, 56,0,247,212, 70,0,125,105, 65,0
+,124,103, 62,0,100, 77, 41,0,221,183, 53,0,245,211, 67,0
+,161,141, 92,0,155,136, 86,0,134,109, 62,0,123, 98, 52,0
+,217,177, 51,0,240,205, 65,0,245,209, 66,0,157,139, 89,0
+,123, 93, 58,0,137, 83, 40,0,157, 96, 49,0, 84, 52, 27,0
+, 78, 48, 25,0,132, 79, 38,0,166, 93, 38,0,117, 67, 29,0
+,224,185, 54,0,241,206, 62,0,149,129, 75,0,144,122, 72,0
+,232,194, 57,0,241,206, 62,0,155,136, 86,0,156,136, 86,0
+,104, 82, 43,0,214,174, 48,0,241,206, 65,0,130,109, 69,0
+,125,105, 65,0,104, 82, 43,0,224,185, 54,0,241,206, 65,0
+,163,144, 96,0,164,145, 96,0,152,130, 81,0,144,121, 71,0
+,144,121, 71,0,141,121, 70,0,238,201, 61,0,241,206, 62,0
+,241,206, 62,0,137, 83, 40,0,157, 96, 49,0, 85, 53, 28,0
+, 75, 46, 24,0,132, 79, 38,0,160, 87, 33,0,117, 67, 29,0
+,228,191, 59,0,246,209, 65,0,153,133, 80,0,246,210, 68,0
+,244,208, 66,0,157,139, 89,0,158,138, 88,0,125,107, 68,0
+, 97, 75, 39,0,210,166, 47,0,246,209, 65,0,122,100, 60,0
+,122,100, 60,0,112, 90, 51,0,235,199, 63,0,247,212, 70,0
+,134,116, 76,0,164,145, 96,0,152,130, 81,0,231,192, 61,0
+,248,211, 69,0,149,129, 75,0,144,121, 71,0,243,205, 64,0
+,246,210, 68,0,140, 84, 42,0,165, 99, 50,0, 84, 52, 27,0
+, 69, 43, 22,0,145, 86, 41,0,150, 82, 30,0,210,169, 51,0
+,212,172, 53,0,226,190, 63,0,229,191, 61,0,227,189, 57,0
+,155,136, 86,0,161,141, 92,0,131,113, 76,0,125,105, 65,0
+,109, 87, 49,0,100, 77, 41,0,220,180, 55,0,238,201, 61,0
+,239,202, 64,0,225,187, 56,0,227,189, 57,0,125,105, 65,0
+,127,109, 71,0,131,114, 73,0,147,124, 78,0,137,114, 66,0
+,223,183, 55,0,228,191, 59,0,223,183, 55,0,225,187, 56,0
+,113, 84, 53,0,161, 97, 49,0,154, 94, 48,0, 80, 50, 25,0
+, 72, 44, 22,0,145, 86, 41,0,150, 82, 30,0,120, 73, 33,0
+,134,109, 62,0,137,114, 66,0,141,118, 71,0,152,130, 81,0
+,159,139, 90,0,134,116, 78,0,132,112, 76,0,132,112, 74,0
+,125,105, 65,0,107, 86, 49,0,122,100, 60,0,144,122, 72,0
+,141,120, 75,0,123,102, 59,0,117, 98, 59,0,131,110, 71,0
+,132,112, 74,0,126,107, 69,0,125,105, 65,0,134,113, 66,0
+,134,113, 66,0,137,114, 66,0,141,118, 71,0,151,129, 80,0
+,122, 85, 52,0,143, 87, 44,0,162, 99, 50,0, 80, 50, 25,0
+, 71, 45, 23,0,140, 84, 42,0,162, 95, 42,0,144, 98, 56,0
+,161,141, 92,0,163,143, 95,0,164,145, 96,0,163,144, 96,0
+,134,115, 78,0,132,112, 74,0,133,113, 75,0,133,113, 75,0
+,126,107, 69,0,141,120, 75,0,159,139, 90,0,158,138, 88,0
+,161,141, 92,0,156,136, 88,0,143,124, 80,0,131,110, 71,0
+,133,114, 76,0,132,112, 74,0,127,109, 71,0,125,107, 68,0
+,152,133, 84,0,155,136, 86,0,159,139, 90,0,159,140, 93,0
+,134, 97, 62,0,148, 89, 46,0,162, 99, 50,0, 78, 48, 25,0
+, 75, 46, 24,0,134, 83, 41,0,171,103, 50,0,142,101, 62,0
+,164,145, 96,0,167,148,101,0,163,144, 98,0,132,114, 76,0
+,130,111, 74,0,135,115, 77,0,137,116, 77,0,132,112, 74,0
+,137,116, 77,0,243,208, 67,0,243,209, 65,0,162,142, 94,0
+,162,143, 92,0,155,136, 86,0,244,208, 66,0,245,209, 66,0
+,129,112, 75,0,133,114, 76,0,133,113, 75,0,125,107, 68,0
+,127,109, 71,0,157,137, 89,0,161,141, 92,0,167,148,101,0
+,128, 97, 65,0,148, 89, 46,0,159, 98, 50,0, 80, 50, 25,0
+, 81, 51, 29,0,136, 84, 42,0,157, 96, 49,0,148,103, 65,0
+,167,148,101,0,164,145, 99,0,131,113, 76,0,132,112, 74,0
+,132,112, 74,0,133,113, 75,0,128,110, 73,0,145,125, 81,0
+,139,116, 69,0,221,179, 53,0,248,214, 73,0,164,145, 96,0
+,163,143, 95,0,157,137, 89,0,247,212, 70,0,247,212, 70,0
+,147,128, 85,0,130,111, 74,0,133,113, 75,0,132,112, 74,0
+,130,111, 74,0,128,111, 74,0,159,140, 93,0,163,144, 96,0
+,126, 96, 62,0,136, 84, 42,0,161, 97, 49,0, 80, 50, 25,0
+, 80, 50, 25,0,144, 88, 45,0,168,102, 50,0,151,106, 65,0
+,163,144, 98,0,128,111, 74,0,128,110, 73,0,130,111, 74,0
+,130,110, 73,0,127,109, 71,0,148,130, 87,0,163,144, 96,0
+,140,117, 70,0,129,106, 61,0,224,185, 54,0,245,209, 66,0
+,156,136, 86,0,242,208, 64,0,244,208, 66,0,163,143, 95,0
+,165,146, 99,0,150,132, 89,0,128,109, 72,0,132,112, 74,0
+,130,111, 74,0,130,111, 74,0,128,110, 73,0,159,140, 93,0
+,123, 93, 58,0,150, 92, 47,0,156, 95, 48,0, 82, 51, 26,0
+, 75, 46, 24,0,140, 84, 42,0,171,103, 50,0,142,101, 62,0
+,129,112, 75,0,130,111, 74,0,130,111, 74,0,130,111, 74,0
+,128,109, 72,0,147,128, 85,0,161,142, 95,0,159,139, 90,0
+,155,136, 86,0,135,112, 64,0,134,109, 62,0,233,196, 58,0
+,244,208, 66,0,244,208, 66,0,160,139, 90,0,164,145, 96,0
+,166,147,100,0,167,148,101,0,150,132, 89,0,131,113, 76,0
+,134,115, 78,0,134,115, 78,0,132,112, 76,0,131,113, 76,0
+,123, 93, 58,0,146, 89, 47,0,153, 93, 47,0, 80, 50, 25,0
+, 71, 45, 23,0,138, 85, 44,0,168,102, 50,0,122, 85, 52,0
+,128,110, 73,0,130,111, 74,0,128,110, 73,0,128,110, 73,0
+,150,132, 89,0,164,146, 97,0,164,146, 97,0,161,142, 93,0
+,157,139, 89,0,146,125, 75,0,137,114, 66,0,227,189, 57,0
+,235,199, 63,0,245,211, 67,0,160,140, 90,0,165,146, 99,0
+,168,149,102,0,167,148,101,0,165,146, 99,0,150,132, 89,0
+,128,110, 73,0,130,111, 74,0,130,111, 74,0,128,110, 73,0
+,103, 81, 51,0,147, 91, 46,0,156, 95, 48,0, 75, 46, 24,0
+, 69, 43, 22,0,150, 92, 47,0,166,101, 49,0,125, 88, 54,0
+,131,113, 76,0,130,111, 74,0,128,109, 72,0,148,130, 87,0
+,163,144, 96,0,166,147,100,0,166,147, 98,0,161,141, 92,0
+,153,132, 84,0,146,125, 75,0,238,201, 61,0,233,196, 58,0
+,134,109, 62,0,225,187, 56,0,241,206, 62,0,163,143, 95,0
+,163,143, 95,0,165,146, 99,0,166,147,100,0,166,147,100,0
+,154,137, 93,0,130,111, 74,0,130,111, 74,0,130,111, 74,0
+,113, 84, 53,0,153, 93, 47,0,172,106, 53,0, 72, 44, 22,0
+, 71, 45, 23,0,143, 87, 44,0,168,102, 50,0,122, 85, 52,0
+,129,112, 75,0,130,111, 74,0,150,132, 89,0,163,144, 96,0
+,163,144, 96,0,161,142, 95,0,159,140, 93,0,157,137, 89,0
+,141,121, 70,0,225,189, 55,0,246,209, 65,0,149,129, 75,0
+,135,112, 64,0,129,104, 58,0,238,201, 61,0,248,211, 69,0
+,163,144, 96,0,166,147,100,0,168,149,102,0,170,151,105,0
+,167,148,101,0,150,132, 89,0,128,111, 74,0,132,112, 74,0
+,113, 84, 53,0,156, 95, 48,0,168,102, 50,0, 78, 48, 25,0
+, 71, 45, 23,0,138, 85, 44,0,168,102, 50,0,134, 86, 50,0
+,132, 92, 57,0,151,106, 65,0,156,110, 68,0,156,110, 68,0
+,149,107, 67,0,151,106, 65,0,150,104, 59,0,150,104, 59,0
+,127, 79, 39,0,209,167, 48,0,234,197, 61,0,150,104, 59,0
+,151, 99, 53,0,120, 73, 33,0,212,170, 48,0,241,206, 65,0
+,149,107, 67,0,151,106, 65,0,149,107, 67,0,149,107, 67,0
+,156,110, 68,0,151,106, 65,0,147,105, 64,0,132, 92, 57,0
+,122, 85, 52,0,170,104, 52,0,162, 99, 50,0, 78, 48, 25,0
+, 75, 46, 24,0,144, 89, 45,0,145, 93, 52,0,159, 98, 50,0
+,154, 94, 48,0,162, 99, 50,0,153, 93, 47,0,144, 88, 45,0
+,144, 89, 45,0,144, 88, 45,0,148, 89, 46,0,152, 89, 41,0
+,131, 71, 25,0,131, 71, 25,0,146, 83, 34,0,152, 89, 41,0
+,134, 76, 33,0,131, 71, 25,0,131, 71, 25,0,134, 76, 33,0
+,156, 95, 48,0,150, 92, 47,0,156, 95, 48,0,156, 95, 48,0
+,144, 88, 45,0,143, 87, 44,0,150, 92, 47,0,147, 91, 46,0
+,155, 96, 51,0,146, 91, 48,0,170,104, 52,0, 82, 51, 26,0
+, 91, 57, 30,0,117, 75, 42,0,138, 85, 44,0,132, 79, 38,0
+,122, 76, 39,0,124, 77, 40,0,120, 74, 37,0,117, 73, 38,0
+,127, 79, 39,0,124, 77, 40,0,132, 79, 38,0,141, 83, 40,0
+,125, 74, 34,0,119, 70, 32,0,120, 73, 33,0,120, 74, 37,0
+,118, 71, 34,0,119, 70, 32,0,119, 70, 32,0,120, 73, 33,0
+,127, 79, 39,0,127, 79, 39,0,124, 77, 40,0,127, 79, 39,0
+,122, 76, 39,0,122, 76, 39,0,117, 73, 38,0,117, 73, 38,0
+,124, 77, 40,0,138, 85, 44,0,140, 89, 49,0, 91, 57, 30,0
+, 61, 39, 23,0, 88, 55, 29,0, 75, 46, 24,0, 69, 43, 22,0
+, 69, 43, 22,0, 72, 44, 22,0, 71, 45, 23,0, 71, 45, 23,0
+, 71, 45, 23,0, 71, 45, 23,0, 75, 46, 24,0, 72, 44, 22,0
+, 72, 44, 22,0, 75, 46, 24,0, 72, 44, 22,0, 69, 43, 22,0
+, 69, 43, 22,0, 72, 44, 22,0, 75, 46, 24,0, 72, 44, 22,0
+, 69, 43, 22,0, 75, 46, 24,0, 69, 43, 22,0, 69, 43, 22,0
+, 71, 45, 23,0, 69, 43, 22,0, 69, 43, 22,0, 69, 43, 22,0
+, 69, 43, 22,0, 75, 46, 24,0, 88, 55, 29,0, 61, 39, 23,0
+#endif
diff --git a/src/gui/dosbox_splash.h b/src/gui/dosbox_splash.h
new file mode 100644
index 000000000..8ae776f24
--- /dev/null
+++ b/src/gui/dosbox_splash.h
@@ -0,0 +1,1028 @@
+/* GIMP RGB C-Source image dump 1-byte-run-length-encoded (t.c) */
+
+#define GIMP_IMAGE_RUN_LENGTH_DECODE(image_buf, rle_data, size, bpp) do \
+{ unsigned int __bpp; unsigned char *__ip; const unsigned char *__il, *__rd; \
+ __bpp = (bpp); __ip = (image_buf); __il = __ip + (size) * __bpp; \
+ __rd = (rle_data); if (__bpp > 3) { /* RGBA */ \
+ while (__ip < __il) { unsigned int __l = *(__rd++); \
+ if (__l & 128) { __l = __l - 128; \
+ do { memcpy (__ip, __rd, 4); __ip += 4; } while (--__l); __rd += 4; \
+ } else { __l *= 4; memcpy (__ip, __rd, __l); \
+ __ip += __l; __rd += __l; } } \
+ } else { /* RGB */ \
+ while (__ip < __il) { unsigned int __l = *(__rd++); \
+ if (__l & 128) { __l = __l - 128; \
+ do { memcpy (__ip, __rd, 3); __ip += 3; } while (--__l); __rd += 3; \
+ } else { __l *= 3; memcpy (__ip, __rd, __l); \
+ __ip += __l; __rd += __l; } } \
+ } } while (0)
+static const struct {
+ unsigned int width;
+ unsigned int height;
+ unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */
+ unsigned char rle_pixel_data[24875 + 1];
+} gimp_image = {
+ 640, 400, 3,
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\245\272"
+ "=\0\3\275D\11\261J\27\260;\2\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\376\272=\0\14\300M\24\317\201[\341\274\251\360\355\352\241~m\37\37\37,\""
+ "\34B'\30]+\23y1\15\2205\10\2469\4\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\365\272=\0\3\303Y%\324\222r\344\305\266\204\362\362\362\1\241~m\207\37"
+ "\37\37\6$\40\36?&\31Y*\23q/\17\2104\12\2378\5\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\354\272=\0\3\310j<\330\235\200\353\334\323\207\362\362\362"
+ "\1\241~m\216\37\37\37\6:%\32S)\25h-\20\2002\14\2347\6\267=\1\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\341\272=\0\4\275D\11\313uK\335\256\227\355"
+ "\342\337\212\362\362\362\1\241~m\224\37\37\37\6""3#\33I)\27b,\22|1\14\227"
+ "6\7\260;\2\377\272=\0\377\272=\0\377\272=\0\377\272=\0\330\272=\0\4\300M"
+ "\24\317\201[\341\274\251\360\355\352\215\362\362\362\1\241~m\232\37\37\37"
+ "\6+!\35B'\30]+\23y1\15\2205\10\2469\4\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\317\272=\0\3\301T\37\324\222r\344\305\266\221\362\362\362\1\241~"
+ "m\240\37\37\37\6$\40\36?&\31Y*\23q/\17\2104\12\2378\5\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\306\272=\0\3\310j<\330\235\200\353\334\323\224"
+ "\362\362\362\1\241~m\247\37\37\37\6:%\32S)\25h-\20\2002\14\2347\6\267=\1"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\273\272=\0\4\275D\11\313uK\335"
+ "\256\227\355\342\337\227\362\362\362\1\241~m\255\37\37\37\6""1\"\33I)\27"
+ "b,\22|1\14\2276\7\255;\3\377\272=\0\377\272=\0\377\272=\0\377\272=\0\262"
+ "\272=\0\4\300M\24\317\201[\341\274\251\360\355\352\232\362\362\362\1\241"
+ "~m\263\37\37\37\6+!\35B'\30]+\23y1\15\2175\11\2469\4\377\272=\0\377\272="
+ "\0\377\272=\0\377\272=\0\251\272=\0\3\301T\37\324\222r\344\305\266\236\362"
+ "\362\362\1\241~m\271\37\37\37\6$\40\36?&\31Y*\23o/\17\2104\12\2378\5\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\240\272=\0\3\310j<\330\235\200\352"
+ "\331\320\241\362\362\362\1\241~m\212\37\37\37\5\240\240\240\252\252\252\212"
+ "\212\212gggGGG\261\37\37\37\6:%\32Q)\25h-\20\2002\14\2347\6\267=\1\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\225\272=\0\4\275D\11\313uK\335\256\227"
+ "\355\342\337\244\362\362\362\1\241~m\212\37\37\37\1\300\300\300\205\362\362"
+ "\362\6\317\317\317\257\257\257\214\214\214lllIII!!!\260\37\37\37\6""1\"\33"
+ "I)\27b,\22|1\14\2276\7\255;\3\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\214\272=\0\4\300M\24\317\201[\341\274\251\360\355\352\247\362\362\362\1"
+ "\241~m\212\37\37\37\1\271\271\271\213\362\362\362\6\324\324\324\261\261\261"
+ "\221\221\221qqqIII&&&\260\37\37\37\6+!\35B'\30]+\23y1\15\2175\11\2469\4\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\206\272=\0\1\350\322\310\252\362"
+ "\362\362\1\241~m\212\37\37\37\1\264\264\264\221\362\362\362\5\327\327\327"
+ "\261\261\261\205\205\205[[[&&&\261\37\37\37\6$\40\36?&\31X*\24o/\17\2104"
+ "\12\2378\5\377\272=\0\377\272=\0\377\272=\0\377\272=\0\1\350\322\310\252"
+ "\362\362\362\1\241~m\212\37\37\37\1\264\264\264\225\362\362\362\3\353\353"
+ "\353\261\261\261ggg\266\37\37\37\6:%\32Q)\25h-\20\2002\14\2347\6\265<\1\377"
+ "\272=\0\377\272=\0\377\272=\0\370\272=\0\1\350\322\310\252\362\362\362\1"
+ "\241~m\212\37\37\37\1\264\264\264\230\362\362\362\3\322\322\322sss$$$\271"
+ "\37\37\37\6""1\"\33I)\27b,\22|1\14\2276\7\255;\3\377\272=\0\377\272=\0\377"
+ "\272=\0\362\272=\0\1\350\322\310\252\362\362\362\1\241~m\212\37\37\37\1\264"
+ "\264\264\232\362\362\362\2\334\334\334xxx\276\37\37\37\6+!\35B'\30]+\23w"
+ "0\15\2175\11\2469\4\377\272=\0\377\272=\0\377\272=\0\354\272=\0\1\350\322"
+ "\310\252\362\362\362\1\241~m\212\37\37\37\1\264\264\264\234\362\362\362\2"
+ "\266\266\266000\302\37\37\37\6$\40\36?&\31X*\24o/\17\2104\12\2378\5\377\272"
+ "=\0\377\272=\0\377\272=\0\346\272=\0\1\350\322\310\252\362\362\362\1\240"
+ "\200n\212\37\37\37\1\264\264\264\235\362\362\362\2\334\334\334QQQ\227\37"
+ "\37\37\3VVVsss\200\200\200\202\236\236\236\202sss\1SSS\251\37\37\37\6:%\32"
+ "Q)\25h-\20~2\14\2347\6\265<\1\377\272=\0\377\272=\0\377\272=\0\337\272=\0"
+ "\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37\1\264\264\264\236\362"
+ "\362\362\2\360\360\360vvv\223\37\37\37\3GGG\243\243\243\341\341\341\210\362"
+ "\362\362\4\350\350\350\264\264\264}}}...\253\37\37\37\6""1\"\33I)\27`,\22"
+ "|1\14\2276\7\255;\3\377\272=\0\377\272=\0\377\272=\0\331\272=\0\1\350\322"
+ "\310\252\362\362\362\1\234~n\212\37\37\37\1\264\264\264\240\362\362\362\1"
+ "qqq\220\37\37\37\3+++\231\231\231\360\360\360\215\362\362\362\3\350\350\350"
+ "\217\217\217...\257\37\37\37\3+!\35@%\30\253:\3\377\272=\0\377\272=\0\377"
+ "\272=\0\326\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37\1\254"
+ "\254\254\240\362\362\362\2\360\360\360[[[\216\37\37\37\2???\331\331\331\221"
+ "\362\362\362\2\350\350\350sss\260\37\37\37\1\2378\5\377\272=\0\377\272=\0"
+ "\377\272=\0\326\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37"
+ "\1\250\250\250\241\362\362\362\2\346\346\346:::\214\37\37\37\2===\341\341"
+ "\341\224\362\362\362\2\240\240\240&&&\256\37\37\37\1\2378\5\377\272=\0\377"
+ "\272=\0\377\272=\0\326\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37"
+ "\37\37\1\250\250\250\242\362\362\362\1\300\300\300\213\37\37\37\2$$$\317"
+ "\317\317\226\362\362\362\2\305\305\305+++\255\37\37\37\1\2378\5\377\272="
+ "\0\377\272=\0\377\272=\0\326\272=\0\1\350\322\310\252\362\362\362\1\234~"
+ "n\212\37\37\37\1\250\250\250\212\362\362\362\3\322\322\322\310\310\310\350"
+ "\350\350\226\362\362\362\1vvv\212\37\37\37\1\217\217\217\230\362\362\362"
+ "\2\261\261\261!!!\224\37\37\37\1:::\227\37\37\37\1\2378\5\377\272=\0\377"
+ "\272=\0\377\272=\0\326\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37"
+ "\37\37\1\250\250\250\212\362\362\362\1\217\217\217\202\37\37\37\7+++IIIl"
+ "ll\205\205\205\236\236\236\305\305\305\343\343\343\217\362\362\362\2\336"
+ "\336\336)))\210\37\37\37\2:::\355\355\355\231\362\362\362\1\221\221\221\217"
+ "\37\37\37\4===\207\207\207\303\303\303\360\360\360\204\362\362\362\4\317"
+ "\317\317\252\252\252xxxBBB\221\37\37\37\1\2378\5\377\272=\0\377\272=\0\377"
+ "\272=\0\326\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37\1\250"
+ "\250\250\212\362\362\362\1\217\217\217\211\37\37\37\3:::iii\264\264\264\215"
+ "\362\362\362\1\202\202\202\210\37\37\37\1\231\231\231\233\362\362\362\1X"
+ "XX\214\37\37\37\2???\273\273\273\214\362\362\362\3\315\315\315}}}...\216"
+ "\37\37\37\1\2469\4\377\272=\0\377\272=\0\377\272=\0\326\272=\0\1\350\322"
+ "\310\252\362\362\362\1\234~n\212\37\37\37\1\250\250\250\212\362\362\362\1"
+ "\217\217\217\214\37\37\37\2""555\250\250\250\213\362\362\362\2\336\336\336"
+ "$$$\206\37\37\37\2&&&\350\350\350\233\362\362\362\2\312\312\312!!!\212\37"
+ "\37\37\2\200\200\200\355\355\355\217\362\362\362\3\346\346\346\202\202\202"
+ "!!!\214\37\37\37\1\250:\4\377\272=\0\377\272=\0\377\272=\0\326\272=\0\1\350"
+ "\322\310\252\362\362\362\1\234~n\212\37\37\37\1\250\250\250\212\362\362\362"
+ "\1\217\217\217\216\37\37\37\2```\353\353\353\212\362\362\362\1eee\206\37"
+ "\37\37\1ggg\235\362\362\362\1lll\211\37\37\37\1\214\214\214\223\362\362\362"
+ "\2\303\303\303888\213\37\37\37\1\250:\4\377\272=\0\377\272=\0\377\272=\0"
+ "\326\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37\1\250\250"
+ "\250\212\362\362\362\1\217\217\217\217\37\37\37\2VVV\355\355\355\211\362"
+ "\362\362\1\264\264\264\206\37\37\37\1\245\245\245\235\362\362\362\2\327\327"
+ "\327!!!\207\37\37\37\1\202\202\202\225\362\362\362\2\343\343\343:::\212\37"
+ "\37\37\1\250:\4\377\272=\0\377\272=\0\377\272=\0\326\272=\0\1\350\322\310"
+ "\252\362\362\362\1\234~n\212\37\37\37\1\236\236\236\212\362\362\362\1\217"
+ "\217\217\220\37\37\37\1\205\205\205\211\362\362\362\2\353\353\353)))\205"
+ "\37\37\37\1\327\327\327\215\362\362\362\3\322\322\322\310\310\310\315\315"
+ "\315\216\362\362\362\1XXX\206\37\37\37\2DDD\355\355\355\225\362\362\362\2"
+ "\353\353\353&&&\212\37\37\37\1\250:\4\377\272=\0\377\272=\0\377\272=\0\326"
+ "\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37\1\233\233\233"
+ "\212\362\362\362\1\217\217\217\220\37\37\37\2$$$\322\322\322\211\362\362"
+ "\362\1```\204\37\37\37\1...\213\362\362\362\3\327\327\327eee!!!\203\37\37"
+ "\37\4)))[[[\236\236\236\355\355\355\212\362\362\362\1\250\250\250\206\37"
+ "\37\37\1\310\310\310\226\362\362\362\1\266\266\266\213\37\37\37\1\250:\4"
+ "\377\272=\0\377\272=\0\377\272=\0\326\272=\0\1\350\322\310\252\362\362\362"
+ "\1\234~n\212\37\37\37\1\233\233\233\212\362\362\362\1\217\217\217\221\37"
+ "\37\37\1}}}\211\362\362\362\1\224\224\224\204\37\37\37\1SSS\212\362\362\362"
+ "\2\322\322\322333\210\37\37\37\2""888\271\271\271\211\362\362\362\2\343\343"
+ "\343!!!\204\37\37\37\1SSS\227\362\362\362\1\202\202\202\213\37\37\37\1\250"
+ ":\4\377\272=\0\377\272=\0\377\272=\0\326\272=\0\1\350\322\310\252\362\362"
+ "\362\1\234~n\212\37\37\37\1\233\233\233\212\362\362\362\1\217\217\217\221"
+ "\37\37\37\1""000\211\362\362\362\1\303\303\303\204\37\37\37\1qqq\212\362"
+ "\362\362\1QQQ\212\37\37\37\2$$$\264\264\264\211\362\362\362\1LLL\204\37\37"
+ "\37\1\257\257\257\227\362\362\362\1GGG\213\37\37\37\1\250:\4\377\272=\0\377"
+ "\272=\0\377\272=\0\326\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37"
+ "\37\37\1\233\233\233\212\362\362\362\1\217\217\217\222\37\37\37\1\324\324"
+ "\324\210\362\362\362\1\350\350\350\204\37\37\37\1\205\205\205\211\362\362"
+ "\362\1\266\266\266\214\37\37\37\2""555\346\346\346\210\362\362\362\1{{{\203"
+ "\37\37\37\2...\360\360\360\226\362\362\362\2\343\343\343!!!\213\37\37\37"
+ "\1\260;\2\377\272=\0\377\272=\0\377\272=\0\326\272=\0\1\350\322\310\252\362"
+ "\362\362\1\234~n\212\37\37\37\1\233\233\233\212\362\362\362\1\217\217\217"
+ "\222\37\37\37\1\254\254\254\211\362\362\362\1""888\203\37\37\37\1\226\226"
+ "\226\211\362\362\362\1vvv\215\37\37\37\1\205\205\205\210\362\362\362\1\245"
+ "\245\245\203\37\37\37\1```\213\362\362\362\6\341\341\341\271\271\271\236"
+ "\236\236\252\252\252\310\310\310\360\360\360\206\362\362\362\1\252\252\252"
+ "\214\37\37\37\1\262<\2\377\272=\0\377\272=\0\377\272=\0\326\272=\0\1\350"
+ "\322\310\252\362\362\362\1\234~n\212\37\37\37\1\233\233\233\212\362\362\362"
+ "\1\217\217\217\222\37\37\37\1\221\221\221\211\362\362\362\1QQQ\203\37\37"
+ "\37\1\236\236\236\211\362\362\362\1BBB\215\37\37\37\1""888\210\362\362\362"
+ "\1\303\303\303\203\37\37\37\1\212\212\212\211\362\362\362\2\341\341\341i"
+ "ii\206\37\37\37\3VVV\233\233\233\346\346\346\203\362\362\362\1vvv\214\37"
+ "\37\37\1\262<\2\377\272=\0\377\272=\0\377\272=\0\326\272=\0\1\350\322\310"
+ "\252\362\362\362\1\234~n\212\37\37\37\1\233\233\233\212\362\362\362\1\217"
+ "\217\217\222\37\37\37\1\205\205\205\211\362\362\362\1eee\203\37\37\37\1\250"
+ "\250\250\210\362\362\362\2\355\355\355!!!\216\37\37\37\1\322\322\322\207"
+ "\362\362\362\1\336\336\336\203\37\37\37\1\250\250\250\210\362\362\362\2\341"
+ "\341\341000\211\37\37\37\5...\233\233\233\360\360\360\362\362\362:::\214"
+ "\37\37\37\1\262<\2\377\272=\0\377\272=\0\377\272=\0\326\272=\0\1\350\322"
+ "\310\252\362\362\362\1\234~n\212\37\37\37\1\233\233\233\212\362\362\362\1"
+ "\217\217\217\222\37\37\37\1xxx\211\362\362\362\1vvv\203\37\37\37\1\250\250"
+ "\250\210\362\362\362\1\327\327\327\217\37\37\37\1\254\254\254\207\362\362"
+ "\362\1\360\360\360\203\37\37\37\1\273\273\273\210\362\362\362\1iii\214\37"
+ "\37\37\2???\233\233\233\215\37\37\37\1\262<\2\377\272=\0\377\272=\0\377\272"
+ "=\0\326\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37\1\221\221"
+ "\221\212\362\362\362\1\226\226\226\222\37\37\37\1vvv\211\362\362\362\1\202"
+ "\202\202\203\37\37\37\1\250\250\250\210\362\362\362\1\305\305\305\217\37"
+ "\37\37\1\212\212\212\210\362\362\362\1+++\202\37\37\37\1\300\300\300\207"
+ "\362\362\362\2\346\346\346!!!\233\37\37\37\1\262<\2\377\272=\0\377\272=\0"
+ "\377\272=\0\326\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37"
+ "\1\217\217\217\212\362\362\362\1\233\233\233\222\37\37\37\1vvv\211\362\362"
+ "\362\1\202\202\202\203\37\37\37\1\250\250\250\210\362\362\362\1\266\266\266"
+ "\217\37\37\37\1vvv\210\362\362\362\1""555\202\37\37\37\1\300\300\300\207"
+ "\362\362\362\1\310\310\310\234\37\37\37\1\262<\2\377\272=\0\377\272=\0\377"
+ "\272=\0\326\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37\1\217"
+ "\217\217\212\362\362\362\1\233\233\233\222\37\37\37\1vvv\211\362\362\362"
+ "\1\212\212\212\203\37\37\37\1\250\250\250\210\362\362\362\1\264\264\264\217"
+ "\37\37\37\1ggg\210\362\362\362\1""888\202\37\37\37\1\266\266\266\207\362"
+ "\362\362\1\305\305\305\234\37\37\37\1\262<\2\377\272=\0\377\272=\0\377\272"
+ "=\0\326\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37\1\217\217"
+ "\217\212\362\362\362\1\233\233\233\222\37\37\37\1vvv\211\362\362\362\1\202"
+ "\202\202\203\37\37\37\1\250\250\250\210\362\362\362\1\250\250\250\217\37"
+ "\37\37\1]]]\210\362\362\362\1""888\202\37\37\37\1\250\250\250\207\362\362"
+ "\362\2\346\346\346!!!\233\37\37\37\1\267=\1\377\272=\0\377\272=\0\377\272"
+ "=\0\326\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37\1\217\217"
+ "\217\212\362\362\362\1\233\233\233\222\37\37\37\1vvv\211\362\362\362\1\202"
+ "\202\202\203\37\37\37\1\257\257\257\210\362\362\362\1\250\250\250\217\37"
+ "\37\37\1]]]\210\362\362\362\1""888\202\37\37\37\1\217\217\217\210\362\362"
+ "\362\1]]]\233\37\37\37\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350"
+ "\322\310\252\362\362\362\1\234~n\212\37\37\37\1\217\217\217\212\362\362\362"
+ "\1\233\233\233\222\37\37\37\1vvv\211\362\362\362\1\202\202\202\203\37\37"
+ "\37\1\264\264\264\210\362\362\362\1\250\250\250\217\37\37\37\1]]]\210\362"
+ "\362\362\1""888\202\37\37\37\1iii\210\362\362\362\2\310\310\310!!!\232\37"
+ "\37\37\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362"
+ "\362\362\1\234~n\212\37\37\37\1\217\217\217\212\362\362\362\1\233\233\233"
+ "\222\37\37\37\1vvv\211\362\362\362\1\202\202\202\203\37\37\37\1\264\264\264"
+ "\210\362\362\362\1\250\250\250\217\37\37\37\1]]]\210\362\362\362\1""000\202"
+ "\37\37\37\1BBB\211\362\362\362\1\221\221\221\232\37\37\37\377\272=\0\377"
+ "\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37"
+ "\37\37\1\217\217\217\212\362\362\362\1\233\233\233\222\37\37\37\1vvv\211"
+ "\362\362\362\1\202\202\202\203\37\37\37\1\264\264\264\210\362\362\362\1\250"
+ "\250\250\217\37\37\37\1]]]\210\362\362\362\1+++\203\37\37\37\1\331\331\331"
+ "\211\362\362\362\1xxx\231\37\37\37\377\272=\0\377\272=\0\377\272=\0\327\272"
+ "=\0\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37\1\217\217\217\212"
+ "\362\362\362\1\233\233\233\222\37\37\37\1vvv\211\362\362\362\1\202\202\202"
+ "\203\37\37\37\1\264\264\264\210\362\362\362\1\250\250\250\217\37\37\37\1"
+ "]]]\210\362\362\362\1+++\203\37\37\37\1\212\212\212\212\362\362\362\1\212"
+ "\212\212\230\37\37\37\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322"
+ "\310\252\362\362\362\1\234~n\212\37\37\37\1\205\205\205\212\362\362\362\1"
+ "\233\233\233\222\37\37\37\1vvv\211\362\362\362\1\202\202\202\203\37\37\37"
+ "\1\264\264\264\210\362\362\362\1\250\250\250\217\37\37\37\1]]]\210\362\362"
+ "\362\1+++\203\37\37\37\2""555\360\360\360\212\362\362\362\2\264\264\264."
+ "..\226\37\37\37\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310"
+ "\252\362\362\362\1\234~n\212\37\37\37\1\202\202\202\212\362\362\362\1\233"
+ "\233\233\222\37\37\37\1vvv\211\362\362\362\1\202\202\202\203\37\37\37\1\264"
+ "\264\264\210\362\362\362\1\243\243\243\217\37\37\37\1ggg\210\362\362\362"
+ "\1+++\204\37\37\37\1\252\252\252\213\362\362\362\2\331\331\331NNN\225\37"
+ "\37\37\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362"
+ "\362\362\1\234~n\212\37\37\37\1\202\202\202\212\362\362\362\1\233\233\233"
+ "\222\37\37\37\1vvv\211\362\362\362\1\202\202\202\203\37\37\37\1\264\264\264"
+ "\210\362\362\362\1\233\233\233\217\37\37\37\1iii\210\362\362\362\1!!!\204"
+ "\37\37\37\2""555\350\350\350\213\362\362\362\2\355\355\355\202\202\202\223"
+ "\37\37\37\1$\40\36\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322"
+ "\310\252\362\362\362\1\234~n\212\37\37\37\1\202\202\202\212\362\362\362\1"
+ "\233\233\233\222\37\37\37\1vvv\211\362\362\362\1\202\202\202\203\37\37\37"
+ "\1\264\264\264\210\362\362\362\1\233\233\233\217\37\37\37\1iii\210\362\362"
+ "\362\206\37\37\37\1{{{\215\362\362\362\2\264\264\264...\221\37\37\37\1)!"
+ "\35\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362"
+ "\362\1\234~n\212\37\37\37\1\202\202\202\212\362\362\362\1\233\233\233\222"
+ "\37\37\37\1vvv\211\362\362\362\1\202\202\202\203\37\37\37\1\264\264\264\210"
+ "\362\362\362\1\233\233\233\217\37\37\37\1iii\210\362\362\362\207\37\37\37"
+ "\1\254\254\254\215\362\362\362\2\331\331\331NNN\220\37\37\37\1)!\35\377\272"
+ "=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\234"
+ "~n\212\37\37\37\1\202\202\202\212\362\362\362\1\233\233\233\222\37\37\37"
+ "\1vvv\211\362\362\362\1\202\202\202\203\37\37\37\1\264\264\264\210\362\362"
+ "\362\1\233\233\233\217\37\37\37\1iii\210\362\362\362\207\37\37\37\2&&&\305"
+ "\305\305\215\362\362\362\2\355\355\355nnn\217\37\37\37\1)!\35\377\272=\0"
+ "\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\234~n\212"
+ "\37\37\37\1\202\202\202\212\362\362\362\1\245\245\245\222\37\37\37\1vvv\211"
+ "\362\362\362\1\202\202\202\203\37\37\37\1\264\264\264\210\362\362\362\1\233"
+ "\233\233\217\37\37\37\1iii\210\362\362\362\210\37\37\37\2+++\276\276\276"
+ "\216\362\362\362\1\207\207\207\216\37\37\37\1)!\35\377\272=\0\377\272=\0"
+ "\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37\37\37"
+ "\1\202\202\202\212\362\362\362\1\250\250\250\222\37\37\37\1vvv\211\362\362"
+ "\362\1\202\202\202\203\37\37\37\1\264\264\264\210\362\362\362\1\233\233\233"
+ "\217\37\37\37\1qqq\207\362\362\362\1\350\350\350\211\37\37\37\2&&&\254\254"
+ "\254\216\362\362\362\2\243\243\243!!!\214\37\37\37\1)!\35\377\272=\0\377"
+ "\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\234~n\212\37"
+ "\37\37\1\202\202\202\212\362\362\362\1\250\250\250\222\37\37\37\1vvv\211"
+ "\362\362\362\1\202\202\202\203\37\37\37\1\264\264\264\210\362\362\362\1\233"
+ "\233\233\217\37\37\37\1vvv\207\362\362\362\1\346\346\346\213\37\37\37\2v"
+ "vv\353\353\353\215\362\362\362\2\266\266\266&&&\213\37\37\37\1)!\35\377\272"
+ "=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\234"
+ "~n\212\37\37\37\1vvv\212\362\362\362\1\250\250\250\222\37\37\37\1vvv\211"
+ "\362\362\362\1\202\202\202\203\37\37\37\1\264\264\264\210\362\362\362\1\233"
+ "\233\233\217\37\37\37\1vvv\207\362\362\362\1\346\346\346\214\37\37\37\2B"
+ "BB\317\317\317\215\362\362\362\2\261\261\261!!!\212\37\37\37\1)!\35\377\272"
+ "=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\234"
+ "~n\212\37\37\37\1vvv\212\362\362\362\1\250\250\250\222\37\37\37\1vvv\211"
+ "\362\362\362\1\202\202\202\203\37\37\37\1\264\264\264\210\362\362\362\1\233"
+ "\233\233\217\37\37\37\1vvv\207\362\362\362\1\346\346\346\215\37\37\37\2&"
+ "&&\231\231\231\215\362\362\362\1\233\233\233\212\37\37\37\1,\"\34\377\272"
+ "=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\227"
+ "|o\212\37\37\37\1vvv\212\362\362\362\1\250\250\250\222\37\37\37\1vvv\211"
+ "\362\362\362\1xxx\203\37\37\37\1\276\276\276\210\362\362\362\1\233\233\233"
+ "\217\37\37\37\1vvv\207\362\362\362\1\346\346\346\217\37\37\37\2[[[\334\334"
+ "\334\214\362\362\362\1lll\211\37\37\37\1""1\"\33\377\272=\0\377\272=\0\377"
+ "\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\226}p\212\37\37\37\1v"
+ "vv\212\362\362\362\1\250\250\250\222\37\37\37\1vvv\211\362\362\362\1vvv\203"
+ "\37\37\37\1\300\300\300\210\362\362\362\1\217\217\217\217\37\37\37\1vvv\207"
+ "\362\362\362\1\331\331\331\220\37\37\37\2+++\250\250\250\213\362\362\362"
+ "\2\346\346\346:::\210\37\37\37\1""1\"\33\377\272=\0\377\272=\0\377\272=\0"
+ "\327\272=\0\1\350\322\310\252\362\362\362\1\226}p\212\37\37\37\1vvv\212\362"
+ "\362\362\1\250\250\250\222\37\37\37\1vvv\211\362\362\362\1vvv\203\37\37\37"
+ "\1\300\300\300\210\362\362\362\1\217\217\217\217\37\37\37\1}}}\207\362\362"
+ "\362\1\331\331\331\222\37\37\37\2[[[\343\343\343\212\362\362\362\1\264\264"
+ "\264\210\37\37\37\1""1\"\33\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1"
+ "\350\322\310\252\362\362\362\1\226}p\212\37\37\37\1vvv\212\362\362\362\1"
+ "\250\250\250\222\37\37\37\1vvv\211\362\362\362\1vvv\203\37\37\37\1\300\300"
+ "\300\210\362\362\362\1\217\217\217\217\37\37\37\1\202\202\202\207\362\362"
+ "\362\1\331\331\331\223\37\37\37\2""888\312\312\312\212\362\362\362\1SSS\207"
+ "\37\37\37\1""1\"\33\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322"
+ "\310\252\362\362\362\1\226}p\212\37\37\37\1vvv\212\362\362\362\1\250\250"
+ "\250\222\37\37\37\1vvv\211\362\362\362\1vvv\203\37\37\37\1\300\300\300\210"
+ "\362\362\362\1\217\217\217\217\37\37\37\1\202\202\202\207\362\362\362\1\331"
+ "\331\331\224\37\37\37\2&&&\273\273\273\211\362\362\362\1\300\300\300\207"
+ "\37\37\37\1""1\"\33\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322"
+ "\310\252\362\362\362\1\226}p\212\37\37\37\1vvv\212\362\362\362\1\250\250"
+ "\250\222\37\37\37\1vvv\211\362\362\362\1vvv\203\37\37\37\1\300\300\300\210"
+ "\362\362\362\1\217\217\217\217\37\37\37\1\202\202\202\207\362\362\362\1\331"
+ "\331\331\225\37\37\37\2&&&\266\266\266\211\362\362\362\1GGG\206\37\37\37"
+ "\1""1\"\33\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252"
+ "\362\362\362\1\226}p\212\37\37\37\1vvv\212\362\362\362\1\250\250\250\222"
+ "\37\37\37\1vvv\211\362\362\362\1vvv\203\37\37\37\1\300\300\300\210\362\362"
+ "\362\1\217\217\217\217\37\37\37\1\202\202\202\207\362\362\362\1\315\315\315"
+ "\226\37\37\37\2+++\322\322\322\210\362\362\362\1\224\224\224\206\37\37\37"
+ "\1""1\"\33\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252"
+ "\362\362\362\1\226}p\212\37\37\37\1iii\212\362\362\362\1\250\250\250\222"
+ "\37\37\37\1vvv\211\362\362\362\1vvv\203\37\37\37\1\300\300\300\210\362\362"
+ "\362\1\217\217\217\217\37\37\37\1\202\202\202\207\362\362\362\1\315\315\315"
+ "\227\37\37\37\2BBB\355\355\355\207\362\362\362\2\334\334\334!!!\205\37\37"
+ "\37\1""3#\33\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252"
+ "\362\362\362\1\226}p\212\37\37\37\1iii\212\362\362\362\1\250\250\250\222"
+ "\37\37\37\1vvv\211\362\362\362\1vvv\203\37\37\37\1\300\300\300\210\362\362"
+ "\362\1\217\217\217\217\37\37\37\1\207\207\207\207\362\362\362\1\315\315\315"
+ "\230\37\37\37\1\231\231\231\210\362\362\362\1DDD\205\37\37\37\1:%\32\377"
+ "\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1"
+ "\226}p\212\37\37\37\1iii\212\362\362\362\1\250\250\250\222\37\37\37\1vvv"
+ "\211\362\362\362\1vvv\203\37\37\37\1\300\300\300\210\362\362\362\1\217\217"
+ "\217\217\37\37\37\1\217\217\217\207\362\362\362\1\315\315\315\230\37\37\37"
+ "\2""555\353\353\353\207\362\362\362\1vvv\205\37\37\37\1:%\32\377\272=\0\377"
+ "\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\226}p\212\37"
+ "\37\37\1iii\212\362\362\362\1\261\261\261\222\37\37\37\1vvv\211\362\362\362"
+ "\1vvv\203\37\37\37\1\300\300\300\210\362\362\362\1\217\217\217\217\37\37"
+ "\37\1\217\217\217\207\362\362\362\1\312\312\312\231\37\37\37\1\252\252\252"
+ "\207\362\362\362\1\233\233\233\205\37\37\37\1:%\32\377\272=\0\377\272=\0"
+ "\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\226}p\212\37\37\37"
+ "\1iii\212\362\362\362\1\264\264\264\222\37\37\37\1vvv\211\362\362\362\1s"
+ "ss\203\37\37\37\1\300\300\300\210\362\362\362\1\217\217\217\217\37\37\37"
+ "\1\217\217\217\207\362\362\362\1\300\300\300\231\37\37\37\1lll\207\362\362"
+ "\362\1\271\271\271\205\37\37\37\1:%\32\377\272=\0\377\272=\0\377\272=\0\327"
+ "\272=\0\1\350\322\310\252\362\362\362\1\226}p\212\37\37\37\1iii\212\362\362"
+ "\362\1\264\264\264\222\37\37\37\1{{{\211\362\362\362\1iii\203\37\37\37\1"
+ "\300\300\300\210\362\362\362\1\221\221\221\217\37\37\37\1\221\221\221\207"
+ "\362\362\362\1\300\300\300\231\37\37\37\1===\207\362\362\362\1\312\312\312"
+ "\205\37\37\37\1:%\32\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322"
+ "\310\252\362\362\362\1\230\177r\212\37\37\37\1iii\212\362\362\362\1\264\264"
+ "\264\222\37\37\37\1\207\207\207\211\362\362\362\1bbb\203\37\37\37\1\300\300"
+ "\300\210\362\362\362\1\236\236\236\217\37\37\37\1\240\240\240\207\362\362"
+ "\362\1\300\300\300\231\37\37\37\2!!!\355\355\355\206\362\362\362\1\331\331"
+ "\331\205\37\37\37\1:%\32\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350"
+ "\322\310\252\362\362\362\1\237\210}\212\37\37\37\1iii\212\362\362\362\1\264"
+ "\264\264\222\37\37\37\1\245\245\245\211\362\362\362\1VVV\203\37\37\37\1\266"
+ "\266\266\210\362\362\362\1\266\266\266\217\37\37\37\1\271\271\271\207\362"
+ "\362\362\1\276\276\276\204\37\37\37\7???XXXsss\236\236\236\266\266\266\317"
+ "\317\317888\217\37\37\37\1\341\341\341\206\362\362\362\1\334\334\334\205"
+ "\37\37\37\1:%\32\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310"
+ "\252\362\362\362\1\237\210}\212\37\37\37\1iii\212\362\362\362\1\264\264\264"
+ "\222\37\37\37\1\322\322\322\211\362\362\362\1===\203\37\37\37\1\261\261\261"
+ "\210\362\362\362\1\336\336\336\217\37\37\37\1\343\343\343\207\362\362\362"
+ "\4\264\264\264\37\37\37lll\317\317\317\207\362\362\362\1SSS\217\37\37\37"
+ "\1\331\331\331\206\362\362\362\1\331\331\331\205\37\37\37\1=$\31\377\272"
+ "=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\237"
+ "\210}\212\37\37\37\1]]]\212\362\362\362\1\264\264\264\221\37\37\37\1GGG\211"
+ "\362\362\362\2\360\360\360$$$\203\37\37\37\1\233\233\233\211\362\362\362"
+ "\1III\215\37\37\37\1GGG\210\362\362\362\3\250\250\250\37\37\37sss\210\362"
+ "\362\362\1xxx\217\37\37\37\1\346\346\346\206\362\362\362\1\322\322\322\205"
+ "\37\37\37\1D&\30\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310"
+ "\252\362\362\362\1\237\210}\212\37\37\37\1]]]\212\362\362\362\1\264\264\264"
+ "\221\37\37\37\1\257\257\257\211\362\362\362\1\322\322\322\204\37\37\37\1"
+ "\200\200\200\211\362\362\362\1\226\226\226\215\37\37\37\1\224\224\224\210"
+ "\362\362\362\3\231\231\231\37\37\37```\210\362\362\362\1\264\264\264\216"
+ "\37\37\37\1""333\207\362\362\362\1\305\305\305\205\37\37\37\1D&\30\377\272"
+ "=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\237"
+ "\210}\212\37\37\37\1]]]\212\362\362\362\1\264\264\264\220\37\37\37\1vvv\212"
+ "\362\362\362\1\250\250\250\204\37\37\37\1eee\211\362\362\362\2\350\350\350"
+ "555\213\37\37\37\2""555\350\350\350\210\362\362\362\3}}}\37\37\37BBB\210"
+ "\362\362\362\2\355\355\355333\215\37\37\37\1nnn\207\362\362\362\1\254\254"
+ "\254\205\37\37\37\1D&\30\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350"
+ "\322\310\252\362\362\362\1\237\210}\212\37\37\37\1]]]\212\362\362\362\1\264"
+ "\264\264\216\37\37\37\2)))\214\214\214\213\362\362\362\1vvv\204\37\37\37"
+ "\1""555\212\362\362\362\2\273\273\273$$$\212\37\37\37\1\257\257\257\211\362"
+ "\362\362\4```\37\37\37!!!\350\350\350\210\362\362\362\1\224\224\224\215\37"
+ "\37\37\1\305\305\305\207\362\362\362\1\217\217\217\205\37\37\37\1D&\30\377"
+ "\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1"
+ "\237\210}\212\37\37\37\1]]]\212\362\362\362\1\300\300\300\204III\1+++\205"
+ "\37\37\37\5:::III```\226\226\226\343\343\343\214\362\362\362\1""888\205\37"
+ "\37\37\1\331\331\331\212\362\362\362\2\261\261\261+++\207\37\37\37\2$$$\233"
+ "\233\233\212\362\362\362\1""333\202\37\37\37\1\264\264\264\210\362\362\362"
+ "\2\360\360\360NNN\213\37\37\37\1qqq\210\362\362\362\1iii\205\37\37\37\1D"
+ "&\30\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362"
+ "\362\1\237\210}\212\37\37\37\1]]]\245\362\362\362\1\303\303\303\206\37\37"
+ "\37\1\233\233\233\213\362\362\362\3\334\334\334}}}000\203\37\37\37\3&&&g"
+ "gg\315\315\315\212\362\362\362\1\327\327\327\203\37\37\37\1vvv\211\362\362"
+ "\362\2\341\341\341GGG\211\37\37\37\2[[[\353\353\353\210\362\362\362\1""8"
+ "88\205\37\37\37\1D&\30\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350"
+ "\322\310\252\362\362\362\1\237\210}\212\37\37\37\1]]]\245\362\362\362\1l"
+ "ll\206\37\37\37\1QQQ\216\362\362\362\3\350\350\350\310\310\310\327\327\327"
+ "\215\362\362\362\1\221\221\221\203\37\37\37\2""333\360\360\360\211\362\362"
+ "\362\3\350\350\350{{{$$$\205\37\37\37\3+++\221\221\221\360\360\360\210\362"
+ "\362\362\1\322\322\322\206\37\37\37\1D&\30\377\272=\0\377\272=\0\377\272"
+ "=\0\327\272=\0\1\350\322\310\252\362\362\362\1\237\210}\212\37\37\37\1]]"
+ "]\244\362\362\362\2\324\324\324$$$\207\37\37\37\1\317\317\317\235\362\362"
+ "\362\1LLL\204\37\37\37\1\261\261\261\213\362\362\362\7\346\346\346\250\250"
+ "\250\212\212\212vvv\236\236\236\261\261\261\350\350\350\212\362\362\362\1"
+ "\202\202\202\206\37\37\37\1D&\30\377\272=\0\377\272=\0\377\272=\0\327\272"
+ "=\0\1\350\322\310\252\362\362\362\1\237\210}\212\37\37\37\1[[[\244\362\362"
+ "\362\1iii\210\37\37\37\1lll\234\362\362\362\1\303\303\303\205\37\37\37\1"
+ "LLL\233\362\362\362\2\360\360\360555\206\37\37\37\1N(\26\377\272=\0\377\272"
+ "=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\237\210}\212\37"
+ "\37\37\1QQQ\243\362\362\362\1\252\252\252\211\37\37\37\2!!!\312\312\312\233"
+ "\362\362\362\1]]]\206\37\37\37\1\245\245\245\232\362\362\362\1\254\254\254"
+ "\207\37\37\37\1N(\26\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322"
+ "\310\252\362\362\362\1\237\210}\212\37\37\37\1QQQ\242\362\362\362\2\317\317"
+ "\317+++\212\37\37\37\2GGG\355\355\355\231\362\362\362\1\271\271\271\207\37"
+ "\37\37\2...\341\341\341\230\362\362\362\2\350\350\350555\207\37\37\37\1N"
+ "(\26\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362"
+ "\362\1\237\210}\212\37\37\37\1QQQ\241\362\362\362\2\317\317\317555\214\37"
+ "\37\37\1sss\230\362\362\362\2\331\331\331000\210\37\37\37\1bbb\230\362\362"
+ "\362\1\202\202\202\210\37\37\37\1N(\26\377\272=\0\377\272=\0\377\272=\0\327"
+ "\272=\0\1\350\322\310\252\362\362\362\1\237\210}\212\37\37\37\1QQQ\240\362"
+ "\362\362\2\240\240\240&&&\216\37\37\37\1\212\212\212\226\362\362\362\2\353"
+ "\353\353DDD\212\37\37\37\1xxx\226\362\362\362\2\273\273\273$$$\210\37\37"
+ "\37\1N(\26\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252"
+ "\362\362\362\1\237\210}\212\37\37\37\1QQQ\236\362\362\362\2\334\334\334b"
+ "bb\221\37\37\37\2sss\353\353\353\223\362\362\362\2\350\350\350XXX\214\37"
+ "\37\37\1}}}\224\362\362\362\2\305\305\305+++\211\37\37\37\1N(\26\377\272"
+ "=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\237"
+ "\210}\212\37\37\37\1QQQ\234\362\362\362\2\273\273\273bbb\224\37\37\37\2G"
+ "GG\324\324\324\221\362\362\362\2\315\315\315BBB\216\37\37\37\2bbb\341\341"
+ "\341\221\362\362\362\2\254\254\254)))\212\37\37\37\1N(\26\377\272=\0\377"
+ "\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\237\210}\212"
+ "\37\37\37\1GGG\202\310\310\310\1\343\343\343\223\362\362\362\6\341\341\341"
+ "\310\310\310\273\273\273\217\217\217bbb333\227\37\37\37\3&&&sss\327\327\327"
+ "\215\362\362\362\3\327\327\327xxx$$$\220\37\37\37\2""000\250\250\250\216"
+ "\362\362\362\2\331\331\331ggg\214\37\37\37\1N(\26\377\272=\0\377\272=\0\377"
+ "\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\237\210}\301\37\37\37"
+ "\3bbb\236\236\236\327\327\327\207\362\362\362\4\334\334\334\252\252\252g"
+ "gg!!!\224\37\37\37\3DDD\233\233\233\336\336\336\210\362\362\362\4\346\346"
+ "\346\250\250\250```$$$\215\37\37\37\1V*\24\377\272=\0\377\272=\0\377\272"
+ "=\0\327\272=\0\1\350\322\310\252\362\362\362\1\237\210}\304\37\37\37\7""8"
+ "88III[[[sssbbbIII===\233\37\37\37\5BBBLLLsss\202\202\202\224\224\224\202"
+ "sss\2III$$$\220\37\37\37\1V*\24\377\272=\0\377\272=\0\377\272=\0\327\272"
+ "=\0\1\350\322\310\252\362\362\362\1\237\210}\377\37\37\37\1V*\24\377\272"
+ "=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\237"
+ "\210}\377\37\37\37\1V*\24\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350"
+ "\322\310\252\362\362\362\1\237\210}\377\37\37\37\1V*\24\377\272=\0\377\272"
+ "=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\237\210}\377\37"
+ "\37\37\1V*\24\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252"
+ "\362\362\362\1\237\210}\377\37\37\37\1V*\24\377\272=\0\377\272=\0\377\272"
+ "=\0\327\272=\0\1\350\322\310\252\362\362\362\1\237\210}\377\37\37\37\1V*"
+ "\24\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362"
+ "\362\1\235\207}\377\37\37\37\1]+\23\377\272=\0\377\272=\0\377\272=\0\327"
+ "\272=\0\1\350\322\310\252\362\362\362\1\233\211\177\377\37\37\37\1`,\22\377"
+ "\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1"
+ "\233\211\177\377\37\37\37\1`,\22\377\272=\0\377\272=\0\377\272=\0\327\272"
+ "=\0\1\350\322\310\252\362\362\362\1\233\211\177\302\37\37\37\4!!![[[sss\207"
+ "\207\207\203\236\236\236\3\212\212\212sssSSS\252\37\37\37\1BBB\202III\1$"
+ "$$\205\37\37\37\1`,\22\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350"
+ "\322\310\252\362\362\362\1\233\211\177\277\37\37\37\4$$$iii\254\254\254\346"
+ "\346\346\211\362\362\362\3\336\336\336\233\233\233III\212\37\37\37\1nnn\202"
+ "\236\236\236\1\221\221\221\230\37\37\37\1sss\202\362\362\362\2\324\324\324"
+ "!!!\205\37\37\37\1`,\22\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350"
+ "\322\310\252\362\362\362\1\233\211\177\227\37\37\37\1...\211III\1)))\234"
+ "\37\37\37\2qqq\334\334\334\217\362\362\362\2\231\231\231+++\210\37\37\37"
+ "\1sss\203\362\362\362\1XXX\226\37\37\37\2!!!\317\317\317\202\362\362\362"
+ "\1vvv\206\37\37\37\1`,\22\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350"
+ "\322\310\252\362\362\362\1\233\211\177\212\37\37\37\1""333\202\310\310\310"
+ "\1\324\324\324\225\362\362\362\3\303\303\303\214\214\214XXX\226\37\37\37"
+ "\2""333\273\273\273\222\362\362\362\2\336\336\336GGG\207\37\37\37\2$$$\336"
+ "\336\336\202\362\362\362\1\266\266\266\226\37\37\37\1```\202\362\362\362"
+ "\2\341\341\341&&&\206\37\37\37\1`,\22\377\272=\0\377\272=\0\377\272=\0\327"
+ "\272=\0\1\350\322\310\252\362\362\362\1\233\211\177\212\37\37\37\1""888\233"
+ "\362\362\362\3\322\322\322nnn!!!\222\37\37\37\2BBB\331\331\331\207\362\362"
+ "\362\2\322\322\322\305\305\305\203\236\236\236\2\300\300\300\331\331\331"
+ "\206\362\362\362\2\341\341\341DDD\207\37\37\37\1\205\205\205\203\362\362"
+ "\362\1GGG\225\37\37\37\1\276\276\276\202\362\362\362\1\214\214\214\207\37"
+ "\37\37\1`,\22\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252"
+ "\362\362\362\1\233\211\177\212\37\37\37\1""888\235\362\362\362\2\327\327"
+ "\327VVV\220\37\37\37\2===\336\336\336\205\362\362\362\3\310\310\310\200\200"
+ "\200888\207\37\37\37\3===\202\202\202\334\334\334\204\362\362\362\2\341\341"
+ "\341:::\206\37\37\37\2+++\350\350\350\202\362\362\362\1\250\250\250\224\37"
+ "\37\37\1NNN\202\362\362\362\2\353\353\353333\207\37\37\37\1e-\21\377\272"
+ "=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\233"
+ "\211\177\212\37\37\37\1""888\237\362\362\362\1\202\202\202\216\37\37\37\2"
+ "&&&\317\317\317\204\362\362\362\2\336\336\336lll\214\37\37\37\2&&&\214\214"
+ "\214\204\362\362\362\1\276\276\276\207\37\37\37\1\226\226\226\202\362\362"
+ "\362\2\360\360\360888\223\37\37\37\1\252\252\252\202\362\362\362\1\236\236"
+ "\236\210\37\37\37\1h-\20\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350"
+ "\322\310\252\362\362\362\1\233\211\177\212\37\37\37\1""888\240\362\362\362"
+ "\1\231\231\231\215\37\37\37\1\233\233\233\204\362\362\362\2\300\300\300+"
+ "++\217\37\37\37\2qqq\360\360\360\203\362\362\362\1iii\206\37\37\37\2""88"
+ "8\360\360\360\202\362\362\362\1\231\231\231\222\37\37\37\2===\360\360\360"
+ "\202\362\362\362\1BBB\210\37\37\37\1h-\20\377\272=\0\377\272=\0\377\272="
+ "\0\327\272=\0\1\350\322\310\252\362\362\362\1\233\211\177\212\37\37\37\1"
+ """555\241\362\362\362\1}}}\213\37\37\37\2GGG\360\360\360\203\362\362\362"
+ "\2\271\271\271&&&\221\37\37\37\1sss\203\362\362\362\2\324\324\324!!!\206"
+ "\37\37\37\1\250\250\250\202\362\362\362\2\350\350\350000\221\37\37\37\1\231"
+ "\231\231\202\362\362\362\1\266\266\266\211\37\37\37\1h-\20\377\272=\0\377"
+ "\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\233\211\177"
+ "\212\37\37\37\1+++\241\362\362\362\2\360\360\360NNN\212\37\37\37\1\266\266"
+ "\266\203\362\362\362\2\327\327\327)))\223\37\37\37\1\266\266\266\203\362"
+ "\362\362\1]]]\206\37\37\37\1GGG\203\362\362\362\1\207\207\207\220\37\37\37"
+ "\2""000\353\353\353\202\362\362\362\1SSS\211\37\37\37\1h-\20\377\272=\0\377"
+ "\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\233\211\177"
+ "\212\37\37\37\1+++\242\362\362\362\1\276\276\276\211\37\37\37\1GGG\203\362"
+ "\362\362\2\360\360\360SSS\224\37\37\37\2""555\355\355\355\202\362\362\362"
+ "\1\250\250\250\207\37\37\37\1\273\273\273\202\362\362\362\2\341\341\341&"
+ "&&\217\37\37\37\1\205\205\205\202\362\362\362\1\312\312\312\212\37\37\37"
+ "\1h-\20\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362"
+ "\362\362\1\233\211\177\212\37\37\37\1+++\243\362\362\362\1XXX\210\37\37\37"
+ "\1\224\224\224\203\362\362\362\1\240\240\240\226\37\37\37\1\243\243\243\202"
+ "\362\362\362\2\346\346\346!!!\206\37\37\37\1[[[\203\362\362\362\1vvv\216"
+ "\37\37\37\2&&&\341\341\341\202\362\362\362\1lll\212\37\37\37\1h-\20\377\272"
+ "=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\233"
+ "\211\177\212\37\37\37\1+++\243\362\362\362\1\300\300\300\207\37\37\37\2!"
+ "!!\336\336\336\202\362\362\362\2\360\360\360===\226\37\37\37\1SSS\203\362"
+ "\362\362\1LLL\207\37\37\37\1\315\315\315\202\362\362\362\2\324\324\324!!"
+ "!\215\37\37\37\1sss\202\362\362\362\2\331\331\331!!!\212\37\37\37\1h-\20"
+ "\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362"
+ "\1\233\211\177\212\37\37\37\1+++\244\362\362\362\1???\206\37\37\37\1DDD\203"
+ "\362\362\362\1\273\273\273\227\37\37\37\2!!!\346\346\346\202\362\362\362"
+ "\1{{{\207\37\37\37\1lll\203\362\362\362\1ggg\214\37\37\37\2!!!\322\322\322"
+ "\202\362\362\362\1\200\200\200\213\37\37\37\1m.\17\377\272=\0\377\272=\0"
+ "\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\233\211\177\212\37"
+ "\37\37\1+++\214\362\362\362\1\331\331\331\206\310\310\310\1\355\355\355\220"
+ "\362\362\362\1\202\202\202\206\37\37\37\1\200\200\200\203\362\362\362\1{"
+ "{{\230\37\37\37\1\271\271\271\202\362\362\362\1\236\236\236\207\37\37\37"
+ "\2$$$\331\331\331\202\362\362\362\1\310\310\310\214\37\37\37\1```\202\362"
+ "\362\362\2\350\350\350+++\213\37\37\37\1q/\17\377\272=\0\377\272=\0\377\272"
+ "=\0\327\272=\0\1\350\322\310\252\362\362\362\1\233\211\177\212\37\37\37\1"
+ "+++\212\362\362\362\1\331\331\331\211\37\37\37\3$$$```\276\276\276\215\362"
+ "\362\362\1\261\261\261\206\37\37\37\1\250\250\250\203\362\362\362\1DDD\230"
+ "\37\37\37\1\221\221\221\202\362\362\362\1\273\273\273\210\37\37\37\1\200"
+ "\200\200\203\362\362\362\1VVV\213\37\37\37\1\303\303\303\202\362\362\362"
+ "\1\224\224\224\214\37\37\37\1q/\17\377\272=\0\377\272=\0\377\272=\0\327\272"
+ "=\0\1\350\322\310\252\362\362\362\1\233\211\177\212\37\37\37\1+++\212\362"
+ "\362\362\1\331\331\331\214\37\37\37\1xxx\214\362\362\362\1\336\336\336\206"
+ "\37\37\37\1\315\315\315\202\362\362\362\2\353\353\353!!!\230\37\37\37\1{"
+ "{{\202\362\362\362\1\317\317\317\210\37\37\37\2)))\346\346\346\202\362\362"
+ "\362\1\266\266\266\212\37\37\37\1NNN\202\362\362\362\2\360\360\360888\214"
+ "\37\37\37\1q/\17\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310"
+ "\252\362\362\362\1\233\211\177\212\37\37\37\1)))\212\362\362\362\1\331\331"
+ "\331\215\37\37\37\1\224\224\224\214\362\362\362\1)))\205\37\37\37\1\353\353"
+ "\353\202\362\362\362\1\312\312\312\231\37\37\37\1ggg\202\362\362\362\1\341"
+ "\341\341\211\37\37\37\1\221\221\221\203\362\362\362\1DDD\211\37\37\37\1\257"
+ "\257\257\202\362\362\362\1\252\252\252\215\37\37\37\1q/\17\377\272=\0\377"
+ "\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\233\211\177"
+ "\213\37\37\37\212\362\362\362\1\331\331\331\215\37\37\37\2+++\350\350\350"
+ "\213\362\362\362\1???\204\37\37\37\1+++\203\362\362\362\1\264\264\264\231"
+ "\37\37\37\1]]]\202\362\362\362\1\346\346\346\211\37\37\37\2""333\355\355"
+ "\355\202\362\362\362\1\245\245\245\210\37\37\37\2===\360\360\360\202\362"
+ "\362\362\1III\215\37\37\37\1q/\17\377\272=\0\377\272=\0\377\272=\0\327\272"
+ "=\0\1\350\322\310\252\362\362\362\1\233\211\177\213\37\37\37\212\362\362"
+ "\362\1\331\331\331\216\37\37\37\1\233\233\233\213\362\362\362\1III\204\37"
+ "\37\37\1???\203\362\362\362\1\243\243\243\231\37\37\37\1]]]\202\362\362\362"
+ "\1\350\350\350\212\37\37\37\1\243\243\243\202\362\362\362\2\360\360\3608"
+ "88\207\37\37\37\1\233\233\233\202\362\362\362\1\276\276\276\216\37\37\37"
+ "\1q/\17\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362"
+ "\362\362\1\233\211\177\213\37\37\37\212\362\362\362\1\331\331\331\216\37"
+ "\37\37\1nnn\213\362\362\362\1QQQ\204\37\37\37\1GGG\203\362\362\362\1\233"
+ "\233\233\231\37\37\37\1]]]\202\362\362\362\1\346\346\346\212\37\37\37\1B"
+ "BB\203\362\362\362\1\226\226\226\206\37\37\37\2""000\353\353\353\202\362"
+ "\362\362\1```\216\37\37\37\1q/\17\377\272=\0\377\272=\0\377\272=\0\327\272"
+ "=\0\1\350\322\310\252\362\362\362\1\233\211\177\213\37\37\37\212\362\362"
+ "\362\1\331\331\331\216\37\37\37\1VVV\213\362\362\362\1LLL\204\37\37\37\1"
+ "QQQ\203\362\362\362\1\233\233\233\231\37\37\37\1]]]\202\362\362\362\1\346"
+ "\346\346\213\37\37\37\1\266\266\266\202\362\362\362\2\350\350\350...\205"
+ "\37\37\37\1\212\212\212\202\362\362\362\2\322\322\322!!!\216\37\37\37\1v"
+ "0\16\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362"
+ "\362\1\233\211\177\213\37\37\37\212\362\362\362\1\331\331\331\216\37\37\37"
+ "\1III\213\362\362\362\1BBB\204\37\37\37\1QQQ\203\362\362\362\1\221\221\221"
+ "\231\37\37\37\1]]]\202\362\362\362\1\346\346\346\213\37\37\37\1SSS\203\362"
+ "\362\362\1\205\205\205\204\37\37\37\2&&&\341\341\341\202\362\362\362\1ss"
+ "s\217\37\37\37\1{1\15\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322"
+ "\310\252\362\362\362\1\233\211\177\213\37\37\37\212\362\362\362\1\331\331"
+ "\331\216\37\37\37\1DDD\213\362\362\362\1""888\204\37\37\37\1QQQ\203\362\362"
+ "\362\1\217\217\217\231\37\37\37\1ggg\202\362\362\362\1\346\346\346\214\37"
+ "\37\37\1\310\310\310\202\362\362\362\2\336\336\336&&&\203\37\37\37\1vvv\202"
+ "\362\362\362\2\341\341\341&&&\217\37\37\37\1{1\15\377\272=\0\377\272=\0\377"
+ "\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\233\211\177\213\37\37"
+ "\37\212\362\362\362\1\331\331\331\216\37\37\37\1III\212\362\362\362\2\360"
+ "\360\360$$$\204\37\37\37\1QQQ\203\362\362\362\1\217\217\217\231\37\37\37"
+ "\1iii\202\362\362\362\1\346\346\346\214\37\37\37\1ggg\203\362\362\362\1s"
+ "ss\202\37\37\37\2!!!\324\324\324\202\362\362\362\1\214\214\214\220\37\37"
+ "\37\1{1\15\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252"
+ "\362\362\362\1\233\211\177\213\37\37\37\212\362\362\362\1\331\331\331\216"
+ "\37\37\37\1]]]\212\362\362\362\1\327\327\327\205\37\37\37\1QQQ\203\362\362"
+ "\362\1\217\217\217\231\37\37\37\1iii\202\362\362\362\1\331\331\331\214\37"
+ "\37\37\2!!!\327\327\327\202\362\362\362\4\324\324\324!!!\37\37\37bbb\202"
+ "\362\362\362\2\353\353\353000\220\37\37\37\1{1\15\377\272=\0\377\272=\0\377"
+ "\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\233\211\177\213\37\37"
+ "\37\1\360\360\360\211\362\362\362\1\346\346\346\216\37\37\37\1}}}\212\362"
+ "\362\362\1\252\252\252\205\37\37\37\1[[[\203\362\362\362\1\217\217\217\231"
+ "\37\37\37\1iii\202\362\362\362\1\331\331\331\215\37\37\37\1xxx\203\362\362"
+ "\362\3eee\37\37\37\303\303\303\202\362\362\362\1\236\236\236\221\37\37\37"
+ "\1{1\15\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362"
+ "\362\362\1\233\211\177\213\37\37\37\1\346\346\346\211\362\362\362\1\346\346"
+ "\346\216\37\37\37\1\261\261\261\212\362\362\362\1vvv\205\37\37\37\1]]]\203"
+ "\362\362\362\1\217\217\217\231\37\37\37\1iii\202\362\362\362\1\331\331\331"
+ "\215\37\37\37\2)))\341\341\341\202\362\362\362\2\305\305\305QQQ\203\362\362"
+ "\362\1BBB\221\37\37\37\1{1\15\377\272=\0\377\272=\0\377\272=\0\327\272=\0"
+ "\1\350\322\310\252\362\362\362\1\233\211\177\213\37\37\37\1\346\346\346\211"
+ "\362\362\362\1\346\346\346\215\37\37\37\2""333\353\353\353\211\362\362\362"
+ "\2\360\360\360555\205\37\37\37\1]]]\203\362\362\362\1\217\217\217\231\37"
+ "\37\37\1nnn\202\362\362\362\1\331\331\331\216\37\37\37\1\214\214\214\203"
+ "\362\362\362\1\324\324\324\202\362\362\362\1\261\261\261\222\37\37\37\1{"
+ "1\15\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362"
+ "\362\1\233\211\177\213\37\37\37\1\346\346\346\211\362\362\362\1\346\346\346"
+ "\215\37\37\37\1\217\217\217\212\362\362\362\1\271\271\271\206\37\37\37\1"
+ "]]]\203\362\362\362\1\217\217\217\231\37\37\37\1vvv\202\362\362\362\1\331"
+ "\331\331\216\37\37\37\2""000\353\353\353\205\362\362\362\1SSS\222\37\37\37"
+ "\1~2\14\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362"
+ "\362\362\1\233\211\177\213\37\37\37\1\346\346\346\211\362\362\362\1\346\346"
+ "\346\214\37\37\37\2XXX\360\360\360\212\362\362\362\1VVV\206\37\37\37\1]]"
+ "]\203\362\362\362\1\217\217\217\231\37\37\37\1vvv\202\362\362\362\1\317\317"
+ "\317\217\37\37\37\1\236\236\236\204\362\362\362\1\312\312\312\223\37\37\37"
+ "\1\2033\13\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252"
+ "\362\362\362\1\233\211\177\213\37\37\37\1\346\346\346\211\362\362\362\1\346"
+ "\346\346\213\37\37\37\2```\346\346\346\212\362\362\362\1\271\271\271\207"
+ "\37\37\37\1]]]\203\362\362\362\1\217\217\217\231\37\37\37\1vvv\202\362\362"
+ "\362\1\315\315\315\217\37\37\37\2===\360\360\360\203\362\362\362\1iii\223"
+ "\37\37\37\1\2033\13\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322"
+ "\310\252\362\362\362\1\233\211\177\213\37\37\37\1\346\346\346\211\362\362"
+ "\362\1\346\346\346\211\37\37\37\2III\252\252\252\213\362\362\362\2\346\346"
+ "\346:::\207\37\37\37\1]]]\203\362\362\362\1\217\217\217\231\37\37\37\1vv"
+ "v\202\362\362\362\1\315\315\315\220\37\37\37\1\276\276\276\203\362\362\362"
+ "\1:::\223\37\37\37\1\2033\13\377\272=\0\377\272=\0\377\272=\0\327\272=\0"
+ "\1\350\322\310\252\362\362\362\1\233\211\177\213\37\37\37\1\346\346\346\211"
+ "\362\362\362\1\346\346\346\203\37\37\37\6&&&IIIggg\217\217\217\264\264\264"
+ "\334\334\334\215\362\362\362\1iii\210\37\37\37\1]]]\203\362\362\362\1\217"
+ "\217\217\231\37\37\37\1vvv\202\362\362\362\1\315\315\315\217\37\37\37\2""0"
+ "00\350\350\350\203\362\362\362\1\233\233\233\223\37\37\37\1\2033\13\377\272"
+ "=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\233"
+ "\211\177\213\37\37\37\1\346\346\346\211\362\362\362\3\360\360\360\310\310"
+ "\310\341\341\341\223\362\362\362\1}}}\211\37\37\37\1]]]\203\362\362\362\1"
+ "\207\207\207\231\37\37\37\1\202\202\202\202\362\362\362\1\315\315\315\217"
+ "\37\37\37\1\212\212\212\204\362\362\362\2\353\353\353333\222\37\37\37\1\203"
+ "3\13\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362"
+ "\362\1\233\211\177\213\37\37\37\1\346\346\346\236\362\362\362\1\226\226\226"
+ "\212\37\37\37\1]]]\203\362\362\362\1\202\202\202\231\37\37\37\1\202\202\202"
+ "\202\362\362\362\1\305\305\305\216\37\37\37\2)))\343\343\343\205\362\362"
+ "\362\1\217\217\217\222\37\37\37\1\2033\13\377\272=\0\377\272=\0\377\272="
+ "\0\327\272=\0\1\350\322\310\252\362\362\362\1\233\211\177\213\37\37\37\1"
+ "\331\331\331\236\362\362\362\3\353\353\353\221\221\221)))\210\37\37\37\1"
+ "]]]\203\362\362\362\1\202\202\202\231\37\37\37\1\202\202\202\202\362\362"
+ "\362\1\300\300\300\216\37\37\37\1}}}\203\362\362\362\1\346\346\346\202\362"
+ "\362\362\2\350\350\350+++\221\37\37\37\1\2033\13\377\272=\0\377\272=\0\377"
+ "\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\233\211\177\213\37\37"
+ "\37\1\331\331\331\240\362\362\362\2\322\322\322BBB\207\37\37\37\1]]]\203"
+ "\362\362\362\1\202\202\202\231\37\37\37\1\202\202\202\202\362\362\362\1\300"
+ "\300\300\215\37\37\37\2$$$\334\334\334\202\362\362\362\2\266\266\266}}}\203"
+ "\362\362\362\1\207\207\207\221\37\37\37\1\2053\13\377\272=\0\377\272=\0\377"
+ "\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37"
+ "\37\1\331\331\331\241\362\362\362\2\346\346\346LLL\206\37\37\37\1]]]\203"
+ "\362\362\362\1\202\202\202\231\37\37\37\1\202\202\202\202\362\362\362\1\300"
+ "\300\300\215\37\37\37\1qqq\203\362\362\362\3SSS)))\341\341\341\202\362\362"
+ "\362\2\341\341\341)))\220\37\37\37\1\2155\11\377\272=\0\377\272=\0\377\272"
+ "=\0\327\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1"
+ "\331\331\331\242\362\362\362\2\346\346\346BBB\205\37\37\37\1]]]\203\362\362"
+ "\362\1\202\202\202\231\37\37\37\1\214\214\214\202\362\362\362\1\300\300\300"
+ "\214\37\37\37\2!!!\322\322\322\202\362\362\362\1\303\303\303\202\37\37\37"
+ "\1\207\207\207\203\362\362\362\1}}}\220\37\37\37\1\2155\11\377\272=\0\377"
+ "\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\225\206\177"
+ "\213\37\37\37\1\331\331\331\243\362\362\362\2\317\317\317$$$\204\37\37\37"
+ "\1]]]\203\362\362\362\1\202\202\202\231\37\37\37\1\217\217\217\202\362\362"
+ "\362\1\273\273\273\214\37\37\37\1eee\203\362\362\362\1bbb\202\37\37\37\2"
+ "+++\346\346\346\202\362\362\362\2\334\334\334$$$\217\37\37\37\1\2155\11\377"
+ "\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1"
+ "\225\206\177\213\37\37\37\1\331\331\331\244\362\362\362\1\217\217\217\204"
+ "\37\37\37\1eee\203\362\362\362\1\202\202\202\231\37\37\37\1\217\217\217\202"
+ "\362\362\362\1\264\264\264\214\37\37\37\1\310\310\310\202\362\362\362\2\317"
+ "\317\317!!!\203\37\37\37\1\212\212\212\203\362\362\362\1sss\217\37\37\37"
+ "\1\2155\11\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252"
+ "\362\362\362\1\225\206\177\213\37\37\37\1\331\331\331\244\362\362\362\2\350"
+ "\350\350000\203\37\37\37\1iii\203\362\362\362\1\202\202\202\231\37\37\37"
+ "\1\217\217\217\202\362\362\362\1\264\264\264\213\37\37\37\1XXX\203\362\362"
+ "\362\1nnn\204\37\37\37\2...\350\350\350\202\362\362\362\2\324\324\324!!!"
+ "\216\37\37\37\1\2155\11\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350"
+ "\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1\331\331\331\245\362"
+ "\362\362\1\221\221\221\203\37\37\37\1iii\203\362\362\362\1\202\202\202\231"
+ "\37\37\37\1\217\217\217\202\362\362\362\1\264\264\264\213\37\37\37\1\273"
+ "\273\273\202\362\362\362\2\331\331\331$$$\205\37\37\37\1\224\224\224\203"
+ "\362\362\362\1iii\216\37\37\37\1\2155\11\377\272=\0\377\272=\0\377\272=\0"
+ "\327\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1\331"
+ "\331\331\221\362\362\362\1\353\353\353\202\310\310\310\1\324\324\324\220"
+ "\362\362\362\2\341\341\341!!!\202\37\37\37\1iii\203\362\362\362\1\202\202"
+ "\202\231\37\37\37\1\224\224\224\202\362\362\362\1\264\264\264\212\37\37\37"
+ "\1LLL\203\362\362\362\1{{{\206\37\37\37\2""333\353\353\353\202\362\362\362"
+ "\1\315\315\315\216\37\37\37\1\2155\11\377\272=\0\377\272=\0\377\272=\0\327"
+ "\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1\315\315"
+ "\315\211\362\362\362\7\355\355\355\236\236\236\224\224\224sssbbbIII000\206"
+ "\37\37\37\3LLL\207\207\207\341\341\341\215\362\362\362\1QQQ\202\37\37\37"
+ "\1iii\203\362\362\362\1\202\202\202\231\37\37\37\1\233\233\233\202\362\362"
+ "\362\1\261\261\261\212\37\37\37\1\257\257\257\202\362\362\362\2\341\341\341"
+ ")))\207\37\37\37\1\226\226\226\203\362\362\362\1```\215\37\37\37\1\2175\11"
+ "\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362"
+ "\1\225\206\177\213\37\37\37\1\315\315\315\211\362\362\362\1\353\353\353\216"
+ "\37\37\37\2+++\254\254\254\214\362\362\362\1\214\214\214\202\37\37\37\1i"
+ "ii\203\362\362\362\1}}}\231\37\37\37\1\233\233\233\202\362\362\362\1\250"
+ "\250\250\211\37\37\37\1BBB\203\362\362\362\1\207\207\207\210\37\37\37\2""5"
+ "55\355\355\355\202\362\362\362\1\305\305\305\215\37\37\37\1\2276\7\377\272"
+ "=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\225"
+ "\206\177\213\37\37\37\1\315\315\315\212\362\362\362\220\37\37\37\1\250\250"
+ "\250\213\362\362\362\1\264\264\264\202\37\37\37\1iii\203\362\362\362\1vv"
+ "v\231\37\37\37\1\233\233\233\202\362\362\362\1\250\250\250\211\37\37\37\1"
+ "\245\245\245\202\362\362\362\2\350\350\350000\211\37\37\37\1\240\240\240"
+ "\203\362\362\362\1VVV\214\37\37\37\1\2276\7\377\272=\0\377\272=\0\377\272"
+ "=\0\327\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1"
+ "\315\315\315\212\362\362\362\220\37\37\37\2)))\343\343\343\212\362\362\362"
+ "\1\336\336\336\202\37\37\37\1iii\203\362\362\362\1vvv\231\37\37\37\1\233"
+ "\233\233\202\362\362\362\1\250\250\250\210\37\37\37\2""888\360\360\360\202"
+ "\362\362\362\1\226\226\226\212\37\37\37\2:::\360\360\360\202\362\362\362"
+ "\1\273\273\273\214\37\37\37\1\2276\7\377\272=\0\377\272=\0\377\272=\0\327"
+ "\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1\315\315"
+ "\315\212\362\362\362\221\37\37\37\1\207\207\207\213\362\362\362\3+++\37\37"
+ "\37iii\203\362\362\362\1vvv\231\37\37\37\1\233\233\233\202\362\362\362\1"
+ "\250\250\250\210\37\37\37\1\231\231\231\202\362\362\362\2\355\355\355888"
+ "\213\37\37\37\1\243\243\243\203\362\362\362\1LLL\213\37\37\37\1\2276\7\377"
+ "\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1"
+ "\225\206\177\213\37\37\37\1\315\315\315\212\362\362\362\221\37\37\37\1DD"
+ "D\213\362\362\362\3???\37\37\37iii\203\362\362\362\1vvv\231\37\37\37\1\250"
+ "\250\250\202\362\362\362\1\240\240\240\207\37\37\37\2""000\353\353\353\202"
+ "\362\362\362\1\243\243\243\214\37\37\37\2???\360\360\360\202\362\362\362"
+ "\1\257\257\257\213\37\37\37\1\2276\7\377\272=\0\377\272=\0\377\272=\0\327"
+ "\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1\315\315"
+ "\315\212\362\362\362\221\37\37\37\2!!!\346\346\346\212\362\362\362\3NNN\37"
+ "\37\37iii\203\362\362\362\1vvv\231\37\37\37\1\254\254\254\202\362\362\362"
+ "\1\226\226\226\207\37\37\37\1\214\214\214\202\362\362\362\2\360\360\360B"
+ "BB\215\37\37\37\1\252\252\252\203\362\362\362\1BBB\212\37\37\37\1\2276\7"
+ "\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362"
+ "\1\225\206\177\213\37\37\37\1\315\315\315\212\362\362\362\222\37\37\37\1"
+ "\310\310\310\212\362\362\362\3QQQ\37\37\37iii\203\362\362\362\1vvv\231\37"
+ "\37\37\1\271\271\271\202\362\362\362\1\214\214\214\206\37\37\37\2)))\346"
+ "\346\346\202\362\362\362\1\257\257\257\216\37\37\37\1DDD\203\362\362\362"
+ "\1\250\250\250\212\37\37\37\1\2276\7\377\272=\0\377\272=\0\377\272=\0\327"
+ "\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1\315\315"
+ "\315\212\362\362\362\222\37\37\37\1\273\273\273\212\362\362\362\3QQQ\37\37"
+ "\37iii\203\362\362\362\1vvv\231\37\37\37\1\317\317\317\202\362\362\362\1"
+ "sss\206\37\37\37\1\200\200\200\203\362\362\362\1LLL\217\37\37\37\1\257\257"
+ "\257\202\362\362\362\2\360\360\360:::\211\37\37\37\1\2276\7\377\272=\0\377"
+ "\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\225\206\177"
+ "\213\37\37\37\1\300\300\300\212\362\362\362\222\37\37\37\1\271\271\271\212"
+ "\362\362\362\3GGG\37\37\37iii\203\362\362\362\1vvv\230\37\37\37\2!!!\350"
+ "\350\350\202\362\362\362\1XXX\205\37\37\37\2$$$\336\336\336\202\362\362\362"
+ "\1\276\276\276\220\37\37\37\1III\203\362\362\362\1\236\236\236\211\37\37"
+ "\37\1\2378\5\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252"
+ "\362\362\362\1\225\206\177\213\37\37\37\1\300\300\300\212\362\362\362\222"
+ "\37\37\37\1\300\300\300\212\362\362\362\3""888\37\37\37iii\203\362\362\362"
+ "\1\200\200\200\230\37\37\37\1DDD\203\362\362\362\1""333\205\37\37\37\1ss"
+ "s\203\362\362\362\1[[[\221\37\37\37\1\266\266\266\202\362\362\362\2\355\355"
+ "\355555\210\37\37\37\1\2378\5\377\272=\0\377\272=\0\377\272=\0\327\272=\0"
+ "\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1\300\300\300\212"
+ "\362\362\362\222\37\37\37\1\324\324\324\211\362\362\362\4\360\360\360$$$"
+ "\37\37\37```\203\362\362\362\1\217\217\217\230\37\37\37\1vvv\202\362\362"
+ "\362\1\327\327\327\205\37\37\37\2!!!\324\324\324\202\362\362\362\1\312\312"
+ "\312\222\37\37\37\1QQQ\203\362\362\362\1\224\224\224\210\37\37\37\1\2378"
+ "\5\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362"
+ "\362\1\225\206\177\213\37\37\37\1\300\300\300\212\362\362\362\221\37\37\37"
+ "\2!!!\355\355\355\211\362\362\362\1\327\327\327\202\37\37\37\1QQQ\203\362"
+ "\362\362\1\250\250\250\230\37\37\37\1\271\271\271\202\362\362\362\1\250\250"
+ "\250\205\37\37\37\1ggg\203\362\362\362\1ggg\223\37\37\37\1\273\273\273\202"
+ "\362\362\362\2\350\350\350...\207\37\37\37\1\2378\5\377\272=\0\377\272=\0"
+ "\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37"
+ "\37\37\1\300\300\300\212\362\362\362\221\37\37\37\1GGG\212\362\362\362\1"
+ "\264\264\264\202\37\37\37\1:::\203\362\362\362\1\327\327\327\227\37\37\37"
+ "\1GGG\203\362\362\362\1```\205\37\37\37\1\312\312\312\202\362\362\362\2\324"
+ "\324\324!!!\223\37\37\37\1VVV\203\362\362\362\1\212\212\212\207\37\37\37"
+ "\1\2378\5\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362"
+ "\362\362\1\225\206\177\213\37\37\37\1\300\300\300\212\362\362\362\1!!!\220"
+ "\37\37\37\1\202\202\202\212\362\362\362\1\205\205\205\202\37\37\37\2!!!\346"
+ "\346\346\203\362\362\362\1""555\226\37\37\37\1\245\245\245\202\362\362\362"
+ "\2\336\336\336&&&\204\37\37\37\1[[[\203\362\362\362\1sss\225\37\37\37\1\303"
+ "\303\303\202\362\362\362\2\343\343\343)))\206\37\37\37\1\2378\5\377\272="
+ "\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\225\206"
+ "\177\213\37\37\37\1\300\300\300\212\362\362\362\1+++\220\37\37\37\1\317\317"
+ "\317\212\362\362\362\1QQQ\203\37\37\37\1\264\264\264\203\362\362\362\1ss"
+ "s\225\37\37\37\2GGG\355\355\355\202\362\362\362\1\202\202\202\205\37\37\37"
+ "\1\276\276\276\202\362\362\362\2\336\336\336$$$\225\37\37\37\1]]]\203\362"
+ "\362\362\1\200\200\200\206\37\37\37\1\2378\5\377\272=\0\377\272=\0\377\272"
+ "=\0\327\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1"
+ "\300\300\300\212\362\362\362\1+++\217\37\37\37\1iii\212\362\362\362\2\343"
+ "\343\343!!!\203\37\37\37\1\202\202\202\203\362\362\362\2\324\324\324!!!\223"
+ "\37\37\37\2&&&\317\317\317\202\362\362\362\2\346\346\346+++\204\37\37\37"
+ "\1NNN\203\362\362\362\1\200\200\200\227\37\37\37\4\310\310\310\362\362\362"
+ "\310\310\310\212\212\212\206\37\37\37\1\2378\5\377\272=\0\377\272=\0\377"
+ "\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37"
+ "\37\1\300\300\300\212\362\362\362\1+++\216\37\37\37\2""555\336\336\336\212"
+ "\362\362\362\1\240\240\240\204\37\37\37\1QQQ\204\362\362\362\1ggg\222\37"
+ "\37\37\2!!!\245\245\245\203\362\362\362\1\214\214\214\205\37\37\37\1\261"
+ "\261\261\202\362\362\362\2\346\346\346+++\227\37\37\37\1""555\211\37\37\37"
+ "\1\250:\4\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362"
+ "\362\362\1\225\206\177\213\37\37\37\1\264\264\264\212\362\362\362\1+++\215"
+ "\37\37\37\2""888\317\317\317\213\362\362\362\1LLL\205\37\37\37\1\322\322"
+ "\322\203\362\362\362\2\346\346\346:::\220\37\37\37\2$$$\252\252\252\203\362"
+ "\362\362\2\322\322\322$$$\204\37\37\37\1DDD\203\362\362\362\1\217\217\217"
+ "\242\37\37\37\1\250:\4\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350"
+ "\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1\264\264\264\212\362"
+ "\362\362\1+++\214\37\37\37\2sss\350\350\350\213\362\362\362\1\300\300\300"
+ "\206\37\37\37\1sss\204\362\362\362\2\312\312\312333\216\37\37\37\2===\276"
+ "\276\276\203\362\362\362\2\353\353\353DDD\205\37\37\37\1\250\250\250\202"
+ "\362\362\362\2\353\353\353333\242\37\37\37\1\250:\4\377\272=\0\377\272=\0"
+ "\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37"
+ "\37\37\1\264\264\264\212\362\362\362\1+++\211\37\37\37\3""888\200\200\200"
+ "\327\327\327\215\362\362\362\1NNN\206\37\37\37\2!!!\322\322\322\204\362\362"
+ "\362\2\331\331\331QQQ\213\37\37\37\3...\212\212\212\353\353\353\203\362\362"
+ "\362\2\360\360\360```\205\37\37\37\2:::\360\360\360\202\362\362\362\1\233"
+ "\233\233\243\37\37\37\1\250:\4\377\272=\0\377\272=\0\377\272=\0\327\272="
+ "\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1\264\264\264"
+ "\212\362\362\362\1+++\204\37\37\37\5+++NNNsss\243\243\243\327\327\327\217"
+ "\362\362\362\1\257\257\257\210\37\37\37\2LLL\355\355\355\205\362\362\362"
+ "\3\266\266\266]]]!!!\205\37\37\37\4""000bbb\245\245\245\355\355\355\204\362"
+ "\362\362\2\355\355\355eee\206\37\37\37\5\233\233\233\362\362\362\353\353"
+ "\353\310\310\310:::\243\37\37\37\1\250:\4\377\272=\0\377\272=\0\377\272="
+ "\0\327\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1"
+ "\264\264\264\212\362\362\362\5vvv\205\205\205\240\240\240\310\310\310\343"
+ "\343\343\223\362\362\362\2\353\353\353===\211\37\37\37\1iii\207\362\362\362"
+ "\1\360\360\360\202\310\310\310\3\264\264\264\310\310\310\324\324\324\207"
+ "\362\362\362\2\334\334\334SSS\207\37\37\37\2VVV:::\246\37\37\37\1\250:\4"
+ "\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362\362"
+ "\1\225\206\177\213\37\37\37\1\264\264\264\242\362\362\362\1xxx\213\37\37"
+ "\37\2```\350\350\350\221\362\362\362\2\231\231\231)))\260\37\37\37\1\250"
+ ":\4\377\272=\0\377\272=\0\377\272=\0\327\272=\0\1\350\322\310\252\362\362"
+ "\362\1\225\206\177\213\37\37\37\1\264\264\264\241\362\362\362\1\250\250\250"
+ "\215\37\37\37\2BBB\303\303\303\215\362\362\362\3\360\360\360\252\252\252"
+ "III\260\37\37\37\3$\40\36?&\31\255;\3\377\272=\0\377\272=\0\377\272=\0\327"
+ "\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1\264\264"
+ "\264\240\362\362\362\2\257\257\257!!!\217\37\37\37\3SSS\264\264\264\350\350"
+ "\350\207\362\362\362\4\360\360\360\276\276\276\212\212\212333\254\37\37\37"
+ "\6$\40\36?&\31Y*\23t0\16\2155\11\2469\4\377\272=\0\377\272=\0\377\272=\0"
+ "\332\272=\0\1\350\322\310\252\362\362\362\1\225\206\177\213\37\37\37\1\264"
+ "\264\264\237\362\362\362\2\221\221\221!!!\223\37\37\37\1NNN\202sss\5{{{s"
+ "ssiiiIII$$$\251\37\37\37\6!\40\37?&\31Y*\23q/\17\2134\11\2449\4\377\272="
+ "\0\377\272=\0\377\272=\0\340\272=\0\1\344\304\264\252\362\362\362\1\225\206"
+ "\177\213\37\37\37\1\250\250\250\235\362\362\362\2\353\353\353lll\301\37\37"
+ "\37\5?&\31X*\24q/\17\2124\12\2439\5\377\272=\0\377\272=\0\377\272=\0\347"
+ "\272=\0\2\312pD\342\277\256\250\362\362\362\1\225\206\177\213\37\37\37\1"
+ "\250\250\250\234\362\362\362\2\305\305\305GGG\274\37\37\37\5?&\31V*\24o/"
+ "\17\2124\12\2418\5\377\272=\0\377\272=\0\377\272=\0\357\272=\0\3\302W\"\332"
+ "\244\212\357\350\345\245\362\362\362\1\225\206\177\213\37\37\37\1\250\250"
+ "\250\232\362\362\362\2\317\317\317nnn\270\37\37\37\5=$\31V*\24m.\17\2104"
+ "\12\2418\5\377\272=\0\377\272=\0\377\272=\0\367\272=\0\3\275F\13\321\211"
+ "e\351\325\313\243\362\362\362\1\225\206\177\213\37\37\37\1\250\250\250\227"
+ "\362\362\362\3\346\346\346\250\250\250XXX\264\37\37\37\5:%\32T)\24m.\17\205"
+ "3\13\2378\5\377\272=\0\377\272=\0\377\272=\0\377\272=\0\3\272=\0\310m@\340"
+ "\271\246\241\362\362\362\1\225\206\177\213\37\37\37\1\250\250\250\223\362"
+ "\362\362\5\322\322\322\257\257\257\200\200\200SSS!!!\260\37\37\37\5:%\32"
+ "S)\25l.\20\2053\13\2368\6\377\272=\0\377\272=\0\377\272=\0\377\272=\0\211"
+ "\272=\0\3\302S\34\330\240\204\356\345\342\236\362\362\362\1\225\206\177\213"
+ "\37\37\37\1\250\250\250\215\362\362\362\6\327\327\327\264\264\264\221\221"
+ "\221nnnIII!!!\257\37\37\37\5:%\32S)\25j.\20\2033\13\2368\6\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\221\272=\0\3\275D\11\320\204^\347\320\305\234"
+ "\362\362\362\1\225\207\177\213\37\37\37\1\250\250\250\207\362\362\362\6\334"
+ "\334\334\271\271\271\226\226\226sssIII&&&\257\37\37\37\5""8$\32Q)\25j.\20"
+ "\2012\13\2347\6\377\272=\0\377\272=\0\377\272=\0\377\272=\0\232\272=\0\3"
+ "\307h9\337\264\237\361\357\357\231\362\362\362\1\221\207\201\213\37\37\37"
+ "\10\250\250\250\362\362\362\341\341\341\276\276\276\233\233\233sssNNN+++"
+ "\257\37\37\37\6""5$\33N(\26h-\20\2012\13\2347\6\270=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\241\272=\0\3\277N\27\327\233}\355\342\337\227\362"
+ "\362\362\1\221\207\201\213\37\37\37\2:::000\257\37\37\37\6""5$\33N(\26g-"
+ "\21\2002\14\2347\6\270=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\251"
+ "\272=\0\3\274B\6\316\177X\346\315\300\225\362\362\362\1\221\207\201\266\37"
+ "\37\37\6""3#\33N(\26g-\21~2\14\2347\6\267=\1\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\262\272=\0\3\307d3\336\261\232\361\356\355\222\362\362\362"
+ "\1\221\207\201\260\37\37\37\6""1\"\33L(\26e-\21~2\14\2347\6\265<\1\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\272\272=\0\3\300M\24\325\225u\355\342"
+ "\333\220\362\362\362\1\221\207\201\252\37\37\37\6""1\"\33I)\27b,\22|1\14"
+ "\2347\6\265<\1\377\272=\0\377\272=\0\377\272=\0\377\272=\0\302\272=\0\3\273"
+ "@\3\316~V\346\315\300\216\362\362\362\1\221\207\201\244\37\37\37\6""0\"\34"
+ "I)\27b,\22|1\14\2317\7\263<\1\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\313\272=\0\3\307d3\336\261\232\361\356\355\213\362\362\362\1\221\207\201"
+ "\236\37\37\37\6.#\34G'\27b,\22|1\14\2317\7\262<\2\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\323\272=\0\3\300M\24\325\225u\355\342\333\211\362\362"
+ "\362\1\221\207\201\230\37\37\37\6.#\34E&\27`,\22|1\14\2276\7\262<\2\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\333\272=\0\3\273@\3\315{S\345\310\271"
+ "\207\362\362\362\1\225\213\205\222\37\37\37\6,\"\34E&\27]+\23|1\14\2276\7"
+ "\260;\2\377\272=\0\377\272=\0\377\272=\0\377\272=\0\344\272=\0\3\306`.\334"
+ "\254\224\360\355\352\204\362\362\362\1\234\221\214\214\37\37\37\6+!\35D&"
+ "\30]+\23|1\14\2246\10\255;\3\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\354\272=\0\3\277J\21\324\221p\353\336\327\202\362\362\362\1\234\221\214"
+ "\206\37\37\37\6+!\35B'\30]+\23{1\15\2246\10\253:\3\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\365\272=\0\11\314xN\344\304\264\234\221\214)!\35B"
+ "'\30]+\23y1\15\2225\10\253:\3\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\375\272=\0\1\262A\12\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377"
+ "\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0"
+ "\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272"
+ "=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\377\272=\0\346"
+ "\272=\0",
+};
+
diff --git a/src/gui/midi.cpp b/src/gui/midi.cpp
new file mode 100644
index 000000000..b5361051f
--- /dev/null
+++ b/src/gui/midi.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <string>
+#include <algorithm>
+
+#include "SDL.h"
+
+#include "dosbox.h"
+#include "midi.h"
+#include "cross.h"
+#include "support.h"
+#include "setup.h"
+#include "mapper.h"
+#include "pic.h"
+#include "hardware.h"
+#include "timer.h"
+
+#define RAWBUF 1024
+
+Bit8u MIDI_evt_len[256] = {
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x00
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x10
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x20
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x30
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x40
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x50
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x60
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x70
+
+ 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0x80
+ 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0x90
+ 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xa0
+ 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xb0
+
+ 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, // 0xc0
+ 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, // 0xd0
+
+ 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xe0
+
+ 0,2,3,2, 0,0,1,0, 1,0,1,1, 1,0,1,0 // 0xf0
+};
+
+MidiHandler * handler_list = 0;
+
+MidiHandler::MidiHandler(){
+ next = handler_list;
+ handler_list = this;
+};
+
+MidiHandler Midi_none;
+
+/* Include different midi drivers, lowest ones get checked first for default */
+
+#if defined(MACOSX)
+
+#include "midi_coremidi.h"
+#include "midi_coreaudio.h"
+
+#elif defined (WIN32)
+
+#include "midi_win32.h"
+
+#else
+
+#include "midi_oss.h"
+
+#endif
+
+#if defined (HAVE_ALSA)
+
+#include "midi_alsa.h"
+
+#endif
+
+DB_Midi midi;
+
+void MIDI_RawOutByte(Bit8u data) {
+ if (midi.sysex.start) {
+ Bit32u passed_ticks = GetTicks() - midi.sysex.start;
+ if (passed_ticks < midi.sysex.delay) SDL_Delay(midi.sysex.delay - passed_ticks);
+ }
+
+ /* Test for a realtime MIDI message */
+ if (data>=0xf8) {
+ midi.rt_buf[0]=data;
+ midi.handler->PlayMsg(midi.rt_buf);
+ return;
+ }
+ /* Test for a active sysex tranfer */
+ if (midi.status==0xf0) {
+ if (!(data&0x80)) {
+ if (midi.sysex.used<(SYSEX_SIZE-1)) midi.sysex.buf[midi.sysex.used++] = data;
+ return;
+ } else {
+ midi.sysex.buf[midi.sysex.used++] = 0xf7;
+
+ if ((midi.sysex.start) && (midi.sysex.used >= 4) && (midi.sysex.used <= 9) && (midi.sysex.buf[1] == 0x41) && (midi.sysex.buf[3] == 0x16)) {
+ LOG(LOG_ALL,LOG_ERROR)("MIDI:Skipping invalid MT-32 SysEx midi message (too short to contain a checksum)");
+ } else {
+// LOG(LOG_ALL,LOG_NORMAL)("Play sysex; address:%02X %02X %02X, length:%4d, delay:%3d", midi.sysex.buf[5], midi.sysex.buf[6], midi.sysex.buf[7], midi.sysex.used, midi.sysex.delay);
+ midi.handler->PlaySysex(midi.sysex.buf, midi.sysex.used);
+ if (midi.sysex.start) {
+ if (midi.sysex.buf[5] == 0x7F) {
+ midi.sysex.delay = 290; // All Parameters reset
+ } else if (midi.sysex.buf[5] == 0x10 && midi.sysex.buf[6] == 0x00 && midi.sysex.buf[7] == 0x04) {
+ midi.sysex.delay = 145; // Viking Child
+ } else if (midi.sysex.buf[5] == 0x10 && midi.sysex.buf[6] == 0x00 && midi.sysex.buf[7] == 0x01) {
+ midi.sysex.delay = 30; // Dark Sun 1
+ } else midi.sysex.delay = (Bitu)(((float)(midi.sysex.used) * 1.25f) * 1000.0f / 3125.0f) + 2;
+ midi.sysex.start = GetTicks();
+ }
+ }
+
+ LOG(LOG_ALL,LOG_NORMAL)("Sysex message size %d", static_cast<int>(midi.sysex.used));
+ if (CaptureState & CAPTURE_MIDI) {
+ CAPTURE_AddMidi( true, midi.sysex.used-1, &midi.sysex.buf[1]);
+ }
+ }
+ }
+ if (data&0x80) {
+ midi.status=data;
+ midi.cmd_pos=0;
+ midi.cmd_len=MIDI_evt_len[data];
+ if (midi.status==0xf0) {
+ midi.sysex.buf[0]=0xf0;
+ midi.sysex.used=1;
+ }
+ }
+ if (midi.cmd_len) {
+ midi.cmd_buf[midi.cmd_pos++]=data;
+ if (midi.cmd_pos >= midi.cmd_len) {
+ if (CaptureState & CAPTURE_MIDI) {
+ CAPTURE_AddMidi(false, midi.cmd_len, midi.cmd_buf);
+ }
+ midi.handler->PlayMsg(midi.cmd_buf);
+ midi.cmd_pos=1; //Use Running status
+ }
+ }
+}
+
+bool MIDI_Available(void) {
+ return midi.available;
+}
+
+class MIDI:public Module_base{
+public:
+ MIDI(Section* configuration):Module_base(configuration){
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+ const char * dev=section->Get_string("mididevice");
+ std::string fullconf=section->Get_string("midiconfig");
+ /* If device = "default" go for first handler that works */
+ MidiHandler * handler;
+// MAPPER_AddHandler(MIDI_SaveRawEvent,MK_f8,MMOD1|MMOD2,"caprawmidi","Cap MIDI");
+ midi.sysex.delay = 0;
+ midi.sysex.start = 0;
+ if (fullconf.find("delaysysex") != std::string::npos) {
+ midi.sysex.start = GetTicks();
+ fullconf.erase(fullconf.find("delaysysex"));
+ LOG_MSG("MIDI: Using delayed SysEx processing");
+ }
+ trim(fullconf);
+ const char * conf = fullconf.c_str();
+ midi.status=0x00;
+ midi.cmd_pos=0;
+ midi.cmd_len=0;
+ if (!strcasecmp(dev,"default")) goto getdefault;
+ handler=handler_list;
+ while (handler) {
+ if (!strcasecmp(dev,handler->GetName())) {
+ if (!handler->Open(conf)) {
+ LOG_MSG("MIDI: Can't open device:%s with config:%s.",dev,conf);
+ goto getdefault;
+ }
+ midi.handler=handler;
+ midi.available=true;
+ LOG_MSG("MIDI: Opened device:%s",handler->GetName());
+ return;
+ }
+ handler=handler->next;
+ }
+ LOG_MSG("MIDI: Can't find device:%s, finding default handler.",dev);
+getdefault:
+ handler=handler_list;
+ while (handler) {
+ if (handler->Open(conf)) {
+ midi.available=true;
+ midi.handler=handler;
+ LOG_MSG("MIDI: Opened device:%s",handler->GetName());
+ return;
+ }
+ handler=handler->next;
+ }
+ /* This shouldn't be possible */
+ }
+ ~MIDI(){
+ if(midi.available) midi.handler->Close();
+ midi.available = false;
+ midi.handler = 0;
+ }
+};
+
+
+static MIDI* test;
+void MIDI_Destroy(Section* /*sec*/){
+ delete test;
+}
+void MIDI_Init(Section * sec) {
+ test = new MIDI(sec);
+ sec->AddDestroyFunction(&MIDI_Destroy,true);
+}
diff --git a/src/gui/midi_alsa.h b/src/gui/midi_alsa.h
new file mode 100644
index 000000000..9bf4b4f9a
--- /dev/null
+++ b/src/gui/midi_alsa.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#define ALSA_PCM_OLD_HW_PARAMS_API
+#define ALSA_PCM_OLD_SW_PARAMS_API
+#include <alsa/asoundlib.h>
+#include <ctype.h>
+#include <string>
+#include <sstream>
+#define ADDR_DELIM ".:"
+
+#if ((SND_LIB_MINOR >= 6) && (SND_LIB_MAJOR == 0)) || (SND_LIB_MAJOR >= 1)
+#define snd_seq_flush_output(x) snd_seq_drain_output(x)
+#define snd_seq_set_client_group(x,name) /*nop */
+#define my_snd_seq_open(seqp) snd_seq_open(seqp, "hw", SND_SEQ_OPEN_OUTPUT, 0)
+#else
+/* SND_SEQ_OPEN_OUT causes oops on early version of ALSA */
+#define my_snd_seq_open(seqp) snd_seq_open(seqp, SND_SEQ_OPEN)
+#endif
+
+class MidiHandler_alsa : public MidiHandler {
+private:
+ snd_seq_event_t ev;
+ snd_seq_t *seq_handle;
+ int seq_client, seq_port;
+ int my_client, my_port;
+ void send_event(int do_flush) {
+ snd_seq_ev_set_direct(&ev);
+ snd_seq_ev_set_source(&ev, my_port);
+ snd_seq_ev_set_dest(&ev, seq_client, seq_port);
+
+ snd_seq_event_output(seq_handle, &ev);
+ if (do_flush)
+ snd_seq_flush_output(seq_handle);
+ }
+
+ bool parse_addr(const char *arg, int *client, int *port) {
+ std::string in(arg);
+ if(in.empty()) return false;
+
+ if(in[0] == 's' || in[0] == 'S') {
+ *client = SND_SEQ_ADDRESS_SUBSCRIBERS;
+ *port = 0;
+ return true;
+ }
+
+ if(in.find_first_of(ADDR_DELIM) == std::string::npos) return false;
+ std::istringstream inp(in);
+ int val1, val2; char c;
+ if(!(inp >> val1)) return false;
+ if(!(inp >> c )) return false;
+ if(!(inp >> val2)) return false;
+ *client = val1; *port = val2;
+ return true;
+ }
+public:
+ MidiHandler_alsa() : MidiHandler() {};
+ const char* GetName(void) { return "alsa"; }
+ void PlaySysex(Bit8u * sysex,Bitu len) {
+ snd_seq_ev_set_sysex(&ev, len, sysex);
+ send_event(1);
+ }
+
+ void PlayMsg(Bit8u * msg) {
+ ev.type = SND_SEQ_EVENT_OSS;
+
+ ev.data.raw32.d[0] = msg[0];
+ ev.data.raw32.d[1] = msg[1];
+ ev.data.raw32.d[2] = msg[2];
+
+ unsigned char chanID = msg[0] & 0x0F;
+ switch (msg[0] & 0xF0) {
+ case 0x80:
+ snd_seq_ev_set_noteoff(&ev, chanID, msg[1], msg[2]);
+ send_event(1);
+ break;
+ case 0x90:
+ snd_seq_ev_set_noteon(&ev, chanID, msg[1], msg[2]);
+ send_event(1);
+ break;
+ case 0xA0:
+ snd_seq_ev_set_keypress(&ev, chanID, msg[1], msg[2]);
+ send_event(1);
+ break;
+ case 0xB0:
+ snd_seq_ev_set_controller(&ev, chanID, msg[1], msg[2]);
+ send_event(1);
+ break;
+ case 0xC0:
+ snd_seq_ev_set_pgmchange(&ev, chanID, msg[1]);
+ send_event(0);
+ break;
+ case 0xD0:
+ snd_seq_ev_set_chanpress(&ev, chanID, msg[1]);
+ send_event(0);
+ break;
+ case 0xE0:{
+ long theBend = ((long)msg[1] + (long)(msg[2] << 7)) - 0x2000;
+ snd_seq_ev_set_pitchbend(&ev, chanID, theBend);
+ send_event(1);
+ }
+ break;
+ default:
+ //Maybe filter out FC as it leads for at least one user to crash, but the entire midi stream has not yet been checked.
+ LOG(LOG_MISC,LOG_WARN)("ALSA:Unknown Command: %02X %02X %02X", msg[0],msg[1],msg[2]);
+ send_event(1);
+ break;
+ }
+ }
+
+ void Close(void) {
+ if (seq_handle)
+ snd_seq_close(seq_handle);
+ }
+
+ bool Open(const char * conf) {
+ char var[10];
+ unsigned int caps;
+ bool defaultport = true; //try 17:0. Seems to be default nowadays
+
+ // try to use port specified in config file
+ if (conf && conf[0]) {
+ safe_strncpy(var, conf, 10);
+ if (!parse_addr(var, &seq_client, &seq_port)) {
+ LOG_MSG("ALSA:Invalid alsa port %s", var);
+ return false;
+ }
+ defaultport = false;
+ }
+ // default port if none specified
+ else if (!parse_addr("65:0", &seq_client, &seq_port)) {
+ LOG_MSG("ALSA:Invalid alsa port 65:0");
+ return false;
+ }
+
+ if (my_snd_seq_open(&seq_handle)) {
+ LOG_MSG("ALSA:Can't open sequencer");
+ return false;
+ }
+
+ my_client = snd_seq_client_id(seq_handle);
+ snd_seq_set_client_name(seq_handle, "DOSBOX");
+ snd_seq_set_client_group(seq_handle, "input");
+
+ caps = SND_SEQ_PORT_CAP_READ;
+ if (seq_client == SND_SEQ_ADDRESS_SUBSCRIBERS)
+ caps = ~SND_SEQ_PORT_CAP_SUBS_READ;
+ my_port =
+ snd_seq_create_simple_port(seq_handle, "DOSBOX", caps,
+ SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION);
+ if (my_port < 0) {
+ snd_seq_close(seq_handle);
+ LOG_MSG("ALSA:Can't create ALSA port");
+ return false;
+ }
+
+ if (seq_client != SND_SEQ_ADDRESS_SUBSCRIBERS) {
+ /* subscribe to MIDI port */
+ if (snd_seq_connect_to(seq_handle, my_port, seq_client, seq_port) < 0) {
+ if (defaultport) { //if port "65:0" (default) try "17:0" as well
+ seq_client = 17; seq_port = 0; //Update reported values
+ if(snd_seq_connect_to(seq_handle,my_port,seq_client,seq_port) < 0) { //Try 128:0 Timidity port as well
+// seq_client = 128; seq_port = 0; //Update reported values
+// if(snd_seq_connect_to(seq_handle,my_port,seq_client,seq_port) < 0) {
+ snd_seq_close(seq_handle);
+ LOG_MSG("ALSA:Can't subscribe to MIDI port (65:0) nor (17:0)");
+ return false;
+// }
+ }
+ } else {
+ snd_seq_close(seq_handle);
+ LOG_MSG("ALSA:Can't subscribe to MIDI port (%d:%d)", seq_client, seq_port);
+ return false;
+ }
+ }
+ }
+
+ LOG_MSG("ALSA:Client initialised [%d:%d]", seq_client, seq_port);
+ return true;
+ }
+
+};
+
+MidiHandler_alsa Midi_alsa;
diff --git a/src/gui/midi_coreaudio.h b/src/gui/midi_coreaudio.h
new file mode 100644
index 000000000..95d39027d
--- /dev/null
+++ b/src/gui/midi_coreaudio.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <AudioToolbox/AUGraph.h>
+#include <CoreServices/CoreServices.h>
+
+// A macro to simplify error handling a bit.
+#define RequireNoErr(error) \
+do { \
+ err = error; \
+ if (err != noErr) \
+ goto bail; \
+} while (false)
+
+// With the release of Mac OS X 10.5 in October 2007, Apple deprecated the
+// AUGraphNewNode & AUGraphGetNodeInfo APIs in favor of the new AUGraphAddNode &
+// AUGraphNodeInfo APIs. While it is easy to switch to those, it breaks
+// compatibility with all pre-10.5 systems.
+//
+// Since 10.5 was the last system to support PowerPC, we use the old, deprecated
+// APIs on PowerPC based systems by default. On all other systems (such as Mac
+// OS X running on Intel hardware, or iOS running on ARM), we use the new API by
+// default.
+//
+// This leaves Mac OS X 10.4 running on x86 processors as the only system
+// combination that this code will not support by default. It seems quite
+// reasonable to assume that anybody with an Intel system has since then moved
+// on to a newer Mac OS X release. But if for some reason you absolutely need to
+// build an x86 version of this code using the old, deprecated API, you can
+// simply do so by manually enable the USE_DEPRECATED_COREAUDIO_API switch (e.g.
+// by adding setting it suitably in CPPFLAGS).
+#if !defined(USE_DEPRECATED_COREAUDIO_API)
+# if TARGET_CPU_PPC || TARGET_CPU_PPC64
+# define USE_DEPRECATED_COREAUDIO_API 1
+# else
+# define USE_DEPRECATED_COREAUDIO_API 0
+# endif
+#endif
+
+class MidiHandler_coreaudio : public MidiHandler {
+private:
+ AUGraph m_auGraph;
+ AudioUnit m_synth;
+ const char *soundfont;
+public:
+ MidiHandler_coreaudio() : m_auGraph(0), m_synth(0) {}
+ const char * GetName(void) { return "coreaudio"; }
+ bool Open(const char * conf) {
+ OSStatus err = 0;
+
+ if (m_auGraph)
+ return false;
+
+ // Open the Music Device.
+ RequireNoErr(NewAUGraph(&m_auGraph));
+
+ AUNode outputNode, synthNode;
+ // OS X 10.5 SDK doesn't know AudioComponentDescription desc;
+#if USE_DEPRECATED_COREAUDIO_API || (MAC_OS_X_VERSION_MAX_ALLOWED <= 1050)
+ ComponentDescription desc;
+#else
+ AudioComponentDescription desc;
+#endif
+
+ // The default output device
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+#if USE_DEPRECATED_COREAUDIO_API
+ RequireNoErr(AUGraphNewNode(m_auGraph, &desc, 0, NULL, &outputNode));
+#else
+ RequireNoErr(AUGraphAddNode(m_auGraph, &desc, &outputNode));
+#endif
+
+ // The built-in default (softsynth) music device
+ desc.componentType = kAudioUnitType_MusicDevice;
+ desc.componentSubType = kAudioUnitSubType_DLSSynth;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+#if USE_DEPRECATED_COREAUDIO_API
+ RequireNoErr(AUGraphNewNode(m_auGraph, &desc, 0, NULL, &synthNode));
+#else
+ RequireNoErr(AUGraphAddNode(m_auGraph, &desc, &synthNode));
+#endif
+
+ // Connect the softsynth to the default output
+ RequireNoErr(AUGraphConnectNodeInput(m_auGraph, synthNode, 0, outputNode, 0));
+
+ // Open and initialize the whole graph
+ RequireNoErr(AUGraphOpen(m_auGraph));
+ RequireNoErr(AUGraphInitialize(m_auGraph));
+
+ // Get the music device from the graph.
+#if USE_DEPRECATED_COREAUDIO_API
+ RequireNoErr(AUGraphGetNodeInfo(m_auGraph, synthNode, NULL, NULL, NULL, &m_synth));
+#else
+ RequireNoErr(AUGraphNodeInfo(m_auGraph, synthNode, NULL, &m_synth));
+#endif
+
+ // Optionally load a soundfont
+ if (conf && conf[0]) {
+ soundfont = conf;
+ OSErr err;
+#if USE_DEPRECATED_COREAUDIO_API
+ FSRef soundfontRef;
+ err = FSPathMakeRef((const UInt8*)soundfont, &soundfontRef, NULL);
+ if (!err) {
+ err = AudioUnitSetProperty(
+ m_synth,
+ kMusicDeviceProperty_SoundBankFSRef,
+ kAudioUnitScope_Global,
+ 0,
+ &soundfontRef,
+ sizeof(soundfontRef)
+ );
+ }
+#else
+ // kMusicDeviceProperty_SoundBankFSRef is present on 10.6+, but
+ // kMusicDeviceProperty_SoundBankURL was added in 10.5 as a future prooof replacement
+ CFURLRef url = CFURLCreateFromFileSystemRepresentation(
+ kCFAllocatorDefault,
+ (const UInt8*)soundfont,
+ strlen(soundfont), false
+ );
+ if (url) {
+ err = AudioUnitSetProperty(
+ m_synth, kMusicDeviceProperty_SoundBankURL,
+ kAudioUnitScope_Global, 0, &url, sizeof(url)
+ );
+ CFRelease(url);
+ } else {
+ LOG_MSG("Failed to allocate CFURLRef from %s",soundfont);
+ }
+#endif
+ if (!err) {
+ LOG_MSG("MIDI:coreaudio: loaded soundfont: %s",soundfont);
+ } else {
+ LOG_MSG("Error loading CoreAudio SoundFont %s",soundfont);
+ // after trying and failing to load a soundfont it's better
+ // to fail initializing the CoreAudio driver or it might crash
+ return false;
+ }
+ }
+
+ // Finally: Start the graph!
+ RequireNoErr(AUGraphStart(m_auGraph));
+
+ return true;
+
+ bail:
+ if (m_auGraph) {
+ AUGraphStop(m_auGraph);
+ DisposeAUGraph(m_auGraph);
+ m_auGraph = 0;
+ }
+ return false;
+ }
+
+ void Close(void) {
+ if (m_auGraph) {
+ AUGraphStop(m_auGraph);
+ DisposeAUGraph(m_auGraph);
+ m_auGraph = 0;
+ }
+ }
+
+ void PlayMsg(Bit8u * msg) {
+ MusicDeviceMIDIEvent(m_synth, msg[0], msg[1], msg[2], 0);
+ }
+
+ void PlaySysex(Bit8u * sysex, Bitu len) {
+ MusicDeviceSysEx(m_synth, sysex, len);
+ }
+};
+
+#undef RequireNoErr
+
+MidiHandler_coreaudio Midi_coreaudio;
diff --git a/src/gui/midi_coremidi.h b/src/gui/midi_coremidi.h
new file mode 100644
index 000000000..9ad046b2d
--- /dev/null
+++ b/src/gui/midi_coremidi.h
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#include <CoreMIDI/MIDIServices.h>
+#include <sstream>
+#include <string>
+
+class MidiHandler_coremidi : public MidiHandler {
+private:
+ MIDIPortRef m_port;
+ MIDIClientRef m_client;
+ MIDIEndpointRef m_endpoint;
+ MIDIPacket* m_pCurPacket;
+public:
+ MidiHandler_coremidi() {m_pCurPacket = 0;}
+ const char * GetName(void) { return "coremidi"; }
+ bool Open(const char * conf) {
+ // Get the MIDIEndPoint
+ m_endpoint = 0;
+ OSStatus result;
+ Bitu numDests = MIDIGetNumberOfDestinations();
+ Bitu destId = numDests;
+ if(conf && *conf) {
+ std::string strconf(conf);
+ std::istringstream configmidi(strconf);
+ configmidi >> destId;
+ if (configmidi.fail() && numDests) {
+ lowcase(strconf);
+ for(Bitu i = 0; i<numDests; i++) {
+ MIDIEndpointRef dummy = MIDIGetDestination(i);
+ if (!dummy) continue;
+ CFStringRef midiname = 0;
+ if (MIDIObjectGetStringProperty(dummy,kMIDIPropertyDisplayName,&midiname) == noErr) {
+ const char* s = CFStringGetCStringPtr(midiname,kCFStringEncodingMacRoman);
+ if (s) {
+ std::string devname(s);
+ lowcase(devname);
+ if (devname.find(strconf) != std::string::npos) {
+ destId = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (destId >= numDests) destId = 0;
+ if (destId < numDests)
+ {
+ m_endpoint = MIDIGetDestination(destId);
+ }
+
+ // Create a MIDI client and port
+ MIDIClientCreate(CFSTR("MyClient"), 0, 0, &m_client);
+
+ if (!m_client)
+ {
+ LOG_MSG("MIDI:coremidi: No client created.");
+ return false;
+ }
+
+ MIDIOutputPortCreate(m_client, CFSTR("MyOutPort"), &m_port);
+
+ if (!m_port)
+ {
+ LOG_MSG("MIDI:coremidi: No port created.");
+ return false;
+ }
+
+
+ return true;
+ }
+
+ void Close(void) {
+ // Dispose the port
+ MIDIPortDispose(m_port);
+
+ // Dispose the client
+ MIDIClientDispose(m_client);
+
+ // Dispose the endpoint
+ // Not, as it is for Endpoints created by us
+// MIDIEndpointDispose(m_endpoint);
+ }
+
+ void PlayMsg(Bit8u * msg) {
+ // Acquire a MIDIPacketList
+ Byte packetBuf[128];
+ MIDIPacketList *packetList = (MIDIPacketList *)packetBuf;
+ m_pCurPacket = MIDIPacketListInit(packetList);
+
+ // Determine the length of msg
+ Bitu len=MIDI_evt_len[*msg];
+
+ // Add msg to the MIDIPacketList
+ MIDIPacketListAdd(packetList, (ByteCount)sizeof(packetBuf), m_pCurPacket, (MIDITimeStamp)0, len, msg);
+
+ // Send the MIDIPacketList
+ MIDISend(m_port,m_endpoint,packetList);
+ }
+
+ void PlaySysex(Bit8u * sysex, Bitu len) {
+ // Acquire a MIDIPacketList
+ Byte packetBuf[SYSEX_SIZE*4];
+ Bitu pos=0;
+ MIDIPacketList *packetList = (MIDIPacketList *)packetBuf;
+ m_pCurPacket = MIDIPacketListInit(packetList);
+
+ // Add msg to the MIDIPacketList
+ MIDIPacketListAdd(packetList, (ByteCount)sizeof(packetBuf), m_pCurPacket, (MIDITimeStamp)0, len, sysex);
+
+ // Send the MIDIPacketList
+ MIDISend(m_port,m_endpoint,packetList);
+ }
+ void ListAll(Program* base) {
+ Bitu numDests = MIDIGetNumberOfDestinations();
+ for(Bitu i = 0; i < numDests; i++){
+ MIDIEndpointRef dest = MIDIGetDestination(i);
+ if (!dest) continue;
+ CFStringRef midiname = 0;
+ if(MIDIObjectGetStringProperty(dest, kMIDIPropertyDisplayName, &midiname) == noErr) {
+ const char * s = CFStringGetCStringPtr(midiname, kCFStringEncodingMacRoman);
+ if (s) base->WriteOut("%02d\t%s\n",i,s);
+ }
+ //This is for EndPoints created by us.
+ //MIDIEndpointDispose(dest);
+ }
+
+ }
+};
+
+MidiHandler_coremidi Midi_coremidi;
+
diff --git a/src/gui/midi_oss.h b/src/gui/midi_oss.h
new file mode 100644
index 000000000..00c1cad7e
--- /dev/null
+++ b/src/gui/midi_oss.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include <fcntl.h>
+#define SEQ_MIDIPUTC 5
+
+class MidiHandler_oss: public MidiHandler {
+private:
+ int device;
+ Bit8u device_num;
+ bool isOpen;
+public:
+ MidiHandler_oss() : MidiHandler(),isOpen(false) {};
+ const char * GetName(void) { return "oss";};
+ bool Open(const char * conf) {
+ char devname[512];
+ if (conf && conf[0]) safe_strncpy(devname,conf,512);
+ else strcpy(devname,"/dev/sequencer");
+ char * devfind=(strrchr(devname,','));
+ if (devfind) {
+ *devfind++=0;
+ device_num=atoi(devfind);
+ } else device_num=0;
+ if (isOpen) return false;
+ device=open(devname, O_WRONLY, 0);
+ if (device<0) return false;
+ return true;
+ };
+ void Close(void) {
+ if (!isOpen) return;
+ if (device>0) close(device);
+ };
+ void PlayMsg(Bit8u * msg) {
+ Bit8u buf[128];Bitu pos=0;
+ Bitu len=MIDI_evt_len[*msg];
+ for (;len>0;len--) {
+ buf[pos++] = SEQ_MIDIPUTC;
+ buf[pos++] = *msg;
+ buf[pos++] = device_num;
+ buf[pos++] = 0;
+ msg++;
+ }
+ write(device,buf,pos);
+ };
+ void PlaySysex(Bit8u * sysex,Bitu len) {
+ Bit8u buf[SYSEX_SIZE*4];Bitu pos=0;
+ for (;len>0;len--) {
+ buf[pos++] = SEQ_MIDIPUTC;
+ buf[pos++] = *sysex++;
+ buf[pos++] = device_num;
+ buf[pos++] = 0;
+ }
+ write(device,buf,pos);
+ }
+};
+
+MidiHandler_oss Midi_oss;
+
+
+
+
diff --git a/src/gui/midi_win32.h b/src/gui/midi_win32.h
new file mode 100644
index 000000000..b7180f6d3
--- /dev/null
+++ b/src/gui/midi_win32.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <mmsystem.h>
+#include <string>
+#include <sstream>
+
+class MidiHandler_win32: public MidiHandler {
+private:
+ HMIDIOUT m_out;
+ MIDIHDR m_hdr;
+ HANDLE m_event;
+ bool isOpen;
+public:
+ MidiHandler_win32() : MidiHandler(),isOpen(false) {};
+ const char * GetName(void) { return "win32";};
+ bool Open(const char * conf) {
+ if (isOpen) return false;
+ m_event = CreateEvent (NULL, true, true, NULL);
+ MMRESULT res = MMSYSERR_NOERROR;
+ if(conf && *conf) {
+ std::string strconf(conf);
+ std::istringstream configmidi(strconf);
+ unsigned int total = midiOutGetNumDevs();
+ unsigned int nummer = total;
+ configmidi >> nummer;
+ if (configmidi.fail() && total) {
+ lowcase(strconf);
+ for(unsigned int i = 0; i< total;i++) {
+ MIDIOUTCAPS mididev;
+ midiOutGetDevCaps(i, &mididev, sizeof(MIDIOUTCAPS));
+ std::string devname(mididev.szPname);
+ lowcase(devname);
+ if (devname.find(strconf) != std::string::npos) {
+ nummer = i;
+ break;
+ }
+ }
+ }
+
+ if (nummer < total) {
+ MIDIOUTCAPS mididev;
+ midiOutGetDevCaps(nummer, &mididev, sizeof(MIDIOUTCAPS));
+ LOG_MSG("MIDI: win32 selected %s",mididev.szPname);
+ res = midiOutOpen(&m_out, nummer, (DWORD_PTR)m_event, 0, CALLBACK_EVENT);
+ }
+ } else {
+ res = midiOutOpen(&m_out, MIDI_MAPPER, (DWORD_PTR)m_event, 0, CALLBACK_EVENT);
+ }
+ if (res != MMSYSERR_NOERROR) return false;
+ isOpen=true;
+ return true;
+ };
+
+ void Close(void) {
+ if (!isOpen) return;
+ isOpen=false;
+ midiOutClose(m_out);
+ CloseHandle (m_event);
+ };
+ void PlayMsg(Bit8u * msg) {
+ midiOutShortMsg(m_out, *(Bit32u*)msg);
+ };
+ void PlaySysex(Bit8u * sysex,Bitu len) {
+ if (WaitForSingleObject (m_event, 2000) == WAIT_TIMEOUT) {
+ LOG(LOG_MISC,LOG_ERROR)("Can't send midi message");
+ return;
+ }
+ midiOutUnprepareHeader (m_out, &m_hdr, sizeof (m_hdr));
+
+ m_hdr.lpData = (char *) sysex;
+ m_hdr.dwBufferLength = len ;
+ m_hdr.dwBytesRecorded = len ;
+ m_hdr.dwUser = 0;
+
+ MMRESULT result = midiOutPrepareHeader (m_out, &m_hdr, sizeof (m_hdr));
+ if (result != MMSYSERR_NOERROR) return;
+ ResetEvent (m_event);
+ result = midiOutLongMsg (m_out,&m_hdr,sizeof(m_hdr));
+ if (result != MMSYSERR_NOERROR) {
+ SetEvent (m_event);
+ return;
+ }
+ }
+ void ListAll(Program* base) {
+ unsigned int total = midiOutGetNumDevs();
+ for(unsigned int i = 0;i < total;i++) {
+ MIDIOUTCAPS mididev;
+ midiOutGetDevCaps(i, &mididev, sizeof(MIDIOUTCAPS));
+ base->WriteOut("%2d\t \"%s\"\n",i,mididev.szPname);
+ }
+ }
+};
+
+MidiHandler_win32 Midi_win32;
+
+
diff --git a/src/gui/render.cpp b/src/gui/render.cpp
new file mode 100644
index 000000000..941fe76f9
--- /dev/null
+++ b/src/gui/render.cpp
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <sys/types.h>
+#include <assert.h>
+#include <math.h>
+
+#include "dosbox.h"
+#include "video.h"
+#include "render.h"
+#include "setup.h"
+#include "control.h"
+#include "mapper.h"
+#include "cross.h"
+#include "hardware.h"
+#include "support.h"
+
+#include "render_scalers.h"
+
+Render_t render;
+ScalerLineHandler_t RENDER_DrawLine;
+
+static void RENDER_CallBack( GFX_CallBackFunctions_t function );
+
+static void Check_Palette(void) {
+ /* Clean up any previous changed palette data */
+ if (render.pal.changed) {
+ memset(render.pal.modified, 0, sizeof(render.pal.modified));
+ render.pal.changed = false;
+ }
+ if (render.pal.first>render.pal.last)
+ return;
+ Bitu i;
+ switch (render.scale.outMode) {
+ case scalerMode8:
+ GFX_SetPalette(render.pal.first,render.pal.last-render.pal.first+1,(GFX_PalEntry *)&render.pal.rgb[render.pal.first]);
+ break;
+ case scalerMode15:
+ case scalerMode16:
+ for (i=render.pal.first;i<=render.pal.last;i++) {
+ Bit8u r=render.pal.rgb[i].red;
+ Bit8u g=render.pal.rgb[i].green;
+ Bit8u b=render.pal.rgb[i].blue;
+ Bit16u newPal = GFX_GetRGB(r,g,b);
+ if (newPal != render.pal.lut.b16[i]) {
+ render.pal.changed = true;
+ render.pal.modified[i] = 1;
+ render.pal.lut.b16[i] = newPal;
+ }
+ }
+ break;
+ case scalerMode32:
+ default:
+ for (i=render.pal.first;i<=render.pal.last;i++) {
+ Bit8u r=render.pal.rgb[i].red;
+ Bit8u g=render.pal.rgb[i].green;
+ Bit8u b=render.pal.rgb[i].blue;
+ Bit32u newPal = GFX_GetRGB(r,g,b);
+ if (newPal != render.pal.lut.b32[i]) {
+ render.pal.changed = true;
+ render.pal.modified[i] = 1;
+ render.pal.lut.b32[i] = newPal;
+ }
+ }
+ break;
+ }
+ /* Setup pal index to startup values */
+ render.pal.first=256;
+ render.pal.last=0;
+}
+
+void RENDER_SetPal(Bit8u entry,Bit8u red,Bit8u green,Bit8u blue) {
+ render.pal.rgb[entry].red=red;
+ render.pal.rgb[entry].green=green;
+ render.pal.rgb[entry].blue=blue;
+ if (render.pal.first>entry) render.pal.first=entry;
+ if (render.pal.last<entry) render.pal.last=entry;
+}
+
+static void RENDER_EmptyLineHandler(const void * src) {
+}
+
+static void RENDER_StartLineHandler(const void * s) {
+ if (s) {
+ const Bitu *src = (Bitu*)s;
+ Bitu *cache = (Bitu*)(render.scale.cacheRead);
+ for (Bits x=render.src.start;x>0;) {
+ if (GCC_UNLIKELY(src[0] != cache[0])) {
+ if (!GFX_StartUpdate( render.scale.outWrite, render.scale.outPitch )) {
+ RENDER_DrawLine = RENDER_EmptyLineHandler;
+ return;
+ }
+ render.scale.outWrite += render.scale.outPitch * Scaler_ChangedLines[0];
+ RENDER_DrawLine = render.scale.lineHandler;
+ RENDER_DrawLine( s );
+ return;
+ }
+ x--; src++; cache++;
+ }
+ }
+ render.scale.cacheRead += render.scale.cachePitch;
+ Scaler_ChangedLines[0] += Scaler_Aspect[ render.scale.inLine ];
+ render.scale.inLine++;
+ render.scale.outLine++;
+}
+
+static void RENDER_FinishLineHandler(const void * s) {
+ if (s) {
+ const Bitu *src = (Bitu*)s;
+ Bitu *cache = (Bitu*)(render.scale.cacheRead);
+ for (Bits x=render.src.start;x>0;) {
+ cache[0] = src[0];
+ x--; src++; cache++;
+ }
+ }
+ render.scale.cacheRead += render.scale.cachePitch;
+}
+
+
+static void RENDER_ClearCacheHandler(const void * src) {
+ Bitu x, width;
+ Bit32u *srcLine, *cacheLine;
+ srcLine = (Bit32u *)src;
+ cacheLine = (Bit32u *)render.scale.cacheRead;
+ width = render.scale.cachePitch / 4;
+ for (x=0;x<width;x++)
+ cacheLine[x] = ~srcLine[x];
+ render.scale.lineHandler( src );
+}
+
+bool RENDER_StartUpdate(void) {
+ if (GCC_UNLIKELY(render.updating))
+ return false;
+ if (GCC_UNLIKELY(!render.active))
+ return false;
+ if (GCC_UNLIKELY(render.frameskip.count<render.frameskip.max)) {
+ render.frameskip.count++;
+ return false;
+ }
+ render.frameskip.count=0;
+ if (render.scale.inMode == scalerMode8) {
+ Check_Palette();
+ }
+ render.scale.inLine = 0;
+ render.scale.outLine = 0;
+ render.scale.cacheRead = (Bit8u*)&scalerSourceCache;
+ render.scale.outWrite = 0;
+ render.scale.outPitch = 0;
+ Scaler_ChangedLines[0] = 0;
+ Scaler_ChangedLineIndex = 0;
+ /* Clearing the cache will first process the line to make sure it's never the same */
+ if (GCC_UNLIKELY( render.scale.clearCache) ) {
+// LOG_MSG("Clearing cache");
+ //Will always have to update the screen with this one anyway, so let's update already
+ if (GCC_UNLIKELY(!GFX_StartUpdate( render.scale.outWrite, render.scale.outPitch )))
+ return false;
+ render.fullFrame = true;
+ render.scale.clearCache = false;
+ RENDER_DrawLine = RENDER_ClearCacheHandler;
+ } else {
+ if (render.pal.changed) {
+ /* Assume pal changes always do a full screen update anyway */
+ if (GCC_UNLIKELY(!GFX_StartUpdate( render.scale.outWrite, render.scale.outPitch )))
+ return false;
+ RENDER_DrawLine = render.scale.linePalHandler;
+ render.fullFrame = true;
+ } else {
+ RENDER_DrawLine = RENDER_StartLineHandler;
+ if (GCC_UNLIKELY(CaptureState & (CAPTURE_IMAGE|CAPTURE_VIDEO)))
+ render.fullFrame = true;
+ else
+ render.fullFrame = false;
+ }
+ }
+ render.updating = true;
+ return true;
+}
+
+static void RENDER_Halt( void ) {
+ RENDER_DrawLine = RENDER_EmptyLineHandler;
+ GFX_EndUpdate( 0 );
+ render.updating=false;
+ render.active=false;
+}
+
+extern Bitu PIC_Ticks;
+void RENDER_EndUpdate( bool abort ) {
+ if (GCC_UNLIKELY(!render.updating))
+ return;
+ RENDER_DrawLine = RENDER_EmptyLineHandler;
+ if (GCC_UNLIKELY(CaptureState & (CAPTURE_IMAGE|CAPTURE_VIDEO))) {
+ Bitu pitch, flags;
+ flags = 0;
+ if (render.src.dblw != render.src.dblh) {
+ if (render.src.dblw) flags|=CAPTURE_FLAG_DBLW;
+ if (render.src.dblh) flags|=CAPTURE_FLAG_DBLH;
+ }
+ float fps = render.src.fps;
+ pitch = render.scale.cachePitch;
+ if (render.frameskip.max)
+ fps /= 1+render.frameskip.max;
+ CAPTURE_AddImage( render.src.width, render.src.height, render.src.bpp, pitch,
+ flags, fps, (Bit8u *)&scalerSourceCache, (Bit8u*)&render.pal.rgb );
+ }
+ if ( render.scale.outWrite ) {
+ GFX_EndUpdate( abort? NULL : Scaler_ChangedLines );
+ render.frameskip.hadSkip[render.frameskip.index] = 0;
+ } else {
+#if 0
+ Bitu total = 0, i;
+ render.frameskip.hadSkip[render.frameskip.index] = 1;
+ for (i = 0;i<RENDER_SKIP_CACHE;i++)
+ total += render.frameskip.hadSkip[i];
+ LOG_MSG( "Skipped frame %d %d", PIC_Ticks, (total * 100) / RENDER_SKIP_CACHE );
+#endif
+ }
+ render.frameskip.index = (render.frameskip.index + 1) & (RENDER_SKIP_CACHE - 1);
+ render.updating=false;
+}
+
+static Bitu MakeAspectTable(Bitu skip,Bitu height,double scaley,Bitu miny) {
+ Bitu i;
+ double lines=0;
+ Bitu linesadded=0;
+ for (i=0;i<skip;i++)
+ Scaler_Aspect[i] = 0;
+
+ height += skip;
+ for (i=skip;i<height;i++) {
+ lines += scaley;
+ if (lines >= miny) {
+ Bitu templines = (Bitu)lines;
+ lines -= templines;
+ linesadded += templines;
+ Scaler_Aspect[i] = templines;
+ } else {
+ Scaler_Aspect[i] = 0;
+ }
+ }
+ return linesadded;
+}
+
+
+static void RENDER_Reset( void ) {
+ Bitu width=render.src.width;
+ Bitu height=render.src.height;
+ bool dblw=render.src.dblw;
+ bool dblh=render.src.dblh;
+
+ double gfx_scalew;
+ double gfx_scaleh;
+
+ Bitu gfx_flags, xscale, yscale;
+ ScalerSimpleBlock_t *simpleBlock = &ScaleNormal1x;
+ ScalerComplexBlock_t *complexBlock = 0;
+ if (render.aspect) {
+ if (render.src.ratio>1.0) {
+ gfx_scalew = 1;
+ gfx_scaleh = render.src.ratio;
+ } else {
+ gfx_scalew = (1/render.src.ratio);
+ gfx_scaleh = 1;
+ }
+ } else {
+ gfx_scalew = 1;
+ gfx_scaleh = 1;
+ }
+ if ((dblh && dblw) || (render.scale.forced && !dblh && !dblw)) {
+ /* Initialize always working defaults */
+ if (render.scale.size == 2)
+ simpleBlock = &ScaleNormal2x;
+ else if (render.scale.size == 3)
+ simpleBlock = &ScaleNormal3x;
+ else
+ simpleBlock = &ScaleNormal1x;
+ /* Maybe override them */
+#if RENDER_USE_ADVANCED_SCALERS>0
+ switch (render.scale.op) {
+#if RENDER_USE_ADVANCED_SCALERS>2
+ case scalerOpAdvInterp:
+ if (render.scale.size == 2)
+ complexBlock = &ScaleAdvInterp2x;
+ else if (render.scale.size == 3)
+ complexBlock = &ScaleAdvInterp3x;
+ break;
+ case scalerOpAdvMame:
+ if (render.scale.size == 2)
+ complexBlock = &ScaleAdvMame2x;
+ else if (render.scale.size == 3)
+ complexBlock = &ScaleAdvMame3x;
+ break;
+ case scalerOpHQ:
+ if (render.scale.size == 2)
+ complexBlock = &ScaleHQ2x;
+ else if (render.scale.size == 3)
+ complexBlock = &ScaleHQ3x;
+ break;
+ case scalerOpSuperSaI:
+ if (render.scale.size == 2)
+ complexBlock = &ScaleSuper2xSaI;
+ break;
+ case scalerOpSuperEagle:
+ if (render.scale.size == 2)
+ complexBlock = &ScaleSuperEagle;
+ break;
+ case scalerOpSaI:
+ if (render.scale.size == 2)
+ complexBlock = &Scale2xSaI;
+ break;
+#endif
+ case scalerOpTV:
+ if (render.scale.size == 2)
+ simpleBlock = &ScaleTV2x;
+ else if (render.scale.size == 3)
+ simpleBlock = &ScaleTV3x;
+ break;
+ case scalerOpRGB:
+ if (render.scale.size == 2)
+ simpleBlock = &ScaleRGB2x;
+ else if (render.scale.size == 3)
+ simpleBlock = &ScaleRGB3x;
+ break;
+ case scalerOpScan:
+ if (render.scale.size == 2)
+ simpleBlock = &ScaleScan2x;
+ else if (render.scale.size == 3)
+ simpleBlock = &ScaleScan3x;
+ break;
+ default:
+ break;
+ }
+#endif
+ } else if (dblw) {
+ simpleBlock = &ScaleNormalDw;
+ } else if (dblh) {
+ simpleBlock = &ScaleNormalDh;
+ } else {
+forcenormal:
+ complexBlock = 0;
+ simpleBlock = &ScaleNormal1x;
+ }
+ if (complexBlock) {
+#if RENDER_USE_ADVANCED_SCALERS>1
+ if ((width >= SCALER_COMPLEXWIDTH - 16) || height >= SCALER_COMPLEXHEIGHT - 16) {
+ LOG_MSG("Scaler can't handle this resolution, going back to normal");
+ goto forcenormal;
+ }
+#else
+ goto forcenormal;
+#endif
+ gfx_flags = complexBlock->gfxFlags;
+ xscale = complexBlock->xscale;
+ yscale = complexBlock->yscale;
+// LOG_MSG("Scaler:%s",complexBlock->name);
+ } else {
+ gfx_flags = simpleBlock->gfxFlags;
+ xscale = simpleBlock->xscale;
+ yscale = simpleBlock->yscale;
+// LOG_MSG("Scaler:%s",simpleBlock->name);
+ }
+ switch (render.src.bpp) {
+ case 8:
+ render.src.start = ( render.src.width * 1) / sizeof(Bitu);
+ if (gfx_flags & GFX_CAN_8)
+ gfx_flags |= GFX_LOVE_8;
+ else
+ gfx_flags |= GFX_LOVE_32;
+ break;
+ case 15:
+ render.src.start = ( render.src.width * 2) / sizeof(Bitu);
+ gfx_flags |= GFX_LOVE_15;
+ gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
+ break;
+ case 16:
+ render.src.start = ( render.src.width * 2) / sizeof(Bitu);
+ gfx_flags |= GFX_LOVE_16;
+ gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
+ break;
+ case 32:
+ render.src.start = ( render.src.width * 4) / sizeof(Bitu);
+ gfx_flags |= GFX_LOVE_32;
+ gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
+ break;
+ }
+ gfx_flags=GFX_GetBestMode(gfx_flags);
+ if (!gfx_flags) {
+ if (!complexBlock && simpleBlock == &ScaleNormal1x)
+ E_Exit("Failed to create a rendering output");
+ else
+ goto forcenormal;
+ }
+ width *= xscale;
+ Bitu skip = complexBlock ? 1 : 0;
+ if (gfx_flags & GFX_SCALING) {
+ height = MakeAspectTable(skip, render.src.height, yscale, yscale );
+ } else {
+ if ((gfx_flags & GFX_CAN_RANDOM) && gfx_scaleh > 1) {
+ gfx_scaleh *= yscale;
+ height = MakeAspectTable( skip, render.src.height, gfx_scaleh, yscale );
+ } else {
+ gfx_flags &= ~GFX_CAN_RANDOM; //Hardware surface when possible
+ height = MakeAspectTable( skip, render.src.height, yscale, yscale);
+ }
+ }
+/* Setup the scaler variables */
+ gfx_flags=GFX_SetSize(width,height,gfx_flags,gfx_scalew,gfx_scaleh,&RENDER_CallBack);
+ if (gfx_flags & GFX_CAN_8)
+ render.scale.outMode = scalerMode8;
+ else if (gfx_flags & GFX_CAN_15)
+ render.scale.outMode = scalerMode15;
+ else if (gfx_flags & GFX_CAN_16)
+ render.scale.outMode = scalerMode16;
+ else if (gfx_flags & GFX_CAN_32)
+ render.scale.outMode = scalerMode32;
+ else
+ E_Exit("Failed to create a rendering output");
+ ScalerLineBlock_t *lineBlock;
+ if (gfx_flags & GFX_HARDWARE) {
+#if RENDER_USE_ADVANCED_SCALERS>1
+ if (complexBlock) {
+ lineBlock = &ScalerCache;
+ render.scale.complexHandler = complexBlock->Linear[ render.scale.outMode ];
+ } else
+#endif
+ {
+ render.scale.complexHandler = 0;
+ lineBlock = &simpleBlock->Linear;
+ }
+ } else {
+#if RENDER_USE_ADVANCED_SCALERS>1
+ if (complexBlock) {
+ lineBlock = &ScalerCache;
+ render.scale.complexHandler = complexBlock->Random[ render.scale.outMode ];
+ } else
+#endif
+ {
+ render.scale.complexHandler = 0;
+ lineBlock = &simpleBlock->Random;
+ }
+ }
+ switch (render.src.bpp) {
+ case 8:
+ render.scale.lineHandler = (*lineBlock)[0][render.scale.outMode];
+ render.scale.linePalHandler = (*lineBlock)[4][render.scale.outMode];
+ render.scale.inMode = scalerMode8;
+ render.scale.cachePitch = render.src.width * 1;
+ break;
+ case 15:
+ render.scale.lineHandler = (*lineBlock)[1][render.scale.outMode];
+ render.scale.linePalHandler = 0;
+ render.scale.inMode = scalerMode15;
+ render.scale.cachePitch = render.src.width * 2;
+ break;
+ case 16:
+ render.scale.lineHandler = (*lineBlock)[2][render.scale.outMode];
+ render.scale.linePalHandler = 0;
+ render.scale.inMode = scalerMode16;
+ render.scale.cachePitch = render.src.width * 2;
+ break;
+ case 32:
+ render.scale.lineHandler = (*lineBlock)[3][render.scale.outMode];
+ render.scale.linePalHandler = 0;
+ render.scale.inMode = scalerMode32;
+ render.scale.cachePitch = render.src.width * 4;
+ break;
+ default:
+ E_Exit("RENDER:Wrong source bpp %d", render.src.bpp );
+ }
+ render.scale.blocks = render.src.width / SCALER_BLOCKSIZE;
+ render.scale.lastBlock = render.src.width % SCALER_BLOCKSIZE;
+ render.scale.inHeight = render.src.height;
+ /* Reset the palette change detection to it's initial value */
+ render.pal.first= 0;
+ render.pal.last = 255;
+ render.pal.changed = false;
+ memset(render.pal.modified, 0, sizeof(render.pal.modified));
+ //Finish this frame using a copy only handler
+ RENDER_DrawLine = RENDER_FinishLineHandler;
+ render.scale.outWrite = 0;
+ /* Signal the next frame to first reinit the cache */
+ render.scale.clearCache = true;
+ render.active=true;
+}
+
+static void RENDER_CallBack( GFX_CallBackFunctions_t function ) {
+ if (function == GFX_CallBackStop) {
+ RENDER_Halt( );
+ return;
+ } else if (function == GFX_CallBackRedraw) {
+ render.scale.clearCache = true;
+ return;
+ } else if ( function == GFX_CallBackReset) {
+ GFX_EndUpdate( 0 );
+ RENDER_Reset();
+ } else {
+ E_Exit("Unhandled GFX_CallBackReset %d", function );
+ }
+}
+
+void RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,float fps,double ratio,bool dblw,bool dblh) {
+ RENDER_Halt( );
+ if (!width || !height || width > SCALER_MAXWIDTH || height > SCALER_MAXHEIGHT) {
+ return;
+ }
+ if ( ratio > 1 ) {
+ double target = height * ratio + 0.025;
+ ratio = target / height;
+ } else {
+ //This would alter the width of the screen, we don't care about rounding errors here
+ }
+ render.src.width=width;
+ render.src.height=height;
+ render.src.bpp=bpp;
+ render.src.dblw=dblw;
+ render.src.dblh=dblh;
+ render.src.fps=fps;
+ render.src.ratio=ratio;
+ RENDER_Reset( );
+}
+
+extern void GFX_SetTitle(Bit32s cycles, Bits frameskip,bool paused);
+static void IncreaseFrameSkip(bool pressed) {
+ if (!pressed)
+ return;
+ if (render.frameskip.max<10) render.frameskip.max++;
+ LOG_MSG("Frame Skip at %d",render.frameskip.max);
+ GFX_SetTitle(-1,render.frameskip.max,false);
+}
+
+static void DecreaseFrameSkip(bool pressed) {
+ if (!pressed)
+ return;
+ if (render.frameskip.max>0) render.frameskip.max--;
+ LOG_MSG("Frame Skip at %d",render.frameskip.max);
+ GFX_SetTitle(-1,render.frameskip.max,false);
+}
+/* Disabled as I don't want to waste a keybind for that. Might be used in the future (Qbix)
+static void ChangeScaler(bool pressed) {
+ if (!pressed)
+ return;
+ render.scale.op = (scalerOperation)((int)render.scale.op+1);
+ if((render.scale.op) >= scalerLast || render.scale.size == 1) {
+ render.scale.op = (scalerOperation)0;
+ if(++render.scale.size > 3)
+ render.scale.size = 1;
+ }
+ RENDER_CallBack( GFX_CallBackReset );
+} */
+
+void RENDER_Init(Section * sec) {
+ Section_prop * section=static_cast<Section_prop *>(sec);
+
+ //For restarting the renderer.
+ static bool running = false;
+ bool aspect = render.aspect;
+ Bitu scalersize = render.scale.size;
+ bool scalerforced = render.scale.forced;
+ scalerOperation_t scaleOp = render.scale.op;
+
+ render.pal.first=256;
+ render.pal.last=0;
+ render.aspect=section->Get_bool("aspect");
+ render.frameskip.max=section->Get_int("frameskip");
+ render.frameskip.count=0;
+ std::string cline;
+ std::string scaler;
+ //Check for commandline paramters and parse them through the configclass so they get checked against allowed values
+ if (control->cmdline->FindString("-scaler",cline,true)) {
+ section->HandleInputline(std::string("scaler=") + cline);
+ } else if (control->cmdline->FindString("-forcescaler",cline,true)) {
+ section->HandleInputline(std::string("scaler=") + cline + " forced");
+ }
+
+ Prop_multival* prop = section->Get_multival("scaler");
+ scaler = prop->GetSection()->Get_string("type");
+ std::string f = prop->GetSection()->Get_string("force");
+ render.scale.forced = false;
+ if(f == "forced") render.scale.forced = true;
+
+ if (scaler == "none") { render.scale.op = scalerOpNormal;render.scale.size = 1; }
+ else if (scaler == "normal2x") { render.scale.op = scalerOpNormal;render.scale.size = 2; }
+ else if (scaler == "normal3x") { render.scale.op = scalerOpNormal;render.scale.size = 3; }
+#if RENDER_USE_ADVANCED_SCALERS>2
+ else if (scaler == "advmame2x") { render.scale.op = scalerOpAdvMame;render.scale.size = 2; }
+ else if (scaler == "advmame3x") { render.scale.op = scalerOpAdvMame;render.scale.size = 3; }
+ else if (scaler == "advinterp2x") { render.scale.op = scalerOpAdvInterp;render.scale.size = 2; }
+ else if (scaler == "advinterp3x") { render.scale.op = scalerOpAdvInterp;render.scale.size = 3; }
+ else if (scaler == "hq2x") { render.scale.op = scalerOpHQ;render.scale.size = 2; }
+ else if (scaler == "hq3x") { render.scale.op = scalerOpHQ;render.scale.size = 3; }
+ else if (scaler == "2xsai") { render.scale.op = scalerOpSaI;render.scale.size = 2; }
+ else if (scaler == "super2xsai") { render.scale.op = scalerOpSuperSaI;render.scale.size = 2; }
+ else if (scaler == "supereagle") { render.scale.op = scalerOpSuperEagle;render.scale.size = 2; }
+#endif
+#if RENDER_USE_ADVANCED_SCALERS>0
+ else if (scaler == "tv2x") { render.scale.op = scalerOpTV;render.scale.size = 2; }
+ else if (scaler == "tv3x") { render.scale.op = scalerOpTV;render.scale.size = 3; }
+ else if (scaler == "rgb2x"){ render.scale.op = scalerOpRGB;render.scale.size = 2; }
+ else if (scaler == "rgb3x"){ render.scale.op = scalerOpRGB;render.scale.size = 3; }
+ else if (scaler == "scan2x"){ render.scale.op = scalerOpScan;render.scale.size = 2; }
+ else if (scaler == "scan3x"){ render.scale.op = scalerOpScan;render.scale.size = 3; }
+#endif
+
+ //If something changed that needs a ReInit
+ // Only ReInit when there is a src.bpp (fixes crashes on startup and directly changing the scaler without a screen specified yet)
+ if(running && render.src.bpp && ((render.aspect != aspect) || (render.scale.op != scaleOp) ||
+ (render.scale.size != scalersize) || (render.scale.forced != scalerforced) ||
+ render.scale.forced))
+ RENDER_CallBack( GFX_CallBackReset );
+
+ if(!running) render.updating=true;
+ running = true;
+
+ MAPPER_AddHandler(DecreaseFrameSkip,MK_f7,MMOD1,"decfskip","Dec Fskip");
+ MAPPER_AddHandler(IncreaseFrameSkip,MK_f8,MMOD1,"incfskip","Inc Fskip");
+ GFX_SetTitle(-1,render.frameskip.max,false);
+}
+
diff --git a/src/gui/render_loops.h b/src/gui/render_loops.h
new file mode 100644
index 000000000..635da6bf3
--- /dev/null
+++ b/src/gui/render_loops.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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 (SCALERLINEAR)
+static void conc3d(SCALERNAME,SBPP,L)(void) {
+#else
+static void conc3d(SCALERNAME,SBPP,R)(void) {
+#endif
+//Skip the first one for multiline input scalers
+ if (!render.scale.outLine) {
+ render.scale.outLine++;
+ return;
+ }
+lastagain:
+ if (!CC[render.scale.outLine][0]) {
+#if defined(SCALERLINEAR)
+ Bitu scaleLines = SCALERHEIGHT;
+#else
+ Bitu scaleLines = Scaler_Aspect[ render.scale.outLine ];
+#endif
+ ScalerAddLines( 0, scaleLines );
+ if (++render.scale.outLine == render.scale.inHeight)
+ goto lastagain;
+ return;
+ }
+ /* Clear the complete line marker */
+ CC[render.scale.outLine][0] = 0;
+ const PTYPE * fc = &FC[render.scale.outLine][1];
+ PTYPE * line0=(PTYPE *)(render.scale.outWrite);
+ Bit8u * changed = &CC[render.scale.outLine][1];
+ Bitu b;
+ for (b=0;b<render.scale.blocks;b++) {
+#if (SCALERHEIGHT > 1)
+ PTYPE * line1;
+#endif
+#if (SCALERHEIGHT > 2)
+ PTYPE * line2;
+#endif
+ /* Clear this block being dirty marker */
+ const Bitu changeType = changed[b];
+ changed[b] = 0;
+ switch (changeType) {
+ case 0:
+ line0 += SCALERWIDTH * SCALER_BLOCKSIZE;
+ fc += SCALER_BLOCKSIZE;
+ continue;
+ case SCALE_LEFT:
+#if (SCALERHEIGHT > 1)
+ line1 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch);
+#endif
+#if (SCALERHEIGHT > 2)
+ line2 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch * 2);
+#endif
+ SCALERFUNC;
+ line0 += SCALERWIDTH * SCALER_BLOCKSIZE;
+ fc += SCALER_BLOCKSIZE;
+ break;
+ case SCALE_LEFT | SCALE_RIGHT:
+#if (SCALERHEIGHT > 1)
+ line1 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch);
+#endif
+#if (SCALERHEIGHT > 2)
+ line2 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch * 2);
+#endif
+ SCALERFUNC;
+ case SCALE_RIGHT:
+#if (SCALERHEIGHT > 1)
+ line1 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch);
+#endif
+#if (SCALERHEIGHT > 2)
+ line2 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch * 2);
+#endif
+ line0 += SCALERWIDTH * (SCALER_BLOCKSIZE -1);
+#if (SCALERHEIGHT > 1)
+ line1 += SCALERWIDTH * (SCALER_BLOCKSIZE -1);
+#endif
+#if (SCALERHEIGHT > 2)
+ line2 += SCALERWIDTH * (SCALER_BLOCKSIZE -1);
+#endif
+ fc += SCALER_BLOCKSIZE -1;
+ SCALERFUNC;
+ line0 += SCALERWIDTH;
+ fc++;
+ break;
+ default:
+#if defined(SCALERLINEAR)
+#if (SCALERHEIGHT > 1)
+ line1 = WC[0];
+#endif
+#if (SCALERHEIGHT > 2)
+ line2 = WC[1];
+#endif
+#else
+#if (SCALERHEIGHT > 1)
+ line1 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch);
+#endif
+#if (SCALERHEIGHT > 2)
+ line2 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch * 2);
+#endif
+#endif //defined(SCALERLINEAR)
+ for (Bitu i = 0; i<SCALER_BLOCKSIZE;i++) {
+ SCALERFUNC;
+ line0 += SCALERWIDTH;
+#if (SCALERHEIGHT > 1)
+ line1 += SCALERWIDTH;
+#endif
+#if (SCALERHEIGHT > 2)
+ line2 += SCALERWIDTH;
+#endif
+ fc++;
+ }
+#if defined(SCALERLINEAR)
+#if (SCALERHEIGHT > 1)
+ BituMove((Bit8u*)(&line0[-SCALER_BLOCKSIZE*SCALERWIDTH])+render.scale.outPitch ,WC[0], SCALER_BLOCKSIZE *SCALERWIDTH*PSIZE);
+#endif
+#if (SCALERHEIGHT > 2)
+ BituMove((Bit8u*)(&line0[-SCALER_BLOCKSIZE*SCALERWIDTH])+render.scale.outPitch*2,WC[1], SCALER_BLOCKSIZE *SCALERWIDTH*PSIZE);
+#endif
+#endif //defined(SCALERLINEAR)
+ break;
+ }
+ }
+#if defined(SCALERLINEAR)
+ Bitu scaleLines = SCALERHEIGHT;
+#else
+ Bitu scaleLines = Scaler_Aspect[ render.scale.outLine ];
+ if ( ((Bits)(scaleLines - SCALERHEIGHT)) > 0 ) {
+ BituMove( render.scale.outWrite + render.scale.outPitch * SCALERHEIGHT,
+ render.scale.outWrite + render.scale.outPitch * (SCALERHEIGHT-1),
+ render.src.width * SCALERWIDTH * PSIZE);
+ }
+#endif
+ ScalerAddLines( 1, scaleLines );
+ if (++render.scale.outLine == render.scale.inHeight)
+ goto lastagain;
+}
+
+#if !defined(SCALERLINEAR)
+#define SCALERLINEAR 1
+#include "render_loops.h"
+#undef SCALERLINEAR
+#endif
diff --git a/src/gui/render_scalers.cpp b/src/gui/render_scalers.cpp
new file mode 100644
index 000000000..6a0f3df2c
--- /dev/null
+++ b/src/gui/render_scalers.cpp
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+//TODO:
+//Maybe just do the cache checking back into the simple scalers so they can
+//just handle it all in one go, but this seems to work well enough for now
+
+#include "dosbox.h"
+#include "render.h"
+#include <string.h>
+
+Bit8u Scaler_Aspect[SCALER_MAXHEIGHT];
+Bit16u Scaler_ChangedLines[SCALER_MAXHEIGHT];
+Bitu Scaler_ChangedLineIndex;
+
+static union {
+ Bit32u b32 [4][SCALER_MAXWIDTH*3];
+ Bit16u b16 [4][SCALER_MAXWIDTH*3];
+ Bit8u b8 [4][SCALER_MAXWIDTH*3];
+} scalerWriteCache;
+//scalerFrameCache_t scalerFrameCache;
+scalerSourceCache_t scalerSourceCache;
+#if RENDER_USE_ADVANCED_SCALERS>1
+scalerChangeCache_t scalerChangeCache;
+#endif
+
+#define _conc2(A,B) A ## B
+#define _conc3(A,B,C) A ## B ## C
+#define _conc4(A,B,C,D) A ## B ## C ## D
+#define _conc5(A,B,C,D,E) A ## B ## C ## D ## E
+#define _conc7(A,B,C,D,E,F,G) A ## B ## C ## D ## E ## F ## G
+
+#define conc2(A,B) _conc2(A,B)
+#define conc3(A,B,C) _conc3(A,B,C)
+#define conc4(A,B,C,D) _conc4(A,B,C,D)
+#define conc2d(A,B) _conc3(A,_,B)
+#define conc3d(A,B,C) _conc5(A,_,B,_,C)
+#define conc4d(A,B,C,D) _conc7(A,_,B,_,C,_,D)
+
+static INLINE void BituMove( void *_dst, const void * _src, Bitu size) {
+ Bitu * dst=(Bitu *)(_dst);
+ const Bitu * src=(Bitu *)(_src);
+ size/=sizeof(Bitu);
+ for (Bitu x=0; x<size;x++)
+ dst[x] = src[x];
+}
+
+static INLINE void ScalerAddLines( Bitu changed, Bitu count ) {
+ if ((Scaler_ChangedLineIndex & 1) == changed ) {
+ Scaler_ChangedLines[Scaler_ChangedLineIndex] += count;
+ } else {
+ Scaler_ChangedLines[++Scaler_ChangedLineIndex] = count;
+ }
+ render.scale.outWrite += render.scale.outPitch * count;
+}
+
+
+#define BituMove2(_DST,_SRC,_SIZE) \
+{ \
+ Bitu bsize=(_SIZE)/sizeof(Bitu); \
+ Bitu * bdst=(Bitu *)(_DST); \
+ Bitu * bsrc=(Bitu *)(_SRC); \
+ while (bsize--) *bdst++=*bsrc++; \
+}
+
+#define interp_w2(P0,P1,W0,W1) \
+ ((((P0&redblueMask)*W0+(P1&redblueMask)*W1)/(W0+W1)) & redblueMask) | \
+ ((((P0& greenMask)*W0+(P1& greenMask)*W1)/(W0+W1)) & greenMask)
+#define interp_w3(P0,P1,P2,W0,W1,W2) \
+ ((((P0&redblueMask)*W0+(P1&redblueMask)*W1+(P2&redblueMask)*W2)/(W0+W1+W2)) & redblueMask) | \
+ ((((P0& greenMask)*W0+(P1& greenMask)*W1+(P2& greenMask)*W2)/(W0+W1+W2)) & greenMask)
+#define interp_w4(P0,P1,P2,P3,W0,W1,W2,W3) \
+ ((((P0&redblueMask)*W0+(P1&redblueMask)*W1+(P2&redblueMask)*W2+(P3&redblueMask)*W3)/(W0+W1+W2+W3)) & redblueMask) | \
+ ((((P0& greenMask)*W0+(P1& greenMask)*W1+(P2& greenMask)*W2+(P3& greenMask)*W3)/(W0+W1+W2+W3)) & greenMask)
+
+
+#define CC scalerChangeCache
+
+/* Include the different rendering routines */
+#define SBPP 8
+#define DBPP 8
+#include "render_templates.h"
+#undef DBPP
+#define DBPP 15
+#include "render_templates.h"
+#undef DBPP
+#define DBPP 16
+#include "render_templates.h"
+#undef DBPP
+#define DBPP 32
+#include "render_templates.h"
+#undef SBPP
+#undef DBPP
+
+/* SBPP 9 is a special case with palette check support */
+#define SBPP 9
+#define DBPP 8
+#include "render_templates.h"
+#undef DBPP
+#define DBPP 15
+#include "render_templates.h"
+#undef DBPP
+#define DBPP 16
+#include "render_templates.h"
+#undef DBPP
+#define DBPP 32
+#include "render_templates.h"
+#undef SBPP
+#undef DBPP
+
+#define SBPP 15
+#define DBPP 15
+#include "render_templates.h"
+#undef DBPP
+#define DBPP 16
+#include "render_templates.h"
+#undef DBPP
+#define DBPP 32
+#include "render_templates.h"
+#undef SBPP
+#undef DBPP
+
+#define SBPP 16
+#define DBPP 15
+#include "render_templates.h"
+#undef DBPP
+#define DBPP 16
+#include "render_templates.h"
+#undef DBPP
+#define DBPP 32
+#include "render_templates.h"
+#undef SBPP
+#undef DBPP
+
+#define SBPP 32
+#define DBPP 15
+#include "render_templates.h"
+#undef DBPP
+#define DBPP 16
+#include "render_templates.h"
+#undef DBPP
+#define DBPP 32
+#include "render_templates.h"
+#undef SBPP
+#undef DBPP
+
+
+#if RENDER_USE_ADVANCED_SCALERS>1
+ScalerLineBlock_t ScalerCache = {
+{ Cache_8_8, Cache_8_15 , Cache_8_16 , Cache_8_32 },
+{ 0, Cache_15_15, Cache_15_16, Cache_15_32},
+{ 0, Cache_16_15, Cache_16_16, Cache_16_32},
+{ 0, Cache_32_15, Cache_32_16, Cache_32_32},
+{ Cache_8_8, Cache_9_15 , Cache_9_16 , Cache_9_32 }
+};
+#endif
+
+ScalerSimpleBlock_t ScaleNormal1x = {
+ "Normal",
+ GFX_CAN_8|GFX_CAN_15|GFX_CAN_16|GFX_CAN_32,
+ 1,1,{
+{ Normal1x_8_8_L, Normal1x_8_15_L , Normal1x_8_16_L , Normal1x_8_32_L },
+{ 0, Normal1x_15_15_L, Normal1x_15_16_L, Normal1x_15_32_L},
+{ 0, Normal1x_16_15_L, Normal1x_16_16_L, Normal1x_16_32_L},
+{ 0, Normal1x_32_15_L, Normal1x_32_16_L, Normal1x_32_32_L},
+{ Normal1x_8_8_L, Normal1x_9_15_L , Normal1x_9_16_L , Normal1x_9_32_L }
+},{
+{ Normal1x_8_8_R, Normal1x_8_15_R , Normal1x_8_16_R , Normal1x_8_32_R },
+{ 0, Normal1x_15_15_R, Normal1x_15_16_R, Normal1x_15_32_R},
+{ 0, Normal1x_16_15_R, Normal1x_16_16_R, Normal1x_16_32_R},
+{ 0, Normal1x_32_15_R, Normal1x_32_16_R, Normal1x_32_32_R},
+{ Normal1x_8_8_R, Normal1x_9_15_R , Normal1x_9_16_R , Normal1x_9_32_R }
+}};
+
+ScalerSimpleBlock_t ScaleNormalDw = {
+ "Normal",
+ GFX_CAN_8|GFX_CAN_15|GFX_CAN_16|GFX_CAN_32,
+ 2,1,{
+{ NormalDw_8_8_L, NormalDw_8_15_L , NormalDw_8_16_L , NormalDw_8_32_L },
+{ 0, NormalDw_15_15_L, NormalDw_15_16_L, NormalDw_15_32_L},
+{ 0, NormalDw_16_15_L, NormalDw_16_16_L, NormalDw_16_32_L},
+{ 0, NormalDw_32_15_L, NormalDw_32_16_L, NormalDw_32_32_L},
+{ NormalDw_8_8_L, NormalDw_9_15_L , NormalDw_9_16_L , NormalDw_9_32_L }
+},{
+{ NormalDw_8_8_R, NormalDw_8_15_R , NormalDw_8_16_R , NormalDw_8_32_R },
+{ 0, NormalDw_15_15_R, NormalDw_15_16_R, NormalDw_15_32_R},
+{ 0, NormalDw_16_15_R, NormalDw_16_16_R, NormalDw_16_32_R},
+{ 0, NormalDw_32_15_R, NormalDw_32_16_R, NormalDw_32_32_R},
+{ NormalDw_8_8_R, NormalDw_9_15_R , NormalDw_9_16_R , NormalDw_9_32_R }
+}};
+
+ScalerSimpleBlock_t ScaleNormalDh = {
+ "Normal",
+ GFX_CAN_8|GFX_CAN_15|GFX_CAN_16|GFX_CAN_32,
+ 1,2,{
+{ NormalDh_8_8_L, NormalDh_8_15_L , NormalDh_8_16_L , NormalDh_8_32_L },
+{ 0, NormalDh_15_15_L, NormalDh_15_16_L, NormalDh_15_32_L},
+{ 0, NormalDh_16_15_L, NormalDh_16_16_L, NormalDh_16_32_L},
+{ 0, NormalDh_32_15_L, NormalDh_32_16_L, NormalDh_32_32_L},
+{ NormalDh_8_8_L, NormalDh_9_15_L , NormalDh_9_16_L , NormalDh_9_32_L }
+},{
+{ NormalDh_8_8_R, NormalDh_8_15_R , NormalDh_8_16_R , NormalDh_8_32_R },
+{ 0, NormalDh_15_15_R, NormalDh_15_16_R, NormalDh_15_32_R},
+{ 0, NormalDh_16_15_R, NormalDh_16_16_R, NormalDh_16_32_R},
+{ 0, NormalDh_32_15_R, NormalDh_32_16_R, NormalDh_32_32_R},
+{ NormalDh_8_8_R, NormalDh_9_15_R , NormalDh_9_16_R , NormalDh_9_32_R }
+}};
+
+ScalerSimpleBlock_t ScaleNormal2x = {
+ "Normal2x",
+ GFX_CAN_8|GFX_CAN_15|GFX_CAN_16|GFX_CAN_32,
+ 2,2,{
+{ Normal2x_8_8_L, Normal2x_8_15_L, Normal2x_8_16_L, Normal2x_8_32_L },
+{ 0, Normal2x_15_15_L, Normal2x_15_16_L, Normal2x_15_32_L},
+{ 0, Normal2x_16_15_L, Normal2x_16_16_L, Normal2x_16_32_L},
+{ 0, Normal2x_32_15_L, Normal2x_32_16_L, Normal2x_32_32_L},
+{ Normal2x_8_8_L, Normal2x_9_15_L , Normal2x_9_16_L, Normal2x_9_32_L }
+},{
+{ Normal2x_8_8_R, Normal2x_8_15_R , Normal2x_8_16_R, Normal2x_8_32_R },
+{ 0, Normal2x_15_15_R, Normal2x_15_16_R, Normal2x_15_32_R},
+{ 0, Normal2x_16_15_R, Normal2x_16_16_R, Normal2x_16_32_R},
+{ 0, Normal2x_32_15_R, Normal2x_32_16_R, Normal2x_32_32_R},
+{ Normal2x_8_8_R, Normal2x_9_15_R , Normal2x_9_16_R, Normal2x_9_32_R },
+}};
+
+ScalerSimpleBlock_t ScaleNormal3x = {
+ "Normal3x",
+ GFX_CAN_8|GFX_CAN_15|GFX_CAN_16|GFX_CAN_32,
+ 3,3,{
+{ Normal3x_8_8_L, Normal3x_8_15_L , Normal3x_8_16_L , Normal3x_8_32_L },
+{ 0, Normal3x_15_15_L, Normal3x_15_16_L, Normal3x_15_32_L},
+{ 0, Normal3x_16_15_L, Normal3x_16_16_L, Normal3x_16_32_L},
+{ 0, Normal3x_32_15_L, Normal3x_32_16_L, Normal3x_32_32_L},
+{ Normal3x_8_8_L, Normal3x_9_15_L , Normal3x_9_16_L , Normal3x_9_32_L }
+},{
+{ Normal3x_8_8_R, Normal3x_8_15_R , Normal3x_8_16_R , Normal3x_8_32_R },
+{ 0, Normal3x_15_15_R, Normal3x_15_16_R, Normal3x_15_32_R},
+{ 0, Normal3x_16_15_R, Normal3x_16_16_R, Normal3x_16_32_R},
+{ 0, Normal3x_32_15_R, Normal3x_32_16_R, Normal3x_32_32_R},
+{ Normal3x_8_8_R, Normal3x_9_15_R , Normal3x_9_16_R , Normal3x_9_32_R }
+}};
+
+#if RENDER_USE_ADVANCED_SCALERS>0
+ScalerSimpleBlock_t ScaleTV2x = {
+ "TV2x",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 2,2,{
+{ 0, TV2x_8_15_L , TV2x_8_16_L , TV2x_8_32_L },
+{ 0, TV2x_15_15_L, TV2x_15_16_L, TV2x_15_32_L},
+{ 0, TV2x_16_15_L, TV2x_16_16_L, TV2x_16_32_L},
+{ 0, TV2x_32_15_L, TV2x_32_16_L, TV2x_32_32_L},
+{ 0, TV2x_9_15_L , TV2x_9_16_L , TV2x_9_32_L }
+},{
+{ 0, TV2x_8_15_R , TV2x_8_16_R , TV2x_8_32_R },
+{ 0, TV2x_15_15_R, TV2x_15_16_R, TV2x_15_32_R},
+{ 0, TV2x_16_15_R, TV2x_16_16_R, TV2x_16_32_R},
+{ 0, TV2x_32_15_R, TV2x_32_16_R, TV2x_32_32_R},
+{ 0, TV2x_9_15_R , TV2x_9_16_R , TV2x_9_32_R }
+}};
+
+ScalerSimpleBlock_t ScaleTV3x = {
+ "TV3x",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 3,3,{
+{ 0, TV3x_8_15_L , TV3x_8_16_L , TV3x_8_32_L },
+{ 0, TV3x_15_15_L, TV3x_15_16_L, TV3x_15_32_L},
+{ 0, TV3x_16_15_L, TV3x_16_16_L, TV3x_16_32_L},
+{ 0, TV3x_32_15_L, TV3x_32_16_L, TV3x_32_32_L},
+{ 0, TV3x_9_15_L , TV3x_9_16_L , TV3x_9_32_L }
+},{
+{ 0, TV3x_8_15_R , TV3x_8_16_R , TV3x_8_32_R },
+{ 0, TV3x_15_15_R, TV3x_15_16_R, TV3x_15_32_R},
+{ 0, TV3x_16_15_R, TV3x_16_16_R, TV3x_16_32_R},
+{ 0, TV3x_32_15_R, TV3x_32_16_R, TV3x_32_32_R},
+{ 0, TV3x_9_15_R , TV3x_9_16_R , TV3x_9_32_R }
+}};
+
+ScalerSimpleBlock_t ScaleScan2x = {
+ "Scan2x",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 2,2,{
+{ 0, Scan2x_8_15_L , Scan2x_8_16_L , Scan2x_8_32_L },
+{ 0, Scan2x_15_15_L, Scan2x_15_16_L, Scan2x_15_32_L},
+{ 0, Scan2x_16_15_L, Scan2x_16_16_L, Scan2x_16_32_L},
+{ 0, Scan2x_32_15_L, Scan2x_32_16_L, Scan2x_32_32_L},
+{ 0, Scan2x_9_15_L , Scan2x_9_16_L , Scan2x_9_32_L }
+},{
+{ 0, Scan2x_8_15_R , Scan2x_8_16_R , Scan2x_8_32_R },
+{ 0, Scan2x_15_15_R, Scan2x_15_16_R, Scan2x_15_32_R},
+{ 0, Scan2x_16_15_R, Scan2x_16_16_R, Scan2x_16_32_R},
+{ 0, Scan2x_32_15_R, Scan2x_32_16_R, Scan2x_32_32_R},
+{ 0, Scan2x_9_15_R , Scan2x_9_16_R , Scan2x_9_32_R }
+}};
+
+ScalerSimpleBlock_t ScaleScan3x = {
+ "Scan3x",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 3,3,{
+{ 0, Scan3x_8_15_L , Scan3x_8_16_L , Scan3x_8_32_L },
+{ 0, Scan3x_15_15_L, Scan3x_15_16_L, Scan3x_15_32_L},
+{ 0, Scan3x_16_15_L, Scan3x_16_16_L, Scan3x_16_32_L},
+{ 0, Scan3x_32_15_L, Scan3x_32_16_L, Scan3x_32_32_L},
+{ 0, Scan3x_9_15_L , Scan3x_9_16_L , Scan3x_9_32_L },
+},{
+{ 0, Scan3x_8_15_R , Scan3x_8_16_R , Scan3x_8_32_R },
+{ 0, Scan3x_15_15_R, Scan3x_15_16_R, Scan3x_15_32_R},
+{ 0, Scan3x_16_15_R, Scan3x_16_16_R, Scan3x_16_32_R},
+{ 0, Scan3x_32_15_R, Scan3x_32_16_R, Scan3x_32_32_R},
+{ 0, Scan3x_9_15_R , Scan3x_9_16_R , Scan3x_9_32_R }
+}};
+
+ScalerSimpleBlock_t ScaleRGB2x = {
+ "RGB2x",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 2,2,{
+{ 0, RGB2x_8_15_L , RGB2x_8_16_L , RGB2x_8_32_L },
+{ 0, RGB2x_15_15_L, RGB2x_15_16_L, RGB2x_15_32_L},
+{ 0, RGB2x_16_15_L, RGB2x_16_16_L, RGB2x_16_32_L},
+{ 0, RGB2x_32_15_L, RGB2x_32_16_L, RGB2x_32_32_L},
+{ 0, RGB2x_9_15_L , RGB2x_9_16_L , RGB2x_9_32_L }
+},{
+{ 0, RGB2x_8_15_R , RGB2x_8_16_R , RGB2x_8_32_R },
+{ 0, RGB2x_15_15_R, RGB2x_15_16_R, RGB2x_15_32_R},
+{ 0, RGB2x_16_15_R, RGB2x_16_16_R, RGB2x_16_32_R},
+{ 0, RGB2x_32_15_R, RGB2x_32_16_R, RGB2x_32_32_R},
+{ 0, RGB2x_9_15_R , RGB2x_9_16_R , RGB2x_9_32_R }
+}};
+
+ScalerSimpleBlock_t ScaleRGB3x = {
+ "RGB3x",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 3,3,{
+{ 0, RGB3x_8_15_L , RGB3x_8_16_L , RGB3x_8_32_L },
+{ 0, RGB3x_15_15_L, RGB3x_15_16_L, RGB3x_15_32_L},
+{ 0, RGB3x_16_15_L, RGB3x_16_16_L, RGB3x_16_32_L},
+{ 0, RGB3x_32_15_L, RGB3x_32_16_L, RGB3x_32_32_L},
+{ 0, RGB3x_9_15_L , RGB3x_9_16_L , RGB3x_9_32_L }
+},{
+{ 0, RGB3x_8_15_R , RGB3x_8_16_R , RGB3x_8_32_R },
+{ 0, RGB3x_15_15_R, RGB3x_15_16_R, RGB3x_15_32_R},
+{ 0, RGB3x_16_15_R, RGB3x_16_16_R, RGB3x_16_32_R},
+{ 0, RGB3x_32_15_R, RGB3x_32_16_R, RGB3x_32_32_R},
+{ 0, RGB3x_9_15_R , RGB3x_9_16_R , RGB3x_9_32_R }
+}};
+#endif
+
+
+/* Complex scalers */
+
+#if RENDER_USE_ADVANCED_SCALERS>2
+ScalerComplexBlock_t ScaleAdvMame2x ={
+ "AdvMame2x",
+ GFX_CAN_8|GFX_CAN_15|GFX_CAN_16|GFX_CAN_32,
+ 2,2,
+{ AdvMame2x_8_L,AdvMame2x_16_L,AdvMame2x_16_L,AdvMame2x_32_L},
+{ AdvMame2x_8_R,AdvMame2x_16_R,AdvMame2x_16_R,AdvMame2x_32_R}
+};
+
+ScalerComplexBlock_t ScaleAdvMame3x = {
+ "AdvMame3x",
+ GFX_CAN_8|GFX_CAN_15|GFX_CAN_16|GFX_CAN_32,
+ 3,3,
+{ AdvMame3x_8_L,AdvMame3x_16_L,AdvMame3x_16_L,AdvMame3x_32_L},
+{ AdvMame3x_8_R,AdvMame3x_16_R,AdvMame3x_16_R,AdvMame3x_32_R}
+};
+
+/* These need specific 15bpp versions */
+ScalerComplexBlock_t ScaleHQ2x ={
+ "HQ2x",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 2,2,
+{ 0,HQ2x_16_L,HQ2x_16_L,HQ2x_32_L},
+{ 0,HQ2x_16_R,HQ2x_16_R,HQ2x_32_R}
+};
+
+ScalerComplexBlock_t ScaleHQ3x ={
+ "HQ3x",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 3,3,
+{ 0,HQ3x_16_L,HQ3x_16_L,HQ3x_32_L},
+{ 0,HQ3x_16_R,HQ3x_16_R,HQ3x_32_R}
+};
+
+ScalerComplexBlock_t ScaleSuper2xSaI ={
+ "Super2xSaI",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 2,2,
+{ 0,Super2xSaI_16_L,Super2xSaI_16_L,Super2xSaI_32_L},
+{ 0,Super2xSaI_16_R,Super2xSaI_16_R,Super2xSaI_32_R}
+};
+
+ScalerComplexBlock_t Scale2xSaI ={
+ "2xSaI",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 2,2,
+{ 0,_2xSaI_16_L,_2xSaI_16_L,_2xSaI_32_L},
+{ 0,_2xSaI_16_R,_2xSaI_16_R,_2xSaI_32_R}
+};
+
+ScalerComplexBlock_t ScaleSuperEagle ={
+ "SuperEagle",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 2,2,
+{ 0,SuperEagle_16_L,SuperEagle_16_L,SuperEagle_32_L},
+{ 0,SuperEagle_16_R,SuperEagle_16_R,SuperEagle_32_R}
+};
+
+ScalerComplexBlock_t ScaleAdvInterp2x = {
+ "AdvInterp2x",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 2,2,
+{ 0,AdvInterp2x_15_L,AdvInterp2x_16_L,AdvInterp2x_32_L},
+{ 0,AdvInterp2x_15_R,AdvInterp2x_16_R,AdvInterp2x_32_R}
+};
+
+ScalerComplexBlock_t ScaleAdvInterp3x = {
+ "AdvInterp3x",
+ GFX_CAN_15|GFX_CAN_16|GFX_CAN_32|GFX_RGBONLY,
+ 3,3,
+{ 0,AdvInterp3x_15_L,AdvInterp3x_16_L,AdvInterp3x_32_L},
+{ 0,AdvInterp3x_15_R,AdvInterp3x_16_R,AdvInterp3x_32_R}
+};
+
+#endif
diff --git a/src/gui/render_scalers.h b/src/gui/render_scalers.h
new file mode 100644
index 000000000..f4351cb6b
--- /dev/null
+++ b/src/gui/render_scalers.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef _RENDER_SCALERS_H
+#define _RENDER_SCALERS_H
+
+//#include "render.h"
+#include "video.h"
+#if RENDER_USE_ADVANCED_SCALERS>0
+#define SCALER_MAXWIDTH 1280
+#define SCALER_MAXHEIGHT 1024
+#else
+// reduced to save some memory
+#define SCALER_MAXWIDTH 800
+#define SCALER_MAXHEIGHT 600
+#endif
+
+#if RENDER_USE_ADVANCED_SCALERS>1
+#define SCALER_COMPLEXWIDTH 800
+#define SCALER_COMPLEXHEIGHT 600
+#endif
+
+#define SCALER_BLOCKSIZE 16
+
+typedef enum {
+ scalerMode8, scalerMode15, scalerMode16, scalerMode32
+} scalerMode_t;
+
+typedef enum scalerOperation {
+ scalerOpNormal,
+#if RENDER_USE_ADVANCED_SCALERS>2
+ scalerOpAdvMame,
+ scalerOpAdvInterp,
+ scalerOpHQ,
+ scalerOpSaI,
+ scalerOpSuperSaI,
+ scalerOpSuperEagle,
+#endif
+#if RENDER_USE_ADVANCED_SCALERS>0
+ scalerOpTV,
+ scalerOpRGB,
+ scalerOpScan,
+#endif
+ scalerLast
+} scalerOperation_t;
+
+typedef void (*ScalerLineHandler_t)(const void *src);
+typedef void (*ScalerComplexHandler_t)(void);
+
+extern Bit8u Scaler_Aspect[];
+extern Bit8u diff_table[];
+extern Bitu Scaler_ChangedLineIndex;
+extern Bit16u Scaler_ChangedLines[];
+#if RENDER_USE_ADVANCED_SCALERS>1
+/* Not entirely happy about those +2's since they make a non power of 2, with muls instead of shift */
+typedef Bit8u scalerChangeCache_t [SCALER_COMPLEXHEIGHT][SCALER_COMPLEXWIDTH / SCALER_BLOCKSIZE] ;
+typedef union {
+ Bit32u b32 [SCALER_COMPLEXHEIGHT] [SCALER_COMPLEXWIDTH];
+ Bit16u b16 [SCALER_COMPLEXHEIGHT] [SCALER_COMPLEXWIDTH];
+ Bit8u b8 [SCALER_COMPLEXHEIGHT] [SCALER_COMPLEXWIDTH];
+} scalerFrameCache_t;
+#endif
+typedef union {
+ Bit32u b32 [SCALER_MAXHEIGHT] [SCALER_MAXWIDTH];
+ Bit16u b16 [SCALER_MAXHEIGHT] [SCALER_MAXWIDTH];
+ Bit8u b8 [SCALER_MAXHEIGHT] [SCALER_MAXWIDTH];
+} scalerSourceCache_t;
+extern scalerSourceCache_t scalerSourceCache;
+#if RENDER_USE_ADVANCED_SCALERS>1
+extern scalerChangeCache_t scalerChangeCache;
+#endif
+typedef ScalerLineHandler_t ScalerLineBlock_t[5][4];
+
+typedef struct {
+ const char *name;
+ Bitu gfxFlags;
+ Bitu xscale,yscale;
+ ScalerComplexHandler_t Linear[4];
+ ScalerComplexHandler_t Random[4];
+} ScalerComplexBlock_t;
+
+typedef struct {
+ const char *name;
+ Bitu gfxFlags;
+ Bitu xscale,yscale;
+ ScalerLineBlock_t Linear;
+ ScalerLineBlock_t Random;
+} ScalerSimpleBlock_t;
+
+
+#define SCALE_LEFT 0x1
+#define SCALE_RIGHT 0x2
+#define SCALE_FULL 0x4
+
+/* Simple scalers */
+extern ScalerSimpleBlock_t ScaleNormal1x;
+extern ScalerSimpleBlock_t ScaleNormalDw;
+extern ScalerSimpleBlock_t ScaleNormalDh;
+extern ScalerSimpleBlock_t ScaleNormal2x;
+extern ScalerSimpleBlock_t ScaleNormal3x;
+#if RENDER_USE_ADVANCED_SCALERS>0
+extern ScalerSimpleBlock_t ScaleTV2x;
+extern ScalerSimpleBlock_t ScaleTV3x;
+extern ScalerSimpleBlock_t ScaleRGB2x;
+extern ScalerSimpleBlock_t ScaleRGB3x;
+extern ScalerSimpleBlock_t ScaleScan2x;
+extern ScalerSimpleBlock_t ScaleScan3x;
+#endif
+/* Complex scalers */
+#if RENDER_USE_ADVANCED_SCALERS>2
+extern ScalerComplexBlock_t ScaleHQ2x;
+extern ScalerComplexBlock_t ScaleHQ3x;
+extern ScalerComplexBlock_t Scale2xSaI;
+extern ScalerComplexBlock_t ScaleSuper2xSaI;
+extern ScalerComplexBlock_t ScaleSuperEagle;
+extern ScalerComplexBlock_t ScaleAdvMame2x;
+extern ScalerComplexBlock_t ScaleAdvMame3x;
+extern ScalerComplexBlock_t ScaleAdvInterp2x;
+extern ScalerComplexBlock_t ScaleAdvInterp3x;
+#endif
+#if RENDER_USE_ADVANCED_SCALERS>1
+extern ScalerLineBlock_t ScalerCache;
+#endif
+#endif
diff --git a/src/gui/render_simple.h b/src/gui/render_simple.h
new file mode 100644
index 000000000..f6e587d7b
--- /dev/null
+++ b/src/gui/render_simple.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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 (SCALERLINEAR)
+static void conc4d(SCALERNAME,SBPP,DBPP,L)(const void *s) {
+#else
+static void conc4d(SCALERNAME,SBPP,DBPP,R)(const void *s) {
+#endif
+#ifdef RENDER_NULL_INPUT
+ if (!s) {
+ render.scale.cacheRead += render.scale.cachePitch;
+#if defined(SCALERLINEAR)
+ Bitu skipLines = SCALERHEIGHT;
+#else
+ Bitu skipLines = Scaler_Aspect[ render.scale.outLine++ ];
+#endif
+ ScalerAddLines( 0, skipLines );
+ return;
+ }
+#endif
+ /* Clear the complete line marker */
+ Bitu hadChange = 0;
+ const SRCTYPE *src = (SRCTYPE*)s;
+ SRCTYPE *cache = (SRCTYPE*)(render.scale.cacheRead);
+ render.scale.cacheRead += render.scale.cachePitch;
+ PTYPE * line0=(PTYPE *)(render.scale.outWrite);
+#if (SBPP == 9)
+ for (Bits x=render.src.width;x>0;) {
+ if (*(Bit32u const*)src == *(Bit32u*)cache && !(
+ render.pal.modified[src[0]] |
+ render.pal.modified[src[1]] |
+ render.pal.modified[src[2]] |
+ render.pal.modified[src[3]] )) {
+ x-=4;
+ src+=4;
+ cache+=4;
+ line0+=4*SCALERWIDTH;
+#else
+ for (Bits x=render.src.width;x>0;) {
+ if (*(Bitu const*)src == *(Bitu*)cache) {
+ x-=(sizeof(Bitu)/sizeof(SRCTYPE));
+ src+=(sizeof(Bitu)/sizeof(SRCTYPE));
+ cache+=(sizeof(Bitu)/sizeof(SRCTYPE));
+ line0+=(sizeof(Bitu)/sizeof(SRCTYPE))*SCALERWIDTH;
+#endif
+ } else {
+#if defined(SCALERLINEAR)
+#if (SCALERHEIGHT > 1)
+ PTYPE *line1 = WC[0];
+#endif
+#if (SCALERHEIGHT > 2)
+ PTYPE *line2 = WC[1];
+#endif
+#else
+#if (SCALERHEIGHT > 1)
+ PTYPE *line1 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch);
+#endif
+#if (SCALERHEIGHT > 2)
+ PTYPE *line2 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch * 2);
+#endif
+#endif //defined(SCALERLINEAR)
+ hadChange = 1;
+ for (Bitu i = x > 32 ? 32 : x;i>0;i--,x--) {
+ const SRCTYPE S = *src;
+ *cache = S;
+ src++;cache++;
+ const PTYPE P = PMAKE(S);
+ SCALERFUNC;
+ line0 += SCALERWIDTH;
+#if (SCALERHEIGHT > 1)
+ line1 += SCALERWIDTH;
+#endif
+#if (SCALERHEIGHT > 2)
+ line2 += SCALERWIDTH;
+#endif
+ }
+#if defined(SCALERLINEAR)
+#if (SCALERHEIGHT > 1)
+ Bitu copyLen = (Bitu)((Bit8u*)line1 - (Bit8u*)WC[0]);
+ BituMove(((Bit8u*)line0)-copyLen+render.scale.outPitch ,WC[0], copyLen );
+#endif
+#if (SCALERHEIGHT > 2)
+ BituMove(((Bit8u*)line0)-copyLen+render.scale.outPitch*2,WC[1], copyLen );
+#endif
+#endif //defined(SCALERLINEAR)
+ }
+ }
+#if defined(SCALERLINEAR)
+ Bitu scaleLines = SCALERHEIGHT;
+#else
+ Bitu scaleLines = Scaler_Aspect[ render.scale.outLine++ ];
+ if ( scaleLines - SCALERHEIGHT && hadChange ) {
+ BituMove( render.scale.outWrite + render.scale.outPitch * SCALERHEIGHT,
+ render.scale.outWrite + render.scale.outPitch * (SCALERHEIGHT-1),
+ render.src.width * SCALERWIDTH * PSIZE);
+ }
+#endif
+ ScalerAddLines( hadChange, scaleLines );
+}
+
+#if !defined(SCALERLINEAR)
+#define SCALERLINEAR 1
+#include "render_simple.h"
+#undef SCALERLINEAR
+#endif
diff --git a/src/gui/render_templates.h b/src/gui/render_templates.h
new file mode 100644
index 000000000..30643937b
--- /dev/null
+++ b/src/gui/render_templates.h
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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 DBPP == 8
+#define PSIZE 1
+#define PTYPE Bit8u
+#define WC scalerWriteCache.b8
+//#define FC scalerFrameCache.b8
+#define FC (*(scalerFrameCache_t*)(&scalerSourceCache.b32[400][0])).b8
+#define redMask 0
+#define greenMask 0
+#define blueMask 0
+#define redBits 0
+#define greenBits 0
+#define blueBits 0
+#define redShift 0
+#define greenShift 0
+#define blueShift 0
+#elif DBPP == 15 || DBPP == 16
+#define PSIZE 2
+#define PTYPE Bit16u
+#define WC scalerWriteCache.b16
+//#define FC scalerFrameCache.b16
+#define FC (*(scalerFrameCache_t*)(&scalerSourceCache.b32[400][0])).b16
+#if DBPP == 15
+#define redMask 0x7C00
+#define greenMask 0x03E0
+#define blueMask 0x001F
+#define redBits 5
+#define greenBits 5
+#define blueBits 5
+#define redShift 10
+#define greenShift 5
+#define blueShift 0
+#elif DBPP == 16
+#define redMask 0xF800
+#define greenMask 0x07E0
+#define blueMask 0x001F
+#define redBits 5
+#define greenBits 6
+#define blueBits 5
+#define redShift 11
+#define greenShift 5
+#define blueShift 0
+#endif
+#elif DBPP == 32
+#define PSIZE 4
+#define PTYPE Bit32u
+#define WC scalerWriteCache.b32
+//#define FC scalerFrameCache.b32
+#define FC (*(scalerFrameCache_t*)(&scalerSourceCache.b32[400][0])).b32
+#define redMask 0xff0000
+#define greenMask 0x00ff00
+#define blueMask 0x0000ff
+#define redBits 8
+#define greenBits 8
+#define blueBits 8
+#define redShift 16
+#define greenShift 8
+#define blueShift 0
+#endif
+
+#define redblueMask (redMask | blueMask)
+
+
+#if SBPP == 8 || SBPP == 9
+#define SC scalerSourceCache.b8
+#if DBPP == 8
+#define PMAKE(_VAL) (_VAL)
+#elif DBPP == 15
+#define PMAKE(_VAL) render.pal.lut.b16[_VAL]
+#elif DBPP == 16
+#define PMAKE(_VAL) render.pal.lut.b16[_VAL]
+#elif DBPP == 32
+#define PMAKE(_VAL) render.pal.lut.b32[_VAL]
+#endif
+#define SRCTYPE Bit8u
+#endif
+
+#if SBPP == 15
+#define SC scalerSourceCache.b16
+#if DBPP == 15
+#define PMAKE(_VAL) (_VAL)
+#elif DBPP == 16
+#define PMAKE(_VAL) (((_VAL) & 31) | ((_VAL) & ~31) << 1)
+#elif DBPP == 32
+#define PMAKE(_VAL) (((_VAL&(31<<10))<<9)|((_VAL&(31<<5))<<6)|((_VAL&31)<<3))
+#endif
+#define SRCTYPE Bit16u
+#endif
+
+#if SBPP == 16
+#define SC scalerSourceCache.b16
+#if DBPP == 15
+#define PMAKE(_VAL) (((_VAL&~31)>>1)|(_VAL&31))
+#elif DBPP == 16
+#define PMAKE(_VAL) (_VAL)
+#elif DBPP == 32
+#define PMAKE(_VAL) (((_VAL&(31<<11))<<8)|((_VAL&(63<<5))<<5)|((_VAL&31)<<3))
+#endif
+#define SRCTYPE Bit16u
+#endif
+
+#if SBPP == 32
+#define SC scalerSourceCache.b32
+#if DBPP == 15
+#define PMAKE(_VAL) (PTYPE)(((_VAL&(31<<19))>>9)|((_VAL&(31<<11))>>6)|((_VAL&(31<<3))>>3))
+#elif DBPP == 16
+#define PMAKE(_VAL) (PTYPE)(((_VAL&(31<<19))>>8)|((_VAL&(63<<10))>>4)|((_VAL&(31<<3))>>3))
+#elif DBPP == 32
+#define PMAKE(_VAL) (_VAL)
+#endif
+#define SRCTYPE Bit32u
+#endif
+
+// C0 C1 C2 D3
+// C3 C4 C5 D4
+// C6 C7 C8 D5
+// D0 D1 D2 D6
+
+#define C0 fc[-1 - SCALER_COMPLEXWIDTH]
+#define C1 fc[+0 - SCALER_COMPLEXWIDTH]
+#define C2 fc[+1 - SCALER_COMPLEXWIDTH]
+#define C3 fc[-1 ]
+#define C4 fc[+0 ]
+#define C5 fc[+1 ]
+#define C6 fc[-1 + SCALER_COMPLEXWIDTH]
+#define C7 fc[+0 + SCALER_COMPLEXWIDTH]
+#define C8 fc[+1 + SCALER_COMPLEXWIDTH]
+
+#define D0 fc[-1 + 2*SCALER_COMPLEXWIDTH]
+#define D1 fc[+0 + 2*SCALER_COMPLEXWIDTH]
+#define D2 fc[+1 + 2*SCALER_COMPLEXWIDTH]
+#define D3 fc[+2 - SCALER_COMPLEXWIDTH]
+#define D4 fc[+2]
+#define D5 fc[+2 + SCALER_COMPLEXWIDTH]
+#define D6 fc[+2 + 2*SCALER_COMPLEXWIDTH]
+
+
+#if RENDER_USE_ADVANCED_SCALERS>1
+static void conc3d(Cache,SBPP,DBPP) (const void * s) {
+#ifdef RENDER_NULL_INPUT
+ if (!s) {
+ render.scale.cacheRead += render.scale.cachePitch;
+ render.scale.inLine++;
+ render.scale.complexHandler();
+ return;
+ }
+#endif
+ const SRCTYPE * src = (SRCTYPE*)s;
+ PTYPE *fc= &FC[render.scale.inLine+1][1];
+ SRCTYPE *sc = (SRCTYPE*)(render.scale.cacheRead);
+ render.scale.cacheRead += render.scale.cachePitch;
+ Bitu b;
+ bool hadChange = false;
+ /* This should also copy the surrounding pixels but it looks nice enough without */
+ for (b=0;b<render.scale.blocks;b++) {
+#if (SBPP == 9)
+ for (Bitu x=0;x<SCALER_BLOCKSIZE;x++) {
+ PTYPE pixel = PMAKE(src[x]);
+ if (pixel != fc[x]) {
+#else
+ for (Bitu x=0;x<SCALER_BLOCKSIZE;x+=sizeof(Bitu)/sizeof(SRCTYPE)) {
+ if (*(Bitu const*)&src[x] != *(Bitu*)&sc[x]) {
+#endif
+ do {
+ fc[x] = PMAKE(src[x]);
+ sc[x] = src[x];
+ x++;
+ } while (x<SCALER_BLOCKSIZE);
+ hadChange = true;
+ /* Change the surrounding blocks */
+ CC[render.scale.inLine+0][1+b-1] |= SCALE_RIGHT;
+ CC[render.scale.inLine+0][1+b+0] |= SCALE_FULL;
+ CC[render.scale.inLine+0][1+b+1] |= SCALE_LEFT;
+ CC[render.scale.inLine+1][1+b-1] |= SCALE_RIGHT;
+ CC[render.scale.inLine+1][1+b+0] |= SCALE_FULL;
+ CC[render.scale.inLine+1][1+b+1] |= SCALE_LEFT;
+ CC[render.scale.inLine+2][1+b-1] |= SCALE_RIGHT;
+ CC[render.scale.inLine+2][1+b+0] |= SCALE_FULL;
+ CC[render.scale.inLine+2][1+b+1] |= SCALE_LEFT;
+ continue;
+ }
+ }
+ fc += SCALER_BLOCKSIZE;
+ sc += SCALER_BLOCKSIZE;
+ src += SCALER_BLOCKSIZE;
+ }
+ if (hadChange) {
+ CC[render.scale.inLine+0][0] = 1;
+ CC[render.scale.inLine+1][0] = 1;
+ CC[render.scale.inLine+2][0] = 1;
+ }
+ render.scale.inLine++;
+ render.scale.complexHandler();
+}
+#endif
+
+
+/* Simple scalers */
+#define SCALERNAME Normal1x
+#define SCALERWIDTH 1
+#define SCALERHEIGHT 1
+#define SCALERFUNC \
+ line0[0] = P;
+#include "render_simple.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME Normal2x
+#define SCALERWIDTH 2
+#define SCALERHEIGHT 2
+#define SCALERFUNC \
+ line0[0] = P; \
+ line0[1] = P; \
+ line1[0] = P; \
+ line1[1] = P;
+#include "render_simple.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME Normal3x
+#define SCALERWIDTH 3
+#define SCALERHEIGHT 3
+#define SCALERFUNC \
+ line0[0] = P; \
+ line0[1] = P; \
+ line0[2] = P; \
+ line1[0] = P; \
+ line1[1] = P; \
+ line1[2] = P; \
+ line2[0] = P; \
+ line2[1] = P; \
+ line2[2] = P;
+#include "render_simple.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME NormalDw
+#define SCALERWIDTH 2
+#define SCALERHEIGHT 1
+#define SCALERFUNC \
+ line0[0] = P; \
+ line0[1] = P;
+#include "render_simple.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME NormalDh
+#define SCALERWIDTH 1
+#define SCALERHEIGHT 2
+#define SCALERFUNC \
+ line0[0] = P; \
+ line1[0] = P;
+#include "render_simple.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#if (DBPP > 8)
+
+#if RENDER_USE_ADVANCED_SCALERS>0
+
+#define SCALERNAME TV2x
+#define SCALERWIDTH 2
+#define SCALERHEIGHT 2
+#define SCALERFUNC \
+{ \
+ Bitu halfpixel=(((P & redblueMask) * 5) >> 3) & redblueMask; \
+ halfpixel|=(((P & greenMask) * 5) >> 3) & greenMask; \
+ line0[0]=P; \
+ line0[1]=P; \
+ line1[0]=halfpixel; \
+ line1[1]=halfpixel; \
+}
+#include "render_simple.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME TV3x
+#define SCALERWIDTH 3
+#define SCALERHEIGHT 3
+#define SCALERFUNC \
+{ \
+ Bitu halfpixel=(((P & redblueMask) * 5) >> 3) & redblueMask; \
+ halfpixel|=(((P & greenMask) * 5) >> 3) & greenMask; \
+ line0[0]=P; \
+ line0[1]=P; \
+ line0[2]=P; \
+ line1[0]=halfpixel; \
+ line1[1]=halfpixel; \
+ line1[2]=halfpixel; \
+ halfpixel=(((P & redblueMask) * 5) >> 4) & redblueMask; \
+ halfpixel|=(((P & greenMask) * 5) >> 4) & greenMask; \
+ line2[0]=halfpixel; \
+ line2[1]=halfpixel; \
+ line2[2]=halfpixel; \
+}
+#include "render_simple.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME RGB2x
+#define SCALERWIDTH 2
+#define SCALERHEIGHT 2
+#define SCALERFUNC \
+ line0[0]=P & redMask; \
+ line0[1]=P & greenMask; \
+ line1[0]=P & blueMask; \
+ line1[1]=P;
+#include "render_simple.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME RGB3x
+#define SCALERWIDTH 3
+#define SCALERHEIGHT 3
+#define SCALERFUNC \
+ line0[0]=P; \
+ line0[1]=P & greenMask; \
+ line0[2]=P & blueMask; \
+ line1[0]=P & greenMask; \
+ line1[1]=P & redMask; \
+ line1[2]=P; \
+ line2[0]=P; \
+ line2[1]=P & blueMask; \
+ line2[2]=P & redMask;
+#include "render_simple.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME Scan2x
+#define SCALERWIDTH 2
+#define SCALERHEIGHT 2
+#define SCALERFUNC \
+ line0[0]=P; \
+ line0[1]=P; \
+ line1[0]=0; \
+ line1[1]=0;
+#include "render_simple.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME Scan3x
+#define SCALERWIDTH 3
+#define SCALERHEIGHT 3
+#define SCALERFUNC \
+ line0[0]=P; \
+ line0[1]=P; \
+ line0[2]=P; \
+ line1[0]=P; \
+ line1[1]=P; \
+ line1[2]=P; \
+ line2[0]=0; \
+ line2[1]=0; \
+ line2[2]=0;
+#include "render_simple.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#endif //#if RENDER_USE_ADVANCED_SCALERS>0
+
+#endif //#if (DBPP > 8)
+
+/* Complex scalers */
+
+#if RENDER_USE_ADVANCED_SCALERS>2
+
+#if (SBPP == DBPP)
+
+
+#if (DBPP > 8)
+
+#include "render_templates_hq.h"
+
+#define SCALERNAME HQ2x
+#define SCALERWIDTH 2
+#define SCALERHEIGHT 2
+#include "render_templates_hq2x.h"
+#define SCALERFUNC conc2d(Hq2x,SBPP)(line0, line1, fc)
+#include "render_loops.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME HQ3x
+#define SCALERWIDTH 3
+#define SCALERHEIGHT 3
+#include "render_templates_hq3x.h"
+#define SCALERFUNC conc2d(Hq3x,SBPP)(line0, line1, line2, fc)
+#include "render_loops.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#include "render_templates_sai.h"
+
+#define SCALERNAME Super2xSaI
+#define SCALERWIDTH 2
+#define SCALERHEIGHT 2
+#define SCALERFUNC conc2d(Super2xSaI,SBPP)(line0, line1, fc)
+#include "render_loops.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME SuperEagle
+#define SCALERWIDTH 2
+#define SCALERHEIGHT 2
+#define SCALERFUNC conc2d(SuperEagle,SBPP)(line0, line1, fc)
+#include "render_loops.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME _2xSaI
+#define SCALERWIDTH 2
+#define SCALERHEIGHT 2
+#define SCALERFUNC conc2d(_2xSaI,SBPP)(line0, line1, fc)
+#include "render_loops.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME AdvInterp2x
+#define SCALERWIDTH 2
+#define SCALERHEIGHT 2
+#define SCALERFUNC \
+ if (C1 != C7 && C3 != C5) { \
+ line0[0] = C3 == C1 ? interp_w2(C3,C4,5U,3U) : C4; \
+ line0[1] = C1 == C5 ? interp_w2(C5,C4,5U,3U) : C4; \
+ line1[0] = C3 == C7 ? interp_w2(C3,C4,5U,3U) : C4; \
+ line1[1] = C7 == C5 ? interp_w2(C5,C4,5U,3U) : C4; \
+ } else { \
+ line0[0] = line0[1] = C4; \
+ line1[0] = line1[1] = C4; \
+ }
+#include "render_loops.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+//TODO, come up with something better for this one
+#define SCALERNAME AdvInterp3x
+#define SCALERWIDTH 3
+#define SCALERHEIGHT 3
+#define SCALERFUNC \
+ if ((C1 != C7) && (C3 != C5)) { \
+ line0[0] = C3 == C1 ? interp_w2(C3,C4,5U,3U) : C4; \
+ line0[1] = (C3 == C1 && C4 != C2) || (C5 == C1 && C4 != C0) ? C1 : C4; \
+ line0[2] = C5 == C1 ? interp_w2(C5,C4,5U,3U) : C4; \
+ line1[0] = (C3 == C1 && C4 != C6) || (C3 == C7 && C4 != C0) ? C3 : C4; \
+ line1[1] = C4; \
+ line1[2] = (C5 == C1 && C4 != C8) || (C5 == C7 && C4 != C2) ? C5 : C4; \
+ line2[0] = C3 == C7 ? interp_w2(C3,C4,5U,3U) : C4; \
+ line2[1] = (C3 == C7 && C4 != C8) || (C5 == C7 && C4 != C6) ? C7 : C4; \
+ line2[2] = C5 == C7 ? interp_w2(C5,C4,5U,3U) : C4; \
+ } else { \
+ line0[0] = line0[1] = line0[2] = C4; \
+ line1[0] = line1[1] = line1[2] = C4; \
+ line2[0] = line2[1] = line2[2] = C4; \
+ }
+#include "render_loops.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#endif // #if (DBPP > 8)
+
+#define SCALERNAME AdvMame2x
+#define SCALERWIDTH 2
+#define SCALERHEIGHT 2
+#define SCALERFUNC \
+ if (C1 != C7 && C3 != C5) { \
+ line0[0] = C3 == C1 ? C3 : C4; \
+ line0[1] = C1 == C5 ? C5 : C4; \
+ line1[0] = C3 == C7 ? C3 : C4; \
+ line1[1] = C7 == C5 ? C5 : C4; \
+ } else { \
+ line0[0] = line0[1] = C4; \
+ line1[0] = line1[1] = C4; \
+ }
+#include "render_loops.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+#define SCALERNAME AdvMame3x
+#define SCALERWIDTH 3
+#define SCALERHEIGHT 3
+#define SCALERFUNC \
+ if ((C1 != C7) && (C3 != C5)) { \
+ line0[0] = C3 == C1 ? C3 : C4; \
+ line0[1] = (C3 == C1 && C4 != C2) || (C5 == C1 && C4 != C0) ? C1 : C4; \
+ line0[2] = C5 == C1 ? C5 : C4; \
+ line1[0] = (C3 == C1 && C4 != C6) || (C3 == C7 && C4 != C0) ? C3 : C4; \
+ line1[1] = C4; \
+ line1[2] = (C5 == C1 && C4 != C8) || (C5 == C7 && C4 != C2) ? C5 : C4; \
+ line2[0] = C3 == C7 ? C3 : C4; \
+ line2[1] = (C3 == C7 && C4 != C8) || (C5 == C7 && C4 != C6) ? C7 : C4; \
+ line2[2] = C5 == C7 ? C5 : C4; \
+ } else { \
+ line0[0] = line0[1] = line0[2] = C4; \
+ line1[0] = line1[1] = line1[2] = C4; \
+ line2[0] = line2[1] = line2[2] = C4; \
+ }
+
+#include "render_loops.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
+
+#endif // (SBPP == DBPP) && !defined (CACHEWITHPAL)
+
+#endif // #if RENDER_USE_ADVANCED_SCALERS>2
+
+#undef PSIZE
+#undef PTYPE
+#undef PMAKE
+#undef WC
+#undef LC
+#undef FC
+#undef SC
+#undef redMask
+#undef greenMask
+#undef blueMask
+#undef redblueMask
+#undef redBits
+#undef greenBits
+#undef blueBits
+#undef redShift
+#undef greenShift
+#undef blueShift
+#undef SRCTYPE
diff --git a/src/gui/render_templates_hq.h b/src/gui/render_templates_hq.h
new file mode 100644
index 000000000..3bfd65e3f
--- /dev/null
+++ b/src/gui/render_templates_hq.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+/*
+ * The HQ3x high quality 3x graphics filter.
+ * Original author Maxim Stepin (see http://www.hiend3d.com/hq3x.html).
+ * Adapted for DOSBox from ScummVM and HiEnd3D code by Kronuz.
+ */
+
+#include <stdlib.h>
+
+#ifndef RENDER_TEMPLATES_HQNX_TABLE_H
+#define RENDER_TEMPLATES_HQNX_TABLE_H
+
+static Bit32u *_RGBtoYUV = 0;
+static inline bool diffYUV(Bit32u yuv1, Bit32u yuv2)
+{
+ static const Bit32u Ymask = 0x00FF0000;
+ static const Bit32u Umask = 0x0000FF00;
+ static const Bit32u Vmask = 0x000000FF;
+ static const Bit32u trY = 0x00300000;
+ static const Bit32u trU = 0x00000700;
+ static const Bit32u trV = 0x00000006;
+
+ Bit32u diff;
+ Bit32u mask;
+
+ diff = ((yuv1 & Ymask) - (yuv2 & Ymask));
+ mask = ((Bit32s)diff) >> 31; // ~1/-1 if value < 0, 0 otherwise
+ diff = (diff ^ mask) - mask; //-1: ~value + 1; 0: value
+ if (diff > trY) return true;
+
+ diff = ((yuv1 & Umask) - (yuv2 & Umask));
+ mask = ((Bit32s)diff)>> 31; // ~1/-1 if value < 0, 0 otherwise
+ diff = (diff ^ mask) - mask; //-1: ~value + 1; 0: value
+ if (diff > trU) return true;
+
+ diff = ((yuv1 & Vmask) - (yuv2 & Vmask));
+ mask = ((Bit32s)diff) >> 31; // ~1/-1 if value < 0, 0 otherwise
+ diff = (diff ^ mask) - mask; //-1: ~value + 1; 0: value
+ if (diff > trV) return true;
+
+ return false;
+}
+
+#endif
+
+static inline void conc2d(InitLUTs,SBPP)(void)
+{
+ int r, g, b;
+ int Y, u, v;
+
+ _RGBtoYUV = (Bit32u *)malloc(65536 * sizeof(Bit32u));
+
+ for (int color = 0; color < 65536; ++color) {
+#if SBPP == 32
+ r = ((color & 0xF800) >> 11) << (8 - 5);
+ g = ((color & 0x07E0) >> 5) << (8 - 6);
+ b = ((color & 0x001F) >> 0) << (8 - 5);
+#else
+ r = ((color & redMask) >> redShift) << (8 - redBits);
+ g = ((color & greenMask) >> greenShift) << (8 - greenBits);
+ b = ((color & blueMask) >> blueShift) << (8 - blueBits);
+#endif
+ Y = (r + g + b) >> 2;
+ u = 128 + ((r - b) >> 2);
+ v = 128 + ((-r + 2 * g - b) >> 3);
+ _RGBtoYUV[color] = (Y << 16) | (u << 8) | v;
+ }
+}
diff --git a/src/gui/render_templates_hq2x.h b/src/gui/render_templates_hq2x.h
new file mode 100644
index 000000000..bcec35754
--- /dev/null
+++ b/src/gui/render_templates_hq2x.h
@@ -0,0 +1,1896 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+/*
+ * The HQ2x high quality 2x graphics filter.
+ * Original author Maxim Stepin (see http://www.hiend3d.com/hq2x.html).
+ * Adapted for DOSBox from ScummVM and HiEnd3D code by Kronuz.
+ */
+
+#ifndef RENDER_TEMPLATES_HQ2X_TABLE_H
+#define RENDER_TEMPLATES_HQ2X_TABLE_H
+
+#define PIXEL00_0 line0[0] = C4;
+#define PIXEL00_10 line0[0] = interp_w2(C4,C0,3U,1U);
+#define PIXEL00_11 line0[0] = interp_w2(C4,C3,3U,1U);
+#define PIXEL00_12 line0[0] = interp_w2(C4,C1,3U,1U);
+#define PIXEL00_20 line0[0] = interp_w3(C4,C3,C1,2U,1U,1U);
+#define PIXEL00_21 line0[0] = interp_w3(C4,C0,C1,2U,1U,1U);
+#define PIXEL00_22 line0[0] = interp_w3(C4,C0,C3,2U,1U,1U);
+#define PIXEL00_60 line0[0] = interp_w3(C4,C1,C3,5U,2U,1U);
+#define PIXEL00_61 line0[0] = interp_w3(C4,C3,C1,5U,2U,1U);
+#define PIXEL00_70 line0[0] = interp_w3(C4,C3,C1,6U,1U,1U);
+#define PIXEL00_90 line0[0] = interp_w3(C4,C3,C1,2U,3U,3U);
+#define PIXEL00_100 line0[0] = interp_w3(C4,C3,C1,14U,1U,1U);
+
+#define PIXEL01_0 line0[1] = C4;
+#define PIXEL01_10 line0[1] = interp_w2(C4,C2,3U,1U);
+#define PIXEL01_11 line0[1] = interp_w2(C4,C1,3U,1U);
+#define PIXEL01_12 line0[1] = interp_w2(C4,C5,3U,1U);
+#define PIXEL01_20 line0[1] = interp_w3(C4,C1,C5,2U,1U,1U);
+#define PIXEL01_21 line0[1] = interp_w3(C4,C2,C5,2U,1U,1U);
+#define PIXEL01_22 line0[1] = interp_w3(C4,C2,C1,2U,1U,1U);
+#define PIXEL01_60 line0[1] = interp_w3(C4,C5,C1,5U,2U,1U);
+#define PIXEL01_61 line0[1] = interp_w3(C4,C1,C5,5U,2U,1U);
+#define PIXEL01_70 line0[1] = interp_w3(C4,C1,C5,6U,1U,1U);
+#define PIXEL01_90 line0[1] = interp_w3(C4,C1,C5,2U,3U,3U);
+#define PIXEL01_100 line0[1] = interp_w3(C4,C1,C5,14U,1U,1U);
+
+#define PIXEL10_0 line1[0] = C4;
+#define PIXEL10_10 line1[0] = interp_w2(C4,C6,3U,1U);
+#define PIXEL10_11 line1[0] = interp_w2(C4,C7,3U,1U);
+#define PIXEL10_12 line1[0] = interp_w2(C4,C3,3U,1U);
+#define PIXEL10_20 line1[0] = interp_w3(C4,C7,C3,2U,1U,1U);
+#define PIXEL10_21 line1[0] = interp_w3(C4,C6,C3,2U,1U,1U);
+#define PIXEL10_22 line1[0] = interp_w3(C4,C6,C7,2U,1U,1U);
+#define PIXEL10_60 line1[0] = interp_w3(C4,C3,C7,5U,2U,1U);
+#define PIXEL10_61 line1[0] = interp_w3(C4,C7,C3,5U,2U,1U);
+#define PIXEL10_70 line1[0] = interp_w3(C4,C7,C3,6U,1U,1U);
+#define PIXEL10_90 line1[0] = interp_w3(C4,C7,C3,2U,3U,3U);
+#define PIXEL10_100 line1[0] = interp_w3(C4,C7,C3,14U,1U,1U);
+
+#define PIXEL11_0 line1[1] = C4;
+#define PIXEL11_10 line1[1] = interp_w2(C4,C8,3U,1U);
+#define PIXEL11_11 line1[1] = interp_w2(C4,C5,3U,1U);
+#define PIXEL11_12 line1[1] = interp_w2(C4,C7,3U,1U);
+#define PIXEL11_20 line1[1] = interp_w3(C4,C5,C7,2U,1U,1U);
+#define PIXEL11_21 line1[1] = interp_w3(C4,C8,C7,2U,1U,1U);
+#define PIXEL11_22 line1[1] = interp_w3(C4,C8,C5,2U,1U,1U);
+#define PIXEL11_60 line1[1] = interp_w3(C4,C7,C5,5U,2U,1U);
+#define PIXEL11_61 line1[1] = interp_w3(C4,C5,C7,5U,2U,1U);
+#define PIXEL11_70 line1[1] = interp_w3(C4,C5,C7,6U,1U,1U);
+#define PIXEL11_90 line1[1] = interp_w3(C4,C5,C7,2U,3U,3U);
+#define PIXEL11_100 line1[1] = interp_w3(C4,C5,C7,14U,1U,1U);
+
+#endif
+
+#if SBPP == 32
+#define RGBtoYUV(c) _RGBtoYUV[((c & 0xf80000) >> 8) | ((c & 0x00fc00) >> 5) | ((c & 0x0000f8) >> 3)]
+#else
+#define RGBtoYUV(c) _RGBtoYUV[c]
+#endif
+
+inline void conc2d(Hq2x,SBPP)(PTYPE * line0, PTYPE * line1, const PTYPE * fc)
+{
+ if (_RGBtoYUV == 0) conc2d(InitLUTs,SBPP)();
+
+ Bit32u pattern = 0;
+ const Bit32u YUV4 = RGBtoYUV(C4);
+ if (C4 != C0 && diffYUV(YUV4, RGBtoYUV(C0))) pattern |= 0x0001;
+ if (C4 != C1 && diffYUV(YUV4, RGBtoYUV(C1))) pattern |= 0x0002;
+ if (C4 != C2 && diffYUV(YUV4, RGBtoYUV(C2))) pattern |= 0x0004;
+ if (C4 != C3 && diffYUV(YUV4, RGBtoYUV(C3))) pattern |= 0x0008;
+ if (C4 != C5 && diffYUV(YUV4, RGBtoYUV(C5))) pattern |= 0x0010;
+ if (C4 != C6 && diffYUV(YUV4, RGBtoYUV(C6))) pattern |= 0x0020;
+ if (C4 != C7 && diffYUV(YUV4, RGBtoYUV(C7))) pattern |= 0x0040;
+ if (C4 != C8 && diffYUV(YUV4, RGBtoYUV(C8))) pattern |= 0x0080;
+
+ switch (pattern) {
+ case 0:
+ case 1:
+ case 4:
+ case 32:
+ case 128:
+ case 5:
+ case 132:
+ case 160:
+ case 33:
+ case 129:
+ case 36:
+ case 133:
+ case 164:
+ case 161:
+ case 37:
+ case 165:
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ case 2:
+ case 34:
+ case 130:
+ case 162:
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ case 16:
+ case 17:
+ case 48:
+ case 49:
+ PIXEL00_20
+ PIXEL01_22
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ case 64:
+ case 65:
+ case 68:
+ case 69:
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ case 8:
+ case 12:
+ case 136:
+ case 140:
+ PIXEL00_21
+ PIXEL01_20
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ case 3:
+ case 35:
+ case 131:
+ case 163:
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ case 6:
+ case 38:
+ case 134:
+ case 166:
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ case 20:
+ case 21:
+ case 52:
+ case 53:
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ case 144:
+ case 145:
+ case 176:
+ case 177:
+ PIXEL00_20
+ PIXEL01_22
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ case 192:
+ case 193:
+ case 196:
+ case 197:
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ case 96:
+ case 97:
+ case 100:
+ case 101:
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ case 40:
+ case 44:
+ case 168:
+ case 172:
+ PIXEL00_21
+ PIXEL01_20
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ case 9:
+ case 13:
+ case 137:
+ case 141:
+ PIXEL00_12
+ PIXEL01_20
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ case 18:
+ case 50:
+ PIXEL00_22
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ case 80:
+ case 81:
+ PIXEL00_20
+ PIXEL01_22
+ PIXEL10_21
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 72:
+ case 76:
+ PIXEL00_21
+ PIXEL01_20
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ case 10:
+ case 138:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ case 66:
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ case 24:
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ case 7:
+ case 39:
+ case 135:
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ case 148:
+ case 149:
+ case 180:
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ case 224:
+ case 228:
+ case 225:
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ case 41:
+ case 169:
+ case 45:
+ PIXEL00_12
+ PIXEL01_20
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ case 22:
+ case 54:
+ PIXEL00_22
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ case 208:
+ case 209:
+ PIXEL00_20
+ PIXEL01_22
+ PIXEL10_21
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 104:
+ case 108:
+ PIXEL00_21
+ PIXEL01_20
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ case 11:
+ case 139:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ case 19:
+ case 51:
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL00_11
+ PIXEL01_10
+ } else {
+ PIXEL00_60
+ PIXEL01_90
+ }
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ case 146:
+ case 178:
+ PIXEL00_22
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ PIXEL11_12
+ } else {
+ PIXEL01_90
+ PIXEL11_61
+ }
+ PIXEL10_20
+ break;
+ case 84:
+ case 85:
+ PIXEL00_20
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL01_11
+ PIXEL11_10
+ } else {
+ PIXEL01_60
+ PIXEL11_90
+ }
+ PIXEL10_21
+ break;
+ case 112:
+ case 113:
+ PIXEL00_20
+ PIXEL01_22
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL10_12
+ PIXEL11_10
+ } else {
+ PIXEL10_61
+ PIXEL11_90
+ }
+ break;
+ case 200:
+ case 204:
+ PIXEL00_21
+ PIXEL01_20
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ PIXEL11_11
+ } else {
+ PIXEL10_90
+ PIXEL11_60
+ }
+ break;
+ case 73:
+ case 77:
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL00_12
+ PIXEL10_10
+ } else {
+ PIXEL00_61
+ PIXEL10_90
+ }
+ PIXEL01_20
+ PIXEL11_22
+ break;
+ case 42:
+ case 170:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ PIXEL10_11
+ } else {
+ PIXEL00_90
+ PIXEL10_60
+ }
+ PIXEL01_21
+ PIXEL11_20
+ break;
+ case 14:
+ case 142:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ PIXEL01_12
+ } else {
+ PIXEL00_90
+ PIXEL01_61
+ }
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ case 67:
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ case 70:
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ case 28:
+ PIXEL00_21
+ PIXEL01_11
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ case 152:
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ case 194:
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ case 98:
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ case 56:
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ case 25:
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ case 26:
+ case 31:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ case 82:
+ case 214:
+ PIXEL00_22
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_21
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 88:
+ case 248:
+ PIXEL00_21
+ PIXEL01_22
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 74:
+ case 107:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ case 27:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ case 86:
+ PIXEL00_22
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_21
+ PIXEL11_10
+ break;
+ case 216:
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_10
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 106:
+ PIXEL00_10
+ PIXEL01_21
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ case 30:
+ PIXEL00_10
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ case 210:
+ PIXEL00_22
+ PIXEL01_10
+ PIXEL10_21
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 120:
+ PIXEL00_21
+ PIXEL01_22
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ case 75:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ PIXEL10_10
+ PIXEL11_22
+ break;
+ case 29:
+ PIXEL00_12
+ PIXEL01_11
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ case 198:
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ case 184:
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ case 99:
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ case 57:
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ case 71:
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ case 156:
+ PIXEL00_21
+ PIXEL01_11
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ case 226:
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ case 60:
+ PIXEL00_21
+ PIXEL01_11
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ case 195:
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ case 102:
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ case 153:
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ case 58:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ case 83:
+ PIXEL00_11
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ PIXEL10_21
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 92:
+ PIXEL00_21
+ PIXEL01_11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 202:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ PIXEL01_21
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ PIXEL11_11
+ break;
+ case 78:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ PIXEL01_12
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ PIXEL11_22
+ break;
+ case 154:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ case 114:
+ PIXEL00_22
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ PIXEL10_12
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 89:
+ PIXEL00_12
+ PIXEL01_22
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 90:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 55:
+ case 23:
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL00_11
+ PIXEL01_0
+ } else {
+ PIXEL00_60
+ PIXEL01_90
+ }
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ case 182:
+ case 150:
+ PIXEL00_22
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ PIXEL11_12
+ } else {
+ PIXEL01_90
+ PIXEL11_61
+ }
+ PIXEL10_20
+ break;
+ case 213:
+ case 212:
+ PIXEL00_20
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL01_11
+ PIXEL11_0
+ } else {
+ PIXEL01_60
+ PIXEL11_90
+ }
+ PIXEL10_21
+ break;
+ case 241:
+ case 240:
+ PIXEL00_20
+ PIXEL01_22
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL10_12
+ PIXEL11_0
+ } else {
+ PIXEL10_61
+ PIXEL11_90
+ }
+ break;
+ case 236:
+ case 232:
+ PIXEL00_21
+ PIXEL01_20
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ PIXEL11_11
+ } else {
+ PIXEL10_90
+ PIXEL11_60
+ }
+ break;
+ case 109:
+ case 105:
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL00_12
+ PIXEL10_0
+ } else {
+ PIXEL00_61
+ PIXEL10_90
+ }
+ PIXEL01_20
+ PIXEL11_22
+ break;
+ case 171:
+ case 43:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ PIXEL10_11
+ } else {
+ PIXEL00_90
+ PIXEL10_60
+ }
+ PIXEL01_21
+ PIXEL11_20
+ break;
+ case 143:
+ case 15:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ PIXEL01_12
+ } else {
+ PIXEL00_90
+ PIXEL01_61
+ }
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ case 124:
+ PIXEL00_21
+ PIXEL01_11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ case 203:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ PIXEL10_10
+ PIXEL11_11
+ break;
+ case 62:
+ PIXEL00_10
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ case 211:
+ PIXEL00_11
+ PIXEL01_10
+ PIXEL10_21
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 118:
+ PIXEL00_22
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_12
+ PIXEL11_10
+ break;
+ case 217:
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_10
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 110:
+ PIXEL00_10
+ PIXEL01_12
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ case 155:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ case 188:
+ PIXEL00_21
+ PIXEL01_11
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ case 185:
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ case 61:
+ PIXEL00_12
+ PIXEL01_11
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ case 157:
+ PIXEL00_12
+ PIXEL01_11
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ case 103:
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ case 227:
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ case 230:
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ case 199:
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ case 220:
+ PIXEL00_21
+ PIXEL01_11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 158:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ case 234:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ PIXEL01_21
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ PIXEL11_11
+ break;
+ case 242:
+ PIXEL00_22
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ PIXEL10_12
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 59:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ case 121:
+ PIXEL00_12
+ PIXEL01_22
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 87:
+ PIXEL00_11
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_21
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 79:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ PIXEL01_12
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ PIXEL11_22
+ break;
+ case 122:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 94:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 218:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 91:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 229:
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ case 167:
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ case 173:
+ PIXEL00_12
+ PIXEL01_20
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ case 181:
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ case 186:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ case 115:
+ PIXEL00_11
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ PIXEL10_12
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 93:
+ PIXEL00_12
+ PIXEL01_11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 206:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ PIXEL01_12
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ PIXEL11_11
+ break;
+ case 205:
+ case 201:
+ PIXEL00_12
+ PIXEL01_20
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_10
+ } else {
+ PIXEL10_70
+ }
+ PIXEL11_11
+ break;
+ case 174:
+ case 46:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_10
+ } else {
+ PIXEL00_70
+ }
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ case 179:
+ case 147:
+ PIXEL00_11
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_10
+ } else {
+ PIXEL01_70
+ }
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ case 117:
+ case 116:
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_12
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_10
+ } else {
+ PIXEL11_70
+ }
+ break;
+ case 189:
+ PIXEL00_12
+ PIXEL01_11
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ case 231:
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ case 126:
+ PIXEL00_10
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ case 219:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ PIXEL10_10
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 125:
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL00_12
+ PIXEL10_0
+ } else {
+ PIXEL00_61
+ PIXEL10_90
+ }
+ PIXEL01_11
+ PIXEL11_10
+ break;
+ case 221:
+ PIXEL00_12
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL01_11
+ PIXEL11_0
+ } else {
+ PIXEL01_60
+ PIXEL11_90
+ }
+ PIXEL10_10
+ break;
+ case 207:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ PIXEL01_12
+ } else {
+ PIXEL00_90
+ PIXEL01_61
+ }
+ PIXEL10_10
+ PIXEL11_11
+ break;
+ case 238:
+ PIXEL00_10
+ PIXEL01_12
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ PIXEL11_11
+ } else {
+ PIXEL10_90
+ PIXEL11_60
+ }
+ break;
+ case 190:
+ PIXEL00_10
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ PIXEL11_12
+ } else {
+ PIXEL01_90
+ PIXEL11_61
+ }
+ PIXEL10_11
+ break;
+ case 187:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ PIXEL10_11
+ } else {
+ PIXEL00_90
+ PIXEL10_60
+ }
+ PIXEL01_10
+ PIXEL11_12
+ break;
+ case 243:
+ PIXEL00_11
+ PIXEL01_10
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL10_12
+ PIXEL11_0
+ } else {
+ PIXEL10_61
+ PIXEL11_90
+ }
+ break;
+ case 119:
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL00_11
+ PIXEL01_0
+ } else {
+ PIXEL00_60
+ PIXEL01_90
+ }
+ PIXEL10_12
+ PIXEL11_10
+ break;
+ case 237:
+ case 233:
+ PIXEL00_12
+ PIXEL01_20
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_100
+ }
+ PIXEL11_11
+ break;
+ case 175:
+ case 47:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_100
+ }
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ case 183:
+ case 151:
+ PIXEL00_11
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_100
+ }
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ case 245:
+ case 244:
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_12
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_100
+ }
+ break;
+ case 250:
+ PIXEL00_10
+ PIXEL01_10
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 123:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ case 95:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_10
+ PIXEL11_10
+ break;
+ case 222:
+ PIXEL00_10
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_10
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 252:
+ PIXEL00_21
+ PIXEL01_11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_100
+ }
+ break;
+ case 249:
+ PIXEL00_12
+ PIXEL01_22
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_100
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 235:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_100
+ }
+ PIXEL11_11
+ break;
+ case 111:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_100
+ }
+ PIXEL01_12
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ case 63:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_100
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ case 159:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_100
+ }
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ case 215:
+ PIXEL00_11
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_100
+ }
+ PIXEL10_21
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 246:
+ PIXEL00_22
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ PIXEL10_12
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_100
+ }
+ break;
+ case 254:
+ PIXEL00_10
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_100
+ }
+ break;
+ case 253:
+ PIXEL00_12
+ PIXEL01_11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_100
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_100
+ }
+ break;
+ case 251:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_100
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 239:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_100
+ }
+ PIXEL01_12
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_100
+ }
+ PIXEL11_11
+ break;
+ case 127:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_100
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_20
+ }
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ case 191:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_100
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_100
+ }
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ case 223:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_20
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_100
+ }
+ PIXEL10_10
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_20
+ }
+ break;
+ case 247:
+ PIXEL00_11
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_100
+ }
+ PIXEL10_12
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_100
+ }
+ break;
+ case 255:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_0
+ } else {
+ PIXEL00_100
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_0
+ } else {
+ PIXEL01_100
+ }
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_0
+ } else {
+ PIXEL10_100
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL11_0
+ } else {
+ PIXEL11_100
+ }
+ break;
+ }
+}
+
+#undef RGBtoYUV
diff --git a/src/gui/render_templates_hq3x.h b/src/gui/render_templates_hq3x.h
new file mode 100644
index 000000000..e067cb86a
--- /dev/null
+++ b/src/gui/render_templates_hq3x.h
@@ -0,0 +1,2872 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+/*
+ * The HQ3x high quality 3x graphics filter.
+ * Original author Maxim Stepin (see http://www.hiend3d.com/hq3x.html).
+ * Adapted for DOSBox from ScummVM and HiEnd3D code by Kronuz.
+ */
+
+#ifndef RENDER_TEMPLATES_HQ3X_TABLE_H
+#define RENDER_TEMPLATES_HQ3X_TABLE_H
+
+#define PIXEL00_1M line0[0] = interp_w2(C4,C0,3U,1U);
+#define PIXEL00_1U line0[0] = interp_w2(C4,C1,3U,1U);
+#define PIXEL00_1L line0[0] = interp_w2(C4,C3,3U,1U);
+#define PIXEL00_2 line0[0] = interp_w3(C4,C3,C1,2U,1U,1U);
+#define PIXEL00_4 line0[0] = interp_w3(C4,C3,C1,2U,7U,7U);
+#define PIXEL00_5 line0[0] = interp_w2(C3,C1,1U,1U);
+#define PIXEL00_C line0[0] = C4;
+
+#define PIXEL01_1 line0[1] = interp_w2(C4,C1,3U,1U);
+#define PIXEL01_3 line0[1] = interp_w2(C4,C1,7U,1U);
+#define PIXEL01_6 line0[1] = interp_w2(C1,C4,3U,1U);
+#define PIXEL01_C line0[1] = C4;
+
+#define PIXEL02_1M line0[2] = interp_w2(C4,C2,3U,1U);
+#define PIXEL02_1U line0[2] = interp_w2(C4,C1,3U,1U);
+#define PIXEL02_1R line0[2] = interp_w2(C4,C5,3U,1U);
+#define PIXEL02_2 line0[2] = interp_w3(C4,C1,C5,2U,1U,1U);
+#define PIXEL02_4 line0[2] = interp_w3(C4,C1,C5,2U,7U,7U);
+#define PIXEL02_5 line0[2] = interp_w2(C1,C5,1U,1U);
+#define PIXEL02_C line0[2] = C4;
+
+#define PIXEL10_1 line1[0] = interp_w2(C4,C3,3U,1U);
+#define PIXEL10_3 line1[0] = interp_w2(C4,C3,7U,1U);
+#define PIXEL10_6 line1[0] = interp_w2(C3,C4,3U,1U);
+#define PIXEL10_C line1[0] = C4;
+
+#define PIXEL11 line1[1] = C4;
+
+#define PIXEL12_1 line1[2] = interp_w2(C4,C5,3U,1U);
+#define PIXEL12_3 line1[2] = interp_w2(C4,C5,7U,1U);
+#define PIXEL12_6 line1[2] = interp_w2(C5,C4,3U,1U);
+#define PIXEL12_C line1[2] = C4;
+
+#define PIXEL20_1M line2[0] = interp_w2(C4,C6,3U,1U);
+#define PIXEL20_1D line2[0] = interp_w2(C4,C7,3U,1U);
+#define PIXEL20_1L line2[0] = interp_w2(C4,C3,3U,1U);
+#define PIXEL20_2 line2[0] = interp_w3(C4,C7,C3,2U,1U,1U);
+#define PIXEL20_4 line2[0] = interp_w3(C4,C7,C3,2U,7U,7U);
+#define PIXEL20_5 line2[0] = interp_w2(C7,C3,1U,1U);
+#define PIXEL20_C line2[0] = C4;
+
+#define PIXEL21_1 line2[1] = interp_w2(C4,C7,3U,1U);
+#define PIXEL21_3 line2[1] = interp_w2(C4,C7,7U,1U);
+#define PIXEL21_6 line2[1] = interp_w2(C7,C4,3U,1U);
+#define PIXEL21_C line2[1] = C4;
+
+#define PIXEL22_1M line2[2] = interp_w2(C4,C8,3U,1U);
+#define PIXEL22_1D line2[2] = interp_w2(C4,C7,3U,1U);
+#define PIXEL22_1R line2[2] = interp_w2(C4,C5,3U,1U);
+#define PIXEL22_2 line2[2] = interp_w3(C4,C5,C7,2U,1U,1U);
+#define PIXEL22_4 line2[2] = interp_w3(C4,C5,C7,2U,7U,7U);
+#define PIXEL22_5 line2[2] = interp_w2(C5,C7,1U,1U);
+#define PIXEL22_C line2[2] = C4;
+
+#endif
+
+#if SBPP == 32
+#define RGBtoYUV(c) _RGBtoYUV[((c & 0xf80000) >> 8) | ((c & 0x00fc00) >> 5) | ((c & 0x0000f8) >> 3)]
+#else
+#define RGBtoYUV(c) _RGBtoYUV[c]
+#endif
+
+inline void conc2d(Hq3x,SBPP)(PTYPE * line0, PTYPE * line1, PTYPE * line2, const PTYPE * fc)
+{
+ if (_RGBtoYUV == 0) conc2d(InitLUTs,SBPP)();
+
+ Bit32u pattern = 0;
+ const Bit32u YUV4 = RGBtoYUV(C4);
+
+ if (C4 != C0 && diffYUV(YUV4, RGBtoYUV(C0))) pattern |= 0x0001;
+ if (C4 != C1 && diffYUV(YUV4, RGBtoYUV(C1))) pattern |= 0x0002;
+ if (C4 != C2 && diffYUV(YUV4, RGBtoYUV(C2))) pattern |= 0x0004;
+ if (C4 != C3 && diffYUV(YUV4, RGBtoYUV(C3))) pattern |= 0x0008;
+ if (C4 != C5 && diffYUV(YUV4, RGBtoYUV(C5))) pattern |= 0x0010;
+ if (C4 != C6 && diffYUV(YUV4, RGBtoYUV(C6))) pattern |= 0x0020;
+ if (C4 != C7 && diffYUV(YUV4, RGBtoYUV(C7))) pattern |= 0x0040;
+ if (C4 != C8 && diffYUV(YUV4, RGBtoYUV(C8))) pattern |= 0x0080;
+
+ switch (pattern) {
+ case 0:
+ case 1:
+ case 4:
+ case 32:
+ case 128:
+ case 5:
+ case 132:
+ case 160:
+ case 33:
+ case 129:
+ case 36:
+ case 133:
+ case 164:
+ case 161:
+ case 37:
+ case 165:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 2:
+ case 34:
+ case 130:
+ case 162:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 16:
+ case 17:
+ case 48:
+ case 49:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 64:
+ case 65:
+ case 68:
+ case 69:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 8:
+ case 12:
+ case 136:
+ case 140:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 3:
+ case 35:
+ case 131:
+ case 163:
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 6:
+ case 38:
+ case 134:
+ case 166:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 20:
+ case 21:
+ case 52:
+ case 53:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 144:
+ case 145:
+ case 176:
+ case 177:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 192:
+ case 193:
+ case 196:
+ case 197:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 96:
+ case 97:
+ case 100:
+ case 101:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 40:
+ case 44:
+ case 168:
+ case 172:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 9:
+ case 13:
+ case 137:
+ case 141:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 18:
+ case 50:
+ PIXEL00_1M
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL12_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 80:
+ case 81:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_1M
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_1M
+ } else {
+ PIXEL12_3
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 72:
+ case 76:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_1M
+ PIXEL21_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ PIXEL21_3
+ }
+ PIXEL22_1M
+ break;
+ case 10:
+ case 138:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ PIXEL10_3
+ }
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 66:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 24:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 7:
+ case 39:
+ case 135:
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 148:
+ case 149:
+ case 180:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 224:
+ case 228:
+ case 225:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 41:
+ case 169:
+ case 45:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 22:
+ case 54:
+ PIXEL00_1M
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 208:
+ case 209:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_1M
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 104:
+ case 108:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ PIXEL21_3
+ }
+ PIXEL22_1M
+ break;
+ case 11:
+ case 139:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ PIXEL10_3
+ }
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 19:
+ case 51:
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL12_C
+ } else {
+ PIXEL00_2
+ PIXEL01_6
+ PIXEL02_5
+ PIXEL12_1
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 146:
+ case 178:
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL12_C
+ PIXEL22_1D
+ } else {
+ PIXEL01_1
+ PIXEL02_5
+ PIXEL12_6
+ PIXEL22_2
+ }
+ PIXEL00_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_2
+ PIXEL21_1
+ break;
+ case 84:
+ case 85:
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL02_1U
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_1M
+ } else {
+ PIXEL02_2
+ PIXEL12_6
+ PIXEL21_1
+ PIXEL22_5
+ }
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_1M
+ break;
+ case 112:
+ case 113:
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1M
+ } else {
+ PIXEL12_1
+ PIXEL20_2
+ PIXEL21_6
+ PIXEL22_5
+ }
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ break;
+ case 200:
+ case 204:
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1R
+ } else {
+ PIXEL10_1
+ PIXEL20_5
+ PIXEL21_6
+ PIXEL22_2
+ }
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL11
+ PIXEL12_1
+ break;
+ case 73:
+ case 77:
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL00_1U
+ PIXEL10_C
+ PIXEL20_1M
+ PIXEL21_C
+ } else {
+ PIXEL00_2
+ PIXEL10_6
+ PIXEL20_5
+ PIXEL21_1
+ }
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL11
+ PIXEL12_1
+ PIXEL22_1M
+ break;
+ case 42:
+ case 170:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL10_C
+ PIXEL20_1D
+ } else {
+ PIXEL00_5
+ PIXEL01_1
+ PIXEL10_6
+ PIXEL20_2
+ }
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_1
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 14:
+ case 142:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_C
+ } else {
+ PIXEL00_5
+ PIXEL01_6
+ PIXEL02_2
+ PIXEL10_1
+ }
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 67:
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 70:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 28:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 152:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 194:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 98:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 56:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 25:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 26:
+ case 31:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL10_3
+ }
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL11
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 82:
+ case 214:
+ PIXEL00_1M
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 88:
+ case 248:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ }
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL22_4
+ }
+ break;
+ case 74:
+ case 107:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ }
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL20_4
+ PIXEL21_3
+ }
+ PIXEL22_1M
+ break;
+ case 27:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ PIXEL10_3
+ }
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 86:
+ PIXEL00_1M
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 216:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL20_1M
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 106:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ PIXEL21_3
+ }
+ PIXEL22_1M
+ break;
+ case 30:
+ PIXEL00_1M
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL10_C
+ PIXEL11
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 210:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_1M
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 120:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ PIXEL21_3
+ }
+ PIXEL22_1M
+ break;
+ case 75:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ PIXEL10_3
+ }
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 29:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 198:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 184:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 99:
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 57:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 71:
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 156:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 226:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 60:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 195:
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 102:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 153:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 58:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 83:
+ PIXEL00_1L
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 92:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 202:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 78:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 154:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 114:
+ PIXEL00_1M
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1L
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 89:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 90:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 55:
+ case 23:
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL00_2
+ PIXEL01_6
+ PIXEL02_5
+ PIXEL12_1
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 182:
+ case 150:
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ PIXEL22_1D
+ } else {
+ PIXEL01_1
+ PIXEL02_5
+ PIXEL12_6
+ PIXEL22_2
+ }
+ PIXEL00_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_2
+ PIXEL21_1
+ break;
+ case 213:
+ case 212:
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL02_1U
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL02_2
+ PIXEL12_6
+ PIXEL21_1
+ PIXEL22_5
+ }
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_1M
+ break;
+ case 241:
+ case 240:
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL12_1
+ PIXEL20_2
+ PIXEL21_6
+ PIXEL22_5
+ }
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ break;
+ case 236:
+ case 232:
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ PIXEL22_1R
+ } else {
+ PIXEL10_1
+ PIXEL20_5
+ PIXEL21_6
+ PIXEL22_2
+ }
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL11
+ PIXEL12_1
+ break;
+ case 109:
+ case 105:
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL00_1U
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL00_2
+ PIXEL10_6
+ PIXEL20_5
+ PIXEL21_1
+ }
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL11
+ PIXEL12_1
+ PIXEL22_1M
+ break;
+ case 171:
+ case 43:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL10_C
+ PIXEL20_1D
+ } else {
+ PIXEL00_5
+ PIXEL01_1
+ PIXEL10_6
+ PIXEL20_2
+ }
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_1
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 143:
+ case 15:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_C
+ } else {
+ PIXEL00_5
+ PIXEL01_6
+ PIXEL02_2
+ PIXEL10_1
+ }
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 124:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ PIXEL21_3
+ }
+ PIXEL22_1M
+ break;
+ case 203:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ PIXEL10_3
+ }
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 62:
+ PIXEL00_1M
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL10_C
+ PIXEL11
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 211:
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_1M
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 118:
+ PIXEL00_1M
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 217:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL20_1M
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 110:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ PIXEL21_3
+ }
+ PIXEL22_1M
+ break;
+ case 155:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ PIXEL10_3
+ }
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 188:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 185:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 61:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 157:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 103:
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 227:
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 230:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 199:
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 220:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_C
+ PIXEL11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 158:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL10_C
+ PIXEL11
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 234:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ PIXEL21_3
+ }
+ PIXEL22_1R
+ break;
+ case 242:
+ PIXEL00_1M
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_1L
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 59:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ PIXEL10_3
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 121:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ PIXEL21_3
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 87:
+ PIXEL00_1L
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_1M
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 79:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ PIXEL10_3
+ }
+ PIXEL02_1R
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 122:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ PIXEL21_3
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 94:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL10_C
+ PIXEL11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 218:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_C
+ PIXEL11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 91:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ PIXEL10_3
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 229:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 167:
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 173:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 181:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 186:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 115:
+ PIXEL00_1L
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1L
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 93:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 206:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 205:
+ case 201:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_1M
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 174:
+ case 46:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_1M
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 179:
+ case 147:
+ PIXEL00_1L
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_1M
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 117:
+ case 116:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1L
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_1M
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 189:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 231:
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 126:
+ PIXEL00_1M
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ PIXEL21_3
+ }
+ PIXEL22_1M
+ break;
+ case 219:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ PIXEL10_3
+ }
+ PIXEL02_1M
+ PIXEL11
+ PIXEL20_1M
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 125:
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL00_1U
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL00_2
+ PIXEL10_6
+ PIXEL20_5
+ PIXEL21_1
+ }
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL11
+ PIXEL12_C
+ PIXEL22_1M
+ break;
+ case 221:
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL02_1U
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL02_2
+ PIXEL12_6
+ PIXEL21_1
+ PIXEL22_5
+ }
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL10_C
+ PIXEL11
+ PIXEL20_1M
+ break;
+ case 207:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_C
+ } else {
+ PIXEL00_5
+ PIXEL01_6
+ PIXEL02_2
+ PIXEL10_1
+ }
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 238:
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ PIXEL22_1R
+ } else {
+ PIXEL10_1
+ PIXEL20_5
+ PIXEL21_6
+ PIXEL22_2
+ }
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL11
+ PIXEL12_1
+ break;
+ case 190:
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ PIXEL22_1D
+ } else {
+ PIXEL01_1
+ PIXEL02_5
+ PIXEL12_6
+ PIXEL22_2
+ }
+ PIXEL00_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL20_1D
+ PIXEL21_1
+ break;
+ case 187:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL10_C
+ PIXEL20_1D
+ } else {
+ PIXEL00_5
+ PIXEL01_1
+ PIXEL10_6
+ PIXEL20_2
+ }
+ PIXEL02_1M
+ PIXEL11
+ PIXEL12_C
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 243:
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL12_1
+ PIXEL20_2
+ PIXEL21_6
+ PIXEL22_5
+ }
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL10_1
+ PIXEL11
+ break;
+ case 119:
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL00_1L
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL00_2
+ PIXEL01_6
+ PIXEL02_5
+ PIXEL12_1
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL20_1L
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 237:
+ case 233:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_2
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_C
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 175:
+ case 47:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_2
+ break;
+ case 183:
+ case 151:
+ PIXEL00_1L
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_C
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_2
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 245:
+ case 244:
+ PIXEL00_2
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1L
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_C
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 250:
+ PIXEL00_1M
+ PIXEL01_C
+ PIXEL02_1M
+ PIXEL11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ }
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL22_4
+ }
+ break;
+ case 123:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ }
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL20_4
+ PIXEL21_3
+ }
+ PIXEL22_1M
+ break;
+ case 95:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL10_3
+ }
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL11
+ PIXEL20_1M
+ PIXEL21_C
+ PIXEL22_1M
+ break;
+ case 222:
+ PIXEL00_1M
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ }
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 252:
+ PIXEL00_1M
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ }
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_C
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 249:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_C
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL22_4
+ }
+ break;
+ case 235:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ }
+ PIXEL02_1M
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_C
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 111:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL20_4
+ PIXEL21_3
+ }
+ PIXEL22_1M
+ break;
+ case 63:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL10_C
+ PIXEL11
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1M
+ break;
+ case 159:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL10_3
+ }
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_C
+ } else {
+ PIXEL02_2
+ }
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 215:
+ PIXEL00_1L
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_C
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1M
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 246:
+ PIXEL00_1M
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1L
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_C
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 254:
+ PIXEL00_1M
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ } else {
+ PIXEL01_3
+ PIXEL02_4
+ }
+ PIXEL11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ } else {
+ PIXEL10_3
+ PIXEL20_4
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL21_3
+ PIXEL22_2
+ }
+ break;
+ case 253:
+ PIXEL00_1U
+ PIXEL01_1
+ PIXEL02_1U
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_C
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_C
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 251:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ } else {
+ PIXEL00_4
+ PIXEL01_3
+ }
+ PIXEL02_1M
+ PIXEL11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL10_C
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL10_3
+ PIXEL20_2
+ PIXEL21_3
+ }
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL12_C
+ PIXEL22_C
+ } else {
+ PIXEL12_3
+ PIXEL22_4
+ }
+ break;
+ case 239:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ PIXEL02_1R
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_1
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_C
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ PIXEL22_1R
+ break;
+ case 127:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL01_C
+ PIXEL10_C
+ } else {
+ PIXEL00_2
+ PIXEL01_3
+ PIXEL10_3
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL02_4
+ PIXEL12_3
+ }
+ PIXEL11
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_C
+ PIXEL21_C
+ } else {
+ PIXEL20_4
+ PIXEL21_3
+ }
+ PIXEL22_1M
+ break;
+ case 191:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_C
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1D
+ PIXEL21_1
+ PIXEL22_1D
+ break;
+ case 223:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ PIXEL10_C
+ } else {
+ PIXEL00_4
+ PIXEL10_3
+ }
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL01_C
+ PIXEL02_C
+ PIXEL12_C
+ } else {
+ PIXEL01_3
+ PIXEL02_2
+ PIXEL12_3
+ }
+ PIXEL11
+ PIXEL20_1M
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL21_C
+ PIXEL22_C
+ } else {
+ PIXEL21_3
+ PIXEL22_4
+ }
+ break;
+ case 247:
+ PIXEL00_1L
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_C
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_1
+ PIXEL11
+ PIXEL12_C
+ PIXEL20_1L
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_C
+ } else {
+ PIXEL22_2
+ }
+ break;
+ case 255:
+ if (diffYUV(RGBtoYUV(C3), RGBtoYUV(C1))) {
+ PIXEL00_C
+ } else {
+ PIXEL00_2
+ }
+ PIXEL01_C
+ if (diffYUV(RGBtoYUV(C1), RGBtoYUV(C5))) {
+ PIXEL02_C
+ } else {
+ PIXEL02_2
+ }
+ PIXEL10_C
+ PIXEL11
+ PIXEL12_C
+ if (diffYUV(RGBtoYUV(C7), RGBtoYUV(C3))) {
+ PIXEL20_C
+ } else {
+ PIXEL20_2
+ }
+ PIXEL21_C
+ if (diffYUV(RGBtoYUV(C5), RGBtoYUV(C7))) {
+ PIXEL22_C
+ } else {
+ PIXEL22_2
+ }
+ break;
+ }
+}
+
+#undef RGBtoYUV
diff --git a/src/gui/render_templates_sai.h b/src/gui/render_templates_sai.h
new file mode 100644
index 000000000..951033675
--- /dev/null
+++ b/src/gui/render_templates_sai.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+static inline int conc2d(GetResult,SBPP)(PTYPE A, PTYPE B, PTYPE C, PTYPE D) {
+ const bool ac = (A==C);
+ const bool bc = (B==C);
+ const int x1 = ac;
+ const int y1 = (bc & !ac);
+ const bool ad = (A==D);
+ const bool bd = (B==D);
+ const int x2 = ad;
+ const int y2 = (bd & !ad);
+ const int x = x1+x2;
+ const int y = y1+y2;
+ static const int rmap[3][3] = {
+ {0, 0, -1},
+ {0, 0, -1},
+ {1, 1, 0}
+ };
+ return rmap[y][x];
+}
+
+inline void conc2d(Super2xSaI,SBPP)(PTYPE * line0, PTYPE * line1, const PTYPE * fc)
+{
+ //--------------------------------------
+ if (C7 == C5 && C4 != C8) {
+ line1[1] = line0[1] = C7;
+ } else if (C4 == C8 && C7 != C5) {
+ line1[1] = line0[1] = C4;
+ } else if (C4 == C8 && C7 == C5) {
+ register int r = 0;
+ r += conc2d(GetResult,SBPP)(C5,C4,C6,D1);
+ r += conc2d(GetResult,SBPP)(C5,C4,C3,C1);
+ r += conc2d(GetResult,SBPP)(C5,C4,D2,D5);
+ r += conc2d(GetResult,SBPP)(C5,C4,C2,D4);
+
+ if (r > 0)
+ line1[1] = line0[1] = C5;
+ else if (r < 0)
+ line1[1] = line0[1] = C4;
+ else {
+ line1[1] = line0[1] = interp_w2(C4,C5,1U,1U);
+ }
+ } else {
+ if (C5 == C8 && C8 == D1 && C7 != D2 && C8 != D0)
+ line1[1] = interp_w2(C8,C7,3U,1U);
+ else if (C4 == C7 && C7 == D2 && D1 != C8 && C7 != D6)
+ line1[1] = interp_w2(C7,C8,3U,1U);
+ else
+ line1[1] = interp_w2(C7,C8,1U,1U);
+
+ if (C5 == C8 && C5 == C1 && C4 != C2 && C5 != C0)
+ line0[1] = interp_w2(C5,C4,3U,1U);
+ else if (C4 == C7 && C4 == C2 && C1 != C5 && C4 != D3)
+ line0[1] = interp_w2(C4,C5,3U,1U);
+ else
+ line0[1] = interp_w2(C4,C5,1U,1U);
+ }
+
+ if (C4 == C8 && C7 != C5 && C3 == C4 && C4 != D2)
+ line1[0] = interp_w2(C7,C4,1U,1U);
+ else if (C4 == C6 && C5 == C4 && C3 != C7 && C4 != D0)
+ line1[0] = interp_w2(C7,C4,1U,1U);
+ else
+ line1[0] = C7;
+
+ if (C7 == C5 && C4 != C8 && C6 == C7 && C7 != C2)
+ line0[0] = interp_w2(C7,C4,1U,1U);
+ else if (C3 == C7 && C8 == C7 && C6 != C4 && C7 != C0)
+ line0[0] = interp_w2(C7,C4,1U,1U);
+ else
+ line0[0] = C4;
+}
+
+inline void conc2d(SuperEagle,SBPP)(PTYPE * line0, PTYPE * line1, const PTYPE * fc)
+{
+ // --------------------------------------
+ if (C4 != C8) {
+ if (C7 == C5) {
+ line0[1] = line1[0] = C7;
+ if ((C6 == C7) || (C5 == C2)) {
+ line0[0] = interp_w2(C7,C4,3U,1U);
+ } else {
+ line0[0] = interp_w2(C4,C5,1U,1U);
+ }
+
+ if ((C5 == D4) || (C7 == D1)) {
+ line1[1] = interp_w2(C7,C8,3U,1U);
+ } else {
+ line1[1] = interp_w2(C7,C8,1U,1U);
+ }
+ } else {
+ line1[1] = interp_w3(C8,C7,C5,6U,1U,1U);
+ line0[0] = interp_w3(C4,C7,C5,6U,1U,1U);
+
+ line1[0] = interp_w3(C7,C4,C8,6U,1U,1U);
+ line0[1] = interp_w3(C5,C4,C8,6U,1U,1U);
+ }
+ } else {
+ if (C7 != C5) {
+ line1[1] = line0[0] = C4;
+
+ if ((C1 == C4) || (C8 == D5)) {
+ line0[1] = interp_w2(C4,C5,3U,1U);
+ } else {
+ line0[1] = interp_w2(C4,C5,1U,1U);
+ }
+
+ if ((C8 == D2) || (C3 == C4)) {
+ line1[0] = interp_w2(C4,C7,3U,1U);
+ } else {
+ line1[0] = interp_w2(C7,C8,1U,1U);
+ }
+ } else {
+ register int r = 0;
+ r += conc2d(GetResult,SBPP)(C5,C4,C6,D1);
+ r += conc2d(GetResult,SBPP)(C5,C4,C3,C1);
+ r += conc2d(GetResult,SBPP)(C5,C4,D2,D5);
+ r += conc2d(GetResult,SBPP)(C5,C4,C2,D4);
+
+ if (r > 0) {
+ line0[1] = line1[0] = C7;
+ line0[0] = line1[1] = interp_w2(C4,C5,1U,1U);
+ } else if (r < 0) {
+ line1[1] = line0[0] = C4;
+ line0[1] = line1[0] = interp_w2(C4,C5,1U,1U);
+ } else {
+ line1[1] = line0[0] = C4;
+ line0[1] = line1[0] = C7;
+ }
+ }
+ }
+}
+
+inline void conc2d(_2xSaI,SBPP)(PTYPE * line0, PTYPE * line1, const PTYPE * fc)
+{
+ if ((C4 == C8) && (C5 != C7)) {
+ if (((C4 == C1) && (C5 == D5)) ||
+ ((C4 == C7) && (C4 == C2) && (C5 != C1) && (C5 == D3))) {
+ line0[1] = C4;
+ } else {
+ line0[1] = interp_w2(C4,C5,1U,1U);
+ }
+
+ if (((C4 == C3) && (C7 == D2)) ||
+ ((C4 == C5) && (C4 == C6) && (C3 != C7) && (C7 == D0))) {
+ line1[0] = C4;
+ } else {
+ line1[0] = interp_w2(C4,C7,1U,1U);
+ }
+ line1[1] = C4;
+ } else if ((C5 == C7) && (C4 != C8)) {
+ if (((C5 == C2) && (C4 == C6)) ||
+ ((C5 == C1) && (C5 == C8) && (C4 != C2) && (C4 == C0))) {
+ line0[1] = C5;
+ } else {
+ line0[1] = interp_w2(C4,C5,1U,1U);
+ }
+
+ if (((C7 == C6) && (C4 == C2)) ||
+ ((C7 == C3) && (C7 == C8) && (C4 != C6) && (C4 == C0))) {
+ line1[0] = C7;
+ } else {
+ line1[0] = interp_w2(C4,C7,1U,1U);
+ }
+ line1[1] = C5;
+ } else if ((C4 == C8) && (C5 == C7)) {
+ if (C4 == C5) {
+ line0[1] = C4;
+ line1[0] = C4;
+ line1[1] = C4;
+ } else {
+ register int r = 0;
+ r += conc2d(GetResult,SBPP)(C4,C5,C3,C1);
+ r -= conc2d(GetResult,SBPP)(C5,C4,D4,C2);
+ r -= conc2d(GetResult,SBPP)(C5,C4,C6,D1);
+ r += conc2d(GetResult,SBPP)(C4,C5,D5,D2);
+
+ if (r > 0)
+ line1[1] = C4;
+ else if (r < 0)
+ line1[1] = C5;
+ else {
+ line1[1] = interp_w4(C4,C5,C7,C8,1U,1U,1U,1U);
+ }
+
+ line1[0] = interp_w2(C4,C7,1U,1U);
+ line0[1] = interp_w2(C4,C5,1U,1U);
+ }
+ } else {
+ line1[1] = interp_w4(C4,C5,C7,C8,1U,1U,1U,1U);
+
+ if ((C4 == C7) && (C4 == C2)
+ && (C5 != C1) && (C5 == D3)) {
+ line0[1] = C4;
+ } else if ((C5 == C1) && (C5 == C8)
+ && (C4 != C2) && (C4 == C0)) {
+ line0[1] = C5;
+ } else {
+ line0[1] = interp_w2(C4,C5,1U,1U);
+ }
+
+ if ((C4 == C5) && (C4 == C6)
+ && (C3 != C7) && (C7 == D0)) {
+ line1[0] = C4;
+ } else if ((C7 == C3) && (C7 == C8)
+ && (C4 != C6) && (C4 == C0)) {
+ line1[0] = C7;
+ } else {
+ line1[0] = interp_w2(C4,C7,1U,1U);
+ }
+ }
+ line0[0] = C4;
+}
diff --git a/src/gui/sdl_gui.cpp b/src/gui/sdl_gui.cpp
new file mode 100644
index 000000000..9640b0651
--- /dev/null
+++ b/src/gui/sdl_gui.cpp
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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 0
+#include "SDL.h"
+#include "../libs/gui_tk/gui_tk.h"
+
+#include "dosbox.h"
+#include "keyboard.h"
+#include "video.h"
+#include "render.h"
+#include "mapper.h"
+#include "setup.h"
+#include "control.h"
+#include "shell.h"
+
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+#include <algorithm>
+#include <cctype>
+#include <assert.h>
+
+extern Bit8u int10_font_14[256 * 14];
+extern bool MSG_Write(const char *);
+extern void GFX_SetTitle(Bit32s cycles, Bits frameskip, bool paused);
+
+static int cursor, saved_bpp;
+static int old_unicode;
+static bool mousetoggle, running, shell_idle;
+
+static SDL_Surface *screenshot, *background;
+
+/* Prepare screen for UI */
+void UI_Init(void) {
+ GUI::Font::addFont("default",new GUI::BitmapFont(int10_font_14,14,10));
+}
+
+static void getPixel(Bits x, Bits y, int &r, int &g, int &b, int shift)
+{
+ if (x >= (Bits)render.src.width) x = (Bits)render.src.width-1;
+ if (y >= (Bits)render.src.height) x = (Bits)render.src.height-1;
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+
+ Bit8u* src = (Bit8u *)&scalerSourceCache;
+ Bit32u pixel;
+ switch (render.scale.inMode) {
+ case scalerMode8:
+ pixel = *(x+(Bit8u*)(src+y*render.scale.cachePitch));
+ r += render.pal.rgb[pixel].red >> shift;
+ g += render.pal.rgb[pixel].green >> shift;
+ b += render.pal.rgb[pixel].blue >> shift;
+ break;
+ case scalerMode15:
+ pixel = *(x+(Bit16u*)(src+y*render.scale.cachePitch));
+ r += (pixel >> (7+shift)) & (0xf8 >> shift);
+ g += (pixel >> (2+shift)) & (0xf8 >> shift);
+ b += (pixel << (3-shift)) & (0xf8 >> shift);
+ break;
+ case scalerMode16:
+ pixel = *(x+(Bit16u*)(src+y*render.scale.cachePitch));
+ r += (pixel >> (8+shift)) & (0xf8 >> shift);
+ g += (pixel >> (3+shift)) & (0xfc >> shift);
+ b += (pixel << (3-shift)) & (0xf8 >> shift);
+ break;
+ case scalerMode32:
+ pixel = *(x+(Bit32u*)(src+y*render.scale.cachePitch));
+ r += (pixel >> (16+shift)) & (0xff >> shift);
+ g += (pixel >> (8+shift)) & (0xff >> shift);
+ b += (pixel >> shift) & (0xff >> shift);
+ break;
+ }
+}
+
+static GUI::ScreenSDL *UI_Startup(GUI::ScreenSDL *screen) {
+ GFX_EndUpdate(0);
+ GFX_SetTitle(-1,-1,true);
+ if(!screen) { //Coming from DOSBox. Clean up the keyboard buffer.
+ KEYBOARD_ClrBuffer();//Clear buffer
+ }
+ GFX_LosingFocus();//Release any keys pressed (buffer gets filled again). (could be in above if, but clearing the mapper input when exiting the mapper is sensible as well
+ SDL_Delay(500);
+
+ // Comparable to the code of intro.com, but not the same! (the code of intro.com is called from within a com file)
+ shell_idle = first_shell && (DOS_PSP(dos.psp()).GetSegment() == DOS_PSP(dos.psp()).GetParent());
+
+ int w, h;
+ bool fs;
+ GFX_GetSize(w, h, fs);
+ if (w < 512) w = 640;
+ if (h < 350) h = 400;
+
+ old_unicode = SDL_EnableUNICODE(1);
+ SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
+ screenshot = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, GUI::Color::RedMask, GUI::Color::GreenMask, GUI::Color::BlueMask, 0);
+
+ // create screenshot for fade effect
+ int rs = screenshot->format->Rshift, gs = screenshot->format->Gshift, bs = screenshot->format->Bshift, am = GUI::Color::AlphaMask;
+ for (int y = 0; y < h; y++) {
+ Bit32u *bg = (Bit32u*)(y*screenshot->pitch + (char*)screenshot->pixels);
+ for (int x = 0; x < w; x++) {
+ int r = 0, g = 0, b = 0;
+ getPixel(x *(int)render.src.width/w, y *(int)render.src.height/h, r, g, b, 0);
+ bg[x] = r << rs | g << gs | b << bs;
+ }
+ }
+
+ background = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, GUI::Color::RedMask, GUI::Color::GreenMask, GUI::Color::BlueMask, GUI::Color::AlphaMask);
+ // use a blurred and sepia-toned screenshot as menu background
+ for (int y = 0; y < h; y++) {
+ Bit32u *bg = (Bit32u*)(y*background->pitch + (char*)background->pixels);
+ for (int x = 0; x < w; x++) {
+ int r = 0, g = 0, b = 0;
+ getPixel(x *(int)render.src.width/w, y *(int)render.src.height/h, r, g, b, 3);
+ getPixel((x-1)*(int)render.src.width/w, y *(int)render.src.height/h, r, g, b, 3);
+ getPixel(x *(int)render.src.width/w, (y-1)*(int)render.src.height/h, r, g, b, 3);
+ getPixel((x-1)*(int)render.src.width/w, (y-1)*(int)render.src.height/h, r, g, b, 3);
+ getPixel((x+1)*(int)render.src.width/w, y *(int)render.src.height/h, r, g, b, 3);
+ getPixel(x *(int)render.src.width/w, (y+1)*(int)render.src.height/h, r, g, b, 3);
+ getPixel((x+1)*(int)render.src.width/w, (y+1)*(int)render.src.height/h, r, g, b, 3);
+ getPixel((x-1)*(int)render.src.width/w, (y+1)*(int)render.src.height/h, r, g, b, 3);
+ int r1 = (int)((r * 393 + g * 769 + b * 189) / 1351);
+ int g1 = (int)((r * 349 + g * 686 + b * 168) / 1203);
+ int b1 = (int)((r * 272 + g * 534 + b * 131) / 2140);
+ bg[x] = r1 << rs | g1 << gs | b1 << bs | am;
+ }
+ }
+
+ cursor = SDL_ShowCursor(SDL_QUERY);
+ SDL_ShowCursor(SDL_ENABLE);
+
+ mousetoggle = mouselocked;
+ if (mouselocked) GFX_CaptureMouse();
+
+ SDL_Surface* sdlscreen = SDL_SetVideoMode(w, h, 32, SDL_SWSURFACE|(fs?SDL_FULLSCREEN:0));
+ if (sdlscreen == NULL) E_Exit("Could not initialize video mode %ix%ix32 for UI: %s", w, h, SDL_GetError());
+
+ // fade out
+ SDL_Event event;
+ for (int i = 0xff; i > 0; i -= 0x11) {
+ SDL_SetAlpha(screenshot, SDL_SRCALPHA, i);
+ SDL_BlitSurface(background, NULL, sdlscreen, NULL);
+ SDL_BlitSurface(screenshot, NULL, sdlscreen, NULL);
+ SDL_UpdateRect(sdlscreen, 0, 0, 0, 0);
+ while (SDL_PollEvent(&event)) {};
+ SDL_Delay(40);
+ }
+
+ SDL_BlitSurface(background, NULL, sdlscreen, NULL);
+ SDL_UpdateRect(sdlscreen, 0, 0, 0, 0);
+
+ if (screen) screen->setSurface(sdlscreen);
+ else screen = new GUI::ScreenSDL(sdlscreen);
+
+ saved_bpp = render.src.bpp;
+ render.src.bpp = 0;
+ running = true;
+ return screen;
+}
+
+/* Restore screen */
+static void UI_Shutdown(GUI::ScreenSDL *screen) {
+ SDL_Surface *sdlscreen = screen->getSurface();
+ render.src.bpp = saved_bpp;
+
+ // fade in
+ SDL_Event event;
+ for (int i = 0x00; i < 0xff; i += 0x11) {
+ SDL_SetAlpha(screenshot, SDL_SRCALPHA, i);
+ SDL_BlitSurface(background, NULL, sdlscreen, NULL);
+ SDL_BlitSurface(screenshot, NULL, sdlscreen, NULL);
+ SDL_UpdateRect(sdlscreen, 0, 0, 0, 0);
+ while (SDL_PollEvent(&event)) {};
+ SDL_Delay(40);
+ }
+
+ // clean up
+ if (mousetoggle) GFX_CaptureMouse();
+ SDL_ShowCursor(cursor);
+ SDL_FreeSurface(background);
+ SDL_FreeSurface(screenshot);
+ SDL_FreeSurface(sdlscreen);
+ screen->setSurface(NULL);
+ GFX_ResetScreen();
+ SDL_EnableUNICODE(old_unicode);
+ SDL_EnableKeyRepeat(0,0);
+ GFX_SetTitle(-1,-1,false);
+}
+
+/* helper class for command execution */
+class VirtualBatch : public BatchFile {
+protected:
+ std::istringstream lines;
+public:
+ VirtualBatch(DOS_Shell *host, const std::string& cmds) : BatchFile(host, "CON", ""), lines(cmds) {
+ }
+ bool ReadLine(char *line) {
+ std::string l;
+ if (!std::getline(lines,l)) {
+ delete this;
+ return false;
+ }
+ strcpy(line,l.c_str());
+ return true;
+ }
+};
+
+static void UI_RunCommands(GUI::ScreenSDL *s, const std::string &cmds) {
+ DOS_Shell temp;
+ temp.call = true;
+ UI_Shutdown(s);
+ temp.bf = new VirtualBatch(&temp, cmds);
+ temp.RunInternal();
+ temp.ShowPrompt();
+ UI_Startup(s);
+}
+
+
+/* stringification and conversion from the c++ FAQ */
+class BadConversion : public std::runtime_error {
+public: BadConversion(const std::string& s) : std::runtime_error(s) { }
+};
+
+template<typename T> inline std::string stringify(const T& x, std::ios_base& ( *pf )(std::ios_base&) = NULL) {
+ std::ostringstream o;
+ if (pf) o << pf;
+ if (!(o << x)) throw BadConversion(std::string("stringify(") + typeid(x).name() + ")");
+ return o.str();
+}
+
+template<typename T> inline void convert(const std::string& s, T& x, bool failIfLeftoverChars = true, std::ios_base& ( *pf )(std::ios_base&) = NULL) {
+ std::istringstream i(s);
+ if (pf) i >> pf;
+ char c;
+ if (!(i >> x) || (failIfLeftoverChars && i.get(c))) throw BadConversion(s);
+}
+
+/*****************************************************************************************************************************************/
+/* UI classes */
+
+class PropertyEditor : public GUI::Window, public GUI::ActionEventSource_Callback {
+protected:
+ Section_prop * section;
+ Property *prop;
+public:
+ PropertyEditor(Window *parent, int x, int y, Section_prop *section, Property *prop) :
+ Window(parent, x, y, 240, 30), section(section), prop(prop) { }
+
+ virtual bool prepare(std::string &buffer) = 0;
+
+ void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
+ std::string line;
+ if (prepare(line)) {
+ if (first_shell) section->ExecuteDestroy(false);
+ prop->SetValue(GUI::String(line));
+ if (first_shell) section->ExecuteInit(false);
+ }
+ }
+};
+
+class PropertyEditorBool : public PropertyEditor {
+ GUI::Checkbox *input;
+public:
+ PropertyEditorBool(Window *parent, int x, int y, Section_prop *section, Property *prop) :
+ PropertyEditor(parent, x, y, section, prop) {
+ input = new GUI::Checkbox(this, 0, 3, prop->propname.c_str());
+ input->setChecked(static_cast<bool>(prop->GetValue()));
+ }
+
+ bool prepare(std::string &buffer) {
+ if (input->isChecked() == static_cast<bool>(prop->GetValue())) return false;
+ buffer.append(input->isChecked()?"true":"false");
+ return true;
+ }
+};
+
+class PropertyEditorString : public PropertyEditor {
+protected:
+ GUI::Input *input;
+public:
+ PropertyEditorString(Window *parent, int x, int y, Section_prop *section, Property *prop) :
+ PropertyEditor(parent, x, y, section, prop) {
+ new GUI::Label(this, 0, 5, prop->propname);
+ input = new GUI::Input(this, 130, 0, 110);
+ std::string temps = prop->GetValue().ToString();
+ input->setText(stringify(temps));
+ }
+
+ bool prepare(std::string &buffer) {
+ std::string temps = prop->GetValue().ToString();
+ if (input->getText() == GUI::String(temps)) return false;
+ buffer.append(static_cast<const std::string&>(input->getText()));
+ return true;
+ }
+};
+
+class PropertyEditorFloat : public PropertyEditor {
+protected:
+ GUI::Input *input;
+public:
+ PropertyEditorFloat(Window *parent, int x, int y, Section_prop *section, Property *prop) :
+ PropertyEditor(parent, x, y, section, prop) {
+ new GUI::Label(this, 0, 5, prop->propname);
+ input = new GUI::Input(this, 130, 0, 50);
+ input->setText(stringify((double)prop->GetValue()));
+ }
+
+ bool prepare(std::string &buffer) {
+ double val;
+ convert(input->getText(), val, false);
+ if (val == (double)prop->GetValue()) return false;
+ buffer.append(stringify(val));
+ return true;
+ }
+};
+
+class PropertyEditorHex : public PropertyEditor {
+protected:
+ GUI::Input *input;
+public:
+ PropertyEditorHex(Window *parent, int x, int y, Section_prop *section, Property *prop) :
+ PropertyEditor(parent, x, y, section, prop) {
+ new GUI::Label(this, 0, 5, prop->propname);
+ input = new GUI::Input(this, 130, 0, 50);
+ std::string temps = prop->GetValue().ToString();
+ input->setText(temps.c_str());
+ }
+
+ bool prepare(std::string &buffer) {
+ int val;
+ convert(input->getText(), val, false, std::hex);
+ if ((Hex)val == prop->GetValue()) return false;
+ buffer.append(stringify(val, std::hex));
+ return true;
+ }
+};
+
+class PropertyEditorInt : public PropertyEditor {
+protected:
+ GUI::Input *input;
+public:
+ PropertyEditorInt(Window *parent, int x, int y, Section_prop *section, Property *prop) :
+ PropertyEditor(parent, x, y, section, prop) {
+ new GUI::Label(this, 0, 5, prop->propname);
+ input = new GUI::Input(this, 130, 0, 50);
+ //Maybe use ToString() of Value
+ input->setText(stringify(static_cast<int>(prop->GetValue())));
+ }
+
+ bool prepare(std::string &buffer) {
+ int val;
+ convert(input->getText(), val, false);
+ if (val == static_cast<int>(prop->GetValue())) return false;
+ buffer.append(stringify(val));
+ return true;
+ }
+};
+
+class HelpWindow : public GUI::MessageBox {
+public:
+ HelpWindow(GUI::Screen *parent, int x, int y, Section *section) :
+ MessageBox(parent, x, y, 580, "", "") {
+ std::string title(section->GetName());
+ title.at(0) = std::toupper(title.at(0));
+ setTitle("Help for "+title);
+ std::string name = section->GetName();
+ std::transform(name.begin(), name.end(), name.begin(), (int(*)(int))std::toupper);
+ name += "_CONFIGFILE_HELP";
+ setText(MSG_Get(name.c_str()));
+ }
+};
+
+class SectionEditor : public GUI::ToplevelWindow {
+ Section_prop * section;
+public:
+ SectionEditor(GUI::Screen *parent, int x, int y, Section_prop *section) :
+ ToplevelWindow(parent, x, y, 510, 300, ""), section(section) {
+ std::string title(section->GetName());
+ title[0] = std::toupper(title[0]);
+ setTitle("Configuration for "+title);
+ new GUI::Label(this, 5, 10, "Settings:");
+ GUI::Button *b = new GUI::Button(this, 120, 220, "Cancel", 70);
+ b->addActionHandler(this);
+ b = new GUI::Button(this, 200, 220, "Help", 70);
+ b->addActionHandler(this);
+ b = new GUI::Button(this, 280, 220, "OK", 70);
+
+ int i = 0;
+ Property *prop;
+ while ((prop = section->Get_prop(i))) {
+ Prop_bool *pbool = dynamic_cast<Prop_bool*>(prop);
+ Prop_int *pint = dynamic_cast<Prop_int*>(prop);
+ Prop_double *pdouble = dynamic_cast<Prop_double*>(prop);
+ Prop_hex *phex = dynamic_cast<Prop_hex*>(prop);
+ Prop_string *pstring = dynamic_cast<Prop_string*>(prop);
+
+ PropertyEditor *p;
+ if (pbool) p = new PropertyEditorBool(this, 5+250*(i/6), 40+(i%6)*30, section, prop);
+ else if (phex) p = new PropertyEditorHex(this, 5+250*(i/6), 40+(i%6)*30, section, prop);
+ else if (pint) p = new PropertyEditorInt(this, 5+250*(i/6), 40+(i%6)*30, section, prop);
+ else if (pdouble) p = new PropertyEditorFloat(this, 5+250*(i/6), 40+(i%6)*30, section, prop);
+ else if (pstring) p = new PropertyEditorString(this, 5+250*(i/6), 40+(i%6)*30, section, prop);
+ else { i++; continue; }
+ b->addActionHandler(p);
+ i++;
+ }
+ b->addActionHandler(this);
+ }
+
+ void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
+ if (arg == "OK" || arg == "Cancel") close();
+ else if (arg == "Help") new HelpWindow(static_cast<GUI::Screen*>(parent), getX()-10, getY()-10, section);
+ else ToplevelWindow::actionExecuted(b, arg);
+ }
+};
+
+class AutoexecEditor : public GUI::ToplevelWindow {
+ Section_line * section;
+ GUI::Input *content;
+public:
+ AutoexecEditor(GUI::Screen *parent, int x, int y, Section_line *section) :
+ ToplevelWindow(parent, x, y, 450, 300, ""), section(section) {
+ std::string title(section->GetName());
+ title[0] = std::toupper(title[0]);
+ setTitle("Edit "+title);
+ new GUI::Label(this, 5, 10, "Content:");
+ content = new GUI::Input(this, 5, 30, 420, 185);
+ content->setText(section->data);
+ if (first_shell) (new GUI::Button(this, 5, 220, "Append History"))->addActionHandler(this);
+ if (shell_idle) (new GUI::Button(this, 180, 220, "Execute Now"))->addActionHandler(this);
+ (new GUI::Button(this, 290, 220, "Cancel", 70))->addActionHandler(this);
+ (new GUI::Button(this, 360, 220, "OK", 70))->addActionHandler(this);
+ }
+
+ void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
+ if (arg == "OK") section->data = *(std::string*)content->getText();
+ if (arg == "OK" || arg == "Cancel") close();
+ else if (arg == "Append Shell Commands") {
+ std::list<std::string>::reverse_iterator i = first_shell->l_history.rbegin();
+ std::string lines = *(std::string*)content->getText();
+ while (i != first_shell->l_history.rend()) {
+ lines += "\n";
+ lines += *i;
+ ++i;
+ }
+ content->setText(lines);
+ } else if (arg == "Execute Now") {
+ UI_RunCommands(dynamic_cast<GUI::ScreenSDL*>(getScreen()), content->getText());
+ } else ToplevelWindow::actionExecuted(b, arg);
+ }
+};
+
+class SaveDialog : public GUI::ToplevelWindow {
+protected:
+ GUI::Input *name;
+public:
+ SaveDialog(GUI::Screen *parent, int x, int y, const char *title) :
+ ToplevelWindow(parent, x, y, 400, 150, title) {
+ new GUI::Label(this, 5, 10, "Enter filename for configuration file:");
+ name = new GUI::Input(this, 5, 30, 350);
+ name->setText("dosbox.conf");
+ (new GUI::Button(this, 120, 70, "Cancel", 70))->addActionHandler(this);
+ (new GUI::Button(this, 210, 70, "OK", 70))->addActionHandler(this);
+ }
+
+ void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
+ if (arg == "OK") control->PrintConfig(name->getText());
+ close();
+ }
+};
+
+class SaveLangDialog : public GUI::ToplevelWindow {
+protected:
+ GUI::Input *name;
+public:
+ SaveLangDialog(GUI::Screen *parent, int x, int y, const char *title) :
+ ToplevelWindow(parent, x, y, 400, 150, title) {
+ new GUI::Label(this, 5, 10, "Enter filename for language file:");
+ name = new GUI::Input(this, 5, 30, 350);
+ name->setText("messages.txt");
+ (new GUI::Button(this, 120, 70, "Cancel", 70))->addActionHandler(this);
+ (new GUI::Button(this, 210, 70, "OK", 70))->addActionHandler(this);
+ }
+
+ void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
+ if (arg == "OK") MSG_Write(name->getText());
+ close();
+ }
+};
+
+class ConfigurationWindow : public GUI::ToplevelWindow {
+public:
+ ConfigurationWindow(GUI::Screen *parent, GUI::Size x, GUI::Size y, GUI::String title) :
+ GUI::ToplevelWindow(parent, x, y, 470, 290, title) {
+
+ (new GUI::Button(this, 180, 215, "Close", 70))->addActionHandler(this);
+
+ GUI::Menubar *bar = new GUI::Menubar(this, 0, 0, getWidth());
+ bar->addMenu("Configuration");
+ bar->addItem(0,"Save...");
+ bar->addItem(0,"Save Language File...");
+ bar->addItem(0,"");
+ bar->addItem(0,"Close");
+ bar->addMenu("Settings");
+ bar->addMenu("Help");
+ bar->addItem(2,"Introduction");
+ bar->addItem(2,"Getting Started");
+ bar->addItem(2,"CD-ROM Support");
+ bar->addItem(2,"Special Keys");
+ bar->addItem(2,"");
+ bar->addItem(2,"About");
+ bar->addActionHandler(this);
+
+ new GUI::Label(this, 10, 30, "Choose a settings group to configure:");
+
+ Section *sec;
+ int i = 0;
+ while ((sec = control->GetSection(i))) {
+ std::string name = sec->GetName();
+ name[0] = std::toupper(name[0]);
+ GUI::Button *b = new GUI::Button(this, 12+(i/5)*160, 50+(i%5)*30, name, 100);
+ b->addActionHandler(this);
+ bar->addItem(1, name);
+ i++;
+ }
+
+ if (first_shell) {
+ (new GUI::Button(this, 12+(i/5)*160, 50+(i%5)*30, "Keyboard", 100))->addActionHandler(this);
+ bar->addItem(1, "");
+ bar->addItem(1, "Keyboard");
+ }
+ }
+
+ ~ConfigurationWindow() { running = false; }
+
+ void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
+ GUI::String sname = arg;
+ sname.at(0) = std::tolower(sname.at(0));
+ Section *sec;
+ if (arg == "Close" || arg == "Cancel") {
+ running = false;
+ } else if (arg == "Keyboard") {
+ UI_Shutdown(dynamic_cast<GUI::ScreenSDL*>(getScreen()));
+ MAPPER_Run(false);
+ UI_Startup(dynamic_cast<GUI::ScreenSDL*>(getScreen()));
+ } else if (sname == "autoexec") {
+ Section_line *section = static_cast<Section_line *>(control->GetSection((const char *)sname));
+ new AutoexecEditor(getScreen(), 50, 30, section);
+ } else if ((sec = control->GetSection((const char *)sname))) {
+ Section_prop *section = static_cast<Section_prop *>(sec);
+ new SectionEditor(getScreen(), 50, 30, section);
+ } else if (arg == "About") {
+ new GUI::MessageBox(getScreen(), 200, 150, 280, "About DOSBox", "\nDOSBox 0.74\nAn emulator for old DOS Games\n\nCopyright 2002-2019\nThe DOSBox Team");
+ } else if (arg == "Introduction") {
+ new GUI::MessageBox(getScreen(), 20, 50, 600, "Introduction", MSG_Get("PROGRAM_INTRO"));
+ } else if (arg == "Getting Started") {
+ std::string msg = MSG_Get("PROGRAM_INTRO_MOUNT_START");
+#ifdef WIN32
+ msg += MSG_Get("PROGRAM_INTRO_MOUNT_WINDOWS");
+#else
+ msg += MSG_Get("PROGRAM_INTRO_MOUNT_OTHER");
+#endif
+ msg += MSG_Get("PROGRAM_INTRO_MOUNT_END");
+
+ new GUI::MessageBox(getScreen(), 20, 50, 600, std::string("Introduction"), msg);
+ } else if (arg == "CD-ROM Support") {
+ new GUI::MessageBox(getScreen(), 20, 50, 600, "Introduction", MSG_Get("PROGRAM_INTRO_CDROM"));
+ } else if (arg == "Special Keys") {
+ new GUI::MessageBox(getScreen(), 20, 50, 600, "Introduction", MSG_Get("PROGRAM_INTRO_SPECIAL"));
+ } else if (arg == "Save...") {
+ new SaveDialog(getScreen(), 90, 100, "Save Configuration...");
+ } else if (arg == "Save Language File...") {
+ new SaveLangDialog(getScreen(), 90, 100, "Save Language File...");
+ } else {
+ return ToplevelWindow::actionExecuted(b, arg);
+ }
+ }
+};
+
+/*********************************************************************************************************************/
+/* UI control functions */
+
+static void UI_Execute(GUI::ScreenSDL *screen) {
+ SDL_Surface *sdlscreen = screen->getSurface();
+ new ConfigurationWindow(screen, 30, 30, "DOSBox Configuration");
+
+ // event loop
+ SDL_Event event;
+ while (running) {
+ while (SDL_PollEvent(&event)) {
+ if (!screen->event(event)) {
+ if (event.type == SDL_QUIT) running = false;
+ }
+ }
+ //Selecting keyboard will create a new surface.
+ sdlscreen = screen->getSurface();
+ SDL_BlitSurface(background, NULL, sdlscreen, NULL);
+ screen->update(4);
+ SDL_UpdateRect(sdlscreen, 0, 0, 0, 0);
+
+ SDL_Delay(40);
+ }
+}
+
+void UI_Run(bool pressed) {
+ if (pressed) return;
+ GUI::ScreenSDL *screen = UI_Startup(NULL);
+ UI_Execute(screen);
+ UI_Shutdown(screen);
+ delete screen;
+}
+#endif
diff --git a/src/gui/sdl_mapper.cpp b/src/gui/sdl_mapper.cpp
new file mode 100644
index 000000000..471777cab
--- /dev/null
+++ b/src/gui/sdl_mapper.cpp
@@ -0,0 +1,2561 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <vector>
+#include <list>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+
+
+#include "SDL.h"
+#include "SDL_thread.h"
+
+#include "dosbox.h"
+#include "video.h"
+#include "keyboard.h"
+#include "joystick.h"
+#include "support.h"
+#include "mapper.h"
+#include "setup.h"
+#include "pic.h"
+
+enum {
+ CLR_BLACK=0,
+ CLR_GREY=1,
+ CLR_WHITE=2,
+ CLR_RED=3,
+ CLR_BLUE=4,
+ CLR_GREEN=5
+};
+
+enum BB_Types {
+ BB_Next,BB_Add,BB_Del,
+ BB_Save,BB_Exit
+};
+
+enum BC_Types {
+ BC_Mod1,BC_Mod2,BC_Mod3,
+ BC_Hold
+};
+
+#define BMOD_Mod1 0x0001
+#define BMOD_Mod2 0x0002
+#define BMOD_Mod3 0x0004
+
+#define BFLG_Hold 0x0001
+#define BFLG_Repeat 0x0004
+
+
+#define MAXSTICKS 8
+#define MAXACTIVE 16
+#define MAXBUTTON 32
+#define MAXBUTTON_CAP 16
+
+class CEvent;
+class CHandlerEvent;
+class CButton;
+class CBind;
+class CBindGroup;
+
+static void SetActiveEvent(CEvent * event);
+static void SetActiveBind(CBind * _bind);
+extern Bit8u int10_font_14[256 * 14];
+
+static std::vector<CEvent *> events;
+static std::vector<CButton *> buttons;
+static std::vector<CBindGroup *> bindgroups;
+static std::vector<CHandlerEvent *> handlergroup;
+typedef std::list<CBind *> CBindList;
+typedef std::list<CEvent *>::iterator CEventList_it;
+typedef std::list<CBind *>::iterator CBindList_it;
+typedef std::vector<CButton *>::iterator CButton_it;
+typedef std::vector<CEvent *>::iterator CEventVector_it;
+typedef std::vector<CHandlerEvent *>::iterator CHandlerEventVector_it;
+typedef std::vector<CBindGroup *>::iterator CBindGroup_it;
+
+static CBindList holdlist;
+
+
+class CEvent {
+public:
+ CEvent(char const * const _entry) {
+ safe_strncpy(entry,_entry,16);
+ events.push_back(this);
+ bindlist.clear();
+ activity=0;
+ current_value=0;
+ }
+ void AddBind(CBind * bind);
+ virtual ~CEvent() {}
+ virtual void Active(bool yesno)=0;
+ virtual void ActivateEvent(bool ev_trigger,bool skip_action)=0;
+ virtual void DeActivateEvent(bool ev_trigger)=0;
+ void DeActivateAll(void);
+ void SetValue(Bits value){
+ current_value=value;
+ }
+ Bits GetValue(void) {
+ return current_value;
+ }
+ char * GetName(void) { return entry; }
+ virtual bool IsTrigger(void)=0;
+ CBindList bindlist;
+protected:
+ Bitu activity;
+ char entry[16];
+ Bits current_value;
+};
+
+/* class for events which can be ON/OFF only: key presses, joystick buttons, joystick hat */
+class CTriggeredEvent : public CEvent {
+public:
+ CTriggeredEvent(char const * const _entry) : CEvent(_entry) {}
+ virtual bool IsTrigger(void) {
+ return true;
+ }
+ void ActivateEvent(bool ev_trigger,bool skip_action) {
+ if (current_value>25000) {
+ /* value exceeds boundary, trigger event if not active */
+ if (!activity && !skip_action) Active(true);
+ if (activity<32767) activity++;
+ } else {
+ if (activity>0) {
+ /* untrigger event if it is fully inactive */
+ DeActivateEvent(ev_trigger);
+ activity=0;
+ }
+ }
+ }
+ void DeActivateEvent(bool /*ev_trigger*/) {
+ activity--;
+ if (!activity) Active(false);
+ }
+};
+
+/* class for events which have a non-boolean state: joystick axis movement */
+class CContinuousEvent : public CEvent {
+public:
+ CContinuousEvent(char const * const _entry) : CEvent(_entry) {}
+ virtual bool IsTrigger(void) {
+ return false;
+ }
+ void ActivateEvent(bool ev_trigger,bool skip_action) {
+ if (ev_trigger) {
+ activity++;
+ if (!skip_action) Active(true);
+ } else {
+ /* test if no trigger-activity is present, this cares especially
+ about activity of the opposite-direction joystick axis for example */
+ if (!GetActivityCount()) Active(true);
+ }
+ }
+ void DeActivateEvent(bool ev_trigger) {
+ if (ev_trigger) {
+ if (activity>0) activity--;
+ if (activity==0) {
+ /* test if still some trigger-activity is present,
+ adjust the state in this case accordingly */
+ if (GetActivityCount()) RepostActivity();
+ else Active(false);
+ }
+ } else {
+ if (!GetActivityCount()) Active(false);
+ }
+ }
+ virtual Bitu GetActivityCount(void) {
+ return activity;
+ }
+ virtual void RepostActivity(void) {}
+};
+
+class CBind {
+public:
+ virtual ~CBind () {
+ list->remove(this);
+// event->bindlist.remove(this);
+ }
+ CBind(CBindList * _list) {
+ list=_list;
+ _list->push_back(this);
+ mods=flags=0;
+ event=0;
+ active=holding=false;
+ }
+ void AddFlags(char * buf) {
+ if (mods & BMOD_Mod1) strcat(buf," mod1");
+ if (mods & BMOD_Mod2) strcat(buf," mod2");
+ if (mods & BMOD_Mod3) strcat(buf," mod3");
+ if (flags & BFLG_Hold) strcat(buf," hold");
+ }
+ void SetFlags(char * buf) {
+ char * word;
+ while (*(word=StripWord(buf))) {
+ if (!strcasecmp(word,"mod1")) mods|=BMOD_Mod1;
+ if (!strcasecmp(word,"mod2")) mods|=BMOD_Mod2;
+ if (!strcasecmp(word,"mod3")) mods|=BMOD_Mod3;
+ if (!strcasecmp(word,"hold")) flags|=BFLG_Hold;
+ }
+ }
+ void ActivateBind(Bits _value,bool ev_trigger,bool skip_action=false) {
+ if (event->IsTrigger()) {
+ /* use value-boundary for on/off events */
+ if (_value>25000) {
+ event->SetValue(_value);
+ if (active) return;
+ event->ActivateEvent(ev_trigger,skip_action);
+ active=true;
+ } else {
+ if (active) {
+ event->DeActivateEvent(ev_trigger);
+ active=false;
+ }
+ }
+ } else {
+ /* store value for possible later use in the activated event */
+ event->SetValue(_value);
+ event->ActivateEvent(ev_trigger,false);
+ }
+ }
+ void DeActivateBind(bool ev_trigger) {
+ if (event->IsTrigger()) {
+ if (!active) return;
+ active=false;
+ if (flags & BFLG_Hold) {
+ if (!holding) {
+ holdlist.push_back(this);
+ holding=true;
+ return;
+ } else {
+ holdlist.remove(this);
+ holding=false;
+ }
+ }
+ event->DeActivateEvent(ev_trigger);
+ } else {
+ /* store value for possible later use in the activated event */
+ event->SetValue(0);
+ event->DeActivateEvent(ev_trigger);
+ }
+ }
+ virtual void ConfigName(char * buf)=0;
+ virtual void BindName(char * buf)=0;
+
+ Bitu mods,flags;
+ Bit16s value;
+ CEvent * event;
+ CBindList * list;
+ bool active,holding;
+};
+
+
+void CEvent::AddBind(CBind * bind) {
+ bindlist.push_front(bind);
+ bind->event=this;
+}
+void CEvent::DeActivateAll(void) {
+ for (CBindList_it bit=bindlist.begin();bit!=bindlist.end();bit++) {
+ (*bit)->DeActivateBind(true);
+ }
+}
+
+
+
+class CBindGroup {
+public:
+ CBindGroup() {
+ bindgroups.push_back(this);
+ }
+ void ActivateBindList(CBindList * list,Bits value,bool ev_trigger);
+ void DeactivateBindList(CBindList * list,bool ev_trigger);
+ virtual CBind * CreateConfigBind(char *&buf)=0;
+ virtual CBind * CreateEventBind(SDL_Event * event)=0;
+
+ virtual bool CheckEvent(SDL_Event * event)=0;
+ virtual const char * ConfigStart(void)=0;
+ virtual const char * BindStart(void)=0;
+ virtual ~CBindGroup (void) { }
+
+protected:
+
+};
+
+
+#define MAX_SDLKEYS 323
+
+static bool usescancodes;
+static Bit8u scancode_map[MAX_SDLKEYS];
+
+#define Z SDLK_UNKNOWN
+
+#if defined (MACOSX)
+static SDLKey sdlkey_map[]={
+ /* Main block printables */
+ /*00-05*/ SDLK_a, SDLK_s, SDLK_d, SDLK_f, SDLK_h, SDLK_g,
+ /*06-0B*/ SDLK_z, SDLK_x, SDLK_c, SDLK_v, SDLK_WORLD_0, SDLK_b,
+ /*0C-11*/ SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_y, SDLK_t,
+ /*12-17*/ SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_6, SDLK_5,
+ /*18-1D*/ SDLK_EQUALS, SDLK_9, SDLK_7, SDLK_MINUS, SDLK_8, SDLK_0,
+ /*1E-21*/ SDLK_RIGHTBRACKET, SDLK_o, SDLK_u, SDLK_LEFTBRACKET,
+ /*22-23*/ SDLK_i, SDLK_p,
+ /*24-29*/ SDLK_RETURN, SDLK_l, SDLK_j, SDLK_QUOTE, SDLK_k, SDLK_SEMICOLON,
+ /*2A-29*/ SDLK_BACKSLASH, SDLK_COMMA, SDLK_SLASH, SDLK_n, SDLK_m,
+ /*2F-2F*/ SDLK_PERIOD,
+
+ /* Spaces, controls, modifiers (dosbox uses LMETA only for
+ * hotkeys, it's not really mapped to an emulated key) */
+ /*30-33*/ SDLK_TAB, SDLK_SPACE, SDLK_BACKQUOTE, SDLK_BACKSPACE,
+ /*34-37*/ Z, SDLK_ESCAPE, Z, SDLK_LMETA,
+ /*38-3B*/ SDLK_LSHIFT, SDLK_CAPSLOCK, SDLK_LALT, SDLK_LCTRL,
+
+ /*3C-40*/ Z, Z, Z, Z, Z,
+
+ /* Keypad (KP_EQUALS not supported, NUMLOCK used on what is CLEAR
+ * in Mac OS X) */
+ /*41-46*/ SDLK_KP_PERIOD, Z, SDLK_KP_MULTIPLY, Z, SDLK_KP_PLUS, Z,
+ /*47-4A*/ SDLK_NUMLOCK /*==SDLK_CLEAR*/, Z, Z, Z,
+ /*4B-4D*/ SDLK_KP_DIVIDE, SDLK_KP_ENTER, Z,
+ /*4E-51*/ SDLK_KP_MINUS, Z, Z, SDLK_KP_EQUALS,
+ /*52-57*/ SDLK_KP0, SDLK_KP1, SDLK_KP2, SDLK_KP3, SDLK_KP4, SDLK_KP5,
+ /*58-5C*/ SDLK_KP6, SDLK_KP7, Z, SDLK_KP8, SDLK_KP9,
+
+ /*5D-5F*/ Z, Z, Z,
+
+ /* Function keys and cursor blocks (F13 not supported, F14 =>
+ * PRINT[SCREEN], F15 => SCROLLOCK, F16 => PAUSE, HELP => INSERT) */
+ /*60-64*/ SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F3, SDLK_F8,
+ /*65-6A*/ SDLK_F9, Z, SDLK_F11, Z, SDLK_F13, SDLK_PAUSE /*==SDLK_F16*/,
+ /*6B-70*/ SDLK_PRINT /*==SDLK_F14*/, Z, SDLK_F10, Z, SDLK_F12, Z,
+ /*71-72*/ SDLK_SCROLLOCK /*==SDLK_F15*/, SDLK_INSERT /*==SDLK_HELP*/,
+ /*73-77*/ SDLK_HOME, SDLK_PAGEUP, SDLK_DELETE, SDLK_F4, SDLK_END,
+ /*78-7C*/ SDLK_F2, SDLK_PAGEDOWN, SDLK_F1, SDLK_LEFT, SDLK_RIGHT,
+ /*7D-7E*/ SDLK_DOWN, SDLK_UP,
+
+ /*7F-7F*/ Z,
+
+ /* 4 extra keys that don't really exist, but are needed for
+ * round-trip mapping (dosbox uses RMETA only for hotkeys, it's
+ * not really mapped to an emulated key) */
+ SDLK_RMETA, SDLK_RSHIFT, SDLK_RALT, SDLK_RCTRL
+};
+#define MAX_SCANCODES (0x80+4)
+/* Make sure that the table above has the expected size. This
+ expression will raise a compiler error if the condition is false. */
+typedef char assert_right_size [MAX_SCANCODES == (sizeof(sdlkey_map)/sizeof(sdlkey_map[0])) ? 1 : -1];
+
+#else // !MACOSX
+
+#define MAX_SCANCODES 0xdf
+static SDLKey sdlkey_map[MAX_SCANCODES]={SDLK_UNKNOWN,SDLK_ESCAPE,
+ SDLK_1,SDLK_2,SDLK_3,SDLK_4,SDLK_5,SDLK_6,SDLK_7,SDLK_8,SDLK_9,SDLK_0,
+ /* 0x0c: */
+ SDLK_MINUS,SDLK_EQUALS,SDLK_BACKSPACE,SDLK_TAB,
+ SDLK_q,SDLK_w,SDLK_e,SDLK_r,SDLK_t,SDLK_y,SDLK_u,SDLK_i,SDLK_o,SDLK_p,
+ SDLK_LEFTBRACKET,SDLK_RIGHTBRACKET,SDLK_RETURN,SDLK_LCTRL,
+ SDLK_a,SDLK_s,SDLK_d,SDLK_f,SDLK_g,SDLK_h,SDLK_j,SDLK_k,SDLK_l,
+ SDLK_SEMICOLON,SDLK_QUOTE,SDLK_BACKQUOTE,SDLK_LSHIFT,SDLK_BACKSLASH,
+ SDLK_z,SDLK_x,SDLK_c,SDLK_v,SDLK_b,SDLK_n,SDLK_m,
+ /* 0x33: */
+ SDLK_COMMA,SDLK_PERIOD,SDLK_SLASH,SDLK_RSHIFT,SDLK_KP_MULTIPLY,
+ SDLK_LALT,SDLK_SPACE,SDLK_CAPSLOCK,
+ SDLK_F1,SDLK_F2,SDLK_F3,SDLK_F4,SDLK_F5,SDLK_F6,SDLK_F7,SDLK_F8,SDLK_F9,SDLK_F10,
+ /* 0x45: */
+ SDLK_NUMLOCK,SDLK_SCROLLOCK,
+ SDLK_KP7,SDLK_KP8,SDLK_KP9,SDLK_KP_MINUS,SDLK_KP4,SDLK_KP5,SDLK_KP6,SDLK_KP_PLUS,
+ SDLK_KP1,SDLK_KP2,SDLK_KP3,SDLK_KP0,SDLK_KP_PERIOD,
+ SDLK_UNKNOWN,SDLK_UNKNOWN,
+ SDLK_LESS,SDLK_F11,SDLK_F12, Z, Z, Z, Z, Z, Z, Z,
+ /* 0x60: */
+ Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
+ /* 0x70: */
+ Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
+ /* 0x80: */
+ Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
+ /* 0x90: */
+ Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
+ /* 0xA0: */
+ Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
+ /* 0xB0: */
+ Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
+ /* 0xC0: */
+ Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
+ /* 0xD0: */
+ Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z//,Z,Z,
+ /* 0xE0: */
+ //Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z,
+ /* 0xF0: */
+// Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z//,Z,Z
+
+};
+#endif
+
+#undef Z
+
+
+SDLKey MapSDLCode(Bitu skey) {
+// LOG_MSG("MapSDLCode %d %X",skey,skey);
+ if (usescancodes) {
+ if (skey<MAX_SCANCODES) return sdlkey_map[skey];
+ else return SDLK_UNKNOWN;
+ } else return (SDLKey)skey;
+}
+
+Bitu GetKeyCode(SDL_keysym keysym) {
+// LOG_MSG("GetKeyCode %X %X %X",keysym.scancode,keysym.sym,keysym.mod);
+ if (usescancodes) {
+ Bitu key=(Bitu)keysym.scancode;
+ if (key==0
+#if defined (MACOSX)
+ /* On Mac on US keyboards, scancode 0 is actually the 'a'
+ * key. For good measure exclude all printables from this
+ * condition. */
+ && (keysym.sym < SDLK_SPACE || keysym.sym > SDLK_WORLD_95)
+#endif
+ ) {
+ /* try to retrieve key from symbolic key as scancode is zero */
+ if (keysym.sym<MAX_SDLKEYS) key=scancode_map[(Bitu)keysym.sym];
+ }
+#if !defined (WIN32) && !defined (MACOSX) && !defined(OS2)
+ /* Linux adds 8 to all scancodes */
+ else key-=8;
+#endif
+#if defined (WIN32)
+ switch (key) {
+ case 0x1c: // ENTER
+ case 0x1d: // CONTROL
+ case 0x35: // SLASH
+ case 0x37: // PRINTSCREEN
+ case 0x38: // ALT
+ case 0x45: // PAUSE
+ case 0x47: // HOME
+ case 0x48: // cursor UP
+ case 0x49: // PAGE UP
+ case 0x4b: // cursor LEFT
+ case 0x4d: // cursor RIGHT
+ case 0x4f: // END
+ case 0x50: // cursor DOWN
+ case 0x51: // PAGE DOWN
+ case 0x52: // INSERT
+ case 0x53: // DELETE
+ if (GFX_SDLUsingWinDIB()) key=scancode_map[(Bitu)keysym.sym];
+ break;
+ }
+#endif
+ return key;
+ } else {
+#if defined (WIN32)
+ /* special handling of 102-key under windows */
+ if ((keysym.sym==SDLK_BACKSLASH) && (keysym.scancode==0x56)) return (Bitu)SDLK_LESS;
+#endif
+ return (Bitu)keysym.sym;
+ }
+}
+
+
+class CKeyBind;
+class CKeyBindGroup;
+
+class CKeyBind : public CBind {
+public:
+ CKeyBind(CBindList * _list,SDLKey _key) : CBind(_list) {
+ key = _key;
+ }
+ void BindName(char * buf) {
+ sprintf(buf,"Key %s",SDL_GetKeyName(MapSDLCode((Bitu)key)));
+ }
+ void ConfigName(char * buf) {
+ sprintf(buf,"key %d",MapSDLCode((Bitu)key));
+ }
+public:
+ SDLKey key;
+};
+
+class CKeyBindGroup : public CBindGroup {
+public:
+ CKeyBindGroup(Bitu _keys) : CBindGroup (){
+ lists=new CBindList[_keys];
+ for (Bitu i=0;i<_keys;i++) lists[i].clear();
+ keys=_keys;
+ configname="key";
+ }
+ ~CKeyBindGroup() { delete[] lists; }
+ CBind * CreateConfigBind(char *& buf) {
+ if (strncasecmp(buf,configname,strlen(configname))) return 0;
+ StripWord(buf);char * num=StripWord(buf);
+ Bitu code=ConvDecWord(num);
+ if (usescancodes) {
+ if (code<MAX_SDLKEYS) code=scancode_map[code];
+ else code=0;
+ }
+ CBind * bind=CreateKeyBind((SDLKey)code);
+ return bind;
+ }
+ CBind * CreateEventBind(SDL_Event * event) {
+ if (event->type!=SDL_KEYDOWN) return 0;
+ return CreateKeyBind((SDLKey)GetKeyCode(event->key.keysym));
+ };
+ bool CheckEvent(SDL_Event * event) {
+ if (event->type!=SDL_KEYDOWN && event->type!=SDL_KEYUP) return false;
+ Bitu key=GetKeyCode(event->key.keysym);
+// LOG_MSG("key type %i is %x [%x %x]",event->type,key,event->key.keysym.sym,event->key.keysym.scancode);
+ assert(Bitu(event->key.keysym.sym)<keys);
+ if (event->type==SDL_KEYDOWN) ActivateBindList(&lists[key],0x7fff,true);
+ else DeactivateBindList(&lists[key],true);
+ return 0;
+ }
+ CBind * CreateKeyBind(SDLKey _key) {
+ if (!usescancodes) assert((Bitu)_key<keys);
+ return new CKeyBind(&lists[(Bitu)_key],_key);
+ }
+private:
+ const char * ConfigStart(void) {
+ return configname;
+ }
+ const char * BindStart(void) {
+ return "Key";
+ }
+protected:
+ const char * configname;
+ CBindList * lists;
+ Bitu keys;
+};
+
+#define MAX_VJOY_BUTTONS 8
+#define MAX_VJOY_HAT 16
+#define MAX_VJOY_AXIS 8
+static struct {
+ bool button_pressed[MAX_VJOY_BUTTONS];
+ Bit16s axis_pos[MAX_VJOY_AXIS];
+ bool hat_pressed[MAX_VJOY_HAT];
+} virtual_joysticks[2];
+
+
+class CJAxisBind;
+class CJButtonBind;
+class CJHatBind;
+
+class CJAxisBind : public CBind {
+public:
+ CJAxisBind(CBindList * _list,CBindGroup * _group,Bitu _axis,bool _positive) : CBind(_list){
+ group = _group;
+ axis = _axis;
+ positive = _positive;
+ }
+ void ConfigName(char * buf) {
+ sprintf(buf,"%s axis %d %d",group->ConfigStart(),axis,positive ? 1 : 0);
+ }
+ void BindName(char * buf) {
+ sprintf(buf,"%s Axis %d%s",group->BindStart(),axis,positive ? "+" : "-");
+ }
+protected:
+ CBindGroup * group;
+ Bitu axis;
+ bool positive;
+};
+
+class CJButtonBind : public CBind {
+public:
+ CJButtonBind(CBindList * _list,CBindGroup * _group,Bitu _button) : CBind(_list) {
+ group = _group;
+ button=_button;
+ }
+ void ConfigName(char * buf) {
+ sprintf(buf,"%s button %d",group->ConfigStart(),button);
+ }
+ void BindName(char * buf) {
+ sprintf(buf,"%s Button %d",group->BindStart(),button);
+ }
+protected:
+ CBindGroup * group;
+ Bitu button;
+};
+
+class CJHatBind : public CBind {
+public:
+ CJHatBind(CBindList * _list,CBindGroup * _group,Bitu _hat,Bit8u _dir) : CBind(_list) {
+ group = _group;
+ hat = _hat;
+ dir = _dir;
+ /* allow only one hat position */
+ if (dir&SDL_HAT_UP) dir=SDL_HAT_UP;
+ else if (dir&SDL_HAT_RIGHT) dir=SDL_HAT_RIGHT;
+ else if (dir&SDL_HAT_DOWN) dir=SDL_HAT_DOWN;
+ else if (dir&SDL_HAT_LEFT) dir=SDL_HAT_LEFT;
+ else E_Exit("MAPPER:JOYSTICK:Invalid hat position");
+ }
+ void ConfigName(char * buf) {
+ sprintf(buf,"%s hat %d %d",group->ConfigStart(),hat,dir);
+ }
+ void BindName(char * buf) {
+ sprintf(buf,"%s Hat %d %s",group->BindStart(),hat,(dir==SDL_HAT_UP)?"up":
+ ((dir==SDL_HAT_RIGHT)?"right":
+ ((dir==SDL_HAT_DOWN)?"down":"left")));
+ }
+protected:
+ CBindGroup * group;
+ Bitu hat;
+ Bit8u dir;
+};
+
+bool autofire = false;
+
+class CStickBindGroup : public CBindGroup {
+public:
+ CStickBindGroup(Bitu _stick,Bitu _emustick,bool _dummy=false) : CBindGroup (){
+ stick=_stick; // the number of the physical device (SDL numbering|)
+ emustick=_emustick; // the number of the emulated device
+ sprintf(configname,"stick_%d",emustick);
+
+ sdl_joystick=NULL;
+ axes=0; buttons=0; hats=0;
+ button_wrap=0;
+ button_cap=0; axes_cap=0; hats_cap=0;
+ emulated_buttons=0; emulated_axes=0; emulated_hats=0;
+
+ is_dummy=_dummy;
+ if (_dummy) return;
+
+ // initialize binding lists and position data
+ pos_axis_lists=new CBindList[4];
+ neg_axis_lists=new CBindList[4];
+ button_lists=new CBindList[MAXBUTTON];
+ hat_lists=new CBindList[4];
+ Bitu i;
+ for (i=0; i<MAXBUTTON; i++) {
+ button_autofire[i]=0;
+ old_button_state[i]=0;
+ }
+ for(i=0;i<16;i++) old_hat_state[i]=0;
+ for (i=0; i<4; i++) {
+ old_pos_axis_state[i]=false;
+ old_neg_axis_state[i]=false;
+ }
+
+ // initialize emulated joystick state
+ emulated_axes=2;
+ emulated_buttons=2;
+ emulated_hats=0;
+ JOYSTICK_Enable(emustick,true);
+
+ sdl_joystick=SDL_JoystickOpen(_stick);
+ if (sdl_joystick==NULL) {
+ button_wrap=emulated_buttons;
+ return;
+ }
+
+ axes=SDL_JoystickNumAxes(sdl_joystick);
+ buttons=SDL_JoystickNumButtons(sdl_joystick);
+ hats=SDL_JoystickNumHats(sdl_joystick);
+ button_wrap=buttons;
+ button_cap=buttons;
+ if (button_wrapping_enabled) {
+ button_wrap=emulated_buttons;
+ if (buttons>MAXBUTTON_CAP) button_cap = MAXBUTTON_CAP;
+ }
+ if (button_wrap > MAXBUTTON) button_wrap = MAXBUTTON;
+ axes_cap=emulated_axes;
+ if (axes_cap>axes) axes_cap=axes;
+ hats_cap=emulated_hats;
+ if (hats_cap>hats) hats_cap=hats;
+ LOG_MSG("Using joystick %s with %d axes, %d buttons and %d hat(s)",SDL_JoystickName(stick),axes,buttons,hats);
+ }
+ ~CStickBindGroup() {
+ SDL_JoystickClose(sdl_joystick);
+ delete[] pos_axis_lists;
+ delete[] neg_axis_lists;
+ delete[] button_lists;
+ delete[] hat_lists;
+ }
+
+ CBind * CreateConfigBind(char *& buf) {
+ if (strncasecmp(configname,buf,strlen(configname))) return 0;
+ StripWord(buf);char * type=StripWord(buf);
+ CBind * bind=0;
+ if (!strcasecmp(type,"axis")) {
+ Bitu ax=ConvDecWord(StripWord(buf));
+ bool pos=ConvDecWord(StripWord(buf)) > 0;
+ bind=CreateAxisBind(ax,pos);
+ } else if (!strcasecmp(type,"button")) {
+ Bitu but=ConvDecWord(StripWord(buf));
+ bind=CreateButtonBind(but);
+ } else if (!strcasecmp(type,"hat")) {
+ Bitu hat=ConvDecWord(StripWord(buf));
+ Bit8u dir=(Bit8u)ConvDecWord(StripWord(buf));
+ bind=CreateHatBind(hat,dir);
+ }
+ return bind;
+ }
+ CBind * CreateEventBind(SDL_Event * event) {
+ if (event->type==SDL_JOYAXISMOTION) {
+ if (event->jaxis.which!=stick) return 0;
+#if defined (REDUCE_JOYSTICK_POLLING)
+ if (event->jaxis.axis>=emulated_axes) return 0;
+#endif
+ if (abs(event->jaxis.value)<25000) return 0;
+ return CreateAxisBind(event->jaxis.axis,event->jaxis.value>0);
+ } else if (event->type==SDL_JOYBUTTONDOWN) {
+ if (event->jbutton.which!=stick) return 0;
+#if defined (REDUCE_JOYSTICK_POLLING)
+ return CreateButtonBind(event->jbutton.button%button_wrap);
+#else
+ return CreateButtonBind(event->jbutton.button);
+#endif
+ } else if (event->type==SDL_JOYHATMOTION) {
+ if (event->jhat.which!=stick) return 0;
+ if (event->jhat.value==0) return 0;
+ if (event->jhat.value>(SDL_HAT_UP|SDL_HAT_RIGHT|SDL_HAT_DOWN|SDL_HAT_LEFT)) return 0;
+ return CreateHatBind(event->jhat.hat,event->jhat.value);
+ } else return 0;
+ }
+
+ virtual bool CheckEvent(SDL_Event * event) {
+ SDL_JoyAxisEvent * jaxis = NULL;
+ SDL_JoyButtonEvent * jbutton = NULL;
+ Bitu but = 0;
+
+ switch(event->type) {
+ case SDL_JOYAXISMOTION:
+ jaxis = &event->jaxis;
+ if(jaxis->which == stick) {
+ if(jaxis->axis == 0)
+ JOYSTICK_Move_X(emustick,(float)(jaxis->value/32768.0));
+ else if(jaxis->axis == 1)
+ JOYSTICK_Move_Y(emustick,(float)(jaxis->value/32768.0));
+ }
+ break;
+ case SDL_JOYBUTTONDOWN:
+ case SDL_JOYBUTTONUP:
+ jbutton = &event->jbutton;
+ bool state;
+ state=jbutton->type==SDL_JOYBUTTONDOWN;
+ but = jbutton->button % emulated_buttons;
+ if (jbutton->which == stick) {
+ JOYSTICK_Button(emustick,but,state);
+ }
+ break;
+ }
+ return false;
+ }
+
+ virtual void UpdateJoystick() {
+ if (is_dummy) return;
+ /* query SDL joystick and activate bindings */
+ ActivateJoystickBoundEvents();
+
+ bool button_pressed[MAXBUTTON];
+ Bitu i;
+ for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
+ for (i=0; i<MAX_VJOY_BUTTONS; i++) {
+ if (virtual_joysticks[emustick].button_pressed[i])
+ button_pressed[i % button_wrap]=true;
+ }
+ for (i=0; i<emulated_buttons; i++) {
+ if (autofire && (button_pressed[i]))
+ JOYSTICK_Button(emustick,i,(++button_autofire[i])&1);
+ else
+ JOYSTICK_Button(emustick,i,button_pressed[i]);
+ }
+
+ JOYSTICK_Move_X(emustick,((float)virtual_joysticks[emustick].axis_pos[0])/32768.0f);
+ JOYSTICK_Move_Y(emustick,((float)virtual_joysticks[emustick].axis_pos[1])/32768.0f);
+ }
+
+ void ActivateJoystickBoundEvents() {
+ if (GCC_UNLIKELY(sdl_joystick==NULL)) return;
+
+ Bitu i;
+
+ bool button_pressed[MAXBUTTON];
+ for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
+ /* read button states */
+ for (i=0; i<button_cap; i++) {
+ if (SDL_JoystickGetButton(sdl_joystick,i))
+ button_pressed[i % button_wrap]=true;
+ }
+ for (i=0; i<button_wrap; i++) {
+ /* activate binding if button state has changed */
+ if (button_pressed[i]!=old_button_state[i]) {
+ if (button_pressed[i]) ActivateBindList(&button_lists[i],32767,true);
+ else DeactivateBindList(&button_lists[i],true);
+ old_button_state[i]=button_pressed[i];
+ }
+ }
+
+ for (i=0; i<axes_cap; i++) {
+ Sint16 caxis_pos=SDL_JoystickGetAxis(sdl_joystick,i);
+ /* activate bindings for joystick position */
+ if (caxis_pos>1) {
+ if (old_neg_axis_state[i]) {
+ DeactivateBindList(&neg_axis_lists[i],false);
+ old_neg_axis_state[i] = false;
+ }
+ ActivateBindList(&pos_axis_lists[i],caxis_pos,false);
+ old_pos_axis_state[i] = true;
+ } else if (caxis_pos<-1) {
+ if (old_pos_axis_state[i]) {
+ DeactivateBindList(&pos_axis_lists[i],false);
+ old_pos_axis_state[i] = false;
+ }
+ if (caxis_pos!=-32768) caxis_pos=(Sint16)abs(caxis_pos);
+ else caxis_pos=32767;
+ ActivateBindList(&neg_axis_lists[i],caxis_pos,false);
+ old_neg_axis_state[i] = true;
+ } else {
+ /* center */
+ if (old_pos_axis_state[i]) {
+ DeactivateBindList(&pos_axis_lists[i],false);
+ old_pos_axis_state[i] = false;
+ }
+ if (old_neg_axis_state[i]) {
+ DeactivateBindList(&neg_axis_lists[i],false);
+ old_neg_axis_state[i] = false;
+ }
+ }
+ }
+
+ for (i=0; i<hats_cap; i++) {
+ Uint8 chat_state=SDL_JoystickGetHat(sdl_joystick,i);
+
+ /* activate binding if hat state has changed */
+ if ((chat_state & SDL_HAT_UP) != (old_hat_state[i] & SDL_HAT_UP)) {
+ if (chat_state & SDL_HAT_UP) ActivateBindList(&hat_lists[(i<<2)+0],32767,true);
+ else DeactivateBindList(&hat_lists[(i<<2)+0],true);
+ }
+ if ((chat_state & SDL_HAT_RIGHT) != (old_hat_state[i] & SDL_HAT_RIGHT)) {
+ if (chat_state & SDL_HAT_RIGHT) ActivateBindList(&hat_lists[(i<<2)+1],32767,true);
+ else DeactivateBindList(&hat_lists[(i<<2)+1],true);
+ }
+ if ((chat_state & SDL_HAT_DOWN) != (old_hat_state[i] & SDL_HAT_DOWN)) {
+ if (chat_state & SDL_HAT_DOWN) ActivateBindList(&hat_lists[(i<<2)+2],32767,true);
+ else DeactivateBindList(&hat_lists[(i<<2)+2],true);
+ }
+ if ((chat_state & SDL_HAT_LEFT) != (old_hat_state[i] & SDL_HAT_LEFT)) {
+ if (chat_state & SDL_HAT_LEFT) ActivateBindList(&hat_lists[(i<<2)+3],32767,true);
+ else DeactivateBindList(&hat_lists[(i<<2)+3],true);
+ }
+ old_hat_state[i]=chat_state;
+ }
+ }
+
+private:
+ CBind * CreateAxisBind(Bitu axis,bool positive) {
+ if (axis<emulated_axes) {
+ if (positive) return new CJAxisBind(&pos_axis_lists[axis],this,axis,positive);
+ else return new CJAxisBind(&neg_axis_lists[axis],this,axis,positive);
+ }
+ return NULL;
+ }
+ CBind * CreateButtonBind(Bitu button) {
+ if (button<button_wrap)
+ return new CJButtonBind(&button_lists[button],this,button);
+ return NULL;
+ }
+ CBind * CreateHatBind(Bitu hat,Bit8u value) {
+ Bitu hat_dir;
+ if (value&SDL_HAT_UP) hat_dir=0;
+ else if (value&SDL_HAT_RIGHT) hat_dir=1;
+ else if (value&SDL_HAT_DOWN) hat_dir=2;
+ else if (value&SDL_HAT_LEFT) hat_dir=3;
+ else return NULL;
+ return new CJHatBind(&hat_lists[(hat<<2)+hat_dir],this,hat,value);
+ }
+ const char * ConfigStart(void) {
+ return configname;
+ }
+ const char * BindStart(void) {
+ if (sdl_joystick!=NULL) return SDL_JoystickName(stick);
+ else return "[missing joystick]";
+ }
+
+protected:
+ CBindList * pos_axis_lists;
+ CBindList * neg_axis_lists;
+ CBindList * button_lists;
+ CBindList * hat_lists;
+ Bitu stick,emustick,axes,buttons,hats,emulated_axes,emulated_buttons,emulated_hats;
+ Bitu button_wrap,button_cap,axes_cap,hats_cap;
+ SDL_Joystick * sdl_joystick;
+ char configname[10];
+ Bitu button_autofire[MAXBUTTON];
+ bool old_button_state[MAXBUTTON];
+ bool old_pos_axis_state[16];
+ bool old_neg_axis_state[16];
+ Uint8 old_hat_state[16];
+ bool is_dummy;
+};
+
+class C4AxisBindGroup : public CStickBindGroup {
+public:
+ C4AxisBindGroup(Bitu _stick,Bitu _emustick) : CStickBindGroup (_stick,_emustick){
+ emulated_axes=4;
+ emulated_buttons=4;
+ if (button_wrapping_enabled) button_wrap=emulated_buttons;
+
+ axes_cap=emulated_axes;
+ if (axes_cap>axes) axes_cap=axes;
+ hats_cap=emulated_hats;
+ if (hats_cap>hats) hats_cap=hats;
+
+ JOYSTICK_Enable(1,true);
+ }
+
+ bool CheckEvent(SDL_Event * event) {
+ SDL_JoyAxisEvent * jaxis = NULL;
+ SDL_JoyButtonEvent * jbutton = NULL;
+ Bitu but = 0;
+
+ switch(event->type) {
+ case SDL_JOYAXISMOTION:
+ jaxis = &event->jaxis;
+ if(jaxis->which == stick && jaxis->axis < 4) {
+ if(jaxis->axis & 1)
+ JOYSTICK_Move_Y(jaxis->axis>>1 & 1,(float)(jaxis->value/32768.0));
+ else
+ JOYSTICK_Move_X(jaxis->axis>>1 & 1,(float)(jaxis->value/32768.0));
+ }
+ break;
+ case SDL_JOYBUTTONDOWN:
+ case SDL_JOYBUTTONUP:
+ jbutton = &event->jbutton;
+ bool state;
+ state=jbutton->type==SDL_JOYBUTTONDOWN;
+ but = jbutton->button % emulated_buttons;
+ if (jbutton->which == stick) {
+ JOYSTICK_Button((but >> 1),(but & 1),state);
+ }
+ break;
+ }
+ return false;
+ }
+
+ virtual void UpdateJoystick() {
+ /* query SDL joystick and activate bindings */
+ ActivateJoystickBoundEvents();
+
+ bool button_pressed[MAXBUTTON];
+ Bitu i;
+ for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
+ for (i=0; i<MAX_VJOY_BUTTONS; i++) {
+ if (virtual_joysticks[0].button_pressed[i])
+ button_pressed[i % button_wrap]=true;
+
+ }
+ for (i=0; i<emulated_buttons; i++) {
+ if (autofire && (button_pressed[i]))
+ JOYSTICK_Button(i>>1,i&1,(++button_autofire[i])&1);
+ else
+ JOYSTICK_Button(i>>1,i&1,button_pressed[i]);
+ }
+
+ JOYSTICK_Move_X(0,((float)virtual_joysticks[0].axis_pos[0])/32768.0f);
+ JOYSTICK_Move_Y(0,((float)virtual_joysticks[0].axis_pos[1])/32768.0f);
+ JOYSTICK_Move_X(1,((float)virtual_joysticks[0].axis_pos[2])/32768.0f);
+ JOYSTICK_Move_Y(1,((float)virtual_joysticks[0].axis_pos[3])/32768.0f);
+ }
+};
+
+class CFCSBindGroup : public CStickBindGroup {
+public:
+ CFCSBindGroup(Bitu _stick,Bitu _emustick) : CStickBindGroup (_stick,_emustick){
+ emulated_axes=4;
+ emulated_buttons=4;
+ old_hat_position=0;
+ emulated_hats=1;
+ if (button_wrapping_enabled) button_wrap=emulated_buttons;
+
+ axes_cap=emulated_axes;
+ if (axes_cap>axes) axes_cap=axes;
+ hats_cap=emulated_hats;
+ if (hats_cap>hats) hats_cap=hats;
+
+ JOYSTICK_Enable(1,true);
+ JOYSTICK_Move_Y(1,1.0);
+ }
+
+ bool CheckEvent(SDL_Event * event) {
+ SDL_JoyAxisEvent * jaxis = NULL;
+ SDL_JoyButtonEvent * jbutton = NULL;
+ SDL_JoyHatEvent * jhat = NULL;
+ Bitu but = 0;
+
+ switch(event->type) {
+ case SDL_JOYAXISMOTION:
+ jaxis = &event->jaxis;
+ if(jaxis->which == stick) {
+ if(jaxis->axis == 0)
+ JOYSTICK_Move_X(0,(float)(jaxis->value/32768.0));
+ else if(jaxis->axis == 1)
+ JOYSTICK_Move_Y(0,(float)(jaxis->value/32768.0));
+ else if(jaxis->axis == 2)
+ JOYSTICK_Move_X(1,(float)(jaxis->value/32768.0));
+ }
+ break;
+ case SDL_JOYHATMOTION:
+ jhat = &event->jhat;
+ if(jhat->which == stick) DecodeHatPosition(jhat->value);
+ break;
+ case SDL_JOYBUTTONDOWN:
+ case SDL_JOYBUTTONUP:
+ jbutton = &event->jbutton;
+ bool state;
+ state=jbutton->type==SDL_JOYBUTTONDOWN;
+ but = jbutton->button % emulated_buttons;
+ if (jbutton->which == stick) {
+ JOYSTICK_Button((but >> 1),(but & 1),state);
+ }
+ break;
+ }
+ return false;
+ }
+
+ virtual void UpdateJoystick() {
+ /* query SDL joystick and activate bindings */
+ ActivateJoystickBoundEvents();
+
+ bool button_pressed[MAXBUTTON];
+ Bitu i;
+ for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
+ for (i=0; i<MAX_VJOY_BUTTONS; i++) {
+ if (virtual_joysticks[0].button_pressed[i])
+ button_pressed[i % button_wrap]=true;
+
+ }
+ for (i=0; i<emulated_buttons; i++) {
+ if (autofire && (button_pressed[i]))
+ JOYSTICK_Button(i>>1,i&1,(++button_autofire[i])&1);
+ else
+ JOYSTICK_Button(i>>1,i&1,button_pressed[i]);
+ }
+
+ JOYSTICK_Move_X(0,((float)virtual_joysticks[0].axis_pos[0])/32768.0f);
+ JOYSTICK_Move_Y(0,((float)virtual_joysticks[0].axis_pos[1])/32768.0f);
+ JOYSTICK_Move_X(1,((float)virtual_joysticks[0].axis_pos[2])/32768.0f);
+
+ Uint8 hat_pos=0;
+ if (virtual_joysticks[0].hat_pressed[0]) hat_pos|=SDL_HAT_UP;
+ else if (virtual_joysticks[0].hat_pressed[2]) hat_pos|=SDL_HAT_DOWN;
+ if (virtual_joysticks[0].hat_pressed[3]) hat_pos|=SDL_HAT_LEFT;
+ else if (virtual_joysticks[0].hat_pressed[1]) hat_pos|=SDL_HAT_RIGHT;
+
+ if (hat_pos!=old_hat_position) {
+ DecodeHatPosition(hat_pos);
+ old_hat_position=hat_pos;
+ }
+ }
+
+private:
+ Uint8 old_hat_position;
+
+ void DecodeHatPosition(Uint8 hat_pos) {
+ switch(hat_pos) {
+ case SDL_HAT_CENTERED:
+ JOYSTICK_Move_Y(1,1.0);
+ break;
+ case SDL_HAT_UP:
+ JOYSTICK_Move_Y(1,-1.0);
+ break;
+ case SDL_HAT_RIGHT:
+ JOYSTICK_Move_Y(1,-0.5);
+ break;
+ case SDL_HAT_DOWN:
+ JOYSTICK_Move_Y(1,0.0);
+ break;
+ case SDL_HAT_LEFT:
+ JOYSTICK_Move_Y(1,0.5);
+ break;
+ case SDL_HAT_LEFTUP:
+ if(JOYSTICK_GetMove_Y(1) < 0)
+ JOYSTICK_Move_Y(1,0.5);
+ else
+ JOYSTICK_Move_Y(1,-1.0);
+ break;
+ case SDL_HAT_RIGHTUP:
+ if(JOYSTICK_GetMove_Y(1) < -0.7)
+ JOYSTICK_Move_Y(1,-0.5);
+ else
+ JOYSTICK_Move_Y(1,-1.0);
+ break;
+ case SDL_HAT_RIGHTDOWN:
+ if(JOYSTICK_GetMove_Y(1) < -0.2)
+ JOYSTICK_Move_Y(1,0.0);
+ else
+ JOYSTICK_Move_Y(1,-0.5);
+ break;
+ case SDL_HAT_LEFTDOWN:
+ if(JOYSTICK_GetMove_Y(1) > 0.2)
+ JOYSTICK_Move_Y(1,0.0);
+ else
+ JOYSTICK_Move_Y(1,0.5);
+ break;
+ }
+ }
+};
+
+class CCHBindGroup : public CStickBindGroup {
+public:
+ CCHBindGroup(Bitu _stick,Bitu _emustick) : CStickBindGroup (_stick,_emustick){
+ emulated_axes=4;
+ emulated_buttons=6;
+ emulated_hats=1;
+ if (button_wrapping_enabled) button_wrap=emulated_buttons;
+
+ axes_cap=emulated_axes;
+ if (axes_cap>axes) axes_cap=axes;
+ hats_cap=emulated_hats;
+ if (hats_cap>hats) hats_cap=hats;
+
+ JOYSTICK_Enable(1,true);
+ button_state=0;
+ }
+
+ bool CheckEvent(SDL_Event * event) {
+ SDL_JoyAxisEvent * jaxis = NULL;
+ SDL_JoyButtonEvent * jbutton = NULL;
+ SDL_JoyHatEvent * jhat = NULL;
+ Bitu but = 0;
+ static unsigned const button_magic[6]={0x02,0x04,0x10,0x100,0x20,0x200};
+ static unsigned const hat_magic[2][5]={{0x8888,0x8000,0x800,0x80,0x08},
+ {0x5440,0x4000,0x400,0x40,0x1000}};
+ switch(event->type) {
+ case SDL_JOYAXISMOTION:
+ jaxis = &event->jaxis;
+ if(jaxis->which == stick && jaxis->axis < 4) {
+ if(jaxis->axis & 1)
+ JOYSTICK_Move_Y(jaxis->axis>>1 & 1,(float)(jaxis->value/32768.0));
+ else
+ JOYSTICK_Move_X(jaxis->axis>>1 & 1,(float)(jaxis->value/32768.0));
+ }
+ break;
+ case SDL_JOYHATMOTION:
+ jhat = &event->jhat;
+ if(jhat->which == stick && jhat->hat < 2) {
+ if(jhat->value == SDL_HAT_CENTERED)
+ button_state&=~hat_magic[jhat->hat][0];
+ if(jhat->value & SDL_HAT_UP)
+ button_state|=hat_magic[jhat->hat][1];
+ if(jhat->value & SDL_HAT_RIGHT)
+ button_state|=hat_magic[jhat->hat][2];
+ if(jhat->value & SDL_HAT_DOWN)
+ button_state|=hat_magic[jhat->hat][3];
+ if(jhat->value & SDL_HAT_LEFT)
+ button_state|=hat_magic[jhat->hat][4];
+ }
+ break;
+ case SDL_JOYBUTTONDOWN:
+ jbutton = &event->jbutton;
+ but = jbutton->button % emulated_buttons;
+ if (jbutton->which == stick)
+ button_state|=button_magic[but];
+ break;
+ case SDL_JOYBUTTONUP:
+ jbutton = &event->jbutton;
+ but = jbutton->button % emulated_buttons;
+ if (jbutton->which == stick)
+ button_state&=~button_magic[but];
+ break;
+ }
+
+ unsigned i;
+ Bit16u j;
+ j=button_state;
+ for(i=0;i<16;i++) if (j & 1) break; else j>>=1;
+ JOYSTICK_Button(0,0,i&1);
+ JOYSTICK_Button(0,1,(i>>1)&1);
+ JOYSTICK_Button(1,0,(i>>2)&1);
+ JOYSTICK_Button(1,1,(i>>3)&1);
+ return false;
+ }
+
+ void UpdateJoystick() {
+ static unsigned const button_priority[6]={7,11,13,14,5,6};
+ static unsigned const hat_priority[2][4]={{0,1,2,3},{8,9,10,12}};
+
+ /* query SDL joystick and activate bindings */
+ ActivateJoystickBoundEvents();
+
+ JOYSTICK_Move_X(0,((float)virtual_joysticks[0].axis_pos[0])/32768.0f);
+ JOYSTICK_Move_Y(0,((float)virtual_joysticks[0].axis_pos[1])/32768.0f);
+ JOYSTICK_Move_X(1,((float)virtual_joysticks[0].axis_pos[2])/32768.0f);
+ JOYSTICK_Move_Y(1,((float)virtual_joysticks[0].axis_pos[3])/32768.0f);
+
+ Bitu bt_state=15;
+
+ Bitu i;
+ for (i=0; i<(hats<2?hats:2); i++) {
+ Uint8 hat_pos=0;
+ if (virtual_joysticks[0].hat_pressed[(i<<2)+0]) hat_pos|=SDL_HAT_UP;
+ else if (virtual_joysticks[0].hat_pressed[(i<<2)+2]) hat_pos|=SDL_HAT_DOWN;
+ if (virtual_joysticks[0].hat_pressed[(i<<2)+3]) hat_pos|=SDL_HAT_LEFT;
+ else if (virtual_joysticks[0].hat_pressed[(i<<2)+1]) hat_pos|=SDL_HAT_RIGHT;
+
+ if (hat_pos & SDL_HAT_UP)
+ if (bt_state>hat_priority[i][0]) bt_state=hat_priority[i][0];
+ if (hat_pos & SDL_HAT_DOWN)
+ if (bt_state>hat_priority[i][1]) bt_state=hat_priority[i][1];
+ if (hat_pos & SDL_HAT_RIGHT)
+ if (bt_state>hat_priority[i][2]) bt_state=hat_priority[i][2];
+ if (hat_pos & SDL_HAT_LEFT)
+ if (bt_state>hat_priority[i][3]) bt_state=hat_priority[i][3];
+ }
+
+ bool button_pressed[MAXBUTTON];
+ for (i=0; i<MAXBUTTON; i++) button_pressed[i]=false;
+ for (i=0; i<MAX_VJOY_BUTTONS; i++) {
+ if (virtual_joysticks[0].button_pressed[i])
+ button_pressed[i % button_wrap]=true;
+
+ }
+ for (i=0; i<6; i++) {
+ if ((button_pressed[i]) && (bt_state>button_priority[i]))
+ bt_state=button_priority[i];
+ }
+
+ if (bt_state>15) bt_state=15;
+ JOYSTICK_Button(0,0,(bt_state&8)==0);
+ JOYSTICK_Button(0,1,(bt_state&4)==0);
+ JOYSTICK_Button(1,0,(bt_state&2)==0);
+ JOYSTICK_Button(1,1,(bt_state&1)==0);
+ }
+
+protected:
+ Bit16u button_state;
+};
+
+static struct CMapper {
+ SDL_Surface * surface;
+ SDL_Surface * draw_surface;
+ bool exit;
+ CEvent * aevent; //Active Event
+ CBind * abind; //Active Bind
+ CBindList_it abindit; //Location of active bind in list
+ bool redraw;
+ bool addbind;
+ Bitu mods;
+ struct {
+ Bitu num_groups,num;
+ CStickBindGroup * stick[MAXSTICKS];
+ } sticks;
+ std::string filename;
+} mapper;
+
+void CBindGroup::ActivateBindList(CBindList * list,Bits value,bool ev_trigger) {
+ Bitu validmod=0;
+ CBindList_it it;
+ for (it=list->begin();it!=list->end();it++) {
+ if (((*it)->mods & mapper.mods) == (*it)->mods) {
+ if (validmod<(*it)->mods) validmod=(*it)->mods;
+ }
+ }
+ for (it=list->begin();it!=list->end();it++) {
+ if (validmod==(*it)->mods) (*it)->ActivateBind(value,ev_trigger);
+ }
+}
+
+void CBindGroup::DeactivateBindList(CBindList * list,bool ev_trigger) {
+ CBindList_it it;
+ for (it=list->begin();it!=list->end();it++) {
+ (*it)->DeActivateBind(ev_trigger);
+ }
+}
+
+static void DrawText(Bitu x,Bitu y,const char * text,Bit8u color) {
+ Bit8u * draw=((Bit8u *)mapper.surface->pixels)+(y*mapper.surface->pitch)+x;
+ while (*text) {
+ Bit8u * font=&int10_font_14[(*text)*14];
+ Bitu i,j;Bit8u * draw_line=draw;
+ for (i=0;i<14;i++) {
+ Bit8u map=*font++;
+ for (j=0;j<8;j++) {
+ if (map & 0x80) *(draw_line+j)=color;
+ else *(draw_line+j)=CLR_BLACK;
+ map<<=1;
+ }
+ draw_line+=mapper.surface->pitch;
+ }
+ text++;draw+=8;
+ }
+}
+
+class CButton {
+public:
+ virtual ~CButton(){};
+ CButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy) {
+ x=_x;y=_y;dx=_dx;dy=_dy;
+ buttons.push_back(this);
+ color=CLR_WHITE;
+ enabled=true;
+ }
+ virtual void Draw(void) {
+ if (!enabled) return;
+ Bit8u * point=((Bit8u *)mapper.surface->pixels)+(y*mapper.surface->pitch)+x;
+ for (Bitu lines=0;lines<dy;lines++) {
+ if (lines==0 || lines==(dy-1)) {
+ for (Bitu cols=0;cols<dx;cols++) *(point+cols)=color;
+ } else {
+ *point=color;*(point+dx-1)=color;
+ }
+ point+=mapper.surface->pitch;
+ }
+ }
+ virtual bool OnTop(Bitu _x,Bitu _y) {
+ return ( enabled && (_x>=x) && (_x<x+dx) && (_y>=y) && (_y<y+dy));
+ }
+ virtual void BindColor(void) {}
+ virtual void Click(void) {}
+ void Enable(bool yes) {
+ enabled=yes;
+ mapper.redraw=true;
+ }
+ void SetColor(Bit8u _col) { color=_col; }
+protected:
+ Bitu x,y,dx,dy;
+ Bit8u color;
+ bool enabled;
+};
+
+class CTextButton : public CButton {
+public:
+ CTextButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text) : CButton(_x,_y,_dx,_dy) { text=_text;}
+ void Draw(void) {
+ if (!enabled) return;
+ CButton::Draw();
+ DrawText(x+2,y+2,text,color);
+ }
+protected:
+ const char * text;
+};
+
+class CEventButton;
+static CEventButton * last_clicked = NULL;
+
+class CEventButton : public CTextButton {
+public:
+ CEventButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,CEvent * _event)
+ : CTextButton(_x,_y,_dx,_dy,_text) {
+ event=_event;
+ }
+ void BindColor(void) {
+ this->SetColor(event->bindlist.begin()==event->bindlist.end() ? CLR_GREY : CLR_WHITE);
+ }
+ void Click(void) {
+ if (last_clicked) last_clicked->BindColor();
+ this->SetColor(CLR_GREEN);
+ SetActiveEvent(event);
+ last_clicked=this;
+ }
+protected:
+ CEvent * event;
+};
+
+class CCaptionButton : public CButton {
+public:
+ CCaptionButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy) : CButton(_x,_y,_dx,_dy){
+ caption[0]=0;
+ }
+ void Change(const char * format,...) GCC_ATTRIBUTE(__format__(__printf__,2,3));
+
+ void Draw(void) {
+ if (!enabled) return;
+ DrawText(x+2,y+2,caption,color);
+ }
+protected:
+ char caption[128];
+};
+
+void CCaptionButton::Change(const char * format,...) {
+ va_list msg;
+ va_start(msg,format);
+ vsprintf(caption,format,msg);
+ va_end(msg);
+ mapper.redraw=true;
+}
+
+static void change_action_text(const char* text,Bit8u col);
+
+static void MAPPER_SaveBinds(void);
+class CBindButton : public CTextButton {
+public:
+ CBindButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,BB_Types _type)
+ : CTextButton(_x,_y,_dx,_dy,_text) {
+ type=_type;
+ }
+ void Click(void) {
+ switch (type) {
+ case BB_Add:
+ mapper.addbind=true;
+ SetActiveBind(0);
+ change_action_text("Press a key/joystick button or move the joystick.",CLR_RED);
+ break;
+ case BB_Del:
+ if (mapper.abindit!=mapper.aevent->bindlist.end()) {
+ delete (*mapper.abindit);
+ mapper.abindit=mapper.aevent->bindlist.erase(mapper.abindit);
+ if (mapper.abindit==mapper.aevent->bindlist.end())
+ mapper.abindit=mapper.aevent->bindlist.begin();
+ }
+ if (mapper.abindit!=mapper.aevent->bindlist.end()) SetActiveBind(*(mapper.abindit));
+ else SetActiveBind(0);
+ break;
+ case BB_Next:
+ if (mapper.abindit!=mapper.aevent->bindlist.end())
+ mapper.abindit++;
+ if (mapper.abindit==mapper.aevent->bindlist.end())
+ mapper.abindit=mapper.aevent->bindlist.begin();
+ SetActiveBind(*(mapper.abindit));
+ break;
+ case BB_Save:
+ MAPPER_SaveBinds();
+ break;
+ case BB_Exit:
+ mapper.exit=true;
+ break;
+ }
+ }
+protected:
+ BB_Types type;
+};
+
+class CCheckButton : public CTextButton {
+public:
+ CCheckButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,BC_Types _type)
+ : CTextButton(_x,_y,_dx,_dy,_text) {
+ type=_type;
+ }
+ void Draw(void) {
+ if (!enabled) return;
+ bool checked=false;
+ switch (type) {
+ case BC_Mod1:
+ checked=(mapper.abind->mods&BMOD_Mod1)>0;
+ break;
+ case BC_Mod2:
+ checked=(mapper.abind->mods&BMOD_Mod2)>0;
+ break;
+ case BC_Mod3:
+ checked=(mapper.abind->mods&BMOD_Mod3)>0;
+ break;
+ case BC_Hold:
+ checked=(mapper.abind->flags&BFLG_Hold)>0;
+ break;
+ }
+ if (checked) {
+ Bit8u * point=((Bit8u *)mapper.surface->pixels)+((y+2)*mapper.surface->pitch)+x+dx-dy+2;
+ for (Bitu lines=0;lines<(dy-4);lines++) {
+ memset(point,color,dy-4);
+ point+=mapper.surface->pitch;
+ }
+ }
+ CTextButton::Draw();
+ }
+ void Click(void) {
+ switch (type) {
+ case BC_Mod1:
+ mapper.abind->mods^=BMOD_Mod1;
+ break;
+ case BC_Mod2:
+ mapper.abind->mods^=BMOD_Mod2;
+ break;
+ case BC_Mod3:
+ mapper.abind->mods^=BMOD_Mod3;
+ break;
+ case BC_Hold:
+ mapper.abind->flags^=BFLG_Hold;
+ break;
+ }
+ mapper.redraw=true;
+ }
+protected:
+ BC_Types type;
+};
+
+class CKeyEvent : public CTriggeredEvent {
+public:
+ CKeyEvent(char const * const _entry,KBD_KEYS _key) : CTriggeredEvent(_entry) {
+ key=_key;
+ }
+ void Active(bool yesno) {
+ KEYBOARD_AddKey(key,yesno);
+ };
+ KBD_KEYS key;
+};
+
+class CJAxisEvent : public CContinuousEvent {
+public:
+ CJAxisEvent(char const * const _entry,Bitu _stick,Bitu _axis,bool _positive,CJAxisEvent * _opposite_axis) : CContinuousEvent(_entry) {
+ stick=_stick;
+ axis=_axis;
+ positive=_positive;
+ opposite_axis=_opposite_axis;
+ if (_opposite_axis) {
+ _opposite_axis->SetOppositeAxis(this);
+ }
+ }
+ void Active(bool /*moved*/) {
+ virtual_joysticks[stick].axis_pos[axis]=(Bit16s)(GetValue()*(positive?1:-1));
+ }
+ virtual Bitu GetActivityCount(void) {
+ return activity|opposite_axis->activity;
+ }
+ virtual void RepostActivity(void) {
+ /* caring for joystick movement into the opposite direction */
+ opposite_axis->Active(true);
+ }
+protected:
+ void SetOppositeAxis(CJAxisEvent * _opposite_axis) {
+ opposite_axis=_opposite_axis;
+ }
+ Bitu stick,axis;
+ bool positive;
+ CJAxisEvent * opposite_axis;
+};
+
+class CJButtonEvent : public CTriggeredEvent {
+public:
+ CJButtonEvent(char const * const _entry,Bitu _stick,Bitu _button) : CTriggeredEvent(_entry) {
+ stick=_stick;
+ button=_button;
+ }
+ void Active(bool pressed) {
+ virtual_joysticks[stick].button_pressed[button]=pressed;
+ }
+protected:
+ Bitu stick,button;
+};
+
+class CJHatEvent : public CTriggeredEvent {
+public:
+ CJHatEvent(char const * const _entry,Bitu _stick,Bitu _hat,Bitu _dir) : CTriggeredEvent(_entry) {
+ stick=_stick;
+ hat=_hat;
+ dir=_dir;
+ }
+ void Active(bool pressed) {
+ virtual_joysticks[stick].hat_pressed[(hat<<2)+dir]=pressed;
+ }
+protected:
+ Bitu stick,hat,dir;
+};
+
+
+class CModEvent : public CTriggeredEvent {
+public:
+ CModEvent(char const * const _entry,Bitu _wmod) : CTriggeredEvent(_entry) {
+ wmod=_wmod;
+ }
+ void Active(bool yesno) {
+ if (yesno) mapper.mods|=(1 << (wmod-1));
+ else mapper.mods&=~(1 << (wmod-1));
+ };
+protected:
+ Bitu wmod;
+};
+
+class CHandlerEvent : public CTriggeredEvent {
+public:
+ CHandlerEvent(char const * const _entry,MAPPER_Handler * _handler,MapKeys _key,Bitu _mod,char const * const _buttonname) : CTriggeredEvent(_entry) {
+ handler=_handler;
+ defmod=_mod;
+ defkey=_key;
+ buttonname=_buttonname;
+ handlergroup.push_back(this);
+ }
+ void Active(bool yesno) {
+ (*handler)(yesno);
+ };
+ const char * ButtonName(void) {
+ return buttonname;
+ }
+ void MakeDefaultBind(char * buf) {
+ Bitu key=0;
+ switch (defkey) {
+ case MK_f1:case MK_f2:case MK_f3:case MK_f4:
+ case MK_f5:case MK_f6:case MK_f7:case MK_f8:
+ case MK_f9:case MK_f10:case MK_f11:case MK_f12:
+ key=SDLK_F1+(defkey-MK_f1);
+ break;
+ case MK_return:
+ key=SDLK_RETURN;
+ break;
+ case MK_kpminus:
+ key=SDLK_KP_MINUS;
+ break;
+ case MK_scrolllock:
+ key=SDLK_SCROLLOCK;
+ break;
+ case MK_pause:
+ key=SDLK_PAUSE;
+ break;
+ case MK_printscreen:
+ key=SDLK_PRINT;
+ break;
+ case MK_home:
+ key=SDLK_HOME;
+ break;
+ }
+ sprintf(buf,"%s \"key %d%s%s%s\"",
+ entry,
+ key,
+ defmod & 1 ? " mod1" : "",
+ defmod & 2 ? " mod2" : "",
+ defmod & 4 ? " mod3" : ""
+ );
+ }
+protected:
+ MapKeys defkey;
+ Bitu defmod;
+ MAPPER_Handler * handler;
+public:
+ const char * buttonname;
+};
+
+
+static struct {
+ CCaptionButton * event_title;
+ CCaptionButton * bind_title;
+ CCaptionButton * selected;
+ CCaptionButton * action;
+ CBindButton * save;
+ CBindButton * exit;
+ CBindButton * add;
+ CBindButton * del;
+ CBindButton * next;
+ CCheckButton * mod1,* mod2,* mod3,* hold;
+} bind_but;
+
+
+static void change_action_text(const char* text,Bit8u col) {
+ bind_but.action->Change(text,"");
+ bind_but.action->SetColor(col);
+}
+
+
+static void SetActiveBind(CBind * _bind) {
+ mapper.abind=_bind;
+ if (_bind) {
+ bind_but.bind_title->Enable(true);
+ char buf[256];_bind->BindName(buf);
+ bind_but.bind_title->Change("BIND:%s",buf);
+ bind_but.del->Enable(true);
+ bind_but.next->Enable(true);
+ bind_but.mod1->Enable(true);
+ bind_but.mod2->Enable(true);
+ bind_but.mod3->Enable(true);
+ bind_but.hold->Enable(true);
+ } else {
+ bind_but.bind_title->Enable(false);
+ bind_but.del->Enable(false);
+ bind_but.next->Enable(false);
+ bind_but.mod1->Enable(false);
+ bind_but.mod2->Enable(false);
+ bind_but.mod3->Enable(false);
+ bind_but.hold->Enable(false);
+ }
+}
+
+static void SetActiveEvent(CEvent * event) {
+ mapper.aevent=event;
+ mapper.redraw=true;
+ mapper.addbind=false;
+ bind_but.event_title->Change("EVENT:%s",event ? event->GetName(): "none");
+ if (!event) {
+ change_action_text("Select an event to change.",CLR_WHITE);
+ bind_but.add->Enable(false);
+ SetActiveBind(0);
+ } else {
+ change_action_text("Select a different event or hit the Add/Del/Next buttons.",CLR_WHITE);
+ mapper.abindit=event->bindlist.begin();
+ if (mapper.abindit!=event->bindlist.end()) {
+ SetActiveBind(*(mapper.abindit));
+ } else SetActiveBind(0);
+ bind_but.add->Enable(true);
+ }
+}
+
+static void DrawButtons(void) {
+ SDL_FillRect(mapper.surface,0,CLR_BLACK);
+ SDL_LockSurface(mapper.surface);
+ for (CButton_it but_it = buttons.begin();but_it!=buttons.end();but_it++) {
+ (*but_it)->Draw();
+ }
+ SDL_UnlockSurface(mapper.surface);
+ SDL_Flip(mapper.surface);
+}
+
+static CKeyEvent * AddKeyButtonEvent(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,char const * const entry,KBD_KEYS key) {
+ char buf[64];
+ strcpy(buf,"key_");
+ strcat(buf,entry);
+ CKeyEvent * event=new CKeyEvent(buf,key);
+ new CEventButton(x,y,dx,dy,title,event);
+ return event;
+}
+
+static CJAxisEvent * AddJAxisButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu stick,Bitu axis,bool positive,CJAxisEvent * opposite_axis) {
+ char buf[64];
+ sprintf(buf,"jaxis_%d_%d%s",stick,axis,positive ? "+" : "-");
+ CJAxisEvent * event=new CJAxisEvent(buf,stick,axis,positive,opposite_axis);
+ new CEventButton(x,y,dx,dy,title,event);
+ return event;
+}
+static CJAxisEvent * AddJAxisButton_hidden(Bitu stick,Bitu axis,bool positive,CJAxisEvent * opposite_axis) {
+ char buf[64];
+ sprintf(buf,"jaxis_%d_%d%s",stick,axis,positive ? "+" : "-");
+ return new CJAxisEvent(buf,stick,axis,positive,opposite_axis);
+}
+
+static void AddJButtonButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu stick,Bitu button) {
+ char buf[64];
+ sprintf(buf,"jbutton_%d_%d",stick,button);
+ CJButtonEvent * event=new CJButtonEvent(buf,stick,button);
+ new CEventButton(x,y,dx,dy,title,event);
+}
+static void AddJButtonButton_hidden(Bitu stick,Bitu button) {
+ char buf[64];
+ sprintf(buf,"jbutton_%d_%d",stick,button);
+ new CJButtonEvent(buf,stick,button);
+}
+
+static void AddJHatButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu _stick,Bitu _hat,Bitu _dir) {
+ char buf[64];
+ sprintf(buf,"jhat_%d_%d_%d",_stick,_hat,_dir);
+ CJHatEvent * event=new CJHatEvent(buf,_stick,_hat,_dir);
+ new CEventButton(x,y,dx,dy,title,event);
+}
+
+static void AddModButton(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,Bitu _mod) {
+ char buf[64];
+ sprintf(buf,"mod_%d",_mod);
+ CModEvent * event=new CModEvent(buf,_mod);
+ new CEventButton(x,y,dx,dy,title,event);
+}
+
+struct KeyBlock {
+ const char * title;
+ const char * entry;
+ KBD_KEYS key;
+};
+static KeyBlock combo_f[12]={
+ {"F1","f1",KBD_f1}, {"F2","f2",KBD_f2}, {"F3","f3",KBD_f3},
+ {"F4","f4",KBD_f4}, {"F5","f5",KBD_f5}, {"F6","f6",KBD_f6},
+ {"F7","f7",KBD_f7}, {"F8","f8",KBD_f8}, {"F9","f9",KBD_f9},
+ {"F10","f10",KBD_f10}, {"F11","f11",KBD_f11}, {"F12","f12",KBD_f12},
+};
+
+static KeyBlock combo_1[14]={
+ {"`~","grave",KBD_grave}, {"1!","1",KBD_1}, {"2@","2",KBD_2},
+ {"3#","3",KBD_3}, {"4$","4",KBD_4}, {"5%","5",KBD_5},
+ {"6^","6",KBD_6}, {"7&","7",KBD_7}, {"8*","8",KBD_8},
+ {"9(","9",KBD_9}, {"0)","0",KBD_0}, {"-_","minus",KBD_minus},
+ {"=+","equals",KBD_equals}, {"\x1B","bspace",KBD_backspace},
+};
+
+static KeyBlock combo_2[12]={
+ {"Q","q",KBD_q}, {"W","w",KBD_w}, {"E","e",KBD_e},
+ {"R","r",KBD_r}, {"T","t",KBD_t}, {"Y","y",KBD_y},
+ {"U","u",KBD_u}, {"I","i",KBD_i}, {"O","o",KBD_o},
+ {"P","p",KBD_p}, {"[{","lbracket",KBD_leftbracket},
+ {"]}","rbracket",KBD_rightbracket},
+};
+
+static KeyBlock combo_3[12]={
+ {"A","a",KBD_a}, {"S","s",KBD_s}, {"D","d",KBD_d},
+ {"F","f",KBD_f}, {"G","g",KBD_g}, {"H","h",KBD_h},
+ {"J","j",KBD_j}, {"K","k",KBD_k}, {"L","l",KBD_l},
+ {";:","semicolon",KBD_semicolon}, {"'\"","quote",KBD_quote},
+ {"\\|","backslash",KBD_backslash},
+};
+
+static KeyBlock combo_4[11]={
+ {"<>","lessthan",KBD_extra_lt_gt},
+ {"Z","z",KBD_z}, {"X","x",KBD_x}, {"C","c",KBD_c},
+ {"V","v",KBD_v}, {"B","b",KBD_b}, {"N","n",KBD_n},
+ {"M","m",KBD_m}, {",<","comma",KBD_comma},
+ {".>","period",KBD_period}, {"/?","slash",KBD_slash},
+};
+
+static CKeyEvent * caps_lock_event=NULL;
+static CKeyEvent * num_lock_event=NULL;
+
+static void CreateLayout(void) {
+ Bitu i;
+ /* Create the buttons for the Keyboard */
+#define BW 28
+#define BH 20
+#define DX 5
+#define PX(_X_) ((_X_)*BW + DX)
+#define PY(_Y_) (10+(_Y_)*BH)
+ AddKeyButtonEvent(PX(0),PY(0),BW,BH,"ESC","esc",KBD_esc);
+ for (i=0;i<12;i++) AddKeyButtonEvent(PX(2+i),PY(0),BW,BH,combo_f[i].title,combo_f[i].entry,combo_f[i].key);
+ for (i=0;i<14;i++) AddKeyButtonEvent(PX( i),PY(1),BW,BH,combo_1[i].title,combo_1[i].entry,combo_1[i].key);
+
+ AddKeyButtonEvent(PX(0),PY(2),BW*2,BH,"TAB","tab",KBD_tab);
+ for (i=0;i<12;i++) AddKeyButtonEvent(PX(2+i),PY(2),BW,BH,combo_2[i].title,combo_2[i].entry,combo_2[i].key);
+
+ AddKeyButtonEvent(PX(14),PY(2),BW*2,BH*2,"ENTER","enter",KBD_enter);
+
+ caps_lock_event=AddKeyButtonEvent(PX(0),PY(3),BW*2,BH,"CLCK","capslock",KBD_capslock);
+ for (i=0;i<12;i++) AddKeyButtonEvent(PX(2+i),PY(3),BW,BH,combo_3[i].title,combo_3[i].entry,combo_3[i].key);
+
+ AddKeyButtonEvent(PX(0),PY(4),BW*2,BH,"SHIFT","lshift",KBD_leftshift);
+ for (i=0;i<11;i++) AddKeyButtonEvent(PX(2+i),PY(4),BW,BH,combo_4[i].title,combo_4[i].entry,combo_4[i].key);
+ AddKeyButtonEvent(PX(13),PY(4),BW*3,BH,"SHIFT","rshift",KBD_rightshift);
+
+ /* Last Row */
+ AddKeyButtonEvent(PX(0) ,PY(5),BW*2,BH,"CTRL","lctrl",KBD_leftctrl);
+ AddKeyButtonEvent(PX(3) ,PY(5),BW*2,BH,"ALT","lalt",KBD_leftalt);
+ AddKeyButtonEvent(PX(5) ,PY(5),BW*6,BH,"SPACE","space",KBD_space);
+ AddKeyButtonEvent(PX(11),PY(5),BW*2,BH,"ALT","ralt",KBD_rightalt);
+ AddKeyButtonEvent(PX(14),PY(5),BW*2,BH,"CTRL","rctrl",KBD_rightctrl);
+
+ /* Arrow Keys */
+#define XO 17
+#define YO 0
+
+ AddKeyButtonEvent(PX(XO+0),PY(YO),BW,BH,"PRT","printscreen",KBD_printscreen);
+ AddKeyButtonEvent(PX(XO+1),PY(YO),BW,BH,"SCL","scrolllock",KBD_scrolllock);
+ AddKeyButtonEvent(PX(XO+2),PY(YO),BW,BH,"PAU","pause",KBD_pause);
+ AddKeyButtonEvent(PX(XO+0),PY(YO+1),BW,BH,"INS","insert",KBD_insert);
+ AddKeyButtonEvent(PX(XO+1),PY(YO+1),BW,BH,"HOM","home",KBD_home);
+ AddKeyButtonEvent(PX(XO+2),PY(YO+1),BW,BH,"PUP","pageup",KBD_pageup);
+ AddKeyButtonEvent(PX(XO+0),PY(YO+2),BW,BH,"DEL","delete",KBD_delete);
+ AddKeyButtonEvent(PX(XO+1),PY(YO+2),BW,BH,"END","end",KBD_end);
+ AddKeyButtonEvent(PX(XO+2),PY(YO+2),BW,BH,"PDN","pagedown",KBD_pagedown);
+ AddKeyButtonEvent(PX(XO+1),PY(YO+4),BW,BH,"\x18","up",KBD_up);
+ AddKeyButtonEvent(PX(XO+0),PY(YO+5),BW,BH,"\x1B","left",KBD_left);
+ AddKeyButtonEvent(PX(XO+1),PY(YO+5),BW,BH,"\x19","down",KBD_down);
+ AddKeyButtonEvent(PX(XO+2),PY(YO+5),BW,BH,"\x1A","right",KBD_right);
+#undef XO
+#undef YO
+#define XO 0
+#define YO 7
+ /* Numeric KeyPad */
+ num_lock_event=AddKeyButtonEvent(PX(XO),PY(YO),BW,BH,"NUM","numlock",KBD_numlock);
+ AddKeyButtonEvent(PX(XO+1),PY(YO),BW,BH,"/","kp_divide",KBD_kpdivide);
+ AddKeyButtonEvent(PX(XO+2),PY(YO),BW,BH,"*","kp_multiply",KBD_kpmultiply);
+ AddKeyButtonEvent(PX(XO+3),PY(YO),BW,BH,"-","kp_minus",KBD_kpminus);
+ AddKeyButtonEvent(PX(XO+0),PY(YO+1),BW,BH,"7","kp_7",KBD_kp7);
+ AddKeyButtonEvent(PX(XO+1),PY(YO+1),BW,BH,"8","kp_8",KBD_kp8);
+ AddKeyButtonEvent(PX(XO+2),PY(YO+1),BW,BH,"9","kp_9",KBD_kp9);
+ AddKeyButtonEvent(PX(XO+3),PY(YO+1),BW,BH*2,"+","kp_plus",KBD_kpplus);
+ AddKeyButtonEvent(PX(XO),PY(YO+2),BW,BH,"4","kp_4",KBD_kp4);
+ AddKeyButtonEvent(PX(XO+1),PY(YO+2),BW,BH,"5","kp_5",KBD_kp5);
+ AddKeyButtonEvent(PX(XO+2),PY(YO+2),BW,BH,"6","kp_6",KBD_kp6);
+ AddKeyButtonEvent(PX(XO+0),PY(YO+3),BW,BH,"1","kp_1",KBD_kp1);
+ AddKeyButtonEvent(PX(XO+1),PY(YO+3),BW,BH,"2","kp_2",KBD_kp2);
+ AddKeyButtonEvent(PX(XO+2),PY(YO+3),BW,BH,"3","kp_3",KBD_kp3);
+ AddKeyButtonEvent(PX(XO+3),PY(YO+3),BW,BH*2,"ENT","kp_enter",KBD_kpenter);
+ AddKeyButtonEvent(PX(XO),PY(YO+4),BW*2,BH,"0","kp_0",KBD_kp0);
+ AddKeyButtonEvent(PX(XO+2),PY(YO+4),BW,BH,".","kp_period",KBD_kpperiod);
+#undef XO
+#undef YO
+#define XO 10
+#define YO 8
+ /* Joystick Buttons/Texts */
+ /* Buttons 1+2 of 1st Joystick */
+ AddJButtonButton(PX(XO),PY(YO),BW,BH,"1" ,0,0);
+ AddJButtonButton(PX(XO+2),PY(YO),BW,BH,"2" ,0,1);
+ /* Axes 1+2 (X+Y) of 1st Joystick */
+ CJAxisEvent * cjaxis=AddJAxisButton(PX(XO+1),PY(YO),BW,BH,"Y-",0,1,false,NULL);
+ AddJAxisButton (PX(XO+1),PY(YO+1),BW,BH,"Y+",0,1,true,cjaxis);
+ cjaxis=AddJAxisButton (PX(XO),PY(YO+1),BW,BH,"X-",0,0,false,NULL);
+ AddJAxisButton (PX(XO+2),PY(YO+1),BW,BH,"X+",0,0,true,cjaxis);
+
+ if (joytype==JOY_2AXIS) {
+ /* Buttons 1+2 of 2nd Joystick */
+ AddJButtonButton(PX(XO+4),PY(YO),BW,BH,"1" ,1,0);
+ AddJButtonButton(PX(XO+4+2),PY(YO),BW,BH,"2" ,1,1);
+ /* Buttons 3+4 of 1st Joystick, not accessible */
+ AddJButtonButton_hidden(0,2);
+ AddJButtonButton_hidden(0,3);
+
+ /* Axes 1+2 (X+Y) of 2nd Joystick */
+ cjaxis= AddJAxisButton(PX(XO+4),PY(YO+1),BW,BH,"X-",1,0,false,NULL);
+ AddJAxisButton(PX(XO+4+2),PY(YO+1),BW,BH,"X+",1,0,true,cjaxis);
+ cjaxis= AddJAxisButton(PX(XO+4+1),PY(YO+0),BW,BH,"Y-",1,1,false,NULL);
+ AddJAxisButton(PX(XO+4+1),PY(YO+1),BW,BH,"Y+",1,1,true,cjaxis);
+ /* Axes 3+4 (X+Y) of 1st Joystick, not accessible */
+ cjaxis= AddJAxisButton_hidden(0,2,false,NULL);
+ AddJAxisButton_hidden(0,2,true,cjaxis);
+ cjaxis= AddJAxisButton_hidden(0,3,false,NULL);
+ AddJAxisButton_hidden(0,3,true,cjaxis);
+ } else {
+ /* Buttons 3+4 of 1st Joystick */
+ AddJButtonButton(PX(XO+4),PY(YO),BW,BH,"3" ,0,2);
+ AddJButtonButton(PX(XO+4+2),PY(YO),BW,BH,"4" ,0,3);
+ /* Buttons 1+2 of 2nd Joystick, not accessible */
+ AddJButtonButton_hidden(1,0);
+ AddJButtonButton_hidden(1,1);
+
+ /* Axes 3+4 (X+Y) of 1st Joystick */
+ cjaxis= AddJAxisButton(PX(XO+4),PY(YO+1),BW,BH,"X-",0,2,false,NULL);
+ AddJAxisButton(PX(XO+4+2),PY(YO+1),BW,BH,"X+",0,2,true,cjaxis);
+ cjaxis= AddJAxisButton(PX(XO+4+1),PY(YO+0),BW,BH,"Y-",0,3,false,NULL);
+ AddJAxisButton(PX(XO+4+1),PY(YO+1),BW,BH,"Y+",0,3,true,cjaxis);
+ /* Axes 1+2 (X+Y) of 2nd Joystick , not accessible*/
+ cjaxis= AddJAxisButton_hidden(1,0,false,NULL);
+ AddJAxisButton_hidden(1,0,true,cjaxis);
+ cjaxis= AddJAxisButton_hidden(1,1,false,NULL);
+ AddJAxisButton_hidden(1,1,true,cjaxis);
+ }
+
+ if (joytype==JOY_CH) {
+ /* Buttons 5+6 of 1st Joystick */
+ AddJButtonButton(PX(XO+8),PY(YO),BW,BH,"5" ,0,4);
+ AddJButtonButton(PX(XO+8+2),PY(YO),BW,BH,"6" ,0,5);
+ } else {
+ /* Buttons 5+6 of 1st Joystick, not accessible */
+ AddJButtonButton_hidden(0,4);
+ AddJButtonButton_hidden(0,5);
+ }
+
+ /* Hat directions up, left, down, right */
+ AddJHatButton(PX(XO+8+1),PY(YO),BW,BH,"UP",0,0,0);
+ AddJHatButton(PX(XO+8+0),PY(YO+1),BW,BH,"LFT",0,0,3);
+ AddJHatButton(PX(XO+8+1),PY(YO+1),BW,BH,"DWN",0,0,2);
+ AddJHatButton(PX(XO+8+2),PY(YO+1),BW,BH,"RGT",0,0,1);
+
+ /* Labels for the joystick */
+ CTextButton * btn;
+ if (joytype ==JOY_2AXIS) {
+ new CTextButton(PX(XO+0),PY(YO-1),3*BW,20,"Joystick 1");
+ new CTextButton(PX(XO+4),PY(YO-1),3*BW,20,"Joystick 2");
+ btn=new CTextButton(PX(XO+8),PY(YO-1),3*BW,20,"Disabled");
+ btn->SetColor(CLR_GREY);
+ } else if(joytype ==JOY_4AXIS || joytype == JOY_4AXIS_2) {
+ new CTextButton(PX(XO+0),PY(YO-1),3*BW,20,"Axis 1/2");
+ new CTextButton(PX(XO+4),PY(YO-1),3*BW,20,"Axis 3/4");
+ btn=new CTextButton(PX(XO+8),PY(YO-1),3*BW,20,"Disabled");
+ btn->SetColor(CLR_GREY);
+ } else if(joytype == JOY_CH) {
+ new CTextButton(PX(XO+0),PY(YO-1),3*BW,20,"Axis 1/2");
+ new CTextButton(PX(XO+4),PY(YO-1),3*BW,20,"Axis 3/4");
+ new CTextButton(PX(XO+8),PY(YO-1),3*BW,20,"Hat/D-pad");
+ } else if ( joytype==JOY_FCS) {
+ new CTextButton(PX(XO+0),PY(YO-1),3*BW,20,"Axis 1/2");
+ new CTextButton(PX(XO+4),PY(YO-1),3*BW,20,"Axis 3");
+ new CTextButton(PX(XO+8),PY(YO-1),3*BW,20,"Hat/D-pad");
+ } else if(joytype == JOY_NONE) {
+ btn=new CTextButton(PX(XO+0),PY(YO-1),3*BW,20,"Disabled");
+ btn->SetColor(CLR_GREY);
+ btn=new CTextButton(PX(XO+4),PY(YO-1),3*BW,20,"Disabled");
+ btn->SetColor(CLR_GREY);
+ btn=new CTextButton(PX(XO+8),PY(YO-1),3*BW,20,"Disabled");
+ btn->SetColor(CLR_GREY);
+ }
+
+
+
+ /* The modifier buttons */
+ AddModButton(PX(0),PY(14),50,20,"Mod1",1);
+ AddModButton(PX(2),PY(14),50,20,"Mod2",2);
+ AddModButton(PX(4),PY(14),50,20,"Mod3",3);
+ /* Create Handler buttons */
+ Bitu xpos=3;Bitu ypos=11;
+ for (CHandlerEventVector_it hit=handlergroup.begin();hit!=handlergroup.end();hit++) {
+ new CEventButton(PX(xpos*3),PY(ypos),BW*3,BH,(*hit)->ButtonName(),(*hit));
+ xpos++;
+ if (xpos>6) {
+ xpos=3;ypos++;
+ }
+ }
+ /* Create some text buttons */
+// new CTextButton(PX(6),0,124,20,"Keyboard Layout");
+// new CTextButton(PX(17),0,124,20,"Joystick Layout");
+
+ bind_but.action=new CCaptionButton(180,350,0,0);
+
+ bind_but.event_title=new CCaptionButton(0,350,0,0);
+ bind_but.bind_title=new CCaptionButton(0,365,0,0);
+
+ /* Create binding support buttons */
+
+ bind_but.mod1=new CCheckButton(20,410,60,20, "mod1",BC_Mod1);
+ bind_but.mod2=new CCheckButton(20,432,60,20, "mod2",BC_Mod2);
+ bind_but.mod3=new CCheckButton(20,454,60,20, "mod3",BC_Mod3);
+ bind_but.hold=new CCheckButton(100,410,60,20,"hold",BC_Hold);
+
+ bind_but.next=new CBindButton(250,400,50,20,"Next",BB_Next);
+
+ bind_but.add=new CBindButton(250,380,50,20,"Add",BB_Add);
+ bind_but.del=new CBindButton(300,380,50,20,"Del",BB_Del);
+
+ bind_but.save=new CBindButton(400,450,50,20,"Save",BB_Save);
+ bind_but.exit=new CBindButton(450,450,50,20,"Exit",BB_Exit);
+
+ bind_but.bind_title->Change("Bind Title");
+}
+
+static SDL_Color map_pal[6]={
+ {0x00,0x00,0x00,0x00}, //0=black
+ {0x7f,0x7f,0x7f,0x00}, //1=grey
+ {0xff,0xff,0xff,0x00}, //2=white
+ {0xff,0x00,0x00,0x00}, //3=red
+ {0x10,0x30,0xff,0x00}, //4=blue
+ {0x00,0xff,0x20,0x00} //5=green
+};
+
+static void CreateStringBind(char * line) {
+ line=trim(line);
+ char * eventname=StripWord(line);
+ CEvent * event;
+ for (CEventVector_it ev_it=events.begin();ev_it!=events.end();ev_it++) {
+ if (!strcasecmp((*ev_it)->GetName(),eventname)) {
+ event=*ev_it;
+ goto foundevent;
+ }
+ }
+ LOG_MSG("Can't find matching event for %s",eventname);
+ return ;
+foundevent:
+ CBind * bind;
+ for (char * bindline=StripWord(line);*bindline;bindline=StripWord(line)) {
+ for (CBindGroup_it it=bindgroups.begin();it!=bindgroups.end();it++) {
+ bind=(*it)->CreateConfigBind(bindline);
+ if (bind) {
+ event->AddBind(bind);
+ bind->SetFlags(bindline);
+ break;
+ }
+ }
+ }
+}
+
+static struct {
+ const char * eventend;
+ Bitu key;
+} DefaultKeys[]={
+ {"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},
+
+ {"1",SDLK_1}, {"2",SDLK_2}, {"3",SDLK_3}, {"4",SDLK_4},
+ {"5",SDLK_5}, {"6",SDLK_6}, {"7",SDLK_7}, {"8",SDLK_8},
+ {"9",SDLK_9}, {"0",SDLK_0},
+
+ {"a",SDLK_a}, {"b",SDLK_b}, {"c",SDLK_c}, {"d",SDLK_d},
+ {"e",SDLK_e}, {"f",SDLK_f}, {"g",SDLK_g}, {"h",SDLK_h},
+ {"i",SDLK_i}, {"j",SDLK_j}, {"k",SDLK_k}, {"l",SDLK_l},
+ {"m",SDLK_m}, {"n",SDLK_n}, {"o",SDLK_o}, {"p",SDLK_p},
+ {"q",SDLK_q}, {"r",SDLK_r}, {"s",SDLK_s}, {"t",SDLK_t},
+ {"u",SDLK_u}, {"v",SDLK_v}, {"w",SDLK_w}, {"x",SDLK_x},
+ {"y",SDLK_y}, {"z",SDLK_z}, {"space",SDLK_SPACE},
+ {"esc",SDLK_ESCAPE}, {"equals",SDLK_EQUALS}, {"grave",SDLK_BACKQUOTE},
+ {"tab",SDLK_TAB}, {"enter",SDLK_RETURN}, {"bspace",SDLK_BACKSPACE},
+ {"lbracket",SDLK_LEFTBRACKET}, {"rbracket",SDLK_RIGHTBRACKET},
+ {"minus",SDLK_MINUS}, {"capslock",SDLK_CAPSLOCK}, {"semicolon",SDLK_SEMICOLON},
+ {"quote", SDLK_QUOTE}, {"backslash",SDLK_BACKSLASH}, {"lshift",SDLK_LSHIFT},
+ {"rshift",SDLK_RSHIFT}, {"lalt",SDLK_LALT}, {"ralt",SDLK_RALT},
+ {"lctrl",SDLK_LCTRL}, {"rctrl",SDLK_RCTRL}, {"comma",SDLK_COMMA},
+ {"period",SDLK_PERIOD}, {"slash",SDLK_SLASH}, {"printscreen",SDLK_PRINT},
+ {"scrolllock",SDLK_SCROLLOCK}, {"pause",SDLK_PAUSE}, {"pagedown",SDLK_PAGEDOWN},
+ {"pageup",SDLK_PAGEUP}, {"insert",SDLK_INSERT}, {"home",SDLK_HOME},
+ {"delete",SDLK_DELETE}, {"end",SDLK_END}, {"up",SDLK_UP},
+ {"left",SDLK_LEFT}, {"down",SDLK_DOWN}, {"right",SDLK_RIGHT},
+ {"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}, {"numlock",SDLK_NUMLOCK},
+ {"kp_divide",SDLK_KP_DIVIDE}, {"kp_multiply",SDLK_KP_MULTIPLY},
+ {"kp_minus",SDLK_KP_MINUS}, {"kp_plus",SDLK_KP_PLUS},
+ {"kp_period",SDLK_KP_PERIOD}, {"kp_enter",SDLK_KP_ENTER},
+
+#if defined (MACOSX)
+ /* Intl Mac keyboards in US layout actually put U+00A7 SECTION SIGN here */
+ {"lessthan",SDLK_WORLD_0},
+#else
+ {"lessthan",SDLK_LESS},
+#endif
+
+ {0,0}
+};
+
+static void CreateDefaultBinds(void) {
+ char buffer[512];
+ Bitu i=0;
+ while (DefaultKeys[i].eventend) {
+ sprintf(buffer,"key_%s \"key %d\"",DefaultKeys[i].eventend,DefaultKeys[i].key);
+ CreateStringBind(buffer);
+ i++;
+ }
+ sprintf(buffer,"mod_1 \"key %d\"",SDLK_RCTRL);CreateStringBind(buffer);
+ sprintf(buffer,"mod_1 \"key %d\"",SDLK_LCTRL);CreateStringBind(buffer);
+ sprintf(buffer,"mod_2 \"key %d\"",SDLK_RALT);CreateStringBind(buffer);
+ sprintf(buffer,"mod_2 \"key %d\"",SDLK_LALT);CreateStringBind(buffer);
+ for (CHandlerEventVector_it hit=handlergroup.begin();hit!=handlergroup.end();hit++) {
+ (*hit)->MakeDefaultBind(buffer);
+ CreateStringBind(buffer);
+ }
+
+ /* joystick1, buttons 1-6 */
+ sprintf(buffer,"jbutton_0_0 \"stick_0 button 0\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jbutton_0_1 \"stick_0 button 1\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jbutton_0_2 \"stick_0 button 2\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jbutton_0_3 \"stick_0 button 3\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jbutton_0_4 \"stick_0 button 4\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jbutton_0_5 \"stick_0 button 5\" ");CreateStringBind(buffer);
+ /* joystick2, buttons 1-2 */
+ sprintf(buffer,"jbutton_1_0 \"stick_1 button 0\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jbutton_1_1 \"stick_1 button 1\" ");CreateStringBind(buffer);
+
+ /* joystick1, axes 1-4 */
+ sprintf(buffer,"jaxis_0_0- \"stick_0 axis 0 0\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jaxis_0_0+ \"stick_0 axis 0 1\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jaxis_0_1- \"stick_0 axis 1 0\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jaxis_0_1+ \"stick_0 axis 1 1\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jaxis_0_2- \"stick_0 axis 2 0\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jaxis_0_2+ \"stick_0 axis 2 1\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jaxis_0_3- \"stick_0 axis 3 0\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jaxis_0_3+ \"stick_0 axis 3 1\" ");CreateStringBind(buffer);
+ /* joystick2, axes 1-2 */
+ sprintf(buffer,"jaxis_1_0- \"stick_1 axis 0 0\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jaxis_1_0+ \"stick_1 axis 0 1\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jaxis_1_1- \"stick_1 axis 1 0\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jaxis_1_1+ \"stick_1 axis 1 1\" ");CreateStringBind(buffer);
+
+ /* joystick1, hat */
+ sprintf(buffer,"jhat_0_0_0 \"stick_0 hat 0 1\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jhat_0_0_1 \"stick_0 hat 0 2\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jhat_0_0_2 \"stick_0 hat 0 4\" ");CreateStringBind(buffer);
+ sprintf(buffer,"jhat_0_0_3 \"stick_0 hat 0 8\" ");CreateStringBind(buffer);
+}
+
+void MAPPER_AddHandler(MAPPER_Handler * handler,MapKeys key,Bitu mods,char const * const eventname,char const * const buttonname) {
+ //Check if it already exists=> if so return.
+ for(CHandlerEventVector_it it=handlergroup.begin();it!=handlergroup.end();it++)
+ if(strcmp((*it)->buttonname,buttonname) == 0) return;
+
+ char tempname[17];
+ strcpy(tempname,"hand_");
+ strcat(tempname,eventname);
+ new CHandlerEvent(tempname,handler,key,mods,buttonname);
+ return ;
+}
+
+static void MAPPER_SaveBinds(void) {
+ FILE * savefile=fopen(mapper.filename.c_str(),"wt+");
+ if (!savefile) {
+ LOG_MSG("Can't open %s for saving the mappings",mapper.filename.c_str());
+ return;
+ }
+ char buf[128];
+ for (CEventVector_it event_it=events.begin();event_it!=events.end();event_it++) {
+ CEvent * event=*(event_it);
+ fprintf(savefile,"%s ",event->GetName());
+ for (CBindList_it bind_it=event->bindlist.begin();bind_it!=event->bindlist.end();bind_it++) {
+ CBind * bind=*(bind_it);
+ bind->ConfigName(buf);
+ bind->AddFlags(buf);
+ fprintf(savefile,"\"%s\" ",buf);
+ }
+ fprintf(savefile,"\n");
+ }
+ fclose(savefile);
+ change_action_text("Mapper file saved.",CLR_WHITE);
+}
+
+static bool MAPPER_LoadBinds(void) {
+ FILE * loadfile=fopen(mapper.filename.c_str(),"rt");
+ if (!loadfile) return false;
+ char linein[512];
+ while (fgets(linein,512,loadfile)) {
+ CreateStringBind(linein);
+ }
+ fclose(loadfile);
+ LOG_MSG("MAPPER: Loading mapper settings from %s", mapper.filename.c_str());
+ return true;
+}
+
+void MAPPER_CheckEvent(SDL_Event * event) {
+ for (CBindGroup_it it=bindgroups.begin();it!=bindgroups.end();it++) {
+ if ((*it)->CheckEvent(event)) return;
+ }
+}
+
+void BIND_MappingEvents(void) {
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_MOUSEBUTTONUP:
+ /* Check the press */
+ for (CButton_it but_it = buttons.begin();but_it!=buttons.end();but_it++) {
+ if ((*but_it)->OnTop(event.button.x,event.button.y)) {
+ (*but_it)->Click();
+ }
+ }
+ break;
+ case SDL_QUIT:
+ mapper.exit=true;
+ break;
+ default:
+ if (mapper.addbind) for (CBindGroup_it it=bindgroups.begin();it!=bindgroups.end();it++) {
+ CBind * newbind=(*it)->CreateEventBind(&event);
+ if (!newbind) continue;
+ mapper.aevent->AddBind(newbind);
+ SetActiveEvent(mapper.aevent);
+ mapper.addbind=false;
+ break;
+ }
+ }
+ }
+}
+
+static void InitializeJoysticks(void) {
+ mapper.sticks.num=0;
+ mapper.sticks.num_groups=0;
+ if (joytype != JOY_NONE) {
+ mapper.sticks.num=SDL_NumJoysticks();
+ if (joytype==JOY_AUTO) {
+ // try to figure out what joystick type to select
+ // depending on the number of physically attached joysticks
+ if (mapper.sticks.num>1) {
+ // more than one joystick present; if all are acceptable use 2axis
+ // to allow emulation of two joysticks
+ bool first_usable=false;
+ SDL_Joystick* tmp_stick1=SDL_JoystickOpen(0);
+ if (tmp_stick1) {
+ if ((SDL_JoystickNumAxes(tmp_stick1)>1) || (SDL_JoystickNumButtons(tmp_stick1)>0)) {
+ first_usable=true;
+ }
+ SDL_JoystickClose(tmp_stick1);
+ }
+ bool second_usable=false;
+ SDL_Joystick* tmp_stick2=SDL_JoystickOpen(1);
+ if (tmp_stick2) {
+ if ((SDL_JoystickNumAxes(tmp_stick2)>1) || (SDL_JoystickNumButtons(tmp_stick2)>0)) {
+ second_usable=true;
+ }
+ SDL_JoystickClose(tmp_stick2);
+ }
+ // choose joystick type now that we know which physical joysticks are usable
+ if (first_usable) {
+ if (second_usable) {
+ joytype=JOY_2AXIS;
+ LOG_MSG("Two or more joysticks reported, initializing with 2axis");
+ } else {
+ joytype=JOY_4AXIS;
+ LOG_MSG("One joystick reported, initializing with 4axis");
+ }
+ } else if (second_usable) {
+ joytype=JOY_4AXIS_2;
+ LOG_MSG("One joystick reported, initializing with 4axis_2");
+ }
+ } else if (mapper.sticks.num) {
+ // one joystick present; if it is acceptable use 4axis
+ joytype=JOY_NONE;
+ SDL_Joystick* tmp_stick1=SDL_JoystickOpen(0);
+ if (tmp_stick1) {
+ if ((SDL_JoystickNumAxes(tmp_stick1)>0) || (SDL_JoystickNumButtons(tmp_stick1)>0)) {
+ joytype=JOY_4AXIS;
+ LOG_MSG("One joystick reported, initializing with 4axis");
+ }
+ }
+ } else {
+ joytype=JOY_NONE;
+ }
+ }
+ }
+}
+
+static void CreateBindGroups(void) {
+ bindgroups.clear();
+ new CKeyBindGroup(SDLK_LAST);
+ if (joytype != JOY_NONE) {
+#if defined (REDUCE_JOYSTICK_POLLING)
+ // direct access to the SDL joystick, thus removed from the event handling
+ if (mapper.sticks.num) SDL_JoystickEventState(SDL_DISABLE);
+#else
+ // enable joystick event handling
+ if (mapper.sticks.num) SDL_JoystickEventState(SDL_ENABLE);
+ else return;
+#endif
+ Bit8u joyno=0;
+ switch (joytype) {
+ case JOY_NONE:
+ break;
+ case JOY_4AXIS:
+ mapper.sticks.stick[mapper.sticks.num_groups++]=new C4AxisBindGroup(joyno,joyno);
+ new CStickBindGroup(joyno+1U,joyno+1U,true);
+ break;
+ case JOY_4AXIS_2:
+ mapper.sticks.stick[mapper.sticks.num_groups++]=new C4AxisBindGroup(joyno+1U,joyno);
+ new CStickBindGroup(joyno,joyno+1U,true);
+ break;
+ case JOY_FCS:
+ mapper.sticks.stick[mapper.sticks.num_groups++]=new CFCSBindGroup(joyno,joyno);
+ new CStickBindGroup(joyno+1U,joyno+1U,true);
+ break;
+ case JOY_CH:
+ mapper.sticks.stick[mapper.sticks.num_groups++]=new CCHBindGroup(joyno,joyno);
+ new CStickBindGroup(joyno+1U,joyno+1U,true);
+ break;
+ case JOY_2AXIS:
+ default:
+ mapper.sticks.stick[mapper.sticks.num_groups++]=new CStickBindGroup(joyno,joyno);
+ if((joyno+1U) < mapper.sticks.num) {
+ mapper.sticks.stick[mapper.sticks.num_groups++]=new CStickBindGroup(joyno+1U,joyno+1U);
+ } else {
+ new CStickBindGroup(joyno+1U,joyno+1U,true);
+ }
+ break;
+ }
+ }
+}
+
+#if defined (REDUCE_JOYSTICK_POLLING)
+void MAPPER_UpdateJoysticks(void) {
+ for (Bitu i=0; i<mapper.sticks.num_groups; i++) {
+ mapper.sticks.stick[i]->UpdateJoystick();
+ }
+}
+#endif
+
+void MAPPER_LosingFocus(void) {
+ for (CEventVector_it evit=events.begin();evit!=events.end();evit++) {
+ if(*evit != caps_lock_event && *evit != num_lock_event)
+ (*evit)->DeActivateAll();
+ }
+}
+
+void MAPPER_RunEvent(Bitu /*val*/) {
+ KEYBOARD_ClrBuffer(); //Clear buffer
+ GFX_LosingFocus(); //Release any keys pressed (buffer gets filled again).
+ MAPPER_RunInternal();
+}
+
+void MAPPER_Run(bool pressed) {
+ if (pressed)
+ return;
+ PIC_AddEvent(MAPPER_RunEvent,0); //In case mapper deletes the key object that ran it
+}
+
+SDL_Surface* SDL_SetVideoMode_Wrap(int width,int height,int bpp,Bit32u flags);
+
+void MAPPER_RunInternal() {
+ int cursor = SDL_ShowCursor(SDL_QUERY);
+ SDL_ShowCursor(SDL_ENABLE);
+ bool mousetoggle=false;
+ if(mouselocked) {
+ mousetoggle=true;
+ GFX_CaptureMouse();
+ }
+
+ /* Be sure that there is no update in progress */
+ GFX_EndUpdate( 0 );
+ mapper.surface=SDL_SetVideoMode_Wrap(640,480,8,0);
+ if (mapper.surface == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError());
+
+ /* Set some palette entries */
+ SDL_SetPalette(mapper.surface, SDL_LOGPAL|SDL_PHYSPAL, map_pal, 0, 6);
+ if (last_clicked) {
+ last_clicked->BindColor();
+ last_clicked=NULL;
+ }
+ /* Go in the event loop */
+ mapper.exit=false;
+ mapper.redraw=true;
+ SetActiveEvent(0);
+#if defined (REDUCE_JOYSTICK_POLLING)
+ SDL_JoystickEventState(SDL_ENABLE);
+#endif
+ while (!mapper.exit) {
+ if (mapper.redraw) {
+ mapper.redraw=false;
+ DrawButtons();
+ }
+ BIND_MappingEvents();
+ SDL_Delay(1);
+ }
+#if defined (REDUCE_JOYSTICK_POLLING)
+ SDL_JoystickEventState(SDL_DISABLE);
+#endif
+ if(mousetoggle) GFX_CaptureMouse();
+ SDL_ShowCursor(cursor);
+ GFX_ResetScreen();
+}
+
+void MAPPER_Init(void) {
+ InitializeJoysticks();
+ CreateLayout();
+ CreateBindGroups();
+ if (!MAPPER_LoadBinds()) CreateDefaultBinds();
+ for (CButton_it but_it = buttons.begin();but_it!=buttons.end();but_it++) {
+ (*but_it)->BindColor();
+ }
+ if (SDL_GetModState()&KMOD_CAPS) {
+ for (CBindList_it bit=caps_lock_event->bindlist.begin();bit!=caps_lock_event->bindlist.end();bit++) {
+#if SDL_VERSION_ATLEAST(1, 2, 14)
+ (*bit)->ActivateBind(32767,true,false);
+ (*bit)->DeActivateBind(false);
+#else
+ (*bit)->ActivateBind(32767,true,true); //Skip the action itself as bios_keyboard.cpp handles the startup state.
+#endif
+ }
+ }
+ if (SDL_GetModState()&KMOD_NUM) {
+ for (CBindList_it bit=num_lock_event->bindlist.begin();bit!=num_lock_event->bindlist.end();bit++) {
+#if SDL_VERSION_ATLEAST(1, 2, 14)
+ (*bit)->ActivateBind(32767,true,false);
+ (*bit)->DeActivateBind(false);
+#else
+ (*bit)->ActivateBind(32767,true,true);
+#endif
+ }
+ }
+}
+//Somehow including them at the top conflicts with something in setup.h
+#ifdef C_X11_XKB
+#include "SDL_syswm.h"
+#include <X11/XKBlib.h>
+#endif
+void MAPPER_StartUp(Section * sec) {
+ Section_prop * section=static_cast<Section_prop *>(sec);
+ mapper.sticks.num=0;
+ mapper.sticks.num_groups=0;
+ memset(&virtual_joysticks,0,sizeof(virtual_joysticks));
+
+ usescancodes = false;
+
+ if (section->Get_bool("usescancodes")) {
+ usescancodes=true;
+
+ /* Note: table has to be tested/updated for various OSs */
+#if defined (MACOSX)
+ /* nothing */
+#elif defined(OS2)
+ sdlkey_map[0x61]=SDLK_UP;
+ sdlkey_map[0x66]=SDLK_DOWN;
+ sdlkey_map[0x63]=SDLK_LEFT;
+ sdlkey_map[0x64]=SDLK_RIGHT;
+ sdlkey_map[0x60]=SDLK_HOME;
+ sdlkey_map[0x65]=SDLK_END;
+ sdlkey_map[0x62]=SDLK_PAGEUP;
+ sdlkey_map[0x67]=SDLK_PAGEDOWN;
+ sdlkey_map[0x68]=SDLK_INSERT;
+ sdlkey_map[0x69]=SDLK_DELETE;
+ sdlkey_map[0x5C]=SDLK_KP_DIVIDE;
+ sdlkey_map[0x5A]=SDLK_KP_ENTER;
+ sdlkey_map[0x5B]=SDLK_RCTRL;
+ sdlkey_map[0x5F]=SDLK_PAUSE;
+// sdlkey_map[0x00]=SDLK_PRINT;
+ sdlkey_map[0x5E]=SDLK_RALT;
+ sdlkey_map[0x40]=SDLK_KP5;
+ sdlkey_map[0x41]=SDLK_KP6;
+#elif !defined (WIN32) /* => Linux & BSDs */
+ bool evdev_input = false;
+#ifdef SDL_VIDEO_DRIVER_X11
+//SDL needs to be compiled to use it, else the next makes no sense.
+#ifdef C_X11_XKB
+ SDL_SysWMinfo info;
+ SDL_VERSION(&info.version);
+ if (SDL_GetWMInfo(&info)) {
+ XkbDescPtr desc = NULL;
+ if((desc = XkbGetMap(info.info.x11.display,XkbAllComponentsMask,XkbUseCoreKbd))) {
+ if(XkbGetNames(info.info.x11.display,XkbAllNamesMask,desc) == 0) {
+ const char* keycodes = XGetAtomName(info.info.x11.display, desc->names->keycodes);
+// const char* geom = XGetAtomName(info.info.x11.display, desc->names->geometry);
+ if(keycodes) {
+ LOG(LOG_MISC,LOG_NORMAL)("keyboard type %s",keycodes);
+ if (strncmp(keycodes,"evdev",5) == 0) evdev_input = true;
+ }
+ XkbFreeNames(desc,XkbAllNamesMask,True);
+ }
+ XkbFreeClientMap(desc,0,True);
+ }
+ }
+#endif
+#endif
+ if (evdev_input) {
+ sdlkey_map[0x67]=SDLK_UP;
+ sdlkey_map[0x6c]=SDLK_DOWN;
+ sdlkey_map[0x69]=SDLK_LEFT;
+ sdlkey_map[0x6a]=SDLK_RIGHT;
+ sdlkey_map[0x66]=SDLK_HOME;
+ sdlkey_map[0x6b]=SDLK_END;
+ sdlkey_map[0x68]=SDLK_PAGEUP;
+ sdlkey_map[0x6d]=SDLK_PAGEDOWN;
+ sdlkey_map[0x6e]=SDLK_INSERT;
+ sdlkey_map[0x6f]=SDLK_DELETE;
+ sdlkey_map[0x62]=SDLK_KP_DIVIDE;
+ sdlkey_map[0x60]=SDLK_KP_ENTER;
+ sdlkey_map[0x61]=SDLK_RCTRL;
+ sdlkey_map[0x77]=SDLK_PAUSE;
+ sdlkey_map[0x63]=SDLK_PRINT;
+ sdlkey_map[0x64]=SDLK_RALT;
+
+ //Win-keys
+ sdlkey_map[0x7d]=SDLK_LSUPER;
+ sdlkey_map[0x7e]=SDLK_RSUPER;
+ sdlkey_map[0x7f]=SDLK_MENU;
+ } else {
+ sdlkey_map[0x5a]=SDLK_UP;
+ sdlkey_map[0x60]=SDLK_DOWN;
+ sdlkey_map[0x5c]=SDLK_LEFT;
+ sdlkey_map[0x5e]=SDLK_RIGHT;
+ sdlkey_map[0x59]=SDLK_HOME;
+ sdlkey_map[0x5f]=SDLK_END;
+ sdlkey_map[0x5b]=SDLK_PAGEUP;
+ sdlkey_map[0x61]=SDLK_PAGEDOWN;
+ sdlkey_map[0x62]=SDLK_INSERT;
+ sdlkey_map[0x63]=SDLK_DELETE;
+ sdlkey_map[0x68]=SDLK_KP_DIVIDE;
+ sdlkey_map[0x64]=SDLK_KP_ENTER;
+ sdlkey_map[0x65]=SDLK_RCTRL;
+ sdlkey_map[0x66]=SDLK_PAUSE;
+ sdlkey_map[0x67]=SDLK_PRINT;
+ sdlkey_map[0x69]=SDLK_RALT;
+ }
+#else
+ sdlkey_map[0xc8]=SDLK_UP;
+ sdlkey_map[0xd0]=SDLK_DOWN;
+ sdlkey_map[0xcb]=SDLK_LEFT;
+ sdlkey_map[0xcd]=SDLK_RIGHT;
+ sdlkey_map[0xc7]=SDLK_HOME;
+ sdlkey_map[0xcf]=SDLK_END;
+ sdlkey_map[0xc9]=SDLK_PAGEUP;
+ sdlkey_map[0xd1]=SDLK_PAGEDOWN;
+ sdlkey_map[0xd2]=SDLK_INSERT;
+ sdlkey_map[0xd3]=SDLK_DELETE;
+ sdlkey_map[0xb5]=SDLK_KP_DIVIDE;
+ sdlkey_map[0x9c]=SDLK_KP_ENTER;
+ sdlkey_map[0x9d]=SDLK_RCTRL;
+ sdlkey_map[0xc5]=SDLK_PAUSE;
+ sdlkey_map[0xb7]=SDLK_PRINT;
+ sdlkey_map[0xb8]=SDLK_RALT;
+
+ //Win-keys
+ sdlkey_map[0xdb]=SDLK_LMETA;
+ sdlkey_map[0xdc]=SDLK_RMETA;
+ sdlkey_map[0xdd]=SDLK_MENU;
+
+#endif
+
+ Bitu i;
+ for (i=0; i<MAX_SDLKEYS; i++) scancode_map[i]=0;
+ for (i=0; i<MAX_SCANCODES; i++) {
+ SDLKey key=sdlkey_map[i];
+ if (key<MAX_SDLKEYS) scancode_map[key]=(Bit8u)i;
+ }
+ }
+
+ Prop_path* pp = section->Get_path("mapperfile");
+ mapper.filename = pp->realpath;
+ MAPPER_AddHandler(&MAPPER_Run,MK_f1,MMOD1,"mapper","Mapper");
+}
+
diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp
new file mode 100644
index 000000000..b06354870
--- /dev/null
+++ b/src/gui/sdlmain.cpp
@@ -0,0 +1,2229 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#ifdef WIN32
+#include <signal.h>
+#include <process.h>
+#endif
+
+#include "cross.h"
+#include "SDL.h"
+
+#include "dosbox.h"
+#include "video.h"
+#include "mouse.h"
+#include "pic.h"
+#include "timer.h"
+#include "setup.h"
+#include "support.h"
+#include "debug.h"
+#include "mapper.h"
+#include "vga.h"
+#include "keyboard.h"
+#include "cpu.h"
+#include "cross.h"
+#include "control.h"
+
+#define MAPPERFILE "mapper-" VERSION ".map"
+//#define DISABLE_JOYSTICK
+
+#if C_OPENGL
+#include "SDL_opengl.h"
+
+#ifndef APIENTRY
+#define APIENTRY
+#endif
+#ifndef APIENTRYP
+#define APIENTRYP APIENTRY *
+#endif
+
+#ifndef GL_ARB_pixel_buffer_object
+#define GL_ARB_pixel_buffer_object 1
+#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB
+#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC
+#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED
+#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF
+#endif
+
+#ifndef GL_ARB_vertex_buffer_object
+#define GL_ARB_vertex_buffer_object 1
+typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers);
+typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer);
+typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers);
+typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
+typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access);
+typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target);
+#endif
+
+PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL;
+PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL;
+PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL;
+PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL;
+PFNGLMAPBUFFERARBPROC glMapBufferARB = NULL;
+PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = NULL;
+
+#endif //C_OPENGL
+
+#if !(ENVIRON_INCLUDED)
+extern char** environ;
+#endif
+
+#ifdef WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#if C_DDRAW
+#include <ddraw.h>
+struct private_hwdata {
+ LPDIRECTDRAWSURFACE3 dd_surface;
+ LPDIRECTDRAWSURFACE3 dd_writebuf;
+};
+#endif
+
+#define STDOUT_FILE TEXT("stdout.txt")
+#define STDERR_FILE TEXT("stderr.txt")
+#define DEFAULT_CONFIG_FILE "/dosbox.conf"
+#elif defined(MACOSX)
+#define DEFAULT_CONFIG_FILE "/Library/Preferences/DOSBox Preferences"
+#else /*linux freebsd*/
+#define DEFAULT_CONFIG_FILE "/.dosboxrc"
+#endif
+
+#if C_SET_PRIORITY
+#include <sys/resource.h>
+#define PRIO_TOTAL (PRIO_MAX-PRIO_MIN)
+#endif
+
+#ifdef OS2
+#define INCL_DOS
+#define INCL_WIN
+#include <os2.h>
+#endif
+
+enum SCREEN_TYPES {
+ SCREEN_SURFACE,
+ SCREEN_SURFACE_DDRAW,
+ SCREEN_OVERLAY,
+ SCREEN_OPENGL
+};
+
+enum PRIORITY_LEVELS {
+ PRIORITY_LEVEL_PAUSE,
+ PRIORITY_LEVEL_LOWEST,
+ PRIORITY_LEVEL_LOWER,
+ PRIORITY_LEVEL_NORMAL,
+ PRIORITY_LEVEL_HIGHER,
+ PRIORITY_LEVEL_HIGHEST
+};
+
+
+struct SDL_Block {
+ bool inited;
+ bool active; //If this isn't set don't draw
+ bool updating;
+ struct {
+ Bit32u width;
+ Bit32u height;
+ Bit32u bpp;
+ Bitu flags;
+ double scalex,scaley;
+ GFX_CallBack_t callback;
+ } draw;
+ bool wait_on_error;
+ struct {
+ struct {
+ Bit16u width, height;
+ bool fixed;
+ } full;
+ struct {
+ Bit16u width, height;
+ } window;
+ Bit8u bpp;
+ bool fullscreen;
+ bool lazy_fullscreen;
+ bool lazy_fullscreen_req;
+ bool doublebuf;
+ SCREEN_TYPES type;
+ SCREEN_TYPES want_type;
+ } desktop;
+#if C_OPENGL
+ struct {
+ Bitu pitch;
+ void * framebuf;
+ GLuint buffer;
+ GLuint texture;
+ GLuint displaylist;
+ GLint max_texsize;
+ bool bilinear;
+ bool packed_pixel;
+ bool paletted_texture;
+ bool pixel_buffer_object;
+ } opengl;
+#endif
+ struct {
+ SDL_Surface * surface;
+#if C_DDRAW
+ RECT rect;
+#endif
+ } blit;
+ struct {
+ PRIORITY_LEVELS focus;
+ PRIORITY_LEVELS nofocus;
+ } priority;
+ SDL_Rect clip;
+ SDL_Surface * surface;
+ SDL_Overlay * overlay;
+ SDL_cond *cond;
+ struct {
+ bool autolock;
+ bool autoenable;
+ bool requestlock;
+ bool locked;
+ int xsensitivity;
+ int ysensitivity;
+ } mouse;
+ SDL_Rect updateRects[1024];
+ Bitu num_joysticks;
+#if defined (WIN32)
+ bool using_windib;
+ // Time when sdl regains focus (alt-tab) in windowed mode
+ Bit32u focus_ticks;
+#endif
+ // state of alt-keys for certain special handlings
+ Bit8u laltstate;
+ Bit8u raltstate;
+};
+
+static SDL_Block sdl;
+
+#define SETMODE_SAVES 1 //Don't set Video Mode if nothing changes.
+#define SETMODE_SAVES_CLEAR 1 //Clear the screen, when the Video Mode is reused
+SDL_Surface* SDL_SetVideoMode_Wrap(int width,int height,int bpp,Bit32u flags){
+#if SETMODE_SAVES
+ static int i_height = 0;
+ static int i_width = 0;
+ static int i_bpp = 0;
+ static Bit32u i_flags = 0;
+ if (sdl.surface != NULL && height == i_height && width == i_width && bpp == i_bpp && flags == i_flags) {
+ // I don't see a difference, so disabled for now, as the code isn't finished either
+#if SETMODE_SAVES_CLEAR
+ //TODO clear it.
+#if C_OPENGL
+ if ((flags & SDL_OPENGL)==0)
+ SDL_FillRect(sdl.surface,NULL,SDL_MapRGB(sdl.surface->format,0,0,0));
+ else {
+ glClearColor (0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ SDL_GL_SwapBuffers();
+ }
+#else //C_OPENGL
+ SDL_FillRect(sdl.surface,NULL,SDL_MapRGB(sdl.surface->format,0,0,0));
+#endif //C_OPENGL
+#endif //SETMODE_SAVES_CLEAR
+ return sdl.surface;
+ }
+
+
+#ifdef WIN32
+ //SDL seems to crash if we are in OpenGL mode currently and change to exactly the same size without OpenGL.
+ //This happens when DOSBox is in textmode with aspect=true and output=opengl and the mapper is started.
+ //The easiest solution is to change the size. The mapper doesn't care. (PART PXX)
+
+ //Also we have to switch back to windowed mode first, as else it may crash as well.
+ //Bug: we end up with a locked mouse cursor, but at least that beats crashing. (output=opengl,aspect=true,fullscreen=true)
+ if((i_flags&SDL_OPENGL) && !(flags&SDL_OPENGL) && (i_flags&SDL_FULLSCREEN) && !(flags&SDL_FULLSCREEN)){
+ GFX_SwitchFullScreen();
+ return SDL_SetVideoMode_Wrap(width,height,bpp,flags);
+ }
+
+ //PXX
+ if ((i_flags&SDL_OPENGL) && !(flags&SDL_OPENGL) && height==i_height && width==i_width && height==480) {
+ height++;
+ }
+#endif //WIN32
+#endif //SETMODE_SAVES
+ SDL_Surface* s = SDL_SetVideoMode(width,height,bpp,flags);
+#if SETMODE_SAVES
+ if (s == NULL) return s; //Only store when successful
+ i_height = height;
+ i_width = width;
+ i_bpp = bpp;
+ i_flags = flags;
+#endif
+ return s;
+}
+
+extern const char* RunningProgram;
+extern bool CPU_CycleAutoAdjust;
+//Globals for keyboard initialisation
+bool startup_state_numlock=false;
+bool startup_state_capslock=false;
+
+void GFX_SetTitle(Bit32s cycles,Bits frameskip,bool paused){
+ char title[200]={0};
+ static Bit32s internal_cycles=0;
+ static Bit32s internal_frameskip=0;
+ if(cycles != -1) internal_cycles = cycles;
+ if(frameskip != -1) internal_frameskip = frameskip;
+ if(CPU_CycleAutoAdjust) {
+ sprintf(title,"DOSBox %s, CPU speed: max %3d%% cycles, Frameskip %2d, Program: %8s",VERSION,internal_cycles,internal_frameskip,RunningProgram);
+ } else {
+ sprintf(title,"DOSBox %s, CPU speed: %8d cycles, Frameskip %2d, Program: %8s",VERSION,internal_cycles,internal_frameskip,RunningProgram);
+ }
+
+ if(paused) strcat(title," PAUSED");
+ SDL_WM_SetCaption(title,VERSION);
+}
+
+static unsigned char logo[32*32*4]= {
+#include "dosbox_logo.h"
+};
+static void GFX_SetIcon() {
+#if !defined(MACOSX)
+ /* Set Icon (must be done before any sdl_setvideomode call) */
+ /* But don't set it on OS X, as we use a nicer external icon there. */
+ /* Made into a separate call, so it can be called again when we restart the graphics output on win32 */
+#ifdef WORDS_BIGENDIAN
+ SDL_Surface* logos= SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0xff000000,0x00ff0000,0x0000ff00,0);
+#else
+ SDL_Surface* logos= SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0x000000ff,0x0000ff00,0x00ff0000,0);
+#endif
+ SDL_WM_SetIcon(logos,NULL);
+#endif
+}
+
+
+static void KillSwitch(bool pressed) {
+ if (!pressed)
+ return;
+ throw 1;
+}
+
+static void PauseDOSBox(bool pressed) {
+ if (!pressed)
+ return;
+ GFX_SetTitle(-1,-1,true);
+ bool paused = true;
+ KEYBOARD_ClrBuffer();
+ SDL_Delay(500);
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ // flush event queue.
+ }
+
+ while (paused) {
+ SDL_WaitEvent(&event); // since we're not polling, cpu usage drops to 0.
+ switch (event.type) {
+
+ case SDL_QUIT: KillSwitch(true); break;
+ case SDL_KEYDOWN: // Must use Pause/Break Key to resume.
+ case SDL_KEYUP:
+ if(event.key.keysym.sym == SDLK_PAUSE) {
+
+ paused = false;
+ GFX_SetTitle(-1,-1,false);
+ break;
+ }
+#if defined (MACOSX)
+ if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) {
+ /* On macs, all aps exit when pressing cmd-q */
+ KillSwitch(true);
+ break;
+ }
+#endif
+ }
+ }
+}
+
+#if defined (WIN32)
+bool GFX_SDLUsingWinDIB(void) {
+ return sdl.using_windib;
+}
+#endif
+
+/* Reset the screen with current values in the sdl structure */
+Bitu GFX_GetBestMode(Bitu flags) {
+ Bitu testbpp,gotbpp;
+ switch (sdl.desktop.want_type) {
+ case SCREEN_SURFACE:
+check_surface:
+ flags &= ~GFX_LOVE_8; //Disable love for 8bpp modes
+ /* Check if we can satisfy the depth it loves */
+ if (flags & GFX_LOVE_8) testbpp=8;
+ else if (flags & GFX_LOVE_15) testbpp=15;
+ else if (flags & GFX_LOVE_16) testbpp=16;
+ else if (flags & GFX_LOVE_32) testbpp=32;
+ else testbpp=0;
+#if C_DDRAW
+check_gotbpp:
+#endif
+ if (sdl.desktop.fullscreen) gotbpp=SDL_VideoModeOK(640,480,testbpp,SDL_FULLSCREEN|SDL_HWSURFACE|SDL_HWPALETTE);
+ else gotbpp=sdl.desktop.bpp;
+ /* If we can't get our favorite mode check for another working one */
+ switch (gotbpp) {
+ case 8:
+ if (flags & GFX_CAN_8) flags&=~(GFX_CAN_15|GFX_CAN_16|GFX_CAN_32);
+ break;
+ case 15:
+ if (flags & GFX_CAN_15) flags&=~(GFX_CAN_8|GFX_CAN_16|GFX_CAN_32);
+ break;
+ case 16:
+ if (flags & GFX_CAN_16) flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_32);
+ break;
+ case 24:
+ case 32:
+ if (flags & GFX_CAN_32) flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16);
+ break;
+ }
+ flags |= GFX_CAN_RANDOM;
+ break;
+#if C_DDRAW
+ case SCREEN_SURFACE_DDRAW:
+ if (!(flags&(GFX_CAN_15|GFX_CAN_16|GFX_CAN_32))) goto check_surface;
+ if (flags & GFX_LOVE_15) testbpp=15;
+ else if (flags & GFX_LOVE_16) testbpp=16;
+ else if (flags & GFX_LOVE_32) testbpp=32;
+ else testbpp=0;
+ flags|=GFX_SCALING;
+ goto check_gotbpp;
+#endif
+ case SCREEN_OVERLAY:
+ //We only accept 32bit output from the scalers here
+ //Can't handle true color inputs
+ if (flags & GFX_RGBONLY || !(flags&GFX_CAN_32)) goto check_surface;
+ flags|=GFX_SCALING;
+ flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16);
+ break;
+#if C_OPENGL
+ case SCREEN_OPENGL:
+ //We only accept 32bit output from the scalers here
+ if (!(flags&GFX_CAN_32)) goto check_surface;
+ flags|=GFX_SCALING;
+ flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16);
+ break;
+#endif
+ default:
+ goto check_surface;
+ break;
+ }
+ return flags;
+}
+
+
+void GFX_ResetScreen(void) {
+ GFX_Stop();
+ if (sdl.draw.callback)
+ (sdl.draw.callback)( GFX_CallBackReset );
+ GFX_Start();
+ CPU_Reset_AutoAdjust();
+}
+
+void GFX_ForceFullscreenExit(void) {
+ if (sdl.desktop.lazy_fullscreen) {
+// sdl.desktop.lazy_fullscreen_req=true;
+ LOG_MSG("GFX LF: invalid screen change");
+ } else {
+ sdl.desktop.fullscreen=false;
+ GFX_ResetScreen();
+ }
+}
+
+static int int_log2 (int val) {
+ int log = 0;
+ while ((val >>= 1) != 0)
+ log++;
+ return log;
+}
+
+
+static SDL_Surface * GFX_SetupSurfaceScaled(Bit32u sdl_flags, Bit32u bpp) {
+ Bit16u fixedWidth;
+ Bit16u fixedHeight;
+
+ if (sdl.desktop.fullscreen) {
+ fixedWidth = sdl.desktop.full.fixed ? sdl.desktop.full.width : 0;
+ fixedHeight = sdl.desktop.full.fixed ? sdl.desktop.full.height : 0;
+ sdl_flags |= SDL_FULLSCREEN|SDL_HWSURFACE;
+ } else {
+ fixedWidth = sdl.desktop.window.width;
+ fixedHeight = sdl.desktop.window.height;
+ sdl_flags |= SDL_HWSURFACE;
+ }
+ if (fixedWidth && fixedHeight) {
+ double ratio_w=(double)fixedWidth/(sdl.draw.width*sdl.draw.scalex);
+ double ratio_h=(double)fixedHeight/(sdl.draw.height*sdl.draw.scaley);
+ if ( ratio_w < ratio_h) {
+ sdl.clip.w=fixedWidth;
+ sdl.clip.h=(Bit16u)(sdl.draw.height*sdl.draw.scaley*ratio_w + 0.1); //possible rounding issues
+ } else {
+ /*
+ * The 0.4 is there to correct for rounding issues.
+ * (partly caused by the rounding issues fix in RENDER_SetSize)
+ */
+ sdl.clip.w=(Bit16u)(sdl.draw.width*sdl.draw.scalex*ratio_h + 0.4);
+ sdl.clip.h=(Bit16u)fixedHeight;
+ }
+ if (sdl.desktop.fullscreen)
+ sdl.surface = SDL_SetVideoMode_Wrap(fixedWidth,fixedHeight,bpp,sdl_flags);
+ else
+ sdl.surface = SDL_SetVideoMode_Wrap(sdl.clip.w,sdl.clip.h,bpp,sdl_flags);
+ if (sdl.surface && sdl.surface->flags & SDL_FULLSCREEN) {
+ sdl.clip.x=(Sint16)((sdl.surface->w-sdl.clip.w)/2);
+ sdl.clip.y=(Sint16)((sdl.surface->h-sdl.clip.h)/2);
+ } else {
+ sdl.clip.x = 0;
+ sdl.clip.y = 0;
+ }
+ return sdl.surface;
+ } else {
+ sdl.clip.x=0;sdl.clip.y=0;
+ sdl.clip.w=(Bit16u)(sdl.draw.width*sdl.draw.scalex);
+ sdl.clip.h=(Bit16u)(sdl.draw.height*sdl.draw.scaley);
+ sdl.surface=SDL_SetVideoMode_Wrap(sdl.clip.w,sdl.clip.h,bpp,sdl_flags);
+ return sdl.surface;
+ }
+}
+
+void GFX_TearDown(void) {
+ if (sdl.updating)
+ GFX_EndUpdate( 0 );
+
+ if (sdl.blit.surface) {
+ SDL_FreeSurface(sdl.blit.surface);
+ sdl.blit.surface=0;
+ }
+}
+
+Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,GFX_CallBack_t callback) {
+ if (sdl.updating)
+ GFX_EndUpdate( 0 );
+
+ sdl.draw.width=width;
+ sdl.draw.height=height;
+ sdl.draw.callback=callback;
+ sdl.draw.scalex=scalex;
+ sdl.draw.scaley=scaley;
+
+ int bpp=0;
+ Bitu retFlags = 0;
+
+ if (sdl.blit.surface) {
+ SDL_FreeSurface(sdl.blit.surface);
+ sdl.blit.surface=0;
+ }
+ switch (sdl.desktop.want_type) {
+ case SCREEN_SURFACE:
+dosurface:
+ if (flags & GFX_CAN_8) bpp=8;
+ if (flags & GFX_CAN_15) bpp=15;
+ if (flags & GFX_CAN_16) bpp=16;
+ if (flags & GFX_CAN_32) bpp=32;
+ sdl.desktop.type=SCREEN_SURFACE;
+ sdl.clip.w=width;
+ sdl.clip.h=height;
+ if (sdl.desktop.fullscreen) {
+ if (sdl.desktop.full.fixed) {
+ sdl.clip.x=(Sint16)((sdl.desktop.full.width-width)/2);
+ sdl.clip.y=(Sint16)((sdl.desktop.full.height-height)/2);
+ sdl.surface=SDL_SetVideoMode_Wrap(sdl.desktop.full.width,sdl.desktop.full.height,bpp,
+ SDL_FULLSCREEN | ((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) |
+ (sdl.desktop.doublebuf ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0) | SDL_HWPALETTE);
+ if (sdl.surface == NULL) E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",sdl.desktop.full.width,sdl.desktop.full.height,bpp,SDL_GetError());
+ } else {
+ sdl.clip.x=0;sdl.clip.y=0;
+ sdl.surface=SDL_SetVideoMode_Wrap(width,height,bpp,
+ SDL_FULLSCREEN | ((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) |
+ (sdl.desktop.doublebuf ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0)|SDL_HWPALETTE);
+ if (sdl.surface == NULL)
+ E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",(int)width,(int)height,bpp,SDL_GetError());
+ }
+ } else {
+ sdl.clip.x=0;sdl.clip.y=0;
+ sdl.surface=SDL_SetVideoMode_Wrap(width,height,bpp,(flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE);
+#ifdef WIN32
+ if (sdl.surface == NULL) {
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ if (!sdl.using_windib) {
+ LOG_MSG("Failed to create hardware surface.\nRestarting video subsystem with windib enabled.");
+ putenv("SDL_VIDEODRIVER=windib");
+ sdl.using_windib=true;
+ } else {
+ LOG_MSG("Failed to create hardware surface.\nRestarting video subsystem with directx enabled.");
+ putenv("SDL_VIDEODRIVER=directx");
+ sdl.using_windib=false;
+ }
+ SDL_InitSubSystem(SDL_INIT_VIDEO);
+ GFX_SetIcon(); //Set Icon again
+ sdl.surface = SDL_SetVideoMode_Wrap(width,height,bpp,SDL_HWSURFACE);
+ if(sdl.surface) GFX_SetTitle(-1,-1,false); //refresh title.
+ }
+#endif
+ if (sdl.surface == NULL)
+ E_Exit("Could not set windowed video mode %ix%i-%i: %s",(int)width,(int)height,bpp,SDL_GetError());
+ }
+ if (sdl.surface) {
+ switch (sdl.surface->format->BitsPerPixel) {
+ case 8:
+ retFlags = GFX_CAN_8;
+ break;
+ case 15:
+ retFlags = GFX_CAN_15;
+ break;
+ case 16:
+ retFlags = GFX_CAN_16;
+ break;
+ case 32:
+ retFlags = GFX_CAN_32;
+ break;
+ }
+ if (retFlags && (sdl.surface->flags & SDL_HWSURFACE))
+ retFlags |= GFX_HARDWARE;
+ if (retFlags && (sdl.surface->flags & SDL_DOUBLEBUF)) {
+ sdl.blit.surface=SDL_CreateRGBSurface(SDL_HWSURFACE,
+ sdl.draw.width, sdl.draw.height,
+ sdl.surface->format->BitsPerPixel,
+ sdl.surface->format->Rmask,
+ sdl.surface->format->Gmask,
+ sdl.surface->format->Bmask,
+ 0);
+ /* If this one fails be ready for some flickering... */
+ }
+ }
+ break;
+#if C_DDRAW
+ case SCREEN_SURFACE_DDRAW:
+ if (flags & GFX_CAN_15) bpp=15;
+ if (flags & GFX_CAN_16) bpp=16;
+ if (flags & GFX_CAN_32) bpp=32;
+ if (!GFX_SetupSurfaceScaled((sdl.desktop.doublebuf && sdl.desktop.fullscreen) ? SDL_DOUBLEBUF : 0,bpp)) goto dosurface;
+ sdl.blit.rect.top=sdl.clip.y;
+ sdl.blit.rect.left=sdl.clip.x;
+ sdl.blit.rect.right=sdl.clip.x+sdl.clip.w;
+ sdl.blit.rect.bottom=sdl.clip.y+sdl.clip.h;
+ sdl.blit.surface=SDL_CreateRGBSurface(SDL_HWSURFACE,sdl.draw.width,sdl.draw.height,
+ sdl.surface->format->BitsPerPixel,
+ sdl.surface->format->Rmask,
+ sdl.surface->format->Gmask,
+ sdl.surface->format->Bmask,
+ 0);
+ if (!sdl.blit.surface || (!sdl.blit.surface->flags&SDL_HWSURFACE)) {
+ if (sdl.blit.surface) {
+ SDL_FreeSurface(sdl.blit.surface);
+ sdl.blit.surface=0;
+ }
+ LOG_MSG("Failed to create ddraw surface, back to normal surface.");
+ goto dosurface;
+ }
+ switch (sdl.surface->format->BitsPerPixel) {
+ case 15:
+ retFlags = GFX_CAN_15 | GFX_SCALING | GFX_HARDWARE;
+ break;
+ case 16:
+ retFlags = GFX_CAN_16 | GFX_SCALING | GFX_HARDWARE;
+ break;
+ case 32:
+ retFlags = GFX_CAN_32 | GFX_SCALING | GFX_HARDWARE;
+ break;
+ }
+ sdl.desktop.type=SCREEN_SURFACE_DDRAW;
+ break;
+#endif
+ case SCREEN_OVERLAY:
+ if (sdl.overlay) {
+ SDL_FreeYUVOverlay(sdl.overlay);
+ sdl.overlay=0;
+ }
+ if (!(flags&GFX_CAN_32) || (flags & GFX_RGBONLY)) goto dosurface;
+ if (!GFX_SetupSurfaceScaled(0,0)) goto dosurface;
+ sdl.overlay=SDL_CreateYUVOverlay(width*2,height,SDL_UYVY_OVERLAY,sdl.surface);
+ if (!sdl.overlay) {
+ LOG_MSG("SDL: Failed to create overlay, switching back to surface");
+ goto dosurface;
+ }
+ sdl.desktop.type=SCREEN_OVERLAY;
+ retFlags = GFX_CAN_32 | GFX_SCALING | GFX_HARDWARE;
+ break;
+#if C_OPENGL
+ case SCREEN_OPENGL:
+ {
+ if (sdl.opengl.pixel_buffer_object) {
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ if (sdl.opengl.buffer) glDeleteBuffersARB(1, &sdl.opengl.buffer);
+ } else if (sdl.opengl.framebuf) {
+ free(sdl.opengl.framebuf);
+ }
+ sdl.opengl.framebuf=0;
+ if (!(flags&GFX_CAN_32)) goto dosurface;
+ int texsize=2 << int_log2(width > height ? width : height);
+ if (texsize>sdl.opengl.max_texsize) {
+ LOG_MSG("SDL:OPENGL: No support for texturesize of %d, falling back to surface",texsize);
+ goto dosurface;
+ }
+ SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
+#if SDL_VERSION_ATLEAST(1, 2, 11)
+ SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 0 );
+#endif
+ GFX_SetupSurfaceScaled(SDL_OPENGL,0);
+ if (!sdl.surface || sdl.surface->format->BitsPerPixel<15) {
+ LOG_MSG("SDL:OPENGL: Can't open drawing surface, are you running in 16bpp (or higher) mode?");
+ goto dosurface;
+ }
+ /* Create the texture and display list */
+ if (sdl.opengl.pixel_buffer_object) {
+ glGenBuffersARB(1, &sdl.opengl.buffer);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer);
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, width*height*4, NULL, GL_STREAM_DRAW_ARB);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ } else {
+ sdl.opengl.framebuf=malloc(width*height*4); //32 bit color
+ }
+ sdl.opengl.pitch=width*4;
+
+ if(sdl.clip.x ==0 && sdl.clip.y ==0 && sdl.desktop.fullscreen && !sdl.desktop.full.fixed && (sdl.clip.w != sdl.surface->w || sdl.clip.h != sdl.surface->h)) {
+// LOG_MSG("attempting to fix the centering to %d %d %d %d",(sdl.surface->w-sdl.clip.w)/2,(sdl.surface->h-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h);
+ glViewport((sdl.surface->w-sdl.clip.w)/2,(sdl.surface->h-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h);
+ } else {
+ glViewport(sdl.clip.x,sdl.clip.y,sdl.clip.w,sdl.clip.h);
+ }
+
+ glMatrixMode (GL_PROJECTION);
+ glDeleteTextures(1,&sdl.opengl.texture);
+ glGenTextures(1,&sdl.opengl.texture);
+ glBindTexture(GL_TEXTURE_2D,sdl.opengl.texture);
+ // No borders
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ if (!sdl.opengl.bilinear || ( (sdl.clip.h % height) == 0 && (sdl.clip.w % width) == 0) ) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ }
+
+ Bit8u* emptytex = new Bit8u[texsize * texsize * 4];
+ memset((void*) emptytex, 0, texsize * texsize * 4);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texsize, texsize, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, (const GLvoid*)emptytex);
+ delete [] emptytex;
+
+ glClearColor (0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ SDL_GL_SwapBuffers();
+ glClear(GL_COLOR_BUFFER_BIT);
+ glShadeModel (GL_FLAT);
+ glDisable (GL_DEPTH_TEST);
+ glDisable (GL_LIGHTING);
+ glDisable(GL_CULL_FACE);
+ glEnable(GL_TEXTURE_2D);
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+
+ GLfloat tex_width=((GLfloat)(width)/(GLfloat)texsize);
+ GLfloat tex_height=((GLfloat)(height)/(GLfloat)texsize);
+
+ if (glIsList(sdl.opengl.displaylist)) glDeleteLists(sdl.opengl.displaylist, 1);
+ sdl.opengl.displaylist = glGenLists(1);
+ glNewList(sdl.opengl.displaylist, GL_COMPILE);
+ glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture);
+ glBegin(GL_QUADS);
+ // lower left
+ glTexCoord2f(0,tex_height); glVertex2f(-1.0f,-1.0f);
+ // lower right
+ glTexCoord2f(tex_width,tex_height); glVertex2f(1.0f, -1.0f);
+ // upper right
+ glTexCoord2f(tex_width,0); glVertex2f(1.0f, 1.0f);
+ // upper left
+ glTexCoord2f(0,0); glVertex2f(-1.0f, 1.0f);
+ glEnd();
+ glEndList();
+ sdl.desktop.type=SCREEN_OPENGL;
+ retFlags = GFX_CAN_32 | GFX_SCALING;
+ if (sdl.opengl.pixel_buffer_object)
+ retFlags |= GFX_HARDWARE;
+ break;
+ }//OPENGL
+#endif //C_OPENGL
+ default:
+ goto dosurface;
+ break;
+ }//CASE
+ if (retFlags)
+ GFX_Start();
+ if (!sdl.mouse.autoenable) SDL_ShowCursor(sdl.mouse.autolock?SDL_DISABLE:SDL_ENABLE);
+ return retFlags;
+}
+
+void GFX_CaptureMouse(void) {
+ sdl.mouse.locked=!sdl.mouse.locked;
+ if (sdl.mouse.locked) {
+ SDL_WM_GrabInput(SDL_GRAB_ON);
+ SDL_ShowCursor(SDL_DISABLE);
+ } else {
+ SDL_WM_GrabInput(SDL_GRAB_OFF);
+ if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE);
+ }
+ mouselocked=sdl.mouse.locked;
+}
+
+void GFX_UpdateSDLCaptureState(void) {
+ if (sdl.mouse.locked) {
+ SDL_WM_GrabInput(SDL_GRAB_ON);
+ SDL_ShowCursor(SDL_DISABLE);
+ } else {
+ SDL_WM_GrabInput(SDL_GRAB_OFF);
+ if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE);
+ }
+ CPU_Reset_AutoAdjust();
+ GFX_SetTitle(-1,-1,false);
+}
+
+bool mouselocked; //Global variable for mapper
+static void CaptureMouse(bool pressed) {
+ if (!pressed)
+ return;
+ GFX_CaptureMouse();
+}
+
+#if defined (WIN32)
+STICKYKEYS stick_keys = {sizeof(STICKYKEYS), 0};
+void sticky_keys(bool restore){
+ static bool inited = false;
+ if (!inited){
+ inited = true;
+ SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &stick_keys, 0);
+ }
+ if (restore) {
+ SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &stick_keys, 0);
+ return;
+ }
+ //Get current sticky keys layout:
+ STICKYKEYS s = {sizeof(STICKYKEYS), 0};
+ SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &s, 0);
+ if ( !(s.dwFlags & SKF_STICKYKEYSON)) { //Not on already
+ s.dwFlags &= ~SKF_HOTKEYACTIVE;
+ SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &s, 0);
+ }
+}
+#endif
+
+void GFX_SwitchFullScreen(void) {
+ sdl.desktop.fullscreen=!sdl.desktop.fullscreen;
+ if (sdl.desktop.fullscreen) {
+ if (!sdl.mouse.locked) GFX_CaptureMouse();
+#if defined (WIN32)
+ sticky_keys(false); //disable sticky keys in fullscreen mode
+#endif
+ } else {
+ if (sdl.mouse.locked) GFX_CaptureMouse();
+#if defined (WIN32)
+ sticky_keys(true); //restore sticky keys to default state in windowed mode.
+#endif
+ }
+ GFX_ResetScreen();
+}
+
+static void SwitchFullScreen(bool pressed) {
+ if (!pressed)
+ return;
+
+ if (sdl.desktop.lazy_fullscreen) {
+// sdl.desktop.lazy_fullscreen_req=true;
+ LOG_MSG("GFX LF: fullscreen switching not supported");
+ } else {
+ GFX_SwitchFullScreen();
+ }
+}
+
+void GFX_SwitchLazyFullscreen(bool lazy) {
+ sdl.desktop.lazy_fullscreen=lazy;
+ sdl.desktop.lazy_fullscreen_req=false;
+}
+
+void GFX_SwitchFullscreenNoReset(void) {
+ sdl.desktop.fullscreen=!sdl.desktop.fullscreen;
+}
+
+bool GFX_LazyFullscreenRequested(void) {
+ if (sdl.desktop.lazy_fullscreen) return sdl.desktop.lazy_fullscreen_req;
+ return false;
+}
+
+void GFX_RestoreMode(void) {
+ GFX_SetSize(sdl.draw.width,sdl.draw.height,sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback);
+ GFX_UpdateSDLCaptureState();
+}
+
+
+bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) {
+ if (!sdl.active || sdl.updating)
+ return false;
+ switch (sdl.desktop.type) {
+ case SCREEN_SURFACE:
+ if (sdl.blit.surface) {
+ if (SDL_MUSTLOCK(sdl.blit.surface) && SDL_LockSurface(sdl.blit.surface))
+ return false;
+ pixels=(Bit8u *)sdl.blit.surface->pixels;
+ pitch=sdl.blit.surface->pitch;
+ } else {
+ if (SDL_MUSTLOCK(sdl.surface) && SDL_LockSurface(sdl.surface))
+ return false;
+ pixels=(Bit8u *)sdl.surface->pixels;
+ pixels+=sdl.clip.y*sdl.surface->pitch;
+ pixels+=sdl.clip.x*sdl.surface->format->BytesPerPixel;
+ pitch=sdl.surface->pitch;
+ }
+ sdl.updating=true;
+ return true;
+#if C_DDRAW
+ case SCREEN_SURFACE_DDRAW:
+ if (SDL_LockSurface(sdl.blit.surface)) {
+// LOG_MSG("SDL Lock failed");
+ return false;
+ }
+ pixels=(Bit8u *)sdl.blit.surface->pixels;
+ pitch=sdl.blit.surface->pitch;
+ sdl.updating=true;
+ return true;
+#endif
+ case SCREEN_OVERLAY:
+ if (SDL_LockYUVOverlay(sdl.overlay)) return false;
+ pixels=(Bit8u *)*(sdl.overlay->pixels);
+ pitch=*(sdl.overlay->pitches);
+ sdl.updating=true;
+ return true;
+#if C_OPENGL
+ case SCREEN_OPENGL:
+ if(sdl.opengl.pixel_buffer_object) {
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer);
+ pixels=(Bit8u *)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY);
+ } else
+ pixels=(Bit8u *)sdl.opengl.framebuf;
+ pitch=sdl.opengl.pitch;
+ sdl.updating=true;
+ return true;
+#endif
+ default:
+ break;
+ }
+ return false;
+}
+
+
+void GFX_EndUpdate( const Bit16u *changedLines ) {
+#if C_DDRAW
+ int ret;
+#endif
+ if (!sdl.updating)
+ return;
+ sdl.updating=false;
+ switch (sdl.desktop.type) {
+ case SCREEN_SURFACE:
+ if (SDL_MUSTLOCK(sdl.surface)) {
+ if (sdl.blit.surface) {
+ SDL_UnlockSurface(sdl.blit.surface);
+ int Blit = SDL_BlitSurface( sdl.blit.surface, 0, sdl.surface, &sdl.clip );
+ LOG(LOG_MISC,LOG_WARN)("BlitSurface returned %d",Blit);
+ } else {
+ SDL_UnlockSurface(sdl.surface);
+ }
+ SDL_Flip(sdl.surface);
+ } else if (changedLines) {
+ Bitu y = 0, index = 0, rectCount = 0;
+ while (y < sdl.draw.height) {
+ if (!(index & 1)) {
+ y += changedLines[index];
+ } else {
+ SDL_Rect *rect = &sdl.updateRects[rectCount++];
+ rect->x = sdl.clip.x;
+ rect->y = sdl.clip.y + y;
+ rect->w = (Bit16u)sdl.draw.width;
+ rect->h = changedLines[index];
+#if 0
+ if (rect->h + rect->y > sdl.surface->h) {
+ LOG_MSG("WTF %d + %d >%d",rect->h,rect->y,sdl.surface->h);
+ }
+#endif
+ y += changedLines[index];
+ }
+ index++;
+ }
+ if (rectCount)
+ SDL_UpdateRects( sdl.surface, rectCount, sdl.updateRects );
+ }
+ break;
+#if C_DDRAW
+ case SCREEN_SURFACE_DDRAW:
+ SDL_UnlockSurface(sdl.blit.surface);
+ ret=IDirectDrawSurface3_Blt(
+ sdl.surface->hwdata->dd_writebuf,&sdl.blit.rect,
+ sdl.blit.surface->hwdata->dd_surface,0,
+ DDBLT_WAIT, NULL);
+ switch (ret) {
+ case DD_OK:
+ break;
+ case DDERR_SURFACELOST:
+ IDirectDrawSurface3_Restore(sdl.blit.surface->hwdata->dd_surface);
+ IDirectDrawSurface3_Restore(sdl.surface->hwdata->dd_surface);
+ break;
+ default:
+ LOG_MSG("DDRAW: Failed to blit, error %X",ret);
+ }
+ SDL_Flip(sdl.surface);
+ break;
+#endif
+ case SCREEN_OVERLAY:
+ SDL_UnlockYUVOverlay(sdl.overlay);
+ SDL_DisplayYUVOverlay(sdl.overlay,&sdl.clip);
+ break;
+#if C_OPENGL
+ case SCREEN_OPENGL:
+ // Clear drawing area. Some drivers (on Linux) have more than 2 buffers and the screen might
+ // be dirty because of other programs.
+ glClearColor (0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ if (sdl.opengl.pixel_buffer_object) {
+ glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
+ glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+ sdl.draw.width, sdl.draw.height, GL_BGRA_EXT,
+ GL_UNSIGNED_INT_8_8_8_8_REV, 0);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ glCallList(sdl.opengl.displaylist);
+ SDL_GL_SwapBuffers();
+ } else if (changedLines) {
+ Bitu y = 0, index = 0;
+ glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture);
+ while (y < sdl.draw.height) {
+ if (!(index & 1)) {
+ y += changedLines[index];
+ } else {
+ Bit8u *pixels = (Bit8u *)sdl.opengl.framebuf + y * sdl.opengl.pitch;
+ Bitu height = changedLines[index];
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y,
+ sdl.draw.width, height, GL_BGRA_EXT,
+ GL_UNSIGNED_INT_8_8_8_8_REV, pixels );
+ y += height;
+ }
+ index++;
+ }
+ glCallList(sdl.opengl.displaylist);
+ SDL_GL_SwapBuffers();
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+
+void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries) {
+ /* I should probably not change the GFX_PalEntry :) */
+ if (sdl.surface->flags & SDL_HWPALETTE) {
+ if (!SDL_SetPalette(sdl.surface,SDL_PHYSPAL,(SDL_Color *)entries,start,count)) {
+ E_Exit("SDL:Can't set palette");
+ }
+ } else {
+ if (!SDL_SetPalette(sdl.surface,SDL_LOGPAL,(SDL_Color *)entries,start,count)) {
+ E_Exit("SDL:Can't set palette");
+ }
+ }
+}
+
+Bitu GFX_GetRGB(Bit8u red,Bit8u green,Bit8u blue) {
+ switch (sdl.desktop.type) {
+ case SCREEN_SURFACE:
+ case SCREEN_SURFACE_DDRAW:
+ return SDL_MapRGB(sdl.surface->format,red,green,blue);
+ case SCREEN_OVERLAY:
+ {
+ Bit8u y = ( 9797*(red) + 19237*(green) + 3734*(blue) ) >> 15;
+ Bit8u u = (18492*((blue)-(y)) >> 15) + 128;
+ Bit8u v = (23372*((red)-(y)) >> 15) + 128;
+#ifdef WORDS_BIGENDIAN
+ return (y << 0) | (v << 8) | (y << 16) | (u << 24);
+#else
+ return (u << 0) | (y << 8) | (v << 16) | (y << 24);
+#endif
+ }
+ case SCREEN_OPENGL:
+// return ((red << 0) | (green << 8) | (blue << 16)) | (255 << 24);
+ //USE BGRA
+ return ((blue << 0) | (green << 8) | (red << 16)) | (255 << 24);
+ }
+ return 0;
+}
+
+void GFX_Stop() {
+ if (sdl.updating)
+ GFX_EndUpdate( 0 );
+ sdl.active=false;
+}
+
+void GFX_Start() {
+ sdl.active=true;
+}
+
+static void GUI_ShutDown(Section * /*sec*/) {
+ GFX_Stop();
+ if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackStop );
+ if (sdl.mouse.locked) GFX_CaptureMouse();
+ if (sdl.desktop.fullscreen) GFX_SwitchFullScreen();
+}
+
+
+static void SetPriority(PRIORITY_LEVELS level) {
+
+#if C_SET_PRIORITY
+// Do nothing if priorties are not the same and not root, else the highest
+// priority can not be set as users can only lower priority (not restore it)
+
+ if((sdl.priority.focus != sdl.priority.nofocus ) &&
+ (getuid()!=0) ) return;
+
+#endif
+ switch (level) {
+#ifdef WIN32
+ case PRIORITY_LEVEL_PAUSE: // if DOSBox is paused, assume idle priority
+ case PRIORITY_LEVEL_LOWEST:
+ SetPriorityClass(GetCurrentProcess(),IDLE_PRIORITY_CLASS);
+ break;
+ case PRIORITY_LEVEL_LOWER:
+ SetPriorityClass(GetCurrentProcess(),BELOW_NORMAL_PRIORITY_CLASS);
+ break;
+ case PRIORITY_LEVEL_NORMAL:
+ SetPriorityClass(GetCurrentProcess(),NORMAL_PRIORITY_CLASS);
+ break;
+ case PRIORITY_LEVEL_HIGHER:
+ SetPriorityClass(GetCurrentProcess(),ABOVE_NORMAL_PRIORITY_CLASS);
+ break;
+ case PRIORITY_LEVEL_HIGHEST:
+ SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS);
+ break;
+#elif C_SET_PRIORITY
+/* Linux use group as dosbox has mulitple threads under linux */
+ case PRIORITY_LEVEL_PAUSE: // if DOSBox is paused, assume idle priority
+ case PRIORITY_LEVEL_LOWEST:
+ setpriority (PRIO_PGRP, 0,PRIO_MAX);
+ break;
+ case PRIORITY_LEVEL_LOWER:
+ setpriority (PRIO_PGRP, 0,PRIO_MAX-(PRIO_TOTAL/3));
+ break;
+ case PRIORITY_LEVEL_NORMAL:
+ setpriority (PRIO_PGRP, 0,PRIO_MAX-(PRIO_TOTAL/2));
+ break;
+ case PRIORITY_LEVEL_HIGHER:
+ setpriority (PRIO_PGRP, 0,PRIO_MAX-((3*PRIO_TOTAL)/5) );
+ break;
+ case PRIORITY_LEVEL_HIGHEST:
+ setpriority (PRIO_PGRP, 0,PRIO_MAX-((3*PRIO_TOTAL)/4) );
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+extern Bit8u int10_font_14[256 * 14];
+static void OutputString(Bitu x,Bitu y,const char * text,Bit32u color,Bit32u color2,SDL_Surface * output_surface) {
+ Bit32u * draw=(Bit32u*)(((Bit8u *)output_surface->pixels)+((y)*output_surface->pitch))+x;
+ while (*text) {
+ Bit8u * font=&int10_font_14[(*text)*14];
+ Bitu i,j;
+ Bit32u * draw_line=draw;
+ for (i=0;i<14;i++) {
+ Bit8u map=*font++;
+ for (j=0;j<8;j++) {
+ if (map & 0x80) *((Bit32u*)(draw_line+j))=color; else *((Bit32u*)(draw_line+j))=color2;
+ map<<=1;
+ }
+ draw_line+=output_surface->pitch/4;
+ }
+ text++;
+ draw+=8;
+ }
+}
+
+#include "dosbox_splash.h"
+
+//extern void UI_Run(bool);
+void Restart(bool pressed);
+
+static void GUI_StartUp(Section * sec) {
+ sec->AddDestroyFunction(&GUI_ShutDown);
+ Section_prop * section=static_cast<Section_prop *>(sec);
+ sdl.active=false;
+ sdl.updating=false;
+
+ GFX_SetIcon();
+
+ sdl.desktop.lazy_fullscreen=false;
+ sdl.desktop.lazy_fullscreen_req=false;
+
+ sdl.desktop.fullscreen=section->Get_bool("fullscreen");
+ sdl.wait_on_error=section->Get_bool("waitonerror");
+
+ Prop_multival* p=section->Get_multival("priority");
+ std::string focus = p->GetSection()->Get_string("active");
+ std::string notfocus = p->GetSection()->Get_string("inactive");
+
+ if (focus == "lowest") { sdl.priority.focus = PRIORITY_LEVEL_LOWEST; }
+ else if (focus == "lower") { sdl.priority.focus = PRIORITY_LEVEL_LOWER; }
+ else if (focus == "normal") { sdl.priority.focus = PRIORITY_LEVEL_NORMAL; }
+ else if (focus == "higher") { sdl.priority.focus = PRIORITY_LEVEL_HIGHER; }
+ else if (focus == "highest") { sdl.priority.focus = PRIORITY_LEVEL_HIGHEST; }
+
+ if (notfocus == "lowest") { sdl.priority.nofocus=PRIORITY_LEVEL_LOWEST; }
+ else if (notfocus == "lower") { sdl.priority.nofocus=PRIORITY_LEVEL_LOWER; }
+ else if (notfocus == "normal") { sdl.priority.nofocus=PRIORITY_LEVEL_NORMAL; }
+ else if (notfocus == "higher") { sdl.priority.nofocus=PRIORITY_LEVEL_HIGHER; }
+ else if (notfocus == "highest") { sdl.priority.nofocus=PRIORITY_LEVEL_HIGHEST; }
+ else if (notfocus == "pause") {
+ /* we only check for pause here, because it makes no sense
+ * for DOSBox to be paused while it has focus
+ */
+ sdl.priority.nofocus=PRIORITY_LEVEL_PAUSE;
+ }
+
+ SetPriority(sdl.priority.focus); //Assume focus on startup
+ sdl.mouse.locked=false;
+ mouselocked=false; //Global for mapper
+ sdl.mouse.requestlock=false;
+ sdl.desktop.full.fixed=false;
+ const char* fullresolution=section->Get_string("fullresolution");
+ sdl.desktop.full.width = 0;
+ sdl.desktop.full.height = 0;
+ if(fullresolution && *fullresolution) {
+ char res[100];
+ safe_strncpy( res, fullresolution, sizeof( res ));
+ fullresolution = lowcase (res);//so x and X are allowed
+ if (strcmp(fullresolution,"original")) {
+ sdl.desktop.full.fixed = true;
+ if (strcmp(fullresolution,"desktop")) { //desktop = 0x0
+ char* height = const_cast<char*>(strchr(fullresolution,'x'));
+ if (height && * height) {
+ *height = 0;
+ sdl.desktop.full.height = (Bit16u)atoi(height+1);
+ sdl.desktop.full.width = (Bit16u)atoi(res);
+ }
+ }
+ }
+ }
+
+ sdl.desktop.window.width = 0;
+ sdl.desktop.window.height = 0;
+ const char* windowresolution=section->Get_string("windowresolution");
+ if(windowresolution && *windowresolution) {
+ char res[100];
+ safe_strncpy( res,windowresolution, sizeof( res ));
+ windowresolution = lowcase (res);//so x and X are allowed
+ if(strcmp(windowresolution,"original")) {
+ char* height = const_cast<char*>(strchr(windowresolution,'x'));
+ if(height && *height) {
+ *height = 0;
+ sdl.desktop.window.height = (Bit16u)atoi(height+1);
+ sdl.desktop.window.width = (Bit16u)atoi(res);
+ }
+ }
+ }
+ sdl.desktop.doublebuf=section->Get_bool("fulldouble");
+#if SDL_VERSION_ATLEAST(1, 2, 10)
+#ifdef WIN32
+ const SDL_VideoInfo* vidinfo = SDL_GetVideoInfo();
+ if (vidinfo) {
+ int sdl_w = vidinfo->current_w;
+ int sdl_h = vidinfo->current_h;
+ int win_w = GetSystemMetrics(SM_CXSCREEN);
+ int win_h = GetSystemMetrics(SM_CYSCREEN);
+ if (sdl_w != win_w && sdl_h != win_h)
+ LOG_MSG("Windows dpi/blurry apps scaling detected! The screen might be too large or not\n"
+ "show properly, please see the DOSBox options file (fullresolution) for details.\n");
+ }
+#else
+ if (!sdl.desktop.full.width || !sdl.desktop.full.height){
+ //Can only be done on the very first call! Not restartable.
+ //On windows don't use it as SDL returns the values without taking in account the dpi scaling
+ const SDL_VideoInfo* vidinfo = SDL_GetVideoInfo();
+ if (vidinfo) {
+ sdl.desktop.full.width = vidinfo->current_w;
+ sdl.desktop.full.height = vidinfo->current_h;
+ }
+ }
+#endif
+#endif
+
+ if (!sdl.desktop.full.width) {
+#ifdef WIN32
+ sdl.desktop.full.width=(Bit16u)GetSystemMetrics(SM_CXSCREEN);
+#else
+ LOG_MSG("Your fullscreen resolution can NOT be determined, it's assumed to be 1024x768.\nPlease edit the configuration file if this value is wrong.");
+ sdl.desktop.full.width=1024;
+#endif
+ }
+ if (!sdl.desktop.full.height) {
+#ifdef WIN32
+ sdl.desktop.full.height=(Bit16u)GetSystemMetrics(SM_CYSCREEN);
+#else
+ sdl.desktop.full.height=768;
+#endif
+ }
+ sdl.mouse.autoenable=section->Get_bool("autolock");
+ if (!sdl.mouse.autoenable) SDL_ShowCursor(SDL_DISABLE);
+ sdl.mouse.autolock=false;
+
+ Prop_multival* p3 = section->Get_multival("sensitivity");
+ sdl.mouse.xsensitivity = p3->GetSection()->Get_int("xsens");
+ sdl.mouse.ysensitivity = p3->GetSection()->Get_int("ysens");
+ std::string output=section->Get_string("output");
+
+ /* Setup Mouse correctly if fullscreen */
+ if(sdl.desktop.fullscreen) GFX_CaptureMouse();
+
+ if (output == "surface") {
+ sdl.desktop.want_type=SCREEN_SURFACE;
+#if C_DDRAW
+ } else if (output == "ddraw") {
+ sdl.desktop.want_type=SCREEN_SURFACE_DDRAW;
+#endif
+ } else if (output == "overlay") {
+ sdl.desktop.want_type=SCREEN_OVERLAY;
+#if C_OPENGL
+ } else if (output == "opengl") {
+ sdl.desktop.want_type=SCREEN_OPENGL;
+ sdl.opengl.bilinear=true;
+ } else if (output == "openglnb") {
+ sdl.desktop.want_type=SCREEN_OPENGL;
+ sdl.opengl.bilinear=false;
+#endif
+ } else {
+ LOG_MSG("SDL: Unsupported output device %s, switching back to surface",output.c_str());
+ sdl.desktop.want_type=SCREEN_SURFACE;//SHOULDN'T BE POSSIBLE anymore
+ }
+
+ sdl.overlay=0;
+#if C_OPENGL
+ if(sdl.desktop.want_type==SCREEN_OPENGL){ /* OPENGL is requested */
+ sdl.surface=SDL_SetVideoMode_Wrap(640,400,0,SDL_OPENGL);
+ if (sdl.surface == NULL) {
+ LOG_MSG("Could not initialize OpenGL, switching back to surface");
+ sdl.desktop.want_type=SCREEN_SURFACE;
+ } else {
+ sdl.opengl.buffer=0;
+ sdl.opengl.framebuf=0;
+ sdl.opengl.texture=0;
+ sdl.opengl.displaylist=0;
+ glGetIntegerv (GL_MAX_TEXTURE_SIZE, &sdl.opengl.max_texsize);
+ glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)SDL_GL_GetProcAddress("glGenBuffersARB");
+ glBindBufferARB = (PFNGLBINDBUFFERARBPROC)SDL_GL_GetProcAddress("glBindBufferARB");
+ glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)SDL_GL_GetProcAddress("glDeleteBuffersARB");
+ glBufferDataARB = (PFNGLBUFFERDATAARBPROC)SDL_GL_GetProcAddress("glBufferDataARB");
+ glMapBufferARB = (PFNGLMAPBUFFERARBPROC)SDL_GL_GetProcAddress("glMapBufferARB");
+ glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)SDL_GL_GetProcAddress("glUnmapBufferARB");
+ const char * gl_ext = (const char *)glGetString (GL_EXTENSIONS);
+ if(gl_ext && *gl_ext){
+ sdl.opengl.packed_pixel=(strstr(gl_ext,"EXT_packed_pixels") != NULL);
+ sdl.opengl.paletted_texture=(strstr(gl_ext,"EXT_paletted_texture") != NULL);
+ sdl.opengl.pixel_buffer_object=(strstr(gl_ext,"GL_ARB_pixel_buffer_object") != NULL ) &&
+ glGenBuffersARB && glBindBufferARB && glDeleteBuffersARB && glBufferDataARB &&
+ glMapBufferARB && glUnmapBufferARB;
+ } else {
+ sdl.opengl.packed_pixel=sdl.opengl.paletted_texture=false;
+ }
+ }
+ } /* OPENGL is requested end */
+
+#endif //OPENGL
+ /* Initialize screen for first time */
+ sdl.surface=SDL_SetVideoMode_Wrap(640,400,0,0);
+ if (sdl.surface == NULL) E_Exit("Could not initialize video: %s",SDL_GetError());
+ sdl.desktop.bpp=sdl.surface->format->BitsPerPixel;
+ if (sdl.desktop.bpp==24) {
+ LOG_MSG("SDL: You are running in 24 bpp mode, this will slow down things!");
+ }
+ GFX_Stop();
+ SDL_WM_SetCaption("DOSBox",VERSION);
+
+/* The endian part is intentionally disabled as somehow it produces correct results without according to rhoenie*/
+//#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+// Bit32u rmask = 0xff000000;
+// Bit32u gmask = 0x00ff0000;
+// Bit32u bmask = 0x0000ff00;
+//#else
+ Bit32u rmask = 0x000000ff;
+ Bit32u gmask = 0x0000ff00;
+ Bit32u bmask = 0x00ff0000;
+//#endif
+
+/* Please leave the Splash screen stuff in working order in DOSBox. We spend a lot of time making DOSBox. */
+ SDL_Surface* splash_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 400, 32, rmask, gmask, bmask, 0);
+ if (splash_surf) {
+ SDL_FillRect(splash_surf, NULL, SDL_MapRGB(splash_surf->format, 0, 0, 0));
+
+ Bit8u* tmpbufp = new Bit8u[640*400*3];
+ GIMP_IMAGE_RUN_LENGTH_DECODE(tmpbufp,gimp_image.rle_pixel_data,640*400,3);
+ for (Bitu y=0; y<400; y++) {
+
+ Bit8u* tmpbuf = tmpbufp + y*640*3;
+ Bit32u * draw=(Bit32u*)(((Bit8u *)splash_surf->pixels)+((y)*splash_surf->pitch));
+ for (Bitu x=0; x<640; x++) {
+//#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+// *draw++ = tmpbuf[x*3+2]+tmpbuf[x*3+1]*0x100+tmpbuf[x*3+0]*0x10000+0x00000000;
+//#else
+ *draw++ = tmpbuf[x*3+0]+tmpbuf[x*3+1]*0x100+tmpbuf[x*3+2]*0x10000+0x00000000;
+//#endif
+ }
+ }
+
+ bool exit_splash = false;
+
+ static Bitu max_splash_loop = 600;
+ static Bitu splash_fade = 100;
+ static bool use_fadeout = true;
+
+ for (Bit32u ct = 0,startticks = GetTicks();ct < max_splash_loop;ct = GetTicks()-startticks) {
+ SDL_Event evt;
+ while (SDL_PollEvent(&evt)) {
+ if (evt.type == SDL_QUIT) {
+ exit_splash = true;
+ break;
+ }
+ }
+ if (exit_splash) break;
+
+ if (ct<1) {
+ SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0));
+ SDL_SetAlpha(splash_surf, SDL_SRCALPHA,255);
+ SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL);
+ SDL_Flip(sdl.surface);
+ } else if (ct>=max_splash_loop-splash_fade) {
+ if (use_fadeout) {
+ SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0));
+ SDL_SetAlpha(splash_surf, SDL_SRCALPHA, (Bit8u)((max_splash_loop-1-ct)*255/(splash_fade-1)));
+ SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL);
+ SDL_Flip(sdl.surface);
+ }
+ }
+
+ }
+
+ if (use_fadeout) {
+ SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0));
+ SDL_Flip(sdl.surface);
+ }
+ SDL_FreeSurface(splash_surf);
+ delete [] tmpbufp;
+
+ }
+
+ /* Get some Event handlers */
+ MAPPER_AddHandler(KillSwitch,MK_f9,MMOD1,"shutdown","ShutDown");
+ MAPPER_AddHandler(CaptureMouse,MK_f10,MMOD1,"capmouse","Cap Mouse");
+ MAPPER_AddHandler(SwitchFullScreen,MK_return,MMOD2,"fullscr","Fullscreen");
+ MAPPER_AddHandler(Restart,MK_home,MMOD1|MMOD2,"restart","Restart");
+#if C_DEBUG
+ /* Pause binds with activate-debugger */
+#else
+ MAPPER_AddHandler(&PauseDOSBox, MK_pause, MMOD2, "pause", "Pause DBox");
+#endif
+ /* Get Keyboard state of numlock and capslock */
+ SDLMod keystate = SDL_GetModState();
+ if(keystate&KMOD_NUM) startup_state_numlock = true;
+ if(keystate&KMOD_CAPS) startup_state_capslock = true;
+}
+
+void Mouse_AutoLock(bool enable) {
+ sdl.mouse.autolock=enable;
+ if (sdl.mouse.autoenable) sdl.mouse.requestlock=enable;
+ else {
+ SDL_ShowCursor(enable?SDL_DISABLE:SDL_ENABLE);
+ sdl.mouse.requestlock=false;
+ }
+}
+
+static void HandleMouseMotion(SDL_MouseMotionEvent * motion) {
+ if (sdl.mouse.locked || !sdl.mouse.autoenable)
+ Mouse_CursorMoved((float)motion->xrel*sdl.mouse.xsensitivity/100.0f,
+ (float)motion->yrel*sdl.mouse.ysensitivity/100.0f,
+ (float)(motion->x-sdl.clip.x)/(sdl.clip.w-1)*sdl.mouse.xsensitivity/100.0f,
+ (float)(motion->y-sdl.clip.y)/(sdl.clip.h-1)*sdl.mouse.ysensitivity/100.0f,
+ sdl.mouse.locked);
+}
+
+static void HandleMouseButton(SDL_MouseButtonEvent * button) {
+ switch (button->state) {
+ case SDL_PRESSED:
+ if (sdl.mouse.requestlock && !sdl.mouse.locked) {
+ GFX_CaptureMouse();
+ // Don't pass click to mouse handler
+ break;
+ }
+ if (!sdl.mouse.autoenable && sdl.mouse.autolock && button->button == SDL_BUTTON_MIDDLE) {
+ GFX_CaptureMouse();
+ break;
+ }
+ switch (button->button) {
+ case SDL_BUTTON_LEFT:
+ Mouse_ButtonPressed(0);
+ break;
+ case SDL_BUTTON_RIGHT:
+ Mouse_ButtonPressed(1);
+ break;
+ case SDL_BUTTON_MIDDLE:
+ Mouse_ButtonPressed(2);
+ break;
+ }
+ break;
+ case SDL_RELEASED:
+ switch (button->button) {
+ case SDL_BUTTON_LEFT:
+ Mouse_ButtonReleased(0);
+ break;
+ case SDL_BUTTON_RIGHT:
+ Mouse_ButtonReleased(1);
+ break;
+ case SDL_BUTTON_MIDDLE:
+ Mouse_ButtonReleased(2);
+ break;
+ }
+ break;
+ }
+}
+
+void GFX_LosingFocus(void) {
+ sdl.laltstate=SDL_KEYUP;
+ sdl.raltstate=SDL_KEYUP;
+ MAPPER_LosingFocus();
+}
+
+bool GFX_IsFullscreen(void) {
+ return sdl.desktop.fullscreen;
+}
+
+#if defined(MACOSX)
+#define DB_POLLSKIP 3
+#else
+//Not used yet, see comment below
+#define DB_POLLSKIP 1
+#endif
+
+#if defined(LINUX)
+#define SDL_XORG_FIX 1
+#else
+#define SDL_XORG_FIX 0
+#endif
+
+void GFX_Events() {
+ //Don't poll too often. This can be heavy on the OS, especially Macs.
+ //In idle mode 3000-4000 polls are done per second without this check.
+ //Macs, with this code, max 250 polls per second. (non-macs unused default max 500)
+ //Currently not implemented for all platforms, given the ALT-TAB stuff for WIN32.
+#if defined (MACOSX)
+ static int last_check = 0;
+ int current_check = GetTicks();
+ if (current_check - last_check <= DB_POLLSKIP) return;
+ last_check = current_check;
+#endif
+
+ SDL_Event event;
+#if defined (REDUCE_JOYSTICK_POLLING)
+ static int poll_delay = 0;
+ int time = GetTicks();
+ if (time - poll_delay > 20) {
+ poll_delay = time;
+ if (sdl.num_joysticks > 0) SDL_JoystickUpdate();
+ MAPPER_UpdateJoysticks();
+ }
+#endif
+ while (SDL_PollEvent(&event)) {
+#if SDL_XORG_FIX
+ // Special code for broken SDL with Xorg 1.20.1, where pairs of inputfocus gain and loss events are generated
+ // when locking the mouse in windowed mode.
+ if (event.type == SDL_ACTIVEEVENT && event.active.state == SDL_APPINPUTFOCUS && event.active.gain == 0) {
+ SDL_Event test; //Check if the next event would undo this one.
+ if (SDL_PeepEvents(&test,1,SDL_PEEKEVENT,SDL_ACTIVEEVENTMASK) == 1 && test.active.state == SDL_APPINPUTFOCUS && test.active.gain == 1) {
+ // Skip both events.
+ SDL_PeepEvents(&test,1,SDL_GETEVENT,SDL_ACTIVEEVENTMASK);
+ continue;
+ }
+ }
+#endif
+
+ switch (event.type) {
+ case SDL_ACTIVEEVENT:
+ if (event.active.state & SDL_APPINPUTFOCUS) {
+ if (event.active.gain) {
+#ifdef WIN32
+ if (!sdl.desktop.fullscreen) sdl.focus_ticks = GetTicks();
+#endif
+ if (sdl.desktop.fullscreen && !sdl.mouse.locked)
+ GFX_CaptureMouse();
+ SetPriority(sdl.priority.focus);
+ CPU_Disable_SkipAutoAdjust();
+ } else {
+ if (sdl.mouse.locked) {
+#ifdef WIN32
+ if (sdl.desktop.fullscreen) {
+ VGA_KillDrawing();
+ GFX_ForceFullscreenExit();
+ }
+#endif
+ GFX_CaptureMouse();
+ }
+ SetPriority(sdl.priority.nofocus);
+ GFX_LosingFocus();
+ CPU_Enable_SkipAutoAdjust();
+ }
+ }
+
+ /* Non-focus priority is set to pause; check to see if we've lost window or input focus
+ * i.e. has the window been minimised or made inactive?
+ */
+ if (sdl.priority.nofocus == PRIORITY_LEVEL_PAUSE) {
+ if ((event.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) && (!event.active.gain)) {
+ /* Window has lost focus, pause the emulator.
+ * This is similar to what PauseDOSBox() does, but the exit criteria is different.
+ * Instead of waiting for the user to hit Alt-Break, we wait for the window to
+ * regain window or input focus.
+ */
+ bool paused = true;
+ SDL_Event ev;
+
+ GFX_SetTitle(-1,-1,true);
+ KEYBOARD_ClrBuffer();
+// SDL_Delay(500);
+// while (SDL_PollEvent(&ev)) {
+ // flush event queue.
+// }
+
+ while (paused) {
+ // WaitEvent waits for an event rather than polling, so CPU usage drops to zero
+ SDL_WaitEvent(&ev);
+
+ switch (ev.type) {
+ case SDL_QUIT: throw(0); break; // a bit redundant at linux at least as the active events gets before the quit event.
+ case SDL_ACTIVEEVENT: // wait until we get window focus back
+ if (ev.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) {
+ // We've got focus back, so unpause and break out of the loop
+ if (ev.active.gain) {
+ paused = false;
+ GFX_SetTitle(-1,-1,false);
+ }
+
+ /* Now poke a "release ALT" command into the keyboard buffer
+ * we have to do this, otherwise ALT will 'stick' and cause
+ * problems with the app running in the DOSBox.
+ */
+ KEYBOARD_AddKey(KBD_leftalt, false);
+ KEYBOARD_AddKey(KBD_rightalt, false);
+ }
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case SDL_MOUSEMOTION:
+ HandleMouseMotion(&event.motion);
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ HandleMouseButton(&event.button);
+ break;
+ case SDL_VIDEORESIZE:
+// HandleVideoResize(&event.resize);
+ break;
+ case SDL_QUIT:
+ throw(0);
+ break;
+ case SDL_VIDEOEXPOSE:
+ if (sdl.draw.callback) sdl.draw.callback( GFX_CallBackRedraw );
+ break;
+#ifdef WIN32
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ // ignore event alt+tab
+ if (event.key.keysym.sym==SDLK_LALT) sdl.laltstate = event.key.type;
+ if (event.key.keysym.sym==SDLK_RALT) sdl.raltstate = event.key.type;
+ if (((event.key.keysym.sym==SDLK_TAB)) &&
+ ((sdl.laltstate==SDL_KEYDOWN) || (sdl.raltstate==SDL_KEYDOWN))) break;
+ // This can happen as well.
+ if (((event.key.keysym.sym == SDLK_TAB )) && (event.key.keysym.mod & KMOD_ALT)) break;
+ // ignore tab events that arrive just after regaining focus. (likely the result of alt-tab)
+ if ((event.key.keysym.sym == SDLK_TAB) && (GetTicks() - sdl.focus_ticks < 2)) break;
+#endif
+#if defined (MACOSX)
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ /* On macs CMD-Q is the default key to close an application */
+ if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) {
+ KillSwitch(true);
+ break;
+ }
+#endif
+ default:
+ void MAPPER_CheckEvent(SDL_Event * event);
+ MAPPER_CheckEvent(&event);
+ }
+ }
+}
+
+#if defined (WIN32)
+static BOOL WINAPI ConsoleEventHandler(DWORD event) {
+ switch (event) {
+ case CTRL_SHUTDOWN_EVENT:
+ case CTRL_LOGOFF_EVENT:
+ case CTRL_CLOSE_EVENT:
+ case CTRL_BREAK_EVENT:
+ raise(SIGTERM);
+ return TRUE;
+ case CTRL_C_EVENT:
+ default: //pass to the next handler
+ return FALSE;
+ }
+}
+#endif
+
+
+/* static variable to show wether there is not a valid stdout.
+ * Fixes some bugs when -noconsole is used in a read only directory */
+static bool no_stdout = false;
+void GFX_ShowMsg(char const* format,...) {
+ char buf[512];
+ va_list msg;
+ va_start(msg,format);
+ vsprintf(buf,format,msg);
+ strcat(buf,"\n");
+ va_end(msg);
+ if(!no_stdout) printf("%s",buf); //Else buf is parsed again.
+}
+
+
+void Config_Add_SDL() {
+ Section_prop * sdl_sec=control->AddSection_prop("sdl",&GUI_StartUp);
+ sdl_sec->AddInitFunction(&MAPPER_StartUp);
+ Prop_bool* Pbool;
+ Prop_string* Pstring;
+ Prop_int* Pint;
+ Prop_multival* Pmulti;
+
+ Pbool = sdl_sec->Add_bool("fullscreen",Property::Changeable::Always,false);
+ Pbool->Set_help("Start dosbox directly in fullscreen. (Press ALT-Enter to go back)");
+
+ Pbool = sdl_sec->Add_bool("fulldouble",Property::Changeable::Always,false);
+ Pbool->Set_help("Use double buffering in fullscreen. It can reduce screen flickering, but it can also result in a slow DOSBox.");
+
+ Pstring = sdl_sec->Add_string("fullresolution",Property::Changeable::Always,"original");
+ Pstring->Set_help("What resolution to use for fullscreen: original, desktop or a fixed size (e.g. 1024x768).\n"
+ "Using your monitor's native resolution with aspect=true might give the best results.\n"
+ "If you end up with small window on a large screen, try an output different from surface."
+ "On Windows 10 with display scaling (Scale and layout) set to a value above 100%, it is recommended\n"
+ "to use a lower full/windowresolution, in order to avoid window size problems.");
+
+ Pstring = sdl_sec->Add_string("windowresolution",Property::Changeable::Always,"original");
+ Pstring->Set_help("Scale the window to this size IF the output device supports hardware scaling.\n"
+ "(output=surface does not!)");
+
+ const char* outputs[] = {
+ "surface", "overlay",
+#if C_OPENGL
+ "opengl", "openglnb",
+#endif
+#if C_DDRAW
+ "ddraw",
+#endif
+ 0 };
+ Pstring = sdl_sec->Add_string("output",Property::Changeable::Always,"surface");
+ Pstring->Set_help("What video system to use for output.");
+ Pstring->Set_values(outputs);
+
+ Pbool = sdl_sec->Add_bool("autolock",Property::Changeable::Always,true);
+ Pbool->Set_help("Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock)");
+
+ Pmulti = sdl_sec->Add_multi("sensitivity",Property::Changeable::Always, ",");
+ Pmulti->Set_help("Mouse sensitivity. The optional second parameter specifies vertical sensitivity (e.g. 100,-50).");
+ Pmulti->SetValue("100");
+ Pint = Pmulti->GetSection()->Add_int("xsens",Property::Changeable::Always,100);
+ Pint->SetMinMax(-1000,1000);
+ Pint = Pmulti->GetSection()->Add_int("ysens",Property::Changeable::Always,100);
+ Pint->SetMinMax(-1000,1000);
+
+ Pbool = sdl_sec->Add_bool("waitonerror",Property::Changeable::Always, true);
+ Pbool->Set_help("Wait before closing the console if dosbox has an error.");
+
+ Pmulti = sdl_sec->Add_multi("priority", Property::Changeable::Always, ",");
+ Pmulti->SetValue("higher,normal");
+ Pmulti->Set_help("Priority levels for dosbox. Second entry behind the comma is for when dosbox is not focused/minimized.\n"
+ "pause is only valid for the second entry.");
+
+ const char* actt[] = { "lowest", "lower", "normal", "higher", "highest", "pause", 0};
+ Pstring = Pmulti->GetSection()->Add_string("active",Property::Changeable::Always,"higher");
+ Pstring->Set_values(actt);
+
+ const char* inactt[] = { "lowest", "lower", "normal", "higher", "highest", "pause", 0};
+ Pstring = Pmulti->GetSection()->Add_string("inactive",Property::Changeable::Always,"normal");
+ Pstring->Set_values(inactt);
+
+ Pstring = sdl_sec->Add_path("mapperfile",Property::Changeable::Always,MAPPERFILE);
+ Pstring->Set_help("File used to load/save the key/event mappings from. Resetmapper only works with the default value.");
+
+ Pbool = sdl_sec->Add_bool("usescancodes",Property::Changeable::Always,true);
+ Pbool->Set_help("Avoid usage of symkeys, might not work on all operating systems.");
+}
+
+static void show_warning(char const * const message) {
+ bool textonly = true;
+#ifdef WIN32
+ textonly = false;
+ if ( !sdl.inited && SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE) < 0 ) textonly = true;
+ sdl.inited = true;
+#endif
+ printf("%s",message);
+ if(textonly) return;
+ if(!sdl.surface) sdl.surface = SDL_SetVideoMode_Wrap(640,400,0,0);
+ if(!sdl.surface) return;
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ Bit32u rmask = 0xff000000;
+ Bit32u gmask = 0x00ff0000;
+ Bit32u bmask = 0x0000ff00;
+#else
+ Bit32u rmask = 0x000000ff;
+ Bit32u gmask = 0x0000ff00;
+ Bit32u bmask = 0x00ff0000;
+#endif
+ SDL_Surface* splash_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 400, 32, rmask, gmask, bmask, 0);
+ if (!splash_surf) return;
+
+ int x = 120,y = 20;
+ std::string m(message),m2;
+ std::string::size_type a,b,c,d;
+
+ while(m.size()) { //Max 50 characters. break on space before or on a newline
+ c = m.find('\n');
+ d = m.rfind(' ',50);
+ if(c>d) a=b=d; else a=b=c;
+ if( a != std::string::npos) b++;
+ m2 = m.substr(0,a); m.erase(0,b);
+ OutputString(x,y,m2.c_str(),0xffffffff,0,splash_surf);
+ y += 20;
+ }
+
+ SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL);
+ SDL_Flip(sdl.surface);
+ SDL_Delay(12000);
+}
+
+static void launcheditor() {
+ std::string path,file;
+ Cross::CreatePlatformConfigDir(path);
+ Cross::GetPlatformConfigName(file);
+ path += file;
+ FILE* f = fopen(path.c_str(),"r");
+ if(!f && !control->PrintConfig(path.c_str())) {
+ printf("tried creating %s. but failed.\n",path.c_str());
+ exit(1);
+ }
+ if(f) fclose(f);
+/* if(edit.empty()) {
+ printf("no editor specified.\n");
+ exit(1);
+ }*/
+ std::string edit;
+ while(control->cmdline->FindString("-editconf",edit,true)) //Loop until one succeeds
+ execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0);
+ //if you get here the launching failed!
+ printf("can't find editor(s) specified at the command line.\n");
+ exit(1);
+}
+#if C_DEBUG
+extern void DEBUG_ShutDown(Section * /*sec*/);
+#endif
+
+void restart_program(std::vector<std::string> & parameters) {
+ char** newargs = new char* [parameters.size()+1];
+ // parameter 0 is the executable path
+ // contents of the vector follow
+ // last one is NULL
+ for(Bitu i = 0; i < parameters.size(); i++) newargs[i]=(char*)parameters[i].c_str();
+ newargs[parameters.size()] = NULL;
+ SDL_CloseAudio();
+ SDL_Delay(50);
+ SDL_Quit();
+#if C_DEBUG
+ // shutdown curses
+ DEBUG_ShutDown(NULL);
+#endif
+
+ if(execvp(newargs[0], newargs) == -1) {
+#ifdef WIN32
+ if(newargs[0][0] == '\"') {
+ //everything specifies quotes around it if it contains a space, however my system disagrees
+ std::string edit = parameters[0];
+ edit.erase(0,1);edit.erase(edit.length() - 1,1);
+ //However keep the first argument of the passed argv (newargs) with quotes, as else repeated restarts go wrong.
+ if(execvp(edit.c_str(), newargs) == -1) E_Exit("Restarting failed");
+ }
+#endif
+ E_Exit("Restarting failed");
+ }
+ free(newargs);
+}
+void Restart(bool pressed) { // mapper handler
+ restart_program(control->startup_params);
+}
+
+static void launchcaptures(std::string const& edit) {
+ std::string path,file;
+ Section* t = control->GetSection("dosbox");
+ if(t) file = t->GetPropValue("captures");
+ if(!t || file == NO_SUCH_PROPERTY) {
+ printf("Config system messed up.\n");
+ exit(1);
+ }
+ Cross::CreatePlatformConfigDir(path);
+ path += file;
+ Cross::CreateDir(path);
+ struct stat cstat;
+ if(stat(path.c_str(),&cstat) || (cstat.st_mode & S_IFDIR) == 0) {
+ printf("%s doesn't exists or isn't a directory.\n",path.c_str());
+ exit(1);
+ }
+/* if(edit.empty()) {
+ printf("no editor specified.\n");
+ exit(1);
+ }*/
+
+ execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0);
+ //if you get here the launching failed!
+ printf("can't find filemanager %s\n",edit.c_str());
+ exit(1);
+}
+
+static void printconfiglocation() {
+ std::string path,file;
+ Cross::CreatePlatformConfigDir(path);
+ Cross::GetPlatformConfigName(file);
+ path += file;
+
+ FILE* f = fopen(path.c_str(),"r");
+ if(!f && !control->PrintConfig(path.c_str())) {
+ printf("tried creating %s. but failed",path.c_str());
+ exit(1);
+ }
+ if(f) fclose(f);
+ printf("%s\n",path.c_str());
+ exit(0);
+}
+
+static void eraseconfigfile() {
+ FILE* f = fopen("dosbox.conf","r");
+ if(f) {
+ fclose(f);
+ show_warning("Warning: dosbox.conf exists in current working directory.\nThis will override the configuration file at runtime.\n");
+ }
+ std::string path,file;
+ Cross::GetPlatformConfigDir(path);
+ Cross::GetPlatformConfigName(file);
+ path += file;
+ f = fopen(path.c_str(),"r");
+ if(!f) exit(0);
+ fclose(f);
+ unlink(path.c_str());
+ exit(0);
+}
+
+static void erasemapperfile() {
+ FILE* g = fopen("dosbox.conf","r");
+ if(g) {
+ fclose(g);
+ show_warning("Warning: dosbox.conf exists in current working directory.\nKeymapping might not be properly reset.\n"
+ "Please reset configuration as well and delete the dosbox.conf.\n");
+ }
+
+ std::string path,file=MAPPERFILE;
+ Cross::GetPlatformConfigDir(path);
+ path += file;
+ FILE* f = fopen(path.c_str(),"r");
+ if(!f) exit(0);
+ fclose(f);
+ unlink(path.c_str());
+ exit(0);
+}
+
+
+//extern void UI_Init(void);
+int main(int argc, char* argv[]) {
+ try {
+ CommandLine com_line(argc,argv);
+ Config myconf(&com_line);
+ control=&myconf;
+ /* Init the configuration system and add default values */
+ Config_Add_SDL();
+ DOSBOX_Init();
+
+ std::string editor;
+ if(control->cmdline->FindString("-editconf",editor,false)) launcheditor();
+ if(control->cmdline->FindString("-opencaptures",editor,true)) launchcaptures(editor);
+ if(control->cmdline->FindExist("-eraseconf")) eraseconfigfile();
+ if(control->cmdline->FindExist("-resetconf")) eraseconfigfile();
+ if(control->cmdline->FindExist("-erasemapper")) erasemapperfile();
+ if(control->cmdline->FindExist("-resetmapper")) erasemapperfile();
+
+ /* Can't disable the console with debugger enabled */
+#if defined(WIN32) && !(C_DEBUG)
+ if (control->cmdline->FindExist("-noconsole")) {
+ FreeConsole();
+ /* Redirect standard input and standard output */
+ if(freopen(STDOUT_FILE, "w", stdout) == NULL)
+ no_stdout = true; // No stdout so don't write messages
+ freopen(STDERR_FILE, "w", stderr);
+ setvbuf(stdout, NULL, _IOLBF, BUFSIZ); /* Line buffered */
+ setbuf(stderr, NULL); /* No buffering */
+ } else {
+ if (AllocConsole()) {
+ fclose(stdin);
+ fclose(stdout);
+ fclose(stderr);
+ freopen("CONIN$","r",stdin);
+ freopen("CONOUT$","w",stdout);
+ freopen("CONOUT$","w",stderr);
+ }
+ SetConsoleTitle("DOSBox Status Window");
+ }
+#endif //defined(WIN32) && !(C_DEBUG)
+ if (control->cmdline->FindExist("-version") ||
+ control->cmdline->FindExist("--version") ) {
+ printf("\nDOSBox version %s, copyright 2002-2019 DOSBox Team.\n\n",VERSION);
+ printf("DOSBox is written by the DOSBox Team (See AUTHORS file))\n");
+ printf("DOSBox comes with ABSOLUTELY NO WARRANTY. This is free software,\n");
+ printf("and you are welcome to redistribute it under certain conditions;\n");
+ printf("please read the COPYING file thoroughly before doing so.\n\n");
+ return 0;
+ }
+ if(control->cmdline->FindExist("-printconf")) printconfiglocation();
+
+#if C_DEBUG
+ DEBUG_SetupConsole();
+#endif
+
+#if defined(WIN32)
+ SetConsoleCtrlHandler((PHANDLER_ROUTINE) ConsoleEventHandler,TRUE);
+#endif
+
+#ifdef OS2
+ PPIB pib;
+ PTIB tib;
+ DosGetInfoBlocks(&tib, &pib);
+ if (pib->pib_ultype == 2) pib->pib_ultype = 3;
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+#endif
+
+ /* Display Welcometext in the console */
+ LOG_MSG("DOSBox version %s",VERSION);
+ LOG_MSG("Copyright 2002-2019 DOSBox Team, published under GNU GPL.");
+ LOG_MSG("---");
+
+ /* Init SDL */
+#if SDL_VERSION_ATLEAST(1, 2, 14)
+ /* Or debian/ubuntu with older libsdl version as they have done this themselves, but then differently.
+ * with this variable they will work correctly. I've only tested the 1.2.14 behaviour against the windows version
+ * of libsdl
+ */
+ putenv(const_cast<char*>("SDL_DISABLE_LOCK_KEYS=1"));
+#endif
+ // Don't init timers, GetTicks seems to work fine and they can use a fair amount of power (Macs again)
+ // Please report problems with audio and other things.
+ if ( SDL_Init( SDL_INIT_AUDIO|SDL_INIT_VIDEO | /*SDL_INIT_TIMER |*/ SDL_INIT_CDROM
+ |SDL_INIT_NOPARACHUTE
+ ) < 0 ) E_Exit("Can't init SDL %s",SDL_GetError());
+ sdl.inited = true;
+
+#ifndef DISABLE_JOYSTICK
+ //Initialise Joystick separately. This way we can warn when it fails instead
+ //of exiting the application
+ if( SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0 ) LOG_MSG("Failed to init joystick support");
+#endif
+
+ sdl.laltstate = SDL_KEYUP;
+ sdl.raltstate = SDL_KEYUP;
+
+#if defined (WIN32)
+#if SDL_VERSION_ATLEAST(1, 2, 10)
+ sdl.using_windib=true;
+#else
+ sdl.using_windib=false;
+#endif
+ char sdl_drv_name[128];
+ if (getenv("SDL_VIDEODRIVER")==NULL) {
+ if (SDL_VideoDriverName(sdl_drv_name,128)!=NULL) {
+ sdl.using_windib=false;
+ if (strcmp(sdl_drv_name,"directx")!=0) {
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ putenv("SDL_VIDEODRIVER=directx");
+ if (SDL_InitSubSystem(SDL_INIT_VIDEO)<0) {
+ putenv("SDL_VIDEODRIVER=windib");
+ if (SDL_InitSubSystem(SDL_INIT_VIDEO)<0) E_Exit("Can't init SDL Video %s",SDL_GetError());
+ sdl.using_windib=true;
+ }
+ }
+ }
+ } else {
+ char* sdl_videodrv = getenv("SDL_VIDEODRIVER");
+ if (strcmp(sdl_videodrv,"directx")==0) sdl.using_windib = false;
+ else if (strcmp(sdl_videodrv,"windib")==0) sdl.using_windib = true;
+ }
+ if (SDL_VideoDriverName(sdl_drv_name,128)!=NULL) {
+ if (strcmp(sdl_drv_name,"windib")==0) LOG_MSG("SDL_Init: Starting up with SDL windib video driver.\n Try to update your video card and directx drivers!");
+ }
+#endif
+ sdl.num_joysticks=SDL_NumJoysticks();
+
+ /* Parse configuration files */
+ std::string config_file, config_path, config_combined;
+ Cross::GetPlatformConfigDir(config_path);
+
+ //First parse -userconf
+ if(control->cmdline->FindExist("-userconf",true)){
+ config_file.clear();
+ Cross::GetPlatformConfigDir(config_path);
+ Cross::GetPlatformConfigName(config_file);
+ config_combined = config_path + config_file;
+ control->ParseConfigFile(config_combined.c_str());
+ if(!control->configfiles.size()) {
+ //Try to create the userlevel configfile.
+ config_file.clear();
+ Cross::CreatePlatformConfigDir(config_path);
+ Cross::GetPlatformConfigName(config_file);
+ config_combined = config_path + config_file;
+ if(control->PrintConfig(config_combined.c_str())) {
+ LOG_MSG("CONFIG: Generating default configuration.\nWriting it to %s",config_combined.c_str());
+ //Load them as well. Makes relative paths much easier
+ control->ParseConfigFile(config_combined.c_str());
+ }
+ }
+ }
+
+ //Second parse -conf switches
+ while(control->cmdline->FindString("-conf",config_file,true)) {
+ if (!control->ParseConfigFile(config_file.c_str())) {
+ // try to load it from the user directory
+ if (!control->ParseConfigFile((config_path + config_file).c_str())) {
+ LOG_MSG("CONFIG: Can't open specified config file: %s",config_file.c_str());
+ }
+ }
+ }
+ // if none found => parse localdir conf
+ if(!control->configfiles.size()) control->ParseConfigFile("dosbox.conf");
+
+ // if none found => parse userlevel conf
+ if(!control->configfiles.size()) {
+ config_file.clear();
+ Cross::GetPlatformConfigName(config_file);
+ control->ParseConfigFile((config_path + config_file).c_str());
+ }
+
+ if(!control->configfiles.size()) {
+ //Try to create the userlevel configfile.
+ config_file.clear();
+ Cross::CreatePlatformConfigDir(config_path);
+ Cross::GetPlatformConfigName(config_file);
+ config_combined = config_path + config_file;
+ if(control->PrintConfig(config_combined.c_str())) {
+ LOG_MSG("CONFIG: Generating default configuration.\nWriting it to %s",config_combined.c_str());
+ //Load them as well. Makes relative paths much easier
+ control->ParseConfigFile(config_combined.c_str());
+ } else {
+ LOG_MSG("CONFIG: Using default settings. Create a configfile to change them");
+ }
+ }
+
+
+#if (ENVIRON_LINKED)
+ control->ParseEnv(environ);
+#endif
+// UI_Init();
+// if (control->cmdline->FindExist("-startui")) UI_Run(false);
+ /* Init all the sections */
+ control->Init();
+ /* Some extra SDL Functions */
+ Section_prop * sdl_sec=static_cast<Section_prop *>(control->GetSection("sdl"));
+
+ if (control->cmdline->FindExist("-fullscreen") || sdl_sec->Get_bool("fullscreen")) {
+ if(!sdl.desktop.fullscreen) { //only switch if not already in fullscreen
+ GFX_SwitchFullScreen();
+ }
+ }
+
+ /* Init the keyMapper */
+ MAPPER_Init();
+ if (control->cmdline->FindExist("-startmapper")) MAPPER_RunInternal();
+ /* Start up main machine */
+ control->StartUp();
+ /* Shutdown everything */
+ } catch (char * error) {
+#if defined (WIN32)
+ sticky_keys(true);
+#endif
+ GFX_ShowMsg("Exit to error: %s",error);
+ fflush(NULL);
+ if(sdl.wait_on_error) {
+ //TODO Maybe look for some way to show message in linux?
+#if (C_DEBUG)
+ GFX_ShowMsg("Press enter to continue");
+ fflush(NULL);
+ fgetc(stdin);
+#elif defined(WIN32)
+ Sleep(5000);
+#endif
+ }
+
+ }
+ catch (int){
+ ; //nothing, pressed killswitch
+ }
+ catch(...){
+ ; // Unknown error, let's just exit.
+ }
+#if defined (WIN32)
+ sticky_keys(true); //Might not be needed if the shutdown function switches to windowed mode, but it doesn't hurt
+#endif
+ //Force visible mouse to end user. Somehow this sometimes doesn't happen
+ SDL_WM_GrabInput(SDL_GRAB_OFF);
+ SDL_ShowCursor(SDL_ENABLE);
+
+ SDL_Quit();//Let's hope sdl will quit as well when it catches an exception
+ return 0;
+}
+
+void GFX_GetSize(int &width, int &height, bool &fullscreen) {
+ width = sdl.draw.width;
+ height = sdl.draw.height;
+ fullscreen = sdl.desktop.fullscreen;
+}
diff --git a/src/hardware/Makefile.am b/src/hardware/Makefile.am
new file mode 100644
index 000000000..2d7394821
--- /dev/null
+++ b/src/hardware/Makefile.am
@@ -0,0 +1,15 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+SUBDIRS = serialport mame
+
+EXTRA_DIST = opl.cpp opl.h adlib.h dbopl.h pci_devices.h
+
+noinst_LIBRARIES = libhardware.a
+
+libhardware_a_SOURCES = adlib.cpp dma.cpp gameblaster.cpp hardware.cpp iohandler.cpp joystick.cpp keyboard.cpp \
+ memory.cpp mixer.cpp pcspeaker.cpp pci_bus.cpp pic.cpp sblaster.cpp tandy_sound.cpp timer.cpp \
+ vga.cpp vga_attr.cpp vga_crtc.cpp vga_dac.cpp vga_draw.cpp vga_gfx.cpp vga_other.cpp \
+ vga_memory.cpp vga_misc.cpp vga_seq.cpp vga_xga.cpp vga_s3.cpp vga_tseng.cpp vga_paradise.cpp \
+ cmos.cpp disney.cpp gus.cpp mpu401.cpp ipx.cpp ipxserver.cpp dbopl.cpp
+
+
diff --git a/src/hardware/adlib.cpp b/src/hardware/adlib.cpp
new file mode 100644
index 000000000..e0b082f9f
--- /dev/null
+++ b/src/hardware/adlib.cpp
@@ -0,0 +1,868 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <sys/types.h>
+#include "adlib.h"
+
+#include "setup.h"
+#include "mapper.h"
+#include "mem.h"
+#include "dbopl.h"
+
+#include "mame/emu.h"
+#include "mame/fmopl.h"
+#include "mame/ymf262.h"
+
+#define OPL2_INTERNAL_FREQ 3600000 // The OPL2 operates at 3.6MHz
+#define OPL3_INTERNAL_FREQ 14400000 // The OPL3 operates at 14.4MHz
+
+namespace OPL2 {
+ #include "opl.cpp"
+
+ struct Handler : public Adlib::Handler {
+ virtual void WriteReg( Bit32u reg, Bit8u val ) {
+ adlib_write(reg,val);
+ }
+ virtual Bit32u WriteAddr( Bit32u port, Bit8u val ) {
+ return val;
+ }
+
+ virtual void Generate( MixerChannel* chan, Bitu samples ) {
+ Bit16s buf[1024];
+ while( samples > 0 ) {
+ Bitu todo = samples > 1024 ? 1024 : samples;
+ samples -= todo;
+ adlib_getsample(buf, todo);
+ chan->AddSamples_m16( todo, buf );
+ }
+ }
+ virtual void Init( Bitu rate ) {
+ adlib_init(rate);
+ }
+ ~Handler() {
+ }
+ };
+}
+
+namespace OPL3 {
+ #define OPLTYPE_IS_OPL3
+ #include "opl.cpp"
+
+ struct Handler : public Adlib::Handler {
+ virtual void WriteReg( Bit32u reg, Bit8u val ) {
+ adlib_write(reg,val);
+ }
+ virtual Bit32u WriteAddr( Bit32u port, Bit8u val ) {
+ adlib_write_index(port, val);
+ return opl_index;
+ }
+ virtual void Generate( MixerChannel* chan, Bitu samples ) {
+ Bit16s buf[1024*2];
+ while( samples > 0 ) {
+ Bitu todo = samples > 1024 ? 1024 : samples;
+ samples -= todo;
+ adlib_getsample(buf, todo);
+ chan->AddSamples_s16( todo, buf );
+ }
+ }
+ virtual void Init( Bitu rate ) {
+ adlib_init(rate);
+ }
+ ~Handler() {
+ }
+ };
+}
+
+namespace MAMEOPL2 {
+
+struct Handler : public Adlib::Handler {
+ void* chip;
+
+ virtual void WriteReg(Bit32u reg, Bit8u val) {
+ ym3812_write(chip, 0, reg);
+ ym3812_write(chip, 1, val);
+ }
+ virtual Bit32u WriteAddr(Bit32u port, Bit8u val) {
+ return val;
+ }
+ virtual void Generate(MixerChannel* chan, Bitu samples) {
+ Bit16s buf[1024 * 2];
+ while (samples > 0) {
+ Bitu todo = samples > 1024 ? 1024 : samples;
+ samples -= todo;
+ ym3812_update_one(chip, buf, todo);
+ chan->AddSamples_m16(todo, buf);
+ }
+ }
+ virtual void Init(Bitu rate) {
+ chip = ym3812_init(0, OPL2_INTERNAL_FREQ, rate);
+ }
+ ~Handler() {
+ ym3812_shutdown(chip);
+ }
+};
+
+}
+
+
+namespace MAMEOPL3 {
+
+struct Handler : public Adlib::Handler {
+ void* chip;
+
+ virtual void WriteReg(Bit32u reg, Bit8u val) {
+ ymf262_write(chip, 0, reg);
+ ymf262_write(chip, 1, val);
+ }
+ virtual Bit32u WriteAddr(Bit32u port, Bit8u val) {
+ return val;
+ }
+ virtual void Generate(MixerChannel* chan, Bitu samples) {
+ //We generate data for 4 channels, but only the first 2 are connected on a pc
+ Bit16s buf[4][1024];
+ Bit16s result[1024][2];
+ Bit16s* buffers[4] = { buf[0], buf[1], buf[2], buf[3] };
+
+ while (samples > 0) {
+ Bitu todo = samples > 1024 ? 1024 : samples;
+ samples -= todo;
+ ymf262_update_one(chip, buffers, todo);
+ //Interleave the samples before mixing
+ for (Bitu i = 0; i < todo; i++) {
+ result[i][0] = buf[0][i];
+ result[i][1] = buf[1][i];
+ }
+ chan->AddSamples_s16(todo, result[0]);
+ }
+ }
+ virtual void Init(Bitu rate) {
+ chip = ymf262_init(0, OPL3_INTERNAL_FREQ, rate);
+ }
+ ~Handler() {
+ ymf262_shutdown(chip);
+ }
+};
+
+}
+
+
+
+#define RAW_SIZE 1024
+
+
+/*
+ Main Adlib implementation
+
+*/
+
+namespace Adlib {
+
+
+/* Raw DRO capture stuff */
+
+#ifdef _MSC_VER
+#pragma pack (1)
+#endif
+
+#define HW_OPL2 0
+#define HW_DUALOPL2 1
+#define HW_OPL3 2
+
+struct RawHeader {
+ Bit8u id[8]; /* 0x00, "DBRAWOPL" */
+ Bit16u versionHigh; /* 0x08, size of the data following the m */
+ Bit16u versionLow; /* 0x0a, size of the data following the m */
+ Bit32u commands; /* 0x0c, Bit32u amount of command/data pairs */
+ Bit32u milliseconds; /* 0x10, Bit32u Total milliseconds of data in this chunk */
+ Bit8u hardware; /* 0x14, Bit8u Hardware Type 0=opl2,1=dual-opl2,2=opl3 */
+ Bit8u format; /* 0x15, Bit8u Format 0=cmd/data interleaved, 1 maybe all cdms, followed by all data */
+ Bit8u compression; /* 0x16, Bit8u Compression Type, 0 = No Compression */
+ Bit8u delay256; /* 0x17, Bit8u Delay 1-256 msec command */
+ Bit8u delayShift8; /* 0x18, Bit8u (delay + 1)*256 */
+ Bit8u conversionTableSize; /* 0x191, Bit8u Raw Conversion Table size */
+} GCC_ATTRIBUTE(packed);
+#ifdef _MSC_VER
+#pragma pack()
+#endif
+/*
+ The Raw Tables is < 128 and is used to convert raw commands into a full register index
+ When the high bit of a raw command is set it indicates the cmd/data pair is to be sent to the 2nd port
+ After the conversion table the raw data follows immediatly till the end of the chunk
+*/
+
+//Table to map the opl register to one <127 for dro saving
+class Capture {
+ //127 entries to go from raw data to registers
+ Bit8u ToReg[127];
+ //How many entries in the ToPort are used
+ Bit8u RawUsed;
+ //256 entries to go from port index to raw data
+ Bit8u ToRaw[256];
+ Bit8u delay256;
+ Bit8u delayShift8;
+ RawHeader header;
+
+ FILE* handle; //File used for writing
+ Bit32u startTicks; //Start used to check total raw length on end
+ Bit32u lastTicks; //Last ticks when last last cmd was added
+ Bit8u buf[1024]; //16 added for delay commands and what not
+ Bit32u bufUsed;
+ Bit8u cmd[2]; //Last cmd's sent to either ports
+ bool doneOpl3;
+ bool doneDualOpl2;
+
+ RegisterCache* cache;
+
+ void MakeEntry( Bit8u reg, Bit8u& raw ) {
+ ToReg[ raw ] = reg;
+ ToRaw[ reg ] = raw;
+ raw++;
+ }
+ void MakeTables( void ) {
+ Bit8u index = 0;
+ memset( ToReg, 0xff, sizeof ( ToReg ) );
+ memset( ToRaw, 0xff, sizeof ( ToRaw ) );
+ //Select the entries that are valid and the index is the mapping to the index entry
+ MakeEntry( 0x01, index ); //0x01: Waveform select
+ MakeEntry( 0x04, index ); //104: Four-Operator Enable
+ MakeEntry( 0x05, index ); //105: OPL3 Mode Enable
+ MakeEntry( 0x08, index ); //08: CSW / NOTE-SEL
+ MakeEntry( 0xbd, index ); //BD: Tremolo Depth / Vibrato Depth / Percussion Mode / BD/SD/TT/CY/HH On
+ //Add the 32 byte range that hold the 18 operators
+ for ( int i = 0 ; i < 24; i++ ) {
+ if ( (i & 7) < 6 ) {
+ MakeEntry(0x20 + i, index ); //20-35: Tremolo / Vibrato / Sustain / KSR / Frequency Multiplication Facto
+ MakeEntry(0x40 + i, index ); //40-55: Key Scale Level / Output Level
+ MakeEntry(0x60 + i, index ); //60-75: Attack Rate / Decay Rate
+ MakeEntry(0x80 + i, index ); //80-95: Sustain Level / Release Rate
+ MakeEntry(0xe0 + i, index ); //E0-F5: Waveform Select
+ }
+ }
+ //Add the 9 byte range that hold the 9 channels
+ for ( int i = 0 ; i < 9; i++ ) {
+ MakeEntry(0xa0 + i, index ); //A0-A8: Frequency Number
+ MakeEntry(0xb0 + i, index ); //B0-B8: Key On / Block Number / F-Number(hi bits)
+ MakeEntry(0xc0 + i, index ); //C0-C8: FeedBack Modulation Factor / Synthesis Type
+ }
+ //Store the amount of bytes the table contains
+ RawUsed = index;
+// assert( RawUsed <= 127 );
+ delay256 = RawUsed;
+ delayShift8 = RawUsed+1;
+ }
+
+ void ClearBuf( void ) {
+ fwrite( buf, 1, bufUsed, handle );
+ header.commands += bufUsed / 2;
+ bufUsed = 0;
+ }
+ void AddBuf( Bit8u raw, Bit8u val ) {
+ buf[bufUsed++] = raw;
+ buf[bufUsed++] = val;
+ if ( bufUsed >= sizeof( buf ) ) {
+ ClearBuf();
+ }
+ }
+ void AddWrite( Bit32u regFull, Bit8u val ) {
+ Bit8u regMask = regFull & 0xff;
+ /*
+ Do some special checks if we're doing opl3 or dualopl2 commands
+ Although you could pretty much just stick to always doing opl3 on the player side
+ */
+ //Enabling opl3 4op modes will make us go into opl3 mode
+ if ( header.hardware != HW_OPL3 && regFull == 0x104 && val && (*cache)[0x105] ) {
+ header.hardware = HW_OPL3;
+ }
+ //Writing a keyon to a 2nd address enables dual opl2 otherwise
+ //Maybe also check for rhythm
+ if ( header.hardware == HW_OPL2 && regFull >= 0x1b0 && regFull <=0x1b8 && val ) {
+ header.hardware = HW_DUALOPL2;
+ }
+ Bit8u raw = ToRaw[ regMask ];
+ if ( raw == 0xff )
+ return;
+ if ( regFull & 0x100 )
+ raw |= 128;
+ AddBuf( raw, val );
+ }
+ void WriteCache( void ) {
+ Bitu i, val;
+ /* Check the registers to add */
+ for (i=0;i<256;i++) {
+ //Skip the note on entries
+ if (i>=0xb0 && i<=0xb8)
+ continue;
+ val = (*cache)[ i ];
+ if (val) {
+ AddWrite( i, val );
+ }
+ val = (*cache)[ 0x100 + i ];
+ if (val) {
+ AddWrite( 0x100 + i, val );
+ }
+ }
+ }
+ void InitHeader( void ) {
+ memset( &header, 0, sizeof( header ) );
+ memcpy( header.id, "DBRAWOPL", 8 );
+ header.versionLow = 0;
+ header.versionHigh = 2;
+ header.delay256 = delay256;
+ header.delayShift8 = delayShift8;
+ header.conversionTableSize = RawUsed;
+ }
+ void CloseFile( void ) {
+ if ( handle ) {
+ ClearBuf();
+ /* Endianize the header and write it to beginning of the file */
+ var_write( &header.versionHigh, header.versionHigh );
+ var_write( &header.versionLow, header.versionLow );
+ var_write( &header.commands, header.commands );
+ var_write( &header.milliseconds, header.milliseconds );
+ fseek( handle, 0, SEEK_SET );
+ fwrite( &header, 1, sizeof( header ), handle );
+ fclose( handle );
+ handle = 0;
+ }
+ }
+public:
+ bool DoWrite( Bit32u regFull, Bit8u val ) {
+ Bit8u regMask = regFull & 0xff;
+ //Check the raw index for this register if we actually have to save it
+ if ( handle ) {
+ /*
+ Check if we actually care for this to be logged, else just ignore it
+ */
+ Bit8u raw = ToRaw[ regMask ];
+ if ( raw == 0xff ) {
+ return true;
+ }
+ /* Check if this command will not just replace the same value
+ in a reg that doesn't do anything with it
+ */
+ if ( (*cache)[ regFull ] == val )
+ return true;
+ /* Check how much time has passed */
+ Bitu passed = PIC_Ticks - lastTicks;
+ lastTicks = PIC_Ticks;
+ header.milliseconds += passed;
+
+ //if ( passed > 0 ) LOG_MSG( "Delay %d", passed ) ;
+
+ // If we passed more than 30 seconds since the last command, we'll restart the the capture
+ if ( passed > 30000 ) {
+ CloseFile();
+ goto skipWrite;
+ }
+ while (passed > 0) {
+ if (passed < 257) { //1-256 millisecond delay
+ AddBuf( delay256, passed - 1 );
+ passed = 0;
+ } else {
+ Bitu shift = (passed >> 8);
+ passed -= shift << 8;
+ AddBuf( delayShift8, shift - 1 );
+ }
+ }
+ AddWrite( regFull, val );
+ return true;
+ }
+skipWrite:
+ //Not yet capturing to a file here
+ //Check for commands that would start capturing, if it's not one of them return
+ if ( !(
+ //note on in any channel
+ ( regMask>=0xb0 && regMask<=0xb8 && (val&0x020) ) ||
+ //Percussion mode enabled and a note on in any percussion instrument
+ ( regMask == 0xbd && ( (val&0x3f) > 0x20 ) )
+ )) {
+ return true;
+ }
+ handle = OpenCaptureFile("Raw Opl",".dro");
+ if (!handle)
+ return false;
+ InitHeader();
+ //Prepare space at start of the file for the header
+ fwrite( &header, 1, sizeof(header), handle );
+ /* write the Raw To Reg table */
+ fwrite( &ToReg, 1, RawUsed, handle );
+ /* Write the cache of last commands */
+ WriteCache( );
+ /* Write the command that triggered this */
+ AddWrite( regFull, val );
+ //Init the timing information for the next commands
+ lastTicks = PIC_Ticks;
+ startTicks = PIC_Ticks;
+ return true;
+ }
+ Capture( RegisterCache* _cache ) {
+ cache = _cache;
+ handle = 0;
+ bufUsed = 0;
+ MakeTables();
+ }
+ ~Capture() {
+ CloseFile();
+ }
+
+};
+
+/*
+Chip
+*/
+
+bool Chip::Write( Bit32u reg, Bit8u val ) {
+ switch ( reg ) {
+ case 0x02:
+ timer[0].counter = val;
+ return true;
+ case 0x03:
+ timer[1].counter = val;
+ return true;
+ case 0x04:
+ double time;
+ time = PIC_FullIndex();
+ if ( val & 0x80 ) {
+ timer[0].Reset( time );
+ timer[1].Reset( time );
+ } else {
+ timer[0].Update( time );
+ timer[1].Update( time );
+ if ( val & 0x1 ) {
+ timer[0].Start( time, 80 );
+ } else {
+ timer[0].Stop( );
+ }
+ timer[0].masked = (val & 0x40) > 0;
+ if ( timer[0].masked )
+ timer[0].overflow = false;
+ if ( val & 0x2 ) {
+ timer[1].Start( time, 320 );
+ } else {
+ timer[1].Stop( );
+ }
+ timer[1].masked = (val & 0x20) > 0;
+ if ( timer[1].masked )
+ timer[1].overflow = false;
+
+ }
+ return true;
+ }
+ return false;
+}
+
+
+Bit8u Chip::Read( ) {
+ double time( PIC_FullIndex() );
+ timer[0].Update( time );
+ timer[1].Update( time );
+ Bit8u ret = 0;
+ //Overflow won't be set if a channel is masked
+ if ( timer[0].overflow ) {
+ ret |= 0x40;
+ ret |= 0x80;
+ }
+ if ( timer[1].overflow ) {
+ ret |= 0x20;
+ ret |= 0x80;
+ }
+ return ret;
+
+}
+
+void Module::CacheWrite( Bit32u reg, Bit8u val ) {
+ //capturing?
+ if ( capture ) {
+ capture->DoWrite( reg, val );
+ }
+ //Store it into the cache
+ cache[ reg ] = val;
+}
+
+void Module::DualWrite( Bit8u index, Bit8u reg, Bit8u val ) {
+ //Make sure you don't use opl3 features
+ //Don't allow write to disable opl3
+ if ( reg == 5 ) {
+ return;
+ }
+ //Only allow 4 waveforms
+ if ( reg >= 0xE0 ) {
+ val &= 3;
+ }
+ //Write to the timer?
+ if ( chip[index].Write( reg, val ) )
+ return;
+ //Enabling panning
+ if ( reg >= 0xc0 && reg <=0xc8 ) {
+ val &= 0x0f;
+ val |= index ? 0xA0 : 0x50;
+ }
+ Bit32u fullReg = reg + (index ? 0x100 : 0);
+ handler->WriteReg( fullReg, val );
+ CacheWrite( fullReg, val );
+}
+
+void Module::CtrlWrite( Bit8u val ) {
+ switch ( ctrl.index ) {
+ case 0x09: /* Left FM Volume */
+ ctrl.lvol = val;
+ goto setvol;
+ case 0x0a: /* Right FM Volume */
+ ctrl.rvol = val;
+setvol:
+ if ( ctrl.mixer ) {
+ //Dune cdrom uses 32 volume steps in an apparent mistake, should be 128
+ mixerChan->SetVolume( (float)(ctrl.lvol&0x1f)/31.0f, (float)(ctrl.rvol&0x1f)/31.0f );
+ }
+ break;
+ }
+}
+
+Bitu Module::CtrlRead( void ) {
+ switch ( ctrl.index ) {
+ case 0x00: /* Board Options */
+ return 0x70; //No options installed
+ case 0x09: /* Left FM Volume */
+ return ctrl.lvol;
+ case 0x0a: /* Right FM Volume */
+ return ctrl.rvol;
+ case 0x15: /* Audio Relocation */
+ return 0x388 >> 3; //Cryo installer detection
+ }
+ return 0xff;
+}
+
+
+void Module::PortWrite( Bitu port, Bitu val, Bitu iolen ) {
+ //Keep track of last write time
+ lastUsed = PIC_Ticks;
+ //Maybe only enable with a keyon?
+ if ( !mixerChan->enabled ) {
+ mixerChan->Enable(true);
+ }
+ if ( port&1 ) {
+ switch ( mode ) {
+ case MODE_OPL3GOLD:
+ if ( port == 0x38b ) {
+ if ( ctrl.active ) {
+ CtrlWrite( val );
+ break;
+ }
+ }
+ //Fall-through if not handled by control chip
+ case MODE_OPL2:
+ case MODE_OPL3:
+ if ( !chip[0].Write( reg.normal, val ) ) {
+ handler->WriteReg( reg.normal, val );
+ CacheWrite( reg.normal, val );
+ }
+ break;
+ case MODE_DUALOPL2:
+ //Not a 0x??8 port, then write to a specific port
+ if ( !(port & 0x8) ) {
+ Bit8u index = ( port & 2 ) >> 1;
+ DualWrite( index, reg.dual[index], val );
+ } else {
+ //Write to both ports
+ DualWrite( 0, reg.dual[0], val );
+ DualWrite( 1, reg.dual[1], val );
+ }
+ break;
+ }
+ } else {
+ //Ask the handler to write the address
+ //Make sure to clip them in the right range
+ switch ( mode ) {
+ case MODE_OPL2:
+ reg.normal = handler->WriteAddr( port, val ) & 0xff;
+ break;
+ case MODE_OPL3GOLD:
+ if ( port == 0x38a ) {
+ if ( val == 0xff ) {
+ ctrl.active = true;
+ break;
+ } else if ( val == 0xfe ) {
+ ctrl.active = false;
+ break;
+ } else if ( ctrl.active ) {
+ ctrl.index = val & 0xff;
+ break;
+ }
+ }
+ //Fall-through if not handled by control chip
+ case MODE_OPL3:
+ reg.normal = handler->WriteAddr( port, val ) & 0x1ff;
+ break;
+ case MODE_DUALOPL2:
+ //Not a 0x?88 port, when write to a specific side
+ if ( !(port & 0x8) ) {
+ Bit8u index = ( port & 2 ) >> 1;
+ reg.dual[index] = val & 0xff;
+ } else {
+ reg.dual[0] = val & 0xff;
+ reg.dual[1] = val & 0xff;
+ }
+ break;
+ }
+ }
+}
+
+
+Bitu Module::PortRead( Bitu port, Bitu iolen ) {
+ switch ( mode ) {
+ case MODE_OPL2:
+ //We allocated 4 ports, so just return -1 for the higher ones
+ if ( !(port & 3 ) ) {
+ //Make sure the low bits are 6 on opl2
+ return chip[0].Read() | 0x6;
+ } else {
+ return 0xff;
+ }
+ case MODE_OPL3GOLD:
+ if ( ctrl.active ) {
+ if ( port == 0x38a ) {
+ return 0; //Control status, not busy
+ } else if ( port == 0x38b ) {
+ return CtrlRead();
+ }
+ }
+ //Fall-through if not handled by control chip
+ case MODE_OPL3:
+ //We allocated 4 ports, so just return -1 for the higher ones
+ if ( !(port & 3 ) ) {
+ return chip[0].Read();
+ } else {
+ return 0xff;
+ }
+ case MODE_DUALOPL2:
+ //Only return for the lower ports
+ if ( port & 1 ) {
+ return 0xff;
+ }
+ //Make sure the low bits are 6 on opl2
+ return chip[ (port >> 1) & 1].Read() | 0x6;
+ }
+ return 0;
+}
+
+
+void Module::Init( Mode m ) {
+ mode = m;
+ switch ( mode ) {
+ case MODE_OPL3:
+ case MODE_OPL3GOLD:
+ case MODE_OPL2:
+ break;
+ case MODE_DUALOPL2:
+ //Setup opl3 mode in the hander
+ handler->WriteReg( 0x105, 1 );
+ //Also set it up in the cache so the capturing will start opl3
+ CacheWrite( 0x105, 1 );
+ break;
+ }
+}
+
+}; //namespace
+
+
+
+static Adlib::Module* module = 0;
+
+static void OPL_CallBack(Bitu len) {
+ module->handler->Generate( module->mixerChan, len );
+ //Disable the sound generation after 30 seconds of silence
+ if ((PIC_Ticks - module->lastUsed) > 30000) {
+ Bitu i;
+ for (i=0xb0;i<0xb9;i++) if (module->cache[i]&0x20||module->cache[i+0x100]&0x20) break;
+ if (i==0xb9) module->mixerChan->Enable(false);
+ else module->lastUsed = PIC_Ticks;
+ }
+}
+
+static Bitu OPL_Read(Bitu port,Bitu iolen) {
+ return module->PortRead( port, iolen );
+}
+
+void OPL_Write(Bitu port,Bitu val,Bitu iolen) {
+ module->PortWrite( port, val, iolen );
+}
+
+/*
+ Save the current state of the operators as instruments in an reality adlib tracker file
+*/
+static void SaveRad() {
+ char b[16 * 1024];
+ int w = 0;
+
+ FILE* handle = OpenCaptureFile("RAD Capture",".rad");
+ if ( !handle )
+ return;
+ //Header
+ fwrite( "RAD by REALiTY!!", 1, 16, handle );
+ b[w++] = 0x10; //version
+ b[w++] = 0x06; //default speed and no description
+ //Write 18 instuments for all operators in the cache
+ for ( int i = 0; i < 18; i++ ) {
+ Bit8u* set = module->cache + ( i / 9 ) * 256;
+ Bitu offset = ((i % 9) / 3) * 8 + (i % 3);
+ Bit8u* base = set + offset;
+ b[w++] = 1 + i; //instrument number
+ b[w++] = base[0x23];
+ b[w++] = base[0x20];
+ b[w++] = base[0x43];
+ b[w++] = base[0x40];
+ b[w++] = base[0x63];
+ b[w++] = base[0x60];
+ b[w++] = base[0x83];
+ b[w++] = base[0x80];
+ b[w++] = set[0xc0 + (i % 9)];
+ b[w++] = base[0xe3];
+ b[w++] = base[0xe0];
+ }
+ b[w++] = 0; //instrument 0, no more instruments following
+ b[w++] = 1; //1 pattern following
+ //Zero out the remaing part of the file a bit to make rad happy
+ for ( int i = 0; i < 64; i++ ) {
+ b[w++] = 0;
+ }
+ fwrite( b, 1, w, handle );
+ fclose( handle );
+};
+
+
+static void OPL_SaveRawEvent(bool pressed) {
+ if (!pressed)
+ return;
+// SaveRad();return;
+ /* Check for previously opened wave file */
+ if ( module->capture ) {
+ delete module->capture;
+ module->capture = 0;
+ LOG_MSG("Stopped Raw OPL capturing.");
+ } else {
+ LOG_MSG("Preparing to capture Raw OPL, will start with first note played.");
+ module->capture = new Adlib::Capture( &module->cache );
+ }
+}
+
+namespace Adlib {
+
+Module::Module( Section* configuration ) : Module_base(configuration) {
+ reg.dual[0] = 0;
+ reg.dual[1] = 0;
+ reg.normal = 0;
+ ctrl.active = false;
+ ctrl.index = 0;
+ ctrl.lvol = 0xff;
+ ctrl.rvol = 0xff;
+ handler = 0;
+ capture = 0;
+
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+ Bitu base = section->Get_hex("sbbase");
+ Bitu rate = section->Get_int("oplrate");
+ //Make sure we can't select lower than 8000 to prevent fixed point issues
+ if ( rate < 8000 )
+ rate = 8000;
+ std::string oplemu( section->Get_string( "oplemu" ) );
+ ctrl.mixer = section->Get_bool("sbmixer");
+
+ mixerChan = mixerObject.Install(OPL_CallBack,rate,"FM");
+ //Used to be 2.0, which was measured to be too high. Exact value depends on card/clone.
+ mixerChan->SetScale( 1.5f );
+
+ if (oplemu == "fast") {
+ handler = new DBOPL::Handler();
+ } else if (oplemu == "compat") {
+ if ( oplmode == OPL_opl2 ) {
+ handler = new OPL2::Handler();
+ } else {
+ handler = new OPL3::Handler();
+ }
+ }
+ else if (oplemu == "mame") {
+ if (oplmode == OPL_opl2) {
+ handler = new MAMEOPL2::Handler();
+ }
+ else {
+ handler = new MAMEOPL3::Handler();
+ }
+ } else {
+ handler = new DBOPL::Handler();
+ }
+ handler->Init( rate );
+ bool single = false;
+ switch ( oplmode ) {
+ case OPL_opl2:
+ single = true;
+ Init( Adlib::MODE_OPL2 );
+ break;
+ case OPL_dualopl2:
+ Init( Adlib::MODE_DUALOPL2 );
+ break;
+ case OPL_opl3:
+ Init( Adlib::MODE_OPL3 );
+ break;
+ case OPL_opl3gold:
+ Init( Adlib::MODE_OPL3GOLD );
+ break;
+ }
+ //0x388 range
+ WriteHandler[0].Install(0x388,OPL_Write,IO_MB, 4 );
+ ReadHandler[0].Install(0x388,OPL_Read,IO_MB, 4 );
+ //0x220 range
+ if ( !single ) {
+ WriteHandler[1].Install(base,OPL_Write,IO_MB, 4 );
+ ReadHandler[1].Install(base,OPL_Read,IO_MB, 4 );
+ }
+ //0x228 range
+ WriteHandler[2].Install(base+8,OPL_Write,IO_MB, 2);
+ ReadHandler[2].Install(base+8,OPL_Read,IO_MB, 1);
+
+ MAPPER_AddHandler(OPL_SaveRawEvent,MK_f7,MMOD1|MMOD2,"caprawopl","Cap OPL");
+}
+
+Module::~Module() {
+ if ( capture ) {
+ delete capture;
+ }
+ if ( handler ) {
+ delete handler;
+ }
+}
+
+//Initialize static members
+OPL_Mode Module::oplmode=OPL_none;
+
+}; //Adlib Namespace
+
+
+void OPL_Init(Section* sec,OPL_Mode oplmode) {
+ Adlib::Module::oplmode = oplmode;
+ module = new Adlib::Module( sec );
+}
+
+void OPL_ShutDown(Section* sec){
+ delete module;
+ module = 0;
+
+}
diff --git a/src/hardware/adlib.h b/src/hardware/adlib.h
new file mode 100644
index 000000000..e22d49ead
--- /dev/null
+++ b/src/hardware/adlib.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_ADLIB_H
+#define DOSBOX_ADLIB_H
+
+#include "dosbox.h"
+#include "mixer.h"
+#include "inout.h"
+#include "setup.h"
+#include "pic.h"
+#include "hardware.h"
+
+
+namespace Adlib {
+
+struct Timer {
+ double start;
+ double delay;
+ bool enabled, overflow, masked;
+ Bit8u counter;
+ Timer() {
+ masked = false;
+ overflow = false;
+ enabled = false;
+ counter = 0;
+ delay = 0;
+ }
+ //Call update before making any further changes
+ void Update( double time ) {
+ if ( !enabled || !delay )
+ return;
+ double deltaStart = time - start;
+ //Only set the overflow flag when not masked
+ if ( deltaStart >= 0 && !masked ) {
+ overflow = 1;
+ }
+ }
+ //On a reset make sure the start is in sync with the next cycle
+ void Reset(const double& time ) {
+ overflow = false;
+ if ( !delay || !enabled )
+ return;
+ double delta = (time - start);
+ double rem = fmod( delta, delay );
+ double next = delay - rem;
+ start = time + next;
+ }
+ void Stop( ) {
+ enabled = false;
+ }
+ void Start( const double& time, Bits scale ) {
+ //Don't enable again
+ if ( enabled ) {
+ return;
+ }
+ enabled = true;
+ delay = 0.001 * (256 - counter ) * scale;
+ start = time + delay;
+ }
+
+};
+
+struct Chip {
+ //Last selected register
+ Timer timer[2];
+ //Check for it being a write to the timer
+ bool Write( Bit32u addr, Bit8u val );
+ //Read the current timer state, will use current double
+ Bit8u Read( );
+};
+
+//The type of handler this is
+typedef enum {
+ MODE_OPL2,
+ MODE_DUALOPL2,
+ MODE_OPL3,
+ MODE_OPL3GOLD
+} Mode;
+
+class Handler {
+public:
+ //Write an address to a chip, returns the address the chip sets
+ virtual Bit32u WriteAddr( Bit32u port, Bit8u val ) = 0;
+ //Write to a specific register in the chip
+ virtual void WriteReg( Bit32u addr, Bit8u val ) = 0;
+ //Generate a certain amount of samples
+ virtual void Generate( MixerChannel* chan, Bitu samples ) = 0;
+ //Initialize at a specific sample rate and mode
+ virtual void Init( Bitu rate ) = 0;
+ virtual ~Handler() {
+ }
+};
+
+//The cache for 2 chips or an opl3
+typedef Bit8u RegisterCache[512];
+
+//Internal class used for dro capturing
+class Capture;
+
+class Module: public Module_base {
+ IO_ReadHandleObject ReadHandler[3];
+ IO_WriteHandleObject WriteHandler[3];
+ MixerObject mixerObject;
+
+ //Mode we're running in
+ Mode mode;
+ //Last selected address in the chip for the different modes
+ union {
+ Bit32u normal;
+ Bit8u dual[2];
+ } reg;
+ struct {
+ bool active;
+ Bit8u index;
+ Bit8u lvol;
+ Bit8u rvol;
+ bool mixer;
+ } ctrl;
+ void CacheWrite( Bit32u reg, Bit8u val );
+ void DualWrite( Bit8u index, Bit8u reg, Bit8u val );
+ void CtrlWrite( Bit8u val );
+ Bitu CtrlRead( void );
+public:
+ static OPL_Mode oplmode;
+ MixerChannel* mixerChan;
+ Bit32u lastUsed; //Ticks when adlib was last used to turn of mixing after a few second
+
+ Handler* handler; //Handler that will generate the sound
+ RegisterCache cache;
+ Capture* capture;
+ Chip chip[2];
+
+ //Handle port writes
+ void PortWrite( Bitu port, Bitu val, Bitu iolen );
+ Bitu PortRead( Bitu port, Bitu iolen );
+ void Init( Mode m );
+
+ Module( Section* configuration);
+ ~Module();
+};
+
+
+} //Adlib namespace
+
+#endif
diff --git a/src/hardware/cmos.cpp b/src/hardware/cmos.cpp
new file mode 100644
index 000000000..e909d382e
--- /dev/null
+++ b/src/hardware/cmos.cpp
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <time.h>
+#include <math.h>
+
+#include "dosbox.h"
+#include "timer.h"
+#include "pic.h"
+#include "inout.h"
+#include "mem.h"
+#include "bios_disk.h"
+#include "setup.h"
+#include "cross.h" //fmod on certain platforms
+
+static struct {
+ Bit8u regs[0x40];
+ bool nmi;
+ bool bcd;
+ Bit8u reg;
+ struct {
+ bool enabled;
+ Bit8u div;
+ float delay;
+ bool acknowledged;
+ } timer;
+ struct {
+ double timer;
+ double ended;
+ double alarm;
+ } last;
+ bool update_ended;
+} cmos;
+
+static void cmos_timerevent(Bitu val) {
+ if (cmos.timer.acknowledged) {
+ cmos.timer.acknowledged=false;
+ PIC_ActivateIRQ(8);
+ }
+ if (cmos.timer.enabled) {
+ PIC_AddEvent(cmos_timerevent,cmos.timer.delay);
+ cmos.regs[0xc] = 0xC0;//Contraption Zack (music)
+ }
+}
+
+static void cmos_checktimer(void) {
+ PIC_RemoveEvents(cmos_timerevent);
+ if (cmos.timer.div<=2) cmos.timer.div+=7;
+ cmos.timer.delay=(1000.0f/(32768.0f / (1 << (cmos.timer.div - 1))));
+ if (!cmos.timer.div || !cmos.timer.enabled) return;
+ LOG(LOG_PIT,LOG_NORMAL)("RTC Timer at %.2f hz",1000.0/cmos.timer.delay);
+// PIC_AddEvent(cmos_timerevent,cmos.timer.delay);
+ /* A rtc is always running */
+ double remd=fmod(PIC_FullIndex(),(double)cmos.timer.delay);
+ PIC_AddEvent(cmos_timerevent,(float)((double)cmos.timer.delay-remd)); //Should be more like a real pc. Check
+// status reg A reading with this (and with other delays actually)
+}
+
+void cmos_selreg(Bitu port,Bitu val,Bitu iolen) {
+ cmos.reg=val & 0x3f;
+ cmos.nmi=(val & 0x80)>0;
+}
+
+static void cmos_writereg(Bitu port,Bitu val,Bitu iolen) {
+ switch (cmos.reg) {
+ case 0x00: /* Seconds */
+ case 0x02: /* Minutes */
+ case 0x04: /* Hours */
+ case 0x06: /* Day of week */
+ case 0x07: /* Date of month */
+ case 0x08: /* Month */
+ case 0x09: /* Year */
+ case 0x32: /* Century */
+ /* Ignore writes to change alarm */
+ break;
+ case 0x01: /* Seconds Alarm */
+ case 0x03: /* Minutes Alarm */
+ case 0x05: /* Hours Alarm */
+ LOG(LOG_BIOS,LOG_NORMAL)("CMOS:Trying to set alarm");
+ cmos.regs[cmos.reg]=val;
+ break;
+ case 0x0a: /* Status reg A */
+ cmos.regs[cmos.reg]=val & 0x7f;
+ if ((val & 0x70)!=0x20) LOG(LOG_BIOS,LOG_ERROR)("CMOS Illegal 22 stage divider value");
+ cmos.timer.div=(val & 0xf);
+ cmos_checktimer();
+ break;
+ case 0x0b: /* Status reg B */
+ cmos.bcd=!(val & 0x4);
+ cmos.regs[cmos.reg]=val & 0x7f;
+ cmos.timer.enabled=(val & 0x40)>0;
+ if (val&0x10) LOG(LOG_BIOS,LOG_ERROR)("CMOS:Updated ended interrupt not supported yet");
+ cmos_checktimer();
+ break;
+ case 0x0d:/* Status reg D */
+ cmos.regs[cmos.reg]=val & 0x80; /*Bit 7=1:RTC Pown on*/
+ break;
+ case 0x0f: /* Shutdown status byte */
+ cmos.regs[cmos.reg]=val & 0x7f;
+ break;
+ default:
+ cmos.regs[cmos.reg]=val & 0x7f;
+ LOG(LOG_BIOS,LOG_ERROR)("CMOS:WRite to unhandled register %x",cmos.reg);
+ }
+}
+
+
+#define MAKE_RETURN(_VAL) (cmos.bcd ? ((((_VAL) / 10) << 4) | ((_VAL) % 10)) : (_VAL));
+
+static Bitu cmos_readreg(Bitu port,Bitu iolen) {
+ if (cmos.reg>0x3f) {
+ LOG(LOG_BIOS,LOG_ERROR)("CMOS:Read from illegal register %x",cmos.reg);
+ return 0xff;
+ }
+ Bitu drive_a, drive_b;
+ Bit8u hdparm;
+ time_t curtime;
+ struct tm *loctime;
+ /* Get the current time. */
+ curtime = time (NULL);
+
+ /* Convert it to local time representation. */
+ loctime = localtime (&curtime);
+
+ switch (cmos.reg) {
+ case 0x00: /* Seconds */
+ return MAKE_RETURN(loctime->tm_sec);
+ case 0x02: /* Minutes */
+ return MAKE_RETURN(loctime->tm_min);
+ case 0x04: /* Hours */
+ return MAKE_RETURN(loctime->tm_hour);
+ case 0x06: /* Day of week */
+ return MAKE_RETURN(loctime->tm_wday + 1);
+ case 0x07: /* Date of month */
+ return MAKE_RETURN(loctime->tm_mday);
+ case 0x08: /* Month */
+ return MAKE_RETURN(loctime->tm_mon + 1);
+ case 0x09: /* Year */
+ return MAKE_RETURN(loctime->tm_year % 100);
+ case 0x32: /* Century */
+ return MAKE_RETURN(loctime->tm_year / 100 + 19);
+ case 0x01: /* Seconds Alarm */
+ case 0x03: /* Minutes Alarm */
+ case 0x05: /* Hours Alarm */
+ return cmos.regs[cmos.reg];
+ case 0x0a: /* Status register A */
+ if (PIC_TickIndex()<0.002) {
+ return (cmos.regs[0x0a]&0x7f) | 0x80;
+ } else {
+ return (cmos.regs[0x0a]&0x7f);
+ }
+ case 0x0c: /* Status register C */
+ cmos.timer.acknowledged=true;
+ if (cmos.timer.enabled) {
+ /* In periodic interrupt mode only care for those flags */
+ Bit8u val=cmos.regs[0xc];
+ cmos.regs[0xc]=0;
+ return val;
+ } else {
+ /* Give correct values at certain times */
+ Bit8u val=0;
+ double index=PIC_FullIndex();
+ if (index>=(cmos.last.timer+cmos.timer.delay)) {
+ cmos.last.timer=index;
+ val|=0x40;
+ }
+ if (index>=(cmos.last.ended+1000)) {
+ cmos.last.ended=index;
+ val|=0x10;
+ }
+ return val;
+ }
+ case 0x10: /* Floppy size */
+ drive_a = 0;
+ drive_b = 0;
+ if(imageDiskList[0] != NULL) drive_a = imageDiskList[0]->GetBiosType();
+ if(imageDiskList[1] != NULL) drive_b = imageDiskList[1]->GetBiosType();
+ return ((drive_a << 4) | (drive_b));
+ /* First harddrive info */
+ case 0x12:
+ hdparm = 0;
+ if(imageDiskList[2] != NULL) hdparm |= 0xf;
+ if(imageDiskList[3] != NULL) hdparm |= 0xf0;
+ return hdparm;
+ case 0x19:
+ if(imageDiskList[2] != NULL) return 47; /* User defined type */
+ return 0;
+ case 0x1b:
+ if(imageDiskList[2] != NULL) return (imageDiskList[2]->cylinders & 0xff);
+ return 0;
+ case 0x1c:
+ if(imageDiskList[2] != NULL) return ((imageDiskList[2]->cylinders & 0xff00)>>8);
+ return 0;
+ case 0x1d:
+ if(imageDiskList[2] != NULL) return (imageDiskList[2]->heads);
+ return 0;
+ case 0x1e:
+ if(imageDiskList[2] != NULL) return 0xff;
+ return 0;
+ case 0x1f:
+ if(imageDiskList[2] != NULL) return 0xff;
+ return 0;
+ case 0x20:
+ if(imageDiskList[2] != NULL) return (0xc0 | (((imageDiskList[2]->heads) > 8) << 3));
+ return 0;
+ case 0x21:
+ if(imageDiskList[2] != NULL) return (imageDiskList[2]->cylinders & 0xff);
+ return 0;
+ case 0x22:
+ if(imageDiskList[2] != NULL) return ((imageDiskList[2]->cylinders & 0xff00)>>8);
+ return 0;
+ case 0x23:
+ if(imageDiskList[2] != NULL) return (imageDiskList[2]->sectors);
+ return 0;
+ /* Second harddrive info */
+ case 0x1a:
+ if(imageDiskList[3] != NULL) return 47; /* User defined type */
+ return 0;
+ case 0x24:
+ if(imageDiskList[3] != NULL) return (imageDiskList[3]->cylinders & 0xff);
+ return 0;
+ case 0x25:
+ if(imageDiskList[3] != NULL) return ((imageDiskList[3]->cylinders & 0xff00)>>8);
+ return 0;
+ case 0x26:
+ if(imageDiskList[3] != NULL) return (imageDiskList[3]->heads);
+ return 0;
+ case 0x27:
+ if(imageDiskList[3] != NULL) return 0xff;
+ return 0;
+ case 0x28:
+ if(imageDiskList[3] != NULL) return 0xff;
+ return 0;
+ case 0x29:
+ if(imageDiskList[3] != NULL) return (0xc0 | (((imageDiskList[3]->heads) > 8) << 3));
+ return 0;
+ case 0x2a:
+ if(imageDiskList[3] != NULL) return (imageDiskList[3]->cylinders & 0xff);
+ return 0;
+ case 0x2b:
+ if(imageDiskList[3] != NULL) return ((imageDiskList[3]->cylinders & 0xff00)>>8);
+ return 0;
+ case 0x2c:
+ if(imageDiskList[3] != NULL) return (imageDiskList[3]->sectors);
+ return 0;
+ case 0x39:
+ return 0;
+ case 0x3a:
+ return 0;
+
+
+ case 0x0b: /* Status register B */
+ case 0x0d: /* Status register D */
+ case 0x0f: /* Shutdown status byte */
+ case 0x14: /* Equipment */
+ case 0x15: /* Base Memory KB Low Byte */
+ case 0x16: /* Base Memory KB High Byte */
+ case 0x17: /* Extended memory in KB Low Byte */
+ case 0x18: /* Extended memory in KB High Byte */
+ case 0x30: /* Extended memory in KB Low Byte */
+ case 0x31: /* Extended memory in KB High Byte */
+// LOG(LOG_BIOS,LOG_NORMAL)("CMOS:Read from reg %X : %04X",cmos.reg,cmos.regs[cmos.reg]);
+ return cmos.regs[cmos.reg];
+ default:
+ LOG(LOG_BIOS,LOG_NORMAL)("CMOS:Read from reg %X",cmos.reg);
+ return cmos.regs[cmos.reg];
+ }
+}
+
+void CMOS_SetRegister(Bitu regNr, Bit8u val) {
+ cmos.regs[regNr] = val;
+}
+
+
+class CMOS:public Module_base{
+private:
+ IO_ReadHandleObject ReadHandler[2];
+ IO_WriteHandleObject WriteHandler[2];
+public:
+ CMOS(Section* configuration):Module_base(configuration){
+ WriteHandler[0].Install(0x70,cmos_selreg,IO_MB);
+ WriteHandler[1].Install(0x71,cmos_writereg,IO_MB);
+ ReadHandler[0].Install(0x71,cmos_readreg,IO_MB);
+ cmos.timer.enabled=false;
+ cmos.timer.acknowledged=true;
+ cmos.reg=0xa;
+ cmos_writereg(0x71,0x26,1);
+ cmos.reg=0xb;
+ cmos_writereg(0x71,0x2,1); //Struct tm *loctime is of 24 hour format,
+ cmos.reg=0xd;
+ cmos_writereg(0x71,0x80,1); /* RTC power on */
+ // Equipment is updated from bios.cpp and bios_disk.cpp
+ /* Fill in base memory size, it is 640K always */
+ cmos.regs[0x15]=(Bit8u)0x80;
+ cmos.regs[0x16]=(Bit8u)0x02;
+ /* Fill in extended memory size */
+ Bitu exsize=(MEM_TotalPages()*4)-1024;
+ cmos.regs[0x17]=(Bit8u)exsize;
+ cmos.regs[0x18]=(Bit8u)(exsize >> 8);
+ cmos.regs[0x30]=(Bit8u)exsize;
+ cmos.regs[0x31]=(Bit8u)(exsize >> 8);
+ }
+};
+
+static CMOS* test;
+
+void CMOS_Destroy(Section* sec){
+ delete test;
+}
+
+void CMOS_Init(Section* sec) {
+ test = new CMOS(sec);
+ sec->AddDestroyFunction(&CMOS_Destroy,true);
+}
diff --git a/src/hardware/dbopl.cpp b/src/hardware/dbopl.cpp
new file mode 100644
index 000000000..acbf2b8ff
--- /dev/null
+++ b/src/hardware/dbopl.cpp
@@ -0,0 +1,1521 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+/*
+ DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator.
+ Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2
+ Except for the table generation it's all integer math
+ Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms
+ The generation was based on the MAME implementation but tried to have it use less memory and be faster in general
+ MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times
+
+ //TODO Don't delay first operator 1 sample in opl3 mode
+ //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter
+ //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though?
+ //TODO Check if having the same accuracy in all frequency multipliers sounds better or not
+
+ //DUNNO Keyon in 4op, switch to 2op without keyoff.
+*/
+
+
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "dosbox.h"
+#include "dbopl.h"
+
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+namespace DBOPL {
+
+#define OPLRATE ((double)(14318180.0 / 288.0))
+#define TREMOLO_TABLE 52
+
+//Try to use most precision for frequencies
+//Else try to keep different waves in synch
+//#define WAVE_PRECISION 1
+#ifndef WAVE_PRECISION
+//Wave bits available in the top of the 32bit range
+//Original adlib uses 10.10, we use 10.22
+#define WAVE_BITS 10
+#else
+//Need some extra bits at the top to have room for octaves and frequency multiplier
+//We support to 8 times lower rate
+//128 * 15 * 8 = 15350, 2^13.9, so need 14 bits
+#define WAVE_BITS 14
+#endif
+#define WAVE_SH ( 32 - WAVE_BITS )
+#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 )
+
+//Use the same accuracy as the waves
+#define LFO_SH ( WAVE_SH - 10 )
+//LFO is controlled by our tremolo 256 sample limit
+#define LFO_MAX ( 256 << ( LFO_SH ) )
+
+
+//Maximum amount of attenuation bits
+//Envelope goes to 511, 9 bits
+#if (DBOPL_WAVE == WAVE_TABLEMUL )
+//Uses the value directly
+#define ENV_BITS ( 9 )
+#else
+//Add 3 bits here for more accuracy and would have to be shifted up either way
+#define ENV_BITS ( 9 )
+#endif
+//Limits of the envelope with those bits and when the envelope goes silent
+#define ENV_MIN 0
+#define ENV_EXTRA ( ENV_BITS - 9 )
+#define ENV_MAX ( 511 << ENV_EXTRA )
+#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) )
+#define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT )
+
+//Attack/decay/release rate counter shift
+#define RATE_SH 24
+#define RATE_MASK ( ( 1 << RATE_SH ) - 1 )
+//Has to fit within 16bit lookuptable
+#define MUL_SH 16
+
+//Check some ranges
+#if ENV_EXTRA > 3
+#error Too many envelope bits
+#endif
+
+
+//How much to substract from the base value for the final attenuation
+static const Bit8u KslCreateTable[16] = {
+ //0 will always be be lower than 7 * 8
+ 64, 32, 24, 19,
+ 16, 12, 11, 10,
+ 8, 6, 5, 4,
+ 3, 2, 1, 0,
+};
+
+#define M(_X_) ((Bit8u)( (_X_) * 2))
+static const Bit8u FreqCreateTable[16] = {
+ M(0.5), M(1 ), M(2 ), M(3 ), M(4 ), M(5 ), M(6 ), M(7 ),
+ M(8 ), M(9 ), M(10), M(10), M(12), M(12), M(15), M(15)
+};
+#undef M
+
+//We're not including the highest attack rate, that gets a special value
+static const Bit8u AttackSamplesTable[13] = {
+ 69, 55, 46, 40,
+ 35, 29, 23, 20,
+ 19, 15, 11, 10,
+ 9
+};
+//On a real opl these values take 8 samples to reach and are based upon larger tables
+static const Bit8u EnvelopeIncreaseTable[13] = {
+ 4, 5, 6, 7,
+ 8, 10, 12, 14,
+ 16, 20, 24, 28,
+ 32,
+};
+
+#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG )
+static Bit16u ExpTable[ 256 ];
+#endif
+
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+//PI table used by WAVEHANDLER
+static Bit16u SinTable[ 512 ];
+#endif
+
+#if ( DBOPL_WAVE > WAVE_HANDLER )
+//Layout of the waveform table in 512 entry intervals
+//With overlapping waves we reduce the table to half it's size
+
+// | |//\\|____|WAV7|//__|/\ |____|/\/\|
+// |\\//| | |WAV7| | \/| | |
+// |06 |0126|17 |7 |3 |4 |4 5 |5 |
+
+//6 is just 0 shifted and masked
+
+static Bit16s WaveTable[ 8 * 512 ];
+//Distance into WaveTable the wave starts
+static const Bit16u WaveBaseTable[8] = {
+ 0x000, 0x200, 0x200, 0x800,
+ 0xa00, 0xc00, 0x100, 0x400,
+
+};
+//Mask the counter with this
+static const Bit16u WaveMaskTable[8] = {
+ 1023, 1023, 511, 511,
+ 1023, 1023, 512, 1023,
+};
+
+//Where to start the counter on at keyon
+static const Bit16u WaveStartTable[8] = {
+ 512, 0, 0, 0,
+ 0, 512, 512, 256,
+};
+#endif
+
+#if ( DBOPL_WAVE == WAVE_TABLEMUL )
+static Bit16u MulTable[ 384 ];
+#endif
+
+static Bit8u KslTable[ 8 * 16 ];
+static Bit8u TremoloTable[ TREMOLO_TABLE ];
+//Start of a channel behind the chip struct start
+static Bit16u ChanOffsetTable[32];
+//Start of an operator behind the chip struct start
+static Bit16u OpOffsetTable[64];
+
+//The lower bits are the shift of the operator vibrato value
+//The highest bit is right shifted to generate -1 or 0 for negation
+//So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0
+static const Bit8s VibratoTable[ 8 ] = {
+ 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00,
+ 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80
+};
+
+//Shift strength for the ksl value determined by ksl strength
+static const Bit8u KslShiftTable[4] = {
+ 31,1,2,0
+};
+
+//Generate a table index and table shift value using input value from a selected rate
+static void EnvelopeSelect( Bit8u val, Bit8u& index, Bit8u& shift ) {
+ if ( val < 13 * 4 ) { //Rate 0 - 12
+ shift = 12 - ( val >> 2 );
+ index = val & 3;
+ } else if ( val < 15 * 4 ) { //rate 13 - 14
+ shift = 0;
+ index = val - 12 * 4;
+ } else { //rate 15 and up
+ shift = 0;
+ index = 12;
+ }
+}
+
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+/*
+ Generate the different waveforms out of the sine/exponetial table using handlers
+*/
+static inline Bits MakeVolume( Bitu wave, Bitu volume ) {
+ Bitu total = wave + volume;
+ Bitu index = total & 0xff;
+ Bitu sig = ExpTable[ index ];
+ Bitu exp = total >> 8;
+#if 0
+ //Check if we overflow the 31 shift limit
+ if ( exp >= 32 ) {
+ LOG_MSG( "WTF %d %d", total, exp );
+ }
+#endif
+ return (sig >> exp);
+};
+
+static Bits DB_FASTCALL WaveForm0( Bitu i, Bitu volume ) {
+ Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0
+ Bitu wave = SinTable[i & 511];
+ return (MakeVolume( wave, volume ) ^ neg) - neg;
+}
+static Bits DB_FASTCALL WaveForm1( Bitu i, Bitu volume ) {
+ Bit32u wave = SinTable[i & 511];
+ wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 );
+ return MakeVolume( wave, volume );
+}
+static Bits DB_FASTCALL WaveForm2( Bitu i, Bitu volume ) {
+ Bitu wave = SinTable[i & 511];
+ return MakeVolume( wave, volume );
+}
+static Bits DB_FASTCALL WaveForm3( Bitu i, Bitu volume ) {
+ Bitu wave = SinTable[i & 255];
+ wave |= ( ( (i ^ 256 ) & 256) - 1) >> ( 32 - 12 );
+ return MakeVolume( wave, volume );
+}
+static Bits DB_FASTCALL WaveForm4( Bitu i, Bitu volume ) {
+ //Twice as fast
+ i <<= 1;
+ Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0
+ Bitu wave = SinTable[i & 511];
+ wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 );
+ return (MakeVolume( wave, volume ) ^ neg) - neg;
+}
+static Bits DB_FASTCALL WaveForm5( Bitu i, Bitu volume ) {
+ //Twice as fast
+ i <<= 1;
+ Bitu wave = SinTable[i & 511];
+ wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 );
+ return MakeVolume( wave, volume );
+}
+static Bits DB_FASTCALL WaveForm6( Bitu i, Bitu volume ) {
+ Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0
+ return (MakeVolume( 0, volume ) ^ neg) - neg;
+}
+static Bits DB_FASTCALL WaveForm7( Bitu i, Bitu volume ) {
+ //Negative is reversed here
+ Bits neg = (( i >> 9) & 1) - 1;
+ Bitu wave = (i << 3);
+ //When negative the volume also runs backwards
+ wave = ((wave ^ neg) - neg) & 4095;
+ return (MakeVolume( wave, volume ) ^ neg) - neg;
+}
+
+static const WaveHandler WaveHandlerTable[8] = {
+ WaveForm0, WaveForm1, WaveForm2, WaveForm3,
+ WaveForm4, WaveForm5, WaveForm6, WaveForm7
+};
+
+#endif
+
+/*
+ Operator
+*/
+
+//We zero out when rate == 0
+inline void Operator::UpdateAttack( const Chip* chip ) {
+ Bit8u rate = reg60 >> 4;
+ if ( rate ) {
+ Bit8u val = (rate << 2) + ksr;
+ attackAdd = chip->attackRates[ val ];
+ rateZero &= ~(1 << ATTACK);
+ } else {
+ attackAdd = 0;
+ rateZero |= (1 << ATTACK);
+ }
+}
+inline void Operator::UpdateDecay( const Chip* chip ) {
+ Bit8u rate = reg60 & 0xf;
+ if ( rate ) {
+ Bit8u val = (rate << 2) + ksr;
+ decayAdd = chip->linearRates[ val ];
+ rateZero &= ~(1 << DECAY);
+ } else {
+ decayAdd = 0;
+ rateZero |= (1 << DECAY);
+ }
+}
+inline void Operator::UpdateRelease( const Chip* chip ) {
+ Bit8u rate = reg80 & 0xf;
+ if ( rate ) {
+ Bit8u val = (rate << 2) + ksr;
+ releaseAdd = chip->linearRates[ val ];
+ rateZero &= ~(1 << RELEASE);
+ if ( !(reg20 & MASK_SUSTAIN ) ) {
+ rateZero &= ~( 1 << SUSTAIN );
+ }
+ } else {
+ rateZero |= (1 << RELEASE);
+ releaseAdd = 0;
+ if ( !(reg20 & MASK_SUSTAIN ) ) {
+ rateZero |= ( 1 << SUSTAIN );
+ }
+ }
+}
+
+inline void Operator::UpdateAttenuation( ) {
+ Bit8u kslBase = (Bit8u)((chanData >> SHIFT_KSLBASE) & 0xff);
+ Bit32u tl = reg40 & 0x3f;
+ Bit8u kslShift = KslShiftTable[ reg40 >> 6 ];
+ //Make sure the attenuation goes to the right bits
+ totalLevel = tl << ( ENV_BITS - 7 ); //Total level goes 2 bits below max
+ totalLevel += ( kslBase << ENV_EXTRA ) >> kslShift;
+}
+
+void Operator::UpdateFrequency( ) {
+ Bit32u freq = chanData & (( 1 << 10 ) - 1);
+ Bit32u block = (chanData >> 10) & 0xff;
+#ifdef WAVE_PRECISION
+ block = 7 - block;
+ waveAdd = ( freq * freqMul ) >> block;
+#else
+ waveAdd = ( freq << block ) * freqMul;
+#endif
+ if ( reg20 & MASK_VIBRATO ) {
+ vibStrength = (Bit8u)(freq >> 7);
+
+#ifdef WAVE_PRECISION
+ vibrato = ( vibStrength * freqMul ) >> block;
+#else
+ vibrato = ( vibStrength << block ) * freqMul;
+#endif
+ } else {
+ vibStrength = 0;
+ vibrato = 0;
+ }
+}
+
+void Operator::UpdateRates( const Chip* chip ) {
+ //Mame seems to reverse this where enabling ksr actually lowers
+ //the rate, but pdf manuals says otherwise?
+ Bit8u newKsr = (Bit8u)((chanData >> SHIFT_KEYCODE) & 0xff);
+ if ( !( reg20 & MASK_KSR ) ) {
+ newKsr >>= 2;
+ }
+ if ( ksr == newKsr )
+ return;
+ ksr = newKsr;
+ UpdateAttack( chip );
+ UpdateDecay( chip );
+ UpdateRelease( chip );
+}
+
+INLINE Bit32s Operator::RateForward( Bit32u add ) {
+ rateIndex += add;
+ Bit32s ret = rateIndex >> RATE_SH;
+ rateIndex = rateIndex & RATE_MASK;
+ return ret;
+}
+
+template< Operator::State yes>
+Bits Operator::TemplateVolume( ) {
+ Bit32s vol = volume;
+ Bit32s change;
+ switch ( yes ) {
+ case OFF:
+ return ENV_MAX;
+ case ATTACK:
+ change = RateForward( attackAdd );
+ if ( !change )
+ return vol;
+ vol += ( (~vol) * change ) >> 3;
+ if ( vol < ENV_MIN ) {
+ volume = ENV_MIN;
+ rateIndex = 0;
+ SetState( DECAY );
+ return ENV_MIN;
+ }
+ break;
+ case DECAY:
+ vol += RateForward( decayAdd );
+ if ( GCC_UNLIKELY(vol >= sustainLevel) ) {
+ //Check if we didn't overshoot max attenuation, then just go off
+ if ( GCC_UNLIKELY(vol >= ENV_MAX) ) {
+ volume = ENV_MAX;
+ SetState( OFF );
+ return ENV_MAX;
+ }
+ //Continue as sustain
+ rateIndex = 0;
+ SetState( SUSTAIN );
+ }
+ break;
+ case SUSTAIN:
+ if ( reg20 & MASK_SUSTAIN ) {
+ return vol;
+ }
+ //In sustain phase, but not sustaining, do regular release
+ case RELEASE:
+ vol += RateForward( releaseAdd );;
+ if ( GCC_UNLIKELY(vol >= ENV_MAX) ) {
+ volume = ENV_MAX;
+ SetState( OFF );
+ return ENV_MAX;
+ }
+ break;
+ }
+ volume = vol;
+ return vol;
+}
+
+static const VolumeHandler VolumeHandlerTable[5] = {
+ &Operator::TemplateVolume< Operator::OFF >,
+ &Operator::TemplateVolume< Operator::RELEASE >,
+ &Operator::TemplateVolume< Operator::SUSTAIN >,
+ &Operator::TemplateVolume< Operator::DECAY >,
+ &Operator::TemplateVolume< Operator::ATTACK >
+};
+
+INLINE Bitu Operator::ForwardVolume() {
+ return currentLevel + (this->*volHandler)();
+}
+
+
+INLINE Bitu Operator::ForwardWave() {
+ waveIndex += waveCurrent;
+ return waveIndex >> WAVE_SH;
+}
+
+void Operator::Write20( const Chip* chip, Bit8u val ) {
+ Bit8u change = (reg20 ^ val );
+ if ( !change )
+ return;
+ reg20 = val;
+ //Shift the tremolo bit over the entire register, saved a branch, YES!
+ tremoloMask = (Bit8s)(val) >> 7;
+ tremoloMask &= ~(( 1 << ENV_EXTRA ) -1);
+ //Update specific features based on changes
+ if ( change & MASK_KSR ) {
+ UpdateRates( chip );
+ }
+ //With sustain enable the volume doesn't change
+ if ( reg20 & MASK_SUSTAIN || ( !releaseAdd ) ) {
+ rateZero |= ( 1 << SUSTAIN );
+ } else {
+ rateZero &= ~( 1 << SUSTAIN );
+ }
+ //Frequency multiplier or vibrato changed
+ if ( change & (0xf | MASK_VIBRATO) ) {
+ freqMul = chip->freqMul[ val & 0xf ];
+ UpdateFrequency();
+ }
+}
+
+void Operator::Write40( const Chip* /*chip*/, Bit8u val ) {
+ if (!(reg40 ^ val ))
+ return;
+ reg40 = val;
+ UpdateAttenuation( );
+}
+
+void Operator::Write60( const Chip* chip, Bit8u val ) {
+ Bit8u change = reg60 ^ val;
+ reg60 = val;
+ if ( change & 0x0f ) {
+ UpdateDecay( chip );
+ }
+ if ( change & 0xf0 ) {
+ UpdateAttack( chip );
+ }
+}
+
+void Operator::Write80( const Chip* chip, Bit8u val ) {
+ Bit8u change = (reg80 ^ val );
+ if ( !change )
+ return;
+ reg80 = val;
+ Bit8u sustain = val >> 4;
+ //Turn 0xf into 0x1f
+ sustain |= ( sustain + 1) & 0x10;
+ sustainLevel = sustain << ( ENV_BITS - 5 );
+ if ( change & 0x0f ) {
+ UpdateRelease( chip );
+ }
+}
+
+void Operator::WriteE0( const Chip* chip, Bit8u val ) {
+ if ( !(regE0 ^ val) )
+ return;
+ //in opl3 mode you can always selet 7 waveforms regardless of waveformselect
+ Bit8u waveForm = val & ( ( 0x3 & chip->waveFormMask ) | (0x7 & chip->opl3Active ) );
+ regE0 = val;
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+ waveHandler = WaveHandlerTable[ waveForm ];
+#else
+ waveBase = WaveTable + WaveBaseTable[ waveForm ];
+ waveStart = WaveStartTable[ waveForm ] << WAVE_SH;
+ waveMask = WaveMaskTable[ waveForm ];
+#endif
+}
+
+INLINE void Operator::SetState( Bit8u s ) {
+ state = s;
+ volHandler = VolumeHandlerTable[ s ];
+}
+
+INLINE bool Operator::Silent() const {
+ if ( !ENV_SILENT( totalLevel + volume ) )
+ return false;
+ if ( !(rateZero & ( 1 << state ) ) )
+ return false;
+ return true;
+}
+
+INLINE void Operator::Prepare( const Chip* chip ) {
+ currentLevel = totalLevel + (chip->tremoloValue & tremoloMask);
+ waveCurrent = waveAdd;
+ if ( vibStrength >> chip->vibratoShift ) {
+ Bit32s add = vibrato >> chip->vibratoShift;
+ //Sign extend over the shift value
+ Bit32s neg = chip->vibratoSign;
+ //Negate the add with -1 or 0
+ add = ( add ^ neg ) - neg;
+ waveCurrent += add;
+ }
+}
+
+void Operator::KeyOn( Bit8u mask ) {
+ if ( !keyOn ) {
+ //Restart the frequency generator
+#if ( DBOPL_WAVE > WAVE_HANDLER )
+ waveIndex = waveStart;
+#else
+ waveIndex = 0;
+#endif
+ rateIndex = 0;
+ SetState( ATTACK );
+ }
+ keyOn |= mask;
+}
+
+void Operator::KeyOff( Bit8u mask ) {
+ keyOn &= ~mask;
+ if ( !keyOn ) {
+ if ( state != OFF ) {
+ SetState( RELEASE );
+ }
+ }
+}
+
+INLINE Bits Operator::GetWave( Bitu index, Bitu vol ) {
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+ return waveHandler( index, vol << ( 3 - ENV_EXTRA ) );
+#elif ( DBOPL_WAVE == WAVE_TABLEMUL )
+ return (waveBase[ index & waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH;
+#elif ( DBOPL_WAVE == WAVE_TABLELOG )
+ Bit32s wave = waveBase[ index & waveMask ];
+ Bit32u total = ( wave & 0x7fff ) + vol << ( 3 - ENV_EXTRA );
+ Bit32s sig = ExpTable[ total & 0xff ];
+ Bit32u exp = total >> 8;
+ Bit32s neg = wave >> 16;
+ return ((sig ^ neg) - neg) >> exp;
+#else
+#error "No valid wave routine"
+#endif
+}
+
+Bits INLINE Operator::GetSample( Bits modulation ) {
+ Bitu vol = ForwardVolume();
+ if ( ENV_SILENT( vol ) ) {
+ //Simply forward the wave
+ waveIndex += waveCurrent;
+ return 0;
+ } else {
+ Bitu index = ForwardWave();
+ index += modulation;
+ return GetWave( index, vol );
+ }
+}
+
+Operator::Operator() {
+ chanData = 0;
+ freqMul = 0;
+ waveIndex = 0;
+ waveAdd = 0;
+ waveCurrent = 0;
+ keyOn = 0;
+ ksr = 0;
+ reg20 = 0;
+ reg40 = 0;
+ reg60 = 0;
+ reg80 = 0;
+ regE0 = 0;
+ SetState( OFF );
+ rateZero = (1 << OFF);
+ sustainLevel = ENV_MAX;
+ currentLevel = ENV_MAX;
+ totalLevel = ENV_MAX;
+ volume = ENV_MAX;
+ releaseAdd = 0;
+}
+
+/*
+ Channel
+*/
+
+Channel::Channel() {
+ old[0] = old[1] = 0;
+ chanData = 0;
+ regB0 = 0;
+ regC0 = 0;
+ maskLeft = -1;
+ maskRight = -1;
+ feedback = 31;
+ fourMask = 0;
+ synthHandler = &Channel::BlockTemplate< sm2FM >;
+};
+
+void Channel::SetChanData( const Chip* chip, Bit32u data ) {
+ Bit32u change = chanData ^ data;
+ chanData = data;
+ Op( 0 )->chanData = data;
+ Op( 1 )->chanData = data;
+ //Since a frequency update triggered this, always update frequency
+ Op( 0 )->UpdateFrequency();
+ Op( 1 )->UpdateFrequency();
+ if ( change & ( 0xff << SHIFT_KSLBASE ) ) {
+ Op( 0 )->UpdateAttenuation();
+ Op( 1 )->UpdateAttenuation();
+ }
+ if ( change & ( 0xff << SHIFT_KEYCODE ) ) {
+ Op( 0 )->UpdateRates( chip );
+ Op( 1 )->UpdateRates( chip );
+ }
+}
+
+void Channel::UpdateFrequency( const Chip* chip, Bit8u fourOp ) {
+ //Extrace the frequency bits
+ Bit32u data = chanData & 0xffff;
+ Bit32u kslBase = KslTable[ data >> 6 ];
+ Bit32u keyCode = ( data & 0x1c00) >> 9;
+ if ( chip->reg08 & 0x40 ) {
+ keyCode |= ( data & 0x100)>>8; /* notesel == 1 */
+ } else {
+ keyCode |= ( data & 0x200)>>9; /* notesel == 0 */
+ }
+ //Add the keycode and ksl into the highest bits of chanData
+ data |= (keyCode << SHIFT_KEYCODE) | ( kslBase << SHIFT_KSLBASE );
+ ( this + 0 )->SetChanData( chip, data );
+ if ( fourOp & 0x3f ) {
+ ( this + 1 )->SetChanData( chip, data );
+ }
+}
+
+void Channel::WriteA0( const Chip* chip, Bit8u val ) {
+ Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask;
+ //Don't handle writes to silent fourop channels
+ if ( fourOp > 0x80 )
+ return;
+ Bit32u change = (chanData ^ val ) & 0xff;
+ if ( change ) {
+ chanData ^= change;
+ UpdateFrequency( chip, fourOp );
+ }
+}
+
+void Channel::WriteB0( const Chip* chip, Bit8u val ) {
+ Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask;
+ //Don't handle writes to silent fourop channels
+ if ( fourOp > 0x80 )
+ return;
+ Bitu change = (chanData ^ ( val << 8 ) ) & 0x1f00;
+ if ( change ) {
+ chanData ^= change;
+ UpdateFrequency( chip, fourOp );
+ }
+ //Check for a change in the keyon/off state
+ if ( !(( val ^ regB0) & 0x20))
+ return;
+ regB0 = val;
+ if ( val & 0x20 ) {
+ Op(0)->KeyOn( 0x1 );
+ Op(1)->KeyOn( 0x1 );
+ if ( fourOp & 0x3f ) {
+ ( this + 1 )->Op(0)->KeyOn( 1 );
+ ( this + 1 )->Op(1)->KeyOn( 1 );
+ }
+ } else {
+ Op(0)->KeyOff( 0x1 );
+ Op(1)->KeyOff( 0x1 );
+ if ( fourOp & 0x3f ) {
+ ( this + 1 )->Op(0)->KeyOff( 1 );
+ ( this + 1 )->Op(1)->KeyOff( 1 );
+ }
+ }
+}
+
+void Channel::WriteC0(const Chip* chip, Bit8u val) {
+ Bit8u change = val ^ regC0;
+ if (!change)
+ return;
+ regC0 = val;
+ feedback = (regC0 >> 1) & 7;
+ if (feedback) {
+ //We shift the input to the right 10 bit wave index value
+ feedback = 9 - feedback;
+ }
+ else {
+ feedback = 31;
+ }
+ UpdateSynth(chip);
+}
+
+void Channel::UpdateSynth( const Chip* chip ) {
+ //Select the new synth mode
+ if ( chip->opl3Active ) {
+ //4-op mode enabled for this channel
+ if ( (chip->reg104 & fourMask) & 0x3f ) {
+ Channel* chan0, *chan1;
+ //Check if it's the 2nd channel in a 4-op
+ if ( !(fourMask & 0x80 ) ) {
+ chan0 = this;
+ chan1 = this + 1;
+ } else {
+ chan0 = this - 1;
+ chan1 = this;
+ }
+
+ Bit8u synth = ( (chan0->regC0 & 1) << 0 )| (( chan1->regC0 & 1) << 1 );
+ switch ( synth ) {
+ case 0:
+ chan0->synthHandler = &Channel::BlockTemplate< sm3FMFM >;
+ break;
+ case 1:
+ chan0->synthHandler = &Channel::BlockTemplate< sm3AMFM >;
+ break;
+ case 2:
+ chan0->synthHandler = &Channel::BlockTemplate< sm3FMAM >;
+ break;
+ case 3:
+ chan0->synthHandler = &Channel::BlockTemplate< sm3AMAM >;
+ break;
+ }
+ //Disable updating percussion channels
+ } else if ((fourMask & 0x40) && ( chip->regBD & 0x20) ) {
+
+ //Regular dual op, am or fm
+ } else if (regC0 & 1 ) {
+ synthHandler = &Channel::BlockTemplate< sm3AM >;
+ } else {
+ synthHandler = &Channel::BlockTemplate< sm3FM >;
+ }
+ maskLeft = (regC0 & 0x10 ) ? -1 : 0;
+ maskRight = (regC0 & 0x20 ) ? -1 : 0;
+ //opl2 active
+ } else {
+ //Disable updating percussion channels
+ if ( (fourMask & 0x40) && ( chip->regBD & 0x20 ) ) {
+
+ //Regular dual op, am or fm
+ } else if (regC0 & 1 ) {
+ synthHandler = &Channel::BlockTemplate< sm2AM >;
+ } else {
+ synthHandler = &Channel::BlockTemplate< sm2FM >;
+ }
+ }
+}
+
+template< bool opl3Mode>
+INLINE void Channel::GeneratePercussion( Chip* chip, Bit32s* output ) {
+ Channel* chan = this;
+
+ //BassDrum
+ Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback;
+ old[0] = old[1];
+ old[1] = Op(0)->GetSample( mod );
+
+ //When bassdrum is in AM mode first operator is ignoed
+ if ( chan->regC0 & 1 ) {
+ mod = 0;
+ } else {
+ mod = old[0];
+ }
+ Bit32s sample = Op(1)->GetSample( mod );
+
+
+ //Precalculate stuff used by other outputs
+ Bit32u noiseBit = chip->ForwardNoise() & 0x1;
+ Bit32u c2 = Op(2)->ForwardWave();
+ Bit32u c5 = Op(5)->ForwardWave();
+ Bit32u phaseBit = (((c2 & 0x88) ^ ((c2<<5) & 0x80)) | ((c5 ^ (c5<<2)) & 0x20)) ? 0x02 : 0x00;
+
+ //Hi-Hat
+ Bit32u hhVol = Op(2)->ForwardVolume();
+ if ( !ENV_SILENT( hhVol ) ) {
+ Bit32u hhIndex = (phaseBit<<8) | (0x34 << ( phaseBit ^ (noiseBit << 1 )));
+ sample += Op(2)->GetWave( hhIndex, hhVol );
+ }
+ //Snare Drum
+ Bit32u sdVol = Op(3)->ForwardVolume();
+ if ( !ENV_SILENT( sdVol ) ) {
+ Bit32u sdIndex = ( 0x100 + (c2 & 0x100) ) ^ ( noiseBit << 8 );
+ sample += Op(3)->GetWave( sdIndex, sdVol );
+ }
+ //Tom-tom
+ sample += Op(4)->GetSample( 0 );
+
+ //Top-Cymbal
+ Bit32u tcVol = Op(5)->ForwardVolume();
+ if ( !ENV_SILENT( tcVol ) ) {
+ Bit32u tcIndex = (1 + phaseBit) << 8;
+ sample += Op(5)->GetWave( tcIndex, tcVol );
+ }
+ sample <<= 1;
+ if ( opl3Mode ) {
+ output[0] += sample;
+ output[1] += sample;
+ } else {
+ output[0] += sample;
+ }
+}
+
+template<SynthMode mode>
+Channel* Channel::BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ) {
+ switch( mode ) {
+ case sm2AM:
+ case sm3AM:
+ if ( Op(0)->Silent() && Op(1)->Silent() ) {
+ old[0] = old[1] = 0;
+ return (this + 1);
+ }
+ break;
+ case sm2FM:
+ case sm3FM:
+ if ( Op(1)->Silent() ) {
+ old[0] = old[1] = 0;
+ return (this + 1);
+ }
+ break;
+ case sm3FMFM:
+ if ( Op(3)->Silent() ) {
+ old[0] = old[1] = 0;
+ return (this + 2);
+ }
+ break;
+ case sm3AMFM:
+ if ( Op(0)->Silent() && Op(3)->Silent() ) {
+ old[0] = old[1] = 0;
+ return (this + 2);
+ }
+ break;
+ case sm3FMAM:
+ if ( Op(1)->Silent() && Op(3)->Silent() ) {
+ old[0] = old[1] = 0;
+ return (this + 2);
+ }
+ break;
+ case sm3AMAM:
+ if ( Op(0)->Silent() && Op(2)->Silent() && Op(3)->Silent() ) {
+ old[0] = old[1] = 0;
+ return (this + 2);
+ }
+ break;
+ }
+ //Init the operators with the the current vibrato and tremolo values
+ Op( 0 )->Prepare( chip );
+ Op( 1 )->Prepare( chip );
+ if ( mode > sm4Start ) {
+ Op( 2 )->Prepare( chip );
+ Op( 3 )->Prepare( chip );
+ }
+ if ( mode > sm6Start ) {
+ Op( 4 )->Prepare( chip );
+ Op( 5 )->Prepare( chip );
+ }
+ for ( Bitu i = 0; i < samples; i++ ) {
+ //Early out for percussion handlers
+ if ( mode == sm2Percussion ) {
+ GeneratePercussion<false>( chip, output + i );
+ continue; //Prevent some unitialized value bitching
+ } else if ( mode == sm3Percussion ) {
+ GeneratePercussion<true>( chip, output + i * 2 );
+ continue; //Prevent some unitialized value bitching
+ }
+
+ //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise
+ Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback;
+ old[0] = old[1];
+ old[1] = Op(0)->GetSample( mod );
+ Bit32s sample;
+ Bit32s out0 = old[0];
+ if ( mode == sm2AM || mode == sm3AM ) {
+ sample = out0 + Op(1)->GetSample( 0 );
+ } else if ( mode == sm2FM || mode == sm3FM ) {
+ sample = Op(1)->GetSample( out0 );
+ } else if ( mode == sm3FMFM ) {
+ Bits next = Op(1)->GetSample( out0 );
+ next = Op(2)->GetSample( next );
+ sample = Op(3)->GetSample( next );
+ } else if ( mode == sm3AMFM ) {
+ sample = out0;
+ Bits next = Op(1)->GetSample( 0 );
+ next = Op(2)->GetSample( next );
+ sample += Op(3)->GetSample( next );
+ } else if ( mode == sm3FMAM ) {
+ sample = Op(1)->GetSample( out0 );
+ Bits next = Op(2)->GetSample( 0 );
+ sample += Op(3)->GetSample( next );
+ } else if ( mode == sm3AMAM ) {
+ sample = out0;
+ Bits next = Op(1)->GetSample( 0 );
+ sample += Op(2)->GetSample( next );
+ sample += Op(3)->GetSample( 0 );
+ }
+ switch( mode ) {
+ case sm2AM:
+ case sm2FM:
+ output[ i ] += sample;
+ break;
+ case sm3AM:
+ case sm3FM:
+ case sm3FMFM:
+ case sm3AMFM:
+ case sm3FMAM:
+ case sm3AMAM:
+ output[ i * 2 + 0 ] += sample & maskLeft;
+ output[ i * 2 + 1 ] += sample & maskRight;
+ break;
+ }
+ }
+ switch( mode ) {
+ case sm2AM:
+ case sm2FM:
+ case sm3AM:
+ case sm3FM:
+ return ( this + 1 );
+ case sm3FMFM:
+ case sm3AMFM:
+ case sm3FMAM:
+ case sm3AMAM:
+ return( this + 2 );
+ case sm2Percussion:
+ case sm3Percussion:
+ return( this + 3 );
+ }
+ return 0;
+}
+
+/*
+ Chip
+*/
+
+Chip::Chip() {
+ reg08 = 0;
+ reg04 = 0;
+ regBD = 0;
+ reg104 = 0;
+ opl3Active = 0;
+}
+
+INLINE Bit32u Chip::ForwardNoise() {
+ noiseCounter += noiseAdd;
+ Bitu count = noiseCounter >> LFO_SH;
+ noiseCounter &= WAVE_MASK;
+ for ( ; count > 0; --count ) {
+ //Noise calculation from mame
+ noiseValue ^= ( 0x800302 ) & ( 0 - (noiseValue & 1 ) );
+ noiseValue >>= 1;
+ }
+ return noiseValue;
+}
+
+INLINE Bit32u Chip::ForwardLFO( Bit32u samples ) {
+ //Current vibrato value, runs 4x slower than tremolo
+ vibratoSign = ( VibratoTable[ vibratoIndex >> 2] ) >> 7;
+ vibratoShift = ( VibratoTable[ vibratoIndex >> 2] & 7) + vibratoStrength;
+ tremoloValue = TremoloTable[ tremoloIndex ] >> tremoloStrength;
+
+ //Check hom many samples there can be done before the value changes
+ Bit32u todo = LFO_MAX - lfoCounter;
+ Bit32u count = (todo + lfoAdd - 1) / lfoAdd;
+ if ( count > samples ) {
+ count = samples;
+ lfoCounter += count * lfoAdd;
+ } else {
+ lfoCounter += count * lfoAdd;
+ lfoCounter &= (LFO_MAX - 1);
+ //Maximum of 7 vibrato value * 4
+ vibratoIndex = ( vibratoIndex + 1 ) & 31;
+ //Clip tremolo to the the table size
+ if ( tremoloIndex + 1 < TREMOLO_TABLE )
+ ++tremoloIndex;
+ else
+ tremoloIndex = 0;
+ }
+ return count;
+}
+
+
+void Chip::WriteBD( Bit8u val ) {
+ Bit8u change = regBD ^ val;
+ if ( !change )
+ return;
+ regBD = val;
+ //TODO could do this with shift and xor?
+ vibratoStrength = (val & 0x40) ? 0x00 : 0x01;
+ tremoloStrength = (val & 0x80) ? 0x00 : 0x02;
+ if ( val & 0x20 ) {
+ //Drum was just enabled, make sure channel 6 has the right synth
+ if ( change & 0x20 ) {
+ if ( opl3Active ) {
+ chan[6].synthHandler = &Channel::BlockTemplate< sm3Percussion >;
+ } else {
+ chan[6].synthHandler = &Channel::BlockTemplate< sm2Percussion >;
+ }
+ }
+ //Bass Drum
+ if ( val & 0x10 ) {
+ chan[6].op[0].KeyOn( 0x2 );
+ chan[6].op[1].KeyOn( 0x2 );
+ } else {
+ chan[6].op[0].KeyOff( 0x2 );
+ chan[6].op[1].KeyOff( 0x2 );
+ }
+ //Hi-Hat
+ if ( val & 0x1 ) {
+ chan[7].op[0].KeyOn( 0x2 );
+ } else {
+ chan[7].op[0].KeyOff( 0x2 );
+ }
+ //Snare
+ if ( val & 0x8 ) {
+ chan[7].op[1].KeyOn( 0x2 );
+ } else {
+ chan[7].op[1].KeyOff( 0x2 );
+ }
+ //Tom-Tom
+ if ( val & 0x4 ) {
+ chan[8].op[0].KeyOn( 0x2 );
+ } else {
+ chan[8].op[0].KeyOff( 0x2 );
+ }
+ //Top Cymbal
+ if ( val & 0x2 ) {
+ chan[8].op[1].KeyOn( 0x2 );
+ } else {
+ chan[8].op[1].KeyOff( 0x2 );
+ }
+ //Toggle keyoffs when we turn off the percussion
+ } else if ( change & 0x20 ) {
+ //Trigger a reset to setup the original synth handler
+ //This makes it call
+ chan[6].UpdateSynth( this );
+ chan[6].op[0].KeyOff( 0x2 );
+ chan[6].op[1].KeyOff( 0x2 );
+ chan[7].op[0].KeyOff( 0x2 );
+ chan[7].op[1].KeyOff( 0x2 );
+ chan[8].op[0].KeyOff( 0x2 );
+ chan[8].op[1].KeyOff( 0x2 );
+ }
+}
+
+
+#define REGOP( _FUNC_ ) \
+ index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \
+ if ( OpOffsetTable[ index ] ) { \
+ Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ] ); \
+ regOp->_FUNC_( this, val ); \
+ }
+
+#define REGCHAN( _FUNC_ ) \
+ index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \
+ if ( ChanOffsetTable[ index ] ) { \
+ Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ] ); \
+ regChan->_FUNC_( this, val ); \
+ }
+
+//Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers
+void Chip::UpdateSynths() {
+ for (int i = 0; i < 18; i++) {
+ chan[i].UpdateSynth(this);
+ }
+}
+
+
+void Chip::WriteReg( Bit32u reg, Bit8u val ) {
+ Bitu index;
+ switch ( (reg & 0xf0) >> 4 ) {
+ case 0x00 >> 4:
+ if ( reg == 0x01 ) {
+ waveFormMask = ( val & 0x20 ) ? 0x7 : 0x0;
+ } else if ( reg == 0x104 ) {
+ //Only detect changes in lowest 6 bits
+ if ( !((reg104 ^ val) & 0x3f) )
+ return;
+ //Always keep the highest bit enabled, for checking > 0x80
+ reg104 = 0x80 | ( val & 0x3f );
+ //Switch synths when changing the 4op combinations
+ UpdateSynths();
+ } else if ( reg == 0x105 ) {
+ //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register
+ if ( !((opl3Active ^ val) & 1 ) )
+ return;
+ opl3Active = ( val & 1 ) ? 0xff : 0;
+ //Just tupdate the synths now that opl3 most have been enabled
+ //This isn't how the real card handles it but need to switch to stereo generating handlers
+ UpdateSynths();
+ } else if ( reg == 0x08 ) {
+ reg08 = val;
+ }
+ case 0x10 >> 4:
+ break;
+ case 0x20 >> 4:
+ case 0x30 >> 4:
+ REGOP( Write20 );
+ break;
+ case 0x40 >> 4:
+ case 0x50 >> 4:
+ REGOP( Write40 );
+ break;
+ case 0x60 >> 4:
+ case 0x70 >> 4:
+ REGOP( Write60 );
+ break;
+ case 0x80 >> 4:
+ case 0x90 >> 4:
+ REGOP( Write80 );
+ break;
+ case 0xa0 >> 4:
+ REGCHAN( WriteA0 );
+ break;
+ case 0xb0 >> 4:
+ if ( reg == 0xbd ) {
+ WriteBD( val );
+ } else {
+ REGCHAN( WriteB0 );
+ }
+ break;
+ case 0xc0 >> 4:
+ REGCHAN( WriteC0 );
+ case 0xd0 >> 4:
+ break;
+ case 0xe0 >> 4:
+ case 0xf0 >> 4:
+ REGOP( WriteE0 );
+ break;
+ }
+}
+
+
+Bit32u Chip::WriteAddr( Bit32u port, Bit8u val ) {
+ switch ( port & 3 ) {
+ case 0:
+ return val;
+ case 2:
+ if ( opl3Active || (val == 0x05) )
+ return 0x100 | val;
+ else
+ return val;
+ }
+ return 0;
+}
+
+void Chip::GenerateBlock2( Bitu total, Bit32s* output ) {
+ while ( total > 0 ) {
+ Bit32u samples = ForwardLFO( total );
+ memset(output, 0, sizeof(Bit32s) * samples);
+// int count = 0;
+ for( Channel* ch = chan; ch < chan + 9; ) {
+// count++;
+ ch = (ch->*(ch->synthHandler))( this, samples, output );
+ }
+ total -= samples;
+ output += samples;
+ }
+}
+
+void Chip::GenerateBlock3( Bitu total, Bit32s* output ) {
+ while ( total > 0 ) {
+ Bit32u samples = ForwardLFO( total );
+ memset(output, 0, sizeof(Bit32s) * samples *2);
+// int count = 0;
+ for( Channel* ch = chan; ch < chan + 18; ) {
+// count++;
+ ch = (ch->*(ch->synthHandler))( this, samples, output );
+ }
+ total -= samples;
+ output += samples * 2;
+ }
+}
+
+void Chip::Setup( Bit32u rate ) {
+ double original = OPLRATE;
+// double original = rate;
+ double scale = original / (double)rate;
+
+ //Noise counter is run at the same precision as general waves
+ noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) );
+ noiseCounter = 0;
+ noiseValue = 1; //Make sure it triggers the noise xor the first time
+ //The low frequency oscillation counter
+ //Every time his overflows vibrato and tremoloindex are increased
+ lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) );
+ lfoCounter = 0;
+ vibratoIndex = 0;
+ tremoloIndex = 0;
+
+ //With higher octave this gets shifted up
+ //-1 since the freqCreateTable = *2
+#ifdef WAVE_PRECISION
+ double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10));
+ for ( int i = 0; i < 16; i++ ) {
+ freqMul[i] = (Bit32u)( 0.5 + freqScale * FreqCreateTable[ i ] );
+ }
+#else
+ Bit32u freqScale = (Bit32u)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10)));
+ for ( int i = 0; i < 16; i++ ) {
+ freqMul[i] = freqScale * FreqCreateTable[ i ];
+ }
+#endif
+
+ //-3 since the real envelope takes 8 steps to reach the single value we supply
+ for ( Bit8u i = 0; i < 76; i++ ) {
+ Bit8u index, shift;
+ EnvelopeSelect( i, index, shift );
+ linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 )));
+ }
+// Bit32s attackDiffs[62];
+ //Generate the best matching attack rate
+ for ( Bit8u i = 0; i < 62; i++ ) {
+ Bit8u index, shift;
+ EnvelopeSelect( i, index, shift );
+ //Original amount of samples the attack would take
+ Bit32s original = (Bit32u)( (AttackSamplesTable[ index ] << shift) / scale);
+
+ Bit32s guessAdd = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH - shift - 3 )));
+ Bit32s bestAdd = guessAdd;
+ Bit32u bestDiff = 1 << 30;
+ for( Bit32u passes = 0; passes < 16; passes ++ ) {
+ Bit32s volume = ENV_MAX;
+ Bit32s samples = 0;
+ Bit32u count = 0;
+ while ( volume > 0 && samples < original * 2 ) {
+ count += guessAdd;
+ Bit32s change = count >> RATE_SH;
+ count &= RATE_MASK;
+ if ( GCC_UNLIKELY(change) ) { // less than 1 %
+ volume += ( ~volume * change ) >> 3;
+ }
+ samples++;
+
+ }
+ Bit32s diff = original - samples;
+ Bit32u lDiff = labs( diff );
+ //Init last on first pass
+ if ( lDiff < bestDiff ) {
+ bestDiff = lDiff;
+ bestAdd = guessAdd;
+ //We hit an exactly matching sample count
+ if ( !bestDiff )
+ break;
+ }
+ //Linear correction factor, not exactly perfect but seems to work
+ double correct = (original - diff) / (double)original;
+ guessAdd = (Bit32u)(guessAdd * correct);
+ //Below our target
+ if ( diff < 0 ) {
+ //Always add one here for rounding, an overshoot will get corrected by another pass decreasing
+ guessAdd++;
+ }
+ }
+ attackRates[i] = bestAdd;
+ //Keep track of the diffs for some debugging
+// attackDiffs[i] = bestDiff;
+ }
+ for ( Bit8u i = 62; i < 76; i++ ) {
+ //This should provide instant volume maximizing
+ attackRates[i] = 8 << RATE_SH;
+ }
+ //Setup the channels with the correct four op flags
+ //Channels are accessed through a table so they appear linear here
+ chan[ 0].fourMask = 0x00 | ( 1 << 0 );
+ chan[ 1].fourMask = 0x80 | ( 1 << 0 );
+ chan[ 2].fourMask = 0x00 | ( 1 << 1 );
+ chan[ 3].fourMask = 0x80 | ( 1 << 1 );
+ chan[ 4].fourMask = 0x00 | ( 1 << 2 );
+ chan[ 5].fourMask = 0x80 | ( 1 << 2 );
+
+ chan[ 9].fourMask = 0x00 | ( 1 << 3 );
+ chan[10].fourMask = 0x80 | ( 1 << 3 );
+ chan[11].fourMask = 0x00 | ( 1 << 4 );
+ chan[12].fourMask = 0x80 | ( 1 << 4 );
+ chan[13].fourMask = 0x00 | ( 1 << 5 );
+ chan[14].fourMask = 0x80 | ( 1 << 5 );
+
+ //mark the percussion channels
+ chan[ 6].fourMask = 0x40;
+ chan[ 7].fourMask = 0x40;
+ chan[ 8].fourMask = 0x40;
+
+ //Clear Everything in opl3 mode
+ WriteReg( 0x105, 0x1 );
+ for ( int i = 0; i < 512; i++ ) {
+ if ( i == 0x105 )
+ continue;
+ WriteReg( i, 0xff );
+ WriteReg( i, 0x0 );
+ }
+ WriteReg( 0x105, 0x0 );
+ //Clear everything in opl2 mode
+ for ( int i = 0; i < 255; i++ ) {
+ WriteReg( i, 0xff );
+ WriteReg( i, 0x0 );
+ }
+}
+
+static bool doneTables = false;
+void InitTables( void ) {
+ if ( doneTables )
+ return;
+ doneTables = true;
+#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG )
+ //Exponential volume table, same as the real adlib
+ for ( int i = 0; i < 256; i++ ) {
+ //Save them in reverse
+ ExpTable[i] = (int)( 0.5 + ( pow(2.0, ( 255 - i) * ( 1.0 /256 ) )-1) * 1024 );
+ ExpTable[i] += 1024; //or remove the -1 oh well :)
+ //Preshift to the left once so the final volume can shift to the right
+ ExpTable[i] *= 2;
+ }
+#endif
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+ //Add 0.5 for the trunc rounding of the integer cast
+ //Do a PI sinetable instead of the original 0.5 PI
+ for ( int i = 0; i < 512; i++ ) {
+ SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 );
+ }
+#endif
+#if ( DBOPL_WAVE == WAVE_TABLEMUL )
+ //Multiplication based tables
+ for ( int i = 0; i < 384; i++ ) {
+ int s = i * 8;
+ //TODO maybe keep some of the precision errors of the original table?
+ double val = ( 0.5 + ( pow(2.0, -1.0 + ( 255 - s) * ( 1.0 /256 ) )) * ( 1 << MUL_SH ));
+ MulTable[i] = (Bit16u)(val);
+ }
+
+ //Sine Wave Base
+ for ( int i = 0; i < 512; i++ ) {
+ WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (PI / 512.0) ) * 4084);
+ WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ];
+ }
+ //Exponential wave
+ for ( int i = 0; i < 256; i++ ) {
+ WaveTable[ 0x700 + i ] = (Bit16s)( 0.5 + ( pow(2.0, -1.0 + ( 255 - i * 8) * ( 1.0 /256 ) ) ) * 4085 );
+ WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ];
+ }
+#endif
+#if ( DBOPL_WAVE == WAVE_TABLELOG )
+ //Sine Wave Base
+ for ( int i = 0; i < 512; i++ ) {
+ WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 );
+ WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i];
+ }
+ //Exponential wave
+ for ( int i = 0; i < 256; i++ ) {
+ WaveTable[ 0x700 + i ] = i * 8;
+ WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8;
+ }
+#endif
+
+ // | |//\\|____|WAV7|//__|/\ |____|/\/\|
+ // |\\//| | |WAV7| | \/| | |
+ // |06 |0126|27 |7 |3 |4 |4 5 |5 |
+
+#if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL ))
+ for ( int i = 0; i < 256; i++ ) {
+ //Fill silence gaps
+ WaveTable[ 0x400 + i ] = WaveTable[0];
+ WaveTable[ 0x500 + i ] = WaveTable[0];
+ WaveTable[ 0x900 + i ] = WaveTable[0];
+ WaveTable[ 0xc00 + i ] = WaveTable[0];
+ WaveTable[ 0xd00 + i ] = WaveTable[0];
+ //Replicate sines in other pieces
+ WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ];
+ //double speed sines
+ WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ];
+ WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ];
+ WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ];
+ WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ];
+ }
+#endif
+
+ //Create the ksl table
+ for ( int oct = 0; oct < 8; oct++ ) {
+ int base = oct * 8;
+ for ( int i = 0; i < 16; i++ ) {
+ int val = base - KslCreateTable[i];
+ if ( val < 0 )
+ val = 0;
+ //*4 for the final range to match attenuation range
+ KslTable[ oct * 16 + i ] = val * 4;
+ }
+ }
+ //Create the Tremolo table, just increase and decrease a triangle wave
+ for ( Bit8u i = 0; i < TREMOLO_TABLE / 2; i++ ) {
+ Bit8u val = i << ENV_EXTRA;
+ TremoloTable[i] = val;
+ TremoloTable[TREMOLO_TABLE - 1 - i] = val;
+ }
+ //Create a table with offsets of the channels from the start of the chip
+ DBOPL::Chip* chip = 0;
+ for ( Bitu i = 0; i < 32; i++ ) {
+ Bitu index = i & 0xf;
+ if ( index >= 9 ) {
+ ChanOffsetTable[i] = 0;
+ continue;
+ }
+ //Make sure the four op channels follow eachother
+ if ( index < 6 ) {
+ index = (index % 3) * 2 + ( index / 3 );
+ }
+ //Add back the bits for highest ones
+ if ( i >= 16 )
+ index += 9;
+ Bitu blah = reinterpret_cast<Bitu>( &(chip->chan[ index ]) );
+ ChanOffsetTable[i] = blah;
+ }
+ //Same for operators
+ for ( Bitu i = 0; i < 64; i++ ) {
+ if ( i % 8 >= 6 || ( (i / 8) % 4 == 3 ) ) {
+ OpOffsetTable[i] = 0;
+ continue;
+ }
+ Bitu chNum = (i / 8) * 3 + (i % 8) % 3;
+ //Make sure we use 16 and up for the 2nd range to match the chanoffset gap
+ if ( chNum >= 12 )
+ chNum += 16 - 12;
+ Bitu opNum = ( i % 8 ) / 3;
+ DBOPL::Channel* chan = 0;
+ Bitu blah = reinterpret_cast<Bitu>( &(chan->op[opNum]) );
+ OpOffsetTable[i] = ChanOffsetTable[ chNum ] + blah;
+ }
+#if 0
+ //Stupid checks if table's are correct
+ for ( Bitu i = 0; i < 18; i++ ) {
+ Bit32u find = (Bit16u)( &(chip->chan[ i ]) );
+ for ( Bitu c = 0; c < 32; c++ ) {
+ if ( ChanOffsetTable[c] == find ) {
+ find = 0;
+ break;
+ }
+ }
+ if ( find ) {
+ find = find;
+ }
+ }
+ for ( Bitu i = 0; i < 36; i++ ) {
+ Bit32u find = (Bit16u)( &(chip->chan[ i / 2 ].op[i % 2]) );
+ for ( Bitu c = 0; c < 64; c++ ) {
+ if ( OpOffsetTable[c] == find ) {
+ find = 0;
+ break;
+ }
+ }
+ if ( find ) {
+ find = find;
+ }
+ }
+#endif
+}
+
+Bit32u Handler::WriteAddr( Bit32u port, Bit8u val ) {
+ return chip.WriteAddr( port, val );
+
+}
+void Handler::WriteReg( Bit32u addr, Bit8u val ) {
+ chip.WriteReg( addr, val );
+}
+
+void Handler::Generate( MixerChannel* chan, Bitu samples ) {
+ Bit32s buffer[ 512 * 2 ];
+ if ( GCC_UNLIKELY(samples > 512) )
+ samples = 512;
+ if ( !chip.opl3Active ) {
+ chip.GenerateBlock2( samples, buffer );
+ chan->AddSamples_m32( samples, buffer );
+ } else {
+ chip.GenerateBlock3( samples, buffer );
+ chan->AddSamples_s32( samples, buffer );
+ }
+}
+
+void Handler::Init( Bitu rate ) {
+ InitTables();
+ chip.Setup( rate );
+}
+
+
+}; //Namespace DBOPL
diff --git a/src/hardware/dbopl.h b/src/hardware/dbopl.h
new file mode 100644
index 000000000..addaed773
--- /dev/null
+++ b/src/hardware/dbopl.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include "adlib.h"
+#include "dosbox.h"
+
+//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume
+#define WAVE_HANDLER 10
+//Use a logarithmic wavetable with an exponential table for volume
+#define WAVE_TABLELOG 11
+//Use a linear wavetable with a multiply table for volume
+#define WAVE_TABLEMUL 12
+
+//Select the type of wave generator routine
+#define DBOPL_WAVE WAVE_TABLEMUL
+
+namespace DBOPL {
+
+struct Chip;
+struct Operator;
+struct Channel;
+
+#if (DBOPL_WAVE == WAVE_HANDLER)
+typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume );
+#endif
+
+typedef Bits ( DBOPL::Operator::*VolumeHandler) ( );
+typedef Channel* ( DBOPL::Channel::*SynthHandler) ( Chip* chip, Bit32u samples, Bit32s* output );
+
+//Different synth modes that can generate blocks of data
+typedef enum {
+ sm2AM,
+ sm2FM,
+ sm3AM,
+ sm3FM,
+ sm4Start,
+ sm3FMFM,
+ sm3AMFM,
+ sm3FMAM,
+ sm3AMAM,
+ sm6Start,
+ sm2Percussion,
+ sm3Percussion,
+} SynthMode;
+
+//Shifts for the values contained in chandata variable
+enum {
+ SHIFT_KSLBASE = 16,
+ SHIFT_KEYCODE = 24,
+};
+
+struct Operator {
+public:
+ //Masks for operator 20 values
+ enum {
+ MASK_KSR = 0x10,
+ MASK_SUSTAIN = 0x20,
+ MASK_VIBRATO = 0x40,
+ MASK_TREMOLO = 0x80,
+ };
+
+ typedef enum {
+ OFF,
+ RELEASE,
+ SUSTAIN,
+ DECAY,
+ ATTACK,
+ } State;
+
+ VolumeHandler volHandler;
+
+#if (DBOPL_WAVE == WAVE_HANDLER)
+ WaveHandler waveHandler; //Routine that generate a wave
+#else
+ Bit16s* waveBase;
+ Bit32u waveMask;
+ Bit32u waveStart;
+#endif
+ Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index
+ Bit32u waveAdd; //The base frequency without vibrato
+ Bit32u waveCurrent; //waveAdd + vibratao
+
+ Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this
+ Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove?
+ Bit32u vibrato; //Scaled up vibrato strength
+ Bit32s sustainLevel; //When stopping at sustain level stop here
+ Bit32s totalLevel; //totalLevel is added to every generated volume
+ Bit32u currentLevel; //totalLevel + tremolo
+ Bit32s volume; //The currently active volume
+
+ Bit32u attackAdd; //Timers for the different states of the envelope
+ Bit32u decayAdd;
+ Bit32u releaseAdd;
+ Bit32u rateIndex; //Current position of the evenlope
+
+ Bit8u rateZero; //Bits for the different states of the envelope having no changes
+ Bit8u keyOn; //Bitmask of different values that can generate keyon
+ //Registers, also used to check for changes
+ Bit8u reg20, reg40, reg60, reg80, regE0;
+ //Active part of the envelope we're in
+ Bit8u state;
+ //0xff when tremolo is enabled
+ Bit8u tremoloMask;
+ //Strength of the vibrato
+ Bit8u vibStrength;
+ //Keep track of the calculated KSR so we can check for changes
+ Bit8u ksr;
+private:
+ void SetState( Bit8u s );
+ void UpdateAttack( const Chip* chip );
+ void UpdateRelease( const Chip* chip );
+ void UpdateDecay( const Chip* chip );
+public:
+ void UpdateAttenuation();
+ void UpdateRates( const Chip* chip );
+ void UpdateFrequency( );
+
+ void Write20( const Chip* chip, Bit8u val );
+ void Write40( const Chip* chip, Bit8u val );
+ void Write60( const Chip* chip, Bit8u val );
+ void Write80( const Chip* chip, Bit8u val );
+ void WriteE0( const Chip* chip, Bit8u val );
+
+ bool Silent() const;
+ void Prepare( const Chip* chip );
+
+ void KeyOn( Bit8u mask);
+ void KeyOff( Bit8u mask);
+
+ template< State state>
+ Bits TemplateVolume( );
+
+ Bit32s RateForward( Bit32u add );
+ Bitu ForwardWave();
+ Bitu ForwardVolume();
+
+ Bits GetSample( Bits modulation );
+ Bits GetWave( Bitu index, Bitu vol );
+public:
+ Operator();
+};
+
+struct Channel {
+ Operator op[2];
+ inline Operator* Op( Bitu index ) {
+ return &( ( this + (index >> 1) )->op[ index & 1 ]);
+ }
+ SynthHandler synthHandler;
+ Bit32u chanData; //Frequency/octave and derived values
+ Bit32s old[2]; //Old data for feedback
+
+ Bit8u feedback; //Feedback shift
+ Bit8u regB0; //Register values to check for changes
+ Bit8u regC0;
+ //This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel
+ Bit8u fourMask;
+ Bit8s maskLeft; //Sign extended values for both channel's panning
+ Bit8s maskRight;
+
+ //Forward the channel data to the operators of the channel
+ void SetChanData( const Chip* chip, Bit32u data );
+ //Change in the chandata, check for new values and if we have to forward to operators
+ void UpdateFrequency( const Chip* chip, Bit8u fourOp );
+ void UpdateSynth(const Chip* chip);
+ void WriteA0( const Chip* chip, Bit8u val );
+ void WriteB0( const Chip* chip, Bit8u val );
+ void WriteC0( const Chip* chip, Bit8u val );
+
+ //call this for the first channel
+ template< bool opl3Mode >
+ void GeneratePercussion( Chip* chip, Bit32s* output );
+
+ //Generate blocks of data in specific modes
+ template<SynthMode mode>
+ Channel* BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output );
+ Channel();
+};
+
+struct Chip {
+ //This is used as the base counter for vibrato and tremolo
+ Bit32u lfoCounter;
+ Bit32u lfoAdd;
+
+
+ Bit32u noiseCounter;
+ Bit32u noiseAdd;
+ Bit32u noiseValue;
+
+ //Frequency scales for the different multiplications
+ Bit32u freqMul[16];
+ //Rates for decay and release for rate of this chip
+ Bit32u linearRates[76];
+ //Best match attack rates for the rate of this chip
+ Bit32u attackRates[76];
+
+ //18 channels with 2 operators each
+ Channel chan[18];
+
+ Bit8u reg104;
+ Bit8u reg08;
+ Bit8u reg04;
+ Bit8u regBD;
+ Bit8u vibratoIndex;
+ Bit8u tremoloIndex;
+ Bit8s vibratoSign;
+ Bit8u vibratoShift;
+ Bit8u tremoloValue;
+ Bit8u vibratoStrength;
+ Bit8u tremoloStrength;
+ //Mask for allowed wave forms
+ Bit8u waveFormMask;
+ //0 or -1 when enabled
+ Bit8s opl3Active;
+
+ //Return the maximum amount of samples before and LFO change
+ Bit32u ForwardLFO( Bit32u samples );
+ Bit32u ForwardNoise();
+
+ void WriteBD( Bit8u val );
+ void WriteReg(Bit32u reg, Bit8u val );
+
+ Bit32u WriteAddr( Bit32u port, Bit8u val );
+
+ void GenerateBlock2( Bitu samples, Bit32s* output );
+ void GenerateBlock3( Bitu samples, Bit32s* output );
+
+ //Update the synth handlers in all channels
+ void UpdateSynths();
+ void Generate( Bit32u samples );
+ void Setup( Bit32u r );
+
+ Chip();
+};
+
+struct Handler : public Adlib::Handler {
+ DBOPL::Chip chip;
+ virtual Bit32u WriteAddr( Bit32u port, Bit8u val );
+ virtual void WriteReg( Bit32u addr, Bit8u val );
+ virtual void Generate( MixerChannel* chan, Bitu samples );
+ virtual void Init( Bitu rate );
+};
+
+
+}; //Namespace
diff --git a/src/hardware/disney.cpp b/src/hardware/disney.cpp
new file mode 100644
index 000000000..629d94ba0
--- /dev/null
+++ b/src/hardware/disney.cpp
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include "dosbox.h"
+#include "inout.h"
+#include "mixer.h"
+#include "pic.h"
+#include "setup.h"
+
+#define DISNEY_BASE 0x0378
+
+#define DISNEY_SIZE 128
+
+typedef struct _dac_channel {
+ Bit8u buffer[DISNEY_SIZE]; // data buffer
+ Bitu used; // current data buffer level
+ double speedcheck_sum;
+ double speedcheck_last;
+ bool speedcheck_failed;
+ bool speedcheck_init;
+} dac_channel;
+
+static struct {
+ // parallel port stuff
+ Bit8u data;
+ Bit8u status;
+ Bit8u control;
+ // the D/A channels
+ dac_channel da[2];
+
+ Bitu last_used;
+ MixerObject * mo;
+ MixerChannel * chan;
+ bool stereo;
+ // which channel do we use for mono output?
+ // and the channel used for stereo
+ dac_channel* leader;
+
+ Bitu state;
+ Bitu interface_det;
+ Bitu interface_det_ext;
+} disney;
+
+#define DS_IDLE 0
+#define DS_RUNNING 1
+#define DS_FINISH 2
+#define DS_ANALYZING 3
+
+static void DISNEY_CallBack(Bitu len);
+
+static void DISNEY_disable(Bitu) {
+ if(disney.mo) {
+ disney.chan->AddSilence();
+ disney.chan->Enable(false);
+ }
+ disney.leader = 0;
+ disney.last_used = 0;
+ disney.state = DS_IDLE;
+ disney.interface_det = 0;
+ disney.interface_det_ext = 0;
+ disney.stereo = false;
+}
+
+static void DISNEY_enable(Bitu freq) {
+ if(freq < 500 || freq > 100000) {
+ // try again..
+ disney.state = DS_IDLE;
+ return;
+ } else {
+#if 0
+ if(disney.stereo) LOG(LOG_MISC,LOG_NORMAL)("disney enable %d Hz, stereo",freq);
+ else LOG(LOG_MISC,LOG_NORMAL)("disney enable %d Hz, mono",freq);
+#endif
+ disney.chan->SetFreq(freq);
+ disney.chan->Enable(true);
+ disney.state = DS_RUNNING;
+ }
+}
+
+static void DISNEY_analyze(Bitu channel){
+ switch(disney.state) {
+ case DS_RUNNING: // should not get here
+ break;
+ case DS_IDLE:
+ // initialize channel data
+ for(int i = 0; i < 2; i++) {
+ disney.da[i].used = 0;
+ disney.da[i].speedcheck_sum = 0;
+ disney.da[i].speedcheck_failed = false;
+ disney.da[i].speedcheck_init = false;
+ }
+ disney.da[channel].speedcheck_last = PIC_FullIndex();
+ disney.da[channel].speedcheck_init = true;
+
+ disney.state = DS_ANALYZING;
+ break;
+
+ case DS_FINISH:
+ {
+ // detect stereo: if we have about the same data amount in both channels
+ Bits st_diff = disney.da[0].used - disney.da[1].used;
+
+ // find leader channel (the one with higher rate) [this good for the stereo case?]
+ if(disney.da[0].used > disney.da[1].used) {
+ //disney.monochannel=0;
+ disney.leader = &disney.da[0];
+ } else {
+ //disney.monochannel=1;
+ disney.leader = &disney.da[1];
+ }
+
+ if((st_diff < 5) && (st_diff > -5)) disney.stereo = true;
+ else disney.stereo = false;
+
+ // calculate rate for both channels
+ Bitu ch_speed[2];
+
+ for(Bitu i = 0; i < 2; i++) {
+ ch_speed[i] = (Bitu)(1.0/((disney.da[i].speedcheck_sum/1000.0) /
+ (float)(((float)disney.da[i].used)-1.0))); // -1.75
+ }
+
+ // choose the larger value
+ DISNEY_enable(ch_speed[0] > ch_speed[1]?
+ ch_speed[0]:ch_speed[1]); // TODO
+ break;
+ }
+ case DS_ANALYZING:
+ {
+ double current = PIC_FullIndex();
+ dac_channel* cch = &disney.da[channel];
+
+ if(!cch->speedcheck_init) {
+ cch->speedcheck_init = true;
+ cch->speedcheck_last = current;
+ break;
+ }
+ cch->speedcheck_sum += current - cch->speedcheck_last;
+ //LOG_MSG("t=%f",current - cch->speedcheck_last);
+
+ // sanity checks (printer...)
+ if((current - cch-> speedcheck_last) < 0.01 ||
+ (current - cch-> speedcheck_last) > 2)
+ cch->speedcheck_failed = true;
+
+ // if both are failed we are back at start
+ if(disney.da[0].speedcheck_failed && disney.da[1].speedcheck_failed) {
+ disney.state=DS_IDLE;
+ break;
+ }
+
+ cch->speedcheck_last = current;
+
+ // analyze finish condition
+ if(disney.da[0].used > 30 || disney.da[1].used > 30)
+ disney.state = DS_FINISH;
+ break;
+ }
+ }
+}
+
+static void disney_write(Bitu port,Bitu val,Bitu iolen) {
+ //LOG_MSG("write disney time %f addr%x val %x",PIC_FullIndex(),port,val);
+ disney.last_used=PIC_Ticks;
+ switch (port-DISNEY_BASE) {
+ case 0: /* Data Port */
+ {
+ disney.data=val;
+ // if data is written here too often without using the stereo
+ // mechanism we use the simple DAC machanism.
+ if(disney.state != DS_RUNNING) {
+ disney.interface_det++;
+ if(disney.interface_det > 5)
+ DISNEY_analyze(0);
+ }
+ if(disney.interface_det > 5) {
+ if(disney.da[0].used < DISNEY_SIZE) {
+ disney.da[0].buffer[disney.da[0].used] = disney.data;
+ disney.da[0].used++;
+ } //else LOG_MSG("disney overflow 0");
+ }
+ break;
+ }
+ case 1: /* Status Port */
+ LOG(LOG_MISC,LOG_NORMAL)("DISNEY:Status write %x",val);
+ break;
+ case 2: /* Control Port */
+ if((disney.control & 0x2) && !(val & 0x2)) {
+ if(disney.state != DS_RUNNING) {
+ disney.interface_det = 0;
+ disney.interface_det_ext = 0;
+ DISNEY_analyze(1);
+ }
+
+ // stereo channel latch
+ if(disney.da[1].used < DISNEY_SIZE) {
+ disney.da[1].buffer[disney.da[1].used] = disney.data;
+ disney.da[1].used++;
+ } //else LOG_MSG("disney overflow 1");
+ }
+
+ if((disney.control & 0x1) && !(val & 0x1)) {
+ if(disney.state != DS_RUNNING) {
+ disney.interface_det = 0;
+ disney.interface_det_ext = 0;
+ DISNEY_analyze(0);
+ }
+ // stereo channel latch
+ if(disney.da[0].used < DISNEY_SIZE) {
+ disney.da[0].buffer[disney.da[0].used] = disney.data;
+ disney.da[0].used++;
+ } //else LOG_MSG("disney overflow 0");
+ }
+
+ if((disney.control & 0x8) && !(val & 0x8)) {
+ // emulate a device with 16-byte sound FIFO
+ if(disney.state != DS_RUNNING) {
+ disney.interface_det_ext++;
+ disney.interface_det = 0;
+ if(disney.interface_det_ext > 5) {
+ disney.leader = &disney.da[0];
+ DISNEY_enable(7000);
+ }
+ }
+ if(disney.interface_det_ext > 5) {
+ if(disney.da[0].used < DISNEY_SIZE) {
+ disney.da[0].buffer[disney.da[0].used] = disney.data;
+ disney.da[0].used++;
+ }
+ }
+ }
+
+// LOG_WARN("DISNEY:Control write %x",val);
+ if (val&0x10) LOG(LOG_MISC,LOG_ERROR)("DISNEY:Parallel IRQ Enabled");
+ disney.control=val;
+ break;
+ }
+}
+
+static Bitu disney_read(Bitu port,Bitu iolen) {
+ Bitu retval;
+ switch (port-DISNEY_BASE) {
+ case 0: /* Data Port */
+// LOG(LOG_MISC,LOG_NORMAL)("DISNEY:Read from data port");
+ return disney.data;
+ break;
+ case 1: /* Status Port */
+// LOG(LOG_MISC,"DISNEY:Read from status port %X",disney.status);
+ retval = 0x07;//0x40; // Stereo-on-1 and (or) New-Stereo DACs present
+ if(disney.interface_det_ext > 5) {
+ if (disney.leader && disney.leader->used >= 16){
+ retval |= 0x40; // ack
+ retval &= ~0x4; // interrupt
+ }
+ }
+ if(!(disney.data&0x80)) retval |= 0x80; // pin 9 is wired to pin 11
+ return retval;
+ break;
+ case 2: /* Control Port */
+ LOG(LOG_MISC,LOG_NORMAL)("DISNEY:Read from control port");
+ return disney.control;
+ break;
+ }
+ return 0xff;
+}
+
+static void DISNEY_PlayStereo(Bitu len, Bit8u* l, Bit8u* r) {
+ static Bit8u stereodata[DISNEY_SIZE*2];
+ for(Bitu i = 0; i < len; i++) {
+ stereodata[i*2] = l[i];
+ stereodata[i*2+1] = r[i];
+ }
+ disney.chan->AddSamples_s8(len,stereodata);
+}
+
+static void DISNEY_CallBack(Bitu len) {
+ if (!len) return;
+
+ // get the smaller used
+ Bitu real_used;
+ if(disney.stereo) {
+ real_used = disney.da[0].used;
+ if(disney.da[1].used < real_used) real_used = disney.da[1].used;
+ } else
+ real_used = disney.leader->used;
+
+ if (real_used >= len) { // enough data for now
+ if(disney.stereo) DISNEY_PlayStereo(len, disney.da[0].buffer, disney.da[1].buffer);
+ else disney.chan->AddSamples_m8(len,disney.leader->buffer);
+
+ // put the rest back to start
+ for(int i = 0; i < 2; i++) {
+ // TODO for mono only one
+ memmove(disney.da[i].buffer,&disney.da[i].buffer[len],DISNEY_SIZE/*real_used*/-len);
+ disney.da[i].used -= len;
+ }
+ // TODO: len > DISNEY
+ } else { // not enough data
+ if(disney.stereo) {
+ Bit8u gapfiller0 = 128;
+ Bit8u gapfiller1 = 128;
+ if(real_used) {
+ gapfiller0 = disney.da[0].buffer[real_used-1];
+ gapfiller1 = disney.da[1].buffer[real_used-1];
+ };
+
+ memset(disney.da[0].buffer+real_used,
+ gapfiller0,len-real_used);
+ memset(disney.da[1].buffer+real_used,
+ gapfiller1,len-real_used);
+
+ DISNEY_PlayStereo(len, disney.da[0].buffer, disney.da[1].buffer);
+ len -= real_used;
+
+ } else { // mono
+ Bit8u gapfiller = 128; //Keep the middle
+ if(real_used) {
+ // fix for some stupid game; it outputs 0 at the end of the stream
+ // causing a click. So if we have at least two bytes availible in the
+ // buffer and the last one is a 0 then ignore that.
+ if(disney.leader->buffer[real_used-1]==0)
+ real_used--;
+ }
+ // do it this way because AddSilence sounds like a gnawing mouse
+ if(real_used)
+ gapfiller = disney.leader->buffer[real_used-1];
+ //LOG_MSG("gapfiller %x, fill len %d, realused %d",gapfiller,len-real_used,real_used);
+ memset(disney.leader->buffer+real_used, gapfiller, len-real_used);
+ disney.chan->AddSamples_m8(len, disney.leader->buffer);
+ }
+ disney.da[0].used =0;
+ disney.da[1].used =0;
+
+ //LOG_MSG("disney underflow %d",len - real_used);
+ }
+ if (disney.last_used+100<PIC_Ticks) {
+ // disable sound output
+ PIC_AddEvent(DISNEY_disable,0.0001f); // I think we shouldn't delete the
+ // mixer while we are inside it
+ }
+}
+
+class DISNEY: public Module_base {
+private:
+ IO_ReadHandleObject ReadHandler;
+ IO_WriteHandleObject WriteHandler;
+ //MixerObject MixerChan;
+public:
+ DISNEY(Section* configuration):Module_base(configuration) {
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+ if(!section->Get_bool("disney")) return;
+
+ WriteHandler.Install(DISNEY_BASE,disney_write,IO_MB,3);
+ ReadHandler.Install(DISNEY_BASE,disney_read,IO_MB,3);
+
+ disney.status=0x84;
+ disney.control=0;
+ disney.last_used=0;
+
+ disney.mo = new MixerObject();
+ disney.chan=disney.mo->Install(&DISNEY_CallBack,10000,"DISNEY");
+ DISNEY_disable(0);
+
+
+ }
+ ~DISNEY(){
+ DISNEY_disable(0);
+ if (disney.mo)
+ delete disney.mo;
+ }
+};
+
+static DISNEY* test;
+
+static void DISNEY_ShutDown(Section* sec){
+ delete test;
+}
+
+void DISNEY_Init(Section* sec) {
+ test = new DISNEY(sec);
+ sec->AddDestroyFunction(&DISNEY_ShutDown,true);
+}
diff --git a/src/hardware/dma.cpp b/src/hardware/dma.cpp
new file mode 100644
index 000000000..f9f26debe
--- /dev/null
+++ b/src/hardware/dma.cpp
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include "dosbox.h"
+#include "mem.h"
+#include "inout.h"
+#include "dma.h"
+#include "pic.h"
+#include "paging.h"
+#include "setup.h"
+
+DmaController *DmaControllers[2];
+
+#define EMM_PAGEFRAME4K ((0xE000*16)/4096)
+Bit32u ems_board_mapping[LINK_START];
+
+static Bit32u dma_wrapping = 0xffff;
+
+static void UpdateEMSMapping(void) {
+ /* if EMS is not present, this will result in a 1:1 mapping */
+ Bitu i;
+ for (i=0;i<0x10;i++) {
+ ems_board_mapping[EMM_PAGEFRAME4K+i]=paging.firstmb[EMM_PAGEFRAME4K+i];
+ }
+}
+
+/* read a block from physical memory */
+static void DMA_BlockRead(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u dma16) {
+ Bit8u * write=(Bit8u *) data;
+ Bitu highpart_addr_page = spage>>12;
+ size <<= dma16;
+ offset <<= dma16;
+ Bit32u dma_wrap = ((0xffff<<dma16)+dma16) | dma_wrapping;
+ for ( ; size ; size--, offset++) {
+ if (offset>(dma_wrapping<<dma16)) {
+ LOG_MSG("DMA segbound wrapping (read): %x:%x size %x [%x] wrap %x",spage,offset,size,dma16,dma_wrapping);
+ }
+ offset &= dma_wrap;
+ Bitu page = highpart_addr_page+(offset >> 12);
+ /* care for EMS pageframe etc. */
+ if (page < EMM_PAGEFRAME4K) page = paging.firstmb[page];
+ else if (page < EMM_PAGEFRAME4K+0x10) page = ems_board_mapping[page];
+ else if (page < LINK_START) page = paging.firstmb[page];
+ *write++=phys_readb(page*4096 + (offset & 4095));
+ }
+}
+
+/* write a block into physical memory */
+static void DMA_BlockWrite(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u dma16) {
+ Bit8u * read=(Bit8u *) data;
+ Bitu highpart_addr_page = spage>>12;
+ size <<= dma16;
+ offset <<= dma16;
+ Bit32u dma_wrap = ((0xffff<<dma16)+dma16) | dma_wrapping;
+ for ( ; size ; size--, offset++) {
+ if (offset>(dma_wrapping<<dma16)) {
+ LOG_MSG("DMA segbound wrapping (write): %x:%x size %x [%x] wrap %x",spage,offset,size,dma16,dma_wrapping);
+ }
+ offset &= dma_wrap;
+ Bitu page = highpart_addr_page+(offset >> 12);
+ /* care for EMS pageframe etc. */
+ if (page < EMM_PAGEFRAME4K) page = paging.firstmb[page];
+ else if (page < EMM_PAGEFRAME4K+0x10) page = ems_board_mapping[page];
+ else if (page < LINK_START) page = paging.firstmb[page];
+ phys_writeb(page*4096 + (offset & 4095), *read++);
+ }
+}
+
+DmaChannel * GetDMAChannel(Bit8u chan) {
+ if (chan<4) {
+ /* channel on first DMA controller */
+ if (DmaControllers[0]) return DmaControllers[0]->GetChannel(chan);
+ } else if (chan<8) {
+ /* channel on second DMA controller */
+ if (DmaControllers[1]) return DmaControllers[1]->GetChannel(chan-4);
+ }
+ return NULL;
+}
+
+/* remove the second DMA controller (ports are removed automatically) */
+void CloseSecondDMAController(void) {
+ if (DmaControllers[1]) {
+ delete DmaControllers[1];
+ DmaControllers[1]=NULL;
+ }
+}
+
+/* check availability of second DMA controller, needed for SB16 */
+bool SecondDMAControllerAvailable(void) {
+ if (DmaControllers[1]) return true;
+ else return false;
+}
+
+static void DMA_Write_Port(Bitu port,Bitu val,Bitu /*iolen*/) {
+ //LOG(LOG_DMACONTROL,LOG_ERROR)("Write %X %X",port,val);
+ if (port<0x10) {
+ /* write to the first DMA controller (channels 0-3) */
+ DmaControllers[0]->WriteControllerReg(port,val,1);
+ } else if (port>=0xc0 && port <=0xdf) {
+ /* write to the second DMA controller (channels 4-7) */
+ DmaControllers[1]->WriteControllerReg((port-0xc0) >> 1,val,1);
+ } else {
+ UpdateEMSMapping();
+ switch (port) {
+ /* write DMA page register */
+ case 0x81:GetDMAChannel(2)->SetPage((Bit8u)val);break;
+ case 0x82:GetDMAChannel(3)->SetPage((Bit8u)val);break;
+ case 0x83:GetDMAChannel(1)->SetPage((Bit8u)val);break;
+ case 0x87:GetDMAChannel(0)->SetPage((Bit8u)val);break;
+ case 0x89:GetDMAChannel(6)->SetPage((Bit8u)val);break;
+ case 0x8a:GetDMAChannel(7)->SetPage((Bit8u)val);break;
+ case 0x8b:GetDMAChannel(5)->SetPage((Bit8u)val);break;
+ }
+ }
+}
+
+static Bitu DMA_Read_Port(Bitu port,Bitu iolen) {
+ //LOG(LOG_DMACONTROL,LOG_ERROR)("Read %X",port);
+ if (port<0x10) {
+ /* read from the first DMA controller (channels 0-3) */
+ return DmaControllers[0]->ReadControllerReg(port,iolen);
+ } else if (port>=0xc0 && port <=0xdf) {
+ /* read from the second DMA controller (channels 4-7) */
+ return DmaControllers[1]->ReadControllerReg((port-0xc0) >> 1,iolen);
+ } else switch (port) {
+ /* read DMA page register */
+ case 0x81:return GetDMAChannel(2)->pagenum;
+ case 0x82:return GetDMAChannel(3)->pagenum;
+ case 0x83:return GetDMAChannel(1)->pagenum;
+ case 0x87:return GetDMAChannel(0)->pagenum;
+ case 0x89:return GetDMAChannel(6)->pagenum;
+ case 0x8a:return GetDMAChannel(7)->pagenum;
+ case 0x8b:return GetDMAChannel(5)->pagenum;
+ }
+ return 0;
+}
+
+void DmaController::WriteControllerReg(Bitu reg,Bitu val,Bitu /*len*/) {
+ DmaChannel * chan;
+ switch (reg) {
+ /* set base address of DMA transfer (1st byte low part, 2nd byte high part) */
+ case 0x0:case 0x2:case 0x4:case 0x6:
+ UpdateEMSMapping();
+ chan=GetChannel((Bit8u)(reg >> 1));
+ flipflop=!flipflop;
+ if (flipflop) {
+ chan->baseaddr=(chan->baseaddr&0xff00)|val;
+ chan->curraddr=(chan->curraddr&0xff00)|val;
+ } else {
+ chan->baseaddr=(chan->baseaddr&0x00ff)|(val << 8);
+ chan->curraddr=(chan->curraddr&0x00ff)|(val << 8);
+ }
+ break;
+ /* set DMA transfer count (1st byte low part, 2nd byte high part) */
+ case 0x1:case 0x3:case 0x5:case 0x7:
+ UpdateEMSMapping();
+ chan=GetChannel((Bit8u)(reg >> 1));
+ flipflop=!flipflop;
+ if (flipflop) {
+ chan->basecnt=(chan->basecnt&0xff00)|val;
+ chan->currcnt=(chan->currcnt&0xff00)|val;
+ } else {
+ chan->basecnt=(chan->basecnt&0x00ff)|(val << 8);
+ chan->currcnt=(chan->currcnt&0x00ff)|(val << 8);
+ }
+ break;
+ case 0x8: /* Comand reg not used */
+ break;
+ case 0x9: /* Request registers, memory to memory */
+ //TODO Warning?
+ break;
+ case 0xa: /* Mask Register */
+ if ((val & 0x4)==0) UpdateEMSMapping();
+ chan=GetChannel(val & 3);
+ chan->SetMask((val & 0x4)>0);
+ break;
+ case 0xb: /* Mode Register */
+ UpdateEMSMapping();
+ chan=GetChannel(val & 3);
+ chan->autoinit=(val & 0x10) > 0;
+ chan->increment=(val & 0x20) > 0;
+ //TODO Maybe other bits?
+ break;
+ case 0xc: /* Clear Flip/Flip */
+ flipflop=false;
+ break;
+ case 0xd: /* Master Clear/Reset */
+ for (Bit8u ct=0;ct<4;ct++) {
+ chan=GetChannel(ct);
+ chan->SetMask(true);
+ chan->tcount=false;
+ }
+ flipflop=false;
+ break;
+ case 0xe: /* Clear Mask register */
+ UpdateEMSMapping();
+ for (Bit8u ct=0;ct<4;ct++) {
+ chan=GetChannel(ct);
+ chan->SetMask(false);
+ }
+ break;
+ case 0xf: /* Multiple Mask register */
+ UpdateEMSMapping();
+ for (Bit8u ct=0;ct<4;ct++) {
+ chan=GetChannel(ct);
+ chan->SetMask(val & 1);
+ val>>=1;
+ }
+ break;
+ }
+}
+
+Bitu DmaController::ReadControllerReg(Bitu reg,Bitu /*len*/) {
+ DmaChannel * chan;Bitu ret;
+ switch (reg) {
+ /* read base address of DMA transfer (1st byte low part, 2nd byte high part) */
+ case 0x0:case 0x2:case 0x4:case 0x6:
+ chan=GetChannel((Bit8u)(reg >> 1));
+ flipflop=!flipflop;
+ if (flipflop) {
+ return chan->curraddr & 0xff;
+ } else {
+ return (chan->curraddr >> 8) & 0xff;
+ }
+ /* read DMA transfer count (1st byte low part, 2nd byte high part) */
+ case 0x1:case 0x3:case 0x5:case 0x7:
+ chan=GetChannel((Bit8u)(reg >> 1));
+ flipflop=!flipflop;
+ if (flipflop) {
+ return chan->currcnt & 0xff;
+ } else {
+ return (chan->currcnt >> 8) & 0xff;
+ }
+ case 0x8: /* Status Register */
+ ret=0;
+ for (Bit8u ct=0;ct<4;ct++) {
+ chan=GetChannel(ct);
+ if (chan->tcount) ret|=1 << ct;
+ chan->tcount=false;
+ if (chan->request) ret|=1 << (4+ct);
+ }
+ return ret;
+ default:
+ LOG(LOG_DMACONTROL,LOG_NORMAL)("Trying to read undefined DMA port %x",reg);
+ break;
+ }
+ return 0xffffffff;
+}
+
+DmaChannel::DmaChannel(Bit8u num, bool dma16) {
+ masked = true;
+ callback = NULL;
+ if(num == 4) return;
+ channum = num;
+ DMA16 = dma16 ? 0x1 : 0x0;
+ pagenum = 0;
+ pagebase = 0;
+ baseaddr = 0;
+ curraddr = 0;
+ basecnt = 0;
+ currcnt = 0;
+ increment = true;
+ autoinit = false;
+ tcount = false;
+ request = false;
+}
+
+Bitu DmaChannel::Read(Bitu want, Bit8u * buffer) {
+ Bitu done=0;
+ curraddr &= dma_wrapping;
+again:
+ Bitu left=(currcnt+1);
+ if (want<left) {
+ DMA_BlockRead(pagebase,curraddr,buffer,want,DMA16);
+ done+=want;
+ curraddr+=want;
+ currcnt-=want;
+ } else {
+ DMA_BlockRead(pagebase,curraddr,buffer,want,DMA16);
+ buffer+=left << DMA16;
+ want-=left;
+ done+=left;
+ ReachedTC();
+ if (autoinit) {
+ currcnt=basecnt;
+ curraddr=baseaddr;
+ if (want) goto again;
+ UpdateEMSMapping();
+ } else {
+ curraddr+=left;
+ currcnt=0xffff;
+ masked=true;
+ UpdateEMSMapping();
+ DoCallBack(DMA_TRANSFEREND);
+ }
+ }
+ return done;
+}
+
+Bitu DmaChannel::Write(Bitu want, Bit8u * buffer) {
+ Bitu done=0;
+ curraddr &= dma_wrapping;
+again:
+ Bitu left=(currcnt+1);
+ if (want<left) {
+ DMA_BlockWrite(pagebase,curraddr,buffer,want,DMA16);
+ done+=want;
+ curraddr+=want;
+ currcnt-=want;
+ } else {
+ DMA_BlockWrite(pagebase,curraddr,buffer,left,DMA16);
+ buffer+=left << DMA16;
+ want-=left;
+ done+=left;
+ ReachedTC();
+ if (autoinit) {
+ currcnt=basecnt;
+ curraddr=baseaddr;
+ if (want) goto again;
+ UpdateEMSMapping();
+ } else {
+ curraddr+=left;
+ currcnt=0xffff;
+ masked=true;
+ UpdateEMSMapping();
+ DoCallBack(DMA_TRANSFEREND);
+ }
+ }
+ return done;
+}
+
+class DMA:public Module_base{
+public:
+ DMA(Section* configuration):Module_base(configuration){
+ Bitu i;
+ DmaControllers[0] = new DmaController(0);
+ if (IS_EGAVGA_ARCH) DmaControllers[1] = new DmaController(1);
+ else DmaControllers[1] = NULL;
+
+ for (i=0;i<0x10;i++) {
+ Bitu mask=IO_MB;
+ if (i<8) mask|=IO_MW;
+ /* install handler for first DMA controller ports */
+ DmaControllers[0]->DMA_WriteHandler[i].Install(i,DMA_Write_Port,mask);
+ DmaControllers[0]->DMA_ReadHandler[i].Install(i,DMA_Read_Port,mask);
+ if (IS_EGAVGA_ARCH) {
+ /* install handler for second DMA controller ports */
+ DmaControllers[1]->DMA_WriteHandler[i].Install(0xc0+i*2,DMA_Write_Port,mask);
+ DmaControllers[1]->DMA_ReadHandler[i].Install(0xc0+i*2,DMA_Read_Port,mask);
+ }
+ }
+ /* install handlers for ports 0x81-0x83 (on the first DMA controller) */
+ DmaControllers[0]->DMA_WriteHandler[0x10].Install(0x81,DMA_Write_Port,IO_MB,3);
+ DmaControllers[0]->DMA_ReadHandler[0x10].Install(0x81,DMA_Read_Port,IO_MB,3);
+ DmaControllers[0]->DMA_WriteHandler[0x11].Install(0x87,DMA_Write_Port,IO_MB,1);
+ DmaControllers[0]->DMA_ReadHandler[0x11].Install(0x87,DMA_Read_Port,IO_MB,1);
+
+ if (IS_EGAVGA_ARCH) {
+ /* install handlers for ports 0x89-0x8B (on the second DMA controller) */
+ DmaControllers[1]->DMA_WriteHandler[0x10].Install(0x89,DMA_Write_Port,IO_MB,3);
+ DmaControllers[1]->DMA_ReadHandler[0x10].Install(0x89,DMA_Read_Port,IO_MB,3);
+ }
+ }
+ ~DMA(){
+ if (DmaControllers[0]) {
+ delete DmaControllers[0];
+ DmaControllers[0]=NULL;
+ }
+ if (DmaControllers[1]) {
+ delete DmaControllers[1];
+ DmaControllers[1]=NULL;
+ }
+ }
+};
+
+void DMA_SetWrapping(Bitu wrap) {
+ dma_wrapping = wrap;
+}
+
+static DMA* test;
+
+void DMA_Destroy(Section* /*sec*/){
+ delete test;
+}
+void DMA_Init(Section* sec) {
+ DMA_SetWrapping(0xffff);
+ test = new DMA(sec);
+ sec->AddDestroyFunction(&DMA_Destroy);
+ Bitu i;
+ for (i=0;i<LINK_START;i++) {
+ ems_board_mapping[i]=i;
+ }
+}
diff --git a/src/hardware/gameblaster.cpp b/src/hardware/gameblaster.cpp
new file mode 100644
index 000000000..c602c0a0e
--- /dev/null
+++ b/src/hardware/gameblaster.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include "dosbox.h"
+#include "inout.h"
+#include "mixer.h"
+#include "mem.h"
+#include "hardware.h"
+#include "setup.h"
+#include "support.h"
+#include "pic.h"
+#include <cstring>
+#include <math.h>
+
+#include "mame/emu.h"
+#include "mame/saa1099.h"
+
+#define MASTER_CLOCK 7159090
+
+//My mixer channel
+static MixerChannel * cms_chan;
+//Timer to disable the channel after a while
+static Bit32u lastWriteTicks;
+static Bit32u cmsBase;
+static saa1099_device* device[2];
+
+static void write_cms(Bitu port, Bitu val, Bitu /* iolen */) {
+ if(cms_chan && (!cms_chan->enabled)) cms_chan->Enable(true);
+ lastWriteTicks = PIC_Ticks;
+ switch ( port - cmsBase ) {
+ case 1:
+ device[0]->control_w(0, 0, val);
+ break;
+ case 0:
+ device[0]->data_w(0, 0, val);
+ break;
+ case 3:
+ device[1]->control_w(0, 0, val);
+ break;
+ case 2:
+ device[1]->data_w(0, 0, val);
+ break;
+ }
+}
+
+static void CMS_CallBack(Bitu len) {
+ enum {
+ BUFFER_SIZE = 2048
+ };
+
+ if ( len > BUFFER_SIZE )
+ return;
+
+ if ( cms_chan ) {
+
+ //Have there been 10 seconds of no commands, disable channel
+ if ( lastWriteTicks + 10000 < PIC_Ticks ) {
+ cms_chan->Enable( false );
+ return;
+ }
+ Bit32s result[BUFFER_SIZE][2];
+ Bit16s work[2][BUFFER_SIZE];
+ Bit16s* buffers[2] = { work[0], work[1] };
+ device_sound_interface::sound_stream stream;
+ device[0]->sound_stream_update(stream, 0, buffers, len);
+ for (Bitu i = 0; i < len; i++) {
+ result[i][0] = work[0][i];
+ result[i][1] = work[1][i];
+ }
+ device[1]->sound_stream_update(stream, 0, buffers, len);
+ for (Bitu i = 0; i < len; i++) {
+ result[i][0] += work[0][i];
+ result[i][1] += work[1][i];
+ }
+ cms_chan->AddSamples_s32( len, result[0] );
+ }
+}
+
+// The Gameblaster detection
+static Bit8u cms_detect_register = 0xff;
+
+static void write_cms_detect(Bitu port, Bitu val, Bitu /* iolen */) {
+ switch ( port - cmsBase ) {
+ case 0x6:
+ case 0x7:
+ cms_detect_register = val;
+ break;
+ }
+}
+
+static Bitu read_cms_detect(Bitu port, Bitu /* iolen */) {
+ Bit8u retval = 0xff;
+ switch ( port - cmsBase ) {
+ case 0x4:
+ retval = 0x7f;
+ break;
+ case 0xa:
+ case 0xb:
+ retval = cms_detect_register;
+ break;
+ }
+ return retval;
+}
+
+
+class CMS:public Module_base {
+private:
+ IO_WriteHandleObject WriteHandler;
+ IO_WriteHandleObject DetWriteHandler;
+ IO_ReadHandleObject DetReadHandler;
+ MixerObject MixerChan;
+
+public:
+ CMS(Section* configuration):Module_base(configuration) {
+ Section_prop * section = static_cast<Section_prop *>(configuration);
+ Bitu sampleRate = section->Get_int( "oplrate" );
+ cmsBase = section->Get_hex("sbbase");
+ WriteHandler.Install( cmsBase, write_cms, IO_MB, 4 );
+
+ // A standalone Gameblaster has a magic chip on it which is
+ // sometimes used for detection.
+ const char * sbtype=section->Get_string("sbtype");
+ if (!strcasecmp(sbtype,"gb")) {
+ DetWriteHandler.Install( cmsBase + 4, write_cms_detect, IO_MB, 12 );
+ DetReadHandler.Install(cmsBase,read_cms_detect,IO_MB,16);
+ }
+
+ /* Register the Mixer CallBack */
+ cms_chan = MixerChan.Install(CMS_CallBack,sampleRate,"CMS");
+
+ lastWriteTicks = PIC_Ticks;
+
+ Bit32u freq = 7159000; //14318180 isa clock / 2
+
+ machine_config config;
+ device[0] = new saa1099_device(config, "", 0, 7159090);
+ device[1] = new saa1099_device(config, "", 0, 7159090);
+
+ device[0]->device_start();
+ device[1]->device_start();
+ }
+
+ ~CMS() {
+ cms_chan = 0;
+ delete device[0];
+ delete device[1];
+ }
+};
+
+
+static CMS* test;
+
+void CMS_Init(Section* sec) {
+ test = new CMS(sec);
+}
+void CMS_ShutDown(Section* sec) {
+ delete test;
+}
diff --git a/src/hardware/gus.cpp b/src/hardware/gus.cpp
new file mode 100644
index 000000000..d1d7e4242
--- /dev/null
+++ b/src/hardware/gus.cpp
@@ -0,0 +1,923 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <iomanip>
+#include <sstream>
+#include "dosbox.h"
+#include "inout.h"
+#include "mixer.h"
+#include "dma.h"
+#include "pic.h"
+#include "setup.h"
+#include "shell.h"
+#include "math.h"
+#include "regs.h"
+using namespace std;
+
+//Extra bits of precision over normal gus
+#define WAVE_FRACT 9
+#define WAVE_FRACT_MASK ((1 << WAVE_FRACT)-1)
+#define WAVE_MSWMASK ((1 << 16)-1)
+#define WAVE_LSWMASK (0xffffffff ^ WAVE_MSWMASK)
+
+//Amount of precision the volume has
+#define RAMP_FRACT (10)
+#define RAMP_FRACT_MASK ((1 << RAMP_FRACT)-1)
+
+#define GUS_BASE myGUS.portbase
+#define GUS_RATE myGUS.rate
+#define LOG_GUS 0
+
+#define VOL_SHIFT 14
+
+#define WCTRL_STOPPED 0x01
+#define WCTRL_STOP 0x02
+#define WCTRL_16BIT 0x04
+#define WCTRL_LOOP 0x08
+#define WCTRL_BIDIRECTIONAL 0x10
+#define WCTRL_IRQENABLED 0x20
+#define WCTRL_DECREASING 0x40
+#define WCTRL_IRQPENDING 0x80
+
+Bit8u adlib_commandreg;
+static MixerChannel * gus_chan;
+static Bit8u irqtable[8] = { 0, 2, 5, 3, 7, 11, 12, 15 };
+static Bit8u dmatable[8] = { 0, 1, 3, 5, 6, 7, 0, 0 };
+static Bit8u GUSRam[1024*1024]; // 1024K of GUS Ram
+static Bit16u vol16bit[4096];
+static Bit32u pantable[16];
+
+class GUSChannels;
+static void CheckVoiceIrq(void);
+
+struct GFGus {
+ Bit8u gRegSelect;
+ Bit16u gRegData;
+ Bit32u gDramAddr;
+ Bit16u gCurChannel;
+
+ Bit8u DMAControl;
+ Bit16u dmaAddr;
+ Bit8u TimerControl;
+ Bit8u SampControl;
+ Bit8u mixControl;
+ Bit8u ActiveChannels;
+ Bit32u basefreq;
+
+ struct GusTimer {
+ Bit8u value;
+ bool reached;
+ bool raiseirq;
+ bool masked;
+ bool running;
+ float delay;
+ } timers[2];
+ Bit32u rate;
+ Bitu portbase;
+ Bit8u dma1;
+ Bit8u dma2;
+
+ Bit8u irq1;
+ Bit8u irq2;
+
+ bool irqenabled;
+ bool ChangeIRQDMA;
+ // IRQ status register values
+ Bit8u IRQStatus;
+ Bit32u ActiveMask;
+ Bit8u IRQChan;
+ Bit32u RampIRQ;
+ Bit32u WaveIRQ;
+} myGUS;
+
+Bitu DEBUG_EnableDebugger(void);
+
+static void GUS_DMA_Callback(DmaChannel * chan,DMAEvent event);
+
+class GUSChannels {
+public:
+ Bit32u WaveStart;
+ Bit32u WaveEnd;
+ Bit32u WaveAddr;
+ Bit32u WaveAdd;
+ Bit8u WaveCtrl;
+ Bit16u WaveFreq;
+
+ Bit32u RampStart;
+ Bit32u RampEnd;
+ Bit32u RampVol;
+ Bit32u RampAdd;
+ Bit32u RampAddReal;
+
+ Bit8u RampRate;
+ Bit8u RampCtrl;
+
+ Bit8u PanPot;
+ Bit8u channum;
+ Bit32u irqmask;
+ Bit32u PanLeft;
+ Bit32u PanRight;
+ Bit32s VolLeft;
+ Bit32s VolRight;
+
+ GUSChannels(Bit8u num) {
+ channum = num;
+ irqmask = 1 << num;
+ WaveStart = 0;
+ WaveEnd = 0;
+ WaveAddr = 0;
+ WaveAdd = 0;
+ WaveFreq = 0;
+ WaveCtrl = 3;
+ RampRate = 0;
+ RampStart = 0;
+ RampEnd = 0;
+ RampCtrl = 3;
+ RampAdd = 0;
+ RampVol = 0;
+ VolLeft = 0;
+ VolRight = 0;
+ PanLeft = 0;
+ PanRight = 0;
+ PanPot = 0x7;
+ }
+
+ // Returns a single 16-bit sample from the Gravis's RAM
+
+ INLINE Bit32s GetSample8() const {
+ Bit32u useAddr = WaveAddr >> WAVE_FRACT;
+ if (WaveAdd >= (1 << WAVE_FRACT)) {
+ Bit32s tmpsmall = (Bit8s)GUSRam[useAddr];
+ return tmpsmall << 8;
+ }
+ else {
+ Bit32u nextAddr = (useAddr + 1) & ( 1024 * 1024 - 1 );
+ // Interpolate
+ Bit32s w1 = ((Bit8s)GUSRam[useAddr]) << 8;
+ Bit32s w2 = ((Bit8s)GUSRam[nextAddr]) << 8;
+ Bit32s diff = w2 - w1;
+ Bit32s scale = (Bit32s)(WaveAddr&WAVE_FRACT_MASK);
+ return (w1 + ((diff*scale) >> WAVE_FRACT));
+ }
+ }
+
+ INLINE Bit32s GetSample16() const {
+ Bit32u useAddr = WaveAddr >> WAVE_FRACT;
+ // Formula used to convert addresses for use with 16-bit samples
+ Bit32u holdAddr = useAddr & 0xc0000L;
+ useAddr = useAddr & 0x1ffffL;
+ useAddr = useAddr << 1;
+ useAddr = (holdAddr | useAddr);
+ if (WaveAdd >= (1 << WAVE_FRACT)) {
+ return (GUSRam[useAddr + 0] | (((Bit8s)GUSRam[useAddr + 1]) << 8));
+ }
+ else {
+ // Interpolate
+ Bit32s w1 = (GUSRam[useAddr + 0] | (((Bit8s)GUSRam[useAddr + 1]) << 8));
+ Bit32s w2 = (GUSRam[useAddr + 2] | (((Bit8s)GUSRam[useAddr + 3]) << 8));
+ Bit32s diff = w2 - w1;
+ Bit32s scale = (Bit32s)(WaveAddr&WAVE_FRACT_MASK);
+ return (w1 + ((diff*scale) >> WAVE_FRACT));
+ }
+ }
+
+ void WriteWaveFreq(Bit16u val) {
+ WaveFreq = val;
+ double frameadd = double(val >> 1)/512.0; //Samples / original gus frame
+ double realadd = (frameadd*(double)myGUS.basefreq/(double)GUS_RATE) * (double)(1 << WAVE_FRACT);
+ WaveAdd = (Bit32u)realadd;
+ }
+ void WriteWaveCtrl(Bit8u val) {
+ Bit32u oldirq=myGUS.WaveIRQ;
+ WaveCtrl = val & 0x7f;
+ if ((val & 0xa0)==0xa0) myGUS.WaveIRQ|=irqmask;
+ else myGUS.WaveIRQ&=~irqmask;
+ if (oldirq != myGUS.WaveIRQ)
+ CheckVoiceIrq();
+ }
+ INLINE Bit8u ReadWaveCtrl(void) {
+ Bit8u ret=WaveCtrl;
+ if (myGUS.WaveIRQ & irqmask) ret|=0x80;
+ return ret;
+ }
+ void UpdateWaveRamp(void) {
+ WriteWaveFreq(WaveFreq);
+ WriteRampRate(RampRate);
+ }
+ void WritePanPot(Bit8u val) {
+ PanPot = val;
+ PanLeft = pantable[0x0f-(val & 0xf)];
+ PanRight = pantable[(val & 0xf)];
+ UpdateVolumes();
+ }
+ Bit8u ReadPanPot(void) {
+ return PanPot;
+ }
+ void WriteRampCtrl(Bit8u val) {
+ Bit32u old=myGUS.RampIRQ;
+ RampCtrl = val & 0x7f;
+ //Manually set the irq
+ if ((val & 0xa0)==0xa0)
+ myGUS.RampIRQ|=irqmask;
+ else
+ myGUS.RampIRQ&=~irqmask;
+ if (old != myGUS.RampIRQ)
+ CheckVoiceIrq();
+ }
+ INLINE Bit8u ReadRampCtrl(void) {
+ Bit8u ret=RampCtrl;
+ if (myGUS.RampIRQ & irqmask) ret |= 0x80;
+ return ret;
+ }
+ void WriteRampRate(Bit8u val) {
+ RampRate = val;
+ double frameadd = (double)(RampRate & 63)/(double)(1 << (3*(val >> 6)));
+ double realadd = (frameadd*(double)myGUS.basefreq/(double)GUS_RATE) * (double)(1 << RAMP_FRACT);
+ RampAdd = (Bit32u)realadd;
+ }
+ INLINE void WaveUpdate(void) {
+ if (WaveCtrl & ( WCTRL_STOP | WCTRL_STOPPED)) return;
+ Bit32s WaveLeft;
+ if (WaveCtrl & WCTRL_DECREASING) {
+ WaveAddr -= WaveAdd;
+ WaveLeft = WaveStart-WaveAddr;
+ } else {
+ WaveAddr += WaveAdd;
+ WaveLeft = WaveAddr-WaveEnd;
+ }
+ //Not yet reaching a boundary
+ if (WaveLeft<0)
+ return;
+ /* Generate an IRQ if needed */
+ if (WaveCtrl & 0x20) {
+ myGUS.WaveIRQ|=irqmask;
+ }
+ /* Check for not being in PCM operation */
+ if (RampCtrl & 0x04) return;
+ /* Check for looping */
+ if (WaveCtrl & WCTRL_LOOP) {
+ /* Bi-directional looping */
+ if (WaveCtrl & WCTRL_BIDIRECTIONAL) WaveCtrl^= WCTRL_DECREASING;
+ WaveAddr = (WaveCtrl & WCTRL_DECREASING) ? (WaveEnd-WaveLeft) : (WaveStart+WaveLeft);
+ } else {
+ WaveCtrl|=1; //Stop the channel
+ WaveAddr = (WaveCtrl & WCTRL_DECREASING) ? WaveStart : WaveEnd;
+ }
+ }
+ INLINE void UpdateVolumes(void) {
+ Bit32s templeft=RampVol - PanLeft;
+ templeft&=~(templeft >> 31);
+ Bit32s tempright=RampVol - PanRight;
+ tempright&=~(tempright >> 31);
+ VolLeft=vol16bit[templeft >> RAMP_FRACT];
+ VolRight=vol16bit[tempright >> RAMP_FRACT];
+ }
+ INLINE void RampUpdate(void) {
+ /* Check if ramping enabled */
+ if (RampCtrl & 0x3) return;
+ Bit32s RampLeft;
+ if (RampCtrl & 0x40) {
+ RampVol-=RampAdd;
+ RampLeft=RampStart-RampVol;
+ } else {
+ RampVol+=RampAdd;
+ RampLeft=RampVol-RampEnd;
+ }
+ if (RampLeft<0) {
+ UpdateVolumes();
+ return;
+ }
+ /* Generate an IRQ if needed */
+ if (RampCtrl & 0x20) {
+ myGUS.RampIRQ|=irqmask;
+ }
+ /* Check for looping */
+ if (RampCtrl & 0x08) {
+ /* Bi-directional looping */
+ if (RampCtrl & 0x10) RampCtrl^=0x40;
+ RampVol = (RampCtrl & 0x40) ? (RampEnd-RampLeft) : (RampStart+RampLeft);
+ } else {
+ RampCtrl|=1; //Stop the channel
+ RampVol = (RampCtrl & 0x40) ? RampStart : RampEnd;
+ }
+ UpdateVolumes();
+ }
+
+ void generateSamples(Bit32s * stream,Bit32u len) {
+ //Disabled channel
+ if (RampCtrl & WaveCtrl & 3) return;
+
+ if (WaveCtrl & WCTRL_16BIT) {
+ for (int i = 0; i < (int)len; i++) {
+ // Get sample
+ Bit32s tmpsamp = GetSample16();
+ // Output stereo sample
+ stream[i << 1] += tmpsamp * VolLeft;
+ stream[(i << 1) + 1] += tmpsamp * VolRight;
+ WaveUpdate();
+ RampUpdate();
+ }
+ }
+ else {
+ for (int i = 0; i < (int)len; i++) {
+ // Get sample
+ Bit32s tmpsamp = GetSample8();
+ // Output stereo sample
+ stream[i << 1] += tmpsamp * VolLeft;
+ stream[(i << 1) + 1] += tmpsamp * VolRight;
+ WaveUpdate();
+ RampUpdate();
+ }
+ }
+ }
+};
+
+static GUSChannels *guschan[32];
+static GUSChannels *curchan;
+
+static void GUSReset(void) {
+ if((myGUS.gRegData & 0x1) == 0x1) {
+ // Reset
+ adlib_commandreg = 85;
+ myGUS.IRQStatus = 0;
+ myGUS.timers[0].raiseirq = false;
+ myGUS.timers[1].raiseirq = false;
+ myGUS.timers[0].reached = false;
+ myGUS.timers[1].reached = false;
+ myGUS.timers[0].running = false;
+ myGUS.timers[1].running = false;
+
+ myGUS.timers[0].value = 0xff;
+ myGUS.timers[1].value = 0xff;
+ myGUS.timers[0].delay = 0.080f;
+ myGUS.timers[1].delay = 0.320f;
+
+ myGUS.ChangeIRQDMA = false;
+ myGUS.mixControl = 0x0b; // latches enabled by default LINEs disabled
+ // Stop all channels
+ int i;
+ for(i=0;i<32;i++) {
+ guschan[i]->RampVol=0;
+ guschan[i]->WriteWaveCtrl(0x1);
+ guschan[i]->WriteRampCtrl(0x1);
+ guschan[i]->WritePanPot(0x7);
+ }
+ myGUS.IRQChan = 0;
+ }
+ if ((myGUS.gRegData & 0x4) != 0) {
+ myGUS.irqenabled = true;
+ } else {
+ myGUS.irqenabled = false;
+ }
+}
+
+static INLINE void GUS_CheckIRQ(void) {
+ if (myGUS.IRQStatus && (myGUS.mixControl & 0x08))
+ PIC_ActivateIRQ(myGUS.irq1);
+
+}
+
+static void CheckVoiceIrq(void) {
+ myGUS.IRQStatus&=0x9f;
+ Bitu totalmask=(myGUS.RampIRQ|myGUS.WaveIRQ) & myGUS.ActiveMask;
+ if (!totalmask) return;
+ if (myGUS.RampIRQ) myGUS.IRQStatus|=0x40;
+ if (myGUS.WaveIRQ) myGUS.IRQStatus|=0x20;
+ GUS_CheckIRQ();
+ for (;;) {
+ Bit32u check=(1 << myGUS.IRQChan);
+ if (totalmask & check) return;
+ myGUS.IRQChan++;
+ if (myGUS.IRQChan>=myGUS.ActiveChannels) myGUS.IRQChan=0;
+ }
+}
+
+static Bit16u ExecuteReadRegister(void) {
+ Bit8u tmpreg;
+// LOG_MSG("Read global reg %x",myGUS.gRegSelect);
+ switch (myGUS.gRegSelect) {
+ case 0x41: // Dma control register - read acknowledges DMA IRQ
+ tmpreg = myGUS.DMAControl & 0xbf;
+ tmpreg |= (myGUS.IRQStatus & 0x80) >> 1;
+ myGUS.IRQStatus&=0x7f;
+ return (Bit16u)(tmpreg << 8);
+ case 0x42: // Dma address register
+ return myGUS.dmaAddr;
+ case 0x45: // Timer control register. Identical in operation to Adlib's timer
+ return (Bit16u)(myGUS.TimerControl << 8);
+ break;
+ case 0x49: // Dma sample register
+ tmpreg = myGUS.DMAControl & 0xbf;
+ tmpreg |= (myGUS.IRQStatus & 0x80) >> 1;
+ return (Bit16u)(tmpreg << 8);
+ case 0x80: // Channel voice control read register
+ if (curchan) return curchan->ReadWaveCtrl() << 8;
+ else return 0x0300;
+
+ case 0x82: // Channel MSB start address register
+ if (curchan) return (Bit16u)(curchan->WaveStart >> 16);
+ else return 0x0000;
+ case 0x83: // Channel LSW start address register
+ if (curchan) return (Bit16u)(curchan->WaveStart );
+ else return 0x0000;
+
+ case 0x89: // Channel volume register
+ if (curchan) return (Bit16u)((curchan->RampVol >> RAMP_FRACT) << 4);
+ else return 0x0000;
+ case 0x8a: // Channel MSB current address register
+ if (curchan) return (Bit16u)(curchan->WaveAddr >> 16);
+ else return 0x0000;
+ case 0x8b: // Channel LSW current address register
+ if (curchan) return (Bit16u)(curchan->WaveAddr );
+ else return 0x0000;
+
+ case 0x8d: // Channel volume control register
+ if (curchan) return curchan->ReadRampCtrl() << 8;
+ else return 0x0300;
+ case 0x8f: // General channel IRQ status register
+ tmpreg=myGUS.IRQChan|0x20;
+ Bit32u mask;
+ mask=1 << myGUS.IRQChan;
+ if (!(myGUS.RampIRQ & mask)) tmpreg|=0x40;
+ if (!(myGUS.WaveIRQ & mask)) tmpreg|=0x80;
+ myGUS.RampIRQ&=~mask;
+ myGUS.WaveIRQ&=~mask;
+ CheckVoiceIrq();
+ return (Bit16u)(tmpreg << 8);
+ default:
+#if LOG_GUS
+ LOG_MSG("Read Register num 0x%x", myGUS.gRegSelect);
+#endif
+ return myGUS.gRegData;
+ }
+}
+
+static void GUS_TimerEvent(Bitu val) {
+ if (!myGUS.timers[val].masked) myGUS.timers[val].reached=true;
+ if (myGUS.timers[val].raiseirq) {
+ myGUS.IRQStatus|=0x4 << val;
+ GUS_CheckIRQ();
+ }
+ if (myGUS.timers[val].running)
+ PIC_AddEvent(GUS_TimerEvent,myGUS.timers[val].delay,val);
+}
+
+
+static void ExecuteGlobRegister(void) {
+ int i;
+// if (myGUS.gRegSelect|1!=0x44) LOG_MSG("write global register %x with %x", myGUS.gRegSelect, myGUS.gRegData);
+ switch(myGUS.gRegSelect) {
+ case 0x0: // Channel voice control register
+ if(curchan) curchan->WriteWaveCtrl((Bit16u)myGUS.gRegData>>8);
+ break;
+ case 0x1: // Channel frequency control register
+ if(curchan) curchan->WriteWaveFreq(myGUS.gRegData);
+ break;
+ case 0x2: // Channel MSW start address register
+ if (curchan) {
+ Bit32u tmpaddr = (Bit32u)((myGUS.gRegData & 0x1fff) << 16);
+ curchan->WaveStart = (curchan->WaveStart & WAVE_MSWMASK) | tmpaddr;
+ }
+ break;
+ case 0x3: // Channel LSW start address register
+ if(curchan != NULL) {
+ Bit32u tmpaddr = (Bit32u)(myGUS.gRegData);
+ curchan->WaveStart = (curchan->WaveStart & WAVE_LSWMASK) | tmpaddr;
+ }
+ break;
+ case 0x4: // Channel MSW end address register
+ if(curchan != NULL) {
+ Bit32u tmpaddr = (Bit32u)(myGUS.gRegData & 0x1fff) << 16;
+ curchan->WaveEnd = (curchan->WaveEnd & WAVE_MSWMASK) | tmpaddr;
+ }
+ break;
+ case 0x5: // Channel MSW end address register
+ if(curchan != NULL) {
+ Bit32u tmpaddr = (Bit32u)(myGUS.gRegData);
+ curchan->WaveEnd = (curchan->WaveEnd & WAVE_LSWMASK) | tmpaddr;
+ }
+ break;
+ case 0x6: // Channel volume ramp rate register
+ if(curchan != NULL) {
+ Bit8u tmpdata = (Bit16u)myGUS.gRegData>>8;
+ curchan->WriteRampRate(tmpdata);
+ }
+ break;
+ case 0x7: // Channel volume ramp start register EEEEMMMM
+ if(curchan != NULL) {
+ Bit8u tmpdata = (Bit16u)myGUS.gRegData >> 8;
+ curchan->RampStart = tmpdata << (4+RAMP_FRACT);
+ }
+ break;
+ case 0x8: // Channel volume ramp end register EEEEMMMM
+ if(curchan != NULL) {
+ Bit8u tmpdata = (Bit16u)myGUS.gRegData >> 8;
+ curchan->RampEnd = tmpdata << (4+RAMP_FRACT);
+ }
+ break;
+ case 0x9: // Channel current volume register
+ if(curchan != NULL) {
+ Bit16u tmpdata = (Bit16u)myGUS.gRegData >> 4;
+ curchan->RampVol = tmpdata << RAMP_FRACT;
+ curchan->UpdateVolumes();
+ }
+ break;
+ case 0xA: // Channel MSW current address register
+ if(curchan != NULL) {
+ Bit32u tmpaddr = (Bit32u)(myGUS.gRegData & 0x1fff) << 16;
+ curchan->WaveAddr = (curchan->WaveAddr & WAVE_MSWMASK) | tmpaddr;
+ }
+ break;
+ case 0xB: // Channel LSW current address register
+ if(curchan != NULL) {
+ Bit32u tmpaddr = (Bit32u)(myGUS.gRegData);
+ curchan->WaveAddr = (curchan->WaveAddr & WAVE_LSWMASK) | tmpaddr;
+ }
+ break;
+ case 0xC: // Channel pan pot register
+ if(curchan) curchan->WritePanPot((Bit16u)myGUS.gRegData>>8);
+ break;
+ case 0xD: // Channel volume control register
+ if(curchan) curchan->WriteRampCtrl((Bit16u)myGUS.gRegData>>8);
+ break;
+ case 0xE: // Set active channel register
+ myGUS.gRegSelect = myGUS.gRegData>>8; //JAZZ Jackrabbit seems to assume this?
+ myGUS.ActiveChannels = 1+((myGUS.gRegData>>8) & 63);
+ if(myGUS.ActiveChannels < 14) myGUS.ActiveChannels = 14;
+ if(myGUS.ActiveChannels > 32) myGUS.ActiveChannels = 32;
+ myGUS.ActiveMask=0xffffffffU >> (32-myGUS.ActiveChannels);
+ gus_chan->Enable(true);
+ myGUS.basefreq = (Bit32u)(0.5 + 1000000.0/(1.619695497*(double)(myGUS.ActiveChannels)));
+#if LOG_GUS
+ LOG_MSG("GUS set to %d channels, freq %d", myGUS.ActiveChannels, myGUS.basefreq);
+#endif
+ for (i=0;i<myGUS.ActiveChannels;i++) guschan[i]->UpdateWaveRamp();
+ break;
+ case 0x10: // Undocumented register used in Fast Tracker 2
+ break;
+ case 0x41: // Dma control register
+ myGUS.DMAControl = (Bit8u)(myGUS.gRegData>>8);
+ GetDMAChannel(myGUS.dma1)->Register_Callback(
+ (myGUS.DMAControl & 0x1) ? GUS_DMA_Callback : 0);
+ break;
+ case 0x42: // Gravis DRAM DMA address register
+ myGUS.dmaAddr = myGUS.gRegData;
+ break;
+ case 0x43: // MSB Peek/poke DRAM position
+ myGUS.gDramAddr = (0xff0000 & myGUS.gDramAddr) | ((Bit32u)myGUS.gRegData);
+ break;
+ case 0x44: // LSW Peek/poke DRAM position
+ myGUS.gDramAddr = (0xffff & myGUS.gDramAddr) | ((Bit32u)myGUS.gRegData>>8) << 16;
+ break;
+ case 0x45: // Timer control register. Identical in operation to Adlib's timer
+ myGUS.TimerControl = (Bit8u)(myGUS.gRegData>>8);
+ myGUS.timers[0].raiseirq=(myGUS.TimerControl & 0x04)>0;
+ if (!myGUS.timers[0].raiseirq) myGUS.IRQStatus&=~0x04;
+ myGUS.timers[1].raiseirq=(myGUS.TimerControl & 0x08)>0;
+ if (!myGUS.timers[1].raiseirq) myGUS.IRQStatus&=~0x08;
+ break;
+ case 0x46: // Timer 1 control
+ myGUS.timers[0].value = (Bit8u)(myGUS.gRegData>>8);
+ myGUS.timers[0].delay = (0x100 - myGUS.timers[0].value) * 0.080f;
+ break;
+ case 0x47: // Timer 2 control
+ myGUS.timers[1].value = (Bit8u)(myGUS.gRegData>>8);
+ myGUS.timers[1].delay = (0x100 - myGUS.timers[1].value) * 0.320f;
+ break;
+ case 0x49: // DMA sampling control register
+ myGUS.SampControl = (Bit8u)(myGUS.gRegData>>8);
+ GetDMAChannel(myGUS.dma1)->Register_Callback(
+ (myGUS.SampControl & 0x1) ? GUS_DMA_Callback : 0);
+ break;
+ case 0x4c: // GUS reset register
+ GUSReset();
+ break;
+ default:
+#if LOG_GUS
+ LOG_MSG("Unimplemented global register %x -- %x", myGUS.gRegSelect, myGUS.gRegData);
+#endif
+ break;
+ }
+ return;
+}
+
+
+static Bitu read_gus(Bitu port,Bitu iolen) {
+// LOG_MSG("read from gus port %x",port);
+ switch(port - GUS_BASE) {
+ case 0x206:
+ return myGUS.IRQStatus;
+ case 0x208:
+ Bit8u tmptime;
+ tmptime = 0;
+ if (myGUS.timers[0].reached) tmptime |= (1 << 6);
+ if (myGUS.timers[1].reached) tmptime |= (1 << 5);
+ if (tmptime & 0x60) tmptime |= (1 << 7);
+ if (myGUS.IRQStatus & 0x04) tmptime|=(1 << 2);
+ if (myGUS.IRQStatus & 0x08) tmptime|=(1 << 1);
+ return tmptime;
+ case 0x20a:
+ return adlib_commandreg;
+ case 0x302:
+ return (Bit8u)myGUS.gCurChannel;
+ case 0x303:
+ return myGUS.gRegSelect;
+ case 0x304:
+ if (iolen==2) return ExecuteReadRegister() & 0xffff;
+ else return ExecuteReadRegister() & 0xff;
+ case 0x305:
+ return ExecuteReadRegister() >> 8;
+ case 0x307:
+ if(myGUS.gDramAddr < sizeof(GUSRam)) {
+ return GUSRam[myGUS.gDramAddr];
+ } else {
+ return 0;
+ }
+ default:
+#if LOG_GUS
+ LOG_MSG("Read GUS at port 0x%x", port);
+#endif
+ break;
+ }
+
+ return 0xff;
+}
+
+
+static void write_gus(Bitu port,Bitu val,Bitu iolen) {
+// LOG_MSG("Write gus port %x val %x",port,val);
+ switch(port - GUS_BASE) {
+ case 0x200:
+ myGUS.mixControl = (Bit8u)val;
+ myGUS.ChangeIRQDMA = true;
+ return;
+ case 0x208:
+ adlib_commandreg = (Bit8u)val;
+ break;
+ case 0x209:
+//TODO adlib_commandreg should be 4 for this to work else it should just latch the value
+ if (val & 0x80) {
+ myGUS.timers[0].reached=false;
+ myGUS.timers[1].reached=false;
+ return;
+ }
+ myGUS.timers[0].masked=(val & 0x40)>0;
+ myGUS.timers[1].masked=(val & 0x20)>0;
+ if (val & 0x1) {
+ if (!myGUS.timers[0].running) {
+ PIC_AddEvent(GUS_TimerEvent,myGUS.timers[0].delay,0);
+ myGUS.timers[0].running=true;
+ }
+ } else myGUS.timers[0].running=false;
+ if (val & 0x2) {
+ if (!myGUS.timers[1].running) {
+ PIC_AddEvent(GUS_TimerEvent,myGUS.timers[1].delay,1);
+ myGUS.timers[1].running=true;
+ }
+ } else myGUS.timers[1].running=false;
+ break;
+//TODO Check if 0x20a register is also available on the gus like on the interwave
+ case 0x20b:
+ if (!myGUS.ChangeIRQDMA) break;
+ myGUS.ChangeIRQDMA=false;
+ if (myGUS.mixControl & 0x40) {
+ // IRQ configuration, only use low bits for irq 1
+ if (irqtable[val & 0x7]) myGUS.irq1=irqtable[val & 0x7];
+#if LOG_GUS
+ LOG_MSG("Assigned GUS to IRQ %d", myGUS.irq1);
+#endif
+ } else {
+ // DMA configuration, only use low bits for dma 1
+ if (dmatable[val & 0x7]) myGUS.dma1=dmatable[val & 0x7];
+#if LOG_GUS
+ LOG_MSG("Assigned GUS to DMA %d", myGUS.dma1);
+#endif
+ }
+ break;
+ case 0x302:
+ myGUS.gCurChannel = val & 31 ;
+ curchan = guschan[myGUS.gCurChannel];
+ break;
+ case 0x303:
+ myGUS.gRegSelect = (Bit8u)val;
+ myGUS.gRegData = 0;
+ break;
+ case 0x304:
+ if (iolen==2) {
+ myGUS.gRegData=(Bit16u)val;
+ ExecuteGlobRegister();
+ } else myGUS.gRegData = (Bit16u)val;
+ break;
+ case 0x305:
+ myGUS.gRegData = (Bit16u)((0x00ff & myGUS.gRegData) | val << 8);
+ ExecuteGlobRegister();
+ break;
+ case 0x307:
+ if(myGUS.gDramAddr < sizeof(GUSRam)) GUSRam[myGUS.gDramAddr] = (Bit8u)val;
+ break;
+ default:
+#if LOG_GUS
+ LOG_MSG("Write GUS at port 0x%x with %x", port, val);
+#endif
+ break;
+ }
+}
+
+static void GUS_DMA_Callback(DmaChannel * chan,DMAEvent event) {
+ if (event!=DMA_UNMASKED) return;
+ Bitu dmaaddr;
+ //Calculate the dma address
+ //DMA transfers can't cross 256k boundaries, so you should be safe to just determine the start once and go from there
+ //Bit 2 - 0 = if DMA channel is an 8 bit channel(0 - 3).
+ if (myGUS.DMAControl & 0x4)
+ dmaaddr = (((myGUS.dmaAddr & 0x1fff) << 1) | (myGUS.dmaAddr & 0xc000)) << 4;
+ else
+ dmaaddr = myGUS.dmaAddr << 4;
+ //Reading from dma?
+ if((myGUS.DMAControl & 0x2) == 0) {
+ Bitu read=chan->Read(chan->currcnt+1,&GUSRam[dmaaddr]);
+ //Check for 16 or 8bit channel
+ read*=(chan->DMA16+1);
+ if((myGUS.DMAControl & 0x80) != 0) {
+ //Invert the MSB to convert twos compliment form
+ Bitu i;
+ if((myGUS.DMAControl & 0x40) == 0) {
+ // 8-bit data
+ for(i=dmaaddr;i<(dmaaddr+read);i++) GUSRam[i] ^= 0x80;
+ } else {
+ // 16-bit data
+ for(i=dmaaddr+1;i<(dmaaddr+read);i+=2) GUSRam[i] ^= 0x80;
+ }
+ }
+ //Writing to dma
+ } else {
+ chan->Write(chan->currcnt+1,&GUSRam[dmaaddr]);
+ }
+ /* Raise the TC irq if needed */
+ if((myGUS.DMAControl & 0x20) != 0) {
+ myGUS.IRQStatus |= 0x80;
+ GUS_CheckIRQ();
+ }
+ chan->Register_Callback(0);
+}
+
+static void GUS_CallBack(Bitu len) {
+ Bit32s buffer[MIXER_BUFSIZE][2];
+ memset(buffer, 0, len * sizeof(buffer[0]));
+
+ for (Bitu i = 0; i < myGUS.ActiveChannels; i++) {
+ guschan[i]->generateSamples(buffer[0], len);
+ }
+ for (Bitu i = 0; i < len; i++) {
+ buffer[i][0] >>= VOL_SHIFT;
+ buffer[i][1] >>= VOL_SHIFT;
+ }
+ gus_chan->AddSamples_s32(len, buffer[0]);
+ CheckVoiceIrq();
+}
+
+// Generate logarithmic to linear volume conversion tables
+static void MakeTables(void) {
+ int i;
+ double out = (double)(1 << 13);
+ for (i=4095;i>=0;i--) {
+ vol16bit[i]=(Bit16s)out;
+ out/=1.002709201; /* 0.0235 dB Steps */
+ //Original amplification routine in the hardware
+ //vol16bit[i] = ((256 + i & 0xff) << VOL_SHIFT) / (1 << (24 - (i >> 8)));
+ }
+ pantable[0] = 4095 << RAMP_FRACT;
+ for (i=1;i<16;i++) {
+ pantable[i]=(Bit32u)(0.5-128.0*(log((double)i/15.0)/log(2.0))*(double)(1 << RAMP_FRACT));
+ }
+}
+
+class GUS:public Module_base{
+private:
+ IO_ReadHandleObject ReadHandler[8];
+ IO_WriteHandleObject WriteHandler[9];
+ AutoexecObject autoexecline[2];
+ MixerObject MixerChan;
+public:
+ GUS(Section* configuration):Module_base(configuration){
+ if(!IS_EGAVGA_ARCH) return;
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+ if(!section->Get_bool("gus")) return;
+
+ memset(&myGUS,0,sizeof(myGUS));
+ memset(GUSRam,0,1024*1024);
+
+ myGUS.rate=section->Get_int("gusrate");
+
+ myGUS.portbase = section->Get_hex("gusbase") - 0x200;
+ int dma_val = section->Get_int("gusdma");
+ if ((dma_val<0) || (dma_val>255)) dma_val = 3; // sensible default
+ int irq_val = section->Get_int("gusirq");
+ if ((irq_val<0) || (irq_val>255)) irq_val = 5; // sensible default
+ myGUS.dma1 = (Bit8u)dma_val;
+ myGUS.dma2 = (Bit8u)dma_val;
+ myGUS.irq1 = (Bit8u)irq_val;
+ myGUS.irq2 = (Bit8u)irq_val;
+
+ // We'll leave the MIDI interface to the MPU-401
+ // Ditto for the Joystick
+ // GF1 Synthesizer
+ ReadHandler[0].Install(0x302 + GUS_BASE,read_gus,IO_MB);
+ WriteHandler[0].Install(0x302 + GUS_BASE,write_gus,IO_MB);
+
+ WriteHandler[1].Install(0x303 + GUS_BASE,write_gus,IO_MB);
+ ReadHandler[1].Install(0x303 + GUS_BASE,read_gus,IO_MB);
+
+ WriteHandler[2].Install(0x304 + GUS_BASE,write_gus,IO_MB|IO_MW);
+ ReadHandler[2].Install(0x304 + GUS_BASE,read_gus,IO_MB|IO_MW);
+
+ WriteHandler[3].Install(0x305 + GUS_BASE,write_gus,IO_MB);
+ ReadHandler[3].Install(0x305 + GUS_BASE,read_gus,IO_MB);
+
+ ReadHandler[4].Install(0x206 + GUS_BASE,read_gus,IO_MB);
+
+ WriteHandler[4].Install(0x208 + GUS_BASE,write_gus,IO_MB);
+ ReadHandler[5].Install(0x208 + GUS_BASE,read_gus,IO_MB);
+
+ WriteHandler[5].Install(0x209 + GUS_BASE,write_gus,IO_MB);
+
+ WriteHandler[6].Install(0x307 + GUS_BASE,write_gus,IO_MB);
+ ReadHandler[6].Install(0x307 + GUS_BASE,read_gus,IO_MB);
+
+ // Board Only
+
+ WriteHandler[7].Install(0x200 + GUS_BASE,write_gus,IO_MB);
+ ReadHandler[7].Install(0x20A + GUS_BASE,read_gus,IO_MB);
+ WriteHandler[8].Install(0x20B + GUS_BASE,write_gus,IO_MB);
+
+ // DmaChannels[myGUS.dma1]->Register_TC_Callback(GUS_DMA_TC_Callback);
+
+ MakeTables();
+
+ for (Bit8u chan_ct=0; chan_ct<32; chan_ct++) {
+ guschan[chan_ct] = new GUSChannels(chan_ct);
+ }
+ // Register the Mixer CallBack
+ gus_chan=MixerChan.Install(GUS_CallBack,GUS_RATE,"GUS");
+ myGUS.gRegData=0x1;
+ GUSReset();
+ myGUS.gRegData=0x0;
+ int portat = 0x200+GUS_BASE;
+
+ // ULTRASND=Port,DMA1,DMA2,IRQ1,IRQ2
+ // [GUS port], [GUS DMA (recording)], [GUS DMA (playback)], [GUS IRQ (playback)], [GUS IRQ (MIDI)]
+ ostringstream temp;
+ temp << "SET ULTRASND=" << hex << setw(3) << portat << ","
+ << dec << (Bitu)myGUS.dma1 << "," << (Bitu)myGUS.dma2 << ","
+ << (Bitu)myGUS.irq1 << "," << (Bitu)myGUS.irq2 << ends;
+ // Create autoexec.bat lines
+ autoexecline[0].Install(temp.str());
+ autoexecline[1].Install(std::string("SET ULTRADIR=") + section->Get_string("ultradir"));
+ }
+
+
+ ~GUS() {
+ if(!IS_EGAVGA_ARCH) return;
+ Section_prop * section=static_cast<Section_prop *>(m_configuration);
+ if(!section->Get_bool("gus")) return;
+
+ myGUS.gRegData=0x1;
+ GUSReset();
+ myGUS.gRegData=0x0;
+
+ for(Bitu i=0;i<32;i++) {
+ delete guschan[i];
+ }
+
+ memset(&myGUS,0,sizeof(myGUS));
+ memset(GUSRam,0,1024*1024);
+ }
+};
+
+static GUS* test;
+
+void GUS_ShutDown(Section* /*sec*/) {
+ delete test;
+}
+
+void GUS_Init(Section* sec) {
+ test = new GUS(sec);
+ sec->AddDestroyFunction(&GUS_ShutDown,true);
+}
diff --git a/src/hardware/hardware.cpp b/src/hardware/hardware.cpp
new file mode 100644
index 000000000..2f800b750
--- /dev/null
+++ b/src/hardware/hardware.cpp
@@ -0,0 +1,778 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "dosbox.h"
+#include "hardware.h"
+#include "setup.h"
+#include "support.h"
+#include "mem.h"
+#include "mapper.h"
+#include "pic.h"
+#include "render.h"
+#include "cross.h"
+
+#if (C_SSHOT)
+#include <png.h>
+#include "../libs/zmbv/zmbv.cpp"
+#endif
+
+static std::string capturedir;
+extern const char* RunningProgram;
+Bitu CaptureState;
+
+#define WAVE_BUF 16*1024
+#define MIDI_BUF 4*1024
+#define AVI_HEADER_SIZE 500
+
+static struct {
+ struct {
+ FILE * handle;
+ Bit16s buf[WAVE_BUF][2];
+ Bitu used;
+ Bit32u length;
+ Bit32u freq;
+ } wave;
+ struct {
+ FILE * handle;
+ Bit8u buffer[MIDI_BUF];
+ Bitu used,done;
+ Bit32u last;
+ } midi;
+ struct {
+ Bitu rowlen;
+ } image;
+#if (C_SSHOT)
+ struct {
+ FILE *handle;
+ Bitu frames;
+ Bit16s audiobuf[WAVE_BUF][2];
+ Bitu audioused;
+ Bitu audiorate;
+ Bitu audiowritten;
+ VideoCodec *codec;
+ Bitu width, height, bpp;
+ Bitu written;
+ float fps;
+ int bufSize;
+ void *buf;
+ Bit8u *index;
+ Bitu indexsize, indexused;
+ } video;
+#endif
+} capture;
+
+FILE * OpenCaptureFile(const char * type,const char * ext) {
+ if(capturedir.empty()) {
+ LOG_MSG("Please specify a capture directory");
+ return 0;
+ }
+
+ Bitu last=0;
+ char file_start[16];
+ dir_information * dir;
+ /* Find a filename to open */
+ dir = open_directory(capturedir.c_str());
+ if (!dir) {
+ //Try creating it first
+ Cross::CreateDir(capturedir);
+ dir=open_directory(capturedir.c_str());
+ if(!dir) {
+
+ LOG_MSG("Can't open dir %s for capturing %s",capturedir.c_str(),type);
+ return 0;
+ }
+ }
+ strcpy(file_start,RunningProgram);
+ lowcase(file_start);
+ strcat(file_start,"_");
+ bool is_directory;
+ char tempname[CROSS_LEN];
+ bool testRead = read_directory_first(dir, tempname, is_directory );
+ for ( ; testRead; testRead = read_directory_next(dir, tempname, is_directory) ) {
+ char * test=strstr(tempname,ext);
+ if (!test || strlen(test)!=strlen(ext))
+ continue;
+ *test=0;
+ if (strncasecmp(tempname,file_start,strlen(file_start))!=0) continue;
+ Bitu num=atoi(&tempname[strlen(file_start)]);
+ if (num>=last) last=num+1;
+ }
+ close_directory( dir );
+ char file_name[CROSS_LEN];
+ sprintf(file_name,"%s%c%s%03d%s",capturedir.c_str(),CROSS_FILESPLIT,file_start,last,ext);
+ /* Open the actual file */
+ FILE * handle=fopen(file_name,"wb");
+ if (handle) {
+ LOG_MSG("Capturing %s to %s",type,file_name);
+ } else {
+ LOG_MSG("Failed to open %s for capturing %s",file_name,type);
+ }
+ return handle;
+}
+
+#if (C_SSHOT)
+static void CAPTURE_AddAviChunk(const char * tag, Bit32u size, void * data, Bit32u flags) {
+ Bit8u chunk[8];Bit8u *index;Bit32u pos, writesize;
+
+ chunk[0] = tag[0];chunk[1] = tag[1];chunk[2] = tag[2];chunk[3] = tag[3];
+ host_writed(&chunk[4], size);
+ /* Write the actual data */
+ fwrite(chunk,1,8,capture.video.handle);
+ writesize = (size+1)&~1;
+ fwrite(data,1,writesize,capture.video.handle);
+ pos = capture.video.written + 4;
+ capture.video.written += writesize + 8;
+ if (capture.video.indexused + 16 >= capture.video.indexsize ) {
+ capture.video.index = (Bit8u*)realloc( capture.video.index, capture.video.indexsize + 16 * 4096);
+ if (!capture.video.index)
+ E_Exit("Ran out of memory during AVI capturing");
+ capture.video.indexsize += 16*4096;
+ }
+ index = capture.video.index+capture.video.indexused;
+ capture.video.indexused += 16;
+ index[0] = tag[0];
+ index[1] = tag[1];
+ index[2] = tag[2];
+ index[3] = tag[3];
+ host_writed(index+4, flags);
+ host_writed(index+8, pos);
+ host_writed(index+12, size);
+}
+#endif
+
+#if (C_SSHOT)
+static void CAPTURE_VideoEvent(bool pressed) {
+ if (!pressed)
+ return;
+ if (CaptureState & CAPTURE_VIDEO) {
+ /* Close the video */
+ CaptureState &= ~CAPTURE_VIDEO;
+ LOG_MSG("Stopped capturing video.");
+
+ Bit8u avi_header[AVI_HEADER_SIZE];
+ Bitu main_list;
+ Bitu header_pos=0;
+#define AVIOUT4(_S_) memcpy(&avi_header[header_pos],_S_,4);header_pos+=4;
+#define AVIOUTw(_S_) host_writew(&avi_header[header_pos], _S_);header_pos+=2;
+#define AVIOUTd(_S_) host_writed(&avi_header[header_pos], _S_);header_pos+=4;
+ /* Try and write an avi header */
+ AVIOUT4("RIFF"); // Riff header
+ AVIOUTd(AVI_HEADER_SIZE + capture.video.written - 8 + capture.video.indexused);
+ AVIOUT4("AVI ");
+ AVIOUT4("LIST"); // List header
+ main_list = header_pos;
+ AVIOUTd(0); // TODO size of list
+ AVIOUT4("hdrl");
+
+ AVIOUT4("avih");
+ AVIOUTd(56); /* # of bytes to follow */
+ AVIOUTd((Bit32u)(1000000 / capture.video.fps)); /* Microseconds per frame */
+ AVIOUTd(0);
+ AVIOUTd(0); /* PaddingGranularity (whatever that might be) */
+ AVIOUTd(0x110); /* Flags,0x10 has index, 0x100 interleaved */
+ AVIOUTd(capture.video.frames); /* TotalFrames */
+ AVIOUTd(0); /* InitialFrames */
+ AVIOUTd(2); /* Stream count */
+ AVIOUTd(0); /* SuggestedBufferSize */
+ AVIOUTd(capture.video.width); /* Width */
+ AVIOUTd(capture.video.height); /* Height */
+ AVIOUTd(0); /* TimeScale: Unit used to measure time */
+ AVIOUTd(0); /* DataRate: Data rate of playback */
+ AVIOUTd(0); /* StartTime: Starting time of AVI data */
+ AVIOUTd(0); /* DataLength: Size of AVI data chunk */
+
+ /* Video stream list */
+ AVIOUT4("LIST");
+ AVIOUTd(4 + 8 + 56 + 8 + 40); /* Size of the list */
+ AVIOUT4("strl");
+ /* video stream header */
+ AVIOUT4("strh");
+ AVIOUTd(56); /* # of bytes to follow */
+ AVIOUT4("vids"); /* Type */
+ AVIOUT4(CODEC_4CC); /* Handler */
+ AVIOUTd(0); /* Flags */
+ AVIOUTd(0); /* Reserved, MS says: wPriority, wLanguage */
+ AVIOUTd(0); /* InitialFrames */
+ AVIOUTd(1000000); /* Scale */
+ AVIOUTd((Bit32u)(1000000 * capture.video.fps)); /* Rate: Rate/Scale == samples/second */
+ AVIOUTd(0); /* Start */
+ AVIOUTd(capture.video.frames); /* Length */
+ AVIOUTd(0); /* SuggestedBufferSize */
+ AVIOUTd(~0); /* Quality */
+ AVIOUTd(0); /* SampleSize */
+ AVIOUTd(0); /* Frame */
+ AVIOUTd(0); /* Frame */
+ /* The video stream format */
+ AVIOUT4("strf");
+ AVIOUTd(40); /* # of bytes to follow */
+ AVIOUTd(40); /* Size */
+ AVIOUTd(capture.video.width); /* Width */
+ AVIOUTd(capture.video.height); /* Height */
+// OUTSHRT(1); OUTSHRT(24); /* Planes, Count */
+ AVIOUTd(0);
+ AVIOUT4(CODEC_4CC); /* Compression */
+ AVIOUTd(capture.video.width * capture.video.height*4); /* SizeImage (in bytes?) */
+ AVIOUTd(0); /* XPelsPerMeter */
+ AVIOUTd(0); /* YPelsPerMeter */
+ AVIOUTd(0); /* ClrUsed: Number of colors used */
+ AVIOUTd(0); /* ClrImportant: Number of colors important */
+
+ /* Audio stream list */
+ AVIOUT4("LIST");
+ AVIOUTd(4 + 8 + 56 + 8 + 16); /* Length of list in bytes */
+ AVIOUT4("strl");
+ /* The audio stream header */
+ AVIOUT4("strh");
+ AVIOUTd(56); /* # of bytes to follow */
+ AVIOUT4("auds");
+ AVIOUTd(0); /* Format (Optionally) */
+ AVIOUTd(0); /* Flags */
+ AVIOUTd(0); /* Reserved, MS says: wPriority, wLanguage */
+ AVIOUTd(0); /* InitialFrames */
+ AVIOUTd(4); /* Scale */
+ AVIOUTd(capture.video.audiorate*4); /* Rate, actual rate is scale/rate */
+ AVIOUTd(0); /* Start */
+ if (!capture.video.audiorate)
+ capture.video.audiorate = 1;
+ AVIOUTd(capture.video.audiowritten/4); /* Length */
+ AVIOUTd(0); /* SuggestedBufferSize */
+ AVIOUTd(~0); /* Quality */
+ AVIOUTd(4); /* SampleSize */
+ AVIOUTd(0); /* Frame */
+ AVIOUTd(0); /* Frame */
+ /* The audio stream format */
+ AVIOUT4("strf");
+ AVIOUTd(16); /* # of bytes to follow */
+ AVIOUTw(1); /* Format, WAVE_ZMBV_FORMAT_PCM */
+ AVIOUTw(2); /* Number of channels */
+ AVIOUTd(capture.video.audiorate); /* SamplesPerSec */
+ AVIOUTd(capture.video.audiorate*4); /* AvgBytesPerSec*/
+ AVIOUTw(4); /* BlockAlign */
+ AVIOUTw(16); /* BitsPerSample */
+ int nmain = header_pos - main_list - 4;
+ /* Finish stream list, i.e. put number of bytes in the list to proper pos */
+
+ int njunk = AVI_HEADER_SIZE - 8 - 12 - header_pos;
+ AVIOUT4("JUNK");
+ AVIOUTd(njunk);
+ /* Fix the size of the main list */
+ header_pos = main_list;
+ AVIOUTd(nmain);
+ header_pos = AVI_HEADER_SIZE - 12;
+ AVIOUT4("LIST");
+ AVIOUTd(capture.video.written+4); /* Length of list in bytes */
+ AVIOUT4("movi");
+ /* First add the index table to the end */
+ memcpy(capture.video.index, "idx1", 4);
+ host_writed( capture.video.index+4, capture.video.indexused - 8 );
+ fwrite( capture.video.index, 1, capture.video.indexused, capture.video.handle);
+ fseek(capture.video.handle, 0, SEEK_SET);
+ fwrite(&avi_header, 1, AVI_HEADER_SIZE, capture.video.handle);
+ fclose( capture.video.handle );
+ free( capture.video.index );
+ free( capture.video.buf );
+ delete capture.video.codec;
+ capture.video.handle = 0;
+ } else {
+ CaptureState |= CAPTURE_VIDEO;
+ }
+}
+#endif
+
+void CAPTURE_AddImage(Bitu width, Bitu height, Bitu bpp, Bitu pitch, Bitu flags, float fps, Bit8u * data, Bit8u * pal) {
+#if (C_SSHOT)
+ Bitu i;
+ Bit8u doubleRow[SCALER_MAXWIDTH*4];
+ Bitu countWidth = width;
+
+ if (flags & CAPTURE_FLAG_DBLH)
+ height *= 2;
+ if (flags & CAPTURE_FLAG_DBLW)
+ width *= 2;
+
+ if (height > SCALER_MAXHEIGHT)
+ return;
+ if (width > SCALER_MAXWIDTH)
+ return;
+
+ if (CaptureState & CAPTURE_IMAGE) {
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_color palette[256];
+
+ CaptureState &= ~CAPTURE_IMAGE;
+ /* Open the actual file */
+ FILE * fp=OpenCaptureFile("Screenshot",".png");
+ if (!fp) goto skip_shot;
+ /* First try to allocate the png structures */
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL, NULL);
+ if (!png_ptr) goto skip_shot;
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr,(png_infopp)NULL);
+ goto skip_shot;
+ }
+
+ /* Finalize the initing of png library */
+ png_init_io(png_ptr, fp);
+ png_set_compression_level(png_ptr,Z_BEST_COMPRESSION);
+
+ /* set other zlib parameters */
+ png_set_compression_mem_level(png_ptr, 8);
+ png_set_compression_strategy(png_ptr,Z_DEFAULT_STRATEGY);
+ png_set_compression_window_bits(png_ptr, 15);
+ png_set_compression_method(png_ptr, 8);
+ png_set_compression_buffer_size(png_ptr, 8192);
+
+ if (bpp==8) {
+ png_set_IHDR(png_ptr, info_ptr, width, height,
+ 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ for (i=0;i<256;i++) {
+ palette[i].red=pal[i*4+0];
+ palette[i].green=pal[i*4+1];
+ palette[i].blue=pal[i*4+2];
+ }
+ png_set_PLTE(png_ptr, info_ptr, palette,256);
+ } else {
+ png_set_bgr( png_ptr );
+ png_set_IHDR(png_ptr, info_ptr, width, height,
+ 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ }
+#ifdef PNG_TEXT_SUPPORTED
+ int fields = 1;
+ png_text text[1] = {};
+ const char* text_s = "DOSBox " VERSION;
+ size_t strl = strlen(text_s);
+ char* ptext_s = new char[strl + 1];
+ strcpy(ptext_s, text_s);
+ char software[9] = { 'S','o','f','t','w','a','r','e',0};
+ text[0].compression = PNG_TEXT_COMPRESSION_NONE;
+ text[0].key = software;
+ text[0].text = ptext_s;
+ png_set_text(png_ptr, info_ptr, text, fields);
+#endif
+ png_write_info(png_ptr, info_ptr);
+#ifdef PNG_TEXT_SUPPORTED
+ delete [] ptext_s;
+#endif
+ for (i=0;i<height;i++) {
+ void *rowPointer;
+ void *srcLine;
+ if (flags & CAPTURE_FLAG_DBLH)
+ srcLine=(data+(i >> 1)*pitch);
+ else
+ srcLine=(data+(i >> 0)*pitch);
+ rowPointer=srcLine;
+ switch (bpp) {
+ case 8:
+ if (flags & CAPTURE_FLAG_DBLW) {
+ for (Bitu x=0;x<countWidth;x++)
+ doubleRow[x*2+0] =
+ doubleRow[x*2+1] = ((Bit8u *)srcLine)[x];
+ rowPointer = doubleRow;
+ }
+ break;
+ case 15:
+ if (flags & CAPTURE_FLAG_DBLW) {
+ for (Bitu x=0;x<countWidth;x++) {
+ Bitu pixel = ((Bit16u *)srcLine)[x];
+ doubleRow[x*6+0] = doubleRow[x*6+3] = ((pixel& 0x001f) * 0x21) >> 2;
+ doubleRow[x*6+1] = doubleRow[x*6+4] = ((pixel& 0x03e0) * 0x21) >> 7;
+ doubleRow[x*6+2] = doubleRow[x*6+5] = ((pixel& 0x7c00) * 0x21) >> 12;
+ }
+ } else {
+ for (Bitu x=0;x<countWidth;x++) {
+ Bitu pixel = ((Bit16u *)srcLine)[x];
+ doubleRow[x*3+0] = ((pixel& 0x001f) * 0x21) >> 2;
+ doubleRow[x*3+1] = ((pixel& 0x03e0) * 0x21) >> 7;
+ doubleRow[x*3+2] = ((pixel& 0x7c00) * 0x21) >> 12;
+ }
+ }
+ rowPointer = doubleRow;
+ break;
+ case 16:
+ if (flags & CAPTURE_FLAG_DBLW) {
+ for (Bitu x=0;x<countWidth;x++) {
+ Bitu pixel = ((Bit16u *)srcLine)[x];
+ doubleRow[x*6+0] = doubleRow[x*6+3] = ((pixel& 0x001f) * 0x21) >> 2;
+ doubleRow[x*6+1] = doubleRow[x*6+4] = ((pixel& 0x07e0) * 0x41) >> 9;
+ doubleRow[x*6+2] = doubleRow[x*6+5] = ((pixel& 0xf800) * 0x21) >> 13;
+ }
+ } else {
+ for (Bitu x=0;x<countWidth;x++) {
+ Bitu pixel = ((Bit16u *)srcLine)[x];
+ doubleRow[x*3+0] = ((pixel& 0x001f) * 0x21) >> 2;
+ doubleRow[x*3+1] = ((pixel& 0x07e0) * 0x41) >> 9;
+ doubleRow[x*3+2] = ((pixel& 0xf800) * 0x21) >> 13;
+ }
+ }
+ rowPointer = doubleRow;
+ break;
+ case 32:
+ if (flags & CAPTURE_FLAG_DBLW) {
+ for (Bitu x=0;x<countWidth;x++) {
+ doubleRow[x*6+0] = doubleRow[x*6+3] = ((Bit8u *)srcLine)[x*4+0];
+ doubleRow[x*6+1] = doubleRow[x*6+4] = ((Bit8u *)srcLine)[x*4+1];
+ doubleRow[x*6+2] = doubleRow[x*6+5] = ((Bit8u *)srcLine)[x*4+2];
+ }
+ } else {
+ for (Bitu x=0;x<countWidth;x++) {
+ doubleRow[x*3+0] = ((Bit8u *)srcLine)[x*4+0];
+ doubleRow[x*3+1] = ((Bit8u *)srcLine)[x*4+1];
+ doubleRow[x*3+2] = ((Bit8u *)srcLine)[x*4+2];
+ }
+ }
+ rowPointer = doubleRow;
+ break;
+ }
+ png_write_row(png_ptr, (png_bytep)rowPointer);
+ }
+ /* Finish writing */
+ png_write_end(png_ptr, 0);
+ /*Destroy PNG structs*/
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ /*close file*/
+ fclose(fp);
+ }
+skip_shot:
+ if (CaptureState & CAPTURE_VIDEO) {
+ zmbv_format_t format;
+ /* Disable capturing if any of the test fails */
+ if (capture.video.handle && (
+ capture.video.width != width ||
+ capture.video.height != height ||
+ capture.video.bpp != bpp ||
+ capture.video.fps != fps))
+ {
+ CAPTURE_VideoEvent(true);
+ }
+ CaptureState &= ~CAPTURE_VIDEO;
+ switch (bpp) {
+ case 8:format = ZMBV_FORMAT_8BPP;break;
+ case 15:format = ZMBV_FORMAT_15BPP;break;
+ case 16:format = ZMBV_FORMAT_16BPP;break;
+ case 32:format = ZMBV_FORMAT_32BPP;break;
+ default:
+ goto skip_video;
+ }
+ if (!capture.video.handle) {
+ capture.video.handle = OpenCaptureFile("Video",".avi");
+ if (!capture.video.handle)
+ goto skip_video;
+ capture.video.codec = new VideoCodec();
+ if (!capture.video.codec)
+ goto skip_video;
+ if (!capture.video.codec->SetupCompress( width, height))
+ goto skip_video;
+ capture.video.bufSize = capture.video.codec->NeededSize(width, height, format);
+ capture.video.buf = malloc( capture.video.bufSize );
+ if (!capture.video.buf)
+ goto skip_video;
+ capture.video.index = (Bit8u*)malloc( 16*4096 );
+ if (!capture.video.index)
+ goto skip_video;
+ capture.video.indexsize = 16*4096;
+ capture.video.indexused = 8;
+
+ capture.video.width = width;
+ capture.video.height = height;
+ capture.video.bpp = bpp;
+ capture.video.fps = fps;
+ for (i=0;i<AVI_HEADER_SIZE;i++)
+ fputc(0,capture.video.handle);
+ capture.video.frames = 0;
+ capture.video.written = 0;
+ capture.video.audioused = 0;
+ capture.video.audiowritten = 0;
+ }
+ int codecFlags;
+ if (capture.video.frames % 300 == 0)
+ codecFlags = 1;
+ else codecFlags = 0;
+ if (!capture.video.codec->PrepareCompressFrame( codecFlags, format, (char *)pal, capture.video.buf, capture.video.bufSize))
+ goto skip_video;
+
+ for (i=0;i<height;i++) {
+ void * rowPointer;
+ if (flags & CAPTURE_FLAG_DBLW) {
+ void *srcLine;
+ Bitu x;
+ Bitu countWidth = width >> 1;
+ if (flags & CAPTURE_FLAG_DBLH)
+ srcLine=(data+(i >> 1)*pitch);
+ else
+ srcLine=(data+(i >> 0)*pitch);
+ switch ( bpp) {
+ case 8:
+ for (x=0;x<countWidth;x++)
+ ((Bit8u *)doubleRow)[x*2+0] =
+ ((Bit8u *)doubleRow)[x*2+1] = ((Bit8u *)srcLine)[x];
+ break;
+ case 15:
+ case 16:
+ for (x=0;x<countWidth;x++)
+ ((Bit16u *)doubleRow)[x*2+0] =
+ ((Bit16u *)doubleRow)[x*2+1] = ((Bit16u *)srcLine)[x];
+ break;
+ case 32:
+ for (x=0;x<countWidth;x++)
+ ((Bit32u *)doubleRow)[x*2+0] =
+ ((Bit32u *)doubleRow)[x*2+1] = ((Bit32u *)srcLine)[x];
+ break;
+ }
+ rowPointer=doubleRow;
+ } else {
+ if (flags & CAPTURE_FLAG_DBLH)
+ rowPointer=(data+(i >> 1)*pitch);
+ else
+ rowPointer=(data+(i >> 0)*pitch);
+ }
+ capture.video.codec->CompressLines( 1, &rowPointer );
+ }
+ int written = capture.video.codec->FinishCompressFrame();
+ if (written < 0)
+ goto skip_video;
+ CAPTURE_AddAviChunk( "00dc", written, capture.video.buf, codecFlags & 1 ? 0x10 : 0x0);
+ capture.video.frames++;
+// LOG_MSG("Frame %d video %d audio %d",capture.video.frames, written, capture.video.audioused *4 );
+ if ( capture.video.audioused ) {
+ CAPTURE_AddAviChunk( "01wb", capture.video.audioused * 4, capture.video.audiobuf, 0);
+ capture.video.audiowritten = capture.video.audioused*4;
+ capture.video.audioused = 0;
+ }
+
+ /* Everything went okay, set flag again for next frame */
+ CaptureState |= CAPTURE_VIDEO;
+ }
+skip_video:
+#endif
+ return;
+}
+
+
+#if (C_SSHOT)
+static void CAPTURE_ScreenShotEvent(bool pressed) {
+ if (!pressed)
+ return;
+ CaptureState |= CAPTURE_IMAGE;
+}
+#endif
+
+
+/* WAV capturing */
+static Bit8u wavheader[]={
+ 'R','I','F','F', 0x0,0x0,0x0,0x0, /* Bit32u Riff Chunk ID / Bit32u riff size */
+ 'W','A','V','E', 'f','m','t',' ', /* Bit32u Riff Format / Bit32u fmt chunk id */
+ 0x10,0x0,0x0,0x0, 0x1,0x0,0x2,0x0, /* Bit32u fmt size / Bit16u encoding/ Bit16u channels */
+ 0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0, /* Bit32u freq / Bit32u byterate */
+ 0x4,0x0,0x10,0x0, 'd','a','t','a', /* Bit16u byte-block / Bit16u bits / Bit16u data chunk id */
+ 0x0,0x0,0x0,0x0, /* Bit32u data size */
+};
+
+void CAPTURE_AddWave(Bit32u freq, Bit32u len, Bit16s * data) {
+#if (C_SSHOT)
+ if (CaptureState & CAPTURE_VIDEO) {
+ Bitu left = WAVE_BUF - capture.video.audioused;
+ if (left > len)
+ left = len;
+ memcpy( &capture.video.audiobuf[capture.video.audioused], data, left*4);
+ capture.video.audioused += left;
+ capture.video.audiorate = freq;
+ }
+#endif
+ if (CaptureState & CAPTURE_WAVE) {
+ if (!capture.wave.handle) {
+ capture.wave.handle=OpenCaptureFile("Wave Output",".wav");
+ if (!capture.wave.handle) {
+ CaptureState &= ~CAPTURE_WAVE;
+ return;
+ }
+ capture.wave.length = 0;
+ capture.wave.used = 0;
+ capture.wave.freq = freq;
+ fwrite(wavheader,1,sizeof(wavheader),capture.wave.handle);
+ }
+ Bit16s * read = data;
+ while (len > 0 ) {
+ Bitu left = WAVE_BUF - capture.wave.used;
+ if (!left) {
+ fwrite(capture.wave.buf,1,4*WAVE_BUF,capture.wave.handle);
+ capture.wave.length += 4*WAVE_BUF;
+ capture.wave.used = 0;
+ left = WAVE_BUF;
+ }
+ if (left > len)
+ left = len;
+ memcpy( &capture.wave.buf[capture.wave.used], read, left*4);
+ capture.wave.used += left;
+ read += left*2;
+ len -= left;
+ }
+ }
+}
+static void CAPTURE_WaveEvent(bool pressed) {
+ if (!pressed)
+ return;
+ /* Check for previously opened wave file */
+ if (capture.wave.handle) {
+ LOG_MSG("Stopped capturing wave output.");
+ /* Write last piece of audio in buffer */
+ fwrite(capture.wave.buf,1,capture.wave.used*4,capture.wave.handle);
+ capture.wave.length+=capture.wave.used*4;
+ /* Fill in the header with useful information */
+ host_writed(&wavheader[0x04],capture.wave.length+sizeof(wavheader)-8);
+ host_writed(&wavheader[0x18],capture.wave.freq);
+ host_writed(&wavheader[0x1C],capture.wave.freq*4);
+ host_writed(&wavheader[0x28],capture.wave.length);
+
+ fseek(capture.wave.handle,0,0);
+ fwrite(wavheader,1,sizeof(wavheader),capture.wave.handle);
+ fclose(capture.wave.handle);
+ capture.wave.handle=0;
+ CaptureState |= CAPTURE_WAVE;
+ }
+ CaptureState ^= CAPTURE_WAVE;
+}
+
+/* MIDI capturing */
+
+static Bit8u midi_header[]={
+ 'M','T','h','d', /* Bit32u, Header Chunk */
+ 0x0,0x0,0x0,0x6, /* Bit32u, Chunk Length */
+ 0x0,0x0, /* Bit16u, Format, 0=single track */
+ 0x0,0x1, /* Bit16u, Track Count, 1 track */
+ 0x01,0xf4, /* Bit16u, Timing, 2 beats/second with 500 frames */
+ 'M','T','r','k', /* Bit32u, Track Chunk */
+ 0x0,0x0,0x0,0x0, /* Bit32u, Chunk Length */
+ //Track data
+};
+
+
+static void RawMidiAdd(Bit8u data) {
+ capture.midi.buffer[capture.midi.used++]=data;
+ if (capture.midi.used >= MIDI_BUF ) {
+ capture.midi.done += capture.midi.used;
+ fwrite(capture.midi.buffer,1,MIDI_BUF,capture.midi.handle);
+ capture.midi.used = 0;
+ }
+}
+
+static void RawMidiAddNumber(Bit32u val) {
+ if (val & 0xfe00000) RawMidiAdd((Bit8u)(0x80|((val >> 21) & 0x7f)));
+ if (val & 0xfffc000) RawMidiAdd((Bit8u)(0x80|((val >> 14) & 0x7f)));
+ if (val & 0xfffff80) RawMidiAdd((Bit8u)(0x80|((val >> 7) & 0x7f)));
+ RawMidiAdd((Bit8u)(val & 0x7f));
+}
+
+void CAPTURE_AddMidi(bool sysex, Bitu len, Bit8u * data) {
+ if (!capture.midi.handle) {
+ capture.midi.handle=OpenCaptureFile("Raw Midi",".mid");
+ if (!capture.midi.handle) {
+ return;
+ }
+ fwrite(midi_header,1,sizeof(midi_header),capture.midi.handle);
+ capture.midi.last=PIC_Ticks;
+ }
+ Bit32u delta=PIC_Ticks-capture.midi.last;
+ capture.midi.last=PIC_Ticks;
+ RawMidiAddNumber(delta);
+ if (sysex) {
+ RawMidiAdd( 0xf0 );
+ RawMidiAddNumber( len );
+ }
+ for (Bitu i=0;i<len;i++)
+ RawMidiAdd(data[i]);
+}
+
+static void CAPTURE_MidiEvent(bool pressed) {
+ if (!pressed)
+ return;
+ /* Check for previously opened wave file */
+ if (capture.midi.handle) {
+ LOG_MSG("Stopping raw midi saving and finalizing file.");
+ //Delta time
+ RawMidiAdd(0x00);
+ //End of track event
+ RawMidiAdd(0xff);
+ RawMidiAdd(0x2F);
+ RawMidiAdd(0x00);
+ /* clear out the final data in the buffer if any */
+ fwrite(capture.midi.buffer,1,capture.midi.used,capture.midi.handle);
+ capture.midi.done+=capture.midi.used;
+ fseek(capture.midi.handle,18, SEEK_SET);
+ Bit8u size[4];
+ size[0]=(Bit8u)(capture.midi.done >> 24);
+ size[1]=(Bit8u)(capture.midi.done >> 16);
+ size[2]=(Bit8u)(capture.midi.done >> 8);
+ size[3]=(Bit8u)(capture.midi.done >> 0);
+ fwrite(&size,1,4,capture.midi.handle);
+ fclose(capture.midi.handle);
+ capture.midi.handle=0;
+ CaptureState &= ~CAPTURE_MIDI;
+ return;
+ }
+ CaptureState ^= CAPTURE_MIDI;
+ if (CaptureState & CAPTURE_MIDI) {
+ LOG_MSG("Preparing for raw midi capture, will start with first data.");
+ capture.midi.used=0;
+ capture.midi.done=0;
+ capture.midi.handle=0;
+ } else {
+ LOG_MSG("Stopped capturing raw midi before any data arrived.");
+ }
+}
+
+class HARDWARE:public Module_base{
+public:
+ HARDWARE(Section* configuration):Module_base(configuration){
+ Section_prop * section = static_cast<Section_prop *>(configuration);
+ Prop_path* proppath= section->Get_path("captures");
+ capturedir = proppath->realpath;
+ CaptureState = 0;
+ MAPPER_AddHandler(CAPTURE_WaveEvent,MK_f6,MMOD1,"recwave","Rec Wave");
+ MAPPER_AddHandler(CAPTURE_MidiEvent,MK_f8,MMOD1|MMOD2,"caprawmidi","Cap MIDI");
+#if (C_SSHOT)
+ MAPPER_AddHandler(CAPTURE_ScreenShotEvent,MK_f5,MMOD1,"scrshot","Screenshot");
+ MAPPER_AddHandler(CAPTURE_VideoEvent,MK_f5,MMOD1|MMOD2,"video","Video");
+#endif
+ }
+ ~HARDWARE(){
+#if (C_SSHOT)
+ if (capture.video.handle) CAPTURE_VideoEvent(true);
+#endif
+ if (capture.wave.handle) CAPTURE_WaveEvent(true);
+ if (capture.midi.handle) CAPTURE_MidiEvent(true);
+ }
+};
+
+static HARDWARE* test;
+
+void HARDWARE_Destroy(Section * sec) {
+ delete test;
+}
+
+void HARDWARE_Init(Section * sec) {
+ test = new HARDWARE(sec);
+ sec->AddDestroyFunction(&HARDWARE_Destroy,true);
+}
diff --git a/src/hardware/iohandler.cpp b/src/hardware/iohandler.cpp
new file mode 100644
index 000000000..af1d01099
--- /dev/null
+++ b/src/hardware/iohandler.cpp
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include "dosbox.h"
+#include "inout.h"
+#include "setup.h"
+#include "cpu.h"
+#include "../src/cpu/lazyflags.h"
+#include "callback.h"
+
+//#define ENABLE_PORTLOG
+
+IO_WriteHandler * io_writehandlers[3][IO_MAX];
+IO_ReadHandler * io_readhandlers[3][IO_MAX];
+
+static Bitu IO_ReadBlocked(Bitu /*port*/,Bitu /*iolen*/) {
+ return ~0;
+}
+
+static void IO_WriteBlocked(Bitu /*port*/,Bitu /*val*/,Bitu /*iolen*/) {
+}
+
+static Bitu IO_ReadDefault(Bitu port,Bitu iolen) {
+ switch (iolen) {
+ case 1:
+ LOG(LOG_IO,LOG_WARN)("Read from port %04X",port);
+ io_readhandlers[0][port]=IO_ReadBlocked;
+ return 0xff;
+ case 2:
+ return
+ (io_readhandlers[0][port+0](port+0,1) << 0) |
+ (io_readhandlers[0][port+1](port+1,1) << 8);
+ case 4:
+ return
+ (io_readhandlers[1][port+0](port+0,2) << 0) |
+ (io_readhandlers[1][port+2](port+2,2) << 16);
+ }
+ return 0;
+}
+
+void IO_WriteDefault(Bitu port,Bitu val,Bitu iolen) {
+ switch (iolen) {
+ case 1:
+ LOG(LOG_IO,LOG_WARN)("Writing %02X to port %04X",val,port);
+ io_writehandlers[0][port]=IO_WriteBlocked;
+ break;
+ case 2:
+ io_writehandlers[0][port+0](port+0,(val >> 0) & 0xff,1);
+ io_writehandlers[0][port+1](port+1,(val >> 8) & 0xff,1);
+ break;
+ case 4:
+ io_writehandlers[1][port+0](port+0,(val >> 0 ) & 0xffff,2);
+ io_writehandlers[1][port+2](port+2,(val >> 16) & 0xffff,2);
+ break;
+ }
+}
+
+void IO_RegisterReadHandler(Bitu port,IO_ReadHandler * handler,Bitu mask,Bitu range) {
+ while (range--) {
+ if (mask&IO_MB) io_readhandlers[0][port]=handler;
+ if (mask&IO_MW) io_readhandlers[1][port]=handler;
+ if (mask&IO_MD) io_readhandlers[2][port]=handler;
+ port++;
+ }
+}
+
+void IO_RegisterWriteHandler(Bitu port,IO_WriteHandler * handler,Bitu mask,Bitu range) {
+ while (range--) {
+ if (mask&IO_MB) io_writehandlers[0][port]=handler;
+ if (mask&IO_MW) io_writehandlers[1][port]=handler;
+ if (mask&IO_MD) io_writehandlers[2][port]=handler;
+ port++;
+ }
+}
+
+void IO_FreeReadHandler(Bitu port,Bitu mask,Bitu range) {
+ while (range--) {
+ if (mask&IO_MB) io_readhandlers[0][port]=IO_ReadDefault;
+ if (mask&IO_MW) io_readhandlers[1][port]=IO_ReadDefault;
+ if (mask&IO_MD) io_readhandlers[2][port]=IO_ReadDefault;
+ port++;
+ }
+}
+
+void IO_FreeWriteHandler(Bitu port,Bitu mask,Bitu range) {
+ while (range--) {
+ if (mask&IO_MB) io_writehandlers[0][port]=IO_WriteDefault;
+ if (mask&IO_MW) io_writehandlers[1][port]=IO_WriteDefault;
+ if (mask&IO_MD) io_writehandlers[2][port]=IO_WriteDefault;
+ port++;
+ }
+}
+
+void IO_ReadHandleObject::Install(Bitu port,IO_ReadHandler * handler,Bitu mask,Bitu range) {
+ if(!installed) {
+ installed=true;
+ m_port=port;
+ m_mask=mask;
+ m_range=range;
+ IO_RegisterReadHandler(port,handler,mask,range);
+ } else E_Exit("IO_readHandler already installed port %x",port);
+}
+
+void IO_ReadHandleObject::Uninstall(){
+ if(!installed) return;
+ IO_FreeReadHandler(m_port,m_mask,m_range);
+ installed=false;
+}
+
+IO_ReadHandleObject::~IO_ReadHandleObject(){
+ Uninstall();
+}
+
+void IO_WriteHandleObject::Install(Bitu port,IO_WriteHandler * handler,Bitu mask,Bitu range) {
+ if(!installed) {
+ installed=true;
+ m_port=port;
+ m_mask=mask;
+ m_range=range;
+ IO_RegisterWriteHandler(port,handler,mask,range);
+ } else E_Exit("IO_writeHandler already installed port %x",port);
+}
+
+void IO_WriteHandleObject::Uninstall() {
+ if(!installed) return;
+ IO_FreeWriteHandler(m_port,m_mask,m_range);
+ installed=false;
+}
+
+IO_WriteHandleObject::~IO_WriteHandleObject(){
+ Uninstall();
+ //LOG_MSG("FreeWritehandler called with port %X",m_port);
+}
+
+struct IOF_Entry {
+ Bitu cs;
+ Bitu eip;
+};
+
+#define IOF_QUEUESIZE 16
+static struct {
+ Bitu used;
+ IOF_Entry entries[IOF_QUEUESIZE];
+} iof_queue;
+
+static Bits IOFaultCore(void) {
+ CPU_CycleLeft+=CPU_Cycles;
+ CPU_Cycles=1;
+ Bits ret=CPU_Core_Full_Run();
+ CPU_CycleLeft+=CPU_Cycles;
+ if (ret<0) E_Exit("Got a dosbox close machine in IO-fault core?");
+ if (ret)
+ return ret;
+ if (!iof_queue.used) E_Exit("IO-faul Core without IO-faul");
+ IOF_Entry * entry=&iof_queue.entries[iof_queue.used-1];
+ if (entry->cs == SegValue(cs) && entry->eip==reg_eip)
+ return -1;
+ return 0;
+}
+
+
+/* Some code to make io operations take some virtual time. Helps certain
+ * games with their timing of certain operations
+ */
+
+
+#define IODELAY_READ_MICROS 1.0
+#define IODELAY_WRITE_MICROS 0.75
+
+inline void IO_USEC_read_delay_old() {
+ if(CPU_CycleMax > static_cast<Bit32s>((IODELAY_READ_MICROS*1000.0))) {
+ // this could be calculated whenever CPU_CycleMax changes
+ Bits delaycyc = static_cast<Bits>((CPU_CycleMax/1000)*IODELAY_READ_MICROS);
+ if(CPU_Cycles > delaycyc) CPU_Cycles -= delaycyc;
+ else CPU_Cycles = 0;
+ }
+}
+
+inline void IO_USEC_write_delay_old() {
+ if(CPU_CycleMax > static_cast<Bit32s>((IODELAY_WRITE_MICROS*1000.0))) {
+ // this could be calculated whenever CPU_CycleMax changes
+ Bits delaycyc = static_cast<Bits>((CPU_CycleMax/1000)*IODELAY_WRITE_MICROS);
+ if(CPU_Cycles > delaycyc) CPU_Cycles -= delaycyc;
+ else CPU_Cycles = 0;
+ }
+}
+
+
+#define IODELAY_READ_MICROSk (Bit32u)(1024/1.0)
+#define IODELAY_WRITE_MICROSk (Bit32u)(1024/0.75)
+
+inline void IO_USEC_read_delay() {
+ Bits delaycyc = CPU_CycleMax/IODELAY_READ_MICROSk;
+ if(GCC_UNLIKELY(delaycyc > CPU_Cycles)) delaycyc = CPU_Cycles;
+ CPU_Cycles -= delaycyc;
+ CPU_IODelayRemoved += delaycyc;
+}
+
+inline void IO_USEC_write_delay() {
+ Bits delaycyc = CPU_CycleMax/IODELAY_WRITE_MICROSk;
+ if(GCC_UNLIKELY(delaycyc > CPU_Cycles)) delaycyc = CPU_Cycles;
+ CPU_Cycles -= delaycyc;
+ CPU_IODelayRemoved += delaycyc;
+}
+
+#ifdef ENABLE_PORTLOG
+static Bit8u crtc_index = 0;
+const char* const len_type[] = {" 8","16","32"};
+void log_io(Bitu width, bool write, Bitu port, Bitu val) {
+ switch(width) {
+ case 0:
+ val&=0xff;
+ break;
+ case 1:
+ val&=0xffff;
+ break;
+ }
+ if (write) {
+ // skip the video cursor position spam
+ if (port==0x3d4) {
+ if (width==0) crtc_index = (Bit8u)val;
+ else if(width==1) crtc_index = (Bit8u)(val>>8);
+ }
+ if (crtc_index==0xe || crtc_index==0xf) {
+ if((width==0 && (port==0x3d4 || port==0x3d5))||(width==1 && port==0x3d4))
+ return;
+ }
+
+ switch(port) {
+ //case 0x020: // interrupt command
+ //case 0x040: // timer 0
+ //case 0x042: // timer 2
+ //case 0x043: // timer control
+ //case 0x061: // speaker control
+ case 0x3c8: // VGA palette
+ case 0x3c9: // VGA palette
+ // case 0x3d4: // VGA crtc
+ // case 0x3d5: // VGA crtc
+ // case 0x3c4: // VGA seq
+ // case 0x3c5: // VGA seq
+ break;
+ default:
+ LOG_MSG("iow%s % 4x % 4x, cs:ip %04x:%04x", len_type[width],
+ port, val, SegValue(cs),reg_eip);
+ break;
+ }
+ } else {
+ switch(port) {
+ //case 0x021: // interrupt status
+ //case 0x040: // timer 0
+ //case 0x042: // timer 2
+ //case 0x061: // speaker control
+ case 0x201: // joystick status
+ case 0x3c9: // VGA palette
+ // case 0x3d4: // VGA crtc index
+ // case 0x3d5: // VGA crtc
+ case 0x3da: // display status - a real spammer
+ // don't log for the above cases
+ break;
+ default:
+ LOG_MSG("ior%s % 4x % 4x,\t\tcs:ip %04x:%04x", len_type[width],
+ port, val, SegValue(cs),reg_eip);
+ break;
+ }
+ }
+}
+#else
+#define log_io(W, X, Y, Z)
+#endif
+
+
+void IO_WriteB(Bitu port,Bitu val) {
+ log_io(0, true, port, val);
+ if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,1)))) {
+ LazyFlags old_lflags;
+ memcpy(&old_lflags,&lflags,sizeof(LazyFlags));
+ CPU_Decoder * old_cpudecoder;
+ old_cpudecoder=cpudecoder;
+ cpudecoder=&IOFaultCore;
+ IOF_Entry * entry=&iof_queue.entries[iof_queue.used++];
+ entry->cs=SegValue(cs);
+ entry->eip=reg_eip;
+ CPU_Push16(SegValue(cs));
+ CPU_Push16(reg_ip);
+ Bit8u old_al = reg_al;
+ Bit16u old_dx = reg_dx;
+ reg_al = val;
+ reg_dx = port;
+ RealPt icb = CALLBACK_RealPointer(call_priv_io);
+ SegSet16(cs,RealSeg(icb));
+ reg_eip = RealOff(icb)+0x08;
+ CPU_Exception(cpu.exception.which,cpu.exception.error);
+
+ DOSBOX_RunMachine();
+ iof_queue.used--;
+
+ reg_al = old_al;
+ reg_dx = old_dx;
+ memcpy(&lflags,&old_lflags,sizeof(LazyFlags));
+ cpudecoder=old_cpudecoder;
+ }
+ else {
+ IO_USEC_write_delay();
+ io_writehandlers[0][port](port,val,1);
+ }
+}
+
+void IO_WriteW(Bitu port,Bitu val) {
+ log_io(1, true, port, val);
+ if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,2)))) {
+ LazyFlags old_lflags;
+ memcpy(&old_lflags,&lflags,sizeof(LazyFlags));
+ CPU_Decoder * old_cpudecoder;
+ old_cpudecoder=cpudecoder;
+ cpudecoder=&IOFaultCore;
+ IOF_Entry * entry=&iof_queue.entries[iof_queue.used++];
+ entry->cs=SegValue(cs);
+ entry->eip=reg_eip;
+ CPU_Push16(SegValue(cs));
+ CPU_Push16(reg_ip);
+ Bit16u old_ax = reg_ax;
+ Bit16u old_dx = reg_dx;
+ reg_ax = val;
+ reg_dx = port;
+ RealPt icb = CALLBACK_RealPointer(call_priv_io);
+ SegSet16(cs,RealSeg(icb));
+ reg_eip = RealOff(icb)+0x0a;
+ CPU_Exception(cpu.exception.which,cpu.exception.error);
+
+ DOSBOX_RunMachine();
+ iof_queue.used--;
+
+ reg_ax = old_ax;
+ reg_dx = old_dx;
+ memcpy(&lflags,&old_lflags,sizeof(LazyFlags));
+ cpudecoder=old_cpudecoder;
+ }
+ else {
+ IO_USEC_write_delay();
+ io_writehandlers[1][port](port,val,2);
+ }
+}
+
+void IO_WriteD(Bitu port,Bitu val) {
+ log_io(2, true, port, val);
+ if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,4)))) {
+ LazyFlags old_lflags;
+ memcpy(&old_lflags,&lflags,sizeof(LazyFlags));
+ CPU_Decoder * old_cpudecoder;
+ old_cpudecoder=cpudecoder;
+ cpudecoder=&IOFaultCore;
+ IOF_Entry * entry=&iof_queue.entries[iof_queue.used++];
+ entry->cs=SegValue(cs);
+ entry->eip=reg_eip;
+ CPU_Push16(SegValue(cs));
+ CPU_Push16(reg_ip);
+ Bit32u old_eax = reg_eax;
+ Bit16u old_dx = reg_dx;
+ reg_eax = val;
+ reg_dx = port;
+ RealPt icb = CALLBACK_RealPointer(call_priv_io);
+ SegSet16(cs,RealSeg(icb));
+ reg_eip = RealOff(icb)+0x0c;
+ CPU_Exception(cpu.exception.which,cpu.exception.error);
+
+ DOSBOX_RunMachine();
+ iof_queue.used--;
+
+ reg_eax = old_eax;
+ reg_dx = old_dx;
+ memcpy(&lflags,&old_lflags,sizeof(LazyFlags));
+ cpudecoder=old_cpudecoder;
+ }
+ else io_writehandlers[2][port](port,val,4);
+}
+
+Bitu IO_ReadB(Bitu port) {
+ Bitu retval;
+ if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,1)))) {
+ LazyFlags old_lflags;
+ memcpy(&old_lflags,&lflags,sizeof(LazyFlags));
+ CPU_Decoder * old_cpudecoder;
+ old_cpudecoder=cpudecoder;
+ cpudecoder=&IOFaultCore;
+ IOF_Entry * entry=&iof_queue.entries[iof_queue.used++];
+ entry->cs=SegValue(cs);
+ entry->eip=reg_eip;
+ CPU_Push16(SegValue(cs));
+ CPU_Push16(reg_ip);
+ Bit8u old_al = reg_al;
+ Bit16u old_dx = reg_dx;
+ reg_dx = port;
+ RealPt icb = CALLBACK_RealPointer(call_priv_io);
+ SegSet16(cs,RealSeg(icb));
+ reg_eip = RealOff(icb)+0x00;
+ CPU_Exception(cpu.exception.which,cpu.exception.error);
+
+ DOSBOX_RunMachine();
+ iof_queue.used--;
+
+ retval = reg_al;
+ reg_al = old_al;
+ reg_dx = old_dx;
+ memcpy(&lflags,&old_lflags,sizeof(LazyFlags));
+ cpudecoder=old_cpudecoder;
+ return retval;
+ }
+ else {
+ IO_USEC_read_delay();
+ retval = io_readhandlers[0][port](port,1);
+ }
+ log_io(0, false, port, retval);
+ return retval;
+}
+
+Bitu IO_ReadW(Bitu port) {
+ Bitu retval;
+ if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,2)))) {
+ LazyFlags old_lflags;
+ memcpy(&old_lflags,&lflags,sizeof(LazyFlags));
+ CPU_Decoder * old_cpudecoder;
+ old_cpudecoder=cpudecoder;
+ cpudecoder=&IOFaultCore;
+ IOF_Entry * entry=&iof_queue.entries[iof_queue.used++];
+ entry->cs=SegValue(cs);
+ entry->eip=reg_eip;
+ CPU_Push16(SegValue(cs));
+ CPU_Push16(reg_ip);
+ Bit16u old_ax = reg_ax;
+ Bit16u old_dx = reg_dx;
+ reg_dx = port;
+ RealPt icb = CALLBACK_RealPointer(call_priv_io);
+ SegSet16(cs,RealSeg(icb));
+ reg_eip = RealOff(icb)+0x02;
+ CPU_Exception(cpu.exception.which,cpu.exception.error);
+
+ DOSBOX_RunMachine();
+ iof_queue.used--;
+
+ retval = reg_ax;
+ reg_ax = old_ax;
+ reg_dx = old_dx;
+ memcpy(&lflags,&old_lflags,sizeof(LazyFlags));
+ cpudecoder=old_cpudecoder;
+ }
+ else {
+ IO_USEC_read_delay();
+ retval = io_readhandlers[1][port](port,2);
+ }
+ log_io(1, false, port, retval);
+ return retval;
+}
+
+Bitu IO_ReadD(Bitu port) {
+ Bitu retval;
+ if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,4)))) {
+ LazyFlags old_lflags;
+ memcpy(&old_lflags,&lflags,sizeof(LazyFlags));
+ CPU_Decoder * old_cpudecoder;
+ old_cpudecoder=cpudecoder;
+ cpudecoder=&IOFaultCore;
+ IOF_Entry * entry=&iof_queue.entries[iof_queue.used++];
+ entry->cs=SegValue(cs);
+ entry->eip=reg_eip;
+ CPU_Push16(SegValue(cs));
+ CPU_Push16(reg_ip);
+ Bit32u old_eax = reg_eax;
+ Bit16u old_dx = reg_dx;
+ reg_dx = port;
+ RealPt icb = CALLBACK_RealPointer(call_priv_io);
+ SegSet16(cs,RealSeg(icb));
+ reg_eip = RealOff(icb)+0x04;
+ CPU_Exception(cpu.exception.which,cpu.exception.error);
+
+ DOSBOX_RunMachine();
+ iof_queue.used--;
+
+ retval = reg_eax;
+ reg_eax = old_eax;
+ reg_dx = old_dx;
+ memcpy(&lflags,&old_lflags,sizeof(LazyFlags));
+ cpudecoder=old_cpudecoder;
+ } else {
+ retval = io_readhandlers[2][port](port,4);
+ }
+ log_io(2, false, port, retval);
+ return retval;
+}
+
+class IO :public Module_base {
+public:
+ IO(Section* configuration):Module_base(configuration){
+ iof_queue.used=0;
+ IO_FreeReadHandler(0,IO_MA,IO_MAX);
+ IO_FreeWriteHandler(0,IO_MA,IO_MAX);
+ }
+ ~IO()
+ {
+ //Same as the constructor ?
+ }
+};
+
+static IO* test;
+
+void IO_Destroy(Section*) {
+ delete test;
+}
+
+void IO_Init(Section * sect) {
+ test = new IO(sect);
+ sect->AddDestroyFunction(&IO_Destroy);
+}
diff --git a/src/hardware/ipx.cpp b/src/hardware/ipx.cpp
new file mode 100644
index 000000000..8b410dae2
--- /dev/null
+++ b/src/hardware/ipx.cpp
@@ -0,0 +1,1200 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+
+#if C_IPX
+
+#include <string.h>
+#include <time.h>
+#include <stdio.h>
+#include "cross.h"
+#include "support.h"
+#include "cpu.h"
+#include "regs.h"
+#include "inout.h"
+#include "setup.h"
+#include "debug.h"
+#include "callback.h"
+#include "dos_system.h"
+#include "mem.h"
+#include "ipx.h"
+#include "ipxserver.h"
+#include "timer.h"
+#include "SDL_net.h"
+#include "programs.h"
+#include "pic.h"
+
+#define SOCKTABLESIZE 150 // DOS IPX driver was limited to 150 open sockets
+
+struct ipxnetaddr {
+ Uint8 netnum[4]; // Both are big endian
+ Uint8 netnode[6];
+} localIpxAddr;
+
+Bit32u udpPort;
+bool isIpxServer;
+bool isIpxConnected;
+IPaddress ipxServConnIp; // IPAddress for client connection to server
+UDPsocket ipxClientSocket;
+int UDPChannel; // Channel used by UDP connection
+Bit8u recvBuffer[IPXBUFFERSIZE]; // Incoming packet buffer
+
+static RealPt ipx_callback;
+
+SDLNet_SocketSet clientSocketSet;
+
+packetBuffer incomingPacket;
+
+static Bit16u socketCount;
+static Bit16u opensockets[SOCKTABLESIZE];
+
+static Bit16u swapByte(Bit16u sockNum) {
+ return (((sockNum>> 8)) | (sockNum << 8));
+}
+
+void UnpackIP(PackedIP ipPack, IPaddress * ipAddr) {
+ ipAddr->host = ipPack.host;
+ ipAddr->port = ipPack.port;
+}
+
+void PackIP(IPaddress ipAddr, PackedIP *ipPack) {
+ ipPack->host = ipAddr.host;
+ ipPack->port = ipAddr.port;
+}
+
+ECBClass *ECBList; // Linked list of ECB's
+ECBClass* ESRList; // ECBs waiting to be ESR notified
+
+#ifdef IPX_DEBUGMSG
+Bitu ECBSerialNumber = 0;
+Bitu ECBAmount = 0;
+#endif
+
+
+ECBClass::ECBClass(Bit16u segment, Bit16u offset) {
+ ECBAddr = RealMake(segment, offset);
+ databuffer = 0;
+
+#ifdef IPX_DEBUGMSG
+ SerialNumber = ECBSerialNumber;
+ ECBSerialNumber++;
+ ECBAmount++;
+
+ LOG_IPX("ECB: SN%7d created. Number of ECBs: %3d, ESR %4x:%4x, ECB %4x:%4x",
+ SerialNumber,ECBAmount,
+ real_readw(RealSeg(ECBAddr),
+ RealOff(ECBAddr)+6),
+ real_readw(RealSeg(ECBAddr),
+ RealOff(ECBAddr)+4),segment,offset);
+#endif
+ isInESRList = false;
+ prevECB = NULL;
+ nextECB = NULL;
+
+ if (ECBList == NULL)
+ ECBList = this;
+ else {
+ // Transverse the list until we hit the end
+ ECBClass *useECB = ECBList;
+
+ while(useECB->nextECB != NULL)
+ useECB = useECB->nextECB;
+
+ useECB->nextECB = this;
+ this->prevECB = useECB;
+ }
+
+ iuflag = getInUseFlag();
+ mysocket = getSocket();
+}
+void ECBClass::writeDataBuffer(Bit8u* buffer, Bit16u length) {
+ if(databuffer!=0) delete [] databuffer;
+ databuffer = new Bit8u[length];
+ memcpy(databuffer,buffer,length);
+ buflen=length;
+
+}
+bool ECBClass::writeData() {
+ Bitu length=buflen;
+ Bit8u* buffer = databuffer;
+ fragmentDescriptor tmpFrag;
+ setInUseFlag(USEFLAG_AVAILABLE);
+ Bitu fragCount = getFragCount();
+ Bitu bufoffset = 0;
+ for(Bitu i = 0;i < fragCount;i++) {
+ getFragDesc(i,&tmpFrag);
+ for(Bitu t = 0;t < tmpFrag.size;t++) {
+ real_writeb(tmpFrag.segment, tmpFrag.offset + t, buffer[bufoffset]);
+ bufoffset++;
+ if(bufoffset >= length) {
+ setCompletionFlag(COMP_SUCCESS);
+ setImmAddress(&buffer[22]); // Write in source node
+ return true;
+ }
+ }
+ }
+ if(bufoffset < length) {
+ setCompletionFlag(COMP_MALFORMED);
+ return false;
+ }
+ return false;
+}
+
+Bit16u ECBClass::getSocket(void) {
+ return swapByte(real_readw(RealSeg(ECBAddr), RealOff(ECBAddr) + 0xa));
+}
+
+Bit8u ECBClass::getInUseFlag(void) {
+ return real_readb(RealSeg(ECBAddr), RealOff(ECBAddr) + 0x8);
+}
+
+void ECBClass::setInUseFlag(Bit8u flagval) {
+ iuflag = flagval;
+ real_writeb(RealSeg(ECBAddr), RealOff(ECBAddr) + 0x8, flagval);
+}
+
+void ECBClass::setCompletionFlag(Bit8u flagval) {
+ real_writeb(RealSeg(ECBAddr), RealOff(ECBAddr) + 0x9, flagval);
+}
+
+Bit16u ECBClass::getFragCount(void) {
+ return real_readw(RealSeg(ECBAddr), RealOff(ECBAddr) + 34);
+}
+
+void ECBClass::getFragDesc(Bit16u descNum, fragmentDescriptor *fragDesc) {
+ Bit16u memoff = RealOff(ECBAddr) + 30 + ((descNum+1) * 6);
+ fragDesc->offset = real_readw(RealSeg(ECBAddr), memoff);
+ memoff += 2;
+ fragDesc->segment = real_readw(RealSeg(ECBAddr), memoff);
+ memoff += 2;
+ fragDesc->size = real_readw(RealSeg(ECBAddr), memoff);
+}
+
+RealPt ECBClass::getESRAddr(void) {
+ return RealMake(real_readw(RealSeg(ECBAddr),
+ RealOff(ECBAddr)+6),
+ real_readw(RealSeg(ECBAddr),
+ RealOff(ECBAddr)+4));
+}
+
+void ECBClass::NotifyESR(void) {
+ Bit32u ESRval = real_readd(RealSeg(ECBAddr), RealOff(ECBAddr)+4);
+ if(ESRval || databuffer) { // databuffer: write data at realmode/v86 time
+ // LOG_IPX("ECB: SN%7d to be notified.", SerialNumber);
+ // take the ECB out of the current list
+ if(prevECB == NULL) { // was the first in the list
+ ECBList = nextECB;
+ if(ECBList != NULL) ECBList->prevECB = NULL;
+ } else { // not the first
+ prevECB->nextECB = nextECB;
+ if(nextECB != NULL) nextECB->prevECB = prevECB;
+ }
+
+ nextECB = NULL;
+ // put it to the notification queue
+ if(ESRList==NULL) {
+ ESRList = this;
+ prevECB = NULL;
+ } else {// put to end of ESR list
+ ECBClass* useECB = ESRList;
+
+ while(useECB->nextECB != NULL)
+ useECB = useECB->nextECB;
+
+ useECB->nextECB = this;
+ prevECB = useECB;
+ }
+ isInESRList = true;
+ PIC_ActivateIRQ(11);
+ }
+ // this one does not want to be notified, delete it right away
+ else delete this;
+}
+
+void ECBClass::setImmAddress(Bit8u *immAddr) {
+ for(Bitu i=0;i<6;i++)
+ real_writeb(RealSeg(ECBAddr), RealOff(ECBAddr)+28+i, immAddr[i]);
+}
+
+void ECBClass::getImmAddress(Bit8u* immAddr) {
+ for(Bitu i=0;i<6;i++)
+ immAddr[i] = real_readb(RealSeg(ECBAddr), RealOff(ECBAddr)+28+i);
+}
+
+ECBClass::~ECBClass() {
+#ifdef IPX_DEBUGMSG
+ ECBAmount--;
+ LOG_IPX("ECB: SN%7d destroyed. Remaining ECBs: %3d", SerialNumber,ECBAmount);
+#endif
+
+ if(isInESRList) {
+ // in ESR list, always the first element is deleted.
+ ESRList=nextECB;
+ } else {
+ if(prevECB == NULL) { // was the first in the list
+ ECBList = nextECB;
+ if(ECBList != NULL) ECBList->prevECB = NULL;
+ } else { // not the first
+ prevECB->nextECB = nextECB;
+ if(nextECB != NULL) nextECB->prevECB = prevECB;
+ }
+ }
+ if(databuffer!=0) delete [] databuffer;
+}
+
+
+
+static bool sockInUse(Bit16u sockNum) {
+ for(Bitu i=0;i<socketCount;i++) {
+ if (opensockets[i] == sockNum) return true;
+ }
+ return false;
+}
+
+static void OpenSocket(void) {
+ Bit16u sockNum, sockAlloc;
+ sockNum = swapByte(reg_dx);
+
+ if(socketCount >= SOCKTABLESIZE) {
+ reg_al = 0xfe; // Socket table full
+ return;
+ }
+
+ if(sockNum == 0x0000) {
+ // Dynamic socket allocation
+ sockAlloc = 0x4002;
+ while(sockInUse(sockAlloc) && (sockAlloc < 0x7fff)) sockAlloc++;
+ if(sockAlloc > 0x7fff) {
+ // I have no idea how this could happen if the IPX driver
+ // is limited to 150 open sockets at a time
+ LOG_MSG("IPX: Out of dynamic sockets");
+ }
+ sockNum = sockAlloc;
+ } else {
+ if(sockInUse(sockNum)) {
+ reg_al = 0xff; // Socket already open
+ return;
+ }
+ }
+
+ opensockets[socketCount] = sockNum;
+ socketCount++;
+
+ reg_al = 0x00; // Success
+ reg_dx = swapByte(sockNum); // Convert back to big-endian
+}
+
+static void CloseSocket(void) {
+ Bit16u sockNum, i;
+ ECBClass* tmpECB = ECBList;
+ ECBClass* tmp2ECB = ECBList;
+
+ sockNum = swapByte(reg_dx);
+ if(!sockInUse(sockNum)) return;
+
+ for(i=0;i<socketCount-1;i++) {
+ if (opensockets[i] == sockNum) {
+ // Realign list of open sockets
+ memcpy(&opensockets[i], &opensockets[i+1], SOCKTABLESIZE - (i + 1));
+ break;
+ }
+ }
+ --socketCount;
+
+ // delete all ECBs of that socket
+ while(tmpECB!=0) {
+ tmp2ECB = tmpECB->nextECB;
+ if(tmpECB->getSocket()==sockNum) {
+ tmpECB->setCompletionFlag(COMP_CANCELLED);
+ tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
+ delete tmpECB;
+ }
+ tmpECB = tmp2ECB;
+ }
+}
+
+//static RealPt IPXVERpointer;
+
+static bool IPX_Multiplex(void) {
+ if(reg_ax != 0x7a00) return false;
+ reg_al = 0xff;
+ SegSet16(es,RealSeg(ipx_callback));
+ reg_di = RealOff(ipx_callback);
+
+ //reg_bx = RealOff(IPXVERpointer);
+ //reg_cx = RealSeg(ipx_callback);
+ return true;
+}
+
+static void IPX_AES_EventHandler(Bitu param)
+{
+ ECBClass* tmpECB = ECBList;
+ ECBClass* tmp2ECB;
+ while(tmpECB!=0) {
+ tmp2ECB = tmpECB->nextECB;
+ if(tmpECB->iuflag==USEFLAG_AESCOUNT && param==(Bitu)tmpECB->ECBAddr) {
+ tmpECB->setCompletionFlag(COMP_SUCCESS);
+ tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
+ tmpECB->NotifyESR();
+ // LOG_IPX("AES Notification: ECB S/N %d",tmpECB->SerialNumber);
+ return;
+ }
+ tmpECB = tmp2ECB;
+ }
+ LOG_MSG("!!!! Rouge AES !!!!" );
+}
+
+static void sendPacket(ECBClass* sendecb);
+
+static void handleIpxRequest(void) {
+ ECBClass *tmpECB;
+
+ switch (reg_bx) {
+ case 0x0000: // Open socket
+ OpenSocket();
+ LOG_IPX("IPX: Open socket %4x", swapByte(reg_dx));
+ break;
+ case 0x0001: // Close socket
+ LOG_IPX("IPX: Close socket %4x", swapByte(reg_dx));
+ CloseSocket();
+ break;
+ case 0x0002: // get local target
+ // es:si
+ // Currently no support for multiple networks
+
+ for(Bitu i = 0; i < 6; i++)
+ real_writeb(SegValue(es),reg_di+i,real_readb(SegValue(es),reg_si+i+4));
+
+ reg_cx=1; // time ticks expected
+ reg_al=0x00; //success
+ break;
+
+ case 0x0003: // Send packet
+ tmpECB = new ECBClass(SegValue(es),reg_si);
+ if(!incomingPacket.connected) {
+ tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
+ tmpECB->setCompletionFlag(COMP_UNDELIVERABLE);
+ delete tmpECB; // not notify?
+ reg_al = 0xff; // Failure
+ } else {
+ tmpECB->setInUseFlag(USEFLAG_SENDING);
+ //LOG_IPX("IPX: Sending packet on %4x", tmpECB->getSocket());
+ reg_al = 0x00; // Success
+ sendPacket(tmpECB);
+ }
+
+ break;
+ case 0x0004: // Listen for packet
+ tmpECB = new ECBClass(SegValue(es),reg_si);
+ // LOG_IPX("ECB: SN%7d RECEIVE.", tmpECB->SerialNumber);
+ if(!sockInUse(tmpECB->getSocket())) { // Socket is not open
+ reg_al = 0xff;
+ tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
+ tmpECB->setCompletionFlag(COMP_HARDWAREERROR);
+ delete tmpECB;
+ } else {
+ reg_al = 0x00; // Success
+ tmpECB->setInUseFlag(USEFLAG_LISTENING);
+ /*LOG_IPX("IPX: Listen for packet on 0x%4x - ESR address %4x:%4x",
+ tmpECB->getSocket(),
+ RealSeg(tmpECB->getESRAddr()),
+ RealOff(tmpECB->getESRAddr()));*/
+ }
+ break;
+
+ case 0x0005: // SCHEDULE IPX EVENT
+ case 0x0007: // SCHEDULE SPECIAL IPX EVENT
+ {
+ tmpECB = new ECBClass(SegValue(es),reg_si);
+ // LOG_IPX("ECB: SN%7d AES. T=%fms.", tmpECB->SerialNumber,
+ // (1000.0f/(1193182.0f/65536.0f))*(float)reg_ax);
+ PIC_AddEvent(IPX_AES_EventHandler,
+ (1000.0f/(1193182.0f/65536.0f))*(float)reg_ax,(Bitu)tmpECB->ECBAddr);
+ tmpECB->setInUseFlag(USEFLAG_AESCOUNT);
+ break;
+ }
+ case 0x0006: // cancel operation
+ {
+ RealPt ecbaddress = RealMake(SegValue(es),reg_si);
+ ECBClass* tmpECB= ECBList;
+ ECBClass* tmp2ECB;
+ while(tmpECB) {
+ tmp2ECB=tmpECB->nextECB;
+ if(tmpECB->ECBAddr == ecbaddress) {
+ if(tmpECB->getInUseFlag()==USEFLAG_AESCOUNT)
+ PIC_RemoveSpecificEvents(IPX_AES_EventHandler,(Bitu)ecbaddress);
+ tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
+ tmpECB->setCompletionFlag(COMP_CANCELLED);
+ delete tmpECB;
+ reg_al=0; // Success
+ LOG_IPX("IPX: ECB canceled.");
+ return;
+ }
+ tmpECB=tmp2ECB;
+ }
+ reg_al=0xff; // Fail
+ break;
+ }
+ case 0x0008: // Get interval marker
+ reg_ax = mem_readw(0x46c); // BIOS_TIMER
+ break;
+ case 0x0009: // Get internetwork address
+ {
+ LOG_IPX("IPX: Get internetwork address %2x:%2x:%2x:%2x:%2x:%2x",
+ localIpxAddr.netnode[5], localIpxAddr.netnode[4],
+ localIpxAddr.netnode[3], localIpxAddr.netnode[2],
+ localIpxAddr.netnode[1], localIpxAddr.netnode[0]);
+
+ Bit8u * addrptr = (Bit8u *)&localIpxAddr;
+ for(Bit16u i=0;i<10;i++)
+ real_writeb(SegValue(es),reg_si+i,addrptr[i]);
+ break;
+ }
+ case 0x000a: // Relinquish control
+ break; // Idle thingy
+
+ case 0x000b: // Disconnect from Target
+ break; // We don't even connect
+
+ case 0x000d: // get packet size
+ reg_cx=0; // retry count
+ reg_ax=1024; // real implementation returns 1024
+ break;
+
+ case 0x0010: // SPX install check
+ reg_al=0; // SPX not installed
+ break;
+
+ case 0x001a: // get driver maximum packet size
+ reg_cx=0; // retry count
+ reg_ax=IPXBUFFERSIZE; // max packet size: something near the
+ // ethernet packet size
+ break;
+
+ default:
+ LOG_MSG("Unhandled IPX function: %4x", reg_bx);
+ break;
+ }
+}
+
+// Entrypoint handler
+Bitu IPX_Handler(void) {
+ handleIpxRequest();
+ return CBRET_NONE;
+}
+
+// INT 7A handler
+Bitu IPX_IntHandler(void) {
+ handleIpxRequest();
+ return CBRET_NONE;
+}
+
+static void pingAck(IPaddress retAddr) {
+ IPXHeader regHeader;
+ UDPpacket regPacket;
+ Bits result;
+
+ SDLNet_Write16(0xffff, regHeader.checkSum);
+ SDLNet_Write16(sizeof(regHeader), regHeader.length);
+
+ SDLNet_Write32(0, regHeader.dest.network);
+ PackIP(retAddr, &regHeader.dest.addr.byIP);
+ SDLNet_Write16(0x2, regHeader.dest.socket);
+
+ SDLNet_Write32(0, regHeader.src.network);
+ memcpy(regHeader.src.addr.byNode.node, localIpxAddr.netnode, sizeof(regHeader.src.addr.byNode.node));
+ SDLNet_Write16(0x2, regHeader.src.socket);
+ regHeader.transControl = 0;
+ regHeader.pType = 0x0;
+
+ regPacket.data = (Uint8 *)&regHeader;
+ regPacket.len = sizeof(regHeader);
+ regPacket.maxlen = sizeof(regHeader);
+ regPacket.channel = UDPChannel;
+
+ result = SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, &regPacket);
+}
+
+static void pingSend(void) {
+ IPXHeader regHeader;
+ UDPpacket regPacket;
+ Bits result;
+
+ SDLNet_Write16(0xffff, regHeader.checkSum);
+ SDLNet_Write16(sizeof(regHeader), regHeader.length);
+
+ SDLNet_Write32(0, regHeader.dest.network);
+ regHeader.dest.addr.byIP.host = 0xffffffff;
+ regHeader.dest.addr.byIP.port = 0xffff;
+ SDLNet_Write16(0x2, regHeader.dest.socket);
+
+ SDLNet_Write32(0, regHeader.src.network);
+ memcpy(regHeader.src.addr.byNode.node, localIpxAddr.netnode, sizeof(regHeader.src.addr.byNode.node));
+ SDLNet_Write16(0x2, regHeader.src.socket);
+ regHeader.transControl = 0;
+ regHeader.pType = 0x0;
+
+ regPacket.data = (Uint8 *)&regHeader;
+ regPacket.len = sizeof(regHeader);
+ regPacket.maxlen = sizeof(regHeader);
+ regPacket.channel = UDPChannel;
+
+ result = SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, &regPacket);
+ if(!result) {
+ LOG_MSG("IPX: SDLNet_UDP_Send: %s\n", SDLNet_GetError());
+ }
+}
+
+static void receivePacket(Bit8u *buffer, Bit16s bufSize) {
+ ECBClass *useECB;
+ ECBClass *nextECB;
+ Bit16u *bufword = (Bit16u *)buffer;
+ Bit16u useSocket = swapByte(bufword[8]);
+ IPXHeader * tmpHeader;
+ tmpHeader = (IPXHeader *)buffer;
+
+ // Check to see if ping packet
+ if(useSocket == 0x2) {
+ // Is this a broadcast?
+ if((tmpHeader->dest.addr.byIP.host == 0xffffffff) &&
+ (tmpHeader->dest.addr.byIP.port == 0xffff)) {
+ // Yes. We should return the ping back to the sender
+ IPaddress tmpAddr;
+ UnpackIP(tmpHeader->src.addr.byIP, &tmpAddr);
+ pingAck(tmpAddr);
+ return;
+ }
+ }
+
+ useECB = ECBList;
+ while(useECB != NULL)
+ {
+ nextECB = useECB->nextECB;
+ if(useECB->iuflag == USEFLAG_LISTENING && useECB->mysocket == useSocket) {
+ useECB->writeDataBuffer(buffer, bufSize);
+ useECB->NotifyESR();
+ return;
+ }
+ useECB = nextECB;
+ }
+ LOG_IPX("IPX: RX Packet loss!");
+}
+
+static void IPX_ClientLoop(void) {
+ int numrecv;
+ UDPpacket inPacket;
+ inPacket.data = (Uint8 *)recvBuffer;
+ inPacket.maxlen = IPXBUFFERSIZE;
+ inPacket.channel = UDPChannel;
+
+ // Its amazing how much simpler UDP is than TCP
+ numrecv = SDLNet_UDP_Recv(ipxClientSocket, &inPacket);
+ if(numrecv) receivePacket(inPacket.data, inPacket.len);
+}
+
+
+void DisconnectFromServer(bool unexpected) {
+ if(unexpected) LOG_MSG("IPX: Server disconnected unexpectedly");
+ if(incomingPacket.connected) {
+ incomingPacket.connected = false;
+ TIMER_DelTickHandler(&IPX_ClientLoop);
+ SDLNet_UDP_Close(ipxClientSocket);
+ }
+}
+
+static void sendPacket(ECBClass* sendecb) {
+ Bit8u outbuffer[IPXBUFFERSIZE];
+ fragmentDescriptor tmpFrag;
+ Bit16u i, fragCount,t;
+ Bit16s packetsize;
+ Bit16u *wordptr;
+ Bits result;
+ UDPpacket outPacket;
+
+ sendecb->setInUseFlag(USEFLAG_AVAILABLE);
+ packetsize = 0;
+ fragCount = sendecb->getFragCount();
+ for(i=0;i<fragCount;i++) {
+ sendecb->getFragDesc(i,&tmpFrag);
+ if(i==0) {
+ // Fragment containing IPX header
+ // Must put source address into header
+ Bit8u * addrptr;
+
+ // source netnum
+ addrptr = (Bit8u *)&localIpxAddr.netnum;
+ for(Bit16u m=0;m<4;m++) {
+ real_writeb(tmpFrag.segment,tmpFrag.offset+m+18,addrptr[m]);
+ }
+ // source node number
+ addrptr = (Bit8u *)&localIpxAddr.netnode;
+ for(Bit16u m=0;m<6;m++) {
+ real_writeb(tmpFrag.segment,tmpFrag.offset+m+22,addrptr[m]);
+ }
+ // Source socket
+ real_writew(tmpFrag.segment,tmpFrag.offset+28, swapByte(sendecb->getSocket()));
+
+ // blank checksum
+ real_writew(tmpFrag.segment,tmpFrag.offset, 0xffff);
+ }
+
+ for(t=0;t<tmpFrag.size;t++) {
+ outbuffer[packetsize] = real_readb(tmpFrag.segment, tmpFrag.offset + t);
+ packetsize++;
+ if(packetsize>=IPXBUFFERSIZE) {
+ LOG_MSG("IPX: Packet size to be sent greater than %d bytes.", IPXBUFFERSIZE);
+ sendecb->setCompletionFlag(COMP_UNDELIVERABLE);
+ sendecb->NotifyESR();
+ return;
+ }
+ }
+ }
+
+ // Add length and source socket to IPX header
+ wordptr = (Bit16u *)&outbuffer[0];
+ // Blank CRC
+ //wordptr[0] = 0xffff;
+ // Length
+ wordptr[1] = swapByte(packetsize);
+ // Source socket
+ //wordptr[14] = swapByte(sendecb->getSocket());
+
+ sendecb->getFragDesc(0,&tmpFrag);
+ real_writew(tmpFrag.segment,tmpFrag.offset+2, swapByte(packetsize));
+
+
+ Bit8u immedAddr[6];
+ sendecb->getImmAddress(immedAddr);
+ // filter out broadcasts and local loopbacks
+ // Real implementation uses the ImmedAddr to check wether this is a broadcast
+
+ bool islocalbroadcast=true;
+ bool isloopback=true;
+
+ Bit8u * addrptr;
+
+ addrptr = (Bit8u *)&localIpxAddr.netnum;
+ for(Bitu m=0;m<4;m++) {
+ if(addrptr[m]!=outbuffer[m+0x6])isloopback=false;
+ }
+ addrptr = (Bit8u *)&localIpxAddr.netnode;
+ for(Bitu m=0;m<6;m++) {
+ if(addrptr[m]!=outbuffer[m+0xa])isloopback=false;
+ if(immedAddr[m]!=0xff) islocalbroadcast=false;
+ }
+ LOG_IPX("SEND crc:%2x",packetCRC(&outbuffer[0], packetsize));
+ if(!isloopback) {
+ outPacket.channel = UDPChannel;
+ outPacket.data = (Uint8 *)&outbuffer[0];
+ outPacket.len = packetsize;
+ outPacket.maxlen = packetsize;
+ // Since we're using a channel, we won't send the IP address again
+ result = SDLNet_UDP_Send(ipxClientSocket, UDPChannel, &outPacket);
+
+ if(result == 0) {
+ LOG_MSG("IPX: Could not send packet: %s", SDLNet_GetError());
+ sendecb->setCompletionFlag(COMP_HARDWAREERROR);
+ sendecb->NotifyESR();
+ DisconnectFromServer(true);
+ return;
+ } else {
+ sendecb->setCompletionFlag(COMP_SUCCESS);
+ LOG_IPX("Packet sent: size: %d",packetsize);
+ }
+ }
+ else sendecb->setCompletionFlag(COMP_SUCCESS);
+
+ if(isloopback||islocalbroadcast) {
+ // Send packet back to ourselves.
+ receivePacket(&outbuffer[0],packetsize);
+ LOG_IPX("Packet back: loopback:%d, broadcast:%d",isloopback,islocalbroadcast);
+ }
+ sendecb->NotifyESR();
+}
+
+static bool pingCheck(IPXHeader * outHeader) {
+ char buffer[1024];
+ Bits result;
+ UDPpacket regPacket;
+ IPXHeader *regHeader;
+ regPacket.data = (Uint8 *)buffer;
+ regPacket.maxlen = sizeof(buffer);
+ regPacket.channel = UDPChannel;
+ regHeader = (IPXHeader *)buffer;
+
+ result = SDLNet_UDP_Recv(ipxClientSocket, &regPacket);
+ if (result != 0) {
+ memcpy(outHeader, regHeader, sizeof(IPXHeader));
+ return true;
+ }
+ return false;
+}
+
+bool ConnectToServer(char const *strAddr) {
+ int numsent;
+ UDPpacket regPacket;
+ IPXHeader regHeader;
+ if(!SDLNet_ResolveHost(&ipxServConnIp, strAddr, (Bit16u)udpPort)) {
+
+ // Generate the MAC address. This is made by zeroing out the first two
+ // octets and then using the actual IP address for the last 4 octets.
+ // This idea is from the IPX over IP implementation as specified in RFC 1234:
+ // http://www.faqs.org/rfcs/rfc1234.html
+
+ // Select an anonymous UDP port
+ ipxClientSocket = SDLNet_UDP_Open(0);
+ if(ipxClientSocket) {
+ // Bind UDP port to address to channel
+ UDPChannel = SDLNet_UDP_Bind(ipxClientSocket,-1,&ipxServConnIp);
+ //ipxClientSocket = SDLNet_TCP_Open(&ipxServConnIp);
+ SDLNet_Write16(0xffff, regHeader.checkSum);
+ SDLNet_Write16(sizeof(regHeader), regHeader.length);
+
+ // Echo packet with zeroed dest and src is a server registration packet
+ SDLNet_Write32(0, regHeader.dest.network);
+ regHeader.dest.addr.byIP.host = 0x0;
+ regHeader.dest.addr.byIP.port = 0x0;
+ SDLNet_Write16(0x2, regHeader.dest.socket);
+
+ SDLNet_Write32(0, regHeader.src.network);
+ regHeader.src.addr.byIP.host = 0x0;
+ regHeader.src.addr.byIP.port = 0x0;
+ SDLNet_Write16(0x2, regHeader.src.socket);
+ regHeader.transControl = 0;
+
+ regPacket.data = (Uint8 *)&regHeader;
+ regPacket.len = sizeof(regHeader);
+ regPacket.maxlen = sizeof(regHeader);
+ regPacket.channel = UDPChannel;
+ // Send registration string to server. If server doesn't get
+ // this, client will not be registered
+ numsent = SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, &regPacket);
+
+ if(!numsent) {
+ LOG_MSG("IPX: Unable to connect to server: %s", SDLNet_GetError());
+ SDLNet_UDP_Close(ipxClientSocket);
+ return false;
+ } else {
+ // Wait for return packet from server.
+ // This will contain our IPX address and port num
+ Bits result;
+ Bit32u ticks, elapsed;
+ ticks = GetTicks();
+
+ while(true) {
+ elapsed = GetTicks() - ticks;
+ if(elapsed > 5000) {
+ LOG_MSG("Timeout connecting to server at %s", strAddr);
+ SDLNet_UDP_Close(ipxClientSocket);
+
+ return false;
+ }
+ CALLBACK_Idle();
+ result = SDLNet_UDP_Recv(ipxClientSocket, &regPacket);
+ if (result != 0) {
+ memcpy(localIpxAddr.netnode, regHeader.dest.addr.byNode.node, sizeof(localIpxAddr.netnode));
+ memcpy(localIpxAddr.netnum, regHeader.dest.network, sizeof(localIpxAddr.netnum));
+ break;
+ }
+
+ }
+
+ LOG_MSG("IPX: Connected to server. IPX address is %d:%d:%d:%d:%d:%d", CONVIPX(localIpxAddr.netnode));
+
+ incomingPacket.connected = true;
+ TIMER_AddTickHandler(&IPX_ClientLoop);
+ return true;
+ }
+ } else {
+ LOG_MSG("IPX: Unable to open socket");
+ }
+ } else {
+ LOG_MSG("IPX: Unable resolve connection to server");
+ }
+ return false;
+}
+
+void IPX_NetworkInit() {
+
+ localIpxAddr.netnum[0] = 0x0;
+ localIpxAddr.netnum[1] = 0x0;
+ localIpxAddr.netnum[2] = 0x0;
+ localIpxAddr.netnum[3] = 0x1;
+ localIpxAddr.netnode[0] = 0x00;
+ localIpxAddr.netnode[1] = 0x00;
+ localIpxAddr.netnode[2] = 0x00;
+ localIpxAddr.netnode[3] = 0x00;
+ localIpxAddr.netnode[4] = 0x00;
+ localIpxAddr.netnode[5] = 0x00;
+
+ socketCount = 0;
+ return;
+}
+
+class IPXNET : public Program {
+public:
+ void HelpCommand(const char *helpStr) {
+ // Help on connect command
+ if(strcasecmp("connect", helpStr) == 0) {
+ WriteOut("IPXNET CONNECT opens a connection to an IPX tunneling server running on another\n");
+ WriteOut("DOSBox session. The \"address\" parameter specifies the IP address or host name\n");
+ WriteOut("of the server computer. One can also specify the UDP port to use. By default\n");
+ WriteOut("IPXNET uses port 213, the assigned IANA port for IPX tunneling, for its\nconnection.\n\n");
+ WriteOut("The syntax for IPXNET CONNECT is:\n\n");
+ WriteOut("IPXNET CONNECT address <port>\n\n");
+ return;
+ }
+ // Help on the disconnect command
+ if(strcasecmp("disconnect", helpStr) == 0) {
+ WriteOut("IPXNET DISCONNECT closes the connection to the IPX tunneling server.\n\n");
+ WriteOut("The syntax for IPXNET DISCONNECT is:\n\n");
+ WriteOut("IPXNET DISCONNECT\n\n");
+ return;
+ }
+ // Help on the startserver command
+ if(strcasecmp("startserver", helpStr) == 0) {
+ WriteOut("IPXNET STARTSERVER starts and IPX tunneling server on this DOSBox session. By\n");
+ WriteOut("default, the server will accept connections on UDP port 213, though this can be\n");
+ WriteOut("changed. Once the server is started, DOSBox will automatically start a client\n");
+ WriteOut("connection to the IPX tunneling server.\n\n");
+ WriteOut("The syntax for IPXNET STARTSERVER is:\n\n");
+ WriteOut("IPXNET STARTSERVER <port>\n\n");
+ return;
+ }
+ // Help on the stop server command
+ if(strcasecmp("stopserver", helpStr) == 0) {
+ WriteOut("IPXNET STOPSERVER stops the IPX tunneling server running on this DOSBox\nsession.");
+ WriteOut(" Care should be taken to ensure that all other connections have\nterminated ");
+ WriteOut("as well since stopping the server may cause lockups on other\nmachines still using ");
+ WriteOut("the IPX tunneling server.\n\n");
+ WriteOut("The syntax for IPXNET STOPSERVER is:\n\n");
+ WriteOut("IPXNET STOPSERVER\n\n");
+ return;
+ }
+ // Help on the ping command
+ if(strcasecmp("ping", helpStr) == 0) {
+ WriteOut("IPXNET PING broadcasts a ping request through the IPX tunneled network. In \n");
+ WriteOut("response, all other connected computers will respond to the ping and report\n");
+ WriteOut("the time it took to receive and send the ping message.\n\n");
+ WriteOut("The syntax for IPXNET PING is:\n\n");
+ WriteOut("IPXNET PING\n\n");
+ return;
+ }
+ // Help on the status command
+ if(strcasecmp("status", helpStr) == 0) {
+ WriteOut("IPXNET STATUS reports the current state of this DOSBox's sessions IPX tunneling\n");
+ WriteOut("network. For a list of the computers connected to the network use the IPXNET \n");
+ WriteOut("PING command.\n\n");
+ WriteOut("The syntax for IPXNET STATUS is:\n\n");
+ WriteOut("IPXNET STATUS\n\n");
+ return;
+ }
+ }
+
+ void Run(void)
+ {
+ WriteOut("IPX Tunneling utility for DOSBox\n\n");
+ if(!cmd->GetCount()) {
+ WriteOut("The syntax of this command is:\n\n");
+ WriteOut("IPXNET [ CONNECT | DISCONNECT | STARTSERVER | STOPSERVER | PING | HELP |\n STATUS ]\n\n");
+ return;
+ }
+
+ if(cmd->FindCommand(1, temp_line)) {
+ if(strcasecmp("help", temp_line.c_str()) == 0) {
+ if(!cmd->FindCommand(2, temp_line)) {
+ WriteOut("The following are valid IPXNET commands:\n\n");
+ WriteOut("IPXNET CONNECT IPXNET DISCONNECT IPXNET STARTSERVER\n");
+ WriteOut("IPXNET STOPSERVER IPXNET PING IPXNET STATUS\n\n");
+ WriteOut("To get help on a specific command, type:\n\n");
+ WriteOut("IPXNET HELP command\n\n");
+
+ } else {
+ HelpCommand(temp_line.c_str());
+ return;
+ }
+ return;
+ }
+ if(strcasecmp("startserver", temp_line.c_str()) == 0) {
+ if(!isIpxServer) {
+ if(incomingPacket.connected) {
+ WriteOut("IPX Tunneling Client already connected to another server. Disconnect first.\n");
+ return;
+ }
+ bool startsuccess;
+ if(!cmd->FindCommand(2, temp_line)) {
+ udpPort = 213;
+ } else {
+ udpPort = strtol(temp_line.c_str(), NULL, 10);
+ }
+ startsuccess = IPX_StartServer((Bit16u)udpPort);
+ if(startsuccess) {
+ WriteOut("IPX Tunneling Server started\n");
+ isIpxServer = true;
+ ConnectToServer("localhost");
+ } else {
+ WriteOut("IPX Tunneling Server failed to start.\n");
+ if(udpPort < 1024) WriteOut("Try a port number above 1024. See IPXNET HELP CONNECT on how to specify a port.\n");
+ }
+ } else {
+ WriteOut("IPX Tunneling Server already started\n");
+ }
+ return;
+ }
+ if(strcasecmp("stopserver", temp_line.c_str()) == 0) {
+ if(!isIpxServer) {
+ WriteOut("IPX Tunneling Server not running in this DOSBox session.\n");
+ } else {
+ isIpxServer = false;
+ DisconnectFromServer(false);
+ IPX_StopServer();
+ WriteOut("IPX Tunneling Server stopped.");
+ }
+ return;
+ }
+ if(strcasecmp("connect", temp_line.c_str()) == 0) {
+ char strHost[1024];
+ if(incomingPacket.connected) {
+ WriteOut("IPX Tunneling Client already connected.\n");
+ return;
+ }
+ if(!cmd->FindCommand(2, temp_line)) {
+ WriteOut("IPX Server address not specified.\n");
+ return;
+ }
+ strcpy(strHost, temp_line.c_str());
+
+ if(!cmd->FindCommand(3, temp_line)) {
+ udpPort = 213;
+ } else {
+ udpPort = strtol(temp_line.c_str(), NULL, 10);
+ }
+
+ if(ConnectToServer(strHost)) {
+ WriteOut("IPX Tunneling Client connected to server at %s.\n", strHost);
+ } else {
+ WriteOut("IPX Tunneling Client failed to connect to server at %s.\n", strHost);
+ }
+ return;
+ }
+
+ if(strcasecmp("disconnect", temp_line.c_str()) == 0) {
+ if(!incomingPacket.connected) {
+ WriteOut("IPX Tunneling Client not connected.\n");
+ return;
+ }
+ // TODO: Send a packet to the server notifying of disconnect
+ WriteOut("IPX Tunneling Client disconnected from server.\n");
+ DisconnectFromServer(false);
+ return;
+ }
+
+ if(strcasecmp("status", temp_line.c_str()) == 0) {
+ WriteOut("IPX Tunneling Status:\n\n");
+ WriteOut("Server status: ");
+ if(isIpxServer) WriteOut("ACTIVE\n"); else WriteOut("INACTIVE\n");
+ WriteOut("Client status: ");
+ if(incomingPacket.connected) {
+ WriteOut("CONNECTED -- Server at %d.%d.%d.%d port %d\n", CONVIP(ipxServConnIp.host), udpPort);
+ } else {
+ WriteOut("DISCONNECTED\n");
+ }
+ if(isIpxServer) {
+ WriteOut("List of active connections:\n\n");
+ int i;
+ IPaddress *ptrAddr;
+ for(i=0;i<SOCKETTABLESIZE;i++) {
+ if(IPX_isConnectedToServer(i,&ptrAddr)) {
+ WriteOut(" %d.%d.%d.%d from port %d\n", CONVIP(ptrAddr->host), SDLNet_Read16(&ptrAddr->port));
+ }
+ }
+ WriteOut("\n");
+ }
+ return;
+ }
+
+ if(strcasecmp("ping", temp_line.c_str()) == 0) {
+ Bit32u ticks;
+ IPXHeader pingHead;
+
+ if(!incomingPacket.connected) {
+ WriteOut("IPX Tunneling Client not connected.\n");
+ return;
+ }
+ TIMER_DelTickHandler(&IPX_ClientLoop);
+ WriteOut("Sending broadcast ping:\n\n");
+ pingSend();
+ ticks = GetTicks();
+ while((GetTicks() - ticks) < 1500) {
+ CALLBACK_Idle();
+ if(pingCheck(&pingHead)) {
+ WriteOut("Response from %d.%d.%d.%d, port %d time=%dms\n", CONVIP(pingHead.src.addr.byIP.host), SDLNet_Read16(&pingHead.src.addr.byIP.port), GetTicks() - ticks);
+ }
+ }
+ TIMER_AddTickHandler(&IPX_ClientLoop);
+ return;
+ }
+ }
+ }
+};
+
+static void IPXNET_ProgramStart(Program * * make) {
+ *make=new IPXNET;
+}
+
+Bitu IPX_ESRHandler(void) {
+ LOG_IPX("ESR: >>>>>>>>>>>>>>>" );
+ while(ESRList!=NULL) {
+ // LOG_IPX("ECB: SN%7d notified.", ESRList->SerialNumber);
+ if(ESRList->databuffer) ESRList->writeData();
+ if(ESRList->getESRAddr()) {
+ // setup registers
+ SegSet16(es, RealSeg(ESRList->ECBAddr));
+ reg_si = RealOff(ESRList->ECBAddr);
+ reg_al = 0xff;
+ CALLBACK_RunRealFar(RealSeg(ESRList->getESRAddr()),
+ RealOff(ESRList->getESRAddr()));
+ }
+ delete ESRList;
+ } // while
+
+ IO_WriteB(0xa0,0x63); //EOI11
+ IO_WriteB(0x20,0x62); //EOI2
+ LOG_IPX("ESR: <<<<<<<<<<<<<<<");
+ return CBRET_NONE;
+}
+
+void VFILE_Remove(const char *name);
+
+class IPX: public Module_base {
+private:
+ CALLBACK_HandlerObject callback_ipx;
+ CALLBACK_HandlerObject callback_esr;
+ CALLBACK_HandlerObject callback_ipxint;
+ RealPt old_73_vector;
+ static Bit16u dospage;
+public:
+ IPX(Section* configuration):Module_base(configuration) {
+ Section_prop * section = static_cast<Section_prop *>(configuration);
+ if(!section->Get_bool("ipx")) return;
+ if(!SDLNetInited) {
+ if(SDLNet_Init() == -1){
+ LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError());
+ return;
+ }
+ SDLNetInited = true;
+ }
+
+ ECBList = NULL;
+ ESRList = NULL;
+ isIpxServer = false;
+ isIpxConnected = false;
+ IPX_NetworkInit();
+
+ DOS_AddMultiplexHandler(IPX_Multiplex);
+
+ callback_ipx.Install(&IPX_Handler,CB_RETF,"IPX Handler");
+ ipx_callback = callback_ipx.Get_RealPointer();
+
+ callback_ipxint.Install(&IPX_IntHandler,CB_IRET,"IPX (int 7a)");
+ callback_ipxint.Set_RealVec(0x7a);
+
+ callback_esr.Allocate(&IPX_ESRHandler,"IPX_ESR");
+ Bit16u call_ipxesr1 = callback_esr.Get_callback();
+
+ if(!dospage) dospage = DOS_GetMemory(2); // can not be freed yet
+
+ PhysPt phyDospage = PhysMake(dospage,0);
+
+ LOG_IPX("ESR callback address: %x, HandlerID %d", phyDospage,call_ipxesr1);
+
+ //save registers
+ phys_writeb(phyDospage+0,(Bit8u)0xFA); // CLI
+ phys_writeb(phyDospage+1,(Bit8u)0x60); // PUSHA
+ phys_writeb(phyDospage+2,(Bit8u)0x1E); // PUSH DS
+ phys_writeb(phyDospage+3,(Bit8u)0x06); // PUSH ES
+ phys_writew(phyDospage+4,(Bit16u)0xA00F); // PUSH FS
+ phys_writew(phyDospage+6,(Bit16u)0xA80F); // PUSH GS
+
+ // callback
+ phys_writeb(phyDospage+8,(Bit8u)0xFE); // GRP 4
+ phys_writeb(phyDospage+9,(Bit8u)0x38); // Extra Callback instruction
+ phys_writew(phyDospage+10,call_ipxesr1); // Callback identifier
+
+ // register recreation
+ phys_writew(phyDospage+12,(Bit16u)0xA90F); // POP GS
+ phys_writew(phyDospage+14,(Bit16u)0xA10F); // POP FS
+ phys_writeb(phyDospage+16,(Bit8u)0x07); // POP ES
+ phys_writeb(phyDospage+17,(Bit8u)0x1F); // POP DS
+ phys_writeb(phyDospage+18,(Bit8u)0x61); // POPA
+ phys_writeb(phyDospage+19,(Bit8u)0xCF); // IRET: restores flags, CS, IP
+
+ // IPX version 2.12
+ //phys_writeb(phyDospage+27,(Bit8u)0x2);
+ //phys_writeb(phyDospage+28,(Bit8u)0x12);
+ //IPXVERpointer = RealMake(dospage,27);
+
+ RealPt ESRRoutineBase = RealMake(dospage, 0);
+
+ // Interrupt enabling
+ RealSetVec(0x73,ESRRoutineBase,old_73_vector); // IRQ11
+ IO_WriteB(0xa1,IO_ReadB(0xa1)&(~8)); // enable IRQ11
+
+ PROGRAMS_MakeFile("IPXNET.COM",IPXNET_ProgramStart);
+ }
+
+ ~IPX() {
+ Section_prop * section = static_cast<Section_prop *>(m_configuration);
+ PIC_RemoveEvents(IPX_AES_EventHandler);
+ if(!section->Get_bool("ipx")) return;
+
+ if(isIpxServer) {
+ isIpxServer = false;
+ IPX_StopServer();
+ }
+ DisconnectFromServer(false);
+
+ DOS_DelMultiplexHandler(IPX_Multiplex);
+ RealSetVec(0x73,old_73_vector);
+ IO_WriteB(0xa1,IO_ReadB(0xa1)|8); // disable IRQ11
+
+ PhysPt phyDospage = PhysMake(dospage,0);
+ for(Bitu i = 0;i < 32;i++)
+ phys_writeb(phyDospage+i,(Bit8u)0x00);
+
+ VFILE_Remove("IPXNET.COM");
+ }
+};
+
+static IPX* test;
+
+void IPX_ShutDown(Section* sec) {
+ delete test;
+}
+
+void IPX_Init(Section* sec) {
+ test = new IPX(sec);
+ sec->AddDestroyFunction(&IPX_ShutDown,true);
+}
+
+//Initialize static members;
+Bit16u IPX::dospage = 0;
+
+#endif
diff --git a/src/hardware/ipxserver.cpp b/src/hardware/ipxserver.cpp
new file mode 100644
index 000000000..d22ac7757
--- /dev/null
+++ b/src/hardware/ipxserver.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+
+#if C_IPX
+
+#include "dosbox.h"
+#include "ipxserver.h"
+#include "timer.h"
+#include <stdlib.h>
+#include <string.h>
+#include "ipx.h"
+
+IPaddress ipxServerIp; // IPAddress for server's listening port
+UDPsocket ipxServerSocket; // Listening server socket
+
+packetBuffer connBuffer[SOCKETTABLESIZE];
+
+Bit8u inBuffer[IPXBUFFERSIZE];
+IPaddress ipconn[SOCKETTABLESIZE]; // Active TCP/IP connection
+UDPsocket tcpconn[SOCKETTABLESIZE]; // Active TCP/IP connections
+SDLNet_SocketSet serverSocketSet;
+TIMER_TickHandler* serverTimer;
+
+Bit8u packetCRC(Bit8u *buffer, Bit16u bufSize) {
+ Bit8u tmpCRC = 0;
+ Bit16u i;
+ for(i=0;i<bufSize;i++) {
+ tmpCRC ^= *buffer;
+ buffer++;
+ }
+ return tmpCRC;
+}
+
+/*
+static void closeSocket(Bit16u sockidx) {
+ Bit32u host;
+
+ host = ipconn[sockidx].host;
+ LOG_MSG("IPXSERVER: %d.%d.%d.%d disconnected", CONVIP(host));
+
+ SDLNet_TCP_DelSocket(serverSocketSet,tcpconn[sockidx]);
+ SDLNet_TCP_Close(tcpconn[sockidx]);
+ connBuffer[sockidx].connected = false;
+ connBuffer[sockidx].waitsize = false;
+}
+*/
+
+static void sendIPXPacket(Bit8u *buffer, Bit16s bufSize) {
+ Bit16u srcport, destport;
+ Bit32u srchost, desthost;
+ Bit16u i;
+ Bits result;
+ UDPpacket outPacket;
+ outPacket.channel = -1;
+ outPacket.data = buffer;
+ outPacket.len = bufSize;
+ outPacket.maxlen = bufSize;
+ IPXHeader *tmpHeader;
+ tmpHeader = (IPXHeader *)buffer;
+
+ srchost = tmpHeader->src.addr.byIP.host;
+ desthost = tmpHeader->dest.addr.byIP.host;
+
+ srcport = tmpHeader->src.addr.byIP.port;
+ destport = tmpHeader->dest.addr.byIP.port;
+
+
+ if(desthost == 0xffffffff) {
+ // Broadcast
+ for(i=0;i<SOCKETTABLESIZE;i++) {
+ if(connBuffer[i].connected && ((ipconn[i].host != srchost)||(ipconn[i].port!=srcport))) {
+ outPacket.address = ipconn[i];
+ result = SDLNet_UDP_Send(ipxServerSocket,-1,&outPacket);
+ if(result == 0) {
+ LOG_MSG("IPXSERVER: %s", SDLNet_GetError());
+ continue;
+ }
+ //LOG_MSG("IPXSERVER: Packet of %d bytes sent from %d.%d.%d.%d to %d.%d.%d.%d (BROADCAST) (%x CRC)", bufSize, CONVIP(srchost), CONVIP(ipconn[i].host), packetCRC(&buffer[30], bufSize-30));
+ }
+ }
+ } else {
+ // Specific address
+ for(i=0;i<SOCKETTABLESIZE;i++) {
+ if((connBuffer[i].connected) && (ipconn[i].host == desthost) && (ipconn[i].port == destport)) {
+ outPacket.address = ipconn[i];
+ result = SDLNet_UDP_Send(ipxServerSocket,-1,&outPacket);
+ if(result == 0) {
+ LOG_MSG("IPXSERVER: %s", SDLNet_GetError());
+ continue;
+ }
+ //LOG_MSG("IPXSERVER: Packet sent from %d.%d.%d.%d to %d.%d.%d.%d", CONVIP(srchost), CONVIP(desthost));
+ }
+ }
+ }
+
+
+
+
+}
+
+bool IPX_isConnectedToServer(Bits tableNum, IPaddress ** ptrAddr) {
+ if(tableNum >= SOCKETTABLESIZE) return false;
+ *ptrAddr = &ipconn[tableNum];
+ return connBuffer[tableNum].connected;
+}
+
+static void ackClient(IPaddress clientAddr) {
+ IPXHeader regHeader;
+ UDPpacket regPacket;
+ Bits result;
+
+ SDLNet_Write16(0xffff, regHeader.checkSum);
+ SDLNet_Write16(sizeof(regHeader), regHeader.length);
+
+ SDLNet_Write32(0, regHeader.dest.network);
+ PackIP(clientAddr, &regHeader.dest.addr.byIP);
+ SDLNet_Write16(0x2, regHeader.dest.socket);
+
+ SDLNet_Write32(1, regHeader.src.network);
+ PackIP(ipxServerIp, &regHeader.src.addr.byIP);
+ SDLNet_Write16(0x2, regHeader.src.socket);
+ regHeader.transControl = 0;
+
+ regPacket.data = (Uint8 *)&regHeader;
+ regPacket.len = sizeof(regHeader);
+ regPacket.maxlen = sizeof(regHeader);
+ regPacket.address = clientAddr;
+ // Send registration string to client. If client doesn't get this, client will not be registered
+ result = SDLNet_UDP_Send(ipxServerSocket,-1,&regPacket);
+
+}
+
+static void IPX_ServerLoop() {
+ UDPpacket inPacket;
+ IPaddress tmpAddr;
+
+ //char regString[] = "IPX Register\0";
+
+ Bit16u i;
+ Bit32u host;
+ Bits result;
+
+ inPacket.channel = -1;
+ inPacket.data = &inBuffer[0];
+ inPacket.maxlen = IPXBUFFERSIZE;
+
+
+ result = SDLNet_UDP_Recv(ipxServerSocket, &inPacket);
+ if (result != 0) {
+ // Check to see if incoming packet is a registration packet
+ // For this, I just spoofed the echo protocol packet designation 0x02
+ IPXHeader *tmpHeader;
+ tmpHeader = (IPXHeader *)&inBuffer[0];
+
+ // Check to see if echo packet
+ if(SDLNet_Read16(tmpHeader->dest.socket) == 0x2) {
+ // Null destination node means its a server registration packet
+ if(tmpHeader->dest.addr.byIP.host == 0x0) {
+ UnpackIP(tmpHeader->src.addr.byIP, &tmpAddr);
+ for(i=0;i<SOCKETTABLESIZE;i++) {
+ if(!connBuffer[i].connected) {
+ // Use prefered host IP rather than the reported source IP
+ // It may be better to use the reported source
+ ipconn[i] = inPacket.address;
+
+ connBuffer[i].connected = true;
+ host = ipconn[i].host;
+ LOG_MSG("IPXSERVER: Connect from %d.%d.%d.%d", CONVIP(host));
+ ackClient(inPacket.address);
+ return;
+ } else {
+ if((ipconn[i].host == tmpAddr.host) && (ipconn[i].port == tmpAddr.port)) {
+
+ LOG_MSG("IPXSERVER: Reconnect from %d.%d.%d.%d", CONVIP(tmpAddr.host));
+ // Update anonymous port number if changed
+ ipconn[i].port = inPacket.address.port;
+ ackClient(inPacket.address);
+ return;
+ }
+ }
+
+ }
+ }
+ }
+
+ // IPX packet is complete. Now interpret IPX header and send to respective IP address
+ sendIPXPacket((Bit8u *)inPacket.data, inPacket.len);
+ }
+}
+
+void IPX_StopServer() {
+ TIMER_DelTickHandler(&IPX_ServerLoop);
+ SDLNet_UDP_Close(ipxServerSocket);
+}
+
+bool IPX_StartServer(Bit16u portnum) {
+ Bit16u i;
+
+ if(!SDLNet_ResolveHost(&ipxServerIp, NULL, portnum)) {
+
+ //serverSocketSet = SDLNet_AllocSocketSet(SOCKETTABLESIZE);
+ ipxServerSocket = SDLNet_UDP_Open(portnum);
+ if(!ipxServerSocket) return false;
+
+ for(i=0;i<SOCKETTABLESIZE;i++) connBuffer[i].connected = false;
+
+ TIMER_AddTickHandler(&IPX_ServerLoop);
+ return true;
+ }
+ return false;
+}
+
+
+#endif
diff --git a/src/hardware/joystick.cpp b/src/hardware/joystick.cpp
new file mode 100644
index 000000000..b6fe5c8ce
--- /dev/null
+++ b/src/hardware/joystick.cpp
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <math.h>
+#include "dosbox.h"
+#include "inout.h"
+#include "setup.h"
+#include "joystick.h"
+#include "pic.h"
+#include "support.h"
+
+
+//TODO: higher axis can't be mapped. Find out why again
+
+//Set to true, to enable automated switching back to square on circle mode if the inputs are outside the cirle.
+#define SUPPORT_MAP_AUTO 0
+
+#define RANGE 64
+#define TIMEOUT 10
+
+#define OHMS 120000/2
+#define JOY_S_CONSTANT 0.0000242
+#define S_PER_OHM 0.000000011
+
+struct JoyStick {
+ enum {JOYMAP_SQUARE,JOYMAP_CIRCLE,JOYMAP_INBETWEEN} mapstate;
+ bool enabled;
+ float xpos, ypos; //position as set by SDL.
+ double xtick, ytick;
+ Bitu xcount, ycount;
+ bool button[2];
+ int deadzone; //Deadzone (value between 0 and 100) interpreted as percentage.
+ bool transformed; //Whether xpos,ypos have been converted to xfinal and yfinal. Cleared when new xpos orypos have been set
+ float xfinal, yfinal; //position returned to the game for stick 0.
+
+ void clip() {
+ if (xfinal > 1.0) xfinal = 1.0;
+ else if (xfinal < -1.0) xfinal = -1.0;
+ if (yfinal > 1.0) yfinal = 1.0;
+ else if (yfinal < -1.0) yfinal = -1.0;
+ }
+
+ void fake_digital() {
+ if (xpos > 0.5f) xfinal = 1.0f;
+ else if (xpos < -0.5f) xfinal = -1.0f;
+ else xfinal = 0.0f;
+ if (ypos > 0.5f) yfinal = 1.0f;
+ else if (ypos < -0.5f) yfinal = -1.0f;
+ else yfinal = 0.0f;
+ }
+
+ void transform_circular(){
+ float r = sqrt(xpos * xpos + ypos * ypos);
+ if (r == 0.0) {xfinal = xpos; yfinal = ypos; return;}
+ float deadzone_f = deadzone / 100.0f;
+ float s = 1.0f - deadzone_f;
+ if (r < deadzone_f) {
+ xfinal = yfinal = 0.0f;
+ return;
+ }
+
+ float deadzonescale = (r - deadzone_f) / s; //r if deadzone=0;
+ float xa = fabs(xpos);
+ float ya = fabs(ypos);
+ float maxpos = (ya>xa?ya:xa);
+ xfinal = xpos * deadzonescale/maxpos;
+ yfinal = ypos * deadzonescale/maxpos;
+ }
+
+ void transform_square() {
+ float deadzone_f = deadzone / 100.0f;
+ float s = 1.0f - deadzone_f;
+
+ if (xpos > deadzone_f) {
+ xfinal = (xpos - deadzone_f)/ s;
+ } else if ( xpos < -deadzone_f) {
+ xfinal = (xpos + deadzone_f) / s;
+ } else xfinal = 0.0f;
+ if (ypos > deadzone_f) {
+ yfinal = (ypos - deadzone_f)/ s;
+ } else if ( ypos < - deadzone_f) {
+ yfinal = (ypos + deadzone_f) / s;
+ } else yfinal = 0.0f;
+ }
+
+#if SUPPORT_MAP_AUTO
+ void transform_inbetween(){
+ //First transform to a circle and crop the values to -1.0 -> 1.0
+ //then keep on doing this in future calls until it is safe to switch square mapping
+ // safe = 0.95 as ratio for both axis, or in deadzone
+ transform_circular();
+ clip();
+
+
+ float xrate = xpos / xfinal;
+ float yrate = ypos / yfinal;
+ if (xrate > 0.95 && yrate > 0.95) {
+ mapstate = JOYMAP_SQUARE; //TODO misschien xfinal=xpos...
+ //LOG_MSG("switched to square %f %f",xrate,yrate);
+ }
+ }
+#endif
+ void transform_input(){
+ if (transformed) return;
+ transformed = true;
+ if (deadzone == 100) fake_digital();
+ else {
+ if (mapstate == JOYMAP_SQUARE) transform_square();
+ else if (mapstate == JOYMAP_CIRCLE) transform_circular();
+#if SUPPORT_MAP_AUTO
+ if (mapstate == JOYMAP_INBETWEEN) transform_inbetween(); //No else here
+#endif
+ clip();
+ }
+ }
+
+
+};
+
+JoystickType joytype;
+static JoyStick stick[2];
+
+static Bitu last_write = 0;
+static bool write_active = false;
+static bool swap34 = false;
+bool button_wrapping_enabled = true;
+
+extern bool autofire; //sdl_mapper.cpp
+
+static Bitu read_p201(Bitu port,Bitu iolen) {
+ /* Reset Joystick to 0 after TIMEOUT ms */
+ if(write_active && ((PIC_Ticks - last_write) > TIMEOUT)) {
+ write_active = false;
+ stick[0].xcount = 0;
+ stick[1].xcount = 0;
+ stick[0].ycount = 0;
+ stick[1].ycount = 0;
+// LOG_MSG("reset by time %d %d",PIC_Ticks,last_write);
+ }
+
+ /** Format of the byte to be returned:
+ ** | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ ** +-------------------------------+
+ ** | | | | | | | |
+ ** Joystick B, Button 2 ---+ | | | | | | +--- Joystick A, X Axis
+ ** Joystick B, Button 1 -------+ | | | | +------- Joystick A, Y Axis
+ ** Joystick A, Button 2 -----------+ | | +----------- Joystick B, X Axis
+ ** Joystick A, Button 1 ---------------+ +--------------- Joystick B, Y Axis
+ **/
+ Bit8u ret=0xff;
+ if (stick[0].enabled) {
+ if (stick[0].xcount) stick[0].xcount--; else ret&=~1;
+ if (stick[0].ycount) stick[0].ycount--; else ret&=~2;
+ if (stick[0].button[0]) ret&=~16;
+ if (stick[0].button[1]) ret&=~32;
+ }
+ if (stick[1].enabled) {
+ if (stick[1].xcount) stick[1].xcount--; else ret&=~4;
+ if (stick[1].ycount) stick[1].ycount--; else ret&=~8;
+ if (stick[1].button[0]) ret&=~64;
+ if (stick[1].button[1]) ret&=~128;
+ }
+ return ret;
+}
+
+static Bitu read_p201_timed(Bitu port,Bitu iolen) {
+ Bit8u ret=0xff;
+ double currentTick = PIC_FullIndex();
+ if( stick[0].enabled ){
+ if( stick[0].xtick < currentTick ) ret &=~1;
+ if( stick[0].ytick < currentTick ) ret &=~2;
+ }
+ if( stick[1].enabled ){
+ if( stick[1].xtick < currentTick ) ret &=~4;
+ if( stick[1].ytick < currentTick ) ret &=~8;
+ }
+
+ if (stick[0].enabled) {
+ if (stick[0].button[0]) ret&=~16;
+ if (stick[0].button[1]) ret&=~32;
+ }
+ if (stick[1].enabled) {
+ if (stick[1].button[0]) ret&=~64;
+ if (stick[1].button[1]) ret&=~128;
+ }
+ return ret;
+}
+
+static void write_p201(Bitu port,Bitu val,Bitu iolen) {
+ /* Store writetime index */
+ write_active = true;
+ last_write = PIC_Ticks;
+ if (stick[0].enabled) {
+ stick[0].transform_input();
+ stick[0].xcount=(Bitu)((stick[0].xfinal*RANGE)+RANGE);
+ stick[0].ycount=(Bitu)((stick[0].yfinal*RANGE)+RANGE);
+ }
+ if (stick[1].enabled) {
+ stick[1].xcount=(Bitu)(((swap34? stick[1].ypos : stick[1].xpos)*RANGE)+RANGE);
+ stick[1].ycount=(Bitu)(((swap34? stick[1].xpos : stick[1].ypos)*RANGE)+RANGE);
+ }
+
+}
+static void write_p201_timed(Bitu port,Bitu val,Bitu iolen) {
+ // Axes take time = 24.2 microseconds + ( 0.011 microsecons/ohm * resistance )
+ // to reset to 0
+ // Pre-calculate the time at which each axis hits 0 here
+ double currentTick = PIC_FullIndex();
+ if (stick[0].enabled) {
+ stick[0].transform_input();
+ stick[0].xtick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM *
+ (double)(((stick[0].xfinal+1.0)* OHMS)) );
+ stick[0].ytick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM *
+ (double)(((stick[0].yfinal+1.0)* OHMS)) );
+ }
+ if (stick[1].enabled) {
+ stick[1].xtick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM *
+ (double)((swap34? stick[1].ypos : stick[1].xpos)+1.0) * OHMS);
+ stick[1].ytick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM *
+ (double)((swap34? stick[1].xpos : stick[1].ypos)+1.0) * OHMS);
+ }
+}
+
+void JOYSTICK_Enable(Bitu which,bool enabled) {
+ if (which<2) stick[which].enabled = enabled;
+}
+
+void JOYSTICK_Button(Bitu which,Bitu num,bool pressed) {
+ if ((which<2) && (num<2)) stick[which].button[num] = pressed;
+}
+
+void JOYSTICK_Move_X(Bitu which,float x) {
+ if(which > 2) return;
+ if (stick[which].xpos == x) return;
+ stick[which].xpos = x;
+ stick[which].transformed = false;
+// if( which == 0 || joytype != JOY_FCS)
+// stick[which].applied_conversion; //todo
+}
+
+void JOYSTICK_Move_Y(Bitu which,float y) {
+ if(which > 2) return;
+ if (stick[which].ypos == y) return;
+ stick[which].ypos = y;
+ stick[which].transformed = false;
+}
+
+bool JOYSTICK_IsEnabled(Bitu which) {
+ if (which<2) return stick[which].enabled;
+ return false;
+}
+
+bool JOYSTICK_GetButton(Bitu which, Bitu num) {
+ if ((which<2) && (num<2)) return stick[which].button[num];
+ return false;
+}
+
+float JOYSTICK_GetMove_X(Bitu which) {
+ if (which > 1) return 0.0f;
+ if (which == 0) { stick[0].transform_input(); return stick[0].xfinal;}
+ return stick[1].xpos;
+}
+
+float JOYSTICK_GetMove_Y(Bitu which) {
+ if (which > 1) return 0.0f;
+ if (which == 0) { stick[0].transform_input(); return stick[0].yfinal;}
+ return stick[1].ypos;
+}
+
+class JOYSTICK:public Module_base{
+private:
+ IO_ReadHandleObject ReadHandler;
+ IO_WriteHandleObject WriteHandler;
+public:
+ JOYSTICK(Section* configuration):Module_base(configuration){
+ Section_prop * section = static_cast<Section_prop *>(configuration);
+ const char * type = section->Get_string("joysticktype");
+ if (!strcasecmp(type,"none")) joytype = JOY_NONE;
+ else if (!strcasecmp(type,"false")) joytype = JOY_NONE;
+ else if (!strcasecmp(type,"auto")) joytype = JOY_AUTO;
+ else if (!strcasecmp(type,"2axis")) joytype = JOY_2AXIS;
+ else if (!strcasecmp(type,"4axis")) joytype = JOY_4AXIS;
+ else if (!strcasecmp(type,"4axis_2")) joytype = JOY_4AXIS_2;
+ else if (!strcasecmp(type,"fcs")) joytype = JOY_FCS;
+ else if (!strcasecmp(type,"ch")) joytype = JOY_CH;
+ else joytype = JOY_AUTO;
+
+ bool timed = section->Get_bool("timed");
+ if (timed) {
+ ReadHandler.Install(0x201,read_p201_timed,IO_MB);
+ WriteHandler.Install(0x201,write_p201_timed,IO_MB);
+ } else {
+ ReadHandler.Install(0x201,read_p201,IO_MB);
+ WriteHandler.Install(0x201,write_p201,IO_MB);
+ }
+ autofire = section->Get_bool("autofire");
+ swap34 = section->Get_bool("swap34");
+ button_wrapping_enabled = section->Get_bool("buttonwrap");
+ stick[0].xtick = stick[0].ytick = stick[1].xtick =
+ stick[1].ytick = PIC_FullIndex();
+ stick[0].xpos = stick[0].ypos = stick[1].xpos = stick[1].ypos = 0.0f;
+ stick[0].transformed = false;
+
+
+ stick[0].mapstate = JoyStick::JOYMAP_SQUARE;
+ bool circ = section->Get_bool("circularinput");
+ if (circ) stick[0].mapstate = JoyStick::JOYMAP_CIRCLE;
+ stick[0].deadzone = section->Get_int("deadzone");
+ }
+};
+static JOYSTICK* test;
+
+void JOYSTICK_Destroy(Section* sec) {
+ delete test;
+}
+
+void JOYSTICK_Init(Section* sec) {
+ test = new JOYSTICK(sec);
+ sec->AddDestroyFunction(&JOYSTICK_Destroy,true);
+}
diff --git a/src/hardware/keyboard.cpp b/src/hardware/keyboard.cpp
new file mode 100644
index 000000000..97fbd86cc
--- /dev/null
+++ b/src/hardware/keyboard.cpp
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "keyboard.h"
+#include "inout.h"
+#include "pic.h"
+#include "mem.h"
+#include "mixer.h"
+#include "timer.h"
+
+#define KEYBUFSIZE 32
+#define KEYDELAY 0.300f //Considering 20-30 khz serial clock and 11 bits/char
+
+enum KeyCommands {
+ CMD_NONE,
+ CMD_SETLEDS,
+ CMD_SETTYPERATE,
+ CMD_SETOUTPORT
+};
+
+static struct {
+ Bit8u buffer[KEYBUFSIZE];
+ Bitu used;
+ Bitu pos;
+ struct {
+ KBD_KEYS key;
+ Bitu wait;
+ Bitu pause,rate;
+ } repeat;
+ KeyCommands command;
+ Bit8u p60data;
+ bool p60changed;
+ bool active;
+ bool scanning;
+ bool scheduled;
+} keyb;
+
+static void KEYBOARD_SetPort60(Bit8u val) {
+ keyb.p60changed=true;
+ keyb.p60data=val;
+ if (machine==MCH_PCJR) PIC_ActivateIRQ(6);
+ else PIC_ActivateIRQ(1);
+}
+
+static void KEYBOARD_TransferBuffer(Bitu val) {
+ keyb.scheduled=false;
+ if (!keyb.used) {
+ LOG(LOG_KEYBOARD,LOG_NORMAL)("Transfer started with empty buffer");
+ return;
+ }
+ KEYBOARD_SetPort60(keyb.buffer[keyb.pos]);
+ if (++keyb.pos>=KEYBUFSIZE) keyb.pos-=KEYBUFSIZE;
+ keyb.used--;
+}
+
+
+void KEYBOARD_ClrBuffer(void) {
+ keyb.used=0;
+ keyb.pos=0;
+ PIC_RemoveEvents(KEYBOARD_TransferBuffer);
+ keyb.scheduled=false;
+}
+
+static void KEYBOARD_AddBuffer(Bit8u data) {
+ if (keyb.used>=KEYBUFSIZE) {
+ LOG(LOG_KEYBOARD,LOG_NORMAL)("Buffer full, dropping code");
+ return;
+ }
+ Bitu start=keyb.pos+keyb.used;
+ if (start>=KEYBUFSIZE) start-=KEYBUFSIZE;
+ keyb.buffer[start]=data;
+ keyb.used++;
+ /* Start up an event to start the first IRQ */
+ if (!keyb.scheduled && !keyb.p60changed) {
+ keyb.scheduled=true;
+ PIC_AddEvent(KEYBOARD_TransferBuffer,KEYDELAY);
+ }
+}
+
+
+static Bitu read_p60(Bitu port,Bitu iolen) {
+ keyb.p60changed=false;
+ if (!keyb.scheduled && keyb.used) {
+ keyb.scheduled=true;
+ PIC_AddEvent(KEYBOARD_TransferBuffer,KEYDELAY);
+ }
+ return keyb.p60data;
+}
+
+static void write_p60(Bitu port,Bitu val,Bitu iolen) {
+ switch (keyb.command) {
+ case CMD_NONE: /* None */
+ /* No active command this would normally get sent to the keyboard then */
+ KEYBOARD_ClrBuffer();
+ switch (val) {
+ case 0xed: /* Set Leds */
+ keyb.command=CMD_SETLEDS;
+ KEYBOARD_AddBuffer(0xfa); /* Acknowledge */
+ break;
+ case 0xee: /* Echo */
+ KEYBOARD_AddBuffer(0xee); /* Echo */
+ break;
+ case 0xf2: /* Identify keyboard */
+ /* AT's just send acknowledge */
+ KEYBOARD_AddBuffer(0xfa); /* Acknowledge */
+ break;
+ case 0xf3: /* Typematic rate programming */
+ keyb.command=CMD_SETTYPERATE;
+ KEYBOARD_AddBuffer(0xfa); /* Acknowledge */
+ break;
+ case 0xf4: /* Enable keyboard,clear buffer, start scanning */
+ LOG(LOG_KEYBOARD,LOG_NORMAL)("Clear buffer,enable Scaning");
+ KEYBOARD_AddBuffer(0xfa); /* Acknowledge */
+ keyb.scanning=true;
+ break;
+ case 0xf5: /* Reset keyboard and disable scanning */
+ LOG(LOG_KEYBOARD,LOG_NORMAL)("Reset, disable scanning");
+ keyb.scanning=false;
+ KEYBOARD_AddBuffer(0xfa); /* Acknowledge */
+ break;
+ case 0xf6: /* Reset keyboard and enable scanning */
+ LOG(LOG_KEYBOARD,LOG_NORMAL)("Reset, enable scanning");
+ KEYBOARD_AddBuffer(0xfa); /* Acknowledge */
+ keyb.scanning=false;
+ break;
+ default:
+ /* Just always acknowledge strange commands */
+ LOG(LOG_KEYBOARD,LOG_ERROR)("60:Unhandled command %X",val);
+ KEYBOARD_AddBuffer(0xfa); /* Acknowledge */
+ }
+ return;
+ case CMD_SETOUTPORT:
+ MEM_A20_Enable((val & 2)>0);
+ keyb.command = CMD_NONE;
+ break;
+ case CMD_SETTYPERATE:
+ {
+ static const int delay[] = { 250, 500, 750, 1000 };
+ static const int repeat[] =
+ { 33,37,42,46,50,54,58,63,67,75,83,92,100,
+ 109,118,125,133,149,167,182,200,217,233,
+ 250,270,303,333,370,400,435,476,500 };
+ keyb.repeat.pause = delay[(val>>5)&3];
+ keyb.repeat.rate = repeat[val&0x1f];
+ keyb.command=CMD_NONE;
+ }
+ /* Fallthrough! as setleds does what we want */
+ case CMD_SETLEDS:
+ keyb.command=CMD_NONE;
+ KEYBOARD_ClrBuffer();
+ KEYBOARD_AddBuffer(0xfa); /* Acknowledge */
+ break;
+ }
+}
+
+extern bool TIMER_GetOutput2(void);
+static Bit8u port_61_data = 0;
+static Bitu read_p61(Bitu port,Bitu iolen) {
+ if (TIMER_GetOutput2()) port_61_data|=0x20;
+ else port_61_data&=~0x20;
+ port_61_data^=0x10;
+ return port_61_data;
+}
+
+extern void TIMER_SetGate2(bool);
+static void write_p61(Bitu port,Bitu val,Bitu iolen) {
+ if ((port_61_data ^ val) & 3) {
+ if((port_61_data ^ val) & 1) TIMER_SetGate2(val&0x1);
+ PCSPEAKER_SetType(val & 3);
+ }
+ port_61_data = val;
+}
+
+static Bitu read_p62(Bitu port,Bitu iolen) {
+ Bit8u ret=~0x20;
+ if (TIMER_GetOutput2()) ret|=0x20;
+ return ret;
+}
+
+static void write_p64(Bitu port,Bitu val,Bitu iolen) {
+ switch (val) {
+ case 0xae: /* Activate keyboard */
+ keyb.active=true;
+ if (keyb.used && !keyb.scheduled && !keyb.p60changed) {
+ keyb.scheduled=true;
+ PIC_AddEvent(KEYBOARD_TransferBuffer,KEYDELAY);
+ }
+ LOG(LOG_KEYBOARD,LOG_NORMAL)("Activated");
+ break;
+ case 0xad: /* Deactivate keyboard */
+ keyb.active=false;
+ LOG(LOG_KEYBOARD,LOG_NORMAL)("De-Activated");
+ break;
+ case 0xd0: /* Outport on buffer */
+ KEYBOARD_SetPort60(MEM_A20_Enabled() ? 0x02 : 0);
+ break;
+ case 0xd1: /* Write to outport */
+ keyb.command=CMD_SETOUTPORT;
+ break;
+ default:
+ LOG(LOG_KEYBOARD,LOG_ERROR)("Port 64 write with val %d",val);
+ break;
+ }
+}
+
+static Bitu read_p64(Bitu port,Bitu iolen) {
+ Bit8u status= 0x1c | (keyb.p60changed? 0x1 : 0x0);
+ return status;
+}
+
+void KEYBOARD_AddKey(KBD_KEYS keytype,bool pressed) {
+ Bit8u ret=0;bool extend=false;
+ switch (keytype) {
+ case KBD_esc:ret=1;break;
+ case KBD_1:ret=2;break;
+ case KBD_2:ret=3;break;
+ case KBD_3:ret=4;break;
+ case KBD_4:ret=5;break;
+ case KBD_5:ret=6;break;
+ case KBD_6:ret=7;break;
+ case KBD_7:ret=8;break;
+ case KBD_8:ret=9;break;
+ case KBD_9:ret=10;break;
+ case KBD_0:ret=11;break;
+
+ case KBD_minus:ret=12;break;
+ case KBD_equals:ret=13;break;
+ case KBD_backspace:ret=14;break;
+ case KBD_tab:ret=15;break;
+
+ case KBD_q:ret=16;break;
+ case KBD_w:ret=17;break;
+ case KBD_e:ret=18;break;
+ case KBD_r:ret=19;break;
+ case KBD_t:ret=20;break;
+ case KBD_y:ret=21;break;
+ case KBD_u:ret=22;break;
+ case KBD_i:ret=23;break;
+ case KBD_o:ret=24;break;
+ case KBD_p:ret=25;break;
+
+ case KBD_leftbracket:ret=26;break;
+ case KBD_rightbracket:ret=27;break;
+ case KBD_enter:ret=28;break;
+ case KBD_leftctrl:ret=29;break;
+
+ case KBD_a:ret=30;break;
+ case KBD_s:ret=31;break;
+ case KBD_d:ret=32;break;
+ case KBD_f:ret=33;break;
+ case KBD_g:ret=34;break;
+ case KBD_h:ret=35;break;
+ case KBD_j:ret=36;break;
+ case KBD_k:ret=37;break;
+ case KBD_l:ret=38;break;
+
+ case KBD_semicolon:ret=39;break;
+ case KBD_quote:ret=40;break;
+ case KBD_grave:ret=41;break;
+ case KBD_leftshift:ret=42;break;
+ case KBD_backslash:ret=43;break;
+ case KBD_z:ret=44;break;
+ case KBD_x:ret=45;break;
+ case KBD_c:ret=46;break;
+ case KBD_v:ret=47;break;
+ case KBD_b:ret=48;break;
+ case KBD_n:ret=49;break;
+ case KBD_m:ret=50;break;
+
+ case KBD_comma:ret=51;break;
+ case KBD_period:ret=52;break;
+ case KBD_slash:ret=53;break;
+ case KBD_rightshift:ret=54;break;
+ case KBD_kpmultiply:ret=55;break;
+ case KBD_leftalt:ret=56;break;
+ case KBD_space:ret=57;break;
+ case KBD_capslock:ret=58;break;
+
+ case KBD_f1:ret=59;break;
+ case KBD_f2:ret=60;break;
+ case KBD_f3:ret=61;break;
+ case KBD_f4:ret=62;break;
+ case KBD_f5:ret=63;break;
+ case KBD_f6:ret=64;break;
+ case KBD_f7:ret=65;break;
+ case KBD_f8:ret=66;break;
+ case KBD_f9:ret=67;break;
+ case KBD_f10:ret=68;break;
+
+ case KBD_numlock:ret=69;break;
+ case KBD_scrolllock:ret=70;break;
+
+ case KBD_kp7:ret=71;break;
+ case KBD_kp8:ret=72;break;
+ case KBD_kp9:ret=73;break;
+ case KBD_kpminus:ret=74;break;
+ case KBD_kp4:ret=75;break;
+ case KBD_kp5:ret=76;break;
+ case KBD_kp6:ret=77;break;
+ case KBD_kpplus:ret=78;break;
+ case KBD_kp1:ret=79;break;
+ case KBD_kp2:ret=80;break;
+ case KBD_kp3:ret=81;break;
+ case KBD_kp0:ret=82;break;
+ case KBD_kpperiod:ret=83;break;
+
+ case KBD_extra_lt_gt:ret=86;break;
+ case KBD_f11:ret=87;break;
+ case KBD_f12:ret=88;break;
+
+ //The Extended keys
+
+ case KBD_kpenter:extend=true;ret=28;break;
+ case KBD_rightctrl:extend=true;ret=29;break;
+ case KBD_kpdivide:extend=true;ret=53;break;
+ case KBD_rightalt:extend=true;ret=56;break;
+ case KBD_home:extend=true;ret=71;break;
+ case KBD_up:extend=true;ret=72;break;
+ case KBD_pageup:extend=true;ret=73;break;
+ case KBD_left:extend=true;ret=75;break;
+ case KBD_right:extend=true;ret=77;break;
+ case KBD_end:extend=true;ret=79;break;
+ case KBD_down:extend=true;ret=80;break;
+ case KBD_pagedown:extend=true;ret=81;break;
+ case KBD_insert:extend=true;ret=82;break;
+ case KBD_delete:extend=true;ret=83;break;
+ case KBD_pause:
+ KEYBOARD_AddBuffer(0xe1);
+ KEYBOARD_AddBuffer(29|(pressed?0:0x80));
+ KEYBOARD_AddBuffer(69|(pressed?0:0x80));
+ return;
+ case KBD_printscreen:
+ KEYBOARD_AddBuffer(0xe0);
+ KEYBOARD_AddBuffer(42|(pressed?0:0x80));
+ KEYBOARD_AddBuffer(0xe0);
+ KEYBOARD_AddBuffer(55|(pressed?0:0x80));
+ return;
+ default:
+ E_Exit("Unsupported key press");
+ break;
+ }
+ /* Add the actual key in the keyboard queue */
+ if (pressed) {
+ if (keyb.repeat.key == keytype) keyb.repeat.wait = keyb.repeat.rate;
+ else keyb.repeat.wait = keyb.repeat.pause;
+ keyb.repeat.key = keytype;
+ } else {
+ if (keyb.repeat.key == keytype) {
+ /* repeated key being released */
+ keyb.repeat.key = KBD_NONE;
+ keyb.repeat.wait = 0;
+ }
+ ret += 128;
+ }
+ if (extend) KEYBOARD_AddBuffer(0xe0);
+ KEYBOARD_AddBuffer(ret);
+}
+
+static void KEYBOARD_TickHandler(void) {
+ if (keyb.repeat.wait) {
+ keyb.repeat.wait--;
+ if (!keyb.repeat.wait) KEYBOARD_AddKey(keyb.repeat.key,true);
+ }
+}
+
+void KEYBOARD_Init(Section* sec) {
+ IO_RegisterWriteHandler(0x60,write_p60,IO_MB);
+ IO_RegisterReadHandler(0x60,read_p60,IO_MB);
+ IO_RegisterWriteHandler(0x61,write_p61,IO_MB);
+ IO_RegisterReadHandler(0x61,read_p61,IO_MB);
+ if (machine==MCH_CGA || machine==MCH_HERC) IO_RegisterReadHandler(0x62,read_p62,IO_MB);
+ IO_RegisterWriteHandler(0x64,write_p64,IO_MB);
+ IO_RegisterReadHandler(0x64,read_p64,IO_MB);
+ TIMER_AddTickHandler(&KEYBOARD_TickHandler);
+ write_p61(0,0,0);
+ /* Init the keyb struct */
+ keyb.active=true;
+ keyb.scanning=true;
+ keyb.command=CMD_NONE;
+ keyb.p60changed=false;
+ keyb.repeat.key=KBD_NONE;
+ keyb.repeat.pause=500;
+ keyb.repeat.rate=33;
+ keyb.repeat.wait=0;
+ KEYBOARD_ClrBuffer();
+}
diff --git a/src/hardware/mame/Makefile.am b/src/hardware/mame/Makefile.am
new file mode 100644
index 000000000..3d551b1fd
--- /dev/null
+++ b/src/hardware/mame/Makefile.am
@@ -0,0 +1,9 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libmame.a
+libmame_a_SOURCES = emu.h \
+ fmopl.cpp fmopl.h \
+ saa1099.cpp saa1099.h \
+ sn76496.cpp sn76496.h \
+ ymdeltat.cpp ymdeltat.h \
+ ymf262.cpp ymf262.h
diff --git a/src/hardware/mame/emu.h b/src/hardware/mame/emu.h
new file mode 100644
index 000000000..85a9dd864
--- /dev/null
+++ b/src/hardware/mame/emu.h
@@ -0,0 +1,140 @@
+#ifndef DOSBOX_EMU_H
+#define DOSBOX_EMU_H
+
+
+#include "dosbox.h"
+#if defined(_MSC_VER) && (_MSC_VER <= 1500)
+#include <SDL.h>
+#else
+#include <stdint.h>
+#endif
+#include <math.h>
+#include <float.h>
+#include <stdlib.h>
+#include <memory.h>
+
+#if C_DEBUG
+#include <stdio.h>
+#include <stdarg.h>
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+typedef Bit16s stream_sample_t;
+
+typedef Bit8u u8;
+typedef Bit32u u32;
+
+class device_t;
+struct machine_config;
+
+#define NAME( _ASDF_ ) 0
+#define BIT( _INPUT_, _BIT_ ) ( ( _INPUT_) >> (_BIT_)) & 1
+
+#define ATTR_UNUSED
+#define DECLARE_READ8_MEMBER(name) u8 name( int, int)
+#define DECLARE_WRITE8_MEMBER(name) void name( int, int, u8 data)
+#define READ8_MEMBER(name) u8 name( int, int)
+#define WRITE8_MEMBER(name) void name( int offset, int space, u8 data)
+
+#define DECLARE_DEVICE_TYPE(Type, Class) \
+ extern const device_type Type; \
+ class Class;
+
+#define DEFINE_DEVICE_TYPE(Type, Class, ShortName, FullName) \
+ const device_type Type = 0;
+
+
+class device_sound_interface {
+public:
+ struct sound_stream {
+ void update() {
+ }
+ };
+ sound_stream temp;
+
+ sound_stream* stream_alloc(int whatever, int channels, int size) {
+ return &temp;
+ };
+
+
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) = 0;
+
+ device_sound_interface(const machine_config &mconfig, device_t& _device) {
+ }
+
+};
+
+struct attotime {
+ int whatever;
+
+ static attotime from_hz(int hz) {
+ return attotime();
+ }
+};
+
+struct machine_config {
+};
+
+typedef int device_type;
+
+class device_t {
+ u32 clockRate;
+public:
+ struct machine_t {
+ int describe_context() const {
+ return 0;
+ }
+ };
+
+ machine_t machine() const {
+ return machine_t();
+ }
+
+ //int offset, space;
+
+ u32 clock() const {
+ return clockRate;
+ }
+
+ void logerror(const char* format, ...) {
+#if C_DEBUG
+ char buf[512*2];
+ va_list msg;
+ va_start(msg,format);
+ vsprintf(buf,format,msg);
+ va_end(msg);
+ LOG(LOG_MISC,LOG_NORMAL)("%s",buf);
+#endif
+ }
+
+ static int tag() {
+ return 0;
+ }
+
+ virtual void device_start() {
+ }
+
+ void save_item(int wtf, int blah= 0) {
+ }
+
+ device_t(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 _clock) : clockRate( _clock ) {
+ }
+
+};
+
+
+
+static void auto_free(const device_t::machine_t& machine, void * buffer) {
+ free(buffer);
+}
+
+#define auto_alloc_array_clear(m, t, c) calloc(c, sizeof(t) )
+#define auto_alloc_clear(m, t) static_cast<t*>( calloc(1, sizeof(t) ) )
+
+
+
+
+#endif
diff --git a/src/hardware/mame/fmopl.cpp b/src/hardware/mame/fmopl.cpp
new file mode 100644
index 000000000..90b88345c
--- /dev/null
+++ b/src/hardware/mame/fmopl.cpp
@@ -0,0 +1,2577 @@
+// license:GPL-2.0+
+// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh
+/*
+**
+** File: fmopl.c - software implementation of FM sound generator
+** types OPL and OPL2
+**
+** Copyright Jarek Burczynski (bujar at mame dot net)
+** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development
+**
+** Version 0.72
+**
+
+Revision History:
+
+04-08-2003 Jarek Burczynski:
+ - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip
+ handles memory read/write or during the adpcm synthesis when the chip
+ requests another byte of ADPCM data.
+
+24-07-2003 Jarek Burczynski:
+ - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after
+ some (unknown) delay). Right now it's always set.
+
+14-06-2003 Jarek Burczynski:
+ - implemented all of the status register flags in Y8950 emulation
+ - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since
+ they can be either RAM or ROM
+
+08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip)
+ - corrected ym3526_read() to always set bit 2 and bit 1
+ to HIGH state - identical to ym3812_read (verified on real YM3526)
+
+04-28-2002 Jarek Burczynski:
+ - binary exact Envelope Generator (verified on real YM3812);
+ compared to YM2151: the EG clock is equal to internal_clock,
+ rates are 2 times slower and volume resolution is one bit less
+ - modified interface functions (they no longer return pointer -
+ that's internal to the emulator now):
+ - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init()
+ - corrected 'off by one' error in feedback calculations (when feedback is off)
+ - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22)
+ - speeded up noise generator calculations (Nicola Salmoria)
+
+03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip)
+ Complete rewrite (all verified on real YM3812):
+ - corrected sin_tab and tl_tab data
+ - corrected operator output calculations
+ - corrected waveform_select_enable register;
+ simply: ignore all writes to waveform_select register when
+ waveform_select_enable == 0 and do not change the waveform previously selected.
+ - corrected KSR handling
+ - corrected Envelope Generator: attack shape, Sustain mode and
+ Percussive/Non-percussive modes handling
+ - Envelope Generator rates are two times slower now
+ - LFO amplitude (tremolo) and phase modulation (vibrato)
+ - rhythm sounds phase generation
+ - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm)
+ - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM)
+ - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1)
+
+12-28-2001 Acho A. Tang
+ - reflected Delta-T EOS status on Y8950 status port.
+ - fixed subscription range of attack/decay tables
+
+
+ To do:
+ add delay before key off in CSM mode (see CSMKeyControll)
+ verify volume of the FM part on the Y8950
+*/
+
+#include "emu.h"
+#include "ymdeltat.h"
+#include "fmopl.h"
+
+
+
+/* output final shift */
+#if (OPL_SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (EG timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+/* envelope output entries */
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX ((1<<(ENV_BITS-1))-1) /*511*/
+#define MIN_ATT_INDEX (0)
+
+/* sinwave entries */
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+#define DV (0.1875 / 2.0 )
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+#define TL_TAB_LEN (12 * 2 * TL_RES_LEN)
+
+/* TL_TAB_LEN is calculated as:
+* 12 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+
+
+#define ENV_QUIET ( TL_TAB_LEN >> 4 )
+#define LFO_AM_TAB_ELEMENTS 210
+
+
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* Envelope Generator phases */
+
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+
+/* save output as raw 16-bit sample */
+
+/*#define SAVE_SAMPLE*/
+
+#ifdef SAVE_SAMPLE
+static inline signed int acc_calc(signed int value)
+{
+ if (value>=0)
+ {
+ if (value < 0x0200)
+ return (value & ~0);
+ if (value < 0x0400)
+ return (value & ~1);
+ if (value < 0x0800)
+ return (value & ~3);
+ if (value < 0x1000)
+ return (value & ~7);
+ if (value < 0x2000)
+ return (value & ~15);
+ if (value < 0x4000)
+ return (value & ~31);
+ return (value & ~63);
+ }
+ /*else value < 0*/
+ if (value > -0x0200)
+ return (~abs(value) & ~0);
+ if (value > -0x0400)
+ return (~abs(value) & ~1);
+ if (value > -0x0800)
+ return (~abs(value) & ~3);
+ if (value > -0x1000)
+ return (~abs(value) & ~7);
+ if (value > -0x2000)
+ return (~abs(value) & ~15);
+ if (value > -0x4000)
+ return (~abs(value) & ~31);
+ return (~abs(value) & ~63);
+}
+
+
+static FILE *sample[1];
+ #if 1 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = acc_calc(lt); \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = rt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+#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 */
+
+/* ---------- 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)
+
+
+namespace {
+
+// TODO: make these static members
+
+#define RATE_STEPS (8)
+extern const unsigned char eg_rate_shift[16+64+16];
+extern const unsigned char eg_rate_select[16+64+16];
+
+
+struct OPL_SLOT
+{
+ uint32_t ar; /* attack rate: AR<<2 */
+ uint32_t dr; /* decay rate: DR<<2 */
+ uint32_t rr; /* release rate:RR<<2 */
+ uint8_t KSR; /* key scale rate */
+ uint8_t ksl; /* keyscale level */
+ uint8_t ksr; /* key scale rate: kcode>>KSR */
+ uint8_t mul; /* multiple: mul_tab[ML] */
+
+ /* Phase Generator */
+ uint32_t Cnt; /* frequency counter */
+ uint32_t Incr; /* frequency counter step */
+ uint8_t FB; /* feedback shift value */
+ int32_t *connect1; /* slot1 output pointer */
+ int32_t op1_out[2]; /* slot1 output for feedback */
+ uint8_t CON; /* connection (algorithm) type */
+
+ /* Envelope Generator */
+ uint8_t eg_type; /* percussive/non-percussive mode */
+ uint8_t state; /* phase type */
+ uint32_t TL; /* total level: TL << 2 */
+ int32_t TLL; /* adjusted now TL */
+ int32_t volume; /* envelope counter */
+ int32_t sl; /* sustain level: sl_tab[SL] */
+ uint8_t eg_sh_ar; /* (attack state) */
+ uint8_t eg_sel_ar; /* (attack state) */
+ uint8_t eg_sh_dr; /* (decay state) */
+ uint8_t eg_sel_dr; /* (decay state) */
+ uint8_t eg_sh_rr; /* (release state) */
+ uint8_t eg_sel_rr; /* (release state) */
+ uint32_t key; /* 0 = KEY OFF, >0 = KEY ON */
+
+ /* LFO */
+ uint32_t AMmask; /* LFO Amplitude Modulation enable mask */
+ uint8_t vib; /* LFO Phase Modulation enable flag (active high)*/
+
+ /* waveform select */
+ uint16_t wavetable;
+
+ void KEYON(uint32_t key_set)
+ {
+ if( !key )
+ {
+ /* restart Phase Generator */
+ Cnt = 0;
+ /* phase -> Attack */
+ state = EG_ATT;
+ }
+ key |= key_set;
+ }
+
+ void KEYOFF(uint32_t key_clr)
+ {
+ if( key )
+ {
+ key &= key_clr;
+
+ if( !key )
+ {
+ /* phase -> Release */
+ if (state>EG_REL)
+ state = EG_REL;
+ }
+ }
+ }
+};
+
+struct OPL_CH
+{
+ OPL_SLOT SLOT[2];
+ /* phase generator state */
+ uint32_t block_fnum; /* block+fnum */
+ uint32_t fc; /* Freq. Increment base */
+ uint32_t ksl_base; /* KeyScaleLevel Base step */
+ uint8_t kcode; /* key code (for key scaling) */
+
+
+ /* update phase increment counter of operator (also update the EG rates if necessary) */
+ void CALC_FCSLOT(OPL_SLOT &SLOT)
+ {
+ /* (frequency) phase increment counter */
+ SLOT.Incr = fc * SLOT.mul;
+ int const ksr = kcode >> SLOT.KSR;
+
+ if( SLOT.ksr != ksr )
+ {
+ SLOT.ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT.ar + SLOT.ksr) < 16+62)
+ {
+ SLOT.eg_sh_ar = eg_rate_shift [SLOT.ar + SLOT.ksr ];
+ SLOT.eg_sel_ar = eg_rate_select[SLOT.ar + SLOT.ksr ];
+ }
+ else
+ {
+ SLOT.eg_sh_ar = 0;
+ SLOT.eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT.eg_sh_dr = eg_rate_shift [SLOT.dr + SLOT.ksr ];
+ SLOT.eg_sel_dr = eg_rate_select[SLOT.dr + SLOT.ksr ];
+ SLOT.eg_sh_rr = eg_rate_shift [SLOT.rr + SLOT.ksr ];
+ SLOT.eg_sel_rr = eg_rate_select[SLOT.rr + SLOT.ksr ];
+ }
+ }
+};
+
+/* OPL state */
+struct FM_OPL
+{
+ /* FM channel slots */
+ OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/
+
+ uint32_t eg_cnt; /* global envelope generator counter */
+ uint32_t eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */
+ uint32_t eg_timer_add; /* step of eg_timer */
+ uint32_t eg_timer_overflow; /* envelope generator timer overflows every 1 sample (on real chip) */
+
+ uint8_t rhythm; /* Rhythm mode */
+
+ uint32_t fn_tab[1024]; /* fnumber->increment counter */
+
+ /* LFO */
+ uint32_t LFO_AM;
+ int32_t LFO_PM;
+
+ uint8_t lfo_am_depth;
+ uint8_t lfo_pm_depth_range;
+ uint32_t lfo_am_cnt;
+ uint32_t lfo_am_inc;
+ uint32_t lfo_pm_cnt;
+ uint32_t lfo_pm_inc;
+
+ uint32_t noise_rng; /* 23 bit noise shift register */
+ uint32_t noise_p; /* current noise 'phase' */
+ uint32_t noise_f; /* current noise period */
+
+ uint8_t wavesel; /* waveform select enable flag */
+
+ uint32_t T[2]; /* timer counters */
+ uint8_t st[2]; /* timer enable */
+
+#if BUILD_Y8950
+ /* Delta-T ADPCM unit (Y8950) */
+
+ YM_DELTAT *deltat;
+
+ /* Keyboard and I/O ports interface */
+ uint8_t portDirection;
+ uint8_t portLatch;
+ OPL_PORTHANDLER_R porthandler_r;
+ OPL_PORTHANDLER_W porthandler_w;
+ device_t * port_param;
+ OPL_PORTHANDLER_R keyboardhandler_r;
+ OPL_PORTHANDLER_W keyboardhandler_w;
+ device_t * keyboard_param;
+#endif
+
+ /* external event callback handlers */
+ OPL_TIMERHANDLER timer_handler; /* TIMER handler */
+ device_t *TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ device_t *IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */
+ device_t *UpdateParam; /* stream update parameter */
+
+ uint8_t type; /* chip type */
+ uint8_t address; /* address register */
+ uint8_t status; /* status flag */
+ uint8_t statusmask; /* status mask */
+ uint8_t mode; /* Reg.08 : CSM,notesel,etc. */
+
+ uint32_t clock; /* master clock (Hz) */
+ uint32_t rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ //attotime TimerBase; /* Timer base time (==sampling time)*/
+ device_t *device;
+
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+ signed int output[1];
+#if BUILD_Y8950
+ int32_t output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */
+#endif
+
+
+ /* status set and IRQ handling */
+ void STATUS_SET(int flag)
+ {
+ /* set status flag */
+ status |= flag;
+ if(!(status & 0x80))
+ {
+ if(status & statusmask)
+ { /* IRQ on */
+ status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(IRQHandler) (IRQHandler)(IRQParam,1);
+ }
+ }
+ }
+
+ /* status reset and IRQ handling */
+ void STATUS_RESET(int flag)
+ {
+ /* reset status flag */
+ status &=~flag;
+ if(status & 0x80)
+ {
+ if (!(status & statusmask) )
+ {
+ status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(IRQHandler) (IRQHandler)(IRQParam,0);
+ }
+ }
+ }
+
+ /* IRQ mask set */
+ void STATUSMASK_SET(int flag)
+ {
+ statusmask = flag;
+ /* IRQ handling check */
+ STATUS_SET(0);
+ STATUS_RESET(0);
+ }
+
+
+ /* advance LFO to next sample */
+ void advance_lfo()
+ {
+ /* LFO */
+ lfo_am_cnt += lfo_am_inc;
+ if (lfo_am_cnt >= (uint32_t(LFO_AM_TAB_ELEMENTS) << LFO_SH)) /* lfo_am_table is 210 elements long */
+ lfo_am_cnt -= (uint32_t(LFO_AM_TAB_ELEMENTS) << LFO_SH);
+
+ uint8_t const tmp = lfo_am_table[ lfo_am_cnt >> LFO_SH ];
+
+ LFO_AM = lfo_am_depth ? tmp : tmp >> 2;
+
+ lfo_pm_cnt += lfo_pm_inc;
+ LFO_PM = (lfo_pm_cnt>>LFO_SH & 7) | lfo_pm_depth_range;
+ }
+
+ /* advance to next sample */
+ void advance()
+ {
+ eg_timer += eg_timer_add;
+
+ while (eg_timer >= eg_timer_overflow)
+ {
+ eg_timer -= eg_timer_overflow;
+
+ eg_cnt++;
+
+ for (int i=0; i<9*2; i++)
+ {
+ OPL_CH &CH = P_CH[i/2];
+ OPL_SLOT &op = CH.SLOT[i&1];
+
+ /* Envelope Generator */
+ switch(op.state)
+ {
+ case EG_ATT: /* attack phase */
+ if ( !(eg_cnt & ((1<<op.eg_sh_ar)-1) ) )
+ {
+ op.volume += (~op.volume *
+ (eg_inc[op.eg_sel_ar + ((eg_cnt>>op.eg_sh_ar)&7)])
+ ) >>3;
+
+ if (op.volume <= MIN_ATT_INDEX)
+ {
+ op.volume = MIN_ATT_INDEX;
+ op.state = EG_DEC;
+ }
+
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if ( !(eg_cnt & ((1<<op.eg_sh_dr)-1) ) )
+ {
+ op.volume += eg_inc[op.eg_sel_dr + ((eg_cnt>>op.eg_sh_dr)&7)];
+
+ if ( op.volume >= op.sl )
+ op.state = EG_SUS;
+
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+
+ /* this is important behaviour:
+ one can change percusive/non-percussive modes on the fly and
+ the chip will remain in sustain phase - verified on real YM3812 */
+
+ if(op.eg_type) /* non-percussive mode */
+ {
+ /* do nothing */
+ }
+ else /* percussive mode */
+ {
+ /* during sustain phase chip adds Release Rate (in percussive mode) */
+ if ( !(eg_cnt & ((1<<op.eg_sh_rr)-1) ) )
+ {
+ op.volume += eg_inc[op.eg_sel_rr + ((eg_cnt>>op.eg_sh_rr)&7)];
+
+ if ( op.volume >= MAX_ATT_INDEX )
+ op.volume = MAX_ATT_INDEX;
+ }
+ /* else do nothing in sustain phase */
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if ( !(eg_cnt & ((1<<op.eg_sh_rr)-1) ) )
+ {
+ op.volume += eg_inc[op.eg_sel_rr + ((eg_cnt>>op.eg_sh_rr)&7)];
+
+ if ( op.volume >= MAX_ATT_INDEX )
+ {
+ op.volume = MAX_ATT_INDEX;
+ op.state = EG_OFF;
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ for (int i=0; i<9*2; i++)
+ {
+ OPL_CH &CH = P_CH[i/2];
+ OPL_SLOT &op = CH.SLOT[i&1];
+
+ /* Phase Generator */
+ if(op.vib)
+ {
+ unsigned int block_fnum = CH.block_fnum;
+ unsigned int const fnum_lfo = (block_fnum&0x0380) >> 7;
+
+ signed int const lfo_fn_table_index_offset = lfo_pm_table[LFO_PM + 16*fnum_lfo ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum += lfo_fn_table_index_offset;
+ uint8_t const block = (block_fnum&0x1c00) >> 10;
+ op.Cnt += (fn_tab[block_fnum&0x03ff] >> (7-block)) * op.mul;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ op.Cnt += op.Incr;
+ }
+ }
+ else /* LFO phase modulation disabled for this operator */
+ {
+ op.Cnt += op.Incr;
+ }
+ }
+
+ /* The Noise Generator of the YM3812 is 23-bit shift register.
+ * Period is equal to 2^23-2 samples.
+ * Register works at sampling frequency of the chip, so output
+ * can change on every sample.
+ *
+ * Output of the register and input to the bit 22 is:
+ * bit0 XOR bit14 XOR bit15 XOR bit22
+ *
+ * Simply use bit 22 as the noise output.
+ */
+
+ noise_p += noise_f;
+ int i = noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */
+ noise_p &= FREQ_MASK;
+ while (i)
+ {
+ /*
+ uint32_t j;
+ j = ( (noise_rng) ^ (noise_rng>>14) ^ (noise_rng>>15) ^ (noise_rng>>22) ) & 1;
+ noise_rng = (j<<22) | (noise_rng>>1);
+ */
+
+ /*
+ Instead of doing all the logic operations above, we
+ use a trick here (and use bit 0 as the noise output).
+ The difference is only that the noise bit changes one
+ step ahead. This doesn't matter since we don't know
+ what is real state of the noise_rng after the reset.
+ */
+
+ if (noise_rng & 1) noise_rng ^= 0x800302;
+ noise_rng >>= 1;
+
+ i--;
+ }
+ }
+
+ /* calculate output */
+ void CALC_CH(OPL_CH &CH)
+ {
+ OPL_SLOT *SLOT;
+ unsigned int env;
+ signed int out;
+
+ phase_modulation = 0;
+
+ /* SLOT 1 */
+ SLOT = &CH.SLOT[SLOT1];
+ env = volume_calc(*SLOT);
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+ *SLOT->connect1 += SLOT->op1_out[0];
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(*SLOT);
+ if( env < ENV_QUIET )
+ output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable);
+ }
+
+ /*
+ operators used in the rhythm sounds generation process:
+
+ Envelope Generator:
+
+ channel operator register number Bass High Snare Tom Top
+ / slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal
+ 6 / 0 12 50 70 90 f0 +
+ 6 / 1 15 53 73 93 f3 +
+ 7 / 0 13 51 71 91 f1 +
+ 7 / 1 16 54 74 94 f4 +
+ 8 / 0 14 52 72 92 f2 +
+ 8 / 1 17 55 75 95 f5 +
+
+ Phase Generator:
+
+ channel operator register number Bass High Snare Tom Top
+ / slot number MULTIPLE Drum Hat Drum Tom Cymbal
+ 6 / 0 12 30 +
+ 6 / 1 15 33 +
+ 7 / 0 13 31 + + +
+ 7 / 1 16 34 ----- n o t u s e d -----
+ 8 / 0 14 32 +
+ 8 / 1 17 35 + +
+
+ channel operator register number Bass High Snare Tom Top
+ number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
+ 6 12,15 B6 A6 +
+
+ 7 13,16 B7 A7 + + +
+
+ 8 14,17 B8 A8 + + +
+
+ */
+
+ /* calculate rhythm */
+
+ void CALC_RH()
+ {
+ unsigned int const noise = BIT(noise_rng, 0);
+
+ OPL_SLOT *SLOT;
+ signed int out;
+ unsigned int env;
+
+
+ /* Bass Drum (verified on real YM3812):
+ - depends on the channel 6 'connect' register:
+ when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)
+ when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored
+ - output sample always is multiplied by 2
+ */
+
+ phase_modulation = 0;
+ /* SLOT 1 */
+ SLOT = &P_CH[6].SLOT[SLOT1];
+ env = volume_calc(*SLOT);
+
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+
+ if (!SLOT->CON)
+ phase_modulation = SLOT->op1_out[0];
+ /* else ignore output of operator 1 */
+
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(*SLOT);
+ if( env < ENV_QUIET )
+ output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable) * 2;
+
+
+ /* Phase generation is based on: */
+ /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */
+ /* SD (16) channel 7->slot 1 */
+ /* TOM (14) channel 8->slot 1 */
+ /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */
+
+ /* Envelope generation based on: */
+ /* HH channel 7->slot1 */
+ /* SD channel 7->slot2 */
+ /* TOM channel 8->slot1 */
+ /* TOP channel 8->slot2 */
+
+
+ /* The following formulas can be well optimized.
+ I leave them in direct form for now (in case I've missed something).
+ */
+
+ /* High Hat (verified on real YM3812) */
+ OPL_SLOT const &SLOT7_1 = P_CH[7].SLOT[SLOT1];
+ OPL_SLOT const &SLOT8_2 = P_CH[8].SLOT[SLOT2];
+ env = volume_calc(SLOT7_1);
+ if( env < ENV_QUIET )
+ {
+ /* high hat phase generation:
+ phase = d0 or 234 (based on frequency only)
+ phase = 34 or 2d0 (based on noise)
+ */
+
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char const bit7 = BIT(SLOT7_1.Cnt >> FREQ_SH, 7);
+ unsigned char const bit3 = BIT(SLOT7_1.Cnt >> FREQ_SH, 3);
+ unsigned char const bit2 = BIT(SLOT7_1.Cnt >> FREQ_SH, 2);
+
+ unsigned char const res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0xd0; */
+ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */
+ uint32_t phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char const bit5e= BIT(SLOT8_2.Cnt >> FREQ_SH, 5);
+ unsigned char const bit3e= BIT(SLOT8_2.Cnt >> FREQ_SH, 3);
+
+ unsigned char const res2 = bit3e ^ bit5e;
+
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */
+ if (res2)
+ phase = (0x200|(0xd0>>2));
+
+
+ /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */
+ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */
+ if (phase&0x200)
+ {
+ if (noise)
+ phase = 0x200|0xd0;
+ }
+ else
+ /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */
+ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */
+ {
+ if (noise)
+ phase = 0xd0>>2;
+ }
+
+ output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1.wavetable) * 2;
+ }
+
+ /* Snare Drum (verified on real YM3812) */
+ OPL_SLOT const &SLOT7_2 = P_CH[7].SLOT[SLOT2];
+ env = volume_calc(SLOT7_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char const bit8 = BIT(SLOT7_1.Cnt >> FREQ_SH, 8);
+
+ /* when bit8 = 0 phase = 0x100; */
+ /* when bit8 = 1 phase = 0x200; */
+ uint32_t phase = bit8 ? 0x200 : 0x100;
+
+ /* Noise bit XOR'es phase by 0x100 */
+ /* when noisebit = 0 pass the phase from calculation above */
+ /* when noisebit = 1 phase ^= 0x100; */
+ /* in other words: phase ^= (noisebit<<8); */
+ if (noise)
+ phase ^= 0x100;
+
+ output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2.wavetable) * 2;
+ }
+
+ /* Tom Tom (verified on real YM3812) */
+ OPL_SLOT const &SLOT8_1 = P_CH[8].SLOT[SLOT1];
+ env = volume_calc(SLOT8_1);
+ if( env < ENV_QUIET )
+ output[0] += op_calc(SLOT8_1.Cnt, env, 0, SLOT8_1.wavetable) * 2;
+
+ /* Top Cymbal (verified on real YM3812) */
+ env = volume_calc(SLOT8_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char const bit7 = BIT(SLOT7_1.Cnt >> FREQ_SH, 7);
+ unsigned char const bit3 = BIT(SLOT7_1.Cnt >> FREQ_SH, 3);
+ unsigned char const bit2 = BIT(SLOT7_1.Cnt >> FREQ_SH, 2);
+
+ unsigned char const res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0x100; */
+ /* when res1 = 1 phase = 0x200 | 0x100; */
+ uint32_t phase = res1 ? 0x300 : 0x100;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char const bit5e= BIT(SLOT8_2.Cnt >> FREQ_SH, 5);
+ unsigned char const bit3e= BIT(SLOT8_2.Cnt >> FREQ_SH, 3);
+
+ unsigned char const res2 = bit3e ^ bit5e;
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | 0x100; */
+ if (res2)
+ phase = 0x300;
+
+ output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2.wavetable) * 2;
+ }
+ }
+
+
+ void initialize();
+
+
+ /* set multi,am,vib,EG-TYP,KSR,mul */
+ void set_mul(int slot, int v)
+ {
+ OPL_CH &CH = P_CH[slot/2];
+ OPL_SLOT &SLOT = CH.SLOT[slot&1];
+
+ SLOT.mul = mul_tab[v&0x0f];
+ SLOT.KSR = (v & 0x10) ? 0 : 2;
+ SLOT.eg_type = (v & 0x20);
+ SLOT.vib = (v & 0x40);
+ SLOT.AMmask = (v & 0x80) ? ~0 : 0;
+ CH.CALC_FCSLOT(SLOT);
+ }
+
+ /* set ksl & tl */
+ void set_ksl_tl(int slot, int v)
+ {
+ OPL_CH &CH = P_CH[slot/2];
+ OPL_SLOT &SLOT = CH.SLOT[slot&1];
+
+ SLOT.ksl = ksl_shift[v >> 6];
+ SLOT.TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */
+
+ SLOT.TLL = SLOT.TL + (CH.ksl_base >> SLOT.ksl);
+ }
+
+ /* set attack rate & decay rate */
+ void set_ar_dr(int slot, int v)
+ {
+ OPL_CH &CH = P_CH[slot/2];
+ OPL_SLOT &SLOT = CH.SLOT[slot&1];
+
+ SLOT.ar = (v>>4) ? 16 + ((v>>4) <<2) : 0;
+
+ if ((SLOT.ar + SLOT.ksr) < 16+62)
+ {
+ SLOT.eg_sh_ar = eg_rate_shift [SLOT.ar + SLOT.ksr ];
+ SLOT.eg_sel_ar = eg_rate_select[SLOT.ar + SLOT.ksr ];
+ }
+ else
+ {
+ SLOT.eg_sh_ar = 0;
+ SLOT.eg_sel_ar = 13*RATE_STEPS;
+ }
+
+ SLOT.dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT.eg_sh_dr = eg_rate_shift [SLOT.dr + SLOT.ksr ];
+ SLOT.eg_sel_dr = eg_rate_select[SLOT.dr + SLOT.ksr ];
+ }
+
+ /* set sustain level & release rate */
+ void set_sl_rr(int slot, int v)
+ {
+ OPL_CH &CH = P_CH[slot/2];
+ OPL_SLOT &SLOT = CH.SLOT[slot&1];
+
+ SLOT.sl = sl_tab[ v>>4 ];
+
+ SLOT.rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT.eg_sh_rr = eg_rate_shift [SLOT.rr + SLOT.ksr ];
+ SLOT.eg_sel_rr = eg_rate_select[SLOT.rr + SLOT.ksr ];
+ }
+
+
+ void WriteReg(int r, int v);
+ void ResetChip();
+ void postload();
+
+
+ /* lock/unlock for common table */
+ static int LockTable(device_t *device)
+ {
+ num_lock++;
+ if(num_lock>1) return 0;
+
+ /* first time */
+
+ /* allocate total level table (128kb space) */
+ if( !init_tables() )
+ {
+ num_lock--;
+ return -1;
+ }
+
+ return 0;
+ }
+
+ static void UnLockTable()
+ {
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+
+ /* last time */
+ CloseTable();
+ }
+
+private:
+ uint32_t volume_calc(OPL_SLOT const &OP) const
+ {
+ return OP.TLL + uint32_t(OP.volume) + (LFO_AM & OP.AMmask);
+ }
+
+ static inline signed int op_calc(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab)
+ {
+ uint32_t const p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ];
+
+ return (p >= TL_TAB_LEN) ? 0 : tl_tab[p];
+ }
+
+ static inline signed int op_calc1(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab)
+ {
+ uint32_t const p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ];
+
+ return (p >= TL_TAB_LEN) ? 0 : tl_tab[p];
+ }
+
+
+ static int init_tables();
+
+ static void CloseTable()
+ {
+#ifdef SAVE_SAMPLE
+ fclose(sample[0]);
+#endif
+ }
+
+ static const double ksl_tab[8*16];
+ static const uint32_t ksl_shift[4];
+ static const int32_t sl_tab[16];
+ static const unsigned char eg_inc[15 * RATE_STEPS];
+
+ static const uint8_t mul_tab[16];
+ static signed int tl_tab[TL_TAB_LEN];
+ static unsigned int sin_tab[SIN_LEN * 4];
+
+ static const uint8_t lfo_am_table[LFO_AM_TAB_ELEMENTS];
+ static const int8_t lfo_pm_table[8 * 8 * 2];
+
+ static int num_lock;
+};
+
+
+
+/* mapping of register number (offset) to slot number used by the emulator */
+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/octave , DV converts this into 6dB/octave */
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */
+const double FM_OPL::ksl_tab[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
+};
+
+/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */
+const uint32_t FM_OPL::ksl_shift[4] = { 31, 1, 2, 0 };
+
+
+/* sustain level 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) int32_t(db * (2.0 / ENV_STEP))
+const int32_t FM_OPL::sl_tab[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)
+};
+
+
+const unsigned char FM_OPL::eg_inc[15*RATE_STEPS]={
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(13) in this table - it's directly in the code */
+const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+
+/* rates 00-12 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 13 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 14 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 15 */
+O(12),O(12),O(12),O(12),
+
+/* 16 dummy rates (same as 15 3) */
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */
+/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */
+/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-12 */
+O(12),O(12),O(12),O(12),
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 16 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+
+};
+#undef O
+
+
+/* multiple table */
+#define ML 2
+const uint8_t FM_OPL::mul_tab[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */
+ ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML,
+ 8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML
+};
+#undef ML
+
+signed int FM_OPL::tl_tab[TL_TAB_LEN];
+
+/* sin waveform table in 'decibel' scale */
+/* four waveforms on OPL2 type chips */
+unsigned int FM_OPL::sin_tab[SIN_LEN * 4];
+
+
+/* LFO Amplitude Modulation table (verified on real YM3812)
+ 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples
+
+ Length: 210 elements.
+
+ Each of the elements has to be repeated
+ exactly 64 times (on 64 consecutive samples).
+ The whole table takes: 64 * 210 = 13440 samples.
+
+ When AM = 1 data is used directly
+ When AM = 0 data is divided by 4 before being used (losing precision is important)
+*/
+
+const uint8_t FM_OPL::lfo_am_table[LFO_AM_TAB_ELEMENTS] = {
+0,0,0,0,0,0,0,
+1,1,1,1,
+2,2,2,2,
+3,3,3,3,
+4,4,4,4,
+5,5,5,5,
+6,6,6,6,
+7,7,7,7,
+8,8,8,8,
+9,9,9,9,
+10,10,10,10,
+11,11,11,11,
+12,12,12,12,
+13,13,13,13,
+14,14,14,14,
+15,15,15,15,
+16,16,16,16,
+17,17,17,17,
+18,18,18,18,
+19,19,19,19,
+20,20,20,20,
+21,21,21,21,
+22,22,22,22,
+23,23,23,23,
+24,24,24,24,
+25,25,25,25,
+26,26,26,
+25,25,25,25,
+24,24,24,24,
+23,23,23,23,
+22,22,22,22,
+21,21,21,21,
+20,20,20,20,
+19,19,19,19,
+18,18,18,18,
+17,17,17,17,
+16,16,16,16,
+15,15,15,15,
+14,14,14,14,
+13,13,13,13,
+12,12,12,12,
+11,11,11,11,
+10,10,10,10,
+9,9,9,9,
+8,8,8,8,
+7,7,7,7,
+6,6,6,6,
+5,5,5,5,
+4,4,4,4,
+3,3,3,3,
+2,2,2,2,
+1,1,1,1
+};
+
+/* LFO Phase Modulation table (verified on real YM3812) */
+const int8_t FM_OPL::lfo_pm_table[8*8*2] = {
+/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/
+};
+
+
+/* lock level of common table */
+int FM_OPL::num_lock = 0;
+
+
+
+static inline int limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+
+/* generic table initialize */
+int FM_OPL::init_tables()
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 1; /* 12 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<12; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x*2);
+ for (i=0; i<12; i++)
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] );
+ logerror("\n");
+ #endif
+ }
+ /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+
+ /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/
+ }
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* waveform 1: __ __ */
+ /* / \____/ \____*/
+ /* output only first half of the sinus waveform (positive one) */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[1*SIN_LEN+i] = sin_tab[i];
+
+ /* waveform 2: __ __ __ __ */
+ /* / \/ \/ \/ \*/
+ /* abs(sin) */
+
+ sin_tab[2*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>1) ];
+
+ /* waveform 3: _ _ _ _ */
+ /* / |_/ |_/ |_/ |_*/
+ /* abs(output only first quarter of the sinus waveform) */
+
+ if (i & (1<<(SIN_BITS-2)) )
+ sin_tab[3*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)];
+
+ /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );
+ logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] );
+ logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/
+ }
+ /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/
+
+
+#ifdef SAVE_SAMPLE
+ sample[0]=fopen("sampsum.pcm","wb");
+#endif
+
+ return 1;
+}
+
+
+void FM_OPL::initialize()
+{
+ int i;
+
+ /* frequency base */
+ freqbase = (rate) ? ((double)clock / 72.0) / rate : 0;
+#if 0
+ rate = (double)clock / 72.0;
+ freqbase = 1.0;
+#endif
+
+ /*logerror("freqbase=%f\n", freqbase);*/
+
+ /* Timer base time */
+ //TimerBase = attotime::from_hz(clock) * 72;
+
+ /* make fnumber -> increment counter table */
+ for( i=0 ; i < 1024 ; i++ )
+ {
+ /* opn phase increment counter = 20bit */
+ fn_tab[i] = (uint32_t)( (double)i * 64 * freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+#if 0
+ logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n",
+ i, fn_tab[i]>>6, fn_tab[i]>>6 );
+#endif
+ }
+
+#if 0
+ for( i=0 ; i < 16 ; i++ )
+ {
+ logerror("FMOPL.C: sl_tab[%i] = %08x\n",
+ i, sl_tab[i] );
+ }
+ for( i=0 ; i < 8 ; i++ )
+ {
+ int j;
+ logerror("FMOPL.C: ksl_tab[oct=%2i] =",i);
+ for (j=0; j<16; j++)
+ {
+ logerror("%08x ", static_cast<uint32_t>(ksl_tab[i*16+j]) );
+ }
+ logerror("\n");
+ }
+#endif
+
+
+ /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */
+ /* One entry from LFO_AM_TABLE lasts for 64 samples */
+ lfo_am_inc = (1.0 / 64.0 ) * (1<<LFO_SH) * freqbase;
+
+ /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */
+ lfo_pm_inc = (1.0 / 1024.0) * (1<<LFO_SH) * freqbase;
+
+ /*logerror ("lfo_am_inc = %8x ; lfo_pm_inc = %8x\n", lfo_am_inc, lfo_pm_inc);*/
+
+ /* Noise generator: a step takes 1 sample */
+ noise_f = (1.0 / 1.0) * (1<<FREQ_SH) * freqbase;
+
+ eg_timer_add = (1<<EG_SH) * freqbase;
+ eg_timer_overflow = ( 1 ) * (1<<EG_SH);
+ /*logerror("OPLinit eg_timer_add=%8x eg_timer_overflow=%8x\n", eg_timer_add, eg_timer_overflow);*/
+}
+
+
+/* write a value v to register r on OPL chip */
+void FM_OPL::WriteReg(int r, int v)
+{
+ OPL_CH *CH;
+ int slot;
+ int block_fnum;
+
+
+ /* adjust bus to 8 bits */
+ r &= 0xff;
+ v &= 0xff;
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:control */
+ switch(r&0x1f)
+ {
+ case 0x01: /* waveform select enable */
+ if(type&OPL_TYPE_WAVESEL)
+ {
+ wavesel = v&0x20;
+ /* do not change the waveform previously selected */
+ }
+ break;
+ case 0x02: /* Timer 1 */
+ T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ T[1] = (256-v)*16;
+ break;
+#if 0
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flag clear */
+ STATUS_RESET(0x7f-0x08); /* don't reset BFRDY flag or we will have to call deltat module to set the flag */
+ }
+ else
+ { /* set IRQ mask ,timer enable*/
+ uint8_t st1 = v&1;
+ uint8_t st2 = (v>>1)&1;
+
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+ STATUS_RESET(v & (0x78-0x08));
+ STATUSMASK_SET((~v) & 0x78);
+
+ /* timer 2 */
+ if(st[1] != st2)
+ {
+ attotime period = st2 ? (TimerBase * T[1]) : attotime::zero;
+ st[1] = st2;
+ if (timer_handler) (timer_handler)(TimerParam,1,period);
+ }
+ /* timer 1 */
+ if(st[0] != st1)
+ {
+ attotime period = st1 ? (TimerBase * T[0]) : attotime::zero;
+ st[0] = st1;
+ if (timer_handler) (timer_handler)(TimerParam,0,period);
+ }
+ }
+ break;
+#endif
+#if BUILD_Y8950
+ case 0x06: /* Key Board OUT */
+ if(type&OPL_TYPE_KEYBOARD)
+ {
+ if(keyboardhandler_w)
+ keyboardhandler_w(keyboard_param,v);
+ else
+ device->logerror("Y8950: write unmapped KEYBOARD port\n");
+ }
+ break;
+ case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
+ if(type&OPL_TYPE_ADPCM)
+ deltat->ADPCM_Write(r-0x07,v);
+ break;
+#endif
+ case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
+ mode = v;
+#if BUILD_Y8950
+ if(type&OPL_TYPE_ADPCM)
+ deltat->ADPCM_Write(r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */
+#endif
+ break;
+
+#if BUILD_Y8950
+ case 0x09: /* START ADD */
+ case 0x0a:
+ case 0x0b: /* STOP ADD */
+ case 0x0c:
+ case 0x0d: /* PRESCALE */
+ case 0x0e:
+ case 0x0f: /* ADPCM data write */
+ case 0x10: /* DELTA-N */
+ case 0x11: /* DELTA-N */
+ case 0x12: /* ADPCM volume */
+ if(type&OPL_TYPE_ADPCM)
+ deltat->ADPCM_Write(r-0x07,v);
+ break;
+
+ case 0x15: /* DAC data high 8 bits (F7,F6...F2) */
+ case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */
+ case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */
+ device->logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v);
+ break;
+
+ case 0x18: /* I/O CTRL (Direction) */
+ if(type&OPL_TYPE_IO)
+ portDirection = v&0x0f;
+ break;
+ case 0x19: /* I/O DATA */
+ if(type&OPL_TYPE_IO)
+ {
+ portLatch = v;
+ if(porthandler_w)
+ porthandler_w(port_param,v&portDirection);
+ }
+ break;
+#endif
+ default:
+ device->logerror("FMOPL.C: write to unknown register: %02x\n",r);
+ break;
+ }
+ break;
+ case 0x20: /* am ON, vib ON, ksr, eg_type, mul */
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_mul(slot,v);
+ break;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ksl_tl(slot,v);
+ break;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ar_dr(slot,v);
+ break;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_sl_rr(slot,v);
+ break;
+ case 0xa0:
+ if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */
+ {
+ lfo_am_depth = v & 0x80;
+ lfo_pm_depth_range = (v&0x40) ? 8 : 0;
+
+ rhythm = v&0x3f;
+
+ if(rhythm&0x20)
+ {
+ /* BD key on/off */
+ if(v&0x10)
+ {
+ P_CH[6].SLOT[SLOT1].KEYON(2);
+ P_CH[6].SLOT[SLOT2].KEYON(2);
+ }
+ else
+ {
+ P_CH[6].SLOT[SLOT1].KEYOFF(~2);
+ P_CH[6].SLOT[SLOT2].KEYOFF(~2);
+ }
+ /* HH key on/off */
+ if(v&0x01) P_CH[7].SLOT[SLOT1].KEYON ( 2);
+ else P_CH[7].SLOT[SLOT1].KEYOFF(~2);
+ /* SD key on/off */
+ if(v&0x08) P_CH[7].SLOT[SLOT2].KEYON ( 2);
+ else P_CH[7].SLOT[SLOT2].KEYOFF(~2);
+ /* TOM key on/off */
+ if(v&0x04) P_CH[8].SLOT[SLOT1].KEYON ( 2);
+ else P_CH[8].SLOT[SLOT1].KEYOFF(~2);
+ /* TOP-CY key on/off */
+ if(v&0x02) P_CH[8].SLOT[SLOT2].KEYON ( 2);
+ else P_CH[8].SLOT[SLOT2].KEYOFF(~2);
+ }
+ else
+ {
+ /* BD key off */
+ P_CH[6].SLOT[SLOT1].KEYOFF(~2);
+ P_CH[6].SLOT[SLOT2].KEYOFF(~2);
+ /* HH key off */
+ P_CH[7].SLOT[SLOT1].KEYOFF(~2);
+ /* SD key off */
+ P_CH[7].SLOT[SLOT2].KEYOFF(~2);
+ /* TOM key off */
+ P_CH[8].SLOT[SLOT1].KEYOFF(~2);
+ /* TOP-CY off */
+ P_CH[8].SLOT[SLOT2].KEYOFF(~2);
+ }
+ return;
+ }
+ /* keyon,block,fnum */
+ if( (r&0x0f) > 8) return;
+ CH = &P_CH[r&0x0f];
+ if(!(r&0x10))
+ { /* a0-a8 */
+ block_fnum = (CH->block_fnum&0x1f00) | v;
+ }
+ else
+ { /* b0-b8 */
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+
+ if(v&0x20)
+ {
+ CH->SLOT[SLOT1].KEYON ( 1);
+ CH->SLOT[SLOT2].KEYON ( 1);
+ }
+ else
+ {
+ CH->SLOT[SLOT1].KEYOFF(~1);
+ CH->SLOT[SLOT2].KEYOFF(~1);
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ uint8_t block = block_fnum >> 10;
+
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = static_cast<uint32_t>(ksl_tab[block_fnum>>6]);
+ CH->fc = fn_tab[block_fnum&0x03ff] >> (7-block);
+
+ /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */
+ CH->kcode = (CH->block_fnum&0x1c00)>>9;
+
+ /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */
+ /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */
+ /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */
+ if (mode&0x40)
+ CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */
+ else
+ CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */
+
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CH->CALC_FCSLOT(CH->SLOT[SLOT1]);
+ CH->CALC_FCSLOT(CH->SLOT[SLOT2]);
+ }
+ break;
+ case 0xc0:
+ /* FB,C */
+ if( (r&0x0f) > 8) return;
+ CH = &P_CH[r&0x0f];
+ CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0;
+ CH->SLOT[SLOT1].CON = v&1;
+ CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &output[0] : &phase_modulation;
+ break;
+ case 0xe0: /* waveform select */
+ /* simply ignore write to the waveform select register if selecting not enabled in test register */
+ if(wavesel)
+ {
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ CH = &P_CH[slot/2];
+
+ CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN;
+ }
+ break;
+ }
+}
+
+
+void FM_OPL::ResetChip()
+{
+ eg_timer = 0;
+ eg_cnt = 0;
+
+ noise_rng = 1; /* noise shift register */
+ mode = 0; /* normal mode */
+ STATUS_RESET(0x7f);
+
+ /* reset with register write */
+ WriteReg(0x01,0); /* wavesel disable */
+ WriteReg(0x02,0); /* Timer1 */
+ WriteReg(0x03,0); /* Timer2 */
+ WriteReg(0x04,0); /* IRQ mask clear */
+ for(int i = 0xff ; i >= 0x20 ; i-- ) WriteReg(i,0);
+
+ /* reset operator parameters */
+// for(OPL_CH &CH : P_CH)
+ for(int ch = 0; ch < sizeof( P_CH )/ sizeof(P_CH[0]); ch++)
+ {
+ OPL_CH &CH = P_CH[ch];
+// for(OPL_SLOT &SLOT : CH.SLOT)
+ for(int slot = 0; slot < sizeof( CH.SLOT ) / sizeof( CH.SLOT[0]); slot++)
+ {
+
+ OPL_SLOT &SLOT = CH.SLOT[slot];
+ /* wave table */
+ SLOT.wavetable = 0;
+ SLOT.state = EG_OFF;
+ SLOT.volume = MAX_ATT_INDEX;
+ }
+ }
+#if BUILD_Y8950
+ if(type&OPL_TYPE_ADPCM)
+ {
+ YM_DELTAT *DELTAT = deltat;
+
+ DELTAT->freqbase = freqbase;
+ DELTAT->output_pointer = &output_deltat[0];
+ DELTAT->portshift = 5;
+ DELTAT->output_range = 1<<23;
+ DELTAT->ADPCM_Reset(0,YM_DELTAT::EMULATION_MODE_NORMAL,device);
+ }
+#endif
+}
+
+
+void FM_OPL::postload()
+{
+ for(int ch = 0; ch < sizeof( P_CH )/ sizeof(P_CH[0]); ch++)
+ {
+ OPL_CH &CH = P_CH[ch];
+ /* Look up key scale level */
+ uint32_t const block_fnum = CH.block_fnum;
+ CH.ksl_base = static_cast<uint32_t>(ksl_tab[block_fnum >> 6]);
+ CH.fc = fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10));
+
+ for(int slot = 0; slot < sizeof( CH.SLOT ) / sizeof( CH.SLOT[0]); slot++)
+ {
+ OPL_SLOT &SLOT = CH.SLOT[slot];
+ /* Calculate key scale rate */
+ SLOT.ksr = CH.kcode >> SLOT.KSR;
+
+ /* Calculate attack, decay and release rates */
+ if ((SLOT.ar + SLOT.ksr) < 16+62)
+ {
+ SLOT.eg_sh_ar = eg_rate_shift [SLOT.ar + SLOT.ksr ];
+ SLOT.eg_sel_ar = eg_rate_select[SLOT.ar + SLOT.ksr ];
+ }
+ else
+ {
+ SLOT.eg_sh_ar = 0;
+ SLOT.eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT.eg_sh_dr = eg_rate_shift [SLOT.dr + SLOT.ksr ];
+ SLOT.eg_sel_dr = eg_rate_select[SLOT.dr + SLOT.ksr ];
+ SLOT.eg_sh_rr = eg_rate_shift [SLOT.rr + SLOT.ksr ];
+ SLOT.eg_sel_rr = eg_rate_select[SLOT.rr + SLOT.ksr ];
+
+ /* Calculate phase increment */
+ SLOT.Incr = CH.fc * SLOT.mul;
+
+ /* Total level */
+ SLOT.TLL = SLOT.TL + (CH.ksl_base >> SLOT.ksl);
+
+ /* Connect output */
+ SLOT.connect1 = SLOT.CON ? &output[0] : &phase_modulation;
+ }
+ }
+#if BUILD_Y8950
+ if ( (type & OPL_TYPE_ADPCM) && (deltat) )
+ {
+ // We really should call the postlod function for the YM_DELTAT, but it's hard without registers
+ // (see the way the YM2610 does it)
+ //deltat->postload(REGS);
+ }
+#endif
+}
+
+} // anonymous namespace
+
+
+#if 0
+static void OPLsave_state_channel(device_t *device, OPL_CH *CH)
+{
+ int slot, ch;
+
+ for( ch=0 ; ch < 9 ; ch++, CH++ )
+ {
+ /* channel */
+ device->save_item(NAME(CH->block_fnum), ch);
+ device->save_item(NAME(CH->kcode), ch);
+ /* slots */
+ for( slot=0 ; slot < 2 ; slot++ )
+ {
+ OPL_SLOT *SLOT = &CH->SLOT[slot];
+
+ device->save_item(NAME(SLOT->ar), ch * 2 + slot);
+ device->save_item(NAME(SLOT->dr), ch * 2 + slot);
+ device->save_item(NAME(SLOT->rr), ch * 2 + slot);
+ device->save_item(NAME(SLOT->KSR), ch * 2 + slot);
+ device->save_item(NAME(SLOT->ksl), ch * 2 + slot);
+ device->save_item(NAME(SLOT->mul), ch * 2 + slot);
+
+ device->save_item(NAME(SLOT->Cnt), ch * 2 + slot);
+ device->save_item(NAME(SLOT->FB), ch * 2 + slot);
+ device->save_item(NAME(SLOT->op1_out), ch * 2 + slot);
+ device->save_item(NAME(SLOT->CON), ch * 2 + slot);
+
+ device->save_item(NAME(SLOT->eg_type), ch * 2 + slot);
+ device->save_item(NAME(SLOT->state), ch * 2 + slot);
+ device->save_item(NAME(SLOT->TL), ch * 2 + slot);
+ device->save_item(NAME(SLOT->volume), ch * 2 + slot);
+ device->save_item(NAME(SLOT->sl), ch * 2 + slot);
+ device->save_item(NAME(SLOT->key), ch * 2 + slot);
+
+ device->save_item(NAME(SLOT->AMmask), ch * 2 + slot);
+ device->save_item(NAME(SLOT->vib), ch * 2 + slot);
+
+ device->save_item(NAME(SLOT->wavetable), ch * 2 + slot);
+ }
+ }
+}
+
+/* Register savestate for a virtual YM3812/YM3526Y8950 */
+
+static void OPL_save_state(FM_OPL *OPL, device_t *device)
+{
+ OPLsave_state_channel(device, OPL->P_CH);
+
+ device->save_item(NAME(OPL->eg_cnt));
+ device->save_item(NAME(OPL->eg_timer));
+
+ device->save_item(NAME(OPL->rhythm));
+
+ device->save_item(NAME(OPL->lfo_am_depth));
+ device->save_item(NAME(OPL->lfo_pm_depth_range));
+ device->save_item(NAME(OPL->lfo_am_cnt));
+ device->save_item(NAME(OPL->lfo_pm_cnt));
+
+ device->save_item(NAME(OPL->noise_rng));
+ device->save_item(NAME(OPL->noise_p));
+
+ if( OPL->type & OPL_TYPE_WAVESEL )
+ {
+ device->save_item(NAME(OPL->wavesel));
+ }
+
+ device->save_item(NAME(OPL->T));
+ device->save_item(NAME(OPL->st));
+
+#if BUILD_Y8950
+ if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) )
+ {
+ OPL->deltat->savestate(device);
+ }
+
+ if ( OPL->type & OPL_TYPE_IO )
+ {
+ device->save_item(NAME(OPL->portDirection));
+ device->save_item(NAME(OPL->portLatch));
+ }
+#endif
+
+ device->save_item(NAME(OPL->address));
+ device->save_item(NAME(OPL->status));
+ device->save_item(NAME(OPL->statusmask));
+ device->save_item(NAME(OPL->mode));
+
+ device->machine().save().register_postload(save_prepost_delegate(FUNC(FM_OPL::postload), OPL));
+}
+
+#endif
+
+static void OPL_clock_changed(FM_OPL *OPL, uint32_t clock, uint32_t rate)
+{
+ OPL->clock = clock;
+ OPL->rate = rate;
+
+ /* init global tables */
+ OPL->initialize();
+}
+
+
+/* Create one of virtual YM3812/YM3526/Y8950 */
+/* 'clock' is chip clock in Hz */
+/* 'rate' is sampling rate */
+static FM_OPL *OPLCreate(device_t *device, uint32_t clock, uint32_t rate, int type)
+{
+ char *ptr;
+ FM_OPL *OPL;
+ int state_size;
+
+ if (FM_OPL::LockTable(device) == -1) return 0;
+
+ /* calculate OPL state size */
+ state_size = sizeof(FM_OPL);
+
+#if BUILD_Y8950
+ if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
+#endif
+
+ /* allocate memory block */
+ ptr = (char *)auto_alloc_array_clear(device->machine(), uint8_t, state_size);
+
+ OPL = (FM_OPL *)ptr;
+
+ ptr += sizeof(FM_OPL);
+
+#if BUILD_Y8950
+ if (type&OPL_TYPE_ADPCM)
+ {
+ OPL->deltat = (YM_DELTAT *)ptr;
+ }
+ ptr += sizeof(YM_DELTAT);
+#endif
+
+ OPL->device = device;
+ OPL->type = type;
+ OPL_clock_changed(OPL, clock, rate);
+
+ return OPL;
+}
+
+/* Destroy one of virtual YM3812 */
+static void OPLDestroy(FM_OPL *OPL)
+{
+ FM_OPL::UnLockTable();
+ auto_free(OPL->device->machine(), OPL);
+}
+
+/* Optional handlers */
+
+static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,device_t *device)
+{
+ OPL->timer_handler = timer_handler;
+ OPL->TimerParam = device;
+}
+static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,device_t *device)
+{
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = device;
+}
+static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,device_t *device)
+{
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = device;
+}
+
+static 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);
+ OPL->WriteReg(OPL->address,v);
+ }
+ return OPL->status>>7;
+}
+
+static unsigned char OPLRead(FM_OPL *OPL,int a)
+{
+ if( !(a&1) )
+ {
+ /* status port */
+
+ #if BUILD_Y8950
+
+ if(OPL->type&OPL_TYPE_ADPCM) /* Y8950 */
+ {
+ return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1);
+ }
+
+ #endif
+
+ /* OPL and OPL2 */
+ return OPL->status & (OPL->statusmask|0x80);
+ }
+
+#if BUILD_Y8950
+ /* 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
+ OPL->device->logerror("Y8950: read unmapped KEYBOARD port\n");
+ }
+ return 0;
+
+ case 0x0f: /* ADPCM-DATA */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ uint8_t val;
+
+ val = OPL->deltat->ADPCM_Read();
+ /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/
+ return val;
+ }
+ return 0;
+
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ if(OPL->porthandler_r)
+ return OPL->porthandler_r(OPL->port_param);
+ else
+ OPL->device->logerror("Y8950:read unmapped I/O port\n");
+ }
+ return 0;
+ case 0x1a: /* PCM-DATA */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ OPL->device->logerror("Y8950 A/D conversion is accessed but not implemented !\n");
+ return 0x80; /* 2's complement PCM data - result from A/D conversion */
+ }
+ return 0;
+ }
+#endif
+
+ return 0xff;
+}
+
+/* CSM Key Controll */
+static inline void CSMKeyControll(OPL_CH *CH)
+{
+ CH->SLOT[SLOT1].KEYON(4);
+ CH->SLOT[SLOT2].KEYON(4);
+
+ /* The key off should happen exactly one sample later - not implemented correctly yet */
+
+ CH->SLOT[SLOT1].KEYOFF(~4);
+ CH->SLOT[SLOT2].KEYOFF(~4);
+}
+
+static int OPLTimerOver(FM_OPL *OPL,int c)
+{
+ if( c )
+ { /* Timer B */
+ OPL->STATUS_SET(0x20);
+ }
+ else
+ { /* Timer A */
+ OPL->STATUS_SET(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->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,OPL->TimerBase * OPL->T[c]);
+ return OPL->status>>7;
+}
+
+#define MAX_OPL_CHIPS 2
+
+
+#if BUILD_YM3812
+
+void ym3812_clock_changed(void *chip, uint32_t clock, uint32_t rate)
+{
+ OPL_clock_changed((FM_OPL *)chip, clock, rate);
+}
+
+void * ym3812_init(device_t *device, uint32_t clock, uint32_t rate)
+{
+ /* emulator create */
+ FM_OPL *YM3812 = OPLCreate(device,clock,rate,OPL_TYPE_YM3812);
+ if (YM3812)
+ {
+ //OPL_save_state(YM3812, device);
+ ym3812_reset_chip(YM3812);
+ }
+ return YM3812;
+}
+
+void ym3812_shutdown(void *chip)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+
+ /* emulator shutdown */
+ OPLDestroy(YM3812);
+}
+void ym3812_reset_chip(void *chip)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ YM3812->ResetChip();
+}
+
+int ym3812_write(void *chip, int a, int v)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ return OPLWrite(YM3812, a, v);
+}
+
+unsigned char ym3812_read(void *chip, int a)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ /* YM3812 always returns bit2 and bit1 in HIGH state */
+ return OPLRead(YM3812, a) | 0x06 ;
+}
+int ym3812_timer_over(void *chip, int c)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ return OPLTimerOver(YM3812, c);
+}
+
+void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, device_t *device)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetTimerHandler(YM3812, timer_handler, device);
+}
+void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,device_t *device)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetIRQHandler(YM3812, IRQHandler, device);
+}
+void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device_t *device)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(YM3812, UpdateHandler, device);
+}
+
+/*
+** Generate samples for one of the YM3812's
+**
+** 'which' is the virtual YM3812 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ uint8_t rhythm = OPL->rhythm&0x20;
+ OPLSAMPLE *buf = buffer;
+ int i;
+
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+
+ OPL->advance_lfo();
+
+ /* FM part */
+ OPL->CALC_CH(OPL->P_CH[0]);
+ OPL->CALC_CH(OPL->P_CH[1]);
+ OPL->CALC_CH(OPL->P_CH[2]);
+ OPL->CALC_CH(OPL->P_CH[3]);
+ OPL->CALC_CH(OPL->P_CH[4]);
+ OPL->CALC_CH(OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL->CALC_CH(OPL->P_CH[6]);
+ OPL->CALC_CH(OPL->P_CH[7]);
+ OPL->CALC_CH(OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL->CALC_RH();
+ }
+
+ lt = OPL->output[0];
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ OPL->advance();
+ }
+
+}
+#endif /* BUILD_YM3812 */
+
+
+
+#if (BUILD_YM3526)
+
+void ym3526_clock_changed(void *chip, uint32_t clock, uint32_t rate)
+{
+ OPL_clock_changed((FM_OPL *)chip, clock, rate);
+}
+
+void *ym3526_init(device_t *device, uint32_t clock, uint32_t rate)
+{
+ /* emulator create */
+ FM_OPL *YM3526 = OPLCreate(device,clock,rate,OPL_TYPE_YM3526);
+ if (YM3526)
+ {
+ //OPL_save_state(YM3526, device);
+ ym3526_reset_chip(YM3526);
+ }
+ return YM3526;
+}
+
+void ym3526_shutdown(void *chip)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ /* emulator shutdown */
+ OPLDestroy(YM3526);
+}
+void ym3526_reset_chip(void *chip)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ YM3526->ResetChip();
+}
+
+int ym3526_write(void *chip, int a, int v)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ return OPLWrite(YM3526, a, v);
+}
+
+unsigned char ym3526_read(void *chip, int a)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ /* YM3526 always returns bit2 and bit1 in HIGH state */
+ return OPLRead(YM3526, a) | 0x06 ;
+}
+int ym3526_timer_over(void *chip, int c)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ return OPLTimerOver(YM3526, c);
+}
+
+void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, device_t *device)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetTimerHandler(YM3526, timer_handler, device);
+}
+void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,device_t *device)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetIRQHandler(YM3526, IRQHandler, device);
+}
+void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device_t *device)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(YM3526, UpdateHandler, device);
+}
+
+
+/*
+** Generate samples for one of the YM3526's
+**
+** 'which' is the virtual YM3526 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ uint8_t rhythm = OPL->rhythm&0x20;
+ OPLSAMPLE *buf = buffer;
+ int i;
+
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+
+ OPL->advance_lfo();
+
+ /* FM part */
+ OPL->CALC_CH(OPL->P_CH[0]);
+ OPL->CALC_CH(OPL->P_CH[1]);
+ OPL->CALC_CH(OPL->P_CH[2]);
+ OPL->CALC_CH(OPL->P_CH[3]);
+ OPL->CALC_CH(OPL->P_CH[4]);
+ OPL->CALC_CH(OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL->CALC_CH(OPL->P_CH[6]);
+ OPL->CALC_CH(OPL->P_CH[7]);
+ OPL->CALC_CH(OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL->CALC_RH();
+ }
+
+ lt = OPL->output[0];
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ OPL->advance();
+ }
+
+}
+#endif /* BUILD_YM3526 */
+
+
+
+
+#if BUILD_Y8950
+
+static void Y8950_deltat_status_set(void *chip, uint8_t changebits)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ Y8950->STATUS_SET(changebits);
+}
+static void Y8950_deltat_status_reset(void *chip, uint8_t changebits)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ Y8950->STATUS_RESET(changebits);
+}
+
+void *y8950_init(device_t *device, uint32_t clock, uint32_t rate)
+{
+ /* emulator create */
+ FM_OPL *Y8950 = OPLCreate(device,clock,rate,OPL_TYPE_Y8950);
+ if (Y8950)
+ {
+ Y8950->deltat->status_set_handler = Y8950_deltat_status_set;
+ Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset;
+ Y8950->deltat->status_change_which_chip = Y8950;
+ Y8950->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */
+ Y8950->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */
+
+ /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */
+ /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */
+ /* reset */
+ //OPL_save_state(Y8950, device);
+ y8950_reset_chip(Y8950);
+ }
+
+ return Y8950;
+}
+
+void y8950_shutdown(void *chip)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ /* emulator shutdown */
+ OPLDestroy(Y8950);
+}
+void y8950_reset_chip(void *chip)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ Y8950->ResetChip();
+}
+
+int y8950_write(void *chip, int a, int v)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLWrite(Y8950, a, v);
+}
+
+unsigned char y8950_read(void *chip, int a)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLRead(Y8950, a);
+}
+int y8950_timer_over(void *chip, int c)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLTimerOver(Y8950, c);
+}
+
+void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, device_t *device)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetTimerHandler(Y8950, timer_handler, device);
+}
+void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,device_t *device)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetIRQHandler(Y8950, IRQHandler, device);
+}
+void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device_t *device)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(Y8950, UpdateHandler, device);
+}
+
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size )
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->deltat->memory = (uint8_t *)(deltat_mem_ptr);
+ OPL->deltat->memory_size = deltat_mem_size;
+}
+
+/*
+** Generate samples for one of the Y8950's
+**
+** 'which' is the virtual Y8950 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ int i;
+ FM_OPL *OPL = (FM_OPL *)chip;
+ uint8_t rhythm = OPL->rhythm&0x20;
+ YM_DELTAT *DELTAT = OPL->deltat;
+ OPLSAMPLE *buf = buffer;
+
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+ OPL->output_deltat[0] = 0;
+
+ OPL->advance_lfo();
+
+ /* deltaT ADPCM */
+ if( DELTAT->portstate&0x80 )
+ DELTAT->ADPCM_CALC();
+
+ /* FM part */
+ OPL->CALC_CH(OPL->P_CH[0]);
+ OPL->CALC_CH(OPL->P_CH[1]);
+ OPL->CALC_CH(OPL->P_CH[2]);
+ OPL->CALC_CH(OPL->P_CH[3]);
+ OPL->CALC_CH(OPL->P_CH[4]);
+ OPL->CALC_CH(OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL->CALC_CH(OPL->P_CH[6]);
+ OPL->CALC_CH(OPL->P_CH[7]);
+ OPL->CALC_CH(OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL->CALC_RH();
+ }
+
+ lt = OPL->output[0] + (OPL->output_deltat[0]>>11);
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ OPL->advance();
+ }
+
+}
+
+void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,device_t *device)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->porthandler_w = PortHandler_w;
+ OPL->porthandler_r = PortHandler_r;
+ OPL->port_param = device;
+}
+
+void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,device_t *device)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->keyboardhandler_w = KeyboardHandler_w;
+ OPL->keyboardhandler_r = KeyboardHandler_r;
+ OPL->keyboard_param = device;
+}
+
+
+#endif
diff --git a/src/hardware/mame/fmopl.h b/src/hardware/mame/fmopl.h
new file mode 100644
index 000000000..3ea60ada7
--- /dev/null
+++ b/src/hardware/mame/fmopl.h
@@ -0,0 +1,113 @@
+// license:GPL-2.0+
+// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh
+#ifndef MAME_SOUND_FMOPL_H
+#define MAME_SOUND_FMOPL_H
+
+#pragma once
+
+#if defined(_MSC_VER) && (_MSC_VER <= 1500)
+#include <SDL.h>
+#else
+#include <stdint.h>
+#endif
+
+
+/* --- select emulation chips --- */
+#define BUILD_YM3812 1
+#define BUILD_YM3526 (0)
+#define BUILD_Y8950 (0)
+
+/* select output bits size of output : 8 or 16 */
+#define OPL_SAMPLE_BITS 16
+
+typedef stream_sample_t OPLSAMPLE;
+/*
+#if (OPL_SAMPLE_BITS==16)
+typedef int16_t OPLSAMPLE;
+#endif
+#if (OPL_SAMPLE_BITS==8)
+typedef int8_t OPLSAMPLE;
+#endif
+*/
+
+typedef void (*OPL_TIMERHANDLER)(device_t *device,int timer,const attotime &period);
+typedef void (*OPL_IRQHANDLER)(device_t *device,int irq);
+typedef void (*OPL_UPDATEHANDLER)(device_t *device,int min_interval_us);
+typedef void (*OPL_PORTHANDLER_W)(device_t *device,unsigned char data);
+typedef unsigned char (*OPL_PORTHANDLER_R)(device_t *device);
+
+
+#if BUILD_YM3812
+
+void *ym3812_init(device_t *device, uint32_t clock, uint32_t rate);
+void ym3812_clock_changed(void *chip, uint32_t clock, uint32_t rate);
+void ym3812_shutdown(void *chip);
+void ym3812_reset_chip(void *chip);
+int ym3812_write(void *chip, int a, int v);
+unsigned char ym3812_read(void *chip, int a);
+int ym3812_timer_over(void *chip, int c);
+void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length);
+
+void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device);
+void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device);
+void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device);
+
+#endif /* BUILD_YM3812 */
+
+
+#if BUILD_YM3526
+
+/*
+** Initialize YM3526 emulator(s).
+**
+** 'num' is the number of virtual YM3526's to allocate
+** 'clock' is the chip clock in Hz
+** 'rate' is sampling rate
+*/
+void *ym3526_init(device_t *device, uint32_t clock, uint32_t rate);
+void ym3526_clock_changed(void *chip, uint32_t clock, uint32_t rate);
+/* shutdown the YM3526 emulators*/
+void ym3526_shutdown(void *chip);
+void ym3526_reset_chip(void *chip);
+int ym3526_write(void *chip, int a, int v);
+unsigned char ym3526_read(void *chip, int a);
+int ym3526_timer_over(void *chip, int c);
+/*
+** Generate samples for one of the YM3526's
+**
+** 'which' is the virtual YM3526 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length);
+
+void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device);
+void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device);
+void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device);
+
+#endif /* BUILD_YM3526 */
+
+
+#if BUILD_Y8950
+
+/* Y8950 port handlers */
+void y8950_set_port_handler(void *chip, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, device_t *device);
+void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, device_t *device);
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size );
+
+void * y8950_init(device_t *device, uint32_t clock, uint32_t rate);
+void y8950_shutdown(void *chip);
+void y8950_reset_chip(void *chip);
+int y8950_write(void *chip, int a, int v);
+unsigned char y8950_read (void *chip, int a);
+int y8950_timer_over(void *chip, int c);
+void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length);
+
+void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device);
+void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device);
+void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device);
+
+#endif /* BUILD_Y8950 */
+
+
+#endif // MAME_SOUND_FMOPL_H
diff --git a/src/hardware/mame/saa1099.cpp b/src/hardware/mame/saa1099.cpp
new file mode 100644
index 000000000..7f9823e9a
--- /dev/null
+++ b/src/hardware/mame/saa1099.cpp
@@ -0,0 +1,471 @@
+// license:BSD-3-Clause
+// copyright-holders:Juergen Buchmueller, Manuel Abadia
+/***************************************************************************
+
+ Philips SAA1099 Sound driver
+
+ By Juergen Buchmueller and Manuel Abadia
+
+ SAA1099 register layout:
+ ========================
+
+ offs | 7654 3210 | description
+ -----+-----------+---------------------------
+ 0x00 | ---- xxxx | Amplitude channel 0 (left)
+ 0x00 | xxxx ---- | Amplitude channel 0 (right)
+ 0x01 | ---- xxxx | Amplitude channel 1 (left)
+ 0x01 | xxxx ---- | Amplitude channel 1 (right)
+ 0x02 | ---- xxxx | Amplitude channel 2 (left)
+ 0x02 | xxxx ---- | Amplitude channel 2 (right)
+ 0x03 | ---- xxxx | Amplitude channel 3 (left)
+ 0x03 | xxxx ---- | Amplitude channel 3 (right)
+ 0x04 | ---- xxxx | Amplitude channel 4 (left)
+ 0x04 | xxxx ---- | Amplitude channel 4 (right)
+ 0x05 | ---- xxxx | Amplitude channel 5 (left)
+ 0x05 | xxxx ---- | Amplitude channel 5 (right)
+ | |
+ 0x08 | xxxx xxxx | Frequency channel 0
+ 0x09 | xxxx xxxx | Frequency channel 1
+ 0x0a | xxxx xxxx | Frequency channel 2
+ 0x0b | xxxx xxxx | Frequency channel 3
+ 0x0c | xxxx xxxx | Frequency channel 4
+ 0x0d | xxxx xxxx | Frequency channel 5
+ | |
+ 0x10 | ---- -xxx | Channel 0 octave select
+ 0x10 | -xxx ---- | Channel 1 octave select
+ 0x11 | ---- -xxx | Channel 2 octave select
+ 0x11 | -xxx ---- | Channel 3 octave select
+ 0x12 | ---- -xxx | Channel 4 octave select
+ 0x12 | -xxx ---- | Channel 5 octave select
+ | |
+ 0x14 | ---- ---x | Channel 0 frequency enable (0 = off, 1 = on)
+ 0x14 | ---- --x- | Channel 1 frequency enable (0 = off, 1 = on)
+ 0x14 | ---- -x-- | Channel 2 frequency enable (0 = off, 1 = on)
+ 0x14 | ---- x--- | Channel 3 frequency enable (0 = off, 1 = on)
+ 0x14 | ---x ---- | Channel 4 frequency enable (0 = off, 1 = on)
+ 0x14 | --x- ---- | Channel 5 frequency enable (0 = off, 1 = on)
+ | |
+ 0x15 | ---- ---x | Channel 0 noise enable (0 = off, 1 = on)
+ 0x15 | ---- --x- | Channel 1 noise enable (0 = off, 1 = on)
+ 0x15 | ---- -x-- | Channel 2 noise enable (0 = off, 1 = on)
+ 0x15 | ---- x--- | Channel 3 noise enable (0 = off, 1 = on)
+ 0x15 | ---x ---- | Channel 4 noise enable (0 = off, 1 = on)
+ 0x15 | --x- ---- | Channel 5 noise enable (0 = off, 1 = on)
+ | |
+ 0x16 | ---- --xx | Noise generator parameters 0
+ 0x16 | --xx ---- | Noise generator parameters 1
+ | |
+ 0x18 | --xx xxxx | Envelope generator 0 parameters
+ 0x18 | x--- ---- | Envelope generator 0 control enable (0 = off, 1 = on)
+ 0x19 | --xx xxxx | Envelope generator 1 parameters
+ 0x19 | x--- ---- | Envelope generator 1 control enable (0 = off, 1 = on)
+ | |
+ 0x1c | ---- ---x | All channels enable (0 = off, 1 = on)
+ 0x1c | ---- --x- | Synch & Reset generators
+
+ Unspecified bits should be written as zero.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "saa1099.h"
+
+#define LEFT 0x00
+#define RIGHT 0x01
+
+static const int amplitude_lookup[16] = {
+ 0*32767/16, 1*32767/16, 2*32767/16, 3*32767/16,
+ 4*32767/16, 5*32767/16, 6*32767/16, 7*32767/16,
+ 8*32767/16, 9*32767/16, 10*32767/16, 11*32767/16,
+ 12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16
+};
+
+static const uint8_t envelope[8][64] = {
+ /* zero amplitude */
+ { 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 },
+ /* maximum amplitude */
+ {15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
+ 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
+ 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
+ 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, },
+ /* single decay */
+ {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 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 },
+ /* repetitive decay */
+ {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
+ 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
+ 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
+ 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 },
+ /* single triangular */
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
+ 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 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 },
+ /* repetitive triangular */
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
+ 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
+ 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 },
+ /* single attack */
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
+ 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 },
+ /* repetitive attack */
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 }
+};
+
+
+// device type definition
+//DEFINE_DEVICE_TYPE(SAA1099, saa1099_device, "saa1099", "Philips SAA1099")
+#define SAA1099 1
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// saa1099_device - constructor
+//-------------------------------------------------
+
+#define FILL_ARRAY( _FILL_ ) memset( _FILL_, 0, sizeof( _FILL_ ) )
+
+saa1099_device::saa1099_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, SAA1099, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+ , m_stream(0)
+#if 0
+ , m_noise_params{ 0, 0 }
+ , m_env_enable{ 0, 0 }
+ , m_env_reverse_right{ 0, 0 }
+ , m_env_mode{ 0, 0 }
+ , m_env_bits{ 0, 0 }
+ , m_env_clock{ 0, 0 }
+ , m_env_step{ 0, 0 }
+#endif
+ , m_all_ch_enable(0)
+ , m_sync_state(0)
+ , m_selected_reg(0)
+ , m_sample_rate(0.0)
+{
+ FILL_ARRAY( m_noise_params );
+ FILL_ARRAY( m_env_enable );
+ FILL_ARRAY( m_env_reverse_right );
+ FILL_ARRAY( m_env_mode );
+ FILL_ARRAY( m_env_bits );
+ FILL_ARRAY( m_env_clock );
+ FILL_ARRAY( m_env_step );
+
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void saa1099_device::device_start()
+{
+ /* copy global parameters */
+ m_master_clock = clock();
+ m_sample_rate = clock() / 256;
+
+ /* for each chip allocate one stream */
+ m_stream = stream_alloc(0, 2, m_sample_rate);
+
+ save_item(NAME(m_noise_params));
+ save_item(NAME(m_env_enable));
+ save_item(NAME(m_env_reverse_right));
+ save_item(NAME(m_env_mode));
+ save_item(NAME(m_env_bits));
+ save_item(NAME(m_env_clock));
+ save_item(NAME(m_env_step));
+ save_item(NAME(m_all_ch_enable));
+ save_item(NAME(m_sync_state));
+ save_item(NAME(m_selected_reg));
+
+ for (int i = 0; i < 6; i++)
+ {
+ save_item(NAME(m_channels[i].frequency), i);
+ save_item(NAME(m_channels[i].freq_enable), i);
+ save_item(NAME(m_channels[i].noise_enable), i);
+ save_item(NAME(m_channels[i].octave), i);
+ save_item(NAME(m_channels[i].amplitude), i);
+ save_item(NAME(m_channels[i].envelope), i);
+ save_item(NAME(m_channels[i].counter), i);
+ save_item(NAME(m_channels[i].freq), i);
+ save_item(NAME(m_channels[i].level), i);
+ }
+
+ for (int i = 0; i < 2; i++)
+ {
+ save_item(NAME(m_noise[i].counter), i);
+ save_item(NAME(m_noise[i].freq), i);
+ save_item(NAME(m_noise[i].level), i);
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void saa1099_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int j, ch;
+ /* if the channels are disabled we're done */
+ if (!m_all_ch_enable)
+ {
+ /* init output data */
+ memset(outputs[LEFT],0,samples*sizeof(*outputs[LEFT]));
+ memset(outputs[RIGHT],0,samples*sizeof(*outputs[RIGHT]));
+ return;
+ }
+
+ for (ch = 0; ch < 2; ch++)
+ {
+ switch (m_noise_params[ch])
+ {
+ case 0: m_noise[ch].freq = m_master_clock/256.0 * 2; break;
+ case 1: m_noise[ch].freq = m_master_clock/512.0 * 2; break;
+ case 2: m_noise[ch].freq = m_master_clock/1024.0 * 2; break;
+ case 3: m_noise[ch].freq = m_channels[ch * 3].freq; break; // todo: this case will be m_master_clock/[ch*3's octave divisor, 0 is = 256*2, higher numbers are higher] * 2 if the tone generator phase reset bit (0x1c bit 1) is set.
+ }
+ }
+
+ /* fill all data needed */
+ for( j = 0; j < samples; j++ )
+ {
+ int output_l = 0, output_r = 0;
+
+ /* for each channel */
+ for (ch = 0; ch < 6; ch++)
+ {
+ if (m_channels[ch].freq == 0.0)
+ m_channels[ch].freq = (double)((2 * m_master_clock / 512) << m_channels[ch].octave) /
+ (511.0 - (double)m_channels[ch].frequency);
+
+ /* check the actual position in the square wave */
+ m_channels[ch].counter -= m_channels[ch].freq;
+ while (m_channels[ch].counter < 0)
+ {
+ /* calculate new frequency now after the half wave is updated */
+ m_channels[ch].freq = (double)((2 * m_master_clock / 512) << m_channels[ch].octave) /
+ (511.0 - (double)m_channels[ch].frequency);
+
+ m_channels[ch].counter += m_sample_rate;
+ m_channels[ch].level ^= 1;
+
+ /* eventually clock the envelope counters */
+ if (ch == 1 && m_env_clock[0] == 0)
+ envelope_w(0);
+ if (ch == 4 && m_env_clock[1] == 0)
+ envelope_w(1);
+ }
+
+ // if the noise is enabled
+ if (m_channels[ch].noise_enable)
+ {
+ // if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5)
+ if (m_noise[ch/3].level & 1)
+ {
+ // subtract to avoid overflows, also use only half amplitude
+ output_l -= m_channels[ch].amplitude[ LEFT] * m_channels[ch].envelope[ LEFT] / 16 / 2;
+ output_r -= m_channels[ch].amplitude[RIGHT] * m_channels[ch].envelope[RIGHT] / 16 / 2;
+ }
+ }
+ // if the square wave is enabled
+ if (m_channels[ch].freq_enable)
+ {
+ // if the channel level is high
+ if (m_channels[ch].level & 1)
+ {
+ output_l += m_channels[ch].amplitude[ LEFT] * m_channels[ch].envelope[ LEFT] / 16;
+ output_r += m_channels[ch].amplitude[RIGHT] * m_channels[ch].envelope[RIGHT] / 16;
+ }
+ }
+ }
+
+ for (ch = 0; ch < 2; ch++)
+ {
+ /* update the state of the noise generator
+ * polynomial is x^18 + x^11 + x (i.e. 0x20400) and is a plain XOR, initial state is probably all 1s
+ * see http://www.vogons.org/viewtopic.php?f=9&t=51695 */
+ m_noise[ch].counter -= m_noise[ch].freq;
+ while (m_noise[ch].counter < 0)
+ {
+ m_noise[ch].counter += m_sample_rate;
+ if( ((m_noise[ch].level & 0x20000) == 0) != ((m_noise[ch].level & 0x0400) == 0) )
+ m_noise[ch].level = (m_noise[ch].level << 1) | 1;
+ else
+ m_noise[ch].level <<= 1;
+ }
+ }
+ /* write sound data to the buffer */
+ outputs[LEFT][j] = output_l / 6;
+ outputs[RIGHT][j] = output_r / 6;
+ }
+}
+
+
+void saa1099_device::envelope_w(int ch)
+{
+ if (m_env_enable[ch])
+ {
+ int step, mode, mask;
+ mode = m_env_mode[ch];
+ /* step from 0..63 and then loop in steps 32..63 */
+ step = m_env_step[ch] =
+ ((m_env_step[ch] + 1) & 0x3f) | (m_env_step[ch] & 0x20);
+
+ mask = 15;
+ if (m_env_bits[ch])
+ mask &= ~1; /* 3 bit resolution, mask LSB */
+
+ m_channels[ch*3+0].envelope[ LEFT] =
+ m_channels[ch*3+1].envelope[ LEFT] =
+ m_channels[ch*3+2].envelope[ LEFT] = envelope[mode][step] & mask;
+ if (m_env_reverse_right[ch] & 0x01)
+ {
+ m_channels[ch*3+0].envelope[RIGHT] =
+ m_channels[ch*3+1].envelope[RIGHT] =
+ m_channels[ch*3+2].envelope[RIGHT] = (15 - envelope[mode][step]) & mask;
+ }
+ else
+ {
+ m_channels[ch*3+0].envelope[RIGHT] =
+ m_channels[ch*3+1].envelope[RIGHT] =
+ m_channels[ch*3+2].envelope[RIGHT] = envelope[mode][step] & mask;
+ }
+ }
+ else
+ {
+ /* envelope mode off, set all envelope factors to 16 */
+ m_channels[ch*3+0].envelope[ LEFT] =
+ m_channels[ch*3+1].envelope[ LEFT] =
+ m_channels[ch*3+2].envelope[ LEFT] =
+ m_channels[ch*3+0].envelope[RIGHT] =
+ m_channels[ch*3+1].envelope[RIGHT] =
+ m_channels[ch*3+2].envelope[RIGHT] = 16;
+ }
+}
+
+
+WRITE8_MEMBER( saa1099_device::control_w )
+{
+ if ((data & 0xff) > 0x1c)
+ {
+ /* Error! */
+ logerror("%s: (SAA1099 '%s') Unknown register selected\n", machine().describe_context(), tag());
+ }
+
+ m_selected_reg = data & 0x1f;
+ if (m_selected_reg == 0x18 || m_selected_reg == 0x19)
+ {
+ /* clock the envelope channels */
+ if (m_env_clock[0])
+ envelope_w(0);
+ if (m_env_clock[1])
+ envelope_w(1);
+ }
+}
+
+
+WRITE8_MEMBER( saa1099_device::data_w )
+{
+ int reg = m_selected_reg;
+ int ch;
+
+ /* first update the stream to this point in time */
+ m_stream->update();
+
+ switch (reg)
+ {
+ /* channel i amplitude */
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
+ ch = reg & 7;
+ m_channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f];
+ m_channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f];
+ break;
+ /* channel i frequency */
+ case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d:
+ ch = reg & 7;
+ m_channels[ch].frequency = data & 0xff;
+ break;
+ /* channel i octave */
+ case 0x10: case 0x11: case 0x12:
+ ch = (reg - 0x10) << 1;
+ m_channels[ch + 0].octave = data & 0x07;
+ m_channels[ch + 1].octave = (data >> 4) & 0x07;
+ break;
+ /* channel i frequency enable */
+ case 0x14:
+ m_channels[0].freq_enable = data & 0x01;
+ m_channels[1].freq_enable = data & 0x02;
+ m_channels[2].freq_enable = data & 0x04;
+ m_channels[3].freq_enable = data & 0x08;
+ m_channels[4].freq_enable = data & 0x10;
+ m_channels[5].freq_enable = data & 0x20;
+ break;
+ /* channel i noise enable */
+ case 0x15:
+ m_channels[0].noise_enable = data & 0x01;
+ m_channels[1].noise_enable = data & 0x02;
+ m_channels[2].noise_enable = data & 0x04;
+ m_channels[3].noise_enable = data & 0x08;
+ m_channels[4].noise_enable = data & 0x10;
+ m_channels[5].noise_enable = data & 0x20;
+ break;
+ /* noise generators parameters */
+ case 0x16:
+ m_noise_params[0] = data & 0x03;
+ m_noise_params[1] = (data >> 4) & 0x03;
+ break;
+ /* envelope generators parameters */
+ case 0x18: case 0x19:
+ ch = reg - 0x18;
+ m_env_reverse_right[ch] = data & 0x01;
+ m_env_mode[ch] = (data >> 1) & 0x07;
+ m_env_bits[ch] = data & 0x10;
+ m_env_clock[ch] = data & 0x20;
+ m_env_enable[ch] = data & 0x80;
+ /* reset the envelope */
+ m_env_step[ch] = 0;
+ break;
+ /* channels enable & reset generators */
+ case 0x1c:
+ m_all_ch_enable = data & 0x01;
+ m_sync_state = data & 0x02;
+ if (data & 0x02)
+ {
+ int i;
+
+ /* Synch & Reset generators */
+ logerror("%s: (SAA1099 '%s') -reg 0x1c- Chip reset\n", machine().describe_context(), tag());
+ for (i = 0; i < 6; i++)
+ {
+ m_channels[i].level = 0;
+ m_channels[i].counter = 0.0;
+ }
+ }
+ break;
+ default: /* Error! */
+ if (data != 0)
+ logerror("%s: (SAA1099 '%s') Unknown operation (reg:%02x, data:%02x)\n", machine().describe_context(), tag(), reg, data);
+ }
+}
+
+WRITE8_MEMBER(saa1099_device::write)
+{
+ if (offset & 1)
+ control_w(space, 0, data);
+ else
+ data_w(space, 0, data);
+}
diff --git a/src/hardware/mame/saa1099.h b/src/hardware/mame/saa1099.h
new file mode 100644
index 000000000..cce4fb558
--- /dev/null
+++ b/src/hardware/mame/saa1099.h
@@ -0,0 +1,120 @@
+// license:BSD-3-Clause
+// copyright-holders:Juergen Buchmueller, Manuel Abadia
+/**********************************************
+ Philips SAA1099 Sound driver
+**********************************************/
+
+#ifndef MAME_SOUND_SAA1099_H
+#define MAME_SOUND_SAA1099_H
+
+#pragma once
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_SAA1099_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, SAA1099, _clock)
+#define MCFG_SAA1099_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, SAA1099, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+//Container class for int that just initalizes to 0
+class NullInt {
+ int value;
+public:
+ operator int& () {
+ return value;
+ }
+
+ int& operator= ( int set ) {
+ value = set;
+ return value;
+ }
+
+ NullInt( int set = 0 ) : value( set ) {
+ }
+};
+
+// ======================> saa1099_device
+
+class saa1099_device : public device_t,
+ public device_sound_interface
+{
+public:
+ saa1099_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ DECLARE_WRITE8_MEMBER( control_w );
+ DECLARE_WRITE8_MEMBER( data_w );
+
+ DECLARE_WRITE8_MEMBER( write );
+
+//protected:
+ // device-level overrides
+ virtual void device_start();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ struct saa1099_channel
+ {
+ saa1099_channel() {
+ //Quite hacky, but let's see how it goes
+ memset( this, 0, sizeof( *this ) );
+ }
+
+ int frequency ; /* frequency (0x00..0xff) */
+ int freq_enable ; /* frequency enable */
+ int noise_enable ; /* noise enable */
+ int octave ; /* octave (0x00..0x07) */
+ int amplitude[2]; /* amplitude (0x00..0x0f) */
+ int envelope[2]; /* envelope (0x00..0x0f or 0x10 == off) */
+
+ /* vars to simulate the square wave */
+ double counter ;
+ double freq ;
+ int level ;
+
+ };
+
+ struct saa1099_noise
+ {
+ saa1099_noise() {
+ counter = 0;
+ freq = 0;
+ level = 0xFFFFFFFF;
+ }
+
+ /* vars to simulate the noise generator output */
+ double counter;
+ double freq;
+ uint32_t level; /* noise polynomial shifter */
+ };
+
+ void envelope_w(int ch);
+
+ sound_stream *m_stream; /* our stream */
+ int m_noise_params[2]; /* noise generators parameters */
+ int m_env_enable[2]; /* envelope generators enable */
+ int m_env_reverse_right[2]; /* envelope reversed for right channel */
+ int m_env_mode[2]; /* envelope generators mode */
+ int m_env_bits[2]; /* non zero = 3 bits resolution */
+ int m_env_clock[2]; /* envelope clock mode (non-zero external) */
+ int m_env_step[2]; /* current envelope step */
+ int m_all_ch_enable; /* all channels enable */
+ int m_sync_state; /* sync all channels */
+ int m_selected_reg; /* selected register */
+ saa1099_channel m_channels[6]; /* channels */
+ saa1099_noise m_noise[2]; /* noise generators */
+ double m_sample_rate;
+ int m_master_clock;
+};
+
+DECLARE_DEVICE_TYPE(SAA1099, saa1099_device)
+
+#endif // MAME_SOUND_SAA1099_H
diff --git a/src/hardware/mame/sn76496.cpp b/src/hardware/mame/sn76496.cpp
new file mode 100644
index 000000000..d11f0eaff
--- /dev/null
+++ b/src/hardware/mame/sn76496.cpp
@@ -0,0 +1,518 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria
+/***************************************************************************
+
+ sn76496.c
+ by Nicola Salmoria
+ with contributions by others
+
+ Routines to emulate the:
+ Texas Instruments SN76489, SN76489A, SN76494/SN76496
+ ( Also known as, or at least compatible with, the TMS9919 and SN94624.)
+ and the Sega 'PSG' used on the Master System, Game Gear, and Megadrive/Genesis
+ This chip is known as the Programmable Sound Generator, or PSG, and is a 4
+ channel sound generator, with three squarewave channels and a noise/arbitrary
+ duty cycle channel.
+
+ Noise emulation for all verified chips should be accurate:
+
+ ** SN76489 uses a 15-bit shift register with taps on bits D and E, output on E,
+ XOR function.
+ It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle.
+ Its output is inverted.
+ ** SN94624 is the same as SN76489 but lacks the /8 divider on its clock input.
+ ** SN76489A uses a 15-bit shift register with taps on bits D and E, output on F,
+ XOR function.
+ It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle.
+ Its output is not inverted.
+ ** SN76494 is the same as SN76489A but lacks the /8 divider on its clock input.
+ ** SN76496 is identical in operation to the SN76489A, but the audio input on pin 9 is
+ documented.
+ All the TI-made PSG chips have an audio input line which is mixed with the 4 channels
+ of output. (It is undocumented and may not function properly on the sn76489, 76489a
+ and 76494; the sn76489a input is mentioned in datasheets for the tms5200)
+ All the TI-made PSG chips act as if the frequency was set to 0x400 if 0 is
+ written to the frequency register.
+ ** Sega Master System III/MD/Genesis PSG uses a 16-bit shift register with taps
+ on bits C and F, output on F
+ It uses a 16-bit ring buffer for periodic noise/arbitrary duty cycle.
+ (whether it uses an XOR or XNOR needs to be verified, assumed XOR)
+ (whether output is inverted or not needs to be verified, assumed to be inverted)
+ ** Sega Game Gear PSG is identical to the SMS3/MD/Genesis one except it has an
+ extra register for mapping which channels go to which speaker.
+ The register, connected to a z80 port, means:
+ for bits 7 6 5 4 3 2 1 0
+ L3 L2 L1 L0 R3 R2 R1 R0
+ Noise is an XOR function, and audio output is negated before being output.
+ All the Sega-made PSG chips act as if the frequency was set to 0 if 0 is written
+ to the frequency register.
+ ** NCR8496 (as used on the Tandy 1000) is similar to the SN76489 but with a
+ different noise LFSR pattern: taps on bits A and E, output on E, XNOR function
+ It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle.
+ Its output is inverted.
+ ** PSSJ-3 (as used on the later Tandy 1000 series computers) is the same as the
+ NCR8496 with the exception that its output is not inverted.
+
+ 28/03/2005 : Sebastien Chevalier
+ Update th SN76496Write func, according to SN76489 doc found on SMSPower.
+ - On write with 0x80 set to 0, when LastRegister is other then TONE,
+ the function is similar than update with 0x80 set to 1
+
+ 23/04/2007 : Lord Nightmare
+ Major update, implement all three different noise generation algorithms and a
+ set_variant call to discern among them.
+
+ 28/04/2009 : Lord Nightmare
+ Add READY line readback; cleaned up struct a bit. Cleaned up comments.
+ Add more TODOs. Fixed some unsaved savestate related stuff.
+
+ 04/11/2009 : Lord Nightmare
+ Changed the way that the invert works (it now selects between XOR and XNOR
+ for the taps), and added R->OldNoise to simulate the extra 0 that is always
+ output before the noise LFSR contents are after an LFSR reset.
+ This fixes SN76489/A to match chips. Added SN94624.
+
+ 14/11/2009 : Lord Nightmare
+ Removed STEP mess, vastly simplifying the code. Made output bipolar rather
+ than always above the 0 line, but disabled that code due to pending issues.
+
+ 16/11/2009 : Lord Nightmare
+ Fix screeching in regulus: When summing together four equal channels, the
+ size of the max amplitude per channel should be 1/4 of the max range, not
+ 1/3. Added NCR8496.
+
+ 18/11/2009 : Lord Nightmare
+ Modify Init functions to support negating the audio output. The gamegear
+ psg does this. Change gamegear and sega psgs to use XOR rather than XNOR
+ based on testing. Got rid of R->OldNoise and fixed taps accordingly.
+ Added stereo support for game gear.
+
+ 15/01/2010 : Lord Nightmare
+ Fix an issue with SN76489 and SN76489A having the wrong periodic noise periods.
+ Note that properly emulating the noise cycle bit timing accurately may require
+ extensive rewriting.
+
+ 24/01/2010: Lord Nightmare
+ Implement periodic noise as forcing one of the XNOR or XOR taps to 1 or 0 respectively.
+ Thanks to PlgDavid for providing samples which helped immensely here.
+ Added true clock divider emulation, so sn94624 and sn76494 run 8x faster than
+ the others, as in real life.
+
+ 15/02/2010: Lord Nightmare & Michael Zapf (additional testing by PlgDavid)
+ Fix noise period when set to mirror channel 3 and channel 3 period is set to 0 (tested on hardware for noise, wave needs tests) - MZ
+ Fix phase of noise on sn94624 and sn76489; all chips use a standard XOR, the only inversion is the output itself - LN, Plgdavid
+ Thanks to PlgDavid and Michael Zapf for providing samples which helped immensely here.
+
+ 23/02/2011: Lord Nightmare & Enik
+ Made it so the Sega PSG chips have a frequency of 0 if 0 is written to the
+ frequency register, while the others have 0x400 as before. Should fix a bug
+ or two on sega games, particularly Vigilante on Sega Master System. Verified
+ on SMS hardware.
+
+ 27/06/2012: Michael Zapf
+ Converted to modern device, legacy devices were gradually removed afterwards.
+
+ 16/09/2015: Lord Nightmare
+ Fix PSG chips to have volume reg inited on reset to 0x0 based on tests by
+ ValleyBell. Made Sega PSG chips start up with register 0x3 selected (volume
+ for channel 2) based on hardware tests by Nemesis.
+
+ 26/08/2018: Lord Nightmare, Qbix, ValleyBell, NewRisingSun
+ * renamed the NCR8496 to its correct name, based on chip pictures on VGMPF
+ * fixed NCR8496 behavior on write to mirrored registers; unlike any of the
+ other variants, the NCR8496 seems to ignore writes to regs 1,3,5,6,7 if 0x80
+ is not set.
+***TODO: the above is NOT verified yet!***
+ * fixed NCR8496's noise lfsr behavior so it is only reset if the mode bit in
+ register 6 is changed.
+ * NCR8496's LFSR feedback function is an XNOR, which is now supported
+ * NCR8496's output is inverted (though PSSJ-3's output is not)
+ * add PSSJ-3 support for the later Tandy computers.
+
+ TODO: * Implement the TMS9919 - any difference to sn94624?
+ * Implement the T6W28; has registers in a weird order, needs writes
+ to be 'sanitized' first. Also is stereo, similar to game gear.
+ * Factor out common code so that the SAA1099 can share some code.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "sn76496.h"
+
+#define MAX_OUTPUT 0x7fff
+//When you go over this create sample
+#define RATE_MAX ( 1 << 30)
+
+sn76496_base_device::sn76496_base_device(
+ const machine_config &mconfig,
+ device_type type,
+ const char *tag,
+ int feedbackmask,
+ int noisetap1,
+ int noisetap2,
+ bool negate,
+ bool stereo,
+ int clockdivider,
+ bool ncr,
+ bool sega,
+ device_t *owner,
+ uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+// , m_ready_handler(*this)
+ , m_feedback_mask(feedbackmask)
+ , m_whitenoise_tap1(noisetap1)
+ , m_whitenoise_tap2(noisetap2)
+ , m_negate(negate)
+ , m_stereo(stereo)
+ , m_clock_divider(clockdivider)
+ , m_ncr_style_psg(ncr)
+ , m_sega_style_psg(sega)
+{
+}
+
+
+sn76496_device::sn76496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sn76496_base_device(mconfig, SN76496, tag, 0x10000, 0x04, 0x08, false, false, 8, false, true, owner, clock)
+{
+}
+
+u8106_device::u8106_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sn76496_base_device(mconfig, U8106, tag, 0x4000, 0x01, 0x02, true, false, 8, false, true, owner, clock)
+{
+}
+
+y2404_device::y2404_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sn76496_base_device(mconfig, Y2404, tag, 0x10000, 0x04, 0x08, false, false, 8, false, true, owner, clock)
+{
+}
+
+sn76489_device::sn76489_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sn76496_base_device(mconfig, SN76489, tag, 0x4000, 0x01, 0x02, true, false, 8, false, true, owner, clock)
+{
+}
+
+sn76489a_device::sn76489a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sn76496_base_device(mconfig, SN76489A, tag, 0x10000, 0x04, 0x08, false, false, 8, false, true, owner, clock)
+{
+}
+
+sn76494_device::sn76494_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sn76496_base_device(mconfig, SN76494, tag, 0x10000, 0x04, 0x08, false, false, 1, false, true, owner, clock)
+{
+}
+
+sn94624_device::sn94624_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sn76496_base_device(mconfig, SN94624, tag, 0x4000, 0x01, 0x02, true, false, 1, false, true, owner, clock)
+{
+}
+
+ncr8496_device::ncr8496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sn76496_base_device(mconfig, NCR8496, tag, 0x8000, 0x02, 0x20, true, false, 8, true, true, owner, clock)
+{
+}
+
+pssj3_device::pssj3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sn76496_base_device(mconfig, PSSJ3, tag, 0x8000, 0x02, 0x20, false, false, 8, true, true, owner, clock)
+{
+}
+
+gamegear_device::gamegear_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sn76496_base_device(mconfig, GAMEGEAR, tag, 0x8000, 0x01, 0x08, true, true, 8, false, false, owner, clock)
+{
+}
+
+segapsg_device::segapsg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sn76496_base_device(mconfig, SEGAPSG, tag, 0x8000, 0x01, 0x08, true, false, 8, false, false, owner, clock)
+{
+}
+
+void sn76496_base_device::device_start()
+{
+ sample_rate = clock()/2;
+ rate_add = RATE_MAX;
+ rate_counter = 0;
+
+ int i;
+ double out;
+ int gain;
+
+ //m_ready_handler.resolve_safe();
+
+ //m_sound = machine().sound().stream_alloc(*this, 0, (m_stereo? 2:1), sample_rate);
+
+ for (i = 0; i < 4; i++) m_volume[i] = 0;
+
+ m_last_register = m_sega_style_psg?3:0; // Sega VDP PSG defaults to selected period reg for 2nd channel
+ for (i = 0; i < 8; i+=2)
+ {
+ m_register[i] = 0;
+ m_register[i + 1] = 0x0; // volume = 0x0 (max volume) on reset; this needs testing on chips other than SN76489A and Sega VDP PSG
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ m_output[i] = 0;
+ m_period[i] = 0;
+ m_count[i] = 0;
+ }
+
+ m_RNG = m_feedback_mask;
+ m_output[3] = m_RNG & 1;
+
+ m_cycles_to_ready = 1; // assume ready is not active immediately on init. is this correct?
+ m_stereo_mask = 0xFF; // all channels enabled
+ m_current_clock = m_clock_divider-1;
+
+ // set gain
+ gain = 0;
+
+ gain &= 0xff;
+
+ // increase max output basing on gain (0.2 dB per step)
+ out = MAX_OUTPUT / 4; // four channels, each gets 1/4 of the total range
+ while (gain-- > 0)
+ out *= 1.023292992; // = (10 ^ (0.2/20))
+
+ // build volume table (2dB per step)
+ for (i = 0; i < 15; i++)
+ {
+ // limit volume to avoid clipping
+ if (out > MAX_OUTPUT / 4) m_vol_table[i] = MAX_OUTPUT / 4;
+ else m_vol_table[i] = out;
+
+ out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
+ }
+ m_vol_table[15] = 0;
+
+ m_ready_state = true;
+
+ //register_for_save_states();
+}
+
+void sn76496_base_device::device_clock_changed()
+{
+// m_sound->set_sample_rate(clock()/2);
+}
+
+WRITE8_MEMBER( sn76496_base_device::stereo_w )
+{
+// m_sound->update();
+// if (m_stereo) m_stereo_mask = data;
+// else fatalerror("sn76496_base_device: Call to stereo write with mono chip!\n");
+}
+
+void sn76496_base_device::write(uint8_t data)
+{
+ int n, r, c;
+
+ // update the output buffer before changing the registers
+// m_sound->update();
+
+ // set number of cycles until READY is active; this is always one
+ // 'sample', i.e. it equals the clock divider exactly
+ m_cycles_to_ready = 1;
+
+ if (data & 0x80)
+ {
+ r = (data & 0x70) >> 4;
+ m_last_register = r;
+ if (((m_ncr_style_psg) && (r == 6)) && ((data&0x04) != (m_register[6]&0x04))) m_RNG = m_feedback_mask;
+ m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f);
+ }
+ else
+ {
+ r = m_last_register;
+ if ((m_ncr_style_psg) && ((r & 1) || (r == 6))) return; // NCR8496 ignores writes to regs 1, 3, 5, 6 and 7 with bit 7 clear
+ }
+
+ c = r >> 1;
+ switch (r)
+ {
+ case 0: // tone 0: frequency
+ case 2: // tone 1: frequency
+ case 4: // tone 2: frequency
+ if ((data & 0x80) == 0) m_register[r] = (m_register[r] & 0x0f) | ((data & 0x3f) << 4);
+ if ((m_register[r] != 0) || (!m_sega_style_psg)) m_period[c] = m_register[r];
+ else m_period[c] = 0x400;
+
+ if (r == 4)
+ {
+ // update noise shift frequency
+ if ((m_register[6] & 0x03) == 0x03) m_period[3] = m_period[2]<<1;
+ }
+ break;
+ case 1: // tone 0: volume
+ case 3: // tone 1: volume
+ case 5: // tone 2: volume
+ case 7: // noise: volume
+ m_volume[c] = m_vol_table[data & 0x0f];
+ if ((data & 0x80) == 0) m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f);
+ break;
+ case 6: // noise: frequency, mode
+ {
+ if ((data & 0x80) == 0) logerror("sn76496_base_device: write to reg 6 with bit 7 clear; data was %03x, new write is %02x! report this to LN!\n", m_register[6], data);
+ if ((data & 0x80) == 0) m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f);
+ n = m_register[6];
+ // N/512,N/1024,N/2048,Tone #3 output
+ m_period[3] = ((n&3) == 3)? (m_period[2]<<1) : (1 << (5+(n&3)));
+ if (!(m_ncr_style_psg)) m_RNG = m_feedback_mask;
+ }
+ break;
+ }
+}
+
+WRITE8_MEMBER( sn76496_base_device::write )
+{
+ write(data);
+}
+
+inline bool sn76496_base_device::in_noise_mode()
+{
+ return ((m_register[6] & 4)!=0);
+}
+
+void sn76496_base_device::countdown_cycles()
+{
+ if (m_cycles_to_ready > 0)
+ {
+ m_cycles_to_ready--;
+ //if (m_ready_state==true) m_ready_handler(CLEAR_LINE);
+ m_ready_state = false;
+ }
+ else
+ {
+ //if (m_ready_state==false) m_ready_handler(ASSERT_LINE);
+ m_ready_state = true;
+ }
+}
+
+void sn76496_base_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i;
+ stream_sample_t *lbuffer = outputs[0];
+ stream_sample_t *rbuffer = (m_stereo)? outputs[1] : 0;//nullptr;
+
+ int16_t out;
+ int16_t out2 = 0;
+
+ while (samples > 0)
+ {
+ // clock chip once
+ if (m_current_clock > 0) // not ready for new divided clock
+ {
+ m_current_clock--;
+ }
+ else // ready for new divided clock, make a new sample
+ {
+ m_current_clock = m_clock_divider-1;
+ // decrement Cycles to READY by one
+ countdown_cycles();
+
+ // handle channels 0,1,2
+ for (i = 0; i < 3; i++)
+ {
+ m_count[i]--;
+ if (m_count[i] <= 0)
+ {
+ m_output[i] ^= 1;
+ m_count[i] = m_period[i];
+ }
+ }
+
+ // handle channel 3
+ m_count[3]--;
+ if (m_count[3] <= 0)
+ {
+ // if noisemode is 1, both taps are enabled
+ // if noisemode is 0, the lower tap, whitenoisetap2, is held at 0
+ // The != was a bit-XOR (^) before
+ if (((m_RNG & m_whitenoise_tap1)!=0) != (((m_RNG & m_whitenoise_tap2)!=(m_ncr_style_psg?m_whitenoise_tap2:0)) && in_noise_mode()))
+ {
+ m_RNG >>= 1;
+ m_RNG |= m_feedback_mask;
+ }
+ else
+ {
+ m_RNG >>= 1;
+ }
+ m_output[3] = m_RNG & 1;
+
+ m_count[3] = m_period[3];
+ }
+ }
+
+ //Skip final generation if you don't need an actual sample
+ rate_counter += rate_add;
+ if (rate_counter < RATE_MAX)
+ continue;
+ rate_counter -= RATE_MAX;
+
+ if (m_stereo)
+ {
+ out = ((((m_stereo_mask & 0x10)!=0) && (m_output[0]!=0))? m_volume[0] : 0)
+ + ((((m_stereo_mask & 0x20)!=0) && (m_output[1]!=0))? m_volume[1] : 0)
+ + ((((m_stereo_mask & 0x40)!=0) && (m_output[2]!=0))? m_volume[2] : 0)
+ + ((((m_stereo_mask & 0x80)!=0) && (m_output[3]!=0))? m_volume[3] : 0);
+
+ out2= ((((m_stereo_mask & 0x1)!=0) && (m_output[0]!=0))? m_volume[0] : 0)
+ + ((((m_stereo_mask & 0x2)!=0) && (m_output[1]!=0))? m_volume[1] : 0)
+ + ((((m_stereo_mask & 0x4)!=0) && (m_output[2]!=0))? m_volume[2] : 0)
+ + ((((m_stereo_mask & 0x8)!=0) && (m_output[3]!=0))? m_volume[3] : 0);
+ }
+ else
+ {
+ out= ((m_output[0]!=0)? m_volume[0]:0)
+ +((m_output[1]!=0)? m_volume[1]:0)
+ +((m_output[2]!=0)? m_volume[2]:0)
+ +((m_output[3]!=0)? m_volume[3]:0);
+ }
+
+ if (m_negate) { out = -out; out2 = -out2; }
+ *(lbuffer++) = out;
+ if (m_stereo) *(rbuffer++) = out2;
+ samples--;
+ }
+}
+
+
+void sn76496_base_device::convert_samplerate(int32_t target_rate) {
+ //Simple 10 bit shift for samplerate conversion
+ rate_add = (int32_t)( RATE_MAX * (target_rate / (double)sample_rate) );
+ rate_counter = 0;
+}
+
+void sn76496_base_device::register_for_save_states()
+{
+ save_item(NAME(m_vol_table));
+ save_item(NAME(m_register));
+ save_item(NAME(m_last_register));
+ save_item(NAME(m_volume));
+ save_item(NAME(m_RNG));
+// save_item(NAME(m_clock_divider));
+ save_item(NAME(m_current_clock));
+// save_item(NAME(m_feedback_mask));
+// save_item(NAME(m_whitenoise_tap1));
+// save_item(NAME(m_whitenoise_tap2));
+// save_item(NAME(m_negate));
+// save_item(NAME(m_stereo));
+ save_item(NAME(m_stereo_mask));
+ save_item(NAME(m_period));
+ save_item(NAME(m_count));
+ save_item(NAME(m_output));
+ save_item(NAME(m_cycles_to_ready));
+// save_item(NAME(m_sega_style_psg));
+}
+
+DEFINE_DEVICE_TYPE(SN76496, sn76496_device, "sn76496", "SN76496")
+DEFINE_DEVICE_TYPE(U8106, u8106_device, "u8106", "U8106")
+DEFINE_DEVICE_TYPE(Y2404, y2404_device, "y2404", "Y2404")
+DEFINE_DEVICE_TYPE(SN76489, sn76489_device, "sn76489", "SN76489")
+DEFINE_DEVICE_TYPE(SN76489A, sn76489a_device, "sn76489a", "SN76489A")
+DEFINE_DEVICE_TYPE(SN76494, sn76494_device, "sn76494", "SN76494")
+DEFINE_DEVICE_TYPE(SN94624, sn94624_device, "sn94624", "SN94624")
+DEFINE_DEVICE_TYPE(NCR8496, ncr8496_device, "ncr8496", "NCR8496")
+DEFINE_DEVICE_TYPE(PSSJ3, pssj3_device, "pssj3", "PSSJ-3")
+DEFINE_DEVICE_TYPE(GAMEGEAR, gamegear_device, "gamegear_psg", "Game Gear PSG")
+DEFINE_DEVICE_TYPE(SEGAPSG, segapsg_device, "segapsg", "Sega VDP PSG")
+
diff --git a/src/hardware/mame/sn76496.h b/src/hardware/mame/sn76496.h
new file mode 100644
index 000000000..b464ba78e
--- /dev/null
+++ b/src/hardware/mame/sn76496.h
@@ -0,0 +1,181 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria
+#ifndef MAME_SOUND_SN76496_H
+#define MAME_SOUND_SN76496_H
+
+#pragma once
+
+
+DECLARE_DEVICE_TYPE(SN76496, sn76496_device)
+DECLARE_DEVICE_TYPE(U8106, u8106_device)
+DECLARE_DEVICE_TYPE(Y2404, y2404_device)
+DECLARE_DEVICE_TYPE(SN76489, sn76489_device)
+DECLARE_DEVICE_TYPE(SN76489A, sn76489a_device)
+DECLARE_DEVICE_TYPE(SN76494, sn76494_device)
+DECLARE_DEVICE_TYPE(SN94624, sn94624_device)
+DECLARE_DEVICE_TYPE(NCR8496, ncr8496_device)
+DECLARE_DEVICE_TYPE(PSSJ3, pssj3_device)
+DECLARE_DEVICE_TYPE(GAMEGEAR, gamegear_device)
+DECLARE_DEVICE_TYPE(SEGAPSG, segapsg_device)
+
+#if 0
+
+
+#define MCFG_SN76496_READY_HANDLER(cb) \
+ downcast<sn76496_base_device &>(*device).set_ready_handler((DEVCB_##cb));
+
+#endif
+
+class sn76496_base_device : public device_t, public device_sound_interface
+{
+public:
+ // configuration helpers
+// template <class Object> devcb_base &set_ready_handler(Object &&cb) { return m_ready_handler.set_callback(std::forward<Object>(cb)); }
+
+ DECLARE_WRITE8_MEMBER( stereo_w );
+ void write(uint8_t data);
+ DECLARE_WRITE8_MEMBER( write );
+// DECLARE_READ_LINE_MEMBER( ready_r ) { return m_ready_state ? 1 : 0; }
+// auto ready_cb() { return m_ready_handler.bind(); }
+
+ void convert_samplerate(int32_t target_rate);
+protected:
+ sn76496_base_device(
+ const machine_config &mconfig,
+ device_type type,
+ const char *tag,
+ int feedbackmask,
+ int noisetap1,
+ int noisetap2,
+ bool negate,
+ bool stereo,
+ int clockdivider,
+ bool ncr,
+ bool sega,
+ device_t *owner,
+ uint32_t clock);
+
+ virtual void device_start();
+ virtual void device_clock_changed();
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ inline bool in_noise_mode();
+ void register_for_save_states();
+ void countdown_cycles();
+
+
+
+ bool m_ready_state;
+
+ //devcb_write_line m_ready_handler;
+
+ //sound_stream* m_sound;
+
+ const int32_t m_feedback_mask; // mask for feedback
+ const int32_t m_whitenoise_tap1; // mask for white noise tap 1 (higher one, usually bit 14)
+ const int32_t m_whitenoise_tap2; // mask for white noise tap 2 (lower one, usually bit 13)
+ const bool m_negate; // output negate flag
+ const bool m_stereo; // whether we're dealing with stereo or not
+ const int32_t m_clock_divider; // clock divider
+ const bool m_ncr_style_psg; // flag to ignore writes to regs 1,3,5,6,7 with bit 7 low
+ const bool m_sega_style_psg; // flag to make frequency zero acts as if it is one more than max (0x3ff+1) or if it acts like 0; the initial register is pointing to 0x3 instead of 0x0; the volume reg is preloaded with 0xF instead of 0x0
+
+ int32_t m_vol_table[16]; // volume table (for 4-bit to db conversion)
+ int32_t m_register[8]; // registers
+ int32_t m_last_register; // last register written
+ int32_t m_volume[4]; // db volume of voice 0-2 and noise
+ uint32_t m_RNG; // noise generator LFSR
+ int32_t m_current_clock;
+ int32_t m_stereo_mask; // the stereo output mask
+ int32_t m_period[4]; // Length of 1/2 of waveform
+ int32_t m_count[4]; // Position within the waveform
+ int32_t m_output[4]; // 1-bit output of each channel, pre-volume
+ int32_t m_cycles_to_ready; // number of cycles until the READY line goes active
+
+ //Modifications for easier sample conversion
+ int32_t sample_rate;
+ //Sample rate conversion
+ int32_t rate_add;
+ int32_t rate_counter;
+};
+
+// SN76496: Whitenoise verified, phase verified, periodic verified (by Michael Zapf)
+class sn76496_device : public sn76496_base_device
+{
+public:
+ sn76496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+// U8106 not verified yet. todo: verify; (a custom marked sn76489? only used on mr. do and maybe other universal games)
+class u8106_device : public sn76496_base_device
+{
+public:
+ u8106_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+// Y2404 not verified yet. todo: verify; (don't be fooled by the Y, it's a TI chip, not Yamaha)
+class y2404_device : public sn76496_base_device
+{
+public:
+ y2404_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+// NCR8496 whitenoise verified, phase verified; verified by ValleyBell & NewRisingSun
+class sn76489_device : public sn76496_base_device
+{
+public:
+ sn76489_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+// PSSJ-3 whitenoise verified, phase verified; verified by ValleyBell & NewRisingSun
+class pssj3_device : public sn76496_base_device
+{
+public:
+ pssj3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+
+// SN76489A: whitenoise verified, phase verified, periodic verified (by plgdavid)
+class sn76489a_device : public sn76496_base_device
+{
+public:
+ sn76489a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+// SN76494 not verified, (according to datasheet: same as sn76489a but without the /8 divider)
+class sn76494_device : public sn76496_base_device
+{
+public:
+ sn76494_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+// SN94624 whitenoise verified, phase verified, period verified; verified by PlgDavid
+class sn94624_device : public sn76496_base_device
+{
+public:
+ sn94624_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+// NCR8496 not verified; info from smspower wiki and vgmpf wiki
+class ncr8496_device : public sn76496_base_device
+{
+public:
+ ncr8496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+// Verified by Justin Kerk
+class gamegear_device : public sn76496_base_device
+{
+public:
+ gamegear_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+// todo: verify; from smspower wiki, assumed to have same invert as gamegear
+class segapsg_device : public sn76496_base_device
+{
+public:
+ segapsg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+#endif // MAME_SOUND_SN76496_H
diff --git a/src/hardware/mame/ymdeltat.cpp b/src/hardware/mame/ymdeltat.cpp
new file mode 100644
index 000000000..e51701385
--- /dev/null
+++ b/src/hardware/mame/ymdeltat.cpp
@@ -0,0 +1,650 @@
+// license:GPL-2.0+
+// copyright-holders:Jarek Burczynski
+/*
+**
+** File: ymdeltat.c
+**
+** YAMAHA DELTA-T adpcm sound emulation subroutine
+** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B)
+**
+** Base program is YM2610 emulator by Hiromitsu Shioya.
+** Written by Tatsuyuki Satoh
+** Improvements by Jarek Burczynski (bujar at mame dot net)
+**
+**
+** History:
+**
+** 03-08-2003 Jarek Burczynski:
+** - fixed BRDY flag implementation.
+**
+** 24-07-2003 Jarek Burczynski, Frits Hilderink:
+** - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset
+**
+** 22-07-2003 Jarek Burczynski, Frits Hilderink:
+** - fixed external memory support
+**
+** 15-06-2003 Jarek Burczynski:
+** - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08)
+** - implemented support for the Limit address register
+** - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM
+** - implemented external memory access (read/write) via the ADPCM data reg reads/writes
+** Thanks go to Frits Hilderink for the example code.
+**
+** 14-06-2003 Jarek Burczynski:
+** - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO
+** - modified EOS handling
+**
+** 05-04-2003 Jarek Burczynski:
+** - implemented partial support for external/processor memory on sample replay
+**
+** 01-12-2002 Jarek Burczynski:
+** - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi
+** - renamed/removed some YM_DELTAT struct fields
+**
+** 28-12-2001 Acho A. Tang
+** - added EOS status report on ADPCM playback.
+**
+** 05-08-2001 Jarek Burczynski:
+** - now_step is initialized with 0 at the start of play.
+**
+** 12-06-2001 Jarek Burczynski:
+** - corrected end of sample bug in YM_DELTAT_ADPCM_CALC.
+** Checked on real YM2610 chip - address register is 24 bits wide.
+** Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem.
+**
+** TO DO:
+** Check size of the address register on the other chips....
+**
+** Version 0.72
+**
+** sound chips that have this unit:
+** YM2608 OPNA
+** YM2610/B OPNB
+** Y8950 MSX AUDIO
+**
+*/
+
+#include "emu.h"
+#include "ymdeltat.h"
+
+#define YM_DELTAT_SHIFT (16)
+
+#define YM_DELTAT_DELTA_MAX (24576)
+#define YM_DELTAT_DELTA_MIN (127)
+#define YM_DELTAT_DELTA_DEF (127)
+
+#define YM_DELTAT_DECODE_RANGE 32768
+#define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE))
+#define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1)
+
+
+/* Forecast to next Forecast (rate = *8) */
+/* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */
+static const int32_t ym_deltat_decode_tableB1[16] = {
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ -1, -3, -5, -7, -9, -11, -13, -15,
+};
+/* delta to next delta (rate= *64) */
+/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */
+static const int32_t ym_deltat_decode_tableB2[16] = {
+ 57, 57, 57, 57, 77, 102, 128, 153,
+ 57, 57, 57, 57, 77, 102, 128, 153
+};
+
+#if 0
+void YM_DELTAT::BRDY_callback()
+{
+ logerror("BRDY_callback reached (flag set) !\n");
+
+ /* set BRDY bit in status register */
+ if(status_set_handler)
+ if(status_change_BRDY_bit)
+ (status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
+}
+#endif
+
+uint8_t YM_DELTAT::ADPCM_Read()
+{
+ uint8_t v = 0;
+
+ /* external memory read */
+ if ((portstate & 0xe0) == 0x20)
+ {
+ /* two dummy reads */
+ if (memread)
+ {
+ now_addr = start << 1;
+ memread--;
+ return 0;
+ }
+
+
+ if (now_addr != (end << 1))
+ {
+ v = memory[now_addr>>1];
+
+ /*logerror("YM Delta-T memory read $%08x, v=$%02x\n", now_addr >> 1, v);*/
+
+ now_addr += 2; /* two nibbles at a time */
+
+ /* reset BRDY bit in status register, which means we are reading the memory now */
+ if (status_reset_handler && status_change_BRDY_bit)
+ (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit);
+
+ /* setup a timer that will callback us in 10 master clock cycles for Y8950
+ * in the callback set the BRDY flag to 1 , which means we have another data ready.
+ * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work.
+ */
+ /* set BRDY bit in status register */
+ if (status_set_handler && status_change_BRDY_bit)
+ (status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
+ }
+ else
+ {
+ /* set EOS bit in status register */
+ if (status_set_handler && status_change_EOS_bit)
+ (status_set_handler)(status_change_which_chip, status_change_EOS_bit);
+ }
+ }
+
+ return v;
+}
+
+
+/* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */
+static const uint8_t dram_rightshift[4]={3,0,0,0};
+
+/* DELTA-T ADPCM write register */
+void YM_DELTAT::ADPCM_Write(int r, int v)
+{
+ if (r >= 0x10) return;
+ reg[r] = v; /* stock data */
+
+ switch (r)
+ {
+ case 0x00:
+/*
+START:
+ Accessing *external* memory is started when START bit (D7) is set to "1", so
+ you must set all conditions needed for recording/playback before starting.
+ If you access *CPU-managed* memory, recording/playback starts after
+ read/write of ADPCM data register $08.
+
+REC:
+ 0 = ADPCM synthesis (playback)
+ 1 = ADPCM analysis (record)
+
+MEMDATA:
+ 0 = processor (*CPU-managed*) memory (means: using register $08)
+ 1 = external memory (using start/end/limit registers to access memory: RAM or ROM)
+
+
+SPOFF:
+ controls output pin that should disable the speaker while ADPCM analysis
+
+RESET and REPEAT only work with external memory.
+
+
+some examples:
+value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning:
+ C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register
+ E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register
+ 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register
+ a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register
+
+ 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08
+ 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08
+
+*/
+ /* handle emulation mode */
+ if (emulation_mode == EMULATION_MODE_YM2610)
+ {
+ v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */
+ }
+
+ portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */
+
+ if (portstate & 0x80)/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */
+ {
+ /* set PCM BUSY bit */
+ PCM_BSY = 1;
+
+ /* start ADPCM */
+ now_step = 0;
+ acc = 0;
+ prev_acc = 0;
+ adpcml = 0;
+ adpcmd = YM_DELTAT_DELTA_DEF;
+ now_data = 0;
+
+ }
+
+ if (portstate & 0x20) /* do we access external memory? */
+ {
+ now_addr = start << 1;
+ memread = 2; /* two dummy reads needed before accesing external memory via register $08*/
+
+ /* if yes, then let's check if ADPCM memory is mapped and big enough */
+ if (!memory)
+ {
+ device->logerror("YM Delta-T ADPCM rom not mapped\n");
+ portstate = 0x00;
+ PCM_BSY = 0;
+ }
+ else
+ {
+ if (end >= memory_size) /* Check End in Range */
+ {
+ device->logerror("YM Delta-T ADPCM end out of range: $%08x\n", end);
+ end = memory_size - 1;
+ }
+ if (start >= memory_size) /* Check Start in Range */
+ {
+ device->logerror("YM Delta-T ADPCM start out of range: $%08x\n", start);
+ portstate = 0x00;
+ PCM_BSY = 0;
+ }
+ }
+ }
+ else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */
+ {
+ now_addr = 0;
+ }
+
+ if (portstate & 0x01)
+ {
+ portstate = 0x00;
+
+ /* clear PCM BUSY bit (in status register) */
+ PCM_BSY = 0;
+
+ /* set BRDY flag */
+ if (status_set_handler && status_change_BRDY_bit)
+ (status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
+ }
+ break;
+
+ case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */
+ /* handle emulation mode */
+ if (emulation_mode == EMULATION_MODE_YM2610)
+ {
+ v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't tave ROM/RAM memory flag bit. */
+ }
+
+ pan = &output_pointer[(v >> 6) & 0x03];
+ if ((control2 & 3) != (v & 3))
+ {
+ /*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */
+ if (DRAMportshift != dram_rightshift[v & 3])
+ {
+ DRAMportshift = dram_rightshift[v & 3];
+
+ /* final shift value depends on chip type and memory type selected:
+ 8 for YM2610 (ROM only),
+ 5 for ROM for Y8950 and YM2608,
+ 5 for x8bit DRAMs for Y8950 and YM2608,
+ 2 for x1bit DRAMs for Y8950 and YM2608.
+ */
+
+ /* refresh addresses */
+ start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift);
+ end = (reg[0x5] * 0x0100 | reg[0x4]) << (portshift - DRAMportshift);
+ end += (1 << (portshift - DRAMportshift)) - 1;
+ limit = (reg[0xd]*0x0100 | reg[0xc]) << (portshift - DRAMportshift);
+ }
+ }
+ control2 = v;
+ break;
+
+ case 0x02: /* Start Address L */
+ case 0x03: /* Start Address H */
+ start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift);
+ /*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",reg[0x2], reg[0x3],start );*/
+ break;
+
+ case 0x04: /* Stop Address L */
+ case 0x05: /* Stop Address H */
+ end = (reg[0x5]*0x0100 | reg[0x4]) << (portshift - DRAMportshift);
+ end += (1 << (portshift - DRAMportshift)) - 1;
+ /*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",reg[0x4], reg[0x5],end );*/
+ break;
+
+ case 0x06: /* Prescale L (ADPCM and Record frq) */
+ case 0x07: /* Prescale H */
+ break;
+
+ case 0x08: /* ADPCM data */
+/*
+some examples:
+value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning:
+ C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register
+ E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register
+ 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register
+ a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register
+
+ 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08
+ 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08
+
+*/
+
+ /* external memory write */
+ if ((portstate & 0xe0) == 0x60)
+ {
+ if (memread)
+ {
+ now_addr = start << 1;
+ memread = 0;
+ }
+
+ /*logerror("YM Delta-T memory write $%08x, v=$%02x\n", now_addr >> 1, v);*/
+
+ if (now_addr != (end << 1))
+ {
+ memory[now_addr >> 1] = v;
+ now_addr += 2; /* two nybbles at a time */
+
+ /* reset BRDY bit in status register, which means we are processing the write */
+ if (status_reset_handler && status_change_BRDY_bit)
+ (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit);
+
+ /* setup a timer that will callback us in 10 master clock cycles for Y8950
+ * in the callback set the BRDY flag to 1 , which means we have written the data.
+ * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work.
+ */
+ /* set BRDY bit in status register */
+ if (status_set_handler && status_change_BRDY_bit)
+ (status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
+
+ }
+ else
+ {
+ /* set EOS bit in status register */
+ if (status_set_handler && status_change_EOS_bit)
+ (status_set_handler)(status_change_which_chip, status_change_EOS_bit);
+ }
+
+ return;
+ }
+
+ /* ADPCM synthesis from CPU */
+ if ((portstate & 0xe0) == 0x80)
+ {
+ CPU_data = v;
+
+ /* Reset BRDY bit in status register, which means we are full of data */
+ if (status_reset_handler && status_change_BRDY_bit)
+ (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit);
+ return;
+ }
+
+ break;
+
+ case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */
+ case 0x0a: /* DELTA-N H */
+ delta = (reg[0xa] * 0x0100 | reg[0x9]);
+ step = uint32_t(double(delta /* *(1<<(YM_DELTAT_SHIFT-16)) */) * freqbase);
+ /*logerror("DELTAT deltan:09=%2x 0a=%2x\n",reg[0x9], reg[0xa]);*/
+ break;
+
+ case 0x0b: /* Output level control (volume, linear) */
+ {
+ const int32_t oldvol = volume;
+ volume = (v & 0xff) * (output_range / 256) / YM_DELTAT_DECODE_RANGE;
+/* v * ((1<<16)>>8) >> 15;
+* thus: v * (1<<8) >> 15;
+* thus: output_range must be (1 << (15+8)) at least
+* v * ((1<<23)>>8) >> 15;
+* v * (1<<15) >> 15;
+*/
+ /*logerror("DELTAT vol = %2x\n",v&0xff);*/
+ if (oldvol != 0)
+ {
+ adpcml = int(double(adpcml) / double(oldvol) * double(volume));
+ }
+ }
+ break;
+
+ case 0x0c: /* Limit Address L */
+ case 0x0d: /* Limit Address H */
+ limit = (reg[0xd] * 0x0100 | reg[0xc]) << (portshift - DRAMportshift);
+ /*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",reg[0xc], reg[0xd],limit );*/
+ break;
+ }
+}
+
+void YM_DELTAT::ADPCM_Reset(int panidx, int mode, device_t *dev)
+{
+ device = dev;
+ now_addr = 0;
+ now_step = 0;
+ step = 0;
+ start = 0;
+ end = 0;
+ limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */
+ volume = 0;
+ pan = &output_pointer[panidx];
+ acc = 0;
+ prev_acc = 0;
+ adpcmd = 127;
+ adpcml = 0;
+ emulation_mode = uint8_t(mode);
+ portstate = (emulation_mode == EMULATION_MODE_YM2610) ? 0x20 : 0;
+ control2 = (emulation_mode == EMULATION_MODE_YM2610) ? 0x01 : 0; /* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */
+ DRAMportshift = dram_rightshift[control2 & 3];
+
+ /* The flag mask register disables the BRDY after the reset, however
+ ** as soon as the mask is enabled the flag needs to be set. */
+
+ /* set BRDY bit in status register */
+ if (status_set_handler && status_change_BRDY_bit)
+ (status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
+}
+
+void YM_DELTAT::postload(uint8_t *regs)
+{
+ /* to keep adpcml */
+ volume = 0;
+ /* update */
+ for (int r = 1; r < 16; r++)
+ ADPCM_Write(r, regs[r]);
+ reg[0] = regs[0];
+
+ /* current rom data */
+ if (memory)
+ now_data = *(memory + (now_addr >> 1));
+
+}
+void YM_DELTAT::savestate(device_t *device)
+{
+#ifdef MAME_EMU_SAVE_H
+ YM_DELTAT *const DELTAT = this; // makes the save name sensible
+ device->save_item(NAME(DELTAT->portstate));
+ device->save_item(NAME(DELTAT->now_addr));
+ device->save_item(NAME(DELTAT->now_step));
+ device->save_item(NAME(DELTAT->acc));
+ device->save_item(NAME(DELTAT->prev_acc));
+ device->save_item(NAME(DELTAT->adpcmd));
+ device->save_item(NAME(DELTAT->adpcml));
+#endif
+}
+
+
+#define YM_DELTAT_Limit(val,max,min) \
+{ \
+ if ( val > max ) val = max; \
+ else if ( val < min ) val = min; \
+}
+
+static inline void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT)
+{
+ uint32_t step;
+ int data;
+
+ DELTAT->now_step += DELTAT->step;
+ if ( DELTAT->now_step >= (1<<YM_DELTAT_SHIFT) )
+ {
+ step = DELTAT->now_step >> YM_DELTAT_SHIFT;
+ DELTAT->now_step &= (1<<YM_DELTAT_SHIFT)-1;
+ do{
+ if ( DELTAT->now_addr == (DELTAT->limit<<1) )
+ DELTAT->now_addr = 0;
+
+ if ( DELTAT->now_addr == (DELTAT->end<<1) ) { /* 12-06-2001 JB: corrected comparison. Was > instead of == */
+ if( DELTAT->portstate&0x10 ){
+ /* repeat start */
+ DELTAT->now_addr = DELTAT->start<<1;
+ DELTAT->acc = 0;
+ DELTAT->adpcmd = YM_DELTAT_DELTA_DEF;
+ DELTAT->prev_acc = 0;
+ }else{
+ /* set EOS bit in status register */
+ if(DELTAT->status_set_handler)
+ if(DELTAT->status_change_EOS_bit)
+ (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit);
+
+ /* clear PCM BUSY bit (reflected in status register) */
+ DELTAT->PCM_BSY = 0;
+
+ DELTAT->portstate = 0;
+ DELTAT->adpcml = 0;
+ DELTAT->prev_acc = 0;
+ return;
+ }
+ }
+
+ if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f;
+ else
+ {
+ DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1));
+ data = DELTAT->now_data >> 4;
+ }
+
+ DELTAT->now_addr++;
+ /* 12-06-2001 JB: */
+ /* YM2610 address register is 24 bits wide.*/
+ /* The "+1" is there because we use 1 bit more for nibble calculations.*/
+ /* WARNING: */
+ /* Side effect: we should take the size of the mapped ROM into account */
+ DELTAT->now_addr &= ( (1<<(24+1))-1);
+
+ /* store accumulator value */
+ DELTAT->prev_acc = DELTAT->acc;
+
+ /* Forecast to next Forecast */
+ DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8);
+ YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN);
+
+ /* delta to next delta */
+ DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64;
+ YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN );
+
+ /* ElSemi: Fix interpolator. */
+ /*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/
+
+ }while(--step);
+
+ }
+
+ /* ElSemi: Fix interpolator. */
+ DELTAT->adpcml = DELTAT->prev_acc * (int)((1<<YM_DELTAT_SHIFT)-DELTAT->now_step);
+ DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step);
+ DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume;
+
+ /* output for work of output channels (outd[OPNxxxx])*/
+ *(DELTAT->pan) += DELTAT->adpcml;
+}
+
+
+
+static inline void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT)
+{
+ uint32_t step;
+ int data;
+
+ DELTAT->now_step += DELTAT->step;
+ if ( DELTAT->now_step >= (1<<YM_DELTAT_SHIFT) )
+ {
+ step = DELTAT->now_step >> YM_DELTAT_SHIFT;
+ DELTAT->now_step &= (1<<YM_DELTAT_SHIFT)-1;
+ do{
+ if( DELTAT->now_addr&1 )
+ {
+ data = DELTAT->now_data & 0x0f;
+
+ DELTAT->now_data = DELTAT->CPU_data;
+
+ /* after we used CPU_data, we set BRDY bit in status register,
+ * which means we are ready to accept another byte of data */
+ if(DELTAT->status_set_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
+ }
+ else
+ {
+ data = DELTAT->now_data >> 4;
+ }
+
+ DELTAT->now_addr++;
+
+ /* store accumulator value */
+ DELTAT->prev_acc = DELTAT->acc;
+
+ /* Forecast to next Forecast */
+ DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8);
+ YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN);
+
+ /* delta to next delta */
+ DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64;
+ YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN );
+
+
+ }while(--step);
+
+ }
+
+ /* ElSemi: Fix interpolator. */
+ DELTAT->adpcml = DELTAT->prev_acc * (int)((1<<YM_DELTAT_SHIFT)-DELTAT->now_step);
+ DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step);
+ DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume;
+
+ /* output for work of output channels (outd[OPNxxxx])*/
+ *(DELTAT->pan) += DELTAT->adpcml;
+}
+
+
+
+/* ADPCM B (Delta-T control type) */
+void YM_DELTAT::ADPCM_CALC()
+{
+/*
+some examples:
+value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning:
+ 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register
+ a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register
+ C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register
+ E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register
+
+ 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08
+ 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08
+
+*/
+
+ if ( (portstate & 0xe0)==0xa0 )
+ {
+ YM_DELTAT_synthesis_from_external_memory(this);
+ return;
+ }
+
+ if ( (portstate & 0xe0)==0x80 )
+ {
+ /* ADPCM synthesis from CPU-managed memory (from reg $08) */
+ YM_DELTAT_synthesis_from_CPU_memory(this); /* change output based on data in ADPCM data reg ($08) */
+ return;
+ }
+
+//todo: ADPCM analysis
+// if ( (portstate & 0xe0)==0xc0 )
+// if ( (portstate & 0xe0)==0xe0 )
+
+ return;
+}
diff --git a/src/hardware/mame/ymdeltat.h b/src/hardware/mame/ymdeltat.h
new file mode 100644
index 000000000..ad9e92ee2
--- /dev/null
+++ b/src/hardware/mame/ymdeltat.h
@@ -0,0 +1,87 @@
+// license:GPL-2.0+
+// copyright-holders:Jarek Burczynski
+#ifndef MAME_SOUND_YMDELTAT_H
+#define MAME_SOUND_YMDELTAT_H
+
+#pragma once
+
+
+typedef void (*STATUS_CHANGE_HANDLER)(void *chip, uint8_t status_bits);
+
+
+/* DELTA-T (adpcm type B) struct */
+struct YM_DELTAT { /* AT: rearranged and tightened structure */
+ enum {
+ EMULATION_MODE_NORMAL = 0,
+ EMULATION_MODE_YM2610 = 1,
+ };
+
+ uint8_t *memory;
+ int32_t *output_pointer;/* pointer of output pointers */
+ int32_t *pan; /* pan : &output_pointer[pan] */
+ double freqbase;
+#if 0
+ double write_time; /* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */
+ double read_time; /* Y8950: 8 cycles of main clock; YM2608: 18 cycles of main clock */
+#endif
+ uint32_t memory_size;
+ int output_range;
+ uint32_t now_addr; /* current address */
+ uint32_t now_step; /* correct step */
+ uint32_t step; /* step */
+ uint32_t start; /* start address */
+ uint32_t limit; /* limit address */
+ uint32_t end; /* end address */
+ uint32_t delta; /* delta scale */
+ int32_t volume; /* current volume */
+ int32_t acc; /* shift Measurement value*/
+ int32_t adpcmd; /* next Forecast */
+ int32_t adpcml; /* current value */
+ int32_t prev_acc; /* leveling value */
+ uint8_t now_data; /* current rom data */
+ uint8_t CPU_data; /* current data from reg 08 */
+ uint8_t portstate; /* port status */
+ uint8_t control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */
+ uint8_t portshift; /* address bits shift-left:
+ ** 8 for YM2610,
+ ** 5 for Y8950 and YM2608 */
+
+ uint8_t DRAMportshift; /* address bits shift-right:
+ ** 0 for ROM and x8bit DRAMs,
+ ** 3 for x1 DRAMs */
+
+ uint8_t memread; /* needed for reading/writing external memory */
+
+ /* handlers and parameters for the status flags support */
+ STATUS_CHANGE_HANDLER status_set_handler;
+ STATUS_CHANGE_HANDLER status_reset_handler;
+
+ /* note that different chips have these flags on different
+ ** bits of the status register
+ */
+ void * status_change_which_chip; /* this chip id */
+ uint8_t status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/
+ uint8_t status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */
+ uint8_t status_change_ZERO_bit; /* 1 if silence lasts for more than 290 milliseconds on ADPCM recording */
+
+ /* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above,
+ ** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608)
+ */
+ uint8_t PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */
+
+ uint8_t reg[16]; /* adpcm registers */
+ uint8_t emulation_mode; /* which chip we're emulating */
+ device_t *device;
+
+ /*void BRDY_callback();*/
+
+ uint8_t ADPCM_Read();
+ void ADPCM_Write(int r, int v);
+ void ADPCM_Reset(int panidx, int mode, device_t *dev);
+ void ADPCM_CALC();
+
+ void postload(uint8_t *regs);
+ void savestate(device_t *device);
+};
+
+#endif // MAME_SOUND_YMDELTAT_H
diff --git a/src/hardware/mame/ymf262.cpp b/src/hardware/mame/ymf262.cpp
new file mode 100644
index 000000000..98056c5d5
--- /dev/null
+++ b/src/hardware/mame/ymf262.cpp
@@ -0,0 +1,2792 @@
+// license:GPL-2.0+
+// copyright-holders:Jarek Burczynski
+/*
+**
+** File: ymf262.c - software implementation of YMF262
+** FM sound generator type OPL3
+**
+** Copyright Jarek Burczynski
+**
+** Version 0.2
+**
+
+Revision History:
+
+03-03-2003: initial release
+ - thanks to Olivier Galibert and Chris Hardy for YMF262 and YAC512 chips
+ - thanks to Stiletto for the datasheets
+
+ Features as listed in 4MF262A6 data sheet:
+ 1. Registers are compatible with YM3812 (OPL2) FM sound source.
+ 2. Up to six sounds can be used as four-operator melody sounds for variety.
+ 3. 18 simultaneous melody sounds, or 15 melody sounds with 5 rhythm sounds (with two operators).
+ 4. 6 four-operator melody sounds and 6 two-operator melody sounds, or 6 four-operator melody
+ sounds, 3 two-operator melody sounds and 5 rhythm sounds (with four operators).
+ 5. 8 selectable waveforms.
+ 6. 4-channel sound output.
+ 7. YMF262 compabile DAC (YAC512) is available.
+ 8. LFO for vibrato and tremolo effedts.
+ 9. 2 programable timers.
+ 10. Shorter register access time compared with YM3812.
+ 11. 5V single supply silicon gate CMOS process.
+ 12. 24 Pin SOP Package (YMF262-M), 48 Pin SQFP Package (YMF262-S).
+
+
+differences between OPL2 and OPL3 not documented in Yamaha datahasheets:
+- sinus table is a little different: the negative part is off by one...
+
+- in order to enable selection of four different waveforms on OPL2
+ one must set bit 5 in register 0x01(test).
+ on OPL3 this bit is ignored and 4-waveform select works *always*.
+ (Don't confuse this with OPL3's 8-waveform select.)
+
+- Envelope Generator: all 15 x rates take zero time on OPL3
+ (on OPL2 15 0 and 15 1 rates take some time while 15 2 and 15 3 rates
+ take zero time)
+
+- channel calculations: output of operator 1 is in perfect sync with
+ output of operator 2 on OPL3; on OPL and OPL2 output of operator 1
+ is always delayed by one sample compared to output of operator 2
+
+
+differences between OPL2 and OPL3 shown in datasheets:
+- YMF262 does not support CSM mode
+
+
+*/
+
+#include "emu.h"
+#include "ymf262.h"
+
+
+/* output final shift */
+#if (OPL3_SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (EG timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+/* envelope output entries */
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX ((1<<(ENV_BITS-1))-1) /*511*/
+#define MIN_ATT_INDEX (0)
+
+/* sinwave entries */
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* Envelope Generator phases */
+
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+/* Routing connections between slots */
+#define CONN_NULL 0
+#define CONN_CHAN0 1
+#define CONN_PHASEMOD 19
+#define CONN_PHASEMOD2 20
+
+namespace {
+
+/* save output as raw 16-bit sample */
+
+/*#define SAVE_SAMPLE*/
+
+#ifdef SAVE_SAMPLE
+static FILE *sample[1];
+ #if 1 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = a; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = a; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = b; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+
+#define OPL3_TYPE_YMF262 (0) /* 36 operators, 8 waveforms */
+
+
+struct OPL3_SLOT
+{
+ uint32_t ar; /* attack rate: AR<<2 */
+ uint32_t dr; /* decay rate: DR<<2 */
+ uint32_t rr; /* release rate:RR<<2 */
+ uint8_t KSR; /* key scale rate */
+ uint8_t ksl; /* keyscale level */
+ uint8_t ksr; /* key scale rate: kcode>>KSR */
+ uint8_t mul; /* multiple: mul_tab[ML] */
+
+ /* Phase Generator */
+ uint32_t Cnt; /* frequency counter */
+ uint32_t Incr; /* frequency counter step */
+ uint8_t FB; /* feedback shift value */
+ uint8_t conn_enum; /* slot output route */
+ int32_t *connect; /* slot output pointer */
+ int32_t op1_out[2]; /* slot1 output for feedback */
+ uint8_t CON; /* connection (algorithm) type */
+
+ /* Envelope Generator */
+ uint8_t eg_type; /* percussive/non-percussive mode */
+ uint8_t state; /* phase type */
+ uint32_t TL; /* total level: TL << 2 */
+ int32_t TLL; /* adjusted now TL */
+ int32_t volume; /* envelope counter */
+ uint32_t sl; /* sustain level: sl_tab[SL] */
+
+ uint32_t eg_m_ar; /* (attack state) */
+ uint8_t eg_sh_ar; /* (attack state) */
+ uint8_t eg_sel_ar; /* (attack state) */
+ uint32_t eg_m_dr; /* (decay state) */
+ uint8_t eg_sh_dr; /* (decay state) */
+ uint8_t eg_sel_dr; /* (decay state) */
+ uint32_t eg_m_rr; /* (release state) */
+ uint8_t eg_sh_rr; /* (release state) */
+ uint8_t eg_sel_rr; /* (release state) */
+
+ uint32_t key; /* 0 = KEY OFF, >0 = KEY ON */
+
+ /* LFO */
+ uint32_t AMmask; /* LFO Amplitude Modulation enable mask */
+ uint8_t vib; /* LFO Phase Modulation enable flag (active high)*/
+
+ /* waveform select */
+ uint8_t waveform_number;
+ unsigned int wavetable;
+
+ //unsigned char reserved[128-84];//speedup: pump up the struct size to power of 2
+ unsigned char reserved[128-100];//speedup: pump up the struct size to power of 2
+
+};
+
+struct OPL3_CH
+{
+ OPL3_SLOT SLOT[2];
+
+ uint32_t block_fnum; /* block+fnum */
+ uint32_t fc; /* Freq. Increment base */
+ uint32_t ksl_base; /* KeyScaleLevel Base step */
+ uint8_t kcode; /* key code (for key scaling) */
+
+ /*
+ there are 12 2-operator channels which can be combined in pairs
+ to form six 4-operator channel, they are:
+ 0 and 3,
+ 1 and 4,
+ 2 and 5,
+ 9 and 12,
+ 10 and 13,
+ 11 and 14
+ */
+ uint8_t extended; /* set to 1 if this channel forms up a 4op channel with another channel(only used by first of pair of channels, ie 0,1,2 and 9,10,11) */
+
+ unsigned char reserved[512-272];//speedup:pump up the struct size to power of 2
+
+};
+
+/* OPL3 state */
+struct OPL3
+{
+ OPL3_CH P_CH[18]; /* OPL3 chips have 18 channels */
+
+ uint32_t pan[18*4]; /* channels output masks (0xffffffff = enable); 4 masks per one channel */
+ uint32_t pan_ctrl_value[18]; /* output control values 1 per one channel (1 value contains 4 masks) */
+
+ signed int chanout[18];
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+ signed int phase_modulation2; /* phase modulation input (SLOT 3 in 4 operator channels) */
+
+ uint32_t eg_cnt; /* global envelope generator counter */
+ uint32_t eg_timer; /* global envelope generator counter works at frequency = chipclock/288 (288=8*36) */
+ uint32_t eg_timer_add; /* step of eg_timer */
+ uint32_t eg_timer_overflow; /* envelope generator timer overflows every 1 sample (on real chip) */
+
+ uint32_t fn_tab[1024]; /* fnumber->increment counter */
+
+ /* LFO */
+ uint32_t LFO_AM;
+ int32_t LFO_PM;
+
+ uint8_t lfo_am_depth;
+ uint8_t lfo_pm_depth_range;
+ uint32_t lfo_am_cnt;
+ uint32_t lfo_am_inc;
+ uint32_t lfo_pm_cnt;
+ uint32_t lfo_pm_inc;
+
+ uint32_t noise_rng; /* 23 bit noise shift register */
+ uint32_t noise_p; /* current noise 'phase' */
+ uint32_t noise_f; /* current noise period */
+
+ uint8_t OPL3_mode; /* OPL3 extension enable flag */
+
+ uint8_t rhythm; /* Rhythm mode */
+
+ int T[2]; /* timer counters */
+ uint8_t st[2]; /* timer enable */
+
+ uint32_t address; /* address register */
+ uint8_t status; /* status flag */
+ uint8_t statusmask; /* status mask */
+
+ uint8_t nts; /* NTS (note select) */
+
+ /* external event callback handlers */
+ OPL3_TIMERHANDLER timer_handler;
+ device_t *TimerParam;
+ OPL3_IRQHANDLER IRQHandler;
+ device_t *IRQParam;
+ OPL3_UPDATEHANDLER UpdateHandler;
+ device_t *UpdateParam;
+
+ uint8_t type; /* chip type */
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ //attotime TimerBase; /* Timer base time (==sampling time)*/
+ device_t *device;
+
+ /* Optional handlers */
+ void SetTimerHandler(OPL3_TIMERHANDLER handler, device_t *device)
+ {
+ timer_handler = handler;
+ TimerParam = device;
+ }
+ void SetIRQHandler(OPL3_IRQHANDLER handler, device_t *device)
+ {
+ IRQHandler = handler;
+ IRQParam = device;
+ }
+ void SetUpdateHandler(OPL3_UPDATEHANDLER handler, device_t *device)
+ {
+ UpdateHandler = handler;
+ UpdateParam = device;
+ }
+};
+
+} // anonymous namespace
+
+
+
+/* mapping of register number (offset) to slot number used by the emulator */
+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/octave , DV converts this into 6dB/octave */
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */
+#define DV (0.1875/2.0)
+static const double ksl_tab[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
+
+/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */
+static const uint32_t ksl_shift[4] = { 31, 1, 2, 0 };
+
+
+/* sustain level 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) (uint32_t) ( db * (2.0/ENV_STEP) )
+static const uint32_t sl_tab[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 RATE_STEPS (8)
+static const unsigned char eg_inc[15*RATE_STEPS]={
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 for decay */
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) */
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/* note that there is no O(13) in this table - it's directly in the code */
+static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+
+/* rates 00-12 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 13 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 14 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 15 */
+O(12),O(12),O(12),O(12),
+
+/* 16 dummy rates (same as 15 3) */
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */
+/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */
+/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-12 */
+O(12),O(12),O(12),O(12),
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 16 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+
+};
+#undef O
+
+
+/* multiple table */
+#define ML 2
+static const uint8_t mul_tab[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */
+ ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML,
+ 8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML
+};
+#undef ML
+
+/* TL_TAB_LEN is calculated as:
+
+* (12+1)=13 - sinus amplitude bits (Y axis)
+* additional 1: to compensate for calculations of negative part of waveform
+* (if we don't add it then the greatest possible _negative_ value would be -2
+* and we really need -1 for waveform #7)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (13*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>4)
+
+/* sin waveform table in 'decibel' scale */
+/* there are eight waveforms on OPL3 chips */
+static unsigned int sin_tab[SIN_LEN * 8];
+
+
+/* LFO Amplitude Modulation table (verified on real YM3812)
+ 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples
+
+ Length: 210 elements.
+
+ Each of the elements has to be repeated
+ exactly 64 times (on 64 consecutive samples).
+ The whole table takes: 64 * 210 = 13440 samples.
+
+ When AM = 1 data is used directly
+ When AM = 0 data is divided by 4 before being used (losing precision is important)
+*/
+
+#define LFO_AM_TAB_ELEMENTS 210
+
+static const uint8_t lfo_am_table[LFO_AM_TAB_ELEMENTS] = {
+0,0,0,0,0,0,0,
+1,1,1,1,
+2,2,2,2,
+3,3,3,3,
+4,4,4,4,
+5,5,5,5,
+6,6,6,6,
+7,7,7,7,
+8,8,8,8,
+9,9,9,9,
+10,10,10,10,
+11,11,11,11,
+12,12,12,12,
+13,13,13,13,
+14,14,14,14,
+15,15,15,15,
+16,16,16,16,
+17,17,17,17,
+18,18,18,18,
+19,19,19,19,
+20,20,20,20,
+21,21,21,21,
+22,22,22,22,
+23,23,23,23,
+24,24,24,24,
+25,25,25,25,
+26,26,26,
+25,25,25,25,
+24,24,24,24,
+23,23,23,23,
+22,22,22,22,
+21,21,21,21,
+20,20,20,20,
+19,19,19,19,
+18,18,18,18,
+17,17,17,17,
+16,16,16,16,
+15,15,15,15,
+14,14,14,14,
+13,13,13,13,
+12,12,12,12,
+11,11,11,11,
+10,10,10,10,
+9,9,9,9,
+8,8,8,8,
+7,7,7,7,
+6,6,6,6,
+5,5,5,5,
+4,4,4,4,
+3,3,3,3,
+2,2,2,2,
+1,1,1,1
+};
+
+/* LFO Phase Modulation table (verified on real YM3812) */
+static const int8_t lfo_pm_table[8*8*2] = {
+/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/
+};
+
+
+/* lock level of common table */
+static int num_lock = 0;
+
+/* work table */
+#define SLOT7_1 (&chip->P_CH[7].SLOT[SLOT1])
+#define SLOT7_2 (&chip->P_CH[7].SLOT[SLOT2])
+#define SLOT8_1 (&chip->P_CH[8].SLOT[SLOT1])
+#define SLOT8_2 (&chip->P_CH[8].SLOT[SLOT2])
+
+
+static inline void OPL3_SLOT_CONNECT(OPL3 *chip, OPL3_SLOT *slot) {
+ if (slot->conn_enum == CONN_NULL) {
+ slot->connect = 0;
+ } else if (slot->conn_enum >= CONN_CHAN0 && slot->conn_enum < CONN_PHASEMOD) {
+ slot->connect = &chip->chanout[slot->conn_enum];
+ } else if (slot->conn_enum == CONN_PHASEMOD) {
+ slot->connect = &chip->phase_modulation;
+ } else if (slot->conn_enum == CONN_PHASEMOD2) {
+ slot->connect = &chip->phase_modulation2;
+ }
+}
+
+static 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 */
+static inline void OPL3_STATUS_SET(OPL3 *chip,int flag)
+{
+ /* set status flag masking out disabled IRQs */
+ chip->status |= (flag & chip->statusmask);
+ if(!(chip->status & 0x80))
+ {
+ if(chip->status & 0x7f)
+ { /* IRQ on */
+ chip->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+static inline void OPL3_STATUS_RESET(OPL3 *chip,int flag)
+{
+ /* reset status flag */
+ chip->status &= ~flag;
+ if(chip->status & 0x80)
+ {
+ if (!(chip->status & 0x7f))
+ {
+ chip->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+static inline void OPL3_STATUSMASK_SET(OPL3 *chip,int flag)
+{
+ chip->statusmask = flag;
+ /* IRQ handling check */
+ OPL3_STATUS_SET(chip,0);
+ OPL3_STATUS_RESET(chip,0);
+}
+
+
+/* advance LFO to next sample */
+static inline void advance_lfo(OPL3 *chip)
+{
+ uint8_t tmp;
+
+ /* LFO */
+ chip->lfo_am_cnt += chip->lfo_am_inc;
+ if (chip->lfo_am_cnt >= ((uint32_t)LFO_AM_TAB_ELEMENTS<<LFO_SH) ) /* lfo_am_table is 210 elements long */
+ chip->lfo_am_cnt -= ((uint32_t)LFO_AM_TAB_ELEMENTS<<LFO_SH);
+
+ tmp = lfo_am_table[ chip->lfo_am_cnt >> LFO_SH ];
+
+ if (chip->lfo_am_depth)
+ chip->LFO_AM = tmp;
+ else
+ chip->LFO_AM = tmp>>2;
+
+ chip->lfo_pm_cnt += chip->lfo_pm_inc;
+ chip->LFO_PM = ((chip->lfo_pm_cnt>>LFO_SH) & 7) | chip->lfo_pm_depth_range;
+}
+
+/* advance to next sample */
+static inline void advance(OPL3 *chip)
+{
+ OPL3_CH *CH;
+ OPL3_SLOT *op;
+ int i;
+
+ chip->eg_timer += chip->eg_timer_add;
+
+ while (chip->eg_timer >= chip->eg_timer_overflow)
+ {
+ chip->eg_timer -= chip->eg_timer_overflow;
+
+ chip->eg_cnt++;
+
+ for (i=0; i<9*2*2; i++)
+ {
+ CH = &chip->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+#if 1
+ /* Envelope Generator */
+ switch(op->state)
+ {
+ case EG_ATT: /* attack phase */
+// if ( !(chip->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )
+ if ( !(chip->eg_cnt & op->eg_m_ar) )
+ {
+ op->volume += (~op->volume *
+ (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)])
+ ) >>3;
+
+ if (op->volume <= MIN_ATT_INDEX)
+ {
+ op->volume = MIN_ATT_INDEX;
+ op->state = EG_DEC;
+ }
+
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+// if ( !(chip->eg_cnt & ((1<<op->eg_sh_dr)-1) ) )
+ if ( !(chip->eg_cnt & op->eg_m_dr) )
+ {
+ op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)];
+
+ if ( op->volume >= op->sl )
+ op->state = EG_SUS;
+
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+
+ /* this is important behaviour:
+ one can change percusive/non-percussive modes on the fly and
+ the chip will remain in sustain phase - verified on real YM3812 */
+
+ if(op->eg_type) /* non-percussive mode */
+ {
+ /* do nothing */
+ }
+ else /* percussive mode */
+ {
+ /* during sustain phase chip adds Release Rate (in percussive mode) */
+// if ( !(chip->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ if ( !(chip->eg_cnt & op->eg_m_rr) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ op->volume = MAX_ATT_INDEX;
+ }
+ /* else do nothing in sustain phase */
+ }
+ break;
+
+ case EG_REL: /* release phase */
+// if ( !(chip->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ if ( !(chip->eg_cnt & op->eg_m_rr) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }
+#endif
+ }
+ }
+
+ for (i=0; i<9*2*2; i++)
+ {
+ CH = &chip->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Phase Generator */
+ if(op->vib)
+ {
+ uint8_t block;
+ unsigned int block_fnum = CH->block_fnum;
+
+ unsigned int fnum_lfo = (block_fnum&0x0380) >> 7;
+
+ signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + 16*fnum_lfo ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum += lfo_fn_table_index_offset;
+ block = (block_fnum&0x1c00) >> 10;
+ op->Cnt += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+ else /* LFO phase modulation disabled for this operator */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+
+ /* The Noise Generator of the YM3812 is 23-bit shift register.
+ * Period is equal to 2^23-2 samples.
+ * Register works at sampling frequency of the chip, so output
+ * can change on every sample.
+ *
+ * Output of the register and input to the bit 22 is:
+ * bit0 XOR bit14 XOR bit15 XOR bit22
+ *
+ * Simply use bit 22 as the noise output.
+ */
+
+ chip->noise_p += chip->noise_f;
+ i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */
+ chip->noise_p &= FREQ_MASK;
+ while (i)
+ {
+ /*
+ uint32_t j;
+ j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1;
+ chip->noise_rng = (j<<22) | (chip->noise_rng>>1);
+ */
+
+ /*
+ Instead of doing all the logic operations above, we
+ use a trick here (and use bit 0 as the noise output).
+ The difference is only that the noise bit changes one
+ step ahead. This doesn't matter since we don't know
+ what is real state of the noise_rng after the reset.
+ */
+
+ if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302;
+ chip->noise_rng >>= 1;
+
+ i--;
+ }
+}
+
+
+static inline signed int op_calc(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ uint32_t p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+static inline signed int op_calc1(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ uint32_t p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm))>>FREQ_SH) & SIN_MASK)];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+
+#define volume_calc(OP) ((OP)->TLL + ((uint32_t)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask))
+
+/* calculate output of a standard 2 operator channel
+ (or 1st part of a 4-op channel) */
+static inline void chan_calc( OPL3 *chip, OPL3_CH *CH )
+{
+ OPL3_SLOT *SLOT;
+ unsigned int env;
+ signed int out;
+
+ chip->phase_modulation = 0;
+ chip->phase_modulation2= 0;
+
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env = volume_calc(SLOT);
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+ SLOT->op1_out[1] = 0;
+ if (env < ENV_QUIET)
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+ if (SLOT->connect) {
+ *SLOT->connect += SLOT->op1_out[1];
+ }
+//logerror("out0=%5i vol0=%4i ", SLOT->op1_out[1], env );
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if ((env < ENV_QUIET) && SLOT->connect)
+ *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable);
+
+//logerror("out1=%5i vol1=%4i\n", op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable), env );
+
+}
+
+/* calculate output of a 2nd part of 4-op channel */
+static inline void chan_calc_ext( OPL3 *chip, OPL3_CH *CH )
+{
+ OPL3_SLOT *SLOT;
+ unsigned int env;
+
+ chip->phase_modulation = 0;
+
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env = volume_calc(SLOT);
+ if (env < ENV_QUIET && SLOT->connect)
+ *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation2, SLOT->wavetable );
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if (env < ENV_QUIET && SLOT->connect)
+ *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable);
+
+}
+
+/*
+ operators used in the rhythm sounds generation process:
+
+ Envelope Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal
+ 6 / 0 12 50 70 90 f0 +
+ 6 / 1 15 53 73 93 f3 +
+ 7 / 0 13 51 71 91 f1 +
+ 7 / 1 16 54 74 94 f4 +
+ 8 / 0 14 52 72 92 f2 +
+ 8 / 1 17 55 75 95 f5 +
+
+ Phase Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number MULTIPLE Drum Hat Drum Tom Cymbal
+ 6 / 0 12 30 +
+ 6 / 1 15 33 +
+ 7 / 0 13 31 + + +
+ 7 / 1 16 34 ----- n o t u s e d -----
+ 8 / 0 14 32 +
+ 8 / 1 17 35 + +
+
+channel operator register number Bass High Snare Tom Top
+number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
+ 6 12,15 B6 A6 +
+
+ 7 13,16 B7 A7 + + +
+
+ 8 14,17 B8 A8 + + +
+
+*/
+
+/* calculate rhythm */
+
+static inline void chan_calc_rhythm( OPL3 *chip, OPL3_CH *CH, unsigned int noise )
+{
+ OPL3_SLOT *SLOT;
+ signed int *chanout = chip->chanout;
+ signed int out;
+ unsigned int env;
+
+
+ /* Bass Drum (verified on real YM3812):
+ - depends on the channel 6 'connect' register:
+ when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)
+ when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored
+ - output sample always is multiplied by 2
+ */
+
+ chip->phase_modulation = 0;
+
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env = volume_calc(SLOT);
+
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+
+ if (!SLOT->CON)
+ chip->phase_modulation = SLOT->op1_out[0];
+ //else ignore output of operator 1
+
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ chanout[6] += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable) * 2;
+
+
+ /* Phase generation is based on: */
+ // HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases)
+ // SD (16) channel 7->slot 1
+ // TOM (14) channel 8->slot 1
+ // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases)
+
+ /* Envelope generation based on: */
+ // HH channel 7->slot1
+ // SD channel 7->slot2
+ // TOM channel 8->slot1
+ // TOP channel 8->slot2
+
+
+ /* The following formulas can be well optimized.
+ I leave them in direct form for now (in case I've missed something).
+ */
+
+ /* High Hat (verified on real YM3812) */
+ env = volume_calc(SLOT7_1);
+ if( env < ENV_QUIET )
+ {
+ /* high hat phase generation:
+ phase = d0 or 234 (based on frequency only)
+ phase = 34 or 2d0 (based on noise)
+ */
+
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0xd0; */
+ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */
+ uint32_t phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */
+ if (res2)
+ phase = (0x200|(0xd0>>2));
+
+
+ /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */
+ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */
+ if (phase&0x200)
+ {
+ if (noise)
+ phase = 0x200|0xd0;
+ }
+ else
+ /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */
+ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */
+ {
+ if (noise)
+ phase = 0xd0>>2;
+ }
+
+ chanout[7] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1->wavetable) * 2;
+ }
+
+ /* Snare Drum (verified on real YM3812) */
+ env = volume_calc(SLOT7_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1;
+
+ /* when bit8 = 0 phase = 0x100; */
+ /* when bit8 = 1 phase = 0x200; */
+ uint32_t phase = bit8 ? 0x200 : 0x100;
+
+ /* Noise bit XOR'es phase by 0x100 */
+ /* when noisebit = 0 pass the phase from calculation above */
+ /* when noisebit = 1 phase ^= 0x100; */
+ /* in other words: phase ^= (noisebit<<8); */
+ if (noise)
+ phase ^= 0x100;
+
+ chanout[7] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2->wavetable) * 2;
+ }
+
+ /* Tom Tom (verified on real YM3812) */
+ env = volume_calc(SLOT8_1);
+ if( env < ENV_QUIET )
+ chanout[8] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2;
+
+ /* Top Cymbal (verified on real YM3812) */
+ env = volume_calc(SLOT8_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0x100; */
+ /* when res1 = 1 phase = 0x200 | 0x100; */
+ uint32_t phase = res1 ? 0x300 : 0x100;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | 0x100; */
+ if (res2)
+ phase = 0x300;
+
+ chanout[8] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2->wavetable) * 2;
+ }
+
+}
+
+
+/* generic table initialize */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 1; /* 12 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = ~tl_tab[ x*2 + 0 ]; /* this *is* different from OPL2 (verified on real YMF262) */
+
+ for (i=1; i<13; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = ~tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; /* this *is* different from OPL2 (verified on real YMF262) */
+ }
+ #if 0
+ logerror("tl %04i", x*2);
+ for (i=0; i<13; i++)
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +0 + i*2*TL_RES_LEN ] ); /* positive */
+ logerror("\n");
+
+ logerror("tl %04i", x*2);
+ for (i=0; i<13; i++)
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +1 + i*2*TL_RES_LEN ] ); /* negative */
+ logerror("\n");
+ #endif
+ }
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+
+ /*logerror("YMF262.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/
+ }
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* these 'pictures' represent _two_ cycles */
+ /* waveform 1: __ __ */
+ /* / \____/ \____*/
+ /* output only first half of the sinus waveform (positive one) */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[1*SIN_LEN+i] = sin_tab[i];
+
+ /* waveform 2: __ __ __ __ */
+ /* / \/ \/ \/ \*/
+ /* abs(sin) */
+
+ sin_tab[2*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>1) ];
+
+ /* waveform 3: _ _ _ _ */
+ /* / |_/ |_/ |_/ |_*/
+ /* abs(output only first quarter of the sinus waveform) */
+
+ if (i & (1<<(SIN_BITS-2)) )
+ sin_tab[3*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)];
+
+ /* waveform 4: */
+ /* /\ ____/\ ____*/
+ /* \/ \/ */
+ /* output whole sinus waveform in half the cycle(step=2) and output 0 on the other half of cycle */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[4*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[4*SIN_LEN+i] = sin_tab[i*2];
+
+ /* waveform 5: */
+ /* /\/\____/\/\____*/
+ /* */
+ /* output abs(whole sinus) waveform in half the cycle(step=2) and output 0 on the other half of cycle */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[5*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[5*SIN_LEN+i] = sin_tab[(i*2) & (SIN_MASK>>1) ];
+
+ /* waveform 6: ____ ____ */
+ /* */
+ /* ____ ____*/
+ /* output maximum in half the cycle and output minimum on the other half of cycle */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[6*SIN_LEN+i] = 1; /* negative */
+ else
+ sin_tab[6*SIN_LEN+i] = 0; /* positive */
+
+ /* waveform 7: */
+ /* |\____ |\____ */
+ /* \| \|*/
+ /* output sawtooth waveform */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ x = ((SIN_LEN-1)-i)*16 + 1; /* negative: from 8177 to 1 */
+ else
+ x = i*16; /*positive: from 0 to 8176 */
+
+ if (x > TL_TAB_LEN)
+ x = TL_TAB_LEN; /* clip to the allowed range */
+
+ sin_tab[7*SIN_LEN+i] = x;
+
+ //logerror("YMF262.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );
+ //logerror("YMF262.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] );
+ //logerror("YMF262.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );
+ //logerror("YMF262.C: sin4[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[4*SIN_LEN+i], tl_tab[sin_tab[4*SIN_LEN+i]] );
+ //logerror("YMF262.C: sin5[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[5*SIN_LEN+i], tl_tab[sin_tab[5*SIN_LEN+i]] );
+ //logerror("YMF262.C: sin6[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[6*SIN_LEN+i], tl_tab[sin_tab[6*SIN_LEN+i]] );
+ //logerror("YMF262.C: sin7[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[7*SIN_LEN+i], tl_tab[sin_tab[7*SIN_LEN+i]] );
+ }
+ /*logerror("YMF262.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/
+
+#ifdef SAVE_SAMPLE
+ sample[0]=fopen("sampsum.pcm","wb");
+#endif
+
+ return 1;
+}
+
+static void OPLCloseTable( void )
+{
+#ifdef SAVE_SAMPLE
+ fclose(sample[0]);
+#endif
+}
+
+
+
+static void OPL3_initalize(OPL3 *chip)
+{
+ int i;
+
+ /* frequency base */
+ chip->freqbase = (chip->rate) ? ((double)chip->clock / (8.0*36)) / chip->rate : 0;
+#if 0
+ chip->rate = (double)chip->clock / (8.0*36);
+ chip->freqbase = 1.0;
+#endif
+
+ /* logerror("YMF262: freqbase=%f\n", chip->freqbase); */
+
+ /* Timer base time */
+ //chip->TimerBase = attotime::from_hz(chip->clock) * (8*36);
+
+ /* make fnumber -> increment counter table */
+ for( i=0 ; i < 1024 ; i++ )
+ {
+ /* opn phase increment counter = 20bit */
+ chip->fn_tab[i] = (uint32_t)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+#if 0
+ logerror("YMF262.C: fn_tab[%4i] = %08x (dec=%8i)\n",
+ i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 );
+#endif
+ }
+
+#if 0
+ for( i=0 ; i < 16 ; i++ )
+ {
+ logerror("YMF262.C: sl_tab[%i] = %08x\n",
+ i, sl_tab[i] );
+ }
+ for( i=0 ; i < 8 ; i++ )
+ {
+ int j;
+ logerror("YMF262.C: ksl_tab[oct=%2i] =",i);
+ for (j=0; j<16; j++)
+ {
+ logerror("%08x ", static_cast<uint32_t>(ksl_tab[i*16+j]) );
+ }
+ logerror("\n");
+ }
+#endif
+
+
+ /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */
+ /* One entry from LFO_AM_TABLE lasts for 64 samples */
+ chip->lfo_am_inc = (1.0 / 64.0 ) * (1<<LFO_SH) * chip->freqbase;
+
+ /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */
+ chip->lfo_pm_inc = (1.0 / 1024.0) * (1<<LFO_SH) * chip->freqbase;
+
+ /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/
+
+ /* Noise generator: a step takes 1 sample */
+ chip->noise_f = (1.0 / 1.0) * (1<<FREQ_SH) * chip->freqbase;
+
+ chip->eg_timer_add = (1<<EG_SH) * chip->freqbase;
+ chip->eg_timer_overflow = ( 1 ) * (1<<EG_SH);
+ /*logerror("YMF262init eg_timer_add=%8x eg_timer_overflow=%8x\n", chip->eg_timer_add, chip->eg_timer_overflow);*/
+
+}
+
+static inline void FM_KEYON(OPL3_SLOT *SLOT, uint32_t key_set)
+{
+ if( !SLOT->key )
+ {
+ /* restart Phase Generator */
+ SLOT->Cnt = 0;
+ /* phase -> Attack */
+ SLOT->state = EG_ATT;
+ }
+ SLOT->key |= key_set;
+}
+
+static inline void FM_KEYOFF(OPL3_SLOT *SLOT, uint32_t key_clr)
+{
+ if( SLOT->key )
+ {
+ SLOT->key &= key_clr;
+
+ if( !SLOT->key )
+ {
+ /* phase -> Release */
+ if (SLOT->state>EG_REL)
+ SLOT->state = EG_REL;
+ }
+ }
+}
+
+/* update phase increment counter of operator (also update the EG rates if necessary) */
+static inline void CALC_FCSLOT(OPL3_CH *CH,OPL3_SLOT *SLOT)
+{
+ int ksr;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+60)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_m_ar = (1<<SLOT->eg_sh_ar)-1;
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_m_ar = (1<<SLOT->eg_sh_ar)-1;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_m_dr = (1<<SLOT->eg_sh_dr)-1;
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_m_rr = (1<<SLOT->eg_sh_rr)-1;
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+ }
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+static inline void set_mul(OPL3 *chip,int slot,int v)
+{
+ OPL3_CH *CH = &chip->P_CH[slot/2];
+ OPL3_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = mul_tab[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_type = (v&0x20);
+ SLOT->vib = (v&0x40);
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+
+ if (chip->OPL3_mode & 1)
+ {
+ int chan_no = slot/2;
+
+ /* in OPL3 mode */
+ //DO THIS:
+ //if this is one of the slots of 1st channel forming up a 4-op channel
+ //do normal operation
+ //else normal 2 operator function
+ //OR THIS:
+ //if this is one of the slots of 2nd channel forming up a 4-op channel
+ //update it using channel data of 1st channel of a pair
+ //else normal 2 operator function
+ switch(chan_no)
+ {
+ case 0: case 1: case 2:
+ case 9: case 10: case 11:
+ if (CH->extended)
+ {
+ /* normal */
+ CALC_FCSLOT(CH,SLOT);
+ }
+ else
+ {
+ /* normal */
+ CALC_FCSLOT(CH,SLOT);
+ }
+ break;
+ case 3: case 4: case 5:
+ case 12: case 13: case 14:
+ if ((CH-3)->extended)
+ {
+ /* update this SLOT using frequency data for 1st channel of a pair */
+ CALC_FCSLOT(CH-3,SLOT);
+ }
+ else
+ {
+ /* normal */
+ CALC_FCSLOT(CH,SLOT);
+ }
+ break;
+ default:
+ /* normal */
+ CALC_FCSLOT(CH,SLOT);
+ break;
+ }
+ }
+ else
+ {
+ /* in OPL2 mode */
+ CALC_FCSLOT(CH,SLOT);
+ }
+}
+
+/* set ksl & tl */
+static inline void set_ksl_tl(OPL3 *chip,int slot,int v)
+{
+ OPL3_CH *CH = &chip->P_CH[slot/2];
+ OPL3_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->ksl = ksl_shift[v >> 6];
+ SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */
+
+ if (chip->OPL3_mode & 1)
+ {
+ int chan_no = slot/2;
+
+ /* in OPL3 mode */
+ //DO THIS:
+ //if this is one of the slots of 1st channel forming up a 4-op channel
+ //do normal operation
+ //else normal 2 operator function
+ //OR THIS:
+ //if this is one of the slots of 2nd channel forming up a 4-op channel
+ //update it using channel data of 1st channel of a pair
+ //else normal 2 operator function
+ switch(chan_no)
+ {
+ case 0: case 1: case 2:
+ case 9: case 10: case 11:
+ if (CH->extended)
+ {
+ /* normal */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+ else
+ {
+ /* normal */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+ break;
+ case 3: case 4: case 5:
+ case 12: case 13: case 14:
+ if ((CH-3)->extended)
+ {
+ /* update this SLOT using frequency data for 1st channel of a pair */
+ SLOT->TLL = SLOT->TL + ((CH-3)->ksl_base>>SLOT->ksl);
+ }
+ else
+ {
+ /* normal */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+ break;
+ default:
+ /* normal */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ break;
+ }
+ }
+ else
+ {
+ /* in OPL2 mode */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+
+}
+
+/* set attack rate & decay rate */
+static inline void set_ar_dr(OPL3 *chip,int slot,int v)
+{
+ OPL3_CH *CH = &chip->P_CH[slot/2];
+ OPL3_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 16+60) /* verified on real YMF262 - all 15 x rates take "zero" time */
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_m_ar = (1<<SLOT->eg_sh_ar)-1;
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_m_ar = (1<<SLOT->eg_sh_ar)-1;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+
+ SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_m_dr = (1<<SLOT->eg_sh_dr)-1;
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+}
+
+/* set sustain level & release rate */
+static inline void set_sl_rr(OPL3 *chip,int slot,int v)
+{
+ OPL3_CH *CH = &chip->P_CH[slot/2];
+ OPL3_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->sl = sl_tab[ v>>4 ];
+
+ SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_m_rr = (1<<SLOT->eg_sh_rr)-1;
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+}
+
+
+static void update_channels(OPL3 *chip, OPL3_CH *CH)
+{
+ /* update channel passed as a parameter and a channel at CH+=3; */
+ if (CH->extended)
+ { /* we've just switched to combined 4 operator mode */
+
+ }
+ else
+ { /* we've just switched to normal 2 operator mode */
+
+ }
+
+}
+
+/* write a value v to register r on OPL chip */
+static void OPL3WriteReg(OPL3 *chip, int r, int v)
+{
+ OPL3_CH *CH;
+ unsigned int ch_offset = 0;
+ int slot;
+ int block_fnum;
+
+ if(r&0x100)
+ {
+ switch(r)
+ {
+ case 0x101: /* test register */
+ return;
+
+ case 0x104: /* 6 channels enable */
+ {
+ uint8_t prev;
+
+ CH = &chip->P_CH[0]; /* channel 0 */
+ prev = CH->extended;
+ CH->extended = (v>>0) & 1;
+ if(prev != CH->extended)
+ update_channels(chip, CH);
+ CH++; /* channel 1 */
+ prev = CH->extended;
+ CH->extended = (v>>1) & 1;
+ if(prev != CH->extended)
+ update_channels(chip, CH);
+ CH++; /* channel 2 */
+ prev = CH->extended;
+ CH->extended = (v>>2) & 1;
+ if(prev != CH->extended)
+ update_channels(chip, CH);
+
+
+ CH = &chip->P_CH[9]; /* channel 9 */
+ prev = CH->extended;
+ CH->extended = (v>>3) & 1;
+ if(prev != CH->extended)
+ update_channels(chip, CH);
+ CH++; /* channel 10 */
+ prev = CH->extended;
+ CH->extended = (v>>4) & 1;
+ if(prev != CH->extended)
+ update_channels(chip, CH);
+ CH++; /* channel 11 */
+ prev = CH->extended;
+ CH->extended = (v>>5) & 1;
+ if(prev != CH->extended)
+ update_channels(chip, CH);
+
+ }
+ return;
+
+ case 0x105: /* OPL3 extensions enable register */
+
+ chip->OPL3_mode = v&0x01; /* OPL3 mode when bit0=1 otherwise it is OPL2 mode */
+
+ /* following behaviour was tested on real YMF262,
+ switching OPL3/OPL2 modes on the fly:
+ - does not change the waveform previously selected (unless when ....)
+ - does not update CH.A, CH.B, CH.C and CH.D output selectors (registers c0-c8) (unless when ....)
+ - does not disable channels 9-17 on OPL3->OPL2 switch
+ - does not switch 4 operator channels back to 2 operator channels
+ */
+
+ return;
+
+ default:
+ if (r < 0x120)
+ chip->device->logerror("YMF262: write to unknown register (set#2): %03x value=%02x\n",r,v);
+ break;
+ }
+
+ ch_offset = 9; /* register page #2 starts from channel 9 (counting from 0) */
+ }
+
+ /* adjust bus to 8 bits */
+ r &= 0xff;
+ v &= 0xff;
+
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:control */
+ switch(r&0x1f)
+ {
+ case 0x01: /* test register */
+ break;
+ case 0x02: /* Timer 1 */
+ chip->T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ chip->T[1] = (256-v)*16;
+ break;
+#if 0
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flags clear */
+ OPL3_STATUS_RESET(chip,0x60);
+ }
+ else
+ { /* set IRQ mask ,timer enable */
+ uint8_t st1 = v & 1;
+ uint8_t st2 = (v>>1) & 1;
+
+ /* IRQRST,T1MSK,t2MSK,x,x,x,ST2,ST1 */
+ OPL3_STATUS_RESET(chip, v & 0x60);
+ OPL3_STATUSMASK_SET(chip, (~v) & 0x60 );
+
+ /* timer 2 */
+ if(chip->st[1] != st2)
+ {
+ attotime period = st2 ? chip->TimerBase * chip->T[1] : attotime::zero;
+ chip->st[1] = st2;
+ if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,1,period);
+ }
+ /* timer 1 */
+ if(chip->st[0] != st1)
+ {
+ attotime period = st1 ? chip->TimerBase * chip->T[0] : attotime::zero;
+ chip->st[0] = st1;
+ if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,0,period);
+ }
+ }
+#endif
+ break;
+ case 0x08: /* x,NTS,x,x, x,x,x,x */
+ chip->nts = v;
+ break;
+
+ default:
+ chip->device->logerror("YMF262: write to unknown register: %02x value=%02x\n",r,v);
+ break;
+ }
+ break;
+ case 0x20: /* am ON, vib ON, ksr, eg_type, mul */
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_mul(chip, slot + ch_offset*2, v);
+ break;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ksl_tl(chip, slot + ch_offset*2, v);
+ break;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ar_dr(chip, slot + ch_offset*2, v);
+ break;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_sl_rr(chip, slot + ch_offset*2, v);
+ break;
+ case 0xa0:
+ if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */
+ {
+ if (ch_offset != 0) /* 0xbd register is present in set #1 only */
+ return;
+
+ chip->lfo_am_depth = v & 0x80;
+ chip->lfo_pm_depth_range = (v&0x40) ? 8 : 0;
+
+ chip->rhythm = v&0x3f;
+
+ if(chip->rhythm&0x20)
+ {
+ /* BD key on/off */
+ if(v&0x10)
+ {
+ FM_KEYON (&chip->P_CH[6].SLOT[SLOT1], 2);
+ FM_KEYON (&chip->P_CH[6].SLOT[SLOT2], 2);
+ }
+ else
+ {
+ FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2);
+ }
+ /* HH key on/off */
+ if(v&0x01) FM_KEYON (&chip->P_CH[7].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key on/off */
+ if(v&0x08) FM_KEYON (&chip->P_CH[7].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key on/off */
+ if(v&0x04) FM_KEYON (&chip->P_CH[8].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY key on/off */
+ if(v&0x02) FM_KEYON (&chip->P_CH[8].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2);
+ }
+ else
+ {
+ /* BD key off */
+ FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2);
+ /* HH key off */
+ FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key off */
+ FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key off */
+ FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY off */
+ FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2);
+ }
+ return;
+ }
+
+ /* keyon,block,fnum */
+ if( (r&0x0f) > 8) return;
+ CH = &chip->P_CH[(r&0x0f) + ch_offset];
+
+ if(!(r&0x10))
+ { /* a0-a8 */
+ block_fnum = (CH->block_fnum&0x1f00) | v;
+ }
+ else
+ { /* b0-b8 */
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+
+ if (chip->OPL3_mode & 1)
+ {
+ int chan_no = (r&0x0f) + ch_offset;
+
+ /* in OPL3 mode */
+ //DO THIS:
+ //if this is 1st channel forming up a 4-op channel
+ //ALSO keyon/off slots of 2nd channel forming up 4-op channel
+ //else normal 2 operator function keyon/off
+ //OR THIS:
+ //if this is 2nd channel forming up 4-op channel just do nothing
+ //else normal 2 operator function keyon/off
+ switch(chan_no)
+ {
+ case 0: case 1: case 2:
+ case 9: case 10: case 11:
+ if (CH->extended)
+ {
+ //if this is 1st channel forming up a 4-op channel
+ //ALSO keyon/off slots of 2nd channel forming up 4-op channel
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ FM_KEYON (&(CH+3)->SLOT[SLOT1], 1);
+ FM_KEYON (&(CH+3)->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ FM_KEYOFF(&(CH+3)->SLOT[SLOT1],~1);
+ FM_KEYOFF(&(CH+3)->SLOT[SLOT2],~1);
+ }
+ }
+ else
+ {
+ //else normal 2 operator function keyon/off
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ }
+ break;
+
+ case 3: case 4: case 5:
+ case 12: case 13: case 14:
+ if ((CH-3)->extended)
+ {
+ //if this is 2nd channel forming up 4-op channel just do nothing
+ }
+ else
+ {
+ //else normal 2 operator function keyon/off
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ }
+ break;
+
+ default:
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ break;
+ }
+ }
+ else
+ {
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ uint8_t block = block_fnum >> 10;
+
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = static_cast<uint32_t>(ksl_tab[block_fnum>>6]);
+ CH->fc = chip->fn_tab[block_fnum&0x03ff] >> (7-block);
+
+ /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */
+ CH->kcode = (CH->block_fnum&0x1c00)>>9;
+
+ /* the info below is actually opposite to what is stated in the Manuals (verifed on real YMF262) */
+ /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */
+ /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */
+ if (chip->nts&0x40)
+ CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */
+ else
+ CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */
+
+ if (chip->OPL3_mode & 1)
+ {
+ int chan_no = (r&0x0f) + ch_offset;
+ /* in OPL3 mode */
+ //DO THIS:
+ //if this is 1st channel forming up a 4-op channel
+ //ALSO update slots of 2nd channel forming up 4-op channel
+ //else normal 2 operator function keyon/off
+ //OR THIS:
+ //if this is 2nd channel forming up 4-op channel just do nothing
+ //else normal 2 operator function keyon/off
+ switch(chan_no)
+ {
+ case 0: case 1: case 2:
+ case 9: case 10: case 11:
+ if (CH->extended)
+ {
+ //if this is 1st channel forming up a 4-op channel
+ //ALSO update slots of 2nd channel forming up 4-op channel
+
+ /* refresh Total Level in FOUR SLOTs of this channel and channel+3 using data from THIS channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+ (CH+3)->SLOT[SLOT1].TLL = (CH+3)->SLOT[SLOT1].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT1].ksl);
+ (CH+3)->SLOT[SLOT2].TLL = (CH+3)->SLOT[SLOT2].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in FOUR SLOTs of this channel and channel+3 using data from THIS channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT2]);
+ }
+ else
+ {
+ //else normal 2 operator function
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ break;
+
+ case 3: case 4: case 5:
+ case 12: case 13: case 14:
+ if ((CH-3)->extended)
+ {
+ //if this is 2nd channel forming up 4-op channel just do nothing
+ }
+ else
+ {
+ //else normal 2 operator function
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ break;
+
+ default:
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ break;
+ }
+ }
+ else
+ {
+ /* in OPL2 mode */
+
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ }
+ break;
+
+ case 0xc0:
+ /* CH.D, CH.C, CH.B, CH.A, FB(3bits), C */
+ if( (r&0xf) > 8) return;
+
+ CH = &chip->P_CH[(r&0xf) + ch_offset];
+
+ if( chip->OPL3_mode & 1 )
+ {
+ int base = ((r&0xf) + ch_offset) * 4;
+
+ /* OPL3 mode */
+ chip->pan[ base ] = (v & 0x10) ? ~0 : 0; /* ch.A */
+ chip->pan[ base +1 ] = (v & 0x20) ? ~0 : 0; /* ch.B */
+ chip->pan[ base +2 ] = (v & 0x40) ? ~0 : 0; /* ch.C */
+ chip->pan[ base +3 ] = (v & 0x80) ? ~0 : 0; /* ch.D */
+ }
+ else
+ {
+ int base = ((r&0xf) + ch_offset) * 4;
+
+ /* OPL2 mode - always enabled */
+ chip->pan[ base ] = ~0; /* ch.A */
+ chip->pan[ base +1 ] = ~0; /* ch.B */
+ chip->pan[ base +2 ] = ~0; /* ch.C */
+ chip->pan[ base +3 ] = ~0; /* ch.D */
+ }
+
+ chip->pan_ctrl_value[ (r&0xf) + ch_offset ] = v; /* store control value for OPL3/OPL2 mode switching on the fly */
+
+ CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0;
+ CH->SLOT[SLOT1].CON = v&1;
+
+ if( chip->OPL3_mode & 1 )
+ {
+ int chan_no = (r&0x0f) + ch_offset;
+
+ switch(chan_no)
+ {
+ case 0: case 1: case 2:
+ case 9: case 10: case 11:
+ if (CH->extended)
+ {
+ uint8_t conn = (CH->SLOT[SLOT1].CON<<1) | ((CH+3)->SLOT[SLOT1].CON<<0);
+ switch(conn)
+ {
+ case 0:
+ /* 1 -> 2 -> 3 -> 4 - out */
+
+ CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD;
+ CH->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2;
+ (CH+3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD;
+ (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3;
+ break;
+ case 1:
+ /* 1 -> 2 -\
+ 3 -> 4 -+- out */
+
+ CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD;
+ CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no;
+ (CH+3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD;
+ (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3;
+ break;
+ case 2:
+ /* 1 -----------\
+ 2 -> 3 -> 4 -+- out */
+
+ CH->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no;
+ CH->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2;
+ (CH+3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD;
+ (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3;
+ break;
+ case 3:
+ /* 1 ------\
+ 2 -> 3 -+- out
+ 4 ------/ */
+ CH->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no;
+ CH->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2;
+ (CH+3)->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no + 3;
+ (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3;
+ break;
+ }
+ OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]);
+ OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]);
+ OPL3_SLOT_CONNECT(chip, &(CH+3)->SLOT[SLOT1]);
+ OPL3_SLOT_CONNECT(chip, &(CH+3)->SLOT[SLOT2]);
+ }
+ else
+ {
+ /* 2 operators mode */
+ CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD;
+ CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset;
+ OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]);
+ OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]);
+ }
+ break;
+
+ case 3: case 4: case 5:
+ case 12: case 13: case 14:
+ if ((CH-3)->extended)
+ {
+ uint8_t conn = ((CH-3)->SLOT[SLOT1].CON<<1) | (CH->SLOT[SLOT1].CON<<0);
+ switch(conn)
+ {
+ case 0:
+ /* 1 -> 2 -> 3 -> 4 - out */
+
+ (CH-3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD;
+ (CH-3)->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2;
+ CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD;
+ CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no;
+ break;
+ case 1:
+ /* 1 -> 2 -\
+ 3 -> 4 -+- out */
+
+ (CH-3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD;
+ (CH-3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no - 3;
+ CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD;
+ CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no;
+ break;
+ case 2:
+ /* 1 -----------\
+ 2 -> 3 -> 4 -+- out */
+
+ (CH-3)->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no - 3;
+ (CH-3)->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2;
+ CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD;
+ CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no;
+ break;
+ case 3:
+ /* 1 ------\
+ 2 -> 3 -+- out
+ 4 ------/ */
+ (CH-3)->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no - 3;
+ (CH-3)->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2;
+ CH->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no;
+ CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no;
+ break;
+ }
+ OPL3_SLOT_CONNECT(chip, &(CH-3)->SLOT[SLOT1]);
+ OPL3_SLOT_CONNECT(chip, &(CH-3)->SLOT[SLOT2]);
+ OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]);
+ OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]);
+ }
+ else
+ {
+ /* 2 operators mode */
+ CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD;
+ CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset;
+ OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]);
+ OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]);
+ }
+ break;
+
+ default:
+ /* 2 operators mode */
+ CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD;
+ CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset;
+ OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]);
+ OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]);
+ break;
+ }
+ }
+ else
+ {
+ /* OPL2 mode - always 2 operators mode */
+ CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD;
+ CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset;
+ OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]);
+ OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]);
+ }
+ break;
+
+ case 0xe0: /* waveform select */
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+
+ slot += ch_offset*2;
+
+ CH = &chip->P_CH[slot/2];
+
+
+ /* store 3-bit value written regardless of current OPL2 or OPL3 mode... (verified on real YMF262) */
+ v &= 7;
+ CH->SLOT[slot&1].waveform_number = v;
+
+ /* ... but select only waveforms 0-3 in OPL2 mode */
+ if( !(chip->OPL3_mode & 1) )
+ {
+ v &= 3; /* we're in OPL2 mode */
+ }
+ CH->SLOT[slot&1].wavetable = v * SIN_LEN;
+ break;
+ }
+}
+
+/* lock/unlock for common table */
+static int OPL3_LockTable(device_t *device)
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+
+ /* first time */
+
+ if( !init_tables() )
+ {
+ num_lock--;
+ return -1;
+ }
+
+ return 0;
+}
+
+static void OPL3_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+
+ /* last time */
+ OPLCloseTable();
+}
+
+static void OPL3ResetChip(OPL3 *chip)
+{
+ int c,s;
+
+ chip->eg_timer = 0;
+ chip->eg_cnt = 0;
+
+ chip->noise_rng = 1; /* noise shift register */
+ chip->nts = 0; /* note split */
+ OPL3_STATUS_RESET(chip,0x60);
+
+ /* reset with register write */
+ OPL3WriteReg(chip,0x01,0); /* test register */
+ OPL3WriteReg(chip,0x02,0); /* Timer1 */
+ OPL3WriteReg(chip,0x03,0); /* Timer2 */
+ OPL3WriteReg(chip,0x04,0); /* IRQ mask clear */
+
+
+//FIX IT registers 101, 104 and 105
+
+
+//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers)
+ for(c = 0xff ; c >= 0x20 ; c-- )
+ OPL3WriteReg(chip,c,0);
+//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers)
+ for(c = 0x1ff ; c >= 0x120 ; c-- )
+ OPL3WriteReg(chip,c,0);
+
+
+
+ /* reset operator parameters */
+ for( c = 0 ; c < 9*2 ; c++ )
+ {
+ OPL3_CH *CH = &chip->P_CH[c];
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ CH->SLOT[s].state = EG_OFF;
+ CH->SLOT[s].volume = MAX_ATT_INDEX;
+ }
+ }
+}
+
+/* Create one of virtual YMF262 */
+/* 'clock' is chip clock in Hz */
+/* 'rate' is sampling rate */
+static OPL3 *OPL3Create(device_t *device, int clock, int rate, int type)
+{
+ OPL3 *chip;
+
+ if (OPL3_LockTable(device) == -1) return 0;
+
+ /* allocate memory block */
+ chip = auto_alloc_clear(device->machine(), OPL3 );
+
+ chip->device = device;
+ chip->type = type;
+ chip->clock = clock;
+ chip->rate = rate;
+
+ /* init global tables */
+ OPL3_initalize(chip);
+
+ /* reset chip */
+ OPL3ResetChip(chip);
+ return chip;
+}
+
+/* Destroy one of virtual YMF262 */
+static void OPL3Destroy(OPL3 *chip)
+{
+ OPL3_UnLockTable();
+ auto_free(chip->device->machine(), chip);
+}
+
+
+/* YMF262 I/O interface */
+static int OPL3Write(OPL3 *chip, int a, int v)
+{
+ /* data bus is 8 bits */
+ v &= 0xff;
+
+
+ switch(a&3)
+ {
+ case 0: /* address port 0 (register set #1) */
+ chip->address = v;
+ break;
+
+ case 1: /* data port - ignore A1 */
+ case 3: /* data port - ignore A1 */
+ if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam,0);
+ OPL3WriteReg(chip,chip->address,v);
+ break;
+
+ case 2: /* address port 1 (register set #2) */
+
+ /* verified on real YMF262:
+ in OPL3 mode:
+ address line A1 is stored during *address* write and ignored during *data* write.
+
+ in OPL2 mode:
+ register set#2 writes go to register set#1 (ignoring A1)
+ verified on registers from set#2: 0x01, 0x04, 0x20-0xef
+ The only exception is register 0x05.
+ */
+ if( chip->OPL3_mode & 1 )
+ {
+ /* OPL3 mode */
+ chip->address = v | 0x100;
+ }
+ else
+ {
+ /* in OPL2 mode the only accessible in set #2 is register 0x05 */
+ if( v==5 )
+ chip->address = v | 0x100;
+ else
+ chip->address = v; /* verified range: 0x01, 0x04, 0x20-0xef(set #2 becomes set #1 in opl2 mode) */
+ }
+ break;
+ }
+
+ return chip->status>>7;
+}
+
+static unsigned char OPL3Read(OPL3 *chip,int a)
+{
+ if( a==0 )
+ {
+ /* status port */
+ return chip->status;
+ }
+
+ return 0x00; /* verified on real YMF262 */
+}
+
+
+
+static int OPL3TimerOver(OPL3 *chip,int c)
+{
+ if( c )
+ { /* Timer B */
+ OPL3_STATUS_SET(chip,0x20);
+ }
+ else
+ { /* Timer A */
+ OPL3_STATUS_SET(chip,0x40);
+ }
+ /* reload timer */
+// if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,c,chip->TimerBase * chip->T[c]);
+ return chip->status>>7;
+}
+
+static void OPL3_save_state(OPL3 *chip, device_t *device) {
+#if 0
+ for (int ch=0; ch<18; ch++) {
+ OPL3_CH *channel = &chip->P_CH[ch];
+ device->save_item(NAME(channel->block_fnum), ch);
+ device->save_item(NAME(channel->fc), ch);
+ device->save_item(NAME(channel->ksl_base), ch);
+ device->save_item(NAME(channel->kcode), ch);
+ device->save_item(NAME(channel->extended), ch);
+
+ for (int sl=0; sl<2; sl++) {
+ OPL3_SLOT *slot = &channel->SLOT[sl];
+ device->save_item(NAME(slot->ar), ch*2+sl);
+ device->save_item(NAME(slot->dr), ch*2+sl);
+ device->save_item(NAME(slot->rr), ch*2+sl);
+ device->save_item(NAME(slot->KSR), ch*2+sl);
+ device->save_item(NAME(slot->ksl), ch*2+sl);
+ device->save_item(NAME(slot->ksr), ch*2+sl);
+ device->save_item(NAME(slot->mul), ch*2+sl);
+
+ device->save_item(NAME(slot->Cnt), ch*2+sl);
+ device->save_item(NAME(slot->Incr), ch*2+sl);
+ device->save_item(NAME(slot->FB), ch*2+sl);
+ device->save_item(NAME(slot->conn_enum), ch*2+sl);
+ device->save_item(NAME(slot->op1_out), ch*2+sl);
+ device->save_item(NAME(slot->CON), ch*2+sl);
+
+ device->save_item(NAME(slot->eg_type), ch*2+sl);
+ device->save_item(NAME(slot->state), ch*2+sl);
+ device->save_item(NAME(slot->TL), ch*2+sl);
+ device->save_item(NAME(slot->TLL), ch*2+sl);
+ device->save_item(NAME(slot->volume), ch*2+sl);
+ device->save_item(NAME(slot->sl), ch*2+sl);
+
+ device->save_item(NAME(slot->eg_m_ar), ch*2+sl);
+ device->save_item(NAME(slot->eg_sh_ar), ch*2+sl);
+ device->save_item(NAME(slot->eg_sel_ar), ch*2+sl);
+ device->save_item(NAME(slot->eg_m_dr), ch*2+sl);
+ device->save_item(NAME(slot->eg_sh_dr), ch*2+sl);
+ device->save_item(NAME(slot->eg_sel_dr), ch*2+sl);
+ device->save_item(NAME(slot->eg_m_rr), ch*2+sl);
+ device->save_item(NAME(slot->eg_sh_rr), ch*2+sl);
+ device->save_item(NAME(slot->eg_sel_rr), ch*2+sl);
+
+ device->save_item(NAME(slot->key), ch*2+sl);
+
+ device->save_item(NAME(slot->AMmask), ch*2+sl);
+ device->save_item(NAME(slot->vib), ch*2+sl);
+
+ device->save_item(NAME(slot->waveform_number), ch*2+sl);
+ device->save_item(NAME(slot->wavetable), ch*2+sl);
+ }
+ }
+
+ device->save_item(NAME(chip->pan));
+ device->save_item(NAME(chip->pan_ctrl_value));
+
+ device->save_item(NAME(chip->lfo_am_depth));
+ device->save_item(NAME(chip->lfo_pm_depth_range));
+
+ device->save_item(NAME(chip->OPL3_mode));
+ device->save_item(NAME(chip->rhythm));
+
+ device->save_item(NAME(chip->address));
+ device->save_item(NAME(chip->status));
+ device->save_item(NAME(chip->statusmask));
+#endif
+}
+
+void * ymf262_init(device_t *device, int clock, int rate)
+{
+ void *chip = OPL3Create(device,clock,rate,OPL3_TYPE_YMF262);
+ OPL3_save_state((OPL3 *)chip, device);
+
+ return chip;
+}
+
+void ymf262_post_load(void *chip) {
+ OPL3 *opl3 = (OPL3 *)chip;
+ for (int ch=0; ch<18; ch++) {
+ for (int sl=0; sl<2; sl++) {
+ OPL3_SLOT_CONNECT(opl3, &(opl3->P_CH[ch].SLOT[sl]));
+ }
+ }
+}
+
+void ymf262_shutdown(void *chip)
+{
+ OPL3Destroy((OPL3 *)chip);
+}
+void ymf262_reset_chip(void *chip)
+{
+ OPL3ResetChip((OPL3 *)chip);
+}
+
+int ymf262_write(void *chip, int a, int v)
+{
+ return OPL3Write((OPL3 *)chip, a, v);
+}
+
+unsigned char ymf262_read(void *chip, int a)
+{
+ /* Note on status register: */
+
+ /* YM3526(OPL) and YM3812(OPL2) return bit2 and bit1 in HIGH state */
+
+ /* YMF262(OPL3) always returns bit2 and bit1 in LOW state */
+ /* which can be used to identify the chip */
+
+ /* YMF278(OPL4) returns bit2 in LOW and bit1 in HIGH state ??? info from manual - not verified */
+
+ return OPL3Read((OPL3 *)chip, a);
+}
+int ymf262_timer_over(void *chip, int c)
+{
+ return OPL3TimerOver((OPL3 *)chip, c);
+}
+
+void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER timer_handler, device_t *device)
+{
+ reinterpret_cast<OPL3 *>(chip)->SetTimerHandler(timer_handler, device);
+}
+void ymf262_set_irq_handler(void *chip, OPL3_IRQHANDLER IRQHandler, device_t *device)
+{
+ reinterpret_cast<OPL3 *>(chip)->SetIRQHandler(IRQHandler, device);
+}
+void ymf262_set_update_handler(void *chip, OPL3_UPDATEHANDLER UpdateHandler, device_t *device)
+{
+ reinterpret_cast<OPL3 *>(chip)->SetUpdateHandler(UpdateHandler, device);
+}
+
+
+/*
+** Generate samples for one of the YMF262's
+**
+** 'which' is the virtual YMF262 number
+** '**buffers' is table of 4 pointers to the buffers: CH.A, CH.B, CH.C and CH.D
+** 'length' is the number of samples that should be generated
+*/
+void ymf262_update_one(void *_chip, OPL3SAMPLE **buffers, int length)
+{
+ int i;
+ OPL3 *chip = (OPL3 *)_chip;
+ signed int *chanout = chip->chanout;
+ uint8_t rhythm = chip->rhythm&0x20;
+
+ OPL3SAMPLE *ch_a = buffers[0];
+ OPL3SAMPLE *ch_b = buffers[1];
+ OPL3SAMPLE *ch_c = buffers[2];
+ OPL3SAMPLE *ch_d = buffers[3];
+
+ for( i=0; i < length ; i++ )
+ {
+ int a,b,c,d;
+
+
+ advance_lfo(chip);
+
+ /* clear channel outputs */
+ memset(chip->chanout, 0, sizeof(chip->chanout));
+
+#if 1
+ /* register set #1 */
+ chan_calc(chip, &chip->P_CH[0]); /* extended 4op ch#0 part 1 or 2op ch#0 */
+ if (chip->P_CH[0].extended)
+ chan_calc_ext(chip, &chip->P_CH[3]); /* extended 4op ch#0 part 2 */
+ else
+ chan_calc(chip, &chip->P_CH[3]); /* standard 2op ch#3 */
+
+
+ chan_calc(chip, &chip->P_CH[1]); /* extended 4op ch#1 part 1 or 2op ch#1 */
+ if (chip->P_CH[1].extended)
+ chan_calc_ext(chip, &chip->P_CH[4]); /* extended 4op ch#1 part 2 */
+ else
+ chan_calc(chip, &chip->P_CH[4]); /* standard 2op ch#4 */
+
+
+ chan_calc(chip, &chip->P_CH[2]); /* extended 4op ch#2 part 1 or 2op ch#2 */
+ if (chip->P_CH[2].extended)
+ chan_calc_ext(chip, &chip->P_CH[5]); /* extended 4op ch#2 part 2 */
+ else
+ chan_calc(chip, &chip->P_CH[5]); /* standard 2op ch#5 */
+
+
+ if(!rhythm)
+ {
+ chan_calc(chip, &chip->P_CH[6]);
+ chan_calc(chip, &chip->P_CH[7]);
+ chan_calc(chip, &chip->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ chan_calc_rhythm(chip, &chip->P_CH[0], (chip->noise_rng>>0)&1 );
+ }
+
+ /* register set #2 */
+ chan_calc(chip, &chip->P_CH[ 9]);
+ if (chip->P_CH[9].extended)
+ chan_calc_ext(chip, &chip->P_CH[12]);
+ else
+ chan_calc(chip, &chip->P_CH[12]);
+
+
+ chan_calc(chip, &chip->P_CH[10]);
+ if (chip->P_CH[10].extended)
+ chan_calc_ext(chip, &chip->P_CH[13]);
+ else
+ chan_calc(chip, &chip->P_CH[13]);
+
+
+ chan_calc(chip, &chip->P_CH[11]);
+ if (chip->P_CH[11].extended)
+ chan_calc_ext(chip, &chip->P_CH[14]);
+ else
+ chan_calc(chip, &chip->P_CH[14]);
+
+
+ /* channels 15,16,17 are fixed 2-operator channels only */
+ chan_calc(chip, &chip->P_CH[15]);
+ chan_calc(chip, &chip->P_CH[16]);
+ chan_calc(chip, &chip->P_CH[17]);
+#endif
+
+ /* accumulator register set #1 */
+ a = chanout[0] & chip->pan[0];
+ b = chanout[0] & chip->pan[1];
+ c = chanout[0] & chip->pan[2];
+ d = chanout[0] & chip->pan[3];
+#if 1
+ a += chanout[1] & chip->pan[4];
+ b += chanout[1] & chip->pan[5];
+ c += chanout[1] & chip->pan[6];
+ d += chanout[1] & chip->pan[7];
+ a += chanout[2] & chip->pan[8];
+ b += chanout[2] & chip->pan[9];
+ c += chanout[2] & chip->pan[10];
+ d += chanout[2] & chip->pan[11];
+
+ a += chanout[3] & chip->pan[12];
+ b += chanout[3] & chip->pan[13];
+ c += chanout[3] & chip->pan[14];
+ d += chanout[3] & chip->pan[15];
+ a += chanout[4] & chip->pan[16];
+ b += chanout[4] & chip->pan[17];
+ c += chanout[4] & chip->pan[18];
+ d += chanout[4] & chip->pan[19];
+ a += chanout[5] & chip->pan[20];
+ b += chanout[5] & chip->pan[21];
+ c += chanout[5] & chip->pan[22];
+ d += chanout[5] & chip->pan[23];
+
+ a += chanout[6] & chip->pan[24];
+ b += chanout[6] & chip->pan[25];
+ c += chanout[6] & chip->pan[26];
+ d += chanout[6] & chip->pan[27];
+ a += chanout[7] & chip->pan[28];
+ b += chanout[7] & chip->pan[29];
+ c += chanout[7] & chip->pan[30];
+ d += chanout[7] & chip->pan[31];
+ a += chanout[8] & chip->pan[32];
+ b += chanout[8] & chip->pan[33];
+ c += chanout[8] & chip->pan[34];
+ d += chanout[8] & chip->pan[35];
+
+ /* accumulator register set #2 */
+ a += chanout[9] & chip->pan[36];
+ b += chanout[9] & chip->pan[37];
+ c += chanout[9] & chip->pan[38];
+ d += chanout[9] & chip->pan[39];
+ a += chanout[10] & chip->pan[40];
+ b += chanout[10] & chip->pan[41];
+ c += chanout[10] & chip->pan[42];
+ d += chanout[10] & chip->pan[43];
+ a += chanout[11] & chip->pan[44];
+ b += chanout[11] & chip->pan[45];
+ c += chanout[11] & chip->pan[46];
+ d += chanout[11] & chip->pan[47];
+
+ a += chanout[12] & chip->pan[48];
+ b += chanout[12] & chip->pan[49];
+ c += chanout[12] & chip->pan[50];
+ d += chanout[12] & chip->pan[51];
+ a += chanout[13] & chip->pan[52];
+ b += chanout[13] & chip->pan[53];
+ c += chanout[13] & chip->pan[54];
+ d += chanout[13] & chip->pan[55];
+ a += chanout[14] & chip->pan[56];
+ b += chanout[14] & chip->pan[57];
+ c += chanout[14] & chip->pan[58];
+ d += chanout[14] & chip->pan[59];
+
+ a += chanout[15] & chip->pan[60];
+ b += chanout[15] & chip->pan[61];
+ c += chanout[15] & chip->pan[62];
+ d += chanout[15] & chip->pan[63];
+ a += chanout[16] & chip->pan[64];
+ b += chanout[16] & chip->pan[65];
+ c += chanout[16] & chip->pan[66];
+ d += chanout[16] & chip->pan[67];
+ a += chanout[17] & chip->pan[68];
+ b += chanout[17] & chip->pan[69];
+ c += chanout[17] & chip->pan[70];
+ d += chanout[17] & chip->pan[71];
+#endif
+ a >>= FINAL_SH;
+ b >>= FINAL_SH;
+ c >>= FINAL_SH;
+ d >>= FINAL_SH;
+
+ /* limit check */
+ a = limit( a , MAXOUT, MINOUT );
+ b = limit( b , MAXOUT, MINOUT );
+ c = limit( c , MAXOUT, MINOUT );
+ d = limit( d , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ ch_a[i] = a;
+ ch_b[i] = b;
+ ch_c[i] = c;
+ ch_d[i] = d;
+
+ advance(chip);
+ }
+
+}
diff --git a/src/hardware/mame/ymf262.h b/src/hardware/mame/ymf262.h
new file mode 100644
index 000000000..b82a89b97
--- /dev/null
+++ b/src/hardware/mame/ymf262.h
@@ -0,0 +1,40 @@
+// license:GPL-2.0+
+// copyright-holders:Jarek Burczynski
+#ifndef MAME_SOUND_YMF262_H
+#define MAME_SOUND_YMF262_H
+
+#pragma once
+
+/* select number of output bits: 8 or 16 */
+#define OPL3_SAMPLE_BITS 16
+
+typedef stream_sample_t OPL3SAMPLE;
+/*
+#if (OPL3_SAMPLE_BITS==16)
+typedef int16_t OPL3SAMPLE;
+#endif
+#if (OPL3_SAMPLE_BITS==8)
+typedef int8_t OPL3SAMPLE;
+#endif
+*/
+
+typedef void (*OPL3_TIMERHANDLER)(device_t *device,int timer,const attotime &period);
+typedef void (*OPL3_IRQHANDLER)(device_t *device,int irq);
+typedef void (*OPL3_UPDATEHANDLER)(device_t *device,int min_interval_us);
+
+
+void *ymf262_init(device_t *device, int clock, int rate);
+void ymf262_post_load(void *chip);
+void ymf262_shutdown(void *chip);
+void ymf262_reset_chip(void *chip);
+int ymf262_write(void *chip, int a, int v);
+unsigned char ymf262_read(void *chip, int a);
+int ymf262_timer_over(void *chip, int c);
+void ymf262_update_one(void *chip, OPL3SAMPLE **buffers, int length);
+
+void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER TimerHandler, device_t *device);
+void ymf262_set_irq_handler(void *chip, OPL3_IRQHANDLER IRQHandler, device_t *device);
+void ymf262_set_update_handler(void *chip, OPL3_UPDATEHANDLER UpdateHandler, device_t *device);
+
+
+#endif // MAME_SOUND_YMF262_H
diff --git a/src/hardware/memory.cpp b/src/hardware/memory.cpp
new file mode 100644
index 000000000..5069b8a54
--- /dev/null
+++ b/src/hardware/memory.cpp
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "mem.h"
+#include "inout.h"
+#include "setup.h"
+#include "paging.h"
+#include "regs.h"
+
+#include <string.h>
+
+#define PAGES_IN_BLOCK ((1024*1024)/MEM_PAGE_SIZE)
+#define SAFE_MEMORY 32
+#define MAX_MEMORY 64
+#define MAX_PAGE_ENTRIES (MAX_MEMORY*1024*1024/4096)
+#define LFB_PAGES 512
+#define MAX_LINKS ((MAX_MEMORY*1024/4)+4096) //Hopefully enough
+
+struct LinkBlock {
+ Bitu used;
+ Bit32u pages[MAX_LINKS];
+};
+
+static struct MemoryBlock {
+ Bitu pages;
+ PageHandler * * phandlers;
+ MemHandle * mhandles;
+ LinkBlock links;
+ struct {
+ Bitu start_page;
+ Bitu end_page;
+ Bitu pages;
+ PageHandler *handler;
+ PageHandler *mmiohandler;
+ } lfb;
+ struct {
+ bool enabled;
+ Bit8u controlport;
+ } a20;
+} memory;
+
+HostPt MemBase;
+
+class IllegalPageHandler : public PageHandler {
+public:
+ IllegalPageHandler() {
+ flags=PFLAG_INIT|PFLAG_NOCODE;
+ }
+ Bitu readb(PhysPt addr) {
+#if C_DEBUG
+ LOG_MSG("Illegal read from %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip);
+#else
+ static Bits lcount=0;
+ if (lcount<1000) {
+ lcount++;
+ LOG_MSG("Illegal read from %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip);
+ }
+#endif
+ return 0xff;
+ }
+ void writeb(PhysPt addr,Bitu val) {
+#if C_DEBUG
+ LOG_MSG("Illegal write to %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip);
+#else
+ static Bits lcount=0;
+ if (lcount<1000) {
+ lcount++;
+ LOG_MSG("Illegal write to %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip);
+ }
+#endif
+ }
+};
+
+class RAMPageHandler : public PageHandler {
+public:
+ RAMPageHandler() {
+ flags=PFLAG_READABLE|PFLAG_WRITEABLE;
+ }
+ HostPt GetHostReadPt(Bitu phys_page) {
+ return MemBase+phys_page*MEM_PAGESIZE;
+ }
+ HostPt GetHostWritePt(Bitu phys_page) {
+ return MemBase+phys_page*MEM_PAGESIZE;
+ }
+};
+
+class ROMPageHandler : public RAMPageHandler {
+public:
+ ROMPageHandler() {
+ flags=PFLAG_READABLE|PFLAG_HASROM;
+ }
+ void writeb(PhysPt addr,Bitu val){
+ LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",val,addr);
+ }
+ void writew(PhysPt addr,Bitu val){
+ LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",val,addr);
+ }
+ void writed(PhysPt addr,Bitu val){
+ LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",val,addr);
+ }
+};
+
+
+
+static IllegalPageHandler illegal_page_handler;
+static RAMPageHandler ram_page_handler;
+static ROMPageHandler rom_page_handler;
+
+void MEM_SetLFB(Bitu page, Bitu pages, PageHandler *handler, PageHandler *mmiohandler) {
+ memory.lfb.handler=handler;
+ memory.lfb.mmiohandler=mmiohandler;
+ memory.lfb.start_page=page;
+ memory.lfb.end_page=page+pages;
+ memory.lfb.pages=pages;
+ PAGING_ClearTLB();
+}
+
+PageHandler * MEM_GetPageHandler(Bitu phys_page) {
+ if (phys_page<memory.pages) {
+ return memory.phandlers[phys_page];
+ } else if ((phys_page>=memory.lfb.start_page) && (phys_page<memory.lfb.end_page)) {
+ return memory.lfb.handler;
+ } else if ((phys_page>=memory.lfb.start_page+0x01000000/4096) &&
+ (phys_page<memory.lfb.start_page+0x01000000/4096+16)) {
+ return memory.lfb.mmiohandler;
+ }
+ return &illegal_page_handler;
+}
+
+void MEM_SetPageHandler(Bitu phys_page,Bitu pages,PageHandler * handler) {
+ for (;pages>0;pages--) {
+ memory.phandlers[phys_page]=handler;
+ phys_page++;
+ }
+}
+
+void MEM_ResetPageHandler(Bitu phys_page, Bitu pages) {
+ for (;pages>0;pages--) {
+ memory.phandlers[phys_page]=&ram_page_handler;
+ phys_page++;
+ }
+}
+
+Bitu mem_strlen(PhysPt pt) {
+ Bitu x=0;
+ while (x<1024) {
+ if (!mem_readb_inline(pt+x)) return x;
+ x++;
+ }
+ return 0; //Hope this doesn't happen
+}
+
+void mem_strcpy(PhysPt dest,PhysPt src) {
+ Bit8u r;
+ while ( (r = mem_readb(src++)) ) mem_writeb_inline(dest++,r);
+ mem_writeb_inline(dest,0);
+}
+
+void mem_memcpy(PhysPt dest,PhysPt src,Bitu size) {
+ while (size--) mem_writeb_inline(dest++,mem_readb_inline(src++));
+}
+
+void MEM_BlockRead(PhysPt pt,void * data,Bitu size) {
+ Bit8u * write=reinterpret_cast<Bit8u *>(data);
+ while (size--) {
+ *write++=mem_readb_inline(pt++);
+ }
+}
+
+void MEM_BlockWrite(PhysPt pt,void const * const data,Bitu size) {
+ Bit8u const * read = reinterpret_cast<Bit8u const * const>(data);
+ while (size--) {
+ mem_writeb_inline(pt++,*read++);
+ }
+}
+
+void MEM_BlockCopy(PhysPt dest,PhysPt src,Bitu size) {
+ mem_memcpy(dest,src,size);
+}
+
+void MEM_StrCopy(PhysPt pt,char * data,Bitu size) {
+ while (size--) {
+ Bit8u r=mem_readb_inline(pt++);
+ if (!r) break;
+ *data++=r;
+ }
+ *data=0;
+}
+
+Bitu MEM_TotalPages(void) {
+ return memory.pages;
+}
+
+Bitu MEM_FreeLargest(void) {
+ Bitu size=0;Bitu largest=0;
+ Bitu index=XMS_START;
+ while (index<memory.pages) {
+ if (!memory.mhandles[index]) {
+ size++;
+ } else {
+ if (size>largest) largest=size;
+ size=0;
+ }
+ index++;
+ }
+ if (size>largest) largest=size;
+ return largest;
+}
+
+Bitu MEM_FreeTotal(void) {
+ Bitu free=0;
+ Bitu index=XMS_START;
+ while (index<memory.pages) {
+ if (!memory.mhandles[index]) free++;
+ index++;
+ }
+ return free;
+}
+
+Bitu MEM_AllocatedPages(MemHandle handle)
+{
+ Bitu pages = 0;
+ while (handle>0) {
+ pages++;
+ handle=memory.mhandles[handle];
+ }
+ return pages;
+}
+
+//TODO Maybe some protection for this whole allocation scheme
+
+INLINE Bitu BestMatch(Bitu size) {
+ Bitu index=XMS_START;
+ Bitu first=0;
+ Bitu best=0xfffffff;
+ Bitu best_first=0;
+ while (index<memory.pages) {
+ /* Check if we are searching for first free page */
+ if (!first) {
+ /* Check if this is a free page */
+ if (!memory.mhandles[index]) {
+ first=index;
+ }
+ } else {
+ /* Check if this still is used page */
+ if (memory.mhandles[index]) {
+ Bitu pages=index-first;
+ if (pages==size) {
+ return first;
+ } else if (pages>size) {
+ if (pages<best) {
+ best=pages;
+ best_first=first;
+ }
+ }
+ first=0; //Always reset for new search
+ }
+ }
+ index++;
+ }
+ /* Check for the final block if we can */
+ if (first && (index-first>=size) && (index-first<best)) {
+ return first;
+ }
+ return best_first;
+}
+
+MemHandle MEM_AllocatePages(Bitu pages,bool sequence) {
+ MemHandle ret;
+ if (!pages) return 0;
+ if (sequence) {
+ Bitu index=BestMatch(pages);
+ if (!index) return 0;
+ MemHandle * next=&ret;
+ while (pages) {
+ *next=index;
+ next=&memory.mhandles[index];
+ index++;pages--;
+ }
+ *next=-1;
+ } else {
+ if (MEM_FreeTotal()<pages) return 0;
+ MemHandle * next=&ret;
+ while (pages) {
+ Bitu index=BestMatch(1);
+ if (!index) E_Exit("MEM:corruption during allocate");
+ while (pages && (!memory.mhandles[index])) {
+ *next=index;
+ next=&memory.mhandles[index];
+ index++;pages--;
+ }
+ *next=-1; //Invalidate it in case we need another match
+ }
+ }
+ return ret;
+}
+
+MemHandle MEM_GetNextFreePage(void) {
+ return (MemHandle)BestMatch(1);
+}
+
+void MEM_ReleasePages(MemHandle handle) {
+ while (handle>0) {
+ MemHandle next=memory.mhandles[handle];
+ memory.mhandles[handle]=0;
+ handle=next;
+ }
+}
+
+bool MEM_ReAllocatePages(MemHandle & handle,Bitu pages,bool sequence) {
+ if (handle<=0) {
+ if (!pages) return true;
+ handle=MEM_AllocatePages(pages,sequence);
+ return (handle>0);
+ }
+ if (!pages) {
+ MEM_ReleasePages(handle);
+ handle=-1;
+ return true;
+ }
+ MemHandle index=handle;
+ MemHandle last;Bitu old_pages=0;
+ while (index>0) {
+ old_pages++;
+ last=index;
+ index=memory.mhandles[index];
+ }
+ if (old_pages == pages) return true;
+ if (old_pages > pages) {
+ /* Decrease size */
+ pages--;index=handle;old_pages--;
+ while (pages) {
+ index=memory.mhandles[index];
+ pages--;old_pages--;
+ }
+ MemHandle next=memory.mhandles[index];
+ memory.mhandles[index]=-1;
+ index=next;
+ while (old_pages) {
+ next=memory.mhandles[index];
+ memory.mhandles[index]=0;
+ index=next;
+ old_pages--;
+ }
+ return true;
+ } else {
+ /* Increase size, check for enough free space */
+ Bitu need=pages-old_pages;
+ if (sequence) {
+ index=last+1;
+ Bitu free=0;
+ while ((index<(MemHandle)memory.pages) && !memory.mhandles[index]) {
+ index++;free++;
+ }
+ if (free>=need) {
+ /* Enough space allocate more pages */
+ index=last;
+ while (need) {
+ memory.mhandles[index]=index+1;
+ need--;index++;
+ }
+ memory.mhandles[index]=-1;
+ return true;
+ } else {
+ /* Not Enough space allocate new block and copy */
+ MemHandle newhandle=MEM_AllocatePages(pages,true);
+ if (!newhandle) return false;
+ MEM_BlockCopy(newhandle*4096,handle*4096,old_pages*4096);
+ MEM_ReleasePages(handle);
+ handle=newhandle;
+ return true;
+ }
+ } else {
+ MemHandle rem=MEM_AllocatePages(need,false);
+ if (!rem) return false;
+ memory.mhandles[last]=rem;
+ return true;
+ }
+ }
+ return 0;
+}
+
+MemHandle MEM_NextHandle(MemHandle handle) {
+ return memory.mhandles[handle];
+}
+
+MemHandle MEM_NextHandleAt(MemHandle handle,Bitu where) {
+ while (where) {
+ where--;
+ handle=memory.mhandles[handle];
+ }
+ return handle;
+}
+
+
+/*
+ A20 line handling,
+ Basically maps the 4 pages at the 1mb to 0mb in the default page directory
+*/
+bool MEM_A20_Enabled(void) {
+ return memory.a20.enabled;
+}
+
+void MEM_A20_Enable(bool enabled) {
+ Bitu phys_base=enabled ? (1024/4) : 0;
+ for (Bitu i=0;i<16;i++) PAGING_MapPage((1024/4)+i,phys_base+i);
+ memory.a20.enabled=enabled;
+}
+
+
+/* Memory access functions */
+Bit16u mem_unalignedreadw(PhysPt address) {
+ Bit16u ret = mem_readb_inline(address);
+ ret |= mem_readb_inline(address+1) << 8;
+ return ret;
+}
+
+Bit32u mem_unalignedreadd(PhysPt address) {
+ Bit32u ret = mem_readb_inline(address);
+ ret |= mem_readb_inline(address+1) << 8;
+ ret |= mem_readb_inline(address+2) << 16;
+ ret |= mem_readb_inline(address+3) << 24;
+ return ret;
+}
+
+
+void mem_unalignedwritew(PhysPt address,Bit16u val) {
+ mem_writeb_inline(address,(Bit8u)val);val>>=8;
+ mem_writeb_inline(address+1,(Bit8u)val);
+}
+
+void mem_unalignedwrited(PhysPt address,Bit32u val) {
+ mem_writeb_inline(address,(Bit8u)val);val>>=8;
+ mem_writeb_inline(address+1,(Bit8u)val);val>>=8;
+ mem_writeb_inline(address+2,(Bit8u)val);val>>=8;
+ mem_writeb_inline(address+3,(Bit8u)val);
+}
+
+
+bool mem_unalignedreadw_checked(PhysPt address, Bit16u * val) {
+ Bit8u rval1,rval2;
+ if (mem_readb_checked(address+0, &rval1)) return true;
+ if (mem_readb_checked(address+1, &rval2)) return true;
+ *val=(Bit16u)(((Bit8u)rval1) | (((Bit8u)rval2) << 8));
+ return false;
+}
+
+bool mem_unalignedreadd_checked(PhysPt address, Bit32u * val) {
+ Bit8u rval1,rval2,rval3,rval4;
+ if (mem_readb_checked(address+0, &rval1)) return true;
+ if (mem_readb_checked(address+1, &rval2)) return true;
+ if (mem_readb_checked(address+2, &rval3)) return true;
+ if (mem_readb_checked(address+3, &rval4)) return true;
+ *val=(Bit32u)(((Bit8u)rval1) | (((Bit8u)rval2) << 8) | (((Bit8u)rval3) << 16) | (((Bit8u)rval4) << 24));
+ return false;
+}
+
+bool mem_unalignedwritew_checked(PhysPt address,Bit16u val) {
+ if (mem_writeb_checked(address,(Bit8u)(val & 0xff))) return true;val>>=8;
+ if (mem_writeb_checked(address+1,(Bit8u)(val & 0xff))) return true;
+ return false;
+}
+
+bool mem_unalignedwrited_checked(PhysPt address,Bit32u val) {
+ if (mem_writeb_checked(address,(Bit8u)(val & 0xff))) return true;val>>=8;
+ if (mem_writeb_checked(address+1,(Bit8u)(val & 0xff))) return true;val>>=8;
+ if (mem_writeb_checked(address+2,(Bit8u)(val & 0xff))) return true;val>>=8;
+ if (mem_writeb_checked(address+3,(Bit8u)(val & 0xff))) return true;
+ return false;
+}
+
+Bit8u mem_readb(PhysPt address) {
+ return mem_readb_inline(address);
+}
+
+Bit16u mem_readw(PhysPt address) {
+ return mem_readw_inline(address);
+}
+
+Bit32u mem_readd(PhysPt address) {
+ return mem_readd_inline(address);
+}
+
+void mem_writeb(PhysPt address,Bit8u val) {
+ mem_writeb_inline(address,val);
+}
+
+void mem_writew(PhysPt address,Bit16u val) {
+ mem_writew_inline(address,val);
+}
+
+void mem_writed(PhysPt address,Bit32u val) {
+ mem_writed_inline(address,val);
+}
+
+static void write_p92(Bitu port,Bitu val,Bitu iolen) {
+ // Bit 0 = system reset (switch back to real mode)
+ if (val&1) E_Exit("XMS: CPU reset via port 0x92 not supported.");
+ memory.a20.controlport = val & ~2;
+ MEM_A20_Enable((val & 2)>0);
+}
+
+static Bitu read_p92(Bitu port,Bitu iolen) {
+ return memory.a20.controlport | (memory.a20.enabled ? 0x02 : 0);
+}
+
+void RemoveEMSPageFrame(void) {
+ /* Setup rom at 0xe0000-0xf0000 */
+ for (Bitu ct=0xe0;ct<0xf0;ct++) {
+ memory.phandlers[ct] = &rom_page_handler;
+ }
+}
+
+void PreparePCJRCartRom(void) {
+ /* Setup rom at 0xd0000-0xe0000 */
+ for (Bitu ct=0xd0;ct<0xe0;ct++) {
+ memory.phandlers[ct] = &rom_page_handler;
+ }
+}
+
+HostPt GetMemBase(void) { return MemBase; }
+
+class MEMORY:public Module_base{
+private:
+ IO_ReadHandleObject ReadHandler;
+ IO_WriteHandleObject WriteHandler;
+public:
+ MEMORY(Section* configuration):Module_base(configuration){
+ Bitu i;
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+
+ /* Setup the Physical Page Links */
+ Bitu memsize=section->Get_int("memsize");
+
+ if (memsize < 1) memsize = 1;
+ /* max 63 to solve problems with certain xms handlers */
+ if (memsize > MAX_MEMORY-1) {
+ LOG_MSG("Maximum memory size is %d MB",MAX_MEMORY - 1);
+ memsize = MAX_MEMORY-1;
+ }
+ if (memsize > SAFE_MEMORY-1) {
+ LOG_MSG("Memory sizes above %d MB are NOT recommended.",SAFE_MEMORY - 1);
+ LOG_MSG("Stick with the default values unless you are absolutely certain.");
+ }
+ MemBase = new Bit8u[memsize*1024*1024];
+ if (!MemBase) E_Exit("Can't allocate main memory of %d MB",memsize);
+ /* Clear the memory, as new doesn't always give zeroed memory
+ * (Visual C debug mode). We want zeroed memory though. */
+ memset((void*)MemBase,0,memsize*1024*1024);
+ memory.pages = (memsize*1024*1024)/4096;
+ /* Allocate the data for the different page information blocks */
+ memory.phandlers=new PageHandler * [memory.pages];
+ memory.mhandles=new MemHandle [memory.pages];
+ for (i = 0;i < memory.pages;i++) {
+ memory.phandlers[i] = &ram_page_handler;
+ memory.mhandles[i] = 0; //Set to 0 for memory allocation
+ }
+ /* Setup rom at 0xc0000-0xc8000 */
+ for (i=0xc0;i<0xc8;i++) {
+ memory.phandlers[i] = &rom_page_handler;
+ }
+ /* Setup rom at 0xf0000-0x100000 */
+ for (i=0xf0;i<0x100;i++) {
+ memory.phandlers[i] = &rom_page_handler;
+ }
+ if (machine==MCH_PCJR) {
+ /* Setup cartridge rom at 0xe0000-0xf0000 */
+ for (i=0xe0;i<0xf0;i++) {
+ memory.phandlers[i] = &rom_page_handler;
+ }
+ }
+ /* Reset some links */
+ memory.links.used = 0;
+ // A20 Line - PS/2 system control port A
+ WriteHandler.Install(0x92,write_p92,IO_MB);
+ ReadHandler.Install(0x92,read_p92,IO_MB);
+ MEM_A20_Enable(false);
+ }
+ ~MEMORY(){
+ delete [] MemBase;
+ delete [] memory.phandlers;
+ delete [] memory.mhandles;
+ }
+};
+
+
+static MEMORY* test;
+
+static void MEM_ShutDown(Section * sec) {
+ delete test;
+}
+
+void MEM_Init(Section * sec) {
+ /* shutdown function */
+ test = new MEMORY(sec);
+ sec->AddDestroyFunction(&MEM_ShutDown);
+}
diff --git a/src/hardware/mixer.cpp b/src/hardware/mixer.cpp
new file mode 100644
index 000000000..41f291f12
--- /dev/null
+++ b/src/hardware/mixer.cpp
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+/*
+ Remove the sdl code from here and have it handeld in the sdlmain.
+ That should call the mixer start from there or something.
+*/
+
+#include <string.h>
+#include <sys/types.h>
+#include <math.h>
+
+#if defined (WIN32)
+//Midi listing
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <mmsystem.h>
+#endif
+
+#include "SDL.h"
+#include "mem.h"
+#include "pic.h"
+#include "dosbox.h"
+#include "mixer.h"
+#include "timer.h"
+#include "setup.h"
+#include "cross.h"
+#include "support.h"
+#include "mapper.h"
+#include "hardware.h"
+#include "programs.h"
+#include "midi.h"
+
+#define MIXER_SSIZE 4
+
+//#define MIXER_SHIFT 14
+//#define MIXER_REMAIN ((1<<MIXER_SHIFT)-1)
+
+#define MIXER_VOLSHIFT 13
+
+#define FREQ_SHIFT 14
+#define FREQ_NEXT ( 1 << FREQ_SHIFT)
+#define FREQ_MASK ( FREQ_NEXT -1 )
+
+#define TICK_SHIFT 14
+#define TICK_NEXT ( 1 << TICK_SHIFT)
+#define TICK_MASK (TICK_NEXT -1)
+
+
+static INLINE Bit16s MIXER_CLIP(Bits SAMP) {
+ if (SAMP < MAX_AUDIO) {
+ if (SAMP > MIN_AUDIO)
+ return SAMP;
+ else return MIN_AUDIO;
+ } else return MAX_AUDIO;
+}
+
+static struct {
+ Bit32s work[MIXER_BUFSIZE][2];
+ //Write/Read pointers for the buffer
+ Bitu pos,done;
+ Bitu needed, min_needed, max_needed;
+ //For every millisecond tick how many samples need to be generated
+ Bit32u tick_add;
+ Bit32u tick_counter;
+ float mastervol[2];
+ MixerChannel * channels;
+ bool nosound;
+ Bit32u freq;
+ Bit32u blocksize;
+} mixer;
+
+Bit8u MixTemp[MIXER_BUFSIZE];
+
+MixerChannel * MIXER_AddChannel(MIXER_Handler handler,Bitu freq,const char * name) {
+ MixerChannel * chan=new MixerChannel();
+ chan->scale = 1.0;
+ chan->handler=handler;
+ chan->name=name;
+ chan->SetFreq(freq);
+ chan->next=mixer.channels;
+ chan->SetVolume(1,1);
+ chan->enabled=false;
+ chan->interpolate = false;
+ mixer.channels=chan;
+ return chan;
+}
+
+MixerChannel * MIXER_FindChannel(const char * name) {
+ MixerChannel * chan=mixer.channels;
+ while (chan) {
+ if (!strcasecmp(chan->name,name)) break;
+ chan=chan->next;
+ }
+ return chan;
+}
+
+void MIXER_DelChannel(MixerChannel* delchan) {
+ MixerChannel * chan=mixer.channels;
+ MixerChannel * * where=&mixer.channels;
+ while (chan) {
+ if (chan==delchan) {
+ *where=chan->next;
+ delete delchan;
+ return;
+ }
+ where=&chan->next;
+ chan=chan->next;
+ }
+}
+
+void MixerChannel::UpdateVolume(void) {
+ volmul[0]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[0]*mixer.mastervol[0]);
+ volmul[1]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[1]*mixer.mastervol[1]);
+}
+
+void MixerChannel::SetVolume(float _left,float _right) {
+ volmain[0]=_left;
+ volmain[1]=_right;
+ UpdateVolume();
+}
+
+void MixerChannel::SetScale( float f ) {
+ scale = f;
+ UpdateVolume();
+}
+
+void MixerChannel::Enable(bool _yesno) {
+ if (_yesno==enabled) return;
+ enabled=_yesno;
+ if (enabled) {
+ freq_counter = 0;
+ SDL_LockAudio();
+ if (done<mixer.done) done=mixer.done;
+ SDL_UnlockAudio();
+ }
+}
+
+void MixerChannel::SetFreq(Bitu freq) {
+ freq_add=(freq<<FREQ_SHIFT)/mixer.freq;
+
+ if (freq != mixer.freq) {
+ interpolate = true;
+ }
+ else {
+ interpolate = false;
+ }
+}
+
+void MixerChannel::Mix(Bitu _needed) {
+ needed=_needed;
+ while (enabled && needed>done) {
+ Bitu left = (needed - done);
+ left *= freq_add;
+ left = (left >> FREQ_SHIFT) + ((left & FREQ_MASK)!=0);
+ handler(left);
+ }
+}
+
+void MixerChannel::AddSilence(void) {
+ if (done<needed) {
+ done=needed;
+ //Make sure the next samples are zero when they get switched to prev
+ nextSample[0] = 0;
+ nextSample[1] = 0;
+ //This should trigger an instant request for new samples
+ freq_counter = FREQ_NEXT;
+ }
+}
+
+template<class Type,bool stereo,bool signeddata,bool nativeorder>
+inline void MixerChannel::AddSamples(Bitu len, const Type* data) {
+ //Position where to write the data
+ Bitu mixpos = mixer.pos + done;
+ //Position in the incoming data
+ Bitu pos = 0;
+ //Mix and data for the full length
+ while (1) {
+ //Does new data need to get read?
+ while (freq_counter >= FREQ_NEXT) {
+ //Would this overflow the source data, then it's time to leave
+ if (pos >= len)
+ return;
+ freq_counter -= FREQ_NEXT;
+ prevSample[0] = nextSample[0];
+ if (stereo) {
+ prevSample[1] = nextSample[1];
+ }
+ if ( sizeof( Type) == 1) {
+ if (!signeddata) {
+ if (stereo) {
+ nextSample[0]=(((Bit8s)(data[pos*2+0] ^ 0x80)) << 8);
+ nextSample[1]=(((Bit8s)(data[pos*2+1] ^ 0x80)) << 8);
+ } else {
+ nextSample[0]=(((Bit8s)(data[pos] ^ 0x80)) << 8);
+ }
+ } else {
+ if (stereo) {
+ nextSample[0]=(data[pos*2+0] << 8);
+ nextSample[1]=(data[pos*2+1] << 8);
+ } else {
+ nextSample[0]=(data[pos] << 8);
+ }
+ }
+ //16bit and 32bit both contain 16bit data internally
+ } else {
+ if (signeddata) {
+ if (stereo) {
+ if (nativeorder) {
+ nextSample[0]=data[pos*2+0];
+ nextSample[1]=data[pos*2+1];
+ } else {
+ if ( sizeof( Type) == 2) {
+ nextSample[0]=(Bit16s)host_readw((HostPt)&data[pos*2+0]);
+ nextSample[1]=(Bit16s)host_readw((HostPt)&data[pos*2+1]);
+ } else {
+ nextSample[0]=(Bit32s)host_readd((HostPt)&data[pos*2+0]);
+ nextSample[1]=(Bit32s)host_readd((HostPt)&data[pos*2+1]);
+ }
+ }
+ } else {
+ if (nativeorder) {
+ nextSample[0] = data[pos];
+ } else {
+ if ( sizeof( Type) == 2) {
+ nextSample[0]=(Bit16s)host_readw((HostPt)&data[pos]);
+ } else {
+ nextSample[0]=(Bit32s)host_readd((HostPt)&data[pos]);
+ }
+ }
+ }
+ } else {
+ if (stereo) {
+ if (nativeorder) {
+ nextSample[0]=(Bits)data[pos*2+0]-32768;
+ nextSample[1]=(Bits)data[pos*2+1]-32768;
+ } else {
+ if ( sizeof( Type) == 2) {
+ nextSample[0]=(Bits)host_readw((HostPt)&data[pos*2+0])-32768;
+ nextSample[1]=(Bits)host_readw((HostPt)&data[pos*2+1])-32768;
+ } else {
+ nextSample[0]=(Bits)host_readd((HostPt)&data[pos*2+0])-32768;
+ nextSample[1]=(Bits)host_readd((HostPt)&data[pos*2+1])-32768;
+ }
+ }
+ } else {
+ if (nativeorder) {
+ nextSample[0]=(Bits)data[pos]-32768;
+ } else {
+ if ( sizeof( Type) == 2) {
+ nextSample[0]=(Bits)host_readw((HostPt)&data[pos])-32768;
+ } else {
+ nextSample[0]=(Bits)host_readd((HostPt)&data[pos])-32768;
+ }
+ }
+ }
+ }
+ }
+ //This sample has been handled now, increase position
+ pos++;
+ }
+ //Where to write
+ mixpos &= MIXER_BUFMASK;
+ Bit32s* write = mixer.work[mixpos];
+ if (!interpolate) {
+ write[0] += prevSample[0] * volmul[0];
+ write[1] += (stereo ? prevSample[1] : prevSample[0]) * volmul[1];
+ }
+ else {
+ Bits diff_mul = freq_counter & FREQ_MASK;
+ Bits sample = prevSample[0] + (((nextSample[0] - prevSample[0]) * diff_mul) >> FREQ_SHIFT);
+ write[0] += sample*volmul[0];
+ if (stereo) {
+ sample = prevSample[1] + (((nextSample[1] - prevSample[1]) * diff_mul) >> FREQ_SHIFT);
+ }
+ write[1] += sample*volmul[1];
+ }
+ //Prepare for next sample
+ freq_counter += freq_add;
+ mixpos++;
+ done++;
+ }
+}
+
+void MixerChannel::AddStretched(Bitu len,Bit16s * data) {
+ if (done >= needed) {
+ LOG_MSG("Can't add, buffer full");
+ return;
+ }
+ //Target samples this inputs gets stretched into
+ Bitu outlen = needed - done;
+ Bitu index = 0;
+ Bitu index_add = (len << FREQ_SHIFT)/outlen;
+ Bitu mixpos = mixer.pos + done;
+ done = needed;
+ Bitu pos = 0;
+
+ while (outlen--) {
+ Bitu new_pos = index >> FREQ_SHIFT;
+ if (pos != new_pos) {
+ pos = new_pos;
+ //Forward the previous sample
+ prevSample[0] = data[0];
+ data++;
+ }
+ Bits diff = data[0] - prevSample[0];
+ Bits diff_mul = index & FREQ_MASK;
+ index += index_add;
+ mixpos &= MIXER_BUFMASK;
+ Bits sample = prevSample[0] + ((diff * diff_mul) >> FREQ_SHIFT);
+ mixer.work[mixpos][0] += sample * volmul[0];
+ mixer.work[mixpos][1] += sample * volmul[1];
+ mixpos++;
+ }
+}
+
+void MixerChannel::AddSamples_m8(Bitu len, const Bit8u * data) {
+ AddSamples<Bit8u,false,false,true>(len,data);
+}
+void MixerChannel::AddSamples_s8(Bitu len,const Bit8u * data) {
+ AddSamples<Bit8u,true,false,true>(len,data);
+}
+void MixerChannel::AddSamples_m8s(Bitu len,const Bit8s * data) {
+ AddSamples<Bit8s,false,true,true>(len,data);
+}
+void MixerChannel::AddSamples_s8s(Bitu len,const Bit8s * data) {
+ AddSamples<Bit8s,true,true,true>(len,data);
+}
+void MixerChannel::AddSamples_m16(Bitu len,const Bit16s * data) {
+ AddSamples<Bit16s,false,true,true>(len,data);
+}
+void MixerChannel::AddSamples_s16(Bitu len,const Bit16s * data) {
+ AddSamples<Bit16s,true,true,true>(len,data);
+}
+void MixerChannel::AddSamples_m16u(Bitu len,const Bit16u * data) {
+ AddSamples<Bit16u,false,false,true>(len,data);
+}
+void MixerChannel::AddSamples_s16u(Bitu len,const Bit16u * data) {
+ AddSamples<Bit16u,true,false,true>(len,data);
+}
+void MixerChannel::AddSamples_m32(Bitu len,const Bit32s * data) {
+ AddSamples<Bit32s,false,true,true>(len,data);
+}
+void MixerChannel::AddSamples_s32(Bitu len,const Bit32s * data) {
+ AddSamples<Bit32s,true,true,true>(len,data);
+}
+void MixerChannel::AddSamples_m16_nonnative(Bitu len,const Bit16s * data) {
+ AddSamples<Bit16s,false,true,false>(len,data);
+}
+void MixerChannel::AddSamples_s16_nonnative(Bitu len,const Bit16s * data) {
+ AddSamples<Bit16s,true,true,false>(len,data);
+}
+void MixerChannel::AddSamples_m16u_nonnative(Bitu len,const Bit16u * data) {
+ AddSamples<Bit16u,false,false,false>(len,data);
+}
+void MixerChannel::AddSamples_s16u_nonnative(Bitu len,const Bit16u * data) {
+ AddSamples<Bit16u,true,false,false>(len,data);
+}
+void MixerChannel::AddSamples_m32_nonnative(Bitu len,const Bit32s * data) {
+ AddSamples<Bit32s,false,true,false>(len,data);
+}
+void MixerChannel::AddSamples_s32_nonnative(Bitu len,const Bit32s * data) {
+ AddSamples<Bit32s,true,true,false>(len,data);
+}
+
+void MixerChannel::FillUp(void) {
+ SDL_LockAudio();
+ if (!enabled || done<mixer.done) {
+ SDL_UnlockAudio();
+ return;
+ }
+ float index=PIC_TickIndex();
+ Mix((Bitu)(index*mixer.needed));
+ SDL_UnlockAudio();
+}
+
+extern bool ticksLocked;
+static inline bool Mixer_irq_important(void) {
+ /* In some states correct timing of the irqs is more important then
+ * non stuttering audo */
+ return (ticksLocked || (CaptureState & (CAPTURE_WAVE|CAPTURE_VIDEO)));
+}
+
+static Bit32u calc_tickadd(Bit32u freq) {
+#if TICK_SHIFT >16
+ Bit64u freq64 = static_cast<Bit64u>(freq);
+ freq64 = (freq64<<TICK_SHIFT)/1000;
+ Bit32u r = static_cast<Bit32u>(freq64);
+ return r;
+#else
+ return (freq<<TICK_SHIFT)/1000;
+#endif
+}
+
+/* Mix a certain amount of new samples */
+static void MIXER_MixData(Bitu needed) {
+ MixerChannel * chan=mixer.channels;
+ while (chan) {
+ chan->Mix(needed);
+ chan=chan->next;
+ }
+ if (CaptureState & (CAPTURE_WAVE|CAPTURE_VIDEO)) {
+ Bit16s convert[1024][2];
+ Bitu added=needed-mixer.done;
+ if (added>1024)
+ added=1024;
+ Bitu readpos=(mixer.pos+mixer.done)&MIXER_BUFMASK;
+ for (Bitu i=0;i<added;i++) {
+ Bits sample=mixer.work[readpos][0] >> MIXER_VOLSHIFT;
+ convert[i][0]=MIXER_CLIP(sample);
+ sample=mixer.work[readpos][1] >> MIXER_VOLSHIFT;
+ convert[i][1]=MIXER_CLIP(sample);
+ readpos=(readpos+1)&MIXER_BUFMASK;
+ }
+ CAPTURE_AddWave( mixer.freq, added, (Bit16s*)convert );
+ }
+ //Reset the the tick_add for constant speed
+ if( Mixer_irq_important() )
+ mixer.tick_add = calc_tickadd(mixer.freq);
+ mixer.done = needed;
+}
+
+static void MIXER_Mix(void) {
+ SDL_LockAudio();
+ MIXER_MixData(mixer.needed);
+ mixer.tick_counter += mixer.tick_add;
+ mixer.needed+=(mixer.tick_counter >> TICK_SHIFT);
+ mixer.tick_counter &= TICK_MASK;
+ SDL_UnlockAudio();
+}
+
+static void MIXER_Mix_NoSound(void) {
+ MIXER_MixData(mixer.needed);
+ /* Clear piece we've just generated */
+ for (Bitu i=0;i<mixer.needed;i++) {
+ mixer.work[mixer.pos][0]=0;
+ mixer.work[mixer.pos][1]=0;
+ mixer.pos=(mixer.pos+1)&MIXER_BUFMASK;
+ }
+ /* Reduce count in channels */
+ for (MixerChannel * chan=mixer.channels;chan;chan=chan->next) {
+ if (chan->done>mixer.needed) chan->done-=mixer.needed;
+ else chan->done=0;
+ }
+ /* Set values for next tick */
+ mixer.tick_counter += mixer.tick_add;
+ mixer.needed = (mixer.tick_counter >> TICK_SHIFT);
+ mixer.tick_counter &= TICK_MASK;
+ mixer.done=0;
+}
+
+static void SDLCALL MIXER_CallBack(void * userdata, Uint8 *stream, int len) {
+ Bitu need=(Bitu)len/MIXER_SSIZE;
+ Bit16s * output=(Bit16s *)stream;
+ Bitu reduce;
+ Bitu pos;
+ //Local resampling counter to manipulate the data when sending it off to the callback
+ Bitu index, index_add;
+ Bits sample;
+ /* Enough room in the buffer ? */
+ if (mixer.done < need) {
+// LOG_MSG("Full underrun need %d, have %d, min %d", need, mixer.done, mixer.min_needed);
+ if((need - mixer.done) > (need >>7) ) //Max 1 procent stretch.
+ return;
+ reduce = mixer.done;
+ index_add = (reduce << TICK_SHIFT) / need;
+ mixer.tick_add = calc_tickadd(mixer.freq+mixer.min_needed);
+ } else if (mixer.done < mixer.max_needed) {
+ Bitu left = mixer.done - need;
+ if (left < mixer.min_needed) {
+ if( !Mixer_irq_important() ) {
+ Bitu needed = mixer.needed - need;
+ Bitu diff = (mixer.min_needed>needed?mixer.min_needed:needed) - left;
+ mixer.tick_add = calc_tickadd(mixer.freq+(diff*3));
+ left = 0; //No stretching as we compensate with the tick_add value
+ } else {
+ left = (mixer.min_needed - left);
+ left = 1 + (2*left) / mixer.min_needed; //left=1,2,3
+ }
+// LOG_MSG("needed underrun need %d, have %d, min %d, left %d", need, mixer.done, mixer.min_needed, left);
+ reduce = need - left;
+ index_add = (reduce << TICK_SHIFT) / need;
+ } else {
+ reduce = need;
+ index_add = (1 << TICK_SHIFT);
+// LOG_MSG("regular run need %d, have %d, min %d, left %d", need, mixer.done, mixer.min_needed, left);
+
+ /* Mixer tick value being updated:
+ * 3 cases:
+ * 1) A lot too high. >division by 5. but maxed by 2* min to prevent too fast drops.
+ * 2) A little too high > division by 8
+ * 3) A little to nothing above the min_needed buffer > go to default value
+ */
+ Bitu diff = left - mixer.min_needed;
+ if(diff > (mixer.min_needed<<1)) diff = mixer.min_needed<<1;
+ if(diff > (mixer.min_needed>>1))
+ mixer.tick_add = calc_tickadd(mixer.freq-(diff/5));
+ else if (diff > (mixer.min_needed>>2))
+ mixer.tick_add = calc_tickadd(mixer.freq-(diff>>3));
+ else
+ mixer.tick_add = calc_tickadd(mixer.freq);
+ }
+ } else {
+ /* There is way too much data in the buffer */
+// LOG_MSG("overflow run need %d, have %d, min %d", need, mixer.done, mixer.min_needed);
+ if (mixer.done > MIXER_BUFSIZE)
+ index_add = MIXER_BUFSIZE - 2*mixer.min_needed;
+ else
+ index_add = mixer.done - 2*mixer.min_needed;
+ index_add = (index_add << TICK_SHIFT) / need;
+ reduce = mixer.done - 2* mixer.min_needed;
+ mixer.tick_add = calc_tickadd(mixer.freq-(mixer.min_needed/5));
+ }
+ /* Reduce done count in all channels */
+ for (MixerChannel * chan=mixer.channels;chan;chan=chan->next) {
+ if (chan->done>reduce) chan->done-=reduce;
+ else chan->done=0;
+ }
+
+ // Reset mixer.tick_add when irqs are important
+ if( Mixer_irq_important() )
+ mixer.tick_add = calc_tickadd(mixer.freq);
+
+ mixer.done -= reduce;
+ mixer.needed -= reduce;
+ pos = mixer.pos;
+ mixer.pos = (mixer.pos + reduce) & MIXER_BUFMASK;
+ index = 0;
+ if(need != reduce) {
+ while (need--) {
+ Bitu i = (pos + (index >> TICK_SHIFT )) & MIXER_BUFMASK;
+ index += index_add;
+ sample=mixer.work[i][0]>>MIXER_VOLSHIFT;
+ *output++=MIXER_CLIP(sample);
+ sample=mixer.work[i][1]>>MIXER_VOLSHIFT;
+ *output++=MIXER_CLIP(sample);
+ }
+ /* Clean the used buffer */
+ while (reduce--) {
+ pos &= MIXER_BUFMASK;
+ mixer.work[pos][0]=0;
+ mixer.work[pos][1]=0;
+ pos++;
+ }
+ } else {
+ while (reduce--) {
+ pos &= MIXER_BUFMASK;
+ sample=mixer.work[pos][0]>>MIXER_VOLSHIFT;
+ *output++=MIXER_CLIP(sample);
+ sample=mixer.work[pos][1]>>MIXER_VOLSHIFT;
+ *output++=MIXER_CLIP(sample);
+ mixer.work[pos][0]=0;
+ mixer.work[pos][1]=0;
+ pos++;
+ }
+ }
+}
+
+static void MIXER_Stop(Section* sec) {
+}
+
+class MIXER : public Program {
+public:
+ void MakeVolume(char * scan,float & vol0,float & vol1) {
+ Bitu w=0;
+ bool db=(toupper(*scan)=='D');
+ if (db) scan++;
+ while (*scan) {
+ if (*scan==':') {
+ ++scan;w=1;
+ }
+ char * before=scan;
+ float val=(float)strtod(scan,&scan);
+ if (before==scan) {
+ ++scan;continue;
+ }
+ if (!db) val/=100;
+ else val=powf(10.0f,(float)val/20.0f);
+ if (val<0) val=1.0f;
+ if (!w) {
+ vol0=val;
+ } else {
+ vol1=val;
+ }
+ }
+ if (!w) vol1=vol0;
+ }
+
+ void Run(void) {
+ if(cmd->FindExist("/LISTMIDI")) {
+ ListMidi();
+ return;
+ }
+ if (cmd->FindString("MASTER",temp_line,false)) {
+ MakeVolume((char *)temp_line.c_str(),mixer.mastervol[0],mixer.mastervol[1]);
+ }
+ MixerChannel * chan=mixer.channels;
+ while (chan) {
+ if (cmd->FindString(chan->name,temp_line,false)) {
+ MakeVolume((char *)temp_line.c_str(),chan->volmain[0],chan->volmain[1]);
+ }
+ chan->UpdateVolume();
+ chan=chan->next;
+ }
+ if (cmd->FindExist("/NOSHOW")) return;
+ chan=mixer.channels;
+ WriteOut("Channel Main Main(dB)\n");
+ ShowVolume("MASTER",mixer.mastervol[0],mixer.mastervol[1]);
+ for (chan=mixer.channels;chan;chan=chan->next)
+ ShowVolume(chan->name,chan->volmain[0],chan->volmain[1]);
+ }
+private:
+ void ShowVolume(const char * name,float vol0,float vol1) {
+ WriteOut("%-8s %3.0f:%-3.0f %+3.2f:%-+3.2f \n",name,
+ vol0*100,vol1*100,
+ 20*log(vol0)/log(10.0f),20*log(vol1)/log(10.0f)
+ );
+ }
+
+ void ListMidi(){
+ if(midi.handler) midi.handler->ListAll(this);
+ };
+
+};
+
+static void MIXER_ProgramStart(Program * * make) {
+ *make=new MIXER;
+}
+
+MixerChannel* MixerObject::Install(MIXER_Handler handler,Bitu freq,const char * name){
+ if(!installed) {
+ if(strlen(name) > 31) E_Exit("Too long mixer channel name");
+ safe_strncpy(m_name,name,32);
+ installed = true;
+ return MIXER_AddChannel(handler,freq,name);
+ } else {
+ E_Exit("already added mixer channel.");
+ return 0; //Compiler happy
+ }
+}
+
+MixerObject::~MixerObject(){
+ if(!installed) return;
+ MIXER_DelChannel(MIXER_FindChannel(m_name));
+}
+
+
+void MIXER_Init(Section* sec) {
+ sec->AddDestroyFunction(&MIXER_Stop);
+
+ Section_prop * section=static_cast<Section_prop *>(sec);
+ /* Read out config section */
+ mixer.freq=section->Get_int("rate");
+ mixer.nosound=section->Get_bool("nosound");
+ mixer.blocksize=section->Get_int("blocksize");
+
+ /* Initialize the internal stuff */
+ mixer.channels=0;
+ mixer.pos=0;
+ mixer.done=0;
+ memset(mixer.work,0,sizeof(mixer.work));
+ mixer.mastervol[0]=1.0f;
+ mixer.mastervol[1]=1.0f;
+
+ /* Start the Mixer using SDL Sound at 22 khz */
+ SDL_AudioSpec spec;
+ SDL_AudioSpec obtained;
+
+ spec.freq=mixer.freq;
+ spec.format=AUDIO_S16SYS;
+ spec.channels=2;
+ spec.callback=MIXER_CallBack;
+ spec.userdata=NULL;
+ spec.samples=(Uint16)mixer.blocksize;
+
+ mixer.tick_counter=0;
+ if (mixer.nosound) {
+ LOG_MSG("MIXER: No Sound Mode Selected.");
+ mixer.tick_add=calc_tickadd(mixer.freq);
+ TIMER_AddTickHandler(MIXER_Mix_NoSound);
+ } else if (SDL_OpenAudio(&spec, &obtained) <0 ) {
+ mixer.nosound = true;
+ LOG_MSG("MIXER: Can't open audio: %s , running in nosound mode.",SDL_GetError());
+ mixer.tick_add=calc_tickadd(mixer.freq);
+ TIMER_AddTickHandler(MIXER_Mix_NoSound);
+ } else {
+ if((mixer.freq != obtained.freq) || (mixer.blocksize != obtained.samples))
+ LOG_MSG("MIXER: Got different values from SDL: freq %d, blocksize %d",obtained.freq,obtained.samples);
+ mixer.freq=obtained.freq;
+ mixer.blocksize=obtained.samples;
+ mixer.tick_add=calc_tickadd(mixer.freq);
+ TIMER_AddTickHandler(MIXER_Mix);
+ SDL_PauseAudio(0);
+ }
+ mixer.min_needed=section->Get_int("prebuffer");
+ if (mixer.min_needed>100) mixer.min_needed=100;
+ mixer.min_needed=(mixer.freq*mixer.min_needed)/1000;
+ mixer.max_needed=mixer.blocksize * 2 + 2*mixer.min_needed;
+ mixer.needed=mixer.min_needed+1;
+ PROGRAMS_MakeFile("MIXER.COM",MIXER_ProgramStart);
+}
diff --git a/src/hardware/mpu401.cpp b/src/hardware/mpu401.cpp
new file mode 100644
index 000000000..4afa56910
--- /dev/null
+++ b/src/hardware/mpu401.cpp
@@ -0,0 +1,676 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include "dosbox.h"
+#include "inout.h"
+#include "pic.h"
+#include "setup.h"
+#include "cpu.h"
+#include "support.h"
+
+void MIDI_RawOutByte(Bit8u data);
+bool MIDI_Available(void);
+
+static void MPU401_Event(Bitu);
+static void MPU401_Reset(void);
+static void MPU401_ResetDone(Bitu);
+static void MPU401_EOIHandler(Bitu val=0);
+static void MPU401_EOIHandlerDispatch(void);
+
+#define MPU401_VERSION 0x15
+#define MPU401_REVISION 0x01
+#define MPU401_QUEUE 32
+#define MPU401_TIMECONSTANT (60000000/1000.0f)
+#define MPU401_RESETBUSY 14.0f
+
+enum MpuMode { M_UART,M_INTELLIGENT };
+enum MpuDataType {T_OVERFLOW,T_MARK,T_MIDI_SYS,T_MIDI_NORM,T_COMMAND};
+
+static void MPU401_WriteData(Bitu port,Bitu val,Bitu iolen);
+
+/* Messages sent to MPU-401 from host */
+#define MSG_EOX 0xf7
+#define MSG_OVERFLOW 0xf8
+#define MSG_MARK 0xfc
+
+/* Messages sent to host from MPU-401 */
+#define MSG_MPU_OVERFLOW 0xf8
+#define MSG_MPU_COMMAND_REQ 0xf9
+#define MSG_MPU_END 0xfc
+#define MSG_MPU_CLOCK 0xfd
+#define MSG_MPU_ACK 0xfe
+
+static struct {
+ bool intelligent;
+ MpuMode mode;
+ Bitu irq;
+ Bit8u queue[MPU401_QUEUE];
+ Bitu queue_pos,queue_used;
+ struct track {
+ Bits counter;
+ Bit8u value[8],sys_val;
+ Bit8u vlength,length;
+ MpuDataType type;
+ } playbuf[8],condbuf;
+ struct {
+ bool conductor,cond_req,cond_set, block_ack;
+ bool playing,reset;
+ bool wsd,wsm,wsd_start;
+ bool run_irq,irq_pending;
+ bool send_now;
+ bool eoi_scheduled;
+ Bits data_onoff;
+ Bitu command_byte,cmd_pending;
+ Bit8u tmask,cmask,amask;
+ Bit16u midi_mask;
+ Bit16u req_mask;
+ Bit8u channel,old_chan;
+ } state;
+ struct {
+ Bit8u timebase,old_timebase;
+ Bit8u tempo,old_tempo;
+ Bit8u tempo_rel,old_tempo_rel;
+ Bit8u tempo_grad;
+ Bit8u cth_rate,cth_counter;
+ bool clock_to_host,cth_active;
+ } clock;
+} mpu;
+
+
+static void QueueByte(Bit8u data) {
+ if (mpu.state.block_ack) {mpu.state.block_ack=false;return;}
+ if (mpu.queue_used==0 && mpu.intelligent) {
+ mpu.state.irq_pending=true;
+ PIC_ActivateIRQ(mpu.irq);
+ }
+ if (mpu.queue_used<MPU401_QUEUE) {
+ Bitu pos=mpu.queue_used+mpu.queue_pos;
+ if (mpu.queue_pos>=MPU401_QUEUE) mpu.queue_pos-=MPU401_QUEUE;
+ if (pos>=MPU401_QUEUE) pos-=MPU401_QUEUE;
+ mpu.queue_used++;
+ mpu.queue[pos]=data;
+ } else LOG(LOG_MISC,LOG_NORMAL)("MPU401:Data queue full");
+}
+
+static void ClrQueue(void) {
+ mpu.queue_used=0;
+ mpu.queue_pos=0;
+}
+
+static Bitu MPU401_ReadStatus(Bitu port,Bitu iolen) {
+ Bit8u ret=0x3f; /* Bits 6 and 7 clear */
+ if (mpu.state.cmd_pending) ret|=0x40;
+ if (!mpu.queue_used) ret|=0x80;
+ return ret;
+}
+
+static void MPU401_WriteCommand(Bitu port,Bitu val,Bitu iolen) {
+ if (mpu.mode==M_UART && val!=0xff) return;
+ if (mpu.state.reset) {
+ if (mpu.state.cmd_pending || val!=0xff) {
+ mpu.state.cmd_pending=val+1;
+ return;
+ }
+ PIC_RemoveEvents(MPU401_ResetDone);
+ mpu.state.reset=false;
+ }
+ if (val<=0x2f) {
+ switch (val&3) { /* MIDI stop, start, continue */
+ case 1: {MIDI_RawOutByte(0xfc);break;}
+ case 2: {MIDI_RawOutByte(0xfa);break;}
+ case 3: {MIDI_RawOutByte(0xfb);break;}
+ }
+ if (val&0x20) LOG(LOG_MISC,LOG_ERROR)("MPU-401:Unhandled Recording Command %x",val);
+ switch (val&0xc) {
+ case 0x4: /* Stop */
+ PIC_RemoveEvents(MPU401_Event);
+ mpu.state.playing=false;
+ for (Bitu i=0xb0;i<0xbf;i++) { /* All notes off */
+ MIDI_RawOutByte(i);
+ MIDI_RawOutByte(0x7b);
+ MIDI_RawOutByte(0);
+ }
+ break;
+ case 0x8: /* Play */
+ LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Intelligent mode playback started");
+ mpu.state.playing=true;
+ PIC_RemoveEvents(MPU401_Event);
+ PIC_AddEvent(MPU401_Event,MPU401_TIMECONSTANT/(mpu.clock.tempo*mpu.clock.timebase));
+ ClrQueue();
+ break;
+ }
+ }
+ else if (val>=0xa0 && val<=0xa7) { /* Request play counter */
+ if (mpu.state.cmask&(1<<(val&7))) QueueByte(mpu.playbuf[val&7].counter);
+ }
+ else if (val>=0xd0 && val<=0xd7) { /* Send data */
+ mpu.state.old_chan=mpu.state.channel;
+ mpu.state.channel=val&7;
+ mpu.state.wsd=true;
+ mpu.state.wsm=false;
+ mpu.state.wsd_start=true;
+ }
+ else
+ switch (val) {
+ case 0xdf: /* Send system message */
+ mpu.state.wsd=false;
+ mpu.state.wsm=true;
+ mpu.state.wsd_start=true;
+ break;
+ case 0x8e: /* Conductor */
+ mpu.state.cond_set=false;
+ break;
+ case 0x8f:
+ mpu.state.cond_set=true;
+ break;
+ case 0x94: /* Clock to host */
+ mpu.clock.clock_to_host=false;
+ break;
+ case 0x95:
+ mpu.clock.clock_to_host=true;
+ break;
+ case 0xc2: /* Internal timebase */
+ mpu.clock.timebase=48;
+ break;
+ case 0xc3:
+ mpu.clock.timebase=72;
+ break;
+ case 0xc4:
+ mpu.clock.timebase=96;
+ break;
+ case 0xc5:
+ mpu.clock.timebase=120;
+ break;
+ case 0xc6:
+ mpu.clock.timebase=144;
+ break;
+ case 0xc7:
+ mpu.clock.timebase=168;
+ break;
+ case 0xc8:
+ mpu.clock.timebase=192;
+ break;
+ /* Commands with data byte */
+ case 0xe0: case 0xe1: case 0xe2: case 0xe4: case 0xe6:
+ case 0xe7: case 0xec: case 0xed: case 0xee: case 0xef:
+ mpu.state.command_byte=val;
+ break;
+ /* Commands 0xa# returning data */
+ case 0xab: /* Request and clear recording counter */
+ QueueByte(MSG_MPU_ACK);
+ QueueByte(0);
+ return;
+ case 0xac: /* Request version */
+ QueueByte(MSG_MPU_ACK);
+ QueueByte(MPU401_VERSION);
+ return;
+ case 0xad: /* Request revision */
+ QueueByte(MSG_MPU_ACK);
+ QueueByte(MPU401_REVISION);
+ return;
+ case 0xaf: /* Request tempo */
+ QueueByte(MSG_MPU_ACK);
+ QueueByte(mpu.clock.tempo);
+ return;
+ case 0xb1: /* Reset relative tempo */
+ mpu.clock.tempo_rel=40;
+ break;
+ case 0xb9: /* Clear play map */
+ case 0xb8: /* Clear play counters */
+ for (Bitu i=0xb0;i<0xbf;i++) { /* All notes off */
+ MIDI_RawOutByte(i);
+ MIDI_RawOutByte(0x7b);
+ MIDI_RawOutByte(0);
+ }
+ for (Bitu i=0;i<8;i++) {
+ mpu.playbuf[i].counter=0;
+ mpu.playbuf[i].type=T_OVERFLOW;
+ }
+ mpu.condbuf.counter=0;
+ mpu.condbuf.type=T_OVERFLOW;
+ if (!(mpu.state.conductor=mpu.state.cond_set)) mpu.state.cond_req=0;
+ mpu.state.amask=mpu.state.tmask;
+ mpu.state.req_mask=0;
+ mpu.state.irq_pending=true;
+ break;
+ case 0xff: /* Reset MPU-401 */
+ LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Reset %X",val);
+ PIC_AddEvent(MPU401_ResetDone,MPU401_RESETBUSY);
+ mpu.state.reset=true;
+ if (mpu.mode==M_UART) {
+ MPU401_Reset();
+ return; //do not send ack in UART mode
+ }
+ MPU401_Reset();
+ break;
+ case 0x3f: /* UART mode */
+ LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Set UART mode %X",val);
+ mpu.mode=M_UART;
+ break;
+ default:;
+ //LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Unhandled command %X",val);
+ }
+ QueueByte(MSG_MPU_ACK);
+}
+
+static Bitu MPU401_ReadData(Bitu port,Bitu iolen) {
+ Bit8u ret=MSG_MPU_ACK;
+ if (mpu.queue_used) {
+ if (mpu.queue_pos>=MPU401_QUEUE) mpu.queue_pos-=MPU401_QUEUE;
+ ret=mpu.queue[mpu.queue_pos];
+ mpu.queue_pos++;mpu.queue_used--;
+ }
+ if (!mpu.intelligent) return ret;
+
+ if (mpu.queue_used == 0) PIC_DeActivateIRQ(mpu.irq);
+
+ if (ret>=0xf0 && ret<=0xf7) { /* MIDI data request */
+ mpu.state.channel=ret&7;
+ mpu.state.data_onoff=0;
+ mpu.state.cond_req=false;
+ }
+ if (ret==MSG_MPU_COMMAND_REQ) {
+ mpu.state.data_onoff=0;
+ mpu.state.cond_req=true;
+ if (mpu.condbuf.type!=T_OVERFLOW) {
+ mpu.state.block_ack=true;
+ MPU401_WriteCommand(0x331,mpu.condbuf.value[0],1);
+ if (mpu.state.command_byte) MPU401_WriteData(0x330,mpu.condbuf.value[1],1);
+ }
+ mpu.condbuf.type=T_OVERFLOW;
+ }
+ if (ret==MSG_MPU_END || ret==MSG_MPU_CLOCK || ret==MSG_MPU_ACK) {
+ mpu.state.data_onoff=-1;
+ MPU401_EOIHandlerDispatch();
+ }
+ return ret;
+}
+
+static void MPU401_WriteData(Bitu port,Bitu val,Bitu iolen) {
+ if (mpu.mode==M_UART) {MIDI_RawOutByte(val);return;}
+ switch (mpu.state.command_byte) { /* 0xe# command data */
+ case 0x00:
+ break;
+ case 0xe0: /* Set tempo */
+ mpu.state.command_byte=0;
+ mpu.clock.tempo=val;
+ return;
+ case 0xe1: /* Set relative tempo */
+ mpu.state.command_byte=0;
+ if (val!=0x40) //default value
+ LOG(LOG_MISC,LOG_ERROR)("MPU-401:Relative tempo change not implemented");
+ return;
+ case 0xe7: /* Set internal clock to host interval */
+ mpu.state.command_byte=0;
+ mpu.clock.cth_rate=val>>2;
+ return;
+ case 0xec: /* Set active track mask */
+ mpu.state.command_byte=0;
+ mpu.state.tmask=val;
+ return;
+ case 0xed: /* Set play counter mask */
+ mpu.state.command_byte=0;
+ mpu.state.cmask=val;
+ return;
+ case 0xee: /* Set 1-8 MIDI channel mask */
+ mpu.state.command_byte=0;
+ mpu.state.midi_mask&=0xff00;
+ mpu.state.midi_mask|=val;
+ return;
+ case 0xef: /* Set 9-16 MIDI channel mask */
+ mpu.state.command_byte=0;
+ mpu.state.midi_mask&=0x00ff;
+ mpu.state.midi_mask|=((Bit16u)val)<<8;
+ return;
+ //case 0xe2: /* Set graduation for relative tempo */
+ //case 0xe4: /* Set metronome */
+ //case 0xe6: /* Set metronome measure length */
+ default:
+ mpu.state.command_byte=0;
+ return;
+ }
+ static Bitu length,cnt,posd;
+ if (mpu.state.wsd) { /* Directly send MIDI message */
+ if (mpu.state.wsd_start) {
+ mpu.state.wsd_start=0;
+ cnt=0;
+ switch (val&0xf0) {
+ case 0xc0:case 0xd0:
+ mpu.playbuf[mpu.state.channel].value[0]=val;
+ length=2;
+ break;
+ case 0x80:case 0x90:case 0xa0:case 0xb0:case 0xe0:
+ mpu.playbuf[mpu.state.channel].value[0]=val;
+ length=3;
+ break;
+ case 0xf0:
+ LOG(LOG_MISC,LOG_ERROR)("MPU-401:Illegal WSD byte");
+ mpu.state.wsd=0;
+ mpu.state.channel=mpu.state.old_chan;
+ return;
+ default: /* MIDI with running status */
+ cnt++;
+ MIDI_RawOutByte(mpu.playbuf[mpu.state.channel].value[0]);
+ }
+ }
+ if (cnt<length) {MIDI_RawOutByte(val);cnt++;}
+ if (cnt==length) {
+ mpu.state.wsd=0;
+ mpu.state.channel=mpu.state.old_chan;
+ }
+ return;
+ }
+ if (mpu.state.wsm) { /* Directly send system message */
+ if (val==MSG_EOX) {MIDI_RawOutByte(MSG_EOX);mpu.state.wsm=0;return;}
+ if (mpu.state.wsd_start) {
+ mpu.state.wsd_start=0;
+ cnt=0;
+ switch (val) {
+ case 0xf2:{ length=3; break;}
+ case 0xf3:{ length=2; break;}
+ case 0xf6:{ length=1; break;}
+ case 0xf0:{ length=0; break;}
+ default:
+ length=0;
+ }
+ }
+ if (!length || cnt<length) {MIDI_RawOutByte(val);cnt++;}
+ if (cnt==length) mpu.state.wsm=0;
+ return;
+ }
+ if (mpu.state.cond_req) { /* Command */
+ switch (mpu.state.data_onoff) {
+ case -1:
+ return;
+ case 0: /* Timing byte */
+ mpu.condbuf.vlength=0;
+ if (val<0xf0) mpu.state.data_onoff++;
+ else {
+ mpu.state.data_onoff=-1;
+ MPU401_EOIHandlerDispatch();
+ return;
+ }
+ if (val==0) mpu.state.send_now=true;
+ else mpu.state.send_now=false;
+ mpu.condbuf.counter=val;
+ break;
+ case 1: /* Command byte #1 */
+ mpu.condbuf.type=T_COMMAND;
+ if (val==0xf8 || val==0xf9) mpu.condbuf.type=T_OVERFLOW;
+ mpu.condbuf.value[mpu.condbuf.vlength]=val;
+ mpu.condbuf.vlength++;
+ if ((val&0xf0)!=0xe0) MPU401_EOIHandlerDispatch();
+ else mpu.state.data_onoff++;
+ break;
+ case 2:/* Command byte #2 */
+ mpu.condbuf.value[mpu.condbuf.vlength]=val;
+ mpu.condbuf.vlength++;
+ MPU401_EOIHandlerDispatch();
+ break;
+ }
+ return;
+ }
+ switch (mpu.state.data_onoff) { /* Data */
+ case -1:
+ return;
+ case 0: /* Timing byte */
+ if (val<0xf0) mpu.state.data_onoff=1;
+ else {
+ mpu.state.data_onoff=-1;
+ MPU401_EOIHandlerDispatch();
+ return;
+ }
+ if (val==0) mpu.state.send_now=true;
+ else mpu.state.send_now=false;
+ mpu.playbuf[mpu.state.channel].counter=val;
+ break;
+ case 1: /* MIDI */
+ mpu.playbuf[mpu.state.channel].vlength++;
+ posd=mpu.playbuf[mpu.state.channel].vlength;
+ if (posd==1) {
+ switch (val&0xf0) {
+ case 0xf0: /* System message or mark */
+ if (val>0xf7) {
+ mpu.playbuf[mpu.state.channel].type=T_MARK;
+ mpu.playbuf[mpu.state.channel].sys_val=val;
+ length=1;
+ } else {
+ LOG(LOG_MISC,LOG_ERROR)("MPU-401:Illegal message");
+ mpu.playbuf[mpu.state.channel].type=T_MIDI_SYS;
+ mpu.playbuf[mpu.state.channel].sys_val=val;
+ length=1;
+ }
+ break;
+ case 0xc0: case 0xd0: /* MIDI Message */
+ mpu.playbuf[mpu.state.channel].type=T_MIDI_NORM;
+ length=mpu.playbuf[mpu.state.channel].length=2;
+ break;
+ case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0:
+ mpu.playbuf[mpu.state.channel].type=T_MIDI_NORM;
+ length=mpu.playbuf[mpu.state.channel].length=3;
+ break;
+ default: /* MIDI data with running status */
+ posd++;
+ mpu.playbuf[mpu.state.channel].vlength++;
+ mpu.playbuf[mpu.state.channel].type=T_MIDI_NORM;
+ length=mpu.playbuf[mpu.state.channel].length;
+ break;
+ }
+ }
+ if (!(posd==1 && val>=0xf0)) mpu.playbuf[mpu.state.channel].value[posd-1]=val;
+ if (posd==length) MPU401_EOIHandlerDispatch();
+ }
+}
+
+static void MPU401_IntelligentOut(Bit8u chan) {
+ Bitu val;
+ switch (mpu.playbuf[chan].type) {
+ case T_OVERFLOW:
+ break;
+ case T_MARK:
+ val=mpu.playbuf[chan].sys_val;
+ if (val==0xfc) {
+ MIDI_RawOutByte(val);
+ mpu.state.amask&=~(1<<chan);
+ mpu.state.req_mask&=~(1<<chan);
+ }
+ break;
+ case T_MIDI_NORM:
+ for (Bitu i=0;i<mpu.playbuf[chan].vlength;i++)
+ MIDI_RawOutByte(mpu.playbuf[chan].value[i]);
+ break;
+ default:
+ break;
+ }
+}
+
+static void UpdateTrack(Bit8u chan) {
+ MPU401_IntelligentOut(chan);
+ if (mpu.state.amask&(1<<chan)) {
+ mpu.playbuf[chan].vlength=0;
+ mpu.playbuf[chan].type=T_OVERFLOW;
+ mpu.playbuf[chan].counter=0xf0;
+ mpu.state.req_mask|=(1<<chan);
+ } else {
+ if (mpu.state.amask==0 && !mpu.state.conductor) mpu.state.req_mask|=(1<<12);
+ }
+}
+
+static void UpdateConductor(void) {
+ if (mpu.condbuf.value[0]==0xfc) {
+ mpu.condbuf.value[0]=0;
+ mpu.state.conductor=false;
+ mpu.state.req_mask&=~(1<<9);
+ if (mpu.state.amask==0) mpu.state.req_mask|=(1<<12);
+ return;
+ }
+ mpu.condbuf.vlength=0;
+ mpu.condbuf.counter=0xf0;
+ mpu.state.req_mask|=(1<<9);
+}
+
+static void MPU401_Event(Bitu val) {
+ if (mpu.mode==M_UART) return;
+ if (mpu.state.irq_pending) goto next_event;
+ for (Bitu i=0;i<8;i++) { /* Decrease counters */
+ if (mpu.state.amask&(1<<i)) {
+ mpu.playbuf[i].counter--;
+ if (mpu.playbuf[i].counter<=0) UpdateTrack(i);
+ }
+ }
+ if (mpu.state.conductor) {
+ mpu.condbuf.counter--;
+ if (mpu.condbuf.counter<=0) UpdateConductor();
+ }
+ if (mpu.clock.clock_to_host) {
+ mpu.clock.cth_counter++;
+ if (mpu.clock.cth_counter >= mpu.clock.cth_rate) {
+ mpu.clock.cth_counter=0;
+ mpu.state.req_mask|=(1<<13);
+ }
+ }
+ if (!mpu.state.irq_pending && mpu.state.req_mask) MPU401_EOIHandler();
+next_event:
+ Bitu new_time;
+ if ((new_time=mpu.clock.tempo*mpu.clock.timebase)==0) return;
+ PIC_AddEvent(MPU401_Event,MPU401_TIMECONSTANT/new_time);
+}
+
+
+static void MPU401_EOIHandlerDispatch(void) {
+ if (mpu.state.send_now) {
+ mpu.state.eoi_scheduled=true;
+ PIC_AddEvent(MPU401_EOIHandler,0.06f); //Possible a bit longer
+ }
+ else if (!mpu.state.eoi_scheduled) MPU401_EOIHandler();
+}
+
+//Updates counters and requests new data on "End of Input"
+static void MPU401_EOIHandler(Bitu val) {
+ mpu.state.eoi_scheduled=false;
+ if (mpu.state.send_now) {
+ mpu.state.send_now=false;
+ if (mpu.state.cond_req) UpdateConductor();
+ else UpdateTrack(mpu.state.channel);
+ }
+ mpu.state.irq_pending=false;
+ if (!mpu.state.playing || !mpu.state.req_mask) return;
+ Bitu i=0;
+ do {
+ if (mpu.state.req_mask&(1<<i)) {
+ QueueByte(0xf0+i);
+ mpu.state.req_mask&=~(1<<i);
+ break;
+ }
+ } while ((i++)<16);
+}
+
+static void MPU401_ResetDone(Bitu) {
+ mpu.state.reset=false;
+ if (mpu.state.cmd_pending) {
+ MPU401_WriteCommand(0x331,mpu.state.cmd_pending-1,1);
+ mpu.state.cmd_pending=0;
+ }
+}
+static void MPU401_Reset(void) {
+ PIC_DeActivateIRQ(mpu.irq);
+ mpu.mode=(mpu.intelligent ? M_INTELLIGENT : M_UART);
+ PIC_RemoveEvents(MPU401_EOIHandler);
+ mpu.state.eoi_scheduled=false;
+ mpu.state.wsd=false;
+ mpu.state.wsm=false;
+ mpu.state.conductor=false;
+ mpu.state.cond_req=false;
+ mpu.state.cond_set=false;
+ mpu.state.playing=false;
+ mpu.state.run_irq=false;
+ mpu.state.irq_pending=false;
+ mpu.state.cmask=0xff;
+ mpu.state.amask=mpu.state.tmask=0;
+ mpu.state.midi_mask=0xffff;
+ mpu.state.data_onoff=-1;
+ mpu.state.command_byte=0;
+ mpu.state.block_ack=false;
+ mpu.clock.tempo=mpu.clock.old_tempo=100;
+ mpu.clock.timebase=mpu.clock.old_timebase=120;
+ mpu.clock.tempo_rel=mpu.clock.old_tempo_rel=40;
+ mpu.clock.tempo_grad=0;
+ mpu.clock.clock_to_host=false;
+ mpu.clock.cth_rate=60;
+ mpu.clock.cth_counter=0;
+ ClrQueue();
+ mpu.state.req_mask=0;
+ mpu.condbuf.counter=0;
+ mpu.condbuf.type=T_OVERFLOW;
+ for (Bitu i=0;i<8;i++) {mpu.playbuf[i].type=T_OVERFLOW;mpu.playbuf[i].counter=0;}
+}
+
+class MPU401:public Module_base{
+private:
+ IO_ReadHandleObject ReadHandler[2];
+ IO_WriteHandleObject WriteHandler[2];
+ bool installed; /*as it can fail to install by 2 ways (config and no midi)*/
+public:
+ MPU401(Section* configuration):Module_base(configuration){
+ installed = false;
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+ const char* s_mpu = section->Get_string("mpu401");
+ if(strcasecmp(s_mpu,"none") == 0) return;
+ if(strcasecmp(s_mpu,"off") == 0) return;
+ if(strcasecmp(s_mpu,"false") == 0) return;
+ if (!MIDI_Available()) return;
+ /*Enabled and there is a Midi */
+ installed = true;
+
+ WriteHandler[0].Install(0x330,&MPU401_WriteData,IO_MB);
+ WriteHandler[1].Install(0x331,&MPU401_WriteCommand,IO_MB);
+ ReadHandler[0].Install(0x330,&MPU401_ReadData,IO_MB);
+ ReadHandler[1].Install(0x331,&MPU401_ReadStatus,IO_MB);
+
+ mpu.queue_used=0;
+ mpu.queue_pos=0;
+ mpu.mode=M_UART;
+ mpu.irq=9; /* Princess Maker 2 wants it on irq 9 */
+
+ mpu.intelligent = true; //Default is on
+ if(strcasecmp(s_mpu,"uart") == 0) mpu.intelligent = false;
+ if (!mpu.intelligent) return;
+ /*Set IRQ and unmask it(for timequest/princess maker 2) */
+ PIC_SetIRQMask(mpu.irq,false);
+ MPU401_Reset();
+ }
+ ~MPU401(){
+ if(!installed) return;
+ Section_prop * section=static_cast<Section_prop *>(m_configuration);
+ if(strcasecmp(section->Get_string("mpu401"),"intelligent")) return;
+ PIC_SetIRQMask(mpu.irq,true);
+ }
+};
+
+static MPU401* test;
+
+void MPU401_Destroy(Section* sec){
+ delete test;
+}
+
+void MPU401_Init(Section* sec) {
+ test = new MPU401(sec);
+ sec->AddDestroyFunction(&MPU401_Destroy,true);
+}
diff --git a/src/hardware/opl.cpp b/src/hardware/opl.cpp
new file mode 100644
index 000000000..a8f21f71b
--- /dev/null
+++ b/src/hardware/opl.cpp
@@ -0,0 +1,1462 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ * OPL2/OPL3 emulation library
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+/*
+ * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman
+ * Copyright (C) 1998-2001 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ */
+
+
+#include <math.h>
+#include <stdlib.h> // rand()
+#include <string.h> // memset()
+#include "dosbox.h"
+#include "opl.h"
+
+
+static fltype recipsamp; // inverse of sampling rate
+static Bit16s wavtable[WAVEPREC*3]; // wave form table
+
+// vibrato/tremolo tables
+static Bit32s vib_table[VIBTAB_SIZE];
+static Bit32s trem_table[TREMTAB_SIZE*2];
+
+static Bit32s vibval_const[BLOCKBUF_SIZE];
+static Bit32s tremval_const[BLOCKBUF_SIZE];
+
+// vibrato value tables (used per-operator)
+static Bit32s vibval_var1[BLOCKBUF_SIZE];
+static Bit32s vibval_var2[BLOCKBUF_SIZE];
+//static Bit32s vibval_var3[BLOCKBUF_SIZE];
+//static Bit32s vibval_var4[BLOCKBUF_SIZE];
+
+// vibrato/trmolo value table pointers
+static Bit32s *vibval1, *vibval2, *vibval3, *vibval4;
+static Bit32s *tremval1, *tremval2, *tremval3, *tremval4;
+
+
+// key scale level lookup table
+static const fltype kslmul[4] = {
+ 0.0, 0.5, 0.25, 1.0 // -> 0, 3, 1.5, 6 dB/oct
+};
+
+// frequency multiplicator lookup table
+static const fltype frqmul_tab[16] = {
+ 0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15
+};
+// calculated frequency multiplication values (depend on sampling rate)
+static fltype frqmul[16];
+
+// key scale levels
+static Bit8u kslev[8][16];
+
+// map a channel number to the register offset of the modulator (=register base)
+static const Bit8u modulatorbase[9] = {
+ 0,1,2,
+ 8,9,10,
+ 16,17,18
+};
+
+// map a register base to a modulator operator number or operator number
+#if defined(OPLTYPE_IS_OPL3)
+static const Bit8u regbase2modop[44] = {
+ 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8, // first set
+ 18,19,20,18,19,20,0,0,21,22,23,21,22,23,0,0,24,25,26,24,25,26 // second set
+};
+static const Bit8u regbase2op[44] = {
+ 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17, // first set
+ 18,19,20,27,28,29,0,0,21,22,23,30,31,32,0,0,24,25,26,33,34,35 // second set
+};
+#else
+static const Bit8u regbase2modop[22] = {
+ 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8
+};
+static const Bit8u regbase2op[22] = {
+ 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17
+};
+#endif
+
+
+// start of the waveform
+static Bit32u waveform[8] = {
+ WAVEPREC,
+ WAVEPREC>>1,
+ WAVEPREC,
+ (WAVEPREC*3)>>2,
+ 0,
+ 0,
+ (WAVEPREC*5)>>2,
+ WAVEPREC<<1
+};
+
+// length of the waveform as mask
+static Bit32u wavemask[8] = {
+ WAVEPREC-1,
+ WAVEPREC-1,
+ (WAVEPREC>>1)-1,
+ (WAVEPREC>>1)-1,
+ WAVEPREC-1,
+ ((WAVEPREC*3)>>2)-1,
+ WAVEPREC>>1,
+ WAVEPREC-1
+};
+
+// where the first entry resides
+static Bit32u wavestart[8] = {
+ 0,
+ WAVEPREC>>1,
+ 0,
+ WAVEPREC>>2,
+ 0,
+ 0,
+ 0,
+ WAVEPREC>>3
+};
+
+// envelope generator function constants
+static fltype attackconst[4] = {
+ (fltype)(1/2.82624),
+ (fltype)(1/2.25280),
+ (fltype)(1/1.88416),
+ (fltype)(1/1.59744)
+};
+static fltype decrelconst[4] = {
+ (fltype)(1/39.28064),
+ (fltype)(1/31.41608),
+ (fltype)(1/26.17344),
+ (fltype)(1/22.44608)
+};
+
+
+void operator_advance(op_type* op_pt, Bit32s vib) {
+ op_pt->wfpos = op_pt->tcount; // waveform position
+
+ // advance waveform time
+ op_pt->tcount += op_pt->tinc;
+ op_pt->tcount += (Bit32s)(op_pt->tinc)*vib/FIXEDPT;
+
+ op_pt->generator_pos += generator_add;
+}
+
+void operator_advance_drums(op_type* op_pt1, Bit32s vib1, op_type* op_pt2, Bit32s vib2, op_type* op_pt3, Bit32s vib3) {
+ Bit32u c1 = op_pt1->tcount/FIXEDPT;
+ Bit32u c3 = op_pt3->tcount/FIXEDPT;
+ Bit32u phasebit = (((c1 & 0x88) ^ ((c1<<5) & 0x80)) | ((c3 ^ (c3<<2)) & 0x20)) ? 0x02 : 0x00;
+
+ Bit32u noisebit = rand()&1;
+
+ Bit32u snare_phase_bit = (((Bitu)((op_pt1->tcount/FIXEDPT) / 0x100))&1);
+
+ //Hihat
+ Bit32u inttm = (phasebit<<8) | (0x34<<(phasebit ^ (noisebit<<1)));
+ op_pt1->wfpos = inttm*FIXEDPT; // waveform position
+ // advance waveform time
+ op_pt1->tcount += op_pt1->tinc;
+ op_pt1->tcount += (Bit32s)(op_pt1->tinc)*vib1/FIXEDPT;
+ op_pt1->generator_pos += generator_add;
+
+ //Snare
+ inttm = ((1+snare_phase_bit) ^ noisebit)<<8;
+ op_pt2->wfpos = inttm*FIXEDPT; // waveform position
+ // advance waveform time
+ op_pt2->tcount += op_pt2->tinc;
+ op_pt2->tcount += (Bit32s)(op_pt2->tinc)*vib2/FIXEDPT;
+ op_pt2->generator_pos += generator_add;
+
+ //Cymbal
+ inttm = (1+phasebit)<<8;
+ op_pt3->wfpos = inttm*FIXEDPT; // waveform position
+ // advance waveform time
+ op_pt3->tcount += op_pt3->tinc;
+ op_pt3->tcount += (Bit32s)(op_pt3->tinc)*vib3/FIXEDPT;
+ op_pt3->generator_pos += generator_add;
+}
+
+
+// output level is sustained, mode changes only when operator is turned off (->release)
+// or when the keep-sustained bit is turned off (->sustain_nokeep)
+void operator_output(op_type* op_pt, Bit32s modulator, Bit32s trem) {
+ if (op_pt->op_state != OF_TYPE_OFF) {
+ op_pt->lastcval = op_pt->cval;
+ Bit32u i = (Bit32u)((op_pt->wfpos+modulator)/FIXEDPT);
+
+ // wform: -16384 to 16383 (0x4000)
+ // trem : 32768 to 65535 (0x10000)
+ // step_amp: 0.0 to 1.0
+ // vol : 1/2^14 to 1/2^29 (/0x4000; /1../0x8000)
+
+ op_pt->cval = (Bit32s)(op_pt->step_amp*op_pt->vol*op_pt->cur_wform[i&op_pt->cur_wmask]*trem/16.0);
+ }
+}
+
+
+// no action, operator is off
+void operator_off(op_type* /*op_pt*/) {
+}
+
+// output level is sustained, mode changes only when operator is turned off (->release)
+// or when the keep-sustained bit is turned off (->sustain_nokeep)
+void operator_sustain(op_type* op_pt) {
+ Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples
+ for (Bit32u ct=0; ct<num_steps_add; ct++) {
+ op_pt->cur_env_step++;
+ }
+ op_pt->generator_pos -= num_steps_add*FIXEDPT;
+}
+
+// operator in release mode, if output level reaches zero the operator is turned off
+void operator_release(op_type* op_pt) {
+ // ??? boundary?
+ if (op_pt->amp > 0.00000001) {
+ // release phase
+ op_pt->amp *= op_pt->releasemul;
+ }
+
+ Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples
+ for (Bit32u ct=0; ct<num_steps_add; ct++) {
+ op_pt->cur_env_step++; // sample counter
+ if ((op_pt->cur_env_step & op_pt->env_step_r)==0) {
+ if (op_pt->amp <= 0.00000001) {
+ // release phase finished, turn off this operator
+ op_pt->amp = 0.0;
+ if (op_pt->op_state == OF_TYPE_REL) {
+ op_pt->op_state = OF_TYPE_OFF;
+ }
+ }
+ op_pt->step_amp = op_pt->amp;
+ }
+ }
+ op_pt->generator_pos -= num_steps_add*FIXEDPT;
+}
+
+// operator in decay mode, if sustain level is reached the output level is either
+// kept (sustain level keep enabled) or the operator is switched into release mode
+void operator_decay(op_type* op_pt) {
+ if (op_pt->amp > op_pt->sustain_level) {
+ // decay phase
+ op_pt->amp *= op_pt->decaymul;
+ }
+
+ Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples
+ for (Bit32u ct=0; ct<num_steps_add; ct++) {
+ op_pt->cur_env_step++;
+ if ((op_pt->cur_env_step & op_pt->env_step_d)==0) {
+ if (op_pt->amp <= op_pt->sustain_level) {
+ // decay phase finished, sustain level reached
+ if (op_pt->sus_keep) {
+ // keep sustain level (until turned off)
+ op_pt->op_state = OF_TYPE_SUS;
+ op_pt->amp = op_pt->sustain_level;
+ } else {
+ // next: release phase
+ op_pt->op_state = OF_TYPE_SUS_NOKEEP;
+ }
+ }
+ op_pt->step_amp = op_pt->amp;
+ }
+ }
+ op_pt->generator_pos -= num_steps_add*FIXEDPT;
+}
+
+// operator in attack mode, if full output level is reached,
+// the operator is switched into decay mode
+void operator_attack(op_type* op_pt) {
+ op_pt->amp = ((op_pt->a3*op_pt->amp + op_pt->a2)*op_pt->amp + op_pt->a1)*op_pt->amp + op_pt->a0;
+
+ Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples
+ for (Bit32u ct=0; ct<num_steps_add; ct++) {
+ op_pt->cur_env_step++; // next sample
+ if ((op_pt->cur_env_step & op_pt->env_step_a)==0) { // check if next step already reached
+ if (op_pt->amp > 1.0) {
+ // attack phase finished, next: decay
+ op_pt->op_state = OF_TYPE_DEC;
+ op_pt->amp = 1.0;
+ op_pt->step_amp = 1.0;
+ }
+ op_pt->step_skip_pos_a <<= 1;
+ if (op_pt->step_skip_pos_a==0) op_pt->step_skip_pos_a = 1;
+ if (op_pt->step_skip_pos_a & op_pt->env_step_skip_a) { // check if required to skip next step
+ op_pt->step_amp = op_pt->amp;
+ }
+ }
+ }
+ op_pt->generator_pos -= num_steps_add*FIXEDPT;
+}
+
+
+typedef void (*optype_fptr)(op_type*);
+
+optype_fptr opfuncs[6] = {
+ operator_attack,
+ operator_decay,
+ operator_release,
+ operator_sustain, // sustain phase (keeping level)
+ operator_release, // sustain_nokeep phase (release-style)
+ operator_off
+};
+
+void change_attackrate(Bitu regbase, op_type* op_pt) {
+ Bits attackrate = adlibreg[ARC_ATTR_DECR+regbase]>>4;
+ if (attackrate) {
+ fltype f = (fltype)(pow(FL2,(fltype)attackrate+(op_pt->toff>>2)-1)*attackconst[op_pt->toff&3]*recipsamp);
+ // attack rate coefficients
+ op_pt->a0 = (fltype)(0.0377*f);
+ op_pt->a1 = (fltype)(10.73*f+1);
+ op_pt->a2 = (fltype)(-17.57*f);
+ op_pt->a3 = (fltype)(7.42*f);
+
+ Bits step_skip = attackrate*4 + op_pt->toff;
+ Bits steps = step_skip >> 2;
+ op_pt->env_step_a = (1<<(steps<=12?12-steps:0))-1;
+
+ Bits step_num = (step_skip<=48)?(4-(step_skip&3)):0;
+ static Bit8u step_skip_mask[5] = {0xff, 0xfe, 0xee, 0xba, 0xaa};
+ op_pt->env_step_skip_a = step_skip_mask[step_num];
+
+#if defined(OPLTYPE_IS_OPL3)
+ if (step_skip>=60) {
+#else
+ if (step_skip>=62) {
+#endif
+ op_pt->a0 = (fltype)(2.0); // something that triggers an immediate transition to amp:=1.0
+ op_pt->a1 = (fltype)(0.0);
+ op_pt->a2 = (fltype)(0.0);
+ op_pt->a3 = (fltype)(0.0);
+ }
+ } else {
+ // attack disabled
+ op_pt->a0 = 0.0;
+ op_pt->a1 = 1.0;
+ op_pt->a2 = 0.0;
+ op_pt->a3 = 0.0;
+ op_pt->env_step_a = 0;
+ op_pt->env_step_skip_a = 0;
+ }
+}
+
+void change_decayrate(Bitu regbase, op_type* op_pt) {
+ Bits decayrate = adlibreg[ARC_ATTR_DECR+regbase]&15;
+ // decaymul should be 1.0 when decayrate==0
+ if (decayrate) {
+ fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*recipsamp);
+ op_pt->decaymul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(decayrate+(op_pt->toff>>2)))));
+ Bits steps = (decayrate*4 + op_pt->toff) >> 2;
+ op_pt->env_step_d = (1<<(steps<=12?12-steps:0))-1;
+ } else {
+ op_pt->decaymul = 1.0;
+ op_pt->env_step_d = 0;
+ }
+}
+
+void change_releaserate(Bitu regbase, op_type* op_pt) {
+ Bits releaserate = adlibreg[ARC_SUSL_RELR+regbase]&15;
+ // releasemul should be 1.0 when releaserate==0
+ if (releaserate) {
+ fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*recipsamp);
+ op_pt->releasemul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(releaserate+(op_pt->toff>>2)))));
+ Bits steps = (releaserate*4 + op_pt->toff) >> 2;
+ op_pt->env_step_r = (1<<(steps<=12?12-steps:0))-1;
+ } else {
+ op_pt->releasemul = 1.0;
+ op_pt->env_step_r = 0;
+ }
+}
+
+void change_sustainlevel(Bitu regbase, op_type* op_pt) {
+ Bits sustainlevel = adlibreg[ARC_SUSL_RELR+regbase]>>4;
+ // sustainlevel should be 0.0 when sustainlevel==15 (max)
+ if (sustainlevel<15) {
+ op_pt->sustain_level = (fltype)(pow(FL2,(fltype)sustainlevel * (-FL05)));
+ } else {
+ op_pt->sustain_level = 0.0;
+ }
+}
+
+void change_waveform(Bitu regbase, op_type* op_pt) {
+#if defined(OPLTYPE_IS_OPL3)
+ if (regbase>=ARC_SECONDSET) regbase -= (ARC_SECONDSET-22); // second set starts at 22
+#endif
+ // waveform selection
+ op_pt->cur_wmask = wavemask[wave_sel[regbase]];
+ op_pt->cur_wform = &wavtable[waveform[wave_sel[regbase]]];
+ // (might need to be adapted to waveform type here...)
+}
+
+void change_keepsustain(Bitu regbase, op_type* op_pt) {
+ op_pt->sus_keep = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x20)>0;
+ if (op_pt->op_state==OF_TYPE_SUS) {
+ if (!op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS_NOKEEP;
+ } else if (op_pt->op_state==OF_TYPE_SUS_NOKEEP) {
+ if (op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS;
+ }
+}
+
+// enable/disable vibrato/tremolo LFO effects
+void change_vibrato(Bitu regbase, op_type* op_pt) {
+ op_pt->vibrato = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x40)!=0;
+ op_pt->tremolo = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x80)!=0;
+}
+
+// change amount of self-feedback
+void change_feedback(Bitu chanbase, op_type* op_pt) {
+ Bits feedback = adlibreg[ARC_FEEDBACK+chanbase]&14;
+ if (feedback) op_pt->mfbi = (Bit32s)(pow(FL2,(fltype)((feedback>>1)+8)));
+ else op_pt->mfbi = 0;
+}
+
+void change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt) {
+ // frequency
+ Bit32u frn = ((((Bit32u)adlibreg[ARC_KON_BNUM+chanbase])&3)<<8) + (Bit32u)adlibreg[ARC_FREQ_NUM+chanbase];
+ // block number/octave
+ Bit32u oct = ((((Bit32u)adlibreg[ARC_KON_BNUM+chanbase])>>2)&7);
+ op_pt->freq_high = (Bit32s)((frn>>7)&7);
+
+ // keysplit
+ Bit32u note_sel = (adlibreg[8]>>6)&1;
+ op_pt->toff = ((frn>>9)&(note_sel^1)) | ((frn>>8)&note_sel);
+ op_pt->toff += (oct<<1);
+
+ // envelope scaling (KSR)
+ if (!(adlibreg[ARC_TVS_KSR_MUL+regbase]&0x10)) op_pt->toff >>= 2;
+
+ // 20+a0+b0:
+ op_pt->tinc = (Bit32u)((((fltype)(frn<<oct))*frqmul[adlibreg[ARC_TVS_KSR_MUL+regbase]&15]));
+ // 40+a0+b0:
+ fltype vol_in = (fltype)((fltype)(adlibreg[ARC_KSL_OUTLEV+regbase]&63) +
+ kslmul[adlibreg[ARC_KSL_OUTLEV+regbase]>>6]*kslev[oct][frn>>6]);
+ op_pt->vol = (fltype)(pow(FL2,(fltype)(vol_in * -0.125 - 14)));
+
+ // operator frequency changed, care about features that depend on it
+ change_attackrate(regbase,op_pt);
+ change_decayrate(regbase,op_pt);
+ change_releaserate(regbase,op_pt);
+}
+
+void enable_operator(Bitu regbase, op_type* op_pt, Bit32u act_type) {
+ // check if this is really an off-on transition
+ if (op_pt->act_state == OP_ACT_OFF) {
+ Bits wselbase = regbase;
+ if (wselbase>=ARC_SECONDSET) wselbase -= (ARC_SECONDSET-22); // second set starts at 22
+
+ op_pt->tcount = wavestart[wave_sel[wselbase]]*FIXEDPT;
+
+ // start with attack mode
+ op_pt->op_state = OF_TYPE_ATT;
+ op_pt->act_state |= act_type;
+ }
+}
+
+void disable_operator(op_type* op_pt, Bit32u act_type) {
+ // check if this is really an on-off transition
+ if (op_pt->act_state != OP_ACT_OFF) {
+ op_pt->act_state &= (~act_type);
+ if (op_pt->act_state == OP_ACT_OFF) {
+ if (op_pt->op_state != OF_TYPE_OFF) op_pt->op_state = OF_TYPE_REL;
+ }
+ }
+}
+
+void adlib_init(Bit32u samplerate) {
+ Bits i, j, oct;
+
+ int_samplerate = samplerate;
+
+ generator_add = (Bit32u)(INTFREQU*FIXEDPT/int_samplerate);
+
+
+ memset((void *)adlibreg,0,sizeof(adlibreg));
+ memset((void *)op,0,sizeof(op_type)*MAXOPERATORS);
+ memset((void *)wave_sel,0,sizeof(wave_sel));
+
+ for (i=0;i<MAXOPERATORS;i++) {
+ op[i].op_state = OF_TYPE_OFF;
+ op[i].act_state = OP_ACT_OFF;
+ op[i].amp = 0.0;
+ op[i].step_amp = 0.0;
+ op[i].vol = 0.0;
+ op[i].tcount = 0;
+ op[i].tinc = 0;
+ op[i].toff = 0;
+ op[i].cur_wmask = wavemask[0];
+ op[i].cur_wform = &wavtable[waveform[0]];
+ op[i].freq_high = 0;
+
+ op[i].generator_pos = 0;
+ op[i].cur_env_step = 0;
+ op[i].env_step_a = 0;
+ op[i].env_step_d = 0;
+ op[i].env_step_r = 0;
+ op[i].step_skip_pos_a = 0;
+ op[i].env_step_skip_a = 0;
+
+#if defined(OPLTYPE_IS_OPL3)
+ op[i].is_4op = false;
+ op[i].is_4op_attached = false;
+ op[i].left_pan = 1;
+ op[i].right_pan = 1;
+#endif
+ }
+
+ recipsamp = 1.0 / (fltype)int_samplerate;
+ for (i=15;i>=0;i--) {
+ frqmul[i] = (fltype)(frqmul_tab[i]*INTFREQU/(fltype)WAVEPREC*(fltype)FIXEDPT*recipsamp);
+ }
+
+ status = 0;
+ opl_index = 0;
+
+
+ // create vibrato table
+ vib_table[0] = 8;
+ vib_table[1] = 4;
+ vib_table[2] = 0;
+ vib_table[3] = -4;
+ for (i=4; i<VIBTAB_SIZE; i++) vib_table[i] = vib_table[i-4]*-1;
+
+ // vibrato at ~6.1 ?? (opl3 docs say 6.1, opl4 docs say 6.0, y8950 docs say 6.4)
+ vibtab_add = static_cast<Bit32u>(VIBTAB_SIZE*FIXEDPT_LFO/8192*INTFREQU/int_samplerate);
+ vibtab_pos = 0;
+
+ for (i=0; i<BLOCKBUF_SIZE; i++) vibval_const[i] = 0;
+
+
+ // create tremolo table
+ Bit32s trem_table_int[TREMTAB_SIZE];
+ for (i=0; i<14; i++) trem_table_int[i] = i-13; // upwards (13 to 26 -> -0.5/6 to 0)
+ for (i=14; i<41; i++) trem_table_int[i] = -i+14; // downwards (26 to 0 -> 0 to -1/6)
+ for (i=41; i<53; i++) trem_table_int[i] = i-40-26; // upwards (1 to 12 -> -1/6 to -0.5/6)
+
+ for (i=0; i<TREMTAB_SIZE; i++) {
+ // 0.0 .. -26/26*4.8/6 == [0.0 .. -0.8], 4/53 steps == [1 .. 0.57]
+ fltype trem_val1=(fltype)(((fltype)trem_table_int[i])*4.8/26.0/6.0); // 4.8db
+ fltype trem_val2=(fltype)((fltype)((Bit32s)(trem_table_int[i]/4))*1.2/6.0/6.0); // 1.2db (larger stepping)
+
+ trem_table[i] = (Bit32s)(pow(FL2,trem_val1)*FIXEDPT);
+ trem_table[TREMTAB_SIZE+i] = (Bit32s)(pow(FL2,trem_val2)*FIXEDPT);
+ }
+
+ // tremolo at 3.7hz
+ tremtab_add = (Bit32u)((fltype)TREMTAB_SIZE * TREM_FREQ * FIXEDPT_LFO / (fltype)int_samplerate);
+ tremtab_pos = 0;
+
+ for (i=0; i<BLOCKBUF_SIZE; i++) tremval_const[i] = FIXEDPT;
+
+
+ static Bitu initfirstime = 0;
+ if (!initfirstime) {
+ initfirstime = 1;
+
+ // create waveform tables
+ for (i=0;i<(WAVEPREC>>1);i++) {
+ wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1) )*PI*2/WAVEPREC));
+ wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1)+1)*PI*2/WAVEPREC));
+ wavtable[i] = wavtable[(i<<1) +WAVEPREC];
+ // alternative: (zero-less)
+/* wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+1)*PI/WAVEPREC));
+ wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+3)*PI/WAVEPREC));
+ wavtable[i] = wavtable[(i<<1)-1+WAVEPREC]; */
+ }
+ for (i=0;i<(WAVEPREC>>3);i++) {
+ wavtable[i+(WAVEPREC<<1)] = wavtable[i+(WAVEPREC>>3)]-16384;
+ wavtable[i+((WAVEPREC*17)>>3)] = wavtable[i+(WAVEPREC>>2)]+16384;
+ }
+
+ // key scale level table verified ([table in book]*8/3)
+ kslev[7][0] = 0; kslev[7][1] = 24; kslev[7][2] = 32; kslev[7][3] = 37;
+ kslev[7][4] = 40; kslev[7][5] = 43; kslev[7][6] = 45; kslev[7][7] = 47;
+ kslev[7][8] = 48;
+ for (i=9;i<16;i++) kslev[7][i] = (Bit8u)(i+41);
+ for (j=6;j>=0;j--) {
+ for (i=0;i<16;i++) {
+ oct = (Bits)kslev[j+1][i]-8;
+ if (oct < 0) oct = 0;
+ kslev[j][i] = (Bit8u)oct;
+ }
+ }
+ }
+
+}
+
+
+
+void adlib_write(Bitu idx, Bit8u val) {
+ Bit32u second_set = idx&0x100;
+ adlibreg[idx] = val;
+
+ switch (idx&0xf0) {
+ case ARC_CONTROL:
+ // here we check for the second set registers, too:
+ switch (idx) {
+ case 0x02: // timer1 counter
+ case 0x03: // timer2 counter
+ break;
+ case 0x04:
+ // IRQ reset, timer mask/start
+ if (val&0x80) {
+ // clear IRQ bits in status register
+ status &= ~0x60;
+ } else {
+ status = 0;
+ }
+ break;
+#if defined(OPLTYPE_IS_OPL3)
+ case 0x04|ARC_SECONDSET:
+ // 4op enable/disable switches for each possible channel
+ op[0].is_4op = (val&1)>0;
+ op[3].is_4op_attached = op[0].is_4op;
+ op[1].is_4op = (val&2)>0;
+ op[4].is_4op_attached = op[1].is_4op;
+ op[2].is_4op = (val&4)>0;
+ op[5].is_4op_attached = op[2].is_4op;
+ op[18].is_4op = (val&8)>0;
+ op[21].is_4op_attached = op[18].is_4op;
+ op[19].is_4op = (val&16)>0;
+ op[22].is_4op_attached = op[19].is_4op;
+ op[20].is_4op = (val&32)>0;
+ op[23].is_4op_attached = op[20].is_4op;
+ break;
+ case 0x05|ARC_SECONDSET:
+ break;
+#endif
+ case 0x08:
+ // CSW, note select
+ break;
+ default:
+ break;
+ }
+ break;
+ case ARC_TVS_KSR_MUL:
+ case ARC_TVS_KSR_MUL+0x10: {
+ // tremolo/vibrato/sustain keeping enabled; key scale rate; frequency multiplication
+ int num = idx&7;
+ Bitu base = (idx-ARC_TVS_KSR_MUL)&0xff;
+ if ((num<6) && (base<22)) {
+ Bitu modop = regbase2modop[second_set?(base+22):base];
+ Bitu regbase = base+second_set;
+ Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop;
+
+ // change tremolo/vibrato and sustain keeping of this operator
+ op_type* op_ptr = &op[modop+((num<3) ? 0 : 9)];
+ change_keepsustain(regbase,op_ptr);
+ change_vibrato(regbase,op_ptr);
+
+ // change frequency calculations of this operator as
+ // key scale rate and frequency multiplicator can be changed
+#if defined(OPLTYPE_IS_OPL3)
+ if ((adlibreg[0x105]&1) && (op[modop].is_4op_attached)) {
+ // operator uses frequency of channel
+ change_frequency(chanbase-3,regbase,op_ptr);
+ } else {
+ change_frequency(chanbase,regbase,op_ptr);
+ }
+#else
+ change_frequency(chanbase,base,op_ptr);
+#endif
+ }
+ }
+ break;
+ case ARC_KSL_OUTLEV:
+ case ARC_KSL_OUTLEV+0x10: {
+ // key scale level; output rate
+ int num = idx&7;
+ Bitu base = (idx-ARC_KSL_OUTLEV)&0xff;
+ if ((num<6) && (base<22)) {
+ Bitu modop = regbase2modop[second_set?(base+22):base];
+ Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop;
+
+ // change frequency calculations of this operator as
+ // key scale level and output rate can be changed
+ op_type* op_ptr = &op[modop+((num<3) ? 0 : 9)];
+#if defined(OPLTYPE_IS_OPL3)
+ Bitu regbase = base+second_set;
+ if ((adlibreg[0x105]&1) && (op[modop].is_4op_attached)) {
+ // operator uses frequency of channel
+ change_frequency(chanbase-3,regbase,op_ptr);
+ } else {
+ change_frequency(chanbase,regbase,op_ptr);
+ }
+#else
+ change_frequency(chanbase,base,op_ptr);
+#endif
+ }
+ }
+ break;
+ case ARC_ATTR_DECR:
+ case ARC_ATTR_DECR+0x10: {
+ // attack/decay rates
+ int num = idx&7;
+ Bitu base = (idx-ARC_ATTR_DECR)&0xff;
+ if ((num<6) && (base<22)) {
+ Bitu regbase = base+second_set;
+
+ // change attack rate and decay rate of this operator
+ op_type* op_ptr = &op[regbase2op[second_set?(base+22):base]];
+ change_attackrate(regbase,op_ptr);
+ change_decayrate(regbase,op_ptr);
+ }
+ }
+ break;
+ case ARC_SUSL_RELR:
+ case ARC_SUSL_RELR+0x10: {
+ // sustain level; release rate
+ int num = idx&7;
+ Bitu base = (idx-ARC_SUSL_RELR)&0xff;
+ if ((num<6) && (base<22)) {
+ Bitu regbase = base+second_set;
+
+ // change sustain level and release rate of this operator
+ op_type* op_ptr = &op[regbase2op[second_set?(base+22):base]];
+ change_releaserate(regbase,op_ptr);
+ change_sustainlevel(regbase,op_ptr);
+ }
+ }
+ break;
+ case ARC_FREQ_NUM: {
+ // 0xa0-0xa8 low8 frequency
+ Bitu base = (idx-ARC_FREQ_NUM)&0xff;
+ if (base<9) {
+ Bits opbase = second_set?(base+18):base;
+#if defined(OPLTYPE_IS_OPL3)
+ if ((adlibreg[0x105]&1) && op[opbase].is_4op_attached) break;
+#endif
+ // regbase of modulator:
+ Bits modbase = modulatorbase[base]+second_set;
+
+ Bitu chanbase = base+second_set;
+
+ change_frequency(chanbase,modbase,&op[opbase]);
+ change_frequency(chanbase,modbase+3,&op[opbase+9]);
+#if defined(OPLTYPE_IS_OPL3)
+ // for 4op channels all four operators are modified to the frequency of the channel
+ if ((adlibreg[0x105]&1) && op[second_set?(base+18):base].is_4op) {
+ change_frequency(chanbase,modbase+8,&op[opbase+3]);
+ change_frequency(chanbase,modbase+3+8,&op[opbase+3+9]);
+ }
+#endif
+ }
+ }
+ break;
+ case ARC_KON_BNUM: {
+ if (idx == ARC_PERC_MODE) {
+#if defined(OPLTYPE_IS_OPL3)
+ if (second_set) return;
+#endif
+
+ if ((val&0x30) == 0x30) { // BassDrum active
+ enable_operator(16,&op[6],OP_ACT_PERC);
+ change_frequency(6,16,&op[6]);
+ enable_operator(16+3,&op[6+9],OP_ACT_PERC);
+ change_frequency(6,16+3,&op[6+9]);
+ } else {
+ disable_operator(&op[6],OP_ACT_PERC);
+ disable_operator(&op[6+9],OP_ACT_PERC);
+ }
+ if ((val&0x28) == 0x28) { // Snare active
+ enable_operator(17+3,&op[16],OP_ACT_PERC);
+ change_frequency(7,17+3,&op[16]);
+ } else {
+ disable_operator(&op[16],OP_ACT_PERC);
+ }
+ if ((val&0x24) == 0x24) { // TomTom active
+ enable_operator(18,&op[8],OP_ACT_PERC);
+ change_frequency(8,18,&op[8]);
+ } else {
+ disable_operator(&op[8],OP_ACT_PERC);
+ }
+ if ((val&0x22) == 0x22) { // Cymbal active
+ enable_operator(18+3,&op[8+9],OP_ACT_PERC);
+ change_frequency(8,18+3,&op[8+9]);
+ } else {
+ disable_operator(&op[8+9],OP_ACT_PERC);
+ }
+ if ((val&0x21) == 0x21) { // Hihat active
+ enable_operator(17,&op[7],OP_ACT_PERC);
+ change_frequency(7,17,&op[7]);
+ } else {
+ disable_operator(&op[7],OP_ACT_PERC);
+ }
+
+ break;
+ }
+ // regular 0xb0-0xb8
+ Bitu base = (idx-ARC_KON_BNUM)&0xff;
+ if (base<9) {
+ Bits opbase = second_set?(base+18):base;
+#if defined(OPLTYPE_IS_OPL3)
+ if ((adlibreg[0x105]&1) && op[opbase].is_4op_attached) break;
+#endif
+ // regbase of modulator:
+ Bits modbase = modulatorbase[base]+second_set;
+
+ if (val&32) {
+ // operator switched on
+ enable_operator(modbase,&op[opbase],OP_ACT_NORMAL); // modulator (if 2op)
+ enable_operator(modbase+3,&op[opbase+9],OP_ACT_NORMAL); // carrier (if 2op)
+#if defined(OPLTYPE_IS_OPL3)
+ // for 4op channels all four operators are switched on
+ if ((adlibreg[0x105]&1) && op[opbase].is_4op) {
+ // turn on chan+3 operators as well
+ enable_operator(modbase+8,&op[opbase+3],OP_ACT_NORMAL);
+ enable_operator(modbase+3+8,&op[opbase+3+9],OP_ACT_NORMAL);
+ }
+#endif
+ } else {
+ // operator switched off
+ disable_operator(&op[opbase],OP_ACT_NORMAL);
+ disable_operator(&op[opbase+9],OP_ACT_NORMAL);
+#if defined(OPLTYPE_IS_OPL3)
+ // for 4op channels all four operators are switched off
+ if ((adlibreg[0x105]&1) && op[opbase].is_4op) {
+ // turn off chan+3 operators as well
+ disable_operator(&op[opbase+3],OP_ACT_NORMAL);
+ disable_operator(&op[opbase+3+9],OP_ACT_NORMAL);
+ }
+#endif
+ }
+
+ Bitu chanbase = base+second_set;
+
+ // change frequency calculations of modulator and carrier (2op) as
+ // the frequency of the channel has changed
+ change_frequency(chanbase,modbase,&op[opbase]);
+ change_frequency(chanbase,modbase+3,&op[opbase+9]);
+#if defined(OPLTYPE_IS_OPL3)
+ // for 4op channels all four operators are modified to the frequency of the channel
+ if ((adlibreg[0x105]&1) && op[second_set?(base+18):base].is_4op) {
+ // change frequency calculations of chan+3 operators as well
+ change_frequency(chanbase,modbase+8,&op[opbase+3]);
+ change_frequency(chanbase,modbase+3+8,&op[opbase+3+9]);
+ }
+#endif
+ }
+ }
+ break;
+ case ARC_FEEDBACK: {
+ // 0xc0-0xc8 feedback/modulation type (AM/FM)
+ Bitu base = (idx-ARC_FEEDBACK)&0xff;
+ if (base<9) {
+ Bits opbase = second_set?(base+18):base;
+ Bitu chanbase = base+second_set;
+ change_feedback(chanbase,&op[opbase]);
+#if defined(OPLTYPE_IS_OPL3)
+ // OPL3 panning
+ op[opbase].left_pan = ((val&0x10)>>4);
+ op[opbase].right_pan = ((val&0x20)>>5);
+#endif
+ }
+ }
+ break;
+ case ARC_WAVE_SEL:
+ case ARC_WAVE_SEL+0x10: {
+ int num = idx&7;
+ Bitu base = (idx-ARC_WAVE_SEL)&0xff;
+ if ((num<6) && (base<22)) {
+#if defined(OPLTYPE_IS_OPL3)
+ Bits wselbase = second_set?(base+22):base; // for easier mapping onto wave_sel[]
+ // change waveform
+ if (adlibreg[0x105]&1) wave_sel[wselbase] = val&7; // opl3 mode enabled, all waveforms accessible
+ else wave_sel[wselbase] = val&3;
+ op_type* op_ptr = &op[regbase2modop[wselbase]+((num<3) ? 0 : 9)];
+ change_waveform(wselbase,op_ptr);
+#else
+ if (adlibreg[0x01]&0x20) {
+ // wave selection enabled, change waveform
+ wave_sel[base] = val&3;
+ op_type* op_ptr = &op[regbase2modop[base]+((num<3) ? 0 : 9)];
+ change_waveform(base,op_ptr);
+ }
+#endif
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+
+Bitu adlib_reg_read(Bitu port) {
+#if defined(OPLTYPE_IS_OPL3)
+ // opl3-detection routines require ret&6 to be zero
+ if ((port&1)==0) {
+ return status;
+ }
+ return 0x00;
+#else
+ // opl2-detection routines require ret&6 to be 6
+ if ((port&1)==0) {
+ return status|6;
+ }
+ return 0xff;
+#endif
+}
+
+void adlib_write_index(Bitu port, Bit8u val) {
+ opl_index = val;
+#if defined(OPLTYPE_IS_OPL3)
+ if ((port&3)!=0) {
+ // possibly second set
+ if (((adlibreg[0x105]&1)!=0) || (opl_index==5)) opl_index |= ARC_SECONDSET;
+ }
+#endif
+}
+
+static void OPL_INLINE clipit16(Bit32s ival, Bit16s* outval) {
+ if (ival<32768) {
+ if (ival>-32769) {
+ *outval=(Bit16s)ival;
+ } else {
+ *outval = -32768;
+ }
+ } else {
+ *outval = 32767;
+ }
+}
+
+
+
+// be careful with this
+// uses cptr and chanval, outputs into outbufl(/outbufr)
+// for opl3 check if opl3-mode is enabled (which uses stereo panning)
+#undef CHANVAL_OUT
+#if defined(OPLTYPE_IS_OPL3)
+#define CHANVAL_OUT \
+ if (adlibreg[0x105]&1) { \
+ outbufl[i] += chanval*cptr[0].left_pan; \
+ outbufr[i] += chanval*cptr[0].right_pan; \
+ } else { \
+ outbufl[i] += chanval; \
+ }
+#else
+#define CHANVAL_OUT \
+ outbufl[i] += chanval;
+#endif
+
+void adlib_getsample(Bit16s* sndptr, Bits numsamples) {
+ Bits i, endsamples;
+ op_type* cptr;
+
+ Bit32s outbufl[BLOCKBUF_SIZE];
+#if defined(OPLTYPE_IS_OPL3)
+ // second output buffer (right channel for opl3 stereo)
+ Bit32s outbufr[BLOCKBUF_SIZE];
+#endif
+
+ // vibrato/tremolo lookup tables (global, to possibly be used by all operators)
+ Bit32s vib_lut[BLOCKBUF_SIZE];
+ Bit32s trem_lut[BLOCKBUF_SIZE];
+
+ Bits samples_to_process = numsamples;
+
+ for (Bits cursmp=0; cursmp<samples_to_process; cursmp+=endsamples) {
+ endsamples = samples_to_process-cursmp;
+ if (endsamples>BLOCKBUF_SIZE) endsamples = BLOCKBUF_SIZE;
+
+ memset((void*)&outbufl,0,endsamples*sizeof(Bit32s));
+#if defined(OPLTYPE_IS_OPL3)
+ // clear second output buffer (opl3 stereo)
+ if (adlibreg[0x105]&1) memset((void*)&outbufr,0,endsamples*sizeof(Bit32s));
+#endif
+
+ // calculate vibrato/tremolo lookup tables
+ Bit32s vib_tshift = ((adlibreg[ARC_PERC_MODE]&0x40)==0) ? 1 : 0; // 14cents/7cents switching
+ for (i=0;i<endsamples;i++) {
+ // cycle through vibrato table
+ vibtab_pos += vibtab_add;
+ if (vibtab_pos/FIXEDPT_LFO>=VIBTAB_SIZE) vibtab_pos-=VIBTAB_SIZE*FIXEDPT_LFO;
+ vib_lut[i] = vib_table[vibtab_pos/FIXEDPT_LFO]>>vib_tshift; // 14cents (14/100 of a semitone) or 7cents
+
+ // cycle through tremolo table
+ tremtab_pos += tremtab_add;
+ if (tremtab_pos/FIXEDPT_LFO>=TREMTAB_SIZE) tremtab_pos-=TREMTAB_SIZE*FIXEDPT_LFO;
+ if (adlibreg[ARC_PERC_MODE]&0x80) trem_lut[i] = trem_table[tremtab_pos/FIXEDPT_LFO];
+ else trem_lut[i] = trem_table[TREMTAB_SIZE+tremtab_pos/FIXEDPT_LFO];
+ }
+
+ if (adlibreg[ARC_PERC_MODE]&0x20) {
+ //BassDrum
+ cptr = &op[6];
+ if (adlibreg[ARC_FEEDBACK+6]&1) {
+ // additive synthesis
+ if (cptr[9].op_state != OF_TYPE_OFF) {
+ if (cptr[9].vibrato) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if (cptr[9].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[9],vibval1[i]);
+ opfuncs[cptr[9].op_state](&cptr[9]);
+ operator_output(&cptr[9],0,tremval1[i]);
+
+ Bit32s chanval = cptr[9].cval*2;
+ CHANVAL_OUT
+ }
+ }
+ } else {
+ // frequency modulation
+ if ((cptr[9].op_state != OF_TYPE_OFF) || (cptr[0].op_state != OF_TYPE_OFF)) {
+ if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) {
+ vibval2 = vibval_var2;
+ for (i=0;i<endsamples;i++)
+ vibval2[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval2 = vibval_const;
+ if (cptr[0].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[9].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[0],vibval1[i]);
+ opfuncs[cptr[0].op_state](&cptr[0]);
+ operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]);
+
+ operator_advance(&cptr[9],vibval2[i]);
+ opfuncs[cptr[9].op_state](&cptr[9]);
+ operator_output(&cptr[9],cptr[0].cval*FIXEDPT,tremval2[i]);
+
+ Bit32s chanval = cptr[9].cval*2;
+ CHANVAL_OUT
+ }
+ }
+ }
+
+ //TomTom (j=8)
+ if (op[8].op_state != OF_TYPE_OFF) {
+ cptr = &op[8];
+ if (cptr[0].vibrato) {
+ vibval3 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval3[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval3 = vibval_const;
+
+ if (cptr[0].tremolo) tremval3 = trem_lut; // tremolo enabled, use table
+ else tremval3 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[0],vibval3[i]);
+ opfuncs[cptr[0].op_state](&cptr[0]); //TomTom
+ operator_output(&cptr[0],0,tremval3[i]);
+ Bit32s chanval = cptr[0].cval*2;
+ CHANVAL_OUT
+ }
+ }
+
+ //Snare/Hihat (j=7), Cymbal (j=8)
+ if ((op[7].op_state != OF_TYPE_OFF) || (op[16].op_state != OF_TYPE_OFF) ||
+ (op[17].op_state != OF_TYPE_OFF)) {
+ cptr = &op[7];
+ if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if ((cptr[9].vibrato) && (cptr[9].op_state == OF_TYPE_OFF)) {
+ vibval2 = vibval_var2;
+ for (i=0;i<endsamples;i++)
+ vibval2[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval2 = vibval_const;
+
+ if (cptr[0].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[9].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+
+ cptr = &op[8];
+ if ((cptr[9].vibrato) && (cptr[9].op_state == OF_TYPE_OFF)) {
+ vibval4 = vibval_var2;
+ for (i=0;i<endsamples;i++)
+ vibval4[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval4 = vibval_const;
+
+ if (cptr[9].tremolo) tremval4 = trem_lut; // tremolo enabled, use table
+ else tremval4 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance_drums(&op[7],vibval1[i],&op[7+9],vibval2[i],&op[8+9],vibval4[i]);
+
+ opfuncs[op[7].op_state](&op[7]); //Hihat
+ operator_output(&op[7],0,tremval1[i]);
+
+ opfuncs[op[7+9].op_state](&op[7+9]); //Snare
+ operator_output(&op[7+9],0,tremval2[i]);
+
+ opfuncs[op[8+9].op_state](&op[8+9]); //Cymbal
+ operator_output(&op[8+9],0,tremval4[i]);
+
+ Bit32s chanval = (op[7].cval + op[7+9].cval + op[8+9].cval)*2;
+ CHANVAL_OUT
+ }
+ }
+ }
+
+ Bitu max_channel = NUM_CHANNELS;
+#if defined(OPLTYPE_IS_OPL3)
+ if ((adlibreg[0x105]&1)==0) max_channel = NUM_CHANNELS/2;
+#endif
+ for (Bits cur_ch=max_channel-1; cur_ch>=0; cur_ch--) {
+ // skip drum/percussion operators
+ if ((adlibreg[ARC_PERC_MODE]&0x20) && (cur_ch >= 6) && (cur_ch < 9)) continue;
+
+ Bitu k = cur_ch;
+#if defined(OPLTYPE_IS_OPL3)
+ if (cur_ch < 9) {
+ cptr = &op[cur_ch];
+ } else {
+ cptr = &op[cur_ch+9]; // second set is operator18-operator35
+ k += (-9+256); // second set uses registers 0x100 onwards
+ }
+ // check if this operator is part of a 4-op
+ if ((adlibreg[0x105]&1) && cptr->is_4op_attached) continue;
+#else
+ cptr = &op[cur_ch];
+#endif
+
+ // check for FM/AM
+ if (adlibreg[ARC_FEEDBACK+k]&1) {
+#if defined(OPLTYPE_IS_OPL3)
+ if ((adlibreg[0x105]&1) && cptr->is_4op) {
+ if (adlibreg[ARC_FEEDBACK+k+3]&1) {
+ // AM-AM-style synthesis (op1[fb] + (op2 * op3) + op4)
+ if (cptr[0].op_state != OF_TYPE_OFF) {
+ if (cptr[0].vibrato) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if (cptr[0].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[0],vibval1[i]);
+ opfuncs[cptr[0].op_state](&cptr[0]);
+ operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]);
+
+ Bit32s chanval = cptr[0].cval;
+ CHANVAL_OUT
+ }
+ }
+
+ if ((cptr[3].op_state != OF_TYPE_OFF) || (cptr[9].op_state != OF_TYPE_OFF)) {
+ if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if (cptr[9].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[3].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[9],vibval1[i]);
+ opfuncs[cptr[9].op_state](&cptr[9]);
+ operator_output(&cptr[9],0,tremval1[i]);
+
+ operator_advance(&cptr[3],0);
+ opfuncs[cptr[3].op_state](&cptr[3]);
+ operator_output(&cptr[3],cptr[9].cval*FIXEDPT,tremval2[i]);
+
+ Bit32s chanval = cptr[3].cval;
+ CHANVAL_OUT
+ }
+ }
+
+ if (cptr[3+9].op_state != OF_TYPE_OFF) {
+ if (cptr[3+9].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[3+9],0);
+ opfuncs[cptr[3+9].op_state](&cptr[3+9]);
+ operator_output(&cptr[3+9],0,tremval1[i]);
+
+ Bit32s chanval = cptr[3+9].cval;
+ CHANVAL_OUT
+ }
+ }
+ } else {
+ // AM-FM-style synthesis (op1[fb] + (op2 * op3 * op4))
+ if (cptr[0].op_state != OF_TYPE_OFF) {
+ if (cptr[0].vibrato) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if (cptr[0].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[0],vibval1[i]);
+ opfuncs[cptr[0].op_state](&cptr[0]);
+ operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]);
+
+ Bit32s chanval = cptr[0].cval;
+ CHANVAL_OUT
+ }
+ }
+
+ if ((cptr[9].op_state != OF_TYPE_OFF) || (cptr[3].op_state != OF_TYPE_OFF) || (cptr[3+9].op_state != OF_TYPE_OFF)) {
+ if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if (cptr[9].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[3].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+ if (cptr[3+9].tremolo) tremval3 = trem_lut; // tremolo enabled, use table
+ else tremval3 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[9],vibval1[i]);
+ opfuncs[cptr[9].op_state](&cptr[9]);
+ operator_output(&cptr[9],0,tremval1[i]);
+
+ operator_advance(&cptr[3],0);
+ opfuncs[cptr[3].op_state](&cptr[3]);
+ operator_output(&cptr[3],cptr[9].cval*FIXEDPT,tremval2[i]);
+
+ operator_advance(&cptr[3+9],0);
+ opfuncs[cptr[3+9].op_state](&cptr[3+9]);
+ operator_output(&cptr[3+9],cptr[3].cval*FIXEDPT,tremval3[i]);
+
+ Bit32s chanval = cptr[3+9].cval;
+ CHANVAL_OUT
+ }
+ }
+ }
+ continue;
+ }
+#endif
+ // 2op additive synthesis
+ if ((cptr[9].op_state == OF_TYPE_OFF) && (cptr[0].op_state == OF_TYPE_OFF)) continue;
+ if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) {
+ vibval2 = vibval_var2;
+ for (i=0;i<endsamples;i++)
+ vibval2[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval2 = vibval_const;
+ if (cptr[0].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[9].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ // carrier1
+ operator_advance(&cptr[0],vibval1[i]);
+ opfuncs[cptr[0].op_state](&cptr[0]);
+ operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]);
+
+ // carrier2
+ operator_advance(&cptr[9],vibval2[i]);
+ opfuncs[cptr[9].op_state](&cptr[9]);
+ operator_output(&cptr[9],0,tremval2[i]);
+
+ Bit32s chanval = cptr[9].cval + cptr[0].cval;
+ CHANVAL_OUT
+ }
+ } else {
+#if defined(OPLTYPE_IS_OPL3)
+ if ((adlibreg[0x105]&1) && cptr->is_4op) {
+ if (adlibreg[ARC_FEEDBACK+k+3]&1) {
+ // FM-AM-style synthesis ((op1[fb] * op2) + (op3 * op4))
+ if ((cptr[0].op_state != OF_TYPE_OFF) || (cptr[9].op_state != OF_TYPE_OFF)) {
+ if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) {
+ vibval2 = vibval_var2;
+ for (i=0;i<endsamples;i++)
+ vibval2[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval2 = vibval_const;
+ if (cptr[0].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[9].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[0],vibval1[i]);
+ opfuncs[cptr[0].op_state](&cptr[0]);
+ operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]);
+
+ operator_advance(&cptr[9],vibval2[i]);
+ opfuncs[cptr[9].op_state](&cptr[9]);
+ operator_output(&cptr[9],cptr[0].cval*FIXEDPT,tremval2[i]);
+
+ Bit32s chanval = cptr[9].cval;
+ CHANVAL_OUT
+ }
+ }
+
+ if ((cptr[3].op_state != OF_TYPE_OFF) || (cptr[3+9].op_state != OF_TYPE_OFF)) {
+ if (cptr[3].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[3+9].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[3],0);
+ opfuncs[cptr[3].op_state](&cptr[3]);
+ operator_output(&cptr[3],0,tremval1[i]);
+
+ operator_advance(&cptr[3+9],0);
+ opfuncs[cptr[3+9].op_state](&cptr[3+9]);
+ operator_output(&cptr[3+9],cptr[3].cval*FIXEDPT,tremval2[i]);
+
+ Bit32s chanval = cptr[3+9].cval;
+ CHANVAL_OUT
+ }
+ }
+
+ } else {
+ // FM-FM-style synthesis (op1[fb] * op2 * op3 * op4)
+ if ((cptr[0].op_state != OF_TYPE_OFF) || (cptr[9].op_state != OF_TYPE_OFF) ||
+ (cptr[3].op_state != OF_TYPE_OFF) || (cptr[3+9].op_state != OF_TYPE_OFF)) {
+ if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) {
+ vibval2 = vibval_var2;
+ for (i=0;i<endsamples;i++)
+ vibval2[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval2 = vibval_const;
+ if (cptr[0].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[9].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+ if (cptr[3].tremolo) tremval3 = trem_lut; // tremolo enabled, use table
+ else tremval3 = tremval_const;
+ if (cptr[3+9].tremolo) tremval4 = trem_lut; // tremolo enabled, use table
+ else tremval4 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ operator_advance(&cptr[0],vibval1[i]);
+ opfuncs[cptr[0].op_state](&cptr[0]);
+ operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]);
+
+ operator_advance(&cptr[9],vibval2[i]);
+ opfuncs[cptr[9].op_state](&cptr[9]);
+ operator_output(&cptr[9],cptr[0].cval*FIXEDPT,tremval2[i]);
+
+ operator_advance(&cptr[3],0);
+ opfuncs[cptr[3].op_state](&cptr[3]);
+ operator_output(&cptr[3],cptr[9].cval*FIXEDPT,tremval3[i]);
+
+ operator_advance(&cptr[3+9],0);
+ opfuncs[cptr[3+9].op_state](&cptr[3+9]);
+ operator_output(&cptr[3+9],cptr[3].cval*FIXEDPT,tremval4[i]);
+
+ Bit32s chanval = cptr[3+9].cval;
+ CHANVAL_OUT
+ }
+ }
+ }
+ continue;
+ }
+#endif
+ // 2op frequency modulation
+ if ((cptr[9].op_state == OF_TYPE_OFF) && (cptr[0].op_state == OF_TYPE_OFF)) continue;
+ if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) {
+ vibval1 = vibval_var1;
+ for (i=0;i<endsamples;i++)
+ vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval1 = vibval_const;
+ if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) {
+ vibval2 = vibval_var2;
+ for (i=0;i<endsamples;i++)
+ vibval2[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC);
+ } else vibval2 = vibval_const;
+ if (cptr[0].tremolo) tremval1 = trem_lut; // tremolo enabled, use table
+ else tremval1 = tremval_const;
+ if (cptr[9].tremolo) tremval2 = trem_lut; // tremolo enabled, use table
+ else tremval2 = tremval_const;
+
+ // calculate channel output
+ for (i=0;i<endsamples;i++) {
+ // modulator
+ operator_advance(&cptr[0],vibval1[i]);
+ opfuncs[cptr[0].op_state](&cptr[0]);
+ operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]);
+
+ // carrier
+ operator_advance(&cptr[9],vibval2[i]);
+ opfuncs[cptr[9].op_state](&cptr[9]);
+ operator_output(&cptr[9],cptr[0].cval*FIXEDPT,tremval2[i]);
+
+ Bit32s chanval = cptr[9].cval;
+ CHANVAL_OUT
+ }
+ }
+ }
+
+#if defined(OPLTYPE_IS_OPL3)
+ if (adlibreg[0x105]&1) {
+ // convert to 16bit samples (stereo)
+ for (i=0;i<endsamples;i++) {
+ clipit16(outbufl[i],sndptr++);
+ clipit16(outbufr[i],sndptr++);
+ }
+ } else {
+ // convert to 16bit samples (mono)
+ for (i=0;i<endsamples;i++) {
+ clipit16(outbufl[i],sndptr++);
+ clipit16(outbufl[i],sndptr++);
+ }
+ }
+#else
+ // convert to 16bit samples
+ for (i=0;i<endsamples;i++)
+ clipit16(outbufl[i],sndptr++);
+#endif
+
+ }
+}
diff --git a/src/hardware/opl.h b/src/hardware/opl.h
new file mode 100644
index 000000000..50291c523
--- /dev/null
+++ b/src/hardware/opl.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ * OPL2/OPL3 emulation library
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+/*
+ * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman
+ * Copyright (C) 1998-2001 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ */
+
+
+#define fltype double
+
+/*
+ define Bits, Bitu, Bit32s, Bit32u, Bit16s, Bit16u, Bit8s, Bit8u here
+*/
+/*
+#include <stdint.h>
+typedef uintptr_t Bitu;
+typedef intptr_t Bits;
+typedef uint32_t Bit32u;
+typedef int32_t Bit32s;
+typedef uint16_t Bit16u;
+typedef int16_t Bit16s;
+typedef uint8_t Bit8u;
+typedef int8_t Bit8s;
+*/
+
+
+/*
+ define attribution that inlines/forces inlining of a function (optional)
+*/
+#define OPL_INLINE INLINE
+
+
+#undef NUM_CHANNELS
+#if defined(OPLTYPE_IS_OPL3)
+#define NUM_CHANNELS 18
+#else
+#define NUM_CHANNELS 9
+#endif
+
+#define MAXOPERATORS (NUM_CHANNELS*2)
+
+
+#define FL05 ((fltype)0.5)
+#define FL2 ((fltype)2.0)
+#define PI ((fltype)3.1415926535897932384626433832795)
+
+
+#define FIXEDPT 0x10000 // fixed-point calculations using 16+16
+#define FIXEDPT_LFO 0x1000000 // fixed-point calculations using 8+24
+
+#define WAVEPREC 1024 // waveform precision (10 bits)
+
+#define INTFREQU ((fltype)(14318180.0 / 288.0)) // clocking of the chip
+
+
+#define OF_TYPE_ATT 0
+#define OF_TYPE_DEC 1
+#define OF_TYPE_REL 2
+#define OF_TYPE_SUS 3
+#define OF_TYPE_SUS_NOKEEP 4
+#define OF_TYPE_OFF 5
+
+#define ARC_CONTROL 0x00
+#define ARC_TVS_KSR_MUL 0x20
+#define ARC_KSL_OUTLEV 0x40
+#define ARC_ATTR_DECR 0x60
+#define ARC_SUSL_RELR 0x80
+#define ARC_FREQ_NUM 0xa0
+#define ARC_KON_BNUM 0xb0
+#define ARC_PERC_MODE 0xbd
+#define ARC_FEEDBACK 0xc0
+#define ARC_WAVE_SEL 0xe0
+
+#define ARC_SECONDSET 0x100 // second operator set for OPL3
+
+
+#define OP_ACT_OFF 0x00
+#define OP_ACT_NORMAL 0x01 // regular channel activated (bitmasked)
+#define OP_ACT_PERC 0x02 // percussion channel activated (bitmasked)
+
+#define BLOCKBUF_SIZE 512
+
+
+// vibrato constants
+#define VIBTAB_SIZE 8
+#define VIBFAC 70/50000 // no braces, integer mul/div
+
+// tremolo constants and table
+#define TREMTAB_SIZE 53
+#define TREM_FREQ ((fltype)(3.7)) // tremolo at 3.7hz
+
+
+/* operator struct definition
+ For OPL2 all 9 channels consist of two operators each, carrier and modulator.
+ Channel x has operators x as modulator and operators (9+x) as carrier.
+ For OPL3 all 18 channels consist either of two operators (2op mode) or four
+ operators (4op mode) which is determined through register4 of the second
+ adlib register set.
+ Only the channels 0,1,2 (first set) and 9,10,11 (second set) can act as
+ 4op channels. The two additional operators for a channel y come from the
+ 2op channel y+3 so the operatorss y, (9+y), y+3, (9+y)+3 make up a 4op
+ channel.
+*/
+typedef struct operator_struct {
+ Bit32s cval, lastcval; // current output/last output (used for feedback)
+ Bit32u tcount, wfpos, tinc; // time (position in waveform) and time increment
+ fltype amp, step_amp; // and amplification (envelope)
+ fltype vol; // volume
+ fltype sustain_level; // sustain level
+ Bit32s mfbi; // feedback amount
+ fltype a0, a1, a2, a3; // attack rate function coefficients
+ fltype decaymul, releasemul; // decay/release rate functions
+ Bit32u op_state; // current state of operator (attack/decay/sustain/release/off)
+ Bit32u toff;
+ Bit32s freq_high; // highest three bits of the frequency, used for vibrato calculations
+ Bit16s* cur_wform; // start of selected waveform
+ Bit32u cur_wmask; // mask for selected waveform
+ Bit32u act_state; // activity state (regular, percussion)
+ bool sus_keep; // keep sustain level when decay finished
+ bool vibrato,tremolo; // vibrato/tremolo enable bits
+
+ // variables used to provide non-continuous envelopes
+ Bit32u generator_pos; // for non-standard sample rates we need to determine how many samples have passed
+ Bits cur_env_step; // current (standardized) sample position
+ Bits env_step_a,env_step_d,env_step_r; // number of std samples of one step (for attack/decay/release mode)
+ Bit8u step_skip_pos_a; // position of 8-cyclic step skipping (always 2^x to check against mask)
+ Bits env_step_skip_a; // bitmask that determines if a step is skipped (respective bit is zero then)
+
+#if defined(OPLTYPE_IS_OPL3)
+ bool is_4op,is_4op_attached; // base of a 4op channel/part of a 4op channel
+ Bit32s left_pan,right_pan; // opl3 stereo panning amount
+#endif
+} op_type;
+
+// per-chip variables
+Bitu chip_num;
+op_type op[MAXOPERATORS];
+
+Bits int_samplerate;
+
+Bit8u status;
+Bit32u opl_index;
+#if defined(OPLTYPE_IS_OPL3)
+Bit8u adlibreg[512]; // adlib register set (including second set)
+Bit8u wave_sel[44]; // waveform selection
+#else
+Bit8u adlibreg[256]; // adlib register set
+Bit8u wave_sel[22]; // waveform selection
+#endif
+
+
+// vibrato/tremolo increment/counter
+Bit32u vibtab_pos;
+Bit32u vibtab_add;
+Bit32u tremtab_pos;
+Bit32u tremtab_add;
+
+
+// enable an operator
+void enable_operator(Bitu regbase, op_type* op_pt);
+
+// functions to change parameters of an operator
+void change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt);
+
+void change_attackrate(Bitu regbase, op_type* op_pt);
+void change_decayrate(Bitu regbase, op_type* op_pt);
+void change_releaserate(Bitu regbase, op_type* op_pt);
+void change_sustainlevel(Bitu regbase, op_type* op_pt);
+void change_waveform(Bitu regbase, op_type* op_pt);
+void change_keepsustain(Bitu regbase, op_type* op_pt);
+void change_vibrato(Bitu regbase, op_type* op_pt);
+void change_feedback(Bitu chanbase, op_type* op_pt);
+
+// general functions
+void adlib_init(Bit32u samplerate);
+void adlib_write(Bitu idx, Bit8u val);
+void adlib_getsample(Bit16s* sndptr, Bits numsamples);
+
+Bitu adlib_reg_read(Bitu port);
+void adlib_write_index(Bitu port, Bit8u val);
+
+static Bit32u generator_add; // should be a chip parameter
diff --git a/src/hardware/pci_bus.cpp b/src/hardware/pci_bus.cpp
new file mode 100644
index 000000000..05e5d6777
--- /dev/null
+++ b/src/hardware/pci_bus.cpp
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "inout.h"
+#include "mem.h"
+#include "pci_bus.h"
+#include "setup.h"
+#include "debug.h"
+#include "callback.h"
+#include "regs.h"
+
+
+#if defined(PCI_FUNCTIONALITY_ENABLED)
+
+static Bit32u pci_caddress=0; // current PCI addressing
+static Bitu pci_devices_installed=0; // number of registered PCI devices
+
+static Bit8u pci_cfg_data[PCI_MAX_PCIDEVICES][PCI_MAX_PCIFUNCTIONS][256]; // PCI configuration data
+static PCI_Device* pci_devices[PCI_MAX_PCIDEVICES]; // registered PCI devices
+
+
+// PCI address
+// 31 - set for a PCI access
+// 30-24 - 0
+// 23-16 - bus number (0x00ff0000)
+// 15-11 - device number (slot) (0x0000f800)
+// 10- 8 - subfunction number (0x00000700)
+// 7- 2 - config register # (0x000000fc)
+
+static void write_pci_addr(Bitu port,Bitu val,Bitu iolen) {
+ LOG(LOG_PCI,LOG_NORMAL)("Write PCI address :=%x",val);
+ pci_caddress=val;
+}
+
+static void write_pci_register(PCI_Device* dev,Bit8u regnum,Bit8u value) {
+ // vendor/device/class IDs/header type/etc. are read-only
+ if ((regnum<0x04) || ((regnum>=0x06) && (regnum<0x0c)) || (regnum==0x0e)) return;
+ if (dev==NULL) return;
+ switch (pci_cfg_data[dev->PCIId()][dev->PCISubfunction()][0x0e]&0x7f) { // header-type specific handling
+ case 0x00:
+ if ((regnum>=0x28) && (regnum<0x30)) return; // subsystem information is read-only
+ break;
+ case 0x01:
+ case 0x02:
+ default:
+ break;
+ }
+
+ // call device routine for special actions and the
+ // possibility to discard/replace the value that is to be written
+ Bits parsed_register=dev->ParseWriteRegister(regnum,value);
+ if (parsed_register>=0)
+ pci_cfg_data[dev->PCIId()][dev->PCISubfunction()][regnum]=(Bit8u)(parsed_register&0xff);
+}
+
+static void write_pci(Bitu port,Bitu val,Bitu iolen) {
+ LOG(LOG_PCI,LOG_NORMAL)("Write PCI data :=%x (len %d)",port,val,iolen);
+
+ // check for enabled/bus 0
+ if ((pci_caddress & 0x80ff0000) == 0x80000000) {
+ Bit8u devnum = (Bit8u)((pci_caddress >> 11) & 0x1f);
+ Bit8u fctnum = (Bit8u)((pci_caddress >> 8) & 0x7);
+ Bit8u regnum = (Bit8u)((pci_caddress & 0xfc) + (port & 0x03));
+ LOG(LOG_PCI,LOG_NORMAL)(" Write to device %x register %x (function %x) (:=%x)",devnum,regnum,fctnum,val);
+
+ if (devnum>=pci_devices_installed) return;
+ PCI_Device* masterdev=pci_devices[devnum];
+ if (masterdev==NULL) return;
+ if (fctnum>masterdev->NumSubdevices()) return;
+
+ PCI_Device* dev=masterdev->GetSubdevice(fctnum);
+ if (dev==NULL) return;
+
+ // write data to PCI device/configuration
+ switch (iolen) {
+ case 1: write_pci_register(dev,regnum+0,(Bit8u)(val&0xff)); break;
+ case 2: write_pci_register(dev,regnum+0,(Bit8u)(val&0xff));
+ write_pci_register(dev,regnum+1,(Bit8u)((val>>8)&0xff)); break;
+ case 4: write_pci_register(dev,regnum+0,(Bit8u)(val&0xff));
+ write_pci_register(dev,regnum+1,(Bit8u)((val>>8)&0xff));
+ write_pci_register(dev,regnum+2,(Bit8u)((val>>16)&0xff));
+ write_pci_register(dev,regnum+3,(Bit8u)((val>>24)&0xff)); break;
+ }
+ }
+}
+
+
+static Bitu read_pci_addr(Bitu port,Bitu iolen) {
+ LOG(LOG_PCI,LOG_NORMAL)("Read PCI address -> %x",pci_caddress);
+ return pci_caddress;
+}
+
+// read single 8bit value from register file (special register treatment included)
+static Bit8u read_pci_register(PCI_Device* dev,Bit8u regnum) {
+ switch (regnum) {
+ case 0x00:
+ return (Bit8u)(dev->VendorID()&0xff);
+ case 0x01:
+ return (Bit8u)((dev->VendorID()>>8)&0xff);
+ case 0x02:
+ return (Bit8u)(dev->DeviceID()&0xff);
+ case 0x03:
+ return (Bit8u)((dev->DeviceID()>>8)&0xff);
+
+ case 0x0e:
+ return (pci_cfg_data[dev->PCIId()][dev->PCISubfunction()][regnum]&0x7f) |
+ ((dev->NumSubdevices()>0)?0x80:0x00);
+ default:
+ break;
+ }
+
+ // call device routine for special actions and possibility to discard/remap register
+ Bits parsed_regnum=dev->ParseReadRegister(regnum);
+ if ((parsed_regnum>=0) && (parsed_regnum<256))
+ return pci_cfg_data[dev->PCIId()][dev->PCISubfunction()][parsed_regnum];
+
+ Bit8u newval, mask;
+ if (dev->OverrideReadRegister(regnum, &newval, &mask)) {
+ Bit8u oldval=pci_cfg_data[dev->PCIId()][dev->PCISubfunction()][regnum] & (~mask);
+ return oldval | (newval & mask);
+ }
+
+ return 0xff;
+}
+
+static Bitu read_pci(Bitu port,Bitu iolen) {
+ LOG(LOG_PCI,LOG_NORMAL)("Read PCI data -> %x",pci_caddress);
+
+ if ((pci_caddress & 0x80ff0000) == 0x80000000) {
+ Bit8u devnum = (Bit8u)((pci_caddress >> 11) & 0x1f);
+ Bit8u fctnum = (Bit8u)((pci_caddress >> 8) & 0x7);
+ Bit8u regnum = (Bit8u)((pci_caddress & 0xfc) + (port & 0x03));
+ if (devnum>=pci_devices_installed) return 0xffffffff;
+ LOG(LOG_PCI,LOG_NORMAL)(" Read from device %x register %x (function %x); addr %x",
+ devnum,regnum,fctnum,pci_caddress);
+
+ PCI_Device* masterdev=pci_devices[devnum];
+ if (masterdev==NULL) return 0xffffffff;
+ if (fctnum>masterdev->NumSubdevices()) return 0xffffffff;
+
+ PCI_Device* dev=masterdev->GetSubdevice(fctnum);
+
+ if (dev!=NULL) {
+ switch (iolen) {
+ case 1:
+ {
+ Bit8u val8=read_pci_register(dev,regnum);
+ return val8;
+ }
+ case 2:
+ {
+ Bit16u val16=read_pci_register(dev,regnum);
+ val16|=(read_pci_register(dev,regnum+1)<<8);
+ return val16;
+ }
+ case 4:
+ {
+ Bit32u val32=read_pci_register(dev,regnum);
+ val32|=(read_pci_register(dev,regnum+1)<<8);
+ val32|=(read_pci_register(dev,regnum+2)<<16);
+ val32|=(read_pci_register(dev,regnum+3)<<24);
+ return val32;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ return 0xffffffff;
+}
+
+
+static Bitu PCI_PM_Handler() {
+ LOG_MSG("PCI PMode handler, function %x",reg_ax);
+ return CBRET_NONE;
+}
+
+
+PCI_Device::PCI_Device(Bit16u vendor, Bit16u device) {
+ pci_id=-1;
+ pci_subfunction=-1;
+ vendor_id=vendor;
+ device_id=device;
+ num_subdevices=0;
+ for (Bitu dct=0;dct<PCI_MAX_PCIFUNCTIONS-1;dct++) subdevices[dct]=0;
+}
+
+void PCI_Device::SetPCIId(Bitu number, Bits subfct) {
+ if ((number>=0) && (number<PCI_MAX_PCIDEVICES)) {
+ pci_id=number;
+ if ((subfct>=0) && (subfct<PCI_MAX_PCIFUNCTIONS-1))
+ pci_subfunction=subfct;
+ else
+ pci_subfunction=-1;
+ }
+}
+
+bool PCI_Device::AddSubdevice(PCI_Device* dev) {
+ if (num_subdevices<PCI_MAX_PCIFUNCTIONS-1) {
+ if (subdevices[num_subdevices]!=NULL) E_Exit("PCI subdevice slot already in use!");
+ subdevices[num_subdevices]=dev;
+ num_subdevices++;
+ return true;
+ }
+ return false;
+}
+
+void PCI_Device::RemoveSubdevice(Bits subfct) {
+ if ((subfct>0) && (subfct<PCI_MAX_PCIFUNCTIONS)) {
+ if (subfct<=this->NumSubdevices()) {
+ delete subdevices[subfct-1];
+ subdevices[subfct-1]=NULL;
+ // should adjust things like num_subdevices as well...
+ }
+ }
+}
+
+PCI_Device* PCI_Device::GetSubdevice(Bits subfct) {
+ if (subfct>=PCI_MAX_PCIFUNCTIONS) return NULL;
+ if (subfct>0) {
+ if (subfct<=this->NumSubdevices()) return subdevices[subfct-1];
+ } else if (subfct==0) {
+ return this;
+ }
+ return NULL;
+}
+
+
+// queued devices (PCI device registering requested before the PCI framework was initialized)
+static const Bitu max_rqueued_devices=16;
+static Bitu num_rqueued_devices=0;
+static PCI_Device* rqueued_devices[max_rqueued_devices];
+
+
+#include "pci_devices.h"
+
+class PCI:public Module_base{
+private:
+ bool initialized;
+
+protected:
+ IO_WriteHandleObject PCI_WriteHandler[5];
+ IO_ReadHandleObject PCI_ReadHandler[5];
+
+ CALLBACK_HandlerObject callback_pci;
+
+public:
+
+ PhysPt GetPModeCallbackPointer(void) {
+ return Real2Phys(callback_pci.Get_RealPointer());
+ }
+
+ bool IsInitialized(void) {
+ return initialized;
+ }
+
+ // set up port handlers and configuration data
+ void InitializePCI(void) {
+ // install PCI-addressing ports
+ PCI_WriteHandler[0].Install(0xcf8,write_pci_addr,IO_MD);
+ PCI_ReadHandler[0].Install(0xcf8,read_pci_addr,IO_MD);
+ // install PCI-register read/write handlers
+ for (Bitu ct=0;ct<4;ct++) {
+ PCI_WriteHandler[1+ct].Install(0xcfc+ct,write_pci,IO_MB);
+ PCI_ReadHandler[1+ct].Install(0xcfc+ct,read_pci,IO_MB);
+ }
+
+ for (Bitu dev=0; dev<PCI_MAX_PCIDEVICES; dev++)
+ for (Bitu fct=0; fct<PCI_MAX_PCIFUNCTIONS-1; fct++)
+ for (Bitu reg=0; reg<256; reg++)
+ pci_cfg_data[dev][fct][reg] = 0;
+
+ callback_pci.Install(&PCI_PM_Handler,CB_IRETD,"PCI PM");
+
+ initialized=true;
+ }
+
+ // register PCI device to bus and setup data
+ Bits RegisterPCIDevice(PCI_Device* device, Bits slot=-1) {
+ if (device==NULL) return -1;
+
+ if (slot>=0) {
+ // specific slot specified, basic check for validity
+ if (slot>=PCI_MAX_PCIDEVICES) return -1;
+ } else {
+ // auto-add to new slot, check if one is still free
+ if (pci_devices_installed>=PCI_MAX_PCIDEVICES) return -1;
+ }
+
+ if (!initialized) InitializePCI();
+
+ if (slot<0) slot=pci_devices_installed; // use next slot
+ Bits subfunction=0; // main device unless specific already-occupied slot is requested
+ if (pci_devices[slot]!=NULL) {
+ subfunction=pci_devices[slot]->GetNextSubdeviceNumber();
+ if (subfunction<0) E_Exit("Too many PCI subdevices!");
+ }
+
+ if (device->InitializeRegisters(pci_cfg_data[slot][subfunction])) {
+ device->SetPCIId(slot, subfunction);
+ if (pci_devices[slot]==NULL) {
+ pci_devices[slot]=device;
+ pci_devices_installed++;
+ } else {
+ pci_devices[slot]->AddSubdevice(device);
+ }
+
+ return slot;
+ }
+
+ return -1;
+ }
+
+ void Deinitialize(void) {
+ initialized=false;
+ pci_devices_installed=0;
+ num_rqueued_devices=0;
+ pci_caddress=0;
+
+ for (Bitu dev=0; dev<PCI_MAX_PCIDEVICES; dev++)
+ for (Bitu fct=0; fct<PCI_MAX_PCIFUNCTIONS-1; fct++)
+ for (Bitu reg=0; reg<256; reg++)
+ pci_cfg_data[dev][fct][reg] = 0;
+
+ // install PCI-addressing ports
+ PCI_WriteHandler[0].Uninstall();
+ PCI_ReadHandler[0].Uninstall();
+ // install PCI-register read/write handlers
+ for (Bitu ct=0;ct<4;ct++) {
+ PCI_WriteHandler[1+ct].Uninstall();
+ PCI_ReadHandler[1+ct].Uninstall();
+ }
+
+ callback_pci.Uninstall();
+ }
+
+ void RemoveDevice(Bit16u vendor_id, Bit16u device_id) {
+ for (Bitu dct=0;dct<pci_devices_installed;dct++) {
+ if (pci_devices[dct]!=NULL) {
+ if (pci_devices[dct]->NumSubdevices()>0) {
+ for (Bitu sct=1;sct<PCI_MAX_PCIFUNCTIONS;sct++) {
+ PCI_Device* sdev=pci_devices[dct]->GetSubdevice(sct);
+ if (sdev!=NULL) {
+ if ((sdev->VendorID()==vendor_id) && (sdev->DeviceID()==device_id)) {
+ pci_devices[dct]->RemoveSubdevice(sct);
+ }
+ }
+ }
+ }
+
+ if ((pci_devices[dct]->VendorID()==vendor_id) && (pci_devices[dct]->DeviceID()==device_id)) {
+ delete pci_devices[dct];
+ pci_devices[dct]=NULL;
+ }
+ }
+ }
+
+ // check if all devices have been removed
+ bool any_device_left=false;
+ for (Bitu dct=0;dct<PCI_MAX_PCIDEVICES;dct++) {
+ if (dct>=pci_devices_installed) break;
+ if (pci_devices[dct]!=NULL) {
+ any_device_left=true;
+ break;
+ }
+ }
+ if (!any_device_left) Deinitialize();
+
+ Bitu last_active_device=PCI_MAX_PCIDEVICES;
+ for (Bitu dct=0;dct<PCI_MAX_PCIDEVICES;dct++) {
+ if (pci_devices[dct]!=NULL) last_active_device=dct;
+ }
+ if (last_active_device<pci_devices_installed)
+ pci_devices_installed=last_active_device+1;
+ }
+
+ PCI(Section* configuration):Module_base(configuration) {
+ initialized=false;
+ pci_devices_installed=0;
+
+ for (Bitu devct=0;devct<PCI_MAX_PCIDEVICES;devct++)
+ pci_devices[devct]=NULL;
+
+ if (num_rqueued_devices>0) {
+ // register all devices that have been added before the PCI bus was instantiated
+ for (Bitu dct=0;dct<num_rqueued_devices;dct++) {
+ this->RegisterPCIDevice(rqueued_devices[dct]);
+ }
+ num_rqueued_devices=0;
+ }
+ }
+
+ ~PCI(){
+ initialized=false;
+ pci_devices_installed=0;
+ num_rqueued_devices=0;
+ }
+
+};
+
+static PCI* pci_interface=NULL;
+
+
+PhysPt PCI_GetPModeInterface(void) {
+ if (pci_interface) {
+ return pci_interface->GetPModeCallbackPointer();
+ }
+ return 0;
+}
+
+bool PCI_IsInitialized() {
+ if (pci_interface) return pci_interface->IsInitialized();
+ return false;
+}
+
+
+void PCI_ShutDown(Section* sec){
+ delete pci_interface;
+ pci_interface=NULL;
+}
+
+void PCI_Init(Section* sec) {
+ pci_interface = new PCI(sec);
+ sec->AddDestroyFunction(&PCI_ShutDown,false);
+}
+
+#endif
diff --git a/src/hardware/pci_devices.h b/src/hardware/pci_devices.h
new file mode 100644
index 000000000..86f5f2dc8
--- /dev/null
+++ b/src/hardware/pci_devices.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
diff --git a/src/hardware/pcspeaker.cpp b/src/hardware/pcspeaker.cpp
new file mode 100644
index 000000000..bde5ba02e
--- /dev/null
+++ b/src/hardware/pcspeaker.cpp
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <math.h>
+#include "dosbox.h"
+#include "mixer.h"
+#include "timer.h"
+#include "setup.h"
+#include "pic.h"
+
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+#define SPKR_ENTRIES 1024
+#define SPKR_VOLUME 5000
+//#define SPKR_SHIFT 8
+#define SPKR_SPEED (float)((SPKR_VOLUME*2)/0.070f)
+
+enum SPKR_MODES {
+ SPKR_OFF,SPKR_ON,SPKR_PIT_OFF,SPKR_PIT_ON
+};
+
+struct DelayEntry {
+ float index;
+ float vol;
+};
+
+static struct {
+ MixerChannel * chan;
+ SPKR_MODES mode;
+ Bitu pit_mode;
+ Bitu rate;
+
+ float pit_last;
+ float pit_new_max,pit_new_half;
+ float pit_max,pit_half;
+ float pit_index;
+ float volwant,volcur;
+ Bitu last_ticks;
+ float last_index;
+ Bitu min_tr;
+ DelayEntry entries[SPKR_ENTRIES];
+ Bitu used;
+} spkr;
+
+static void AddDelayEntry(float index,float vol) {
+ if (spkr.used==SPKR_ENTRIES) {
+ return;
+ }
+ spkr.entries[spkr.used].index=index;
+ spkr.entries[spkr.used].vol=vol;
+ spkr.used++;
+}
+
+
+static void ForwardPIT(float newindex) {
+ float passed=(newindex-spkr.last_index);
+ float delay_base=spkr.last_index;
+ spkr.last_index=newindex;
+ switch (spkr.pit_mode) {
+ case 0:
+ return;
+ case 1:
+ return;
+ case 2:
+ while (passed>0) {
+ /* passed the initial low cycle? */
+ if (spkr.pit_index>=spkr.pit_half) {
+ /* Start a new low cycle */
+ if ((spkr.pit_index+passed)>=spkr.pit_max) {
+ float delay=spkr.pit_max-spkr.pit_index;
+ delay_base+=delay;passed-=delay;
+ spkr.pit_last=-SPKR_VOLUME;
+ if (spkr.mode==SPKR_PIT_ON) AddDelayEntry(delay_base,spkr.pit_last);
+ spkr.pit_index=0;
+ } else {
+ spkr.pit_index+=passed;
+ return;
+ }
+ } else {
+ if ((spkr.pit_index+passed)>=spkr.pit_half) {
+ float delay=spkr.pit_half-spkr.pit_index;
+ delay_base+=delay;passed-=delay;
+ spkr.pit_last=SPKR_VOLUME;
+ if (spkr.mode==SPKR_PIT_ON) AddDelayEntry(delay_base,spkr.pit_last);
+ spkr.pit_index=spkr.pit_half;
+ } else {
+ spkr.pit_index+=passed;
+ return;
+ }
+ }
+ }
+ break;
+ //END CASE 2
+ case 3:
+ while (passed>0) {
+ /* Determine where in the wave we're located */
+ if (spkr.pit_index>=spkr.pit_half) {
+ if ((spkr.pit_index+passed)>=spkr.pit_max) {
+ float delay=spkr.pit_max-spkr.pit_index;
+ delay_base+=delay;passed-=delay;
+ spkr.pit_last=SPKR_VOLUME;
+ if (spkr.mode==SPKR_PIT_ON) AddDelayEntry(delay_base,spkr.pit_last);
+ spkr.pit_index=0;
+ /* Load the new count */
+ spkr.pit_half=spkr.pit_new_half;
+ spkr.pit_max=spkr.pit_new_max;
+ } else {
+ spkr.pit_index+=passed;
+ return;
+ }
+ } else {
+ if ((spkr.pit_index+passed)>=spkr.pit_half) {
+ float delay=spkr.pit_half-spkr.pit_index;
+ delay_base+=delay;passed-=delay;
+ spkr.pit_last=-SPKR_VOLUME;
+ if (spkr.mode==SPKR_PIT_ON) AddDelayEntry(delay_base,spkr.pit_last);
+ spkr.pit_index=spkr.pit_half;
+ /* Load the new count */
+ spkr.pit_half=spkr.pit_new_half;
+ spkr.pit_max=spkr.pit_new_max;
+ } else {
+ spkr.pit_index+=passed;
+ return;
+ }
+ }
+ }
+ break;
+ //END CASE 3
+ case 4:
+ if (spkr.pit_index<spkr.pit_max) {
+ /* Check if we're gonna pass the end this block */
+ if (spkr.pit_index+passed>=spkr.pit_max) {
+ float delay=spkr.pit_max-spkr.pit_index;
+ delay_base+=delay;passed-=delay;
+ spkr.pit_last=-SPKR_VOLUME;
+ if (spkr.mode==SPKR_PIT_ON) AddDelayEntry(delay_base,spkr.pit_last); //No new events unless reprogrammed
+ spkr.pit_index=spkr.pit_max;
+ } else spkr.pit_index+=passed;
+ }
+ break;
+ //END CASE 4
+ }
+}
+
+void PCSPEAKER_SetCounter(Bitu cntr,Bitu mode) {
+ if (!spkr.last_ticks) {
+ if(spkr.chan) spkr.chan->Enable(true);
+ spkr.last_index=0;
+ }
+ spkr.last_ticks=PIC_Ticks;
+ float newindex=PIC_TickIndex();
+ ForwardPIT(newindex);
+ switch (mode) {
+ case 0: /* Mode 0 one shot, used with realsound */
+ if (spkr.mode!=SPKR_PIT_ON) return;
+ if (cntr>80) {
+ cntr=80;
+ }
+ spkr.pit_last=((float)cntr-40)*(SPKR_VOLUME/40.0f);
+ AddDelayEntry(newindex,spkr.pit_last);
+ spkr.pit_index=0;
+ break;
+ case 1:
+ if (spkr.mode!=SPKR_PIT_ON) return;
+ spkr.pit_last=SPKR_VOLUME;
+ AddDelayEntry(newindex,spkr.pit_last);
+ break;
+ case 2: /* Single cycle low, rest low high generator */
+ spkr.pit_index=0;
+ spkr.pit_last=-SPKR_VOLUME;
+ AddDelayEntry(newindex,spkr.pit_last);
+ spkr.pit_half=(1000.0f/PIT_TICK_RATE)*1;
+ spkr.pit_max=(1000.0f/PIT_TICK_RATE)*cntr;
+ break;
+ case 3: /* Square wave generator */
+ if (cntr==0 || cntr<spkr.min_tr) {
+ /* skip frequencies that can't be represented */
+ spkr.pit_last=0;
+ spkr.pit_mode=0;
+ return;
+ }
+ spkr.pit_new_max=(1000.0f/PIT_TICK_RATE)*cntr;
+ spkr.pit_new_half=spkr.pit_new_max/2;
+ break;
+ case 4: /* Software triggered strobe */
+ spkr.pit_last=SPKR_VOLUME;
+ AddDelayEntry(newindex,spkr.pit_last);
+ spkr.pit_index=0;
+ spkr.pit_max=(1000.0f/PIT_TICK_RATE)*cntr;
+ break;
+ default:
+#if C_DEBUG
+ LOG_MSG("Unhandled speaker mode %d",mode);
+#endif
+ return;
+ }
+ spkr.pit_mode=mode;
+}
+
+void PCSPEAKER_SetType(Bitu mode) {
+ if (!spkr.last_ticks) {
+ if(spkr.chan) spkr.chan->Enable(true);
+ spkr.last_index=0;
+ }
+ spkr.last_ticks=PIC_Ticks;
+ float newindex=PIC_TickIndex();
+ ForwardPIT(newindex);
+ switch (mode) {
+ case 0:
+ spkr.mode=SPKR_OFF;
+ AddDelayEntry(newindex,-SPKR_VOLUME);
+ break;
+ case 1:
+ spkr.mode=SPKR_PIT_OFF;
+ AddDelayEntry(newindex,-SPKR_VOLUME);
+ break;
+ case 2:
+ spkr.mode=SPKR_ON;
+ AddDelayEntry(newindex,SPKR_VOLUME);
+ break;
+ case 3:
+ if (spkr.mode!=SPKR_PIT_ON) {
+ AddDelayEntry(newindex,spkr.pit_last);
+ }
+ spkr.mode=SPKR_PIT_ON;
+ break;
+ };
+}
+
+static void PCSPEAKER_CallBack(Bitu len) {
+ Bit16s * stream=(Bit16s*)MixTemp;
+ ForwardPIT(1);
+ spkr.last_index=0;
+ Bitu count=len;
+ Bitu pos=0;
+ float sample_base=0;
+ float sample_add=(1.0001f)/len;
+ while (count--) {
+ float index=sample_base;
+ sample_base+=sample_add;
+ float end=sample_base;
+ double value=0;
+ while(index<end) {
+ /* Check if there is an upcoming event */
+ if (spkr.used && spkr.entries[pos].index<=index) {
+ spkr.volwant=spkr.entries[pos].vol;
+ pos++;spkr.used--;
+ continue;
+ }
+ float vol_end;
+ if (spkr.used && spkr.entries[pos].index<end) {
+ vol_end=spkr.entries[pos].index;
+ } else vol_end=end;
+ float vol_len=vol_end-index;
+ /* Check if we have to slide the volume */
+ float vol_diff=spkr.volwant-spkr.volcur;
+ if (vol_diff == 0) {
+ value+=spkr.volcur*vol_len;
+ index+=vol_len;
+ } else {
+ /* Check how long it will take to goto new level */
+ float vol_time=fabs(vol_diff)/SPKR_SPEED;
+ if (vol_time<=vol_len) {
+ /* Volume reaches endpoint in this block, calc until that point */
+ value+=vol_time*spkr.volcur;
+ value+=vol_time*vol_diff/2;
+ index+=vol_time;
+ spkr.volcur=spkr.volwant;
+ } else {
+ /* Volume still not reached in this block */
+ value+=spkr.volcur*vol_len;
+ if (vol_diff<0) {
+ value-=(SPKR_SPEED*vol_len*vol_len)/2;
+ spkr.volcur-=SPKR_SPEED*vol_len;
+ } else {
+ value+=(SPKR_SPEED*vol_len*vol_len)/2;
+ spkr.volcur+=SPKR_SPEED*vol_len;
+ }
+ index+=vol_len;
+ }
+ }
+ }
+ *stream++=(Bit16s)(value/sample_add);
+ }
+ if(spkr.chan) spkr.chan->AddSamples_m16(len,(Bit16s*)MixTemp);
+
+ //Turn off speaker after 10 seconds of idle or one second idle when in off mode
+ bool turnoff = false;
+ Bitu test_ticks = PIC_Ticks;
+ if ((spkr.last_ticks + 10000) < test_ticks) turnoff = true;
+ if((spkr.mode == SPKR_OFF) && ((spkr.last_ticks + 1000) < test_ticks)) turnoff = true;
+
+ if(turnoff){
+ if(spkr.volwant == 0) {
+ spkr.last_ticks = 0;
+ if(spkr.chan) spkr.chan->Enable(false);
+ } else {
+ if(spkr.volwant > 0) spkr.volwant--; else spkr.volwant++;
+
+ }
+ }
+
+}
+class PCSPEAKER:public Module_base {
+private:
+ MixerObject MixerChan;
+public:
+ PCSPEAKER(Section* configuration):Module_base(configuration){
+ spkr.chan=0;
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+ if(!section->Get_bool("pcspeaker")) return;
+ spkr.mode=SPKR_OFF;
+ spkr.last_ticks=0;
+ spkr.last_index=0;
+ spkr.rate=section->Get_int("pcrate");
+ spkr.pit_mode=3;
+ spkr.pit_max=(1000.0f/PIT_TICK_RATE)*1320;
+ spkr.pit_half=spkr.pit_max/2;
+ spkr.pit_new_max=spkr.pit_max;
+ spkr.pit_new_half=spkr.pit_half;
+ spkr.pit_index=0;
+ spkr.min_tr=(PIT_TICK_RATE+spkr.rate/2-1)/(spkr.rate/2);
+ spkr.used=0;
+ /* Register the sound channel */
+ spkr.chan=MixerChan.Install(&PCSPEAKER_CallBack,spkr.rate,"SPKR");
+ }
+ ~PCSPEAKER(){
+ Section_prop * section=static_cast<Section_prop *>(m_configuration);
+ if(!section->Get_bool("pcspeaker")) return;
+ }
+};
+static PCSPEAKER* test;
+
+void PCSPEAKER_ShutDown(Section* sec){
+ delete test;
+}
+
+void PCSPEAKER_Init(Section* sec) {
+ test = new PCSPEAKER(sec);
+ sec->AddDestroyFunction(&PCSPEAKER_ShutDown,true);
+}
diff --git a/src/hardware/pic.cpp b/src/hardware/pic.cpp
new file mode 100644
index 000000000..9a681d518
--- /dev/null
+++ b/src/hardware/pic.cpp
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include "dosbox.h"
+#include "inout.h"
+#include "cpu.h"
+#include "callback.h"
+#include "pic.h"
+#include "timer.h"
+#include "setup.h"
+
+#define PIC_QUEUESIZE 512
+
+struct PIC_Controller {
+ Bitu icw_words;
+ Bitu icw_index;
+ bool special;
+ bool auto_eoi;
+ bool rotate_on_auto_eoi;
+ bool single;
+ bool request_issr;
+ Bit8u vector_base;
+
+ Bit8u irr; // request register
+ Bit8u imr; // mask register
+ Bit8u imrr; // mask register reversed (makes bit tests simpler)
+ Bit8u isr; // in service register
+ Bit8u isrr; // in service register reversed (makes bit tests simpler)
+ Bit8u active_irq; //currently active irq
+
+
+ void set_imr(Bit8u val);
+
+ void check_after_EOI(){
+ //Update the active_irq as an EOI is likely to change that.
+ update_active_irq();
+ if((irr&imrr)&isrr) check_for_irq();
+ }
+
+ void update_active_irq() {
+ if(isr == 0) {active_irq = 8; return;}
+ for(Bit8u i = 0, s = 1; i < 8;i++, s<<=1){
+ if( isr & s){
+ active_irq = i;
+ return;
+ }
+ }
+ }
+
+ void check_for_irq(){
+ const Bit8u possible_irq = (irr&imrr)&isrr;
+ if (possible_irq) {
+ const Bit8u a_irq = special?8:active_irq;
+ for(Bit8u i = 0, s = 1; i < a_irq;i++, s<<=1){
+ if ( possible_irq & s ) {
+ //There is an irq ready to be served => signal master and/or cpu
+ activate();
+ return;
+ }
+ }
+ }
+ deactivate(); //No irq, remove signal to master and/or cpu
+ }
+
+ //Signals master/cpu that there is an irq ready.
+ void activate();
+
+ //Removes signal to master/cpu that there is an irq ready.
+ void deactivate();
+
+ void raise_irq(Bit8u val){
+ Bit8u bit = 1 << (val);
+ if((irr & bit)==0) { //value changed (as it is currently not active)
+ irr|=bit;
+ if((bit&imrr)&isrr) { //not masked and not in service
+ if(special || val < active_irq) activate();
+ }
+ }
+ }
+
+ void lower_irq(Bit8u val){
+ Bit8u bit = 1 << ( val);
+ if(irr & bit) { //value will change (as it is currently active)
+ irr&=~bit;
+ if((bit&imrr)&isrr) { //not masked and not in service
+ //This irq might have toggled PIC_IRQCheck/caused irq 2 on master, when it was raised.
+ //If it is active, then recheck it, we can't just deactivate as there might be more IRQS raised.
+ if(special || val < active_irq) check_for_irq();
+ }
+ }
+ }
+
+ //handles all bits and logic related to starting this IRQ, it does NOT start the interrupt on the CPU.
+ void start_irq(Bit8u val);
+};
+
+static PIC_Controller pics[2];
+static PIC_Controller& master = pics[0];
+static PIC_Controller& slave = pics[1];
+Bitu PIC_Ticks = 0;
+Bitu PIC_IRQCheck = 0; //Maybe make it a bool and/or ensure 32bit size (x86 dynamic core seems to assume 32 bit variable size)
+
+
+void PIC_Controller::set_imr(Bit8u val) {
+ if (GCC_UNLIKELY(machine==MCH_PCJR)) {
+ //irq 6 is a NMI on the PCJR
+ if (this == &master) val &= ~(1 <<(6));
+ }
+ Bit8u change = (imr) ^ (val); //Bits that have changed become 1.
+ imr = val;
+ imrr = ~val;
+
+ //Test if changed bits are set in irr and are not being served at the moment
+ //Those bits have impact on whether the cpu emulation should be paused or not.
+ if((irr & change)&isrr) check_for_irq();
+}
+
+void PIC_Controller::activate() {
+ //Stops CPU if master, signals master if slave
+ if(this == &master) {
+ PIC_IRQCheck = 1;
+ //cycles 0, take care of the port IO stuff added in raise_irq base caller.
+ CPU_CycleLeft += CPU_Cycles;
+ CPU_Cycles = 0;
+ //maybe when coming from a EOI, give a tiny delay. (for the cpu to pick it up) (see PIC_Activate_IRQ)
+ } else {
+ master.raise_irq(2);
+ }
+}
+
+void PIC_Controller::deactivate() {
+ //removes irq check value if master, signals master if slave
+ if(this == &master) {
+ PIC_IRQCheck = 0;
+ } else {
+ master.lower_irq(2);
+ }
+}
+
+void PIC_Controller::start_irq(Bit8u val){
+ irr&=~(1<<(val));
+ if (!auto_eoi) {
+ active_irq = val;
+ isr |= 1<<(val);
+ isrr = ~isr;
+ } else if (GCC_UNLIKELY(rotate_on_auto_eoi)) {
+ E_Exit("rotate on auto EOI not handled");
+ }
+}
+
+
+struct PICEntry {
+ float index;
+ Bitu value;
+ PIC_EventHandler pic_event;
+ PICEntry * next;
+};
+
+static struct {
+ PICEntry entries[PIC_QUEUESIZE];
+ PICEntry * free_entry;
+ PICEntry * next_entry;
+} pic_queue;
+
+static void write_command(Bitu port,Bitu val,Bitu iolen) {
+ PIC_Controller * pic=&pics[port==0x20 ? 0 : 1];
+
+ if (GCC_UNLIKELY(val&0x10)) { // ICW1 issued
+ if (val&0x04) E_Exit("PIC: 4 byte interval not handled");
+ if (val&0x08) E_Exit("PIC: level triggered mode not handled");
+ if (val&0xe0) E_Exit("PIC: 8080/8085 mode not handled");
+ pic->single=(val&0x02)==0x02;
+ pic->icw_index=1; // next is ICW2
+ pic->icw_words=2 + (val&0x01); // =3 if ICW4 needed
+ } else if (GCC_UNLIKELY(val&0x08)) { // OCW3 issued
+ if (val&0x04) E_Exit("PIC: poll command not handled");
+ if (val&0x02) { // function select
+ if (val&0x01) pic->request_issr=true; /* select read interrupt in-service register */
+ else pic->request_issr=false; /* select read interrupt request register */
+ }
+ if (val&0x40) { // special mask select
+ if (val&0x20) pic->special = true;
+ else pic->special = false;
+ //Check if there are irqs ready to run, as the priority system has possibly been changed.
+ pic->check_for_irq();
+ LOG(LOG_PIC,LOG_NORMAL)("port %X : special mask %s",port,(pic->special)?"ON":"OFF");
+ }
+ } else { // OCW2 issued
+ if (val&0x20) { // EOI commands
+ if (GCC_UNLIKELY(val&0x80)) E_Exit("rotate mode not supported");
+ if (val&0x40) { // specific EOI
+ pic->isr &= ~(1<< ((val-0x60)));
+ pic->isrr = ~pic->isr;
+ pic->check_after_EOI();
+// if (val&0x80); // perform rotation
+ } else { // nonspecific EOI
+ if (pic->active_irq != 8) {
+ //If there is no irq in service, ignore the call, some games send an eoi to both pics when a sound irq happens (regardless of the irq).
+ pic->isr &= ~(1 << (pic->active_irq));
+ pic->isrr = ~pic->isr;
+ pic->check_after_EOI();
+ }
+// if (val&0x80); // perform rotation
+ }
+ } else {
+ if ((val&0x40)==0) { // rotate in auto EOI mode
+ if (val&0x80) pic->rotate_on_auto_eoi=true;
+ else pic->rotate_on_auto_eoi=false;
+ } else if (val&0x80) {
+ LOG(LOG_PIC,LOG_NORMAL)("set priority command not handled");
+ } // else NOP command
+ }
+ } // end OCW2
+}
+
+static void write_data(Bitu port,Bitu val,Bitu iolen) {
+ PIC_Controller * pic=&pics[port==0x21 ? 0 : 1];
+ switch(pic->icw_index) {
+ case 0: /* mask register */
+ pic->set_imr(val);
+ break;
+ case 1: /* icw2 */
+ LOG(LOG_PIC,LOG_NORMAL)("%d:Base vector %X",port==0x21 ? 0 : 1,val);
+ pic->vector_base = val&0xf8;
+ if(pic->icw_index++ >= pic->icw_words) pic->icw_index=0;
+ else if(pic->single) pic->icw_index=3; /* skip ICW3 in single mode */
+ break;
+ case 2: /* icw 3 */
+ LOG(LOG_PIC,LOG_NORMAL)("%d:ICW 3 %X",port==0x21 ? 0 : 1,val);
+ if(pic->icw_index++ >= pic->icw_words) pic->icw_index=0;
+ break;
+ case 3: /* icw 4 */
+ /*
+ 0 1 8086/8080 0 mcs-8085 mode
+ 1 1 Auto EOI 0 Normal EOI
+ 2-3 0x Non buffer Mode
+ 10 Buffer Mode Slave
+ 11 Buffer mode Master
+ 4 Special/Not Special nested mode
+ */
+ pic->auto_eoi=(val & 0x2)>0;
+
+ LOG(LOG_PIC,LOG_NORMAL)("%d:ICW 4 %X",port==0x21 ? 0 : 1,val);
+
+ if ((val&0x01)==0) E_Exit("PIC:ICW4: %x, 8085 mode not handled",val);
+ if ((val&0x10)!=0) LOG_MSG("PIC:ICW4: %x, special fully-nested mode not handled",val);
+
+ if(pic->icw_index++ >= pic->icw_words) pic->icw_index=0;
+ break;
+ default:
+ LOG(LOG_PIC,LOG_NORMAL)("ICW HUH? %X",val);
+ break;
+ }
+}
+
+
+static Bitu read_command(Bitu port,Bitu iolen) {
+ PIC_Controller * pic=&pics[port==0x20 ? 0 : 1];
+ if (pic->request_issr){
+ return pic->isr;
+ } else {
+ return pic->irr;
+ }
+}
+
+
+static Bitu read_data(Bitu port,Bitu iolen) {
+ PIC_Controller * pic=&pics[port==0x21 ? 0 : 1];
+ return pic->imr;
+}
+
+void PIC_ActivateIRQ(Bitu irq) {
+ Bitu t = irq>7 ? (irq - 8): irq;
+ PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
+
+ Bit32s OldCycles = CPU_Cycles;
+ pic->raise_irq(t); //Will set the CPU_Cycles to zero if this IRQ will be handled directly
+
+ if (GCC_UNLIKELY(OldCycles != CPU_Cycles)) {
+ // if CPU_Cycles have changed, this means that the interrupt was triggered by an I/O
+ // register write rather than an event.
+ // Real hardware executes 0 to ~13 NOPs or comparable instructions
+ // before the processor picks up the interrupt. Let's try with 2
+ // cycles here.
+ // Required by Panic demo (irq0), It came from the desert (MPU401)
+ // Does it matter if CPU_CycleLeft becomes negative?
+
+ // It might be an idea to do this always in order to simulate this
+ // So on write mask and EOI as well. (so inside the activate function)
+// CPU_CycleLeft += (CPU_Cycles-2);
+ CPU_CycleLeft -= 2;
+ CPU_Cycles = 2;
+ }
+}
+
+void PIC_DeActivateIRQ(Bitu irq) {
+ Bitu t = irq>7 ? (irq - 8): irq;
+ PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
+ pic->lower_irq(t);
+}
+
+static void slave_startIRQ(){
+ Bit8u pic1_irq = 8;
+ const Bit8u p = (slave.irr & slave.imrr)&slave.isrr;
+ const Bit8u max = slave.special?8:slave.active_irq;
+ for(Bit8u i = 0,s = 1;i < max;i++, s<<=1){
+ if (p&s){
+ pic1_irq = i;
+ break;
+ }
+ }
+ // Maybe change the E_Exit to a return
+ if (GCC_UNLIKELY(pic1_irq == 8)) E_Exit("irq 2 is active, but no irq active on the slave PIC.");
+
+ slave.start_irq(pic1_irq);
+ master.start_irq(2);
+ CPU_HW_Interrupt(slave.vector_base + pic1_irq);
+}
+
+static void inline master_startIRQ(Bitu i){
+ master.start_irq(i);
+ CPU_HW_Interrupt(master.vector_base + i);
+}
+
+void PIC_runIRQs(void) {
+ if (!GETFLAG(IF)) return;
+ if (GCC_UNLIKELY(!PIC_IRQCheck)) return;
+ if (GCC_UNLIKELY(cpudecoder==CPU_Core_Normal_Trap_Run)) return;
+
+ const Bit8u p = (master.irr & master.imrr)&master.isrr;
+ const Bit8u max = master.special?8:master.active_irq;
+ for(Bit8u i = 0,s = 1;i < max;i++, s<<=1){
+ if (p&s){
+ if (i==2) { //second pic
+ slave_startIRQ();
+ } else {
+ master_startIRQ(i);
+ }
+ break;
+ }
+ }
+ //Disable check variable.
+ PIC_IRQCheck = 0;
+}
+
+void PIC_SetIRQMask(Bitu irq, bool masked) {
+ Bitu t = irq>7 ? (irq - 8): irq;
+ PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
+ //clear bit
+ Bit8u bit = 1 <<(t);
+ Bit8u newmask = pic->imr;
+ newmask &= ~bit;
+ if (masked) newmask |= bit;
+ pic->set_imr(newmask);
+}
+
+static void AddEntry(PICEntry * entry) {
+ PICEntry * find_entry=pic_queue.next_entry;
+ if (GCC_UNLIKELY(find_entry ==0)) {
+ entry->next=0;
+ pic_queue.next_entry=entry;
+ } else if (find_entry->index>entry->index) {
+ pic_queue.next_entry=entry;
+ entry->next=find_entry;
+ } else while (find_entry) {
+ if (find_entry->next) {
+ /* See if the next index comes later than this one */
+ if (find_entry->next->index > entry->index) {
+ entry->next=find_entry->next;
+ find_entry->next=entry;
+ break;
+ } else {
+ find_entry=find_entry->next;
+ }
+ } else {
+ entry->next=find_entry->next;
+ find_entry->next=entry;
+ break;
+ }
+ }
+ Bits cycles=PIC_MakeCycles(pic_queue.next_entry->index-PIC_TickIndex());
+ if (cycles<CPU_Cycles) {
+ CPU_CycleLeft+=CPU_Cycles;
+ CPU_Cycles=0;
+ }
+}
+static bool InEventService = false;
+static float srv_lag = 0;
+
+void PIC_AddEvent(PIC_EventHandler handler,float delay,Bitu val) {
+ if (GCC_UNLIKELY(!pic_queue.free_entry)) {
+ LOG(LOG_PIC,LOG_ERROR)("Event queue full");
+ return;
+ }
+ PICEntry * entry=pic_queue.free_entry;
+ if(InEventService) entry->index = delay + srv_lag;
+ else entry->index = delay + PIC_TickIndex();
+
+ entry->pic_event=handler;
+ entry->value=val;
+ pic_queue.free_entry=pic_queue.free_entry->next;
+ AddEntry(entry);
+}
+
+void PIC_RemoveSpecificEvents(PIC_EventHandler handler, Bitu val) {
+ PICEntry * entry=pic_queue.next_entry;
+ PICEntry * prev_entry;
+ prev_entry = 0;
+ while (entry) {
+ if (GCC_UNLIKELY((entry->pic_event == handler)) && (entry->value == val)) {
+ if (prev_entry) {
+ prev_entry->next=entry->next;
+ entry->next=pic_queue.free_entry;
+ pic_queue.free_entry=entry;
+ entry=prev_entry->next;
+ continue;
+ } else {
+ pic_queue.next_entry=entry->next;
+ entry->next=pic_queue.free_entry;
+ pic_queue.free_entry=entry;
+ entry=pic_queue.next_entry;
+ continue;
+ }
+ }
+ prev_entry=entry;
+ entry=entry->next;
+ }
+}
+
+void PIC_RemoveEvents(PIC_EventHandler handler) {
+ PICEntry * entry=pic_queue.next_entry;
+ PICEntry * prev_entry;
+ prev_entry=0;
+ while (entry) {
+ if (GCC_UNLIKELY(entry->pic_event==handler)) {
+ if (prev_entry) {
+ prev_entry->next=entry->next;
+ entry->next=pic_queue.free_entry;
+ pic_queue.free_entry=entry;
+ entry=prev_entry->next;
+ continue;
+ } else {
+ pic_queue.next_entry=entry->next;
+ entry->next=pic_queue.free_entry;
+ pic_queue.free_entry=entry;
+ entry=pic_queue.next_entry;
+ continue;
+ }
+ }
+ prev_entry=entry;
+ entry=entry->next;
+ }
+}
+
+
+bool PIC_RunQueue(void) {
+ /* Check to see if a new millisecond needs to be started */
+ CPU_CycleLeft+=CPU_Cycles;
+ CPU_Cycles=0;
+ if (CPU_CycleLeft<=0) {
+ return false;
+ }
+ /* Check the queue for an entry */
+ Bits index_nd=PIC_TickIndexND();
+ InEventService = true;
+ while (pic_queue.next_entry && (pic_queue.next_entry->index*CPU_CycleMax<=index_nd)) {
+ PICEntry * entry=pic_queue.next_entry;
+ pic_queue.next_entry=entry->next;
+
+ srv_lag = entry->index;
+ (entry->pic_event)(entry->value); // call the event handler
+
+ /* Put the entry in the free list */
+ entry->next=pic_queue.free_entry;
+ pic_queue.free_entry=entry;
+ }
+ InEventService = false;
+
+ /* Check when to set the new cycle end */
+ if (pic_queue.next_entry) {
+ Bits cycles=(Bits)(pic_queue.next_entry->index*CPU_CycleMax-index_nd);
+ if (GCC_UNLIKELY(!cycles)) cycles=1;
+ if (cycles<CPU_CycleLeft) {
+ CPU_Cycles=cycles;
+ } else {
+ CPU_Cycles=CPU_CycleLeft;
+ }
+ } else CPU_Cycles=CPU_CycleLeft;
+ CPU_CycleLeft-=CPU_Cycles;
+ if (PIC_IRQCheck) PIC_runIRQs();
+ return true;
+}
+
+/* The TIMER Part */
+struct TickerBlock {
+ TIMER_TickHandler handler;
+ TickerBlock * next;
+};
+
+static TickerBlock * firstticker=0;
+
+
+void TIMER_DelTickHandler(TIMER_TickHandler handler) {
+ TickerBlock * ticker=firstticker;
+ TickerBlock * * tick_where=&firstticker;
+ while (ticker) {
+ if (ticker->handler==handler) {
+ *tick_where=ticker->next;
+ delete ticker;
+ return;
+ }
+ tick_where=&ticker->next;
+ ticker=ticker->next;
+ }
+}
+
+void TIMER_AddTickHandler(TIMER_TickHandler handler) {
+ TickerBlock * newticker=new TickerBlock;
+ newticker->next=firstticker;
+ newticker->handler=handler;
+ firstticker=newticker;
+}
+
+void TIMER_AddTick(void) {
+ /* Setup new amount of cycles for PIC */
+ CPU_CycleLeft=CPU_CycleMax;
+ CPU_Cycles=0;
+ PIC_Ticks++;
+ /* Go through the list of scheduled events and lower their index with 1000 */
+ PICEntry * entry=pic_queue.next_entry;
+ while (entry) {
+ entry->index -= 1.0;
+ entry=entry->next;
+ }
+ /* Call our list of ticker handlers */
+ TickerBlock * ticker=firstticker;
+ while (ticker) {
+ TickerBlock * nextticker=ticker->next;
+ ticker->handler();
+ ticker=nextticker;
+ }
+}
+
+/* Use full name to avoid name clash with compile option for position-independent code */
+class PIC_8259A: public Module_base {
+private:
+ IO_ReadHandleObject ReadHandler[4];
+ IO_WriteHandleObject WriteHandler[4];
+public:
+ PIC_8259A(Section* configuration):Module_base(configuration){
+ /* Setup pic0 and pic1 with initial values like DOS has normally */
+ PIC_IRQCheck=0;
+ PIC_Ticks=0;
+ Bitu i;
+ for (i=0;i<2;i++) {
+ pics[i].auto_eoi=false;
+ pics[i].rotate_on_auto_eoi=false;
+ pics[i].request_issr=false;
+ pics[i].special=false;
+ pics[i].single=false;
+ pics[i].icw_index=0;
+ pics[i].icw_words=0;
+ pics[i].irr = pics[i].isr = pics[i].imrr = 0;
+ pics[i].isrr = pics[i].imr = 0xff;
+ pics[i].active_irq = 8;
+ }
+ master.vector_base = 0x08;
+ slave.vector_base = 0x70;
+
+ PIC_SetIRQMask(0,false); /* Enable system timer */
+ PIC_SetIRQMask(1,false); /* Enable system timer */
+ PIC_SetIRQMask(2,false); /* Enable second pic */
+ PIC_SetIRQMask(8,false); /* Enable RTC IRQ */
+
+ if (machine==MCH_PCJR) {
+ /* Enable IRQ6 (replacement for the NMI for PCJr) */
+ PIC_SetIRQMask(6,false);
+ }
+ ReadHandler[0].Install(0x20,read_command,IO_MB);
+ ReadHandler[1].Install(0x21,read_data,IO_MB);
+ WriteHandler[0].Install(0x20,write_command,IO_MB);
+ WriteHandler[1].Install(0x21,write_data,IO_MB);
+ ReadHandler[2].Install(0xa0,read_command,IO_MB);
+ ReadHandler[3].Install(0xa1,read_data,IO_MB);
+ WriteHandler[2].Install(0xa0,write_command,IO_MB);
+ WriteHandler[3].Install(0xa1,write_data,IO_MB);
+ /* Initialize the pic queue */
+ for (i=0;i<PIC_QUEUESIZE-1;i++) {
+ pic_queue.entries[i].next=&pic_queue.entries[i+1];
+ }
+ pic_queue.entries[PIC_QUEUESIZE-1].next=0;
+ pic_queue.free_entry=&pic_queue.entries[0];
+ pic_queue.next_entry=0;
+ }
+
+ ~PIC_8259A(){
+ }
+};
+
+static PIC_8259A* test;
+
+void PIC_Destroy(Section* sec){
+ delete test;
+}
+
+void PIC_Init(Section* sec) {
+ test = new PIC_8259A(sec);
+ sec->AddDestroyFunction(&PIC_Destroy);
+}
diff --git a/src/hardware/sblaster.cpp b/src/hardware/sblaster.cpp
new file mode 100644
index 000000000..e4a9b0295
--- /dev/null
+++ b/src/hardware/sblaster.cpp
@@ -0,0 +1,1738 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <iomanip>
+#include <sstream>
+#include <string.h>
+#include <math.h>
+#include "dosbox.h"
+#include "inout.h"
+#include "mixer.h"
+#include "dma.h"
+#include "pic.h"
+#include "hardware.h"
+#include "setup.h"
+#include "support.h"
+#include "shell.h"
+using namespace std;
+
+void MIDI_RawOutByte(Bit8u data);
+bool MIDI_Available(void);
+
+#define SB_PIC_EVENTS 0
+
+#define DSP_MAJOR 3
+#define DSP_MINOR 1
+
+#define MIXER_INDEX 0x04
+#define MIXER_DATA 0x05
+
+#define DSP_RESET 0x06
+#define DSP_READ_DATA 0x0A
+#define DSP_WRITE_DATA 0x0C
+#define DSP_WRITE_STATUS 0x0C
+#define DSP_READ_STATUS 0x0E
+#define DSP_ACK_16BIT 0x0f
+
+#define DSP_NO_COMMAND 0
+
+#define DMA_BUFSIZE 1024
+#define DSP_BUFSIZE 64
+#define DSP_DACSIZE 512
+
+//Should be enough for sound generated in millisecond blocks
+#define SB_BUF_SIZE 8096
+#define SB_SH 14
+#define SB_SH_MASK ((1 << SB_SH)-1)
+
+enum {DSP_S_RESET,DSP_S_RESET_WAIT,DSP_S_NORMAL,DSP_S_HIGHSPEED};
+enum SB_TYPES {SBT_NONE=0,SBT_1=1,SBT_PRO1=2,SBT_2=3,SBT_PRO2=4,SBT_16=6,SBT_GB=7};
+enum SB_IRQS {SB_IRQ_8,SB_IRQ_16,SB_IRQ_MPU};
+
+enum DSP_MODES {
+ MODE_NONE,
+ MODE_DAC,
+ MODE_DMA,
+ MODE_DMA_PAUSE,
+ MODE_DMA_MASKED
+
+};
+
+enum DMA_MODES {
+ DSP_DMA_NONE,
+ DSP_DMA_2,DSP_DMA_3,DSP_DMA_4,DSP_DMA_8,
+ DSP_DMA_16,DSP_DMA_16_ALIASED
+};
+
+enum {
+ PLAY_MONO,PLAY_STEREO
+};
+
+struct SB_INFO {
+ Bitu freq;
+ struct {
+ bool stereo,sign,autoinit;
+ DMA_MODES mode;
+ Bitu rate,mul;
+ Bitu total,left,min;
+ Bit64u start;
+ union {
+ Bit8u b8[DMA_BUFSIZE];
+ Bit16s b16[DMA_BUFSIZE];
+ } buf;
+ Bitu bits;
+ DmaChannel * chan;
+ Bitu remain_size;
+ } dma;
+ bool speaker;
+ bool midi;
+ Bit8u time_constant;
+ DSP_MODES mode;
+ SB_TYPES type;
+ struct {
+ bool pending_8bit;
+ bool pending_16bit;
+ } irq;
+ struct {
+ Bit8u state;
+ Bit8u cmd;
+ Bit8u cmd_len;
+ Bit8u cmd_in_pos;
+ Bit8u cmd_in[DSP_BUFSIZE];
+ struct {
+ Bit8u lastval;
+ Bit8u data[DSP_BUFSIZE];
+ Bitu pos,used;
+ } in,out;
+ Bit8u test_register;
+ Bitu write_busy;
+ } dsp;
+ struct {
+ Bit16s data[DSP_DACSIZE+1];
+ Bitu used;
+ Bit16s last;
+ } dac;
+ struct {
+ Bit8u index;
+ Bit8u dac[2],fm[2],cda[2],master[2],lin[2];
+ Bit8u mic;
+ bool stereo;
+ bool enabled;
+ bool filtered;
+ Bit8u unhandled[0x48];
+ } mixer;
+ struct {
+ Bit8u reference;
+ Bits stepsize;
+ bool haveref;
+ } adpcm;
+ struct {
+ Bitu base;
+ Bitu irq;
+ Bit8u dma8,dma16;
+ } hw;
+ struct {
+ Bits value;
+ Bitu count;
+ } e2;
+ MixerChannel * chan;
+};
+
+
+
+static SB_INFO sb;
+
+static char const * const copyright_string="COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
+
+// number of bytes in input for commands (sb/sbpro)
+static Bit8u DSP_cmd_len_sb[256] = {
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x00
+// 1,0,0,0, 2,0,2,2, 0,0,0,0, 0,0,0,0, // 0x10
+ 1,0,0,0, 2,2,2,2, 0,0,0,0, 0,0,0,0, // 0x10 Wari hack
+ 0,0,0,0, 2,0,0,0, 0,0,0,0, 0,0,0,0, // 0x20
+ 0,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0, // 0x30
+
+ 1,2,2,0, 0,0,0,0, 2,0,0,0, 0,0,0,0, // 0x40
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x50
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x60
+ 0,0,0,0, 2,2,2,2, 0,0,0,0, 0,0,0,0, // 0x70
+
+ 2,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x80
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x90
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0xa0
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0xb0
+
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0xc0
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0xd0
+ 1,0,1,0, 1,0,0,0, 0,0,0,0, 0,0,0,0, // 0xe0
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 // 0xf0
+};
+
+// number of bytes in input for commands (sb16)
+static Bit8u DSP_cmd_len_sb16[256] = {
+ 0,0,0,0, 1,2,0,0, 1,0,0,0, 0,0,2,1, // 0x00
+// 1,0,0,0, 2,0,2,2, 0,0,0,0, 0,0,0,0, // 0x10
+ 1,0,0,0, 2,2,2,2, 0,0,0,0, 0,0,0,0, // 0x10 Wari hack
+ 0,0,0,0, 2,0,0,0, 0,0,0,0, 0,0,0,0, // 0x20
+ 0,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0, // 0x30
+
+ 1,2,2,0, 0,0,0,0, 2,0,0,0, 0,0,0,0, // 0x40
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x50
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x60
+ 0,0,0,0, 2,2,2,2, 0,0,0,0, 0,0,0,0, // 0x70
+
+ 2,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x80
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x90
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0xa0
+ 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xb0
+
+ 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xc0
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0xd0
+ 1,0,1,0, 1,0,0,0, 0,0,0,0, 0,0,0,0, // 0xe0
+ 0,0,0,0, 0,0,0,0, 0,1,0,0, 0,0,0,0 // 0xf0
+};
+
+static Bit8u ASP_regs[256];
+static bool ASP_init_in_progress = false;
+
+static int E2_incr_table[4][9] = {
+ { 0x01, -0x02, -0x04, 0x08, -0x10, 0x20, 0x40, -0x80, -106 },
+ { -0x01, 0x02, -0x04, 0x08, 0x10, -0x20, 0x40, -0x80, 165 },
+ { -0x01, 0x02, 0x04, -0x08, 0x10, -0x20, -0x40, 0x80, -151 },
+ { 0x01, -0x02, 0x04, -0x08, -0x10, 0x20, -0x40, 0x80, 90 }
+};
+
+#ifndef max
+#define max(a,b) ((a)>(b)?(a):(b))
+#endif
+#ifndef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+
+static void DSP_ChangeMode(DSP_MODES mode);
+static void CheckDMAEnd();
+static void END_DMA_Event(Bitu);
+static void DMA_Silent_Event(Bitu val);
+static void GenerateDMASound(Bitu size);
+
+static void DSP_SetSpeaker(bool how) {
+ if (sb.speaker==how) return;
+ sb.speaker=how;
+ if (sb.type==SBT_16) return;
+ sb.chan->Enable(how);
+ if (sb.speaker) {
+ PIC_RemoveEvents(DMA_Silent_Event);
+ CheckDMAEnd();
+ } else {
+
+ }
+}
+
+static INLINE void SB_RaiseIRQ(SB_IRQS type) {
+ LOG(LOG_SB,LOG_NORMAL)("Raising IRQ");
+ switch (type) {
+ case SB_IRQ_8:
+ if (sb.irq.pending_8bit) {
+// LOG_MSG("SB: 8bit irq pending");
+ return;
+ }
+ sb.irq.pending_8bit=true;
+ PIC_ActivateIRQ(sb.hw.irq);
+ break;
+ case SB_IRQ_16:
+ if (sb.irq.pending_16bit) {
+// LOG_MSG("SB: 16bit irq pending");
+ return;
+ }
+ sb.irq.pending_16bit=true;
+ PIC_ActivateIRQ(sb.hw.irq);
+ break;
+ default:
+ break;
+ }
+}
+
+static INLINE void DSP_FlushData(void) {
+ sb.dsp.out.used=0;
+ sb.dsp.out.pos=0;
+}
+
+static double last_dma_callback = 0.0f;
+
+static void DSP_DMA_CallBack(DmaChannel * chan, DMAEvent event) {
+ if (chan!=sb.dma.chan || event==DMA_REACHED_TC) return;
+ else if (event==DMA_MASKED) {
+ if (sb.mode==MODE_DMA) {
+ //Catch up to current time, but don't generate an IRQ!
+ //Fixes problems with later sci games.
+ double t = PIC_FullIndex() - last_dma_callback;
+ Bitu s = static_cast<Bitu>(sb.dma.rate * t / 1000.0f);
+ if (s > sb.dma.min) {
+ LOG(LOG_SB,LOG_NORMAL)("limiting amount masked to sb.dma.min");
+ s = sb.dma.min;
+ }
+ Bitu min_size = sb.dma.mul >> SB_SH;
+ if (!min_size) min_size = 1;
+ min_size *= 2;
+ if (sb.dma.left > min_size) {
+ if (s > (sb.dma.left-min_size)) s = sb.dma.left - min_size;
+ GenerateDMASound(s);
+ }
+ sb.mode=MODE_DMA_MASKED;
+// DSP_ChangeMode(MODE_DMA_MASKED);
+ LOG(LOG_SB,LOG_NORMAL)("DMA masked,stopping output, left %d",chan->currcnt);
+ }
+ } else if (event==DMA_TRANSFEREND) {
+ if (sb.mode==MODE_DMA) sb.mode=MODE_DMA_MASKED;
+ } else if (event==DMA_UNMASKED) {
+ if (sb.mode==MODE_DMA_MASKED && sb.dma.mode!=DSP_DMA_NONE) {
+ DSP_ChangeMode(MODE_DMA);
+// sb.mode=MODE_DMA;
+ CheckDMAEnd();
+ LOG(LOG_SB,LOG_NORMAL)("DMA unmasked,starting output, auto %d block %d",chan->autoinit,chan->basecnt);
+ }
+ }
+}
+
+#define MIN_ADAPTIVE_STEP_SIZE 0
+#define MAX_ADAPTIVE_STEP_SIZE 32767
+#define DC_OFFSET_FADE 254
+
+static INLINE Bit8u decode_ADPCM_4_sample(Bit8u sample,Bit8u & reference,Bits& scale) {
+ static const Bit8s scaleMap[64] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 0, -1, -2, -3, -4, -5, -6, -7,
+ 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15,
+ 2, 6, 10, 14, 18, 22, 26, 30, -2, -6, -10, -14, -18, -22, -26, -30,
+ 4, 12, 20, 28, 36, 44, 52, 60, -4, -12, -20, -28, -36, -44, -52, -60
+ };
+ static const Bit8u adjustMap[64] = {
+ 0, 0, 0, 0, 0, 16, 16, 16,
+ 0, 0, 0, 0, 0, 16, 16, 16,
+ 240, 0, 0, 0, 0, 16, 16, 16,
+ 240, 0, 0, 0, 0, 16, 16, 16,
+ 240, 0, 0, 0, 0, 16, 16, 16,
+ 240, 0, 0, 0, 0, 16, 16, 16,
+ 240, 0, 0, 0, 0, 0, 0, 0,
+ 240, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ Bits samp = sample + scale;
+
+ if ((samp < 0) || (samp > 63)) {
+ LOG(LOG_SB,LOG_ERROR)("Bad ADPCM-4 sample");
+ if(samp < 0 ) samp = 0;
+ if(samp > 63) samp = 63;
+ }
+
+ Bits ref = reference + scaleMap[samp];
+ if (ref > 0xff) reference = 0xff;
+ else if (ref < 0x00) reference = 0x00;
+ else reference = (Bit8u)(ref&0xff);
+ scale = (scale + adjustMap[samp]) & 0xff;
+
+ return reference;
+}
+
+static INLINE Bit8u decode_ADPCM_2_sample(Bit8u sample,Bit8u & reference,Bits& scale) {
+ static const Bit8s scaleMap[24] = {
+ 0, 1, 0, -1, 1, 3, -1, -3,
+ 2, 6, -2, -6, 4, 12, -4, -12,
+ 8, 24, -8, -24, 6, 48, -16, -48
+ };
+ static const Bit8u adjustMap[24] = {
+ 0, 4, 0, 4,
+ 252, 4, 252, 4, 252, 4, 252, 4,
+ 252, 4, 252, 4, 252, 4, 252, 4,
+ 252, 0, 252, 0
+ };
+
+ Bits samp = sample + scale;
+ if ((samp < 0) || (samp > 23)) {
+ LOG(LOG_SB,LOG_ERROR)("Bad ADPCM-2 sample");
+ if(samp < 0 ) samp = 0;
+ if(samp > 23) samp = 23;
+ }
+
+ Bits ref = reference + scaleMap[samp];
+ if (ref > 0xff) reference = 0xff;
+ else if (ref < 0x00) reference = 0x00;
+ else reference = (Bit8u)(ref&0xff);
+ scale = (scale + adjustMap[samp]) & 0xff;
+
+ return reference;
+}
+
+INLINE Bit8u decode_ADPCM_3_sample(Bit8u sample,Bit8u & reference,Bits& scale) {
+ static const Bit8s scaleMap[40] = {
+ 0, 1, 2, 3, 0, -1, -2, -3,
+ 1, 3, 5, 7, -1, -3, -5, -7,
+ 2, 6, 10, 14, -2, -6, -10, -14,
+ 4, 12, 20, 28, -4, -12, -20, -28,
+ 5, 15, 25, 35, -5, -15, -25, -35
+ };
+ static const Bit8u adjustMap[40] = {
+ 0, 0, 0, 8, 0, 0, 0, 8,
+ 248, 0, 0, 8, 248, 0, 0, 8,
+ 248, 0, 0, 8, 248, 0, 0, 8,
+ 248, 0, 0, 8, 248, 0, 0, 8,
+ 248, 0, 0, 0, 248, 0, 0, 0
+ };
+
+ Bits samp = sample + scale;
+ if ((samp < 0) || (samp > 39)) {
+ LOG(LOG_SB,LOG_ERROR)("Bad ADPCM-3 sample");
+ if(samp < 0 ) samp = 0;
+ if(samp > 39) samp = 39;
+ }
+
+ Bits ref = reference + scaleMap[samp];
+ if (ref > 0xff) reference = 0xff;
+ else if (ref < 0x00) reference = 0x00;
+ else reference = (Bit8u)(ref&0xff);
+ scale = (scale + adjustMap[samp]) & 0xff;
+
+ return reference;
+}
+
+static void GenerateDMASound(Bitu size) {
+ Bitu read=0;Bitu done=0;Bitu i=0;
+ last_dma_callback = PIC_FullIndex();
+
+ if(sb.dma.autoinit) {
+ if (sb.dma.left <= size)
+ size = sb.dma.left;
+ } else {
+ if (sb.dma.left <= sb.dma.min)
+ size = sb.dma.left;
+ }
+
+ switch (sb.dma.mode) {
+ case DSP_DMA_2:
+ read=sb.dma.chan->Read(size,sb.dma.buf.b8);
+ if (read && sb.adpcm.haveref) {
+ sb.adpcm.haveref=false;
+ sb.adpcm.reference=sb.dma.buf.b8[0];
+ sb.adpcm.stepsize=MIN_ADAPTIVE_STEP_SIZE;
+ i++;
+ }
+ for (;i<read;i++) {
+ MixTemp[done++]=decode_ADPCM_2_sample((sb.dma.buf.b8[i] >> 6) & 0x3,sb.adpcm.reference,sb.adpcm.stepsize);
+ MixTemp[done++]=decode_ADPCM_2_sample((sb.dma.buf.b8[i] >> 4) & 0x3,sb.adpcm.reference,sb.adpcm.stepsize);
+ MixTemp[done++]=decode_ADPCM_2_sample((sb.dma.buf.b8[i] >> 2) & 0x3,sb.adpcm.reference,sb.adpcm.stepsize);
+ MixTemp[done++]=decode_ADPCM_2_sample((sb.dma.buf.b8[i] >> 0) & 0x3,sb.adpcm.reference,sb.adpcm.stepsize);
+ }
+ sb.chan->AddSamples_m8(done,MixTemp);
+ break;
+ case DSP_DMA_3:
+ read=sb.dma.chan->Read(size,sb.dma.buf.b8);
+ if (read && sb.adpcm.haveref) {
+ sb.adpcm.haveref=false;
+ sb.adpcm.reference=sb.dma.buf.b8[0];
+ sb.adpcm.stepsize=MIN_ADAPTIVE_STEP_SIZE;
+ i++;
+ }
+ for (;i<read;i++) {
+ MixTemp[done++]=decode_ADPCM_3_sample((sb.dma.buf.b8[i] >> 5) & 0x7,sb.adpcm.reference,sb.adpcm.stepsize);
+ MixTemp[done++]=decode_ADPCM_3_sample((sb.dma.buf.b8[i] >> 2) & 0x7,sb.adpcm.reference,sb.adpcm.stepsize);
+ MixTemp[done++]=decode_ADPCM_3_sample((sb.dma.buf.b8[i] & 0x3) << 1,sb.adpcm.reference,sb.adpcm.stepsize);
+ }
+ sb.chan->AddSamples_m8(done,MixTemp);
+ break;
+ case DSP_DMA_4:
+ read=sb.dma.chan->Read(size,sb.dma.buf.b8);
+ if (read && sb.adpcm.haveref) {
+ sb.adpcm.haveref=false;
+ sb.adpcm.reference=sb.dma.buf.b8[0];
+ sb.adpcm.stepsize=MIN_ADAPTIVE_STEP_SIZE;
+ i++;
+ }
+ for (;i<read;i++) {
+ MixTemp[done++]=decode_ADPCM_4_sample(sb.dma.buf.b8[i] >> 4,sb.adpcm.reference,sb.adpcm.stepsize);
+ MixTemp[done++]=decode_ADPCM_4_sample(sb.dma.buf.b8[i]& 0xf,sb.adpcm.reference,sb.adpcm.stepsize);
+ }
+ sb.chan->AddSamples_m8(done,MixTemp);
+ break;
+ case DSP_DMA_8:
+ if (sb.dma.stereo) {
+ read=sb.dma.chan->Read(size,&sb.dma.buf.b8[sb.dma.remain_size]);
+ Bitu total=read+sb.dma.remain_size;
+ if (!sb.dma.sign) sb.chan->AddSamples_s8(total>>1,sb.dma.buf.b8);
+ else sb.chan->AddSamples_s8s(total>>1,(Bit8s*)sb.dma.buf.b8);
+ if (total&1) {
+ sb.dma.remain_size=1;
+ sb.dma.buf.b8[0]=sb.dma.buf.b8[total-1];
+ } else sb.dma.remain_size=0;
+ } else {
+ read=sb.dma.chan->Read(size,sb.dma.buf.b8);
+ if (!sb.dma.sign) sb.chan->AddSamples_m8(read,sb.dma.buf.b8);
+ else sb.chan->AddSamples_m8s(read,(Bit8s *)sb.dma.buf.b8);
+ }
+ break;
+ case DSP_DMA_16:
+ case DSP_DMA_16_ALIASED:
+ if (sb.dma.stereo) {
+ /* In DSP_DMA_16_ALIASED mode temporarily divide by 2 to get number of 16-bit
+ samples, because 8-bit DMA Read returns byte size, while in DSP_DMA_16 mode
+ 16-bit DMA Read returns word size */
+ read=sb.dma.chan->Read(size,(Bit8u *)&sb.dma.buf.b16[sb.dma.remain_size])
+ >> (sb.dma.mode==DSP_DMA_16_ALIASED ? 1:0);
+ Bitu total=read+sb.dma.remain_size;
+#if defined(WORDS_BIGENDIAN)
+ if (sb.dma.sign) sb.chan->AddSamples_s16_nonnative(total>>1,sb.dma.buf.b16);
+ else sb.chan->AddSamples_s16u_nonnative(total>>1,(Bit16u *)sb.dma.buf.b16);
+#else
+ if (sb.dma.sign) sb.chan->AddSamples_s16(total>>1,sb.dma.buf.b16);
+ else sb.chan->AddSamples_s16u(total>>1,(Bit16u *)sb.dma.buf.b16);
+#endif
+ if (total&1) {
+ sb.dma.remain_size=1;
+ sb.dma.buf.b16[0]=sb.dma.buf.b16[total-1];
+ } else sb.dma.remain_size=0;
+ } else {
+ read=sb.dma.chan->Read(size,(Bit8u *)sb.dma.buf.b16)
+ >> (sb.dma.mode==DSP_DMA_16_ALIASED ? 1:0);
+#if defined(WORDS_BIGENDIAN)
+ if (sb.dma.sign) sb.chan->AddSamples_m16_nonnative(read,sb.dma.buf.b16);
+ else sb.chan->AddSamples_m16u_nonnative(read,(Bit16u *)sb.dma.buf.b16);
+#else
+ if (sb.dma.sign) sb.chan->AddSamples_m16(read,sb.dma.buf.b16);
+ else sb.chan->AddSamples_m16u(read,(Bit16u *)sb.dma.buf.b16);
+#endif
+ }
+ //restore buffer length value to byte size in aliased mode
+ if (sb.dma.mode==DSP_DMA_16_ALIASED) read=read<<1;
+ break;
+ default:
+ LOG_MSG("Unhandled dma mode %d",sb.dma.mode);
+ sb.mode=MODE_NONE;
+ return;
+ }
+ sb.dma.left-=read;
+ if (!sb.dma.left) {
+ PIC_RemoveEvents(END_DMA_Event);
+ if (sb.dma.mode >= DSP_DMA_16)
+ SB_RaiseIRQ(SB_IRQ_16);
+ else
+ SB_RaiseIRQ(SB_IRQ_8);
+ //Copy the new size
+ sb.dma.left = sb.dma.total;
+ if (!sb.dma.autoinit) {
+ if (!sb.dma.left) {
+ LOG(LOG_SB, LOG_NORMAL)("Single cycle transfer ended");
+ sb.mode = MODE_NONE;
+ sb.dma.mode = DSP_DMA_NONE;
+ }
+ else {
+ //Copied this value as the count for the final single cycle
+ sb.dma.total = 0;
+ LOG(LOG_SB, LOG_NORMAL)("Switch to Single cycle transfer begun");
+ }
+ } else {
+ if (!sb.dma.left) {
+ LOG(LOG_SB,LOG_NORMAL)("Auto-init transfer with 0 size");
+ sb.mode=MODE_NONE;
+ }
+ }
+ }
+}
+
+/* old version...
+static void GenerateDACSound(Bitu len) {
+ if (!sb.dac.used) {
+ sb.mode=MODE_NONE;
+ return;
+ }
+ Bitu dac_add=(sb.dac.used<<16)/len;
+ Bitu dac_pos=0;
+ Bit16s * out=(Bit16s *)MixTemp;
+ for (Bitu i=len;i;i--) {
+ *out++=sb.dac.data[0+(dac_pos>>16)];
+ dac_pos+=dac_add;
+ }
+ sb.dac.used=0;
+ sb.chan->AddSamples_m16(len,(Bit16s *)MixTemp);
+}
+*/
+
+static void DMA_Silent_Event(Bitu val) {
+ if (sb.dma.left<val) val=sb.dma.left;
+ Bitu read=sb.dma.chan->Read(val,sb.dma.buf.b8);
+ sb.dma.left-=read;
+ if (!sb.dma.left) {
+ if (sb.dma.mode >= DSP_DMA_16) SB_RaiseIRQ(SB_IRQ_16);
+ else SB_RaiseIRQ(SB_IRQ_8);
+ if (sb.dma.autoinit)
+ sb.dma.left=sb.dma.total;
+ else {
+ sb.mode=MODE_NONE;
+ sb.dma.mode=DSP_DMA_NONE;
+ }
+ }
+ if (sb.dma.left) {
+ Bitu bigger=(sb.dma.left > sb.dma.min) ? sb.dma.min : sb.dma.left;
+ float delay=(bigger*1000.0f)/sb.dma.rate;
+ PIC_AddEvent(DMA_Silent_Event,delay,bigger);
+ }
+
+}
+
+static void END_DMA_Event(Bitu val) {
+ GenerateDMASound(val);
+}
+
+static void CheckDMAEnd(void) {
+ if (!sb.dma.left) return;
+ if (!sb.speaker && sb.type!=SBT_16) {
+ Bitu bigger=(sb.dma.left > sb.dma.min) ? sb.dma.min : sb.dma.left;
+ float delay=(bigger*1000.0f)/sb.dma.rate;
+ PIC_AddEvent(DMA_Silent_Event,delay,bigger);
+ LOG(LOG_SB,LOG_NORMAL)("Silent DMA Transfer scheduling IRQ in %.3f milliseconds",delay);
+ } else if (sb.dma.left<sb.dma.min) {
+ float delay=(sb.dma.left*1000.0f)/sb.dma.rate;
+ LOG(LOG_SB,LOG_NORMAL)("Short transfer scheduling IRQ in %.3f milliseconds",delay);
+ PIC_AddEvent(END_DMA_Event,delay,sb.dma.left);
+ }
+}
+
+static void DSP_ChangeMode(DSP_MODES mode) {
+ if (sb.mode==mode) return;
+ else sb.chan->FillUp();
+ sb.mode=mode;
+}
+
+static void DSP_RaiseIRQEvent(Bitu /*val*/) {
+ SB_RaiseIRQ(SB_IRQ_8);
+}
+
+static void DSP_DoDMATransfer(DMA_MODES mode,Bitu freq,bool autoinit, bool stereo) {
+ char const * type;
+ //Fill up before changing state?
+ sb.chan->FillUp();
+
+ //Starting a new transfer will clear any active irqs?
+ sb.irq.pending_8bit = false;
+ sb.irq.pending_16bit = false;
+ PIC_DeActivateIRQ(sb.hw.irq);
+
+ switch (mode) {
+ case DSP_DMA_2:
+ type="2-bits ADPCM";
+ sb.dma.mul=(1 << SB_SH)/4;
+ break;
+ case DSP_DMA_3:
+ type="3-bits ADPCM";
+ sb.dma.mul=(1 << SB_SH)/3;
+ break;
+ case DSP_DMA_4:
+ type="4-bits ADPCM";
+ sb.dma.mul=(1 << SB_SH)/2;
+ break;
+ case DSP_DMA_8:
+ type="8-bits PCM";
+ sb.dma.mul=(1 << SB_SH);
+ break;
+ case DSP_DMA_16_ALIASED:
+ type="16-bits(aliased) PCM";
+ sb.dma.mul=(1 << SB_SH)*2;
+ break;
+ case DSP_DMA_16:
+ type="16-bits PCM";
+ sb.dma.mul=(1 << SB_SH);
+ break;
+ default:
+ LOG(LOG_SB,LOG_ERROR)("DSP:Illegal transfer mode %d",mode);
+ return;
+ }
+
+#if (C_DEBUG)
+ LOG(LOG_SB, LOG_NORMAL)("DMA Transfer:%s %s %s freq %d rate %d size %d",
+ type,
+ stereo ? "Stereo" : "Mono",
+ autoinit ? "Auto-Init" : "Single-Cycle",
+ freq, sb.dma.rate, sb.dma.total
+ );
+#endif
+ //Going from an active autoinit into a single cycle
+ if (sb.mode >= MODE_DMA && sb.dma.autoinit && !autoinit) {
+ //Don't do anything, the total will flip over on the next transfer
+ }
+ //Just a normal single cycle transfer
+ else if (!autoinit) {
+ sb.dma.left = sb.dma.total;
+ sb.dma.total = 0;
+ }
+ //Going into an autoinit transfer
+ else {
+ //Transfer full cycle again
+ sb.dma.left = sb.dma.total;
+ }
+
+ sb.dma.autoinit = autoinit;
+ sb.dma.mode = mode;
+ sb.dma.stereo = stereo;
+ //Double the reading speed for stereo mode
+ if (sb.dma.stereo)
+ sb.dma.mul*=2;
+ sb.dma.rate=(sb.freq*sb.dma.mul) >> SB_SH;
+ sb.dma.min=(sb.dma.rate*3)/1000;
+ sb.chan->SetFreq(freq);
+
+ PIC_RemoveEvents(END_DMA_Event);
+ //Set to be masked, the dma call can change this again.
+ sb.mode = MODE_DMA_MASKED;
+ sb.dma.chan->Register_Callback(DSP_DMA_CallBack);
+}
+
+static void DSP_PrepareDMA_Old(DMA_MODES mode,bool autoinit,bool sign) {
+ sb.dma.sign=sign;
+ if (!autoinit) sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8);
+ sb.dma.chan=GetDMAChannel(sb.hw.dma8);
+ DSP_DoDMATransfer(mode,sb.freq / (sb.mixer.stereo ? 2 : 1), autoinit, sb.mixer.stereo);
+}
+
+static void DSP_PrepareDMA_New(DMA_MODES mode,Bitu length,bool autoinit,bool stereo) {
+ Bitu freq=sb.freq;
+ //equal length if data format and dma channel are both 16-bit or 8-bit
+ sb.dma.total=length;
+ if (mode==DSP_DMA_16) {
+ if (sb.hw.dma16!=0xff) {
+ sb.dma.chan=GetDMAChannel(sb.hw.dma16);
+ if (sb.dma.chan==NULL) {
+ sb.dma.chan=GetDMAChannel(sb.hw.dma8);
+ mode=DSP_DMA_16_ALIASED;
+ sb.dma.total<<=1;
+ }
+ } else {
+ sb.dma.chan=GetDMAChannel(sb.hw.dma8);
+ mode=DSP_DMA_16_ALIASED;
+ //UNDOCUMENTED:
+ //In aliased mode sample length is written to DSP as number of
+ //16-bit samples so we need double 8-bit DMA buffer length
+ sb.dma.total<<=1;
+ }
+ } else sb.dma.chan=GetDMAChannel(sb.hw.dma8);
+ DSP_DoDMATransfer(mode,freq,autoinit,stereo);
+}
+
+
+static void DSP_AddData(Bit8u val) {
+ if (sb.dsp.out.used<DSP_BUFSIZE) {
+ Bitu start=sb.dsp.out.used+sb.dsp.out.pos;
+ if (start>=DSP_BUFSIZE) start-=DSP_BUFSIZE;
+ sb.dsp.out.data[start]=val;
+ sb.dsp.out.used++;
+ } else {
+ LOG(LOG_SB,LOG_ERROR)("DSP:Data Output buffer full");
+ }
+}
+
+
+static void DSP_FinishReset(Bitu /*val*/) {
+ DSP_FlushData();
+ DSP_AddData(0xaa);
+ sb.dsp.state=DSP_S_NORMAL;
+}
+
+static void DSP_Reset(void) {
+ LOG(LOG_SB,LOG_ERROR)("DSP:Reset");
+ PIC_DeActivateIRQ(sb.hw.irq);
+
+ DSP_ChangeMode(MODE_NONE);
+ DSP_FlushData();
+ sb.dsp.cmd=DSP_NO_COMMAND;
+ sb.dsp.cmd_len=0;
+ sb.dsp.in.pos=0;
+ sb.dsp.write_busy=0;
+ PIC_RemoveEvents(DSP_FinishReset);
+
+ sb.dma.left=0;
+ sb.dma.total=0;
+ sb.dma.stereo=false;
+ sb.dma.sign=false;
+ sb.dma.autoinit=false;
+ sb.dma.mode=DSP_DMA_NONE;
+ sb.dma.remain_size=0;
+ if (sb.dma.chan) sb.dma.chan->Clear_Request();
+
+ sb.freq=22050;
+ sb.time_constant=45;
+ sb.dac.used=0;
+ sb.dac.last=0;
+ sb.e2.value=0xaa;
+ sb.e2.count=0;
+ sb.irq.pending_8bit=false;
+ sb.irq.pending_16bit=false;
+ sb.chan->SetFreq(22050);
+// DSP_SetSpeaker(false);
+ PIC_RemoveEvents(END_DMA_Event);
+}
+
+static void DSP_DoReset(Bit8u val) {
+ if (((val&1)!=0) && (sb.dsp.state!=DSP_S_RESET)) {
+//TODO Get out of highspeed mode
+ DSP_Reset();
+ sb.dsp.state=DSP_S_RESET;
+ } else if (((val&1)==0) && (sb.dsp.state==DSP_S_RESET)) { // reset off
+ sb.dsp.state=DSP_S_RESET_WAIT;
+ PIC_RemoveEvents(DSP_FinishReset);
+ PIC_AddEvent(DSP_FinishReset,20.0f/1000.0f,0); // 20 microseconds
+ }
+}
+
+static void DSP_E2_DMA_CallBack(DmaChannel * /*chan*/, DMAEvent event) {
+ if (event==DMA_UNMASKED) {
+ Bit8u val=(Bit8u)(sb.e2.value&0xff);
+ DmaChannel * chan=GetDMAChannel(sb.hw.dma8);
+ chan->Register_Callback(0);
+ chan->Write(1,&val);
+ }
+}
+
+static void DSP_ADC_CallBack(DmaChannel * /*chan*/, DMAEvent event) {
+ if (event!=DMA_UNMASKED) return;
+ Bit8u val=128;
+ DmaChannel * ch=GetDMAChannel(sb.hw.dma8);
+ while (sb.dma.left--) {
+ ch->Write(1,&val);
+ }
+ SB_RaiseIRQ(SB_IRQ_8);
+ ch->Register_Callback(0);
+}
+
+static void DSP_ChangeRate(Bitu freq) {
+ if (sb.freq!=freq && sb.dma.mode!=DSP_DMA_NONE) {
+ sb.chan->FillUp();
+ sb.chan->SetFreq(freq / (sb.mixer.stereo ? 2 : 1));
+ sb.dma.rate=(freq*sb.dma.mul) >> SB_SH;
+ sb.dma.min=(sb.dma.rate*3)/1000;
+ }
+ sb.freq=freq;
+}
+
+Bitu DEBUG_EnableDebugger(void);
+
+#define DSP_SB16_ONLY if (sb.type != SBT_16) { LOG(LOG_SB,LOG_ERROR)("DSP:Command %2X requires SB16",sb.dsp.cmd); break; }
+#define DSP_SB2_ABOVE if (sb.type <= SBT_1) { LOG(LOG_SB,LOG_ERROR)("DSP:Command %2X requires SB2 or above",sb.dsp.cmd); break; }
+
+static void DSP_DoCommand(void) {
+// LOG_MSG("DSP Command %X",sb.dsp.cmd);
+ switch (sb.dsp.cmd) {
+ case 0x04:
+ if (sb.type == SBT_16) {
+ /* SB16 ASP set mode register */
+ if ((sb.dsp.in.data[0]&0xf1)==0xf1) ASP_init_in_progress=true;
+ else ASP_init_in_progress=false;
+ LOG(LOG_SB,LOG_NORMAL)("DSP Unhandled SB16ASP command %X (set mode register to %X)",sb.dsp.cmd,sb.dsp.in.data[0]);
+ } else {
+ /* DSP Status SB 2.0/pro version. NOT SB16. */
+ DSP_FlushData();
+ if (sb.type == SBT_2) DSP_AddData(0x88);
+ else if ((sb.type == SBT_PRO1) || (sb.type == SBT_PRO2)) DSP_AddData(0x7b);
+ else DSP_AddData(0xff); //Everything enabled
+ }
+ break;
+ case 0x05: /* SB16 ASP set codec parameter */
+ LOG(LOG_SB,LOG_NORMAL)("DSP Unhandled SB16ASP command %X (set codec parameter)",sb.dsp.cmd);
+ break;
+ case 0x08: /* SB16 ASP get version */
+ LOG(LOG_SB,LOG_NORMAL)("DSP Unhandled SB16ASP command %X sub %X",sb.dsp.cmd,sb.dsp.in.data[0]);
+ if (sb.type == SBT_16) {
+ switch (sb.dsp.in.data[0]) {
+ case 0x03:
+ DSP_AddData(0x18); // version ID (??)
+ break;
+ default:
+ LOG(LOG_SB,LOG_NORMAL)("DSP Unhandled SB16ASP command %X sub %X",sb.dsp.cmd,sb.dsp.in.data[0]);
+ break;
+ }
+ } else {
+ LOG(LOG_SB,LOG_NORMAL)("DSP Unhandled SB16ASP command %X sub %X",sb.dsp.cmd,sb.dsp.in.data[0]);
+ }
+ break;
+ case 0x0e: /* SB16 ASP set register */
+ if (sb.type == SBT_16) {
+// LOG(LOG_SB,LOG_NORMAL)("SB16 ASP set register %X := %X",sb.dsp.in.data[0],sb.dsp.in.data[1]);
+ ASP_regs[sb.dsp.in.data[0]] = sb.dsp.in.data[1];
+ } else {
+ LOG(LOG_SB,LOG_NORMAL)("DSP Unhandled SB16ASP command %X (set register)",sb.dsp.cmd);
+ }
+ break;
+ case 0x0f: /* SB16 ASP get register */
+ if (sb.type == SBT_16) {
+ if ((ASP_init_in_progress) && (sb.dsp.in.data[0]==0x83)) {
+ ASP_regs[0x83] = ~ASP_regs[0x83];
+ }
+// LOG(LOG_SB,LOG_NORMAL)("SB16 ASP get register %X == %X",sb.dsp.in.data[0],ASP_regs[sb.dsp.in.data[0]]);
+ DSP_AddData(ASP_regs[sb.dsp.in.data[0]]);
+ } else {
+ LOG(LOG_SB,LOG_NORMAL)("DSP Unhandled SB16ASP command %X (get register)",sb.dsp.cmd);
+ }
+ break;
+ case 0x10: /* Direct DAC */
+ DSP_ChangeMode(MODE_DAC);
+ if (sb.dac.used<DSP_DACSIZE) {
+ sb.dac.data[sb.dac.used++]=(Bit8s(sb.dsp.in.data[0] ^ 0x80)) << 8;
+ sb.dac.data[sb.dac.used++]=(Bit8s(sb.dsp.in.data[0] ^ 0x80)) << 8;
+ }
+ break;
+ case 0x24: /* Singe Cycle 8-Bit DMA ADC */
+ sb.dma.left=sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8);
+ sb.dma.sign=false;
+ LOG(LOG_SB,LOG_ERROR)("DSP:Faked ADC for %d bytes",sb.dma.total);
+ GetDMAChannel(sb.hw.dma8)->Register_Callback(DSP_ADC_CallBack);
+ break;
+ case 0x14: /* Singe Cycle 8-Bit DMA DAC */
+ case 0x15: /* Wari hack. Waru uses this one instead of 0x14, but some weird stuff going on there anyway */
+ case 0x91: /* Singe Cycle 8-Bit DMA High speed DAC */
+ /* Note: 0x91 is documented only for DSP ver.2.x and 3.x, not 4.x */
+ DSP_PrepareDMA_Old(DSP_DMA_8,false,false);
+ break;
+ case 0x90: /* Auto Init 8-bit DMA High Speed */
+ case 0x1c: /* Auto Init 8-bit DMA */
+ DSP_SB2_ABOVE; /* Note: 0x90 is documented only for DSP ver.2.x and 3.x, not 4.x */
+ DSP_PrepareDMA_Old(DSP_DMA_8,true,false);
+ break;
+ case 0x38: /* Write to SB MIDI Output */
+ if (sb.midi == true) MIDI_RawOutByte(sb.dsp.in.data[0]);
+ break;
+ case 0x40: /* Set Timeconstant */
+ DSP_ChangeRate(1000000 / (256 - sb.dsp.in.data[0]));
+ break;
+ case 0x41: /* Set Output Samplerate */
+ case 0x42: /* Set Input Samplerate */
+ /* Note: 0x42 is handled like 0x41, needed by Fasttracker II */
+ DSP_SB16_ONLY;
+ DSP_ChangeRate((sb.dsp.in.data[0] << 8) | sb.dsp.in.data[1]);
+ break;
+ case 0x48: /* Set DMA Block Size */
+ DSP_SB2_ABOVE;
+ //TODO Maybe check limit for new irq?
+ sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8);
+ break;
+ case 0x75: /* 075h : Single Cycle 4-bit ADPCM Reference */
+ sb.adpcm.haveref=true;
+ case 0x74: /* 074h : Single Cycle 4-bit ADPCM */
+ DSP_PrepareDMA_Old(DSP_DMA_4,false,false);
+ break;
+ case 0x77: /* 077h : Single Cycle 3-bit(2.6bit) ADPCM Reference*/
+ sb.adpcm.haveref=true;
+ case 0x76: /* 074h : Single Cycle 3-bit(2.6bit) ADPCM */
+ DSP_PrepareDMA_Old(DSP_DMA_3,false,false);
+ break;
+ case 0x7d: /* Auto Init 4-bit ADPCM Reference */
+ DSP_SB2_ABOVE;
+ sb.adpcm.haveref=true;
+ DSP_PrepareDMA_Old(DSP_DMA_4,true,false);
+ break;
+ case 0x17: /* 017h : Single Cycle 2-bit ADPCM Reference*/
+ sb.adpcm.haveref=true;
+ case 0x16: /* 074h : Single Cycle 2-bit ADPCM */
+ DSP_PrepareDMA_Old(DSP_DMA_2,false,false);
+ break;
+ case 0x80: /* Silence DAC */
+ PIC_AddEvent(&DSP_RaiseIRQEvent,
+ (1000.0f*(1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8))/sb.freq));
+ break;
+ case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7:
+ case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf:
+ case 0xc0: case 0xc1: case 0xc2: case 0xc3: case 0xc4: case 0xc5: case 0xc6: case 0xc7:
+ case 0xc8: case 0xc9: case 0xca: case 0xcb: case 0xcc: case 0xcd: case 0xce: case 0xcf:
+ DSP_SB16_ONLY;
+ /* Generic 8/16 bit DMA */
+// DSP_SetSpeaker(true); //SB16 always has speaker enabled
+ sb.dma.sign=(sb.dsp.in.data[0] & 0x10) > 0;
+ DSP_PrepareDMA_New((sb.dsp.cmd & 0x10) ? DSP_DMA_16 : DSP_DMA_8,
+ 1+sb.dsp.in.data[1]+(sb.dsp.in.data[2] << 8),
+ (sb.dsp.cmd & 0x4)>0,
+ (sb.dsp.in.data[0] & 0x20) > 0
+ );
+ break;
+ case 0xd5: /* Halt 16-bit DMA */
+ DSP_SB16_ONLY;
+ case 0xd0: /* Halt 8-bit DMA */
+// DSP_ChangeMode(MODE_NONE);
+// Games sometimes already program a new dma before stopping, gives noise
+ if (sb.mode==MODE_NONE) {
+ // possibly different code here that does not switch to MODE_DMA_PAUSE
+ }
+ sb.mode=MODE_DMA_PAUSE;
+ PIC_RemoveEvents(END_DMA_Event);
+ break;
+ case 0xd1: /* Enable Speaker */
+ DSP_SetSpeaker(true);
+ break;
+ case 0xd3: /* Disable Speaker */
+ DSP_SetSpeaker(false);
+ break;
+ case 0xd8: /* Speaker status */
+ DSP_SB2_ABOVE;
+ DSP_FlushData();
+ if (sb.speaker) DSP_AddData(0xff);
+ else DSP_AddData(0x00);
+ break;
+ case 0xd6: /* Continue DMA 16-bit */
+ DSP_SB16_ONLY;
+ case 0xd4: /* Continue DMA 8-bit*/
+ if (sb.mode==MODE_DMA_PAUSE) {
+ sb.mode=MODE_DMA_MASKED;
+ if (sb.dma.chan!=NULL) sb.dma.chan->Register_Callback(DSP_DMA_CallBack);
+ }
+ break;
+ case 0xd9: /* Exit Autoinitialize 16-bit */
+ DSP_SB16_ONLY;
+ case 0xda: /* Exit Autoinitialize 8-bit */
+ DSP_SB2_ABOVE;
+ /* Set mode to single transfer so it ends with current block */
+ sb.dma.autoinit=false; //Should stop itself
+ sb.dma.total = 0; //This will cancel the switch to single cycle mode
+ //Should really have some sb.dma.autoexit variable since we don't support continue autoinit dsp commands
+ break;
+ case 0xe0: /* DSP Identification - SB2.0+ */
+ DSP_FlushData();
+ DSP_AddData(~sb.dsp.in.data[0]);
+ break;
+ case 0xe1: /* Get DSP Version */
+ DSP_FlushData();
+ switch (sb.type) {
+ case SBT_1:
+ DSP_AddData(0x1);DSP_AddData(0x05);break;
+ case SBT_2:
+ DSP_AddData(0x2);DSP_AddData(0x1);break;
+ case SBT_PRO1:
+ DSP_AddData(0x3);DSP_AddData(0x0);break;
+ case SBT_PRO2:
+ DSP_AddData(0x3);DSP_AddData(0x2);break;
+ case SBT_16:
+ DSP_AddData(0x4);DSP_AddData(0x5);break;
+ default:
+ break;
+ }
+ break;
+ case 0xe2: /* Weird DMA identification write routine */
+ {
+ LOG(LOG_SB,LOG_NORMAL)("DSP Function 0xe2");
+ for (Bitu i = 0; i < 8; i++)
+ if ((sb.dsp.in.data[0] >> i) & 0x01) sb.e2.value += E2_incr_table[sb.e2.count % 4][i];
+ sb.e2.value += E2_incr_table[sb.e2.count % 4][8];
+ sb.e2.count++;
+ GetDMAChannel(sb.hw.dma8)->Register_Callback(DSP_E2_DMA_CallBack);
+ }
+ break;
+ case 0xe3: /* DSP Copyright */
+ {
+ DSP_FlushData();
+ for (size_t i=0;i<=strlen(copyright_string);i++) {
+ DSP_AddData(copyright_string[i]);
+ }
+ }
+ break;
+ case 0xe4: /* Write Test Register */
+ sb.dsp.test_register=sb.dsp.in.data[0];
+ break;
+ case 0xe8: /* Read Test Register */
+ DSP_FlushData();
+ DSP_AddData(sb.dsp.test_register);;
+ break;
+ case 0xf2: /* Trigger 8bit IRQ */
+ //Small delay in order to emulate the slowness of the DSP, fixes Llamatron 2012 and Lemmings 3D
+ PIC_AddEvent(&DSP_RaiseIRQEvent,0.01f);
+ break;
+ case 0xf3: /* Trigger 16bit IRQ */
+ DSP_SB16_ONLY;
+ SB_RaiseIRQ(SB_IRQ_16);
+ break;
+ case 0xf8: /* Undocumented, pre-SB16 only */
+ DSP_FlushData();
+ DSP_AddData(0);
+ break;
+ case 0x30: case 0x31:
+ LOG(LOG_SB,LOG_ERROR)("DSP:Unimplemented MIDI I/O command %2X",sb.dsp.cmd);
+ break;
+ case 0x34: case 0x35: case 0x36: case 0x37:
+ DSP_SB2_ABOVE;
+ LOG(LOG_SB,LOG_ERROR)("DSP:Unimplemented MIDI UART command %2X",sb.dsp.cmd);
+ break;
+ case 0x7f: case 0x1f:
+ DSP_SB2_ABOVE;
+ LOG(LOG_SB,LOG_ERROR)("DSP:Unimplemented auto-init DMA ADPCM command %2X",sb.dsp.cmd);
+ break;
+ case 0x20:
+ DSP_AddData(0x7f); // fake silent input for Creative parrot
+ break;
+ case 0x2c:
+ case 0x98: case 0x99: /* Documented only for DSP 2.x and 3.x */
+ case 0xa0: case 0xa8: /* Documented only for DSP 3.x */
+ LOG(LOG_SB,LOG_ERROR)("DSP:Unimplemented input command %2X",sb.dsp.cmd);
+ break;
+ case 0xf9: /* SB16 ASP ??? */
+ if (sb.type == SBT_16) {
+ LOG(LOG_SB,LOG_NORMAL)("SB16 ASP unknown function %x",sb.dsp.in.data[0]);
+ // just feed it what it expects
+ switch (sb.dsp.in.data[0]) {
+ case 0x0b:
+ DSP_AddData(0x00);
+ break;
+ case 0x0e:
+ DSP_AddData(0xff);
+ break;
+ case 0x0f:
+ DSP_AddData(0x07);
+ break;
+ case 0x23:
+ DSP_AddData(0x00);
+ break;
+ case 0x24:
+ DSP_AddData(0x00);
+ break;
+ case 0x2b:
+ DSP_AddData(0x00);
+ break;
+ case 0x2c:
+ DSP_AddData(0x00);
+ break;
+ case 0x2d:
+ DSP_AddData(0x00);
+ break;
+ case 0x37:
+ DSP_AddData(0x38);
+ break;
+ default:
+ DSP_AddData(0x00);
+ break;
+ }
+ } else {
+ LOG(LOG_SB,LOG_NORMAL)("SB16 ASP unknown function %X",sb.dsp.cmd);
+ }
+ break;
+ default:
+ LOG(LOG_SB,LOG_ERROR)("DSP:Unhandled (undocumented) command %2X",sb.dsp.cmd);
+ break;
+ }
+ sb.dsp.cmd=DSP_NO_COMMAND;
+ sb.dsp.cmd_len=0;
+ sb.dsp.in.pos=0;
+}
+
+static void DSP_DoWrite(Bit8u val) {
+ switch (sb.dsp.cmd) {
+ case DSP_NO_COMMAND:
+ sb.dsp.cmd=val;
+ if (sb.type == SBT_16) sb.dsp.cmd_len=DSP_cmd_len_sb16[val];
+ else sb.dsp.cmd_len=DSP_cmd_len_sb[val];
+ sb.dsp.in.pos=0;
+ if (!sb.dsp.cmd_len) DSP_DoCommand();
+ break;
+ default:
+ sb.dsp.in.data[sb.dsp.in.pos]=val;
+ sb.dsp.in.pos++;
+ if (sb.dsp.in.pos>=sb.dsp.cmd_len) DSP_DoCommand();
+ }
+}
+
+static Bit8u DSP_ReadData(void) {
+/* Static so it repeats the last value on succesive reads (JANGLE DEMO) */
+ if (sb.dsp.out.used) {
+ sb.dsp.out.lastval=sb.dsp.out.data[sb.dsp.out.pos];
+ sb.dsp.out.pos++;
+ if (sb.dsp.out.pos>=DSP_BUFSIZE) sb.dsp.out.pos-=DSP_BUFSIZE;
+ sb.dsp.out.used--;
+ }
+ return sb.dsp.out.lastval;
+}
+
+static float calc_vol(Bit8u amount) {
+ Bit8u count = 31 - amount;
+ float db = static_cast<float>(count);
+ if (sb.type == SBT_PRO1 || sb.type == SBT_PRO2) {
+ if (count) {
+ if (count < 16) db -= 1.0f;
+ else if (count > 16) db += 1.0f;
+ if (count == 24) db += 2.0f;
+ if (count > 27) return 0.0f; //turn it off.
+ }
+ } else { //Give the rest, the SB16 scale, as we don't have data.
+ db *= 2.0f;
+ if (count > 20) db -= 1.0f;
+ }
+ return (float) pow (10.0f,-0.05f * db);
+}
+static void CTMIXER_UpdateVolumes(void) {
+ if (!sb.mixer.enabled) return;
+ MixerChannel * chan;
+ float m0 = calc_vol(sb.mixer.master[0]);
+ float m1 = calc_vol(sb.mixer.master[1]);
+ chan = MIXER_FindChannel("SB");
+ if (chan) chan->SetVolume(m0 * calc_vol(sb.mixer.dac[0]), m1 * calc_vol(sb.mixer.dac[1]));
+ chan = MIXER_FindChannel("FM");
+ if (chan) chan->SetVolume(m0 * calc_vol(sb.mixer.fm[0]) , m1 * calc_vol(sb.mixer.fm[1]) );
+ chan = MIXER_FindChannel("CDAUDIO");
+ if (chan) chan->SetVolume(m0 * calc_vol(sb.mixer.cda[0]), m1 * calc_vol(sb.mixer.cda[1]));
+}
+
+static void CTMIXER_Reset(void) {
+ sb.mixer.fm[0]=
+ sb.mixer.fm[1]=
+ sb.mixer.cda[0]=
+ sb.mixer.cda[1]=
+ sb.mixer.dac[0]=
+ sb.mixer.dac[1]=31;
+ sb.mixer.master[0]=
+ sb.mixer.master[1]=31;
+ CTMIXER_UpdateVolumes();
+}
+
+#define SETPROVOL(_WHICH_,_VAL_) \
+ _WHICH_[0]= ((((_VAL_) & 0xf0) >> 3)|(sb.type==SBT_16 ? 1:3)); \
+ _WHICH_[1]= ((((_VAL_) & 0x0f) << 1)|(sb.type==SBT_16 ? 1:3)); \
+
+#define MAKEPROVOL(_WHICH_) \
+ ((((_WHICH_[0] & 0x1e) << 3) | ((_WHICH_[1] & 0x1e) >> 1)) | \
+ ((sb.type==SBT_PRO1 || sb.type==SBT_PRO2) ? 0x11:0))
+
+static void DSP_ChangeStereo(bool stereo) {
+ if (!sb.dma.stereo && stereo) {
+ sb.chan->SetFreq(sb.freq/2);
+ sb.dma.mul*=2;
+ sb.dma.rate=(sb.freq*sb.dma.mul) >> SB_SH;
+ sb.dma.min=(sb.dma.rate*3)/1000;
+ } else if (sb.dma.stereo && !stereo) {
+ sb.chan->SetFreq(sb.freq);
+ sb.dma.mul/=2;
+ sb.dma.rate=(sb.freq*sb.dma.mul) >> SB_SH;
+ sb.dma.min=(sb.dma.rate*3)/1000;
+ }
+ sb.dma.stereo=stereo;
+}
+
+static void CTMIXER_Write(Bit8u val) {
+ switch (sb.mixer.index) {
+ case 0x00: /* Reset */
+ CTMIXER_Reset();
+ LOG(LOG_SB,LOG_WARN)("Mixer reset value %x",val);
+ break;
+ case 0x02: /* Master Volume (SB2 Only) */
+ SETPROVOL(sb.mixer.master,(val&0xf)|(val<<4));
+ CTMIXER_UpdateVolumes();
+ break;
+ case 0x04: /* DAC Volume (SBPRO) */
+ SETPROVOL(sb.mixer.dac,val);
+ CTMIXER_UpdateVolumes();
+ break;
+ case 0x06: /* FM output selection, Somewhat obsolete with dual OPL SBpro + FM volume (SB2 Only) */
+ //volume controls both channels
+ SETPROVOL(sb.mixer.fm,(val&0xf)|(val<<4));
+ CTMIXER_UpdateVolumes();
+ if(val&0x60) LOG(LOG_SB,LOG_WARN)("Turned FM one channel off. not implemented %X",val);
+ //TODO Change FM Mode if only 1 fm channel is selected
+ break;
+ case 0x08: /* CDA Volume (SB2 Only) */
+ SETPROVOL(sb.mixer.cda,(val&0xf)|(val<<4));
+ CTMIXER_UpdateVolumes();
+ break;
+ case 0x0a: /* Mic Level (SBPRO) or DAC Volume (SB2): 2-bit, 3-bit on SB16 */
+ if (sb.type==SBT_2) {
+ sb.mixer.dac[0]=sb.mixer.dac[1]=((val & 0x6) << 2)|3;
+ CTMIXER_UpdateVolumes();
+ } else {
+ sb.mixer.mic=((val & 0x7) << 2)|(sb.type==SBT_16?1:3);
+ }
+ break;
+ case 0x0e: /* Output/Stereo Select */
+ sb.mixer.stereo=(val & 0x2) > 0;
+ sb.mixer.filtered=(val & 0x20) > 0;
+ DSP_ChangeStereo(sb.mixer.stereo);
+ LOG(LOG_SB,LOG_WARN)("Mixer set to %s",sb.dma.stereo ? "STEREO" : "MONO");
+ break;
+ case 0x22: /* Master Volume (SBPRO) */
+ SETPROVOL(sb.mixer.master,val);
+ CTMIXER_UpdateVolumes();
+ break;
+ case 0x26: /* FM Volume (SBPRO) */
+ SETPROVOL(sb.mixer.fm,val);
+ CTMIXER_UpdateVolumes();
+ break;
+ case 0x28: /* CD Audio Volume (SBPRO) */
+ SETPROVOL(sb.mixer.cda,val);
+ CTMIXER_UpdateVolumes();
+ break;
+ case 0x2e: /* Line-in Volume (SBPRO) */
+ SETPROVOL(sb.mixer.lin,val);
+ break;
+ //case 0x20: /* Master Volume Left (SBPRO) ? */
+ case 0x30: /* Master Volume Left (SB16) */
+ if (sb.type==SBT_16) {
+ sb.mixer.master[0]=val>>3;
+ CTMIXER_UpdateVolumes();
+ }
+ break;
+ //case 0x21: /* Master Volume Right (SBPRO) ? */
+ case 0x31: /* Master Volume Right (SB16) */
+ if (sb.type==SBT_16) {
+ sb.mixer.master[1]=val>>3;
+ CTMIXER_UpdateVolumes();
+ }
+ break;
+ case 0x32: /* DAC Volume Left (SB16) */
+ if (sb.type==SBT_16) {
+ sb.mixer.dac[0]=val>>3;
+ CTMIXER_UpdateVolumes();
+ }
+ break;
+ case 0x33: /* DAC Volume Right (SB16) */
+ if (sb.type==SBT_16) {
+ sb.mixer.dac[1]=val>>3;
+ CTMIXER_UpdateVolumes();
+ }
+ break;
+ case 0x34: /* FM Volume Left (SB16) */
+ if (sb.type==SBT_16) {
+ sb.mixer.fm[0]=val>>3;
+ CTMIXER_UpdateVolumes();
+ }
+ break;
+ case 0x35: /* FM Volume Right (SB16) */
+ if (sb.type==SBT_16) {
+ sb.mixer.fm[1]=val>>3;
+ CTMIXER_UpdateVolumes();
+ }
+ break;
+ case 0x36: /* CD Volume Left (SB16) */
+ if (sb.type==SBT_16) {
+ sb.mixer.cda[0]=val>>3;
+ CTMIXER_UpdateVolumes();
+ }
+ break;
+ case 0x37: /* CD Volume Right (SB16) */
+ if (sb.type==SBT_16) {
+ sb.mixer.cda[1]=val>>3;
+ CTMIXER_UpdateVolumes();
+ }
+ break;
+ case 0x38: /* Line-in Volume Left (SB16) */
+ if (sb.type==SBT_16) sb.mixer.lin[0]=val>>3;
+ break;
+ case 0x39: /* Line-in Volume Right (SB16) */
+ if (sb.type==SBT_16) sb.mixer.lin[1]=val>>3;
+ break;
+ case 0x3a:
+ if (sb.type==SBT_16) sb.mixer.mic=val>>3;
+ break;
+ case 0x80: /* IRQ Select */
+ sb.hw.irq=0xff;
+ if (val & 0x1) sb.hw.irq=2;
+ else if (val & 0x2) sb.hw.irq=5;
+ else if (val & 0x4) sb.hw.irq=7;
+ else if (val & 0x8) sb.hw.irq=10;
+ break;
+ case 0x81: /* DMA Select */
+ sb.hw.dma8=0xff;
+ sb.hw.dma16=0xff;
+ if (val & 0x1) sb.hw.dma8=0;
+ else if (val & 0x2) sb.hw.dma8=1;
+ else if (val & 0x8) sb.hw.dma8=3;
+ if (val & 0x20) sb.hw.dma16=5;
+ else if (val & 0x40) sb.hw.dma16=6;
+ else if (val & 0x80) sb.hw.dma16=7;
+ LOG(LOG_SB,LOG_NORMAL)("Mixer select dma8:%x dma16:%x",sb.hw.dma8,sb.hw.dma16);
+ break;
+ default:
+
+ if( ((sb.type == SBT_PRO1 || sb.type == SBT_PRO2) && sb.mixer.index==0x0c) || /* Input control on SBPro */
+ (sb.type == SBT_16 && sb.mixer.index >= 0x3b && sb.mixer.index <= 0x47)) /* New SB16 registers */
+ sb.mixer.unhandled[sb.mixer.index] = val;
+ LOG(LOG_SB,LOG_WARN)("MIXER:Write %X to unhandled index %X",val,sb.mixer.index);
+ }
+}
+
+static Bit8u CTMIXER_Read(void) {
+ Bit8u ret;
+// if ( sb.mixer.index< 0x80) LOG_MSG("Read mixer %x",sb.mixer.index);
+ switch (sb.mixer.index) {
+ case 0x00: /* RESET */
+ return 0x00;
+ case 0x02: /* Master Volume (SB2 Only) */
+ return ((sb.mixer.master[1]>>1) & 0xe);
+ case 0x22: /* Master Volume (SBPRO) */
+ return MAKEPROVOL(sb.mixer.master);
+ case 0x04: /* DAC Volume (SBPRO) */
+ return MAKEPROVOL(sb.mixer.dac);
+ case 0x06: /* FM Volume (SB2 Only) + FM output selection */
+ return ((sb.mixer.fm[1]>>1) & 0xe);
+ case 0x08: /* CD Volume (SB2 Only) */
+ return ((sb.mixer.cda[1]>>1) & 0xe);
+ case 0x0a: /* Mic Level (SBPRO) or Voice (SB2 Only) */
+ if (sb.type==SBT_2) return (sb.mixer.dac[0]>>2);
+ else return ((sb.mixer.mic >> 2) & (sb.type==SBT_16 ? 7:6));
+ case 0x0e: /* Output/Stereo Select */
+ return 0x11|(sb.mixer.stereo ? 0x02 : 0x00)|(sb.mixer.filtered ? 0x20 : 0x00);
+ case 0x26: /* FM Volume (SBPRO) */
+ return MAKEPROVOL(sb.mixer.fm);
+ case 0x28: /* CD Audio Volume (SBPRO) */
+ return MAKEPROVOL(sb.mixer.cda);
+ case 0x2e: /* Line-IN Volume (SBPRO) */
+ return MAKEPROVOL(sb.mixer.lin);
+ case 0x30: /* Master Volume Left (SB16) */
+ if (sb.type==SBT_16) return sb.mixer.master[0]<<3;
+ ret=0xa;
+ break;
+ case 0x31: /* Master Volume Right (S16) */
+ if (sb.type==SBT_16) return sb.mixer.master[1]<<3;
+ ret=0xa;
+ break;
+ case 0x32: /* DAC Volume Left (SB16) */
+ if (sb.type==SBT_16) return sb.mixer.dac[0]<<3;
+ ret=0xa;
+ break;
+ case 0x33: /* DAC Volume Right (SB16) */
+ if (sb.type==SBT_16) return sb.mixer.dac[1]<<3;
+ ret=0xa;
+ break;
+ case 0x34: /* FM Volume Left (SB16) */
+ if (sb.type==SBT_16) return sb.mixer.fm[0]<<3;
+ ret=0xa;
+ break;
+ case 0x35: /* FM Volume Right (SB16) */
+ if (sb.type==SBT_16) return sb.mixer.fm[1]<<3;
+ ret=0xa;
+ break;
+ case 0x36: /* CD Volume Left (SB16) */
+ if (sb.type==SBT_16) return sb.mixer.cda[0]<<3;
+ ret=0xa;
+ break;
+ case 0x37: /* CD Volume Right (SB16) */
+ if (sb.type==SBT_16) return sb.mixer.cda[1]<<3;
+ ret=0xa;
+ break;
+ case 0x38: /* Line-in Volume Left (SB16) */
+ if (sb.type==SBT_16) return sb.mixer.lin[0]<<3;
+ ret=0xa;
+ break;
+ case 0x39: /* Line-in Volume Right (SB16) */
+ if (sb.type==SBT_16) return sb.mixer.lin[1]<<3;
+ ret=0xa;
+ break;
+ case 0x3a: /* Mic Volume (SB16) */
+ if (sb.type==SBT_16) return sb.mixer.mic<<3;
+ ret=0xa;
+ break;
+ case 0x80: /* IRQ Select */
+ switch (sb.hw.irq) {
+ case 2: return 0x1;
+ case 5: return 0x2;
+ case 7: return 0x4;
+ case 10: return 0x8;
+ }
+ case 0x81: /* DMA Select */
+ ret=0;
+ switch (sb.hw.dma8) {
+ case 0:ret|=0x1;break;
+ case 1:ret|=0x2;break;
+ case 3:ret|=0x8;break;
+ }
+ switch (sb.hw.dma16) {
+ case 5:ret|=0x20;break;
+ case 6:ret|=0x40;break;
+ case 7:ret|=0x80;break;
+ }
+ return ret;
+ case 0x82: /* IRQ Status */
+ return (sb.irq.pending_8bit ? 0x1 : 0) |
+ (sb.irq.pending_16bit ? 0x2 : 0) |
+ ((sb.type == SBT_16) ? 0x20 : 0);
+ default:
+ if ( ((sb.type == SBT_PRO1 || sb.type == SBT_PRO2) && sb.mixer.index==0x0c) || /* Input control on SBPro */
+ (sb.type == SBT_16 && sb.mixer.index >= 0x3b && sb.mixer.index <= 0x47)) /* New SB16 registers */
+ ret = sb.mixer.unhandled[sb.mixer.index];
+ else
+ ret=0xa;
+ LOG(LOG_SB,LOG_WARN)("MIXER:Read from unhandled index %X",sb.mixer.index);
+ }
+ return ret;
+}
+
+
+static Bitu read_sb(Bitu port,Bitu /*iolen*/) {
+ switch (port-sb.hw.base) {
+ case MIXER_INDEX:
+ return sb.mixer.index;
+ case MIXER_DATA:
+ return CTMIXER_Read();
+ case DSP_READ_DATA:
+ return DSP_ReadData();
+ case DSP_READ_STATUS:
+ //TODO See for high speed dma :)
+ if (sb.irq.pending_8bit) {
+ sb.irq.pending_8bit=false;
+ PIC_DeActivateIRQ(sb.hw.irq);
+ }
+ if (sb.dsp.out.used) return 0xff;
+ else return 0x7f;
+ case DSP_ACK_16BIT:
+ sb.irq.pending_16bit=false;
+ break;
+ case DSP_WRITE_STATUS:
+ switch (sb.dsp.state) {
+ case DSP_S_NORMAL:
+ sb.dsp.write_busy++;
+ if (sb.dsp.write_busy & 8) return 0xff;
+ return 0x7f;
+ case DSP_S_RESET:
+ case DSP_S_RESET_WAIT:
+ return 0xff;
+ }
+ return 0xff;
+ case DSP_RESET:
+ return 0xff;
+ default:
+ LOG(LOG_SB,LOG_NORMAL)("Unhandled read from SB Port %4X",port);
+ break;
+ }
+ return 0xff;
+}
+
+static void write_sb(Bitu port,Bitu val,Bitu /*iolen*/) {
+ Bit8u val8=(Bit8u)(val&0xff);
+ switch (port-sb.hw.base) {
+ case DSP_RESET:
+ DSP_DoReset(val8);
+ break;
+ case DSP_WRITE_DATA:
+ DSP_DoWrite(val8);
+ break;
+ case MIXER_INDEX:
+ sb.mixer.index=val8;
+ break;
+ case MIXER_DATA:
+ CTMIXER_Write(val8);
+ break;
+ default:
+ LOG(LOG_SB,LOG_NORMAL)("Unhandled write to SB Port %4X",port);
+ break;
+ }
+}
+
+static void adlib_gusforward(Bitu /*port*/,Bitu val,Bitu /*iolen*/) {
+ adlib_commandreg=(Bit8u)(val&0xff);
+}
+
+bool SB_Get_Address(Bitu& sbaddr, Bitu& sbirq, Bitu& sbdma) {
+ sbaddr=0;
+ sbirq =0;
+ sbdma =0;
+ if (sb.type == SBT_NONE) return false;
+ else {
+ sbaddr=sb.hw.base;
+ sbirq =sb.hw.irq;
+ sbdma = sb.hw.dma8;
+ return true;
+ }
+}
+
+static void SBLASTER_CallBack(Bitu len) {
+ switch (sb.mode) {
+ case MODE_NONE:
+ case MODE_DMA_PAUSE:
+ case MODE_DMA_MASKED:
+ sb.chan->AddSilence();
+ break;
+ case MODE_DAC:
+// GenerateDACSound(len);
+// break;
+ if (!sb.dac.used) {
+ sb.mode=MODE_NONE;
+ return;
+ }
+ sb.chan->AddStretched(sb.dac.used,sb.dac.data);
+ sb.dac.used=0;
+ break;
+ case MODE_DMA:
+ len*=sb.dma.mul;
+ if (len&SB_SH_MASK) len+=1 << SB_SH;
+ len>>=SB_SH;
+ if (len>sb.dma.left) len=sb.dma.left;
+ GenerateDMASound(len);
+ break;
+ }
+}
+
+class SBLASTER: public Module_base {
+private:
+ /* Data */
+ IO_ReadHandleObject ReadHandler[0x10];
+ IO_WriteHandleObject WriteHandler[0x10];
+ AutoexecObject autoexecline;
+ MixerObject MixerChan;
+ OPL_Mode oplmode;
+
+ /* Support Functions */
+ void Find_Type_And_Opl(Section_prop* config,SB_TYPES& type, OPL_Mode& opl_mode){
+ const char * sbtype=config->Get_string("sbtype");
+ if (!strcasecmp(sbtype,"sb1")) type=SBT_1;
+ else if (!strcasecmp(sbtype,"sb2")) type=SBT_2;
+ else if (!strcasecmp(sbtype,"sbpro1")) type=SBT_PRO1;
+ else if (!strcasecmp(sbtype,"sbpro2")) type=SBT_PRO2;
+ else if (!strcasecmp(sbtype,"sb16")) type=SBT_16;
+ else if (!strcasecmp(sbtype,"gb")) type=SBT_GB;
+ else if (!strcasecmp(sbtype,"none")) type=SBT_NONE;
+ else type=SBT_16;
+
+ if (type==SBT_16) {
+ if ((!IS_EGAVGA_ARCH) || !SecondDMAControllerAvailable()) type=SBT_PRO2;
+ }
+
+ /* OPL/CMS Init */
+ const char * omode=config->Get_string("oplmode");
+ if (!strcasecmp(omode,"none")) opl_mode=OPL_none;
+ else if (!strcasecmp(omode,"cms")) opl_mode=OPL_cms;
+ else if (!strcasecmp(omode,"opl2")) opl_mode=OPL_opl2;
+ else if (!strcasecmp(omode,"dualopl2")) opl_mode=OPL_dualopl2;
+ else if (!strcasecmp(omode,"opl3")) opl_mode=OPL_opl3;
+ else if (!strcasecmp(omode,"opl3gold")) opl_mode=OPL_opl3gold;
+ /* Else assume auto */
+ else {
+ switch (type) {
+ case SBT_NONE:
+ opl_mode=OPL_none;
+ break;
+ case SBT_GB:
+ opl_mode=OPL_cms;
+ break;
+ case SBT_1:
+ case SBT_2:
+ opl_mode=OPL_opl2;
+ break;
+ case SBT_PRO1:
+ opl_mode=OPL_dualopl2;
+ break;
+ case SBT_PRO2:
+ case SBT_16:
+ opl_mode=OPL_opl3;
+ break;
+ }
+ }
+ }
+public:
+ SBLASTER(Section* configuration):Module_base(configuration) {
+ Bitu i;
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+
+ sb.hw.base=section->Get_hex("sbbase");
+ sb.hw.irq=section->Get_int("irq");
+ Bitu dma8bit=section->Get_int("dma");
+ if (dma8bit>0xff) dma8bit=0xff;
+ sb.hw.dma8=(Bit8u)(dma8bit&0xff);
+ Bitu dma16bit=section->Get_int("hdma");
+ if (dma16bit>0xff) dma16bit=0xff;
+ sb.hw.dma16=(Bit8u)(dma16bit&0xff);
+
+ sb.mixer.enabled=section->Get_bool("sbmixer");
+ sb.mixer.stereo=false;
+
+ Find_Type_And_Opl(section,sb.type,oplmode);
+
+ switch (oplmode) {
+ case OPL_none:
+ WriteHandler[0].Install(0x388,adlib_gusforward,IO_MB);
+ break;
+ case OPL_cms:
+ WriteHandler[0].Install(0x388,adlib_gusforward,IO_MB);
+ CMS_Init(section);
+ break;
+ case OPL_opl2:
+ CMS_Init(section);
+ // fall-through
+ case OPL_dualopl2:
+ case OPL_opl3:
+ case OPL_opl3gold:
+ OPL_Init(section,oplmode);
+ break;
+ }
+ if (sb.type==SBT_NONE || sb.type==SBT_GB) return;
+
+ sb.chan=MixerChan.Install(&SBLASTER_CallBack,22050,"SB");
+ sb.dsp.state=DSP_S_NORMAL;
+ sb.dsp.out.lastval=0xaa;
+ sb.dma.chan=NULL;
+
+ for (i=4;i<=0xf;i++) {
+ if (i==8 || i==9) continue;
+ //Disable mixer ports for lower soundblaster
+ if ((sb.type==SBT_1 || sb.type==SBT_2) && (i==4 || i==5)) continue;
+ ReadHandler[i].Install(sb.hw.base+i,read_sb,IO_MB);
+ WriteHandler[i].Install(sb.hw.base+i,write_sb,IO_MB);
+ }
+ for (i=0;i<256;i++) ASP_regs[i] = 0;
+ ASP_regs[5] = 0x01;
+ ASP_regs[9] = 0xf8;
+
+ DSP_Reset();
+ CTMIXER_Reset();
+
+ // The documentation does not specify if SB gets initialized with the speaker enabled
+ // or disabled. Real SBPro2 has it disabled.
+ sb.speaker=false;
+ // On SB16 the speaker flag does not affect actual speaker state.
+ if (sb.type == SBT_16) sb.chan->Enable(true);
+ else sb.chan->Enable(false);
+
+ // Create set blaster line
+ ostringstream temp;
+ temp << "SET BLASTER=A" << setw(3)<< hex << sb.hw.base
+ << " I" << dec << (Bitu)sb.hw.irq << " D" << (Bitu)sb.hw.dma8;
+ if (sb.type==SBT_16) temp << " H" << (Bitu)sb.hw.dma16;
+ temp << " T" << static_cast<unsigned int>(sb.type) << ends;
+
+ autoexecline.Install(temp.str());
+
+ /* Soundblaster midi interface */
+ if (!MIDI_Available()) sb.midi = false;
+ else sb.midi = true;
+ }
+
+ ~SBLASTER() {
+ switch (oplmode) {
+ case OPL_none:
+ break;
+ case OPL_cms:
+ CMS_ShutDown(m_configuration);
+ break;
+ case OPL_opl2:
+ CMS_ShutDown(m_configuration);
+ // fall-through
+ case OPL_dualopl2:
+ case OPL_opl3:
+ case OPL_opl3gold:
+ OPL_ShutDown(m_configuration);
+ break;
+ }
+ if (sb.type==SBT_NONE || sb.type==SBT_GB) return;
+ DSP_Reset(); // Stop everything
+ }
+}; //End of SBLASTER class
+
+
+static SBLASTER* test;
+void SBLASTER_ShutDown(Section* /*sec*/) {
+ delete test;
+}
+
+void SBLASTER_Init(Section* sec) {
+ test = new SBLASTER(sec);
+ sec->AddDestroyFunction(&SBLASTER_ShutDown,true);
+}
diff --git a/src/hardware/serialport/Makefile.am b/src/hardware/serialport/Makefile.am
new file mode 100644
index 000000000..d2e720052
--- /dev/null
+++ b/src/hardware/serialport/Makefile.am
@@ -0,0 +1,9 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libserial.a
+
+libserial_a_SOURCES = directserial.cpp directserial.h \
+ libserial.cpp libserial.h \
+ serialdummy.cpp serialdummy.h serialport.cpp \
+ softmodem.cpp softmodem.h misc_util.cpp misc_util.h \
+ nullmodem.cpp nullmodem.h
diff --git a/src/hardware/serialport/directserial.cpp b/src/hardware/serialport/directserial.cpp
new file mode 100644
index 000000000..c6ef6e49b
--- /dev/null
+++ b/src/hardware/serialport/directserial.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+
+#if C_DIRECTSERIAL
+
+#include "serialport.h"
+#include "directserial.h"
+#include "misc_util.h"
+#include "pic.h"
+
+#include "libserial.h"
+
+/* This is a serial passthrough class. Its amazingly simple to */
+/* write now that the serial ports themselves were abstracted out */
+
+CDirectSerial::CDirectSerial (Bitu id, CommandLine* cmd)
+ :CSerial (id, cmd) {
+ InstallationSuccessful = false;
+ comport = 0;
+
+ rx_retry = 0;
+ rx_retry_max = 0;
+
+ std::string tmpstring;
+ if(!cmd->FindStringBegin("realport:",tmpstring,false)) return;
+
+ LOG_MSG ("Serial%d: Opening %s", COMNUMBER, tmpstring.c_str());
+ if(!SERIAL_open(tmpstring.c_str(), &comport)) {
+ char errorbuffer[256];
+ SERIAL_getErrorString(errorbuffer, sizeof(errorbuffer));
+ LOG_MSG("Serial%d: Serial Port \"%s\" could not be opened.",
+ COMNUMBER, tmpstring.c_str());
+ LOG_MSG("%s",errorbuffer);
+ return;
+ }
+
+#if SERIAL_DEBUG
+ dbgmsg_poll_block=false;
+ dbgmsg_rx_block=false;
+#endif
+
+ // rxdelay: How many milliseconds to wait before causing an
+ // overflow when the application is unresponsive.
+ if(getBituSubstring("rxdelay:", &rx_retry_max, cmd)) {
+ if(!(rx_retry_max<=10000)) {
+ rx_retry_max=0;
+ }
+ }
+
+ CSerial::Init_Registers();
+ InstallationSuccessful = true;
+ rx_state = D_RX_IDLE;
+ setEvent(SERIAL_POLLING_EVENT, 1); // millisecond receive tick
+}
+
+CDirectSerial::~CDirectSerial () {
+ if(comport) SERIAL_close(comport);
+ // We do not use own events so we don't have to clear them.
+}
+
+// CanReceive: true:UART part has room left
+// doReceive: true:there was really a byte to receive
+// rx_retry is incremented in polling events
+
+// in POLLING_EVENT: always add new polling event
+// D_RX_IDLE + CanReceive + doReceive -> D_RX_WAIT , add RX_EVENT
+// D_RX_IDLE + CanReceive + not doReceive -> D_RX_IDLE
+// D_RX_IDLE + not CanReceive -> D_RX_BLOCKED, add RX_EVENT
+
+// D_RX_BLOCKED + CanReceive + doReceive -> D_RX_FASTWAIT, rem RX_EVENT
+// rx_retry=0 , add RX_EVENT
+// D_RX_BLOCKED + CanReceive + !doReceive -> D_RX_IDLE, rem RX_EVENT
+// rx_retry=0
+// D_RX_BLOCKED + !CanReceive + doReceive + retry < max -> D_RX_BLOCKED, rx_retry++
+// D_RX_BLOCKED + !CanReceive + doReceive + retry >=max -> rx_retry=0
+
+// to be continued...
+
+void CDirectSerial::handleUpperEvent(Bit16u type) {
+ switch(type) {
+ case SERIAL_POLLING_EVENT: {
+ setEvent(SERIAL_POLLING_EVENT, 1.0f);
+ // update Modem input line states
+ switch(rx_state) {
+ case D_RX_IDLE:
+ if(CanReceiveByte()) {
+ if(doReceive()) {
+ // a byte was received
+ rx_state=D_RX_WAIT;
+ setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
+ } // else still idle
+ } else {
+#if SERIAL_DEBUG
+ if(!dbgmsg_poll_block) {
+ log_ser(dbg_aux,"Directserial: block on polling.");
+ dbgmsg_poll_block=true;
+ }
+#endif
+ rx_state=D_RX_BLOCKED;
+ // have both delays (1ms + bytetime)
+ setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
+ }
+ break;
+ case D_RX_BLOCKED:
+ // one timeout tick
+ if(!CanReceiveByte()) {
+ rx_retry++;
+ if(rx_retry>=rx_retry_max) {
+ // it has timed out:
+ rx_retry=0;
+ removeEvent(SERIAL_RX_EVENT);
+ if(doReceive()) {
+ // read away everything
+ // this will set overrun errors
+ while(doReceive());
+ rx_state=D_RX_WAIT;
+ setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
+ } else {
+ // much trouble about nothing
+ rx_state=D_RX_IDLE;
+ }
+ } // else wait further
+ } else {
+ // good: we can receive again
+#if SERIAL_DEBUG
+ dbgmsg_poll_block=false;
+ dbgmsg_rx_block=false;
+#endif
+ removeEvent(SERIAL_RX_EVENT);
+ rx_retry=0;
+ if(doReceive()) {
+ rx_state=D_RX_FASTWAIT;
+ setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
+ } else {
+ // much trouble about nothing
+ rx_state=D_RX_IDLE;
+ }
+ }
+ break;
+
+ case D_RX_WAIT:
+ case D_RX_FASTWAIT:
+ break;
+ }
+ updateMSR();
+ break;
+ }
+ case SERIAL_RX_EVENT: {
+ switch(rx_state) {
+ case D_RX_IDLE:
+ LOG_MSG("internal error in directserial");
+ break;
+
+ case D_RX_BLOCKED: // try to receive
+ case D_RX_WAIT:
+ case D_RX_FASTWAIT:
+ if(CanReceiveByte()) {
+ // just works or unblocked
+ rx_retry=0; // not waiting anymore
+ if(doReceive()) {
+ if(rx_state==D_RX_WAIT) setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
+ else {
+ // maybe unblocked
+ rx_state=D_RX_FASTWAIT;
+ setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
+ }
+ } else {
+ // didn't receive anything
+ rx_state=D_RX_IDLE;
+ }
+ } else {
+ // blocking now or still blocked
+#if SERIAL_DEBUG
+ if(rx_state==D_RX_BLOCKED) {
+ if(!dbgmsg_rx_block) {
+ log_ser(dbg_aux,"Directserial: rx still blocked (retry=%d)",rx_retry);
+ dbgmsg_rx_block=true;
+ }
+ }
+
+
+
+
+
+
+ else log_ser(dbg_aux,"Directserial: block on continued rx (retry=%d).",rx_retry);
+#endif
+ setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
+ rx_state=D_RX_BLOCKED;
+ }
+
+ break;
+ }
+ updateMSR();
+ break;
+ }
+ case SERIAL_TX_EVENT: {
+ // Maybe echo cirquit works a bit better this way
+ if(rx_state==D_RX_IDLE && CanReceiveByte()) {
+ if(doReceive()) {
+ // a byte was received
+ rx_state=D_RX_WAIT;
+ setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
+ }
+ }
+ ByteTransmitted();
+ updateMSR();
+ break;
+ }
+ case SERIAL_THR_EVENT: {
+ ByteTransmitting();
+ setEvent(SERIAL_TX_EVENT,bytetime*1.1f);
+ break;
+ }
+ }
+}
+
+bool CDirectSerial::doReceive() {
+ int value = SERIAL_getextchar(comport);
+ if(value) {
+ receiveByteEx((Bit8u)(value&0xff),(Bit8u)((value&0xff00)>>8));
+ return true;
+ }
+ return false;
+}
+
+// updatePortConfig is called when emulated app changes the serial port
+// parameters baudrate, stopbits, number of databits, parity.
+void CDirectSerial::updatePortConfig (Bit16u divider, Bit8u lcr) {
+ Bit8u parity = 0;
+
+ switch ((lcr & 0x38)>>3) {
+ case 0x1: parity='o'; break;
+ case 0x3: parity='e'; break;
+ case 0x5: parity='m'; break;
+ case 0x7: parity='s'; break;
+ default: parity='n'; break;
+ }
+
+ Bit8u bytelength = (lcr & 0x3)+5;
+
+ // baudrate
+ Bitu baudrate;
+ if(divider==0) baudrate=115200;
+ else baudrate = 115200 / divider;
+
+ // stopbits
+ Bit8u stopbits;
+ if (lcr & 0x4) {
+ if (bytelength == 5) stopbits = SERIAL_15STOP;
+ else stopbits = SERIAL_2STOP;
+ } else stopbits = SERIAL_1STOP;
+
+ if(!SERIAL_setCommParameters(comport, baudrate, parity, stopbits, bytelength)) {
+#if SERIAL_DEBUG
+ log_ser(dbg_aux,"Serial port settings not supported by host." );
+#endif
+ LOG_MSG ("Serial%d: Desired serial mode not supported (%d,%d,%c,%d)",
+ COMNUMBER, baudrate,bytelength,parity,stopbits);
+ }
+ CDirectSerial::setRTSDTR(getRTS(), getDTR());
+}
+
+void CDirectSerial::updateMSR () {
+ int new_status = SERIAL_getmodemstatus(comport);
+
+ setCTS(new_status&SERIAL_CTS? true:false);
+ setDSR(new_status&SERIAL_DSR? true:false);
+ setRI(new_status&SERIAL_RI? true:false);
+ setCD(new_status&SERIAL_CD? true:false);
+}
+
+void CDirectSerial::transmitByte (Bit8u val, bool first) {
+ if(!SERIAL_sendchar(comport, val))
+ LOG_MSG("Serial%d: COM port error: write failed!", COMNUMBER);
+ if(first) setEvent(SERIAL_THR_EVENT, bytetime/8);
+ else setEvent(SERIAL_TX_EVENT, bytetime);
+}
+
+
+// setBreak(val) switches break on or off
+void CDirectSerial::setBreak (bool value) {
+ SERIAL_setBREAK(comport,value);
+}
+
+// updateModemControlLines(mcr) sets DTR and RTS.
+void CDirectSerial::setRTSDTR(bool rts, bool dtr) {
+ SERIAL_setRTS(comport,rts);
+ SERIAL_setDTR(comport,dtr);
+}
+
+void CDirectSerial::setRTS(bool val) {
+ SERIAL_setRTS(comport,val);
+}
+
+void CDirectSerial::setDTR(bool val) {
+ SERIAL_setDTR(comport,val);
+}
+
+#endif
diff --git a/src/hardware/serialport/directserial.h b/src/hardware/serialport/directserial.h
new file mode 100644
index 000000000..8c252357a
--- /dev/null
+++ b/src/hardware/serialport/directserial.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+// include guard
+#ifndef DOSBOX_DIRECTSERIAL_WIN32_H
+#define DOSBOX_DIRECTSERIAL_WIN32_H
+
+#include "dosbox.h"
+
+#if C_DIRECTSERIAL
+
+#define DIRECTSERIAL_AVAILIBLE
+#include "serialport.h"
+
+#include "libserial.h"
+
+class CDirectSerial : public CSerial {
+public:
+ CDirectSerial(Bitu id, CommandLine* cmd);
+ ~CDirectSerial();
+
+ void updatePortConfig(Bit16u divider, Bit8u lcr);
+ void updateMSR();
+ void transmitByte(Bit8u val, bool first);
+ void setBreak(bool value);
+
+ void setRTSDTR(bool rts, bool dtr);
+ void setRTS(bool val);
+ void setDTR(bool val);
+ void handleUpperEvent(Bit16u type);
+
+private:
+ COMPORT comport;
+
+ Bitu rx_state;
+#define D_RX_IDLE 0
+#define D_RX_WAIT 1
+#define D_RX_BLOCKED 2
+#define D_RX_FASTWAIT 3
+
+ Bitu rx_retry; // counter of retries (every millisecond)
+ Bitu rx_retry_max; // how many POLL_EVENTS to wait before causing
+ // an overrun error.
+ bool doReceive();
+
+#if SERIAL_DEBUG
+ bool dbgmsg_poll_block;
+ bool dbgmsg_rx_block;
+#endif
+
+};
+
+#endif // C_DIRECTSERIAL
+#endif // include guard
diff --git a/src/hardware/serialport/libserial.cpp b/src/hardware/serialport/libserial.cpp
new file mode 100644
index 000000000..3b0a4520d
--- /dev/null
+++ b/src/hardware/serialport/libserial.cpp
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "libserial.h"
+
+#include "config.h"
+
+#ifdef WIN32
+
+#include <windows.h>
+#include <stdio.h>
+
+struct _COMPORT {
+ HANDLE porthandle;
+ bool breakstatus;
+ DCB orig_dcb;
+};
+
+bool SERIAL_open(const char* portname, COMPORT* port) {
+ // allocate COMPORT structure
+ COMPORT cp = (_COMPORT*)malloc(sizeof(_COMPORT));
+ if(cp == NULL) return false;
+
+ cp->breakstatus=false;
+
+ // open the port in NT object space (recommended by Microsoft)
+ // allows the user to open COM10+ and custom port names.
+ size_t len = strlen(portname);
+ if(len > 240) {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ free(cp);
+ return false;
+ }
+ char extended_portname[256] = "\\\\.\\";
+ memcpy(extended_portname+4,portname,len+1);
+
+ cp->porthandle = CreateFile (extended_portname,
+ GENERIC_READ | GENERIC_WRITE, 0,
+ // must be opened with exclusive-access
+ NULL, // no security attributes
+ OPEN_EXISTING, // must use OPEN_EXISTING
+ 0, // non overlapped I/O
+ NULL // hTemplate must be NULL for comm devices
+ );
+
+ if (cp->porthandle == INVALID_HANDLE_VALUE) goto cleanup_error;
+
+ cp->orig_dcb.DCBlength=sizeof(DCB);
+
+ if(!GetCommState(cp->porthandle, &cp->orig_dcb)) {
+ goto cleanup_error;
+ }
+
+ // configure the port for polling
+ DCB newdcb;
+ memcpy(&newdcb,&cp->orig_dcb,sizeof(DCB));
+
+ newdcb.fBinary=true;
+ newdcb.fParity=true;
+ newdcb.fOutxCtsFlow=false;
+ newdcb.fOutxDsrFlow=false;
+ newdcb.fDtrControl=DTR_CONTROL_DISABLE;
+ newdcb.fDsrSensitivity=false;
+
+ newdcb.fOutX=false;
+ newdcb.fInX=false;
+ newdcb.fErrorChar=0;
+ newdcb.fNull=false;
+ newdcb.fRtsControl=RTS_CONTROL_DISABLE;
+ newdcb.fAbortOnError=false;
+
+ if(!SetCommState(cp->porthandle, &newdcb)) {
+ goto cleanup_error;
+ }
+
+ // Configure timeouts to effectively use polling
+ COMMTIMEOUTS ct;
+ ct.ReadIntervalTimeout = MAXDWORD;
+ ct.ReadTotalTimeoutConstant = 0;
+ ct.ReadTotalTimeoutMultiplier = 0;
+ ct.WriteTotalTimeoutConstant = 0;
+ ct.WriteTotalTimeoutMultiplier = 0;
+ if(!SetCommTimeouts(cp->porthandle, &ct)) {
+ goto cleanup_error;
+ }
+ if(!ClearCommBreak(cp->porthandle)) {
+ // Bluetooth Bluesoleil seems to not implement it
+ //goto cleanup_error;
+ }
+ DWORD errors;
+ if(!ClearCommError(cp->porthandle, &errors, NULL)) {
+ goto cleanup_error;
+ }
+ *port = cp;
+ return true;
+
+cleanup_error:
+ if (cp->porthandle != INVALID_HANDLE_VALUE) CloseHandle(cp->porthandle);
+ free(cp);
+ return false;
+}
+
+void SERIAL_close(COMPORT port) {
+ // restore original DCB, close handle, free the COMPORT struct
+ if (port->porthandle != INVALID_HANDLE_VALUE) {
+ SetCommState(port->porthandle, &port->orig_dcb);
+ CloseHandle(port->porthandle);
+ }
+ free(port);
+}
+
+void SERIAL_getErrorString(char* buffer, size_t length) {
+ int error = GetLastError();
+ if(length < 50) return;
+ memset(buffer,0,length);
+ // get the error message text from the operating system
+ LPVOID sysmessagebuffer;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &sysmessagebuffer,
+ 0,NULL);
+
+ const char* err5text = "The specified port is already in use.\n";
+ const char* err2text = "The specified port does not exist.\n";
+
+ size_t sysmsg_offset = 0;
+
+ if(error == 5) {
+ sysmsg_offset = strlen(err5text);
+ memcpy(buffer,err5text,sysmsg_offset);
+
+ } else if(error == 2) {
+ sysmsg_offset = strlen(err2text);
+ memcpy(buffer,err2text,sysmsg_offset);
+ }
+
+ // Go for length > so there will be bytes left afterwards.
+ // (which are 0 due to memset, thus the buffer is 0 terminated
+ if ( length > (sysmsg_offset + strlen((const char*)sysmessagebuffer)) ) {
+ memcpy(buffer + sysmsg_offset, sysmessagebuffer,
+ strlen((const char*)sysmessagebuffer));
+ }
+
+ LocalFree(sysmessagebuffer);
+}
+
+
+void SERIAL_setDTR(COMPORT port, bool value) {
+ EscapeCommFunction(port->porthandle, value ? SETDTR:CLRDTR);
+}
+
+void SERIAL_setRTS(COMPORT port, bool value) {
+ EscapeCommFunction(port->porthandle, value ? SETRTS:CLRRTS);
+}
+
+void SERIAL_setBREAK(COMPORT port, bool value) {
+ EscapeCommFunction(port->porthandle, value ? SETBREAK:CLRBREAK);
+ port->breakstatus = value;
+}
+
+int SERIAL_getmodemstatus(COMPORT port) {
+ DWORD retval = 0;
+ GetCommModemStatus (port->porthandle, &retval);
+ return (int)retval;
+}
+
+bool SERIAL_sendchar(COMPORT port, char data) {
+ DWORD bytesWritten;
+
+ // mean bug: with break = 1, WriteFile will never return.
+ if(port->breakstatus) return true; // true or false?!
+
+ WriteFile (port->porthandle, &data, 1, &bytesWritten, NULL);
+ if(bytesWritten==1) return true;
+ else return false;
+}
+
+// 0-7 char data, higher=flags
+int SERIAL_getextchar(COMPORT port) {
+ DWORD errors = 0; // errors from API
+ DWORD dwRead = 0; // Number of chars read
+ char chRead;
+
+ int retval = 0;
+ // receive a byte; TODO communicate failure
+ if (ReadFile (port->porthandle, &chRead, 1, &dwRead, NULL)) {
+ if (dwRead) {
+ // check for errors
+ ClearCommError(port->porthandle, &errors, NULL);
+ // mask bits are identical
+ errors &= CE_BREAK|CE_FRAME|CE_RXPARITY|CE_OVERRUN;
+ retval |= (errors<<8);
+ retval |= (chRead & 0xff);
+ retval |= 0x10000;
+ }
+ }
+ return retval;
+}
+
+bool SERIAL_setCommParameters(COMPORT port,
+ int baudrate, char parity, int stopbits, int length) {
+
+ DCB dcb;
+ dcb.DCBlength=sizeof(dcb);
+ GetCommState(port->porthandle,&dcb);
+
+ // parity
+ switch (parity) {
+ case 'n': dcb.Parity = NOPARITY; break;
+ case 'o': dcb.Parity = ODDPARITY; break;
+ case 'e': dcb.Parity = EVENPARITY; break;
+ case 'm': dcb.Parity = MARKPARITY; break;
+ case 's': dcb.Parity = SPACEPARITY; break;
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // stopbits
+ switch(stopbits) {
+ case SERIAL_1STOP: dcb.StopBits = ONESTOPBIT; break;
+ case SERIAL_2STOP: dcb.StopBits = TWOSTOPBITS; break;
+ case SERIAL_15STOP: dcb.StopBits = ONE5STOPBITS; break;
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // byte length
+ if(length > 8 || length < 5) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+ dcb.ByteSize = length;
+ dcb.BaudRate = baudrate;
+
+ if (!SetCommState (port->porthandle, &dcb)) return false;
+ return true;
+}
+#endif
+
+#if defined (LINUX) || defined (MACOSX) || defined (BSD)
+
+#include <string.h> // strlen
+#include <stdlib.h>
+
+#include <termios.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h> // sprinf
+
+struct _COMPORT {
+ int porthandle;
+ bool breakstatus;
+ termios backup;
+};
+
+bool SERIAL_open(const char* portname, COMPORT* port) {
+ int result;
+ // allocate COMPORT structure
+ COMPORT cp = (_COMPORT*)malloc(sizeof(_COMPORT));
+ if(cp == NULL) return false;
+
+ cp->breakstatus=false;
+
+ size_t len = strlen(portname);
+ if(len > 240) {
+ ///////////////////////////////////SetLastError(ERROR_BUFFER_OVERFLOW);
+ return false;
+ }
+ char extended_portname[256] = "/dev/";
+ memcpy(extended_portname+5,portname,len);
+
+ cp->porthandle = open (extended_portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (cp->porthandle < 0) goto cleanup_error;
+
+ result = tcgetattr(cp->porthandle,&cp->backup);
+ if (result == -1) goto cleanup_error;
+
+ // get port settings
+ termios termInfo;
+ memcpy(&termInfo,&cp->backup,sizeof(termios));
+
+ // initialize the port
+ termInfo.c_cflag = CS8 | CREAD | CLOCAL; // noparity, 1 stopbit
+ termInfo.c_iflag = PARMRK | INPCK;
+ termInfo.c_oflag = 0;
+ termInfo.c_lflag = 0;
+ termInfo.c_cc[VMIN] = 0;
+ termInfo.c_cc[VTIME] = 0;
+
+ tcflush (cp->porthandle, TCIFLUSH);
+ tcsetattr (cp->porthandle, TCSANOW, &termInfo);
+
+ *port = cp;
+ return true;
+
+cleanup_error:
+ if (cp->porthandle != 0) close(cp->porthandle);
+ free(cp);
+ return false;
+}
+
+void SERIAL_close(COMPORT port) {
+ // restore original termios, close handle, free the COMPORT struct
+ if (port->porthandle >= 0) {
+ tcsetattr(port->porthandle, TCSANOW, &port->backup);
+ close(port->porthandle);
+ }
+ free(port);
+}
+
+void SERIAL_getErrorString(char* buffer, size_t length) {
+ int error = errno;
+ if(length < 50) return;
+ memset(buffer,0,length);
+ // get the error message text from the operating system
+ // TODO (or not)
+
+ const char* err5text = "The specified port is already in use.\n";
+ const char* err2text = "The specified port does not exist.\n";
+
+ size_t sysmsg_offset = 0;
+
+ if(error == EBUSY) {
+ sysmsg_offset = strlen(err5text);
+ memcpy(buffer,err5text,sysmsg_offset);
+
+ } else if(error == 2) {
+ sysmsg_offset = strlen(err2text);
+ memcpy(buffer,err2text,sysmsg_offset);
+ }
+
+ sprintf(buffer + sysmsg_offset, "System error %d.",error);
+
+}
+
+int SERIAL_getmodemstatus(COMPORT port) {
+ long flags = 0;
+ ioctl (port->porthandle, TIOCMGET, &flags);
+ int retval = 0;
+ if (flags & TIOCM_CTS) retval |= SERIAL_CTS;
+ if (flags & TIOCM_DSR) retval |= SERIAL_DSR;
+ if (flags & TIOCM_RI) retval |= SERIAL_RI;
+ if (flags & TIOCM_CD) retval |= SERIAL_CD;
+ return retval;
+}
+
+bool SERIAL_sendchar(COMPORT port, char data) {
+ if(port->breakstatus) return true; // true or false?!; Does POSIX need this check?
+ int bytesWritten = write(port->porthandle, &data, 1);
+ if(bytesWritten==1) return true;
+ else return false;
+}
+
+int SERIAL_getextchar(COMPORT port) {
+ unsigned char chRead = 0;
+ int dwRead = 0;
+ unsigned char error = 0;
+ int retval = 0;
+
+ dwRead=read(port->porthandle,&chRead,1);
+ if (dwRead==1) {
+ if(chRead==0xff) // error escape
+ {
+ dwRead=read(port->porthandle,&chRead,1);
+ if(chRead==0x00) // an error
+ {
+ dwRead=read(port->porthandle,&chRead,1);
+ if(chRead==0x0) error=SERIAL_BREAK_ERR;
+ else error=SERIAL_FRAMING_ERR;
+ }
+ }
+ retval |= (error<<8);
+ retval |= chRead;
+ retval |= 0x10000;
+ }
+ return retval;
+}
+
+bool SERIAL_setCommParameters(COMPORT port,
+ int baudrate, char parity, int stopbits, int length) {
+
+ termios termInfo;
+ int result = tcgetattr(port->porthandle, &termInfo);
+ if (result==-1) return false;
+ termInfo.c_cflag = CREAD | CLOCAL;
+
+ // parity
+ // "works on many systems"
+ #define CMSPAR 010000000000
+ switch (parity) {
+ case 'n': break;
+ case 'o': termInfo.c_cflag |= (PARODD | PARENB); break;
+ case 'e': termInfo.c_cflag |= PARENB; break;
+ case 'm': termInfo.c_cflag |= (PARENB | CMSPAR | PARODD); break;
+ case 's': termInfo.c_cflag |= (PARENB | CMSPAR); break;
+ default:
+ return false;
+ }
+ // stopbits
+ switch(stopbits) {
+ case SERIAL_1STOP: break;
+ case SERIAL_2STOP:
+ case SERIAL_15STOP: termInfo.c_cflag |= CSTOPB; break;
+ default:
+ return false;
+ }
+ // byte length
+ if(length > 8 || length < 5) return false;
+ switch (length) {
+ case 5: termInfo.c_cflag |= CS5; break;
+ case 6: termInfo.c_cflag |= CS6; break;
+ case 7: termInfo.c_cflag |= CS7; break;
+ case 8: termInfo.c_cflag |= CS8; break;
+ }
+ // baudrate
+ int posix_baudrate=0;
+ switch(baudrate) {
+ case 115200: posix_baudrate = B115200; break;
+ case 57600: posix_baudrate = B57600; break;
+ case 38400: posix_baudrate = B38400; break;
+ case 19200: posix_baudrate = B19200; break;
+ case 9600: posix_baudrate = B9600; break;
+ case 4800: posix_baudrate = B4800; break;
+ case 2400: posix_baudrate = B2400; break;
+ case 1200: posix_baudrate = B1200; break;
+ case 600: posix_baudrate = B600; break;
+ case 300: posix_baudrate = B300; break;
+ case 110: posix_baudrate = B110; break;
+ default: return false;
+ }
+ cfsetospeed (&termInfo, posix_baudrate);
+ cfsetispeed (&termInfo, posix_baudrate);
+
+ int retval = tcsetattr(port->porthandle, TCSANOW, &termInfo);
+ if(retval==-1) return false;
+ return true;
+}
+
+void SERIAL_setBREAK(COMPORT port, bool value) {
+ ioctl(port->porthandle, value?TIOCSBRK:TIOCCBRK);
+}
+
+void SERIAL_setDTR(COMPORT port, bool value) {
+ long flag = TIOCM_DTR;
+ ioctl(port->porthandle, value?TIOCMBIS:TIOCMBIC, &flag);
+}
+
+void SERIAL_setRTS(COMPORT port, bool value) {
+ long flag = TIOCM_RTS;
+ ioctl(port->porthandle, value?TIOCMBIS:TIOCMBIC, &flag);
+}
+#endif
+
+#ifdef OS2
+// OS/2 related headers
+#define INCL_DOSFILEMGR
+#define INCL_DOSERRORS
+#define INCL_DOSDEVICES
+#define INCL_DOSDEVIOCTL
+#define INCL_DOSPROCESS
+#include <os2.h>
+#include <malloc.h>
+#include <string.h>
+#include <stdio.h>
+
+struct _COMPORT {
+ HFILE porthandle;
+ DCBINFO orig_dcb;
+};
+// TODO: THIS IS INCOMPLETE and UNTESTED.
+
+bool SERIAL_open(const char* portname, COMPORT* port) {
+ // allocate COMPORT structure
+ COMPORT cp = (_COMPORT*)malloc(sizeof(_COMPORT));
+ if(cp == NULL) return false;
+ cp->porthandle=0;
+
+ USHORT errors = 0;
+ ULONG ulAction = 0;
+ ULONG ulParmLen = sizeof(DCBINFO);
+ APIRET rc = DosOpen((PSZ)portname, &cp->porthandle,
+ &ulAction, 0L, FILE_NORMAL, FILE_OPEN,
+ OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE | OPEN_FLAGS_SEQUENTIAL, 0L);
+ if (rc != NO_ERROR) {
+ goto cleanup_error;
+ }
+
+ rc = DosDevIOCtl(cp->porthandle, IOCTL_ASYNC, ASYNC_GETDCBINFO,
+ 0, 0, 0, &cp->orig_dcb, ulParmLen, &ulParmLen);
+ if ( rc != NO_ERROR) {
+ goto cleanup_error;
+ }
+ // configure the port for polling
+ DCBINFO newdcb;
+ memcpy(&newdcb,&cp->orig_dcb,sizeof(DCBINFO));
+
+ newdcb.usWriteTimeout = 0;
+ newdcb.usReadTimeout = 0; //65535;
+ newdcb.fbCtlHndShake = cp->orig_dcb.fbFlowReplace = 0;
+ newdcb.fbTimeout = 6;
+
+ rc = DosDevIOCtl(cp->porthandle, IOCTL_ASYNC, ASYNC_SETDCBINFO,
+ &newdcb, ulParmLen, &ulParmLen, 0, 0, 0);
+ if ( rc != NO_ERROR) {
+ goto cleanup_error;
+ }
+
+ ulParmLen = sizeof(errors);
+ rc = DosDevIOCtl(cp->porthandle, IOCTL_ASYNC, ASYNC_GETCOMMERROR,
+ 0, 0, 0, &errors, ulParmLen, &ulParmLen);
+ if ( rc != NO_ERROR) {
+ goto cleanup_error;
+ }
+
+ *port = cp;
+ return true;
+
+cleanup_error:
+ // TODO error string - rc value
+ if (cp->porthandle != 0) DosClose(cp->porthandle);
+ free(cp);
+ return false;
+}
+
+void SERIAL_getErrorString(char* buffer, size_t length) {
+ sprintf(buffer, "TODO: error handling is not fun");
+}
+void SERIAL_close(COMPORT port) {
+ ULONG ulParmLen = sizeof(DCBINFO);
+ // restore original DCB, close handle, free the COMPORT struct
+ if (port->porthandle != 0) {
+ DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETDCBINFO,
+ &port->orig_dcb, ulParmLen, &ulParmLen, 0, 0, 0);
+ DosClose (port->porthandle);
+ }
+ free(port);
+}
+bool SERIAL_sendchar(COMPORT port, char data) {
+ ULONG bytesWritten = 0;
+
+ APIRET rc = DosWrite(port->porthandle, &data, 1, &bytesWritten);
+ if (rc == NO_ERROR && bytesWritten > 0) return true;
+ else return false;
+}
+
+void SERIAL_setBREAK(COMPORT port, bool value) {
+ USHORT error;
+ ULONG ulParmLen = sizeof(error);
+ DosDevIOCtl(port->porthandle, IOCTL_ASYNC,
+ value ? ASYNC_SETBREAKON : ASYNC_SETBREAKOFF,
+ 0,0,0, &error, ulParmLen, &ulParmLen);
+}
+
+int SERIAL_getextchar(COMPORT port) {
+ ULONG dwRead = 0; // Number of chars read
+ char chRead;
+
+ int retval = 0;
+ // receive a byte; TODO communicate failure
+ if (DosRead(port->porthandle, &chRead, 1, &dwRead) == NO_ERROR) {
+ if (dwRead) {
+ // check for errors; will OS/2 clear the error on reading its data?
+ // if yes then this is in wrong order
+ USHORT errors = 0, event = 0;
+ ULONG ulParmLen = sizeof(errors);
+ DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_GETCOMMEVENT,
+ 0, 0, 0, &event, ulParmLen, &ulParmLen);
+ if (event & (64 + 128) ) { // Break (Bit 6) or Frame or Parity (Bit 7) error
+ Bit8u errreg = 0;
+ if (event & 64) retval |= SERIAL_BREAK_ERR;
+ if (event & 128) {
+ DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_GETCOMMERROR,
+ 0, 0, 0, &errors, ulParmLen, &ulParmLen);
+ if (errors & 8) retval |= SERIAL_FRAMING_ERR;
+ if (errors & 4) retval |= SERIAL_PARITY_ERR;
+ }
+ }
+ retval |= (chRead & 0xff);
+ retval |= 0x10000;
+ }
+ }
+ return retval;
+}
+
+
+int SERIAL_getmodemstatus(COMPORT port) {
+ UCHAR dptr = 0;
+ ULONG ulParmLen = sizeof(dptr);
+ DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_GETMODEMINPUT,
+ 0, 0, 0, &dptr, ulParmLen, &ulParmLen);
+ // bits are the same as return value
+ return (int)dptr;
+}
+void SERIAL_setDTR(COMPORT port, bool value) {
+ UCHAR masks[2];
+ ULONG ulParmLen = sizeof(masks);
+ if(value) {
+ masks[0]=0x01;
+ masks[1]=0xFF;
+ } else {
+ masks[0]=0x00;
+ masks[1]=0xFE;
+ }
+ DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETMODEMCTRL,
+ 0,0,0, &masks, ulParmLen, &ulParmLen);
+}
+
+void SERIAL_setRTS(COMPORT port, bool value) {
+ UCHAR masks[2];
+ ULONG ulParmLen = sizeof(masks);
+ if(value) {
+ masks[0]=0x02;
+ masks[1]=0xFF;
+ } else {
+ masks[0]=0x00;
+ masks[1]=0xFD;
+ }
+ DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETMODEMCTRL,
+ 0,0,0, &masks, ulParmLen, &ulParmLen);
+}
+
+
+
+bool SERIAL_setCommParameters(COMPORT port,
+ int baudrate, char parity, int stopbits, int length) {
+ // baud
+ struct {
+ ULONG baud;
+ BYTE fraction;
+ } setbaud;
+
+ setbaud.baud = baudrate;
+ setbaud.fraction = 0;
+ ULONG ulParmLen = sizeof(setbaud);
+ APIRET rc = DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_EXTSETBAUDRATE,
+ &setbaud, ulParmLen, &ulParmLen, 0, 0, 0);
+ if (rc != NO_ERROR) {
+ return false;
+ }
+
+ struct {
+ UCHAR data;
+ UCHAR parity;
+ UCHAR stop;
+ } paramline;
+
+ // byte length
+ if(length > 8 || length < 5) {
+ // TODO SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+ paramline.data = length;
+
+ // parity
+ switch (parity) {
+ case 'n': paramline.parity = 0; break;
+ case 'o': paramline.parity = 1; break;
+ case 'e': paramline.parity = 2; break;
+ case 'm': paramline.parity = 3; break;
+ case 's': paramline.parity = 4; break;
+ default:
+ // TODO SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+ // stopbits
+ switch(stopbits) {
+ case SERIAL_1STOP: paramline.stop = 0; break;
+ case SERIAL_2STOP: paramline.stop = 2; break;
+ case SERIAL_15STOP: paramline.stop = 1; break;
+ default:
+ // TODO SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+ // set it
+ ulParmLen = sizeof(paramline);
+ rc = DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETLINECTRL,
+ &paramline, ulParmLen, &ulParmLen, 0, 0, 0);
+ if ( rc != NO_ERROR)
+ return false;
+ return true;
+}
+#endif
diff --git a/src/hardware/serialport/libserial.h b/src/hardware/serialport/libserial.h
new file mode 100644
index 000000000..e3e5a2961
--- /dev/null
+++ b/src/hardware/serialport/libserial.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include <string.h>
+
+typedef struct _COMPORT *COMPORT;
+
+bool SERIAL_open(const char* portname, COMPORT* port);
+void SERIAL_close(COMPORT port);
+void SERIAL_getErrorString(char* buffer, size_t length);
+
+#define SERIAL_1STOP 1
+#define SERIAL_2STOP 2
+#define SERIAL_15STOP 0
+
+// parity: n, o, e, m, s
+
+bool SERIAL_setCommParameters(COMPORT port,
+ int baudrate, char parity, int stopbits, int length);
+
+void SERIAL_setDTR(COMPORT port, bool value);
+void SERIAL_setRTS(COMPORT port, bool value);
+void SERIAL_setBREAK(COMPORT port, bool value);
+
+#define SERIAL_CTS 0x10
+#define SERIAL_DSR 0x20
+#define SERIAL_RI 0x40
+#define SERIAL_CD 0x80
+
+int SERIAL_getmodemstatus(COMPORT port);
+bool SERIAL_setmodemcontrol(COMPORT port, int flags);
+
+bool SERIAL_sendchar(COMPORT port, char data);
+
+// 0-7 char data, higher=flags
+#define SERIAL_BREAK_ERR 0x10
+#define SERIAL_FRAMING_ERR 0x08
+#define SERIAL_PARITY_ERR 0x04
+#define SERIAL_OVERRUN_ERR 0x02
+
+int SERIAL_getextchar(COMPORT port);
diff --git a/src/hardware/serialport/misc_util.cpp b/src/hardware/serialport/misc_util.cpp
new file mode 100644
index 000000000..b0eed831c
--- /dev/null
+++ b/src/hardware/serialport/misc_util.cpp
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+/* $Id $ */
+
+
+#include "config.h"
+
+#if C_MODEM
+
+/*****************************************************************************/
+// C++ SDLnet wrapper
+
+#include "misc_util.h"
+
+struct _TCPsocketX {
+ int ready;
+#ifdef NATIVESOCKETS
+ SOCKET channel;
+#endif
+ IPaddress remoteAddress;
+ IPaddress localAddress;
+ int sflag;
+};
+
+Bit32u Netwrapper_GetCapabilities()
+{
+ Bit32u retval=0;
+ retval = CAPWORD;
+ return retval;
+}
+
+#ifdef NATIVESOCKETS
+TCPClientSocket::TCPClientSocket(int platformsocket) {
+ sendbuffer=0;
+ nativetcpstruct = new Bit8u[sizeof(struct _TCPsocketX)];
+
+ mysock = (TCPsocket)nativetcpstruct;
+ isopen = false;
+ if(!SDLNetInited) {
+ if(SDLNet_Init()==-1) {
+ LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError());
+ return;
+ }
+ SDLNetInited = true;
+ }
+ // fill the SDL socket manually
+ ((struct _TCPsocketX*)nativetcpstruct)->ready=0;
+ ((struct _TCPsocketX*)nativetcpstruct)->sflag=0;
+ ((struct _TCPsocketX*)nativetcpstruct)->channel=(SOCKET) platformsocket;
+ sockaddr_in sa;
+#ifdef OS2
+ int sz;
+#else
+ socklen_t sz;
+#endif
+ sz=sizeof(sa);
+ if(getpeername(platformsocket, (sockaddr *)(&sa), &sz)==0) {
+ ((struct _TCPsocketX*)nativetcpstruct)->
+ remoteAddress.host=/*ntohl(*/sa.sin_addr.s_addr;//);
+ ((struct _TCPsocketX*)nativetcpstruct)->
+ remoteAddress.port=/*ntohs(*/sa.sin_port;//);
+ }
+ else {
+ mysock=0;
+ return;
+ }
+ sz=sizeof(sa);
+ if(getsockname(platformsocket, (sockaddr *)(&sa), &sz)==0) {
+ ((struct _TCPsocketX*)nativetcpstruct)->
+ localAddress.host=/*ntohl(*/sa.sin_addr.s_addr;//);
+ ((struct _TCPsocketX*)nativetcpstruct)->
+ localAddress.port=/*ntohs(*/sa.sin_port;//);
+ }
+ else {
+ mysock=0;
+ return;
+ }
+ if(mysock!=0) {
+ listensocketset = SDLNet_AllocSocketSet(1);
+ if(!listensocketset) return;
+ SDLNet_TCP_AddSocket(listensocketset, mysock);
+ isopen=true;
+ return;
+ }
+ mysock=0;
+ return;
+}
+#endif // NATIVESOCKETS
+
+TCPClientSocket::TCPClientSocket(TCPsocket source) {
+#ifdef NATIVESOCKETS
+ nativetcpstruct=0;
+#endif
+ sendbuffer=0;
+ isopen = false;
+ if(!SDLNetInited) {
+ if(SDLNet_Init()==-1) {
+ LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError());
+ return;
+ }
+ SDLNetInited = true;
+ }
+
+ mysock=0;
+ listensocketset=0;
+ if(source!=0) {
+ mysock = source;
+ listensocketset = SDLNet_AllocSocketSet(1);
+ if(!listensocketset) return;
+ SDLNet_TCP_AddSocket(listensocketset, source);
+
+ isopen=true;
+ }
+}
+TCPClientSocket::TCPClientSocket(const char* destination, Bit16u port) {
+#ifdef NATIVESOCKETS
+ nativetcpstruct=0;
+#endif
+ sendbuffer=0;
+ isopen = false;
+ if(!SDLNetInited) {
+ if(SDLNet_Init()==-1) {
+ LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError());
+ return;
+ }
+ SDLNetInited = true;
+ }
+ mysock=0;
+ listensocketset=0;
+
+ IPaddress openip;
+ //Ancient versions of SDL_net had this as char*. People still appear to be using this one.
+ if (!SDLNet_ResolveHost(&openip,const_cast<char*>(destination),port)) {
+ listensocketset = SDLNet_AllocSocketSet(1);
+ if(!listensocketset) return;
+ mysock = SDLNet_TCP_Open(&openip);
+ if(!mysock) return;
+ SDLNet_TCP_AddSocket(listensocketset, mysock);
+ isopen=true;
+ }
+}
+
+TCPClientSocket::~TCPClientSocket() {
+
+ if(sendbuffer) delete [] sendbuffer;
+#ifdef NATIVESOCKETS
+ if(nativetcpstruct) delete [] nativetcpstruct;
+ else
+#endif
+ if(mysock) {
+ if(listensocketset) SDLNet_TCP_DelSocket(listensocketset,mysock);
+ SDLNet_TCP_Close(mysock);
+ }
+
+ if(listensocketset) SDLNet_FreeSocketSet(listensocketset);
+}
+bool TCPClientSocket::GetRemoteAddressString(Bit8u* buffer) {
+ IPaddress* remote_ip;
+ Bit8u b1, b2, b3, b4;
+ remote_ip=SDLNet_TCP_GetPeerAddress(mysock);
+ if(!remote_ip) return false;
+ b4=remote_ip->host>>24;
+ b3=(remote_ip->host>>16)&0xff;
+ b2=(remote_ip->host>>8)&0xff;
+ b1=remote_ip->host&0xff;
+ sprintf((char*)buffer,"%u.%u.%u.%u",b1,b2,b3,b4);
+ return true;
+}
+
+bool TCPClientSocket::ReceiveArray(Bit8u* data, Bitu* size) {
+ if(SDLNet_CheckSockets(listensocketset,0))
+ {
+ Bits retval = SDLNet_TCP_Recv(mysock, data, *size);
+ if(retval<1) {
+ isopen=false;
+ *size=0;
+ return false;
+ } else {
+ *size=retval;
+ return true;
+ }
+ }
+ else {
+ *size=0;
+ return true;
+ }
+}
+
+
+Bits TCPClientSocket::GetcharNonBlock() {
+// return:
+// -1: no data
+// -2: socket closed
+// 0..255: data
+ if(SDLNet_CheckSockets(listensocketset,0))
+ {
+ Bitu retval =0;
+ if(SDLNet_TCP_Recv(mysock, &retval, 1)!=1) {
+ isopen=false;
+ return -2;
+ } else return retval;
+ }
+ else return -1;
+}
+bool TCPClientSocket::Putchar(Bit8u data) {
+ if(SDLNet_TCP_Send(mysock, &data, 1)!=1) {
+ isopen=false;
+ return false;
+ }
+ return true;
+}
+
+bool TCPClientSocket::SendArray(Bit8u* data, Bitu bufsize) {
+ if(SDLNet_TCP_Send(mysock, data, bufsize)!=bufsize) {
+ isopen=false;
+ return false;
+ }
+ return true;
+}
+
+bool TCPClientSocket::SendByteBuffered(Bit8u data) {
+
+ if(sendbufferindex==(sendbuffersize-1)) {
+ // buffer is full, get rid of it
+ sendbuffer[sendbufferindex]=data;
+ sendbufferindex=0;
+
+ if(SDLNet_TCP_Send(mysock, sendbuffer, sendbuffersize)!=sendbuffersize) {
+ isopen=false;
+ return false;
+ }
+ } else {
+ sendbuffer[sendbufferindex]=data;
+ sendbufferindex++;
+ }
+ return true;
+}
+/*
+bool TCPClientSocket::SendArrayBuffered(Bit8u* data, Bitu bufsize) {
+
+ Bitu bytes
+ while(
+
+ // first case, buffer already full
+ if(sendbufferindex==(sendbuffersize-1)) {
+ // buffer is full, get rid of it
+ sendbuffer[sendbufferindex]=data;
+ sendbufferindex=0;
+
+ if(SDLNet_TCP_Send(mysock, sendbuffer, sendbuffersize)!=sendbuffersize) {
+ isopen=false;
+ return false;
+ }
+ }
+}
+*/
+void TCPClientSocket::FlushBuffer() {
+ if(sendbufferindex) {
+ if(SDLNet_TCP_Send(mysock, sendbuffer,
+ sendbufferindex)!=sendbufferindex) {
+ isopen=false;
+ return;
+ }
+ sendbufferindex=0;
+ }
+}
+
+void TCPClientSocket::SetSendBufferSize(Bitu bufsize) {
+ if(sendbuffer) delete [] sendbuffer;
+ sendbuffer = new Bit8u[bufsize];
+ sendbuffersize=bufsize;
+ sendbufferindex=0;
+}
+
+
+TCPServerSocket::TCPServerSocket(Bit16u port)
+{
+ isopen = false;
+ mysock = 0;
+ if(!SDLNetInited) {
+ if(SDLNet_Init()==-1) {
+ LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError());
+ return;
+ }
+ SDLNetInited = true;
+ }
+ if (port) {
+ IPaddress listen_ip;
+ SDLNet_ResolveHost(&listen_ip, NULL, port);
+ mysock=SDLNet_TCP_Open(&listen_ip);
+ if(!mysock) return;
+ }
+ else return;
+ isopen = true;
+}
+
+TCPServerSocket::~TCPServerSocket() {
+ if(mysock) SDLNet_TCP_Close(mysock);
+}
+
+TCPClientSocket* TCPServerSocket::Accept() {
+
+ TCPsocket new_tcpsock;
+
+ new_tcpsock=SDLNet_TCP_Accept(mysock);
+ if(!new_tcpsock) {
+ //printf("SDLNet_TCP_Accept: %s\n", SDLNet_GetError());
+ return 0;
+ }
+
+ return new TCPClientSocket(new_tcpsock);
+}
+#endif // #if C_MODEM
diff --git a/src/hardware/serialport/misc_util.h b/src/hardware/serialport/misc_util.h
new file mode 100644
index 000000000..a15c69371
--- /dev/null
+++ b/src/hardware/serialport/misc_util.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef SDLNETWRAPPER_H
+#define SDLNETWRAPPER_H
+
+#ifndef DOSBOX_DOSBOX_H
+#include "dosbox.h"
+#endif
+
+#if C_MODEM
+
+# ifndef DOSBOX_SUPPORT_H
+#include "support.h"
+#endif
+
+// Netwrapper Capabilities
+#define NETWRAPPER_TCP 1
+#define NETWRAPPER_TCP_NATIVESOCKET 2
+
+#if defined WIN32
+ #define NATIVESOCKETS
+ #include <winsock2.h>
+ #include <ws2tcpip.h> //for socklen_t
+ //typedef int socklen_t;
+
+//Tests for BSD/OS2/LINUX
+#elif defined HAVE_STDLIB_H && defined HAVE_SYS_TYPES_H && defined HAVE_SYS_SOCKET_H && defined HAVE_NETINET_IN_H
+ #define NATIVESOCKETS
+ #define SOCKET int
+ #include <stdio.h> //darwin
+ #include <stdlib.h> //darwin
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ //socklen_t should be handled by configure
+#endif
+
+#ifdef NATIVESOCKETS
+ #define CAPWORD (NETWRAPPER_TCP|NETWRAPPER_TCP_NATIVESOCKET)
+#else
+ #define CAPWORD NETWRAPPER_TCP
+#endif
+
+#include "SDL_net.h"
+
+
+
+Bit32u Netwrapper_GetCapabilities();
+
+
+class TCPClientSocket {
+ public:
+ TCPClientSocket(TCPsocket source);
+ TCPClientSocket(const char* destination, Bit16u port);
+#ifdef NATIVESOCKETS
+ Bit8u* nativetcpstruct;
+ TCPClientSocket(int platformsocket);
+#endif
+ ~TCPClientSocket();
+
+ // return:
+ // -1: no data
+ // -2: socket closed
+ // >0: data char
+ Bits GetcharNonBlock();
+
+
+ bool Putchar(Bit8u data);
+ bool SendArray(Bit8u* data, Bitu bufsize);
+ bool ReceiveArray(Bit8u* data, Bitu* size);
+ bool isopen;
+
+ bool GetRemoteAddressString(Bit8u* buffer);
+
+ void FlushBuffer();
+ void SetSendBufferSize(Bitu bufsize);
+
+ // buffered send functions
+ bool SendByteBuffered(Bit8u data);
+ bool SendArrayBuffered(Bit8u* data, Bitu bufsize);
+
+ private:
+ TCPsocket mysock;
+ SDLNet_SocketSet listensocketset;
+
+ // Items for send buffering
+ Bitu sendbuffersize;
+ Bitu sendbufferindex;
+
+ Bit8u* sendbuffer;
+};
+
+class TCPServerSocket {
+ public:
+ bool isopen;
+ TCPsocket mysock;
+ TCPServerSocket(Bit16u port);
+ ~TCPServerSocket();
+ TCPClientSocket* Accept();
+};
+
+
+#endif //C_MODEM
+
+#endif //# SDLNETWRAPPER_H
diff --git a/src/hardware/serialport/nullmodem.cpp b/src/hardware/serialport/nullmodem.cpp
new file mode 100644
index 000000000..95fc232c5
--- /dev/null
+++ b/src/hardware/serialport/nullmodem.cpp
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+
+#if C_MODEM
+
+#include "control.h"
+#include "serialport.h"
+#include "nullmodem.h"
+
+CNullModem::CNullModem(Bitu id, CommandLine* cmd):CSerial (id, cmd) {
+ Bitu temptcpport=23;
+ memset(&telClient, 0, sizeof(telClient));
+ InstallationSuccessful = false;
+ serversocket = 0;
+ clientsocket = 0;
+ serverport = 0;
+ clientport = 0;
+
+ rx_retry = 0;
+ rx_retry_max = 20;
+ rx_state=N_RX_DISC;
+
+ tx_gather = 12;
+
+ dtrrespect=false;
+ tx_block=false;
+ receiveblock=false;
+ transparent=false;
+ telnet=false;
+
+ Bitu bool_temp=0;
+
+ // usedtr: The nullmodem will
+ // 1) when it is client connect to the server not immediately but
+ // as soon as a modem-aware application is started (DTR is switched on).
+ // 2) only receive data when DTR is on.
+ if (getBituSubstring("usedtr:", &bool_temp, cmd)) {
+ if (bool_temp==1) {
+ dtrrespect=true;
+ transparent=true;
+ DTR_delta=false; // connect immediately when DTR is already 1
+ }
+ }
+ // transparent: don't add additional handshake control.
+ if (getBituSubstring("transparent:", &bool_temp, cmd)) {
+ if (bool_temp==1) transparent=true;
+ else transparent=false;
+ }
+ // telnet: interpret telnet commands.
+ if (getBituSubstring("telnet:", &bool_temp, cmd)) {
+ if (bool_temp==1) {
+ transparent=true;
+ telnet=true;
+ }
+ }
+ // rxdelay: How many milliseconds to wait before causing an
+ // overflow when the application is unresponsive.
+ if (getBituSubstring("rxdelay:", &rx_retry_max, cmd)) {
+ if (!(rx_retry_max<=10000)) {
+ rx_retry_max=50;
+ }
+ }
+ // txdelay: How many milliseconds to wait before sending data.
+ // This reduces network overhead quite a lot.
+ if (getBituSubstring("txdelay:", &tx_gather, cmd)) {
+ if (!(tx_gather<=500)) {
+ tx_gather=12;
+ }
+ }
+ // port is for both server and client
+ if (getBituSubstring("port:", &temptcpport, cmd)) {
+ if (!(temptcpport>0&&temptcpport<65536)) {
+ temptcpport=23;
+ }
+ }
+ // socket inheritance (client-alike)
+ if (getBituSubstring("inhsocket:", &bool_temp, cmd)) {
+#ifdef NATIVESOCKETS
+ if (Netwrapper_GetCapabilities()&NETWRAPPER_TCP_NATIVESOCKET) {
+ if (bool_temp==1) {
+ int sock;
+ if (control->cmdline->FindInt("-socket",sock,true)) {
+ dtrrespect=false;
+ transparent=true;
+ LOG_MSG("Inheritance socket handle: %d",sock);
+ if (!ClientConnect(new TCPClientSocket(sock)))
+ return;
+ } else {
+ LOG_MSG("Serial%d: -socket parameter missing.",COMNUMBER);
+ return;
+ }
+ }
+ } else {
+ LOG_MSG("Serial%d: socket inheritance not supported on this platform.",
+ COMNUMBER);
+ return;
+ }
+#else
+ LOG_MSG("Serial%d: socket inheritance not available.", COMNUMBER);
+#endif
+ } else {
+ // normal server/client
+ std::string tmpstring;
+ if (cmd->FindStringBegin("server:",tmpstring,false)) {
+ // we are a client
+ const char* hostnamechar=tmpstring.c_str();
+ size_t hostlen=strlen(hostnamechar)+1;
+ if (hostlen>sizeof(hostnamebuffer)) {
+ hostlen=sizeof(hostnamebuffer);
+ hostnamebuffer[sizeof(hostnamebuffer)-1]=0;
+ }
+ memcpy(hostnamebuffer,hostnamechar,hostlen);
+ clientport=(Bit16u)temptcpport;
+ if (dtrrespect) {
+ // we connect as soon as DTR is switched on
+ setEvent(SERIAL_NULLMODEM_DTR_EVENT, 50);
+ LOG_MSG("Serial%d: Waiting for DTR...",COMNUMBER);
+ } else if (!ClientConnect(
+ new TCPClientSocket((char*)hostnamebuffer,(Bit16u)clientport)))
+ return;
+ } else {
+ // we are a server
+ serverport = (Bit16u)temptcpport;
+ if (!ServerListen()) return;
+ }
+ }
+ CSerial::Init_Registers();
+ InstallationSuccessful = true;
+
+ setCTS(dtrrespect||transparent);
+ setDSR(dtrrespect||transparent);
+ setRI(false);
+ setCD(clientsocket != 0); // CD on if connection established
+}
+
+CNullModem::~CNullModem() {
+ if (serversocket) delete serversocket;
+ if (clientsocket) delete clientsocket;
+ // remove events
+ for(Bit16u i = SERIAL_BASE_EVENT_COUNT+1;
+ i <= SERIAL_NULLMODEM_EVENT_COUNT; i++) {
+ removeEvent(i);
+ }
+}
+
+void CNullModem::WriteChar(Bit8u data) {
+ if (clientsocket)clientsocket->SendByteBuffered(data);
+ if (!tx_block) {
+ //LOG_MSG("setevreduct");
+ setEvent(SERIAL_TX_REDUCTION, (float)tx_gather);
+ tx_block=true;
+ }
+}
+
+Bits CNullModem::readChar() {
+ Bits rxchar = clientsocket->GetcharNonBlock();
+ if (telnet && rxchar>=0) return TelnetEmulation((Bit8u)rxchar);
+ else if (rxchar==0xff && !transparent) {// escape char
+ // get the next char
+ Bits rxchar = clientsocket->GetcharNonBlock();
+ if (rxchar==0xff) return rxchar; // 0xff 0xff -> 0xff was meant
+ rxchar&0x1? setCTS(true) : setCTS(false);
+ rxchar&0x2? setDSR(true) : setDSR(false);
+ if (rxchar&0x4) receiveByteEx(0x0,0x10);
+ return -1; // no "payload" received
+ } else return rxchar;
+}
+
+bool CNullModem::ClientConnect(TCPClientSocket* newsocket) {
+ Bit8u peernamebuf[16];
+ clientsocket = newsocket;
+
+ if (!clientsocket->isopen) {
+ LOG_MSG("Serial%d: Connection failed.",COMNUMBER);
+ delete clientsocket;
+ clientsocket=0;
+ setCD(false);
+ return false;
+ }
+ clientsocket->SetSendBufferSize(256);
+ clientsocket->GetRemoteAddressString(peernamebuf);
+ // transmit the line status
+ if (!transparent) setRTSDTR(getRTS(), getDTR());
+ rx_state=N_RX_IDLE;
+ LOG_MSG("Serial%d: Connected to %s",COMNUMBER,peernamebuf);
+ setEvent(SERIAL_POLLING_EVENT, 1);
+ setCD(true);
+ return true;
+}
+
+bool CNullModem::ServerListen() {
+ // Start the server listen port.
+ serversocket = new TCPServerSocket(serverport);
+ if (!serversocket->isopen) return false;
+ LOG_MSG("Serial%d: Nullmodem server waiting for connection on port %d...",
+ COMNUMBER,serverport);
+ setEvent(SERIAL_SERVER_POLLING_EVENT, 50);
+ setCD(false);
+ return true;
+}
+
+bool CNullModem::ServerConnect() {
+ // check if a connection is available.
+ clientsocket=serversocket->Accept();
+ if (!clientsocket) return false;
+
+ Bit8u peeripbuf[16];
+ clientsocket->GetRemoteAddressString(peeripbuf);
+ LOG_MSG("Serial%d: A client (%s) has connected.",COMNUMBER,peeripbuf);
+#if SERIAL_DEBUG
+ log_ser(dbg_aux,"Nullmodem: A client (%s) has connected.", peeripbuf);
+#endif
+ clientsocket->SetSendBufferSize(256);
+ rx_state=N_RX_IDLE;
+ setEvent(SERIAL_POLLING_EVENT, 1);
+
+ // we don't accept further connections
+ delete serversocket;
+ serversocket=0;
+
+ // transmit the line status
+ setRTSDTR(getRTS(), getDTR());
+ if (transparent) setCD(true);
+ return true;
+}
+
+void CNullModem::Disconnect() {
+ removeEvent(SERIAL_POLLING_EVENT);
+ removeEvent(SERIAL_RX_EVENT);
+ // it was disconnected; free the socket and restart the server socket
+ LOG_MSG("Serial%d: Disconnected.",COMNUMBER);
+ delete clientsocket;
+ clientsocket=0;
+ setDSR(false);
+ setCTS(false);
+ setCD(false);
+
+ if (serverport) {
+ serversocket = new TCPServerSocket(serverport);
+ if (serversocket->isopen)
+ setEvent(SERIAL_SERVER_POLLING_EVENT, 50);
+ else delete serversocket;
+ } else if (dtrrespect) {
+ setEvent(SERIAL_NULLMODEM_DTR_EVENT,50);
+ DTR_delta = getDTR(); // try to reconnect the next time DTR is set
+ }
+}
+
+void CNullModem::handleUpperEvent(Bit16u type) {
+
+ switch(type) {
+ case SERIAL_POLLING_EVENT: {
+ // periodically check if new data arrived, disconnect
+ // if required. Add it back.
+ setEvent(SERIAL_POLLING_EVENT, 1.0f);
+ // update Modem input line states
+ updateMSR();
+ switch(rx_state) {
+ case N_RX_IDLE:
+ if (CanReceiveByte()) {
+ if (doReceive()) {
+ // a byte was received
+ rx_state=N_RX_WAIT;
+ setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
+ } // else still idle
+ } else {
+#if SERIAL_DEBUG
+ log_ser(dbg_aux,"Nullmodem: block on polling.");
+#endif
+ rx_state=N_RX_BLOCKED;
+ // have both delays (1ms + bytetime)
+ setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
+ }
+ break;
+ case N_RX_BLOCKED:
+ // one timeout tick
+ if (!CanReceiveByte()) {
+ rx_retry++;
+ if (rx_retry>=rx_retry_max) {
+ // it has timed out:
+ rx_retry=0;
+ removeEvent(SERIAL_RX_EVENT);
+ if (doReceive()) {
+ // read away everything
+ while(doReceive());
+ rx_state=N_RX_WAIT;
+ setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
+ } else {
+ // much trouble about nothing
+ rx_state=N_RX_IDLE;
+#if SERIAL_DEBUG
+ log_ser(dbg_aux,"Nullmodem: unblock due to no more data",rx_retry);
+#endif
+ }
+ } // else wait further
+ } else {
+ // good: we can receive again
+ removeEvent(SERIAL_RX_EVENT);
+ rx_retry=0;
+ if (doReceive()) {
+ rx_state=N_RX_FASTWAIT;
+ setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
+ } else {
+ // much trouble about nothing
+ rx_state=N_RX_IDLE;
+ }
+ }
+ break;
+
+ case N_RX_WAIT:
+ case N_RX_FASTWAIT:
+ break;
+ }
+ break;
+ }
+ case SERIAL_RX_EVENT: {
+ switch(rx_state) {
+ case N_RX_IDLE:
+ LOG_MSG("internal error in nullmodem");
+ break;
+
+ case N_RX_BLOCKED: // try to receive
+ case N_RX_WAIT:
+ case N_RX_FASTWAIT:
+ if (CanReceiveByte()) {
+ // just works or unblocked
+ if (doReceive()) {
+ rx_retry=0; // not waiting anymore
+ if (rx_state==N_RX_WAIT) setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
+ else {
+ // maybe unblocked
+ rx_state=N_RX_FASTWAIT;
+ setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
+ }
+ } else {
+ // didn't receive anything
+ rx_retry=0;
+ rx_state=N_RX_IDLE;
+ }
+ } else {
+ // blocking now or still blocked
+#if SERIAL_DEBUG
+ if (rx_state==N_RX_BLOCKED)
+ log_ser(dbg_aux,"Nullmodem: rx still blocked (retry=%d)",rx_retry);
+ else log_ser(dbg_aux,"Nullmodem: block on continued rx (retry=%d).",rx_retry);
+#endif
+ setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
+ rx_state=N_RX_BLOCKED;
+ }
+
+ break;
+ }
+ break;
+ }
+ case SERIAL_TX_EVENT: {
+ // Maybe echo cirquit works a bit better this way
+ if (rx_state==N_RX_IDLE && CanReceiveByte() && clientsocket) {
+ if (doReceive()) {
+ // a byte was received
+ rx_state=N_RX_WAIT;
+ setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
+ }
+ }
+ ByteTransmitted();
+ break;
+ }
+ case SERIAL_THR_EVENT: {
+ ByteTransmitting();
+ // actually send it
+ setEvent(SERIAL_TX_EVENT,bytetime+0.01f);
+ break;
+ }
+ case SERIAL_SERVER_POLLING_EVENT: {
+ // As long as nothing is connected to our server poll the
+ // connection.
+ if (!ServerConnect()) {
+ // continue looking
+ setEvent(SERIAL_SERVER_POLLING_EVENT, 50);
+ }
+ break;
+ }
+ case SERIAL_TX_REDUCTION: {
+ // Flush the data in the transmitting buffer.
+ if (clientsocket) clientsocket->FlushBuffer();
+ tx_block=false;
+ break;
+ }
+ case SERIAL_NULLMODEM_DTR_EVENT: {
+ if ((!DTR_delta) && getDTR()) {
+ // DTR went positive. Try to connect.
+ if (ClientConnect(new TCPClientSocket((char*)hostnamebuffer,
+ (Bit16u)clientport)))
+ break; // no more DTR wait event when connected
+ }
+ DTR_delta = getDTR();
+ setEvent(SERIAL_NULLMODEM_DTR_EVENT,50);
+ break;
+ }
+ }
+}
+
+/*****************************************************************************/
+/* updatePortConfig is called when emulated app changes the serial port **/
+/* parameters baudrate, stopbits, number of databits, parity. **/
+/*****************************************************************************/
+void CNullModem::updatePortConfig (Bit16u /*divider*/, Bit8u /*lcr*/) {
+
+}
+
+void CNullModem::updateMSR () {
+
+}
+
+bool CNullModem::doReceive () {
+ Bits rxchar = readChar();
+ if (rxchar>=0) {
+ receiveByteEx((Bit8u)rxchar,0);
+ return true;
+ }
+ else if (rxchar==-2) {
+ Disconnect();
+ }
+ return false;
+}
+
+void CNullModem::transmitByte (Bit8u val, bool first) {
+ // transmit it later in THR_Event
+ if (first) setEvent(SERIAL_THR_EVENT, bytetime/8);
+ else setEvent(SERIAL_TX_EVENT, bytetime);
+
+ // disable 0xff escaping when transparent mode is enabled
+ if (!transparent && (val==0xff)) WriteChar(0xff);
+
+ WriteChar(val);
+}
+
+Bits CNullModem::TelnetEmulation(Bit8u data) {
+ Bit8u response[3];
+ if (telClient.inIAC) {
+ if (telClient.recCommand) {
+ if ((data != 0) && (data != 1) && (data != 3)) {
+ LOG_MSG("Serial%d: Unrecognized telnet option %d",COMNUMBER, data);
+ if (telClient.command>250) {
+ /* Reject anything we don't recognize */
+ response[0]=0xff;
+ response[1]=252;
+ response[2]=data; /* We won't do crap! */
+ if (clientsocket) clientsocket->SendArray(response, 3);
+ }
+ }
+ switch(telClient.command) {
+ case 251: /* Will */
+ if (data == 0) telClient.binary[TEL_SERVER] = true;
+ if (data == 1) telClient.echo[TEL_SERVER] = true;
+ if (data == 3) telClient.supressGA[TEL_SERVER] = true;
+ break;
+ case 252: /* Won't */
+ if (data == 0) telClient.binary[TEL_SERVER] = false;
+ if (data == 1) telClient.echo[TEL_SERVER] = false;
+ if (data == 3) telClient.supressGA[TEL_SERVER] = false;
+ break;
+ case 253: /* Do */
+ if (data == 0) {
+ telClient.binary[TEL_CLIENT] = true;
+ response[0]=0xff;
+ response[1]=251;
+ response[2]=0; /* Will do binary transfer */
+ if (clientsocket) clientsocket->SendArray(response, 3);
+ }
+ if (data == 1) {
+ telClient.echo[TEL_CLIENT] = false;
+ response[0]=0xff;
+ response[1]=252;
+ response[2]=1; /* Won't echo (too lazy) */
+ if (clientsocket) clientsocket->SendArray(response, 3);
+ }
+ if (data == 3) {
+ telClient.supressGA[TEL_CLIENT] = true;
+ response[0]=0xff;
+ response[1]=251;
+ response[2]=3; /* Will Suppress GA */
+ if (clientsocket) clientsocket->SendArray(response, 3);
+ }
+ break;
+ case 254: /* Don't */
+ if (data == 0) {
+ telClient.binary[TEL_CLIENT] = false;
+ response[0]=0xff;
+ response[1]=252;
+ response[2]=0; /* Won't do binary transfer */
+ if (clientsocket) clientsocket->SendArray(response, 3);
+ }
+ if (data == 1) {
+ telClient.echo[TEL_CLIENT] = false;
+ response[0]=0xff;
+ response[1]=252;
+ response[2]=1; /* Won't echo (fine by me) */
+ if (clientsocket) clientsocket->SendArray(response, 3);
+ }
+ if (data == 3) {
+ telClient.supressGA[TEL_CLIENT] = true;
+ response[0]=0xff;
+ response[1]=251;
+ response[2]=3; /* Will Suppress GA (too lazy) */
+ if (clientsocket) clientsocket->SendArray(response, 3);
+ }
+ break;
+ default:
+ LOG_MSG("MODEM: Telnet client sent IAC %d", telClient.command);
+ break;
+ }
+ telClient.inIAC = false;
+ telClient.recCommand = false;
+ return -1; //continue;
+ } else {
+ if (data==249) {
+ /* Go Ahead received */
+ telClient.inIAC = false;
+ return -1; //continue;
+ }
+ telClient.command = data;
+ telClient.recCommand = true;
+
+ if ((telClient.binary[TEL_SERVER]) && (data == 0xff)) {
+ /* Binary data with value of 255 */
+ telClient.inIAC = false;
+ telClient.recCommand = false;
+ return 0xff;
+ }
+ }
+ } else {
+ if (data == 0xff) {
+ telClient.inIAC = true;
+ return -1;
+ }
+ return data;
+ }
+ return -1; // ???
+}
+
+
+/*****************************************************************************/
+/* setBreak(val) switches break on or off **/
+/*****************************************************************************/
+
+void CNullModem::setBreak (bool /*value*/) {
+ CNullModem::setRTSDTR(getRTS(), getDTR());
+}
+
+/*****************************************************************************/
+/* updateModemControlLines(mcr) sets DTR and RTS. **/
+/*****************************************************************************/
+void CNullModem::setRTSDTR(bool xrts, bool xdtr) {
+ if (!transparent) {
+ Bit8u control[2];
+ control[0]=0xff;
+ control[1]=0x0;
+ if (xrts) control[1]|=1;
+ if (xdtr) control[1]|=2;
+ if (LCR&LCR_BREAK_MASK) control[1]|=4;
+ if (clientsocket) clientsocket->SendArray(control, 2);
+ }
+}
+void CNullModem::setRTS(bool val) {
+ setRTSDTR(val, getDTR());
+}
+void CNullModem::setDTR(bool val) {
+ setRTSDTR(getRTS(), val);
+}
+#endif
diff --git a/src/hardware/serialport/nullmodem.h b/src/hardware/serialport/nullmodem.h
new file mode 100644
index 000000000..15859e52b
--- /dev/null
+++ b/src/hardware/serialport/nullmodem.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+// include guard
+#ifndef DOSBOX_NULLMODEM_WIN32_H
+#define DOSBOX_NULLMODEM_WIN32_H
+
+#include "dosbox.h"
+
+#if C_MODEM
+
+#include "misc_util.h"
+#include "serialport.h"
+
+#define SERIAL_SERVER_POLLING_EVENT SERIAL_BASE_EVENT_COUNT+1
+#define SERIAL_TX_REDUCTION SERIAL_BASE_EVENT_COUNT+2
+#define SERIAL_NULLMODEM_DTR_EVENT SERIAL_BASE_EVENT_COUNT+3
+#define SERIAL_NULLMODEM_EVENT_COUNT SERIAL_BASE_EVENT_COUNT+3
+
+class CNullModem : public CSerial {
+public:
+ CNullModem(Bitu id, CommandLine* cmd);
+ ~CNullModem();
+
+ void updatePortConfig(Bit16u divider, Bit8u lcr);
+ void updateMSR();
+ void transmitByte(Bit8u val, bool first);
+ void setBreak(bool value);
+
+ void setRTSDTR(bool rts, bool dtr);
+ void setRTS(bool val);
+ void setDTR(bool val);
+ void handleUpperEvent(Bit16u type);
+
+private:
+ TCPServerSocket* serversocket;
+ TCPClientSocket* clientsocket;
+
+ bool receiveblock; // It's not a block of data it rather blocks
+ Bit16u serverport; // we are a server if this is nonzero
+ Bit16u clientport;
+
+ Bit8u hostnamebuffer[128]; // the name passed to us by the user
+
+ Bitu rx_state;
+#define N_RX_IDLE 0
+#define N_RX_WAIT 1
+#define N_RX_BLOCKED 2
+#define N_RX_FASTWAIT 3
+#define N_RX_DISC 4
+
+ bool doReceive();
+ bool ClientConnect(TCPClientSocket* newsocket);
+ bool ServerListen();
+ bool ServerConnect();
+ void Disconnect();
+ Bits readChar();
+ void WriteChar(Bit8u data);
+
+ bool DTR_delta; // with dtrrespect, we try to establish a connection
+ // whenever DTR switches to 1. This variable is
+ // used to remember the old state.
+
+ bool tx_block; // true while the SERIAL_TX_REDUCTION event
+ // is pending
+
+ Bitu rx_retry; // counter of retries
+
+ Bitu rx_retry_max; // how many POLL_EVENTS to wait before causing
+ // a overrun error.
+
+ Bitu tx_gather; // how long to gather tx data before
+ // sending all of them [milliseconds]
+
+
+ bool dtrrespect; // dtr behavior - only send data to the serial
+ // port when DTR is on
+
+ bool transparent; // if true, don't send 0xff 0xXX to toggle
+ // DSR/CTS.
+
+ bool telnet; // Do Telnet parsing.
+
+ // Telnet's brain
+#define TEL_CLIENT 0
+#define TEL_SERVER 1
+
+ Bits TelnetEmulation(Bit8u data);
+
+ // Telnet's memory
+ struct {
+ bool binary[2];
+ bool echo[2];
+ bool supressGA[2];
+ bool timingMark[2];
+
+ bool inIAC;
+ bool recCommand;
+ Bit8u command;
+ } telClient;
+};
+
+#endif // C_MODEM
+#endif // include guard
diff --git a/src/hardware/serialport/serialdummy.cpp b/src/hardware/serialport/serialdummy.cpp
new file mode 100644
index 000000000..a3aaf9e06
--- /dev/null
+++ b/src/hardware/serialport/serialdummy.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+
+#include "setup.h"
+#include "serialdummy.h"
+#include "serialport.h"
+
+CSerialDummy::CSerialDummy(Bitu id, CommandLine* cmd):CSerial(id, cmd) {
+ CSerial::Init_Registers();
+ setRI(false);
+ setDSR(false);
+ setCD(false);
+ setCTS(false);
+ InstallationSuccessful=true;
+}
+
+CSerialDummy::~CSerialDummy() {
+ // clear events
+ removeEvent(SERIAL_TX_EVENT);
+}
+
+void CSerialDummy::handleUpperEvent(Bit16u type) {
+ if(type==SERIAL_TX_EVENT) {
+ //LOG_MSG("SERIAL_TX_EVENT");
+#ifdef CHECKIT_TESTPLUG
+ receiveByte(loopbackdata);
+#endif
+ ByteTransmitted(); // tx timeout
+ }
+ else if(type==SERIAL_THR_EVENT){
+ //LOG_MSG("SERIAL_THR_EVENT");
+ ByteTransmitting();
+ setEvent(SERIAL_TX_EVENT,bytetime);
+ }
+
+}
+
+/*****************************************************************************/
+/* updatePortConfig is called when emulated app changes the serial port **/
+/* parameters baudrate, stopbits, number of databits, parity. **/
+/*****************************************************************************/
+void CSerialDummy::updatePortConfig(Bit16u divider, Bit8u lcr) {
+ //LOG_MSG("Serial port at 0x%x: Port params changed: %d Baud", base,dcb.BaudRate);
+}
+
+void CSerialDummy::updateMSR() {
+}
+void CSerialDummy::transmitByte(Bit8u val, bool first) {
+
+ if(first) setEvent(SERIAL_THR_EVENT, bytetime/10);
+ else setEvent(SERIAL_TX_EVENT, bytetime);
+
+#ifdef CHECKIT_TESTPLUG
+ loopbackdata=val;
+#endif
+}
+
+/*****************************************************************************/
+/* setBreak(val) switches break on or off **/
+/*****************************************************************************/
+
+void CSerialDummy::setBreak(bool value) {
+ //LOG_MSG("UART 0x%x: Break toggeled: %d", base, value);
+}
+
+/*****************************************************************************/
+/* setRTSDTR sets the modem control lines **/
+/*****************************************************************************/
+void CSerialDummy::setRTSDTR(bool rts, bool dtr) {
+ setRTS(rts);
+ setDTR(dtr);
+}
+void CSerialDummy::setRTS(bool val) {
+#ifdef CHECKIT_TESTPLUG
+ setCTS(val);
+#endif
+}
+void CSerialDummy::setDTR(bool val) {
+#ifdef CHECKIT_TESTPLUG
+ setDSR(val);
+ setRI(val);
+ setCD(val);
+#endif
+}
diff --git a/src/hardware/serialport/serialdummy.h b/src/hardware/serialport/serialdummy.h
new file mode 100644
index 000000000..9df7c2c4d
--- /dev/null
+++ b/src/hardware/serialport/serialdummy.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef INCLUDEGUARD_SERIALDUMMY_H
+#define INCLUDEGUARD_SERIALDUMMY_H
+
+#include "serialport.h"
+
+//#define CHECKIT_TESTPLUG
+
+class CSerialDummy : public CSerial {
+public:
+ CSerialDummy(Bitu id, CommandLine* cmd);
+ ~CSerialDummy();
+
+ void setRTSDTR(bool rts, bool dtr);
+ void setRTS(bool val);
+ void setDTR(bool val);
+
+ void updatePortConfig(Bit16u, Bit8u lcr);
+ void updateMSR();
+ void transmitByte(Bit8u val, bool first);
+ void setBreak(bool value);
+ void handleUpperEvent(Bit16u type);
+
+#ifdef CHECKIT_TESTPLUG
+ Bit8u loopbackdata;
+#endif
+
+};
+
+#endif // INCLUDEGUARD
diff --git a/src/hardware/serialport/serialport.cpp b/src/hardware/serialport/serialport.cpp
new file mode 100644
index 000000000..7f8627992
--- /dev/null
+++ b/src/hardware/serialport/serialport.cpp
@@ -0,0 +1,1298 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <ctype.h>
+
+#include "dosbox.h"
+
+#include "inout.h"
+#include "pic.h"
+#include "setup.h"
+#include "bios.h" // SetComPorts(..)
+#include "callback.h" // CALLBACK_Idle
+
+#include "serialport.h"
+#include "directserial.h"
+#include "serialdummy.h"
+#include "softmodem.h"
+#include "nullmodem.h"
+
+#include "cpu.h"
+
+#define LOG_SER(x) log_ser
+
+bool device_COM::Read(Bit8u * data,Bit16u * size) {
+ // DTR + RTS on
+ sclass->Write_MCR(0x03);
+ for (Bit16u i=0; i<*size; i++)
+ {
+ Bit8u status;
+ if(!(sclass->Getchar(&data[i],&status,true,1000))) {
+ *size=i;
+ return true;
+ }
+ }
+ return true;
+}
+
+
+bool device_COM::Write(Bit8u * data,Bit16u * size) {
+ // DTR + RTS on
+ sclass->Write_MCR(0x03);
+ for (Bit16u i=0; i<*size; i++)
+ {
+ if(!(sclass->Putchar(data[i],true,true,1000))) {
+ *size=i;
+ sclass->Write_MCR(0x01);
+ return false;
+ }
+ }
+ // RTS off
+ sclass->Write_MCR(0x01);
+ return true;
+}
+
+bool device_COM::Seek(Bit32u * pos,Bit32u type) {
+ *pos = 0;
+ return true;
+}
+
+bool device_COM::Close() {
+ return false;
+}
+
+Bit16u device_COM::GetInformation(void) {
+ return 0x80A0;
+}
+
+device_COM::device_COM(class CSerial* sc) {
+ sclass = sc;
+ SetName(serial_comname[sclass->idnumber]);
+}
+
+device_COM::~device_COM() {
+}
+
+
+
+// COM1 - COM4 objects
+CSerial* serialports[4] ={0,0,0,0};
+
+static Bitu SERIAL_Read (Bitu port, Bitu iolen) {
+ Bitu i;
+ Bitu retval;
+ Bitu index = port & 0x7;
+ switch(port&0xff8) {
+ case 0x3f8: i=0; break;
+ case 0x2f8: i=1; break;
+ case 0x3e8: i=2; break;
+ case 0x2e8: i=3; break;
+ default: return 0xff;
+ }
+ if(serialports[i]==0) return 0xff;
+
+ switch (index) {
+ case RHR_OFFSET:
+ retval = serialports[i]->Read_RHR();
+ break;
+ case IER_OFFSET:
+ retval = serialports[i]->Read_IER();
+ break;
+ case ISR_OFFSET:
+ retval = serialports[i]->Read_ISR();
+ break;
+ case LCR_OFFSET:
+ retval = serialports[i]->Read_LCR();
+ break;
+ case MCR_OFFSET:
+ retval = serialports[i]->Read_MCR();
+ break;
+ case LSR_OFFSET:
+ retval = serialports[i]->Read_LSR();
+ break;
+ case MSR_OFFSET:
+ retval = serialports[i]->Read_MSR();
+ break;
+ case SPR_OFFSET:
+ retval = serialports[i]->Read_SPR();
+ break;
+ }
+
+#if SERIAL_DEBUG
+ const char* const dbgtext[]=
+ {"RHR","IER","ISR","LCR","MCR","LSR","MSR","SPR","DLL","DLM"};
+ if(serialports[i]->dbg_register) {
+ if((index<2) && ((serialports[i]->LCR)&LCR_DIVISOR_Enable_MASK))
+ index += 8;
+ serialports[i]->log_ser(serialports[i]->dbg_register,
+ "read 0x%2x from %s.",retval,dbgtext[index]);
+ }
+#endif
+ return retval;
+}
+static void SERIAL_Write (Bitu port, Bitu val, Bitu) {
+ Bitu i;
+ Bitu index = port & 0x7;
+ switch(port&0xff8) {
+ case 0x3f8: i=0; break;
+ case 0x2f8: i=1; break;
+ case 0x3e8: i=2; break;
+ case 0x2e8: i=3; break;
+ default: return;
+ }
+ if(serialports[i]==0) return;
+
+#if SERIAL_DEBUG
+ const char* const dbgtext[]={"THR","IER","FCR",
+ "LCR","MCR","!LSR","MSR","SPR","DLL","DLM"};
+ if(serialports[i]->dbg_register) {
+ Bitu debugindex=index;
+ if((index<2) && ((serialports[i]->LCR)&LCR_DIVISOR_Enable_MASK))
+ debugindex += 8;
+ serialports[i]->log_ser(serialports[i]->dbg_register,
+ "write 0x%2x to %s.",val,dbgtext[debugindex]);
+ }
+#endif
+ switch (index) {
+ case THR_OFFSET:
+ serialports[i]->Write_THR (val);
+ return;
+ case IER_OFFSET:
+ serialports[i]->Write_IER (val);
+ return;
+ case FCR_OFFSET:
+ serialports[i]->Write_FCR (val);
+ return;
+ case LCR_OFFSET:
+ serialports[i]->Write_LCR (val);
+ return;
+ case MCR_OFFSET:
+ serialports[i]->Write_MCR (val);
+ return;
+ case MSR_OFFSET:
+ serialports[i]->Write_MSR (val);
+ return;
+ case SPR_OFFSET:
+ serialports[i]->Write_SPR (val);
+ return;
+ default:
+ serialports[i]->Write_reserved (val, port & 0x7);
+ }
+}
+#if SERIAL_DEBUG
+void CSerial::log_ser(bool active, char const* format,...) {
+ if(active) {
+ // copied from DEBUG_SHOWMSG
+ char buf[512];
+ buf[0]=0;
+ sprintf(buf,"%12.3f [% 7u] ",PIC_FullIndex(), SDL_GetTicks());
+ va_list msg;
+ va_start(msg,format);
+ vsprintf(buf+strlen(buf),format,msg);
+ va_end(msg);
+ // Add newline if not present
+ Bitu len=strlen(buf);
+ if(buf[len-1]!='\n') strcat(buf,"\r\n");
+ fputs(buf,debugfp);
+ }
+}
+#endif
+
+void CSerial::changeLineProperties() {
+ // update the event wait time
+ float bitlen;
+
+ if(baud_divider==0) bitlen=(1000.0f/115200.0f);
+ else bitlen = (1000.0f/115200.0f)*(float)baud_divider;
+ bytetime=bitlen*(float)(1+5+1); // startbit + minimum length + stopbit
+ bytetime+= bitlen*(float)(LCR&0x3); // databits
+ if(LCR&0x4) bytetime+=bitlen; // 2nd stopbit
+ if(LCR&0x8) bytetime+=bitlen; // parity
+
+#if SERIAL_DEBUG
+ const char* const dbgtext[]={"none","odd","none","even","none","mark","none","space"};
+ log_ser(dbg_serialtraffic,"New COM parameters: baudrate %5.0f, parity %s, wordlen %d, stopbits %d",
+ 1.0/bitlen*1000.0f,dbgtext[(LCR&0x38)>>3],(LCR&0x3)+5,((LCR&0x4)>>2)+1);
+#endif
+ updatePortConfig (baud_divider, LCR);
+}
+
+static void Serial_EventHandler(Bitu val) {
+ Bitu serclassid=val&0x3;
+ if(serialports[serclassid]!=0)
+ serialports[serclassid]->handleEvent(val>>2);
+}
+
+void CSerial::setEvent(Bit16u type, float duration) {
+ PIC_AddEvent(Serial_EventHandler,duration,(type<<2)|idnumber);
+}
+
+void CSerial::removeEvent(Bit16u type) {
+ // TODO
+ PIC_RemoveSpecificEvents(Serial_EventHandler,(type<<2)|idnumber);
+}
+
+void CSerial::handleEvent(Bit16u type) {
+ switch(type) {
+ case SERIAL_TX_LOOPBACK_EVENT: {
+
+#if SERIAL_DEBUG
+ log_ser(dbg_serialtraffic,loopback_data<0x10?
+ "tx 0x%02x (%u) (loopback)":"tx 0x%02x (%c) (loopback)",
+ loopback_data, loopback_data);
+#endif
+ receiveByte (loopback_data);
+ ByteTransmitted ();
+ break;
+ }
+ case SERIAL_THR_LOOPBACK_EVENT: {
+ loopback_data=txfifo->probeByte();
+ ByteTransmitting();
+ setEvent(SERIAL_TX_LOOPBACK_EVENT,bytetime);
+ break;
+ }
+ case SERIAL_ERRMSG_EVENT: {
+ LOG_MSG("Serial%d: Errors: "\
+ "Framing %d, Parity %d, Overrun RX:%d (IF0:%d), TX:%d, Break %d",
+ COMNUMBER, framingErrors, parityErrors, overrunErrors,
+ overrunIF0,txOverrunErrors, breakErrors);
+ errormsg_pending=false;
+ framingErrors=0;
+ parityErrors=0;
+ overrunErrors=0;
+ txOverrunErrors=0;
+ overrunIF0=0;
+ breakErrors=0;
+ break;
+ }
+ case SERIAL_RX_TIMEOUT_EVENT: {
+ rise(TIMEOUT_PRIORITY);
+ break;
+ }
+ default: handleUpperEvent(type);
+ }
+}
+
+/*****************************************************************************/
+/* Interrupt control routines **/
+/*****************************************************************************/
+void CSerial::rise (Bit8u priority) {
+#if SERIAL_DEBUG
+ if(priority&TX_PRIORITY && !(waiting_interrupts&TX_PRIORITY))
+ log_ser(dbg_interrupt,"tx interrupt on.");
+ if(priority&RX_PRIORITY && !(waiting_interrupts&RX_PRIORITY))
+ log_ser(dbg_interrupt,"rx interrupt on.");
+ if(priority&MSR_PRIORITY && !(waiting_interrupts&MSR_PRIORITY))
+ log_ser(dbg_interrupt,"msr interrupt on.");
+ if(priority&TIMEOUT_PRIORITY && !(waiting_interrupts&TIMEOUT_PRIORITY))
+ log_ser(dbg_interrupt,"fifo rx timeout interrupt on.");
+#endif
+
+ waiting_interrupts |= priority;
+ ComputeInterrupts();
+}
+
+// clears the pending interrupt, triggers other waiting interrupt
+void CSerial::clear (Bit8u priority) {
+
+#if SERIAL_DEBUG
+ if(priority&TX_PRIORITY && (waiting_interrupts&TX_PRIORITY))
+ log_ser(dbg_interrupt,"tx interrupt off.");
+ if(priority&RX_PRIORITY && (waiting_interrupts&RX_PRIORITY))
+ log_ser(dbg_interrupt,"rx interrupt off.");
+ if(priority&MSR_PRIORITY && (waiting_interrupts&MSR_PRIORITY))
+ log_ser(dbg_interrupt,"msr interrupt off.");
+ if(priority&ERROR_PRIORITY && (waiting_interrupts&ERROR_PRIORITY))
+ log_ser(dbg_interrupt,"error interrupt off.");
+#endif
+ waiting_interrupts &= (~priority);
+ ComputeInterrupts();
+}
+
+void CSerial::ComputeInterrupts () {
+
+ Bitu val = IER & waiting_interrupts;
+
+ if (val & ERROR_PRIORITY) ISR = ISR_ERROR_VAL;
+ else if (val & TIMEOUT_PRIORITY) ISR = ISR_FIFOTIMEOUT_VAL;
+ else if (val & RX_PRIORITY) ISR = ISR_RX_VAL;
+ else if (val & TX_PRIORITY) ISR = ISR_TX_VAL;
+ else if (val & MSR_PRIORITY) ISR = ISR_MSR_VAL;
+ else ISR = ISR_CLEAR_VAL;
+
+ if(val && !irq_active)
+ {
+ irq_active=true;
+ if(op2) {
+ PIC_ActivateIRQ(irq);
+#if SERIAL_DEBUG
+ log_ser(dbg_interrupt,"IRQ%d on.",irq);
+#endif
+ }
+ } else if((!val) && irq_active) {
+ irq_active=false;
+ if(op2) {
+ PIC_DeActivateIRQ(irq);
+#if SERIAL_DEBUG
+ log_ser(dbg_interrupt,"IRQ%d off.",irq);
+#endif
+ }
+ }
+}
+
+/*****************************************************************************/
+/* Can a byte be received? **/
+/*****************************************************************************/
+bool CSerial::CanReceiveByte() {
+ return !rxfifo->isFull();
+}
+
+/*****************************************************************************/
+/* A byte was received **/
+/*****************************************************************************/
+void CSerial::receiveByteEx (Bit8u data, Bit8u error) {
+#if SERIAL_DEBUG
+ log_ser(dbg_serialtraffic,data<0x10 ? "\t\t\t\trx 0x%02x (%u)":
+ "\t\t\t\trx 0x%02x (%c)", data, data);
+#endif
+ if (!(rxfifo->addb(data))) {
+ // Overrun error ;o
+ error |= LSR_OVERRUN_ERROR_MASK;
+ }
+ removeEvent(SERIAL_RX_TIMEOUT_EVENT);
+ if(rxfifo->getUsage()==rx_interrupt_threshold) rise (RX_PRIORITY);
+ else setEvent(SERIAL_RX_TIMEOUT_EVENT,bytetime*4.0f);
+
+ if(error) {
+ // A lot of UART chips generate a framing error too when receiving break
+ if(error&LSR_RX_BREAK_MASK) error |= LSR_FRAMING_ERROR_MASK;
+#if SERIAL_DEBUG
+ log_ser(dbg_serialtraffic,"with error: framing=%d,overrun=%d,break=%d,parity=%d",
+ (error&LSR_FRAMING_ERROR_MASK)>0,(error&LSR_OVERRUN_ERROR_MASK)>0,
+ (error&LSR_RX_BREAK_MASK)>0,(error&LSR_PARITY_ERROR_MASK)>0);
+#endif
+ if(FCR&FCR_ACTIVATE) {
+ // error and FIFO active
+ if(!errorfifo->isFull()) {
+ errors_in_fifo++;
+ errorfifo->addb(error);
+ }
+ else {
+ Bit8u toperror=errorfifo->getTop();
+ if(!toperror) errors_in_fifo++;
+ errorfifo->addb(error|toperror);
+ }
+ if(errorfifo->probeByte()) {
+ // the next byte in the error fifo has an error
+ rise (ERROR_PRIORITY);
+ LSR |= error;
+ }
+ } else {
+ // error and FIFO inactive
+ rise (ERROR_PRIORITY);
+ LSR |= error;
+ };
+ if(error&LSR_PARITY_ERROR_MASK) {
+ parityErrors++;
+ };
+ if(error&LSR_OVERRUN_ERROR_MASK) {
+ overrunErrors++;
+ if(!GETFLAG(IF)) overrunIF0++;
+#if SERIAL_DEBUG
+ log_ser(dbg_serialtraffic,"rx overrun (IF=%d)", GETFLAG(IF)>0);
+#endif
+ };
+ if(error&LSR_FRAMING_ERROR_MASK) {
+ framingErrors++;
+ }
+ if(error&LSR_RX_BREAK_MASK) {
+ breakErrors++;
+ }
+ // trigger status window error notification
+ if(!errormsg_pending) {
+ errormsg_pending=true;
+ setEvent(SERIAL_ERRMSG_EVENT,1000);
+ }
+ } else {
+ // no error
+ if(FCR&FCR_ACTIVATE) {
+ errorfifo->addb(error);
+ }
+ }
+}
+
+void CSerial::receiveByte (Bit8u data) {
+ receiveByteEx(data,0);
+}
+
+/*****************************************************************************/
+/* ByteTransmitting: Byte has made it from THR to TX. **/
+/*****************************************************************************/
+void CSerial::ByteTransmitting() {
+ if(sync_guardtime) {
+ //LOG_MSG("byte transmitting after guard");
+ //if(txfifo->isEmpty()) LOG_MSG("Serial port: FIFO empty when it should not");
+ sync_guardtime=false;
+ txfifo->getb();
+ } //else LOG_MSG("byte transmitting");
+ if(txfifo->isEmpty())rise (TX_PRIORITY);
+}
+
+
+/*****************************************************************************/
+/* ByteTransmitted: When a byte was sent, notify here. **/
+/*****************************************************************************/
+void CSerial::ByteTransmitted () {
+ if(!txfifo->isEmpty()) {
+ // there is more data
+ Bit8u data = txfifo->getb();
+#if SERIAL_DEBUG
+ log_ser(dbg_serialtraffic,data<0x10?
+ "\t\t\t\t\ttx 0x%02x (%u) (from buffer)":
+ "\t\t\t\t\ttx 0x%02x (%c) (from buffer)",data,data);
+#endif
+ if (loopback) setEvent(SERIAL_TX_LOOPBACK_EVENT, bytetime);
+ else transmitByte(data,false);
+ if(txfifo->isEmpty())rise (TX_PRIORITY);
+
+ } else {
+#if SERIAL_DEBUG
+ log_ser(dbg_serialtraffic,"tx buffer empty.");
+#endif
+ LSR |= LSR_TX_EMPTY_MASK;
+ }
+}
+
+/*****************************************************************************/
+/* Transmit Holding Register, also LSB of Divisor Latch (r/w) **/
+/*****************************************************************************/
+void CSerial::Write_THR (Bit8u data) {
+ // 0-7 transmit data
+
+ if ((LCR & LCR_DIVISOR_Enable_MASK)) {
+ // write to DLL
+ baud_divider&=0xFF00;
+ baud_divider |= data;
+ changeLineProperties();
+ } else {
+ // write to THR
+ clear (TX_PRIORITY);
+
+ if((LSR & LSR_TX_EMPTY_MASK))
+ { // we were idle before
+ //LOG_MSG("starting new transmit cycle");
+ //if(sync_guardtime) LOG_MSG("Serial port internal error 1");
+ //if(!(LSR & LSR_TX_EMPTY_MASK)) LOG_MSG("Serial port internal error 2");
+ //if(txfifo->getUsage()) LOG_MSG("Serial port internal error 3");
+
+ // need "warming up" time
+ sync_guardtime=true;
+ // block the fifo so it returns THR full (or not in case of FIFO on)
+ txfifo->addb(data);
+ // transmit shift register is busy
+ LSR &= (~LSR_TX_EMPTY_MASK);
+ if(loopback) setEvent(SERIAL_THR_LOOPBACK_EVENT, bytetime/10);
+ else {
+#if SERIAL_DEBUG
+ log_ser(dbg_serialtraffic,data<0x10?
+ "\t\t\t\t\ttx 0x%02x (%u) [FIFO=%2d]":
+ "\t\t\t\t\ttx 0x%02x (%c) [FIFO=%2d]",data,data,txfifo->getUsage());
+#endif
+ transmitByte (data,true);
+ }
+ } else {
+ // shift register is transmitting
+ if(!txfifo->addb(data)) {
+ // TX overflow
+#if SERIAL_DEBUG
+ log_ser(dbg_serialtraffic,"tx overflow");
+#endif
+ txOverrunErrors++;
+ if(!errormsg_pending) {
+ errormsg_pending=true;
+ setEvent(SERIAL_ERRMSG_EVENT,1000);
+ }
+ }
+ }
+ }
+}
+
+
+/*****************************************************************************/
+/* Receive Holding Register, also LSB of Divisor Latch (r/w) **/
+/*****************************************************************************/
+Bitu CSerial::Read_RHR () {
+ // 0-7 received data
+ if ((LCR & LCR_DIVISOR_Enable_MASK)) return baud_divider&0xff;
+ else {
+ Bit8u data=rxfifo->getb();
+ if(FCR&FCR_ACTIVATE) {
+ Bit8u error=errorfifo->getb();
+ if(error) errors_in_fifo--;
+ // new error
+ if(!rxfifo->isEmpty()) {
+ error=errorfifo->probeByte();
+ if(error) {
+ LSR |= error;
+ rise(ERROR_PRIORITY);
+ }
+ }
+ }
+ // Reading RHR resets the FIFO timeout
+ clear (TIMEOUT_PRIORITY);
+ // RX int. is cleared if the buffer holds less data than the threshold
+ if(rxfifo->getUsage()<rx_interrupt_threshold)clear(RX_PRIORITY);
+ removeEvent(SERIAL_RX_TIMEOUT_EVENT);
+ if(!rxfifo->isEmpty()) setEvent(SERIAL_RX_TIMEOUT_EVENT,bytetime*4.0f);
+ return data;
+ }
+}
+
+/*****************************************************************************/
+/* Interrupt Enable Register, also MSB of Divisor Latch (r/w) **/
+/*****************************************************************************/
+// Modified by:
+// - writing to it.
+Bitu CSerial::Read_IER () {
+ // 0 receive holding register (byte received)
+ // 1 transmit holding register (byte sent)
+ // 2 receive line status (overrun, parity error, frame error, break)
+ // 3 modem status
+ // 4-7 0
+
+ if (LCR & LCR_DIVISOR_Enable_MASK) return baud_divider>>8;
+ else return IER&0x0f;
+}
+
+void CSerial::Write_IER (Bit8u data) {
+ if ((LCR & LCR_DIVISOR_Enable_MASK)) { // write to DLM
+ baud_divider&=0xff;
+ baud_divider |= ((Bit16u)data)<<8;
+ changeLineProperties();
+ } else {
+ // Retrigger TX interrupt
+ if (txfifo->isEmpty()&& (data&TX_PRIORITY))
+ waiting_interrupts |= TX_PRIORITY;
+
+ IER = data&0xF;
+ if((FCR&FCR_ACTIVATE)&&data&RX_PRIORITY) IER |= TIMEOUT_PRIORITY;
+ ComputeInterrupts();
+ }
+}
+
+/*****************************************************************************/
+/* Interrupt Status Register (r) **/
+/*****************************************************************************/
+// modified by:
+// - incoming interrupts
+// - loopback mode
+Bitu CSerial::Read_ISR () {
+ // 0 0:interrupt pending 1: no interrupt
+ // 1-3 identification
+ // 011 LSR
+ // 010 RXRDY
+ // 110 RX_TIMEOUT
+ // 001 TXRDY
+ // 000 MSR
+ // 4-7 0
+
+ if(IER&Modem_Status_INT_Enable_MASK) updateMSR();
+ Bit8u retval = ISR;
+
+ // clear changes ISR!! mean..
+ if(ISR==ISR_TX_VAL) clear(TX_PRIORITY);
+ if(FCR&FCR_ACTIVATE) retval |= FIFO_STATUS_ACTIVE;
+
+ return retval;
+}
+
+#define BIT_CHANGE_H(oldv,newv,bitmask) (!(oldv&bitmask) && (newv&bitmask))
+#define BIT_CHANGE_L(oldv,newv,bitmask) ((oldv&bitmask) && !(newv&bitmask))
+
+void CSerial::Write_FCR (Bit8u data) {
+ if(BIT_CHANGE_H(FCR,data,FCR_ACTIVATE)) {
+ // FIFO was switched on
+ errors_in_fifo=0; // should already be 0
+ errorfifo->setSize(fifosize);
+ rxfifo->setSize(fifosize);
+ txfifo->setSize(fifosize);
+ } else if(BIT_CHANGE_L(FCR,data,FCR_ACTIVATE)) {
+ // FIFO was switched off
+ errors_in_fifo=0;
+ errorfifo->setSize(1);
+ rxfifo->setSize(1);
+ txfifo->setSize(1);
+ rx_interrupt_threshold=1;
+ }
+ FCR=data&0xCF;
+ if(FCR&FCR_CLEAR_RX) {
+ errors_in_fifo=0;
+ errorfifo->clear();
+ rxfifo->clear();
+ }
+ if(FCR&FCR_CLEAR_TX) txfifo->clear();
+ if(FCR&FCR_ACTIVATE) {
+ switch(FCR>>6) {
+ case 0: rx_interrupt_threshold=1; break;
+ case 1: rx_interrupt_threshold=4; break;
+ case 2: rx_interrupt_threshold=8; break;
+ case 3: rx_interrupt_threshold=14; break;
+ }
+ }
+}
+
+/*****************************************************************************/
+/* Line Control Register (r/w) **/
+/*****************************************************************************/
+// signal decoder configuration:
+// - parity, stopbits, word length
+// - send break
+// - switch between RHR/THR and baud rate registers
+// Modified by:
+// - writing to it.
+Bitu CSerial::Read_LCR () {
+ // 0-1 word length
+ // 2 stop bits
+ // 3 parity enable
+ // 4-5 parity type
+ // 6 set break
+ // 7 divisor latch enable
+ return LCR;
+}
+
+void CSerial::Write_LCR (Bit8u data) {
+ Bit8u lcr_old = LCR;
+ LCR = data;
+ if (((data ^ lcr_old) & LCR_PORTCONFIG_MASK) != 0) {
+ changeLineProperties();
+ }
+ if (((data ^ lcr_old) & LCR_BREAK_MASK) != 0) {
+ if(!loopback) setBreak ((LCR & LCR_BREAK_MASK)!=0);
+ else {
+ // TODO: set loopback break event to reveiveError after
+ }
+#if SERIAL_DEBUG
+ log_ser(dbg_serialtraffic,((LCR & LCR_BREAK_MASK)!=0) ?
+ "break on.":"break off.");
+#endif
+ }
+}
+
+/*****************************************************************************/
+/* Modem Control Register (r/w) **/
+/*****************************************************************************/
+// Set levels of RTS and DTR, as well as loopback-mode.
+// Modified by:
+// - writing to it.
+Bitu CSerial::Read_MCR () {
+ // 0 -DTR
+ // 1 -RTS
+ // 2 -OP1
+ // 3 -OP2
+ // 4 loopback enable
+ // 5-7 0
+ Bit8u retval=0;
+ if(dtr) retval|=MCR_DTR_MASK;
+ if(rts) retval|=MCR_RTS_MASK;
+ if(op1) retval|=MCR_OP1_MASK;
+ if(op2) retval|=MCR_OP2_MASK;
+ if(loopback) retval|=MCR_LOOPBACK_Enable_MASK;
+ return retval;
+}
+
+void CSerial::Write_MCR (Bit8u data) {
+ // WARNING: At the time setRTSDTR is called rts and dsr members are still wrong.
+ if (data&FIFO_FLOWCONTROL) LOG_MSG("Warning: tried to activate hardware handshake.");
+ bool new_dtr = data & MCR_DTR_MASK? true:false;
+ bool new_rts = data & MCR_RTS_MASK? true:false;
+ bool new_op1 = data & MCR_OP1_MASK? true:false;
+ bool new_op2 = data & MCR_OP2_MASK? true:false;
+ bool new_loopback = data & MCR_LOOPBACK_Enable_MASK? true:false;
+ if (loopback != new_loopback) {
+ if (new_loopback) setRTSDTR(false,false);
+ else setRTSDTR(new_rts,new_dtr);
+ }
+
+ if (new_loopback) { // is on:
+ // DTR->DSR
+ // RTS->CTS
+ // OP1->RI
+ // OP2->CD
+ if (new_dtr != dtr && !d_dsr) {
+ d_dsr = true;
+ rise (MSR_PRIORITY);
+ }
+ if (new_rts != rts && !d_cts) {
+ d_cts = true;
+ rise (MSR_PRIORITY);
+ }
+ if (new_op1 != op1 && !d_ri) {
+ // interrupt only at trailing edge
+ if (!new_op1) {
+ d_ri = true;
+ rise (MSR_PRIORITY);
+ }
+ }
+ if (new_op2 != op2 && !d_cd) {
+ d_cd = true;
+ rise (MSR_PRIORITY);
+ }
+ } else {
+ // loopback is off
+ if (new_rts != rts) {
+ // RTS difference
+ if (new_dtr != dtr) {
+ // both difference
+
+#if SERIAL_DEBUG
+ log_ser(dbg_modemcontrol,"RTS %x.",new_rts);
+ log_ser(dbg_modemcontrol,"DTR %x.",new_dtr);
+#endif
+ setRTSDTR(new_rts, new_dtr);
+ } else {
+ // only RTS
+
+#if SERIAL_DEBUG
+ log_ser(dbg_modemcontrol,"RTS %x.",new_rts);
+#endif
+ setRTS(new_rts);
+ }
+ } else if (new_dtr != dtr) {
+ // only DTR
+#if SERIAL_DEBUG
+ log_ser(dbg_modemcontrol,"%DTR %x.",new_dtr);
+#endif
+ setDTR(new_dtr);
+ }
+ }
+ // interrupt logic: if new_OP2 is 0, the IRQ line is tristated (pulled high)
+ // which turns off the IRQ generation.
+ if ((!op2) && new_op2) {
+ // irq has been enabled (tristate high -> irq level)
+ // Generate one if ComputeInterrupts has set irq_active to true
+ if (irq_active) PIC_ActivateIRQ(irq);
+ } else if (op2 && (!new_op2)) {
+ // irq has been disabled (irq level -> tristate)
+ // Remove the IRQ signal if the irq was being generated before
+ if (irq_active) PIC_DeActivateIRQ(irq);
+ }
+
+ dtr=new_dtr;
+ rts=new_rts;
+ op1=new_op1;
+ op2=new_op2;
+ loopback=new_loopback;
+}
+
+/*****************************************************************************/
+/* Line Status Register (r) **/
+/*****************************************************************************/
+// errors, tx registers status, rx register status
+// modified by:
+// - event from real serial port
+// - loopback
+Bitu CSerial::Read_LSR () {
+ Bitu retval = LSR & (LSR_ERROR_MASK|LSR_TX_EMPTY_MASK);
+ if(txfifo->isEmpty()) retval |= LSR_TX_HOLDING_EMPTY_MASK;
+ if(!(rxfifo->isEmpty()))retval |= LSR_RX_DATA_READY_MASK;
+ if(errors_in_fifo) retval |= FIFO_ERROR;
+ LSR &= (~LSR_ERROR_MASK); // clear error bits on read
+ clear (ERROR_PRIORITY);
+ return retval;
+}
+
+void CSerial::Write_MSR (Bit8u val) {
+ d_cts = (val&MSR_dCTS_MASK)?true:false;
+ d_dsr = (val&MSR_dDSR_MASK)?true:false;
+ d_cd = (val&MSR_dCD_MASK)?true:false;
+ d_ri = (val&MSR_dRI_MASK)?true:false;
+}
+
+/*****************************************************************************/
+/* Modem Status Register (r) **/
+/*****************************************************************************/
+// Contains status of the control input lines (CD, RI, DSR, CTS) and
+// their "deltas": if level changed since last read delta = 1.
+// modified by:
+// - real values
+// - write operation to MCR in loopback mode
+Bitu CSerial::Read_MSR () {
+ Bit8u retval=0;
+
+ if (loopback) {
+
+ if (rts) retval |= MSR_CTS_MASK;
+ if (dtr) retval |= MSR_DSR_MASK;
+ if (op1) retval |= MSR_RI_MASK;
+ if (op2) retval |= MSR_CD_MASK;
+
+ } else {
+
+ updateMSR();
+ if (cd) retval |= MSR_CD_MASK;
+ if (ri) retval |= MSR_RI_MASK;
+ if (dsr) retval |= MSR_DSR_MASK;
+ if (cts) retval |= MSR_CTS_MASK;
+
+ }
+ // new delta flags
+ if(d_cd) retval|=MSR_dCD_MASK;
+ if(d_ri) retval|=MSR_dRI_MASK;
+ if(d_cts) retval|=MSR_dCTS_MASK;
+ if(d_dsr) retval|=MSR_dDSR_MASK;
+
+ d_cd = false;
+ d_ri = false;
+ d_cts = false;
+ d_dsr = false;
+
+ clear (MSR_PRIORITY);
+ return retval;
+}
+
+/*****************************************************************************/
+/* Scratchpad Register (r/w) **/
+/*****************************************************************************/
+// Just a memory register. Not much to do here.
+Bitu CSerial::Read_SPR () {
+ return SPR;
+}
+
+void CSerial::Write_SPR (Bit8u data) {
+ SPR = data;
+}
+
+/*****************************************************************************/
+/* Write_reserved **/
+/*****************************************************************************/
+void CSerial::Write_reserved (Bit8u data, Bit8u address) {
+ /*LOG_UART("Serial%d: Write to reserved register, value 0x%x, register %x",
+ COMNUMBER, data, address);*/
+}
+
+/*****************************************************************************/
+/* MCR Access: returns cirquit state as boolean. **/
+/*****************************************************************************/
+bool CSerial::getDTR () {
+ if(loopback) return false;
+ else return dtr;
+}
+
+bool CSerial::getRTS () {
+ if(loopback) return false;
+ else return rts;
+}
+
+/*****************************************************************************/
+/* MSR Access **/
+/*****************************************************************************/
+bool CSerial::getRI () {
+ return ri;
+}
+
+bool CSerial::getCD () {
+ return cd;
+}
+
+bool CSerial::getDSR () {
+ return dsr;
+}
+
+bool CSerial::getCTS () {
+ return cts;
+}
+
+void CSerial::setRI (bool value) {
+ if (value != ri) {
+
+#if SERIAL_DEBUG
+ log_ser(dbg_modemcontrol,"%RI %x.",value);
+#endif
+ // don't change delta when in loopback mode
+ ri=value;
+ if(!loopback) {
+ if(value==false) d_ri=true;
+ rise (MSR_PRIORITY);
+ }
+ }
+ //else no change
+}
+void CSerial::setDSR (bool value) {
+ if (value != dsr) {
+#if SERIAL_DEBUG
+ log_ser(dbg_modemcontrol,"DSR %x.",value);
+#endif
+ // don't change delta when in loopback mode
+ dsr=value;
+ if(!loopback) {
+ d_dsr=true;
+ rise (MSR_PRIORITY);
+ }
+ }
+ //else no change
+}
+void CSerial::setCD (bool value) {
+ if (value != cd) {
+#if SERIAL_DEBUG
+ log_ser(dbg_modemcontrol,"CD %x.",value);
+#endif
+ // don't change delta when in loopback mode
+ cd=value;
+ if(!loopback) {
+ d_cd=true;
+ rise (MSR_PRIORITY);
+ }
+ }
+ //else no change
+}
+void CSerial::setCTS (bool value) {
+ if (value != cts) {
+#if SERIAL_DEBUG
+ log_ser(dbg_modemcontrol,"CTS %x.",value);
+#endif
+ // don't change delta when in loopback mode
+ cts=value;
+ if(!loopback) {
+ d_cts=true;
+ rise (MSR_PRIORITY);
+ }
+ }
+ //else no change
+}
+
+/*****************************************************************************/
+/* Initialisation **/
+/*****************************************************************************/
+void CSerial::Init_Registers () {
+ // The "power on" settings
+ irq_active=false;
+ waiting_interrupts = 0x0;
+
+ Bit32u initbps = 9600;
+ Bit8u bytesize = 8;
+ char parity = 'N';
+
+ Bit8u lcrresult = 0;
+ Bit16u baudresult = 0;
+
+ IER = 0;
+ ISR = 0x1;
+ LCR = 0;
+ //MCR = 0xff;
+ loopback = true;
+ dtr=true;
+ rts=true;
+ op1=true;
+ op2=true;
+
+ sync_guardtime=false;
+ FCR=0xff;
+ Write_FCR(0x00);
+
+
+ LSR = 0x60;
+ d_cts = true;
+ d_dsr = true;
+ d_ri = true;
+ d_cd = true;
+ cts = true;
+ dsr = true;
+ ri = true;
+ cd = true;
+
+ SPR = 0xFF;
+
+ baud_divider=0x0;
+
+ // make lcr: byte size, parity, stopbits, baudrate
+
+ if (bytesize == 5)
+ lcrresult |= LCR_DATABITS_5;
+ else if (bytesize == 6)
+ lcrresult |= LCR_DATABITS_6;
+ else if (bytesize == 7)
+ lcrresult |= LCR_DATABITS_7;
+ else
+ lcrresult |= LCR_DATABITS_8;
+
+ switch(parity)
+ {
+ case 'N':
+ case 'n':
+ lcrresult |= LCR_PARITY_NONE;
+ break;
+ case 'O':
+ case 'o':
+ lcrresult |= LCR_PARITY_ODD;
+ break;
+ case 'E':
+ case 'e':
+ lcrresult |= LCR_PARITY_EVEN;
+ break;
+ case 'M':
+ case 'm':
+ lcrresult |= LCR_PARITY_MARK;
+ break;
+ case 'S':
+ case 's':
+ lcrresult |= LCR_PARITY_SPACE;
+ break;
+ }
+
+ // baudrate
+ if (initbps > 0)
+ baudresult = (Bit16u) (115200 / initbps);
+ else
+ baudresult = 12; // = 9600 baud
+
+ Write_MCR (0);
+ Write_LCR (LCR_DIVISOR_Enable_MASK);
+ Write_THR ((Bit8u) baudresult & 0xff);
+ Write_IER ((Bit8u) (baudresult >> 8));
+ Write_LCR (lcrresult);
+ updateMSR();
+ Read_MSR();
+ PIC_DeActivateIRQ(irq);
+}
+
+CSerial::CSerial(Bitu id, CommandLine* cmd) {
+ idnumber=id;
+ Bit16u base = serial_baseaddr[id];
+
+ irq = serial_defaultirq[id];
+ getBituSubstring("irq:",&irq, cmd);
+ if (irq < 2 || irq > 15) irq = serial_defaultirq[id];
+
+#if SERIAL_DEBUG
+ dbg_serialtraffic = cmd->FindExist("dbgtr", false);
+ dbg_modemcontrol = cmd->FindExist("dbgmd", false);
+ dbg_register = cmd->FindExist("dbgreg", false);
+ dbg_interrupt = cmd->FindExist("dbgirq", false);
+ dbg_aux = cmd->FindExist("dbgaux", false);
+
+ if(cmd->FindExist("dbgall", false)) {
+ dbg_serialtraffic=
+ dbg_modemcontrol=
+ dbg_register=
+ dbg_interrupt=
+ dbg_aux= true;
+ }
+
+
+ if(dbg_serialtraffic|dbg_modemcontrol|dbg_register|dbg_interrupt|dbg_aux)
+ debugfp=OpenCaptureFile("serlog",".serlog.txt");
+ else debugfp=0;
+
+ if(debugfp == 0) {
+ dbg_serialtraffic=
+ dbg_modemcontrol=
+ dbg_register=
+ dbg_interrupt=
+ dbg_aux= false;
+ } else {
+ std::string cleft;
+ cmd->GetStringRemain(cleft);
+
+ log_ser(true,"Serial%d: BASE %3x, IRQ %d, initstring \"%s\"\r\n\r\n",
+ COMNUMBER,base,irq,cleft.c_str());
+ }
+#endif
+ fifosize=16;
+
+ errorfifo = new MyFifo(fifosize);
+ rxfifo = new MyFifo(fifosize);
+ txfifo = new MyFifo(fifosize);
+
+ mydosdevice=new device_COM(this);
+ DOS_AddDevice(mydosdevice);
+
+ errormsg_pending=false;
+ framingErrors=0;
+ parityErrors=0;
+ overrunErrors=0;
+ txOverrunErrors=0;
+ overrunIF0=0;
+ breakErrors=0;
+
+ for (Bitu i = 0; i <= 7; i++) {
+ WriteHandler[i].Install (i + base, SERIAL_Write, IO_MB);
+ ReadHandler[i].Install (i + base, SERIAL_Read, IO_MB);
+ }
+}
+
+bool CSerial::getBituSubstring(const char* name,Bitu* data, CommandLine* cmd) {
+ std::string tmpstring;
+ if(!(cmd->FindStringBegin(name,tmpstring,false))) return false;
+ const char* tmpchar = tmpstring.c_str();
+ unsigned int d = 0;
+ if(sscanf(tmpchar,"%u",&d) != 1) return false;
+ *data = static_cast<Bitu>(d);
+ return true;
+}
+
+CSerial::~CSerial(void) {
+ DOS_DelDevice(mydosdevice);
+ for(Bitu i = 0; i <= SERIAL_BASE_EVENT_COUNT; i++)
+ removeEvent(i);
+};
+bool CSerial::Getchar(Bit8u* data, Bit8u* lsr, bool wait_dsr, Bitu timeout) {
+ double starttime=PIC_FullIndex();
+ // wait for DSR on
+ if(wait_dsr) {
+ while((!(Read_MSR()&0x20))&&(starttime>PIC_FullIndex()-timeout))
+ CALLBACK_Idle();
+ if(!(starttime>PIC_FullIndex()-timeout)) {
+#if SERIAL_DEBUG
+ log_ser(dbg_aux,"Getchar status timeout: MSR 0x%x",Read_MSR());
+#endif
+ return false;
+ }
+ }
+ // wait for a byte to arrive
+ while((!((*lsr=Read_LSR())&0x1))&&(starttime>PIC_FullIndex()-timeout))
+ CALLBACK_Idle();
+
+ if(!(starttime>PIC_FullIndex()-timeout)) {
+#if SERIAL_DEBUG
+ log_ser(dbg_aux,"Getchar data timeout: MSR 0x%x",Read_MSR());
+#endif
+ return false;
+ }
+ *data=Read_RHR();
+
+#if SERIAL_DEBUG
+ log_ser(dbg_aux,"Getchar read 0x%x",*data);
+#endif
+ return true;
+}
+
+
+bool CSerial::Putchar(Bit8u data, bool wait_dsr, bool wait_cts, Bitu timeout) {
+
+ double starttime=PIC_FullIndex();
+ // wait for it to become empty
+ while(!(Read_LSR()&0x20)) {
+ CALLBACK_Idle();
+ }
+ // wait for DSR+CTS on
+ if(wait_dsr||wait_cts) {
+ if(wait_dsr||wait_cts) {
+ while(((Read_MSR()&0x30)!=0x30)&&(starttime>PIC_FullIndex()-timeout))
+ CALLBACK_Idle();
+ } else if(wait_dsr) {
+ while(!(Read_MSR()&0x20)&&(starttime>PIC_FullIndex()-timeout))
+ CALLBACK_Idle();
+ } else if(wait_cts) {
+ while(!(Read_MSR()&0x10)&&(starttime>PIC_FullIndex()-timeout))
+ CALLBACK_Idle();
+ }
+ if(!(starttime>PIC_FullIndex()-timeout)) {
+#if SERIAL_DEBUG
+ log_ser(dbg_aux,"Putchar timeout: MSR 0x%x",Read_MSR());
+#endif
+ return false;
+ }
+ }
+ Write_THR(data);
+
+#if SERIAL_DEBUG
+ log_ser(dbg_aux,"Putchar 0x%x",data);
+#endif
+
+ return true;
+}
+
+class SERIALPORTS:public Module_base {
+public:
+ SERIALPORTS (Section * configuration):Module_base (configuration) {
+ Bit16u biosParameter[4] = { 0, 0, 0, 0 };
+ Section_prop *section = static_cast <Section_prop*>(configuration);
+
+ char s_property[] = "serialx";
+ for(Bitu i = 0; i < 4; i++) {
+ // get the configuration property
+ s_property[6] = '1' + i;
+ Prop_multival* p = section->Get_multival(s_property);
+ std::string type = p->GetSection()->Get_string("type");
+ CommandLine cmd(0,p->GetSection()->Get_string("parameters"));
+
+ // detect the type
+ if (type=="dummy") {
+ serialports[i] = new CSerialDummy (i, &cmd);
+ }
+#ifdef DIRECTSERIAL_AVAILIBLE
+ else if (type=="directserial") {
+ serialports[i] = new CDirectSerial (i, &cmd);
+ if (!serialports[i]->InstallationSuccessful) {
+ // serial port name was wrong or already in use
+ delete serialports[i];
+ serialports[i] = NULL;
+ }
+ }
+#endif
+#if C_MODEM
+ else if(type=="modem") {
+ serialports[i] = new CSerialModem (i, &cmd);
+ if (!serialports[i]->InstallationSuccessful) {
+ delete serialports[i];
+ serialports[i] = NULL;
+ }
+ }
+ else if(type=="nullmodem") {
+ serialports[i] = new CNullModem (i, &cmd);
+ if (!serialports[i]->InstallationSuccessful) {
+ delete serialports[i];
+ serialports[i] = NULL;
+ }
+ }
+#endif
+ else if(type=="disabled") {
+ serialports[i] = NULL;
+ } else {
+ serialports[i] = NULL;
+ LOG_MSG("Invalid type for serial%d",i+1);
+ }
+ if(serialports[i]) biosParameter[i] = serial_baseaddr[i];
+ } // for 1-4
+ BIOS_SetComPorts (biosParameter);
+ }
+
+ ~SERIALPORTS () {
+ for (Bitu i = 0; i < 4; i++)
+ if (serialports[i]) {
+ delete serialports[i];
+ serialports[i] = 0;
+ }
+ }
+};
+
+static SERIALPORTS *testSerialPortsBaseclass;
+
+void SERIAL_Destroy (Section * sec) {
+ delete testSerialPortsBaseclass;
+ testSerialPortsBaseclass = NULL;
+}
+
+void SERIAL_Init (Section * sec) {
+ // should never happen
+ if (testSerialPortsBaseclass) delete testSerialPortsBaseclass;
+ testSerialPortsBaseclass = new SERIALPORTS (sec);
+ sec->AddDestroyFunction (&SERIAL_Destroy, true);
+}
diff --git a/src/hardware/serialport/softmodem.cpp b/src/hardware/serialport/softmodem.cpp
new file mode 100644
index 000000000..63625b3dd
--- /dev/null
+++ b/src/hardware/serialport/softmodem.cpp
@@ -0,0 +1,837 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+
+#if C_MODEM
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "support.h"
+#include "serialport.h"
+#include "softmodem.h"
+#include "misc_util.h"
+
+//#include "mixer.h"
+
+
+CSerialModem::CSerialModem(Bitu id, CommandLine* cmd):CSerial(id, cmd) {
+ InstallationSuccessful=false;
+ connected=false;
+
+ rqueue=new CFifo(MODEM_BUFFER_QUEUE_SIZE);
+ tqueue=new CFifo(MODEM_BUFFER_QUEUE_SIZE);
+
+ // Default to direct null modem connection. Telnet mode interprets IAC codes
+ telnetmode = false;
+
+ // Initialize the sockets and setup the listening port
+ listenport = 23;
+ waitingclientsocket=0;
+ clientsocket = 0;
+ serversocket = 0;
+ getBituSubstring("listenport:", &listenport, cmd);
+
+ // TODO: Fix dialtones if requested
+ //mhd.chan=MIXER_AddChannel((MIXER_MixHandler)this->MODEM_CallBack,8000,"MODEM");
+ //MIXER_Enable(mhd.chan,false);
+ //MIXER_SetMode(mhd.chan,MIXER_16MONO);
+
+ CSerial::Init_Registers();
+ Reset(); // reset calls EnterIdleState
+
+ setEvent(SERIAL_POLLING_EVENT,1);
+ InstallationSuccessful=true;
+}
+
+CSerialModem::~CSerialModem() {
+ if(serversocket) delete serversocket;
+ if(clientsocket) delete clientsocket;
+ if(waitingclientsocket) delete waitingclientsocket;
+
+ delete rqueue;
+ delete tqueue;
+
+ // remove events
+ for(Bitu i = SERIAL_BASE_EVENT_COUNT+1; i <= SERIAL_MODEM_EVENT_COUNT; i++)
+ removeEvent(i);
+}
+
+void CSerialModem::handleUpperEvent(Bit16u type) {
+ switch (type) {
+ case SERIAL_RX_EVENT: {
+ // check for bytes to be sent to port
+ if(CSerial::CanReceiveByte())
+ if(rqueue->inuse() && (CSerial::getRTS()||(flowcontrol!=3))) {
+ Bit8u rbyte = rqueue->getb();
+ //LOG_MSG("Modem: sending byte %2x back to UART3",rbyte);
+ CSerial::receiveByte(rbyte);
+ }
+ if(CSerial::CanReceiveByte()) setEvent(SERIAL_RX_EVENT, bytetime*0.98f);
+ break;
+ }
+ case MODEM_TX_EVENT: {
+ if (tqueue->left()) {
+ tqueue->addb(waiting_tx_character);
+ if (tqueue->left() < 2) {
+ CSerial::setCTS(false);
+ }
+ } else {
+ static Bits lcount=0;
+ if (lcount<1000) {
+ lcount++;
+ LOG_MSG("MODEM: TX Buffer overflow!");
+ }
+ }
+ ByteTransmitted();
+ break;
+ }
+ case SERIAL_POLLING_EVENT: {
+ if (rqueue->inuse()) {
+ removeEvent(SERIAL_RX_EVENT);
+ setEvent(SERIAL_RX_EVENT, (float)0.01);
+ }
+ Timer2();
+ setEvent(SERIAL_POLLING_EVENT,1);
+ break;
+ }
+
+ case MODEM_RING_EVENT: {
+ break;
+ }
+ }
+}
+
+void CSerialModem::SendLine(const char *line) {
+ rqueue->addb(0xd);
+ rqueue->addb(0xa);
+ rqueue->adds((Bit8u *)line,strlen(line));
+ rqueue->addb(0xd);
+ rqueue->addb(0xa);
+}
+
+// only for numbers < 1000...
+void CSerialModem::SendNumber(Bitu val) {
+ rqueue->addb(0xd);
+ rqueue->addb(0xa);
+
+ rqueue->addb(val/100+'0');
+ val = val%100;
+ rqueue->addb(val/10+'0');
+ val = val%10;
+ rqueue->addb(val+'0');
+
+ rqueue->addb(0xd);
+ rqueue->addb(0xa);
+}
+
+void CSerialModem::SendRes(ResTypes response) {
+ char const * string;Bitu code;
+ switch (response)
+ {
+ case ResNONE: return;
+ case ResOK: string="OK"; code=0; break;
+ case ResERROR: string="ERROR"; code=4; break;
+ case ResRING: string="RING"; code=2; break;
+ case ResNODIALTONE: string="NO DIALTONE"; code=6; break;
+ case ResNOCARRIER: string="NO CARRIER" ;code=3; break;
+ case ResCONNECT: string="CONNECT 57600"; code=1; break;
+ }
+
+ if(doresponse!=1) {
+ if(doresponse==2 && (response==ResRING ||
+ response == ResCONNECT || response==ResNOCARRIER)) return;
+ if(numericresponse) SendNumber(code);
+ else SendLine(string);
+
+ //if(CSerial::CanReceiveByte()) // very fast response
+ // if(rqueue->inuse() && CSerial::getRTS())
+ // { Bit8u rbyte =rqueue->getb();
+ // CSerial::receiveByte(rbyte);
+ // LOG_MSG("Modem: sending byte %2x back to UART2",rbyte);
+ // }
+
+ LOG_MSG("Modem response: %s", string);
+ }
+}
+
+bool CSerialModem::Dial(char * host) {
+
+ // Scan host for port
+ Bit16u port;
+ char * hasport=strrchr(host,':');
+ if (hasport) {
+ *hasport++=0;
+ port=(Bit16u)atoi(hasport);
+ }
+ else port=MODEM_DEFAULT_PORT;
+ // Resolve host we're gonna dial
+ LOG_MSG("Connecting to host %s port %d",host,port);
+ clientsocket = new TCPClientSocket(host, port);
+ if(!clientsocket->isopen) {
+ delete clientsocket;
+ clientsocket=0;
+ LOG_MSG("Failed to connect.");
+ SendRes(ResNOCARRIER);
+ EnterIdleState();
+ return false;
+ } else {
+ EnterConnectedState();
+ return true;
+ }
+}
+
+void CSerialModem::AcceptIncomingCall(void) {
+ if(waitingclientsocket) {
+ clientsocket=waitingclientsocket;
+ waitingclientsocket=0;
+ EnterConnectedState();
+ } else {
+ EnterIdleState();
+ }
+}
+
+Bitu CSerialModem::ScanNumber(char * & scan) {
+ Bitu ret=0;
+ while (char c=*scan) {
+ if (c>='0' && c<='9') {
+ ret*=10;
+ ret+=c-'0';
+ scan++;
+ } else break;
+ }
+ return ret;
+}
+
+char CSerialModem::GetChar(char * & scan) {
+ char ch = *scan;
+ scan++;
+ return ch;
+}
+
+void CSerialModem::Reset(){
+ EnterIdleState();
+ cmdpos = 0;
+ cmdbuf[0]=0;
+ oldDTRstate = getDTR();
+ flowcontrol = 0;
+ plusinc = 0;
+ if(clientsocket) {
+ delete clientsocket;
+ clientsocket=0;
+ }
+ memset(&reg,0,sizeof(reg));
+ reg[MREG_AUTOANSWER_COUNT]=0; // no autoanswer
+ reg[MREG_RING_COUNT] = 1;
+ reg[MREG_ESCAPE_CHAR]='+';
+ reg[MREG_CR_CHAR]='\r';
+ reg[MREG_LF_CHAR]='\n';
+ reg[MREG_BACKSPACE_CHAR]='\b';
+
+ cmdpause = 0;
+ echo = true;
+ doresponse = 0;
+ numericresponse = false;
+
+ /* Default to direct null modem connection. Telnet mode interprets IAC codes */
+ telnetmode = false;
+}
+
+void CSerialModem::EnterIdleState(void){
+ connected=false;
+ ringing=false;
+
+ if(clientsocket) {
+ delete clientsocket;
+ clientsocket=0;
+ }
+
+ if(waitingclientsocket) { // clear current incoming socket
+ delete waitingclientsocket;
+ waitingclientsocket=0;
+ }
+ // get rid of everything
+ if(serversocket) {
+ while( (waitingclientsocket=serversocket->Accept()) )
+ delete waitingclientsocket;
+ } else if (listenport) {
+
+ serversocket=new TCPServerSocket(listenport);
+ if(!serversocket->isopen) {
+ LOG_MSG("Serial%d: Modem could not open TCP port %d.",COMNUMBER,listenport);
+ delete serversocket;
+ serversocket=0;
+ } else LOG_MSG("Serial%d: Modem listening on port %d...",COMNUMBER,listenport);
+ }
+ waitingclientsocket=0;
+
+ commandmode = true;
+ CSerial::setCD(false);
+ CSerial::setRI(false);
+ CSerial::setDSR(true);
+ CSerial::setCTS(true);
+ tqueue->clear();
+}
+
+void CSerialModem::EnterConnectedState(void) {
+ if(serversocket) {
+ // we don't accept further calls
+ delete serversocket;
+ serversocket=0;
+ }
+ SendRes(ResCONNECT);
+ commandmode = false;
+ memset(&telClient, 0, sizeof(telClient));
+ connected = true;
+ ringing = false;
+ CSerial::setCD(true);
+ CSerial::setRI(false);
+}
+
+void CSerialModem::DoCommand() {
+ cmdbuf[cmdpos] = 0;
+ cmdpos = 0; //Reset for next command
+ upcase(cmdbuf);
+ LOG_MSG("Command sent to modem: ->%s<-\n", cmdbuf);
+ /* Check for empty line, stops dialing and autoanswer */
+ if (!cmdbuf[0]) {
+ reg[0]=0; // autoanswer off
+ return;
+ }
+ //else {
+ //MIXER_Enable(mhd.chan,false);
+ // dialing = false;
+ // SendRes(ResNOCARRIER);
+ // goto ret_none;
+ //}
+ /* AT command set interpretation */
+
+ if ((cmdbuf[0] != 'A') || (cmdbuf[1] != 'T')) {
+ SendRes(ResERROR);
+ return;
+ }
+ if (strstr(cmdbuf,"NET0")) {
+ telnetmode = false;
+ SendRes(ResOK);
+ return;
+ }
+ else if (strstr(cmdbuf,"NET1")) {
+ telnetmode = true;
+ SendRes(ResOK);
+ return;
+ }
+
+ char * scanbuf = &cmdbuf[2];
+ while (1) {
+ // LOG_MSG("loopstart ->%s<-",scanbuf);
+ char chr = GetChar(scanbuf);
+ switch (chr) {
+ case 'D': { // Dial
+ char * foundstr=&scanbuf[0];
+ if (*foundstr=='T' || *foundstr=='P') foundstr++;
+ // Small protection against empty line and long string
+ if ((!foundstr[0]) || (strlen(foundstr)>100)) {
+ SendRes(ResERROR);
+ return;
+ }
+ char* helper;
+ // scan for and remove spaces; weird bug: with leading spaces in the string,
+ // SDLNet_ResolveHost will return no error but not work anyway (win)
+ while(foundstr[0]==' ') foundstr++;
+ helper=foundstr;
+ helper+=strlen(foundstr);
+ while(helper[0]==' ') {
+ helper[0]=0;
+ helper--;
+ }
+
+ //Large enough scope, so the buffers are still valid when reaching Dail.
+ char buffer[128];
+ char obuffer[128];
+ if (strlen(foundstr) >= 12) {
+ // Check if supplied parameter only consists of digits
+ bool isNum = true;
+ size_t fl = strlen(foundstr);
+ for (size_t i = 0; i < fl; i++)
+ if (foundstr[i] < '0' || foundstr[i] > '9') isNum = false;
+ if (isNum) {
+ // Parameter is a number with at least 12 digits => this cannot
+ // be a valid IP/name
+ // Transform by adding dots
+ size_t j = 0;
+ size_t foundlen = strlen(foundstr);
+ for (size_t i = 0; i < foundlen; i++) {
+ buffer[j++] = foundstr[i];
+ // Add a dot after the third, sixth and ninth number
+ if (i == 2 || i == 5 || i == 8)
+ buffer[j++] = '.';
+ // If the string is longer than 12 digits,
+ // interpret the rest as port
+ if (i == 11 && strlen(foundstr)>12)
+ buffer[j++] = ':';
+ }
+ buffer[j] = 0;
+ foundstr = buffer;
+
+ // Remove Zeros from beginning of octets
+ size_t k = 0;
+ size_t foundlen2 = strlen(foundstr);
+ for (size_t i = 0; i < foundlen2; i++) {
+ if (i == 0 && foundstr[0] == '0') continue;
+ if (i == 1 && foundstr[0] == '0' && foundstr[1] == '0') continue;
+ if (foundstr[i] == '0' && foundstr[i-1] == '.') continue;
+ if (foundstr[i] == '0' && foundstr[i-1] == '0' && foundstr[i-2] == '.') continue;
+ obuffer[k++] = foundstr[i];
+ }
+ obuffer[k] = 0;
+ foundstr = obuffer;
+ }
+ }
+ Dial(foundstr);
+ return;
+ }
+ case 'I': // Some strings about firmware
+ switch (ScanNumber(scanbuf)) {
+ case 3: SendLine("DOSBox Emulated Modem Firmware V1.00"); break;
+ case 4: SendLine("Modem compiled for DOSBox version " VERSION); break;
+ }
+ break;
+ case 'E': // Echo on/off
+ switch (ScanNumber(scanbuf)) {
+ case 0: echo = false; break;
+ case 1: echo = true; break;
+ }
+ break;
+ case 'V':
+ switch (ScanNumber(scanbuf)) {
+ case 0: numericresponse = true; break;
+ case 1: numericresponse = false; break;
+ }
+ break;
+ case 'H': // Hang up
+ switch (ScanNumber(scanbuf)) {
+ case 0:
+ if (connected) {
+ SendRes(ResNOCARRIER);
+ EnterIdleState();
+ return;
+ }
+ // else return ok
+ }
+ break;
+ case 'O': // Return to data mode
+ switch (ScanNumber(scanbuf)) {
+ case 0:
+ if (clientsocket) {
+ commandmode = false;
+ return;
+ } else {
+ SendRes(ResERROR);
+ return;
+ }
+ }
+ break;
+ case 'T': // Tone Dial
+ case 'P': // Pulse Dial
+ break;
+ case 'M': // Monitor
+ case 'L': // Volume
+ ScanNumber(scanbuf);
+ break;
+ case 'A': // Answer call
+ if (waitingclientsocket) {
+ AcceptIncomingCall();
+ } else {
+ SendRes(ResERROR);
+ return;
+ }
+ return;
+ case 'Z': { // Reset and load profiles
+ // scan the number away, if any
+ ScanNumber(scanbuf);
+ if (clientsocket) SendRes(ResNOCARRIER);
+ Reset();
+ break;
+ }
+ case ' ': // skip space
+ break;
+ case 'Q': {
+ // Response options
+ // 0 = all on, 1 = all off,
+ // 2 = no ring and no connect/carrier in answermode
+ Bitu val = ScanNumber(scanbuf);
+ if(!(val>2)) {
+ doresponse=val;
+ break;
+ } else {
+ SendRes(ResERROR);
+ return;
+ }
+ }
+ case 'S': { // Registers
+ Bitu index=ScanNumber(scanbuf);
+ if(index>=SREGS) {
+ SendRes(ResERROR);
+ return; //goto ret_none;
+ }
+
+ while(scanbuf[0]==' ') scanbuf++; // skip spaces
+
+ if(scanbuf[0]=='=') { // set register
+ scanbuf++;
+ while(scanbuf[0]==' ') scanbuf++; // skip spaces
+ Bitu val = ScanNumber(scanbuf);
+ reg[index]=val;
+ break;
+ }
+ else if(scanbuf[0]=='?') { // get register
+ SendNumber(reg[index]);
+ scanbuf++;
+ break;
+ }
+ //else LOG_MSG("print reg %d with %d",index,reg[index]);
+ }
+ break;
+ case '&': { // & escaped commands
+ char cmdchar = GetChar(scanbuf);
+ switch(cmdchar) {
+ case 'K': {
+ Bitu val = ScanNumber(scanbuf);
+ if(val<5) flowcontrol=val;
+ else {
+ SendRes(ResERROR);
+ return;
+ }
+ break;
+ }
+ case '\0':
+ // end of string
+ SendRes(ResERROR);
+ return;
+ default:
+ LOG_MSG("Modem: Unhandled command: &%c%d",cmdchar,ScanNumber(scanbuf));
+ break;
+ }
+ break;
+ }
+ case '\\': { // \ escaped commands
+ char cmdchar = GetChar(scanbuf);
+ switch(cmdchar) {
+ case 'N':
+ // error correction stuff - not emulated
+ if (ScanNumber(scanbuf) > 5) {
+ SendRes(ResERROR);
+ return;
+ }
+ break;
+ case '\0':
+ // end of string
+ SendRes(ResERROR);
+ return;
+ default:
+ LOG_MSG("Modem: Unhandled command: \\%c%d",cmdchar, ScanNumber(scanbuf));
+ break;
+ }
+ break;
+ }
+ case '\0':
+ SendRes(ResOK);
+ return;
+ default:
+ LOG_MSG("Modem: Unhandled command: %c%d",chr,ScanNumber(scanbuf));
+ break;
+ }
+ }
+}
+
+void CSerialModem::TelnetEmulation(Bit8u * data, Bitu size) {
+ Bitu i;
+ Bit8u c;
+ for(i=0;i<size;i++) {
+ c = data[i];
+ if(telClient.inIAC) {
+ if(telClient.recCommand) {
+ if((c != 0) && (c != 1) && (c != 3)) {
+ LOG_MSG("MODEM: Unrecognized option %d", c);
+ if(telClient.command>250) {
+ /* Reject anything we don't recognize */
+ tqueue->addb(0xff);
+ tqueue->addb(252);
+ tqueue->addb(c); /* We won't do crap! */
+ }
+ }
+ switch(telClient.command) {
+ case 251: /* Will */
+ if(c == 0) telClient.binary[TEL_SERVER] = true;
+ if(c == 1) telClient.echo[TEL_SERVER] = true;
+ if(c == 3) telClient.supressGA[TEL_SERVER] = true;
+ break;
+ case 252: /* Won't */
+ if(c == 0) telClient.binary[TEL_SERVER] = false;
+ if(c == 1) telClient.echo[TEL_SERVER] = false;
+ if(c == 3) telClient.supressGA[TEL_SERVER] = false;
+ break;
+ case 253: /* Do */
+ if(c == 0) {
+ telClient.binary[TEL_CLIENT] = true;
+ tqueue->addb(0xff);
+ tqueue->addb(251);
+ tqueue->addb(0); /* Will do binary transfer */
+ }
+ if(c == 1) {
+ telClient.echo[TEL_CLIENT] = false;
+ tqueue->addb(0xff);
+ tqueue->addb(252);
+ tqueue->addb(1); /* Won't echo (too lazy) */
+ }
+ if(c == 3) {
+ telClient.supressGA[TEL_CLIENT] = true;
+ tqueue->addb(0xff);
+ tqueue->addb(251);
+ tqueue->addb(3); /* Will Suppress GA */
+ }
+ break;
+ case 254: /* Don't */
+ if(c == 0) {
+ telClient.binary[TEL_CLIENT] = false;
+ tqueue->addb(0xff);
+ tqueue->addb(252);
+ tqueue->addb(0); /* Won't do binary transfer */
+ }
+ if(c == 1) {
+ telClient.echo[TEL_CLIENT] = false;
+ tqueue->addb(0xff);
+ tqueue->addb(252);
+ tqueue->addb(1); /* Won't echo (fine by me) */
+ }
+ if(c == 3) {
+ telClient.supressGA[TEL_CLIENT] = true;
+ tqueue->addb(0xff);
+ tqueue->addb(251);
+ tqueue->addb(3); /* Will Suppress GA (too lazy) */
+ }
+ break;
+ default:
+ LOG_MSG("MODEM: Telnet client sent IAC %d", telClient.command);
+ break;
+ }
+ telClient.inIAC = false;
+ telClient.recCommand = false;
+ continue;
+ } else {
+ if(c==249) {
+ /* Go Ahead received */
+ telClient.inIAC = false;
+ continue;
+ }
+ telClient.command = c;
+ telClient.recCommand = true;
+
+ if((telClient.binary[TEL_SERVER]) && (c == 0xff)) {
+ /* Binary data with value of 255 */
+ telClient.inIAC = false;
+ telClient.recCommand = false;
+ rqueue->addb(0xff);
+ continue;
+ }
+ }
+ } else {
+ if(c == 0xff) {
+ telClient.inIAC = true;
+ continue;
+ }
+ rqueue->addb(c);
+ }
+ }
+}
+
+void CSerialModem::Timer2(void) {
+
+ unsigned long args = 1;
+ bool sendbyte = true;
+ Bitu usesize;
+ Bit8u txval;
+ Bitu txbuffersize =0;
+
+ // Check for eventual break command
+ if (!commandmode) cmdpause++;
+ // Handle incoming data from serial port, read as much as available
+ CSerial::setCTS(true); // buffer will get 'emptier', new data can be received
+ while (tqueue->inuse()) {
+ txval = tqueue->getb();
+ if (commandmode) {
+ if (echo) {
+ rqueue->addb(txval);
+ //LOG_MSG("Echo back to queue: %x",txval);
+ }
+ if (txval==0xa) continue; //Real modem doesn't seem to skip this?
+ else if (txval==0x8 && (cmdpos > 0)) --cmdpos; // backspace
+ else if (txval==0xd) DoCommand(); // return
+ else if (txval != '+') {
+ if(cmdpos<99) {
+ cmdbuf[cmdpos] = txval;
+ cmdpos++;
+ }
+ }
+ }
+ else {// + character
+ // 1000 ticks have passed, can check for pause command
+ if (cmdpause > 1000) {
+ if(txval ==reg[MREG_ESCAPE_CHAR]) // +
+ {
+ plusinc++;
+ if(plusinc>=3) {
+ LOG_MSG("Modem: Entering command mode(escape sequence)");
+ commandmode = true;
+ SendRes(ResOK);
+ plusinc = 0;
+ }
+ sendbyte=false;
+ } else {
+ plusinc=0;
+ }
+ // If not a special pause command, should go for bigger blocks to send
+ }
+ tmpbuf[txbuffersize] = txval;
+ txbuffersize++;
+ }
+ } // while loop
+
+ if (clientsocket && sendbyte && txbuffersize) {
+ // down here it saves a lot of network traffic
+ if(!clientsocket->SendArray(tmpbuf,txbuffersize)) {
+ SendRes(ResNOCARRIER);
+ EnterIdleState();
+ }
+ }
+ // Handle incoming to the serial port
+ if(!commandmode && clientsocket && rqueue->left()) {
+ usesize = rqueue->left();
+ if (usesize>16) usesize=16;
+ if(!clientsocket->ReceiveArray(tmpbuf, &usesize)) {
+ SendRes(ResNOCARRIER);
+ EnterIdleState();
+ } else if(usesize) {
+ // Filter telnet commands
+ if(telnetmode) TelnetEmulation(tmpbuf, usesize);
+ else rqueue->adds(tmpbuf,usesize);
+ cmdpause = 0;
+ }
+ }
+ // Check for incoming calls
+ if (!connected && !waitingclientsocket && serversocket) {
+ waitingclientsocket=serversocket->Accept();
+ if(waitingclientsocket) {
+ if(!CSerial::getDTR()) {
+ // accept no calls with DTR off; TODO: AT &Dn
+ EnterIdleState();
+ } else {
+ ringing=true;
+ SendRes(ResRING);
+ CSerial::setRI(!CSerial::getRI());
+ //MIXER_Enable(mhd.chan,true);
+ ringtimer = 3000;
+ reg[1] = 0; //Reset ring counter reg
+ }
+ }
+ }
+ if (ringing) {
+ if (ringtimer <= 0) {
+ reg[1]++;
+ if ((reg[0]>0) && (reg[0]>=reg[1])) {
+ AcceptIncomingCall();
+ return;
+ }
+ SendRes(ResRING);
+ CSerial::setRI(!CSerial::getRI());
+
+ //MIXER_Enable(mhd.chan,true);
+ ringtimer = 3000;
+ }
+ --ringtimer;
+ }
+}
+
+
+//TODO
+void CSerialModem::RXBufferEmpty() {
+ // see if rqueue has some more bytes
+ if(rqueue->inuse() && (CSerial::getRTS()||(flowcontrol!=3))){
+ Bit8u rbyte = rqueue->getb();
+ //LOG_MSG("Modem: sending byte %2x back to UART1",rbyte);
+ CSerial::receiveByte(rbyte);
+ }
+}
+
+void CSerialModem::transmitByte(Bit8u val, bool first) {
+ waiting_tx_character=val;
+ setEvent(MODEM_TX_EVENT, bytetime); // TX event
+ if(first) ByteTransmitting();
+ //LOG_MSG("MODEM: Byte %x to be transmitted",val);
+}
+
+void CSerialModem::updatePortConfig(Bit16u, Bit8u lcr) {
+// nothing to do here right?
+}
+
+void CSerialModem::updateMSR() {
+ // think it is not needed
+}
+
+void CSerialModem::setBreak(bool) {
+ // TODO: handle this
+}
+
+void CSerialModem::setRTSDTR(bool rts, bool dtr) {
+ setDTR(dtr);
+}
+void CSerialModem::setRTS(bool val) {
+
+}
+void CSerialModem::setDTR(bool val) {
+ if(!val && connected) {
+ // If DTR goes low, hang up.
+ SendRes(ResNOCARRIER);
+ EnterIdleState();
+ LOG_MSG("Modem: Hang up due to dropped DTR.");
+ }
+}
+/*
+void CSerialModem::updateModemControlLines() {
+ //bool txrdy=tqueue->left();
+ //if(CSerial::getRTS() && txrdy) CSerial::setCTS(true);
+ //else CSerial::setCTS(tqueue->left());
+
+ // If DTR goes low, hang up.
+ if(connected)
+ if(oldDTRstate)
+ if(!getDTR()) {
+ SendRes(ResNOCARRIER);
+ EnterIdleState();
+ LOG_MSG("Modem: Hang up due to dropped DTR.");
+ }
+
+ oldDTRstate = getDTR();
+}
+*/
+
+#endif
+
diff --git a/src/hardware/serialport/softmodem.h b/src/hardware/serialport/softmodem.h
new file mode 100644
index 000000000..63b0a1246
--- /dev/null
+++ b/src/hardware/serialport/softmodem.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#ifndef DOSBOX_SERIALMODEM_H
+#define DOSBOX_SERIALMODEM_H
+
+#include "dosbox.h"
+#if C_MODEM
+#include "serialport.h"
+
+#include "misc_util.h"
+
+#define MODEMSPD 57600
+#define SREGS 100
+
+//If it's too high you overflow terminal clients buffers i think
+#define MODEM_BUFFER_QUEUE_SIZE 1024
+
+#define MODEM_DEFAULT_PORT 23
+
+#define MODEM_TX_EVENT SERIAL_BASE_EVENT_COUNT + 1
+#define MODEM_RX_POLLING SERIAL_BASE_EVENT_COUNT + 2
+#define MODEM_RING_EVENT SERIAL_BASE_EVENT_COUNT + 3
+#define SERIAL_MODEM_EVENT_COUNT SERIAL_BASE_EVENT_COUNT+3
+
+
+enum ResTypes {
+ ResNONE,
+ ResOK,ResERROR,
+ ResCONNECT,ResRING,
+ ResBUSY,ResNODIALTONE,ResNOCARRIER
+};
+
+#define TEL_CLIENT 0
+#define TEL_SERVER 1
+
+class CFifo {
+public:
+ CFifo(Bitu _size) {
+ size=_size;
+ pos=used=0;
+ data=new Bit8u[size];
+ }
+ ~CFifo() {
+ delete[] data;
+ }
+ INLINE Bitu left(void) {
+ return size-used;
+ }
+ INLINE Bitu inuse(void) {
+ return used;
+ }
+ void clear(void) {
+ used=pos=0;
+ }
+
+ void addb(Bit8u _val) {
+ if(used>=size) {
+ static Bits lcount=0;
+ if (lcount<1000) {
+ lcount++;
+ LOG_MSG("MODEM: FIFO Overflow! (addb)");
+ }
+ return;
+ }
+ //assert(used<size);
+ Bitu where=pos+used;
+ if (where>=size) where-=size;
+ data[where]=_val;
+ //LOG_MSG("+%x",_val);
+ used++;
+ }
+ void adds(Bit8u * _str,Bitu _len) {
+ if((used+_len)>size) {
+ static Bits lcount=0;
+ if (lcount<1000) {
+ lcount++;
+ LOG_MSG("MODEM: FIFO Overflow! (adds len %u)",_len);
+ }
+ return;
+ }
+
+ //assert((used+_len)<=size);
+ Bitu where=pos+used;
+ used+=_len;
+ while (_len--) {
+ if (where>=size) where-=size;
+ //LOG_MSG("+'%x'",*_str);
+ data[where++]=*_str++;
+ }
+ }
+ Bit8u getb(void) {
+ if (!used) {
+ static Bits lcount=0;
+ if (lcount<1000) {
+ lcount++;
+ LOG_MSG("MODEM: FIFO UNDERFLOW! (getb)");
+ }
+ return data[pos];
+ }
+ Bitu where=pos;
+ if (++pos>=size) pos-=size;
+ used--;
+ //LOG_MSG("-%x",data[where]);
+ return data[where];
+ }
+ void gets(Bit8u * _str,Bitu _len) {
+ if (!used) {
+ static Bits lcount=0;
+ if (lcount<1000) {
+ lcount++;
+ LOG_MSG("MODEM: FIFO UNDERFLOW! (gets len %d)",_len);
+ }
+ return;
+ }
+ //assert(used>=_len);
+ used-=_len;
+ while (_len--) {
+ //LOG_MSG("-%x",data[pos]);
+ *_str++=data[pos];
+ if (++pos>=size) pos-=size;
+ }
+ }
+private:
+ Bit8u * data;
+ Bitu size,pos,used;
+};
+#define MREG_AUTOANSWER_COUNT 0
+#define MREG_RING_COUNT 1
+#define MREG_ESCAPE_CHAR 2
+#define MREG_CR_CHAR 3
+#define MREG_LF_CHAR 4
+#define MREG_BACKSPACE_CHAR 5
+
+
+class CSerialModem : public CSerial {
+public:
+
+ CFifo *rqueue;
+ CFifo *tqueue;
+
+ CSerialModem(Bitu id, CommandLine* cmd);
+ ~CSerialModem();
+
+ void Reset();
+
+ void SendLine(const char *line);
+ void SendRes(ResTypes response);
+ void SendNumber(Bitu val);
+
+ void EnterIdleState();
+ void EnterConnectedState();
+
+ void openConnection(void);
+ bool Dial(char * host);
+ void AcceptIncomingCall(void);
+ Bitu ScanNumber(char * & scan);
+ char GetChar(char * & scan);
+
+ void DoCommand();
+
+ void MC_Changed(Bitu new_mc);
+
+ void TelnetEmulation(Bit8u * data, Bitu size);
+
+ //TODO
+ void Timer2(void);
+ void handleUpperEvent(Bit16u type);
+
+ void RXBufferEmpty();
+
+ void transmitByte(Bit8u val, bool first);
+ void updatePortConfig(Bit16u divider, Bit8u lcr);
+ void updateMSR();
+
+ void setBreak(bool);
+
+ void setRTSDTR(bool rts, bool dtr);
+ void setRTS(bool val);
+ void setDTR(bool val);
+
+protected:
+ char cmdbuf[255];
+ bool commandmode; // true: interpret input as commands
+ bool echo; // local echo on or off
+
+ bool oldDTRstate;
+ bool ringing;
+ //bool response;
+ bool numericresponse; // true: send control response as number.
+ // false: send text (i.e. NO DIALTONE)
+ bool telnetmode; // true: process IAC commands.
+
+ bool connected;
+ Bitu doresponse;
+
+ Bit8u waiting_tx_character;
+
+ Bitu cmdpause;
+ Bits ringtimer;
+ Bits ringcount;
+ Bitu plusinc;
+ Bitu cmdpos;
+ Bitu flowcontrol;
+
+ Bit8u tmpbuf[MODEM_BUFFER_QUEUE_SIZE];
+
+ Bitu listenport;
+ Bit8u reg[SREGS];
+
+
+ TCPServerSocket* serversocket;
+ TCPClientSocket* clientsocket;
+ TCPClientSocket* waitingclientsocket;
+
+ struct {
+ bool binary[2];
+ bool echo[2];
+ bool supressGA[2];
+ bool timingMark[2];
+
+ bool inIAC;
+ bool recCommand;
+ Bit8u command;
+ } telClient;
+ struct {
+ bool active;
+ double f1, f2;
+ Bitu len,pos;
+ char str[256];
+ } dial;
+};
+#endif
+#endif
diff --git a/src/hardware/tandy_sound.cpp b/src/hardware/tandy_sound.cpp
new file mode 100644
index 000000000..fe9101f66
--- /dev/null
+++ b/src/hardware/tandy_sound.cpp
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+/*
+ Based of sn76496.c of the M.A.M.E. project
+*/
+
+#include "dosbox.h"
+#include "inout.h"
+#include "mixer.h"
+#include "mem.h"
+#include "setup.h"
+#include "pic.h"
+#include "dma.h"
+#include "hardware.h"
+#include <cstring>
+#include <math.h>
+#include "mame/emu.h"
+#include "mame/sn76496.h"
+
+
+#define SOUND_CLOCK (14318180 / 4)
+
+#define TDAC_DMA_BUFSIZE 1024
+
+static struct {
+ MixerChannel * chan;
+ bool enabled;
+ Bitu last_write;
+ struct {
+ MixerChannel * chan;
+ bool enabled;
+ struct {
+ Bitu base;
+ Bit8u irq,dma;
+ } hw;
+ struct {
+ Bitu rate;
+ Bit8u buf[TDAC_DMA_BUFSIZE];
+ Bit8u last_sample;
+ DmaChannel * chan;
+ bool transfer_done;
+ } dma;
+ Bit8u mode,control;
+ Bit16u frequency;
+ Bit8u amplitude;
+ bool irq_activated;
+ } dac;
+} tandy;
+
+static sn76496_device device_sn76496(machine_config(), 0, 0, SOUND_CLOCK );
+static ncr8496_device device_ncr8496(machine_config(), 0, 0, SOUND_CLOCK);
+
+static sn76496_base_device* activeDevice = &device_ncr8496;
+#define device (*activeDevice)
+
+static void SN76496Write(Bitu /*port*/,Bitu data,Bitu /*iolen*/) {
+ tandy.last_write=PIC_Ticks;
+ if (!tandy.enabled) {
+ tandy.chan->Enable(true);
+ tandy.enabled=true;
+ }
+ device.write(data);
+
+// LOG_MSG("3voice write %X at time %7.3f",data,PIC_FullIndex());
+}
+
+static void SN76496Update(Bitu length) {
+ //Disable the channel if it's been quiet for a while
+ if ((tandy.last_write+5000)<PIC_Ticks) {
+ tandy.enabled=false;
+ tandy.chan->Enable(false);
+ return;
+ }
+ const Bitu MAX_SAMPLES = 2048;
+ if (length > MAX_SAMPLES)
+ return;
+ Bit16s buffer[MAX_SAMPLES];
+ Bit16s* outputs = buffer;
+
+ device_sound_interface::sound_stream stream;
+ static_cast<device_sound_interface&>(device).sound_stream_update(stream, 0, &outputs, length);
+ tandy.chan->AddSamples_m16(length, buffer);
+}
+
+bool TS_Get_Address(Bitu& tsaddr, Bitu& tsirq, Bitu& tsdma) {
+ tsaddr=0;
+ tsirq =0;
+ tsdma =0;
+ if (tandy.dac.enabled) {
+ tsaddr=tandy.dac.hw.base;
+ tsirq =tandy.dac.hw.irq;
+ tsdma =tandy.dac.hw.dma;
+ return true;
+ }
+ return false;
+}
+
+
+static void TandyDAC_DMA_CallBack(DmaChannel * /*chan*/, DMAEvent event) {
+ if (event == DMA_REACHED_TC) {
+ tandy.dac.dma.transfer_done=true;
+ PIC_ActivateIRQ(tandy.dac.hw.irq);
+ }
+}
+
+static void TandyDACModeChanged(void) {
+ switch (tandy.dac.mode&3) {
+ case 0:
+ // joystick mode
+ break;
+ case 1:
+ break;
+ case 2:
+ // recording
+ break;
+ case 3:
+ // playback
+ tandy.dac.chan->FillUp();
+ if (tandy.dac.frequency!=0) {
+ float freq=3579545.0f/((float)tandy.dac.frequency);
+ tandy.dac.chan->SetFreq((Bitu)freq);
+ float vol=((float)tandy.dac.amplitude)/7.0f;
+ tandy.dac.chan->SetVolume(vol,vol);
+ if ((tandy.dac.mode&0x0c)==0x0c) {
+ tandy.dac.dma.transfer_done=false;
+ tandy.dac.dma.chan=GetDMAChannel(tandy.dac.hw.dma);
+ if (tandy.dac.dma.chan) {
+ tandy.dac.dma.chan->Register_Callback(TandyDAC_DMA_CallBack);
+ tandy.dac.chan->Enable(true);
+// LOG_MSG("Tandy DAC: playback started with freqency %f, volume %f",freq,vol);
+ }
+ }
+ }
+ break;
+ }
+}
+
+static void TandyDACDMAEnabled(void) {
+ TandyDACModeChanged();
+}
+
+static void TandyDACDMADisabled(void) {
+}
+
+static void TandyDACWrite(Bitu port,Bitu data,Bitu /*iolen*/) {
+ switch (port) {
+ case 0xc4: {
+ Bitu oldmode = tandy.dac.mode;
+ tandy.dac.mode = (Bit8u)(data&0xff);
+ if ((data&3)!=(oldmode&3)) {
+ TandyDACModeChanged();
+ }
+ if (((data&0x0c)==0x0c) && ((oldmode&0x0c)!=0x0c)) {
+ TandyDACDMAEnabled();
+ } else if (((data&0x0c)!=0x0c) && ((oldmode&0x0c)==0x0c)) {
+ TandyDACDMADisabled();
+ }
+ }
+ break;
+ case 0xc5:
+ switch (tandy.dac.mode&3) {
+ case 0:
+ // joystick mode
+ break;
+ case 1:
+ tandy.dac.control = (Bit8u)(data&0xff);
+ break;
+ case 2:
+ break;
+ case 3:
+ // direct output
+ break;
+ }
+ break;
+ case 0xc6:
+ tandy.dac.frequency = (tandy.dac.frequency & 0xf00) | (Bit8u)(data & 0xff);
+ switch (tandy.dac.mode&3) {
+ case 0:
+ // joystick mode
+ break;
+ case 1:
+ case 2:
+ case 3:
+ TandyDACModeChanged();
+ break;
+ }
+ break;
+ case 0xc7:
+ tandy.dac.frequency = (tandy.dac.frequency & 0x00ff) | (((Bit8u)(data & 0xf)) << 8);
+ tandy.dac.amplitude = (Bit8u)(data>>5);
+ switch (tandy.dac.mode&3) {
+ case 0:
+ // joystick mode
+ break;
+ case 1:
+ case 2:
+ case 3:
+ TandyDACModeChanged();
+ break;
+ }
+ break;
+ }
+}
+
+static Bitu TandyDACRead(Bitu port,Bitu /*iolen*/) {
+ switch (port) {
+ case 0xc4:
+ return (tandy.dac.mode&0x77) | (tandy.dac.irq_activated ? 0x08 : 0x00);
+ case 0xc6:
+ return (Bit8u)(tandy.dac.frequency&0xff);
+ case 0xc7:
+ return (Bit8u)(((tandy.dac.frequency>>8)&0xf) | (tandy.dac.amplitude<<5));
+ }
+ LOG_MSG("Tandy DAC: Read from unknown %X",port);
+ return 0xff;
+}
+
+static void TandyDACGenerateDMASound(Bitu length) {
+ if (length) {
+ Bitu read=tandy.dac.dma.chan->Read(length,tandy.dac.dma.buf);
+ tandy.dac.chan->AddSamples_m8(read,tandy.dac.dma.buf);
+ if (read < length) {
+ if (read>0) tandy.dac.dma.last_sample=tandy.dac.dma.buf[read-1];
+ for (Bitu ct=read; ct < length; ct++) {
+ tandy.dac.chan->AddSamples_m8(1,&tandy.dac.dma.last_sample);
+ }
+ }
+ }
+}
+
+static void TandyDACUpdate(Bitu length) {
+ if (tandy.dac.enabled && ((tandy.dac.mode&0x0c)==0x0c)) {
+ if (!tandy.dac.dma.transfer_done) {
+ Bitu len = length;
+ TandyDACGenerateDMASound(len);
+ } else {
+ for (Bitu ct=0; ct < length; ct++) {
+ tandy.dac.chan->AddSamples_m8(1,&tandy.dac.dma.last_sample);
+ }
+ }
+ } else {
+ tandy.dac.chan->AddSilence();
+ }
+}
+
+
+class TANDYSOUND: public Module_base {
+private:
+ IO_WriteHandleObject WriteHandler[4];
+ IO_ReadHandleObject ReadHandler[4];
+ MixerObject MixerChan;
+ MixerObject MixerChanDAC;
+public:
+ TANDYSOUND(Section* configuration):Module_base(configuration){
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+
+ bool enable_hw_tandy_dac=true;
+ Bitu sbport, sbirq, sbdma;
+ if (SB_Get_Address(sbport, sbirq, sbdma)) {
+ enable_hw_tandy_dac=false;
+ }
+
+ //Select the correct tandy chip implementation
+ if (machine == MCH_PCJR) activeDevice = &device_sn76496;
+ else activeDevice = &device_ncr8496;
+
+ real_writeb(0x40,0xd4,0x00);
+ if (IS_TANDY_ARCH) {
+ /* enable tandy sound if tandy=true/auto */
+ if ((strcmp(section->Get_string("tandy"),"true")!=0) &&
+ (strcmp(section->Get_string("tandy"),"on")!=0) &&
+ (strcmp(section->Get_string("tandy"),"auto")!=0)) return;
+ } else {
+ /* only enable tandy sound if tandy=true */
+ if ((strcmp(section->Get_string("tandy"),"true")!=0) &&
+ (strcmp(section->Get_string("tandy"),"on")!=0)) return;
+
+ /* ports from second DMA controller conflict with tandy ports */
+ CloseSecondDMAController();
+
+ if (enable_hw_tandy_dac) {
+ WriteHandler[2].Install(0x1e0,SN76496Write,IO_MB,2);
+ WriteHandler[3].Install(0x1e4,TandyDACWrite,IO_MB,4);
+// ReadHandler[3].Install(0x1e4,TandyDACRead,IO_MB,4);
+ }
+ }
+
+
+ Bit32u sample_rate = section->Get_int("tandyrate");
+ tandy.chan=MixerChan.Install(&SN76496Update,sample_rate,"TANDY");
+
+ WriteHandler[0].Install(0xc0,SN76496Write,IO_MB,2);
+
+ if (enable_hw_tandy_dac) {
+ // enable low-level Tandy DAC emulation
+ WriteHandler[1].Install(0xc4,TandyDACWrite,IO_MB,4);
+ ReadHandler[1].Install(0xc4,TandyDACRead,IO_MB,4);
+
+ tandy.dac.enabled=true;
+ tandy.dac.chan=MixerChanDAC.Install(&TandyDACUpdate,sample_rate,"TANDYDAC");
+
+ tandy.dac.hw.base=0xc4;
+ tandy.dac.hw.irq =7;
+ tandy.dac.hw.dma =1;
+ } else {
+ tandy.dac.enabled=false;
+ tandy.dac.hw.base=0;
+ tandy.dac.hw.irq =0;
+ tandy.dac.hw.dma =0;
+ }
+
+ tandy.dac.control=0;
+ tandy.dac.mode =0;
+ tandy.dac.irq_activated=false;
+ tandy.dac.frequency=0;
+ tandy.dac.amplitude=0;
+ tandy.dac.dma.last_sample=0;
+
+
+ tandy.enabled=false;
+ real_writeb(0x40,0xd4,0xff); /* BIOS Tandy DAC initialization value */
+
+ ((device_t&)device).device_start();
+ device.convert_samplerate(sample_rate);
+
+ }
+ ~TANDYSOUND(){ }
+};
+
+
+
+static TANDYSOUND* test;
+
+void TANDYSOUND_ShutDown(Section* /*sec*/) {
+ delete test;
+}
+
+void TANDYSOUND_Init(Section* sec) {
+ test = new TANDYSOUND(sec);
+ sec->AddDestroyFunction(&TANDYSOUND_ShutDown,true);
+}
diff --git a/src/hardware/timer.cpp b/src/hardware/timer.cpp
new file mode 100644
index 000000000..ac07064f8
--- /dev/null
+++ b/src/hardware/timer.cpp
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <math.h>
+#include "dosbox.h"
+#include "inout.h"
+#include "pic.h"
+#include "mem.h"
+#include "mixer.h"
+#include "timer.h"
+#include "setup.h"
+
+static INLINE void BIN2BCD(Bit16u& val) {
+ Bit16u temp=val%10 + (((val/10)%10)<<4)+ (((val/100)%10)<<8) + (((val/1000)%10)<<12);
+ val=temp;
+}
+
+static INLINE void BCD2BIN(Bit16u& val) {
+ Bit16u temp= (val&0x0f) +((val>>4)&0x0f) *10 +((val>>8)&0x0f) *100 +((val>>12)&0x0f) *1000;
+ val=temp;
+}
+
+struct PIT_Block {
+ Bitu cntr;
+ float delay;
+ double start;
+
+ Bit16u read_latch;
+ Bit16u write_latch;
+
+ Bit8u mode;
+ Bit8u latch_mode;
+ Bit8u read_state;
+ Bit8u write_state;
+
+ bool bcd;
+ bool go_read_latch;
+ bool new_mode;
+ bool counterstatus_set;
+ bool counting;
+ bool update_count;
+};
+
+static PIT_Block pit[3];
+static bool gate2;
+
+static Bit8u latched_timerstatus;
+// the timer status can not be overwritten until it is read or the timer was
+// reprogrammed.
+static bool latched_timerstatus_locked;
+
+static void PIT0_Event(Bitu /*val*/) {
+ PIC_ActivateIRQ(0);
+ if (pit[0].mode != 0) {
+ pit[0].start += pit[0].delay;
+
+ if (GCC_UNLIKELY(pit[0].update_count)) {
+ pit[0].delay=(1000.0f/((float)PIT_TICK_RATE/(float)pit[0].cntr));
+ pit[0].update_count=false;
+ }
+ PIC_AddEvent(PIT0_Event,pit[0].delay);
+ }
+}
+
+static bool counter_output(Bitu counter) {
+ PIT_Block * p=&pit[counter];
+ double index=PIC_FullIndex()-p->start;
+ switch (p->mode) {
+ case 0:
+ if (p->new_mode) return false;
+ if (index>p->delay) return true;
+ else return false;
+ break;
+ case 2:
+ if (p->new_mode) return true;
+ index=fmod(index,(double)p->delay);
+ return index>0;
+ case 3:
+ if (p->new_mode) return true;
+ index=fmod(index,(double)p->delay);
+ return index*2<p->delay;
+ case 4:
+ //Only low on terminal count
+ // if(fmod(index,(double)p->delay) == 0) return false; //Maybe take one rate tick in consideration
+ //Easiest solution is to report always high (Space marines uses this mode)
+ return true;
+ default:
+ LOG(LOG_PIT,LOG_ERROR)("Illegal Mode %d for reading output",p->mode);
+ return true;
+ }
+}
+static void status_latch(Bitu counter) {
+ // the timer status can not be overwritten until it is read or the timer was
+ // reprogrammed.
+ if(!latched_timerstatus_locked) {
+ PIT_Block * p=&pit[counter];
+ latched_timerstatus=0;
+ // Timer Status Word
+ // 0: BCD
+ // 1-3: Timer mode
+ // 4-5: read/load mode
+ // 6: "NULL" - this is 0 if "the counter value is in the counter" ;)
+ // should rarely be 1 (i.e. on exotic modes)
+ // 7: OUT - the logic level on the Timer output pin
+ if(p->bcd)latched_timerstatus|=0x1;
+ latched_timerstatus|=((p->mode&7)<<1);
+ if((p->read_state==0)||(p->read_state==3)) latched_timerstatus|=0x30;
+ else if(p->read_state==1) latched_timerstatus|=0x10;
+ else if(p->read_state==2) latched_timerstatus|=0x20;
+ if(counter_output(counter)) latched_timerstatus|=0x80;
+ if(p->new_mode) latched_timerstatus|=0x40;
+ // The first thing that is being read from this counter now is the
+ // counter status.
+ p->counterstatus_set=true;
+ latched_timerstatus_locked=true;
+ }
+}
+static void counter_latch(Bitu counter) {
+ /* Fill the read_latch of the selected counter with current count */
+ PIT_Block * p=&pit[counter];
+ p->go_read_latch=false;
+
+ //If gate2 is disabled don't update the read_latch
+ if (counter == 2 && !gate2 && p->mode !=1) return;
+ if (GCC_UNLIKELY(p->new_mode)) {
+ double passed_time = PIC_FullIndex() - p->start;
+ Bitu ticks_since_then = (Bitu)(passed_time / (1000.0/PIT_TICK_RATE));
+ //if (p->mode==3) ticks_since_then /= 2; // TODO figure this out on real hardware
+ p->read_latch -= ticks_since_then;
+ return;
+ }
+ double index=PIC_FullIndex()-p->start;
+ switch (p->mode) {
+ case 4: /* Software Triggered Strobe */
+ case 0: /* Interrupt on Terminal Count */
+ /* Counter keeps on counting after passing terminal count */
+ if (index>p->delay) {
+ index-=p->delay;
+ if(p->bcd) {
+ index = fmod(index,(1000.0/PIT_TICK_RATE)*10000.0);
+ p->read_latch = (Bit16u)(9999-index*(PIT_TICK_RATE/1000.0));
+ } else {
+ index = fmod(index,(1000.0/PIT_TICK_RATE)*(double)0x10000);
+ p->read_latch = (Bit16u)(0xffff-index*(PIT_TICK_RATE/1000.0));
+ }
+ } else {
+ p->read_latch=(Bit16u)(p->cntr-index*(PIT_TICK_RATE/1000.0));
+ }
+ break;
+ case 1: // countdown
+ if(p->counting) {
+ if (index>p->delay) { // has timed out
+ p->read_latch = 0xffff; //unconfirmed
+ } else {
+ p->read_latch=(Bit16u)(p->cntr-index*(PIT_TICK_RATE/1000.0));
+ }
+ }
+ break;
+ case 2: /* Rate Generator */
+ index=fmod(index,(double)p->delay);
+ p->read_latch=(Bit16u)(p->cntr - (index/p->delay)*p->cntr);
+ break;
+ case 3: /* Square Wave Rate Generator */
+ index=fmod(index,(double)p->delay);
+ index*=2;
+ if (index>p->delay) index-=p->delay;
+ p->read_latch=(Bit16u)(p->cntr - (index/p->delay)*p->cntr);
+ // In mode 3 it never returns odd numbers LSB (if odd number is written 1 will be
+ // subtracted on first clock and then always 2)
+ // fixes "Corncob 3D"
+ p->read_latch&=0xfffe;
+ break;
+ default:
+ LOG(LOG_PIT,LOG_ERROR)("Illegal Mode %d for reading counter %d",p->mode,counter);
+ p->read_latch=0xffff;
+ break;
+ }
+}
+
+
+static void write_latch(Bitu port,Bitu val,Bitu /*iolen*/) {
+//LOG(LOG_PIT,LOG_ERROR)("port %X write:%X state:%X",port,val,pit[port-0x40].write_state);
+ Bitu counter=port-0x40;
+ PIT_Block * p=&pit[counter];
+ if(p->bcd == true) BIN2BCD(p->write_latch);
+
+ switch (p->write_state) {
+ case 0:
+ p->write_latch = p->write_latch | ((val & 0xff) << 8);
+ p->write_state = 3;
+ break;
+ case 3:
+ p->write_latch = val & 0xff;
+ p->write_state = 0;
+ break;
+ case 1:
+ p->write_latch = val & 0xff;
+ break;
+ case 2:
+ p->write_latch = (val & 0xff) << 8;
+ break;
+ }
+ if (p->bcd==true) BCD2BIN(p->write_latch);
+ if (p->write_state != 0) {
+ if (p->write_latch == 0) {
+ if (p->bcd == false) p->cntr = 0x10000;
+ else p->cntr=9999;
+ } else p->cntr = p->write_latch;
+
+ if ((!p->new_mode) && (p->mode == 2) && (counter == 0)) {
+ // In mode 2 writing another value has no direct effect on the count
+ // until the old one has run out. This might apply to other modes too.
+ // This is not fixed for PIT2 yet!!
+ p->update_count=true;
+ return;
+ }
+ p->start=PIC_FullIndex();
+ p->delay=(1000.0f/((float)PIT_TICK_RATE/(float)p->cntr));
+
+ switch (counter) {
+ case 0x00: /* Timer hooked to IRQ 0 */
+ if (p->new_mode || p->mode == 0 ) {
+ if(p->mode==0) PIC_RemoveEvents(PIT0_Event); // DoWhackaDo demo
+ PIC_AddEvent(PIT0_Event,p->delay);
+ } else LOG(LOG_PIT,LOG_NORMAL)("PIT 0 Timer set without new control word");
+ LOG(LOG_PIT,LOG_NORMAL)("PIT 0 Timer at %.4f Hz mode %d",1000.0/p->delay,p->mode);
+ break;
+ case 0x02: /* Timer hooked to PC-Speaker */
+// LOG(LOG_PIT,"PIT 2 Timer at %.3g Hz mode %d",PIT_TICK_RATE/(double)p->cntr,p->mode);
+ PCSPEAKER_SetCounter(p->cntr,p->mode);
+ break;
+ default:
+ LOG(LOG_PIT,LOG_ERROR)("PIT:Illegal timer selected for writing");
+ }
+ p->new_mode=false;
+ }
+}
+
+static Bitu read_latch(Bitu port,Bitu /*iolen*/) {
+//LOG(LOG_PIT,LOG_ERROR)("port read %X",port);
+ Bit32u counter=port-0x40;
+ Bit8u ret=0;
+ if(GCC_UNLIKELY(pit[counter].counterstatus_set)){
+ pit[counter].counterstatus_set = false;
+ latched_timerstatus_locked = false;
+ ret = latched_timerstatus;
+ } else {
+ if (pit[counter].go_read_latch == true)
+ counter_latch(counter);
+
+ if( pit[counter].bcd == true) BIN2BCD(pit[counter].read_latch);
+
+ switch (pit[counter].read_state) {
+ case 0: /* read MSB & return to state 3 */
+ ret=(pit[counter].read_latch >> 8) & 0xff;
+ pit[counter].read_state = 3;
+ pit[counter].go_read_latch = true;
+ break;
+ case 3: /* read LSB followed by MSB */
+ ret = pit[counter].read_latch & 0xff;
+ pit[counter].read_state = 0;
+ break;
+ case 1: /* read LSB */
+ ret = pit[counter].read_latch & 0xff;
+ pit[counter].go_read_latch = true;
+ break;
+ case 2: /* read MSB */
+ ret = (pit[counter].read_latch >> 8) & 0xff;
+ pit[counter].go_read_latch = true;
+ break;
+ default:
+ E_Exit("Timer.cpp: error in readlatch");
+ break;
+ }
+ if( pit[counter].bcd == true) BCD2BIN(pit[counter].read_latch);
+ }
+ return ret;
+}
+
+static void write_p43(Bitu /*port*/,Bitu val,Bitu /*iolen*/) {
+//LOG(LOG_PIT,LOG_ERROR)("port 43 %X",val);
+ Bitu latch=(val >> 6) & 0x03;
+ switch (latch) {
+ case 0:
+ case 1:
+ case 2:
+ if ((val & 0x30) == 0) {
+ /* Counter latch command */
+ counter_latch(latch);
+ } else {
+ // save output status to be used with timer 0 irq
+ bool old_output = counter_output(0);
+ // save the current count value to be re-used in undocumented newmode
+ counter_latch(latch);
+ pit[latch].bcd = (val&1)>0;
+ if (val & 1) {
+ if(pit[latch].cntr>=9999) pit[latch].cntr=9999;
+ }
+
+ // Timer is being reprogrammed, unlock the status
+ if(pit[latch].counterstatus_set) {
+ pit[latch].counterstatus_set=false;
+ latched_timerstatus_locked=false;
+ }
+ pit[latch].start = PIC_FullIndex(); // for undocumented newmode
+ pit[latch].go_read_latch = true;
+ pit[latch].update_count = false;
+ pit[latch].counting = false;
+ pit[latch].read_state = (val >> 4) & 0x03;
+ pit[latch].write_state = (val >> 4) & 0x03;
+ Bit8u mode = (val >> 1) & 0x07;
+ if (mode > 5)
+ mode -= 4; //6,7 become 2 and 3
+
+ pit[latch].mode = mode;
+
+ /* If the line goes from low to up => generate irq.
+ * ( BUT needs to stay up until acknowlegded by the cpu!!! therefore: )
+ * If the line goes to low => disable irq.
+ * Mode 0 starts with a low line. (so always disable irq)
+ * Mode 2,3 start with a high line.
+ * counter_output tells if the current counter is high or low
+ * So actually a mode 3 timer enables and disables irq al the time. (not handled) */
+
+ if (latch == 0) {
+ PIC_RemoveEvents(PIT0_Event);
+ if((mode != 0)&& !old_output) {
+ PIC_ActivateIRQ(0);
+ } else {
+ PIC_DeActivateIRQ(0);
+ }
+ } else if (latch == 2) {
+ PCSPEAKER_SetCounter(0,3);
+ }
+ pit[latch].new_mode = true;
+ }
+ break;
+ case 3:
+ if ((val & 0x20)==0) { /* Latch multiple pit counters */
+ if (val & 0x02) counter_latch(0);
+ if (val & 0x04) counter_latch(1);
+ if (val & 0x08) counter_latch(2);
+ }
+ // status and values can be latched simultaneously
+ if ((val & 0x10)==0) { /* Latch status words */
+ // but only 1 status can be latched simultaneously
+ if (val & 0x02) status_latch(0);
+ else if (val & 0x04) status_latch(1);
+ else if (val & 0x08) status_latch(2);
+ }
+ break;
+ }
+}
+
+void TIMER_SetGate2(bool in) {
+ //No changes if gate doesn't change
+ if(gate2 == in) return;
+ Bit8u & mode=pit[2].mode;
+ switch(mode) {
+ case 0:
+ if(in) pit[2].start = PIC_FullIndex();
+ else {
+ //Fill readlatch and store it.
+ counter_latch(2);
+ pit[2].cntr = pit[2].read_latch;
+ }
+ break;
+ case 1:
+ // gate 1 on: reload counter; off: nothing
+ if(in) {
+ pit[2].counting = true;
+ pit[2].start = PIC_FullIndex();
+ }
+ break;
+ case 2:
+ case 3:
+ //If gate is enabled restart counting. If disable store the current read_latch
+ if(in) pit[2].start = PIC_FullIndex();
+ else counter_latch(2);
+ break;
+ case 4:
+ case 5:
+ LOG(LOG_MISC,LOG_WARN)("unsupported gate 2 mode %x",mode);
+ break;
+ }
+ gate2 = in; //Set it here so the counter_latch above works
+}
+
+bool TIMER_GetOutput2(void) {
+ return counter_output(2);
+}
+
+class TIMER:public Module_base{
+private:
+ IO_ReadHandleObject ReadHandler[4];
+ IO_WriteHandleObject WriteHandler[4];
+public:
+ TIMER(Section* configuration):Module_base(configuration){
+ WriteHandler[0].Install(0x40,write_latch,IO_MB);
+ // WriteHandler[1].Install(0x41,write_latch,IO_MB);
+ WriteHandler[2].Install(0x42,write_latch,IO_MB);
+ WriteHandler[3].Install(0x43,write_p43,IO_MB);
+ ReadHandler[0].Install(0x40,read_latch,IO_MB);
+ ReadHandler[1].Install(0x41,read_latch,IO_MB);
+ ReadHandler[2].Install(0x42,read_latch,IO_MB);
+ /* Setup Timer 0 */
+ pit[0].cntr=0x10000;
+ pit[0].write_state = 3;
+ pit[0].read_state = 3;
+ pit[0].read_latch=0;
+ pit[0].write_latch=0;
+ pit[0].mode=3;
+ pit[0].bcd = false;
+ pit[0].go_read_latch = true;
+ pit[0].counterstatus_set = false;
+ pit[0].update_count = false;
+
+ pit[1].bcd = false;
+ pit[1].write_state = 1;
+ pit[1].read_state = 1;
+ pit[1].go_read_latch = true;
+ pit[1].cntr = 18;
+ pit[1].mode = 2;
+ pit[1].write_state = 3;
+ pit[1].counterstatus_set = false;
+
+ pit[2].read_latch=1320; /* MadTv1 */
+ pit[2].write_state = 3; /* Chuck Yeager */
+ pit[2].read_state = 3;
+ pit[2].mode=3;
+ pit[2].bcd=false;
+ pit[2].cntr=1320;
+ pit[2].go_read_latch=true;
+ pit[2].counterstatus_set = false;
+ pit[2].counting = false;
+
+ pit[0].delay=(1000.0f/((float)PIT_TICK_RATE/(float)pit[0].cntr));
+ pit[1].delay=(1000.0f/((float)PIT_TICK_RATE/(float)pit[1].cntr));
+ pit[2].delay=(1000.0f/((float)PIT_TICK_RATE/(float)pit[2].cntr));
+
+ latched_timerstatus_locked=false;
+ gate2 = false;
+ PIC_AddEvent(PIT0_Event,pit[0].delay);
+ }
+ ~TIMER(){
+ PIC_RemoveEvents(PIT0_Event);
+ }
+};
+static TIMER* test;
+
+void TIMER_Destroy(Section*){
+ delete test;
+}
+void TIMER_Init(Section* sec) {
+ test = new TIMER(sec);
+ sec->AddDestroyFunction(&TIMER_Destroy);
+}
diff --git a/src/hardware/vga.cpp b/src/hardware/vga.cpp
new file mode 100644
index 000000000..386faeed3
--- /dev/null
+++ b/src/hardware/vga.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+//#include "setup.h"
+#include "video.h"
+#include "pic.h"
+#include "vga.h"
+
+#include <string.h>
+
+VGA_Type vga;
+SVGA_Driver svga;
+
+Bit32u CGA_2_Table[16];
+Bit32u CGA_4_Table[256];
+Bit32u CGA_4_HiRes_Table[256];
+Bit32u CGA_16_Table[256];
+Bit32u TXT_Font_Table[16];
+Bit32u TXT_FG_Table[16];
+Bit32u TXT_BG_Table[16];
+Bit32u ExpandTable[256];
+Bit32u Expand16Table[4][16];
+Bit32u FillTable[16];
+Bit32u ColorTable[16];
+
+void VGA_SetModeNow(VGAModes mode) {
+ if (vga.mode == mode) return;
+ vga.mode=mode;
+ VGA_SetupHandlers();
+ VGA_StartResize(0);
+}
+
+
+void VGA_SetMode(VGAModes mode) {
+ if (vga.mode == mode) return;
+ vga.mode=mode;
+ VGA_SetupHandlers();
+ VGA_StartResize();
+}
+
+void VGA_DetermineMode(void) {
+ if (svga.determine_mode) {
+ svga.determine_mode();
+ return;
+ }
+ /* Test for VGA output active or direct color modes */
+ switch (vga.s3.misc_control_2 >> 4) {
+ case 0:
+ if (vga.attr.mode_control & 1) { // graphics mode
+ if (IS_VGA_ARCH && (vga.gfx.mode & 0x40)) {
+ // access above 256k?
+ if (vga.s3.reg_31 & 0x8) VGA_SetMode(M_LIN8);
+ else VGA_SetMode(M_VGA);
+ }
+ else if (vga.gfx.mode & 0x20) VGA_SetMode(M_CGA4);
+ else if ((vga.gfx.miscellaneous & 0x0c)==0x0c) VGA_SetMode(M_CGA2);
+ else {
+ // access above 256k?
+ if (vga.s3.reg_31 & 0x8) VGA_SetMode(M_LIN4);
+ else VGA_SetMode(M_EGA);
+ }
+ } else {
+ VGA_SetMode(M_TEXT);
+ }
+ break;
+ case 1:VGA_SetMode(M_LIN8);break;
+ case 3:VGA_SetMode(M_LIN15);break;
+ case 5:VGA_SetMode(M_LIN16);break;
+ case 13:VGA_SetMode(M_LIN32);break;
+ }
+}
+
+void VGA_StartResize(Bitu delay /*=50*/) {
+ if (!vga.draw.resizing) {
+ vga.draw.resizing=true;
+ if (vga.mode==M_ERROR) delay = 5;
+ /* Start a resize after delay (default 50 ms) */
+ if (delay==0) VGA_SetupDrawing(0);
+ else PIC_AddEvent(VGA_SetupDrawing,(float)delay);
+ }
+}
+
+void VGA_SetClock(Bitu which,Bitu target) {
+ if (svga.set_clock) {
+ svga.set_clock(which, target);
+ return;
+ }
+ struct{
+ Bitu n,m;
+ Bits err;
+ } best;
+ best.err=target;
+ best.m=1;
+ best.n=1;
+ Bitu n,r;
+ Bits m;
+
+ for (r = 0; r <= 3; r++) {
+ Bitu f_vco = target * (1 << r);
+ if (MIN_VCO <= f_vco && f_vco < MAX_VCO) break;
+ }
+ for (n=1;n<=31;n++) {
+ m=(target * (n + 2) * (1 << r) + (S3_CLOCK_REF/2)) / S3_CLOCK_REF - 2;
+ if (0 <= m && m <= 127) {
+ Bitu temp_target = S3_CLOCK(m,n,r);
+ Bits err = target - temp_target;
+ if (err < 0) err = -err;
+ if (err < best.err) {
+ best.err = err;
+ best.m = m;
+ best.n = n;
+ }
+ }
+ }
+ /* Program the s3 clock chip */
+ vga.s3.clk[which].m=best.m;
+ vga.s3.clk[which].r=r;
+ vga.s3.clk[which].n=best.n;
+ VGA_StartResize();
+}
+
+void VGA_SetCGA2Table(Bit8u val0,Bit8u val1) {
+ Bit8u total[2]={ val0,val1};
+ for (Bitu i=0;i<16;i++) {
+ CGA_2_Table[i]=
+#ifdef WORDS_BIGENDIAN
+ (total[(i >> 0) & 1] << 0 ) | (total[(i >> 1) & 1] << 8 ) |
+ (total[(i >> 2) & 1] << 16 ) | (total[(i >> 3) & 1] << 24 );
+#else
+ (total[(i >> 3) & 1] << 0 ) | (total[(i >> 2) & 1] << 8 ) |
+ (total[(i >> 1) & 1] << 16 ) | (total[(i >> 0) & 1] << 24 );
+#endif
+ }
+}
+
+void VGA_SetCGA4Table(Bit8u val0,Bit8u val1,Bit8u val2,Bit8u val3) {
+ Bit8u total[4]={ val0,val1,val2,val3};
+ for (Bitu i=0;i<256;i++) {
+ CGA_4_Table[i]=
+#ifdef WORDS_BIGENDIAN
+ (total[(i >> 0) & 3] << 0 ) | (total[(i >> 2) & 3] << 8 ) |
+ (total[(i >> 4) & 3] << 16 ) | (total[(i >> 6) & 3] << 24 );
+#else
+ (total[(i >> 6) & 3] << 0 ) | (total[(i >> 4) & 3] << 8 ) |
+ (total[(i >> 2) & 3] << 16 ) | (total[(i >> 0) & 3] << 24 );
+#endif
+ CGA_4_HiRes_Table[i]=
+#ifdef WORDS_BIGENDIAN
+ (total[((i >> 0) & 1) | ((i >> 3) & 2)] << 0 ) | (total[((i >> 1) & 1) | ((i >> 4) & 2)] << 8 ) |
+ (total[((i >> 2) & 1) | ((i >> 5) & 2)] << 16 ) | (total[((i >> 3) & 1) | ((i >> 6) & 2)] << 24 );
+#else
+ (total[((i >> 3) & 1) | ((i >> 6) & 2)] << 0 ) | (total[((i >> 2) & 1) | ((i >> 5) & 2)] << 8 ) |
+ (total[((i >> 1) & 1) | ((i >> 4) & 2)] << 16 ) | (total[((i >> 0) & 1) | ((i >> 3) & 2)] << 24 );
+#endif
+ }
+}
+
+void VGA_Init(Section* sec) {
+// Section_prop * section=static_cast<Section_prop *>(sec);
+ vga.draw.resizing=false;
+ vga.mode=M_ERROR; //For first init
+ SVGA_Setup_Driver();
+ VGA_SetupMemory(sec);
+ VGA_SetupMisc();
+ VGA_SetupDAC();
+ VGA_SetupGFX();
+ VGA_SetupSEQ();
+ VGA_SetupAttr();
+ VGA_SetupOther();
+ VGA_SetupXGA();
+ VGA_SetClock(0,CLK_25);
+ VGA_SetClock(1,CLK_28);
+/* Generate tables */
+ VGA_SetCGA2Table(0,1);
+ VGA_SetCGA4Table(0,1,2,3);
+ Bitu i,j;
+ for (i=0;i<256;i++) {
+ ExpandTable[i]=i | (i << 8)| (i <<16) | (i << 24);
+ }
+ for (i=0;i<16;i++) {
+ TXT_FG_Table[i]=i | (i << 8)| (i <<16) | (i << 24);
+ TXT_BG_Table[i]=i | (i << 8)| (i <<16) | (i << 24);
+#ifdef WORDS_BIGENDIAN
+ FillTable[i]=
+ ((i & 1) ? 0xff000000 : 0) |
+ ((i & 2) ? 0x00ff0000 : 0) |
+ ((i & 4) ? 0x0000ff00 : 0) |
+ ((i & 8) ? 0x000000ff : 0) ;
+ TXT_Font_Table[i]=
+ ((i & 1) ? 0x000000ff : 0) |
+ ((i & 2) ? 0x0000ff00 : 0) |
+ ((i & 4) ? 0x00ff0000 : 0) |
+ ((i & 8) ? 0xff000000 : 0) ;
+#else
+ FillTable[i]=
+ ((i & 1) ? 0x000000ff : 0) |
+ ((i & 2) ? 0x0000ff00 : 0) |
+ ((i & 4) ? 0x00ff0000 : 0) |
+ ((i & 8) ? 0xff000000 : 0) ;
+ TXT_Font_Table[i]=
+ ((i & 1) ? 0xff000000 : 0) |
+ ((i & 2) ? 0x00ff0000 : 0) |
+ ((i & 4) ? 0x0000ff00 : 0) |
+ ((i & 8) ? 0x000000ff : 0) ;
+#endif
+ }
+ for (j=0;j<4;j++) {
+ for (i=0;i<16;i++) {
+#ifdef WORDS_BIGENDIAN
+ Expand16Table[j][i] =
+ ((i & 1) ? 1 << j : 0) |
+ ((i & 2) ? 1 << (8 + j) : 0) |
+ ((i & 4) ? 1 << (16 + j) : 0) |
+ ((i & 8) ? 1 << (24 + j) : 0);
+#else
+ Expand16Table[j][i] =
+ ((i & 1) ? 1 << (24 + j) : 0) |
+ ((i & 2) ? 1 << (16 + j) : 0) |
+ ((i & 4) ? 1 << (8 + j) : 0) |
+ ((i & 8) ? 1 << j : 0);
+#endif
+ }
+ }
+}
+
+void SVGA_Setup_Driver(void) {
+ memset(&svga, 0, sizeof(SVGA_Driver));
+
+ switch(svgaCard) {
+ case SVGA_S3Trio:
+ SVGA_Setup_S3Trio();
+ break;
+ case SVGA_TsengET4K:
+ SVGA_Setup_TsengET4K();
+ break;
+ case SVGA_TsengET3K:
+ SVGA_Setup_TsengET3K();
+ break;
+ case SVGA_ParadisePVGA1A:
+ SVGA_Setup_ParadisePVGA1A();
+ break;
+ default:
+ vga.vmemsize = vga.vmemwrap = 256*1024;
+ break;
+ }
+}
diff --git a/src/hardware/vga_attr.cpp b/src/hardware/vga_attr.cpp
new file mode 100644
index 000000000..ea7b17cae
--- /dev/null
+++ b/src/hardware/vga_attr.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "inout.h"
+#include "vga.h"
+
+#define attr(blah) vga.attr.blah
+
+void VGA_ATTR_SetEGAMonitorPalette(EGAMonitorMode m) {
+ // palette bit assignment:
+ // bit | pin | EGA | CGA | monochrome
+ // ----+-----+------------+-----------+------------
+ // 0 | 5 | blue | blue | nc
+ // 1 | 4 | green | green* | nc
+ // 2 | 3 | red | red* | nc
+ // 3 | 7 | blue sec. | nc | video
+ // 4 | 6 | green sec. | intensity | intensity
+ // 5 | 2 | red sec. | nc | nc
+ // 6-7 | not used
+ // * additive color brown instead of yellow
+ switch(m) {
+ case CGA:
+ //LOG_MSG("Monitor CGA");
+ for (Bitu i=0;i<64;i++) {
+ vga.dac.rgb[i].red=((i & 0x4)? 0x2a:0) + ((i & 0x10)? 0x15:0);
+ vga.dac.rgb[i].blue=((i & 0x1)? 0x2a:0) + ((i & 0x10)? 0x15:0);
+
+ // replace yellow with brown
+ if ((i & 0x17) == 0x6) vga.dac.rgb[i].green = 0x15;
+ else vga.dac.rgb[i].green =
+ ((i & 0x2)? 0x2a:0) + ((i & 0x10)? 0x15:0);
+ }
+ break;
+ case EGA:
+ //LOG_MSG("Monitor EGA");
+ for (Bitu i=0;i<64;i++) {
+ vga.dac.rgb[i].red=((i & 0x4)? 0x2a:0) + ((i & 0x20)? 0x15:0);
+ vga.dac.rgb[i].green=((i & 0x2)? 0x2a:0) + ((i & 0x10)? 0x15:0);
+ vga.dac.rgb[i].blue=((i & 0x1)? 0x2a:0) + ((i & 0x8)? 0x15:0);
+ }
+ break;
+ case MONO:
+ //LOG_MSG("Monitor MONO");
+ for (Bitu i=0;i<64;i++) {
+ Bit8u value = ((i & 0x8)? 0x2a:0) + ((i & 0x10)? 0x15:0);
+ vga.dac.rgb[i].red = vga.dac.rgb[i].green =
+ vga.dac.rgb[i].blue = value;
+ }
+ break;
+ }
+
+ // update the mappings
+ for (Bit8u i=0;i<0x10;i++)
+ VGA_ATTR_SetPalette(i,vga.attr.palette[i]);
+}
+
+void VGA_ATTR_SetPalette(Bit8u index, Bit8u val) {
+ // the attribute table stores only 6 bits
+ val &= 63;
+ vga.attr.palette[index] = val;
+
+ // apply the plane mask
+ val = vga.attr.palette[index & vga.attr.color_plane_enable];
+
+ // replace bits 4-5 if configured
+ if (vga.attr.mode_control & 0x80)
+ val = (val&0xf) | (vga.attr.color_select << 4);
+
+ // set bits 6 and 7 (not relevant for EGA)
+ val |= (vga.attr.color_select & 0xc) << 4;
+
+ // apply
+ VGA_DAC_CombineColor(index,val);
+}
+
+Bitu read_p3c0(Bitu /*port*/,Bitu /*iolen*/) {
+ // Wcharts, Win 3.11 & 95 SVGA
+ Bitu retval = attr(index) & 0x1f;
+ if (!(attr(disabled) & 0x1)) retval |= 0x20;
+ return retval;
+}
+
+void write_p3c0(Bitu /*port*/,Bitu val,Bitu iolen) {
+ if (!vga.internal.attrindex) {
+ attr(index)=val & 0x1F;
+ vga.internal.attrindex=true;
+ if (val & 0x20) attr(disabled) &= ~1;
+ else attr(disabled) |= 1;
+ /*
+ 0-4 Address of data register to write to port 3C0h or read from port 3C1h
+ 5 If set screen output is enabled and the palette can not be modified,
+ if clear screen output is disabled and the palette can be modified.
+ */
+ return;
+ } else {
+ vga.internal.attrindex=false;
+ switch (attr(index)) {
+ /* Palette */
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ if (attr(disabled) & 0x1) VGA_ATTR_SetPalette(attr(index),(Bit8u)val);
+ /*
+ 0-5 Index into the 256 color DAC table. May be modified by 3C0h index
+ 10h and 14h.
+ */
+ break;
+ case 0x10: { /* Mode Control Register */
+ if (!IS_VGA_ARCH) val&=0x1f; // not really correct, but should do it
+ Bitu difference = attr(mode_control)^val;
+ attr(mode_control)=(Bit8u)val;
+
+ if (difference & 0x80) {
+ for (Bit8u i=0;i<0x10;i++)
+ VGA_ATTR_SetPalette(i,vga.attr.palette[i]);
+ }
+ if (difference & 0x08)
+ VGA_SetBlinking(val & 0x8);
+
+ if (difference & 0x41)
+ VGA_DetermineMode();
+
+ if (difference & 0x04) {
+ // recompute the panning value
+ if(vga.mode==M_TEXT) {
+ Bit8u pan_reg = attr(horizontal_pel_panning);
+ if (pan_reg > 7)
+ vga.config.pel_panning=0;
+ else if (val&0x4) // 9-dot wide characters
+ vga.config.pel_panning=(Bit8u)(pan_reg+1);
+ else // 8-dot characters
+ vga.config.pel_panning=(Bit8u)pan_reg;
+ }
+ }
+ /*
+ 0 Graphics mode if set, Alphanumeric mode else.
+ 1 Monochrome mode if set, color mode else.
+ 2 9-bit wide characters if set.
+ The 9th bit of characters C0h-DFh will be the same as
+ the 8th bit. Otherwise it will be the background color.
+ 3 If set Attribute bit 7 is blinking, else high intensity.
+ 5 If set the PEL panning register (3C0h index 13h) is temporarily set
+ to 0 from when the line compare causes a wrap around until the next
+ vertical retrace when the register is automatically reloaded with
+ the old value, else the PEL panning register ignores line compares.
+ 6 If set pixels are 8 bits wide. Used in 256 color modes.
+ 7 If set bit 4-5 of the index into the DAC table are taken from port
+ 3C0h index 14h bit 0-1, else the bits in the palette register are
+ used.
+ */
+ break;
+ }
+ case 0x11: /* Overscan Color Register */
+ attr(overscan_color)=(Bit8u)val;
+ /* 0-5 Color of screen border. Color is defined as in the palette registers. */
+ break;
+ case 0x12: /* Color Plane Enable Register */
+ /* Why disable colour planes? */
+ /* To support weird modes. */
+ if ((attr(color_plane_enable)^val) & 0xf) {
+ // in case the plane enable bits change...
+ attr(color_plane_enable)=(Bit8u)val;
+ for (Bit8u i=0;i<0x10;i++)
+ VGA_ATTR_SetPalette(i,vga.attr.palette[i]);
+ } else
+ attr(color_plane_enable)=(Bit8u)val;
+ /*
+ 0 Bit plane 0 is enabled if set.
+ 1 Bit plane 1 is enabled if set.
+ 2 Bit plane 2 is enabled if set.
+ 3 Bit plane 3 is enabled if set.
+ 4-5 Video Status MUX. Diagnostics use only.
+ Two attribute bits appear on bits 4 and 5 of the Input Status
+ Register 1 (3dAh). 0: Bit 2/0, 1: Bit 5/4, 2: bit 3/1, 3: bit 7/6
+ */
+ break;
+ case 0x13: /* Horizontal PEL Panning Register */
+ attr(horizontal_pel_panning)=val & 0xF;
+ switch (vga.mode) {
+ case M_TEXT:
+ if (val > 7)
+ vga.config.pel_panning=0;
+ else if (vga.attr.mode_control&0x4) // 9-dot wide characters
+ vga.config.pel_panning=(Bit8u)(val+1);
+ else // 8-dot characters
+ vga.config.pel_panning=(Bit8u)val;
+ break;
+ case M_VGA:
+ case M_LIN8:
+ vga.config.pel_panning=(val & 0x7)/2;
+ break;
+ case M_LIN16:
+ default:
+ vga.config.pel_panning=(val & 0x7);
+ }
+ if (machine==MCH_EGA)
+ // On the EGA panning can be programmed for every scanline:
+ vga.draw.panning = vga.config.pel_panning;
+ /*
+ 0-3 Indicates number of pixels to shift the display left
+ Value 9bit textmode 256color mode Other modes
+ 0 1 0 0
+ 1 2 n/a 1
+ 2 3 1 2
+ 3 4 n/a 3
+ 4 5 2 4
+ 5 6 n/a 5
+ 6 7 3 6
+ 7 8 n/a 7
+ 8 0 n/a n/a
+ */
+ break;
+ case 0x14: /* Color Select Register */
+ if (!IS_VGA_ARCH) {
+ attr(color_select)=0;
+ break;
+ }
+ if (attr(color_select) ^ val) {
+ attr(color_select)=(Bit8u)val;
+ for (Bit8u i=0;i<0x10;i++)
+ VGA_ATTR_SetPalette(i,vga.attr.palette[i]);
+ }
+ /*
+ 0-1 If 3C0h index 10h bit 7 is set these 2 bits are used as bits 4-5 of
+ the index into the DAC table.
+ 2-3 These 2 bits are used as bit 6-7 of the index into the DAC table
+ except in 256 color mode.
+ Note: this register does not affect 256 color modes.
+ */
+ break;
+ default:
+ if (svga.write_p3c0) {
+ svga.write_p3c0(attr(index), val, iolen);
+ break;
+ }
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:ATTR:Write to unkown Index %2X",attr(index));
+ break;
+ }
+ }
+}
+
+Bitu read_p3c1(Bitu /*port*/,Bitu iolen) {
+// vga.internal.attrindex=false;
+ switch (attr(index)) {
+ /* Palette */
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ return attr(palette[attr(index)]);
+ case 0x10: /* Mode Control Register */
+ return attr(mode_control);
+ case 0x11: /* Overscan Color Register */
+ return attr(overscan_color);
+ case 0x12: /* Color Plane Enable Register */
+ return attr(color_plane_enable);
+ case 0x13: /* Horizontal PEL Panning Register */
+ return attr(horizontal_pel_panning);
+ case 0x14: /* Color Select Register */
+ return attr(color_select);
+ default:
+ if (svga.read_p3c1)
+ return svga.read_p3c1(attr(index), iolen);
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:ATTR:Read from unkown Index %2X",attr(index));
+ }
+ return 0;
+}
+
+
+void VGA_SetupAttr(void) {
+ if (IS_EGAVGA_ARCH) {
+ IO_RegisterWriteHandler(0x3c0,write_p3c0,IO_MB);
+ if (IS_VGA_ARCH) {
+ IO_RegisterReadHandler(0x3c0,read_p3c0,IO_MB);
+ IO_RegisterReadHandler(0x3c1,read_p3c1,IO_MB);
+ }
+ }
+}
diff --git a/src/hardware/vga_crtc.cpp b/src/hardware/vga_crtc.cpp
new file mode 100644
index 000000000..541ea13f1
--- /dev/null
+++ b/src/hardware/vga_crtc.cpp
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include "dosbox.h"
+#include "inout.h"
+#include "vga.h"
+#include "debug.h"
+#include "cpu.h"
+#include "video.h"
+#include "pic.h"
+
+#define crtc(blah) vga.crtc.blah
+
+
+void VGA_MapMMIO(void);
+void VGA_UnmapMMIO(void);
+
+void vga_write_p3d5(Bitu port,Bitu val,Bitu iolen);
+Bitu DEBUG_EnableDebugger(void);
+
+void vga_write_p3d4(Bitu port,Bitu val,Bitu iolen) {
+ crtc(index)=val;
+}
+
+Bitu vga_read_p3d4(Bitu port,Bitu iolen) {
+ return crtc(index);
+}
+
+void vga_write_p3d5(Bitu port,Bitu val,Bitu iolen) {
+// if (crtc(index)>0x18) LOG_MSG("VGA CRCT write %X to reg %X",val,crtc(index));
+ switch(crtc(index)) {
+ case 0x00: /* Horizontal Total Register */
+ if (crtc(read_only)) break;
+ crtc(horizontal_total)=val;
+ /* 0-7 Horizontal Total Character Clocks-5 */
+ break;
+ case 0x01: /* Horizontal Display End Register */
+ if (crtc(read_only)) break;
+ if (val != crtc(horizontal_display_end)) {
+ crtc(horizontal_display_end)=val;
+ VGA_StartResize();
+ }
+ /* 0-7 Number of Character Clocks Displayed -1 */
+ break;
+ case 0x02: /* Start Horizontal Blanking Register */
+ if (crtc(read_only)) break;
+ crtc(start_horizontal_blanking)=val;
+ /* 0-7 The count at which Horizontal Blanking starts */
+ break;
+ case 0x03: /* End Horizontal Blanking Register */
+ if (crtc(read_only)) break;
+ crtc(end_horizontal_blanking)=val;
+ /*
+ 0-4 Horizontal Blanking ends when the last 6 bits of the character
+ counter equals this field. Bit 5 is at 3d4h index 5 bit 7.
+ 5-6 Number of character clocks to delay start of display after Horizontal
+ Total has been reached.
+ 7 Access to Vertical Retrace registers if set. If clear reads to 3d4h
+ index 10h and 11h access the Lightpen read back registers ??
+ */
+ break;
+ case 0x04: /* Start Horizontal Retrace Register */
+ if (crtc(read_only)) break;
+ crtc(start_horizontal_retrace)=val;
+ /* 0-7 Horizontal Retrace starts when the Character Counter reaches this value. */
+ break;
+ case 0x05: /* End Horizontal Retrace Register */
+ if (crtc(read_only)) break;
+ crtc(end_horizontal_retrace)=val;
+ /*
+ 0-4 Horizontal Retrace ends when the last 5 bits of the character counter
+ equals this value.
+ 5-6 Number of character clocks to delay start of display after Horizontal
+ Retrace.
+ 7 bit 5 of the End Horizontal Blanking count (See 3d4h index 3 bit 0-4)
+ */
+ break;
+ case 0x06: /* Vertical Total Register */
+ if (crtc(read_only)) break;
+ if (val != crtc(vertical_total)) {
+ crtc(vertical_total)=val;
+ VGA_StartResize();
+ }
+ /* 0-7 Lower 8 bits of the Vertical Total. Bit 8 is found in 3d4h index 7
+ bit 0. Bit 9 is found in 3d4h index 7 bit 5.
+ Note: For the VGA this value is the number of scan lines in the display -2.
+ */
+ break;
+ case 0x07: /* Overflow Register */
+ //Line compare bit ignores read only */
+ vga.config.line_compare=(vga.config.line_compare & 0x6ff) | (val & 0x10) << 4;
+ if (crtc(read_only)) break;
+ if ((vga.crtc.overflow ^ val) & 0xd6) {
+ crtc(overflow)=val;
+ VGA_StartResize();
+ } else crtc(overflow)=val;
+ /*
+ 0 Bit 8 of Vertical Total (3d4h index 6)
+ 1 Bit 8 of Vertical Display End (3d4h index 12h)
+ 2 Bit 8 of Vertical Retrace Start (3d4h index 10h)
+ 3 Bit 8 of Start Vertical Blanking (3d4h index 15h)
+ 4 Bit 8 of Line Compare Register (3d4h index 18h)
+ 5 Bit 9 of Vertical Total (3d4h index 6)
+ 6 Bit 9 of Vertical Display End (3d4h index 12h)
+ 7 Bit 9 of Vertical Retrace Start (3d4h index 10h)
+ */
+ break;
+ case 0x08: /* Preset Row Scan Register */
+ crtc(preset_row_scan)=val;
+ vga.config.hlines_skip=val&31;
+ if (IS_VGA_ARCH) vga.config.bytes_skip=(val>>5)&3;
+ else vga.config.bytes_skip=0;
+// LOG_DEBUG("Skip lines %d bytes %d",vga.config.hlines_skip,vga.config.bytes_skip);
+ /*
+ 0-4 Number of lines we have scrolled down in the first character row.
+ Provides Smooth Vertical Scrolling.b
+ 5-6 Number of bytes to skip at the start of scanline. Provides Smooth
+ Horizontal Scrolling together with the Horizontal Panning Register
+ (3C0h index 13h).
+ */
+ break;
+ case 0x09: /* Maximum Scan Line Register */
+ if (IS_VGA_ARCH)
+ vga.config.line_compare=(vga.config.line_compare & 0x5ff)|(val&0x40)<<3;
+
+ if (IS_VGA_ARCH && (svgaCard==SVGA_None) && (vga.mode==M_EGA || vga.mode==M_VGA)) {
+ // in vgaonly mode we take special care of line repeats (excluding CGA modes)
+ if ((vga.crtc.maximum_scan_line ^ val) & 0x20) {
+ crtc(maximum_scan_line)=val;
+ VGA_StartResize();
+ } else {
+ crtc(maximum_scan_line)=val;
+ }
+ vga.draw.address_line_total = (val &0x1F) + 1;
+ if (val&0x80) vga.draw.address_line_total*=2;
+ } else {
+ if ((vga.crtc.maximum_scan_line ^ val) & 0xbf) {
+ crtc(maximum_scan_line)=val;
+ VGA_StartResize();
+ } else {
+ crtc(maximum_scan_line)=val;
+ }
+ }
+ /*
+ 0-4 Number of scan lines in a character row -1. In graphics modes this is
+ the number of times (-1) the line is displayed before passing on to
+ the next line (0: normal, 1: double, 2: triple...).
+ This is independent of bit 7, except in CGA modes which seems to
+ require this field to be 1 and bit 7 to be set to work.
+ 5 Bit 9 of Start Vertical Blanking
+ 6 Bit 9 of Line Compare Register
+ 7 Doubles each scan line if set. I.e. displays 200 lines on a 400 display.
+ */
+ break;
+ case 0x0A: /* Cursor Start Register */
+ crtc(cursor_start)=val;
+ vga.draw.cursor.sline=val&0x1f;
+ if (IS_VGA_ARCH) vga.draw.cursor.enabled=!(val&0x20);
+ else vga.draw.cursor.enabled=true;
+ /*
+ 0-4 First scanline of cursor within character.
+ 5 Turns Cursor off if set
+ */
+ break;
+ case 0x0B: /* Cursor End Register */
+ crtc(cursor_end)=val;
+ vga.draw.cursor.eline=val&0x1f;
+ vga.draw.cursor.delay=(val>>5)&0x3;
+
+ /*
+ 0-4 Last scanline of cursor within character
+ 5-6 Delay of cursor data in character clocks.
+ */
+ break;
+ case 0x0C: /* Start Address High Register */
+ crtc(start_address_high)=val;
+ vga.config.display_start=(vga.config.display_start & 0xFF00FF)| (val << 8);
+ /* 0-7 Upper 8 bits of the start address of the display buffer */
+ break;
+ case 0x0D: /* Start Address Low Register */
+ crtc(start_address_low)=val;
+ vga.config.display_start=(vga.config.display_start & 0xFFFF00)| val;
+ /* 0-7 Lower 8 bits of the start address of the display buffer */
+ break;
+ case 0x0E: /*Cursor Location High Register */
+ crtc(cursor_location_high)=val;
+ vga.config.cursor_start&=0xff00ff;
+ vga.config.cursor_start|=val << 8;
+ /* 0-7 Upper 8 bits of the address of the cursor */
+ break;
+ case 0x0F: /* Cursor Location Low Register */
+//TODO update cursor on screen
+ crtc(cursor_location_low)=val;
+ vga.config.cursor_start&=0xffff00;
+ vga.config.cursor_start|=val;
+ /* 0-7 Lower 8 bits of the address of the cursor */
+ break;
+ case 0x10: /* Vertical Retrace Start Register */
+ crtc(vertical_retrace_start)=val;
+ /*
+ 0-7 Lower 8 bits of Vertical Retrace Start. Vertical Retrace starts when
+ the line counter reaches this value. Bit 8 is found in 3d4h index 7
+ bit 2. Bit 9 is found in 3d4h index 7 bit 7.
+ */
+ break;
+ case 0x11: /* Vertical Retrace End Register */
+ crtc(vertical_retrace_end)=val;
+
+ if (IS_EGAVGA_ARCH && !(val & 0x10)) {
+ vga.draw.vret_triggered=false;
+ if (GCC_UNLIKELY(machine==MCH_EGA)) PIC_DeActivateIRQ(9);
+ }
+ if (IS_VGA_ARCH) crtc(read_only)=(val & 128)>0;
+ else crtc(read_only)=false;
+ /*
+ 0-3 Vertical Retrace ends when the last 4 bits of the line counter equals
+ this value.
+ 4 if clear Clears pending Vertical Interrupts.
+ 5 Vertical Interrupts (IRQ 2) disabled if set. Can usually be left
+ disabled, but some systems (including PS/2) require it to be enabled.
+ 6 If set selects 5 refresh cycles per scanline rather than 3.
+ 7 Disables writing to registers 0-7 if set 3d4h index 7 bit 4 is not
+ affected by this bit.
+ */
+ break;
+ case 0x12: /* Vertical Display End Register */
+ if (val!=crtc(vertical_display_end)) {
+ if (abs(static_cast<int>((Bits)val-(Bits)crtc(vertical_display_end)))<3) {
+ // delay small vde changes a bit to avoid screen resizing
+ // if they are reverted in a short timeframe
+ PIC_RemoveEvents(VGA_SetupDrawing);
+ vga.draw.resizing=false;
+ crtc(vertical_display_end)=val;
+ VGA_StartResize(150);
+ } else {
+ crtc(vertical_display_end)=val;
+ VGA_StartResize();
+ }
+ }
+ /*
+ 0-7 Lower 8 bits of Vertical Display End. The display ends when the line
+ counter reaches this value. Bit 8 is found in 3d4h index 7 bit 1.
+ Bit 9 is found in 3d4h index 7 bit 6.
+ */
+ break;
+ case 0x13: /* Offset register */
+ crtc(offset)=val;
+ vga.config.scan_len&=0x300;
+ vga.config.scan_len|=val;
+ VGA_CheckScanLength();
+ /*
+ 0-7 Number of bytes in a scanline / K. Where K is 2 for byte mode, 4 for
+ word mode and 8 for Double Word mode.
+ */
+ break;
+ case 0x14: /* Underline Location Register */
+ crtc(underline_location)=val;
+ if (IS_VGA_ARCH) {
+ //Byte,word,dword mode
+ if ( crtc(underline_location) & 0x20 )
+ vga.config.addr_shift = 2;
+ else if ( crtc( mode_control) & 0x40 )
+ vga.config.addr_shift = 0;
+ else
+ vga.config.addr_shift = 1;
+ } else {
+ vga.config.addr_shift = 1;
+ }
+ /*
+ 0-4 Position of underline within Character cell.
+ 5 If set memory address is only changed every fourth character clock.
+ 6 Double Word mode addressing if set
+ */
+ break;
+ case 0x15: /* Start Vertical Blank Register */
+ if (val!=crtc(start_vertical_blanking)) {
+ crtc(start_vertical_blanking)=val;
+ VGA_StartResize();
+ }
+ /*
+ 0-7 Lower 8 bits of Vertical Blank Start. Vertical blanking starts when
+ the line counter reaches this value. Bit 8 is found in 3d4h index 7
+ bit 3.
+ */
+ break;
+ case 0x16: /* End Vertical Blank Register */
+ if (val!=crtc(end_vertical_blanking)) {
+ crtc(end_vertical_blanking)=val;
+ VGA_StartResize();
+ }
+ /*
+ 0-6 Vertical blanking stops when the lower 7 bits of the line counter
+ equals this field. Some SVGA chips uses all 8 bits!
+ IBM actually says bits 0-7.
+ */
+ break;
+ case 0x17: /* Mode Control Register */
+ crtc(mode_control)=val;
+ vga.tandy.line_mask = (~val) & 3;
+ //Byte,word,dword mode
+ if ( crtc(underline_location) & 0x20 )
+ vga.config.addr_shift = 2;
+ else if ( crtc( mode_control) & 0x40 )
+ vga.config.addr_shift = 0;
+ else
+ vga.config.addr_shift = 1;
+
+ if ( vga.tandy.line_mask ) {
+ vga.tandy.line_shift = 13;
+ vga.tandy.addr_mask = (1 << 13) - 1;
+ } else {
+ vga.tandy.addr_mask = ~0;
+ vga.tandy.line_shift = 0;
+ }
+ //Should we really need to do a determinemode here?
+// VGA_DetermineMode();
+ /*
+ 0 If clear use CGA compatible memory addressing system
+ by substituting character row scan counter bit 0 for address bit 13,
+ thus creating 2 banks for even and odd scan lines.
+ 1 If clear use Hercules compatible memory addressing system by
+ substituting character row scan counter bit 1 for address bit 14,
+ thus creating 4 banks.
+ 2 If set increase scan line counter only every second line.
+ 3 If set increase memory address counter only every other character clock.
+ 5 When in Word Mode bit 15 is rotated to bit 0 if this bit is set else
+ bit 13 is rotated into bit 0.
+ 6 If clear system is in word mode. Addresses are rotated 1 position up
+ bringing either bit 13 or 15 into bit 0.
+ 7 Clearing this bit will reset the display system until the bit is set again.
+ */
+ break;
+ case 0x18: /* Line Compare Register */
+ crtc(line_compare)=val;
+ vga.config.line_compare=(vga.config.line_compare & 0x700) | val;
+ /*
+ 0-7 Lower 8 bits of the Line Compare. When the Line counter reaches this
+ value, the display address wraps to 0. Provides Split Screen
+ facilities. Bit 8 is found in 3d4h index 7 bit 4.
+ Bit 9 is found in 3d4h index 9 bit 6.
+ */
+ break;
+ default:
+ if (svga.write_p3d5) {
+ svga.write_p3d5(crtc(index), val, iolen);
+ } else {
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:CRTC:Write to unknown index %X",crtc(index));
+ }
+ break;
+ }
+}
+
+Bitu vga_read_p3d5(Bitu port,Bitu iolen) {
+// LOG_MSG("VGA CRCT read from reg %X",crtc(index));
+ switch(crtc(index)) {
+ case 0x00: /* Horizontal Total Register */
+ return crtc(horizontal_total);
+ case 0x01: /* Horizontal Display End Register */
+ return crtc(horizontal_display_end);
+ case 0x02: /* Start Horizontal Blanking Register */
+ return crtc(start_horizontal_blanking);
+ case 0x03: /* End Horizontal Blanking Register */
+ return crtc(end_horizontal_blanking);
+ case 0x04: /* Start Horizontal Retrace Register */
+ return crtc(start_horizontal_retrace);
+ case 0x05: /* End Horizontal Retrace Register */
+ return crtc(end_horizontal_retrace);
+ case 0x06: /* Vertical Total Register */
+ return crtc(vertical_total);
+ case 0x07: /* Overflow Register */
+ return crtc(overflow);
+ case 0x08: /* Preset Row Scan Register */
+ return crtc(preset_row_scan);
+ case 0x09: /* Maximum Scan Line Register */
+ return crtc(maximum_scan_line);
+ case 0x0A: /* Cursor Start Register */
+ return crtc(cursor_start);
+ case 0x0B: /* Cursor End Register */
+ return crtc(cursor_end);
+ case 0x0C: /* Start Address High Register */
+ return crtc(start_address_high);
+ case 0x0D: /* Start Address Low Register */
+ return crtc(start_address_low);
+ case 0x0E: /*Cursor Location High Register */
+ return crtc(cursor_location_high);
+ case 0x0F: /* Cursor Location Low Register */
+ return crtc(cursor_location_low);
+ case 0x10: /* Vertical Retrace Start Register */
+ return crtc(vertical_retrace_start);
+ case 0x11: /* Vertical Retrace End Register */
+ return crtc(vertical_retrace_end);
+ case 0x12: /* Vertical Display End Register */
+ return crtc(vertical_display_end);
+ case 0x13: /* Offset register */
+ return crtc(offset);
+ case 0x14: /* Underline Location Register */
+ return crtc(underline_location);
+ case 0x15: /* Start Vertical Blank Register */
+ return crtc(start_vertical_blanking);
+ case 0x16: /* End Vertical Blank Register */
+ return crtc(end_vertical_blanking);
+ case 0x17: /* Mode Control Register */
+ return crtc(mode_control);
+ case 0x18: /* Line Compare Register */
+ return crtc(line_compare);
+ default:
+ if (svga.read_p3d5) {
+ return svga.read_p3d5(crtc(index), iolen);
+ } else {
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:CRTC:Read from unknown index %X",crtc(index));
+ return 0x0;
+ }
+ }
+}
+
+
+
+
diff --git a/src/hardware/vga_dac.cpp b/src/hardware/vga_dac.cpp
new file mode 100644
index 000000000..80f6f361a
--- /dev/null
+++ b/src/hardware/vga_dac.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include "dosbox.h"
+#include "inout.h"
+#include "render.h"
+#include "vga.h"
+
+/*
+3C6h (R/W): PEL Mask
+bit 0-7 This register is anded with the palette index sent for each dot.
+ Should be set to FFh.
+
+3C7h (R): DAC State Register
+bit 0-1 0 indicates the DAC is in Write Mode and 3 indicates Read mode.
+
+3C7h (W): PEL Address Read Mode
+bit 0-7 The PEL data register (0..255) to be read from 3C9h.
+Note: After reading the 3 bytes at 3C9h this register will increment,
+ pointing to the next data register.
+
+3C8h (R/W): PEL Address Write Mode
+bit 0-7 The PEL data register (0..255) to be written to 3C9h.
+Note: After writing the 3 bytes at 3C9h this register will increment, pointing
+ to the next data register.
+
+3C9h (R/W): PEL Data Register
+bit 0-5 Color value
+Note: Each read or write of this register will cycle through first the
+ registers for Red, Blue and Green, then increment the appropriate
+ address register, thus the entire palette can be loaded by writing 0 to
+ the PEL Address Write Mode register 3C8h and then writing all 768 bytes
+ of the palette to this register.
+*/
+
+enum {DAC_READ,DAC_WRITE};
+
+static void VGA_DAC_SendColor( Bitu index, Bitu src ) {
+ const Bit8u red = vga.dac.rgb[src].red;
+ const Bit8u green = vga.dac.rgb[src].green;
+ const Bit8u blue = vga.dac.rgb[src].blue;
+ //Set entry in 16bit output lookup table
+ vga.dac.xlat16[index] = ((blue>>1)&0x1f) | (((green)&0x3f)<<5) | (((red>>1)&0x1f) << 11);
+
+ RENDER_SetPal( index, (red << 2) | ( red >> 4 ), (green << 2) | ( green >> 4 ), (blue << 2) | ( blue >> 4 ) );
+}
+
+static void VGA_DAC_UpdateColor( Bitu index ) {
+ Bitu maskIndex = index & vga.dac.pel_mask;
+ VGA_DAC_SendColor( index, maskIndex );
+}
+
+static void write_p3c6(Bitu port,Bitu val,Bitu iolen) {
+ if ( vga.dac.pel_mask != val ) {
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:DCA:Pel Mask set to %X", val);
+ vga.dac.pel_mask = val;
+ for ( Bitu i = 0;i<256;i++)
+ VGA_DAC_UpdateColor( i );
+ }
+}
+
+
+static Bitu read_p3c6(Bitu port,Bitu iolen) {
+ return vga.dac.pel_mask;
+}
+
+
+static void write_p3c7(Bitu port,Bitu val,Bitu iolen) {
+ vga.dac.read_index=val;
+ vga.dac.pel_index=0;
+ vga.dac.state=DAC_READ;
+ vga.dac.write_index= val + 1;
+}
+
+static Bitu read_p3c7(Bitu port,Bitu iolen) {
+ if (vga.dac.state==DAC_READ) return 0x3;
+ else return 0x0;
+}
+
+static void write_p3c8(Bitu port,Bitu val,Bitu iolen) {
+ vga.dac.write_index=val;
+ vga.dac.pel_index=0;
+ vga.dac.state=DAC_WRITE;
+ vga.dac.read_index= val - 1;
+}
+
+static Bitu read_p3c8(Bitu port, Bitu iolen){
+ return vga.dac.write_index;
+}
+
+static void write_p3c9(Bitu port,Bitu val,Bitu iolen) {
+ val&=0x3f;
+ switch (vga.dac.pel_index) {
+ case 0:
+ vga.dac.rgb[vga.dac.write_index].red=val;
+ vga.dac.pel_index=1;
+ break;
+ case 1:
+ vga.dac.rgb[vga.dac.write_index].green=val;
+ vga.dac.pel_index=2;
+ break;
+ case 2:
+ vga.dac.rgb[vga.dac.write_index].blue=val;
+ switch (vga.mode) {
+ case M_VGA:
+ case M_LIN8:
+ VGA_DAC_UpdateColor( vga.dac.write_index );
+ if ( GCC_UNLIKELY( vga.dac.pel_mask != 0xff)) {
+ Bitu index = vga.dac.write_index;
+ if ( (index & vga.dac.pel_mask) == index ) {
+ for ( Bitu i = index+1;i<256;i++)
+ if ( (i & vga.dac.pel_mask) == index )
+ VGA_DAC_UpdateColor( i );
+ }
+ }
+ break;
+ default:
+ /* Check for attributes and DAC entry link */
+ for (Bitu i=0;i<16;i++) {
+ if (vga.dac.combine[i]==vga.dac.write_index) {
+ VGA_DAC_SendColor( i, vga.dac.write_index );
+ }
+ }
+ }
+ vga.dac.write_index++;
+// vga.dac.read_index = vga.dac.write_index - 1;//disabled as it breaks Wari
+ vga.dac.pel_index=0;
+ break;
+ default:
+ LOG(LOG_VGAGFX,LOG_NORMAL)("VGA:DAC:Illegal Pel Index"); //If this can actually happen that will be the day
+ break;
+ };
+}
+
+static Bitu read_p3c9(Bitu port,Bitu iolen) {
+ Bit8u ret;
+ switch (vga.dac.pel_index) {
+ case 0:
+ ret=vga.dac.rgb[vga.dac.read_index].red;
+ vga.dac.pel_index=1;
+ break;
+ case 1:
+ ret=vga.dac.rgb[vga.dac.read_index].green;
+ vga.dac.pel_index=2;
+ break;
+ case 2:
+ ret=vga.dac.rgb[vga.dac.read_index].blue;
+ vga.dac.read_index++;
+ vga.dac.pel_index=0;
+// vga.dac.write_index=vga.dac.read_index+1;//disabled as it breaks wari
+ break;
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:DAC:Illegal Pel Index"); //If this can actually happen that will be the day
+ ret=0;
+ break;
+ }
+ return ret;
+}
+
+void VGA_DAC_CombineColor(Bit8u attr,Bit8u pal) {
+ /* Check if this is a new color */
+ vga.dac.combine[attr]=pal;
+ switch (vga.mode) {
+ case M_LIN8:
+ break;
+ case M_VGA:
+ // used by copper demo; almost no video card seems to suport it
+ if(!IS_VGA_ARCH || (svgaCard!=SVGA_None)) break;
+ default:
+ VGA_DAC_SendColor( attr, pal );
+ }
+}
+
+void VGA_DAC_SetEntry(Bitu entry,Bit8u red,Bit8u green,Bit8u blue) {
+ //Should only be called in machine != vga
+ vga.dac.rgb[entry].red=red;
+ vga.dac.rgb[entry].green=green;
+ vga.dac.rgb[entry].blue=blue;
+ for (Bitu i=0;i<16;i++)
+ if (vga.dac.combine[i]==entry)
+ VGA_DAC_SendColor( i, i );
+}
+
+void VGA_SetupDAC(void) {
+ vga.dac.first_changed=256;
+ vga.dac.bits=6;
+ vga.dac.pel_mask=0xff;
+ vga.dac.pel_index=0;
+ vga.dac.state=DAC_READ;
+ vga.dac.read_index=0;
+ vga.dac.write_index=0;
+ if (IS_VGA_ARCH) {
+ /* Setup the DAC IO port Handlers */
+ IO_RegisterWriteHandler(0x3c6,write_p3c6,IO_MB);
+ IO_RegisterReadHandler(0x3c6,read_p3c6,IO_MB);
+ IO_RegisterWriteHandler(0x3c7,write_p3c7,IO_MB);
+ IO_RegisterReadHandler(0x3c7,read_p3c7,IO_MB);
+ IO_RegisterWriteHandler(0x3c8,write_p3c8,IO_MB);
+ IO_RegisterReadHandler(0x3c8,read_p3c8,IO_MB);
+ IO_RegisterWriteHandler(0x3c9,write_p3c9,IO_MB);
+ IO_RegisterReadHandler(0x3c9,read_p3c9,IO_MB);
+ }
+}
diff --git a/src/hardware/vga_draw.cpp b/src/hardware/vga_draw.cpp
new file mode 100644
index 000000000..80217750a
--- /dev/null
+++ b/src/hardware/vga_draw.cpp
@@ -0,0 +1,1622 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <math.h>
+#include "dosbox.h"
+#include "video.h"
+#include "render.h"
+#include "../gui/render_scalers.h"
+#include "vga.h"
+#include "pic.h"
+
+//#undef C_DEBUG
+//#define C_DEBUG 1
+//#define LOG(X,Y) LOG_MSG
+
+#define VGA_PARTS 4
+
+typedef Bit8u * (* VGA_Line_Handler)(Bitu vidstart, Bitu line);
+
+static VGA_Line_Handler VGA_DrawLine;
+static Bit8u TempLine[SCALER_MAXWIDTH * 4];
+
+static Bit8u * VGA_Draw_1BPP_Line(Bitu vidstart, Bitu line) {
+ const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
+ Bit32u *draw = (Bit32u *)TempLine;
+ for (Bitu x=vga.draw.blocks;x>0;x--, vidstart++) {
+ Bitu val = base[(vidstart & (8 * 1024 -1))];
+ *draw++=CGA_2_Table[val >> 4];
+ *draw++=CGA_2_Table[val & 0xf];
+ }
+ return TempLine;
+}
+
+static Bit8u * VGA_Draw_2BPP_Line(Bitu vidstart, Bitu line) {
+ const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
+ Bit32u * draw=(Bit32u *)TempLine;
+ for (Bitu x=0;x<vga.draw.blocks;x++) {
+ Bitu val = base[vidstart & vga.tandy.addr_mask];
+ vidstart++;
+ *draw++=CGA_4_Table[val];
+ }
+ return TempLine;
+}
+
+static Bit8u * VGA_Draw_2BPPHiRes_Line(Bitu vidstart, Bitu line) {
+ const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
+ Bit32u * draw=(Bit32u *)TempLine;
+ for (Bitu x=0;x<vga.draw.blocks;x++) {
+ Bitu val1 = base[vidstart & vga.tandy.addr_mask];
+ ++vidstart;
+ Bitu val2 = base[vidstart & vga.tandy.addr_mask];
+ ++vidstart;
+ *draw++=CGA_4_HiRes_Table[(val1>>4)|(val2&0xf0)];
+ *draw++=CGA_4_HiRes_Table[(val1&0x0f)|((val2&0x0f)<<4)];
+ }
+ return TempLine;
+}
+
+static Bitu temp[643]={0};
+
+static Bit8u * VGA_Draw_CGA16_Line(Bitu vidstart, Bitu line) {
+ const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
+#define CGA16_READER(OFF) (base[(vidstart +(OFF))& (8*1024 -1)])
+ Bit32u * draw=(Bit32u *)TempLine;
+ //There are 640 hdots in each line of the screen.
+ //The color of an even hdot always depends on only 4 bits of video RAM.
+ //The color of an odd hdot depends on 4 bits of video RAM in
+ //1-hdot-per-pixel modes and 6 bits of video RAM in 2-hdot-per-pixel
+ //modes. We always assume 6 and use duplicate palette entries in
+ //1-hdot-per-pixel modes so that we can use the same routine for all
+ //composite modes.
+ temp[1] = (CGA16_READER(0) >> 6) & 3;
+ for(Bitu x = 2; x < 640; x+=2) {
+ temp[x] = (temp[x-1] & 0xf);
+ temp[x+1] = (temp[x] << 2) | ((( CGA16_READER(x>>3)) >> (6-(x&6)) )&3);
+ }
+ temp[640] = temp[639] & 0xf;
+ temp[641] = temp[640] << 2;
+ temp[642] = temp[641] & 0xf;
+
+ Bitu i = 2;
+ for (Bitu x=0;x<vga.draw.blocks;x++) {
+ *draw++ = 0xc0708030 | temp[i] | (temp[i+1] << 8) | (temp[i+2] << 16) | (temp[i+3] << 24);
+ i += 4;
+ *draw++ = 0xc0708030 | temp[i] | (temp[i+1] << 8) | (temp[i+2] << 16) | (temp[i+3] << 24);
+ i += 4;
+ }
+ return TempLine;
+#undef CGA16_READER
+}
+
+static Bit8u * VGA_Draw_4BPP_Line(Bitu vidstart, Bitu line) {
+ const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
+ Bit8u* draw=TempLine;
+ Bitu end = vga.draw.blocks*2;
+ while(end) {
+ Bit8u byte = base[vidstart & vga.tandy.addr_mask];
+ *draw++=vga.attr.palette[byte >> 4];
+ *draw++=vga.attr.palette[byte & 0x0f];
+ vidstart++;
+ end--;
+ }
+ return TempLine;
+}
+
+static Bit8u * VGA_Draw_4BPP_Line_Double(Bitu vidstart, Bitu line) {
+ const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
+ Bit8u* draw=TempLine;
+ Bitu end = vga.draw.blocks;
+ while(end) {
+ Bit8u byte = base[vidstart & vga.tandy.addr_mask];
+ Bit8u data = vga.attr.palette[byte >> 4];
+ *draw++ = data; *draw++ = data;
+ data = vga.attr.palette[byte & 0x0f];
+ *draw++ = data; *draw++ = data;
+ vidstart++;
+ end--;
+ }
+ return TempLine;
+}
+
+#ifdef VGA_KEEP_CHANGES
+static Bit8u * VGA_Draw_Changes_Line(Bitu vidstart, Bitu line) {
+ Bitu checkMask = vga.changes.checkMask;
+ Bit8u *map = vga.changes.map;
+ Bitu start = (vidstart >> VGA_CHANGE_SHIFT);
+ Bitu end = ((vidstart + vga.draw.line_length ) >> VGA_CHANGE_SHIFT);
+ for (; start <= end;start++) {
+ if ( map[start] & checkMask ) {
+ Bitu offset = vidstart & vga.draw.linear_mask;
+ if (vga.draw.linear_mask-offset < vga.draw.line_length)
+ memcpy(vga.draw.linear_base+vga.draw.linear_mask+1, vga.draw.linear_base, vga.draw.line_length);
+ Bit8u *ret = &vga.draw.linear_base[ offset ];
+#if !defined(C_UNALIGNED_MEMORY)
+ if (GCC_UNLIKELY( ((Bitu)ret) & (sizeof(Bitu)-1)) ) {
+ memcpy( TempLine, ret, vga.draw.line_length );
+ return TempLine;
+ }
+#endif
+ return ret;
+ }
+ }
+// memset( TempLine, 0x30, vga.changes.lineWidth );
+// return TempLine;
+ return 0;
+}
+
+#endif
+
+static Bit8u * VGA_Draw_Linear_Line(Bitu vidstart, Bitu /*line*/) {
+ Bitu offset = vidstart & vga.draw.linear_mask;
+ Bit8u* ret = &vga.draw.linear_base[offset];
+
+ // in case (vga.draw.line_length + offset) has bits set that
+ // are not set in the mask: ((x|y)!=y) equals (x&~y)
+ if (GCC_UNLIKELY((vga.draw.line_length + offset)& ~vga.draw.linear_mask)) {
+ // this happens, if at all, only once per frame (1 of 480 lines)
+ // in some obscure games
+ Bitu end = (offset + vga.draw.line_length) & vga.draw.linear_mask;
+
+ // assuming lines not longer than 4096 pixels
+ Bitu wrapped_len = end & 0xFFF;
+ Bitu unwrapped_len = vga.draw.line_length-wrapped_len;
+
+ // unwrapped chunk: to top of memory block
+ memcpy(TempLine, &vga.draw.linear_base[offset], unwrapped_len);
+ // wrapped chunk: from base of memory block
+ memcpy(&TempLine[unwrapped_len], vga.draw.linear_base, wrapped_len);
+
+ ret = TempLine;
+ }
+
+#if !defined(C_UNALIGNED_MEMORY)
+ if (GCC_UNLIKELY( ((Bitu)ret) & (sizeof(Bitu)-1)) ) {
+ memcpy( TempLine, ret, vga.draw.line_length );
+ return TempLine;
+ }
+#endif
+ return ret;
+}
+
+static Bit8u * VGA_Draw_Xlat16_Linear_Line(Bitu vidstart, Bitu /*line*/) {
+ Bitu offset = vidstart & vga.draw.linear_mask;
+ Bit8u *ret = &vga.draw.linear_base[offset];
+ Bit16u* temps = (Bit16u*) TempLine;
+
+ // see VGA_Draw_Linear_Line
+ if (GCC_UNLIKELY((vga.draw.line_length + offset)& ~vga.draw.linear_mask)) {
+ Bitu end = (offset + vga.draw.line_length) & vga.draw.linear_mask;
+
+ // assuming lines not longer than 4096 pixels
+ Bitu wrapped_len = end & 0xFFF;
+ Bitu unwrapped_len = vga.draw.line_length-wrapped_len;
+
+ // unwrapped chunk: to top of memory block
+ for(Bitu i = 0; i < unwrapped_len; i++)
+ temps[i]=vga.dac.xlat16[ret[i]];
+
+ // wrapped chunk: from base of memory block
+ for(Bitu i = 0; i < wrapped_len; i++)
+ temps[i + unwrapped_len]=vga.dac.xlat16[vga.draw.linear_base[i]];
+
+ } else {
+ for(Bitu i = 0; i < vga.draw.line_length; i++) {
+ temps[i]=vga.dac.xlat16[ret[i]];
+ }
+ }
+ return TempLine;
+}
+
+//Test version, might as well keep it
+/* static Bit8u * VGA_Draw_Chain_Line(Bitu vidstart, Bitu line) {
+ Bitu i = 0;
+ for ( i = 0; i < vga.draw.width;i++ ) {
+ Bitu addr = vidstart + i;
+ TempLine[i] = vga.mem.linear[((addr&~3)<<2)+(addr&3)];
+ }
+ return TempLine;
+} */
+
+static Bit8u * VGA_Draw_VGA_Line_HWMouse( Bitu vidstart, Bitu /*line*/) {
+ if (!svga.hardware_cursor_active || !svga.hardware_cursor_active())
+ // HW Mouse not enabled, use the tried and true call
+ return &vga.mem.linear[vidstart];
+
+ Bitu lineat = (vidstart-(vga.config.real_start<<2)) / vga.draw.width;
+ if ((vga.s3.hgc.posx >= vga.draw.width) ||
+ (lineat < vga.s3.hgc.originy) ||
+ (lineat > (vga.s3.hgc.originy + (63U-vga.s3.hgc.posy))) ) {
+ // the mouse cursor *pattern* is not on this line
+ return &vga.mem.linear[ vidstart ];
+ } else {
+ // Draw mouse cursor: cursor is a 64x64 pattern which is shifted (inside the
+ // 64x64 mouse cursor space) to the right by posx pixels and up by posy pixels.
+ // This is used when the mouse cursor partially leaves the screen.
+ // It is arranged as bitmap of 16bits of bitA followed by 16bits of bitB, each
+ // AB bits corresponding to a cursor pixel. The whole map is 8kB in size.
+ memcpy(TempLine, &vga.mem.linear[ vidstart ], vga.draw.width);
+ // the index of the bit inside the cursor bitmap we start at:
+ Bitu sourceStartBit = ((lineat - vga.s3.hgc.originy) + vga.s3.hgc.posy)*64 + vga.s3.hgc.posx;
+ // convert to video memory addr and bit index
+ // start adjusted to the pattern structure (thus shift address by 2 instead of 3)
+ // Need to get rid of the third bit, so "/8 *2" becomes ">> 2 & ~1"
+ Bitu cursorMemStart = ((sourceStartBit >> 2)& ~1) + (((Bit32u)vga.s3.hgc.startaddr) << 10);
+ Bitu cursorStartBit = sourceStartBit & 0x7;
+ // stay at the right position in the pattern
+ if (cursorMemStart & 0x2) cursorMemStart--;
+ Bitu cursorMemEnd = cursorMemStart + ((64-vga.s3.hgc.posx) >> 2);
+ Bit8u* xat = &TempLine[vga.s3.hgc.originx]; // mouse data start pos. in scanline
+ for (Bitu m = cursorMemStart; m < cursorMemEnd; (m&1)?(m+=3):m++) {
+ // for each byte of cursor data
+ Bit8u bitsA = vga.mem.linear[m];
+ Bit8u bitsB = vga.mem.linear[m+2];
+ for (Bit8u bit=(0x80 >> cursorStartBit); bit != 0; bit >>= 1) {
+ // for each bit
+ cursorStartBit=0; // only the first byte has some bits cut off
+ if (bitsA&bit) {
+ if (bitsB&bit) *xat ^= 0xFF; // Invert screen data
+ //else Transparent
+ } else if (bitsB&bit) {
+ *xat = vga.s3.hgc.forestack[0]; // foreground color
+ } else {
+ *xat = vga.s3.hgc.backstack[0];
+ }
+ xat++;
+ }
+ }
+ return TempLine;
+ }
+}
+
+static Bit8u * VGA_Draw_LIN16_Line_HWMouse(Bitu vidstart, Bitu /*line*/) {
+ if (!svga.hardware_cursor_active || !svga.hardware_cursor_active())
+ return &vga.mem.linear[vidstart];
+
+ Bitu lineat = ((vidstart-(vga.config.real_start<<2)) >> 1) / vga.draw.width;
+ if ((vga.s3.hgc.posx >= vga.draw.width) ||
+ (lineat < vga.s3.hgc.originy) ||
+ (lineat > (vga.s3.hgc.originy + (63U-vga.s3.hgc.posy))) ) {
+ return &vga.mem.linear[vidstart];
+ } else {
+ memcpy(TempLine, &vga.mem.linear[ vidstart ], vga.draw.width*2);
+ Bitu sourceStartBit = ((lineat - vga.s3.hgc.originy) + vga.s3.hgc.posy)*64 + vga.s3.hgc.posx;
+ Bitu cursorMemStart = ((sourceStartBit >> 2)& ~1) + (((Bit32u)vga.s3.hgc.startaddr) << 10);
+ Bitu cursorStartBit = sourceStartBit & 0x7;
+ if (cursorMemStart & 0x2) cursorMemStart--;
+ Bitu cursorMemEnd = cursorMemStart + ((64-vga.s3.hgc.posx) >> 2);
+ Bit16u* xat = &((Bit16u*)TempLine)[vga.s3.hgc.originx];
+ for (Bitu m = cursorMemStart; m < cursorMemEnd; (m&1)?(m+=3):m++) {
+ // for each byte of cursor data
+ Bit8u bitsA = vga.mem.linear[m];
+ Bit8u bitsB = vga.mem.linear[m+2];
+ for (Bit8u bit=(0x80 >> cursorStartBit); bit != 0; bit >>= 1) {
+ // for each bit
+ cursorStartBit=0;
+ if (bitsA&bit) {
+ // byte order doesn't matter here as all bits get flipped
+ if (bitsB&bit) *xat ^= ~0U;
+ //else Transparent
+ } else if (bitsB&bit) {
+ // Source as well as destination are Bit8u arrays,
+ // so this should work out endian-wise?
+ *xat = *(Bit16u*)vga.s3.hgc.forestack;
+ } else {
+ *xat = *(Bit16u*)vga.s3.hgc.backstack;
+ }
+ xat++;
+ }
+ }
+ return TempLine;
+ }
+}
+
+static Bit8u * VGA_Draw_LIN32_Line_HWMouse(Bitu vidstart, Bitu /*line*/) {
+ if (!svga.hardware_cursor_active || !svga.hardware_cursor_active())
+ return &vga.mem.linear[vidstart];
+
+ Bitu lineat = ((vidstart-(vga.config.real_start<<2)) >> 2) / vga.draw.width;
+ if ((vga.s3.hgc.posx >= vga.draw.width) ||
+ (lineat < vga.s3.hgc.originy) ||
+ (lineat > (vga.s3.hgc.originy + (63U-vga.s3.hgc.posy))) ) {
+ return &vga.mem.linear[ vidstart ];
+ } else {
+ memcpy(TempLine, &vga.mem.linear[ vidstart ], vga.draw.width*4);
+ Bitu sourceStartBit = ((lineat - vga.s3.hgc.originy) + vga.s3.hgc.posy)*64 + vga.s3.hgc.posx;
+ Bitu cursorMemStart = ((sourceStartBit >> 2)& ~1) + (((Bit32u)vga.s3.hgc.startaddr) << 10);
+ Bitu cursorStartBit = sourceStartBit & 0x7;
+ if (cursorMemStart & 0x2) cursorMemStart--;
+ Bitu cursorMemEnd = cursorMemStart + ((64-vga.s3.hgc.posx) >> 2);
+ Bit32u* xat = &((Bit32u*)TempLine)[vga.s3.hgc.originx];
+ for (Bitu m = cursorMemStart; m < cursorMemEnd; (m&1)?(m+=3):m++) {
+ // for each byte of cursor data
+ Bit8u bitsA = vga.mem.linear[m];
+ Bit8u bitsB = vga.mem.linear[m+2];
+ for (Bit8u bit=(0x80 >> cursorStartBit); bit != 0; bit >>= 1) { // for each bit
+ cursorStartBit=0;
+ if (bitsA&bit) {
+ if (bitsB&bit) *xat ^= ~0U;
+ //else Transparent
+ } else if (bitsB&bit) {
+ *xat = *(Bit32u*)vga.s3.hgc.forestack;
+ } else {
+ *xat = *(Bit32u*)vga.s3.hgc.backstack;
+ }
+ xat++;
+ }
+ }
+ return TempLine;
+ }
+}
+
+static const Bit8u* VGA_Text_Memwrap(Bitu vidstart) {
+ vidstart &= vga.draw.linear_mask;
+ Bitu line_end = 2 * vga.draw.blocks;
+ if (GCC_UNLIKELY((vidstart + line_end) > vga.draw.linear_mask)) {
+ // wrapping in this line
+ Bitu break_pos = (vga.draw.linear_mask - vidstart) + 1;
+ // need a temporary storage - TempLine/2 is ok for a bit more than 132 columns
+ memcpy(&TempLine[sizeof(TempLine)/2], &vga.tandy.draw_base[vidstart], break_pos);
+ memcpy(&TempLine[sizeof(TempLine)/2 + break_pos],&vga.tandy.draw_base[0], line_end - break_pos);
+ return &TempLine[sizeof(TempLine)/2];
+ } else return &vga.tandy.draw_base[vidstart];
+}
+
+static Bit32u FontMask[2]={0xffffffff,0x0};
+static Bit8u * VGA_TEXT_Draw_Line(Bitu vidstart, Bitu line) {
+ Bits font_addr;
+ Bit32u * draw=(Bit32u *)TempLine;
+ const Bit8u* vidmem = VGA_Text_Memwrap(vidstart);
+ for (Bitu cx=0;cx<vga.draw.blocks;cx++) {
+ Bitu chr=vidmem[cx*2];
+ Bitu col=vidmem[cx*2+1];
+ Bitu font=vga.draw.font_tables[(col >> 3)&1][chr*32+line];
+ Bit32u mask1=TXT_Font_Table[font>>4] & FontMask[col >> 7];
+ Bit32u mask2=TXT_Font_Table[font&0xf] & FontMask[col >> 7];
+ Bit32u fg=TXT_FG_Table[col&0xf];
+ Bit32u bg=TXT_BG_Table[col>>4];
+ *draw++=(fg&mask1) | (bg&~mask1);
+ *draw++=(fg&mask2) | (bg&~mask2);
+ }
+ if (!vga.draw.cursor.enabled || !(vga.draw.cursor.count&0x8)) goto skip_cursor;
+ font_addr = (vga.draw.cursor.address-vidstart) >> 1;
+ if (font_addr>=0 && font_addr<(Bits)vga.draw.blocks) {
+ if (line<vga.draw.cursor.sline) goto skip_cursor;
+ if (line>vga.draw.cursor.eline) goto skip_cursor;
+ draw=(Bit32u *)&TempLine[font_addr*8];
+ Bit32u att=TXT_FG_Table[vga.tandy.draw_base[vga.draw.cursor.address+1]&0xf];
+ *draw++=att;*draw++=att;
+ }
+skip_cursor:
+ return TempLine;
+}
+
+static Bit8u * VGA_TEXT_Herc_Draw_Line(Bitu vidstart, Bitu line) {
+ Bits font_addr;
+ Bit32u * draw=(Bit32u *)TempLine;
+ const Bit8u* vidmem = VGA_Text_Memwrap(vidstart);
+
+ for (Bitu cx=0;cx<vga.draw.blocks;cx++) {
+ Bitu chr=vidmem[cx*2];
+ Bitu attrib=vidmem[cx*2+1];
+ if (!(attrib&0x77)) {
+ // 00h, 80h, 08h, 88h produce black space
+ *draw++=0;
+ *draw++=0;
+ } else {
+ Bit32u bg, fg;
+ bool underline=false;
+ if ((attrib&0x77)==0x70) {
+ bg = TXT_BG_Table[0x7];
+ if (attrib&0x8) fg = TXT_FG_Table[0xf];
+ else fg = TXT_FG_Table[0x0];
+ } else {
+ if (((Bitu)(vga.crtc.underline_location&0x1f)==line) && ((attrib&0x77)==0x1)) underline=true;
+ bg = TXT_BG_Table[0x0];
+ if (attrib&0x8) fg = TXT_FG_Table[0xf];
+ else fg = TXT_FG_Table[0x7];
+ }
+ Bit32u mask1, mask2;
+ if (GCC_UNLIKELY(underline)) mask1 = mask2 = FontMask[attrib >> 7];
+ else {
+ Bitu font=vga.draw.font_tables[0][chr*32+line];
+ mask1=TXT_Font_Table[font>>4] & FontMask[attrib >> 7]; // blinking
+ mask2=TXT_Font_Table[font&0xf] & FontMask[attrib >> 7];
+ }
+ *draw++=(fg&mask1) | (bg&~mask1);
+ *draw++=(fg&mask2) | (bg&~mask2);
+ }
+ }
+ if (!vga.draw.cursor.enabled || !(vga.draw.cursor.count&0x8)) goto skip_cursor;
+ font_addr = (vga.draw.cursor.address-vidstart) >> 1;
+ if (font_addr>=0 && font_addr<(Bits)vga.draw.blocks) {
+ if (line<vga.draw.cursor.sline) goto skip_cursor;
+ if (line>vga.draw.cursor.eline) goto skip_cursor;
+ draw=(Bit32u *)&TempLine[font_addr*8];
+ Bit8u attr = vga.tandy.draw_base[vga.draw.cursor.address+1];
+ Bit32u cg;
+ if (attr&0x8) {
+ cg = TXT_FG_Table[0xf];
+ } else if ((attr&0x77)==0x70) {
+ cg = TXT_FG_Table[0x0];
+ } else {
+ cg = TXT_FG_Table[0x7];
+ }
+ *draw++=cg;*draw++=cg;
+ }
+skip_cursor:
+ return TempLine;
+}
+/*
+// combined 8/9-dot wide text mode 8bpp line drawing function
+static Bit8u* VGA_TEXT_Draw_Line(Bitu vidstart, Bitu line) {
+ // keep it aligned:
+ Bit8u* draw = ((Bit8u*)TempLine) + 16 - vga.draw.panning;
+ const Bit8u* vidmem = VGA_Text_Memwrap(vidstart); // pointer to chars+attribs
+ Bitu blocks = vga.draw.blocks;
+ if (vga.draw.panning) blocks++; // if the text is panned part of an
+ // additional character becomes visible
+ while (blocks--) { // for each character in the line
+ Bitu chr = *vidmem++;
+ Bitu attr = *vidmem++;
+ // the font pattern
+ Bitu font = vga.draw.font_tables[(attr >> 3)&1][(chr<<5)+line];
+
+ Bitu background = attr >> 4;
+ // if blinking is enabled bit7 is not mapped to attributes
+ if (vga.draw.blinking) background &= ~0x8;
+ // choose foreground color if blinking not set for this cell or blink on
+ Bitu foreground = (vga.draw.blink || (!(attr&0x80)))?
+ (attr&0xf):background;
+ // underline: all foreground [freevga: 0x77, previous 0x7]
+ if (GCC_UNLIKELY(((attr&0x77) == 0x01) &&
+ (vga.crtc.underline_location&0x1f)==line))
+ background = foreground;
+ if (vga.draw.char9dot) {
+ font <<=1; // 9 pixels
+ // extend to the 9th pixel if needed
+ if ((font&0x2) && (vga.attr.mode_control&0x04) &&
+ (chr>=0xc0) && (chr<=0xdf)) font |= 1;
+ for (Bitu n = 0; n < 9; n++) {
+ *draw++ = (font&0x100)? foreground:background;
+ font <<= 1;
+ }
+ } else {
+ for (Bitu n = 0; n < 8; n++) {
+ *draw++ = (font&0x80)? foreground:background;
+ font <<= 1;
+ }
+ }
+ }
+ // draw the text mode cursor if needed
+ if ((vga.draw.cursor.count&0x8) && (line >= vga.draw.cursor.sline) &&
+ (line <= vga.draw.cursor.eline) && vga.draw.cursor.enabled) {
+ // the adress of the attribute that makes up the cell the cursor is in
+ Bits attr_addr = (vga.draw.cursor.address-vidstart) >> 1;
+ if (attr_addr >= 0 && attr_addr < (Bits)vga.draw.blocks) {
+ Bitu index = attr_addr * (vga.draw.char9dot? 9:8);
+ draw = (Bit8u*)(&TempLine[index]) + 16 - vga.draw.panning;
+
+ Bitu foreground = vga.tandy.draw_base[vga.draw.cursor.address+1] & 0xf;
+ for (Bitu i = 0; i < 8; i++) {
+ *draw++ = foreground;
+ }
+ }
+ }
+ return TempLine+16;
+}
+*/
+// combined 8/9-dot wide text mode 16bpp line drawing function
+static Bit8u* VGA_TEXT_Xlat16_Draw_Line(Bitu vidstart, Bitu line) {
+ // keep it aligned:
+ Bit16u* draw = ((Bit16u*)TempLine) + 16 - vga.draw.panning;
+ const Bit8u* vidmem = VGA_Text_Memwrap(vidstart); // pointer to chars+attribs
+ Bitu blocks = vga.draw.blocks;
+ if (vga.draw.panning) blocks++; // if the text is panned part of an
+ // additional character becomes visible
+ while (blocks--) { // for each character in the line
+ Bitu chr = *vidmem++;
+ Bitu attr = *vidmem++;
+ // the font pattern
+ Bitu font = vga.draw.font_tables[(attr >> 3)&1][(chr<<5)+line];
+
+ Bitu background = attr >> 4;
+ // if blinking is enabled bit7 is not mapped to attributes
+ if (vga.draw.blinking) background &= ~0x8;
+ // choose foreground color if blinking not set for this cell or blink on
+ Bitu foreground = (vga.draw.blink || (!(attr&0x80)))?
+ (attr&0xf):background;
+ // underline: all foreground [freevga: 0x77, previous 0x7]
+ if (GCC_UNLIKELY(((attr&0x77) == 0x01) &&
+ (vga.crtc.underline_location&0x1f)==line))
+ background = foreground;
+ if (vga.draw.char9dot) {
+ font <<=1; // 9 pixels
+ // extend to the 9th pixel if needed
+ if ((font&0x2) && (vga.attr.mode_control&0x04) &&
+ (chr>=0xc0) && (chr<=0xdf)) font |= 1;
+ for (Bitu n = 0; n < 9; n++) {
+ *draw++ = vga.dac.xlat16[(font&0x100)? foreground:background];
+ font <<= 1;
+ }
+ } else {
+ for (Bitu n = 0; n < 8; n++) {
+ *draw++ = vga.dac.xlat16[(font&0x80)? foreground:background];
+ font <<= 1;
+ }
+ }
+ }
+ // draw the text mode cursor if needed
+ if ((vga.draw.cursor.count&0x8) && (line >= vga.draw.cursor.sline) &&
+ (line <= vga.draw.cursor.eline) && vga.draw.cursor.enabled) {
+ // the adress of the attribute that makes up the cell the cursor is in
+ Bits attr_addr = (vga.draw.cursor.address-vidstart) >> 1;
+ if (attr_addr >= 0 && attr_addr < (Bits)vga.draw.blocks) {
+ Bitu index = attr_addr * (vga.draw.char9dot? 18:16);
+ draw = (Bit16u*)(&TempLine[index]) + 16 - vga.draw.panning;
+
+ Bitu foreground = vga.tandy.draw_base[vga.draw.cursor.address+1] & 0xf;
+ for (Bitu i = 0; i < 8; i++) {
+ *draw++ = vga.dac.xlat16[foreground];
+ }
+ }
+ }
+ return TempLine+32;
+}
+
+#ifdef VGA_KEEP_CHANGES
+static INLINE void VGA_ChangesEnd(void ) {
+ if ( vga.changes.active ) {
+// vga.changes.active = false;
+ Bitu end = vga.draw.address >> VGA_CHANGE_SHIFT;
+ Bitu total = 4 + end - vga.changes.start;
+ Bit32u clearMask = vga.changes.clearMask;
+ total >>= 2;
+ Bit32u *clear = (Bit32u *)&vga.changes.map[ vga.changes.start & ~3 ];
+ while ( total-- ) {
+ clear[0] &= clearMask;
+ clear++;
+ }
+ }
+}
+#endif
+
+
+static void VGA_ProcessSplit() {
+ if (vga.attr.mode_control&0x20) {
+ vga.draw.address=0;
+ // reset panning to 0 here so we don't have to check for
+ // it in the character draw functions. It will be set back
+ // to its proper value in v-retrace
+ vga.draw.panning=0;
+ } else {
+ // In text mode only the characters are shifted by panning, not the address;
+ // this is done in the text line draw function.
+ vga.draw.address = vga.draw.byte_panning_shift*vga.draw.bytes_skip;
+ if ((vga.mode!=M_TEXT)&&(machine!=MCH_EGA)) vga.draw.address += vga.draw.panning;
+ }
+ vga.draw.address_line=0;
+}
+
+static Bit8u bg_color_index = 0; // screen-off black index
+static void VGA_DrawSingleLine(Bitu /*blah*/) {
+ if (GCC_UNLIKELY(vga.attr.disabled)) {
+ switch(machine) {
+ case MCH_PCJR:
+ // Displays the border color when screen is disabled
+ bg_color_index = vga.tandy.border_color;
+ break;
+ case MCH_TANDY:
+ // Either the PCJr way or the CGA way
+ if (vga.tandy.gfx_control& 0x4) {
+ bg_color_index = vga.tandy.border_color;
+ } else if (vga.mode==M_TANDY4)
+ bg_color_index = vga.attr.palette[0];
+ else bg_color_index = 0;
+ break;
+ case MCH_CGA:
+ // the background color
+ bg_color_index = vga.attr.overscan_color;
+ break;
+ case MCH_EGA:
+ case MCH_VGA:
+ // DoWhackaDo, Alien Carnage, TV sports Football
+ // when disabled by attribute index bit 5:
+ // ET3000, ET4000, Paradise display the border color
+ // S3 displays the content of the currently selected attribute register
+ // when disabled by sequencer the screen is black "257th color"
+
+ // the DAC table may not match the bits of the overscan register
+ // so use black for this case too...
+ //if (vga.attr.disabled& 2) {
+ if (vga.dac.xlat16[bg_color_index] != 0) {
+ for(Bitu i = 0; i < 256; i++)
+ if (vga.dac.xlat16[i] == 0) {
+ bg_color_index = i;
+ break;
+ }
+ }
+ //} else
+ // bg_color_index = vga.attr.overscan_color;
+ break;
+ default:
+ bg_color_index = 0;
+ break;
+ }
+ if (vga.draw.bpp==8) {
+ memset(TempLine, bg_color_index, sizeof(TempLine));
+ } else if (vga.draw.bpp==16) {
+ Bit16u* wptr = (Bit16u*) TempLine;
+ Bit16u value = vga.dac.xlat16[bg_color_index];
+ for (Bitu i = 0; i < sizeof(TempLine)/2; i++) {
+ wptr[i] = value;
+ }
+ }
+ RENDER_DrawLine(TempLine);
+ } else {
+ Bit8u * data=VGA_DrawLine( vga.draw.address, vga.draw.address_line );
+ RENDER_DrawLine(data);
+ }
+
+ vga.draw.address_line++;
+ if (vga.draw.address_line>=vga.draw.address_line_total) {
+ vga.draw.address_line=0;
+ vga.draw.address+=vga.draw.address_add;
+ }
+ vga.draw.lines_done++;
+ if (vga.draw.split_line==vga.draw.lines_done) VGA_ProcessSplit();
+ if (vga.draw.lines_done < vga.draw.lines_total) {
+ PIC_AddEvent(VGA_DrawSingleLine,(float)vga.draw.delay.htotal);
+ } else RENDER_EndUpdate(false);
+}
+
+static void VGA_DrawEGASingleLine(Bitu /*blah*/) {
+ if (GCC_UNLIKELY(vga.attr.disabled)) {
+ memset(TempLine, 0, sizeof(TempLine));
+ RENDER_DrawLine(TempLine);
+ } else {
+ Bitu address = vga.draw.address;
+ if (vga.mode!=M_TEXT) address += vga.draw.panning;
+ Bit8u * data=VGA_DrawLine(address, vga.draw.address_line );
+ RENDER_DrawLine(data);
+ }
+
+ vga.draw.address_line++;
+ if (vga.draw.address_line>=vga.draw.address_line_total) {
+ vga.draw.address_line=0;
+ vga.draw.address+=vga.draw.address_add;
+ }
+ vga.draw.lines_done++;
+ if (vga.draw.split_line==vga.draw.lines_done) VGA_ProcessSplit();
+ if (vga.draw.lines_done < vga.draw.lines_total) {
+ PIC_AddEvent(VGA_DrawEGASingleLine,(float)vga.draw.delay.htotal);
+ } else RENDER_EndUpdate(false);
+}
+
+static void VGA_DrawPart(Bitu lines) {
+ while (lines--) {
+ Bit8u * data=VGA_DrawLine( vga.draw.address, vga.draw.address_line );
+ RENDER_DrawLine(data);
+ vga.draw.address_line++;
+ if (vga.draw.address_line>=vga.draw.address_line_total) {
+ vga.draw.address_line=0;
+ vga.draw.address+=vga.draw.address_add;
+ }
+ vga.draw.lines_done++;
+ if (vga.draw.split_line==vga.draw.lines_done) {
+#ifdef VGA_KEEP_CHANGES
+ VGA_ChangesEnd( );
+#endif
+ VGA_ProcessSplit();
+#ifdef VGA_KEEP_CHANGES
+ vga.changes.start = vga.draw.address >> VGA_CHANGE_SHIFT;
+#endif
+ }
+ }
+ if (--vga.draw.parts_left) {
+ PIC_AddEvent(VGA_DrawPart,(float)vga.draw.delay.parts,
+ (vga.draw.parts_left!=1) ? vga.draw.parts_lines : (vga.draw.lines_total - vga.draw.lines_done));
+ } else {
+#ifdef VGA_KEEP_CHANGES
+ VGA_ChangesEnd();
+#endif
+ RENDER_EndUpdate(false);
+ }
+}
+
+void VGA_SetBlinking(Bitu enabled) {
+ Bitu b;
+ LOG(LOG_VGA,LOG_NORMAL)("Blinking %d",enabled);
+ if (enabled) {
+ b=0;vga.draw.blinking=1; //used to -1 but blinking is unsigned
+ vga.attr.mode_control|=0x08;
+ vga.tandy.mode_control|=0x20;
+ } else {
+ b=8;vga.draw.blinking=0;
+ vga.attr.mode_control&=~0x08;
+ vga.tandy.mode_control&=~0x20;
+ }
+ for (Bitu i=0;i<8;i++) TXT_BG_Table[i+8]=(b+i) | ((b+i) << 8)| ((b+i) <<16) | ((b+i) << 24);
+}
+
+#ifdef VGA_KEEP_CHANGES
+static void INLINE VGA_ChangesStart( void ) {
+ vga.changes.start = vga.draw.address >> VGA_CHANGE_SHIFT;
+ vga.changes.last = vga.changes.start;
+ if ( vga.changes.lastAddress != vga.draw.address ) {
+// LOG_MSG("Address");
+ VGA_DrawLine = VGA_Draw_Linear_Line;
+ vga.changes.lastAddress = vga.draw.address;
+ } else if ( render.fullFrame ) {
+// LOG_MSG("Full Frame");
+ VGA_DrawLine = VGA_Draw_Linear_Line;
+ } else {
+// LOG_MSG("Changes");
+ VGA_DrawLine = VGA_Draw_Changes_Line;
+ }
+ vga.changes.active = true;
+ vga.changes.checkMask = vga.changes.writeMask;
+ vga.changes.clearMask = ~( 0x01010101 << (vga.changes.frame & 7));
+ vga.changes.frame++;
+ vga.changes.writeMask = 1 << (vga.changes.frame & 7);
+}
+#endif
+
+static void VGA_VertInterrupt(Bitu /*val*/) {
+ if ((!vga.draw.vret_triggered) && ((vga.crtc.vertical_retrace_end&0x30)==0x10)) {
+ vga.draw.vret_triggered=true;
+ if (GCC_UNLIKELY(machine==MCH_EGA)) PIC_ActivateIRQ(9);
+ }
+}
+
+static void VGA_Other_VertInterrupt(Bitu val) {
+ if (val) PIC_ActivateIRQ(5);
+ else PIC_DeActivateIRQ(5);
+}
+
+static void VGA_DisplayStartLatch(Bitu /*val*/) {
+ vga.config.real_start=vga.config.display_start & (vga.vmemwrap-1);
+ vga.draw.bytes_skip = vga.config.bytes_skip;
+}
+
+static void VGA_PanningLatch(Bitu /*val*/) {
+ vga.draw.panning = vga.config.pel_panning;
+}
+
+static void VGA_VerticalTimer(Bitu /*val*/) {
+ vga.draw.delay.framestart = PIC_FullIndex();
+ PIC_AddEvent( VGA_VerticalTimer, (float)vga.draw.delay.vtotal );
+
+ switch(machine) {
+ case MCH_PCJR:
+ case MCH_TANDY:
+ // PCJr: Vsync is directly connected to the IRQ controller
+ // Some earlier Tandy models are said to have a vsync interrupt too
+ PIC_AddEvent(VGA_Other_VertInterrupt, (float)vga.draw.delay.vrstart, 1);
+ PIC_AddEvent(VGA_Other_VertInterrupt, (float)vga.draw.delay.vrend, 0);
+ // fall-through
+ case MCH_CGA:
+ case MCH_HERC:
+ // MC6845-powered graphics: Loading the display start latch happens somewhere
+ // after vsync off and before first visible scanline, so probably here
+ VGA_DisplayStartLatch(0);
+ break;
+ case MCH_VGA:
+ PIC_AddEvent(VGA_DisplayStartLatch, (float)vga.draw.delay.vrstart);
+ PIC_AddEvent(VGA_PanningLatch, (float)vga.draw.delay.vrend);
+ // EGA: 82c435 datasheet: interrupt happens at display end
+ // VGA: checked with scope; however disabled by default by jumper on VGA boards
+ // add a little amount of time to make sure the last drawpart has already fired
+ PIC_AddEvent(VGA_VertInterrupt,(float)(vga.draw.delay.vdend + 0.005));
+ break;
+ case MCH_EGA:
+ PIC_AddEvent(VGA_DisplayStartLatch, (float)vga.draw.delay.vrend);
+ PIC_AddEvent(VGA_VertInterrupt,(float)(vga.draw.delay.vdend + 0.005));
+ break;
+ default:
+ E_Exit("This new machine needs implementation in VGA_VerticalTimer too.");
+ break;
+ }
+ //Check if we can actually render, else skip the rest (frameskip)
+ if (vga.draw.vga_override || !RENDER_StartUpdate())
+ return;
+
+ vga.draw.address_line = vga.config.hlines_skip;
+ if (IS_EGAVGA_ARCH) {
+ vga.draw.split_line = (Bitu)((vga.config.line_compare+1)/vga.draw.lines_scaled);
+ if ((svgaCard==SVGA_S3Trio) && (vga.config.line_compare==0)) vga.draw.split_line=0;
+ vga.draw.split_line -= vga.draw.vblank_skip;
+ } else {
+ vga.draw.split_line = 0x10000; // don't care
+ }
+ vga.draw.address = vga.config.real_start;
+ vga.draw.byte_panning_shift = 0;
+ // go figure...
+ if (machine==MCH_EGA) {
+ if (vga.draw.doubleheight) // Spacepigs EGA Megademo
+ vga.draw.split_line*=2;
+ vga.draw.split_line++; // EGA adds one buggy scanline
+ }
+// if (machine==MCH_EGA) vga.draw.split_line = ((((vga.config.line_compare&0x5ff)+1)*2-1)/vga.draw.lines_scaled);
+#ifdef VGA_KEEP_CHANGES
+ bool startaddr_changed=false;
+#endif
+ switch (vga.mode) {
+ case M_EGA:
+ if (!(vga.crtc.mode_control&0x1)) vga.draw.linear_mask &= ~0x10000;
+ else vga.draw.linear_mask |= 0x10000;
+ case M_LIN4:
+ vga.draw.byte_panning_shift = 8;
+ vga.draw.address += vga.draw.bytes_skip;
+ vga.draw.address *= vga.draw.byte_panning_shift;
+ if (machine!=MCH_EGA) vga.draw.address += vga.draw.panning;
+#ifdef VGA_KEEP_CHANGES
+ startaddr_changed=true;
+#endif
+ break;
+ case M_VGA:
+ if (vga.config.compatible_chain4 && (vga.crtc.underline_location & 0x40)) {
+ vga.draw.linear_base = vga.fastmem;
+ vga.draw.linear_mask = 0xffff;
+ } else {
+ vga.draw.linear_base = vga.mem.linear;
+ vga.draw.linear_mask = vga.vmemwrap - 1;
+ }
+ case M_LIN8:
+ case M_LIN15:
+ case M_LIN16:
+ case M_LIN32:
+ vga.draw.byte_panning_shift = 4;
+ vga.draw.address += vga.draw.bytes_skip;
+ vga.draw.address *= vga.draw.byte_panning_shift;
+ vga.draw.address += vga.draw.panning;
+#ifdef VGA_KEEP_CHANGES
+ startaddr_changed=true;
+#endif
+ break;
+ case M_TEXT:
+ vga.draw.byte_panning_shift = 2;
+ vga.draw.address += vga.draw.bytes_skip;
+ // fall-through
+ case M_TANDY_TEXT:
+ case M_HERC_TEXT:
+ if (machine==MCH_HERC) vga.draw.linear_mask = 0xfff; // 1 page
+ else if (IS_EGAVGA_ARCH) vga.draw.linear_mask = 0x7fff; // 8 pages
+ else vga.draw.linear_mask = 0x3fff; // CGA, Tandy 4 pages
+ vga.draw.cursor.address=vga.config.cursor_start*2;
+ vga.draw.address *= 2;
+ vga.draw.cursor.count++;
+ /* check for blinking and blinking change delay */
+ FontMask[1]=(vga.draw.blinking & (vga.draw.cursor.count >> 4)) ?
+ 0 : 0xffffffff;
+ /* if blinking is enabled, 'blink' will toggle between true
+ * and false. Otherwise it's true */
+ vga.draw.blink = ((vga.draw.blinking & (vga.draw.cursor.count >> 4))
+ || !vga.draw.blinking) ? true:false;
+ break;
+ case M_HERC_GFX:
+ case M_CGA4:case M_CGA2:
+ vga.draw.address=(vga.draw.address*2)&0x1fff;
+ break;
+ case M_CGA16:
+ case M_TANDY2:case M_TANDY4:case M_TANDY16:
+ vga.draw.address *= 2;
+ break;
+ default:
+ break;
+ }
+ if (GCC_UNLIKELY(vga.draw.split_line==0)) VGA_ProcessSplit();
+#ifdef VGA_KEEP_CHANGES
+ if (startaddr_changed) VGA_ChangesStart();
+#endif
+
+ // check if some lines at the top off the screen are blanked
+ float draw_skip = 0.0;
+ if (GCC_UNLIKELY(vga.draw.vblank_skip)) {
+ draw_skip = (float)(vga.draw.delay.htotal * vga.draw.vblank_skip);
+ vga.draw.address += vga.draw.address_add * (vga.draw.vblank_skip/(vga.draw.address_line_total));
+ }
+
+ // add the draw event
+ switch (vga.draw.mode) {
+ case PART:
+ if (GCC_UNLIKELY(vga.draw.parts_left)) {
+ LOG(LOG_VGAMISC,LOG_NORMAL)( "Parts left: %d", vga.draw.parts_left );
+ PIC_RemoveEvents(VGA_DrawPart);
+ RENDER_EndUpdate(true);
+ }
+ vga.draw.lines_done = 0;
+ vga.draw.parts_left = vga.draw.parts_total;
+ PIC_AddEvent(VGA_DrawPart,(float)vga.draw.delay.parts + draw_skip,vga.draw.parts_lines);
+ break;
+ case DRAWLINE:
+ case EGALINE:
+ if (GCC_UNLIKELY(vga.draw.lines_done < vga.draw.lines_total)) {
+ LOG(LOG_VGAMISC,LOG_NORMAL)( "Lines left: %d",
+ vga.draw.lines_total-vga.draw.lines_done);
+ if (vga.draw.mode==EGALINE) PIC_RemoveEvents(VGA_DrawEGASingleLine);
+ else PIC_RemoveEvents(VGA_DrawSingleLine);
+ RENDER_EndUpdate(true);
+ }
+ vga.draw.lines_done = 0;
+ if (vga.draw.mode==EGALINE)
+ PIC_AddEvent(VGA_DrawEGASingleLine,(float)(vga.draw.delay.htotal/4.0 + draw_skip));
+ else PIC_AddEvent(VGA_DrawSingleLine,(float)(vga.draw.delay.htotal/4.0 + draw_skip));
+ break;
+ }
+}
+
+void VGA_CheckScanLength(void) {
+ switch (vga.mode) {
+ case M_EGA:
+ case M_LIN4:
+ vga.draw.address_add=vga.config.scan_len*16;
+ break;
+ case M_VGA:
+ case M_LIN8:
+ case M_LIN15:
+ case M_LIN16:
+ case M_LIN32:
+ vga.draw.address_add=vga.config.scan_len*8;
+ break;
+ case M_TEXT:
+ vga.draw.address_add=vga.config.scan_len*4;
+ break;
+ case M_CGA2:
+ case M_CGA4:
+ case M_CGA16:
+ vga.draw.address_add=80;
+ return;
+ case M_TANDY2:
+ vga.draw.address_add=vga.draw.blocks/4;
+ break;
+ case M_TANDY4:
+ vga.draw.address_add=vga.draw.blocks;
+ break;
+ case M_TANDY16:
+ vga.draw.address_add=vga.draw.blocks;
+ break;
+ case M_TANDY_TEXT:
+ vga.draw.address_add=vga.draw.blocks*2;
+ break;
+ case M_HERC_TEXT:
+ vga.draw.address_add=vga.draw.blocks*2;
+ break;
+ case M_HERC_GFX:
+ vga.draw.address_add=vga.draw.blocks;
+ break;
+ default:
+ vga.draw.address_add=vga.draw.blocks*8;
+ break;
+ }
+}
+
+void VGA_ActivateHardwareCursor(void) {
+ bool hwcursor_active=false;
+ if (svga.hardware_cursor_active) {
+ if (svga.hardware_cursor_active()) hwcursor_active=true;
+ }
+ if (hwcursor_active) {
+ switch(vga.mode) {
+ case M_LIN32:
+ VGA_DrawLine=VGA_Draw_LIN32_Line_HWMouse;
+ break;
+ case M_LIN15:
+ case M_LIN16:
+ VGA_DrawLine=VGA_Draw_LIN16_Line_HWMouse;
+ break;
+ default:
+ VGA_DrawLine=VGA_Draw_VGA_Line_HWMouse;
+ }
+ } else {
+ VGA_DrawLine=VGA_Draw_Linear_Line;
+ }
+}
+
+void VGA_SetupDrawing(Bitu /*val*/) {
+ if (vga.mode==M_ERROR) {
+ PIC_RemoveEvents(VGA_VerticalTimer);
+ PIC_RemoveEvents(VGA_PanningLatch);
+ PIC_RemoveEvents(VGA_DisplayStartLatch);
+ return;
+ }
+ // set the drawing mode
+ switch (machine) {
+ case MCH_CGA:
+ case MCH_PCJR:
+ case MCH_TANDY:
+ vga.draw.mode = DRAWLINE;
+ break;
+ case MCH_EGA:
+ // Note: The Paradise SVGA uses the same panning mechanism as EGA
+ vga.draw.mode = EGALINE;
+ break;
+ case MCH_VGA:
+ if (svgaCard==SVGA_None) {
+ vga.draw.mode = DRAWLINE;
+ break;
+ }
+ // fall-through
+ default:
+ vga.draw.mode = PART;
+ break;
+ }
+
+ /* Calculate the FPS for this screen */
+ double fps; Bitu clock;
+ Bitu htotal, hdend, hbstart, hbend, hrstart, hrend;
+ Bitu vtotal, vdend, vbstart, vbend, vrstart, vrend;
+ Bitu vblank_skip;
+ if (IS_EGAVGA_ARCH) {
+ htotal = vga.crtc.horizontal_total;
+ hdend = vga.crtc.horizontal_display_end;
+ hbend = vga.crtc.end_horizontal_blanking&0x1F;
+ hbstart = vga.crtc.start_horizontal_blanking;
+ hrstart = vga.crtc.start_horizontal_retrace;
+
+ vtotal= vga.crtc.vertical_total | ((vga.crtc.overflow & 1) << 8);
+ vdend = vga.crtc.vertical_display_end | ((vga.crtc.overflow & 2)<<7);
+ vbstart = vga.crtc.start_vertical_blanking | ((vga.crtc.overflow & 0x08) << 5);
+ vrstart = vga.crtc.vertical_retrace_start + ((vga.crtc.overflow & 0x04) << 6);
+
+ if (IS_VGA_ARCH) {
+ // additional bits only present on vga cards
+ htotal |= (vga.s3.ex_hor_overflow & 0x1) << 8;
+ htotal += 3;
+ hdend |= (vga.s3.ex_hor_overflow & 0x2) << 7;
+ hbend |= (vga.crtc.end_horizontal_retrace&0x80) >> 2;
+ hbstart |= (vga.s3.ex_hor_overflow & 0x4) << 6;
+ hrstart |= (vga.s3.ex_hor_overflow & 0x10) << 4;
+
+ vtotal |= (vga.crtc.overflow & 0x20) << 4;
+ vtotal |= (vga.s3.ex_ver_overflow & 0x1) << 10;
+ vdend |= (vga.crtc.overflow & 0x40) << 3;
+ vdend |= (vga.s3.ex_ver_overflow & 0x2) << 9;
+ vbstart |= (vga.crtc.maximum_scan_line & 0x20) << 4;
+ vbstart |= (vga.s3.ex_ver_overflow & 0x4) << 8;
+ vrstart |= ((vga.crtc.overflow & 0x80) << 2);
+ vrstart |= (vga.s3.ex_ver_overflow & 0x10) << 6;
+ vbend = vga.crtc.end_vertical_blanking & 0x7f;
+ } else { // EGA
+ vbend = vga.crtc.end_vertical_blanking & 0x1f;
+ }
+ htotal += 2;
+ vtotal += 2;
+ hdend += 1;
+ vdend += 1;
+
+ hbend = hbstart + ((hbend - hbstart) & 0x3F);
+ hrend = vga.crtc.end_horizontal_retrace & 0x1f;
+ hrend = (hrend - hrstart) & 0x1f;
+
+ if ( !hrend ) hrend = hrstart + 0x1f + 1;
+ else hrend = hrstart + hrend;
+
+ vrend = vga.crtc.vertical_retrace_end & 0xF;
+ vrend = ( vrend - vrstart)&0xF;
+
+ if ( !vrend) vrend = vrstart + 0xf + 1;
+ else vrend = vrstart + vrend;
+
+ // Special case vbstart==0:
+ // Most graphics cards agree that lines zero to vbend are
+ // blanked. ET4000 doesn't blank at all if vbstart==vbend.
+ // ET3000 blanks lines 1 to vbend (255/6 lines).
+ if (vbstart != 0) {
+ vbstart += 1;
+ vbend = (vbend - vbstart) & 0x7f;
+ if ( !vbend) vbend = vbstart + 0x7f + 1;
+ else vbend = vbstart + vbend;
+ }
+ vbend++;
+
+ if (svga.get_clock) {
+ clock = svga.get_clock();
+ } else {
+ switch ((vga.misc_output >> 2) & 3) {
+ case 0:
+ clock = (machine==MCH_EGA) ? 14318180 : 25175000;
+ break;
+ case 1:
+ default:
+ clock = (machine==MCH_EGA) ? 16257000 : 28322000;
+ break;
+ }
+ }
+
+ /* Check for 8 for 9 character clock mode */
+ if (vga.seq.clocking_mode & 1 ) clock/=8; else clock/=9;
+ /* Check for pixel doubling, master clock/2 */
+ if (vga.seq.clocking_mode & 0x8) {
+ htotal*=2;
+ }
+ vga.draw.address_line_total=(vga.crtc.maximum_scan_line&0x1f)+1;
+ if (IS_VGA_ARCH && (svgaCard==SVGA_None) && (vga.mode==M_EGA || vga.mode==M_VGA)) {
+ // vgaonly; can't use with CGA because these use address_line for their
+ // own purposes.
+ // Set the low resolution modes to have as many lines as are scanned -
+ // Quite a few demos change the max_scanline register at display time
+ // to get SFX: Majic12 show, Magic circle, Copper, GBU, Party91
+ if ( vga.crtc.maximum_scan_line&0x80) vga.draw.address_line_total*=2;
+ vga.draw.double_scan=false;
+ }
+ else if (IS_VGA_ARCH) vga.draw.double_scan=(vga.crtc.maximum_scan_line&0x80)>0;
+ else vga.draw.double_scan=(vtotal==262);
+ } else {
+ htotal = vga.other.htotal + 1;
+ hdend = vga.other.hdend;
+ hbstart = hdend;
+ hbend = htotal;
+ hrstart = vga.other.hsyncp;
+ hrend = hrstart + vga.other.hsyncw;
+
+ vga.draw.address_line_total = vga.other.max_scanline + 1;
+ vtotal = vga.draw.address_line_total * (vga.other.vtotal+1)+vga.other.vadjust;
+ vdend = vga.draw.address_line_total * vga.other.vdend;
+ vrstart = vga.draw.address_line_total * vga.other.vsyncp;
+ vrend = vrstart + 16; // vsync width is fixed to 16 lines on the MC6845 TODO Tandy
+ vbstart = vdend;
+ vbend = vtotal;
+ vga.draw.double_scan=false;
+ switch (machine) {
+ case MCH_CGA:
+ case TANDY_ARCH_CASE:
+ clock=((vga.tandy.mode_control & 1) ? 14318180 : (14318180/2))/8;
+ break;
+ case MCH_HERC:
+ if (vga.herc.mode_control & 0x2) clock=16000000/16;
+ else clock=16000000/8;
+ break;
+ default:
+ clock = 14318180;
+ break;
+ }
+ vga.draw.delay.hdend = hdend*1000.0/clock; //in milliseconds
+ }
+#if C_DEBUG
+ LOG(LOG_VGA,LOG_NORMAL)("h total %d end %d blank (%d/%d) retrace (%d/%d)",
+ htotal, hdend, hbstart, hbend, hrstart, hrend );
+ LOG(LOG_VGA,LOG_NORMAL)("v total %d end %d blank (%d/%d) retrace (%d/%d)",
+ vtotal, vdend, vbstart, vbend, vrstart, vrend );
+#endif
+ if (!htotal) return;
+ if (!vtotal) return;
+
+ // The screen refresh frequency
+ fps=(double)clock/(vtotal*htotal);
+ // Horizontal total (that's how long a line takes with whistles and bells)
+ vga.draw.delay.htotal = htotal*1000.0/clock; //in milliseconds
+ // Start and End of horizontal blanking
+ vga.draw.delay.hblkstart = hbstart*1000.0/clock; //in milliseconds
+ vga.draw.delay.hblkend = hbend*1000.0/clock;
+ // Start and End of horizontal retrace
+ vga.draw.delay.hrstart = hrstart*1000.0/clock;
+ vga.draw.delay.hrend = hrend*1000.0/clock;
+ // Start and End of vertical blanking
+ vga.draw.delay.vblkstart = vbstart * vga.draw.delay.htotal;
+ vga.draw.delay.vblkend = vbend * vga.draw.delay.htotal;
+ // Start and End of vertical retrace pulse
+ vga.draw.delay.vrstart = vrstart * vga.draw.delay.htotal;
+ vga.draw.delay.vrend = vrend * vga.draw.delay.htotal;
+
+ // Vertical blanking tricks
+ vblank_skip = 0;
+ if (IS_VGA_ARCH) { // others need more investigation
+ if (vbstart < vtotal) { // There will be no blanking at all otherwise
+ if (vbend > vtotal) {
+ // blanking wraps to the start of the screen
+ vblank_skip = vbend&0x7f;
+
+ // on blanking wrap to 0, the first line is not blanked
+ // this is used by the S3 BIOS and other S3 drivers in some SVGA modes
+ if ((vbend&0x7f)==1) vblank_skip = 0;
+
+ // it might also cut some lines off the bottom
+ if (vbstart < vdend) {
+ vdend = vbstart;
+ }
+ LOG(LOG_VGA,LOG_WARN)("Blanking wrap to line %d", vblank_skip);
+ } else if (vbstart<=1) {
+ // blanking is used to cut lines at the start of the screen
+ vblank_skip = vbend;
+ LOG(LOG_VGA,LOG_WARN)("Upper %d lines of the screen blanked", vblank_skip);
+ } else if (vbstart < vdend) {
+ if (vbend < vdend) {
+ // the game wants a black bar somewhere on the screen
+ LOG(LOG_VGA,LOG_WARN)("Unsupported blanking: line %d-%d",vbstart,vbend);
+ } else {
+ // blanking is used to cut off some lines from the bottom
+ vdend = vbstart;
+ }
+ }
+ vdend -= vblank_skip;
+ }
+ }
+ // Display end
+ vga.draw.delay.vdend = vdend * vga.draw.delay.htotal;
+
+ // EGA frequency dependent monitor palette
+ if (machine == MCH_EGA) {
+ if (vga.misc_output & 1) {
+ // EGA card is in color mode
+ if ((1.0f/vga.draw.delay.htotal) > 19.0f) {
+ // 64 color EGA mode
+ VGA_ATTR_SetEGAMonitorPalette(EGA);
+ } else {
+ // 16 color CGA mode compatibility
+ VGA_ATTR_SetEGAMonitorPalette(CGA);
+ }
+ } else {
+ // EGA card in monochrome mode
+ // It is not meant to be autodetected that way, you either
+ // have a monochrome or color monitor connected and
+ // the EGA switches configured appropriately.
+ // But this would only be a problem if a program sets
+ // the adapter to monochrome mode and still expects color output.
+ // Such a program should be shot to the moon...
+ VGA_ATTR_SetEGAMonitorPalette(MONO);
+ }
+ }
+
+ vga.draw.parts_total=VGA_PARTS;
+ /*
+ 6 Horizontal Sync Polarity. Negative if set
+ 7 Vertical Sync Polarity. Negative if set
+ Bit 6-7 indicates the number of lines on the display:
+ 1: 400, 2: 350, 3: 480
+ */
+ //Try to determine the pixel size, aspect correct is based around square pixels
+
+ //Base pixel width around 100 clocks horizontal
+ //For 9 pixel text modes this should be changed, but we don't support that anyway :)
+ //Seems regular vga only listens to the 9 char pixel mode with character mode enabled
+ double pwidth = (machine==MCH_EGA) ? (114.0 / htotal) : (100.0 / htotal);
+ //Base pixel height around vertical totals of modes that have 100 clocks horizontal
+ //Different sync values gives different scaling of the whole vertical range
+ //VGA monitor just seems to thighten or widen the whole vertical range
+ double pheight;
+ double target_total = (machine==MCH_EGA) ? 262.0 : 449.0;
+ Bitu sync = vga.misc_output >> 6;
+ switch ( sync ) {
+ case 0: // This is not defined in vga specs,
+ // Kiet, seems to be slightly less than 350 on my monitor
+ //340 line mode, filled with 449 total
+ pheight = (480.0 / 340.0) * ( target_total / vtotal );
+ break;
+ case 1: //400 line mode, filled with 449 total
+ pheight = (480.0 / 400.0) * ( target_total / vtotal );
+ break;
+ case 2: //350 line mode, filled with 449 total
+ //This mode seems to get regular 640x400 timing and goes for a loong retrace
+ //Depends on the monitor to stretch the screen
+ pheight = (480.0 / 350.0) * ( target_total / vtotal );
+ break;
+ case 3: //480 line mode, filled with 525 total
+ default:
+ //Allow 527 total ModeX to have exact 1:1 aspect
+ target_total = (vga.mode==M_VGA && vtotal==527) ? 527.0 : 525.0;
+ pheight = (480.0 / 480.0) * ( target_total / vtotal );
+ break;
+ }
+
+ double aspect_ratio = pheight / pwidth;
+
+ vga.draw.delay.parts = vga.draw.delay.vdend/vga.draw.parts_total;
+ vga.draw.resizing=false;
+ vga.draw.vret_triggered=false;
+
+ //Check to prevent useless black areas
+ if (hbstart<hdend) hdend=hbstart;
+ if ((!IS_VGA_ARCH) && (vbstart<vdend)) vdend=vbstart;
+
+
+ Bitu width=hdend;
+ Bitu height=vdend;
+ bool doubleheight=false;
+ bool doublewidth=false;
+
+ //Set the bpp
+ Bitu bpp;
+ switch (vga.mode) {
+ case M_LIN15:
+ bpp = 15;
+ break;
+ case M_LIN16:
+ bpp = 16;
+ break;
+ case M_LIN32:
+ bpp = 32;
+ break;
+ default:
+ bpp = 8;
+ break;
+ }
+ vga.draw.linear_base = vga.mem.linear;
+ vga.draw.linear_mask = vga.vmemwrap - 1;
+ switch (vga.mode) {
+ case M_VGA:
+ doublewidth=true;
+ width<<=2;
+ if ((IS_VGA_ARCH) && (svgaCard==SVGA_None)) {
+ bpp=16;
+ VGA_DrawLine = VGA_Draw_Xlat16_Linear_Line;
+ } else VGA_DrawLine = VGA_Draw_Linear_Line;
+ break;
+ case M_LIN8:
+ if (vga.crtc.mode_control & 0x8)
+ width >>=1;
+ else if (svgaCard == SVGA_S3Trio && !(vga.s3.reg_3a&0x10)) {
+ doublewidth=true;
+ width >>=1;
+ }
+ // fall-through
+ case M_LIN32:
+ width<<=3;
+ if (vga.crtc.mode_control & 0x8)
+ doublewidth = true;
+ /* Use HW mouse cursor drawer if enabled */
+ VGA_ActivateHardwareCursor();
+ break;
+ case M_LIN15:
+ case M_LIN16:
+ // 15/16 bpp modes double the horizontal values
+ width<<=2;
+ if ((vga.crtc.mode_control & 0x8) || (svgaCard == SVGA_S3Trio && (vga.s3.pll.cmd & 0x10)))
+ doublewidth = true;
+ /* Use HW mouse cursor drawer if enabled */
+ VGA_ActivateHardwareCursor();
+ break;
+ case M_LIN4:
+ doublewidth=(vga.seq.clocking_mode & 0x8) > 0;
+ vga.draw.blocks = width;
+ width<<=3;
+ VGA_DrawLine=VGA_Draw_Linear_Line;
+ vga.draw.linear_base = vga.fastmem;
+ vga.draw.linear_mask = (vga.vmemwrap<<1) - 1;
+ break;
+ case M_EGA:
+ doublewidth=(vga.seq.clocking_mode & 0x8) > 0;
+ vga.draw.blocks = width;
+ width<<=3;
+ if ((IS_VGA_ARCH) && (svgaCard==SVGA_None)) {
+ // This would also be required for EGA in Spacepigs Megademo
+ bpp=16;
+ VGA_DrawLine = VGA_Draw_Xlat16_Linear_Line;
+ } else VGA_DrawLine=VGA_Draw_Linear_Line;
+
+ vga.draw.linear_base = vga.fastmem;
+ vga.draw.linear_mask = (vga.vmemwrap<<1) - 1;
+ break;
+ case M_CGA16:
+ aspect_ratio=1.2;
+ doubleheight=true;
+ vga.draw.blocks=width*2;
+ width<<=4;
+ VGA_DrawLine=VGA_Draw_CGA16_Line;
+ break;
+ case M_CGA4:
+ doublewidth=true;
+ vga.draw.blocks=width*2;
+ width<<=3;
+ VGA_DrawLine=VGA_Draw_2BPP_Line;
+ break;
+ case M_CGA2:
+ doubleheight=true;
+ vga.draw.blocks=2*width;
+ width<<=3;
+ VGA_DrawLine=VGA_Draw_1BPP_Line;
+ break;
+ case M_TEXT:
+ vga.draw.blocks=width;
+ doublewidth=(vga.seq.clocking_mode & 0x8) > 0;
+ if ((IS_VGA_ARCH) && (svgaCard==SVGA_None)) {
+ // vgaonly: allow 9-pixel wide fonts
+ if (vga.seq.clocking_mode&0x01) {
+ vga.draw.char9dot = false;
+ width*=8;
+ } else {
+ vga.draw.char9dot = true;
+ width*=9;
+ aspect_ratio*=1.125;
+ }
+ VGA_DrawLine=VGA_TEXT_Xlat16_Draw_Line;
+ bpp=16;
+ } else {
+ // not vgaonly: force 8-pixel wide fonts
+ width*=8; // 8 bit wide text font
+ vga.draw.char9dot = false;
+ VGA_DrawLine=VGA_TEXT_Draw_Line;
+ }
+ break;
+ case M_HERC_GFX:
+ vga.draw.blocks=width*2;
+ width*=16;
+ aspect_ratio=((double)width/(double)height)*(3.0/4.0);
+ VGA_DrawLine=VGA_Draw_1BPP_Line;
+ break;
+ case M_TANDY2:
+ aspect_ratio=1.2;
+ doubleheight=true;
+ if (machine==MCH_PCJR) doublewidth=(vga.tandy.gfx_control & 0x8)==0x00;
+ else doublewidth=(vga.tandy.mode_control & 0x10)==0;
+ vga.draw.blocks=width * (doublewidth ? 4:8);
+ width=vga.draw.blocks*2;
+ VGA_DrawLine=VGA_Draw_1BPP_Line;
+ break;
+ case M_TANDY4:
+ aspect_ratio=1.2;
+ doubleheight=true;
+ if (machine==MCH_TANDY) doublewidth=(vga.tandy.mode_control & 0x10)==0;
+ else doublewidth=(vga.tandy.mode_control & 0x01)==0x00;
+ vga.draw.blocks=width * 2;
+ width=vga.draw.blocks*4;
+ if ((machine==MCH_TANDY && (vga.tandy.gfx_control & 0x8)) ||
+ (machine==MCH_PCJR && (vga.tandy.mode_control==0x0b)))
+ VGA_DrawLine=VGA_Draw_2BPPHiRes_Line;
+ else VGA_DrawLine=VGA_Draw_2BPP_Line;
+ break;
+ case M_TANDY16:
+ aspect_ratio=1.2;
+ doubleheight=true;
+ vga.draw.blocks=width*2;
+ if (vga.tandy.mode_control & 0x1) {
+ if (( machine==MCH_TANDY ) && ( vga.tandy.mode_control & 0x10 )) {
+ doublewidth = false;
+ vga.draw.blocks*=2;
+ width=vga.draw.blocks*2;
+ } else {
+ doublewidth = true;
+ width=vga.draw.blocks*2;
+ }
+ VGA_DrawLine=VGA_Draw_4BPP_Line;
+ } else {
+ doublewidth=true;
+ width=vga.draw.blocks*4;
+ VGA_DrawLine=VGA_Draw_4BPP_Line_Double;
+ }
+ break;
+ case M_TANDY_TEXT:
+ doublewidth=(vga.tandy.mode_control & 0x1)==0;
+ aspect_ratio=1.2;
+ doubleheight=true;
+ vga.draw.blocks=width;
+ width<<=3;
+ VGA_DrawLine=VGA_TEXT_Draw_Line;
+ break;
+ case M_HERC_TEXT:
+ aspect_ratio=((double)480)/((double)350);
+ vga.draw.blocks=width;
+ width<<=3;
+ VGA_DrawLine=VGA_TEXT_Herc_Draw_Line;
+ break;
+ default:
+ LOG(LOG_VGA,LOG_ERROR)("Unhandled VGA mode %d while checking for resolution",vga.mode);
+ break;
+ }
+ VGA_CheckScanLength();
+ if (vga.draw.double_scan) {
+ if (IS_VGA_ARCH) {
+ vga.draw.vblank_skip /= 2;
+ height/=2;
+ }
+ doubleheight=true;
+ }
+ vga.draw.vblank_skip = vblank_skip;
+
+ if (!(IS_VGA_ARCH && (svgaCard==SVGA_None) && (vga.mode==M_EGA || vga.mode==M_VGA))) {
+ //Only check for extra double height in vga modes
+ //(line multiplying by address_line_total)
+ if (!doubleheight && (vga.mode<M_TEXT) && !(vga.draw.address_line_total & 1)) {
+ vga.draw.address_line_total/=2;
+ doubleheight=true;
+ height/=2;
+ }
+ }
+ vga.draw.lines_total=height;
+ vga.draw.parts_lines=vga.draw.lines_total/vga.draw.parts_total;
+ vga.draw.line_length = width * ((bpp + 1) / 8);
+#ifdef VGA_KEEP_CHANGES
+ vga.changes.active = false;
+ vga.changes.frame = 0;
+ vga.changes.writeMask = 1;
+#endif
+ /*
+ Cheap hack to just make all > 640x480 modes have 4:3 aspect ratio
+ */
+ if ( width >= 640 && height >= 480 ) {
+ aspect_ratio = ((float)width / (float)height) * ( 3.0 / 4.0);
+ }
+// LOG_MSG("ht %d vt %d ratio %f", htotal, vtotal, aspect_ratio );
+
+ bool fps_changed = false;
+ // need to change the vertical timing?
+ if (fabs(vga.draw.delay.vtotal - 1000.0 / fps) > 0.0001) {
+ fps_changed = true;
+ vga.draw.delay.vtotal = 1000.0 / fps;
+ VGA_KillDrawing();
+ PIC_RemoveEvents(VGA_Other_VertInterrupt);
+ PIC_RemoveEvents(VGA_VerticalTimer);
+ PIC_RemoveEvents(VGA_PanningLatch);
+ PIC_RemoveEvents(VGA_DisplayStartLatch);
+ VGA_VerticalTimer(0);
+ }
+
+#if C_DEBUG
+ LOG(LOG_VGA,LOG_NORMAL)("h total %2.5f (%3.2fkHz) blank(%02.5f/%02.5f) retrace(%02.5f/%02.5f)",
+ vga.draw.delay.htotal,(1.0/vga.draw.delay.htotal),
+ vga.draw.delay.hblkstart,vga.draw.delay.hblkend,
+ vga.draw.delay.hrstart,vga.draw.delay.hrend);
+ LOG(LOG_VGA,LOG_NORMAL)("v total %2.5f (%3.2fHz) blank(%02.5f/%02.5f) retrace(%02.5f/%02.5f)",
+ vga.draw.delay.vtotal,(1000.0/vga.draw.delay.vtotal),
+ vga.draw.delay.vblkstart,vga.draw.delay.vblkend,
+ vga.draw.delay.vrstart,vga.draw.delay.vrend);
+#endif
+
+ // need to resize the output window?
+ if ((width != vga.draw.width) ||
+ (height != vga.draw.height) ||
+ (vga.draw.doublewidth != doublewidth) ||
+ (vga.draw.doubleheight != doubleheight) ||
+ (fabs(aspect_ratio - vga.draw.aspect_ratio) > 0.0001) ||
+ (vga.draw.bpp != bpp) || fps_changed) {
+
+ VGA_KillDrawing();
+
+ vga.draw.width = width;
+ vga.draw.height = height;
+ vga.draw.doublewidth = doublewidth;
+ vga.draw.doubleheight = doubleheight;
+ vga.draw.aspect_ratio = aspect_ratio;
+ vga.draw.bpp = bpp;
+ if (doubleheight) vga.draw.lines_scaled=2;
+ else vga.draw.lines_scaled=1;
+#if C_DEBUG
+ LOG(LOG_VGA,LOG_NORMAL)("Width %d, Height %d, fps %f",width,height,fps);
+ LOG(LOG_VGA,LOG_NORMAL)("%s width, %s height aspect %f",
+ doublewidth ? "double":"normal",doubleheight ? "double":"normal",aspect_ratio);
+#endif
+ if (!vga.draw.vga_override)
+ RENDER_SetSize(width, height, bpp, (float)fps, aspect_ratio,
+ doublewidth, doubleheight);
+ }
+}
+
+void VGA_KillDrawing(void) {
+ PIC_RemoveEvents(VGA_DrawPart);
+ PIC_RemoveEvents(VGA_DrawSingleLine);
+ PIC_RemoveEvents(VGA_DrawEGASingleLine);
+ vga.draw.parts_left = 0;
+ vga.draw.lines_done = ~0;
+ if (!vga.draw.vga_override) RENDER_EndUpdate(true);
+}
+
+void VGA_SetOverride(bool vga_override) {
+ if (vga.draw.vga_override!=vga_override) {
+
+ if (vga_override) {
+ VGA_KillDrawing();
+ vga.draw.vga_override=true;
+ } else {
+ vga.draw.vga_override=false;
+ vga.draw.width=0; // change it so the output window gets updated
+ VGA_SetupDrawing(0);
+ }
+ }
+}
diff --git a/src/hardware/vga_gfx.cpp b/src/hardware/vga_gfx.cpp
new file mode 100644
index 000000000..86d3520b3
--- /dev/null
+++ b/src/hardware/vga_gfx.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "inout.h"
+#include "vga.h"
+
+#define gfx(blah) vga.gfx.blah
+static bool index9warned=false;
+
+static void write_p3ce(Bitu port,Bitu val,Bitu iolen) {
+ gfx(index)=val & 0x0f;
+}
+
+static Bitu read_p3ce(Bitu port,Bitu iolen) {
+ return gfx(index);
+}
+
+static void write_p3cf(Bitu port,Bitu val,Bitu iolen) {
+ switch (gfx(index)) {
+ case 0: /* Set/Reset Register */
+ gfx(set_reset)=val & 0x0f;
+ vga.config.full_set_reset=FillTable[val & 0x0f];
+ vga.config.full_enable_and_set_reset=vga.config.full_set_reset &
+ vga.config.full_enable_set_reset;
+ /*
+ 0 If in Write Mode 0 and bit 0 of 3CEh index 1 is set a write to
+ display memory will set all the bits in plane 0 of the byte to this
+ bit, if the corresponding bit is set in the Map Mask Register (3CEh
+ index 8).
+ 1 Same for plane 1 and bit 1 of 3CEh index 1.
+ 2 Same for plane 2 and bit 2 of 3CEh index 1.
+ 3 Same for plane 3 and bit 3 of 3CEh index 1.
+ */
+// LOG_DEBUG("Set Reset = %2X",val);
+ break;
+ case 1: /* Enable Set/Reset Register */
+ gfx(enable_set_reset)=val & 0x0f;
+ vga.config.full_enable_set_reset=FillTable[val & 0x0f];
+ vga.config.full_not_enable_set_reset=~vga.config.full_enable_set_reset;
+ vga.config.full_enable_and_set_reset=vga.config.full_set_reset &
+ vga.config.full_enable_set_reset;
+// if (gfx(enable_set_reset)) vga.config.mh_mask|=MH_SETRESET else vga.config.mh_mask&=~MH_SETRESET;
+ break;
+ case 2: /* Color Compare Register */
+ gfx(color_compare)=val & 0x0f;
+ /*
+ 0-3 In Read Mode 1 each pixel at the address of the byte read is compared
+ to this color and the corresponding bit in the output set to 1 if
+ they match, 0 if not. The Color Don't Care Register (3CEh index 7)
+ can exclude bitplanes from the comparison.
+ */
+ vga.config.color_compare=val & 0xf;
+// LOG_DEBUG("Color Compare = %2X",val);
+ break;
+ case 3: /* Data Rotate */
+ gfx(data_rotate)=val;
+ vga.config.data_rotate=val & 7;
+// if (val) vga.config.mh_mask|=MH_ROTATEOP else vga.config.mh_mask&=~MH_ROTATEOP;
+ vga.config.raster_op=(val>>3) & 3;
+ /*
+ 0-2 Number of positions to rotate data right before it is written to
+ display memory. Only active in Write Mode 0.
+ 3-4 In Write Mode 2 this field controls the relation between the data
+ written from the CPU, the data latched from the previous read and the
+ data written to display memory:
+ 0: CPU Data is written unmodified
+ 1: CPU data is ANDed with the latched data
+ 2: CPU data is ORed with the latch data.
+ 3: CPU data is XORed with the latched data.
+ */
+ break;
+ case 4: /* Read Map Select Register */
+ /* 0-1 number of the plane Read Mode 0 will read from */
+ gfx(read_map_select)=val & 0x03;
+ vga.config.read_map_select=val & 0x03;
+// LOG_DEBUG("Read Map %2X",val);
+ break;
+ case 5: /* Mode Register */
+ if ((gfx(mode) ^ val) & 0xf0) {
+ gfx(mode)=val;
+ VGA_DetermineMode();
+ } else gfx(mode)=val;
+ vga.config.write_mode=val & 3;
+ vga.config.read_mode=(val >> 3) & 1;
+// LOG_DEBUG("Write Mode %d Read Mode %d val %d",vga.config.write_mode,vga.config.read_mode,val);
+ /*
+ 0-1 Write Mode: Controls how data from the CPU is transformed before
+ being written to display memory:
+ 0: Mode 0 works as a Read-Modify-Write operation.
+ First a read access loads the data latches of the VGA with the
+ value in video memory at the addressed location. Then a write
+ access will provide the destination address and the CPU data
+ byte. The data written is modified by the function code in the
+ Data Rotate register (3CEh index 3) as a function of the CPU
+ data and the latches, then data is rotated as specified by the
+ same register.
+ 1: Mode 1 is used for video to video transfers.
+ A read access will load the data latches with the contents of
+ the addressed byte of video memory. A write access will write
+ the contents of the latches to the addressed byte. Thus a single
+ MOVSB instruction can copy all pixels in the source address byte
+ to the destination address.
+ 2: Mode 2 writes a color to all pixels in the addressed byte of
+ video memory. Bit 0 of the CPU data is written to plane 0 et
+ cetera. Individual bits can be enabled or disabled through the
+ Bit Mask register (3CEh index 8).
+ 3: Mode 3 can be used to fill an area with a color and pattern. The
+ CPU data is rotated according to 3CEh index 3 bits 0-2 and anded
+ with the Bit Mask Register (3CEh index 8). For each bit in the
+ result the corresponding pixel is set to the color in the
+ Set/Reset Register (3CEh index 0 bits 0-3) if the bit is set and
+ to the contents of the processor latch if the bit is clear.
+ 3 Read Mode
+ 0: Data is read from one of 4 bit planes depending on the Read Map
+ Select Register (3CEh index 4).
+ 1: Data returned is a comparison between the 8 pixels occupying the
+ read byte and the color in the Color Compare Register (3CEh
+ index 2). A bit is set if the color of the corresponding pixel
+ matches the register.
+ 4 Enables Odd/Even mode if set (See 3C4h index 4 bit 2).
+ 5 Enables CGA style 4 color pixels using even/odd bit pairs if set.
+ 6 Enables 256 color mode if set.
+ */
+ break;
+ case 6: /* Miscellaneous Register */
+ if ((gfx(miscellaneous) ^ val) & 0x0c) {
+ gfx(miscellaneous)=val;
+ VGA_DetermineMode();
+ } else gfx(miscellaneous)=val;
+ VGA_SetupHandlers();
+ /*
+ 0 Indicates Graphics Mode if set, Alphanumeric mode else.
+ 1 Enables Odd/Even mode if set.
+ 2-3 Memory Mapping:
+ 0: use A000h-BFFFh
+ 1: use A000h-AFFFh VGA Graphics modes
+ 2: use B000h-B7FFh Monochrome modes
+ 3: use B800h-BFFFh CGA modes
+ */
+ break;
+ case 7: /* Color Don't Care Register */
+ gfx(color_dont_care)=val & 0x0f;
+ /*
+ 0 Ignore bit plane 0 in Read mode 1 if clear.
+ 1 Ignore bit plane 1 in Read mode 1 if clear.
+ 2 Ignore bit plane 2 in Read mode 1 if clear.
+ 3 Ignore bit plane 3 in Read mode 1 if clear.
+ */
+ vga.config.color_dont_care=val & 0xf;
+// LOG_DEBUG("Color don't care = %2X",val);
+ break;
+ case 8: /* Bit Mask Register */
+ gfx(bit_mask)=val;
+ vga.config.full_bit_mask=ExpandTable[val];
+// LOG_DEBUG("Bit mask %2X",val);
+ /*
+ 0-7 Each bit if set enables writing to the corresponding bit of a byte in
+ display memory.
+ */
+ break;
+ default:
+ if (svga.write_p3cf) {
+ svga.write_p3cf(gfx(index), val, iolen);
+ break;
+ }
+ if (gfx(index) == 9 && !index9warned) {
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:3CF:Write %2X to illegal index 9",val);
+ index9warned=true;
+ break;
+ }
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:3CF:Write %2X to illegal index %2X",val,gfx(index));
+ break;
+ }
+}
+
+static Bitu read_p3cf(Bitu port,Bitu iolen) {
+ switch (gfx(index)) {
+ case 0: /* Set/Reset Register */
+ return gfx(set_reset);
+ case 1: /* Enable Set/Reset Register */
+ return gfx(enable_set_reset);
+ case 2: /* Color Compare Register */
+ return gfx(color_compare);
+ case 3: /* Data Rotate */
+ return gfx(data_rotate);
+ case 4: /* Read Map Select Register */
+ return gfx(read_map_select);
+ case 5: /* Mode Register */
+ return gfx(mode);
+ case 6: /* Miscellaneous Register */
+ return gfx(miscellaneous);
+ case 7: /* Color Don't Care Register */
+ return gfx(color_dont_care);
+ case 8: /* Bit Mask Register */
+ return gfx(bit_mask);
+ default:
+ if (svga.read_p3cf)
+ return svga.read_p3cf(gfx(index), iolen);
+ LOG(LOG_VGAMISC,LOG_NORMAL)("Reading from illegal index %2X in port %4X",static_cast<Bit32u>(gfx(index)),port);
+ break;
+ }
+ return 0; /* Compiler happy */
+}
+
+
+
+void VGA_SetupGFX(void) {
+ if (IS_EGAVGA_ARCH) {
+ IO_RegisterWriteHandler(0x3ce,write_p3ce,IO_MB);
+ IO_RegisterWriteHandler(0x3cf,write_p3cf,IO_MB);
+ if (IS_VGA_ARCH) {
+ IO_RegisterReadHandler(0x3ce,read_p3ce,IO_MB);
+ IO_RegisterReadHandler(0x3cf,read_p3cf,IO_MB);
+ }
+ }
+}
+
+
diff --git a/src/hardware/vga_memory.cpp b/src/hardware/vga_memory.cpp
new file mode 100644
index 000000000..e52bff043
--- /dev/null
+++ b/src/hardware/vga_memory.cpp
@@ -0,0 +1,988 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include "dosbox.h"
+#include "mem.h"
+#include "vga.h"
+#include "paging.h"
+#include "pic.h"
+#include "inout.h"
+#include "setup.h"
+
+
+#ifndef C_VGARAM_CHECKED
+#define C_VGARAM_CHECKED 1
+#endif
+
+#if C_VGARAM_CHECKED
+// Checked linear offset
+#define CHECKED(v) ((v)&(vga.vmemwrap-1))
+// Checked planar offset (latched access)
+#define CHECKED2(v) ((v)&((vga.vmemwrap>>2)-1))
+#else
+#define CHECKED(v) (v)
+#define CHECKED2(v) (v)
+#endif
+
+#define CHECKED3(v) ((v)&(vga.vmemwrap-1))
+#define CHECKED4(v) ((v)&((vga.vmemwrap>>2)-1))
+
+
+#ifdef VGA_KEEP_CHANGES
+#define MEM_CHANGED( _MEM ) vga.changes.map[ (_MEM) >> VGA_CHANGE_SHIFT ] |= vga.changes.writeMask;
+//#define MEM_CHANGED( _MEM ) vga.changes.map[ (_MEM) >> VGA_CHANGE_SHIFT ] = 1;
+#else
+#define MEM_CHANGED( _MEM )
+#endif
+
+#define TANDY_VIDBASE(_X_) &MemBase[ 0x80000 + (_X_)]
+
+template <class Size>
+static INLINE void hostWrite(HostPt off, Bitu val) {
+ if ( sizeof( Size ) == 1)
+ host_writeb( off, (Bit8u)val );
+ else if ( sizeof( Size ) == 2)
+ host_writew( off, (Bit16u)val );
+ else if ( sizeof( Size ) == 4)
+ host_writed( off, (Bit32u)val );
+}
+
+template <class Size>
+static INLINE Bitu hostRead(HostPt off ) {
+ if ( sizeof( Size ) == 1)
+ return host_readb( off );
+ else if ( sizeof( Size ) == 2)
+ return host_readw( off );
+ else if ( sizeof( Size ) == 4)
+ return host_readd( off );
+ return 0;
+}
+
+
+void VGA_MapMMIO(void);
+//Nice one from DosEmu
+INLINE static Bit32u RasterOp(Bit32u input,Bit32u mask) {
+ switch (vga.config.raster_op) {
+ case 0x00: /* None */
+ return (input & mask) | (vga.latch.d & ~mask);
+ case 0x01: /* AND */
+ return (input | ~mask) & vga.latch.d;
+ case 0x02: /* OR */
+ return (input & mask) | vga.latch.d;
+ case 0x03: /* XOR */
+ return (input & mask) ^ vga.latch.d;
+ };
+ return 0;
+}
+
+INLINE static Bit32u ModeOperation(Bit8u val) {
+ Bit32u full;
+ switch (vga.config.write_mode) {
+ case 0x00:
+ // Write Mode 0: In this mode, the host data is first rotated as per the Rotate Count field, then the Enable Set/Reset mechanism selects data from this or the Set/Reset field. Then the selected Logical Operation is performed on the resulting data and the data in the latch register. Then the Bit Mask field is used to select which bits come from the resulting data and which come from the latch register. Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory.
+ val=((val >> vga.config.data_rotate) | (val << (8-vga.config.data_rotate)));
+ full=ExpandTable[val];
+ full=(full & vga.config.full_not_enable_set_reset) | vga.config.full_enable_and_set_reset;
+ full=RasterOp(full,vga.config.full_bit_mask);
+ break;
+ case 0x01:
+ // Write Mode 1: In this mode, data is transferred directly from the 32 bit latch register to display memory, affected only by the Memory Plane Write Enable field. The host data is not used in this mode.
+ full=vga.latch.d;
+ break;
+ case 0x02:
+ //Write Mode 2: In this mode, the bits 3-0 of the host data are replicated across all 8 bits of their respective planes. Then the selected Logical Operation is performed on the resulting data and the data in the latch register. Then the Bit Mask field is used to select which bits come from the resulting data and which come from the latch register. Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory.
+ full=RasterOp(FillTable[val&0xF],vga.config.full_bit_mask);
+ break;
+ case 0x03:
+ // Write Mode 3: In this mode, the data in the Set/Reset field is used as if the Enable Set/Reset field were set to 1111b. Then the host data is first rotated as per the Rotate Count field, then logical ANDed with the value of the Bit Mask field. The resulting value is used on the data obtained from the Set/Reset field in the same way that the Bit Mask field would ordinarily be used. to select which bits come from the expansion of the Set/Reset field and which come from the latch register. Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory.
+ val=((val >> vga.config.data_rotate) | (val << (8-vga.config.data_rotate)));
+ full=RasterOp(vga.config.full_set_reset,ExpandTable[val] & vga.config.full_bit_mask);
+ break;
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:Unsupported write mode %d",vga.config.write_mode);
+ full=0;
+ break;
+ }
+ return full;
+}
+
+/* Gonna assume that whoever maps vga memory, maps it on 32/64kb boundary */
+
+#define VGA_PAGES (128/4)
+#define VGA_PAGE_A0 (0xA0000/4096)
+#define VGA_PAGE_B0 (0xB0000/4096)
+#define VGA_PAGE_B8 (0xB8000/4096)
+
+static struct {
+ Bitu base, mask;
+} vgapages;
+
+class VGA_UnchainedRead_Handler : public PageHandler {
+public:
+ Bitu readHandler(PhysPt start) {
+ vga.latch.d=((Bit32u*)vga.mem.linear)[start];
+ switch (vga.config.read_mode) {
+ case 0:
+ return (vga.latch.b[vga.config.read_map_select]);
+ case 1:
+ VGA_Latch templatch;
+ templatch.d=(vga.latch.d & FillTable[vga.config.color_dont_care]) ^ FillTable[vga.config.color_compare & vga.config.color_dont_care];
+ return (Bit8u)~(templatch.b[0] | templatch.b[1] | templatch.b[2] | templatch.b[3]);
+ }
+ return 0;
+ }
+public:
+ Bitu readb(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_read_full;
+ addr = CHECKED2(addr);
+ return readHandler(addr);
+ }
+ Bitu readw(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_read_full;
+ addr = CHECKED2(addr);
+ Bitu ret = (readHandler(addr+0) << 0);
+ ret |= (readHandler(addr+1) << 8);
+ return ret;
+ }
+ Bitu readd(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_read_full;
+ addr = CHECKED2(addr);
+ Bitu ret = (readHandler(addr+0) << 0);
+ ret |= (readHandler(addr+1) << 8);
+ ret |= (readHandler(addr+2) << 16);
+ ret |= (readHandler(addr+3) << 24);
+ return ret;
+ }
+};
+
+class VGA_ChainedEGA_Handler : public PageHandler {
+public:
+ Bitu readHandler(PhysPt addr) {
+ return vga.mem.linear[addr];
+ }
+ void writeHandler(PhysPt start, Bit8u val) {
+ ModeOperation(val);
+ /* Update video memory and the pixel buffer */
+ VGA_Latch pixels;
+ vga.mem.linear[start] = val;
+ start >>= 2;
+ pixels.d=((Bit32u*)vga.mem.linear)[start];
+
+ Bit8u * write_pixels=&vga.fastmem[start<<3];
+
+ Bit32u colors0_3, colors4_7;
+ VGA_Latch temp;temp.d=(pixels.d>>4) & 0x0f0f0f0f;
+ colors0_3 =
+ Expand16Table[0][temp.b[0]] |
+ Expand16Table[1][temp.b[1]] |
+ Expand16Table[2][temp.b[2]] |
+ Expand16Table[3][temp.b[3]];
+ *(Bit32u *)write_pixels=colors0_3;
+ temp.d=pixels.d & 0x0f0f0f0f;
+ colors4_7 =
+ Expand16Table[0][temp.b[0]] |
+ Expand16Table[1][temp.b[1]] |
+ Expand16Table[2][temp.b[2]] |
+ Expand16Table[3][temp.b[3]];
+ *(Bit32u *)(write_pixels+4)=colors4_7;
+ }
+public:
+ VGA_ChainedEGA_Handler() {
+ flags=PFLAG_NOCODE;
+ }
+ void writeb(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED(addr);
+ MEM_CHANGED( addr << 3);
+ writeHandler(addr+0,(Bit8u)(val >> 0));
+ }
+ void writew(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED(addr);
+ MEM_CHANGED( addr << 3);
+ writeHandler(addr+0,(Bit8u)(val >> 0));
+ writeHandler(addr+1,(Bit8u)(val >> 8));
+ }
+ void writed(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED(addr);
+ MEM_CHANGED( addr << 3);
+ writeHandler(addr+0,(Bit8u)(val >> 0));
+ writeHandler(addr+1,(Bit8u)(val >> 8));
+ writeHandler(addr+2,(Bit8u)(val >> 16));
+ writeHandler(addr+3,(Bit8u)(val >> 24));
+ }
+ Bitu readb(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_read_full;
+ addr = CHECKED(addr);
+ return readHandler(addr);
+ }
+ Bitu readw(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_read_full;
+ addr = CHECKED(addr);
+ Bitu ret = (readHandler(addr+0) << 0);
+ ret |= (readHandler(addr+1) << 8);
+ return ret;
+ }
+ Bitu readd(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_read_full;
+ addr = CHECKED(addr);
+ Bitu ret = (readHandler(addr+0) << 0);
+ ret |= (readHandler(addr+1) << 8);
+ ret |= (readHandler(addr+2) << 16);
+ ret |= (readHandler(addr+3) << 24);
+ return ret;
+ }
+};
+
+class VGA_UnchainedEGA_Handler : public VGA_UnchainedRead_Handler {
+public:
+ template< bool wrapping>
+ void writeHandler(PhysPt start, Bit8u val) {
+ Bit32u data=ModeOperation(val);
+ /* Update video memory and the pixel buffer */
+ VGA_Latch pixels;
+ pixels.d=((Bit32u*)vga.mem.linear)[start];
+ pixels.d&=vga.config.full_not_map_mask;
+ pixels.d|=(data & vga.config.full_map_mask);
+ ((Bit32u*)vga.mem.linear)[start]=pixels.d;
+ Bit8u * write_pixels=&vga.fastmem[start<<3];
+
+ Bit32u colors0_3, colors4_7;
+ VGA_Latch temp;temp.d=(pixels.d>>4) & 0x0f0f0f0f;
+ colors0_3 =
+ Expand16Table[0][temp.b[0]] |
+ Expand16Table[1][temp.b[1]] |
+ Expand16Table[2][temp.b[2]] |
+ Expand16Table[3][temp.b[3]];
+ *(Bit32u *)write_pixels=colors0_3;
+ temp.d=pixels.d & 0x0f0f0f0f;
+ colors4_7 =
+ Expand16Table[0][temp.b[0]] |
+ Expand16Table[1][temp.b[1]] |
+ Expand16Table[2][temp.b[2]] |
+ Expand16Table[3][temp.b[3]];
+ *(Bit32u *)(write_pixels+4)=colors4_7;
+ }
+public:
+ VGA_UnchainedEGA_Handler() {
+ flags=PFLAG_NOCODE;
+ }
+ void writeb(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED2(addr);
+ MEM_CHANGED( addr << 3);
+ writeHandler<true>(addr+0,(Bit8u)(val >> 0));
+ }
+ void writew(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED2(addr);
+ MEM_CHANGED( addr << 3);
+ writeHandler<true>(addr+0,(Bit8u)(val >> 0));
+ writeHandler<true>(addr+1,(Bit8u)(val >> 8));
+ }
+ void writed(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED2(addr);
+ MEM_CHANGED( addr << 3);
+ writeHandler<true>(addr+0,(Bit8u)(val >> 0));
+ writeHandler<true>(addr+1,(Bit8u)(val >> 8));
+ writeHandler<true>(addr+2,(Bit8u)(val >> 16));
+ writeHandler<true>(addr+3,(Bit8u)(val >> 24));
+ }
+};
+
+//Slighly unusual version, will directly write 8,16,32 bits values
+class VGA_ChainedVGA_Handler : public PageHandler {
+public:
+ VGA_ChainedVGA_Handler() {
+ flags=PFLAG_NOCODE;
+ }
+ template <class Size>
+ static INLINE Bitu readHandler(PhysPt addr ) {
+ return hostRead<Size>( &vga.mem.linear[((addr&~3)<<2)+(addr&3)] );
+ }
+ template <class Size>
+ static INLINE void writeCache(PhysPt addr, Bitu val) {
+ hostWrite<Size>( &vga.fastmem[addr], val );
+ if (GCC_UNLIKELY(addr < 320)) {
+ // And replicate the first line
+ hostWrite<Size>( &vga.fastmem[addr+64*1024], val );
+ }
+ }
+ template <class Size>
+ static INLINE void writeHandler(PhysPt addr, Bitu val) {
+ // No need to check for compatible chains here, this one is only enabled if that bit is set
+ hostWrite<Size>( &vga.mem.linear[((addr&~3)<<2)+(addr&3)], val );
+ }
+ Bitu readb(PhysPt addr ) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_read_full;
+ addr = CHECKED(addr);
+ return readHandler<Bit8u>( addr );
+ }
+ Bitu readw(PhysPt addr ) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_read_full;
+ addr = CHECKED(addr);
+ if (GCC_UNLIKELY(addr & 1)) {
+ Bitu ret = (readHandler<Bit8u>( addr+0 ) << 0 );
+ ret |= (readHandler<Bit8u>( addr+1 ) << 8 );
+ return ret;
+ } else
+ return readHandler<Bit16u>( addr );
+ }
+ Bitu readd(PhysPt addr ) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_read_full;
+ addr = CHECKED(addr);
+ if (GCC_UNLIKELY(addr & 3)) {
+ Bitu ret = (readHandler<Bit8u>( addr+0 ) << 0 );
+ ret |= (readHandler<Bit8u>( addr+1 ) << 8 );
+ ret |= (readHandler<Bit8u>( addr+2 ) << 16 );
+ ret |= (readHandler<Bit8u>( addr+3 ) << 24 );
+ return ret;
+ } else
+ return readHandler<Bit32u>( addr );
+ }
+ void writeb(PhysPt addr, Bitu val ) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED(addr);
+ MEM_CHANGED( addr );
+ writeHandler<Bit8u>( addr, val );
+ writeCache<Bit8u>( addr, val );
+ }
+ void writew(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED(addr);
+ MEM_CHANGED( addr );
+// MEM_CHANGED( addr + 1);
+ if (GCC_UNLIKELY(addr & 1)) {
+ writeHandler<Bit8u>( addr+0, val >> 0 );
+ writeHandler<Bit8u>( addr+1, val >> 8 );
+ } else {
+ writeHandler<Bit16u>( addr, val );
+ }
+ writeCache<Bit16u>( addr, val );
+ }
+ void writed(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED(addr);
+ MEM_CHANGED( addr );
+// MEM_CHANGED( addr + 3);
+ if (GCC_UNLIKELY(addr & 3)) {
+ writeHandler<Bit8u>( addr+0, val >> 0 );
+ writeHandler<Bit8u>( addr+1, val >> 8 );
+ writeHandler<Bit8u>( addr+2, val >> 16 );
+ writeHandler<Bit8u>( addr+3, val >> 24 );
+ } else {
+ writeHandler<Bit32u>( addr, val );
+ }
+ writeCache<Bit32u>( addr, val );
+ }
+};
+
+class VGA_UnchainedVGA_Handler : public VGA_UnchainedRead_Handler {
+public:
+ void writeHandler( PhysPt addr, Bit8u val ) {
+ Bit32u data=ModeOperation(val);
+ VGA_Latch pixels;
+ pixels.d=((Bit32u*)vga.mem.linear)[addr];
+ pixels.d&=vga.config.full_not_map_mask;
+ pixels.d|=(data & vga.config.full_map_mask);
+ ((Bit32u*)vga.mem.linear)[addr]=pixels.d;
+// if(vga.config.compatible_chain4)
+// ((Bit32u*)vga.mem.linear)[CHECKED2(addr+64*1024)]=pixels.d;
+ }
+public:
+ VGA_UnchainedVGA_Handler() {
+ flags=PFLAG_NOCODE;
+ }
+ void writeb(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED2(addr);
+ MEM_CHANGED( addr << 2 );
+ writeHandler(addr+0,(Bit8u)(val >> 0));
+ }
+ void writew(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED2(addr);
+ MEM_CHANGED( addr << 2);
+ writeHandler(addr+0,(Bit8u)(val >> 0));
+ writeHandler(addr+1,(Bit8u)(val >> 8));
+ }
+ void writed(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED2(addr);
+ MEM_CHANGED( addr << 2);
+ writeHandler(addr+0,(Bit8u)(val >> 0));
+ writeHandler(addr+1,(Bit8u)(val >> 8));
+ writeHandler(addr+2,(Bit8u)(val >> 16));
+ writeHandler(addr+3,(Bit8u)(val >> 24));
+ }
+};
+
+class VGA_TEXT_PageHandler : public PageHandler {
+public:
+ VGA_TEXT_PageHandler() {
+ flags=PFLAG_NOCODE;
+ }
+ Bitu readb(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ switch(vga.gfx.read_map_select) {
+ case 0: // character index
+ return vga.mem.linear[CHECKED3(vga.svga.bank_read_full+addr)];
+ case 1: // character attribute
+ return vga.mem.linear[CHECKED3(vga.svga.bank_read_full+addr+1)];
+ case 2: // font map
+ return vga.draw.font[addr];
+ default: // 3=unused, but still RAM that could save values
+ return 0;
+ }
+ }
+ void writeb(PhysPt addr,Bitu val){
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+
+ if (GCC_LIKELY(vga.seq.map_mask == 0x4)) {
+ vga.draw.font[addr]=(Bit8u)val;
+ } else {
+ if (vga.seq.map_mask & 0x4) // font map
+ vga.draw.font[addr]=(Bit8u)val;
+ if (vga.seq.map_mask & 0x2) // character attribute
+ vga.mem.linear[CHECKED3(vga.svga.bank_read_full+addr+1)]=(Bit8u)val;
+ if (vga.seq.map_mask & 0x1) // character index
+ vga.mem.linear[CHECKED3(vga.svga.bank_read_full+addr)]=(Bit8u)val;
+ }
+ }
+};
+
+class VGA_Map_Handler : public PageHandler {
+public:
+ VGA_Map_Handler() {
+ flags=PFLAG_READABLE|PFLAG_WRITEABLE|PFLAG_NOCODE;
+ }
+ HostPt GetHostReadPt(Bitu phys_page) {
+ phys_page-=vgapages.base;
+ return &vga.mem.linear[CHECKED3(vga.svga.bank_read_full+phys_page*4096)];
+ }
+ HostPt GetHostWritePt(Bitu phys_page) {
+ phys_page-=vgapages.base;
+ return &vga.mem.linear[CHECKED3(vga.svga.bank_write_full+phys_page*4096)];
+ }
+};
+
+class VGA_Changes_Handler : public PageHandler {
+public:
+ VGA_Changes_Handler() {
+ flags=PFLAG_NOCODE;
+ }
+ Bitu readb(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_read_full;
+ addr = CHECKED(addr);
+ return hostRead<Bit8u>( &vga.mem.linear[addr] );
+ }
+ Bitu readw(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_read_full;
+ addr = CHECKED(addr);
+ return hostRead<Bit16u>( &vga.mem.linear[addr] );
+ }
+ Bitu readd(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_read_full;
+ addr = CHECKED(addr);
+ return hostRead<Bit32u>( &vga.mem.linear[addr] );
+ }
+ void writeb(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED(addr);
+ MEM_CHANGED( addr );
+ hostWrite<Bit8u>( &vga.mem.linear[addr], val );
+ }
+ void writew(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED(addr);
+ MEM_CHANGED( addr );
+ hostWrite<Bit16u>( &vga.mem.linear[addr], val );
+ }
+ void writed(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
+ addr += vga.svga.bank_write_full;
+ addr = CHECKED(addr);
+ MEM_CHANGED( addr );
+ hostWrite<Bit32u>( &vga.mem.linear[addr], val );
+ }
+};
+
+class VGA_LIN4_Handler : public VGA_UnchainedEGA_Handler {
+public:
+ VGA_LIN4_Handler() {
+ flags=PFLAG_NOCODE;
+ }
+ void writeb(PhysPt addr,Bitu val) {
+ addr = vga.svga.bank_write_full + (PAGING_GetPhysicalAddress(addr) & 0xffff);
+ addr = CHECKED4(addr);
+ MEM_CHANGED( addr << 3 );
+ writeHandler<false>(addr+0,(Bit8u)(val >> 0));
+ }
+ void writew(PhysPt addr,Bitu val) {
+ addr = vga.svga.bank_write_full + (PAGING_GetPhysicalAddress(addr) & 0xffff);
+ addr = CHECKED4(addr);
+ MEM_CHANGED( addr << 3 );
+ writeHandler<false>(addr+0,(Bit8u)(val >> 0));
+ writeHandler<false>(addr+1,(Bit8u)(val >> 8));
+ }
+ void writed(PhysPt addr,Bitu val) {
+ addr = vga.svga.bank_write_full + (PAGING_GetPhysicalAddress(addr) & 0xffff);
+ addr = CHECKED4(addr);
+ MEM_CHANGED( addr << 3 );
+ writeHandler<false>(addr+0,(Bit8u)(val >> 0));
+ writeHandler<false>(addr+1,(Bit8u)(val >> 8));
+ writeHandler<false>(addr+2,(Bit8u)(val >> 16));
+ writeHandler<false>(addr+3,(Bit8u)(val >> 24));
+ }
+ Bitu readb(PhysPt addr) {
+ addr = vga.svga.bank_read_full + (PAGING_GetPhysicalAddress(addr) & 0xffff);
+ addr = CHECKED4(addr);
+ return readHandler(addr);
+ }
+ Bitu readw(PhysPt addr) {
+ addr = vga.svga.bank_read_full + (PAGING_GetPhysicalAddress(addr) & 0xffff);
+ addr = CHECKED4(addr);
+ Bitu ret = (readHandler(addr+0) << 0);
+ ret |= (readHandler(addr+1) << 8);
+ return ret;
+ }
+ Bitu readd(PhysPt addr) {
+ addr = vga.svga.bank_read_full + (PAGING_GetPhysicalAddress(addr) & 0xffff);
+ addr = CHECKED4(addr);
+ Bitu ret = (readHandler(addr+0) << 0);
+ ret |= (readHandler(addr+1) << 8);
+ ret |= (readHandler(addr+2) << 16);
+ ret |= (readHandler(addr+3) << 24);
+ return ret;
+ }
+};
+
+
+class VGA_LFBChanges_Handler : public PageHandler {
+public:
+ VGA_LFBChanges_Handler() {
+ flags=PFLAG_NOCODE;
+ }
+ Bitu readb(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) - vga.lfb.addr;
+ addr = CHECKED(addr);
+ return hostRead<Bit8u>( &vga.mem.linear[addr] );
+ }
+ Bitu readw(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) - vga.lfb.addr;
+ addr = CHECKED(addr);
+ return hostRead<Bit16u>( &vga.mem.linear[addr] );
+ }
+ Bitu readd(PhysPt addr) {
+ addr = PAGING_GetPhysicalAddress(addr) - vga.lfb.addr;
+ addr = CHECKED(addr);
+ return hostRead<Bit32u>( &vga.mem.linear[addr] );
+ }
+ void writeb(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) - vga.lfb.addr;
+ addr = CHECKED(addr);
+ hostWrite<Bit8u>( &vga.mem.linear[addr], val );
+ MEM_CHANGED( addr );
+ }
+ void writew(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) - vga.lfb.addr;
+ addr = CHECKED(addr);
+ hostWrite<Bit16u>( &vga.mem.linear[addr], val );
+ MEM_CHANGED( addr );
+ }
+ void writed(PhysPt addr,Bitu val) {
+ addr = PAGING_GetPhysicalAddress(addr) - vga.lfb.addr;
+ addr = CHECKED(addr);
+ hostWrite<Bit32u>( &vga.mem.linear[addr], val );
+ MEM_CHANGED( addr );
+ }
+};
+
+class VGA_LFB_Handler : public PageHandler {
+public:
+ VGA_LFB_Handler() {
+ flags=PFLAG_READABLE|PFLAG_WRITEABLE|PFLAG_NOCODE;
+ }
+ HostPt GetHostReadPt( Bitu phys_page ) {
+ phys_page -= vga.lfb.page;
+ return &vga.mem.linear[CHECKED3(phys_page * 4096)];
+ }
+ HostPt GetHostWritePt( Bitu phys_page ) {
+ return GetHostReadPt( phys_page );
+ }
+};
+
+extern void XGA_Write(Bitu port, Bitu val, Bitu len);
+extern Bitu XGA_Read(Bitu port, Bitu len);
+
+class VGA_MMIO_Handler : public PageHandler {
+public:
+ VGA_MMIO_Handler() {
+ flags=PFLAG_NOCODE;
+ }
+ void writeb(PhysPt addr,Bitu val) {
+ Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
+ XGA_Write(port, val, 1);
+ }
+ void writew(PhysPt addr,Bitu val) {
+ Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
+ XGA_Write(port, val, 2);
+ }
+ void writed(PhysPt addr,Bitu val) {
+ Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
+ XGA_Write(port, val, 4);
+ }
+
+ Bitu readb(PhysPt addr) {
+ Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
+ return XGA_Read(port, 1);
+ }
+ Bitu readw(PhysPt addr) {
+ Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
+ return XGA_Read(port, 2);
+ }
+ Bitu readd(PhysPt addr) {
+ Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
+ return XGA_Read(port, 4);
+ }
+};
+
+class VGA_TANDY_PageHandler : public PageHandler {
+public:
+ VGA_TANDY_PageHandler() {
+ flags=PFLAG_READABLE|PFLAG_WRITEABLE;
+// |PFLAG_NOCODE;
+ }
+ HostPt GetHostReadPt(Bitu phys_page) {
+ // Odd banks are limited to 16kB and repeated
+ if (vga.tandy.mem_bank & 1)
+ phys_page&=0x03;
+ else
+ phys_page&=0x07;
+ return vga.tandy.mem_base + (phys_page * 4096);
+ }
+ HostPt GetHostWritePt(Bitu phys_page) {
+ return GetHostReadPt( phys_page );
+ }
+};
+
+
+class VGA_PCJR_Handler : public PageHandler {
+public:
+ VGA_PCJR_Handler() {
+ flags=PFLAG_READABLE|PFLAG_WRITEABLE;
+ }
+ HostPt GetHostReadPt(Bitu phys_page) {
+ phys_page-=0xb8;
+ // The 16kB map area is repeated in the 32kB range
+ // On CGA CPU A14 is not decoded so it repeats there too
+ phys_page&=0x03;
+ return vga.tandy.mem_base + (phys_page * 4096);
+ }
+ HostPt GetHostWritePt(Bitu phys_page) {
+ return GetHostReadPt( phys_page );
+ }
+};
+
+class VGA_HERC_Handler : public PageHandler {
+public:
+ VGA_HERC_Handler() {
+ flags=PFLAG_READABLE|PFLAG_WRITEABLE;
+ }
+ HostPt GetHostReadPt(Bitu phys_page) {
+ // The 4kB map area is repeated in the 32kB range
+ return &vga.mem.linear[0];
+ }
+ HostPt GetHostWritePt(Bitu phys_page) {
+ return GetHostReadPt( phys_page );
+ }
+};
+
+class VGA_Empty_Handler : public PageHandler {
+public:
+ VGA_Empty_Handler() {
+ flags=PFLAG_NOCODE;
+ }
+ Bitu readb(PhysPt /*addr*/) {
+// LOG(LOG_VGA, LOG_NORMAL ) ( "Read from empty memory space at %x", addr );
+ return 0xff;
+ }
+ void writeb(PhysPt /*addr*/,Bitu /*val*/) {
+// LOG(LOG_VGA, LOG_NORMAL ) ( "Write %x to empty memory space at %x", val, addr );
+ }
+};
+
+static struct vg {
+ VGA_Map_Handler map;
+ VGA_Changes_Handler changes;
+ VGA_TEXT_PageHandler text;
+ VGA_TANDY_PageHandler tandy;
+ VGA_ChainedEGA_Handler cega;
+ VGA_ChainedVGA_Handler cvga;
+ VGA_UnchainedEGA_Handler uega;
+ VGA_UnchainedVGA_Handler uvga;
+ VGA_PCJR_Handler pcjr;
+ VGA_HERC_Handler herc;
+ VGA_LIN4_Handler lin4;
+ VGA_LFB_Handler lfb;
+ VGA_LFBChanges_Handler lfbchanges;
+ VGA_MMIO_Handler mmio;
+ VGA_Empty_Handler empty;
+} vgaph;
+
+void VGA_ChangedBank(void) {
+#ifndef VGA_LFB_MAPPED
+ //If the mode is accurate than the correct mapper must have been installed already
+ if ( vga.mode >= M_LIN4 && vga.mode <= M_LIN32 ) {
+ return;
+ }
+#endif
+ VGA_SetupHandlers();
+}
+
+void VGA_SetupHandlers(void) {
+ vga.svga.bank_read_full = vga.svga.bank_read*vga.svga.bank_size;
+ vga.svga.bank_write_full = vga.svga.bank_write*vga.svga.bank_size;
+
+ PageHandler *newHandler;
+ switch (machine) {
+ case MCH_CGA:
+ case MCH_PCJR:
+ MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.empty );
+ MEM_SetPageHandler( VGA_PAGE_B0, 8, &vgaph.empty );
+ MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.pcjr );
+ goto range_done;
+ case MCH_HERC:
+ MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.empty );
+ vgapages.base=VGA_PAGE_B0;
+ if (vga.herc.enable_bits & 0x2) {
+ vgapages.mask=0xffff;
+ MEM_SetPageHandler(VGA_PAGE_B0,16,&vgaph.map);
+ } else {
+ vgapages.mask=0x7fff;
+ // With hercules in 32kB mode it leaves a memory hole on 0xb800
+ // and has MDA-compatible address wrapping when graphics are disabled
+ if (vga.herc.enable_bits & 0x1)
+ MEM_SetPageHandler(VGA_PAGE_B0,8,&vgaph.map);
+ else
+ MEM_SetPageHandler(VGA_PAGE_B0,8,&vgaph.herc);
+ MEM_SetPageHandler(VGA_PAGE_B8,8,&vgaph.empty);
+ }
+ goto range_done;
+ case MCH_TANDY:
+ /* Always map 0xa000 - 0xbfff, might overwrite 0xb800 */
+ vgapages.base=VGA_PAGE_A0;
+ vgapages.mask=0x1ffff;
+ MEM_SetPageHandler(VGA_PAGE_A0, 32, &vgaph.map );
+ if ( vga.tandy.extended_ram & 1 ) {
+ //You seem to be able to also map different 64kb banks, but have to figure that out
+ //This seems to work so far though
+ vga.tandy.draw_base = vga.mem.linear;
+ vga.tandy.mem_base = vga.mem.linear;
+ } else {
+ vga.tandy.draw_base = TANDY_VIDBASE( vga.tandy.draw_bank * 16 * 1024);
+ vga.tandy.mem_base = TANDY_VIDBASE( vga.tandy.mem_bank * 16 * 1024);
+ MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.tandy );
+ }
+ goto range_done;
+// MEM_SetPageHandler(vga.tandy.mem_bank<<2,vga.tandy.is_32k_mode ? 0x08 : 0x04,range_handler);
+ case EGAVGA_ARCH_CASE:
+ break;
+ default:
+ LOG_MSG("Illegal machine type %d", machine );
+ return;
+ }
+
+ /* This should be vga only */
+ switch (vga.mode) {
+ case M_ERROR:
+ default:
+ return;
+ case M_LIN4:
+ newHandler = &vgaph.lin4;
+ break;
+ case M_LIN15:
+ case M_LIN16:
+ case M_LIN32:
+#ifdef VGA_LFB_MAPPED
+ newHandler = &vgaph.map;
+#else
+ newHandler = &vgaph.changes;
+#endif
+ break;
+ case M_LIN8:
+ case M_VGA:
+ if (vga.config.chained) {
+ if(vga.config.compatible_chain4)
+ newHandler = &vgaph.cvga;
+ else
+#ifdef VGA_LFB_MAPPED
+ newHandler = &vgaph.map;
+#else
+ newHandler = &vgaph.changes;
+#endif
+ } else {
+ newHandler = &vgaph.uvga;
+ }
+ break;
+ case M_EGA:
+ if (vga.config.chained)
+ newHandler = &vgaph.cega;
+ else
+ newHandler = &vgaph.uega;
+ break;
+ case M_TEXT:
+ /* Check if we're not in odd/even mode */
+ if (vga.gfx.miscellaneous & 0x2) newHandler = &vgaph.map;
+ else newHandler = &vgaph.text;
+ break;
+ case M_CGA4:
+ case M_CGA2:
+ newHandler = &vgaph.map;
+ break;
+ }
+ switch ((vga.gfx.miscellaneous >> 2) & 3) {
+ case 0:
+ vgapages.base = VGA_PAGE_A0;
+ switch (svgaCard) {
+ case SVGA_TsengET3K:
+ case SVGA_TsengET4K:
+ vgapages.mask = 0xffff;
+ break;
+ case SVGA_S3Trio:
+ default:
+ vgapages.mask = 0x1ffff;
+ break;
+ }
+ MEM_SetPageHandler(VGA_PAGE_A0, 32, newHandler );
+ break;
+ case 1:
+ vgapages.base = VGA_PAGE_A0;
+ vgapages.mask = 0xffff;
+ MEM_SetPageHandler( VGA_PAGE_A0, 16, newHandler );
+ MEM_SetPageHandler( VGA_PAGE_B0, 16, &vgaph.empty );
+ break;
+ case 2:
+ vgapages.base = VGA_PAGE_B0;
+ vgapages.mask = 0x7fff;
+ MEM_SetPageHandler( VGA_PAGE_B0, 8, newHandler );
+ MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.empty );
+ MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.empty );
+ break;
+ case 3:
+ vgapages.base = VGA_PAGE_B8;
+ vgapages.mask = 0x7fff;
+ MEM_SetPageHandler( VGA_PAGE_B8, 8, newHandler );
+ MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.empty );
+ MEM_SetPageHandler( VGA_PAGE_B0, 8, &vgaph.empty );
+ break;
+ }
+ if(svgaCard == SVGA_S3Trio && (vga.s3.ext_mem_ctrl & 0x10))
+ MEM_SetPageHandler(VGA_PAGE_A0, 16, &vgaph.mmio);
+range_done:
+ PAGING_ClearTLB();
+}
+
+void VGA_StartUpdateLFB(void) {
+ vga.lfb.page = vga.s3.la_window << 4;
+ vga.lfb.addr = vga.s3.la_window << 16;
+#ifdef VGA_LFB_MAPPED
+ vga.lfb.handler = &vgaph.lfb;
+#else
+ vga.lfb.handler = &vgaph.lfbchanges;
+#endif
+ MEM_SetLFB(vga.s3.la_window << 4 ,vga.vmemsize/4096, vga.lfb.handler, &vgaph.mmio);
+}
+
+static void VGA_Memory_ShutDown(Section * /*sec*/) {
+ delete[] vga.mem.linear_orgptr;
+ delete[] vga.fastmem_orgptr;
+#ifdef VGA_KEEP_CHANGES
+ delete[] vga.changes.map;
+#endif
+}
+
+void VGA_SetupMemory(Section* sec) {
+ vga.svga.bank_read = vga.svga.bank_write = 0;
+ vga.svga.bank_read_full = vga.svga.bank_write_full = 0;
+
+ Bit32u vga_allocsize=vga.vmemsize;
+ // Keep lower limit at 512k
+ if (vga_allocsize<512*1024) vga_allocsize=512*1024;
+ // We reserve extra 2K for one scan line
+ vga_allocsize+=2048;
+ vga.mem.linear_orgptr = new Bit8u[vga_allocsize+16];
+ vga.mem.linear=(Bit8u*)(((Bitu)vga.mem.linear_orgptr + 16-1) & ~(16-1));
+ memset(vga.mem.linear,0,vga_allocsize);
+
+ vga.fastmem_orgptr = new Bit8u[(vga.vmemsize<<1)+4096+16];
+ vga.fastmem=(Bit8u*)(((Bitu)vga.fastmem_orgptr + 16-1) & ~(16-1));
+
+ // In most cases these values stay the same. Assumptions: vmemwrap is power of 2,
+ // vmemwrap <= vmemsize, fastmem implicitly has mem wrap twice as big
+ vga.vmemwrap = vga.vmemsize;
+
+#ifdef VGA_KEEP_CHANGES
+ memset( &vga.changes, 0, sizeof( vga.changes ));
+ int changesMapSize = (vga.vmemsize >> VGA_CHANGE_SHIFT) + 32;
+ vga.changes.map = new Bit8u[changesMapSize];
+ memset(vga.changes.map, 0, changesMapSize);
+#endif
+ vga.svga.bank_read = vga.svga.bank_write = 0;
+ vga.svga.bank_read_full = vga.svga.bank_write_full = 0;
+ vga.svga.bank_size = 0x10000; /* most common bank size is 64K */
+
+ sec->AddDestroyFunction(&VGA_Memory_ShutDown);
+
+ if (machine==MCH_PCJR) {
+ /* PCJr does not have dedicated graphics memory but uses
+ conventional memory below 128k */
+ //TODO map?
+ }
+}
diff --git a/src/hardware/vga_misc.cpp b/src/hardware/vga_misc.cpp
new file mode 100644
index 000000000..3218fc141
--- /dev/null
+++ b/src/hardware/vga_misc.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "inout.h"
+#include "pic.h"
+#include "vga.h"
+#include <math.h>
+
+
+void vga_write_p3d4(Bitu port,Bitu val,Bitu iolen);
+Bitu vga_read_p3d4(Bitu port,Bitu iolen);
+void vga_write_p3d5(Bitu port,Bitu val,Bitu iolen);
+Bitu vga_read_p3d5(Bitu port,Bitu iolen);
+
+Bitu vga_read_p3da(Bitu port,Bitu iolen) {
+ Bit8u retval=0;
+ double timeInFrame = PIC_FullIndex()-vga.draw.delay.framestart;
+
+ vga.internal.attrindex=false;
+ vga.tandy.pcjr_flipflop=false;
+
+ // 3DAh (R): Status Register
+ // bit 0 Horizontal or Vertical blanking
+ // 3 Vertical sync
+
+ if (timeInFrame >= vga.draw.delay.vrstart &&
+ timeInFrame <= vga.draw.delay.vrend)
+ retval |= 8;
+ if (timeInFrame >= vga.draw.delay.vdend) {
+ retval |= 1;
+ } else {
+ double timeInLine=fmod(timeInFrame,vga.draw.delay.htotal);
+ if (timeInLine >= vga.draw.delay.hblkstart &&
+ timeInLine <= vga.draw.delay.hblkend) {
+ retval |= 1;
+ }
+ }
+ return retval;
+}
+
+static void write_p3c2(Bitu port,Bitu val,Bitu iolen) {
+ vga.misc_output=val;
+
+ Bitu base=(val & 0x1) ? 0x3d0 : 0x3b0;
+ Bitu free=(val & 0x1) ? 0x3b0 : 0x3d0;
+ Bitu first=2, last=2;
+ if (machine==MCH_EGA) {first=0; last=3;}
+
+ for (Bitu i=first; i<=last; i++) {
+ IO_RegisterWriteHandler(base+i*2,vga_write_p3d4,IO_MB);
+ IO_RegisterReadHandler(base+i*2,vga_read_p3d4,IO_MB);
+ IO_RegisterWriteHandler(base+i*2+1,vga_write_p3d5,IO_MB);
+ IO_RegisterReadHandler(base+i*2+1,vga_read_p3d5,IO_MB);
+ IO_FreeWriteHandler(free+i*2,IO_MB);
+ IO_FreeReadHandler(free+i*2,IO_MB);
+ IO_FreeWriteHandler(free+i*2+1,IO_MB);
+ IO_FreeReadHandler(free+i*2+1,IO_MB);
+ }
+
+ IO_RegisterReadHandler(base+0xa,vga_read_p3da,IO_MB);
+ IO_FreeReadHandler(free+0xa,IO_MB);
+
+ /*
+ 0 If set Color Emulation. Base Address=3Dxh else Mono Emulation. Base Address=3Bxh.
+ 2-3 Clock Select. 0: 25MHz, 1: 28MHz
+ 5 When in Odd/Even modes Select High 64k bank if set
+ 6 Horizontal Sync Polarity. Negative if set
+ 7 Vertical Sync Polarity. Negative if set
+ Bit 6-7 indicates the number of lines on the display:
+ 1: 400, 2: 350, 3: 480
+ Note: Set to all zero on a hardware reset.
+ Note: This register can be read from port 3CCh.
+ */
+}
+
+
+static Bitu read_p3cc(Bitu port,Bitu iolen) {
+ return vga.misc_output;
+}
+
+// VGA feature control register
+static Bitu read_p3ca(Bitu port,Bitu iolen) {
+ return 0;
+}
+
+static Bitu read_p3c8(Bitu port,Bitu iolen) {
+ return 0x10;
+}
+
+static Bitu read_p3c2(Bitu port,Bitu iolen) {
+ Bit8u retval=0;
+
+ if (machine==MCH_EGA) retval = 0x0F;
+ else if (IS_VGA_ARCH) retval = 0x60;
+ if ((machine==MCH_VGA) || (((vga.misc_output>>2)&3)==0) || (((vga.misc_output>>2)&3)==3)) {
+ retval |= 0x10;
+ }
+
+ if (vga.draw.vret_triggered) retval |= 0x80;
+ return retval;
+ /*
+ 0-3 0xF on EGA, 0x0 on VGA
+ 4 Status of the switch selected by the Miscellaneous Output
+ Register 3C2h bit 2-3. Switch high if set.
+ (apparently always 1 on VGA)
+ 5 (EGA) Pin 19 of the Feature Connector (FEAT0) is high if set
+ 6 (EGA) Pin 17 of the Feature Connector (FEAT1) is high if set
+ (default differs by card, ET4000 sets them both)
+ 7 If set IRQ 2 has happened due to Vertical Retrace.
+ Should be cleared by IRQ 2 interrupt routine by clearing port 3d4h
+ index 11h bit 4.
+ */
+}
+
+void VGA_SetupMisc(void) {
+ if (IS_EGAVGA_ARCH) {
+ vga.draw.vret_triggered=false;
+ IO_RegisterReadHandler(0x3c2,read_p3c2,IO_MB);
+ IO_RegisterWriteHandler(0x3c2,write_p3c2,IO_MB);
+ if (IS_VGA_ARCH) {
+ IO_RegisterReadHandler(0x3ca,read_p3ca,IO_MB);
+ IO_RegisterReadHandler(0x3cc,read_p3cc,IO_MB);
+ } else {
+ IO_RegisterReadHandler(0x3c8,read_p3c8,IO_MB);
+ }
+ } else if (machine==MCH_CGA || IS_TANDY_ARCH) {
+ IO_RegisterReadHandler(0x3da,vga_read_p3da,IO_MB);
+ }
+}
diff --git a/src/hardware/vga_other.cpp b/src/hardware/vga_other.cpp
new file mode 100644
index 000000000..63be10a60
--- /dev/null
+++ b/src/hardware/vga_other.cpp
@@ -0,0 +1,867 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <math.h>
+#include "dosbox.h"
+#include "inout.h"
+#include "vga.h"
+#include "mem.h"
+#include "pic.h"
+#include "render.h"
+#include "mapper.h"
+
+static void write_crtc_index_other(Bitu /*port*/,Bitu val,Bitu /*iolen*/) {
+ vga.other.index=(Bit8u)val;
+}
+
+static Bitu read_crtc_index_other(Bitu /*port*/,Bitu /*iolen*/) {
+ return vga.other.index;
+}
+
+static void write_crtc_data_other(Bitu /*port*/,Bitu val,Bitu /*iolen*/) {
+ switch (vga.other.index) {
+ case 0x00: //Horizontal total
+ if (vga.other.htotal ^ val) VGA_StartResize();
+ vga.other.htotal=(Bit8u)val;
+ break;
+ case 0x01: //Horizontal displayed chars
+ if (vga.other.hdend ^ val) VGA_StartResize();
+ vga.other.hdend=(Bit8u)val;
+ break;
+ case 0x02: //Horizontal sync position
+ vga.other.hsyncp=(Bit8u)val;
+ break;
+ case 0x03: //Horizontal sync width
+ if (machine==MCH_TANDY) vga.other.vsyncw=(Bit8u)(val >> 4);
+ else vga.other.vsyncw = 16; // The MC6845 has a fixed v-sync width of 16 lines
+ vga.other.hsyncw=(Bit8u)(val & 0xf);
+ break;
+ case 0x04: //Vertical total
+ if (vga.other.vtotal ^ val) VGA_StartResize();
+ vga.other.vtotal=(Bit8u)val;
+ break;
+ case 0x05: //Vertical display adjust
+ if (vga.other.vadjust ^ val) VGA_StartResize();
+ vga.other.vadjust=(Bit8u)val;
+ break;
+ case 0x06: //Vertical rows
+ if (vga.other.vdend ^ val) VGA_StartResize();
+ vga.other.vdend=(Bit8u)val;
+ break;
+ case 0x07: //Vertical sync position
+ vga.other.vsyncp=(Bit8u)val;
+ break;
+ case 0x09: //Max scanline
+ val &= 0x1f; // VGADOC says bit 0-3 but the MC6845 datasheet says bit 0-4
+ if (vga.other.max_scanline ^ val) VGA_StartResize();
+ vga.other.max_scanline=(Bit8u)val;
+ break;
+ case 0x0A: /* Cursor Start Register */
+ vga.other.cursor_start = (Bit8u)(val & 0x3f);
+ vga.draw.cursor.sline = (Bit8u)(val&0x1f);
+ vga.draw.cursor.enabled = ((val & 0x60) != 0x20);
+ break;
+ case 0x0B: /* Cursor End Register */
+ vga.other.cursor_end = (Bit8u)(val&0x1f);
+ vga.draw.cursor.eline = (Bit8u)(val&0x1f);
+ break;
+ case 0x0C: /* Start Address High Register */
+ // Bit 12 (depending on video mode) and 13 are actually masked too,
+ // but so far no need to implement it.
+ vga.config.display_start=(vga.config.display_start & 0x00FF) | ((val&0x3F) << 8);
+ break;
+ case 0x0D: /* Start Address Low Register */
+ vga.config.display_start=(vga.config.display_start & 0xFF00) | val;
+ break;
+ case 0x0E: /*Cursor Location High Register */
+ vga.config.cursor_start&=0x00ff;
+ vga.config.cursor_start|=((Bit8u)val) << 8;
+ break;
+ case 0x0F: /* Cursor Location Low Register */
+ vga.config.cursor_start&=0xff00;
+ vga.config.cursor_start|=(Bit8u)val;
+ break;
+ case 0x10: /* Light Pen High */
+ vga.other.lightpen &= 0xff;
+ vga.other.lightpen |= (val & 0x3f)<<8; // only 6 bits
+ break;
+ case 0x11: /* Light Pen Low */
+ vga.other.lightpen &= 0xff00;
+ vga.other.lightpen |= (Bit8u)val;
+ break;
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("MC6845:Write %X to illegal index %x",val,vga.other.index);
+ }
+}
+static Bitu read_crtc_data_other(Bitu /*port*/,Bitu /*iolen*/) {
+ switch (vga.other.index) {
+ case 0x00: //Horizontal total
+ return vga.other.htotal;
+ case 0x01: //Horizontal displayed chars
+ return vga.other.hdend;
+ case 0x02: //Horizontal sync position
+ return vga.other.hsyncp;
+ case 0x03: //Horizontal and vertical sync width
+ if (machine==MCH_TANDY)
+ return vga.other.hsyncw | (vga.other.vsyncw << 4);
+ else return vga.other.hsyncw;
+ case 0x04: //Vertical total
+ return vga.other.vtotal;
+ case 0x05: //Vertical display adjust
+ return vga.other.vadjust;
+ case 0x06: //Vertical rows
+ return vga.other.vdend;
+ case 0x07: //Vertical sync position
+ return vga.other.vsyncp;
+ case 0x09: //Max scanline
+ return vga.other.max_scanline;
+ case 0x0A: /* Cursor Start Register */
+ return vga.other.cursor_start;
+ case 0x0B: /* Cursor End Register */
+ return vga.other.cursor_end;
+ case 0x0C: /* Start Address High Register */
+ return (Bit8u)(vga.config.display_start >> 8);
+ case 0x0D: /* Start Address Low Register */
+ return (Bit8u)(vga.config.display_start & 0xff);
+ case 0x0E: /*Cursor Location High Register */
+ return (Bit8u)(vga.config.cursor_start >> 8);
+ case 0x0F: /* Cursor Location Low Register */
+ return (Bit8u)(vga.config.cursor_start & 0xff);
+ case 0x10: /* Light Pen High */
+ return (Bit8u)(vga.other.lightpen >> 8);
+ case 0x11: /* Light Pen Low */
+ return (Bit8u)(vga.other.lightpen & 0xff);
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("MC6845:Read from illegal index %x",vga.other.index);
+ }
+ return (Bitu)(~0);
+}
+
+static void write_lightpen(Bitu port,Bitu val,Bitu) {
+ switch (port) {
+ case 0x3db: // Clear lightpen latch
+ vga.other.lightpen_triggered = false;
+ break;
+ case 0x3dc: // Preset lightpen latch
+ if (!vga.other.lightpen_triggered) {
+ vga.other.lightpen_triggered = true; // TODO: this shows at port 3ba/3da bit 1
+
+ double timeInFrame = PIC_FullIndex()-vga.draw.delay.framestart;
+ double timeInLine = fmod(timeInFrame,vga.draw.delay.htotal);
+ Bitu current_scanline = (Bitu)(timeInFrame / vga.draw.delay.htotal);
+
+ vga.other.lightpen = (Bit16u)((vga.draw.address_add/2) * (current_scanline/2));
+ vga.other.lightpen += (Bit16u)((timeInLine / vga.draw.delay.hdend) *
+ ((float)(vga.draw.address_add/2)));
+ }
+ break;
+ }
+}
+
+static double hue_offset = 0.0;
+static Bit8u cga_comp = 0;
+static bool new_cga = 0;
+
+static Bit8u cga16_val = 0;
+static void update_cga16_color(void);
+static Bit8u herc_pal = 0;
+
+static void cga16_color_select(Bit8u val) {
+ cga16_val = val;
+ update_cga16_color();
+}
+
+static void update_cga16_color(void) {
+// New algorithm based on code by reenigne
+// Works in all CGA graphics modes/color settings and can simulate older and newer CGA revisions
+ static const double tau = 6.28318531; // == 2*pi
+ static const double ns = 567.0/440; // degrees of hue shift per nanosecond
+
+ double tv_brightness = 0.0; // hardcoded for simpler implementation
+ double tv_saturation = (new_cga ? 0.7 : 0.6);
+
+ bool bw = (vga.tandy.mode_control&4) != 0;
+ bool color_sel = (cga16_val&0x20) != 0;
+ bool background_i = (cga16_val&0x10) != 0; // Really foreground intensity, but this is what the CGA schematic calls it.
+ bool bpp1 = (vga.tandy.mode_control&0x10) != 0;
+ Bit8u overscan = cga16_val&0x0f; // aka foreground colour in 1bpp mode
+
+ double chroma_coefficient = new_cga ? 0.29 : 0.72;
+ double b_coefficient = new_cga ? 0.07 : 0;
+ double g_coefficient = new_cga ? 0.22 : 0;
+ double r_coefficient = new_cga ? 0.1 : 0;
+ double i_coefficient = new_cga ? 0.32 : 0.28;
+ double rgbi_coefficients[0x10];
+ for (int c = 0; c < 0x10; c++) {
+ double v = 0;
+ if ((c & 1) != 0)
+ v += b_coefficient;
+ if ((c & 2) != 0)
+ v += g_coefficient;
+ if ((c & 4) != 0)
+ v += r_coefficient;
+ if ((c & 8) != 0)
+ v += i_coefficient;
+ rgbi_coefficients[c] = v;
+ }
+
+ // The pixel clock delay calculation is not accurate for 2bpp, but the difference is small and a more accurate calculation would be too slow.
+ static const double rgbi_pixel_delay = 15.5*ns;
+ static const double chroma_pixel_delays[8] = {
+ 0, // Black: no chroma
+ 35*ns, // Blue: no XORs
+ 44.5*ns, // Green: XOR on rising and falling edges
+ 39.5*ns, // Cyan: XOR on falling but not rising edge
+ 44.5*ns, // Red: XOR on rising and falling edges
+ 39.5*ns, // Magenta: XOR on falling but not rising edge
+ 44.5*ns, // Yellow: XOR on rising and falling edges
+ 39.5*ns}; // White: XOR on falling but not rising edge
+ double pixel_clock_delay;
+ int o = overscan == 0 ? 15 : overscan;
+ if (overscan == 8)
+ pixel_clock_delay = rgbi_pixel_delay;
+ else {
+ double d = rgbi_coefficients[o];
+ pixel_clock_delay = (chroma_pixel_delays[o & 7]*chroma_coefficient + rgbi_pixel_delay*d)/(chroma_coefficient + d);
+ }
+ pixel_clock_delay -= 21.5*ns; // correct for delay of color burst
+
+ double hue_adjust = (-(90-33)-hue_offset+pixel_clock_delay)*tau/360.0;
+ double chroma_signals[8][4];
+ for (Bit8u i=0; i<4; i++) {
+ chroma_signals[0][i] = 0;
+ chroma_signals[7][i] = 1;
+ for (Bit8u j=0; j<6; j++) {
+ static const double phases[6] = {
+ 270 - 21.5*ns, // blue
+ 135 - 29.5*ns, // green
+ 180 - 21.5*ns, // cyan
+ 0 - 21.5*ns, // red
+ 315 - 29.5*ns, // magenta
+ 90 - 21.5*ns}; // yellow/burst
+ // All the duty cycle fractions are the same, just under 0.5 as the rising edge is delayed 2ns more than the falling edge.
+ static const double duty = 0.5 - 2*ns/360.0;
+
+ // We have a rectangle wave with period 1 (in units of the reciprocal of the color burst frequency) and duty
+ // cycle fraction "duty" and phase "phase". We band-limit this wave to frequency 2 and sample it at intervals of 1/4.
+ // We model our band-limited wave with 4 frequency components:
+ // f(x) = a + b*sin(x*tau) + c*cos(x*tau) + d*sin(x*2*tau)
+ // Then:
+ // a = integral(0, 1, f(x)*dx) = duty
+ // b = 2*integral(0, 1, f(x)*sin(x*tau)*dx) = 2*integral(0, duty, sin(x*tau)*dx) = 2*(1-cos(x*tau))/tau
+ // c = 2*integral(0, 1, f(x)*cos(x*tau)*dx) = 2*integral(0, duty, cos(x*tau)*dx) = 2*sin(duty*tau)/tau
+ // d = 2*integral(0, 1, f(x)*sin(x*2*tau)*dx) = 2*integral(0, duty, sin(x*4*pi)*dx) = 2*(1-cos(2*tau*duty))/(2*tau)
+ double a = duty;
+ double b = 2.0*(1.0-cos(duty*tau))/tau;
+ double c = 2.0*sin(duty*tau)/tau;
+ double d = 2.0*(1.0-cos(duty*2*tau))/(2*tau);
+
+ double x = (phases[j] + 21.5*ns + pixel_clock_delay)/360.0 + i/4.0;
+
+ chroma_signals[j+1][i] = a + b*sin(x*tau) + c*cos(x*tau) + d*sin(x*2*tau);
+ }
+ }
+ Bitu CGApal[4] = {
+ overscan,
+ static_cast<Bitu>(2 + (color_sel||bw ? 1 : 0) + (background_i ? 8 : 0)),
+ static_cast<Bitu>(4 + (color_sel&&!bw? 1 : 0) + (background_i ? 8 : 0)),
+ static_cast<Bitu>(6 + (color_sel||bw ? 1 : 0) + (background_i ? 8 : 0))
+ };
+ for (Bit8u x=0; x<4; x++) { // Position of pixel in question
+ bool even = (x & 1) == 0;
+ for (Bit8u bits=0; bits<(even ? 0x10 : 0x40); ++bits) {
+ double Y=0, I=0, Q=0;
+ for (Bit8u p=0; p<4; p++) { // Position within color carrier cycle
+ // generate pixel pattern.
+ Bit8u rgbi;
+ if (bpp1)
+ rgbi = ((bits >> (3-p)) & (even ? 1 : 2)) != 0 ? overscan : 0;
+ else
+ if (even)
+ rgbi = CGApal[(bits >> (2-(p&2)))&3];
+ else
+ rgbi = CGApal[(bits >> (4-((p+1)&6)))&3];
+ Bit8u c = rgbi & 7;
+ if (bw && c != 0)
+ c = 7;
+
+ // calculate composite output
+ double chroma = chroma_signals[c][(p+x)&3]*chroma_coefficient;
+ double composite = chroma + rgbi_coefficients[rgbi];
+
+ Y+=composite;
+ if (!bw) { // burst on
+ I+=composite*2*cos(hue_adjust + (p+x)*tau/4.0);
+ Q+=composite*2*sin(hue_adjust + (p+x)*tau/4.0);
+ }
+ }
+
+ double contrast = 1 - tv_brightness;
+
+ Y = (contrast*Y/4.0) + tv_brightness; if (Y>1.0) Y=1.0; if (Y<0.0) Y=0.0;
+ I = (contrast*I/4.0) * tv_saturation; if (I>0.5957) I=0.5957; if (I<-0.5957) I=-0.5957;
+ Q = (contrast*Q/4.0) * tv_saturation; if (Q>0.5226) Q=0.5226; if (Q<-0.5226) Q=-0.5226;
+
+ static const double gamma = 2.2;
+
+ double R = Y + 0.9563*I + 0.6210*Q; R = (R - 0.075) / (1-0.075); if (R<0) R=0; if (R>1) R=1;
+ double G = Y - 0.2721*I - 0.6474*Q; G = (G - 0.075) / (1-0.075); if (G<0) G=0; if (G>1) G=1;
+ double B = Y - 1.1069*I + 1.7046*Q; B = (B - 0.075) / (1-0.075); if (B<0) B=0; if (B>1) B=1;
+ R = pow(R, gamma);
+ G = pow(G, gamma);
+ B = pow(B, gamma);
+
+ int r = static_cast<int>(255*pow( 1.5073*R -0.3725*G -0.0832*B, 1/gamma)); if (r<0) r=0; if (r>255) r=255;
+ int g = static_cast<int>(255*pow(-0.0275*R +0.9350*G +0.0670*B, 1/gamma)); if (g<0) g=0; if (g>255) g=255;
+ int b = static_cast<int>(255*pow(-0.0272*R -0.0401*G +1.1677*B, 1/gamma)); if (b<0) b=0; if (b>255) b=255;
+
+ Bit8u index = bits | ((x & 1) == 0 ? 0x30 : 0x80) | ((x & 2) == 0 ? 0x40 : 0);
+ RENDER_SetPal(index,r,g,b);
+ }
+ }
+}
+
+static void IncreaseHue(bool pressed) {
+ if (!pressed)
+ return;
+ hue_offset += 5.0;
+ update_cga16_color();
+ LOG_MSG("Hue at %f",hue_offset);
+}
+
+static void DecreaseHue(bool pressed) {
+ if (!pressed)
+ return;
+ hue_offset -= 5.0;
+ update_cga16_color();
+ LOG_MSG("Hue at %f",hue_offset);
+}
+
+static void write_cga_color_select(Bitu val) {
+ vga.tandy.color_select=val;
+ switch(vga.mode) {
+ case M_TANDY4: {
+ Bit8u base = (val & 0x10) ? 0x08 : 0;
+ Bit8u bg = val & 0xf;
+ if (vga.tandy.mode_control & 0x4) // cyan red white
+ VGA_SetCGA4Table(bg, 3+base, 4+base, 7+base);
+ else if (val & 0x20) // cyan magenta white
+ VGA_SetCGA4Table(bg, 3+base, 5+base, 7+base);
+ else // green red brown
+ VGA_SetCGA4Table(bg, 2+base, 4+base, 6+base);
+ vga.tandy.border_color = bg;
+ vga.attr.overscan_color = bg;
+ break;
+ }
+ case M_TANDY2:
+ VGA_SetCGA2Table(0,val & 0xf);
+ vga.attr.overscan_color = 0;
+ break;
+ case M_CGA16:
+ cga16_color_select(val);
+ break;
+ case M_TEXT:
+ vga.tandy.border_color = val & 0xf;
+ vga.attr.overscan_color = 0;
+ break;
+ default: //Else unhandled values warning
+ break;
+ }
+}
+
+static void write_cga(Bitu port,Bitu val,Bitu /*iolen*/) {
+ switch (port) {
+ case 0x3d8:
+ vga.tandy.mode_control=(Bit8u)val;
+ vga.attr.disabled = (val&0x8)? 0: 1;
+ if (vga.tandy.mode_control & 0x2) { // graphics mode
+ if (vga.tandy.mode_control & 0x10) {// highres mode
+ if (cga_comp==1 || (cga_comp==0 && !(val&0x4))) { // composite display
+ VGA_SetMode(M_CGA16); // composite ntsc 640x200 16 color mode
+ } else {
+ VGA_SetMode(M_TANDY2);
+ }
+ } else { // lowres mode
+ if (cga_comp==1) { // composite display
+ VGA_SetMode(M_CGA16); // composite ntsc 640x200 16 color mode
+ } else {
+ VGA_SetMode(M_TANDY4);
+ }
+ }
+
+ write_cga_color_select(vga.tandy.color_select);
+ } else {
+ VGA_SetMode(M_TANDY_TEXT);
+ }
+ VGA_SetBlinking(val & 0x20);
+ break;
+ case 0x3d9: // color select
+ write_cga_color_select(val);
+ break;
+ }
+}
+
+static void CGAModel(bool pressed) {
+ if (!pressed) return;
+ new_cga = !new_cga;
+ update_cga16_color();
+ LOG_MSG("%s model CGA selected", new_cga ? "Late" : "Early");
+}
+
+static void Composite(bool pressed) {
+ if (!pressed) return;
+ if (++cga_comp>2) cga_comp=0;
+ LOG_MSG("Composite output: %s",(cga_comp==0)?"auto":((cga_comp==1)?"on":"off"));
+ // switch RGB and Composite if in graphics mode
+ if (vga.tandy.mode_control & 0x2)
+ write_cga(0x3d8,vga.tandy.mode_control,1);
+}
+
+static void tandy_update_palette() {
+ // TODO mask off bits if needed
+ if (machine == MCH_TANDY) {
+ switch (vga.mode) {
+ case M_TANDY2:
+ VGA_SetCGA2Table(vga.attr.palette[0],
+ vga.attr.palette[vga.tandy.color_select&0xf]);
+ break;
+ case M_TANDY4:
+ if (vga.tandy.gfx_control & 0x8) {
+ // 4-color high resolution - might be an idea to introduce M_TANDY4H
+ VGA_SetCGA4Table( // function sets both medium and highres 4color tables
+ vga.attr.palette[0], vga.attr.palette[1],
+ vga.attr.palette[2], vga.attr.palette[3]);
+ } else {
+ Bit8u color_set = 0;
+ Bit8u r_mask = 0xf;
+ if (vga.tandy.color_select & 0x10) color_set |= 8; // intensity
+ if (vga.tandy.color_select & 0x20) color_set |= 1; // Cyan Mag. White
+ if (vga.tandy.mode_control & 0x04) { // Cyan Red White
+ color_set |= 1;
+ r_mask &= ~1;
+ }
+ VGA_SetCGA4Table(
+ vga.attr.palette[vga.tandy.color_select&0xf],
+ vga.attr.palette[(2|color_set)& vga.tandy.palette_mask],
+ vga.attr.palette[(4|(color_set& r_mask))& vga.tandy.palette_mask],
+ vga.attr.palette[(6|color_set)& vga.tandy.palette_mask]);
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ // PCJr
+ switch (vga.mode) {
+ case M_TANDY2:
+ VGA_SetCGA2Table(vga.attr.palette[0],vga.attr.palette[1]);
+ break;
+ case M_TANDY4:
+ VGA_SetCGA4Table(
+ vga.attr.palette[0], vga.attr.palette[1],
+ vga.attr.palette[2], vga.attr.palette[3]);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void VGA_SetModeNow(VGAModes mode);
+
+static void TANDY_FindMode(void) {
+ if (vga.tandy.mode_control & 0x2) {
+ if (vga.tandy.gfx_control & 0x10) {
+ if (vga.mode==M_TANDY4) {
+ VGA_SetModeNow(M_TANDY16);
+ } else VGA_SetMode(M_TANDY16);
+ }
+ else if (vga.tandy.gfx_control & 0x08) {
+ VGA_SetMode(M_TANDY4);
+ }
+ else if (vga.tandy.mode_control & 0x10)
+ VGA_SetMode(M_TANDY2);
+ else {
+ if (vga.mode==M_TANDY16) {
+ VGA_SetModeNow(M_TANDY4);
+ } else VGA_SetMode(M_TANDY4);
+ }
+ tandy_update_palette();
+ } else {
+ VGA_SetMode(M_TANDY_TEXT);
+ }
+}
+
+static void PCJr_FindMode(void) {
+ if (vga.tandy.mode_control & 0x2) {
+ if (vga.tandy.mode_control & 0x10) {
+ /* bit4 of mode control 1 signals 16 colour graphics mode */
+ if (vga.mode==M_TANDY4) VGA_SetModeNow(M_TANDY16); // TODO lowres mode only
+ else VGA_SetMode(M_TANDY16);
+ } else if (vga.tandy.gfx_control & 0x08) {
+ /* bit3 of mode control 2 signals 2 colour graphics mode */
+ VGA_SetMode(M_TANDY2);
+ } else {
+ /* otherwise some 4-colour graphics mode */
+ if (vga.mode==M_TANDY16) VGA_SetModeNow(M_TANDY4);
+ else VGA_SetMode(M_TANDY4);
+ }
+ tandy_update_palette();
+ } else {
+ VGA_SetMode(M_TANDY_TEXT);
+ }
+}
+
+static void TandyCheckLineMask(void ) {
+ if ( vga.tandy.extended_ram & 1 ) {
+ vga.tandy.line_mask = 0;
+ } else if ( vga.tandy.mode_control & 0x2) {
+ vga.tandy.line_mask |= 1;
+ }
+ if ( vga.tandy.line_mask ) {
+ vga.tandy.line_shift = 13;
+ vga.tandy.addr_mask = (1 << 13) - 1;
+ } else {
+ vga.tandy.addr_mask = (Bitu)(~0);
+ vga.tandy.line_shift = 0;
+ }
+}
+
+static void write_tandy_reg(Bit8u val) {
+ switch (vga.tandy.reg_index) {
+ case 0x0:
+ if (machine==MCH_PCJR) {
+ vga.tandy.mode_control=val;
+ VGA_SetBlinking(val & 0x20);
+ PCJr_FindMode();
+ if (val&0x8) vga.attr.disabled &= ~1;
+ else vga.attr.disabled |= 1;
+ } else {
+ LOG(LOG_VGAMISC,LOG_NORMAL)("Unhandled Write %2X to tandy reg %X",val,vga.tandy.reg_index);
+ }
+ break;
+ case 0x1: /* Palette mask */
+ vga.tandy.palette_mask = val;
+ tandy_update_palette();
+ break;
+ case 0x2: /* Border color */
+ vga.tandy.border_color=val;
+ break;
+ case 0x3: /* More control */
+ vga.tandy.gfx_control=val;
+ if (machine==MCH_TANDY) TANDY_FindMode();
+ else PCJr_FindMode();
+ break;
+ case 0x5: /* Extended ram page register */
+ // Bit 0 enables extended ram
+ // Bit 7 Switches clock, 0 -> cga 28.6 , 1 -> mono 32.5
+ vga.tandy.extended_ram = val;
+ //This is a bit of a hack to enable mapping video memory differently for highres mode
+ TandyCheckLineMask();
+ VGA_SetupHandlers();
+ break;
+ default:
+ if ((vga.tandy.reg_index & 0xf0) == 0x10) { // color palette
+ vga.attr.palette[vga.tandy.reg_index-0x10] = val&0xf;
+ tandy_update_palette();
+ } else
+ LOG(LOG_VGAMISC,LOG_NORMAL)("Unhandled Write %2X to tandy reg %X",val,vga.tandy.reg_index);
+ }
+}
+
+static void write_tandy(Bitu port,Bitu val,Bitu /*iolen*/) {
+ switch (port) {
+ case 0x3d8:
+ val &= 0x3f; // only bits 0-6 are used
+ if (vga.tandy.mode_control ^ val) {
+ vga.tandy.mode_control=(Bit8u)val;
+ if (val&0x8) vga.attr.disabled &= ~1;
+ else vga.attr.disabled |= 1;
+ TandyCheckLineMask();
+ VGA_SetBlinking(val & 0x20);
+ TANDY_FindMode();
+ VGA_StartResize();
+ }
+ break;
+ case 0x3d9:
+ vga.tandy.color_select=val;
+ tandy_update_palette();
+ break;
+ case 0x3da:
+ vga.tandy.reg_index=(Bit8u)val;
+ //if (val&0x10) vga.attr.disabled |= 2;
+ //else vga.attr.disabled &= ~2;
+ break;
+// case 0x3dd: //Extended ram page address register:
+// break;
+ case 0x3de:
+ write_tandy_reg((Bit8u)val);
+ break;
+ case 0x3df:
+ // CRT/processor page register
+ // See the comments on the PCJr version of this register.
+ // A difference to it is:
+ // Bit 3-5: Processor page CPU_PG
+ // The remapped range is 32kB instead of 16. Therefore CPU_PG bit 0
+ // appears to be ORed with CPU A14 (to preserve some sort of
+ // backwards compatibility?), resulting in odd pages being mapped
+ // as 2x16kB. Implemeted in vga_memory.cpp Tandy handler.
+
+ vga.tandy.line_mask = (Bit8u)(val >> 6);
+ vga.tandy.draw_bank = val & ((vga.tandy.line_mask&2) ? 0x6 : 0x7);
+ vga.tandy.mem_bank = (val >> 3) & 7;
+ TandyCheckLineMask();
+ VGA_SetupHandlers();
+ break;
+ }
+}
+
+static void write_pcjr(Bitu port,Bitu val,Bitu /*iolen*/) {
+ switch (port) {
+ case 0x3da:
+ if (vga.tandy.pcjr_flipflop) write_tandy_reg((Bit8u)val);
+ else {
+ vga.tandy.reg_index=(Bit8u)val;
+ if (vga.tandy.reg_index & 0x10)
+ vga.attr.disabled |= 2;
+ else vga.attr.disabled &= ~2;
+ }
+ vga.tandy.pcjr_flipflop=!vga.tandy.pcjr_flipflop;
+ break;
+ case 0x3df:
+ // CRT/processor page register
+
+ // Bit 0-2: CRT page PG0-2
+ // In one- and two bank modes, bit 0-2 select the 16kB memory
+ // area of system RAM that is displayed on the screen.
+ // In 4-banked modes, bit 1-2 select the 32kB memory area.
+ // Bit 2 only has effect when the PCJR upgrade to 128k is installed.
+
+ // Bit 3-5: Processor page CPU_PG
+ // Selects the 16kB area of system RAM that is mapped to
+ // the B8000h IBM PC video memory window. Since A14-A16 of the
+ // processor are unconditionally replaced with these bits when
+ // B8000h is accessed, the 16kB area is mapped to the 32kB
+ // range twice in a row. (Scuba Venture writes across the boundary)
+
+ // Bit 6-7: Video Address mode
+ // 0: CRTC addresses A0-12 directly, accessing 8k characters
+ // (+8k attributes). Used in text modes (one bank).
+ // PG0-2 in effect. 16k range.
+ // 1: CRTC A12 is replaced with CRTC RA0 (see max_scanline).
+ // This results in the even/odd scanline two bank system.
+ // PG0-2 in effect. 16k range.
+ // 2: Documented as unused. CRTC addresses A0-12, PG0 is replaced
+ // with RA1. Looks like nonsense.
+ // PG1-2 in effect. 32k range which cannot be used completely.
+ // 3: CRTC A12 is replaced with CRTC RA0, PG0 is replaced with
+ // CRTC RA1. This results in the 4-bank mode.
+ // PG1-2 in effect. 32k range.
+
+ vga.tandy.line_mask = (Bit8u)(val >> 6);
+ vga.tandy.draw_bank = val & ((vga.tandy.line_mask&2) ? 0x6 : 0x7);
+ vga.tandy.mem_bank = (val >> 3) & 7;
+ vga.tandy.draw_base = &MemBase[vga.tandy.draw_bank * 16 * 1024];
+ vga.tandy.mem_base = &MemBase[vga.tandy.mem_bank * 16 * 1024];
+ TandyCheckLineMask();
+ VGA_SetupHandlers();
+ break;
+ }
+}
+
+static void CycleHercPal(bool pressed) {
+ if (!pressed) return;
+ if (++herc_pal>2) herc_pal=0;
+ Herc_Palette();
+ VGA_DAC_CombineColor(1,7);
+}
+
+void Herc_Palette(void) {
+ switch (herc_pal) {
+ case 0: // White
+ VGA_DAC_SetEntry(0x7,0x2a,0x2a,0x2a);
+ VGA_DAC_SetEntry(0xf,0x3f,0x3f,0x3f);
+ break;
+ case 1: // Amber
+ VGA_DAC_SetEntry(0x7,0x34,0x20,0x00);
+ VGA_DAC_SetEntry(0xf,0x3f,0x34,0x00);
+ break;
+ case 2: // Green
+ VGA_DAC_SetEntry(0x7,0x00,0x26,0x00);
+ VGA_DAC_SetEntry(0xf,0x00,0x3f,0x00);
+ break;
+ }
+}
+
+static void write_hercules(Bitu port,Bitu val,Bitu /*iolen*/) {
+ switch (port) {
+ case 0x3b8: {
+ // the protected bits can always be cleared but only be set if the
+ // protection bits are set
+ if (vga.herc.mode_control&0x2) {
+ // already set
+ if (!(val&0x2)) {
+ vga.herc.mode_control &= ~0x2;
+ VGA_SetMode(M_HERC_TEXT);
+ }
+ } else {
+ // not set, can only set if protection bit is set
+ if ((val & 0x2) && (vga.herc.enable_bits & 0x1)) {
+ vga.herc.mode_control |= 0x2;
+ VGA_SetMode(M_HERC_GFX);
+ }
+ }
+ if (vga.herc.mode_control&0x80) {
+ if (!(val&0x80)) {
+ vga.herc.mode_control &= ~0x80;
+ vga.tandy.draw_base = &vga.mem.linear[0];
+ }
+ } else {
+ if ((val & 0x80) && (vga.herc.enable_bits & 0x2)) {
+ vga.herc.mode_control |= 0x80;
+ vga.tandy.draw_base = &vga.mem.linear[32*1024];
+ }
+ }
+ vga.draw.blinking = (val&0x20)!=0;
+ vga.herc.mode_control &= 0x82;
+ vga.herc.mode_control |= val & ~0x82;
+ break;
+ }
+ case 0x3bf:
+ if ( vga.herc.enable_bits ^ val) {
+ vga.herc.enable_bits=val;
+ // Bit 1 enables the upper 32k of video memory,
+ // so update the handlers
+ VGA_SetupHandlers();
+ }
+ break;
+ }
+}
+
+/* static Bitu read_hercules(Bitu port,Bitu iolen) {
+ LOG_MSG("read from Herc port %x",port);
+ return 0;
+} */
+
+Bitu read_herc_status(Bitu /*port*/,Bitu /*iolen*/) {
+ // 3BAh (R): Status Register
+ // bit 0 Horizontal sync
+ // 1 Light pen status (only some cards)
+ // 3 Video signal
+ // 4-6 000: Hercules
+ // 001: Hercules Plus
+ // 101: Hercules InColor
+ // 111: Unknown clone
+ // 7 Vertical sync inverted
+
+ double timeInFrame = PIC_FullIndex()-vga.draw.delay.framestart;
+ Bit8u retval=0x72; // Hercules ident; from a working card (Winbond W86855AF)
+ // Another known working card has 0x76 ("KeysoGood", full-length)
+ if (timeInFrame < vga.draw.delay.vrstart ||
+ timeInFrame > vga.draw.delay.vrend) retval |= 0x80;
+
+ double timeInLine=fmod(timeInFrame,vga.draw.delay.htotal);
+ if (timeInLine >= vga.draw.delay.hrstart &&
+ timeInLine <= vga.draw.delay.hrend) retval |= 0x1;
+
+ // 688 Attack sub checks bit 3 - as a workaround have the bit enabled
+ // if no sync active (corresponds to a completely white screen)
+ if ((retval&0x81)==0x80) retval |= 0x8;
+ return retval;
+}
+
+
+void VGA_SetupOther(void) {
+ Bitu i;
+ memset( &vga.tandy, 0, sizeof( vga.tandy ));
+ vga.attr.disabled = 0;
+ vga.config.bytes_skip=0;
+
+ //Initialize values common for most machines, can be overwritten
+ vga.tandy.draw_base = vga.mem.linear;
+ vga.tandy.mem_base = vga.mem.linear;
+ vga.tandy.addr_mask = 8*1024 - 1;
+ vga.tandy.line_mask = 3;
+ vga.tandy.line_shift = 13;
+
+ if (machine==MCH_CGA || IS_TANDY_ARCH) {
+ extern Bit8u int10_font_08[256 * 8];
+ for (i=0;i<256;i++) memcpy(&vga.draw.font[i*32],&int10_font_08[i*8],8);
+ vga.draw.font_tables[0]=vga.draw.font_tables[1]=vga.draw.font;
+ }
+ if (machine==MCH_CGA || IS_TANDY_ARCH || machine==MCH_HERC) {
+ IO_RegisterWriteHandler(0x3db,write_lightpen,IO_MB);
+ IO_RegisterWriteHandler(0x3dc,write_lightpen,IO_MB);
+ }
+ if (machine==MCH_HERC) {
+ extern Bit8u int10_font_14[256 * 14];
+ for (i=0;i<256;i++) memcpy(&vga.draw.font[i*32],&int10_font_14[i*14],14);
+ vga.draw.font_tables[0]=vga.draw.font_tables[1]=vga.draw.font;
+ MAPPER_AddHandler(CycleHercPal,MK_f11,0,"hercpal","Herc Pal");
+ }
+ if (machine==MCH_CGA) {
+ IO_RegisterWriteHandler(0x3d8,write_cga,IO_MB);
+ IO_RegisterWriteHandler(0x3d9,write_cga,IO_MB);
+ MAPPER_AddHandler(IncreaseHue,MK_f11,MMOD2,"inchue","Inc Hue");
+ MAPPER_AddHandler(DecreaseHue,MK_f11,0,"dechue","Dec Hue");
+ MAPPER_AddHandler(CGAModel,MK_f11,MMOD1|MMOD2,"cgamodel","CGA Model");
+ MAPPER_AddHandler(Composite,MK_f12,0,"cgacomp","CGA Comp");
+ }
+ if (machine==MCH_TANDY) {
+ write_tandy( 0x3df, 0x0, 0 );
+ IO_RegisterWriteHandler(0x3d8,write_tandy,IO_MB);
+ IO_RegisterWriteHandler(0x3d9,write_tandy,IO_MB);
+ IO_RegisterWriteHandler(0x3da,write_tandy,IO_MB);
+ IO_RegisterWriteHandler(0x3de,write_tandy,IO_MB);
+ IO_RegisterWriteHandler(0x3df,write_tandy,IO_MB);
+ }
+ if (machine==MCH_PCJR) {
+ //write_pcjr will setup base address
+ write_pcjr( 0x3df, 0x7 | (0x7 << 3), 0 );
+ IO_RegisterWriteHandler(0x3da,write_pcjr,IO_MB);
+ IO_RegisterWriteHandler(0x3df,write_pcjr,IO_MB);
+ }
+ if (machine==MCH_HERC) {
+ Bitu base=0x3b0;
+ for (Bitu i = 0; i < 4; i++) {
+ // The registers are repeated as the address is not decoded properly;
+ // The official ports are 3b4, 3b5
+ IO_RegisterWriteHandler(base+i*2,write_crtc_index_other,IO_MB);
+ IO_RegisterWriteHandler(base+i*2+1,write_crtc_data_other,IO_MB);
+ IO_RegisterReadHandler(base+i*2,read_crtc_index_other,IO_MB);
+ IO_RegisterReadHandler(base+i*2+1,read_crtc_data_other,IO_MB);
+ }
+ vga.herc.enable_bits=0;
+ vga.herc.mode_control=0xa; // first mode written will be text mode
+ vga.crtc.underline_location = 13;
+ IO_RegisterWriteHandler(0x3b8,write_hercules,IO_MB);
+ IO_RegisterWriteHandler(0x3bf,write_hercules,IO_MB);
+ IO_RegisterReadHandler(0x3ba,read_herc_status,IO_MB);
+ } else if (!IS_EGAVGA_ARCH) {
+ Bitu base=0x3d0;
+ for (Bitu port_ct=0; port_ct<4; port_ct++) {
+ IO_RegisterWriteHandler(base+port_ct*2,write_crtc_index_other,IO_MB);
+ IO_RegisterWriteHandler(base+port_ct*2+1,write_crtc_data_other,IO_MB);
+ IO_RegisterReadHandler(base+port_ct*2,read_crtc_index_other,IO_MB);
+ IO_RegisterReadHandler(base+port_ct*2+1,read_crtc_data_other,IO_MB);
+ }
+ }
+
+}
diff --git a/src/hardware/vga_paradise.cpp b/src/hardware/vga_paradise.cpp
new file mode 100644
index 000000000..f2f1334ff
--- /dev/null
+++ b/src/hardware/vga_paradise.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "setup.h"
+#include "vga.h"
+#include "inout.h"
+#include "mem.h"
+
+typedef struct {
+ Bitu PR0A;
+ Bitu PR0B;
+ Bitu PR1;
+ Bitu PR2;
+ Bitu PR3;
+ Bitu PR4;
+ Bitu PR5;
+
+ inline bool locked() { return (PR5&7)!=5; }
+
+ Bitu clockFreq[4];
+ Bitu biosMode;
+} SVGA_PVGA1A_DATA;
+
+static SVGA_PVGA1A_DATA pvga1a = { 0,0, 0,0,0,0,0, {0,0,0,0}, 0 };
+
+
+static void bank_setup_pvga1a() {
+// Note: There is some inconsistency in available documentation. Most sources tell that PVGA1A used
+// only 7 bits of bank index (VGADOC and Ferraro agree on that) but also point that there are
+// implementations with 1M of RAM which is simply not possible with 7-bit banks. This implementation
+// assumes that the eighth bit was actually wired and could be used. This does not conflict with
+// anything and actually works in WHATVGA just fine.
+ if (pvga1a.PR1 & 0x08) {
+ // TODO: Dual bank function is not supported yet
+ // TODO: Requirements are not compatible with vga_memory implementation.
+ } else {
+ // Single bank config is straightforward
+ vga.svga.bank_read = vga.svga.bank_write = pvga1a.PR0A;
+ vga.svga.bank_size = 4*1024;
+ VGA_SetupHandlers();
+ }
+}
+
+void write_p3cf_pvga1a(Bitu reg,Bitu val,Bitu iolen) {
+ if (pvga1a.locked() && reg >= 0x09 && reg <= 0x0e)
+ return;
+
+ switch (reg) {
+ case 0x09:
+ // Bank A, 4K granularity, not using bit 7
+ // Maps to A800h-AFFFh if PR1 bit 3 set and 64k config B000h-BFFFh if 128k config. A000h-AFFFh otherwise.
+ pvga1a.PR0A = val;
+ bank_setup_pvga1a();
+ break;
+ case 0x0a:
+ // Bank B, 4K granularity, not using bit 7
+ // Maps to A000h-A7FFh if PR1 bit 3 set and 64k config, A000h-AFFFh if 128k
+ pvga1a.PR0B = val;
+ bank_setup_pvga1a();
+ break;
+ case 0x0b:
+ // Memory size. We only allow to mess with bit 3 here (enable bank B) - this may break some detection schemes
+ pvga1a.PR1 = (pvga1a.PR1 & ~0x08) | (val & 0x08);
+ bank_setup_pvga1a();
+ break;
+ case 0x0c:
+ // Video configuration
+ // TODO: Figure out if there is anything worth implementing here.
+ pvga1a.PR2 = val;
+ break;
+ case 0x0d:
+ // CRT control. Bits 3-4 contain bits 16-17 of CRT start.
+ // TODO: Implement bit 2 (CRT address doubling - this mechanism is present in other chipsets as well,
+ // but not implemented in DosBox core)
+ pvga1a.PR3 = val;
+ vga.config.display_start = (vga.config.display_start & 0xffff) | ((val & 0x18)<<13);
+ vga.config.cursor_start = (vga.config.cursor_start & 0xffff) | ((val & 0x18)<<13);
+ break;
+ case 0x0e:
+ // Video control
+ // TODO: Figure out if there is anything worth implementing here.
+ pvga1a.PR4 = val;
+ break;
+ case 0x0f:
+ // Enable extended registers
+ pvga1a.PR5 = val;
+ break;
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:GFX:PVGA1A:Write to illegal index %2X", reg);
+ break;
+ }
+}
+
+Bitu read_p3cf_pvga1a(Bitu reg,Bitu iolen) {
+ if (pvga1a.locked() && reg >= 0x09 && reg <= 0x0e)
+ return 0x0;
+
+ switch (reg) {
+ case 0x09:
+ return pvga1a.PR0A;
+ case 0x0a:
+ return pvga1a.PR0B;
+ case 0x0b:
+ return pvga1a.PR1;
+ case 0x0c:
+ return pvga1a.PR2;
+ case 0x0d:
+ return pvga1a.PR3;
+ case 0x0e:
+ return pvga1a.PR4;
+ case 0x0f:
+ return pvga1a.PR5;
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:GFX:PVGA1A:Read from illegal index %2X", reg);
+ break;
+ }
+
+ return 0x0;
+}
+
+void FinishSetMode_PVGA1A(Bitu /*crtc_base*/, VGA_ModeExtraData* modeData) {
+ pvga1a.biosMode = modeData->modeNo;
+
+// Reset to single bank and set it to 0. May need to unlock first (DPaint locks on exit)
+ IO_Write(0x3ce, 0x0f);
+ Bitu oldlock = IO_Read(0x3cf);
+ IO_Write(0x3cf, 0x05);
+ IO_Write(0x3ce, 0x09);
+ IO_Write(0x3cf, 0x00);
+ IO_Write(0x3ce, 0x0a);
+ IO_Write(0x3cf, 0x00);
+ IO_Write(0x3ce, 0x0b);
+ Bit8u val = IO_Read(0x3cf);
+ IO_Write(0x3cf, val & ~0x08);
+ IO_Write(0x3ce, 0x0c);
+ IO_Write(0x3cf, 0x00);
+ IO_Write(0x3ce, 0x0d);
+ IO_Write(0x3cf, 0x00);
+ IO_Write(0x3ce, 0x0e);
+ IO_Write(0x3cf, 0x00);
+ IO_Write(0x3ce, 0x0f);
+ IO_Write(0x3cf, oldlock);
+
+ if (svga.determine_mode)
+ svga.determine_mode();
+
+ if(vga.mode != M_VGA) {
+ vga.config.compatible_chain4 = false;
+ vga.vmemwrap = vga.vmemsize;
+ } else {
+ vga.config.compatible_chain4 = true;
+ vga.vmemwrap = 256*1024;
+ }
+
+ VGA_SetupHandlers();
+}
+
+void DetermineMode_PVGA1A() {
+ // Close replica from the base implementation. It will stay here
+ // until I figure a way to either distinguish M_VGA and M_LIN8 or
+ // merge them.
+ if (vga.attr.mode_control & 1) {
+ if (vga.gfx.mode & 0x40) VGA_SetMode((pvga1a.biosMode<=0x13)?M_VGA:M_LIN8);
+ else if (vga.gfx.mode & 0x20) VGA_SetMode(M_CGA4);
+ else if ((vga.gfx.miscellaneous & 0x0c)==0x0c) VGA_SetMode(M_CGA2);
+ else VGA_SetMode((pvga1a.biosMode<=0x13)?M_EGA:M_LIN4);
+ } else {
+ VGA_SetMode(M_TEXT);
+ }
+}
+
+void SetClock_PVGA1A(Bitu which,Bitu target) {
+ if (which < 4) {
+ pvga1a.clockFreq[which]=1000*target;
+ VGA_StartResize();
+ }
+}
+
+Bitu GetClock_PVGA1A() {
+ return pvga1a.clockFreq[(vga.misc_output >> 2) & 3];
+}
+
+bool AcceptsMode_PVGA1A(Bitu mode) {
+ return VideoModeMemSize(mode) < vga.vmemsize;
+}
+
+void SVGA_Setup_ParadisePVGA1A(void) {
+ svga.write_p3cf = &write_p3cf_pvga1a;
+ svga.read_p3cf = &read_p3cf_pvga1a;
+
+ svga.set_video_mode = &FinishSetMode_PVGA1A;
+ svga.determine_mode = &DetermineMode_PVGA1A;
+ svga.set_clock = &SetClock_PVGA1A;
+ svga.get_clock = &GetClock_PVGA1A;
+ svga.accepts_mode = &AcceptsMode_PVGA1A;
+
+ VGA_SetClock(0,CLK_25);
+ VGA_SetClock(1,CLK_28);
+ VGA_SetClock(2,32400); // could not find documentation
+ VGA_SetClock(3,35900);
+
+ // Adjust memory, default to 512K
+ if (vga.vmemsize == 0)
+ vga.vmemsize = 512*1024;
+
+ if (vga.vmemsize < 512*1024) {
+ vga.vmemsize = 256*1024;
+ pvga1a.PR1 = 1<<6;
+ } else if (vga.vmemsize > 512*1024) {
+ vga.vmemsize = 1024*1024;
+ pvga1a.PR1 = 3<<6;
+ } else {
+ pvga1a.PR1 = 2<<6;
+ }
+
+ // Paradise ROM signature
+ PhysPt rom_base=PhysMake(0xc000,0);
+ phys_writeb(rom_base+0x007d,'V');
+ phys_writeb(rom_base+0x007e,'G');
+ phys_writeb(rom_base+0x007f,'A');
+ phys_writeb(rom_base+0x0080,'=');
+
+ IO_Write(0x3cf, 0x05); // Enable!
+}
diff --git a/src/hardware/vga_s3.cpp b/src/hardware/vga_s3.cpp
new file mode 100644
index 000000000..b0347ea8f
--- /dev/null
+++ b/src/hardware/vga_s3.cpp
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "inout.h"
+#include "vga.h"
+#include "mem.h"
+
+void SVGA_S3_WriteCRTC(Bitu reg,Bitu val,Bitu iolen) {
+ switch (reg) {
+ case 0x31: /* CR31 Memory Configuration */
+//TODO Base address
+ vga.s3.reg_31 = val;
+ vga.config.compatible_chain4 = !(val&0x08);
+ if (vga.config.compatible_chain4) vga.vmemwrap = 256*1024;
+ else vga.vmemwrap = vga.vmemsize;
+ vga.config.display_start = (vga.config.display_start&~0x30000)|((val&0x30)<<12);
+ VGA_DetermineMode();
+ VGA_SetupHandlers();
+ break;
+ /*
+ 0 Enable Base Address Offset (CPUA BASE). Enables bank operation if
+ set, disables if clear.
+ 1 Two Page Screen Image. If set enables 2048 pixel wide screen setup
+ 2 VGA 16bit Memory Bus Width. Set for 16bit, clear for 8bit
+ 3 Use Enhanced Mode Memory Mapping (ENH MAP). Set to enable access to
+ video memory above 256k.
+ 4-5 Bit 16-17 of the Display Start Address. For the 801/5,928 see index
+ 51h, for the 864/964 see index 69h.
+ 6 High Speed Text Display Font Fetch Mode. If set enables Page Mode
+ for Alpha Mode Font Access.
+ 7 (not 864/964) Extended BIOS ROM Space Mapped out. If clear the area
+ C6800h-C7FFFh is mapped out, if set it is accessible.
+ */
+ case 0x35: /* CR35 CRT Register Lock */
+ if (vga.s3.reg_lock1 != 0x48) return; //Needed for uvconfig detection
+ vga.s3.reg_35=val & 0xf0;
+ if ((vga.svga.bank_read & 0xf) ^ (val & 0xf)) {
+ vga.svga.bank_read&=0xf0;
+ vga.svga.bank_read|=val & 0xf;
+ vga.svga.bank_write = vga.svga.bank_read;
+ VGA_SetupHandlers();
+ }
+ break;
+ /*
+ 0-3 CPU Base Address. 64k bank number. For the 801/5 and 928 see 3d4h
+ index 51h bits 2-3. For the 864/964 see index 6Ah.
+ 4 Lock Vertical Timing Registers (LOCK VTMG). Locks 3d4h index 6, 7
+ (bits 0,2,3,5,7), 9 bit 5, 10h, 11h bits 0-3, 15h, 16h if set
+ 5 Lock Horizontal Timing Registers (LOCK HTMG). Locks 3d4h index
+ 0,1,2,3,4,5,17h bit 2 if set
+ 6 (911/924) Lock VSync Polarity.
+ 7 (911/924) Lock HSync Polarity.
+ */
+ case 0x38: /* CR38 Register Lock 1 */
+ vga.s3.reg_lock1=val;
+ break;
+ case 0x39: /* CR39 Register Lock 2 */
+ vga.s3.reg_lock2=val;
+ break;
+ case 0x3a:
+ vga.s3.reg_3a = val;
+ break;
+ case 0x40: /* CR40 System Config */
+ vga.s3.reg_40 = val;
+ break;
+ case 0x41: /* CR41 BIOS flags */
+ vga.s3.reg_41 = val;
+ break;
+ case 0x43: /* CR43 Extended Mode */
+ vga.s3.reg_43=val & ~0x4;
+ if (((val & 0x4) ^ (vga.config.scan_len >> 6)) & 0x4) {
+ vga.config.scan_len&=0x2ff;
+ vga.config.scan_len|=(val & 0x4) << 6;
+ VGA_CheckScanLength();
+ }
+ break;
+ /*
+ 2 Logical Screen Width bit 8. Bit 8 of the Display Offset Register/
+ (3d4h index 13h). (801/5,928) Only active if 3d4h index 51h bits 4-5
+ are 0
+ */
+ case 0x45: /* Hardware cursor mode */
+ vga.s3.hgc.curmode = val;
+ // Activate hardware cursor code if needed
+ VGA_ActivateHardwareCursor();
+ break;
+ case 0x46:
+ vga.s3.hgc.originx = (vga.s3.hgc.originx & 0x00ff) | (val << 8);
+ break;
+ case 0x47: /* HGC orgX */
+ vga.s3.hgc.originx = (vga.s3.hgc.originx & 0xff00) | val;
+ break;
+ case 0x48:
+ vga.s3.hgc.originy = (vga.s3.hgc.originy & 0x00ff) | (val << 8);
+ break;
+ case 0x49: /* HGC orgY */
+ vga.s3.hgc.originy = (vga.s3.hgc.originy & 0xff00) | val;
+ break;
+ case 0x4A: /* HGC foreground stack */
+ if (vga.s3.hgc.fstackpos > 2) vga.s3.hgc.fstackpos = 0;
+ vga.s3.hgc.forestack[vga.s3.hgc.fstackpos] = val;
+ vga.s3.hgc.fstackpos++;
+ break;
+ case 0x4B: /* HGC background stack */
+ if (vga.s3.hgc.bstackpos > 2) vga.s3.hgc.bstackpos = 0;
+ vga.s3.hgc.backstack[vga.s3.hgc.bstackpos] = val;
+ vga.s3.hgc.bstackpos++;
+ break;
+ case 0x4c: /* HGC start address high byte*/
+ vga.s3.hgc.startaddr &=0xff;
+ vga.s3.hgc.startaddr |= ((val & 0xf) << 8);
+ if ((((Bitu)vga.s3.hgc.startaddr)<<10)+((64*64*2)/8) > vga.vmemsize) {
+ vga.s3.hgc.startaddr &= 0xff; // put it back to some sane area;
+ // if read back of this address is ever implemented this needs to change
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:S3:CRTC: HGC pattern address beyond video memory" );
+ }
+ break;
+ case 0x4d: /* HGC start address low byte*/
+ vga.s3.hgc.startaddr &=0xff00;
+ vga.s3.hgc.startaddr |= (val & 0xff);
+ break;
+ case 0x4e: /* HGC pattern start X */
+ vga.s3.hgc.posx = val & 0x3f; // bits 0-5
+ break;
+ case 0x4f: /* HGC pattern start Y */
+ vga.s3.hgc.posy = val & 0x3f; // bits 0-5
+ break;
+ case 0x50: // Extended System Control 1
+ vga.s3.reg_50 = val;
+ switch (val & S3_XGA_CMASK) {
+ case S3_XGA_32BPP: vga.s3.xga_color_mode = M_LIN32; break;
+ case S3_XGA_16BPP: vga.s3.xga_color_mode = M_LIN16; break;
+ case S3_XGA_8BPP: vga.s3.xga_color_mode = M_LIN8; break;
+ }
+ switch (val & S3_XGA_WMASK) {
+ case S3_XGA_1024: vga.s3.xga_screen_width = 1024; break;
+ case S3_XGA_1152: vga.s3.xga_screen_width = 1152; break;
+ case S3_XGA_640: vga.s3.xga_screen_width = 640; break;
+ case S3_XGA_800: vga.s3.xga_screen_width = 800; break;
+ case S3_XGA_1280: vga.s3.xga_screen_width = 1280; break;
+ default: vga.s3.xga_screen_width = 1024; break;
+ }
+ break;
+ case 0x51: /* Extended System Control 2 */
+ vga.s3.reg_51=val & 0xc0; //Only store bits 6,7
+ vga.config.display_start&=0xF3FFFF;
+ vga.config.display_start|=(val & 3) << 18;
+ if ((vga.svga.bank_read&0x30) ^ ((val&0xc)<<2)) {
+ vga.svga.bank_read&=0xcf;
+ vga.svga.bank_read|=(val&0xc)<<2;
+ vga.svga.bank_write = vga.svga.bank_read;
+ VGA_SetupHandlers();
+ }
+ if (((val & 0x30) ^ (vga.config.scan_len >> 4)) & 0x30) {
+ vga.config.scan_len&=0xff;
+ vga.config.scan_len|=(val & 0x30) << 4;
+ VGA_CheckScanLength();
+ }
+ break;
+ /*
+ 0 (80x) Display Start Address bit 18
+ 0-1 (928 +) Display Start Address bit 18-19
+ Bits 16-17 are in index 31h bits 4-5, Bits 0-15 are in 3d4h index
+ 0Ch,0Dh. For the 864/964 see 3d4h index 69h
+ 2 (80x) CPU BASE. CPU Base Address Bit 18.
+ 2-3 (928 +) Old CPU Base Address Bits 19-18.
+ 64K Bank register bits 4-5. Bits 0-3 are in 3d4h index 35h.
+ For the 864/964 see 3d4h index 6Ah
+ 4-5 Logical Screen Width Bit [8-9]. Bits 8-9 of the CRTC Offset register
+ (3d4h index 13h). If this field is 0, 3d4h index 43h bit 2 is active
+ 6 (928,964) DIS SPXF. Disable Split Transfers if set. Spilt Transfers
+ allows transferring one half of the VRAM shift register data while
+ the other half is being output. For the 964 Split Transfers
+ must be enabled in enhanced modes (4AE8h bit 0 set). Guess: They
+ probably can't time the VRAM load cycle closely enough while the
+ graphics engine is running.
+ 7 (not 864/964) Enable EPROM Write. If set enables flash memory write
+ control to the BIOS ROM address
+ */
+ case 0x52: // Extended System Control 1
+ vga.s3.reg_52 = val;
+ break;
+ case 0x53:
+ // Map or unmap MMIO
+ // bit 4 = MMIO at A0000
+ // bit 3 = MMIO at LFB + 16M (should be fine if its always enabled for now)
+ if(vga.s3.ext_mem_ctrl!=val) {
+ vga.s3.ext_mem_ctrl = val;
+ VGA_SetupHandlers();
+ }
+ break;
+ case 0x55: /* Extended Video DAC Control */
+ vga.s3.reg_55=val;
+ break;
+ /*
+ 0-1 DAC Register Select Bits. Passed to the RS2 and RS3 pins on the
+ RAMDAC, allowing access to all 8 or 16 registers on advanced RAMDACs.
+ If this field is 0, 3d4h index 43h bit 1 is active.
+ 2 Enable General Input Port Read. If set DAC reads are disabled and the
+ STRD strobe for reading the General Input Port is enabled for reading
+ while DACRD is active, if clear DAC reads are enabled.
+ 3 (928) Enable External SID Operation if set. If set video data is
+ passed directly from the VRAMs to the DAC rather than through the
+ VGA chip
+ 4 Hardware Cursor MS/X11 Mode. If set the Hardware Cursor is in X11
+ mode, if clear in MS-Windows mode
+ 5 (80x,928) Hardware Cursor External Operation Mode. If set the two
+ bits of cursor data ,is output on the HC[0-1] pins for the video DAC
+ The SENS pin becomes HC1 and the MID2 pin becomes HC0.
+ 6 ??
+ 7 (80x,928) Disable PA Output. If set PA[0-7] and VCLK are tristated.
+ (864/964) TOFF VCLK. Tri-State Off VCLK Output. VCLK output tri
+ -stated if set
+ */
+ case 0x58: /* Linear Address Window Control */
+ vga.s3.reg_58=val;
+ break;
+ /*
+ 0-1 Linear Address Window Size. Must be less than or equal to video
+ memory size. 0: 64K, 1: 1MB, 2: 2MB, 3: 4MB (928)/8Mb (864/964)
+ 2 (not 864/964) Enable Read Ahead Cache if set
+ 3 (80x,928) ISA Latch Address. If set latches address during every ISA
+ cycle, unlatches during every ISA cycle if clear.
+ (864/964) LAT DEL. Address Latch Delay Control (VL-Bus only). If set
+ address latching occours in the T1 cycle, if clear in the T2 cycle
+ (I.e. one clock cycle delayed).
+ 4 ENB LA. Enable Linear Addressing if set.
+ 5 (not 864/964) Limit Entry Depth for Write-Post. If set limits Write
+ -Post Entry Depth to avoid ISA bus timeout due to wait cycle limit.
+ 6 (928,964) Serial Access Mode (SAM) 256 Words Control. If set SAM
+ control is 256 words, if clear 512 words.
+ 7 (928) RAS 6-MCLK. If set the random read/write cycle time is 6MCLKs,
+ if clear 7MCLKs
+ */
+ case 0x59: /* Linear Address Window Position High */
+ if ((vga.s3.la_window&0xff00) ^ (val << 8)) {
+ vga.s3.la_window=(vga.s3.la_window&0x00ff) | (val << 8);
+ VGA_StartUpdateLFB();
+ }
+ break;
+ case 0x5a: /* Linear Address Window Position Low */
+ if ((vga.s3.la_window&0x00ff) ^ val) {
+ vga.s3.la_window=(vga.s3.la_window&0xff00) | val;
+ VGA_StartUpdateLFB();
+ }
+ break;
+ case 0x5D: /* Extended Horizontal Overflow */
+ if ((val ^ vga.s3.ex_hor_overflow) & 3) {
+ vga.s3.ex_hor_overflow=val;
+ VGA_StartResize();
+ } else vga.s3.ex_hor_overflow=val;
+ break;
+ /*
+ 0 Horizontal Total bit 8. Bit 8 of the Horizontal Total register (3d4h
+ index 0)
+ 1 Horizontal Display End bit 8. Bit 8 of the Horizontal Display End
+ register (3d4h index 1)
+ 2 Start Horizontal Blank bit 8. Bit 8 of the Horizontal Start Blanking
+ register (3d4h index 2).
+ 3 (864,964) EHB+64. End Horizontal Blank +64. If set the /BLANK pulse
+ is extended by 64 DCLKs. Note: Is this bit 6 of 3d4h index 3 or
+ does it really extend by 64 ?
+ 4 Start Horizontal Sync Position bit 8. Bit 8 of the Horizontal Start
+ Retrace register (3d4h index 4).
+ 5 (864,964) EHS+32. End Horizontal Sync +32. If set the HSYNC pulse
+ is extended by 32 DCLKs. Note: Is this bit 5 of 3d4h index 5 or
+ does it really extend by 32 ?
+ 6 (928,964) Data Transfer Position bit 8. Bit 8 of the Data Transfer
+ Position register (3d4h index 3Bh)
+ 7 (928,964) Bus-Grant Terminate Position bit 8. Bit 8 of the Bus Grant
+ Termination register (3d4h index 5Fh).
+ */
+ case 0x5e: /* Extended Vertical Overflow */
+ vga.config.line_compare=(vga.config.line_compare & 0x3ff) | (val & 0x40) << 4;
+ if ((val ^ vga.s3.ex_ver_overflow) & 0x3) {
+ vga.s3.ex_ver_overflow=val;
+ VGA_StartResize();
+ } else vga.s3.ex_ver_overflow=val;
+ break;
+ /*
+ 0 Vertical Total bit 10. Bit 10 of the Vertical Total register (3d4h
+ index 6). Bits 8 and 9 are in 3d4h index 7 bit 0 and 5.
+ 1 Vertical Display End bit 10. Bit 10 of the Vertical Display End
+ register (3d4h index 12h). Bits 8 and 9 are in 3d4h index 7 bit 1
+ and 6
+ 2 Start Vertical Blank bit 10. Bit 10 of the Vertical Start Blanking
+ register (3d4h index 15h). Bit 8 is in 3d4h index 7 bit 3 and bit 9
+ in 3d4h index 9 bit 5
+ 4 Vertical Retrace Start bit 10. Bit 10 of the Vertical Start Retrace
+ register (3d4h index 10h). Bits 8 and 9 are in 3d4h index 7 bit 2
+ and 7.
+ 6 Line Compare Position bit 10. Bit 10 of the Line Compare register
+ (3d4h index 18h). Bit 8 is in 3d4h index 7 bit 4 and bit 9 in 3d4h
+ index 9 bit 6.
+ */
+ case 0x67: /* Extended Miscellaneous Control 2 */
+ /*
+ 0 VCLK PHS. VCLK Phase With Respect to DCLK. If clear VLKC is inverted
+ DCLK, if set VCLK = DCLK.
+ 2-3 (Trio64V+) streams mode
+ 00 disable Streams Processor
+ 01 overlay secondary stream on VGA-mode background
+ 10 reserved
+ 11 full Streams Processor operation
+ 4-7 Pixel format.
+ 0 Mode 0: 8bit (1 pixel/VCLK)
+ 1 Mode 8: 8bit (2 pixels/VCLK)
+ 3 Mode 9: 15bit (1 pixel/VCLK)
+ 5 Mode 10: 16bit (1 pixel/VCLK)
+ 7 Mode 11: 24/32bit (2 VCLKs/pixel)
+ 13 (732/764) 32bit (1 pixel/VCLK)
+ */
+ vga.s3.misc_control_2=val;
+ VGA_DetermineMode();
+ break;
+ case 0x69: /* Extended System Control 3 */
+ if (((vga.config.display_start & 0x1f0000)>>16) ^ (val & 0x1f)) {
+ vga.config.display_start&=0xffff;
+ vga.config.display_start|=(val & 0x1f) << 16;
+ }
+ break;
+ case 0x6a: /* Extended System Control 4 */
+ vga.svga.bank_read=val & 0x7f;
+ vga.svga.bank_write = vga.svga.bank_read;
+ VGA_SetupHandlers();
+ break;
+ case 0x6b: // BIOS scratchpad: LFB address
+ vga.s3.reg_6b=(Bit8u)val;
+ break;
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:S3:CRTC:Write to illegal index %2X", reg );
+ break;
+ }
+}
+
+Bitu SVGA_S3_ReadCRTC( Bitu reg, Bitu iolen) {
+ switch (reg) {
+ case 0x24: /* attribute controller index (read only) */
+ case 0x26:
+ return ((vga.attr.disabled & 1)?0x00:0x20) | (vga.attr.index & 0x1f);
+ case 0x2d: /* Extended Chip ID (high byte of PCI device ID) */
+ return 0x88;
+ case 0x2e: /* New Chip ID (low byte of PCI device ID) */
+ return 0x11; // Trio64
+ case 0x2f: /* Revision */
+ return 0x00; // Trio64 (exact value?)
+// return 0x44; // Trio64 V+
+ case 0x30: /* CR30 Chip ID/REV register */
+ return 0xe1; // Trio+ dual byte
+ case 0x31: /* CR31 Memory Configuration */
+//TODO mix in bits from baseaddress;
+ return vga.s3.reg_31;
+ case 0x35: /* CR35 CRT Register Lock */
+ return vga.s3.reg_35|(vga.svga.bank_read & 0xf);
+ case 0x36: /* CR36 Reset State Read 1 */
+ return vga.s3.reg_36;
+ case 0x37: /* Reset state read 2 */
+ return 0x2b;
+ case 0x38: /* CR38 Register Lock 1 */
+ return vga.s3.reg_lock1;
+ case 0x39: /* CR39 Register Lock 2 */
+ return vga.s3.reg_lock2;
+ case 0x3a:
+ return vga.s3.reg_3a;
+ case 0x40: /* CR40 system config */
+ return vga.s3.reg_40;
+ case 0x41: /* CR40 system config */
+ return vga.s3.reg_41;
+ case 0x42: // not interlaced
+ return 0x0d;
+ case 0x43: /* CR43 Extended Mode */
+ return vga.s3.reg_43|((vga.config.scan_len>>6)&0x4);
+ case 0x45: /* Hardware cursor mode */
+ vga.s3.hgc.bstackpos = 0;
+ vga.s3.hgc.fstackpos = 0;
+ return vga.s3.hgc.curmode|0xa0;
+ case 0x46:
+ return vga.s3.hgc.originx>>8;
+ case 0x47: /* HGC orgX */
+ return vga.s3.hgc.originx&0xff;
+ case 0x48:
+ return vga.s3.hgc.originy>>8;
+ case 0x49: /* HGC orgY */
+ return vga.s3.hgc.originy&0xff;
+ case 0x4A: /* HGC foreground stack */
+ return vga.s3.hgc.forestack[vga.s3.hgc.fstackpos];
+ case 0x4B: /* HGC background stack */
+ return vga.s3.hgc.backstack[vga.s3.hgc.bstackpos];
+ case 0x50: // CR50 Extended System Control 1
+ return vga.s3.reg_50;
+ case 0x51: /* Extended System Control 2 */
+ return ((vga.config.display_start >> 16) & 3 ) |
+ ((vga.svga.bank_read & 0x30) >> 2) |
+ ((vga.config.scan_len & 0x300) >> 4) |
+ vga.s3.reg_51;
+ case 0x52: // CR52 Extended BIOS flags 1
+ return vga.s3.reg_52;
+ case 0x53:
+ return vga.s3.ext_mem_ctrl;
+ case 0x55: /* Extended Video DAC Control */
+ return vga.s3.reg_55;
+ case 0x58: /* Linear Address Window Control */
+ return vga.s3.reg_58;
+ case 0x59: /* Linear Address Window Position High */
+ return (vga.s3.la_window >> 8);
+ case 0x5a: /* Linear Address Window Position Low */
+ return (vga.s3.la_window & 0xff);
+ case 0x5D: /* Extended Horizontal Overflow */
+ return vga.s3.ex_hor_overflow;
+ case 0x5e: /* Extended Vertical Overflow */
+ return vga.s3.ex_ver_overflow;
+ case 0x67: /* Extended Miscellaneous Control 2 */
+ return vga.s3.misc_control_2;
+ case 0x69: /* Extended System Control 3 */
+ return (Bit8u)((vga.config.display_start & 0x1f0000)>>16);
+ case 0x6a: /* Extended System Control 4 */
+ return (Bit8u)(vga.svga.bank_read & 0x7f);
+ case 0x6b: // BIOS scatchpad: LFB address
+ return vga.s3.reg_6b;
+ default:
+ return 0x00;
+ }
+}
+
+void SVGA_S3_WriteSEQ(Bitu reg,Bitu val,Bitu iolen) {
+ if (reg>0x8 && vga.s3.pll.lock!=0x6) return;
+ switch (reg) {
+ case 0x08:
+ vga.s3.pll.lock=val;
+ break;
+ case 0x10: /* Memory PLL Data Low */
+ vga.s3.mclk.n=val & 0x1f;
+ vga.s3.mclk.r=val >> 5;
+ break;
+ case 0x11: /* Memory PLL Data High */
+ vga.s3.mclk.m=val & 0x7f;
+ break;
+ case 0x12: /* Video PLL Data Low */
+ vga.s3.clk[3].n=val & 0x1f;
+ vga.s3.clk[3].r=val >> 5;
+ break;
+ case 0x13: /* Video PLL Data High */
+ vga.s3.clk[3].m=val & 0x7f;
+ break;
+ case 0x15:
+ vga.s3.pll.cmd=val;
+ VGA_StartResize();
+ break;
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:S3:SEQ:Write to illegal index %2X", reg );
+ break;
+ }
+}
+
+Bitu SVGA_S3_ReadSEQ(Bitu reg,Bitu iolen) {
+ /* S3 specific group */
+ if (reg>0x8 && vga.s3.pll.lock!=0x6) {
+ if (reg<0x1b) return 0;
+ else return reg;
+ }
+ switch (reg) {
+ case 0x08: /* PLL Unlock */
+ return vga.s3.pll.lock;
+ case 0x10: /* Memory PLL Data Low */
+ return vga.s3.mclk.n || (vga.s3.mclk.r << 5);
+ case 0x11: /* Memory PLL Data High */
+ return vga.s3.mclk.m;
+ case 0x12: /* Video PLL Data Low */
+ return vga.s3.clk[3].n || (vga.s3.clk[3].r << 5);
+ case 0x13: /* Video Data High */
+ return vga.s3.clk[3].m;
+ case 0x15:
+ return vga.s3.pll.cmd;
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:S3:SEQ:Read from illegal index %2X", reg);
+ return 0;
+ }
+}
+
+Bitu SVGA_S3_GetClock(void) {
+ Bitu clock = (vga.misc_output >> 2) & 3;
+ if (clock == 0)
+ clock = 25175000;
+ else if (clock == 1)
+ clock = 28322000;
+ else
+ clock=1000*S3_CLOCK(vga.s3.clk[clock].m,vga.s3.clk[clock].n,vga.s3.clk[clock].r);
+ /* Check for dual transfer, master clock/2 */
+ if (vga.s3.pll.cmd & 0x10) clock/=2;
+ return clock;
+}
+
+bool SVGA_S3_HWCursorActive(void) {
+ return (vga.s3.hgc.curmode & 0x1) != 0;
+}
+
+bool SVGA_S3_AcceptsMode(Bitu mode) {
+ return VideoModeMemSize(mode) < vga.vmemsize;
+}
+
+void SVGA_Setup_S3Trio(void) {
+ svga.write_p3d5 = &SVGA_S3_WriteCRTC;
+ svga.read_p3d5 = &SVGA_S3_ReadCRTC;
+ svga.write_p3c5 = &SVGA_S3_WriteSEQ;
+ svga.read_p3c5 = &SVGA_S3_ReadSEQ;
+ svga.write_p3c0 = 0; /* no S3-specific functionality */
+ svga.read_p3c1 = 0; /* no S3-specific functionality */
+
+ svga.set_video_mode = 0; /* implemented in core */
+ svga.determine_mode = 0; /* implemented in core */
+ svga.set_clock = 0; /* implemented in core */
+ svga.get_clock = &SVGA_S3_GetClock;
+ svga.hardware_cursor_active = &SVGA_S3_HWCursorActive;
+ svga.accepts_mode = &SVGA_S3_AcceptsMode;
+
+ if (vga.vmemsize == 0)
+ vga.vmemsize = 2*1024*1024; // the most common S3 configuration
+
+ // Set CRTC 36 to specify amount of VRAM and PCI
+ if (vga.vmemsize < 1024*1024) {
+ vga.vmemsize = 512*1024;
+ vga.s3.reg_36 = 0xfa; // less than 1mb fast page mode
+ } else if (vga.vmemsize < 2048*1024) {
+ vga.vmemsize = 1024*1024;
+ vga.s3.reg_36 = 0xda; // 1mb fast page mode
+ } else if (vga.vmemsize < 3072*1024) {
+ vga.vmemsize = 2048*1024;
+ vga.s3.reg_36 = 0x9a; // 2mb fast page mode
+ } else if (vga.vmemsize < 4096*1024) {
+ vga.vmemsize = 3072*1024;
+ vga.s3.reg_36 = 0x5a; // 3mb fast page mode
+ } else { // Trio64 supported only up to 4M
+ vga.vmemsize = 4096*1024;
+ vga.s3.reg_36 = 0x1a; // 4mb fast page mode
+ }
+
+ // S3 ROM signature
+ PhysPt rom_base=PhysMake(0xc000,0);
+ phys_writeb(rom_base+0x003f,'S');
+ phys_writeb(rom_base+0x0040,'3');
+ phys_writeb(rom_base+0x0041,' ');
+ phys_writeb(rom_base+0x0042,'8');
+ phys_writeb(rom_base+0x0043,'6');
+ phys_writeb(rom_base+0x0044,'C');
+ phys_writeb(rom_base+0x0045,'7');
+ phys_writeb(rom_base+0x0046,'6');
+ phys_writeb(rom_base+0x0047,'4');
+}
diff --git a/src/hardware/vga_seq.cpp b/src/hardware/vga_seq.cpp
new file mode 100644
index 000000000..7ce5ab2a3
--- /dev/null
+++ b/src/hardware/vga_seq.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "inout.h"
+#include "vga.h"
+
+#define seq(blah) vga.seq.blah
+
+Bitu read_p3c4(Bitu /*port*/,Bitu /*iolen*/) {
+ return seq(index);
+}
+
+void write_p3c4(Bitu /*port*/,Bitu val,Bitu /*iolen*/) {
+ seq(index)=val;
+}
+
+void write_p3c5(Bitu /*port*/,Bitu val,Bitu iolen) {
+// LOG_MSG("SEQ WRITE reg %X val %X",seq(index),val);
+ switch(seq(index)) {
+ case 0: /* Reset */
+ seq(reset)=val;
+ break;
+ case 1: /* Clocking Mode */
+ if (val!=seq(clocking_mode)) {
+ // don't resize if only the screen off bit was changed
+ if ((val&(~0x20))!=(seq(clocking_mode)&(~0x20))) {
+ seq(clocking_mode)=val;
+ VGA_StartResize();
+ } else {
+ seq(clocking_mode)=val;
+ }
+ if (val & 0x20) vga.attr.disabled |= 0x2;
+ else vga.attr.disabled &= ~0x2;
+ }
+ /* TODO Figure this out :)
+ 0 If set character clocks are 8 dots wide, else 9.
+ 2 If set loads video serializers every other character
+ clock cycle, else every one.
+ 3 If set the Dot Clock is Master Clock/2, else same as Master Clock
+ (See 3C2h bit 2-3). (Doubles pixels). Note: on some SVGA chipsets
+ this bit also affects the Sequencer mode.
+ 4 If set loads video serializers every fourth character clock cycle,
+ else every one.
+ 5 if set turns off screen and gives all memory cycles to the CPU
+ interface.
+ */
+ break;
+ case 2: /* Map Mask */
+ seq(map_mask)=val & 15;
+ vga.config.full_map_mask=FillTable[val & 15];
+ vga.config.full_not_map_mask=~vga.config.full_map_mask;
+ /*
+ 0 Enable writes to plane 0 if set
+ 1 Enable writes to plane 1 if set
+ 2 Enable writes to plane 2 if set
+ 3 Enable writes to plane 3 if set
+ */
+ break;
+ case 3: /* Character Map Select */
+ {
+ seq(character_map_select)=val;
+ Bit8u font1=(val & 0x3) << 1;
+ if (IS_VGA_ARCH) font1|=(val & 0x10) >> 4;
+ vga.draw.font_tables[0]=&vga.draw.font[font1*8*1024];
+ Bit8u font2=((val & 0xc) >> 1);
+ if (IS_VGA_ARCH) font2|=(val & 0x20) >> 5;
+ vga.draw.font_tables[1]=&vga.draw.font[font2*8*1024];
+ }
+ /*
+ 0,1,4 Selects VGA Character Map (0..7) if bit 3 of the character
+ attribute is clear.
+ 2,3,5 Selects VGA Character Map (0..7) if bit 3 of the character
+ attribute is set.
+ Note: Character Maps are placed as follows:
+ Map 0 at 0k, 1 at 16k, 2 at 32k, 3: 48k, 4: 8k, 5: 24k, 6: 40k, 7: 56k
+ */
+ break;
+ case 4: /* Memory Mode */
+ /*
+ 0 Set if in an alphanumeric mode, clear in graphics modes.
+ 1 Set if more than 64kbytes on the adapter.
+ 2 Enables Odd/Even addressing mode if set. Odd/Even mode places all odd
+ bytes in plane 1&3, and all even bytes in plane 0&2.
+ 3 If set address bit 0-1 selects video memory planes (256 color mode),
+ rather than the Map Mask and Read Map Select Registers.
+ */
+ seq(memory_mode)=val;
+ if (IS_VGA_ARCH) {
+ /* Changing this means changing the VGA Memory Read/Write Handler */
+ if (val&0x08) vga.config.chained=true;
+ else vga.config.chained=false;
+ VGA_SetupHandlers();
+ }
+ break;
+ default:
+ if (svga.write_p3c5) {
+ svga.write_p3c5(seq(index), val, iolen);
+ } else {
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:SEQ:Write to illegal index %2X",seq(index));
+ }
+ break;
+ }
+}
+
+
+Bitu read_p3c5(Bitu /*port*/,Bitu iolen) {
+// LOG_MSG("VGA:SEQ:Read from index %2X",seq(index));
+ switch(seq(index)) {
+ case 0: /* Reset */
+ return seq(reset);
+ break;
+ case 1: /* Clocking Mode */
+ return seq(clocking_mode);
+ break;
+ case 2: /* Map Mask */
+ return seq(map_mask);
+ break;
+ case 3: /* Character Map Select */
+ return seq(character_map_select);
+ break;
+ case 4: /* Memory Mode */
+ return seq(memory_mode);
+ break;
+ default:
+ if (svga.read_p3c5)
+ return svga.read_p3c5(seq(index), iolen);
+ break;
+ }
+ return 0;
+}
+
+
+void VGA_SetupSEQ(void) {
+ if (IS_EGAVGA_ARCH) {
+ IO_RegisterWriteHandler(0x3c4,write_p3c4,IO_MB);
+ IO_RegisterWriteHandler(0x3c5,write_p3c5,IO_MB);
+ if (IS_VGA_ARCH) {
+ IO_RegisterReadHandler(0x3c4,read_p3c4,IO_MB);
+ IO_RegisterReadHandler(0x3c5,read_p3c5,IO_MB);
+ }
+ }
+}
+
diff --git a/src/hardware/vga_tseng.cpp b/src/hardware/vga_tseng.cpp
new file mode 100644
index 000000000..14f38e724
--- /dev/null
+++ b/src/hardware/vga_tseng.cpp
@@ -0,0 +1,807 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+
+#include "dosbox.h"
+#include "setup.h"
+#include "vga.h"
+#include "inout.h"
+#include "mem.h"
+#include <cstdlib>
+// Tseng ET4K data
+typedef struct {
+ Bit8u extensionsEnabled;
+
+// Stored exact values of some registers. Documentation only specifies some bits but hardware checks may
+// expect other bits to be preserved.
+ Bitu store_3d4_31;
+ Bitu store_3d4_32;
+ Bitu store_3d4_33;
+ Bitu store_3d4_34;
+ Bitu store_3d4_35;
+ Bitu store_3d4_36;
+ Bitu store_3d4_37;
+ Bitu store_3d4_3f;
+
+ Bitu store_3c0_16;
+ Bitu store_3c0_17;
+
+ Bitu store_3c4_06;
+ Bitu store_3c4_07;
+
+ Bitu clockFreq[16];
+ Bitu biosMode;
+} SVGA_ET4K_DATA;
+
+static SVGA_ET4K_DATA et4k = { 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 };
+
+#define STORE_ET4K(port, index) \
+ case 0x##index: \
+ et4k.store_##port##_##index = val; \
+ break;
+
+#define RESTORE_ET4K(port, index) \
+ case 0x##index: \
+ return et4k.store_##port##_##index;
+
+// Tseng ET4K implementation
+void write_p3d5_et4k(Bitu reg,Bitu val,Bitu iolen) {
+ if(!et4k.extensionsEnabled && reg!=0x33)
+ return;
+
+ switch(reg) {
+ /*
+ 3d4h index 31h (R/W): General Purpose
+ bit 0-3 Scratch pad
+ 6-7 Clock Select bits 3-4. Bits 0-1 are in 3C2h/3CCh bits 2-3.
+ */
+ STORE_ET4K(3d4, 31);
+
+ // 3d4h index 32h - RAS/CAS Configuration (R/W)
+ // No effect on emulation. Should not be written by software.
+ STORE_ET4K(3d4, 32);
+
+ case 0x33:
+ // 3d4 index 33h (R/W): Extended start Address
+ // 0-1 Display Start Address bits 16-17
+ // 2-3 Cursor start address bits 16-17
+ // Used by standard Tseng ID scheme
+ et4k.store_3d4_33 = val;
+ vga.config.display_start = (vga.config.display_start & 0xffff) | ((val & 0x03)<<16);
+ vga.config.cursor_start = (vga.config.cursor_start & 0xffff) | ((val & 0x0c)<<14);
+ break;
+
+ /*
+ 3d4h index 34h (R/W): 6845 Compatibility Control Register
+ bit 0 Enable CS0 (alternate clock timing)
+ 1 Clock Select bit 2. Bits 0-1 in 3C2h bits 2-3, bits 3-4 are in 3d4h
+ index 31h bits 6-7
+ 2 Tristate ET4000 bus and color outputs if set
+ 3 Video Subsystem Enable Register at 46E8h if set, at 3C3h if clear.
+ 4 Enable Translation ROM for reading CRTC and MISCOUT if set
+ 5 Enable Translation ROM for writing CRTC and MISCOUT if set
+ 6 Enable double scan in AT&T compatibility mode if set
+ 7 Enable 6845 compatibility if set
+ */
+ // TODO: Bit 6 may have effect on emulation
+ STORE_ET4K(3d4, 34);
+
+ case 0x35:
+ /*
+ 3d4h index 35h (R/W): Overflow High
+ bit 0 Vertical Blank Start Bit 10 (3d4h index 15h).
+ 1 Vertical Total Bit 10 (3d4h index 6).
+ 2 Vertical Display End Bit 10 (3d4h index 12h).
+ 3 Vertical Sync Start Bit 10 (3d4h index 10h).
+ 4 Line Compare Bit 10 (3d4h index 18h).
+ 5 Gen-Lock Enabled if set (External sync)
+ 6 (4000) Read/Modify/Write Enabled if set. Currently not implemented.
+ 7 Vertical interlace if set. The Vertical timing registers are
+ programmed as if the mode was non-interlaced!!
+ */
+ et4k.store_3d4_35 = val;
+ vga.config.line_compare = (vga.config.line_compare & 0x3ff) | ((val&0x10)<<6);
+ // Abusing s3 ex_ver_overflow field. This is to be cleaned up later.
+ {
+ Bit8u s3val =
+ ((val & 0x01) << 2) | // vbstart
+ ((val & 0x02) >> 1) | // vtotal
+ ((val & 0x04) >> 1) | // vdispend
+ ((val & 0x08) << 1) | // vsyncstart (?)
+ ((val & 0x10) << 2); // linecomp
+ if ((s3val ^ vga.s3.ex_ver_overflow) & 0x3) {
+ vga.s3.ex_ver_overflow=s3val;
+ VGA_StartResize();
+ } else vga.s3.ex_ver_overflow=s3val;
+ }
+ break;
+
+ // 3d4h index 36h - Video System Configuration 1 (R/W)
+ // VGADOC provides a lot of info on this register, Ferraro has significantly less detail.
+ // This is unlikely to be used by any games. Bit 4 switches chipset into linear mode -
+ // that may be useful in some cases if there is any software actually using it.
+ // TODO (not near future): support linear addressing
+ STORE_ET4K(3d4, 36);
+
+ // 3d4h index 37 - Video System Configuration 2 (R/W)
+ // Bits 0,1, and 3 provides information about memory size:
+ // 0-1 Bus width (1: 8 bit, 2: 16 bit, 3: 32 bit)
+ // 3 Size of RAM chips (0: 64Kx, 1: 256Kx)
+ // Other bits have no effect on emulation.
+ case 0x37:
+ if (val != et4k.store_3d4_37) {
+ et4k.store_3d4_37 = val;
+ vga.vmemwrap = ((64*1024)<<((val&8)>>2))<<((val&3)-1);
+ VGA_SetupHandlers();
+ }
+ break;
+
+ case 0x3f:
+ /*
+ 3d4h index 3Fh (R/W):
+ bit 0 Bit 8 of the Horizontal Total (3d4h index 0)
+ 2 Bit 8 of the Horizontal Blank Start (3d4h index 3)
+ 4 Bit 8 of the Horizontal Retrace Start (3d4h index 4)
+ 7 Bit 8 of the CRTC offset register (3d4h index 13h).
+ */
+ // The only unimplemented one is bit 7
+ et4k.store_3d4_3f = val;
+ // Abusing s3 ex_hor_overflow field which very similar. This is
+ // to be cleaned up later
+ if ((val ^ vga.s3.ex_hor_overflow) & 3) {
+ vga.s3.ex_hor_overflow=(val&0x15);
+ VGA_StartResize();
+ } else vga.s3.ex_hor_overflow=(val&0x15);
+ break;
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:CRTC:ET4K:Write to illegal index %2X", reg);
+ break;
+ }
+}
+
+Bitu read_p3d5_et4k(Bitu reg,Bitu iolen) {
+ if (!et4k.extensionsEnabled && reg!=0x33)
+ return 0x0;
+ switch(reg) {
+ RESTORE_ET4K(3d4, 31);
+ RESTORE_ET4K(3d4, 32);
+ RESTORE_ET4K(3d4, 33);
+ RESTORE_ET4K(3d4, 34);
+ RESTORE_ET4K(3d4, 35);
+ RESTORE_ET4K(3d4, 36);
+ RESTORE_ET4K(3d4, 37);
+ RESTORE_ET4K(3d4, 3f);
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:CRTC:ET4K:Read from illegal index %2X", reg);
+ break;
+ }
+ return 0x0;
+}
+
+void write_p3c5_et4k(Bitu reg,Bitu val,Bitu iolen) {
+ switch(reg) {
+ /*
+ 3C4h index 6 (R/W): TS State Control
+ bit 1-2 Font Width Select in dots/character
+ If 3C4h index 4 bit 0 clear:
+ 0: 9 dots, 1: 10 dots, 2: 12 dots, 3: 6 dots
+ If 3C4h index 5 bit 0 set:
+ 0: 8 dots, 1: 11 dots, 2: 7 dots, 3: 16 dots
+ Only valid if 3d4h index 34h bit 3 set.
+ */
+ // TODO: Figure out if this has any practical use
+ STORE_ET4K(3c4, 06);
+ // 3C4h index 7 (R/W): TS Auxiliary Mode
+ // Unlikely to be used by games (things like ROM enable/disable and emulation of VGA vs EGA)
+ STORE_ET4K(3c4, 07);
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:SEQ:ET4K:Write to illegal index %2X", reg);
+ break;
+ }
+}
+
+Bitu read_p3c5_et4k(Bitu reg,Bitu iolen) {
+ switch(reg) {
+ RESTORE_ET4K(3c4, 06);
+ RESTORE_ET4K(3c4, 07);
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:SEQ:ET4K:Read from illegal index %2X", reg);
+ break;
+ }
+ return 0x0;
+}
+
+/*
+3CDh (R/W): Segment Select
+bit 0-3 64k Write bank number (0..15)
+ 4-7 64k Read bank number (0..15)
+*/
+void write_p3cd_et4k(Bitu port,Bitu val,Bitu iolen) {
+ vga.svga.bank_write = val & 0x0f;
+ vga.svga.bank_read = (val>>4) & 0x0f;
+ VGA_SetupHandlers();
+}
+
+Bitu read_p3cd_et4k(Bitu port,Bitu iolen) {
+ return (vga.svga.bank_read<<4)|vga.svga.bank_write;
+}
+
+void write_p3c0_et4k(Bitu reg,Bitu val,Bitu iolen) {
+ switch(reg) {
+ // 3c0 index 16h: ATC Miscellaneous
+ // VGADOC provides a lot of information, Ferarro documents only two bits
+ // and even those incompletely. The register is used as part of identification
+ // scheme.
+ // Unlikely to be used by any games but double timing may be useful.
+ // TODO: Figure out if this has any practical use
+ STORE_ET4K(3c0, 16);
+ /*
+ 3C0h index 17h (R/W): Miscellaneous 1
+ bit 7 If set protects the internal palette ram and redefines the attribute
+ bits as follows:
+ Monochrome:
+ bit 0-2 Select font 0-7
+ 3 If set selects blinking
+ 4 If set selects underline
+ 5 If set prevents the character from being displayed
+ 6 If set displays the character at half intensity
+ 7 If set selects reverse video
+ Color:
+ bit 0-1 Selects font 0-3
+ 2 Foreground Blue
+ 3 Foreground Green
+ 4 Foreground Red
+ 5 Background Blue
+ 6 Background Green
+ 7 Background Red
+ */
+ // TODO: Figure out if this has any practical use
+ STORE_ET4K(3c0, 17);
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:ATTR:ET4K:Write to illegal index %2X", reg);
+ break;
+ }
+}
+
+Bitu read_p3c1_et4k(Bitu reg,Bitu iolen) {
+ switch(reg) {
+ RESTORE_ET4K(3c0, 16);
+ RESTORE_ET4K(3c0, 17);
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:ATTR:ET4K:Read from illegal index %2X", reg);
+ break;
+ }
+ return 0x0;
+}
+
+/*
+These ports are used but have little if any effect on emulation:
+ 3BFh (R/W): Hercules Compatibility Mode
+ 3CBh (R/W): PEL Address/Data Wd
+ 3CEh index 0Dh (R/W): Microsequencer Mode
+ 3CEh index 0Eh (R/W): Microsequencer Reset
+ 3d8h (R/W): Display Mode Control
+ 3DEh (W); AT&T Mode Control Register
+*/
+
+static Bitu get_clock_index_et4k() {
+ // Ignoring bit 4, using "only" 16 frequencies. Looks like most implementations had only that
+ return ((vga.misc_output>>2)&3) | ((et4k.store_3d4_34<<1)&4) | ((et4k.store_3d4_31>>3)&8);
+}
+
+static void set_clock_index_et4k(Bitu index) {
+ // Shortwiring register reads/writes for simplicity
+ IO_Write(0x3c2, (vga.misc_output&~0x0c)|((index&3)<<2));
+ et4k.store_3d4_34 = (et4k.store_3d4_34&~0x02)|((index&4)>>1);
+ et4k.store_3d4_31 = (et4k.store_3d4_31&~0xc0)|((index&8)<<3); // (index&0x18) if 32 clock frequencies are to be supported
+}
+
+void FinishSetMode_ET4K(Bitu crtc_base, VGA_ModeExtraData* modeData) {
+ et4k.biosMode = modeData->modeNo;
+
+ IO_Write(0x3cd, 0x00); // both banks to 0
+
+ // Reinterpret hor_overflow. Curiously, three bits out of four are
+ // in the same places. Input has hdispend (not supported), output
+ // has CRTC offset (also not supported)
+ Bit8u et4k_hor_overflow =
+ (modeData->hor_overflow & 0x01) |
+ (modeData->hor_overflow & 0x04) |
+ (modeData->hor_overflow & 0x10);
+ IO_Write(crtc_base,0x3f);IO_Write(crtc_base+1,et4k_hor_overflow);
+
+ // Reinterpret ver_overflow
+ Bit8u et4k_ver_overflow =
+ ((modeData->ver_overflow & 0x01) << 1) | // vtotal10
+ ((modeData->ver_overflow & 0x02) << 1) | // vdispend10
+ ((modeData->ver_overflow & 0x04) >> 2) | // vbstart10
+ ((modeData->ver_overflow & 0x10) >> 1) | // vretrace10 (tseng has vsync start?)
+ ((modeData->ver_overflow & 0x40) >> 2); // line_compare
+ IO_Write(crtc_base,0x35);IO_Write(crtc_base+1,et4k_ver_overflow);
+
+ // Clear remaining ext CRTC registers
+ IO_Write(crtc_base,0x31);IO_Write(crtc_base+1,0);
+ IO_Write(crtc_base,0x32);IO_Write(crtc_base+1,0);
+ IO_Write(crtc_base,0x33);IO_Write(crtc_base+1,0);
+ IO_Write(crtc_base,0x34);IO_Write(crtc_base+1,0);
+ IO_Write(crtc_base,0x36);IO_Write(crtc_base+1,0);
+ IO_Write(crtc_base,0x37);IO_Write(crtc_base+1,0x0c|(vga.vmemsize==1024*1024?3:vga.vmemsize==512*1024?2:1));
+ // Clear ext SEQ
+ IO_Write(0x3c4,0x06);IO_Write(0x3c5,0);
+ IO_Write(0x3c4,0x07);IO_Write(0x3c5,0);
+ // Clear ext ATTR
+ IO_Write(0x3c0,0x16);IO_Write(0x3c0,0);
+ IO_Write(0x3c0,0x17);IO_Write(0x3c0,0);
+
+ // Select SVGA clock to get close to 60Hz (not particularly clean implementation)
+ if (modeData->modeNo > 0x13) {
+ Bits target = static_cast<Bits>(modeData->vtotal * 8 * modeData->htotal * 60);
+ Bitu best = 1;
+ int dist = 100000000;
+ for (Bitu i = 0; i < 16; i++) {
+ int cdiff = abs( static_cast<int>(target - static_cast<Bits>(et4k.clockFreq[i])) );
+ if (cdiff < dist) {
+ best = i;
+ dist = cdiff;
+ }
+ }
+ set_clock_index_et4k(best);
+ }
+
+ if(svga.determine_mode)
+ svga.determine_mode();
+
+ // Verified (on real hardware and in a few games): Tseng ET4000 used chain4 implementation
+ // different from standard VGA. It was also not limited to 64K in regular mode 13h.
+ vga.config.compatible_chain4 = false;
+ vga.vmemwrap = vga.vmemsize;
+
+ VGA_SetupHandlers();
+}
+
+void DetermineMode_ET4K() {
+ // Close replica from the base implementation. It will stay here
+ // until I figure a way to either distinguish M_VGA and M_LIN8 or
+ // merge them.
+ if (vga.attr.mode_control & 1) {
+ if (vga.gfx.mode & 0x40) VGA_SetMode((et4k.biosMode<=0x13)?M_VGA:M_LIN8); // Ugly...
+ else if (vga.gfx.mode & 0x20) VGA_SetMode(M_CGA4);
+ else if ((vga.gfx.miscellaneous & 0x0c)==0x0c) VGA_SetMode(M_CGA2);
+ else VGA_SetMode((et4k.biosMode<=0x13)?M_EGA:M_LIN4);
+ } else {
+ VGA_SetMode(M_TEXT);
+ }
+}
+
+void SetClock_ET4K(Bitu which,Bitu target) {
+ et4k.clockFreq[which]=1000*target;
+ VGA_StartResize();
+}
+
+Bitu GetClock_ET4K() {
+ return et4k.clockFreq[get_clock_index_et4k()];
+}
+
+bool AcceptsMode_ET4K(Bitu mode) {
+ return VideoModeMemSize(mode) < vga.vmemsize;
+// return mode != 0x3d;
+}
+
+void SVGA_Setup_TsengET4K(void) {
+ svga.write_p3d5 = &write_p3d5_et4k;
+ svga.read_p3d5 = &read_p3d5_et4k;
+ svga.write_p3c5 = &write_p3c5_et4k;
+ svga.read_p3c5 = &read_p3c5_et4k;
+ svga.write_p3c0 = &write_p3c0_et4k;
+ svga.read_p3c1 = &read_p3c1_et4k;
+
+ svga.set_video_mode = &FinishSetMode_ET4K;
+ svga.determine_mode = &DetermineMode_ET4K;
+ svga.set_clock = &SetClock_ET4K;
+ svga.get_clock = &GetClock_ET4K;
+ svga.accepts_mode = &AcceptsMode_ET4K;
+
+ // From the depths of X86Config, probably inexact
+ VGA_SetClock(0,CLK_25);
+ VGA_SetClock(1,CLK_28);
+ VGA_SetClock(2,32400);
+ VGA_SetClock(3,35900);
+ VGA_SetClock(4,39900);
+ VGA_SetClock(5,44700);
+ VGA_SetClock(6,31400);
+ VGA_SetClock(7,37500);
+ VGA_SetClock(8,50000);
+ VGA_SetClock(9,56500);
+ VGA_SetClock(10,64900);
+ VGA_SetClock(11,71900);
+ VGA_SetClock(12,79900);
+ VGA_SetClock(13,89600);
+ VGA_SetClock(14,62800);
+ VGA_SetClock(15,74800);
+
+ IO_RegisterReadHandler(0x3cd,read_p3cd_et4k,IO_MB);
+ IO_RegisterWriteHandler(0x3cd,write_p3cd_et4k,IO_MB);
+
+ // Default to 1M of VRAM
+ if (vga.vmemsize == 0)
+ vga.vmemsize = 1024*1024;
+
+ if (vga.vmemsize < 512*1024)
+ vga.vmemsize = 256*1024;
+ else if (vga.vmemsize < 1024*1024)
+ vga.vmemsize = 512*1024;
+ else
+ vga.vmemsize = 1024*1024;
+
+ // Tseng ROM signature
+ PhysPt rom_base=PhysMake(0xc000,0);
+ phys_writeb(rom_base+0x0075,' ');
+ phys_writeb(rom_base+0x0076,'T');
+ phys_writeb(rom_base+0x0077,'s');
+ phys_writeb(rom_base+0x0078,'e');
+ phys_writeb(rom_base+0x0079,'n');
+ phys_writeb(rom_base+0x007a,'g');
+ phys_writeb(rom_base+0x007b,' ');
+}
+
+
+// Tseng ET3K implementation
+typedef struct {
+// Stored exact values of some registers. Documentation only specifies some bits but hardware checks may
+// expect other bits to be preserved.
+ Bitu store_3d4_1b;
+ Bitu store_3d4_1c;
+ Bitu store_3d4_1d;
+ Bitu store_3d4_1e;
+ Bitu store_3d4_1f;
+ Bitu store_3d4_20;
+ Bitu store_3d4_21;
+ Bitu store_3d4_23; // note that 22 is missing
+ Bitu store_3d4_24;
+ Bitu store_3d4_25;
+
+ Bitu store_3c0_16;
+ Bitu store_3c0_17;
+
+ Bitu store_3c4_06;
+ Bitu store_3c4_07;
+
+ Bitu clockFreq[8];
+ Bitu biosMode;
+} SVGA_ET3K_DATA;
+
+static SVGA_ET3K_DATA et3k = { 0,0,0,0,0,0,0,0,0,0, 0,0, 0,0, {0,0,0,0,0,0,0,0}, 0 };
+
+#define STORE_ET3K(port, index) \
+ case 0x##index: \
+ et3k.store_##port##_##index = val; \
+ break;
+
+#define RESTORE_ET3K(port, index) \
+ case 0x##index: \
+ return et3k.store_##port##_##index;
+
+
+void write_p3d5_et3k(Bitu reg,Bitu val,Bitu iolen) {
+ switch(reg) {
+ // 3d4 index 1bh-21h: Hardware zoom control registers
+ // I am not sure if there was a piece of software that used these.
+ // Not implemented and will probably stay this way.
+ STORE_ET3K(3d4, 1b);
+ STORE_ET3K(3d4, 1c);
+ STORE_ET3K(3d4, 1d);
+ STORE_ET3K(3d4, 1e);
+ STORE_ET3K(3d4, 1f);
+ STORE_ET3K(3d4, 20);
+ STORE_ET3K(3d4, 21);
+
+ case 0x23:
+ /*
+ 3d4h index 23h (R/W): Extended start ET3000
+ bit 0 Cursor start address bit 16
+ 1 Display start address bit 16
+ 2 Zoom start address bit 16
+ 7 If set memory address 8 is output on the MBSL pin (allowing access to
+ 1MB), if clear the blanking signal is output.
+ */
+ // Only bits 1 and 2 are supported. Bit 2 is related to hardware zoom, bit 7 is too obscure to be useful
+ et3k.store_3d4_23 = val;
+ vga.config.display_start = (vga.config.display_start & 0xffff) | ((val & 0x02)<<15);
+ vga.config.cursor_start = (vga.config.cursor_start & 0xffff) | ((val & 0x01)<<16);
+ break;
+
+
+ /*
+ 3d4h index 24h (R/W): Compatibility Control
+ bit 0 Enable Clock Translate if set
+ 1 Clock Select bit 2. Bits 0-1 are in 3C2h/3CCh.
+ 2 Enable tri-state for all output pins if set
+ 3 Enable input A8 of 1MB DRAMs from the INTL output if set
+ 4 Reserved
+ 5 Enable external ROM CRTC translation if set
+ 6 Enable Double Scan and Underline Attribute if set
+ 7 Enable 6845 compatibility if set.
+ */
+ // TODO: Some of these may be worth implementing.
+ STORE_ET3K(3d4, 24);
+
+
+ case 0x25:
+ /*
+ 3d4h index 25h (R/W): Overflow High
+ bit 0 Vertical Blank Start bit 10
+ 1 Vertical Total Start bit 10
+ 2 Vertical Display End bit 10
+ 3 Vertical Sync Start bit 10
+ 4 Line Compare bit 10
+ 5-6 Reserved
+ 7 Vertical Interlace if set
+ */
+ et3k.store_3d4_25 = val;
+ vga.config.line_compare = (vga.config.line_compare & 0x3ff) | ((val&0x10)<<6);
+ // Abusing s3 ex_ver_overflow field. This is to be cleaned up later.
+ {
+ Bit8u s3val =
+ ((val & 0x01) << 2) | // vbstart
+ ((val & 0x02) >> 1) | // vtotal
+ ((val & 0x04) >> 1) | // vdispend
+ ((val & 0x08) << 1) | // vsyncstart (?)
+ ((val & 0x10) << 2); // linecomp
+ if ((s3val ^ vga.s3.ex_ver_overflow) & 0x3) {
+ vga.s3.ex_ver_overflow=s3val;
+ VGA_StartResize();
+ } else vga.s3.ex_ver_overflow=s3val;
+ }
+ break;
+
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:CRTC:ET3K:Write to illegal index %2X", reg);
+ break;
+ }
+}
+
+Bitu read_p3d5_et3k(Bitu reg,Bitu iolen) {
+ switch(reg) {
+ RESTORE_ET3K(3d4, 1b);
+ RESTORE_ET3K(3d4, 1c);
+ RESTORE_ET3K(3d4, 1d);
+ RESTORE_ET3K(3d4, 1e);
+ RESTORE_ET3K(3d4, 1f);
+ RESTORE_ET3K(3d4, 20);
+ RESTORE_ET3K(3d4, 21);
+ RESTORE_ET3K(3d4, 23);
+ RESTORE_ET3K(3d4, 24);
+ RESTORE_ET3K(3d4, 25);
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:CRTC:ET3K:Read from illegal index %2X", reg);
+ break;
+ }
+ return 0x0;
+}
+
+void write_p3c5_et3k(Bitu reg,Bitu val,Bitu iolen) {
+ switch(reg) {
+ // Both registers deal mostly with hardware zoom which is not implemented. Other bits
+ // seem to be useless for emulation with the exception of index 7 bit 4 (font select)
+ STORE_ET3K(3c4, 06);
+ STORE_ET3K(3c4, 07);
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:SEQ:ET3K:Write to illegal index %2X", reg);
+ break;
+ }
+}
+
+Bitu read_p3c5_et3k(Bitu reg,Bitu iolen) {
+ switch(reg) {
+ RESTORE_ET3K(3c4, 06);
+ RESTORE_ET3K(3c4, 07);
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:SEQ:ET3K:Read from illegal index %2X", reg);
+ break;
+ }
+ return 0x0;
+}
+
+/*
+3CDh (R/W): Segment Select
+bit 0-2 64k Write bank number
+ 3-5 64k Read bank number
+ 6-7 Segment Configuration.
+ 0 128K segments
+ 1 64K segments
+ 2 1M linear memory
+NOTES: 1M linear memory is not supported
+*/
+void write_p3cd_et3k(Bitu port,Bitu val,Bitu iolen) {
+ vga.svga.bank_write = val & 0x07;
+ vga.svga.bank_read = (val>>3) & 0x07;
+ vga.svga.bank_size = (val&0x40)?64*1024:128*1024;
+ VGA_SetupHandlers();
+}
+
+Bitu read_p3cd_et3k(Bitu port,Bitu iolen) {
+ return (vga.svga.bank_read<<3)|vga.svga.bank_write|((vga.svga.bank_size==128*1024)?0:0x40);
+}
+
+void write_p3c0_et3k(Bitu reg,Bitu val,Bitu iolen) {
+// See ET4K notes.
+ switch(reg) {
+ STORE_ET3K(3c0, 16);
+ STORE_ET3K(3c0, 17);
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:ATTR:ET3K:Write to illegal index %2X", reg);
+ break;
+ }
+}
+
+Bitu read_p3c1_et3k(Bitu reg,Bitu iolen) {
+ switch(reg) {
+ RESTORE_ET3K(3c0, 16);
+ RESTORE_ET3K(3c0, 17);
+ default:
+ LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:ATTR:ET3K:Read from illegal index %2X", reg);
+ break;
+ }
+ return 0x0;
+}
+
+/*
+These ports are used but have little if any effect on emulation:
+ 3B8h (W): Display Mode Control Register
+ 3BFh (R/W): Hercules Compatibility Mode
+ 3CBh (R/W): PEL Address/Data Wd
+ 3CEh index 0Dh (R/W): Microsequencer Mode
+ 3CEh index 0Eh (R/W): Microsequencer Reset
+ 3d8h (R/W): Display Mode Control
+ 3D9h (W): Color Select Register
+ 3dAh (W): Feature Control Register
+ 3DEh (W); AT&T Mode Control Register
+*/
+
+static Bitu get_clock_index_et3k() {
+ return ((vga.misc_output>>2)&3) | ((et3k.store_3d4_24<<1)&4);
+}
+
+static void set_clock_index_et3k(Bitu index) {
+ // Shortwiring register reads/writes for simplicity
+ IO_Write(0x3c2, (vga.misc_output&~0x0c)|((index&3)<<2));
+ et3k.store_3d4_24 = (et3k.store_3d4_24&~0x02)|((index&4)>>1);
+}
+
+void FinishSetMode_ET3K(Bitu crtc_base, VGA_ModeExtraData* modeData) {
+ et3k.biosMode = modeData->modeNo;
+
+ IO_Write(0x3cd, 0x40); // both banks to 0, 64K bank size
+
+ // Tseng ET3K does not have horizontal overflow bits
+ // Reinterpret ver_overflow
+ Bit8u et4k_ver_overflow =
+ ((modeData->ver_overflow & 0x01) << 1) | // vtotal10
+ ((modeData->ver_overflow & 0x02) << 1) | // vdispend10
+ ((modeData->ver_overflow & 0x04) >> 2) | // vbstart10
+ ((modeData->ver_overflow & 0x10) >> 1) | // vretrace10 (tseng has vsync start?)
+ ((modeData->ver_overflow & 0x40) >> 2); // line_compare
+ IO_Write(crtc_base,0x25);IO_Write(crtc_base+1,et4k_ver_overflow);
+
+ // Clear remaining ext CRTC registers
+ for (Bitu i=0x16; i<=0x21; i++) {
+ IO_Write(crtc_base,i);
+ IO_Write(crtc_base+1,0);
+ }
+ IO_Write(crtc_base,0x23);IO_Write(crtc_base+1,0);
+ IO_Write(crtc_base,0x24);IO_Write(crtc_base+1,0);
+ // Clear ext SEQ
+ IO_Write(0x3c4,0x06);IO_Write(0x3c5,0);
+ IO_Write(0x3c4,0x07);IO_Write(0x3c5,0x40); // 0 in this register breaks WHATVGA
+ // Clear ext ATTR
+ IO_Write(0x3c0,0x16);IO_Write(0x3c0,0);
+ IO_Write(0x3c0,0x17);IO_Write(0x3c0,0);
+
+ // Select SVGA clock to get close to 60Hz (not particularly clean implementation)
+ if (modeData->modeNo > 0x13) {
+ Bits target = static_cast<Bits>(modeData->vtotal * 8 * modeData->htotal * 60);
+ Bitu best = 1;
+ int dist = 100000000;
+ for (Bitu i = 0; i < 8; i++) {
+ int cdiff = abs( static_cast<Bit32s>(target - static_cast<Bits>(et3k.clockFreq[i])) );
+ if (cdiff < dist) {
+ best = i;
+ dist = cdiff;
+ }
+ }
+ set_clock_index_et3k(best);
+ }
+
+ if (svga.determine_mode)
+ svga.determine_mode();
+
+ // Verified on functioning (at last!) hardware: Tseng ET3000 is the same as ET4000 when
+ // it comes to chain4 architecture
+ vga.config.compatible_chain4 = false;
+ vga.vmemwrap = vga.vmemsize;
+
+ VGA_SetupHandlers();
+}
+
+void DetermineMode_ET3K() {
+ // Close replica from the base implementation. It will stay here
+ // until I figure a way to either distinguish M_VGA and M_LIN8 or
+ // merge them.
+ if (vga.attr.mode_control & 1) {
+ if (vga.gfx.mode & 0x40) VGA_SetMode((et3k.biosMode<=0x13)?M_VGA:M_LIN8); // Ugly...
+ else if (vga.gfx.mode & 0x20) VGA_SetMode(M_CGA4);
+ else if ((vga.gfx.miscellaneous & 0x0c)==0x0c) VGA_SetMode(M_CGA2);
+ else VGA_SetMode((et3k.biosMode<=0x13)?M_EGA:M_LIN4);
+ } else {
+ VGA_SetMode(M_TEXT);
+ }
+}
+
+void SetClock_ET3K(Bitu which,Bitu target) {
+ et3k.clockFreq[which]=1000*target;
+ VGA_StartResize();
+}
+
+Bitu GetClock_ET3K() {
+ return et3k.clockFreq[get_clock_index_et3k()];
+}
+
+bool AcceptsMode_ET3K(Bitu mode) {
+ return mode <= 0x37 && mode != 0x2f && VideoModeMemSize(mode) < vga.vmemsize;
+}
+
+void SVGA_Setup_TsengET3K(void) {
+ svga.write_p3d5 = &write_p3d5_et3k;
+ svga.read_p3d5 = &read_p3d5_et3k;
+ svga.write_p3c5 = &write_p3c5_et3k;
+ svga.read_p3c5 = &read_p3c5_et3k;
+ svga.write_p3c0 = &write_p3c0_et3k;
+ svga.read_p3c1 = &read_p3c1_et3k;
+
+ svga.set_video_mode = &FinishSetMode_ET3K;
+ svga.determine_mode = &DetermineMode_ET3K;
+ svga.set_clock = &SetClock_ET3K;
+ svga.get_clock = &GetClock_ET3K;
+ svga.accepts_mode = &AcceptsMode_ET3K;
+
+ VGA_SetClock(0,CLK_25);
+ VGA_SetClock(1,CLK_28);
+ VGA_SetClock(2,32400);
+ VGA_SetClock(3,35900);
+ VGA_SetClock(4,39900);
+ VGA_SetClock(5,44700);
+ VGA_SetClock(6,31400);
+ VGA_SetClock(7,37500);
+
+ IO_RegisterReadHandler(0x3cd,read_p3cd_et3k,IO_MB);
+ IO_RegisterWriteHandler(0x3cd,write_p3cd_et3k,IO_MB);
+
+ vga.vmemsize = 512*1024; // Cannot figure how this was supposed to work on the real card
+
+ // Tseng ROM signature
+ PhysPt rom_base=PhysMake(0xc000,0);
+ phys_writeb(rom_base+0x0075,' ');
+ phys_writeb(rom_base+0x0076,'T');
+ phys_writeb(rom_base+0x0077,'s');
+ phys_writeb(rom_base+0x0078,'e');
+ phys_writeb(rom_base+0x0079,'n');
+ phys_writeb(rom_base+0x007a,'g');
+ phys_writeb(rom_base+0x007b,' ');
+}
diff --git a/src/hardware/vga_xga.cpp b/src/hardware/vga_xga.cpp
new file mode 100644
index 000000000..58470ab05
--- /dev/null
+++ b/src/hardware/vga_xga.cpp
@@ -0,0 +1,1319 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include "dosbox.h"
+#include "inout.h"
+#include "vga.h"
+#include <math.h>
+#include <stdio.h>
+#include "callback.h"
+#include "cpu.h" // for 0x3da delay
+
+#define XGA_SCREEN_WIDTH vga.s3.xga_screen_width
+#define XGA_COLOR_MODE vga.s3.xga_color_mode
+
+#define XGA_SHOW_COMMAND_TRACE 0
+
+struct XGAStatus {
+ struct scissorreg {
+ Bit16u x1, y1, x2, y2;
+ } scissors;
+
+ Bit32u readmask;
+ Bit32u writemask;
+
+ Bit32u forecolor;
+ Bit32u backcolor;
+
+ Bitu curcommand;
+
+ Bit16u foremix;
+ Bit16u backmix;
+
+ Bit16u curx, cury;
+ Bit16u destx, desty;
+
+ Bit16u ErrTerm;
+ Bit16u MIPcount;
+ Bit16u MAPcount;
+
+ Bit16u pix_cntl;
+ Bit16u control1;
+ Bit16u control2;
+ Bit16u read_sel;
+
+ struct XGA_WaitCmd {
+ bool newline;
+ bool wait;
+ Bit16u cmd;
+ Bit16u curx, cury;
+ Bit16u x1, y1, x2, y2, sizex, sizey;
+ Bit32u data; /* transient data passed by multiple calls */
+ Bitu datasize;
+ Bitu buswidth;
+ } waitcmd;
+
+} xga;
+
+void XGA_Write_Multifunc(Bitu val, Bitu len) {
+ Bitu regselect = val >> 12;
+ Bitu dataval = val & 0xfff;
+ switch(regselect) {
+ case 0: // minor axis pixel count
+ xga.MIPcount = dataval;
+ break;
+ case 1: // top scissors
+ xga.scissors.y1 = dataval;
+ break;
+ case 2: // left
+ xga.scissors.x1 = dataval;
+ break;
+ case 3: // bottom
+ xga.scissors.y2 = dataval;
+ break;
+ case 4: // right
+ xga.scissors.x2 = dataval;
+ break;
+ case 0xa: // data manip control
+ xga.pix_cntl = dataval;
+ break;
+ case 0xd: // misc 2
+ xga.control2 = dataval;
+ break;
+ case 0xe:
+ xga.control1 = dataval;
+ break;
+ case 0xf:
+ xga.read_sel = dataval;
+ break;
+ default:
+ LOG_MSG("XGA: Unhandled multifunction command %x", regselect);
+ break;
+ }
+}
+
+Bitu XGA_Read_Multifunc() {
+ switch(xga.read_sel++) {
+ case 0: return xga.MIPcount;
+ case 1: return xga.scissors.y1;
+ case 2: return xga.scissors.x1;
+ case 3: return xga.scissors.y2;
+ case 4: return xga.scissors.x2;
+ case 5: return xga.pix_cntl;
+ case 6: return xga.control1;
+ case 7: return 0; // TODO
+ case 8: return 0; // TODO
+ case 9: return 0; // TODO
+ case 10: return xga.control2;
+ default: return 0;
+ }
+}
+
+
+void XGA_DrawPoint(Bitu x, Bitu y, Bitu c) {
+ if(!(xga.curcommand & 0x1)) return;
+ if(!(xga.curcommand & 0x10)) return;
+
+ if(x < xga.scissors.x1) return;
+ if(x > xga.scissors.x2) return;
+ if(y < xga.scissors.y1) return;
+ if(y > xga.scissors.y2) return;
+
+ Bit32u memaddr = (y * XGA_SCREEN_WIDTH) + x;
+ /* Need to zero out all unused bits in modes that have any (15-bit or "32"-bit -- the last
+ one is actually 24-bit. Without this step there may be some graphics corruption (mainly,
+ during windows dragging. */
+ switch(XGA_COLOR_MODE) {
+ case M_LIN8:
+ if (GCC_UNLIKELY(memaddr >= vga.vmemsize)) break;
+ vga.mem.linear[memaddr] = c;
+ break;
+ case M_LIN15:
+ if (GCC_UNLIKELY(memaddr*2 >= vga.vmemsize)) break;
+ ((Bit16u*)(vga.mem.linear))[memaddr] = (Bit16u)(c&0x7fff);
+ break;
+ case M_LIN16:
+ if (GCC_UNLIKELY(memaddr*2 >= vga.vmemsize)) break;
+ ((Bit16u*)(vga.mem.linear))[memaddr] = (Bit16u)(c&0xffff);
+ break;
+ case M_LIN32:
+ if (GCC_UNLIKELY(memaddr*4 >= vga.vmemsize)) break;
+ ((Bit32u*)(vga.mem.linear))[memaddr] = c;
+ break;
+ default:
+ break;
+ }
+
+}
+
+Bitu XGA_GetPoint(Bitu x, Bitu y) {
+ Bit32u memaddr = (y * XGA_SCREEN_WIDTH) + x;
+
+ switch(XGA_COLOR_MODE) {
+ case M_LIN8:
+ if (GCC_UNLIKELY(memaddr >= vga.vmemsize)) break;
+ return vga.mem.linear[memaddr];
+ case M_LIN15:
+ case M_LIN16:
+ if (GCC_UNLIKELY(memaddr*2 >= vga.vmemsize)) break;
+ return ((Bit16u*)(vga.mem.linear))[memaddr];
+ case M_LIN32:
+ if (GCC_UNLIKELY(memaddr*4 >= vga.vmemsize)) break;
+ return ((Bit32u*)(vga.mem.linear))[memaddr];
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+Bitu XGA_GetMixResult(Bitu mixmode, Bitu srcval, Bitu dstdata) {
+ Bitu destval = 0;
+ switch(mixmode & 0xf) {
+ case 0x00: /* not DST */
+ destval = ~dstdata;
+ break;
+ case 0x01: /* 0 (false) */
+ destval = 0;
+ break;
+ case 0x02: /* 1 (true) */
+ destval = 0xffffffff;
+ break;
+ case 0x03: /* 2 DST */
+ destval = dstdata;
+ break;
+ case 0x04: /* not SRC */
+ destval = ~srcval;
+ break;
+ case 0x05: /* SRC xor DST */
+ destval = srcval ^ dstdata;
+ break;
+ case 0x06: /* not (SRC xor DST) */
+ destval = ~(srcval ^ dstdata);
+ break;
+ case 0x07: /* SRC */
+ destval = srcval;
+ break;
+ case 0x08: /* not (SRC and DST) */
+ destval = ~(srcval & dstdata);
+ break;
+ case 0x09: /* (not SRC) or DST */
+ destval = (~srcval) | dstdata;
+ break;
+ case 0x0a: /* SRC or (not DST) */
+ destval = srcval | (~dstdata);
+ break;
+ case 0x0b: /* SRC or DST */
+ destval = srcval | dstdata;
+ break;
+ case 0x0c: /* SRC and DST */
+ destval = srcval & dstdata;
+ break;
+ case 0x0d: /* SRC and (not DST) */
+ destval = srcval & (~dstdata);
+ break;
+ case 0x0e: /* (not SRC) and DST */
+ destval = (~srcval) & dstdata;
+ break;
+ case 0x0f: /* not (SRC or DST) */
+ destval = ~(srcval | dstdata);
+ break;
+ default:
+ LOG_MSG("XGA: GetMixResult: Unknown mix. Shouldn't be able to get here!");
+ break;
+ }
+ return destval;
+}
+
+void XGA_DrawLineVector(Bitu val) {
+ Bits xat, yat;
+ Bitu srcval;
+ Bitu destval;
+ Bitu dstdata;
+ Bits i;
+
+ Bits dx, sx, sy;
+
+ dx = xga.MAPcount;
+ xat = xga.curx;
+ yat = xga.cury;
+
+ switch((val >> 5) & 0x7) {
+ case 0x00: /* 0 degrees */
+ sx = 1;
+ sy = 0;
+ break;
+ case 0x01: /* 45 degrees */
+ sx = 1;
+ sy = -1;
+ break;
+ case 0x02: /* 90 degrees */
+ sx = 0;
+ sy = -1;
+ break;
+ case 0x03: /* 135 degrees */
+ sx = -1;
+ sy = -1;
+ break;
+ case 0x04: /* 180 degrees */
+ sx = -1;
+ sy = 0;
+ break;
+ case 0x05: /* 225 degrees */
+ sx = -1;
+ sy = 1;
+ break;
+ case 0x06: /* 270 degrees */
+ sx = 0;
+ sy = 1;
+ break;
+ case 0x07: /* 315 degrees */
+ sx = 1;
+ sy = 1;
+ break;
+ default: // Should never get here
+ sx = 0;
+ sy = 0;
+ break;
+ }
+
+ for (i=0;i<=dx;i++) {
+ Bitu mixmode = (xga.pix_cntl >> 6) & 0x3;
+ switch (mixmode) {
+ case 0x00: /* FOREMIX always used */
+ mixmode = xga.foremix;
+ switch((mixmode >> 5) & 0x03) {
+ case 0x00: /* Src is background color */
+ srcval = xga.backcolor;
+ break;
+ case 0x01: /* Src is foreground color */
+ srcval = xga.forecolor;
+ break;
+ case 0x02: /* Src is pixel data from PIX_TRANS register */
+ //srcval = tmpval;
+ //LOG_MSG("XGA: DrawRect: Wants data from PIX_TRANS register");
+ break;
+ case 0x03: /* Src is bitmap data */
+ LOG_MSG("XGA: DrawRect: Wants data from srcdata");
+ //srcval = srcdata;
+ break;
+ default:
+ LOG_MSG("XGA: DrawRect: Shouldn't be able to get here!");
+ break;
+ }
+ dstdata = XGA_GetPoint(xat,yat);
+
+ destval = XGA_GetMixResult(mixmode, srcval, dstdata);
+
+ XGA_DrawPoint(xat,yat, destval);
+ break;
+ default:
+ LOG_MSG("XGA: DrawLine: Needs mixmode %x", mixmode);
+ break;
+ }
+ xat += sx;
+ yat += sy;
+ }
+
+ xga.curx = xat-1;
+ xga.cury = yat;
+}
+
+void XGA_DrawLineBresenham(Bitu val) {
+ Bits xat, yat;
+ Bitu srcval;
+ Bitu destval;
+ Bitu dstdata;
+ Bits i;
+ Bits tmpswap;
+ bool steep;
+
+#define SWAP(a,b) tmpswap = a; a = b; b = tmpswap;
+
+ Bits dx, sx, dy, sy, e, dmajor, dminor,destxtmp;
+
+ // Probably a lot easier way to do this, but this works.
+
+ dminor = (Bits)((Bit16s)xga.desty);
+ if(xga.desty&0x2000) dminor |= ~0x1fff;
+ dminor >>= 1;
+
+ destxtmp=(Bits)((Bit16s)xga.destx);
+ if(xga.destx&0x2000) destxtmp |= ~0x1fff;
+
+
+ dmajor = -(destxtmp - (dminor << 1)) >> 1;
+
+ dx = dmajor;
+ if((val >> 5) & 0x1) {
+ sx = 1;
+ } else {
+ sx = -1;
+ }
+ dy = dminor;
+ if((val >> 7) & 0x1) {
+ sy = 1;
+ } else {
+ sy = -1;
+ }
+ e = (Bits)((Bit16s)xga.ErrTerm);
+ if(xga.ErrTerm&0x2000) e |= ~0x1fff;
+ xat = xga.curx;
+ yat = xga.cury;
+
+ if((val >> 6) & 0x1) {
+ steep = false;
+ SWAP(xat, yat);
+ SWAP(sx, sy);
+ } else {
+ steep = true;
+ }
+
+ //LOG_MSG("XGA: Bresenham: ASC %d, LPDSC %d, sx %d, sy %d, err %d, steep %d, length %d, dmajor %d, dminor %d, xstart %d, ystart %d", dx, dy, sx, sy, e, steep, xga.MAPcount, dmajor, dminor,xat,yat);
+
+ for (i=0;i<=xga.MAPcount;i++) {
+ Bitu mixmode = (xga.pix_cntl >> 6) & 0x3;
+ switch (mixmode) {
+ case 0x00: /* FOREMIX always used */
+ mixmode = xga.foremix;
+ switch((mixmode >> 5) & 0x03) {
+ case 0x00: /* Src is background color */
+ srcval = xga.backcolor;
+ break;
+ case 0x01: /* Src is foreground color */
+ srcval = xga.forecolor;
+ break;
+ case 0x02: /* Src is pixel data from PIX_TRANS register */
+ //srcval = tmpval;
+ LOG_MSG("XGA: DrawRect: Wants data from PIX_TRANS register");
+ break;
+ case 0x03: /* Src is bitmap data */
+ LOG_MSG("XGA: DrawRect: Wants data from srcdata");
+ //srcval = srcdata;
+ break;
+ default:
+ LOG_MSG("XGA: DrawRect: Shouldn't be able to get here!");
+ break;
+ }
+
+ if(steep) {
+ dstdata = XGA_GetPoint(xat,yat);
+ } else {
+ dstdata = XGA_GetPoint(yat,xat);
+ }
+
+ destval = XGA_GetMixResult(mixmode, srcval, dstdata);
+
+ if(steep) {
+ XGA_DrawPoint(xat,yat, destval);
+ } else {
+ XGA_DrawPoint(yat,xat, destval);
+ }
+
+ break;
+ default:
+ LOG_MSG("XGA: DrawLine: Needs mixmode %x", mixmode);
+ break;
+ }
+ while (e > 0) {
+ yat += sy;
+ e -= (dx << 1);
+ }
+ xat += sx;
+ e += (dy << 1);
+ }
+
+ if(steep) {
+ xga.curx = xat;
+ xga.cury = yat;
+ } else {
+ xga.curx = yat;
+ xga.cury = xat;
+ }
+ // }
+ //}
+
+}
+
+void XGA_DrawRectangle(Bitu val) {
+ Bit32u xat, yat;
+ Bitu srcval;
+ Bitu destval;
+ Bitu dstdata;
+
+ Bits srcx, srcy, dx, dy;
+
+ dx = -1;
+ dy = -1;
+
+ if(((val >> 5) & 0x01) != 0) dx = 1;
+ if(((val >> 7) & 0x01) != 0) dy = 1;
+
+ srcy = xga.cury;
+
+ for(yat=0;yat<=xga.MIPcount;yat++) {
+ srcx = xga.curx;
+ for(xat=0;xat<=xga.MAPcount;xat++) {
+ Bitu mixmode = (xga.pix_cntl >> 6) & 0x3;
+ switch (mixmode) {
+ case 0x00: /* FOREMIX always used */
+ mixmode = xga.foremix;
+ switch((mixmode >> 5) & 0x03) {
+ case 0x00: /* Src is background color */
+ srcval = xga.backcolor;
+ break;
+ case 0x01: /* Src is foreground color */
+ srcval = xga.forecolor;
+ break;
+ case 0x02: /* Src is pixel data from PIX_TRANS register */
+ //srcval = tmpval;
+ LOG_MSG("XGA: DrawRect: Wants data from PIX_TRANS register");
+ break;
+ case 0x03: /* Src is bitmap data */
+ LOG_MSG("XGA: DrawRect: Wants data from srcdata");
+ //srcval = srcdata;
+ break;
+ default:
+ LOG_MSG("XGA: DrawRect: Shouldn't be able to get here!");
+ break;
+ }
+ dstdata = XGA_GetPoint(srcx,srcy);
+
+ destval = XGA_GetMixResult(mixmode, srcval, dstdata);
+
+ XGA_DrawPoint(srcx,srcy, destval);
+ break;
+ default:
+ LOG_MSG("XGA: DrawRect: Needs mixmode %x", mixmode);
+ break;
+ }
+ srcx += dx;
+ }
+ srcy += dy;
+ }
+ xga.curx = srcx;
+ xga.cury = srcy;
+
+ //LOG_MSG("XGA: Draw rect (%d, %d)-(%d, %d), %d", x1, y1, x2, y2, xga.forecolor);
+}
+
+bool XGA_CheckX(void) {
+ bool newline = false;
+ if(!xga.waitcmd.newline) {
+
+ if((xga.waitcmd.curx<2048) && xga.waitcmd.curx > (xga.waitcmd.x2)) {
+ xga.waitcmd.curx = xga.waitcmd.x1;
+ xga.waitcmd.cury++;
+ xga.waitcmd.cury&=0x0fff;
+ newline = true;
+ xga.waitcmd.newline = true;
+ if((xga.waitcmd.cury<2048)&&(xga.waitcmd.cury > xga.waitcmd.y2))
+ xga.waitcmd.wait = false;
+ } else if(xga.waitcmd.curx>=2048) {
+ Bit16u realx = 4096-xga.waitcmd.curx;
+ if(xga.waitcmd.x2>2047) { // x end is negative too
+ Bit16u realxend=4096-xga.waitcmd.x2;
+ if(realx==realxend) {
+ xga.waitcmd.curx = xga.waitcmd.x1;
+ xga.waitcmd.cury++;
+ xga.waitcmd.cury&=0x0fff;
+ newline = true;
+ xga.waitcmd.newline = true;
+ if((xga.waitcmd.cury<2048)&&(xga.waitcmd.cury > xga.waitcmd.y2))
+ xga.waitcmd.wait = false;
+ }
+ } else { // else overlapping
+ if(realx==xga.waitcmd.x2) {
+ xga.waitcmd.curx = xga.waitcmd.x1;
+ xga.waitcmd.cury++;
+ xga.waitcmd.cury&=0x0fff;
+ newline = true;
+ xga.waitcmd.newline = true;
+ if((xga.waitcmd.cury<2048)&&(xga.waitcmd.cury > xga.waitcmd.y2))
+ xga.waitcmd.wait = false;
+ }
+ }
+ }
+ } else {
+ xga.waitcmd.newline = false;
+ }
+ return newline;
+}
+
+void XGA_DrawWaitSub(Bitu mixmode, Bitu srcval) {
+ Bitu destval;
+ Bitu dstdata;
+ dstdata = XGA_GetPoint(xga.waitcmd.curx, xga.waitcmd.cury);
+ destval = XGA_GetMixResult(mixmode, srcval, dstdata);
+ //LOG_MSG("XGA: DrawPattern: Mixmode: %x srcval: %x", mixmode, srcval);
+
+ XGA_DrawPoint(xga.waitcmd.curx, xga.waitcmd.cury, destval);
+ xga.waitcmd.curx++;
+ xga.waitcmd.curx&=0x0fff;
+ XGA_CheckX();
+}
+
+void XGA_DrawWait(Bitu val, Bitu len) {
+ if(!xga.waitcmd.wait) return;
+ Bitu mixmode = (xga.pix_cntl >> 6) & 0x3;
+ Bitu srcval;
+ switch(xga.waitcmd.cmd) {
+ case 2: /* Rectangle */
+ switch(mixmode) {
+ case 0x00: /* FOREMIX always used */
+ mixmode = xga.foremix;
+
+/* switch((mixmode >> 5) & 0x03) {
+ case 0x00: // Src is background color
+ srcval = xga.backcolor;
+ break;
+ case 0x01: // Src is foreground color
+ srcval = xga.forecolor;
+ break;
+ case 0x02: // Src is pixel data from PIX_TRANS register
+*/
+ if(((mixmode >> 5) & 0x03) != 0x2) {
+ // those cases don't seem to occur
+ LOG_MSG("XGA: unsupported drawwait operation");
+ break;
+ }
+ switch(xga.waitcmd.buswidth) {
+ case M_LIN8: // 8 bit
+ XGA_DrawWaitSub(mixmode, val);
+ break;
+ case 0x20 | M_LIN8: // 16 bit
+ for(Bitu i = 0; i < len; i++) {
+ XGA_DrawWaitSub(mixmode, (val>>(8*i))&0xff);
+ if(xga.waitcmd.newline) break;
+ }
+ break;
+ case 0x40 | M_LIN8: // 32 bit
+ for(int i = 0; i < 4; i++)
+ XGA_DrawWaitSub(mixmode, (val>>(8*i))&0xff);
+ break;
+ case (0x20 | M_LIN32):
+ if(len!=4) { // Win 3.11 864 'hack?'
+ if(xga.waitcmd.datasize == 0) {
+ // set it up to wait for the next word
+ xga.waitcmd.data = val;
+ xga.waitcmd.datasize = 2;
+ return;
+ } else {
+ srcval = (val<<16)|xga.waitcmd.data;
+ xga.waitcmd.data = 0;
+ xga.waitcmd.datasize = 0;
+ XGA_DrawWaitSub(mixmode, srcval);
+ }
+ break;
+ } // fall-through
+ case 0x40 | M_LIN32: // 32 bit
+ XGA_DrawWaitSub(mixmode, val);
+ break;
+ case 0x20 | M_LIN15: // 16 bit
+ case 0x20 | M_LIN16: // 16 bit
+ XGA_DrawWaitSub(mixmode, val);
+ break;
+ case 0x40 | M_LIN15: // 32 bit
+ case 0x40 | M_LIN16: // 32 bit
+ XGA_DrawWaitSub(mixmode, val&0xffff);
+ if(!xga.waitcmd.newline)
+ XGA_DrawWaitSub(mixmode, val>>16);
+ break;
+ default:
+ // Let's hope they never show up ;)
+ LOG_MSG("XGA: unsupported bpp / datawidth combination %x",
+ xga.waitcmd.buswidth);
+ break;
+ };
+ break;
+
+ case 0x02: // Data from PIX_TRANS selects the mix
+ Bitu chunksize;
+ Bitu chunks;
+ switch(xga.waitcmd.buswidth&0x60) {
+ case 0x0:
+ chunksize=8;
+ chunks=1;
+ break;
+ case 0x20: // 16 bit
+ chunksize=16;
+ if(len==4) chunks=2;
+ else chunks = 1;
+ break;
+ case 0x40: // 32 bit
+ chunksize=16;
+ if(len==4) chunks=2;
+ else chunks = 1;
+ break;
+ case 0x60: // undocumented guess (but works)
+ chunksize=8;
+ chunks=4;
+ break;
+ }
+
+ for(Bitu k = 0; k < chunks; k++) { // chunks counter
+ xga.waitcmd.newline = false;
+ for(Bitu n = 0; n < chunksize; n++) { // pixels
+ Bitu mixmode;
+
+ // This formula can rule the world ;)
+ Bitu mask = 1 << ((((n&0xF8)+(8-(n&0x7)))-1)+chunksize*k);
+ if(val&mask) mixmode = xga.foremix;
+ else mixmode = xga.backmix;
+
+ switch((mixmode >> 5) & 0x03) {
+ case 0x00: // Src is background color
+ srcval = xga.backcolor;
+ break;
+ case 0x01: // Src is foreground color
+ srcval = xga.forecolor;
+ break;
+ default:
+ LOG_MSG("XGA: DrawBlitWait: Unsupported src %x",
+ (mixmode >> 5) & 0x03);
+ srcval=0;
+ break;
+ }
+ XGA_DrawWaitSub(mixmode, srcval);
+
+ if((xga.waitcmd.cury<2048) &&
+ (xga.waitcmd.cury >= xga.waitcmd.y2)) {
+ xga.waitcmd.wait = false;
+ k=1000; // no more chunks
+ break;
+ }
+ // next chunk goes to next line
+ if(xga.waitcmd.newline) break;
+ } // pixels loop
+ } // chunks loop
+ break;
+
+ default:
+ LOG_MSG("XGA: DrawBlitWait: Unhandled mixmode: %d", mixmode);
+ break;
+ } // switch mixmode
+ break;
+ default:
+ LOG_MSG("XGA: Unhandled draw command %x", xga.waitcmd.cmd);
+ break;
+ }
+}
+
+void XGA_BlitRect(Bitu val) {
+ Bit32u xat, yat;
+ Bitu srcdata;
+ Bitu dstdata;
+
+ Bitu srcval;
+ Bitu destval;
+
+ Bits srcx, srcy, tarx, tary, dx, dy;
+
+ dx = -1;
+ dy = -1;
+
+ if(((val >> 5) & 0x01) != 0) dx = 1;
+ if(((val >> 7) & 0x01) != 0) dy = 1;
+
+ srcx = xga.curx;
+ srcy = xga.cury;
+ tarx = xga.destx;
+ tary = xga.desty;
+
+ Bitu mixselect = (xga.pix_cntl >> 6) & 0x3;
+ Bitu mixmode = 0x67; /* Source is bitmap data, mix mode is src */
+ switch(mixselect) {
+ case 0x00: /* Foreground mix is always used */
+ mixmode = xga.foremix;
+ break;
+ case 0x02: /* CPU Data determines mix used */
+ LOG_MSG("XGA: DrawPattern: Mixselect data from PIX_TRANS register");
+ break;
+ case 0x03: /* Video memory determines mix */
+ //LOG_MSG("XGA: Srcdata: %x, Forecolor %x, Backcolor %x, Foremix: %x Backmix: %x", srcdata, xga.forecolor, xga.backcolor, xga.foremix, xga.backmix);
+ break;
+ default:
+ LOG_MSG("XGA: BlitRect: Unknown mix select register");
+ break;
+ }
+
+
+ /* Copy source to video ram */
+ for(yat=0;yat<=xga.MIPcount ;yat++) {
+ srcx = xga.curx;
+ tarx = xga.destx;
+
+ for(xat=0;xat<=xga.MAPcount;xat++) {
+ srcdata = XGA_GetPoint(srcx, srcy);
+ dstdata = XGA_GetPoint(tarx, tary);
+
+ if(mixselect == 0x3) {
+ if(srcdata == xga.forecolor) {
+ mixmode = xga.foremix;
+ } else {
+ if(srcdata == xga.backcolor) {
+ mixmode = xga.backmix;
+ } else {
+ /* Best guess otherwise */
+ mixmode = 0x67; /* Source is bitmap data, mix mode is src */
+ }
+ }
+ }
+
+ switch((mixmode >> 5) & 0x03) {
+ case 0x00: /* Src is background color */
+ srcval = xga.backcolor;
+ break;
+ case 0x01: /* Src is foreground color */
+ srcval = xga.forecolor;
+ break;
+ case 0x02: /* Src is pixel data from PIX_TRANS register */
+ LOG_MSG("XGA: DrawPattern: Wants data from PIX_TRANS register");
+ break;
+ case 0x03: /* Src is bitmap data */
+ srcval = srcdata;
+ break;
+ default:
+ LOG_MSG("XGA: DrawPattern: Shouldn't be able to get here!");
+ srcval = 0;
+ break;
+ }
+
+ destval = XGA_GetMixResult(mixmode, srcval, dstdata);
+ //LOG_MSG("XGA: DrawPattern: Mixmode: %x Mixselect: %x", mixmode, mixselect);
+
+ XGA_DrawPoint(tarx, tary, destval);
+
+ srcx += dx;
+ tarx += dx;
+ }
+ srcy += dy;
+ tary += dy;
+ }
+}
+
+void XGA_DrawPattern(Bitu val) {
+ Bitu srcdata;
+ Bitu dstdata;
+
+ Bitu srcval;
+ Bitu destval;
+
+ Bits xat, yat, srcx, srcy, tarx, tary, dx, dy;
+
+ dx = -1;
+ dy = -1;
+
+ if(((val >> 5) & 0x01) != 0) dx = 1;
+ if(((val >> 7) & 0x01) != 0) dy = 1;
+
+ srcx = xga.curx;
+ srcy = xga.cury;
+
+ tary = xga.desty;
+
+ Bitu mixselect = (xga.pix_cntl >> 6) & 0x3;
+ Bitu mixmode = 0x67; /* Source is bitmap data, mix mode is src */
+ switch(mixselect) {
+ case 0x00: /* Foreground mix is always used */
+ mixmode = xga.foremix;
+ break;
+ case 0x02: /* CPU Data determines mix used */
+ LOG_MSG("XGA: DrawPattern: Mixselect data from PIX_TRANS register");
+ break;
+ case 0x03: /* Video memory determines mix */
+ //LOG_MSG("XGA: Pixctl: %x, Srcdata: %x, Forecolor %x, Backcolor %x, Foremix: %x Backmix: %x",xga.pix_cntl, srcdata, xga.forecolor, xga.backcolor, xga.foremix, xga.backmix);
+ break;
+ default:
+ LOG_MSG("XGA: DrawPattern: Unknown mix select register");
+ break;
+ }
+
+ for(yat=0;yat<=xga.MIPcount;yat++) {
+ tarx = xga.destx;
+ for(xat=0;xat<=xga.MAPcount;xat++) {
+
+ srcdata = XGA_GetPoint(srcx + (tarx & 0x7), srcy + (tary & 0x7));
+ //LOG_MSG("patternpoint (%3d/%3d)v%x",srcx + (tarx & 0x7), srcy + (tary & 0x7),srcdata);
+ dstdata = XGA_GetPoint(tarx, tary);
+
+
+ if(mixselect == 0x3) {
+ // TODO lots of guessing here but best results this way
+ /*if(srcdata == xga.forecolor)*/ mixmode = xga.foremix;
+ // else
+ if(srcdata == xga.backcolor || srcdata == 0)
+ mixmode = xga.backmix;
+ }
+
+ switch((mixmode >> 5) & 0x03) {
+ case 0x00: /* Src is background color */
+ srcval = xga.backcolor;
+ break;
+ case 0x01: /* Src is foreground color */
+ srcval = xga.forecolor;
+ break;
+ case 0x02: /* Src is pixel data from PIX_TRANS register */
+ LOG_MSG("XGA: DrawPattern: Wants data from PIX_TRANS register");
+ break;
+ case 0x03: /* Src is bitmap data */
+ srcval = srcdata;
+ break;
+ default:
+ LOG_MSG("XGA: DrawPattern: Shouldn't be able to get here!");
+ srcval = 0;
+ break;
+ }
+
+ destval = XGA_GetMixResult(mixmode, srcval, dstdata);
+
+ XGA_DrawPoint(tarx, tary, destval);
+
+ tarx += dx;
+ }
+ tary += dy;
+ }
+}
+
+void XGA_DrawCmd(Bitu val, Bitu len) {
+ Bit16u cmd;
+ cmd = val >> 13;
+#if XGA_SHOW_COMMAND_TRACE == 1
+ //LOG_MSG("XGA: Draw command %x", cmd);
+#endif
+ xga.curcommand = val;
+ switch(cmd) {
+ case 1: /* Draw line */
+ if((val & 0x100) == 0) {
+ if((val & 0x8) == 0) {
+#if XGA_SHOW_COMMAND_TRACE == 1
+ LOG_MSG("XGA: Drawing Bresenham line");
+#endif
+ XGA_DrawLineBresenham(val);
+ } else {
+#if XGA_SHOW_COMMAND_TRACE == 1
+ LOG_MSG("XGA: Drawing vector line");
+#endif
+ XGA_DrawLineVector(val);
+ }
+ } else {
+ LOG_MSG("XGA: Wants line drawn from PIX_TRANS register!");
+ }
+ break;
+ case 2: /* Rectangle fill */
+ if((val & 0x100) == 0) {
+ xga.waitcmd.wait = false;
+#if XGA_SHOW_COMMAND_TRACE == 1
+ LOG_MSG("XGA: Draw immediate rect: xy(%3d/%3d), len(%3d/%3d)",
+ xga.curx,xga.cury,xga.MAPcount,xga.MIPcount);
+#endif
+ XGA_DrawRectangle(val);
+
+ } else {
+
+ xga.waitcmd.newline = true;
+ xga.waitcmd.wait = true;
+ xga.waitcmd.curx = xga.curx;
+ xga.waitcmd.cury = xga.cury;
+ xga.waitcmd.x1 = xga.curx;
+ xga.waitcmd.y1 = xga.cury;
+ xga.waitcmd.x2 = (Bit16u)((xga.curx + xga.MAPcount)&0x0fff);
+ xga.waitcmd.y2 = (Bit16u)((xga.cury + xga.MIPcount + 1)&0x0fff);
+ xga.waitcmd.sizex = xga.MAPcount;
+ xga.waitcmd.sizey = xga.MIPcount + 1;
+ xga.waitcmd.cmd = 2;
+ xga.waitcmd.buswidth = vga.mode | ((val&0x600) >> 4);
+ xga.waitcmd.data = 0;
+ xga.waitcmd.datasize = 0;
+
+#if XGA_SHOW_COMMAND_TRACE == 1
+ LOG_MSG("XGA: Draw wait rect, w/h(%3d/%3d), x/y1(%3d/%3d), x/y2(%3d/%3d), %4x",
+ xga.MAPcount+1, xga.MIPcount+1,xga.curx,xga.cury,
+ (xga.curx + xga.MAPcount)&0x0fff,
+ (xga.cury + xga.MIPcount + 1)&0x0fff,val&0xffff);
+#endif
+
+ }
+ break;
+ case 6: /* BitBLT */
+#if XGA_SHOW_COMMAND_TRACE == 1
+ LOG_MSG("XGA: Blit Rect");
+#endif
+ XGA_BlitRect(val);
+ break;
+ case 7: /* Pattern fill */
+#if XGA_SHOW_COMMAND_TRACE == 1
+ LOG_MSG("XGA: Pattern fill: src(%3d/%3d), dest(%3d/%3d), fill(%3d/%3d)",
+ xga.curx,xga.cury,xga.destx,xga.desty,xga.MAPcount,xga.MIPcount);
+#endif
+ XGA_DrawPattern(val);
+ break;
+ default:
+ LOG_MSG("XGA: Unhandled draw command %x", cmd);
+ break;
+ }
+}
+
+void XGA_SetDualReg(Bit32u& reg, Bitu val) {
+ switch(XGA_COLOR_MODE) {
+ case M_LIN8:
+ reg = (Bit8u)(val&0xff); break;
+ case M_LIN15:
+ case M_LIN16:
+ reg = (Bit16u)(val&0xffff); break;
+ case M_LIN32:
+ if (xga.control1 & 0x200)
+ reg = val;
+ else if (xga.control1 & 0x10)
+ reg = (reg&0x0000ffff)|(val<<16);
+ else
+ reg = (reg&0xffff0000)|(val&0x0000ffff);
+ xga.control1 ^= 0x10;
+ break;
+ }
+}
+
+Bitu XGA_GetDualReg(Bit32u reg) {
+ switch(XGA_COLOR_MODE) {
+ case M_LIN8:
+ return (Bit8u)(reg&0xff);
+ case M_LIN15: case M_LIN16:
+ return (Bit16u)(reg&0xffff);
+ case M_LIN32:
+ if (xga.control1 & 0x200) return reg;
+ xga.control1 ^= 0x10;
+ if (xga.control1 & 0x10) return reg&0x0000ffff;
+ else return reg>>16;
+ }
+ return 0;
+}
+
+extern Bitu vga_read_p3da(Bitu port,Bitu iolen);
+
+extern void vga_write_p3d4(Bitu port,Bitu val,Bitu iolen);
+extern Bitu vga_read_p3d4(Bitu port,Bitu iolen);
+
+extern void vga_write_p3d5(Bitu port,Bitu val,Bitu iolen);
+extern Bitu vga_read_p3d5(Bitu port,Bitu iolen);
+
+void XGA_Write(Bitu port, Bitu val, Bitu len) {
+// LOG_MSG("XGA: Write to port %x, val %8x, len %x", port,val, len);
+
+ switch(port) {
+ case 0x8100:// drawing control: row (low word), column (high word)
+ // "CUR_X" and "CUR_Y" (see PORT 82E8h,PORT 86E8h)
+ xga.cury = val & 0x0fff;
+ if(len==4) xga.curx = (val>>16)&0x0fff;
+ break;
+ case 0x8102:
+ xga.curx = val& 0x0fff;
+ break;
+
+ case 0x8108:// DWORD drawing control: destination Y and axial step
+ // constant (low word), destination X and axial step
+ // constant (high word) (see PORT 8AE8h,PORT 8EE8h)
+ xga.desty = val&0x3FFF;
+ if(len==4) xga.destx = (val>>16)&0x3fff;
+ break;
+ case 0x810a:
+ xga.destx = val&0x3fff;
+ break;
+ case 0x8110: // WORD error term (see PORT 92E8h)
+ xga.ErrTerm = val&0x3FFF;
+ break;
+
+ case 0x8120: // packed MMIO: DWORD background color (see PORT A2E8h)
+ xga.backcolor = val;
+ break;
+ case 0x8124: // packed MMIO: DWORD foreground color (see PORT A6E8h)
+ xga.forecolor = val;
+ break;
+ case 0x8128: // DWORD write mask (see PORT AAE8h)
+ xga.writemask = val;
+ break;
+ case 0x812C: // DWORD read mask (see PORT AEE8h)
+ xga.readmask = val;
+ break;
+ case 0x8134: // packed MMIO: DWORD background mix (low word) and
+ // foreground mix (high word) (see PORT B6E8h,PORT BAE8h)
+ xga.backmix = val&0xFFFF;
+ if(len==4) xga.foremix = (val>>16);
+ break;
+ case 0x8136:
+ xga.foremix = val;
+ break;
+ case 0x8138:// DWORD top scissors (low word) and left scissors (high
+ // word) (see PORT BEE8h,#P1047)
+ xga.scissors.y1=val&0x0fff;
+ if(len==4) xga.scissors.x1 = (val>>16)&0x0fff;
+ break;
+ case 0x813a:
+ xga.scissors.x1 = val&0x0fff;
+ break;
+ case 0x813C:// DWORD bottom scissors (low word) and right scissors
+ // (high word) (see PORT BEE8h,#P1047)
+ xga.scissors.y2=val&0x0fff;
+ if(len==4) xga.scissors.x2 = (val>>16)&0x0fff;
+ break;
+ case 0x813e:
+ xga.scissors.x2 = val&0x0fff;
+ break;
+
+ case 0x8140:// DWORD data manipulation control (low word) and
+ // miscellaneous 2 (high word) (see PORT BEE8h,#P1047)
+ xga.pix_cntl=val&0xFFFF;
+ if(len==4) xga.control2=(val>>16)&0x0fff;
+ break;
+ case 0x8144:// DWORD miscellaneous (low word) and read register select
+ // (high word)(see PORT BEE8h,#P1047)
+ xga.control1=val&0xffff;
+ if(len==4)xga.read_sel=(val>>16)&0x7;
+ break;
+ case 0x8148:// DWORD minor axis pixel count (low word) and major axis
+ // pixel count (high word) (see PORT BEE8h,#P1047,PORT 96E8h)
+ xga.MIPcount = val&0x0fff;
+ if(len==4) xga.MAPcount = (val>>16)&0x0fff;
+ break;
+ case 0x814a:
+ xga.MAPcount = val&0x0fff;
+ break;
+ case 0x92e8:
+ xga.ErrTerm = val&0x3FFF;
+ break;
+ case 0x96e8:
+ xga.MAPcount = val&0x0fff;
+ break;
+ case 0x9ae8:
+ case 0x8118: // Trio64V+ packed MMIO
+ XGA_DrawCmd(val, len);
+ break;
+ case 0xa2e8:
+ XGA_SetDualReg(xga.backcolor, val);
+ break;
+ case 0xa6e8:
+ XGA_SetDualReg(xga.forecolor, val);
+ break;
+ case 0xaae8:
+ XGA_SetDualReg(xga.writemask, val);
+ break;
+ case 0xaee8:
+ XGA_SetDualReg(xga.readmask, val);
+ break;
+ case 0x82e8:
+ xga.cury = val&0x0fff;
+ break;
+ case 0x86e8:
+ xga.curx = val&0x0fff;
+ break;
+ case 0x8ae8:
+ xga.desty = val&0x3fff;
+ break;
+ case 0x8ee8:
+ xga.destx = val&0x3fff;
+ break;
+ case 0xb2e8:
+ LOG_MSG("COLOR_CMP not implemented");
+ break;
+ case 0xb6e8:
+ xga.backmix = val;
+ break;
+ case 0xbae8:
+ xga.foremix = val;
+ break;
+ case 0xbee8:
+ XGA_Write_Multifunc(val, len);
+ break;
+ case 0xe2e8:
+ xga.waitcmd.newline = false;
+ XGA_DrawWait(val, len);
+ break;
+ case 0x83d4:
+ if(len==1) vga_write_p3d4(0,val,1);
+ else if(len==2) {
+ vga_write_p3d4(0,val&0xff,1);
+ vga_write_p3d5(0,val>>8,1);
+ }
+ else E_Exit("unimplemented XGA MMIO");
+ break;
+ case 0x83d5:
+ if(len==1) vga_write_p3d5(0,val,1);
+ else E_Exit("unimplemented XGA MMIO");
+ break;
+ default:
+ if(port <= 0x4000) {
+ //LOG_MSG("XGA: Wrote to port %4x with %08x, len %x", port, val, len);
+ xga.waitcmd.newline = false;
+ XGA_DrawWait(val, len);
+
+ }
+ else LOG_MSG("XGA: Wrote to port %x with %x, len %x", port, val, len);
+ break;
+ }
+}
+
+Bitu XGA_Read(Bitu port, Bitu len) {
+ switch(port) {
+ case 0x8118:
+ case 0x9ae8:
+ return 0x400; // nothing busy
+ break;
+ case 0x81ec: // S3 video data processor
+ return 0x00007000;
+ break;
+ case 0x83da:
+ {
+ Bits delaycyc = CPU_CycleMax/5000;
+ if(GCC_UNLIKELY(CPU_Cycles < 3*delaycyc)) delaycyc = 0;
+ CPU_Cycles -= delaycyc;
+ CPU_IODelayRemoved += delaycyc;
+ return vga_read_p3da(0,0);
+ break;
+ }
+ case 0x83d4:
+ if(len==1) return vga_read_p3d4(0,0);
+ else E_Exit("unimplemented XGA MMIO");
+ break;
+ case 0x83d5:
+ if(len==1) return vga_read_p3d5(0,0);
+ else E_Exit("unimplemented XGA MMIO");
+ break;
+ case 0x9ae9:
+ if(xga.waitcmd.wait) return 0x4;
+ else return 0x0;
+ case 0xbee8:
+ return XGA_Read_Multifunc();
+ case 0xa2e8:
+ return XGA_GetDualReg(xga.backcolor);
+ break;
+ case 0xa6e8:
+ return XGA_GetDualReg(xga.forecolor);
+ break;
+ case 0xaae8:
+ return XGA_GetDualReg(xga.writemask);
+ break;
+ case 0xaee8:
+ return XGA_GetDualReg(xga.readmask);
+ break;
+ default:
+ //LOG_MSG("XGA: Read from port %x, len %x", port, len);
+ break;
+ }
+ return 0xffffffff;
+}
+
+void VGA_SetupXGA(void) {
+ if (!IS_VGA_ARCH) return;
+
+ memset(&xga, 0, sizeof(XGAStatus));
+
+ xga.scissors.y1 = 0;
+ xga.scissors.x1 = 0;
+ xga.scissors.y2 = 0xFFF;
+ xga.scissors.x2 = 0xFFF;
+
+ IO_RegisterWriteHandler(0x42e8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x42e8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0x46e8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0x4ae8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0x82e8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x82e8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0x82e9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x82e9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0x86e8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x86e8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0x86e9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x86e9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0x8ae8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x8ae8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0x8ee8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x8ee8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0x8ee9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x8ee9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0x92e8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x92e8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0x92e9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x92e9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0x96e8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x96e8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0x96e9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x96e9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0x9ae8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x9ae8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0x9ae9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x9ae9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0x9ee8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x9ee8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0x9ee9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0x9ee9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0xa2e8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xa2e8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0xa6e8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xa6e8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0xa6e9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xa6e9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0xaae8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xaae8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0xaae9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xaae9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0xaee8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xaee8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0xaee9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xaee9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0xb2e8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xb2e8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0xb2e9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xb2e9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0xb6e8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xb6e8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0xbee8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xbee8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0xbee9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xbee9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0xbae8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xbae8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+ IO_RegisterWriteHandler(0xbae9,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xbae9,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0xe2e8,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xe2e8,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0xe2e0,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xe2e0,&XGA_Read,IO_MB | IO_MW | IO_MD);
+
+ IO_RegisterWriteHandler(0xe2ea,&XGA_Write,IO_MB | IO_MW | IO_MD);
+ IO_RegisterReadHandler(0xe2ea,&XGA_Read,IO_MB | IO_MW | IO_MD);
+}
diff --git a/src/ints/Makefile.am b/src/ints/Makefile.am
new file mode 100644
index 000000000..e994d09fe
--- /dev/null
+++ b/src/ints/Makefile.am
@@ -0,0 +1,7 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libints.a
+libints_a_SOURCES = mouse.cpp xms.cpp xms.h ems.cpp \
+ int10.cpp int10.h int10_char.cpp int10_memory.cpp int10_misc.cpp int10_modes.cpp \
+ int10_vesa.cpp int10_pal.cpp int10_put_pixel.cpp int10_video_state.cpp int10_vptable.cpp \
+ bios.cpp bios_disk.cpp bios_keyboard.cpp
diff --git a/src/ints/bios.cpp b/src/ints/bios.cpp
new file mode 100644
index 000000000..dfeb0a15e
--- /dev/null
+++ b/src/ints/bios.cpp
@@ -0,0 +1,1361 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "mem.h"
+#include "bios.h"
+#include "regs.h"
+#include "cpu.h"
+#include "callback.h"
+#include "inout.h"
+#include "pic.h"
+#include "hardware.h"
+#include "pci_bus.h"
+#include "joystick.h"
+#include "mouse.h"
+#include "setup.h"
+#include "serialport.h"
+#include <time.h>
+
+#if defined(DB_HAVE_CLOCK_GETTIME) && ! defined(WIN32)
+//time.h is already included
+#else
+#include <sys/timeb.h>
+#endif
+
+/* if mem_systems 0 then size_extended is reported as the real size else
+ * zero is reported. ems and xms can increase or decrease the other_memsystems
+ * counter using the BIOS_ZeroExtendedSize call */
+static Bit16u size_extended;
+static Bits other_memsystems=0;
+void CMOS_SetRegister(Bitu regNr, Bit8u val); //For setting equipment word
+
+static Bitu INT70_Handler(void) {
+ /* Acknowledge irq with cmos */
+ IO_Write(0x70,0xc);
+ IO_Read(0x71);
+ if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) {
+ Bit32u count=mem_readd(BIOS_WAIT_FLAG_COUNT);
+ if (count>997) {
+ mem_writed(BIOS_WAIT_FLAG_COUNT,count-997);
+ } else {
+ mem_writed(BIOS_WAIT_FLAG_COUNT,0);
+ PhysPt where=Real2Phys(mem_readd(BIOS_WAIT_FLAG_POINTER));
+ mem_writeb(where,mem_readb(where)|0x80);
+ mem_writeb(BIOS_WAIT_FLAG_ACTIVE,0);
+ mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(0,BIOS_WAIT_FLAG_TEMP));
+ IO_Write(0x70,0xb);
+ IO_Write(0x71,IO_Read(0x71)&~0x40);
+ }
+ }
+ /* Signal EOI to both pics */
+ IO_Write(0xa0,0x20);
+ IO_Write(0x20,0x20);
+ return 0;
+}
+
+CALLBACK_HandlerObject* tandy_DAC_callback[2];
+static struct {
+ Bit16u port;
+ Bit8u irq;
+ Bit8u dma;
+} tandy_sb;
+static struct {
+ Bit16u port;
+ Bit8u irq;
+ Bit8u dma;
+} tandy_dac;
+
+static bool Tandy_InitializeSB() {
+ /* see if soundblaster module available and at what port/IRQ/DMA */
+ Bitu sbport, sbirq, sbdma;
+ if (SB_Get_Address(sbport, sbirq, sbdma)) {
+ tandy_sb.port=(Bit16u)(sbport&0xffff);
+ tandy_sb.irq =(Bit8u)(sbirq&0xff);
+ tandy_sb.dma =(Bit8u)(sbdma&0xff);
+ return true;
+ } else {
+ /* no soundblaster accessible, disable Tandy DAC */
+ tandy_sb.port=0;
+ return false;
+ }
+}
+
+static bool Tandy_InitializeTS() {
+ /* see if Tandy DAC module available and at what port/IRQ/DMA */
+ Bitu tsport, tsirq, tsdma;
+ if (TS_Get_Address(tsport, tsirq, tsdma)) {
+ tandy_dac.port=(Bit16u)(tsport&0xffff);
+ tandy_dac.irq =(Bit8u)(tsirq&0xff);
+ tandy_dac.dma =(Bit8u)(tsdma&0xff);
+ return true;
+ } else {
+ /* no Tandy DAC accessible */
+ tandy_dac.port=0;
+ return false;
+ }
+}
+
+/* check if Tandy DAC is still playing */
+static bool Tandy_TransferInProgress(void) {
+ if (real_readw(0x40,0xd0)) return true; /* not yet done */
+ if (real_readb(0x40,0xd4)==0xff) return false; /* still in init-state */
+
+ Bit8u tandy_dma = 1;
+ if (tandy_sb.port) tandy_dma = tandy_sb.dma;
+ else if (tandy_dac.port) tandy_dma = tandy_dac.dma;
+
+ IO_Write(0x0c,0x00);
+ Bit16u datalen=(Bit8u)(IO_ReadB(tandy_dma*2+1)&0xff);
+ datalen|=(IO_ReadB(tandy_dma*2+1)<<8);
+ if (datalen==0xffff) return false; /* no DMA transfer */
+ else if ((datalen<0x10) && (real_readb(0x40,0xd4)==0x0f) && (real_readw(0x40,0xd2)==0x1c)) {
+ /* stop already requested */
+ return false;
+ }
+ return true;
+}
+
+static void Tandy_SetupTransfer(PhysPt bufpt,bool isplayback) {
+ Bitu length=real_readw(0x40,0xd0);
+ if (length==0) return; /* nothing to do... */
+
+ if ((tandy_sb.port==0) && (tandy_dac.port==0)) return;
+
+ Bit8u tandy_irq = 7;
+ if (tandy_sb.port) tandy_irq = tandy_sb.irq;
+ else if (tandy_dac.port) tandy_irq = tandy_dac.irq;
+ Bit8u tandy_irq_vector = tandy_irq;
+ if (tandy_irq_vector<8) tandy_irq_vector += 8;
+ else tandy_irq_vector += (0x70-8);
+
+ /* revector IRQ-handler if necessary */
+ RealPt current_irq=RealGetVec(tandy_irq_vector);
+ if (current_irq!=tandy_DAC_callback[0]->Get_RealPointer()) {
+ real_writed(0x40,0xd6,current_irq);
+ RealSetVec(tandy_irq_vector,tandy_DAC_callback[0]->Get_RealPointer());
+ }
+
+ Bit8u tandy_dma = 1;
+ if (tandy_sb.port) tandy_dma = tandy_sb.dma;
+ else if (tandy_dac.port) tandy_dma = tandy_dac.dma;
+
+ if (tandy_sb.port) {
+ IO_Write(tandy_sb.port+0xc,0xd0); /* stop DMA transfer */
+ IO_Write(0x21,IO_Read(0x21)&(~(1<<tandy_irq))); /* unmask IRQ */
+ IO_Write(tandy_sb.port+0xc,0xd1); /* turn speaker on */
+ } else {
+ IO_Write(tandy_dac.port,IO_Read(tandy_dac.port)&0x60); /* disable DAC */
+ IO_Write(0x21,IO_Read(0x21)&(~(1<<tandy_irq))); /* unmask IRQ */
+ }
+
+ IO_Write(0x0a,0x04|tandy_dma); /* mask DMA channel */
+ IO_Write(0x0c,0x00); /* clear DMA flipflop */
+ if (isplayback) IO_Write(0x0b,0x48|tandy_dma);
+ else IO_Write(0x0b,0x44|tandy_dma);
+ /* set physical address of buffer */
+ Bit8u bufpage=(Bit8u)((bufpt>>16)&0xff);
+ IO_Write(tandy_dma*2,(Bit8u)(bufpt&0xff));
+ IO_Write(tandy_dma*2,(Bit8u)((bufpt>>8)&0xff));
+ switch (tandy_dma) {
+ case 0: IO_Write(0x87,bufpage); break;
+ case 1: IO_Write(0x83,bufpage); break;
+ case 2: IO_Write(0x81,bufpage); break;
+ case 3: IO_Write(0x82,bufpage); break;
+ }
+ real_writeb(0x40,0xd4,bufpage);
+
+ /* calculate transfer size (respects segment boundaries) */
+ Bit32u tlength=length;
+ if (tlength+(bufpt&0xffff)>0x10000) tlength=0x10000-(bufpt&0xffff);
+ real_writew(0x40,0xd0,(Bit16u)(length-tlength)); /* remaining buffer length */
+ tlength--;
+
+ /* set transfer size */
+ IO_Write(tandy_dma*2+1,(Bit8u)(tlength&0xff));
+ IO_Write(tandy_dma*2+1,(Bit8u)((tlength>>8)&0xff));
+
+ Bit16u delay=(Bit16u)(real_readw(0x40,0xd2)&0xfff);
+ Bit8u amplitude=(Bit8u)((real_readw(0x40,0xd2)>>13)&0x7);
+ if (tandy_sb.port) {
+ IO_Write(0x0a,tandy_dma); /* enable DMA channel */
+ /* set frequency */
+ IO_Write(tandy_sb.port+0xc,0x40);
+ IO_Write(tandy_sb.port+0xc,256-delay*100/358);
+ /* set playback type to 8bit */
+ if (isplayback) IO_Write(tandy_sb.port+0xc,0x14);
+ else IO_Write(tandy_sb.port+0xc,0x24);
+ /* set transfer size */
+ IO_Write(tandy_sb.port+0xc,(Bit8u)(tlength&0xff));
+ IO_Write(tandy_sb.port+0xc,(Bit8u)((tlength>>8)&0xff));
+ } else {
+ if (isplayback) IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7c) | 0x03);
+ else IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7c) | 0x02);
+ IO_Write(tandy_dac.port+2,(Bit8u)(delay&0xff));
+ IO_Write(tandy_dac.port+3,(Bit8u)(((delay>>8)&0xf) | (amplitude<<5)));
+ if (isplayback) IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7c) | 0x1f);
+ else IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7c) | 0x1e);
+ IO_Write(0x0a,tandy_dma); /* enable DMA channel */
+ }
+
+ if (!isplayback) {
+ /* mark transfer as recording operation */
+ real_writew(0x40,0xd2,(Bit16u)(delay|0x1000));
+ }
+}
+
+static Bitu IRQ_TandyDAC(void) {
+ if (tandy_dac.port) {
+ IO_Read(tandy_dac.port);
+ }
+ if (real_readw(0x40,0xd0)) { /* play/record next buffer */
+ /* acknowledge IRQ */
+ IO_Write(0x20,0x20);
+ if (tandy_sb.port) {
+ IO_Read(tandy_sb.port+0xe);
+ }
+
+ /* buffer starts at the next page */
+ Bit8u npage=real_readb(0x40,0xd4)+1;
+ real_writeb(0x40,0xd4,npage);
+
+ Bitu rb=real_readb(0x40,0xd3);
+ if (rb&0x10) {
+ /* start recording */
+ real_writeb(0x40,0xd3,rb&0xef);
+ Tandy_SetupTransfer(npage<<16,false);
+ } else {
+ /* start playback */
+ Tandy_SetupTransfer(npage<<16,true);
+ }
+ } else { /* playing/recording is finished */
+ Bit8u tandy_irq = 7;
+ if (tandy_sb.port) tandy_irq = tandy_sb.irq;
+ else if (tandy_dac.port) tandy_irq = tandy_dac.irq;
+ Bit8u tandy_irq_vector = tandy_irq;
+ if (tandy_irq_vector<8) tandy_irq_vector += 8;
+ else tandy_irq_vector += (0x70-8);
+
+ RealSetVec(tandy_irq_vector,real_readd(0x40,0xd6));
+
+ /* turn off speaker and acknowledge soundblaster IRQ */
+ if (tandy_sb.port) {
+ IO_Write(tandy_sb.port+0xc,0xd3);
+ IO_Read(tandy_sb.port+0xe);
+ }
+
+ /* issue BIOS tandy sound device busy callout */
+ SegSet16(cs, RealSeg(tandy_DAC_callback[1]->Get_RealPointer()));
+ reg_ip = RealOff(tandy_DAC_callback[1]->Get_RealPointer());
+ }
+ return CBRET_NONE;
+}
+
+static void TandyDAC_Handler(Bit8u tfunction) {
+ if ((!tandy_sb.port) && (!tandy_dac.port)) return;
+ switch (tfunction) {
+ case 0x81: /* Tandy sound system check */
+ if (tandy_dac.port) {
+ reg_ax=tandy_dac.port;
+ } else {
+ reg_ax=0xc4;
+ }
+ CALLBACK_SCF(Tandy_TransferInProgress());
+ break;
+ case 0x82: /* Tandy sound system start recording */
+ case 0x83: /* Tandy sound system start playback */
+ if (Tandy_TransferInProgress()) {
+ /* cannot play yet as the last transfer isn't finished yet */
+ reg_ah=0x00;
+ CALLBACK_SCF(true);
+ break;
+ }
+ /* store buffer length */
+ real_writew(0x40,0xd0,reg_cx);
+ /* store delay and volume */
+ real_writew(0x40,0xd2,(reg_dx&0xfff)|((reg_al&7)<<13));
+ Tandy_SetupTransfer(PhysMake(SegValue(es),reg_bx),reg_ah==0x83);
+ reg_ah=0x00;
+ CALLBACK_SCF(false);
+ break;
+ case 0x84: /* Tandy sound system stop playing */
+ reg_ah=0x00;
+
+ /* setup for a small buffer with silence */
+ real_writew(0x40,0xd0,0x0a);
+ real_writew(0x40,0xd2,0x1c);
+ Tandy_SetupTransfer(PhysMake(0xf000,0xa084),true);
+ CALLBACK_SCF(false);
+ break;
+ case 0x85: /* Tandy sound system reset */
+ if (tandy_dac.port) {
+ IO_Write(tandy_dac.port,(Bit8u)(IO_Read(tandy_dac.port)&0xe0));
+ }
+ reg_ah=0x00;
+ CALLBACK_SCF(false);
+ break;
+ }
+}
+
+static Bitu INT1A_Handler(void) {
+ switch (reg_ah) {
+ case 0x00: /* Get System time */
+ {
+ Bit32u ticks=mem_readd(BIOS_TIMER);
+ reg_al=mem_readb(BIOS_24_HOURS_FLAG);
+ mem_writeb(BIOS_24_HOURS_FLAG,0); // reset the "flag"
+ reg_cx=(Bit16u)(ticks >> 16);
+ reg_dx=(Bit16u)(ticks & 0xffff);
+ break;
+ }
+ case 0x01: /* Set System time */
+ mem_writed(BIOS_TIMER,(reg_cx<<16)|reg_dx);
+ break;
+ case 0x02: /* GET REAL-TIME CLOCK TIME (AT,XT286,PS) */
+ IO_Write(0x70,0x04); //Hours
+ reg_ch=IO_Read(0x71);
+ IO_Write(0x70,0x02); //Minutes
+ reg_cl=IO_Read(0x71);
+ IO_Write(0x70,0x00); //Seconds
+ reg_dh=IO_Read(0x71);
+ reg_dl=0; //Daylight saving disabled
+ CALLBACK_SCF(false);
+ break;
+ case 0x04: /* GET REAL-TIME ClOCK DATE (AT,XT286,PS) */
+ IO_Write(0x70,0x32); //Centuries
+ reg_ch=IO_Read(0x71);
+ IO_Write(0x70,0x09); //Years
+ reg_cl=IO_Read(0x71);
+ IO_Write(0x70,0x08); //Months
+ reg_dh=IO_Read(0x71);
+ IO_Write(0x70,0x07); //Days
+ reg_dl=IO_Read(0x71);
+ CALLBACK_SCF(false);
+ break;
+ case 0x80: /* Pcjr Setup Sound Multiplexer */
+ LOG(LOG_BIOS,LOG_ERROR)("INT1A:80:Setup tandy sound multiplexer to %d",reg_al);
+ break;
+ case 0x81: /* Tandy sound system check */
+ case 0x82: /* Tandy sound system start recording */
+ case 0x83: /* Tandy sound system start playback */
+ case 0x84: /* Tandy sound system stop playing */
+ case 0x85: /* Tandy sound system reset */
+ TandyDAC_Handler(reg_ah);
+ break;
+ case 0xb1: /* PCI Bios Calls */
+ LOG(LOG_BIOS,LOG_WARN)("INT1A:PCI bios call %2X",reg_al);
+#if defined(PCI_FUNCTIONALITY_ENABLED)
+ switch (reg_al) {
+ case 0x01: // installation check
+ if (PCI_IsInitialized()) {
+ reg_ah=0x00;
+ reg_al=0x01; // cfg space mechanism 1 supported
+ reg_bx=0x0210; // ver 2.10
+ reg_cx=0x0000; // only one PCI bus
+ reg_edx=0x20494350;
+ reg_edi=PCI_GetPModeInterface();
+ CALLBACK_SCF(false);
+ } else {
+ CALLBACK_SCF(true);
+ }
+ break;
+ case 0x02: { // find device
+ Bitu devnr=0;
+ Bitu count=0x100;
+ Bit32u devicetag=(reg_cx<<16)|reg_dx;
+ Bits found=-1;
+ for (Bitu i=0; i<=count; i++) {
+ IO_WriteD(0xcf8,0x80000000|(i<<8)); // query unique device/subdevice entries
+ if (IO_ReadD(0xcfc)==devicetag) {
+ if (devnr==reg_si) {
+ found=i;
+ break;
+ } else {
+ // device found, but not the SIth device
+ devnr++;
+ }
+ }
+ }
+ if (found>=0) {
+ reg_ah=0x00;
+ reg_bh=0x00; // bus 0
+ reg_bl=(Bit8u)(found&0xff);
+ CALLBACK_SCF(false);
+ } else {
+ reg_ah=0x86; // device not found
+ CALLBACK_SCF(true);
+ }
+ }
+ break;
+ case 0x03: { // find device by class code
+ Bitu devnr=0;
+ Bitu count=0x100;
+ Bit32u classtag=reg_ecx&0xffffff;
+ Bits found=-1;
+ for (Bitu i=0; i<=count; i++) {
+ IO_WriteD(0xcf8,0x80000000|(i<<8)); // query unique device/subdevice entries
+ if (IO_ReadD(0xcfc)!=0xffffffff) {
+ IO_WriteD(0xcf8,0x80000000|(i<<8)|0x08);
+ if ((IO_ReadD(0xcfc)>>8)==classtag) {
+ if (devnr==reg_si) {
+ found=i;
+ break;
+ } else {
+ // device found, but not the SIth device
+ devnr++;
+ }
+ }
+ }
+ }
+ if (found>=0) {
+ reg_ah=0x00;
+ reg_bh=0x00; // bus 0
+ reg_bl=(Bit8u)(found&0xff);
+ CALLBACK_SCF(false);
+ } else {
+ reg_ah=0x86; // device not found
+ CALLBACK_SCF(true);
+ }
+ }
+ break;
+ case 0x08: // read configuration byte
+ IO_WriteD(0xcf8,0x80000000|(reg_bx<<8)|(reg_di&0xfc));
+ reg_cl=IO_ReadB(0xcfc+(reg_di&3));
+ CALLBACK_SCF(false);
+ break;
+ case 0x09: // read configuration word
+ IO_WriteD(0xcf8,0x80000000|(reg_bx<<8)|(reg_di&0xfc));
+ reg_cx=IO_ReadW(0xcfc+(reg_di&2));
+ CALLBACK_SCF(false);
+ break;
+ case 0x0a: // read configuration dword
+ IO_WriteD(0xcf8,0x80000000|(reg_bx<<8)|(reg_di&0xfc));
+ reg_ecx=IO_ReadD(0xcfc+(reg_di&3));
+ CALLBACK_SCF(false);
+ break;
+ case 0x0b: // write configuration byte
+ IO_WriteD(0xcf8,0x80000000|(reg_bx<<8)|(reg_di&0xfc));
+ IO_WriteB(0xcfc+(reg_di&3),reg_cl);
+ CALLBACK_SCF(false);
+ break;
+ case 0x0c: // write configuration word
+ IO_WriteD(0xcf8,0x80000000|(reg_bx<<8)|(reg_di&0xfc));
+ IO_WriteW(0xcfc+(reg_di&2),reg_cx);
+ CALLBACK_SCF(false);
+ break;
+ case 0x0d: // write configuration dword
+ IO_WriteD(0xcf8,0x80000000|(reg_bx<<8)|(reg_di&0xfc));
+ IO_WriteD(0xcfc+(reg_di&3),reg_ecx);
+ CALLBACK_SCF(false);
+ break;
+ default:
+ LOG(LOG_BIOS,LOG_ERROR)("INT1A:PCI BIOS: unknown function %x (%x %x %x)",
+ reg_ax,reg_bx,reg_cx,reg_dx);
+ CALLBACK_SCF(true);
+ break;
+ }
+#else
+ CALLBACK_SCF(true);
+#endif
+ break;
+ default:
+ LOG(LOG_BIOS,LOG_ERROR)("INT1A:Undefined call %2X",reg_ah);
+ }
+ return CBRET_NONE;
+}
+
+static Bitu INT11_Handler(void) {
+ reg_ax=mem_readw(BIOS_CONFIGURATION);
+ return CBRET_NONE;
+}
+/*
+ * Define the following define to 1 if you want dosbox to check
+ * the system time every 5 seconds and adjust 1/2 a second to sync them.
+ */
+#ifndef DOSBOX_CLOCKSYNC
+#define DOSBOX_CLOCKSYNC 0
+#endif
+
+static void BIOS_HostTimeSync() {
+ Bit32u milli = 0;
+#if defined(DB_HAVE_CLOCK_GETTIME) && ! defined(WIN32)
+ struct timespec tp;
+ clock_gettime(CLOCK_REALTIME,&tp);
+
+ struct tm *loctime;
+ loctime = localtime(&tp.tv_sec);
+ milli = (Bit32u) (tp.tv_nsec / 1000000);
+#else
+ /* Setup time and date */
+ struct timeb timebuffer;
+ ftime(&timebuffer);
+
+ struct tm *loctime;
+ loctime = localtime (&timebuffer.time);
+ milli = (Bit32u) timebuffer.millitm;
+#endif
+ /*
+ loctime->tm_hour = 23;
+ loctime->tm_min = 59;
+ loctime->tm_sec = 45;
+ loctime->tm_mday = 28;
+ loctime->tm_mon = 2-1;
+ loctime->tm_year = 2007 - 1900;
+ */
+
+ dos.date.day=(Bit8u)loctime->tm_mday;
+ dos.date.month=(Bit8u)loctime->tm_mon+1;
+ dos.date.year=(Bit16u)loctime->tm_year+1900;
+
+ Bit32u ticks=(Bit32u)(((double)(
+ loctime->tm_hour*3600*1000+
+ loctime->tm_min*60*1000+
+ loctime->tm_sec*1000+
+ milli))*(((double)PIT_TICK_RATE/65536.0)/1000.0));
+ mem_writed(BIOS_TIMER,ticks);
+}
+
+static Bitu INT8_Handler(void) {
+ /* Increase the bios tick counter */
+ Bit32u value = mem_readd(BIOS_TIMER) + 1;
+ if(value >= 0x1800B0) {
+ // time wrap at midnight
+ mem_writeb(BIOS_24_HOURS_FLAG,mem_readb(BIOS_24_HOURS_FLAG)+1);
+ value=0;
+ }
+
+#if DOSBOX_CLOCKSYNC
+ static bool check = false;
+ if((value %50)==0) {
+ if(((value %100)==0) && check) {
+ check = false;
+ time_t curtime;struct tm *loctime;
+ curtime = time (NULL);loctime = localtime (&curtime);
+ Bit32u ticksnu = (Bit32u)((loctime->tm_hour*3600+loctime->tm_min*60+loctime->tm_sec)*(float)PIT_TICK_RATE/65536.0);
+ Bit32s bios = value;Bit32s tn = ticksnu;
+ Bit32s diff = tn - bios;
+ if(diff>0) {
+ if(diff < 18) { diff = 0; } else diff = 9;
+ } else {
+ if(diff > -18) { diff = 0; } else diff = -9;
+ }
+
+ value += diff;
+ } else if((value%100)==50) check = true;
+ }
+#endif
+ mem_writed(BIOS_TIMER,value);
+
+ /* decrease floppy motor timer */
+ Bit8u val = mem_readb(BIOS_DISK_MOTOR_TIMEOUT);
+ if (val) mem_writeb(BIOS_DISK_MOTOR_TIMEOUT,val-1);
+ /* and running drive */
+ mem_writeb(BIOS_DRIVE_RUNNING,mem_readb(BIOS_DRIVE_RUNNING) & 0xF0);
+ return CBRET_NONE;
+}
+#undef DOSBOX_CLOCKSYNC
+
+static Bitu INT1C_Handler(void) {
+ return CBRET_NONE;
+}
+
+static Bitu INT12_Handler(void) {
+ reg_ax=mem_readw(BIOS_MEMORY_SIZE);
+ return CBRET_NONE;
+}
+
+static Bitu INT17_Handler(void) {
+ LOG(LOG_BIOS,LOG_NORMAL)("INT17:Function %X",reg_ah);
+ switch(reg_ah) {
+ case 0x00: /* PRINTER: Write Character */
+ reg_ah=1; /* Report a timeout */
+ break;
+ case 0x01: /* PRINTER: Initialize port */
+ break;
+ case 0x02: /* PRINTER: Get Status */
+ reg_ah=0;
+ break;
+ };
+ return CBRET_NONE;
+}
+
+static bool INT14_Wait(Bit16u port, Bit8u mask, Bit8u timeout, Bit8u* retval) {
+ double starttime = PIC_FullIndex();
+ double timeout_f = timeout * 1000.0;
+ while (((*retval = IO_ReadB(port)) & mask) != mask) {
+ if (starttime < (PIC_FullIndex() - timeout_f)) {
+ return false;
+ }
+ CALLBACK_Idle();
+ }
+ return true;
+}
+
+static Bitu INT14_Handler(void) {
+ if (reg_ah > 0x3 || reg_dx > 0x3) { // 0-3 serial port functions
+ // and no more than 4 serial ports
+ LOG_MSG("BIOS INT14: Unhandled call AH=%2X DX=%4x",reg_ah,reg_dx);
+ return CBRET_NONE;
+ }
+
+ Bit16u port = real_readw(0x40,reg_dx*2); // DX is always port number
+ Bit8u timeout = mem_readb(BIOS_COM1_TIMEOUT + reg_dx);
+ if (port==0) {
+ LOG(LOG_BIOS,LOG_NORMAL)("BIOS INT14: port %d does not exist.",reg_dx);
+ return CBRET_NONE;
+ }
+ switch (reg_ah) {
+ case 0x00: {
+ // Initialize port
+ // Parameters: Return:
+ // AL: port parameters AL: modem status
+ // AH: line status
+
+ // set baud rate
+ Bitu baudrate = 9600;
+ Bit16u baudresult;
+ Bitu rawbaud=reg_al>>5;
+
+ if (rawbaud==0){ baudrate=110;}
+ else if (rawbaud==1){ baudrate=150;}
+ else if (rawbaud==2){ baudrate=300;}
+ else if (rawbaud==3){ baudrate=600;}
+ else if (rawbaud==4){ baudrate=1200;}
+ else if (rawbaud==5){ baudrate=2400;}
+ else if (rawbaud==6){ baudrate=4800;}
+ else if (rawbaud==7){ baudrate=9600;}
+
+ baudresult = (Bit16u)(115200 / baudrate);
+
+ IO_WriteB(port+3, 0x80); // enable divider access
+ IO_WriteB(port, (Bit8u)baudresult&0xff);
+ IO_WriteB(port+1, (Bit8u)(baudresult>>8));
+
+ // set line parameters, disable divider access
+ IO_WriteB(port+3, reg_al&0x1F); // LCR
+
+ // disable interrupts
+ IO_WriteB(port+1, 0); // IER
+
+ // get result
+ reg_ah=(Bit8u)(IO_ReadB(port+5)&0xff);
+ reg_al=(Bit8u)(IO_ReadB(port+6)&0xff);
+ CALLBACK_SCF(false);
+ break;
+ }
+ case 0x01: // Transmit character
+ // Parameters: Return:
+ // AL: character AL: unchanged
+ // AH: 0x01 AH: line status from just before the char was sent
+ // (0x80 | unpredicted) in case of timeout
+ // [undoc] (0x80 | line status) in case of tx timeout
+ // [undoc] (0x80 | modem status) in case of dsr/cts timeout
+
+ // set DTR & RTS on
+ IO_WriteB(port+4,0x3);
+ // wait for DSR & CTS
+ if (INT14_Wait(port+6, 0x30, timeout, &reg_ah)) {
+ // wait for TX buffer empty
+ if (INT14_Wait(port+5, 0x20, timeout, &reg_ah)) {
+ // fianlly send the character
+ IO_WriteB(port,reg_al);
+ } else
+ reg_ah |= 0x80;
+ } else // timed out
+ reg_ah |= 0x80;
+
+ CALLBACK_SCF(false);
+ break;
+ case 0x02: // Read character
+ // Parameters: Return:
+ // AH: 0x02 AL: received character
+ // [undoc] will be trashed in case of timeout
+ // AH: (line status & 0x1E) in case of success
+ // (0x80 | unpredicted) in case of timeout
+ // [undoc] (0x80 | line status) in case of rx timeout
+ // [undoc] (0x80 | modem status) in case of dsr timeout
+
+ // set DTR on
+ IO_WriteB(port+4,0x1);
+
+ // wait for DSR
+ if (INT14_Wait(port+6, 0x20, timeout, &reg_ah)) {
+ // wait for character to arrive
+ if (INT14_Wait(port+5, 0x01, timeout, &reg_ah)) {
+ reg_ah &= 0x1E;
+ reg_al = IO_ReadB(port);
+ } else
+ reg_ah |= 0x80;
+ } else
+ reg_ah |= 0x80;
+
+ CALLBACK_SCF(false);
+ break;
+ case 0x03: // get status
+ reg_ah=(Bit8u)(IO_ReadB(port+5)&0xff);
+ reg_al=(Bit8u)(IO_ReadB(port+6)&0xff);
+ CALLBACK_SCF(false);
+ break;
+
+ }
+ return CBRET_NONE;
+}
+
+static Bitu INT15_Handler(void) {
+ static Bit16u biosConfigSeg=0;
+ switch (reg_ah) {
+ case 0xC0: /* Get Configuration*/
+ {
+ if (biosConfigSeg==0) biosConfigSeg = DOS_GetMemory(1); //We have 16 bytes
+ PhysPt data = PhysMake(biosConfigSeg,0);
+ mem_writew(data,8); // 8 Bytes following
+ if (IS_TANDY_ARCH) {
+ if (machine==MCH_TANDY) {
+ // Model ID (Tandy)
+ mem_writeb(data+2,0xFF);
+ } else {
+ // Model ID (PCJR)
+ mem_writeb(data+2,0xFD);
+ }
+ mem_writeb(data+3,0x0A); // Submodel ID
+ mem_writeb(data+4,0x10); // Bios Revision
+ /* Tandy doesn't have a 2nd PIC, left as is for now */
+ mem_writeb(data+5,(1<<6)|(1<<5)|(1<<4)); // Feature Byte 1
+ } else {
+ mem_writeb(data+2,0xFC); // Model ID (PC)
+ mem_writeb(data+3,0x00); // Submodel ID
+ mem_writeb(data+4,0x01); // Bios Revision
+ mem_writeb(data+5,(1<<6)|(1<<5)|(1<<4)); // Feature Byte 1
+ }
+ mem_writeb(data+6,(1<<6)); // Feature Byte 2
+ mem_writeb(data+7,0); // Feature Byte 3
+ mem_writeb(data+8,0); // Feature Byte 4
+ mem_writeb(data+9,0); // Feature Byte 5
+ CPU_SetSegGeneral(es,biosConfigSeg);
+ reg_bx = 0;
+ reg_ah = 0;
+ CALLBACK_SCF(false);
+ }; break;
+ case 0x4f: /* BIOS - Keyboard intercept */
+ /* Carry should be set but let's just set it just in case */
+ CALLBACK_SCF(true);
+ break;
+ case 0x83: /* BIOS - SET EVENT WAIT INTERVAL */
+ {
+ if(reg_al == 0x01) { /* Cancel it */
+ mem_writeb(BIOS_WAIT_FLAG_ACTIVE,0);
+ IO_Write(0x70,0xb);
+ IO_Write(0x71,IO_Read(0x71)&~0x40);
+ CALLBACK_SCF(false);
+ break;
+ }
+ if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) {
+ reg_ah=0x80;
+ CALLBACK_SCF(true);
+ break;
+ }
+ Bit32u count=(reg_cx<<16)|reg_dx;
+ mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(SegValue(es),reg_bx));
+ mem_writed(BIOS_WAIT_FLAG_COUNT,count);
+ mem_writeb(BIOS_WAIT_FLAG_ACTIVE,1);
+ /* Reprogram RTC to start */
+ IO_Write(0x70,0xb);
+ IO_Write(0x71,IO_Read(0x71)|0x40);
+ CALLBACK_SCF(false);
+ }
+ break;
+ case 0x84: /* BIOS - JOYSTICK SUPPORT (XT after 11/8/82,AT,XT286,PS) */
+ if (reg_dx == 0x0000) {
+ // Get Joystick button status
+ if (JOYSTICK_IsEnabled(0) || JOYSTICK_IsEnabled(1)) {
+ reg_al = IO_ReadB(0x201)&0xf0;
+ CALLBACK_SCF(false);
+ } else {
+ // dos values
+ reg_ax = 0x00f0; reg_dx = 0x0201;
+ CALLBACK_SCF(true);
+ }
+ } else if (reg_dx == 0x0001) {
+ if (JOYSTICK_IsEnabled(0)) {
+ reg_ax = (Bit16u)(JOYSTICK_GetMove_X(0)*127+128);
+ reg_bx = (Bit16u)(JOYSTICK_GetMove_Y(0)*127+128);
+ if(JOYSTICK_IsEnabled(1)) {
+ reg_cx = (Bit16u)(JOYSTICK_GetMove_X(1)*127+128);
+ reg_dx = (Bit16u)(JOYSTICK_GetMove_Y(1)*127+128);
+ }
+ else {
+ reg_cx = reg_dx = 0;
+ }
+ CALLBACK_SCF(false);
+ } else if (JOYSTICK_IsEnabled(1)) {
+ reg_ax = reg_bx = 0;
+ reg_cx = (Bit16u)(JOYSTICK_GetMove_X(1)*127+128);
+ reg_dx = (Bit16u)(JOYSTICK_GetMove_Y(1)*127+128);
+ CALLBACK_SCF(false);
+ } else {
+ reg_ax = reg_bx = reg_cx = reg_dx = 0;
+ CALLBACK_SCF(true);
+ }
+ } else {
+ LOG(LOG_BIOS,LOG_ERROR)("INT15:84:Unknown Bios Joystick functionality.");
+ }
+ break;
+ case 0x86: /* BIOS - WAIT (AT,PS) */
+ {
+ if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) {
+ reg_ah=0x83;
+ CALLBACK_SCF(true);
+ break;
+ }
+ Bit32u count=(reg_cx<<16)|reg_dx;
+ mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(0,BIOS_WAIT_FLAG_TEMP));
+ mem_writed(BIOS_WAIT_FLAG_COUNT,count);
+ mem_writeb(BIOS_WAIT_FLAG_ACTIVE,1);
+ /* Reprogram RTC to start */
+ IO_Write(0x70,0xb);
+ IO_Write(0x71,IO_Read(0x71)|0x40);
+ while (mem_readd(BIOS_WAIT_FLAG_COUNT)) {
+ CALLBACK_Idle();
+ }
+ CALLBACK_SCF(false);
+ break;
+ }
+ case 0x87: /* Copy extended memory */
+ {
+ bool enabled = MEM_A20_Enabled();
+ MEM_A20_Enable(true);
+ Bitu bytes = reg_cx * 2;
+ PhysPt data = SegPhys(es)+reg_si;
+ PhysPt source = (mem_readd(data+0x12) & 0x00FFFFFF) + (mem_readb(data+0x16)<<24);
+ PhysPt dest = (mem_readd(data+0x1A) & 0x00FFFFFF) + (mem_readb(data+0x1E)<<24);
+ MEM_BlockCopy(dest,source,bytes);
+ reg_ax = 0x00;
+ MEM_A20_Enable(enabled);
+ CALLBACK_SCF(false);
+ break;
+ }
+ case 0x88: /* SYSTEM - GET EXTENDED MEMORY SIZE (286+) */
+ reg_ax=other_memsystems?0:size_extended;
+ LOG(LOG_BIOS,LOG_NORMAL)("INT15:Function 0x88 Remaining %04X kb",reg_ax);
+ CALLBACK_SCF(false);
+ break;
+ case 0x89: /* SYSTEM - SWITCH TO PROTECTED MODE */
+ {
+ IO_Write(0x20,0x10);IO_Write(0x21,reg_bh);IO_Write(0x21,0);
+ IO_Write(0xA0,0x10);IO_Write(0xA1,reg_bl);IO_Write(0xA1,0);
+ MEM_A20_Enable(true);
+ PhysPt table=SegPhys(es)+reg_si;
+ CPU_LGDT(mem_readw(table+0x8),mem_readd(table+0x8+0x2) & 0xFFFFFF);
+ CPU_LIDT(mem_readw(table+0x10),mem_readd(table+0x10+0x2) & 0xFFFFFF);
+ CPU_SET_CRX(0,CPU_GET_CRX(0)|1);
+ CPU_SetSegGeneral(ds,0x18);
+ CPU_SetSegGeneral(es,0x20);
+ CPU_SetSegGeneral(ss,0x28);
+ reg_sp+=6; //Clear stack of interrupt frame
+ CPU_SetFlags(0,FMASK_ALL);
+ reg_ax=0;
+ CPU_JMP(false,0x30,reg_cx,0);
+ }
+ break;
+ case 0x90: /* OS HOOK - DEVICE BUSY */
+ CALLBACK_SCF(false);
+ reg_ah=0;
+ break;
+ case 0x91: /* OS HOOK - DEVICE POST */
+ CALLBACK_SCF(false);
+ reg_ah=0;
+ break;
+ case 0xc2: /* BIOS PS2 Pointing Device Support */
+ switch (reg_al) {
+ case 0x00: // enable/disable
+ if (reg_bh==0) { // disable
+ Mouse_SetPS2State(false);
+ reg_ah=0;
+ CALLBACK_SCF(false);
+ } else if (reg_bh==0x01) { //enable
+ if (!Mouse_SetPS2State(true)) {
+ reg_ah=5;
+ CALLBACK_SCF(true);
+ break;
+ }
+ reg_ah=0;
+ CALLBACK_SCF(false);
+ } else {
+ CALLBACK_SCF(true);
+ reg_ah=1;
+ }
+ break;
+ case 0x01: // reset
+ reg_bx=0x00aa; // mouse
+ // fall through
+ case 0x05: // initialize
+ if ((reg_al==0x05) && (reg_bh!=0x03)) {
+ // non-standard data packet sizes not supported
+ CALLBACK_SCF(true);
+ reg_ah=2;
+ break;
+ }
+ Mouse_SetPS2State(false);
+ CALLBACK_SCF(false);
+ reg_ah=0;
+ break;
+ case 0x02: // set sampling rate
+ case 0x03: // set resolution
+ CALLBACK_SCF(false);
+ reg_ah=0;
+ break;
+ case 0x04: // get type
+ reg_bh=0; // ID
+ CALLBACK_SCF(false);
+ reg_ah=0;
+ break;
+ case 0x06: // extended commands
+ if ((reg_bh==0x01) || (reg_bh==0x02)) {
+ CALLBACK_SCF(false);
+ reg_ah=0;
+ } else {
+ CALLBACK_SCF(true);
+ reg_ah=1;
+ }
+ break;
+ case 0x07: // set callback
+ Mouse_ChangePS2Callback(SegValue(es),reg_bx);
+ CALLBACK_SCF(false);
+ reg_ah=0;
+ break;
+ default:
+ CALLBACK_SCF(true);
+ reg_ah=1;
+ break;
+ }
+ break;
+ case 0xc3: /* set carry flag so BorlandRTM doesn't assume a VECTRA/PS2 */
+ reg_ah=0x86;
+ CALLBACK_SCF(true);
+ break;
+ case 0xc4: /* BIOS POS Programm option Select */
+ LOG(LOG_BIOS,LOG_NORMAL)("INT15:Function %X called, bios mouse not supported",reg_ah);
+ CALLBACK_SCF(true);
+ break;
+ default:
+ LOG(LOG_BIOS,LOG_ERROR)("INT15:Unknown call %4X",reg_ax);
+ reg_ah=0x86;
+ CALLBACK_SCF(true);
+ if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA)) {
+ /* relict from comparisons, as int15 exits with a retf2 instead of an iret */
+ CALLBACK_SZF(false);
+ }
+ }
+ return CBRET_NONE;
+}
+
+static Bitu Default_IRQ_Handler(void) {
+ IO_WriteB(0x20,0x0b);
+ Bit8u master_isr=IO_ReadB(0x20);
+ if (master_isr) {
+ IO_WriteB(0xa0,0x0b);
+ Bit8u slave_isr=IO_ReadB(0xa0);
+ if (slave_isr) {
+ IO_WriteB(0xa1,IO_ReadB(0xa1)|slave_isr);
+ IO_WriteB(0xa0,0x20);
+ } else IO_WriteB(0x21,IO_ReadB(0x21)|(master_isr&~4));
+ IO_WriteB(0x20,0x20);
+#if C_DEBUG
+ Bit16u irq=0,isr=master_isr;
+ if (slave_isr) isr=slave_isr<<8;
+ while (isr>>=1) irq++;
+ LOG(LOG_BIOS,LOG_WARN)("Unexpected IRQ %u",irq);
+#endif
+ } else master_isr=0xff;
+ mem_writeb(BIOS_LAST_UNEXPECTED_IRQ,master_isr);
+ return CBRET_NONE;
+}
+
+static Bitu Reboot_Handler(void) {
+ // switch to text mode, notify user (let's hope INT10 still works)
+ const char* const text = "\n\n Reboot requested, quitting now.";
+ reg_ax = 0;
+ CALLBACK_RunRealInt(0x10);
+ reg_ah = 0xe;
+ reg_bx = 0;
+ for(Bitu i = 0; i < strlen(text);i++) {
+ reg_al = text[i];
+ CALLBACK_RunRealInt(0x10);
+ }
+ LOG_MSG(text);
+ double start = PIC_FullIndex();
+ while((PIC_FullIndex()-start)<3000) CALLBACK_Idle();
+ throw 1;
+ return CBRET_NONE;
+}
+
+void BIOS_ZeroExtendedSize(bool in) {
+ if(in) other_memsystems++;
+ else other_memsystems--;
+ if(other_memsystems < 0) other_memsystems=0;
+}
+
+void BIOS_SetupKeyboard(void);
+void BIOS_SetupDisks(void);
+
+class BIOS:public Module_base{
+private:
+ CALLBACK_HandlerObject callback[11];
+public:
+ BIOS(Section* configuration):Module_base(configuration){
+ /* tandy DAC can be requested in tandy_sound.cpp by initializing this field */
+ bool use_tandyDAC=(real_readb(0x40,0xd4)==0xff);
+
+ /* Clear the Bios Data Area (0x400-0x5ff, 0x600- is accounted to DOS) */
+ for (Bit16u i=0;i<0x200;i++) real_writeb(0x40,i,0);
+
+ /* Setup all the interrupt handlers the bios controls */
+
+ /* INT 8 Clock IRQ Handler */
+ Bitu call_irq0=CALLBACK_Allocate();
+ CALLBACK_Setup(call_irq0,INT8_Handler,CB_IRQ0,Real2Phys(BIOS_DEFAULT_IRQ0_LOCATION),"IRQ 0 Clock");
+ RealSetVec(0x08,BIOS_DEFAULT_IRQ0_LOCATION);
+ // pseudocode for CB_IRQ0:
+ // sti
+ // callback INT8_Handler
+ // push ds,ax,dx
+ // int 0x1c
+ // cli
+ // mov al, 0x20
+ // out 0x20, al
+ // pop dx,ax,ds
+ // iret
+
+ mem_writed(BIOS_TIMER,0); //Calculate the correct time
+
+ /* INT 11 Get equipment list */
+ callback[1].Install(&INT11_Handler,CB_IRET,"Int 11 Equipment");
+ callback[1].Set_RealVec(0x11);
+
+ /* INT 12 Memory Size default at 640 kb */
+ callback[2].Install(&INT12_Handler,CB_IRET,"Int 12 Memory");
+ callback[2].Set_RealVec(0x12);
+ if (IS_TANDY_ARCH) {
+ /* reduce reported memory size for the Tandy (32k graphics memory
+ at the end of the conventional 640k) */
+ if (machine==MCH_TANDY) mem_writew(BIOS_MEMORY_SIZE,624);
+ else mem_writew(BIOS_MEMORY_SIZE,640);
+ mem_writew(BIOS_TRUE_MEMORY_SIZE,640);
+ } else mem_writew(BIOS_MEMORY_SIZE,640);
+
+ /* INT 13 Bios Disk Support */
+ BIOS_SetupDisks();
+
+ /* INT 14 Serial Ports */
+ callback[3].Install(&INT14_Handler,CB_IRET_STI,"Int 14 COM-port");
+ callback[3].Set_RealVec(0x14);
+
+ /* INT 15 Misc Calls */
+ callback[4].Install(&INT15_Handler,CB_IRET,"Int 15 Bios");
+ callback[4].Set_RealVec(0x15);
+
+ /* INT 16 Keyboard handled in another file */
+ BIOS_SetupKeyboard();
+
+ /* INT 17 Printer Routines */
+ callback[5].Install(&INT17_Handler,CB_IRET_STI,"Int 17 Printer");
+ callback[5].Set_RealVec(0x17);
+
+ /* INT 1A TIME and some other functions */
+ callback[6].Install(&INT1A_Handler,CB_IRET_STI,"Int 1a Time");
+ callback[6].Set_RealVec(0x1A);
+
+ /* INT 1C System Timer tick called from INT 8 */
+ callback[7].Install(&INT1C_Handler,CB_IRET,"Int 1c Timer");
+ callback[7].Set_RealVec(0x1C);
+
+ /* IRQ 8 RTC Handler */
+ callback[8].Install(&INT70_Handler,CB_IRET,"Int 70 RTC");
+ callback[8].Set_RealVec(0x70);
+
+ /* Irq 9 rerouted to irq 2 */
+ callback[9].Install(NULL,CB_IRQ9,"irq 9 bios");
+ callback[9].Set_RealVec(0x71);
+
+ /* Reboot */
+ // This handler is an exit for more than only reboots, since we
+ // don't handle these cases
+ callback[10].Install(&Reboot_Handler,CB_IRET,"reboot");
+
+ // INT 18h: Enter BASIC
+ // Non-IBM BIOS would display "NO ROM BASIC" here
+ callback[10].Set_RealVec(0x18);
+ RealPt rptr = callback[10].Get_RealPointer();
+
+ // INT 19h: Boot function
+ // This is not a complete reboot as it happens after the POST
+ // We don't handle it, so use the reboot function as exit.
+ RealSetVec(0x19,rptr);
+
+ // The farjump at the processor reset entry point (jumps to POST routine)
+ phys_writeb(0xFFFF0,0xEA); // FARJMP
+ phys_writew(0xFFFF1,RealOff(BIOS_DEFAULT_RESET_LOCATION)); // offset
+ phys_writew(0xFFFF3,RealSeg(BIOS_DEFAULT_RESET_LOCATION)); // segment
+
+ // Compatible POST routine location: jump to the callback
+ phys_writeb(Real2Phys(BIOS_DEFAULT_RESET_LOCATION)+0,0xEA); // FARJMP
+ phys_writew(Real2Phys(BIOS_DEFAULT_RESET_LOCATION)+1,RealOff(rptr)); // offset
+ phys_writew(Real2Phys(BIOS_DEFAULT_RESET_LOCATION)+3,RealSeg(rptr)); // segment
+
+ /* Irq 2 */
+ Bitu call_irq2=CALLBACK_Allocate();
+ CALLBACK_Setup(call_irq2,NULL,CB_IRET_EOI_PIC1,Real2Phys(BIOS_DEFAULT_IRQ2_LOCATION),"irq 2 bios");
+ RealSetVec(0x0a,BIOS_DEFAULT_IRQ2_LOCATION);
+
+ /* Default IRQ handler */
+ Bitu call_irq_default=CALLBACK_Allocate();
+ CALLBACK_Setup(call_irq_default,&Default_IRQ_Handler,CB_IRET,"irq default");
+ RealSetVec(0x0b,CALLBACK_RealPointer(call_irq_default)); // IRQ 3
+ RealSetVec(0x0c,CALLBACK_RealPointer(call_irq_default)); // IRQ 4
+ RealSetVec(0x0d,CALLBACK_RealPointer(call_irq_default)); // IRQ 5
+ RealSetVec(0x0f,CALLBACK_RealPointer(call_irq_default)); // IRQ 7
+ RealSetVec(0x72,CALLBACK_RealPointer(call_irq_default)); // IRQ 10
+ RealSetVec(0x73,CALLBACK_RealPointer(call_irq_default)); // IRQ 11
+
+ // INT 05h: Print Screen
+ // IRQ1 handler calls it when PrtSc key is pressed; does nothing unless hooked
+ phys_writeb(Real2Phys(BIOS_DEFAULT_INT5_LOCATION),0xcf);
+ RealSetVec(0x05,BIOS_DEFAULT_INT5_LOCATION);
+
+ /* Some hardcoded vectors */
+ phys_writeb(Real2Phys(BIOS_DEFAULT_HANDLER_LOCATION),0xcf); /* bios default interrupt vector location -> IRET */
+ phys_writew(Real2Phys(RealGetVec(0x12))+0x12,0x20); //Hack for Jurresic
+
+ if (machine==MCH_TANDY) phys_writeb(0xffffe,0xff) ; /* Tandy model */
+ else if (machine==MCH_PCJR) phys_writeb(0xffffe,0xfd); /* PCJr model */
+ else phys_writeb(0xffffe,0xfc); /* PC */
+
+ // System BIOS identification
+ const char* const b_type =
+ "IBM COMPATIBLE 486 BIOS COPYRIGHT The DOSBox Team.";
+ for(Bitu i = 0; i < strlen(b_type); i++) phys_writeb(0xfe00e + i,b_type[i]);
+
+ // System BIOS version
+ const char* const b_vers =
+ "DOSBox FakeBIOS v1.0";
+ for(Bitu i = 0; i < strlen(b_vers); i++) phys_writeb(0xfe061+i,b_vers[i]);
+
+ // write system BIOS date
+ const char* const b_date = "01/01/92";
+ for(Bitu i = 0; i < strlen(b_date); i++) phys_writeb(0xffff5+i,b_date[i]);
+ phys_writeb(0xfffff,0x55); // signature
+
+ tandy_sb.port=0;
+ tandy_dac.port=0;
+ if (use_tandyDAC) {
+ /* tandy DAC sound requested, see if soundblaster device is available */
+ Bitu tandy_dac_type = 0;
+ if (Tandy_InitializeSB()) {
+ tandy_dac_type = 1;
+ } else if (Tandy_InitializeTS()) {
+ tandy_dac_type = 2;
+ }
+ if (tandy_dac_type) {
+ real_writew(0x40,0xd0,0x0000);
+ real_writew(0x40,0xd2,0x0000);
+ real_writeb(0x40,0xd4,0xff); /* tandy DAC init value */
+ real_writed(0x40,0xd6,0x00000000);
+ /* install the DAC callback handler */
+ tandy_DAC_callback[0]=new CALLBACK_HandlerObject();
+ tandy_DAC_callback[1]=new CALLBACK_HandlerObject();
+ tandy_DAC_callback[0]->Install(&IRQ_TandyDAC,CB_IRET,"Tandy DAC IRQ");
+ tandy_DAC_callback[1]->Install(NULL,CB_TDE_IRET,"Tandy DAC end transfer");
+ // pseudocode for CB_TDE_IRET:
+ // push ax
+ // mov ax, 0x91fb
+ // int 15
+ // cli
+ // mov al, 0x20
+ // out 0x20, al
+ // pop ax
+ // iret
+
+ Bit8u tandy_irq = 7;
+ if (tandy_dac_type==1) tandy_irq = tandy_sb.irq;
+ else if (tandy_dac_type==2) tandy_irq = tandy_dac.irq;
+ Bit8u tandy_irq_vector = tandy_irq;
+ if (tandy_irq_vector<8) tandy_irq_vector += 8;
+ else tandy_irq_vector += (0x70-8);
+
+ RealPt current_irq=RealGetVec(tandy_irq_vector);
+ real_writed(0x40,0xd6,current_irq);
+ for (Bit16u i=0; i<0x10; i++) phys_writeb(PhysMake(0xf000,0xa084+i),0x80);
+ } else real_writeb(0x40,0xd4,0x00);
+ }
+
+ /* Setup some stuff in 0x40 bios segment */
+
+ // port timeouts
+ // always 1 second even if the port does not exist
+ mem_writeb(BIOS_LPT1_TIMEOUT,1);
+ mem_writeb(BIOS_LPT2_TIMEOUT,1);
+ mem_writeb(BIOS_LPT3_TIMEOUT,1);
+ mem_writeb(BIOS_COM1_TIMEOUT,1);
+ mem_writeb(BIOS_COM2_TIMEOUT,1);
+ mem_writeb(BIOS_COM3_TIMEOUT,1);
+ mem_writeb(BIOS_COM4_TIMEOUT,1);
+
+ /* detect parallel ports */
+ Bitu ppindex=0; // number of lpt ports
+ if ((IO_Read(0x378)!=0xff)|(IO_Read(0x379)!=0xff)) {
+ // this is our LPT1
+ mem_writew(BIOS_ADDRESS_LPT1,0x378);
+ ppindex++;
+ if((IO_Read(0x278)!=0xff)|(IO_Read(0x279)!=0xff)) {
+ // this is our LPT2
+ mem_writew(BIOS_ADDRESS_LPT2,0x278);
+ ppindex++;
+ if((IO_Read(0x3bc)!=0xff)|(IO_Read(0x3be)!=0xff)) {
+ // this is our LPT3
+ mem_writew(BIOS_ADDRESS_LPT3,0x3bc);
+ ppindex++;
+ }
+ } else if((IO_Read(0x3bc)!=0xff)|(IO_Read(0x3be)!=0xff)) {
+ // this is our LPT2
+ mem_writew(BIOS_ADDRESS_LPT2,0x3bc);
+ ppindex++;
+ }
+ } else if((IO_Read(0x3bc)!=0xff)|(IO_Read(0x3be)!=0xff)) {
+ // this is our LPT1
+ mem_writew(BIOS_ADDRESS_LPT1,0x3bc);
+ ppindex++;
+ if((IO_Read(0x278)!=0xff)|(IO_Read(0x279)!=0xff)) {
+ // this is our LPT2
+ mem_writew(BIOS_ADDRESS_LPT2,0x278);
+ ppindex++;
+ }
+ } else if((IO_Read(0x278)!=0xff)|(IO_Read(0x279)!=0xff)) {
+ // this is our LPT1
+ mem_writew(BIOS_ADDRESS_LPT1,0x278);
+ ppindex++;
+ }
+
+ /* Setup equipment list */
+ // look http://www.bioscentral.com/misc/bda.htm
+
+ //Bit16u config=0x4400; //1 Floppy, 2 serial and 1 parallel
+ Bit16u config = 0x0;
+
+ // set number of parallel ports
+ // if(ppindex == 0) config |= 0x8000; // looks like 0 ports are not specified
+ //else if(ppindex == 1) config |= 0x0000;
+ if(ppindex == 2) config |= 0x4000;
+ else config |= 0xc000; // 3 ports
+#if (C_FPU)
+ //FPU
+ config|=0x2;
+#endif
+ switch (machine) {
+ case MCH_HERC:
+ //Startup monochrome
+ config|=0x30;
+ break;
+ case EGAVGA_ARCH_CASE:
+ case MCH_CGA:
+ case TANDY_ARCH_CASE:
+ //Startup 80x25 color
+ config|=0x20;
+ break;
+ default:
+ //EGA VGA
+ config|=0;
+ break;
+ }
+ // PS2 mouse
+ config |= 0x04;
+ // DMA *not* supported - Ancient Art of War CGA uses this to identify PCjr
+ if (machine==MCH_PCJR) config |= 0x100;
+ // Gameport
+ config |= 0x1000;
+ mem_writew(BIOS_CONFIGURATION,config);
+ if (IS_EGAVGA_ARCH) config &= ~0x30; //EGA/VGA startup display mode differs in CMOS
+ CMOS_SetRegister(0x14,(Bit8u)(config&0xff)); //Should be updated on changes
+ /* Setup extended memory size */
+ IO_Write(0x70,0x30);
+ size_extended=IO_Read(0x71);
+ IO_Write(0x70,0x31);
+ size_extended|=(IO_Read(0x71) << 8);
+ BIOS_HostTimeSync();
+ }
+ ~BIOS(){
+ /* abort DAC playing */
+ if (tandy_sb.port) {
+ IO_Write(tandy_sb.port+0xc,0xd3);
+ IO_Write(tandy_sb.port+0xc,0xd0);
+ }
+ real_writeb(0x40,0xd4,0x00);
+ if (tandy_DAC_callback[0]) {
+ Bit32u orig_vector=real_readd(0x40,0xd6);
+ if (orig_vector==tandy_DAC_callback[0]->Get_RealPointer()) {
+ /* set IRQ vector to old value */
+ Bit8u tandy_irq = 7;
+ if (tandy_sb.port) tandy_irq = tandy_sb.irq;
+ else if (tandy_dac.port) tandy_irq = tandy_dac.irq;
+ Bit8u tandy_irq_vector = tandy_irq;
+ if (tandy_irq_vector<8) tandy_irq_vector += 8;
+ else tandy_irq_vector += (0x70-8);
+
+ RealSetVec(tandy_irq_vector,real_readd(0x40,0xd6));
+ real_writed(0x40,0xd6,0x00000000);
+ }
+ delete tandy_DAC_callback[0];
+ delete tandy_DAC_callback[1];
+ tandy_DAC_callback[0]=NULL;
+ tandy_DAC_callback[1]=NULL;
+ }
+ }
+};
+
+// set com port data in bios data area
+// parameter: array of 4 com port base addresses, 0 = none
+void BIOS_SetComPorts(Bit16u baseaddr[]) {
+ Bit16u portcount=0;
+ Bit16u equipmentword;
+ for(Bitu i = 0; i < 4; i++) {
+ if(baseaddr[i]!=0) portcount++;
+ if(i==0) mem_writew(BIOS_BASE_ADDRESS_COM1,baseaddr[i]);
+ else if(i==1) mem_writew(BIOS_BASE_ADDRESS_COM2,baseaddr[i]);
+ else if(i==2) mem_writew(BIOS_BASE_ADDRESS_COM3,baseaddr[i]);
+ else mem_writew(BIOS_BASE_ADDRESS_COM4,baseaddr[i]);
+ }
+ // set equipment word
+ equipmentword = mem_readw(BIOS_CONFIGURATION);
+ equipmentword &= (~0x0E00);
+ equipmentword |= (portcount << 9);
+ mem_writew(BIOS_CONFIGURATION,equipmentword);
+ if (IS_EGAVGA_ARCH) equipmentword &= ~0x30; //EGA/VGA startup display mode differs in CMOS
+ CMOS_SetRegister(0x14,(Bit8u)(equipmentword&0xff)); //Should be updated on changes
+}
+
+
+static BIOS* test;
+
+void BIOS_Destroy(Section* /*sec*/){
+ delete test;
+}
+
+void BIOS_Init(Section* sec) {
+ test = new BIOS(sec);
+ sec->AddDestroyFunction(&BIOS_Destroy,false);
+}
diff --git a/src/ints/bios_disk.cpp b/src/ints/bios_disk.cpp
new file mode 100644
index 000000000..7edc771c2
--- /dev/null
+++ b/src/ints/bios_disk.cpp
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "callback.h"
+#include "bios.h"
+#include "bios_disk.h"
+#include "regs.h"
+#include "mem.h"
+#include "dos_inc.h" /* for Drives[] */
+#include "../dos/drives.h"
+#include "mapper.h"
+
+
+
+diskGeo DiskGeometryList[] = {
+ { 160, 8, 1, 40, 0},
+ { 180, 9, 1, 40, 0},
+ { 200, 10, 1, 40, 0},
+ { 320, 8, 2, 40, 1},
+ { 360, 9, 2, 40, 1},
+ { 400, 10, 2, 40, 1},
+ { 720, 9, 2, 80, 3},
+ {1200, 15, 2, 80, 2},
+ {1440, 18, 2, 80, 4},
+ {2880, 36, 2, 80, 6},
+ {0, 0, 0, 0, 0}
+};
+
+Bitu call_int13;
+Bitu diskparm0, diskparm1;
+static Bit8u last_status;
+static Bit8u last_drive;
+Bit16u imgDTASeg;
+RealPt imgDTAPtr;
+DOS_DTA *imgDTA;
+bool killRead;
+static bool swapping_requested;
+
+void CMOS_SetRegister(Bitu regNr, Bit8u val); //For setting equipment word
+
+/* 2 floppys and 2 harddrives, max */
+imageDisk *imageDiskList[MAX_DISK_IMAGES];
+imageDisk *diskSwap[MAX_SWAPPABLE_DISKS];
+Bit32s swapPosition;
+
+void updateDPT(void) {
+ Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
+ if(imageDiskList[2] != NULL) {
+ PhysPt dp0physaddr=CALLBACK_PhysPointer(diskparm0);
+ imageDiskList[2]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
+ phys_writew(dp0physaddr,(Bit16u)tmpcyl);
+ phys_writeb(dp0physaddr+0x2,(Bit8u)tmpheads);
+ phys_writew(dp0physaddr+0x3,0);
+ phys_writew(dp0physaddr+0x5,(Bit16u)-1);
+ phys_writeb(dp0physaddr+0x7,0);
+ phys_writeb(dp0physaddr+0x8,(0xc0 | (((imageDiskList[2]->heads) > 8) << 3)));
+ phys_writeb(dp0physaddr+0x9,0);
+ phys_writeb(dp0physaddr+0xa,0);
+ phys_writeb(dp0physaddr+0xb,0);
+ phys_writew(dp0physaddr+0xc,(Bit16u)tmpcyl);
+ phys_writeb(dp0physaddr+0xe,(Bit8u)tmpsect);
+ }
+ if(imageDiskList[3] != NULL) {
+ PhysPt dp1physaddr=CALLBACK_PhysPointer(diskparm1);
+ imageDiskList[3]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
+ phys_writew(dp1physaddr,(Bit16u)tmpcyl);
+ phys_writeb(dp1physaddr+0x2,(Bit8u)tmpheads);
+ phys_writeb(dp1physaddr+0xe,(Bit8u)tmpsect);
+ }
+}
+
+void incrementFDD(void) {
+ Bit16u equipment=mem_readw(BIOS_CONFIGURATION);
+ if(equipment&1) {
+ Bitu numofdisks = (equipment>>6)&3;
+ numofdisks++;
+ if(numofdisks > 1) numofdisks=1;//max 2 floppies at the moment
+ equipment&=~0x00C0;
+ equipment|=(numofdisks<<6);
+ } else equipment|=1;
+ mem_writew(BIOS_CONFIGURATION,equipment);
+ CMOS_SetRegister(0x14, (Bit8u)(equipment&0xff));
+}
+
+void swapInDisks(void) {
+ bool allNull = true;
+ Bit32s diskcount = 0;
+ Bit32s swapPos = swapPosition;
+ Bit32s i;
+
+ /* Check to make sure that there is at least one setup image */
+ for(i=0;i<MAX_SWAPPABLE_DISKS;i++) {
+ if(diskSwap[i]!=NULL) {
+ allNull = false;
+ break;
+ }
+ }
+
+ /* No disks setup... fail */
+ if (allNull) return;
+
+ /* If only one disk is loaded, this loop will load the same disk in dive A and drive B */
+ while(diskcount<2) {
+ if(diskSwap[swapPos] != NULL) {
+ LOG_MSG("Loaded disk %d from swaplist position %d - \"%s\"", diskcount, swapPos, diskSwap[swapPos]->diskname);
+ imageDiskList[diskcount] = diskSwap[swapPos];
+ diskcount++;
+ }
+ swapPos++;
+ if(swapPos>=MAX_SWAPPABLE_DISKS) swapPos=0;
+ }
+}
+
+bool getSwapRequest(void) {
+ bool sreq=swapping_requested;
+ swapping_requested = false;
+ return sreq;
+}
+
+void swapInNextDisk(bool pressed) {
+ if (!pressed)
+ return;
+ DriveManager::CycleAllDisks();
+ /* Hack/feature: rescan all disks as well */
+ LOG_MSG("Diskcaching reset for normal mounted drives.");
+ for(Bitu i=0;i<DOS_DRIVES;i++) {
+ if (Drives[i]) Drives[i]->EmptyCache();
+ }
+ swapPosition++;
+ if(diskSwap[swapPosition] == NULL) swapPosition = 0;
+ swapInDisks();
+ swapping_requested = true;
+}
+
+
+Bit8u imageDisk::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data) {
+ Bit32u sectnum;
+
+ sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L;
+
+ return Read_AbsoluteSector(sectnum, data);
+}
+
+Bit8u imageDisk::Read_AbsoluteSector(Bit32u sectnum, void * data) {
+ Bit32u bytenum;
+
+ bytenum = sectnum * sector_size;
+
+ if (last_action==WRITE || bytenum!=current_fpos) fseek(diskimg,bytenum,SEEK_SET);
+ size_t ret=fread(data, 1, sector_size, diskimg);
+ current_fpos=bytenum+ret;
+ last_action=READ;
+
+ return 0x00;
+}
+
+Bit8u imageDisk::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data) {
+ Bit32u sectnum;
+
+ sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L;
+
+ return Write_AbsoluteSector(sectnum, data);
+}
+
+
+Bit8u imageDisk::Write_AbsoluteSector(Bit32u sectnum, void *data) {
+ Bit32u bytenum;
+
+ bytenum = sectnum * sector_size;
+
+ //LOG_MSG("Writing sectors to %ld at bytenum %d", sectnum, bytenum);
+
+ if (last_action==READ || bytenum!=current_fpos) fseek(diskimg,bytenum,SEEK_SET);
+ size_t ret=fwrite(data, 1, sector_size, diskimg);
+ current_fpos=bytenum+ret;
+ last_action=WRITE;
+
+ return ((ret>0)?0x00:0x05);
+
+}
+
+imageDisk::imageDisk(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) {
+ heads = 0;
+ cylinders = 0;
+ sectors = 0;
+ sector_size = 512;
+ current_fpos = 0;
+ last_action = NONE;
+ diskimg = imgFile;
+ fseek(diskimg,0,SEEK_SET);
+
+ memset(diskname,0,512);
+ if(strlen((const char *)imgName) > 511) {
+ memcpy(diskname, imgName, 511);
+ } else {
+ strcpy((char *)diskname, (const char *)imgName);
+ }
+
+ active = false;
+ hardDrive = isHardDisk;
+ if(!isHardDisk) {
+ Bit8u i=0;
+ bool founddisk = false;
+ while (DiskGeometryList[i].ksize!=0x0) {
+ if ((DiskGeometryList[i].ksize==imgSizeK) ||
+ (DiskGeometryList[i].ksize+1==imgSizeK)) {
+ if (DiskGeometryList[i].ksize!=imgSizeK)
+ LOG_MSG("ImageLoader: image file with additional data, might not load!");
+ founddisk = true;
+ active = true;
+ floppytype = i;
+ heads = DiskGeometryList[i].headscyl;
+ cylinders = DiskGeometryList[i].cylcount;
+ sectors = DiskGeometryList[i].secttrack;
+ break;
+ }
+ i++;
+ }
+ if(!founddisk) {
+ active = false;
+ } else {
+ incrementFDD();
+ }
+ }
+}
+
+void imageDisk::Set_Geometry(Bit32u setHeads, Bit32u setCyl, Bit32u setSect, Bit32u setSectSize) {
+ heads = setHeads;
+ cylinders = setCyl;
+ sectors = setSect;
+ sector_size = setSectSize;
+ active = true;
+}
+
+void imageDisk::Get_Geometry(Bit32u * getHeads, Bit32u *getCyl, Bit32u *getSect, Bit32u *getSectSize) {
+ *getHeads = heads;
+ *getCyl = cylinders;
+ *getSect = sectors;
+ *getSectSize = sector_size;
+}
+
+Bit8u imageDisk::GetBiosType(void) {
+ if(!hardDrive) {
+ return (Bit8u)DiskGeometryList[floppytype].biosval;
+ } else return 0;
+}
+
+Bit32u imageDisk::getSectSize(void) {
+ return sector_size;
+}
+
+static Bit8u GetDosDriveNumber(Bit8u biosNum) {
+ switch(biosNum) {
+ case 0x0:
+ return 0x0;
+ case 0x1:
+ return 0x1;
+ case 0x80:
+ return 0x2;
+ case 0x81:
+ return 0x3;
+ case 0x82:
+ return 0x4;
+ case 0x83:
+ return 0x5;
+ default:
+ return 0x7f;
+ }
+}
+
+static bool driveInactive(Bit8u driveNum) {
+ if(driveNum>=(2 + MAX_HDD_IMAGES)) {
+ LOG(LOG_BIOS,LOG_ERROR)("Disk %d non-existant", driveNum);
+ last_status = 0x01;
+ CALLBACK_SCF(true);
+ return true;
+ }
+ if(imageDiskList[driveNum] == NULL) {
+ LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", driveNum);
+ last_status = 0x01;
+ CALLBACK_SCF(true);
+ return true;
+ }
+ if(!imageDiskList[driveNum]->active) {
+ LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", driveNum);
+ last_status = 0x01;
+ CALLBACK_SCF(true);
+ return true;
+ }
+ return false;
+}
+
+
+static Bitu INT13_DiskHandler(void) {
+ Bit16u segat, bufptr;
+ Bit8u sectbuf[512];
+ Bit8u drivenum;
+ Bitu i,t;
+ last_drive = reg_dl;
+ drivenum = GetDosDriveNumber(reg_dl);
+ bool any_images = false;
+ for(i = 0;i < MAX_DISK_IMAGES;i++) {
+ if(imageDiskList[i]) any_images=true;
+ }
+
+ // unconditionally enable the interrupt flag
+ CALLBACK_SIF(true);
+
+ //drivenum = 0;
+ //LOG_MSG("INT13: Function %x called on drive %x (dos drive %d)", reg_ah, reg_dl, drivenum);
+
+ // NOTE: the 0xff error code returned in some cases is questionable; 0x01 seems more correct
+ switch(reg_ah) {
+ case 0x0: /* Reset disk */
+ {
+ /* if there aren't any diskimages (so only localdrives and virtual drives)
+ * always succeed on reset disk. If there are diskimages then and only then
+ * do real checks
+ */
+ if (any_images && driveInactive(drivenum)) {
+ /* driveInactive sets carry flag if the specified drive is not available */
+ if ((machine==MCH_CGA) || (machine==MCH_PCJR)) {
+ /* those bioses call floppy drive reset for invalid drive values */
+ if (((imageDiskList[0]) && (imageDiskList[0]->active)) || ((imageDiskList[1]) && (imageDiskList[1]->active))) {
+ if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++;
+ last_status = 0x00;
+ CALLBACK_SCF(false);
+ }
+ }
+ return CBRET_NONE;
+ }
+ if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++;
+ last_status = 0x00;
+ CALLBACK_SCF(false);
+ }
+ break;
+ case 0x1: /* Get status of last operation */
+
+ if(last_status != 0x00) {
+ reg_ah = last_status;
+ CALLBACK_SCF(true);
+ } else {
+ reg_ah = 0x00;
+ CALLBACK_SCF(false);
+ }
+ break;
+ case 0x2: /* Read sectors */
+ if (reg_al==0) {
+ reg_ah = 0x01;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ if (!any_images) {
+ if (drivenum >= DOS_DRIVES || !Drives[drivenum] || Drives[drivenum]->isRemovable()) {
+ reg_ah = 0x01;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ // Inherit the Earth cdrom and Amberstar use it as a disk test
+ if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) {
+ if (reg_ch==0) {
+ PhysPt ptr = PhysMake(SegValue(es),reg_bx);
+ // write some MBR data into buffer for Amberstar installer
+ mem_writeb(ptr+0x1be,0x80); // first partition is active
+ mem_writeb(ptr+0x1c2,0x06); // first partition is FAT16B
+ }
+ reg_ah = 0;
+ CALLBACK_SCF(false);
+ return CBRET_NONE;
+ }
+ }
+ if (driveInactive(drivenum)) {
+ reg_ah = 0xff;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+
+ segat = SegValue(es);
+ bufptr = reg_bx;
+ for(i=0;i<reg_al;i++) {
+ last_status = imageDiskList[drivenum]->Read_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf);
+ if((last_status != 0x00) || (killRead)) {
+ LOG_MSG("Error in disk read");
+ killRead = false;
+ reg_ah = 0x04;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ for(t=0;t<512;t++) {
+ real_writeb(segat,bufptr,sectbuf[t]);
+ bufptr++;
+ }
+ }
+ reg_ah = 0x00;
+ CALLBACK_SCF(false);
+ break;
+ case 0x3: /* Write sectors */
+
+ if(driveInactive(drivenum)) {
+ reg_ah = 0xff;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+
+
+ bufptr = reg_bx;
+ for(i=0;i<reg_al;i++) {
+ for(t=0;t<imageDiskList[drivenum]->getSectSize();t++) {
+ sectbuf[t] = real_readb(SegValue(es),bufptr);
+ bufptr++;
+ }
+
+ last_status = imageDiskList[drivenum]->Write_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0) << 2)), (Bit32u)((reg_cl & 63) + i), &sectbuf[0]);
+ if(last_status != 0x00) {
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ }
+ reg_ah = 0x00;
+ CALLBACK_SCF(false);
+ break;
+ case 0x04: /* Verify sectors */
+ if (reg_al==0) {
+ reg_ah = 0x01;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ if(driveInactive(drivenum)) {
+ reg_ah = last_status;
+ return CBRET_NONE;
+ }
+
+ /* TODO: Finish coding this section */
+ /*
+ segat = SegValue(es);
+ bufptr = reg_bx;
+ for(i=0;i<reg_al;i++) {
+ last_status = imageDiskList[drivenum]->Read_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf);
+ if(last_status != 0x00) {
+ LOG_MSG("Error in disk read");
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ for(t=0;t<512;t++) {
+ real_writeb(segat,bufptr,sectbuf[t]);
+ bufptr++;
+ }
+ }*/
+ reg_ah = 0x00;
+ //Qbix: The following codes don't match my specs. al should be number of sector verified
+ //reg_al = 0x10; /* CRC verify failed */
+ //reg_al = 0x00; /* CRC verify succeeded */
+ CALLBACK_SCF(false);
+
+ break;
+ case 0x05: /* Format track */
+ if (driveInactive(drivenum)) {
+ reg_ah = 0xff;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ reg_ah = 0x00;
+ CALLBACK_SCF(false);
+ break;
+ case 0x08: /* Get drive parameters */
+ if(driveInactive(drivenum)) {
+ last_status = 0x07;
+ reg_ah = last_status;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ reg_ax = 0x00;
+ reg_bl = imageDiskList[drivenum]->GetBiosType();
+ Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
+ imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
+ if (tmpcyl==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: cylinder count zero!");
+ else tmpcyl--; // cylinder count -> max cylinder
+ if (tmpheads==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: head count zero!");
+ else tmpheads--; // head count -> max head
+ reg_ch = (Bit8u)(tmpcyl & 0xff);
+ reg_cl = (Bit8u)(((tmpcyl >> 2) & 0xc0) | (tmpsect & 0x3f));
+ reg_dh = (Bit8u)tmpheads;
+ last_status = 0x00;
+ if (reg_dl&0x80) { // harddisks
+ reg_dl = 0;
+ if(imageDiskList[2] != NULL) reg_dl++;
+ if(imageDiskList[3] != NULL) reg_dl++;
+ } else { // floppy disks
+ reg_dl = 0;
+ if(imageDiskList[0] != NULL) reg_dl++;
+ if(imageDiskList[1] != NULL) reg_dl++;
+ }
+ CALLBACK_SCF(false);
+ break;
+ case 0x11: /* Recalibrate drive */
+ reg_ah = 0x00;
+ CALLBACK_SCF(false);
+ break;
+ case 0x15: /* Get disk type */
+ /* Korean Powerdolls uses this to detect harddrives */
+ LOG(LOG_BIOS,LOG_WARN)("INT13: Get disktype used!");
+ if (any_images) {
+ if(driveInactive(drivenum)) {
+ last_status = 0x07;
+ reg_ah = last_status;
+ CALLBACK_SCF(true);
+ return CBRET_NONE;
+ }
+ Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
+ imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
+ Bit64u largesize = tmpheads*tmpcyl*tmpsect*tmpsize;
+ largesize/=512;
+ Bit32u ts = static_cast<Bit32u>(largesize);
+ reg_ah = (drivenum <2)?1:3; //With 2 for floppy MSDOS starts calling int 13 ah 16
+ if(reg_ah == 3) {
+ reg_cx = static_cast<Bit16u>(ts >>16);
+ reg_dx = static_cast<Bit16u>(ts & 0xffff);
+ }
+ CALLBACK_SCF(false);
+ } else {
+ if (drivenum <DOS_DRIVES && (Drives[drivenum] != 0 || drivenum <2)) {
+ if (drivenum <2) {
+ //TODO use actual size (using 1.44 for now).
+ reg_ah = 0x1; // type
+// reg_cx = 0;
+// reg_dx = 2880; //Only set size for harddrives.
+ } else {
+ //TODO use actual size (using 105 mb for now).
+ reg_ah = 0x3; // type
+ reg_cx = 3;
+ reg_dx = 0x4800;
+ }
+ CALLBACK_SCF(false);
+ } else {
+ LOG(LOG_BIOS,LOG_WARN)("INT13: no images, but invalid drive for call 15");
+ reg_ah=0xff;
+ CALLBACK_SCF(true);
+ }
+ }
+ break;
+ case 0x17: /* Set disk type for format */
+ /* Pirates! needs this to load */
+ killRead = true;
+ reg_ah = 0x00;
+ CALLBACK_SCF(false);
+ break;
+ default:
+ LOG(LOG_BIOS,LOG_ERROR)("INT13: Function %x called on drive %x (dos drive %d)", reg_ah, reg_dl, drivenum);
+ reg_ah=0xff;
+ CALLBACK_SCF(true);
+ }
+ return CBRET_NONE;
+}
+
+
+void BIOS_SetupDisks(void) {
+/* TODO Start the time correctly */
+ call_int13=CALLBACK_Allocate();
+ CALLBACK_Setup(call_int13,&INT13_DiskHandler,CB_INT13,"Int 13 Bios disk");
+ RealSetVec(0x13,CALLBACK_RealPointer(call_int13));
+ int i;
+ for(i=0;i<4;i++) {
+ imageDiskList[i] = NULL;
+ }
+
+ for(i=0;i<MAX_SWAPPABLE_DISKS;i++) {
+ diskSwap[i] = NULL;
+ }
+
+ diskparm0 = CALLBACK_Allocate();
+ diskparm1 = CALLBACK_Allocate();
+ swapPosition = 0;
+
+ RealSetVec(0x41,CALLBACK_RealPointer(diskparm0));
+ RealSetVec(0x46,CALLBACK_RealPointer(diskparm1));
+
+ PhysPt dp0physaddr=CALLBACK_PhysPointer(diskparm0);
+ PhysPt dp1physaddr=CALLBACK_PhysPointer(diskparm1);
+ for(i=0;i<16;i++) {
+ phys_writeb(dp0physaddr+i,0);
+ phys_writeb(dp1physaddr+i,0);
+ }
+
+ imgDTASeg = 0;
+
+/* Setup the Bios Area */
+ mem_writeb(BIOS_HARDDISK_COUNT,2);
+
+ MAPPER_AddHandler(swapInNextDisk,MK_f4,MMOD1,"swapimg","Swap Image");
+ killRead = false;
+ swapping_requested = false;
+}
diff --git a/src/ints/bios_keyboard.cpp b/src/ints/bios_keyboard.cpp
new file mode 100644
index 000000000..ef14570fe
--- /dev/null
+++ b/src/ints/bios_keyboard.cpp
@@ -0,0 +1,680 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "callback.h"
+#include "mem.h"
+#include "bios.h"
+#include "keyboard.h"
+#include "regs.h"
+#include "inout.h"
+#include "dos_inc.h"
+#include "SDL.h"
+
+/* SDL by default treats numlock and scrolllock different from all other keys.
+ * In recent versions this can disabled by a environment variable which we set in sdlmain.cpp
+ * Define the following if this is the case */
+#if SDL_VERSION_ATLEAST(1, 2, 14)
+#define CAN_USE_LOCK 1
+/* For lower versions of SDL we also use a slight hack to get the startup states of numclock and capslock right.
+ * The proper way is in the mapper, but the repeating key is an unwanted side effect for lower versions of SDL */
+#endif
+
+static Bitu call_int16,call_irq1,call_irq6;
+
+/* Nice table from BOCHS i should feel bad for ripping this */
+#define none 0
+static struct {
+ Bit16u normal;
+ Bit16u shift;
+ Bit16u control;
+ Bit16u alt;
+ } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
+ { none, none, none, none },
+ { 0x011b, 0x011b, 0x011b, 0x01f0 }, /* escape */
+ { 0x0231, 0x0221, none, 0x7800 }, /* 1! */
+ { 0x0332, 0x0340, 0x0300, 0x7900 }, /* 2@ */
+ { 0x0433, 0x0423, none, 0x7a00 }, /* 3# */
+ { 0x0534, 0x0524, none, 0x7b00 }, /* 4$ */
+ { 0x0635, 0x0625, none, 0x7c00 }, /* 5% */
+ { 0x0736, 0x075e, 0x071e, 0x7d00 }, /* 6^ */
+ { 0x0837, 0x0826, none, 0x7e00 }, /* 7& */
+ { 0x0938, 0x092a, none, 0x7f00 }, /* 8* */
+ { 0x0a39, 0x0a28, none, 0x8000 }, /* 9( */
+ { 0x0b30, 0x0b29, none, 0x8100 }, /* 0) */
+ { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200 }, /* -_ */
+ { 0x0d3d, 0x0d2b, none, 0x8300 }, /* =+ */
+ { 0x0e08, 0x0e08, 0x0e7f, 0x0ef0 }, /* backspace */
+ { 0x0f09, 0x0f00, 0x9400, none }, /* tab */
+ { 0x1071, 0x1051, 0x1011, 0x1000 }, /* Q */
+ { 0x1177, 0x1157, 0x1117, 0x1100 }, /* W */
+ { 0x1265, 0x1245, 0x1205, 0x1200 }, /* E */
+ { 0x1372, 0x1352, 0x1312, 0x1300 }, /* R */
+ { 0x1474, 0x1454, 0x1414, 0x1400 }, /* T */
+ { 0x1579, 0x1559, 0x1519, 0x1500 }, /* Y */
+ { 0x1675, 0x1655, 0x1615, 0x1600 }, /* U */
+ { 0x1769, 0x1749, 0x1709, 0x1700 }, /* I */
+ { 0x186f, 0x184f, 0x180f, 0x1800 }, /* O */
+ { 0x1970, 0x1950, 0x1910, 0x1900 }, /* P */
+ { 0x1a5b, 0x1a7b, 0x1a1b, 0x1af0 }, /* [{ */
+ { 0x1b5d, 0x1b7d, 0x1b1d, 0x1bf0 }, /* ]} */
+ { 0x1c0d, 0x1c0d, 0x1c0a, none }, /* Enter */
+ { none, none, none, none }, /* L Ctrl */
+ { 0x1e61, 0x1e41, 0x1e01, 0x1e00 }, /* A */
+ { 0x1f73, 0x1f53, 0x1f13, 0x1f00 }, /* S */
+ { 0x2064, 0x2044, 0x2004, 0x2000 }, /* D */
+ { 0x2166, 0x2146, 0x2106, 0x2100 }, /* F */
+ { 0x2267, 0x2247, 0x2207, 0x2200 }, /* G */
+ { 0x2368, 0x2348, 0x2308, 0x2300 }, /* H */
+ { 0x246a, 0x244a, 0x240a, 0x2400 }, /* J */
+ { 0x256b, 0x254b, 0x250b, 0x2500 }, /* K */
+ { 0x266c, 0x264c, 0x260c, 0x2600 }, /* L */
+ { 0x273b, 0x273a, none, 0x27f0 }, /* ;: */
+ { 0x2827, 0x2822, none, 0x28f0 }, /* '" */
+ { 0x2960, 0x297e, none, 0x29f0 }, /* `~ */
+ { none, none, none, none }, /* L shift */
+ { 0x2b5c, 0x2b7c, 0x2b1c, 0x2bf0 }, /* |\ */
+ { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00 }, /* Z */
+ { 0x2d78, 0x2d58, 0x2d18, 0x2d00 }, /* X */
+ { 0x2e63, 0x2e43, 0x2e03, 0x2e00 }, /* C */
+ { 0x2f76, 0x2f56, 0x2f16, 0x2f00 }, /* V */
+ { 0x3062, 0x3042, 0x3002, 0x3000 }, /* B */
+ { 0x316e, 0x314e, 0x310e, 0x3100 }, /* N */
+ { 0x326d, 0x324d, 0x320d, 0x3200 }, /* M */
+ { 0x332c, 0x333c, none, 0x33f0 }, /* ,< */
+ { 0x342e, 0x343e, none, 0x34f0 }, /* .> */
+ { 0x352f, 0x353f, none, 0x35f0 }, /* /? */
+ { none, none, none, none }, /* R Shift */
+ { 0x372a, 0x372a, 0x9600, 0x37f0 }, /* * */
+ { none, none, none, none }, /* L Alt */
+ { 0x3920, 0x3920, 0x3920, 0x3920 }, /* space */
+ { none, none, none, none }, /* caps lock */
+ { 0x3b00, 0x5400, 0x5e00, 0x6800 }, /* F1 */
+ { 0x3c00, 0x5500, 0x5f00, 0x6900 }, /* F2 */
+ { 0x3d00, 0x5600, 0x6000, 0x6a00 }, /* F3 */
+ { 0x3e00, 0x5700, 0x6100, 0x6b00 }, /* F4 */
+ { 0x3f00, 0x5800, 0x6200, 0x6c00 }, /* F5 */
+ { 0x4000, 0x5900, 0x6300, 0x6d00 }, /* F6 */
+ { 0x4100, 0x5a00, 0x6400, 0x6e00 }, /* F7 */
+ { 0x4200, 0x5b00, 0x6500, 0x6f00 }, /* F8 */
+ { 0x4300, 0x5c00, 0x6600, 0x7000 }, /* F9 */
+ { 0x4400, 0x5d00, 0x6700, 0x7100 }, /* F10 */
+ { none, none, none, none }, /* Num Lock */
+ { none, none, none, none }, /* Scroll Lock */
+ { 0x4700, 0x4737, 0x7700, 0x0007 }, /* 7 Home */
+ { 0x4800, 0x4838, 0x8d00, 0x0008 }, /* 8 UP */
+ { 0x4900, 0x4939, 0x8400, 0x0009 }, /* 9 PgUp */
+ { 0x4a2d, 0x4a2d, 0x8e00, 0x4af0 }, /* - */
+ { 0x4b00, 0x4b34, 0x7300, 0x0004 }, /* 4 Left */
+ { 0x4cf0, 0x4c35, 0x8f00, 0x0005 }, /* 5 */
+ { 0x4d00, 0x4d36, 0x7400, 0x0006 }, /* 6 Right */
+ { 0x4e2b, 0x4e2b, 0x9000, 0x4ef0 }, /* + */
+ { 0x4f00, 0x4f31, 0x7500, 0x0001 }, /* 1 End */
+ { 0x5000, 0x5032, 0x9100, 0x0002 }, /* 2 Down */
+ { 0x5100, 0x5133, 0x7600, 0x0003 }, /* 3 PgDn */
+ { 0x5200, 0x5230, 0x9200, 0x0000 }, /* 0 Ins */
+ { 0x5300, 0x532e, 0x9300, none }, /* Del */
+ { none, none, none, none },
+ { none, none, none, none },
+ { 0x565c, 0x567c, none, none }, /* (102-key) */
+ { 0x8500, 0x8700, 0x8900, 0x8b00 }, /* F11 */
+ { 0x8600, 0x8800, 0x8a00, 0x8c00 } /* F12 */
+ };
+
+bool BIOS_AddKeyToBuffer(Bit16u code) {
+ if (mem_readb(BIOS_KEYBOARD_FLAGS2)&8) return true;
+ Bit16u start,end,head,tail,ttail;
+ if (machine==MCH_PCJR) {
+ /* should be done for cga and others as well, to be tested */
+ start=0x1e;
+ end=0x3e;
+ } else {
+ start=mem_readw(BIOS_KEYBOARD_BUFFER_START);
+ end =mem_readw(BIOS_KEYBOARD_BUFFER_END);
+ }
+ head =mem_readw(BIOS_KEYBOARD_BUFFER_HEAD);
+ tail =mem_readw(BIOS_KEYBOARD_BUFFER_TAIL);
+ ttail=tail+2;
+ if (ttail>=end) {
+ ttail=start;
+ }
+ /* Check for buffer Full */
+ //TODO Maybe beeeeeeep or something although that should happend when internal buffer is full
+ if (ttail==head) return false;
+ real_writew(0x40,tail,code);
+ mem_writew(BIOS_KEYBOARD_BUFFER_TAIL,ttail);
+ return true;
+}
+
+static void add_key(Bit16u code) {
+ if (code!=0) BIOS_AddKeyToBuffer(code);
+}
+
+static bool get_key(Bit16u &code) {
+ Bit16u start,end,head,tail,thead;
+ if (machine==MCH_PCJR) {
+ /* should be done for cga and others as well, to be tested */
+ start=0x1e;
+ end=0x3e;
+ } else {
+ start=mem_readw(BIOS_KEYBOARD_BUFFER_START);
+ end =mem_readw(BIOS_KEYBOARD_BUFFER_END);
+ }
+ head =mem_readw(BIOS_KEYBOARD_BUFFER_HEAD);
+ tail =mem_readw(BIOS_KEYBOARD_BUFFER_TAIL);
+
+ if (head==tail) return false;
+ thead=head+2;
+ if (thead>=end) thead=start;
+ mem_writew(BIOS_KEYBOARD_BUFFER_HEAD,thead);
+ code = real_readw(0x40,head);
+ return true;
+}
+
+static bool check_key(Bit16u &code) {
+ Bit16u head,tail;
+ head =mem_readw(BIOS_KEYBOARD_BUFFER_HEAD);
+ tail =mem_readw(BIOS_KEYBOARD_BUFFER_TAIL);
+ if (head==tail) return false;
+ code = real_readw(0x40,head);
+ return true;
+}
+
+ /* Flag Byte 1
+ bit 7 =1 INSert active
+ bit 6 =1 Caps Lock active
+ bit 5 =1 Num Lock active
+ bit 4 =1 Scroll Lock active
+ bit 3 =1 either Alt pressed
+ bit 2 =1 either Ctrl pressed
+ bit 1 =1 Left Shift pressed
+ bit 0 =1 Right Shift pressed
+ */
+ /* Flag Byte 2
+ bit 7 =1 INSert pressed
+ bit 6 =1 Caps Lock pressed
+ bit 5 =1 Num Lock pressed
+ bit 4 =1 Scroll Lock pressed
+ bit 3 =1 Pause state active
+ bit 2 =1 Sys Req pressed
+ bit 1 =1 Left Alt pressed
+ bit 0 =1 Left Ctrl pressed
+ */
+ /*
+ Keyboard status byte 3
+ bit 7 =1 read-ID in progress
+ bit 6 =1 last code read was first of two ID codes
+ bit 5 =1 force Num Lock if read-ID and enhanced keyboard
+ bit 4 =1 enhanced keyboard installed
+ bit 3 =1 Right Alt pressed
+ bit 2 =1 Right Ctrl pressed
+ bit 1 =1 last code read was E0h
+ bit 0 =1 last code read was E1h
+ */
+
+
+/* the scancode is in reg_al */
+static Bitu IRQ1_Handler(void) {
+/* handling of the locks key is difficult as sdl only gives
+ * states for numlock capslock.
+ */
+ Bitu scancode=reg_al; /* Read the code */
+
+ Bit8u flags1,flags2,flags3,leds;
+ flags1=mem_readb(BIOS_KEYBOARD_FLAGS1);
+ flags2=mem_readb(BIOS_KEYBOARD_FLAGS2);
+ flags3=mem_readb(BIOS_KEYBOARD_FLAGS3);
+ leds =mem_readb(BIOS_KEYBOARD_LEDS);
+#ifdef CAN_USE_LOCK
+ /* No hack anymore! */
+#else
+ flags2&=~(0x40+0x20);//remove numlock/capslock pressed (hack for sdl only reporting states)
+#endif
+ if (DOS_LayoutKey(scancode,flags1,flags2,flags3)) return CBRET_NONE;
+//LOG_MSG("key input %d %d %d %d",scancode,flags1,flags2,flags3);
+ switch (scancode) {
+ /* First the hard ones */
+ case 0xfa: /* ack. Do nothing for now */
+ break;
+ case 0xe1: /* Extended key special. Only pause uses this */
+ flags3 |=0x01;
+ break;
+ case 0xe0: /* Extended key */
+ flags3 |=0x02;
+ break;
+ case 0x1d: /* Ctrl Pressed */
+ if (!(flags3 &0x01)) {
+ flags1 |=0x04;
+ if (flags3 &0x02) flags3 |=0x04;
+ else flags2 |=0x01;
+ } /* else it's part of the pause scancodes */
+ break;
+ case 0x9d: /* Ctrl Released */
+ if (!(flags3 &0x01)) {
+ if (flags3 &0x02) flags3 &=~0x04;
+ else flags2 &=~0x01;
+ if( !( (flags3 &0x04) || (flags2 &0x01) ) ) flags1 &=~0x04;
+ }
+ break;
+ case 0x2a: /* Left Shift Pressed */
+ flags1 |=0x02;
+ break;
+ case 0xaa: /* Left Shift Released */
+ flags1 &=~0x02;
+ break;
+ case 0x36: /* Right Shift Pressed */
+ flags1 |=0x01;
+ break;
+ case 0xb6: /* Right Shift Released */
+ flags1 &=~0x01;
+ break;
+ case 0x37: /* Keypad * or PrtSc Pressed */
+ if (!(flags3 &0x02)) goto normal_key;
+ reg_ip+=7; // call int 5
+ break;
+ case 0xb7: /* Keypad * or PrtSc Released */
+ if (!(flags3 &0x02)) goto normal_key;
+ break;
+ case 0x38: /* Alt Pressed */
+ flags1 |=0x08;
+ if (flags3 &0x02) flags3 |=0x08;
+ else flags2 |=0x02;
+ break;
+ case 0xb8: /* Alt Released */
+ if (flags3 &0x02) flags3 &= ~0x08;
+ else flags2 &= ~0x02;
+ if( !( (flags3 &0x08) || (flags2 &0x02) ) ) { /* Both alt released */
+ flags1 &= ~0x08;
+ Bit16u token =mem_readb(BIOS_KEYBOARD_TOKEN);
+ if(token != 0){
+ add_key(token);
+ mem_writeb(BIOS_KEYBOARD_TOKEN,0);
+ }
+ }
+ break;
+
+#ifdef CAN_USE_LOCK
+ case 0x3a:flags2 |=0x40;break;//CAPSLOCK
+ case 0xba:flags1 ^=0x40;flags2 &=~0x40;leds ^=0x04;break;
+#else
+ case 0x3a:flags2 |=0x40;flags1 |=0x40;leds |=0x04;break; //SDL gives only the state instead of the toggle /* Caps Lock */
+ case 0xba:flags1 &=~0x40;leds &=~0x04;break;
+#endif
+ case 0x45:
+ if (flags3 &0x01) {
+ /* last scancode of pause received; first remove 0xe1-prefix */
+ flags3 &=~0x01;
+ mem_writeb(BIOS_KEYBOARD_FLAGS3,flags3);
+ if (flags2&1) {
+ /* ctrl-pause (break), special handling needed:
+ add zero to the keyboard buffer, call int 0x1b which
+ sets ctrl-c flag which calls int 0x23 in certain dos
+ input/output functions; not handled */
+ } else if ((flags2&8)==0) {
+ /* normal pause key, enter loop */
+ mem_writeb(BIOS_KEYBOARD_FLAGS2,flags2|8);
+ IO_Write(0x20,0x20);
+ while (mem_readb(BIOS_KEYBOARD_FLAGS2)&8) CALLBACK_Idle(); // pause loop
+ reg_ip+=5; // skip out 20,20
+ return CBRET_NONE;
+ }
+ } else {
+ /* Num Lock */
+#ifdef CAN_USE_LOCK
+ flags2 |=0x20;
+#else
+ flags2 |=0x20;
+ flags1 |=0x20;
+ leds |=0x02;
+#endif
+ }
+ break;
+ case 0xc5:
+ if (flags3 &0x01) {
+ /* pause released */
+ flags3 &=~0x01;
+ } else {
+#ifdef CAN_USE_LOCK
+ flags1^=0x20;
+ leds^=0x02;
+ flags2&=~0x20;
+#else
+ /* Num Lock released */
+ flags1 &=~0x20;
+ leds &=~0x02;
+#endif
+ }
+ break;
+ case 0x46:flags2 |=0x10;break; /* Scroll Lock SDL Seems to do this one fine (so break and make codes) */
+ case 0xc6:flags1 ^=0x10;flags2 &=~0x10;leds ^=0x01;break;
+// case 0x52:flags2|=128;break;//See numpad /* Insert */
+ case 0xd2:
+ if(flags3&0x02) { /* Maybe honour the insert on keypad as well */
+ flags1^=0x80;
+ flags2&=~0x80;
+ break;
+ } else {
+ goto irq1_end;/*Normal release*/
+ }
+ case 0x47: /* Numpad */
+ case 0x48:
+ case 0x49:
+ case 0x4b:
+ case 0x4c:
+ case 0x4d:
+ case 0x4f:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53: /* del . Not entirely correct, but works fine */
+ if(flags3 &0x02) { /*extend key. e.g key above arrows or arrows*/
+ if(scancode == 0x52) flags2 |=0x80; /* press insert */
+ if(flags1 &0x08) {
+ add_key(scan_to_scanascii[scancode].normal+0x5000);
+ } else if (flags1 &0x04) {
+ add_key((scan_to_scanascii[scancode].control&0xff00)|0xe0);
+ } else if( ((flags1 &0x3) != 0) || ((flags1 &0x20) != 0) ) { //Due to |0xe0 results are identical.
+ add_key((scan_to_scanascii[scancode].shift&0xff00)|0xe0);
+ } else add_key((scan_to_scanascii[scancode].normal&0xff00)|0xe0);
+ break;
+ }
+ if(flags1 &0x08) {
+ Bit8u token = mem_readb(BIOS_KEYBOARD_TOKEN);
+ token = token*10 + (Bit8u)(scan_to_scanascii[scancode].alt&0xff);
+ mem_writeb(BIOS_KEYBOARD_TOKEN,token);
+ } else if (flags1 &0x04) {
+ add_key(scan_to_scanascii[scancode].control);
+ } else if( ((flags1 &0x3) != 0) ^ ((flags1 &0x20) != 0) ) { //Xor shift and numlock (both means off)
+ add_key(scan_to_scanascii[scancode].shift);
+ } else add_key(scan_to_scanascii[scancode].normal);
+ break;
+
+ default: /* Normal Key */
+normal_key:
+ Bit16u asciiscan;
+ /* Now Handle the releasing of keys and see if they match up for a code */
+ /* Handle the actual scancode */
+ if (scancode & 0x80) goto irq1_end;
+ if (scancode > MAX_SCAN_CODE) goto irq1_end;
+ if (flags1 & 0x08) { /* Alt is being pressed */
+ asciiscan=scan_to_scanascii[scancode].alt;
+#if 0 /* old unicode support disabled*/
+ } else if (ascii) {
+ asciiscan=(scancode << 8) | ascii;
+#endif
+ } else if (flags1 & 0x04) { /* Ctrl is being pressed */
+ asciiscan=scan_to_scanascii[scancode].control;
+ } else if (flags1 & 0x03) { /* Either shift is being pressed */
+ asciiscan=scan_to_scanascii[scancode].shift;
+ } else {
+ asciiscan=scan_to_scanascii[scancode].normal;
+ }
+ /* cancel shift is letter and capslock active */
+ if(flags1&64) {
+ if(flags1&3) {
+ /*cancel shift */
+ if(((asciiscan&0x00ff) >0x40) && ((asciiscan&0x00ff) <0x5b))
+ asciiscan=scan_to_scanascii[scancode].normal;
+ } else {
+ /* add shift */
+ if(((asciiscan&0x00ff) >0x60) && ((asciiscan&0x00ff) <0x7b))
+ asciiscan=scan_to_scanascii[scancode].shift;
+ }
+ }
+ if (flags3 &0x02) {
+ /* extended key (numblock), return and slash need special handling */
+ if (scancode==0x1c) { /* return */
+ if (flags1 &0x08) asciiscan=0xa600;
+ else asciiscan=(asciiscan&0xff)|0xe000;
+ } else if (scancode==0x35) { /* slash */
+ if (flags1 &0x08) asciiscan=0xa400;
+ else if (flags1 &0x04) asciiscan=0x9500;
+ else asciiscan=0xe02f;
+ }
+ }
+ add_key(asciiscan);
+ break;
+ };
+irq1_end:
+ if(scancode !=0xe0) flags3 &=~0x02; //Reset 0xE0 Flag
+ mem_writeb(BIOS_KEYBOARD_FLAGS1,flags1);
+ if ((scancode&0x80)==0) flags2&=0xf7;
+ mem_writeb(BIOS_KEYBOARD_FLAGS2,flags2);
+ mem_writeb(BIOS_KEYBOARD_FLAGS3,flags3);
+ mem_writeb(BIOS_KEYBOARD_LEDS,leds);
+/* IO_Write(0x20,0x20); moved out of handler to be virtualizable */
+#if 0
+/* Signal the keyboard for next code */
+/* In dosbox port 60 reads do this as well */
+ Bit8u old61=IO_Read(0x61);
+ IO_Write(0x61,old61 | 128);
+ IO_Write(0x64,0xae);
+#endif
+ return CBRET_NONE;
+}
+
+
+/* check whether key combination is enhanced or not,
+ translate key if necessary */
+static bool IsEnhancedKey(Bit16u &key) {
+ /* test for special keys (return and slash on numblock) */
+ if ((key>>8)==0xe0) {
+ if (((key&0xff)==0x0a) || ((key&0xff)==0x0d)) {
+ /* key is return on the numblock */
+ key=(key&0xff)|0x1c00;
+ } else {
+ /* key is slash on the numblock */
+ key=(key&0xff)|0x3500;
+ }
+ /* both keys are not considered enhanced keys */
+ return false;
+ } else if (((key>>8)>0x84) || (((key&0xff)==0xf0) && (key>>8))) {
+ /* key is enhanced key (either scancode part>0x84 or
+ specially-marked keyboard combination, low part==0xf0) */
+ return true;
+ }
+ /* convert key if necessary (extended keys) */
+ if ((key>>8) && ((key&0xff)==0xe0)) {
+ key&=0xff00;
+ }
+ return false;
+}
+
+static Bitu INT16_Handler(void) {
+ Bit16u temp=0;
+ switch (reg_ah) {
+ case 0x00: /* GET KEYSTROKE */
+ if ((get_key(temp)) && (!IsEnhancedKey(temp))) {
+ /* normal key found, return translated key in ax */
+ reg_ax=temp;
+ } else {
+ /* enter small idle loop to allow for irqs to happen */
+ reg_ip+=1;
+ }
+ break;
+ case 0x10: /* GET KEYSTROKE (enhanced keyboards only) */
+ if (get_key(temp)) {
+ if (((temp&0xff)==0xf0) && (temp>>8)) {
+ /* special enhanced key, clear low part before returning key */
+ temp&=0xff00;
+ }
+ reg_ax=temp;
+ } else {
+ /* enter small idle loop to allow for irqs to happen */
+ reg_ip+=1;
+ }
+ break;
+ case 0x01: /* CHECK FOR KEYSTROKE */
+ // enable interrupt-flag after IRET of this int16
+ CALLBACK_SIF(true);
+ for (;;) {
+ if (check_key(temp)) {
+ if (!IsEnhancedKey(temp)) {
+ /* normal key, return translated key in ax */
+ CALLBACK_SZF(false);
+ reg_ax=temp;
+ break;
+ } else {
+ /* remove enhanced key from buffer and ignore it */
+ get_key(temp);
+ }
+ } else {
+ /* no key available */
+ CALLBACK_SZF(true);
+ break;
+ }
+// CALLBACK_Idle();
+ }
+ break;
+ case 0x11: /* CHECK FOR KEYSTROKE (enhanced keyboards only) */
+ // enable interrupt-flag after IRET of this int16
+ CALLBACK_SIF(true);
+ if (!check_key(temp)) {
+ CALLBACK_SZF(true);
+ } else {
+ CALLBACK_SZF(false);
+ if (((temp&0xff)==0xf0) && (temp>>8)) {
+ /* special enhanced key, clear low part before returning key */
+ temp&=0xff00;
+ }
+ reg_ax=temp;
+ }
+ break;
+ case 0x02: /* GET SHIFT FLAGS */
+ reg_al=mem_readb(BIOS_KEYBOARD_FLAGS1);
+ break;
+ case 0x03: /* SET TYPEMATIC RATE AND DELAY */
+ if (reg_al == 0x00) { // set default delay and rate
+ IO_Write(0x60,0xf3);
+ IO_Write(0x60,0x20); // 500 msec delay, 30 cps
+ } else if (reg_al == 0x05) { // set repeat rate and delay
+ IO_Write(0x60,0xf3);
+ IO_Write(0x60,(reg_bh&3)<<5|(reg_bl&0x1f));
+ } else {
+ LOG(LOG_BIOS,LOG_ERROR)("INT16:Unhandled Typematic Rate Call %2X BX=%X",reg_al,reg_bx);
+ }
+ break;
+ case 0x05: /* STORE KEYSTROKE IN KEYBOARD BUFFER */
+ if (BIOS_AddKeyToBuffer(reg_cx)) reg_al=0;
+ else reg_al=1;
+ break;
+ case 0x12: /* GET EXTENDED SHIFT STATES */
+ reg_al = mem_readb(BIOS_KEYBOARD_FLAGS1);
+ reg_ah = (mem_readb(BIOS_KEYBOARD_FLAGS2)&0x73) |
+ ((mem_readb(BIOS_KEYBOARD_FLAGS2)&4)<<5) | // SysReq pressed, bit 7
+ (mem_readb(BIOS_KEYBOARD_FLAGS3)&0x0c); // Right Ctrl/Alt pressed, bits 2,3
+ break;
+ case 0x55:
+ /* Weird call used by some dos apps */
+ LOG(LOG_BIOS,LOG_NORMAL)("INT16:55:Word TSR compatible call");
+ break;
+ default:
+ LOG(LOG_BIOS,LOG_ERROR)("INT16:Unhandled call %02X",reg_ah);
+ break;
+
+ };
+
+ return CBRET_NONE;
+}
+
+//Keyboard initialisation. src/gui/sdlmain.cpp
+extern bool startup_state_numlock;
+extern bool startup_state_capslock;
+
+static void InitBiosSegment(void) {
+ /* Setup the variables for keyboard in the bios data segment */
+ mem_writew(BIOS_KEYBOARD_BUFFER_START,0x1e);
+ mem_writew(BIOS_KEYBOARD_BUFFER_END,0x3e);
+ mem_writew(BIOS_KEYBOARD_BUFFER_HEAD,0x1e);
+ mem_writew(BIOS_KEYBOARD_BUFFER_TAIL,0x1e);
+ Bit8u flag1 = 0;
+ Bit8u leds = 16; /* Ack received */
+
+#if SDL_VERSION_ATLEAST(1, 2, 14)
+//Nothing, mapper handles all.
+#else
+ if (startup_state_capslock) { flag1|=0x40; leds|=0x04;}
+ if (startup_state_numlock) { flag1|=0x20; leds|=0x02;}
+#endif
+
+ mem_writeb(BIOS_KEYBOARD_FLAGS1,flag1);
+ mem_writeb(BIOS_KEYBOARD_FLAGS2,0);
+ mem_writeb(BIOS_KEYBOARD_FLAGS3,16); /* Enhanced keyboard installed */
+ mem_writeb(BIOS_KEYBOARD_TOKEN,0);
+ mem_writeb(BIOS_KEYBOARD_LEDS,leds);
+}
+
+void BIOS_SetupKeyboard(void) {
+ /* Init the variables */
+ InitBiosSegment();
+
+ /* Allocate/setup a callback for int 0x16 and for standard IRQ 1 handler */
+ call_int16=CALLBACK_Allocate();
+ CALLBACK_Setup(call_int16,&INT16_Handler,CB_INT16,"Keyboard");
+ RealSetVec(0x16,CALLBACK_RealPointer(call_int16));
+
+ call_irq1=CALLBACK_Allocate();
+ CALLBACK_Setup(call_irq1,&IRQ1_Handler,CB_IRQ1,Real2Phys(BIOS_DEFAULT_IRQ1_LOCATION),"IRQ 1 Keyboard");
+ RealSetVec(0x09,BIOS_DEFAULT_IRQ1_LOCATION);
+ // pseudocode for CB_IRQ1:
+ // push ax
+ // in al, 0x60
+ // mov ah, 0x4f
+ // stc
+ // int 15
+ // jc skip
+ // callback IRQ1_Handler
+ // label skip:
+ // cli
+ // mov al, 0x20
+ // out 0x20, al
+ // pop ax
+ // iret
+ // cli
+ // mov al, 0x20
+ // out 0x20, al
+ // push bp
+ // int 0x05
+ // pop bp
+ // pop ax
+ // iret
+
+ if (machine==MCH_PCJR) {
+ call_irq6=CALLBACK_Allocate();
+ CALLBACK_Setup(call_irq6,NULL,CB_IRQ6_PCJR,"PCJr kb irq");
+ RealSetVec(0x0e,CALLBACK_RealPointer(call_irq6));
+ // pseudocode for CB_IRQ6_PCJR:
+ // push ax
+ // in al, 0x60
+ // cmp al, 0xe0
+ // je skip
+ // push ds
+ // push 0x40
+ // pop ds
+ // int 0x09
+ // pop ds
+ // label skip:
+ // cli
+ // mov al, 0x20
+ // out 0x20, al
+ // pop ax
+ // iret
+ }
+}
+
diff --git a/src/ints/ems.cpp b/src/ints/ems.cpp
new file mode 100644
index 000000000..94aed6ae1
--- /dev/null
+++ b/src/ints/ems.cpp
@@ -0,0 +1,1471 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <stdlib.h>
+#include "dosbox.h"
+#include "callback.h"
+#include "mem.h"
+#include "paging.h"
+#include "bios.h"
+#include "keyboard.h"
+#include "regs.h"
+#include "inout.h"
+#include "dos_inc.h"
+#include "setup.h"
+#include "support.h"
+#include "cpu.h"
+#include "dma.h"
+
+#define EMM_PAGEFRAME 0xE000
+#define EMM_PAGEFRAME4K ((EMM_PAGEFRAME*16)/4096)
+#define EMM_MAX_HANDLES 200 /* 255 Max */
+#define EMM_PAGE_SIZE (16*1024U)
+#define EMM_MAX_PAGES (32 * 1024 / 16 )
+#define EMM_MAX_PHYS 4 /* 4 16kb pages in pageframe */
+
+#define EMM_VERSION 0x40
+#define EMM_MINOR_VERSION 0x00
+//#define EMM_MINOR_VERSION 0x30 // emm386 4.48
+#define GEMMIS_VERSION 0x0001 // Version 1.0
+
+#define EMM_SYSTEM_HANDLE 0x0000
+#define NULL_HANDLE 0xffff
+#define NULL_PAGE 0xffff
+
+#define ENABLE_VCPI 1
+#define ENABLE_V86_STARTUP 0
+
+
+/* EMM errors */
+#define EMM_NO_ERROR 0x00
+#define EMM_SOFT_MAL 0x80
+#define EMM_HARD_MAL 0x81
+#define EMM_INVALID_HANDLE 0x83
+#define EMM_FUNC_NOSUP 0x84
+#define EMM_OUT_OF_HANDLES 0x85
+#define EMM_SAVEMAP_ERROR 0x86
+#define EMM_OUT_OF_PHYS 0x87
+#define EMM_OUT_OF_LOG 0x88
+#define EMM_ZERO_PAGES 0x89
+#define EMM_LOG_OUT_RANGE 0x8a
+#define EMM_ILL_PHYS 0x8b
+#define EMM_PAGE_MAP_SAVED 0x8d
+#define EMM_NO_SAVED_PAGE_MAP 0x8e
+#define EMM_INVALID_SUB 0x8f
+#define EMM_FEAT_NOSUP 0x91
+#define EMM_MOVE_OVLAP 0x92
+#define EMM_MOVE_OVLAPI 0x97
+#define EMM_NOT_FOUND 0xa0
+
+
+struct EMM_Mapping {
+ Bit16u handle;
+ Bit16u page;
+};
+
+struct EMM_Handle {
+ Bit16u pages;
+ MemHandle mem;
+ char name[8];
+ bool saved_page_map;
+ EMM_Mapping page_map[EMM_MAX_PHYS];
+};
+
+static Bitu ems_type;
+
+static EMM_Handle emm_handles[EMM_MAX_HANDLES];
+static EMM_Mapping emm_mappings[EMM_MAX_PHYS];
+static EMM_Mapping emm_segmentmappings[0x40];
+
+
+static Bit16u GEMMIS_seg;
+
+class device_EMM : public DOS_Device {
+public:
+ device_EMM(bool is_emm386_avail) {
+ is_emm386=is_emm386_avail;
+ SetName("EMMXXXX0");
+ GEMMIS_seg=0;
+ }
+ bool Read(Bit8u * /*data*/,Bit16u * /*size*/) { return false;}
+ bool Write(Bit8u * /*data*/,Bit16u * /*size*/){
+ LOG(LOG_IOCTL,LOG_NORMAL)("EMS:Write to device");
+ return false;
+ }
+ bool Seek(Bit32u * /*pos*/,Bit32u /*type*/){return false;}
+ bool Close(){return false;}
+ Bit16u GetInformation(void){return 0xc0c0;}
+ bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode);
+ bool WriteToControlChannel(PhysPt /*bufptr*/,Bit16u /*size*/,Bit16u * /*retcode*/){return true;}
+private:
+ Bit8u cache;
+ bool is_emm386;
+};
+
+bool device_EMM::ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) {
+ Bitu subfct=mem_readb(bufptr);
+ switch (subfct) {
+ case 0x00:
+ if (size!=6) return false;
+ mem_writew(bufptr+0x00,0x0023); // ID
+ mem_writed(bufptr+0x02,0); // private API entry point
+ *retcode=6;
+ return true;
+ case 0x01: {
+ if (!is_emm386) return false;
+ if (size!=6) return false;
+ if (GEMMIS_seg==0) GEMMIS_seg=DOS_GetMemory(0x20);
+ PhysPt GEMMIS_addr=PhysMake(GEMMIS_seg,0);
+
+ mem_writew(GEMMIS_addr+0x00,0x0004); // flags
+ mem_writew(GEMMIS_addr+0x02,0x019d); // size of this structure
+ mem_writew(GEMMIS_addr+0x04,GEMMIS_VERSION); // version 1.0 (provide ems information only)
+ mem_writed(GEMMIS_addr+0x06,0); // reserved
+
+ /* build non-EMS frames (0-0xe000) */
+ for (Bitu frct=0; frct<EMM_PAGEFRAME4K/4; frct++) {
+ mem_writeb(GEMMIS_addr+0x0a+frct*6,0x00); // frame type: NONE
+ mem_writeb(GEMMIS_addr+0x0b+frct*6,0xff); // owner: NONE
+ mem_writew(GEMMIS_addr+0x0c+frct*6,0xffff); // non-EMS frame
+ mem_writeb(GEMMIS_addr+0x0e + frct*6,0xff); // EMS page number (NONE)
+ mem_writeb(GEMMIS_addr+0x0f+frct*6,0xaa); // flags: direct mapping
+ }
+ /* build EMS page frame (0xe000-0xf000) */
+ for (Bitu frct=0; frct<0x10/4; frct++) {
+ Bitu frnr=(frct+EMM_PAGEFRAME4K/4)*6;
+ mem_writeb(GEMMIS_addr+0x0a+frnr,0x03); // frame type: EMS frame in 64k page
+ mem_writeb(GEMMIS_addr+0x0b+frnr,0xff); // owner: NONE
+ mem_writew(GEMMIS_addr+0x0c+frnr,0x7fff); // no logical page number
+ mem_writeb(GEMMIS_addr+0x0e + frnr,(Bit8u)(frct&0xff)); // physical EMS page number
+ mem_writeb(GEMMIS_addr+0x0f+frnr,0x00); // EMS frame
+ }
+ /* build non-EMS ROM frames (0xf000-0x10000) */
+ for (Bitu frct=(EMM_PAGEFRAME4K+0x10)/4; frct<0xf0/4; frct++) {
+ mem_writeb(GEMMIS_addr+0x0a+frct*6,0x00); // frame type: NONE
+ mem_writeb(GEMMIS_addr+0x0b+frct*6,0xff); // owner: NONE
+ mem_writew(GEMMIS_addr+0x0c+frct*6,0xffff); // non-EMS frame
+ mem_writeb(GEMMIS_addr+0x0e + frct*6,0xff); // EMS page number (NONE)
+ mem_writeb(GEMMIS_addr+0x0f+frct*6,0xaa); // flags: direct mapping
+ }
+
+ mem_writeb(GEMMIS_addr+0x18a,0x74); // ???
+ mem_writeb(GEMMIS_addr+0x18b,0x00); // no UMB descriptors following
+ mem_writeb(GEMMIS_addr+0x18c,0x01); // 1 EMS handle info recort
+ mem_writew(GEMMIS_addr+0x18d,0x0000); // system handle
+ mem_writed(GEMMIS_addr+0x18f,0); // handle name
+ mem_writed(GEMMIS_addr+0x193,0); // handle name
+ if (emm_handles[EMM_SYSTEM_HANDLE].pages != NULL_HANDLE) {
+ mem_writew(GEMMIS_addr+0x197,(emm_handles[EMM_SYSTEM_HANDLE].pages+3)/4);
+ mem_writed(GEMMIS_addr+0x199,emm_handles[EMM_SYSTEM_HANDLE].mem<<12); // physical address
+ } else {
+ mem_writew(GEMMIS_addr+0x197,0x0001); // system handle
+ mem_writed(GEMMIS_addr+0x199,0x00110000); // physical address
+ }
+
+ /* fill buffer with import structure */
+ mem_writed(bufptr+0x00,GEMMIS_seg<<4);
+ mem_writew(bufptr+0x04,GEMMIS_VERSION);
+ *retcode=6;
+ return true;
+ }
+ case 0x02:
+ if (!is_emm386) return false;
+ if (size!=2) return false;
+ mem_writeb(bufptr+0x00,EMM_VERSION>>4); // version 4
+ mem_writeb(bufptr+0x01,EMM_MINOR_VERSION);
+ *retcode=2;
+ return true;
+ case 0x03:
+ if (!is_emm386) return false;
+ if (EMM_MINOR_VERSION < 0x2d) return false;
+ if (size!=4) return false;
+ mem_writew(bufptr+0x00,(Bit16u)(MEM_TotalPages()*4)); // max size (kb)
+ mem_writew(bufptr+0x02,0x80); // min size (kb)
+ *retcode=2;
+ return true;
+ }
+ return false;
+}
+
+static struct {
+ bool enabled;
+ Bit16u ems_handle;
+ Bitu pm_interface;
+ MemHandle private_area;
+ Bit8u pic1_remapping,pic2_remapping;
+} vcpi ;
+
+struct MoveRegion {
+ Bit32u bytes;
+ Bit8u src_type;
+ Bit16u src_handle;
+ Bit16u src_offset;
+ Bit16u src_page_seg;
+ Bit8u dest_type;
+ Bit16u dest_handle;
+ Bit16u dest_offset;
+ Bit16u dest_page_seg;
+};
+
+static Bit16u EMM_GetFreePages(void) {
+ Bitu count=MEM_FreeTotal()/4;
+ if (count>0x7fff) count=0x7fff;
+ return (Bit16u)count;
+}
+
+static bool INLINE ValidHandle(Bit16u handle) {
+ if (handle>=EMM_MAX_HANDLES) return false;
+ if (emm_handles[handle].pages==NULL_HANDLE) return false;
+ return true;
+}
+
+static Bit8u EMM_AllocateMemory(Bit16u pages,Bit16u & dhandle,bool can_allocate_zpages) {
+ /* Check for 0 page allocation */
+ if (!pages) {
+ if (!can_allocate_zpages) return EMM_ZERO_PAGES;
+ }
+ /* Check for enough free pages */
+ if ((MEM_FreeTotal()/ 4) < pages) { return EMM_OUT_OF_LOG;}
+ Bit16u handle = 1;
+ /* Check for a free handle */
+ while (emm_handles[handle].pages != NULL_HANDLE) {
+ if (++handle >= EMM_MAX_HANDLES) {return EMM_OUT_OF_HANDLES;}
+ }
+ MemHandle mem = 0;
+ if (pages) {
+ mem = MEM_AllocatePages(pages*4,false);
+ if (!mem) E_Exit("EMS:Memory allocation failure");
+ }
+ emm_handles[handle].pages = pages;
+ emm_handles[handle].mem = mem;
+ /* Change handle only if there is no error. */
+ dhandle = handle;
+ return EMM_NO_ERROR;
+}
+
+static Bit8u EMM_AllocateSystemHandle(Bit16u pages) {
+ /* Check for enough free pages */
+ if ((MEM_FreeTotal()/ 4) < pages) { return EMM_OUT_OF_LOG;}
+ Bit16u handle = EMM_SYSTEM_HANDLE; // emm system handle (reserved for OS usage)
+ /* Release memory if already allocated */
+ if (emm_handles[handle].pages != NULL_HANDLE) {
+ MEM_ReleasePages(emm_handles[handle].mem);
+ }
+ MemHandle mem = MEM_AllocatePages(pages*4,false);
+ if (!mem) E_Exit("EMS:System handle memory allocation failure");
+ emm_handles[handle].pages = pages;
+ emm_handles[handle].mem = mem;
+ return EMM_NO_ERROR;
+}
+
+static Bit8u EMM_ReallocatePages(Bit16u handle,Bit16u & pages) {
+ /* Check for valid handle */
+ if (!ValidHandle(handle)) return EMM_INVALID_HANDLE;
+ if (emm_handles[handle].pages != 0) {
+ /* Check for enough pages */
+ if (!MEM_ReAllocatePages(emm_handles[handle].mem,pages*4,false)) return EMM_OUT_OF_LOG;
+ } else {
+ MemHandle mem = MEM_AllocatePages(pages*4,false);
+ if (!mem) E_Exit("EMS:Memory allocation failure during reallocation");
+ emm_handles[handle].mem = mem;
+ }
+ /* Update size */
+ emm_handles[handle].pages=pages;
+ return EMM_NO_ERROR;
+}
+
+static Bit8u EMM_MapPage(Bitu phys_page,Bit16u handle,Bit16u log_page) {
+// LOG_MSG("EMS MapPage handle %d phys %d log %d",handle,phys_page,log_page);
+ /* Check for too high physical page */
+ if (phys_page>=EMM_MAX_PHYS) return EMM_ILL_PHYS;
+
+ /* unmapping doesn't need valid handle (as handle isn't used) */
+ if (log_page==NULL_PAGE) {
+ /* Unmapping */
+ emm_mappings[phys_page].handle=NULL_HANDLE;
+ emm_mappings[phys_page].page=NULL_PAGE;
+ for (Bitu i=0;i<4;i++)
+ PAGING_MapPage(EMM_PAGEFRAME4K+phys_page*4+i,EMM_PAGEFRAME4K+phys_page*4+i);
+ PAGING_ClearTLB();
+ return EMM_NO_ERROR;
+ }
+ /* Check for valid handle */
+ if (!ValidHandle(handle)) return EMM_INVALID_HANDLE;
+
+ if (log_page<emm_handles[handle].pages) {
+ /* Mapping it is */
+ emm_mappings[phys_page].handle=handle;
+ emm_mappings[phys_page].page=log_page;
+
+ MemHandle memh=MEM_NextHandleAt(emm_handles[handle].mem,log_page*4);;
+ for (Bitu i=0;i<4;i++) {
+ PAGING_MapPage(EMM_PAGEFRAME4K+phys_page*4+i,memh);
+ memh=MEM_NextHandle(memh);
+ }
+ PAGING_ClearTLB();
+ return EMM_NO_ERROR;
+ } else {
+ /* Illegal logical page it is */
+ return EMM_LOG_OUT_RANGE;
+ }
+}
+
+static Bit8u EMM_MapSegment(Bitu segment,Bit16u handle,Bit16u log_page) {
+// LOG_MSG("EMS MapSegment handle %d segment %d log %d",handle,segment,log_page);
+
+ bool valid_segment=false;
+
+ if ((ems_type==1) || (ems_type==3)) {
+ if (segment<0xf000+0x1000) valid_segment=true;
+ } else {
+ if ((segment>=0xa000) && (segment<0xb000)) {
+ valid_segment=true; // allow mapping of graphics memory
+ }
+ if ((segment>=EMM_PAGEFRAME) && (segment<EMM_PAGEFRAME+0x1000)) {
+ valid_segment=true; // allow mapping of EMS page frame
+ }
+/* if ((segment>=EMM_PAGEFRAME-0x1000) && (segment<EMM_PAGEFRAME)) {
+ valid_segment=true;
+ } */
+ }
+
+ if (valid_segment) {
+ Bit32s tphysPage = ((Bit32s)segment-EMM_PAGEFRAME)/(0x1000/EMM_MAX_PHYS);
+
+ /* unmapping doesn't need valid handle (as handle isn't used) */
+ if (log_page==NULL_PAGE) {
+ /* Unmapping */
+ if ((tphysPage>=0) && (tphysPage<EMM_MAX_PHYS)) {
+ emm_mappings[tphysPage].handle=NULL_HANDLE;
+ emm_mappings[tphysPage].page=NULL_PAGE;
+ } else {
+ emm_segmentmappings[segment>>10].handle=NULL_HANDLE;
+ emm_segmentmappings[segment>>10].page=NULL_PAGE;
+ }
+ for (Bitu i=0;i<4;i++)
+ PAGING_MapPage(segment*16/4096+i,segment*16/4096+i);
+ PAGING_ClearTLB();
+ return EMM_NO_ERROR;
+ }
+ /* Check for valid handle */
+ if (!ValidHandle(handle)) return EMM_INVALID_HANDLE;
+
+ if (log_page<emm_handles[handle].pages) {
+ /* Mapping it is */
+ if ((tphysPage>=0) && (tphysPage<EMM_MAX_PHYS)) {
+ emm_mappings[tphysPage].handle=handle;
+ emm_mappings[tphysPage].page=log_page;
+ } else {
+ emm_segmentmappings[segment>>10].handle=handle;
+ emm_segmentmappings[segment>>10].page=log_page;
+ }
+
+ MemHandle memh=MEM_NextHandleAt(emm_handles[handle].mem,log_page*4);;
+ for (Bitu i=0;i<4;i++) {
+ PAGING_MapPage(segment*16/4096+i,memh);
+ memh=MEM_NextHandle(memh);
+ }
+ PAGING_ClearTLB();
+ return EMM_NO_ERROR;
+ } else {
+ /* Illegal logical page it is */
+ return EMM_LOG_OUT_RANGE;
+ }
+ }
+
+ return EMM_ILL_PHYS;
+}
+
+static Bit8u EMM_ReleaseMemory(Bit16u handle) {
+ /* Check for valid handle */
+ if (!ValidHandle(handle)) return EMM_INVALID_HANDLE;
+
+ // should check for saved_page_map flag here, returning an error if it's true
+ // as apps are required to restore the pagemap beforehand; to be checked
+// if (emm_handles[handle].saved_page_map) return EMM_SAVEMAP_ERROR;
+
+ if (emm_handles[handle].pages != 0) {
+ MEM_ReleasePages(emm_handles[handle].mem);
+ }
+ /* Reset handle */
+ emm_handles[handle].mem=0;
+ if (handle==0) {
+ emm_handles[handle].pages=0; // OS handle is NEVER deallocated
+ } else {
+ emm_handles[handle].pages=NULL_HANDLE;
+ }
+ emm_handles[handle].saved_page_map=false;
+ memset(&emm_handles[handle].name,0,8);
+ return EMM_NO_ERROR;
+}
+
+static Bit8u EMM_SavePageMap(Bit16u handle) {
+ /* Check for valid handle */
+ if (handle>=EMM_MAX_HANDLES || emm_handles[handle].pages==NULL_HANDLE) {
+ if (handle!=0) return EMM_INVALID_HANDLE;
+ }
+ /* Check for previous save */
+ if (emm_handles[handle].saved_page_map) return EMM_PAGE_MAP_SAVED;
+ /* Copy the mappings over */
+ for (Bitu i=0;i<EMM_MAX_PHYS;i++) {
+ emm_handles[handle].page_map[i].page=emm_mappings[i].page;
+ emm_handles[handle].page_map[i].handle=emm_mappings[i].handle;
+ }
+ emm_handles[handle].saved_page_map=true;
+ return EMM_NO_ERROR;
+}
+
+static Bit8u EMM_RestoreMappingTable(void) {
+ Bit8u result;
+ /* Move through the mappings table and setup mapping accordingly */
+ for (Bitu i=0;i<0x40;i++) {
+ /* Skip the pageframe */
+ if ((i>=EMM_PAGEFRAME/0x400) && (i<(EMM_PAGEFRAME/0x400)+EMM_MAX_PHYS)) continue;
+ result=EMM_MapSegment(i<<10,emm_segmentmappings[i].handle,emm_segmentmappings[i].page);
+ }
+ for (Bitu i=0;i<EMM_MAX_PHYS;i++) {
+ result=EMM_MapPage(i,emm_mappings[i].handle,emm_mappings[i].page);
+ }
+ return EMM_NO_ERROR;
+}
+static Bit8u EMM_RestorePageMap(Bit16u handle) {
+ /* Check for valid handle */
+ if (handle>=EMM_MAX_HANDLES || emm_handles[handle].pages==NULL_HANDLE) {
+ if (handle!=0) return EMM_INVALID_HANDLE;
+ }
+ /* Check for previous save */
+ if (!emm_handles[handle].saved_page_map) return EMM_NO_SAVED_PAGE_MAP;
+ /* Restore the mappings */
+ emm_handles[handle].saved_page_map=false;
+ for (Bitu i=0;i<EMM_MAX_PHYS;i++) {
+ emm_mappings[i].page=emm_handles[handle].page_map[i].page;
+ emm_mappings[i].handle=emm_handles[handle].page_map[i].handle;
+ }
+ return EMM_RestoreMappingTable();
+}
+
+static Bit8u EMM_GetPagesForAllHandles(PhysPt table,Bit16u & handles) {
+ handles=0;
+ for (Bit16u i=0;i<EMM_MAX_HANDLES;i++) {
+ if (emm_handles[i].pages!=NULL_HANDLE) {
+ handles++;
+ mem_writew(table,i);
+ mem_writew(table+2,emm_handles[i].pages);
+ table+=4;
+ }
+ }
+ return EMM_NO_ERROR;
+}
+
+static Bit8u EMM_PartialPageMapping(void) {
+ PhysPt list,data;Bit16u count;
+ switch (reg_al) {
+ case 0x00: /* Save Partial Page Map */
+ list = SegPhys(ds)+reg_si;
+ data = SegPhys(es)+reg_di;
+ count=mem_readw(list);list+=2;
+ mem_writew(data,count);data+=2;
+ for (;count>0;count--) {
+ Bit16u segment=mem_readw(list);list+=2;
+ if ((segment>=EMM_PAGEFRAME) && (segment<EMM_PAGEFRAME+0x1000)) {
+ Bit16u page = (segment-EMM_PAGEFRAME) / (EMM_PAGE_SIZE>>4);
+ mem_writew(data,segment);data+=2;
+ MEM_BlockWrite(data,&emm_mappings[page],sizeof(EMM_Mapping));
+ data+=sizeof(EMM_Mapping);
+ } else if ((ems_type==1) || (ems_type==3) || ((segment>=EMM_PAGEFRAME-0x1000) && (segment<EMM_PAGEFRAME)) || ((segment>=0xa000) && (segment<0xb000))) {
+ mem_writew(data,segment);data+=2;
+ MEM_BlockWrite(data,&emm_segmentmappings[segment>>10],sizeof(EMM_Mapping));
+ data+=sizeof(EMM_Mapping);
+ } else {
+ return EMM_ILL_PHYS;
+ }
+ }
+ break;
+ case 0x01: /* Restore Partial Page Map */
+ data = SegPhys(ds)+reg_si;
+ count= mem_readw(data);data+=2;
+ for (;count>0;count--) {
+ Bit16u segment=mem_readw(data);data+=2;
+ if ((segment>=EMM_PAGEFRAME) && (segment<EMM_PAGEFRAME+0x1000)) {
+ Bit16u page = (segment-EMM_PAGEFRAME) / (EMM_PAGE_SIZE>>4);
+ MEM_BlockRead(data,&emm_mappings[page],sizeof(EMM_Mapping));
+ } else if ((ems_type==1) || (ems_type==3) || ((segment>=EMM_PAGEFRAME-0x1000) && (segment<EMM_PAGEFRAME)) || ((segment>=0xa000) && (segment<0xb000))) {
+ MEM_BlockRead(data,&emm_segmentmappings[segment>>10],sizeof(EMM_Mapping));
+ } else {
+ return EMM_ILL_PHYS;
+ }
+ data+=sizeof(EMM_Mapping);
+ }
+ return EMM_RestoreMappingTable();
+ break;
+ case 0x02: /* Get Partial Page Map Array Size */
+ reg_al=(Bit8u)(2+reg_bx*(2+sizeof(EMM_Mapping)));
+ break;
+ default:
+ LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
+ return EMM_FUNC_NOSUP;
+ }
+ return EMM_NO_ERROR;
+}
+
+static Bit8u HandleNameSearch(void) {
+ char name[9];
+ Bit16u handle=0;PhysPt data;
+ switch (reg_al) {
+ case 0x00: /* Get all handle names */
+ reg_al=0;data=SegPhys(es)+reg_di;
+ for (handle=0;handle<EMM_MAX_HANDLES;handle++) {
+ if (emm_handles[handle].pages!=NULL_HANDLE) {
+ reg_al++;
+ mem_writew(data,handle);
+ MEM_BlockWrite(data+2,emm_handles[handle].name,8);
+ data+=10;
+ }
+ }
+ break;
+ case 0x01: /* Search for a handle name */
+ MEM_StrCopy(SegPhys(ds)+reg_si,name,8);name[8]=0;
+ for (handle=0;handle<EMM_MAX_HANDLES;handle++) {
+ if (emm_handles[handle].pages!=NULL_HANDLE) {
+ if (!strncmp(name,emm_handles[handle].name,8)) {
+ reg_dx=handle;
+ return EMM_NO_ERROR;
+ }
+ }
+ }
+ return EMM_NOT_FOUND;
+ break;
+ case 0x02: /* Get Total number of handles */
+ reg_bx=EMM_MAX_HANDLES;
+ break;
+ default:
+ LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
+ return EMM_INVALID_SUB;
+ }
+ return EMM_NO_ERROR;
+}
+
+static Bit8u GetSetHandleName(void) {
+ Bit16u handle=reg_dx;
+ switch (reg_al) {
+ case 0x00: /* Get Handle Name */
+ if (handle>=EMM_MAX_HANDLES || emm_handles[handle].pages==NULL_HANDLE) return EMM_INVALID_HANDLE;
+ MEM_BlockWrite(SegPhys(es)+reg_di,emm_handles[handle].name,8);
+ break;
+ case 0x01: /* Set Handle Name */
+ if (handle>=EMM_MAX_HANDLES || emm_handles[handle].pages==NULL_HANDLE) return EMM_INVALID_HANDLE;
+ MEM_BlockRead(SegPhys(es)+reg_di,emm_handles[handle].name,8);
+ break;
+ default:
+ LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
+ return EMM_INVALID_SUB;
+ }
+ return EMM_NO_ERROR;
+
+}
+
+
+static void LoadMoveRegion(PhysPt data,MoveRegion & region) {
+ region.bytes=mem_readd(data+0x0);
+
+ region.src_type=mem_readb(data+0x4);
+ region.src_handle=mem_readw(data+0x5);
+ region.src_offset=mem_readw(data+0x7);
+ region.src_page_seg=mem_readw(data+0x9);
+
+ region.dest_type=mem_readb(data+0xb);
+ region.dest_handle=mem_readw(data+0xc);
+ region.dest_offset=mem_readw(data+0xe);
+ region.dest_page_seg=mem_readw(data+0x10);
+}
+
+static Bit8u MemoryRegion(void) {
+ MoveRegion region;
+ Bit8u buf_src[MEM_PAGE_SIZE];
+ Bit8u buf_dest[MEM_PAGE_SIZE];
+ if (reg_al>1) {
+ LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
+ return EMM_FUNC_NOSUP;
+ }
+ LoadMoveRegion(SegPhys(ds)+reg_si,region);
+ /* Parse the region for information */
+ PhysPt src_mem = 0,dest_mem = 0;
+ MemHandle src_handle = 0,dest_handle = 0;
+ Bitu src_off = 0,dest_off = 0 ;Bitu src_remain = 0,dest_remain = 0;
+ if (!region.src_type) {
+ src_mem=region.src_page_seg*16+region.src_offset;
+ } else {
+ if (!ValidHandle(region.src_handle)) return EMM_INVALID_HANDLE;
+ if ((emm_handles[region.src_handle].pages*EMM_PAGE_SIZE) < ((region.src_page_seg*EMM_PAGE_SIZE)+region.src_offset+region.bytes)) return EMM_LOG_OUT_RANGE;
+ src_handle=emm_handles[region.src_handle].mem;
+ Bitu pages=region.src_page_seg*4+(region.src_offset/MEM_PAGE_SIZE);
+ for (;pages>0;pages--) src_handle=MEM_NextHandle(src_handle);
+ src_off=region.src_offset&(MEM_PAGE_SIZE-1);
+ src_remain=MEM_PAGE_SIZE-src_off;
+ }
+ if (!region.dest_type) {
+ dest_mem=region.dest_page_seg*16+region.dest_offset;
+ } else {
+ if (!ValidHandle(region.dest_handle)) return EMM_INVALID_HANDLE;
+ if (emm_handles[region.dest_handle].pages*EMM_PAGE_SIZE < (region.dest_page_seg*EMM_PAGE_SIZE)+region.dest_offset+region.bytes) return EMM_LOG_OUT_RANGE;
+ dest_handle=emm_handles[region.dest_handle].mem;
+ Bitu pages=region.dest_page_seg*4+(region.dest_offset/MEM_PAGE_SIZE);
+ for (;pages>0;pages--) dest_handle=MEM_NextHandle(dest_handle);
+ dest_off=region.dest_offset&(MEM_PAGE_SIZE-1);
+ dest_remain=MEM_PAGE_SIZE-dest_off;
+ }
+ Bitu toread;
+ while (region.bytes>0) {
+ if (region.bytes>MEM_PAGE_SIZE) toread=MEM_PAGE_SIZE;
+ else toread=region.bytes;
+ /* Read from the source */
+ if (!region.src_type) {
+ MEM_BlockRead(src_mem,buf_src,toread);
+ } else {
+ if (toread<src_remain) {
+ MEM_BlockRead((src_handle*MEM_PAGE_SIZE)+src_off,buf_src,toread);
+ } else {
+ MEM_BlockRead((src_handle*MEM_PAGE_SIZE)+src_off,buf_src,src_remain);
+ MEM_BlockRead((MEM_NextHandle(src_handle)*MEM_PAGE_SIZE),&buf_src[src_remain],toread-src_remain);
+ }
+ }
+ /* Check for a move */
+ if (reg_al==1) {
+ /* Read from the destination */
+ if (!region.dest_type) {
+ MEM_BlockRead(dest_mem,buf_dest,toread);
+ } else {
+ if (toread<dest_remain) {
+ MEM_BlockRead((dest_handle*MEM_PAGE_SIZE)+dest_off,buf_dest,toread);
+ } else {
+ MEM_BlockRead((dest_handle*MEM_PAGE_SIZE)+dest_off,buf_dest,dest_remain);
+ MEM_BlockRead((MEM_NextHandle(dest_handle)*MEM_PAGE_SIZE),&buf_dest[dest_remain],toread-dest_remain);
+ }
+ }
+ /* Write to the source */
+ if (!region.src_type) {
+ MEM_BlockWrite(src_mem,buf_dest,toread);
+ } else {
+ if (toread<src_remain) {
+ MEM_BlockWrite((src_handle*MEM_PAGE_SIZE)+src_off,buf_dest,toread);
+ } else {
+ MEM_BlockWrite((src_handle*MEM_PAGE_SIZE)+src_off,buf_dest,src_remain);
+ MEM_BlockWrite((MEM_NextHandle(src_handle)*MEM_PAGE_SIZE),&buf_dest[src_remain],toread-src_remain);
+ }
+ }
+ }
+ /* Write to the destination */
+ if (!region.dest_type) {
+ MEM_BlockWrite(dest_mem,buf_src,toread);
+ } else {
+ if (toread<dest_remain) {
+ MEM_BlockWrite((dest_handle*MEM_PAGE_SIZE)+dest_off,buf_src,toread);
+ } else {
+ MEM_BlockWrite((dest_handle*MEM_PAGE_SIZE)+dest_off,buf_src,dest_remain);
+ MEM_BlockWrite((MEM_NextHandle(dest_handle)*MEM_PAGE_SIZE),&buf_src[dest_remain],toread-dest_remain);
+ }
+ }
+ /* Advance the pointers */
+ if (!region.src_type) src_mem+=toread;
+ else src_handle=MEM_NextHandle(src_handle);
+ if (!region.dest_type) dest_mem+=toread;
+ else dest_handle=MEM_NextHandle(dest_handle);
+ region.bytes-=toread;
+ }
+ return EMM_NO_ERROR;
+}
+
+
+static Bitu INT67_Handler(void) {
+ Bitu i;
+ switch (reg_ah) {
+ case 0x40: /* Get Status */
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0x41: /* Get PageFrame Segment */
+ reg_bx=EMM_PAGEFRAME;
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0x42: /* Get number of pages */
+ reg_dx=(Bit16u)(MEM_TotalPages()/4); //Not entirely correct but okay
+ reg_bx=EMM_GetFreePages();
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0x43: /* Get Handle and Allocate Pages */
+ reg_ah=EMM_AllocateMemory(reg_bx,reg_dx,false);
+ break;
+ case 0x44: /* Map Expanded Memory Page */
+ reg_ah=EMM_MapPage(reg_al,reg_dx,reg_bx);
+ break;
+ case 0x45: /* Release handle and free pages */
+ reg_ah=EMM_ReleaseMemory(reg_dx);
+ break;
+ case 0x46: /* Get EMM Version */
+ reg_ah=EMM_NO_ERROR;
+ reg_al=EMM_VERSION;
+ break;
+ case 0x47: /* Save Page Map */
+ reg_ah=EMM_SavePageMap(reg_dx);
+ break;
+ case 0x48: /* Restore Page Map */
+ reg_ah=EMM_RestorePageMap(reg_dx);
+ break;
+ case 0x4b: /* Get Handle Count */
+ reg_bx=0;
+ for (i=0;i<EMM_MAX_HANDLES;i++) if (emm_handles[i].pages!=NULL_HANDLE) reg_bx++;
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0x4c: /* Get Pages for one Handle */
+ if (!ValidHandle(reg_dx)) {reg_ah=EMM_INVALID_HANDLE;break;}
+ reg_bx=emm_handles[reg_dx].pages;
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0x4d: /* Get Pages for all Handles */
+ reg_ah=EMM_GetPagesForAllHandles(SegPhys(es)+reg_di,reg_bx);
+ break;
+ case 0x4e: /*Save/Restore Page Map */
+ switch (reg_al) {
+ case 0x00: /* Save Page Map */
+ MEM_BlockWrite(SegPhys(es)+reg_di,emm_mappings,sizeof(emm_mappings));
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0x01: /* Restore Page Map */
+ MEM_BlockRead(SegPhys(ds)+reg_si,emm_mappings,sizeof(emm_mappings));
+ reg_ah=EMM_RestoreMappingTable();
+ break;
+ case 0x02: /* Save and Restore Page Map */
+ MEM_BlockWrite(SegPhys(es)+reg_di,emm_mappings,sizeof(emm_mappings));
+ MEM_BlockRead(SegPhys(ds)+reg_si,emm_mappings,sizeof(emm_mappings));
+ reg_ah=EMM_RestoreMappingTable();
+ break;
+ case 0x03: /* Get Page Map Array Size */
+ reg_al=sizeof(emm_mappings);
+ reg_ah=EMM_NO_ERROR;
+ break;
+ default:
+ LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
+ reg_ah=EMM_INVALID_SUB;
+ break;
+ }
+ break;
+ case 0x4f: /* Save/Restore Partial Page Map */
+ reg_ah=EMM_PartialPageMapping();
+ break;
+ case 0x50: /* Map/Unmap multiple handle pages */
+ reg_ah = EMM_NO_ERROR;
+ switch (reg_al) {
+ case 0x00: // use physical page numbers
+ { PhysPt data = SegPhys(ds)+reg_si;
+ for (int i=0; i<reg_cx; i++) {
+ Bit16u logPage = mem_readw(data); data+=2;
+ Bit16u physPage = mem_readw(data); data+=2;
+ reg_ah = EMM_MapPage(physPage,reg_dx,logPage);
+ if (reg_ah!=EMM_NO_ERROR) break;
+ };
+ } break;
+ case 0x01: // use segment address
+ { PhysPt data = SegPhys(ds)+reg_si;
+ for (int i=0; i<reg_cx; i++) {
+ Bit16u logPage = mem_readw(data); data+=2;
+ reg_ah = EMM_MapSegment(mem_readw(data),reg_dx,logPage); data+=2;
+ if (reg_ah!=EMM_NO_ERROR) break;
+ };
+ }
+ break;
+ default:
+ LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
+ reg_ah=EMM_INVALID_SUB;
+ break;
+ }
+ break;
+ case 0x51: /* Reallocate Pages */
+ reg_ah=EMM_ReallocatePages(reg_dx,reg_bx);
+ break;
+ case 0x53: // Set/Get Handlename
+ reg_ah=GetSetHandleName();
+ break;
+ case 0x54: /* Handle Functions */
+ reg_ah=HandleNameSearch();
+ break;
+ case 0x57: /* Memory region */
+ reg_ah=MemoryRegion();
+ if (reg_ah) LOG(LOG_MISC,LOG_ERROR)("EMS:Function 57 move failed");
+ break;
+ case 0x58: // Get mappable physical array address array
+ if (reg_al==0x00) {
+ PhysPt data = SegPhys(es)+reg_di;
+ Bit16u step = 0x1000 / EMM_MAX_PHYS;
+ for (Bit16u i=0; i<EMM_MAX_PHYS; i++) {
+ mem_writew(data,EMM_PAGEFRAME+step*i); data+=2;
+ mem_writew(data,i); data+=2;
+ };
+ };
+ // Set number of pages
+ reg_cx = EMM_MAX_PHYS;
+ reg_ah = EMM_NO_ERROR;
+ break;
+ case 0x5A: /* Allocate standard/raw Pages */
+ if (reg_al<=0x01) {
+ reg_ah=EMM_AllocateMemory(reg_bx,reg_dx,true); // can allocate 0 pages
+ } else {
+ LOG(LOG_MISC,LOG_ERROR)("EMS:Call 5A subfct %2X not supported",reg_al);
+ reg_ah=EMM_INVALID_SUB;
+ };
+ break;
+ case 0xDE: /* VCPI Functions */
+ if (!vcpi.enabled) {
+ LOG(LOG_MISC,LOG_ERROR)("EMS:VCPI Call %2X not supported",reg_al);
+ reg_ah=EMM_FUNC_NOSUP;
+ } else {
+ switch (reg_al) {
+ case 0x00: /* VCPI Installation Check */
+ if (((reg_cx==0) && (reg_di==0x0012)) || (cpu.pmode && (reg_flags & FLAG_VM))) {
+ /* JEMM detected or already in v86 mode */
+ reg_ah=EMM_NO_ERROR;
+ reg_bx=0x100;
+ } else {
+ reg_ah=EMM_FUNC_NOSUP;
+ }
+ break;
+ case 0x01: { /* VCPI Get Protected Mode Interface */
+ Bit16u ct;
+ /* Set up page table buffer */
+ for (ct=0; ct<0xff; ct++) {
+ real_writeb(SegValue(es),reg_di+ct*4+0x00,0x67); // access bits
+ real_writew(SegValue(es),reg_di+ct*4+0x01,ct*0x10); // mapping
+ real_writeb(SegValue(es),reg_di+ct*4+0x03,0x00);
+ }
+ for (ct=0xff; ct<0x100; ct++) {
+ real_writeb(SegValue(es),reg_di+ct*4+0x00,0x67); // access bits
+ real_writew(SegValue(es),reg_di+ct*4+0x01,(ct-0xff)*0x10+0x1100); // mapping
+ real_writeb(SegValue(es),reg_di+ct*4+0x03,0x00);
+ }
+ /* adjust paging entries for page frame (if mapped) */
+ for (ct=0; ct<4; ct++) {
+ Bit16u handle=emm_mappings[ct].handle;
+ if (handle!=0xffff) {
+ Bit16u memh=(Bit16u)MEM_NextHandleAt(emm_handles[handle].mem,emm_mappings[ct].page*4);
+ Bit16u entry_addr=reg_di+(EMM_PAGEFRAME>>6)+(ct*0x10);
+ real_writew(SegValue(es),entry_addr+0x00+0x01,(memh+0)*0x10); // mapping of 1/4 of page
+ real_writew(SegValue(es),entry_addr+0x04+0x01,(memh+1)*0x10); // mapping of 2/4 of page
+ real_writew(SegValue(es),entry_addr+0x08+0x01,(memh+2)*0x10); // mapping of 3/4 of page
+ real_writew(SegValue(es),entry_addr+0x0c+0x01,(memh+3)*0x10); // mapping of 4/4 of page
+ }
+ }
+ reg_di+=0x400; // advance pointer by 0x100*4
+
+ /* Set up three descriptor table entries */
+ Bit32u cbseg_low=(CALLBACK_GetBase()&0xffff)<<16;
+ Bit32u cbseg_high=(CALLBACK_GetBase()&0x1f0000)>>16;
+ /* Descriptor 1 (code segment, callback segment) */
+ real_writed(SegValue(ds),reg_si+0x00,0x0000ffff|cbseg_low);
+ real_writed(SegValue(ds),reg_si+0x04,0x00009a00|cbseg_high);
+ /* Descriptor 2 (data segment, full access) */
+ real_writed(SegValue(ds),reg_si+0x08,0x0000ffff);
+ real_writed(SegValue(ds),reg_si+0x0c,0x00009200);
+ /* Descriptor 3 (full access) */
+ real_writed(SegValue(ds),reg_si+0x10,0x0000ffff);
+ real_writed(SegValue(ds),reg_si+0x14,0x00009200);
+
+ reg_ebx=(vcpi.pm_interface&0xffff);
+ reg_ah=EMM_NO_ERROR;
+ break;
+ }
+ case 0x02: /* VCPI Maximum Physical Address */
+ reg_edx=((MEM_TotalPages()*MEM_PAGESIZE)-1)&0xfffff000;
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0x03: /* VCPI Get Number of Free Pages */
+ reg_edx=MEM_FreeTotal();
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0x04: { /* VCPI Allocate one Page */
+ MemHandle mem = MEM_AllocatePages(1,false);
+ if (mem) {
+ reg_edx=mem<<12;
+ reg_ah=EMM_NO_ERROR;
+ } else {
+ reg_ah=EMM_OUT_OF_LOG;
+ }
+ break;
+ }
+ case 0x05: /* VCPI Free Page */
+ MEM_ReleasePages(reg_edx>>12);
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0x06: { /* VCPI Get Physical Address of Page in 1st MB */
+ if (((reg_cx<<8)>=EMM_PAGEFRAME) && ((reg_cx<<8)<EMM_PAGEFRAME+0x1000)) {
+ /* Page is in Pageframe, so check what EMS-page it is
+ and return the physical address */
+ Bit8u phys_page;
+ Bit16u mem_seg=reg_cx<<8;
+ if (mem_seg<EMM_PAGEFRAME+0x400) phys_page=0;
+ else if (mem_seg<EMM_PAGEFRAME+0x800) phys_page=1;
+ else if (mem_seg<EMM_PAGEFRAME+0xc00) phys_page=2;
+ else phys_page=3;
+ Bit16u handle=emm_mappings[phys_page].handle;
+ if (handle==0xffff) {
+ reg_ah=EMM_ILL_PHYS;
+ break;
+ } else {
+ MemHandle memh=MEM_NextHandleAt(
+ emm_handles[handle].mem,
+ emm_mappings[phys_page].page*4);
+ reg_edx=(memh+(reg_cx&3))<<12;
+ }
+ } else {
+ /* Page not in Pageframe, so just translate into physical address */
+ reg_edx=reg_cx<<12;
+ }
+
+ reg_ah=EMM_NO_ERROR;
+ }
+ break;
+ case 0x0a: /* VCPI Get PIC Vector Mappings */
+ reg_bx=vcpi.pic1_remapping; // master PIC
+ reg_cx=vcpi.pic2_remapping; // slave PIC
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0x0b: /* VCPI Set PIC Vector Mappings */
+ reg_flags&=(~FLAG_IF);
+ vcpi.pic1_remapping=reg_bx&0xff;
+ vcpi.pic2_remapping=reg_cx&0xff;
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0x0c: { /* VCPI Switch from V86 to Protected Mode */
+ reg_flags&=(~FLAG_IF);
+ cpu.cpl=0;
+
+ /* Read data from ESI (linear address) */
+ Bit32u new_cr3=mem_readd(reg_esi);
+ Bit32u new_gdt_addr=mem_readd(reg_esi+4);
+ Bit32u new_idt_addr=mem_readd(reg_esi+8);
+ Bit16u new_ldt=mem_readw(reg_esi+0x0c);
+ Bit16u new_tr=mem_readw(reg_esi+0x0e);
+ Bit32u new_eip=mem_readd(reg_esi+0x10);
+ Bit16u new_cs=mem_readw(reg_esi+0x14);
+
+ /* Get GDT and IDT entries */
+ Bit16u new_gdt_limit=mem_readw(new_gdt_addr);
+ Bit32u new_gdt_base=mem_readd(new_gdt_addr+2);
+ Bit16u new_idt_limit=mem_readw(new_idt_addr);
+ Bit32u new_idt_base=mem_readd(new_idt_addr+2);
+
+ /* Switch to protected mode, paging enabled if necessary */
+ Bit32u new_cr0=CPU_GET_CRX(0)|1;
+ if (new_cr3!=0) new_cr0|=0x80000000;
+ CPU_SET_CRX(0, new_cr0);
+ CPU_SET_CRX(3, new_cr3);
+
+ PhysPt tbaddr=new_gdt_base+(new_tr&0xfff8)+5;
+ Bit8u tb=mem_readb(tbaddr);
+ mem_writeb(tbaddr, tb&0xfd);
+
+ /* Load tables and initialize segment registers */
+ CPU_LGDT(new_gdt_limit, new_gdt_base);
+ CPU_LIDT(new_idt_limit, new_idt_base);
+ if (CPU_LLDT(new_ldt)) LOG_MSG("VCPI: Could not load LDT with %x",new_ldt);
+ if (CPU_LTR(new_tr)) LOG_MSG("VCPI: Could not load TR with %x",new_tr);
+
+ CPU_SetSegGeneral(ds,0);
+ CPU_SetSegGeneral(es,0);
+ CPU_SetSegGeneral(fs,0);
+ CPU_SetSegGeneral(gs,0);
+
+// MEM_A20_Enable(true);
+
+ /* Switch to protected mode */
+ reg_flags&=(~(FLAG_VM|FLAG_NT));
+ reg_flags|=0x3000;
+ CPU_JMP(true, new_cs, new_eip, 0);
+ }
+ break;
+ default:
+ LOG(LOG_MISC,LOG_ERROR)("EMS:VCPI Call %x not supported",reg_ax);
+ reg_ah=EMM_FUNC_NOSUP;
+ break;
+ }
+ }
+ break;
+ default:
+ LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X not supported",reg_ah);
+ reg_ah=EMM_FUNC_NOSUP;
+ break;
+ }
+ return CBRET_NONE;
+}
+
+static Bitu VCPI_PM_Handler() {
+// LOG_MSG("VCPI PMODE handler, function %x",reg_ax);
+ switch (reg_ax) {
+ case 0xDE03: /* VCPI Get Number of Free Pages */
+ reg_edx=MEM_FreeTotal();
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0xDE04: { /* VCPI Allocate one Page */
+ MemHandle mem = MEM_AllocatePages(1,false);
+ if (mem) {
+ reg_edx=mem<<12;
+ reg_ah=EMM_NO_ERROR;
+ } else {
+ reg_ah=EMM_OUT_OF_LOG;
+ }
+ break;
+ }
+ case 0xDE05: /* VCPI Free Page */
+ MEM_ReleasePages(reg_edx>>12);
+ reg_ah=EMM_NO_ERROR;
+ break;
+ case 0xDE0C: { /* VCPI Switch from Protected Mode to V86 */
+ reg_flags&=(~FLAG_IF);
+
+ /* Flags need to be filled in, VM=true, IOPL=3 */
+ mem_writed(SegPhys(ss) + (reg_esp & cpu.stack.mask)+0x10, 0x23002);
+
+ /* Disable Paging */
+ CPU_SET_CRX(0, CPU_GET_CRX(0)&0x7ffffff7);
+ CPU_SET_CRX(3, 0);
+
+ PhysPt tbaddr=vcpi.private_area+0x0000+(0x10&0xfff8)+5;
+ Bit8u tb=mem_readb(tbaddr);
+ mem_writeb(tbaddr, tb&0xfd);
+
+ /* Load descriptor table registers */
+ CPU_LGDT(0xff, vcpi.private_area+0x0000);
+ CPU_LIDT(0x7ff, vcpi.private_area+0x2000);
+ if (CPU_LLDT(0x08)) LOG_MSG("VCPI: Could not load LDT");
+ if (CPU_LTR(0x10)) LOG_MSG("VCPI: Could not load TR");
+
+ reg_flags&=(~FLAG_NT);
+ reg_esp+=8; // skip interrupt return information
+// MEM_A20_Enable(false);
+
+ /* Switch to v86-task */
+ CPU_IRET(true,0);
+ }
+ break;
+ default:
+ LOG(LOG_MISC,LOG_WARN)("Unhandled VCPI-function %x in protected mode",reg_al);
+ break;
+ }
+ return CBRET_NONE;
+}
+
+static Bitu V86_Monitor() {
+ /* Calculate which interrupt did occur */
+ Bitu int_num=(mem_readw(SegPhys(ss)+(reg_esp & cpu.stack.mask))-0x2803);
+
+ /* See if Exception 0x0d and not Interrupt 0x0d */
+ if ((int_num==(0x0d*4)) && ((reg_sp&0xffff)!=0x1fda)) {
+ /* Protection violation during V86-execution,
+ needs intervention by monitor (depends on faulting opcode) */
+
+ reg_esp+=6; // skip ip of CALL and error code of EXCEPTION 0x0d
+
+ /* Get address of faulting instruction */
+ Bit16u v86_cs=mem_readw(SegPhys(ss)+((reg_esp+4) & cpu.stack.mask));
+ Bit16u v86_ip=mem_readw(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask));
+ Bit8u v86_opcode=mem_readb((v86_cs<<4)+v86_ip);
+// LOG_MSG("v86 monitor caught protection violation at %x:%x, opcode=%x",v86_cs,v86_ip,v86_opcode);
+ switch (v86_opcode) {
+ case 0x0f: // double byte opcode
+ v86_opcode=mem_readb((v86_cs<<4)+v86_ip+1);
+ switch (v86_opcode) {
+ case 0x20: { // mov reg,CRx
+ Bitu rm_val=mem_readb((v86_cs<<4)+v86_ip+2);
+ Bitu which=(rm_val >> 3) & 7;
+ if ((rm_val<0xc0) || (rm_val>=0xe8))
+ E_Exit("Invalid opcode 0x0f 0x20 %x caused a protection fault!",static_cast<unsigned int>(rm_val));
+ Bit32u crx=CPU_GET_CRX(which);
+ switch (rm_val&7) {
+ case 0: reg_eax=crx; break;
+ case 1: reg_ecx=crx; break;
+ case 2: reg_edx=crx; break;
+ case 3: reg_ebx=crx; break;
+ case 4: reg_esp=crx; break;
+ case 5: reg_ebp=crx; break;
+ case 6: reg_esi=crx; break;
+ case 7: reg_edi=crx; break;
+ }
+ mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+3);
+ }
+ break;
+ case 0x22: { // mov CRx,reg
+ Bitu rm_val=mem_readb((v86_cs<<4)+v86_ip+2);
+ Bitu which=(rm_val >> 3) & 7;
+ if ((rm_val<0xc0) || (rm_val>=0xe8))
+ E_Exit("Invalid opcode 0x0f 0x22 %x caused a protection fault!",static_cast<unsigned int>(rm_val));
+ Bit32u crx=0;
+ switch (rm_val&7) {
+ case 0: crx=reg_eax; break;
+ case 1: crx=reg_ecx; break;
+ case 2: crx=reg_edx; break;
+ case 3: crx=reg_ebx; break;
+ case 4: crx=reg_esp; break;
+ case 5: crx=reg_ebp; break;
+ case 6: crx=reg_esi; break;
+ case 7: crx=reg_edi; break;
+ }
+ if (which==0) crx|=1; // protection bit always on
+ CPU_SET_CRX(which,crx);
+ mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+3);
+ }
+ break;
+ default:
+ E_Exit("Unhandled opcode 0x0f %x caused a protection fault!",v86_opcode);
+ }
+ break;
+ case 0xe4: // IN AL,Ib
+ reg_al=(Bit8u)(IO_ReadB(mem_readb((v86_cs<<4)+v86_ip+1))&0xff);
+ mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+2);
+ break;
+ case 0xe5: // IN AX,Ib
+ reg_ax=(Bit16u)(IO_ReadW(mem_readb((v86_cs<<4)+v86_ip+1))&0xffff);
+ mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+2);
+ break;
+ case 0xe6: // OUT Ib,AL
+ IO_WriteB(mem_readb((v86_cs<<4)+v86_ip+1),reg_al);
+ mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+2);
+ break;
+ case 0xe7: // OUT Ib,AX
+ IO_WriteW(mem_readb((v86_cs<<4)+v86_ip+1),reg_ax);
+ mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+2);
+ break;
+ case 0xec: // IN AL,DX
+ reg_al=(Bit8u)(IO_ReadB(reg_dx)&0xff);
+ mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+1);
+ break;
+ case 0xed: // IN AX,DX
+ reg_ax=(Bit16u)(IO_ReadW(reg_dx)&0xffff);
+ mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+1);
+ break;
+ case 0xee: // OUT DX,AL
+ IO_WriteB(reg_dx,reg_al);
+ mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+1);
+ break;
+ case 0xef: // OUT DX,AX
+ IO_WriteW(reg_dx,reg_ax);
+ mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+1);
+ break;
+ case 0xf0: // LOCK prefix
+ mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+1);
+ break;
+ case 0xf4: // HLT
+ reg_flags|=FLAG_IF;
+ CPU_HLT(reg_eip);
+ mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+1);
+ break;
+ default:
+ E_Exit("Unhandled opcode %x caused a protection fault!",v86_opcode);
+ }
+ return CBRET_NONE;
+ }
+
+ /* Get address to interrupt handler */
+ Bit16u vint_vector_seg=mem_readw(SegValue(ds)+int_num+2);
+ Bit16u vint_vector_ofs=mem_readw(int_num);
+ if (reg_sp!=0x1fda) reg_esp+=(2+3*4); // Interrupt from within protected mode
+ else reg_esp+=2;
+
+ /* Read entries that were pushed onto the stack by the interrupt */
+ Bit16u return_ip=mem_readw(SegPhys(ss)+(reg_esp & cpu.stack.mask));
+ Bit16u return_cs=mem_readw(SegPhys(ss)+((reg_esp+4) & cpu.stack.mask));
+ Bit32u return_eflags=mem_readd(SegPhys(ss)+((reg_esp+8) & cpu.stack.mask));
+
+ /* Modify stack to call v86-interrupt handler */
+ mem_writed(SegPhys(ss)+(reg_esp & cpu.stack.mask),vint_vector_ofs);
+ mem_writed(SegPhys(ss)+((reg_esp+4) & cpu.stack.mask),vint_vector_seg);
+ mem_writed(SegPhys(ss)+((reg_esp+8) & cpu.stack.mask),return_eflags&(~(FLAG_IF|FLAG_TF)));
+
+ /* Adjust SP of v86-stack */
+ Bit16u v86_ss=mem_readw(SegPhys(ss)+((reg_esp+0x10) & cpu.stack.mask));
+ Bit16u v86_sp=mem_readw(SegPhys(ss)+((reg_esp+0x0c) & cpu.stack.mask))-6;
+ mem_writew(SegPhys(ss)+((reg_esp+0x0c) & cpu.stack.mask),v86_sp);
+
+ /* Return to original code after v86-interrupt handler */
+ mem_writew((v86_ss<<4)+v86_sp+0,return_ip);
+ mem_writew((v86_ss<<4)+v86_sp+2,return_cs);
+ mem_writew((v86_ss<<4)+v86_sp+4,(Bit16u)(return_eflags&0xffff));
+ return CBRET_NONE;
+}
+
+static void SetupVCPI() {
+ vcpi.enabled=false;
+
+ vcpi.ems_handle=0; // use EMM system handle for VCPI data
+
+ vcpi.enabled=true;
+
+ vcpi.pic1_remapping=0x08; // master PIC base
+ vcpi.pic2_remapping=0x70; // slave PIC base
+
+ vcpi.private_area=emm_handles[vcpi.ems_handle].mem<<12;
+
+ /* GDT */
+ mem_writed(vcpi.private_area+0x0000,0x00000000); // descriptor 0
+ mem_writed(vcpi.private_area+0x0004,0x00000000); // descriptor 0
+
+ Bit32u ldt_address=(vcpi.private_area+0x1000);
+ Bit16u ldt_limit=0xff;
+ Bit32u ldt_desc_part=((ldt_address&0xffff)<<16)|ldt_limit;
+ mem_writed(vcpi.private_area+0x0008,ldt_desc_part); // descriptor 1 (LDT)
+ ldt_desc_part=((ldt_address&0xff0000)>>16)|(ldt_address&0xff000000)|0x8200;
+ mem_writed(vcpi.private_area+0x000c,ldt_desc_part); // descriptor 1
+
+ Bit32u tss_address=(vcpi.private_area+0x3000);
+ Bit32u tss_desc_part=((tss_address&0xffff)<<16)|(0x0068+0x200);
+ mem_writed(vcpi.private_area+0x0010,tss_desc_part); // descriptor 2 (TSS)
+ tss_desc_part=((tss_address&0xff0000)>>16)|(tss_address&0xff000000)|0x8900;
+ mem_writed(vcpi.private_area+0x0014,tss_desc_part); // descriptor 2
+
+ /* LDT */
+ mem_writed(vcpi.private_area+0x1000,0x00000000); // descriptor 0
+ mem_writed(vcpi.private_area+0x1004,0x00000000); // descriptor 0
+ Bit32u cs_desc_part=((vcpi.private_area&0xffff)<<16)|0xffff;
+ mem_writed(vcpi.private_area+0x1008,cs_desc_part); // descriptor 1 (code)
+ cs_desc_part=((vcpi.private_area&0xff0000)>>16)|(vcpi.private_area&0xff000000)|0x9a00;
+ mem_writed(vcpi.private_area+0x100c,cs_desc_part); // descriptor 1
+ Bit32u ds_desc_part=((vcpi.private_area&0xffff)<<16)|0xffff;
+ mem_writed(vcpi.private_area+0x1010,ds_desc_part); // descriptor 2 (data)
+ ds_desc_part=((vcpi.private_area&0xff0000)>>16)|(vcpi.private_area&0xff000000)|0x9200;
+ mem_writed(vcpi.private_area+0x1014,ds_desc_part); // descriptor 2
+
+ /* IDT setup */
+ for (Bit16u int_ct=0; int_ct<0x100; int_ct++) {
+ /* build a CALL NEAR V86MON, the value of IP pushed by the
+ CALL is used to identify the interrupt number */
+ mem_writeb(vcpi.private_area+0x2800+int_ct*4+0,0xe8); // call
+ mem_writew(vcpi.private_area+0x2800+int_ct*4+1,0x05fd-(int_ct*4));
+ mem_writeb(vcpi.private_area+0x2800+int_ct*4+3,0xcf); // iret (dummy)
+
+ /* put a Gate-Descriptor into the IDT */
+ mem_writed(vcpi.private_area+0x2000+int_ct*8+0,0x000c0000|(0x2800+int_ct*4));
+ mem_writed(vcpi.private_area+0x2000+int_ct*8+4,0x0000ee00);
+ }
+
+ /* TSS */
+ for (Bitu tse_ct=0; tse_ct<0x68+0x200; tse_ct++) {
+ /* clear the TSS as most entries are not used here */
+ mem_writeb(vcpi.private_area+0x3000,0);
+ }
+ /* Set up the ring0-stack */
+ mem_writed(vcpi.private_area+0x3004,0x00002000); // esp
+ mem_writed(vcpi.private_area+0x3008,0x00000014); // ss
+
+ mem_writed(vcpi.private_area+0x3066,0x0068); // io-map base (map follows, all zero)
+}
+
+static Bitu INT4B_Handler() {
+ switch (reg_ah) {
+ case 0x81:
+ CALLBACK_SCF(true);
+ reg_ax=0x1;
+ break;
+ default:
+ LOG(LOG_MISC,LOG_WARN)("Unhandled interrupt 4B function %x",reg_ah);
+ break;
+ }
+ return CBRET_NONE;
+}
+
+Bitu GetEMSType(Section_prop * section) {
+ Bitu rtype = 0;
+ std::string emstypestr(section->Get_string("ems"));
+ if (emstypestr=="true") {
+ rtype = 1; // mixed mode
+ } else if (emstypestr=="emsboard") {
+ rtype = 2;
+ } else if (emstypestr=="emm386") {
+ rtype = 3;
+ } else {
+ rtype = 0;
+ }
+ return rtype;
+}
+
+
+class EMS: public Module_base {
+private:
+ DOS_Device * emm_device;
+ /* location in protected unfreeable memory where the ems name and callback are
+ * stored 32 bytes.*/
+ static Bit16u ems_baseseg;
+ RealPt old4b_pointer,old67_pointer;
+ CALLBACK_HandlerObject call_vdma,call_vcpi,call_v86mon;
+ Bitu call_int67;
+
+public:
+ EMS(Section* configuration):Module_base(configuration) {
+ emm_device=NULL;
+ ems_type=0;
+
+ /* Virtual DMA interrupt callback */
+ call_vdma.Install(&INT4B_Handler,CB_IRET,"Int 4b vdma");
+ call_vdma.Set_RealVec(0x4b);
+
+ vcpi.enabled=false;
+ GEMMIS_seg=0;
+
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+ ems_type=GetEMSType(section);
+ if (ems_type<=0) return;
+
+ if (machine==MCH_PCJR) {
+ ems_type=0;
+ LOG_MSG("EMS disabled for PCJr machine");
+ return;
+ }
+ BIOS_ZeroExtendedSize(true);
+
+ if (!ems_baseseg) ems_baseseg=DOS_GetMemory(2); //We have 32 bytes
+
+ /* Add a little hack so it appears that there is an actual ems device installed */
+ char const* emsname="EMMXXXX0";
+ MEM_BlockWrite(PhysMake(ems_baseseg,0xa),emsname,(Bitu)(strlen(emsname)+1));
+
+ call_int67=CALLBACK_Allocate();
+ CALLBACK_Setup(call_int67,&INT67_Handler,CB_IRET,PhysMake(ems_baseseg,4),"Int 67 ems");
+ RealSetVec(0x67,RealMake(ems_baseseg,4),old67_pointer);
+
+ /* Register the ems device */
+ //TODO MAYBE put it in the class.
+ emm_device = new device_EMM(ems_type!=2);
+ DOS_AddDevice(emm_device);
+
+ /* Clear handle and page tables */
+ Bitu i;
+ for (i=0;i<EMM_MAX_HANDLES;i++) {
+ emm_handles[i].mem=0;
+ emm_handles[i].pages=NULL_HANDLE;
+ memset(&emm_handles[i].name,0,8);
+ }
+ for (i=0;i<EMM_MAX_PHYS;i++) {
+ emm_mappings[i].page=NULL_PAGE;
+ emm_mappings[i].handle=NULL_HANDLE;
+ }
+ for (i=0;i<0x40;i++) {
+ emm_segmentmappings[i].page=NULL_PAGE;
+ emm_segmentmappings[i].handle=NULL_HANDLE;
+ }
+
+ EMM_AllocateSystemHandle(24); // allocate OS-dedicated handle (ems handle zero, 384kb)
+
+ if (ems_type==3) {
+ DMA_SetWrapping(0xffffffff); // emm386-bug that disables dma wrapping
+ }
+
+ if (!ENABLE_VCPI) return;
+
+ if (ems_type!=2) {
+ /* Install a callback that handles VCPI-requests in protected mode requests */
+ call_vcpi.Install(&VCPI_PM_Handler,CB_IRETD,"VCPI PM");
+ vcpi.pm_interface=(call_vcpi.Get_callback())*CB_SIZE;
+
+ /* Initialize private data area and set up descriptor tables */
+ SetupVCPI();
+
+ if (!vcpi.enabled) return;
+
+ /* Install v86-callback that handles interrupts occuring
+ in v86 mode, including protection fault exceptions */
+ call_v86mon.Install(&V86_Monitor,CB_IRET,"V86 Monitor");
+
+ mem_writeb(vcpi.private_area+0x2e00,(Bit8u)0xFE); //GRP 4
+ mem_writeb(vcpi.private_area+0x2e01,(Bit8u)0x38); //Extra Callback instruction
+ mem_writew(vcpi.private_area+0x2e02,call_v86mon.Get_callback()); //The immediate word
+ mem_writeb(vcpi.private_area+0x2e04,(Bit8u)0x66);
+ mem_writeb(vcpi.private_area+0x2e05,(Bit8u)0xCF); //A IRETD Instruction
+
+ /* Testcode only, starts up dosbox in v86-mode */
+ if (ENABLE_V86_STARTUP) {
+ /* Prepare V86-task */
+ CPU_SET_CRX(0, 1);
+ CPU_LGDT(0xff, vcpi.private_area+0x0000);
+ CPU_LIDT(0x7ff, vcpi.private_area+0x2000);
+ if (CPU_LLDT(0x08)) LOG_MSG("VCPI: Could not load LDT");
+ if (CPU_LTR(0x10)) LOG_MSG("VCPI: Could not load TR");
+
+ CPU_Push32(SegValue(gs));
+ CPU_Push32(SegValue(fs));
+ CPU_Push32(SegValue(ds));
+ CPU_Push32(SegValue(es));
+ CPU_Push32(SegValue(ss));
+ CPU_Push32(0x23002);
+ CPU_Push32(SegValue(cs));
+ CPU_Push32(reg_eip&0xffff);
+ /* Switch to V86-mode */
+ cpu.cpl=0;
+ CPU_IRET(true,0);
+ }
+ }
+ }
+
+ ~EMS() {
+ if (ems_type<=0) return;
+
+ /* Undo Biosclearing */
+ BIOS_ZeroExtendedSize(false);
+
+ /* Remove ems device */
+ if (emm_device!=NULL) {
+ DOS_DelDevice(emm_device);
+ emm_device=NULL;
+ }
+ GEMMIS_seg=0;
+
+ /* Remove the emsname and callback hack */
+ char buf[32]= { 0 };
+ MEM_BlockWrite(PhysMake(ems_baseseg,0),buf,32);
+ RealSetVec(0x67,old67_pointer);
+
+ /* Release memory allocated to system handle */
+ if (emm_handles[EMM_SYSTEM_HANDLE].pages != NULL_HANDLE) {
+ MEM_ReleasePages(emm_handles[EMM_SYSTEM_HANDLE].mem);
+ }
+
+ /* Clear handle and page tables */
+ //TODO
+
+ if ((!ENABLE_VCPI) || (!vcpi.enabled)) return;
+
+ if (cpu.pmode && GETFLAG(VM)) {
+ /* Switch back to real mode if in v86-mode */
+ CPU_SET_CRX(0, 0);
+ CPU_SET_CRX(3, 0);
+ reg_flags&=(~(FLAG_IOPL|FLAG_VM));
+ CPU_LIDT(0x3ff, 0);
+ cpu.cpl=0;
+ }
+ }
+};
+
+static EMS* test;
+
+void EMS_ShutDown(Section* /*sec*/) {
+ delete test;
+}
+
+void EMS_Init(Section* sec) {
+ test = new EMS(sec);
+ sec->AddDestroyFunction(&EMS_ShutDown,true);
+}
+
+//Initialize static members
+Bit16u EMS::ems_baseseg = 0;
diff --git a/src/ints/int10.cpp b/src/ints/int10.cpp
new file mode 100644
index 000000000..a5cfa7972
--- /dev/null
+++ b/src/ints/int10.cpp
@@ -0,0 +1,769 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "mem.h"
+#include "callback.h"
+#include "regs.h"
+#include "inout.h"
+#include "int10.h"
+#include "mouse.h"
+#include "setup.h"
+
+Int10Data int10;
+static Bitu call_10;
+static bool warned_ff=false;
+
+static Bitu INT10_Handler(void) {
+#if 0
+ switch (reg_ah) {
+ case 0x02:
+ case 0x03:
+ case 0x09:
+ case 0xc:
+ case 0xd:
+ case 0x0e:
+ case 0x10:
+ case 0x4f:
+
+ break;
+ default:
+ LOG(LOG_INT10,LOG_NORMAL)("Function AX:%04X , BX %04X DX %04X",reg_ax,reg_bx,reg_dx);
+ break;
+ }
+#endif
+ INT10_SetCurMode();
+
+ switch (reg_ah) {
+ case 0x00: /* Set VideoMode */
+ Mouse_BeforeNewVideoMode(true);
+ INT10_SetVideoMode(reg_al);
+ Mouse_AfterNewVideoMode(true);
+ break;
+ case 0x01: /* Set TextMode Cursor Shape */
+ INT10_SetCursorShape(reg_ch,reg_cl);
+ break;
+ case 0x02: /* Set Cursor Pos */
+ INT10_SetCursorPos(reg_dh,reg_dl,reg_bh);
+ break;
+ case 0x03: /* get Cursor Pos and Cursor Shape*/
+// reg_ah=0;
+ reg_dl=CURSOR_POS_COL(reg_bh);
+ reg_dh=CURSOR_POS_ROW(reg_bh);
+ reg_cx=real_readw(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE);
+ break;
+ case 0x04: /* read light pen pos YEAH RIGHT */
+ /* Light pen is not supported */
+ reg_ax=0;
+ break;
+ case 0x05: /* Set Active Page */
+ if ((reg_al & 0x80) && IS_TANDY_ARCH) {
+ Bit8u crtcpu=real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE);
+ switch (reg_al) {
+ case 0x80:
+ reg_bh=crtcpu & 7;
+ reg_bl=(crtcpu >> 3) & 0x7;
+ break;
+ case 0x81:
+ crtcpu=(crtcpu & 0xc7) | ((reg_bl & 7) << 3);
+ break;
+ case 0x82:
+ crtcpu=(crtcpu & 0xf8) | (reg_bh & 7);
+ break;
+ case 0x83:
+ crtcpu=(crtcpu & 0xc0) | (reg_bh & 7) | ((reg_bl & 7) << 3);
+ break;
+ }
+ if (machine==MCH_PCJR) {
+ /* always return graphics mapping, even for invalid values of AL */
+ reg_bh=crtcpu & 7;
+ reg_bl=(crtcpu >> 3) & 0x7;
+ }
+ IO_WriteB(0x3df,crtcpu);
+ real_writeb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE,crtcpu);
+ }
+ else INT10_SetActivePage(reg_al);
+ break;
+ case 0x06: /* Scroll Up */
+ INT10_ScrollWindow(reg_ch,reg_cl,reg_dh,reg_dl,-reg_al,reg_bh,0xFF);
+ break;
+ case 0x07: /* Scroll Down */
+ INT10_ScrollWindow(reg_ch,reg_cl,reg_dh,reg_dl,reg_al,reg_bh,0xFF);
+ break;
+ case 0x08: /* Read character & attribute at cursor */
+ INT10_ReadCharAttr(&reg_ax,reg_bh);
+ break;
+ case 0x09: /* Write Character & Attribute at cursor CX times */
+ if (real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE)==0x11)
+ INT10_WriteChar(reg_al,(reg_bl&0x80)|0x3f,reg_bh,reg_cx,true);
+ else INT10_WriteChar(reg_al,reg_bl,reg_bh,reg_cx,true);
+ break;
+ case 0x0A: /* Write Character at cursor CX times */
+ INT10_WriteChar(reg_al,reg_bl,reg_bh,reg_cx,false);
+ break;
+ case 0x0B: /* Set Background/Border Colour & Set Palette*/
+ switch (reg_bh) {
+ case 0x00: //Background/Border color
+ INT10_SetBackgroundBorder(reg_bl);
+ break;
+ case 0x01: //Set color Select
+ default:
+ INT10_SetColorSelect(reg_bl);
+ break;
+ }
+ break;
+ case 0x0C: /* Write Graphics Pixel */
+ INT10_PutPixel(reg_cx,reg_dx,reg_bh,reg_al);
+ break;
+ case 0x0D: /* Read Graphics Pixel */
+ INT10_GetPixel(reg_cx,reg_dx,reg_bh,&reg_al);
+ break;
+ case 0x0E: /* Teletype OutPut */
+ INT10_TeletypeOutput(reg_al,reg_bl);
+ break;
+ case 0x0F: /* Get videomode */
+ reg_bh=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+ reg_al=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE)|(real_readb(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL)&0x80);
+ reg_ah=(Bit8u)real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ break;
+ case 0x10: /* Palette functions */
+ if (!IS_EGAVGA_ARCH && (reg_al>0x02)) break;
+ else if (!IS_VGA_ARCH && (reg_al>0x03)) break;
+ switch (reg_al) {
+ case 0x00: /* SET SINGLE PALETTE REGISTER */
+ INT10_SetSinglePaletteRegister(reg_bl,reg_bh);
+ break;
+ case 0x01: /* SET BORDER (OVERSCAN) COLOR*/
+ INT10_SetOverscanBorderColor(reg_bh);
+ break;
+ case 0x02: /* SET ALL PALETTE REGISTERS */
+ INT10_SetAllPaletteRegisters(SegPhys(es)+reg_dx);
+ break;
+ case 0x03: /* TOGGLE INTENSITY/BLINKING BIT */
+ INT10_ToggleBlinkingBit(reg_bl);
+ break;
+ case 0x07: /* GET SINGLE PALETTE REGISTER */
+ INT10_GetSinglePaletteRegister(reg_bl,&reg_bh);
+ break;
+ case 0x08: /* READ OVERSCAN (BORDER COLOR) REGISTER */
+ INT10_GetOverscanBorderColor(&reg_bh);
+ break;
+ case 0x09: /* READ ALL PALETTE REGISTERS AND OVERSCAN REGISTER */
+ INT10_GetAllPaletteRegisters(SegPhys(es)+reg_dx);
+ break;
+ case 0x10: /* SET INDIVIDUAL DAC REGISTER */
+ INT10_SetSingleDACRegister(reg_bl,reg_dh,reg_ch,reg_cl);
+ break;
+ case 0x12: /* SET BLOCK OF DAC REGISTERS */
+ INT10_SetDACBlock(reg_bx,reg_cx,SegPhys(es)+reg_dx);
+ break;
+ case 0x13: /* SELECT VIDEO DAC COLOR PAGE */
+ INT10_SelectDACPage(reg_bl,reg_bh);
+ break;
+ case 0x15: /* GET INDIVIDUAL DAC REGISTER */
+ INT10_GetSingleDACRegister(reg_bl,&reg_dh,&reg_ch,&reg_cl);
+ break;
+ case 0x17: /* GET BLOCK OF DAC REGISTER */
+ INT10_GetDACBlock(reg_bx,reg_cx,SegPhys(es)+reg_dx);
+ break;
+ case 0x18: /* undocumented - SET PEL MASK */
+ INT10_SetPelMask(reg_bl);
+ break;
+ case 0x19: /* undocumented - GET PEL MASK */
+ INT10_GetPelMask(reg_bl);
+ reg_bh=0; // bx for get mask
+ break;
+ case 0x1A: /* GET VIDEO DAC COLOR PAGE */
+ INT10_GetDACPage(&reg_bl,&reg_bh);
+ break;
+ case 0x1B: /* PERFORM GRAY-SCALE SUMMING */
+ INT10_PerformGrayScaleSumming(reg_bx,reg_cx);
+ break;
+ case 0xF0: /* ET4000: SET HiColor GRAPHICS MODE */
+ case 0xF1: /* ET4000: GET DAC TYPE */
+ case 0xF2: /* ET4000: CHECK/SET HiColor MODE */
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("Function 10:Unhandled EGA/VGA Palette Function %2X",reg_al);
+ break;
+ }
+ break;
+ case 0x11: /* Character generator functions */
+ if (!IS_EGAVGA_ARCH)
+ break;
+ if ((reg_al&0xf0)==0x10) Mouse_BeforeNewVideoMode(false);
+ switch (reg_al) {
+/* Textmode calls */
+ case 0x00: /* Load user font */
+ case 0x10:
+ INT10_LoadFont(SegPhys(es)+reg_bp,reg_al==0x10,reg_cx,reg_dx,reg_bl&0x7f,reg_bh);
+ break;
+ case 0x01: /* Load 8x14 font */
+ case 0x11:
+ INT10_LoadFont(Real2Phys(int10.rom.font_14),reg_al==0x11,256,0,reg_bl&0x7f,14);
+ break;
+ case 0x02: /* Load 8x8 font */
+ case 0x12:
+ INT10_LoadFont(Real2Phys(int10.rom.font_8_first),reg_al==0x12,256,0,reg_bl&0x7f,8);
+ break;
+ case 0x03: /* Set Block Specifier */
+ IO_Write(0x3c4,0x3);IO_Write(0x3c5,reg_bl);
+ break;
+ case 0x04: /* Load 8x16 font */
+ case 0x14:
+ if (!IS_VGA_ARCH) break;
+ INT10_LoadFont(Real2Phys(int10.rom.font_16),reg_al==0x14,256,0,reg_bl&0x7f,16);
+ break;
+/* Graphics mode calls */
+ case 0x20: /* Set User 8x8 Graphics characters */
+ RealSetVec(0x1f,RealMake(SegValue(es),reg_bp));
+ break;
+ case 0x21: /* Set user graphics characters */
+ RealSetVec(0x43,RealMake(SegValue(es),reg_bp));
+ real_writew(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,reg_cx);
+ goto graphics_chars;
+ case 0x22: /* Rom 8x14 set */
+ RealSetVec(0x43,int10.rom.font_14);
+ real_writew(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,14);
+ goto graphics_chars;
+ case 0x23: /* Rom 8x8 double dot set */
+ RealSetVec(0x43,int10.rom.font_8_first);
+ real_writew(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,8);
+ goto graphics_chars;
+ case 0x24: /* Rom 8x16 set */
+ if (!IS_VGA_ARCH) break;
+ RealSetVec(0x43,int10.rom.font_16);
+ real_writew(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,16);
+ goto graphics_chars;
+graphics_chars:
+ switch (reg_bl) {
+ case 0x00:real_writeb(BIOSMEM_SEG,BIOSMEM_NB_ROWS,reg_dl-1);break;
+ case 0x01:real_writeb(BIOSMEM_SEG,BIOSMEM_NB_ROWS,13);break;
+ case 0x03:real_writeb(BIOSMEM_SEG,BIOSMEM_NB_ROWS,42);break;
+ case 0x02:
+ default:real_writeb(BIOSMEM_SEG,BIOSMEM_NB_ROWS,24);break;
+ }
+ break;
+/* General */
+ case 0x30:/* Get Font Information */
+ switch (reg_bh) {
+ case 0x00: /* interupt 0x1f vector */
+ {
+ RealPt int_1f=RealGetVec(0x1f);
+ SegSet16(es,RealSeg(int_1f));
+ reg_bp=RealOff(int_1f);
+ }
+ break;
+ case 0x01: /* interupt 0x43 vector */
+ {
+ RealPt int_43=RealGetVec(0x43);
+ SegSet16(es,RealSeg(int_43));
+ reg_bp=RealOff(int_43);
+ }
+ break;
+ case 0x02: /* font 8x14 */
+ SegSet16(es,RealSeg(int10.rom.font_14));
+ reg_bp=RealOff(int10.rom.font_14);
+ break;
+ case 0x03: /* font 8x8 first 128 */
+ SegSet16(es,RealSeg(int10.rom.font_8_first));
+ reg_bp=RealOff(int10.rom.font_8_first);
+ break;
+ case 0x04: /* font 8x8 second 128 */
+ SegSet16(es,RealSeg(int10.rom.font_8_second));
+ reg_bp=RealOff(int10.rom.font_8_second);
+ break;
+ case 0x05: /* alpha alternate 9x14 */
+ SegSet16(es,RealSeg(int10.rom.font_14_alternate));
+ reg_bp=RealOff(int10.rom.font_14_alternate);
+ break;
+ case 0x06: /* font 8x16 */
+ if (!IS_VGA_ARCH) break;
+ SegSet16(es,RealSeg(int10.rom.font_16));
+ reg_bp=RealOff(int10.rom.font_16);
+ break;
+ case 0x07: /* alpha alternate 9x16 */
+ if (!IS_VGA_ARCH) break;
+ SegSet16(es,RealSeg(int10.rom.font_16_alternate));
+ reg_bp=RealOff(int10.rom.font_16_alternate);
+ break;
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("Function 11:30 Request for font %2X",reg_bh);
+ break;
+ }
+ if ((reg_bh<=7) || (svgaCard==SVGA_TsengET4K)) {
+ reg_cx=real_readw(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ reg_dl=real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS);
+ }
+ break;
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("Function 11:Unsupported character generator call %2X",reg_al);
+ break;
+ }
+ if ((reg_al&0xf0)==0x10) Mouse_AfterNewVideoMode(false);
+ break;
+ case 0x12: /* alternate function select */
+ if (!IS_EGAVGA_ARCH)
+ break;
+ switch (reg_bl) {
+ case 0x10: /* Get EGA Information */
+ reg_bh=(real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS)==0x3B4);
+ reg_bl=3; //256 kb
+ reg_cl=real_readb(BIOSMEM_SEG,BIOSMEM_SWITCHES) & 0x0F;
+ reg_ch=real_readb(BIOSMEM_SEG,BIOSMEM_SWITCHES) >> 4;
+ break;
+ case 0x20: /* Set alternate printscreen */
+ break;
+ case 0x30: /* Select vertical resolution */
+ {
+ if (!IS_VGA_ARCH) break;
+ LOG(LOG_INT10,LOG_WARN)("Function 12:Call %2X (select vertical resolution)",reg_bl);
+ if (svgaCard != SVGA_None) {
+ if (reg_al > 2) {
+ reg_al=0; // invalid subfunction
+ break;
+ }
+ }
+ Bit8u modeset_ctl = real_readb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL);
+ Bit8u video_switches = real_readb(BIOSMEM_SEG,BIOSMEM_SWITCHES)&0xf0;
+ switch(reg_al) {
+ case 0: // 200
+ modeset_ctl &= 0xef;
+ modeset_ctl |= 0x80;
+ video_switches |= 8; // ega normal/cga emulation
+ break;
+ case 1: // 350
+ modeset_ctl &= 0x6f;
+ video_switches |= 9; // ega enhanced
+ break;
+ case 2: // 400
+ modeset_ctl &= 0x6f;
+ modeset_ctl |= 0x10; // use 400-line mode at next mode set
+ video_switches |= 9; // ega enhanced
+ break;
+ default:
+ modeset_ctl &= 0xef;
+ video_switches |= 8; // ega normal/cga emulation
+ break;
+ }
+ real_writeb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL,modeset_ctl);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_SWITCHES,video_switches);
+ reg_al=0x12; // success
+ break;
+ }
+ case 0x31: /* Palette loading on modeset */
+ {
+ if (!IS_VGA_ARCH) break;
+ if (svgaCard==SVGA_TsengET4K) reg_al&=1;
+ if (reg_al>1) {
+ reg_al=0; //invalid subfunction
+ break;
+ }
+ Bit8u temp = real_readb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL) & 0xf7;
+ if (reg_al&1) temp|=8; // enable if al=0
+ real_writeb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL,temp);
+ reg_al=0x12;
+ break;
+ }
+ case 0x32: /* Video addressing */
+ if (!IS_VGA_ARCH) break;
+ LOG(LOG_INT10,LOG_ERROR)("Function 12:Call %2X not handled",reg_bl);
+ if (svgaCard==SVGA_TsengET4K) reg_al&=1;
+ if (reg_al>1) reg_al=0; //invalid subfunction
+ else reg_al=0x12; //fake a success call
+ break;
+ case 0x33: /* SWITCH GRAY-SCALE SUMMING */
+ {
+ if (!IS_VGA_ARCH) break;
+ if (svgaCard==SVGA_TsengET4K) reg_al&=1;
+ if (reg_al>1) {
+ reg_al=0;
+ break;
+ }
+ Bit8u temp = real_readb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL) & 0xfd;
+ if (!(reg_al&1)) temp|=2; // enable if al=0
+ real_writeb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL,temp);
+ reg_al=0x12;
+ break;
+ }
+ case 0x34: /* ALTERNATE FUNCTION SELECT (VGA) - CURSOR EMULATION */
+ {
+ // bit 0: 0=enable, 1=disable
+ if (!IS_VGA_ARCH) break;
+ if (svgaCard==SVGA_TsengET4K) reg_al&=1;
+ if (reg_al>1) {
+ reg_al=0;
+ break;
+ }
+ Bit8u temp = real_readb(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL) & 0xfe;
+ real_writeb(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,temp|reg_al);
+ reg_al=0x12;
+ break;
+ }
+ case 0x35:
+ if (!IS_VGA_ARCH) break;
+ LOG(LOG_INT10,LOG_ERROR)("Function 12:Call %2X not handled",reg_bl);
+ reg_al=0x12;
+ break;
+ case 0x36: { /* VGA Refresh control */
+ if (!IS_VGA_ARCH) break;
+ if ((svgaCard==SVGA_S3Trio) && (reg_al>1)) {
+ reg_al=0;
+ break;
+ }
+ IO_Write(0x3c4,0x1);
+ Bit8u clocking = IO_Read(0x3c5);
+
+ if (reg_al==0) clocking &= ~0x20;
+ else clocking |= 0x20;
+
+ IO_Write(0x3c4,0x1);
+ IO_Write(0x3c5,clocking);
+
+ reg_al=0x12; // success
+ break;
+ }
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("Function 12:Call %2X not handled",reg_bl);
+ if (machine!=MCH_EGA) reg_al=0;
+ break;
+ }
+ break;
+ case 0x13: /* Write String */
+ INT10_WriteString(reg_dh,reg_dl,reg_al,reg_bl,SegPhys(es)+reg_bp,reg_cx,reg_bh);
+ break;
+ case 0x1A: /* Display Combination */
+ if (!IS_VGA_ARCH) break;
+ if (reg_al==0) { // get dcc
+ // walk the tables...
+ RealPt vsavept=real_readd(BIOSMEM_SEG,BIOSMEM_VS_POINTER);
+ RealPt svstable=real_readd(RealSeg(vsavept),RealOff(vsavept)+0x10);
+ if (svstable) {
+ RealPt dcctable=real_readd(RealSeg(svstable),RealOff(svstable)+0x02);
+ Bit8u entries=real_readb(RealSeg(dcctable),RealOff(dcctable)+0x00);
+ Bit8u idx=real_readb(BIOSMEM_SEG,BIOSMEM_DCC_INDEX);
+ // check if index within range
+ if (idx<entries) {
+ Bit16u dccentry=real_readw(RealSeg(dcctable),RealOff(dcctable)+0x04+idx*2);
+ if ((dccentry&0xff)==0) reg_bx=dccentry>>8;
+ else reg_bx=dccentry;
+ } else reg_bx=0xffff;
+ } else reg_bx=0xffff;
+ reg_ax=0x1A; // high part destroyed or zeroed depending on BIOS
+ } else if (reg_al==1) { // set dcc
+ Bit8u newidx=0xff;
+ // walk the tables...
+ RealPt vsavept=real_readd(BIOSMEM_SEG,BIOSMEM_VS_POINTER);
+ RealPt svstable=real_readd(RealSeg(vsavept),RealOff(vsavept)+0x10);
+ if (svstable) {
+ RealPt dcctable=real_readd(RealSeg(svstable),RealOff(svstable)+0x02);
+ Bit8u entries=real_readb(RealSeg(dcctable),RealOff(dcctable)+0x00);
+ if (entries) {
+ Bitu ct;
+ Bit16u swpidx=reg_bh|(reg_bl<<8);
+ // search the ddc index in the dcc table
+ for (ct=0; ct<entries; ct++) {
+ Bit16u dccentry=real_readw(RealSeg(dcctable),RealOff(dcctable)+0x04+ct*2);
+ if ((dccentry==reg_bx) || (dccentry==swpidx)) {
+ newidx=(Bit8u)ct;
+ break;
+ }
+ }
+ }
+ }
+
+ real_writeb(BIOSMEM_SEG,BIOSMEM_DCC_INDEX,newidx);
+ reg_ax=0x1A; // high part destroyed or zeroed depending on BIOS
+ }
+ break;
+ case 0x1B: /* functionality State Information */
+ if (!IS_VGA_ARCH) break;
+ switch (reg_bx) {
+ case 0x0000:
+ INT10_GetFuncStateInformation(SegPhys(es)+reg_di);
+ reg_al=0x1B;
+ break;
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("1B:Unhandled call BX %2X",reg_bx);
+ reg_al=0;
+ break;
+ }
+ break;
+ case 0x1C: /* Video Save Area */
+ if (!IS_VGA_ARCH) break;
+ switch (reg_al) {
+ case 0: {
+ Bitu ret=INT10_VideoState_GetSize(reg_cx);
+ if (ret) {
+ reg_al=0x1c;
+ reg_bx=(Bit16u)ret;
+ } else reg_al=0;
+ }
+ break;
+ case 1:
+ if (INT10_VideoState_Save(reg_cx,RealMake(SegValue(es),reg_bx))) reg_al=0x1c;
+ else reg_al=0;
+ break;
+ case 2:
+ if (INT10_VideoState_Restore(reg_cx,RealMake(SegValue(es),reg_bx))) reg_al=0x1c;
+ else reg_al=0;
+ break;
+ default:
+ if (svgaCard==SVGA_TsengET4K) reg_ax=0;
+ else reg_al=0;
+ break;
+ }
+ break;
+ case 0x4f: /* VESA Calls */
+ if ((!IS_VGA_ARCH) || (svgaCard!=SVGA_S3Trio)) break;
+ switch (reg_al) {
+ case 0x00: /* Get SVGA Information */
+ reg_al=0x4f;
+ reg_ah=VESA_GetSVGAInformation(SegValue(es),reg_di);
+ break;
+ case 0x01: /* Get SVGA Mode Information */
+ reg_al=0x4f;
+ reg_ah=VESA_GetSVGAModeInformation(reg_cx,SegValue(es),reg_di);
+ break;
+ case 0x02: /* Set videomode */
+ Mouse_BeforeNewVideoMode(true);
+ reg_al=0x4f;
+ reg_ah=VESA_SetSVGAMode(reg_bx);
+ Mouse_AfterNewVideoMode(true);
+ break;
+ case 0x03: /* Get videomode */
+ reg_al=0x4f;
+ reg_ah=VESA_GetSVGAMode(reg_bx);
+ break;
+ case 0x04: /* Save/restore state */
+ reg_al=0x4f;
+ switch (reg_dl) {
+ case 0: {
+ Bitu ret=INT10_VideoState_GetSize(reg_cx);
+ if (ret) {
+ reg_ah=0;
+ reg_bx=(Bit16u)ret;
+ } else reg_ah=1;
+ }
+ break;
+ case 1:
+ if (INT10_VideoState_Save(reg_cx,RealMake(SegValue(es),reg_bx))) reg_ah=0;
+ else reg_ah=1;
+ break;
+ case 2:
+ if (INT10_VideoState_Restore(reg_cx,RealMake(SegValue(es),reg_bx))) reg_ah=0;
+ else reg_ah=1;
+ break;
+ default:
+ reg_ah=1;
+ break;
+ }
+ break;
+ case 0x05:
+ if (reg_bh==0) { /* Set CPU Window */
+ reg_ah=VESA_SetCPUWindow(reg_bl,reg_dl);
+ reg_al=0x4f;
+ } else if (reg_bh == 1) { /* Get CPU Window */
+ reg_ah=VESA_GetCPUWindow(reg_bl,reg_dx);
+ reg_al=0x4f;
+ } else {
+ LOG(LOG_INT10,LOG_ERROR)("Unhandled VESA Function %X Subfunction %X",reg_al,reg_bh);
+ reg_ah=0x01;
+ }
+ break;
+ case 0x06:
+ reg_al=0x4f;
+ reg_ah=VESA_ScanLineLength(reg_bl,reg_cx,reg_bx,reg_cx,reg_dx);
+ break;
+ case 0x07:
+ switch (reg_bl) {
+ case 0x80: /* Set Display Start during retrace */
+ case 0x00: /* Set display Start */
+ reg_al=0x4f;
+ reg_ah=VESA_SetDisplayStart(reg_cx,reg_dx,reg_bl==0x80);
+ break;
+ case 0x01:
+ reg_al=0x4f;
+ reg_bh=0x00; //reserved
+ reg_ah=VESA_GetDisplayStart(reg_cx,reg_dx);
+ break;
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("Unhandled VESA Function %X Subfunction %X",reg_al,reg_bl);
+ reg_ah=0x1;
+ break;
+ }
+ break;
+ case 0x09:
+ switch (reg_bl) {
+ case 0x80: /* Set Palette during retrace */
+ case 0x00: /* Set Palette */
+ reg_ah=VESA_SetPalette(SegPhys(es)+reg_di,reg_dx,reg_cx,reg_bl==0x80);
+ reg_al=0x4f;
+ break;
+ case 0x01: /* Get Palette */
+ reg_ah=VESA_GetPalette(SegPhys(es)+reg_di,reg_dx,reg_cx);
+ reg_al=0x4f;
+ break;
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("Unhandled VESA Function %X Subfunction %X",reg_al,reg_bl);
+ reg_ah=0x01;
+ break;
+ }
+ break;
+ case 0x0a: /* Get Pmode Interface */
+ if (int10.vesa_oldvbe) {
+ reg_ax=0x014f;
+ break;
+ }
+ switch (reg_bl) {
+ case 0x00:
+ SegSet16(es,RealSeg(int10.rom.pmode_interface));
+ reg_di=RealOff(int10.rom.pmode_interface);
+ reg_cx=int10.rom.pmode_interface_size;
+ reg_ax=0x004f;
+ break;
+ case 0x01: /* Get code for "set window" */
+ SegSet16(es,RealSeg(int10.rom.pmode_interface));
+ reg_di=RealOff(int10.rom.pmode_interface)+int10.rom.pmode_interface_window;
+ reg_cx=int10.rom.pmode_interface_start-int10.rom.pmode_interface_window;
+ reg_ax=0x004f;
+ break;
+ case 0x02: /* Get code for "set display start" */
+ SegSet16(es,RealSeg(int10.rom.pmode_interface));
+ reg_di=RealOff(int10.rom.pmode_interface)+int10.rom.pmode_interface_start;
+ reg_cx=int10.rom.pmode_interface_palette-int10.rom.pmode_interface_start;
+ reg_ax=0x004f;
+ break;
+ case 0x03: /* Get code for "set palette" */
+ SegSet16(es,RealSeg(int10.rom.pmode_interface));
+ reg_di=RealOff(int10.rom.pmode_interface)+int10.rom.pmode_interface_palette;
+ reg_cx=int10.rom.pmode_interface_size-int10.rom.pmode_interface_palette;
+ reg_ax=0x004f;
+ break;
+ default:
+ reg_ax=0x014f;
+ break;
+ }
+ break;
+
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("Unhandled VESA Function %X",reg_al);
+ reg_al=0x0;
+ break;
+ }
+ break;
+ case 0xf0:
+ INT10_EGA_RIL_ReadRegister(reg_bl, reg_dx);
+ break;
+ case 0xf1:
+ INT10_EGA_RIL_WriteRegister(reg_bl, reg_bh, reg_dx);
+ break;
+ case 0xf2:
+ INT10_EGA_RIL_ReadRegisterRange(reg_ch, reg_cl, reg_dx, SegPhys(es)+reg_bx);
+ break;
+ case 0xf3:
+ INT10_EGA_RIL_WriteRegisterRange(reg_ch, reg_cl, reg_dx, SegPhys(es)+reg_bx);
+ break;
+ case 0xf4:
+ INT10_EGA_RIL_ReadRegisterSet(reg_cx, SegPhys(es)+reg_bx);
+ break;
+ case 0xf5:
+ INT10_EGA_RIL_WriteRegisterSet(reg_cx, SegPhys(es)+reg_bx);
+ break;
+ case 0xfa: {
+ RealPt pt=INT10_EGA_RIL_GetVersionPt();
+ SegSet16(es,RealSeg(pt));
+ reg_bx=RealOff(pt);
+ }
+ break;
+ case 0xff:
+ if (!warned_ff) LOG(LOG_INT10,LOG_NORMAL)("INT10:FF:Weird NC call");
+ warned_ff=true;
+ break;
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("Function %4X not supported",reg_ax);
+// reg_al=0x00; //Successfull, breaks marriage
+ break;
+ };
+ return CBRET_NONE;
+}
+
+static void INT10_Seg40Init(void) {
+ // the default char height
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,16);
+ // Clear the screen
+ real_writeb(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,0x60);
+ // Set the basic screen we have
+ real_writeb(BIOSMEM_SEG,BIOSMEM_SWITCHES,0xF9);
+ // Set the basic modeset options
+ real_writeb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL,0x51);
+ // Set the default MSR
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x09);
+ // Set the pointer to video save pointer table
+ real_writed(BIOSMEM_SEG,BIOSMEM_VS_POINTER,int10.rom.video_save_pointers);
+}
+
+
+static void INT10_InitVGA(void) {
+ if (IS_EGAVGA_ARCH) {
+ /* switch to color mode and enable CPU access 480 lines */
+ IO_Write(0x3c2,0xc3);
+ /* More than 64k */
+ IO_Write(0x3c4,0x04);
+ IO_Write(0x3c5,0x02);
+ if (IS_VGA_ARCH) {
+ /* Initialize DAC */
+ IO_Write(0x3c8,0);
+ for (Bitu i=0;i<3*256;i++) IO_Write(0x3c9,0);
+ }
+ }
+}
+
+static void SetupTandyBios(void) {
+ static Bit8u TandyConfig[130]= {
+ 0x21, 0x42, 0x49, 0x4f, 0x53, 0x20, 0x52, 0x4f, 0x4d, 0x20, 0x76, 0x65, 0x72,
+ 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x30, 0x32, 0x2e, 0x30, 0x30, 0x2e, 0x30, 0x30,
+ 0x0d, 0x0a, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69,
+ 0x74, 0x79, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0d, 0x0a,
+ 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x43, 0x29,
+ 0x20, 0x31, 0x39, 0x38, 0x34, 0x2c, 0x31, 0x39, 0x38, 0x35, 0x2c, 0x31, 0x39,
+ 0x38, 0x36, 0x2c, 0x31, 0x39, 0x38, 0x37, 0x0d, 0x0a, 0x50, 0x68, 0x6f, 0x65,
+ 0x6e, 0x69, 0x78, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20,
+ 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x73, 0x20, 0x4c, 0x74,
+ 0x64, 0x2e, 0x0d, 0x0a, 0x61, 0x6e, 0x64, 0x20, 0x54, 0x61, 0x6e, 0x64, 0x79
+ };
+ if (machine==MCH_TANDY) {
+ Bitu i;
+ for(i=0;i<130;i++) {
+ phys_writeb(0xf0000+i+0xc000, TandyConfig[i]);
+ }
+ }
+}
+
+void INT10_Init(Section* /*sec*/) {
+ INT10_InitVGA();
+ if (IS_TANDY_ARCH) SetupTandyBios();
+ /* Setup the INT 10 vector */
+ call_10=CALLBACK_Allocate();
+ CALLBACK_Setup(call_10,&INT10_Handler,CB_IRET,"Int 10 video");
+ RealSetVec(0x10,CALLBACK_RealPointer(call_10));
+ //Init the 0x40 segment and init the datastructures in the the video rom area
+ INT10_SetupRomMemory();
+ INT10_Seg40Init();
+ INT10_SetVideoMode(0x3);
+}
diff --git a/src/ints/int10.h b/src/ints/int10.h
new file mode 100644
index 000000000..2ba402e02
--- /dev/null
+++ b/src/ints/int10.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "vga.h"
+
+#define S3_LFB_BASE 0xC0000000
+
+#define BIOSMEM_SEG 0x40
+
+#define BIOSMEM_INITIAL_MODE 0x10
+#define BIOSMEM_CURRENT_MODE 0x49
+#define BIOSMEM_NB_COLS 0x4A
+#define BIOSMEM_PAGE_SIZE 0x4C
+#define BIOSMEM_CURRENT_START 0x4E
+#define BIOSMEM_CURSOR_POS 0x50
+#define BIOSMEM_CURSOR_TYPE 0x60
+#define BIOSMEM_CURRENT_PAGE 0x62
+#define BIOSMEM_CRTC_ADDRESS 0x63
+#define BIOSMEM_CURRENT_MSR 0x65
+#define BIOSMEM_CURRENT_PAL 0x66
+#define BIOSMEM_NB_ROWS 0x84
+#define BIOSMEM_CHAR_HEIGHT 0x85
+#define BIOSMEM_VIDEO_CTL 0x87
+#define BIOSMEM_SWITCHES 0x88
+#define BIOSMEM_MODESET_CTL 0x89
+#define BIOSMEM_DCC_INDEX 0x8A
+#define BIOSMEM_CRTCPU_PAGE 0x8A
+#define BIOSMEM_VS_POINTER 0xA8
+
+
+/*
+ *
+ * VGA registers
+ *
+ */
+#define VGAREG_ACTL_ADDRESS 0x3c0
+#define VGAREG_ACTL_WRITE_DATA 0x3c0
+#define VGAREG_ACTL_READ_DATA 0x3c1
+
+#define VGAREG_INPUT_STATUS 0x3c2
+#define VGAREG_WRITE_MISC_OUTPUT 0x3c2
+#define VGAREG_VIDEO_ENABLE 0x3c3
+#define VGAREG_SEQU_ADDRESS 0x3c4
+#define VGAREG_SEQU_DATA 0x3c5
+
+#define VGAREG_PEL_MASK 0x3c6
+#define VGAREG_DAC_STATE 0x3c7
+#define VGAREG_DAC_READ_ADDRESS 0x3c7
+#define VGAREG_DAC_WRITE_ADDRESS 0x3c8
+#define VGAREG_DAC_DATA 0x3c9
+
+#define VGAREG_READ_FEATURE_CTL 0x3ca
+#define VGAREG_READ_MISC_OUTPUT 0x3cc
+
+#define VGAREG_GRDC_ADDRESS 0x3ce
+#define VGAREG_GRDC_DATA 0x3cf
+
+#define VGAREG_MDA_CRTC_ADDRESS 0x3b4
+#define VGAREG_MDA_CRTC_DATA 0x3b5
+#define VGAREG_VGA_CRTC_ADDRESS 0x3d4
+#define VGAREG_VGA_CRTC_DATA 0x3d5
+
+#define VGAREG_MDA_WRITE_FEATURE_CTL 0x3ba
+#define VGAREG_VGA_WRITE_FEATURE_CTL 0x3da
+#define VGAREG_ACTL_RESET 0x3da
+#define VGAREG_TDY_RESET 0x3da
+#define VGAREG_TDY_ADDRESS 0x3da
+#define VGAREG_TDY_DATA 0x3de
+#define VGAREG_PCJR_DATA 0x3da
+
+#define VGAREG_MDA_MODECTL 0x3b8
+#define VGAREG_CGA_MODECTL 0x3d8
+#define VGAREG_CGA_PALETTE 0x3d9
+
+/* Video memory */
+#define VGAMEM_GRAPH 0xA000
+#define VGAMEM_CTEXT 0xB800
+#define VGAMEM_MTEXT 0xB000
+
+#define BIOS_NCOLS Bit16u ncols=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+#define BIOS_NROWS Bit16u nrows=(Bit16u)real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+
+extern Bit8u int10_font_08[256 * 8];
+extern Bit8u int10_font_14[256 * 14];
+extern Bit8u int10_font_16[256 * 16];
+extern Bit8u int10_font_14_alternate[20 * 15 + 1];
+extern Bit8u int10_font_16_alternate[19 * 17 + 1];
+
+struct VideoModeBlock {
+ Bit16u mode;
+ VGAModes type;
+ Bitu swidth, sheight;
+ Bitu twidth, theight;
+ Bitu cwidth, cheight;
+ Bitu ptotal,pstart,plength;
+
+ Bitu htotal,vtotal;
+ Bitu hdispend,vdispend;
+ Bitu special;
+
+};
+extern VideoModeBlock ModeList_VGA[];
+extern VideoModeBlock * CurMode;
+
+typedef struct {
+ struct {
+ RealPt font_8_first;
+ RealPt font_8_second;
+ RealPt font_14;
+ RealPt font_16;
+ RealPt font_14_alternate;
+ RealPt font_16_alternate;
+ RealPt static_state;
+ RealPt video_save_pointers;
+ RealPt video_parameter_table;
+ RealPt video_save_pointer_table;
+ RealPt video_dcc_table;
+ RealPt oemstring;
+ RealPt vesa_modes;
+ RealPt wait_retrace;
+ RealPt set_window;
+ RealPt pmode_interface;
+ Bit16u pmode_interface_size;
+ Bit16u pmode_interface_start;
+ Bit16u pmode_interface_window;
+ Bit16u pmode_interface_palette;
+ Bit16u used;
+ } rom;
+ Bit16u vesa_setmode;
+ bool vesa_nolfb;
+ bool vesa_oldvbe;
+} Int10Data;
+
+extern Int10Data int10;
+
+static inline Bit8u CURSOR_POS_COL(Bit8u page) {
+ return real_readb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2);
+}
+
+static inline Bit8u CURSOR_POS_ROW(Bit8u page) {
+ return real_readb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2+1);
+}
+
+bool INT10_SetVideoMode(Bit16u mode);
+void INT10_SetCurMode(void);
+
+void INT10_ScrollWindow(Bit8u rul,Bit8u cul,Bit8u rlr,Bit8u clr,Bit8s nlines,Bit8u attr,Bit8u page);
+
+void INT10_SetActivePage(Bit8u page);
+void INT10_GetFuncStateInformation(PhysPt save);
+
+void INT10_SetCursorShape(Bit8u first,Bit8u last);
+void INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page);
+void INT10_TeletypeOutput(Bit8u chr,Bit8u attr);
+void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr);
+void INT10_ReadCharAttr(Bit16u * result,Bit8u page);
+void INT10_WriteChar(Bit8u chr,Bit8u attr,Bit8u page,Bit16u count,bool showattr);
+void INT10_WriteString(Bit8u row,Bit8u col,Bit8u flag,Bit8u attr,PhysPt string,Bit16u count,Bit8u page);
+
+/* Graphics Stuff */
+void INT10_PutPixel(Bit16u x,Bit16u y,Bit8u page,Bit8u color);
+void INT10_GetPixel(Bit16u x,Bit16u y,Bit8u page,Bit8u * color);
+
+/* Font Stuff */
+void INT10_LoadFont(PhysPt font,bool reload,Bitu count,Bitu offset,Bitu map,Bitu height);
+void INT10_ReloadFont(void);
+
+/* Palette Group */
+void INT10_SetBackgroundBorder(Bit8u val);
+void INT10_SetColorSelect(Bit8u val);
+void INT10_SetSinglePaletteRegister(Bit8u reg,Bit8u val);
+void INT10_SetOverscanBorderColor(Bit8u val);
+void INT10_SetAllPaletteRegisters(PhysPt data);
+void INT10_ToggleBlinkingBit(Bit8u state);
+void INT10_GetSinglePaletteRegister(Bit8u reg,Bit8u * val);
+void INT10_GetOverscanBorderColor(Bit8u * val);
+void INT10_GetAllPaletteRegisters(PhysPt data);
+void INT10_SetSingleDACRegister(Bit8u index,Bit8u red,Bit8u green,Bit8u blue);
+void INT10_GetSingleDACRegister(Bit8u index,Bit8u * red,Bit8u * green,Bit8u * blue);
+void INT10_SetDACBlock(Bit16u index,Bit16u count,PhysPt data);
+void INT10_GetDACBlock(Bit16u index,Bit16u count,PhysPt data);
+void INT10_SelectDACPage(Bit8u function,Bit8u mode);
+void INT10_GetDACPage(Bit8u* mode,Bit8u* page);
+void INT10_SetPelMask(Bit8u mask);
+void INT10_GetPelMask(Bit8u & mask);
+void INT10_PerformGrayScaleSumming(Bit16u start_reg,Bit16u count);
+
+
+/* Vesa Group */
+Bit8u VESA_GetSVGAInformation(Bit16u seg,Bit16u off);
+Bit8u VESA_GetSVGAModeInformation(Bit16u mode,Bit16u seg,Bit16u off);
+Bit8u VESA_SetSVGAMode(Bit16u mode);
+Bit8u VESA_GetSVGAMode(Bit16u & mode);
+Bit8u VESA_SetCPUWindow(Bit8u window,Bit8u address);
+Bit8u VESA_GetCPUWindow(Bit8u window,Bit16u & address);
+Bit8u VESA_ScanLineLength(Bit8u subcall, Bit16u val, Bit16u & bytes,Bit16u & pixels,Bit16u & lines);
+Bit8u VESA_SetDisplayStart(Bit16u x,Bit16u y,bool wait);
+Bit8u VESA_GetDisplayStart(Bit16u & x,Bit16u & y);
+Bit8u VESA_SetPalette(PhysPt data,Bitu index,Bitu count,bool wait);
+Bit8u VESA_GetPalette(PhysPt data,Bitu index,Bitu count);
+
+/* Sub Groups */
+void INT10_SetupRomMemory(void);
+void INT10_SetupRomMemoryChecksum(void);
+void INT10_SetupVESA(void);
+
+/* EGA RIL */
+RealPt INT10_EGA_RIL_GetVersionPt(void);
+void INT10_EGA_RIL_ReadRegister(Bit8u & bl, Bit16u dx);
+void INT10_EGA_RIL_WriteRegister(Bit8u & bl, Bit8u bh, Bit16u dx);
+void INT10_EGA_RIL_ReadRegisterRange(Bit8u ch, Bit8u cl, Bit16u dx, PhysPt dst);
+void INT10_EGA_RIL_WriteRegisterRange(Bit8u ch, Bit8u cl, Bit16u dx, PhysPt dst);
+void INT10_EGA_RIL_ReadRegisterSet(Bit16u cx, PhysPt tbl);
+void INT10_EGA_RIL_WriteRegisterSet(Bit16u cx, PhysPt tbl);
+
+/* Video State */
+Bitu INT10_VideoState_GetSize(Bitu state);
+bool INT10_VideoState_Save(Bitu state,RealPt buffer);
+bool INT10_VideoState_Restore(Bitu state,RealPt buffer);
+
+/* Video Parameter Tables */
+Bit16u INT10_SetupVideoParameterTable(PhysPt basepos);
+void INT10_SetupBasicVideoParameterTable(void);
diff --git a/src/ints/int10_char.cpp b/src/ints/int10_char.cpp
new file mode 100644
index 000000000..084f25078
--- /dev/null
+++ b/src/ints/int10_char.cpp
@@ -0,0 +1,721 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+/* Character displaying moving functions */
+
+#include "dosbox.h"
+#include "bios.h"
+#include "mem.h"
+#include "inout.h"
+#include "int10.h"
+#include "pic.h"
+#include "callback.h"
+
+static void CGA2_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
+ Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ PhysPt dest=base+((CurMode->twidth*rnew)*(cheight/2)+cleft);
+ PhysPt src=base+((CurMode->twidth*rold)*(cheight/2)+cleft);
+ Bitu copy=(cright-cleft);
+ Bitu nextline=CurMode->twidth;
+ for (Bitu i=0;i<cheight/2U;i++) {
+ MEM_BlockCopy(dest,src,copy);
+ MEM_BlockCopy(dest+8*1024,src+8*1024,copy);
+ dest+=nextline;src+=nextline;
+ }
+}
+
+static void CGA4_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
+ Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ PhysPt dest=base+((CurMode->twidth*rnew)*(cheight/2)+cleft)*2;
+ PhysPt src=base+((CurMode->twidth*rold)*(cheight/2)+cleft)*2;
+ Bitu copy=(cright-cleft)*2;Bitu nextline=CurMode->twidth*2;
+ for (Bitu i=0;i<cheight/2U;i++) {
+ MEM_BlockCopy(dest,src,copy);
+ MEM_BlockCopy(dest+8*1024,src+8*1024,copy);
+ dest+=nextline;src+=nextline;
+ }
+}
+
+static void TANDY16_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
+ Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ Bit8u banks=CurMode->twidth/10;
+ PhysPt dest=base+((CurMode->twidth*rnew)*(cheight/banks)+cleft)*4;
+ PhysPt src=base+((CurMode->twidth*rold)*(cheight/banks)+cleft)*4;
+ Bitu copy=(cright-cleft)*4;Bitu nextline=CurMode->twidth*4;
+ for (Bitu i=0;i<static_cast<Bitu>(cheight/banks);i++) {
+ for (Bitu b=0;b<banks;b++) MEM_BlockCopy(dest+b*8*1024,src+b*8*1024,copy);
+ dest+=nextline;src+=nextline;
+ }
+}
+
+static void EGA16_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
+ PhysPt src,dest;Bitu copy;
+ Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ dest=base+(CurMode->twidth*rnew)*cheight+cleft;
+ src=base+(CurMode->twidth*rold)*cheight+cleft;
+ Bitu nextline=CurMode->twidth;
+ /* Setup registers correctly */
+ IO_Write(0x3ce,5);IO_Write(0x3cf,1); /* Memory transfer mode */
+ IO_Write(0x3c4,2);IO_Write(0x3c5,0xf); /* Enable all Write planes */
+ /* Do some copying */
+ Bitu rowsize=(cright-cleft);
+ copy=cheight;
+ for (;copy>0;copy--) {
+ for (Bitu x=0;x<rowsize;x++) mem_writeb(dest+x,mem_readb(src+x));
+ dest+=nextline;src+=nextline;
+ }
+ /* Restore registers */
+ IO_Write(0x3ce,5);IO_Write(0x3cf,0); /* Normal transfer mode */
+}
+
+static void VGA_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
+ PhysPt src,dest;Bitu copy;
+ Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ dest=base+8*((CurMode->twidth*rnew)*cheight+cleft);
+ src=base+8*((CurMode->twidth*rold)*cheight+cleft);
+ Bitu nextline=8*CurMode->twidth;
+ Bitu rowsize=8*(cright-cleft);
+ copy=cheight;
+ for (;copy>0;copy--) {
+ for (Bitu x=0;x<rowsize;x++) mem_writeb(dest+x,mem_readb(src+x));
+ dest+=nextline;src+=nextline;
+ }
+}
+
+static void TEXT_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
+ PhysPt src,dest;
+ src=base+(rold*CurMode->twidth+cleft)*2;
+ dest=base+(rnew*CurMode->twidth+cleft)*2;
+ MEM_BlockCopy(dest,src,(cright-cleft)*2);
+}
+
+static void CGA2_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
+ Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ PhysPt dest=base+((CurMode->twidth*row)*(cheight/2)+cleft);
+ Bitu copy=(cright-cleft);
+ Bitu nextline=CurMode->twidth;
+ attr=(attr & 0x3) | ((attr & 0x3) << 2) | ((attr & 0x3) << 4) | ((attr & 0x3) << 6);
+ for (Bitu i=0;i<cheight/2U;i++) {
+ for (Bitu x=0;x<copy;x++) {
+ mem_writeb(dest+x,attr);
+ mem_writeb(dest+8*1024+x,attr);
+ }
+ dest+=nextline;
+ }
+}
+
+static void CGA4_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
+ Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ PhysPt dest=base+((CurMode->twidth*row)*(cheight/2)+cleft)*2;
+ Bitu copy=(cright-cleft)*2;Bitu nextline=CurMode->twidth*2;
+ attr=(attr & 0x3) | ((attr & 0x3) << 2) | ((attr & 0x3) << 4) | ((attr & 0x3) << 6);
+ for (Bitu i=0;i<cheight/2U;i++) {
+ for (Bitu x=0;x<copy;x++) {
+ mem_writeb(dest+x,attr);
+ mem_writeb(dest+8*1024+x,attr);
+ }
+ dest+=nextline;
+ }
+}
+
+static void TANDY16_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
+ Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ Bit8u banks=CurMode->twidth/10;
+ PhysPt dest=base+((CurMode->twidth*row)*(cheight/banks)+cleft)*4;
+ Bitu copy=(cright-cleft)*4;Bitu nextline=CurMode->twidth*4;
+ attr=(attr & 0xf) | (attr & 0xf) << 4;
+ for (Bitu i=0;i<static_cast<Bitu>(cheight/banks);i++) {
+ for (Bitu x=0;x<copy;x++) {
+ for (Bitu b=0;b<banks;b++) mem_writeb(dest+b*8*1024+x,attr);
+ }
+ dest+=nextline;
+ }
+}
+
+static void EGA16_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
+ /* Set Bitmask / Color / Full Set Reset */
+ IO_Write(0x3ce,0x8);IO_Write(0x3cf,0xff);
+ IO_Write(0x3ce,0x0);IO_Write(0x3cf,attr);
+ IO_Write(0x3ce,0x1);IO_Write(0x3cf,0xf);
+ /* Enable all Write planes */
+ IO_Write(0x3c4,2);IO_Write(0x3c5,0xf);
+ /* Write some bytes */
+ Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ PhysPt dest=base+(CurMode->twidth*row)*cheight+cleft;
+ Bitu nextline=CurMode->twidth;
+ Bitu copy = cheight;Bitu rowsize=(cright-cleft);
+ for (;copy>0;copy--) {
+ for (Bitu x=0;x<rowsize;x++) mem_writeb(dest+x,0xff);
+ dest+=nextline;
+ }
+ IO_Write(0x3cf,0);
+}
+
+static void VGA_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
+ /* Write some bytes */
+ Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ PhysPt dest=base+8*((CurMode->twidth*row)*cheight+cleft);
+ Bitu nextline=8*CurMode->twidth;
+ Bitu copy = cheight;Bitu rowsize=8*(cright-cleft);
+ for (;copy>0;copy--) {
+ for (Bitu x=0;x<rowsize;x++) mem_writeb(dest+x,attr);
+ dest+=nextline;
+ }
+}
+
+static void TEXT_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
+ /* Do some filing */
+ PhysPt dest;
+ dest=base+(row*CurMode->twidth+cleft)*2;
+ Bit16u fill=(attr<<8)+' ';
+ for (Bit8u x=0;x<(cright-cleft);x++) {
+ mem_writew(dest,fill);
+ dest+=2;
+ }
+}
+
+
+void INT10_ScrollWindow(Bit8u rul,Bit8u cul,Bit8u rlr,Bit8u clr,Bit8s nlines,Bit8u attr,Bit8u page) {
+/* Do some range checking */
+ if (CurMode->type!=M_TEXT) page=0xff;
+ BIOS_NCOLS;BIOS_NROWS;
+ if(rul>rlr) return;
+ if(cul>clr) return;
+ if(rlr>=nrows) rlr=(Bit8u)nrows-1;
+ if(clr>=ncols) clr=(Bit8u)ncols-1;
+ clr++;
+
+ /* Get the correct page: current start address for current page (0xFF),
+ otherwise calculate from page number and page size */
+ PhysPt base=CurMode->pstart;
+ if (page==0xff) base+=real_readw(BIOSMEM_SEG,BIOSMEM_CURRENT_START);
+ else base+=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
+
+ if (GCC_UNLIKELY(machine==MCH_PCJR)) {
+ if (real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_MODE) >= 9) {
+ // PCJr cannot handle these modes at 0xb800
+ // See INT10_PutPixel M_TANDY16
+ Bitu cpupage =
+ (real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7;
+
+ base = cpupage << 14;
+ if (page!=0xff)
+ base += page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
+ }
+ }
+
+ /* See how much lines need to be copied */
+ Bit8u start,end;Bits next;
+ /* Copy some lines */
+ if (nlines>0) {
+ start=rlr-nlines+1;
+ end=rul;
+ next=-1;
+ } else if (nlines<0) {
+ start=rul-nlines-1;
+ end=rlr;
+ next=1;
+ } else {
+ nlines=rlr-rul+1;
+ goto filling;
+ }
+ while (start!=end) {
+ start+=next;
+ switch (CurMode->type) {
+ case M_TEXT:
+ TEXT_CopyRow(cul,clr,start,start+nlines,base);break;
+ case M_CGA2:
+ CGA2_CopyRow(cul,clr,start,start+nlines,base);break;
+ case M_CGA4:
+ CGA4_CopyRow(cul,clr,start,start+nlines,base);break;
+ case M_TANDY16:
+ TANDY16_CopyRow(cul,clr,start,start+nlines,base);break;
+ case M_EGA:
+ EGA16_CopyRow(cul,clr,start,start+nlines,base);break;
+ case M_VGA:
+ VGA_CopyRow(cul,clr,start,start+nlines,base);break;
+ case M_LIN4:
+ if ((machine==MCH_VGA) && (svgaCard==SVGA_TsengET4K) &&
+ (CurMode->swidth<=800)) {
+ // the ET4000 BIOS supports text output in 800x600 SVGA
+ EGA16_CopyRow(cul,clr,start,start+nlines,base);break;
+ }
+ // fall-through
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("Unhandled mode %d for scroll",CurMode->type);
+ }
+ }
+ /* Fill some lines */
+filling:
+ if (nlines>0) {
+ start=rul;
+ } else {
+ nlines=-nlines;
+ start=rlr-nlines+1;
+ }
+ for (;nlines>0;nlines--) {
+ switch (CurMode->type) {
+ case M_TEXT:
+ TEXT_FillRow(cul,clr,start,base,attr);break;
+ case M_CGA2:
+ CGA2_FillRow(cul,clr,start,base,attr);break;
+ case M_CGA4:
+ CGA4_FillRow(cul,clr,start,base,attr);break;
+ case M_TANDY16:
+ TANDY16_FillRow(cul,clr,start,base,attr);break;
+ case M_EGA:
+ EGA16_FillRow(cul,clr,start,base,attr);break;
+ case M_VGA:
+ VGA_FillRow(cul,clr,start,base,attr);break;
+ case M_LIN4:
+ if ((machine==MCH_VGA) && (svgaCard==SVGA_TsengET4K) &&
+ (CurMode->swidth<=800)) {
+ EGA16_FillRow(cul,clr,start,base,attr);break;
+ }
+ // fall-through
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("Unhandled mode %d for scroll",CurMode->type);
+ }
+ start++;
+ }
+}
+
+void INT10_SetActivePage(Bit8u page) {
+ Bit16u mem_address;
+ if (page>7) LOG(LOG_INT10,LOG_ERROR)("INT10_SetActivePage page %d",page);
+
+ if (IS_EGAVGA_ARCH && (svgaCard==SVGA_S3Trio)) page &= 7;
+
+ mem_address=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
+ /* Write the new page start */
+ real_writew(BIOSMEM_SEG,BIOSMEM_CURRENT_START,mem_address);
+ if (IS_EGAVGA_ARCH) {
+ if (CurMode->mode<8) mem_address>>=1;
+ // rare alternative: if (CurMode->type==M_TEXT) mem_address>>=1;
+ } else {
+ mem_address>>=1;
+ }
+ /* Write the new start address in vgahardware */
+ Bit16u base=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ IO_Write(base,0x0c);
+ IO_Write(base+1,(Bit8u)(mem_address>>8));
+ IO_Write(base,0x0d);
+ IO_Write(base+1,(Bit8u)mem_address);
+
+ // And change the BIOS page
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE,page);
+ Bit8u cur_row=CURSOR_POS_ROW(page);
+ Bit8u cur_col=CURSOR_POS_COL(page);
+ // Display the cursor, now the page is active
+ INT10_SetCursorPos(cur_row,cur_col,page);
+}
+
+void INT10_SetCursorShape(Bit8u first,Bit8u last) {
+ real_writew(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE,last|(first<<8));
+ if (machine==MCH_CGA) goto dowrite;
+ if (IS_TANDY_ARCH) goto dowrite;
+ /* Skip CGA cursor emulation if EGA/VGA system is active */
+ if (!(real_readb(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL) & 0x8)) {
+ /* Check for CGA type 01, invisible */
+ if ((first & 0x60) == 0x20) {
+ first=0x1e;
+ last=0x00;
+ goto dowrite;
+ }
+ /* Check if we need to convert CGA Bios cursor values */
+ if (!(real_readb(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL) & 0x1)) { // set by int10 fun12 sub34
+// if (CurMode->mode>0x3) goto dowrite; //Only mode 0-3 are text modes on cga
+ if ((first & 0xe0) || (last & 0xe0)) goto dowrite;
+ Bit8u cheight=real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT)-1;
+ /* Creative routine i based of the original ibmvga bios */
+
+ if (last<first) {
+ if (!last) goto dowrite;
+ first=last;
+ last=cheight;
+ /* Test if this might be a cga style cursor set, if not don't do anything */
+ } else if (((first | last)>=cheight) || !(last==(cheight-1)) || !(first==cheight) ) {
+ if (last<=3) goto dowrite;
+ if (first+2<last) {
+ if (first>2) {
+ first=(cheight+1)/2;
+ last=cheight;
+ } else {
+ last=cheight;
+ }
+ } else {
+ first=(first-last)+cheight;
+ last=cheight;
+
+ if (cheight>0xc) { // vgatest sets 15 15 2x where only one should be decremented to 14 14
+ first--; // implementing int10 fun12 sub34 fixed this.
+ last--;
+ }
+ }
+ }
+
+ }
+ }
+dowrite:
+ Bit16u base=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ IO_Write(base,0xa);IO_Write(base+1,first);
+ IO_Write(base,0xb);IO_Write(base+1,last);
+}
+
+void INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page) {
+ Bit16u address;
+
+ if (page>7) LOG(LOG_INT10,LOG_ERROR)("INT10_SetCursorPos page %d",page);
+ // Bios cursor pos
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2,col);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2+1,row);
+ // Set the hardware cursor
+ Bit8u current=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+ if(page==current) {
+ // Get the dimensions
+ BIOS_NCOLS;
+ // Calculate the address knowing nbcols nbrows and page num
+ // NOTE: BIOSMEM_CURRENT_START counts in colour/flag pairs
+ address=(ncols*row)+col+real_readw(BIOSMEM_SEG,BIOSMEM_CURRENT_START)/2;
+ // CRTC regs 0x0e and 0x0f
+ Bit16u base=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ IO_Write(base,0x0e);
+ IO_Write(base+1,(Bit8u)(address>>8));
+ IO_Write(base,0x0f);
+ IO_Write(base+1,(Bit8u)address);
+ }
+}
+
+void ReadCharAttr(Bit16u col,Bit16u row,Bit8u page,Bit16u * result) {
+ /* Externally used by the mouse routine */
+ PhysPt fontdata;
+ Bit16u cols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ bool split_chr = false;
+ switch (CurMode->type) {
+ case M_TEXT:
+ {
+ // Compute the address
+ Bit16u address=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
+ address+=(row*cols+col)*2;
+ // read the char
+ PhysPt where = CurMode->pstart+address;
+ *result=mem_readw(where);
+ }
+ return;
+ case M_CGA4:
+ case M_CGA2:
+ case M_TANDY16:
+ split_chr = true;
+ switch (machine) {
+ case MCH_CGA:
+ case MCH_HERC:
+ fontdata=PhysMake(0xf000,0xfa6e);
+ break;
+ case TANDY_ARCH_CASE:
+ fontdata=Real2Phys(RealGetVec(0x44));
+ break;
+ default:
+ fontdata=Real2Phys(RealGetVec(0x43));
+ break;
+ }
+ break;
+ default:
+ fontdata=Real2Phys(RealGetVec(0x43));
+ break;
+ }
+
+ Bitu x=col*8,y=row*cheight*(cols/CurMode->twidth);
+
+ for (Bit16u chr=0;chr<256;chr++) {
+
+ if (chr==128 && split_chr) fontdata=Real2Phys(RealGetVec(0x1f));
+
+ bool error=false;
+ Bit16u ty=(Bit16u)y;
+ for (Bit8u h=0;h<cheight;h++) {
+ Bit8u bitsel=128;
+ Bit8u bitline=mem_readb(fontdata++);
+ Bit8u res=0;
+ Bit8u vidline=0;
+ Bit16u tx=(Bit16u)x;
+ while (bitsel) {
+ //Construct bitline in memory
+ INT10_GetPixel(tx,ty,page,&res);
+ if(res) vidline|=bitsel;
+ tx++;
+ bitsel>>=1;
+ }
+ ty++;
+ if(bitline != vidline){
+ /* It's not character 'chr', move on to the next */
+ fontdata+=(cheight-h-1);
+ error = true;
+ break;
+ }
+ }
+ if(!error){
+ /* We found it */
+ *result = chr;
+ return;
+ }
+ }
+ LOG(LOG_INT10,LOG_ERROR)("ReadChar didn't find character");
+ *result = 0;
+}
+void INT10_ReadCharAttr(Bit16u * result,Bit8u page) {
+ if(page==0xFF) page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+ Bit8u cur_row=CURSOR_POS_ROW(page);
+ Bit8u cur_col=CURSOR_POS_COL(page);
+ ReadCharAttr(cur_col,cur_row,page,result);
+}
+void WriteChar(Bit16u col,Bit16u row,Bit8u page,Bit8u chr,Bit8u attr,bool useattr) {
+ /* Externally used by the mouse routine */
+ PhysPt fontdata;
+ Bit16u cols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ Bit8u back,cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ switch (CurMode->type) {
+ case M_TEXT:
+ {
+ // Compute the address
+ Bit16u address=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
+ address+=(row*cols+col)*2;
+ // Write the char
+ PhysPt where = CurMode->pstart+address;
+ mem_writeb(where,chr);
+ if (useattr) mem_writeb(where+1,attr);
+ }
+ return;
+ case M_CGA4:
+ case M_CGA2:
+ case M_TANDY16:
+ if (chr>=128) {
+ chr-=128;
+ fontdata=Real2Phys(RealGetVec(0x1f));
+ break;
+ }
+ switch (machine) {
+ case MCH_CGA:
+ case MCH_HERC:
+ fontdata=PhysMake(0xf000,0xfa6e);
+ break;
+ case TANDY_ARCH_CASE:
+ fontdata=Real2Phys(RealGetVec(0x44));
+ break;
+ default:
+ fontdata=Real2Phys(RealGetVec(0x43));
+ break;
+ }
+ break;
+ default:
+ fontdata=Real2Phys(RealGetVec(0x43));
+ break;
+ }
+ fontdata+=chr*cheight;
+
+ if(GCC_UNLIKELY(!useattr)) { //Set attribute(color) to a sensible value
+ static bool warned_use = false;
+ if(GCC_UNLIKELY(!warned_use)){
+ LOG(LOG_INT10,LOG_ERROR)("writechar used without attribute in non-textmode %c %X",chr,chr);
+ warned_use = true;
+ }
+ switch(CurMode->type) {
+ case M_CGA4:
+ attr = 0x3;
+ break;
+ case M_CGA2:
+ attr = 0x1;
+ break;
+ case M_TANDY16:
+ case M_EGA:
+ default:
+ attr = 0xf;
+ break;
+ }
+ }
+
+ //Attribute behavior of mode 6; mode 11 does something similar but
+ //it is in INT 10h handler because it only applies to function 09h
+ if (CurMode->mode==0x06) attr=(attr&0x80)|1;
+
+ switch (CurMode->type) {
+ case M_VGA:
+ case M_LIN8:
+ // 256-color modes have background color instead of page
+ back=page;
+ page=0;
+ break;
+ case M_EGA:
+ /* enable all planes for EGA modes (Ultima 1 colour bug) */
+ /* might be put into INT10_PutPixel but different vga bios
+ implementations have different opinions about this */
+ IO_Write(0x3c4,0x2);IO_Write(0x3c5,0xf);
+ // fall-through
+ default:
+ back=attr&0x80;
+ break;
+ }
+
+ Bitu x=col*8,y=row*cheight*(cols/CurMode->twidth);
+
+ Bit16u ty=(Bit16u)y;
+ for (Bit8u h=0;h<cheight;h++) {
+ Bit8u bitsel=128;
+ Bit8u bitline=mem_readb(fontdata++);
+ Bit16u tx=(Bit16u)x;
+ while (bitsel) {
+ INT10_PutPixel(tx,ty,page,(bitline&bitsel)?attr:back);
+ tx++;
+ bitsel>>=1;
+ }
+ ty++;
+ }
+}
+
+void INT10_WriteChar(Bit8u chr,Bit8u attr,Bit8u page,Bit16u count,bool showattr) {
+ Bit8u pospage=page;
+ if (CurMode->type!=M_TEXT) {
+ showattr=true; //Use attr in graphics mode always
+ switch (machine) {
+ case EGAVGA_ARCH_CASE:
+ switch (CurMode->type) {
+ case M_VGA:
+ case M_LIN8:
+ pospage=0;
+ break;
+ default:
+ page%=CurMode->ptotal;
+ pospage=page;
+ break;
+ }
+ break;
+ case MCH_CGA:
+ case MCH_PCJR:
+ page=0;
+ pospage=0;
+ break;
+ }
+ }
+
+ Bit8u cur_row=CURSOR_POS_ROW(pospage);
+ Bit8u cur_col=CURSOR_POS_COL(pospage);
+ BIOS_NCOLS;
+ while (count>0) {
+ WriteChar(cur_col,cur_row,page,chr,attr,showattr);
+ count--;
+ cur_col++;
+ if(cur_col==ncols) {
+ cur_col=0;
+ cur_row++;
+ }
+ }
+
+ if (CurMode->type==M_EGA) {
+ // Reset write ops for EGA graphics modes
+ IO_Write(0x3ce,0x3);IO_Write(0x3cf,0x0);
+ }
+}
+
+static void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr,Bit8u page) {
+ BIOS_NCOLS;BIOS_NROWS;
+ Bit8u cur_row=CURSOR_POS_ROW(page);
+ Bit8u cur_col=CURSOR_POS_COL(page);
+ switch (chr) {
+ case 7: /* Beep */
+ // Prepare PIT counter 2 for ~900 Hz square wave
+ IO_Write(0x43,0xb6);
+ IO_Write(0x42,0x28);
+ IO_Write(0x42,0x05);
+ // Speaker on
+ IO_Write(0x61,IO_Read(0x61)|3);
+ // Idle for 1/3rd of a second
+ double start;
+ start=PIC_FullIndex();
+ while ((PIC_FullIndex()-start)<333.0) CALLBACK_Idle();
+ // Speaker off
+ IO_Write(0x61,IO_Read(0x61)&~3);
+ // No change in position
+ return;
+ case 8:
+ if(cur_col>0) cur_col--;
+ break;
+ case '\r':
+ cur_col=0;
+ break;
+ case '\n':
+// cur_col=0; //Seems to break an old chess game
+ cur_row++;
+ break;
+ default:
+ /* Draw the actual Character */
+ WriteChar(cur_col,cur_row,page,chr,attr,useattr);
+ cur_col++;
+ }
+ if(cur_col==ncols) {
+ cur_col=0;
+ cur_row++;
+ }
+ // Do we need to scroll ?
+ if(cur_row==nrows) {
+ //Fill with black on non-text modes and with attribute at cursor on textmode
+ Bit8u fill=0;
+ if (CurMode->type==M_TEXT) {
+ Bit16u chat;
+ INT10_ReadCharAttr(&chat,page);
+ fill=(Bit8u)(chat>>8);
+ }
+ INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,fill,page);
+ cur_row--;
+ }
+ // Set the cursor for the page
+ INT10_SetCursorPos(cur_row,cur_col,page);
+}
+
+void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr) {
+ INT10_TeletypeOutputAttr(chr,attr,useattr,real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE));
+}
+
+void INT10_TeletypeOutput(Bit8u chr,Bit8u attr) {
+ INT10_TeletypeOutputAttr(chr,attr,CurMode->type!=M_TEXT);
+}
+
+void INT10_WriteString(Bit8u row,Bit8u col,Bit8u flag,Bit8u attr,PhysPt string,Bit16u count,Bit8u page) {
+ Bit8u cur_row=CURSOR_POS_ROW(page);
+ Bit8u cur_col=CURSOR_POS_COL(page);
+
+ // if row=0xff special case : use current cursor position
+ if (row==0xff) {
+ row=cur_row;
+ col=cur_col;
+ }
+ INT10_SetCursorPos(row,col,page);
+ while (count>0) {
+ Bit8u chr=mem_readb(string);
+ string++;
+ if (flag&2) {
+ attr=mem_readb(string);
+ string++;
+ };
+ INT10_TeletypeOutputAttr(chr,attr,true,page);
+ count--;
+ }
+ if (!(flag&1)) {
+ INT10_SetCursorPos(cur_row,cur_col,page);
+ }
+}
diff --git a/src/ints/int10_memory.cpp b/src/ints/int10_memory.cpp
new file mode 100644
index 000000000..e3c40e2a8
--- /dev/null
+++ b/src/ints/int10_memory.cpp
@@ -0,0 +1,1634 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "mem.h"
+#include "inout.h"
+#include "int10.h"
+
+
+static Bit8u static_functionality[0x10]=
+{
+ /* 0 */ 0xff, // All modes supported #1
+ /* 1 */ 0xff, // All modes supported #2
+ /* 2 */ 0x0f, // All modes supported #3
+ /* 3 */ 0x00, 0x00, 0x00, 0x00, // reserved
+ /* 7 */ 0x07, // 200, 350, 400 scan lines
+ /* 8 */ 0x04, // total number of character blocks available in text modes
+ /* 9 */ 0x02, // maximum number of active character blocks in text modes
+ /* a */ 0xff, // Misc Flags Everthing supported
+ /* b */ 0x0e, // Support for Display combination, intensity/blinking and video state saving/restoring
+ /* c */ 0x00, // reserved
+ /* d */ 0x00, // reserved
+ /* e */ 0x00, // Change to add new functions
+ /* f */ 0x00 // reserved
+};
+
+static Bit16u map_offset[8]={
+ 0x0000,0x4000,0x8000,0xc000,
+ 0x2000,0x6000,0xa000,0xe000
+};
+
+void INT10_LoadFont(PhysPt font,bool reload,Bitu count,Bitu offset,Bitu map,Bitu height) {
+ PhysPt ftwhere=PhysMake(0xa000,map_offset[map & 0x7]+(Bit16u)(offset*32));
+ Bit16u base=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ bool mono=(base==VGAREG_MDA_CRTC_ADDRESS);
+
+ //Put video adapter in planar mode
+ IO_Write(0x3c4,0x02);IO_Write(0x3c5,0x04); // select plane 2 for writing
+ IO_Write(0x3c4,0x04);IO_Write(0x3c5,0x07); // odd/even off in SEQ
+ IO_Write(0x3ce,0x04);IO_Write(0x3cf,0x02); // select plane 2 for reading
+ IO_Write(0x3ce,0x05);IO_Write(0x3cf,0x00); // write mode 0, odd/even off in GFX
+ IO_Write(0x3ce,0x06);IO_Write(0x3cf,0x04); // CPU memory window A0000-AFFFF
+
+ //Load character patterns
+ for (Bitu i=0;i<count;i++) {
+ MEM_BlockCopy(ftwhere+i*32,font,height);
+ font+=height;
+ }
+ //Load alternate character patterns
+ if (map & 0x80) {
+ while (Bitu chr=(Bitu)mem_readb(font++)) {
+ MEM_BlockCopy(ftwhere+chr*32,font,height);
+ font+=height;
+ }
+ }
+
+ //Return to normal text mode
+ IO_Write(0x3c4,0x02);IO_Write(0x3c5,0x03); // select planes 0&1 for writing
+ IO_Write(0x3c4,0x04);IO_Write(0x3c5,0x03); // odd/even on in SEQ
+ IO_Write(0x3ce,0x04);IO_Write(0x3cf,0x00); // select plane 0 for reading
+ IO_Write(0x3ce,0x05);IO_Write(0x3cf,0x10); // write mode 0, odd/even on in GFX
+ IO_Write(0x3ce,0x06);IO_Write(0x3cf,mono?0x0a:0x0e); // Bx000-BxFFF, odd/even on
+
+ /* Reload tables and registers with new values based on this height */
+ if (reload) {
+ //Max scanline
+ IO_Write(base,0x9);
+ IO_Write(base+1,(IO_Read(base+1) & 0xe0)|(height-1));
+ //Vertical display end
+ Bitu rows=CurMode->sheight/height;
+ Bitu vdend=rows*height*((CurMode->sheight==200)?2:1)-1;
+ IO_Write(base,0x12);
+ IO_Write(base+1,(Bit8u)vdend);
+ //Underline location
+ if (CurMode->mode==7) {
+ IO_Write(base,0x14);
+ IO_Write(base+1,(IO_Read(base+1) & ~0x1f)|(height-1));
+ }
+ //Rows setting in bios segment
+ real_writeb(BIOSMEM_SEG,BIOSMEM_NB_ROWS,rows-1);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,(Bit8u)height);
+ //Page size
+ Bitu pagesize=rows*real_readb(BIOSMEM_SEG,BIOSMEM_NB_COLS)*2;
+ pagesize+=0x100; // bios adds extra on reload
+ real_writew(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE,pagesize);
+ //Cursor shape
+ if (height>=14) height--; // move up one line on 14+ line fonts
+ INT10_SetCursorShape(height-2,height-1);
+ }
+}
+
+void INT10_ReloadFont(void) {
+ Bitu map=0;
+ switch(CurMode->cheight) {
+ case 8:
+ INT10_LoadFont(Real2Phys(int10.rom.font_8_first),false,256,0,map,8);
+ break;
+ case 14:
+ if (IS_VGA_ARCH && svgaCard==SVGA_None && CurMode->mode==7) map=0x80;
+ INT10_LoadFont(Real2Phys(int10.rom.font_14),false,256,0,map,14);
+ break;
+ case 16:
+ default:
+ if (IS_VGA_ARCH && svgaCard==SVGA_None) map=0x80;
+ INT10_LoadFont(Real2Phys(int10.rom.font_16),false,256,0,map,16);
+ break;
+ }
+}
+
+
+void INT10_SetupRomMemory(void) {
+/* This should fill up certain structures inside the Video Bios Rom Area */
+ PhysPt rom_base=PhysMake(0xc000,0);
+ Bitu i;
+ int10.rom.used=3;
+ if (IS_EGAVGA_ARCH) {
+ // set up the start of the ROM
+ phys_writew(rom_base+0,0xaa55);
+ phys_writeb(rom_base+2,0x40); // Size of ROM: 64 512-blocks = 32KB
+ phys_writeb(rom_base+0x1e,0x49); // IBM string
+ phys_writeb(rom_base+0x1f,0x42);
+ phys_writeb(rom_base+0x20,0x4d);
+ phys_writeb(rom_base+0x21,0x20);
+ int10.rom.used=0x100;
+ }
+
+ if (IS_VGA_ARCH && svgaCard==SVGA_S3Trio) INT10_SetupVESA();
+
+ int10.rom.font_8_first=RealMake(0xC000,int10.rom.used);
+ for (i=0;i<128*8;i++) {
+ phys_writeb(rom_base+int10.rom.used++,int10_font_08[i]);
+ }
+ int10.rom.font_8_second=RealMake(0xC000,int10.rom.used);
+ for (i=0;i<128*8;i++) {
+ phys_writeb(rom_base+int10.rom.used++,int10_font_08[i+128*8]);
+ }
+ int10.rom.font_14=RealMake(0xC000,int10.rom.used);
+ for (i=0;i<256*14;i++) {
+ phys_writeb(rom_base+int10.rom.used++,int10_font_14[i]);
+ }
+ int10.rom.font_14_alternate=RealMake(0xC000,int10.rom.used);
+ for (i=0;i<20*15+1;i++) {
+ phys_writeb(rom_base+int10.rom.used++,int10_font_14_alternate[i]);
+ }
+ int10.rom.font_16=RealMake(0xC000,int10.rom.used);
+ for (i=0;i<256*16;i++) {
+ phys_writeb(rom_base+int10.rom.used++,int10_font_16[i]);
+ }
+ int10.rom.font_16_alternate=RealMake(0xC000,int10.rom.used);
+ for (i=0;i<19*17+1;i++) {
+ phys_writeb(rom_base+int10.rom.used++,int10_font_16_alternate[i]);
+ }
+ int10.rom.static_state=RealMake(0xC000,int10.rom.used);
+ for (i=0;i<0x10;i++) {
+ phys_writeb(rom_base+int10.rom.used++,static_functionality[i]);
+ }
+ for (i=0;i<128*8;i++) {
+ phys_writeb(PhysMake(0xf000,0xfa6e)+i,int10_font_08[i]);
+ }
+ RealSetVec(0x1F,int10.rom.font_8_second);
+
+ if (IS_EGAVGA_ARCH) {
+ int10.rom.video_parameter_table=RealMake(0xC000,int10.rom.used);
+ int10.rom.used+=INT10_SetupVideoParameterTable(rom_base+int10.rom.used);
+
+ if (IS_VGA_ARCH) {
+ int10.rom.video_dcc_table=RealMake(0xC000,int10.rom.used);
+ phys_writeb(rom_base+int10.rom.used++,0x10); // number of entries
+ phys_writeb(rom_base+int10.rom.used++,1); // version number
+ phys_writeb(rom_base+int10.rom.used++,8); // maximum display code
+ phys_writeb(rom_base+int10.rom.used++,0); // reserved
+ // display combination codes
+ phys_writew(rom_base+int10.rom.used,0x0000); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0100); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0200); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0102); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0400); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0104); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0500); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0502); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0600); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0601); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0605); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0800); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0801); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0700); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0702); int10.rom.used+=2;
+ phys_writew(rom_base+int10.rom.used,0x0706); int10.rom.used+=2;
+
+ int10.rom.video_save_pointer_table=RealMake(0xC000,int10.rom.used);
+ phys_writew(rom_base+int10.rom.used,0x1a); // length of table
+ int10.rom.used+=2;
+ phys_writed(rom_base+int10.rom.used,int10.rom.video_dcc_table);
+ int10.rom.used+=4;
+ phys_writed(rom_base+int10.rom.used,0); // alphanumeric charset override
+ int10.rom.used+=4;
+ phys_writed(rom_base+int10.rom.used,0); // user palette table
+ int10.rom.used+=4;
+ phys_writed(rom_base+int10.rom.used,0); int10.rom.used+=4;
+ phys_writed(rom_base+int10.rom.used,0); int10.rom.used+=4;
+ phys_writed(rom_base+int10.rom.used,0); int10.rom.used+=4;
+ }
+
+ int10.rom.video_save_pointers=RealMake(0xC000,int10.rom.used);
+ phys_writed(rom_base+int10.rom.used,int10.rom.video_parameter_table);
+ int10.rom.used+=4;
+ phys_writed(rom_base+int10.rom.used,0); // dynamic save area pointer
+ int10.rom.used+=4;
+ phys_writed(rom_base+int10.rom.used,0); // alphanumeric character set override
+ int10.rom.used+=4;
+ phys_writed(rom_base+int10.rom.used,0); // graphics character set override
+ int10.rom.used+=4;
+ if (IS_VGA_ARCH) {
+ phys_writed(rom_base+int10.rom.used,int10.rom.video_save_pointer_table);
+ } else {
+ phys_writed(rom_base+int10.rom.used,0); // secondary save pointer table
+ }
+ int10.rom.used+=4;
+ phys_writed(rom_base+int10.rom.used,0); int10.rom.used+=4;
+ phys_writed(rom_base+int10.rom.used,0); int10.rom.used+=4;
+ }
+
+ INT10_SetupBasicVideoParameterTable();
+ INT10_SetupRomMemoryChecksum();
+
+ if (IS_TANDY_ARCH) {
+ RealSetVec(0x44,RealMake(0xf000,0xfa6e));
+ }
+}
+
+void INT10_ReloadRomFonts(void) {
+ // 16x8 font
+ PhysPt font16pt=Real2Phys(int10.rom.font_16);
+ for (Bitu i=0;i<256*16;i++) {
+ phys_writeb(font16pt+i,int10_font_16[i]);
+ }
+ phys_writeb(Real2Phys(int10.rom.font_16_alternate),0x1d);
+ // 14x8 font
+ PhysPt font14pt=Real2Phys(int10.rom.font_14);
+ for (Bitu i=0;i<256*14;i++) {
+ phys_writeb(font14pt+i,int10_font_14[i]);
+ }
+ phys_writeb(Real2Phys(int10.rom.font_14_alternate),0x1d);
+ // 8x8 fonts
+ PhysPt font8pt=Real2Phys(int10.rom.font_8_first);
+ for (Bitu i=0;i<128*8;i++) {
+ phys_writeb(font8pt+i,int10_font_08[i]);
+ }
+ font8pt=Real2Phys(int10.rom.font_8_second);
+ for (Bitu i=0;i<128*8;i++) {
+ phys_writeb(font8pt+i,int10_font_08[i+128*8]);
+ }
+ INT10_SetupRomMemoryChecksum();
+}
+
+void INT10_SetupRomMemoryChecksum(void) {
+ if (IS_EGAVGA_ARCH) { //EGA/VGA. Just to be safe
+ /* Sum of all bytes in rom module 256 should be 0 */
+ Bit8u sum = 0;
+ PhysPt rom_base = PhysMake(0xc000,0);
+ Bitu last_rombyte = 32*1024 - 1; //32 KB romsize
+ for (Bitu i = 0;i < last_rombyte;i++)
+ sum += phys_readb(rom_base + i); //OVERFLOW IS OKAY
+ sum = (Bit8u)((256 - (Bitu)sum)&0xff);
+ phys_writeb(rom_base + last_rombyte,sum);
+ }
+}
+
+
+Bit8u int10_font_08[256 * 8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
+ 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
+ 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
+ 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
+ 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
+ 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
+ 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
+ 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
+ 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
+ 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
+ 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
+ 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
+ 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
+ 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
+ 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
+ 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
+ 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
+ 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
+ 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
+ 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
+ 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
+ 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
+ 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
+ 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
+ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
+ 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
+ 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
+ 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
+ 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
+ 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
+ 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
+ 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
+ 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
+ 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
+ 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
+ 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
+ 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
+ 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
+ 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
+ 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
+ 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
+ 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
+ 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
+ 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
+ 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
+ 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
+ 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
+ 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
+ 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
+ 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
+ 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
+ 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
+ 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
+ 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
+ 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
+ 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
+ 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
+ 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
+ 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
+ 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
+ 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
+ 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78,
+ 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00,
+ 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38,
+ 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00,
+ 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00,
+ 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00,
+ 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00,
+ 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00,
+ 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00,
+ 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
+ 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18,
+ 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00,
+ 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30,
+ 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7,
+ 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70,
+ 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00,
+ 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00,
+ 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00,
+ 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00,
+ 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00,
+ 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f,
+ 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03,
+ 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00,
+ 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00,
+ 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00,
+ 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0,
+ 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00,
+ 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00,
+ 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00,
+ 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0,
+ 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc,
+ 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00,
+ 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00,
+ 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0,
+ 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00,
+ 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00,
+ 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00,
+ 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00,
+ 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00,
+ 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70,
+ 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00,
+ 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00,
+ 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c,
+ 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00,
+ 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+Bit8u int10_font_14[256 * 14] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81,
+ 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff,
+ 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c,
+ 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18,
+ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c,
+ 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3,
+ 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32,
+ 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18,
+ 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x70, 0xf0,
+ 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63,
+ 0x7f, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c,
+ 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe, 0xf8,
+ 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x06, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x06,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c,
+ 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
+ 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x7c,
+ 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38,
+ 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18,
+ 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60,
+ 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0,
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c,
+ 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c,
+ 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06,
+ 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x66,
+ 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
+ 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
+ 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18,
+ 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6,
+ 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+ 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06,
+ 0x3c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe,
+ 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x60,
+ 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x0c,
+ 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6,
+ 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x0c,
+ 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30,
+ 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30,
+ 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c,
+ 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde,
+ 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6,
+ 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66,
+ 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0,
+ 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66,
+ 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66,
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66,
+ 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0,
+ 0xc0, 0xde, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xe6, 0x66, 0x6c, 0x6c,
+ 0x78, 0x6c, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60,
+ 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xe6,
+ 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60,
+ 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c,
+ 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66,
+ 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0xe6, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60,
+ 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xd6, 0xd6, 0xfe, 0x7c, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x38,
+ 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18,
+ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6,
+ 0x8c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c,
+ 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x3c, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc,
+ 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60,
+ 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x7c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
+ 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc,
+ 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
+ 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76,
+ 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66,
+ 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18,
+ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06,
+ 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66,
+ 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66,
+ 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6,
+ 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66,
+ 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c,
+ 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
+ 0xc6, 0x70, 0x1c, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30,
+ 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6,
+ 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38,
+ 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e,
+ 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x66, 0xfe, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18,
+ 0x70, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18,
+ 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18,
+ 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38,
+ 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc2,
+ 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, 0x00,
+ 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0x76, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30,
+ 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78,
+ 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x78, 0x0c, 0x7c,
+ 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x60,
+ 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc,
+ 0x76, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38,
+ 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66,
+ 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe,
+ 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xcc, 0xcc, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18,
+ 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x38,
+ 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18,
+ 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x60,
+ 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18,
+ 0x3c, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x10,
+ 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c,
+ 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c,
+ 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0x76, 0x36, 0x7e, 0xd8, 0xd8,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c,
+ 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xce, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60,
+ 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0xcc,
+ 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, 0x00, 0xc6,
+ 0xc6, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x00,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60,
+ 0x60, 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60,
+ 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc, 0xcc,
+ 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xc6, 0x00,
+ 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18,
+ 0x7e, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c,
+ 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x0c,
+ 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18,
+ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60,
+ 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66,
+ 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x76, 0xdc,
+ 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6,
+ 0xc6, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c,
+ 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60,
+ 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8,
+ 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x66,
+ 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x3c,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c,
+ 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+ 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x55, 0xaa,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77,
+ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+ 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe,
+ 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8,
+ 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc,
+ 0xc0, 0xc0, 0x40, 0x00, 0x00, 0x00, 0xfe, 0xc6,
+ 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c,
+ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30,
+ 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66,
+ 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
+ 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0xee, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c,
+ 0x3e, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb,
+ 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30,
+ 0x60, 0x60, 0x7c, 0x60, 0x60, 0x30, 0x1c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00,
+ 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18,
+ 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60,
+ 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8,
+ 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc,
+ 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0xec, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70,
+ 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+Bit8u int10_font_16[256 * 16] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd,
+ 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3,
+ 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe,
+ 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7,
+ 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff,
+ 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c,
+ 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3,
+ 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42,
+ 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd,
+ 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c,
+ 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30,
+ 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63,
+ 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7,
+ 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8,
+ 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e,
+ 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18,
+ 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6,
+ 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18,
+ 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe,
+ 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe,
+ 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0,
+ 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe,
+ 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c,
+ 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c,
+ 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18,
+ 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c,
+ 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06,
+ 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18,
+ 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff,
+ 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e,
+ 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18,
+ 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xd6, 0xd6,
+ 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30,
+ 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06,
+ 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe,
+ 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06,
+ 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18,
+ 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06,
+ 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60,
+ 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
+ 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06,
+ 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18,
+ 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde,
+ 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66,
+ 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66,
+ 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68,
+ 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68,
+ 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde,
+ 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78,
+ 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60,
+ 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60,
+ 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c,
+ 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c,
+ 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6,
+ 0xd6, 0xfe, 0xee, 0x6c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x7c, 0x38, 0x38,
+ 0x7c, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x86, 0x0c, 0x18, 0x30,
+ 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38,
+ 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66,
+ 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0,
+ 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe,
+ 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60,
+ 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66,
+ 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78,
+ 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66,
+ 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66,
+ 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66,
+ 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60,
+ 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30,
+ 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66,
+ 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6,
+ 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38,
+ 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18,
+ 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18,
+ 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18,
+ 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6,
+ 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe,
+ 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60,
+ 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe,
+ 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe,
+ 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe,
+ 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6,
+ 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6,
+ 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c,
+ 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x76, 0x36,
+ 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
+ 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x60, 0x60,
+ 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60,
+ 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
+ 0x7e, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde,
+ 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66,
+ 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde,
+ 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60,
+ 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06,
+ 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30,
+ 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30,
+ 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18,
+ 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8,
+ 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36,
+ 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+ 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8,
+ 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc,
+ 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c,
+ 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18,
+ 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66,
+ 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66,
+ 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe,
+ 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c,
+ 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66,
+ 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb,
+ 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb,
+ 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60,
+ 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe,
+ 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18,
+ 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c,
+ 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30,
+ 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e,
+ 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00,
+ 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec,
+ 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c,
+ 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+Bit8u int10_font_14_alternate[20 * 15 + 1] = {
+ 0x1d,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff,
+ 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x22,
+ 0x00, 0x63, 0x63, 0x63, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2b,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff,
+ 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x2d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x4d,
+ 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xdb, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00,
+ 0x54,
+ 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x56,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00,
+ 0x57,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb,
+ 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00,
+ 0x58,
+ 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18,
+ 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00,
+ 0x59,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x5a,
+ 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18,
+ 0x30, 0x61, 0xc3, 0xff, 0x00, 0x00, 0x00,
+ 0x6d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00,
+ 0x76,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3,
+ 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00,
+ 0x77,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3,
+ 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00,
+ 0x91,
+ 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b,
+ 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00,
+ 0x9b,
+ 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0,
+ 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x9d,
+ 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff,
+ 0x18, 0xff, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x9e,
+ 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66,
+ 0x6f, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00,
+ 0xf1,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0x18,
+ 0x18, 0x18, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0xf6,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00
+};
+
+Bit8u int10_font_16_alternate[19 * 17 + 1] = {
+ 0x1d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff,
+ 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30,
+ 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb,
+ 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x4d,
+ 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x54,
+ 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x56,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x57,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb,
+ 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x58,
+ 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18,
+ 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x59,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18,
+ 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x5a,
+ 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30,
+ 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x6d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00,
+ 0x76,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x77,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3,
+ 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c,
+ 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x91,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b,
+ 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00,
+ 0x9b,
+ 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0,
+ 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x9d,
+ 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18,
+ 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x9e,
+ 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f,
+ 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00,
+ 0xab,
+ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30,
+ 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00,
+ 0xac,
+ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30,
+ 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00,
+ 0x00
+};
diff --git a/src/ints/int10_misc.cpp b/src/ints/int10_misc.cpp
new file mode 100644
index 000000000..cc4673d81
--- /dev/null
+++ b/src/ints/int10_misc.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "mem.h"
+#include "inout.h"
+#include "int10.h"
+
+
+
+#pragma pack(1)
+struct Dynamic_Functionality {
+ RealPt static_table; /* 00h DWORD address of static functionality table */
+ Bit8u cur_mode; /* 04h BYTE video mode in effect */
+ Bit16u num_cols; /* 05h WORD number of columns */
+ Bit16u regen_size; /* 07h WORD length of regen buffer in bytes */
+ Bit16u regen_start; /* 09h WORD starting address of regen buffer*/
+ Bit16u cursor_pos[8]; /* 0Bh WORD cursor position for page 0-7 */
+ Bit16u cursor_type; /* 1Bh WORD cursor type */
+ Bit8u active_page; /* 1Dh BYTE active display page */
+ Bit16u crtc_address; /* 1Eh WORD CRTC port address */
+ Bit8u reg_3x8; /* 20h BYTE current setting of register (3?8) */
+ Bit8u reg_3x9; /* 21h BYTE current setting of register (3?9) */
+ Bit8u num_rows; /* 22h BYTE number of rows */
+ Bit16u bytes_per_char; /* 23h WORD bytes/character */
+ Bit8u dcc; /* 25h BYTE display combination code of active display */
+ Bit8u dcc_alternate; /* 26h BYTE DCC of alternate display */
+ Bit16u num_colors; /* 27h WORD number of colors supported in current mode */
+ Bit8u num_pages; /* 29h BYTE number of pages supported in current mode */
+ Bit8u num_scanlines; /* 2Ah BYTE number of scan lines active mode (0,1,2,3) = (200,350,400,480) */
+ Bit8u pri_char_block; /* 2Bh BYTE primary character block */
+ Bit8u sec_char_block; /* 2Ch BYTE secondary character block */
+ Bit8u misc_flags; /* 2Dh BYTE miscellaneous flags
+ bit 0 all modes on all displays on
+ 1 grey summing on
+ 2 monochrome display attached
+ 3 default palette loading disabled
+ 4 cursor emulation enabled
+ 5 0 = intensity; 1 = blinking
+ 6 PS/2 P70 plasma display (without 9-dot wide font) active
+ 7 reserved
+ */
+ Bit8u reserved1[3]; /* 2Eh 3 BYTEs reserved (00h) */
+ Bit8u vid_mem; /* 31h BYTE video memory available 00h = 64K, 01h = 128K, 02h = 192K, 03h = 256K */
+ Bit8u savep_state_flag; /* 32h BYTE save pointer state flags
+ bit 0 512 character set active
+ 1 dynamic save area present
+ 2 alpha font override active
+ 3 graphics font override active
+ 4 palette override active
+ 5 DCC override active
+ 6 reserved
+ 7 reserved
+ */
+ Bit8u reserved2[13]; /* 33h 13 BYTEs reserved (00h) */
+} GCC_ATTRIBUTE(packed);
+#pragma pack()
+
+void INT10_GetFuncStateInformation(PhysPt save) {
+ /* set static state pointer */
+ mem_writed(save,int10.rom.static_state);
+ /* Copy BIOS Segment areas */
+ Bit16u i;
+
+ /* First area in Bios Seg */
+ for (i=0;i<0x1e;i++) {
+ mem_writeb(save+0x4+i,real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE+i));
+ }
+ /* Second area */
+ mem_writeb(save+0x22,real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1);
+ for (i=1;i<3;i++) {
+ mem_writeb(save+0x22+i,real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS+i));
+ }
+ /* Zero out rest of block */
+ for (i=0x25;i<0x40;i++) mem_writeb(save+i,0);
+ /* DCC */
+// mem_writeb(save+0x25,real_readb(BIOSMEM_SEG,BIOSMEM_DCC_INDEX));
+ Bit8u dccode = 0x00;
+ RealPt vsavept=real_readd(BIOSMEM_SEG,BIOSMEM_VS_POINTER);
+ RealPt svstable=real_readd(RealSeg(vsavept),RealOff(vsavept)+0x10);
+ if (svstable) {
+ RealPt dcctable=real_readd(RealSeg(svstable),RealOff(svstable)+0x02);
+ Bit8u entries=real_readb(RealSeg(dcctable),RealOff(dcctable)+0x00);
+ Bit8u idx=real_readb(BIOSMEM_SEG,BIOSMEM_DCC_INDEX);
+ // check if index within range
+ if (idx<entries) {
+ Bit16u dccentry=real_readw(RealSeg(dcctable),RealOff(dcctable)+0x04+idx*2);
+ if ((dccentry&0xff)==0) dccode=(Bit8u)((dccentry>>8)&0xff);
+ else dccode=(Bit8u)(dccentry&0xff);
+ }
+ }
+ mem_writeb(save+0x25,dccode);
+
+ Bit16u col_count=0;
+ switch (CurMode->type) {
+ case M_TEXT:
+ if (CurMode->mode==0x7) col_count=1; else col_count=16;break;
+ case M_CGA2:
+ col_count=2;break;
+ case M_CGA4:
+ col_count=4;break;
+ case M_EGA:
+ if (CurMode->mode==0x11 || CurMode->mode==0x0f)
+ col_count=2;
+ else
+ col_count=16;
+ break;
+ case M_VGA:
+ col_count=256;break;
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("Get Func State illegal mode type %d",CurMode->type);
+ }
+ /* Colour count */
+ mem_writew(save+0x27,col_count);
+ /* Page count */
+ mem_writeb(save+0x29,CurMode->ptotal);
+ /* scan lines */
+ switch (CurMode->sheight) {
+ case 200:
+ mem_writeb(save+0x2a,0);break;
+ case 350:
+ mem_writeb(save+0x2a,1);break;
+ case 400:
+ mem_writeb(save+0x2a,2);break;
+ case 480:
+ mem_writeb(save+0x2a,3);break;
+ };
+ /* misc flags */
+ if (CurMode->type==M_TEXT) mem_writeb(save+0x2d,0x21);
+ else mem_writeb(save+0x2d,0x01);
+ /* Video Memory available */
+ mem_writeb(save+0x31,3);
+}
+
+RealPt INT10_EGA_RIL_GetVersionPt(void) {
+ /* points to a graphics ROM location at the moment
+ as checks test for bx!=0 only */
+ return RealMake(0xc000,0x30);
+}
+
+static void EGA_RIL(Bit16u dx, Bit16u& port, Bit16u& regs) {
+ port = 0;
+ regs = 0; //if nul is returned it's a single register port
+ switch(dx) {
+ case 0x00: /* CRT Controller (25 reg) 3B4h mono modes, 3D4h color modes */
+ port = real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ regs = 25;
+ break;
+ case 0x08: /* Sequencer (5 registers) 3C4h */
+ port = 0x3C4;
+ regs = 5;
+ break;
+ case 0x10: /* Graphics Controller (9 registers) 3CEh */
+ port = 0x3CE;
+ regs = 9;
+ break;
+ case 0x18: /* Attribute Controller (20 registers) 3C0h */
+ port = 0x3c0;
+ regs = 20;
+ break;
+ case 0x20: /* Miscellaneous Output register 3C2h */
+ port = 0x3C2;
+ break;
+ case 0x28: /* Feature Control register (3BAh mono modes, 3DAh color modes) */
+ port = real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS) + 6;
+ break;
+ case 0x30: /* Graphics 1 Position register 3CCh */
+ port = 0x3CC;
+ break;
+ case 0x38: /* Graphics 2 Position register 3CAh */
+ port = 0x3CA;
+ break;
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("unknown RIL port selection %X",dx);
+ break;
+ }
+}
+
+void INT10_EGA_RIL_ReadRegister(Bit8u & bl, Bit16u dx) {
+ Bit16u port = 0;
+ Bit16u regs = 0;
+ EGA_RIL(dx,port,regs);
+ if(regs == 0) {
+ if(port) bl = IO_Read(port);
+ } else {
+ if(port == 0x3c0) IO_Read(real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS) + 6);
+ IO_Write(port,bl);
+ bl = IO_Read(port+1);
+ if(port == 0x3c0) IO_Read(real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS) + 6);
+ LOG(LOG_INT10,LOG_NORMAL)("EGA RIL read used with multi-reg");
+ }
+}
+
+void INT10_EGA_RIL_WriteRegister(Bit8u & bl, Bit8u bh, Bit16u dx) {
+ Bit16u port = 0;
+ Bit16u regs = 0;
+ EGA_RIL(dx,port,regs);
+ if(regs == 0) {
+ if(port) IO_Write(port,bl);
+ } else {
+ if(port == 0x3c0) {
+ IO_Read(real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS) + 6);
+ IO_Write(port,bl);
+ IO_Write(port,bh);
+ } else {
+ IO_Write(port,bl);
+ IO_Write(port+1,bh);
+ }
+ bl = bh;//Not sure
+ LOG(LOG_INT10,LOG_NORMAL)("EGA RIL write used with multi-reg");
+ }
+}
+
+void INT10_EGA_RIL_ReadRegisterRange(Bit8u ch, Bit8u cl, Bit16u dx, PhysPt dst) {
+ Bit16u port = 0;
+ Bit16u regs = 0;
+ EGA_RIL(dx,port,regs);
+ if(regs == 0) {
+ LOG(LOG_INT10,LOG_ERROR)("EGA RIL range read with port %x called",port);
+ } else {
+ if(ch<regs) {
+ if ((Bitu)ch+cl>regs) cl=(Bit8u)(regs-ch);
+ for (Bitu i=0; i<cl; i++) {
+ if(port == 0x3c0) IO_Read(real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS) + 6);
+ IO_Write(port,ch+i);
+ mem_writeb(dst++,IO_Read(port+1));
+ }
+ if(port == 0x3c0) IO_Read(real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS) + 6);
+ } else LOG(LOG_INT10,LOG_ERROR)("EGA RIL range read from %x for invalid register %x",port,ch);
+ }
+}
+
+void INT10_EGA_RIL_WriteRegisterRange(Bit8u ch, Bit8u cl, Bit16u dx, PhysPt src) {
+ Bit16u port = 0;
+ Bit16u regs = 0;
+ EGA_RIL(dx,port,regs);
+ if(regs == 0) {
+ LOG(LOG_INT10,LOG_ERROR)("EGA RIL range write called with port %x",port);
+ } else {
+ if(ch<regs) {
+ if ((Bitu)ch+cl>regs) cl=(Bit8u)(regs-ch);
+ if(port == 0x3c0) {
+ IO_Read(real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS) + 6);
+ for (Bitu i=0; i<cl; i++) {
+ IO_Write(port,ch+i);
+ IO_Write(port,mem_readb(src++));
+ }
+ } else {
+ for (Bitu i=0; i<cl; i++) {
+ IO_Write(port,ch+i);
+ IO_Write(port+1,mem_readb(src++));
+ }
+ }
+ } else LOG(LOG_INT10,LOG_ERROR)("EGA RIL range write to %x with invalid register %x",port,ch);
+ }
+}
+
+/* register sets are of the form
+ offset 0 (word): group index
+ offset 2 (byte): register number (0 for single registers, ignored)
+ offset 3 (byte): register value (return value when reading)
+*/
+void INT10_EGA_RIL_ReadRegisterSet(Bit16u cx, PhysPt tbl) {
+ /* read cx register sets */
+ for (Bit16u i=0; i<cx; i++) {
+ Bit8u vl=mem_readb(tbl+2);
+ INT10_EGA_RIL_ReadRegister(vl, mem_readw(tbl));
+ mem_writeb(tbl+3, vl);
+ tbl+=4;
+ }
+}
+
+void INT10_EGA_RIL_WriteRegisterSet(Bit16u cx, PhysPt tbl) {
+ /* write cx register sets */
+ Bit16u port = 0;
+ Bit16u regs = 0;
+ for (Bit16u i=0; i<cx; i++) {
+ EGA_RIL(mem_readw(tbl),port,regs);
+ Bit8u vl=mem_readb(tbl+3);
+ if(regs == 0) {
+ if(port) IO_Write(port,vl);
+ } else {
+ Bit8u idx=mem_readb(tbl+2);
+ if(port == 0x3c0) {
+ IO_Read(real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS) + 6);
+ IO_Write(port,idx);
+ IO_Write(port,vl);
+ } else {
+ IO_Write(port,idx);
+ IO_Write(port+1,vl);
+ }
+ }
+ tbl+=4;
+ }
+}
diff --git a/src/ints/int10_modes.cpp b/src/ints/int10_modes.cpp
new file mode 100644
index 000000000..a3ad0aec8
--- /dev/null
+++ b/src/ints/int10_modes.cpp
@@ -0,0 +1,1515 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+
+#include "dosbox.h"
+#include "mem.h"
+#include "inout.h"
+#include "int10.h"
+#include "vga.h"
+
+#define _EGA_HALF_CLOCK 0x0001
+#define _EGA_LINE_DOUBLE 0x0002
+#define _VGA_PIXEL_DOUBLE 0x0004
+
+#define SEQ_REGS 0x05
+#define GFX_REGS 0x09
+#define ATT_REGS 0x15
+
+VideoModeBlock ModeList_VGA[]={
+/* mode ,type ,sw ,sh ,tw ,th ,cw,ch ,pt,pstart ,plength,htot,vtot,hde,vde special flags */
+{ 0x000 ,M_TEXT ,360 ,400 ,40 ,25 ,9 ,16 ,8 ,0xB8000 ,0x0800 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK },
+{ 0x001 ,M_TEXT ,360 ,400 ,40 ,25 ,9 ,16 ,8 ,0xB8000 ,0x0800 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK },
+{ 0x002 ,M_TEXT ,720 ,400 ,80 ,25 ,9 ,16 ,8 ,0xB8000 ,0x1000 ,100 ,449 ,80 ,400 ,0 },
+{ 0x003 ,M_TEXT ,720 ,400 ,80 ,25 ,9 ,16 ,8 ,0xB8000 ,0x1000 ,100 ,449 ,80 ,400 ,0 },
+{ 0x004 ,M_CGA4 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE},
+{ 0x005 ,M_CGA4 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE},
+{ 0x006 ,M_CGA2 ,640 ,200 ,80 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,100 ,449 ,80 ,400 ,_EGA_LINE_DOUBLE},
+{ 0x007 ,M_TEXT ,720 ,400 ,80 ,25 ,9 ,16 ,8 ,0xB0000 ,0x1000 ,100 ,449 ,80 ,400 ,0 },
+
+{ 0x00D ,M_EGA ,320 ,200 ,40 ,25 ,8 ,8 ,8 ,0xA0000 ,0x2000 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE },
+{ 0x00E ,M_EGA ,640 ,200 ,80 ,25 ,8 ,8 ,4 ,0xA0000 ,0x4000 ,100 ,449 ,80 ,400 ,_EGA_LINE_DOUBLE },
+{ 0x00F ,M_EGA ,640 ,350 ,80 ,25 ,8 ,14 ,2 ,0xA0000 ,0x8000 ,100 ,449 ,80 ,350 ,0 },/*was EGA_2*/
+{ 0x010 ,M_EGA ,640 ,350 ,80 ,25 ,8 ,14 ,2 ,0xA0000 ,0x8000 ,100 ,449 ,80 ,350 ,0 },
+{ 0x011 ,M_EGA ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0xA000 ,100 ,525 ,80 ,480 ,0 },/*was EGA_2 */
+{ 0x012 ,M_EGA ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0xA000 ,100 ,525 ,80 ,480 ,0 },
+{ 0x013 ,M_VGA ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xA0000 ,0x2000 ,100 ,449 ,80 ,400 ,0 },
+
+{ 0x054 ,M_TEXT ,1056,344, 132,43, 8, 8, 1 ,0xB8000 ,0x4000, 160, 449, 132,344, 0 },
+{ 0x055 ,M_TEXT ,1056,400, 132,25, 8, 16, 1 ,0xB8000 ,0x2000, 160, 449, 132,400, 0 },
+
+/* Alias of mode 101 */
+{ 0x069 ,M_LIN8 ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0x10000,100 ,525 ,80 ,480 ,0 },
+/* Alias of mode 102 */
+{ 0x06A ,M_LIN4 ,800 ,600 ,100,37 ,8 ,16 ,1 ,0xA0000 ,0x10000,128 ,663 ,100,600 ,0 },
+
+/* Follow vesa 1.2 for first 0x20 */
+{ 0x100 ,M_LIN8 ,640 ,400 ,80 ,25 ,8 ,16 ,1 ,0xA0000 ,0x10000,100 ,449 ,80 ,400 ,0 },
+{ 0x101 ,M_LIN8 ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0x10000,100 ,525 ,80 ,480 ,0 },
+{ 0x102 ,M_LIN4 ,800 ,600 ,100,37 ,8 ,16 ,1 ,0xA0000 ,0x10000,132 ,628 ,100,600 ,0 },
+{ 0x103 ,M_LIN8 ,800 ,600 ,100,37 ,8 ,16 ,1 ,0xA0000 ,0x10000,132 ,628 ,100,600 ,0 },
+{ 0x104 ,M_LIN4 ,1024,768 ,128,48 ,8 ,16 ,1 ,0xA0000 ,0x10000,168 ,806 ,128,768 ,0 },
+{ 0x105 ,M_LIN8 ,1024,768 ,128,48 ,8 ,16 ,1 ,0xA0000 ,0x10000,168 ,806 ,128,768 ,0 },
+{ 0x106 ,M_LIN4 ,1280,1024,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,212 ,1066,160,1024,0 },
+{ 0x107 ,M_LIN8 ,1280,1024,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,212 ,1066,160,1024,0 },
+
+/* VESA text modes */
+{ 0x108 ,M_TEXT ,640 ,480, 80,60, 8, 8 ,2 ,0xB8000 ,0x4000, 100 ,525 ,80 ,480 ,0 },
+{ 0x109 ,M_TEXT ,1056,400, 132,25, 8, 16, 1 ,0xB8000 ,0x2000, 160, 449, 132,400, 0 },
+{ 0x10A ,M_TEXT ,1056,688, 132,43, 8, 8, 1 ,0xB8000 ,0x4000, 160, 449, 132,344, 0 },
+{ 0x10B ,M_TEXT ,1056,400, 132,50, 8, 8, 1 ,0xB8000 ,0x4000, 160, 449, 132,400, 0 },
+{ 0x10C ,M_TEXT ,1056,480, 132,60, 8, 8, 2 ,0xB8000 ,0x4000, 160, 531, 132,480, 0 },
+
+/* VESA higher color modes */
+{ 0x10D ,M_LIN15 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,449 ,80 ,400 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
+{ 0x10E ,M_LIN16 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,449 ,80 ,400 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
+{ 0x10F ,M_LIN32 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xA0000 ,0x10000,50 ,449 ,40 ,400 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
+{ 0x110 ,M_LIN15 ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0x10000,200 ,525 ,160,480 ,0 },
+{ 0x111 ,M_LIN16 ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0x10000,200 ,525 ,160,480 ,0 },
+{ 0x112 ,M_LIN32 ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0x10000,100 ,525 ,80 ,480 ,0 },
+{ 0x113 ,M_LIN15 ,800 ,600 ,100,37 ,8 ,16 ,1 ,0xA0000 ,0x10000,264 ,628 ,200,600 ,0 },
+{ 0x114 ,M_LIN16 ,800 ,600 ,100,37 ,8 ,16 ,1 ,0xA0000 ,0x10000,264 ,628 ,200,600 ,0 },
+{ 0x115 ,M_LIN32 ,800 ,600 ,100,37 ,8 ,16 ,1 ,0xA0000 ,0x10000,132 ,628 ,100,600 ,0 },
+{ 0x116 ,M_LIN15 ,1024,768 ,128,48 ,8 ,16 ,1 ,0xA0000 ,0x10000,336 ,806 ,256,768 ,0 },
+{ 0x117 ,M_LIN16 ,1024,768 ,128,48 ,8 ,16 ,1 ,0xA0000 ,0x10000,336 ,806 ,256,768 ,0 },
+{ 0x118 ,M_LIN32 ,1024,768 ,128,48 ,8 ,16 ,1 ,0xA0000 ,0x10000,168 ,806 ,128,768 ,0 },
+
+/* those should be interlaced but ok */
+//{ 0x119 ,M_LIN15 ,1280,1024,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,424 ,1066,320,1024,0 },
+//{ 0x11A ,M_LIN16 ,1280,1024,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,424 ,1066,320,1024,0 },
+
+{ 0x150 ,M_LIN8 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,449 ,80 ,400 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
+{ 0x151 ,M_LIN8 ,320 ,240 ,40 ,30 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,525 ,80 ,480 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
+{ 0x152 ,M_LIN8 ,320 ,400 ,40 ,50 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,449 ,80 ,400 , _VGA_PIXEL_DOUBLE },
+{ 0x153 ,M_LIN8 ,320 ,480 ,40 ,60 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,525 ,80 ,480 , _VGA_PIXEL_DOUBLE },
+
+{ 0x160 ,M_LIN15 ,320 ,240 ,40 ,30 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,525 , 80 ,480 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
+{ 0x161 ,M_LIN15 ,320 ,400 ,40 ,50 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,449 , 80 ,400 , _VGA_PIXEL_DOUBLE },
+{ 0x162 ,M_LIN15 ,320 ,480 ,40 ,60 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,525 , 80 ,480 , _VGA_PIXEL_DOUBLE },
+{ 0x165 ,M_LIN15 ,640 ,400 ,80 ,25 ,8 ,16 ,1 ,0xA0000 ,0x10000,200 ,449 ,160 ,400 ,0 },
+
+{ 0x170 ,M_LIN16 ,320 ,240 ,40 ,30 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,525 , 80 ,480 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
+{ 0x171 ,M_LIN16 ,320 ,400 ,40 ,50 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,449 , 80 ,400 , _VGA_PIXEL_DOUBLE },
+{ 0x172 ,M_LIN16 ,320 ,480 ,40 ,60 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,525 , 80 ,480 , _VGA_PIXEL_DOUBLE },
+{ 0x175 ,M_LIN16 ,640 ,400 ,80 ,25 ,8 ,16 ,1 ,0xA0000 ,0x10000,200 ,449 ,160 ,400 ,0 },
+
+{ 0x190 ,M_LIN32 ,320 ,240 ,40 ,30 ,8 ,8 ,1 ,0xA0000 ,0x10000, 50 ,525 ,40 ,480 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
+{ 0x191 ,M_LIN32 ,320 ,400 ,40 ,50 ,8 ,8 ,1 ,0xA0000 ,0x10000, 50 ,449 ,40 ,400 , _VGA_PIXEL_DOUBLE },
+{ 0x192 ,M_LIN32 ,320 ,480 ,40 ,60 ,8 ,8 ,1 ,0xA0000 ,0x10000, 50 ,525 ,40 ,480 , _VGA_PIXEL_DOUBLE },
+
+/* S3 specific modes */
+{ 0x207 ,M_LIN8 ,1152,864,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,182 ,948 ,144,864 ,0 },
+{ 0x209 ,M_LIN15 ,1152,864,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,364 ,948 ,288,864 ,0 },
+{ 0x20A ,M_LIN16 ,1152,864,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,364 ,948 ,288,864 ,0 },
+//{ 0x20B ,M_LIN32 ,1152,864,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,182 ,948 ,144,864 ,0 },
+{ 0x213 ,M_LIN32 ,640 ,400,80 ,25 ,8 ,16 ,1 ,0xA0000 ,0x10000,100 ,449 ,80 ,400 ,0 },
+
+/* Some custom modes */
+//{ 0x220 ,M_LIN32 ,1280,1024,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,212 ,1066,160,1024,0 },
+// A nice 16:9 mode
+{ 0x222 ,M_LIN8 ,848 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0x10000,132 ,525 ,106 ,480 ,0 },
+{ 0x223 ,M_LIN15 ,848 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0x10000,264 ,525 ,212 ,480 ,0 },
+{ 0x224 ,M_LIN16 ,848 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0x10000,264 ,525 ,212 ,480 ,0 },
+{ 0x225 ,M_LIN32 ,848 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0x10000,132 ,525 ,106 ,480 ,0 },
+
+{0xFFFF ,M_ERROR ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0x00000 ,0x0000 ,0 ,0 ,0 ,0 ,0 },
+};
+
+VideoModeBlock ModeList_VGA_Text_200lines[]={
+/* mode ,type ,sw ,sh ,tw ,th ,cw,ch ,pt,pstart ,plength,htot,vtot,hde,vde special flags */
+{ 0x000 ,M_TEXT ,320 ,200 ,40 ,25 ,8 , 8 ,8 ,0xB8000 ,0x0800 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE},
+{ 0x001 ,M_TEXT ,320 ,200 ,40 ,25 ,8 , 8 ,8 ,0xB8000 ,0x0800 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE},
+{ 0x002 ,M_TEXT ,640 ,200 ,80 ,25 ,8 , 8 ,8 ,0xB8000 ,0x1000 ,100 ,449 ,80 ,400 ,_EGA_LINE_DOUBLE },
+{ 0x003 ,M_TEXT ,640 ,200 ,80 ,25 ,8 , 8 ,8 ,0xB8000 ,0x1000 ,100 ,449 ,80 ,400 ,_EGA_LINE_DOUBLE }
+};
+
+VideoModeBlock ModeList_VGA_Text_350lines[]={
+/* mode ,type ,sw ,sh ,tw ,th ,cw,ch ,pt,pstart ,plength,htot,vtot,hde,vde special flags */
+{ 0x000 ,M_TEXT ,320 ,350 ,40 ,25 ,8 ,14 ,8 ,0xB8000 ,0x0800 ,50 ,449 ,40 ,350 ,_EGA_HALF_CLOCK },
+{ 0x001 ,M_TEXT ,320 ,350 ,40 ,25 ,8 ,14 ,8 ,0xB8000 ,0x0800 ,50 ,449 ,40 ,350 ,_EGA_HALF_CLOCK },
+{ 0x002 ,M_TEXT ,640 ,350 ,80 ,25 ,8 ,14 ,8 ,0xB8000 ,0x1000 ,100 ,449 ,80 ,350 ,0 },
+{ 0x003 ,M_TEXT ,640 ,350 ,80 ,25 ,8 ,14 ,8 ,0xB8000 ,0x1000 ,100 ,449 ,80 ,350 ,0 },
+{ 0x007 ,M_TEXT ,720 ,350 ,80 ,25 ,9 ,14 ,8 ,0xB0000 ,0x1000 ,100 ,449 ,80 ,350 ,0 }
+};
+
+VideoModeBlock ModeList_VGA_Tseng[]={
+/* mode ,type ,sw ,sh ,tw ,th ,cw,ch ,pt,pstart ,plength,htot,vtot,hde,vde special flags */
+{ 0x000 ,M_TEXT ,360 ,400 ,40 ,25 ,9 ,16 ,8 ,0xB8000 ,0x0800 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK },
+{ 0x001 ,M_TEXT ,360 ,400 ,40 ,25 ,9 ,16 ,8 ,0xB8000 ,0x0800 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK },
+{ 0x002 ,M_TEXT ,720 ,400 ,80 ,25 ,9 ,16 ,8 ,0xB8000 ,0x1000 ,100 ,449 ,80 ,400 ,0 },
+{ 0x003 ,M_TEXT ,720 ,400 ,80 ,25 ,9 ,16 ,8 ,0xB8000 ,0x1000 ,100 ,449 ,80 ,400 ,0 },
+{ 0x004 ,M_CGA4 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE},
+{ 0x005 ,M_CGA4 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE},
+{ 0x006 ,M_CGA2 ,640 ,200 ,80 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,100 ,449 ,80 ,400 ,_EGA_LINE_DOUBLE},
+{ 0x007 ,M_TEXT ,720 ,400 ,80 ,25 ,9 ,16 ,8 ,0xB0000 ,0x1000 ,100 ,449 ,80 ,400 ,0 },
+
+{ 0x00D ,M_EGA ,320 ,200 ,40 ,25 ,8 ,8 ,8 ,0xA0000 ,0x2000 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE },
+{ 0x00E ,M_EGA ,640 ,200 ,80 ,25 ,8 ,8 ,4 ,0xA0000 ,0x4000 ,100 ,449 ,80 ,400 ,_EGA_LINE_DOUBLE },
+{ 0x00F ,M_EGA ,640 ,350 ,80 ,25 ,8 ,14 ,2 ,0xA0000 ,0x8000 ,100 ,449 ,80 ,350 ,0 },/*was EGA_2*/
+{ 0x010 ,M_EGA ,640 ,350 ,80 ,25 ,8 ,14 ,2 ,0xA0000 ,0x8000 ,100 ,449 ,80 ,350 ,0 },
+{ 0x011 ,M_EGA ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0xA000 ,100 ,525 ,80 ,480 ,0 },/*was EGA_2 */
+{ 0x012 ,M_EGA ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0xA000 ,100 ,525 ,80 ,480 ,0 },
+{ 0x013 ,M_VGA ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xA0000 ,0x2000 ,100 ,449 ,80 ,400 ,0 },
+
+{ 0x018 ,M_TEXT ,1056 ,688, 132,44, 8, 8, 1 ,0xB0000 ,0x4000, 192, 800, 132, 704, 0 },
+{ 0x019 ,M_TEXT ,1056 ,400, 132,25, 8, 16,1 ,0xB0000 ,0x2000, 192, 449, 132, 400, 0 },
+{ 0x01A ,M_TEXT ,1056 ,400, 132,28, 8, 16,1 ,0xB0000 ,0x2000, 192, 449, 132, 448, 0 },
+{ 0x022 ,M_TEXT ,1056 ,688, 132,44, 8, 8, 1 ,0xB8000 ,0x4000, 192, 800, 132, 704, 0 },
+{ 0x023 ,M_TEXT ,1056 ,400, 132,25, 8, 16,1 ,0xB8000 ,0x2000, 192, 449, 132, 400, 0 },
+{ 0x024 ,M_TEXT ,1056 ,400, 132,28, 8, 16,1 ,0xB8000 ,0x2000, 192, 449, 132, 448, 0 },
+{ 0x025 ,M_LIN4 ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0xA000 ,100 ,525 ,80 ,480 , 0 },
+{ 0x029 ,M_LIN4 ,800 ,600 ,100,37 ,8 ,16 ,1 ,0xA0000 ,0xA000, 128 ,663 ,100,600 , 0 },
+{ 0x02D ,M_LIN8 ,640 ,350 ,80 ,21 ,8 ,16 ,1 ,0xA0000 ,0x10000,100 ,449 ,80 ,350 , 0 },
+{ 0x02E ,M_LIN8 ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0x10000,100 ,525 ,80 ,480 , 0 },
+{ 0x02F ,M_LIN8 ,640 ,400 ,80 ,25 ,8 ,16 ,1 ,0xA0000 ,0x10000,100 ,449 ,80 ,400 , 0 },/* ET4000 only */
+{ 0x030 ,M_LIN8 ,800 ,600 ,100,37 ,8 ,16 ,1 ,0xA0000 ,0x10000,128 ,663 ,100,600 , 0 },
+{ 0x036 ,M_LIN4 ,960 , 720,120,45 ,8 ,16 ,1 ,0xA0000 ,0xA000, 120 ,800 ,120,720 , 0 },/* STB only */
+{ 0x037 ,M_LIN4 ,1024, 768,128,48 ,8 ,16 ,1 ,0xA0000 ,0xA000, 128 ,800 ,128,768 , 0 },
+{ 0x038 ,M_LIN8 ,1024 ,768,128,48 ,8 ,16 ,1 ,0xA0000 ,0x10000,128 ,800 ,128,768 , 0 },/* ET4000 only */
+{ 0x03D ,M_LIN4 ,1280,1024,160,64 ,8 ,16 ,1 ,0xA0000 ,0xA000, 160 ,1152,160,1024, 0 },/* newer ET4000 */
+{ 0x03E ,M_LIN4 ,1280, 960,160,60 ,8 ,16 ,1 ,0xA0000 ,0xA000, 160 ,1024,160,960 , 0 },/* Definicon only */
+{ 0x06A ,M_LIN4 ,800 ,600 ,100,37 ,8 ,16 ,1 ,0xA0000 ,0xA000, 128 ,663 ,100,600 , 0 },/* newer ET4000 */
+
+{0xFFFF ,M_ERROR ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0x00000 ,0x0000 ,0 ,0 ,0 ,0 ,0 },
+};
+
+VideoModeBlock ModeList_VGA_Paradise[]={
+/* mode ,type ,sw ,sh ,tw ,th ,cw,ch ,pt,pstart ,plength,htot,vtot,hde,vde special flags */
+{ 0x000 ,M_TEXT ,360 ,400 ,40 ,25 ,9 ,16 ,8 ,0xB8000 ,0x0800 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK },
+{ 0x001 ,M_TEXT ,360 ,400 ,40 ,25 ,9 ,16 ,8 ,0xB8000 ,0x0800 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK },
+{ 0x002 ,M_TEXT ,720 ,400 ,80 ,25 ,9 ,16 ,8 ,0xB8000 ,0x1000 ,100 ,449 ,80 ,400 ,0 },
+{ 0x003 ,M_TEXT ,720 ,400 ,80 ,25 ,9 ,16 ,8 ,0xB8000 ,0x1000 ,100 ,449 ,80 ,400 ,0 },
+{ 0x004 ,M_CGA4 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE},
+{ 0x005 ,M_CGA4 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE},
+{ 0x006 ,M_CGA2 ,640 ,200 ,80 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,100 ,449 ,80 ,400 ,_EGA_LINE_DOUBLE},
+{ 0x007 ,M_TEXT ,720 ,400 ,80 ,25 ,9 ,16 ,8 ,0xB0000 ,0x1000 ,100 ,449 ,80 ,400 ,0 },
+
+{ 0x00D ,M_EGA ,320 ,200 ,40 ,25 ,8 ,8 ,8 ,0xA0000 ,0x2000 ,50 ,449 ,40 ,400 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE },
+{ 0x00E ,M_EGA ,640 ,200 ,80 ,25 ,8 ,8 ,4 ,0xA0000 ,0x4000 ,100 ,449 ,80 ,400 ,_EGA_LINE_DOUBLE },
+{ 0x00F ,M_EGA ,640 ,350 ,80 ,25 ,8 ,14 ,2 ,0xA0000 ,0x8000 ,100 ,449 ,80 ,350 ,0 },/*was EGA_2*/
+{ 0x010 ,M_EGA ,640 ,350 ,80 ,25 ,8 ,14 ,2 ,0xA0000 ,0x8000 ,100 ,449 ,80 ,350 ,0 },
+{ 0x011 ,M_EGA ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0xA000 ,100 ,525 ,80 ,480 ,0 },/*was EGA_2 */
+{ 0x012 ,M_EGA ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0xA000 ,100 ,525 ,80 ,480 ,0 },
+{ 0x013 ,M_VGA ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xA0000 ,0x2000 ,100 ,449 ,80 ,400 ,0 },
+
+{ 0x054 ,M_TEXT ,1056 ,688, 132,43, 8, 9, 1, 0xB0000, 0x4000, 192, 720, 132,688, 0 },
+{ 0x055 ,M_TEXT ,1056 ,400, 132,25, 8, 16,1, 0xB0000, 0x2000, 192, 449, 132,400, 0 },
+{ 0x056 ,M_TEXT ,1056 ,688, 132,43, 8, 9, 1, 0xB0000, 0x4000, 192, 720, 132,688, 0 },
+{ 0x057 ,M_TEXT ,1056 ,400, 132,25, 8, 16,1, 0xB0000, 0x2000, 192, 449, 132,400, 0 },
+{ 0x058 ,M_LIN4 ,800 , 600, 100,37, 8, 16,1, 0xA0000, 0xA000, 128 ,663 ,100,600, 0 },
+{ 0x05D ,M_LIN4 ,1024, 768, 128,48 ,8, 16,1, 0xA0000, 0x10000,128 ,800 ,128,768 ,0 }, // documented only on C00 upwards
+{ 0x05E ,M_LIN8 ,640 , 400, 80 ,25, 8, 16,1, 0xA0000, 0x10000,100 ,449 ,80 ,400, 0 },
+{ 0x05F ,M_LIN8 ,640 , 480, 80 ,30, 8, 16,1, 0xA0000, 0x10000,100 ,525 ,80 ,480, 0 },
+
+{0xFFFF ,M_ERROR ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0x00000 ,0x0000 ,0 ,0 ,0 ,0 ,0 },
+};
+
+
+VideoModeBlock ModeList_EGA[]={
+/* mode ,type ,sw ,sh ,tw ,th ,cw,ch ,pt,pstart ,plength,htot,vtot,hde,vde special flags */
+{ 0x000 ,M_TEXT ,320 ,350 ,40 ,25 ,8 ,14 ,8 ,0xB8000 ,0x0800 ,50 ,366 ,40 ,350 ,_EGA_HALF_CLOCK },
+{ 0x001 ,M_TEXT ,320 ,350 ,40 ,25 ,8 ,14 ,8 ,0xB8000 ,0x0800 ,50 ,366 ,40 ,350 ,_EGA_HALF_CLOCK },
+{ 0x002 ,M_TEXT ,640 ,350 ,80 ,25 ,8 ,14 ,8 ,0xB8000 ,0x1000 ,96 ,366 ,80 ,350 ,0 },
+{ 0x003 ,M_TEXT ,640 ,350 ,80 ,25 ,8 ,14 ,8 ,0xB8000 ,0x1000 ,96 ,366 ,80 ,350 ,0 },
+{ 0x004 ,M_CGA4 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,60 ,262 ,40 ,200 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE},
+{ 0x005 ,M_CGA4 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,60 ,262 ,40 ,200 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE},
+{ 0x006 ,M_CGA2 ,640 ,200 ,80 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,117 ,262 ,80 ,200 ,_EGA_LINE_DOUBLE},
+{ 0x007 ,M_TEXT ,720 ,350 ,80 ,25 ,9 ,14 ,8 ,0xB0000 ,0x1000 ,101 ,370 ,80 ,350 ,0 },
+
+{ 0x00D ,M_EGA ,320 ,200 ,40 ,25 ,8 ,8 ,8 ,0xA0000 ,0x2000 ,60 ,262 ,40 ,200 ,_EGA_HALF_CLOCK | _EGA_LINE_DOUBLE },
+{ 0x00E ,M_EGA ,640 ,200 ,80 ,25 ,8 ,8 ,4 ,0xA0000 ,0x4000 ,117 ,262 ,80 ,200 ,_EGA_LINE_DOUBLE },
+{ 0x00F ,M_EGA ,640 ,350 ,80 ,25 ,8 ,14 ,2 ,0xA0000 ,0x8000 ,101 ,370 ,80 ,350 ,0 },/*was EGA_2*/
+{ 0x010 ,M_EGA ,640 ,350 ,80 ,25 ,8 ,14 ,2 ,0xA0000 ,0x8000 ,96 ,366 ,80 ,350 ,0 },
+
+{0xFFFF ,M_ERROR ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0x00000 ,0x0000 ,0 ,0 ,0 ,0 ,0 },
+};
+
+VideoModeBlock ModeList_OTHER[]={
+/* mode ,type ,sw ,sh ,tw ,th ,cw,ch ,pt,pstart ,plength,htot,vtot,hde,vde ,special flags */
+{ 0x000 ,M_TEXT ,320 ,400 ,40 ,25 ,8 ,8 ,8 ,0xB8000 ,0x0800 ,56 ,31 ,40 ,25 ,0 },
+{ 0x001 ,M_TEXT ,320 ,400 ,40 ,25 ,8 ,8 ,8 ,0xB8000 ,0x0800 ,56 ,31 ,40 ,25 ,0 },
+{ 0x002 ,M_TEXT ,640 ,400 ,80 ,25 ,8 ,8 ,4 ,0xB8000 ,0x1000 ,113 ,31 ,80 ,25 ,0 },
+{ 0x003 ,M_TEXT ,640 ,400 ,80 ,25 ,8 ,8 ,4 ,0xB8000 ,0x1000 ,113 ,31 ,80 ,25 ,0 },
+{ 0x004 ,M_CGA4 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,56 ,127 ,40 ,100 ,0 },
+{ 0x005 ,M_CGA4 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,56 ,127 ,40 ,100 ,0 },
+{ 0x006 ,M_CGA2 ,640 ,200 ,80 ,25 ,8 ,8 ,1 ,0xB8000 ,0x4000 ,56 ,127 ,40 ,100 ,0 },
+{ 0x008 ,M_TANDY16,160 ,200 ,20 ,25 ,8 ,8 ,8 ,0xB8000 ,0x2000 ,56 ,127 ,40 ,100 ,0 },
+{ 0x009 ,M_TANDY16,320 ,200 ,40 ,25 ,8 ,8 ,8 ,0xB8000 ,0x2000 ,113 ,63 ,80 ,50 ,0 },
+{ 0x00A ,M_CGA4 ,640 ,200 ,80 ,25 ,8 ,8 ,8 ,0xB8000 ,0x2000 ,113 ,63 ,80 ,50 ,0 },
+//{ 0x00E ,M_TANDY16,640 ,200 ,80 ,25 ,8 ,8 ,8 ,0xA0000 ,0x10000 ,113 ,256 ,80 ,200 ,0 },
+{0xFFFF ,M_ERROR ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0x00000 ,0x0000 ,0 ,0 ,0 ,0 ,0 },
+};
+
+VideoModeBlock Hercules_Mode=
+{ 0x007 ,M_TEXT ,640 ,350 ,80 ,25 ,8 ,14 ,1 ,0xB0000 ,0x1000 ,97 ,25 ,80 ,25 ,0 };
+
+static Bit8u text_palette[64][3]=
+{
+ {0x00,0x00,0x00},{0x00,0x00,0x2a},{0x00,0x2a,0x00},{0x00,0x2a,0x2a},{0x2a,0x00,0x00},{0x2a,0x00,0x2a},{0x2a,0x2a,0x00},{0x2a,0x2a,0x2a},
+ {0x00,0x00,0x15},{0x00,0x00,0x3f},{0x00,0x2a,0x15},{0x00,0x2a,0x3f},{0x2a,0x00,0x15},{0x2a,0x00,0x3f},{0x2a,0x2a,0x15},{0x2a,0x2a,0x3f},
+ {0x00,0x15,0x00},{0x00,0x15,0x2a},{0x00,0x3f,0x00},{0x00,0x3f,0x2a},{0x2a,0x15,0x00},{0x2a,0x15,0x2a},{0x2a,0x3f,0x00},{0x2a,0x3f,0x2a},
+ {0x00,0x15,0x15},{0x00,0x15,0x3f},{0x00,0x3f,0x15},{0x00,0x3f,0x3f},{0x2a,0x15,0x15},{0x2a,0x15,0x3f},{0x2a,0x3f,0x15},{0x2a,0x3f,0x3f},
+ {0x15,0x00,0x00},{0x15,0x00,0x2a},{0x15,0x2a,0x00},{0x15,0x2a,0x2a},{0x3f,0x00,0x00},{0x3f,0x00,0x2a},{0x3f,0x2a,0x00},{0x3f,0x2a,0x2a},
+ {0x15,0x00,0x15},{0x15,0x00,0x3f},{0x15,0x2a,0x15},{0x15,0x2a,0x3f},{0x3f,0x00,0x15},{0x3f,0x00,0x3f},{0x3f,0x2a,0x15},{0x3f,0x2a,0x3f},
+ {0x15,0x15,0x00},{0x15,0x15,0x2a},{0x15,0x3f,0x00},{0x15,0x3f,0x2a},{0x3f,0x15,0x00},{0x3f,0x15,0x2a},{0x3f,0x3f,0x00},{0x3f,0x3f,0x2a},
+ {0x15,0x15,0x15},{0x15,0x15,0x3f},{0x15,0x3f,0x15},{0x15,0x3f,0x3f},{0x3f,0x15,0x15},{0x3f,0x15,0x3f},{0x3f,0x3f,0x15},{0x3f,0x3f,0x3f}
+};
+
+static Bit8u mtext_palette[64][3]=
+{
+ {0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
+ {0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},
+ {0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
+ {0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},
+ {0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
+ {0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},
+ {0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
+ {0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f}
+};
+
+static Bit8u mtext_s3_palette[64][3]=
+{
+ {0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
+ {0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},
+ {0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},
+ {0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},
+ {0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
+ {0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},
+ {0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},{0x2a,0x2a,0x2a},
+ {0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f},{0x3f,0x3f,0x3f}
+};
+
+static Bit8u ega_palette[64][3]=
+{
+ {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
+ {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
+ {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
+ {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
+ {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
+ {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
+ {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
+ {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f}
+};
+
+static Bit8u cga_palette[16][3]=
+{
+ {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
+ {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
+};
+
+static Bit8u cga_palette_2[64][3]=
+{
+ {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
+ {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
+ {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
+ {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
+ {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
+ {0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
+ {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
+ {0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
+};
+
+static Bit8u vga_palette[248][3]=
+{
+ {0x00,0x00,0x00},{0x00,0x00,0x2a},{0x00,0x2a,0x00},{0x00,0x2a,0x2a},{0x2a,0x00,0x00},{0x2a,0x00,0x2a},{0x2a,0x15,0x00},{0x2a,0x2a,0x2a},
+ {0x15,0x15,0x15},{0x15,0x15,0x3f},{0x15,0x3f,0x15},{0x15,0x3f,0x3f},{0x3f,0x15,0x15},{0x3f,0x15,0x3f},{0x3f,0x3f,0x15},{0x3f,0x3f,0x3f},
+ {0x00,0x00,0x00},{0x05,0x05,0x05},{0x08,0x08,0x08},{0x0b,0x0b,0x0b},{0x0e,0x0e,0x0e},{0x11,0x11,0x11},{0x14,0x14,0x14},{0x18,0x18,0x18},
+ {0x1c,0x1c,0x1c},{0x20,0x20,0x20},{0x24,0x24,0x24},{0x28,0x28,0x28},{0x2d,0x2d,0x2d},{0x32,0x32,0x32},{0x38,0x38,0x38},{0x3f,0x3f,0x3f},
+ {0x00,0x00,0x3f},{0x10,0x00,0x3f},{0x1f,0x00,0x3f},{0x2f,0x00,0x3f},{0x3f,0x00,0x3f},{0x3f,0x00,0x2f},{0x3f,0x00,0x1f},{0x3f,0x00,0x10},
+ {0x3f,0x00,0x00},{0x3f,0x10,0x00},{0x3f,0x1f,0x00},{0x3f,0x2f,0x00},{0x3f,0x3f,0x00},{0x2f,0x3f,0x00},{0x1f,0x3f,0x00},{0x10,0x3f,0x00},
+ {0x00,0x3f,0x00},{0x00,0x3f,0x10},{0x00,0x3f,0x1f},{0x00,0x3f,0x2f},{0x00,0x3f,0x3f},{0x00,0x2f,0x3f},{0x00,0x1f,0x3f},{0x00,0x10,0x3f},
+ {0x1f,0x1f,0x3f},{0x27,0x1f,0x3f},{0x2f,0x1f,0x3f},{0x37,0x1f,0x3f},{0x3f,0x1f,0x3f},{0x3f,0x1f,0x37},{0x3f,0x1f,0x2f},{0x3f,0x1f,0x27},
+
+ {0x3f,0x1f,0x1f},{0x3f,0x27,0x1f},{0x3f,0x2f,0x1f},{0x3f,0x37,0x1f},{0x3f,0x3f,0x1f},{0x37,0x3f,0x1f},{0x2f,0x3f,0x1f},{0x27,0x3f,0x1f},
+ {0x1f,0x3f,0x1f},{0x1f,0x3f,0x27},{0x1f,0x3f,0x2f},{0x1f,0x3f,0x37},{0x1f,0x3f,0x3f},{0x1f,0x37,0x3f},{0x1f,0x2f,0x3f},{0x1f,0x27,0x3f},
+ {0x2d,0x2d,0x3f},{0x31,0x2d,0x3f},{0x36,0x2d,0x3f},{0x3a,0x2d,0x3f},{0x3f,0x2d,0x3f},{0x3f,0x2d,0x3a},{0x3f,0x2d,0x36},{0x3f,0x2d,0x31},
+ {0x3f,0x2d,0x2d},{0x3f,0x31,0x2d},{0x3f,0x36,0x2d},{0x3f,0x3a,0x2d},{0x3f,0x3f,0x2d},{0x3a,0x3f,0x2d},{0x36,0x3f,0x2d},{0x31,0x3f,0x2d},
+ {0x2d,0x3f,0x2d},{0x2d,0x3f,0x31},{0x2d,0x3f,0x36},{0x2d,0x3f,0x3a},{0x2d,0x3f,0x3f},{0x2d,0x3a,0x3f},{0x2d,0x36,0x3f},{0x2d,0x31,0x3f},
+ {0x00,0x00,0x1c},{0x07,0x00,0x1c},{0x0e,0x00,0x1c},{0x15,0x00,0x1c},{0x1c,0x00,0x1c},{0x1c,0x00,0x15},{0x1c,0x00,0x0e},{0x1c,0x00,0x07},
+ {0x1c,0x00,0x00},{0x1c,0x07,0x00},{0x1c,0x0e,0x00},{0x1c,0x15,0x00},{0x1c,0x1c,0x00},{0x15,0x1c,0x00},{0x0e,0x1c,0x00},{0x07,0x1c,0x00},
+ {0x00,0x1c,0x00},{0x00,0x1c,0x07},{0x00,0x1c,0x0e},{0x00,0x1c,0x15},{0x00,0x1c,0x1c},{0x00,0x15,0x1c},{0x00,0x0e,0x1c},{0x00,0x07,0x1c},
+
+ {0x0e,0x0e,0x1c},{0x11,0x0e,0x1c},{0x15,0x0e,0x1c},{0x18,0x0e,0x1c},{0x1c,0x0e,0x1c},{0x1c,0x0e,0x18},{0x1c,0x0e,0x15},{0x1c,0x0e,0x11},
+ {0x1c,0x0e,0x0e},{0x1c,0x11,0x0e},{0x1c,0x15,0x0e},{0x1c,0x18,0x0e},{0x1c,0x1c,0x0e},{0x18,0x1c,0x0e},{0x15,0x1c,0x0e},{0x11,0x1c,0x0e},
+ {0x0e,0x1c,0x0e},{0x0e,0x1c,0x11},{0x0e,0x1c,0x15},{0x0e,0x1c,0x18},{0x0e,0x1c,0x1c},{0x0e,0x18,0x1c},{0x0e,0x15,0x1c},{0x0e,0x11,0x1c},
+ {0x14,0x14,0x1c},{0x16,0x14,0x1c},{0x18,0x14,0x1c},{0x1a,0x14,0x1c},{0x1c,0x14,0x1c},{0x1c,0x14,0x1a},{0x1c,0x14,0x18},{0x1c,0x14,0x16},
+ {0x1c,0x14,0x14},{0x1c,0x16,0x14},{0x1c,0x18,0x14},{0x1c,0x1a,0x14},{0x1c,0x1c,0x14},{0x1a,0x1c,0x14},{0x18,0x1c,0x14},{0x16,0x1c,0x14},
+ {0x14,0x1c,0x14},{0x14,0x1c,0x16},{0x14,0x1c,0x18},{0x14,0x1c,0x1a},{0x14,0x1c,0x1c},{0x14,0x1a,0x1c},{0x14,0x18,0x1c},{0x14,0x16,0x1c},
+ {0x00,0x00,0x10},{0x04,0x00,0x10},{0x08,0x00,0x10},{0x0c,0x00,0x10},{0x10,0x00,0x10},{0x10,0x00,0x0c},{0x10,0x00,0x08},{0x10,0x00,0x04},
+ {0x10,0x00,0x00},{0x10,0x04,0x00},{0x10,0x08,0x00},{0x10,0x0c,0x00},{0x10,0x10,0x00},{0x0c,0x10,0x00},{0x08,0x10,0x00},{0x04,0x10,0x00},
+
+ {0x00,0x10,0x00},{0x00,0x10,0x04},{0x00,0x10,0x08},{0x00,0x10,0x0c},{0x00,0x10,0x10},{0x00,0x0c,0x10},{0x00,0x08,0x10},{0x00,0x04,0x10},
+ {0x08,0x08,0x10},{0x0a,0x08,0x10},{0x0c,0x08,0x10},{0x0e,0x08,0x10},{0x10,0x08,0x10},{0x10,0x08,0x0e},{0x10,0x08,0x0c},{0x10,0x08,0x0a},
+ {0x10,0x08,0x08},{0x10,0x0a,0x08},{0x10,0x0c,0x08},{0x10,0x0e,0x08},{0x10,0x10,0x08},{0x0e,0x10,0x08},{0x0c,0x10,0x08},{0x0a,0x10,0x08},
+ {0x08,0x10,0x08},{0x08,0x10,0x0a},{0x08,0x10,0x0c},{0x08,0x10,0x0e},{0x08,0x10,0x10},{0x08,0x0e,0x10},{0x08,0x0c,0x10},{0x08,0x0a,0x10},
+ {0x0b,0x0b,0x10},{0x0c,0x0b,0x10},{0x0d,0x0b,0x10},{0x0f,0x0b,0x10},{0x10,0x0b,0x10},{0x10,0x0b,0x0f},{0x10,0x0b,0x0d},{0x10,0x0b,0x0c},
+ {0x10,0x0b,0x0b},{0x10,0x0c,0x0b},{0x10,0x0d,0x0b},{0x10,0x0f,0x0b},{0x10,0x10,0x0b},{0x0f,0x10,0x0b},{0x0d,0x10,0x0b},{0x0c,0x10,0x0b},
+ {0x0b,0x10,0x0b},{0x0b,0x10,0x0c},{0x0b,0x10,0x0d},{0x0b,0x10,0x0f},{0x0b,0x10,0x10},{0x0b,0x0f,0x10},{0x0b,0x0d,0x10},{0x0b,0x0c,0x10}
+};
+VideoModeBlock * CurMode;
+
+static bool SetCurMode(VideoModeBlock modeblock[],Bit16u mode) {
+ Bitu i=0;
+ while (modeblock[i].mode!=0xffff) {
+ if (modeblock[i].mode!=mode) i++;
+ else {
+ if ((!int10.vesa_oldvbe) || (ModeList_VGA[i].mode<0x120)) {
+ CurMode=&modeblock[i];
+ return true;
+ }
+ return false;
+ }
+ }
+ return false;
+}
+
+static void SetTextLines(void) {
+ // check for scanline backwards compatibility (VESA text modes??)
+ switch (real_readb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)&0x90) {
+ case 0x80: // 200 lines emulation
+ if (CurMode->mode <= 3) {
+ CurMode = &ModeList_VGA_Text_200lines[CurMode->mode];
+ } else if (CurMode->mode == 7) {
+ CurMode = &ModeList_VGA_Text_350lines[4];
+ }
+ break;
+ case 0x00: // 350 lines emulation
+ if (CurMode->mode <= 3) {
+ CurMode = &ModeList_VGA_Text_350lines[CurMode->mode];
+ } else if (CurMode->mode == 7) {
+ CurMode = &ModeList_VGA_Text_350lines[4];
+ }
+ break;
+ }
+}
+
+void INT10_SetCurMode(void) {
+ Bit16u bios_mode=(Bit16u)real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ if (GCC_UNLIKELY(CurMode->mode!=bios_mode)) {
+ bool mode_changed=false;
+ switch (machine) {
+ case MCH_CGA:
+ if (bios_mode<7) mode_changed=SetCurMode(ModeList_OTHER,bios_mode);
+ break;
+ case TANDY_ARCH_CASE:
+ if (bios_mode!=7 && bios_mode<=0xa) mode_changed=SetCurMode(ModeList_OTHER,bios_mode);
+ break;
+ case MCH_HERC:
+ if (bios_mode<7) mode_changed=SetCurMode(ModeList_OTHER,bios_mode);
+ else if (bios_mode==7) {mode_changed=true;CurMode=&Hercules_Mode;}
+ break;
+ case MCH_EGA:
+ mode_changed=SetCurMode(ModeList_EGA,bios_mode);
+ break;
+ case VGA_ARCH_CASE:
+ switch (svgaCard) {
+ case SVGA_TsengET4K:
+ case SVGA_TsengET3K:
+ mode_changed=SetCurMode(ModeList_VGA_Tseng,bios_mode);
+ break;
+ case SVGA_ParadisePVGA1A:
+ mode_changed=SetCurMode(ModeList_VGA_Paradise,bios_mode);
+ break;
+ case SVGA_S3Trio:
+ if (bios_mode>=0x68 && CurMode->mode==(bios_mode+0x98)) break;
+ // fall-through
+ default:
+ mode_changed=SetCurMode(ModeList_VGA,bios_mode);
+ break;
+ }
+ if (mode_changed && CurMode->type==M_TEXT) SetTextLines();
+ break;
+ }
+ if (mode_changed) LOG(LOG_INT10,LOG_WARN)("BIOS video mode changed to %X",bios_mode);
+ }
+}
+
+static void FinishSetMode(bool clearmem) {
+ /* Clear video memory if needs be */
+ if (clearmem) {
+ switch (CurMode->type) {
+ case M_TANDY16:
+ case M_CGA4:
+ if ((machine==MCH_PCJR) && (CurMode->mode >= 9)) {
+ // PCJR cannot access the full 32k at 0xb800
+ for (Bit16u ct=0;ct<16*1024;ct++) {
+ // 0x1800 is the last 32k block in 128k, as set in the CRTCPU_PAGE register
+ real_writew(0x1800,ct*2,0x0000);
+ }
+ break;
+ }
+ // fall-through
+ case M_CGA2:
+ for (Bit16u ct=0;ct<16*1024;ct++) {
+ real_writew( 0xb800,ct*2,0x0000);
+ }
+ break;
+ case M_TEXT: {
+ Bit16u seg = (CurMode->mode==7)?0xb000:0xb800;
+ for (Bit16u ct=0;ct<16*1024;ct++) real_writew(seg,ct*2,0x0720);
+ break;
+ }
+ case M_EGA:
+ case M_VGA:
+ case M_LIN8:
+ case M_LIN4:
+ case M_LIN15:
+ case M_LIN16:
+ case M_LIN32:
+ /* Hack we just access the memory directly */
+ memset(vga.mem.linear,0,vga.vmemsize);
+ memset(vga.fastmem, 0, vga.vmemsize<<1);
+ }
+ }
+ /* Setup the BIOS */
+ if (CurMode->mode<128) real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE,(Bit8u)CurMode->mode);
+ else real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE,(Bit8u)(CurMode->mode-0x98)); //Looks like the s3 bios
+ real_writew(BIOSMEM_SEG,BIOSMEM_NB_COLS,(Bit16u)CurMode->twidth);
+ real_writew(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE,(Bit16u)CurMode->plength);
+ real_writew(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS,((CurMode->mode==7 )|| (CurMode->mode==0x0f)) ? 0x3b4 : 0x3d4);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_NB_ROWS,(Bit8u)(CurMode->theight-1));
+ real_writew(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,(Bit16u)CurMode->cheight);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,(0x60|(clearmem?0:0x80)));
+ real_writeb(BIOSMEM_SEG,BIOSMEM_SWITCHES,0x09);
+
+ // this is an index into the dcc table:
+ if (IS_VGA_ARCH) real_writeb(BIOSMEM_SEG,BIOSMEM_DCC_INDEX,0x0b);
+
+ // Set cursor shape
+ if (CurMode->type==M_TEXT) {
+ INT10_SetCursorShape(0x06,07);
+ }
+ // Set cursor pos for page 0..7
+ for (Bit8u ct=0;ct<8;ct++) INT10_SetCursorPos(0,0,ct);
+ // Set active page 0
+ INT10_SetActivePage(0);
+ /* Set some interrupt vectors */
+ if (CurMode->mode<=3 || CurMode->mode==7)
+ RealSetVec(0x43,int10.rom.font_8_first);
+ else {
+ switch (CurMode->cheight) {
+ case 8:RealSetVec(0x43,int10.rom.font_8_first);break;
+ case 14:RealSetVec(0x43,int10.rom.font_14);break;
+ case 16:RealSetVec(0x43,int10.rom.font_16);break;
+ }
+ }
+}
+
+bool INT10_SetVideoMode_OTHER(Bit16u mode,bool clearmem) {
+ switch (machine) {
+ case MCH_CGA:
+ if (mode>6) return false;
+ case TANDY_ARCH_CASE:
+ if (mode>0xa) return false;
+ if (mode==7) mode=0; // PCJR defaults to 0 on illegal mode 7
+ if (!SetCurMode(ModeList_OTHER,mode)) {
+ LOG(LOG_INT10,LOG_ERROR)("Trying to set illegal mode %X",mode);
+ return false;
+ }
+ break;
+ case MCH_HERC:
+ // Allow standard color modes if equipment word is not set to mono (Victory Road)
+ if ((real_readw(BIOSMEM_SEG,BIOSMEM_INITIAL_MODE)&0x30)!=0x30 && mode<7) {
+ SetCurMode(ModeList_OTHER,mode);
+ FinishSetMode(clearmem);
+ return true;
+ }
+ CurMode=&Hercules_Mode;
+ mode=7; // in case the video parameter table is modified
+ break;
+ }
+ LOG(LOG_INT10,LOG_NORMAL)("Set Video Mode %X",mode);
+
+ /* Setup the VGA to the correct mode */
+// VGA_SetMode(CurMode->type);
+ /* Setup the CRTC */
+ Bitu crtc_base=machine==MCH_HERC ? 0x3b4 : 0x3d4;
+ //Horizontal total
+ IO_WriteW(crtc_base,0x00 | (CurMode->htotal) << 8);
+ //Horizontal displayed
+ IO_WriteW(crtc_base,0x01 | (CurMode->hdispend) << 8);
+ //Horizontal sync position
+ IO_WriteW(crtc_base,0x02 | (CurMode->hdispend+1) << 8);
+ //Horizontal sync width, seems to be fixed to 0xa, for cga at least, hercules has 0xf
+ IO_WriteW(crtc_base,0x03 | (0xa) << 8);
+ ////Vertical total
+ IO_WriteW(crtc_base,0x04 | (CurMode->vtotal) << 8);
+ //Vertical total adjust, 6 for cga,hercules,tandy
+ IO_WriteW(crtc_base,0x05 | (6) << 8);
+ //Vertical displayed
+ IO_WriteW(crtc_base,0x06 | (CurMode->vdispend) << 8);
+ //Vertical sync position
+ IO_WriteW(crtc_base,0x07 | (CurMode->vdispend + ((CurMode->vtotal - CurMode->vdispend)/2)-1) << 8);
+ //Maximum scanline
+ Bit8u scanline,crtpage;
+ scanline=8;
+ switch(CurMode->type) {
+ case M_TEXT:
+ if (machine==MCH_HERC) scanline=14;
+ else scanline=8;
+ break;
+ case M_CGA2:
+ scanline=2;
+ break;
+ case M_CGA4:
+ if (CurMode->mode!=0xa) scanline=2;
+ else scanline=4;
+ break;
+ case M_TANDY16:
+ if (CurMode->mode!=0x9) scanline=2;
+ else scanline=4;
+ break;
+ }
+ IO_WriteW(crtc_base,0x09 | (scanline-1) << 8);
+ //Setup the CGA palette using VGA DAC palette
+ for (Bit8u ct=0;ct<16;ct++) VGA_DAC_SetEntry(ct,cga_palette[ct][0],cga_palette[ct][1],cga_palette[ct][2]);
+ //Setup the tandy palette
+ for (Bit8u ct=0;ct<16;ct++) VGA_DAC_CombineColor(ct,ct);
+ //Setup the special registers for each machine type
+ Bit8u mode_control_list[0xa+1]={
+ 0x2c,0x28,0x2d,0x29, //0-3
+ 0x2a,0x2e,0x1e,0x29, //4-7
+ 0x2a,0x2b,0x3b //8-a
+ };
+ Bit8u mode_control_list_pcjr[0xa+1]={
+ 0x0c,0x08,0x0d,0x09, //0-3
+ 0x0a,0x0e,0x0e,0x09, //4-7
+ 0x1a,0x1b,0x0b //8-a
+ };
+ Bit8u mode_control,color_select;
+ switch (machine) {
+ case MCH_HERC:
+ IO_WriteB(0x3b8,0x28); // TEXT mode and blinking characters
+
+ Herc_Palette();
+ VGA_DAC_CombineColor(0,0);
+ VGA_DAC_CombineColor(1,7);
+
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x29); // attribute controls blinking
+ break;
+ case MCH_CGA:
+ mode_control=mode_control_list[CurMode->mode];
+ if (CurMode->mode == 0x6) color_select=0x3f;
+ else color_select=0x30;
+ IO_WriteB(0x3d8,mode_control);
+ IO_WriteB(0x3d9,color_select);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,mode_control);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,color_select);
+ break;
+ case MCH_TANDY:
+ /* Init some registers */
+ IO_WriteB(0x3da,0x1);IO_WriteB(0x3de,0xf); //Palette mask always 0xf
+ IO_WriteB(0x3da,0x2);IO_WriteB(0x3de,0x0); //black border
+ IO_WriteB(0x3da,0x3); //Tandy color overrides?
+ switch (CurMode->mode) {
+ case 0x8:
+ IO_WriteB(0x3de,0x14);break;
+ case 0x9:
+ IO_WriteB(0x3de,0x14);break;
+ case 0xa:
+ IO_WriteB(0x3de,0x0c);break;
+ default:
+ IO_WriteB(0x3de,0x0);break;
+ }
+ // write palette
+ for(Bitu i = 0; i < 16; i++) {
+ IO_WriteB(0x3da,i+0x10);
+ IO_WriteB(0x3de,i);
+ }
+ //Clear extended mapping
+ IO_WriteB(0x3da,0x5);
+ IO_WriteB(0x3de,0x0);
+ //Clear monitor mode
+ IO_WriteB(0x3da,0x8);
+ IO_WriteB(0x3de,0x0);
+ crtpage=(CurMode->mode>=0x9) ? 0xf6 : 0x3f;
+ IO_WriteB(0x3df,crtpage);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CRTCPU_PAGE,crtpage);
+ mode_control=mode_control_list[CurMode->mode];
+ if (CurMode->mode == 0x6 || CurMode->mode==0xa) color_select=0x3f;
+ else color_select=0x30;
+ IO_WriteB(0x3d8,mode_control);
+ IO_WriteB(0x3d9,color_select);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,mode_control);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,color_select);
+ break;
+ case MCH_PCJR:
+ /* Init some registers */
+ IO_ReadB(0x3da);
+ IO_WriteB(0x3da,0x1);IO_WriteB(0x3da,0xf); //Palette mask always 0xf
+ IO_WriteB(0x3da,0x2);IO_WriteB(0x3da,0x0); //black border
+ IO_WriteB(0x3da,0x3);
+ if (CurMode->mode<=0x04) IO_WriteB(0x3da,0x02);
+ else if (CurMode->mode==0x06) IO_WriteB(0x3da,0x08);
+ else IO_WriteB(0x3da,0x00);
+
+ /* set CRT/Processor page register */
+ if (CurMode->mode<0x04) crtpage=0x3f;
+ else if (CurMode->mode>=0x09) crtpage=0xf6;
+ else crtpage=0x7f;
+ IO_WriteB(0x3df,crtpage);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CRTCPU_PAGE,crtpage);
+
+ mode_control=mode_control_list_pcjr[CurMode->mode];
+ IO_WriteB(0x3da,0x0);IO_WriteB(0x3da,mode_control);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,mode_control);
+
+ if (CurMode->mode == 0x6 || CurMode->mode==0xa) color_select=0x3f;
+ else color_select=0x30;
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,color_select);
+ INT10_SetColorSelect(1);
+ INT10_SetBackgroundBorder(0);
+ break;
+ }
+
+ RealPt vparams = RealGetVec(0x1d);
+ if ((vparams != RealMake(0xf000,0xf0a4)) && (mode < 8)) {
+ // load crtc parameters from video params table
+ Bit16u crtc_block_index = 0;
+ if (mode < 2) crtc_block_index = 0;
+ else if (mode < 4) crtc_block_index = 1;
+ else if (mode < 7) crtc_block_index = 2;
+ else if (mode == 7) crtc_block_index = 3; // MDA mono mode; invalid for others
+ else if (mode < 9) crtc_block_index = 2;
+ else crtc_block_index = 3; // Tandy/PCjr modes
+
+ // init CRTC registers
+ for (Bit16u i = 0; i < 16; i++)
+ IO_WriteW(crtc_base, i | (real_readb(RealSeg(vparams),
+ RealOff(vparams) + i + crtc_block_index*16) << 8));
+ }
+ FinishSetMode(clearmem);
+ return true;
+}
+
+
+bool INT10_SetVideoMode(Bit16u mode) {
+ bool clearmem=true;Bitu i;
+ if (mode>=0x100) {
+ if ((mode & 0x4000) && int10.vesa_nolfb) return false;
+ if (mode & 0x8000) clearmem=false;
+ mode&=0xfff;
+ }
+ if ((mode<0x100) && (mode & 0x80)) {
+ clearmem=false;
+ mode-=0x80;
+ }
+ int10.vesa_setmode=0xffff;
+ LOG(LOG_INT10,LOG_NORMAL)("Set Video Mode %X",mode);
+ if (!IS_EGAVGA_ARCH) return INT10_SetVideoMode_OTHER(mode,clearmem);
+
+ /* First read mode setup settings from bios area */
+// Bit8u video_ctl=real_readb(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL);
+// Bit8u vga_switches=real_readb(BIOSMEM_SEG,BIOSMEM_SWITCHES);
+ Bit8u modeset_ctl=real_readb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL);
+
+ if (IS_VGA_ARCH) {
+ if (svga.accepts_mode) {
+ if (!svga.accepts_mode(mode)) return false;
+ }
+
+ switch(svgaCard) {
+ case SVGA_TsengET4K:
+ case SVGA_TsengET3K:
+ if (!SetCurMode(ModeList_VGA_Tseng,mode)){
+ LOG(LOG_INT10,LOG_ERROR)("VGA:Trying to set illegal mode %X",mode);
+ return false;
+ }
+ break;
+ case SVGA_ParadisePVGA1A:
+ if (!SetCurMode(ModeList_VGA_Paradise,mode)){
+ LOG(LOG_INT10,LOG_ERROR)("VGA:Trying to set illegal mode %X",mode);
+ return false;
+ }
+ break;
+ default:
+ if (!SetCurMode(ModeList_VGA,mode)){
+ LOG(LOG_INT10,LOG_ERROR)("VGA:Trying to set illegal mode %X",mode);
+ return false;
+ }
+ }
+ if (CurMode->type==M_TEXT) SetTextLines();
+ } else {
+ if (!SetCurMode(ModeList_EGA,mode)){
+ LOG(LOG_INT10,LOG_ERROR)("EGA:Trying to set illegal mode %X",mode);
+ return false;
+ }
+ }
+
+ /* Setup the VGA to the correct mode */
+
+ Bit16u crtc_base;
+ bool mono_mode=(mode == 7) || (mode==0xf);
+ if (mono_mode) crtc_base=0x3b4;
+ else crtc_base=0x3d4;
+
+ if (IS_VGA_ARCH && (svgaCard == SVGA_S3Trio)) {
+ // Disable MMIO here so we can read / write memory
+ IO_Write(crtc_base,0x53);
+ IO_Write(crtc_base+1,0x0);
+ }
+
+ /* Setup MISC Output Register */
+ Bit8u misc_output=0x2 | (mono_mode ? 0x0 : 0x1);
+
+ if (machine==MCH_EGA) {
+ // 16MHz clock for 350-line EGA modes except mode F
+ if ((CurMode->vdispend==350) && (mode!=0xf)) misc_output|=0x4;
+ } else {
+ // 28MHz clock for 9-pixel wide chars
+ if ((CurMode->type==M_TEXT) && (CurMode->cwidth==9)) misc_output|=0x4;
+ }
+
+ switch (CurMode->vdispend) {
+ case 400:
+ misc_output|=0x60;
+ break;
+ case 480:
+ misc_output|=0xe0;
+ break;
+ case 350:
+ misc_output|=0xa0;
+ break;
+ default:
+ misc_output|=0x60;
+ }
+ IO_Write(0x3c2,misc_output); //Setup for 3b4 or 3d4
+
+ /* Program Sequencer */
+ Bit8u seq_data[SEQ_REGS];
+ memset(seq_data,0,SEQ_REGS);
+ seq_data[1]|=0x01; //8 dot fonts by default
+ if (CurMode->special & _EGA_HALF_CLOCK) seq_data[1]|=0x08; //Check for half clock
+ if ((machine==MCH_EGA) && (CurMode->special & _EGA_HALF_CLOCK)) seq_data[1]|=0x02;
+ seq_data[4]|=0x02; //More than 64kb
+ switch (CurMode->type) {
+ case M_TEXT:
+ if (CurMode->cwidth==9) seq_data[1] &= ~1;
+ seq_data[2]|=0x3; //Enable plane 0 and 1
+ seq_data[4]|=0x01; //Alpanumeric
+ if (IS_VGA_ARCH) seq_data[4]|=0x04; //odd/even enabled
+ break;
+ case M_CGA2:
+ seq_data[2]|=0xf; //Enable plane 0
+ if (machine==MCH_EGA) seq_data[4]|=0x04; //odd/even enabled
+ break;
+ case M_CGA4:
+ if (machine==MCH_EGA) seq_data[2]|=0x03; //Enable plane 0 and 1
+ break;
+ case M_LIN4:
+ case M_EGA:
+ seq_data[2]|=0xf; //Enable all planes for writing
+ if (machine==MCH_EGA) seq_data[4]|=0x04; //odd/even enabled
+ break;
+ case M_LIN8: //Seems to have the same reg layout from testing
+ case M_LIN15:
+ case M_LIN16:
+ case M_LIN32:
+ case M_VGA:
+ seq_data[2]|=0xf; //Enable all planes for writing
+ seq_data[4]|=0xc; //Graphics - odd/even - Chained
+ break;
+ }
+ for (Bit8u ct=0;ct<SEQ_REGS;ct++) {
+ IO_Write(0x3c4,ct);
+ IO_Write(0x3c5,seq_data[ct]);
+ }
+ vga.config.compatible_chain4 = true; // this may be changed by SVGA chipset emulation
+
+ /* Program CRTC */
+ /* First disable write protection */
+ IO_Write(crtc_base,0x11);
+ IO_Write(crtc_base+1,IO_Read(crtc_base+1)&0x7f);
+ /* Clear all the regs */
+ for (Bit8u ct=0x0;ct<=0x18;ct++) {
+ IO_Write(crtc_base,ct);IO_Write(crtc_base+1,0);
+ }
+ Bit8u overflow=0;Bit8u max_scanline=0;
+ Bit8u ver_overflow=0;Bit8u hor_overflow=0;
+ /* Horizontal Total */
+ IO_Write(crtc_base,0x00);IO_Write(crtc_base+1,(Bit8u)(CurMode->htotal-5));
+ hor_overflow|=((CurMode->htotal-5) & 0x100) >> 8;
+ /* Horizontal Display End */
+ IO_Write(crtc_base,0x01);IO_Write(crtc_base+1,(Bit8u)(CurMode->hdispend-1));
+ hor_overflow|=((CurMode->hdispend-1) & 0x100) >> 7;
+ /* Start horizontal Blanking */
+ IO_Write(crtc_base,0x02);IO_Write(crtc_base+1,(Bit8u)CurMode->hdispend);
+ hor_overflow|=((CurMode->hdispend) & 0x100) >> 6;
+ /* End horizontal Blanking */
+ Bitu blank_end=(CurMode->htotal-2) & 0x7f;
+ IO_Write(crtc_base,0x03);IO_Write(crtc_base+1,0x80|(blank_end & 0x1f));
+
+ /* Start Horizontal Retrace */
+ Bitu ret_start;
+ if ((CurMode->special & _EGA_HALF_CLOCK) && (CurMode->type!=M_CGA2)) ret_start = (CurMode->hdispend+3);
+ else if (CurMode->type==M_TEXT) ret_start = (CurMode->hdispend+5);
+ else ret_start = (CurMode->hdispend+4);
+ IO_Write(crtc_base,0x04);IO_Write(crtc_base+1,(Bit8u)ret_start);
+ hor_overflow|=(ret_start & 0x100) >> 4;
+
+ /* End Horizontal Retrace */
+ Bitu ret_end;
+ if (CurMode->special & _EGA_HALF_CLOCK) {
+ if (CurMode->type==M_CGA2) ret_end=0; // mode 6
+ else if (CurMode->special & _EGA_LINE_DOUBLE) ret_end = (CurMode->htotal-18) & 0x1f;
+ else ret_end = ((CurMode->htotal-18) & 0x1f) | 0x20; // mode 0&1 have 1 char sync delay
+ } else if (CurMode->type==M_TEXT) ret_end = (CurMode->htotal-3) & 0x1f;
+ else ret_end = (CurMode->htotal-4) & 0x1f;
+
+ IO_Write(crtc_base,0x05);IO_Write(crtc_base+1,(Bit8u)(ret_end | (blank_end & 0x20) << 2));
+
+ /* Vertical Total */
+ IO_Write(crtc_base,0x06);IO_Write(crtc_base+1,(Bit8u)(CurMode->vtotal-2));
+ overflow|=((CurMode->vtotal-2) & 0x100) >> 8;
+ overflow|=((CurMode->vtotal-2) & 0x200) >> 4;
+ ver_overflow|=((CurMode->vtotal-2) & 0x400) >> 10;
+
+ Bitu vretrace;
+ if (IS_VGA_ARCH) {
+ switch (CurMode->vdispend) {
+ case 400: vretrace=CurMode->vdispend+12;
+ break;
+ case 480: vretrace=CurMode->vdispend+10;
+ break;
+ case 350: vretrace=CurMode->vdispend+37;
+ break;
+ default: vretrace=CurMode->vdispend+12;
+ }
+ } else {
+ switch (CurMode->vdispend) {
+ case 350: vretrace=CurMode->vdispend;
+ break;
+ default: vretrace=CurMode->vdispend+24;
+ }
+ }
+
+ /* Vertical Retrace Start */
+ IO_Write(crtc_base,0x10);IO_Write(crtc_base+1,(Bit8u)vretrace);
+ overflow|=(vretrace & 0x100) >> 6;
+ overflow|=(vretrace & 0x200) >> 2;
+ ver_overflow|=(vretrace & 0x400) >> 6;
+
+ /* Vertical Retrace End */
+ IO_Write(crtc_base,0x11);IO_Write(crtc_base+1,(vretrace+2) & 0xF);
+
+ /* Vertical Display End */
+ IO_Write(crtc_base,0x12);IO_Write(crtc_base+1,(Bit8u)(CurMode->vdispend-1));
+ overflow|=((CurMode->vdispend-1) & 0x100) >> 7;
+ overflow|=((CurMode->vdispend-1) & 0x200) >> 3;
+ ver_overflow|=((CurMode->vdispend-1) & 0x400) >> 9;
+
+ Bitu vblank_trim;
+ if (IS_VGA_ARCH) {
+ switch (CurMode->vdispend) {
+ case 400: vblank_trim=6;
+ break;
+ case 480: vblank_trim=7;
+ break;
+ case 350: vblank_trim=5;
+ break;
+ default: vblank_trim=8;
+ }
+ } else {
+ switch (CurMode->vdispend) {
+ case 350: vblank_trim=0;
+ break;
+ default: vblank_trim=23;
+ }
+ }
+
+ /* Vertical Blank Start */
+ IO_Write(crtc_base,0x15);IO_Write(crtc_base+1,(Bit8u)(CurMode->vdispend+vblank_trim));
+ overflow|=((CurMode->vdispend+vblank_trim) & 0x100) >> 5;
+ max_scanline|=((CurMode->vdispend+vblank_trim) & 0x200) >> 4;
+ ver_overflow|=((CurMode->vdispend+vblank_trim) & 0x400) >> 8;
+
+ /* Vertical Blank End */
+ IO_Write(crtc_base,0x16);IO_Write(crtc_base+1,(Bit8u)(CurMode->vtotal-vblank_trim-2));
+
+ /* Line Compare */
+ Bitu line_compare=(CurMode->vtotal < 1024) ? 1023 : 2047;
+ IO_Write(crtc_base,0x18);IO_Write(crtc_base+1,line_compare&0xff);
+ overflow|=(line_compare & 0x100) >> 4;
+ max_scanline|=(line_compare & 0x200) >> 3;
+ ver_overflow|=(line_compare & 0x400) >> 4;
+ Bit8u underline=0;
+ /* Maximum scanline / Underline Location */
+ if (CurMode->special & _EGA_LINE_DOUBLE) {
+ if (machine!=MCH_EGA) max_scanline|=0x80;
+ }
+ switch (CurMode->type) {
+ case M_TEXT:
+ max_scanline|=CurMode->cheight-1;
+ underline=mono_mode ? CurMode->cheight-1 : 0x1f; // mode 7 uses underline position
+ break;
+ case M_VGA:
+ underline=0x40;
+ max_scanline|=1; //Vga doesn't use double line but this
+ break;
+ case M_LIN8:
+ case M_LIN15:
+ case M_LIN16:
+ case M_LIN32:
+ underline=0x60; //Seems to enable the every 4th clock on my s3
+ break;
+ case M_CGA2:
+ case M_CGA4:
+ max_scanline|=1;
+ break;
+ default:
+ if (CurMode->vdispend==350) underline=0x0f;
+ break;
+ }
+
+ IO_Write(crtc_base,0x09);IO_Write(crtc_base+1,max_scanline);
+ IO_Write(crtc_base,0x14);IO_Write(crtc_base+1,underline);
+
+ /* OverFlow */
+ IO_Write(crtc_base,0x07);IO_Write(crtc_base+1,overflow);
+
+ if (svgaCard == SVGA_S3Trio) {
+ /* Extended Horizontal Overflow */
+ IO_Write(crtc_base,0x5d);IO_Write(crtc_base+1,hor_overflow);
+ /* Extended Vertical Overflow */
+ IO_Write(crtc_base,0x5e);IO_Write(crtc_base+1,ver_overflow);
+ }
+
+ /* Offset Register */
+ Bitu offset;
+ switch (CurMode->type) {
+ case M_LIN8:
+ offset = CurMode->swidth/8;
+ break;
+ case M_LIN15:
+ case M_LIN16:
+ offset = 2 * CurMode->swidth/8;
+ break;
+ case M_LIN32:
+ offset = 4 * CurMode->swidth/8;
+ break;
+ default:
+ offset = CurMode->hdispend/2;
+ }
+ IO_Write(crtc_base,0x13);
+ IO_Write(crtc_base + 1,offset & 0xff);
+
+ if (svgaCard == SVGA_S3Trio) {
+ /* Extended System Control 2 Register */
+ /* This register actually has more bits but only use the extended offset ones */
+ IO_Write(crtc_base,0x51);
+ IO_Write(crtc_base + 1,(Bit8u)((offset & 0x300) >> 4));
+ /* Clear remaining bits of the display start */
+ IO_Write(crtc_base,0x69);
+ IO_Write(crtc_base + 1,0);
+ /* Extended Vertical Overflow */
+ IO_Write(crtc_base,0x5e);IO_Write(crtc_base+1,ver_overflow);
+ }
+
+ /* Mode Control */
+ Bit8u mode_control=0;
+
+ switch (CurMode->type) {
+ case M_CGA2:
+ mode_control=0xc2; // 0x06 sets address wrap.
+ break;
+ case M_CGA4:
+ mode_control=0xa2;
+ break;
+ case M_LIN4:
+ case M_EGA:
+ if (CurMode->mode==0x11) // 0x11 also sets address wrap. thought maybe all 2 color modes did but 0x0f doesn't.
+ mode_control=0xc3; // so.. 0x11 or 0x0f a one off?
+ else {
+ if (machine==MCH_EGA) {
+ if (CurMode->special & _EGA_LINE_DOUBLE) mode_control=0xc3;
+ else mode_control=0x8b;
+ } else {
+ mode_control=0xe3;
+ }
+ }
+ break;
+ case M_TEXT:
+ case M_VGA:
+ case M_LIN8:
+ case M_LIN15:
+ case M_LIN16:
+ case M_LIN32:
+ mode_control=0xa3;
+ if (CurMode->special & _VGA_PIXEL_DOUBLE)
+ mode_control |= 0x08;
+ break;
+ }
+
+ IO_Write(crtc_base,0x17);IO_Write(crtc_base+1,mode_control);
+ /* Renable write protection */
+ IO_Write(crtc_base,0x11);
+ IO_Write(crtc_base+1,IO_Read(crtc_base+1)|0x80);
+
+ if (svgaCard == SVGA_S3Trio) {
+ /* Setup the correct clock */
+ if (CurMode->mode>=0x100) {
+ if (CurMode->vdispend>480)
+ misc_output|=0xc0; //480-line sync
+ misc_output|=0x0c; //Select clock 3
+ Bitu clock=CurMode->vtotal*8*CurMode->htotal*70;
+ VGA_SetClock(3,clock/1000);
+ }
+ Bit8u misc_control_2;
+ /* Setup Pixel format */
+ switch (CurMode->type) {
+ case M_LIN8:
+ misc_control_2=0x00;
+ break;
+ case M_LIN15:
+ misc_control_2=0x30;
+ break;
+ case M_LIN16:
+ misc_control_2=0x50;
+ break;
+ case M_LIN32:
+ misc_control_2=0xd0;
+ break;
+ default:
+ misc_control_2=0x0;
+ break;
+ }
+ IO_WriteB(crtc_base,0x67);IO_WriteB(crtc_base+1,misc_control_2);
+ }
+
+ /* Write Misc Output */
+ IO_Write(0x3c2,misc_output);
+ /* Program Graphics controller */
+ Bit8u gfx_data[GFX_REGS];
+ memset(gfx_data,0,GFX_REGS);
+ gfx_data[0x7]=0xf; /* Color don't care */
+ gfx_data[0x8]=0xff; /* BitMask */
+ switch (CurMode->type) {
+ case M_TEXT:
+ gfx_data[0x5]|=0x10; //Odd-Even Mode
+ gfx_data[0x6]|=mono_mode ? 0x0a : 0x0e; //Either b800 or b000
+ break;
+ case M_LIN8:
+ case M_LIN15:
+ case M_LIN16:
+ case M_LIN32:
+ case M_VGA:
+ gfx_data[0x5]|=0x40; //256 color mode
+ gfx_data[0x6]|=0x05; //graphics mode at 0xa000-affff
+ break;
+ case M_LIN4:
+ case M_EGA:
+ if (CurMode->mode == 0x0f)
+ gfx_data[0x7]=0x05; // only planes 0 and 2 are used
+ gfx_data[0x6]|=0x05; //graphics mode at 0xa000-affff
+ break;
+ case M_CGA4:
+ gfx_data[0x5]|=0x20; //CGA mode
+ gfx_data[0x6]|=0x0f; //graphics mode at at 0xb800=0xbfff
+ if (machine==MCH_EGA) gfx_data[0x5]|=0x10;
+ break;
+ case M_CGA2:
+ if (machine==MCH_EGA) {
+ gfx_data[0x6]|=0x0d; //graphics mode at at 0xb800=0xbfff
+ } else {
+ gfx_data[0x6]|=0x0f; //graphics mode at at 0xb800=0xbfff
+ }
+ break;
+ }
+ for (Bit8u ct=0;ct<GFX_REGS;ct++) {
+ IO_Write(0x3ce,ct);
+ IO_Write(0x3cf,gfx_data[ct]);
+ }
+ Bit8u att_data[ATT_REGS];
+ memset(att_data,0,ATT_REGS);
+ att_data[0x12]=0xf; //Always have all color planes enabled
+ /* Program Attribute Controller */
+ switch (CurMode->type) {
+ case M_EGA:
+ case M_LIN4:
+ att_data[0x10]=0x01; //Color Graphics
+ switch (CurMode->mode) {
+ case 0x0f:
+ att_data[0x12]=0x05; // planes 0 and 2 enabled
+ att_data[0x10]|=0x0a; // monochrome and blinking
+
+ att_data[0x01]=0x08; // low-intensity
+ att_data[0x04]=0x18; // blink-on case
+ att_data[0x05]=0x18; // high-intensity
+ att_data[0x09]=0x08; // low-intensity in blink-off case
+ att_data[0x0d]=0x18; // high-intensity in blink-off
+ break;
+ case 0x11:
+ for (i=1;i<16;i++) att_data[i]=0x3f;
+ break;
+ case 0x10:
+ case 0x12:
+ goto att_text16;
+ default:
+ if ( CurMode->type == M_LIN4 )
+ goto att_text16;
+ for (Bit8u ct=0;ct<8;ct++) {
+ att_data[ct]=ct;
+ att_data[ct+8]=ct+0x10;
+ }
+ break;
+ }
+ break;
+ case M_TANDY16:
+ att_data[0x10]=0x01; //Color Graphics
+ for (Bit8u ct=0;ct<16;ct++) att_data[ct]=ct;
+ break;
+ case M_TEXT:
+ if (CurMode->cwidth==9) {
+ att_data[0x13]=0x08; //Pel panning on 8, although we don't have 9 dot text mode
+ att_data[0x10]=0x0C; //Color Text with blinking, 9 Bit characters
+ } else {
+ att_data[0x13]=0x00;
+ att_data[0x10]=0x08; //Color Text with blinking, 8 Bit characters
+ }
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,0x30);
+att_text16:
+ if (CurMode->mode==7) {
+ att_data[0]=0x00;
+ att_data[8]=0x10;
+ for (i=1; i<8; i++) {
+ att_data[i]=0x08;
+ att_data[i+8]=0x18;
+ }
+ } else {
+ for (Bit8u ct=0;ct<8;ct++) {
+ att_data[ct]=ct;
+ att_data[ct+8]=ct+0x38;
+ }
+ att_data[0x06]=0x14; //Odd Color 6 yellow/brown.
+ }
+ break;
+ case M_CGA2:
+ att_data[0x10]=0x01; //Color Graphics
+ att_data[0]=0x0;
+ for (i=1;i<0x10;i++) att_data[i]=0x17;
+ att_data[0x12]=0x1; //Only enable 1 plane
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,0x3f);
+ break;
+ case M_CGA4:
+ att_data[0x10]=0x01; //Color Graphics
+ att_data[0]=0x0;
+ att_data[1]=0x13;
+ att_data[2]=0x15;
+ att_data[3]=0x17;
+ att_data[4]=0x02;
+ att_data[5]=0x04;
+ att_data[6]=0x06;
+ att_data[7]=0x07;
+ for (Bit8u ct=0x8;ct<0x10;ct++)
+ att_data[ct] = ct + 0x8;
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,0x30);
+ break;
+ case M_VGA:
+ case M_LIN8:
+ case M_LIN15:
+ case M_LIN16:
+ case M_LIN32:
+ for (Bit8u ct=0;ct<16;ct++) att_data[ct]=ct;
+ att_data[0x10]=0x41; //Color Graphics 8-bit
+ break;
+ }
+ IO_Read(mono_mode ? 0x3ba : 0x3da);
+ if ((modeset_ctl & 8)==0) {
+ for (Bit8u ct=0;ct<ATT_REGS;ct++) {
+ IO_Write(0x3c0,ct);
+ IO_Write(0x3c0,att_data[ct]);
+ }
+ vga.config.pel_panning = 0;
+ IO_Write(0x3c0,0x20); IO_Write(0x3c0,0x00); //Disable palette access
+ IO_Write(0x3c6,0xff); //Reset Pelmask
+ /* Setup the DAC */
+ IO_Write(0x3c8,0);
+ switch (CurMode->type) {
+ case M_EGA:
+ if (CurMode->mode>0xf) {
+ goto dac_text16;
+ } else if (CurMode->mode==0xf) {
+ for (i=0;i<64;i++) {
+ IO_Write(0x3c9,mtext_s3_palette[i][0]);
+ IO_Write(0x3c9,mtext_s3_palette[i][1]);
+ IO_Write(0x3c9,mtext_s3_palette[i][2]);
+ }
+ } else {
+ for (i=0;i<64;i++) {
+ IO_Write(0x3c9,ega_palette[i][0]);
+ IO_Write(0x3c9,ega_palette[i][1]);
+ IO_Write(0x3c9,ega_palette[i][2]);
+ }
+ }
+ break;
+ case M_CGA2:
+ case M_CGA4:
+ case M_TANDY16:
+ for (i=0;i<64;i++) {
+ IO_Write(0x3c9,cga_palette_2[i][0]);
+ IO_Write(0x3c9,cga_palette_2[i][1]);
+ IO_Write(0x3c9,cga_palette_2[i][2]);
+ }
+ break;
+ case M_TEXT:
+ if (CurMode->mode==7) {
+ if ((IS_VGA_ARCH) && (svgaCard == SVGA_S3Trio)) {
+ for (i=0;i<64;i++) {
+ IO_Write(0x3c9,mtext_s3_palette[i][0]);
+ IO_Write(0x3c9,mtext_s3_palette[i][1]);
+ IO_Write(0x3c9,mtext_s3_palette[i][2]);
+ }
+ } else {
+ for (i=0;i<64;i++) {
+ IO_Write(0x3c9,mtext_palette[i][0]);
+ IO_Write(0x3c9,mtext_palette[i][1]);
+ IO_Write(0x3c9,mtext_palette[i][2]);
+ }
+ }
+ break;
+ } //FALLTHROUGH!!!!
+ case M_LIN4: //Added for CAD Software
+dac_text16:
+ for (i=0;i<64;i++) {
+ IO_Write(0x3c9,text_palette[i][0]);
+ IO_Write(0x3c9,text_palette[i][1]);
+ IO_Write(0x3c9,text_palette[i][2]);
+ }
+ break;
+ case M_VGA:
+ case M_LIN8:
+ case M_LIN15:
+ case M_LIN16:
+ case M_LIN32:
+ // IBM and clones use 248 default colors in the palette for 256-color mode.
+ // The last 8 colors of the palette are only initialized to 0 at BIOS init.
+ // Palette index is left at 0xf8 as on most clones, IBM leaves it at 0x10.
+ for (i=0;i<248;i++) {
+ IO_Write(0x3c9,vga_palette[i][0]);
+ IO_Write(0x3c9,vga_palette[i][1]);
+ IO_Write(0x3c9,vga_palette[i][2]);
+ }
+ break;
+ }
+ if (IS_VGA_ARCH) {
+ /* check if gray scale summing is enabled */
+ if (real_readb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL) & 2) {
+ INT10_PerformGrayScaleSumming(0,256);
+ }
+ }
+ } else {
+ for (Bit8u ct=0x10;ct<ATT_REGS;ct++) {
+ if (ct==0x11) continue; // skip overscan register
+ IO_Write(0x3c0,ct);
+ IO_Write(0x3c0,att_data[ct]);
+ }
+ vga.config.pel_panning = 0;
+ IO_Write(0x3c0,0x20); //Disable palette access
+ }
+ /* Write palette register data to dynamic save area if pointer is non-zero */
+ RealPt vsavept=real_readd(BIOSMEM_SEG,BIOSMEM_VS_POINTER);
+ RealPt dsapt=real_readd(RealSeg(vsavept),RealOff(vsavept)+4);
+ if (dsapt) {
+ for (Bit8u ct=0;ct<0x10;ct++) {
+ real_writeb(RealSeg(dsapt),RealOff(dsapt)+ct,att_data[ct]);
+ }
+ real_writeb(RealSeg(dsapt),RealOff(dsapt)+0x10,0); // overscan
+ }
+ /* Setup some special stuff for different modes */
+ Bit8u feature=real_readb(BIOSMEM_SEG,BIOSMEM_INITIAL_MODE);
+ switch (CurMode->type) {
+ case M_CGA2:
+ feature=(feature&~0x30)|0x20;
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x1e);
+ break;
+ case M_CGA4:
+ feature=(feature&~0x30)|0x20;
+ if (CurMode->mode==4) real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x2a);
+ else if (CurMode->mode==5) real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x2e);
+ else real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x2);
+ break;
+ case M_TANDY16:
+ feature=(feature&~0x30)|0x20;
+ break;
+ case M_TEXT:
+ feature=(feature&~0x30)|0x20;
+ switch (CurMode->mode) {
+ case 0:real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x2c);break;
+ case 1:real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x28);break;
+ case 2:real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x2d);break;
+ case 3:
+ case 7:real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x29);break;
+ }
+ break;
+ case M_LIN4:
+ case M_EGA:
+ case M_VGA:
+ feature=(feature&~0x30);
+ break;
+ }
+ // disabled, has to be set in bios.cpp exclusively
+// real_writeb(BIOSMEM_SEG,BIOSMEM_INITIAL_MODE,feature);
+
+ if (svgaCard == SVGA_S3Trio) {
+ /* Setup the CPU Window */
+ IO_Write(crtc_base,0x6a);
+ IO_Write(crtc_base+1,0);
+ /* Setup the linear frame buffer */
+ IO_Write(crtc_base,0x59);
+ IO_Write(crtc_base+1,(Bit8u)((S3_LFB_BASE >> 24)&0xff));
+ IO_Write(crtc_base,0x5a);
+ IO_Write(crtc_base+1,(Bit8u)((S3_LFB_BASE >> 16)&0xff));
+ IO_Write(crtc_base,0x6b); // BIOS scratchpad
+ IO_Write(crtc_base+1,(Bit8u)((S3_LFB_BASE >> 24)&0xff));
+
+ /* Setup some remaining S3 registers */
+ IO_Write(crtc_base,0x41); // BIOS scratchpad
+ IO_Write(crtc_base+1,0x88);
+ IO_Write(crtc_base,0x52); // extended BIOS scratchpad
+ IO_Write(crtc_base+1,0x80);
+
+ IO_Write(0x3c4,0x15);
+ IO_Write(0x3c5,0x03);
+
+ // Accellerator setup
+ Bitu reg_50=S3_XGA_8BPP;
+ switch (CurMode->type) {
+ case M_LIN15:
+ case M_LIN16: reg_50|=S3_XGA_16BPP; break;
+ case M_LIN32: reg_50|=S3_XGA_32BPP; break;
+ default: break;
+ }
+ switch(CurMode->swidth) {
+ case 640: reg_50|=S3_XGA_640; break;
+ case 800: reg_50|=S3_XGA_800; break;
+ case 1024: reg_50|=S3_XGA_1024; break;
+ case 1152: reg_50|=S3_XGA_1152; break;
+ case 1280: reg_50|=S3_XGA_1280; break;
+ default: break;
+ }
+ IO_WriteB(crtc_base,0x50); IO_WriteB(crtc_base+1,reg_50);
+
+ Bit8u reg_31, reg_3a;
+ switch (CurMode->type) {
+ case M_LIN15:
+ case M_LIN16:
+ case M_LIN32:
+ reg_3a=0x15;
+ break;
+ case M_LIN8:
+ // S3VBE20 does it this way. The other double pixel bit does not
+ // seem to have an effect on the Trio64.
+ if(CurMode->special&_VGA_PIXEL_DOUBLE) reg_3a=0x5;
+ else reg_3a=0x15;
+ break;
+ default:
+ reg_3a=5;
+ break;
+ };
+
+ switch (CurMode->type) {
+ case M_LIN4: // <- Theres a discrepance with real hardware on this
+ case M_LIN8:
+ case M_LIN15:
+ case M_LIN16:
+ case M_LIN32:
+ reg_31 = 9;
+ break;
+ default:
+ reg_31 = 5;
+ break;
+ }
+ IO_Write(crtc_base,0x3a);IO_Write(crtc_base+1,reg_3a);
+ IO_Write(crtc_base,0x31);IO_Write(crtc_base+1,reg_31); //Enable banked memory and 256k+ access
+ IO_Write(crtc_base,0x58);IO_Write(crtc_base+1,0x3); //Enable 8 mb of linear addressing
+
+ IO_Write(crtc_base,0x38);IO_Write(crtc_base+1,0x48); //Register lock 1
+ IO_Write(crtc_base,0x39);IO_Write(crtc_base+1,0xa5); //Register lock 2
+ } else if (svga.set_video_mode) {
+ VGA_ModeExtraData modeData;
+ modeData.ver_overflow = ver_overflow;
+ modeData.hor_overflow = hor_overflow;
+ modeData.offset = offset;
+ modeData.modeNo = CurMode->mode;
+ modeData.htotal = CurMode->htotal;
+ modeData.vtotal = CurMode->vtotal;
+ svga.set_video_mode(crtc_base, &modeData);
+ }
+
+ FinishSetMode(clearmem);
+
+ /* Set vga attrib register into defined state */
+ IO_Read(mono_mode ? 0x3ba : 0x3da);
+ IO_Write(0x3c0,0x20);
+ IO_Read(mono_mode ? 0x3ba : 0x3da); // Kukoo2 demo
+
+ /* Load text mode font */
+ if (CurMode->type==M_TEXT) {
+ INT10_ReloadFont();
+ }
+ return true;
+}
+
+Bitu VideoModeMemSize(Bitu mode) {
+ if (!IS_VGA_ARCH)
+ return 0;
+
+ VideoModeBlock* modelist = NULL;
+
+ switch (svgaCard) {
+ case SVGA_TsengET4K:
+ case SVGA_TsengET3K:
+ modelist = ModeList_VGA_Tseng;
+ break;
+ case SVGA_ParadisePVGA1A:
+ modelist = ModeList_VGA_Paradise;
+ break;
+ default:
+ modelist = ModeList_VGA;
+ break;
+ }
+
+ VideoModeBlock* vmodeBlock = NULL;
+ Bitu i=0;
+ while (modelist[i].mode!=0xffff) {
+ if (modelist[i].mode==mode) {
+ vmodeBlock = &modelist[i];
+ break;
+ }
+ i++;
+ }
+ if (!vmodeBlock)
+ return 0;
+
+ switch(vmodeBlock->type) {
+ case M_LIN4:
+ return vmodeBlock->swidth*vmodeBlock->sheight/2;
+ case M_LIN8:
+ return vmodeBlock->swidth*vmodeBlock->sheight;
+ case M_LIN15: case M_LIN16:
+ return vmodeBlock->swidth*vmodeBlock->sheight*2;
+ case M_LIN32:
+ return vmodeBlock->swidth*vmodeBlock->sheight*4;
+ case M_TEXT:
+ return vmodeBlock->twidth*vmodeBlock->theight*2;
+ }
+ // Return 0 for all other types, those always fit in memory
+ return 0;
+}
diff --git a/src/ints/int10_pal.cpp b/src/ints/int10_pal.cpp
new file mode 100644
index 000000000..39d6c8fa5
--- /dev/null
+++ b/src/ints/int10_pal.cpp
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include "dosbox.h"
+#include "mem.h"
+#include "inout.h"
+#include "int10.h"
+
+#define ACTL_MAX_REG 0x14
+
+static INLINE void ResetACTL(void) {
+ IO_Read(real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS) + 6);
+}
+
+static INLINE void WriteTandyACTL(Bit8u creg,Bit8u val) {
+ IO_Write(VGAREG_TDY_ADDRESS,creg);
+ if (machine==MCH_TANDY) IO_Write(VGAREG_TDY_DATA,val);
+ else IO_Write(VGAREG_PCJR_DATA,val);
+}
+
+void INT10_SetSinglePaletteRegister(Bit8u reg,Bit8u val) {
+ switch (machine) {
+ case MCH_PCJR:
+ reg&=0xf;
+ IO_Read(VGAREG_TDY_RESET);
+ WriteTandyACTL(reg+0x10,val);
+ IO_Write(0x3da,0x0); // palette back on
+ break;
+ case MCH_TANDY:
+ // TODO waits for vertical retrace
+ switch(vga.mode) {
+ case M_TANDY2:
+ if (reg >= 0x10) break;
+ else if (reg==1) reg = 0x1f;
+ else reg |= 0x10;
+ WriteTandyACTL(reg+0x10,val);
+ break;
+ case M_TANDY4: {
+ if (CurMode->mode!=0x0a) {
+ // Palette values are kept constand by the BIOS.
+ // The four colors are mapped to special palette values by hardware.
+ // 3D8/3D9 registers influence this mapping. We need to figure out
+ // which entry is used for the requested color.
+ if (reg > 3) break;
+ if (reg != 0) { // 0 is assumed to be at 0
+ Bit8u color_select=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL);
+ reg = reg*2+8; // Green Red Brown
+ if (color_select& 0x20) reg++; // Cyan Magenta White
+ }
+ WriteTandyACTL(reg+0x10,val);
+ }
+ // 4-color high resolution mode 0x0a isn't handled specially
+ else WriteTandyACTL(reg+0x10,val);
+ break;
+ }
+ default:
+ WriteTandyACTL(reg+0x10,val);
+ break;
+ }
+ IO_Write(0x3da,0x0); // palette back on
+ break;
+ case EGAVGA_ARCH_CASE:
+ if (!IS_VGA_ARCH) reg&=0x1f;
+ if(reg<=ACTL_MAX_REG) {
+ ResetACTL();
+ IO_Write(VGAREG_ACTL_ADDRESS,reg);
+ IO_Write(VGAREG_ACTL_WRITE_DATA,val);
+ }
+ IO_Write(VGAREG_ACTL_ADDRESS,32); //Enable output and protect palette
+ break;
+ }
+}
+
+
+void INT10_SetOverscanBorderColor(Bit8u val) {
+ switch (machine) {
+ case TANDY_ARCH_CASE:
+ IO_Read(VGAREG_TDY_RESET);
+ WriteTandyACTL(0x02,val);
+ IO_Write(VGAREG_TDY_ADDRESS, 0); // enable the screen
+ break;
+ case EGAVGA_ARCH_CASE:
+ ResetACTL();
+ IO_Write(VGAREG_ACTL_ADDRESS,0x11);
+ IO_Write(VGAREG_ACTL_WRITE_DATA,val);
+ IO_Write(VGAREG_ACTL_ADDRESS,32); //Enable output and protect palette
+ break;
+ }
+}
+
+void INT10_SetAllPaletteRegisters(PhysPt data) {
+ switch (machine) {
+ case TANDY_ARCH_CASE:
+ IO_Read(VGAREG_TDY_RESET);
+ // First the colors
+ for(Bit8u i=0;i<0x10;i++) {
+ WriteTandyACTL(i+0x10,mem_readb(data));
+ data++;
+ }
+ // Then the border
+ WriteTandyACTL(0x02,mem_readb(data));
+ break;
+ case EGAVGA_ARCH_CASE:
+ ResetACTL();
+ // First the colors
+ for(Bit8u i=0;i<0x10;i++) {
+ IO_Write(VGAREG_ACTL_ADDRESS,i);
+ IO_Write(VGAREG_ACTL_WRITE_DATA,mem_readb(data));
+ data++;
+ }
+ // Then the border
+ IO_Write(VGAREG_ACTL_ADDRESS,0x11);
+ IO_Write(VGAREG_ACTL_WRITE_DATA,mem_readb(data));
+ IO_Write(VGAREG_ACTL_ADDRESS,32); //Enable output and protect palette
+ break;
+ }
+}
+
+void INT10_ToggleBlinkingBit(Bit8u state) {
+ if(IS_VGA_ARCH) {
+ Bit8u value;
+ // state&=0x01;
+ if ((state>1) && (svgaCard==SVGA_S3Trio)) return;
+ ResetACTL();
+
+ IO_Write(VGAREG_ACTL_ADDRESS,0x10);
+ value=IO_Read(VGAREG_ACTL_READ_DATA);
+ if (state<=1) {
+ value&=0xf7;
+ value|=state<<3;
+ }
+
+ ResetACTL();
+ IO_Write(VGAREG_ACTL_ADDRESS,0x10);
+ IO_Write(VGAREG_ACTL_WRITE_DATA,value);
+ IO_Write(VGAREG_ACTL_ADDRESS,32); //Enable output and protect palette
+
+ if (state<=1) {
+ Bit8u msrval=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR)&0xdf;
+ if (state) msrval|=0x20;
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,msrval);
+ }
+ } else { // EGA
+ // Usually it reads this from the mode list in ROM
+ if (CurMode->type!=M_TEXT) return;
+
+ Bit8u value = (CurMode->cwidth==9)? 0x4:0x0;
+ if (state) value |= 0x8;
+
+ ResetACTL();
+ IO_Write(VGAREG_ACTL_ADDRESS,0x10);
+ IO_Write(VGAREG_ACTL_WRITE_DATA,value);
+ IO_Write(VGAREG_ACTL_ADDRESS,0x20);
+
+ Bit8u msrval=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR)& ~0x20;
+ if (state) msrval|=0x20;
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,msrval);
+ }
+}
+
+void INT10_GetSinglePaletteRegister(Bit8u reg,Bit8u * val) {
+ if(reg<=ACTL_MAX_REG) {
+ ResetACTL();
+ IO_Write(VGAREG_ACTL_ADDRESS,reg+32);
+ *val=IO_Read(VGAREG_ACTL_READ_DATA);
+ IO_Write(VGAREG_ACTL_WRITE_DATA,*val);
+ }
+}
+
+void INT10_GetOverscanBorderColor(Bit8u * val) {
+ ResetACTL();
+ IO_Write(VGAREG_ACTL_ADDRESS,0x11+32);
+ *val=IO_Read(VGAREG_ACTL_READ_DATA);
+ IO_Write(VGAREG_ACTL_WRITE_DATA,*val);
+}
+
+void INT10_GetAllPaletteRegisters(PhysPt data) {
+ ResetACTL();
+ // First the colors
+ for(Bit8u i=0;i<0x10;i++) {
+ IO_Write(VGAREG_ACTL_ADDRESS,i);
+ mem_writeb(data,IO_Read(VGAREG_ACTL_READ_DATA));
+ ResetACTL();
+ data++;
+ }
+ // Then the border
+ IO_Write(VGAREG_ACTL_ADDRESS,0x11+32);
+ mem_writeb(data,IO_Read(VGAREG_ACTL_READ_DATA));
+ ResetACTL();
+}
+
+void INT10_SetSingleDACRegister(Bit8u index,Bit8u red,Bit8u green,Bit8u blue) {
+ IO_Write(VGAREG_DAC_WRITE_ADDRESS,(Bit8u)index);
+ if ((real_readb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)&0x06)==0) {
+ IO_Write(VGAREG_DAC_DATA,red);
+ IO_Write(VGAREG_DAC_DATA,green);
+ IO_Write(VGAREG_DAC_DATA,blue);
+ } else {
+ /* calculate clamped intensity, taken from VGABIOS */
+ Bit32u i=(( 77*red + 151*green + 28*blue ) + 0x80) >> 8;
+ Bit8u ic=(i>0x3f) ? 0x3f : ((Bit8u)(i & 0xff));
+ IO_Write(VGAREG_DAC_DATA,ic);
+ IO_Write(VGAREG_DAC_DATA,ic);
+ IO_Write(VGAREG_DAC_DATA,ic);
+ }
+}
+
+void INT10_GetSingleDACRegister(Bit8u index,Bit8u * red,Bit8u * green,Bit8u * blue) {
+ IO_Write(VGAREG_DAC_READ_ADDRESS,index);
+ *red=IO_Read(VGAREG_DAC_DATA);
+ *green=IO_Read(VGAREG_DAC_DATA);
+ *blue=IO_Read(VGAREG_DAC_DATA);
+}
+
+void INT10_SetDACBlock(Bit16u index,Bit16u count,PhysPt data) {
+ IO_Write(VGAREG_DAC_WRITE_ADDRESS,(Bit8u)index);
+ if ((real_readb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)&0x06)==0) {
+ for (;count>0;count--) {
+ IO_Write(VGAREG_DAC_DATA,mem_readb(data++));
+ IO_Write(VGAREG_DAC_DATA,mem_readb(data++));
+ IO_Write(VGAREG_DAC_DATA,mem_readb(data++));
+ }
+ } else {
+ for (;count>0;count--) {
+ Bit8u red=mem_readb(data++);
+ Bit8u green=mem_readb(data++);
+ Bit8u blue=mem_readb(data++);
+
+ /* calculate clamped intensity, taken from VGABIOS */
+ Bit32u i=(( 77*red + 151*green + 28*blue ) + 0x80) >> 8;
+ Bit8u ic=(i>0x3f) ? 0x3f : ((Bit8u)(i & 0xff));
+ IO_Write(VGAREG_DAC_DATA,ic);
+ IO_Write(VGAREG_DAC_DATA,ic);
+ IO_Write(VGAREG_DAC_DATA,ic);
+ }
+ }
+}
+
+void INT10_GetDACBlock(Bit16u index,Bit16u count,PhysPt data) {
+ IO_Write(VGAREG_DAC_READ_ADDRESS,(Bit8u)index);
+ for (;count>0;count--) {
+ mem_writeb(data++,IO_Read(VGAREG_DAC_DATA));
+ mem_writeb(data++,IO_Read(VGAREG_DAC_DATA));
+ mem_writeb(data++,IO_Read(VGAREG_DAC_DATA));
+ }
+}
+
+void INT10_SelectDACPage(Bit8u function,Bit8u mode) {
+ ResetACTL();
+ IO_Write(VGAREG_ACTL_ADDRESS,0x10);
+ Bit8u old10=IO_Read(VGAREG_ACTL_READ_DATA);
+ if (!function) { //Select paging mode
+ if (mode) old10|=0x80;
+ else old10&=0x7f;
+ //IO_Write(VGAREG_ACTL_ADDRESS,0x10);
+ IO_Write(VGAREG_ACTL_WRITE_DATA,old10);
+ } else { //Select page
+ IO_Write(VGAREG_ACTL_WRITE_DATA,old10);
+ if (!(old10 & 0x80)) mode<<=2;
+ mode&=0xf;
+ IO_Write(VGAREG_ACTL_ADDRESS,0x14);
+ IO_Write(VGAREG_ACTL_WRITE_DATA,mode);
+ }
+ IO_Write(VGAREG_ACTL_ADDRESS,32); //Enable output and protect palette
+}
+
+void INT10_GetDACPage(Bit8u* mode,Bit8u* page) {
+ ResetACTL();
+ IO_Write(VGAREG_ACTL_ADDRESS,0x10);
+ Bit8u reg10=IO_Read(VGAREG_ACTL_READ_DATA);
+ IO_Write(VGAREG_ACTL_WRITE_DATA,reg10);
+ *mode=(reg10&0x80)?0x01:0x00;
+ IO_Write(VGAREG_ACTL_ADDRESS,0x14);
+ *page=IO_Read(VGAREG_ACTL_READ_DATA);
+ IO_Write(VGAREG_ACTL_WRITE_DATA,*page);
+ if(*mode) {
+ *page&=0xf;
+ } else {
+ *page&=0xc;
+ *page>>=2;
+ }
+}
+
+void INT10_SetPelMask(Bit8u mask) {
+ IO_Write(VGAREG_PEL_MASK,mask);
+}
+
+void INT10_GetPelMask(Bit8u & mask) {
+ mask=IO_Read(VGAREG_PEL_MASK);
+}
+
+void INT10_SetBackgroundBorder(Bit8u val) {
+ Bit8u color_select=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL);
+ color_select=(color_select & 0xe0) | (val & 0x1f);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,color_select);
+
+ switch (machine) {
+ case MCH_CGA:
+ // only write the color select register
+ IO_Write(0x3d9,color_select);
+ break;
+ case MCH_TANDY:
+ // TODO handle val == 0x1x, wait for retrace
+ switch(CurMode->mode) {
+ default: // modes 0-5: write to color select and border
+ INT10_SetOverscanBorderColor(val);
+ IO_Write(0x3d9, color_select);
+ break;
+ case 0x06: // 2-color: only write the color select register
+ IO_Write(0x3d9, color_select);
+ break;
+ case 0x07: // Tandy monochrome not implemented
+ break;
+ case 0x08:
+ case 0x09: // 16-color: write to color select, border and pal. index 0
+ INT10_SetOverscanBorderColor(val);
+ INT10_SetSinglePaletteRegister(0, val);
+ IO_Write(0x3d9, color_select);
+ break;
+ case 0x0a: // 4-color highres:
+ // write zero to color select, write palette to indexes 1-3
+ // TODO palette
+ IO_Write(0x3d9, 0);
+ break;
+ }
+ break;
+ case MCH_PCJR:
+ IO_Read(VGAREG_TDY_RESET); // reset the flipflop
+ if (vga.mode!=M_TANDY_TEXT) {
+ IO_Write(VGAREG_TDY_ADDRESS, 0x10);
+ IO_Write(VGAREG_PCJR_DATA, color_select&0xf);
+ }
+ IO_Write(VGAREG_TDY_ADDRESS, 0x2); // border color
+ IO_Write(VGAREG_PCJR_DATA, color_select&0xf);
+ break;
+ case EGAVGA_ARCH_CASE:
+ val = ((val << 1) & 0x10) | (val & 0x7);
+ /* Always set the overscan color */
+ INT10_SetSinglePaletteRegister( 0x11, val );
+ /* Don't set any extra colors when in text mode */
+ if (CurMode->mode <= 3)
+ return;
+ INT10_SetSinglePaletteRegister( 0, val );
+ val = (color_select & 0x10) | 2 | ((color_select & 0x20) >> 5);
+ INT10_SetSinglePaletteRegister( 1, val );
+ val+=2;
+ INT10_SetSinglePaletteRegister( 2, val );
+ val+=2;
+ INT10_SetSinglePaletteRegister( 3, val );
+ break;
+ }
+}
+
+void INT10_SetColorSelect(Bit8u val) {
+ Bit8u temp=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL);
+ temp=(temp & 0xdf) | ((val & 1) ? 0x20 : 0x0);
+ real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,temp);
+ if (machine == MCH_CGA || machine==MCH_TANDY)
+ IO_Write(0x3d9,temp);
+ else if (machine == MCH_PCJR) {
+ IO_Read(VGAREG_TDY_RESET); // reset the flipflop
+ switch(vga.mode) {
+ case M_TANDY2:
+ IO_Write(VGAREG_TDY_ADDRESS, 0x11);
+ IO_Write(VGAREG_PCJR_DATA, val&1? 0xf:0);
+ break;
+ case M_TANDY4:
+ for(Bit8u i = 0x11; i < 0x14; i++) {
+ const Bit8u t4_table[] = {0,2,4,6, 0,3,5,0xf};
+ IO_Write(VGAREG_TDY_ADDRESS, i);
+ IO_Write(VGAREG_PCJR_DATA, t4_table[(i-0x10)+(val&1? 4:0)]);
+ }
+ break;
+ default:
+ // 16-color modes: always write the same palette
+ for(Bit8u i = 0x11; i < 0x20; i++) {
+ IO_Write(VGAREG_TDY_ADDRESS, i);
+ IO_Write(VGAREG_PCJR_DATA, i-0x10);
+ }
+ break;
+ }
+ IO_Write(VGAREG_TDY_ADDRESS, 0); // enable palette
+ }
+ else if (IS_EGAVGA_ARCH) {
+ if (CurMode->mode <= 3) //Maybe even skip the total function!
+ return;
+ val = (temp & 0x10) | 2 | val;
+ INT10_SetSinglePaletteRegister( 1, val );
+ val+=2;
+ INT10_SetSinglePaletteRegister( 2, val );
+ val+=2;
+ INT10_SetSinglePaletteRegister( 3, val );
+ }
+}
+
+void INT10_PerformGrayScaleSumming(Bit16u start_reg,Bit16u count) {
+ if (count>0x100) count=0x100;
+ for (Bitu ct=0; ct<count; ct++) {
+ IO_Write(VGAREG_DAC_READ_ADDRESS,start_reg+ct);
+ Bit8u red=IO_Read(VGAREG_DAC_DATA);
+ Bit8u green=IO_Read(VGAREG_DAC_DATA);
+ Bit8u blue=IO_Read(VGAREG_DAC_DATA);
+
+ /* calculate clamped intensity, taken from VGABIOS */
+ Bit32u i=(( 77*red + 151*green + 28*blue ) + 0x80) >> 8;
+ Bit8u ic=(i>0x3f) ? 0x3f : ((Bit8u)(i & 0xff));
+ INT10_SetSingleDACRegister(start_reg+ct,ic,ic,ic);
+ }
+}
diff --git a/src/ints/int10_put_pixel.cpp b/src/ints/int10_put_pixel.cpp
new file mode 100644
index 000000000..1e052cd9d
--- /dev/null
+++ b/src/ints/int10_put_pixel.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "mem.h"
+#include "inout.h"
+#include "int10.h"
+
+static Bit8u cga_masks[4]={0x3f,0xcf,0xf3,0xfc};
+static Bit8u cga_masks2[8]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};
+
+void INT10_PutPixel(Bit16u x,Bit16u y,Bit8u page,Bit8u color) {
+ static bool putpixelwarned = false;
+
+ switch (CurMode->type) {
+ case M_CGA4:
+ {
+ if (real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE)<=5) {
+ // this is a 16k mode
+ Bit16u off=(y>>1)*80+(x>>2);
+ if (y&1) off+=8*1024;
+
+ Bit8u old=real_readb(0xb800,off);
+ if (color & 0x80) {
+ color&=3;
+ old^=color << (2*(3-(x&3)));
+ } else {
+ old=(old&cga_masks[x&3])|((color&3) << (2*(3-(x&3))));
+ }
+ real_writeb(0xb800,off,old);
+ } else {
+ // a 32k mode: PCJr special case (see M_TANDY16)
+ Bit16u seg;
+ if (machine==MCH_PCJR) {
+ Bitu cpupage =
+ (real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7;
+ seg = cpupage << 10; // A14-16 to addr bits 14-16
+ } else
+ seg = 0xb800;
+
+ Bit16u off=(y>>2)*160+((x>>2)&(~1));
+ off+=(8*1024) * (y & 3);
+
+ Bit16u old=real_readw(seg,off);
+ if (color & 0x80) {
+ old^=(color&1) << (7-(x&7));
+ old^=((color&2)>>1) << ((7-(x&7))+8);
+ } else {
+ old=(old&(~(0x101<<(7-(x&7))))) | ((color&1) << (7-(x&7))) | (((color&2)>>1) << ((7-(x&7))+8));
+ }
+ real_writew(seg,off,old);
+ }
+ }
+ break;
+ case M_CGA2:
+ {
+ Bit16u off=(y>>1)*80+(x>>3);
+ if (y&1) off+=8*1024;
+ Bit8u old=real_readb(0xb800,off);
+ if (color & 0x80) {
+ color&=1;
+ old^=color << ((7-(x&7)));
+ } else {
+ old=(old&cga_masks2[x&7])|((color&1) << ((7-(x&7))));
+ }
+ real_writeb(0xb800,off,old);
+ }
+ break;
+ case M_TANDY16:
+ {
+ // find out if we are in a 32k mode (0x9 or 0xa)
+ // This requires special handling on the PCJR
+ // because only 16k are mapped at 0xB800
+ bool is_32k = (real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_MODE) >= 9)?
+ true:false;
+
+ Bit16u segment, offset;
+ if (is_32k) {
+ if (machine==MCH_PCJR) {
+ Bitu cpupage =
+ (real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7;
+ segment = cpupage << 10; // A14-16 to addr bits 14-16
+ } else
+ segment = 0xb800;
+ // bits 1 and 0 of y select the bank
+ // two pixels per byte (thus x>>1)
+ offset = (y >> 2) * (CurMode->swidth >> 1) + (x>>1);
+ // select the scanline bank
+ offset += (8*1024) * (y & 3);
+ } else {
+ segment = 0xb800;
+ // bit 0 of y selects the bank
+ offset = (y >> 1) * (CurMode->swidth >> 1) + (x>>1);
+ offset += (8*1024) * (y & 1);
+ }
+
+ // update the pixel
+ Bit8u old=real_readb(segment, offset);
+ Bit8u p[2];
+ p[1] = (old >> 4) & 0xf;
+ p[0] = old & 0xf;
+ Bitu ind = 1-(x & 0x1);
+
+ if (color & 0x80) {
+ // color is to be XORed
+ p[ind]^=(color & 0x7f);
+ } else {
+ p[ind]=color;
+ }
+ old = (p[1] << 4) | p[0];
+ real_writeb(segment,offset, old);
+ }
+ break;
+ case M_LIN4:
+ if ((machine!=MCH_VGA) || (svgaCard!=SVGA_TsengET4K) ||
+ (CurMode->swidth>800)) {
+ // the ET4000 BIOS supports text output in 800x600 SVGA (Gateway 2)
+ // putpixel warining?
+ break;
+ }
+ case M_EGA:
+ {
+ /* Set the correct bitmask for the pixel position */
+ IO_Write(0x3ce,0x8);Bit8u mask=128>>(x&7);IO_Write(0x3cf,mask);
+ /* Set the color to set/reset register */
+ IO_Write(0x3ce,0x0);IO_Write(0x3cf,color);
+ /* Enable all the set/resets */
+ IO_Write(0x3ce,0x1);IO_Write(0x3cf,0xf);
+ /* test for xorring */
+ if (color & 0x80) { IO_Write(0x3ce,0x3);IO_Write(0x3cf,0x18); }
+ //Perhaps also set mode 1
+ /* Calculate where the pixel is in video memory */
+ if (CurMode->plength!=(Bitu)real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE))
+ LOG(LOG_INT10,LOG_ERROR)("PutPixel_EGA_p: %x!=%x",CurMode->plength,real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE));
+ if (CurMode->swidth!=(Bitu)real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8)
+ LOG(LOG_INT10,LOG_ERROR)("PutPixel_EGA_w: %x!=%x",CurMode->swidth,real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
+ PhysPt off=0xa0000+real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE)*page+
+ ((y*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8+x)>>3);
+ /* Bitmask and set/reset should do the rest */
+ mem_readb(off);
+ mem_writeb(off,0xff);
+ /* Restore bitmask */
+ IO_Write(0x3ce,0x8);IO_Write(0x3cf,0xff);
+ IO_Write(0x3ce,0x1);IO_Write(0x3cf,0);
+ /* Restore write operating if changed */
+ if (color & 0x80) { IO_Write(0x3ce,0x3);IO_Write(0x3cf,0x0); }
+ break;
+ }
+
+ case M_VGA:
+ mem_writeb(PhysMake(0xa000,y*320+x),color);
+ break;
+ case M_LIN8: {
+ if (CurMode->swidth!=(Bitu)real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8)
+ LOG(LOG_INT10,LOG_ERROR)("PutPixel_VGA_w: %x!=%x",CurMode->swidth,real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
+ PhysPt off=S3_LFB_BASE+y*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8+x;
+ mem_writeb(off,color);
+ break;
+ }
+ default:
+ if(GCC_UNLIKELY(!putpixelwarned)) {
+ putpixelwarned = true;
+ LOG(LOG_INT10,LOG_ERROR)("PutPixel unhandled mode type %d",CurMode->type);
+ }
+ break;
+ }
+}
+
+void INT10_GetPixel(Bit16u x,Bit16u y,Bit8u page,Bit8u * color) {
+ switch (CurMode->type) {
+ case M_CGA4:
+ {
+ Bit16u off=(y>>1)*80+(x>>2);
+ if (y&1) off+=8*1024;
+ Bit8u val=real_readb(0xb800,off);
+ *color=(val>>(((3-(x&3)))*2)) & 3 ;
+ }
+ break;
+ case M_CGA2:
+ {
+ Bit16u off=(y>>1)*80+(x>>3);
+ if (y&1) off+=8*1024;
+ Bit8u val=real_readb(0xb800,off);
+ *color=(val>>(((7-(x&7))))) & 1 ;
+ }
+ break;
+ case M_TANDY16:
+ {
+ bool is_32k = (real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_MODE) >= 9)?true:false;
+ Bit16u segment, offset;
+ if (is_32k) {
+ if (machine==MCH_PCJR) {
+ Bitu cpupage = (real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7;
+ segment = cpupage << 10;
+ } else segment = 0xb800;
+ offset = (y >> 2) * (CurMode->swidth >> 1) + (x>>1);
+ offset += (8*1024) * (y & 3);
+ } else {
+ segment = 0xb800;
+ offset = (y >> 1) * (CurMode->swidth >> 1) + (x>>1);
+ offset += (8*1024) * (y & 1);
+ }
+ Bit8u val=real_readb(segment,offset);
+ *color=(val>>((x&1)?0:4)) & 0xf;
+ }
+ break;
+ case M_EGA:
+ {
+ /* Calculate where the pixel is in video memory */
+ if (CurMode->plength!=(Bitu)real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE))
+ LOG(LOG_INT10,LOG_ERROR)("GetPixel_EGA_p: %x!=%x",CurMode->plength,real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE));
+ if (CurMode->swidth!=(Bitu)real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8)
+ LOG(LOG_INT10,LOG_ERROR)("GetPixel_EGA_w: %x!=%x",CurMode->swidth,real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
+ PhysPt off=0xa0000+real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE)*page+
+ ((y*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8+x)>>3);
+ Bitu shift=7-(x & 7);
+ /* Set the read map */
+ *color=0;
+ IO_Write(0x3ce,0x4);IO_Write(0x3cf,0);
+ *color|=((mem_readb(off)>>shift) & 1) << 0;
+ IO_Write(0x3ce,0x4);IO_Write(0x3cf,1);
+ *color|=((mem_readb(off)>>shift) & 1) << 1;
+ IO_Write(0x3ce,0x4);IO_Write(0x3cf,2);
+ *color|=((mem_readb(off)>>shift) & 1) << 2;
+ IO_Write(0x3ce,0x4);IO_Write(0x3cf,3);
+ *color|=((mem_readb(off)>>shift) & 1) << 3;
+ break;
+ }
+ case M_VGA:
+ *color=mem_readb(PhysMake(0xa000,320*y+x));
+ break;
+ case M_LIN8: {
+ if (CurMode->swidth!=(Bitu)real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8)
+ LOG(LOG_INT10,LOG_ERROR)("GetPixel_VGA_w: %x!=%x",CurMode->swidth,real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
+ PhysPt off=S3_LFB_BASE+y*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8+x;
+ *color = mem_readb(off);
+ break;
+ }
+ default:
+ LOG(LOG_INT10,LOG_ERROR)("GetPixel unhandled mode type %d",CurMode->type);
+ break;
+ }
+}
+
diff --git a/src/ints/int10_vesa.cpp b/src/ints/int10_vesa.cpp
new file mode 100644
index 000000000..6e3801b7f
--- /dev/null
+++ b/src/ints/int10_vesa.cpp
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <stddef.h>
+
+#include "dosbox.h"
+#include "callback.h"
+#include "regs.h"
+#include "mem.h"
+#include "inout.h"
+#include "int10.h"
+#include "dos_inc.h"
+
+#define VESA_SUCCESS 0x00
+#define VESA_FAIL 0x01
+#define VESA_HW_UNSUPPORTED 0x02
+#define VESA_MODE_UNSUPPORTED 0x03
+// internal definition to pass to the caller
+#define VESA_UNIMPLEMENTED 0xFF
+
+static struct {
+ Bitu rmWindow;
+ Bitu pmStart;
+ Bitu pmWindow;
+ Bitu pmPalette;
+} callback;
+
+static char string_oem[]="S3 Incorporated. Trio64";
+static char string_vendorname[]="DOSBox Development Team";
+static char string_productname[]="DOSBox - The DOS Emulator";
+static char string_productrev[]="DOSBox " VERSION;
+
+#ifdef _MSC_VER
+#pragma pack (1)
+#endif
+struct MODE_INFO{
+ Bit16u ModeAttributes;
+ Bit8u WinAAttributes;
+ Bit8u WinBAttributes;
+ Bit16u WinGranularity;
+ Bit16u WinSize;
+ Bit16u WinASegment;
+ Bit16u WinBSegment;
+ Bit32u WinFuncPtr;
+ Bit16u BytesPerScanLine;
+ Bit16u XResolution;
+ Bit16u YResolution;
+ Bit8u XCharSize;
+ Bit8u YCharSize;
+ Bit8u NumberOfPlanes;
+ Bit8u BitsPerPixel;
+ Bit8u NumberOfBanks;
+ Bit8u MemoryModel;
+ Bit8u BankSize;
+ Bit8u NumberOfImagePages;
+ Bit8u Reserved_page;
+ Bit8u RedMaskSize;
+ Bit8u RedMaskPos;
+ Bit8u GreenMaskSize;
+ Bit8u GreenMaskPos;
+ Bit8u BlueMaskSize;
+ Bit8u BlueMaskPos;
+ Bit8u ReservedMaskSize;
+ Bit8u ReservedMaskPos;
+ Bit8u DirectColorModeInfo;
+ Bit32u PhysBasePtr;
+ Bit32u OffScreenMemOffset;
+ Bit16u OffScreenMemSize;
+ Bit8u Reserved[206];
+} GCC_ATTRIBUTE(packed);
+#ifdef _MSC_VER
+#pragma pack()
+#endif
+
+
+
+Bit8u VESA_GetSVGAInformation(Bit16u seg,Bit16u off) {
+ /* Fill 256 byte buffer with VESA information */
+ PhysPt buffer=PhysMake(seg,off);
+ Bitu i;
+ bool vbe2=false;Bit16u vbe2_pos=256+off;
+ Bitu id=mem_readd(buffer);
+ if (((id==0x56424532)||(id==0x32454256)) && (!int10.vesa_oldvbe)) vbe2=true;
+ if (vbe2) {
+ for (i=0;i<0x200;i++) mem_writeb(buffer+i,0);
+ } else {
+ for (i=0;i<0x100;i++) mem_writeb(buffer+i,0);
+ }
+ /* Fill common data */
+ MEM_BlockWrite(buffer,(void *)"VESA",4); //Identification
+ if (!int10.vesa_oldvbe) mem_writew(buffer+0x04,0x200); //Vesa version 2.0
+ else mem_writew(buffer+0x04,0x102); //Vesa version 1.2
+ if (vbe2) {
+ mem_writed(buffer+0x06,RealMake(seg,vbe2_pos));
+ for (i=0;i<sizeof(string_oem);i++) real_writeb(seg,vbe2_pos++,string_oem[i]);
+ mem_writew(buffer+0x14,0x200); //VBE 2 software revision
+ mem_writed(buffer+0x16,RealMake(seg,vbe2_pos));
+ for (i=0;i<sizeof(string_vendorname);i++) real_writeb(seg,vbe2_pos++,string_vendorname[i]);
+ mem_writed(buffer+0x1a,RealMake(seg,vbe2_pos));
+ for (i=0;i<sizeof(string_productname);i++) real_writeb(seg,vbe2_pos++,string_productname[i]);
+ mem_writed(buffer+0x1e,RealMake(seg,vbe2_pos));
+ for (i=0;i<sizeof(string_productrev);i++) real_writeb(seg,vbe2_pos++,string_productrev[i]);
+ } else {
+ mem_writed(buffer+0x06,int10.rom.oemstring); //Oemstring
+ }
+ mem_writed(buffer+0x0a,0x0); //Capabilities and flags
+ mem_writed(buffer+0x0e,int10.rom.vesa_modes); //VESA Mode list
+ mem_writew(buffer+0x12,(Bit16u)(vga.vmemsize/(64*1024))); // memory size in 64kb blocks
+ return VESA_SUCCESS;
+}
+
+Bit8u VESA_GetSVGAModeInformation(Bit16u mode,Bit16u seg,Bit16u off) {
+ MODE_INFO minfo;
+ memset(&minfo,0,sizeof(minfo));
+ PhysPt buf=PhysMake(seg,off);
+ Bitu pageSize;
+ Bit8u modeAttributes;
+ Bitu i=0;
+
+ mode&=0x3fff; // vbe2 compatible, ignore lfb and keep screen content bits
+ if (mode<0x100) return 0x01;
+ if (svga.accepts_mode) {
+ if (!svga.accepts_mode(mode)) return 0x01;
+ }
+ while (ModeList_VGA[i].mode!=0xffff) {
+ if (mode==ModeList_VGA[i].mode) goto foundit; else i++;
+ }
+ return VESA_FAIL;
+foundit:
+ if ((int10.vesa_oldvbe) && (ModeList_VGA[i].mode>=0x120)) return 0x01;
+ VideoModeBlock * mblock=&ModeList_VGA[i];
+ switch (mblock->type) {
+ case M_LIN4:
+ pageSize = mblock->sheight * mblock->swidth/2;
+ var_write(&minfo.BytesPerScanLine,mblock->swidth/8);
+ var_write(&minfo.NumberOfPlanes,0x4);
+ var_write(&minfo.BitsPerPixel,4);
+ var_write(&minfo.MemoryModel,3); //ega planar mode
+ modeAttributes = 0x1b; // Color, graphics, no linear buffer
+ break;
+ case M_LIN8:
+ pageSize = mblock->sheight * mblock->swidth;
+ var_write(&minfo.BytesPerScanLine,mblock->swidth);
+ var_write(&minfo.NumberOfPlanes,0x1);
+ var_write(&minfo.BitsPerPixel,8);
+ var_write(&minfo.MemoryModel,4); //packed pixel
+ modeAttributes = 0x1b; // Color, graphics
+ if (!int10.vesa_nolfb) modeAttributes |= 0x80; // linear framebuffer
+ break;
+ case M_LIN15:
+ pageSize = mblock->sheight * mblock->swidth*2;
+ var_write(&minfo.BytesPerScanLine,mblock->swidth*2);
+ var_write(&minfo.NumberOfPlanes,0x1);
+ var_write(&minfo.BitsPerPixel,15);
+ var_write(&minfo.MemoryModel,6); //HiColour
+ var_write(&minfo.RedMaskSize,5);
+ var_write(&minfo.RedMaskPos,10);
+ var_write(&minfo.GreenMaskSize,5);
+ var_write(&minfo.GreenMaskPos,5);
+ var_write(&minfo.BlueMaskSize,5);
+ var_write(&minfo.BlueMaskPos,0);
+ var_write(&minfo.ReservedMaskSize,0x01);
+ var_write(&minfo.ReservedMaskPos,0x0f);
+ modeAttributes = 0x1b; // Color, graphics
+ if (!int10.vesa_nolfb) modeAttributes |= 0x80; // linear framebuffer
+ break;
+ case M_LIN16:
+ pageSize = mblock->sheight * mblock->swidth*2;
+ var_write(&minfo.BytesPerScanLine,mblock->swidth*2);
+ var_write(&minfo.NumberOfPlanes,0x1);
+ var_write(&minfo.BitsPerPixel,16);
+ var_write(&minfo.MemoryModel,6); //HiColour
+ var_write(&minfo.RedMaskSize,5);
+ var_write(&minfo.RedMaskPos,11);
+ var_write(&minfo.GreenMaskSize,6);
+ var_write(&minfo.GreenMaskPos,5);
+ var_write(&minfo.BlueMaskSize,5);
+ var_write(&minfo.BlueMaskPos,0);
+ modeAttributes = 0x1b; // Color, graphics
+ if (!int10.vesa_nolfb) modeAttributes |= 0x80; // linear framebuffer
+ break;
+ case M_LIN32:
+ pageSize = mblock->sheight * mblock->swidth*4;
+ var_write(&minfo.BytesPerScanLine,mblock->swidth*4);
+ var_write(&minfo.NumberOfPlanes,0x1);
+ var_write(&minfo.BitsPerPixel,32);
+ var_write(&minfo.MemoryModel,6); //HiColour
+ var_write(&minfo.RedMaskSize,8);
+ var_write(&minfo.RedMaskPos,0x10);
+ var_write(&minfo.GreenMaskSize,0x8);
+ var_write(&minfo.GreenMaskPos,0x8);
+ var_write(&minfo.BlueMaskSize,0x8);
+ var_write(&minfo.BlueMaskPos,0x0);
+ var_write(&minfo.ReservedMaskSize,0x8);
+ var_write(&minfo.ReservedMaskPos,0x18);
+ modeAttributes = 0x1b; // Color, graphics
+ if (!int10.vesa_nolfb) modeAttributes |= 0x80; // linear framebuffer
+ break;
+ case M_TEXT:
+ pageSize = 0;
+ var_write(&minfo.BytesPerScanLine, mblock->twidth * 2);
+ var_write(&minfo.NumberOfPlanes,0x4);
+ var_write(&minfo.BitsPerPixel,4);
+ var_write(&minfo.MemoryModel,0); // text
+ modeAttributes = 0x0f; //Color, text, bios output
+ break;
+ default:
+ return VESA_FAIL;
+ }
+ if (pageSize & 0xFFFF) {
+ // It is documented that many applications assume 64k-aligned page sizes
+ // VBETEST is one of them
+ pageSize += 0x10000;
+ pageSize &= ~0xFFFF;
+ }
+ Bitu pages = 0;
+ if (pageSize > vga.vmemsize) {
+ // mode not supported by current hardware configuration
+ modeAttributes &= ~0x1;
+ } else if (pageSize) {
+ pages = (vga.vmemsize / pageSize)-1;
+ }
+ var_write(&minfo.NumberOfImagePages, pages);
+ var_write(&minfo.ModeAttributes, modeAttributes);
+ var_write(&minfo.WinAAttributes, 0x7); // Exists/readable/writable
+
+ if (mblock->type==M_TEXT) {
+ var_write(&minfo.WinGranularity,32);
+ var_write(&minfo.WinSize,32);
+ var_write(&minfo.WinASegment,0xb800);
+ var_write(&minfo.XResolution,mblock->twidth);
+ var_write(&minfo.YResolution,mblock->theight);
+ } else {
+ var_write(&minfo.WinGranularity,64);
+ var_write(&minfo.WinSize,64);
+ var_write(&minfo.WinASegment,0xa000);
+ var_write(&minfo.XResolution,mblock->swidth);
+ var_write(&minfo.YResolution,mblock->sheight);
+ }
+ var_write(&minfo.WinFuncPtr,int10.rom.set_window);
+ var_write(&minfo.NumberOfBanks,0x1);
+ var_write(&minfo.Reserved_page,0x1);
+ var_write(&minfo.XCharSize,mblock->cwidth);
+ var_write(&minfo.YCharSize,mblock->cheight);
+ if (!int10.vesa_nolfb) var_write(&minfo.PhysBasePtr,S3_LFB_BASE);
+
+ MEM_BlockWrite(buf,&minfo,sizeof(MODE_INFO));
+ return VESA_SUCCESS;
+}
+
+
+Bit8u VESA_SetSVGAMode(Bit16u mode) {
+ if (INT10_SetVideoMode(mode)) {
+ int10.vesa_setmode=mode&0x7fff;
+ return VESA_SUCCESS;
+ }
+ return VESA_FAIL;
+}
+
+Bit8u VESA_GetSVGAMode(Bit16u & mode) {
+ if (int10.vesa_setmode!=0xffff) mode=int10.vesa_setmode;
+ else mode=CurMode->mode;
+ return VESA_SUCCESS;
+}
+
+Bit8u VESA_SetCPUWindow(Bit8u window,Bit8u address) {
+ if (window) return VESA_FAIL;
+ if (((Bit32u)(address)*64*1024<vga.vmemsize)) {
+ IO_Write(0x3d4,0x6a);
+ IO_Write(0x3d5,(Bit8u)address);
+ return VESA_SUCCESS;
+ } else return VESA_FAIL;
+}
+
+Bit8u VESA_GetCPUWindow(Bit8u window,Bit16u & address) {
+ if (window) return VESA_FAIL;
+ IO_Write(0x3d4,0x6a);
+ address=IO_Read(0x3d5);
+ return VESA_SUCCESS;
+}
+
+
+Bit8u VESA_SetPalette(PhysPt data,Bitu index,Bitu count,bool wait) {
+//Structure is (vesa 3.0 doc): blue,green,red,alignment
+ Bit8u r,g,b;
+ if (index>255) return VESA_FAIL;
+ if (index+count>256) return VESA_FAIL;
+
+ // Wait for retrace if requested
+ if (wait) CALLBACK_RunRealFar(RealSeg(int10.rom.wait_retrace),RealOff(int10.rom.wait_retrace));
+
+ IO_Write(0x3c8,(Bit8u)index);
+ while (count) {
+ b = mem_readb(data++);
+ g = mem_readb(data++);
+ r = mem_readb(data++);
+ data++;
+ IO_Write(0x3c9,r);
+ IO_Write(0x3c9,g);
+ IO_Write(0x3c9,b);
+ count--;
+ }
+ return VESA_SUCCESS;
+}
+
+
+Bit8u VESA_GetPalette(PhysPt data,Bitu index,Bitu count) {
+ Bit8u r,g,b;
+ if (index>255) return VESA_FAIL;
+ if (index+count>256) return VESA_FAIL;
+ IO_Write(0x3c7,(Bit8u)index);
+ while (count) {
+ r = IO_Read(0x3c9);
+ g = IO_Read(0x3c9);
+ b = IO_Read(0x3c9);
+ mem_writeb(data++,b);
+ mem_writeb(data++,g);
+ mem_writeb(data++,r);
+ data++;
+ count--;
+ }
+ return VESA_SUCCESS;
+}
+
+// maximum offset for the S3 Trio64 is 10 bits
+#define S3_MAX_OFFSET 0x3ff
+
+Bit8u VESA_ScanLineLength(Bit8u subcall,Bit16u val, Bit16u & bytes,Bit16u & pixels,Bit16u & lines) {
+ // offset register: virtual scanline length
+ Bitu pixels_per_offset;
+ Bitu bytes_per_offset = 8;
+ Bitu vmemsize = vga.vmemsize;
+ Bitu new_offset = vga.config.scan_len;
+ Bitu screen_height = CurMode->sheight;
+
+ switch (CurMode->type) {
+ case M_TEXT:
+ vmemsize = 0x8000; // we have only the 32kB window here
+ screen_height = CurMode->theight;
+ pixels_per_offset = 16; // two characters each 8 pixels wide
+ bytes_per_offset = 4; // 2 characters + 2 attributes
+ break;
+ case M_LIN4:
+ pixels_per_offset = 16;
+ break;
+ case M_LIN8:
+ pixels_per_offset = 8;
+ break;
+ case M_LIN15:
+ case M_LIN16:
+ pixels_per_offset = 4;
+ break;
+ case M_LIN32:
+ pixels_per_offset = 2;
+ break;
+ default:
+ return VESA_MODE_UNSUPPORTED;
+ }
+ switch (subcall) {
+ case 0x00: // set scan length in pixels
+ new_offset = val / pixels_per_offset;
+ if (val % pixels_per_offset) new_offset++;
+
+ if (new_offset > S3_MAX_OFFSET)
+ return VESA_HW_UNSUPPORTED; // scanline too long
+ vga.config.scan_len = new_offset;
+ VGA_CheckScanLength();
+ break;
+
+ case 0x01: // get current scanline length
+ // implemented at the end of this function
+ break;
+
+ case 0x02: // set scan length in bytes
+ new_offset = val / bytes_per_offset;
+ if (val % bytes_per_offset) new_offset++;
+
+ if (new_offset > S3_MAX_OFFSET)
+ return VESA_HW_UNSUPPORTED; // scanline too long
+ vga.config.scan_len = new_offset;
+ VGA_CheckScanLength();
+ break;
+
+ case 0x03: // get maximum scan line length
+ // the smaller of either the hardware maximum scanline length or
+ // the limit to get full y resolution of this mode
+ new_offset = S3_MAX_OFFSET;
+ if ((new_offset * bytes_per_offset * screen_height) > vmemsize)
+ new_offset = vmemsize / (bytes_per_offset * screen_height);
+ break;
+
+ default:
+ return VESA_UNIMPLEMENTED;
+ }
+
+ // set up the return values
+ bytes = (Bit16u)(new_offset * bytes_per_offset);
+ pixels = (Bit16u)(new_offset * pixels_per_offset);
+ if (!bytes)
+ // return failure on division by zero
+ // some real VESA BIOS implementations may crash here
+ return VESA_FAIL;
+
+ lines = (Bit16u)(vmemsize / bytes);
+
+ if (CurMode->type==M_TEXT)
+ lines *= CurMode->cheight;
+
+ return VESA_SUCCESS;
+}
+
+Bit8u VESA_SetDisplayStart(Bit16u x,Bit16u y,bool wait) {
+ Bitu pixels_per_offset;
+ Bitu panning_factor = 1;
+
+ switch (CurMode->type) {
+ case M_TEXT:
+ case M_LIN4:
+ pixels_per_offset = 16;
+ break;
+ case M_LIN8:
+ panning_factor = 2; // the panning register ignores bit0 in this mode
+ pixels_per_offset = 8;
+ break;
+ case M_LIN15:
+ case M_LIN16:
+ panning_factor = 2; // this may be DOSBox specific
+ pixels_per_offset = 4;
+ break;
+ case M_LIN32:
+ pixels_per_offset = 2;
+ break;
+ default:
+ return VESA_MODE_UNSUPPORTED;
+ }
+ // We would have to divide y by the character height for text modes and
+ // write the remainder to the CRTC preset row scan register,
+ // but VBE2 BIOSes that actually get that far also don't.
+ // Only a VBE3 BIOS got it right.
+ Bitu virtual_screen_width = vga.config.scan_len * pixels_per_offset;
+ Bitu new_start_pixel = virtual_screen_width * y + x;
+ Bitu new_crtc_start = new_start_pixel / (pixels_per_offset/2);
+ Bitu new_panning = new_start_pixel % (pixels_per_offset/2);
+ new_panning *= panning_factor;
+
+ vga.config.display_start = new_crtc_start;
+
+ // Setting the panning register is nice as it allows for super smooth
+ // scrolling, but if we hit the retrace pulse there may be flicker as
+ // panning and display start are latched at different times.
+
+ IO_Read(0x3da); // reset attribute flipflop
+ IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on
+ IO_Write(0x3c0,new_panning);
+
+ // Wait for retrace if requested
+ if (wait) CALLBACK_RunRealFar(RealSeg(int10.rom.wait_retrace),RealOff(int10.rom.wait_retrace));
+
+ return VESA_SUCCESS;
+}
+
+Bit8u VESA_GetDisplayStart(Bit16u & x,Bit16u & y) {
+ Bitu pixels_per_offset;
+ Bitu panning_factor = 1;
+
+ switch (CurMode->type) {
+ case M_TEXT:
+ pixels_per_offset = 16;
+ break;
+ case M_LIN4:
+ pixels_per_offset = 16;
+ break;
+ case M_LIN8:
+ panning_factor = 2;
+ pixels_per_offset = 8;
+ break;
+ case M_LIN15:
+ case M_LIN16:
+ panning_factor = 2;
+ pixels_per_offset = 4;
+ break;
+ case M_LIN32:
+ pixels_per_offset = 2;
+ break;
+ default:
+ return VESA_MODE_UNSUPPORTED;
+ }
+
+ IO_Read(0x3da); // reset attribute flipflop
+ IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on
+ Bit8u panning = IO_Read(0x3c1);
+
+ Bitu virtual_screen_width = vga.config.scan_len * pixels_per_offset;
+ Bitu start_pixel = vga.config.display_start * (pixels_per_offset/2)
+ + panning / panning_factor;
+
+ y = start_pixel / virtual_screen_width;
+ x = start_pixel % virtual_screen_width;
+ return VESA_SUCCESS;
+}
+
+static Bitu VESA_SetWindow(void) {
+ if (reg_bh) reg_ah=VESA_GetCPUWindow(reg_bl,reg_dx);
+ else reg_ah=VESA_SetCPUWindow(reg_bl,(Bit8u)reg_dx);
+ reg_al=0x4f;
+ return CBRET_NONE;
+}
+
+static Bitu VESA_PMSetWindow(void) {
+ IO_Write(0x3d4,0x6a);
+ IO_Write(0x3d5,reg_dl);
+ return CBRET_NONE;
+}
+static Bitu VESA_PMSetPalette(void) {
+ PhysPt data=SegPhys(es)+reg_edi;
+ Bit32u count=reg_cx;
+ IO_Write(0x3c8,reg_dl);
+ do {
+ IO_Write(0x3c9,mem_readb(data+2));
+ IO_Write(0x3c9,mem_readb(data+1));
+ IO_Write(0x3c9,mem_readb(data));
+ data+=4;
+ } while (--count);
+ return CBRET_NONE;
+}
+static Bitu VESA_PMSetStart(void) {
+ Bit32u start = (reg_dx << 16) | reg_cx;
+ vga.config.display_start = start;
+ return CBRET_NONE;
+}
+
+
+
+
+void INT10_SetupVESA(void) {
+ /* Put the mode list somewhere in memory */
+ Bitu i;
+ i=0;
+ int10.rom.vesa_modes=RealMake(0xc000,int10.rom.used);
+//TODO Maybe add normal vga modes too, but only seems to complicate things
+ while (ModeList_VGA[i].mode!=0xffff) {
+ bool canuse_mode=false;
+ if (!svga.accepts_mode) canuse_mode=true;
+ else {
+ if (svga.accepts_mode(ModeList_VGA[i].mode)) canuse_mode=true;
+ }
+ if (ModeList_VGA[i].mode>=0x100 && canuse_mode) {
+ if ((!int10.vesa_oldvbe) || (ModeList_VGA[i].mode<0x120)) {
+ phys_writew(PhysMake(0xc000,int10.rom.used),ModeList_VGA[i].mode);
+ int10.rom.used+=2;
+ }
+ }
+ i++;
+ }
+ phys_writew(PhysMake(0xc000,int10.rom.used),0xffff);
+ int10.rom.used+=2;
+ int10.rom.oemstring=RealMake(0xc000,int10.rom.used);
+ Bitu len=(Bitu)(strlen(string_oem)+1);
+ for (i=0;i<len;i++) {
+ phys_writeb(0xc0000+int10.rom.used++,string_oem[i]);
+ }
+ switch (svgaCard) {
+ case SVGA_S3Trio:
+ break;
+ }
+ /* Prepare the real mode interface */
+ int10.rom.wait_retrace=RealMake(0xc000,int10.rom.used);
+ int10.rom.used += (Bit16u)CALLBACK_Setup(0, NULL, CB_VESA_WAIT, PhysMake(0xc000,int10.rom.used), "");
+ callback.rmWindow=CALLBACK_Allocate();
+ int10.rom.set_window=RealMake(0xc000,int10.rom.used);
+ int10.rom.used += (Bit16u)CALLBACK_Setup(callback.rmWindow, VESA_SetWindow, CB_RETF, PhysMake(0xc000,int10.rom.used), "VESA Real Set Window");
+ /* Prepare the pmode interface */
+ int10.rom.pmode_interface=RealMake(0xc000,int10.rom.used);
+ int10.rom.used += 8; //Skip the byte later used for offsets
+ /* PM Set Window call */
+ int10.rom.pmode_interface_window = int10.rom.used - RealOff( int10.rom.pmode_interface );
+ phys_writew( Real2Phys(int10.rom.pmode_interface) + 0, int10.rom.pmode_interface_window );
+ callback.pmWindow=CALLBACK_Allocate();
+ int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmWindow, VESA_PMSetWindow, CB_RETN, PhysMake(0xc000,int10.rom.used), "VESA PM Set Window");
+ /* PM Set start call */
+ int10.rom.pmode_interface_start = int10.rom.used - RealOff( int10.rom.pmode_interface );
+ phys_writew( Real2Phys(int10.rom.pmode_interface) + 2, int10.rom.pmode_interface_start);
+ callback.pmStart=CALLBACK_Allocate();
+ int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmStart, VESA_PMSetStart, CB_VESA_PM, PhysMake(0xc000,int10.rom.used), "VESA PM Set Start");
+ /* PM Set Palette call */
+ int10.rom.pmode_interface_palette = int10.rom.used - RealOff( int10.rom.pmode_interface );
+ phys_writew( Real2Phys(int10.rom.pmode_interface) + 4, int10.rom.pmode_interface_palette);
+ callback.pmPalette=CALLBACK_Allocate();
+ int10.rom.used += (Bit16u)CALLBACK_Setup(0, NULL, CB_VESA_PM, PhysMake(0xc000,int10.rom.used), "");
+ int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmPalette, VESA_PMSetPalette, CB_RETN, PhysMake(0xc000,int10.rom.used), "VESA PM Set Palette");
+ /* Finalize the size and clear the required ports pointer */
+ phys_writew( Real2Phys(int10.rom.pmode_interface) + 6, 0);
+ int10.rom.pmode_interface_size=int10.rom.used - RealOff( int10.rom.pmode_interface );
+}
+
diff --git a/src/ints/int10_video_state.cpp b/src/ints/int10_video_state.cpp
new file mode 100644
index 000000000..0dbc34e60
--- /dev/null
+++ b/src/ints/int10_video_state.cpp
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "mem.h"
+#include "inout.h"
+#include "int10.h"
+
+Bitu INT10_VideoState_GetSize(Bitu state) {
+ // state: bit0=hardware, bit1=bios data, bit2=color regs/dac state
+ if ((state&7)==0) return 0;
+
+ Bitu size=0x20;
+ if (state&1) size+=0x46;
+ if (state&2) size+=0x3a;
+ if (state&4) size+=0x303;
+ if ((svgaCard==SVGA_S3Trio) && (state&8)) size+=0x43;
+ if (size!=0) size=(size-1)/64+1;
+ return size;
+}
+
+bool INT10_VideoState_Save(Bitu state,RealPt buffer) {
+ Bitu ct;
+ if ((state&7)==0) return false;
+
+ Bitu base_seg=RealSeg(buffer);
+ Bitu base_dest=RealOff(buffer)+0x20;
+
+ if (state&1) {
+ real_writew(base_seg,RealOff(buffer),base_dest);
+
+ Bit16u crt_reg=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ real_writew(base_seg,base_dest+0x40,crt_reg);
+
+ real_writeb(base_seg,base_dest+0x00,IO_ReadB(0x3c4));
+ real_writeb(base_seg,base_dest+0x01,IO_ReadB(0x3d4));
+ real_writeb(base_seg,base_dest+0x02,IO_ReadB(0x3ce));
+ IO_ReadB(crt_reg+6);
+ real_writeb(base_seg,base_dest+0x03,IO_ReadB(0x3c0));
+ real_writeb(base_seg,base_dest+0x04,IO_ReadB(0x3ca));
+
+ // sequencer
+ for (ct=1; ct<5; ct++) {
+ IO_WriteB(0x3c4,ct);
+ real_writeb(base_seg,base_dest+0x04+ct,IO_ReadB(0x3c5));
+ }
+
+ real_writeb(base_seg,base_dest+0x09,IO_ReadB(0x3cc));
+
+ // crt controller
+ for (ct=0; ct<0x19; ct++) {
+ IO_WriteB(crt_reg,ct);
+ real_writeb(base_seg,base_dest+0x0a+ct,IO_ReadB(crt_reg+1));
+ }
+
+ // attr registers
+ for (ct=0; ct<4; ct++) {
+ IO_ReadB(crt_reg+6);
+ IO_WriteB(0x3c0,0x10+ct);
+ real_writeb(base_seg,base_dest+0x33+ct,IO_ReadB(0x3c1));
+ }
+
+ // graphics registers
+ for (ct=0; ct<9; ct++) {
+ IO_WriteB(0x3ce,ct);
+ real_writeb(base_seg,base_dest+0x37+ct,IO_ReadB(0x3cf));
+ }
+
+ // save some registers
+ IO_WriteB(0x3c4,2);
+ Bit8u crtc_2=IO_ReadB(0x3c5);
+ IO_WriteB(0x3c4,4);
+ Bit8u crtc_4=IO_ReadB(0x3c5);
+ IO_WriteB(0x3ce,6);
+ Bit8u gfx_6=IO_ReadB(0x3cf);
+ IO_WriteB(0x3ce,5);
+ Bit8u gfx_5=IO_ReadB(0x3cf);
+ IO_WriteB(0x3ce,4);
+ Bit8u gfx_4=IO_ReadB(0x3cf);
+
+ // reprogram for full access to plane latches
+ IO_WriteW(0x3c4,0x0f02);
+ IO_WriteW(0x3c4,0x0704);
+ IO_WriteW(0x3ce,0x0406);
+ IO_WriteW(0x3ce,0x0105);
+ mem_writeb(0xaffff,0);
+
+ for (ct=0; ct<4; ct++) {
+ IO_WriteW(0x3ce,0x0004+ct*0x100);
+ real_writeb(base_seg,base_dest+0x42+ct,mem_readb(0xaffff));
+ }
+
+ // restore registers
+ IO_WriteW(0x3ce,0x0004|(gfx_4<<8));
+ IO_WriteW(0x3ce,0x0005|(gfx_5<<8));
+ IO_WriteW(0x3ce,0x0006|(gfx_6<<8));
+ IO_WriteW(0x3c4,0x0004|(crtc_4<<8));
+ IO_WriteW(0x3c4,0x0002|(crtc_2<<8));
+
+ for (ct=0; ct<0x10; ct++) {
+ IO_ReadB(crt_reg+6);
+ IO_WriteB(0x3c0,ct);
+ real_writeb(base_seg,base_dest+0x23+ct,IO_ReadB(0x3c1));
+ }
+ IO_WriteB(0x3c0,0x20);
+
+ base_dest+=0x46;
+ }
+
+ if (state&2) {
+ real_writew(base_seg,RealOff(buffer)+2,base_dest);
+
+ real_writeb(base_seg,base_dest+0x00,mem_readb(0x410)&0x30);
+ for (ct=0; ct<0x1e; ct++) {
+ real_writeb(base_seg,base_dest+0x01+ct,mem_readb(0x449+ct));
+ }
+ for (ct=0; ct<0x07; ct++) {
+ real_writeb(base_seg,base_dest+0x1f+ct,mem_readb(0x484+ct));
+ }
+ real_writed(base_seg,base_dest+0x26,mem_readd(0x48a));
+ real_writed(base_seg,base_dest+0x2a,mem_readd(0x14)); // int 5
+ real_writed(base_seg,base_dest+0x2e,mem_readd(0x74)); // int 1d
+ real_writed(base_seg,base_dest+0x32,mem_readd(0x7c)); // int 1f
+ real_writed(base_seg,base_dest+0x36,mem_readd(0x10c)); // int 43
+
+ base_dest+=0x3a;
+ }
+
+ if (state&4) {
+ real_writew(base_seg,RealOff(buffer)+4,base_dest);
+
+ Bit16u crt_reg=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+
+ IO_ReadB(crt_reg+6);
+ IO_WriteB(0x3c0,0x14);
+ real_writeb(base_seg,base_dest+0x303,IO_ReadB(0x3c1));
+
+ Bitu dac_state=IO_ReadB(0x3c7)&1;
+ Bitu dac_windex=IO_ReadB(0x3c8);
+ if (dac_state!=0) dac_windex--;
+ real_writeb(base_seg,base_dest+0x000,dac_state);
+ real_writeb(base_seg,base_dest+0x001,dac_windex);
+ real_writeb(base_seg,base_dest+0x002,IO_ReadB(0x3c6));
+
+ for (ct=0; ct<0x100; ct++) {
+ IO_WriteB(0x3c7,ct);
+ real_writeb(base_seg,base_dest+0x003+ct*3+0,IO_ReadB(0x3c9));
+ real_writeb(base_seg,base_dest+0x003+ct*3+1,IO_ReadB(0x3c9));
+ real_writeb(base_seg,base_dest+0x003+ct*3+2,IO_ReadB(0x3c9));
+ }
+
+ IO_ReadB(crt_reg+6);
+ IO_WriteB(0x3c0,0x20);
+
+ base_dest+=0x303;
+ }
+
+ if ((svgaCard==SVGA_S3Trio) && (state&8)) {
+ real_writew(base_seg,RealOff(buffer)+6,base_dest);
+
+ Bit16u crt_reg=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+
+ IO_WriteB(0x3c4,0x08);
+// Bitu seq_8=IO_ReadB(0x3c5);
+ IO_ReadB(0x3c5);
+// real_writeb(base_seg,base_dest+0x00,IO_ReadB(0x3c5));
+ IO_WriteB(0x3c5,0x06); // unlock s3-specific registers
+
+ // sequencer
+ for (ct=0; ct<0x13; ct++) {
+ IO_WriteB(0x3c4,0x09+ct);
+ real_writeb(base_seg,base_dest+0x00+ct,IO_ReadB(0x3c5));
+ }
+
+ // unlock s3-specific registers
+ IO_WriteW(crt_reg,0x4838);
+ IO_WriteW(crt_reg,0xa539);
+
+ // crt controller
+ Bitu ct_dest=0x13;
+ for (ct=0; ct<0x40; ct++) {
+ if ((ct==0x4a-0x30) || (ct==0x4b-0x30)) {
+ IO_WriteB(crt_reg,0x45);
+ IO_ReadB(crt_reg+1);
+ IO_WriteB(crt_reg,0x30+ct);
+ real_writeb(base_seg,base_dest+(ct_dest++),IO_ReadB(crt_reg+1));
+ real_writeb(base_seg,base_dest+(ct_dest++),IO_ReadB(crt_reg+1));
+ real_writeb(base_seg,base_dest+(ct_dest++),IO_ReadB(crt_reg+1));
+ } else {
+ IO_WriteB(crt_reg,0x30+ct);
+ real_writeb(base_seg,base_dest+(ct_dest++),IO_ReadB(crt_reg+1));
+ }
+ }
+ }
+ return true;
+}
+
+bool INT10_VideoState_Restore(Bitu state,RealPt buffer) {
+ Bitu ct;
+ if ((state&7)==0) return false;
+
+ Bit16u base_seg=RealSeg(buffer);
+ Bit16u base_dest;
+
+ if (state&1) {
+ base_dest=real_readw(base_seg,RealOff(buffer));
+ Bit16u crt_reg=real_readw(base_seg,base_dest+0x40);
+
+ // reprogram for full access to plane latches
+ IO_WriteW(0x3c4,0x0704);
+ IO_WriteW(0x3ce,0x0406);
+ IO_WriteW(0x3ce,0x0005);
+
+ IO_WriteW(0x3c4,0x0002);
+ mem_writeb(0xaffff,real_readb(base_seg,base_dest+0x42));
+ IO_WriteW(0x3c4,0x0102);
+ mem_writeb(0xaffff,real_readb(base_seg,base_dest+0x43));
+ IO_WriteW(0x3c4,0x0202);
+ mem_writeb(0xaffff,real_readb(base_seg,base_dest+0x44));
+ IO_WriteW(0x3c4,0x0402);
+ mem_writeb(0xaffff,real_readb(base_seg,base_dest+0x45));
+ IO_WriteW(0x3c4,0x0f02);
+ mem_readb(0xaffff);
+
+ IO_WriteW(0x3c4,0x0100);
+
+ // sequencer
+ for (ct=1; ct<5; ct++) {
+ IO_WriteW(0x3c4,ct+(real_readb(base_seg,base_dest+0x04+ct)<<8));
+ }
+
+ IO_WriteB(0x3c2,real_readb(base_seg,base_dest+0x09));
+ IO_WriteW(0x3c4,0x0300);
+ IO_WriteW(crt_reg,0x0011);
+
+ // crt controller
+ for (ct=0; ct<0x19; ct++) {
+ IO_WriteW(crt_reg,ct+(real_readb(base_seg,base_dest+0x0a+ct)<<8));
+ }
+
+ IO_ReadB(crt_reg+6);
+ // attr registers
+ for (ct=0; ct<4; ct++) {
+ IO_WriteB(0x3c0,0x10+ct);
+ IO_WriteB(0x3c0,real_readb(base_seg,base_dest+0x33+ct));
+ }
+
+ // graphics registers
+ for (ct=0; ct<9; ct++) {
+ IO_WriteW(0x3ce,ct+(real_readb(base_seg,base_dest+0x37+ct)<<8));
+ }
+
+ IO_WriteB(crt_reg+6,real_readb(base_seg,base_dest+0x04));
+ IO_ReadB(crt_reg+6);
+
+ // attr registers
+ for (ct=0; ct<0x10; ct++) {
+ IO_WriteB(0x3c0,ct);
+ IO_WriteB(0x3c0,real_readb(base_seg,base_dest+0x23+ct));
+ }
+
+ IO_WriteB(0x3c4,real_readb(base_seg,base_dest+0x00));
+ IO_WriteB(0x3d4,real_readb(base_seg,base_dest+0x01));
+ IO_WriteB(0x3ce,real_readb(base_seg,base_dest+0x02));
+ IO_ReadB(crt_reg+6);
+ IO_WriteB(0x3c0,real_readb(base_seg,base_dest+0x03));
+ }
+
+ if (state&2) {
+ base_dest=real_readw(base_seg,RealOff(buffer)+2);
+
+ mem_writeb(0x410,(mem_readb(0x410)&0xcf) | real_readb(base_seg,base_dest+0x00));
+ for (ct=0; ct<0x1e; ct++) {
+ mem_writeb(0x449+ct,real_readb(base_seg,base_dest+0x01+ct));
+ }
+ for (ct=0; ct<0x07; ct++) {
+ mem_writeb(0x484+ct,real_readb(base_seg,base_dest+0x1f+ct));
+ }
+ mem_writed(0x48a,real_readd(base_seg,base_dest+0x26));
+ mem_writed(0x14,real_readd(base_seg,base_dest+0x2a)); // int 5
+ mem_writed(0x74,real_readd(base_seg,base_dest+0x2e)); // int 1d
+ mem_writed(0x7c,real_readd(base_seg,base_dest+0x32)); // int 1f
+ mem_writed(0x10c,real_readd(base_seg,base_dest+0x36)); // int 43
+ }
+
+ if (state&4) {
+ base_dest=real_readw(base_seg,RealOff(buffer)+4);
+
+ Bit16u crt_reg=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+
+ IO_WriteB(0x3c6,real_readb(base_seg,base_dest+0x002));
+
+ for (ct=0; ct<0x100; ct++) {
+ IO_WriteB(0x3c8,ct);
+ IO_WriteB(0x3c9,real_readb(base_seg,base_dest+0x003+ct*3+0));
+ IO_WriteB(0x3c9,real_readb(base_seg,base_dest+0x003+ct*3+1));
+ IO_WriteB(0x3c9,real_readb(base_seg,base_dest+0x003+ct*3+2));
+ }
+
+ IO_ReadB(crt_reg+6);
+ IO_WriteB(0x3c0,0x14);
+ IO_WriteB(0x3c0,real_readb(base_seg,base_dest+0x303));
+
+ Bitu dac_state=real_readb(base_seg,base_dest+0x000);
+ if (dac_state==0) {
+ IO_WriteB(0x3c8,real_readb(base_seg,base_dest+0x001));
+ } else {
+ IO_WriteB(0x3c7,real_readb(base_seg,base_dest+0x001));
+ }
+ }
+
+ if ((svgaCard==SVGA_S3Trio) && (state&8)) {
+ base_dest=real_readw(base_seg,RealOff(buffer)+6);
+
+ Bit16u crt_reg=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+
+ Bitu seq_idx=IO_ReadB(0x3c4);
+ IO_WriteB(0x3c4,0x08);
+// Bitu seq_8=IO_ReadB(0x3c5);
+ IO_ReadB(0x3c5);
+// real_writeb(base_seg,base_dest+0x00,IO_ReadB(0x3c5));
+ IO_WriteB(0x3c5,0x06); // unlock s3-specific registers
+
+ // sequencer
+ for (ct=0; ct<0x13; ct++) {
+ IO_WriteW(0x3c4,(0x09+ct)+(real_readb(base_seg,base_dest+0x00+ct)<<8));
+ }
+ IO_WriteB(0x3c4,seq_idx);
+
+// Bitu crtc_idx=IO_ReadB(0x3d4);
+
+ // unlock s3-specific registers
+ IO_WriteW(crt_reg,0x4838);
+ IO_WriteW(crt_reg,0xa539);
+
+ // crt controller
+ Bitu ct_dest=0x13;
+ for (ct=0; ct<0x40; ct++) {
+ if ((ct==0x4a-0x30) || (ct==0x4b-0x30)) {
+ IO_WriteB(crt_reg,0x45);
+ IO_ReadB(crt_reg+1);
+ IO_WriteB(crt_reg,0x30+ct);
+ IO_WriteB(crt_reg,real_readb(base_seg,base_dest+(ct_dest++)));
+ } else {
+ IO_WriteW(crt_reg,(0x30+ct)+(real_readb(base_seg,base_dest+(ct_dest++))<<8));
+ }
+ }
+
+ // mmio
+/* IO_WriteB(crt_reg,0x40);
+ Bitu sysval1=IO_ReadB(crt_reg+1);
+ IO_WriteB(crt_reg+1,sysval|1);
+ IO_WriteB(crt_reg,0x53);
+ Bitu sysva2=IO_ReadB(crt_reg+1);
+ IO_WriteB(crt_reg+1,sysval2|0x10);
+
+ real_writew(0xa000,0x8128,0xffff);
+
+ IO_WriteB(crt_reg,0x40);
+ IO_WriteB(crt_reg,sysval1);
+ IO_WriteB(crt_reg,0x53);
+ IO_WriteB(crt_reg,sysval2);
+ IO_WriteB(crt_reg,crtc_idx); */
+ }
+
+ return true;
+}
diff --git a/src/ints/int10_vptable.cpp b/src/ints/int10_vptable.cpp
new file mode 100644
index 000000000..3ec451d30
--- /dev/null
+++ b/src/ints/int10_vptable.cpp
@@ -0,0 +1,797 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "mem.h"
+#include "inout.h"
+#include "int10.h"
+
+
+const Bit8u vparams[] = {
+ // 40x25 mode 0 and 1 crtc registers
+ 0x38, 0x28, 0x2d, 0x0a, 0x1f, 0x06, 0x19, 0x1c, 0x02, 0x07, 0x06, 0x07, 0,0,0,0,
+ // 80x25 mode 2 and 3 crtc registers
+ 0x71, 0x50, 0x5a, 0x0a, 0x1f, 0x06, 0x19, 0x1c, 0x02, 0x07, 0x06, 0x07, 0,0,0,0,
+ // graphics modes 4, 5 and 6
+ 0x38, 0x28, 0x2d, 0x0a, 0x7f, 0x06, 0x64, 0x70, 0x02, 0x01, 0x06, 0x07, 0,0,0,0,
+ // mode 7 MDA text
+ 0x61, 0x50, 0x52, 0x0f, 0x19, 0x06, 0x19, 0x19, 0x02, 0x0d, 0x0b, 0x0c, 0,0,0,0,
+ // buffer length words 2048, 4096, 16384, 16384
+ 0x00, 0x08, 0x00, 0x10, 0x00, 0x40, 0x00, 0x40,
+ // columns
+ 40, 40, 80, 80, 40, 40, 80, 80,
+ // CGA mode register
+ 0x2c, 0x28, 0x2d, 0x29, 0x2a, 0x2e, 0x1e, 0x29
+};
+
+const Bit8u vparams_pcjr[] = {
+ // 40x25 mode 0 and 1 crtc registers
+ 0x38, 0x28, 0x2c, 0x06, 0x1f, 0x06, 0x19, 0x1c, 0x02, 0x07, 0x06, 0x07, 0,0,0,0,
+ // 80x25 mode 2 and 3 crtc registers
+ 0x71, 0x50, 0x5a, 0x0c, 0x1f, 0x06, 0x19, 0x1c, 0x02, 0x07, 0x06, 0x07, 0,0,0,0,
+ // graphics modes 4, 5, 6, 8
+ 0x38, 0x28, 0x2b, 0x06, 0x7f, 0x06, 0x64, 0x70, 0x02, 0x01, 0x26, 0x07, 0,0,0,0,
+ // other graphics modes
+ 0x71, 0x50, 0x56, 0x0c, 0x3f, 0x06, 0x32, 0x38, 0x02, 0x03, 0x26, 0x07, 0,0,0,0,
+ // buffer length words 2048, 4096, 16384, 16384
+ 0x00, 0x08, 0x00, 0x10, 0x00, 0x40, 0x00, 0x40,
+ // columns
+ 40, 40, 80, 80, 40, 40, 80, 80,
+ // CGA mode register
+ 0x2c, 0x28, 0x2d, 0x29, 0x2a, 0x2e, 0x1e, 0x29
+};
+
+const Bit8u vparams_tandy[] = {
+ // 40x25 mode 0 and 1 crtc registers
+ 0x38, 0x28, 0x2c, 0x08, 0x1f, 0x06, 0x19, 0x1c, 0x02, 0x07, 0x06, 0x07, 0,0,0,0,
+ // 80x25 mode 2 and 3 crtc registers
+ 0x71, 0x50, 0x58, 0x10, 0x1f, 0x06, 0x19, 0x1c, 0x02, 0x07, 0x06, 0x07, 0,0,0,0,
+ // graphics modes 4, 5 and 6
+ 0x38, 0x28, 0x2c, 0x08, 0x7f, 0x06, 0x64, 0x70, 0x02, 0x01, 0x06, 0x07, 0,0,0,0,
+ // graphics mode 7
+ 0x71, 0x50, 0x58, 0x10, 0x3f, 0x06, 0x32, 0x38, 0x02, 0x03, 0x06, 0x07, 0,0,0,0,
+ // buffer length words 2048, 4096, 16384, 16384
+ 0x00, 0x08, 0x00, 0x10, 0x00, 0x40, 0x00, 0x40,
+ // columns
+ 40, 40, 80, 80, 40, 40, 80, 80,
+ // CGA mode register
+ 0x2c, 0x28, 0x2d, 0x29, 0x2a, 0x2e, 0x1e, 0x29
+};
+
+const Bit8u vparams_tandy_td[] = {
+ // 40x25 mode 0 and 1 crtc registers
+ 0x38, 0x28, 0x2d, 0x0a, 0x1f, 0x06, 0x19, 0x1c, 0x02, 0x07, 0x06, 0x07, 0,0,0,0,
+ // 80x25 mode 2 and 3 crtc registers
+ 0x71, 0x50, 0x5a, 0x0a, 0x1f, 0x06, 0x19, 0x1c, 0x02, 0x07, 0x06, 0x07, 0,0,0,0,
+ // graphics modes 4, 5 and 6
+ 0x38, 0x28, 0x2d, 0x0a, 0x7f, 0x06, 0x64, 0x70, 0x02, 0x01, 0x06, 0x07, 0,0,0,0,
+ // mode 7 MDA text
+ 0x61, 0x50, 0x52, 0x0f, 0x19, 0x06, 0x19, 0x19, 0x02, 0x0d, 0x0b, 0x0c, 0,0,0,0,
+ // ?? mode 2 and 3 crtc registers
+ 0x71, 0x50, 0x5a, 0x0a, 0x3f, 0x06, 0x32, 0x38, 0x02, 0x03, 0x06, 0x07, 0,0,0,0,
+ // buffer length words 2048, 4096, 16384, 16384
+ 0x00, 0x08, 0x00, 0x10, 0x00, 0x40, 0x00, 0x40,
+ // columns
+ 40, 40, 80, 80, 40, 40, 80, 80,
+ // CGA mode register
+ 0x2c, 0x28, 0x2d, 0x29, 0x2a, 0x2e, 0x1e, 0x29
+};
+
+
+static Bit8u video_parameter_table_vga[0x40*0x1d]={
+// video parameter table for mode 0 (cga emulation)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 1 (cga emulation)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 2 (cga emulation)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 3 (cga emulation)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 4
+ 0x28, 0x18, 0x08, 0x00, 0x40, // bios data
+ 0x09, 0x00, 0x00, 0x02, // sequencer registers
+ 0x63, // misc output registers
+ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, // crtc registers 0-7
+ 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2, 0xff, // crtc registers 16-24
+ 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x01, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0f, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 5
+ 0x28, 0x18, 0x08, 0x00, 0x40, // bios data
+ 0x09, 0x00, 0x00, 0x02, // sequencer registers
+ 0x63, // misc output registers
+ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, // crtc registers 0-7
+ 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2, 0xff, // crtc registers 16-24
+ 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x01, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0f, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 6
+ 0x50, 0x18, 0x08, 0x00, 0x40, // bios data
+ 0x09, 0x0f, 0x00, 0x02, // sequencer registers
+ 0x63, // misc output registers
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, // crtc registers 0-7
+ 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xc2, 0xff, // crtc registers 16-24
+ 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, // attr registers 0-7
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, // attr registers 8-15
+ 0x01, 0x00, 0x01, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 7
+ 0x50, 0x18, 0x10, 0x00, 0x10, // bios data
+ 0x00, 0x0f, 0x00, 0x07, // sequencer registers
+ 0x66, // misc output registers
+ 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, // crtc registers 0-7
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, // attr registers 0-7
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, // attr registers 8-15
+ 0x0c, 0x00, 0x0f, 0x08, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 8
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 9
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode a
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode b
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode c
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode d
+ 0x28, 0x18, 0x08, 0x00, 0x20, // bios data
+ 0x09, 0x0f, 0x00, 0x02, // sequencer registers
+ 0x63, // misc output registers
+ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, // crtc registers 0-7
+ 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xe3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x01, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode e
+ 0x50, 0x18, 0x08, 0x00, 0x40, // bios data
+ 0x01, 0x0f, 0x00, 0x02, // sequencer registers
+ 0x63, // misc output registers
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, // crtc registers 0-7
+ 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xe3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x01, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode f (64k graphics memory)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 10 (64k graphics memory)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode f (>64k graphics memory)
+ 0x50, 0x18, 0x0e, 0x00, 0x80, // bios data
+ 0x01, 0x0f, 0x00, 0x02, // sequencer registers
+ 0xa2, // misc output registers
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, // crtc registers 0-7
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3, 0xff, // crtc registers 16-24
+ 0x00, 0x08, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, // attr registers 0-7
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, // attr registers 8-15
+ 0x0b, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 10 (>64k graphics memory)
+ 0x50, 0x18, 0x0e, 0x00, 0x80, // bios data
+ 0x01, 0x0f, 0x00, 0x02, // sequencer registers
+ 0xa3, // misc output registers
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, // crtc registers 0-7
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, // attr registers 0-7
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, // attr registers 8-15
+ 0x01, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 0 (350 lines)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 1 (350 lines)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 2 (350 lines)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 3 (350 lines)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode e
+ 0x28, 0x18, 0x10, 0x00, 0x08, // bios data
+ 0x08, 0x0f, 0x00, 0x07, // sequencer registers
+ 0x67, // misc output registers
+ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0xa0, 0xbf, 0x1f, // crtc registers 0-7
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x9c, 0x8e, 0x8f, 0x14, 0x1f, 0x96, 0xb9, 0xa3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, // attr registers 0-7
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, // attr registers 8-15
+ 0x0c, 0x00, 0x0f, 0x08, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode f
+ 0x50, 0x18, 0x10, 0x00, 0x10, // bios data
+ 0x00, 0x0f, 0x00, 0x07, // sequencer registers
+ 0x67, // misc output registers
+ 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, // crtc registers 0-7
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, // attr registers 0-7
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, // attr registers 8-15
+ 0x0c, 0x00, 0x0f, 0x08, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 10
+ 0x50, 0x18, 0x10, 0x00, 0x10, // bios data
+ 0x00, 0x0f, 0x00, 0x07, // sequencer registers
+ 0x66, // misc output registers
+ 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, // crtc registers 0-7
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, // attr registers 0-7
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, // attr registers 8-15
+ 0x0c, 0x00, 0x0f, 0x08, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 11
+ 0x50, 0x1d, 0x10, 0x00, 0xa0, // bios data
+ 0x01, 0x0f, 0x00, 0x02, // sequencer registers
+ 0xe3, // misc output registers
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, // crtc registers 0-7
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xc3, 0xff, // crtc registers 16-24
+ 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, // attr registers 0-7
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, // attr registers 8-15
+ 0x01, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 12
+ 0x50, 0x1d, 0x10, 0x00, 0xa0, // bios data
+ 0x01, 0x0f, 0x00, 0x02, // sequencer registers
+ 0xe3, // misc output registers
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, // crtc registers 0-7
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, // attr registers 0-7
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, // attr registers 8-15
+ 0x01, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 13
+ 0x28, 0x18, 0x08, 0x00, 0x20, // bios data
+ 0x01, 0x0f, 0x00, 0x0e, // sequencer registers
+ 0x63, // misc output registers
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, // crtc registers 0-7
+ 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x9c, 0x8e, 0x8f, 0x28, 0x40, 0x96, 0xb9, 0xa3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // attr registers 0-7
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // attr registers 8-15
+ 0x41, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff // graphics registers 0-8
+};
+
+static Bit8u video_parameter_table_ega[0x40*0x17]={
+// video parameter table for mode 0 (cga emulation)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 1 (cga emulation)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 2 (cga emulation)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 3 (cga emulation)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 4
+ 0x28, 0x18, 0x08, 0x00, 0x40, // bios data
+ 0x09, 0x03, 0x00, 0x02, // sequencer registers
+ 0x63, // misc output registers
+ 0x37, 0x27, 0x28, 0x9a, 0x2b, 0x8a, 0x04, 0x11, // crtc registers 0-7
+ 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0xd4, 0x86, 0xc7, 0x14, 0x00, 0xd0, 0xfc, 0xb2, 0xff, // crtc registers 16-24
+ 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x01, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 5
+ 0x28, 0x18, 0x08, 0x00, 0x40, // bios data
+ 0x09, 0x03, 0x00, 0x02, // sequencer registers
+ 0x63, // misc output registers
+ 0x37, 0x27, 0x28, 0x9a, 0x2b, 0x8a, 0x04, 0x11, // crtc registers 0-7
+ 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0xd4, 0x86, 0xc7, 0x14, 0x00, 0xd0, 0xfc, 0xb2, 0xff, // crtc registers 16-24
+ 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x01, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 6
+ 0x50, 0x18, 0x08, 0x00, 0x40, // bios data
+ 0x01, 0x0f, 0x00, 0x06, // sequencer registers
+ 0x63, // misc output registers
+ 0x73, 0x4f, 0x50, 0x96, 0x54, 0x94, 0x04, 0x11, // crtc registers 0-7
+ 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0xd4, 0x86, 0xc7, 0x28, 0x00, 0xd0, 0xfc, 0xd2, 0xff, // crtc registers 16-24
+ 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, // attr registers 0-7
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, // attr registers 8-15
+ 0x01, 0x00, 0x01, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 7
+ 0x50, 0x18, 0x0e, 0x00, 0x10, // bios data
+ 0x00, 0x0f, 0x00, 0x03, // sequencer registers
+ 0xa2, // misc output registers
+ 0x73, 0x4f, 0x50, 0x96, 0x55, 0x95, 0xb6, 0x1f, // crtc registers 0-7
+ 0x00, 0x4d, 0x0b, 0x0c, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xb1, 0xb3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x08, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 8
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 9
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode a
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode b
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode c
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode d
+ 0x28, 0x18, 0x08, 0x00, 0x20, // bios data
+ 0x09, 0x0f, 0x00, 0x06, // sequencer registers
+ 0x63, // misc output registers
+ 0x37, 0x27, 0x28, 0x9a, 0x2b, 0x8a, 0x04, 0x11, // crtc registers 0-7
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0xd4, 0x86, 0xc7, 0x14, 0x00, 0xd0, 0xfc, 0xd3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x01, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode e
+ 0x50, 0x18, 0x08, 0x00, 0x40, // bios data
+ 0x01, 0x0f, 0x00, 0x06, // sequencer registers
+ 0x63, // misc output registers
+ 0x73, 0x4f, 0x50, 0x96, 0x54, 0x94, 0x04, 0x11, // crtc registers 0-7
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0xd4, 0x86, 0xc7, 0x28, 0x00, 0xd0, 0xfc, 0xd3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x01, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode f (64k graphics memory)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode 10 (64k graphics memory)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// video parameter table for mode f (>64k graphics memory)
+ 0x50, 0x18, 0x0e, 0x00, 0x80, // bios data
+ 0x01, 0x0f, 0x00, 0x06, // sequencer registers
+ 0xa2, // misc output registers
+ 0x73, 0x4f, 0x50, 0x96, 0x54, 0x94, 0xb6, 0x1f, // crtc registers 0-7
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x83, 0x85, 0x5d, 0x14, 0x0f, 0x63, 0xb1, 0x9b, 0xff, // crtc registers 16-24
+ 0x00, 0x08, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, // attr registers 0-7
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, // attr registers 8-15
+ 0x0b, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 10 (>64k graphics memory)
+ 0x50, 0x18, 0x0e, 0x00, 0x80, // bios data
+ 0x01, 0x0f, 0x00, 0x06, // sequencer registers
+ 0xa3, // misc output registers
+ 0x5b, 0x4f, 0x50, 0x9e, 0x54, 0x1c, 0x4e, 0x1f, // crtc registers 0-7
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x83, 0x85, 0x5d, 0x14, 0x0f, 0x63, 0x49, 0x9b, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x01, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 0 (350 lines)
+ 0x28, 0x18, 0x0e, 0x00, 0x08, // bios data
+ 0x09, 0x0f, 0x00, 0x03, // sequencer registers
+ 0xa3, // misc output registers
+ 0x37, 0x27, 0x28, 0x9a, 0x2b, 0xaa, 0x04, 0x1f, // crtc registers 0-7
+ 0x00, 0x4d, 0x0b, 0x0c, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x83, 0x85, 0x5d, 0x14, 0x0f, 0x63, 0xff, 0xb3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x08, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 1 (350 lines)
+ 0x28, 0x18, 0x0e, 0x00, 0x08, // bios data
+ 0x09, 0x0f, 0x00, 0x03, // sequencer registers
+ 0xa3, // misc output registers
+ 0x37, 0x27, 0x28, 0x9a, 0x2b, 0xaa, 0x04, 0x1f, // crtc registers 0-7
+ 0x00, 0x4d, 0x0b, 0x0c, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x83, 0x85, 0x5d, 0x14, 0x0f, 0x63, 0xff, 0xb3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x08, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 2 (350 lines)
+ 0x50, 0x18, 0x0e, 0x00, 0x10, // bios data
+ 0x01, 0x0f, 0x00, 0x03, // sequencer registers
+ 0xa3, // misc output registers
+ 0x73, 0x4f, 0x50, 0x96, 0x55, 0x95, 0xb6, 0x1f, // crtc registers 0-7
+ 0x00, 0x4d, 0x0b, 0x0c, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xb1, 0xb3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x08, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, // graphics registers 0-8
+// video parameter table for mode 3 (350 lines)
+ 0x50, 0x18, 0x0e, 0x00, 0x10, // bios data
+ 0x01, 0x0f, 0x00, 0x03, // sequencer registers
+ 0xa3, // misc output registers
+ 0x73, 0x4f, 0x50, 0x96, 0x55, 0x95, 0xb6, 0x1f, // crtc registers 0-7
+ 0x00, 0x4d, 0x0b, 0x0c, 0x00, 0x00, 0x00, 0x00, // crtc registers 8-15
+ 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xb1, 0xb3, 0xff, // crtc registers 16-24
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // attr registers 0-7
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // attr registers 8-15
+ 0x08, 0x00, 0x0f, 0x00, // attr registers 16-19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff // graphics registers 0-8
+};
+
+
+Bit16u INT10_SetupVideoParameterTable(PhysPt basepos) {
+ if (IS_VGA_ARCH) {
+ for (Bitu i=0;i<0x40*0x1d;i++) {
+ phys_writeb(basepos+i,video_parameter_table_vga[i]);
+ }
+ return 0x40*0x1d;
+ } else {
+ for (Bitu i=0;i<0x40*0x17;i++) {
+ phys_writeb(basepos+i,video_parameter_table_ega[i]);
+ }
+ return 0x40*0x17;
+ }
+}
+
+void INT10_SetupBasicVideoParameterTable(void) {
+ /* video parameter table at F000:F0A4 */
+ RealSetVec(0x1d,RealMake(0xF000, 0xF0A4));
+ switch (machine) {
+ case MCH_TANDY:
+ for (Bit16u i = 0; i < sizeof(vparams_tandy); i++) {
+ phys_writeb(0xFF0A4+i,vparams_tandy[i]);
+ }
+ break;
+ case MCH_PCJR:
+ for (Bit16u i = 0; i < sizeof(vparams_pcjr); i++) {
+ phys_writeb(0xFF0A4+i,vparams_pcjr[i]);
+ }
+ break;
+ default:
+ for (Bit16u i = 0; i < sizeof(vparams); i++) {
+ phys_writeb(0xFF0A4+i,vparams[i]);
+ }
+ break;
+ }
+}
+
+#if 0
+void INT10_GenerateVideoParameterTable(void) {
+ if (!IS_VGA_ARCH) E_Exit("Be sure that all graphics registers are readable!");
+ Bitu i;
+ for (i=0; i<4; i++) {
+ LOG_MSG("// video parameter table for mode %x (cga emulation)",i);
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ }
+ for (i=4; i<0x0f; i++) {
+ Bitu ct;
+ LOG_MSG("// video parameter table for mode %x",i);
+ if ((i>=8) && (i<0x0d)) {
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ } else {
+ INT10_SetVideoMode(i);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // bios data",real_readb(0x40,0x4a),real_readb(0x40,0x84),real_readb(0x40,0x85),real_readb(0x40,0x4c),real_readb(0x40,0x4d));
+ Bitu seq_regs[4];
+ for (ct=0; ct<4; ct++) {
+ IO_WriteB(0x3c4,ct+1);
+ seq_regs[ct]=IO_ReadB(0x3c5);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, // sequencer registers",seq_regs[0],seq_regs[1],seq_regs[2],seq_regs[3]);
+ LOG_MSG(" 0x%02x, // misc output registers",IO_ReadB(0x3cc));
+ Bitu crtc_regs[0x19];
+ Bit16u crt_addr=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ for (ct=0; ct<0x19; ct++) {
+ IO_WriteB(crt_addr,ct);
+ crtc_regs[ct]=IO_ReadB(crt_addr+1);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // crtc registers 0-7",
+ crtc_regs[0x00],crtc_regs[0x01],crtc_regs[0x02],crtc_regs[0x03],
+ crtc_regs[0x04],crtc_regs[0x05],crtc_regs[0x06],crtc_regs[0x07]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // crtc registers 8-15",
+ crtc_regs[0x08],crtc_regs[0x09],crtc_regs[0x0a],crtc_regs[0x0b],
+ crtc_regs[0x0c],crtc_regs[0x0d],crtc_regs[0x0e],crtc_regs[0x0f]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // crtc registers 16-24",
+ crtc_regs[0x10],crtc_regs[0x11],crtc_regs[0x12],crtc_regs[0x13],
+ crtc_regs[0x14],crtc_regs[0x15],crtc_regs[0x16],crtc_regs[0x17],crtc_regs[0x18]);
+ Bitu attr_regs[0x14];
+ for (ct=0; ct<0x14; ct++) {
+ IO_ReadB(crt_addr+6);
+ IO_WriteB(0x3c0,ct);
+ attr_regs[ct]=IO_ReadB(0x3c1);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // attr registers 0-7",
+ attr_regs[0x00],attr_regs[0x01],attr_regs[0x02],attr_regs[0x03],
+ attr_regs[0x04],attr_regs[0x05],attr_regs[0x06],attr_regs[0x07]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // attr registers 8-15",
+ attr_regs[0x08],attr_regs[0x09],attr_regs[0x0a],attr_regs[0x0b],
+ attr_regs[0x0c],attr_regs[0x0d],attr_regs[0x0e],attr_regs[0x0f]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, // attr registers 16-19",
+ attr_regs[0x10],attr_regs[0x11],attr_regs[0x12],attr_regs[0x13]);
+ Bitu gfx_regs[9];
+ for (ct=0; ct<0x09; ct++) {
+ IO_WriteB(0x3ce,ct);
+ gfx_regs[ct]=IO_ReadB(0x3cf);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // graphics registers 0-8",
+ gfx_regs[0x00],gfx_regs[0x01],gfx_regs[0x02],gfx_regs[0x03],
+ gfx_regs[0x04],gfx_regs[0x05],gfx_regs[0x06],gfx_regs[0x07],gfx_regs[0x08]);
+ }
+ }
+ for (i=0x0f; i<0x11; i++) {
+ LOG_MSG("// video parameter table for mode %x (64k graphics memory)",i);
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ }
+ for (i=0x0f; i<0x11; i++) {
+ Bitu ct;
+ INT10_SetVideoMode(i);
+ LOG_MSG("// video parameter table for mode %x (>64k graphics memory)",i);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // bios data",real_readb(0x40,0x4a),real_readb(0x40,0x84),real_readb(0x40,0x85),real_readb(0x40,0x4c),real_readb(0x40,0x4d));
+ Bitu seq_regs[4];
+ for (ct=0; ct<4; ct++) {
+ IO_WriteB(0x3c4,ct+1);
+ seq_regs[ct]=IO_ReadB(0x3c5);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, // sequencer registers",seq_regs[0],seq_regs[1],seq_regs[2],seq_regs[3]);
+ LOG_MSG(" 0x%02x, // misc output registers",IO_ReadB(0x3cc));
+ Bitu crtc_regs[0x19];
+ Bit16u crt_addr=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ for (ct=0; ct<0x19; ct++) {
+ IO_WriteB(crt_addr,ct);
+ crtc_regs[ct]=IO_ReadB(crt_addr+1);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // crtc registers 0-7",
+ crtc_regs[0x00],crtc_regs[0x01],crtc_regs[0x02],crtc_regs[0x03],
+ crtc_regs[0x04],crtc_regs[0x05],crtc_regs[0x06],crtc_regs[0x07]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // crtc registers 8-15",
+ crtc_regs[0x08],crtc_regs[0x09],crtc_regs[0x0a],crtc_regs[0x0b],
+ crtc_regs[0x0c],crtc_regs[0x0d],crtc_regs[0x0e],crtc_regs[0x0f]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // crtc registers 16-24",
+ crtc_regs[0x10],crtc_regs[0x11],crtc_regs[0x12],crtc_regs[0x13],
+ crtc_regs[0x14],crtc_regs[0x15],crtc_regs[0x16],crtc_regs[0x17],crtc_regs[0x18]);
+ Bitu attr_regs[0x14];
+ for (ct=0; ct<0x14; ct++) {
+ IO_ReadB(crt_addr+6);
+ IO_WriteB(0x3c0,ct);
+ attr_regs[ct]=IO_ReadB(0x3c1);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // attr registers 0-7",
+ attr_regs[0x00],attr_regs[0x01],attr_regs[0x02],attr_regs[0x03],
+ attr_regs[0x04],attr_regs[0x05],attr_regs[0x06],attr_regs[0x07]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // attr registers 8-15",
+ attr_regs[0x08],attr_regs[0x09],attr_regs[0x0a],attr_regs[0x0b],
+ attr_regs[0x0c],attr_regs[0x0d],attr_regs[0x0e],attr_regs[0x0f]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, // attr registers 16-19",
+ attr_regs[0x10],attr_regs[0x11],attr_regs[0x12],attr_regs[0x13]);
+ Bitu gfx_regs[9];
+ for (ct=0; ct<0x09; ct++) {
+ IO_WriteB(0x3ce,ct);
+ gfx_regs[ct]=IO_ReadB(0x3cf);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // graphics registers 0-8",
+ gfx_regs[0x00],gfx_regs[0x01],gfx_regs[0x02],gfx_regs[0x03],
+ gfx_regs[0x04],gfx_regs[0x05],gfx_regs[0x06],gfx_regs[0x07],gfx_regs[0x08]);
+ }
+ for (i=0; i<4; i++) {
+ if (IS_VGA_ARCH) {
+ LOG_MSG("// video parameter table for mode %x (350 lines)",i);
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ LOG_MSG(" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,");
+ } else {
+ Bitu ct;
+ INT10_SetVideoMode(i);
+ LOG_MSG("// video parameter table for mode %x (350 lines)",i);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // bios data",real_readb(0x40,0x4a),real_readb(0x40,0x84),real_readb(0x40,0x85),real_readb(0x40,0x4c),real_readb(0x40,0x4d));
+ Bitu seq_regs[4];
+ for (ct=0; ct<4; ct++) {
+ IO_WriteB(0x3c4,ct+1);
+ seq_regs[ct]=IO_ReadB(0x3c5);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, // sequencer registers",seq_regs[0],seq_regs[1],seq_regs[2],seq_regs[3]);
+ LOG_MSG(" 0x%02x, // misc output registers",IO_ReadB(0x3cc));
+ Bitu crtc_regs[0x19];
+ Bit16u crt_addr=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ for (ct=0; ct<0x19; ct++) {
+ IO_WriteB(crt_addr,ct);
+ crtc_regs[ct]=IO_ReadB(crt_addr+1);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // crtc registers 0-7",
+ crtc_regs[0x00],crtc_regs[0x01],crtc_regs[0x02],crtc_regs[0x03],
+ crtc_regs[0x04],crtc_regs[0x05],crtc_regs[0x06],crtc_regs[0x07]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // crtc registers 8-15",
+ crtc_regs[0x08],crtc_regs[0x09],crtc_regs[0x0a],crtc_regs[0x0b],
+ crtc_regs[0x0c],crtc_regs[0x0d],crtc_regs[0x0e],crtc_regs[0x0f]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // crtc registers 16-24",
+ crtc_regs[0x10],crtc_regs[0x11],crtc_regs[0x12],crtc_regs[0x13],
+ crtc_regs[0x14],crtc_regs[0x15],crtc_regs[0x16],crtc_regs[0x17],crtc_regs[0x18]);
+ Bitu attr_regs[0x14];
+ for (ct=0; ct<0x14; ct++) {
+ IO_ReadB(crt_addr+6);
+ IO_WriteB(0x3c0,ct);
+ attr_regs[ct]=IO_ReadB(0x3c1);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // attr registers 0-7",
+ attr_regs[0x00],attr_regs[0x01],attr_regs[0x02],attr_regs[0x03],
+ attr_regs[0x04],attr_regs[0x05],attr_regs[0x06],attr_regs[0x07]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // attr registers 8-15",
+ attr_regs[0x08],attr_regs[0x09],attr_regs[0x0a],attr_regs[0x0b],
+ attr_regs[0x0c],attr_regs[0x0d],attr_regs[0x0e],attr_regs[0x0f]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, // attr registers 16-19",
+ attr_regs[0x10],attr_regs[0x11],attr_regs[0x12],attr_regs[0x13]);
+ Bitu gfx_regs[9];
+ for (ct=0; ct<0x09; ct++) {
+ IO_WriteB(0x3ce,ct);
+ gfx_regs[ct]=IO_ReadB(0x3cf);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // graphics registers 0-8",
+ gfx_regs[0x00],gfx_regs[0x01],gfx_regs[0x02],gfx_regs[0x03],
+ gfx_regs[0x04],gfx_regs[0x05],gfx_regs[0x06],gfx_regs[0x07],gfx_regs[0x08]);
+ }
+ }
+ if (IS_VGA_ARCH) {
+ for (i=0x0e; i<0x14; i++) {
+ Bitu ct=i;
+ if (i==0x0e) ct=1;
+ if (i==0x0f) ct=3;
+ if (i==0x010) ct=7;
+ INT10_SetVideoMode(ct);
+ LOG_MSG("// video parameter table for mode %x",i);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // bios data",real_readb(0x40,0x4a),real_readb(0x40,0x84),real_readb(0x40,0x85),real_readb(0x40,0x4c),real_readb(0x40,0x4d));
+ Bitu seq_regs[4];
+ for (ct=0; ct<4; ct++) {
+ IO_WriteB(0x3c4,ct+1);
+ seq_regs[ct]=IO_ReadB(0x3c5);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, // sequencer registers",seq_regs[0],seq_regs[1],seq_regs[2],seq_regs[3]);
+ LOG_MSG(" 0x%02x, // misc output registers",IO_ReadB(0x3cc));
+ Bitu crtc_regs[0x19];
+ Bit16u crt_addr=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ for (ct=0; ct<0x19; ct++) {
+ IO_WriteB(crt_addr,ct);
+ crtc_regs[ct]=IO_ReadB(crt_addr+1);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // crtc registers 0-7",
+ crtc_regs[0x00],crtc_regs[0x01],crtc_regs[0x02],crtc_regs[0x03],
+ crtc_regs[0x04],crtc_regs[0x05],crtc_regs[0x06],crtc_regs[0x07]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // crtc registers 8-15",
+ crtc_regs[0x08],crtc_regs[0x09],crtc_regs[0x0a],crtc_regs[0x0b],
+ crtc_regs[0x0c],crtc_regs[0x0d],crtc_regs[0x0e],crtc_regs[0x0f]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // crtc registers 16-24",
+ crtc_regs[0x10],crtc_regs[0x11],crtc_regs[0x12],crtc_regs[0x13],
+ crtc_regs[0x14],crtc_regs[0x15],crtc_regs[0x16],crtc_regs[0x17],crtc_regs[0x18]);
+ Bitu attr_regs[0x14];
+ for (ct=0; ct<0x14; ct++) {
+ IO_ReadB(crt_addr+6);
+ IO_WriteB(0x3c0,ct);
+ attr_regs[ct]=IO_ReadB(0x3c1);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // attr registers 0-7",
+ attr_regs[0x00],attr_regs[0x01],attr_regs[0x02],attr_regs[0x03],
+ attr_regs[0x04],attr_regs[0x05],attr_regs[0x06],attr_regs[0x07]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // attr registers 8-15",
+ attr_regs[0x08],attr_regs[0x09],attr_regs[0x0a],attr_regs[0x0b],
+ attr_regs[0x0c],attr_regs[0x0d],attr_regs[0x0e],attr_regs[0x0f]);
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, // attr registers 16-19",
+ attr_regs[0x10],attr_regs[0x11],attr_regs[0x12],attr_regs[0x13]);
+ Bitu gfx_regs[9];
+ for (ct=0; ct<0x09; ct++) {
+ IO_WriteB(0x3ce,ct);
+ gfx_regs[ct]=IO_ReadB(0x3cf);
+ }
+ LOG_MSG(" 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, // graphics registers 0-8",
+ gfx_regs[0x00],gfx_regs[0x01],gfx_regs[0x02],gfx_regs[0x03],
+ gfx_regs[0x04],gfx_regs[0x05],gfx_regs[0x06],gfx_regs[0x07],gfx_regs[0x08]);
+ }
+ }
+ INT10_SetVideoMode(3);
+ E_Exit("done!");
+}
+#endif
diff --git a/src/ints/mouse.cpp b/src/ints/mouse.cpp
new file mode 100644
index 000000000..b463a60f1
--- /dev/null
+++ b/src/ints/mouse.cpp
@@ -0,0 +1,1179 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <math.h>
+
+
+#include "dosbox.h"
+#include "callback.h"
+#include "mem.h"
+#include "regs.h"
+#include "cpu.h"
+#include "mouse.h"
+#include "pic.h"
+#include "inout.h"
+#include "int10.h"
+#include "bios.h"
+#include "dos_inc.h"
+
+static Bitu call_int33,call_int74,int74_ret_callback,call_mouse_bd;
+static Bit16u ps2cbseg,ps2cbofs;
+static bool useps2callback,ps2callbackinit;
+static Bitu call_ps2,call_uir;
+static RealPt ps2_callback,uir_callback;
+static Bit16s oldmouseX, oldmouseY;
+// forward
+void WriteMouseIntVector(void);
+
+struct button_event {
+ Bit8u type;
+ Bit8u buttons;
+};
+
+#define QUEUE_SIZE 32
+#define MOUSE_BUTTONS 3
+#define MOUSE_IRQ 12
+#define POS_X (static_cast<Bit16s>(mouse.x) & mouse.gran_x)
+#define POS_Y (static_cast<Bit16s>(mouse.y) & mouse.gran_y)
+
+#define CURSORX 16
+#define CURSORY 16
+#define HIGHESTBIT (1<<(CURSORX-1))
+
+static Bit16u defaultTextAndMask = 0x77FF;
+static Bit16u defaultTextXorMask = 0x7700;
+
+static Bit16u defaultScreenMask[CURSORY] = {
+ 0x3FFF, 0x1FFF, 0x0FFF, 0x07FF,
+ 0x03FF, 0x01FF, 0x00FF, 0x007F,
+ 0x003F, 0x001F, 0x01FF, 0x00FF,
+ 0x30FF, 0xF87F, 0xF87F, 0xFCFF
+};
+
+static Bit16u defaultCursorMask[CURSORY] = {
+ 0x0000, 0x4000, 0x6000, 0x7000,
+ 0x7800, 0x7C00, 0x7E00, 0x7F00,
+ 0x7F80, 0x7C00, 0x6C00, 0x4600,
+ 0x0600, 0x0300, 0x0300, 0x0000
+};
+
+static Bit16u userdefScreenMask[CURSORY];
+static Bit16u userdefCursorMask[CURSORY];
+
+static struct {
+ Bit8u buttons;
+ Bit16u times_pressed[MOUSE_BUTTONS];
+ Bit16u times_released[MOUSE_BUTTONS];
+ Bit16u last_released_x[MOUSE_BUTTONS];
+ Bit16u last_released_y[MOUSE_BUTTONS];
+ Bit16u last_pressed_x[MOUSE_BUTTONS];
+ Bit16u last_pressed_y[MOUSE_BUTTONS];
+ Bit16u hidden;
+ float add_x,add_y;
+ Bit16s min_x,max_x,min_y,max_y;
+ float mickey_x,mickey_y;
+ float x,y;
+ button_event event_queue[QUEUE_SIZE];
+ Bit8u events;//Increase if QUEUE_SIZE >255 (currently 32)
+ Bit16u sub_seg,sub_ofs;
+ Bit16u sub_mask;
+
+ bool background;
+ Bit16s backposx, backposy;
+ Bit8u backData[CURSORX*CURSORY];
+ Bit16u* screenMask;
+ Bit16u* cursorMask;
+ Bit16s clipx,clipy;
+ Bit16s hotx,hoty;
+ Bit16u textAndMask, textXorMask;
+
+ float mickeysPerPixel_x;
+ float mickeysPerPixel_y;
+ float pixelPerMickey_x;
+ float pixelPerMickey_y;
+ Bit16u senv_x_val;
+ Bit16u senv_y_val;
+ Bit16u dspeed_val;
+ float senv_x;
+ float senv_y;
+ Bit16s updateRegion_x[2];
+ Bit16s updateRegion_y[2];
+ Bit16u doubleSpeedThreshold;
+ Bit16u language;
+ Bit16u cursorType;
+ Bit16u oldhidden;
+ Bit8u page;
+ bool enabled;
+ bool inhibit_draw;
+ bool timer_in_progress;
+ bool in_UIR;
+ Bit8u mode;
+ Bit16s gran_x,gran_y;
+} mouse;
+
+bool Mouse_SetPS2State(bool use) {
+ if (use && (!ps2callbackinit)) {
+ useps2callback = false;
+ PIC_SetIRQMask(MOUSE_IRQ,true);
+ return false;
+ }
+ useps2callback = use;
+ Mouse_AutoLock(useps2callback);
+ PIC_SetIRQMask(MOUSE_IRQ,!useps2callback);
+ return true;
+}
+
+void Mouse_ChangePS2Callback(Bit16u pseg, Bit16u pofs) {
+ if ((pseg==0) && (pofs==0)) {
+ ps2callbackinit = false;
+ Mouse_AutoLock(false);
+ } else {
+ ps2callbackinit = true;
+ ps2cbseg = pseg;
+ ps2cbofs = pofs;
+ }
+ Mouse_AutoLock(ps2callbackinit);
+}
+
+void DoPS2Callback(Bit16u data, Bit16s mouseX, Bit16s mouseY) {
+ if (useps2callback) {
+ Bit16u mdat = (data & 0x03) | 0x08;
+ Bit16s xdiff = mouseX-oldmouseX;
+ Bit16s ydiff = oldmouseY-mouseY;
+ oldmouseX = mouseX;
+ oldmouseY = mouseY;
+ if ((xdiff>0xff) || (xdiff<-0xff)) mdat |= 0x40; // x overflow
+ if ((ydiff>0xff) || (ydiff<-0xff)) mdat |= 0x80; // y overflow
+ xdiff %= 256;
+ ydiff %= 256;
+ if (xdiff<0) {
+ xdiff = (0x100+xdiff);
+ mdat |= 0x10;
+ }
+ if (ydiff<0) {
+ ydiff = (0x100+ydiff);
+ mdat |= 0x20;
+ }
+ CPU_Push16((Bit16u)mdat);
+ CPU_Push16((Bit16u)(xdiff % 256));
+ CPU_Push16((Bit16u)(ydiff % 256));
+ CPU_Push16((Bit16u)0);
+ CPU_Push16(RealSeg(ps2_callback));
+ CPU_Push16(RealOff(ps2_callback));
+ SegSet16(cs, ps2cbseg);
+ reg_ip = ps2cbofs;
+ }
+}
+
+Bitu PS2_Handler(void) {
+ CPU_Pop16();CPU_Pop16();CPU_Pop16();CPU_Pop16();// remove the 4 words
+ return CBRET_NONE;
+}
+
+
+#define X_MICKEY 8
+#define Y_MICKEY 8
+
+#define MOUSE_HAS_MOVED 1
+#define MOUSE_LEFT_PRESSED 2
+#define MOUSE_LEFT_RELEASED 4
+#define MOUSE_RIGHT_PRESSED 8
+#define MOUSE_RIGHT_RELEASED 16
+#define MOUSE_MIDDLE_PRESSED 32
+#define MOUSE_MIDDLE_RELEASED 64
+#define MOUSE_DELAY 5.0
+
+void MOUSE_Limit_Events(Bitu /*val*/) {
+ mouse.timer_in_progress = false;
+ if (mouse.events) {
+ mouse.timer_in_progress = true;
+ PIC_AddEvent(MOUSE_Limit_Events,MOUSE_DELAY);
+ PIC_ActivateIRQ(MOUSE_IRQ);
+ }
+}
+
+INLINE void Mouse_AddEvent(Bit8u type) {
+ if (mouse.events<QUEUE_SIZE) {
+ if (mouse.events>0) {
+ /* Skip duplicate events */
+ if (type==MOUSE_HAS_MOVED) return;
+ /* Always put the newest element in the front as that the events are
+ * handled backwards (prevents doubleclicks while moving)
+ */
+ for(Bitu i = mouse.events ; i ; i--)
+ mouse.event_queue[i] = mouse.event_queue[i-1];
+ }
+ mouse.event_queue[0].type=type;
+ mouse.event_queue[0].buttons=mouse.buttons;
+ mouse.events++;
+ }
+ if (!mouse.timer_in_progress) {
+ mouse.timer_in_progress = true;
+ PIC_AddEvent(MOUSE_Limit_Events,MOUSE_DELAY);
+ PIC_ActivateIRQ(MOUSE_IRQ);
+ }
+}
+
+// ***************************************************************************
+// Mouse cursor - text mode
+// ***************************************************************************
+/* Write and read directly to the screen. Do no use int_setcursorpos (LOTUS123) */
+extern void WriteChar(Bit16u col,Bit16u row,Bit8u page,Bit8u chr,Bit8u attr,bool useattr);
+extern void ReadCharAttr(Bit16u col,Bit16u row,Bit8u page,Bit16u * result);
+
+void RestoreCursorBackgroundText() {
+ if (mouse.hidden || mouse.inhibit_draw) return;
+
+ if (mouse.background) {
+ WriteChar(mouse.backposx,mouse.backposy,real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE),mouse.backData[0],mouse.backData[1],true);
+ mouse.background = false;
+ }
+}
+
+void DrawCursorText() {
+ // Restore Background
+ RestoreCursorBackgroundText();
+
+ // Check if cursor in update region
+ if ((POS_Y <= mouse.updateRegion_y[1]) && (POS_Y >= mouse.updateRegion_y[0]) &&
+ (POS_X <= mouse.updateRegion_x[1]) && (POS_X >= mouse.updateRegion_x[0])) {
+ return;
+ }
+
+ // Save Background
+ mouse.backposx = POS_X>>3;
+ mouse.backposy = POS_Y>>3;
+ if (mouse.mode < 2) mouse.backposx >>= 1;
+
+ //use current page (CV program)
+ Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+
+ if (mouse.cursorType == 0) {
+ Bit16u result;
+ ReadCharAttr(mouse.backposx,mouse.backposy,page,&result);
+ mouse.backData[0] = (Bit8u)(result & 0xFF);
+ mouse.backData[1] = (Bit8u)(result>>8);
+ mouse.background = true;
+ // Write Cursor
+ result = (result & mouse.textAndMask) ^ mouse.textXorMask;
+ WriteChar(mouse.backposx,mouse.backposy,page,(Bit8u)(result&0xFF),(Bit8u)(result>>8),true);
+ } else {
+ Bit16u address=page * real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
+ address += (mouse.backposy * real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS) + mouse.backposx) * 2;
+ address /= 2;
+ Bit16u cr = real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ IO_Write(cr , 0xe);
+ IO_Write(cr + 1, (address>>8) & 0xff);
+ IO_Write(cr , 0xf);
+ IO_Write(cr + 1, address & 0xff);
+ }
+}
+
+// ***************************************************************************
+// Mouse cursor - graphic mode
+// ***************************************************************************
+
+static Bit8u gfxReg3CE[9];
+static Bit8u index3C4,gfxReg3C5;
+void SaveVgaRegisters() {
+ if (IS_VGA_ARCH) {
+ for (Bit8u i=0; i<9; i++) {
+ IO_Write (0x3CE,i);
+ gfxReg3CE[i] = IO_Read(0x3CF);
+ }
+ /* Setup some default values in GFX regs that should work */
+ IO_Write(0x3CE,3); IO_Write(0x3Cf,0); //disable rotate and operation
+ IO_Write(0x3CE,5); IO_Write(0x3Cf,gfxReg3CE[5]&0xf0); //Force read/write mode 0
+
+ //Set Map to all planes. Celtic Tales
+ index3C4 = IO_Read(0x3c4); IO_Write(0x3C4,2);
+ gfxReg3C5 = IO_Read(0x3C5); IO_Write(0x3C5,0xF);
+ } else if (machine==MCH_EGA) {
+ //Set Map to all planes.
+ IO_Write(0x3C4,2);
+ IO_Write(0x3C5,0xF);
+ }
+}
+
+void RestoreVgaRegisters() {
+ if (IS_VGA_ARCH) {
+ for (Bit8u i=0; i<9; i++) {
+ IO_Write(0x3CE,i);
+ IO_Write(0x3CF,gfxReg3CE[i]);
+ }
+
+ IO_Write(0x3C4,2);
+ IO_Write(0x3C5,gfxReg3C5);
+ IO_Write(0x3C4,index3C4);
+ }
+}
+
+void ClipCursorArea(Bit16s& x1, Bit16s& x2, Bit16s& y1, Bit16s& y2,
+ Bit16u& addx1, Bit16u& addx2, Bit16u& addy) {
+ addx1 = addx2 = addy = 0;
+ // Clip up
+ if (y1<0) {
+ addy += (-y1);
+ y1 = 0;
+ }
+ // Clip down
+ if (y2>mouse.clipy) {
+ y2 = mouse.clipy;
+ };
+ // Clip left
+ if (x1<0) {
+ addx1 += (-x1);
+ x1 = 0;
+ };
+ // Clip right
+ if (x2>mouse.clipx) {
+ addx2 = x2 - mouse.clipx;
+ x2 = mouse.clipx;
+ };
+}
+
+void RestoreCursorBackground() {
+ if (mouse.hidden || mouse.inhibit_draw) return;
+
+ SaveVgaRegisters();
+ if (mouse.background) {
+ // Restore background
+ Bit16s x,y;
+ Bit16u addx1,addx2,addy;
+ Bit16u dataPos = 0;
+ Bit16s x1 = mouse.backposx;
+ Bit16s y1 = mouse.backposy;
+ Bit16s x2 = x1 + CURSORX - 1;
+ Bit16s y2 = y1 + CURSORY - 1;
+
+ ClipCursorArea(x1, x2, y1, y2, addx1, addx2, addy);
+
+ dataPos = addy * CURSORX;
+ for (y=y1; y<=y2; y++) {
+ dataPos += addx1;
+ for (x=x1; x<=x2; x++) {
+ INT10_PutPixel(x,y,mouse.page,mouse.backData[dataPos++]);
+ };
+ dataPos += addx2;
+ };
+ mouse.background = false;
+ };
+ RestoreVgaRegisters();
+}
+
+void DrawCursor() {
+ if (mouse.hidden || mouse.inhibit_draw) return;
+ INT10_SetCurMode();
+ // In Textmode ?
+ if (CurMode->type==M_TEXT) {
+ DrawCursorText();
+ return;
+ }
+
+ // Check video page. Seems to be ignored for text mode.
+ // hence the text mode handled above this
+ if (real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE)!=mouse.page) return;
+// Check if cursor in update region
+/* if ((POS_X >= mouse.updateRegion_x[0]) && (POS_X <= mouse.updateRegion_x[1]) &&
+ (POS_Y >= mouse.updateRegion_y[0]) && (POS_Y <= mouse.updateRegion_y[1])) {
+ if (CurMode->type==M_TEXT16)
+ RestoreCursorBackgroundText();
+ else
+ RestoreCursorBackground();
+ mouse.shown--;
+ return;
+ }
+ */ /*Not sure yet what to do update region should be set to ??? */
+
+ // Get Clipping ranges
+
+
+ mouse.clipx = (Bit16s)((Bits)CurMode->swidth-1); /* Get from bios ? */
+ mouse.clipy = (Bit16s)((Bits)CurMode->sheight-1);
+
+ /* might be vidmode == 0x13?2:1 */
+ Bit16s xratio = 640;
+ if (CurMode->swidth>0) xratio/=CurMode->swidth;
+ if (xratio==0) xratio = 1;
+
+ RestoreCursorBackground();
+
+ SaveVgaRegisters();
+
+ // Save Background
+ Bit16s x,y;
+ Bit16u addx1,addx2,addy;
+ Bit16u dataPos = 0;
+ Bit16s x1 = POS_X / xratio - mouse.hotx;
+ Bit16s y1 = POS_Y - mouse.hoty;
+ Bit16s x2 = x1 + CURSORX - 1;
+ Bit16s y2 = y1 + CURSORY - 1;
+
+ ClipCursorArea(x1,x2,y1,y2, addx1, addx2, addy);
+
+ dataPos = addy * CURSORX;
+ for (y=y1; y<=y2; y++) {
+ dataPos += addx1;
+ for (x=x1; x<=x2; x++) {
+ INT10_GetPixel(x,y,mouse.page,&mouse.backData[dataPos++]);
+ };
+ dataPos += addx2;
+ };
+ mouse.background= true;
+ mouse.backposx = POS_X / xratio - mouse.hotx;
+ mouse.backposy = POS_Y - mouse.hoty;
+
+ // Draw Mousecursor
+ dataPos = addy * CURSORX;
+ for (y=y1; y<=y2; y++) {
+ Bit16u scMask = mouse.screenMask[addy+y-y1];
+ Bit16u cuMask = mouse.cursorMask[addy+y-y1];
+ if (addx1>0) { scMask<<=addx1; cuMask<<=addx1; dataPos += addx1; };
+ for (x=x1; x<=x2; x++) {
+ Bit8u pixel = 0;
+ // ScreenMask
+ if (scMask & HIGHESTBIT) pixel = mouse.backData[dataPos];
+ scMask<<=1;
+ // CursorMask
+ if (cuMask & HIGHESTBIT) pixel = pixel ^ 0x0F;
+ cuMask<<=1;
+ // Set Pixel
+ INT10_PutPixel(x,y,mouse.page,pixel);
+ dataPos++;
+ };
+ dataPos += addx2;
+ };
+ RestoreVgaRegisters();
+}
+
+void Mouse_CursorMoved(float xrel,float yrel,float x,float y,bool emulate) {
+ float dx = xrel * mouse.pixelPerMickey_x;
+ float dy = yrel * mouse.pixelPerMickey_y;
+
+ if((fabs(xrel) > 1.0) || (mouse.senv_x < 1.0)) dx *= mouse.senv_x;
+ if((fabs(yrel) > 1.0) || (mouse.senv_y < 1.0)) dy *= mouse.senv_y;
+ if (useps2callback) dy *= 2;
+
+ mouse.mickey_x += (dx * mouse.mickeysPerPixel_x);
+ mouse.mickey_y += (dy * mouse.mickeysPerPixel_y);
+ if (mouse.mickey_x >= 32768.0) mouse.mickey_x -= 65536.0;
+ else if (mouse.mickey_x <= -32769.0) mouse.mickey_x += 65536.0;
+ if (mouse.mickey_y >= 32768.0) mouse.mickey_y -= 65536.0;
+ else if (mouse.mickey_y <= -32769.0) mouse.mickey_y += 65536.0;
+ if (emulate) {
+ mouse.x += dx;
+ mouse.y += dy;
+ } else {
+ if (CurMode->type == M_TEXT) {
+ mouse.x = x*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8;
+ mouse.y = y*(real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1)*8;
+ } else if ((mouse.max_x < 2048) || (mouse.max_y < 2048) || (mouse.max_x != mouse.max_y)) {
+ if ((mouse.max_x > 0) && (mouse.max_y > 0)) {
+ mouse.x = x*mouse.max_x;
+ mouse.y = y*mouse.max_y;
+ } else {
+ mouse.x += xrel;
+ mouse.y += yrel;
+ }
+ } else { // Games faking relative movement through absolute coordinates. Quite surprising that this actually works..
+ mouse.x += xrel;
+ mouse.y += yrel;
+ }
+ }
+
+ /* ignore constraints if using PS2 mouse callback in the bios */
+
+ if (!useps2callback) {
+ if (mouse.x > mouse.max_x) mouse.x = mouse.max_x;
+ if (mouse.x < mouse.min_x) mouse.x = mouse.min_x;
+ if (mouse.y > mouse.max_y) mouse.y = mouse.max_y;
+ if (mouse.y < mouse.min_y) mouse.y = mouse.min_y;
+ } else {
+ if (mouse.x >= 32768.0) mouse.x -= 65536.0;
+ else if (mouse.x <= -32769.0) mouse.x += 65536.0;
+ if (mouse.y >= 32768.0) mouse.y -= 65536.0;
+ else if (mouse.y <= -32769.0) mouse.y += 65536.0;
+ }
+ Mouse_AddEvent(MOUSE_HAS_MOVED);
+ DrawCursor();
+}
+
+void Mouse_CursorSet(float x,float y) {
+ mouse.x=x;
+ mouse.y=y;
+ DrawCursor();
+}
+
+void Mouse_ButtonPressed(Bit8u button) {
+ switch (button) {
+#if (MOUSE_BUTTONS >= 1)
+ case 0:
+ if (mouse.buttons&1) return;
+ mouse.buttons|=1;
+ Mouse_AddEvent(MOUSE_LEFT_PRESSED);
+ break;
+#endif
+#if (MOUSE_BUTTONS >= 2)
+ case 1:
+ if (mouse.buttons&2) return;
+ mouse.buttons|=2;
+ Mouse_AddEvent(MOUSE_RIGHT_PRESSED);
+ break;
+#endif
+#if (MOUSE_BUTTONS >= 3)
+ case 2:
+ if (mouse.buttons&4) return;
+ mouse.buttons|=4;
+ Mouse_AddEvent(MOUSE_MIDDLE_PRESSED);
+ break;
+#endif
+ default:
+ return;
+ }
+ mouse.times_pressed[button]++;
+ mouse.last_pressed_x[button]=POS_X;
+ mouse.last_pressed_y[button]=POS_Y;
+}
+
+void Mouse_ButtonReleased(Bit8u button) {
+ switch (button) {
+#if (MOUSE_BUTTONS >= 1)
+ case 0:
+ if (!(mouse.buttons&1)) return;
+ mouse.buttons&=~1;
+ Mouse_AddEvent(MOUSE_LEFT_RELEASED);
+ break;
+#endif
+#if (MOUSE_BUTTONS >= 2)
+ case 1:
+ if (!(mouse.buttons&2)) return;
+ mouse.buttons&=~2;
+ Mouse_AddEvent(MOUSE_RIGHT_RELEASED);
+ break;
+#endif
+#if (MOUSE_BUTTONS >= 3)
+ case 2:
+ if (!(mouse.buttons&4)) return;
+ mouse.buttons&=~4;
+ Mouse_AddEvent(MOUSE_MIDDLE_RELEASED);
+ break;
+#endif
+ default:
+ return;
+ }
+ mouse.times_released[button]++;
+ mouse.last_released_x[button]=POS_X;
+ mouse.last_released_y[button]=POS_Y;
+}
+
+static void Mouse_SetMickeyPixelRate(Bit16s px, Bit16s py){
+ if ((px!=0) && (py!=0)) {
+ mouse.mickeysPerPixel_x = (float)px/X_MICKEY;
+ mouse.mickeysPerPixel_y = (float)py/Y_MICKEY;
+ mouse.pixelPerMickey_x = X_MICKEY/(float)px;
+ mouse.pixelPerMickey_y = Y_MICKEY/(float)py;
+ }
+}
+
+static void Mouse_SetSensitivity(Bit16u px, Bit16u py, Bit16u dspeed){
+ if(px>100) px=100;
+ if(py>100) py=100;
+ if(dspeed>100) dspeed=100;
+ // save values
+ mouse.senv_x_val=px;
+ mouse.senv_y_val=py;
+ mouse.dspeed_val=dspeed;
+ if ((px!=0) && (py!=0)) {
+ px--; //Inspired by cutemouse
+ py--; //Although their cursor update routine is far more complex then ours
+ mouse.senv_x=(static_cast<float>(px)*px)/3600.0f +1.0f/3.0f;
+ mouse.senv_y=(static_cast<float>(py)*py)/3600.0f +1.0f/3.0f;
+ }
+}
+
+
+static void Mouse_ResetHardware(void){
+ PIC_SetIRQMask(MOUSE_IRQ,false);
+}
+
+void Mouse_BeforeNewVideoMode(bool setmode) {
+ if (CurMode->type!=M_TEXT) RestoreCursorBackground();
+ else RestoreCursorBackgroundText();
+ mouse.hidden = 1;
+ mouse.oldhidden = 1;
+ mouse.background = false;
+}
+
+//Does way to much. Many things should be moved to mouse reset one day
+void Mouse_AfterNewVideoMode(bool setmode) {
+ mouse.inhibit_draw = false;
+ /* Get the correct resolution from the current video mode */
+ Bit8u mode = mem_readb(BIOS_VIDEO_MODE);
+ if (setmode && mode == mouse.mode) LOG(LOG_MOUSE,LOG_NORMAL)("New video mode is the same as the old");
+ mouse.gran_x = (Bit16s)0xffff;
+ mouse.gran_y = (Bit16s)0xffff;
+ switch (mode) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x07: {
+ mouse.gran_x = (mode<2)?0xfff0:0xfff8;
+ mouse.gran_y = (Bit16s)0xfff8;
+ Bitu rows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS);
+ if ((rows == 0) || (rows > 250)) rows = 25 - 1;
+ mouse.max_y = 8*(rows+1) - 1;
+ break;
+ }
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0d:
+ case 0x0e:
+ case 0x13:
+ if (mode == 0x0d || mode == 0x13) mouse.gran_x = (Bit16s)0xfffe;
+ mouse.max_y = 199;
+ break;
+ case 0x0f:
+ case 0x10:
+ mouse.max_y = 349;
+ break;
+ case 0x11:
+ case 0x12:
+ mouse.max_y = 479;
+ break;
+ default:
+ LOG(LOG_MOUSE,LOG_ERROR)("Unhandled videomode %X on reset",mode);
+ mouse.inhibit_draw = true;
+ return;
+ }
+ mouse.mode = mode;
+ mouse.max_x = 639;
+ mouse.min_x = 0;
+ mouse.min_y = 0;
+
+ mouse.events = 0;
+ mouse.timer_in_progress = false;
+ PIC_RemoveEvents(MOUSE_Limit_Events);
+
+ mouse.hotx = 0;
+ mouse.hoty = 0;
+ mouse.screenMask = defaultScreenMask;
+ mouse.cursorMask = defaultCursorMask;
+ mouse.textAndMask= defaultTextAndMask;
+ mouse.textXorMask= defaultTextXorMask;
+ mouse.language = 0;
+ mouse.page = 0;
+ mouse.doubleSpeedThreshold = 64;
+ mouse.updateRegion_y[1] = -1; //offscreen
+ mouse.cursorType = 0; //Test
+ mouse.enabled=true;
+
+ oldmouseX = static_cast<Bit16s>(mouse.x);
+ oldmouseY = static_cast<Bit16s>(mouse.y);
+
+
+}
+
+//Much too empty, Mouse_NewVideoMode contains stuff that should be in here
+static void Mouse_Reset(void) {
+ Mouse_BeforeNewVideoMode(false);
+ Mouse_AfterNewVideoMode(false);
+ Mouse_SetMickeyPixelRate(8,16);
+
+ mouse.mickey_x = 0;
+ mouse.mickey_y = 0;
+
+ mouse.buttons = 0;
+
+ for (Bit16u but=0; but<MOUSE_BUTTONS; but++) {
+ mouse.times_pressed[but] = 0;
+ mouse.times_released[but] = 0;
+ mouse.last_pressed_x[but] = 0;
+ mouse.last_pressed_y[but] = 0;
+ mouse.last_released_x[but] = 0;
+ mouse.last_released_y[but] = 0;
+ }
+
+ // Dont set max coordinates here. it is done by SetResolution!
+ mouse.x = static_cast<float>((mouse.max_x + 1)/ 2);
+ mouse.y = static_cast<float>((mouse.max_y + 1)/ 2);
+ mouse.sub_mask = 0;
+ mouse.in_UIR = false;
+}
+
+static Bitu INT33_Handler(void) {
+// LOG(LOG_MOUSE,LOG_NORMAL)("MOUSE: %04X %X %X %d %d",reg_ax,reg_bx,reg_cx,POS_X,POS_Y);
+ switch (reg_ax) {
+ case 0x00: /* Reset Driver and Read Status */
+ Mouse_ResetHardware(); /* fallthrough */
+ case 0x21: /* Software Reset */
+ reg_ax=0xffff;
+ reg_bx=MOUSE_BUTTONS;
+ Mouse_Reset();
+ Mouse_AutoLock(true);
+ break;
+ case 0x01: /* Show Mouse */
+ if(mouse.hidden) mouse.hidden--;
+ mouse.updateRegion_y[1] = -1; //offscreen
+ Mouse_AutoLock(true);
+ DrawCursor();
+ break;
+ case 0x02: /* Hide Mouse */
+ {
+ if (CurMode->type!=M_TEXT) RestoreCursorBackground();
+ else RestoreCursorBackgroundText();
+ mouse.hidden++;
+ }
+ break;
+ case 0x03: /* Return position and Button Status */
+ reg_bx=mouse.buttons;
+ reg_cx=POS_X;
+ reg_dx=POS_Y;
+ break;
+ case 0x04: /* Position Mouse */
+ /* If position isn't different from current position
+ * don't change it then. (as position is rounded so numbers get
+ * lost when the rounded number is set) (arena/simulation Wolf) */
+ if ((Bit16s)reg_cx >= mouse.max_x) mouse.x = static_cast<float>(mouse.max_x);
+ else if (mouse.min_x >= (Bit16s)reg_cx) mouse.x = static_cast<float>(mouse.min_x);
+ else if ((Bit16s)reg_cx != POS_X) mouse.x = static_cast<float>(reg_cx);
+
+ if ((Bit16s)reg_dx >= mouse.max_y) mouse.y = static_cast<float>(mouse.max_y);
+ else if (mouse.min_y >= (Bit16s)reg_dx) mouse.y = static_cast<float>(mouse.min_y);
+ else if ((Bit16s)reg_dx != POS_Y) mouse.y = static_cast<float>(reg_dx);
+ DrawCursor();
+ break;
+ case 0x05: /* Return Button Press Data */
+ {
+ Bit16u but=reg_bx;
+ reg_ax=mouse.buttons;
+ if (but>=MOUSE_BUTTONS) but = MOUSE_BUTTONS - 1;
+ reg_cx=mouse.last_pressed_x[but];
+ reg_dx=mouse.last_pressed_y[but];
+ reg_bx=mouse.times_pressed[but];
+ mouse.times_pressed[but]=0;
+ break;
+ }
+ case 0x06: /* Return Button Release Data */
+ {
+ Bit16u but=reg_bx;
+ reg_ax=mouse.buttons;
+ if (but>=MOUSE_BUTTONS) but = MOUSE_BUTTONS - 1;
+ reg_cx=mouse.last_released_x[but];
+ reg_dx=mouse.last_released_y[but];
+ reg_bx=mouse.times_released[but];
+ mouse.times_released[but]=0;
+ break;
+ }
+ case 0x07: /* Define horizontal cursor range */
+ { //lemmings set 1-640 and wants that. iron seeds set 0-640 but doesn't like 640
+ //Iron seed works if newvideo mode with mode 13 sets 0-639
+ //Larry 6 actually wants newvideo mode with mode 13 to set it to 0-319
+ Bit16s max,min;
+ if ((Bit16s)reg_cx<(Bit16s)reg_dx) { min=(Bit16s)reg_cx;max=(Bit16s)reg_dx;}
+ else { min=(Bit16s)reg_dx;max=(Bit16s)reg_cx;}
+ mouse.min_x=min;
+ mouse.max_x=max;
+ /* Battlechess wants this */
+ if(mouse.x > mouse.max_x) mouse.x = mouse.max_x;
+ if(mouse.x < mouse.min_x) mouse.x = mouse.min_x;
+ /* Or alternatively this:
+ mouse.x = (mouse.max_x - mouse.min_x + 1)/2;*/
+ LOG(LOG_MOUSE,LOG_NORMAL)("Define Hortizontal range min:%d max:%d",min,max);
+ }
+ break;
+ case 0x08: /* Define vertical cursor range */
+ { // not sure what to take instead of the CurMode (see case 0x07 as well)
+ // especially the cases where sheight= 400 and we set it with the mouse_reset to 200
+ //disabled it at the moment. Seems to break syndicate who want 400 in mode 13
+ Bit16s max,min;
+ if ((Bit16s)reg_cx<(Bit16s)reg_dx) { min=(Bit16s)reg_cx;max=(Bit16s)reg_dx;}
+ else { min=(Bit16s)reg_dx;max=(Bit16s)reg_cx;}
+ mouse.min_y=min;
+ mouse.max_y=max;
+ /* Battlechess wants this */
+ if(mouse.y > mouse.max_y) mouse.y = mouse.max_y;
+ if(mouse.y < mouse.min_y) mouse.y = mouse.min_y;
+ /* Or alternatively this:
+ mouse.y = (mouse.max_y - mouse.min_y + 1)/2;*/
+ LOG(LOG_MOUSE,LOG_NORMAL)("Define Vertical range min:%d max:%d",min,max);
+ }
+ break;
+ case 0x09: /* Define GFX Cursor */
+ {
+ PhysPt src = SegPhys(es)+reg_dx;
+ MEM_BlockRead(src ,userdefScreenMask,CURSORY*2);
+ MEM_BlockRead(src+CURSORY*2,userdefCursorMask,CURSORY*2);
+ mouse.screenMask = userdefScreenMask;
+ mouse.cursorMask = userdefCursorMask;
+ mouse.hotx = reg_bx;
+ mouse.hoty = reg_cx;
+ mouse.cursorType = 2;
+ DrawCursor();
+ }
+ break;
+ case 0x0a: /* Define Text Cursor */
+ mouse.cursorType = (reg_bx?1:0);
+ mouse.textAndMask = reg_cx;
+ mouse.textXorMask = reg_dx;
+ if (reg_bx) {
+ INT10_SetCursorShape(reg_cl,reg_dl);
+ LOG(LOG_MOUSE,LOG_NORMAL)("Hardware Text cursor selected");
+ }
+ DrawCursor();
+ break;
+ case 0x0b: /* Read Motion Data */
+ reg_cx=static_cast<Bit16s>(mouse.mickey_x);
+ reg_dx=static_cast<Bit16s>(mouse.mickey_y);
+ mouse.mickey_x=0;
+ mouse.mickey_y=0;
+ break;
+ case 0x0c: /* Define interrupt subroutine parameters */
+ mouse.sub_mask=reg_cx;
+ mouse.sub_seg=SegValue(es);
+ mouse.sub_ofs=reg_dx;
+ Mouse_AutoLock(true); //Some games don't seem to reset the mouse before using
+ break;
+ case 0x0f: /* Define mickey/pixel rate */
+ Mouse_SetMickeyPixelRate(reg_cx,reg_dx);
+ break;
+ case 0x10: /* Define screen region for updating */
+ mouse.updateRegion_x[0]=(Bit16s)reg_cx;
+ mouse.updateRegion_y[0]=(Bit16s)reg_dx;
+ mouse.updateRegion_x[1]=(Bit16s)reg_si;
+ mouse.updateRegion_y[1]=(Bit16s)reg_di;
+ DrawCursor();
+ break;
+ case 0x11: /* Get number of buttons */
+ reg_ax=0xffff;
+ reg_bx=MOUSE_BUTTONS;
+ break;
+ case 0x13: /* Set double-speed threshold */
+ mouse.doubleSpeedThreshold=(reg_bx ? reg_bx : 64);
+ break;
+ case 0x14: /* Exchange event-handler */
+ {
+ Bit16u oldSeg = mouse.sub_seg;
+ Bit16u oldOfs = mouse.sub_ofs;
+ Bit16u oldMask= mouse.sub_mask;
+ // Set new values
+ mouse.sub_mask= reg_cx;
+ mouse.sub_seg = SegValue(es);
+ mouse.sub_ofs = reg_dx;
+ // Return old values
+ reg_cx = oldMask;
+ reg_dx = oldOfs;
+ SegSet16(es,oldSeg);
+ }
+ break;
+ case 0x15: /* Get Driver storage space requirements */
+ reg_bx = sizeof(mouse);
+ break;
+ case 0x16: /* Save driver state */
+ {
+ LOG(LOG_MOUSE,LOG_WARN)("Saving driver state...");
+ PhysPt dest = SegPhys(es)+reg_dx;
+ MEM_BlockWrite(dest, &mouse, sizeof(mouse));
+ }
+ break;
+ case 0x17: /* load driver state */
+ {
+ LOG(LOG_MOUSE,LOG_WARN)("Loading driver state...");
+ PhysPt src = SegPhys(es)+reg_dx;
+ MEM_BlockRead(src, &mouse, sizeof(mouse));
+ }
+ break;
+ case 0x1a: /* Set mouse sensitivity */
+ // ToDo : double mouse speed value
+ Mouse_SetSensitivity(reg_bx,reg_cx,reg_dx);
+
+ LOG(LOG_MOUSE,LOG_WARN)("Set sensitivity used with %d %d (%d)",reg_bx,reg_cx,reg_dx);
+ break;
+ case 0x1b: /* Get mouse sensitivity */
+ reg_bx = mouse.senv_x_val;
+ reg_cx = mouse.senv_y_val;
+ reg_dx = mouse.dspeed_val;
+
+ LOG(LOG_MOUSE,LOG_WARN)("Get sensitivity %d %d",reg_bx,reg_cx);
+ break;
+ case 0x1c: /* Set interrupt rate */
+ /* Can't really set a rate this is host determined */
+ break;
+ case 0x1d: /* Set display page number */
+ mouse.page=reg_bl;
+ break;
+ case 0x1e: /* Get display page number */
+ reg_bx=mouse.page;
+ break;
+ case 0x1f: /* Disable Mousedriver */
+ /* ES:BX old mouse driver Zero at the moment TODO */
+ reg_bx=0;
+ SegSet16(es,0);
+ mouse.enabled=false; /* Just for reporting not doing a thing with it */
+ mouse.oldhidden=mouse.hidden;
+ mouse.hidden=1;
+ break;
+ case 0x20: /* Enable Mousedriver */
+ mouse.enabled=true;
+ mouse.hidden=mouse.oldhidden;
+ break;
+ case 0x22: /* Set language for messages */
+ /*
+ * Values for mouse driver language:
+ *
+ * 00h English
+ * 01h French
+ * 02h Dutch
+ * 03h German
+ * 04h Swedish
+ * 05h Finnish
+ * 06h Spanish
+ * 07h Portugese
+ * 08h Italian
+ *
+ */
+ mouse.language=reg_bx;
+ break;
+ case 0x23: /* Get language for messages */
+ reg_bx=mouse.language;
+ break;
+ case 0x24: /* Get Software version and mouse type */
+ reg_bx=0x805; //Version 8.05 woohoo
+ reg_ch=0x04; /* PS/2 type */
+ reg_cl=0; /* PS/2 (unused) */
+ break;
+ case 0x26: /* Get Maximum virtual coordinates */
+ reg_bx=(mouse.enabled ? 0x0000 : 0xffff);
+ reg_cx=(Bit16u)mouse.max_x;
+ reg_dx=(Bit16u)mouse.max_y;
+ break;
+ case 0x2a: /* Get cursor hot spot */
+ reg_al=(Bit8u)-mouse.hidden; // Microsoft uses a negative byte counter for cursor visibility
+ reg_bx=(Bit16u)mouse.hotx;
+ reg_cx=(Bit16u)mouse.hoty;
+ reg_dx=0x04; // PS/2 mouse type
+ break;
+ case 0x31: /* Get Current Minimum/Maximum virtual coordinates */
+ reg_ax=(Bit16u)mouse.min_x;
+ reg_bx=(Bit16u)mouse.min_y;
+ reg_cx=(Bit16u)mouse.max_x;
+ reg_dx=(Bit16u)mouse.max_y;
+ break;
+ default:
+ LOG(LOG_MOUSE,LOG_ERROR)("Mouse Function %04X not implemented!",reg_ax);
+ break;
+ }
+ return CBRET_NONE;
+}
+
+static Bitu MOUSE_BD_Handler(void) {
+ // the stack contains offsets to register values
+ Bit16u raxpt=real_readw(SegValue(ss),reg_sp+0x0a);
+ Bit16u rbxpt=real_readw(SegValue(ss),reg_sp+0x08);
+ Bit16u rcxpt=real_readw(SegValue(ss),reg_sp+0x06);
+ Bit16u rdxpt=real_readw(SegValue(ss),reg_sp+0x04);
+
+ // read out the actual values, registers ARE overwritten
+ Bit16u rax=real_readw(SegValue(ds),raxpt);
+ reg_ax=rax;
+ reg_bx=real_readw(SegValue(ds),rbxpt);
+ reg_cx=real_readw(SegValue(ds),rcxpt);
+ reg_dx=real_readw(SegValue(ds),rdxpt);
+// LOG_MSG("MOUSE BD: %04X %X %X %X %d %d",reg_ax,reg_bx,reg_cx,reg_dx,POS_X,POS_Y);
+
+ // some functions are treated in a special way (additional registers)
+ switch (rax) {
+ case 0x09: /* Define GFX Cursor */
+ case 0x16: /* Save driver state */
+ case 0x17: /* load driver state */
+ SegSet16(es,SegValue(ds));
+ break;
+ case 0x0c: /* Define interrupt subroutine parameters */
+ case 0x14: /* Exchange event-handler */
+ if (reg_bx!=0) SegSet16(es,reg_bx);
+ else SegSet16(es,SegValue(ds));
+ break;
+ case 0x10: /* Define screen region for updating */
+ reg_cx=real_readw(SegValue(ds),rdxpt);
+ reg_dx=real_readw(SegValue(ds),rdxpt+2);
+ reg_si=real_readw(SegValue(ds),rdxpt+4);
+ reg_di=real_readw(SegValue(ds),rdxpt+6);
+ break;
+ default:
+ break;
+ }
+
+ INT33_Handler();
+
+ // save back the registers, too
+ real_writew(SegValue(ds),raxpt,reg_ax);
+ real_writew(SegValue(ds),rbxpt,reg_bx);
+ real_writew(SegValue(ds),rcxpt,reg_cx);
+ real_writew(SegValue(ds),rdxpt,reg_dx);
+ switch (rax) {
+ case 0x1f: /* Disable Mousedriver */
+ real_writew(SegValue(ds),rbxpt,SegValue(es));
+ break;
+ case 0x14: /* Exchange event-handler */
+ real_writew(SegValue(ds),rcxpt,SegValue(es));
+ break;
+ default:
+ break;
+ }
+
+ reg_ax=rax;
+ return CBRET_NONE;
+}
+
+static Bitu INT74_Handler(void) {
+ if (mouse.events>0 && !mouse.in_UIR) {
+ mouse.events--;
+ /* Check for an active Interrupt Handler that will get called */
+ if (mouse.sub_mask & mouse.event_queue[mouse.events].type) {
+ reg_ax=mouse.event_queue[mouse.events].type;
+ reg_bx=mouse.event_queue[mouse.events].buttons;
+ reg_cx=POS_X;
+ reg_dx=POS_Y;
+ reg_si=static_cast<Bit16s>(mouse.mickey_x);
+ reg_di=static_cast<Bit16s>(mouse.mickey_y);
+ CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
+ CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback))+7);
+ CPU_Push16(RealSeg(uir_callback));
+ CPU_Push16(RealOff(uir_callback));
+ CPU_Push16(mouse.sub_seg);
+ CPU_Push16(mouse.sub_ofs);
+ mouse.in_UIR = true;
+ //LOG(LOG_MOUSE,LOG_ERROR)("INT 74 %X",mouse.event_queue[mouse.events].type );
+ } else if (useps2callback) {
+ CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
+ CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback)));
+ DoPS2Callback(mouse.event_queue[mouse.events].buttons, static_cast<Bit16s>(mouse.x), static_cast<Bit16s>(mouse.y));
+ } else {
+ SegSet16(cs, RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
+ reg_ip = RealOff(CALLBACK_RealPointer(int74_ret_callback));
+ //LOG(LOG_MOUSE,LOG_ERROR)("INT 74 not interested");
+ }
+ } else {
+ SegSet16(cs, RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
+ reg_ip = RealOff(CALLBACK_RealPointer(int74_ret_callback));
+ //LOG(LOG_MOUSE,LOG_ERROR)("INT 74 no events");
+ }
+ return CBRET_NONE;
+}
+
+Bitu INT74_Ret_Handler(void) {
+ if (mouse.events) {
+ if (!mouse.timer_in_progress) {
+ mouse.timer_in_progress = true;
+ PIC_AddEvent(MOUSE_Limit_Events,MOUSE_DELAY);
+ }
+ }
+ return CBRET_NONE;
+}
+
+Bitu UIR_Handler(void) {
+ mouse.in_UIR = false;
+ return CBRET_NONE;
+}
+
+void MOUSE_Init(Section* /*sec*/) {
+ // Callback for mouse interrupt 0x33
+ call_int33=CALLBACK_Allocate();
+// RealPt i33loc=RealMake(CB_SEG+1,(call_int33*CB_SIZE)-0x10);
+ RealPt i33loc=RealMake(DOS_GetMemory(0x1)-1,0x10);
+ CALLBACK_Setup(call_int33,&INT33_Handler,CB_MOUSE,Real2Phys(i33loc),"Mouse");
+ // Wasteland needs low(seg(int33))!=0 and low(ofs(int33))!=0
+ real_writed(0,0x33<<2,i33loc);
+
+ call_mouse_bd=CALLBACK_Allocate();
+ CALLBACK_Setup(call_mouse_bd,&MOUSE_BD_Handler,CB_RETF8,
+ PhysMake(RealSeg(i33loc),RealOff(i33loc)+2),"MouseBD");
+ // pseudocode for CB_MOUSE (including the special backdoor entry point):
+ // jump near i33hd
+ // callback MOUSE_BD_Handler
+ // retf 8
+ // label i33hd:
+ // callback INT33_Handler
+ // iret
+
+
+ // Callback for ps2 irq
+ call_int74=CALLBACK_Allocate();
+ CALLBACK_Setup(call_int74,&INT74_Handler,CB_IRQ12,"int 74");
+ // pseudocode for CB_IRQ12:
+ // sti
+ // push ds
+ // push es
+ // pushad
+ // callback INT74_Handler
+ // ps2 or user callback if requested
+ // otherwise jumps to CB_IRQ12_RET
+ // push ax
+ // mov al, 0x20
+ // out 0xa0, al
+ // out 0x20, al
+ // pop ax
+ // cld
+ // retf
+
+ int74_ret_callback=CALLBACK_Allocate();
+ CALLBACK_Setup(int74_ret_callback,&INT74_Ret_Handler,CB_IRQ12_RET,"int 74 ret");
+ // pseudocode for CB_IRQ12_RET:
+ // cli
+ // mov al, 0x20
+ // out 0xa0, al
+ // out 0x20, al
+ // callback INT74_Ret_Handler
+ // popad
+ // pop es
+ // pop ds
+ // iret
+
+ Bit8u hwvec=(MOUSE_IRQ>7)?(0x70+MOUSE_IRQ-8):(0x8+MOUSE_IRQ);
+ RealSetVec(hwvec,CALLBACK_RealPointer(call_int74));
+
+ // Callback for ps2 user callback handling
+ useps2callback = false; ps2callbackinit = false;
+ call_ps2=CALLBACK_Allocate();
+ CALLBACK_Setup(call_ps2,&PS2_Handler,CB_RETF,"ps2 bios callback");
+ ps2_callback=CALLBACK_RealPointer(call_ps2);
+
+ // Callback for mouse user routine return
+ call_uir=CALLBACK_Allocate();
+ CALLBACK_Setup(call_uir,&UIR_Handler,CB_RETF_CLI,"mouse uir ret");
+ uir_callback=CALLBACK_RealPointer(call_uir);
+
+ memset(&mouse,0,sizeof(mouse));
+ mouse.hidden = 1; //Hide mouse on startup
+ mouse.timer_in_progress = false;
+ mouse.mode = 0xFF; //Non existing mode
+
+ mouse.sub_mask=0;
+ mouse.sub_seg=0x6362; // magic value
+ mouse.sub_ofs=0;
+
+ Mouse_ResetHardware();
+ Mouse_Reset();
+ Mouse_SetSensitivity(50,50,50);
+}
diff --git a/src/ints/xms.cpp b/src/ints/xms.cpp
new file mode 100644
index 000000000..1e0bf899b
--- /dev/null
+++ b/src/ints/xms.cpp
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include "dosbox.h"
+#include "callback.h"
+#include "mem.h"
+#include "regs.h"
+#include "dos_inc.h"
+#include "setup.h"
+#include "inout.h"
+#include "xms.h"
+#include "bios.h"
+
+#define XMS_HANDLES 50 /* 50 XMS Memory Blocks */
+#define XMS_VERSION 0x0300 /* version 3.00 */
+#define XMS_DRIVER_VERSION 0x0301 /* my driver version 3.01 */
+
+#define XMS_GET_VERSION 0x00
+#define XMS_ALLOCATE_HIGH_MEMORY 0x01
+#define XMS_FREE_HIGH_MEMORY 0x02
+#define XMS_GLOBAL_ENABLE_A20 0x03
+#define XMS_GLOBAL_DISABLE_A20 0x04
+#define XMS_LOCAL_ENABLE_A20 0x05
+#define XMS_LOCAL_DISABLE_A20 0x06
+#define XMS_QUERY_A20 0x07
+#define XMS_QUERY_FREE_EXTENDED_MEMORY 0x08
+#define XMS_ALLOCATE_EXTENDED_MEMORY 0x09
+#define XMS_FREE_EXTENDED_MEMORY 0x0a
+#define XMS_MOVE_EXTENDED_MEMORY_BLOCK 0x0b
+#define XMS_LOCK_EXTENDED_MEMORY_BLOCK 0x0c
+#define XMS_UNLOCK_EXTENDED_MEMORY_BLOCK 0x0d
+#define XMS_GET_EMB_HANDLE_INFORMATION 0x0e
+#define XMS_RESIZE_EXTENDED_MEMORY_BLOCK 0x0f
+#define XMS_ALLOCATE_UMB 0x10
+#define XMS_DEALLOCATE_UMB 0x11
+#define XMS_QUERY_ANY_FREE_MEMORY 0x88
+#define XMS_ALLOCATE_ANY_MEMORY 0x89
+#define XMS_GET_EMB_HANDLE_INFORMATION_EXT 0x8e
+#define XMS_RESIZE_ANY_EXTENDED_MEMORY_BLOCK 0x8f
+
+#define XMS_FUNCTION_NOT_IMPLEMENTED 0x80
+#define HIGH_MEMORY_NOT_EXIST 0x90
+#define HIGH_MEMORY_IN_USE 0x91
+#define HIGH_MEMORY_NOT_ALLOCATED 0x93
+#define XMS_OUT_OF_SPACE 0xa0
+#define XMS_OUT_OF_HANDLES 0xa1
+#define XMS_INVALID_HANDLE 0xa2
+#define XMS_INVALID_SOURCE_HANDLE 0xa3
+#define XMS_INVALID_SOURCE_OFFSET 0xa4
+#define XMS_INVALID_DEST_HANDLE 0xa5
+#define XMS_INVALID_DEST_OFFSET 0xa6
+#define XMS_INVALID_LENGTH 0xa7
+#define XMS_BLOCK_NOT_LOCKED 0xaa
+#define XMS_BLOCK_LOCKED 0xab
+#define UMB_ONLY_SMALLER_BLOCK 0xb0
+#define UMB_NO_BLOCKS_AVAILABLE 0xb1
+
+struct XMS_Block {
+ Bitu size;
+ MemHandle mem;
+ Bit8u locked;
+ bool free;
+};
+
+#ifdef _MSC_VER
+#pragma pack (1)
+#endif
+struct XMS_MemMove{
+ Bit32u length;
+ Bit16u src_handle;
+ union {
+ RealPt realpt;
+ Bit32u offset;
+ } src;
+ Bit16u dest_handle;
+ union {
+ RealPt realpt;
+ Bit32u offset;
+ } dest;
+
+} GCC_ATTRIBUTE(packed);
+#ifdef _MSC_VER
+#pragma pack ()
+#endif
+
+
+Bitu XMS_EnableA20(bool enable) {
+ Bit8u val = IO_Read (0x92);
+ if (enable) IO_Write(0x92,val | 2);
+ else IO_Write(0x92,val & ~2);
+ return 0;
+}
+
+Bitu XMS_GetEnabledA20(void) {
+ return (IO_Read(0x92)&2)>0;
+}
+
+static RealPt xms_callback;
+static bool umb_available;
+
+static XMS_Block xms_handles[XMS_HANDLES];
+
+static INLINE bool InvalidHandle(Bitu handle) {
+ return (!handle || (handle>=XMS_HANDLES) || xms_handles[handle].free);
+}
+
+Bitu XMS_QueryFreeMemory(Bit16u& largestFree, Bit16u& totalFree) {
+ /* Scan the tree for free memory and find largest free block */
+ totalFree=(Bit16u)(MEM_FreeTotal()*4);
+ largestFree=(Bit16u)(MEM_FreeLargest()*4);
+ if (!totalFree) return XMS_OUT_OF_SPACE;
+ return 0;
+}
+
+Bitu XMS_AllocateMemory(Bitu size, Bit16u& handle) { // size = kb
+ /* Find free handle */
+ Bit16u index=1;
+ while (!xms_handles[index].free) {
+ if (++index>=XMS_HANDLES) return XMS_OUT_OF_HANDLES;
+ }
+ MemHandle mem;
+ if (size!=0) {
+ Bitu pages=(size/4) + ((size & 3) ? 1 : 0);
+ mem=MEM_AllocatePages(pages,true);
+ if (!mem) return XMS_OUT_OF_SPACE;
+ } else {
+ mem=MEM_GetNextFreePage();
+ if (mem==0) LOG(LOG_MISC,LOG_ERROR)("XMS:Allocate zero pages with no memory left");
+ }
+ xms_handles[index].free=false;
+ xms_handles[index].mem=mem;
+ xms_handles[index].locked=0;
+ xms_handles[index].size=size;
+ handle=index;
+ return 0;
+}
+
+Bitu XMS_FreeMemory(Bitu handle) {
+ if (InvalidHandle(handle)) return XMS_INVALID_HANDLE;
+ MEM_ReleasePages(xms_handles[handle].mem);
+ xms_handles[handle].mem=-1;
+ xms_handles[handle].size=0;
+ xms_handles[handle].free=true;
+ return 0;
+}
+
+Bitu XMS_MoveMemory(PhysPt bpt) {
+ /* Read the block with mem_read's */
+ Bitu length=mem_readd(bpt+offsetof(XMS_MemMove,length));
+ Bitu src_handle=mem_readw(bpt+offsetof(XMS_MemMove,src_handle));
+ union {
+ RealPt realpt;
+ Bit32u offset;
+ } src,dest;
+ src.offset=mem_readd(bpt+offsetof(XMS_MemMove,src.offset));
+ Bitu dest_handle=mem_readw(bpt+offsetof(XMS_MemMove,dest_handle));
+ dest.offset=mem_readd(bpt+offsetof(XMS_MemMove,dest.offset));
+ PhysPt srcpt,destpt;
+ if (src_handle) {
+ if (InvalidHandle(src_handle)) {
+ return XMS_INVALID_SOURCE_HANDLE;
+ }
+ if (src.offset>=(xms_handles[src_handle].size*1024U)) {
+ return XMS_INVALID_SOURCE_OFFSET;
+ }
+ if (length>xms_handles[src_handle].size*1024U-src.offset) {
+ return XMS_INVALID_LENGTH;
+ }
+ srcpt=(xms_handles[src_handle].mem*4096)+src.offset;
+ } else {
+ srcpt=Real2Phys(src.realpt);
+ }
+ if (dest_handle) {
+ if (InvalidHandle(dest_handle)) {
+ return XMS_INVALID_DEST_HANDLE;
+ }
+ if (dest.offset>=(xms_handles[dest_handle].size*1024U)) {
+ return XMS_INVALID_DEST_OFFSET;
+ }
+ if (length>xms_handles[dest_handle].size*1024U-dest.offset) {
+ return XMS_INVALID_LENGTH;
+ }
+ destpt=(xms_handles[dest_handle].mem*4096)+dest.offset;
+ } else {
+ destpt=Real2Phys(dest.realpt);
+ }
+// LOG_MSG("XMS move src %X dest %X length %X",srcpt,destpt,length);
+ mem_memcpy(destpt,srcpt,length);
+ return 0;
+}
+
+Bitu XMS_LockMemory(Bitu handle, Bit32u& address) {
+ if (InvalidHandle(handle)) return XMS_INVALID_HANDLE;
+ if (xms_handles[handle].locked<255) xms_handles[handle].locked++;
+ address = xms_handles[handle].mem*4096;
+ return 0;
+}
+
+Bitu XMS_UnlockMemory(Bitu handle) {
+ if (InvalidHandle(handle)) return XMS_INVALID_HANDLE;
+ if (xms_handles[handle].locked) {
+ xms_handles[handle].locked--;
+ return 0;
+ }
+ return XMS_BLOCK_NOT_LOCKED;
+}
+
+Bitu XMS_GetHandleInformation(Bitu handle, Bit8u& lockCount, Bit8u& numFree, Bit16u& size) {
+ if (InvalidHandle(handle)) return XMS_INVALID_HANDLE;
+ lockCount = xms_handles[handle].locked;
+ /* Find available blocks */
+ numFree=0;
+ for (Bitu i=1;i<XMS_HANDLES;i++) {
+ if (xms_handles[i].free) numFree++;
+ }
+ size=(Bit16u)(xms_handles[handle].size);
+ return 0;
+}
+
+Bitu XMS_ResizeMemory(Bitu handle, Bitu newSize) {
+ if (InvalidHandle(handle)) return XMS_INVALID_HANDLE;
+ // Block has to be unlocked
+ if (xms_handles[handle].locked>0) return XMS_BLOCK_LOCKED;
+ Bitu pages=newSize/4 + ((newSize & 3) ? 1 : 0);
+ if (MEM_ReAllocatePages(xms_handles[handle].mem,pages,true)) {
+ xms_handles[handle].size = newSize;
+ return 0;
+ } else return XMS_OUT_OF_SPACE;
+}
+
+static bool multiplex_xms(void) {
+ switch (reg_ax) {
+ case 0x4300: /* XMS installed check */
+ reg_al=0x80;
+ return true;
+ case 0x4310: /* XMS handler seg:offset */
+ SegSet16(es,RealSeg(xms_callback));
+ reg_bx=RealOff(xms_callback);
+ return true;
+ }
+ return false;
+
+}
+
+INLINE void SET_RESULT(Bitu res,bool touch_bl_on_succes=true) {
+ if(touch_bl_on_succes || res) reg_bl = (Bit8u)res;
+ reg_ax = (res==0);
+}
+
+Bitu XMS_Handler(void) {
+// LOG(LOG_MISC,LOG_ERROR)("XMS: CALL %02X",reg_ah);
+ switch (reg_ah) {
+ case XMS_GET_VERSION: /* 00 */
+ reg_ax=XMS_VERSION;
+ reg_bx=XMS_DRIVER_VERSION;
+ reg_dx=0; /* No we don't have HMA */
+ break;
+ case XMS_ALLOCATE_HIGH_MEMORY: /* 01 */
+ reg_ax=0;
+ reg_bl=HIGH_MEMORY_NOT_EXIST;
+ break;
+ case XMS_FREE_HIGH_MEMORY: /* 02 */
+ reg_ax=0;
+ reg_bl=HIGH_MEMORY_NOT_EXIST;
+ break;
+
+ case XMS_GLOBAL_ENABLE_A20: /* 03 */
+ case XMS_LOCAL_ENABLE_A20: /* 05 */
+ SET_RESULT(XMS_EnableA20(true));
+ break;
+ case XMS_GLOBAL_DISABLE_A20: /* 04 */
+ case XMS_LOCAL_DISABLE_A20: /* 06 */
+ SET_RESULT(XMS_EnableA20(false));
+ break;
+ case XMS_QUERY_A20: /* 07 */
+ reg_ax = XMS_GetEnabledA20();
+ reg_bl = 0;
+ break;
+ case XMS_QUERY_FREE_EXTENDED_MEMORY: /* 08 */
+ reg_bl = XMS_QueryFreeMemory(reg_ax,reg_dx);
+ break;
+ case XMS_ALLOCATE_ANY_MEMORY: /* 89 */
+ reg_edx &= 0xffff;
+ // fall through
+ case XMS_ALLOCATE_EXTENDED_MEMORY: /* 09 */
+ {
+ Bit16u handle = 0;
+ SET_RESULT(XMS_AllocateMemory(reg_dx,handle));
+ reg_dx = handle;
+ }; break;
+ case XMS_FREE_EXTENDED_MEMORY: /* 0a */
+ SET_RESULT(XMS_FreeMemory(reg_dx));
+ break;
+ case XMS_MOVE_EXTENDED_MEMORY_BLOCK: /* 0b */
+ SET_RESULT(XMS_MoveMemory(SegPhys(ds)+reg_si),false);
+ break;
+ case XMS_LOCK_EXTENDED_MEMORY_BLOCK: { /* 0c */
+ Bit32u address;
+ Bitu res = XMS_LockMemory(reg_dx, address);
+ if(res) reg_bl = (Bit8u)res;
+ reg_ax = (res==0);
+ if (res==0) { // success
+ reg_bx=(Bit16u)(address & 0xFFFF);
+ reg_dx=(Bit16u)(address >> 16);
+ };
+ }; break;
+ case XMS_UNLOCK_EXTENDED_MEMORY_BLOCK: /* 0d */
+ SET_RESULT(XMS_UnlockMemory(reg_dx));
+ break;
+ case XMS_GET_EMB_HANDLE_INFORMATION: /* 0e */
+ SET_RESULT(XMS_GetHandleInformation(reg_dx,reg_bh,reg_bl,reg_dx),false);
+ break;
+ case XMS_RESIZE_ANY_EXTENDED_MEMORY_BLOCK: /* 0x8f */
+ if(reg_ebx > reg_bx) LOG_MSG("64MB memory limit!");
+ //fall through
+ case XMS_RESIZE_EXTENDED_MEMORY_BLOCK: /* 0f */
+ SET_RESULT(XMS_ResizeMemory(reg_dx, reg_bx));
+ break;
+ case XMS_ALLOCATE_UMB: { /* 10 */
+ if (!umb_available) {
+ reg_ax=0;
+ reg_bl=XMS_FUNCTION_NOT_IMPLEMENTED;
+ break;
+ }
+ Bit16u umb_start=dos_infoblock.GetStartOfUMBChain();
+ if (umb_start==0xffff) {
+ reg_ax=0;
+ reg_bl=UMB_NO_BLOCKS_AVAILABLE;
+ reg_dx=0; // no upper memory available
+ break;
+ }
+ /* Save status and linkage of upper UMB chain and link upper
+ memory to the regular MCB chain */
+ Bit8u umb_flag=dos_infoblock.GetUMBChainState();
+ if ((umb_flag&1)==0) DOS_LinkUMBsToMemChain(1);
+ Bit8u old_memstrat=DOS_GetMemAllocStrategy()&0xff;
+ DOS_SetMemAllocStrategy(0x40); // search in UMBs only
+
+ Bit16u size=reg_dx;Bit16u seg;
+ if (DOS_AllocateMemory(&seg,&size)) {
+ reg_ax=1;
+ reg_bx=seg;
+ } else {
+ reg_ax=0;
+ if (size==0) reg_bl=UMB_NO_BLOCKS_AVAILABLE;
+ else reg_bl=UMB_ONLY_SMALLER_BLOCK;
+ reg_dx=size; // size of largest available UMB
+ }
+
+ /* Restore status and linkage of upper UMB chain */
+ Bit8u current_umb_flag=dos_infoblock.GetUMBChainState();
+ if ((current_umb_flag&1)!=(umb_flag&1)) DOS_LinkUMBsToMemChain(umb_flag);
+ DOS_SetMemAllocStrategy(old_memstrat);
+ }
+ break;
+ case XMS_DEALLOCATE_UMB: /* 11 */
+ if (!umb_available) {
+ reg_ax=0;
+ reg_bl=XMS_FUNCTION_NOT_IMPLEMENTED;
+ break;
+ }
+ if (dos_infoblock.GetStartOfUMBChain()!=0xffff) {
+ if (DOS_FreeMemory(reg_dx)) {
+ reg_ax=0x0001;
+ break;
+ }
+ }
+ reg_ax=0x0000;
+ reg_bl=UMB_NO_BLOCKS_AVAILABLE;
+ break;
+ case XMS_QUERY_ANY_FREE_MEMORY: /* 88 */
+ reg_bl = XMS_QueryFreeMemory(reg_ax,reg_dx);
+ reg_eax &= 0xffff;
+ reg_edx &= 0xffff;
+ reg_ecx = (MEM_TotalPages()*MEM_PAGESIZE)-1; // highest known physical memory address
+ break;
+ case XMS_GET_EMB_HANDLE_INFORMATION_EXT: { /* 8e */
+ Bit8u free_handles;
+ Bitu result = XMS_GetHandleInformation(reg_dx,reg_bh,free_handles,reg_dx);
+ if (result != 0) reg_bl = result;
+ else {
+ reg_edx &= 0xffff;
+ reg_cx = free_handles;
+ }
+ reg_ax = (result==0);
+ } break;
+ default:
+ LOG(LOG_MISC,LOG_ERROR)("XMS: unknown function %02X",reg_ah);
+ reg_ax=0;
+ reg_bl=XMS_FUNCTION_NOT_IMPLEMENTED;
+ }
+// LOG(LOG_MISC,LOG_ERROR)("XMS: CALL Result: %02X",reg_bl);
+ return CBRET_NONE;
+}
+
+Bitu GetEMSType(Section_prop * section);
+
+class XMS: public Module_base {
+private:
+ CALLBACK_HandlerObject callbackhandler;
+public:
+ XMS(Section* configuration):Module_base(configuration){
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+ umb_available=false;
+ if (!section->Get_bool("xms")) return;
+ Bitu i;
+ BIOS_ZeroExtendedSize(true);
+ DOS_AddMultiplexHandler(multiplex_xms);
+
+ /* place hookable callback in writable memory area */
+ xms_callback=RealMake(DOS_GetMemory(0x1)-1,0x10);
+ callbackhandler.Install(&XMS_Handler,CB_HOOKABLE,Real2Phys(xms_callback),"XMS Handler");
+ // pseudocode for CB_HOOKABLE:
+ // jump near skip
+ // nop,nop,nop
+ // label skip:
+ // callback XMS_Handler
+ // retf
+
+ for (i=0;i<XMS_HANDLES;i++) {
+ xms_handles[i].free=true;
+ xms_handles[i].mem=-1;
+ xms_handles[i].size=0;
+ xms_handles[i].locked=0;
+ }
+ /* Disable the 0 handle */
+ xms_handles[0].free = false;
+
+ /* Set up UMB chain */
+ umb_available=section->Get_bool("umb");
+ bool ems_available = GetEMSType(section)>0;
+ DOS_BuildUMBChain(section->Get_bool("umb"),ems_available);
+ }
+
+ ~XMS(){
+ Section_prop * section = static_cast<Section_prop *>(m_configuration);
+ /* Remove upper memory information */
+ dos_infoblock.SetStartOfUMBChain(0xffff);
+ if (umb_available) {
+ dos_infoblock.SetUMBChainState(0);
+ umb_available=false;
+ }
+
+ if (!section->Get_bool("xms")) return;
+ /* Undo biosclearing */
+ BIOS_ZeroExtendedSize(false);
+
+ /* Remove Multiplex */
+ DOS_DelMultiplexHandler(multiplex_xms);
+
+ /* Free used memory while skipping the 0 handle */
+ for (Bitu i = 1;i<XMS_HANDLES;i++)
+ if(!xms_handles[i].free) XMS_FreeMemory(i);
+ }
+
+};
+static XMS* test;
+
+void XMS_ShutDown(Section* /*sec*/) {
+ delete test;
+}
+
+void XMS_Init(Section* sec) {
+ test = new XMS(sec);
+ sec->AddDestroyFunction(&XMS_ShutDown,true);
+}
diff --git a/src/ints/xms.h b/src/ints/xms.h
new file mode 100644
index 000000000..d397c498d
--- /dev/null
+++ b/src/ints/xms.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef __XMS_H__
+#define __XMS_H__
+
+Bitu XMS_QueryFreeMemory (Bit16u& largestFree, Bit16u& totalFree);
+Bitu XMS_AllocateMemory (Bitu size, Bit16u& handle);
+Bitu XMS_FreeMemory (Bitu handle);
+Bitu XMS_MoveMemory (PhysPt bpt);
+Bitu XMS_LockMemory (Bitu handle, Bit32u& address);
+Bitu XMS_UnlockMemory (Bitu handle);
+Bitu XMS_GetHandleInformation(Bitu handle, Bit8u& lockCount, Bit8u& numFree, Bit16u& size);
+Bitu XMS_ResizeMemory (Bitu handle, Bitu newSize);
+
+Bitu XMS_EnableA20 (bool enable);
+Bitu XMS_GetEnabledA20 (void);
+
+#endif
diff --git a/src/libs/Makefile.am b/src/libs/Makefile.am
new file mode 100644
index 000000000..303bc370e
--- /dev/null
+++ b/src/libs/Makefile.am
@@ -0,0 +1,3 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+SUBDIRS = zmbv gui_tk
diff --git a/src/libs/gui_tk/Doxyfile b/src/libs/gui_tk/Doxyfile
new file mode 100644
index 000000000..598d6fef2
--- /dev/null
+++ b/src/libs/gui_tk/Doxyfile
@@ -0,0 +1,238 @@
+# Doxyfile 1.5.2
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+DOXYFILE_ENCODING = UTF-8
+PROJECT_NAME = gui::tk
+PROJECT_NUMBER = "Version 1.0"
+OUTPUT_DIRECTORY =
+CREATE_SUBDIRS = NO
+OUTPUT_LANGUAGE = English
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH = .
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = NO
+JAVADOC_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP = YES
+INHERIT_DOCS = YES
+SEPARATE_MEMBER_PAGES = NO
+TAB_SIZE = 8
+ALIASES =
+OPTIMIZE_OUTPUT_FOR_C = NO
+OPTIMIZE_OUTPUT_JAVA = NO
+BUILTIN_STL_SUPPORT = YES
+CPP_CLI_SUPPORT = NO
+DISTRIBUTE_GROUP_DOC = NO
+SUBGROUPING = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL = NO
+EXTRACT_PRIVATE = NO
+EXTRACT_STATIC = NO
+EXTRACT_LOCAL_CLASSES = YES
+EXTRACT_LOCAL_METHODS = NO
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+INTERNAL_DOCS = NO
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = NO
+SHOW_INCLUDE_FILES = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+SORT_BY_SCOPE_NAME = NO
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = YES
+SHOW_DIRECTORIES = YES
+FILE_VERSION_FILTER =
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = NO
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = .
+INPUT_ENCODING = UTF-8
+FILE_PATTERNS = gui_tk.h \
+ gui_tk.cpp
+RECURSIVE = NO
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXCLUDE_SYMBOLS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS = *
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = YES
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS = NO
+VERBATIM_HEADERS = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = NO
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = NO
+USE_PDFLATEX = NO
+LATEX_BATCHMODE = NO
+LATEX_HIDE_INDICES = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = NO
+XML_OUTPUT = xml
+XML_SCHEMA =
+XML_DTD =
+XML_PROGRAMLISTING = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD = NO
+PERLMOD_LATEX = NO
+PERLMOD_PRETTY = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED = TESTING SDL_MAJOR_VERSION
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE = DOSBox.tag
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+MSCGEN_PATH =
+HIDE_UNDOC_RELATIONS = YES
+HAVE_DOT = YES
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+GROUP_GRAPHS = YES
+UML_LOOK = YES
+TEMPLATE_RELATIONS = YES
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = YES
+CALLER_GRAPH = YES
+GRAPHICAL_HIERARCHY = YES
+DIRECTORY_GRAPH = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+DOT_GRAPH_MAX_NODES = 50
+DOT_TRANSPARENT = YES
+DOT_MULTI_TARGETS = YES
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
diff --git a/src/libs/gui_tk/Makefile.am b/src/libs/gui_tk/Makefile.am
new file mode 100644
index 000000000..de7764eda
--- /dev/null
+++ b/src/libs/gui_tk/Makefile.am
@@ -0,0 +1,4 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libgui_tk.a
+libgui_tk_a_SOURCES = gui_tk.cpp gui_tk.h
diff --git a/src/libs/gui_tk/gui_tk.cpp b/src/libs/gui_tk/gui_tk.cpp
new file mode 100644
index 000000000..8d80cd1e7
--- /dev/null
+++ b/src/libs/gui_tk/gui_tk.cpp
@@ -0,0 +1,1706 @@
+#if 0
+/*
+ * gui_tk - framework-agnostic GUI toolkit
+ * Copyright (C) 2005-2007 Jörg Walter
+ *
+ * gui_tk 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gui_tk 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, see <http://www.gnu.org/licenses/>
+ */
+
+/* TODO:
+ - make menu a bufferedwindow with shadow
+*/
+
+/** \file
+ * \brief Implementation file for gui_tk.
+ *
+ * It contains implementations for all non-inlined class methods.
+ *
+ * Also contained is a small test program that demonstrates all features of
+ * gui_tk. It is enabled by defining the preprocessor macro TESTING.
+ */
+
+#include <SDL.h>
+#include "gui_tk.h"
+
+namespace GUI {
+
+namespace Color {
+
+RGB Background3D = 0xffc0c0c0;
+
+RGB Light3D = 0xfffcfcfc;
+
+RGB Shadow3D = 0xff808080;
+
+RGB Border = 0xff000000;
+
+RGB Text = 0xff000000;
+
+RGB Background = 0xffc0c0c0;
+
+RGB SelectionBackground = 0xff000080;
+
+RGB SelectionForeground = 0xffffffff;
+
+RGB EditableBackground = 0xffffffff;
+
+RGB Titlebar = 0xff000080;
+
+RGB TitlebarText = 0xffffffff;
+
+}
+
+void Drawable::drawText(const String& text, bool interpret, Size start, Size len) {
+ if (interpret) {
+ if (len > text.size()-start) len = (Size)(text.size()-start);
+ len += start;
+
+ Size wordstart = start;
+ int width = 0;
+
+ while (start < len) {
+ switch (font->toSpecial(text[start])) {
+ case Font::CR:
+ if (wordstart != start) {
+ drawText(text,false,wordstart,start-wordstart);
+ wordstart = start;
+ width = 0;
+ }
+ wordstart++;
+ gotoXY(0,y);
+ break;
+ case Font::LF:
+ if (wordstart != start) {
+ drawText(text,false,wordstart,start-wordstart);
+ wordstart = start;
+ width = 0;
+ }
+ wordstart++;
+ gotoXY(0,y+font->getHeight());
+ break;
+ case Font::BS:
+ if (wordstart != start) {
+ drawText(text,false,wordstart,start-wordstart);
+ wordstart = start;
+ width = 0;
+ }
+ wordstart++;
+ gotoXY(imax(0,x-font->getWidth()),y);
+ break;
+ case Font::Tab:
+ if (wordstart != start) {
+ drawText(text,false,wordstart,start-wordstart);
+ wordstart = start;
+ width = 0;
+ }
+ wordstart++;
+ gotoXY((((int)(x/font->getWidth()/8))+1)*8*font->getWidth(),y);
+ break;
+ case Font::Space:
+ if (wordstart != start) {
+ drawText(text,false,wordstart,start-wordstart);
+ wordstart = start;
+ width = 0;
+ }
+ wordstart++;
+ font->drawString(this,text,start,1);
+ break;
+ case Font::ESC: // ignore ANSI sequences except for colors
+ if (wordstart != start) {
+ drawText(text,false,wordstart,start-wordstart);
+ wordstart = start;
+ width = 0;
+ }
+ wordstart++;
+ do {
+ int seqstart = start+1;
+ Char c;
+ do {
+ start++;
+ wordstart++;
+ c = font->toSpecial(text[start]);
+ } while (start < len && ((c >= '0' && c <= '9') || c == ';' || c == '['));
+ if (c == 'm' && start < len) {
+ if (font->toSpecial(text[seqstart++]) != '[') break;
+ c = font->toSpecial(text[seqstart++]);
+ while (c != 'm') {
+ int param = 0;
+ if (c == ';') c = '0';
+ while (c != 'm' && c != ';') {
+ param = param * 10 + c-'0';
+ c = font->toSpecial(text[seqstart++]);
+ }
+ const RGB bright = 0x00808080;
+ const RGB intensity = (color&bright?~0:~bright);
+ switch (param) {
+ case 0: setColor(Color::Black); break;
+ case 1: setColor(color | 0x00808080); break;
+ case 30: setColor((Color::Black|bright) & intensity); break;
+ case 31: setColor(Color::Red & intensity); break;
+ case 32: setColor(Color::Green & intensity); break;
+ case 33: setColor(Color::Yellow & intensity); break;
+ case 34: setColor(Color::Blue & intensity); break;
+ case 35: setColor(Color::Magenta & intensity); break;
+ case 36: setColor(Color::Cyan & intensity); break;
+ case 37: setColor(Color::White & intensity); break;
+ default: break;
+ }
+ }
+ }
+ } while (0);
+ default:
+ width += font->getWidth(text[start]);
+ if (x > 0 && x+width > cw) gotoXY(0,y+font->getHeight());
+ }
+ start++;
+ }
+ if (wordstart != start) drawText(text,false,wordstart,start-wordstart);
+ return;
+ }
+
+ font->drawString(this,text,start,len);
+}
+
+bool ToplevelWindow::mouseDown(int x, int y, MouseButton button) {
+ if (button == Left && x > 32 && x < width-6 && y > 4 && y < 31) {
+ dragx = x;
+ dragy = y;
+ mouseChild = NULL;
+ systemMenu->setVisible(false);
+ return true;
+ } else if (button == Left && x < 32 && x > 6 && y > 4 && y < 31) {
+ mouseChild = NULL;
+ raise();
+ systemMenu->setVisible(!systemMenu->isVisible());
+ return true;
+ }
+ systemMenu->setVisible(false);
+ BorderedWindow::mouseDown(x,y,button);
+ return true;
+}
+
+Drawable::Drawable(int w, int h, RGB clear) :
+ buffer(new RGB[w*h]),
+ width(w), height(h),
+ owner(true),
+ color(Color::Black),
+ font(NULL),
+ lineWidth(1),
+ tx(0), ty(0),
+ cx(0), cy(0),
+ cw(w), ch(h),
+ x(0), y(0)
+{
+ this->clear(clear);
+}
+
+Drawable::Drawable(Drawable &src, RGB clear) :
+ buffer(new RGB[src.cw*src.ch]),
+ width(src.cw), height(src.ch),
+ owner(true),
+ color(src.color),
+ font(src.font),
+ lineWidth(src.lineWidth),
+ tx(0), ty(0),
+ cx(0), cy(0),
+ cw(src.cw), ch(src.ch),
+ x(src.x), y(src.y)
+{
+ if (clear != 0) {
+ this->clear(clear);
+ } else {
+ for (int h = 0; h < src.ch; h++) {
+ memcpy(buffer+src.cw*h,src.buffer+src.width*(h+src.ty)+src.tx,4*src.cw);
+ }
+ }
+}
+
+Drawable::Drawable(Drawable &src, int x, int y, int w, int h) :
+ buffer(src.buffer),
+ width(src.width), height(src.height),
+ owner(false),
+ color(src.color),
+ font(src.font),
+ lineWidth(src.lineWidth),
+ tx(src.tx+x), ty(src.ty+y),
+ cx(imax(imax(-x,src.cx-x),0)), cy(imax(imax(-y,src.cy-y),0)),
+ cw(imax(0,imin(src.cw-x,w))), ch(imax(0,imin(src.ch-y,h))),
+ x(imax(0,imin(src.tx-tx,cw))), y(imax(0,imin(src.ty-ty,cw)))
+{
+}
+
+Drawable::~Drawable()
+{
+ if (owner) delete[] buffer;
+}
+
+void Drawable::clear(RGB clear)
+{
+ for (int y = cy; y < ch; y++) {
+ for (int x = cx; x < cw; x++) {
+ buffer[(y+ty)*width+x+tx] = clear;
+ }
+ }
+}
+
+void Drawable::drawLine(int x2, int y2)
+{
+ int x0 = x2, x1 = x, y0 = y2, y1 = y;
+ int dx = x2-x1, dy = y2-y1;
+ drawPixel();
+
+ if (abs(dx) > abs(dy)) {
+ if (x1 > x2) {
+ x = x2; x2 = x1; x1 = x;
+ y = y2; y2 = y1; y1 = y;
+ }
+ for (x = x1; x <= x2; x++) {
+ y = y1+(x-x1)*dy/dx-lineWidth/2;
+ for (int i = 0; i < lineWidth; i++, y++) {
+ drawPixel();
+ }
+ }
+ } else if (y1 != y2) {
+ if (y1 > y2) {
+ x = x2; x2 = x1; x1 = x;
+ y = y2; y2 = y1; y1 = y;
+ }
+ for (y = y1; y <= y2; y ++) {
+ x = x1+(y-y1)*dx/dy-lineWidth/2;
+ for (int i = 0; i < lineWidth; i++, x++) {
+ drawPixel();
+ }
+ }
+ }
+
+ drawPixel(x0,y0);
+}
+
+void Drawable::drawCircle(int d) {
+ int xo = 0, yo = d/2, rest = (d+1)/2-yo, x0 = x, y0 = y, rsq = d*d/4, lwo = lineWidth/2;
+
+ while (xo <= yo) {
+ while (xo*xo+(2*yo-1)*(2*yo-1)/4 > rsq) yo--;
+ for (int i = 0, yow = yo+lwo; i < lineWidth; i++, yow--) {
+ drawPixel(x0+xo,y0-yow-rest);
+ drawPixel(x0+yow,y0-xo-rest);
+ drawPixel(x0+yow,y0+xo);
+ drawPixel(x0+xo,y0+yow);
+
+ drawPixel(x0-xo-rest,y0-yow-rest);
+ drawPixel(x0-yow-rest,y0-xo-rest);
+ drawPixel(x0-yow-rest,y0+xo);
+ drawPixel(x0-xo-rest,y0+yow);
+ }
+
+ xo++;
+ }
+ gotoXY(x0,y0);
+}
+
+void Drawable::drawRect(int w, int h)
+{
+ gotoXY(x-lineWidth/2,y);
+ drawLine(x+w+lineWidth-1,y);
+ gotoXY(x-(lineWidth-1)/2,y);
+ drawLine(x,y+h);
+ gotoXY(x+(lineWidth-1)/2,y);
+ drawLine(x-w-lineWidth+1,y);
+ gotoXY(x+lineWidth/2,y);
+ drawLine(x,y-h);
+}
+
+void Drawable::fill()
+{
+ int x0 = x, xmin;
+ RGB color = getPixel();
+
+ if (color == this->color) return;
+
+ for (x--; x >= 0 && getPixel() == color; x--) drawPixel();
+ xmin = ++x;
+ for (x = x0; x < cw && getPixel() == color; x++) drawPixel();
+ y++;
+ for (x--; x >= xmin; x--) {
+ if (getPixel() == color) fill();
+ y -= 2;
+ if (getPixel() == color) fill();
+ y += 2;
+ }
+ y--;
+ x = x0;
+}
+
+void Drawable::fillCircle(int d)
+{
+ int xo = 0, yo = d/2, rest = (d+1)/2-yo, x0 = x, y0 = y, rsq = d*d/4;
+
+ while (xo <= yo) {
+ while (xo*xo+(2*yo-1)*(2*yo-1)/4 > rsq) yo--;
+ x = x0+xo;
+ for (y = y0-yo-rest; y <= y0+yo; y++) drawPixel();
+ x = x0-xo-rest;
+ for (y = y0-yo-rest; y <= y0+yo; y++) drawPixel();
+
+ y = y0-xo-rest;
+ for (x = x0-yo-rest; x <= x0+yo; x++) drawPixel();
+ y = y0+xo;
+ for (x = x0-yo-rest; x <= x0+yo; x++) drawPixel();
+
+ xo++;
+ }
+ gotoXY(x0,y0);
+}
+
+void Drawable::fillRect(int w, int h)
+{
+ int x0 = x, y0 = y, w0 = w;
+ for (; h > 0; h--, y++) {
+ for (x = x0, w = w0; w > 0; w--, x++) {
+ drawPixel();
+ }
+ }
+ gotoXY(x0,y0);
+}
+
+void Drawable::drawDrawable(Drawable &d, unsigned char alpha)
+{
+ int scw = d.cw, sch = d.ch, w, h;
+ RGB *src, *dest;
+
+ for (h = imax(d.cy,-ty-y); h < sch && y+h < ch; h++) {
+ src = d.buffer+d.width*(h+d.ty)+d.tx;
+ dest = buffer+width*(y+ty+h)+tx+x;
+ for (w = imax(d.cx,-tx-x); w < scw && x+w < cw; w++) {
+ RGB srcb = src[w], destb = dest[w];
+ unsigned int sop = Color::A(srcb)*((unsigned int)alpha)/255;
+ unsigned int rop = Color::A(destb) + sop - Color::A(destb)*sop/255;
+ if (rop == 0) {
+ dest[w] = Color::Transparent;
+ } else {
+ unsigned int dop = Color::A(destb)*(255-sop)/255;
+ unsigned int magval = ((destb&Color::MagentaMask)*dop+(srcb&Color::MagentaMask)*sop);
+ dest[w] = (((magval&0xffff)/rop)&Color::BlueMask) |
+ (((magval&0xffff0000)/rop)&Color::RedMask) |
+ ((((destb&Color::GreenMask)*dop+(srcb&Color::GreenMask)*sop)/rop)&Color::GreenMask) |
+ (rop<<Color::AlphaShift);
+ }
+ }
+ }
+}
+
+void Drawable::drawText(const Char c, bool interpret)
+{
+ if (interpret) {
+ switch (font->toSpecial(c)) {
+ case Font::CR: gotoXY(0,y); return;
+ case Font::LF: gotoXY(0,y+font->getHeight()); return;
+ case Font::BS: gotoXY(imax(0,x-font->getWidth()),y); return;
+ case Font::Tab: gotoXY((((int)(x/font->getWidth()/8))+1)*8*font->getWidth(),y); return;
+ default: break;
+ }
+ if (font->getWidth(c)+x > cw) gotoXY(0,y+font->getHeight());
+ }
+ font->drawChar(this,c);
+}
+
+#define move(x) (ptr += ((x)+bit)/8-(((x)+bit)<0), bit = ((x)+bit+(((x)+bit)<0?8:0))%8)
+void BitmapFont::drawChar(Drawable *d, const Char c) const {
+ const unsigned char *ptr = bitmap;
+ int bit = 0;
+
+ if (c > last) return;
+
+ if (char_position != NULL) {
+ ptr = char_position[c];
+ bit = 0;
+ } else {
+ move(character_step*((int)c));
+ }
+
+ int rs = row_step;
+ int w = (widths != NULL?widths[c]:width);
+ int h = (ascents != NULL?ascents[c]:height);
+ Drawable out(*d,d->getX(),d->getY()-ascent,w,h);
+
+ if (rs == 0) rs = isign(col_step)*w;
+ if (rs < 0) move(-rs*(h-1));
+ if (col_step < 0) move(abs(rs)-1);
+
+ for (int row = height-h; row < height; row++, move(rs-w*col_step)) {
+ for (int col = 0; col < w; col++, move(col_step)) {
+ if (!background_set ^ !(*ptr&(1<<bit)))
+ out.drawPixel(col,row);
+ }
+ }
+ d->gotoXY(d->getX()+w,d->getY());
+}
+#undef move
+
+std::map<const char *,Font *,Font::ltstr> Font::registry;
+
+void Timer::check(unsigned int ticks)
+{
+ if (timers.empty()) return;
+
+ if (Timer::ticks > (Timer::ticks+ticks)) {
+ ticks -= -1-Timer::ticks;
+ check(-1-Timer::ticks);
+ }
+
+ std::multimap<unsigned int,Timer_Callback*,Timer::ltuint>::iterator old, i = timers.lower_bound(Timer::ticks+1);
+ Timer::ticks += ticks;
+
+ while (i != timers.end() && (*i).first <= Timer::ticks) {
+ Timer_Callback *c = (*i).second;
+ unsigned int time = (*i).first;
+ old = i;
+ ++i;
+ timers.erase(old);
+ unsigned int next = c->timerExpired(time);
+ if (next) add(c, time+next-Timer::ticks);
+ }
+}
+
+void Timer::remove(const Timer_Callback *const timer)
+{
+ if (timers.empty()) return;
+
+ std::multimap<unsigned int,Timer_Callback*,Timer::ltuint>::iterator old, i = timers.begin();
+
+ while (i != timers.end()) {
+ old = i;
+ ++i;
+ if ((*old).second == timer) timers.erase(old);
+ }
+}
+
+unsigned int Timer::next()
+{
+ if (timers.empty()) return 0;
+
+ std::multimap<unsigned int,Timer_Callback*,Timer::ltuint>::iterator i = timers.upper_bound(ticks);
+
+ if (i == timers.end()) return 0;
+ return (*i).first-Timer::ticks;
+}
+
+std::multimap<unsigned int,Timer_Callback*,Timer::ltuint> Timer::timers;
+unsigned int Timer::ticks = 0;
+
+BitmapFont::BitmapFont(const unsigned char *data, int height, int ascent, bool owner,
+ int width, bool background_set,
+ int col_step, int row_step, int character_step, Char last,
+ const int *widths, const int *ascents, const unsigned char *const* char_position,
+ const Font::SpecialChar *special) :
+ bitmap(data),
+ width(width), height(height), ascent(ascent), widths(widths), ascents(ascents),
+ background_set(background_set), col_step(col_step), row_step(row_step),
+ character_step(character_step?character_step:abs((row_step?row_step:width*col_step)*height)),
+ char_position(char_position), special(special), owner(owner), last(last)
+{
+}
+
+BitmapFont::~BitmapFont() {
+ if (owner) {
+ if (bitmap != NULL) delete bitmap;
+ if (ascents != NULL) delete ascents;
+ if (widths != NULL) delete widths;
+ if (special != NULL) delete special;
+ }
+}
+
+Window::Window(Window *parent, int x, int y, int w, int h) :
+ width(w), height(h),
+ x(x), y(y),
+ dirty(true),
+ visible(true),
+ parent(parent),
+ mouseChild(NULL)
+{
+ parent->addChild(this);
+}
+
+Window::Window() :
+ width(0), height(0),
+ x(0), y(0),
+ dirty(false),
+ visible(true),
+ parent(NULL),
+ mouseChild(NULL)
+{
+}
+
+
+Window::~Window()
+{
+ while (!children.empty()) delete children.front();
+ if (parent) parent->removeChild(this);
+ if (parent && parent->mouseChild == this) parent->mouseChild = NULL;
+}
+
+void Window::addChild(Window *child)
+{
+ children.push_back(child);
+ setDirty();
+}
+
+void Window::removeChild(Window *child)
+{
+ children.remove(child);
+ setDirty();
+}
+
+void Window::move(int x, int y)
+{
+ this->x = x;
+ this->y = y;
+ std::list<Window_Callback*>::iterator i = movehandlers.begin();
+ bool end = (i == movehandlers.end());
+ while (!end) {
+ Window_Callback *c = *i;
+ ++i;
+ end = (i == movehandlers.end());
+ c->windowMoved(this,x,y);
+ }
+ parent->setDirty();
+}
+
+void Window::resize(int w, int h)
+{
+ this->width = w;
+ this->height = h;
+ setDirty();
+}
+
+void Window::paintAll(Drawable &d) const
+{
+ paint(d);
+ std::list<Window *>::const_iterator i = children.begin();
+ while (i != children.end()) {
+ Window *child = *i;
+ ++i;
+ if (child->visible) {
+ Drawable *cd = new Drawable(d,child->x,child->y,child->width,child->height);
+ child->paintAll(*cd);
+ delete cd;
+ }
+ }
+}
+
+bool Window::keyDown(const Key &key)
+{
+ if (children.empty()) return false;
+ if ((*children.rbegin())->keyDown(key)) return true;
+ if (key.ctrl || key.alt || key.windows || key.special != Key::Tab) return false;
+
+ if (key.shift) {
+ std::list<Window *>::reverse_iterator i = children.rbegin(), e = children.rend();
+ ++i;
+ while (i != e && !(*i)->raise()) ++i;
+ return i != e;
+ } else {
+ std::list<Window *>::iterator i = children.begin(), e = children.end();
+ while (i != e && !(*i)->raise()) ++i;
+ return (i != e);
+ }
+}
+
+bool Window::keyUp(const Key &key)
+{
+ if (children.empty()) return false;
+ return (*children.rbegin())->keyUp(key);
+}
+
+bool Window::mouseMoved(int x, int y)
+{
+ std::list<Window *>::reverse_iterator i = children.rbegin();
+ bool end = (i == children.rend());
+ while (!end) {
+ Window *w = *i;
+ i++;
+ end = (i == children.rend());
+ if (w->visible && x >= w->x && x <= w->x+w->width
+ && y >= w->y && y <= w->y+w->height
+ && w->mouseMoved(x-w->x, y-w->y)) return true;
+ }
+ return false;
+}
+
+bool Window::mouseDragged(int x, int y, MouseButton button)
+{
+ if (mouseChild == NULL) return false;
+ return mouseChild->mouseDragged(x-mouseChild->x, y-mouseChild->y, button);
+}
+
+bool Window::mouseDown(int x, int y, MouseButton button)
+{
+ Window *last = NULL;
+ std::list<Window *>::reverse_iterator i = children.rbegin();
+ bool end = (i == children.rend());
+ while (!end) {
+ Window *w = *i;
+ i++;
+ end = (i == children.rend());
+ if (w->visible && x >= w->x && x <= w->x+w->width
+ && y >= w->y && y <= w->y+w->height
+ && (mouseChild = last = w)
+ && w->mouseDown(x-w->x, y-w->y, button)
+ && w->raise()) {
+ return true;
+ }
+ }
+ mouseChild = NULL;
+ if (last != NULL) last->raise();
+ return false;
+}
+
+bool Window::mouseUp(int x, int y, MouseButton button)
+{
+ if (mouseChild == NULL) return false;
+ return mouseChild->mouseUp(x-mouseChild->x, y-mouseChild->y, button);
+}
+
+bool Window::mouseClicked(int x, int y, MouseButton button)
+{
+ if (mouseChild == NULL) return false;
+ return mouseChild->mouseClicked(x-mouseChild->x, y-mouseChild->y, button);
+}
+
+bool Window::mouseDoubleClicked(int x, int y, MouseButton button)
+{
+ if (mouseChild == NULL) return false;
+ return mouseChild->mouseDoubleClicked(x-mouseChild->x, y-mouseChild->y, button);
+}
+
+bool BorderedWindow::mouseDown(int x, int y, MouseButton button)
+{
+ mouseChild = NULL;
+ if (x > width-border_right || y > width-border_bottom) return false;
+ x -= border_left; y -= border_top;
+ if (x < 0 || y < 0) return false;
+ return Window::mouseDown(x,y,button);
+}
+
+bool BorderedWindow::mouseMoved(int x, int y)
+{
+ if (x > width-border_right || y > width-border_bottom) return false;
+ x -= border_left; y -= border_top;
+ if (x < 0 || y < 0) return false;
+ return Window::mouseMoved(x,y);
+}
+
+bool BorderedWindow::mouseDragged(int x, int y, MouseButton button)
+{
+ if (x > width-border_right || y > width-border_bottom) return false;
+ x -= border_left; y -= border_top;
+ if (x < 0 || y < 0) return false;
+ return Window::mouseDragged(x,y,button);
+}
+
+void ToplevelWindow::paint(Drawable &d) const
+{
+ int mask = (systemMenu->isVisible()?Color::RedMask|Color::GreenMask|Color::BlueMask:0);
+ d.clear(Color::Background);
+
+ d.setColor(Color::Border);
+ d.drawLine(0,height-1,width-1,height-1);
+ d.drawLine(width-1,0,width-1,height-1);
+
+ d.setColor(Color::Shadow3D);
+ d.drawLine(0,0,width-2,0);
+ d.drawLine(0,0,0,height-2);
+ d.drawLine(0,height-2,width-2,height-2);
+ d.drawLine(width-2,0,width-2,height-2);
+
+ d.drawLine(5,4,width-7,4);
+ d.drawLine(5,4,5,30);
+
+ d.setColor(Color::Light3D);
+ d.drawLine(1,1,width-3,1);
+ d.drawLine(1,1,1,height-3);
+
+ d.drawLine(5,31,width-6,31);
+ d.drawLine(width-6,5,width-6,31);
+
+ d.setColor(Color::Background3D^mask);
+ d.fillRect(6,5,26,26);
+ d.setColor(Color::Grey50^mask);
+ d.fillRect(9,17,20,4);
+ d.setColor(Color::Black^mask);
+ d.fillRect(8,16,20,4);
+ d.setColor(Color::White^mask);
+ d.fillRect(9,17,18,2);
+
+ d.setColor(Color::Border);
+ d.drawLine(32,5,32,30);
+
+ d.setColor(Color::Titlebar);
+ d.fillRect(33,5,width-39,26);
+
+ const Font *font = Font::getFont("title");
+ d.setColor(Color::TitlebarText);
+ d.setFont(font);
+ d.drawText(31+(width-39-font->getWidth(title))/2,5+(26-font->getHeight())/2+font->getAscent(),title,false,0);
+}
+
+void Input::paint(Drawable &d) const
+{
+ d.clear(Color::EditableBackground);
+
+ d.setColor(Color::Shadow3D);
+ d.drawLine(0,0,width-2,0);
+ d.drawLine(0,0,0,height-2);
+
+ d.setColor(Color::Background3D);
+ d.drawLine(1,height-2,width-2,height-2);
+ d.drawLine(width-2,1,width-2,height-2);
+
+ d.setColor(Color::Text);
+ d.drawLine(1,1,width-3,1);
+ d.drawLine(1,1,1,height-3);
+
+ const Font *f = Font::getFont("input");
+ d.setFont(f);
+
+ Drawable d1(d,3,4,width-6,height-8);
+ Drawable dr(d1,(multi?0:-offset),(multi?-offset:0),width-6+(multi?0:offset),height-8+(multi?offset:0));
+
+ const Size start = imin(start_sel, end_sel), end = imax(start_sel, end_sel);
+ dr.drawText(0,f->getAscent()+1,text,multi,0,start);
+
+ int sx = dr.getX(), sy = dr.getY();
+ dr.drawText(text, multi, start, end-start);
+ int ex = dr.getX(), ey = dr.getY();
+
+ if (sx != ex || sy != ey) {
+ dr.setColor(Color::SelectionBackground);
+ if (sy == ey) dr.fillRect(sx,sy-f->getAscent(),ex-sx,f->getHeight()+1);
+ else {
+ dr.fillRect(sx, sy-f->getAscent(), width-sx+offset, f->getHeight() );
+ dr.fillRect(0, sy-f->getAscent()+f->getHeight(), width+offset, ey-sy-f->getHeight());
+ dr.fillRect(0, ey-f->getAscent(), ex, f->getHeight() );
+ }
+ dr.setColor(Color::SelectionForeground);
+ dr.drawText(sx, sy, text, multi, start, end-start);
+ }
+
+ dr.setColor(Color::Text);
+
+ dr.drawText(text, multi, end);
+
+ if (blink && hasFocus()) {
+ if (insert) dr.drawLine(posx,posy,posx,posy+f->getHeight()+1);
+ else dr.fillRect(posx,posy,f->getWidth(text[pos]),f->getHeight()+1);
+ }
+}
+
+Size Input::findPos(int x, int y) {
+ const Font *f = Font::getFont("input");
+ if (multi) y += offset;
+ else x += offset;
+ y = (y-4) / f->getHeight();
+ int line = 0;
+ Size pos = 0;
+ while (line < y && pos < text.size()) if (f->toSpecial(text[pos++]) == Font::LF) line++;
+ Drawable d(width-6,1);
+ d.setFont(f);
+ while (pos <= text.size() && d.getY() == 0 && x > d.getX()) {
+ d.drawText(String(text), multi, pos, 1);
+ pos++;
+ }
+ if (pos > 0) pos--;
+ return pos;
+}
+
+bool Input::mouseDown(int x, int y, MouseButton button)
+{
+ if (button == Left || (button == Middle && start_sel == end_sel)) {
+ end_sel = start_sel = pos = findPos(x,y);
+ blink = true;
+ checkOffset();
+ }
+ if (button == Middle) keyDown(Key(0,Key::Insert,true,false,false,false));
+ return true;
+}
+
+bool Input::mouseDragged(int x, int y, MouseButton button)
+{
+ if (button == Left) {
+ end_sel = pos = findPos(x,y);
+ blink = true;
+ checkOffset();
+ }
+ return true;
+}
+
+bool Input::keyDown(const Key &key)
+{
+ const Font *f = Font::getFont("input");
+ switch (key.special) {
+ case Key::None:
+ if (key.ctrl) {
+ switch (key.character) {
+ case 1:
+ case 'a':
+ case 'A':
+ if (key.shift) {
+ start_sel = end_sel = pos;
+ } else {
+ start_sel = 0;
+ pos = end_sel = (Size)text.size();
+ }
+ break;
+ case 24:
+ case 'x':
+ case 'X':
+ cutSelection();
+ break;
+ case 3:
+ case 'c':
+ case 'C':
+ copySelection();
+ break;
+ case 22:
+ case 'v':
+ case 'V':
+ pasteSelection();
+ break;
+ default: printf("Ctrl-0x%x\n",key.character); break;
+ }
+ break;
+ }
+ if (start_sel != end_sel) clearSelection();
+ if (insert || pos >= text.size() ) text.insert(text.begin()+pos++,key.character);
+ else text[pos++] = key.character;
+ break;
+ case Key::Left:
+ if (pos > 0) pos--;
+ break;
+ case Key::Right:
+ if (pos < text.size()) pos++;
+ break;
+ case Key::Down:
+ if (multi) pos = findPos(posx+3, posy-offset+f->getHeight()+4);
+ break;
+ case Key::Up:
+ if (multi) pos = findPos(posx+3, posy-offset-f->getHeight()+4);
+ break;
+ case Key::Home:
+ if (multi) {
+ while (pos > 0 && f->toSpecial(text[pos-1]) != Font::LF) pos--;
+ } else pos = 0;
+ break;
+ case Key::End:
+ if (multi) {
+ while (pos < text.size() && f->toSpecial(text[pos]) != Font::LF) pos++;
+ } else pos = (Size)text.size();
+ break;
+ case Key::Backspace:
+ if (!key.shift && start_sel != end_sel) clearSelection();
+ else if (pos > 0) text.erase(text.begin()+ --pos);
+ break;
+ case Key::Delete:
+ if (key.shift) cutSelection();
+ else if (start_sel != end_sel) clearSelection();
+ else if (pos < text.size()) text.erase(text.begin()+pos);
+ break;
+ case Key::Insert:
+ if (key.ctrl) copySelection();
+ else if (key.shift) pasteSelection();
+ else insert = !insert;
+ break;
+ case Key::Enter:
+ if (multi) {
+ if (start_sel != end_sel) clearSelection();
+ if (insert || pos >= text.size() ) text.insert(text.begin()+pos++,f->fromSpecial(Font::LF));
+ else text[pos++] = f->fromSpecial(Font::LF);
+ } else executeAction(text);
+ break;
+ case Key::Tab:
+ if (multi) {
+ if (start_sel != end_sel) clearSelection();
+ if (insert || pos >= text.size() ) text.insert(text.begin()+pos++,f->fromSpecial(Font::Tab));
+ else text[pos++] = f->fromSpecial(Font::Tab);
+ } else return false;
+ break;
+ default:
+ return false;
+ }
+ if (!key.ctrl) {
+ if (!key.shift || key.special == Key::None) start_sel = end_sel = pos;
+ else end_sel = pos;
+ }
+ checkOffset();
+ blink = true;
+ return true;
+}
+
+void BorderedWindow::paintAll(Drawable &d) const
+{
+ this->paint(d);
+ Drawable dchild(d,border_left,border_top,width-border_left-border_right,height-border_top-border_bottom);
+ for (std::list<Window *>::const_iterator i = children.begin(); i != children.end(); ++i) {
+ Window *child = *i;
+ if (child->isVisible()) {
+ Drawable cd(dchild,child->getX(),child->getY(),child->getWidth(),child->getHeight());
+ child->paintAll(cd);
+ }
+ }
+}
+
+void Button::paint(Drawable &d) const
+{
+ int offset = -1;
+
+ if (hasFocus()) {
+ offset = 0;
+ d.setColor(Color::Border);
+ d.drawLine(0,0,width,0);
+ d.drawLine(0,0,0,height);
+
+ d.drawLine(0,height-1,width,height-1);
+ d.drawLine(width-1,0,width-1,height);
+ }
+
+ d.setColor(Color::Background3D);
+ d.fillRect(2,2,width-4,height-4);
+
+ if (pressed) {
+ d.setColor(Color::Shadow3D);
+
+ d.drawLine(1+offset,1+offset,width-2-offset,1+offset);
+ d.drawLine(1+offset,1+offset,1+offset,height-2-offset);
+ } else {
+ d.setColor(Color::Background3D);
+
+ d.drawLine(1+offset,1+offset,width-3-offset,1+offset);
+ d.drawLine(1+offset,1+offset,1+offset,height-3-offset);
+
+ d.setColor(Color::Light3D);
+
+ d.drawLine(2+offset,2+offset,width-4-offset,2+offset);
+ d.drawLine(2+offset,2+offset,2+offset,height-4-offset);
+
+ d.setColor(Color::Shadow3D);
+
+ d.drawLine(2+offset,height-3-offset,width-2-offset,height-3-offset);
+ d.drawLine(width-3-offset,2+offset,width-3-offset,height-2-offset);
+
+ d.setColor(Color::Border);
+
+ d.drawLine(width-2-offset,1+offset,width-2-offset,height-2-offset);
+ d.drawLine(1+offset,height-2-offset,width-2-offset,height-2-offset);
+ }
+}
+
+bool Checkbox::keyDown(const Key &key)
+{
+ switch (key.special) {
+ case Key::None:
+ if (key.character != ' ') return false;
+ case Key::Enter:
+ break;
+ default: return false;
+ }
+ mouseDown(0,0,Left);
+ return true;
+}
+
+bool Checkbox::keyUp(const Key &key)
+{
+ if (key.ctrl || key.alt || key.windows || (key.character != ' ' && key.special != Key::Enter)) return false;
+ mouseUp(0,0,Left);
+ mouseClicked(0,0,Left);
+ return true;
+}
+
+void Checkbox::paint(Drawable &d) const
+{
+ d.setColor(Color::Background3D);
+ d.fillRect(2,(height/2)-7,14,14);
+
+ d.setColor(Color::Shadow3D);
+ d.drawLine(2,(height/2)-7,13,(height/2)-7);
+ d.drawLine(2,(height/2)-7,2,(height/2)+5);
+
+ d.setColor(Color::Light3D);
+ d.drawLine(2,(height/2)+5,14,(height/2)+5);
+ d.drawLine(14,(height/2)-7,14,(height/2)+5);
+
+ d.setColor(Color::EditableBackground);
+ d.fillRect(4,(height/2)-5,9,9);
+
+ d.setColor(Color::Border);
+ d.drawLine(3,(height/2)-6,12,(height/2)-6);
+ d.drawLine(3,(height/2)-6,3,(height/2)+4);
+
+ if (checked) {
+ d.setColor(Color::Text);
+ d.drawLine(5,(height/2)-2,7,(height/2) );
+ d.drawLine(11,(height/2)-4);
+ d.drawLine(5,(height/2)-1,7,(height/2)+1);
+ d.drawLine(11,(height/2)-3);
+ d.drawLine(5,(height/2) ,7,(height/2)+2);
+ d.drawLine(11,(height/2)-2);
+ }
+}
+
+Radiobox::Radiobox(Frame *parent, int x, int y, int w, int h) : BorderedWindow(static_cast<Window *>(parent),x,y,w,h,16,0,0,0), ActionEventSource("GUI::Radiobox"), checked(0)
+{
+ addActionHandler(parent);
+}
+
+bool Radiobox::keyDown(const Key &key)
+{
+ switch (key.special) {
+ case Key::None:
+ if (key.character != ' ') return false;
+ case Key::Enter:
+ break;
+ default: return false;
+ }
+ mouseDown(0,0,Left);
+ return true;
+}
+
+bool Radiobox::keyUp(const Key &key)
+{
+ if (key.ctrl || key.alt || key.windows || (key.character != ' ' && key.special != Key::Enter)) return false;
+ mouseUp(0,0,Left);
+ mouseClicked(0,0,Left);
+ return true;
+}
+
+void Radiobox::paint(Drawable &d) const
+{
+ d.setColor(Color::Light3D);
+ d.drawLine(6,(height/2)+6,9,(height/2)+6);
+ d.drawLine(4,(height/2)+5,11,(height/2)+5);
+ d.drawLine(13,(height/2)-1,13,(height/2)+2);
+ d.drawLine(12,(height/2)-2,12,(height/2)+4);
+
+ d.setColor(Color::Background3D);
+ d.drawLine(6,(height/2)+5,9,(height/2)+5);
+ d.drawLine(4,(height/2)+4,11,(height/2)+4);
+ d.drawLine(12,(height/2)-1,12,(height/2)+2);
+ d.drawLine(11,(height/2)-2,11,(height/2)+4);
+
+ d.setColor(Color::Shadow3D);
+ d.drawLine(6,(height/2)-5,9,(height/2)-5);
+ d.drawLine(4,(height/2)-4,11,(height/2)-4);
+ d.drawLine(2,(height/2)-1,2,(height/2)+2);
+ d.drawLine(3,(height/2)-3,3,(height/2)+4);
+
+ d.setColor(Color::Border);
+ d.drawLine(6,(height/2)-4,9,(height/2)-4);
+ d.drawLine(4,(height/2)-3,11,(height/2)-3);
+ d.drawLine(3,(height/2)-1,3,(height/2)+2);
+ d.drawLine(4,(height/2)-3,4,(height/2)+3);
+
+ d.setColor(Color::EditableBackground);
+ d.fillRect(5,(height/2)-2,6,6);
+ d.fillRect(4,(height/2)-1,8,4);
+ d.fillRect(6,(height/2)-3,4,8);
+
+ if (checked) {
+ d.setColor(Color::Text);
+ d.fillRect(6,(height/2),4,2);
+ d.fillRect(7,(height/2)-1,2,4);
+ }
+}
+
+void Menu::paint(Drawable &d) const
+{
+ d.clear(Color::Background3D);
+
+ d.setColor(Color::Border);
+ d.drawLine(0,height-1,width-1,height-1);
+ d.drawLine(width-1,0,width-1,height-1);
+
+ d.setColor(Color::Shadow3D);
+ d.drawLine(0,0,width-2,0);
+ d.drawLine(0,0,0,height-2);
+ d.drawLine(0,height-2,width-2,height-2);
+ d.drawLine(width-2,0,width-2,height-2);
+
+ d.setColor(Color::Light3D);
+ d.drawLine(1,1,width-3,1);
+ d.drawLine(1,1,1,height-3);
+
+ d.setFont(Font::getFont("menu"));
+ const int asc = Font::getFont("menu")->getAscent()+1;
+ const int height = Font::getFont("menu")->getHeight()+2;
+ int y = asc+3;
+ int index = 0;
+ for (std::vector<String>::const_iterator i = items.begin(); i != items.end(); ++i) {
+ if ((*i).empty()) {
+ d.setColor(Color::Shadow3D);
+ d.drawLine(4,y-asc+6,width-5,y-asc+6);
+ d.setColor(Color::Light3D);
+ d.drawLine(4,y-asc+7,width-5,y-asc+7);
+ y += 12;
+ } else {
+ if (index == selected && hasFocus()) {
+ d.setColor(Color::SelectionBackground);
+ d.fillRect(3,y-asc,width-6,height);
+ d.setColor(Color::SelectionForeground);
+ } else {
+ d.setColor(Color::Text);
+ }
+ d.drawText(20,y,(*i),false,0);
+ y += height;
+ }
+ index++;
+ }
+}
+
+void Menubar::paint(Drawable &d) const
+{
+ const Font *f = Font::getFont("menu");
+
+ d.setColor(Color::Light3D);
+ d.drawLine(0,height-1,width-1,height-1);
+ d.setColor(Color::Shadow3D);
+ d.drawLine(0,height-2,width-1,height-2);
+
+ d.gotoXY(7,f->getAscent()+2);
+
+ int index = 0;
+ for (std::vector<Menu*>::const_iterator i = menus.begin(); i != menus.end(); ++i, ++index) {
+ if (index == selected && (*i)->isVisible()) {
+ int w = f->getWidth((*i)->getName());
+ d.setColor(Color::SelectionBackground);
+ d.fillRect(d.getX()-7,0,w+14,height-2);
+ d.setColor(Color::SelectionForeground);
+ d.gotoXY(d.getX()+7,f->getAscent()+2);
+ } else {
+ d.setColor(Color::Text);
+ }
+ d.drawText((*i)->getName(),false);
+ d.gotoXY(d.getX()+14,f->getAscent()+2);
+ }
+}
+
+bool Button::keyDown(const Key &key)
+{
+ switch (key.special) {
+ case Key::None:
+ if (key.character != ' ') return false;
+ case Key::Enter:
+ break;
+ default: return false;
+ }
+ mouseDown(0,0,Left);
+ return true;
+}
+
+bool Button::keyUp(const Key &key)
+{
+ if (key.ctrl || key.alt || key.windows || (key.character != ' ' && key.special != Key::Enter)) return false;
+ mouseUp(0,0,Left);
+ mouseClicked(0,0,Left);
+ return true;
+}
+
+void Frame::paint(Drawable &d) const {
+ const Font *f = Font::getFont("default");
+ const int top = (label.empty()?1:f->getAscent()/2+1);
+
+ d.setColor(Color::Shadow3D);
+ d.drawLine(1,height-2,1,top);
+ d.drawLine(8,top);
+ d.drawLine((label.empty()?8:f->getWidth(label)+14),top,width-2,top);
+ d.drawLine(2,height-3,width-3,height-3);
+ d.drawLine(width-3,top+1);
+
+ d.setColor(Color::Light3D);
+ d.drawLine(2,height-3,2,top+1);
+ d.drawLine(8,top+1);
+ d.drawLine((label.empty()?8:f->getWidth(label)+14),top+1,width-3,top+1);
+ d.drawLine(2,height-2,width-2,height-2);
+ d.drawLine(width-2,top+1);
+
+ d.setColor(Color::Text);
+ d.drawText(11,f->getAscent()+1,label,false,0);
+}
+
+Screen *Window::getScreen() { return (parent == NULL?dynamic_cast<Screen*>(this):parent->getScreen()); }
+
+Screen::Screen(unsigned int width, unsigned int height) :
+ Window(),
+ buffer(new Drawable(width, height))
+{
+ this->width = width;
+ this->height = height;
+}
+
+Screen::Screen(Drawable *d) :
+ Window(),
+ buffer(d)
+{
+ this->width = d->getClipWidth();
+ this->height = d->getClipHeight();
+}
+
+Screen::~Screen()
+{
+}
+
+void Screen::paint(Drawable &d) const
+{
+ d.clear(Color::Transparent);
+}
+
+unsigned int Screen::update(void *surface, unsigned int ticks)
+{
+ Timer::check(ticks);
+
+ paintAll(*buffer);
+ RGB *buf = buffer->buffer;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++, buf++) {
+ RGB sval = surfaceToRGB(surface);
+ RGB bval = *buf;
+ int a = Color::A(bval);
+ bval = ((((sval&Color::MagentaMask)*a+(bval&Color::MagentaMask)*(256-a))>>8)&Color::MagentaMask)
+ | ((((sval&Color::GreenMask)*a+(bval&Color::GreenMask)*(256-a))>>8)&Color::GreenMask);
+ rgbToSurface(bval, &surface);
+ }
+ }
+
+ return Timer::next();
+}
+
+void Screen::move(int x, int y)
+{
+}
+
+void Screen::resize(int w, int h)
+{
+}
+
+#ifdef TESTING
+static void test(Drawable &d) {
+ const int width = d.getClipWidth();
+ const int height = d.getClipHeight();
+
+ d.clear(Color::rgba(0,0,255,128));
+
+ d.setColor(Color::Black);
+ for (int x = 0; x < width; x += 10) d.drawLine(x,0,x,height);
+ for (int y = 0; y < height; y += 10) d.drawLine(0,y,width,y);
+
+ d.setColor(Color::Red);
+ for (int x = 10; x <= 130 ; x += 10) {
+ d.drawLine(x,10,70,70);
+ d.drawLine(x,130);
+ }
+ for (int y = 10; y <= 130 ; y += 10) {
+ d.drawLine(10,y,70,70);
+ d.drawLine(130,y);
+ }
+
+ d.setColor(Color::Yellow);
+ d.fillRect(150,10,30,30);
+ d.setColor(Color::Blue);
+ d.drawRect(30,30);
+
+ d.drawRect(150,60,30,30);
+ d.setColor(Color::Yellow);
+ d.fillRect(30,30);
+
+ for (int x = 0; x <= 100 ; x += 10) {
+ d.setColor(Color::rgba(0xff,0x00,0xff,(255*x/100)&255));
+ d.fillRect(200+2*x,0,20,20);
+ }
+
+ d.setColor(Color::Yellow);
+ d.fillCircle(210,60,40);
+ d.setColor(Color::Blue);
+ d.drawCircle(40);
+
+ d.drawCircle(210,110,40);
+ d.setColor(Color::Yellow);
+ d.fillCircle(40);
+
+ d.setColor(Color::rgba(0,0,255,128));
+ d.fillRect(251,41,9,59);
+ d.fillRect(251,41,59,9);
+ d.fillRect(301,41,9,59);
+ d.fillRect(291,91,19,9);
+ d.fillRect(291,51,9,49);
+ d.fillRect(261,51,39,9);
+ d.fillRect(261,51,9,49);
+ d.fillRect(261,91,29,9);
+ d.fillRect(281,61,9,39);
+ d.fillRect(271,61,19,9);
+ d.fillRect(271,61,9,29);
+ d.fillRect(241,41,9,59);
+ d.fillRect(241,41,19,9);
+ d.fillRect(241,91,19,9);
+ d.setColor(Color::rgba(255,0,0,128));
+ d.fill(255,64);
+
+ d.setColor(Color::Green);
+ Drawable(d,500,355,30,30).fillCircle(65);
+
+ for (int i = 0; i <= 100; i += 10) {
+ Drawable(d,25,155+i*3,420,30).drawDrawable(0,0,d,255*i/100);
+ }
+
+ d.setColor(Color::White);
+ d.setFont(Font::getFont("VGA14"));
+ d.drawText(270,110,"GUI:: Test Program\n");
+ d.drawText("Still testing\tTable\n");
+ d.drawText("More of...\tTable\n");
+ d.drawText("Overwrite\rXXXXXXXXX\n");
+ d.drawText("Fake int'l chars: O\b/e\b\"\n");
+ d.drawText("Real ones: \211\222\234\345\246\321");
+}
+#else
+static void test(Drawable &d) { (void)d; }
+#endif
+
+
+void ScreenRGB32le::paint(Drawable &d) const
+{
+ parent->paint(d);
+ test(d);
+}
+
+static MouseButton SDL_to_GUI(const int button)
+{
+ switch (button) {
+ case SDL_BUTTON_LEFT: return GUI::Left;
+ case SDL_BUTTON_RIGHT: return GUI::Right;
+ case SDL_BUTTON_MIDDLE: return GUI::Middle;
+ case SDL_BUTTON_WHEELUP: return GUI::WheelUp;
+ case SDL_BUTTON_WHEELDOWN: return GUI::WheelDown;
+ default: return GUI::NoButton;
+ }
+}
+
+static const Key SDL_to_GUI(const SDL_keysym &key)
+{
+ GUI::Key::Special ksym = GUI::Key::None;
+ switch (key.sym) {
+ case SDLK_ESCAPE: ksym = GUI::Key::Escape; break;
+ case SDLK_BACKSPACE: ksym = GUI::Key::Backspace; break;
+ case SDLK_TAB: ksym = GUI::Key::Tab; break;
+ case SDLK_LEFT: ksym = GUI::Key::Left; break;
+ case SDLK_RIGHT: ksym = GUI::Key::Right; break;
+ case SDLK_UP: ksym = GUI::Key::Up; break;
+ case SDLK_DOWN: ksym = GUI::Key::Down; break;
+ case SDLK_HOME: ksym = GUI::Key::Home; break;
+ case SDLK_END: ksym = GUI::Key::End; break;
+ case SDLK_DELETE: ksym = GUI::Key::Delete; break;
+ case SDLK_INSERT: ksym = GUI::Key::Insert; break;
+ case SDLK_RETURN: ksym = GUI::Key::Enter; break;
+ case SDLK_MENU: ksym = GUI::Key::Menu; break;
+ case SDLK_PAGEUP: ksym = GUI::Key::PageUp; break;
+ case SDLK_PAGEDOWN: ksym = GUI::Key::PageDown; break;
+ case SDLK_PRINT: ksym = GUI::Key::Print; break;
+ case SDLK_PAUSE: ksym = GUI::Key::Pause; break;
+ case SDLK_BREAK: ksym = GUI::Key::Break; break;
+ case SDLK_CAPSLOCK: ksym = GUI::Key::CapsLock; break;
+ case SDLK_NUMLOCK: ksym = GUI::Key::NumLock; break;
+ case SDLK_SCROLLOCK: ksym = GUI::Key::ScrollLock; break;
+ case SDLK_F1:case SDLK_F2:case SDLK_F3:case SDLK_F4:case SDLK_F5:case SDLK_F6:
+ case SDLK_F7:case SDLK_F8:case SDLK_F9:case SDLK_F10:case SDLK_F11:case SDLK_F12:
+ ksym = (GUI::Key::Special)(GUI::Key::F1 + key.sym-SDLK_F1);
+ default: break;
+ }
+ return Key(key.unicode, ksym,
+ (key.mod&KMOD_SHIFT)>0,
+ (key.mod&KMOD_CTRL)>0,
+ (key.mod&KMOD_ALT)>0,
+ (key.mod&KMOD_META)>0);
+}
+
+/** \brief Internal class that handles different screen bit depths and layouts the SDL way */
+class SDL_Drawable : public Drawable {
+protected:
+ SDL_Surface *const surface;
+
+public:
+ SDL_Drawable(int width, int height, RGB clear = Color::Transparent) : Drawable(width, height, clear), surface(SDL_CreateRGBSurfaceFrom(buffer, width, height, 32, width*4, Color::RedMask, Color::GreenMask, Color::BlueMask, Color::AlphaMask)) {
+ surface->flags |= SDL_SRCALPHA;
+ }
+
+ ~SDL_Drawable() {
+ SDL_FreeSurface(surface);
+ }
+
+ void update(SDL_Surface *dest) const {
+ SDL_BlitSurface(surface, NULL, dest, NULL);
+ }
+};
+
+ScreenSDL::ScreenSDL(SDL_Surface *surface) : Screen(new SDL_Drawable(surface->w, surface->h)), surface(surface), downx(0), downy(0), lastclick(0) {}
+
+Ticks ScreenSDL::update(Ticks ticks)
+{
+ Timer::check(ticks);
+
+ paintAll(*buffer);
+ static_cast<SDL_Drawable*>(buffer)->update(surface);
+
+ return Timer::next();
+}
+
+void ScreenSDL::paint(Drawable &d) const {
+ d.clear(Color::Transparent);
+ test(d);
+}
+
+bool ScreenSDL::event(const SDL_Event &event) {
+ bool rc;
+
+ switch (event.type) {
+ case SDL_KEYUP: {
+ const Key &key = SDL_to_GUI(event.key.keysym);
+ if (key.special == GUI::Key::None && key.character == 0) break;
+ if (key.special == GUI::Key::CapsLock || key.special == GUI::Key::NumLock) keyDown(key);
+ return keyUp(key);
+ }
+ case SDL_KEYDOWN: {
+ const Key &key = SDL_to_GUI(event.key.keysym);
+ if (key.special == GUI::Key::None && key.character == 0) break;
+ rc = keyDown(key);
+ if (key.special == GUI::Key::CapsLock || key.special == GUI::Key::NumLock) keyUp(key);
+ return rc;
+ }
+ case SDL_MOUSEMOTION:
+ if (event.motion.state) {
+ if (abs(event.motion.x-downx) <= 10 && abs(event.motion.y-downy) <= 10)
+ break;
+ downx = -11; downy = -11;
+ if (event.motion.state&SDL_BUTTON(1))
+ return mouseDragged(event.motion.x, event.motion.y, GUI::Left);
+ else if (event.motion.state&SDL_BUTTON(2))
+ return mouseDragged(event.motion.x, event.motion.y, GUI::Middle);
+ else if (event.motion.state&SDL_BUTTON(3))
+ return mouseDragged(event.motion.x, event.motion.y, GUI::Right);
+ break;
+ }
+
+ return mouseMoved(event.motion.x, event.motion.y);
+
+ case SDL_MOUSEBUTTONDOWN:
+ rc = mouseDown(event.button.x, event.button.y, SDL_to_GUI(event.button.button));
+ if (abs(event.button.x-downx) > 10 || abs(event.button.y-downy) > 10) lastclick = 0;
+ downx = event.button.x; downy = event.button.y;
+ return rc;
+
+ case SDL_MOUSEBUTTONUP:
+ rc = mouseUp(event.button.x, event.button.y, SDL_to_GUI(event.button.button));
+ if (abs(event.button.x-downx) < 10 && abs(event.button.y-downy) < 10) {
+ if (lastclick == 0 || (GUI::Timer::now()-lastclick) > 20) {
+ lastclick = GUI::Timer::now();
+ rc |= mouseClicked(downx, downy, SDL_to_GUI(event.button.button));
+ } else if (lastclick != 0) {
+ rc |= mouseDoubleClicked(downx, downy, SDL_to_GUI(event.button.button));
+ lastclick = 0;
+ } else {
+ lastclick = 0;
+ }
+ } else {
+ lastclick = 0;
+ }
+ return rc;
+ }
+
+ return false;
+}
+
+}
+
+
+
+#ifdef TESTING
+#include <stdio.h>
+
+/** \brief A test program that serves as an example for all GUI elements.
+ *
+ * Note that you need SDL installed and a file "testfont.h" for this
+ * to compile, which must contain a bitmap font declared as
+ *
+ * \code
+ * static const unsigned char testfont[256 * 14] = { ... };
+ * \endcode
+ *
+ * (256 chars, 8x14 fixed-width font, for example the IBM PC VGA 14-line font,
+ * you can get it from DOSBox)
+ *
+ * To compile it, use a command line like this:
+ *
+ * \code
+ * g++ -DTESTING `sdl-config --cflags` `sdl-config --libs` gui_tk.cpp -o testgui_tk
+ * \endcode
+ *
+ */
+
+int main(int argc, char *argv[])
+{
+ printf("GUI:: test program\n");
+
+ SDL_Surface *screen;
+
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
+ exit(1);
+ }
+
+ atexit(SDL_Quit);
+
+ screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);
+ if (screen == NULL) {
+ fprintf(stderr, "Couldn't set 640x480x32 video mode: %s\n", SDL_GetError());
+ exit(1);
+ }
+ printf("GUI:: color depth %i\n",screen->format->BitsPerPixel);
+
+ SDL_EnableUNICODE(true);
+ SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
+
+ #include "testfont.h"
+ GUI::Font::addFont("default",new GUI::BitmapFont(testfont,14,10));
+
+ GUI::ScreenSDL guiscreen(screen);
+ GUI::ToplevelWindow *frame = new GUI::ToplevelWindow(&guiscreen,205,100,380,250,"GUI::Frame");
+ static struct delwin : public GUI::ActionEventSource_Callback {
+ void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
+ dynamic_cast<GUI::ToplevelWindow *>(dynamic_cast<GUI::Button*>(b)->getParent())->close();
+ }
+ } dw;
+ struct newwin : public GUI::ActionEventSource_Callback {
+ GUI::Screen *screen;
+ int n;
+ void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
+ char title[256];
+ sprintf(title,"Window %i",++n);
+ GUI::ToplevelWindow *w = new GUI::ToplevelWindow(screen,405,100,120,150,title);
+ GUI::Button *close = new GUI::Button(w,5,5,"Close");
+ close->addActionHandler(&dw);
+ }
+ } nw;
+ nw.screen = &guiscreen;
+ nw.n = 0;
+ struct quit : public GUI::ActionEventSource_Callback {
+ GUI::ToplevelWindow *frame;
+ void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
+ if (arg == "Quit" && b->getName() != "GUI::Input") exit(0);
+ frame->setTitle(arg);
+ }
+ } ex;
+ ex.frame = frame;
+
+ GUI::Button *b = new GUI::Button(frame,8,20,"Open a new Window");
+ b->addActionHandler(&nw);
+ (new GUI::Button(frame,200,20,"1"))->addActionHandler(&ex);
+ (new GUI::Button(frame,235,20,"2"))->addActionHandler(&ex);
+ b = new GUI::Button(frame,270,20,"Quit");
+ b->addActionHandler(&ex);
+ (new GUI::Input(frame,16,55,150))->addActionHandler(&ex);
+
+ printf("Title: %s\n",(const char*)frame->getTitle());
+
+ struct movewin : public GUI::Timer_Callback, public GUI::ToplevelWindow_Callback {
+ public:
+ GUI::ToplevelWindow *frame;
+ GUI::Checkbox *cb1, *cb2;
+ int x, y;
+ virtual unsigned int timerExpired(unsigned int t) {
+ if (cb2->isChecked()) frame->move(frame->getX()+x,frame->getY()+y);
+ if (frame->getX() <= -frame->getWidth()) x = 1;
+ if (frame->getX() >= 640) x = -1;
+ if (frame->getY() <= -frame->getHeight()) y = 1;
+ if (frame->getY() >= 480) y = -1;
+ return 10;
+ }
+ virtual bool windowClosing(GUI::ToplevelWindow *win) {
+ if (!cb1->isChecked()) return false;
+ return true;
+ }
+ virtual void windowClosed(GUI::ToplevelWindow *win) {
+ GUI::Timer::remove(this);
+ }
+ } mw;
+ mw.frame = frame;
+ mw.x = -1;
+ mw.y = 1;
+ GUI::Frame *box = new GUI::Frame(frame,16,80,300,50);
+ mw.cb1 = new GUI::Checkbox(box,0,0,"Allow to close this window");
+ mw.cb2 = new GUI::Checkbox(box,0,20,"Move this window");
+ box = new GUI::Frame(frame,16,130,300,80,"Radio Buttons");
+ (new GUI::Radiobox(box,0,0,"Normal"))->setChecked(true);
+ new GUI::Radiobox(box,0,20,"Dynamic");
+ new GUI::Radiobox(box,0,40,"Simple");
+ box->addActionHandler(&ex);
+ GUI::Timer::add(&mw,10);
+ frame->addWindowHandler(&mw);
+ GUI::Menubar *bar = new GUI::Menubar(frame,0,0,frame->getWidth());
+ bar->addMenu("File");
+ bar->addItem(0,"New...");
+ bar->addItem(0,"Open...");
+ bar->addItem(0,"");
+ bar->addItem(0,"Save");
+ bar->addItem(0,"Save as...");
+ bar->addItem(0,"");
+ bar->addItem(0,"Close");
+ bar->addItem(0,"Quit");
+ bar->addMenu("Edit");
+ bar->addItem(1,"Undo");
+ bar->addItem(1,"Redo");
+ bar->addItem(1,"");
+ bar->addItem(1,"Cut");
+ bar->addItem(1,"Copy");
+ bar->addItem(1,"Paste");
+ bar->addItem(1,"");
+ bar->addItem(1,"Select all");
+ bar->addItem(1,"Select none");
+ bar->addMenu("View");
+ bar->addItem(2,"Zoom...");
+ bar->addItem(2,"Zoom in");
+ bar->addItem(2,"Zoom out");
+ bar->addMenu("?");
+ bar->addItem(3,"Manual");
+ bar->addItem(3,"Search...");
+ bar->addItem(3,"");
+ bar->addItem(3,"About");
+ bar->addActionHandler(&ex);
+
+ SDL_Event event;
+ while (1) {
+ while (SDL_PollEvent(&event)) {
+ if (!guiscreen.event(event)) {
+ if (event.type == SDL_QUIT) exit(0);
+ }
+ }
+
+ if (SDL_MUSTLOCK(screen)) SDL_LockSurface(screen);
+ memset(screen->pixels,0xff,4*640*15);
+ memset(((char *)screen->pixels)+4*640*15,0x00,4*640*450);
+ memset(((char *)screen->pixels)+4*640*465,0xff,4*640*15);
+ if (SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
+ guiscreen.update(4);
+ SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
+
+ SDL_Delay(40);
+ }
+}
+#endif
+
+#endif
diff --git a/src/libs/gui_tk/gui_tk.h b/src/libs/gui_tk/gui_tk.h
new file mode 100644
index 000000000..19e5212dd
--- /dev/null
+++ b/src/libs/gui_tk/gui_tk.h
@@ -0,0 +1,2224 @@
+/** \mainpage gui::tk - framework-agnostic C++ GUI toolkit
+ *
+ * \section i Introduction
+ *
+ * gui::tk is a simple one-file C++ GUI toolkit for use with arbitrary
+ * memory framebuffers.
+ *
+ * \section f Features
+ *
+ * \li small source and binary code size
+ * \li reasonable performance and memory usage
+ * \li comfortable usage
+ * \li suitable for embedded usage: integer math only
+ * \li extensibility via OO
+ * \li non-intrusive: can be integrated with any event mechanism of your liking
+ * \li no dependencies apart from standards-conformant ANSI C++ (including a little STL)
+ * \li support for different encodings, single- and multibyte
+ * \li flexible font support
+ *
+ * \section o Overview
+ *
+ * The toolkit draws on a surface you provide, using any size or pixel format.
+ * Create a GUI::Screen with the buffer to draw on, then pass that object
+ * (or a GUI::Window created from it) to all other widgets' constructors.
+ *
+ * It doesn't provide an own event loop. Instead, it relies on you passing events
+ * and updating the screen regularly. This way, it can easily be integrated with
+ * any event loop available (SDL, Qt, glib, X11, Win32, selfmade, ...)
+ *
+ * Many functions and concepts were taken from other well-known toolkits, so if you
+ * know Qt, Java or wxWindows, you may encounter (intentional) similarities. However,
+ * simplicity of code has been given priority over maximum optimization and pixel-exact
+ * replication of their appearance.
+ *
+ * A note about font support and encodings: All text-related functions use templates to
+ * support any encoding. Fonts define the charset.
+ *
+ * \section g Getting Started
+ *
+ * gui::tk contains an SDL adapter, so if your program is already using SDL,
+ * things are really easy. The rough sequence to get up and running is:
+ *
+ * \code
+ * // setup a suitable video mode with SDL
+ * SDL_surface *screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);
+ *
+ * // add a default font, you will most probably need it
+ * GUI::Font::addFont("default",new GUI::BitmapFont(testfont,14,10));
+ *
+ * // create the root-level screen object, the parent of all top-level windows
+ * GUI::ScreenSDL guiscreen(screen);
+ *
+ * // create any amount of toplevel windows you like
+ * GUI::ToplevelWindow *frame = new GUI::ToplevelWindow(&guiscreen, 205, 100, 380, 250, "gui::tk example");
+ *
+ * // add some GUI elements to the toplevel window
+ * GUI::Button *b = new GUI::Button(frame,8,20,"Quit");
+ *
+ * // Define and add an event handler
+ * struct quit : public GUI::ActionEventSource_Callback {
+ * void actionExecuted(GUI::ActionEventSource *b, const GUI::String &arg) {
+ * exit(0);
+ * }
+ * } ex;
+ * b->addActionHandler(&ex);
+ *
+ * // Enter an event loop, calling update() regularly.
+ * SDL_Event event;
+ * while (1) {
+ * while (SDL_PollEvent(&event)) {
+ * if (!guiscreen.event(event)) { // gui::tk didn't handle that event
+ * if (event.type == SDL_QUIT) exit(0);
+ * }
+ * }
+ *
+ * guiscreen.update(4); // 4 ticks = 40ms
+ * SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
+ *
+ * SDL_Delay(40); // wait 40ms
+ * }
+ * \endcode
+ *
+ * Look at gui_tk.h for more detailed documentation and reference of all classes.
+ *
+ * A test program that shows off the capabilities of gui::tk is available as part of gui_tk.cpp.
+ *
+ * \section l License
+ *
+ * Copyright (C) 2005-2007 Jörg Walter
+ *
+ * gui::tk 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gui::tk 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, see <http://www.gnu.org/licenses/>
+ */
+
+
+#ifndef GUI__TOOLKIT_H
+#define GUI__TOOLKIT_H
+
+#define imin(x,y) ((x)<(y)?(x):(y))
+#define imax(x,y) ((x)>(y)?(x):(y))
+#define isign(x) (((x)<0?-1:1))
+
+/** \file
+ * \brief Header file for gui::tk.
+ *
+ * All you need to do is to include "gui_tk.h". If you want SDL support, then include \c SDL.h first.
+ */
+
+#ifdef _MSC_VER
+typedef signed __int8 int8_t;
+typedef signed __int16 int16_t;
+typedef signed __int32 int32_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+#else
+#include <stdint.h>
+#endif
+
+#include <list>
+#include <map>
+#include <vector>
+#include <typeinfo>
+#include <string>
+#include <iostream>
+
+/// This namespace contains all GUI toolkit classes, types and functions.
+namespace GUI {
+
+/// ARGB 24-bit color value: (a<<24)|(r<<16)|(g<<8)|(b)
+typedef uint32_t RGB;
+
+/// Collection of all color-related constants and functions.
+namespace Color {
+/// A fully transparent pixel.
+const RGB Transparent = 0x00ffffff;
+
+/// A fully opaque black pixel.
+const RGB Black = 0xff000000;
+
+/// A fully opaque white pixel.
+const RGB White = 0xffffffff;
+
+/// Alpha mask.
+const RGB AlphaMask = 0xff000000;
+
+/// Offset of alpha value.
+const int AlphaShift = 24;
+
+/// Red mask.
+const RGB RedMask = 0x00ff0000;
+
+/// Full-intensity red.
+const RGB Red = Black|RedMask;
+
+/// Offset of red value.
+const int RedShift = 16;
+
+/// Green mask.
+const RGB GreenMask = 0x0000ff00;
+
+/// Full-intensity green.
+const RGB Green = Black|GreenMask;
+
+/// Offset of green value.
+const int GreenShift = 8;
+
+/// Blue mask.
+const RGB BlueMask = 0x000000ff;
+
+/// Full-intensity blue.
+const RGB Blue = Black|BlueMask;
+
+/// Offset of blue value.
+const int BlueShift = 0;
+
+/// Full-intensity Magenta.
+const RGB Magenta = Red|Blue;
+
+/// Magenta mask.
+const RGB MagentaMask = RedMask|BlueMask;
+
+/// Full-intensity Cyan.
+const RGB Cyan = Green|Blue;
+
+/// Cyan mask.
+const RGB CyanMask = GreenMask|BlueMask;
+
+/// Full-intensity Yellow.
+const RGB Yellow = Red|Green;
+
+/// Yellow mask.
+const RGB YellowMask = RedMask|GreenMask;
+
+/// 50% grey
+const RGB Grey50 = 0xff808080;
+
+/// Background color for 3D effects. May be customized.
+extern RGB Background3D;
+
+/// Light highlight color for 3D effects. May be customized.
+extern RGB Light3D;
+
+/// Dark highlight color (shadow) for 3D effects. May be customized.
+extern RGB Shadow3D;
+
+/// Generic border color for highlights or similar. May be customized.
+extern RGB Border;
+
+/// Foreground color for regular content (mainly text). May be customized.
+extern RGB Text;
+
+/// Background color for inactive areas. May be customized.
+extern RGB Background;
+
+/// Background color for selected areas. May be customized.
+extern RGB SelectionBackground;
+
+/// Foreground color for selected areas. May be customized.
+extern RGB SelectionForeground;
+
+/// Background color for inputs / application area. May be customized.
+extern RGB EditableBackground;
+
+/// Title bar color for windows. May be customized.
+extern RGB Titlebar;
+
+/// Title bar text color for windows. May be customized.
+extern RGB TitlebarText;
+
+/// Convert separate r, g, b and a values (each 0-255) to an RGB value.
+static inline RGB rgba(int r, int g, int b, int a=0) {
+ return (((r&255)<<RedShift)|((g&255)<<GreenShift)|((b&255)<<BlueShift)|((a&255)<<AlphaShift));
+}
+
+/// Get red value (0-255) from an RGB value.
+static inline int R(RGB val) { return ((val&Color::RedMask)>>Color::RedShift); }
+/// Get green value (0-255) from an RGB value.
+static inline int G(RGB val) { return ((val&Color::GreenMask)>>Color::GreenShift); }
+/// Get blue value (0-255) from an RGB value.
+static inline int B(RGB val) { return ((val&Color::BlueMask)>>Color::BlueShift); }
+/// Get alpha value (0-255) from an RGB value.
+static inline int A(RGB val) { return ((val&Color::AlphaMask)>>Color::AlphaShift); }
+
+}
+
+/// Identifies a mouse button.
+enum MouseButton { NoButton, Left, Right, Middle, WheelUp, WheelDown, WheelLeft, WheelRight };
+
+/// A type which holds size values that can be very large.
+typedef unsigned int Size;
+
+/// A type which holds a single character. Large enough for Unicode.
+typedef uint32_t Char;
+
+/// A type which holds a number of timer ticks.
+typedef unsigned int Ticks;
+
+/// Identifies a keyboard key.
+class Key {
+public:
+ /// Translated character value.
+ /** No encoding is implied. The Font that is used to display this character
+ * determines the appearance. */
+ Char character;
+ /// Special keyboard keys.
+ /** It is modeled after PC keyboards. When you feed keyboard events to a GUI::Screen,
+ * try to map native keys to this set. Some special keys have a character value. Set
+ * to \c None if the key has no special meaning. */
+ enum Special {
+ None,
+ F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
+ Up, Down, Left, Right, Backspace, Tab, Backtab, Enter, Escape,
+ Home, End, PageUp, PageDown, Insert, Delete, Menu,
+ Print, Pause, Break, CapsLock, NumLock, ScrollLock,
+ Alt, Ctrl, Shift, Windows
+ } special;
+ /// Set if the Shift key is currently down.
+ bool shift;
+ /// Set if the Ctrl key is currently down.
+ bool ctrl;
+ /// Set if the Alt (PC) or Meta (classic Unix) key is currently down.
+ bool alt;
+ /// Set if the "Windows"/Meta (PC) or Super (classic Unix) key is currently down.
+ /** Do not depend too heavily on this, as many keyboards and systems do not have such a key. */
+ bool windows;
+
+ /// Constructor.
+ Key(int character, Special special, bool shift, bool ctrl, bool alt, bool windows) :
+ character(character), special(special),
+ shift(shift), ctrl(ctrl), alt(alt), windows(windows) {}
+};
+
+class Drawable;
+class String;
+
+/** \brief Converts between strings of various types and String objects.
+ *
+ * It is used to deal with string encoding. It is intended to be used with template
+ * specializations. Having such a specialization means you can feed the corresponding type to
+ * all functions that expect a string -- without any conversion. You can add specializations
+ * yourself in your program's code to deal with unsupported types.
+ *
+ * As an example, see the std::string version.
+ *
+ * Encodings, as opposed to character sets, define how bytes map to character values.
+ * ASCII, for example, has an \em encoding where each byte is one character, while the
+ * ASCII \em character \em set says that values 0-127 are valid and 65 is the upper case
+ * letter 'A'. UTF-8 has 1-6 bytes per character, and the character set is unicode.
+ *
+ * GUI::Font deals with the character set, and this class encapsulates encodings.
+ *
+ */
+template <typename STR> class NativeString {
+protected:
+ friend class String;
+
+ /// Converts a native string into a String object
+ static void getString(String &dest, const STR &src) { STR::_this_string_type_is_not_supported_(); }
+
+ /** \brief Converts a string object to native representation.
+ *
+ * If some characters cannot be converted, they should silently be skipped. Apart from that,
+ * \c nativeString(stringNative(String(),X)) should be value-equal to \c X.
+ */
+ static STR& getNative(const String &src) { STR::_this_string_type_is_not_supported_();return*new STR(); }
+};
+
+template <typename STR> class NativeString<STR*> {
+protected:
+ friend class String;
+ static void getString(String &dest, const STR *src);
+ static STR* getNative(const String &src);
+};
+
+template <typename STR, Size N> class NativeString<STR[N]> : public NativeString<STR*> {};
+template <typename STR, Size N> class NativeString<const STR[N]> : public NativeString<STR*> {};
+template <typename STR> class NativeString<const STR*> : public NativeString<STR*> {};
+
+/// 'less-than' comparison between pointer addresses
+struct ltvoid { bool operator()(const void* s1, const void* s2) const { return s1 < s2; } };
+
+/** \brief Simple STL-based string class.
+ *
+ * This is intended as internal helper class to allow gui::tk to work with any kind of
+ * string objects. While you can use this in normal application code, you should better
+ * use the string class of your application framework (like Qt). If you don't have any,
+ * use std::string.
+ *
+ * It supports arbitrary characters, no character set is implied. Conversion from/to usual
+ * string types like \c char* is automatic but not thread-safe for non-class types.
+ */
+class String : public std::vector<Char> {
+protected:
+ template <typename STR> friend class NativeString;
+ /// Simple pointer encapsulation class for memory management.
+ class Native { public: virtual ~Native() {}; };
+ /// Simple pointer encapsulation class for memory management.
+ template <typename STR> class NativeArray: public Native {
+ STR *data;
+ public:
+ NativeArray(STR *data) : data(data) {}
+ virtual ~NativeArray() { delete[] data; }
+ };
+ template <typename STR> class NativeObject: public Native {
+ STR *data;
+ public:
+ NativeObject(STR *data) : data(data) {}
+ virtual ~NativeObject() { delete data; }
+ };
+
+private:
+ /// Semi-static memory management for pointer string types.
+ mutable std::map<const class std::type_info *, Native *, ltvoid> strings;
+
+protected:
+ /// Manage a native string's memory.
+ void addNative(Native *dest) const {
+ const class std::type_info &type = typeid(dest);
+ if (strings[&type] != NULL) delete strings[&type];
+ strings[&type] = dest;
+ }
+
+public:
+ /// Allocate a new String initialized from native string.
+ template <typename STR> String(const STR& src) { NativeString<STR>::getString(*this, src); }
+
+ /// Taken from STL.
+ template <class InputIterator> String(InputIterator a, InputIterator b) : std::vector<Char>(a,b) {}
+
+ /// Copy-constructor
+ String(const String &src) : std::vector<Char>(src), strings() {};
+
+ /// Allocate a new String.
+ String() { }
+
+ /// Deallocate a String.
+ ~String() {
+ for (std::map<const class std::type_info *, Native *, ltvoid>::iterator i = strings.begin(); i != strings.end(); ++i)
+ delete (*i).second;
+ };
+
+ /// Convert to native representation.
+ /** For pointer types like \c char*, the returned pointer is usually only valid as long as
+ * this object exists, or until it is modified and cast to the same type again. */
+ template <typename T> operator T() const { return NativeString<T>::getNative(*this); }
+
+ /// Compare with native representation.
+ template <typename T> bool operator==(const T &src) const { return *this == String(src); }
+ /// Compare with other Strings.
+ bool operator==(const String &src) const { return *(std::vector<Char>*)this == src; }
+
+ /// Compare with native representation.
+ template <typename T> bool operator!=(const T &src) const { return *this != String(src); }
+ /// Compare with other Strings.
+ bool operator!=(const String &src) const { return *(std::vector<Char>*)this != src; }
+};
+
+template <typename STR> void NativeString<STR*>::getString(String &dest, const STR* src) {
+ Size strlen = 0;
+ while (src[strlen]) strlen++;
+ dest.resize(strlen);
+ for (strlen = 0; src[strlen]; strlen++) dest[strlen] = (sizeof(STR)==1?(unsigned char)src[strlen]:sizeof(STR)==2?(unsigned short)src[strlen]:src[strlen]);
+}
+
+template <typename STR> STR* NativeString<STR*>::getNative(const String &src) {
+ Size strlen = (Size)src.size();
+ STR* dest = new STR[strlen+1];
+ dest[strlen] = 0;
+ for (; strlen > 0; strlen--) dest[strlen-1] = src[strlen-1];
+ src.addNative(new String::NativeArray<const STR>(dest));
+ return dest;
+}
+
+template <> class NativeString<std::string*> {
+protected:
+ friend class String;
+ static void getString(String &dest, const std::string *src) {
+ Size strlen = (Size)src->length();
+ dest.resize(strlen);
+ for (Size i = 0; i< strlen; i++) dest[i] = (*src)[i];
+ }
+ static std::string* getNative(const String &src) {
+ Size strlen = (Size)src.size();
+ std::string* dest = new std::string();
+ for (Size i = 0; i < strlen; i++) dest->append(1,src[i]);
+ src.addNative(new String::NativeObject<std::string>(dest));
+ return dest;
+ }
+};
+
+template <> class NativeString<const std::string*> : public NativeString<std::string*> {};
+
+template <> class NativeString<std::string> {
+protected:
+ friend class String;
+ static void getString(String &dest, const std::string &src) {
+ Size strlen = (Size)src.length();
+ dest.resize(strlen);
+ for (Size i = 0; i< strlen; i++) dest[i] = src[i];
+ }
+ static std::string& getNative(const String &src) {
+ Size strlen = (Size)src.size();
+ std::string* dest = new std::string();
+ for (Size i = 0; i < strlen; i++) dest->append(1,src[i]);
+ src.addNative(new String::NativeObject<std::string>(dest));
+ return *dest;
+ }
+};
+
+template <> class NativeString<const std::string> : public NativeString<std::string> {};
+
+class ToplevelWindow;
+class Menu;
+class TransientWindow;
+class Screen;
+class Window;
+
+/// Callback for window events.
+struct Window_Callback {
+public:
+ /// Called whenever this window changes position.
+ virtual void windowMoved(Window *win, int x, int y) = 0;
+ virtual ~Window_Callback() {}
+};
+
+
+/** \brief A Window is a rectangular sub-area of another window.
+ *
+ * Windows are arranged hierarchically. A Window cannot leave its parent's
+ * area. They may contain their own buffer or share it with their parent.
+ *
+ * Usually, every GUI element is a subclass of Window. Note that this is
+ * \em not a GUI window with decorations like border and title bar. See ToplevelWindow
+ * for that.
+ */
+class Window {
+protected:
+ friend class ToplevelWindow;
+ friend class TransientWindow;
+ friend class Menu;
+
+ /// Width of the window.
+ int width;
+ /// Height of the window.
+ int height;
+ /// X position relative to parent.
+ int x;
+ /// Y position relative to parent.
+ int y;
+
+ /// \c true if anything changed in this window or one of it's children
+ bool dirty;
+
+ /// \c true if this window should be visible on screen.
+ bool visible;
+
+ /// Parent window.
+ Window *const parent;
+
+ /// Child window of last button-down event
+ /** It receives all drag/up/click/doubleclick events until an up event is received */
+ Window *mouseChild;
+
+ /// Child windows.
+ /** Z ordering is done in list order. The first element is the lowermost
+ * window. This window's content is below all children. */
+ std::list<Window *> children;
+
+ /// List of registered event handlers.
+ std::list<Window_Callback *> movehandlers;
+
+ /// Register child window.
+ virtual void addChild(Window *child);
+
+ /// Remove child window.
+ virtual void removeChild(Window *child);
+
+ /// Mark this window and all parents as dirty.
+ void setDirty() { if (dirty) return; dirty = true; if (parent != NULL) parent->setDirty(); };
+
+ /// Replace clipboard content.
+ virtual void setClipboard(const String &s) { parent->setClipboard(s); };
+
+ /// Get clipboard content.
+ virtual const String& getClipboard() { return parent->getClipboard(); };
+
+ /// Default constructor. Only used in class Screen. Do not use.
+ Window();
+
+ /// Called whenever the focus changes.
+ virtual void focusChanged(bool gained) {
+ if (children.size() > 0) children.back()->focusChanged(gained);
+ }
+
+public:
+
+ /// Add an event handler.
+ void addWindowHandler(Window_Callback *handler) { movehandlers.push_back(handler); }
+
+ /// Remove an event handler.
+ void removeWindowHandler(Window_Callback *handler) { movehandlers.remove(handler); }
+
+ /// Create a subwindow of the given parent window.
+ Window(Window *parent, int x, int y, int w, int h);
+
+ /// Destructor.
+ virtual ~Window();
+
+ /// Resize this window to the given dimensions.
+ /** If that would move part of the window outside of this window's area,
+ * the outside area will silently be clipped while drawing. */
+ virtual void resize(int w, int h);
+ /// Return this window's width
+ virtual int getWidth() const { return width; }
+ /// Return this window's height
+ virtual int getHeight() const { return height; }
+
+ /// Move this window to the given position relative to the parent window's origin.
+ /** If that would move part of the window outside of this window's area,
+ * the outside area will silently be clipped while drawing. */
+ virtual void move(int x, int y);
+ /// Return this window's X position relative to the parent's top left corner
+ virtual int getX() const { return x; }
+ /// Return this window's Y position relative to the parent's top left corner
+ virtual int getY() const { return y; }
+ /// Return this window's contents' X position relative to the screen's top left corner
+ virtual int getScreenX() const { return (parent == NULL?0:parent->getScreenX()+x); }
+ /// Return this window's contents' Y position relative to the screen's top left corner
+ virtual int getScreenY() const { return (parent == NULL?0:parent->getScreenY()+y); }
+
+ /// Draw this window's content including all children.
+ virtual void paintAll(Drawable &d) const;
+
+ /// Draw this window's content.
+ virtual void paint(Drawable &d) const {};
+
+ /// Show or hide this window.
+ /** By default, most windows are shown when created. Hidden windows do not receive any events. */
+ virtual void setVisible(bool v) { visible = !!v; parent->setDirty(); }
+
+ /// Returns \c true if this window is visible.
+ virtual bool isVisible() const { return (!parent || parent->isVisible()) && visible; }
+
+ /// Return parent window.
+ /** May return NULL if this is the topmost window (the Screen). */
+ Window *getParent() const { return parent; }
+
+ /// Get the topmost window (the Screen)
+ Screen *getScreen();
+
+ /// Returns \c true if this window has currently the keyboard focus.
+ virtual bool hasFocus() const { return parent->hasFocus() && *parent->children.rbegin() == this; }
+
+ /// Mouse was moved. Returns true if event was handled.
+ virtual bool mouseMoved(int x, int y);
+ /// Mouse was moved while a button was pressed. Returns true if event was handled.
+ virtual bool mouseDragged(int x, int y, MouseButton button);
+ /// Mouse was pressed. Returns true if event was handled.
+ virtual bool mouseDown(int x, int y, MouseButton button);
+ /// Mouse was released. Returns true if event was handled.
+ virtual bool mouseUp(int x, int y, MouseButton button);
+ /// Mouse was clicked. Returns true if event was handled.
+ /** Clicking means pressing and releasing the mouse button while not moving it. */
+ virtual bool mouseClicked(int x, int y, MouseButton button);
+ /// Mouse was double-clicked. Returns true if event was handled.
+ virtual bool mouseDoubleClicked(int x, int y, MouseButton button);
+
+ /// Key was pressed. Returns true if event was handled.
+ virtual bool keyDown(const Key &key);
+ /// Key was released. Returns true if event was handled.
+ virtual bool keyUp(const Key &key);
+
+ /// Put this window on top of all it's siblings. Preserves relative order.
+ /** Returns true if the window accepts the raise request. */
+ virtual bool raise() {
+ Window *last = parent->children.back();
+ for (Window *cur = parent->children.back(); cur != this; cur = parent->children.back()) {
+ parent->children.remove(cur);
+ parent->children.push_front(cur);
+ }
+ if (last != this) {
+ focusChanged(true);
+ last->focusChanged(false);
+ }
+ parent->setDirty();
+ return true;
+ }
+
+ /// Put this window below all of it's siblings. Does not preserve relative order.
+ virtual void lower() {
+ parent->children.remove(this);
+ parent->children.push_front(this);
+ if (this != parent->children.back()) {
+ parent->children.back()->focusChanged(true);
+ focusChanged(false);
+ }
+ parent->setDirty();
+ }
+
+ /// Return the \p n th child
+ Window *getChild(int n) {
+ for (std::list<Window *>::const_iterator i = children.begin(); i != children.end(); ++i) {
+ if (n--) return *i;
+ }
+ return NULL;
+ }
+
+};
+
+/** \brief A Screen represents the framebuffer that is the final destination of the GUI.
+ *
+ * It's main purpose is to manage the current contents of the surface and to combine
+ * it with all GUI elements. You can't resize and move the screen. Requests to do so
+ * will be ignored.
+ *
+ * This is an abstract base class. You need to use a subclass which implements rgbToSurface
+ * and surfaceToRGB.
+ *
+ * To make things work, Screen needs events. Call the mouse and key event functions (see Window)
+ * whenever such an event occurs. Call update(void *surface, int ticks) regularly, if possible about
+ * every 40msec (25 fps). If nothing has changed, screen updates are quite fast.
+ */
+class Screen : public Window {
+protected:
+ /// Screen buffer.
+ Drawable *const buffer;
+
+ /// Clipboard.
+ String clipboard;
+
+ /// Currently pressed mouse button.
+ MouseButton button;
+
+ /// Store a single RGB triple (8 bit each) as a native pixel value and advance pointer.
+ virtual void rgbToSurface(RGB color, void **pixel) = 0;
+
+ /// Map a single framebuffer pixel to an RGB value.
+ virtual RGB surfaceToRGB(void *pixel) = 0;
+
+ /// Create a new screen with the given characteristics.
+ Screen(Size width, Size height);
+
+ /// Create a new screen from the given GUI::Drawable.
+ Screen(Drawable *d);
+
+public:
+ /// Destructor.
+ virtual ~Screen();
+
+ /// Set clipboard content.
+ template <typename STR> void setClipboard(const STR s) { this->setClipboard(String(s)); }
+
+ /// Set clipboard content.
+ virtual void setClipboard(const String &s) { clipboard = s; }
+
+ /// Get clipboard content.
+ virtual const String& getClipboard() { return clipboard; }
+
+ /// Do nothing.
+ virtual void resize(int w, int h);
+
+ /// Do nothing.
+ virtual void move(int x, int y);
+
+ /// Screen has always focus.
+ virtual bool hasFocus() const { return true; }
+
+ /// Update the given surface with this screen's content, fully honouring the alpha channel.
+ /** \p ticks can be set to a different value depending on how much time has passed. Timing
+ * doesn't need to be perfect, but try to call this at least every 40 msec. Each tick
+ * amounts to about 10 msec. Returns the number of ticks until the next event is scheduled, or
+ * 0 if none. */
+ Ticks update(void *surface, Ticks ticks = 1);
+
+ /// Default: clear screen.
+ virtual void paint(Drawable &d) const;
+};
+
+class Timer;
+
+/// Timer callback type
+struct Timer_Callback {
+public:
+ /// The timer has expired.
+ /** Callbacks for timers take one parameter, the number of ticks since
+ * application start. Note that this value may wrap after a little less
+ * than 500 days. If you want callbacks to be called again, return the
+ * delay in ticks relative to the scheduled time of this
+ * callback (which may be earlier than now() ). Otherwise return 0. */
+ virtual Ticks timerExpired(Ticks time) = 0;
+ virtual ~Timer_Callback() {}
+};
+
+/** \brief Timing service.
+ * Time is measured in ticks. A tick is about 10 msec.
+ *
+ * Note that this is not suitable as a high-accuracy timing service. Timer
+ * events can be off by many ticks, and a tick may be slightly more or
+ * slightly less than 10 msec. Because of that, Timers are only intended for
+ * simple animation effects, input timeouts and similar things.
+ *
+ */
+class Timer {
+protected:
+ /// Number of ticks since application start.
+ static Ticks ticks;
+
+ /// Compare two integers for 'less-than'.
+ struct ltuint { bool operator()(Ticks i1, Ticks i2) const {
+ return (i1 < i2);
+ } };
+
+ /// Active timers.
+ static std::multimap<Ticks,Timer_Callback*,ltuint> timers;
+
+public:
+ /// Advance time and check for expired timers.
+ static void check(Ticks ticks);
+
+ /// Add a timed callback. \p ticks is a value relative to now().
+ /** \p cb is not copied. */
+ static void add(Timer_Callback *cb, const Ticks ticks) { timers.insert(std::pair<const Ticks,Timer_Callback *>(ticks+Timer::ticks,cb)); }
+
+ static void remove(const Timer_Callback *const cb);
+
+ /// Return current time (ticks since application start)
+ static Ticks now() { return ticks; }
+
+ /// Return number of ticks until next scheduled timer or 0 if no timers
+ static Ticks next();
+};
+
+
+/** \brief A 24 bit per pixel RGB framebuffer aligned to 32 bit per pixel.
+ *
+ * Warning: This framebuffer type varies with CPU endiannes. It is meant as
+ * a testing/debugging tool.
+ */
+class ScreenRGB32le : public Screen {
+protected:
+ /// Map a single RGB triple (8 bit each) to a native pixel value.
+ virtual void rgbToSurface(RGB color, void **pixel) { RGB **p = (RGB **)pixel; **p = color; (*p)++; };
+
+ /// Map a single surface pixel to an RGB value.
+ virtual RGB surfaceToRGB(void *pixel) { return *(RGB*)pixel; };
+public:
+ ScreenRGB32le(Size width, Size height) : Screen(width,height) {};
+
+ virtual void paint(Drawable &d) const;
+};
+
+
+#ifdef SDL_MAJOR_VERSION
+/** \brief An SDL surface adapter.
+ *
+ * This screen type will draw on an SDL surface (honouring transparency if the
+ * surface is already filled) and react on SDL events. It does not provide an
+ * application event loop, you have to call update() and event() regularly until
+ * you decide to quit the application.
+ *
+ * The implementation of this class could as well have been integrated into
+ * GUI::Screen. That might have been a little more efficient, but this way
+ * this class serves as an example for custom screen classes.
+ *
+ * Note that you have to include \c SDL.h \em before \c gui_tk.h for this class
+ * to become available.
+ */
+class ScreenSDL : public Screen {
+protected:
+ /// not used.
+ virtual void rgbToSurface(RGB color, void **pixel) {};
+
+ /// not used.
+ virtual RGB surfaceToRGB(void *pixel) { return 0; };
+
+ /// The SDL surface being drawn to.
+ SDL_Surface *surface;
+
+ /// Position of last mouse down.
+ int downx, downy;
+
+ /// time of last click for double-click detection.
+ Ticks lastclick;
+
+public:
+
+ /** Initialize SDL screen with a surface
+ *
+ * The dimensions of this surface will define the screen dimensions. Changing the surface
+ * later on will not change the available area.
+ */
+ ScreenSDL(SDL_Surface *surface);
+
+ /** Change current surface
+ *
+ * The new surface may have different dimensions than the current one, but
+ * the screen size will not change. This means that either the screen will
+ * not be displayed fully, or there will be borders around the screen.
+ */
+ void setSurface(SDL_Surface *surface) { this->surface = surface; }
+
+ /// Retrieve current surface.
+ SDL_Surface *getSurface() { return surface; }
+
+ /// Overridden: makes background transparent by default.
+ virtual void paint(Drawable &d) const;
+
+ /// Use this to update the SDL surface. The screen must not be locked.
+ Ticks update(Ticks ticks);
+
+ /// Process an SDL event. Returns \c true if event was handled.
+ bool event(const SDL_Event *ev) { return event(*ev); }
+
+
+ /// Process an SDL event. Returns \c true if event was handled.
+ bool event(const SDL_Event& ev);
+};
+#endif
+
+class Font;
+
+/** \brief A drawable represents a rectangular off-screen drawing area.
+ *
+ * It is an off-screen buffer which can be copied to other drawables or
+ * to a Screen's framebuffer. The drawable's origin is at the top left. All
+ * operations are silently clipped to the available area. The alpha channel
+ * is honoured while copying one drawable to another, but not during other
+ * drawing operations.
+ *
+ * Drawables have a current font, color and (x,y) position. Drawing takes place
+ * at the given point using the given font and color. The current position is
+ * then moved to the final point of the drawing primitive unless otherwise
+ * noted. Pixel width of all lines is selectable, but be aware that visual
+ * appearance of lines with width > 1 is not as sophisticated as in well-known
+ * toolkits like Java or Qt.
+ *
+ * Some drawing primitives are overloaded to take full coordinates. These ignore
+ * the current position, but update it just like their regular brethren.
+ */
+class Drawable {
+protected:
+ friend Ticks Screen::update(void *, Ticks);
+ /// The actual pixel buffer.
+ RGB *const buffer;
+ /// Total width of buffer.
+ const int width;
+ /// Total height of buffer.
+ const int height;
+ /// \c true if \a buffer must be freed by this instance.
+ const bool owner;
+
+ /// Current color.
+ RGB color;
+ /// Current font.
+ const Font *font;
+ /// Current line width.
+ int lineWidth;
+
+ /// X translation.
+ const int tx;
+ /// Y translation.
+ const int ty;
+ /// clip x.
+ const int cx;
+ /// clip y.
+ const int cy;
+ /// clip width.
+ const int cw;
+ /// clip height.
+ const int ch;
+
+ /// Current position x coordinate.
+ int x;
+ /// Current position y coordinate.
+ int y;
+
+public:
+ /// Create an empty drawable object with given dimensions.
+ /** Optionally, the area is cleared with a given color (default: fully transparent). */
+ Drawable(int w, int h, RGB clear = Color::Transparent);
+
+ /// Deep-copying copy constructor.
+ /** It honours clip and translate so that the resulting drawable, if drawn
+ * to the source drawable at (0,0), yields the same result as drawing
+ * directly to the source surface. If \p fill is not explicitly set, will
+ * copy the original surface's contents */
+ Drawable(Drawable &src, RGB clear = 0);
+
+ /// Shallow-copying copy constructor with clip & translate. See setClipTranslate(int x, int y, int w, int h).
+ Drawable(Drawable &src, int x, int y, int w, int h);
+
+ /// Destructor.
+ virtual ~Drawable();
+
+ /// Clears the surface.
+ void clear(RGB clear = Color::Transparent);
+
+ /// Change current drawing color.
+ /** The alpha part is honoured in all drawing primitives like this:
+ All drawing operations in this window will unconditionally overwrite
+ earlier content of this window. Only when combining this window with
+ it's parent, the alpha channel is fully honoured. */
+ void setColor(RGB c) { color = c; };
+ /// Return the currently selected drawing color.
+ RGB getColor() { return color; };
+
+ /// Change current drawing font.
+ void setFont(const Font *f) { font = f; };
+ /// Return the currently selected drawing font.
+ const Font *getFont() { return font; };
+
+ /// Change current line width.
+ void setLineWidth(int w) { lineWidth = w; };
+ /// Return the current line width.
+ int getLineWidth() { return lineWidth; };
+
+ /// Move the current position to the given coordinates.
+ void gotoXY(int x, int y) { this->x = x; this->y = y; };
+ /// Return current position X.
+ int getX() { return x; }
+ /// Return current position Y.
+ int getY() { return y; }
+
+ /// Return clip width
+ int getClipX() { return cx; }
+ /// Return clip height
+ int getClipY() { return cy; }
+ /// Return clip width
+ int getClipWidth() { return cw; }
+ /// Return clip height
+ int getClipHeight() { return ch; }
+
+ /// Paint a single pixel at the current position.
+ void drawPixel() { if (x >= cx && x < cw && y >= cy && y < ch) buffer[x+tx+(y+ty)*width] = color; };
+ /// Paint a single pixel at the given coordinates.
+ void drawPixel(int x, int y) { gotoXY(x,y); drawPixel(); };
+
+ /// Return the pixel color at the current position.
+ RGB getPixel() { if (x >= cx && x < cw && y >= cy && y < ch) return buffer[x+tx+(y+ty)*width]; return Color::Transparent; };
+ /// Return the pixel color at the given coordinates.
+ RGB getPixel(int x, int y) { gotoXY(x,y); return getPixel(); };
+
+ /// Draw a straight line from the current position to the given coordinates.
+ void drawLine(int x2, int y2);
+ /// Draw a straight line from (\p x1,\p y1) to (\p x2,\p y2).
+ void drawLine(int x1, int y1, int x2, int y2) { gotoXY(x1,y1); drawLine(x2,y2); };
+
+ /// Draw a circle centered at the current position with diameter \p d.
+ /** The current position is not changed. */
+ void drawCircle(int d);
+ /// Draw a circle centered at the given coordinates with diameter \p d.
+ /** The current position is set to the center. */
+ void drawCircle(int x, int y, int d) { gotoXY(x, y); drawCircle(d); };
+
+ /// Draw a rectangle with top left at the current position and size \p w, \p h.
+ /** The current position is not changed. */
+ void drawRect(int w, int h);
+ /// Draw a rectangle with top left at the given coordinates and size \p w, \p h.
+ /** The current position is set to the top left corner. */
+ void drawRect(int x, int y, int w, int h) { gotoXY(x, y); drawRect(w, h); };
+
+ /// Flood-fill an area at the current position.
+ /** A continuous area with the same RGB value as the selected pixel is
+ changed to the current color. The current position is not changed. */
+ void fill();
+ /// Flood-fill an area at a given position.
+ /** see fill(), but moves current position to the given coordinates */
+ void fill(int x, int y) { gotoXY(x,y); fill(); };
+
+ /// Draw a filled circle centered at the current position with diameter \p d.
+ /** The current position is not changed. */
+ void fillCircle(int d);
+ /// Draw a filled circle centered at the given coordinates with diameter \p d.
+ /** The current position is set to the center. */
+ void fillCircle(int x, int y, int d) { gotoXY(x, y); fillCircle(d); };
+
+ /// Draw a filled rectangle with top left at the current position and size \p w, \p h.
+ /** The current position is not changed. */
+ void fillRect(int w, int h);
+ /// Draw a filled rectangle with top left at the given coordinates and size \p w, \p h.
+ /** The current position is set to the top left corner. */
+ void fillRect(int x, int y, int w, int h) { gotoXY(x, y); fillRect(w, h); };
+
+ /// Draw a text string.
+ /** Current position is the leftmost pixel on the baseline of the character.
+ The current position is moved to the next character. Background is not
+ cleared. If \p interpret is \c true, some ASCII control characters like
+ backspace, line-feed, tab and ANSI colors are interpreted, and text is word-wrapped at
+ the window borders. You can optionally pass start and length of a substring
+ to print */
+ void drawText(const String& text, bool interpret = true, Size start = 0, Size len = (Size)-1);
+
+ /// Draw a text string at a fixed position.
+ /** see drawText(const String& text, bool interpret, Size start, Size len) */
+ template <typename STR> void drawText(int x, int y, const STR text, bool interpret, Size start, Size len = (Size)-1) { gotoXY(x,y); drawText(String(text), interpret, start, len); };
+ /// Draw a single character.
+ /** see drawText(const STR text, bool interpret), except wrapping is
+ performed on this character only */
+ void drawText(const Char c, bool interpret = false);
+ /// Draw a single character at a fixed position.
+ /** see drawText(const Char c, bool interpret). */
+ void drawText(int x, int y, const Char c, bool interpret = false) { gotoXY(x,y); drawText(c, interpret); };
+
+ /// Copy the contents of another Drawable to this one.
+ /** The top left corner of the source Drawable is put at the current
+ position. The alpha channel is fully honoured. Additionally, an extra
+ \p alpha value may be given which is multiplied with the source
+ alpha channel. The current position remains unchanged. */
+ void drawDrawable(Drawable &d, unsigned char alpha = 1);
+ /// Copy the contents of another Drawable to this one at a given position.
+ /** see drawDrawable(Drawable &d, unsigned char alpha). The current position
+ is moved to the given coordinates. */
+ void drawDrawable(int x, int y, Drawable &d, unsigned char alpha = 1) { gotoXY(x,y); drawDrawable(d, alpha); };
+
+};
+
+/** \brief A variable- or fixed-width fixed-size Font.
+ *
+ * These Fonts can't be scaled once instantiated, but it is possible
+ * to subclass this abstract font class to encapsulate scalable
+ * fonts. Fonts can be anti-aliasing and multi-coloured, depending on
+ * subclass. There is no encoding enforced. The font object implicitly
+ * knows about it's encoding. Because of that, there is a utility
+ * function for string examination as well.
+ *
+ * You can't instantiate objects of this class directly, use one of the
+ * subclasses like BitmapFont.
+ *
+ * This class provides a font registry which allows you to register
+ * and retrieve font objects using a name of your choice. It is recommended
+ * to use a naming scheme like "FontName-variant-pixelsize-encoding" where
+ * variant is "normal", "bold", "italic" or "bolditalic". No one enforces
+ * this, however.
+ *
+ * The GUI uses some special font names. You must add them before creating
+ * the relevant GUI item.
+ * \li \c default - used if a requested font was not found
+ * \li \c title - GUI::ToplevelWindow title
+ * \li \c input - GUI::Input
+ *
+ */
+class Font {
+protected:
+ friend void Drawable::drawText(const Char c, bool interpret);
+ friend void Drawable::drawText(const String& s, bool interpret, Size start, Size len);
+
+ /// Compare two strings for 'less-than'.
+ struct ltstr { bool operator()(const char* s1, const char* s2) const {
+ return strcmp(s1, s2) < 0;
+ } };
+ /// Font registry. Contains all known font objects indexed by name.
+ static std::map<const char *,Font *,ltstr> registry;
+
+ /// Default constructor.
+ Font() {};
+
+ /// Draw character to a drawable at the current position.
+ /** \p d's current position is advanced to the position of the next character.
+ * The y coordinate is located at the baseline before and after the call. */
+ virtual void drawChar(Drawable *d, const Char c) const = 0;
+
+ /// Draw \p len characters to a drawable at the current position, starting at string position \p start.
+ /** This can be used to provide kerning. */
+ virtual void drawString(Drawable *d, const String &s, Size start, Size len) const {
+ if (len > s.size()-start) len = (Size)(s.size()-start);
+ len += start;
+ while (start < len) drawChar(d,s[start++]);
+ }
+
+public:
+ /// Return a font with the given name (case-sensitive).
+ /** If no font was registered with that name, returns NULL. */
+ static const Font *getFont(const char *name) {
+ std::map<const char *,Font *,ltstr>::iterator i = registry.find(name);
+ if (i == registry.end()) return(strcmp(name,"default")?getFont("default"):NULL);
+ return (*i).second;
+ }
+
+ /// Add a font with a given name.
+ /** Don't delete this font once added. This class will do that for you.
+ * If a font with that name already exists, it will be replaced. */
+ static void addFont(const char *name, Font *font) {
+ std::map<const char *,Font *,ltstr>::iterator i = registry.find(name);
+ if (i != registry.end()) delete (*i).second;
+ registry[name] = font;
+ }
+
+ virtual ~Font() {};
+
+ /// Retrieve total height of font in pixels.
+ virtual int getHeight() const = 0;
+
+ /// Retrieve the ascent, i.e. the number of pixels above the base line.
+ virtual int getAscent() const = 0;
+
+ /// Return width of a string.
+ template <typename STR> int getWidth(const STR s, Size start = 0, Size len = (Size)-1) const {
+ return this->getWidth(String(s), start, len);
+ }
+
+ /// Retrieve width of a character.
+ virtual int getWidth(Char c = 'M') const = 0;
+
+ /// Retrieve width of a string of characters.
+ /** Can be used to provide kerning. */
+ virtual int getWidth(const String &s, Size start = 0, Size len = (Size)-1) const {
+ int width = 0;
+ if (start+len > s.size()) len = (Size)(s.size()-start);
+ while (len--) width += getWidth(s[start++]);
+ return width;
+ }
+
+ /// Characters with special appearance or meaning.
+ enum SpecialChar { CR = '\r', LF = '\n', BS = '\b', Tab = '\t', Space = ' ', ESC = 27 };
+
+ /// Convert a character to an equivalent SpecialChar. May return values not in SpecialChar.
+ virtual SpecialChar toSpecial(Char c) const { return (SpecialChar)(c<255?c:255); }
+
+ /// Convert a SpecialChar to an equivalent character.
+ virtual Char fromSpecial(SpecialChar c) const { return c; }
+};
+
+/** \brief A bitmap font.
+ * This is a font which is defined by a binary bit map. Each bit in the bitmap
+ * defines one pixel. Bits may be arranged in various common patterns.
+ *
+ * Encoding free, character size depends on number of characters in the font.
+ */
+class BitmapFont : public Font {
+protected:
+ /// The actual font data.
+ const unsigned char *const bitmap;
+
+ /// Width of a character cell.
+ const int width;
+
+ /// Height of all characters.
+ const int height;
+
+ /// Ascent of all characters.
+ const int ascent;
+
+ /// Array of character widths. If font is fixed-width, this is NULL and \a width is used.
+ const int *const widths;
+
+ /// Array of character ascents. If this is NULL, \a ascent is used for all characters.
+ /** This allows character data to be flush to the top or bottom of it's bitmap area. */
+ const int *const ascents;
+
+ /// True if set bits are background, false otherwise.
+ const bool background_set;
+
+ /// Number of bits added to get from a column to the next column.
+ const int col_step;
+
+ /// Distance between 2 rows of a character, or 0 for variable-width rows.
+ const int row_step;
+
+ /// Distance of two characters in the bitmap in bits.
+ /** This is calculated as abs(row_step*height) unless explicitly specified. */
+ const int character_step;
+
+ /// Array of pointers to font data. If set, neither \a bitmap nor \a character_step are used.
+ const unsigned char *const*const char_position;
+
+ /// Array of SpecialChar equivalents.
+ /** If unset, encoding is assumed ASCII-like for the first 32 characters */
+ const SpecialChar *const special;
+
+ /// If \c true, then all arrays are freed on destruction.
+ const bool owner;
+
+ /// Last defined character. Characters above this will be ignored.
+ const Char last;
+
+ /// Draw character to a drawable at the current position.
+ /** \p d's current position is advanced to the position of the next character.
+ * The y coordinate is located at the baseline before and after the call. */
+ virtual void drawChar(Drawable *d, const Char c) const;
+
+public:
+ /// Constructor.
+ /** The default values provide an 8 bit wide fixed-width pixel layout with each byte a row,
+ * arranged top-to-bottom just like a PC's VGA font. See the individual member documentation
+ * for details on the parameters. */
+ BitmapFont(const unsigned char *data, int height, int ascent, bool owner = false,
+ int width = 8, bool background_set = false,
+ int col_step = -1, int row_step = 8, int character_step = 0, Char last = 256,
+ const int *widths = NULL, const int *ascents = NULL,
+ const unsigned char *const* char_position = NULL,
+ const SpecialChar *special = NULL);
+
+ virtual ~BitmapFont();
+
+ /// Retrieve total height of font in pixels.
+ virtual int getHeight() const { return height; };
+
+ /// Retrieve the ascent, i.e. the number of pixels above the base line.
+ virtual int getAscent() const { return ascent; };
+
+ /// Retrieve width of a character.
+ virtual int getWidth(Char c = 'M') const { return (widths != NULL?widths[c]:width); };
+
+ /// Convert a character to an equivalent SpecialChar. See Font::toSpecial(Char c)
+ virtual SpecialChar toSpecial(Char c) const { return (special != NULL?special[c]:Font::toSpecial(c)); }
+
+ /// Convert a character to an equivalent character. See Font::fromSpecial(SpecialChar c).
+ virtual Char fromSpecial(SpecialChar c) const { if (special == NULL) return Font::fromSpecial(c); Char i = 0; while(special[i] != c) i++; return i; }
+
+};
+
+class ActionEventSource;
+/// Callback for action events.
+struct ActionEventSource_Callback {
+public:
+ /// Handler with optional String argument.
+ /** If the event source doesn't provide an additional argument, the name will be used. */
+ virtual void actionExecuted(ActionEventSource *source, const String &arg) = 0;
+ virtual ~ActionEventSource_Callback() {}
+};
+
+/// Event class for action events.
+/** Action events are events generated by GUI elements like Buttons, Menus and by pressing Enter in
+ * an Input. All of these are handled similarly: The source of such an event has a name, and the
+ * Event may also be connected with a String describing what was executed, like the name of the
+ * Menu entry or the contents of the Input.
+ */
+class ActionEventSource {
+protected:
+ /// List of registered action handlers.
+ std::list<ActionEventSource_Callback *> actionHandlers;
+
+ /// This event source's name.
+ /** The name is primarily meant for your own purposes, for example identification of the activated
+ * element. One exception are Menubars, which display the name of their Menus. */
+ String name;
+
+public:
+ /// Create a named event source.
+ template <typename STR> ActionEventSource(const STR name) : name(String(name)) { }
+
+ /// Dummy destructor.
+ virtual ~ActionEventSource() {}
+
+ /// Add a button press event handler.
+ void addActionHandler(ActionEventSource_Callback *handler) { actionHandlers.push_back(handler); }
+
+ /// Remove a button press event handler.
+ void removeActionHandler(ActionEventSource_Callback *handler) { actionHandlers.remove(handler); }
+
+ /// Set the name of this event source.
+ template <typename STR> void setName(const STR name) { this->name = String(name); }
+
+ /// Get the name of this event source.
+ const String &getName() const { return name; }
+
+ /// Execute handlers.
+ void executeAction(const String &arg) {
+ std::list<ActionEventSource_Callback*>::iterator i = actionHandlers.begin();
+ bool end = (i == actionHandlers.end());
+ while (!end) {
+ ActionEventSource_Callback *c = *i;
+ ++i;
+ end = (i == actionHandlers.end());
+ c->actionExecuted(this,arg);
+ }
+ }
+
+ /// Execute handlers.
+ void executeAction() { executeAction(name); }
+};
+
+/// Internal class for windows whose child content should not span the entire area.
+class BorderedWindow : public Window {
+protected:
+ /// Borders.
+ int border_left, border_top, border_right, border_bottom;
+
+ /// Create a bordered window.
+ BorderedWindow(Window *parent, int x, int y, int w, int h, int bl, int bt, int br, int bb) :
+ Window(parent,x,y,w,h), border_left(bl), border_top(bt), border_right(br), border_bottom(bb) {}
+
+public:
+ virtual void paintAll(Drawable &d) const;
+ virtual bool mouseMoved(int x, int y);
+ virtual bool mouseDown(int x, int y, MouseButton button);
+ virtual bool mouseDragged(int x, int y, MouseButton button);
+ virtual int getScreenX() const { return Window::getScreenX()+border_left; }
+ virtual int getScreenY() const { return Window::getScreenY()+border_top; }
+};
+
+/// A text label
+/** Text labels are positioned relative to the top left corner of their bounding box.
+ * They size themselves automatically and display their text in non-interpreted mode.
+ */
+
+class Label : public Window {
+ /// The Font used
+ const Font *font;
+
+ /// Text color
+ RGB color;
+
+ /// Text
+ String text;
+
+ /// multiline text?
+ bool interpret;
+
+public:
+ /// Create a text label with given position, \p text, \p font and \p color.
+ /** If \p width is given, the resulting label is a word-wrapped multiline label */
+ template <typename STR> Label(Window *parent, int x, int y, const STR text, int width = 0, const Font *font = Font::getFont("default"), RGB color = Color::Text) :
+ Window(parent, x, y, (width?width:1), 1), font(font), color(color), text(text), interpret(width != 0)
+ { resize(); }
+
+ /// Set a new text. Size of the label is adjusted accordingly.
+ template <typename STR> void setText(const STR text) { this->text = text; resize(); }
+ /// Retrieve current text
+ const String& getText() { return text; }
+
+ /// Set a new font. Size of the label is adjusted accordingly.
+ void setFont(const Font *font) { this->font = font; resize(); }
+ /// Retrieve current font
+ const Font *getFont() { return font; }
+
+ /// Set a new text color.
+ void setColor(const RGB color) { this->color = color; resize(); }
+ /// Retrieve current color
+ RGB getColor() { return color; }
+
+ /// Calculate label size. Parameters are ignored.
+ virtual void resize(int w = -1, int h = -1) {
+ if (w == -1) w = (interpret?getWidth():0);
+ else interpret = (w != 0);
+ Drawable d((w?w:1), 1);
+ d.setFont(font);
+ d.drawText(0, font->getAscent(), text, interpret, 0);
+ if (interpret) Window::resize(w, d.getY()-font->getAscent()+font->getHeight());
+ else Window::resize(d.getX(), font->getHeight());
+ }
+
+ /// Paint label
+ virtual void paint(Drawable &d) const { d.setColor(color); d.drawText(0, font->getAscent(), text, interpret, 0); }
+
+ virtual bool raise() { return false; }
+};
+
+
+/// A single-line text input
+/** It uses Font::getFont("input") to display content.
+ * It supports selection, the clipboard and all well-known key bindings (except undo).
+ */
+class Input : public Window, public Timer_Callback, public ActionEventSource {
+protected:
+ /// The text entered.
+ String text;
+
+ /// Current position in \a text.
+ Size pos;
+
+ /// Last updated position in \a text.
+ Size lastpos;
+
+ /// Coordinates according to pos.
+ int posx, posy;
+
+ /// Selection in \a text.
+ Size start_sel, end_sel;
+
+ /// Is cursor visible at the moment?
+ bool blink;
+
+ /// Insert mode?
+ bool insert;
+
+ /// Multiline?
+ bool multi;
+
+ /// Horizontal scrolling offset.
+ int offset;
+
+ /// Ensure that pos is visible.
+ void checkOffset() {
+ if (lastpos == pos) return;
+ const Font *f = Font::getFont("input");
+ if (multi) {
+ Drawable d(width-6,1);
+ d.setFont(f);
+ d.drawText(0, 0, text, multi, 0, pos);
+ posy = d.getY();
+ posx = d.getX();
+ if (posy-offset > height-8-f->getHeight()) offset = posy-height+8+f->getHeight();
+ if (posy-offset < 0) offset = posy;
+ } else {
+ posy = 0;
+ posx = f->getWidth(text,0,pos);
+ if (f->getWidth(text,0,pos+1)-offset > width-10) offset = f->getWidth(text,0,pos+1)-width+10;
+ if (f->getWidth(text,0,(pos>0?pos-1:0))-offset < 0) offset = f->getWidth(text,0,(pos>0?pos-1:0));
+ }
+ lastpos = pos;
+ setDirty();
+ }
+
+public:
+ /// Create an input with given position and width. If not set, height is calculated from the font and input is single-line.
+ Input(Window *parent, int x, int y, int w, int h = 0) :
+ Window(parent,x,y,w,(h?h:Font::getFont("input")->getHeight()+10)), ActionEventSource("GUI::Input"),
+ text(""), pos(0), lastpos(0), posx(0), posy(0), start_sel(0), end_sel(0), blink(true), insert(true), multi(h != 0), offset(0)
+ { Timer::add(this,30); }
+
+ ~Input() {
+ Timer::remove(this);
+ }
+
+ /// Paint input.
+ virtual void paint(Drawable &d) const;
+
+ /// Clear selected area.
+ void clearSelection() {
+ text.erase(text.begin()+(pos = imin(start_sel,end_sel)),text.begin()+imax(start_sel,end_sel));
+ start_sel = end_sel = pos;
+ }
+
+ /// Copy selection to clipboard.
+ void copySelection() {
+ setClipboard(String(text.begin()+imin(start_sel,end_sel),text.begin()+imax(start_sel,end_sel)));
+ }
+
+ /// Cut selection to clipboard.
+ void cutSelection() {
+ setClipboard(String(text.begin()+imin(start_sel,end_sel),text.begin()+imax(start_sel,end_sel)));
+ clearSelection();
+ }
+
+ /// Paste from clipboard.
+ void pasteSelection() {
+ String c = getClipboard();
+ clearSelection();
+ text.insert(text.begin()+pos,c.begin(),c.end());
+ start_sel = end_sel = pos += (Size)c.size();
+ }
+
+ /// get character position corresponding to coordinates
+ Size findPos(int x, int y);
+
+ /// Set text to be edited.
+ template <typename STR> void setText(const STR text) { this->text = text; setDirty(); };
+ /// Get the entered text. If you need it longer, copy it immediately.
+ const String& getText() { return text; };
+
+ /// Handle text input.
+ virtual bool keyDown(const Key &key);
+
+ /// Handle mouse input.
+ virtual bool mouseDown(int x, int y, MouseButton button);
+
+ /// Handle mouse input.
+ virtual bool mouseDragged(int x, int y, MouseButton button);
+
+ /// Timer callback function
+ virtual Ticks timerExpired(Ticks time)
+ { blink = !blink; setDirty(); return 30; }
+
+};
+
+class ToplevelWindow;
+/// Callbacks for window events.
+struct ToplevelWindow_Callback {
+public:
+ /// The window has been asked to be closed.
+ /** Return \c false in order to block the requested action. Do not do any
+ * deallocation here, as other callbacks may abort the close process.
+ */
+ virtual bool windowClosing(ToplevelWindow *win) = 0;
+
+ /// The window will been closed.
+ /** Now it is safe to deallocate all external resources that applications
+ * may have associated with this window, like registering this window
+ * with external callbacks.
+ */
+ virtual void windowClosed(ToplevelWindow *win) = 0;
+ virtual ~ToplevelWindow_Callback() {}
+};
+
+/// An actual decorated window.
+class ToplevelWindow : public BorderedWindow, public ActionEventSource_Callback {
+protected:
+ /// Title text
+ String title;
+
+ /// Drag base
+ int dragx, dragy;
+
+ /// List of registered event handlers.
+ std::list<ToplevelWindow_Callback *> closehandlers;
+
+ /// System menu (top left)
+ Menu *systemMenu;
+
+public:
+ /// Create a new GUI Frame with title bar, border and close button
+ template <typename STR> ToplevelWindow(Screen *parent, int x, int y, int w, int h, const STR title);
+
+ /// Call cleanup handlers
+ ~ToplevelWindow() {
+ std::list<ToplevelWindow_Callback*>::iterator i = closehandlers.begin();
+ bool end = (i == closehandlers.end());
+ while (!end) {
+ ToplevelWindow_Callback *c = *i;
+ ++i;
+ end = (i == closehandlers.end());
+ c->windowClosed(this);
+ }
+ }
+
+ /// Menu callback function
+ virtual void actionExecuted(ActionEventSource *src, const String &item) {
+ if (item == String("Close")) close();
+ }
+
+ /// Add a window event handler.
+ void addCloseHandler(ToplevelWindow_Callback *handler) { closehandlers.push_back(handler); }
+
+ /// Remove a window event handler.
+ void removeCloseHandler(ToplevelWindow_Callback *handler) { closehandlers.remove(handler); }
+
+ virtual void paint(Drawable &d) const;
+ virtual bool mouseDown(int x, int y, MouseButton button);
+ virtual bool mouseDoubleClicked(int x, int y, MouseButton button) {
+ if (button == Left && x < 32 && x > 6 && y > 4 && y < 31) {
+ close();
+ return true;
+ }
+ BorderedWindow::mouseClicked(x,y,button);
+ return true;
+ }
+ virtual bool mouseUp(int x, int y, MouseButton button) {
+ if (button == Left && dragx >= 0 && dragy >= 0) {
+ dragx = dragy = -1;
+ return true;
+ }
+ BorderedWindow::mouseUp(x,y,button);
+ return true;
+ }
+ virtual bool mouseDragged(int x, int y, MouseButton button) {
+ if (button == Left && dragx >= 0 && dragy >= 0) {
+ move(x-dragx+this->x,y-dragy+this->y);
+ return true;
+ }
+ BorderedWindow::mouseDragged(x,y,button);
+ return true;
+ }
+ virtual bool mouseMoved(int x, int y) {
+ BorderedWindow::mouseMoved(x,y);
+ return true;
+ }
+
+ /// Put window on top of all other windows without changing their relative order
+ virtual bool raise() {
+ Window *last = parent->children.back();
+ parent->children.remove(this);
+ parent->children.push_back(this);
+ if (last != this) {
+ focusChanged(true);
+ last->focusChanged(false);
+ }
+ return true;
+ }
+
+ /// Set a new title.
+ template <typename STR> void setTitle(const STR title) { this->title = title; setDirty(); }
+ /// Retrieve current title.
+ const String& getTitle() { return title; }
+
+ /// Close window.
+ void close() {
+ bool doit = true;
+ std::list<ToplevelWindow_Callback*>::iterator i = closehandlers.begin();
+ bool end = (i == closehandlers.end());
+ while (!end) {
+ ToplevelWindow_Callback *c = *i;
+ ++i;
+ end = (i == closehandlers.end());
+ doit = doit && c->windowClosing(this);
+ }
+ if (doit) delete this;
+ }
+};
+
+/// A floating temporary window that is not restricted by it's parent's area.
+/** They have a parent which displays them,
+ * but that parent is not their real parent in the window hierarchy: They are always top-level elements, thus
+ * they are not clipped to their logical parent's area, they can freely overlay any part of the screen.
+ * As another consequence, they are not automatically deleted when their logical parent is deleted.
+ *
+ * You should observe the following points:
+ * \li TransientWindows behave mostly as if their logical parent was their true parent, but are not
+ * restricted to it's area
+ * \li TransientWindows are deleted automatically when the ToplevelWindow is deleted in which their logical
+ * parent resides; do NOT delete them in your destructor or bad things will happen
+ * \li only the logical parent object can show/hide a TransientWindow at will
+ * \li it will close automatically upon clicking other GUI elements, \em except for the
+ * logical parent and it's children
+ */
+class TransientWindow : public Window, Window_Callback, ToplevelWindow_Callback {
+protected:
+ /// The true parent window.
+ Window *realparent;
+
+ /// User selected position relative to logical parent.
+ int relx, rely;
+
+public:
+ /// Handle automatic hiding
+ virtual void focusChanged(bool gained) {
+ Window::focusChanged(gained);
+ if (isVisible() && !gained) {
+ if (realparent->hasFocus()) raise();
+ else setVisible(false);
+ }
+ }
+
+ /// Handle automatic delete
+ void windowClosed(ToplevelWindow *win) {
+ delete this;
+ }
+
+ /// No-op
+ bool windowClosing(ToplevelWindow *win) { return true; }
+
+ /// Create a transient window with given position and size
+ /** \a parent is the logical parent. The true parent object is
+ * always the screen the logical parent resides on. */
+ TransientWindow(Window *parent, int x, int y, int w, int h) :
+ Window(parent->getScreen(),x+parent->getScreenX(),y+parent->getScreenY(),w,h),
+ realparent(parent), relx(x), rely(y) {
+ Window *p = realparent, *last = NULL, *last2 = NULL;
+ while (p != NULL) {
+ p->addWindowHandler(this);
+ last2 = last;
+ last = p;
+ p = p->getParent();
+ }
+ dynamic_cast<ToplevelWindow *>(last2)->addCloseHandler(this);
+ }
+
+ ~TransientWindow() {
+ Window *p = realparent, *last = NULL, *last2 = NULL;
+ while (p != NULL) {
+ p->removeWindowHandler(this);
+ last2 = last;
+ last = p;
+ p = p->getParent();
+ }
+ dynamic_cast<ToplevelWindow *>(last2)->removeCloseHandler(this);
+ }
+
+ virtual void move(int x, int y) { relx = x; rely = y;
+ Window::move(x+realparent->getScreenX(),y+realparent->getScreenY()); }
+ virtual int getX() const { return x-realparent->getScreenX(); }
+ virtual int getY() const { return y-realparent->getScreenY(); }
+ virtual void setVisible(bool v) { if (v) raise(); Window::setVisible(v); }
+ virtual void windowMoved(Window *src, int x, int y) { move(relx,rely); }
+
+ /// Put window on top of all other windows without changing their relative order
+ virtual bool raise() {
+ Window *last = parent->children.back();
+ parent->children.remove(this);
+ parent->children.push_back(this);
+ if (last != this) {
+ focusChanged(true);
+ last->focusChanged(false);
+ }
+ return true;
+ }
+
+};
+
+/// A popup menu.
+/** Popup menus are used as context menus or as part of a menu bar. Menus are not visible when created.
+ * Menus have a name which can be used in event handlers to identify it, and it is used in Menubars as well.
+ *
+ * Currently, menu entries are not interpreted in any way. This may change in the future. To ensure upwards
+ * compatibility, avoid the \c '&' character in menu entries, since it may have a special meaning in future
+ * versions. Menus use the Font named "menu".
+ *
+ * Please note the general remarks in GUI::TransientWindow
+ */
+class Menu : public TransientWindow, public ActionEventSource {
+protected:
+ /// List of menu items (displayed text)
+ std::vector<String> items;
+
+ /// Currently selected menu item.
+ /** Can be -1 if no item is currently active. */
+ int selected;
+
+ /// Flag to skip the first mouseUp-event
+ bool firstMouseUp;
+
+ /// Where we grabbed the mouse from.
+ Window *mouseTakenFrom;
+
+ /// Selects menu item at position (x,y).
+ /** \a selected is set to -1 if there is no active item at that location. */
+ virtual void selectItem(int x, int y) {
+ y -= 2;
+ selected = -1;
+ const int height = Font::getFont("menu")->getHeight()+2;
+ std::vector<String>::iterator i;
+ for (i = items.begin(); i != items.end() && y > 0; ++i) {
+ selected++;
+ if ((*i).size() > 0) y -= height;
+ else y -= 12;
+ }
+ if (y > 0 || (selected >= 0 && items[selected].size() == 0)) selected = -1;
+ }
+
+ virtual Size getPreferredWidth() {
+ Size width = 0;
+ const Font *f = Font::getFont("menu");
+ std::vector<String>::iterator i;
+ for (i = items.begin(); i != items.end() && y > 0; ++i) {
+ Size newwidth = f->getWidth(*i);
+ if (newwidth > width) width = newwidth;
+ }
+ return width+39;
+ }
+
+ virtual Size getPreferredHeight() {
+ Size height = 0;
+ const Size h = Font::getFont("menu")->getHeight()+2;
+ std::vector<String>::iterator i;
+ for (i = items.begin(); i != items.end() && y > 0; ++i) {
+ height += ((*i).size() > 0?h:12);
+ }
+ return height+6;
+ }
+
+public:
+ /// Create a menu with given position
+ /** Size is determined dynamically. \a parent is the logical parent. The true parent object is
+ * always the screen the logical parent resides on. */
+ template <typename STR> Menu(Window *parent, int x, int y, const STR name) :
+ TransientWindow(parent,x,y,4,4), ActionEventSource(name), selected(-1)
+ { setVisible(false); }
+
+ ~Menu() {
+ setVisible(false);
+ }
+
+ /// Paint button.
+ virtual void paint(Drawable &d) const;
+
+ /// Highlight current item.
+ virtual bool mouseMoved(int x, int y) {
+ selectItem(x,y);
+ return true;
+ }
+
+ /// Highlight current item.
+ virtual bool mouseDragged(int x, int y, MouseButton b) {
+ selectItem(x,y);
+ return true;
+ }
+
+ virtual bool mouseDown(int x, int y, MouseButton button) { return true; }
+
+ /// Possibly select item.
+ virtual bool mouseUp(int x, int y, MouseButton button) {
+ selectItem(x,y);
+ if (firstMouseUp) firstMouseUp = false;
+ else setVisible(false);
+ execute();
+ return true;
+ }
+
+ /// Handle keyboard input.
+ virtual bool keyDown(const Key &key) {
+ if (key.special == Key::Up) selected--;
+ else if (key.special == Key::Down) selected++;
+ else if (key.special == Key::Enter) { execute(); return true; }
+ else if (key.special == Key::Escape) { setVisible(false); return true; }
+ else return true;
+ if (items[selected].size() == 0 && items.size() > 1) return keyDown(key);
+ if (selected < 0) selected = (int)(items.size()-1);
+ if (selected >= (int)items.size()) selected = 0;
+ return true;
+ }
+
+
+ /// Add a menu item at end. An empty string denotes a separator.
+ template <typename T> void addItem(const T item) {
+ items.push_back(String(item));
+ resize(getPreferredWidth(),getPreferredHeight());
+ }
+
+ /// Remove an existing menu item.
+ template <typename T> void removeItem(const T item) {
+ const String s(item);
+ std::vector<String>::iterator i = items.begin();
+ while (i != items.end() && s != (*i)) ++i;
+ if (i != items.end()) items.erase(i);
+ resize(getPreferredWidth(),getPreferredHeight());
+ }
+
+ virtual void setVisible(bool v) {
+ TransientWindow::setVisible(v);
+ if (v) {
+ parent->mouseChild = this;
+ raise();
+ firstMouseUp = true;
+ }
+ }
+
+ /// Execute menu item.
+ void execute() {
+ if (selected >= 0) {
+ setVisible(false);
+ executeAction(items[selected]);
+ }
+ }
+};
+
+/// A push button
+/** Buttons have 3D appearance and can have any child widget as content.
+ * There are convenience constructors for common forms of buttons.
+ */
+class Button : public BorderedWindow, public ActionEventSource {
+protected:
+ /// \c true, if button is currently pressed down.
+ bool pressed;
+
+public:
+ /// Create a button with given position and size
+ Button(Window *parent, int x, int y, int w, int h) : BorderedWindow(parent,x,y,w,h,6,5,6,5), ActionEventSource("GUI::Button"), pressed(0) {}
+
+ /// Create a text button.
+ /** If a size is specified, text is centered. Otherwise, button size is adjusted for the text. */
+ template <typename T> Button(Window *parent, int x, int y, const T text, int w = -1, int h = -1);
+
+ /// Paint button.
+ virtual void paint(Drawable &d) const;
+
+ /// Press button.
+ virtual bool mouseDown(int x, int y, MouseButton button) {
+ border_left = 7; border_right = 5; border_top = 7; border_bottom = 3;
+ pressed = true;
+ return true;
+ }
+
+ /// Release button.
+ virtual bool mouseUp(int x, int y, MouseButton button) {
+ border_left = 6; border_right = 6; border_top = 5; border_bottom = 5;
+ pressed = false;
+ return true;
+ }
+
+ /// Handle mouse activation.
+ virtual bool mouseClicked(int x, int y, MouseButton button) {
+ if (button == Left) {
+ executeAction();
+ return true;
+ }
+ return false;
+ }
+
+ /// Handle keyboard input.
+ virtual bool keyDown(const Key &key);
+
+ /// Handle keyboard input.
+ virtual bool keyUp(const Key &key);
+
+};
+
+/// A menu bar at the top of a ToplevelWindow
+/** Menu bars aggregate several Menus. They have a simple border at the bottom. If your application
+ * has a work area with 3D sunken look, place it so that it overlaps the menu by one row.
+ *
+ * As with Menus, avoid the character \c '&' in Menu names as it may
+ * have a special meaning in future versions. Menubars use the Font named "menu".
+ */
+class Menubar : public Window, public ActionEventSource, ActionEventSource_Callback {
+protected:
+ /// Currently activated menu index.
+ int selected;
+
+ /// Horizontal position of next menu.
+ int lastx;
+
+ /// List of Menus.
+ std::vector<Menu*> menus;
+
+public:
+ /// Create a menubar with given position and size
+ /** Height is autocalculated from font size */
+ Menubar(Window *parent, int x, int y, int w) : Window(parent,x,y,w,Font::getFont("menu")->getHeight()+5), ActionEventSource("GUI::Menubar"), selected(-1), lastx(0) {}
+
+ /// Add a Menu.
+ template <typename STR> void addMenu(const STR name) {
+ const String n(name);
+ menus.push_back(new Menu(this,lastx,height-2,n));
+ menus.back()->addActionHandler(this);
+ lastx += Font::getFont("menu")->getWidth(n)+14;
+ }
+
+ /// Add a Menuitem.
+ template <typename STR> void addItem(int index, const STR name) { menus[index]->addItem(name); }
+
+ /// Remove a Menuitem.
+ template <typename STR> void removeItem(int index, const STR name) { menus[index]->removeItem(name); }
+
+ /// Paint menubar.
+ virtual void paint(Drawable &d) const;
+
+ /// Open menu.
+ virtual bool mouseDown(int x, int y, MouseButton button) {
+ int oldselected = selected;
+ if (selected >= 0 && !menus[selected]->isVisible()) oldselected = -1;
+ if (selected >= 0) menus[selected]->setVisible(false);
+ if (x < 0 || x >= lastx) return true;
+ for (selected = (int)(menus.size()-1); menus[selected]->getX() > x; selected--) {};
+ if (oldselected == selected) selected = -1;
+ else menus[selected]->setVisible(true);
+ return true;
+ }
+
+ /// Handle keyboard input.
+ virtual bool keyDown(const Key &key) { return true; };
+
+ /// Handle keyboard input.
+ virtual bool keyUp(const Key &key) { return true; };
+
+ virtual void actionExecuted(ActionEventSource *src, const String &arg) {
+ std::list<ActionEventSource_Callback*>::iterator i = actionHandlers.begin();
+ bool end = (i == actionHandlers.end());
+ while (!end) {
+ ActionEventSource_Callback *c = *i;
+ ++i;
+ end = (i == actionHandlers.end());
+ c->actionExecuted(src,arg);
+ }
+ }
+};
+
+/// A checkbox
+/** Checkboxes can have any child widget as content.
+ * There are convenience constructors for common forms of checkboxes.
+ */
+class Checkbox : public BorderedWindow, public ActionEventSource {
+protected:
+ /// \c true, if checkbox is currently selected.
+ bool checked;
+
+public:
+ /// Create a checkbox with given position and size
+ Checkbox(Window *parent, int x, int y, int w, int h) : BorderedWindow(parent,x,y,w,h,16,0,0,0), ActionEventSource("GUI::Checkbox"), checked(0) {}
+
+ /// Create a checkbox with text label.
+ /** If a size is specified, text is centered. Otherwise, checkbox size is adjusted for the text. */
+ template <typename T> Checkbox(Window *parent, int x, int y, const T text, int w = -1, int h = -1);
+
+ /// Paint checkbox.
+ virtual void paint(Drawable &d) const;
+
+ /// Change checkbox state.
+ virtual void setChecked(bool checked) { this->checked = checked; }
+
+ /// Get checkbox state.
+ virtual bool isChecked() { return checked; }
+
+ /// Press checkbox.
+ virtual bool mouseDown(int x, int y, MouseButton button) {
+ checked = !checked;
+ return true;
+ }
+
+ /// Release checkbox.
+ virtual bool mouseUp(int x, int y, MouseButton button) {
+ execute();
+ return true;
+ }
+
+ /// Handle keyboard input.
+ virtual bool keyDown(const Key &key);
+
+ /// Handle keyboard input.
+ virtual bool keyUp(const Key &key);
+
+ /// Execute handlers.
+ virtual void execute() {
+ String arg(name);
+ if (!checked) arg.insert(arg.begin(),'!');
+ executeAction(arg);
+ }
+};
+
+class Frame;
+
+/// A radio box.
+/** Radio boxes can have any child widget as content.
+ * There are convenience constructors for common forms of radio boxes.
+ */
+class Radiobox : public BorderedWindow, public ActionEventSource {
+protected:
+ /// \c true, if radio box is currently selected.
+ bool checked;
+
+public:
+ /// Create a radio box with given position and size
+ Radiobox(Frame *parent, int x, int y, int w, int h);
+
+ /// Create a radio box with text label.
+ /** If a size is specified, text is centered. Otherwise, checkbox size is adjusted for the text. */
+ template <typename T> Radiobox(Frame *parent, int x, int y, const T text, int w = -1, int h = -1);
+
+ /// Paint radio box.
+ virtual void paint(Drawable &d) const;
+
+ /// Change radio box state.
+ virtual void setChecked(bool checked) { this->checked = checked; }
+
+ /// Get radio box state.
+ virtual bool isChecked() { return checked; }
+
+ /// Press radio box.
+ virtual bool mouseDown(int x, int y, MouseButton button) {
+ checked = true;
+ return true;
+ }
+
+ /// Release checkbox.
+ virtual bool mouseUp(int x, int y, MouseButton button) {
+ executeAction();
+ return true;
+ }
+
+ /// Handle keyboard input.
+ virtual bool keyDown(const Key &key);
+
+ /// Handle keyboard input.
+ virtual bool keyUp(const Key &key);
+};
+
+/// A rectangular 3D sunken frame
+/** These can be used as generic grouping elements and also serve as aggregators for RadioBoxes.
+ */
+class Frame : public BorderedWindow, public ActionEventSource, protected ActionEventSource_Callback {
+protected:
+ friend class Radiobox;
+
+ /// Currently selected radio box.
+ int selected;
+
+ /// Label of frame.
+ String label;
+
+ /// Execute handlers.
+ virtual void actionExecuted(ActionEventSource *src, const String &arg) {
+ for (std::list<Window *>::iterator i = children.begin(); i != children.end(); ++i) {
+ Radiobox *r = dynamic_cast<Radiobox*>(*i);
+ if (r != NULL && src != dynamic_cast<ActionEventSource*>(r)) r->setChecked(false);
+ }
+ executeAction(src->getName());
+ }
+
+public:
+ /// Create a non-labeled frame with given position and size
+ Frame(Window *parent, int x, int y, int w, int h) : BorderedWindow(parent,x,y,w,h,5,5,5,5), ActionEventSource("GUI::Frame"), selected(0) {}
+
+ /// Create a frame with text label.
+ template <typename T> Frame(Window *parent, int x, int y, int w, int h, const T text) :
+ BorderedWindow(parent,x,y,w,h,5,Font::getFont("default")->getHeight()+2,5,5),
+ ActionEventSource(text), selected(0), label(text) { }
+
+ /// Paint frame.
+ virtual void paint(Drawable &d) const;
+
+};
+
+/// A message box with a single "Close" button.
+class MessageBox : public GUI::ToplevelWindow {
+protected:
+ Label *message;
+ Button *close;
+public:
+ /// Create a new message box
+ template <typename STR> MessageBox(Screen *parent, int x, int y, int width, const STR title, const STR text) :
+ ToplevelWindow(parent, x, y, width, 1, title) {
+ message = new Label(this, 5, 5, text, width-10);
+ close = new GUI::Button(this, width/2-40, 10, "Close", 70);
+ close->addActionHandler(this);
+ setText(text);
+ }
+
+ /// Set a new text. Size of the box is adjusted accordingly.
+ template <typename STR> void setText(const STR text) {
+ message->setText(text);
+ close->move(width/2-40, 20+message->getHeight());
+ resize(width, message->getHeight()+100);
+ }
+};
+
+template <typename STR> ToplevelWindow::ToplevelWindow(Screen *parent, int x, int y, int w, int h, const STR title) :
+ BorderedWindow(parent, x, y, w, h, 6, 33, 6, 3), title(title),
+ dragx(-1), dragy(-1), closehandlers(), systemMenu(new Menu(this,-1,-2,"System Menu")) {
+ systemMenu->addItem("Move");
+ systemMenu->addItem("Resize");
+ systemMenu->addItem("");
+ systemMenu->addItem("Minimize");
+ systemMenu->addItem("Maximize");
+ systemMenu->addItem("Restore");
+ systemMenu->addItem("");
+ systemMenu->addItem("Close");
+ systemMenu->addActionHandler(this);
+}
+
+template <typename STR> Button::Button(Window *parent, int x, int y, const STR text, int w, int h) :
+ BorderedWindow(parent,x,y,w,h,6,5,6,5), ActionEventSource(text), pressed(0)
+{
+
+ Label *l = new Label(this,0,0,text);
+ if (width < 0) resize(l->getWidth()+border_left+border_right+10,height);
+ if (height < 0) resize(width,l->getHeight()+border_top+border_bottom+6);
+ l->move((width-border_left-border_right-l->getWidth())/2,
+ (height-border_top-border_bottom-l->getHeight())/2);
+}
+
+template <typename STR> Checkbox::Checkbox(Window *parent, int x, int y, const STR text, int w, int h) :
+ BorderedWindow(parent,x,y,w,h,16,0,0,0), ActionEventSource(text), checked(0)
+{
+ Label *l = new Label(this,0,0,text);
+ if (width < 0) resize(l->getWidth()+border_left+border_right+4,height);
+ if (height < 0) resize(width,l->getHeight()+border_top+border_bottom+4);
+ l->move((width-border_left-border_right-l->getWidth())/2,
+ (height-border_top-border_bottom-l->getHeight())/2);
+}
+
+template <typename STR> Radiobox::Radiobox(Frame *parent, int x, int y, const STR text, int w, int h) :
+ BorderedWindow(parent,x,y,w,h,16,0,0,0), ActionEventSource(text), checked(0)
+{
+ Label *l = new Label(this,0,0,text);
+ if (width < 0) resize(l->getWidth()+border_left+border_right+4,height);
+ if (height < 0) resize(width,l->getHeight()+border_top+border_bottom+4);
+ l->move((width-border_left-border_right-l->getWidth())/2,
+ (height-border_top-border_bottom-l->getHeight())/2);
+ addActionHandler(parent);
+}
+
+};
+
+#endif
diff --git a/src/libs/zmbv/Makefile.am b/src/libs/zmbv/Makefile.am
new file mode 100644
index 000000000..311db44bb
--- /dev/null
+++ b/src/libs/zmbv/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = zmbv.cpp zmbv.h
diff --git a/src/libs/zmbv/drvproc.cpp b/src/libs/zmbv/drvproc.cpp
new file mode 100644
index 000000000..299e5e670
--- /dev/null
+++ b/src/libs/zmbv/drvproc.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+//
+// Zipped Motion Block Video
+//
+// Based on Huffyuv by Ben Rudiak-Gould.
+// which was based on MSYUV sample code, which is:
+// Copyright (c) 1993 Microsoft Corporation.
+// All Rights Reserved.
+//
+
+#include "zmbv_vfw.h"
+
+/***************************************************************************
+ * DriverProc - The entry point for an installable driver.
+ *
+ * PARAMETERS
+ * dwDriverId: For most messages, <dwDriverId> is the DWORD
+ * value that the driver returns in response to a <DRV_OPEN> message.
+ * Each time that the driver is opened, through the <DrvOpen> API,
+ * the driver receives a <DRV_OPEN> message and can return an
+ * arbitrary, non-zero value. The installable driver interface
+ * saves this value and returns a unique driver handle to the
+ * application. Whenever the application sends a message to the
+ * driver using the driver handle, the interface routes the message
+ * to this entry point and passes the corresponding <dwDriverId>.
+ * This mechanism allows the driver to use the same or different
+ * identifiers for multiple opens but ensures that driver handles
+ * are unique at the application interface layer.
+ *
+ * The following messages are not related to a particular open
+ * instance of the driver. For these messages, the dwDriverId
+ * will always be zero.
+ *
+ * DRV_LOAD, DRV_FREE, DRV_ENABLE, DRV_DISABLE, DRV_OPEN
+ *
+ * hDriver: This is the handle returned to the application by the
+ * driver interface.
+ *
+ * uiMessage: The requested action to be performed. Message
+ * values below <DRV_RESERVED> are used for globally defined messages.
+ * Message values from <DRV_RESERVED> to <DRV_USER> are used for
+ * defined driver protocols. Messages above <DRV_USER> are used
+ * for driver specific messages.
+ *
+ * lParam1: Data for this message. Defined separately for
+ * each message
+ *
+ * lParam2: Data for this message. Defined separately for
+ * each message
+ *
+ * RETURNS
+ * Defined separately for each message.
+ *
+ ***************************************************************************/
+extern "C" LRESULT PASCAL __declspec(dllexport) DriverProc(DWORD dwDriverID, HDRVR hDriver, UINT uiMessage, LPARAM lParam1, LPARAM lParam2) {
+ CodecInst* pi = (CodecInst*)dwDriverID;
+
+ switch (uiMessage) {
+ case DRV_LOAD:
+ return (LRESULT)1L;
+
+ case DRV_FREE:
+ return (LRESULT)1L;
+
+ case DRV_OPEN:
+ // GAAH! This used to return a pointer to 0xFFFF0000 when lParam==0!
+ return (LRESULT)(DWORD)(UINT) Open((ICOPEN*) lParam2);
+
+ case DRV_CLOSE:
+ if (pi) Close(pi);
+ return (LRESULT)1L;
+
+ /*********************************************************************
+
+ state messages
+
+ *********************************************************************/
+
+ // cwk
+ case DRV_QUERYCONFIGURE: // configuration from drivers applet
+ return (LRESULT)1L;
+
+ case DRV_CONFIGURE:
+ pi->Configure((HWND)lParam1);
+ return DRV_OK;
+
+ case ICM_CONFIGURE:
+ //
+ // return ICERR_OK if you will do a configure box, error otherwise
+ //
+ if (lParam1 == -1)
+ return pi->QueryConfigure() ? ICERR_OK : ICERR_UNSUPPORTED;
+ else
+ return pi->Configure((HWND)lParam1);
+
+ case ICM_ABOUT:
+ //
+ // return ICERR_OK if you will do a about box, error otherwise
+ //
+ if (lParam1 == -1)
+ return pi->QueryAbout() ? ICERR_OK : ICERR_UNSUPPORTED;
+ else
+ return pi->About((HWND)lParam1);
+
+ case ICM_GETSTATE:
+ return pi->GetState((LPVOID)lParam1, (DWORD)lParam2);
+
+ case ICM_SETSTATE:
+ return pi->SetState((LPVOID)lParam1, (DWORD)lParam2);
+
+ case ICM_GETINFO:
+ return pi->GetInfo((ICINFO*)lParam1, (DWORD)lParam2);
+
+ case ICM_GETDEFAULTQUALITY:
+ if (lParam1) {
+ *((LPDWORD)lParam1) = 10000;
+ return ICERR_OK;
+ }
+ break;
+
+ /*********************************************************************
+
+ compression messages
+
+ *********************************************************************/
+
+ case ICM_COMPRESS_QUERY:
+ return pi->CompressQuery((LPBITMAPINFOHEADER)lParam1, (LPBITMAPINFOHEADER)lParam2);
+
+ case ICM_COMPRESS_BEGIN:
+ return pi->CompressBegin((LPBITMAPINFOHEADER)lParam1, (LPBITMAPINFOHEADER)lParam2);
+
+ case ICM_COMPRESS_GET_FORMAT:
+ return pi->CompressGetFormat((LPBITMAPINFOHEADER)lParam1, (LPBITMAPINFOHEADER)lParam2);
+
+ case ICM_COMPRESS_GET_SIZE:
+ return pi->CompressGetSize((LPBITMAPINFOHEADER)lParam1, (LPBITMAPINFOHEADER)lParam2);
+
+ case ICM_COMPRESS:
+ return pi->Compress((ICCOMPRESS*)lParam1, (DWORD)lParam2);
+
+ case ICM_COMPRESS_END:
+ return pi->CompressEnd();
+
+ /*********************************************************************
+
+ decompress messages
+
+ *********************************************************************/
+
+ case ICM_DECOMPRESS_QUERY:
+ return pi->DecompressQuery((LPBITMAPINFOHEADER)lParam1, (LPBITMAPINFOHEADER)lParam2);
+
+ case ICM_DECOMPRESS_BEGIN:
+ return pi->DecompressBegin((LPBITMAPINFOHEADER)lParam1, (LPBITMAPINFOHEADER)lParam2);
+
+ case ICM_DECOMPRESS_GET_FORMAT:
+ return pi->DecompressGetFormat((LPBITMAPINFOHEADER)lParam1, (LPBITMAPINFOHEADER)lParam2);
+
+ case ICM_DECOMPRESS_GET_PALETTE:
+ return pi->DecompressGetPalette((LPBITMAPINFOHEADER)lParam1, (LPBITMAPINFOHEADER)lParam2);
+
+ case ICM_DECOMPRESS:
+ return pi->Decompress((ICDECOMPRESS*)lParam1, (DWORD)lParam2);
+
+ case ICM_DECOMPRESS_END:
+ return pi->DecompressEnd();
+
+ /*********************************************************************
+
+ standard driver messages
+
+ *********************************************************************/
+
+ case DRV_DISABLE:
+ case DRV_ENABLE:
+ return (LRESULT)1L;
+
+ case DRV_INSTALL:
+ case DRV_REMOVE:
+ return (LRESULT)DRV_OK;
+ }
+
+ if (uiMessage < DRV_USER)
+ return DefDriverProc(dwDriverID, hDriver, uiMessage, lParam1, lParam2);
+ else
+ return ICERR_UNSUPPORTED;
+}
+
+
+HMODULE hmoduleCodec=0;
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD, LPVOID) {
+ hmoduleCodec = (HMODULE) hinstDLL;
+ return TRUE;
+}
diff --git a/src/libs/zmbv/makedll.mk b/src/libs/zmbv/makedll.mk
new file mode 100644
index 000000000..c2b0b0249
--- /dev/null
+++ b/src/libs/zmbv/makedll.mk
@@ -0,0 +1,17 @@
+CPP=g++
+CPPFLAGS=-O3
+LIBS=-static -lz -lwinmm -static-libgcc -static-libstdc++
+
+OBJ = resource.o zmbv.o drvproc.o zmbv_vfw.o
+
+default: zmbv.dll
+
+
+resource.o: resource.rc
+ windres -i resource.rc -o resource.o
+
+%.o: %.cpp
+ $(CPP) -c -o $@ $< $(CPPFLAGS)
+
+zmbv.dll: $(OBJ)
+ $(CPP) -shared -o $@ $^ $(CPPFLAGS) $(LIBS) zmbv_mingw.def \ No newline at end of file
diff --git a/src/libs/zmbv/resource.h b/src/libs/zmbv/resource.h
new file mode 100644
index 000000000..0d6f72a08
--- /dev/null
+++ b/src/libs/zmbv/resource.h
@@ -0,0 +1,8 @@
+//{{NO_DEPENDENCIES}}
+
+#define IDD_ABOUT 101
+#define IDD_CONFIGURE 102
+#define IDC_HOMEPAGE 1000
+#define IDC_EMAIL 1001
+#define IDC_SLIDER1 1008
+
diff --git a/src/libs/zmbv/resource.rc b/src/libs/zmbv/resource.rc
new file mode 100644
index 000000000..5e4e3a484
--- /dev/null
+++ b/src/libs/zmbv/resource.rc
@@ -0,0 +1,40 @@
+#include "resource.h"
+#include "windows.h"
+
+#ifndef IDC_STATIC
+#define IDC_STATIC -1
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUT DIALOGEX 0, 0, 167, 55
+STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "DOSBox Video Codec v0.1a"
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,131,34,29,14
+ CTEXT "Zipped Motion Block Video v 0.1\nCopyright 2009-2019 DOSBox Team",
+ IDC_STATIC,7,7,153,25,SS_NOPREFIX
+ PUSHBUTTON "Email author",IDC_EMAIL,7,34,50,14
+ PUSHBUTTON "Visit home page",IDC_HOMEPAGE,59,34,58,14
+END
+
+IDD_CONFIGURE DIALOGEX 0, 0, 213, 146
+STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "ZMBV configuration dialog"
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ PUSHBUTTON "Email author",IDC_EMAIL,44,86,50,14
+ PUSHBUTTON "Visit home page",IDC_HOMEPAGE,109,87,58,14
+ DEFPUSHBUTTON "OK",IDOK,44,125,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,117,125,50,14
+ CONTROL "",IDC_SLIDER1,"msctls_trackbar32",TBS_BOTH |
+ TBS_NOTICKS | WS_TABSTOP,57,30,92,18
+END
+
+
diff --git a/src/libs/zmbv/zmbv.cpp b/src/libs/zmbv/zmbv.cpp
new file mode 100644
index 000000000..dc2c0478d
--- /dev/null
+++ b/src/libs/zmbv/zmbv.cpp
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#include <zlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "zmbv.h"
+
+#define DBZV_VERSION_HIGH 0
+#define DBZV_VERSION_LOW 1
+
+#define COMPRESSION_NONE 0
+#define COMPRESSION_ZLIB 1
+
+#define MAX_VECTOR 16
+
+#define Mask_KeyFrame 0x01
+#define Mask_DeltaPalette 0x02
+
+zmbv_format_t BPPFormat( int bpp ) {
+ switch (bpp) {
+ case 8:
+ return ZMBV_FORMAT_8BPP;
+ case 15:
+ return ZMBV_FORMAT_15BPP;
+ case 16:
+ return ZMBV_FORMAT_16BPP;
+ case 32:
+ return ZMBV_FORMAT_32BPP;
+ }
+ return ZMBV_FORMAT_NONE;
+}
+int VideoCodec::NeededSize( int _width, int _height, zmbv_format_t _format) {
+ int f;
+ switch (_format) {
+ case ZMBV_FORMAT_8BPP:f = 1;break;
+ case ZMBV_FORMAT_15BPP:f = 2;break;
+ case ZMBV_FORMAT_16BPP:f = 2;break;
+ case ZMBV_FORMAT_32BPP:f = 4;break;
+ default:
+ return -1;
+ }
+ f = f*_width*_height + 2*(1+(_width/8)) * (1+(_height/8))+1024;
+ return f + f/1000;
+}
+
+bool VideoCodec::SetupBuffers(zmbv_format_t _format, int blockwidth, int blockheight) {
+ FreeBuffers();
+ palsize = 0;
+ switch (_format) {
+ case ZMBV_FORMAT_8BPP:
+ pixelsize = 1;
+ palsize = 256;
+ break;
+ case ZMBV_FORMAT_15BPP:
+ pixelsize = 2;
+ break;
+ case ZMBV_FORMAT_16BPP:
+ pixelsize = 2;
+ break;
+ case ZMBV_FORMAT_32BPP:
+ pixelsize = 4;
+ break;
+ default:
+ return false;
+ };
+ bufsize = (height+2*MAX_VECTOR)*pitch*pixelsize+2048;
+
+ buf1 = new unsigned char[bufsize];
+ buf2 = new unsigned char[bufsize];
+ work = new unsigned char[bufsize];
+
+ int xblocks = (width/blockwidth);
+ int xleft = width % blockwidth;
+ if (xleft) xblocks++;
+ int yblocks = (height/blockheight);
+ int yleft = height % blockheight;
+ if (yleft) yblocks++;
+ blockcount=yblocks*xblocks;
+ blocks=new FrameBlock[blockcount];
+
+ if (!buf1 || !buf2 || !work || !blocks) {
+ FreeBuffers();
+ return false;
+ }
+ int y,x,i;
+ i=0;
+ for (y=0;y<yblocks;y++) {
+ for (x=0;x<xblocks;x++) {
+ blocks[i].start=((y*blockheight)+MAX_VECTOR)*pitch+
+ (x*blockwidth)+MAX_VECTOR;
+ if (xleft && x==(xblocks-1)) {
+ blocks[i].dx=xleft;
+ } else {
+ blocks[i].dx=blockwidth;
+ }
+ if (yleft && y==(yblocks-1)) {
+ blocks[i].dy=yleft;
+ } else {
+ blocks[i].dy=blockheight;
+ }
+ i++;
+ }
+ }
+
+ memset(buf1,0,bufsize);
+ memset(buf2,0,bufsize);
+ memset(work,0,bufsize);
+ oldframe=buf1;
+ newframe=buf2;
+ format = _format;
+ return true;
+}
+
+void VideoCodec::CreateVectorTable(void) {
+ int x,y,s;
+ VectorCount=1;
+
+ VectorTable[0].x=VectorTable[0].y=0;
+ for (s=1;s<=10;s++) {
+ for (y=0-s;y<=0+s;y++) for (x=0-s;x<=0+s;x++) {
+ if (abs(x)==s || abs(y)==s) {
+ VectorTable[VectorCount].x=x;
+ VectorTable[VectorCount].y=y;
+ VectorCount++;
+ }
+ }
+ }
+}
+
+template<class P>
+INLINE int VideoCodec::PossibleBlock(int vx,int vy,FrameBlock * block) {
+ int ret=0;
+ P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
+ P * pnew=((P*)newframe)+block->start;;
+ for (int y=0;y<block->dy;y+=4) {
+ for (int x=0;x<block->dx;x+=4) {
+ int test=0-((pold[x]-pnew[x])&0x00ffffff);
+ ret-=(test>>31);
+ }
+ pold+=pitch*4;
+ pnew+=pitch*4;
+ }
+ return ret;
+}
+
+template<class P>
+INLINE int VideoCodec::CompareBlock(int vx,int vy,FrameBlock * block) {
+ int ret=0;
+ P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
+ P * pnew=((P*)newframe)+block->start;;
+ for (int y=0;y<block->dy;y++) {
+ for (int x=0;x<block->dx;x++) {
+ int test=0-((pold[x]-pnew[x])&0x00ffffff);
+ ret-=(test>>31);
+ }
+ pold+=pitch;
+ pnew+=pitch;
+ }
+ return ret;
+}
+
+template<class P>
+INLINE void VideoCodec::AddXorBlock(int vx,int vy,FrameBlock * block) {
+ P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
+ P * pnew=((P*)newframe)+block->start;
+ for (int y=0;y<block->dy;y++) {
+ for (int x=0;x<block->dx;x++) {
+ *((P*)&work[workUsed])=pnew[x] ^ pold[x];
+ workUsed+=sizeof(P);
+ }
+ pold+=pitch;
+ pnew+=pitch;
+ }
+}
+
+template<class P>
+void VideoCodec::AddXorFrame(void) {
+ int written=0;
+ int lastvector=0;
+ signed char * vectors=(signed char*)&work[workUsed];
+ /* Align the following xor data on 4 byte boundary*/
+ workUsed=(workUsed + blockcount*2 +3) & ~3;
+ int totalx=0;
+ int totaly=0;
+ for (int b=0;b<blockcount;b++) {
+ FrameBlock * block=&blocks[b];
+ int bestvx = 0;
+ int bestvy = 0;
+ int bestchange=CompareBlock<P>(0,0, block);
+ int possibles=64;
+ for (int v=0;v<VectorCount && possibles;v++) {
+ if (bestchange<4) break;
+ int vx = VectorTable[v].x;
+ int vy = VectorTable[v].y;
+ if (PossibleBlock<P>(vx, vy, block) < 4) {
+ possibles--;
+// if (!possibles) Msg("Ran out of possibles, at %d of %d best %d\n",v,VectorCount,bestchange);
+ int testchange=CompareBlock<P>(vx,vy, block);
+ if (testchange<bestchange) {
+ bestchange=testchange;
+ bestvx = vx;
+ bestvy = vy;
+ }
+ }
+ }
+ vectors[b*2+0]=(bestvx << 1);
+ vectors[b*2+1]=(bestvy << 1);
+ if (bestchange) {
+ vectors[b*2+0]|=1;
+ AddXorBlock<P>(bestvx, bestvy, block);
+ }
+ }
+}
+
+bool VideoCodec::SetupCompress( int _width, int _height ) {
+ width = _width;
+ height = _height;
+ pitch = _width + 2*MAX_VECTOR;
+ format = ZMBV_FORMAT_NONE;
+ if (deflateInit (&zstream, 4) != Z_OK)
+ return false;
+ return true;
+}
+
+bool VideoCodec::SetupDecompress( int _width, int _height) {
+ width = _width;
+ height = _height;
+ pitch = _width + 2*MAX_VECTOR;
+ format = ZMBV_FORMAT_NONE;
+ if (inflateInit (&zstream) != Z_OK)
+ return false;
+ return true;
+}
+
+bool VideoCodec::PrepareCompressFrame(int flags, zmbv_format_t _format, char * pal, void *writeBuf, int writeSize) {
+ int i;
+ unsigned char *firstByte;
+
+ if (_format != format) {
+ if (!SetupBuffers( _format, 16, 16))
+ return false;
+ flags|=1; //Force a keyframe
+ }
+ /* replace oldframe with new frame */
+ unsigned char *copyFrame = newframe;
+ newframe = oldframe;
+ oldframe = copyFrame;
+
+ compress.linesDone = 0;
+ compress.writeSize = writeSize;
+ compress.writeDone = 1;
+ compress.writeBuf = (unsigned char *)writeBuf;
+ /* Set a pointer to the first byte which will contain info about this frame */
+ firstByte = compress.writeBuf;
+ *firstByte = 0;
+ //Reset the work buffer
+ workUsed = 0;workPos = 0;
+ if (flags & 1) {
+ /* Make a keyframe */
+ *firstByte |= Mask_KeyFrame;
+ KeyframeHeader * header = (KeyframeHeader *)(compress.writeBuf + compress.writeDone);
+ header->high_version = DBZV_VERSION_HIGH;
+ header->low_version = DBZV_VERSION_LOW;
+ header->compression = COMPRESSION_ZLIB;
+ header->format = format;
+ header->blockwidth = 16;
+ header->blockheight = 16;
+ compress.writeDone += sizeof(KeyframeHeader);
+ /* Copy the new frame directly over */
+ if (palsize) {
+ if (pal)
+ memcpy(&palette, pal, sizeof(palette));
+ else
+ memset(&palette,0, sizeof(palette));
+ /* keyframes get the full palette */
+ for (i=0;i<palsize;i++) {
+ work[workUsed++] = palette[i*4+0];
+ work[workUsed++] = palette[i*4+1];
+ work[workUsed++] = palette[i*4+2];
+ }
+ }
+ /* Restart deflate */
+ deflateReset(&zstream);
+ } else {
+ if (palsize && pal && memcmp(pal, palette, palsize * 4)) {
+ *firstByte |= Mask_DeltaPalette;
+ for(i=0;i<palsize;i++) {
+ work[workUsed++]=palette[i*4+0] ^ pal[i*4+0];
+ work[workUsed++]=palette[i*4+1] ^ pal[i*4+1];
+ work[workUsed++]=palette[i*4+2] ^ pal[i*4+2];
+ }
+ memcpy(&palette,pal, palsize * 4);
+ }
+ }
+ return true;
+}
+
+void VideoCodec::CompressLines(int lineCount, void *lineData[]) {
+ int linePitch = pitch * pixelsize;
+ int lineWidth = width * pixelsize;
+ int i = 0;
+ unsigned char *destStart = newframe + pixelsize*(MAX_VECTOR+(compress.linesDone+MAX_VECTOR)*pitch);
+ while ( i < lineCount && (compress.linesDone < height)) {
+ memcpy(destStart, lineData[i], lineWidth );
+ destStart += linePitch;
+ i++;compress.linesDone++;
+ }
+}
+
+int VideoCodec::FinishCompressFrame( void ) {
+ unsigned char firstByte = *compress.writeBuf;
+ if (firstByte & Mask_KeyFrame) {
+ int i;
+ /* Add the full frame data */
+ unsigned char * readFrame = newframe + pixelsize*(MAX_VECTOR+MAX_VECTOR*pitch);
+ for (i=0;i<height;i++) {
+ memcpy(&work[workUsed], readFrame, width*pixelsize);
+ readFrame += pitch*pixelsize;
+ workUsed += width*pixelsize;
+ }
+ } else {
+ /* Add the delta frame data */
+ switch (format) {
+ case ZMBV_FORMAT_8BPP:
+ AddXorFrame<char>();
+ break;
+ case ZMBV_FORMAT_15BPP:
+ case ZMBV_FORMAT_16BPP:
+ AddXorFrame<short>();
+ break;
+ case ZMBV_FORMAT_32BPP:
+ AddXorFrame<long>();
+ break;
+ }
+ }
+ /* Create the actual frame with compression */
+ zstream.next_in = (Bytef *)work;
+ zstream.avail_in = workUsed;
+ zstream.total_in = 0;
+
+ zstream.next_out = (Bytef *)(compress.writeBuf + compress.writeDone);
+ zstream.avail_out = compress.writeSize - compress.writeDone;
+ zstream.total_out = 0;
+ int res = deflate(&zstream, Z_SYNC_FLUSH);
+ return compress.writeDone + zstream.total_out;
+}
+
+template<class P>
+INLINE void VideoCodec::UnXorBlock(int vx,int vy,FrameBlock * block) {
+ P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
+ P * pnew=((P*)newframe)+block->start;
+ for (int y=0;y<block->dy;y++) {
+ for (int x=0;x<block->dx;x++) {
+ pnew[x]=pold[x]^*((P*)&work[workPos]);
+ workPos+=sizeof(P);
+ }
+ pold+=pitch;
+ pnew+=pitch;
+ }
+}
+
+template<class P>
+INLINE void VideoCodec::CopyBlock(int vx,int vy,FrameBlock * block) {
+ P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
+ P * pnew=((P*)newframe)+block->start;
+ for (int y=0;y<block->dy;y++) {
+ for (int x=0;x<block->dx;x++) {
+ pnew[x]=pold[x];
+ }
+ pold+=pitch;
+ pnew+=pitch;
+ }
+}
+
+template<class P>
+void VideoCodec::UnXorFrame(void) {
+ signed char * vectors=(signed char *)&work[workPos];
+ workPos=(workPos + blockcount*2 + 3) & ~3;
+ for (int b=0;b<blockcount;b++) {
+ FrameBlock * block=&blocks[b];
+ int delta = vectors[b*2+0] & 1;
+ int vx = vectors[b*2+0] >> 1;
+ int vy = vectors[b*2+1] >> 1;
+ if (delta) UnXorBlock<P>(vx,vy,block);
+ else CopyBlock<P>(vx,vy,block);
+ }
+}
+
+bool VideoCodec::DecompressFrame(void * framedata, int size) {
+ unsigned char *data=(unsigned char *)framedata;
+ unsigned char tag;int i;
+
+ tag = *data++;
+ if (--size<=0)
+ return false;
+ if (tag & Mask_KeyFrame) {
+ KeyframeHeader * header = (KeyframeHeader *)data;
+ size -= sizeof(KeyframeHeader);data += sizeof(KeyframeHeader);
+ if (size<=0)
+ return false;
+ if (header->low_version != DBZV_VERSION_LOW || header->high_version != DBZV_VERSION_HIGH)
+ return false;
+ if (format != (zmbv_format_t)header->format && !SetupBuffers((zmbv_format_t)header->format, header->blockwidth, header->blockheight))
+ return false;
+ inflateReset(&zstream);
+ }
+ zstream.next_in = (Bytef *)data;
+ zstream.avail_in = size;
+ zstream.total_in = 0;
+
+ zstream.next_out = (Bytef *)work;
+ zstream.avail_out = bufsize;
+ zstream.total_out = 0;
+ int res = inflate(&zstream, Z_FINISH);
+ workUsed= zstream.total_out;
+ workPos = 0;
+ if (tag & Mask_KeyFrame) {
+ if (palsize) {
+ for (i=0;i<palsize;i++) {
+ palette[i*4+0] = work[workPos++];
+ palette[i*4+1] = work[workPos++];
+ palette[i*4+2] = work[workPos++];
+ }
+ }
+ newframe = buf1;
+ oldframe = buf2;
+ unsigned char * writeframe = newframe + pixelsize*(MAX_VECTOR+MAX_VECTOR*pitch);
+ for (i=0;i<height;i++) {
+ memcpy(writeframe,&work[workPos],width*pixelsize);
+ writeframe+=pitch*pixelsize;
+ workPos+=width*pixelsize;
+ }
+ } else {
+ data = oldframe;
+ oldframe = newframe;
+ newframe = data;
+ if (tag & Mask_DeltaPalette) {
+ for (i=0;i<palsize;i++) {
+ palette[i*4+0] ^= work[workPos++];
+ palette[i*4+1] ^= work[workPos++];
+ palette[i*4+2] ^= work[workPos++];
+ }
+ }
+ switch (format) {
+ case ZMBV_FORMAT_8BPP:
+ UnXorFrame<char>();
+ break;
+ case ZMBV_FORMAT_15BPP:
+ case ZMBV_FORMAT_16BPP:
+ UnXorFrame<short>();
+ break;
+ case ZMBV_FORMAT_32BPP:
+ UnXorFrame<long>();
+ break;
+ }
+ }
+ return true;
+}
+
+void VideoCodec::Output_UpsideDown_24(void *output) {
+ int i;
+ unsigned char *r;
+ unsigned char *w = (unsigned char *)output;
+ int pad = width & 3;
+
+ for (i=height-1;i>=0;i--) {
+ r = newframe + pixelsize*(MAX_VECTOR+(i+MAX_VECTOR)*pitch);
+ switch (format) {
+ case ZMBV_FORMAT_8BPP:
+ for (int j=0;j<width;j++) {
+ int c=r[j];
+ *w++=palette[c*4+2];
+ *w++=palette[c*4+1];
+ *w++=palette[c*4+0];
+ }
+ break;
+ case ZMBV_FORMAT_15BPP:
+ for (int j=0;j<width;j++) {
+ unsigned short c = *(unsigned short *)&r[j*2];
+ *w++ = (unsigned char)(((c & 0x001f) * 0x21) >> 2);
+ *w++ = (unsigned char)(((c & 0x03e0) * 0x21) >> 7);
+ *w++ = (unsigned char)(((c & 0x7c00) * 0x21) >> 12);
+ }
+ break;
+ case ZMBV_FORMAT_16BPP:
+ for (int j=0;j<width;j++) {
+ unsigned short c = *(unsigned short *)&r[j*2];
+ *w++ = (unsigned char)(((c & 0x001f) * 0x21) >> 2);
+ *w++ = (unsigned char)(((c & 0x07e0) * 0x41) >> 9);
+ *w++ = (unsigned char)(((c & 0xf800) * 0x21) >> 13);
+ }
+ break;
+ case ZMBV_FORMAT_32BPP:
+ for (int j=0;j<width;j++) {
+ *w++ = r[j*4+0];
+ *w++ = r[j*4+1];
+ *w++ = r[j*4+2];
+ }
+ break;
+ }
+
+ // Maintain 32-bit alignment for scanlines.
+ w += pad;
+ }
+}
+
+void VideoCodec::FreeBuffers(void) {
+ if (blocks) {
+ delete[] blocks;blocks=0;
+ }
+ if (buf1) {
+ delete[] buf1;buf1=0;
+ }
+ if (buf2) {
+ delete[] buf2;buf2=0;
+ }
+ if (work) {
+ delete[] work;work=0;
+ }
+}
+
+
+VideoCodec::VideoCodec() {
+ CreateVectorTable();
+ blocks = 0;
+ buf1 = 0;
+ buf2 = 0;
+ work = 0;
+ memset( &zstream, 0, sizeof(zstream));
+}
diff --git a/src/libs/zmbv/zmbv.def b/src/libs/zmbv/zmbv.def
new file mode 100644
index 000000000..00b2164d9
--- /dev/null
+++ b/src/libs/zmbv/zmbv.def
@@ -0,0 +1,4 @@
+LIBRARY ZMBV
+
+EXPORTS
+ DriverProc
diff --git a/src/libs/zmbv/zmbv.h b/src/libs/zmbv/zmbv.h
new file mode 100644
index 000000000..7a6705c0e
--- /dev/null
+++ b/src/libs/zmbv/zmbv.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+#ifndef DOSBOX_DOSBOX_H
+#ifdef _MSC_VER
+#define INLINE __forceinline
+#else
+#define INLINE inline
+#endif
+#endif
+
+#define CODEC_4CC "ZMBV"
+
+typedef enum {
+ ZMBV_FORMAT_NONE = 0x00,
+ ZMBV_FORMAT_1BPP = 0x01,
+ ZMBV_FORMAT_2BPP = 0x02,
+ ZMBV_FORMAT_4BPP = 0x03,
+ ZMBV_FORMAT_8BPP = 0x04,
+ ZMBV_FORMAT_15BPP = 0x05,
+ ZMBV_FORMAT_16BPP = 0x06,
+ ZMBV_FORMAT_24BPP = 0x07,
+ ZMBV_FORMAT_32BPP = 0x08
+} zmbv_format_t;
+
+void Msg(const char fmt[], ...);
+class VideoCodec {
+private:
+ struct FrameBlock {
+ int start;
+ int dx,dy;
+ };
+ struct CodecVector {
+ int x,y;
+ int slot;
+ };
+ struct KeyframeHeader {
+ unsigned char high_version;
+ unsigned char low_version;
+ unsigned char compression;
+ unsigned char format;
+ unsigned char blockwidth,blockheight;
+ };
+
+ struct {
+ int linesDone;
+ int writeSize;
+ int writeDone;
+ unsigned char *writeBuf;
+ } compress;
+
+ CodecVector VectorTable[512];
+ int VectorCount;
+
+ unsigned char *oldframe, *newframe;
+ unsigned char *buf1, *buf2, *work;
+ int bufsize;
+
+ int blockcount;
+ FrameBlock * blocks;
+
+ int workUsed, workPos;
+
+ int palsize;
+ char palette[256*4];
+ int height, width, pitch;
+ zmbv_format_t format;
+ int pixelsize;
+
+ z_stream zstream;
+
+ // methods
+ void FreeBuffers(void);
+ void CreateVectorTable(void);
+ bool SetupBuffers(zmbv_format_t format, int blockwidth, int blockheight);
+
+ template<class P>
+ void AddXorFrame(void);
+ template<class P>
+ void UnXorFrame(void);
+ template<class P>
+ INLINE int PossibleBlock(int vx,int vy,FrameBlock * block);
+ template<class P>
+ INLINE int CompareBlock(int vx,int vy,FrameBlock * block);
+ template<class P>
+ INLINE void AddXorBlock(int vx,int vy,FrameBlock * block);
+ template<class P>
+ INLINE void UnXorBlock(int vx,int vy,FrameBlock * block);
+ template<class P>
+ INLINE void CopyBlock(int vx, int vy,FrameBlock * block);
+public:
+ VideoCodec();
+ bool SetupCompress( int _width, int _height);
+ bool SetupDecompress( int _width, int _height);
+ zmbv_format_t BPPFormat( int bpp );
+ int NeededSize( int _width, int _height, zmbv_format_t _format);
+
+ void CompressLines(int lineCount, void *lineData[]);
+ bool PrepareCompressFrame(int flags, zmbv_format_t _format, char * pal, void *writeBuf, int writeSize);
+ int FinishCompressFrame( void );
+ bool DecompressFrame(void * framedata, int size);
+ void Output_UpsideDown_24(void * output);
+};
diff --git a/src/libs/zmbv/zmbv.inf b/src/libs/zmbv/zmbv.inf
new file mode 100644
index 000000000..a98ea0167
--- /dev/null
+++ b/src/libs/zmbv/zmbv.inf
@@ -0,0 +1,105 @@
+[Version]
+Signature="$CHICAGO$"
+Provider=%ZMBV_PUBLISHER%
+Class=MEDIA
+
+
+[DefaultInstall]
+CopyFiles=ZMBV.Files.Inf,ZMBV.Files.Dll
+AddReg=ZMBV.Reg9x
+UpdateInis=ZMBV.INIs
+
+[DefaultInstall.ntx86]
+CopyFiles=ZMBV.Files.Inf,ZMBV.Files.Dll
+AddReg=ZMBV.RegNTx86
+UpdateInis=ZMBV.INIs
+
+[DefaultInstall.ntamd64]
+CopyFiles=ZMBV.Files.Inf,ZMBV.Files.Dll.NTamd64
+AddReg=ZMBV.RegNTamd64
+UpdateInis=ZMBV.INIs
+
+[DefaultUnInstall]
+DelFiles=ZMBV.Files.Dll,ZMBV.Files.Inf,ZMBV.Files.Ini
+DelReg=ZMBV.Reg9x
+UpdateInis=ZMBV.INIs.Del
+
+[DefaultUnInstall.ntx86]
+DelFiles=ZMBV.Files.Dll,ZMBV.Files.Inf,ZMBV.Files.Ini
+DelReg=ZMBV.RegNTx86
+UpdateInis=ZMBV.INIs.Del
+
+[DefaultUnInstall.ntamd64]
+DelFiles=ZMBV.Files.Dll.NTamd64,ZMBV.Files.Inf,ZMBV.Files.Ini
+DelReg=ZMBV.RegNTamd64
+UpdateInis=ZMBV.INIs.Del
+
+[SourceDisksNames]
+1="Zip Motion Block Video codec","",1
+
+[SourceDisksFiles]
+ZMBV.INF=1
+
+[DestinationDirs]
+ZMBV.Files.Inf=17
+ZMBV.Files.Dll=11
+ZMBV.Files.Dll.NTamd64=10,SysWOW64
+ZMBV.Files.Ini=25
+
+[ZMBV.Files.Inf]
+zmbv.inf
+
+[ZMBV.Files.Dll]
+zmbv.dll
+
+[ZMBV.Files.Dll.NTamd64]
+zmbv.dll
+
+[ZMBV.Files.Ini]
+zmbv.ini
+
+[ZMBV.Reg9x]
+HKLM,SYSTEM\CurrentControlSet\Control\MediaResources\icm\VIDC.ZMBV,Description,,"%ZMBV_DISPLAY_NAME%"
+HKLM,SYSTEM\CurrentControlSet\Control\MediaResources\icm\VIDC.ZMBV,Driver,,"zmbv.dll"
+HKLM,SYSTEM\CurrentControlSet\Control\MediaResources\icm\VIDC.ZMBV,FriendlyName,,"%ZMBV_DISPLAY_NAME%"
+
+HKLM,Software\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV
+HKLM,Software\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,DisplayName,,"%ZMBV_UNINST_DISPLAY_NAME%"
+HKLM,Software\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,UninstallString,,"rundll.exe setupx.dll,InstallHinfSection DefaultUninstall 132 %17%\ZMBV.INF"
+
+[ZMBV.RegNTx86]
+HKLM,SOFTWARE\Microsoft\Windows NT\CurrentVersion\drivers.desc,zmbv.dll,,"%ZMBV_DISPLAY_NAME%"
+HKLM,SOFTWARE\Microsoft\Windows NT\CurrentVersion\drivers32,vidc.zmbv,,zmbv.dll
+
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,DisplayName,,"%ZMBV_UNINST_DISPLAY_NAME%"
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,Publisher,,"%ZMBV_PUBLISHER%"
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,URLInfoAbout,,"%ZMBV_URL_HOMEPAGE%"
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,NoRepair,0x10001,01,00,00,00
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,NoModify,0x10001,01,00,00,00
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,UninstallString,,"rundll32.exe setupapi,InstallHinfSection DefaultUninstall 132 %17%\ZMBV.INF"
+
+[ZMBV.RegNTamd64]
+HKLM,SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\drivers.desc,zmbv.dll,,"%ZMBV_DISPLAY_NAME%"
+HKLM,SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\drivers32,vidc.zmbv,,zmbv.dll
+
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,DisplayName,,"%ZMBV_UNINST_DISPLAY_NAME%"
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,Publisher,,"%ZMBV_PUBLISHER%"
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,URLInfoAbout,,"%ZMBV_URL_HOMEPAGE%"
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,NoRepair,0x10001,01,00,00,00
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,NoModify,0x10001,01,00,00,00
+HKLM,SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ZMBV,UninstallString,,"rundll32.exe setupapi.dll,InstallHinfSection DefaultUninstall 132 %17%\ZMBV.INF"
+
+[ZMBV.INIs]
+system.ini, drivers32,, "VIDC.ZMBV=zmbv.dll"
+
+[ZMBV.INIs.Del]
+system.ini, drivers32, "VIDC.ZMBV=zmbv.dll"
+
+[Strings]
+ZMBV_PUBLISHER = "DOSBox Team"
+ZMBV_DISPLAY_NAME = "Zip Motion Block Video [ZMBV]"
+ZMBV_UNINST_DISPLAY_NAME = "Zip Motion Block Video codec (Remove Only)"
+ZMBV_URL_HOMEPAGE = "http://www.dosbox.com/"
+CODEC_INSTALLATION_FINISHED = "Zip Motion Block Video codec installation is complete."
diff --git a/src/libs/zmbv/zmbv.sln b/src/libs/zmbv/zmbv.sln
new file mode 100644
index 000000000..aea19d7cd
--- /dev/null
+++ b/src/libs/zmbv/zmbv.sln
@@ -0,0 +1,21 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zmbv", "zmbv.vcproj", "{619CF3F9-C373-4BD5-93DA-025F5E16F8FA}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ Debug = Debug
+ Release = Release
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {619CF3F9-C373-4BD5-93DA-025F5E16F8FA}.Debug.ActiveCfg = Debug|Win32
+ {619CF3F9-C373-4BD5-93DA-025F5E16F8FA}.Debug.Build.0 = Debug|Win32
+ {619CF3F9-C373-4BD5-93DA-025F5E16F8FA}.Release.ActiveCfg = Release|Win32
+ {619CF3F9-C373-4BD5-93DA-025F5E16F8FA}.Release.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/src/libs/zmbv/zmbv.vcproj b/src/libs/zmbv/zmbv.vcproj
new file mode 100644
index 000000000..ce973b868
--- /dev/null
+++ b/src/libs/zmbv/zmbv.vcproj
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="zmbv"
+ ProjectGUID="{619CF3F9-C373-4BD5-93DA-025F5E16F8FA}"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug"
+ IntermediateDirectory="Debug"
+ ConfigurationType="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="5"
+ BufferSecurityCheck="TRUE"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="winmm.lib odbc32.lib odbccp32.lib zlib.lib"
+ OutputFile="$(OutDir)/zmbv.dll"
+ LinkIncremental="2"
+ ModuleDefinitionFile="zmbv.def"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/zmbv.pdb"
+ SubSystem="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Release"
+ IntermediateDirectory="Release"
+ ConfigurationType="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
+ RuntimeLibrary="4"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="winmm.lib odbc32.lib odbccp32.lib zlib.lib"
+ OutputFile="$(OutDir)/zmbv.dll"
+ LinkIncremental="1"
+ ModuleDefinitionFile="zmbv.def"
+ GenerateDebugInformation="TRUE"
+ MapExports="TRUE"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath=".\drvproc.cpp">
+ </File>
+ <File
+ RelativePath=".\Resource.h">
+ </File>
+ <File
+ RelativePath=".\zmbv.cpp">
+ </File>
+ <File
+ RelativePath=".\zmbv.def">
+ </File>
+ <File
+ RelativePath=".\zmbv.h">
+ </File>
+ <File
+ RelativePath=".\zmbv_vfw.cpp">
+ </File>
+ <File
+ RelativePath=".\zmbv_vfw.rc">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src/libs/zmbv/zmbv_mingw.def b/src/libs/zmbv/zmbv_mingw.def
new file mode 100644
index 000000000..041427446
--- /dev/null
+++ b/src/libs/zmbv/zmbv_mingw.def
@@ -0,0 +1,2 @@
+EXPORTS
+ DriverProc = DriverProc@20 @1
diff --git a/src/libs/zmbv/zmbv_vfw.cpp b/src/libs/zmbv/zmbv_vfw.cpp
new file mode 100644
index 000000000..3be8459ec
--- /dev/null
+++ b/src/libs/zmbv/zmbv_vfw.cpp
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+//
+// Zipped Motion Block Video
+//
+// Based on Huffyuv by Ben Rudiak-Gould.
+// which was based on MSYUV sample code, which is:
+// Copyright (c) 1993 Microsoft Corporation.
+// All Rights Reserved.
+//
+
+#include "zmbv_vfw.h"
+#include "resource.h"
+
+#include <crtdbg.h>
+#include <string.h>
+
+TCHAR szDescription[] = TEXT("Zipped Motion Block Video v0.1a");
+TCHAR szName[] = TEXT(CODEC_4CC);
+
+#define VERSION 0x00000002 // newer version
+
+/********************************************************************
+********************************************************************/
+
+CodecInst *encode_table_owner, *decode_table_owner;
+
+/********************************************************************
+********************************************************************/
+
+void Msg(const char fmt[], ...) {
+ DWORD written;
+ char buf[2000];
+ va_list val;
+
+ va_start(val, fmt);
+ wvsprintf(buf, fmt, val);
+ va_end(val);
+
+ const COORD _80x50 = {80,50};
+ static BOOL startup = (AllocConsole(), SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), _80x50));
+ WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), buf, lstrlen(buf), &written, 0);
+}
+
+
+/********************************************************************
+********************************************************************/
+
+CodecInst::CodecInst() {
+ codec = 0;
+}
+
+CodecInst* Open(ICOPEN* icinfo) {
+ if (icinfo && icinfo->fccType != ICTYPE_VIDEO)
+ return NULL;
+
+ CodecInst* pinst = new CodecInst();
+
+ if (icinfo) icinfo->dwError = pinst ? ICERR_OK : ICERR_MEMORY;
+
+ return pinst;
+}
+
+DWORD Close(CodecInst* pinst) {
+// delete pinst; // this caused problems when deleting at app close time
+ return 1;
+}
+
+/********************************************************************
+********************************************************************/
+
+
+/********************************************************************
+********************************************************************/
+
+BOOL CodecInst::QueryAbout() { return TRUE; }
+
+static INT_PTR CALLBACK AboutDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ if (uMsg == WM_COMMAND) {
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ EndDialog(hwndDlg, 0);
+ break;
+ case IDC_HOMEPAGE:
+ ShellExecute(NULL, NULL, "http://www.dosbox.com", NULL, NULL, SW_SHOW);
+ break;
+ case IDC_EMAIL:
+ ShellExecute(NULL, NULL, "mailto:dosbox.crew@gmail.com", NULL, NULL, SW_SHOW);
+ break;
+ }
+ }
+ return FALSE;
+}
+DWORD CodecInst::About(HWND hwnd) {
+ DialogBox(hmoduleCodec, MAKEINTRESOURCE(IDD_ABOUT), hwnd, AboutDialogProc);
+ return ICERR_OK;
+}
+
+static INT_PTR CALLBACK ConfigureDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+
+ if (uMsg == WM_INITDIALOG) {
+
+ } else if (uMsg == WM_COMMAND) {
+
+ switch (LOWORD(wParam)) {
+
+ case IDOK:
+
+ case IDCANCEL:
+ EndDialog(hwndDlg, 0);
+ break;
+
+ default:
+ return AboutDialogProc(hwndDlg, uMsg, wParam, lParam); // handle email and home-page buttons
+ }
+ }
+ return FALSE;
+}
+
+BOOL CodecInst::QueryConfigure() { return TRUE; }
+
+DWORD CodecInst::Configure(HWND hwnd) {
+ DialogBox(hmoduleCodec, MAKEINTRESOURCE(IDD_CONFIGURE), hwnd, ConfigureDialogProc);
+ return ICERR_OK;
+}
+
+
+/********************************************************************
+********************************************************************/
+
+
+// we have no state information which needs to be stored
+
+DWORD CodecInst::GetState(LPVOID pv, DWORD dwSize) { return 0; }
+
+DWORD CodecInst::SetState(LPVOID pv, DWORD dwSize) { return 0; }
+
+
+DWORD CodecInst::GetInfo(ICINFO* icinfo, DWORD dwSize) {
+ if (icinfo == NULL)
+ return sizeof(ICINFO);
+
+ if (dwSize < sizeof(ICINFO))
+ return 0;
+
+ icinfo->dwSize = sizeof(ICINFO);
+ icinfo->fccType = ICTYPE_VIDEO;
+ memcpy(&icinfo->fccHandler,CODEC_4CC, 4);
+ icinfo->dwFlags = VIDCF_FASTTEMPORALC | VIDCF_FASTTEMPORALD | VIDCF_TEMPORAL;
+
+ icinfo->dwVersion = VERSION;
+ icinfo->dwVersionICM = ICVERSION;
+ MultiByteToWideChar(CP_ACP, 0, szDescription, -1, icinfo->szDescription, sizeof(icinfo->szDescription)/sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, szName, -1, icinfo->szName, sizeof(icinfo->szName)/sizeof(WCHAR));
+
+ return sizeof(ICINFO);
+}
+
+/********************************************************************
+****************************************************************/
+
+static int GetInputBitDepth(const BITMAPINFOHEADER *lpbiIn) {
+ //Msg( "Get input depth compression %d bitcount %d\n", lpbiIn->biCompression, lpbiIn->biBitCount );
+ if (lpbiIn->biCompression == BI_RGB) {
+ if (lpbiIn->biPlanes != 1)
+ return -1;
+
+ switch(lpbiIn->biBitCount) {
+ case 8:
+ return 8;
+ case 16:
+ return 15; // Standard Windows 16-bit RGB is 1555.
+ case 32:
+ return 32;
+ }
+
+ } else if (lpbiIn->biCompression == BI_BITFIELDS) {
+ // BI_BITFIELDS RGB masks lie right after the BITMAPINFOHEADER structure,
+ // at (ptr+40). This is true even for a BITMAPV4HEADER or BITMAPV5HEADER.
+ const DWORD *masks = (const DWORD *)(lpbiIn + 1);
+
+ if (lpbiIn->biBitCount == 16) {
+ // Test for 16 (555)
+ if (masks[0] == 0x7C00 && masks[1] == 0x03E0 && masks[2] == 0x001F)
+ return 15;
+
+ // Test for 16 (565)
+ if (masks[0] == 0xF800 && masks[1] == 0x07E0 && masks[2] == 0x001F)
+ return 16;
+ } else if (lpbiIn->biBitCount == 32) {
+ if (masks[0] == 0xFF0000 && masks[1] == 0x00FF00 && masks[2] == 0x0000FF)
+ return 32;
+ }
+ }
+
+ return -1;
+}
+
+static bool CanCompress(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut, bool requireOutput) {
+ if (lpbiIn) {
+ if (GetInputBitDepth(lpbiIn) < 0)
+ return false;
+ } else
+ return false;
+ if (lpbiOut) {
+ //Needs to match our 4cc format
+ if (memcmp(&lpbiOut->biCompression,CODEC_4CC, 4))
+ return false;
+ } else
+ return !requireOutput;
+ return true;
+}
+
+/********************************************************************
+****************************************************************/
+
+DWORD CodecInst::CompressQuery(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) {
+ if (CanCompress(lpbiIn,lpbiOut,false))
+ return ICERR_OK;
+ return ICERR_BADFORMAT;
+}
+
+DWORD CodecInst::CompressGetFormat(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) {
+ if (!lpbiOut)
+ return sizeof(BITMAPINFOHEADER);
+ lpbiOut->biSize = sizeof(BITMAPINFOHEADER);
+ lpbiOut->biWidth = lpbiIn->biWidth;
+ lpbiOut->biHeight = lpbiIn->biHeight;
+ lpbiOut->biPlanes = 1;
+ lpbiOut->biCompression = *(const DWORD *)CODEC_4CC;
+ lpbiOut->biBitCount = lpbiIn->biBitCount;
+ lpbiOut->biSizeImage = lpbiIn->biWidth * lpbiIn->biHeight * lpbiIn->biBitCount/8 + 1024;
+ lpbiOut->biXPelsPerMeter = lpbiIn->biXPelsPerMeter;
+ lpbiOut->biYPelsPerMeter = lpbiIn->biYPelsPerMeter;
+ lpbiOut->biClrUsed = 0;
+ lpbiOut->biClrImportant = 0;
+ return ICERR_OK;
+}
+
+DWORD CodecInst::CompressBegin(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) {
+ CompressEnd(); // free resources if necessary
+ if (!CanCompress(lpbiIn, lpbiOut, true))
+ return ICERR_BADFORMAT;
+ codec = new VideoCodec();
+ if (!codec)
+ return ICERR_MEMORY;
+ if (!codec->SetupCompress( lpbiIn->biWidth, lpbiIn->biHeight))
+ return ICERR_MEMORY;
+ return ICERR_OK;
+}
+
+DWORD CodecInst::CompressGetSize(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) {
+ if (!CanCompress(lpbiIn, lpbiOut, true))
+ return ICERR_BADFORMAT;
+ return lpbiIn->biWidth * lpbiIn->biHeight * lpbiIn->biBitCount/8 + 1024;
+}
+
+DWORD CodecInst::Compress(ICCOMPRESS* icinfo, DWORD dwSize) {
+ int i, pitch;
+ zmbv_format_t format;
+ LPBITMAPINFOHEADER lpbiIn=icinfo->lpbiInput;
+ LPBITMAPINFOHEADER lpbiOut=icinfo->lpbiOutput;
+ if (!CanCompress(lpbiIn, lpbiOut, true))
+ return ICERR_BADFORMAT;
+ if (!icinfo->lpInput || !icinfo->lpOutput)
+ return ICERR_ABORT;
+ switch (GetInputBitDepth(lpbiIn)) {
+ case 8:
+ format = ZMBV_FORMAT_8BPP;
+ pitch = lpbiIn->biWidth;
+ break;
+ case 15:
+ format = ZMBV_FORMAT_15BPP;
+ pitch = lpbiIn->biWidth * 2;
+ break;
+ case 16:
+ format = ZMBV_FORMAT_16BPP;
+ pitch = lpbiIn->biWidth * 2;
+ break;
+ case 32:
+ format = ZMBV_FORMAT_32BPP;
+ pitch = lpbiIn->biWidth * 4;
+ break;
+ }
+
+ // DIB scanlines for RGB formats are always aligned to DWORD.
+ pitch = (pitch + 3) & ~3;
+
+ // force a key frame if requested by the client
+ int flags = 0;
+ if (icinfo->dwFlags & ICCOMPRESS_KEYFRAME)
+ flags |= 1;
+
+ char palette[256 * 4];
+ char *pal = NULL;
+
+ if (lpbiIn->biBitCount == 8) {
+ const int entries = icinfo->lpbiInput->biClrUsed ? icinfo->lpbiInput->biClrUsed : 256;
+ const char* srcpal = (char *)icinfo->lpbiInput + icinfo->lpbiInput->biSize;
+ char* dstpal = palette;
+
+ memset(palette, 0, sizeof palette);
+
+ for(int i=0; i<entries; ++i) {
+ dstpal[0] = srcpal[2];
+ dstpal[1] = srcpal[1];
+ dstpal[2] = srcpal[0];
+ dstpal[3] = 0;
+ dstpal += 4;
+ srcpal += 4;
+ }
+
+ pal = palette;
+ }
+
+ codec->PrepareCompressFrame( flags, format, pal, icinfo->lpOutput, 99999999);
+ char *readPt = (char *)icinfo->lpInput + pitch*(lpbiIn->biHeight - 1);
+ for(i = 0;i<lpbiIn->biHeight;i++) {
+ codec->CompressLines(1, (void **)&readPt );
+ readPt -= pitch;
+ }
+ lpbiOut->biSizeImage = codec->FinishCompressFrame();
+
+ if (flags & 1)
+ *icinfo->lpdwFlags = AVIIF_KEYFRAME;
+ else
+ *icinfo->lpdwFlags = 0;
+
+ return ICERR_OK;
+}
+
+
+DWORD CodecInst::CompressEnd() {
+ if (codec)
+ delete codec;
+ codec = 0;
+ return ICERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool CanDecompress(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) {
+ if (memcmp(&lpbiIn->biCompression,CODEC_4CC,4))
+ return false;
+ if (lpbiOut) {
+ if (lpbiOut->biCompression!=0) return false;
+ if (lpbiOut->biBitCount != 24) return false;
+ if (lpbiIn->biWidth!=lpbiOut->biWidth || lpbiIn->biHeight!=lpbiOut->biHeight)
+ return false;
+ }
+ return true;
+}
+
+/********************************************************************
+********************************************************************/
+
+
+DWORD CodecInst::DecompressQuery(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) {
+ return CanDecompress(lpbiIn, lpbiOut) ? ICERR_OK : ICERR_BADFORMAT;
+}
+
+DWORD CodecInst::DecompressGetFormat(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) {
+ if (memcmp(&lpbiIn->biCompression,CODEC_4CC,4))
+ return ICERR_BADFORMAT;
+ if (!lpbiOut)
+ return sizeof(BITMAPINFOHEADER);
+ *lpbiOut = *lpbiIn;
+ lpbiOut->biPlanes = 1;
+ lpbiOut->biSize = sizeof(BITMAPINFOHEADER);
+ lpbiOut->biBitCount = 24;
+ lpbiOut->biSizeImage = ((lpbiOut->biWidth*3 + 3) & ~3) * lpbiOut->biHeight;
+ lpbiOut->biCompression = BI_RGB;
+ lpbiOut->biClrUsed = 0;
+ lpbiOut->biClrImportant = 0;
+ return ICERR_OK;
+}
+
+DWORD CodecInst::DecompressBegin(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) {
+ DecompressEnd(); // free resources if necessary
+ if (!CanDecompress(lpbiIn, lpbiOut))
+ return ICERR_BADFORMAT;
+ codec=new VideoCodec();
+ if (!codec)
+ return ICERR_MEMORY;
+ if (!codec->SetupDecompress( lpbiIn->biWidth, lpbiIn->biHeight))
+ return ICERR_MEMORY;
+ return ICERR_OK;
+}
+
+DWORD CodecInst::Decompress(ICDECOMPRESS* icinfo, DWORD dwSize) {
+ if (!codec || !icinfo)
+ return ICERR_ABORT;
+ if (codec->DecompressFrame( icinfo->lpInput, icinfo->lpbiInput->biSizeImage)) {
+ codec->Output_UpsideDown_24(icinfo->lpOutput);
+ } else return ICERR_DONTDRAW;
+ return ICERR_OK;
+}
+
+
+// palette-mapped output only
+DWORD CodecInst::DecompressGetPalette(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) {
+ return ICERR_BADFORMAT;
+}
+
+DWORD CodecInst::DecompressEnd() {
+ if (codec)
+ delete codec;
+ codec = 0;
+ return ICERR_OK;
+}
diff --git a/src/libs/zmbv/zmbv_vfw.h b/src/libs/zmbv/zmbv_vfw.h
new file mode 100644
index 000000000..9f50656b3
--- /dev/null
+++ b/src/libs/zmbv/zmbv_vfw.h
@@ -0,0 +1,61 @@
+//
+// Huffyuv v2.1.1, by Ben Rudiak-Gould.
+// http://www.math.berkeley.edu/~benrg/huffyuv.html
+//
+// This file is copyright 2000 Ben Rudiak-Gould, and distributed under
+// the terms of the GNU General Public License, v2 or later. See
+// http://www.gnu.org/copyleft/gpl.html.
+//
+// I edit these files in 10-point Verdana, a proportionally-spaced font.
+// You may notice formatting oddities if you use a monospaced font.
+//
+
+
+#include <windows.h>
+#include <vfw.h>
+#include <zlib.h>
+#include "zmbv.h"
+#pragma hdrstop
+
+extern HMODULE hmoduleCodec;
+
+
+// huffyuv.cpp
+
+class CodecInst {
+private:
+ VideoCodec * codec;
+public:
+ CodecInst();
+ ~CodecInst();
+
+ BOOL QueryAbout();
+ DWORD About(HWND hwnd);
+
+ BOOL QueryConfigure();
+ DWORD Configure(HWND hwnd);
+
+ DWORD GetState(LPVOID pv, DWORD dwSize);
+ DWORD SetState(LPVOID pv, DWORD dwSize);
+
+ DWORD GetInfo(ICINFO* icinfo, DWORD dwSize);
+
+ DWORD CompressQuery(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut);
+ DWORD CompressGetFormat(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut);
+ DWORD CompressBegin(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut);
+ DWORD CompressGetSize(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut);
+ DWORD Compress(ICCOMPRESS* icinfo, DWORD dwSize);
+ DWORD CompressEnd();
+
+ DWORD DecompressQuery(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut);
+ DWORD DecompressGetFormat(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut);
+ DWORD DecompressBegin(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut);
+
+ DWORD Decompress(ICDECOMPRESS* icinfo, DWORD dwSize);
+ DWORD DecompressGetPalette(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut);
+ DWORD DecompressEnd();
+};
+
+CodecInst* Open(ICOPEN* icinfo);
+DWORD Close(CodecInst* pinst);
+
diff --git a/src/libs/zmbv/zmbv_vfw.rc b/src/libs/zmbv/zmbv_vfw.rc
new file mode 100644
index 000000000..643418af5
--- /dev/null
+++ b/src/libs/zmbv/zmbv_vfw.rc
@@ -0,0 +1,123 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUT DIALOGEX 0, 0, 167, 55
+STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "DOSBox Video Codec v0.1"
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,131,34,29,14
+ CTEXT "Zipped Motion Block Video v 0.1\nCopyright 2009-2019 DOSBox Team",
+ IDC_STATIC,7,7,153,25,SS_NOPREFIX
+ PUSHBUTTON "Email author",IDC_EMAIL,7,34,50,14
+ PUSHBUTTON "Visit home page",IDC_HOMEPAGE,59,34,58,14
+END
+
+IDD_CONFIGURE DIALOGEX 0, 0, 213, 146
+STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "ZMBV configuration dialog"
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ PUSHBUTTON "Email author",IDC_EMAIL,44,86,50,14
+ PUSHBUTTON "Visit home page",IDC_HOMEPAGE,109,87,58,14
+ DEFPUSHBUTTON "OK",IDOK,44,125,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,117,125,50,14
+ CONTROL "",IDC_SLIDER1,"msctls_trackbar32",TBS_BOTH |
+ TBS_NOTICKS | WS_TABSTOP,57,30,92,18
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_ABOUT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 160
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 48
+ END
+
+ IDD_CONFIGURE, DIALOG
+ BEGIN
+ LEFTMARGIN, 44
+ RIGHTMARGIN, 167
+ TOPMARGIN, 6
+ BOTTOMMARGIN, 139
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/src/misc/Makefile.am b/src/misc/Makefile.am
new file mode 100644
index 000000000..968151af9
--- /dev/null
+++ b/src/misc/Makefile.am
@@ -0,0 +1,4 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libmisc.a
+libmisc_a_SOURCES = cross.cpp messages.cpp programs.cpp setup.cpp support.cpp
diff --git a/src/misc/cross.cpp b/src/misc/cross.cpp
new file mode 100644
index 000000000..49c20aa8a
--- /dev/null
+++ b/src/misc/cross.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "cross.h"
+#include "support.h"
+#include <string>
+#include <limits.h>
+#include <stdlib.h>
+
+#ifdef WIN32
+#ifndef _WIN32_IE
+#define _WIN32_IE 0x0400
+#endif
+#include <shlobj.h>
+#endif
+
+#if defined HAVE_SYS_TYPES_H && defined HAVE_PWD_H
+#include <sys/types.h>
+#include <pwd.h>
+#endif
+
+#ifdef WIN32
+static void W32_ConfDir(std::string& in,bool create) {
+ int c = create?1:0;
+ char result[MAX_PATH] = { 0 };
+ BOOL r = SHGetSpecialFolderPath(NULL,result,CSIDL_LOCAL_APPDATA,c);
+ if(!r || result[0] == 0) r = SHGetSpecialFolderPath(NULL,result,CSIDL_APPDATA,c);
+ if(!r || result[0] == 0) {
+ char const * windir = getenv("windir");
+ if(!windir) windir = "c:\\windows";
+ safe_strncpy(result,windir,MAX_PATH);
+ char const* appdata = "\\Application Data";
+ size_t len = strlen(result);
+ if(len + strlen(appdata) < MAX_PATH) strcat(result,appdata);
+ if(create) mkdir(result);
+ }
+ in = result;
+}
+#endif
+
+void Cross::GetPlatformConfigDir(std::string& in) {
+#ifdef WIN32
+ W32_ConfDir(in,false);
+ in += "\\DOSBox";
+#elif defined(MACOSX)
+ in = "~/Library/Preferences";
+ ResolveHomedir(in);
+#else
+ in = "~/.dosbox";
+ ResolveHomedir(in);
+#endif
+ in += CROSS_FILESPLIT;
+}
+
+void Cross::GetPlatformConfigName(std::string& in) {
+#ifdef WIN32
+#define DEFAULT_CONFIG_FILE "dosbox-" VERSION ".conf"
+#elif defined(MACOSX)
+#define DEFAULT_CONFIG_FILE "DOSBox " VERSION " Preferences"
+#else /*linux freebsd*/
+#define DEFAULT_CONFIG_FILE "dosbox-" VERSION ".conf"
+#endif
+ in = DEFAULT_CONFIG_FILE;
+}
+
+void Cross::CreatePlatformConfigDir(std::string& in) {
+#ifdef WIN32
+ W32_ConfDir(in,true);
+ in += "\\DOSBox";
+ mkdir(in.c_str());
+#elif defined(MACOSX)
+ in = "~/Library/Preferences";
+ ResolveHomedir(in);
+ //Don't create it. Assume it exists
+#else
+ in = "~/.dosbox";
+ ResolveHomedir(in);
+ mkdir(in.c_str(),0700);
+#endif
+ in += CROSS_FILESPLIT;
+}
+
+void Cross::ResolveHomedir(std::string & temp_line) {
+ if(!temp_line.size() || temp_line[0] != '~') return; //No ~
+
+ if(temp_line.size() == 1 || temp_line[1] == CROSS_FILESPLIT) { //The ~ and ~/ variant
+ char * home = getenv("HOME");
+ if(home) temp_line.replace(0,1,std::string(home));
+#if defined HAVE_SYS_TYPES_H && defined HAVE_PWD_H
+ } else { // The ~username variant
+ std::string::size_type namelen = temp_line.find(CROSS_FILESPLIT);
+ if(namelen == std::string::npos) namelen = temp_line.size();
+ std::string username = temp_line.substr(1,namelen - 1);
+ struct passwd* pass = getpwnam(username.c_str());
+ if(pass) temp_line.replace(0,namelen,pass->pw_dir); //namelen -1 +1(for the ~)
+#endif // USERNAME lookup code
+ }
+}
+
+void Cross::CreateDir(std::string const& in) {
+#ifdef WIN32
+ mkdir(in.c_str());
+#else
+ mkdir(in.c_str(),0700);
+#endif
+}
+
+bool Cross::IsPathAbsolute(std::string const& in) {
+ // Absolute paths
+#if defined (WIN32) || defined(OS2)
+ // drive letter
+ if (in.size() > 2 && in[1] == ':' ) return true;
+ // UNC path
+ else if (in.size() > 2 && in[0]=='\\' && in[1]=='\\') return true;
+#else
+ if (in.size() > 1 && in[0] == '/' ) return true;
+#endif
+ return false;
+}
+
+#if defined (WIN32)
+
+dir_information* open_directory(const char* dirname) {
+ if (dirname == NULL) return NULL;
+
+ size_t len = strlen(dirname);
+ if (len == 0) return NULL;
+
+ static dir_information dir;
+
+ safe_strncpy(dir.base_path,dirname,MAX_PATH);
+
+ if (dirname[len-1] == '\\') strcat(dir.base_path,"*.*");
+ else strcat(dir.base_path,"\\*.*");
+
+ dir.handle = INVALID_HANDLE_VALUE;
+
+ return (access(dirname,0) ? NULL : &dir);
+}
+
+bool read_directory_first(dir_information* dirp, char* entry_name, bool& is_directory) {
+ if (!dirp) return false;
+ dirp->handle = FindFirstFile(dirp->base_path, &dirp->search_data);
+ if (INVALID_HANDLE_VALUE == dirp->handle) {
+ return false;
+ }
+
+ safe_strncpy(entry_name,dirp->search_data.cFileName,(MAX_PATH<CROSS_LEN)?MAX_PATH:CROSS_LEN);
+
+ if (dirp->search_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) is_directory = true;
+ else is_directory = false;
+
+ return true;
+}
+
+bool read_directory_next(dir_information* dirp, char* entry_name, bool& is_directory) {
+ if (!dirp) return false;
+ int result = FindNextFile(dirp->handle, &dirp->search_data);
+ if (result==0) return false;
+
+ safe_strncpy(entry_name,dirp->search_data.cFileName,(MAX_PATH<CROSS_LEN)?MAX_PATH:CROSS_LEN);
+
+ if (dirp->search_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) is_directory = true;
+ else is_directory = false;
+
+ return true;
+}
+
+void close_directory(dir_information* dirp) {
+ if (dirp && dirp->handle != INVALID_HANDLE_VALUE) {
+ FindClose(dirp->handle);
+ dirp->handle = INVALID_HANDLE_VALUE;
+ }
+}
+
+#else
+
+dir_information* open_directory(const char* dirname) {
+ static dir_information dir;
+ dir.dir=opendir(dirname);
+ safe_strncpy(dir.base_path,dirname,CROSS_LEN);
+ return dir.dir?&dir:NULL;
+}
+
+bool read_directory_first(dir_information* dirp, char* entry_name, bool& is_directory) {
+ if (!dirp) return false;
+ return read_directory_next(dirp,entry_name,is_directory);
+}
+
+bool read_directory_next(dir_information* dirp, char* entry_name, bool& is_directory) {
+ if (!dirp) return false;
+ struct dirent* dentry = readdir(dirp->dir);
+ if (dentry==NULL) {
+ return false;
+ }
+
+// safe_strncpy(entry_name,dentry->d_name,(FILENAME_MAX<MAX_PATH)?FILENAME_MAX:MAX_PATH); // [include stdio.h], maybe pathconf()
+ safe_strncpy(entry_name,dentry->d_name,CROSS_LEN);
+
+#ifdef DIRENT_HAS_D_TYPE
+ if(dentry->d_type == DT_DIR) {
+ is_directory = true;
+ return true;
+ } else if(dentry->d_type == DT_REG) {
+ is_directory = false;
+ return true;
+ }
+#endif
+
+ //Maybe only for DT_UNKNOWN if DIRENT_HAD_D_TYPE..
+ static char buffer[2 * CROSS_LEN + 1] = { 0 };
+ static char split[2] = { CROSS_FILESPLIT , 0 };
+ buffer[0] = 0;
+ strcpy(buffer,dirp->base_path);
+ size_t buflen = strlen(buffer);
+ if (buflen && buffer[buflen - 1] != CROSS_FILESPLIT ) strcat(buffer, split);
+ strcat(buffer,entry_name);
+ struct stat status;
+
+ if (stat(buffer,&status) == 0) is_directory = (S_ISDIR(status.st_mode)>0);
+ else is_directory = false;
+
+ return true;
+}
+
+void close_directory(dir_information* dirp) {
+ if (dirp) closedir(dirp->dir);
+}
+
+#endif
+
+FILE *fopen_wrap(const char *path, const char *mode) {
+#if defined(WIN32) || defined(OS2)
+ ;
+#elif defined (MACOSX)
+ ;
+#else
+#if defined (HAVE_REALPATH)
+ char work[CROSS_LEN] = {0};
+ strncpy(work,path,CROSS_LEN-1);
+ char* last = strrchr(work,'/');
+
+ if (last) {
+ if (last != work) {
+ *last = 0;
+ //If this compare fails, then we are dealing with files in /
+ //Which is outside the scope, but test anyway.
+ //However as realpath only works for exising files. The testing is
+ //in that case not done against new files.
+ }
+ char* check = realpath(work,NULL);
+ if (check) {
+ if ( ( strlen(check) == 5 && strcmp(check,"/proc") == 0) || strncmp(check,"/proc/",6) == 0) {
+// LOG_MSG("lst hit %s blocking!",path);
+ free(check);
+ return NULL;
+ }
+ free(check);
+ }
+ }
+
+#if 0
+//Lightweight version, but then existing files can still be read, which is not ideal
+ if (strpbrk(mode,"aw+") != NULL) {
+ LOG_MSG("pbrk ok");
+ char* check = realpath(path,NULL);
+ //Will be null if file doesn't exist.... ENOENT
+ //TODO What about unlink /proc/self/mem and then create it ?
+ //Should be safe for what we want..
+ if (check) {
+ if (strncmp(check,"/proc/",6) == 0) {
+ free(check);
+ return NULL;
+ }
+ free(check);
+ }
+ }
+*/
+#endif //0
+
+#endif //HAVE_REALPATH
+#endif
+
+ return fopen(path,mode);
+}
+
+
diff --git a/src/misc/messages.cpp b/src/misc/messages.cpp
new file mode 100644
index 000000000..b0e659415
--- /dev/null
+++ b/src/misc/messages.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "dosbox.h"
+#include "cross.h"
+#include "support.h"
+#include "setup.h"
+#include "control.h"
+#include <list>
+#include <string>
+using namespace std;
+
+
+
+#define LINE_IN_MAXLEN 2048
+
+struct MessageBlock {
+ string name;
+ string val;
+ MessageBlock(const char* _name, const char* _val):
+ name(_name),val(_val){}
+};
+
+static list<MessageBlock> Lang;
+typedef list<MessageBlock>::iterator itmb;
+
+void MSG_Add(const char * _name, const char* _val) {
+ /* Find the message */
+ for(itmb tel=Lang.begin();tel!=Lang.end();tel++) {
+ if((*tel).name==_name) {
+// LOG_MSG("double entry for %s",_name); //Message file might be loaded before default text messages
+ return;
+ }
+ }
+ /* if the message doesn't exist add it */
+ Lang.push_back(MessageBlock(_name,_val));
+}
+
+void MSG_Replace(const char * _name, const char* _val) {
+ /* Find the message */
+ for(itmb tel=Lang.begin();tel!=Lang.end();tel++) {
+ if((*tel).name==_name) {
+ Lang.erase(tel);
+ break;
+ }
+ }
+ /* Even if the message doesn't exist add it */
+ Lang.push_back(MessageBlock(_name,_val));
+}
+
+static void LoadMessageFile(const char * fname) {
+ if (!fname) return;
+ if(*fname=='\0') return;//empty string=no languagefile
+ FILE * mfile=fopen(fname,"rt");
+ /* This should never happen and since other modules depend on this use a normal printf */
+ if (!mfile) {
+ E_Exit("MSG:Can't load messages: %s",fname);
+ }
+ char linein[LINE_IN_MAXLEN];
+ char name[LINE_IN_MAXLEN];
+ char string[LINE_IN_MAXLEN*10];
+ /* Start out with empty strings */
+ name[0]=0;string[0]=0;
+ while(fgets(linein, LINE_IN_MAXLEN, mfile)!=0) {
+ /* Parse the read line */
+ /* First remove characters 10 and 13 from the line */
+ char * parser=linein;
+ char * writer=linein;
+ while (*parser) {
+ if (*parser!=10 && *parser!=13) {
+ *writer++=*parser;
+ }
+ parser++;
+ }
+ *writer=0;
+ /* New string name */
+ if (linein[0]==':') {
+ string[0]=0;
+ strcpy(name,linein+1);
+ /* End of string marker */
+ } else if (linein[0]=='.') {
+ /* Replace/Add the string to the internal languagefile */
+ /* Remove last newline (marker is \n.\n) */
+ size_t ll = strlen(string);
+ if(ll && string[ll - 1] == '\n') string[ll - 1] = 0; //Second if should not be needed, but better be safe.
+ MSG_Replace(name,string);
+ } else {
+ /* Normal string to be added */
+ strcat(string,linein);
+ strcat(string,"\n");
+ }
+ }
+ fclose(mfile);
+}
+
+const char * MSG_Get(char const * msg) {
+ for(itmb tel=Lang.begin();tel!=Lang.end();tel++){
+ if((*tel).name==msg)
+ {
+ return (*tel).val.c_str();
+ }
+ }
+ return "Message not Found!\n";
+}
+
+
+bool MSG_Write(const char * location) {
+ FILE* out=fopen(location,"w+t");
+ if(out==NULL) return false;//maybe an error?
+ for(itmb tel=Lang.begin();tel!=Lang.end();tel++){
+ fprintf(out,":%s\n%s\n.\n",(*tel).name.c_str(),(*tel).val.c_str());
+ }
+ fclose(out);
+ return true;
+}
+
+void MSG_Init(Section_prop * section) {
+ std::string file_name;
+ if (control->cmdline->FindString("-lang",file_name,true)) {
+ LoadMessageFile(file_name.c_str());
+ } else {
+ Prop_path* pathprop = section->Get_path("language");
+ if(pathprop) LoadMessageFile(pathprop->realpath.c_str());
+ }
+}
diff --git a/src/misc/programs.cpp b/src/misc/programs.cpp
new file mode 100644
index 000000000..ea33b0c66
--- /dev/null
+++ b/src/misc/programs.cpp
@@ -0,0 +1,825 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <vector>
+#include <sstream>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include "programs.h"
+#include "callback.h"
+#include "regs.h"
+#include "support.h"
+#include "cross.h"
+#include "control.h"
+#include "shell.h"
+
+Bitu call_program;
+
+/* This registers a file on the virtual drive and creates the correct structure for it*/
+
+static Bit8u exe_block[]={
+ 0xbc,0x00,0x04, //MOV SP,0x400 decrease stack size
+ 0xbb,0x40,0x00, //MOV BX,0x040 for memory resize
+ 0xb4,0x4a, //MOV AH,0x4A Resize memory block
+ 0xcd,0x21, //INT 0x21
+//pos 12 is callback number
+ 0xFE,0x38,0x00,0x00, //CALLBack number
+ 0xb8,0x00,0x4c, //Mov ax,4c00
+ 0xcd,0x21, //INT 0x21
+};
+
+#define CB_POS 12
+
+static std::vector<PROGRAMS_Main*> internal_progs;
+
+void PROGRAMS_MakeFile(char const * const name,PROGRAMS_Main * main) {
+ Bit8u * comdata=(Bit8u *)malloc(32); //MEM LEAK
+ memcpy(comdata,&exe_block,sizeof(exe_block));
+ comdata[CB_POS]=(Bit8u)(call_program&0xff);
+ comdata[CB_POS+1]=(Bit8u)((call_program>>8)&0xff);
+
+ /* Copy save the pointer in the vector and save it's index */
+ if (internal_progs.size()>255) E_Exit("PROGRAMS_MakeFile program size too large (%d)",static_cast<int>(internal_progs.size()));
+ Bit8u index = (Bit8u)internal_progs.size();
+ internal_progs.push_back(main);
+
+ memcpy(&comdata[sizeof(exe_block)],&index,sizeof(index));
+ Bit32u size=sizeof(exe_block)+sizeof(index);
+ VFILE_Register(name,comdata,size);
+}
+
+
+
+static Bitu PROGRAMS_Handler(void) {
+ /* This sets up everything for a program start up call */
+ Bitu size=sizeof(Bit8u);
+ Bit8u index;
+ /* Read the index from program code in memory */
+ PhysPt reader=PhysMake(dos.psp(),256+sizeof(exe_block));
+ HostPt writer=(HostPt)&index;
+ for (;size>0;size--) *writer++=mem_readb(reader++);
+ Program * new_program;
+ if (index >= internal_progs.size()) E_Exit("something is messing with the memory");
+ PROGRAMS_Main * handler = internal_progs[index];
+ (*handler)(&new_program);
+ new_program->Run();
+ delete new_program;
+ return CBRET_NONE;
+}
+
+
+/* Main functions used in all program */
+
+
+Program::Program() {
+ /* Find the command line and setup the PSP */
+ psp = new DOS_PSP(dos.psp());
+ /* Scan environment for filename */
+ PhysPt envscan=PhysMake(psp->GetEnvironment(),0);
+ while (mem_readb(envscan)) envscan+=mem_strlen(envscan)+1;
+ envscan+=3;
+ CommandTail tail;
+ MEM_BlockRead(PhysMake(dos.psp(),128),&tail,128);
+ if (tail.count<127) tail.buffer[tail.count]=0;
+ else tail.buffer[126]=0;
+ char filename[256+1];
+ MEM_StrCopy(envscan,filename,256);
+ cmd = new CommandLine(filename,tail.buffer);
+}
+
+extern std::string full_arguments;
+
+void Program::ChangeToLongCmd() {
+ /*
+ * Get arguments directly from the shell instead of the psp.
+ * this is done in securemode: (as then the arguments to mount and friends
+ * can only be given on the shell ( so no int 21 4b)
+ * Securemode part is disabled as each of the internal command has already
+ * protection for it. (and it breaks games like cdman)
+ * it is also done for long arguments to as it is convient (as the total commandline can be longer then 127 characters.
+ * imgmount with lot's of parameters
+ * Length of arguments can be ~120. but switch when above 100 to be sure
+ */
+
+ if (/*control->SecureMode() ||*/ cmd->Get_arglength() > 100) {
+ CommandLine* temp = new CommandLine(cmd->GetFileName(),full_arguments.c_str());
+ delete cmd;
+ cmd = temp;
+ }
+ full_arguments.assign(""); //Clear so it gets even more save
+}
+
+static char last_written_character = 0;//For 0xA to OxD 0xA expansion
+void Program::WriteOut(const char * format,...) {
+ char buf[2048];
+ va_list msg;
+
+ va_start(msg,format);
+ vsnprintf(buf,2047,format,msg);
+ va_end(msg);
+
+ Bit16u size = (Bit16u)strlen(buf);
+ dos.internal_output=true;
+ for(Bit16u i = 0; i < size;i++) {
+ Bit8u out;Bit16u s=1;
+ if (buf[i] == 0xA && last_written_character != 0xD) {
+ out = 0xD;DOS_WriteFile(STDOUT,&out,&s);
+ }
+ last_written_character = out = buf[i];
+ DOS_WriteFile(STDOUT,&out,&s);
+ }
+ dos.internal_output=false;
+
+// DOS_WriteFile(STDOUT,(Bit8u *)buf,&size);
+}
+
+void Program::WriteOut_NoParsing(const char * format) {
+ Bit16u size = (Bit16u)strlen(format);
+ char const* buf = format;
+ dos.internal_output=true;
+ for(Bit16u i = 0; i < size;i++) {
+ Bit8u out;Bit16u s=1;
+ if (buf[i] == 0xA && last_written_character != 0xD) {
+ out = 0xD;DOS_WriteFile(STDOUT,&out,&s);
+ }
+ last_written_character = out = buf[i];
+ DOS_WriteFile(STDOUT,&out,&s);
+ }
+ dos.internal_output=false;
+
+// DOS_WriteFile(STDOUT,(Bit8u *)format,&size);
+}
+
+
+bool Program::GetEnvStr(const char * entry,std::string & result) {
+ /* Walk through the internal environment and see for a match */
+ PhysPt env_read=PhysMake(psp->GetEnvironment(),0);
+
+ char env_string[1024+1];
+ result.erase();
+ if (!entry[0]) return false;
+ do {
+ MEM_StrCopy(env_read,env_string,1024);
+ if (!env_string[0]) return false;
+ env_read += (PhysPt)(strlen(env_string)+1);
+ char* equal = strchr(env_string,'=');
+ if (!equal) continue;
+ /* replace the = with \0 to get the length */
+ *equal = 0;
+ if (strlen(env_string) != strlen(entry)) continue;
+ if (strcasecmp(entry,env_string)!=0) continue;
+ /* restore the = to get the original result */
+ *equal = '=';
+ result = env_string;
+ return true;
+ } while (1);
+ return false;
+}
+
+bool Program::GetEnvNum(Bitu num,std::string & result) {
+ char env_string[1024+1];
+ PhysPt env_read=PhysMake(psp->GetEnvironment(),0);
+ do {
+ MEM_StrCopy(env_read,env_string,1024);
+ if (!env_string[0]) break;
+ if (!num) { result=env_string;return true;}
+ env_read += (PhysPt)(strlen(env_string)+1);
+ num--;
+ } while (1);
+ return false;
+}
+
+Bitu Program::GetEnvCount(void) {
+ PhysPt env_read=PhysMake(psp->GetEnvironment(),0);
+ Bitu num=0;
+ while (mem_readb(env_read)!=0) {
+ for (;mem_readb(env_read);env_read++) {};
+ env_read++;
+ num++;
+ }
+ return num;
+}
+
+bool Program::SetEnv(const char * entry,const char * new_string) {
+ PhysPt env_read = PhysMake(psp->GetEnvironment(),0);
+
+ //Get size of environment.
+ DOS_MCB mcb(psp->GetEnvironment()-1);
+ Bit16u envsize = mcb.GetSize()*16;
+
+
+ PhysPt env_write = env_read;
+ PhysPt env_write_start = env_read;
+ char env_string[1024+1] = { 0 };
+ do {
+ MEM_StrCopy(env_read,env_string,1024);
+ if (!env_string[0]) break;
+ env_read += (PhysPt)(strlen(env_string)+1);
+ if (!strchr(env_string,'=')) continue; /* Remove corrupt entry? */
+ if ((strncasecmp(entry,env_string,strlen(entry))==0) &&
+ env_string[strlen(entry)]=='=') continue;
+ MEM_BlockWrite(env_write,env_string,(Bitu)(strlen(env_string)+1));
+ env_write += (PhysPt)(strlen(env_string)+1);
+ } while (1);
+/* TODO Maybe save the program name sometime. not really needed though */
+ /* Save the new entry */
+
+ //ensure room
+ if (envsize <= (env_write-env_write_start) + strlen(entry) + 1 + strlen(new_string) + 2) return false;
+
+ if (new_string[0]) {
+ std::string bigentry(entry);
+ for (std::string::iterator it = bigentry.begin(); it != bigentry.end(); ++it) *it = toupper(*it);
+ snprintf(env_string,1024+1,"%s=%s",bigentry.c_str(),new_string);
+ MEM_BlockWrite(env_write,env_string,(Bitu)(strlen(env_string)+1));
+ env_write += (PhysPt)(strlen(env_string)+1);
+ }
+ /* Clear out the final piece of the environment */
+ mem_writeb(env_write,0);
+ return true;
+}
+
+bool MSG_Write(const char *);
+void restart_program(std::vector<std::string> & parameters);
+
+class CONFIG : public Program {
+public:
+ void Run(void);
+private:
+ void restart(const char* useconfig);
+
+ void writeconf(std::string name, bool configdir) {
+ if (configdir) {
+ // write file to the default config directory
+ std::string config_path;
+ Cross::GetPlatformConfigDir(config_path);
+ name = config_path + name;
+ }
+ WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_WHICH"),name.c_str());
+ if (!control->PrintConfig(name.c_str())) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_ERROR"),name.c_str());
+ }
+ return;
+ }
+
+ bool securemode_check() {
+ if (control->SecureMode()) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
+ return true;
+ }
+ return false;
+ }
+};
+
+void CONFIG::Run(void) {
+ static const char* const params[] = {
+ "-r", "-wcp", "-wcd", "-wc", "-writeconf", "-l", "-rmconf",
+ "-h", "-help", "-?", "-axclear", "-axadd", "-axtype", "-get", "-set",
+ "-writelang", "-wl", "-securemode", "" };
+ enum prs {
+ P_NOMATCH, P_NOPARAMS, // fixed return values for GetParameterFromList
+ P_RESTART,
+ P_WRITECONF_PORTABLE, P_WRITECONF_DEFAULT, P_WRITECONF, P_WRITECONF2,
+ P_LISTCONF, P_KILLCONF,
+ P_HELP, P_HELP2, P_HELP3,
+ P_AUTOEXEC_CLEAR, P_AUTOEXEC_ADD, P_AUTOEXEC_TYPE,
+ P_GETPROP, P_SETPROP,
+ P_WRITELANG, P_WRITELANG2,
+ P_SECURE
+ } presult = P_NOMATCH;
+
+ bool first = true;
+ std::vector<std::string> pvars;
+ // Loop through the passed parameters
+ while(presult != P_NOPARAMS) {
+ presult = (enum prs)cmd->GetParameterFromList(params, pvars);
+ switch(presult) {
+
+ case P_RESTART:
+ if (securemode_check()) return;
+ if (pvars.size() == 0) restart_program(control->startup_params);
+ else {
+ std::vector<std::string> restart_params;
+ restart_params.push_back(control->cmdline->GetFileName());
+ for(size_t i = 0; i < pvars.size(); i++) {
+ restart_params.push_back(pvars[i]);
+ if (pvars[i].find(' ') != std::string::npos) {
+ pvars[i] = "\""+pvars[i]+"\""; // add back spaces
+ }
+ }
+ // the rest on the commandline, too
+ cmd->FillVector(restart_params);
+ restart_program(restart_params);
+ }
+ return;
+
+ case P_LISTCONF: {
+ Bitu size = control->configfiles.size();
+ std::string config_path;
+ Cross::GetPlatformConfigDir(config_path);
+ WriteOut(MSG_Get("PROGRAM_CONFIG_CONFDIR"), VERSION,config_path.c_str());
+ if (size==0) WriteOut(MSG_Get("PROGRAM_CONFIG_NOCONFIGFILE"));
+ else {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_PRIMARY_CONF"),control->configfiles.front().c_str());
+ if (size > 1) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_ADDITIONAL_CONF"));
+ for(Bitu i = 1; i < size; i++)
+ WriteOut("%s\n",control->configfiles[i].c_str());
+ }
+ }
+ if (control->startup_params.size() > 0) {
+ std::string test;
+ for(size_t k = 0; k < control->startup_params.size(); k++)
+ test += control->startup_params[k] + " ";
+ WriteOut(MSG_Get("PROGRAM_CONFIG_PRINT_STARTUP"), test.c_str());
+ }
+ break;
+ }
+ case P_WRITECONF: case P_WRITECONF2:
+ if (securemode_check()) return;
+ if (pvars.size() > 1) return;
+ else if (pvars.size() == 1) {
+ // write config to specific file, except if it is an absolute path
+ writeconf(pvars[0], !Cross::IsPathAbsolute(pvars[0]));
+ } else {
+ // -wc without parameter: write primary config file
+ if (control->configfiles.size()) writeconf(control->configfiles[0], false);
+ else WriteOut(MSG_Get("PROGRAM_CONFIG_NOCONFIGFILE"));
+ }
+ break;
+ case P_WRITECONF_DEFAULT: {
+ // write to /userdir/dosbox0.xx.conf
+ if (securemode_check()) return;
+ if (pvars.size() > 0) return;
+ std::string confname;
+ Cross::GetPlatformConfigName(confname);
+ writeconf(confname, true);
+ break;
+ }
+ case P_WRITECONF_PORTABLE:
+ if (securemode_check()) return;
+ if (pvars.size() > 1) return;
+ else if (pvars.size() == 1) {
+ // write config to startup directory
+ writeconf(pvars[0], false);
+ } else {
+ // -wcp without parameter: write dosbox.conf to startup directory
+ if (control->configfiles.size()) writeconf(std::string("dosbox.conf"), false);
+ else WriteOut(MSG_Get("PROGRAM_CONFIG_NOCONFIGFILE"));
+ }
+ break;
+
+ case P_NOPARAMS:
+ if (!first) break;
+
+ case P_NOMATCH:
+ WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE"));
+ return;
+
+ case P_HELP: case P_HELP2: case P_HELP3: {
+ switch(pvars.size()) {
+ case 0:
+ WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE"));
+ return;
+ case 1: {
+ if (!strcasecmp("sections",pvars[0].c_str())) {
+ // list the sections
+ WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_SECTLIST"));
+ Bitu i = 0;
+ while(true) {
+ Section* sec = control->GetSection(i++);
+ if (!sec) break;
+ WriteOut("%s\n",sec->GetName());
+ }
+ return;
+ }
+ // if it's a section, leave it as one-param
+ Section* sec = control->GetSection(pvars[0].c_str());
+ if (!sec) {
+ // could be a property
+ sec = control->GetSectionFromProperty(pvars[0].c_str());
+ if (!sec) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
+ return;
+ }
+ pvars.insert(pvars.begin(),std::string(sec->GetName()));
+ }
+ break;
+ }
+ case 2: {
+ // sanity check
+ Section* sec = control->GetSection(pvars[0].c_str());
+ Section* sec2 = control->GetSectionFromProperty(pvars[1].c_str());
+ if (sec != sec2) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
+ }
+ break;
+ }
+ default:
+ WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE"));
+ return;
+ }
+ // if we have one value in pvars, it's a section
+ // two values are section + property
+ Section* sec = control->GetSection(pvars[0].c_str());
+ if (sec==NULL) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
+ return;
+ }
+ Section_prop* psec = dynamic_cast <Section_prop*>(sec);
+ if (psec==NULL) {
+ // failed; maybe it's the autoexec section?
+ Section_line* pline = dynamic_cast <Section_line*>(sec);
+ if (pline==NULL) E_Exit("Section dynamic cast failed.");
+
+ WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_LINEHLP"),
+ pline->GetName(),
+ // this is 'unclean' but the autoexec section has no help associated
+ MSG_Get("AUTOEXEC_CONFIGFILE_HELP"),
+ pline->data.c_str() );
+ return;
+ }
+ if (pvars.size()==1) {
+ size_t i = 0;
+ WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_SECTHLP"),pvars[0].c_str());
+ while(true) {
+ // list the properties
+ Property* p = psec->Get_prop(i++);
+ if (p==NULL) break;
+ WriteOut("%s\n", p->propname.c_str());
+ }
+ } else {
+ // find the property by it's name
+ size_t i = 0;
+ while (true) {
+ Property *p = psec->Get_prop(i++);
+ if (p==NULL) break;
+ if (!strcasecmp(p->propname.c_str(),pvars[1].c_str())) {
+ // found it; make the list of possible values
+ std::string propvalues;
+ std::vector<Value> pv = p->GetValues();
+
+ if (p->Get_type()==Value::V_BOOL) {
+ // possible values for boolean are true, false
+ propvalues += "true, false";
+ } else if (p->Get_type()==Value::V_INT) {
+ // print min, max for integer values if used
+ Prop_int* pint = dynamic_cast <Prop_int*>(p);
+ if (pint==NULL) E_Exit("Int property dynamic cast failed.");
+ if (pint->getMin() != pint->getMax()) {
+ std::ostringstream oss;
+ oss << pint->getMin();
+ oss << "..";
+ oss << pint->getMax();
+ propvalues += oss.str();
+ }
+ }
+ for(Bitu k = 0; k < pv.size(); k++) {
+ if (pv[k].ToString() =="%u")
+ propvalues += MSG_Get("PROGRAM_CONFIG_HLP_POSINT");
+ else propvalues += pv[k].ToString();
+ if ((k+1) < pv.size()) propvalues += ", ";
+ }
+
+ WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_PROPHLP"),
+ p->propname.c_str(),
+ sec->GetName(),
+ p->Get_help(),propvalues.c_str(),
+ p->Get_Default_Value().ToString().c_str(),
+ p->GetValue().ToString().c_str());
+ // print 'changability'
+ if (p->getChange()==Property::Changeable::OnlyAtStart) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_NOCHANGE"));
+ }
+ return;
+ }
+ }
+ break;
+ }
+ return;
+ }
+ case P_AUTOEXEC_CLEAR: {
+ Section_line* sec = dynamic_cast <Section_line*>
+ (control->GetSection(std::string("autoexec")));
+ sec->data.clear();
+ break;
+ }
+ case P_AUTOEXEC_ADD: {
+ if (pvars.size() == 0) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_MISSINGPARAM"));
+ return;
+ }
+ Section_line* sec = dynamic_cast <Section_line*>
+ (control->GetSection(std::string("autoexec")));
+
+ for(Bitu i = 0; i < pvars.size(); i++) sec->HandleInputline(pvars[i]);
+ break;
+ }
+ case P_AUTOEXEC_TYPE: {
+ Section_line* sec = dynamic_cast <Section_line*>
+ (control->GetSection(std::string("autoexec")));
+ WriteOut("\n%s",sec->data.c_str());
+ break;
+ }
+ case P_GETPROP: {
+ // "section property"
+ // "property"
+ // "section"
+ // "section" "property"
+ if (pvars.size()==0) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_GET_SYNTAX"));
+ return;
+ }
+ std::string::size_type spcpos = pvars[0].find_first_of(' ');
+ // split on the ' '
+ if (spcpos != std::string::npos) {
+ pvars.insert(pvars.begin()+1,pvars[0].substr(spcpos+1));
+ pvars[0].erase(spcpos);
+ }
+ switch(pvars.size()) {
+ case 1: {
+ // property/section only
+ // is it a section?
+ Section* sec = control->GetSection(pvars[0].c_str());
+ if (sec) {
+ // list properties in section
+ Bitu i = 0;
+ Section_prop* psec = dynamic_cast <Section_prop*>(sec);
+ if (psec==NULL) {
+ // autoexec section
+ Section_line* pline = dynamic_cast <Section_line*>(sec);
+ if (pline==NULL) E_Exit("Section dynamic cast failed.");
+
+ WriteOut("%s",pline->data.c_str());
+ break;
+ }
+ while(true) {
+ // list the properties
+ Property* p = psec->Get_prop(i++);
+ if (p==NULL) break;
+ WriteOut("%s=%s\n", p->propname.c_str(),
+ p->GetValue().ToString().c_str());
+ }
+ } else {
+ // no: maybe it's a property?
+ sec = control->GetSectionFromProperty(pvars[0].c_str());
+ if (!sec) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
+ return;
+ }
+ // it's a property name
+ std::string val = sec->GetPropValue(pvars[0].c_str());
+ WriteOut("%s",val.c_str());
+ first_shell->SetEnv("CONFIG",val.c_str());
+ }
+ break;
+ }
+ case 2: {
+ // section + property
+ Section* sec = control->GetSection(pvars[0].c_str());
+ if (!sec) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_SECTION_ERROR"));
+ return;
+ }
+ std::string val = sec->GetPropValue(pvars[1].c_str());
+ if (val == NO_SUCH_PROPERTY) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_NO_PROPERTY"),
+ pvars[1].c_str(),pvars[0].c_str());
+ return;
+ }
+ WriteOut("%s",val.c_str());
+ first_shell->SetEnv("CONFIG",val.c_str());
+ break;
+ }
+ default:
+ WriteOut(MSG_Get("PROGRAM_CONFIG_GET_SYNTAX"));
+ return;
+ }
+ return;
+ }
+ case P_SETPROP: {
+ // Code for the configuration changes
+ // Official format: config -set "section property=value"
+ // Accepted: with or without -set,
+ // "section property value"
+ // "section property=value"
+ // "property" "value"
+ // "section" "property=value"
+ // "section" "property=value" "value" "value" ...
+ // "section" "property" "value" "value" ...
+ // "section property" "value" "value" ...
+ // "property" "value" "value" ...
+ // "property=value" "value" "value" ...
+
+ if (pvars.size()==0) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_SET_SYNTAX"));
+ return;
+ }
+
+ // add rest of command
+ std::string rest;
+ if (cmd->GetStringRemain(rest)) pvars.push_back(rest);
+
+ // attempt to split off the first word
+ std::string::size_type spcpos = pvars[0].find_first_of(' ');
+ std::string::size_type equpos = pvars[0].find_first_of('=');
+
+ if ((equpos != std::string::npos) &&
+ ((spcpos == std::string::npos) || (equpos < spcpos))) {
+ // If we have a '=' possibly before a ' ' split on the =
+ pvars.insert(pvars.begin()+1,pvars[0].substr(equpos+1));
+ pvars[0].erase(equpos);
+ // As we had a = the first thing must be a property now
+ Section* sec=control->GetSectionFromProperty(pvars[0].c_str());
+ if (sec) pvars.insert(pvars.begin(),std::string(sec->GetName()));
+ else {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
+ return;
+ }
+ // order in the vector should be ok now
+ } else {
+ if ((spcpos != std::string::npos) &&
+ ((equpos == std::string::npos) || (spcpos < equpos))) {
+ // ' ' before a possible '=', split on the ' '
+ pvars.insert(pvars.begin()+1,pvars[0].substr(spcpos+1));
+ pvars[0].erase(spcpos);
+ }
+ // check if the first parameter is a section or property
+ Section* sec = control->GetSection(pvars[0].c_str());
+ if (!sec) {
+ // not a section: little duplicate from above
+ Section* sec=control->GetSectionFromProperty(pvars[0].c_str());
+ if (sec) pvars.insert(pvars.begin(),std::string(sec->GetName()));
+ else {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
+ return;
+ }
+ } else {
+ // first of pvars is most likely a section, but could still be gus
+ // have a look at the second parameter
+ if (pvars.size() < 2) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_SET_SYNTAX"));
+ return;
+ }
+ std::string::size_type spcpos2 = pvars[1].find_first_of(' ');
+ std::string::size_type equpos2 = pvars[1].find_first_of('=');
+ if ((equpos2 != std::string::npos) &&
+ ((spcpos2 == std::string::npos) || (equpos2 < spcpos2))) {
+ // split on the =
+ pvars.insert(pvars.begin()+2,pvars[1].substr(equpos2+1));
+ pvars[1].erase(equpos2);
+ } else if ((spcpos2 != std::string::npos) &&
+ ((equpos2 == std::string::npos) || (spcpos2 < equpos2))) {
+ // split on the ' '
+ pvars.insert(pvars.begin()+2,pvars[1].substr(spcpos2+1));
+ pvars[1].erase(spcpos2);
+ }
+ // is this a property?
+ Section* sec2 = control->GetSectionFromProperty(pvars[1].c_str());
+ if (!sec2) {
+ // not a property,
+ Section* sec3 = control->GetSectionFromProperty(pvars[0].c_str());
+ if (sec3) {
+ // section and property name are identical
+ pvars.insert(pvars.begin(),pvars[0]);
+ } // else has been checked above already
+ }
+ }
+ }
+ if(pvars.size() < 3) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_SET_SYNTAX"));
+ return;
+ }
+ // check if the property actually exists in the section
+ Section* sec2 = control->GetSectionFromProperty(pvars[1].c_str());
+ if (!sec2) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_NO_PROPERTY"),
+ pvars[1].c_str(),pvars[0].c_str());
+ return;
+ }
+ // Input has been parsed (pvar[0]=section, [1]=property, [2]=value)
+ // now execute
+ Section* tsec = control->GetSection(pvars[0]);
+ std::string value(pvars[2]);
+ //Due to parsing there can be a = at the start of value.
+ while (value.size() && (value.at(0) ==' ' ||value.at(0) =='=') ) value.erase(0,1);
+ for(Bitu i = 3; i < pvars.size(); i++) value += (std::string(" ") + pvars[i]);
+ if (value.empty() ) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_SET_SYNTAX"));
+ return;
+ }
+ std::string inputline = pvars[1] + "=" + value;
+
+ tsec->ExecuteDestroy(false);
+ bool change_success = tsec->HandleInputline(inputline.c_str());
+ if (!change_success) WriteOut(MSG_Get("PROGRAM_CONFIG_VALUE_ERROR"),
+ value.c_str(),pvars[1].c_str());
+ tsec->ExecuteInit(false);
+ return;
+ }
+ case P_WRITELANG: case P_WRITELANG2:
+ // In secure mode don't allow a new languagefile to be created
+ // Who knows which kind of file we would overwrite.
+ if (securemode_check()) return;
+ if (pvars.size() < 1) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_MISSINGPARAM"));
+ return;
+ }
+ if (!MSG_Write(pvars[0].c_str())) {
+ WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_ERROR"),pvars[0].c_str());
+ return;
+ }
+ break;
+
+ case P_SECURE:
+ // Code for switching to secure mode
+ control->SwitchToSecureMode();
+ WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_ON"));
+ return;
+
+ default:
+ E_Exit("bug");
+ break;
+ }
+ first = false;
+ }
+ return;
+}
+
+
+static void CONFIG_ProgramStart(Program * * make) {
+ *make=new CONFIG;
+}
+
+
+void PROGRAMS_Init(Section* /*sec*/) {
+ /* Setup a special callback to start virtual programs */
+ call_program=CALLBACK_Allocate();
+ CALLBACK_Setup(call_program,&PROGRAMS_Handler,CB_RETF,"internal program");
+ PROGRAMS_MakeFile("CONFIG.COM",CONFIG_ProgramStart);
+
+ // listconf
+ MSG_Add("PROGRAM_CONFIG_NOCONFIGFILE","No config file loaded!\n");
+ MSG_Add("PROGRAM_CONFIG_PRIMARY_CONF","Primary config file: \n%s\n");
+ MSG_Add("PROGRAM_CONFIG_ADDITIONAL_CONF","Additional config files:\n");
+ MSG_Add("PROGRAM_CONFIG_CONFDIR","DOSBox %s configuration directory: \n%s\n\n");
+
+ // writeconf
+ MSG_Add("PROGRAM_CONFIG_FILE_ERROR","\nCan't open file %s\n");
+ MSG_Add("PROGRAM_CONFIG_FILE_WHICH","Writing config file %s");
+
+ // help
+ MSG_Add("PROGRAM_CONFIG_USAGE","Config tool:\n"\
+ "-writeconf or -wc without parameter: write to primary loaded config file.\n"\
+ "-writeconf or -wc with filename: write file to config directory.\n"\
+ "Use -writelang or -wl filename to write the current language strings.\n"\
+ "-r [parameters]\n Restart DOSBox, either using the previous parameters or any that are appended.\n"\
+ "-wcp [filename]\n Write config file to the program directory, dosbox.conf or the specified \n filename.\n"\
+ "-wcd\n Write to the default config file in the config directory.\n"\
+ "-l lists configuration parameters.\n"\
+ "-h, -help, -? sections / sectionname / propertyname\n"\
+ " Without parameters, displays this help screen. Add \"sections\" for a list of\n sections."\
+ " For info about a specific section or property add its name behind.\n"\
+ "-axclear clears the autoexec section.\n"\
+ "-axadd [line] adds a line to the autoexec section.\n"\
+ "-axtype prints the content of the autoexec section.\n"\
+ "-securemode switches to secure mode.\n"\
+ "-get \"section property\" returns the value of the property.\n"\
+ "-set \"section property=value\" sets the value." );
+ MSG_Add("PROGRAM_CONFIG_HLP_PROPHLP","Purpose of property \"%s\" (contained in section \"%s\"):\n%s\n\nPossible Values: %s\nDefault value: %s\nCurrent value: %s\n");
+ MSG_Add("PROGRAM_CONFIG_HLP_LINEHLP","Purpose of section \"%s\":\n%s\nCurrent value:\n%s\n");
+ MSG_Add("PROGRAM_CONFIG_HLP_NOCHANGE","This property cannot be changed at runtime.\n");
+ MSG_Add("PROGRAM_CONFIG_HLP_POSINT","positive integer");
+ MSG_Add("PROGRAM_CONFIG_HLP_SECTHLP","Section %s contains the following properties:\n");
+ MSG_Add("PROGRAM_CONFIG_HLP_SECTLIST","DOSBox configuration contains the following sections:\n\n");
+
+ MSG_Add("PROGRAM_CONFIG_SECURE_ON","Switched to secure mode.\n");
+ MSG_Add("PROGRAM_CONFIG_SECURE_DISALLOW","This operation is not permitted in secure mode.\n");
+ MSG_Add("PROGRAM_CONFIG_SECTION_ERROR","Section %s doesn't exist.\n");
+ MSG_Add("PROGRAM_CONFIG_VALUE_ERROR","\"%s\" is not a valid value for property %s.\n");
+ MSG_Add("PROGRAM_CONFIG_PROPERTY_ERROR","No such section or property.\n");
+ MSG_Add("PROGRAM_CONFIG_NO_PROPERTY","There is no property %s in section %s.\n");
+ MSG_Add("PROGRAM_CONFIG_SET_SYNTAX","Correct syntax: config -set \"section property\".\n");
+ MSG_Add("PROGRAM_CONFIG_GET_SYNTAX","Correct syntax: config -get \"section property\".\n");
+ MSG_Add("PROGRAM_CONFIG_PRINT_STARTUP","\nDOSBox was started with the following command line parameters:\n%s");
+ MSG_Add("PROGRAM_CONFIG_MISSINGPARAM","Missing parameter.");
+}
diff --git a/src/misc/setup.cpp b/src/misc/setup.cpp
new file mode 100644
index 000000000..479462845
--- /dev/null
+++ b/src/misc/setup.cpp
@@ -0,0 +1,1208 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "cross.h"
+#include "setup.h"
+#include "control.h"
+#include "support.h"
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <list>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits>
+#include <limits.h>
+
+using namespace std;
+static std::string current_config_dir; // Set by parseconfigfile so Prop_path can use it to construct the realpath
+void Value::destroy() throw(){
+ if (type == V_STRING) delete _string;
+}
+
+Value& Value::copy(Value const& in) {
+ if (this != &in) { //Selfassigment!
+ if(type != V_NONE && type != in.type) throw WrongType();
+ destroy();
+ plaincopy(in);
+ }
+ return *this;
+}
+
+void Value::plaincopy(Value const& in) throw(){
+ type = in.type;
+ _int = in._int;
+ _double = in._double;
+ _bool = in._bool;
+ _hex = in._hex;
+ if(type == V_STRING) _string = new string(*in._string);
+}
+
+Value::operator bool () const {
+ if(type != V_BOOL) throw WrongType();
+ return _bool;
+}
+
+Value::operator Hex () const {
+ if(type != V_HEX) throw WrongType();
+ return _hex;
+}
+
+Value::operator int () const {
+ if(type != V_INT) throw WrongType();
+ return _int;
+}
+
+Value::operator double () const {
+ if(type != V_DOUBLE) throw WrongType();
+ return _double;
+}
+
+Value::operator char const* () const {
+ if(type != V_STRING) throw WrongType();
+ return _string->c_str();
+}
+
+bool Value::operator==(Value const& other) {
+ if(this == &other) return true;
+ if(type != other.type) return false;
+ switch(type){
+ case V_BOOL:
+ if(_bool == other._bool) return true;
+ break;
+ case V_INT:
+ if(_int == other._int) return true;
+ break;
+ case V_HEX:
+ if(_hex == other._hex) return true;
+ break;
+ case V_DOUBLE:
+ if(_double == other._double) return true;
+ break;
+ case V_STRING:
+ if((*_string) == (*other._string)) return true;
+ break;
+ default:
+ E_Exit("comparing stuff that doesn't make sense");
+ break;
+ }
+ return false;
+}
+bool Value::SetValue(string const& in,Etype _type) {
+ /* Throw exception if the current type isn't the wanted type
+ * Unless the wanted type is current.
+ */
+ if(_type == V_CURRENT && type == V_NONE) throw WrongType();
+ if(_type != V_CURRENT) {
+ if(type != V_NONE && type != _type) throw WrongType();
+ type = _type;
+ }
+ bool retval = true;
+ switch(type){
+ case V_HEX:
+ retval = set_hex(in);
+ break;
+ case V_INT:
+ retval = set_int(in);
+ break;
+ case V_BOOL:
+ retval = set_bool(in);
+ break;
+ case V_STRING:
+ set_string(in);
+ break;
+ case V_DOUBLE:
+ retval = set_double(in);
+ break;
+
+ case V_NONE:
+ case V_CURRENT:
+ default:
+ /* Shouldn't happen!/Unhandled */
+ throw WrongType();
+ break;
+ }
+ return retval;
+}
+
+bool Value::set_hex(std::string const& in) {
+ istringstream input(in);
+ input.flags(ios::hex);
+ Bits result = INT_MIN;
+ input >> result;
+ if(result == INT_MIN) return false;
+ _hex = result;
+ return true;
+}
+
+bool Value::set_int(string const &in) {
+ istringstream input(in);
+ Bits result = INT_MIN;
+ input >> result;
+ if(result == INT_MIN) return false;
+ _int = result;
+ return true;
+}
+bool Value::set_double(string const &in) {
+ istringstream input(in);
+ double result = std::numeric_limits<double>::infinity();
+ input >> result;
+ if(result == std::numeric_limits<double>::infinity()) return false;
+ _double = result;
+ return true;
+}
+
+bool Value::set_bool(string const &in) {
+ istringstream input(in);
+ string result;
+ input >> result;
+ lowcase(result);
+ _bool = true; // TODO
+ if(!result.size()) return false;
+
+ if(result=="0" || result=="disabled" || result=="false" || result=="off") {
+ _bool = false;
+ } else if(result=="1" || result=="enabled" || result=="true" || result=="on") {
+ _bool = true;
+ } else return false;
+
+ return true;
+}
+
+void Value::set_string(string const & in) {
+ if(!_string) _string = new string();
+ _string->assign(in);
+}
+
+string Value::ToString() const {
+ ostringstream oss;
+ switch(type) {
+ case V_HEX:
+ oss.flags(ios::hex);
+ oss << _hex;
+ break;
+ case V_INT:
+ oss << _int;
+ break;
+ case V_BOOL:
+ oss << boolalpha << _bool;
+ break;
+ case V_STRING:
+ oss << *_string;
+ break;
+ case V_DOUBLE:
+ oss.precision(2);
+ oss << fixed << _double;
+ break;
+ case V_NONE:
+ case V_CURRENT:
+ default:
+ E_Exit("ToString messed up ?");
+ break;
+ }
+ return oss.str();
+}
+
+bool Property::CheckValue(Value const& in, bool warn){
+ if(suggested_values.empty()) return true;
+ for(iter it = suggested_values.begin();it != suggested_values.end();it++) {
+ if ( (*it) == in) { //Match!
+ return true;
+ }
+ }
+ if (warn) LOG_MSG("\"%s\" is not a valid value for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),propname.c_str(),default_value.ToString().c_str());
+ return false;
+}
+
+void Property::Set_help(string const& in) {
+ string result = string("CONFIG_") + propname;
+ upcase(result);
+ MSG_Add(result.c_str(),in.c_str());
+}
+
+char const* Property::Get_help() {
+ string result = string("CONFIG_") + propname;
+ upcase(result);
+ return MSG_Get(result.c_str());
+}
+
+bool Prop_int::SetVal(Value const& in, bool forced, bool warn) {
+ if (forced) {
+ value = in;
+ return true;
+ } else if (!suggested_values.empty()){
+ if ( CheckValue(in,warn) ) {
+ value = in;
+ return true;
+ } else {
+ value = default_value;
+ return false;
+ }
+ } else {
+ //Handle ranges if specified
+ int mi = min;
+ int ma = max;
+ int va = static_cast<int>(Value(in));
+
+ //No ranges
+ if (mi == -1 && ma == -1) { value = in; return true;}
+
+ //Inside range
+ if (va >= mi && va <= ma) { value = in; return true;}
+
+ //Outside range, set it to the closest boundary
+ if (va > ma ) va = ma; else va = mi;
+
+ if (warn) LOG_MSG("%s is outside the allowed range %s-%s for variable: %s.\nIt has been set to the closest boundary: %d.",in.ToString().c_str(),min.ToString().c_str(),max.ToString().c_str(),propname.c_str(),va);
+
+ value = va;
+ return true;
+ }
+}
+bool Prop_int::CheckValue(Value const& in, bool warn) {
+// if(!suggested_values.empty() && Property::CheckValue(in,warn)) return true;
+ if(!suggested_values.empty()) return Property::CheckValue(in,warn);
+ LOG_MSG("still used ?");
+ //No >= and <= in Value type and == is ambigious
+ int mi = min;
+ int ma = max;
+ int va = static_cast<int>(Value(in));
+ if (mi == -1 && ma == -1) return true;
+ if (va >= mi && va <= ma) return true;
+
+ if (warn) LOG_MSG("%s lies outside the range %s-%s for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),min.ToString().c_str(),max.ToString().c_str(),propname.c_str(),default_value.ToString().c_str());
+ return false;
+}
+
+bool Prop_double::SetValue(std::string const& input) {
+ Value val;
+ if(!val.SetValue(input,Value::V_DOUBLE)) return false;
+ return SetVal(val,false,true);
+}
+
+//void Property::SetValue(char* input){
+// value.SetValue(input, Value::V_CURRENT);
+//}
+bool Prop_int::SetValue(std::string const& input) {
+ Value val;
+ if (!val.SetValue(input,Value::V_INT)) return false;
+ bool retval = SetVal(val,false,true);
+ return retval;
+}
+
+bool Prop_string::SetValue(std::string const& input) {
+ //Special version for lowcase stuff
+ std::string temp(input);
+ //suggested values always case insensitive.
+ //If there are none then it can be paths and such which are case sensitive
+ if (!suggested_values.empty()) lowcase(temp);
+ Value val(temp,Value::V_STRING);
+ return SetVal(val,false,true);
+}
+bool Prop_string::CheckValue(Value const& in, bool warn) {
+ if (suggested_values.empty()) return true;
+ for(iter it = suggested_values.begin();it != suggested_values.end();it++) {
+ if ( (*it) == in) { //Match!
+ return true;
+ }
+ if ((*it).ToString() == "%u") {
+ Bit32u value;
+ if(sscanf(in.ToString().c_str(),"%u",&value) == 1) {
+ return true;
+ }
+ }
+ }
+ if (warn) LOG_MSG("\"%s\" is not a valid value for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),propname.c_str(),default_value.ToString().c_str());
+ return false;
+}
+
+bool Prop_path::SetValue(std::string const& input) {
+ //Special version to merge realpath with it
+
+ Value val(input,Value::V_STRING);
+ bool retval = SetVal(val,false,true);
+
+ if (input.empty()) {
+ realpath = "";
+ return false;
+ }
+ std::string workcopy(input);
+ Cross::ResolveHomedir(workcopy); //Parse ~ and friends
+ //Prepend config directory in it exists. Check for absolute paths later
+ if ( current_config_dir.empty()) realpath = workcopy;
+ else realpath = current_config_dir + CROSS_FILESPLIT + workcopy;
+ //Absolute paths
+ if (Cross::IsPathAbsolute(workcopy)) realpath = workcopy;
+ return retval;
+}
+
+bool Prop_bool::SetValue(std::string const& input) {
+ return value.SetValue(input,Value::V_BOOL);
+}
+
+bool Prop_hex::SetValue(std::string const& input) {
+ Value val;
+ val.SetValue(input,Value::V_HEX);
+ return SetVal(val,false,true);
+}
+
+void Prop_multival::make_default_value() {
+ Bitu i = 1;
+ Property *p = section->Get_prop(0);
+ if (!p) return;
+
+ std::string result = p->Get_Default_Value().ToString();
+ while( (p = section->Get_prop(i++)) ) {
+ std::string props = p->Get_Default_Value().ToString();
+ if (props == "") continue;
+ result += separator; result += props;
+ }
+ Value val(result,Value::V_STRING);
+ SetVal(val,false,true);
+}
+
+//TODO checkvalue stuff
+bool Prop_multival_remain::SetValue(std::string const& input) {
+ Value val(input,Value::V_STRING);
+ bool retval = SetVal(val,false,true);
+
+ std::string local(input);
+ int i = 0,number_of_properties = 0;
+ Property *p = section->Get_prop(0);
+ //No properties in this section. do nothing
+ if (!p) return false;
+
+ while( (section->Get_prop(number_of_properties)) )
+ number_of_properties++;
+
+ string::size_type loc = string::npos;
+ while( (p = section->Get_prop(i++)) ) {
+ //trim leading separators
+ loc = local.find_first_not_of(separator);
+ if (loc != string::npos) local.erase(0,loc);
+ loc = local.find_first_of(separator);
+ string in = "";//default value
+ /* when i == number_of_properties add the total line. (makes more then
+ * one string argument possible for parameters of cpu) */
+ if (loc != string::npos && i < number_of_properties) { //separator found
+ in = local.substr(0,loc);
+ local.erase(0,loc+1);
+ } else if (local.size()) { //last argument or last property
+ in = local;
+ local = "";
+ }
+ //Test Value. If it fails set default
+ Value valtest (in,p->Get_type());
+ if (!p->CheckValue(valtest,true)) {
+ make_default_value();
+ return false;
+ }
+ p->SetValue(in);
+ }
+ return retval;
+}
+
+//TODO checkvalue stuff
+bool Prop_multival::SetValue(std::string const& input) {
+ Value val(input,Value::V_STRING);
+ bool retval = SetVal(val,false,true);
+
+ std::string local(input);
+ int i = 0;
+ Property *p = section->Get_prop(0);
+ //No properties in this section. do nothing
+ if (!p) return false;
+ Value::Etype prevtype = Value::V_NONE;
+ string prevargument = "";
+
+ string::size_type loc = string::npos;
+ while( (p = section->Get_prop(i++)) ) {
+ //trim leading separators
+ loc = local.find_first_not_of(separator);
+ if (loc != string::npos) local.erase(0,loc);
+ loc = local.find_first_of(separator);
+ string in = "";//default value
+ if (loc != string::npos) { //separator found
+ in = local.substr(0,loc);
+ local.erase(0,loc+1);
+ } else if (local.size()) { //last argument
+ in = local;
+ local = "";
+ }
+
+ if (p->Get_type() == Value::V_STRING) {
+ //Strings are only checked against the suggested values list.
+ //Test Value. If it fails set default
+ Value valtest (in,p->Get_type());
+ if (!p->CheckValue(valtest,true)) {
+ make_default_value();
+ return false;
+ }
+ p->SetValue(in);
+ } else {
+ //Non-strings can have more things, conversion alone is not enough (as invalid values as converted to 0)
+ bool r = p->SetValue(in);
+ if (!r) {
+ if (in.empty() && p->Get_type() == prevtype ) {
+ //Nothing there, but same type of variable, so repeat it (sensitivity)
+ in = prevargument;
+ p->SetValue(in);
+ } else {
+ //Something was there to be parsed or not the same type. Invalidate entire property.
+ make_default_value();
+ }
+ }
+ }
+ prevtype = p->Get_type();
+ prevargument = in;
+
+ }
+ return retval;
+}
+
+const std::vector<Value>& Property::GetValues() const {
+ return suggested_values;
+}
+const std::vector<Value>& Prop_multival::GetValues() const {
+ Property *p = section->Get_prop(0);
+ //No properties in this section. do nothing
+ if (!p) return suggested_values;
+ int i =0;
+ while( (p = section->Get_prop(i++)) ) {
+ std::vector<Value> v = p->GetValues();
+ if(!v.empty()) return p->GetValues();
+ }
+ return suggested_values;
+}
+
+/*
+void Section_prop::Add_double(char const * const _propname, double _value) {
+ Property* test=new Prop_double(_propname,_value);
+ properties.push_back(test);
+}*/
+
+void Property::Set_values(const char * const *in) {
+ Value::Etype type = default_value.type;
+ int i = 0;
+ while (in[i]) {
+ Value val(in[i],type);
+ suggested_values.push_back(val);
+ i++;
+ }
+}
+
+Prop_int* Section_prop::Add_int(string const& _propname, Property::Changeable::Value when, int _value) {
+ Prop_int* test=new Prop_int(_propname,when,_value);
+ properties.push_back(test);
+ return test;
+}
+
+Prop_string* Section_prop::Add_string(string const& _propname, Property::Changeable::Value when, char const * const _value) {
+ Prop_string* test=new Prop_string(_propname,when,_value);
+ properties.push_back(test);
+ return test;
+}
+
+Prop_path* Section_prop::Add_path(string const& _propname, Property::Changeable::Value when, char const * const _value) {
+ Prop_path* test=new Prop_path(_propname,when,_value);
+ properties.push_back(test);
+ return test;
+}
+
+Prop_bool* Section_prop::Add_bool(string const& _propname, Property::Changeable::Value when, bool _value) {
+ Prop_bool* test=new Prop_bool(_propname,when,_value);
+ properties.push_back(test);
+ return test;
+}
+
+Prop_hex* Section_prop::Add_hex(string const& _propname, Property::Changeable::Value when, Hex _value) {
+ Prop_hex* test=new Prop_hex(_propname,when,_value);
+ properties.push_back(test);
+ return test;
+}
+
+Prop_multival* Section_prop::Add_multi(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) {
+ Prop_multival* test = new Prop_multival(_propname,when,sep);
+ properties.push_back(test);
+ return test;
+}
+
+Prop_multival_remain* Section_prop::Add_multiremain(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) {
+ Prop_multival_remain* test = new Prop_multival_remain(_propname,when,sep);
+ properties.push_back(test);
+ return test;
+}
+
+int Section_prop::Get_int(string const&_propname) const {
+ for(const_it tel=properties.begin();tel!=properties.end();tel++){
+ if ((*tel)->propname==_propname){
+ return ((*tel)->GetValue());
+ }
+ }
+ return 0;
+}
+
+bool Section_prop::Get_bool(string const& _propname) const {
+ for(const_it tel=properties.begin();tel!=properties.end();tel++){
+ if ((*tel)->propname==_propname){
+ return ((*tel)->GetValue());
+ }
+ }
+ return false;
+}
+
+double Section_prop::Get_double(string const& _propname) const {
+ for(const_it tel=properties.begin();tel!=properties.end();tel++){
+ if ((*tel)->propname==_propname){
+ return ((*tel)->GetValue());
+ }
+ }
+ return 0.0;
+}
+
+Prop_path* Section_prop::Get_path(string const& _propname) const {
+ for(const_it tel=properties.begin();tel!=properties.end();tel++){
+ if ((*tel)->propname==_propname){
+ Prop_path* val = dynamic_cast<Prop_path*>((*tel));
+ if (val) return val; else return NULL;
+ }
+ }
+ return NULL;
+}
+
+Prop_multival* Section_prop::Get_multival(string const& _propname) const {
+ for(const_it tel=properties.begin();tel!=properties.end();tel++){
+ if((*tel)->propname==_propname){
+ Prop_multival* val = dynamic_cast<Prop_multival*>((*tel));
+ if(val) return val; else return NULL;
+ }
+ }
+ return NULL;
+}
+
+Prop_multival_remain* Section_prop::Get_multivalremain(string const& _propname) const {
+ for(const_it tel=properties.begin();tel!=properties.end();tel++){
+ if ((*tel)->propname==_propname){
+ Prop_multival_remain* val = dynamic_cast<Prop_multival_remain*>((*tel));
+ if (val) return val; else return NULL;
+ }
+ }
+ return NULL;
+}
+Property* Section_prop::Get_prop(int index){
+ for(it tel=properties.begin();tel!=properties.end();tel++){
+ if (!index--) return (*tel);
+ }
+ return NULL;
+}
+
+const char* Section_prop::Get_string(string const& _propname) const {
+ for(const_it tel=properties.begin();tel!=properties.end();tel++){
+ if((*tel)->propname==_propname){
+ return ((*tel)->GetValue());
+ }
+ }
+ return "";
+}
+Hex Section_prop::Get_hex(string const& _propname) const {
+ for(const_it tel=properties.begin();tel!=properties.end();tel++){
+ if((*tel)->propname==_propname){
+ return ((*tel)->GetValue());
+ }
+ }
+ return 0;
+}
+
+bool Section_prop::HandleInputline(string const& gegevens){
+ string str1 = gegevens;
+ string::size_type loc = str1.find('=');
+ if (loc == string::npos) return false;
+ string name = str1.substr(0,loc);
+ string val = str1.substr(loc + 1);
+
+ /* Remove quotes around value */
+ trim(val);
+ string::size_type length = val.length();
+ if (length > 1 &&
+ ((val[0] == '\"' && val[length - 1] == '\"' ) ||
+ (val[0] == '\'' && val[length - 1] == '\''))
+ ) val = val.substr(1,length - 2);
+ /* trim the results incase there were spaces somewhere */
+ trim(name);trim(val);
+ for(it tel = properties.begin();tel != properties.end();tel++){
+ if(!strcasecmp((*tel)->propname.c_str(),name.c_str())){
+ return (*tel)->SetValue(val);
+ }
+ }
+ return false;
+}
+
+void Section_prop::PrintData(FILE* outfile) const {
+ /* Now print out the individual section entries */
+ size_t len = 0;
+ // Determine maximum length of the props in this section
+ for(const_it tel = properties.begin();tel != properties.end();tel++) {
+ if ((*tel)->propname.length() > len)
+ len = (*tel)->propname.length();
+ }
+
+ for(const_it tel = properties.begin();tel != properties.end();tel++) {
+ fprintf(outfile,"%-*s = %s\n", len, (*tel)->propname.c_str(), (*tel)->GetValue().ToString().c_str());
+ }
+}
+
+string Section_prop::GetPropValue(string const& _property) const {
+ for(const_it tel=properties.begin();tel!=properties.end();tel++){
+ if (!strcasecmp((*tel)->propname.c_str(),_property.c_str())){
+ return (*tel)->GetValue().ToString();
+ }
+ }
+ return NO_SUCH_PROPERTY;
+}
+
+bool Section_line::HandleInputline(string const& line) {
+ if (!data.empty()) data += "\n"; //Add return to previous line in buffer
+ data += line;
+ return true;
+}
+
+void Section_line::PrintData(FILE* outfile) const {
+ fprintf(outfile,"%s",data.c_str());
+}
+
+string Section_line::GetPropValue(string const& /* _property*/) const {
+ return NO_SUCH_PROPERTY;
+}
+
+bool Config::PrintConfig(char const * const configfilename) const {
+ char temp[50];char helpline[256];
+ FILE* outfile=fopen(configfilename,"w+t");
+ if(outfile==NULL) return false;
+
+ /* Print start of configfile and add a return to improve readibility. */
+ fprintf(outfile,MSG_Get("CONFIGFILE_INTRO"),VERSION);
+ fprintf(outfile,"\n");
+ for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){
+ /* Print out the Section header */
+ strcpy(temp,(*tel)->GetName());
+ lowcase(temp);
+ fprintf(outfile,"[%s]\n",temp);
+
+ Section_prop *sec = dynamic_cast<Section_prop *>(*tel);
+ if (sec) {
+ Property *p;
+ size_t i = 0, maxwidth = 0;
+ while ((p = sec->Get_prop(i++))) {
+ size_t w = strlen(p->propname.c_str());
+ if (w > maxwidth) maxwidth = w;
+ }
+ i=0;
+ char prefix[80];
+ snprintf(prefix,80, "\n# %*s ", (int)maxwidth, "");
+ while ((p = sec->Get_prop(i++))) {
+ std::string help = p->Get_help();
+ std::string::size_type pos = std::string::npos;
+ while ((pos = help.find("\n", pos+1)) != std::string::npos) {
+ help.replace(pos, 1, prefix);
+ }
+
+ fprintf(outfile, "# %*s: %s", (int)maxwidth, p->propname.c_str(), help.c_str());
+
+ std::vector<Value> values = p->GetValues();
+ if (!values.empty()) {
+ fprintf(outfile, "%s%s:", prefix, MSG_Get("CONFIG_SUGGESTED_VALUES"));
+ std::vector<Value>::iterator it = values.begin();
+ while (it != values.end()) {
+ if((*it).ToString() != "%u") { //Hack hack hack. else we need to modify GetValues, but that one is const...
+ if (it != values.begin()) fputs(",", outfile);
+ fprintf(outfile, " %s", (*it).ToString().c_str());
+ }
+ ++it;
+ }
+ fprintf(outfile,".");
+ }
+ fprintf(outfile, "\n");
+ }
+ } else {
+ upcase(temp);
+ strcat(temp,"_CONFIGFILE_HELP");
+ const char * helpstr=MSG_Get(temp);
+ char * helpwrite=helpline;
+ while (*helpstr) {
+ *helpwrite++=*helpstr;
+ if (*helpstr == '\n') {
+ *helpwrite=0;
+ fprintf(outfile,"# %s",helpline);
+ helpwrite=helpline;
+ }
+ helpstr++;
+ }
+ }
+
+ fprintf(outfile,"\n");
+ (*tel)->PrintData(outfile);
+ fprintf(outfile,"\n"); /* Always an empty line between sections */
+ }
+ fclose(outfile);
+ return true;
+}
+
+
+Section_prop* Config::AddSection_prop(char const * const _name,void (*_initfunction)(Section*),bool canchange) {
+ Section_prop* blah = new Section_prop(_name);
+ blah->AddInitFunction(_initfunction,canchange);
+ sectionlist.push_back(blah);
+ return blah;
+}
+
+Section_prop::~Section_prop() {
+ //ExecuteDestroy should be here else the destroy functions use destroyed properties
+ ExecuteDestroy(true);
+ /* Delete properties themself (properties stores the pointer of a prop */
+ for(it prop = properties.begin(); prop != properties.end(); prop++)
+ delete (*prop);
+}
+
+
+Section_line* Config::AddSection_line(char const * const _name,void (*_initfunction)(Section*)) {
+ Section_line* blah = new Section_line(_name);
+ blah->AddInitFunction(_initfunction);
+ sectionlist.push_back(blah);
+ return blah;
+}
+
+
+void Config::Init() {
+ for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++) {
+ (*tel)->ExecuteInit();
+ }
+}
+
+void Section::AddInitFunction(SectionFunction func,bool canchange) {
+ initfunctions.push_back(Function_wrapper(func,canchange));
+}
+
+void Section::AddDestroyFunction(SectionFunction func,bool canchange) {
+ destroyfunctions.push_front(Function_wrapper(func,canchange));
+}
+
+
+void Section::ExecuteInit(bool initall) {
+ typedef std::list<Function_wrapper>::iterator func_it;
+ for (func_it tel=initfunctions.begin(); tel!=initfunctions.end(); tel++) {
+ if(initall || (*tel).canchange) (*tel).function(this);
+ }
+}
+
+void Section::ExecuteDestroy(bool destroyall) {
+ typedef std::list<Function_wrapper>::iterator func_it;
+ for (func_it tel=destroyfunctions.begin(); tel!=destroyfunctions.end(); ) {
+ if(destroyall || (*tel).canchange) {
+ (*tel).function(this);
+ tel=destroyfunctions.erase(tel); //Remove destroyfunction once used
+ } else tel++;
+ }
+}
+
+Config::~Config() {
+ reverse_it cnt=sectionlist.rbegin();
+ while (cnt!=sectionlist.rend()) {
+ delete (*cnt);
+ cnt++;
+ }
+}
+
+Section* Config::GetSection(int index) {
+ for (it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){
+ if (!index--) return (*tel);
+ }
+ return NULL;
+}
+
+Section* Config::GetSection(string const& _sectionname) const {
+ for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){
+ if (!strcasecmp((*tel)->GetName(),_sectionname.c_str())) return (*tel);
+ }
+ return NULL;
+}
+
+Section* Config::GetSectionFromProperty(char const * const prop) const {
+ for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){
+ if ((*tel)->GetPropValue(prop) != NO_SUCH_PROPERTY) return (*tel);
+ }
+ return NULL;
+}
+
+bool Config::ParseConfigFile(char const * const configfilename) {
+ //static bool first_configfile = true;
+ ifstream in(configfilename);
+ if (!in) return false;
+ const char * settings_type;
+ settings_type = (configfiles.size() == 0)? "primary":"additional";
+ configfiles.push_back(configfilename);
+
+ LOG_MSG("CONFIG: Loading %s settings from config file %s", settings_type,configfilename);
+
+ //Get directory from configfilename, used with relative paths.
+ current_config_dir=configfilename;
+ std::string::size_type pos = current_config_dir.rfind(CROSS_FILESPLIT);
+ if(pos == std::string::npos) pos = 0; //No directory then erase string
+ current_config_dir.erase(pos);
+
+ string gegevens;
+ Section* currentsection = NULL;
+ Section* testsec = NULL;
+ while (getline(in,gegevens)) {
+
+ /* strip leading/trailing whitespace */
+ trim(gegevens);
+ if(!gegevens.size()) continue;
+
+ switch(gegevens[0]){
+ case '%':
+ case '\0':
+ case '#':
+ case ' ':
+ case '\n':
+ continue;
+ break;
+ case '[':
+ {
+ string::size_type loc = gegevens.find(']');
+ if(loc == string::npos) continue;
+ gegevens.erase(loc);
+ testsec = GetSection(gegevens.substr(1));
+ if(testsec != NULL ) currentsection = testsec;
+ testsec = NULL;
+ }
+ break;
+ default:
+ try {
+ if(currentsection) currentsection->HandleInputline(gegevens);
+ } catch(const char* message) {
+ message=0;
+ //EXIT with message
+ }
+ break;
+ }
+ }
+ current_config_dir.clear();//So internal changes don't use the path information
+ return true;
+}
+
+/*const char* Config::GetPrimaryConfigFile() {
+ return configfile.c_str();
+}*/
+
+void Config::ParseEnv(char ** envp) {
+ for(char** env=envp; *env;env++) {
+ char copy[1024];
+ safe_strncpy(copy,*env,1024);
+ if(strncasecmp(copy,"DOSBOX_",7))
+ continue;
+ char* sec_name = &copy[7];
+ if(!(*sec_name))
+ continue;
+ char* prop_name = strrchr(sec_name,'_');
+ if(!prop_name || !(*prop_name))
+ continue;
+ *prop_name++=0;
+ Section* sect = GetSection(sec_name);
+ if(!sect)
+ continue;
+ sect->HandleInputline(prop_name);
+ }
+}
+
+void Config::SetStartUp(void (*_function)(void)) {
+ _start_function=_function;
+}
+
+
+void Config::StartUp(void) {
+ initialised=true;
+ (*_start_function)();
+}
+
+bool CommandLine::FindExist(char const * const name,bool remove) {
+ cmd_it it;
+ if (!(FindEntry(name,it,false))) return false;
+ if (remove) cmds.erase(it);
+ return true;
+}
+
+bool CommandLine::FindHex(char const * const name,int & value,bool remove) {
+ cmd_it it,it_next;
+ if (!(FindEntry(name,it,true))) return false;
+ it_next=it;it_next++;
+ sscanf((*it_next).c_str(),"%X",&value);
+ if (remove) cmds.erase(it,++it_next);
+ return true;
+}
+
+bool CommandLine::FindInt(char const * const name,int & value,bool remove) {
+ cmd_it it,it_next;
+ if (!(FindEntry(name,it,true))) return false;
+ it_next=it;it_next++;
+ value=atoi((*it_next).c_str());
+ if (remove) cmds.erase(it,++it_next);
+ return true;
+}
+
+bool CommandLine::FindString(char const * const name,std::string & value,bool remove) {
+ cmd_it it,it_next;
+ if (!(FindEntry(name,it,true))) return false;
+ it_next=it;it_next++;
+ value=*it_next;
+ if (remove) cmds.erase(it,++it_next);
+ return true;
+}
+
+bool CommandLine::FindCommand(unsigned int which,std::string & value) {
+ if (which<1) return false;
+ if (which>cmds.size()) return false;
+ cmd_it it=cmds.begin();
+ for (;which>1;which--) it++;
+ value=(*it);
+ return true;
+}
+
+bool CommandLine::FindEntry(char const * const name,cmd_it & it,bool neednext) {
+ for (it=cmds.begin();it!=cmds.end();it++) {
+ if (!strcasecmp((*it).c_str(),name)) {
+ cmd_it itnext=it;itnext++;
+ if (neednext && (itnext==cmds.end())) return false;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CommandLine::FindStringBegin(char const* const begin,std::string & value, bool remove) {
+ size_t len = strlen(begin);
+ for (cmd_it it=cmds.begin();it!=cmds.end();it++) {
+ if (strncmp(begin,(*it).c_str(),len)==0) {
+ value=((*it).c_str() + len);
+ if (remove) cmds.erase(it);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CommandLine::FindStringRemain(char const * const name,std::string & value) {
+ cmd_it it;value="";
+ if (!FindEntry(name,it)) return false;
+ it++;
+ for (;it!=cmds.end();it++) {
+ value+=" ";
+ value+=(*it);
+ }
+ return true;
+}
+
+/* Only used for parsing command.com /C
+ * Allowing /C dir and /Cdir
+ * Restoring quotes back into the commands so command /C mount d "/tmp/a b" works as intended
+ */
+bool CommandLine::FindStringRemainBegin(char const * const name,std::string & value) {
+ cmd_it it;value="";
+ if (!FindEntry(name,it)) {
+ size_t len = strlen(name);
+ for (it=cmds.begin();it!=cmds.end();it++) {
+ if (strncasecmp(name,(*it).c_str(),len)==0) {
+ std::string temp = ((*it).c_str() + len);
+ //Restore quotes for correct parsing in later stages
+ if(temp.find(" ") != std::string::npos)
+ value = std::string("\"") + temp + std::string("\"");
+ else
+ value = temp;
+ break;
+ }
+ }
+ if( it == cmds.end()) return false;
+ }
+ it++;
+ for (;it!=cmds.end();it++) {
+ value += " ";
+ std::string temp = (*it);
+ if(temp.find(" ") != std::string::npos)
+ value += std::string("\"") + temp + std::string("\"");
+ else
+ value += temp;
+ }
+ return true;
+}
+
+bool CommandLine::GetStringRemain(std::string & value) {
+ if (!cmds.size()) return false;
+
+ cmd_it it=cmds.begin();value=(*it++);
+ for(;it != cmds.end();it++) {
+ value+=" ";
+ value+=(*it);
+ }
+ return true;
+}
+
+
+unsigned int CommandLine::GetCount(void) {
+ return (unsigned int)cmds.size();
+}
+
+void CommandLine::FillVector(std::vector<std::string> & vector) {
+ for(cmd_it it=cmds.begin(); it != cmds.end(); it++) {
+ vector.push_back((*it));
+ }
+ // add back the \" if the parameter contained a space
+ for(Bitu i = 0; i < vector.size(); i++) {
+ if(vector[i].find(' ') != std::string::npos) {
+ vector[i] = "\""+vector[i]+"\"";
+ }
+ }
+}
+
+int CommandLine::GetParameterFromList(const char* const params[], std::vector<std::string> & output) {
+ // return values: 0 = P_NOMATCH, 1 = P_NOPARAMS
+ // TODO return nomoreparams
+ int retval = 1;
+ output.clear();
+ enum {
+ P_START, P_FIRSTNOMATCH, P_FIRSTMATCH
+ } parsestate = P_START;
+ cmd_it it = cmds.begin();
+ while(it!=cmds.end()) {
+ bool found = false;
+ for(Bitu i = 0; *params[i]!=0; i++) {
+ if (!strcasecmp((*it).c_str(),params[i])) {
+ // found a parameter
+ found = true;
+ switch(parsestate) {
+ case P_START:
+ retval = i+2;
+ parsestate = P_FIRSTMATCH;
+ break;
+ case P_FIRSTMATCH:
+ case P_FIRSTNOMATCH:
+ return retval;
+ }
+ }
+ }
+ if(!found)
+ switch(parsestate) {
+ case P_START:
+ retval = 0; // no match
+ parsestate = P_FIRSTNOMATCH;
+ output.push_back(*it);
+ break;
+ case P_FIRSTMATCH:
+ case P_FIRSTNOMATCH:
+ output.push_back(*it);
+ break;
+ }
+ cmd_it itold = it;
+ it++;
+ cmds.erase(itold);
+
+ }
+
+ return retval;
+/*
+bool CommandLine::FindEntry(char const * const name,cmd_it & it,bool neednext) {
+ for (it=cmds.begin();it!=cmds.end();it++) {
+ if (!strcasecmp((*it).c_str(),name)) {
+ cmd_it itnext=it;itnext++;
+ if (neednext && (itnext==cmds.end())) return false;
+ return true;
+ }
+ }
+ return false;
+*/
+
+
+/*
+ cmd_it it=cmds.begin();value=(*it++);
+ while(it != cmds.end()) {
+ if(params.
+
+ it++;
+ }
+*/
+ // find next parameter
+ //return -1;
+
+}
+
+
+CommandLine::CommandLine(int argc,char const * const argv[]) {
+ if (argc>0) {
+ file_name=argv[0];
+ }
+ int i=1;
+ while (i<argc) {
+ cmds.push_back(argv[i]);
+ i++;
+ }
+}
+Bit16u CommandLine::Get_arglength() {
+ if (cmds.empty()) return 0;
+ Bit16u i=1;
+ for(cmd_it it=cmds.begin();it != cmds.end();it++)
+ i+=(*it).size() + 1;
+ return --i;
+}
+
+
+CommandLine::CommandLine(char const * const name,char const * const cmdline) {
+ if (name) file_name=name;
+ /* Parse the cmds and put them in the list */
+ bool inword,inquote;char c;
+ inword=false;inquote=false;
+ std::string str;
+ const char * c_cmdline=cmdline;
+ while ((c=*c_cmdline)!=0) {
+ if (inquote) {
+ if (c!='"') str+=c;
+ else {
+ inquote=false;
+ cmds.push_back(str);
+ str.erase();
+ }
+ } else if (inword) {
+ if (c!=' ') str+=c;
+ else {
+ inword=false;
+ cmds.push_back(str);
+ str.erase();
+ }
+ }
+ else if (c=='\"') { inquote=true;}
+ else if (c!=' ') { str+=c;inword=true;}
+ c_cmdline++;
+ }
+ if (inword || inquote) cmds.push_back(str);
+}
+
+void CommandLine::Shift(unsigned int amount) {
+ while(amount--) {
+ file_name = cmds.size()?(*(cmds.begin())):"";
+ if(cmds.size()) cmds.erase(cmds.begin());
+ }
+}
diff --git a/src/misc/support.cpp b/src/misc/support.cpp
new file mode 100644
index 000000000..89f7802a7
--- /dev/null
+++ b/src/misc/support.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <algorithm>
+#include <cctype>
+#include <string>
+
+#include "dosbox.h"
+#include "debug.h"
+#include "support.h"
+#include "video.h"
+
+
+void upcase(std::string &str) {
+ int (*tf)(int) = std::toupper;
+ std::transform(str.begin(), str.end(), str.begin(), tf);
+}
+
+void lowcase(std::string &str) {
+ int (*tf)(int) = std::tolower;
+ std::transform(str.begin(), str.end(), str.begin(), tf);
+}
+
+void trim(std::string &str) {
+ std::string::size_type loc = str.find_first_not_of(" \r\t\f\n");
+ if (loc != std::string::npos) str.erase(0,loc);
+ loc = str.find_last_not_of(" \r\t\f\n");
+ if (loc != std::string::npos) str.erase(loc+1);
+}
+
+/*
+ Ripped some source from freedos for this one.
+
+*/
+
+
+/*
+ * replaces all instances of character o with character c
+ */
+
+
+void strreplace(char * str,char o,char n) {
+ while (*str) {
+ if (*str==o) *str=n;
+ str++;
+ }
+}
+char *ltrim(char *str) {
+ while (*str && isspace(*reinterpret_cast<unsigned char*>(str))) str++;
+ return str;
+}
+
+char *rtrim(char *str) {
+ char *p;
+ p = strchr(str, '\0');
+ while (--p >= str && isspace(*reinterpret_cast<unsigned char*>(p))) {};
+ p[1] = '\0';
+ return str;
+}
+
+char *trim(char *str) {
+ return ltrim(rtrim(str));
+}
+
+char * upcase(char * str) {
+ for (char* idx = str; *idx ; idx++) *idx = toupper(*reinterpret_cast<unsigned char*>(idx));
+ return str;
+}
+
+char * lowcase(char * str) {
+ for(char* idx = str; *idx ; idx++) *idx = tolower(*reinterpret_cast<unsigned char*>(idx));
+ return str;
+}
+
+
+
+bool ScanCMDBool(char * cmd,char const * const check) {
+ char * scan=cmd;size_t c_len=strlen(check);
+ while ((scan=strchr(scan,'/'))) {
+ /* found a / now see behind it */
+ scan++;
+ if (strncasecmp(scan,check,c_len)==0 && (scan[c_len]==' ' || scan[c_len]=='\t' || scan[c_len]=='/' || scan[c_len]==0)) {
+ /* Found a math now remove it from the string */
+ memmove(scan-1,scan+c_len,strlen(scan+c_len)+1);
+ trim(scan-1);
+ return true;
+ }
+ }
+ return false;
+}
+
+/* This scans the command line for a remaining switch and reports it else returns 0*/
+char * ScanCMDRemain(char * cmd) {
+ char * scan,*found;;
+ if ((scan=found=strchr(cmd,'/'))) {
+ while ( *scan && !isspace(*reinterpret_cast<unsigned char*>(scan)) ) scan++;
+ *scan=0;
+ return found;
+ } else return 0;
+}
+
+char * StripWord(char *&line) {
+ char * scan=line;
+ scan=ltrim(scan);
+ if (*scan=='"') {
+ char * end_quote=strchr(scan+1,'"');
+ if (end_quote) {
+ *end_quote=0;
+ line=ltrim(++end_quote);
+ return (scan+1);
+ }
+ }
+ char * begin=scan;
+ for (char c = *scan ;(c = *scan);scan++) {
+ if (isspace(*reinterpret_cast<unsigned char*>(&c))) {
+ *scan++=0;
+ break;
+ }
+ }
+ line=scan;
+ return begin;
+}
+
+Bits ConvDecWord(char * word) {
+ bool negative=false;Bitu ret=0;
+ if (*word=='-') {
+ negative=true;
+ word++;
+ }
+ while (char c=*word) {
+ ret*=10;
+ ret+=c-'0';
+ word++;
+ }
+ if (negative) return 0-ret;
+ else return ret;
+}
+
+Bits ConvHexWord(char * word) {
+ Bitu ret=0;
+ while (char c=toupper(*reinterpret_cast<unsigned char*>(word))) {
+ ret*=16;
+ if (c>='0' && c<='9') ret+=c-'0';
+ else if (c>='A' && c<='F') ret+=10+(c-'A');
+ word++;
+ }
+ return ret;
+}
+
+double ConvDblWord(char * word) {
+ return 0.0f;
+}
+
+
+static char buf[1024]; //greater scope as else it doesn't always gets thrown right (linux/gcc2.95)
+void E_Exit(const char * format,...) {
+#if C_DEBUG && C_HEAVY_DEBUG
+ DEBUG_HeavyWriteLogInstruction();
+#endif
+ va_list msg;
+ va_start(msg,format);
+ vsprintf(buf,format,msg);
+ va_end(msg);
+ strcat(buf,"\n");
+
+ throw(buf);
+}
diff --git a/src/platform/Makefile.am b/src/platform/Makefile.am
new file mode 100644
index 000000000..34b4b57e9
--- /dev/null
+++ b/src/platform/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = visualc
+
+EXTRA_DIST = sdl-win32.diff \ No newline at end of file
diff --git a/src/platform/sdl-win32.diff b/src/platform/sdl-win32.diff
new file mode 100644
index 000000000..0b9ae53ac
--- /dev/null
+++ b/src/platform/sdl-win32.diff
@@ -0,0 +1,55 @@
+diff -ru SDL-1.2.12/src/main/win32/version.rc SDL-1.2.12release/src/main/win32/version.rc
+--- SDL-1.2.12/src/main/win32/version.rc 2007-07-20 07:52:05.000000000 +0200
++++ SDL-1.2.12release/src/main/win32/version.rc 2007-08-17 19:03:42.000000000 +0200
+@@ -13,7 +13,7 @@
+ FILEVERSION 1,2,12,0
+ PRODUCTVERSION 1,2,12,0
+ FILEFLAGSMASK 0x3fL
+- FILEFLAGS 0x0L
++ FILEFLAGS 0x4L
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+diff -ru SDL-1.2.12/src/video/SDL_video.c SDL-1.2.12release/src/video/SDL_video.c
+--- SDL-1.2.12/src/video/SDL_video.c 2007-07-20 07:52:25.000000000 +0200
++++ SDL-1.2.12release/src/video/SDL_video.c 2007-08-17 02:51:52.000000000 +0200
+@@ -75,12 +75,12 @@
+ #if SDL_VIDEO_DRIVER_GAPI
+ &GAPI_bootstrap,
+ #endif
+-#if SDL_VIDEO_DRIVER_WINDIB
+- &WINDIB_bootstrap,
+-#endif
+ #if SDL_VIDEO_DRIVER_DDRAW
+ &DIRECTX_bootstrap,
+ #endif
++#if SDL_VIDEO_DRIVER_WINDIB
++ &WINDIB_bootstrap,
++#endif
+ #if SDL_VIDEO_DRIVER_BWINDOW
+ &BWINDOW_bootstrap,
+ #endif
+diff -ru SDL-1.2.12/src/video/windx5/SDL_dx5events.c SDL-1.2.12release/src/video/windx5/SDL_dx5events.c
+--- SDL-1.2.12/src/video/windx5/SDL_dx5events.c 2007-07-20 07:52:25.000000000 +0200
++++ SDL-1.2.12release/src/video/windx5/SDL_dx5events.c 2007-08-17 02:51:52.000000000 +0200
+@@ -519,7 +519,7 @@
+ case WM_SYSKEYDOWN: {
+ /* Pass syskey to DefWindwoProc (ALT-F4, etc.) */
+ }
+- break;
++// break;
+ case WM_KEYUP:
+ case WM_KEYDOWN: {
+ /* Ignore windows keyboard messages */;
+diff -ru SDL-1.2.12/src/video/windx5/SDL_dx5video.c SDL-1.2.12release/src/video/windx5/SDL_dx5video.c
+--- SDL-1.2.12/src/video/windx5/SDL_dx5video.c 2007-07-20 07:52:25.000000000 +0200
++++ SDL-1.2.12release/src/video/windx5/SDL_dx5video.c 2007-08-17 02:51:52.000000000 +0200
+@@ -1496,7 +1496,7 @@
+ }
+ }
+ dd_surface3 = NULL;
+-#if 0 /* FIXME: enable this when SDL consistently reports lost surfaces */
++#if 1 /* FIXME: enable this when SDL consistently reports lost surfaces */
+ if ( (flags & SDL_HWSURFACE) == SDL_HWSURFACE ) {
+ video->flags |= SDL_HWSURFACE;
+ } else {
diff --git a/src/platform/visualc/Makefile.am b/src/platform/visualc/Makefile.am
new file mode 100644
index 000000000..35f203906
--- /dev/null
+++ b/src/platform/visualc/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = unistd.h config.h ntddscsi.h ntddcdrm.h
diff --git a/src/platform/visualc/config.h b/src/platform/visualc/config.h
new file mode 100644
index 000000000..8b7bab265
--- /dev/null
+++ b/src/platform/visualc/config.h
@@ -0,0 +1,78 @@
+#define VERSION "SVN"
+
+/* Define to 1 to enable internal debugger, requires libcurses */
+#define C_DEBUG 0
+
+/* Define to 1 to enable output=ddraw */
+#define C_DDRAW 1
+
+/* Define to 1 to enable screenshots, requires libpng */
+#define C_SSHOT 1
+
+/* Define to 1 to use opengl display output support */
+#define C_OPENGL 1
+
+/* Define to 1 to enable internal modem support, requires SDL_net */
+#define C_MODEM 1
+
+/* Define to 1 to enable IPX networking support, requires SDL_net */
+#define C_IPX 1
+
+/* Enable some heavy debugging options */
+#define C_HEAVY_DEBUG 0
+
+/* The type of cpu this host has */
+#define C_TARGETCPU X86
+//#define C_TARGETCPU X86_64
+
+/* Define to 1 to use x86 dynamic cpu core */
+#define C_DYNAMIC_X86 1
+
+/* Define to 1 to use recompiling cpu core. Can not be used together with the dynamic-x86 core */
+#define C_DYNREC 0
+
+/* Enable memory function inlining in */
+#define C_CORE_INLINE 0
+
+/* Enable the FPU module, still only for beta testing */
+#define C_FPU 1
+
+/* Define to 1 to use a x86 assembly fpu core */
+#define C_FPU_X86 1
+
+/* Define to 1 to use a unaligned memory access */
+#define C_UNALIGNED_MEMORY 1
+
+/* environ is defined */
+#define ENVIRON_INCLUDED 1
+
+/* environ can be linked */
+#define ENVIRON_LINKED 1
+
+/* Define to 1 if you want serial passthrough support. */
+#define C_DIRECTSERIAL 1
+
+#define GCC_ATTRIBUTE(x) /* attribute not supported */
+#define GCC_UNLIKELY(x) (x)
+#define GCC_LIKELY(x) (x)
+
+#define INLINE __forceinline
+#define DB_FASTCALL __fastcall
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#pragma warning(disable : 4996)
+#endif
+
+typedef double Real64;
+/* The internal types */
+typedef unsigned char Bit8u;
+typedef signed char Bit8s;
+typedef unsigned short Bit16u;
+typedef signed short Bit16s;
+typedef unsigned long Bit32u;
+typedef signed long Bit32s;
+typedef unsigned __int64 Bit64u;
+typedef signed __int64 Bit64s;
+typedef unsigned int Bitu;
+typedef signed int Bits;
+
diff --git a/src/platform/visualc/ntddcdrm.h b/src/platform/visualc/ntddcdrm.h
new file mode 100644
index 000000000..c1ee8c2ab
--- /dev/null
+++ b/src/platform/visualc/ntddcdrm.h
@@ -0,0 +1,320 @@
+/*
+ * ntddcdrm.h
+ *
+ * CDROM IOCTL interface.
+ *
+ * This file is part of the w32api package.
+ *
+ * Contributors:
+ * Created by Casper S. Hornstrup <chorns@users.sourceforge.net>
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef __NTDDCDRM_H
+#define __NTDDCDRM_H
+
+#if __GNUC__ >=3
+#pragma GCC system_header
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#pragma pack(push,4)
+
+#define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM
+
+#define IOCTL_CDROM_CHECK_VERIFY \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_FIND_NEW_DEVICES \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_GET_CONTROL \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x000D, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_GET_DRIVE_GEOMETRY \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0013, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_GET_LAST_SESSION \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x000E, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_GET_VOLUME \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_PAUSE_AUDIO \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_PLAY_AUDIO_MSF \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0006, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_RAW_READ \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x000F, METHOD_OUT_DIRECT, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_READ_Q_CHANNEL \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x000B, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_READ_TOC \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_READ_TOC_EX \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0015, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_RESUME_AUDIO \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_SEEK_AUDIO_MSF \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_SET_VOLUME \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x000A, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_SIMBAD \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x1003, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_STOP_AUDIO \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0002, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+
+#define MAXIMUM_NUMBER_TRACKS 100
+#define MAXIMUM_CDROM_SIZE 804
+#define MINIMUM_CDROM_READ_TOC_EX_SIZE 2
+
+typedef struct _TRACK_DATA {
+ UCHAR Reserved;
+ UCHAR Control : 4;
+ UCHAR Adr : 4;
+ UCHAR TrackNumber;
+ UCHAR Reserved1;
+ UCHAR Address[4];
+} TRACK_DATA, *PTRACK_DATA;
+
+/* CDROM_DISK_DATA.DiskData flags */
+#define CDROM_DISK_AUDIO_TRACK 0x00000001
+#define CDROM_DISK_DATA_TRACK 0x00000002
+
+typedef struct _CDROM_DISK_DATA {
+ ULONG DiskData;
+} CDROM_DISK_DATA, *PCDROM_DISK_DATA;
+
+typedef struct _CDROM_PLAY_AUDIO_MSF {
+ UCHAR StartingM;
+ UCHAR StartingS;
+ UCHAR StartingF;
+ UCHAR EndingM;
+ UCHAR EndingS;
+ UCHAR EndingF;
+} CDROM_PLAY_AUDIO_MSF, *PCDROM_PLAY_AUDIO_MSF;
+
+/* CDROM_READ_TOC_EX.Format constants */
+#define CDROM_READ_TOC_EX_FORMAT_TOC 0x00
+#define CDROM_READ_TOC_EX_FORMAT_SESSION 0x01
+#define CDROM_READ_TOC_EX_FORMAT_FULL_TOC 0x02
+#define CDROM_READ_TOC_EX_FORMAT_PMA 0x03
+#define CDROM_READ_TOC_EX_FORMAT_ATIP 0x04
+#define CDROM_READ_TOC_EX_FORMAT_CDTEXT 0x05
+
+typedef struct _CDROM_READ_TOC_EX {
+ UCHAR Format : 4;
+ UCHAR Reserved1 : 3;
+ UCHAR Msf : 1;
+ UCHAR SessionTrack;
+ UCHAR Reserved2;
+ UCHAR Reserved3;
+} CDROM_READ_TOC_EX, *PCDROM_READ_TOC_EX;
+
+typedef struct _CDROM_SEEK_AUDIO_MSF {
+ UCHAR M;
+ UCHAR S;
+ UCHAR F;
+} CDROM_SEEK_AUDIO_MSF, *PCDROM_SEEK_AUDIO_MSF;
+
+/* CDROM_SUB_Q_DATA_FORMAT.Format constants */
+#define IOCTL_CDROM_SUB_Q_CHANNEL 0x00
+#define IOCTL_CDROM_CURRENT_POSITION 0x01
+#define IOCTL_CDROM_MEDIA_CATALOG 0x02
+#define IOCTL_CDROM_TRACK_ISRC 0x03
+
+typedef struct _CDROM_SUB_Q_DATA_FORMAT {
+ UCHAR Format;
+ UCHAR Track;
+} CDROM_SUB_Q_DATA_FORMAT, *PCDROM_SUB_Q_DATA_FORMAT;
+
+typedef struct _CDROM_TOC {
+ UCHAR Length[2];
+ UCHAR FirstTrack;
+ UCHAR LastTrack;
+ TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS];
+} CDROM_TOC, *PCDROM_TOC;
+
+#define CDROM_TOC_SIZE sizeof(CDROM_TOC)
+
+typedef struct _CDROM_TOC_ATIP_DATA_BLOCK {
+ UCHAR CdrwReferenceSpeed : 3;
+ UCHAR Reserved3 : 1;
+ UCHAR WritePower : 3;
+ UCHAR True1 : 1;
+ UCHAR Reserved4 : 6;
+ UCHAR UnrestrictedUse : 1;
+ UCHAR Reserved5 : 1;
+ UCHAR A3Valid : 1;
+ UCHAR A2Valid : 1;
+ UCHAR A1Valid : 1;
+ UCHAR Reserved6 : 3;
+ UCHAR IsCdrw : 1;
+ UCHAR True2 : 1;
+ UCHAR Reserved7;
+ UCHAR LeadInMsf[3];
+ UCHAR Reserved8;
+ UCHAR LeadOutMsf[3];
+ UCHAR Reserved9;
+ UCHAR A1Values[3];
+ UCHAR Reserved10;
+ UCHAR A2Values[3];
+ UCHAR Reserved11;
+ UCHAR A3Values[3];
+ UCHAR Reserved12;
+} CDROM_TOC_ATIP_DATA_BLOCK, *PCDROM_TOC_ATIP_DATA_BLOCK;
+
+/* CDROM_TOC_CD_TEXT_DATA_BLOCK.PackType constants */
+#define CDROM_CD_TEXT_PACK_ALBUM_NAME 0x80
+#define CDROM_CD_TEXT_PACK_PERFORMER 0x81
+#define CDROM_CD_TEXT_PACK_SONGWRITER 0x82
+#define CDROM_CD_TEXT_PACK_COMPOSER 0x83
+#define CDROM_CD_TEXT_PACK_ARRANGER 0x84
+#define CDROM_CD_TEXT_PACK_MESSAGES 0x85
+#define CDROM_CD_TEXT_PACK_DISC_ID 0x86
+#define CDROM_CD_TEXT_PACK_GENRE 0x87
+#define CDROM_CD_TEXT_PACK_TOC_INFO 0x88
+#define CDROM_CD_TEXT_PACK_TOC_INFO2 0x89
+#define CDROM_CD_TEXT_PACK_UPC_EAN 0x8e
+#define CDROM_CD_TEXT_PACK_SIZE_INFO 0x8f
+
+typedef struct _CDROM_TOC_CD_TEXT_DATA_BLOCK {
+ UCHAR PackType;
+ UCHAR TrackNumber : 7;
+ UCHAR ExtensionFlag : 1;
+ UCHAR SequenceNumber;
+ UCHAR CharacterPosition : 4;
+ UCHAR BlockNumber : 3;
+ UCHAR Unicode : 1;
+ union {
+ UCHAR Text[12];
+ WCHAR WText[6];
+ };
+ UCHAR CRC[2];
+} CDROM_TOC_CD_TEXT_DATA_BLOCK, *PCDROM_TOC_CD_TEXT_DATA_BLOCK;
+
+/* CDROM_TOC_FULL_TOC_DATA_BLOCK.Adr constants */
+#define ADR_NO_MODE_INFORMATION 0x0
+#define ADR_ENCODES_CURRENT_POSITION 0x1
+#define ADR_ENCODES_MEDIA_CATALOG 0x2
+#define ADR_ENCODES_ISRC 0x3
+
+typedef struct _CDROM_TOC_FULL_TOC_DATA_BLOCK {
+ UCHAR SessionNumber;
+ UCHAR Control : 4;
+ UCHAR Adr : 4;
+ UCHAR Reserved1;
+ UCHAR Point;
+ UCHAR MsfExtra[3];
+ UCHAR Zero;
+ UCHAR Msf[3];
+} CDROM_TOC_FULL_TOC_DATA_BLOCK, *PCDROM_TOC_FULL_TOC_DATA_BLOCK;
+
+/* SUB_Q_HEADER.AudioStatus constants */
+#define AUDIO_STATUS_NOT_SUPPORTED 0x00
+#define AUDIO_STATUS_IN_PROGRESS 0x11
+#define AUDIO_STATUS_PAUSED 0x12
+#define AUDIO_STATUS_PLAY_COMPLETE 0x13
+#define AUDIO_STATUS_PLAY_ERROR 0x14
+#define AUDIO_STATUS_NO_STATUS 0x15
+
+typedef struct _SUB_Q_HEADER {
+ UCHAR Reserved;
+ UCHAR AudioStatus;
+ UCHAR DataLength[2];
+} SUB_Q_HEADER, *PSUB_Q_HEADER;
+
+typedef struct _SUB_Q_MEDIA_CATALOG_NUMBER {
+ SUB_Q_HEADER Header;
+ UCHAR FormatCode;
+ UCHAR Reserved[3];
+ UCHAR Reserved1 : 7;
+ UCHAR Mcval :1;
+ UCHAR MediaCatalog[15];
+} SUB_Q_MEDIA_CATALOG_NUMBER, *PSUB_Q_MEDIA_CATALOG_NUMBER;
+
+typedef struct _SUB_Q_TRACK_ISRC {
+ SUB_Q_HEADER Header;
+ UCHAR FormatCode;
+ UCHAR Reserved0;
+ UCHAR Track;
+ UCHAR Reserved1;
+ UCHAR Reserved2 : 7;
+ UCHAR Tcval : 1;
+ UCHAR TrackIsrc[15];
+} SUB_Q_TRACK_ISRC, *PSUB_Q_TRACK_ISRC;
+
+typedef struct _SUB_Q_CURRENT_POSITION {
+ SUB_Q_HEADER Header;
+ UCHAR FormatCode;
+ UCHAR Control : 4;
+ UCHAR ADR : 4;
+ UCHAR TrackNumber;
+ UCHAR IndexNumber;
+ UCHAR AbsoluteAddress[4];
+ UCHAR TrackRelativeAddress[4];
+} SUB_Q_CURRENT_POSITION, *PSUB_Q_CURRENT_POSITION;
+
+typedef union _SUB_Q_CHANNEL_DATA {
+ SUB_Q_CURRENT_POSITION CurrentPosition;
+ SUB_Q_MEDIA_CATALOG_NUMBER MediaCatalog;
+ SUB_Q_TRACK_ISRC TrackIsrc;
+} SUB_Q_CHANNEL_DATA, *PSUB_Q_CHANNEL_DATA;
+
+/* CDROM_AUDIO_CONTROL.LbaFormat constants */
+#define AUDIO_WITH_PREEMPHASIS 0x1
+#define DIGITAL_COPY_PERMITTED 0x2
+#define AUDIO_DATA_TRACK 0x4
+#define TWO_FOUR_CHANNEL_AUDIO 0x8
+
+typedef struct _CDROM_AUDIO_CONTROL {
+ UCHAR LbaFormat;
+ USHORT LogicalBlocksPerSecond;
+} CDROM_AUDIO_CONTROL, *PCDROM_AUDIO_CONTROL;
+
+typedef struct _VOLUME_CONTROL {
+ UCHAR PortVolume[4];
+} VOLUME_CONTROL, *PVOLUME_CONTROL;
+
+typedef enum _TRACK_MODE_TYPE {
+ YellowMode2,
+ XAForm2,
+ CDDA
+} TRACK_MODE_TYPE, *PTRACK_MODE_TYPE;
+
+typedef struct __RAW_READ_INFO {
+ LARGE_INTEGER DiskOffset;
+ ULONG SectorCount;
+ TRACK_MODE_TYPE TrackMode;
+} RAW_READ_INFO, *PRAW_READ_INFO;
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NTDDCDRM_H */
diff --git a/src/platform/visualc/ntddscsi.h b/src/platform/visualc/ntddscsi.h
new file mode 100644
index 000000000..e07132464
--- /dev/null
+++ b/src/platform/visualc/ntddscsi.h
@@ -0,0 +1,174 @@
+/*
+ * ntddscsi.h
+ *
+ * SCSI port IOCTL interface.
+ *
+ * This file is part of the w32api package.
+ *
+ * Contributors:
+ * Created by Casper S. Hornstrup <chorns@users.sourceforge.net>
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef __NTDDSCSI_H
+#define __NTDDSCSI_H
+
+#if __GNUC__ >=3
+#pragma GCC system_header
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#pragma pack(push,4)
+
+//#include "ntddk.h"
+
+#define DD_SCSI_DEVICE_NAME "\\Device\\ScsiPort"
+#define DD_SCSI_DEVICE_NAME_U L"\\Device\\ScsiPort"
+
+#define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER
+
+#define IOCTL_SCSI_GET_INQUIRY_DATA \
+ CTL_CODE(IOCTL_SCSI_BASE, 0x0403, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_SCSI_GET_CAPABILITIES \
+ CTL_CODE(IOCTL_SCSI_BASE, 0x0404, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_SCSI_GET_ADDRESS \
+ CTL_CODE(IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_SCSI_MINIPORT \
+ CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_SCSI_PASS_THROUGH \
+ CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_SCSI_PASS_THROUGH_DIRECT \
+ CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_SCSI_RESCAN_BUS \
+ CTL_CODE(IOCTL_SCSI_BASE, 0x0407, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+
+DEFINE_GUID(ScsiRawInterfaceGuid, 0x53f56309L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+
+DEFINE_GUID(WmiScsiAddressGuid, 0x53f5630fL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+
+typedef struct _SCSI_PASS_THROUGH {
+ USHORT Length;
+ UCHAR ScsiStatus;
+ UCHAR PathId;
+ UCHAR TargetId;
+ UCHAR Lun;
+ UCHAR CdbLength;
+ UCHAR SenseInfoLength;
+ UCHAR DataIn;
+ ULONG DataTransferLength;
+ ULONG TimeOutValue;
+ ULONG_PTR DataBufferOffset;
+ ULONG SenseInfoOffset;
+ UCHAR Cdb[16];
+} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
+
+typedef struct _SCSI_PASS_THROUGH_DIRECT {
+ USHORT Length;
+ UCHAR ScsiStatus;
+ UCHAR PathId;
+ UCHAR TargetId;
+ UCHAR Lun;
+ UCHAR CdbLength;
+ UCHAR SenseInfoLength;
+ UCHAR DataIn;
+ ULONG DataTransferLength;
+ ULONG TimeOutValue;
+ PVOID DataBuffer;
+ ULONG SenseInfoOffset;
+ UCHAR Cdb[16];
+} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT;
+
+typedef struct _SRB_IO_CONTROL {
+ ULONG HeaderLength;
+ UCHAR Signature[8];
+ ULONG Timeout;
+ ULONG ControlCode;
+ ULONG ReturnCode;
+ ULONG Length;
+} SRB_IO_CONTROL, *PSRB_IO_CONTROL;
+
+typedef struct _SCSI_ADDRESS {
+ ULONG Length;
+ UCHAR PortNumber;
+ UCHAR PathId;
+ UCHAR TargetId;
+ UCHAR Lun;
+} SCSI_ADDRESS, *PSCSI_ADDRESS;
+
+typedef struct _SCSI_BUS_DATA {
+ UCHAR NumberOfLogicalUnits;
+ UCHAR InitiatorBusId;
+ ULONG InquiryDataOffset;
+}SCSI_BUS_DATA, *PSCSI_BUS_DATA;
+
+typedef struct _SCSI_ADAPTER_BUS_INFO {
+ UCHAR NumberOfBuses;
+ SCSI_BUS_DATA BusData[1];
+} SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO;
+
+typedef struct _IO_SCSI_CAPABILITIES {
+ ULONG Length;
+ ULONG MaximumTransferLength;
+ ULONG MaximumPhysicalPages;
+ ULONG SupportedAsynchronousEvents;
+ ULONG AlignmentMask;
+ BOOLEAN TaggedQueuing;
+ BOOLEAN AdapterScansDown;
+ BOOLEAN AdapterUsesPio;
+} IO_SCSI_CAPABILITIES, *PIO_SCSI_CAPABILITIES;
+
+typedef struct _SCSI_INQUIRY_DATA {
+ UCHAR PathId;
+ UCHAR TargetId;
+ UCHAR Lun;
+ BOOLEAN DeviceClaimed;
+ ULONG InquiryDataLength;
+ ULONG NextInquiryDataOffset;
+ UCHAR InquiryData[1];
+} SCSI_INQUIRY_DATA, *PSCSI_INQUIRY_DATA;
+
+#define SCSI_IOCTL_DATA_OUT 0
+#define SCSI_IOCTL_DATA_IN 1
+#define SCSI_IOCTL_DATA_UNSPECIFIED 2
+
+/*
+typedef struct _DUMP_POINTERS {
+ PADAPTER_OBJECT AdapterObject;
+ PVOID MappedRegisterBase;
+ PVOID DumpData;
+ PVOID CommonBufferVa;
+ LARGE_INTEGER CommonBufferPa;
+ ULONG CommonBufferSize;
+ BOOLEAN AllocateCommonBuffers;
+ BOOLEAN UseDiskDump;
+ UCHAR Spare1[2];
+ PVOID DeviceObject;
+} DUMP_POINTERS, *PDUMP_POINTERS;
+*/
+#pragma pack(pop)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NTDDSCSI_H */
diff --git a/src/platform/visualc/unistd.h b/src/platform/visualc/unistd.h
new file mode 100644
index 000000000..8f51f7661
--- /dev/null
+++ b/src/platform/visualc/unistd.h
@@ -0,0 +1,10 @@
+/*
+ * This file is part of the Mingw32 package.
+ *
+ * unistd.h maps (roughly) to io.h
+ */
+
+#ifndef __STRICT_ANSI__
+#include <io.h>
+#endif
+
diff --git a/src/shell/Makefile.am b/src/shell/Makefile.am
new file mode 100644
index 000000000..06f8c5f14
--- /dev/null
+++ b/src/shell/Makefile.am
@@ -0,0 +1,4 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libshell.a
+libshell_a_SOURCES = shell.cpp shell_batch.cpp shell_cmds.cpp shell_misc.cpp
diff --git a/src/shell/shell.cpp b/src/shell/shell.cpp
new file mode 100644
index 000000000..88691abbd
--- /dev/null
+++ b/src/shell/shell.cpp
@@ -0,0 +1,766 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include "dosbox.h"
+#include "regs.h"
+#include "control.h"
+#include "shell.h"
+#include "callback.h"
+#include "support.h"
+
+
+Bitu call_shellstop;
+/* Larger scope so shell_del autoexec can use it to
+ * remove things from the environment */
+DOS_Shell * first_shell = 0;
+
+static Bitu shellstop_handler(void) {
+ return CBRET_STOP;
+}
+
+static void SHELL_ProgramStart(Program * * make) {
+ *make = new DOS_Shell;
+}
+//Repeat it with the correct type, could do it in the function below, but this way it should be
+//clear that if the above function is changed, this function might need a change as well.
+static void SHELL_ProgramStart_First_shell(DOS_Shell * * make) {
+ *make = new DOS_Shell;
+}
+
+#define AUTOEXEC_SIZE 4096
+static char autoexec_data[AUTOEXEC_SIZE] = { 0 };
+static std::list<std::string> autoexec_strings;
+typedef std::list<std::string>::iterator auto_it;
+
+void VFILE_Remove(const char *name);
+
+void AutoexecObject::Install(const std::string &in) {
+ if(GCC_UNLIKELY(installed)) E_Exit("autoexec: already created %s",buf.c_str());
+ installed = true;
+ buf = in;
+ autoexec_strings.push_back(buf);
+ this->CreateAutoexec();
+
+ //autoexec.bat is normally created AUTOEXEC_Init.
+ //But if we are already running (first_shell)
+ //we have to update the envirionment to display changes
+
+ if(first_shell) {
+ //create a copy as the string will be modified
+ std::string::size_type n = buf.size();
+ char* buf2 = new char[n + 1];
+ safe_strncpy(buf2, buf.c_str(), n + 1);
+ if((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){
+ char* after_set = buf2 + 4;//move to variable that is being set
+ char* test = strpbrk(after_set,"=");
+ if(!test) {first_shell->SetEnv(after_set,"");return;}
+ *test++ = 0;
+ //If the shell is running/exists update the environment
+ first_shell->SetEnv(after_set,test);
+ }
+ delete [] buf2;
+ }
+}
+
+void AutoexecObject::InstallBefore(const std::string &in) {
+ if(GCC_UNLIKELY(installed)) E_Exit("autoexec: already created %s",buf.c_str());
+ installed = true;
+ buf = in;
+ autoexec_strings.push_front(buf);
+ this->CreateAutoexec();
+}
+
+void AutoexecObject::CreateAutoexec(void) {
+ /* Remove old autoexec.bat if the shell exists */
+ if(first_shell) VFILE_Remove("AUTOEXEC.BAT");
+
+ //Create a new autoexec.bat
+ autoexec_data[0] = 0;
+ size_t auto_len;
+ for(auto_it it = autoexec_strings.begin(); it != autoexec_strings.end(); it++) {
+
+ std::string linecopy = (*it);
+ std::string::size_type offset = 0;
+ //Lets have \r\n as line ends in autoexec.bat.
+ while(offset < linecopy.length()) {
+ std::string::size_type n = linecopy.find("\n",offset);
+ if ( n == std::string::npos ) break;
+ std::string::size_type rn = linecopy.find("\r\n",offset);
+ if ( rn != std::string::npos && rn + 1 == n) {offset = n + 1; continue;}
+ // \n found without matching \r
+ linecopy.replace(n,1,"\r\n");
+ offset = n + 2;
+ }
+
+ auto_len = strlen(autoexec_data);
+ if ((auto_len+linecopy.length() + 3) > AUTOEXEC_SIZE) {
+ E_Exit("SYSTEM:Autoexec.bat file overflow");
+ }
+ sprintf((autoexec_data + auto_len),"%s\r\n",linecopy.c_str());
+ }
+ if (first_shell) VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data));
+}
+
+AutoexecObject::~AutoexecObject(){
+ if(!installed) return;
+
+ // Remove the line from the autoexecbuffer and update environment
+ for(auto_it it = autoexec_strings.begin(); it != autoexec_strings.end(); ) {
+ if ((*it) == buf) {
+ std::string::size_type n = buf.size();
+ char* buf2 = new char[n + 1];
+ safe_strncpy(buf2, buf.c_str(), n + 1);
+ bool stringset = false;
+ // If it's a environment variable remove it from there as well
+ if ((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){
+ char* after_set = buf2 + 4;//move to variable that is being set
+ char* test = strpbrk(after_set,"=");
+ if (!test) continue;
+ *test = 0;
+ stringset = true;
+ //If the shell is running/exists update the environment
+ if (first_shell) first_shell->SetEnv(after_set,"");
+ }
+ delete [] buf2;
+ if (stringset && first_shell && first_shell->bf && first_shell->bf->filename.find("AUTOEXEC.BAT") != std::string::npos) {
+ //Replace entry with spaces if it is a set and from autoexec.bat, as else the location counter will be off.
+ *it = buf.assign(buf.size(),' ');
+ it++;
+ } else {
+ it = autoexec_strings.erase(it);
+ }
+ } else it++;
+ }
+ this->CreateAutoexec();
+}
+
+DOS_Shell::DOS_Shell():Program(){
+ input_handle=STDIN;
+ echo=true;
+ exit=false;
+ bf=0;
+ call=false;
+ completion_start = NULL;
+}
+
+Bitu DOS_Shell::GetRedirection(char *s, char **ifn, char **ofn,bool * append) {
+
+ char * lr=s;
+ char * lw=s;
+ char ch;
+ Bitu num=0;
+ bool quote = false;
+ char* t;
+
+ while ( (ch=*lr++) ) {
+ if(quote && ch != '"') { /* don't parse redirection within quotes. Not perfect yet. Escaped quotes will mess the count up */
+ *lw++ = ch;
+ continue;
+ }
+
+ switch (ch) {
+ case '"':
+ quote = !quote;
+ break;
+ case '>':
+ *append=((*lr)=='>');
+ if (*append) lr++;
+ lr=ltrim(lr);
+ if (*ofn) free(*ofn);
+ *ofn=lr;
+ while (*lr && *lr!=' ' && *lr!='<' && *lr!='|') lr++;
+ //if it ends on a : => remove it.
+ if((*ofn != lr) && (lr[-1] == ':')) lr[-1] = 0;
+// if(*lr && *(lr+1))
+// *lr++=0;
+// else
+// *lr=0;
+ t = (char*)malloc(lr-*ofn+1);
+ safe_strncpy(t,*ofn,lr-*ofn+1);
+ *ofn=t;
+ continue;
+ case '<':
+ if (*ifn) free(*ifn);
+ lr=ltrim(lr);
+ *ifn=lr;
+ while (*lr && *lr!=' ' && *lr!='>' && *lr != '|') lr++;
+ if((*ifn != lr) && (lr[-1] == ':')) lr[-1] = 0;
+// if(*lr && *(lr+1))
+// *lr++=0;
+// else
+// *lr=0;
+ t = (char*)malloc(lr-*ifn+1);
+ safe_strncpy(t,*ifn,lr-*ifn+1);
+ *ifn=t;
+ continue;
+ case '|':
+ ch=0;
+ num++;
+ }
+ *lw++=ch;
+ }
+ *lw=0;
+ return num;
+}
+
+void DOS_Shell::ParseLine(char * line) {
+ LOG(LOG_EXEC,LOG_ERROR)("Parsing command line: %s",line);
+ /* Check for a leading @ */
+ if (line[0] == '@') line[0] = ' ';
+ line = trim(line);
+
+ /* Do redirection and pipe checks */
+
+ char * in = 0;
+ char * out = 0;
+
+ Bit16u dummy,dummy2;
+ Bit32u bigdummy = 0;
+ Bitu num = 0; /* Number of commands in this line */
+ bool append;
+ bool normalstdin = false; /* wether stdin/out are open on start. */
+ bool normalstdout = false; /* Bug: Assumed is they are "con" */
+
+ num = GetRedirection(line,&in, &out,&append);
+ if (num>1) LOG_MSG("SHELL: Multiple command on 1 line not supported");
+ if (in || out) {
+ normalstdin = (psp->GetFileHandle(0) != 0xff);
+ normalstdout = (psp->GetFileHandle(1) != 0xff);
+ }
+ if (in) {
+ if(DOS_OpenFile(in,OPEN_READ,&dummy)) { //Test if file exists
+ DOS_CloseFile(dummy);
+ LOG_MSG("SHELL: Redirect input from %s",in);
+ if(normalstdin) DOS_CloseFile(0); //Close stdin
+ DOS_OpenFile(in,OPEN_READ,&dummy); //Open new stdin
+ }
+ }
+ if (out){
+ LOG_MSG("SHELL: Redirect output to %s",out);
+ if(normalstdout) DOS_CloseFile(1);
+ if(!normalstdin && !in) DOS_OpenFile("con",OPEN_READWRITE,&dummy);
+ bool status = true;
+ /* Create if not exist. Open if exist. Both in read/write mode */
+ if(append) {
+ if( (status = DOS_OpenFile(out,OPEN_READWRITE,&dummy)) ) {
+ DOS_SeekFile(1,&bigdummy,DOS_SEEK_END);
+ } else {
+ status = DOS_CreateFile(out,DOS_ATTR_ARCHIVE,&dummy); //Create if not exists.
+ }
+ } else {
+ status = DOS_OpenFileExtended(out,OPEN_READWRITE,DOS_ATTR_ARCHIVE,0x12,&dummy,&dummy2);
+ }
+
+ if(!status && normalstdout) DOS_OpenFile("con",OPEN_READWRITE,&dummy); //Read only file, open con again
+ if(!normalstdin && !in) DOS_CloseFile(0);
+ }
+ /* Run the actual command */
+ DoCommand(line);
+ /* Restore handles */
+ if(in) {
+ DOS_CloseFile(0);
+ if(normalstdin) DOS_OpenFile("con",OPEN_READWRITE,&dummy);
+ free(in);
+ }
+ if(out) {
+ DOS_CloseFile(1);
+ if(!normalstdin) DOS_OpenFile("con",OPEN_READWRITE,&dummy);
+ if(normalstdout) DOS_OpenFile("con",OPEN_READWRITE,&dummy);
+ if(!normalstdin) DOS_CloseFile(0);
+ free(out);
+ }
+}
+
+
+
+void DOS_Shell::RunInternal(void) {
+ char input_line[CMD_MAXLINE] = {0};
+ while (bf) {
+ if (bf->ReadLine(input_line)) {
+ if (echo) {
+ if (input_line[0] != '@') {
+ ShowPrompt();
+ WriteOut_NoParsing(input_line);
+ WriteOut_NoParsing("\n");
+ }
+ }
+ ParseLine(input_line);
+ if (echo) WriteOut_NoParsing("\n");
+ }
+ }
+}
+
+void DOS_Shell::Run(void) {
+ char input_line[CMD_MAXLINE] = {0};
+ std::string line;
+ if (cmd->FindStringRemainBegin("/C",line)) {
+ strcpy(input_line,line.c_str());
+ char* sep = strpbrk(input_line,"\r\n"); //GTA installer
+ if (sep) *sep = 0;
+ DOS_Shell temp;
+ temp.echo = echo;
+ temp.ParseLine(input_line); //for *.exe *.com |*.bat creates the bf needed by runinternal;
+ temp.RunInternal(); // exits when no bf is found.
+ return;
+ }
+ /* Start a normal shell and check for a first command init */
+ if (cmd->FindString("/INIT",line,true)) {
+ WriteOut(MSG_Get("SHELL_STARTUP_BEGIN"),VERSION);
+#if C_DEBUG
+ WriteOut(MSG_Get("SHELL_STARTUP_DEBUG"));
+#endif
+ if (machine == MCH_CGA) WriteOut(MSG_Get("SHELL_STARTUP_CGA"));
+ if (machine == MCH_HERC) WriteOut(MSG_Get("SHELL_STARTUP_HERC"));
+ WriteOut(MSG_Get("SHELL_STARTUP_END"));
+
+ strcpy(input_line,line.c_str());
+ line.erase();
+ ParseLine(input_line);
+ } else {
+ WriteOut(MSG_Get("SHELL_STARTUP_SUB"),VERSION);
+ }
+ do {
+ if (bf){
+ if(bf->ReadLine(input_line)) {
+ if (echo) {
+ if (input_line[0]!='@') {
+ ShowPrompt();
+ WriteOut_NoParsing(input_line);
+ WriteOut_NoParsing("\n");
+ };
+ };
+ ParseLine(input_line);
+ if (echo) WriteOut("\n");
+ }
+ } else {
+ if (echo) ShowPrompt();
+ InputCommand(input_line);
+ ParseLine(input_line);
+ if (echo && !bf) WriteOut_NoParsing("\n");
+ }
+ } while (!exit);
+}
+
+void DOS_Shell::SyntaxError(void) {
+ WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
+}
+
+class AUTOEXEC:public Module_base {
+private:
+ AutoexecObject autoexec[17];
+ AutoexecObject autoexec_echo;
+public:
+ AUTOEXEC(Section* configuration):Module_base(configuration) {
+ /* Register a virtual AUOEXEC.BAT file */
+ std::string line;
+ Section_line * section=static_cast<Section_line *>(configuration);
+
+ /* Check -securemode switch to disable mount/imgmount/boot after running autoexec.bat */
+ bool secure = control->cmdline->FindExist("-securemode",true);
+
+ /* add stuff from the configfile unless -noautexec or -securemode is specified. */
+ char * extra = const_cast<char*>(section->data.c_str());
+ if (extra && !secure && !control->cmdline->FindExist("-noautoexec",true)) {
+ /* detect if "echo off" is the first line */
+ size_t firstline_length = strcspn(extra,"\r\n");
+ bool echo_off = !strncasecmp(extra,"echo off",8);
+ if (echo_off && firstline_length == 8) extra += 8;
+ else {
+ echo_off = !strncasecmp(extra,"@echo off",9);
+ if (echo_off && firstline_length == 9) extra += 9;
+ else echo_off = false;
+ }
+
+ /* if "echo off" move it to the front of autoexec.bat */
+ if (echo_off) {
+ autoexec_echo.InstallBefore("@echo off");
+ if (*extra == '\r') extra++; //It can point to \0
+ if (*extra == '\n') extra++; //same
+ }
+
+ /* Install the stuff from the configfile if anything left after moving echo off */
+
+ if (*extra) autoexec[0].Install(std::string(extra));
+ }
+
+ /* Check to see for extra command line options to be added (before the command specified on commandline) */
+ /* Maximum of extra commands: 10 */
+ Bitu i = 1;
+ while (control->cmdline->FindString("-c",line,true) && (i <= 11)) {
+#if defined (WIN32) || defined (OS2)
+ //replace single with double quotes so that mount commands can contain spaces
+ for(Bitu temp = 0;temp < line.size();++temp) if(line[temp] == '\'') line[temp]='\"';
+#endif //Linux users can simply use \" in their shell
+ autoexec[i++].Install(line);
+ }
+
+ /* Check for the -exit switch which causes dosbox to when the command on the commandline has finished */
+ bool addexit = control->cmdline->FindExist("-exit",true);
+
+ /* Check for first command being a directory or file */
+ char buffer[CROSS_LEN+1];
+ char orig[CROSS_LEN+1];
+ char cross_filesplit[2] = {CROSS_FILESPLIT , 0};
+
+ Bitu dummy = 1;
+ bool command_found = false;
+ while (control->cmdline->FindCommand(dummy++,line) && !command_found) {
+ struct stat test;
+ if (line.length() > CROSS_LEN) continue;
+ strcpy(buffer,line.c_str());
+ if (stat(buffer,&test)) {
+ if (getcwd(buffer,CROSS_LEN) == NULL) continue;
+ if (strlen(buffer) + line.length() + 1 > CROSS_LEN) continue;
+ strcat(buffer,cross_filesplit);
+ strcat(buffer,line.c_str());
+ if (stat(buffer,&test)) continue;
+ }
+ if (test.st_mode & S_IFDIR) {
+ autoexec[12].Install(std::string("MOUNT C \"") + buffer + "\"");
+ autoexec[13].Install("C:");
+ if(secure) autoexec[14].Install("z:\\config.com -securemode");
+ command_found = true;
+ } else {
+ char* name = strrchr(buffer,CROSS_FILESPLIT);
+ if (!name) { //Only a filename
+ line = buffer;
+ if (getcwd(buffer,CROSS_LEN) == NULL) continue;
+ if (strlen(buffer) + line.length() + 1 > CROSS_LEN) continue;
+ strcat(buffer,cross_filesplit);
+ strcat(buffer,line.c_str());
+ if(stat(buffer,&test)) continue;
+ name = strrchr(buffer,CROSS_FILESPLIT);
+ if(!name) continue;
+ }
+ *name++ = 0;
+ if (access(buffer,F_OK)) continue;
+ autoexec[12].Install(std::string("MOUNT C \"") + buffer + "\"");
+ autoexec[13].Install("C:");
+ /* Save the non-modified filename (so boot and imgmount can use it (long filenames, case sensivitive)) */
+ strcpy(orig,name);
+ upcase(name);
+ if(strstr(name,".BAT") != 0) {
+ if(secure) autoexec[14].Install("z:\\config.com -securemode");
+ /* BATch files are called else exit will not work */
+ autoexec[15].Install(std::string("CALL ") + name);
+ if(addexit) autoexec[16].Install("exit");
+ } else if((strstr(name,".IMG") != 0) || (strstr(name,".IMA") !=0 )) {
+ //No secure mode here as boot is destructive and enabling securemode disables boot
+ /* Boot image files */
+ autoexec[15].Install(std::string("BOOT ") + orig);
+ } else if((strstr(name,".ISO") != 0) || (strstr(name,".CUE") !=0 )) {
+ /* imgmount CD image files */
+ /* securemode gets a different number from the previous branches! */
+ autoexec[14].Install(std::string("IMGMOUNT D \"") + orig + std::string("\" -t iso"));
+ //autoexec[16].Install("D:");
+ if(secure) autoexec[15].Install("z:\\config.com -securemode");
+ /* Makes no sense to exit here */
+ } else {
+ if(secure) autoexec[14].Install("z:\\config.com -securemode");
+ autoexec[15].Install(name);
+ if(addexit) autoexec[16].Install("exit");
+ }
+ command_found = true;
+ }
+ }
+
+ /* Combining -securemode, noautoexec and no parameters leaves you with a lovely Z:\. */
+ if ( !command_found ) {
+ if ( secure ) autoexec[12].Install("z:\\config.com -securemode");
+ }
+ VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data));
+ }
+};
+
+static AUTOEXEC* test;
+
+void AUTOEXEC_Init(Section * sec) {
+ test = new AUTOEXEC(sec);
+}
+
+static Bitu INT2E_Handler(void) {
+ /* Save return address and current process */
+ RealPt save_ret=real_readd(SegValue(ss),reg_sp);
+ Bit16u save_psp=dos.psp();
+
+ /* Set first shell as process and copy command */
+ dos.psp(DOS_FIRST_SHELL);
+ DOS_PSP psp(DOS_FIRST_SHELL);
+ psp.SetCommandTail(RealMakeSeg(ds,reg_si));
+ SegSet16(ss,RealSeg(psp.GetStack()));
+ reg_sp=2046;
+
+ /* Read and fix up command string */
+ CommandTail tail;
+ MEM_BlockRead(PhysMake(dos.psp(),128),&tail,128);
+ if (tail.count<127) tail.buffer[tail.count]=0;
+ else tail.buffer[126]=0;
+ char* crlf=strpbrk(tail.buffer,"\r\n");
+ if (crlf) *crlf=0;
+
+ /* Execute command */
+ if (strlen(tail.buffer)) {
+ DOS_Shell temp;
+ temp.ParseLine(tail.buffer);
+ temp.RunInternal();
+ }
+
+ /* Restore process and "return" to caller */
+ dos.psp(save_psp);
+ SegSet16(cs,RealSeg(save_ret));
+ reg_ip=RealOff(save_ret);
+ reg_ax=0;
+ return CBRET_NONE;
+}
+
+static char const * const path_string="PATH=Z:\\";
+static char const * const comspec_string="COMSPEC=Z:\\COMMAND.COM";
+static char const * const full_name="Z:\\COMMAND.COM";
+static char const * const init_line="/INIT AUTOEXEC.BAT";
+
+void SHELL_Init() {
+ /* Add messages */
+ MSG_Add("SHELL_ILLEGAL_PATH","Illegal Path.\n");
+ MSG_Add("SHELL_CMD_HELP","If you want a list of all supported commands type \033[33;1mhelp /all\033[0m .\nA short list of the most often used commands:\n");
+ MSG_Add("SHELL_CMD_ECHO_ON","ECHO is on.\n");
+ MSG_Add("SHELL_CMD_ECHO_OFF","ECHO is off.\n");
+ MSG_Add("SHELL_ILLEGAL_SWITCH","Illegal switch: %s.\n");
+ MSG_Add("SHELL_MISSING_PARAMETER","Required parameter missing.\n");
+ MSG_Add("SHELL_CMD_CHDIR_ERROR","Unable to change to: %s.\n");
+ MSG_Add("SHELL_CMD_CHDIR_HINT","Hint: To change to different drive type \033[31m%c:\033[0m\n");
+ MSG_Add("SHELL_CMD_CHDIR_HINT_2","directoryname is longer than 8 characters and/or contains spaces.\nTry \033[31mcd %s\033[0m\n");
+ MSG_Add("SHELL_CMD_CHDIR_HINT_3","You are still on drive Z:, change to a mounted drive with \033[31mC:\033[0m.\n");
+ MSG_Add("SHELL_CMD_DATE_HELP","Displays or changes the internal date.\n");
+ MSG_Add("SHELL_CMD_DATE_ERROR","The specified date is not correct.\n");
+ MSG_Add("SHELL_CMD_DATE_DAYS","3SunMonTueWedThuFriSat"); // "2SoMoDiMiDoFrSa"
+ MSG_Add("SHELL_CMD_DATE_NOW","Current date: ");
+ MSG_Add("SHELL_CMD_DATE_SETHLP","Type 'date MM-DD-YYYY' to change.\n");
+ MSG_Add("SHELL_CMD_DATE_FORMAT","M/D/Y");
+ MSG_Add("SHELL_CMD_DATE_HELP_LONG","DATE [[/T] [/H] [/S] | MM-DD-YYYY]\n"\
+ " MM-DD-YYYY: new date to set\n"\
+ " /S: Permanently use host time and date as DOS time\n"\
+ " /F: Switch back to DOSBox internal time (opposite of /S)\n"\
+ " /T: Only display date\n"\
+ " /H: Synchronize with host\n");
+ MSG_Add("SHELL_CMD_TIME_HELP","Displays the internal time.\n");
+ MSG_Add("SHELL_CMD_TIME_NOW","Current time: ");
+ MSG_Add("SHELL_CMD_TIME_HELP_LONG","TIME [/T] [/H]\n"\
+ " /T: Display simple time\n"\
+ " /H: Synchronize with host\n");
+ MSG_Add("SHELL_CMD_MKDIR_ERROR","Unable to make: %s.\n");
+ MSG_Add("SHELL_CMD_RMDIR_ERROR","Unable to remove: %s.\n");
+ MSG_Add("SHELL_CMD_DEL_ERROR","Unable to delete: %s.\n");
+ MSG_Add("SHELL_SYNTAXERROR","The syntax of the command is incorrect.\n");
+ MSG_Add("SHELL_CMD_SET_NOT_SET","Environment variable %s not defined.\n");
+ MSG_Add("SHELL_CMD_SET_OUT_OF_SPACE","Not enough environment space left.\n");
+ MSG_Add("SHELL_CMD_IF_EXIST_MISSING_FILENAME","IF EXIST: Missing filename.\n");
+ MSG_Add("SHELL_CMD_IF_ERRORLEVEL_MISSING_NUMBER","IF ERRORLEVEL: Missing number.\n");
+ MSG_Add("SHELL_CMD_IF_ERRORLEVEL_INVALID_NUMBER","IF ERRORLEVEL: Invalid number.\n");
+ MSG_Add("SHELL_CMD_GOTO_MISSING_LABEL","No label supplied to GOTO command.\n");
+ MSG_Add("SHELL_CMD_GOTO_LABEL_NOT_FOUND","GOTO: Label %s not found.\n");
+ MSG_Add("SHELL_CMD_FILE_NOT_FOUND","File %s not found.\n");
+ MSG_Add("SHELL_CMD_FILE_EXISTS","File %s already exists.\n");
+ MSG_Add("SHELL_CMD_DIR_INTRO","Directory of %s.\n");
+ MSG_Add("SHELL_CMD_DIR_BYTES_USED","%5d File(s) %17s Bytes.\n");
+ MSG_Add("SHELL_CMD_DIR_BYTES_FREE","%5d Dir(s) %17s Bytes free.\n");
+ MSG_Add("SHELL_EXECUTE_DRIVE_NOT_FOUND","Drive %c does not exist!\nYou must \033[31mmount\033[0m it first. Type \033[1;33mintro\033[0m or \033[1;33mintro mount\033[0m for more information.\n");
+ MSG_Add("SHELL_EXECUTE_ILLEGAL_COMMAND","Illegal command: %s.\n");
+ MSG_Add("SHELL_CMD_PAUSE","Press any key to continue.\n");
+ MSG_Add("SHELL_CMD_PAUSE_HELP","Waits for 1 keystroke to continue.\n");
+ MSG_Add("SHELL_CMD_COPY_FAILURE","Copy failure : %s.\n");
+ MSG_Add("SHELL_CMD_COPY_SUCCESS"," %d File(s) copied.\n");
+ MSG_Add("SHELL_CMD_SUBST_NO_REMOVE","Unable to remove, drive not in use.\n");
+ MSG_Add("SHELL_CMD_SUBST_FAILURE","SUBST failed. You either made an error in your commandline or the target drive is already used.\nIt's only possible to use SUBST on Local drives");
+
+ MSG_Add("SHELL_STARTUP_BEGIN",
+ "\033[44;1m\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\n"
+ "\xBA \033[32mWelcome to DOSBox %-8s\033[37m \xBA\n"
+ "\xBA \xBA\n"
+// "\xBA DOSBox runs real and protected mode games. \xBA\n"
+ "\xBA For a short introduction for new users type: \033[33mINTRO\033[37m \xBA\n"
+ "\xBA For supported shell commands type: \033[33mHELP\033[37m \xBA\n"
+ "\xBA \xBA\n"
+ "\xBA To adjust the emulated CPU speed, use \033[31mctrl-F11\033[37m and \033[31mctrl-F12\033[37m. \xBA\n"
+ "\xBA To activate the keymapper \033[31mctrl-F1\033[37m. \xBA\n"
+ "\xBA For more information read the \033[36mREADME\033[37m file in the DOSBox directory. \xBA\n"
+ "\xBA \xBA\n"
+ );
+ MSG_Add("SHELL_STARTUP_CGA","\xBA DOSBox supports Composite CGA mode. \xBA\n"
+ "\xBA Use \033[31mF12\033[37m to set composite output ON, OFF, or AUTO (default). \xBA\n"
+ "\xBA \033[31m(Alt-)F11\033[37m changes hue; \033[31mctrl-alt-F11\033[37m selects early/late CGA model. \xBA\n"
+ "\xBA \xBA\n"
+ );
+ MSG_Add("SHELL_STARTUP_HERC","\xBA Use \033[31mF11\033[37m to cycle through white, amber, and green monochrome color. \xBA\n"
+ "\xBA \xBA\n"
+ );
+ MSG_Add("SHELL_STARTUP_DEBUG",
+ "\xBA Press \033[31malt-Pause\033[37m to enter the debugger or start the exe with \033[33mDEBUG\033[37m. \xBA\n"
+ "\xBA \xBA\n"
+ );
+ MSG_Add("SHELL_STARTUP_END",
+ "\xBA \033[32mHAVE FUN!\033[37m \xBA\n"
+ "\xBA \033[32mThe DOSBox Team \033[33mhttp://www.dosbox.com\033[37m \xBA\n"
+ "\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
+ "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\033[0m\n"
+ //"\n" //Breaks the startup message if you type a mount and a drive change.
+ );
+ MSG_Add("SHELL_STARTUP_SUB","\n\n\033[32;1mDOSBox %s Command Shell\033[0m\n\n");
+ MSG_Add("SHELL_CMD_CHDIR_HELP","Displays/changes the current directory.\n");
+ MSG_Add("SHELL_CMD_CHDIR_HELP_LONG","CHDIR [drive:][path]\n"
+ "CHDIR [..]\n"
+ "CD [drive:][path]\n"
+ "CD [..]\n\n"
+ " .. Specifies that you want to change to the parent directory.\n\n"
+ "Type CD drive: to display the current directory in the specified drive.\n"
+ "Type CD without parameters to display the current drive and directory.\n");
+ MSG_Add("SHELL_CMD_CLS_HELP","Clear screen.\n");
+ MSG_Add("SHELL_CMD_DIR_HELP","Directory View.\n");
+ MSG_Add("SHELL_CMD_ECHO_HELP","Display messages and enable/disable command echoing.\n");
+ MSG_Add("SHELL_CMD_EXIT_HELP","Exit from the shell.\n");
+ MSG_Add("SHELL_CMD_HELP_HELP","Show help.\n");
+ MSG_Add("SHELL_CMD_MKDIR_HELP","Make Directory.\n");
+ MSG_Add("SHELL_CMD_MKDIR_HELP_LONG","MKDIR [drive:][path]\n"
+ "MD [drive:][path]\n");
+ MSG_Add("SHELL_CMD_RMDIR_HELP","Remove Directory.\n");
+ MSG_Add("SHELL_CMD_RMDIR_HELP_LONG","RMDIR [drive:][path]\n"
+ "RD [drive:][path]\n");
+ MSG_Add("SHELL_CMD_SET_HELP","Change environment variables.\n");
+ MSG_Add("SHELL_CMD_IF_HELP","Performs conditional processing in batch programs.\n");
+ MSG_Add("SHELL_CMD_GOTO_HELP","Jump to a labeled line in a batch script.\n");
+ MSG_Add("SHELL_CMD_SHIFT_HELP","Leftshift commandline parameters in a batch script.\n");
+ MSG_Add("SHELL_CMD_TYPE_HELP","Display the contents of a text-file.\n");
+ MSG_Add("SHELL_CMD_TYPE_HELP_LONG","TYPE [drive:][path][filename]\n");
+ MSG_Add("SHELL_CMD_REM_HELP","Add comments in a batch file.\n");
+ MSG_Add("SHELL_CMD_REM_HELP_LONG","REM [comment]\n");
+ MSG_Add("SHELL_CMD_NO_WILD","This is a simple version of the command, no wildcards allowed!\n");
+ MSG_Add("SHELL_CMD_RENAME_HELP","Renames one or more files.\n");
+ MSG_Add("SHELL_CMD_RENAME_HELP_LONG","RENAME [drive:][path]filename1 filename2.\n"
+ "REN [drive:][path]filename1 filename2.\n\n"
+ "Note that you can not specify a new drive or path for your destination file.\n");
+ MSG_Add("SHELL_CMD_DELETE_HELP","Removes one or more files.\n");
+ MSG_Add("SHELL_CMD_COPY_HELP","Copy files.\n");
+ MSG_Add("SHELL_CMD_CALL_HELP","Start a batch file from within another batch file.\n");
+ MSG_Add("SHELL_CMD_SUBST_HELP","Assign an internal directory to a drive.\n");
+ MSG_Add("SHELL_CMD_LOADHIGH_HELP","Loads a program into upper memory (requires xms=true,umb=true).\n");
+ MSG_Add("SHELL_CMD_CHOICE_HELP","Waits for a keypress and sets ERRORLEVEL.\n");
+ MSG_Add("SHELL_CMD_CHOICE_HELP_LONG","CHOICE [/C:choices] [/N] [/S] text\n"
+ " /C[:]choices - Specifies allowable keys. Default is: yn.\n"
+ " /N - Do not display the choices at end of prompt.\n"
+ " /S - Enables case-sensitive choices to be selected.\n"
+ " text - The text to display as a prompt.\n");
+ MSG_Add("SHELL_CMD_ATTRIB_HELP","Does nothing. Provided for compatibility.\n");
+ MSG_Add("SHELL_CMD_PATH_HELP","Provided for compatibility.\n");
+ MSG_Add("SHELL_CMD_VER_HELP","View and set the reported DOS version.\n");
+ MSG_Add("SHELL_CMD_VER_VER","DOSBox version %s. Reported DOS version %d.%02d.\n");
+
+ /* Regular startup */
+ call_shellstop=CALLBACK_Allocate();
+ /* Setup the startup CS:IP to kill the last running machine when exitted */
+ RealPt newcsip=CALLBACK_RealPointer(call_shellstop);
+ SegSet16(cs,RealSeg(newcsip));
+ reg_ip=RealOff(newcsip);
+
+ CALLBACK_Setup(call_shellstop,shellstop_handler,CB_IRET,"shell stop");
+ PROGRAMS_MakeFile("COMMAND.COM",SHELL_ProgramStart);
+
+ /* Now call up the shell for the first time */
+ Bit16u psp_seg=DOS_FIRST_SHELL;
+ Bit16u env_seg=DOS_FIRST_SHELL+19; //DOS_GetMemory(1+(4096/16))+1;
+ Bit16u stack_seg=DOS_GetMemory(2048/16);
+ SegSet16(ss,stack_seg);
+ reg_sp=2046;
+
+ /* Set up int 24 and psp (Telarium games) */
+ real_writeb(psp_seg+16+1,0,0xea); /* far jmp */
+ real_writed(psp_seg+16+1,1,real_readd(0,0x24*4));
+ real_writed(0,0x24*4,((Bit32u)psp_seg<<16) | ((16+1)<<4));
+
+ /* Set up int 23 to "int 20" in the psp. Fixes what.exe */
+ real_writed(0,0x23*4,((Bit32u)psp_seg<<16));
+
+ /* Set up int 2e handler */
+ Bitu call_int2e=CALLBACK_Allocate();
+ RealPt addr_int2e=RealMake(psp_seg+16+1,8);
+ CALLBACK_Setup(call_int2e,&INT2E_Handler,CB_IRET_STI,Real2Phys(addr_int2e),"Shell Int 2e");
+ RealSetVec(0x2e,addr_int2e);
+
+ /* Setup MCBs */
+ DOS_MCB pspmcb((Bit16u)(psp_seg-1));
+ pspmcb.SetPSPSeg(psp_seg); // MCB of the command shell psp
+ pspmcb.SetSize(0x10+2);
+ pspmcb.SetType(0x4d);
+ DOS_MCB envmcb((Bit16u)(env_seg-1));
+ envmcb.SetPSPSeg(psp_seg); // MCB of the command shell environment
+ envmcb.SetSize(DOS_MEM_START-env_seg);
+ envmcb.SetType(0x4d);
+
+ /* Setup environment */
+ PhysPt env_write=PhysMake(env_seg,0);
+ MEM_BlockWrite(env_write,path_string,(Bitu)(strlen(path_string)+1));
+ env_write += (PhysPt)(strlen(path_string)+1);
+ MEM_BlockWrite(env_write,comspec_string,(Bitu)(strlen(comspec_string)+1));
+ env_write += (PhysPt)(strlen(comspec_string)+1);
+ mem_writeb(env_write++,0);
+ mem_writew(env_write,1);
+ env_write+=2;
+ MEM_BlockWrite(env_write,full_name,(Bitu)(strlen(full_name)+1));
+
+ DOS_PSP psp(psp_seg);
+ psp.MakeNew(0);
+ dos.psp(psp_seg);
+
+ /* The start of the filetable in the psp must look like this:
+ * 01 01 01 00 02
+ * In order to achieve this: First open 2 files. Close the first and
+ * duplicate the second (so the entries get 01) */
+ Bit16u dummy=0;
+ DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDIN */
+ DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDOUT */
+ DOS_CloseFile(0); /* Close STDIN */
+ DOS_ForceDuplicateEntry(1,0); /* "new" STDIN */
+ DOS_ForceDuplicateEntry(1,2); /* STDERR */
+ DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDAUX */
+ DOS_OpenFile("PRN",OPEN_READWRITE,&dummy); /* STDPRN */
+
+ psp.SetParent(psp_seg);
+ /* Set the environment */
+ psp.SetEnvironment(env_seg);
+ /* Set the command line for the shell start up */
+ CommandTail tail;
+ tail.count=(Bit8u)strlen(init_line);
+ memset(&tail.buffer,0,127);
+ strcpy(tail.buffer,init_line);
+ MEM_BlockWrite(PhysMake(psp_seg,128),&tail,128);
+
+ /* Setup internal DOS Variables */
+ dos.dta(RealMake(psp_seg,0x80));
+ dos.psp(psp_seg);
+
+
+ SHELL_ProgramStart_First_shell(&first_shell);
+ first_shell->Run();
+ delete first_shell;
+ first_shell = 0;//Make clear that it shouldn't be used anymore
+}
diff --git a/src/shell/shell_batch.cpp b/src/shell/shell_batch.cpp
new file mode 100644
index 000000000..033def6f2
--- /dev/null
+++ b/src/shell/shell_batch.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "shell.h"
+#include "support.h"
+
+BatchFile::BatchFile(DOS_Shell * host,char const * const resolved_name,char const * const entered_name, char const * const cmd_line) {
+ location = 0;
+ prev=host->bf;
+ echo=host->echo;
+ shell=host;
+ char totalname[DOS_PATHLENGTH+4];
+ DOS_Canonicalize(resolved_name,totalname); // Get fullname including drive specificiation
+ cmd = new CommandLine(entered_name,cmd_line);
+ filename = totalname;
+
+ //Test if file is openable
+ if (!DOS_OpenFile(totalname,(DOS_NOT_INHERIT|OPEN_READ),&file_handle)) {
+ //TODO Come up with something better
+ E_Exit("SHELL:Can't open BatchFile %s",totalname);
+ }
+ DOS_CloseFile(file_handle);
+}
+
+BatchFile::~BatchFile() {
+ delete cmd;
+ shell->bf=prev;
+ shell->echo=echo;
+}
+
+bool BatchFile::ReadLine(char * line) {
+ //Open the batchfile and seek to stored postion
+ if (!DOS_OpenFile(filename.c_str(),(DOS_NOT_INHERIT|OPEN_READ),&file_handle)) {
+ LOG(LOG_MISC,LOG_ERROR)("ReadLine Can't open BatchFile %s",filename.c_str());
+ delete this;
+ return false;
+ }
+ DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_SET);
+
+ Bit8u c=0;Bit16u n=1;
+ char temp[CMD_MAXLINE];
+emptyline:
+ char * cmd_write=temp;
+ do {
+ n=1;
+ DOS_ReadFile(file_handle,&c,&n);
+ if (n>0) {
+ /* Why are we filtering this ?
+ * Exclusion list: tab for batch files
+ * escape for ansi
+ * backspace for alien odyssey */
+ if (c>31 || c==0x1b || c=='\t' || c==8) {
+ //Only add it if room for it (and trailing zero) in the buffer, but do the check here instead at the end
+ //So we continue reading till EOL/EOF
+ if (((cmd_write - temp) + 1) < (CMD_MAXLINE - 1))
+ *cmd_write++ = c;
+ }
+ }
+ } while (c!='\n' && n);
+ *cmd_write=0;
+ if (!n && cmd_write==temp) {
+ //Close file and delete bat file
+ DOS_CloseFile(file_handle);
+ delete this;
+ return false;
+ }
+ if (!strlen(temp)) goto emptyline;
+ if (temp[0]==':') goto emptyline;
+
+ /* Now parse the line read from the bat file for % stuff */
+ cmd_write=line;
+ char * cmd_read=temp;
+ while (*cmd_read) {
+ if (*cmd_read == '%') {
+ cmd_read++;
+ if (cmd_read[0] == '%') {
+ cmd_read++;
+ if (((cmd_write - line) + 1) < (CMD_MAXLINE - 1))
+ *cmd_write++ = '%';
+ continue;
+ }
+ if (cmd_read[0] == '0') { /* Handle %0 */
+ const char *file_name = cmd->GetFileName();
+ cmd_read++;
+ size_t name_len = strlen(file_name);
+ if (((cmd_write - line) + name_len) < (CMD_MAXLINE - 1)) {
+ strcpy(cmd_write,file_name);
+ cmd_write += name_len;
+ }
+ continue;
+ }
+ char next = cmd_read[0];
+ if(next > '0' && next <= '9') {
+ /* Handle %1 %2 .. %9 */
+ cmd_read++; //Progress reader
+ next -= '0';
+ if (cmd->GetCount()<(unsigned int)next) continue;
+ std::string word;
+ if (!cmd->FindCommand(next,word)) continue;
+ size_t name_len = strlen(word.c_str());
+ if (((cmd_write - line) + name_len) < (CMD_MAXLINE - 1)) {
+ strcpy(cmd_write,word.c_str());
+ cmd_write += name_len;
+ }
+ continue;
+ } else {
+ /* Not a command line number has to be an environment */
+ char * first = strchr(cmd_read,'%');
+ /* No env afterall. Ignore a single % */
+ if (!first) {/* *cmd_write++ = '%';*/continue;}
+ *first++ = 0;
+ std::string env;
+ if (shell->GetEnvStr(cmd_read,env)) {
+ const char* equals = strchr(env.c_str(),'=');
+ if (!equals) continue;
+ equals++;
+ size_t name_len = strlen(equals);
+ if (((cmd_write - line) + name_len) < (CMD_MAXLINE - 1)) {
+ strcpy(cmd_write,equals);
+ cmd_write += name_len;
+ }
+ }
+ cmd_read = first;
+ }
+ } else {
+ if (((cmd_write - line) + 1) < (CMD_MAXLINE - 1))
+ *cmd_write++ = *cmd_read++;
+ }
+ }
+ *cmd_write = 0;
+ //Store current location and close bat file
+ this->location = 0;
+ DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_CUR);
+ DOS_CloseFile(file_handle);
+ return true;
+}
+
+bool BatchFile::Goto(char * where) {
+ //Open bat file and search for the where string
+ if (!DOS_OpenFile(filename.c_str(),(DOS_NOT_INHERIT|OPEN_READ),&file_handle)) {
+ LOG(LOG_MISC,LOG_ERROR)("SHELL:Goto Can't open BatchFile %s",filename.c_str());
+ delete this;
+ return false;
+ }
+
+ char cmd_buffer[CMD_MAXLINE];
+ char * cmd_write;
+
+ /* Scan till we have a match or return false */
+ Bit8u c;Bit16u n;
+again:
+ cmd_write=cmd_buffer;
+ do {
+ n=1;
+ DOS_ReadFile(file_handle,&c,&n);
+ if (n>0) {
+ if (c>31) {
+ if (((cmd_write - cmd_buffer) + 1) < (CMD_MAXLINE - 1))
+ *cmd_write++ = c;
+ }
+ }
+ } while (c!='\n' && n);
+ *cmd_write++ = 0;
+ char *nospace = trim(cmd_buffer);
+ if (nospace[0] == ':') {
+ nospace++; //Skip :
+ //Strip spaces and = from it.
+ while(*nospace && (isspace(*reinterpret_cast<unsigned char*>(nospace)) || (*nospace == '=')))
+ nospace++;
+
+ //label is until space/=/eol
+ char* const beginlabel = nospace;
+ while(*nospace && !isspace(*reinterpret_cast<unsigned char*>(nospace)) && (*nospace != '='))
+ nospace++;
+
+ *nospace = 0;
+ if (strcasecmp(beginlabel,where)==0) {
+ //Found it! Store location and continue
+ this->location = 0;
+ DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_CUR);
+ DOS_CloseFile(file_handle);
+ return true;
+ }
+
+ }
+ if (!n) {
+ DOS_CloseFile(file_handle);
+ delete this;
+ return false;
+ }
+ goto again;
+ return false;
+}
+
+void BatchFile::Shift(void) {
+ cmd->Shift(1);
+}
diff --git a/src/shell/shell_cmds.cpp b/src/shell/shell_cmds.cpp
new file mode 100644
index 000000000..88cb072c1
--- /dev/null
+++ b/src/shell/shell_cmds.cpp
@@ -0,0 +1,1333 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include "dosbox.h"
+#include "shell.h"
+#include "callback.h"
+#include "regs.h"
+#include "bios.h"
+#include "../dos/drives.h"
+#include "support.h"
+#include "control.h"
+#include <algorithm>
+#include <cstring>
+#include <cctype>
+#include <cstdlib>
+#include <vector>
+#include <string>
+#include <time.h>
+
+static SHELL_Cmd cmd_list[]={
+{ "DIR", 0, &DOS_Shell::CMD_DIR, "SHELL_CMD_DIR_HELP"},
+{ "CHDIR", 1, &DOS_Shell::CMD_CHDIR, "SHELL_CMD_CHDIR_HELP"},
+{ "ATTRIB", 1, &DOS_Shell::CMD_ATTRIB, "SHELL_CMD_ATTRIB_HELP"},
+{ "CALL", 1, &DOS_Shell::CMD_CALL, "SHELL_CMD_CALL_HELP"},
+{ "CD", 0, &DOS_Shell::CMD_CHDIR, "SHELL_CMD_CHDIR_HELP"},
+{ "CHOICE", 1, &DOS_Shell::CMD_CHOICE, "SHELL_CMD_CHOICE_HELP"},
+{ "CLS", 0, &DOS_Shell::CMD_CLS, "SHELL_CMD_CLS_HELP"},
+{ "COPY", 0, &DOS_Shell::CMD_COPY, "SHELL_CMD_COPY_HELP"},
+{ "DATE", 0, &DOS_Shell::CMD_DATE, "SHELL_CMD_DATE_HELP"},
+{ "DEL", 0, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"},
+{ "DELETE", 1, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"},
+{ "ERASE", 1, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"},
+{ "ECHO", 1, &DOS_Shell::CMD_ECHO, "SHELL_CMD_ECHO_HELP"},
+{ "EXIT", 0, &DOS_Shell::CMD_EXIT, "SHELL_CMD_EXIT_HELP"},
+{ "GOTO", 1, &DOS_Shell::CMD_GOTO, "SHELL_CMD_GOTO_HELP"},
+{ "HELP", 1, &DOS_Shell::CMD_HELP, "SHELL_CMD_HELP_HELP"},
+{ "IF", 1, &DOS_Shell::CMD_IF, "SHELL_CMD_IF_HELP"},
+{ "LOADHIGH", 1, &DOS_Shell::CMD_LOADHIGH, "SHELL_CMD_LOADHIGH_HELP"},
+{ "LH", 1, &DOS_Shell::CMD_LOADHIGH, "SHELL_CMD_LOADHIGH_HELP"},
+{ "MKDIR", 1, &DOS_Shell::CMD_MKDIR, "SHELL_CMD_MKDIR_HELP"},
+{ "MD", 0, &DOS_Shell::CMD_MKDIR, "SHELL_CMD_MKDIR_HELP"},
+{ "PATH", 1, &DOS_Shell::CMD_PATH, "SHELL_CMD_PATH_HELP"},
+{ "PAUSE", 1, &DOS_Shell::CMD_PAUSE, "SHELL_CMD_PAUSE_HELP"},
+{ "RMDIR", 1, &DOS_Shell::CMD_RMDIR, "SHELL_CMD_RMDIR_HELP"},
+{ "RD", 0, &DOS_Shell::CMD_RMDIR, "SHELL_CMD_RMDIR_HELP"},
+{ "REM", 1, &DOS_Shell::CMD_REM, "SHELL_CMD_REM_HELP"},
+{ "RENAME", 1, &DOS_Shell::CMD_RENAME, "SHELL_CMD_RENAME_HELP"},
+{ "REN", 0, &DOS_Shell::CMD_RENAME, "SHELL_CMD_RENAME_HELP"},
+{ "SET", 1, &DOS_Shell::CMD_SET, "SHELL_CMD_SET_HELP"},
+{ "SHIFT", 1, &DOS_Shell::CMD_SHIFT, "SHELL_CMD_SHIFT_HELP"},
+{ "SUBST", 1, &DOS_Shell::CMD_SUBST, "SHELL_CMD_SUBST_HELP"},
+{ "TIME", 0, &DOS_Shell::CMD_TIME, "SHELL_CMD_TIME_HELP"},
+{ "TYPE", 0, &DOS_Shell::CMD_TYPE, "SHELL_CMD_TYPE_HELP"},
+{ "VER", 0, &DOS_Shell::CMD_VER, "SHELL_CMD_VER_HELP"},
+{0,0,0,0}
+};
+
+/* support functions */
+static char empty_char = 0;
+static char* empty_string = &empty_char;
+static void StripSpaces(char*&args) {
+ while(args && *args && isspace(*reinterpret_cast<unsigned char*>(args)))
+ args++;
+}
+
+static void StripSpaces(char*&args,char also) {
+ while(args && *args && (isspace(*reinterpret_cast<unsigned char*>(args)) || (*args == also)))
+ args++;
+}
+
+static char* ExpandDot(char*args, char* buffer , size_t bufsize) {
+ if(*args == '.') {
+ if(*(args+1) == 0){
+ safe_strncpy(buffer, "*.*", bufsize);
+ return buffer;
+ }
+ if( (*(args+1) != '.') && (*(args+1) != '\\') ) {
+ buffer[0] = '*';
+ buffer[1] = 0;
+ if (bufsize > 2) strncat(buffer,args,bufsize - 1 /*used buffer portion*/ - 1 /*trailing zero*/ );
+ return buffer;
+ } else
+ safe_strncpy (buffer, args, bufsize);
+ }
+ else safe_strncpy(buffer,args, bufsize);
+ return buffer;
+}
+
+
+
+bool DOS_Shell::CheckConfig(char* cmd_in,char*line) {
+ Section* test = control->GetSectionFromProperty(cmd_in);
+ if(!test) return false;
+ if(line && !line[0]) {
+ std::string val = test->GetPropValue(cmd_in);
+ if(val != NO_SUCH_PROPERTY) WriteOut("%s\n",val.c_str());
+ return true;
+ }
+ char newcom[1024]; newcom[0] = 0; strcpy(newcom,"z:\\config -set ");
+ strcat(newcom,test->GetName()); strcat(newcom," ");
+ strcat(newcom,cmd_in);strcat(newcom,line);
+ DoCommand(newcom);
+ return true;
+}
+
+void DOS_Shell::DoCommand(char * line) {
+/* First split the line into command and arguments */
+ line=trim(line);
+ char cmd_buffer[CMD_MAXLINE];
+ char * cmd_write=cmd_buffer;
+ while (*line) {
+ if (*line == 32) break;
+ if (*line == '/') break;
+ if (*line == '\t') break;
+ if (*line == '=') break;
+// if (*line == ':') break; //This breaks drive switching as that is handled at a later stage.
+ if ((*line == '.') ||(*line == '\\')) { //allow stuff like cd.. and dir.exe cd\kees
+ *cmd_write=0;
+ Bit32u cmd_index=0;
+ while (cmd_list[cmd_index].name) {
+ if (strcasecmp(cmd_list[cmd_index].name,cmd_buffer)==0) {
+ (this->*(cmd_list[cmd_index].handler))(line);
+ return;
+ }
+ cmd_index++;
+ }
+ }
+ *cmd_write++=*line++;
+ }
+ *cmd_write=0;
+ if (strlen(cmd_buffer)==0) return;
+/* Check the internal list */
+ Bit32u cmd_index=0;
+ while (cmd_list[cmd_index].name) {
+ if (strcasecmp(cmd_list[cmd_index].name,cmd_buffer)==0) {
+ (this->*(cmd_list[cmd_index].handler))(line);
+ return;
+ }
+ cmd_index++;
+ }
+/* This isn't an internal command execute it */
+ if(Execute(cmd_buffer,line)) return;
+ if(CheckConfig(cmd_buffer,line)) return;
+ WriteOut(MSG_Get("SHELL_EXECUTE_ILLEGAL_COMMAND"),cmd_buffer);
+}
+
+#define HELP(command) \
+ if (ScanCMDBool(args,"?")) { \
+ WriteOut(MSG_Get("SHELL_CMD_" command "_HELP")); \
+ const char* long_m = MSG_Get("SHELL_CMD_" command "_HELP_LONG"); \
+ WriteOut("\n"); \
+ if(strcmp("Message not Found!\n",long_m)) WriteOut(long_m); \
+ else WriteOut(command "\n"); \
+ return; \
+ }
+
+void DOS_Shell::CMD_CLS(char * args) {
+ HELP("CLS");
+ reg_ax=0x0003;
+ CALLBACK_RunRealInt(0x10);
+}
+
+void DOS_Shell::CMD_DELETE(char * args) {
+ HELP("DELETE");
+ /* Command uses dta so set it to our internal dta */
+ RealPt save_dta=dos.dta();
+ dos.dta(dos.tables.tempdta);
+
+ char * rem=ScanCMDRemain(args);
+ if (rem) {
+ WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
+ return;
+ }
+ /* If delete accept switches mind the space infront of them. See the dir /p code */
+
+ char full[DOS_PATHLENGTH];
+ char buffer[CROSS_LEN];
+ args = ExpandDot(args,buffer, CROSS_LEN);
+ StripSpaces(args);
+ if (!DOS_Canonicalize(args,full)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));return; }
+//TODO Maybe support confirmation for *.* like dos does.
+ bool res=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME);
+ if (!res) {
+ WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),args);
+ dos.dta(save_dta);
+ return;
+ }
+ //end can't be 0, but if it is we'll get a nice crash, who cares :)
+ char * end=strrchr(full,'\\')+1;*end=0;
+ char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u time,date;Bit8u attr;
+ DOS_DTA dta(dos.dta());
+ while (res) {
+ dta.GetResult(name,size,date,time,attr);
+ if (!(attr & (DOS_ATTR_DIRECTORY|DOS_ATTR_READ_ONLY))) {
+ strcpy(end,name);
+ if (!DOS_UnlinkFile(full)) WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),full);
+ }
+ res=DOS_FindNext();
+ }
+ dos.dta(save_dta);
+}
+
+void DOS_Shell::CMD_HELP(char * args){
+ HELP("HELP");
+ bool optall=ScanCMDBool(args,"ALL");
+ /* Print the help */
+ if(!optall) WriteOut(MSG_Get("SHELL_CMD_HELP"));
+ Bit32u cmd_index=0,write_count=0;
+ while (cmd_list[cmd_index].name) {
+ if (optall || !cmd_list[cmd_index].flags) {
+ WriteOut("<\033[34;1m%-8s\033[0m> %s",cmd_list[cmd_index].name,MSG_Get(cmd_list[cmd_index].help));
+ if(!(++write_count%22)) CMD_PAUSE(empty_string);
+ }
+ cmd_index++;
+ }
+}
+
+void DOS_Shell::CMD_RENAME(char * args){
+ HELP("RENAME");
+ StripSpaces(args);
+ if (!*args) {SyntaxError();return;}
+ if ((strchr(args,'*')!=NULL) || (strchr(args,'?')!=NULL) ) { WriteOut(MSG_Get("SHELL_CMD_NO_WILD"));return;}
+ char * arg1=StripWord(args);
+ StripSpaces(args);
+ if (!*args) {SyntaxError();return;}
+ char* slash = strrchr(arg1,'\\');
+ if (slash) {
+ /* If directory specified (crystal caves installer)
+ * rename from c:\X : rename c:\abc.exe abc.shr.
+ * File must appear in C:\
+ * Ren X:\A\B C => ren X:\A\B X:\A\C */
+
+ char dir_source[DOS_PATHLENGTH + 4] = {0}; //not sure if drive portion is included in pathlength
+ //Copy first and then modify, makes GCC happy
+ safe_strncpy(dir_source,arg1,DOS_PATHLENGTH + 4);
+ char* dummy = strrchr(dir_source,'\\');
+ if (!dummy) { //Possible due to length
+ WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
+ return;
+ }
+ dummy++;
+ *dummy = 0;
+
+ //Maybe check args for directory, as I think that isn't allowed
+
+ //dir_source and target are introduced for when we support multiple files being renamed.
+ char target[DOS_PATHLENGTH+CROSS_LEN + 5] = {0};
+ strcpy(target,dir_source);
+ strncat(target,args,CROSS_LEN);
+
+ DOS_Rename(arg1,target);
+
+ } else {
+ DOS_Rename(arg1,args);
+ }
+}
+
+void DOS_Shell::CMD_ECHO(char * args){
+ if (!*args) {
+ if (echo) { WriteOut(MSG_Get("SHELL_CMD_ECHO_ON"));}
+ else { WriteOut(MSG_Get("SHELL_CMD_ECHO_OFF"));}
+ return;
+ }
+ char buffer[512];
+ char* pbuffer = buffer;
+ safe_strncpy(buffer,args,512);
+ StripSpaces(pbuffer);
+ if (strcasecmp(pbuffer,"OFF")==0) {
+ echo=false;
+ return;
+ }
+ if (strcasecmp(pbuffer,"ON")==0) {
+ echo=true;
+ return;
+ }
+ if(strcasecmp(pbuffer,"/?")==0) { HELP("ECHO"); }
+
+ args++;//skip first character. either a slash or dot or space
+ size_t len = strlen(args); //TODO check input of else ook nodig is.
+ if(len && args[len - 1] == '\r') {
+ LOG(LOG_MISC,LOG_WARN)("Hu ? carriage return already present. Is this possible?");
+ WriteOut("%s\n",args);
+ } else WriteOut("%s\r\n",args);
+}
+
+
+void DOS_Shell::CMD_EXIT(char * args) {
+ HELP("EXIT");
+ exit = true;
+}
+
+void DOS_Shell::CMD_CHDIR(char * args) {
+ HELP("CHDIR");
+ StripSpaces(args);
+ Bit8u drive = DOS_GetDefaultDrive()+'A';
+ char dir[DOS_PATHLENGTH];
+ if (!*args) {
+ DOS_GetCurrentDir(0,dir);
+ WriteOut("%c:\\%s\n",drive,dir);
+ } else if(strlen(args) == 2 && args[1]==':') {
+ Bit8u targetdrive = (args[0] | 0x20)-'a' + 1;
+ unsigned char targetdisplay = *reinterpret_cast<unsigned char*>(&args[0]);
+ if(!DOS_GetCurrentDir(targetdrive,dir)) {
+ if(drive == 'Z') {
+ WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(targetdisplay));
+ } else {
+ WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
+ }
+ return;
+ }
+ WriteOut("%c:\\%s\n",toupper(targetdisplay),dir);
+ if(drive == 'Z')
+ WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT"),toupper(targetdisplay));
+ } else if (!DOS_ChangeDir(args)) {
+ /* Changedir failed. Check if the filename is longer then 8 and/or contains spaces */
+
+ std::string temps(args),slashpart;
+ std::string::size_type separator = temps.find_first_of("\\/");
+ if(!separator) {
+ slashpart = temps.substr(0,1);
+ temps.erase(0,1);
+ }
+ separator = temps.find_first_of("\\/");
+ if(separator != std::string::npos) temps.erase(separator);
+ separator = temps.rfind('.');
+ if(separator != std::string::npos) temps.erase(separator);
+ separator = temps.find(' ');
+ if(separator != std::string::npos) {/* Contains spaces */
+ temps.erase(separator);
+ if(temps.size() >6) temps.erase(6);
+ temps += "~1";
+ WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_2"),temps.insert(0,slashpart).c_str());
+ } else if (temps.size()>8) {
+ temps.erase(6);
+ temps += "~1";
+ WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_2"),temps.insert(0,slashpart).c_str());
+ } else {
+ if (drive == 'Z') {
+ WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_3"));
+ } else {
+ WriteOut(MSG_Get("SHELL_CMD_CHDIR_ERROR"),args);
+ }
+ }
+ }
+}
+
+void DOS_Shell::CMD_MKDIR(char * args) {
+ HELP("MKDIR");
+ StripSpaces(args);
+ char * rem=ScanCMDRemain(args);
+ if (rem) {
+ WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
+ return;
+ }
+ if (!DOS_MakeDir(args)) {
+ WriteOut(MSG_Get("SHELL_CMD_MKDIR_ERROR"),args);
+ }
+}
+
+void DOS_Shell::CMD_RMDIR(char * args) {
+ HELP("RMDIR");
+ StripSpaces(args);
+ char * rem=ScanCMDRemain(args);
+ if (rem) {
+ WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
+ return;
+ }
+ if (!DOS_RemoveDir(args)) {
+ WriteOut(MSG_Get("SHELL_CMD_RMDIR_ERROR"),args);
+ }
+}
+
+static void FormatNumber(Bit32u num,char * buf) {
+ Bit32u numm,numk,numb,numg;
+ numb=num % 1000;
+ num/=1000;
+ numk=num % 1000;
+ num/=1000;
+ numm=num % 1000;
+ num/=1000;
+ numg=num;
+ if (numg) {
+ sprintf(buf,"%d,%03d,%03d,%03d",numg,numm,numk,numb);
+ return;
+ };
+ if (numm) {
+ sprintf(buf,"%d,%03d,%03d",numm,numk,numb);
+ return;
+ };
+ if (numk) {
+ sprintf(buf,"%d,%03d",numk,numb);
+ return;
+ };
+ sprintf(buf,"%d",numb);
+}
+
+struct DtaResult {
+ char name[DOS_NAMELENGTH_ASCII];
+ Bit32u size;
+ Bit16u date;
+ Bit16u time;
+ Bit8u attr;
+
+ static bool compareName(const DtaResult &lhs, const DtaResult &rhs) { return strcmp(lhs.name, rhs.name) < 0; }
+ static bool compareExt(const DtaResult &lhs, const DtaResult &rhs) { return strcmp(lhs.getExtension(), rhs.getExtension()) < 0; }
+ static bool compareSize(const DtaResult &lhs, const DtaResult &rhs) { return lhs.size < rhs.size; }
+ static bool compareDate(const DtaResult &lhs, const DtaResult &rhs) { return lhs.date < rhs.date || (lhs.date == rhs.date && lhs.time < rhs.time); }
+
+ const char * getExtension() const {
+ const char * ext = empty_string;
+ if (name[0] != '.') {
+ ext = strrchr(name, '.');
+ if (!ext) ext = empty_string;
+ }
+ return ext;
+ }
+
+};
+
+
+void DOS_Shell::CMD_DIR(char * args) {
+ HELP("DIR");
+ char numformat[16];
+ char path[DOS_PATHLENGTH];
+
+ std::string line;
+ if(GetEnvStr("DIRCMD",line)){
+ std::string::size_type idx = line.find('=');
+ std::string value=line.substr(idx +1 , std::string::npos);
+ line = std::string(args) + " " + value;
+ args=const_cast<char*>(line.c_str());
+ }
+
+ bool optW=ScanCMDBool(args,"W");
+ ScanCMDBool(args,"S");
+ bool optP=ScanCMDBool(args,"P");
+ if (ScanCMDBool(args,"WP") || ScanCMDBool(args,"PW")) {
+ optW=optP=true;
+ }
+ bool optB=ScanCMDBool(args,"B");
+ bool optAD=ScanCMDBool(args,"AD");
+ bool optAminusD=ScanCMDBool(args,"A-D");
+ // Sorting flags
+ bool reverseSort = false;
+ bool optON=ScanCMDBool(args,"ON");
+ if (ScanCMDBool(args,"O-N")) {
+ optON = true;
+ reverseSort = true;
+ }
+ bool optOD=ScanCMDBool(args,"OD");
+ if (ScanCMDBool(args,"O-D")) {
+ optOD = true;
+ reverseSort = true;
+ }
+ bool optOE=ScanCMDBool(args,"OE");
+ if (ScanCMDBool(args,"O-E")) {
+ optOE = true;
+ reverseSort = true;
+ }
+ bool optOS=ScanCMDBool(args,"OS");
+ if (ScanCMDBool(args,"O-S")) {
+ optOS = true;
+ reverseSort = true;
+ }
+ char * rem=ScanCMDRemain(args);
+ if (rem) {
+ WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
+ return;
+ }
+ Bit32u byte_count,file_count,dir_count;
+ Bitu w_count=0;
+ Bitu p_count=0;
+ Bitu w_size = optW?5:1;
+ byte_count=file_count=dir_count=0;
+
+ char buffer[CROSS_LEN];
+ args = trim(args);
+ size_t argLen = strlen(args);
+ if (argLen == 0) {
+ strcpy(args,"*.*"); //no arguments.
+ } else {
+ switch (args[argLen-1])
+ {
+ case '\\': // handle \, C:\, etc.
+ case ':' : // handle C:, etc.
+ strcat(args,"*.*");
+ break;
+ default:
+ break;
+ }
+ }
+ args = ExpandDot(args,buffer,CROSS_LEN);
+
+ if (!strrchr(args,'*') && !strrchr(args,'?')) {
+ Bit16u attribute=0;
+ if(DOS_GetFileAttr(args,&attribute) && (attribute&DOS_ATTR_DIRECTORY) ) {
+ strcat(args,"\\*.*"); // if no wildcard and a directory, get its files
+ }
+ }
+ if (!strrchr(args,'.')) {
+ strcat(args,".*"); // if no extension, get them all
+ }
+
+ /* Make a full path in the args */
+ if (!DOS_Canonicalize(args,path)) {
+ WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
+ return;
+ }
+ *(strrchr(path,'\\')+1)=0;
+ if (!optB) WriteOut(MSG_Get("SHELL_CMD_DIR_INTRO"),path);
+
+ /* Command uses dta so set it to our internal dta */
+ RealPt save_dta=dos.dta();
+ dos.dta(dos.tables.tempdta);
+ DOS_DTA dta(dos.dta());
+ bool ret=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME);
+ if (!ret) {
+ if (!optB) WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
+ dos.dta(save_dta);
+ return;
+ }
+
+ std::vector<DtaResult> results;
+
+ do { /* File name and extension */
+ DtaResult result;
+ dta.GetResult(result.name,result.size,result.date,result.time,result.attr);
+
+ /* Skip non-directories if option AD is present, or skip dirs in case of A-D */
+ if(optAD && !(result.attr&DOS_ATTR_DIRECTORY) ) continue;
+ else if(optAminusD && (result.attr&DOS_ATTR_DIRECTORY) ) continue;
+
+ results.push_back(result);
+
+ } while ( (ret=DOS_FindNext()) );
+
+ if (optON) {
+ // Sort by name
+ std::sort(results.begin(), results.end(), DtaResult::compareName);
+ } else if (optOE) {
+ // Sort by extension
+ std::sort(results.begin(), results.end(), DtaResult::compareExt);
+ } else if (optOD) {
+ // Sort by date
+ std::sort(results.begin(), results.end(), DtaResult::compareDate);
+ } else if (optOS) {
+ // Sort by size
+ std::sort(results.begin(), results.end(), DtaResult::compareSize);
+ }
+ if (reverseSort) {
+ std::reverse(results.begin(), results.end());
+ }
+
+ for (std::vector<DtaResult>::iterator iter = results.begin(); iter != results.end(); iter++) {
+
+ char * name = iter->name;
+ Bit32u size = iter->size;
+ Bit16u date = iter->date;
+ Bit16u time = iter->time;
+ Bit8u attr = iter->attr;
+
+ /* output the file */
+ if (optB) {
+ // this overrides pretty much everything
+ if (strcmp(".",name) && strcmp("..",name)) {
+ WriteOut("%s\n",name);
+ }
+ } else {
+ char * ext = empty_string;
+ if (!optW && (name[0] != '.')) {
+ ext = strrchr(name, '.');
+ if (!ext) ext = empty_string;
+ else *ext++ = 0;
+ }
+ Bit8u day = (Bit8u)(date & 0x001f);
+ Bit8u month = (Bit8u)((date >> 5) & 0x000f);
+ Bit16u year = (Bit16u)((date >> 9) + 1980);
+ Bit8u hour = (Bit8u)((time >> 5 ) >> 6);
+ Bit8u minute = (Bit8u)((time >> 5) & 0x003f);
+
+ if (attr & DOS_ATTR_DIRECTORY) {
+ if (optW) {
+ WriteOut("[%s]",name);
+ size_t namelen = strlen(name);
+ if (namelen <= 14) {
+ for (size_t i=14-namelen;i>0;i--) WriteOut(" ");
+ }
+ } else {
+ WriteOut("%-8s %-3s %-16s %02d-%02d-%04d %2d:%02d\n",name,ext,"<DIR>",day,month,year,hour,minute);
+ }
+ dir_count++;
+ } else {
+ if (optW) {
+ WriteOut("%-16s",name);
+ } else {
+ FormatNumber(size,numformat);
+ WriteOut("%-8s %-3s %16s %02d-%02d-%04d %2d:%02d\n",name,ext,numformat,day,month,year,hour,minute);
+ }
+ file_count++;
+ byte_count+=size;
+ }
+ if (optW) {
+ w_count++;
+ }
+ }
+ if (optP && !(++p_count%(22*w_size))) {
+ CMD_PAUSE(empty_string);
+ }
+ }
+
+
+ if (optW) {
+ if (w_count%5) WriteOut("\n");
+ }
+ if (!optB) {
+ /* Show the summary of results */
+ FormatNumber(byte_count,numformat);
+ WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_USED"),file_count,numformat);
+ Bit8u drive=dta.GetSearchDrive();
+ //TODO Free Space
+ Bitu free_space=1024*1024*100;
+ if (Drives[drive]) {
+ Bit16u bytes_sector;Bit8u sectors_cluster;Bit16u total_clusters;Bit16u free_clusters;
+ Drives[drive]->AllocationInfo(&bytes_sector,&sectors_cluster,&total_clusters,&free_clusters);
+ free_space=bytes_sector*sectors_cluster*free_clusters;
+ }
+ FormatNumber(free_space,numformat);
+ WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_FREE"),dir_count,numformat);
+ }
+ dos.dta(save_dta);
+}
+
+struct copysource {
+ std::string filename;
+ bool concat;
+ copysource(std::string filein,bool concatin):
+ filename(filein),concat(concatin){ };
+ copysource():filename(""),concat(false){ };
+};
+
+
+void DOS_Shell::CMD_COPY(char * args) {
+ HELP("COPY");
+ static char defaulttarget[] = ".";
+ StripSpaces(args);
+ /* Command uses dta so set it to our internal dta */
+ RealPt save_dta=dos.dta();
+ dos.dta(dos.tables.tempdta);
+ DOS_DTA dta(dos.dta());
+ Bit32u size;Bit16u date;Bit16u time;Bit8u attr;
+ char name[DOS_NAMELENGTH_ASCII];
+ std::vector<copysource> sources;
+ // ignore /b and /t switches: always copy binary
+ while(ScanCMDBool(args,"B")) ;
+ while(ScanCMDBool(args,"T")) ; //Shouldn't this be A ?
+ while(ScanCMDBool(args,"A")) ;
+ ScanCMDBool(args,"Y");
+ ScanCMDBool(args,"-Y");
+ ScanCMDBool(args,"V");
+
+ char * rem=ScanCMDRemain(args);
+ if (rem) {
+ WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
+ dos.dta(save_dta);
+ return;
+ }
+ // Gather all sources (extension to copy more then 1 file specified at command line)
+ // Concatenating files go as follows: All parts except for the last bear the concat flag.
+ // This construction allows them to be counted (only the non concat set)
+ char* source_p = NULL;
+ char source_x[DOS_PATHLENGTH+CROSS_LEN];
+ while ( (source_p = StripWord(args)) && *source_p ) {
+ do {
+ char* plus = strchr(source_p,'+');
+ // If StripWord() previously cut at a space before a plus then
+ // set concatenate flag on last source and remove leading plus.
+ if (plus == source_p && sources.size()) {
+ sources[sources.size()-1].concat = true;
+ // If spaces also followed plus then item is only a plus.
+ if (strlen(++source_p)==0) break;
+ plus = strchr(source_p,'+');
+ }
+ if (plus) *plus++ = 0;
+ safe_strncpy(source_x,source_p,CROSS_LEN);
+ bool has_drive_spec = false;
+ size_t source_x_len = strlen(source_x);
+ if (source_x_len>0) {
+ if (source_x[source_x_len-1]==':') has_drive_spec = true;
+ }
+ if (!has_drive_spec && !strpbrk(source_p,"*?") ) { //doubt that fu*\*.* is valid
+ if (DOS_FindFirst(source_p,0xffff & ~DOS_ATTR_VOLUME)) {
+ dta.GetResult(name,size,date,time,attr);
+ if (attr & DOS_ATTR_DIRECTORY)
+ strcat(source_x,"\\*.*");
+ }
+ }
+ sources.push_back(copysource(source_x,(plus)?true:false));
+ source_p = plus;
+ } while(source_p && *source_p);
+ }
+ // At least one source has to be there
+ if (!sources.size() || !sources[0].filename.size()) {
+ WriteOut(MSG_Get("SHELL_MISSING_PARAMETER"));
+ dos.dta(save_dta);
+ return;
+ };
+
+ copysource target;
+ // If more then one object exists and last target is not part of a
+ // concat sequence then make it the target.
+ if(sources.size()>1 && !sources[sources.size()-2].concat){
+ target = sources.back();
+ sources.pop_back();
+ }
+ //If no target => default target with concat flag true to detect a+b+c
+ if(target.filename.size() == 0) target = copysource(defaulttarget,true);
+
+ copysource oldsource;
+ copysource source;
+ Bit32u count = 0;
+ while(sources.size()) {
+ /* Get next source item and keep track of old source for concat start end */
+ oldsource = source;
+ source = sources[0];
+ sources.erase(sources.begin());
+
+ //Skip first file if doing a+b+c. Set target to first file
+ if(!oldsource.concat && source.concat && target.concat) {
+ target = source;
+ continue;
+ }
+
+ /* Make a full path in the args */
+ char pathSource[DOS_PATHLENGTH];
+ char pathTarget[DOS_PATHLENGTH];
+
+ if (!DOS_Canonicalize(const_cast<char*>(source.filename.c_str()),pathSource)) {
+ WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
+ dos.dta(save_dta);
+ return;
+ }
+ // cut search pattern
+ char* pos = strrchr(pathSource,'\\');
+ if (pos) *(pos+1) = 0;
+
+ if (!DOS_Canonicalize(const_cast<char*>(target.filename.c_str()),pathTarget)) {
+ WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
+ dos.dta(save_dta);
+ return;
+ }
+ char* temp = strstr(pathTarget,"*.*");
+ if(temp) *temp = 0;//strip off *.* from target
+
+ // add '\\' if target is a directory
+ bool target_is_file = true;
+ if (pathTarget[strlen(pathTarget)-1]!='\\') {
+ if (DOS_FindFirst(pathTarget,0xffff & ~DOS_ATTR_VOLUME)) {
+ dta.GetResult(name,size,date,time,attr);
+ if (attr & DOS_ATTR_DIRECTORY) {
+ strcat(pathTarget,"\\");
+ target_is_file = false;
+ }
+ }
+ } else target_is_file = false;
+
+ //Find first sourcefile
+ bool ret = DOS_FindFirst(const_cast<char*>(source.filename.c_str()),0xffff & ~DOS_ATTR_VOLUME);
+ if (!ret) {
+ WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),const_cast<char*>(source.filename.c_str()));
+ dos.dta(save_dta);
+ return;
+ }
+
+ Bit16u sourceHandle,targetHandle;
+ char nameTarget[DOS_PATHLENGTH];
+ char nameSource[DOS_PATHLENGTH];
+
+ bool second_file_of_current_source = false;
+ while (ret) {
+ dta.GetResult(name,size,date,time,attr);
+
+ if ((attr & DOS_ATTR_DIRECTORY)==0) {
+ strcpy(nameSource,pathSource);
+ strcat(nameSource,name);
+ // Open Source
+ if (DOS_OpenFile(nameSource,0,&sourceHandle)) {
+ // Create Target or open it if in concat mode
+ strcpy(nameTarget,pathTarget);
+ if (nameTarget[strlen(nameTarget)-1]=='\\') strcat(nameTarget,name);
+
+ //Special variable to ensure that copy * a_file, where a_file is not a directory concats.
+ bool special = second_file_of_current_source && target_is_file;
+ second_file_of_current_source = true;
+ if (special) oldsource.concat = true;
+ //Don't create a new file when in concat mode
+ if (oldsource.concat || DOS_CreateFile(nameTarget,0,&targetHandle)) {
+ Bit32u dummy=0;
+ //In concat mode. Open the target and seek to the eof
+ if (!oldsource.concat || (DOS_OpenFile(nameTarget,OPEN_READWRITE,&targetHandle) &&
+ DOS_SeekFile(targetHandle,&dummy,DOS_SEEK_END))) {
+ // Copy
+ static Bit8u buffer[0x8000]; // static, otherwise stack overflow possible.
+ bool failed = false;
+ Bit16u toread = 0x8000;
+ do {
+ failed |= DOS_ReadFile(sourceHandle,buffer,&toread);
+ failed |= DOS_WriteFile(targetHandle,buffer,&toread);
+ } while (toread==0x8000);
+ failed |= DOS_CloseFile(sourceHandle);
+ failed |= DOS_CloseFile(targetHandle);
+ WriteOut(" %s\n",name);
+ if(!source.concat && !special) count++; //Only count concat files once
+ } else {
+ DOS_CloseFile(sourceHandle);
+ WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(target.filename.c_str()));
+ }
+ } else {
+ DOS_CloseFile(sourceHandle);
+ WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(target.filename.c_str()));
+ }
+ } else WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(source.filename.c_str()));
+ };
+ //On to the next file if the previous one wasn't a device
+ if ((attr&DOS_ATTR_DEVICE) == 0) ret = DOS_FindNext();
+ else ret = false;
+ };
+ }
+
+ WriteOut(MSG_Get("SHELL_CMD_COPY_SUCCESS"),count);
+ dos.dta(save_dta);
+}
+
+void DOS_Shell::CMD_SET(char * args) {
+ HELP("SET");
+ StripSpaces(args);
+ std::string line;
+ if (!*args) {
+ /* No command line show all environment lines */
+ Bitu count=GetEnvCount();
+ for (Bitu a=0;a<count;a++) {
+ if (GetEnvNum(a,line)) WriteOut("%s\n",line.c_str());
+ }
+ return;
+ }
+ //There are args:
+ char * pcheck = args;
+ while ( *pcheck && (*pcheck == ' ' || *pcheck == '\t')) pcheck++;
+ if (*pcheck && strlen(pcheck) >3 && (strncasecmp(pcheck,"/p ",3) == 0)) E_Exit("Set /P is not supported. Use Choice!");
+
+ char * p=strpbrk(args, "=");
+ if (!p) {
+ if (!GetEnvStr(args,line)) WriteOut(MSG_Get("SHELL_CMD_SET_NOT_SET"),args);
+ WriteOut("%s\n",line.c_str());
+ } else {
+ *p++=0;
+ /* parse p for envirionment variables */
+ char parsed[CMD_MAXLINE];
+ char* p_parsed = parsed;
+ while(*p) {
+ if(*p != '%') *p_parsed++ = *p++; //Just add it (most likely path)
+ else if( *(p+1) == '%') {
+ *p_parsed++ = '%'; p += 2; //%% => %
+ } else {
+ char * second = strchr(++p,'%');
+ if (!second) continue;
+ *second++ = 0;
+ std::string temp;
+ if (GetEnvStr(p,temp)) {
+ std::string::size_type equals = temp.find('=');
+ if (equals == std::string::npos) continue;
+ strcpy(p_parsed,temp.substr(equals+1).c_str());
+ p_parsed += strlen(p_parsed);
+ }
+ p = second;
+ }
+ }
+ *p_parsed = 0;
+ /* Try setting the variable */
+ if (!SetEnv(args,parsed)) {
+ WriteOut(MSG_Get("SHELL_CMD_SET_OUT_OF_SPACE"));
+ }
+ }
+}
+
+void DOS_Shell::CMD_IF(char * args) {
+ HELP("IF");
+ StripSpaces(args,'=');
+ bool has_not=false;
+
+ while (strncasecmp(args,"NOT",3) == 0) {
+ if (!isspace(*reinterpret_cast<unsigned char*>(&args[3])) && (args[3] != '=')) break;
+ args += 3; //skip text
+ //skip more spaces
+ StripSpaces(args,'=');
+ has_not = !has_not;
+ }
+
+ if(strncasecmp(args,"ERRORLEVEL",10) == 0) {
+ args += 10; //skip text
+ //Strip spaces and ==
+ StripSpaces(args,'=');
+ char* word = StripWord(args);
+ if(!isdigit(*word)) {
+ WriteOut(MSG_Get("SHELL_CMD_IF_ERRORLEVEL_MISSING_NUMBER"));
+ return;
+ }
+
+ Bit8u n = 0;
+ do n = n * 10 + (*word - '0');
+ while (isdigit(*++word));
+ if(*word && !isspace(*word)) {
+ WriteOut(MSG_Get("SHELL_CMD_IF_ERRORLEVEL_INVALID_NUMBER"));
+ return;
+ }
+ /* Read the error code from DOS */
+ if ((dos.return_code>=n) ==(!has_not)) DoCommand(args);
+ return;
+ }
+
+ if(strncasecmp(args,"EXIST ",6) == 0) {
+ args += 6; //Skip text
+ StripSpaces(args);
+ char* word = StripWord(args);
+ if (!*word) {
+ WriteOut(MSG_Get("SHELL_CMD_IF_EXIST_MISSING_FILENAME"));
+ return;
+ }
+
+ { /* DOS_FindFirst uses dta so set it to our internal dta */
+ RealPt save_dta=dos.dta();
+ dos.dta(dos.tables.tempdta);
+ bool ret=DOS_FindFirst(word,0xffff & ~DOS_ATTR_VOLUME);
+ dos.dta(save_dta);
+ if (ret==(!has_not)) DoCommand(args);
+ }
+ return;
+ }
+
+ /* Normal if string compare */
+
+ char* word1 = args;
+ // first word is until space or =
+ while (*args && !isspace(*reinterpret_cast<unsigned char*>(args)) && (*args != '='))
+ args++;
+ char* end_word1 = args;
+
+ // scan for =
+ while (*args && (*args != '='))
+ args++;
+ // check for ==
+ if ((*args==0) || (args[1] != '=')) {
+ SyntaxError();
+ return;
+ }
+ args += 2;
+ StripSpaces(args,'=');
+
+ char* word2 = args;
+ // second word is until space or =
+ while (*args && !isspace(*reinterpret_cast<unsigned char*>(args)) && (*args != '='))
+ args++;
+
+ if (*args) {
+ *end_word1 = 0; // mark end of first word
+ *args++ = 0; // mark end of second word
+ StripSpaces(args,'=');
+
+ if ((strcmp(word1,word2)==0)==(!has_not)) DoCommand(args);
+ }
+}
+
+void DOS_Shell::CMD_GOTO(char * args) {
+ HELP("GOTO");
+ StripSpaces(args);
+ if (!bf) return;
+ if (*args &&(*args==':')) args++;
+ //label ends at the first space
+ char* non_space = args;
+ while (*non_space) {
+ if((*non_space == ' ') || (*non_space == '\t'))
+ *non_space = 0;
+ else non_space++;
+ }
+ if (!*args) {
+ WriteOut(MSG_Get("SHELL_CMD_GOTO_MISSING_LABEL"));
+ return;
+ }
+ if (!bf->Goto(args)) {
+ WriteOut(MSG_Get("SHELL_CMD_GOTO_LABEL_NOT_FOUND"),args);
+ return;
+ }
+}
+
+void DOS_Shell::CMD_SHIFT(char * args ) {
+ HELP("SHIFT");
+ if(bf) bf->Shift();
+}
+
+void DOS_Shell::CMD_TYPE(char * args) {
+ HELP("TYPE");
+ StripSpaces(args);
+ if (!*args) {
+ WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
+ return;
+ }
+ Bit16u handle;
+ char * word;
+nextfile:
+ word=StripWord(args);
+ if (!DOS_OpenFile(word,0,&handle)) {
+ WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),word);
+ return;
+ }
+ Bit16u n;Bit8u c;
+ do {
+ n=1;
+ DOS_ReadFile(handle,&c,&n);
+ if (c==0x1a) break; // stop at EOF
+ DOS_WriteFile(STDOUT,&c,&n);
+ } while (n);
+ DOS_CloseFile(handle);
+ if (*args) goto nextfile;
+}
+
+void DOS_Shell::CMD_REM(char * args) {
+ HELP("REM");
+}
+
+void DOS_Shell::CMD_PAUSE(char * args){
+ HELP("PAUSE");
+ WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
+ Bit8u c;Bit16u n=1;
+ DOS_ReadFile(STDIN,&c,&n);
+ if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
+}
+
+void DOS_Shell::CMD_CALL(char * args){
+ HELP("CALL");
+ this->call=true; /* else the old batchfile will be closed first */
+ this->ParseLine(args);
+ this->call=false;
+}
+
+void DOS_Shell::CMD_DATE(char * args) {
+ HELP("DATE");
+ if(ScanCMDBool(args,"H")) {
+ // synchronize date with host parameter
+ time_t curtime;
+ struct tm *loctime;
+ curtime = time (NULL);
+ loctime = localtime (&curtime);
+
+ reg_cx = loctime->tm_year+1900;
+ reg_dh = loctime->tm_mon+1;
+ reg_dl = loctime->tm_mday;
+
+ reg_ah=0x2b; // set system date
+ CALLBACK_RunRealInt(0x21);
+ return;
+ }
+ // check if a date was passed in command line
+ Bit32u newday,newmonth,newyear;
+ if(sscanf(args,"%u-%u-%u",&newmonth,&newday,&newyear)==3) {
+ reg_cx = static_cast<Bit16u>(newyear);
+ reg_dh = static_cast<Bit8u>(newmonth);
+ reg_dl = static_cast<Bit8u>(newday);
+
+ reg_ah=0x2b; // set system date
+ CALLBACK_RunRealInt(0x21);
+ if(reg_al==0xff) WriteOut(MSG_Get("SHELL_CMD_DATE_ERROR"));
+ return;
+ }
+ // display the current date
+ reg_ah=0x2a; // get system date
+ CALLBACK_RunRealInt(0x21);
+
+ const char* datestring = MSG_Get("SHELL_CMD_DATE_DAYS");
+ Bit32u length;
+ char day[6] = {0};
+ if(sscanf(datestring,"%u",&length) && (length<5) && (strlen(datestring)==(length*7+1))) {
+ // date string appears valid
+ for(Bit32u i = 0; i < length; i++) day[i] = datestring[reg_al*length+1+i];
+ }
+ bool dateonly = ScanCMDBool(args,"T");
+ if(!dateonly) WriteOut(MSG_Get("SHELL_CMD_DATE_NOW"));
+
+ const char* formatstring = MSG_Get("SHELL_CMD_DATE_FORMAT");
+ if(strlen(formatstring)!=5) return;
+ char buffer[15] = {0};
+ Bitu bufferptr=0;
+ for(Bitu i = 0; i < 5; i++) {
+ if(i==1 || i==3) {
+ buffer[bufferptr] = formatstring[i];
+ bufferptr++;
+ } else {
+ if(formatstring[i]=='M') bufferptr += sprintf(buffer+bufferptr,"%02u",(Bit8u) reg_dh);
+ if(formatstring[i]=='D') bufferptr += sprintf(buffer+bufferptr,"%02u",(Bit8u) reg_dl);
+ if(formatstring[i]=='Y') bufferptr += sprintf(buffer+bufferptr,"%04u",(Bit16u) reg_cx);
+ }
+ }
+ WriteOut("%s %s\n",day, buffer);
+ if(!dateonly) WriteOut(MSG_Get("SHELL_CMD_DATE_SETHLP"));
+};
+
+void DOS_Shell::CMD_TIME(char * args) {
+ HELP("TIME");
+ if(ScanCMDBool(args,"H")) {
+ // synchronize time with host parameter
+ time_t curtime;
+ struct tm *loctime;
+ curtime = time (NULL);
+ loctime = localtime (&curtime);
+
+ //reg_cx = loctime->;
+ //reg_dh = loctime->;
+ //reg_dl = loctime->;
+
+ // reg_ah=0x2d; // set system time TODO
+ // CALLBACK_RunRealInt(0x21);
+
+ Bit32u ticks=(Bit32u)(((double)(loctime->tm_hour*3600+
+ loctime->tm_min*60+
+ loctime->tm_sec))*18.206481481);
+ mem_writed(BIOS_TIMER,ticks);
+ return;
+ }
+ bool timeonly = ScanCMDBool(args,"T");
+
+ reg_ah=0x2c; // get system time
+ CALLBACK_RunRealInt(0x21);
+/*
+ reg_dl= // 1/100 seconds
+ reg_dh= // seconds
+ reg_cl= // minutes
+ reg_ch= // hours
+*/
+ if(timeonly) {
+ WriteOut("%2u:%02u\n",reg_ch,reg_cl);
+ } else {
+ WriteOut(MSG_Get("SHELL_CMD_TIME_NOW"));
+ WriteOut("%2u:%02u:%02u,%02u\n",reg_ch,reg_cl,reg_dh,reg_dl);
+ }
+};
+
+void DOS_Shell::CMD_SUBST (char * args) {
+/* If more that one type can be substed think of something else
+ * E.g. make basedir member dos_drive instead of localdrive
+ */
+ HELP("SUBST");
+ localDrive* ldp=0;
+ char mountstring[DOS_PATHLENGTH+CROSS_LEN+20];
+ char temp_str[2] = { 0,0 };
+ try {
+ strcpy(mountstring,"MOUNT ");
+ StripSpaces(args);
+ std::string arg;
+ CommandLine command(0,args);
+
+ if (command.GetCount() != 2) throw 0 ;
+
+ command.FindCommand(1,arg);
+ if( (arg.size()>1) && arg[1] !=':') throw(0);
+ temp_str[0]=(char)toupper(args[0]);
+ command.FindCommand(2,arg);
+ if((arg=="/D") || (arg=="/d")) {
+ if(!Drives[temp_str[0]-'A'] ) throw 1; //targetdrive not in use
+ strcat(mountstring,"-u ");
+ strcat(mountstring,temp_str);
+ this->ParseLine(mountstring);
+ return;
+ }
+ if(Drives[temp_str[0]-'A'] ) throw 0; //targetdrive in use
+ strcat(mountstring,temp_str);
+ strcat(mountstring," ");
+
+ Bit8u drive;char fulldir[DOS_PATHLENGTH];
+ if (!DOS_MakeName(const_cast<char*>(arg.c_str()),fulldir,&drive)) throw 0;
+
+ if( ( ldp=dynamic_cast<localDrive*>(Drives[drive])) == 0 ) throw 0;
+ char newname[CROSS_LEN];
+ strcpy(newname, ldp->basedir);
+ strcat(newname,fulldir);
+ CROSS_FILENAME(newname);
+ ldp->dirCache.ExpandName(newname);
+ strcat(mountstring,"\"");
+ strcat(mountstring, newname);
+ strcat(mountstring,"\"");
+ this->ParseLine(mountstring);
+ }
+ catch(int a){
+ if(a == 0) {
+ WriteOut(MSG_Get("SHELL_CMD_SUBST_FAILURE"));
+ } else {
+ WriteOut(MSG_Get("SHELL_CMD_SUBST_NO_REMOVE"));
+ }
+ return;
+ }
+ catch(...) { //dynamic cast failed =>so no localdrive
+ WriteOut(MSG_Get("SHELL_CMD_SUBST_FAILURE"));
+ return;
+ }
+
+ return;
+}
+
+void DOS_Shell::CMD_LOADHIGH(char *args){
+ HELP("LOADHIGH");
+ Bit16u umb_start=dos_infoblock.GetStartOfUMBChain();
+ Bit8u umb_flag=dos_infoblock.GetUMBChainState();
+ Bit8u old_memstrat=(Bit8u)(DOS_GetMemAllocStrategy()&0xff);
+ if (umb_start==0x9fff) {
+ if ((umb_flag&1)==0) DOS_LinkUMBsToMemChain(1);
+ DOS_SetMemAllocStrategy(0x80); // search in UMBs first
+ this->ParseLine(args);
+ Bit8u current_umb_flag=dos_infoblock.GetUMBChainState();
+ if ((current_umb_flag&1)!=(umb_flag&1)) DOS_LinkUMBsToMemChain(umb_flag);
+ DOS_SetMemAllocStrategy(old_memstrat); // restore strategy
+ } else this->ParseLine(args);
+}
+
+void DOS_Shell::CMD_CHOICE(char * args){
+ HELP("CHOICE");
+ static char defchoice[3] = {'y','n',0};
+ char *rem = NULL, *ptr;
+ bool optN = ScanCMDBool(args,"N");
+ bool optS = ScanCMDBool(args,"S"); //Case-sensitive matching
+ ScanCMDBool(args,"T"); //Default Choice after timeout
+ if (args) {
+ char *last = strchr(args,0);
+ StripSpaces(args);
+ rem = ScanCMDRemain(args);
+ if (rem && *rem && (tolower(rem[1]) != 'c')) {
+ WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
+ return;
+ }
+ if (args == rem) args = strchr(rem,0)+1;
+ if (rem) rem += 2;
+ if(rem && rem[0]==':') rem++; /* optional : after /c */
+ if (args > last) args = NULL;
+ }
+ if (!rem || !*rem) rem = defchoice; /* No choices specified use YN */
+ ptr = rem;
+ Bit8u c;
+ if(!optS) while ((c = *ptr)) *ptr++ = (char)toupper(c); /* When in no case-sensitive mode. make everything upcase */
+ if(args && *args ) {
+ StripSpaces(args);
+ size_t argslen = strlen(args);
+ if(argslen>1 && args[0] == '"' && args[argslen-1] =='"') {
+ args[argslen-1] = 0; //Remove quotes
+ args++;
+ }
+ WriteOut(args);
+ }
+ /* Show question prompt of the form [a,b]? where a b are the choice values */
+ if (!optN) {
+ if(args && *args) WriteOut(" ");
+ WriteOut("[");
+ size_t len = strlen(rem);
+ for(size_t t = 1; t < len; t++) {
+ WriteOut("%c,",rem[t-1]);
+ }
+ WriteOut("%c]?",rem[len-1]);
+ }
+
+ Bit16u n=1;
+ do {
+ DOS_ReadFile (STDIN,&c,&n);
+ } while (!c || !(ptr = strchr(rem,(optS?c:toupper(c)))));
+ c = optS?c:(Bit8u)toupper(c);
+ DOS_WriteFile (STDOUT,&c, &n);
+ dos.return_code = (Bit8u)(ptr-rem+1);
+}
+
+void DOS_Shell::CMD_ATTRIB(char *args){
+ HELP("ATTRIB");
+ // No-Op for now.
+}
+
+void DOS_Shell::CMD_PATH(char *args){
+ HELP("PATH");
+ if(args && *args && strlen(args)){
+ char pathstring[DOS_PATHLENGTH+CROSS_LEN+20]={ 0 };
+ strcpy(pathstring,"set PATH=");
+ while(args && *args && (*args=='='|| *args==' '))
+ args++;
+ strcat(pathstring,args);
+ this->ParseLine(pathstring);
+ return;
+ } else {
+ std::string line;
+ if(GetEnvStr("PATH",line)) {
+ WriteOut("%s",line.c_str());
+ } else {
+ WriteOut("PATH=(null)");
+ }
+ }
+}
+
+void DOS_Shell::CMD_VER(char *args) {
+ HELP("VER");
+ if(args && *args) {
+ char* word = StripWord(args);
+ if(strcasecmp(word,"set")) return;
+ word = StripWord(args);
+ if (!*args && !*word) { //Reset
+ dos.version.major = 5;
+ dos.version.minor = 0;
+ } else if (*args == 0 && *word && (strchr(word,'.') != 0)) { //Allow: ver set 5.1
+ const char * p = strchr(word,'.');
+ dos.version.major = (Bit8u)(atoi(word));
+ dos.version.minor = (Bit8u)(atoi(p+1));
+ } else { //Official syntax: ver set 5 2
+ dos.version.major = (Bit8u)(atoi(word));
+ dos.version.minor = (Bit8u)(atoi(args));
+ }
+ } else WriteOut(MSG_Get("SHELL_CMD_VER_VER"),VERSION,dos.version.major,dos.version.minor);
+}
diff --git a/src/shell/shell_misc.cpp b/src/shell/shell_misc.cpp
new file mode 100644
index 000000000..8723014e8
--- /dev/null
+++ b/src/shell/shell_misc.cpp
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2002-2019 The DOSBox Team
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <algorithm> //std::copy
+#include <iterator> //std::front_inserter
+#include "shell.h"
+#include "regs.h"
+#include "callback.h"
+#include "support.h"
+
+void DOS_Shell::ShowPrompt(void) {
+ Bit8u drive=DOS_GetDefaultDrive()+'A';
+ char dir[DOS_PATHLENGTH];
+ dir[0] = 0; //DOS_GetCurrentDir doesn't always return something. (if drive is messed up)
+ DOS_GetCurrentDir(0,dir);
+ WriteOut("%c:\\%s>",drive,dir);
+}
+
+static void outc(Bit8u c) {
+ Bit16u n=1;
+ DOS_WriteFile(STDOUT,&c,&n);
+}
+
+void DOS_Shell::InputCommand(char * line) {
+ Bitu size=CMD_MAXLINE-2; //lastcharacter+0
+ Bit8u c;Bit16u n=1;
+ Bitu str_len=0;Bitu str_index=0;
+ Bit16u len=0;
+ bool current_hist=false; // current command stored in history?
+
+ line[0] = '\0';
+
+ std::list<std::string>::iterator it_history = l_history.begin(), it_completion = l_completion.begin();
+
+ while (size) {
+ dos.echo=false;
+ while(!DOS_ReadFile(input_handle,&c,&n)) {
+ Bit16u dummy;
+ DOS_CloseFile(input_handle);
+ DOS_OpenFile("con",2,&dummy);
+ LOG(LOG_MISC,LOG_ERROR)("Reopening the input handle. This is a bug!");
+ }
+ if (!n) {
+ size=0; //Kill the while loop
+ continue;
+ }
+ switch (c) {
+ case 0x00: /* Extended Keys */
+ {
+ DOS_ReadFile(input_handle,&c,&n);
+ switch (c) {
+
+ case 0x3d: /* F3 */
+ if (!l_history.size()) break;
+ it_history = l_history.begin();
+ if (it_history != l_history.end() && it_history->length() > str_len) {
+ const char *reader = &(it_history->c_str())[str_len];
+ while ((c = *reader++)) {
+ line[str_index ++] = c;
+ DOS_WriteFile(STDOUT,&c,&n);
+ }
+ str_len = str_index = (Bitu)it_history->length();
+ size = CMD_MAXLINE - str_index - 2;
+ line[str_len] = 0;
+ }
+ break;
+
+ case 0x4B: /* LEFT */
+ if (str_index) {
+ outc(8);
+ str_index --;
+ }
+ break;
+
+ case 0x4D: /* RIGHT */
+ if (str_index < str_len) {
+ outc(line[str_index++]);
+ }
+ break;
+
+ case 0x47: /* HOME */
+ while (str_index) {
+ outc(8);
+ str_index--;
+ }
+ break;
+
+ case 0x4F: /* END */
+ while (str_index < str_len) {
+ outc(line[str_index++]);
+ }
+ break;
+
+ case 0x48: /* UP */
+ if (l_history.empty() || it_history == l_history.end()) break;
+
+ // store current command in history if we are at beginning
+ if (it_history == l_history.begin() && !current_hist) {
+ current_hist=true;
+ l_history.push_front(line);
+ }
+
+ for (;str_index>0; str_index--) {
+ // removes all characters
+ outc(8); outc(' '); outc(8);
+ }
+ strcpy(line, it_history->c_str());
+ len = (Bit16u)it_history->length();
+ str_len = str_index = len;
+ size = CMD_MAXLINE - str_index - 2;
+ DOS_WriteFile(STDOUT, (Bit8u *)line, &len);
+ it_history ++;
+ break;
+
+ case 0x50: /* DOWN */
+ if (l_history.empty() || it_history == l_history.begin()) break;
+
+ // not very nice but works ..
+ it_history --;
+ if (it_history == l_history.begin()) {
+ // no previous commands in history
+ it_history ++;
+
+ // remove current command from history
+ if (current_hist) {
+ current_hist=false;
+ l_history.pop_front();
+ }
+ break;
+ } else it_history --;
+
+ for (;str_index>0; str_index--) {
+ // removes all characters
+ outc(8); outc(' '); outc(8);
+ }
+ strcpy(line, it_history->c_str());
+ len = (Bit16u)it_history->length();
+ str_len = str_index = len;
+ size = CMD_MAXLINE - str_index - 2;
+ DOS_WriteFile(STDOUT, (Bit8u *)line, &len);
+ it_history ++;
+
+ break;
+ case 0x53:/* DELETE */
+ {
+ if(str_index>=str_len) break;
+ Bit16u a=str_len-str_index-1;
+ Bit8u* text=reinterpret_cast<Bit8u*>(&line[str_index+1]);
+ DOS_WriteFile(STDOUT,text,&a);//write buffer to screen
+ outc(' ');outc(8);
+ for(Bitu i=str_index;i<str_len-1;i++) {
+ line[i]=line[i+1];
+ outc(8);
+ }
+ line[--str_len]=0;
+ size++;
+ }
+ break;
+ case 15: /* Shift-Tab */
+ if (l_completion.size()) {
+ if (it_completion == l_completion.begin()) it_completion = l_completion.end ();
+ it_completion--;
+
+ if (it_completion->length()) {
+ for (;str_index > completion_index; str_index--) {
+ // removes all characters
+ outc(8); outc(' '); outc(8);
+ }
+
+ strcpy(&line[completion_index], it_completion->c_str());
+ len = (Bit16u)it_completion->length();
+ str_len = str_index = completion_index + len;
+ size = CMD_MAXLINE - str_index - 2;
+ DOS_WriteFile(STDOUT, (Bit8u *)it_completion->c_str(), &len);
+ }
+ }
+ default:
+ break;
+ }
+ };
+ break;
+ case 0x08: /* BackSpace */
+ if (str_index) {
+ outc(8);
+ Bit32u str_remain=str_len - str_index;
+ size++;
+ if (str_remain) {
+ memmove(&line[str_index-1],&line[str_index],str_remain);
+ line[--str_len]=0;
+ str_index --;
+ /* Go back to redraw */
+ for (Bit16u i=str_index; i < str_len; i++)
+ outc(line[i]);
+ } else {
+ line[--str_index] = '\0';
+ str_len--;
+ }
+ outc(' '); outc(8);
+ // moves the cursor left
+ while (str_remain--) outc(8);
+ }
+ if (l_completion.size()) l_completion.clear();
+ break;
+ case 0x0a: /* New Line not handled */
+ /* Don't care */
+ break;
+ case 0x0d: /* Return */
+ outc('\r');
+ outc('\n');
+ size=0; //Kill the while loop
+ break;
+ case'\t':
+ {
+ if (l_completion.size()) {
+ it_completion ++;
+ if (it_completion == l_completion.end()) it_completion = l_completion.begin();
+ } else {
+ // build new completion list
+ // Lines starting with CD will only get directories in the list
+ bool dir_only = (strncasecmp(line,"CD ",3)==0);
+
+ // get completion mask
+ char *p_completion_start = strrchr(line, ' ');
+
+ if (p_completion_start) {
+ p_completion_start ++;
+ completion_index = (Bit16u)(str_len - strlen(p_completion_start));
+ } else {
+ p_completion_start = line;
+ completion_index = 0;
+ }
+
+ char *path;
+ if ((path = strrchr(line+completion_index,'\\'))) completion_index = (Bit16u)(path-line+1);
+ if ((path = strrchr(line+completion_index,'/'))) completion_index = (Bit16u)(path-line+1);
+
+ // build the completion list
+ char mask[DOS_PATHLENGTH] = {0};
+ if (strlen(p_completion_start) + 3 >= DOS_PATHLENGTH) {
+ //Beep;
+ break;
+ }
+ if (p_completion_start) {
+ safe_strncpy(mask, p_completion_start,DOS_PATHLENGTH);
+ char* dot_pos=strrchr(mask,'.');
+ char* bs_pos=strrchr(mask,'\\');
+ char* fs_pos=strrchr(mask,'/');
+ char* cl_pos=strrchr(mask,':');
+ // not perfect when line already contains wildcards, but works
+ if ((dot_pos-bs_pos>0) && (dot_pos-fs_pos>0) && (dot_pos-cl_pos>0))
+ strncat(mask, "*",DOS_PATHLENGTH - 1);
+ else strncat(mask, "*.*",DOS_PATHLENGTH - 1);
+ } else {
+ strcpy(mask, "*.*");
+ }
+
+ RealPt save_dta=dos.dta();
+ dos.dta(dos.tables.tempdta);
+
+ bool res = DOS_FindFirst(mask, 0xffff & ~DOS_ATTR_VOLUME);
+ if (!res) {
+ dos.dta(save_dta);
+ break; // TODO: beep
+ }
+
+ DOS_DTA dta(dos.dta());
+ char name[DOS_NAMELENGTH_ASCII];Bit32u sz;Bit16u date;Bit16u time;Bit8u att;
+
+ std::list<std::string> executable;
+ while (res) {
+ dta.GetResult(name,sz,date,time,att);
+ // add result to completion list
+
+ char *ext; // file extension
+ if (strcmp(name, ".") && strcmp(name, "..")) {
+ if (dir_only) { //Handle the dir only case different (line starts with cd)
+ if(att & DOS_ATTR_DIRECTORY) l_completion.push_back(name);
+ } else {
+ ext = strrchr(name, '.');
+ if (ext && (strcmp(ext, ".BAT") == 0 || strcmp(ext, ".COM") == 0 || strcmp(ext, ".EXE") == 0))
+ // we add executables to the a seperate list and place that list infront of the normal files
+ executable.push_front(name);
+ else
+ l_completion.push_back(name);
+ }
+ }
+ res=DOS_FindNext();
+ }
+ /* Add executable list to front of completion list. */
+ std::copy(executable.begin(),executable.end(),std::front_inserter(l_completion));
+ it_completion = l_completion.begin();
+ dos.dta(save_dta);
+ }
+
+ if (l_completion.size() && it_completion->length()) {
+ for (;str_index > completion_index; str_index--) {
+ // removes all characters
+ outc(8); outc(' '); outc(8);
+ }
+
+ strcpy(&line[completion_index], it_completion->c_str());
+ len = (Bit16u)it_completion->length();
+ str_len = str_index = completion_index + len;
+ size = CMD_MAXLINE - str_index - 2;
+ DOS_WriteFile(STDOUT, (Bit8u *)it_completion->c_str(), &len);
+ }
+ }
+ break;
+ case 0x1b: /* ESC */
+ //write a backslash and return to the next line
+ outc('\\');
+ outc('\r');
+ outc('\n');
+ *line = 0; // reset the line.
+ if (l_completion.size()) l_completion.clear(); //reset the completion list.
+ this->InputCommand(line); //Get the NEW line.
+ size = 0; // stop the next loop
+ str_len = 0; // prevent multiple adds of the same line
+ break;
+ default:
+ if (l_completion.size()) l_completion.clear();
+ if(str_index < str_len && true) { //mem_readb(BIOS_KEYBOARD_FLAGS1)&0x80) dev_con.h ?
+ outc(' ');//move cursor one to the right.
+ Bit16u a = str_len - str_index;
+ Bit8u* text=reinterpret_cast<Bit8u*>(&line[str_index]);
+ DOS_WriteFile(STDOUT,text,&a);//write buffer to screen
+ outc(8);//undo the cursor the right.
+ for(Bitu i=str_len;i>str_index;i--) {
+ line[i]=line[i-1]; //move internal buffer
+ outc(8); //move cursor back (from write buffer to screen)
+ }
+ line[++str_len]=0;//new end (as the internal buffer moved one place to the right
+ size--;
+ };
+
+ line[str_index]=c;
+ str_index ++;
+ if (str_index > str_len){
+ line[str_index] = '\0';
+ str_len++;
+ size--;
+ }
+ DOS_WriteFile(STDOUT,&c,&n);
+ break;
+ }
+ }
+
+ if (!str_len) return;
+ str_len++;
+
+ // remove current command from history if it's there
+ if (current_hist) {
+ current_hist=false;
+ l_history.pop_front();
+ }
+
+ // add command line to history
+ l_history.push_front(line); it_history = l_history.begin();
+ if (l_completion.size()) l_completion.clear();
+}
+
+std::string full_arguments = "";
+bool DOS_Shell::Execute(char * name,char * args) {
+/* return true => don't check for hardware changes in do_command
+ * return false => check for hardware changes in do_command */
+ char fullname[DOS_PATHLENGTH+4]; //stores results from Which
+ char* p_fullname;
+ char line[CMD_MAXLINE];
+ if(strlen(args)!= 0){
+ if(*args != ' '){ //put a space in front
+ line[0]=' ';line[1]=0;
+ strncat(line,args,CMD_MAXLINE-2);
+ line[CMD_MAXLINE-1]=0;
+ }
+ else
+ {
+ safe_strncpy(line,args,CMD_MAXLINE);
+ }
+ }else{
+ line[0]=0;
+ };
+
+ /* check for a drive change */
+ if (((strcmp(name + 1, ":") == 0) || (strcmp(name + 1, ":\\") == 0)) && isalpha(*name))
+ {
+ if (!DOS_SetDrive(toupper(name[0])-'A')) {
+ WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0]));
+ }
+ return true;
+ }
+ /* Check for a full name */
+ p_fullname = Which(name);
+ if (!p_fullname) return false;
+ strcpy(fullname,p_fullname);
+ const char* extension = strrchr(fullname,'.');
+
+ /*always disallow files without extension from being executed. */
+ /*only internal commands can be run this way and they never get in this handler */
+ if(extension == 0)
+ {
+ //Check if the result will fit in the parameters. Else abort
+ if(strlen(fullname) >( DOS_PATHLENGTH - 1) ) return false;
+ char temp_name[DOS_PATHLENGTH+4],* temp_fullname;
+ //try to add .com, .exe and .bat extensions to filename
+
+ strcpy(temp_name,fullname);
+ strcat(temp_name,".COM");
+ temp_fullname=Which(temp_name);
+ if (temp_fullname) { extension=".com";strcpy(fullname,temp_fullname); }
+
+ else
+ {
+ strcpy(temp_name,fullname);
+ strcat(temp_name,".EXE");
+ temp_fullname=Which(temp_name);
+ if (temp_fullname) { extension=".exe";strcpy(fullname,temp_fullname);}
+
+ else
+ {
+ strcpy(temp_name,fullname);
+ strcat(temp_name,".BAT");
+ temp_fullname=Which(temp_name);
+ if (temp_fullname) { extension=".bat";strcpy(fullname,temp_fullname);}
+
+ else
+ {
+ return false;
+ }
+
+ }
+ }
+ }
+
+ if (strcasecmp(extension, ".bat") == 0)
+ { /* Run the .bat file */
+ /* delete old batch file if call is not active*/
+ bool temp_echo=echo; /*keep the current echostate (as delete bf might change it )*/
+ if(bf && !call) delete bf;
+ bf=new BatchFile(this,fullname,name,line);
+ echo=temp_echo; //restore it.
+ }
+ else
+ { /* only .bat .exe .com extensions maybe be executed by the shell */
+ if(strcasecmp(extension, ".com") !=0)
+ {
+ if(strcasecmp(extension, ".exe") !=0) return false;
+ }
+ /* Run the .exe or .com file from the shell */
+ /* Allocate some stack space for tables in physical memory */
+ reg_sp-=0x200;
+ //Add Parameter block
+ DOS_ParamBlock block(SegPhys(ss)+reg_sp);
+ block.Clear();
+ //Add a filename
+ RealPt file_name=RealMakeSeg(ss,reg_sp+0x20);
+ MEM_BlockWrite(Real2Phys(file_name),fullname,(Bitu)(strlen(fullname)+1));
+
+ /* HACK: Store full commandline for mount and imgmount */
+ full_arguments.assign(line);
+
+ /* Fill the command line */
+ CommandTail cmdtail;
+ cmdtail.count = 0;
+ memset(&cmdtail.buffer,0,127); //Else some part of the string is unitialized (valgrind)
+ if (strlen(line)>126) line[126]=0;
+ cmdtail.count=(Bit8u)strlen(line);
+ memcpy(cmdtail.buffer,line,strlen(line));
+ cmdtail.buffer[strlen(line)]=0xd;
+ /* Copy command line in stack block too */
+ MEM_BlockWrite(SegPhys(ss)+reg_sp+0x100,&cmdtail,128);
+
+
+ /* Split input line up into parameters, using a few special rules, most notable the one for /AAA => A\0AA
+ * Qbix: It is extremly messy, but this was the only way I could get things like /:aa and :/aa to work correctly */
+
+ //Prepare string first
+ char parseline[258] = { 0 };
+ for(char *pl = line,*q = parseline; *pl ;pl++,q++) {
+ if (*pl == '=' || *pl == ';' || *pl ==',' || *pl == '\t' || *pl == ' ') *q = 0; else *q = *pl; //Replace command seperators with 0.
+ } //No end of string \0 needed as parseline is larger than line
+
+ for(char* p = parseline; (p-parseline) < 250 ;p++) { //Stay relaxed within boundaries as we have plenty of room
+ if (*p == '/') { //Transform /Hello into H\0ello
+ *p = 0;
+ p++;
+ while ( *p == 0 && (p-parseline) < 250) p++; //Skip empty fields
+ if ((p-parseline) < 250) { //Found something. Lets get the first letter and break it up
+ p++;
+ memmove(static_cast<void*>(p + 1),static_cast<void*>(p),(250-(p-parseline)));
+ if ((p-parseline) < 250) *p = 0;
+ }
+ }
+ }
+ parseline[255] = parseline[256] = parseline[257] = 0; //Just to be safe.
+
+ /* Parse FCB (first two parameters) and put them into the current DOS_PSP */
+ Bit8u add;
+ Bit16u skip = 0;
+ //find first argument, we end up at parseline[256] if there is only one argument (similar for the second), which exists and is 0.
+ while(skip < 256 && parseline[skip] == 0) skip++;
+ FCB_Parsename(dos.psp(),0x5C,0x01,parseline + skip,&add);
+ skip += add;
+
+ //Move to next argument if it exists
+ while(parseline[skip] != 0) skip++; //This is safe as there is always a 0 in parseline at the end.
+ while(skip < 256 && parseline[skip] == 0) skip++; //Which is higher than 256
+ FCB_Parsename(dos.psp(),0x6C,0x01,parseline + skip,&add);
+
+ block.exec.fcb1=RealMake(dos.psp(),0x5C);
+ block.exec.fcb2=RealMake(dos.psp(),0x6C);
+ /* Set the command line in the block and save it */
+ block.exec.cmdtail=RealMakeSeg(ss,reg_sp+0x100);
+ block.SaveData();
+#if 0
+ /* Save CS:IP to some point where i can return them from */
+ Bit32u oldeip=reg_eip;
+ Bit16u oldcs=SegValue(cs);
+ RealPt newcsip=CALLBACK_RealPointer(call_shellstop);
+ SegSet16(cs,RealSeg(newcsip));
+ reg_ip=RealOff(newcsip);
+#endif
+ /* Start up a dos execute interrupt */
+ reg_ax=0x4b00;
+ //Filename pointer
+ SegSet16(ds,SegValue(ss));
+ reg_dx=RealOff(file_name);
+ //Paramblock
+ SegSet16(es,SegValue(ss));
+ reg_bx=reg_sp;
+ SETFLAGBIT(IF,false);
+ CALLBACK_RunRealInt(0x21);
+ /* Restore CS:IP and the stack */
+ reg_sp+=0x200;
+#if 0
+ reg_eip=oldeip;
+ SegSet16(cs,oldcs);
+#endif
+ }
+ return true; //Executable started
+}
+
+
+
+
+static const char * bat_ext=".BAT";
+static const char * com_ext=".COM";
+static const char * exe_ext=".EXE";
+static char which_ret[DOS_PATHLENGTH+4];
+
+char * DOS_Shell::Which(char * name) {
+ size_t name_len = strlen(name);
+ if(name_len >= DOS_PATHLENGTH) return 0;
+
+ /* Parse through the Path to find the correct entry */
+ /* Check if name is already ok but just misses an extension */
+
+ if (DOS_FileExists(name)) return name;
+ /* try to find .com .exe .bat */
+ strcpy(which_ret,name);
+ strcat(which_ret,com_ext);
+ if (DOS_FileExists(which_ret)) return which_ret;
+ strcpy(which_ret,name);
+ strcat(which_ret,exe_ext);
+ if (DOS_FileExists(which_ret)) return which_ret;
+ strcpy(which_ret,name);
+ strcat(which_ret,bat_ext);
+ if (DOS_FileExists(which_ret)) return which_ret;
+
+
+ /* No Path in filename look through path environment string */
+ char path[DOS_PATHLENGTH];std::string temp;
+ if (!GetEnvStr("PATH",temp)) return 0;
+ const char * pathenv=temp.c_str();
+ if (!pathenv) return 0;
+ pathenv = strchr(pathenv,'=');
+ if (!pathenv) return 0;
+ pathenv++;
+ Bitu i_path = 0;
+ while (*pathenv) {
+ /* remove ; and ;; at the beginning. (and from the second entry etc) */
+ while(*pathenv == ';')
+ pathenv++;
+
+ /* get next entry */
+ i_path = 0; /* reset writer */
+ while(*pathenv && (*pathenv !=';') && (i_path < DOS_PATHLENGTH) )
+ path[i_path++] = *pathenv++;
+
+ if(i_path == DOS_PATHLENGTH) {
+ /* If max size. move till next ; and terminate path */
+ while(*pathenv && (*pathenv != ';'))
+ pathenv++;
+ path[DOS_PATHLENGTH - 1] = 0;
+ } else path[i_path] = 0;
+
+
+ /* check entry */
+ if(size_t len = strlen(path)){
+ if(len >= (DOS_PATHLENGTH - 2)) continue;
+
+ if(path[len - 1] != '\\') {
+ strcat(path,"\\");
+ len++;
+ }
+
+ //If name too long =>next
+ if((name_len + len + 1) >= DOS_PATHLENGTH) continue;
+ strcat(path,name);
+
+ strcpy(which_ret,path);
+ if (DOS_FileExists(which_ret)) return which_ret;
+ strcpy(which_ret,path);
+ strcat(which_ret,com_ext);
+ if (DOS_FileExists(which_ret)) return which_ret;
+ strcpy(which_ret,path);
+ strcat(which_ret,exe_ext);
+ if (DOS_FileExists(which_ret)) return which_ret;
+ strcpy(which_ret,path);
+ strcat(which_ret,bat_ext);
+ if (DOS_FileExists(which_ret)) return which_ret;
+ }
+ }
+ return 0;
+}
diff --git a/src/winres.rc b/src/winres.rc
new file mode 100644
index 000000000..176cf8b40
--- /dev/null
+++ b/src/winres.rc
@@ -0,0 +1,37 @@
+
+#include "winresrc.h"
+
+// icon resource
+dosbox_ico ICON "dosbox.ico"
+
+
+// version resource
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,74,0,0
+ PRODUCTVERSION 0,74,0,0
+ FILEFLAGSMASK 0x3fL
+ FILEFLAGS 0x0L
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "© 2002-2019 DOSBox Team, published under GNU GPL"
+ VALUE "CompanyName", "DOSBox Team"
+ VALUE "FileDescription", "DOSBox DOS Emulator"
+ VALUE "FileVersion", "0, 74, 0, 0"
+ VALUE "InternalName", "DOSBox"
+ VALUE "LegalCopyright", "Copyright © 2002-2019 DOSBox Team"
+ VALUE "OriginalFilename", "dosbox.exe"
+ VALUE "ProductName", "DOSBox DOS Emulator"
+ VALUE "ProductVersion", "0, 74, 0, 0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/visualc_net/Makefile.am b/visualc_net/Makefile.am
new file mode 100644
index 000000000..6d1c6dd61
--- /dev/null
+++ b/visualc_net/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = dosbox.sln dosbox.vcproj \ No newline at end of file
diff --git a/visualc_net/dosbox.sln b/visualc_net/dosbox.sln
new file mode 100644
index 000000000..5f2be4c32
--- /dev/null
+++ b/visualc_net/dosbox.sln
@@ -0,0 +1,21 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dosbox", "dosbox.vcproj", "{7FCFFB9B-8629-4D51-849C-8490CECF8AB7}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ Debug = Debug
+ Release = Release
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {7FCFFB9B-8629-4D51-849C-8490CECF8AB7}.Debug.ActiveCfg = Debug|Win32
+ {7FCFFB9B-8629-4D51-849C-8490CECF8AB7}.Debug.Build.0 = Debug|Win32
+ {7FCFFB9B-8629-4D51-849C-8490CECF8AB7}.Release.ActiveCfg = Release|Win32
+ {7FCFFB9B-8629-4D51-849C-8490CECF8AB7}.Release.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/visualc_net/dosbox.vcproj b/visualc_net/dosbox.vcproj
new file mode 100644
index 000000000..5a1bac189
--- /dev/null
+++ b/visualc_net/dosbox.vcproj
@@ -0,0 +1,920 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="dosbox"
+ SccProjectName=""
+ SccLocalPath="">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../include,../src/platform/visualc"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ RuntimeTypeInfo="TRUE"
+ PrecompiledHeaderFile=".\Debug/dosbox.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="opengl32.lib sdl_net.lib winmm.lib zlib.lib libpng.lib sdlmain.lib sdl.lib curses.lib odbc32.lib odbccp32.lib ws2_32.lib"
+ OutputFile=".\Debug/dosbox.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\Debug/dosbox.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\Debug/dosbox.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2"
+ WholeProgramOptimization="FALSE">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="3"
+ GlobalOptimizations="TRUE"
+ InlineFunctionExpansion="2"
+ EnableIntrinsicFunctions="TRUE"
+ ImproveFloatingPointConsistency="TRUE"
+ FavorSizeOrSpeed="1"
+ OmitFramePointers="TRUE"
+ OptimizeForProcessor="2"
+ OptimizeForWindowsApplication="TRUE"
+ AdditionalIncludeDirectories="../include,../src/platform/visualc"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ StringPooling="TRUE"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="FALSE"
+ EnableFunctionLevelLinking="TRUE"
+ RuntimeTypeInfo="TRUE"
+ PrecompiledHeaderFile=".\Release/dosbox.pch"
+ AssemblerOutput="4"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="3"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="opengl32.lib winmm.lib zlib.lib libpng.lib sdl_net.lib sdlmain.lib sdl.lib curses.lib odbc32.lib odbccp32.lib ws2_32.lib"
+ OutputFile=".\Release/dosbox.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=""
+ GenerateMapFile="TRUE"
+ SubSystem="1"
+ TargetMachine="1"
+ FixedBaseAddress="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\Release/dosbox.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+ <File
+ RelativePath="..\src\dosbox.cpp">
+ </File>
+ <File
+ RelativePath="..\src\winres.rc">
+ </File>
+ <Filter
+ Name="cpu"
+ Filter="">
+ <File
+ RelativePath="..\src\cpu\callback.cpp">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dyn_x86.cpp">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dynrec.cpp">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_full.cpp">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_normal.cpp">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_prefetch.cpp">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_simple.cpp">
+ </File>
+ <File
+ RelativePath="..\src\cpu\cpu.cpp">
+ </File>
+ <File
+ RelativePath="..\src\cpu\flags.cpp">
+ </File>
+ <File
+ RelativePath="..\src\cpu\instructions.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\lazyflags.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\modrm.cpp">
+ </File>
+ <File
+ RelativePath="..\src\cpu\modrm.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\paging.cpp">
+ </File>
+ <Filter
+ Name="core_normal"
+ Filter="">
+ <File
+ RelativePath="..\src\cpu\core_normal\helpers.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_normal\prefix_0f.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_normal\prefix_66.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_normal\prefix_66_0f.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_normal\prefix_none.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_normal\string.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_normal\support.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_normal\table_ea.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="core_full"
+ Filter="">
+ <File
+ RelativePath="..\src\cpu\core_full\ea_lookup.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_full\load.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_full\loadwrite.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_full\op.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_full\optable.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_full\save.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_full\string.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_full\support.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="core_dyn_x86"
+ Filter="">
+ <File
+ RelativePath="..\src\cpu\core_dyn_x86\cache.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dyn_x86\decoder.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dyn_x86\dyn_fpu.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dyn_x86\dyn_fpu_dh.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dyn_x86\helpers.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dyn_x86\risc_x86.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dyn_x86\string.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="core_dynrec"
+ Filter="">
+ <File
+ RelativePath="..\src\cpu\core_dynrec\cache.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dynrec\decoder.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dynrec\decoder_basic.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dynrec\decoder_opcodes.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dynrec\dyn_fpu.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dynrec\operators.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dynrec\risc_x64.h">
+ </File>
+ <File
+ RelativePath="..\src\cpu\core_dynrec\risc_x86.h">
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="debug"
+ Filter="">
+ <File
+ RelativePath="..\src\debug\debug.cpp">
+ </File>
+ <File
+ RelativePath="..\src\debug\debug_disasm.cpp">
+ </File>
+ <File
+ RelativePath="..\src\debug\debug_gui.cpp">
+ </File>
+ <File
+ RelativePath="..\src\debug\debug_inc.h">
+ </File>
+ <File
+ RelativePath="..\src\debug\debug_win32.cpp">
+ </File>
+ <File
+ RelativePath="..\src\debug\disasm_tables.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="dos"
+ Filter="">
+ <File
+ RelativePath="..\src\dos\dev_con.h">
+ </File>
+ <File
+ RelativePath="..\src\dos\dos.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\dos_classes.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\dos_devices.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\dos_execute.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\dos_files.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\dos_ioctl.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\dos_keyboard_layout.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\dos_memory.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\dos_misc.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\dos_mscdex.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\dos_programs.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\dos_tables.cpp">
+ </File>
+ <Filter
+ Name="win32headers"
+ Filter="">
+ <File
+ RelativePath="..\src\dos\drives.h">
+ </File>
+ <File
+ RelativePath="..\src\dos\Ntddcdrm.h">
+ </File>
+ <File
+ RelativePath="..\src\dos\Ntddscsi.h">
+ </File>
+ <File
+ RelativePath="..\src\dos\Ntddstor.h">
+ </File>
+ <File
+ RelativePath="..\src\dos\scsidefs.h">
+ </File>
+ <File
+ RelativePath="..\src\dos\wnaspi32.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="cdrom"
+ Filter="">
+ <File
+ RelativePath="..\src\dos\cdrom.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\cdrom.h">
+ </File>
+ <File
+ RelativePath="..\src\dos\cdrom_aspi_win32.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\cdrom_image.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\cdrom_ioctl_win32.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="drives"
+ Filter="">
+ <File
+ RelativePath="..\src\dos\drive_cache.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\drive_fat.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\drive_iso.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\drive_local.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\drive_overlay.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\drive_virtual.cpp">
+ </File>
+ <File
+ RelativePath="..\src\dos\drives.cpp">
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="hardware"
+ Filter="">
+ <File
+ RelativePath="..\src\hardware\cmos.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\dma.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\font-switch.h">
+ </File>
+ <File
+ RelativePath="..\src\hardware\hardware.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\iohandler.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\ipx.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\ipxserver.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\joystick.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\keyboard.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\memory.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\mixer.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\pci_bus.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\pic.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\timer.cpp">
+ </File>
+ <Filter
+ Name="vga"
+ Filter="">
+ <File
+ RelativePath="..\src\hardware\vga.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_attr.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_crtc.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_dac.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_draw.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_gfx.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_memory.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_misc.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_other.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_paradise.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_s3.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_seq.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_tseng.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\vga_xga.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="sound"
+ Filter="">
+ <File
+ RelativePath="..\src\hardware\adlib.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\dbopl.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\disney.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\gameblaster.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\gus.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\mpu401.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\pcspeaker.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\sblaster.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\tandy_sound.cpp">
+ </File>
+ <Filter
+ Name="mame"
+ Filter="">
+ <File
+ RelativePath="..\src\hardware\mame\emu.h">
+ </File>
+ <File
+ RelativePath="..\src\hardware\mame\fmopl.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\mame\fmopl.h">
+ </File>
+ <File
+ RelativePath="..\src\hardware\mame\saa1099.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\mame\saa1099.h">
+ </File>
+ <File
+ RelativePath="..\src\hardware\mame\sn76496.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\mame\sn76496.h">
+ </File>
+ <File
+ RelativePath="..\src\hardware\mame\ymdeltat.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\mame\ymdeltat.h">
+ </File>
+ <File
+ RelativePath="..\src\hardware\mame\ymf262.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\mame\ymf262.h">
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="serialport"
+ Filter="">
+ <File
+ RelativePath="..\src\hardware\serialport\directserial.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\serialport\directserial.h">
+ </File>
+ <File
+ RelativePath="..\src\hardware\serialport\libserial.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\serialport\libserial.h">
+ </File>
+ <File
+ RelativePath="..\src\hardware\serialport\misc_util.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\serialport\misc_util.h">
+ </File>
+ <File
+ RelativePath="..\src\hardware\serialport\nullmodem.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\serialport\nullmodem.h">
+ </File>
+ <File
+ RelativePath="..\src\hardware\serialport\serialdummy.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\serialport\serialdummy.h">
+ </File>
+ <File
+ RelativePath="..\src\hardware\serialport\serialport.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\serialport\softmodem.cpp">
+ </File>
+ <File
+ RelativePath="..\src\hardware\serialport\softmodem.h">
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="gui"
+ Filter="">
+ <File
+ RelativePath="..\src\libs\gui_tk\gui_tk.cpp">
+ </File>
+ <File
+ RelativePath="..\src\gui\midi.cpp">
+ </File>
+ <File
+ RelativePath="..\src\gui\midi_win32.h">
+ </File>
+ <File
+ RelativePath="..\src\gui\render.cpp">
+ </File>
+ <File
+ RelativePath="..\src\gui\render_scalers.cpp">
+ </File>
+ <File
+ RelativePath="..\src\gui\render_scalers.h">
+ </File>
+ <File
+ RelativePath="..\src\gui\render_templates.h">
+ </File>
+ <File
+ RelativePath="..\src\gui\sdl_gui.cpp">
+ </File>
+ <File
+ RelativePath="..\src\gui\sdl_mapper.cpp">
+ </File>
+ <File
+ RelativePath="..\src\gui\sdlmain.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="ints"
+ Filter="">
+ <File
+ RelativePath="..\src\ints\bios.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\bios_disk.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\bios_keyboard.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\ems.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\mouse.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\xms.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\xms.h">
+ </File>
+ <Filter
+ Name="int10"
+ Filter="">
+ <File
+ RelativePath="..\src\ints\int10.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\int10.h">
+ </File>
+ <File
+ RelativePath="..\src\ints\int10_char.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\int10_memory.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\int10_misc.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\int10_modes.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\int10_pal.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\int10_put_pixel.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\int10_vesa.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\int10_video_state.cpp">
+ </File>
+ <File
+ RelativePath="..\src\ints\int10_vptable.cpp">
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="shell"
+ Filter="">
+ <File
+ RelativePath="..\src\shell\shell.cpp">
+ </File>
+ <File
+ RelativePath="..\src\shell\shell_batch.cpp">
+ </File>
+ <File
+ RelativePath="..\src\shell\shell_cmds.cpp">
+ </File>
+ <File
+ RelativePath="..\src\shell\shell_misc.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="misc"
+ Filter="">
+ <File
+ RelativePath="..\src\misc\cross.cpp">
+ </File>
+ <File
+ RelativePath="..\src\misc\messages.cpp">
+ </File>
+ <File
+ RelativePath="..\src\misc\programs.cpp">
+ </File>
+ <File
+ RelativePath="..\src\misc\setup.cpp">
+ </File>
+ <File
+ RelativePath="..\src\misc\support.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="visualc"
+ Filter="">
+ <File
+ RelativePath="..\src\platform\visualc\config.h">
+ </File>
+ <File
+ RelativePath="..\src\platform\visualc\unistd.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="fpu"
+ Filter="">
+ <File
+ RelativePath="..\src\fpu\fpu.cpp">
+ </File>
+ <File
+ RelativePath="..\src\fpu\fpu_instructions.h">
+ </File>
+ <File
+ RelativePath="..\src\fpu\fpu_instructions_x86.h">
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl">
+ <File
+ RelativePath="..\include\bios.h">
+ </File>
+ <File
+ RelativePath="..\include\bios_disk.h">
+ </File>
+ <File
+ RelativePath="..\include\callback.h">
+ </File>
+ <File
+ RelativePath="..\include\control.h">
+ </File>
+ <File
+ RelativePath="..\include\cpu.h">
+ </File>
+ <File
+ RelativePath="..\include\cross.h">
+ </File>
+ <File
+ RelativePath="..\include\debug.h">
+ </File>
+ <File
+ RelativePath="..\include\dma.h">
+ </File>
+ <File
+ RelativePath="..\include\dos_inc.h">
+ </File>
+ <File
+ RelativePath="..\include\dos_system.h">
+ </File>
+ <File
+ RelativePath="..\include\dosbox.h">
+ </File>
+ <File
+ RelativePath="..\include\fpu.h">
+ </File>
+ <File
+ RelativePath="..\include\hardware.h">
+ </File>
+ <File
+ RelativePath="..\include\inout.h">
+ </File>
+ <File
+ RelativePath="..\include\joystick.h">
+ </File>
+ <File
+ RelativePath="..\include\keyboard.h">
+ </File>
+ <File
+ RelativePath="..\include\logging.h">
+ </File>
+ <File
+ RelativePath="..\include\mem.h">
+ </File>
+ <File
+ RelativePath="..\include\midi.h">
+ </File>
+ <File
+ RelativePath="..\include\mixer.h">
+ </File>
+ <File
+ RelativePath="..\include\mouse.h">
+ </File>
+ <File
+ RelativePath="..\include\paging.h">
+ </File>
+ <File
+ RelativePath="..\include\pci_bus.h">
+ </File>
+ <File
+ RelativePath="..\include\pic.h">
+ </File>
+ <File
+ RelativePath="..\include\programs.h">
+ </File>
+ <File
+ RelativePath="..\include\regs.h">
+ </File>
+ <File
+ RelativePath="..\include\render.h">
+ </File>
+ <File
+ RelativePath="..\include\serialport.h">
+ </File>
+ <File
+ RelativePath="..\include\setup.h">
+ </File>
+ <File
+ RelativePath="..\include\shell.h">
+ </File>
+ <File
+ RelativePath="..\include\support.h">
+ </File>
+ <File
+ RelativePath="..\include\timer.h">
+ </File>
+ <File
+ RelativePath="..\include\vga.h">
+ </File>
+ <File
+ RelativePath="..\include\video.h">
+ </File>
+ </Filter>
+ <File
+ RelativePath="..\src\dosbox.ico">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>