From d4f8d4d6068e23a085871c278b8855ee87072feb Mon Sep 17 00:00:00 2001 From: David Schmidt Date: Mon, 12 Apr 2010 10:48:59 +0000 Subject: [PATCH] Initially merge Christopher Mason's changes in (build not working yet) --- src/Makefile | 254 ++-- src/README.ethernet.txt | 74 ++ src/SDL.dll | Bin 0 -> 324096 bytes src/config.c | 196 ++- src/config.h | 1 + src/engine_c.c | 19 +- src/joystick_driver.c | 5 + src/kegs.sln | 26 + src/kegs.vcproj | 1238 ++++++++++++++++++ src/kegs32.aps | Bin 0 -> 59964 bytes src/kegs32.ico | Bin 0 -> 8478 bytes src/kegs32.rc | 209 +++ src/moremem.c | 134 +- src/objects.xib | 8 +- src/op_routs.h | 29 +- src/paddles.c | 29 +- src/parallel.c | 76 ++ src/parallel.rom | Bin 0 -> 256 bytes src/printer.cpp | 2046 ++++++++++++++++++++++++++++++ src/printer.h | 279 ++++ src/printer_charmaps.h | 327 +++++ src/protos.h | 78 ++ src/resource | 58 + src/resource.h | 57 + src/scc_socket_driver.c | 2 +- src/scc_windriver.c | 49 +- src/sim65816.c | 57 +- src/support.h | 58 + src/tfe/Tfesupp.c | 188 +++ src/tfe/Tfesupp.h | 53 + src/tfe/Types.h | 73 ++ src/tfe/arch/win32/Bittypes.h | 86 ++ src/tfe/arch/win32/Bpf.h | 516 ++++++++ src/tfe/arch/win32/Ip6_misc.h | 159 +++ src/tfe/arch/win32/pcap-stdinc.h | 44 + src/tfe/arch/win32/pcap.h | 273 ++++ src/tfe/arch/win32/tfearch.c | 556 ++++++++ src/tfe/tfe.c | 1987 +++++++++++++++++++++++++++++ src/vars_kegs32 | 10 + src/win32.ico | Bin 0 -> 8478 bytes src/win32.rc | 168 ++- src/win32snd_driver.c | 37 +- src/windriver.c | 1930 ++++++++++++++++++---------- src/winresource.h | 16 + src/wintoolbar.bmp | Bin 0 -> 758 bytes 45 files changed, 10394 insertions(+), 1011 deletions(-) create mode 100644 src/README.ethernet.txt create mode 100644 src/SDL.dll create mode 100644 src/kegs.sln create mode 100644 src/kegs.vcproj create mode 100644 src/kegs32.aps create mode 100644 src/kegs32.ico create mode 100644 src/kegs32.rc create mode 100644 src/parallel.c create mode 100644 src/parallel.rom create mode 100644 src/printer.cpp create mode 100644 src/printer.h create mode 100644 src/printer_charmaps.h create mode 100644 src/resource create mode 100644 src/resource.h create mode 100644 src/support.h create mode 100644 src/tfe/Tfesupp.c create mode 100644 src/tfe/Tfesupp.h create mode 100644 src/tfe/Types.h create mode 100644 src/tfe/arch/win32/Bittypes.h create mode 100644 src/tfe/arch/win32/Bpf.h create mode 100644 src/tfe/arch/win32/Ip6_misc.h create mode 100644 src/tfe/arch/win32/pcap-stdinc.h create mode 100644 src/tfe/arch/win32/pcap.h create mode 100644 src/tfe/arch/win32/tfearch.c create mode 100644 src/tfe/tfe.c create mode 100644 src/vars_kegs32 create mode 100644 src/win32.ico create mode 100644 src/wintoolbar.bmp diff --git a/src/Makefile b/src/Makefile index b53e763..7dcac75 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,128 +1,126 @@ -# $Id: release_makefile_base,v 1.15 2003/11/21 20:00:42 kentd Exp kentd $ - -OBJECTS1 = adb.o clock.o config.o dis.o engine_c.o scc.o iwm.o \ - joystick_driver.o moremem.o paddles.o sim65816.o smartport.o \ - sound.o sound_driver.o video.o scc_socket_driver.o scc_windriver.o \ - scc_macdriver.o - -include vars - -.SUFFIXES: .dep .proto - -AS = $(CC) - -XLIBS = -L/usr/X11R6/lib -PERL = perl - -all: $(TARGET) - -specials: 8inst_s 16inst_s 8size 16size 8inst_c 16inst_c size_c size_s - -specials_clean: - rm -f 8inst_s 16inst_s 8size 16size 8inst_c 16inst_c size_c size_s - - -# Mac builds: -gsportmac: $(OBJECTS) compile_time.o - $(CC) $(CCOPTS) $(LDOPTS) -arch ppc $(OBJECTS) compile_time.o $(LDFLAGS) -o gsport $(EXTRA_LIBS) -prebind -framework Carbon -framework Quicktime - mkdir -p ../GSPORT.app/Contents/Resources/English.lproj/main.nib - mkdir -p ../GSPORT.app/Contents/MacOS - mv gsport ../GSPORT.app/Contents/MacOS/GSPORTMAC - echo "APPL????" > ../GSPORT.app/Contents/PkgInfo - cp -f Info.plist ../GSPORT.app/Contents/ - cp -f info.nib ../GSPORT.app/Contents/Resources/English.lproj/main.nib - cp -f classes.nib ../GSPORT.app/Contents/Resources/English.lproj/main.nib - cp -f objects.xib ../GSPORT.app/Contents/Resources/English.lproj/main.nib - cp -f kegsicon.icns ../GSPORT.app/Contents/Resources/ - cp -f 525.icns ../GSPORT.app/Contents/Resources/ - cp -f 2mg.icns ../GSPORT.app/Contents/Resources/ - touch '../GSPORT.app/Icon?' - cp -f ../config.template ../config.txt - -# Linux for X builds: -gsportx: $(OBJECTS) compile_time.o - $(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) -lX11 - mv gsportx .. - cp -f ../config.template ../config.txt - -# Cygwin for X builds: -gsport.exe: $(OBJECTS) compile_time.o - $(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) -lXext -lX11 -lm - mv gsport.exe .. - cp -f ../config.template ../config.txt - -# Mingw32 (native windows) builds: -gsportwin.exe: $(OBJECTS) compile_time.o - $(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS) -lwinmm -lgdi32 -ldsound -lcomctl32 -lws2_32 - mv $(NAME)$(SUFFIX) .. - cp -f ../config.template ../config.txt - - -8inst_c.h: instable.h - $(PERL) make_inst c 8 instable.h > 8inst_c.h - -16inst_c.h: instable.h - $(PERL) make_inst c 16 instable.h > 16inst_c.h - -size_c.h: size_tab.h - $(PERL) make_size c size_tab.h > size_c.h - -engine_c.o: 8inst_c.h 16inst_c.h size_c.h - -8inst_s.h: instable.h - $(PERL) make_inst s 8 instable.h > 8inst_s.h - -16inst_s.h: instable.h - $(PERL) make_inst s 16 instable.h > 16inst_s.h - -size_s.h: size_tab.h - $(PERL) make_size s size_tab.h > size_s.h - -8size_s.h: size_tab.h - $(PERL) make_size 8 size_tab.h > 8size_s.h - -16size_s.h: size_tab.h - $(PERL) make_size 16 size_tab.h > 16size_s.h - -engine_s.o: 8inst_s.h 16inst_s.h 8size_s.h 16size_s.h size_s.h - -.s.o: - $(AS) -c $(OPTS) -I. $*.s - -.c.o: - $(CC) $(CCOPTS) $(XOPTS) -c $(OPTS) -I. $*.c - -partls: partls.c - cc $(CCOPTS) $(XOPTS) $(OPTS) -o partls partls.c - -to_pro: prodos.h prodos_protos.h to_pro.c - cc $(CCOPTS) $(XOPTS) $(OPTS) -o to_pro to_pro.c - - -compile_time.o: $(OBJECTS) - - -# dependency stuff -adb.o: adb.c adb.h defc.h defcomm.h iwm.h protos.h -engine_c.o: engine_c.c defc.h defcomm.h iwm.h protos.h protos_engine_c.h size_c.h op_routs.h defs_instr.h 8inst_c.h 16inst_c.h -clock.o: clock.c defc.h defcomm.h iwm.h protos.h -compile_time.o: compile_time.c -config.o: config.c defc.h defcomm.h iwm.h protos.h config.h -dis.o: dis.c defc.h defcomm.h iwm.h protos.h disas.h -scc.o: scc.c defc.h defcomm.h iwm.h protos.h scc.h -scc_socket_driver.o: scc_socket_driver.c defc.h defcomm.h iwm.h protos.h scc.h -scc_windriver.o: scc_windriver.c defc.h defcomm.h iwm.h protos.h scc.h -scc_macdriver.o: scc_macdriver.c defc.h defcomm.h iwm.h protos.h scc.h -iwm.o: iwm.c defc.h defcomm.h iwm.h protos.h iwm_35_525.h -joystick_driver.o: joystick_driver.c defc.h defcomm.h iwm.h protos.h -moremem.o: moremem.c defc.h defcomm.h iwm.h protos.h -paddles.o: paddles.c defc.h defcomm.h iwm.h protos.h -sim65816.o: sim65816.c defc.h defcomm.h iwm.h protos.h -smartport.o: smartport.c defc.h defcomm.h iwm.h protos.h -sound.o: sound.c defc.h defcomm.h iwm.h protos.h sound.h -sound_driver.o: sound_driver.c defc.h defcomm.h iwm.h protos.h sound.h -video.o: video.c defc.h defcomm.h iwm.h protos.h superhires.h kegsfont.h -macdriver.o: macdriver.c defc.h defcomm.h iwm.h protos.h protos_macdriver.h -macsnd_driver.o: macsnd_driver.c defc.h defcomm.h iwm.h protos.h sound.h -windriver.o: windriver.c defc.h defcomm.h iwm.h protos.h protos_windriver.h winresource.h -win32snd_driver.o: win32snd_driver.c defc.h defcomm.h iwm.h protos.h sound.h +# $Id: release_makefile_base,v 1.15 2003/11/21 20:00:42 kentd Exp kentd $ + +OBJECTS1 = adb.o clock.o config.o dis.o engine_c.o scc.o iwm.o \ + joystick_driver.o moremem.o paddles.o sim65816.o smartport.o \ + sound.o sound_driver.o video.o scc_socket_driver.o scc_windriver.o \ + scc_macdriver.o + +include vars + +.SUFFIXES: .dep .proto + +AS = $(CC) + +XLIBS = -L/usr/X11R6/lib +PERL = perl + +all: $(TARGET) + +specials: 8inst_s 16inst_s 8size 16size 8inst_c 16inst_c size_c size_s + +specials_clean: + rm -f 8inst_s 16inst_s 8size 16size 8inst_c 16inst_c size_c size_s + + +# Mac builds: +kegsmac: $(OBJECTS) compile_time.o + $(CC) $(CCOPTS) $(LDOPTS) -arch ppc $(OBJECTS) compile_time.o $(LDFLAGS) -o kegsmac $(EXTRA_LIBS) -prebind -framework Carbon -framework Quicktime + mkdir -p ../KEGSMAC.app/Contents/Resources/English.lproj/main.nib + mkdir -p ../KEGSMAC.app/Contents/MacOS + mv kegsmac ../KEGSMAC.app/Contents/MacOS/KEGSMAC + echo "APPL????" > ../KEGSMAC.app/Contents/PkgInfo + cp -f Info.plist ../KEGSMAC.app/Contents/ + cp -f info.nib ../KEGSMAC.app/Contents/Resources/English.lproj/main.nib + cp -f classes.nib ../KEGSMAC.app/Contents/Resources/English.lproj/main.nib + cp -f objects.xib ../KEGSMAC.app/Contents/Resources/English.lproj/main.nib + cp -f kegsicon.icns ../KEGSMAC.app/Contents/Resources/ + cp -f 525.icns ../KEGSMAC.app/Contents/Resources/ + cp -f 2mg.icns ../KEGSMAC.app/Contents/Resources/ + touch '../KEGSMAC.app/Icon?' + +# Linux for X builds: +xkegs: $(OBJECTS) compile_time.o + $(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) -lX11 + mv xkegs .. + +# Cygwin for X builds: +kegs.exe: $(OBJECTS) compile_time.o + $(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) -lXext -lX11 -lm + mv kegs.exe .. + +# Mingw32 (native windows) builds: +kegswin.exe: $(OBJECTS) compile_time.o + $(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS) -lwinmm -lgdi32 -ldsound -lcomctl32 -lcomdlg32 -lws2_32 + mv $(NAME)$(SUFFIX) .. + + +8inst_c.h: instable.h + $(PERL) make_inst c 8 instable.h > 8inst_c.h + +16inst_c.h: instable.h + $(PERL) make_inst c 16 instable.h > 16inst_c.h + +size_c.h: size_tab.h + $(PERL) make_size c size_tab.h > size_c.h + +engine_c.o: 8inst_c.h 16inst_c.h size_c.h + +8inst_s.h: instable.h + $(PERL) make_inst s 8 instable.h > 8inst_s.h + +16inst_s.h: instable.h + $(PERL) make_inst s 16 instable.h > 16inst_s.h + +size_s.h: size_tab.h + $(PERL) make_size s size_tab.h > size_s.h + +8size_s.h: size_tab.h + $(PERL) make_size 8 size_tab.h > 8size_s.h + +16size_s.h: size_tab.h + $(PERL) make_size 16 size_tab.h > 16size_s.h + +engine_s.o: 8inst_s.h 16inst_s.h 8size_s.h 16size_s.h size_s.h + +.s.o: + $(AS) -c $(OPTS) -I. $*.s + +.c.o: + $(CC) $(CCOPTS) $(XOPTS) -c $(OPTS) -I. $*.c + +partls: partls.c + cc $(CCOPTS) $(XOPTS) $(OPTS) -o partls partls.c + +to_pro: prodos.h prodos_protos.h to_pro.c + cc $(CCOPTS) $(XOPTS) $(OPTS) -o to_pro to_pro.c + +kegs32.o: win32.rc winresource.h + windres win32.rc -o kegs32.o + +compile_time.o: $(OBJECTS) + + +# dependency stuff +adb.o: adb.c adb.h defc.h defcomm.h iwm.h protos.h +engine_c.o: engine_c.c defc.h defcomm.h iwm.h protos.h protos_engine_c.h size_c.h op_routs.h defs_instr.h 8inst_c.h 16inst_c.h +clock.o: clock.c defc.h defcomm.h iwm.h protos.h +compile_time.o: compile_time.c +config.o: config.c defc.h defcomm.h iwm.h protos.h config.h +dis.o: dis.c defc.h defcomm.h iwm.h protos.h disas.h +scc.o: scc.c defc.h defcomm.h iwm.h protos.h scc.h +scc_socket_driver.o: scc_socket_driver.c defc.h defcomm.h iwm.h protos.h scc.h +scc_windriver.o: scc_windriver.c defc.h defcomm.h iwm.h protos.h scc.h +scc_macdriver.o: scc_macdriver.c defc.h defcomm.h iwm.h protos.h scc.h +iwm.o: iwm.c defc.h defcomm.h iwm.h protos.h iwm_35_525.h +joystick_driver.o: joystick_driver.c defc.h defcomm.h iwm.h protos.h +moremem.o: moremem.c defc.h defcomm.h iwm.h protos.h +paddles.o: paddles.c defc.h defcomm.h iwm.h protos.h +sim65816.o: sim65816.c defc.h defcomm.h iwm.h protos.h +smartport.o: smartport.c defc.h defcomm.h iwm.h protos.h +sound.o: sound.c defc.h defcomm.h iwm.h protos.h sound.h +sound_driver.o: sound_driver.c defc.h defcomm.h iwm.h protos.h sound.h +video.o: video.c defc.h defcomm.h iwm.h protos.h superhires.h kegsfont.h +macdriver.o: macdriver.c defc.h defcomm.h iwm.h protos.h protos_macdriver.h +macsnd_driver.o: macsnd_driver.c defc.h defcomm.h iwm.h protos.h sound.h +windriver.o: windriver.c defc.h defcomm.h iwm.h protos.h protos_windriver.h winresource.h kegs32.o +win32snd_driver.o: win32snd_driver.c defc.h defcomm.h iwm.h protos.h sound.h diff --git a/src/README.ethernet.txt b/src/README.ethernet.txt new file mode 100644 index 0000000..372e8a5 --- /dev/null +++ b/src/README.ethernet.txt @@ -0,0 +1,74 @@ +Ethernet support HOW-TO +----------------------- + +Acknowledgment: +--------------- + +Uthernet (TFE) support in KEGS was made possible by implementing the GPL source written by Spiro Trikaliotis for the Vice emulator - http://www.viceteam.org/ This version of KEGS contains the latest code from VICE 2.1. + +Details: + +Right now Uthernet emulation only works under Windows. Future support for PCap under OS X and Linux is planned. +In order to use Uthernet emulation, you must install WinPCap ( http://www.WinPcap.org/install/default.htm ) and +have a wired ethernet connecton on the host computer. + + +KEGS Setup: +----------- + +After KEGS starts, press F4 to enter the text based menu and select the "Ethernet Card Configuration" option. + +By default, Uthernet emulation is turned off, enable it by setting "Uthernet Card in Slot 3"to "On". + +Next, select the host interface you wish to use to communicate with the outside world. A list of available +interfaces is provided on screen. For most the default of interface "0" is correct. + +Return back to the main menu and save you configuration for good measure. Due to limitations, you must exit +and restart KEGS for the changes you made to take effect. Next is configuring the Apple IIgs side of things. + +GS/OS Setup: +------------ + +In order to use TCP/IP connectivity under GS/OS, you have to install the latest version of Marinetti and the +Uthernet Link Layer. + +First download and install the latest version of Marinetti 3.0 available at: +http://www.apple2.org/marinetti/Marinetti3.0b1.SHK + +Then install the latest TCPIP INIT available at +http://www.apple2.org/marinetti/TCPIP30b3.SHK + +Just extract and copy the file to the "System.Setup" folder in your GS/OS system folder, overwriting the existing file + +Last, install the latest Uthernet Link Layer (1.0.1b5) available at: +http://www.wannop.info/speccie/uthernetll.bxy + +Just extract and copy the file to the "TCPIP" folder in your GS/OS system folder. + +After copying over all the files, reboot the emulated IIgs. Once at the desktop, bring up the graphical control +panel available in the Apple menu and double click the "TCP/IP" icon. + +Click on "Setup Connection..." Select the "Uthernet" link layer from the drop down and then click "Configure..." + +Set "Lan Slot:" to "3", check off the DHCP option and then click "Save". Click "OK" to leave the setup dialog. + +You should be able to click "Connect to network" and successfully connect to your TCP/IP netowork. + +From this point on, you are free to run any TCP/IP aware GS/OS applications. + +The following applications have been tested and seem to work fine: +------------------------------------------------------------------ + +Spectrum Automated File Exchange 2.1.9 (FTP client, use passive mode) +Telnet application included with Marinetti + +The following applications have been tested and DO NOT work: +------------------------------------------------------------ + +Casper web server: It will not serve web pages. This is likely a limitation of WinPCap. + +A note about 8-bit applications: +-------------------------------- + +Uthernet enabled versions of Contiki seem to work fine with KEGS. Other 8-bit software should work fine but +are untested at this time. \ No newline at end of file diff --git a/src/SDL.dll b/src/SDL.dll new file mode 100644 index 0000000000000000000000000000000000000000..628cdfcf0183d17c802025e041e685f3f92537f4 GIT binary patch literal 324096 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 E02O&aUH||9 literal 0 HcmV?d00001 diff --git a/src/config.c b/src/config.c index f0cb611..4dead06 100644 --- a/src/config.c +++ b/src/config.c @@ -24,6 +24,11 @@ #include "config.h" #include +#if defined (WIN32) || (WIN64) +#define snprintf _snprintf +typedef unsigned int mode_t; +#endif + extern int Verbose; extern word32 g_vbl_count; extern Iwm iwm; @@ -59,6 +64,21 @@ extern int g_joystick_trim_amount_x; extern int g_joystick_trim_amount_y; extern int g_swap_paddles; extern int g_invert_paddles; +extern int g_ethernet; +extern int g_ethernet_interface; +extern int g_parallel; +extern int g_parallel_out_masking; +extern int g_printer; +extern int g_printer_dpi; +extern char* g_printer_output; +extern int g_printer_multipage; +extern char* g_printer_font_roman; +extern char* g_printer_font_sans; +extern char* g_printer_font_prestige; +extern char* g_printer_font_courier; +extern char* g_printer_font_script; +extern char* g_printer_font_ocra; +extern int g_printer_timeout; extern int g_screen_index[]; extern word32 g_full_refresh_needed; @@ -68,7 +88,6 @@ extern int g_new_a2_stat_cur_line; extern int g_key_down; extern const char g_kegs_version_str[]; - int g_config_control_panel = 0; char g_config_kegs_name[1024]; char g_cfg_cwd_str[CFG_PATH_MAX] = { 0 }; @@ -101,6 +120,7 @@ int g_cfg_curs_mousetext = 0; int g_cfg_opts_vals[CFG_MAX_OPTS]; char g_cfg_opts_strs[CFG_MAX_OPTS][CFG_OPT_MAXSTR]; +char g_cfg_opts_strvals[CFG_MAX_OPTS][CFG_OPT_MAXSTR]; char g_cfg_opt_buf[CFG_OPT_MAXSTR]; char *g_cfg_rom_path = "ROM"; @@ -142,7 +162,8 @@ Cfg_menu g_cfg_disk_menu[] = { Cfg_menu g_cfg_joystick_menu[] = { { "Joystick Configuration", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU }, { "Joystick Emulation,0,Keypad Joystick,1,Mouse Joystick,2,Native Joystick 1," - "3,Native Joystick 2", KNMP(g_joystick_type), CFGTYPE_INT }, + "3,Native Joystick 2,4,Disable Joystick", + KNMP(g_joystick_type), CFGTYPE_INT }, { "Joystick Scale X,0x100,Standard,0x119,+10%,0x133,+20%," "0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%", KNMP(g_joystick_scale_factor_x), CFGTYPE_INT }, @@ -185,12 +206,64 @@ Cfg_menu g_cfg_serial_menu[] = { { 0, 0, 0, 0, 0 }, }; +Cfg_menu g_cfg_parallel_menu[] = { +{ "Parallel Card Configuration", g_cfg_parallel_menu, 0, 0, CFGTYPE_MENU }, +{ "Parallel Card in Slot 1,0,Off,1,On", + KNMP(g_parallel), CFGTYPE_INT }, +{ "Parallel Output,0,Send full 8-bit data,1,Mask off high bit", + KNMP(g_parallel_out_masking), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_ethernet_menu[] = { +{ "Ethernet Card Configuration", g_cfg_ethernet_menu, 0, 0, CFGTYPE_MENU }, +{ "Uthernet Card in Slot 3,0,Off,1,On", + KNMP(g_ethernet), CFGTYPE_INT }, +{ "Use Interface Number,0,0,1,1,2,2,3,3,4,4", + KNMP(g_ethernet_interface), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_printer_menu[] = { +{ "Virtual Printer Configuration", g_cfg_ethernet_menu, 0, 0, CFGTYPE_MENU }, +{ "Virtual Printer Type,0,Epson LQ", + KNMP(g_printer), CFGTYPE_INT }, +{ "Printer DPI,60,60x60 dpi,180,180x180 dpi,360,360x360 dpi", + KNMP(g_printer_dpi), CFGTYPE_INT }, +{ "Printer Output Type,bmp,Windows Bitmap,ps,Postscript (B&W),printer,Direct to host printer", + KNMP(g_printer_output), CFGTYPE_STR }, +{ "Multipage Files? (PS Only),0,No,1,Yes", + KNMP(g_printer_multipage), CFGTYPE_INT }, +{ "Printer Timeout,0,Never,2,2 sec.,15,15 sec.,30,30 sec.,60, 1 min.", + KNMP(g_printer_timeout), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Epson LQ Fonts", 0, 0, 0, 0 }, +{ "--------------", 0, 0, 0, 0 }, +{ "", 0, 0, 0, 0 }, +{ "Roman", KNMP(g_printer_font_roman), CFGTYPE_FILE }, +{ "Sans Serif", KNMP(g_printer_font_sans), CFGTYPE_FILE }, +{ "Courier", KNMP(g_printer_font_courier), CFGTYPE_FILE }, +{ "Prestige", KNMP(g_printer_font_prestige), CFGTYPE_FILE }, +{ "Script", KNMP(g_printer_font_script), CFGTYPE_FILE }, +{ "OCR A/B", KNMP(g_printer_font_ocra), CFGTYPE_FILE }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + Cfg_menu g_cfg_main_menu[] = { { "GSport Configuration", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, { "Disk Configuration", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, { "Joystick Configuration", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU }, { "ROM File Selection", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU }, { "Serial Port Configuration", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU }, +{ "Ethernet Card Configuration", g_cfg_ethernet_menu, 0, 0, CFGTYPE_MENU }, +{ "Parallel Card Configuration", g_cfg_parallel_menu, 0, 0, CFGTYPE_MENU }, +{ "Virtual Printer Configuration", g_cfg_printer_menu, 0, 0, CFGTYPE_MENU }, { "Force X-windows display depth", KNMP(g_force_depth), CFGTYPE_INT }, { "Auto-update configuration file,0,Manual,1,Immediately", KNMP(g_config_kegs_auto_update), CFGTYPE_INT }, @@ -241,7 +314,7 @@ int g_cfg_file_pathfield = 0; const char *g_kegs_rom_names[] = { "ROM", "ROM", "ROM.01", "ROM.03", 0 }; /* First entry is special--it will be overwritten by g_cfg_rom_path */ -const char *g_kegs_c1rom_names[] = { 0 }; +const char *g_kegs_c1rom_names[] = { "parallel.rom" }; const char *g_kegs_c2rom_names[] = { 0 }; const char *g_kegs_c3rom_names[] = { 0 }; const char *g_kegs_c4rom_names[] = { 0 }; @@ -331,6 +404,15 @@ config_init_menus(Cfg_menu *menuptr) defptr->intval = val; menuptr->defptr = &(defptr->intval); break; + case CFGTYPE_STR: + str_ptr = (char **)menuptr->ptr; + str = *str_ptr; + // We need to malloc this string since all + // string values must be dynamically alloced + defptr->strval = str; // this can have a copy + *str_ptr = kegs_malloc_str(str); + menuptr->defptr = &(defptr->strval); + break; case CFGTYPE_FILE: str_ptr = (char **)menuptr->ptr; str = *str_ptr; @@ -437,6 +519,37 @@ cfg_text_screen_dump() fclose(ofile); } +void +cfg_get_tfe_name() +{ + int i = 0; + char *ppname = NULL; + char *ppdes = NULL; + cfg_htab_vtab(0,9); + if (tfe_enumadapter_open()) + { + cfg_printf("Interface List:\n---------------"); + while(tfe_enumadapter(&ppname,&ppdes)) + { + cfg_htab_vtab(0, 11+i); + cfg_printf("%2d: %s",i,ppdes); + i++; + lib_free(ppname); + lib_free(ppdes); + } + tfe_enumadapter_close(); + } + else + { + #ifdef WIN32 + cfg_printf("ERROR: Install/Enable WinPcap for Ethernet Support!!"); + #else + cfg_printf("ERROR: Install/Enable LibPcap for Ethernet Support!!"); + #endif + } + return; +} + void config_vbl_update(int doit_3_persec) { @@ -523,6 +636,13 @@ config_parse_option(char *buf, int pos, int len, int line) iptr = (int *)menuptr->ptr; *iptr = val; break; + case CFGTYPE_STR: + strptr = (char **)menuptr->ptr; + if(strptr && *strptr) { + free(*strptr); + } + *strptr = kegs_malloc_str(&buf[pos]); + break; case CFGTYPE_FILE: strptr = (char **)menuptr->ptr; if(strptr && *strptr) { @@ -686,6 +806,7 @@ config_load_roms() continue; } close(fd); + fd = 0; } } @@ -1062,6 +1183,14 @@ config_write_config_kegs_file() curval); } } + if(type == CFGTYPE_STR) { + curstr = *((char **)menuptr->ptr); + defstr = *((char **)menuptr->defptr); + if(strcmp(curstr, defstr) != 0) { + fprintf(fconf, "%s = %s\n", menuptr->name_str, + curstr); + } + } if(type == CFGTYPE_FILE) { curstr = *((char **)menuptr->ptr); defstr = *((char **)menuptr->defptr); @@ -1891,6 +2020,7 @@ cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change) char *curstr, *defstr; char *str; char *outstr; + char *strval; int *iptr; int val; int num_opts; @@ -1975,8 +2105,16 @@ cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change) my_exit(1); } } else { + if (type == CFGTYPE_INT) + { val = strtoul(valbuf, 0, 0); g_cfg_opts_vals[num_opts] = val; + } + + if (type == CFGTYPE_STR) + { + strncpy(&(g_cfg_opts_strvals[num_opts][0]),&(valbuf[0]),CFG_OPT_MAXSTR); + } outstr = &(g_cfg_opts_strs[num_opts][0]); opt_get_str = 1; } @@ -2005,6 +2143,16 @@ cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change) g_cfg_opt_buf[4] = '\t'; } } + if(type == CFGTYPE_STR) { + str_ptr = (char **)menuptr->ptr; + curstr = *str_ptr; + str_ptr = (char **)menuptr->defptr; + defstr = *str_ptr; + if(strcmp(curstr,defstr) == 0) { + g_cfg_opt_buf[3] = 'D'; /* checkmark */ + g_cfg_opt_buf[4] = '\t'; + } + } if(type == CFGTYPE_FILE) { str_ptr = (char **)menuptr->ptr; curstr = *str_ptr; @@ -2039,6 +2187,18 @@ cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change) } } } + if(type == CFGTYPE_STR) { + g_cfg_opt_buf[bufpos++] = ' '; + g_cfg_opt_buf[bufpos++] = '='; + g_cfg_opt_buf[bufpos++] = ' '; + g_cfg_opt_buf[bufpos] = 0; + for(i = 0; i < num_opts; i++) { + if(!strcmp(curstr,g_cfg_opts_strvals[i])) { + opt_num = i; + break; + } + } + } if(change != 0) { if(type == CFGTYPE_INT) { @@ -2058,6 +2218,23 @@ cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change) iptr = (int *)menuptr->ptr; *iptr = curval; } + if(type == CFGTYPE_STR) { + if(num_opts > 0) { + opt_num += change; + if(opt_num >= num_opts) { + opt_num = 0; + } + if(opt_num < 0) { + opt_num = num_opts - 1; + } + curstr = g_cfg_opts_strvals[opt_num]; + } else { + //curstr += change; + /* HACK: min_val, max_val testing here */ + } + str_ptr = (char **)menuptr->ptr; + *str_ptr = curstr; + } g_config_kegs_update_needed = 1; } @@ -2073,6 +2250,10 @@ cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change) if(type == CFGTYPE_INT) { str = &(g_cfg_opts_strs[0][0]); snprintf(str, CFG_OPT_MAXSTR, "%d", curval); + } else if (type == CFGTYPE_STR) { + str = &(g_cfg_opts_strs[0][0]); + printf("curstr is: %s str is: %s\n", curstr,str); + snprintf(str, CFG_OPT_MAXSTR, "%s", curstr); } else if (type == CFGTYPE_DISK) { str = &(g_cfg_opts_strs[0][0]), cfg_get_disk_name(str, CFG_OPT_MAXSTR, type_ext, 1); @@ -2297,7 +2478,7 @@ cfg_dirent_sortfn(const void *obj1, const void *obj2) direntptr1 = (const Cfg_dirent *)obj1; direntptr2 = (const Cfg_dirent *)obj2; #if defined(MAC) || defined(_WIN32) - ret = strcasecmp(direntptr1->name, direntptr2->name); + ret = _stricmp(direntptr1->name, direntptr2->name); #else ret = strcmp(direntptr1->name, direntptr2->name); #endif @@ -2966,6 +3147,13 @@ config_control_panel() if(g_cfg_slotdrive >= 0) { cfg_file_draw(); } + /*HACK eh, at least I think it is. Display the available ethernet interfaces + when in the ethernet control panel. This is the only way one can customize a menu pane. + Kent did it with the directory browser, so why not.*/ + if(menuptr == g_cfg_ethernet_menu) + { + cfg_get_tfe_name(); + } key = -1; while(g_config_control_panel) { diff --git a/src/config.h b/src/config.h index fe53b5d..869bf99 100644 --- a/src/config.h +++ b/src/config.h @@ -36,6 +36,7 @@ const char rcsid_config_h[] = "@(#)$KmKId: config.h,v 1.9 2004-11-12 23:10:28-05 #define CFGTYPE_DISK 3 #define CFGTYPE_FUNC 4 #define CFGTYPE_FILE 5 +#define CFGTYPE_STR 6 /* CFGTYPE limited to just 4 bits: 0-15 */ /* Cfg_menu, Cfg_dirent and Cfg_listhdr are defined in defc.h */ diff --git a/src/engine_c.c b/src/engine_c.c index 18e6d7e..1f40e2c 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -53,6 +53,9 @@ extern byte *g_memory_ptr; extern byte *g_rom_fc_ff_ptr; extern byte *g_rom_cards_ptr; extern byte *g_dummy_memory1_ptr; +extern int g_c068_statereg; +unsigned char ioslotsel = 0; +unsigned char iostrobe = 0; extern int g_num_breakpoints; extern word32 g_breakpts[]; @@ -152,12 +155,12 @@ extern Page_info page_info_rd_wr[]; extern word32 slow_mem_changed[]; #define GET_MEMORY8(addr,dest) \ - addr_latch = (addr); \ - CYCLES_PLUS_1; \ + addr_latch = (addr);\ + CYCLES_PLUS_1; \ stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ wstat = PTR2WORD(stat) & 0xff; \ ptr = stat - wstat + ((addr) & 0xff); \ - if(wstat & (1 << (31 - BANK_IO_BIT))) { \ + if(wstat & (1 << (31 - BANK_IO_BIT)) || iostrobe == 1) { \ fcycles_tmp1 = fcycles; \ dest = get_memory8_io_stub((addr), stat, \ &fcycles_tmp1, fplus_x_m1); \ @@ -374,13 +377,13 @@ get_memory8_io_stub(word32 addr, byte *stat, double *fcycs_ptr, double fcycles; word32 wstat; byte *ptr; - wstat = PTR2WORD(stat) & 0xff; + if(wstat & BANK_BREAK) { check_breakpoints(addr); } fcycles = *fcycs_ptr; - if(wstat & BANK_IO2_TMP) { + if(wstat & BANK_IO2_TMP || iostrobe == 1) { FCYCLES_ROUND; *fcycs_ptr = fcycles; return get_memory_io((addr), fcycs_ptr); @@ -403,7 +406,6 @@ get_memory16_pieces_stub(word32 addr, byte *stat, double *fcycs_ptr, word32 addr_latch; word32 ret; word32 tmp1; - fcycles = *fcycs_ptr; fplus_1 = fplus_ptr->plus_1; fplus_x_m1 = fplus_ptr->plus_x_minus_1; @@ -431,7 +433,6 @@ get_memory24_pieces_stub(word32 addr, byte *stat, double *fcycs_ptr, word32 ret; word32 tmp1; word32 tmp2; - fcycles = *fcycs_ptr; fplus_1 = fplus_ptr->plus_1; fplus_x_m1 = fplus_ptr->plus_x_minus_1; @@ -506,7 +507,6 @@ set_memory16_pieces_stub(word32 addr, word32 val, double *fcycs_ptr, double fcycles, fcycles_tmp1; word32 addrp1; word32 wstat; - fcycles = *fcycs_ptr; SET_MEMORY8(addr, val); addrp1 = addr + 1; @@ -598,7 +598,6 @@ set_memory_c(word32 addr, word32 val, int cycs) double fplus_1; double fplus_x_m1; word32 wstat; - fcycles = g_cur_dcycs - g_last_vbl_dcycs; fplus_1 = 0; fplus_x_m1 = 0; @@ -886,7 +885,7 @@ get_remaining_operands(word32 addr, word32 opcode, word32 psr, Fplus *fplus_ptr) FINISH(RET_C70D, 0); \ } \ } \ - if(wstat & (1 << (31 - BANK_IO2_BIT))) { \ + if(wstat & (1 << (31 - BANK_IO2_BIT)) || iostrobe == 1) { \ FCYCLES_ROUND; \ fcycles_tmp1 = fcycles; \ opcode = get_memory_io((addr), &fcycles_tmp1); \ diff --git a/src/joystick_driver.c b/src/joystick_driver.c index f1fcbbd..52006dc 100644 --- a/src/joystick_driver.c +++ b/src/joystick_driver.c @@ -179,6 +179,11 @@ joystick_init() } } + if (g_joystick_native_type1<0 && g_joystick_native_type2 <0) { + printf ("No joystick is attached\n"); + return; + } + for(i = 0; i < 4; i++) { g_paddle_val[i] = 32767; } diff --git a/src/kegs.sln b/src/kegs.sln new file mode 100644 index 0000000..3d35c04 --- /dev/null +++ b/src/kegs.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kegs", "kegs.vcproj", "{4E045654-5CE8-482F-AE4C-5DD99C943461}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4E045654-5CE8-482F-AE4C-5DD99C943461}.Debug|Win32.ActiveCfg = Debug|Win32 + {4E045654-5CE8-482F-AE4C-5DD99C943461}.Debug|Win32.Build.0 = Debug|Win32 + {4E045654-5CE8-482F-AE4C-5DD99C943461}.Debug|x64.ActiveCfg = Debug|x64 + {4E045654-5CE8-482F-AE4C-5DD99C943461}.Debug|x64.Build.0 = Debug|x64 + {4E045654-5CE8-482F-AE4C-5DD99C943461}.Release|Win32.ActiveCfg = Release|Win32 + {4E045654-5CE8-482F-AE4C-5DD99C943461}.Release|Win32.Build.0 = Release|Win32 + {4E045654-5CE8-482F-AE4C-5DD99C943461}.Release|x64.ActiveCfg = Release|x64 + {4E045654-5CE8-482F-AE4C-5DD99C943461}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/kegs.vcproj b/src/kegs.vcproj new file mode 100644 index 0000000..412f146 --- /dev/null +++ b/src/kegs.vcproj @@ -0,0 +1,1238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/kegs32.aps b/src/kegs32.aps new file mode 100644 index 0000000000000000000000000000000000000000..7e93301186ff4df49ce21c30500a8ae23aed8323 GIT binary patch literal 59964 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 NfOmdBz@v5J{{cYzFLnR` literal 0 HcmV?d00001 diff --git a/src/kegs32.ico b/src/kegs32.ico new file mode 100644 index 0000000000000000000000000000000000000000..f9f12279d09eaeeda64a3018bb6f307f4b110b19 GIT binary patch literal 8478 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 literal 0 HcmV?d00001 diff --git a/src/kegs32.rc b/src/kegs32.rc new file mode 100644 index 0000000..3920f48 --- /dev/null +++ b/src/kegs32.rc @@ -0,0 +1,209 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource." + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.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 + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDC_KEGS32 MENU +BEGIN + POPUP "&Emulator" + BEGIN + MENUITEM "&Set Disk Configuration\tALT-F1", ID_FILE_DISK + MENUITEM "Send CTRL Reset\tCTRL-BREAK", ID_FILE_SENDRESET + MENUITEM "Reboot\tCTRL-ALT-BREAK", ID_FILE_SENDREBOOT + MENUITEM SEPARATOR + MENUITEM "Flush Printer", ID_FILE_FLUSHPRINTER + MENUITEM "Toggle &Joystick", ID_FILE_JOYSTICK + MENUITEM "Toggle Debug Statistics", ID_FILE_DEBUGSTAT + MENUITEM SEPARATOR + MENUITEM "E&xit\tALT-F4", ID_FILE_EXIT + END + POPUP "&Help" + BEGIN + MENUITEM "Key Commands", ID_HELP_KEY + MENUITEM SEPARATOR + MENUITEM "&About", ID_HELP_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDC_KEGS32 BITMAP "wintoolbar.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCEL ACCELERATORS +BEGIN + VK_F1, ID_FILE_DISK, VIRTKEY, ALT, NOINVERT + VK_F4, ID_FILE_EXIT, VIRTKEY, ALT, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDC_KEGS32 ICON "kegs32.ico" +KEGS32_ICON ICON "kegs32.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUT_DIALOG DIALOGEX 0, 0, 207, 82 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,78,61,50,14 + LTEXT "KEGS32: GS Emulator.\nBased on KEGS by Kent Dickey\nWindows Port by Chea Chee Keong\n\nThis software is free for non-commercial use.",IDC_STATIC,38,7,162,45,NOT WS_GROUP + ICON "KEGS32_ICON",IDC_STATIC,7,7,20,20 +END + +IDD_DLG_DISKCONF DIALOGEX 0, 0, 268, 182 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Disk Configuration" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,150,161,50,14 + PUSHBUTTON "Cancel",IDCANCEL,203,161,50,14 + LTEXT "S5D1",IDC_STATIC,19,46,19,8 + EDITTEXT IDC_EDIT_S5D1,43,42,156,14,ES_AUTOHSCROLL,WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S5D1,203,42,50,14 + LTEXT "S5D2",IDC_STATIC,19,62,19,8 + EDITTEXT IDC_EDIT_S5D2,43,60,155,14,ES_AUTOHSCROLL,WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S5D2,203,60,50,14 + LTEXT "S6D1",IDC_STATIC,19,80,19,8 + EDITTEXT IDC_EDIT_S6D1,43,77,156,14,ES_AUTOHSCROLL,WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S6D1,203,77,50,14 + LTEXT "S6D2",IDC_STATIC,19,98,19,8 + EDITTEXT IDC_EDIT_S6D2,43,95,156,14,ES_AUTOHSCROLL,WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S6D2,203,96,50,14 + LTEXT "S7D1",IDC_STATIC,19,118,19,8 + EDITTEXT IDC_EDIT_S7D1,43,115,155,14,ES_AUTOHSCROLL,WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S7D1,203,115,50,14 + LTEXT "S7D2",IDC_STATIC,19,137,19,8 + EDITTEXT IDC_EDIT_S7D2,43,135,155,14,ES_AUTOHSCROLL,WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S7D2,203,135,50,14 + GROUPBOX "Disk settings",IDC_STATIC,7,7,254,148 + LTEXT "Configure your disk images for each drive. Disk image formats supported\nare *.2MG,*.PO and *.DSK. ",IDC_STATIC,19,20,234,16 +END + +IDD_SPEEDDIALOG DIALOG 0, 0, 240, 129 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Speed Control" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "1 MHz",IDC_SLOW,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,45,41,51,13 + CONTROL "2.5 MHz",IDC_NORMAL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,45,55,43,13 + CONTROL "As fast as possible",IDC_FASTEST,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,45,69,74,13 + CONTROL "Custom (MHz)",IDC_CUSTOM,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,45,83,69,13 + EDITTEXT IDC_EDITCUSTOM,117,83,79,13,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,133,107,50,15 + PUSHBUTTON "Cancel",IDCANCEL,183,107,50,15 + GROUPBOX "Speed Control",IDC_STATIC,7,7,226,96,WS_GROUP + LTEXT "Adjust the speed of your emulator by selecting the appropriate speed control",IDC_STATIC,46,19,181,19,NOT WS_GROUP + ICON IDC_KEGS32,IDC_STATIC,14,19,21,20 +END + +IDD_KEGS32_KEY DIALOGEX 0, 0, 186, 172 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Help About Key Commands" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,65,151,50,14 + LTEXT "KEGS32 Key Commands",IDC_STATIC,7,7,82,10 + LTEXT "Alt/F1\t\tOpen-Apple\nF2\t\tClose-Apple\nF7\t\tToggle Fast Disk Emulation\nF8\t\tToggle Mouse Pointer\n\t\tDisplay\n",IDC_STATIC,21,25,151,42 + LTEXT "F11\t\tToggle Fullscreen Display\nF12\t\tReset\nCtrl-Alt-Break\tReboot Emulator\nCtrl-Alt-Esc\tControl-Panel\nCtrl-Break\tReset Emulator",IDC_STATIC,21,66,141,50 + CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME | WS_DISABLED | WS_BORDER,7,17,172,1 + LTEXT "For more information, please consult the readme.kegs file",IDC_STATIC,21,124,144,19 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + ID_FILE_DISK "Disk Configuration" +END + +STRINGTABLE +BEGIN + ID_SPEED_1MHZ "Set Speed to 1 Mhz" + ID_SPEED_2MHZ "Set Speed to 2.5 Mhz" + ID_SPEED_FMHZ "Set Speed to as fast as possible" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/moremem.c b/src/moremem.c index 4833490..e18f616 100644 --- a/src/moremem.c +++ b/src/moremem.c @@ -30,6 +30,9 @@ extern byte *g_dummy_memory1_ptr; extern byte *g_slow_memory_ptr; extern byte *g_rom_fc_ff_ptr; extern byte *g_rom_cards_ptr; +extern byte *g_grappler_rom; +extern unsigned char ioslotsel; +extern unsigned char iostrobe; extern word32 slow_mem_changed[]; @@ -41,8 +44,9 @@ extern Page_info page_info_rd_wr[]; extern int Verbose; extern int g_rom_version; extern int g_user_page2_shadow; +extern int g_parallel; - +char c; /* from iwm.c */ int g_num_shadow_all_banks = 0; @@ -275,7 +279,7 @@ fixup_intcx() int start_k; word32 mask; int j, k; - + int test1; rom10000 = &(g_rom_fc_ff_ptr[0x30000]); no_io_shadow = (g_c035_shadow_reg & 0x40); @@ -311,25 +315,27 @@ fixup_intcx() } } for(j = 0xc8; j < 0xd0; j++) { - /* c800 - cfff */ - if(((g_c02d_int_crom & (1 << 3)) == 0) || INTCX) { - rom_inc = rom10000 + (j << 8); - } else { - /* c800 space not necessarily mapped */ - /* just map in ROM */ + + /*c800 - cfff */ + if(((g_c02d_int_crom & (1 << 3)) == 0) || INTCX) + { rom_inc = rom10000 + (j << 8); } + else + { + rom_inc = rom10000 + (j << 8); + + } SET_PAGE_INFO_RD(j + off, rom_inc); } + iostrobe = 0; for(j = 0xc0; j < 0xd0; j++) { SET_PAGE_INFO_WR(j + off, SET_BANK_IO); } } - if(!no_io_shadow) { SET_PAGE_INFO_RD(0xc7, SET_BANK_IO); /* smartport */ } - fixup_brks(); } @@ -1458,8 +1464,16 @@ io_read(word32 loc, double *cyc_ptr) case 0x94: case 0x95: case 0x96: case 0x97: case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f: - /* UNIMPL_READ; */ - return 0; + if (g_parallel) + { + return parallel_read((word16)loc & 0xf); + } + else + { + UNIMPL_READ; + } + + /* 0xc0a0 - 0xc0af */ case 0xa0: case 0xa1: case 0xa2: case 0xa3: case 0xa4: case 0xa5: case 0xa6: case 0xa7: @@ -1469,19 +1483,41 @@ io_read(word32 loc, double *cyc_ptr) /* UNIMPL_READ; */ /* 0xc0b0 - 0xc0bf */ - case 0xb0: + //case 0xb0: /* c0b0: female voice tool033 look at this */ - return 0; - case 0xb1: case 0xb2: case 0xb3: - case 0xb4: case 0xb5: case 0xb6: case 0xb7: - case 0xb9: case 0xba: case 0xbb: - case 0xbc: case 0xbd: case 0xbe: case 0xbf: + // return 0; + //case 0xb1: case 0xb2: case 0xb3: + //case 0xb4: case 0xb5: case 0xb6: case 0xb7: + //case 0xb9: case 0xba: case 0xbb: + //case 0xbc: case 0xbd: case 0xbe: case 0xbf: /* UNIMPL_READ; */ - return 0; + // return 0; /* c0b8: Second Sight card stuff: return 0 */ + //case 0xb8: + // return 0; + // break; + /*Uthernet read access on slot 3*/ + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: case 0xb8: - return 0; - break; + case 0xb9: + case 0xba: + case 0xbb: + case 0xbc: + case 0xbd: + case 0xbe: + case 0xbf: + if (tfe_enabled){ + return tfe_read((word16)loc & 0xf); + } + else + {return 0;} /* 0xc0c0 - 0xc0cf */ case 0xc0: case 0xc1: case 0xc2: case 0xc3: @@ -1537,12 +1573,13 @@ io_read(word32 loc, double *cyc_ptr) } UNIMPL_READ; case 0xf: + if((loc & 0xfff) == 0xfff) { + return g_rom_fc_ff_ptr[0x3cfff]; + } + if(INTCX || ((g_c02d_int_crom & (1 << 3)) == 0)) { return(g_rom_fc_ff_ptr[0x3c000 + (loc & 0xfff)]); } - if((loc & 0xfff) == 0xfff) { - return g_rom_fc_ff_ptr[0x3cfff]; - } UNIMPL_READ; } @@ -1569,6 +1606,7 @@ io_write(word32 loc, int val, double *cyc_ptr) val = val & 0xff; switch((loc >> 8) & 0xf) { case 0: /* 0xc000 - 0xc0ff */ + //printf ("ioaddress: %x", (loc & 0xf)); switch(loc & 0xff) { /* 0xc000 - 0xc00f */ case 0x00: /* 0xc000 */ @@ -2113,11 +2151,19 @@ io_write(word32 loc, int val, double *cyc_ptr) return; /* 0xc090 - 0xc09f */ - case 0x90: case 0x91: case 0x92: case 0x93: + 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: - UNIMPL_WRITE; + if (g_parallel) + { + return parallel_write((word16)loc & 0xf, (byte)val); + } + else + { + UNIMPL_WRITE; + } /* 0xc0a0 - 0xc0af */ case 0xa0: case 0xa1: case 0xa3: @@ -2128,17 +2174,43 @@ io_write(word32 loc, int val, double *cyc_ptr) case 0xa2: /* Burger Times writes here on error */ case 0xa8: /* Kurzweil SMP writes to 0xc0a8, ignore it */ + UNIMPL_WRITE; return; /* 0xc0b0 - 0xc0bf */ - case 0xb0: + //case 0xb0: /* Second sight stuff--ignore it */ return; - 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 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: + // UNIMPL_WRITE; + /*Uthernet write access on slot 3*/ + 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: + if (tfe_enabled) + { + return tfe_store((word16)loc & 0xf, (byte)val); + } + else + { UNIMPL_WRITE; + } /* 0xc0c0 - 0xc0cf */ case 0xc0: case 0xc1: case 0xc2: case 0xc3: diff --git a/src/objects.xib b/src/objects.xib index aa104c0..616ea10 100644 --- a/src/objects.xib +++ b/src/objects.xib @@ -9,12 +9,12 @@ main - GSPORTMAC + KEGSMAC - GSPORTMAC + KEGSMAC - About GSPORTMAC + About KEGSMAC 0 abou @@ -39,7 +39,7 @@ Configuration F4 0 KCFG - Enter GSport Configuration Panel + Enter KEGS Configuration Panel diff --git a/src/op_routs.h b/src/op_routs.h index ff89cb8..a8d6489 100644 --- a/src/op_routs.h +++ b/src/op_routs.h @@ -1,23 +1,12 @@ -/* - GSport - an Apple //gs Emulator - Copyright (C) 2010 by GSport contributors - - Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ #ifdef ASM # ifdef INCLUDE_RCSID_S diff --git a/src/paddles.c b/src/paddles.c index b3db782..0fcfe26 100644 --- a/src/paddles.c +++ b/src/paddles.c @@ -1,23 +1,12 @@ -/* - GSport - an Apple //gs Emulator - Copyright (C) 2010 by GSport contributors - - Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ const char rcsid_paddles_c[] = "@(#)$KmKId: paddles.c,v 1.14 2004-10-19 14:52:36-04 kentd Exp $"; diff --git a/src/parallel.c b/src/parallel.c new file mode 100644 index 0000000..08a4fc9 --- /dev/null +++ b/src/parallel.c @@ -0,0 +1,76 @@ +/* + GSport - an Apple //gs Emulator + Copyright (C) 2010 by GSport contributors + + Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* +parallel.c + +This file handles the Apple II Parallel Card emulation in slot 1. Its very +basic, but allows for future support of redirecting the output to a real +parallel port, files, and additional types of emulated printers. +*/ + +#include "defc.h" +#include "printer.h" +extern int g_parallel_out_masking; +extern int g_vbl_count; +extern int g_printer_timeout; +int printer_vbl_count = 0; +int port_block = 0; + +byte parallel_read(word16 io_address) +{ + printf("parallel card status called at %x\n", io_address); + //since we only have a virtual printer, always return state as "Ready" + return 0xff; +} +void parallel_write(word16 io_address, byte val) +{ + //Mask MSB if user has it set. + if(g_parallel_out_masking) { + val = val & 0xfe; + } + printf("parallel card called at %x\n", io_address); + //send a byte to the virtual printer + //By default all output to $C090 gets sent to the printer + if (io_address == 0x00) + { + port_block = 1; + printer_loop(val); + printer_vbl_count = g_vbl_count+(g_printer_timeout*60); + port_block = 0; + } + return; +} + +//This function handles the automatic timeout of the virtual printer if an +//application doesn't send a form feed at the end of the page. It also +//allows multipage mode Postscript and native printer documents to +//print somewhat how a regular application would. +void printer_update() +{ + if (port_block != 1 && printer_vbl_count != 0 && g_vbl_count >= printer_vbl_count) + { + printf("Calling printer_update and flushing!\n"); + printer_feed(); + printer_vbl_count = 0; + } + return; +} \ No newline at end of file diff --git a/src/parallel.rom b/src/parallel.rom new file mode 100644 index 0000000000000000000000000000000000000000..78360afc21ad57386a1315066fcf4ff5584665db GIT binary patch literal 256 zcmb2xVByi_F~ftSLLuV+t_&bpacWkE#;OSxdv~x3NY2o#_cS=kHNpC1#Rg4J+YgE} zH0RH?V4G04cL$q+$WoQN7VH~%PYQ0}Hb_d%8CeOyXJJ>D+ zFW{=RVC}f@Ywiw~xfZMz8=Q}-Ot8vWk+CYn=fmF#|2F)a*>GTjxhMMyPcE(tY&%+2 z=31}=jrS^G+MvwEwNib<`MnnGe|NB){4s$!Q+>@!hE^4cx*e=w6A~t +#include "support.h" +//#include "png.h" +//#pragma comment( lib, “libpng.lib” ) +//#pragma comment (lib, “zdll.lib” ) + +static CPrinter* defaultPrinter = NULL; + +#define PARAM16(I) (params[I+1]*256+params[I]) +#define PIXX ((Bitu)floor(curX*dpi+0.5)) +#define PIXY ((Bitu)floor(curY*dpi+0.5)) + +static Bit16u confdpi, confwidth, confheight; +static Bitu printer_timout; +static bool timeout_dirty; +static const char* document_path; +static char confoutputDevice[50]; +static bool confmultipageOutput; +extern "C" char* g_printer_font_roman; +extern "C" char* g_printer_font_sans; +extern "C" char* g_printer_font_courier; +extern "C" char* g_printer_font_prestige; +extern "C" char* g_printer_font_script; +extern "C" char* g_printer_font_ocra; +Bit8u paramc = '0'; + +#include "printer_charmaps.h" + +void CPrinter::FillPalette(Bit8u redmax, Bit8u greenmax, Bit8u bluemax, Bit8u colorID, SDL_Palette* pal) +{ + float red=redmax/30.9; + float green=greenmax/30.9; + float blue=bluemax/30.9; + + Bit8u colormask=colorID<<=5; + + for(int i = 0; i < 32;i++) { + pal->colors[i+colormask].r=255-(red*(float)i); + pal->colors[i+colormask].g=255-(green*(float)i); + pal->colors[i+colormask].b=255-(blue*(float)i); + } +} + +CPrinter::CPrinter(Bit16u dpi, Bit16u width, Bit16u height, char* output, bool multipageOutput) +{ + if (FT_Init_FreeType(&FTlib)) + { + page = NULL; + } + else + { + this->dpi = dpi; + this->output = output; + this->multipageOutput = multipageOutput; + + defaultPageWidth = (Real64)width/(Real64)10; + defaultPageHeight = (Real64)height/(Real64)10; + + // Create page + page = SDL_CreateRGBSurface( + SDL_SWSURFACE, + (Bitu)(defaultPageWidth*dpi), + (Bitu)(defaultPageHeight*dpi), + 8, + 0, + 0, + 0, + 0); + + // Set a grey palette + SDL_Palette* palette = page->format->palette; + + for (Bitu i=0; i<32; i++) + { + palette->colors[i].r =255; + palette->colors[i].g =255; + palette->colors[i].b =255; + } + // 0 = all white needed for logic 000 + FillPalette( 0, 0, 0, 1, palette); + // 1 = magenta* 001 + FillPalette( 0, 255, 0, 1, palette); + // 2 = cyan* 010 + FillPalette(255, 0, 0, 2, palette); + // 3 = "violet" 011 + FillPalette(255, 255, 0, 3, palette); + // 4 = yellow* 100 + FillPalette( 0, 0, 255, 4, palette); + // 5 = red 101 + FillPalette( 0, 255, 255, 5, palette); + // 6 = green 110 + FillPalette(255, 0, 255, 6, palette); + // 7 = black 111 + FillPalette(255, 255, 255, 7, palette); + + // yyyxxxxx bit pattern: yyy=color xxxxx = intensity: 31=max + // Printing colors on top of each other ORs them and gets the + // correct resulting color. + // i.e. magenta on blank page yyy=001 + // then yellow on magenta 001 | 100 = 101 = red + + color=COLOR_BLACK; + + curFont = NULL; + charRead = false; + autoFeed = false; + outputHandle = NULL; + + resetPrinter(); + + if (strcasecmp(output, "printer") == 0) + { +#if defined (WIN32) + // Show Print dialog to obtain a printer device context + + PRINTDLG pd; + pd.lStructSize = sizeof(PRINTDLG); + pd.hDevMode = (HANDLE) NULL; + pd.hDevNames = (HANDLE) NULL; + pd.Flags = PD_RETURNDC; + pd.hwndOwner = NULL; + pd.hDC = (HDC) NULL; + pd.nFromPage = 1; + pd.nToPage = 1; + pd.nMinPage = 0; + pd.nMaxPage = 0; + pd.nCopies = 1; + pd.hInstance = NULL; + pd.lCustData = 0L; + pd.lpfnPrintHook = (LPPRINTHOOKPROC) NULL; + pd.lpfnSetupHook = (LPSETUPHOOKPROC) NULL; + pd.lpPrintTemplateName = (LPCSTR) NULL; + pd.lpSetupTemplateName = (LPCSTR) NULL; + pd.hPrintTemplate = (HANDLE) NULL; + pd.hSetupTemplate = (HANDLE) NULL; + PrintDlg(&pd); + // TODO: what if user presses cancel? + printerDC = pd.hDC; +#endif + } + } +}; + +void CPrinter::resetPrinterHard() +{ + charRead = false; + resetPrinter(); +} + +void CPrinter::resetPrinter() +{ + color=COLOR_BLACK; + curX = curY = 0.0; + ESCSeen = false; + FSSeen = false; + ESCCmd = 0; + numParam = neededParam = 0; + topMargin = 0.0; + leftMargin = 0.0; + rightMargin = pageWidth = defaultPageWidth; + bottomMargin = pageHeight = defaultPageHeight; + lineSpacing = (Real64)1/6; + cpi = 10.0; + curCharTable = 1; + style = 0; + extraIntraSpace = 0.0; + printUpperContr = true; + bitGraph.remBytes = 0; + densk = 0; + densl = 1; + densy = 2; + densz = 3; + charTables[0] = 0; // Italics + charTables[1] = charTables[2] = charTables[3] = 437; + definedUnit = -1; + multipoint = false; + multiPointSize = 0.0; + multicpi = 0.0; + hmi = -1.0; + msb = 255; + numPrintAsChar = 0; + LQtypeFace = roman; + + selectCodepage(charTables[curCharTable]); + + updateFont(); + + newPage(false,true); + + // Default tabs => Each eight characters + for (Bitu i=0;i<32;i++) + horiztabs[i] = i*8*(1/(Real64)cpi); + numHorizTabs = 32; + + numVertTabs = 255; +} + + +CPrinter::~CPrinter(void) +{ + finishMultipage(); + if (page != NULL) + { + SDL_FreeSurface(page); + page = NULL; + FT_Done_FreeType(FTlib); + } +#if defined (WIN32) + DeleteDC(printerDC); +#endif +}; + +void CPrinter::selectCodepage(Bit16u cp) +{ + Bit16u *mapToUse = NULL; + + switch(cp) + { + case 0: // Italics, use cp437 + case 437: + mapToUse = (Bit16u*)&cp437Map; + break; + case 737: + mapToUse = (Bit16u*)&cp737Map; + break; + case 775: + mapToUse = (Bit16u*)&cp775Map; + break; + case 850: + mapToUse = (Bit16u*)&cp850Map; + break; + case 852: + mapToUse = (Bit16u*)&cp852Map; + break; + case 855: + mapToUse = (Bit16u*)&cp855Map; + break; + case 857: + mapToUse = (Bit16u*)&cp857Map; + break; + case 860: + mapToUse = (Bit16u*)&cp860Map; + break; + case 861: + mapToUse = (Bit16u*)&cp861Map; + break; + case 863: + mapToUse = (Bit16u*)&cp863Map; + break; + case 864: + mapToUse = (Bit16u*)&cp864Map; + break; + case 865: + mapToUse = (Bit16u*)&cp865Map; + break; + case 866: + mapToUse = (Bit16u*)&cp866Map; + break; + default: + //LOG(LOG_MISC,LOG_WARN)("Unsupported codepage %i. Using CP437 instead.", cp); + mapToUse = (Bit16u*)&cp437Map; + } + + for (int i=0; i<256; i++) + curMap[i] = mapToUse[i]; +} + +void CPrinter::updateFont() +{ + // char buffer[1000]; + if (curFont != NULL) + FT_Done_Face(curFont); + + char* fontName; + + switch (LQtypeFace) + { + case roman: + fontName = g_printer_font_roman; + break; + case sansserif: + fontName = g_printer_font_sans; + break; + case courier: + fontName = g_printer_font_courier; + break; + case prestige: + fontName = g_printer_font_prestige; + break; + case script: + fontName = g_printer_font_script; + break; + case ocra: + case ocrb: + fontName = g_printer_font_ocra; + break; + default: + fontName = g_printer_font_roman; + } + + if (FT_New_Face(FTlib, fontName, 0, &curFont)) + { + + printf("Unable to load font %s"); + //LOG_MSG("Unable to load font %s", fontName); + curFont = NULL; + } + + Real64 horizPoints = 10.5; + Real64 vertPoints = 10.5; + + if (!multipoint) { + actcpi = cpi; + /* + switch(style & (STYLE_CONDENSED|STYLE_PROP)) { + case STYLE_CONDENSED: // only condensed + if (cpi == 10.0) { + actcpi = 17.14; + horizPoints *= 10.0/17.14; + } else if(cpi == 12.0) { + actcpi = 20.0; + horizPoints *= 10.0/20.0; + vertPoints *= 10.0/12.0; + } else { + // ignored + } + break; + case STYLE_PROP|STYLE_CONDENSED: + horizPoints /= 2.0; + break; + case 0: // neither + case STYLE_PROP: // only proportional + horizPoints *= 10.0/cpi; + vertPoints *= 10.0/cpi; + break; + } + */ + if (!(style & STYLE_CONDENSED)) { + horizPoints *= 10.0/cpi; + vertPoints *= 10.0/cpi; + } + + if (!(style & STYLE_PROP)) { + if ((cpi == 10.0) && (style & STYLE_CONDENSED)) { + actcpi = 17.14; + horizPoints *= 10.0/17.14; + } + if ((cpi == 12.0) && (style & STYLE_CONDENSED)) { + actcpi = 20.0; + horizPoints *= 10.0/20.0; + vertPoints *= 10.0/12.0; + } + } else if (style & STYLE_CONDENSED) horizPoints /= 2.0; + + + if ((style & STYLE_DOUBLEWIDTH) || (style & STYLE_DOUBLEWIDTHONELINE)) { + actcpi /= 2.0; + horizPoints *= 2.0; + } + + if (style & STYLE_DOUBLEHEIGHT) vertPoints *= 2.0; + } else { // multipoint true + actcpi = multicpi; + horizPoints = vertPoints = multiPointSize; + } + + if ((style & STYLE_SUPERSCRIPT) || (style & STYLE_SUBSCRIPT)) { + horizPoints *= 2.0/3.0; + vertPoints *= 2.0/3.0; + actcpi /= 2.0/3.0; + } + + FT_Set_Char_Size(curFont, (Bit16u)horizPoints*64, (Bit16u)vertPoints*64, dpi, dpi); + + if (style & STYLE_ITALICS || charTables[curCharTable] == 0) + { + FT_Matrix matrix; + matrix.xx = 0x10000L; + matrix.xy = (FT_Fixed)(0.20 * 0x10000L); + matrix.yx = 0; + matrix.yy = 0x10000L; + FT_Set_Transform(curFont, &matrix, 0); + } +} + +bool CPrinter::processCommandChar(Bit8u ch) +{ + if (ESCSeen || FSSeen) + { + ESCCmd = ch; + if(FSSeen) ESCCmd |= 0x800; + ESCSeen = FSSeen = false; + numParam = 0; + + switch (ESCCmd) { + case 0x02: // Undocumented + case 0x0a: // Reverse line feed (ESC LF) + case 0x0c: // Return to top of current page (ESC FF) + case 0x0e: // Select double-width printing (one line) (ESC SO) + case 0x0f: // Select condensed printing (ESC SI) + case 0x23: // Cancel MSB control (ESC #) + case 0x30: // Select 1/8-inch line spacing (ESC 0) + case 0x31: // Select 7/60-inch line spacing (ESC 1) + case 0x32: // Select 1/6-inch line spacing (ESC 2) + case 0x34: // Select italic font (ESC 4) + case 0x35: // Cancel italic font (ESC 5) + case 0x36: // Enable printing of upper control codes (ESC 6) + case 0x37: // Enable upper control codes (ESC 7) + case 0x38: // Disable paper-out detector (ESC 8) + case 0x39: // Enable paper-out detector (ESC 9) + case 0x3c: // Unidirectional mode (one line) (ESC <) + case 0x3d: // Set MSB to 0 (ESC =) + case 0x3e: // Set MSB to 1 (ESC >) + case 0x40: // Initialize printer (ESC @) + case 0x45: // Select bold font (ESC E) + case 0x46: // Cancel bold font (ESC F) + case 0x47: // Select double-strike printing (ESC G) + case 0x48: // Cancel double-strike printing (ESC H) + case 0x4d: // Select 10.5-point, 12-cpi (ESC M) + case 0x4f: // Cancel bottom margin [conflict] (ESC O) + case 0x50: // Select 10.5-point, 10-cpi (ESC P) + case 0x54: // Cancel superscript/subscript printing (ESC T) + case 0x5e: // Enable printing of all character codes on next character (ESC ^) + case 0x67: // Select 10.5-point, 15-cpi (ESC g) + + case 0x834: // Select italic font (FS 4) (= ESC 4) + case 0x835: // Cancel italic font (FS 5) (= ESC 5) + case 0x846: // Select forward feed mode (FS F) + case 0x852: // Select reverse feed mode (FS R) + neededParam = 0; + break; + case 0x19: // Control paper loading/ejecting (ESC EM) + case 0x20: // Set intercharacter space (ESC SP) + case 0x21: // Master select (ESC !) + case 0x2b: // Set n/360-inch line spacing (ESC +) + case 0x2d: // Turn underline on/off (ESC -) + case 0x2f: // Select vertical tab channel (ESC /) + case 0x33: // Set n/180-inch line spacing (ESC 3) + case 0x41: // Set n/60-inch line spacing (ESC A) + case 0x43: // Set page length in lines (ESC C) + case 0x49: // Select character type and print pitch (ESC I) + case 0x4a: // Advance print position vertically (ESC J) + case 0x4e: // Set bottom margin (ESC N) + case 0x51: // Set right margin (ESC Q) + case 0x52: // Select an international character set (ESC R) + case 0x53: // Select superscript/subscript printing (ESC S) + case 0x55: // Turn unidirectional mode on/off (ESC U) + //case 0x56: // Repeat data (ESC V) + case 0x57: // Turn double-width printing on/off (ESC W) + case 0x61: // Select justification (ESC a) + case 0x66: // Absolute horizontal tab in columns [conflict] (ESC f) + case 0x68: // Select double or quadruple size (ESC h) + case 0x69: // Immediate print (ESC i) + case 0x6a: // Reverse paper feed (ESC j) + case 0x6b: // Select typeface (ESC k) + case 0x6c: // Set left margin (ESC 1) + case 0x70: // Turn proportional mode on/off (ESC p) + case 0x72: // Select printing color (ESC r) + case 0x73: // Low-speed mode on/off (ESC s) + case 0x74: // Select character table (ESC t) + case 0x77: // Turn double-height printing on/off (ESC w) + case 0x78: // Select LQ or draft (ESC x) + case 0x7e: // Select/Deselect slash zero (ESC ~) + + case 0x832: // Select 1/6-inch line spacing (FS 2) (= ESC 2) + case 0x833: // Set n/360-inch line spacing (FS 3) (= ESC +) + case 0x841: // Set n/60-inch line spacing (FS A) (= ESC A) + case 0x843: // Select LQ type style (FS C) (= ESC k) + case 0x845: // Select character width (FS E) + case 0x849: // Select character table (FS I) (= ESC t) + case 0x853: // Select High Speed/High Density elite pitch (FS S) + case 0x856: // Turn double-height printing on/off (FS V) (= ESC w) + neededParam = 1; + break; + case 0x24: // Set absolute horizontal print position (ESC $) + case 0x3f: // Reassign bit-image mode (ESC ?) + case 0x4b: // Select 60-dpi graphics (ESC K) + case 0x4c: // Select 120-dpi graphics (ESC L) + case 0x59: // Select 120-dpi, double-speed graphics (ESC Y) + case 0x5a: // Select 240-dpi graphics (ESC Z) + case 0x5c: // Set relative horizontal print position (ESC \) + case 0x63: // Set horizontal motion index (HMI) [conflict] (ESC c) + case 0x65: // Set vertical tab stops every n lines (ESC e) + case 0x85a: // Print 24-bit hex-density graphics (FS Z) + neededParam = 2; + break; + case 0x2a: // Select bit image (ESC *) + case 0x58: // Select font by pitch and point [conflict] (ESC X) + neededParam = 3; + break; + case 0x5b: // Select character height, width, line spacing + neededParam = 7; + break; + case 0x62: // Set vertical tabs in VFU channels (ESC b) + case 0x42: // Set vertical tabs (ESC B) + numVertTabs = 0; + return true; + case 0x44: // Set horizontal tabs (ESC D) + numHorizTabs = 0; + return true; + case 0x25: // Select user-defined set (ESC %) + case 0x26: // Define user-defined characters (ESC &) + case 0x3a: // Copy ROM to RAM (ESC :) + //LOG(LOG_MISC,LOG_ERROR)("User-defined characters not supported!"); + return true; + case 0x28: // Two bytes sequence + return true; + default: + /*LOG_MSG("PRINTER: Unknown command %c (%02Xh) %c , unable to skip parameters.", + (ESCCmd & 0x800)?"FS":"ESC",ESCCmd, ESCCmd);*/ + + neededParam = 0; + ESCCmd = 0; + return true; + } + + if (neededParam > 0) + return true; + } + + // Two bytes sequence + if (ESCCmd == '(') + { + ESCCmd = 0x200 + ch; + + switch (ESCCmd) + { + case 0x242: // Bar code setup and print (ESC (B) + case 0x25e: // Print data as characters (ESC (^) + neededParam = 2; + break; + case 0x255: // Set unit (ESC (U) + neededParam = 3; + break; + case 0x243: // Set page length in defined unit (ESC (C) + case 0x256: // Set absolute vertical print position (ESC (V) + case 0x276: // Set relative vertical print position (ESC (v) + neededParam = 4; + break; + case 0x274: // Assign character table (ESC (t) + case 0x22d: // Select line/score (ESC (-) + neededParam = 5; + break; + case 0x263: // Set page format (ESC (c) + neededParam = 6; + break; + default: + // ESC ( commands are always followed by a "number of parameters" word parameter + //LOG(LOG_MISC,LOG_ERROR) + printf("PRINTER: Skipping unsupported command ESC ( %c (%02X).", ESCCmd, ESCCmd); + neededParam = 2; + ESCCmd = 0x101; + return true; + } + + if (neededParam > 0) + return true; + } + + // Ignore VFU channel setting + if (ESCCmd == 0x62) { + ESCCmd = 0x42; + return true; + } + + // Collect vertical tabs + if (ESCCmd == 0x42) { + if (ch == 0 || (numVertTabs>0 && verttabs[numVertTabs-1] > (Real64)ch*lineSpacing)) // Done + ESCCmd = 0; + else + if (numVertTabs < 16) + verttabs[numVertTabs++] = (Real64)ch*lineSpacing; + } + + // Collect horizontal tabs + if (ESCCmd == 0x44) + { + if (ch == 0 || (numHorizTabs>0 && horiztabs[numHorizTabs-1] > (Real64)ch*(1/(Real64)cpi))) // Done + ESCCmd = 0; + else + if (numHorizTabs < 32) + horiztabs[numHorizTabs++] = (Real64)ch*(1/(Real64)cpi); + } + + if (numParam < neededParam) + { + params[numParam++] = ch; + + if (numParam < neededParam) + return true; + } + + if (ESCCmd != 0) + { + switch (ESCCmd) + { + case 0x02: // Undocumented + // Ignore + break; + case 0x0e: // Select double-width printing (one line) (ESC SO) + if (!multipoint) + { + hmi = -1; + style |= STYLE_DOUBLEWIDTHONELINE; + updateFont(); + } + break; + case 0x0f: // Select condensed printing (ESC SI) + if (!multipoint && (cpi!=15.0)) { + hmi = -1; + style |= STYLE_CONDENSED; + updateFont(); + } + break; + case 0x19: // Control paper loading/ejecting (ESC EM) + // We are not really loading paper, so most commands can be ignored + if (params[0] == 'R') + newPage(true,false); // TODO resetx? + break; + case 0x20: // Set intercharacter space (ESC SP) + if (!multipoint) + { + extraIntraSpace = (Real64)params[0] / (printQuality==QUALITY_DRAFT?120:180); + hmi = -1; + updateFont(); + } + break; + case 0x21: // Master select (ESC !) + cpi = params[0] & 0x01 ? 12:10; + + // Reset first seven bits + style &= 0xFF80; + if (params[0] & 0x02) + style |= STYLE_PROP; + if (params[0] & 0x04) + style |= STYLE_CONDENSED; + if (params[0] & 0x08) + style |= STYLE_BOLD; + if (params[0] & 0x10) + style |= STYLE_DOUBLESTRIKE; + if (params[0] & 0x20) + style |= STYLE_DOUBLEWIDTH; + if (params[0] & 0x40) + style |= STYLE_ITALICS; + if (params[0] & 0x80) + { + score = SCORE_SINGLE; + style |= STYLE_UNDERLINE; + } + + hmi = -1; + multipoint = false; + updateFont(); + break; + case 0x23: // Cancel MSB control (ESC #) + msb = 255; + break; + case 0x24: // Set absolute horizontal print position (ESC $) + { + Real64 unitSize = definedUnit; + if (unitSize < 0) + unitSize = (Real64)60.0; + + Real64 newX = leftMargin + ((Real64)PARAM16(0)/unitSize); + if (newX <= rightMargin) + curX = newX; + } + break; + case 0x85a: // Print 24-bit hex-density graphics (FS Z) + setupBitImage(40, PARAM16(0)); + break; + case 0x2a: // Select bit image (ESC *) + setupBitImage(params[0], PARAM16(1)); + break; + case 0x2b: // Set n/360-inch line spacing (ESC +) + case 0x833: // Set n/360-inch line spacing (FS 3) + lineSpacing = (Real64)params[0]/360; + break; + case 0x2d: // Turn underline on/off (ESC -) + if (params[0] == 0 || params[0] == 48) + style &= ~STYLE_UNDERLINE; + if (params[0] == 1 || params[0] == 49) + { + style |= STYLE_UNDERLINE; + score = SCORE_SINGLE; + } + updateFont(); + break; + case 0x2f: // Select vertical tab channel (ESC /) + // Ignore + break; + case 0x30: // Select 1/8-inch line spacing (ESC 0) + lineSpacing = (Real64)1/8; + break; + case 0x32: // Select 1/6-inch line spacing (ESC 2) + lineSpacing = (Real64)1/6; + break; + case 0x33: // Set n/180-inch line spacing (ESC 3) + lineSpacing = (Real64)params[0]/180; + break; + case 0x34: // Select italic font (ESC 4) + style |= STYLE_ITALICS; + updateFont(); + break; + case 0x35: // Cancel italic font (ESC 5) + style &= ~STYLE_ITALICS; + updateFont(); + break; + case 0x36: // Enable printing of upper control codes (ESC 6) + printUpperContr = true; + break; + case 0x37: // Enable upper control codes (ESC 7) + printUpperContr = false; + break; + case 0x3c: // Unidirectional mode (one line) (ESC <) + // We don't have a print head, so just ignore this + break; + case 0x3d: // Set MSB to 0 (ESC =) + msb = 0; + break; + case 0x3e: // Set MSB to 1 (ESC >) + msb = 1; + break; + case 0x3f: // Reassign bit-image mode (ESC ?) + if (params[0] == 75) + densk = params[1]; + if (params[0] == 76) + densl = params[1]; + if (params[0] == 89) + densy = params[1]; + if (params[0] == 90) + densz = params[1]; + break; + case 0x40: // Initialize printer (ESC @) + resetPrinter(); + break; + case 0x41: // Set n/60-inch line spacing + case 0x841: + lineSpacing = (Real64)params[0]/60; + break; + case 0x43: // Set page length in lines (ESC C) + if (params[0] != 0) + pageHeight = bottomMargin = (Real64)params[0] * lineSpacing; + else // == 0 => Set page length in inches + { + neededParam = 1; + numParam = 0; + ESCCmd = 0x100; + return true; + } + break; + case 0x45: // Select bold font (ESC E) + style |= STYLE_BOLD; + updateFont(); + break; + case 0x46: // Cancel bold font (ESC F) + style &= ~STYLE_BOLD; + updateFont(); + break; + case 0x47: // Select dobule-strike printing (ESC G) + style |= STYLE_DOUBLESTRIKE; + break; + case 0x48: // Cancel double-strike printing (ESC H) + style &= ~STYLE_DOUBLESTRIKE; + break; + case 0x4a: // Advance print position vertically (ESC J n) + curY += (Real64)((Real64)params[0] / 180); + if (curY > bottomMargin) + newPage(true,false); + break; + case 0x4b: // Select 60-dpi graphics (ESC K) + setupBitImage(densk, PARAM16(0)); + break; + case 0x4c: // Select 120-dpi graphics (ESC L) + setupBitImage(densl, PARAM16(0)); + break; + case 0x4d: // Select 10.5-point, 12-cpi (ESC M) + cpi = 12; + hmi = -1; + multipoint = false; + updateFont(); + break; + case 0x4e: // Set bottom margin (ESC N) + topMargin = 0.0; + bottomMargin = (Real64)params[0] * lineSpacing; + break; + case 0x4f: // Cancel bottom (and top) margin + topMargin = 0.0; + bottomMargin = pageHeight; + break; + case 0x50: // Select 10.5-point, 10-cpi (ESC P) + cpi = 10; + hmi = -1; + multipoint = false; + updateFont(); + break; + case 0x51: // Set right margin + rightMargin = (Real64)(params[0]-1.0) / (Real64)cpi; + break; + case 0x52: // Select an international character set (ESC R) + if (params[0] <= 13 || params[0] == 64) + { + if (params[0] == 64) + params[0] = 14; + + curMap[0x23] = intCharSets[params[0]][0]; + curMap[0x24] = intCharSets[params[0]][1]; + curMap[0x40] = intCharSets[params[0]][2]; + curMap[0x5b] = intCharSets[params[0]][3]; + curMap[0x5c] = intCharSets[params[0]][4]; + curMap[0x5d] = intCharSets[params[0]][5]; + curMap[0x5e] = intCharSets[params[0]][6]; + curMap[0x60] = intCharSets[params[0]][7]; + curMap[0x7b] = intCharSets[params[0]][8]; + curMap[0x7c] = intCharSets[params[0]][9]; + curMap[0x7d] = intCharSets[params[0]][10]; + curMap[0x7e] = intCharSets[params[0]][11]; + } + break; + case 0x53: // Select superscript/subscript printing (ESC S) + if (params[0] == 0 || params[0] == 48) + style |= STYLE_SUBSCRIPT; + if (params[0] == 1 || params[1] == 49) + style |= STYLE_SUPERSCRIPT; + updateFont(); + break; + case 0x54: // Cancel superscript/subscript printing (ESC T) + style &= 0xFFFF - STYLE_SUPERSCRIPT - STYLE_SUBSCRIPT; + updateFont(); + break; + case 0x55: // Turn unidirectional mode on/off (ESC U) + // We don't have a print head, so just ignore this + break; + case 0x57: // Turn double-width printing on/off (ESC W) + if (!multipoint) + { + hmi = -1; + if (params[0] == 0 || params[0] == 48) + style &= ~STYLE_DOUBLEWIDTH; + if (params[0] == 1 || params[0] == 49) + style |= STYLE_DOUBLEWIDTH; + updateFont(); + } + break; + case 0x58: // Select font by pitch and point (ESC X) + multipoint = true; + // Copy currently non-multipoint CPI if no value was set so far + if (multicpi == 0) + multicpi = cpi; + if (params[0] > 0) // Set CPI + { + if (params[0] == 1) // Proportional spacing + style |= STYLE_PROP; + else if (params[0] >= 5) + multicpi = (Real64)360 / (Real64)params[0]; + } + if (multiPointSize == 0) + multiPointSize = (Real64)10.5; + if (PARAM16(1) > 0) // Set points + multiPointSize = ((Real64)PARAM16(1)) / 2; + updateFont(); + break; + case 0x59: // Select 120-dpi, double-speed graphics (ESC Y) + setupBitImage(densy, PARAM16(0)); + break; + case 0x5a: // Select 240-dpi graphics (ESC Z) + setupBitImage(densz, PARAM16(0)); + break; + case 0x5c: // Set relative horizontal print position (ESC \) + { + Bit16s toMove = PARAM16(0); + Real64 unitSize = definedUnit; + if (unitSize < 0) + unitSize = (Real64)(printQuality==QUALITY_DRAFT?120.0:180.0); + curX += (Real64)((Real64)toMove / unitSize); + } + break; + case 0x61: // Select justification (ESC a) + // Ignore + break; + case 0x63: // Set horizontal motion index (HMI) (ESC c) + hmi = (Real64)PARAM16(0) / (Real64)360.0; + extraIntraSpace = 0.0; + break; + case 0x67: // Select 10.5-point, 15-cpi (ESC g) + cpi = 15; + hmi = -1; + multipoint = false; + updateFont(); + break; + case 0x846: // Select forward feed mode (FS F) - set reverse not implemented yet + if(lineSpacing < 0) lineSpacing *= -1; + break; + case 0x6a: // Reverse paper feed (ESC j) + { + Real64 reverse = (Real64)PARAM16(0) / (Real64)216.0; + reverse = curY - reverse; + if(reverse < leftMargin) curY = leftMargin; + else curY = reverse; + break; + } + case 0x6b: // Select typeface (ESC k) + paramc = params[0]-'0'; + //if (params[0] <= 11 || params[0] == 30 || params[0] == 31) + if (paramc <= 11 || paramc == 30 || paramc == 31) + LQtypeFace = (Typeface)paramc; + updateFont(); + break; + case 0x6c: // Set left margin (ESC l) + leftMargin = (Real64)(params[0]-1.0) / (Real64)cpi; + if (curX < leftMargin) + curX = leftMargin; + break; + case 0x70: // Turn proportional mode on/off (ESC p) + if (params[0] == 0 || params[0] == 48) + style &= (0xffff - STYLE_PROP); + if (params[0] == 1 || params[0] == 49) + { + style |= STYLE_PROP; + printQuality = QUALITY_LQ; + } + multipoint = false; + hmi = -1; + updateFont(); + break; + case 0x72: // Select printing color (ESC r) + + if(params[0]==0 || params[0] > 6) color = COLOR_BLACK; + else color = params[0]<<5; + break; + case 0x73: // Select low-speed mode (ESC s) + // Ignore + break; + case 0x74: // Select character table (ESC t) + case 0x849: // Select character table (FS I) + if (params[0] < 4) + curCharTable = params[0]; + if (params[0] >= 48 && params[0] <= 51) + curCharTable = params[0] - 48; + selectCodepage(charTables[curCharTable]); + updateFont(); + break; + case 0x77: // Turn double-height printing on/off (ESC w) + if (!multipoint) + { + if (params[0] == 0 || params[0] == 48) + style &= ~STYLE_DOUBLEHEIGHT; + if (params[0] == 1 || params[0] == 49) + style |= STYLE_DOUBLEHEIGHT; + updateFont(); + } + break; + case 0x78: // Select LQ or draft (ESC x) + if (params[0] == 0 || params[0] == 48) { + printQuality = QUALITY_DRAFT; + style |= STYLE_CONDENSED; + } + if (params[0] == 1 || params[0] == 49) { + printQuality = QUALITY_LQ; + style &= ~STYLE_CONDENSED; + } + hmi = -1; + updateFont(); + break; + case 0x100: // Set page length in inches (ESC C NUL) + pageHeight = (Real64)params[0]; + bottomMargin = pageHeight; + topMargin = 0.0; + break; + case 0x101: // Skip unsupported ESC ( command + neededParam = PARAM16(0); + numParam = 0; + break; + case 0x274: // Assign character table (ESC (t) + if (params[2] < 4 && params[3] < 16) + { + charTables[params[2]] = codepages[params[3]]; + //LOG_MSG("curr table: %d, p2: %d, p3: %d",curCharTable,params[2],params[3]); + if (params[2] == curCharTable) + selectCodepage(charTables[curCharTable]); + } + break; + case 0x22d: // Select line/score (ESC (-) + style &= ~(STYLE_UNDERLINE | STYLE_STRIKETHROUGH | STYLE_OVERSCORE); + score = params[4]; + if (score) + { + if (params[3] == 1) + style |= STYLE_UNDERLINE; + if (params[3] == 2) + style |= STYLE_STRIKETHROUGH; + if (params[3] == 3) + style |= STYLE_OVERSCORE; + } + updateFont(); + break; + case 0x242: // Bar code setup and print (ESC (B) + //LOG(LOG_MISC,LOG_ERROR)("PRINTER: Bardcode printing not supported"); + // Find out how many bytes to skip + neededParam = PARAM16(0); + numParam = 0; + break; + case 0x243: // Set page length in defined unit (ESC (C) + if (params[0] != 0 && definedUnit > 0) + { + pageHeight = bottomMargin = ((Real64)PARAM16(2)) * definedUnit; + topMargin = 0.0; + } + break; + case 0x255: // Set unit (ESC (U) + definedUnit = (Real64)params[2] / (Real64)3600; + break; + case 0x256: // Set absolute vertical print position (ESC (V) + { + Real64 unitSize = definedUnit; + if (unitSize < 0) + unitSize = (Real64)360.0; + Real64 newPos = topMargin + (((Real64)PARAM16(2)) * unitSize); + if (newPos > bottomMargin) + newPage(true,false); + else + curY = newPos; + } + break; + case 0x25e: // Print data as characters (ESC (^) + numPrintAsChar = PARAM16(0); + break; + case 0x263: // Set page format (ESC (c) + if (definedUnit > 0) + { + Real64 newTop, newBottom; + newTop = ((Real64)PARAM16(2)) * definedUnit; + newBottom = ((Real64)PARAM16(4)) * definedUnit; + if(newTop >= newBottom) break; + if(newTop < pageHeight) topMargin = newTop; + if(newBottom < pageHeight) bottomMargin = newBottom; + if(topMargin > curY) curY = topMargin; + //LOG_MSG("du %d, p1 %d, p2 %d, newtop %f, newbott %f, nt %f, nb %f, ph %f", + // (Bitu)definedUnit,PARAM16(2),PARAM16(4),topMargin,bottomMargin, + // newTop,newBottom,pageHeight); + } + break; + case 0x276: // Set relative vertical print position (ESC (v) + { + Real64 unitSize = definedUnit; + if (unitSize < 0) + unitSize = (Real64)360.0; + Real64 newPos = curY + ((Real64)((Bit16s)PARAM16(2)) * unitSize); + if (newPos > topMargin) + { + if (newPos > bottomMargin) + newPage(true,false); + else + curY = newPos; + } + } + break; + default: + if (ESCCmd < 0x100); + //LOG(LOG_MISC,LOG_WARN) + //LOG_MSG("PRINTER: Skipped unsupported command ESC %c (%02X)", ESCCmd, ESCCmd); + else; + //LOG(LOG_MISC,LOG_WARN) + //LOG_MSG("PRINTER: Skipped unsupported command ESC ( %c (%02X)", ESCCmd-0x200, ESCCmd-0x200); + } + + ESCCmd = 0; + return true; + } + + switch (ch) + { + case 0x00: // NUL is ignored by the printer + return true; + case 0x07: // Beeper (BEL) + // BEEEP! + return true; + case 0x08: // Backspace (BS) + { + Real64 newX = curX - (1/(Real64)actcpi); + if (hmi > 0) + newX = curX - hmi; + if (newX >= leftMargin) + curX = newX; + } + return true; + case 0x09: // Tab horizontally (HT) + { + // Find tab right to current pos + Real64 moveTo = -1; + for (Bit8u i=0; i curX) + moveTo = horiztabs[i]; + // Nothing found => Ignore + if (moveTo > 0 && moveTo < rightMargin) + curX = moveTo; + } + return true; + case 0x0b: // Tab vertically (VT) + if (numVertTabs == 0) // All tabs cancelled => Act like CR + curX = leftMargin; + else if (numVertTabs == 255) // No tabs set since reset => Act like LF + { + curX = leftMargin; + curY += lineSpacing; + if (curY > bottomMargin) + newPage(true,false); + } + else + { + // Find tab below current pos + Real64 moveTo = -1; + for (Bit8u i=0; i curY) + moveTo = verttabs[i]; + + // Nothing found => Act like FF + if (moveTo > bottomMargin || moveTo < 0) + newPage(true,false); + else + curY = moveTo; + } + if (style & STYLE_DOUBLEWIDTHONELINE) + { + style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE; + updateFont(); + } + return true; + case 0x0c: // Form feed (FF) + if (style & STYLE_DOUBLEWIDTHONELINE) + { + style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE; + updateFont(); + } + newPage(true,true); + return true; + case 0x0d: // Carriage Return (CR) + curX = leftMargin; + if (!autoFeed) + return true; + case 0x0a: // Line feed + if (style & STYLE_DOUBLEWIDTHONELINE) + { + style &= 0xFFFF - STYLE_DOUBLEWIDTHONELINE; + updateFont(); + } + curX = leftMargin; + curY += lineSpacing; + if (curY > bottomMargin) + newPage(true,false); + return true; + case 0x0e: //Select Real64-width printing (one line) (SO) + if (!multipoint) + { + hmi = -1; + style |= STYLE_DOUBLEWIDTHONELINE; + updateFont(); + } + return true; + case 0x0f: // Select condensed printing (SI) + if (!multipoint && (cpi!=15.0)) { + hmi = -1; + style |= STYLE_CONDENSED; + updateFont(); + } + return true; + case 0x11: // Select printer (DC1) + // Ignore + return true; + case 0x12: // Cancel condensed printing (DC2) + hmi = -1; + style &= ~STYLE_CONDENSED; + updateFont(); + return true; + case 0x13: // Deselect printer (DC3) + // Ignore + return true; + case 0x14: // Cancel double-width printing (one line) (DC4) + hmi = -1; + style &= ~STYLE_DOUBLEWIDTHONELINE; + updateFont(); + return true; + case 0x18: // Cancel line (CAN) + return true; + case 0x1b: // ESC + ESCSeen = true; + return true; + case 0x1c: // FS (IBM commands) + FSSeen = true; + return true; + default: + return false; + } + + return false; +} + +static void PRINTER_EventHandler(Bitu param); + +void CPrinter::newPage(bool save, bool resetx) +{ + //PIC_RemoveEvents(PRINTER_EventHandler); + if(printer_timout) timeout_dirty=false; + + if (save) + outputPage(); + + if(resetx) curX=leftMargin; + curY = topMargin; + + SDL_Rect rect; + rect.x = 0; + rect.y = 0; + rect.w = page->w; + rect.h = page->h; + SDL_FillRect(page, &rect, SDL_MapRGB(page->format, 255, 255, 255)); + + /*for(int i = 0; i < 256; i++) + { + *((Bit8u*)page->pixels+i)=i; + }*/ +} + +void CPrinter::printChar(Bit8u ch) +{ + charRead = true; + if (page == NULL) return; + // Don't think that DOS programs uses this but well: Apply MSB if desired + if (msb != 255) { + if (msb == 0) ch &= 0x7F; + if (msb == 1) ch |= 0x80; + } + + // Are we currently printing a bit graphic? + if (bitGraph.remBytes > 0) { + printBitGraph(ch); + return; + } + + // Print everything? + if (numPrintAsChar > 0) numPrintAsChar--; + else if (processCommandChar(ch)) return; + + // Do not print if no font is available + if (!curFont) return; + if(ch==0x1) ch=0x20; + + // Find the glyph for the char to render + FT_UInt index = FT_Get_Char_Index(curFont, curMap[ch]); + + // Load the glyph + FT_Load_Glyph(curFont, index, FT_LOAD_DEFAULT); + + // Render a high-quality bitmap + FT_Render_Glyph(curFont->glyph, FT_RENDER_MODE_NORMAL); + + Bit16u penX = PIXX + curFont->glyph->bitmap_left; + Bit16u penY = PIXY - curFont->glyph->bitmap_top + curFont->size->metrics.ascender/64; + + if (style & STYLE_SUBSCRIPT) penY += curFont->glyph->bitmap.rows / 2; + + // Copy bitmap into page + SDL_LockSurface(page); + + blitGlyph(curFont->glyph->bitmap, penX, penY, false); + blitGlyph(curFont->glyph->bitmap, penX+1, penY, true); + + // Doublestrike => Print the glyph a second time one pixel below + if (style & STYLE_DOUBLESTRIKE) { + blitGlyph(curFont->glyph->bitmap, penX, penY+1, true); + blitGlyph(curFont->glyph->bitmap, penX+1, penY+1, true); + } + + // Bold => Print the glyph a second time one pixel to the right + // or be a bit more bold... + if (style & STYLE_BOLD) { + blitGlyph(curFont->glyph->bitmap, penX+1, penY, true); + blitGlyph(curFont->glyph->bitmap, penX+2, penY, true); + blitGlyph(curFont->glyph->bitmap, penX+3, penY, true); + } + SDL_UnlockSurface(page); + + // For line printing + Bit16u lineStart = PIXX; + + // advance the cursor to the right + Real64 x_advance; + if (style & STYLE_PROP) + x_advance = (Real64)((Real64)(curFont->glyph->advance.x)/(Real64)(dpi*64)); + else { + if (hmi < 0) x_advance = 1/(Real64)actcpi; + else x_advance = hmi; + } + x_advance += extraIntraSpace; + curX += x_advance; + + // Draw lines if desired + if ((score != SCORE_NONE) && (style & + (STYLE_UNDERLINE|STYLE_STRIKETHROUGH|STYLE_OVERSCORE))) + { + // Find out where to put the line + Bit16u lineY = PIXY; + double height = (curFont->size->metrics.height>>6); // TODO height is fixed point madness... + + if (style & STYLE_UNDERLINE) lineY = PIXY + (Bit16u)(height*0.9); + else if (style & STYLE_STRIKETHROUGH) lineY = PIXY + (Bit16u)(height*0.45); + else if (style & STYLE_OVERSCORE) + lineY = PIXY - (((score == SCORE_DOUBLE)||(score == SCORE_DOUBLEBROKEN))?5:0); + + drawLine(lineStart, PIXX, lineY, score==SCORE_SINGLEBROKEN || score==SCORE_DOUBLEBROKEN); + + // draw second line if needed + if ((score == SCORE_DOUBLE)||(score == SCORE_DOUBLEBROKEN)) + drawLine(lineStart, PIXX, lineY + 5, score==SCORE_SINGLEBROKEN || score==SCORE_DOUBLEBROKEN); + } + // If the next character would go beyond the right margin, line-wrap. + if((curX + x_advance) > rightMargin) { + curX = leftMargin; + curY += lineSpacing; + if (curY > bottomMargin) newPage(true,false); + } +} + +void CPrinter::blitGlyph(FT_Bitmap bitmap, Bit16u destx, Bit16u desty, bool add) { + for (Bitu y=0; y 0 && (destx+x < page->w) && (desty+y < page->h) ) { + Bit8u* target = (Bit8u*)page->pixels + (x+destx) + (y+desty)*page->pitch; + source>>=3; + + if (add) { + if (((*target)&0x1f )+ source > 31) *target |= (color|0x1f); + else { + *target += source; + *target |= color; + } + } + else *target = source|color; + } + } + } +} + +void CPrinter::drawLine(Bitu fromx, Bitu tox, Bitu y, bool broken) +{ + SDL_LockSurface(page); + + Bitu breakmod = dpi / 15; + Bitu gapstart = (breakmod * 4)/5; + + // Draw anti-aliased line + for (Bitu x=fromx; x<=tox; x++) + { + // Skip parts if broken line or going over the border + if ((!broken || (x%breakmod <= gapstart)) && (x < page->w)) + { + if (y > 0 && (y-1) < page->h) + *((Bit8u*)page->pixels + x + (y-1)*page->pitch) = 240; + if (y < page->h) + *((Bit8u*)page->pixels + x + y*page->pitch) = !broken?255:240; + if (y+1 < page->h) + *((Bit8u*)page->pixels + x + (y+1)*page->pitch) = 240; + } + } + SDL_UnlockSurface(page); +} + +void CPrinter::setAutofeed(bool feed) { + autoFeed = feed; +} + +bool CPrinter::getAutofeed() { + return autoFeed; +} + +bool CPrinter::isBusy() { + // We're never busy + return false; +} + +bool CPrinter::ack() { + // Acknowledge last char read + if(charRead) { + charRead=false; + return true; + } + return false; +} + +void CPrinter::setupBitImage(Bit8u dens, Bit16u numCols) { + switch (dens) + { + case 0: + bitGraph.horizDens = 60; + bitGraph.vertDens = 60; + bitGraph.adjacent = true; + bitGraph.bytesColumn = 1; + break; + case 1: + bitGraph.horizDens = 120; + bitGraph.vertDens = 60; + bitGraph.adjacent = true; + bitGraph.bytesColumn = 1; + break; + case 2: + bitGraph.horizDens = 120; + bitGraph.vertDens = 60; + bitGraph.adjacent = false; + bitGraph.bytesColumn = 1; + break; + case 3: + bitGraph.horizDens = 60; + bitGraph.vertDens = 240; + bitGraph.adjacent = false; + bitGraph.bytesColumn = 1; + break; + case 4: + bitGraph.horizDens = 80; + bitGraph.vertDens = 60; + bitGraph.adjacent = true; + bitGraph.bytesColumn = 1; + break; + case 6: + bitGraph.horizDens = 90; + bitGraph.vertDens = 60; + bitGraph.adjacent = true; + bitGraph.bytesColumn = 1; + break; + case 32: + bitGraph.horizDens = 60; + bitGraph.vertDens = 180; + bitGraph.adjacent = true; + bitGraph.bytesColumn = 3; + break; + case 33: + bitGraph.horizDens = 120; + bitGraph.vertDens = 180; + bitGraph.adjacent = true; + bitGraph.bytesColumn = 3; + break; + case 38: + bitGraph.horizDens = 90; + bitGraph.vertDens = 180; + bitGraph.adjacent = true; + bitGraph.bytesColumn = 3; + break; + case 39: + bitGraph.horizDens = 180; + bitGraph.vertDens = 180; + bitGraph.adjacent = true; + bitGraph.bytesColumn = 3; + break; + case 40: + bitGraph.horizDens = 360; + bitGraph.vertDens = 180; + bitGraph.adjacent = false; + bitGraph.bytesColumn = 3; + break; + case 71: + bitGraph.horizDens = 180; + bitGraph.vertDens = 360; + bitGraph.adjacent = true; + bitGraph.bytesColumn = 6; + break; + case 72: + bitGraph.horizDens = 360; + bitGraph.vertDens = 360; + bitGraph.adjacent = false; + bitGraph.bytesColumn = 6; + break; + case 73: + bitGraph.horizDens = 360; + bitGraph.vertDens = 360; + bitGraph.adjacent = true; + bitGraph.bytesColumn = 6; + break; + default: + //break; + printf("PRINTER: Unsupported bit image density"); + } + + bitGraph.remBytes = numCols * bitGraph.bytesColumn; + bitGraph.readBytesColumn = 0; +} + +void CPrinter::printBitGraph(Bit8u ch) +{ + bitGraph.column[bitGraph.readBytesColumn++] = ch; + bitGraph.remBytes--; + + // Only print after reading a full column + if (bitGraph.readBytesColumn < bitGraph.bytesColumn) + return; + + Real64 oldY = curY; + + SDL_LockSurface(page); + + // When page dpi is greater than graphics dpi, the drawn pixels get "bigger" + Bitu pixsizeX=1; + Bitu pixsizeY=1; + if(bitGraph.adjacent) { + pixsizeX = dpi/bitGraph.horizDens > 0? dpi/bitGraph.horizDens : 1; + pixsizeY = dpi/bitGraph.vertDens > 0? dpi/bitGraph.vertDens : 1; + } + // TODO figure this out for 360dpi mode in windows + +// Bitu pixsizeX = dpi/bitGraph.horizDens > 0? dpi/bitGraph.horizDens : 1; +// Bitu pixsizeY = dpi/bitGraph.vertDens > 0? dpi/bitGraph.vertDens : 1; + + for (Bitu i=0; i>=1) { // for each bit + if (bitGraph.column[i] & j) { + for (Bitu xx=0; xxw) && ((PIXY + yy) < page->h)) + *((Bit8u*)page->pixels + (PIXX+xx) + (PIXY+yy)*page->pitch) |= (color|0x1F); + } + } // else white pixel + + curY += (Real64)1/(Real64)bitGraph.vertDens; // TODO line wrap? + } + } + SDL_UnlockSurface(page); + + curY = oldY; + + bitGraph.readBytesColumn = 0; + + // Advance to the left + curX += (Real64)1/(Real64)bitGraph.horizDens; +} + +void CPrinter::formFeed() +{ + // Don't output blank pages + newPage(!isBlank(),true); + + finishMultipage(); +} + +static void findNextName(char* front, char* ext, char* fname) +{ + document_path = ""; + Bitu i = 1; + Bitu slen = strlen(document_path); + if(slen>(200-15)) { + fname[0]=0; + return; + } + FILE *test = NULL; + do + { + strcpy(fname, document_path); + printf(fname); +#ifdef WIN32 + const char* const pathstring = ".\\%s%d%s"; +#else + const char* const pathstring = "/%s%d%s"; +#endif + sprintf(fname+strlen(fname), pathstring, front,i++,ext); + test = fopen(fname, "rb"); + if (test != NULL) + fclose(test); + } + while (test != NULL ); +} + +void CPrinter::outputPage() +{ +/* SDL_Surface *screen; + +screen = SDL_SetVideoMode(1024, 768, 16, SDL_DOUBLEBUF | SDL_RESIZABLE); +if (screen == NULL) { + printf("Unable to set video mode: %s\n", SDL_GetError()); +} +SDL_Surface *image; +SDL_LockSurface(page); +image = SDL_DisplayFormat(page); +SDL_UnlockSurface(page); +SDL_Rect src, dest; + +src.x = 0; +src.y = 0; +src.w = image->w; +src.h = image->h; + +dest.x = 100; +dest.y = 100; +dest.w = image->w; +dest.h = image->h; + +SDL_BlitSurface(image, &src, screen, &dest); +SDL_Flip(screen); + +SDL_Delay(2000); +SDL_FreeSurface(image);*/ + + char fname[200]; + if (strcasecmp(output, "printer") == 0) + { +#if defined (WIN32) + + Bit16u physW = GetDeviceCaps(printerDC, PHYSICALWIDTH); + Bit16u physH = GetDeviceCaps(printerDC, PHYSICALHEIGHT); + + Real64 scaleW, scaleH; + + if (page->w > physW) + scaleW = (Real64)page->w / (Real64)physW; + else + scaleW = (Real64)physW / (Real64)page->w; + + if (page->h > physH) + scaleH = (Real64)page->h / (Real64)physH; + else + scaleH = (Real64)physH / (Real64)page->h; + + HDC memHDC = CreateCompatibleDC(printerDC); + BITMAPINFO *BitmapInfo; + HBITMAP bitmap; + + // Start new printer job? + if (outputHandle == NULL) + { + DOCINFO docinfo; + docinfo.cbSize = sizeof(docinfo); + docinfo.lpszDocName = "KEGS Virtual Printer"; + docinfo.lpszOutput = NULL; + docinfo.lpszDatatype = NULL; + docinfo.fwType = 0; + + StartDoc(printerDC, &docinfo); + multiPageCounter = 1; + } + SDL_LockSurface(page); + StartPage(printerDC); + DWORD TotalSize; + HGDIOBJ Prev; + void* Pixels; + BitmapInfo = (BITMAPINFO*) + malloc (sizeof (BITMAPINFO)+255*sizeof (RGBQUAD)); + memset (BitmapInfo,0,sizeof (bitmap)); + BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + BitmapInfo->bmiHeader.biWidth = page->w; + BitmapInfo->bmiHeader.biHeight = -page->h; + BitmapInfo->bmiHeader.biPlanes = 1; + BitmapInfo->bmiHeader.biBitCount = page->format->BitsPerPixel; + BitmapInfo->bmiHeader.biCompression = BI_RGB; + BitmapInfo->bmiHeader.biSizeImage = page->h * page->pitch; + BitmapInfo->bmiHeader.biXPelsPerMeter = 0; + BitmapInfo->bmiHeader.biYPelsPerMeter = 0; + BitmapInfo->bmiHeader.biClrUsed = page->format->palette->ncolors; + BitmapInfo->bmiHeader.biClrImportant = 0; + if (page->format->palette) { + for (int I=0; Iformat->palette->ncolors; I++) { + BitmapInfo->bmiColors[I].rgbRed = + (page->format->palette->colors+I)->r; + BitmapInfo->bmiColors[I].rgbGreen = + (page->format->palette->colors+I)->g; + BitmapInfo->bmiColors[I].rgbBlue = + (page->format->palette->colors+I)->b; + } + } + memHDC = CreateCompatibleDC(printerDC); + if (memHDC) { + bitmap = CreateDIBSection(memHDC, BitmapInfo, DIB_RGB_COLORS, + (&Pixels), NULL, 0); + if (bitmap) { + memcpy (Pixels, page->pixels, + BitmapInfo->bmiHeader.biSizeImage); + Prev = SelectObject (memHDC, bitmap); + StretchBlt(printerDC, 0, 0, physW, physH, memHDC, 0, 0, page->w, page->h, SRCCOPY); + SelectObject (memHDC,Prev); + DeleteObject (bitmap); + } + } + free (BitmapInfo); + SDL_UnlockSurface(page); + EndPage(printerDC); + + if (multipageOutput) + { + multiPageCounter++; + outputHandle = printerDC; + } + else + { + EndDoc(printerDC); + outputHandle = NULL; + } + DeleteObject(bitmap); + DeleteDC(memHDC); +#else + //LOG_MSG("PRINTER: Direct printing not supported under this OS"); +#endif + } +#ifdef C_LIBPNG + else if (strcasecmp(output, "png") == 0) + { + // Find a page that does not exists + findNextName("page", ".png", &fname[0]); + + png_structp png_ptr; + png_infop info_ptr; + png_bytep * row_pointers; + png_color palette[256]; + Bitu i; + + /* Open the actual file */ + FILE * fp=fopen(fname,"wb"); + if (!fp) + { + //LOG(LOG_MISC,LOG_ERROR)("PRINTER: Can't open file %s for printer output", fname); + return; + } + + /* First try to alloacte the png structures */ + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL, NULL); + if (!png_ptr) return; + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr,(png_infopp)NULL); + return; + } + + /* 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); + + + png_set_IHDR(png_ptr, info_ptr, page->w, page->h, + 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 = page->format->palette->colors[i].r; + palette[i].green = page->format->palette->colors[i].g; + palette[i].blue = page->format->palette->colors[i].b; + } + png_set_PLTE(png_ptr, info_ptr, palette,256); + png_set_packing(png_ptr); + SDL_LockSurface(page); + + // Allocate an array of scanline pointers + row_pointers = (png_bytep*)malloc(page->h*sizeof(png_bytep)); + for (i=0; ih; i++) + row_pointers[i] = ((Bit8u*)page->pixels+(i*page->pitch)); + + // tell the png library what to encode. + png_set_rows(png_ptr, info_ptr, row_pointers); + + // Write image to file + png_write_png(png_ptr, info_ptr, 0, NULL); + + + + + SDL_UnlockSurface(page); + + /*close file*/ + fclose(fp); + + /*Destroy PNG structs*/ + png_destroy_write_struct(&png_ptr, &info_ptr); + + /*clean up dynamically allocated RAM.*/ + free(row_pointers); + } +#endif + else if (strcasecmp(output, "ps") == 0) + { + FILE* psfile = NULL; + + // Continue postscript file? + if (outputHandle != NULL) + psfile = (FILE*)outputHandle; + + // Create new file? + if (psfile == NULL) + { + if (!multipageOutput) + findNextName("page", ".ps", &fname[0]); + else + findNextName("doc", ".ps", &fname[0]); + + psfile = fopen(fname, "wb"); + if (!psfile) + { + //LOG(LOG_MISC,LOG_ERROR)("PRINTER: Can't open file %s for printer output", fname); + return; + } + + // Print header + fprintf(psfile, "%%!PS-Adobe-3.0\n"); + fprintf(psfile, "%%%%Pages: (atend)\n"); + fprintf(psfile, "%%%%BoundingBox: 0 0 %i %i\n", (Bit16u)(defaultPageWidth*74), (Bit16u)(defaultPageHeight*74)); + fprintf(psfile, "%%%%Creator: KEGS Virtual Printer\n"); + fprintf(psfile, "%%%%DocumentData: Clean7Bit\n"); + fprintf(psfile, "%%%%LanguageLevel: 2\n"); + fprintf(psfile, "%%%%EndComments\n"); + multiPageCounter = 1; + } + + fprintf(psfile, "%%%%Page: %i %i\n", multiPageCounter, multiPageCounter); + fprintf(psfile, "%i %i scale\n", (Bit16u)(defaultPageWidth*74), (Bit16u)(defaultPageHeight*74)); + fprintf(psfile, "%i %i 8 [%i 0 0 -%i 0 %i]\n", page->w, page->h, page->w, page->h, page->h); + fprintf(psfile, "currentfile\n"); + fprintf(psfile, "/ASCII85Decode filter\n"); + fprintf(psfile, "/RunLengthDecode filter\n"); + fprintf(psfile, "image\n"); + + SDL_LockSurface(page); + + Bit32u pix = 0; + Bit32u numpix = page->h*page->w; + ASCII85BufferPos = ASCII85CurCol = 0; + + while (pix < numpix) + { + // Compress data using RLE + + if ((pix < numpix-2) && (getPixel(pix) == getPixel(pix+1)) && (getPixel(pix) == getPixel(pix+2))) + { + // Found three or more pixels with the same color + Bit8u sameCount = 3; + Bit8u col = getPixel(pix); + while (sameCount < 128 && sameCount+pix < numpix && col == getPixel(pix+sameCount)) + sameCount++; + + fprintASCII85(psfile, 257-sameCount); + fprintASCII85(psfile, 255-col); + + // Skip ahead + pix += sameCount; + } + else + { + // Find end of heterogenous area + Bit8u diffCount = 1; + while (diffCount < 128 && diffCount+pix < numpix && + ( + (diffCount+pix < numpix-2) + || (getPixel(pix+diffCount) != getPixel(pix+diffCount+1)) + || (getPixel(pix+diffCount) != getPixel(pix+diffCount+2)) + )) + diffCount++; + + fprintASCII85(psfile, diffCount-1); + for (Bit8u i=0; i= 79) + { + ASCII85CurCol = 0; + fprintf(f, "\n"); + } + } + else + { + char buffer[5]; + for (Bit8s i=4; i>=0; i--) + { + buffer[i] = (Bit8u)((Bit32u)num % (Bit32u)85); + buffer[i] += 33; + num /= (Bit32u)85; + } + + // Make sure a line never starts with a % (which may be mistaken as start of a comment) + if (ASCII85CurCol == 0 && buffer[0] == '%') + fprintf(f, " "); + + for (int i=0; i<((b != 257)?5:ASCII85BufferPos+1); i++) + { + fprintf(f, "%c", buffer[i]); + if (++ASCII85CurCol >= 79) + { + ASCII85CurCol = 0; + fprintf(f, "\n"); + } + } + } + + ASCII85BufferPos = 0; + } + + } + else // Close string + { + // Partial tupel if there are still bytes in the buffer + if (ASCII85BufferPos > 0) + { + for (Bit8u i = ASCII85BufferPos; i < 4; i++) + ASCII85Buffer[i] = 0; + + fprintASCII85(f, 257); + } + + fprintf(f, "~"); + fprintf(f, ">\n"); + } +} + +void CPrinter::finishMultipage() +{ + if (outputHandle != NULL) + { + if (strcasecmp(output, "ps") == 0) + { + FILE* psfile = (FILE*)outputHandle; + fprintf(psfile, "%%%%Pages: %i\n", multiPageCounter); + fprintf(psfile, "%%%%EOF\n"); + fclose(psfile); + } + else if (strcasecmp(output, "printer") == 0) + { +#if defined (WIN32) + EndDoc(printerDC); +#endif + } + outputHandle = NULL; + } +} + +bool CPrinter::isBlank() { + bool blank = true; + SDL_LockSurface(page); + + for (Bit16u y=0; yh; y++) + for (Bit16u x=0; xw; x++) + if (*((Bit8u*)page->pixels + x + (y*page->pitch)) != 0) + blank = false; + + SDL_UnlockSurface(page); + return blank; +} + +Bit8u CPrinter::getPixel(Bit32u num) { + Bit32u pixel = *((Bit8u*)page->pixels + (num % page->w) + ((num / page->w) * page->pitch)); + return *((Bit8u*)page->pixels + (num % page->w) + ((num / page->w) * page->pitch)); +} + +//Interfaces to C code + + +extern "C" void printer_init(int pdpi, int pwidth, int pheight, char* poutput, bool mpage) +{ + if (defaultPrinter != NULL) return; + defaultPrinter = new CPrinter(pdpi, pwidth,pheight, poutput, mpage); +} +extern "C" void printer_loop(Bit8u pchar) +{ + if (defaultPrinter == NULL) return; + defaultPrinter->printChar(pchar); +} + +extern "C" void printer_close() +{ + delete defaultPrinter; + defaultPrinter = NULL; +} +extern "C" void printer_feed() +{ + if(defaultPrinter == NULL) return; + defaultPrinter->formFeed(); +} \ No newline at end of file diff --git a/src/printer.h b/src/printer.h new file mode 100644 index 0000000..7bf7812 --- /dev/null +++ b/src/printer.h @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2002-2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +//#include +//#include "config.h" + + + +#if !defined __PRINTER_H +#define __PRINTER_H +#ifdef __cplusplus +#ifdef C_LIBPNG +#include +#endif + +#include "SDL.h" + +#include +#include FT_FREETYPE_H + +#if defined (WIN32) +#include +#include +#endif + +#define STYLE_PROP 0x01 +#define STYLE_CONDENSED 0x02 +#define STYLE_BOLD 0x04 +#define STYLE_DOUBLESTRIKE 0x08 +#define STYLE_DOUBLEWIDTH 0x10 +#define STYLE_ITALICS 0x20 +#define STYLE_UNDERLINE 0x40 +#define STYLE_SUPERSCRIPT 0x80 +#define STYLE_SUBSCRIPT 0x100 +#define STYLE_STRIKETHROUGH 0x200 +#define STYLE_OVERSCORE 0x400 +#define STYLE_DOUBLEWIDTHONELINE 0x800 +#define STYLE_DOUBLEHEIGHT 0x1000 + +#define SCORE_NONE 0x00 +#define SCORE_SINGLE 0x01 +#define SCORE_DOUBLE 0x02 +#define SCORE_SINGLEBROKEN 0x05 +#define SCORE_DOUBLEBROKEN 0x06 + +#define QUALITY_DRAFT 0x01 +#define QUALITY_LQ 0x02 + +#define COLOR_BLACK 7<<5 +typedef unsigned Bitu; +typedef signed Bits; +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 double Real64; +#if defined(_MSC_VER) +typedef unsigned __int64 Bit64u; +typedef signed __int64 Bit64s; +#else +typedef unsigned long long int Bit64u; +typedef signed long long int Bit64s; +#endif +enum Typeface +{ + roman = 0, + sansserif, + courier, + prestige, + script, + ocrb, + ocra, + orator, + orators, + scriptc, + romant, + sansserifh, + svbusaba = 30, + svjittra = 31 +}; + +typedef struct { + Bitu codepage; + const Bit16u* map; +} CHARMAP; + + +class CPrinter { +public: + + CPrinter (Bit16u dpi, Bit16u width, Bit16u height, char* output, bool multipageOutput); + virtual ~CPrinter(); + + // Process one character sent to virtual printer + void printChar(Bit8u ch); + + // Hard Reset (like switching printer off and on) + void resetPrinterHard(); + + // Set Autofeed value + void setAutofeed(bool feed); + + // Get Autofeed value + bool getAutofeed(); + + // True if printer is unable to process more data right now (do not use printChar) + bool isBusy(); + + // True if the last sent character was received + bool ack(); + + // Manual formfeed + void formFeed(); + + // Returns true if the current page is blank + bool isBlank(); + +private: + + // used to fill the color "sub-pallettes" + void FillPalette(Bit8u redmax, Bit8u greenmax, Bit8u bluemax, Bit8u colorID, + SDL_Palette* pal); + + // Checks if given char belongs to a command and process it. If false, the character + // should be printed + bool processCommandChar(Bit8u ch); + + // Resets the printer to the factory settings + void resetPrinter(); + + // Reload font. Must be called after changing dpi, style or cpi + void updateFont(); + + // Clears page. If save is true, saves the current page to a bitmap + void newPage(bool save, bool resetx); + + // Blits the given glyph on the page surface. If add is true, the values of bitmap are + // added to the values of the pixels in the page + void blitGlyph(FT_Bitmap bitmap, Bit16u destx, Bit16u desty, bool add); + + // Draws an anti-aliased line from (fromx, y) to (tox, y). If broken is true, gaps are included + void drawLine(Bitu fromx, Bitu tox, Bitu y, bool broken); + + // Setup the bitGraph structure + void setupBitImage(Bit8u dens, Bit16u numCols); + + // Process a character that is part of bit image. Must be called iff bitGraph.remBytes > 0. + void printBitGraph(Bit8u ch); + + // Copies the codepage mapping from the constant array to CurMap + void selectCodepage(Bit16u cp); + + // Output current page + void outputPage(); + + // Prints out a byte using ASCII85 encoding (only outputs something every four bytes). When b>255, closes the ASCII85 string + void fprintASCII85(FILE* f, Bit16u b); + + // Closes a multipage document + void finishMultipage(); + + // Returns value of the num-th pixel (couting left-right, top-down) in a safe way + Bit8u getPixel(Bit32u num); + + FT_Library FTlib; // FreeType2 library used to render the characters + + SDL_Surface* page; // Surface representing the current page + FT_Face curFont; // The font currently used to render characters + Bit8u color; + + Real64 curX, curY; // Position of the print head (in inch) + + Bit16u dpi; // dpi of the page + Bit16u ESCCmd; // ESC-command that is currently processed + bool ESCSeen; // True if last read character was an ESC (0x1B) + bool FSSeen; // True if last read character was an FS (0x1C) (IBM commands) + + Bit8u numParam, neededParam; // Numbers of parameters already read/needed to process command + + Bit8u params[20]; // Buffer for the read params + Bit16u style; // Style of font (see STYLE_* constants) + Real64 cpi, actcpi; // CPI value set by program and the actual one (taking in account font types) + Bit8u score; // Score for lines (see SCORE_* constants) + + Real64 topMargin, bottomMargin, rightMargin, leftMargin; // Margins of the page (in inch) + Real64 pageWidth, pageHeight; // Size of page (in inch) + Real64 defaultPageWidth, defaultPageHeight; // Default size of page (in inch) + Real64 lineSpacing; // Size of one line (in inch) + + Real64 horiztabs[32]; // Stores the set horizontal tabs (in inch) + Bit8u numHorizTabs; // Number of configured tabs + + Real64 verttabs[16]; // Stores the set vertical tabs (in inch) + Bit8u numVertTabs; // Number of configured tabs + + Bit8u curCharTable; // Currently used char table und charset + Bit8u printQuality; // Print quality (see QUALITY_* constants) + + Typeface LQtypeFace; // Typeface used in LQ printing mode + + Real64 extraIntraSpace; // Extra space between two characters (set by program, in inch) + + bool charRead; // True if a character was read since the printer was last initialized + bool autoFeed; // True if a LF should automatically added after a CR + bool printUpperContr; // True if the upper command characters should be printed + + struct bitGraphicParams // Holds information about printing bit images + { + Bit16u horizDens, vertDens; // Density of image to print (in dpi) + bool adjacent; // Print adjacent pixels? (ignored) + Bit8u bytesColumn; // Bytes per column + Bit16u remBytes; // Bytes left to read before image is done + Bit8u column[6]; // Bytes of the current and last column + Bit8u readBytesColumn; // Bytes read so far for the current column + } bitGraph; + + Bit8u densk, densl, densy, densz; // Image density modes used in ESC K/L/Y/Z commands + + Bit16u curMap[256]; // Currently used ASCII => Unicode mapping + Bit16u charTables[4]; // Charactertables + + Real64 definedUnit; // Unit used by some ESC/P2 commands (negative => use default) + + bool multipoint; // If multipoint mode is enabled + Real64 multiPointSize; // Point size of font in multipoint mode + Real64 multicpi; // CPI used in multipoint mode + + Real64 hmi; // Horizontal motion index (in inch; overrides CPI settings) + + Bit8u msb; // MSB mode + Bit16u numPrintAsChar; // Number of bytes to print as characters (even when normally control codes) + +#if defined (WIN32) + HDC printerDC; // Win32 printer device +#endif + + char* output; // Output method selected by user + void* outputHandle; // If not null, additional pages will be appended to the given handle + bool multipageOutput; // If true, all pages are combined to one file/print job etc. until the "eject page" button is pressed + Bit16u multiPageCounter; // Current page (when printing multipages) + + Bit8u ASCII85Buffer[4]; // Buffer used in ASCII85 encoding + Bit8u ASCII85BufferPos; // Position in ASCII85 encode buffer + Bit8u ASCII85CurCol; // Columns printed so far in the current lines +}; + + +//Interfaces to C code +#ifdef __cplusplus +extern "C" +{ +#endif +void printer_init(int pdpi, int pwidth, int pheight, char* poutput, bool mpage); +void printer_loop(Bit8u pchar); +void printer_close(); +void printer_feed(); +#ifdef __cplusplus +} +#endif + +#endif +#endif diff --git a/src/printer_charmaps.h b/src/printer_charmaps.h new file mode 100644 index 0000000..74d42d0 --- /dev/null +++ b/src/printer_charmaps.h @@ -0,0 +1,327 @@ +/* + GSport - an Apple //gs Emulator + Copyright (C) 2010 by GSport contributors + + Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +// Various ASCII codepage to unicode maps + +static const Bit16u cp437Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, +0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192, +0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, +0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, +0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +static const Bit16u cp737Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0, +0x03a1,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,0x03a9,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8, +0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c3,0x03c2,0x03c4,0x03c5,0x03c6,0x03c7,0x03c8, +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, +0x03c9,0x03ac,0x03ad,0x03ae,0x03ca,0x03af,0x03cc,0x03cd,0x03cb,0x03ce,0x0386,0x0388,0x0389,0x038a,0x038c,0x038e, +0x038f,0x00b1,0x2265,0x2264,0x03aa,0x03ab,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +static const Bit16u cp775Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x0106,0x00fc,0x00e9,0x0101,0x00e4,0x0123,0x00e5,0x0107,0x0142,0x0113,0x0156,0x0157,0x012b,0x0179,0x00c4,0x00c5, +0x00c9,0x00e6,0x00c6,0x014d,0x00f6,0x0122,0x00a2,0x015a,0x015b,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x00d7,0x00a4, +0x0100,0x012a,0x00f3,0x017b,0x017c,0x017a,0x201d,0x00a6,0x00a9,0x00ae,0x00ac,0x00bd,0x00bc,0x0141,0x00ab,0x00bb, +0x2591,0x2592,0x2593,0x2502,0x2524,0x0104,0x010c,0x0118,0x0116,0x2563,0x2551,0x2557,0x255d,0x012e,0x0160,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x0172,0x016a,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x017d, +0x0105,0x010d,0x0119,0x0117,0x012f,0x0161,0x0173,0x016b,0x017e,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, +0x00d3,0x00df,0x014c,0x0143,0x00f5,0x00d5,0x00b5,0x0144,0x0136,0x0137,0x013b,0x013c,0x0146,0x0112,0x0145,0x2019, +0x00ad,0x00b1,0x201c,0x00be,0x00b6,0x00a7,0x00f7,0x201e,0x00b0,0x2219,0x00b7,0x00b9,0x00b3,0x00b2,0x25a0,0x00a0 +}; + +static const Bit16u cp850Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, +0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x00d7,0x0192, +0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x00ae,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, +0x2591,0x2592,0x2593,0x2502,0x2524,0x00c1,0x00c2,0x00c0,0x00a9,0x2563,0x2551,0x2557,0x255d,0x00a2,0x00a5,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x00e3,0x00c3,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4, +0x00f0,0x00d0,0x00ca,0x00cb,0x00c8,0x0131,0x00cd,0x00ce,0x00cf,0x2518,0x250c,0x2588,0x2584,0x00a6,0x00cc,0x2580, +0x00d3,0x00df,0x00d4,0x00d2,0x00f5,0x00d5,0x00b5,0x00fe,0x00de,0x00da,0x00db,0x00d9,0x00fd,0x00dd,0x00af,0x00b4, +0x00ad,0x00b1,0x2017,0x00be,0x00b6,0x00a7,0x00f7,0x00b8,0x00b0,0x00a8,0x00b7,0x00b9,0x00b3,0x00b2,0x25a0,0x00a0 +}; + +static const Bit16u cp852Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x016f,0x0107,0x00e7,0x0142,0x00eb,0x0150,0x0151,0x00ee,0x0179,0x00c4,0x0106, +0x00c9,0x0139,0x013a,0x00f4,0x00f6,0x013d,0x013e,0x015a,0x015b,0x00d6,0x00dc,0x0164,0x0165,0x0141,0x00d7,0x010d, +0x00e1,0x00ed,0x00f3,0x00fa,0x0104,0x0105,0x017d,0x017e,0x0118,0x0119,0x00ac,0x017a,0x010c,0x015f,0x00ab,0x00bb, +0x2591,0x2592,0x2593,0x2502,0x2524,0x00c1,0x00c2,0x011a,0x015e,0x2563,0x2551,0x2557,0x255d,0x017b,0x017c,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x0102,0x0103,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4, +0x0111,0x0110,0x010e,0x00cb,0x010f,0x0147,0x00cd,0x00ce,0x011b,0x2518,0x250c,0x2588,0x2584,0x0162,0x016e,0x2580, +0x00d3,0x00df,0x00d4,0x0143,0x0144,0x0148,0x0160,0x0161,0x0154,0x00da,0x0155,0x0170,0x00fd,0x00dd,0x0163,0x00b4, +0x00ad,0x02dd,0x02db,0x02c7,0x02d8,0x00a7,0x00f7,0x00b8,0x00b0,0x00a8,0x02d9,0x0171,0x0158,0x0159,0x25a0,0x00a0 +}; + +static const Bit16u cp855Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x0452,0x0402,0x0453,0x0403,0x0451,0x0401,0x0454,0x0404,0x0455,0x0405,0x0456,0x0406,0x0457,0x0407,0x0458,0x0408, +0x0459,0x0409,0x045a,0x040a,0x045b,0x040b,0x045c,0x040c,0x045e,0x040e,0x045f,0x040f,0x044e,0x042e,0x044a,0x042a, +0x0430,0x0410,0x0431,0x0411,0x0446,0x0426,0x0434,0x0414,0x0435,0x0415,0x0444,0x0424,0x0433,0x0413,0x00ab,0x00bb, +0x2591,0x2592,0x2593,0x2502,0x2524,0x0445,0x0425,0x0438,0x0418,0x2563,0x2551,0x2557,0x255d,0x0439,0x0419,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x043a,0x041a,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4, +0x043b,0x041b,0x043c,0x041c,0x043d,0x041d,0x043e,0x041e,0x043f,0x2518,0x250c,0x2588,0x2584,0x041f,0x044f,0x2580, +0x042f,0x0440,0x0420,0x0441,0x0421,0x0442,0x0422,0x0443,0x0423,0x0436,0x0416,0x0432,0x0412,0x044c,0x042c,0x2116, +0x00ad,0x044b,0x042b,0x0437,0x0417,0x0448,0x0428,0x044d,0x042d,0x0449,0x0429,0x0447,0x0427,0x00a7,0x25a0,0x00a0 +}; + +static const Bit16u cp857Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x0131,0x00c4,0x00c5, +0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x0130,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x015e,0x015f, +0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x011e,0x011f,0x00bf,0x00ae,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, +0x2591,0x2592,0x2593,0x2502,0x2524,0x00c1,0x00c2,0x00c0,0x00a9,0x2563,0x2551,0x2557,0x255d,0x00a2,0x00a5,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x00e3,0x00c3,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4, +0x00ba,0x00aa,0x00ca,0x00cb,0x00c8,0x0000,0x00cd,0x00ce,0x00cf,0x2518,0x250c,0x2588,0x2584,0x00a6,0x00cc,0x2580, +0x00d3,0x00df,0x00d4,0x00d2,0x00f5,0x00d5,0x00b5,0x0000,0x00d7,0x00da,0x00db,0x00d9,0x00ec,0x00ff,0x00af,0x00b4, +0x00ad,0x00b1,0x0000,0x00be,0x00b6,0x00a7,0x00f7,0x00b8,0x00b0,0x00a8,0x00b7,0x00b9,0x00b3,0x00b2,0x25a0,0x00a0 +}; + +static const Bit16u cp860Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x00c7,0x00fc,0x00e9,0x00e2,0x00e3,0x00e0,0x00c1,0x00e7,0x00ea,0x00ca,0x00e8,0x00cd,0x00d4,0x00ec,0x00c3,0x00c2, +0x00c9,0x00c0,0x00c8,0x00f4,0x00f5,0x00f2,0x00da,0x00f9,0x00cc,0x00d5,0x00dc,0x00a2,0x00a3,0x00d9,0x20a7,0x00d3, +0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x00d2,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, +0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, +0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +static const Bit16u cp861Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00d0,0x00f0,0x00de,0x00c4,0x00c5, +0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00fe,0x00fb,0x00dd,0x00fd,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x20a7,0x0192, +0x00e1,0x00ed,0x00f3,0x00fa,0x00c1,0x00cd,0x00d3,0x00da,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, +0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, +0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +static const Bit16u cp862Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x05d0,0x05d1,0x05d2,0x05d3,0x05d4,0x05d5,0x05d6,0x05d7,0x05d8,0x05d9,0x05da,0x05db,0x05dc,0x05dd,0x05de,0x05df, +0x05e0,0x05e1,0x05e2,0x05e3,0x05e4,0x05e5,0x05e6,0x05e7,0x05e8,0x05e9,0x05ea,0x00a2,0x00a3,0x00a5,0x20a7,0x0192, +0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, +0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, +0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +static const Bit16u cp863Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x00c7,0x00fc,0x00e9,0x00e2,0x00c2,0x00e0,0x00b6,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x2017,0x00c0,0x00a7, +0x00c9,0x00c8,0x00ca,0x00f4,0x00cb,0x00cf,0x00fb,0x00f9,0x00a4,0x00d4,0x00dc,0x00a2,0x00a3,0x00d9,0x00db,0x0192, +0x00a6,0x00b4,0x00f3,0x00fa,0x00a8,0x00b8,0x00b3,0x00af,0x00ce,0x2310,0x00ac,0x00bd,0x00bc,0x00be,0x00ab,0x00bb, +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, +0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, +0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +static const Bit16u cp864Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x066a,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x00b0,0x00b7,0x2219,0x221a,0x2592,0x2500,0x2502,0x253c,0x2524,0x252c,0x251c,0x2534,0x2510,0x250c,0x2514,0x2518, +0x03b2,0x221e,0x03c6,0x00b1,0x00bd,0x00bc,0x2248,0x00ab,0x00bb,0xfef7,0xfef8,0x0000,0x0000,0xfefb,0xfefc,0x0000, +0x00a0,0x00ad,0xfe82,0x00a3,0x00a4,0xfe84,0x0000,0x0000,0xfe8e,0xfe8f,0xfe95,0xfe99,0x060c,0xfe9d,0xfea1,0xfea5, +0x0660,0x0661,0x0662,0x0663,0x0664,0x0665,0x0666,0x0667,0x0668,0x0669,0xfed1,0x061b,0xfeb1,0xfeb5,0xfeb9,0x061f, +0x00a2,0xfe80,0xfe81,0xfe83,0xfe85,0xfeca,0xfe8b,0xfe8d,0xfe91,0xfe93,0xfe97,0xfe9b,0xfe9f,0xfea3,0xfea7,0xfea9, +0xfeab,0xfead,0xfeaf,0xfeb3,0xfeb7,0xfebb,0xfebf,0xfec1,0xfec5,0xfecb,0xfecf,0x00a6,0x00ac,0x00f7,0x00d7,0xfec9, +0x0640,0xfed3,0xfed7,0xfedb,0xfedf,0xfee3,0xfee7,0xfeeb,0xfeed,0xfeef,0xfef3,0xfebd,0xfecc,0xfece,0xfecd,0xfee1, +0xfe7d,0x0651,0xfee5,0xfee9,0xfeec,0xfef0,0xfef2,0xfed0,0xfed5,0xfef5,0xfef6,0xfedd,0xfed9,0xfef1,0x25a0, +}; + +static const Bit16u cp865Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, +0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x20a7,0x0192, +0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00a4, +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, +0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, +0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +static const Bit16u cp866Map[256] = { +0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, +0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, +0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, +0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, +0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, +0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, +0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, +0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, +0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f, +0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f, +0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,0x043f, +0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, +0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, +0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, +0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,0x044f, +0x0401,0x0451,0x0404,0x0454,0x0407,0x0457,0x040e,0x045e,0x00b0,0x2219,0x00b7,0x221a,0x2116,0x00a4,0x25a0,0x00a0 +}; + +CHARMAP charmap[] = { + {437, cp437Map}, + {737, cp737Map}, + {775, cp775Map}, + {850, cp850Map}, + {852, cp852Map}, + {855, cp855Map}, + {857, cp857Map}, + {860, cp860Map}, + {861, cp861Map}, + {863, cp863Map}, + {864, cp864Map}, + {865, cp865Map}, + {866, cp866Map}, + {0, NULL}, +}; + +static const Bit16u codepages[15] = {0, 437, 932, 850, 851, 853, 855, 860, 863, 865, 852, 857, 862, 864, 866}; + +// TODO: Implement all international charsets +static const Bit16u intCharSets[15][12] = +{ + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, // USA + {0x0023, 0x0024, 0x00e0, 0x00ba, 0x00e7, 0x00a7, 0x005e, 0x0060, 0x00e9, 0x00f9, 0x00e8, 0x00a8}, // France + {0x0023, 0x0024, 0x00a7, 0x00c4, 0x00d6, 0x00dc, 0x005e, 0x0060, 0x00e4, 0x00f6, 0x00fc, 0x00df}, // Germany + {0x00a3, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, // UK + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, + {0x0023, 0x0024, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e}, + {0x0023, 0x0024, 0x00a7, 0x00c4, 0x0027, 0x0022, 0x00b6, 0x0060, 0x00a9, 0x00ae, 0x2020, 0x2122} // Legal +}; diff --git a/src/protos.h b/src/protos.h index 9e80e2d..ed6809a 100644 --- a/src/protos.h +++ b/src/protos.h @@ -159,6 +159,7 @@ void config_init(void); void cfg_exit(void); void cfg_toggle_config_panel(void); void cfg_text_screen_dump(void); +void cfg_get_tfe_name(void); void config_vbl_update(int doit_3_persec); void config_parse_option(char *buf, int pos, int len, int line); void config_parse_bram(char *buf, int pos, int len); @@ -539,3 +540,80 @@ void video_update_status_line(int line, const char *string); void video_show_debug_info(void); word32 float_bus(double dcycs); +/*parallel.c*/ +byte parallel_read(word16 paddr); +void parallel_write(word16 paddr, byte pvar); +void printer_update(); + +/*tfc.c*/ +int tfe_enabled; + +void tfe_init(void); +int tfe_resources_init(void); +int tfe_cmdline_options_init(void); +int set_tfe_interface(const char* name); +void get_disabled_state(int * param); + +void tfe_reset(void); +void tfe_shutdown(void); +byte tfe_read(word16 addr); +void tfe_store(word16 addr, byte var); +int tfe_read_snapshot_module(struct snapshot_s *s); +int tfe_write_snapshot_module(struct snapshot_s *s); +int tfe_enumadapter_open(void); +int tfe_enumadapter(char **ppname, char **ppdescription); +int tfe_enumadapter_close(void); + +/*tfearch.c*/ +int tfe_arch_init(void); +void tfe_arch_pre_reset(void); +void tfe_arch_post_reset(void); +int tfe_arch_activate(const char *interface_name); +void tfe_arch_deactivate(void); +void tfe_arch_set_mac(const byte mac[6]); +void tfe_arch_set_hashfilter(const int hash_mask[2]); + +void tfe_arch_recv_ctl( int bBroadcast, /* broadcast */ + int bIA, /* individual address (IA) */ + int bMulticast, /* multicast if address passes the hash filter */ + int bCorrect, /* accept correct frames */ + int bPromiscuous, /* promiscuous mode */ + int bIAHash /* accept if IA passes the hash filter */ + ); + +void tfe_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver); + +void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in transmit buffer */ + int onecoll, /* ONECOLL: Terminate after just one collision */ + int inhibit_crc, /* INHIBITCRC: Do not append CRC to the transmission */ + int tx_pad_dis, /* TXPADDIS: Disable padding to 60 Bytes */ + int txlength, /* Frame length */ + byte *txframe /* Pointer to the frame to be transmitted */ + ); + +int tfe_arch_receive(byte *pbuffer , /* where to store a frame */ + int *plen, /* IN: maximum length of frame to copy; + OUT: length of received frame + OUT can be bigger than IN if received frame was + longer than supplied buffer */ + int *phashed, /* set if the dest. address is accepted by the hash filter */ + int *phash_index, /* hash table index if hashed == TRUE */ + int *prx_ok, /* set if good CRC and valid length */ + int *pcorrect_mac, /* set if dest. address is exactly our IA */ + int *pbroadcast, /* set if dest. address is a broadcast address */ + int *pcrc_error /* set if received frame had a CRC error */ + ); + +/* + This is a helper for tfe_receive() to determine if the received frame should be accepted + according to the settings. + + This function is even allowed to be called in tfearch.c from tfe_arch_receive() if + necessary, which is the reason why its prototype is included here in tfearch.h. +*/ +int tfe_should_accept(unsigned char *buffer, int length, int *phashed, int *phash_index, + int *pcorrect_mac, int *pbroadcast, int *pmulticast); + +int tfe_arch_enumadapter_open(void); +int tfe_arch_enumadapter(char **ppname, char **ppdescription); +int tfe_arch_enumadapter_close(void); diff --git a/src/resource b/src/resource new file mode 100644 index 0000000..0e1f280 --- /dev/null +++ b/src/resource @@ -0,0 +1,58 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by kegs32.rc +// +#define IDD_ABOUT_DIALOG 101 +#define IDC_KEGS32 102 +#define IDR_TOOLBAR 103 +#define IDD_DLG_DISKCONF 104 +#define IDR_ACCEL 105 +#define IDD_KEGS32_KEY 106 +#define IDD_SPEEDDIALOG 117 +#define IDC_SLOW 1007 +#define IDC_CUSTOM 1008 +#define IDC_EDITCUSTOM 1009 +#define IDC_NORMAL 1010 +#define IDC_FASTEST 1011 +#define ID_TOOLBAR 5000 +#define ID_STATUSBAR 5001 +#define IDC_EDIT_S5D1 10051 +#define IDC_EDIT_S5D2 10052 +#define IDC_EDIT_S6D1 10061 +#define IDC_EDIT_S6D2 10062 +#define IDC_EDIT_S7D1 10071 +#define IDC_EDIT_S7D2 10072 +#define IDC_BTN_S5D1 11051 +#define IDC_BTN_S5D2 11052 +#define IDC_BTN_S6D1 11061 +#define IDC_BTN_S6D2 11062 +#define IDC_BTN_S7D1 11071 +#define IDC_BTN_S7D2 11072 +#define ID_HELP_ABOUT 40001 +#define ID_FILE_EXIT 40002 +#define ID_FILE_DISK 40003 +#define ID_FILE_SENDRESET 40004 +#define ID_FILE_JOYSTICK 40005 +#define ID_FILE_DEBUGSTAT 40006 +#define ID_FILE_SENDREBOOT 40007 +#define ID_FILE_FULLSCREEN 40012 +#define ID_FILE_SPEED 40013 +#define ID_HELP_KEY 40014 +#define ID_FILE_SEND_REBOOT 40014 +#define ID_Menu 40015 +#define ID_EMULATOR_FLUSHPRINTER 40016 +#define ID_FILE_FLUSHPRINTER 40017 +#define ID_SPEED_1MHZ 50001 +#define ID_SPEED_2MHZ 50002 +#define ID_SPEED_FMHZ 50003 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 116 +#define _APS_NEXT_COMMAND_VALUE 40018 +#define _APS_NEXT_CONTROL_VALUE 1003 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/resource.h b/src/resource.h new file mode 100644 index 0000000..eebe4cd --- /dev/null +++ b/src/resource.h @@ -0,0 +1,57 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by kegs32.rc +// +#define IDD_ABOUT_DIALOG 101 +#define IDC_KEGS32 102 +#define IDR_TOOLBAR 103 +#define IDD_DLG_DISKCONF 104 +#define IDR_ACCEL 105 +#define IDD_KEGS32_KEY 106 +#define ID_TOOLBAR 5000 +#define ID_STATUSBAR 5001 +#define IDC_EDIT_S5D1 10051 +#define IDC_EDIT_S5D2 10052 +#define IDC_EDIT_S6D1 10061 +#define IDC_EDIT_S6D2 10062 +#define IDC_EDIT_S7D1 10071 +#define IDC_EDIT_S7D2 10072 +#define IDC_BTN_S5D1 11051 +#define IDC_BTN_S5D2 11052 +#define IDC_BTN_S6D1 11061 +#define IDC_BTN_S6D2 11062 +#define IDC_BTN_S7D1 11071 +#define IDC_BTN_S7D2 11072 +#define ID_HELP_ABOUT 40001 +#define ID_FILE_EXIT 40002 +#define ID_FILE_DISK 40003 +#define ID_FILE_SENDRESET 40004 +#define ID_FILE_JOYSTICK 40005 +#define ID_FILE_DEBUGSTAT 40006 +#define ID_FILE_SENDREBOOT 40007 +#define ID_FILE_FULLSCREEN 40012 +#define ID_FILE_SPEED 40013 +#define ID_HELP_KEY 40014 +#define ID_SPEED_1MHZ 50001 +#define ID_SPEED_2MHZ 50002 +#define ID_SPEED_FMHZ 50003 + +#define IDD_SPEEDDIALOG 117 +#define IDC_SLOW 1007 +#define IDC_CUSTOM 1008 +#define IDC_EDITCUSTOM 1009 +#define IDC_NORMAL 1010 +#define IDC_FASTEST 1011 + +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 111 +#define _APS_NEXT_COMMAND_VALUE 40013 +#define _APS_NEXT_CONTROL_VALUE 1003 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/scc_socket_driver.c b/src/scc_socket_driver.c index e9e5c57..fb7f11b 100644 --- a/src/scc_socket_driver.c +++ b/src/scc_socket_driver.c @@ -30,7 +30,7 @@ const char rcsid_scc_socket_driver_c[] = "@(#)$KmKId: scc_socket_driver.c,v 1.11 extern Scc scc_stat[2]; extern int g_serial_modem[]; -extern int h_errno; +//extern int h_errno; int g_wsastartup_called = 0; /* Usage: scc_socket_init() called to init socket mode */ diff --git a/src/scc_windriver.c b/src/scc_windriver.c index 3f13e46..faddb47 100644 --- a/src/scc_windriver.c +++ b/src/scc_windriver.c @@ -25,6 +25,7 @@ const char rcsid_scc_windriver_c[] = "@(#)$KmKId: scc_windriver.c,v 1.4 2004-11- #include "defc.h" #include "scc.h" +#include "printer.h" extern Scc scc_stat[2]; extern word32 g_c025_val; @@ -32,8 +33,11 @@ extern word32 g_c025_val; #ifdef _WIN32 int scc_serial_win_init(int port) -{ - COMMTIMEOUTS commtimeouts; +{ int state; + Scc *scc_ptr; + scc_ptr = &(scc_stat[port]); + printf("Called port init.\n"); + /*COMMTIMEOUTS commtimeouts; char str_buf[8]; Scc *scc_ptr; HANDLE host_handle; @@ -42,13 +46,12 @@ scc_serial_win_init(int port) scc_ptr = &(scc_stat[port]); - scc_ptr->state = 0; /* mark as failed */ - + scc_ptr->state = 0; */ /* mark as failed */ +/* sprintf(&str_buf[0], "COM%d", port+1); - host_handle = CreateFile(&str_buf[0], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - + printf("scc port is mapped to: %s\n", &str_buf); scc_ptr->host_handle = host_handle; scc_ptr->host_handle2 = malloc(sizeof(DCB)); @@ -72,7 +75,7 @@ scc_serial_win_init(int port) if(ret == 0) { printf("setcommtimeout ret: %d\n", ret); } - +*/ //printer_init(360,85,110,'bmp',0); state = 2; /* raw serial */ scc_ptr->state = state; @@ -163,7 +166,7 @@ scc_serial_win_change_params(int port) void scc_serial_win_fill_readbuf(int port, int space_left, double dcycs) -{ +{/* byte tmp_buf[256]; Scc *scc_ptr; HANDLE host_handle; @@ -177,9 +180,9 @@ scc_serial_win_fill_readbuf(int port, int space_left, double dcycs) if(host_handle == 0) { return; } - +*/ /* Try reading some bytes */ - space_left = MIN(256, space_left); +/* space_left = MIN(256, space_left); ret = ReadFile(host_handle, tmp_buf, space_left, &bytes_read, NULL); if(ret == 0) { @@ -191,7 +194,7 @@ scc_serial_win_fill_readbuf(int port, int space_left, double dcycs) scc_add_to_readbuf(port, tmp_buf[i], dcycs); } } - + */ } void @@ -210,10 +213,10 @@ scc_serial_win_empty_writebuf(int port) scc_ptr = &(scc_stat[port]); //printf("win_empty_writebuf, host_handle: %d\n", scc_ptr->host_handle); - host_handle = scc_ptr->host_handle; + /*host_handle = scc_ptr->host_handle; if(host_handle == 0) { return; - } + }*/ /* Try writing some bytes */ done = 0; @@ -237,18 +240,22 @@ scc_serial_win_empty_writebuf(int port) break; } bytes_written = 1; - ret = WriteFile(host_handle, &(scc_ptr->out_buf[rdptr]), len, - &bytes_written, NULL); + /*ret = WriteFile(host_handle, &(scc_ptr->out_buf[rdptr]), len, + &bytes_written, NULL);*/ + printer_loop(scc_ptr->out_buf[rdptr]); + /*ret = printf("WriteFile ret: %d, bytes_written:%d, len:%d\n", ret, - (int)bytes_written, len); - + (int)bytes_written, len);*/ + /*printf("WriteFile bytes_written:%d, len:%d\n", + (int)bytes_written, len);*/ err_code = (word32)-1; - if(ret == 0) { - err_code = (word32)GetLastError(); +/* if(ret == 0) { + err_code = 0; //(word32)GetLastError(); printf("WriteFile ret:0, err_code: %08x\n", err_code); - } + }*/ - if(ret == 0 || (bytes_written == 0)) { +// if(ret == 0 || (bytes_written == 0)) { + if (bytes_written == 0) { done = 1; break; } else { diff --git a/src/sim65816.c b/src/sim65816.c index 07eccc5..0613f44 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -26,7 +26,7 @@ const char rcsid_sim65816_c[] = "@(#)$KmKId: sim65816.c,v 1.367 2004-11-22 02:39 #define INCLUDE_RCSID_C #include "defc.h" #undef INCLUDE_RCSID_C - +#include "printer.h" #define PC_LOG_LEN (8*1024) @@ -109,6 +109,21 @@ int g_raw_serial = 1; int g_iw2_emul = 0; int g_serial_out_masking = 0; int g_serial_modem[2] = { 0, 1 }; +int g_ethernet = 0; +int g_ethernet_interface = 0; +int g_parallel = 0; +int g_parallel_out_masking = 0; +int g_printer = 0; +int g_printer_dpi = 360; +char* g_printer_output = "bmp"; +int g_printer_multipage = 0; +int g_printer_timeout = 2; +char* g_printer_font_roman = "roman.ttf"; +char* g_printer_font_sans = "sansserif.ttf"; +char* g_printer_font_courier = "courier.ttf"; +char* g_printer_font_prestige = "prestige.ttf"; +char* g_printer_font_script = "script.ttf"; +char* g_printer_font_ocra = "ocra.ttf"; int g_config_iwm_vbl_count = 0; const char g_kegs_version_str[] = "0.1"; @@ -391,7 +406,6 @@ get_memory_io(word32 loc, double *cyc_ptr) halt_printf("get_memory_io:%08x out of range==halt!\n", loc); return 0; } - tmp = loc & 0xfef000; if(tmp == 0xc000 || tmp == 0xe0c000) { return(io_read(loc & 0xfff, cyc_ptr)); @@ -508,7 +522,6 @@ void set_memory_io(word32 loc, int val, double *cyc_ptr) { word32 tmp; - tmp = loc & 0xfef000; if(tmp == 0xc000 || tmp == 0xe0c000) { io_write(loc, val, cyc_ptr); @@ -609,6 +622,7 @@ void my_exit(int ret) { end_screen(); + printer_close(); printf("exiting\n"); exit(ret); } @@ -861,6 +875,15 @@ kegsmain(int argc, char **argv) printf("Forcing black-and-white hires modes\n"); g_cur_a2_stat |= ALL_STAT_COLOR_C021; g_use_bw_hires = 1; + } else if(!strcmp("-enet", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + tmp1 = strtol(argv[i+1], 0, 0); + printf("Using %d as ethernet enable val\n", tmp1); + g_ethernet = tmp1; + i++; } else { printf("Bad option: %s\n", argv[i]); exit(3); @@ -898,6 +921,33 @@ kegsmain(int argc, char **argv) iwm_init(); config_init(); + printer_init(g_printer_dpi,85,110,g_printer_output,g_printer_multipage); + //If ethernet is enabled in config.kegs, lets initialize it + if (g_ethernet == 1) + { + int i = 0; + char *ppname = NULL; + char *ppdes = NULL; + if (tfe_enumadapter_open()) + { + //Loop through the available adapters until we reach the interface number specified in config.kegs + while(tfe_enumadapter(&ppname,&ppdes)) + { + if (i == g_ethernet_interface) break; + i++; + } + tfe_enumadapter_close(); + printf("Using host ethernet interface: %s\nUthernet support is ON.\n",ppdes); + } + else + { + printf("No ethernet host adapters found. Do you have PCap installed/enabled?\nUthernet support is OFF.\n"); + } + set_tfe_interface(ppname); //Connect the emulated ethernet device with the selected host adapter + lib_free(ppname); + lib_free(ppdes); + tfe_init(); + } load_roms_init_memory(); @@ -912,7 +962,6 @@ kegsmain(int argc, char **argv) //sleep(1); #endif sound_init(); - scc_init(); adb_init(); joystick_init(); diff --git a/src/support.h b/src/support.h new file mode 100644 index 0000000..4a7a6a3 --- /dev/null +++ b/src/support.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2002-2009 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* $Id: support.h,v 1.18 2009/05/27 09:15:41 qbix79 Exp $ */ + +#ifndef DOSBOX_SUPPORT_H +#define DOSBOX_SUPPORT_H + +#include +#include +#include +typedef signed Bits; + +#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 +#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 upcase(std::string &str); +void lowcase(std::string &str); + +#endif diff --git a/src/tfe/Tfesupp.c b/src/tfe/Tfesupp.c new file mode 100644 index 0000000..9b2124f --- /dev/null +++ b/src/tfe/Tfesupp.c @@ -0,0 +1,188 @@ +/* + * This file is a consolidation of functions required for tfe + * emulation taken from the following files + * + * lib.c - Library functions. + * util.c - Miscellaneous utility functions. + * crc32.c + * + * Written by + * Andreas Boose + * Ettore Perazzoli + * Andreas Matthies + * Tibor Biczo + * Spiro Trikaliotis * + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tfesupp.h" + + +// Lib Stuff +/* #define LIB_DEBUG*/ + + +#ifdef LIB_DEBUG +#define LIB_DEBUG_SIZE 0x10000 +#define LIB_DEBUG_GUARD 0x1000 +#endif + +#define CRC32_POLY 0xedb88320 +static unsigned long crc32_table[256]; +static int crc32_is_initialized = 0; + +void lib_free(void *ptr) +{ +#ifdef LIB_DEBUG + lib_debug_free(ptr, 1, 1); +#endif + +#ifdef LIB_DEBUG + lib_debug_libc_free(ptr); +#else + free(ptr); +#endif +} + +void *lib_malloc(size_t size) +{ +#ifdef LIB_DEBUG + void *ptr = lib_debug_libc_malloc(size); +#else + void *ptr = malloc(size); +#endif + +#ifndef __OS2__ + if (ptr == NULL && size > 0) + exit(-1); +#endif +#ifdef LIB_DEBUG + lib_debug_alloc(ptr, size, 3); +#endif + + return ptr; +} + +/*-----------------------------------------------------------------------*/ + +/* Malloc enough space for `str', copy `str' into it and return its + address. */ +char *lib_stralloc(const char *str) +{ + size_t size; + char *ptr; + + if (str == NULL) + exit(-1); + + size = strlen(str) + 1; + ptr = (char *)lib_malloc(size); + + memcpy(ptr, str, size); + return ptr; +} + + + +/* Like realloc, but abort if not enough memory is available. */ +void *lib_realloc(void *ptr, size_t size) +{ +#ifdef LIB_DEBUG + void *new_ptr = lib_debug_libc_realloc(ptr, size); +#else + void *new_ptr = realloc(ptr, size); +#endif + +#ifndef __OS2__ + if (new_ptr == NULL) + exit(-1); +#endif +#ifdef LIB_DEBUG + lib_debug_free(ptr, 1, 0); + lib_debug_alloc(new_ptr, size, 1); +#endif + + return new_ptr; +} + +// Util Stuff + +/* Set a new value to the dynamically allocated string *str. + Returns `-1' if nothing has to be done. */ +int util_string_set(char **str, const char *new_value) +{ + if (*str == NULL) { + if (new_value != NULL) + *str = lib_stralloc(new_value); + } else { + if (new_value == NULL) { + lib_free(*str); + *str = NULL; + } else { + /* Skip copy if src and dest are already the same. */ + if (strcmp(*str, new_value) == 0) + return -1; + + *str = (char *)lib_realloc(*str, strlen(new_value) + 1); + strcpy(*str, new_value); + } + } + return 0; +} + + +// crc32 Stuff + +unsigned long crc32_buf(const char *buffer, unsigned int len) +{ + int i, j; + unsigned long crc, c; + const char *p; + + if (!crc32_is_initialized) { + for (i = 0; i < 256; i++) { + c = (unsigned long) i; + for (j = 0; j < 8; j++) + c = c & 1 ? CRC32_POLY ^ (c >> 1) : c >> 1; + crc32_table[i] = c; + } + crc32_is_initialized = 1; + } + + crc = 0xffffffff; + for (p = buffer; len > 0; ++p, --len) + crc = (crc >> 8) ^ crc32_table[(crc ^ *p) & 0xff]; + + return ~crc; +} + diff --git a/src/tfe/Tfesupp.h b/src/tfe/Tfesupp.h new file mode 100644 index 0000000..ef6928e --- /dev/null +++ b/src/tfe/Tfesupp.h @@ -0,0 +1,53 @@ +/* + * This file is a consolidation of functions required for tfe + * emulation taken from the following files + * + * lib.h - Library functions. + * util.h - Miscellaneous utility functions. + * crc32.h + * + * Written by + * Andreas Boose + * Ettore Perazzoli + * Manfred Spraul + * Andreas Matthies + * Tibor Biczo + * Spiro Trikaliotis * + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + + + +#ifndef _TFESUPP_H +#define _TFESUPP_H + +extern FILE* g_fh; // Filehandle for log file + +extern void *lib_malloc(size_t size); +extern void *lib_realloc(void *p, size_t size); +extern void lib_free(void *ptr); +extern char *lib_stralloc(const char *str); + +extern int util_string_set(char **str, const char *new_value); + +extern unsigned long crc32_buf(const char *buffer, unsigned int len); + +#endif diff --git a/src/tfe/Types.h b/src/tfe/Types.h new file mode 100644 index 0000000..e62a772 --- /dev/null +++ b/src/tfe/Types.h @@ -0,0 +1,73 @@ +/* + * types.h - Type definitions for VICE. + * + * Written by + * Ettore Perazzoli + * André Fachat + * Teemu Rantanen + * Andreas Boose + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +#ifndef _TYPES_H +#define _TYPES_H + +/*#ifndef BYTE +#define BYTE unsigned char +#endif + +#ifndef WORD +#define WORD unsigned short +#endif*/ + +#ifndef DWORD + //#ifdef DWORD_IS_LONG + #define DWORD unsigned long + //#else + //#define DWORD unsigned int + //#endif +#endif + +/* RGJ added for AppleWin */ +#ifndef ULONG +typedef unsigned long ULONG; +#endif + +typedef signed char SIGNED_CHAR; +typedef signed short SWORD; +typedef signed int SDWORD; + +typedef DWORD CLOCK; +/* Maximum value of a CLOCK. */ +#define CLOCK_MAX (~((CLOCK)0)) +/* +#if defined(__GNUC__) && defined(__i386__) && !defined(NO_REGPARM) +#define REGPARM1 __attribute__((regparm(1))) +#define REGPARM2 __attribute__((regparm(2))) +#define REGPARM3 __attribute__((regparm(3))) +#else +#define REGPARM1 +#define REGPARM2 +#define REGPARM3 +#endif +*/ +#endif + diff --git a/src/tfe/arch/win32/Bittypes.h b/src/tfe/arch/win32/Bittypes.h new file mode 100644 index 0000000..654f37b --- /dev/null +++ b/src/tfe/arch/win32/Bittypes.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _BITTYPES_H +#define _BITTYPES_H + +#ifndef HAVE_U_INT8_T + +#if SIZEOF_CHAR == 1 +typedef unsigned char u_int8_t; +typedef signed char int8_t; +#elif SIZEOF_INT == 1 +typedef unsigned int u_int8_t; +typedef signed int int8_t; +#else /* XXX */ +#error "there's no appropriate type for u_int8_t" +#endif +#define HAVE_U_INT8_T 1 +#define HAVE_INT8_T 1 + +#endif /* HAVE_U_INT8_T */ + +#ifndef HAVE_U_INT16_T + +#if SIZEOF_SHORT == 2 +typedef unsigned short u_int16_t; +typedef signed short int16_t; +#elif SIZEOF_INT == 2 +typedef unsigned int u_int16_t; +typedef signed int int16_t; +#elif SIZEOF_CHAR == 2 +typedef unsigned char u_int16_t; +typedef signed char int16_t; +#else /* XXX */ +#error "there's no appropriate type for u_int16_t" +#endif +#define HAVE_U_INT16_T 1 +#define HAVE_INT16_T 1 + +#endif /* HAVE_U_INT16_T */ + +#ifndef HAVE_U_INT32_T + +#if SIZEOF_INT == 4 +typedef unsigned int u_int32_t; +typedef signed int int32_t; +#elif SIZEOF_LONG == 4 +typedef unsigned long u_int32_t; +typedef signed long int32_t; +#elif SIZEOF_SHORT == 4 +typedef unsigned short u_int32_t; +typedef signed short int32_t; +#else /* XXX */ +#error "there's no appropriate type for u_int32_t" +#endif +#define HAVE_U_INT32_T 1 +#define HAVE_INT32_T 1 + +#endif /* HAVE_U_INT32_T */ + +#endif /* _BITTYPES_H */ diff --git a/src/tfe/arch/win32/Bpf.h b/src/tfe/arch/win32/Bpf.h new file mode 100644 index 0000000..0acfc7d --- /dev/null +++ b/src/tfe/arch/win32/Bpf.h @@ -0,0 +1,516 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bpf.h 7.1 (Berkeley) 5/7/91 + * + * @(#) $Header$ (LBL) + */ + +#ifndef BPF_MAJOR_VERSION + +#ifdef __cplusplus +extern "C" { +#endif + +/* BSD style release date */ +#define BPF_RELEASE 199606 + +typedef int bpf_int32; +typedef u_int bpf_u_int32; + +/* + * Alignment macros. BPF_WORDALIGN rounds up to the next + * even multiple of BPF_ALIGNMENT. + */ +#ifndef __NetBSD__ +#define BPF_ALIGNMENT sizeof(bpf_int32) +#else +#define BPF_ALIGNMENT sizeof(long) +#endif +#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1)) + +#define BPF_MAXINSNS 512 +#define BPF_MAXBUFSIZE 0x8000 +#define BPF_MINBUFSIZE 32 + +/* + * Structure for BIOCSETF. + */ +struct bpf_program { + u_int bf_len; + struct bpf_insn *bf_insns; +}; + +/* + * Struct returned by BIOCGSTATS. + */ +struct bpf_stat { + u_int bs_recv; /* number of packets received */ + u_int bs_drop; /* number of packets dropped */ +}; + +/* + * Struct return by BIOCVERSION. This represents the version number of + * the filter language described by the instruction encodings below. + * bpf understands a program iff kernel_major == filter_major && + * kernel_minor >= filter_minor, that is, if the value returned by the + * running kernel has the same major number and a minor number equal + * equal to or less than the filter being downloaded. Otherwise, the + * results are undefined, meaning an error may be returned or packets + * may be accepted haphazardly. + * It has nothing to do with the source code version. + */ +struct bpf_version { + u_short bv_major; + u_short bv_minor; +}; +/* Current version number of filter architecture. */ +#define BPF_MAJOR_VERSION 1 +#define BPF_MINOR_VERSION 1 + +/* + * BPF ioctls + * + * The first set is for compatibility with Sun's pcc style + * header files. If your using gcc, we assume that you + * have run fixincludes so the latter set should work. + */ +#if (defined(sun) || defined(ibm032)) && !defined(__GNUC__) +#define BIOCGBLEN _IOR(B,102, u_int) +#define BIOCSBLEN _IOWR(B,102, u_int) +#define BIOCSETF _IOW(B,103, struct bpf_program) +#define BIOCFLUSH _IO(B,104) +#define BIOCPROMISC _IO(B,105) +#define BIOCGDLT _IOR(B,106, u_int) +#define BIOCGETIF _IOR(B,107, struct ifreq) +#define BIOCSETIF _IOW(B,108, struct ifreq) +#define BIOCSRTIMEOUT _IOW(B,109, struct timeval) +#define BIOCGRTIMEOUT _IOR(B,110, struct timeval) +#define BIOCGSTATS _IOR(B,111, struct bpf_stat) +#define BIOCIMMEDIATE _IOW(B,112, u_int) +#define BIOCVERSION _IOR(B,113, struct bpf_version) +#define BIOCSTCPF _IOW(B,114, struct bpf_program) +#define BIOCSUDPF _IOW(B,115, struct bpf_program) +#else +#define BIOCGBLEN _IOR('B',102, u_int) +#define BIOCSBLEN _IOWR('B',102, u_int) +#define BIOCSETF _IOW('B',103, struct bpf_program) +#define BIOCFLUSH _IO('B',104) +#define BIOCPROMISC _IO('B',105) +#define BIOCGDLT _IOR('B',106, u_int) +#define BIOCGETIF _IOR('B',107, struct ifreq) +#define BIOCSETIF _IOW('B',108, struct ifreq) +#define BIOCSRTIMEOUT _IOW('B',109, struct timeval) +#define BIOCGRTIMEOUT _IOR('B',110, struct timeval) +#define BIOCGSTATS _IOR('B',111, struct bpf_stat) +#define BIOCIMMEDIATE _IOW('B',112, u_int) +#define BIOCVERSION _IOR('B',113, struct bpf_version) +#define BIOCSTCPF _IOW('B',114, struct bpf_program) +#define BIOCSUDPF _IOW('B',115, struct bpf_program) +#endif + +/* + * Structure prepended to each packet. + */ +struct bpf_hdr { + struct timeval bh_tstamp; /* time stamp */ + bpf_u_int32 bh_caplen; /* length of captured portion */ + bpf_u_int32 bh_datalen; /* original length of packet */ + u_short bh_hdrlen; /* length of bpf header (this struct + plus alignment padding) */ +}; +/* + * Because the structure above is not a multiple of 4 bytes, some compilers + * will insist on inserting padding; hence, sizeof(struct bpf_hdr) won't work. + * Only the kernel needs to know about it; applications use bh_hdrlen. + */ +#if defined(KERNEL) || defined(_KERNEL) +#define SIZEOF_BPF_HDR 18 +#endif + +/* + * Data-link level type codes. + */ + +/* + * These are the types that are the same on all platforms; on other + * platforms, a should be supplied that defines the additional + * DLT_* codes appropriately for that platform (the BSDs, for example, + * should not just pick up this version of "bpf.h"; they should also define + * the additional DLT_* codes used by their kernels, as well as the values + * defined here - and, if the values they use for particular DLT_ types + * differ from those here, they should use their values, not the ones + * here). + */ +#define DLT_NULL 0 /* no link-layer encapsulation */ +#define DLT_EN10MB 1 /* Ethernet (10Mb) */ +#define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */ +#define DLT_AX25 3 /* Amateur Radio AX.25 */ +#define DLT_PRONET 4 /* Proteon ProNET Token Ring */ +#define DLT_CHAOS 5 /* Chaos */ +#define DLT_IEEE802 6 /* IEEE 802 Networks */ +#define DLT_ARCNET 7 /* ARCNET, with BSD-style header */ +#define DLT_SLIP 8 /* Serial Line IP */ +#define DLT_PPP 9 /* Point-to-point Protocol */ +#define DLT_FDDI 10 /* FDDI */ + +/* + * These are values from the traditional libpcap "bpf.h". + * Ports of this to particular platforms should replace these definitions + * with the ones appropriate to that platform, if the values are + * different on that platform. + */ +#define DLT_ATM_RFC1483 11 /* LLC/SNAP encapsulated atm */ +#define DLT_RAW 12 /* raw IP */ + +/* + * These are values from BSD/OS's "bpf.h". + * These are not the same as the values from the traditional libpcap + * "bpf.h"; however, these values shouldn't be generated by any + * OS other than BSD/OS, so the correct values to use here are the + * BSD/OS values. + * + * Platforms that have already assigned these values to other + * DLT_ codes, however, should give these codes the values + * from that platform, so that programs that use these codes will + * continue to compile - even though they won't correctly read + * files of these types. + */ +#ifdef __NetBSD__ +#ifndef DLT_SLIP_BSDOS +#define DLT_SLIP_BSDOS 13 /* BSD/OS Serial Line IP */ +#define DLT_PPP_BSDOS 14 /* BSD/OS Point-to-point Protocol */ +#endif +#else +#define DLT_SLIP_BSDOS 15 /* BSD/OS Serial Line IP */ +#define DLT_PPP_BSDOS 16 /* BSD/OS Point-to-point Protocol */ +#endif + +#define DLT_ATM_CLIP 19 /* Linux Classical-IP over ATM */ + +/* + * These values are defined by NetBSD; other platforms should refrain from + * using them for other purposes, so that NetBSD savefiles with link + * types of 50 or 51 can be read as this type on all platforms. + */ +#define DLT_PPP_SERIAL 50 /* PPP over serial with HDLC encapsulation */ +#define DLT_PPP_ETHER 51 /* PPP over Ethernet */ + +/* + * Values between 100 and 103 are used in capture file headers as + * link-layer types corresponding to DLT_ types that differ + * between platforms; don't use those values for new DLT_ new types. + */ + +/* + * This value was defined by libpcap 0.5; platforms that have defined + * it with a different value should define it here with that value - + * a link type of 104 in a save file will be mapped to DLT_C_HDLC, + * whatever value that happens to be, so programs will correctly + * handle files with that link type regardless of the value of + * DLT_C_HDLC. + * + * The name DLT_C_HDLC was used by BSD/OS; we use that name for source + * compatibility with programs written for BSD/OS. + * + * libpcap 0.5 defined it as DLT_CHDLC; we define DLT_CHDLC as well, + * for source compatibility with programs written for libpcap 0.5. + */ +#define DLT_C_HDLC 104 /* Cisco HDLC */ +#define DLT_CHDLC DLT_C_HDLC + +#define DLT_IEEE802_11 105 /* IEEE 802.11 wireless */ + +/* + * 106 is reserved for Linux Classical IP over ATM; it's like DLT_RAW, + * except when it isn't. (I.e., sometimes it's just raw IP, and + * sometimes it isn't.) We currently handle it as DLT_LINUX_SLL, + * so that we don't have to worry about the link-layer header.) + */ + +/* + * Frame Relay; BSD/OS has a DLT_FR with a value of 11, but that collides + * with other values. + * DLT_FR and DLT_FRELAY packets start with the Q.922 Frame Relay header + * (DLCI, etc.). + */ +#define DLT_FRELAY 107 + +/* + * OpenBSD DLT_LOOP, for loopback devices; it's like DLT_NULL, except + * that the AF_ type in the link-layer header is in network byte order. + * + * OpenBSD defines it as 12, but that collides with DLT_RAW, so we + * define it as 108 here. If OpenBSD picks up this file, it should + * define DLT_LOOP as 12 in its version, as per the comment above - + * and should not use 108 as a DLT_ value. + */ +#define DLT_LOOP 108 + +/* + * Values between 109 and 112 are used in capture file headers as + * link-layer types corresponding to DLT_ types that might differ + * between platforms; don't use those values for new DLT_ types + * other than the corresponding DLT_ types. + */ + +/* + * This is for Linux cooked sockets. + */ +#define DLT_LINUX_SLL 113 + +/* + * Apple LocalTalk hardware. + */ +#define DLT_LTALK 114 + +/* + * Acorn Econet. + */ +#define DLT_ECONET 115 + +/* + * Reserved for use with OpenBSD ipfilter. + */ +#define DLT_IPFILTER 116 + +/* + * Reserved for use in capture-file headers as a link-layer type + * corresponding to OpenBSD DLT_PFLOG; DLT_PFLOG is 17 in OpenBSD, + * but that's DLT_LANE8023 in SuSE 6.3, so we can't use 17 for it + * in capture-file headers. + */ +#define DLT_PFLOG 117 + +/* + * Registered for Cisco-internal use. + */ +#define DLT_CISCO_IOS 118 + +/* + * Reserved for 802.11 cards using the Prism II chips, with a link-layer + * header including Prism monitor mode information plus an 802.11 + * header. + */ +#define DLT_PRISM_HEADER 119 + +/* + * Reserved for Aironet 802.11 cards, with an Aironet link-layer header + * (see Doug Ambrisko's FreeBSD patches). + */ +#define DLT_AIRONET_HEADER 120 + +/* + * Reserved for Siemens HiPath HDLC. + */ +#define DLT_HHDLC 121 + +/* + * This is for RFC 2625 IP-over-Fibre Channel. + * + * This is not for use with raw Fibre Channel, where the link-layer + * header starts with a Fibre Channel frame header; it's for IP-over-FC, + * where the link-layer header starts with an RFC 2625 Network_Header + * field. + */ +#define DLT_IP_OVER_FC 122 + +/* + * This is for Full Frontal ATM on Solaris with SunATM, with a + * pseudo-header followed by an AALn PDU. + * + * There may be other forms of Full Frontal ATM on other OSes, + * with different pseudo-headers. + * + * If ATM software returns a pseudo-header with VPI/VCI information + * (and, ideally, packet type information, e.g. signalling, ILMI, + * LANE, LLC-multiplexed traffic, etc.), it should not use + * DLT_ATM_RFC1483, but should get a new DLT_ value, so tcpdump + * and the like don't have to infer the presence or absence of a + * pseudo-header and the form of the pseudo-header. + */ +#define DLT_SUNATM 123 /* Solaris+SunATM */ + +/* + * Reserved as per request from Kent Dahlgren + * for private use. + */ +#define DLT_RIO 124 /* RapidIO */ +#define DLT_PCI_EXP 125 /* PCI Express */ +#define DLT_AURORA 126 /* Xilinx Aurora link layer */ + +/* + * For future use with 802.11 captures - defined by AbsoluteValue + * Systems to store a number of bits of link-layer information: + * + * http://www.shaftnet.org/~pizza/software/capturefrm.txt + * + * but could and arguably should also be used by non-AVS Linux + * 802.11 drivers and BSD drivers; that may happen in the future. + */ +#define DLT_IEEE802_11_RADIO 127 /* 802.11 plus WLAN header */ + +/* + * Reserved for the TZSP encapsulation, as per request from + * Chris Waters + * TZSP is a generic encapsulation for any other link type, + * which includes a means to include meta-information + * with the packet, e.g. signal strength and channel + * for 802.11 packets. + */ +#define DLT_TZSP 128 /* Tazmen Sniffer Protocol */ + +/* + * BSD's ARCNET headers have the source host, destination host, + * and type at the beginning of the packet; that's what's handed + * up to userland via BPF. + * + * Linux's ARCNET headers, however, have a 2-byte offset field + * between the host IDs and the type; that's what's handed up + * to userland via PF_PACKET sockets. + * + * We therefore have to have separate DLT_ values for them. + */ +#define DLT_ARCNET_LINUX 129 /* ARCNET */ + +/* + * The instruction encodings. + */ +/* instruction classes */ +#define BPF_CLASS(code) ((code) & 0x07) +#define BPF_LD 0x00 +#define BPF_LDX 0x01 +#define BPF_ST 0x02 +#define BPF_STX 0x03 +#define BPF_ALU 0x04 +#define BPF_JMP 0x05 +#define BPF_RET 0x06 +#define BPF_MISC 0x07 + +/* ld/ldx fields */ +#define BPF_SIZE(code) ((code) & 0x18) +#define BPF_W 0x00 +#define BPF_H 0x08 +#define BPF_B 0x10 +#define BPF_MODE(code) ((code) & 0xe0) +#define BPF_IMM 0x00 +#define BPF_ABS 0x20 +#define BPF_IND 0x40 +#define BPF_MEM 0x60 +#define BPF_LEN 0x80 +#define BPF_MSH 0xa0 + +/* alu/jmp fields */ +#define BPF_OP(code) ((code) & 0xf0) +#define BPF_ADD 0x00 +#define BPF_SUB 0x10 +#define BPF_MUL 0x20 +#define BPF_DIV 0x30 +#define BPF_OR 0x40 +#define BPF_AND 0x50 +#define BPF_LSH 0x60 +#define BPF_RSH 0x70 +#define BPF_NEG 0x80 +#define BPF_JA 0x00 +#define BPF_JEQ 0x10 +#define BPF_JGT 0x20 +#define BPF_JGE 0x30 +#define BPF_JSET 0x40 +#define BPF_SRC(code) ((code) & 0x08) +#define BPF_K 0x00 +#define BPF_X 0x08 + +/* ret - BPF_K and BPF_X also apply */ +#define BPF_RVAL(code) ((code) & 0x18) +#define BPF_A 0x10 + +/* misc */ +#define BPF_MISCOP(code) ((code) & 0xf8) +#define BPF_TAX 0x00 +#define BPF_TXA 0x80 + +/* + * The instruction data structure. + */ +struct bpf_insn { + u_short code; + u_char jt; + u_char jf; + bpf_int32 k; +}; + +/* + * Macros for insn array initializers. + */ +#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k } +#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k } + +#if defined(BSD) && (defined(KERNEL) || defined(_KERNEL)) +/* + * Systems based on non-BSD kernels don't have ifnet's (or they don't mean + * anything if it is in ) and won't work like this. + */ +# if __STDC__ +extern void bpf_tap(struct ifnet *, u_char *, u_int); +extern void bpf_mtap(struct ifnet *, struct mbuf *); +extern void bpfattach(struct ifnet *, u_int, u_int); +extern void bpfilterattach(int); +# else +extern void bpf_tap(); +extern void bpf_mtap(); +extern void bpfattach(); +extern void bpfilterattach(); +# endif /* __STDC__ */ +#endif /* BSD && (_KERNEL || KERNEL) */ +#if __STDC__ || defined(__cplusplus) +extern int bpf_validate(struct bpf_insn *, int); +extern u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int); +#else +extern int bpf_validate(); +extern u_int bpf_filter(); +#endif + +/* + * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST). + */ +#define BPF_MEMWORDS 16 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/tfe/arch/win32/Ip6_misc.h b/src/tfe/arch/win32/Ip6_misc.h new file mode 100644 index 0000000..6714f0e --- /dev/null +++ b/src/tfe/arch/win32/Ip6_misc.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 1993, 1994, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Header$ (LBL) + */ + +/* + * This file contains a collage of declarations for IPv6 from FreeBSD not present in Windows + */ + +#include + +#ifndef __MINGW32__ +#include +#endif /* __MINGW32__ */ + +#define IN_MULTICAST(a) IN_CLASSD(a) + +#define IN_EXPERIMENTAL(a) ((((u_int32_t) (a)) & 0xe0000000) == 0xe0000000) + +#define IN_LOOPBACKNET 127 + +#ifdef __MINGW32__ +/* IPv6 address */ +struct in6_addr + { + union + { + u_int8_t u6_addr8[16]; + u_int16_t u6_addr16[8]; + u_int32_t u6_addr32[4]; + } in6_u; +#define s6_addr in6_u.u6_addr8 +#define s6_addr16 in6_u.u6_addr16 +#define s6_addr32 in6_u.u6_addr32 +#define s6_addr64 in6_u.u6_addr64 + }; + +#define IN6ADDR_ANY_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } +#define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } +#endif /* __MINGW32__ */ + + +#ifdef __MINGW32__ +typedef unsigned short sa_family_t; + +#define __SOCKADDR_COMMON(sa_prefix) \ + sa_family_t sa_prefix##family + +/* Ditto, for IPv6. */ +struct sockaddr_in6 + { + __SOCKADDR_COMMON (sin6_); + u_int16_t sin6_port; /* Transport layer port # */ + u_int32_t sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ + }; + +#define IN6_IS_ADDR_V4MAPPED(a) \ + ((((u_int32_t *) (a))[0] == 0) && (((u_int32_t *) (a))[1] == 0) && \ + (((u_int32_t *) (a))[2] == htonl (0xffff))) + +#define IN6_IS_ADDR_MULTICAST(a) (((u_int8_t *) (a))[0] == 0xff) + +#define IN6_IS_ADDR_LINKLOCAL(a) \ + ((((u_int32_t *) (a))[0] & htonl (0xffc00000)) == htonl (0xfe800000)) + +#define IN6_IS_ADDR_LOOPBACK(a) \ + (((u_int32_t *) (a))[0] == 0 && ((u_int32_t *) (a))[1] == 0 && \ + ((u_int32_t *) (a))[2] == 0 && ((u_int32_t *) (a))[3] == htonl (1)) +#endif /* __MINGW32__ */ + +#define ip6_vfc ip6_ctlun.ip6_un2_vfc +#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow +#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen +#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt +#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim +#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim + +#define nd_rd_type nd_rd_hdr.icmp6_type +#define nd_rd_code nd_rd_hdr.icmp6_code +#define nd_rd_cksum nd_rd_hdr.icmp6_cksum +#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0] + +/* + * IPV6 extension headers + */ +#define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */ +#define IPPROTO_IPV6 41 /* IPv6 header. */ +#define IPPROTO_ROUTING 43 /* IPv6 routing header */ +#define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */ +#define IPPROTO_ESP 50 /* encapsulating security payload */ +#define IPPROTO_AH 51 /* authentication header */ +#define IPPROTO_ICMPV6 58 /* ICMPv6 */ +#define IPPROTO_NONE 59 /* IPv6 no next header */ +#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */ +#define IPPROTO_PIM 103 /* Protocol Independent Multicast. */ + +#define IPV6_RTHDR_TYPE_0 0 + +/* Option types and related macros */ +#define IP6OPT_PAD1 0x00 /* 00 0 00000 */ +#define IP6OPT_PADN 0x01 /* 00 0 00001 */ +#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */ +#define IP6OPT_JUMBO_LEN 6 +#define IP6OPT_ROUTER_ALERT 0x05 /* 00 0 00101 */ + +#define IP6OPT_RTALERT_LEN 4 +#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */ +#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */ +#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */ +#define IP6OPT_MINLEN 2 + +#define IP6OPT_BINDING_UPDATE 0xc6 /* 11 0 00110 */ +#define IP6OPT_BINDING_ACK 0x07 /* 00 0 00111 */ +#define IP6OPT_BINDING_REQ 0x08 /* 00 0 01000 */ +#define IP6OPT_HOME_ADDRESS 0xc9 /* 11 0 01001 */ +#define IP6OPT_EID 0x8a /* 10 0 01010 */ + +#define IP6OPT_TYPE(o) ((o) & 0xC0) +#define IP6OPT_TYPE_SKIP 0x00 +#define IP6OPT_TYPE_DISCARD 0x40 +#define IP6OPT_TYPE_FORCEICMP 0x80 +#define IP6OPT_TYPE_ICMP 0xC0 + +#define IP6OPT_MUTABLE 0x20 + + +#ifdef __MINGW32__ +#ifndef EAI_ADDRFAMILY +struct addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for hostname */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ +}; +#endif +#endif /* __MINGW32__ */ diff --git a/src/tfe/arch/win32/pcap-stdinc.h b/src/tfe/arch/win32/pcap-stdinc.h new file mode 100644 index 0000000..221003d --- /dev/null +++ b/src/tfe/arch/win32/pcap-stdinc.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2002 + * Politecnico di Torino. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the Politecnico + * di Torino, and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Header$ (LBL) + */ + +#define SIZEOF_CHAR 1 +#define SIZEOF_SHORT 2 +#define SIZEOF_INT 4 + +#define _WINSOCKAPI_ +#include + +#include +#include "bittypes.h" +#include +#include + +#ifndef __MINGW32__ +#include "IP6_misc.h" +#endif + +#define caddr_t char* + +#define snprintf _snprintf +//#define vsnprintf _vsnprintf + diff --git a/src/tfe/arch/win32/pcap.h b/src/tfe/arch/win32/pcap.h new file mode 100644 index 0000000..52d958f --- /dev/null +++ b/src/tfe/arch/win32/pcap.h @@ -0,0 +1,273 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) $Header$ (LBL) + */ + +#ifndef lib_pcap_h +#define lib_pcap_h + +#ifdef WIN32 +/* RGJ Changed it to "pcap-stdinc.h" for AppleWin */ +#include "pcap-stdinc.h" +#else /* WIN32 */ +#include +#include +#endif /* WIN32 */ + +/* RGJ Changed it to "bpf.h" for AppleWin */ +#include "bpf.h" + +#include + +#ifdef REMOTE + // We have to define the SOCKET here, although it has been defined in sockutils.h + // This is to avoid the distribution of the 'sockutils.h' file around + // (for example in the WinPcap developer's pack) + #ifndef SOCKET + #ifdef WIN32 + #define SOCKET unsigned int + #else + #define SOCKET int + #endif + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define PCAP_VERSION_MAJOR 2 +#define PCAP_VERSION_MINOR 4 + +#define PCAP_ERRBUF_SIZE 256 + +/* + * Compatibility for systems that have a bpf.h that + * predates the bpf typedefs for 64-bit support. + */ +#if BPF_RELEASE - 0 < 199406 +typedef int bpf_int32; +typedef u_int bpf_u_int32; +#endif + +typedef struct pcap pcap_t; +typedef struct pcap_dumper pcap_dumper_t; +typedef struct pcap_if pcap_if_t; +typedef struct pcap_addr pcap_addr_t; + +/* + * The first record in the file contains saved values for some + * of the flags used in the printout phases of tcpdump. + * Many fields here are 32 bit ints so compilers won't insert unwanted + * padding; these files need to be interchangeable across architectures. + * + * Do not change the layout of this structure, in any way (this includes + * changes that only affect the length of fields in this structure). + * + * Also, do not change the interpretation of any of the members of this + * structure, in any way (this includes using values other than + * LINKTYPE_ values, as defined in "savefile.c", in the "linktype" + * field). + * + * Instead: + * + * introduce a new structure for the new format, if the layout + * of the structure changed; + * + * send mail to "tcpdump-workers@tcpdump.org", requesting a new + * magic number for your new capture file format, and, when + * you get the new magic number, put it in "savefile.c"; + * + * use that magic number for save files with the changed file + * header; + * + * make the code in "savefile.c" capable of reading files with + * the old file header as well as files with the new file header + * (using the magic number to determine the header format). + * + * Then supply the changes to "patches@tcpdump.org", so that future + * versions of libpcap and programs that use it (such as tcpdump) will + * be able to read your new capture file format. + */ +struct pcap_file_header { + bpf_u_int32 magic; + u_short version_major; + u_short version_minor; + bpf_int32 thiszone; /* gmt to local correction */ + bpf_u_int32 sigfigs; /* accuracy of timestamps */ + bpf_u_int32 snaplen; /* max length saved portion of each pkt */ + bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */ +}; + +/* + * Each packet in the dump file is prepended with this generic header. + * This gets around the problem of different headers for different + * packet interfaces. + */ +struct pcap_pkthdr { + struct timeval ts; /* time stamp */ + bpf_u_int32 caplen; /* length of portion present */ + bpf_u_int32 len; /* length this packet (off wire) */ +}; + +/* + * As returned by the pcap_stats() + */ +struct pcap_stat { + u_int ps_recv; /* number of packets received */ + u_int ps_drop; /* number of packets dropped */ + u_int ps_ifdrop; /* drops by interface XXX not yet supported */ +#ifdef REMOTE +#ifdef WIN32 +// u_int bs_capt; /* number of packets that reach the application */ +#endif /* WIN32 */ + u_int ps_capt; /* number of packets that reach the application; please get rid off the Win32 ifdef */ + u_int ps_sent; /* number of packets sent by the server on the network */ + u_int ps_netdrop; /* number of packets lost on the network */ +#endif +}; + +/* + * Item in a list of interfaces. + */ +struct pcap_if { + struct pcap_if *next; + char *name; /* name to hand to "pcap_open_live()" */ + char *description; /* textual description of interface, or NULL */ + struct pcap_addr *addresses; + bpf_u_int32 flags; /* PCAP_IF_ interface flags */ +}; + +#define PCAP_IF_LOOPBACK 0x00000001 /* interface is loopback */ + +/* + * Representation of an interface address. + */ +struct pcap_addr { + struct pcap_addr *next; + struct sockaddr *addr; /* address */ + struct sockaddr *netmask; /* netmask for that address */ + struct sockaddr *broadaddr; /* broadcast address for that address */ + struct sockaddr *dstaddr; /* P2P destination address for that address */ +}; + +typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, + const u_char *); + +char *pcap_lookupdev(char *); +int pcap_lookupnet(const char *, bpf_u_int32 *, bpf_u_int32 *, char *); +pcap_t *pcap_open_live(const char *, int, int, int, char *); +pcap_t *pcap_open_dead(int, int); +pcap_t *pcap_open_offline(const char *, char *); +void pcap_close(pcap_t *); +int pcap_loop(pcap_t *, int, pcap_handler, u_char *); +int pcap_dispatch(pcap_t *, int, pcap_handler, u_char *); +const u_char* + pcap_next(pcap_t *, struct pcap_pkthdr *); +int pcap_stats(pcap_t *, struct pcap_stat *); +int pcap_setfilter(pcap_t *, struct bpf_program *); +int pcap_getnonblock(pcap_t *, char *); +int pcap_setnonblock(pcap_t *, int, char *); +void pcap_perror(pcap_t *, char *); +char *pcap_strerror(int); +char *pcap_geterr(pcap_t *); +int pcap_compile(pcap_t *, struct bpf_program *, char *, int, + bpf_u_int32); +int pcap_compile_nopcap(int, int, struct bpf_program *, + char *, int, bpf_u_int32); +void pcap_freecode(struct bpf_program *); +int pcap_datalink(pcap_t *); +int pcap_list_datalinks(pcap_t *, int **); +int pcap_set_datalink(pcap_t *, int); +int pcap_datalink_name_to_val(const char *); +const char *pcap_datalink_val_to_name(int); +int pcap_snapshot(pcap_t *); +int pcap_is_swapped(pcap_t *); +int pcap_major_version(pcap_t *); +int pcap_minor_version(pcap_t *); + +/* XXX */ +FILE *pcap_file(pcap_t *); +int pcap_fileno(pcap_t *); + +pcap_dumper_t *pcap_dump_open(pcap_t *, const char *); +int pcap_dump_flush(pcap_dumper_t *); +void pcap_dump_close(pcap_dumper_t *); +void pcap_dump(u_char *, const struct pcap_pkthdr *, const u_char *); + +int pcap_findalldevs(pcap_if_t **, char *); +void pcap_freealldevs(pcap_if_t *); + +/* To avoid callback, this returns one packet at a time */ +int pcap_next_ex(pcap_t *p, struct pcap_pkthdr **pkt_header, u_char **pkt_data); + +/* XXX this guy lives in the bpf tree */ +u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int); +int bpf_validate(struct bpf_insn *f, int len); +char *bpf_image(struct bpf_insn *, int); +void bpf_dump(struct bpf_program *, int); + +#ifdef WIN32 +/* + * Win32 definitions + */ + +int pcap_setbuff(pcap_t *p, int dim); +int pcap_setmode(pcap_t *p, int mode); +int pcap_sendpacket(pcap_t *p, u_char *buf, int size); +int pcap_setmintocopy(pcap_t *p, int size); + +#ifdef WPCAP +/* Include file with the wpcap-specific extensions */ +#include +#endif + +#define MODE_CAPT 0 +#define MODE_STAT 1 +#define MODE_MON 2 + +#endif /* WIN32 */ + +#ifdef REMOTE +/* Includes most of the public stuff that is needed for the remote capture */ +#include "remote-ext.h" +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/tfe/arch/win32/tfearch.c b/src/tfe/arch/win32/tfearch.c new file mode 100644 index 0000000..d85527c --- /dev/null +++ b/src/tfe/arch/win32/tfearch.c @@ -0,0 +1,556 @@ +/* + * tfearch.c - TFE ("The final ethernet") emulation, + * architecture-dependant stuff + * + * Written by + * Spiro Trikaliotis + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + + +/* #define WPCAP */ + +#include "pcap.h" + +#include +#include +#include +#include + +#include "..\..\defc.h" + + +typedef pcap_t *(*pcap_open_live_t)(const char *, int, int, int, char *); +typedef int (*pcap_dispatch_t)(pcap_t *, int, pcap_handler, u_char *); +typedef int (*pcap_setnonblock_t)(pcap_t *, int, char *); +typedef int (*pcap_datalink_t)(pcap_t *); +typedef int (*pcap_findalldevs_t)(pcap_if_t **, char *); +typedef void (*pcap_freealldevs_t)(pcap_if_t *); +typedef int (*pcap_sendpacket_t)(pcap_t *p, u_char *buf, int size); + +/** #define TFE_DEBUG_ARCH 1 **/ +/** #define TFE_DEBUG_PKTDUMP 1 **/ + +/* #define TFE_DEBUG_FRAMES - might be defined in TFE.H! */ + +#define TFE_DEBUG_WARN 1 /* this should not be deactivated */ + +static pcap_open_live_t p_pcap_open_live; +static pcap_dispatch_t p_pcap_dispatch; +static pcap_setnonblock_t p_pcap_setnonblock; +static pcap_findalldevs_t p_pcap_findalldevs; +static pcap_freealldevs_t p_pcap_freealldevs; +static pcap_sendpacket_t p_pcap_sendpacket; +static pcap_datalink_t p_pcap_datalink; + +static HINSTANCE pcap_library = NULL; + + +/* ------------------------------------------------------------------------- */ +/* variables needed */ + + +//static log_t tfe_arch_log = LOG_ERR; + + +static pcap_if_t *TfePcapNextDev = NULL; +static pcap_if_t *TfePcapAlldevs = NULL; +static pcap_t *TfePcapFP = NULL; + +static char TfePcapErrbuf[PCAP_ERRBUF_SIZE]; + +#ifdef TFE_DEBUG_PKTDUMP + +static +void debug_output( const char *text, BYTE *what, int count ) +{ + char buffer[256]; + char *p = buffer; + char *pbuffer1 = what; + int len1 = count; + int i; + + sprintf(buffer, "\n%s: length = %u\n", text, len1); + OutputDebugString(buffer); + do { + p = buffer; + for (i=0; (i<8) && len1>0; len1--, i++) { + sprintf( p, "%02x ", (unsigned int)(unsigned char)*pbuffer1++); + p += 3; + } + *(p-1) = '\n'; *p = 0; + OutputDebugString(buffer); + } while (len1>0); +} +#endif // #ifdef TFE_DEBUG_PKTDUMP + + +static +void TfePcapFreeLibrary(void) +{ + if (pcap_library) { + if (!FreeLibrary(pcap_library)) { + //log_message(tfe_arch_log, "FreeLibrary WPCAP.DLL failed!"); + } + pcap_library = NULL; + + p_pcap_open_live = NULL; + p_pcap_dispatch = NULL; + p_pcap_setnonblock = NULL; + p_pcap_findalldevs = NULL; + p_pcap_freealldevs = NULL; + p_pcap_sendpacket = NULL; + p_pcap_datalink = NULL; + } +} + +/* since I don't like typing too much... */ +#define GET_PROC_ADDRESS_AND_TEST( _name_ ) \ + p_##_name_ = (_name_##_t) GetProcAddress(pcap_library, #_name_ ); \ + if (!p_##_name_ ) { \ + /*log_message(tfe_arch_log, "GetProcAddress " #_name_ " failed!");*/ \ + TfePcapFreeLibrary(); \ + return FALSE; \ + } + +static +BOOL TfePcapLoadLibrary(void) +{ + if (!pcap_library) { + pcap_library = LoadLibrary("wpcap.dll"); + + if (!pcap_library) { + //log_message(tfe_arch_log, "LoadLibrary WPCAP.DLL failed!" ); + return FALSE; + } + + GET_PROC_ADDRESS_AND_TEST(pcap_open_live); + GET_PROC_ADDRESS_AND_TEST(pcap_dispatch); + GET_PROC_ADDRESS_AND_TEST(pcap_setnonblock); + GET_PROC_ADDRESS_AND_TEST(pcap_findalldevs); + GET_PROC_ADDRESS_AND_TEST(pcap_freealldevs); + GET_PROC_ADDRESS_AND_TEST(pcap_sendpacket); + GET_PROC_ADDRESS_AND_TEST(pcap_datalink); + } + + return TRUE; +} + +#undef GET_PROC_ADDRESS_AND_TEST + + +/* +static +void TfePcapCloseAdapter(void) +{ + if (TfePcapAlldevs) { + (*p_pcap_freealldevs)(TfePcapAlldevs); + TfePcapAlldevs = NULL; + } +} +*/ + +/* + These functions let the UI enumerate the available interfaces. + + First, TfeEnumAdapterOpen() is used to start enumeration. + + TfeEnumAdapter is then used to gather information for each adapter present + on the system, where: + + ppname points to a pointer which will hold the name of the interface + ppdescription points to a pointer which will hold the description of the interface + + For each of these parameters, new memory is allocated, so it has to be + freed with lib_free(). + + TfeEnumAdapterClose() must be used to stop processing. + + Each function returns 1 on success, and 0 on failure. + TfeEnumAdapter() only fails if there is no more adpater; in this case, + *ppname and *ppdescription are not altered. +*/ +int tfe_arch_enumadapter_open(void) +{ + if (!TfePcapLoadLibrary()) { + return 0; + } + + if ((*p_pcap_findalldevs)(&TfePcapAlldevs, TfePcapErrbuf) == -1) + { + //log_message(tfe_arch_log, "ERROR in TfeEnumAdapterOpen: pcap_findalldevs: '%s'", TfePcapErrbuf); + return 0; + } + + if (!TfePcapAlldevs) { + /*log_message(tfe_arch_log, "ERROR in TfeEnumAdapterOpen, finding all pcap devices - " + "Do we have the necessary privilege rights?");*/ + return 0; + } + + TfePcapNextDev = TfePcapAlldevs; + + return 1; +} + +int tfe_arch_enumadapter(char **ppname, char **ppdescription) +{ + if (!TfePcapNextDev) + return 0; + + *ppname = lib_stralloc(TfePcapNextDev->name); + *ppdescription = lib_stralloc(TfePcapNextDev->description); + + TfePcapNextDev = TfePcapNextDev->next; + + return 1; +} + +int tfe_arch_enumadapter_close(void) +{ + if (TfePcapAlldevs) { + (*p_pcap_freealldevs)(TfePcapAlldevs); + TfePcapAlldevs = NULL; + } + return 1; +} + +static +BOOL TfePcapOpenAdapter(const char *interface_name) +{ + pcap_if_t *TfePcapDevice = NULL; + + if (!tfe_enumadapter_open()) { + return FALSE; + } + else { + /* look if we can find the specified adapter */ + char *pname; + char *pdescription; + BOOL found = FALSE; + + if (interface_name) { + /* we have an interface name, try it */ + TfePcapDevice = TfePcapAlldevs; + + while (tfe_enumadapter(&pname, &pdescription)) { + if (strcmp(pname, interface_name)==0) { + found = TRUE; + } + lib_free(pname); + lib_free(pdescription); + if (found) break; + TfePcapDevice = TfePcapNextDev; + } + } + + if (!found) { + /* just take the first adapter */ + TfePcapDevice = TfePcapAlldevs; + } + } + + TfePcapFP = (*p_pcap_open_live)(TfePcapDevice->name, 1700, 1, 20, TfePcapErrbuf); + if ( TfePcapFP == NULL) + { + //log_message(tfe_arch_log, "ERROR opening adapter: '%s'", TfePcapErrbuf); + tfe_enumadapter_close(); + return FALSE; + } + + if ((*p_pcap_setnonblock)(TfePcapFP, 1, TfePcapErrbuf)<0) + { + //log_message(tfe_arch_log, "WARNING: Setting PCAP to non-blocking failed: '%s'", TfePcapErrbuf); + } + + /* Check the link layer. We support only Ethernet for simplicity. */ + if((*p_pcap_datalink)(TfePcapFP) != DLT_EN10MB) + { + //log_message(tfe_arch_log, "ERROR: TFE works only on Ethernet networks."); + tfe_enumadapter_close(); + return FALSE; + } + + tfe_enumadapter_close(); + return TRUE; +} + + +/* ------------------------------------------------------------------------- */ +/* the architecture-dependend functions */ + + +int tfe_arch_init(void) +{ + //tfe_arch_log = log_open("TFEARCH"); + + if (!TfePcapLoadLibrary()) { + return 0; + } + + return 1; +} + +void tfe_arch_pre_reset( void ) +{ +#ifdef TFE_DEBUG_ARCH + log_message( tfe_arch_log, "tfe_arch_pre_reset()." ); +#endif +} + +void tfe_arch_post_reset( void ) +{ +#ifdef TFE_DEBUG_ARCH + log_message( tfe_arch_log, "tfe_arch_post_reset()." ); +#endif +} + +int tfe_arch_activate(const char *interface_name) +{ +#ifdef TFE_DEBUG_ARCH + log_message( tfe_arch_log, "tfe_arch_activate()." ); +#endif + if (!TfePcapOpenAdapter(interface_name)) { + return 0; + } + return 1; +} + +void tfe_arch_deactivate( void ) +{ +#ifdef TFE_DEBUG_ARCH + log_message( tfe_arch_log, "tfe_arch_deactivate()." ); +#endif +} + +void tfe_arch_set_mac( const BYTE mac[6] ) +{ +#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES) + log_message( tfe_arch_log, "New MAC address set: %02X:%02X:%02X:%02X:%02X:%02X.", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); +#endif +} + +void tfe_arch_set_hashfilter(const int hash_mask[2]) +{ +#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES) + log_message( tfe_arch_log, "New hash filter set: %08X:%08X.", + hash_mask[1], hash_mask[0]); +#endif +} + + +/* +void tfe_arch_receive_remove_committed_frame(void) +{ +#ifdef TFE_DEBUG_ARCH + log_message( tfe_arch_log, "tfe_arch_receive_remove_committed_frame()." ); +#endif +} +*/ + +void tfe_arch_recv_ctl( int bBroadcast, /* broadcast */ + int bIA, /* individual address (IA) */ + int bMulticast, /* multicast if address passes the hash filter */ + int bCorrect, /* accept correct frames */ + int bPromiscuous, /* promiscuous mode */ + int bIAHash /* accept if IA passes the hash filter */ + ) +{ +#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES) + log_message( tfe_arch_log, "tfe_arch_recv_ctl() called with the following parameters:" ); + log_message( tfe_arch_log, "\tbBroadcast = %s", bBroadcast ? "TRUE" : "FALSE" ); + log_message( tfe_arch_log, "\tbIA = %s", bIA ? "TRUE" : "FALSE" ); + log_message( tfe_arch_log, "\tbMulticast = %s", bMulticast ? "TRUE" : "FALSE" ); + log_message( tfe_arch_log, "\tbCorrect = %s", bCorrect ? "TRUE" : "FALSE" ); + log_message( tfe_arch_log, "\tbPromiscuous = %s", bPromiscuous ? "TRUE" : "FALSE" ); + log_message( tfe_arch_log, "\tbIAHash = %s", bIAHash ? "TRUE" : "FALSE" ); +#endif +} + +void tfe_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver ) +{ +#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES) + log_message( tfe_arch_log, "tfe_arch_line_ctl() called with the following parameters:" ); + log_message( tfe_arch_log, "\tbEnableTransmitter = %s", bEnableTransmitter ? "TRUE" : "FALSE" ); + log_message( tfe_arch_log, "\tbEnableReceiver = %s", bEnableReceiver ? "TRUE" : "FALSE" ); +#endif +} + + +typedef struct TFE_PCAP_INTERNAL_tag { + + unsigned int len; + BYTE *buffer; + +} TFE_PCAP_INTERNAL; + +/* Callback function invoked by libpcap for every incoming packet */ +static +void TfePcapPacketHandler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) +{ + TFE_PCAP_INTERNAL *pinternal = (void*)param; + + /* determine the count of bytes which has been returned, + * but make sure not to overrun the buffer + */ + if (header->caplen < pinternal->len) + pinternal->len = header->caplen; + + memcpy(pinternal->buffer, pkt_data, pinternal->len); +} + +/* the following function receives a frame. + + If there's none, it returns a -1. + If there is one, it returns the length of the frame in bytes. + + It copies the frame to *buffer and returns the number of copied + bytes as return value. + + At most 'len' bytes are copied. +*/ +static +int tfe_arch_receive_frame(TFE_PCAP_INTERNAL *pinternal) +{ + int ret = -1; + + /* check if there is something to receive */ + if ((*p_pcap_dispatch)(TfePcapFP, 1, TfePcapPacketHandler, (void*)pinternal)!=0) { + /* Something has been received */ + ret = pinternal->len; + } + +#ifdef TFE_DEBUG_ARCH + log_message( tfe_arch_log, "tfe_arch_receive_frame() called, returns %d.", ret ); +#endif + + return ret; +} + +void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in transmit buffer */ + int onecoll, /* ONECOLL: Terminate after just one collision */ + int inhibit_crc, /* INHIBITCRC: Do not append CRC to the transmission */ + int tx_pad_dis, /* TXPADDIS: Disable padding to 60 Bytes */ + int txlength, /* Frame length */ + BYTE *txframe /* Pointer to the frame to be transmitted */ + ) +{ +#ifdef TFE_DEBUG_ARCH + log_message( tfe_arch_log, "tfe_arch_transmit() called, with: " + "force = %s, onecoll = %s, inhibit_crc=%s, tx_pad_dis=%s, txlength=%u", + force ? "TRUE" : "FALSE", + onecoll ? "TRUE" : "FALSE", + inhibit_crc ? "TRUE" : "FALSE", + tx_pad_dis ? "TRUE" : "FALSE", + txlength + ); +#endif + +#ifdef TFE_DEBUG_PKTDUMP + debug_output( "Transmit frame: ", txframe, txlength); +#endif // #ifdef TFE_DEBUG_PKTDUMP + + if ((*p_pcap_sendpacket)(TfePcapFP, txframe, txlength) == -1) { + //log_message(tfe_arch_log, "WARNING! Could not send packet!"); + } +} + +/* + tfe_arch_receive() + + This function checks if there was a frame received. + If so, it returns 1, else 0. + + If there was no frame, none of the parameters is changed! + + If there was a frame, the following actions are done: + + - at maximum *plen byte are transferred into the buffer given by pbuffer + - *plen gets the length of the received frame, EVEN if this is more + than has been copied to pbuffer! + - if the dest. address was accepted by the hash filter, *phashed is set, else + cleared. + - if the dest. address was accepted by the hash filter, *phash_index is + set to the number of the rule leading to the acceptance + - if the receive was ok (good CRC and valid length), *prx_ok is set, + else cleared. + - if the dest. address was accepted because it's exactly our MAC address + (set by tfe_arch_set_mac()), *pcorrect_mac is set, else cleared. + - if the dest. address was accepted since it was a broadcast address, + *pbroadcast is set, else cleared. + - if the received frame had a crc error, *pcrc_error is set, else cleared +*/ +int tfe_arch_receive(BYTE *pbuffer , /* where to store a frame */ + int *plen, /* IN: maximum length of frame to copy; + OUT: length of received frame + OUT can be bigger than IN if received frame was + longer than supplied buffer */ + int *phashed, /* set if the dest. address is accepted by the hash filter */ + int *phash_index, /* hash table index if hashed == TRUE */ + int *prx_ok, /* set if good CRC and valid length */ + int *pcorrect_mac, /* set if dest. address is exactly our IA */ + int *pbroadcast, /* set if dest. address is a broadcast address */ + int *pcrc_error /* set if received frame had a CRC error */ + ) +{ + int len; + + TFE_PCAP_INTERNAL internal = { *plen, pbuffer }; + + +#ifdef TFE_DEBUG_ARCH + log_message( tfe_arch_log, "tfe_arch_receive() called, with *plen=%u.", *plen ); +#endif + + assert((*plen&1)==0); + + len = tfe_arch_receive_frame(&internal); + + if (len!=-1) { + +#ifdef TFE_DEBUG_PKTDUMP + debug_output( "Received frame: ", internal.buffer, internal.len ); +#endif // #ifdef TFE_DEBUG_PKTDUMP + + if (len&1) + ++len; + + *plen = len; + + /* we don't decide if this frame fits the needs; + * by setting all zero, we let tfe.c do the work + * for us + */ + *phashed = + *phash_index = + *pbroadcast = + *pcorrect_mac = + *pcrc_error = 0; + + /* this frame has been received correctly */ + *prx_ok = 1; + + return 1; + } + + return 0; +} diff --git a/src/tfe/tfe.c b/src/tfe/tfe.c new file mode 100644 index 0000000..63ddc09 --- /dev/null +++ b/src/tfe/tfe.c @@ -0,0 +1,1987 @@ +/* + * tfe.c - TFE ("The final ethernet") emulation. + * + * Written by + * Spiro Trikaliotis + * Christian Vogelgsang + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + + + + +#include +#include +#include +#include +#ifdef DOS_TFE +#include +#endif + +#include "..\defc.h" +#include "tfesupp.h" + + +/**/ + +/* warn illegal behaviour */ +/* #define TFE_DEBUG_WARN_REG 1 */ /* warn about invalid register accesses */ +/* #define TFE_DEBUG_WARN_RXTX 1 */ /* warn about invalid rx or tx conditions */ + +/** #define TFE_DEBUG_INIT 1 **/ +/** #define TFE_DEBUG_LOAD 1 **/ /* enable to see tfe port reads */ +/** #define TFE_DEBUG_STORE 1 **/ /* enable to see tfe port writes */ +/** #define TFE_DEBUG_REGISTERS 1 **/ /* enable to see CS8900a register I/O */ +/** #define TFE_DEBUG_IGNORE_RXEVENT 1 **/ /* enable to ignore RXEVENT in DEBUG_REGISTERS */ +/** #define TFE_DEBUG_RXTX_STATE 1 **/ /* enable to see tranceiver state changes */ +/** #define TFE_DEBUG_RXTX_DATA 1 **/ /* enable to see data in/out flow */ +/** #define TFE_DEBUG_FRAMES 1 **/ /* enable to see arch frame send/recv */ + +/* ------------------------------------------------------------------------- */ +/* variables needed */ + +/* + This variable is used when we need to postpone the initialization + because tfe_init() is not yet called +*/ +static int should_activate = 0; +static int init_tfe_flag = 0; +typedef signed int log_t; +#define LOG_ERR ((log_t)-1) +#define LOG_DEFAULT ((log_t)-2) + +static log_t tfe_log = LOG_ERR; + + +/* status which received packages to accept + This is used in tfe_should_accept(). +*/ +static byte tfe_ia_mac[6] = { 0,0,0,0,0,0 }; + +/* remember the value of the hash mask */ +static DWORD tfe_hash_mask[2]; + +/* reveiver setup */ +static word16 tfe_recv_control = 0; /* copy of CC_RXCTL (contains all bits below) */ +static int tfe_recv_broadcast = 0; /* broadcast */ +static int tfe_recv_mac = 0; /* individual address (IA) */ +static int tfe_recv_multicast = 0; /* multicast if address passes the hash filter */ +static int tfe_recv_correct = 0; /* accept correct frames */ +static int tfe_recv_promiscuous = 0; /* promiscuous mode */ +static int tfe_recv_hashfilter = 0; /* accept if IA passes the hash filter */ + +/* Flag: Can we even use TFE, or is the hardware not available? */ +static int tfe_cannot_use = 0; + +/* Flag: Do we have the TFE enabled? */ +int tfe_enabled = 0; + +/* Flag: Do we use the "original" memory map or the memory map of the RR-Net? */ +int tfe_as_rr_net = 0; + +static char *tfe_interface = NULL; + +/* TFE registers */ +/* these are the 8 16-bit-ports for "I/O space configuration" + (see 4.10 on page 75 of cs8900a-4.pdf, the cs8900a data sheet) + + REMARK: The TFE operatoes the cs8900a in IO space configuration, as + it generates I/OW and I/OR signals. +*/ +#define TFE_COUNT_IO_REGISTER 0x10 /* we have 16 I/O register */ + +static byte *tfe = NULL; +/* + RW: RXTXDATA = DE00/DE01 + RW: RXTXDATA2 = DE02/DE03 (for 32-bit-operation) + -W: TXCMD = DE04/DE05 (TxCMD, Transmit Command) mapped to PP + 0144 (Reg. 9, Sec. 4.4, page 46) + -W: TXLENGTH = DE06/DE07 (TxLenght, Transmit Length) mapped to PP + 0146 + R-: INTSTQUEUE = DE08/DE09 (Interrupt Status Queue) mapped to PP + 0120 (ISQ, Sec. 5.1, page 78) + RW: PP_PTR = DE0A/DE0B (PacketPage Pointer) (see. page 75p: Read -011.---- ----.----) + RW: PP_DATA0 = DE0C/DE0D (PacketPage Data (Port 0)) + RW: PP_DATA1 = DE0E/DE0F (PacketPage Data (Port 1)) (for 32 bit only) +*/ + +#define TFE_ADDR_RXTXDATA 0x00 /* RW */ +#define TFE_ADDR_RXTXDATA2 0x02 /* RW 32 bit only! */ +#define TFE_ADDR_TXCMD 0x04 /* -W Maps to PP+0144 */ +#define TFE_ADDR_TXLENGTH 0x06 /* -W Maps to PP+0146 */ +#define TFE_ADDR_INTSTQUEUE 0x08 /* R- Interrupt status queue, maps to PP + 0120 */ +#define TFE_ADDR_PP_PTR 0x0a /* RW PacketPage Pointer */ +#define TFE_ADDR_PP_DATA 0x0c /* RW PacketPage Data, Port 0 */ +#define TFE_ADDR_PP_DATA2 0x0e /* RW PacketPage Data, Port 1 - 32 bit only */ + +/* Makros for reading and writing the visible TFE register: */ +#define GET_TFE_8( _xxx_ ) \ + ( assert(_xxx_> 8) & 0xff; \ + } while (0) + +/* The PacketPage register */ +/* note: The locations 0 to MAX_PACKETPAGE_ARRAY-1 are handled in this array. */ + +#define MAX_PACKETPAGE_ARRAY 0x1000 /* 4 KB */ + +static byte *tfe_packetpage = NULL; + +static word16 tfe_packetpage_ptr = 0; + +/* Makros for reading and writing the PacketPage register: */ + +#define GET_PP_8( _xxx_ ) \ + (assert(_xxx_> 8) & 0xFF; \ + } while (0) + +#define SET_PP_32( _xxx_, _val_ ) \ + do { \ + assert(_xxx_> 8) & 0xFF; \ + tfe_packetpage[_xxx_+2] = (_val_>>16) & 0xFF; \ + tfe_packetpage[_xxx_+3] = (_val_>>24) & 0xFF; \ + } while (0) + + +/* The packetpage register: see p. 39f */ +#define TFE_PP_ADDR_PRODUCTID 0x0000 /* R- - 4.3., p. 41 */ +#define TFE_PP_ADDR_IOBASE 0x0020 /* i RW - 4.3., p. 41 - 4.7., p. 72 */ +#define TFE_PP_ADDR_INTNO 0x0022 /* i RW - 3.2., p. 17 - 4.3., p. 41 */ +#define TFE_PP_ADDR_DMA_CHAN 0x0024 /* i RW - 3.2., p. 17 - 4.3., p. 41 */ +#define TFE_PP_ADDR_DMA_SOF 0x0026 /* ? R- - 4.3., p. 41 - 5.4., p. 89 */ +#define TFE_PP_ADDR_DMA_FC 0x0028 /* ? R- - 4.3., p. 41, "Receive DMA" */ +#define TFE_PP_ADDR_RXDMA_BC 0x002a /* ? R- - 4.3., p. 41 - 5.4., p. 89 */ +#define TFE_PP_ADDR_MEMBASE 0x002c /* i RW - 4.3., p. 41 - 4.9., p. 73 */ +#define TFE_PP_ADDR_BPROM_BASE 0x0030 /* i RW - 3.6., p. 24 - 4.3., p. 41 */ +#define TFE_PP_ADDR_BPROM_MASK 0x0034 /* i RW - 3.6., p. 24 - 4.3., p. 41 */ +/* 0x0038 - 0x003F: reserved */ +#define TFE_PP_ADDR_EEPROM_CMD 0x0040 /* i RW - 3.5., p. 23 - 4.3., p. 41 */ +#define TFE_PP_ADDR_EEPROM_DATA 0x0042 /* i RW - 3.5., p. 23 - 4.3., p. 41 */ +/* 0x0044 - 0x004F: reserved */ +#define TFE_PP_ADDR_REC_FRAME_BC 0x0050 /* RW - 4.3., p. 41 - 5.2.9., p. 86 */ +/* 0x0052 - 0x00FF: reserved */ +#define TFE_PP_ADDR_CONF_CTRL 0x0100 /* - RW - 4.4., p. 46; see below */ +#define TFE_PP_ADDR_STATUS_EVENT 0x0120 /* - R- - 4.4., p. 46; see below */ +/* 0x0140 - 0x0143: reserved */ +#define TFE_PP_ADDR_TXCMD 0x0144 /* # -W - 4.5., p. 70 - 5.7., p. 98 */ +#define TFE_PP_ADDR_TXLENGTH 0x0146 /* # -W - 4.5., p. 70 - 5.7., p. 98 */ +/* 0x0148 - 0x014F: reserved */ +#define TFE_PP_ADDR_LOG_ADDR_FILTER 0x0150 /* RW - 4.6., p. 71 - 5.3., p. 86 */ +#define TFE_PP_ADDR_MAC_ADDR 0x0158 /* # RW - 4.6., p. 71 - 5.3., p. 86 */ +/* 0x015E - 0x03FF: reserved */ +#define TFE_PP_ADDR_RXSTATUS 0x0400 /* R- - 4.7., p. 72 - 5.2., p. 78 */ +#define TFE_PP_ADDR_RXLENGTH 0x0402 /* R- - 4.7., p. 72 - 5.2., p. 78 */ +#define TFE_PP_ADDR_RX_FRAMELOC 0x0404 /* R- - 4.7., p. 72 - 5.2., p. 78 */ +/* here, the received frame is stored */ +#define TFE_PP_ADDR_TX_FRAMELOC 0x0A00 /* -W - 4.7., p. 72 - 5.7., p. 98 */ +/* here, the frame to transmit is stored */ +#define TFE_PP_ADDR_END 0x1000 /* memory to TFE_PP_ADDR_END-1 is used */ + + +/* TFE_PP_ADDR_CONF_CTRL is subdivided: */ +#define TFE_PP_ADDR_CC_RXCFG 0x0102 /* # RW - 4.4.6., p. 52 - 0003 */ +#define TFE_PP_ADDR_CC_RXCTL 0x0104 /* # RW - 4.4.8., p. 54 - 0005 */ +#define TFE_PP_ADDR_CC_TXCFG 0x0106 /* RW - 4.4.9., p. 55 - 0007 */ +#define TFE_PP_ADDR_CC_TXCMD 0x0108 /* R- - 4.4.11., p. 57 - 0009 */ +#define TFE_PP_ADDR_CC_BUFCFG 0x010A /* RW - 4.4.12., p. 58 - 000B */ +#define TFE_PP_ADDR_CC_LINECTL 0x0112 /* # RW - 4.4.16., p. 62 - 0013 */ +#define TFE_PP_ADDR_CC_SELFCTL 0x0114 /* RW - 4.4.18., p. 64 - 0015 */ +#define TFE_PP_ADDR_CC_BUSCTL 0x0116 /* RW - 4.4.20., p. 66 - 0017 */ +#define TFE_PP_ADDR_CC_TESTCTL 0x0118 /* RW - 4.4.22., p. 68 - 0019 */ + +/* TFE_PP_ADDR_STATUS_EVENT is subdivided: */ +#define TFE_PP_ADDR_SE_ISQ 0x0120 /* R- - 4.4.5., p. 51 - 0000 */ +#define TFE_PP_ADDR_SE_RXEVENT 0x0124 /* # R- - 4.4.7., p. 53 - 0004 */ +#define TFE_PP_ADDR_SE_TXEVENT 0x0128 /* R- - 4.4.10., p. 56 - 0008 */ +#define TFE_PP_ADDR_SE_BUFEVENT 0x012C /* R- - 4.4.13., p. 59 - 000C */ +#define TFE_PP_ADDR_SE_RXMISS 0x0130 /* R- - 4.4.14., p. 60 - 0010 */ +#define TFE_PP_ADDR_SE_TXCOL 0x0132 /* R- - 4.4.15., p. 61 - 0012 */ +#define TFE_PP_ADDR_SE_LINEST 0x0134 /* R- - 4.4.17., p. 63 - 0014 */ +#define TFE_PP_ADDR_SE_SELFST 0x0136 /* R- - 4.4.19., p. 65 - 0016 */ +#define TFE_PP_ADDR_SE_BUSST 0x0138 /* # R- - 4.4.21., p. 67 - 0018 */ +#define TFE_PP_ADDR_SE_TDR 0x013C /* R- - 4.4.23., p. 69 - 001C */ + + +/* ------------------------------------------------------------------------- */ +/* more variables needed */ + +static word16 tx_buffer = TFE_PP_ADDR_TX_FRAMELOC; +static word16 rx_buffer = TFE_PP_ADDR_RXSTATUS; + +static word16 tx_count = 0; +static word16 rx_count = 0; +static word16 tx_length = 0; +static word16 rx_length = 0; + +#define TFE_TX_IDLE 0 +#define TFE_TX_GOT_CMD 1 +#define TFE_TX_GOT_LEN 2 +#define TFE_TX_READ_BUSST 3 + +#define TFE_RX_IDLE 0 +#define TFE_RX_GOT_FRAME 1 + +/* tranceiver state */ +static int tx_state = TFE_TX_IDLE; +static int rx_state = TFE_RX_IDLE; +static int tx_enabled = 0; +static int rx_enabled = 0; + +static int rxevent_read_mask = 3; /* set if L and/or H byte was read in RXEVENT? */ + + +/* ------------------------------------------------------------------------- */ +/* some parameter definitions */ + +#define MAX_TXLENGTH 1518 +#define MIN_TXLENGTH 4 + +#define MAX_RXLENGTH 1518 +#define MIN_RXLENGTH 64 + + +/* ------------------------------------------------------------------------- */ +/* debugging functions */ + +#ifdef TFE_DEBUG_FRAMES + +static int TfeDebugMaxFrameLengthToDump = 150; + +static char *debug_outbuffer(const int length, const unsigned char * const buffer) +{ +#define MAXLEN_DEBUG 1600 + + int i; + static char outbuffer[MAXLEN_DEBUG*4+1]; + char *p = outbuffer; + + assert( TfeDebugMaxFrameLengthToDump <= MAXLEN_DEBUG ); + + *p = 0; + + for (i=0; i=length) + break; + + sprintf( p, "%02X%c", buffer[i], ((i+1)%16==0)?'*':(((i+1)%8==0)?'-':' ')); + p+=3; + } + + return outbuffer; +} + +#endif + +/* ------------------------------------------------------------------------- */ +/* initialization and deinitialization functions */ + +static void tfe_set_tx_status(int ready,int error) +{ + word16 old_status = GET_PP_16(TFE_PP_ADDR_SE_BUSST); + + /* mask out TxBidErr and Rdy4TxNOW */ + word16 new_status = old_status & ~0x180; + if(ready) + new_status |= 0x100; /* set Rdy4TxNOW */ + if(error) + new_status |= 0x080; /* set TxBidErr */ + + if(new_status!=old_status) { + SET_PP_16(TFE_PP_ADDR_SE_BUSST,new_status); +#ifdef TFE_DEBUG_RXTX_STATE + log_message(tfe_log,"TX: set status Rdy4TxNOW=%d TxBidErr=%d", + ready,error); +#endif + } +} + +static void tfe_set_receiver(int enabled) +{ + rx_enabled = enabled; + rx_state = TFE_RX_IDLE; + + rxevent_read_mask = 3; /* was L or H byte read in RXEVENT? */ +} + +static void tfe_set_transmitter(int enabled) +{ + tx_enabled = enabled; + tx_state = TFE_TX_IDLE; + + tfe_set_tx_status(0,0); +} + +void tfe_reset(void) +{ + if (tfe_enabled && !should_activate) + { + int i; + + assert( tfe ); + assert( tfe_packetpage ); + + tfe_arch_pre_reset(); + + /* initialize visible IO register and PacketPage registers */ + memset( tfe, 0, TFE_COUNT_IO_REGISTER ); + memset( tfe_packetpage, 0, MAX_PACKETPAGE_ARRAY ); + + /* according to page 19 unless stated otherwise */ + SET_PP_32(TFE_PP_ADDR_PRODUCTID, 0x0900630E ); /* p.41: 0E630009 for Rev. D; reversed order! */ + SET_PP_16(TFE_PP_ADDR_IOBASE, 0x0300); + SET_PP_16(TFE_PP_ADDR_INTNO, 0x0004); /* xxxx xxxx xxxx x100b */ + SET_PP_16(TFE_PP_ADDR_DMA_CHAN, 0x0003); /* xxxx xxxx xxxx xx11b */ + +#if 0 /* not needed since all memory is initialized with 0 */ + SET_PP_16(TFE_PP_ADDR_DMA_SOF, 0x0000); + SET_PP_16(TFE_PP_ADDR_DMA_FC, 0x0000); /* x000h */ + SET_PP_16(TFE_PP_ADDR_RXDMA_BC, 0x0000); + SET_PP_32(TFE_PP_ADDR_MEMBASE, 0x0000); /* xxx0 0000h */ + SET_PP_32(TFE_PP_ADDR_BPROM_BASE, 0x00000000); /* xxx0 0000h */ + SET_PP_32(TFE_PP_ADDR_BPROM_MASK, 0x00000000); /* xxx0 0000h */ + + SET_PP_16(TFE_PP_ADDR_SE_ISQ, 0x0000); /* p. 51 */ +#endif + + /* according to descriptions of the registers, see definitions of + TFE_PP_ADDR_CC_... and TFE_PP_ADDR_SE_... above! */ + + SET_PP_16(TFE_PP_ADDR_CC_RXCFG, 0x0003); + SET_PP_16(TFE_PP_ADDR_CC_RXCTL, 0x0005); + SET_PP_16(TFE_PP_ADDR_CC_TXCFG, 0x0007); + SET_PP_16(TFE_PP_ADDR_CC_TXCMD, 0x0009); + SET_PP_16(TFE_PP_ADDR_CC_BUFCFG, 0x000B); + SET_PP_16(TFE_PP_ADDR_CC_LINECTL, 0x0013); + SET_PP_16(TFE_PP_ADDR_CC_SELFCTL, 0x0015); + SET_PP_16(TFE_PP_ADDR_CC_BUSCTL, 0x0017); + SET_PP_16(TFE_PP_ADDR_CC_TESTCTL, 0x0019); + + SET_PP_16(TFE_PP_ADDR_SE_ISQ, 0x0000); + SET_PP_16(TFE_PP_ADDR_SE_RXEVENT, 0x0004); + SET_PP_16(TFE_PP_ADDR_SE_TXEVENT, 0x0008); + SET_PP_16(TFE_PP_ADDR_SE_BUFEVENT, 0x000C); + SET_PP_16(TFE_PP_ADDR_SE_RXMISS, 0x0010); + SET_PP_16(TFE_PP_ADDR_SE_TXCOL, 0x0012); + SET_PP_16(TFE_PP_ADDR_SE_LINEST, 0x0014); + SET_PP_16(TFE_PP_ADDR_SE_SELFST, 0x0016); + SET_PP_16(TFE_PP_ADDR_SE_BUSST, 0x0018); + SET_PP_16(TFE_PP_ADDR_SE_TDR, 0x001C); + + SET_PP_16(TFE_PP_ADDR_TXCMD, 0x0009); + + /* 4.4.19 Self Status Register, p. 65 + Important: set INITD (Bit 7) to signal device is ready */ + SET_PP_16(TFE_PP_ADDR_SE_SELFST, 0x0896); + + tfe_recv_control = GET_PP_16(TFE_PP_ADDR_CC_RXCTL); + + /* spec: mac address is undefined after reset. + real HW: keeps the last set address. */ + for(i=0;i<6;i++) + SET_PP_8(TFE_PP_ADDR_MAC_ADDR+i,tfe_ia_mac[i]); + + /* reset state */ + tfe_set_transmitter(0); + tfe_set_receiver(0); + + tfe_arch_post_reset(); + + printf("CS8900a rev.D reset\n"); + } +} + +#ifdef DOS_TFE +static void set_standard_tfe_interface(void) +{ + char *dev, errbuf[PCAP_ERRBUF_SIZE]; + dev = pcap_lookupdev(errbuf); + util_string_set(&tfe_interface, dev); +} +#endif + +static +int tfe_activate_i(void) +{ + assert( tfe == NULL ); + assert( tfe_packetpage == NULL ); + +#ifdef TFE_DEBUG + log_message( tfe_log, "tfe_activate_i()." ); +#endif + + /* allocate memory for visible IO register */ + tfe = lib_malloc( TFE_COUNT_IO_REGISTER ); + if (tfe==NULL) + { +#ifdef TFE_DEBUG_INIT + log_message(tfe_log, "tfe_activate_i: Allocating tfe failed."); +#endif + tfe_enabled = 0; + return 0; + } + + /* allocate memory for PacketPage register */ + tfe_packetpage = lib_malloc( MAX_PACKETPAGE_ARRAY ); + if (tfe_packetpage==NULL) + { +#ifdef TFE_DEBUG_INIT + log_message(tfe_log, "tfe_activate: Allocating tfe_packetpage failed."); +#endif + lib_free(tfe); + tfe=NULL; + tfe_enabled = 0; + return 0; + } + +#ifdef TFE_DEBUG_INIT + log_message(tfe_log, "tfe_activate: Allocated memory successfully."); + log_message(tfe_log, "\ttfe at $%08X, tfe_packetpage at $%08X", tfe, tfe_packetpage ); +#endif + +#ifdef DOS_TFE + set_standard_tfe_interface(); +#endif + + if (!tfe_arch_activate(tfe_interface)) { + lib_free(tfe_packetpage); + lib_free(tfe); + tfe=NULL; + tfe_packetpage=NULL; + tfe_enabled = 0; + tfe_cannot_use = 1; + return 0; + } + + /* virtually reset the LAN chip */ + tfe_reset(); + + return 0; +} + +static +int tfe_deactivate_i(void) +{ +#ifdef TFE_DEBUG + log_message( tfe_log, "tfe_deactivate_i()." ); +#endif + + assert(tfe && tfe_packetpage); + + tfe_arch_deactivate(); + + lib_free(tfe); + tfe = NULL; + lib_free(tfe_packetpage); + tfe_packetpage = NULL; + return 0; +} + +static +int tfe_activate(void) { +#ifdef TFE_DEBUG + log_message( tfe_log, "tfe_activate()." ); +#endif + + if (init_tfe_flag) { + return tfe_activate_i(); + } + else { + should_activate = 1; + } + return 0; +} + +static +int tfe_deactivate(void) { +#ifdef TFE_DEBUG + log_message( tfe_log, "tfe_deactivate()." ); +#endif + + if (should_activate) + should_activate = 0; + else { + if (tfe_log != LOG_ERR) + return tfe_deactivate_i(); + } + + return 0; +} + +void tfe_init(void) +{ + tfe_enabled = 1; + init_tfe_flag = 1; + should_activate = 1; + if (!tfe_arch_init()) { + tfe_enabled = 0; + tfe_cannot_use = 1; + } + + if (should_activate) { + should_activate = 0; + if (tfe_activate() < 0) { + tfe_enabled = 0; + tfe_cannot_use = 1; + } + } +} + +void tfe_shutdown(void) +{ + assert( (tfe && tfe_packetpage) || (!tfe && !tfe_packetpage)); + + if (tfe) + tfe_deactivate(); + + if (tfe_interface != NULL) + lib_free(tfe_interface); +} + + +/* ------------------------------------------------------------------------- */ +/* reading and writing TFE register functions */ + +/* +These registers are currently fully or partially supported: + +TFE_PP_ADDR_CC_RXCFG 0x0102 * # RW - 4.4.6., p. 52 - 0003 * +TFE_PP_ADDR_CC_RXCTL 0x0104 * # RW - 4.4.8., p. 54 - 0005 * +TFE_PP_ADDR_CC_LINECTL 0x0112 * # RW - 4.4.16., p. 62 - 0013 * +TFE_PP_ADDR_SE_RXEVENT 0x0124 * # R- - 4.4.7., p. 53 - 0004 * +TFE_PP_ADDR_SE_BUSST 0x0138 * # R- - 4.4.21., p. 67 - 0018 * +TFE_PP_ADDR_TXCMD 0x0144 * # -W - 4.5., p. 70 - 5.7., p. 98 * +TFE_PP_ADDR_TXLENGTH 0x0146 * # -W - 4.5., p. 70 - 5.7., p. 98 * +TFE_PP_ADDR_MAC_ADDR 0x0158 * # RW - 4.6., p. 71 - 5.3., p. 86 * + 0x015a + 0x015c +*/ + + +#ifdef TFE_DEBUG_FRAMES + #define return( _x_ ) \ + { \ + int retval = _x_; \ + \ + log_message(tfe_log, "%s correct_mac=%u, broadcast=%u, multicast=%u, hashed=%u, hash_index=%u", (retval? "+++ ACCEPTED":"--- rejected"), *pcorrect_mac, *pbroadcast, *pmulticast, *phashed, *phash_index); \ + \ + return retval; \ + } + +#endif +/* + This is a helper for tfe_receive() to determine if the received frame should be accepted + according to the settings. + + This function is even allowed to be called in tfearch.c from tfe_arch_receive() if + necessary, which is the reason why its prototype is included here in tfearch.h. +*/ +int tfe_should_accept(unsigned char *buffer, int length, int *phashed, int *phash_index, + int *pcorrect_mac, int *pbroadcast, int *pmulticast) +{ + int hashreg; /* Hash Register (for hash computation) */ + + assert(length>=6); /* we need at least 6 octets since the DA has this length */ + + /* first of all, delete any status */ + *phashed = 0; + *phash_index = 0; + *pcorrect_mac = 0; + *pbroadcast = 0; + *pmulticast = 0; + +#ifdef TFE_DEBUG_FRAMES + log_message(tfe_log, "tfe_should_accept called with %02X:%02X:%02X:%02X:%02X:%02X, length=%4u and buffer %s", + tfe_ia_mac[0], tfe_ia_mac[1], tfe_ia_mac[2], + tfe_ia_mac[3], tfe_ia_mac[4], tfe_ia_mac[5], + length, + debug_outbuffer(length, buffer) + ); +#endif + + + if ( buffer[0]==tfe_ia_mac[0] + && buffer[1]==tfe_ia_mac[1] + && buffer[2]==tfe_ia_mac[2] + && buffer[3]==tfe_ia_mac[3] + && buffer[4]==tfe_ia_mac[4] + && buffer[5]==tfe_ia_mac[5] + ) { + /* this is our individual address (IA) */ + + *pcorrect_mac = 1; + + /* if we don't want "correct MAC", we might have the chance + * that this address fits the hash index + */ + if (tfe_recv_mac || tfe_recv_promiscuous) + return(1); + } + + if ( buffer[0]==0xFF + && buffer[1]==0xFF + && buffer[2]==0xFF + && buffer[3]==0xFF + && buffer[4]==0xFF + && buffer[5]==0xFF + ) { + /* this is a broadcast address */ + *pbroadcast = 1; + + /* broadcasts cannot be accepted by the hash filter */ + return((tfe_recv_broadcast || tfe_recv_promiscuous) ? 1 : 0); + } + + /* now check if DA passes the hash filter */ + hashreg = (~crc32_buf((char *)buffer,6) >> 26) & 0x3F; + + *phashed = (tfe_hash_mask[(hashreg>=32)?1:0] & (1 << (hashreg&0x1F))) ? 1 : 0; + if (*phashed) { + *phash_index = hashreg; + + if (buffer[0] & 0x80) { + /* we have a multicast address */ + *pmulticast = 1; + + /* if the multicast address fits into the hash filter, + * the hashed bit has to be clear + */ + *phashed = 0; + + return((tfe_recv_multicast || tfe_recv_promiscuous) ? 1 : 0); + } + return((tfe_recv_hashfilter || tfe_recv_promiscuous) ? 1 : 0); + } + + return(tfe_recv_promiscuous ? 1 : 0); +} + +#ifdef TFE_DEBUG_FRAMES + #undef return +#endif + +static +word16 tfe_receive(void) +{ + word16 ret_val = 0x0004; + + byte buffer[MAX_RXLENGTH]; + + int len; + int hashed; + int hash_index; + int rx_ok; + int correct_mac; + int broadcast; + int multicast; + int crc_error; + + int newframe; + + int ready; + + do { + len = MAX_RXLENGTH; + + ready = 1 ; /* assume we will find a good frame */ + + newframe = tfe_arch_receive( + buffer, /* where to store a frame */ + &len, /* length of received frame */ + &hashed, /* set if the dest. address is accepted by the hash filter */ + &hash_index, /* hash table index if hashed == TRUE */ + &rx_ok, /* set if good CRC and valid length */ + &correct_mac, /* set if dest. address is exactly our IA */ + &broadcast, /* set if dest. address is a broadcast address */ + &crc_error /* set if received frame had a CRC error */ + ); + + assert((len&1) == 0); /* length has to be even! */ + + if (newframe) { + if (hashed || correct_mac || broadcast) { + /* we already know the type of frame: Trust it! */ +#ifdef TFE_DEBUG_FRAMES + log_message( tfe_log, "+++ tfe_receive(): *** hashed=%u, correct_mac=%u, " + "broadcast=%u", hashed, correct_mac, broadcast); +#endif + } + else { + /* determine ourself the type of frame */ + if (!tfe_should_accept(buffer, + len, &hashed, &hash_index, &correct_mac, &broadcast, &multicast)) { + + /* if we should not accept this frame, just do nothing + * now, look for another one */ + ready = 0; /* try another frame */ + continue; + } + } + + + /* we did receive a frame, return that status */ + ret_val |= rx_ok ? 0x0100 : 0; + ret_val |= multicast ? 0x0200 : 0; + + if (!multicast) { + ret_val |= hashed ? 0x0040 : 0; + } + + if (hashed && rx_ok) { + /* we have the 2nd, special format with hash index: */ + assert(hash_index < 64); + ret_val |= hash_index << 9; + } + else { + /* we have the regular format */ + ret_val |= correct_mac ? 0x0400 : 0; + ret_val |= broadcast ? 0x0800 : 0; + ret_val |= crc_error ? 0x1000 : 0; + ret_val |= (lenMAX_RXLENGTH) ? 0x4000 : 0; + } + + /* discard any octets that are beyond the MAX_RXLEN */ + if (len>MAX_RXLENGTH) { + len = MAX_RXLENGTH; + } + + if (rx_ok) { + int i; + + /* set relevant parts of the PP area to correct values */ + SET_PP_16(TFE_PP_ADDR_RXLENGTH, len); + + for (i=0;i=6) && (!odd_address)) + rx_buffer += 2; + + addr += rx_buffer; + value = GET_PP_8(addr); + rx_count++; + } + +#ifdef TFE_DEBUG_RXTX_DATA + log_message(tfe_log,"RX: %04x/%04x: %02x (buffer=%04x,odd=%d)", + rx_count,rx_length+4,value,addr,odd_address); +#endif + + /* check frame end */ + if(rx_count>=rx_length+4) { + /* reset receiver state to idle */ + rx_state = TFE_RX_IDLE; +#ifdef TFE_DEBUG_RXTX_STATE + log_message(tfe_log,"RX: read frame (length=%04x)",rx_length); +#endif + } + return value; + } +} + +/* ------------------------------------------------------------------------- */ +/* handle side-effects of read and write operations */ + +/* + This is called *after* the relevant octets are written +*/ +static +void tfe_sideeffects_write_pp(word16 ppaddress, int odd_address) +{ + const char *on_off[2] = { "on","off" }; +#define on_off_str(x) ((x) ? on_off[0] : on_off[1]) + word16 content = GET_PP_16( ppaddress ); + + assert((ppaddress & 1) == 0); + + switch (ppaddress) + { + case TFE_PP_ADDR_CC_RXCFG: + /* Skip_1 Flag: remove current (partial) tx frame and restore state */ + if (content & 0x40) { + +/* tfe_arch_receive_remove_committed_frame(); */ + + /* restore tx state */ + if(tx_state!=TFE_TX_IDLE) { + tx_state = TFE_TX_IDLE; +#ifdef TFE_DEBUG_RXTX_STATE + log_message(tfe_log,"TX: skipping current frame"); +#endif + } + + /* reset transmitter */ + tfe_set_transmitter(tx_enabled); + + /* this is an "act once" bit, thus restore it to zero. */ + content &= ~0x40; + SET_PP_16( ppaddress, content ); + } + break; + + case TFE_PP_ADDR_CC_RXCTL: + if(tfe_recv_control!=content) { + tfe_recv_broadcast = content & 0x0800; /* broadcast */ + tfe_recv_mac = content & 0x0400; /* individual address (IA) */ + tfe_recv_multicast = content & 0x0200; /* multicast if address passes the hash filter */ + tfe_recv_correct = content & 0x0100; /* accept correct frames */ + tfe_recv_promiscuous = content & 0x0080; /* promiscuous mode */ + tfe_recv_hashfilter = content & 0x0040; /* accept if IA passes the hash filter */ + tfe_recv_control = content; + + /*log_message(tfe_log,"setup receiver: broadcast=%s mac=%s multicast=%s correct=%s promiscuous=%s hashfilter=%s", + on_off_str(tfe_recv_broadcast), + on_off_str(tfe_recv_mac), + on_off_str(tfe_recv_multicast), + on_off_str(tfe_recv_correct), + on_off_str(tfe_recv_promiscuous), + on_off_str(tfe_recv_hashfilter));*/ + + tfe_arch_recv_ctl( tfe_recv_broadcast, + tfe_recv_mac, + tfe_recv_multicast, + tfe_recv_correct, + tfe_recv_promiscuous, + tfe_recv_hashfilter + ); + } + break; + + case TFE_PP_ADDR_CC_LINECTL: + { + int enable_tx = (content & 0x0080) == 0x0080; + int enable_rx = (content & 0x0040) == 0x0040; + + if((enable_tx!=tx_enabled)||(enable_rx!=rx_enabled)) { + tfe_arch_line_ctl(enable_tx,enable_rx); + tfe_set_transmitter(enable_tx); + tfe_set_receiver(enable_rx); + + /*log_message(tfe_log,"line control: transmitter=%s receiver=%s", + on_off_str(enable_tx), + on_off_str(enable_rx));*/ + } + } + break; + + case TFE_PP_ADDR_CC_SELFCTL: + { + /* reset chip? */ + if((content & 0x40)==0x40) { + tfe_reset(); + } + } + break; + + case TFE_PP_ADDR_TXCMD: + { + if(odd_address) { + word16 txcommand = GET_PP_16(TFE_PP_ADDR_TXCMD); + + /* already transmitting? */ + if(tx_state == TFE_TX_READ_BUSST) { +#ifdef TFE_DEBUG_WARN_RXTX + log_message(tfe_log, "WARNING! Early abort of transmitted frame"); +#endif + } + + /* The transmit status command gets the last transmit command */ + SET_PP_16(TFE_PP_ADDR_CC_TXCMD, txcommand); + + /* set transmit state */ + tx_state = TFE_TX_GOT_CMD; + tfe_set_tx_status(0,0); + +#ifdef TFE_DEBUG_RXTX_STATE + log_message(tfe_log, "TX: COMMAND accepted (%04x)",txcommand); +#endif + } + } + break; + + case TFE_PP_ADDR_TXLENGTH: + { + if(odd_address && (tx_state == TFE_TX_GOT_CMD)) { + word16 txlength = GET_PP_16(TFE_PP_ADDR_TXLENGTH); + word16 txcommand = GET_PP_16(TFE_PP_ADDR_CC_TXCMD); + + if(txlength<4) { + /* frame to short */ +#ifdef TFE_DEBUG_RXTX_STATE + log_message(tfe_log, "TX: LENGTH rejected - too short! (%04x)",txlength); +#endif + /* mask space available but do not commit */ + tx_state = TFE_TX_IDLE; + tfe_set_tx_status(1,0); + } + else if ( (txlength>MAX_TXLENGTH) + || ((txlength>MAX_TXLENGTH-4) && (!(txcommand&0x1000))) + ) { + tx_state = TFE_TX_IDLE; +#ifdef TFE_DEBUG_RXTX_STATE + log_message(tfe_log, "TX: LENGTH rejected - too long! (%04x)",txlength); +#endif + /* txlength too big, mark an error */ + tfe_set_tx_status(0,1); + } + else { + /* make sure we put the octets to transmit at the right place */ + tx_buffer = TFE_PP_ADDR_TX_FRAMELOC; + tx_count = 0; + tx_length = txlength; + tx_state = TFE_TX_GOT_LEN; + +#ifdef TFE_DEBUG_RXTX_STATE + log_message(tfe_log, "TX: LENGTH accepted (%04x)",txlength); +#endif + /* all right, signal that we're ready for the next frame */ + tfe_set_tx_status(1,0); + } + } + } + break; + + case TFE_PP_ADDR_LOG_ADDR_FILTER: + case TFE_PP_ADDR_LOG_ADDR_FILTER+2: + case TFE_PP_ADDR_LOG_ADDR_FILTER+4: + case TFE_PP_ADDR_LOG_ADDR_FILTER+6: + { + unsigned int pos = 8 * (ppaddress - TFE_PP_ADDR_LOG_ADDR_FILTER + odd_address); + DWORD *p = (pos < 32) ? &tfe_hash_mask[0] : &tfe_hash_mask[1]; + + *p &= ~(0xFF << pos); /* clear out relevant bits */ + *p |= GET_PP_8(ppaddress+odd_address) << pos; + + tfe_arch_set_hashfilter(tfe_hash_mask); + +#if 0 + if(odd_address && (ppaddress == TFE_PP_ADDR_LOG_ADDR_FILTER+6)) + log_message(tfe_log,"set hash filter: %02x:%02x:%02x:%02x:%02x:%02x", + tfe_hash_mask[0], + tfe_hash_mask[1], + tfe_hash_mask[2], + tfe_hash_mask[3], + tfe_hash_mask[4], + tfe_hash_mask[5]); +#endif + } + break; + + case TFE_PP_ADDR_MAC_ADDR: + case TFE_PP_ADDR_MAC_ADDR+2: + case TFE_PP_ADDR_MAC_ADDR+4: + /* the MAC address has been changed */ + tfe_ia_mac[ppaddress-TFE_PP_ADDR_MAC_ADDR+odd_address] = + GET_PP_8(ppaddress+odd_address); + tfe_arch_set_mac(tfe_ia_mac); + + if(odd_address && (ppaddress == TFE_PP_ADDR_MAC_ADDR+4)) + /*log_message(tfe_log,"set MAC address: %02x:%02x:%02x:%02x:%02x:%02x", + tfe_ia_mac[0],tfe_ia_mac[1],tfe_ia_mac[2], + tfe_ia_mac[3],tfe_ia_mac[4],tfe_ia_mac[5]);*/ + break; + } +#undef on_off_str +} + +/* + This is called *before* the relevant octets are read +*/ +static +void tfe_sideeffects_read_pp(word16 ppaddress,int odd_address) +{ + switch (ppaddress) + { + case TFE_PP_ADDR_SE_RXEVENT: + /* reading this before all octets of the frame are read + performs an "implied skip" */ + { + int access_mask = (odd_address) ? 1 : 2; + + /* update the status register only if the full word of the last + status was read! unfortunately different access patterns are + possible: either the status is read LH, LH, LH... + or HL, HL, HL, or even L, L, L or H, H, H */ + if((access_mask & rxevent_read_mask)!=0) { + /* receiver is not enabled */ + if(!rx_enabled) { +#ifdef TFE_DEBUG_WARN_RXTX + log_message(tfe_log,"WARNING! Can't receive any frame (Receiver is not enabled)!"); +#endif + } else { + /* perform frame reception */ + word16 ret_val = tfe_receive(); + + /* RXSTATUS and RXEVENT are the same, except that RXSTATUS buffers + the old value while RXEVENT sets a new value whenever it is called + */ + SET_PP_16(TFE_PP_ADDR_RXSTATUS, ret_val); + SET_PP_16(TFE_PP_ADDR_SE_RXEVENT, ret_val); + } + + /* reset read mask of (possible) other access */ + rxevent_read_mask = access_mask; + } else { + /* add access bit to mask */ + rxevent_read_mask |= access_mask; + } + } + + break; + + case TFE_PP_ADDR_SE_BUSST: + if(odd_address) { + /* read busst before transmit condition is fullfilled */ + if(tx_state == TFE_TX_GOT_LEN) { + word16 bus_status = GET_PP_16(TFE_PP_ADDR_SE_BUSST); + /* check Rdy4TXNow flag */ + if((bus_status & 0x100) == 0x100) { + tx_state = TFE_TX_READ_BUSST; +#ifdef TFE_DEBUG_RXTX_STATE + log_message(tfe_log, "TX: Ready4TXNow set! (%04x)", + bus_status); +#endif + } else { +#ifdef TFE_DEBUG_RXTX_STATE + log_message(tfe_log, "TX: waiting for Ready4TXNow! (%04x)", + bus_status); +#endif + } + } + } + break; + } +} + +/* ------------------------------------------------------------------------- */ +/* read/write from packet page register */ + +/* read a register from packet page */ +static word16 tfe_read_register(word16 ppaddress) +{ + word16 value = GET_PP_16(ppaddress); + + /* --- check the register address --- */ + if(ppaddress<0x100) { + /* reserved range reads 0x0300 on real HW */ + if((ppaddress>=0x0004)&&(ppaddress<0x0020)) { + return 0x0300; + } + } + + /* --- read control register range --- */ + else if(ppaddress<0x120) { + word16 regNum = ppaddress - 0x100; + regNum &= ~1; + regNum ++; +#ifdef TFE_DEBUG_REGISTERS + log_message(tfe_log, + "Read Control Register %04x: %04x (reg=%02x)", + ppaddress,value,regNum); +#endif + + /* reserved register? */ + if((regNum==0x01)||(regNum==0x11)||(regNum>0x19)) { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Read reserved Control Register %04x (reg=%02x)", + ppaddress,regNum); +#endif + /* real HW returns 0x0300 in reserved register range */ + return 0x0300; + } + + /* make sure interal address is always valid */ + assert((value&0x3f) == regNum); + } + + /* --- read status register range --- */ + else if(ppaddress<0x140) { + word16 regNum = ppaddress - 0x120; + regNum &= ~1; +#ifdef TFE_DEBUG_REGISTERS +#ifdef TFE_DEBUG_IGNORE_RXEVENT + if(regNum!=4) // do not show RXEVENT +#endif + log_message(tfe_log, + "Read Status Register %04x: %04x (reg=%02x)", + ppaddress,value,regNum); +#endif + + /* reserved register? */ + if((regNum==0x02)||(regNum==0x06)||(regNum==0x0a)|| + (regNum==0x0e)||(regNum==0x1a)||(regNum==0x1e)) { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Read reserved Status Register %04x (reg=%02x)", + ppaddress,regNum); +#endif + /* real HW returns 0x0300 in reserved register range */ + return 0x0300; + } + + /* make sure interal address is always valid */ + assert((value&0x3f) == regNum); + } + + /* --- read transmit register range --- */ + else if(ppaddress<0x150) { + if(ppaddress==0x144) { + /* make sure interal address is always valid */ + assert((value&0x3f) == 0x09); +#ifdef TFE_DEBUG_REGISTERS + log_message(tfe_log, + "Read TX Cmd Register %04x: %04x", + ppaddress,value); +#endif + } + else if(ppaddress==0x146) { +#ifdef TFE_DEBUG_REGISTERS + log_message(tfe_log, + "Read TX Len Register %04x: %04x", + ppaddress,value); +#endif + } + /* reserved range */ + else { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Read reserved Initiate Transmit Register %04x", + ppaddress); +#endif + /* real HW returns 0x0300 in reserved register range */ + return 0x0300; + } + } + + /* --- read address filter register range --- */ + else if(ppaddress<0x160) { + /* reserved range */ + if(ppaddress>=0x15e) { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Read reserved Address Filter Register %04x", + ppaddress); +#endif + /* real HW returns 0x0300 in reserved register range */ + return 0x0300; + } + } + + /* --- reserved range below 0x400 --- + returns 0x300 on real HW + */ + else if(ppaddress<0x400) { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Read reserved Register %04x", + ppaddress); +#endif + return 0x0300; + } + + /* --- range from 0x400 .. 0x9ff --- RX Frame */ + else if(ppaddress<0xa00) { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Read from RX Buffer Range %04x", + ppaddress); +#endif + return 0x0000; + } + + /* --- range from 0xa00 .. 0xfff --- TX Frame */ + else { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Read from TX Buffer Range %04x", + ppaddress); +#endif + return 0x0000; + } + + /* actually read from pp memory */ + return value; +} + +void tfe_write_register(word16 ppaddress,word16 value) +{ + /* --- write bus interface register range --- */ + if(ppaddress<0x100) { + int ignore = 0; + if(ppaddress<0x20) { + ignore = 1; + } else if((ppaddress>=0x26)&&(ppaddress<0x2c)) { + ignore = 1; + } else if(ppaddress==0x38) { + ignore = 1; + } else if(ppaddress>=0x44) { + ignore = 1; + } + if(ignore) { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Ignoring write to read only/reserved Bus Interface Register %04x", + ppaddress); +#endif + return; + } + } + + /* --- write to control register range --- */ + else if(ppaddress<0x120) { + word16 regNum = ppaddress - 0x100; + regNum &= ~1; + regNum += 1; + /* validate internal address */ + if((value&0x3f) != regNum) { + /* fix internal address */ + value &= ~0x3f; + value |= regNum; + } +#ifdef TFE_DEBUG_REGISTERS + log_message(tfe_log, + "Write Control Register %04x: %04x (reg=%02x)", + ppaddress,value,regNum); +#endif + + /* invalid register? -> ignore! */ + if((regNum==0x01)||(regNum==0x11)||(regNum>0x19)) { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Ignoring write to reserved Control Register %04x (reg=%02x)", + ppaddress,regNum); + #endif + return; + } + } + + /* --- write to status register range --- */ + else if(ppaddress<0x140) { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Ignoring write to read-only Status Register %04x", + ppaddress); +#endif + return; + } + + /* --- write to initiate transmit register range --- */ + else if(ppaddress<0x150) { + /* check tx_cmd register */ + if(ppaddress==0x144) { + /* validate internal address */ + if((value&0x3f) != 0x09) { + /* fix internal address */ + value &= ~0x3f; + value |= 0x09; + } + /* mask out reserved bits */ + value &= 0x33ff; +#ifdef TFE_DEBUG_REGISTERS + log_message(tfe_log, + "Write TX Cmd Register %04x: %04x", + ppaddress,value); +#endif + } + /* check tx_length register */ + else if(ppaddress==0x146) { + /* HW always masks 0x0fff */ + value &= 0x0fff; +#ifdef TFE_DEBUG_REGISTERS + log_message(tfe_log, + "Write TX Len Register %04x: %04x", + ppaddress,value); +#endif + } + /* reserved range */ + else if((ppaddress<0x144)||(ppaddress>0x147)) { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Ignoring write to reserved Initiate Transmit Register %04x", + ppaddress); +#endif + return; + } + } + + /* --- write to address filter register range --- */ + else if(ppaddress<0x160) { + /* reserved range */ + if(ppaddress>=0x15e) { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Ingoring write to reserved Address Filter Register %04x", + ppaddress); +#endif + return; + } + } + + /* --- ignore write outside --- */ + else if(ppaddress<0x400) { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Ingoring write to reserved Register %04x", + ppaddress); +#endif + return; + } + else if(ppaddress<0xa00){ +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNING! Ignoring write to RX Buffer Range %04x", + ppaddress); +#endif + return; + } + else { +#ifdef TFE_DEBUG_WARN_REG + log_message(tfe_log, + "WARNIGN! Ignoring write to TX Buffer Range %04x", + ppaddress); +#endif + return; + } + + /* actually set value */ + SET_PP_16(ppaddress, value); +} + +#define PP_PTR_AUTO_INCR_FLAG 0x8000 /* auto increment flag in package pointer */ +#define PP_PTR_FLAG_MASK 0xf000 /* is always : x y 1 1 (with x=auto incr) */ +#define PP_PTR_ADDR_MASK 0x0fff /* address portion of packet page pointer */ + +static void tfe_auto_incr_pp_ptr(void) +{ + /* perform auto increment of packet page pointer */ + if((tfe_packetpage_ptr & PP_PTR_AUTO_INCR_FLAG)==PP_PTR_AUTO_INCR_FLAG) { + /* pointer is always increment by one on real HW */ + word16 ptr = tfe_packetpage_ptr & PP_PTR_ADDR_MASK; + word16 flags = tfe_packetpage_ptr & PP_PTR_FLAG_MASK; + ptr++; + tfe_packetpage_ptr = ptr | flags; + } +} + +/* ------------------------------------------------------------------------- */ +/* read/write TFE registers from VICE */ + +#define LO_BYTE(x) (byte)((x) & 0xff) +#define HI_BYTE(x) (byte)(((x) >> 8) & 0xff) +#define LOHI_WORD(x,y) ( (word16)(x) | ( ((word16)(y)) <<8 ) ) + +/* ----- read byte from I/O range in VICE ----- */ +byte tfe_read(word16 io_address) +{ + byte retval,lo,hi; + word16 word_value; + word16 reg_base; + + assert( tfe ); + assert( tfe_packetpage ); + assert( io_address < 0x10); + + /*if (tfe_as_rr_net) { + /* rr status register is handled by rr cartidge + if (io_address < 0x02) { + return 0; + } + io_address ^= 0x08; + }*/ + /* register base addr */ + reg_base = io_address & ~1; + + /* RX register is special as it reads from RX buffer directly */ + if((reg_base==TFE_ADDR_RXTXDATA)||(reg_base==TFE_ADDR_RXTXDATA2)) { + //io_source=IO_SOURCE_TFE_RR_NET; + return tfe_read_rx_buffer(io_address & 0x01); + } + + /* read packet page pointer */ + if(reg_base==TFE_ADDR_PP_PTR) { + word_value = tfe_packetpage_ptr; + } + /* read a register from packet page */ + else { + word16 ppaddress; + + /* determine read addr in packet page */ + switch (reg_base) { + /* PP_DATA2 behaves like PP_DATA on real HW + both show the contents at the page pointer */ + case TFE_ADDR_PP_DATA: + case TFE_ADDR_PP_DATA2: + /* mask and align address of packet pointer */ + ppaddress = tfe_packetpage_ptr & PP_PTR_ADDR_MASK; + ppaddress &= ~1; + /* if flags match then auto incr pointer */ + tfe_auto_incr_pp_ptr(); + break; + case TFE_ADDR_INTSTQUEUE: + ppaddress = TFE_PP_ADDR_SE_ISQ; + break; + case TFE_ADDR_TXCMD: + ppaddress = TFE_PP_ADDR_TXCMD; + break; + case TFE_ADDR_TXLENGTH: + ppaddress = TFE_PP_ADDR_TXLENGTH; + break; + default: + /* invalid! */ + assert(0); + break; + } + + /* do side effects before access */ + tfe_sideeffects_read_pp(ppaddress,io_address&1); + + /* read register value */ + word_value = tfe_read_register(ppaddress); + +#ifdef TFE_DEBUG_LOAD + log_message(tfe_log, "reading PP Ptr: $%04X => $%04X.",ppaddress,word_value); +#endif + } + + /* extract return value from word_value */ + lo = LO_BYTE(word_value); + hi = HI_BYTE(word_value); + if((io_address & 1) == 0) { + /* low byte on even address */ + retval = lo; + } else { + /* high byte on odd address */ + retval = hi; + } + +#ifdef TFE_DEBUG_LOAD + log_message(tfe_log, "read [$%02X] => $%02X.", io_address, retval); +#endif + + /* update _word_ value in register bank */ + tfe[reg_base] = lo; + tfe[reg_base+1] = hi; + + //io_source=IO_SOURCE_TFE_RR_NET; + return retval; +} + +/* ----- write byte to I/O range of VICE ----- */ +void tfe_store(word16 io_address, byte var) +{ + word16 reg_base; + word16 word_value; + assert( tfe ); + assert( tfe_packetpage ); + assert( io_address < 0x10); + + /*if (tfe_as_rr_net) { + /* rr control register is handled by rr cartidge + if (io_address < 0x02) { + return; + } + io_address ^= 0x08; + }*/ +#ifdef TFE_DEBUG_STORE + log_message(tfe_log, "store [$%02X] <= $%02X.", io_address, (int)byte); +#endif + + /* register base addr */ + reg_base = io_address & ~1; + + /* TX Register is special as it writes to TX buffer directly */ + if((reg_base==TFE_ADDR_RXTXDATA)||(reg_base==TFE_ADDR_RXTXDATA2)) { + tfe_write_tx_buffer(var,io_address & 1); + return; + } + + /* combine stored value with new written byte */ + if((io_address & 1) == 0) { + /* overwrite low byte */ + word_value = LOHI_WORD(var,tfe[reg_base+1]); + } else { + /* overwrite high byte */ + word_value = LOHI_WORD(tfe[reg_base],var); + } + + if(reg_base==TFE_ADDR_PP_PTR) { + /* cv: we store the full package pointer in tfe_packetpage_ptr variable. + this includes the mask area (0xf000) and the addr range (0x0fff). + we ensure that the bits 0x3000 are always set (as in real HW). + odd values of the pointer are valid and supported. + only register read and write have to be mapped to word boundary. */ + word_value |= 0x3000; + tfe_packetpage_ptr = word_value; +#ifdef TFE_DEBUG_STORE + log_message(tfe_log, "set PP Ptr to $%04X.", tfe_packetpage_ptr); +#endif + } else { + /* write a register */ + + /*! \TODO: Find a reasonable default */ + word16 ppaddress = TFE_PP_ADDR_PRODUCTID; + + /* now determine address of write in packet page */ + switch(reg_base) { + case TFE_ADDR_PP_DATA: + case TFE_ADDR_PP_DATA2: + /* mask and align ppaddress from page pointer */ + ppaddress = tfe_packetpage_ptr & (MAX_PACKETPAGE_ARRAY-1); + ppaddress &= ~1; + /* auto increment pp ptr */ + tfe_auto_incr_pp_ptr(); + break; + case TFE_ADDR_TXCMD: + ppaddress = TFE_PP_ADDR_TXCMD; + break; + case TFE_ADDR_TXLENGTH: + ppaddress = TFE_PP_ADDR_TXLENGTH; + break; + case TFE_ADDR_INTSTQUEUE: + ppaddress = TFE_PP_ADDR_SE_ISQ; + break; + case TFE_ADDR_PP_PTR: + break; + default: + /* invalid */ + assert(0); + break; + } + +#ifdef TFE_DEBUG_STORE + log_message(tfe_log, "before writing to PP Ptr: $%04X <= $%04X.", + ppaddress,word_value); +#endif + + /* perform the write */ + tfe_write_register(ppaddress,word_value); + + /* handle sideeffects */ + tfe_sideeffects_write_pp(ppaddress, io_address&1); + + /* update word value if it was changed in write register or by side effect */ + word_value = GET_PP_16(ppaddress); + +#ifdef TFE_DEBUG_STORE + log_message(tfe_log, "after writing to PP Ptr: $%04X <= $%04X.", + ppaddress,word_value); +#endif + } + + /* update tfe registers */ + tfe[reg_base] = LO_BYTE(word_value); + tfe[reg_base+1] = HI_BYTE(word_value); +} + +/* ------------------------------------------------------------------------- */ +/* resources support functions */ +static int set_tfe_disabled(int val, void *param) +{ + /* dummy function since we don't want "disabled" to be stored on disk */ + return 0; +} + +/*static int set_tfe_rr_net(int val, void *param) +{ + if (!tfe_cannot_use) { + if (!val) { + /* TFE should not be used as rr net + if (tfe_as_rr_net) { + tfe_as_rr_net = 0; + + /* if adapter is already enabled then reset the LAN chip + if (tfe) { + tfe_reset(); + } + } + return 0; + } else { + if (!tfe_as_rr_net) { + tfe_as_rr_net = 1; + + /* if adapter is already enabled then reset the LAN chip + if (tfe) { + tfe_reset(); + } + } + return 0; + } + } + + return 0; +}*/ + +static int set_tfe_enabled(int val, void *param) +{ + if (!tfe_cannot_use) { + if (!val) { + /* TFE should be deactived */ + if (tfe_enabled) { + tfe_enabled = 0; + if (tfe_deactivate() < 0) { + return -1; + } + } + return 0; + } else { + if (!tfe_enabled) { + tfe_enabled = 1; + if (tfe_activate() < 0) { + return -1; + } + } + + return 0; + } + + } + return 0; +} + + +int set_tfe_interface(const char *name) +{ + if (tfe_interface != NULL && name != NULL + && strcmp(name, tfe_interface) == 0) + return 0; + + util_string_set(&tfe_interface, name); + + if (tfe_enabled) { + /* ethernet is enabled, make sure that the new name is + taken account of + */ + if (tfe_deactivate() < 0) { + return -1; + } + if (tfe_activate() < 0) { + return -1; + } + + /* virtually reset the LAN chip */ + if (tfe) { + tfe_reset(); + } + } + return 0; +} + + +/*static const resource_string_t resources_string[] = { + { "ETHERNET_INTERFACE", + ARCHDEP_ETHERNET_DEFAULT_DEVICE, RES_EVENT_NO, NULL, + &tfe_interface, set_tfe_interface, NULL }, + { NULL } +}; + +static const resource_int_t resources_int[] = { + { "ETHERNET_DISABLED", 0, RES_EVENT_NO, NULL, + &tfe_cannot_use, set_tfe_disabled, NULL }, + { "ETHERNET_ACTIVE", 0, RES_EVENT_STRICT, (resource_value_t)0, + &tfe_enabled, set_tfe_enabled, NULL }, + { "ETHERNET_AS_RR", 0, RES_EVENT_NO, NULL, + &tfe_as_rr_net, set_tfe_rr_net, NULL }, + { NULL } +};*/ + +/*int tfe_resources_init(void) +{ + if (resources_register_string(resources_string) < 0) + return -1; + + return resources_register_int(resources_int); +}*/ + +/* ------------------------------------------------------------------------- */ +/* commandline support functions */ + +/*static const cmdline_option_t cmdline_options[] = +{ + { "-tfe", SET_RESOURCE, 0, + NULL, NULL, "ETHERNET_ACTIVE", (resource_value_t)1, + USE_PARAM_STRING, USE_DESCRIPTION_ID, + IDCLS_UNUSED, IDCLS_ENABLE_TFE, + NULL, NULL }, + { "+tfe", SET_RESOURCE, 0, + NULL, NULL, "ETHERNET_ACTIVE", (resource_value_t)0, + USE_PARAM_STRING, USE_DESCRIPTION_ID, + IDCLS_UNUSED, IDCLS_DISABLE_TFE, + NULL, NULL }, + { "-tfeif", SET_RESOURCE, 1, + NULL, NULL, "ETHERNET_INTERFACE", NULL, + USE_PARAM_ID, USE_DESCRIPTION_ID, + IDCLS_P_NAME, IDCLS_TFE_INTERFACE, + NULL, NULL }, + { "-tferrnet", SET_RESOURCE, 0, + NULL, NULL, "ETHERNET_AS_RR", (resource_value_t)1, + USE_PARAM_STRING, USE_DESCRIPTION_ID, + IDCLS_UNUSED, IDCLS_ENABLE_TFE_AS_RRNET, + NULL, NULL }, + { "+tferrnet", SET_RESOURCE, 0, + NULL, NULL, "ETHERNET_AS_RR", (resource_value_t)0, + USE_PARAM_STRING, USE_DESCRIPTION_ID, + IDCLS_UNUSED, IDCLS_DISABLE_TFE_AS_RRNET, + NULL, NULL }, + { NULL } +}; + +int tfe_cmdline_options_init(void) +{ + return cmdline_register_options(cmdline_options); +}*/ + + +/* ------------------------------------------------------------------------- */ +/* snapshot support functions */ + +#if 0 + +static char snap_module_name[] = "TFE1764"; +#define SNAP_MAJOR 0 +#define SNAP_MINOR 0 + +int tfe_read_snapshot_module(struct snapshot_s *s) +{ + /* @SRT TODO: not yet implemented */ + return -1; +} + +int tfe_write_snapshot_module(struct snapshot_s *s) +{ + /* @SRT TODO: not yet implemented */ + return -1; +} + +#endif /* #if 0 */ + +/* ------------------------------------------------------------------------- */ +/* functions for selecting and querying available NICs */ + +int tfe_enumadapter_open(void) +{ + if (!tfe_arch_enumadapter_open()) { + tfe_cannot_use = 1; + return 0; + } + return 1; +} + +int tfe_enumadapter(char **ppname, char **ppdescription) +{ + return tfe_arch_enumadapter(ppname, ppdescription); +} + +int tfe_enumadapter_close(void) +{ + return tfe_arch_enumadapter_close(); +} + +//#endif /* #ifdef HAVE_TFE */ diff --git a/src/vars_kegs32 b/src/vars_kegs32 new file mode 100644 index 0000000..f264d63 --- /dev/null +++ b/src/vars_kegs32 @@ -0,0 +1,10 @@ + +TARGET = kegswin.exe +OBJECTS = $(OBJECTS1) win32snd_driver.o windriver.o kegs32.o +CCOPTS = -O2 -DKEGS_LITTLE_ENDIAN +SUFFIX = ".exe" +NAME = kegswin + +XOPTS = -Wall -fomit-frame-pointer -march=pentium +XLIBS = + diff --git a/src/win32.ico b/src/win32.ico new file mode 100644 index 0000000000000000000000000000000000000000..f9f12279d09eaeeda64a3018bb6f307f4b110b19 GIT binary patch literal 8478 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 literal 0 HcmV?d00001 diff --git a/src/win32.rc b/src/win32.rc index e0cd945..76afa5b 100644 --- a/src/win32.rc +++ b/src/win32.rc @@ -1,24 +1,3 @@ -/* - GSport - an Apple //gs Emulator - Copyright (C) 2010 by GSport contributors - - Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - #include #include "winresource.h" @@ -28,67 +7,45 @@ #define IDC_STATIC (-1) #endif -IDD_DLG_DISKCONF DIALOGEX 0, 0, 268, 182 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Disk Configuration" -FONT 8, "MS Sans Serif" -BEGIN - DEFPUSHBUTTON "OK",IDOK,150,161,50,14 - PUSHBUTTON "Cancel",IDCANCEL,203,161,50,14 - LTEXT "S5D1",IDC_STATIC,19,46,19,8 - EDITTEXT IDC_EDIT_S5D1,43,42,156,14,ES_AUTOHSCROLL, - WS_EX_ACCEPTFILES - PUSHBUTTON "Browse",IDC_BTN_S5D1,203,42,50,14 - LTEXT "S5D2",IDC_STATIC,19,62,19,8 - EDITTEXT IDC_EDIT_S5D2,43,60,155,14,ES_AUTOHSCROLL - PUSHBUTTON "Browse",IDC_BTN_S5D2,203,60,50,14 - LTEXT "S6D1",IDC_STATIC,19,80,19,8 - EDITTEXT IDC_EDIT_S6D1,43,77,156,14,ES_AUTOHSCROLL - PUSHBUTTON "Browse",IDC_BTN_S6D1,203,77,50,14 - LTEXT "S6D2",IDC_STATIC,19,98,19,8 - EDITTEXT IDC_EDIT_S6D2,43,95,156,14,ES_AUTOHSCROLL - PUSHBUTTON "Browse",IDC_BTN_S6D2,203,96,50,14 - LTEXT "S7D1",IDC_STATIC,19,118,19,8 - EDITTEXT IDC_EDIT_S7D1,43,115,155,14,ES_AUTOHSCROLL - PUSHBUTTON "Browse",IDC_BTN_S7D1,203,115,50,14 - LTEXT "S7D2",IDC_STATIC,19,137,19,8 - EDITTEXT IDC_EDIT_S7D2,43,135,155,14,ES_AUTOHSCROLL - PUSHBUTTON "Browse",IDC_BTN_S7D2,203,135,50,14 - GROUPBOX "Disk settings",IDC_STATIC,7,7,254,148 - LTEXT "Configure your disk images for each drive. Disk image formats supported\nare *.2MG,*.PO and *.DSK. ", - IDC_STATIC,19,20,234,16 -END - ///////////////////////////////////////////////////////////////////////////// // // Menu // -IDC_KEGS32 MENU DISCARDABLE +IDC_KEGS32 MENU DISCARDABLE BEGIN POPUP "&Emulator" BEGIN - MENUITEM "&Set Disk Configuration\tALT-F1", ID_FILE_DISK - MENUITEM "Send CTRL Open-Apple Reset\tCTRL-ALT-BREAK", - ID_FILE_SENDRESET + MENUITEM "&Set Disk Configuration", ID_FILE_DISK + MENUITEM "Send CTRL Reset\tCTRL-BREAK", ID_FILE_SENDRESET + MENUITEM "Reboot\tCTRL-ALT-BREAK", ID_FILE_SENDREBOOT + MENUITEM SEPARATOR MENUITEM "Toggle &Joystick", ID_FILE_JOYSTICK MENUITEM "Toggle Debug Statistics", ID_FILE_DEBUGSTAT MENUITEM SEPARATOR - MENUITEM "E&xit\tALT-F4", ID_FILE_EXIT + MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Help" BEGIN + MENUITEM "Key Commands", ID_HELP_KEY + MENUITEM SEPARATOR MENUITEM "&About", ID_HELP_ABOUT END END +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +// CREATEPROCESS_MANIFEST RT_MANIFEST "kegs.manifest" ///////////////////////////////////////////////////////////////////////////// // // Bitmap // -IDR_TOOLBAR BITMAP DISCARDABLE "wintoolbar.bmp" +IDC_KEGS32 BITMAP DISCARDABLE "wintoolbar.bmp" ///////////////////////////////////////////////////////////////////////////// // @@ -122,9 +79,102 @@ CAPTION "About" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,78,61,50,14 - LTEXT "KEGS32: GS Emulator.\nBased on KEGS by Kent Dickey\nWindows Port by Chea Chee Keong\n\nThis software is free for non-commercial use.", + LTEXT "KEGS32: GS Emulator.\nBased on KEGS by Kent Dickey\nWindows Port by Chea Chee Keong\n\nThis software is licensed under GNU GPL.", IDC_STATIC,38,7,162,45,NOT WS_GROUP ICON "KEGS32_ICON",IDC_STATIC,7,7,21,20,0 END +IDD_DLG_DISKCONF DIALOGEX 0, 0, 268, 182 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Disk Configuration" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,150,161,50,14 + PUSHBUTTON "Cancel",IDCANCEL,203,161,50,14 + LTEXT "S5D1",IDC_STATIC,19,46,19,8 + EDITTEXT IDC_EDIT_S5D1,43,42,156,14,ES_AUTOHSCROLL, + WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S5D1,203,42,50,14 + LTEXT "S5D2",IDC_STATIC,19,62,19,8 + EDITTEXT IDC_EDIT_S5D2,43,60,155,14,ES_AUTOHSCROLL, + WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S5D2,203,60,50,14 + LTEXT "S6D1",IDC_STATIC,19,80,19,8 + EDITTEXT IDC_EDIT_S6D1,43,77,156,14,ES_AUTOHSCROLL, + WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S6D1,203,77,50,14 + LTEXT "S6D2",IDC_STATIC,19,98,19,8 + EDITTEXT IDC_EDIT_S6D2,43,95,156,14,ES_AUTOHSCROLL, + WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S6D2,203,96,50,14 + LTEXT "S7D1",IDC_STATIC,19,118,19,8 + EDITTEXT IDC_EDIT_S7D1,43,115,155,14,ES_AUTOHSCROLL, + WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S7D1,203,115,50,14 + LTEXT "S7D2",IDC_STATIC,19,137,19,8 + EDITTEXT IDC_EDIT_S7D2,43,135,155,14,ES_AUTOHSCROLL, + WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S7D2,203,135,50,14 + GROUPBOX "Disk settings",IDC_STATIC,7,7,254,148 + LTEXT "Configure your disk images for each drive. Disk image formats supported\nare *.2MG,*.PO and *.DSK. ", + IDC_STATIC,19,20,234,16 +END + +IDD_SPEEDDIALOG DIALOG 0, 0, 240, 129 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Speed Control" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "1 MHz",IDC_SLOW,"Button",BS_AUTORADIOBUTTON | + WS_TABSTOP,45,41,51,13 + CONTROL "2.5 MHz",IDC_NORMAL,"Button",BS_AUTORADIOBUTTON | + WS_TABSTOP,45,55,43,13 + CONTROL "As fast as possible",IDC_FASTEST,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,45,69,74,13 + CONTROL "Custom (MHz)",IDC_CUSTOM,"Button",BS_AUTORADIOBUTTON | + WS_TABSTOP,45,83,69,13 + EDITTEXT IDC_EDITCUSTOM,117,83,79,13,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,133,107,50,15 + PUSHBUTTON "Cancel",IDCANCEL,183,107,50,15 + GROUPBOX "Speed Control",IDC_STATIC,7,7,226,96,WS_GROUP + LTEXT "Adjust the speed of your emulator by selecting the appropriate speed control", + IDC_STATIC,46,19,181,19,NOT WS_GROUP + ICON IDC_KEGS32,IDC_STATIC,14,19,21,20 +END + +IDD_KEGS32_KEY DIALOG 0, 0, 186, 172 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Help About Key Commands" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,65,151,50,14 + LTEXT "KEGS32 Key Commands",IDC_STATIC,7,7,82,10 + LTEXT "Alt/F1\t\tOpen-Apple\nF2\t\tClose-Apple\nF7\t\tToggle Fast Disk Emulation\nF8\t\tToggle Mouse Pointer\n\t\tDisplay\n", + IDC_STATIC,21,25,151,42 + LTEXT "F11\t\tToggle Fullscreen Display\nF12\t\tReset\nCtrl-Alt-Break\tReboot Emulator\nCtrl-Alt-Esc\tControl-Panel\nCtrl-Break\tReset Emulator", + IDC_STATIC,21,66,141,50 + CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME | WS_DISABLED | + WS_BORDER,7,17,172,1 + LTEXT "For more information, please consult the readme.kegs file", + IDC_STATIC,21,124,144,19 +END + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + ID_FILE_DISK "Disk Configuration" +END + +STRINGTABLE +BEGIN + ID_SPEED_1MHZ "Set Speed to 1 Mhz" + ID_SPEED_2MHZ "Set Speed to 2.5 Mhz" + ID_SPEED_FMHZ "Set Speed to as fast as possible" +END + + diff --git a/src/win32snd_driver.c b/src/win32snd_driver.c index 869bc17..9be2947 100644 --- a/src/win32snd_driver.c +++ b/src/win32snd_driver.c @@ -1,23 +1,12 @@ -/* - GSport - an Apple //gs Emulator - Copyright (C) 2010 by GSport contributors - - Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ const char rcsid_win32snd_driver_c[] = "@(#)$KmKId: win32snd_driver.c,v 1.5 2002-11-19 03:09:59-05 kadickey Exp $"; @@ -28,7 +17,7 @@ const char rcsid_win32snd_driver_c[] = "@(#)$KmKId: win32snd_driver.c,v 1.5 2002 # include # include #endif -#include +//#include extern int Verbose; @@ -69,8 +58,8 @@ win32snd_shutdown() #ifndef __CYGWIN__ void CALLBACK -handle_wav_snd(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, - DWORD dwParam2) +handle_wav_snd(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, + DWORD_PTR dwParam2) { LPWAVEHDR lpwavehdr; @@ -131,7 +120,7 @@ child_sound_init_win32() } res = waveOutOpen(&g_wave_handle, WAVE_MAPPER, &wavefmt, - (DWORD)handle_wav_snd, 0, CALLBACK_FUNCTION | WAVE_ALLOWSYNC); + (DWORD_PTR)handle_wav_snd, 0, CALLBACK_FUNCTION | WAVE_ALLOWSYNC); if(res != MMSYSERR_NOERROR) { printf("Cannot register audio\n"); diff --git a/src/windriver.c b/src/windriver.c index 5a7bc6d..75960fc 100644 --- a/src/windriver.c +++ b/src/windriver.c @@ -1,685 +1,1245 @@ -/* - GSport - an Apple //gs Emulator - Copyright (C) 2010 by GSport contributors - - Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -const char rcsid_windriver_c[] = "@(#)$KmKId: windriver.c,v 1.11 2004-11-24 16:43:46-05 kentd Exp $"; - -/* Based on code from Chea Chee Keong from KEGS32, which is available at */ -/* http://www.geocities.com/akilgard/kegs32 */ - -#define WIN32_LEAN_AND_MEAN /* Tell windows we want less header gunk */ -#define STRICT /* Tell Windows we want compile type checks */ - -#include -#include -#include -#include -#include - -#include "defc.h" -#include "protos_windriver.h" - -extern int Verbose; - -extern int g_warp_pointer; -extern int g_screen_depth; -extern int g_force_depth; -int g_screen_mdepth = 0; - -extern int g_quit_sim_now; - -int g_use_shmem = 1; -int g_has_focus = 0; -int g_auto_repeat_on = -1; - -extern Kimage g_mainwin_kimage; - -HDC g_main_dc; -HDC g_main_cdc; -int g_main_height = 0; - -int g_win_capslock_down = 0; - -extern word32 g_palette_8to1624[256]; -extern word32 g_a2palette_8to1624[256]; - -extern word32 g_full_refresh_needed; - -extern int g_border_sides_refresh_needed; -extern int g_border_special_refresh_needed; -extern int g_status_refresh_needed; - -extern int g_lores_colors[]; -extern int g_cur_a2_stat; - -extern int g_a2vid_palette; - -extern int g_installed_full_superhires_colormap; - -extern int g_screen_redraw_skip_amt; - -extern word32 g_a2_screen_buffer_changed; - -HWND g_hwnd_main; -BITMAPINFO *g_bmapinfo_ptr = 0; -volatile BITMAPINFOHEADER *g_bmaphdr_ptr = 0; - -int g_num_a2_keycodes = 0; - -extern char *g_status_ptrs[MAX_STATUS_LINES]; - -int g_win_button_states = 0; - - -/* this table is used to search for the Windows VK_* in col 1 or 2 */ -/* flags bit 8 is or'ed into the VK, so we can distinguish keypad keys */ -/* regardless of numlock */ -int g_a2_key_to_wsym[][3] = { - { 0x35, VK_ESCAPE, 0 }, - { 0x7a, VK_F1, 0 }, - { 0x7b, VK_F2, 0 }, - { 0x63, VK_F3, 0 }, - { 0x76, VK_F4, 0 }, - { 0x60, VK_F5, 0 }, - { 0x61, VK_F6, 0 }, - { 0x62, VK_F7, 0 }, - { 0x64, VK_F8, 0 }, - { 0x65, VK_F9, 0 }, - { 0x6d, VK_F10, 0 }, - { 0x67, VK_F11, 0 }, - { 0x6f, VK_F12, 0 }, - { 0x69, VK_F13, 0 }, - { 0x6b, VK_F14, 0 }, - { 0x71, VK_F15, 0 }, - { 0x7f, VK_PAUSE, VK_CANCEL+0x100 }, - - { 0x32, 0xc0, 0 }, /* '`' */ - { 0x12, '1', 0 }, - { 0x13, '2', 0 }, - { 0x14, '3', 0 }, - { 0x15, '4', 0 }, - { 0x17, '5', 0 }, - { 0x16, '6', 0 }, - { 0x1a, '7', 0 }, - { 0x1c, '8', 0 }, - { 0x19, '9', 0 }, - { 0x1d, '0', 0 }, - { 0x1b, 0xbd, 0 }, /* '-' */ - { 0x18, 0xbb, 0 }, /* '=' */ - { 0x33, VK_BACK, 0 }, /* backspace */ - { 0x72, VK_INSERT+0x100, 0 }, /* Insert key */ -/* { 0x73, XK_Home, 0 }, alias VK_HOME to be KP_Equal! */ - { 0x74, VK_PRIOR+0x100, 0 }, /* pageup */ - { 0x47, VK_NUMLOCK, VK_NUMLOCK+0x100 }, /* clear */ - { 0x51, VK_HOME+0x100, 0 }, /* KP_equal is HOME key */ - { 0x4b, VK_DIVIDE, VK_DIVIDE+0x100 }, - { 0x43, VK_MULTIPLY, VK_MULTIPLY+0x100 }, - - { 0x30, VK_TAB, 0 }, - { 0x0c, 'Q', 0 }, - { 0x0d, 'W', 0 }, - { 0x0e, 'E', 0 }, - { 0x0f, 'R', 0 }, - { 0x11, 'T', 0 }, - { 0x10, 'Y', 0 }, - { 0x20, 'U', 0 }, - { 0x22, 'I', 0 }, - { 0x1f, 'O', 0 }, - { 0x23, 'P', 0 }, - { 0x21, 0xdb, 0 }, /* [ */ - { 0x1e, 0xdd, 0 }, /* ] */ - { 0x2a, 0xdc, 0 }, /* backslash, bar */ - { 0x75, VK_DELETE+0x100, 0 }, - { 0x77, VK_END+0x100, VK_END }, - { 0x79, VK_NEXT+0x100, 0 }, - { 0x59, VK_NUMPAD7, VK_HOME }, - { 0x5b, VK_NUMPAD8, VK_UP }, - { 0x5c, VK_NUMPAD9, VK_PRIOR }, - { 0x4e, VK_SUBTRACT, VK_SUBTRACT+0x100 }, - - // { 0x39, VK_CAPITAL, 0 }, // Handled specially! - { 0x00, 'A', 0 }, - { 0x01, 'S', 0 }, - { 0x02, 'D', 0 }, - { 0x03, 'F', 0 }, - { 0x05, 'G', 0 }, - { 0x04, 'H', 0 }, - { 0x26, 'J', 0 }, - { 0x28, 'K', 0 }, - { 0x25, 'L', 0 }, - { 0x29, 0xba, 0 }, /* ; */ - { 0x27, 0xde, 0 }, /* single quote */ - { 0x24, VK_RETURN, 0 }, - { 0x56, VK_NUMPAD4, VK_LEFT }, - { 0x57, VK_NUMPAD5, VK_CLEAR }, - { 0x58, VK_NUMPAD6, VK_RIGHT }, - { 0x45, VK_ADD, 0 }, - - { 0x38, VK_SHIFT, 0 }, - { 0x06, 'Z', 0 }, - { 0x07, 'X', 0 }, - { 0x08, 'C', 0 }, - { 0x09, 'V', 0 }, - { 0x0b, 'B', 0 }, - { 0x2d, 'N', 0 }, - { 0x2e, 'M', 0 }, - { 0x2b, 0xbc, 0 }, /* , */ - { 0x2f, 0xbe, 0 }, /* . */ - { 0x2c, 0xbf, 0 }, /* / */ - { 0x3e, VK_UP+0x100, 0 }, - { 0x53, VK_NUMPAD1, VK_END }, - { 0x54, VK_NUMPAD2, VK_DOWN }, - { 0x55, VK_NUMPAD3, VK_NEXT }, - - { 0x36, VK_CONTROL, VK_CONTROL+0x100 }, - { 0x3a, VK_SNAPSHOT+0x100, VK_MENU+0x100 },/* Opt=prntscrn or alt-r */ - { 0x37, VK_SCROLL, VK_MENU }, /* Command=scr_lock or alt-l */ - { 0x31, ' ', 0 }, - { 0x3b, VK_LEFT+0x100, 0 }, - { 0x3d, VK_DOWN+0x100, 0 }, - { 0x3c, VK_RIGHT+0x100, 0 }, - { 0x52, VK_NUMPAD0, VK_INSERT }, - { 0x41, VK_DECIMAL, VK_DECIMAL }, - { 0x4c, VK_RETURN+0x100, 0 }, - { -1, -1, -1 } -}; - -int -win_nonblock_read_stdin(int fd, char *bufptr, int len) -{ - HANDLE oshandle; - DWORD dwret; - int ret; - - errno = EAGAIN; - oshandle = (HANDLE)_get_osfhandle(fd); // get stdin handle - dwret = WaitForSingleObject(oshandle, 1); // wait 1msec for data - ret = -1; - if(dwret == WAIT_OBJECT_0) { - ret = read(fd, bufptr, len); - } - return ret; -} - -void -x_dialog_create_kegs_conf(const char *str) -{ -} - -int -x_show_alert(int is_fatal, const char *str) -{ - return 0; -} - -int -win_update_mouse(int x, int y, int button_states, int buttons_valid) -{ - int buttons_changed; - - buttons_changed = ((g_win_button_states & buttons_valid) != - button_states); - g_win_button_states = (g_win_button_states & ~buttons_valid) | - (button_states & buttons_valid); - if(g_warp_pointer && (x == A2_WINDOW_WIDTH/2) && - (y == A2_WINDOW_HEIGHT/2) && (!buttons_changed) ) { - /* tell adb routs to recenter but ignore this motion */ - update_mouse(x, y, 0, -1); - return 0; - } - return update_mouse(x, y, button_states, buttons_valid & 7); -} - -void -win_event_mouse(WPARAM wParam, LPARAM lParam) -{ - POINT pt; - word32 flags; - int buttons; - int x, y; - int motion; - - flags = wParam; - x = LOWORD(lParam) - BASE_MARGIN_LEFT; - y = HIWORD(lParam) - BASE_MARGIN_TOP; - - buttons = (flags & 1) + - (((flags >> 1) & 1) << 2) + - (((flags >> 4) & 1) << 1); -#if 0 - printf("Mouse at %d, %d fl: %08x, but: %d\n", x, y, flags, buttons); -#endif - motion = win_update_mouse(x, y, buttons, 7); - - if(motion && g_warp_pointer) { - /* move mouse to center of screen */ - pt.x = BASE_MARGIN_LEFT + A2_WINDOW_WIDTH/2; - pt.y = BASE_MARGIN_TOP + A2_WINDOW_HEIGHT/2; - ClientToScreen(g_hwnd_main, &pt); - SetCursorPos(pt.x, pt.y); - } -} - -void -win_event_key(HWND hwnd, UINT raw_vk, BOOL down, int repeat, UINT flags) -{ - word32 vk; - int a2code; - int is_up; - int capslock_down; - int i; - - if((flags & 0x4000) && down) { - /* auto-repeating, just ignore it */ - return; - } - - vk = raw_vk + (flags & 0x100); -#if 0 - printf("Key event, vk=%04x, down:%d, repeat: %d, flags: %08x\n", - vk, down, repeat, flags); -#endif - - /* remap a few keys here.. sigh */ - if((vk & 0xff) == VK_APPS) { - /* remap to command */ - vk = VK_MENU; - } - - if((vk & 0xff) == VK_CAPITAL) { - // Windows gives us up-and-down events of the actual key - // Use GetKeyState to get the true toggle state, and pass - // that on to the adb interface - capslock_down = GetKeyState(VK_CAPITAL) & 0x01; - if(capslock_down != g_win_capslock_down) { - g_win_capslock_down = capslock_down; - adb_physical_key_update(0x39, !capslock_down); - } - - return; // Do no more processing! - } - - /* search a2key_to_wsym to find wsym in col 1 or 2 */ - i = 0; - is_up = !down; - for(i = g_num_a2_keycodes-1; i >= 0; i--) { - a2code = g_a2_key_to_wsym[i][0]; - if((vk == g_a2_key_to_wsym[i][1]) || - (vk == g_a2_key_to_wsym[i][2])) { - vid_printf("Found vk:%04x = %02x\n", vk, a2code); - adb_physical_key_update(a2code, is_up); - return; - } - } - printf("VK: %04x unknown\n", vk); -} - -void -win_event_quit(HWND hwnd) -{ - g_quit_sim_now = 1; - my_exit(0); -} - -void -win_event_redraw() -{ - g_full_refresh_needed = -1; - g_a2_screen_buffer_changed = -1; - g_status_refresh_needed = 1; - g_border_sides_refresh_needed = 1; - g_border_special_refresh_needed = 1; -} - -LRESULT CALLBACK -win_event_handler(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam) -{ - switch(umsg) { - case WM_MOUSEMOVE: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - win_event_mouse(wParam, lParam); - return 0; - case WM_PAINT: - win_event_redraw(); - break; - } - switch(umsg) { - HANDLE_MSG(hwnd, WM_KEYUP, win_event_key); - HANDLE_MSG(hwnd, WM_KEYDOWN, win_event_key); - HANDLE_MSG(hwnd, WM_SYSKEYUP, win_event_key); - HANDLE_MSG(hwnd, WM_SYSKEYDOWN, win_event_key); - HANDLE_MSG(hwnd, WM_DESTROY, win_event_quit); - } - -#if 0 - switch(umsg) { - case WM_NCACTIVATE: - case WM_NCHITTEST: - case WM_NCMOUSEMOVE: - case WM_SETCURSOR: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_CONTEXTMENU: - case WM_RBUTTONUP: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - case WM_PAINT: - - break; - default: - printf("Got umsg2: %d\n", umsg); - } -#endif - - return DefWindowProc(hwnd, umsg, wParam, lParam); -} - - -int -main(int argc, char **argv) -{ - WNDCLASS wndclass; - RECT rect; - int height; - - InitCommonControls(); - - wndclass.style = 0; - wndclass.lpfnWndProc = (WNDPROC)win_event_handler; - wndclass.cbClsExtra = 0; - wndclass.cbWndExtra = 0; - wndclass.hInstance = GetModuleHandle(NULL); - wndclass.hIcon = LoadIcon((HINSTANCE)NULL, IDI_APPLICATION); - wndclass.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); - wndclass.hbrBackground = GetStockObject(WHITE_BRUSH); - wndclass.lpszMenuName = NULL; - wndclass.lpszClassName = "gsportwin"; - - // Register the window - if(!RegisterClass(&wndclass)) { - printf("Registering window failed\n"); - exit(1); - } - - height = X_A2_WINDOW_HEIGHT + (MAX_STATUS_LINES * 16) + 32; - g_main_height = height; - - g_hwnd_main = CreateWindow("gsportwin", "GSportWin - Apple //gs Emulator", - WS_TILED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, - CW_USEDEFAULT, CW_USEDEFAULT, - X_A2_WINDOW_WIDTH, height, - NULL, NULL, GetModuleHandle(NULL), NULL); - - printf("g_hwnd_main = %p, height = %d\n", g_hwnd_main, height); - GetWindowRect(g_hwnd_main, &rect); - printf("...rect is: %ld, %ld, %ld, %ld\n", rect.left, rect.top, - rect.right, rect.bottom); - - g_main_dc = GetDC(g_hwnd_main); - - SetTextColor(g_main_dc, 0); - SetBkColor(g_main_dc, 0xffffff); - - g_main_cdc = CreateCompatibleDC(g_main_dc); - - g_screen_depth = 24; - g_screen_mdepth = 32; - - - // Call kegsmain - return kegsmain(argc, argv); -} - - -void -check_input_events() -{ - MSG msg; - - while(PeekMessage(&msg, g_hwnd_main, 0, 0, PM_NOREMOVE)) { - if(GetMessage(&msg, g_hwnd_main, 0, 0) > 0) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } else { - printf("GetMessage returned <= 0\n"); - my_exit(2); - } - } - - return; -} - - -void -x_update_color(int col_num, int red, int green, int blue, word32 rgb) -{ -} - -void -x_update_physical_colormap() -{ -} - -void -show_xcolor_array() -{ - int i; - - for(i = 0; i < 256; i++) { - printf("%02x: %08x\n", i, g_palette_8to1624[i]); - } -} - - -void -xdriver_end() -{ - printf("xdriver_end\n"); -} - - -void -x_get_kimage(Kimage *kimage_ptr) -{ - byte *ptr; - int width; - int height; - int depth, mdepth; - int size; - - width = kimage_ptr->width_req; - height = kimage_ptr->height; - depth = kimage_ptr->depth; - mdepth = kimage_ptr->mdepth; - - size = 0; - if(depth == g_screen_depth) { - /* Use g_bmapinfo_ptr, adjusting width, height */ - g_bmaphdr_ptr->biWidth = width; - g_bmaphdr_ptr->biHeight = -height; - kimage_ptr->dev_handle = CreateDIBSection(g_main_dc, - g_bmapinfo_ptr, DIB_RGB_COLORS, - (VOID **)&(kimage_ptr->data_ptr), NULL, 0); - } else { - /* allocate buffers for video.c to draw into */ - - size = (width*height*mdepth) >> 3; - ptr = (byte *)malloc(size); - - if(ptr == 0) { - printf("malloc for data failed, mdepth: %d\n", mdepth); - exit(2); - } - - kimage_ptr->data_ptr = ptr; - - kimage_ptr->dev_handle = (void *)-1; - - } - printf("kim: %p, dev:%p data: %p, size: %08x\n", kimage_ptr, - kimage_ptr->dev_handle, kimage_ptr->data_ptr, size); - - return; -} - - -void -dev_video_init() -{ - int extra_size; - int lores_col; - int a2code; - int i; - - printf("Preparing graphics system\n"); - - g_num_a2_keycodes = 0; - for(i = 0; i < 0x7f; i++) { - a2code = g_a2_key_to_wsym[i][0]; - if(a2code < 0) { - g_num_a2_keycodes = i; - } - } - - g_screen_depth = 24; - g_screen_mdepth = 32; - - extra_size = sizeof(RGBQUAD); - if(g_screen_depth == 8) { - extra_size = 256 * sizeof(RGBQUAD); - } - g_bmapinfo_ptr = (BITMAPINFO *)GlobalAlloc(GPTR, - sizeof(BITMAPINFOHEADER) + extra_size); - - g_bmaphdr_ptr = (BITMAPINFOHEADER *)g_bmapinfo_ptr; - g_bmaphdr_ptr->biSize = sizeof(BITMAPINFOHEADER); - g_bmaphdr_ptr->biWidth = A2_WINDOW_WIDTH; - g_bmaphdr_ptr->biHeight = -A2_WINDOW_HEIGHT; - g_bmaphdr_ptr->biPlanes = 1; - g_bmaphdr_ptr->biBitCount = g_screen_mdepth; - g_bmaphdr_ptr->biCompression = BI_RGB; - g_bmaphdr_ptr->biClrUsed = 0; - - video_get_kimages(); - - if(g_screen_depth != 8) { - // Allocate g_mainwin_kimage - video_get_kimage(&g_mainwin_kimage, 0, g_screen_depth, - g_screen_mdepth); - } - - for(i = 0; i < 256; i++) { - lores_col = g_lores_colors[i & 0xf]; - video_update_color_raw(i, lores_col); - g_a2palette_8to1624[i] = g_palette_8to1624[i]; - } - - g_installed_full_superhires_colormap = 1; - - ShowWindow(g_hwnd_main, SW_SHOWDEFAULT); - UpdateWindow(g_hwnd_main); - - printf("Done with dev_video_init\n"); - fflush(stdout); - -} - -void -x_redraw_status_lines() -{ - COLORREF oldtextcolor, oldbkcolor; - char *buf; - int line; - int len; - int height; - int margin; - - height = 16; - margin = 0; - - oldtextcolor = SetTextColor(g_main_dc, 0); - oldbkcolor = SetBkColor(g_main_dc, 0xffffff); - for(line = 0; line < MAX_STATUS_LINES; line++) { - buf = g_status_ptrs[line]; - if(buf != 0) { - len = strlen(buf); - TextOut(g_main_dc, 10, X_A2_WINDOW_HEIGHT + - height*line + margin, buf, len); - } - } - SetTextColor(g_main_dc, oldtextcolor); - SetBkColor(g_main_dc, oldbkcolor); -} - - -void -x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, - int width, int height) -{ - void *bitm_old; - POINT point; - - point.x = 0; - point.y = 0; - ClientToScreen(g_hwnd_main, &point); - bitm_old = SelectObject(g_main_cdc, kimage_ptr->dev_handle); - - BitBlt(g_main_dc, destx, desty, width, height, - g_main_cdc, srcx, srcy, SRCCOPY); - - SelectObject(g_main_cdc, bitm_old); -} - -void -x_push_done() -{ -} - -void -x_auto_repeat_on(int must) -{ -} - -void -x_auto_repeat_off(int must) -{ -} - -void -x_hide_pointer(int do_hide) -{ - if(do_hide) { - ShowCursor(0); - } else { - ShowCursor(1); - } -} - -void -x_full_screen(int do_full) -{ - return; -} +/* + GSport - an Apple //gs Emulator + Copyright (C) 2010 by GSport contributors + + Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +const char rcsid_windriver_c[] = "@(#)$KmKId: windriver.c,v 1.11 2004-11-24 16:43:46-05 kentd Exp $"; + +/* Based on code from Chea Chee Keong from KEGS32, which is available at */ +/* http://www.geocities.com/akilgard/kegs32 */ + +#define WIN32_LEAN_AND_MEAN /* Tell windows we want less header junk */ +#define STRICT /* Tell Windows we want compile type checks */ + +#pragma comment(linker, \ + "\"/manifestdependency:type='Win32' "\ + "name='Microsoft.Windows.Common-Controls' "\ + "version='6.0.0.0' "\ + "processorArchitecture='*' "\ + "publicKeyToken='6595b64144ccf1df' "\ + "language='*'\"") + +#include +#include +#include +#include +#include +#include +#include +#include +#include "winresource.h" +#include "defc.h" +#include "protos_windriver.h" +#include "printer.h" + + +#if defined _MSC_VER && defined _WIN64 +#define gwlproc GWLP_WNDPROC +#endif +#if defined _MSC_VER && defined _WIN32 && !defined _WIN64 +#define gwlproc GWL_WNDPROC +#endif + +extern int Verbose; + +extern int g_warp_pointer; +extern int g_screen_depth; +extern int g_force_depth; + +int g_screen_mdepth = 0; + +extern int g_quit_sim_now; + +int g_use_shmem = 1; +int g_has_focus = 0; +int g_auto_repeat_on = -1; + +extern Kimage g_mainwin_kimage; + +HDC g_main_dc; +HDC g_main_cdc; +int g_main_height = 0; + +int g_win_capslock_down = 0; + +extern word32 g_palette_8to1624[256]; +extern word32 g_a2palette_8to1624[256]; + +extern word32 g_full_refresh_needed; + +extern int g_border_sides_refresh_needed; +extern int g_border_special_refresh_needed; +extern int g_status_refresh_needed; + +extern int g_lores_colors[]; +extern int g_cur_a2_stat; + +extern int g_a2vid_palette; + +extern int g_installed_full_superhires_colormap; + +extern int g_screen_redraw_skip_amt; + +extern word32 g_a2_screen_buffer_changed; + +HWND g_hwnd_main; +BITMAPINFO *g_bmapinfo_ptr = 0; +volatile BITMAPINFOHEADER *g_bmaphdr_ptr = 0; + +// KEGS32 specific customisations +HWND g_win_toolbar=NULL; +HWND g_win_status=NULL; +RECT g_win_status_height={0}; +RECT g_win_toolbar_height={0}; +int g_win_status_debug = 0; +RECT g_main_window_saved_rect; +HMENU g_main_window_menu_saved; +int g_win_fullscreen_state = 0; +WNDPROC oldEditWndProc; +extern int g_joystick_type; +extern int g_config_kegs_update_needed; +extern int g_limit_speed; +extern int g_doc_vol; + +int g_num_a2_keycodes = 0; + +extern char *g_status_ptrs[MAX_STATUS_LINES]; + +int g_win_button_states = 0; + + +/* this table is used to search for the Windows VK_* in col 1 or 2 */ +/* flags bit 8 is or'ed into the VK, so we can distinguish keypad keys */ +/* regardless of numlock */ +int g_a2_key_to_wsym[][3] = { + { 0x35, VK_ESCAPE, 0 }, + { 0x7a, VK_F1, 0 }, + { 0x78, VK_F2, 0 }, + { 0x63, VK_F3, 0 }, + { 0x76, VK_F4, 0 }, + { 0x60, VK_F5, 0 }, + { 0x61, VK_F6, 0 }, + { 0x62, VK_F7, 0 }, + { 0x64, VK_F8, 0 }, + { 0x65, VK_F9, 0 }, + { 0x6d, VK_F10, 0 }, + { 0x67, VK_F11, 0 }, + { 0x6f, VK_F12, 0 }, + { 0x69, VK_F13, 0 }, + { 0x6b, VK_F14, 0 }, + { 0x71, VK_F15, 0 }, + { 0x7f, VK_PAUSE, VK_CANCEL+0x100 }, + + { 0x32, 0xc0, 0 }, /* '`' */ + { 0x12, '1', 0 }, + { 0x13, '2', 0 }, + { 0x14, '3', 0 }, + { 0x15, '4', 0 }, + { 0x17, '5', 0 }, + { 0x16, '6', 0 }, + { 0x1a, '7', 0 }, + { 0x1c, '8', 0 }, + { 0x19, '9', 0 }, + { 0x1d, '0', 0 }, + { 0x1b, 0xbd, 0 }, /* '-' */ + { 0x18, 0xbb, 0 }, /* '=' */ + { 0x33, VK_BACK, 0 }, /* backspace */ + { 0x72, VK_INSERT+0x100, 0 }, /* Insert key */ +/* { 0x73, XK_Home, 0 }, alias VK_HOME to be KP_Equal! */ + { 0x74, VK_PRIOR+0x100, 0 }, /* pageup */ + { 0x47, VK_NUMLOCK, VK_NUMLOCK+0x100 }, /* clear */ + { 0x51, VK_HOME+0x100, 0 }, /* KP_equal is HOME key */ + { 0x4b, VK_DIVIDE, VK_DIVIDE+0x100 }, + { 0x43, VK_MULTIPLY, VK_MULTIPLY+0x100 }, + + { 0x30, VK_TAB, 0 }, + { 0x0c, 'Q', 0 }, + { 0x0d, 'W', 0 }, + { 0x0e, 'E', 0 }, + { 0x0f, 'R', 0 }, + { 0x11, 'T', 0 }, + { 0x10, 'Y', 0 }, + { 0x20, 'U', 0 }, + { 0x22, 'I', 0 }, + { 0x1f, 'O', 0 }, + { 0x23, 'P', 0 }, + { 0x21, 0xdb, 0 }, /* [ */ + { 0x1e, 0xdd, 0 }, /* ] */ + { 0x2a, 0xdc, 0 }, /* backslash, bar */ + { 0x75, VK_DELETE+0x100, 0 }, + { 0x77, VK_END+0x100, VK_END }, + { 0x79, VK_NEXT+0x100, 0 }, + { 0x59, VK_NUMPAD7, VK_HOME }, + { 0x5b, VK_NUMPAD8, VK_UP }, + { 0x5c, VK_NUMPAD9, VK_PRIOR }, + { 0x4e, VK_SUBTRACT, VK_SUBTRACT+0x100 }, + + // { 0x39, VK_CAPITAL, 0 }, // Handled specially! + { 0x00, 'A', 0 }, + { 0x01, 'S', 0 }, + { 0x02, 'D', 0 }, + { 0x03, 'F', 0 }, + { 0x05, 'G', 0 }, + { 0x04, 'H', 0 }, + { 0x26, 'J', 0 }, + { 0x28, 'K', 0 }, + { 0x25, 'L', 0 }, + { 0x29, 0xba, 0 }, /* ; */ + { 0x27, 0xde, 0 }, /* single quote */ + { 0x24, VK_RETURN, 0 }, + { 0x56, VK_NUMPAD4, VK_LEFT }, + { 0x57, VK_NUMPAD5, VK_CLEAR }, + { 0x58, VK_NUMPAD6, VK_RIGHT }, + { 0x45, VK_ADD, 0 }, + + { 0x38, VK_SHIFT, 0 }, + { 0x06, 'Z', 0 }, + { 0x07, 'X', 0 }, + { 0x08, 'C', 0 }, + { 0x09, 'V', 0 }, + { 0x0b, 'B', 0 }, + { 0x2d, 'N', 0 }, + { 0x2e, 'M', 0 }, + { 0x2b, 0xbc, 0 }, /* , */ + { 0x2f, 0xbe, 0 }, /* . */ + { 0x2c, 0xbf, 0 }, /* / */ + { 0x3e, VK_UP+0x100, 0 }, + { 0x53, VK_NUMPAD1, VK_END }, + { 0x54, VK_NUMPAD2, VK_DOWN }, + { 0x55, VK_NUMPAD3, VK_NEXT }, + + { 0x36, VK_CONTROL, VK_CONTROL+0x100 }, + { 0x3a, VK_SNAPSHOT+0x100, VK_MENU+0x100 },/* Opt=prntscrn or alt-r */ + { 0x37, VK_SCROLL, VK_MENU }, /* Command=scr_lock or alt-l */ + { 0x31, ' ', 0 }, + { 0x3b, VK_LEFT+0x100, 0 }, + { 0x3d, VK_DOWN+0x100, 0 }, + { 0x3c, VK_RIGHT+0x100, 0 }, + { 0x52, VK_NUMPAD0, VK_INSERT }, + { 0x41, VK_DECIMAL, VK_DECIMAL }, + { 0x4c, VK_RETURN+0x100, 0 }, + { -1, -1, -1 } +}; + +int +win_nonblock_read_stdin(int fd, char *bufptr, int len) +{ + HANDLE oshandle; + DWORD dwret; + int ret; + + errno = EAGAIN; + oshandle = (HANDLE)_get_osfhandle(fd); // get stdin handle + dwret = WaitForSingleObject(oshandle, 1); // wait 1msec for data + ret = -1; + if(dwret == WAIT_OBJECT_0) { + ret = read(fd, bufptr, len); + } + return ret; +} + +void +init_window(HWND hwnd,BOOL initFlag) { + RECT rect; + RECT wrect; + int adjx,adjy; + int win_height; + GetClientRect(hwnd,&rect); + GetWindowRect(hwnd,&wrect); + adjx=(wrect.right-wrect.left)-(rect.right-rect.left); + adjy=(wrect.bottom-wrect.top)-(rect.bottom-rect.top); + + win_height=X_A2_WINDOW_HEIGHT+(g_win_toolbar_height.bottom-g_win_toolbar_height.top)+(g_win_status_height.bottom-g_win_status_height.top); + + if (g_win_status_debug) { + win_height+=(MAX_STATUS_LINES*16); + } + + if (initFlag) { + SetWindowPos(hwnd,NULL, + g_main_window_saved_rect.left, + g_main_window_saved_rect.top, + X_A2_WINDOW_WIDTH+adjx, + win_height+adjy, + SWP_NOACTIVATE | SWP_NOZORDER); + } else { + SetWindowPos(hwnd,HWND_NOTOPMOST, + g_main_window_saved_rect.left, + g_main_window_saved_rect.top, + X_A2_WINDOW_WIDTH+adjx, + win_height+adjy, + SWP_SHOWWINDOW); + } + SendMessage(g_win_status,WM_SIZE,0,0); + SendMessage(g_win_toolbar,WM_SIZE,0,0); +} + +void win_center_dialog (HWND hDlg) { + RECT rc, rcDlg,rcOwner; + HWND hwndOwner; + + if ((hwndOwner = GetParent(hDlg)) == NULL) { + hwndOwner = GetDesktopWindow(); + } + + GetWindowRect(hwndOwner,&rcOwner); + GetWindowRect(hDlg,&rcDlg); + CopyRect(&rc,&rcOwner); + OffsetRect(&rcDlg,-rcDlg.left,-rcDlg.top); + OffsetRect(&rc,-rc.left,-rc.top); + OffsetRect(&rc,-rcDlg.right,-rcDlg.bottom); + + SetWindowPos(hDlg,g_hwnd_main,rcOwner.left+(rc.right/2), + rcOwner.top+(rc.bottom/2), 0,0, + SWP_SHOWWINDOW | SWP_NOSIZE ); +} + +void +read_disk_entry(HWND hDlg) +{ + char buf[2048]; + int slot,drive; + HWND hwnd; + for (slot=5;slot<=7;slot++) { + for(drive=0;drive<=1;drive++) { + Disk* dsk = cfg_get_dsk_from_slot_drive(slot,drive); + buf[0]=0; + if (dsk->name_ptr && dsk->fd > 0) { + hwnd = GetDlgItem(hDlg,10000+slot*10+(drive+1)); + strcpy(buf,dsk->name_ptr); + SetWindowText(hwnd,buf); + } + } + } +} + +void +write_disk_entry(HWND hDlg) +{ + char buf[2048]; + int slot,drive; + HWND hwnd; + for (slot=5;slot<=7;slot++) { + for(drive=0;drive<=1;drive++) { + hwnd = GetDlgItem(hDlg,10000+slot*10+(drive+1)); + buf[0]=0; + GetWindowText(hwnd,buf,2048); + if (lstrlen(buf)>0) { + insert_disk(slot, drive, buf, 0, 0, 0, -1); + } else { + eject_disk_by_num(slot,drive); + } + } + } +} + +// Message handler for handling edit control +// For dropping files +LRESULT CALLBACK win_dialog_edit(HWND hwnd, UINT message, WPARAM wParam, + LPARAM lParam) { + switch (message) { + case WM_DROPFILES: + { + TCHAR buffer[2048]={0}; + HDROP hdrop = (HDROP) wParam; + memset(buffer,0,2048*sizeof(TCHAR)); + DragQueryFile(hdrop,0,buffer,2048); + SetWindowText(hwnd,buffer); + DragFinish(hdrop); + return 0; + } + default: + return CallWindowProc(oldEditWndProc,hwnd,message,wParam,lParam); + } +} + +// Message handler for disk config. +LRESULT CALLBACK win_dialog_disk(HWND hDlg, UINT message, WPARAM wParam, + LPARAM lParam) +{ + typedef LONG(CALLBACK *SETWL)(HWND,int,LONG); + + SETWL SetWL=NULL; + + switch (message) { + case WM_INITDIALOG: + win_center_dialog(hDlg); + + // Subclass the edit-control + SetWL=SetWindowLong; + oldEditWndProc=(WNDPROC) SetWL(GetDlgItem(hDlg,IDC_EDIT_S5D1), + gwlproc,(LONG)win_dialog_edit); + SetWL(GetDlgItem(hDlg,IDC_EDIT_S5D2),gwlproc, + (LONG)win_dialog_edit); + SetWL(GetDlgItem(hDlg,IDC_EDIT_S6D1),gwlproc, + (LONG)win_dialog_edit); + SetWL(GetDlgItem(hDlg,IDC_EDIT_S6D2),gwlproc, + (LONG)win_dialog_edit); + SetWL(GetDlgItem(hDlg,IDC_EDIT_S7D1),gwlproc, + (LONG)win_dialog_edit); + SetWL(GetDlgItem(hDlg,IDC_EDIT_S7D2),gwlproc, + (LONG)win_dialog_edit); + + // Clean entry field (init) + SetWindowText(GetDlgItem(hDlg,IDC_EDIT_S5D1),_T("")); + SetWindowText(GetDlgItem(hDlg,IDC_EDIT_S5D2),_T("")); + SetWindowText(GetDlgItem(hDlg,IDC_EDIT_S6D1),_T("")); + SetWindowText(GetDlgItem(hDlg,IDC_EDIT_S6D2),_T("")); + SetWindowText(GetDlgItem(hDlg,IDC_EDIT_S7D1),_T("")); + SetWindowText(GetDlgItem(hDlg,IDC_EDIT_S7D2),_T("")); + read_disk_entry(hDlg); + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + write_disk_entry(hDlg); + EndDialog(hDlg, LOWORD(wParam)); + return TRUE; + case IDCANCEL: + EndDialog(hDlg, LOWORD(wParam)); + return FALSE; + case IDC_BTN_S5D1: + case IDC_BTN_S5D2: + case IDC_BTN_S6D1: + case IDC_BTN_S6D2: + case IDC_BTN_S7D1: + case IDC_BTN_S7D2: + { + + TCHAR filename[2048]={0}; + + OPENFILENAME opfn; + ZeroMemory(&opfn,sizeof(opfn)); + opfn.lStructSize=sizeof(opfn); + opfn.hwndOwner=hDlg; + opfn.lpstrFilter=_T("2mg format (*.2mg)\0*.2mg\0" + "Prodos order format (*.po)\0*.po\0" + "Dos order format (*.dsk)\0*.dsk\0" + "All Files (*.*)\0*.*\0" + "\0\0"); + opfn.lpstrFile=filename; + opfn.nMaxFile=2048; + opfn.Flags=OFN_EXPLORER | OFN_FILEMUSTEXIST | + OFN_HIDEREADONLY | OFN_NOCHANGEDIR ; + if (GetOpenFileName(&opfn)) { + SetWindowText(GetDlgItem(hDlg,LOWORD(wParam-1000)), + filename); + } + } + break; + } + break; + } + return FALSE; +} + + + +// Message handler for about box. +LRESULT CALLBACK win_dialog_about_dialog(HWND hDlg, + UINT message, WPARAM wParam, + LPARAM lParam) +{ + switch (message) { + case WM_INITDIALOG: + win_center_dialog(hDlg); + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + EndDialog(hDlg, LOWORD(wParam)); + return TRUE; + case IDCANCEL: + EndDialog(hDlg, LOWORD(wParam)); + return FALSE; + + } + } + return FALSE; +} + +void +x_dialog_create_kegs_conf(const char *str) +{ +} + +int +x_show_alert(int is_fatal, const char *str) +{ + return 0; +} + +int +win_update_mouse(int x, int y, int button_states, int buttons_valid) +{ + int buttons_changed; + + buttons_changed = ((g_win_button_states & buttons_valid) != + button_states); + g_win_button_states = (g_win_button_states & ~buttons_valid) | + (button_states & buttons_valid); + if(g_warp_pointer && (x == A2_WINDOW_WIDTH/2) && + (y == A2_WINDOW_HEIGHT/2) && (!buttons_changed) ) { + /* tell adb routs to recenter but ignore this motion */ + update_mouse(x, y, 0, -1); + return 0; + } + return update_mouse(x, y, button_states, buttons_valid & 7); +} + +void +win_event_mouse(WPARAM wParam, LPARAM lParam) +{ + POINT pt; + word32 flags; + int buttons; + int x, y; + int motion; + + flags = wParam; + x = LOWORD(lParam) - BASE_MARGIN_LEFT; + y = HIWORD(lParam) - BASE_MARGIN_TOP; + + buttons = (flags & 1) + + (((flags >> 1) & 1) << 2) + + (((flags >> 4) & 1) << 1); +#if 0 + printf("Mouse at %d, %d fl: %08x, but: %d\n", x, y, flags, buttons); +#endif + motion = win_update_mouse(x, y, buttons, 7); + + if(motion && g_warp_pointer) { + /* move mouse to center of screen */ + pt.x = BASE_MARGIN_LEFT + A2_WINDOW_WIDTH/2; + pt.y = BASE_MARGIN_TOP + A2_WINDOW_HEIGHT/2; + ClientToScreen(g_hwnd_main, &pt); + SetCursorPos(pt.x, pt.y); + } +} + +void +win_event_command(HWND hwnd, int id, HWND g_wnd_ctl, UINT code_notify) +{ + switch (id) { + case ID_HELP_ABOUT: + DialogBoxParam(GetModuleHandle(NULL), + (LPCTSTR)IDD_ABOUT_DIALOG, + hwnd, + (DLGPROC)win_dialog_about_dialog,0); + break; + case ID_HELP_KEY: + DialogBoxParam(GetModuleHandle(NULL), + (LPCTSTR)IDD_KEGS32_KEY, + hwnd, + (DLGPROC)win_dialog_about_dialog,0); + break; + case ID_FILE_EXIT: + PostQuitMessage(0); + break; + + case ID_FILE_SENDRESET: + // Simulate key pressing to send reset + adb_physical_key_update(0x36, 0); + adb_physical_key_update(0x7f, 0); + adb_physical_key_update(0x7f, 1); + adb_physical_key_update(0x36, 1); + break; + case ID_FILE_SENDREBOOT: + // Simulate key pressing to send reset + adb_physical_key_update(0x36, 0); + adb_physical_key_update(0x37, 0); + adb_physical_key_update(0x7f, 0); + adb_physical_key_update(0x7f, 1); + adb_physical_key_update(0x36, 1); + adb_physical_key_update(0x37, 1); + adb_physical_key_update(0x37, 0); + break; + case ID_FILE_FLUSHPRINTER: + printer_feed(); + break; + case ID_FILE_DEBUGSTAT: + g_win_status_debug = ! g_win_status_debug; + GetWindowRect(g_hwnd_main,&g_main_window_saved_rect); + init_window(g_hwnd_main,FALSE); + if (g_win_status_debug) { + CheckMenuItem(GetMenu(g_hwnd_main), + ID_FILE_DEBUGSTAT,MF_CHECKED); + } else { + CheckMenuItem(GetMenu(g_hwnd_main), + ID_FILE_DEBUGSTAT,MF_UNCHECKED); + } + break; + case ID_FILE_JOYSTICK: + if (g_joystick_type == 4) { + g_joystick_type=2; + + } else { + g_joystick_type=4; + } + if (g_joystick_type==4) { + CheckMenuItem(GetMenu(g_hwnd_main),ID_FILE_JOYSTICK, + MF_UNCHECKED); + } else { + CheckMenuItem(GetMenu(g_hwnd_main),ID_FILE_JOYSTICK, + MF_CHECKED); + } + g_config_kegs_update_needed=1; + break; + case ID_FILE_DISK: + DialogBoxParam(GetModuleHandle(NULL), + (LPCTSTR)IDD_DLG_DISKCONF, + g_hwnd_main, + (DLGPROC)win_dialog_disk,0); + break; + case ID_SPEED_1MHZ: + g_limit_speed = 1; + g_config_kegs_update_needed=1; + break; + case ID_SPEED_2MHZ: + g_limit_speed = 2; + g_config_kegs_update_needed=1; + break; + case ID_SPEED_8MHZ: + g_limit_speed = 3; + g_config_kegs_update_needed=1; + break; + case ID_SPEED_FMHZ: + g_limit_speed = 0; + g_config_kegs_update_needed=1; + break; + default: + break; + } +} + +void +win_event_key(HWND hwnd, UINT raw_vk, BOOL down, int repeat, UINT flags) +{ + word32 vk; + int a2code; + int is_up; + int capslock_down; + int i; + + if((flags & 0x4000) && down) { + /* auto-repeating, just ignore it */ + return; + } + + vk = raw_vk + (flags & 0x100); + +#if 0 + printf("Key event, vk=%04x, down:%d, repeat: %d, flags: %08x\n", + vk, down, repeat, flags); +#endif + + /* remap a few keys here.. sigh */ + if((vk & 0xff) == VK_APPS) { + /* remap to command */ + vk = VK_MENU; + } + + if((vk & 0xff) == VK_CAPITAL) { + // Windows gives us up-and-down events of the actual key + // Use GetKeyState to get the true toggle state, and pass + // that on to the adb interface + capslock_down = GetKeyState(VK_CAPITAL) & 0x01; + if(capslock_down != g_win_capslock_down) { + g_win_capslock_down = capslock_down; + adb_physical_key_update(0x39, !capslock_down); + } + + return; // Do no more processing! + } + + /* search a2key_to_wsym to find wsym in col 1 or 2 */ + i = 0; + is_up = !down; + for(i = g_num_a2_keycodes-1; i >= 0; i--) { + a2code = g_a2_key_to_wsym[i][0]; + if((vk == g_a2_key_to_wsym[i][1]) || + (vk == g_a2_key_to_wsym[i][2])) { + vid_printf("Found vk:%04x = %02x\n", vk, a2code); + adb_physical_key_update(a2code, is_up); + return; + } + } + printf("VK: %04x unknown\n", vk); +} + +void +win_event_quit(HWND hwnd) +{ + g_quit_sim_now = 1; + my_exit(0); +} + +void +win_event_redraw() +{ + g_full_refresh_needed = -1; + g_a2_screen_buffer_changed = -1; + g_status_refresh_needed = 1; + g_border_sides_refresh_needed = 1; + g_border_special_refresh_needed = 1; +} + +LRESULT CALLBACK +win_event_handler(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam) +{ + switch(umsg) { + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + win_event_mouse(wParam, lParam); + return 0; + case WM_PAINT: + win_event_redraw(); + break; + } + switch(umsg) { + HANDLE_MSG(hwnd, WM_KEYUP, win_event_key); + HANDLE_MSG(hwnd, WM_KEYDOWN, win_event_key); + HANDLE_MSG(hwnd, WM_SYSKEYUP, win_event_key); + HANDLE_MSG(hwnd, WM_SYSKEYDOWN, win_event_key); + HANDLE_MSG(hwnd, WM_DESTROY, win_event_quit); + HANDLE_MSG(hwnd, WM_COMMAND, win_event_command); + } + +#if 0 + switch(umsg) { + case WM_NCACTIVATE: + case WM_NCHITTEST: + case WM_NCMOUSEMOVE: + case WM_SETCURSOR: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_CONTEXTMENU: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_PAINT: + + break; + default: + printf("Got umsg2: %d\n", umsg); + } +#endif + + return DefWindowProc(hwnd, umsg, wParam, lParam); +} + + +int +_tmain(int argc, char **argv) +{ + WNDCLASS wndclass; + RECT rect; + int height; + int iStatusWidths[] = {60, 100,200,300, -1}; + + InitCommonControls(); + + wndclass.style = 0; + wndclass.lpfnWndProc = (WNDPROC)win_event_handler; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = GetModuleHandle(NULL); + wndclass.hIcon = LoadIcon((HINSTANCE) GetModuleHandle(NULL), + MAKEINTRESOURCE(IDC_KEGS32)); + wndclass.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); + wndclass.hbrBackground = GetStockObject(BLACK_BRUSH); + wndclass.lpszMenuName = MAKEINTRESOURCE(IDC_KEGS32); + wndclass.lpszClassName = _T("Kegs32"); + + // Register the window + if(!RegisterClass(&wndclass)) { + printf("Registering window failed\n"); + exit(1); + } + + height = X_A2_WINDOW_HEIGHT + (MAX_STATUS_LINES * 16) + 32; + g_main_height = height; + + g_hwnd_main = CreateWindow(_T("KEGS32"), + _T("KEGS32 - Apple //gs Emulator"), + WS_TILED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, + CW_USEDEFAULT, CW_USEDEFAULT, + X_A2_WINDOW_WIDTH, height, + NULL, NULL, GetModuleHandle(NULL), NULL); + + if (!g_hwnd_main) { + printf("Window create failed\n"); + exit(1); + } + + // Create Toolbar + g_win_toolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, + WS_CHILD | WS_VISIBLE | CCS_ADJUSTABLE | TBSTYLE_TOOLTIPS, + 0, 0, 0, 0, + g_hwnd_main,(HMENU)IDC_KEGS32, GetModuleHandle(NULL), NULL); + SendMessage(g_win_toolbar,TB_BUTTONSTRUCTSIZE,(WPARAM)sizeof(TBBUTTON),0); + { + TBADDBITMAP tbab; + TBBUTTON tbb[6]; + int i,j; + int idCmd[]={ID_FILE_DISK,-1, + ID_SPEED_1MHZ,ID_SPEED_2MHZ,ID_SPEED_8MHZ,ID_SPEED_FMHZ}; + + ZeroMemory(tbb, sizeof(tbb)); + + for (i=0,j=0;i 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } else { + printf("GetMessage returned <= 0\n"); + my_exit(2); + } + } + + return; +} + + +void +x_update_color(int col_num, int red, int green, int blue, word32 rgb) +{ +} + +void +x_update_physical_colormap() +{ +} + +void +show_xcolor_array() +{ + int i; + + for(i = 0; i < 256; i++) { + printf("%02x: %08x\n", i, g_palette_8to1624[i]); + } +} + + +void +xdriver_end() +{ + printf("xdriver_end\n"); +} + + +void +x_get_kimage(Kimage *kimage_ptr) +{ + byte *ptr; + int width; + int height; + int depth, mdepth; + int size; + + width = kimage_ptr->width_req; + height = kimage_ptr->height; + depth = kimage_ptr->depth; + mdepth = kimage_ptr->mdepth; + + size = 0; + if(depth == g_screen_depth) { + /* Use g_bmapinfo_ptr, adjusting width, height */ + g_bmaphdr_ptr->biWidth = width; + g_bmaphdr_ptr->biHeight = -height; + kimage_ptr->dev_handle = CreateDIBSection(g_main_dc, + g_bmapinfo_ptr, DIB_RGB_COLORS, + (VOID **)&(kimage_ptr->data_ptr), NULL, 0); + } else { + /* allocate buffers for video.c to draw into */ + + size = (width*height*mdepth) >> 3; + ptr = (byte *)malloc(size); + + if(ptr == 0) { + printf("malloc for data failed, mdepth: %d\n", mdepth); + exit(2); + } + + kimage_ptr->data_ptr = ptr; + + kimage_ptr->dev_handle = (void *)-1; + + } + printf("kim: %p, dev:%p data: %p, size: %08x\n", kimage_ptr, + kimage_ptr->dev_handle, kimage_ptr->data_ptr, size); + + return; +} + +void +dev_video_init() +{ + int extra_size; + int lores_col; + int a2code; + int i; + + printf("Preparing graphics system\n"); + + g_num_a2_keycodes = 0; + for(i = 0; i < 0x7f; i++) { + a2code = g_a2_key_to_wsym[i][0]; + if(a2code < 0) { + g_num_a2_keycodes = i; + } + } + + g_screen_depth = 24; + g_screen_mdepth = 32; + + extra_size = sizeof(RGBQUAD); + if(g_screen_depth == 8) { + extra_size = 256 * sizeof(RGBQUAD); + } + g_bmapinfo_ptr = (BITMAPINFO *)GlobalAlloc(GPTR, + sizeof(BITMAPINFOHEADER) + extra_size); + + g_bmaphdr_ptr = (BITMAPINFOHEADER *)g_bmapinfo_ptr; + g_bmaphdr_ptr->biSize = sizeof(BITMAPINFOHEADER); + g_bmaphdr_ptr->biWidth = A2_WINDOW_WIDTH; + g_bmaphdr_ptr->biHeight = -A2_WINDOW_HEIGHT; + g_bmaphdr_ptr->biPlanes = 1; + g_bmaphdr_ptr->biBitCount = g_screen_mdepth; + g_bmaphdr_ptr->biCompression = BI_RGB; + g_bmaphdr_ptr->biClrUsed = 0; + + video_get_kimages(); + + if(g_screen_depth != 8) { + // Allocate g_mainwin_kimage + video_get_kimage(&g_mainwin_kimage, 0, g_screen_depth, + g_screen_mdepth); + } + + for(i = 0; i < 256; i++) { + lores_col = g_lores_colors[i & 0xf]; + video_update_color_raw(i, lores_col); + g_a2palette_8to1624[i] = g_palette_8to1624[i]; + } + + g_installed_full_superhires_colormap = 1; + + GetWindowRect(g_hwnd_main,&g_main_window_saved_rect); + init_window(g_hwnd_main,TRUE); + ShowWindow(g_hwnd_main, SW_SHOWDEFAULT); + UpdateWindow(g_hwnd_main); + + printf("Done with dev_video_init\n"); + fflush(stdout); + +} + +void +x_redraw_status_lines() +{ + COLORREF oldtextcolor, oldbkcolor; + char *buf; + int line; + int len; + int height; + int margin; + int center_x=0; + int center_y=0; + int toolbar_height; + RECT wrect; + TCHAR buffer[255]; + + height = 16; + margin = 0; + + toolbar_height = + (g_win_toolbar_height.bottom-g_win_toolbar_height.top); + + if (g_win_fullscreen_state) { + GetWindowRect(g_hwnd_main,&wrect); + center_x=(wrect.right-wrect.left)-X_A2_WINDOW_WIDTH; + center_x=center_x/2; + center_y=(wrect.bottom-wrect.top)-X_A2_WINDOW_HEIGHT; + if (g_win_status_debug) { + center_y-=(MAX_STATUS_LINES*16); + } + center_y=center_y/2; + toolbar_height=0; + } + + if (g_win_status_debug) { + oldtextcolor = SetTextColor(g_main_dc, 0xffffff); + oldbkcolor = SetBkColor(g_main_dc, 0x0); + for(line = 0; line < MAX_STATUS_LINES; line++) { + buf = g_status_ptrs[line]; + if(buf != 0) { + len = strlen(buf); + TextOut(g_main_dc, 2+center_x, + X_A2_WINDOW_HEIGHT + + height*line + margin + + toolbar_height+center_y, + buf, len); + } + } + SetTextColor(g_main_dc, oldtextcolor); + SetBkColor(g_main_dc, oldbkcolor); + } + + if (g_win_status !=NULL) { + SendMessage(g_win_status, SB_SETTEXT,0,(LPARAM)_T("GSport ")); + _stprintf(buffer,_T("Vol:%d"),g_doc_vol); + SendMessage(g_win_status, SB_SETTEXT,1,(LPARAM)buffer); + buf=g_status_ptrs[0]; + if (buf != NULL) { + buf=strstr(g_status_ptrs[0],"sim MHz:"); + len=strchr(buf+9,' ')-buf; + buffer[len]=0; + if (sizeof(TCHAR) <2) { + strncpy((char *)buffer,buf,len); + } else { + mbstowcs((wchar_t *)buffer,buf,len); + } + SendMessageA(g_win_status, SB_SETTEXT,2,(LPARAM) buffer); + } else { + SendMessageA(g_win_status,SB_SETTEXT,2,(LPARAM)_T( + "sim MHz:???")); + } + buf=g_status_ptrs[0]; + if (buf != NULL) { + buf=strstr(g_status_ptrs[0],"Eff MHz:"); + len=strchr(buf+9,',')-buf; + buffer[len]=0; + if (sizeof(TCHAR) <2) { + strncpy((char *)buffer,buf,len); + } else { + mbstowcs((wchar_t *)buffer,buf,len); + } + SendMessageA(g_win_status, SB_SETTEXT,3,(LPARAM) buffer); + } else { + SendMessageA(g_win_status,SB_SETTEXT,3,(LPARAM)_T( + "Eff MHz:???")); + } + buf=g_status_ptrs[5]; + if (buf != NULL) { + buf=strstr(g_status_ptrs[5],"fast"); + len=buf-&(g_status_ptrs[5][0]); + buffer[len]=0; + if (sizeof(TCHAR) <2) { + strncpy((char *)buffer,g_status_ptrs[5],len); + } else { + mbstowcs((wchar_t *)buffer,g_status_ptrs[5],len); + } + SendMessageA(g_win_status, SB_SETTEXT,4,(LPARAM) buffer); + } else { + + } + } +} + + +void +x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, + int width, int height) +{ + + void *bitm_old; + POINT point; + RECT wrect; + int toolbar_height= 0; + int center_x=0; + int center_y=0; + + point.x = 0; + point.y = 0; + ClientToScreen(g_hwnd_main, &point); + bitm_old = SelectObject(g_main_cdc, kimage_ptr->dev_handle); + + + + if (!g_win_fullscreen_state) { + toolbar_height= + (g_win_toolbar_height.bottom-g_win_toolbar_height.top); + } else { + GetWindowRect(g_hwnd_main,&wrect); + center_x=(wrect.right-wrect.left)-X_A2_WINDOW_WIDTH; + center_x=center_x/2; + center_y=(wrect.bottom-wrect.top)-X_A2_WINDOW_HEIGHT; + if (g_win_status_debug) { + center_y-=(MAX_STATUS_LINES*16); + } + center_y=center_y/2; + } + + BitBlt( g_main_dc, + destx+center_x, + desty+toolbar_height+center_y, + width, height, + g_main_cdc, srcx, srcy, SRCCOPY); + + SelectObject(g_main_cdc, bitm_old); + +} + +void +x_push_done() +{ +} + +void +x_auto_repeat_on(int must) +{ +} + +void +x_auto_repeat_off(int must) +{ +} + +void +x_hide_pointer(int do_hide) +{ + if(do_hide) { + ShowCursor(0); + } else { + ShowCursor(1); + } +} + +void +x_full_screen(int do_full) +{ + DEVMODE dmScreenSettings; + int style; + + if (do_full && !g_win_fullscreen_state) { + dmScreenSettings.dmSize=sizeof(dmScreenSettings); + dmScreenSettings.dmPelsWidth = 800; + dmScreenSettings.dmPelsHeight = 600; + dmScreenSettings.dmBitsPerPel = 24; + dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH| + DM_PELSHEIGHT; + + if (ChangeDisplaySettings(&dmScreenSettings, 2) + !=DISP_CHANGE_SUCCESSFUL) { + // If 24-bit palette does not work, try 32-bit + dmScreenSettings.dmBitsPerPel = 32; + if (ChangeDisplaySettings(&dmScreenSettings, 2)) { + printf ( + "-- Unable to switch to fullscreen mode\n"); + printf ( + "-- No 24-bit or 32-bit mode for fullscreen\n"); + dmScreenSettings.dmBitsPerPel=-1; + } + } + + if (dmScreenSettings.dmBitsPerPel >0) { + g_win_fullscreen_state=!g_win_fullscreen_state; + GetWindowRect(g_hwnd_main,&g_main_window_saved_rect); + ChangeDisplaySettings(&dmScreenSettings, 4); + style=GetWindowLong(g_hwnd_main,GWL_STYLE); + style &= ~WS_CAPTION; + SetWindowLong(g_hwnd_main,GWL_STYLE,style); + g_main_window_menu_saved=GetMenu(g_hwnd_main); + SetMenu(g_hwnd_main,NULL); + ShowWindow(g_win_status,FALSE); + ShowWindow(g_win_toolbar,FALSE); + SetWindowPos(g_hwnd_main,HWND_TOPMOST,0,0, + GetSystemMetrics(SM_CXSCREEN), + GetSystemMetrics(SM_CYSCREEN), + SWP_SHOWWINDOW); + + } + } else { + if (g_win_fullscreen_state) { + ChangeDisplaySettings(NULL,0); + style=GetWindowLong(g_hwnd_main,GWL_STYLE); + style |= WS_CAPTION; + SetWindowLong(g_hwnd_main,GWL_STYLE,style); + SetMenu(g_hwnd_main,g_main_window_menu_saved); + ShowWindow(g_win_status,TRUE); + ShowWindow(g_win_toolbar,TRUE); + init_window(g_hwnd_main,FALSE); + g_win_fullscreen_state=!g_win_fullscreen_state; + } + } + return; +} diff --git a/src/winresource.h b/src/winresource.h index 64e56e3..7d5d615 100644 --- a/src/winresource.h +++ b/src/winresource.h @@ -3,11 +3,13 @@ // Microsoft Developer Studio generated include file. // Used by win32.rc // +#define CREATEPROCESS_MANIFEST 1 #define IDD_ABOUT_DIALOG 101 #define IDC_KEGS32 102 #define IDR_TOOLBAR 103 #define IDD_DLG_DISKCONF 104 #define IDR_ACCEL 105 +#define IDD_KEGS32_KEY 106 #define ID_TOOLBAR 5000 #define ID_STATUSBAR 5001 #define IDC_EDIT_S5D1 10051 @@ -29,6 +31,20 @@ #define ID_FILE_JOYSTICK 40005 #define ID_FILE_DEBUGSTAT 40006 #define ID_FILE_FULLSCREEN 40012 +#define ID_FILE_SPEED 40013 +#define ID_HELP_KEY 40014 +#define ID_FILE_SENDREBOOT 40007 +#define ID_FILE_FLUSHPRINTER 40017 +#define ID_SPEED_1MHZ 50001 +#define ID_SPEED_2MHZ 50002 +#define ID_SPEED_8MHZ 50003 +#define ID_SPEED_FMHZ 50004 +#define IDD_SPEEDDIALOG 117 +#define IDC_SLOW 1007 +#define IDC_CUSTOM 1008 +#define IDC_EDITCUSTOM 1009 +#define IDC_NORMAL 1010 +#define IDC_FASTEST 1011 // Next default values for new objects // diff --git a/src/wintoolbar.bmp b/src/wintoolbar.bmp new file mode 100644 index 0000000000000000000000000000000000000000..291ba38b97de6b29a23c51c992179a3b8c88f277 GIT binary patch literal 758 zcma)2u}TCn5RD?_KoCE}Qiz>Jz}~}8IZzxHmLp&z2woZT9X1ECSWrk|{^Obht!xf1 zzGQMLYB+ay=4EEyyqWCT`TMRY&o%rJ>;iTO+Yx*4LKxJa57HM4=u*L1RE4>G}HYj^}>yvOsRhyhyP}pI+ULQY!c0Vnq7;+yF&P$p2X>g+>;E k0PFnFyG*MGnOSx1KJT69ZFqG*QtG&5t6uBAm9rH-0A@;PKL7v# literal 0 HcmV?d00001