david 15 gadi atpakaļ
vecāks
revīzija
770c49a5f5
100 mainītis faili ar 31299 papildinājumiem un 0 dzēšanām
  1. 173 0
      ucon64/2.0/src/GoodCodes.txt
  2. 380 0
      ucon64/2.0/src/Makefile.in
  3. 380 0
      ucon64/2.0/src/Makefile.orig
  4. 223 0
      ucon64/2.0/src/Makefile.vc6
  5. 118 0
      ucon64/2.0/src/backup/NTSC-PAL notes.txt
  6. 419 0
      ucon64/2.0/src/backup/SWC-compatibility.txt
  7. 62 0
      ucon64/2.0/src/backup/backup.h
  8. 768 0
      ucon64/2.0/src/backup/cartlib.c
  9. 504 0
      ucon64/2.0/src/backup/cd64.c
  10. 41 0
      ucon64/2.0/src/backup/cd64.h
  11. 985 0
      ucon64/2.0/src/backup/cmc.c
  12. 32 0
      ucon64/2.0/src/backup/cmc.h
  13. 139 0
      ucon64/2.0/src/backup/dex.c
  14. 34 0
      ucon64/2.0/src/backup/dex.h
  15. 399 0
      ucon64/2.0/src/backup/doctor64.c
  16. 31 0
      ucon64/2.0/src/backup/doctor64.h
  17. 587 0
      ucon64/2.0/src/backup/doctor64jr.c
  18. 31 0
      ucon64/2.0/src/backup/doctor64jr.h
  19. 1637 0
      ucon64/2.0/src/backup/f2a.c
  20. 34 0
      ucon64/2.0/src/backup/f2a.h
  21. 1671 0
      ucon64/2.0/src/backup/fal.c
  22. 35 0
      ucon64/2.0/src/backup/fal.h
  23. 316 0
      ucon64/2.0/src/backup/ffe.c
  24. 80 0
      ucon64/2.0/src/backup/ffe.h
  25. 739 0
      ucon64/2.0/src/backup/fig.c
  26. 100 0
      ucon64/2.0/src/backup/fig.h
  27. 1991 0
      ucon64/2.0/src/backup/gbx.c
  28. 34 0
      ucon64/2.0/src/backup/gbx.h
  29. 1144 0
      ucon64/2.0/src/backup/gd.c
  30. 49 0
      ucon64/2.0/src/backup/gd.h
  31. 46 0
      ucon64/2.0/src/backup/interceptor.c
  32. 31 0
      ucon64/2.0/src/backup/interceptor.h
  33. 169 0
      ucon64/2.0/src/backup/libcd64/Makefile
  34. 62 0
      ucon64/2.0/src/backup/libcd64/Makefile.vc6
  35. 28 0
      ucon64/2.0/src/backup/libcd64/cd64.def
  36. 1005 0
      ucon64/2.0/src/backup/libcd64/cd64io.c
  37. 114 0
      ucon64/2.0/src/backup/libcd64/cd64io.h
  38. 990 0
      ucon64/2.0/src/backup/libcd64/cd64lib.c
  39. 13 0
      ucon64/2.0/src/backup/libcd64/ultra64/cartmem.h
  40. 8 0
      ucon64/2.0/src/backup/libcd64/ultra64/host/cartinfo.h
  41. 189 0
      ucon64/2.0/src/backup/libcd64/ultra64/host/cd64lib.h
  42. 60 0
      ucon64/2.0/src/backup/libcd64/ultra64/rom.h
  43. 1055 0
      ucon64/2.0/src/backup/lynxit.c
  44. 31 0
      ucon64/2.0/src/backup/lynxit.h
  45. 158 0
      ucon64/2.0/src/backup/mccl.c
  46. 30 0
      ucon64/2.0/src/backup/mccl.h
  47. BIN
      ucon64/2.0/src/backup/mccl.png
  48. 144 0
      ucon64/2.0/src/backup/mcd.c
  49. 30 0
      ucon64/2.0/src/backup/mcd.h
  50. 395 0
      ucon64/2.0/src/backup/md-pro.c
  51. 37 0
      ucon64/2.0/src/backup/md-pro.h
  52. 466 0
      ucon64/2.0/src/backup/mgd.c
  53. 185 0
      ucon64/2.0/src/backup/mgd.h
  54. 250 0
      ucon64/2.0/src/backup/msg.c
  55. 46 0
      ucon64/2.0/src/backup/msg.h
  56. 204 0
      ucon64/2.0/src/backup/pce-pro.c
  57. 35 0
      ucon64/2.0/src/backup/pce-pro.h
  58. 782 0
      ucon64/2.0/src/backup/pl.c
  59. 33 0
      ucon64/2.0/src/backup/pl.h
  60. 1068 0
      ucon64/2.0/src/backup/psxpblib.c
  61. 208 0
      ucon64/2.0/src/backup/psxpblib.h
  62. 371 0
      ucon64/2.0/src/backup/sflash.c
  63. 33 0
      ucon64/2.0/src/backup/sflash.h
  64. 407 0
      ucon64/2.0/src/backup/smc.c
  65. 47 0
      ucon64/2.0/src/backup/smc.h
  66. 343 0
      ucon64/2.0/src/backup/smd.c
  67. 53 0
      ucon64/2.0/src/backup/smd.h
  68. 348 0
      ucon64/2.0/src/backup/smsgg-pro.c
  69. 37 0
      ucon64/2.0/src/backup/smsgg-pro.h
  70. 46 0
      ucon64/2.0/src/backup/ssc.c
  71. 34 0
      ucon64/2.0/src/backup/ssc.h
  72. 1358 0
      ucon64/2.0/src/backup/swc.c
  73. 72 0
      ucon64/2.0/src/backup/swc.h
  74. 428 0
      ucon64/2.0/src/backup/tototek.c
  75. 47 0
      ucon64/2.0/src/backup/tototek.h
  76. 45 0
      ucon64/2.0/src/backup/ufo.c
  77. 144 0
      ucon64/2.0/src/backup/ufo.h
  78. 46 0
      ucon64/2.0/src/backup/yoko.c
  79. 26 0
      ucon64/2.0/src/backup/yoko.h
  80. 46 0
      ucon64/2.0/src/backup/z64.c
  81. 26 0
      ucon64/2.0/src/backup/z64.h
  82. 128 0
      ucon64/2.0/src/config.h.in
  83. 129 0
      ucon64/2.0/src/config.h.orig
  84. 129 0
      ucon64/2.0/src/config.h.vc6
  85. 312 0
      ucon64/2.0/src/configure
  86. 183 0
      ucon64/2.0/src/configure.in
  87. 65 0
      ucon64/2.0/src/console/boardnames
  88. 782 0
      ucon64/2.0/src/console/boardtable.txt
  89. 38 0
      ucon64/2.0/src/console/console.h
  90. 646 0
      ucon64/2.0/src/console/dc.c
  91. 29 0
      ucon64/2.0/src/console/dc.h
  92. 703 0
      ucon64/2.0/src/console/gb.c
  93. 39 0
      ucon64/2.0/src/console/gb.h
  94. 850 0
      ucon64/2.0/src/console/gba.c
  95. 37 0
      ucon64/2.0/src/console/gba.h
  96. 1728 0
      ucon64/2.0/src/console/genesis.c
  97. 43 0
      ucon64/2.0/src/console/genesis.h
  98. 109 0
      ucon64/2.0/src/console/jaguar.c
  99. 27 0
      ucon64/2.0/src/console/jaguar.h
  100. 357 0
      ucon64/2.0/src/console/lynx.c

+ 173 - 0
ucon64/2.0/src/GoodCodes.txt

@@ -0,0 +1,173 @@
+               ..................
+...............: STANDARD CODES ::...............
+:                                               :\
+:   [a] Alternate        [p] Pirate             :\
+:   [b] Bad Dump         [t] Trained            :\
+:   [f] Fixed            [T-] OldTranslation    :\
+:                        [T+] NewerTranslation  :\
+:   [h] Hack             (-) Unknown Year       :\
+:   [o] Overdump         [!] Verified Good Dump :\
+:  (M#) Multilanguage (# of Languages)          :\
+: (###) Checksum       (??k) ROM Size           :\
+:  ZZZ_ Unclassified   (Unl) Unlicensed		:\
+:...............................................:\
+ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+
+                .................
+................: SPECIAL CODES ::...............
+:                                               :\
+: .-----Gameboy-----.  .----Super Nintendo----. :\
+: [  [C] Color      ]  [ (BS) BS ROMs         ] :\
+: [  [S] Super      ]  [ (ST) Sufami Turbo    ] :\
+: [ [BF] Bung Fix   ]  [ (NP) Nintendo Power  ] :\
+: `-----------------'  `----------------------' :\
+:                      .--------Atari---------. :\
+: .-----Genesis-----.  [ (PAL) Euro Version   ] :\
+: [ (1) Japan       ]  `----------------------' :\
+: [ (4) USA         ]  .---------GBA----------. :\
+: [ (5) NTSC Only   ]  [ [hI??] Intro hacks   ] :\
+: [ (8) PAL Only    ]  `----------------------' :\
+: [ (B) non USA     ]  .--------Coleco--------. :\
+: [ [c] Checksum    ]  [ (Adam) ADAM Version  ] :\
+: [ [x] Bad Checksum]  `----------------------' :\
+: [ [R-] Countries  ]                           :\
+: `-----------------'                           :\
+:                      .--------NES/FC--------. :\
+: .--NeoGeo Pocket--.  [ (PC10) PlayChoice 10 ] :\
+: [ [M] Mono Only   ]  [   (VS) Versus        ] :\
+: `-----------------'  [ [hFFE] FFE Copier fmt] :\
+:                      `----------------------' :\
+:...............................................:\
+ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+
+                .................
+................: COUNTRY CODES ::...............
+:                                               :\
+:   (1) Japan & Korea      (4) USA & BrazilNTSC :\
+:   (A) Australia          (J) Japan            :\
+:   (B) non USA (Genesis)  (K) Korea            :\
+:   (C) China             (NL) Netherlands      :\
+:   (E) Europe            (PD) Public Domain    :\
+:   (F) France             (S) Spain            :\
+:   (F) World (Genesis)                         :\
+:  (FC) French Canadian   (SW) Sweden           :\
+:  (FN) Finland            (U) USA              :\
+:   (G) Germany           (UK) England          :\
+:  (GR) Greece           (Unk) Unknown Country  :\
+:  (HK) Hong Kong          (I) Italy		:\
+:  (H)  Holland          (Unl) Unlicensed       :\
+:...............................................:\
+ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+
+             .......................
+.............: STANDARD CODE NOTES ::............
+:                                               :\
+: [a] This is simply an alternate version of a  :\
+:     ROM. Many games have been re-released to  :\
+:     fix bugs or even to eliminate Game Genie  :\
+:     codes (Yes, Nintendo hates that device).  :\
+:             -------------------               :\
+: [b] A bad dump often occurs with an older     :\
+:     game or a faulty dumper (bad connection). :\
+:     Another common source of [b] ROMs is a    :\
+:     corrupted upload to a release FTP.        :\
+:             -------------------               :\
+: [f] A fixed game has been altered in some way :\
+:     so that it will run better on a copier    :\
+:     or emulator.                              :\
+:             -------------------               :\
+: [h] Something in this ROM is not quite as it  :\
+:     should be. Often a hacked ROM simply has  :\
+:     a changed header or has been enabled to   :\
+:     run in different regions. Other times it  :\
+:     could be a release group intro, or just   :\
+:     some kind of cheating or funny hack.      :\
+:             -------------------               :\
+: [o] An overdumped ROM image has more data     :\
+:     than is actually in the cart. The extra   :\
+:     information means nothing and is removed  :\
+:     from the true image.                      :\
+:             -------------------               :\
+: [t] A trainer is special code which executes  :\
+:     before the game is begun. It allows you   :\
+:     to access cheats from a menu.             :\
+:             -------------------               :\
+: [!] Verified good dump. Thank God for these!  :\
+:...............................................:\
+ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+
+             ......................
+.............: SPECIAL CODE NOTES ::.............
+:                                               :\
+: **** SNES ****                                :\
+: (BS) These Japanese ROMs were distributed     :\
+:      through a satellite system in Japan      :\
+:      known as the Broadcast Satellaview.      :\
+:      They were transmitted along with a TV    :\
+:      show which was connected to the game in  :\
+:      some way. These games were only playable :\
+:      during the show, and thus stop after an  :\
+:      hour, and many were timed so that only   :\
+:      certain time periods were playable.      :\
+:             -------------------               :\
+: (ST) The Sufami Turbo device allowed two      :\
+:      GameBoy sized carts to be plugged into   :\
+:      the SNES. Certain carts combined into    :\
+:      new games much like the Sonic & Knuckles :\
+:      lock-on technology by Sega.              :\
+:             -------------------               :\
+: (NP) Nintendo Power has been known to release :\
+:      games only available to its subscribers. :\
+:      Most of these ROMs are Japanese, as this :\
+:      practice occured mainly in Japan.        :\
+:             -------------------               :\
+:                                               :\
+: **** Genesis ****                             :\
+:  (1) Carts with this code will run on both    :\
+:      Japanese and Korean machines.            :\
+:             -------------------               :\
+:  (4) While this code is technically the same  :\
+:      as a (U) code, it is a newer header      :\
+:      format and represents that the cart will :\
+:      run on USA and Brazil NTSC machines.     :\
+:             -------------------               :\
+:  (B) This country code indicates that the     :\
+:      cart will run on any non US machine.     :\
+:             -------------------               :\
+:  [c] This code represents a cart with known   :\
+:      faulty checksum routines.                :\
+:             -------------------               :\
+:                                               :\
+: **** GameBoy ****                             :\
+: [BF] Bung released a programmable cartridge   :\
+:      compatable with the GameBoy which could  :\
+:      hold any data you wished to play.        :\
+:      However, many games do not function on   :\
+:      Bung v1.0 carts and have to be 'fixed.'  :\
+:             -------------------               :\
+:                                               :\
+: **** Nintendo ****                            :\
+: PC10 The PlayChoice 10 was an arcade unit     :\
+:      which played exact copies of NES games   :\
+:      in an arcade cabinet. The machines had a :\
+:      choice of 10 games to choose from and    :\
+:      ran for about 3 minutes on 25 cents.     :\
+:             -------------------               :\
+:                                               :\
+:   VS The Versus system ran on similar hard-   :\
+:      ware to the PC10 machines, but simply    :\
+:      allowed you to play against each other.  :\
+:...............................................:\
+ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+
+                   ...........
+...................: Credits ::..................
+:                                               :\
+: Document written by Psych0phobiA / q^-o|o-^p  :\
+:                                               :\
+: All codes developed by Cowering for the       :\
+: Goodxxxx series ROM file renaming utilities.  :\
+:                                               :\
+: Visit #rareroms on NewNet in IRC!             :\ 
+:...............................................:\
+ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

+ 380 - 0
ucon64/2.0/src/Makefile.in

@@ -0,0 +1,380 @@
+.PHONY: all clean distclean install uninstall
+
+@DEFINE_DLOPEN_MAKE@
+@DEFINE_ZLIB_MAKE@
+@DEFINE_DISCMAGE_MAKE@
+@DEFINE_USB_MAKE@
+@DEFINE_LIBCD64_MAKE@
+
+CC=@CC@
+CFLAGS=-I. -Wall -W -O3 @DEFS@
+LDFLAGS=-s
+TARGETS=
+
+ifdef USE_DISCMAGE
+LIBNAME_DM=discmage
+endif
+
+# The test for Cygwin should be done before the test for DJGPP, because the
+# environment variable DJGPP can be set under Bash for people who have
+# installed both GCC (and friends) ports.
+
+GCC_WIN=0
+# test cygwin before DJGPP; OSTYPE is not exported on Cygwin
+ifeq ($(TERM),cygwin)
+GCC_WIN=1
+endif
+# test msys before DJGPP; MSYS, MinGW's POSIX build environment
+ifeq ($(OSTYPE),msys)
+GCC_WIN=1
+endif
+
+ifeq ($(GCC_WIN),1)
+
+ifdef USE_DISCMAGE
+FULLLIBNAME_DM=$(LIBNAME_DM).dll
+ifndef DLOPEN
+LDFLAGS+=libdiscmage/$(LIBNAME_DM).a
+endif
+endif
+
+else
+ifdef DJGPP
+
+ifdef USE_DISCMAGE
+FULLLIBNAME_DM=$(LIBNAME_DM).dxe
+ifndef DLOPEN
+LDFLAGS+=libdiscmage/$(LIBNAME_DM).a
+endif
+endif
+
+else                                    # Unix, BeOS or Mac OS X (Darwin)
+
+ifeq ($(findstring openbsd,$(OSTYPE)),openbsd) # for example "openbsd3.4"
+# i386_iopl() is located in libi386.a
+LDFLAGS+=@LIBI386_MAKE@
+endif
+
+ifdef USE_DISCMAGE
+ifeq ($(findstring darwin,$(OSTYPE)),darwin) # for example "darwin7.0"
+FULLLIBNAME_DM=$(LIBNAME_DM).dylib
+else
+FULLLIBNAME_DM=$(LIBNAME_DM).so
+endif
+endif
+
+ifdef DLOPEN
+
+ifneq ($(OSTYPE),beos)
+ifeq ($(findstring freebsd,$(OSTYPE)),) # false if OSTYPE contains "freebsd"
+ifeq ($(findstring openbsd,$(OSTYPE)),) # false if OSTYPE contains "openbsd"
+LDFLAGS+=-ldl
+endif
+endif
+endif
+
+else                                    # DLOPEN
+ifdef USE_DISCMAGE                      # GNU specific: "simply expanded variable"
+FULLLIBNAME_DM:=$(addprefix lib,$(FULLLIBNAME_DM))
+LDFLAGS+=-Llibdiscmage -l$(LIBNAME_DM)
+endif
+
+endif
+
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+
+TARGETS+=libdiscmage/$(FULLLIBNAME_DM)
+
+
+ifdef USE_LIBCD64
+LDFLAGS+=backup/libcd64/libcd64.a
+TARGETS+=backup/libcd64/libcd64.a
+endif
+
+
+OBJECTS=ucon64.o ucon64_dat.o ucon64_misc.o ucon64_opts.o \
+        misc/chksum.o misc/file.o misc/getopt.o misc/getopt2.o \
+        misc/misc.o misc/parallel.o misc/property.o misc/string.o \
+        patch/aps.o patch/bsl.o patch/gg.o patch/ips.o patch/pal4u.o \
+        patch/ppf.o patch/xps.o \
+        console/dc.o console/gb.o console/gba.o console/genesis.o \
+        console/jaguar.o console/lynx.o console/n64.o console/neogeo.o \
+        console/nes.o console/ngp.o console/pce.o console/psx.o console/sms.o \
+        console/snes.o console/swan.o \
+        backup/cd64.o backup/cmc.o backup/dex.o backup/doctor64.o \
+        backup/doctor64jr.o backup/f2a.o backup/fal.o backup/ffe.o \
+        backup/fig.o backup/gbx.o backup/gd.o backup/interceptor.o \
+        backup/lynxit.o backup/mccl.o backup/mcd.o backup/md-pro.o \
+        backup/mgd.o backup/msg.o backup/pce-pro.o backup/pl.o \
+        backup/psxpblib.o backup/sflash.o backup/smc.o backup/smd.o \
+        backup/smsgg-pro.o backup/ssc.o backup/swc.o backup/tototek.o \
+        backup/ufo.o backup/yoko.o backup/z64.o
+ifdef USE_ZLIB
+LDFLAGS+=-lz
+OBJECTS+=misc/archive.o misc/map.o misc/unzip.o
+endif
+ifdef USE_USB
+LDFLAGS+=-lusb
+OBJECTS+=misc/usb.o
+endif
+
+ifdef DLOPEN
+OBJECTS+=misc/dlopen.o
+ifndef USE_ZLIB
+ifeq ($(GCC_WIN),1)
+else
+ifdef DJGPP                             # DJGPP code in dlopen needs map code
+OBJECTS+=misc/map.o
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+endif                                   # USE_ZLIB
+else
+ifeq ($(GCC_WIN),1)                     # Cygwin/MinGW code in ucon64_misc needs dlopen code
+OBJECTS+=misc/dlopen.o
+endif                                   # GCC_WIN
+endif                                   # DLOPEN
+
+
+TARGET=ucon64
+ifeq ($(GCC_WIN),1)
+TARGET:=$(addsuffix .exe,$(TARGET))     # adding .exe avoids "problems" with Cygwin/MinGW
+else
+ifdef DJGPP                             # OSTYPE is not defined by default under DOS
+TARGET:=$(addsuffix .exe,$(TARGET))
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+TARGETS+=$(TARGET)
+
+all: $(TARGETS)
+
+
+CLEAN_CMD=rm -f $(TARGET) $(OBJECTS) *.core *.stackdump *~ */*~ */*/*~; \
+cd libdiscmage && $(MAKE) clean; \
+cd ../backup/libcd64 && $(MAKE) clean
+
+clean:
+ifeq ($(GCC_WIN),1)
+	$(CLEAN_CMD)
+else
+ifdef DJGPP
+	del *.o
+	del patch\*.o
+	del console\*.o
+	del backup\*.o
+	del misc\*.o
+	del $(TARGET)
+	cd libdiscmage
+	$(MAKE) clean
+	cd ../backup/libcd64
+	$(MAKE) clean
+	cd ../..
+else                                    # Unix, BeOS or Mac OS X (Darwin)
+	$(CLEAN_CMD)
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+
+
+DISTCLEAN_CMD=rm -f Makefile config.log config.status config.cache config.h; \
+cd libdiscmage && $(MAKE) distclean; \
+cd ../backup/libcd64 && $(MAKE) clean
+# libcd64 Makefile has no distclean target
+
+distclean: clean
+ifeq ($(GCC_WIN),1)
+	$(DISTCLEAN_CMD)
+else
+ifdef DJGPP
+	del Makefile
+	del config.log
+	del config.status
+	del config.cache
+	del config.h
+	cd libdiscmage
+	$(MAKE) distclean
+	cd ../backup/libcd64
+	$(MAKE) clean
+	cd ../..
+else
+	$(DISTCLEAN_CMD)
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+
+
+.c.o:
+	$(CC) $(CFLAGS) -c $< -o $@
+
+backup/cd64.o: backup/cd64.c
+	$(CC) $(CFLAGS) -Ibackup/libcd64 -c $< -o $@
+
+
+ifdef USE_DISCMAGE
+libdiscmage/$(FULLLIBNAME_DM):
+ifeq ($(GCC_WIN),1)
+	cd libdiscmage && $(MAKE)
+else
+ifdef DJGPP
+	cd libdiscmage
+	$(MAKE)
+	cd ..
+else
+	cd libdiscmage && $(MAKE)
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+endif                                   # USE_DISCMAGE
+
+ifdef USE_LIBCD64
+backup/libcd64/libcd64.a:
+ifeq ($(GCC_WIN),1)
+	cd backup/libcd64 && $(MAKE)
+else
+ifdef DJGPP
+	cd backup/libcd64
+	$(MAKE)
+	cd ../..
+else
+	cd backup/libcd64 && $(MAKE)
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+endif                                   # USE_DISCMAGE
+
+$(TARGET): $(OBJECTS)
+	$(CC) $(OBJECTS) $(LDFLAGS) -o $@
+
+
+install:
+ifeq ($(TERM),cygwin)                   # test cygwin before DJGPP
+else
+ifeq ($(OSTYPE),msys)                   # test msys before DJGPP
+else
+ifdef DJGPP
+else
+ifeq ($(OSTYPE),beos)
+	./install_beos.sh
+else
+	./install.sh
+endif                                   # beos
+endif                                   # DJGPP
+endif                                   # msys
+endif                                   # cygwin
+ifndef DLOPEN
+	cd libdiscmage && $(MAKE) install
+endif
+
+
+uninstall:
+ifeq ($(TERM),cygwin)                   # test cygwin before DJGPP
+else
+ifeq ($(OSTYPE),msys)                   # test msys before DJGPP
+else
+ifdef DJGPP
+else
+ifeq ($(OSTYPE),beos)
+	rm -f $(HOME)/config/bin/$(TARGET)
+else
+	rm -f /usr/local/bin/$(TARGET)
+endif                                   # beos
+endif                                   # DJGPP
+endif                                   # msys
+endif                                   # cygwin
+ifndef DLOPEN
+	cd libdiscmage && $(MAKE) uninstall
+endif
+
+
+# Dependencies
+
+# Most source files include these
+UCON64_STD_H=ucon64.h ucon64_misc.h misc/misc.h config.h ucon64_defines.h
+
+misc/archive.o: misc/archive.h misc/map.h config.h
+misc/chksum.o: misc/chksum.h config.h
+misc/dlopen.o: misc/dlopen.h misc/dxedll_pub.h config.h
+misc/getopt.o: misc/getopt.h
+misc/map.o: misc/map.h config.h
+misc/misc.o: misc/misc.h misc/archive.h config.h
+misc/parallel.o: misc/parallel.h config.h
+misc/usb.o: misc/usb.h config.h
+misc/unzip.o: misc/unzip.h config.h
+ucon64.o: misc/dlopen.h misc/getopt.h ucon64_dat.h ucon64_opts.h \
+          console/dc.h console/gb.h console/gba.h console/genesis.h \
+          console/jaguar.h console/lynx.h console/n64.h console/neogeo.h \
+          console/nes.h console/ngp.h console/pce.h console/psx.h console/sms.h \
+          console/snes.h console/swan.h \
+          backup/cd64.h backup/dex.h backup/doctor64.h backup/doctor64jr.h \
+          backup/f2a.h backup/fal.h backup/ffe.h backup/fig.h backup/gbx.h \
+          backup/gd.h backup/interceptor.h backup/lynxit.h backup/mccl.h \
+          backup/mcd.h backup/md-pro.h backup/mgd.h backup/msg.h \
+          backup/pce-pro.h backup/pl.h backup/smc.h backup/smd.h \
+          backup/smsgg-pro.h backup/ssc.h backup/swc.h backup/tototek.h \
+          backup/ufo.h backup/yoko.h backup/z64.h \
+          patch/aps.h patch/bsl.h patch/gg.h patch/ips.h patch/pal4u.h \
+          patch/ppf.h patch/xps.h $(UCON64_STD_H)
+ucon64_dat.o: ucon64_dat.h $(UCON64_STD_H)
+ucon64_misc.o: misc/dlopen.h $(UCON64_STD_H)
+ucon64_opts.o: misc/dlopen.h misc/getopt.h ucon64_dat.h ucon64_opts.h \
+               console/dc.h console/gb.h console/gba.h console/genesis.h \
+               console/jaguar.h console/lynx.h console/n64.h console/neogeo.h \
+               console/nes.h console/ngp.h console/pce.h console/psx.h \
+               console/sms.h console/snes.h console/swan.h \
+               backup/cd64.h backup/dex.h backup/doctor64.h \
+               backup/doctor64jr.h backup/f2a.h backup/fal.h backup/ffe.h \
+               backup/fig.h backup/gbx.h backup/gd.h backup/interceptor.h \
+               backup/lynxit.h backup/mccl.h backup/mcd.h backup/md-pro.h \
+               backup/mgd.h backup/msg.h backup/pce-pro.h backup/pl.h \
+               backup/smc.h backup/smd.h backup/smsgg-pro.h backup/ssc.h \
+               backup/swc.h backup/tototek.h backup/ufo.h backup/yoko.h \
+               backup/z64.h \
+               patch/aps.h patch/bsl.h patch/gg.h patch/ips.h patch/pal4u.h \
+               patch/ppf.h patch/xps.h $(UCON64_STD_H)
+console/dc.o: console/dc.h $(UCON64_STD_H)
+console/gb.o: console/gb.h backup/mgd.h $(UCON64_STD_H)
+console/gba.o: console/gba.h $(UCON64_STD_H)
+console/genesis.o: console/genesis.h backup/smd.h backup/mgd.h $(UCON64_STD_H)
+console/jaguar.o: console/jaguar.h $(UCON64_STD_H)
+console/lynx.o: console/lynx.h $(UCON64_STD_H)
+console/n64.o: console/n64.h $(UCON64_STD_H)
+console/neogeo.o: console/neogeo.h $(UCON64_STD_H)
+console/nes.o: console/nes.h $(UCON64_STD_H)
+console/ngp.o: console/ngp.h $(UCON64_STD_H)
+console/pce.o: console/pce.h backup/mgd.h $(UCON64_STD_H)
+console/psx.o: console/psx.h $(UCON64_STD_H)
+console/sms.o: console/sms.h backup/smd.h backup/mgd.h $(UCON64_STD_H)
+console/snes.o: console/snes.h backup/mgd.h $(UCON64_STD_H)
+console/swan.o: console/swan.h $(UCON64_STD_H)
+backup/cd64.o: backup/cd64.h $(UCON64_STD_H)
+backup/cmc.o: backup/cmc.h $(UCON64_STD_H)
+backup/dex.o: backup/dex.h backup/psxpblib.h $(UCON64_STD_H)
+backup/doctor64.o: backup/doctor64.h $(UCON64_STD_H)
+backup/doctor64jr.o: backup/doctor64jr.h $(UCON64_STD_H)
+backup/f2a.o: backup/f2a.h $(UCON64_STD_H)
+backup/fal.o: backup/fal.h backup/cartlib.c $(UCON64_STD_H)
+backup/ffe.o: backup/ffe.h $(UCON64_STD_H)
+backup/fig.o: backup/fig.h console/snes.h $(UCON64_STD_H)
+backup/gd.o: backup/gd.h console/snes.h $(UCON64_STD_H)
+backup/gbx.o: backup/gbx.h $(UCON64_STD_H)
+backup/lynxit.o: backup/lynxit.h $(UCON64_STD_H)
+backup/mccl.o: backup/mccl.h $(UCON64_STD_H)
+backup/mcd.o: backup/mcd.h $(UCON64_STD_H)
+backup/md-pro.o: backup/md-pro.h backup/tototek.h $(UCON64_STD_H)
+backup/mgd.o: backup/mgd.h $(UCON64_STD_H)
+backup/msg.o: backup/msg.h backup/ffe.h $(UCON64_STD_H)
+backup/pce-pro.o: backup/pce-pro.h backup/tototek.h $(UCON64_STD_H)
+backup/pl.o: backup/pl.h $(UCON64_STD_H)
+backup/psxpblib.o: backup/psxpblib.h $(UCON64_STD_H)
+backup/sflash.o: backup/sflash.h backup/tototek.h $(UCON64_STD_H)
+backup/smc.o: backup/smc.h backup/ffe.h $(UCON64_STD_H)
+backup/smd.o: backup/smd.h backup/ffe.h $(UCON64_STD_H)
+backup/smsgg-pro.o: backup/smsgg-pro.h backup/tototek.h $(UCON64_STD_H)
+backup/swc.o: backup/swc.h backup/ffe.h console/snes.h $(UCON64_STD_H)
+backup/tototek.o: backup/tototek.h $(UCON64_STD_H)
+backup/ufo.o: backup/ufo.h $(UCON64_STD_H)
+backup/yoko.o: backup/yoko.h $(UCON64_STD_H)
+backup/z64.o: backup/z64.h $(UCON64_STD_H)
+patch/aps.o: patch/aps.h $(UCON64_STD_H)
+patch/bsl.o: patch/bsl.h $(UCON64_STD_H)
+patch/gg.o: patch/gg.h $(UCON64_STD_H)
+patch/ips.o: patch/ips.h $(UCON64_STD_H)
+patch/pal4u.o: patch/pal4u.h $(UCON64_STD_H)
+patch/ppf.o: patch/ppf.h $(UCON64_STD_H)
+patch/xps.o: patch/xps.h $(UCON64_STD_H)

+ 380 - 0
ucon64/2.0/src/Makefile.orig

@@ -0,0 +1,380 @@
+.PHONY: all clean distclean install uninstall
+
+DLOPEN=1
+#USE_ZLIB=1
+USE_DISCMAGE=1
+#USE_USB=1
+USE_LIBCD64=1
+
+CC=gcc
+CFLAGS=-I. -Wall -W -O3 -DHAVE_CONFIG_H
+LDFLAGS=-s
+TARGETS=
+
+ifdef USE_DISCMAGE
+LIBNAME_DM=discmage
+endif
+
+# The test for Cygwin should be done before the test for DJGPP, because the
+# environment variable DJGPP can be set under Bash for people who have
+# installed both GCC (and friends) ports.
+
+GCC_WIN=0
+# test cygwin before DJGPP; OSTYPE is not exported on Cygwin
+ifeq ($(TERM),cygwin)
+GCC_WIN=1
+endif
+# test msys before DJGPP; MSYS, MinGW's POSIX build environment
+ifeq ($(OSTYPE),msys)
+GCC_WIN=1
+endif
+
+ifeq ($(GCC_WIN),1)
+
+ifdef USE_DISCMAGE
+FULLLIBNAME_DM=$(LIBNAME_DM).dll
+ifndef DLOPEN
+LDFLAGS+=libdiscmage/$(LIBNAME_DM).a
+endif
+endif
+
+else
+ifdef DJGPP
+
+ifdef USE_DISCMAGE
+FULLLIBNAME_DM=$(LIBNAME_DM).dxe
+ifndef DLOPEN
+LDFLAGS+=libdiscmage/$(LIBNAME_DM).a
+endif
+endif
+
+else                                    # Unix, BeOS or Mac OS X (Darwin)
+
+ifeq ($(findstring openbsd,$(OSTYPE)),openbsd) # for example "openbsd3.4"
+# i386_iopl() is located in libi386.a
+LDFLAGS+=
+endif
+
+ifdef USE_DISCMAGE
+ifeq ($(findstring darwin,$(OSTYPE)),darwin) # for example "darwin7.0"
+FULLLIBNAME_DM=$(LIBNAME_DM).dylib
+else
+FULLLIBNAME_DM=$(LIBNAME_DM).so
+endif
+endif
+
+ifdef DLOPEN
+
+ifneq ($(OSTYPE),beos)
+ifeq ($(findstring freebsd,$(OSTYPE)),) # false if OSTYPE contains "freebsd"
+ifeq ($(findstring openbsd,$(OSTYPE)),) # false if OSTYPE contains "openbsd"
+LDFLAGS+=-ldl
+endif
+endif
+endif
+
+else                                    # DLOPEN
+ifdef USE_DISCMAGE                      # GNU specific: "simply expanded variable"
+FULLLIBNAME_DM:=$(addprefix lib,$(FULLLIBNAME_DM))
+LDFLAGS+=-Llibdiscmage -l$(LIBNAME_DM)
+endif
+
+endif
+
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+
+TARGETS+=libdiscmage/$(FULLLIBNAME_DM)
+
+
+ifdef USE_LIBCD64
+LDFLAGS+=backup/libcd64/libcd64.a
+TARGETS+=backup/libcd64/libcd64.a
+endif
+
+
+OBJECTS=ucon64.o ucon64_dat.o ucon64_misc.o ucon64_opts.o \
+        misc/chksum.o misc/file.o misc/getopt.o misc/getopt2.o \
+        misc/misc.o misc/parallel.o misc/property.o misc/string.o \
+        patch/aps.o patch/bsl.o patch/gg.o patch/ips.o patch/pal4u.o \
+        patch/ppf.o patch/xps.o \
+        console/dc.o console/gb.o console/gba.o console/genesis.o \
+        console/jaguar.o console/lynx.o console/n64.o console/neogeo.o \
+        console/nes.o console/ngp.o console/pce.o console/psx.o console/sms.o \
+        console/snes.o console/swan.o \
+        backup/cd64.o backup/cmc.o backup/dex.o backup/doctor64.o \
+        backup/doctor64jr.o backup/f2a.o backup/fal.o backup/ffe.o \
+        backup/fig.o backup/gbx.o backup/gd.o backup/interceptor.o \
+        backup/lynxit.o backup/mccl.o backup/mcd.o backup/md-pro.o \
+        backup/mgd.o backup/msg.o backup/pce-pro.o backup/pl.o \
+        backup/psxpblib.o backup/sflash.o backup/smc.o backup/smd.o \
+        backup/smsgg-pro.o backup/ssc.o backup/swc.o backup/tototek.o \
+        backup/ufo.o backup/yoko.o backup/z64.o
+ifdef USE_ZLIB
+LDFLAGS+=-lz
+OBJECTS+=misc/archive.o misc/map.o misc/unzip.o
+endif
+ifdef USE_USB
+LDFLAGS+=-lusb
+OBJECTS+=misc/usb.o
+endif
+
+ifdef DLOPEN
+OBJECTS+=misc/dlopen.o
+ifndef USE_ZLIB
+ifeq ($(GCC_WIN),1)
+else
+ifdef DJGPP                             # DJGPP code in dlopen needs map code
+OBJECTS+=misc/map.o
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+endif                                   # USE_ZLIB
+else
+ifeq ($(GCC_WIN),1)                     # Cygwin/MinGW code in ucon64_misc needs dlopen code
+OBJECTS+=misc/dlopen.o
+endif                                   # GCC_WIN
+endif                                   # DLOPEN
+
+
+TARGET=ucon64
+ifeq ($(GCC_WIN),1)
+TARGET:=$(addsuffix .exe,$(TARGET))     # adding .exe avoids "problems" with Cygwin/MinGW
+else
+ifdef DJGPP                             # OSTYPE is not defined by default under DOS
+TARGET:=$(addsuffix .exe,$(TARGET))
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+TARGETS+=$(TARGET)
+
+all: $(TARGETS)
+
+
+CLEAN_CMD=rm -f $(TARGET) $(OBJECTS) *.core *.stackdump *~ */*~ */*/*~; \
+cd libdiscmage && $(MAKE) clean; \
+cd ../backup/libcd64 && $(MAKE) clean
+
+clean:
+ifeq ($(GCC_WIN),1)
+	$(CLEAN_CMD)
+else
+ifdef DJGPP
+	del *.o
+	del patch\*.o
+	del console\*.o
+	del backup\*.o
+	del misc\*.o
+	del $(TARGET)
+	cd libdiscmage
+	$(MAKE) clean
+	cd ../backup/libcd64
+	$(MAKE) clean
+	cd ../..
+else                                    # Unix, BeOS or Mac OS X (Darwin)
+	$(CLEAN_CMD)
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+
+
+DISTCLEAN_CMD=rm -f Makefile config.log config.status config.cache config.h; \
+cd libdiscmage && $(MAKE) distclean; \
+cd ../backup/libcd64 && $(MAKE) clean
+# libcd64 Makefile has no distclean target
+
+distclean: clean
+ifeq ($(GCC_WIN),1)
+	$(DISTCLEAN_CMD)
+else
+ifdef DJGPP
+	del Makefile
+	del config.log
+	del config.status
+	del config.cache
+	del config.h
+	cd libdiscmage
+	$(MAKE) distclean
+	cd ../backup/libcd64
+	$(MAKE) clean
+	cd ../..
+else
+	$(DISTCLEAN_CMD)
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+
+
+.c.o:
+	$(CC) $(CFLAGS) -c $< -o $@
+
+backup/cd64.o: backup/cd64.c
+	$(CC) $(CFLAGS) -Ibackup/libcd64 -c $< -o $@
+
+
+ifdef USE_DISCMAGE
+libdiscmage/$(FULLLIBNAME_DM):
+ifeq ($(GCC_WIN),1)
+	cd libdiscmage && $(MAKE)
+else
+ifdef DJGPP
+	cd libdiscmage
+	$(MAKE)
+	cd ..
+else
+	cd libdiscmage && $(MAKE)
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+endif                                   # USE_DISCMAGE
+
+ifdef USE_LIBCD64
+backup/libcd64/libcd64.a:
+ifeq ($(GCC_WIN),1)
+	cd backup/libcd64 && $(MAKE)
+else
+ifdef DJGPP
+	cd backup/libcd64
+	$(MAKE)
+	cd ../..
+else
+	cd backup/libcd64 && $(MAKE)
+endif                                   # DJGPP
+endif                                   # GCC_WIN
+endif                                   # USE_DISCMAGE
+
+$(TARGET): $(OBJECTS)
+	$(CC) $(OBJECTS) $(LDFLAGS) -o $@
+
+
+install:
+ifeq ($(TERM),cygwin)                   # test cygwin before DJGPP
+else
+ifeq ($(OSTYPE),msys)                   # test msys before DJGPP
+else
+ifdef DJGPP
+else
+ifeq ($(OSTYPE),beos)
+	./install_beos.sh
+else
+	./install.sh
+endif                                   # beos
+endif                                   # DJGPP
+endif                                   # msys
+endif                                   # cygwin
+ifndef DLOPEN
+	cd libdiscmage && $(MAKE) install
+endif
+
+
+uninstall:
+ifeq ($(TERM),cygwin)                   # test cygwin before DJGPP
+else
+ifeq ($(OSTYPE),msys)                   # test msys before DJGPP
+else
+ifdef DJGPP
+else
+ifeq ($(OSTYPE),beos)
+	rm -f $(HOME)/config/bin/$(TARGET)
+else
+	rm -f /usr/local/bin/$(TARGET)
+endif                                   # beos
+endif                                   # DJGPP
+endif                                   # msys
+endif                                   # cygwin
+ifndef DLOPEN
+	cd libdiscmage && $(MAKE) uninstall
+endif
+
+
+# Dependencies
+
+# Most source files include these
+UCON64_STD_H=ucon64.h ucon64_misc.h misc/misc.h config.h ucon64_defines.h
+
+misc/archive.o: misc/archive.h misc/map.h config.h
+misc/chksum.o: misc/chksum.h config.h
+misc/dlopen.o: misc/dlopen.h misc/dxedll_pub.h config.h
+misc/getopt.o: misc/getopt.h
+misc/map.o: misc/map.h config.h
+misc/misc.o: misc/misc.h misc/archive.h config.h
+misc/parallel.o: misc/parallel.h config.h
+misc/usb.o: misc/usb.h config.h
+misc/unzip.o: misc/unzip.h config.h
+ucon64.o: misc/dlopen.h misc/getopt.h ucon64_dat.h ucon64_opts.h \
+          console/dc.h console/gb.h console/gba.h console/genesis.h \
+          console/jaguar.h console/lynx.h console/n64.h console/neogeo.h \
+          console/nes.h console/ngp.h console/pce.h console/psx.h console/sms.h \
+          console/snes.h console/swan.h \
+          backup/cd64.h backup/dex.h backup/doctor64.h backup/doctor64jr.h \
+          backup/f2a.h backup/fal.h backup/ffe.h backup/fig.h backup/gbx.h \
+          backup/gd.h backup/interceptor.h backup/lynxit.h backup/mccl.h \
+          backup/mcd.h backup/md-pro.h backup/mgd.h backup/msg.h \
+          backup/pce-pro.h backup/pl.h backup/smc.h backup/smd.h \
+          backup/smsgg-pro.h backup/ssc.h backup/swc.h backup/tototek.h \
+          backup/ufo.h backup/yoko.h backup/z64.h \
+          patch/aps.h patch/bsl.h patch/gg.h patch/ips.h patch/pal4u.h \
+          patch/ppf.h patch/xps.h $(UCON64_STD_H)
+ucon64_dat.o: ucon64_dat.h $(UCON64_STD_H)
+ucon64_misc.o: misc/dlopen.h $(UCON64_STD_H)
+ucon64_opts.o: misc/dlopen.h misc/getopt.h ucon64_dat.h ucon64_opts.h \
+               console/dc.h console/gb.h console/gba.h console/genesis.h \
+               console/jaguar.h console/lynx.h console/n64.h console/neogeo.h \
+               console/nes.h console/ngp.h console/pce.h console/psx.h \
+               console/sms.h console/snes.h console/swan.h \
+               backup/cd64.h backup/dex.h backup/doctor64.h \
+               backup/doctor64jr.h backup/f2a.h backup/fal.h backup/ffe.h \
+               backup/fig.h backup/gbx.h backup/gd.h backup/interceptor.h \
+               backup/lynxit.h backup/mccl.h backup/mcd.h backup/md-pro.h \
+               backup/mgd.h backup/msg.h backup/pce-pro.h backup/pl.h \
+               backup/smc.h backup/smd.h backup/smsgg-pro.h backup/ssc.h \
+               backup/swc.h backup/tototek.h backup/ufo.h backup/yoko.h \
+               backup/z64.h \
+               patch/aps.h patch/bsl.h patch/gg.h patch/ips.h patch/pal4u.h \
+               patch/ppf.h patch/xps.h $(UCON64_STD_H)
+console/dc.o: console/dc.h $(UCON64_STD_H)
+console/gb.o: console/gb.h backup/mgd.h $(UCON64_STD_H)
+console/gba.o: console/gba.h $(UCON64_STD_H)
+console/genesis.o: console/genesis.h backup/smd.h backup/mgd.h $(UCON64_STD_H)
+console/jaguar.o: console/jaguar.h $(UCON64_STD_H)
+console/lynx.o: console/lynx.h $(UCON64_STD_H)
+console/n64.o: console/n64.h $(UCON64_STD_H)
+console/neogeo.o: console/neogeo.h $(UCON64_STD_H)
+console/nes.o: console/nes.h $(UCON64_STD_H)
+console/ngp.o: console/ngp.h $(UCON64_STD_H)
+console/pce.o: console/pce.h backup/mgd.h $(UCON64_STD_H)
+console/psx.o: console/psx.h $(UCON64_STD_H)
+console/sms.o: console/sms.h backup/smd.h backup/mgd.h $(UCON64_STD_H)
+console/snes.o: console/snes.h backup/mgd.h $(UCON64_STD_H)
+console/swan.o: console/swan.h $(UCON64_STD_H)
+backup/cd64.o: backup/cd64.h $(UCON64_STD_H)
+backup/cmc.o: backup/cmc.h $(UCON64_STD_H)
+backup/dex.o: backup/dex.h backup/psxpblib.h $(UCON64_STD_H)
+backup/doctor64.o: backup/doctor64.h $(UCON64_STD_H)
+backup/doctor64jr.o: backup/doctor64jr.h $(UCON64_STD_H)
+backup/f2a.o: backup/f2a.h $(UCON64_STD_H)
+backup/fal.o: backup/fal.h backup/cartlib.c $(UCON64_STD_H)
+backup/ffe.o: backup/ffe.h $(UCON64_STD_H)
+backup/fig.o: backup/fig.h console/snes.h $(UCON64_STD_H)
+backup/gd.o: backup/gd.h console/snes.h $(UCON64_STD_H)
+backup/gbx.o: backup/gbx.h $(UCON64_STD_H)
+backup/lynxit.o: backup/lynxit.h $(UCON64_STD_H)
+backup/mccl.o: backup/mccl.h $(UCON64_STD_H)
+backup/mcd.o: backup/mcd.h $(UCON64_STD_H)
+backup/md-pro.o: backup/md-pro.h backup/tototek.h $(UCON64_STD_H)
+backup/mgd.o: backup/mgd.h $(UCON64_STD_H)
+backup/msg.o: backup/msg.h backup/ffe.h $(UCON64_STD_H)
+backup/pce-pro.o: backup/pce-pro.h backup/tototek.h $(UCON64_STD_H)
+backup/pl.o: backup/pl.h $(UCON64_STD_H)
+backup/psxpblib.o: backup/psxpblib.h $(UCON64_STD_H)
+backup/sflash.o: backup/sflash.h backup/tototek.h $(UCON64_STD_H)
+backup/smc.o: backup/smc.h backup/ffe.h $(UCON64_STD_H)
+backup/smd.o: backup/smd.h backup/ffe.h $(UCON64_STD_H)
+backup/smsgg-pro.o: backup/smsgg-pro.h backup/tototek.h $(UCON64_STD_H)
+backup/swc.o: backup/swc.h backup/ffe.h console/snes.h $(UCON64_STD_H)
+backup/tototek.o: backup/tototek.h $(UCON64_STD_H)
+backup/ufo.o: backup/ufo.h $(UCON64_STD_H)
+backup/yoko.o: backup/yoko.h $(UCON64_STD_H)
+backup/z64.o: backup/z64.h $(UCON64_STD_H)
+patch/aps.o: patch/aps.h $(UCON64_STD_H)
+patch/bsl.o: patch/bsl.h $(UCON64_STD_H)
+patch/gg.o: patch/gg.h $(UCON64_STD_H)
+patch/ips.o: patch/ips.h $(UCON64_STD_H)
+patch/pal4u.o: patch/pal4u.h $(UCON64_STD_H)
+patch/ppf.o: patch/ppf.h $(UCON64_STD_H)
+patch/xps.o: patch/xps.h $(UCON64_STD_H)

+ 223 - 0
ucon64/2.0/src/Makefile.vc6

@@ -0,0 +1,223 @@
+DLOPEN=1
+#USE_ZLIB=1
+USE_DISCMAGE=1
+#USE_USB=1
+USE_LIBCD64=1
+
+CC=cl.exe
+CFLAGS=/nologo /I. /W3 /O2 /DHAVE_CONFIG_H
+LDFLAGS=/NOLOGO setargv.obj
+TARGETS=
+
+!ifdef USE_DISCMAGE
+LIBNAME_DM=discmage
+!endif
+
+!ifdef USE_DISCMAGE
+FULLLIBNAME_DM=$(LIBNAME_DM).dll
+!ifndef DLOPEN
+LDFLAGS=$(LDFLAGS) libdiscmage/$(LIBNAME_DM).lib
+!endif
+!endif
+
+TARGETS=$(TARGETS) libdiscmage/$(FULLLIBNAME_DM)
+
+
+!ifdef USE_LIBCD64
+LDFLAGS=$(LDFLAGS) backup/libcd64/cd64.lib
+TARGETS=$(TARGETS) backup/libcd64/cd64.lib
+!endif
+
+
+OBJECTS=ucon64.obj ucon64_dat.obj ucon64_misc.obj ucon64_opts.obj \
+        misc/chksum.obj misc/file.obj misc/getopt.obj misc/getopt2.obj \
+        misc/misc.obj misc/parallel.obj misc/property.obj misc/string.obj \
+        misc/dlopen.obj \
+        patch/aps.obj patch/bsl.obj patch/gg.obj patch/ips.obj \
+        patch/pal4u.obj patch/ppf.obj patch/xps.obj \
+        console/dc.obj console/gb.obj console/gba.obj console/genesis.obj \
+        console/jaguar.obj console/lynx.obj console/n64.obj \
+        console/neogeo.obj console/nes.obj console/ngp.obj console/pce.obj \
+        console/psx.obj console/sms.obj console/snes.obj console/swan.obj \
+        backup/cd64.obj backup/cmc.obj backup/dex.obj backup/doctor64.obj \
+        backup/doctor64jr.obj backup/f2a.obj backup/fal.obj backup/ffe.obj \
+        backup/fig.obj backup/gbx.obj backup/gd.obj backup/interceptor.obj \
+        backup/lynxit.obj backup/mccl.obj backup/mcd.obj backup/md-pro.obj \
+        backup/mgd.obj backup/msg.obj backup/pce-pro.obj backup/pl.obj \
+        backup/psxpblib.obj backup/sflash.obj backup/smc.obj backup/smd.obj \
+        backup/smsgg-pro.obj backup/ssc.obj backup/swc.obj backup/tototek.obj \
+        backup/ufo.obj backup/yoko.obj backup/z64.obj
+!ifdef USE_ZLIB
+LDFLAGS=$(LDFLAGS) zlib.lib
+OBJECTS=$(OBJECTS) misc/map.obj misc/archive.obj misc/unzip.obj
+!endif
+!ifdef USE_USB
+LDFLAGS=$(LDFLAGS) usb.lib
+OBJECTS=$(OBJECTS) misc/usb.obj
+!endif
+
+
+TARGET=ucon64.exe
+TARGETS=$(TARGETS) $(TARGET)
+
+all: $(TARGETS)
+
+
+clean:
+	del *.obj
+	del patch\*.obj
+	del console\*.obj
+	del backup\*.obj
+	del misc\*.obj
+	del $(TARGET)
+	cd libdiscmage
+	$(MAKE) /NOLOGO /f Makefile.vc6 clean
+	cd ..\backup\libcd64
+	$(MAKE) /NOLOGO /f Makefile.vc6 clean
+	cd ..\..
+
+
+distclean: clean
+	del config.h
+	cd libdiscmage
+	$(MAKE) /NOLOGO /f Makefile.vc6 distclean
+	cd ..\backup\libcd64
+# libcd64 Makefile has no distclean target
+	$(MAKE) /NOLOGO /f Makefile.vc6 clean
+	cd ..\..
+
+
+.c.obj:
+	$(CC) $(CFLAGS) /c $< /Fo$@
+
+backup/cd64.obj:
+	$(CC) $(CFLAGS) /Ibackup/libcd64 /c $*.c /Fo$@
+
+
+!ifdef USE_DISCMAGE
+libdiscmage/$(FULLLIBNAME_DM):
+	cd libdiscmage
+	$(MAKE) /NOLOGO /f Makefile.vc6
+	cd ..
+!endif
+
+!ifdef USE_LIBCD64
+backup/libcd64/cd64.lib:
+	cd backup/libcd64
+	$(MAKE) /NOLOGO /f Makefile.vc6
+	cd ../..
+!endif
+
+
+$(TARGET): $(OBJECTS)
+	link.exe $(OBJECTS) $(LDFLAGS) /OUT:$@
+
+
+install:
+	cd libdiscmage
+	$(MAKE) /NOLOGO /f Makefile.vc6 install
+	cd ..
+
+
+uninstall:
+	cd libdiscmage
+	$(MAKE) /NOLOGO /f Makefile.vc6 uninstall
+	cd ..
+
+
+# Dependencies
+
+# Most source files include these
+UCON64_STD_H=ucon64.h ucon64_misc.h misc/misc.h config.h ucon64_defines.h
+
+misc/archive.obj: misc/archive.h misc/map.h config.h
+misc/chk.obj: misc/chksum.h config.h
+misc/dlopen.obj: misc/dlopen.h misc/dxedll_pub.h config.h
+misc/getopt.obj: misc/getopt.h
+misc/map.obj: misc/map.h config.h
+misc/misc.obj: misc/misc.h misc/archive.h config.h
+misc/parallel.obj: misc/parallel.h config.h
+misc/usb.obj: misc/usb.h config.h
+misc/unzip.obj: misc/unzip.h config.h
+ucon64.obj: misc/dlopen.h misc/getopt.h ucon64_dat.h ucon64_opts.h \
+            console/dc.h console/gb.h console/gba.h console/genesis.h \
+            console/jaguar.h console/lynx.h console/n64.h console/neogeo.h \
+            console/nes.h console/ngp.h console/pce.h console/psx.h \
+            console/sms.h console/snes.h console/swan.h \
+            backup/cd64.h backup/dex.h backup/doctor64.h backup/doctor64jr.h \
+            backup/f2a.h backup/fal.h backup/ffe.h backup/fig.h backup/gbx.h \
+            backup/gd.h backup/interceptor.h backup/lynxit.h backup/mccl.h \
+            backup/mcd.h backup/md-pro.h backup/mgd.h backup/msg.h \
+            backup/pce-pro.h backup/pl.h backup/smc.h backup/smd.h \
+            backup/smsgg-pro.h backup/ssc.h backup/swc.h backup/tototek.h \
+            backup/ufo.h backup/yoko.h backup/z64.h \
+            patch/aps.h patch/bsl.h patch/gg.h patch/ips.h patch/pal4u.h \
+            patch/ppf.h patch/xps.h $(UCON64_STD_H)
+ucon64_dat.obj: ucon64_dat.h $(UCON64_STD_H)
+ucon64_misc.obj: misc/dlopen.h $(UCON64_STD_H)
+ucon64_opts.obj: misc/dlopen.h misc/getopt.h ucon64_dat.h ucon64_opts.h \
+                 console/dc.h console/gb.h console/gba.h console/genesis.h \
+                 console/jaguar.h console/lynx.h console/n64.h \
+                 console/neogeo.h console/nes.h console/ngp.h console/pce.h \
+                 console/psx.h console/sms.h console/snes.h console/swan.h \
+                 backup/cd64.h backup/dex.h backup/doctor64.h \
+                 backup/doctor64jr.h backup/f2a.h backup/fal.h backup/ffe.h \
+                 backup/fig.h backup/gbx.h backup/gd.h backup/interceptor.h \
+                 backup/lynxit.h backup/mccl.h backup/mcd.h backup/md-pro.h \
+                 backup/mgd.h backup/msg.h backup/pce-pro.h backup/pl.h \
+                 backup/smc.h backup/smd.h backup/smsgg-pro.h backup/ssc.h \
+                 backup/swc.h backup/tototek.h backup/ufo.h backup/yoko.h \
+                 backup/z64.h \
+                 patch/aps.h patch/bsl.h patch/gg.h patch/ips.h patch/pal4u.h \
+                 patch/ppf.h patch/xps.h $(UCON64_STD_H)
+console/dc.obj: console/dc.h $(UCON64_STD_H)
+console/gb.obj: console/gb.h backup/mgd.h $(UCON64_STD_H)
+console/gba.obj: console/gba.h $(UCON64_STD_H)
+console/genesis.obj: console/genesis.h backup/smd.h backup/mgd.h $(UCON64_STD_H)
+console/jaguar.obj: console/jaguar.h $(UCON64_STD_H)
+console/lynx.obj: console/lynx.h $(UCON64_STD_H)
+console/n64.obj: console/n64.h $(UCON64_STD_H)
+console/neogeo.obj: console/neogeo.h $(UCON64_STD_H)
+console/nes.obj: console/nes.h $(UCON64_STD_H)
+console/ngp.obj: console/ngp.h $(UCON64_STD_H)
+console/pce.obj: console/pce.h backup/mgd.h $(UCON64_STD_H)
+console/psx.obj: console/psx.h $(UCON64_STD_H)
+console/sms.obj: console/sms.h backup/smd.h backup/mgd.h $(UCON64_STD_H)
+console/snes.obj: console/snes.h backup/mgd.h $(UCON64_STD_H)
+console/swan.obj: console/swan.h $(UCON64_STD_H)
+backup/cd64.obj: backup/cd64.h $(UCON64_STD_H)
+backup/cmc.obj: backup/cmc.h $(UCON64_STD_H)
+backup/dex.obj: backup/dex.h backup/psxpblib.h $(UCON64_STD_H)
+backup/doctor64.obj: backup/doctor64.h $(UCON64_STD_H)
+backup/doctor64jr.obj: backup/doctor64jr.h $(UCON64_STD_H)
+backup/f2a.obj: backup/f2a.h $(UCON64_STD_H)
+backup/fal.obj: backup/fal.h backup/cartlib.c $(UCON64_STD_H)
+backup/ffe.obj: backup/ffe.h $(UCON64_STD_H)
+backup/fig.obj: backup/fig.h console/snes.h $(UCON64_STD_H)
+backup/gd.obj: backup/gd.h console/snes.h $(UCON64_STD_H)
+backup/gbx.obj: backup/gbx.h $(UCON64_STD_H)
+backup/lynxit.obj: backup/lynxit.h $(UCON64_STD_H)
+backup/mcd.obj: backup/mcd.h $(UCON64_STD_H)
+backup/mccl.obj: backup/mccl.h $(UCON64_STD_H)
+backup/md-pro.obj: backup/md-pro.h backup/tototek.h $(UCON64_STD_H)
+backup/mgd.obj: backup/mgd.h $(UCON64_STD_H)
+backup/msg.obj: backup/msg.h backup/ffe.h $(UCON64_STD_H)
+backup/pce-pro.obj: backup/pce-pro.h backup/tototek.h $(UCON64_STD_H)
+backup/pl.obj: backup/pl.h $(UCON64_STD_H)
+backup/psxpblib.obj: backup/psxpblib.h $(UCON64_STD_H)
+backup/sflash.obj: backup/sflash.h backup/tototek.h $(UCON64_STD_H)
+backup/smc.obj: backup/smc.h backup/ffe.h $(UCON64_STD_H)
+backup/smd.obj: backup/smd.h backup/ffe.h $(UCON64_STD_H)
+backup/smsgg-pro.obj: backup/smsgg-pro.h backup/tototek.h $(UCON64_STD_H)
+backup/swc.obj: backup/swc.h backup/ffe.h console/snes.h $(UCON64_STD_H)
+backup/tototek.obj: backup/tototek.h $(UCON64_STD_H)
+backup/ufo.obj: backup/ufo.h $(UCON64_STD_H)
+backup/yoko.obj: backup/yoko.h $(UCON64_STD_H)
+backup/z64.obj: backup/z64.h $(UCON64_STD_H)
+patch/aps.obj: patch/aps.h $(UCON64_STD_H)
+patch/bsl.obj: patch/bsl.h $(UCON64_STD_H)
+patch/gg.obj: patch/gg.h $(UCON64_STD_H)
+patch/ips.obj: patch/ips.h $(UCON64_STD_H)
+patch/pal4u.obj: patch/pal4u.h $(UCON64_STD_H)
+patch/ppf.obj: patch/ppf.h $(UCON64_STD_H)
+patch/xps.obj: patch/xps.h $(UCON64_STD_H)

+ 118 - 0
ucon64/2.0/src/backup/NTSC-PAL notes.txt

@@ -0,0 +1,118 @@
+Legend for Code1:
+0 = seems to work without modification
+1 = needs crack (uCON64 option -k)
+2 = needs NTSC/PAL fix (uCON64 option -f)
+3 = needs backup unit header fix
+4 = seems to work, but doesn't work correctly
+5 = music doesn't work
+6 = copy protection screen
+7 = wrong television standard (NTSC/PAL) screen
+8 = corrupted/distorted graphics
+9 = doesn't work
+
+
+Legend for Code2:
+N1  = 3f 21 29/89 10 f0                     3f 21 29/89 10 80
+N2  = ad 3f 21 29 10 d0                     ad 3f 21 29 10 ea ea
+P3  = ad 3f 21 89 10 d0                     ad 3f 21 89 10 80                - Terranigma
+N4  = ad 3f 21 89 10 d0                     ad 3f 21 89 10 80/(ea ea)        - Live A Live
+N5  = 3f 21 29/89 10 00 f0                  3f 21 29/89 10 00 80             - Clock Tower
+N6  = 3f 21 29/89 10 00 d0                  3f 21 29/89 10 00 ea ea          - Mario no Super Picross
+P7  = ad 3f 21 29 10 00 d0                  ad 3f 21 29 10 00 80
+P8  = ad 3f 21 89 10 00 d0                  a9 10 00 89 10 00 d0             - Eric Cantona Football ?
+N9  =    3f 21 89 10 c2 XX f0                  3f 21 89 10 c2 XX 80          - Front Mission - Gun Hazard
+N10 =    3f 21 89 10 c2 XX d0                  3f 21 89 10 c2 XX ea ea       - Robotrek
+N11 = 3f 21 29/89 10 c9 10 f0               3f 21 29/89 10 c9 10 80
+N12 = ad 3f 21 29 10 c9 00 f0               ad 3f 21 29 10 c9 00 80/(ea ea) <= original uCON used 80
+N13 = ad 3f 21 29 10 c9 00 d0               ad 3f 21 29 10 c9 00 80
+N14 = ad 3f 21 29 10 c9 10 d0               ad 3f 21 29 10 c9 10 ea ea
+P15 = ad 3f 21 29 10 cf bd ff XX f0         ad 3f 21 29 10 cf bd ff XX 80    - Pop'n Twinbee E
+N16 =    3f 21 29 10 cf XX YY 80 f0            3f 21 29 10 cf XX YY 80 80    - Gokujyou Parodius/Tokimeki Memorial
+N17 = ad 3f 21 8d XX YY 29 10 8d            ad 3f 21 8d XX YY 29 00 8d       - Dragon Ball Z - Super Butoden 2 ?
+N18 = 3f 21 00 29/89 10 f0                  3f 21 00 29/89 10 80             - Kirby's Dream Course U
+P19 = af 3f 21 00 29 10 d0                  af 3f 21 00 29 10 80
+N20 = af 3f 21 00 29/89 10 d0               af 3f 21 00 29/89 10 ea ea       - Kirby no Kirakira Kids/Final Fight Guy
+P21 = af 3f 21 00 29 10 00 d0               af 3f 21 00 29 10 00 ea ea
+N22 = af 3f 21 00 29/89 10 00 f0            af 3f 21 00 29/89 10 00 80
+P23 = af 3f 21 00 29 XX c9 XX f0            af 3f 21 00 29 XX c9 XX 80       - Secret of Mana E
+N24 = af 3f 21 00 29 XX c9 XX f0            af 3f 21 00 29 XX c9 XX 80       - Seiken Densetsu 3
+N25 = af 3f 21 00 29 10 80 2d 00 1b         af 3f 21 00 29 00 80 2d 00 1b    - Seiken Densetsu 2/Secret of Mana U
+N26 =    3f 21 00 89 10 c2 XX f0               3f 21 00 89 10 c2 XX 80       - Dragon - The Bruce Lee Story U
+N27 = af 3f 21 00 XX YY 29 10 00 d0         af 3f 21 00 XX YY 29 10 00 ea ea - Fatal Fury Special ?
+N28 =    3f 21 c2 XX 29 10 00 f0               3f 21 c2 XX 29 10 00 80       - Metal Warriors
+N29 =    3f 21 c2 XX 29 10 00 d0               3f 21 c2 XX 29 10 00 ea ea    - Dual Orb II
+N30 = af 3f 21 ea 89 10 00 d0               a9 00 00 ea 89 10 00 d0          - Super Famista 3 ?
+P31 = a2 18 01 bd 27 20 89 10 00 f0 01      a2 18 01 bd 27 20 89 10 00 ea ea - Donkey Kong Country E
+N32 = a2 18 01 bd 27 20 89 10 00 d0 01      a2 18 01 bd 27 20 89 10 00 ea ea - Donkey Kong Country U
+N33 = 29 10 00 a2 00 00 c9 10 00 d0         29 10 00 a2 00 00 c9 10 00 80    - Wolfenstein 3D U
+
+
+Comment:
+Code1 is valid for an SWC 2.8cc 32 Mbit PAL. For example Terranigma has a Code1
+of 0, but a Code2 of P3, because it runs without modification on a PAL SNES but
+needs an NTSC/PAL fix for an NTSC SNES.
+The prefix N indicates an NTSC protection code, the prefix P indicates a PAL
+protection code. Cracking or fixing an NTSC protection code makes a game run
+on a PAL SNES and vice versa.
+
+
+Code1   Code2   Game
+2       N5      Clock Tower (J) <= 0x29
+1 + 2   N1      Demon's Crest (U) <= (0xad) 0x29
+1 + 2 + 4  N6   Donkey Kong Country 2 - Diddy's Kong Quest (U) (V1.1) <= can't get past the first level; 0x29
+1       P31     Donkey Kong Country (E) (V1.0)
+1       P31     Donkey Kong Country (E) (V1.1)
+1 + 2   N32     Donkey Kong Country (U) (V1.0)
+1 + 2   N32     Donkey Kong Country (U) (V1.1)
+2       N26     Dragon - The Bruce Lee Story (U)
+2       N4      Dr. Mario (J) (NP) <= 0xea 0xea
+2       N29     Dual Orb II (J)
+1 + 2   N18     Earthbound (U) <= 0x29
+2 + 3   N1      Final Fight 3 (U) <= emulation mode select byte: 0x1c; (0xad) 0x29
+2       N20     Final Fight Guy (U) <= 0x89
+2 + 3   N1      Final Fight Tough (J) <= emulation mode select byte: 0x1c; (0xad) 0x29
+1 + 2   N9      Front Mission - Gun Hazard (J) <= modification protection; code 0 for NTSC SNES; 0x20
+2       N16     Gokujou Parodius (J) <=  0xad 0xff
+2       N1      Illusion of Gaia (U) <= (0xad) 0x89
+2 + 9   N16     Jikkyou Oshaberi Parodius (J) <= 0xad 0xff
+2       N1      Joe & Mac 2 - Lost in the Tropics (U) (35468) <= (0xad) 0x29
+2       N1      Joe & Mac 2 - Lost in the Tropics (U) (54227) <= (0xad) 0x29
+2 + 9   N5      Kaite Tukutte Asoberu Dezaemon (J) <= 0x89
+2       N20     Kirby no Kirakira Kids (J) (NP) <= 0x29
+1 + 2   N18     Kirby's Dream Course (U) <= 0x29
+2       N4      Live A Live (J) <= 0xea 0xea
+2       N28     Magical Pop'n (J)
+1 + 2   N6      Mario no Super Picross (J) <= 0x89
+2       N1      Mega Man's Soccer (U) <= (0xad) 0x89
+2       N1      Mega Man VII (U) <= (0xad) 0x29
+2 + 9   N1      Mega Man X 2 (U) <= the intro can be viewed; (0xad) 0x89
+1 + 2   N1      Mega Man X (U) (V1.0) <= (0xad) 0x89
+1 + 2   N1      Mega Man X (U) (V1.1) <= (0xad) 0x89
+2       N28     Metal Warriors (U)
+2 + 3   N1 + N2 Mickey to Donald Magical Adventure 3 (J) <= emulation mode select byte: 0x1c; (0xad) 0x29
+2       N2      Ninja Gaiden Trilogy (U)
+0       P15     Pop'n Twinbee (E) <= 0x80
+0       P15     Pop'n Twinbee - Rainbow Bell Adventures (E) <= 0x00
+2       N10     Robotrek (U) <= 0x20
+2       N18     Romancing Sa-Ga 2 (J) <= 0x89
+2       N18     Romancing Sa-Ga 3 (J) (V1.0) <= 0x89
+2       N18     Romancing Sa-Ga 3 (J) (V1.1) <= 0x89
+2       N25     Secret of Mana (U)
+0       P23     Secret of Mana (E) (V1.0) <= 0x10 0x10
+0       P23     Secret of Mana (E) (V1.1) <= 0x10 0x10
+2       N25     Seiken Densetsu 2 (J)
+2       N24     Seiken Densetsu 3 (J) <= 0x10 0x00
+2       N1      Street Fighter II Turbo (U) <= (0xad) 0x29
+1 + 2   N1      Super Mario All-Stars (U) <= (0xad) 0x89
+1 + 2   N1      Super Mario All-Stars & World (U) <= (0xad) 0x89
+1 + 2   N1      Super Mario Collection (J) (V1.1) <= (0xad) 0x89
+1 + 2   N1      Super Metroid (JU) <= (0xad) 0x89
+0       P3      Terranigma (E)
+1       P3      Tetris Attack (E)
+1 + 2   N1      Tetris Attack (U) <= (0xad) 0x89
+2 + 8 + 9  N16  Tokimeki Memorial - Densetsu no Ki no Shita de (J) (V1.1) <= the game halts after a while; 0xad 0xff
+2       N33     Wolfenstein 3D (U)
+2       N1      Yoshi's Safari (U) <= I don't have a Super Scope...; (0xad) 0x29
+2       N2      Ys V - Expert (J)
+
+EOF

+ 419 - 0
ucon64/2.0/src/backup/SWC-compatibility.txt

@@ -0,0 +1,419 @@
+A Super Wild Card compatibility list
+Version: 1.27
+Author: dbjh "of uCON64" with great help from CL "of NSRT" and The Dumper
+Date: 30 November 2003
+Hardware: Super Wild Card 2.8cc 32 Mbit PAL
+Software: uCON64 1.9.8-3
+
+
+Legend:
+0 = seems to work without modification
+1 = needs crack (uCON64 option -k)
+2 = needs NTSC/PAL fix (uCON64 option -f)
+3 = needs backup unit header fix
+4 = seems to work, but doesn't work correctly
+5 = music doesn't work
+6 = copy protection screen
+7 = wrong television standard (NTSC/PAL) screen
+8 = corrupted/distorted graphics
+9 = doesn't work
+
+
+Comment:
+I use the word seems, because I have tested most games only for a few minutes
+or so. Even for games that I played from the beginning till the end I can't be
+sure if they run as they do from a cartridge.
+Of course I tried to crack or fix the games that displayed a copy protection
+screen. The games that have that code can't be cracked with uCON64.
+Non-playing music seems to be a BS-specific problem.
+For some games I put an extra comment after the character sequence "<=". In
+some comments I mention the so-called emulation mode select byte. This is the
+byte at offset 2 in the header. You can check the value of the emulation mode
+select byte by passing the command line option -dbuh to uCON64. You can change
+it with a hex(adecimal) editor or by using the uCON64 option -poke (new in
+version 1.9.8beta8). For example, to change the emulation mode select byte for
+Mickey to Donald Magical Adventure 3 (J).swc you would type:
+  ucon64 -poke=2:1c "Mickey to Donald Magical Adventure 3 (J).swc"
+I have sent all games to the Super Wild Card via a parallel cable. Note that
+for LoROM games that don't use SRAM uCON64 adjusts the emulation mode select
+byte before sending the game. This causes that some games don't have code 3,
+because uCON64 already makes the required modification. For example, if Addams
+Family Values is loaded from diskette the emulation mode select byte should
+have the value 0x2c instead of the value 0x0c (before the file is split).
+For games marked with an asterisk uCON64 1.9.8-3 is required (or an updated
+version of snescopy.txt and/or snesntsc.txt/snespal.txt).
+
+
+Code    Game
+0 -     7th Saga, The (U)
+0 -     AAAHH!!! Real Monsters (U)
+8 + 9 - Ace wo Nerae! (J) <= it does work with Super Mario Kart (E) plugged in
+0 -     ActRaiser 2 (E)
+4 -     ActRaiser 2 (U) <= level(s?) (Industen) can't be entered
+0 -     ActRaiser (U)
+0 -     Addams Family, The (E)
+0 -     Addams Family, The - Pugsley's Scavenger Hunt (Beta)
+0 -     Addams Family, The - Pugsley's Scavenger Hunt (E)
+0 -     Addams Family Values (E)
+0 -     Adventures of Batman & Robin, The (E)
+0 -     Aladdin (E)
+0 -     Alcahest (J)
+0 -     Alfred Chicken (E) (4285)
+0 -     Alfred Chicken (E) (6937)
+0 -     Alfred Chicken (U)
+0 -     Alien 3 (E)
+0 -     Alien vs. Predator (U)
+0 -     Another World (E)
+0 -     Arabian Nights - Sabaku no Seirei Ou (J)
+0 -     Arcade's Greatest Hits (E)
+0 -     Axelay (E)
+0 -     Bahamut Lagoon (J)
+0 -     Bastard!! Ankoku no Hakai-shin (J)
+0 -     Batman Returns (E)
+0 -     Batman Returns (J)
+3 -     Batman - Revenge of the Joker (U) <= emulation mode select byte: 0x0c
+0 -     Battletoads & Double Dragon - The Ultimate Team (U)
+0 -     Battletoads in Battlemaniacs (U) (26454)
+0 -     Beauty and the Beast (E)
+0 -     Bishoujo Senshi Sailor Moon (J)
+0 -     Brawl Brothers (U)
+1 -     Breath of Fire II (E)
+0 -     Breath of Fire (U)
+9 -     BS Bandai Satellaview-X BIOS
+0 -     BS Chrono Trigger - Character Library (J)
+0 -     BS Chrono Trigger - Jet Bike Special (J)
+0 -     BS Chrono Trigger - Music Library (J)
+8 -     BS Dan Dan Belt Conveyor Ukulele no Maki (J) (NG-Dump Known)
+0 -     BS Dr. Mario (J)
+0 -     BS Dynami Tracer (J)
+0 -     BS F-Zero Grand Prix 2 (J)
+0 -     BS Koi ha Balance - Battle of Lovers (J)
+5 -     BS Mario Collection 3 (J)
+8 -     BS Pokekame Magajin (J)
+9 -     BS Radical Dreamers (J) [f1] <= with the translation patch it does work
+9 -     BS Radical Dreamers (J) <= idem
+0 -     BS Super Earth Defense Force (J)
+0 -     BS Super Famicom Wars (J) [h1]
+0 -     BS Super Famicom Wars (J)
+5 -     BS Super Mario USA 4 (J)
+8 -     BS Treasure Conflix (J)
+0 -     BS Wai Wai Check 3-7th (J)
+0 -     BS Wario no Mori (J)
+0 -     BS Yoshi no Panepon (J)
+0 -     BS Zelda no Densetsu - Kamigami no Triforce (J)
+9 -     BS Zelda no Densetsu Kodai no Sekiban Dai 1 Hanashi (J)
+9 -     BS Zelda no Densetsu Kodai no Sekiban Dai 3 Hanashi (J)
+1 -     BS Zelda no Densetsu Remix (J)
+0 -     BS Zootto Mahjong! Event Version (J)
+0 -     Bubsy II (U)
+0 -     Bubsy in Claws Encounters of the Furred Kind (E)
+0 -     Castlevania - Vampire's Kiss (E)
+0 -     Choh Makai-Mura (J)
+0 -     Choplifter III (U) (30545)
+0 -     Chou Jikuu Yousai Macross - Scrambled Valkyrie (J)
+0 -     Chrono Trigger - Kurono Toriga (J)
+0 -     Chrono Trigger (U)
+0 -     Chuck Rock (U)
+0 -     Clay Fighter 2 - Judgment Clay (E)
+0 -     Clay Fighter (E)
+7 -     Clay Fighter (U)
+2 -     Clock Tower (J)
+0 -     Contra III - The Alien Wars (U)
+0 -     Cybernator (U)
+0 -     Daffy Duck - The Marvin Missions (U)
+7 -     Dai-3-Ji Super Robot Taisen (J)
+9 -     Daikaijuu Monogatari 2 (J) <= works on an SWC DX2 64 Mbit PAL
+0 -     Darius Twin (E)
+0 -     Darius Twin (U)
+1 + 2 - Demon's Crest (U)
+0 -     Der Langrisser (J) (V1.1)
+0 -     Desert Strike - Return to the Gulf (U)
+9 -     Dirt Trax FX (E)
+0 -     Donald Duck Mahou no Boushi (J)
+1 -     Donkey Kong Country 2 - Diddy's Kong Quest (E)
+1+2+4 - Donkey Kong Country 2 - Diddy's Kong Quest (U) (V1.1) <= can't get past the first level
+1 -     Donkey Kong Country 3 - Dixie Kong's Double Trouble (E)
+0 -     Donkey Kong Country - Competition Cartridge (U)
+1 -     Donkey Kong Country (E) (V1.0)
+1 -     Donkey Kong Country (E) (V1.1)
+1 + 2 - Donkey Kong Country (U) (V1.0)
+1 + 2 - Donkey Kong Country (U) (V1.1)
+9 -     Doom (U)
+0 -     Doraemon - Nobita to Yosei no Kuni (J)
+0 -     Dragon Quest I & II (J)
+0 -     Dragon Quest III (J)
+0 -     Dragon Quest VI (J)
+0 -     Dragon Quest V (J)
+0 -     Dragon's Lair (Beta)
+0 -     Dragon's Lair (E)
+0 -     Dragon - The Bruce Lee Story (E) [a1]
+2 -     Dragon - The Bruce Lee Story (U)
+2 -     Dr. Mario (J) (NP)
+2 -     Dual Orb II (J)
+1 + 2 - Earthbound (U)
+0 -     Earthworm Jim 2 (U)
+0 -     Earthworm Jim (U)
+0 -     Eek! The Cat (E)
+0 -     E.V.O. Search For Eden (U)
+0 -     F-1 Grand Prix (J)
+8 + 9 - F1 ROC II - Race of Champions (U) <= course is not visible
+0 -     Final Fantasy III (U) (V1.0)
+0 -     Final Fantasy III (U) (V1.1)
+0 -     Final Fantasy II (U) (V1.0)
+0 -     Final Fantasy IV (J)
+0 -     Final Fantasy - Mystic Quest (U) (V1.0)
+0 -     Final Fantasy VI (J)
+0 -     Final Fantasy V (J)
+0 -     Final Fight 2 (E)
+2 + 3 - Final Fight 3 (U) <= emulation mode select byte: 0x1c
+2 -     Final Fight Guy (U)
+2 + 3 - Final Fight Tough (J) <= emulation mode select byte: 0x1c
+0 -     Final Fight (U)
+0 -     Flashback (E) (M2)
+0 -     Frogger (U)
+1*+2* - Front Mission - Gun Hazard (J) <= modification protection; code 0 for NTSC SNES
+0 -     Front Mission (J) (V1.0)
+0 -     F-Zero (E)
+0 -     Ganbare Goemon - Yuki hime Kyuushuutsu emaki (J) (V1.2)
+0 -     Ganpuru - Gunman's Proof (J)
+2 -     Gokujou Parodius (J)
+0 -     Goof Troop (E)
+0 -     Gradius III (U)
+0 -     Harvest Moon (U)
+9 -     Hayazashi Nidan Morita Shogi 2 (J) <= halts on "TRANSMIT WAIT"
+0 -     Hebereke's Popoitto (E)
+9 -     Hoshi no Kirby 3 (J)
+2 -     Illusion of Gaia (U)
+0 -     Illusion of Time (E)
+0 -     International Superstar Soccer Deluxe (E)
+0 -     International Superstar Soccer (E)
+0 -     Itchy & Scratchy Game, The (E) <= was 8 + 9!?
+2 + 9   Jikkyou Oshaberi Parodius (J)
+2 -     Joe & Mac 2 - Lost in the Tropics (U) (35468)
+2 -     Joe & Mac 2 - Lost in the Tropics (U) (54227)
+0 -     Joe & Mac - Caveman Ninja (E)
+0 -     Joe & Mac (U)
+0 -     J.R.R. Tolkien's The Lord of the Rings - Volume 1 (U)
+0 -     Juutei Senki (J)
+2 + 9 - Kaite Tukutte Asoberu Dezaemon (J)
+0 -     Kiki KaiKai - Nazo no Kuro Manto (J)
+1 -     Killer Instinct (E)
+2 -     Kirby no Kirakira Kids (J) (NP)
+1 -     Kirby's Dream Course (E)
+1 + 2 - Kirby's Dream Course (U)
+9 -     Kirby's Dream Land 3 (U)
+9 -     Kirby's Fun Pak (E)
+0 -     Kirby's Ghost Trap (E)
+0 -     Krusty's Super Fun House (U) (V1.1)
+0 -     Legend of The Mystical Ninja, The (E)
+0 -     Legend of Zelda, The - A Link to the Past (E)
+0 -     Legend of Zelda, The - A Link to the Past (U)
+0 -     Lemmings (E)
+2 -     Live A Live (J)
+0 -     Lost Vikings II, The (E)
+0 -     Lost Vikings, The (U)
+1 -     Lufia II - Rise of the Sinistrals (H)
+1 -     Lufia II - Rise of the Sinistrals (U)
+0 -     Lufia & The Fortress of Doom (U)
+0 -     Magical Drop (J)
+2 -     Magical Pop'n (J)
+0 -     Magical Quest Starring Mickey Mouse, The (Beta)
+0 -     Magical Quest Starring Mickey Mouse, The (E)
+0 -     Magical Quest Starring Mickey Mouse, The (U)
+1 + 2 - Mario no Super Picross (J)
+0 -     Mario Paint (E) <= I don't have a SNES mouse...
+0 -     Mario & Wario (J) <= idem
+9 -     Masou Kishin - Super Robot Taisen Gaiden - The Lord of Elemental (J)
+2 -     Mega Man's Soccer (U)
+2 -     Mega Man VII (U)
+9 -     Mega Man X 2 (E) <= the intro can be viewed
+2 + 9 - Mega Man X 2 (U) <= idem
+8 + 9 - Mega Man X 3 (U) <= game can be started, but sprites are not visible
+1 -     Mega Man X (E)
+1 + 2 - Mega Man X (U) (V1.0)
+1 + 2 - Mega Man X (U) (V1.1)
+8 + 9 - Metal Combat - Falcon's Revenge (U) <= sprites are not visible
+2 -     Metal Warriors (U)
+2 + 3 - Mickey to Donald Magical Adventure 3 (J) <= emulation mode select byte: 0x1c
+0 -     Micro Machines 2 - Turbo Tournament (E)
+0 -     Micro Machines (U)
+9 -     Momotarou Dentetsu Happy (J) <= halts on "SPC7110 check program v3.0"
+0 -     Mortal Kombat 3 (E)
+0 -     Mortal Kombat (Beta)
+0 -     Mortal Kombat (E)
+0 -     Mortal Kombat II (E) (V1.0)
+0 -     Mortal Kombat II (U) (V1.1)
+0 -     NBA Jam (Beta)
+0 -     NBA Jam (E) (V1.0)
+2 -     Ninja Gaiden Trilogy (U)
+0 -     Ogre Battle - The March of the Black Queen (U)
+0 -     Out of This World (U)
+0 -     Parodius Da! Shinwa kara Owarai he (J)
+0 -     Parodius - Non-Sense Fantasy (E)
+0 -     Phalanx - The Enforce Fighter A-144 (E)
+9 -     Pilotwings (E) <= black screen as soon as the real flying begins
+9 -     Pilotwings (U) <= with the DSP patch it does work with Super Mario Kart (E)
+0 -     Pitfall - The Mayan Adventure (Beta)
+0 -     Pitfall - The Mayan Adventure (E)
+0 -     Pocky & Rocky 2 (U) (54250)
+0 -     Pocky & Rocky (E)
+0 -     Pop'n Twinbee (E)
+0 -     Pop'n Twinbee - Rainbow Bell Adventures (E)
+0 -     Primal Rage (E)
+0 -     Primal Rage (U) (With Sound Test)
+0 -     Prince of Persia 2 - The Shadow & The Flame (E)
+0 -     Prince of Persia (E)
+0 -     Prince of Persia (J)
+0 -     Push-Over (E)
+0 -     Puzzle Bobble (E)
+0 -     Ranma Nibun no Ichi - Akanekodan no Hihou (J)
+0 -     Ranma Nibun no Ichi - Hard Battle (U)
+2 -     Robotrek (U)
+0 -     Rockman & Forte (J)
+0 -     Rock N' Roll Racing (E)
+0 -     Romance of the Three Kingdoms IV - Wall of Fire (U)
+2 -     Romancing Sa-Ga 2 (J)
+2 -     Romancing Sa-Ga 3 (J) (V1.0)
+2 -     Romancing Sa-Ga 3 (J) (V1.1)
+0 -     Romancing Sa-Ga (J) (V1.0)
+0 -     RPG Tsukuru 2 (J)
+0 -     R-Type III - The Third Lightning (E) (21451)
+0 -     Rudra no Hihou (J)
+0 -     Sanrio World Smash Ball! (J)
+0 -     Secret of Evermore (U)
+0 -     Secret of Mana (E) (V1.0)
+0 -     Secret of Mana (E) (V1.1)
+2 -     Secret of Mana (U)
+2 -     Seiken Densetsu 2 (J)
+0 -     Seiken Densetsu 3 (J) (Sample)
+2 -     Seiken Densetsu 3 (J)
+0 -     Shadowrun (E)
+0 -     Shin Megami Tensei (J) (V1.0)
+0 -     Sim Ant (U) (37113)
+0 -     Sim City (E)
+0 -     Sim City (U)
+0 -     Simpsons, The - Bart's Nightmare (U)
+0 -     Smash Tennis (E)
+0 -     Smurfs, The (E)
+0 -     Soldiers of Fortune (U)
+0 -     Sonic the Hedgehog (Unl) [p1][h1]
+0 -     Soul Blazer (U)
+0 -     Spider-Man and the X-Men in Arcade's Revenge (E)
+9 -     Star Fox 2 (Beta)
+9 -     Star Fox 2 (Beta TD)
+9 -     Star Fox Super Weekend Competition (U)
+9 -     Star Fox (U) (V1.0)
+9 -     Star Fox (U) (V1.2)
+9 -     Star Ocean (J)
+9 -     StarWing (E) (V1.0)
+9 -     StarWing (E) (V1.1)
+9 -     Street Fighter Alpha 2 (E) [b1]
+9 -     Street Fighter Alpha 2 (E)
+9 -     Street Fighter Alpha 2 (U)
+0 -     Street Fighter II Champ. Edition (Hack)
+0 -     Street Fighter II - The World Warrior (E)
+0 -     Street Fighter II - The World Warrior (U)
+0 -     Street Fighter II Turbo (E) (V1.1)
+2 -     Street Fighter II Turbo (U)
+9 -     Street Fighter Zero 2 (J)
+0 -     Street Racer (Beta)
+0 -     Street Racer (E)
+0 -     Strike Gunner (E)
+9 -     Stunt Race FX (E)
+0 -     Sunset Riders (E)
+0 -     Super Adventure Island II (U)
+0 -     Super Adventure Island (U)
+0 -     Super Aleste (E)
+0 -     Super Aleste (J)
+0 -     Super Aleste (J) [t1]
+0 -     Super Battletank - War in the Gulf (U) (V1.0)
+0 -     Super Bomberman 2 (E)
+0 -     Super Bomberman 3 (E)
+0 -     Super Bomberman 4 (J)
+0 -     Super Bomberman 5 (J)
+0 -     Super Bomberman (E)
+0 -     Super Castlevania IV (E)
+0 -     Super Earth Defense Force (E)
+0 -     Super Famicom Wars (J) (NP)
+0 -     Super Ghouls 'N Ghosts (E)
+1 -     Super Mario All-Stars (E)
+1 + 2 - Super Mario All-Stars (U)
+1 -     Super Mario All-Stars & World (E)
+1 + 2 - Super Mario All-Stars & World (U)
+1 + 2 - Super Mario Collection (J) (V1.1)
+9 -     Super Mario Kart (E) <= it does work with Super Mario Kart (E) plugged in
+9 -     Super Mario Kart (U) <= idem
+9 -     Super Mario RPG (J) (V1.0)
+9 -     Super Mario RPG (J) (V1.1) (NG-Dump Known)
+9 -     Super Mario RPG - Legend of the Seven Stars (U)
+9 -     Super Mario World 2 - Yoshi's Island (E) (V1.0)
+0 -     Super Mario World (E) (V1.1)
+0 -     Super Mario World (U)
+1 -     Super Metroid (E)
+1 + 2 - Super Metroid (JU)
+0 -     Super NES Super Scope 6 (U) <= I don't have a Super Scope...
+0 -     Super Off Road (U)
+0 -     Super Power League 3 (J)
+9 -     Super Power League 4 (J) <= halts on "SPC7110 check program v3.0"
+0 -     Super Probotector - The Alien Rebels (E)
+0 -     Super Punch-Out!! (U)
+0 -     Super Puyo Puyo (J) (V1.2)
+0 -     Super R-Type (E)
+0 -     Super R-Type (U)
+0 -     Super Smash T.V. (E)
+0 -     Super Smash T.V. (U)
+0 -     Super Star Wars (E)
+0 -     Super Star Wars - Return of the Jedi (E)
+0 -     Super Star Wars - The Empire Strikes Back (Beta)
+0 -     Super Star Wars - The Empire Strikes Back (E)
+0 -     Super Star Wars - The Empire Strikes Back (U) (V1.1)
+0 -     Super Star Wars (U) (31438)
+0 -     Super Street Fighter II - The New Challengers (E)
+0 -     Super Tennis (E) (V1.0)
+0 -     Super Turrican (E)
+0 -     Syndicate (E)
+0 -     T2 - The Arcade Game (U)
+0 -     Tactics Ogre - Let Us Cling Together (J) (V1.2)
+9 -     Tales of Phantasia (J) <= works on an SWC DX2 64 Mbit PAL
+0 -     Teenage Mutant Hero Turtles IV - Turtles in Time (E)
+0 -     Teenage Mutant Hero Turtles - Tournament Fighters (E)
+0 -     Teenage Mutant Ninja Turtles IV - Turtles in Time (A)
+0 -     Teenage Mutant Ninja Turtles IV - Turtles in Time (Beta)
+0 -     Teenage Mutant Ninja Turtles - Turtles in Time (J)
+0 -     Tenchi Muyou! Game Hen (J)
+9 -     Tengai Makyou Zero (J)
+9 -     Tengai Makyou Zero - Shounen Jump no Sho (J)
+0 -     Terranigma (E)
+0 -     Terranigma (S)
+0 -     Test Drive II - The Duel (U) (20429)
+1 -     Tetris Attack (E)
+1 + 2 - Tetris Attack (U)
+0 -     Theme Park (E)
+0 -     Thoroughbred Breeder III (J)
+0 -     Tiny Toon Adventures - Buster Busts Loose! (E)
+2+8+9 - Tokimeki Memorial - Densetsu no Ki no Shita de (J) (V1.1) <= the game halts after a while
+0 -     Tom & Jerry (U)
+0 -     Top Racer (J)
+0 -     Treasure Hunter G (J)
+0 -     Ultimate Mortal Kombat 3 (U)
+0 -     Ultima VII - The Black Gate (Beta)
+0 -     Ultima VII - The Black Gate (U)
+1 -     Uniracers (U)
+1 -     Unirally (E)
+0 -     U.N. Squadron (E)
+9 -     Vortex (E)
+0 -     Wario's Woods (U)
+0 -     Wolfenstein 3D (E)
+2 -     Wolfenstein 3D (U)
+0 -     Wolverine - Adamantium Rage (U)
+0 -     Worms (E)
+0 -     WWF Super Wrestlemania (E)
+9 -     X-Band Modem BIOS (U)
+0 -     X-Men - Mutant Apocalypse (E)
+2 -     Yoshi's Safari (U) <= I don't have a Super Scope...
+2 -     Ys V - Expert (J)
+2 -     Yuu Yuu Hakusho - Tokubetsuhen (J)
+0 -     Zelda no Densetsu - Kamigami no Triforce (J) (V1.0)
+0 -     Zombies (E)
+
+EOF

+ 62 - 0
ucon64/2.0/src/backup/backup.h

@@ -0,0 +1,62 @@
+/*
+backup.h - single header file for all backup unit functions
+
+Copyright (c) 2003 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef BACKUP_H
+#define BACKUP_H
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef  USE_PARALLEL
+#include "cd64.h"
+#include "cmc.h"
+#include "dex.h"
+#include "doctor64.h"
+#include "doctor64jr.h"
+#include "fal.h"
+//#include "ffe.h"
+#include "fig.h"
+#include "gbx.h"
+#include "gd.h"
+#include "interceptor.h"
+#include "lynxit.h"
+#include "mccl.h"
+#include "mcd.h"
+#include "md-pro.h"
+#include "mgd.h"
+#include "msg.h"
+#include "pce-pro.h"
+#include "pl.h"
+//#include "psxpblib.h"
+#include "sflash.h"
+#include "smc.h"
+#include "smd.h"
+#include "smsgg-pro.h"
+#include "ssc.h"
+#include "swc.h"
+#include "ufo.h"
+#include "yoko.h"
+#include "z64.h"
+#endif // USE_PARALLEL
+#if     defined USE_PARALLEL || defined USE_USB
+#include "f2a.h"
+#endif
+
+#endif // BACKUP_H

+ 768 - 0
ucon64/2.0/src/backup/cartlib.c

@@ -0,0 +1,768 @@
+/*
+cartlib.c - Flash Linker Advance support for uCON64
+
+Copyright (c) 2001        Jeff Frohwein
+Copyright (c) 2002 - 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#ifdef  USE_PARALLEL
+
+// *** GBA flash cart support routines in GCC ***
+//  This library allows programming FA/Visoly (both Turbo
+// and non-Turbo) and official Nintendo flash carts. They
+// can be used with the Flash Linker or can be called
+// from GBA code to allow in-system flash cart programming.
+// NOTE: ALL OF THESE ROUTINES MUST BE LOCATED IN GBA RAM
+// IF THIS LIBRARY IS USED FOR IN-SYSTEM PROGRAMMING.
+//
+// by Jeff Frohwein, Started 2001-Aug-29
+//
+//  v1.0 - 2001-Sept-25 - Original release
+//  v1.1 - 2001-Nov-13  - Slightly modified SetVisolyBackupRWMode by removing >>1.
+//
+// Routines -
+//
+//  void SetFAFlashRWMode (void)
+//
+//      Used to enable modifications of FA/Visoly cart flash chip(s)
+//     and also used for CartTypeDetect routine. YOU MUST CALL THIS
+//     ROUTINE BEFORE USING ANY OF THE OTHER FA/VISOLY ROUTINES.
+//
+//  u8 CartTypeDetect (void)
+//      Return a value indicating type of cart installed:
+//       0x00 = Hudson Cart,   0x2e = Standard ROM
+//       0xe2 = N Flash Cart,  0xff = Unknown
+//       0x17 = FA 64M,        0x96 = Turbo FA 64M
+//       0x18 = FA 128M,       0x97 = Turbo FA 128M
+//
+//  u32 EraseNintendoFlashBlocks (u32 StartAddr, u32 BlockCount)
+//
+//      Erase official Nintendo flash cart blocks.
+//       Ex: EraseNintendoFlashBlocks (0x8000000, 1);  // erase block 1
+//           EraseNintendoFlashBlocks (0x8010000, 2);  // erase blocks 2 & 3
+//
+//  u32 EraseNonTurboFABlocks (u32 StartAddr, u32 BlockCount)
+//
+//      Erase older (non-Turbo) Visoly flash cart blocks.
+//       Ex: EraseNonTurboFABlocks (0x8000000, 1);  // erase block 1
+//           EraseNonTurboFABlocks (0x8020000, 2);  // erase blocks 2 & 3
+//
+//  u32 EraseTurboFABlocks (u32 StartAddr, u32 BlockCount)
+//
+//      Erase newer (Turbo) Visoly flash cart blocks.
+//       Ex: EraseTurboFABlocks (0x8000000, 1);  // erase block 1
+//           EraseTurboFABlocks (0x8040000, 2);  // erase blocks 2 & 3
+//
+//  u32 WriteNintendoFlashCart (u32 SrcAddr, u32 FlashAddr, u32 Length)
+//
+//      Write 2 x Length bytes to official Nintendo flash cart.
+//       Ex: WriteNintendoFlashCart (SrcAddr, 0x8000000, 2)  // write 4 bytes
+//
+//  u32 WriteNonTurboFACart (u32 SrcAddr, u32 FlashAddr, u32 Length)
+//
+//      Write 32 x Length bytes to older (non-Turbo) Visoly flash cart.
+//       Ex: WriteNonTurboFACart (SrcAddr, 0x8000000, 2)  // write 64 bytes
+//
+//  u32 WriteTurboFACart (u32 SrcAddr, u32 FlashAddr, u32 Length)
+//
+//      Write 64 x Length bytes to newer (Turbo) Visoly flash cart.
+//       Ex: WriteTurboFACart (SrcAddr, 0x8000000, 2)  // write 128 bytes
+//
+// To reduce the library size and remove support for any
+// of the following types, comment out one or more of
+// the following lines using //
+#define NONTURBO_FA_SUPPORT 1   // Visoly Non-Turbo flash carts
+#define TURBO_FA_SUPPORT 1      // Visoly Turbo flash carts
+#define NOA_FLASH_CART_SUPPORT 1        // Official Nintendo flash carts
+//#define SET_CL_SECTION 1           // Enable setting code section for cartlib
+
+//
+//
+//
+
+#ifdef TURBO_FA_SUPPORT
+#define COMMON_FA_SUPPORT 1
+#endif
+#ifdef NONTURBO_FA_SUPPORT
+#define COMMON_FA_SUPPORT 1
+#endif
+
+#ifdef FLINKER
+ // FLinker programming defines
+#define _MEM_INC 1
+#define FP_TIMEOUT1 0x4000
+#define FP_TIMEOUT2 0x8000
+#define FP_TIMEOUT3 0x80000
+#define _CART_START  0
+#define FLINKER_SET         l40226c ()
+#define READ_NTURBO_SR(a,b) WriteFlash (a, INTEL28F_READSR); \
+                             outpb (SPPCtrlPort, 0);          \
+                             b = (PPReadWord() & 0xff)
+#define READ_NTURBO_S(a)    outpb (SPPCtrlPort, 0);          \
+                             a = (PPReadWord() & 0xff)
+#define READ_TURBO_SR(a)    WriteFlash (0, INTEL28F_READSR); \
+                             PPWriteWord (INTEL28F_READSR);   \
+                             outpb (SPPCtrlPort, 0);          \
+                             a = PPReadWord() & 0xff;         \
+                             a += (PPReadWord() & 0xff) << 8
+#define READ_TURBO_S(a)     outpb (SPPCtrlPort, 0);          \
+                             a = PPReadWord() & 0xff;         \
+                             a += (PPReadWord() & 0xff) << 8
+#define READ_TURBO_S2(a,b,c) outpb (SPPCtrlPort, 0);          \
+                              b = PPReadWord () & 0x80;        \
+                              c = PPReadWord () & 0x80
+#define WRITE_FLASH_NEXT(a,b) PPWriteWord (b)
+#define SET_CART_ADDR(a)    SetCartAddr (a);                 \
+                             l4021d0 (3)
+#define CTRL_PORT_0         outpb (SPPCtrlPort, 0)
+#define CTRL_PORT_1         outpb (SPPCtrlPort, 1)
+
+void
+WriteRepeat (int addr, int data, int count)
+{
+  int i;
+  for (i = 0; i < count; i++)
+    WriteFlash (addr, data);
+}
+
+#else
+ // GBA in-system programming defines
+#define _MEM_INC 2
+#define FP_TIMEOUT1 0x4000      // Probably could be MUCH smaller
+#define FP_TIMEOUT2 0x8000      // Probably could be MUCH smaller
+#define FP_TIMEOUT3 0x80000     // Probably could be MUCH smaller
+#define INTEL28F_BLOCKERASE 0x20
+#define INTEL28F_CLEARSR    0x50
+#define INTEL28F_CONFIRM    0xD0
+#define INTEL28F_QUIRY      0x98
+#define INTEL28F_READARRAY  0xff
+#define INTEL28F_READSR     0x70
+#define INTEL28F_RIC        0x90
+#define INTEL28F_WRTOBUF    0xe8
+
+#define SHARP28F_BLOCKERASE 0x20
+#define SHARP28F_CONFIRM    0xD0
+#define SHARP28F_WORDWRITE  0x10
+#define SHARP28F_READARRAY  0xff
+// typedef     volatile unsigned char           vu8;
+// typedef     volatile unsigned short int      vu16;
+// typedef     volatile unsigned int            vu32;
+// typedef     volatile unsigned long long int  vu64;
+
+// typedef     unsigned char           u8;
+// typedef     unsigned short int      u16;
+// typedef     unsigned int            u32;
+// typedef     unsigned long long int  u64;
+
+#define _CART_START 0x8000000
+#define _BACKUP_START 0xe000000
+#define FLINKER_SET         {}
+#define READ_NTURBO_SR(a,b) *(vu16 *)a = INTEL28F_READSR;     \
+                             b = *(vu16 *)a
+#define READ_NTURBO_S(a)    a = *(vu16 *)_CART_START
+#define READ_TURBO_SR(a)    *(vu16 *)_CART_START = INTEL28F_READSR;     \
+                             *(vu16 *)(_CART_START+2) = INTEL28F_READSR; \
+                             a = *(vu16 *)_CART_START & 0xff;            \
+                             a += (*(vu16 *)(_CART_START+2) & 0xff) << 8
+#define READ_TURBO_S(a)     a = *(vu16 *)_CART_START & 0xff;            \
+                             a += (*(vu16 *)(_CART_START+2) & 0xff) << 8
+#define READ_TURBO_S2(a,b,c) b = *(vu16 *)a & 0x80;            \
+                              c = *(vu16 *)(a+2) & 0x80
+#define WRITE_FLASH_NEXT(a,b) WriteFlash (a, b)
+#define SET_CART_ADDR(a)    {}
+#define CTRL_PORT_0         {}
+#define CTRL_PORT_1         {}
+#define CL_SECTION __attribute__ ((section (".iwram")))
+
+#ifdef SET_CL_SECTION
+ // Prototypes to allow placing routines in any section
+void
+WriteFlash (u32 addr, u16 data)
+  CL_SECTION;
+     u16 ReadFlash (u32 addr) CL_SECTION;
+     void WriteRepeat (u32 addr, u16 data, u16 count) CL_SECTION;
+     void VisolyModePreamble (void) CL_SECTION;
+     void SetVisolyFlashRWMode (void) CL_SECTION;
+     void SetVisolyBackupRWMode (int i) CL_SECTION;
+     u8 CartTypeDetect (void) CL_SECTION;
+     u32 EraseNintendoFlashBlocks (u32 StartAddr, u32 BlockCount) CL_SECTION;
+     u32 EraseNonTurboFABlocks (u32 StartAddr, u32 BlockCount) CL_SECTION;
+     u32 EraseTurboFABlocks (u32 StartAddr, u32 BlockCount) CL_SECTION;
+     u32 WriteNintendoFlashCart (u32 SrcAddr, u32 FlashAddr,
+                                 u32 Length) CL_SECTION;
+     u32 WriteNonTurboFACart (u32 SrcAddr, u32 FlashAddr,
+                              u32 Length) CL_SECTION;
+     u32 WriteTurboFACart (u32 SrcAddr, u32 FlashAddr, u32 Length) CL_SECTION;
+#endif
+
+     void WriteFlash (u32 addr, u16 data)
+{
+  *(vu16 *) addr = data;
+}
+
+u16
+ReadFlash (u32 addr)
+{
+  return (*(vu16 *) addr);
+}
+
+void
+WriteRepeat (u32 addr, u16 data, u16 count)
+{
+  u16 i;
+  for (i = 0; i < count; i++)
+    *(vu16 *) (_CART_START + (addr << 1)) = data;
+}
+#endif
+
+#ifdef COMMON_FA_SUPPORT
+void
+VisolyModePreamble (void)       // 402438
+{
+  FLINKER_SET;
+  WriteRepeat (0x987654, 0x5354, 1);
+  WriteRepeat (0x12345, 0x1234, 500);
+  WriteRepeat (0x7654, 0x5354, 1);
+  WriteRepeat (0x12345, 0x5354, 1);
+  WriteRepeat (0x12345, 0x5678, 500);
+  WriteRepeat (0x987654, 0x5354, 1);
+  WriteRepeat (0x12345, 0x5354, 1);
+  WriteRepeat (0x765400, 0x5678, 1);
+  WriteRepeat (0x13450, 0x1234, 1);
+  WriteRepeat (0x12345, 0xabcd, 500);
+  WriteRepeat (0x987654, 0x5354, 1);
+}
+
+void
+SetVisolyFlashRWMode (void)
+{
+  VisolyModePreamble ();
+  WriteRepeat (0xf12345, 0x9413, 1);
+}
+
+void
+SetVisolyBackupRWMode (int i)   // 402550
+{
+  VisolyModePreamble ();
+  WriteRepeat (0xa12345, i, 1);
+}
+#endif
+
+// Cart Type Detect
+//   Return a value indicating type of cart installed:
+//     0xdc = Hudson Cart,   0x2e = Standard ROM Cart
+//     0xe2 = N Flash Cart,  0xff = Unknown
+//     0x17 = FA 64M,        0x96 = Turbo FA 64M
+//     0x18 = FA 128M,       0x97 = Turbo FA 128M
+
+u8
+CartTypeDetect (void)
+{
+  u8 type = 0xff;
+  u16 Manuf, Device;
+
+  WriteFlash (_CART_START, INTEL28F_RIC);       // Read Identifier codes from flash.
+  // Works for intel 28F640J3A & Sharp LH28F320BJE.
+  Manuf = ReadFlash (_CART_START);
+  Device = ReadFlash (_CART_START + _MEM_INC);
+
+  switch (Manuf)
+    {
+    case 0:                    // Hudson Cart
+      type = 0xdc;
+      break;
+    case 0x2e:                 // Standard ROM
+      type = (u8) Manuf;
+      break;
+    case 0x89:                 // Intel chips
+      switch (Device)
+        {
+        case 0x16:             // i28F320J3A
+        case 0x17:             // i28F640J3A
+        case 0x18:             // i28F128J3A
+          type = (u8) Device;
+          break;
+        default:
+          // Check to see if this is a Visoly "Turbo" cart
+          Device = ReadFlash (_CART_START + _MEM_INC + _MEM_INC);
+          switch (Device)
+            {
+            case 0x16:         // 2 x i28F320J3A
+            case 0x17:         // 2 x i28F640J3A
+            case 0x18:         // 2 x i28F128J3A
+              type = Device + 0x80;
+              break;
+            }
+        }
+      break;
+    case 0xb0:                 // Sharp chips
+      switch (Device)
+        {
+        case 0xe2:
+          type = (u8) Device;
+          break;
+        }
+      break;
+    }
+  WriteFlash (_CART_START, INTEL28F_READARRAY); // Set flash to normal read mode
+  return type;
+}
+
+#ifdef NOA_FLASH_CART_SUPPORT
+// Erase official Nintendo flash cart blocks
+// Function returns true if erase was successful.
+// Each block represents 64k bytes.
+
+u32
+EraseNintendoFlashBlocks (u32 StartAddr, u32 BlockCount)
+{
+  int i = 0;
+  int j, k;
+  time_t starttime = time (NULL);
+
+  for (k = 0; k < (int) BlockCount; k++)
+    {
+      i = StartAddr + (k * 32768 * _MEM_INC);
+
+      do
+        {
+          READ_NTURBO_SR (i, j);
+        }
+      while ((j & 0x80) == 0);
+      WriteFlash (i, SHARP28F_BLOCKERASE);      // Erase a 64k byte block
+      WriteFlash (i, SHARP28F_CONFIRM); // Comfirm block erase
+      ucon64_gauge (starttime, (k + 1) * 64 * 1024, BlockCount * 64 * 1024);
+    }
+
+  do
+    {
+      READ_NTURBO_SR (i, j);
+    }
+  while ((j & 0x80) == 0);
+  WriteFlash (i, SHARP28F_READARRAY);   // Set normal read mode
+  return 1;
+}
+#endif
+
+#ifdef NONTURBO_FA_SUPPORT
+// Erase older (non-Turbo) FA/Visoly flash cart blocks
+// (Single flash chip)
+// Function returns true if erase was successful.
+// Each block represents 128k bytes.
+
+u32
+EraseNonTurboFABlocks (u32 StartAddr, u32 BlockCount)
+{
+  u16 k;
+  u16 Ready = 1;
+  u32 i = 0;
+  u32 Timeout;
+  time_t starttime = time (NULL);
+
+  for (k = 0; k < BlockCount; k++)
+    {
+      i = StartAddr + (k * 65536 * _MEM_INC);
+
+      Ready = 0;
+      Timeout = FP_TIMEOUT2;
+
+      while ((Ready == 0) && (Timeout != 0))
+        {
+          READ_NTURBO_SR (_CART_START, Ready);
+          Ready &= 0x80;
+          Timeout--;
+        }
+
+      if (Ready)
+        {
+          WriteFlash (i, INTEL28F_BLOCKERASE);  // Erase a 128k byte block
+          Ready = 0;
+          Timeout = FP_TIMEOUT3;
+
+          while ((!Ready) && (Timeout != 0))
+            {
+              READ_NTURBO_S (Ready);
+              Ready = (Ready == 0x80);
+              Timeout--;
+            }
+
+          if (Ready)
+            {
+              WriteFlash (i, INTEL28F_CONFIRM); // Comfirm block erase
+              Ready = 0;
+              Timeout = FP_TIMEOUT3;
+
+              while ((!Ready) && (Timeout != 0))
+                {
+                  READ_NTURBO_S (Ready);
+                  Ready = (Ready == 0x80);
+                  Timeout--;
+                }
+
+              if (Ready)
+                {
+                  READ_NTURBO_SR (_CART_START, Ready);
+                  Ready = (Ready == 0x80);
+
+                  if (!Ready)
+                    break;
+                }
+              else
+                break;
+            }
+          else
+            break;
+        }
+      else
+        break;
+      ucon64_gauge (starttime, (k + 1) * 128 * 1024,
+                     BlockCount * 128 * 1024);
+    }
+
+  if (!Ready)
+    {
+      WriteFlash (i, INTEL28F_CLEARSR); // Clear flash status register
+    }
+
+  WriteFlash (i, INTEL28F_READARRAY);   // Set flash to normal read mode
+  WriteFlash (i, INTEL28F_READARRAY);   // Set flash to normal read mode
+
+  return Ready != 0;
+}
+#endif
+
+#ifdef TURBO_FA_SUPPORT
+// Erase newer (Turbo) FA/Visoly flash cart blocks
+// (Dual chip / Interleave)
+// Function returns true if erase was successful.
+// Each block represents 256k bytes.
+
+u32
+EraseTurboFABlocks (u32 StartAddr, u32 BlockCount)
+{
+  u16 j, k;
+  u16 done1, done2;
+  u16 Ready = 1;
+  u32 i = 0;
+  u32 Timeout;
+  time_t starttime = time (NULL);
+
+  for (k = 0; k < BlockCount; k++)
+    {
+      i = StartAddr + (k * 131072 * _MEM_INC);
+
+      Ready = 0;
+      Timeout = FP_TIMEOUT2;
+
+      while ((!Ready) && (Timeout != 0))
+        {
+          READ_TURBO_SR (j);
+          Ready = (j == 0x8080);
+          Timeout--;
+        }
+
+      if (Ready)
+        {
+          done1 = 0;
+          done2 = 0;
+          Ready = 0;
+          Timeout = FP_TIMEOUT3;
+
+          while ((!Ready) && (Timeout != 0))
+            {
+              if (done1 == 0)
+                WriteFlash (i, INTEL28F_BLOCKERASE);    // Erase a 128k byte block in flash #1
+              if (done2 == 0)
+                WriteFlash (i + _MEM_INC, INTEL28F_BLOCKERASE); // Erase a 128k byte block in flash #2
+
+              READ_TURBO_S2 (_CART_START, done1, done2);
+              Ready = ((done1 + done2) == 0x100);
+
+              Timeout--;
+            }
+
+          if (Ready)
+            {
+              WriteFlash (i, INTEL28F_CONFIRM); // Comfirm block erase in flash #1
+              WriteFlash (i + _MEM_INC, INTEL28F_CONFIRM);      // Comfirm block erase in flash #2
+
+              Ready = 0;
+              Timeout = FP_TIMEOUT3;
+              j = 0;
+
+              while (((j & 0x8080) != 0x8080) && (Timeout != 0))
+                {
+                  READ_TURBO_S (j);
+                  Ready = (j == 0x8080);
+
+                  Timeout--;
+                }
+
+              if (!Ready)
+                break;
+            }
+          else
+            break;
+        }
+      else
+        break;
+      ucon64_gauge (starttime, (k + 1) * 256 * 1024,
+                     BlockCount * 256 * 1024);
+    }
+
+  if (!Ready)
+    {
+      WriteFlash (i, INTEL28F_CLEARSR);
+      WriteFlash (i + _MEM_INC, INTEL28F_CLEARSR);
+    }
+
+  WriteFlash (_CART_START, INTEL28F_READARRAY);
+  WriteFlash (_CART_START + _MEM_INC, INTEL28F_READARRAY);
+  WriteFlash (_CART_START, INTEL28F_READARRAY);
+  WriteFlash (_CART_START + _MEM_INC, INTEL28F_READARRAY);
+
+  return Ready != 0;
+}
+#endif
+
+#ifdef NOA_FLASH_CART_SUPPORT
+// Write 2 x Length bytes to official Nintendo flash cart.
+// Function returns true if write was successful.
+
+u32
+WriteNintendoFlashCart (u32 SrcAddr, u32 FlashAddr, u32 Length)
+{
+  int j;
+  int LoopCount = 0;
+  u16 *SrcAddr2 = (u16 *)
+#ifdef  __LP64__
+                  (u64)
+#endif
+                  SrcAddr;
+
+  while (LoopCount < (int) Length)
+    {
+      do
+        {
+          READ_NTURBO_SR (FlashAddr, j);
+        }
+      while ((j & 0x80) == 0);
+
+      WriteFlash (FlashAddr, SHARP28F_WORDWRITE);
+      WriteFlash (FlashAddr, *SrcAddr2);
+      SrcAddr2 += 2;
+      FlashAddr += _MEM_INC;
+      LoopCount++;
+    }
+
+  do
+    {
+      READ_NTURBO_SR (FlashAddr, j);
+    }
+  while ((j & 0x80) == 0);
+
+  WriteFlash (_CART_START, SHARP28F_READARRAY);
+//   CTRL_PORT_0;
+  return 1;
+}
+#endif
+
+#ifdef NONTURBO_FA_SUPPORT
+// Write 32 x Length bytes to older (non-Turbo) FA/Visoly flash cart.
+// Function returns true if write was successful.
+
+u32
+WriteNonTurboFACart (u32 SrcAddr, u32 FlashAddr, u32 Length)
+{
+  int Ready = 0;
+  int Timeout = 0;
+  int LoopCount = 0;
+  u16 *SrcAddr2 = (u16 *)
+#ifdef  __LP64__
+                  (u64)
+#endif
+                  SrcAddr;
+
+  while (LoopCount < (int) Length)
+    {
+      Ready = 0;
+      Timeout = FP_TIMEOUT1;
+
+      while ((Ready == 0) && (Timeout != 0))
+        {
+          WriteFlash (FlashAddr, INTEL28F_WRTOBUF);
+          READ_NTURBO_S (Ready);
+          Ready &= 0x80;
+
+          Timeout--;
+        }
+
+      if (Ready)
+        {
+          int i;
+
+          WriteFlash (FlashAddr, 15);   // Write 15+1 16bit words
+
+          SET_CART_ADDR (FlashAddr);
+
+          for (i = 0; i < 16; i++)
+            {
+              WRITE_FLASH_NEXT (FlashAddr, *SrcAddr2);
+              SrcAddr2 += 2;
+              FlashAddr += _MEM_INC;
+            }
+
+          WRITE_FLASH_NEXT (FlashAddr, INTEL28F_CONFIRM);
+
+          Ready = 0;
+          Timeout = FP_TIMEOUT1;
+
+          while ((Ready == 0) && (Timeout != 0))
+            {
+              READ_NTURBO_SR (_CART_START, i);
+              Ready = i & 0x80;
+
+              Timeout--;
+            }
+
+          if (Ready)
+            {
+              if (i & 0x7f)
+                {
+                  // One or more status register error bits are set
+                  CTRL_PORT_1;
+                  WriteFlash (0, INTEL28F_CLEARSR);
+                  Ready = 0;
+                  break;
+                }
+            }
+          else
+            {
+              CTRL_PORT_1;
+              WriteFlash (0, INTEL28F_CLEARSR);
+              break;
+            }
+        }
+      else
+        {
+          break;
+        }
+
+      LoopCount++;
+    }
+  WriteFlash (_CART_START, INTEL28F_READARRAY); // Set flash to normal read mode
+  WriteFlash (_CART_START, INTEL28F_READARRAY); // Set flash to normal read mode
+  return Ready != 0;
+}
+#endif
+
+#ifdef TURBO_FA_SUPPORT
+// Write 64 x Length bytes to newer (Turbo) FA/Visoly flash cart.
+// Function returns true if write was successful.
+
+u32
+WriteTurboFACart (u32 SrcAddr, u32 FlashAddr, u32 Length)
+{
+  int i, k;
+  int done1, done2;
+  int Timeout;
+  int Ready = 0;
+  int LoopCount = 0;
+  u16 *SrcAddr2 = (u16 *)
+#ifdef  __LP64__
+                  (u64)
+#endif
+                  SrcAddr;
+
+  while (LoopCount < (int) Length)
+    {
+      done1 = 0;
+      done2 = 0;
+      Ready = 0;
+      Timeout = 0x4000;
+
+      while ((!Ready) && (Timeout != 0))
+        {
+          if (done1 == 0)
+            WriteFlash (FlashAddr, INTEL28F_WRTOBUF);
+          if (done2 == 0)
+            WriteFlash (FlashAddr + _MEM_INC, INTEL28F_WRTOBUF);
+
+          SET_CART_ADDR (FlashAddr);
+          READ_TURBO_S2 (FlashAddr, done1, done2);
+
+          Ready = ((done1 + done2) == 0x100);
+
+          Timeout--;
+        }
+
+      if (Ready)
+        {
+          WriteFlash (FlashAddr, 15);   // Write 15+1 16bit words
+          WRITE_FLASH_NEXT (FlashAddr + _MEM_INC, 15);  // Write 15+1 16bit words
+
+          SET_CART_ADDR (FlashAddr);
+
+          for (i = 0; i < 32; i++)
+            {
+              WRITE_FLASH_NEXT (FlashAddr, *SrcAddr2);
+              SrcAddr2 += 2;
+              FlashAddr += _MEM_INC;
+            }
+          WRITE_FLASH_NEXT (FlashAddr, INTEL28F_CONFIRM);
+          WRITE_FLASH_NEXT (FlashAddr + _MEM_INC, INTEL28F_CONFIRM);
+
+          Ready = 0;
+          Timeout = 0x4000;
+          k = 0;
+
+          while (((k & 0x8080) != 0x8080) && (Timeout != 0))
+            {
+              READ_TURBO_S (k);
+              Ready = (k == 0x8080);
+
+              Timeout--;
+            }
+
+          if (!Ready)
+            break;
+        }
+      else
+        break;
+      LoopCount++;
+    }
+
+  WriteFlash (_CART_START, INTEL28F_READARRAY);
+  CTRL_PORT_0;
+  WriteFlash (_CART_START + _MEM_INC, INTEL28F_READARRAY);
+  CTRL_PORT_0;
+
+  if (!Ready)
+    {
+      WriteFlash (_CART_START, INTEL28F_CLEARSR);
+      WriteFlash (_CART_START + _MEM_INC, INTEL28F_CLEARSR);
+    }
+  return Ready != 0;
+}
+#endif
+
+#endif // USE_PARALLEL

+ 504 - 0
ucon64/2.0/src/backup/cd64.c

@@ -0,0 +1,504 @@
+/*
+cd64.c - CD64 support for uCON64
+
+Copyright (c) 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ultra64/host/cd64lib.h>
+#include "misc/misc.h"
+#include "misc/parallel.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_dat.h"
+#include "ucon64_misc.h"
+#include "cd64.h"
+
+
+const st_getopt2_t cd64_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "CD64"/*"19XX UFO http://www.cd64.com"*/,
+      NULL
+    },
+#if     defined USE_PARALLEL && defined USE_LIBCD64
+    {
+      "xcd64", 0, 0, UCON64_XCD64,
+      NULL, "send/receive ROM to/from CD64; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically (64 Mbits) when ROM does not exist",
+      &ucon64_wf[WF_OBJ_N64_DEFAULT_STOP_NO_ROM]
+    },
+    {
+      "xcd64c", 1, 0, UCON64_XCD64C,
+      "N", "receive N Mbits of ROM from CD64; " OPTION_LONG_S "port=PORT",
+      &ucon64_wf[WF_OBJ_N64_STOP_NO_ROM]
+    },
+    {
+      "xcd64b", 0, 0, UCON64_XCD64B,
+      NULL, "send boot emu to CD64; " OPTION_LONG_S "port=PORT",
+      &ucon64_wf[WF_OBJ_N64_DEFAULT_STOP]
+    },
+    {
+      "xcd64s", 0, 0, UCON64_XCD64S,
+      NULL, "send/receive SRAM to/from CD64; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM file does not exist",
+      &ucon64_wf[WF_OBJ_N64_STOP_NO_ROM]
+    },
+    {
+      "xcd64f", 0, 0, UCON64_XCD64F,
+      NULL, "send/receive flash RAM to/from CD64; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when flash RAM file does not exist",
+      &ucon64_wf[WF_OBJ_N64_STOP_NO_ROM]
+    },
+    {
+      "xcd64e", 0, 0, UCON64_XCD64E,
+      NULL, "send/receive EEPROM data to/from CD64; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when EEPROM file does not exist",
+      &ucon64_wf[WF_OBJ_N64_STOP_NO_ROM]
+    },
+    {
+      "xcd64m", 1, 0, UCON64_XCD64M,
+      "INDEX", "send/receive memory pack data to/from CD64; " OPTION_LONG_S "port=PORT\n"
+      "INDEX is ignored for CD64 BIOS protocol\n"
+      "receives automatically when memory pack file does not exist",
+      &ucon64_wf[WF_OBJ_N64_STOP_NO_ROM]
+    },
+    {
+      "xcd64p", 1, 0, UCON64_XCD64P,
+      "PROT", "use protocol PROT when communicating with CD64; " OPTION_LONG_S "port=PORT\n"
+      "PROT=0 CD64 BIOS\n"
+      "PROT=1 Ghemor\n"
+      "PROT=2 UltraLink",
+      &ucon64_wf[WF_OBJ_N64_SWITCH]
+    },
+#endif // USE_PARALLEL && USE_LIBCD64
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#if     defined USE_PARALLEL && defined USE_LIBCD64
+
+static time_t cd64_starttime;
+
+
+static void
+cd64_progress (uint32_t current, uint32_t total)
+{
+  ucon64_gauge (cd64_starttime, current, total);
+}
+
+
+static int
+cd64_notice_helper (FILE *file, const char *prefix, const char *format,
+                    va_list argptr)
+{
+  int n_chars;
+
+  fputs (prefix, file);
+  n_chars = vfprintf (file, format, argptr);
+  fputc ('\n', file);
+  fflush (file);
+
+  return n_chars;
+}
+
+
+static int
+cd64_notice (const char *format, ...)
+{
+  va_list argptr;
+  int n_chars;
+
+  va_start (argptr, format);
+  n_chars = cd64_notice_helper (stdout, "NOTE (libcd64): ", format, argptr);
+  va_end (argptr);
+
+  return n_chars;
+}
+
+
+static int
+cd64_notice2 (const char *format, ...)
+{
+  va_list argptr;
+  int n_chars;
+
+  va_start (argptr, format);
+  n_chars = cd64_notice_helper (stderr, "ERROR (libcd64): ", format, argptr);
+  va_end (argptr);
+
+  return n_chars;
+}
+
+
+static int
+fread_wrapper (void *io_id, void *buffer, uint32_t size)
+{
+  return fread (buffer, 1, size, (FILE *) io_id);
+}
+
+
+static int
+fwrite_wrapper (void *io_id, void *buffer, uint32_t size)
+{
+  return fwrite (buffer, 1, size, (FILE *) io_id);
+}
+
+
+static int32_t
+ftell_wrapper (void *io_id)
+{
+  return (int32_t) ftell ((FILE *) io_id);
+}
+
+
+static int
+fseek_wrapper (void *io_id, int32_t offset, int whence)
+{
+  return fseek ((FILE *) io_id, offset, whence);
+}
+
+
+static struct cd64_t *
+cd64_init (void)
+{
+  struct cd64_t *cd64;
+#ifdef  USE_PPDEV
+  uint16_t port = strtol (&ucon64.parport_dev[strlen (ucon64.parport_dev) - 1], NULL, 10);
+  method_t method = PPDEV;
+#else
+  uint16_t port = ucon64.parport;
+  method_t method = RAWIO;
+#endif
+  int is_parallel = 1;
+
+  if ((cd64 = (struct cd64_t *) calloc (1, sizeof (struct cd64_t))) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[BUFFER_ERROR], sizeof (struct cd64_t));
+      exit (1);
+    }
+
+#ifndef USE_PPDEV
+  if (ucon64.parport == UCON64_UNKNOWN)
+    {
+      fputs ("ERROR: No port or invalid port specified\n"
+             "TIP:   Specify one with --port or in the configuration file\n", stderr);
+      exit (1);
+    }
+  if (port >= 0x300 && port <= 0x330)
+    is_parallel = 0;
+#endif
+
+  cd64->notice_callback = cd64_notice;
+  cd64->notice_callback2 = cd64_notice2;
+
+  if (!cd64_create (cd64, method, port, (protocol_t) ucon64.io_mode, is_parallel))
+    {
+      fputs ("ERROR: Could not initialise libcd64\n", stderr);
+      exit (1);
+    }
+
+  cd64->read_callback = fread_wrapper;          // actually f*2(), if zlib
+  cd64->write_callback = fwrite_wrapper;        //  support is enabled
+  cd64->tell_callback = ftell_wrapper;
+  cd64->seek_callback = fseek_wrapper;
+  cd64->progress_callback = cd64_progress;
+  strcpy (cd64->io_driver_dir, ucon64.configdir);
+
+  // parport_print_info() displays a reasonable message (even if we're using a
+  //  comms link)
+  parport_print_info ();
+
+  if (!cd64->devopen (cd64))
+    {
+      fputs ("ERROR: Could not open I/O device for CD64\n", stderr);
+      exit (1);
+    }
+#if     defined __unix__ && !defined __MSDOS__
+  drop_privileges ();
+#endif
+
+  return cd64;
+}
+
+
+int
+cd64_read_rom (const char *filename, int size)
+{
+  FILE *file;
+  struct cd64_t *cd64 = cd64_init ();
+
+  if ((file = fopen (filename, "w+b")) == NULL) // cd64_download_cart() also
+    {                                           //  reads from file
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+
+  cd64_starttime = time (NULL);
+  cd64_download_cart (cd64, file, size * MBIT, NULL);
+
+  cd64->devclose (cd64);
+  fclose (file);
+  free (cd64);
+
+  return 0;
+}
+
+
+int
+cd64_write_rom (const char *filename)
+{
+  FILE *file;
+  struct cd64_t *cd64 = cd64_init ();
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+
+  cd64_starttime = time (NULL);
+  cd64_upload_dram (cd64, file, ucon64.file_size, NULL, 1);
+
+  cd64->devclose (cd64);
+  fclose (file);
+  free (cd64);
+
+  return 0;
+}
+
+
+int
+cd64_write_bootemu (const char *filename)
+{
+  FILE *file;
+  struct cd64_t *cd64 = cd64_init ();
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+
+  cd64_starttime = time (NULL);
+  cd64_upload_bootemu (cd64, file, ucon64.file_size, NULL);
+
+  cd64->devclose (cd64);
+  fclose (file);
+  free (cd64);
+
+  return 0;
+}
+
+
+int
+cd64_read_sram (const char *filename)
+{
+  FILE *file;
+  struct cd64_t *cd64 = cd64_init ();
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+
+  cd64_starttime = time (NULL);
+  cd64_download_sram (cd64, file);
+
+  cd64->devclose (cd64);
+  fclose (file);
+  free (cd64);
+
+  return 0;
+}
+
+
+int
+cd64_write_sram (const char *filename)
+{
+  FILE *file;
+  struct cd64_t *cd64 = cd64_init ();
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+
+  cd64_starttime = time (NULL);
+  cd64_upload_sram (cd64, file);
+
+  cd64->devclose (cd64);
+  fclose (file);
+  free (cd64);
+
+  return 0;
+}
+
+
+int
+cd64_read_flashram (const char *filename)
+{
+  FILE *file;
+  struct cd64_t *cd64 = cd64_init ();
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+
+  cd64_starttime = time (NULL);
+  cd64_download_flashram (cd64, file);
+
+  cd64->devclose (cd64);
+  fclose (file);
+  free (cd64);
+
+  return 0;
+}
+
+
+int
+cd64_write_flashram (const char *filename)
+{
+  FILE *file;
+  struct cd64_t *cd64 = cd64_init ();
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+
+  cd64_starttime = time (NULL);
+  cd64_upload_flashram (cd64, file);
+
+  cd64->devclose (cd64);
+  fclose (file);
+  free (cd64);
+
+  return 0;
+}
+
+
+int
+cd64_read_eeprom (const char *filename)
+{
+  FILE *file;
+  struct cd64_t *cd64 = cd64_init ();
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+
+  cd64_starttime = time (NULL);
+  cd64_download_eeprom (cd64, file);
+
+  cd64->devclose (cd64);
+  fclose (file);
+  free (cd64);
+
+  return 0;
+}
+
+
+int
+cd64_write_eeprom (const char *filename)
+{
+  FILE *file;
+  struct cd64_t *cd64 = cd64_init ();
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+
+  cd64_starttime = time (NULL);
+  cd64_upload_eeprom (cd64, file);
+
+  cd64->devclose (cd64);
+  fclose (file);
+  free (cd64);
+
+  return 0;
+}
+
+
+int
+cd64_read_mempack (const char *filename, int index)
+{
+  FILE *file;
+  struct cd64_t *cd64 = cd64_init ();
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+
+  if (ucon64.io_mode == CD64BIOS)
+    index = -1;
+  cd64_starttime = time (NULL);
+  cd64_download_mempak (cd64, file, (int8_t) index);
+
+  cd64->devclose (cd64);
+  fclose (file);
+  free (cd64);
+
+  return 0;
+}
+
+
+int
+cd64_write_mempack (const char *filename, int index)
+{
+  FILE *file;
+  struct cd64_t *cd64 = cd64_init ();
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+
+  if (ucon64.io_mode == CD64BIOS)
+    index = -1;
+  cd64_starttime = time (NULL);
+  cd64_upload_mempak (cd64, file, (int8_t) index);
+
+  cd64->devclose (cd64);
+  fclose (file);
+  free (cd64);
+
+  return 0;
+}
+
+#endif // USE_PARALLEL && USE_LIBCD64

+ 41 - 0
ucon64/2.0/src/backup/cd64.h

@@ -0,0 +1,41 @@
+/*
+cd64.h - CD64 support for uCON64
+
+Copyright (c) 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef CD64_H
+#define CD64_H
+
+extern const st_getopt2_t cd64_usage[];
+
+#if     defined USE_PARALLEL && defined USE_LIBCD64
+extern int cd64_read_rom(const char *filename, int size);
+extern int cd64_write_rom(const char *filename);
+extern int cd64_write_bootemu (const char *filename);
+extern int cd64_read_sram(const char *filename);
+extern int cd64_write_sram(const char *filename);
+extern int cd64_read_flashram(const char *filename);
+extern int cd64_write_flashram(const char *filename);
+extern int cd64_read_eeprom(const char *filename);
+extern int cd64_write_eeprom(const char *filename);
+extern int cd64_read_mempack(const char *filename, int index);
+extern int cd64_write_mempack(const char *filename, int index);
+#endif // USE_PARALLEL && USE_LIBCD64
+
+#endif // CD64_H

+ 985 - 0
ucon64/2.0/src/backup/cmc.c

@@ -0,0 +1,985 @@
+/*
+cmc.c - Cyan's Megadrive ROM copier support for uCON64
+
+Copyright (c) 1999 - 2004 Cyan Helkaraxe
+
+Special thanks to dbjh for helping with the uCON64 integration
+of this software, and providing the wrapping code.
+
+CMC version: 2.5
+For hardware version 1.x
+
+Copies Sega Megadrive/Genesis cartridges into .BIN format ROM files.
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+Additional information:
+This software is distributed in accordance with the GNU General
+Public License. The author of the hardware design/documentation and
+software, Cyan Helkaraxe, retains the copyright over the 'cmc' code
+('software'), the hardware design and construction documentation itself.
+Cyan grants you the rights of the GNU General Public License over
+the software, as stated above. The copyright does not affect your
+rights in relation to the 'cmc' code under the GNU General Public
+License in any way.
+Cyan Helkaraxe does NOT grant you any rights relating to the hardware
+design/documentation, over and above the right to build the device for
+personal, not-for-profit use. Likewise, the same applies to the ROM
+copier construction documentation, available at
+http://www.emulationzone.org/projects/cyan/docs/ for not-for-profit
+personal use.
+
+Obviously, feel free to make changes to this software, but if you do so,
+*please* clearly mark the fact it is modified, to avoid confusion.
+A define is provided below this commented area, which should be edited
+as described. This is to inform users which version of the code they are
+using at runtime, and if they encounter a problem, it makes it far easier
+for me to help debug it. I have no evil nefarious intentions. =P
+
+Obviously, your changes must adhere to the GNU GPL, and please keep the
+copyright, Cyan's name, e-mail and web site address in the comments
+somewhere.
+
+If you wish to do anything with the hardware design or the documentation
+that goes beyond personal use, get in touch with Cyan first;
+cyan@emulationzone.org
+
+Disclaimer / Warranty:
+This is to emphasise, not replace, any warranty information provided in
+the GNU GPL or uCON64.
+There is no warranty whatsoever, for either the software or accompanying
+hardware/design/documentation. This software is provided free of charge,
+AS-IS, in the hope that it may be useful.
+Use it at your own risk. The author does not make any guarantee that you
+will be able to use this software or accompanying
+hardware/design/documentation, or that it will perform smoothly. There is
+a possibility that damage or loss (including but not limited to financial
+loss, hardware or software damage, data corruption, privacy violation,
+or personal injury, suffering, legal action, imprisonment or death) may
+arise through the use of this software or the accompanying
+hardware/design/documentation, in addition to many other possible outcomes.
+You take sole responsibility for any outcomes; by using this software or
+accompanying hardware/design/ documentation, you agree that the author will
+not be held responsible for anything that may occur. If your jurisdiction
+does not allow the author to be isolated from responsibility, then you
+must *not* use this software or accompanying hardware/design/documentation.
+The author does not condone software piracy. You may not use this software
+to engage in piracy. The author is not responsible for anything you choose
+to use this software for, although the author strongly recommends that you
+use the software for the purpose for which it was intended to be used --
+primarily as an educational tool, and secondarily to make backup copies of
+your expensive/rare game cartridges, or a similarly harmless and legal
+purpose.
+Note that although the author isn't aware of any patent violations caused
+by this software/hardware/design/documentation, it is possible that
+there may be patents covering parts of this device. It is your
+responsibility to check this before building or using the hardware or
+software, and Cyan may not be held responsible in the event of an
+infringement.
+
+That being said, if you do encounter any problems with the hardware or
+software, then feel free to get in touch with the author; use the subject
+line 'ROM Copier Technical Support'. No promises or guarantees are made,
+however.
+
+Also note that the author is not affiliated with anyone involved with uCON64;
+therefore, only correspondence relating to this particular file (the 'cmc'
+code) or the accompanying hardware design should be directed to Cyan.
+If you have more general uCON64 questions, Cyan is *not* the person to ask.
+Likewise, the terms "the author" and "software" in this file (cmc.c, and
+additionally cmc.h), along with similar terms, apply only to Cyan, and the
+CMC software you see in this file. The disclaimer above, for example, relates
+exclusively to the CMC code.
+
+All trademarks, indicated or otherwise, are the property of their
+respective owners.
+*/
+
+/*
+  NOTE!
+  Please edit the following line, and remove the word "original" from it
+  if you have made any modifications to this file. This reduces user
+  confusion.
+*/
+#define INTRO_TEXT "Cyan's Megadrive Copier             (c) 1999-2004 Cyan Helkaraxe\n" \
+                   "Software version 2.5 original, designed for hardware version 1.x\n\n"
+
+
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "misc/bswap.h"
+#include "misc/misc.h"
+#include "misc/parallel.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "cmc.h"
+
+#ifdef  USE_PARALLEL
+
+#define RSLO 0x40                               // reset line 1 d7
+#define RSHI 0x01                               // reset line 2 d0
+#define CNLO 0x10                               // counter line 1 d4
+#define CNHI 0x04                               // counter line 2 d2
+#define INLO 0x40                               // input 1 ack (int disabled)
+#define INHI 0x10                               // input 2 selectin
+
+#define MBYTE (1024 * 1024)
+#define DEFAULT_SPEED 3
+
+#ifdef  _MSC_VER
+// Visual C++ doesn't allow inline in C source code
+#define inline __inline
+#endif
+
+
+/************************
+ *  Internal functions  *
+ ************************/
+
+static inline void
+cyan_write_copier (unsigned char data, unsigned int parport)
+// write a value to the data register of the parallel port
+{
+  outportb ((unsigned short) (parport + PARPORT_DATA), data);
+}
+
+
+static inline unsigned char
+cyan_read_copier (unsigned int parport)
+// read a value from the status register of the parallel port
+{
+  return inportb ((unsigned short) (parport + PARPORT_STATUS));
+}
+
+
+static inline unsigned char
+cyan_verify_copier (unsigned int parport)
+// read a value back from the data register for verification
+{
+  return inportb ((unsigned short) (parport + PARPORT_DATA));
+}
+
+
+/**** non-hardware, non-accessing ****/
+
+static unsigned long
+cyan_calculate_rom_size (unsigned char *buffer, int test_mode)
+/*
+  Calculate the ROM size, by looking at the ROM size entry in the ROM 'header',
+  and the overall structure.
+  This function always returns a value rounded to a power of two between 128
+  kbytes and 4 Mbytes. It also inspects the ROM for 0's or ffh's. If test_mode
+  is 1 it causes an error on that condition, frees the buffer and exits.
+*/
+{
+  unsigned long i = 0x80000000, reported_size;
+
+  // look at reported size
+  reported_size = (buffer[0x1a4] << 24) +
+                  (buffer[0x1a5] << 16) +
+                  (buffer[0x1a6] << 8) +
+                   buffer[0x1a7] + 1;
+  // cap
+  // there is a minimum valid size for ROMs, according to some sources
+  if (reported_size < MBIT)
+    reported_size = MBIT;
+  if (reported_size > 4 * MBYTE)
+    reported_size = 4 * MBYTE;
+  // round
+  if (reported_size & (reported_size - 1))
+    {
+      while (!(reported_size & 0x80000000))
+        {
+          i >>= 1;
+          reported_size = (reported_size << 1) | 1;
+        }
+      reported_size = i << 1;
+    }
+  // calculate real size
+  for (i = 2 * MBYTE; i >= 65536; i >>= 1)
+    if (memcmp (buffer, buffer + i, i))
+      {
+        i >>= 1;
+        break;
+      }
+  i <<= 2;
+  if (reported_size < i)
+    reported_size = i;                          // pick the safest (largest) of the two
+  if (i == MBIT)
+    {
+      for (i = 0; i < MBIT; i++)
+        if ((buffer[i] != 0xff) && (buffer[i] != 0x00))
+          break;
+      if (i == MBIT)
+        {
+          FILE *output;
+
+          if (test_mode)
+            {
+              output = stderr;
+              fputs ("\nERROR:   ", stderr);
+            }
+          else
+            {
+              output = stdout;
+              fputs ("\nWARNING: ", stdout);
+            }
+
+          //     "WARNING: "
+          fputs (         "The ROM file appears to consist of nothing but 0x00 / 0xff values.\n"
+                 "         This usually indicates a serious problem. Perhaps your parallel port\n"
+                 "         isn't configured correctly, or there is some problem with the ROM\n"
+                 "         copier. Is it getting power? Is a cartridge inserted? Is it properly\n"
+                 "         attached to the PC?\n",
+                 output);
+          if (test_mode)
+            {
+              free (buffer);
+              exit (1);
+            }
+        }
+    }
+  return reported_size;
+}
+
+
+static int
+cyan_checksum_rom (unsigned char *buffer)
+// return 0 on success, -1 on failure
+{
+  unsigned char *buffer2 = buffer;
+  unsigned short reported_sum, running_sum = 0;
+
+  reported_sum = (buffer2[0x18e] << 8) + buffer2[0x18f];
+  buffer2 += ((buffer2[0x1a4] << 24) +
+              (buffer2[0x1a5] << 16) +
+              (buffer2[0x1a6] << 8) +
+              (buffer2[0x1a7] & 0xfe)) + 2;
+  if (buffer2 > buffer + 4 * MBYTE)
+    buffer2 = buffer + 4 * MBYTE;
+  buffer += 0x200;
+  if (buffer2 < buffer + 2)
+    return -1;
+
+  while (buffer2 != buffer)
+    {
+      running_sum += *--buffer2;
+      running_sum += (*--buffer2) << 8;
+    }
+
+  return (running_sum & 0xffff) != reported_sum ? -1 : 0;
+}
+
+
+static inline unsigned long
+cyan_get_address (unsigned long b)
+// return the true address (word -- 0 - 2 M) based on word input
+{
+  return ((b & 0x000800) >> 11) |               // bit 0
+         ((b & 0x002000) >> 12) |               // bit 1
+         ((b & 0x004000) >> 12) |               // bit 2
+         ((b & 0x000020) >> 2) |                // bit 3
+         ((b & 0x100000) >> 16) |               // bit 4
+         ((b & 0x020000) >> 12) |               // bit 5
+         ((b & 0x000400) >> 4) |                // bit 6
+         ((b & 0x000001) << 7) |                // bit 7
+         ((b & 0x000002) << 7) |                // bit 8
+         ((b & 0x000010) << 5) |                // bit 9
+         ((b & 0x000040) << 4) |                // bit 10
+         ((b & 0x040000) >> 7) |                // bit 11
+         ((b & 0x080000) >> 7) |                // bit 12
+         ((b & 0x000080) << 6) |                // bit 13
+         ((b & 0x008000) >> 1) |                // bit 14
+         ((b & 0x010000) >> 1) |                // bit 15
+         ((b & 0x001000) << 4) |                // bit 16
+         ((b & 0x000004) << 15) |               // bit 17
+         ((b & 0x000008) << 15) |               // bit 18
+         ((b & 0x000200) << 10) |               // bit 19
+         ((b & 0x000100) << 12);                // bit 20
+}
+
+
+/**** non-hardware, indirectly accessing ****/
+
+static inline void
+cyan_delay (int speed, unsigned int parport)
+// Delays a certain amount of time depending on speed selected. 0=long delay,
+//  used for reset and hi counter.
+{
+  int i, scritch = 0;
+
+  switch (speed)
+    {
+    case 0:
+      for (i = 0; i < 128; i++)
+        scritch += cyan_read_copier (parport);
+    case 1:                                     // falling through
+      for (i = 0; i < 64; i++)
+        scritch += cyan_read_copier (parport);
+    case 2:                                     // falling through
+      for (i = 0; i < 12; i++)
+        scritch += cyan_read_copier (parport);
+    case 3:                                     // falling through
+      scritch += cyan_read_copier (parport);
+      scritch += cyan_read_copier (parport);
+    }
+}
+
+
+static void
+cyan_reset (unsigned int parport)
+// resets the copier
+{
+  cyan_delay (0, parport);
+  // zero all data outputs first, before going into SPP mode
+  cyan_write_copier (0, parport);
+  // reset the port to SPP, float all control lines high
+  cyan_write_copier (0, parport + PARPORT_CONTROL);
+  cyan_delay (0, parport);
+  cyan_write_copier (RSLO | RSHI, parport);     // both reset lines hi
+  cyan_delay (0, parport);
+  cyan_write_copier (0, parport);               // both reset lines lo
+  cyan_delay (0, parport);
+}
+
+
+static inline unsigned short
+cyan_get_word (int speed, unsigned int parport)
+// gets a byte pair from the ROM and return two bytes in big endian byte order
+{
+  unsigned short value = 0;
+  unsigned char tempz;
+
+  cyan_write_copier (0, parport);
+  cyan_delay (speed, parport);
+  tempz = cyan_read_copier (parport);
+  value |= tempz & INLO;                        // bit 6
+  value |= (tempz & INHI) << 8;                 // bit 12
+
+  cyan_write_copier (CNLO, parport);
+  cyan_delay (speed, parport);
+  tempz = cyan_read_copier (parport);
+  value |= (tempz & INLO) >> 5;                 // bit 1
+  value |= (tempz & INHI) << 9;                 // bit 13
+
+  cyan_write_copier (0, parport);
+  cyan_delay (speed, parport);
+  tempz = cyan_read_copier (parport);
+  value |= (tempz & INLO) << 3;                 // bit 9
+  value |= (tempz & INHI) << 10;                // bit 14
+
+  cyan_write_copier (CNLO, parport);
+  cyan_delay (speed, parport);
+  tempz = cyan_read_copier (parport);
+  value |= (tempz & INLO) >> 1;                 // bit 5
+  value |= (tempz & INHI) << 11;                // bit 15
+
+  cyan_write_copier (0, parport);
+  cyan_delay (speed, parport);
+  tempz = cyan_read_copier (parport);
+  value |= (tempz & INLO) >> 4;                 // bit 2
+  value |= (tempz & INHI) << 4;                 // bit 8
+
+  cyan_write_copier (CNLO, parport);
+  cyan_delay (speed, parport);
+  tempz = cyan_read_copier (parport);
+  value |= (tempz & INLO) << 4;                 // bit 10
+  value |= (tempz & INHI) >> 4;                 // bit 0
+
+  cyan_write_copier (0, parport);
+  cyan_delay (speed, parport);
+  tempz = cyan_read_copier (parport);
+  value |= (tempz & INLO) >> 2;                 // bit 4
+  value |= (tempz & INHI) << 3;                 // bit 7
+
+  cyan_write_copier (CNLO, parport);
+  cyan_delay (speed, parport);
+  tempz = cyan_read_copier (parport);
+  value |= (tempz & INLO) >> 3;                 // bit 3
+  value |= (tempz & INHI) << 7;                 // bit 11
+
+  return me2be_16 (value);
+}
+
+
+static inline int
+check_exit (void)
+// check for user abort
+{
+  int temp;
+
+  if (ucon64.frontend)
+    return 0;
+  if (!kbhit ())
+    return 0;
+  temp = getch ();
+  if (temp == 'q' || (temp == 27))
+    return 1;
+  return 0;
+}
+
+
+static unsigned char *
+cyan_read_rom (int speed, unsigned int parport, unsigned char *buffer)
+/*
+  Read the ROM and return a pointer to a 4 MB area of memory containing all ROM
+  data. Designed to be used from inside cyan_copy_rom(), although it can be
+  called elsewhere if a raw (but decoded) dump is required.
+*/
+{
+  unsigned long q;
+  time_t t;
+
+  // allocate the dump area
+  if (!buffer)
+    if ((buffer = (unsigned char *) malloc (4 * MBYTE)) == NULL)
+      {
+        fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], 4 * MBYTE);
+        exit (1);
+      }
+
+  cyan_reset (parport);                         // reset the copier
+
+  t = time (NULL);
+  // copy routine
+  for (q = 0; q < 2 * MBYTE; )                  // loop through all words
+    {
+      // get a (16-bit) word from the ROM
+      ((unsigned short *) buffer)[cyan_get_address (q)] = cyan_get_word (speed, parport);
+
+      // periodically update progress bar, without hammering ucon64_gauge()
+      if (!(q & (0xfff >> (5 - speed))))
+        {
+          if (check_exit ())
+            {
+              free (buffer);
+              puts ("\n"
+                    "Copy aborted.\n"
+                    "Don't forget to turn the ROM copier off and never insert or remove a cartridge\n"
+                    "with the power on");
+              break;
+            }
+          ucon64_gauge (t, q * 2, 4 * MBYTE);
+        }
+
+      if (!(++q & 0x3ff)) // advance loop counter and carry to hi counter (11 bits)
+        {
+          cyan_delay (0, parport);
+          cyan_write_copier (CNHI, parport);
+          cyan_delay (0, parport);
+          cyan_write_copier (0, parport);
+          cyan_delay (0, parport);
+        }
+    }
+
+  // make sure it's left in a state where it's safe to remove the cart
+  cyan_reset (parport);
+
+  if (q != 2 * MBYTE)
+    return NULL;
+
+  ucon64_gauge (t, q * 2, 4 * MBYTE);           // make the progress bar reach 100%
+
+  return buffer;
+}
+
+
+static void
+cyan_test_parport (unsigned int parport)
+// Test the parallel port to see if it appears to be functioning correctly, and
+//  terminate if there's an error.
+{
+  unsigned short temp;
+
+  cyan_reset (parport);
+  fputs ("Basic parallel port test: ", stdout);
+  fflush (stdout);
+  cyan_write_copier (170, parport);
+  cyan_delay (0, parport);
+  temp = cyan_verify_copier (parport) & 170;
+  cyan_reset (parport);
+
+  // even in unidirectional mode, the parallel port is bidirectional; at
+  //  least, for a few short moments before the driver IC smokes
+  if ((cyan_verify_copier (parport) & 170) != 0 || temp != 170)
+    {
+      puts ("FAILED");
+      fputs ("ERROR: Parallel port error\n"
+             "       Check that your parallel port is configured properly, in the BIOS, OS,\n"
+             "       and uCON64, and check for short circuits on the parallel port connector.\n"
+             "       Also ensure that the ROM copier is getting power, and a cartridge is\n"
+             "       inserted\n",
+             stderr);
+      exit (1);
+    }
+  else
+    puts ("Passed");
+
+  // discharge caps to see if we've got power
+  cyan_reset (parport);
+  cyan_reset (parport);
+  cyan_write_copier (CNLO + CNHI, parport);
+  cyan_delay (0, parport);
+  for (temp = 0; temp < 1000; temp++)
+    {
+      cyan_write_copier (0, parport);
+      cyan_delay (3, parport);
+      cyan_write_copier (CNLO + CNHI, parport);
+      cyan_delay (3, parport);
+    }
+  cyan_reset (parport);
+  cyan_reset (parport);
+
+  fputs ("Parallel port output test: ", stdout);
+  fflush (stdout);
+  cyan_write_copier (255, parport);
+  cyan_delay (0, parport);
+  temp = (cyan_verify_copier (parport) != 255);
+  cyan_write_copier (0, parport);
+  cyan_delay (0, parport);
+  temp |= (cyan_verify_copier (parport) != 0);
+  cyan_write_copier (CNLO, parport);
+  cyan_delay (0, parport);
+  temp |= (cyan_verify_copier (parport) != CNLO);
+  cyan_write_copier (CNHI, parport);
+  cyan_delay (0, parport);
+  temp |= (cyan_verify_copier (parport) != CNHI);
+  cyan_write_copier (RSLO, parport);
+  cyan_delay (0, parport);
+  temp |= (cyan_verify_copier (parport) != RSLO);
+  cyan_write_copier (RSHI, parport);
+  cyan_delay (0, parport);
+  temp |= (cyan_verify_copier (parport) != RSHI);
+  cyan_reset (parport);
+
+  // if it's still okay after that, then try reading the first set of inputs
+  //  with lines high and low
+  if (!temp)
+    {
+      fputs ("Passed\n"
+             "Input crosstalk test: ",
+             stdout);
+      fflush (stdout);
+      temp = cyan_read_copier (parport) & (INLO | INHI);
+      cyan_write_copier (255 - CNLO, parport);
+      cyan_delay (0, parport);
+      temp = (temp != (cyan_read_copier (parport) & (INLO | INHI)));
+      cyan_reset (parport);
+    }
+
+  if (temp)
+    {
+      puts ("FAILED");
+      fputs ("ERROR: Parallel port error\n"
+             "Possible causes: ROM copier not getting power (check or replace battery)\n"
+             "                 Short circuit or bad connection (on parallel port or board)\n"
+             "                 Cartridge not inserted properly (or not inserted at all)\n"
+             "                 Parallel port not configured correctly\n"
+             "                 Orange, grey or green wire(s) soldered to the wrong locations\n"
+             "                 Chips inserted backwards\n"
+             "NOTE: Don't forget the ROM copier needs to be turned on before starting!\n",
+             stderr);
+      exit (1);
+    }
+  else
+    puts ("Passed");
+}
+
+
+static int
+cyan_test_copier (int test, int speed, unsigned int parport)
+{
+  unsigned char *buffer1, *buffer2 = NULL;
+  int count = 1;
+
+  fputs (INTRO_TEXT, stdout);
+  parport_print_info ();
+
+  switch (test)
+    {
+    // reliability test -- note: this test may be required to run for 8 hours or more
+    case 1:
+      printf ("Reliability test mode selected, speed %d\n", speed);
+      cyan_test_parport (parport);
+      puts ("\n"
+            "Entering non-stop reliability test mode (press escape or q to exit, and turn\n"
+            "ROM copier off immediately afterwards)\n"
+            "\n"
+            "Copy process will continue indefinitely until an error is encountered, at\n"
+            "which point the program will terminate.\n"
+            "A large number of passes suggests that the copier is working reliably at the\n"
+            "selected speed\n");
+      printf ("                                                                          P %2d",
+              count);
+      fflush (stdout);
+      if (ucon64.frontend)
+        fputc ('\n', stdout);
+      buffer1 = cyan_read_rom (speed, parport, NULL);
+      if (!buffer1)                             // user abort
+        exit (0);
+
+      // detect if ROM is all 0x00 or 0xff and print an error if so
+      cyan_calculate_rom_size (buffer1, 1);
+
+      while (1)
+        {
+          clear_line ();                        // remove last gauge
+          printf ("   Pass %2d OK\n", count);
+          count++;
+
+          // verify checksum of first pass
+          if (count == 2)                       // check only in first iteration
+            if (cyan_checksum_rom (buffer1))    // verify checksum
+              puts ("\n"
+                    "WARNING: Checksum of ROM does not appear to be correct.\n"
+                    "         This may be normal for this ROM, or it may indicate a bad copy\n");
+
+          printf ("                                                                          P %2d",
+                  count);
+          fflush (stdout);
+          if (ucon64.frontend)
+            fputc ('\n', stdout);
+          buffer2 = cyan_read_rom (speed, parport, buffer2);
+          if (!buffer2)
+            {
+              free (buffer1);
+              exit (0);
+            }
+          if (memcmp (buffer1, buffer2, 4 * MBYTE))
+            {
+              // error
+              printf ("\n"
+                      "\n"
+                      "Error detected on pass number %d\n"
+                      "\n",
+                      count);
+              if (count == 2)
+                puts ("A failure this early suggests a critical fault, such as a misconfigured or\n"
+                      "incompatible parallel port, extremely poor wiring, or power supply problems --\n"
+                      "you may wish to replace the battery or try another power supply, and use\n"
+                      "shorter cables.\n"
+                      "Try lowering the speed and running this test again, as a too high speed can\n"
+                      "often cause these symptoms.\n"
+                      "Alternatively, it may have been a one-time glitch; re-run the test to be sure.\n"
+                      "When (if?) you find a lower speed which works reliably, use that speed for\n"
+                      "copying ROMs\n");
+              else
+                puts ("The first couple of passes were successful. This indicates that you have a\n"
+                      "minor intermittent problem; most likely power supply problems, bad wiring, or\n"
+                      "some kind of one-time glitch.\n"
+                      "You may wish to replace the battery or try another power supply, and use\n"
+                      "shorter cables.\n"
+                      "Make sure no electrical appliances turn on or off during the copy.\n"
+                      "Re-run the test to be sure; it's recommended that you use a lower speed\n");
+              free (buffer1);
+              free (buffer2);
+              exit (1);
+            }
+        }
+
+      free (buffer1);
+      free (buffer2);
+      break;
+    // manual test
+    case 2:
+      cyan_reset (parport);
+      cyan_write_copier (CNHI, parport);
+      cyan_delay (0, parport);
+      cyan_write_copier (0, parport);
+      cyan_delay (0, parport);
+
+      if (speed != DEFAULT_SPEED)
+        puts ("Ignoring specified speed; test bench mode does not require a speed setting");
+      // print screen
+      puts ("Entering manual test bench mode\n"
+            "\n"
+            "Probe the board and verify that the counters are being clocked, and are\n"
+            "counting correctly. The upper counter should be one count ahead of the lower\n"
+            "counter, with both clocked at the same rate.\n"
+            "Inject logic levels into the multiplexers to verify that the data bits are\n"
+            "being read correctly:\n"
+            "*=high .=low, layout: L H L H L H L H (L=low multiplexer, H=high multiplexer)\n"
+            "NOTE: The signals in question are the chip native signals; D0 below corresponds\n"
+            "to D0 on the multiplexer, NOT D0 on the cartridge port. Likewise with the\n"
+            "address lines. The input lines are in counter order, left to right.\n"
+            "Press escape or q to exit; be sure to turn the ROM copier off immediately after\n"
+            "exiting, to reset the device.\n"
+            "\n"
+            "If the above didn't make any sense to you, press escape or q and turn the ROM\n"
+            "copier off immediately!\n"
+            "This test is designed for advanced users only\n");
+      while (1)
+        {
+          const char *status[2] = {"* ", ". "};
+
+          fputc ('\r', stdout);
+
+          cyan_write_copier (0, parport);
+          cyan_delay (1, parport);
+          count = cyan_read_copier (parport);
+          fputs (status[((count ^ INLO) >> 6) & 1], stdout);
+          fputs (status[((count ^ INHI) >> 4) & 1], stdout);
+
+          cyan_write_copier (CNLO | CNHI, parport);
+          cyan_delay (1, parport);
+          count = cyan_read_copier (parport);
+          fputs (status[((count ^ INLO) >> 6) & 1], stdout);
+          fputs (status[((count ^ INHI) >> 4) & 1], stdout);
+
+          cyan_write_copier (0, parport);
+          cyan_delay (1, parport);
+          count = cyan_read_copier (parport);
+          fputs (status[((count ^ INLO) >> 6) & 1], stdout);
+          fputs (status[((count ^ INHI) >> 4) & 1], stdout);
+
+          cyan_write_copier (CNLO | CNHI, parport);
+          cyan_delay (1, parport);
+          count = cyan_read_copier (parport);
+          fputs (status[((count ^ INLO) >> 6) & 1], stdout);
+          fputs (status[((count ^ INHI) >> 4) & 1], stdout);
+
+          cyan_write_copier (0, parport);
+          cyan_delay (1, parport);
+          count = cyan_read_copier (parport);
+          fputs (status[((count ^ INLO) >> 6) & 1], stdout);
+          fputs (status[((count ^ INHI) >> 4) & 1], stdout);
+
+          cyan_write_copier (CNLO | CNHI, parport);
+          cyan_delay (1, parport);
+          count = cyan_read_copier (parport);
+          fputs (status[((count ^ INLO) >> 6) & 1], stdout);
+          fputs (status[((count ^ INHI) >> 4) & 1], stdout);
+
+          cyan_write_copier (0, parport);
+          cyan_delay (1, parport);
+          count = cyan_read_copier (parport);
+          fputs (status[((count ^ INLO) >> 6) & 1], stdout);
+          fputs (status[((count ^ INHI) >> 4) & 1], stdout);
+
+          cyan_write_copier (CNLO | CNHI, parport);
+          cyan_delay (1, parport);
+          count = cyan_read_copier (parport);
+          fputs (status[((count ^ INLO) >> 6) & 1], stdout);
+          fputs (status[((count ^ INHI) >> 4) & 1], stdout);
+
+          cyan_write_copier (0, parport);
+          cyan_delay (1, parport);
+          fflush (stdout);
+
+          if (check_exit ())
+            {
+              cyan_reset (parport);
+              puts ("\nUser aborted test");
+              exit (0);
+            }
+        }
+      break;
+    default: // cmc_test() should only pass a correct speed value
+      fputs ("INTERNAL ERROR: Invalid test number passed to cyan_test_copier()\n", stderr);
+      exit (1);
+    }
+  return 0;
+}
+
+
+static int
+cyan_copy_rom (const char *filename, int speed, unsigned int parport)
+/*
+  Copy a ROM file -- this assumes the filename is valid and the file does not
+  already exist, since it will blindly try to write (overwrite) the filename you
+  give it.
+  If the open failed due to an invalid filename or path, it prints an error.
+  Speed setting should be between 1-4, 3 is default, and this is verified.
+*/
+{
+  unsigned long romsize;
+  unsigned char *buffer;
+  FILE *f;
+
+  fputs (INTRO_TEXT, stdout);
+  parport_print_info ();
+
+  if (!strlen (filename))
+    {
+      fputs ("ERROR: Filename not specified\n"
+             "       You must specify a filename on the commandline, as follows:\n"
+             "       ucon64 " OPTION_LONG_S "xcmc dump.bin\n", stderr);
+      exit (1);
+    }
+
+  printf ("Speed %d selected\n", speed);
+  cyan_test_parport (parport);
+  printf ("Destination file: %s\n", filename);
+
+  if ((f = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  fclose (f);
+
+  puts ("NOTE: Dumping copier's full address space (file will be automatically trimmed\n"
+        "      after dumping)\n"
+        "Press escape or q to abort\n");
+
+  buffer = cyan_read_rom (speed, parport, NULL);
+  if (!buffer)
+    {
+      remove (filename);
+      exit(0);
+    }
+
+  fputc ('\n', stdout);
+  romsize = cyan_calculate_rom_size (buffer, 0);
+
+  fputs ("Writing ROM to disk... ", stdout);
+  fflush (stdout);
+  if ((f = fopen (filename, "wb")) == NULL)
+    {
+      puts ("FAILED");
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      free (buffer);
+      exit (1);
+    }
+  if (fwrite (buffer, 1, romsize, f) != romsize)
+    {
+      puts ("FAILED");
+      fprintf (stderr, ucon64_msg[WRITE_ERROR], filename);
+      free (buffer);
+      fclose (f);
+      exit (1);
+    }
+  fclose (f);
+  printf ("%d kBytes OK\n"
+          "Verifying checksum... ", (int) (romsize / 1024));
+  fflush (stdout);
+
+  if (cyan_checksum_rom (buffer))
+    {
+      puts ("FAILED\n"
+            "WARNING: Checksum of ROM does not appear to be correct.\n"
+            "         This may be normal for this ROM, or it may indicate a bad copy.\n"
+            "         Please verify the ROM, and consider running a copier test");
+    }
+  else
+    puts ("OK");
+
+  puts ("Copy complete!\n"
+        "Don't forget to turn the ROM copier off, and never insert or remove a\n"
+        "cartridge with the power on");
+
+  free (buffer);
+  return 0;
+
+}
+
+#endif // USE_PARALLEL
+
+
+/*******************
+ * uCON64 wrapping *
+ *******************/
+
+const st_getopt2_t cmc_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Cyan's Megadrive ROM copier"/*"1999-2004 Cyan Helkaraxe"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xcmc", 0, 0, UCON64_XCMC,
+      NULL, "receive ROM from Cyan's Megadrive ROM copier; " OPTION_LONG_S "port=PORT",
+      &ucon64_wf[WF_OBJ_GEN_STOP_NO_ROM]
+    },
+    {
+      "xcmct", 1, 0, UCON64_XCMCT,
+      "TEST", "run test TEST\n"
+      "TEST=1 burn-in reliability test (specify speed)\n"
+      "TEST=2 testbench mode (experts only)",
+      &ucon64_wf[WF_OBJ_GEN_STOP_NO_ROM]
+    },
+    {
+      "xcmcm", 1, 0, UCON64_XCMCM,
+      "SPEED", "specify transfer speed\n"
+      "SPEED=1 slow (debug)\n"
+      "SPEED=2 medium\n"
+      "SPEED=3 fast (default)\n"                // verify with value of DEFAULT_SPEED
+      "SPEED=4 full speed (risky)",
+      &ucon64_wf[WF_OBJ_GEN_SWITCH]
+    },
+#endif // USE_PARALLEL
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef  USE_PARALLEL
+
+int
+cmc_read_rom (const char *filename, unsigned int parport, int speed)
+{
+#if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
+  init_conio ();
+#endif
+
+  if (speed < 1 || speed > 4)
+    speed = DEFAULT_SPEED;
+  cyan_copy_rom (filename, speed, parport);
+
+#if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
+  deinit_conio ();
+#endif
+
+  return 0;
+}
+
+
+int
+cmc_test (int test, unsigned int parport, int speed)
+{
+#if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
+  init_conio ();
+#endif
+
+  if (test < 1 || test > 2)
+    {
+      fputs ("ERROR: Choose a test between 1 and 2 (inclusive)\n", stderr);
+      exit (1);
+    }
+  if (speed < 1 || speed > 4)
+    speed = DEFAULT_SPEED;
+  cyan_test_copier (test, speed, parport);
+
+#if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
+  deinit_conio ();
+#endif
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 32 - 0
ucon64/2.0/src/backup/cmc.h

@@ -0,0 +1,32 @@
+/*
+cmc.h - Cyan's Megadrive ROM copier support for uCON64
+
+Copyright (c) 1999-2004 Cyan Helkaraxe
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+// See cmc.c for important information
+#ifndef CMC_H
+#define CMC_H
+
+extern const st_getopt2_t cmc_usage[];
+
+#ifdef USE_PARALLEL
+extern int cmc_read_rom (const char *filename, unsigned int parport, int speed);
+extern int cmc_test (int test, unsigned int parport, int speed);
+#endif
+
+#endif

+ 139 - 0
ucon64/2.0/src/backup/dex.c

@@ -0,0 +1,139 @@
+/*
+dex.c - DexDrive support for uCON64
+
+Copyright (c) 2002 NoisyB <noisyb@gmx.net>
+Copyright (c) 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include "misc/misc.h"
+#include "misc/file.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "dex.h"
+#include "psxpblib.h"
+#include "misc/parallel.h"
+
+
+const st_getopt2_t dex_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "DexDrive"/*"19XX InterAct http://www.dexdrive.de"*/,
+      NULL
+    },
+    {
+      "xdex", 1, 0, UCON64_XDEX,
+      "N", "send/receive Block N to/from DexDrive; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT_STOP_NO_ROM]
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef USE_PARALLEL
+
+#define CONPORT 1
+#define TAP 1
+#define DELAY 4
+#define FRAME_SIZE 128
+#define BLOCK_SIZE (64*FRAME_SIZE)
+
+static int print_data;
+
+
+static unsigned char *
+read_block (int block_num)
+{
+  return psx_memcard_read_block (print_data, CONPORT, TAP, DELAY, block_num);
+}
+
+
+static int
+write_block (int block_num, unsigned char *data)
+{
+  return psx_memcard_write_block (print_data, CONPORT, TAP, DELAY, block_num,
+                                  data);
+}
+
+
+#if 0
+char *
+read_frame (int frame, char *data)
+{
+  return psx_memcard_read_frame (print_data, CONPORT, TAP, DELAY, frame);
+}
+
+
+int
+write_frame (int frame, char *data)
+{
+  return psx_memcard_write_frame (print_data, CONPORT, TAP, DELAY, frame,
+                                  data);
+}
+#endif
+
+
+int
+dex_read_block (const char *filename, int block_num, unsigned int parport)
+{
+  unsigned char *data;
+
+  print_data = parport;
+  parport_print_info ();
+
+  if ((data = read_block (block_num)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[PARPORT_ERROR]);
+      exit (1);
+    }
+
+  ucon64_fwrite (data, 0, BLOCK_SIZE, filename, "wb");
+
+  return 0;
+}
+
+
+int
+dex_write_block (const char *filename, int block_num, unsigned int parport)
+{
+  unsigned char data[BLOCK_SIZE];
+
+  print_data = parport;
+  parport_print_info ();
+
+  ucon64_fread (data, 0, BLOCK_SIZE, filename);
+
+  if (write_block (block_num, data) == -1)
+    {
+      fprintf (stderr, ucon64_msg[PARPORT_ERROR]);
+      exit (1);
+    }
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 34 - 0
ucon64/2.0/src/backup/dex.h

@@ -0,0 +1,34 @@
+/*
+dex.h - DexDrive support for uCON64
+
+Copyright (c) 2002 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef DEX_H
+#define DEX_H
+
+extern const st_getopt2_t dex_usage[];
+
+#define DEX_HEADER_START 0
+#define DEX_HEADER_LEN 0
+
+#ifdef USE_PARALLEL
+extern int dex_read_block (const char *filename, int block_num, unsigned int parport);
+extern int dex_write_block (const char *filename, int block_num, unsigned int parport);
+#endif // USE_PARALLEL
+
+#endif

+ 399 - 0
ucon64/2.0/src/backup/doctor64.c

@@ -0,0 +1,399 @@
+/*
+doctor64.c - Bung Doctor V64 support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef  HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "misc/misc.h"
+#include "misc/file.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "doctor64.h"
+#include "misc/parallel.h"
+
+
+const st_getopt2_t doctor64_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Doctor V64"/*"19XX Bung Enterprises Ltd http://www.bung.com.hk"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xv64", 0, 0, UCON64_XV64,
+      NULL, "send/receive ROM to/from Doctor V64; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when ROM does not exist",
+      &ucon64_wf[WF_OBJ_N64_DEFAULT_STOP_NO_ROM]
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef USE_PARALLEL
+
+#define SYNC_MAX_CNT 8192
+#define SYNC_MAX_TRY 32
+#define SEND_MAX_WAIT 0x300000
+#define REC_HIGH_NIBBLE 0x80
+#define REC_LOW_NIBBLE 0x00
+#define REC_MAX_WAIT SEND_MAX_WAIT
+
+
+static int
+parport_write (char src[], int len, unsigned int parport)
+{
+  int maxwait, i;
+
+  for (i = 0; i < len; i++)
+    {
+      maxwait = SEND_MAX_WAIT;
+      if ((inportb ((unsigned short) (parport + 2)) & 1) == 0) // check ~strobe
+        {
+          while (((inportb ((unsigned short) (parport + 2)) & 2) != 0) && maxwait--)
+            ;                                   // wait for
+          if (maxwait <= 0)
+            return 1;                           // auto feed == 0
+          outportb ((unsigned short) parport, src[i]);           // write data
+          outportb ((unsigned short) (parport + 2), 5); // ~strobe = 1
+        }
+      else
+        {
+          while (((inportb ((unsigned short) (parport + 2)) & 2) == 0) && maxwait--)
+            ;                                   // wait for
+          if (maxwait <= 0)
+            return 1;                           // auto feed == 1
+          outportb ((unsigned short) parport, src[i]);           // write data
+          outportb ((unsigned short) (parport + 2), 4);            // ~strobe = 0
+        }
+    }
+  return 0;
+}
+
+
+static int
+parport_read (char dest[], int len, unsigned int parport)
+{
+  int i, maxwait;
+  unsigned char c;
+
+  for (i = 0; i < len; i++)
+    {
+      outportb ((unsigned short) parport, REC_HIGH_NIBBLE);
+      maxwait = REC_MAX_WAIT;
+      while (((inportb ((unsigned short) (parport + 1)) & 0x80) == 0) && maxwait--)
+        ;                                       // wait for ~busy=1
+      if (maxwait <= 0)
+        return len - i;
+      c = (inportb ((unsigned short) (parport + 1)) >> 3) & 0x0f;  // ~ack, pe, slct, ~error
+
+      outportb ((unsigned short) parport, REC_LOW_NIBBLE);
+      maxwait = REC_MAX_WAIT;
+      while (((inportb ((unsigned short) (parport + 1)) & 0x80) != 0) && maxwait--)
+        ;                                       // wait for ~busy=0
+      if (maxwait <= 0)
+        return len - i;
+      c |= (inportb ((unsigned short) (parport + 1)) << 1) & 0xf0; // ~ack, pe, slct, ~error
+
+      dest[i] = c;
+    }
+  outportb ((unsigned short) parport, REC_HIGH_NIBBLE);
+  return 0;
+}
+
+
+int
+syncHeader (unsigned int baseport)
+{
+  int i = 0;
+
+  outportb ((unsigned short) baseport, 0);      // data = 00000000
+  outportb ((unsigned short) (baseport + 2), 4); // ~strobe=0
+  while (i < SYNC_MAX_CNT)
+    {
+      if ((inportb ((unsigned short) (baseport + 2)) & 8) == 0) // wait for select=0
+        {
+          outportb ((unsigned short) (baseport), 0xaa); // data = 10101010
+          outportb ((unsigned short) (baseport + 2), 0); // ~strobe=0, ~init=0
+          while (i < SYNC_MAX_CNT)
+            {
+              if ((inportb ((unsigned short) (baseport + 2)) & 8) != 0) // wait for select=1
+                {
+                  outportb ((unsigned short) (baseport + 2), 4); // ~strobe=0
+                  while (i < SYNC_MAX_CNT)
+                    {
+                      if ((inportb ((unsigned short) (baseport + 2)) & 8) == 0) // w for select=0
+                        {
+                          outportb ((unsigned short) baseport, 0x55); // data = 01010101
+                          outportb ((unsigned short) (baseport + 2), 0); // ~strobe=0, ~init=0
+                          while (i < SYNC_MAX_CNT)
+                            {
+                              if ((inportb ((unsigned short) (baseport + 2)) & 8) != 0)    // w select=1
+                                {
+                                  outportb ((unsigned short) (baseport + 2), 4); // ~strobe=0
+                                  while (i < SYNC_MAX_CNT)
+                                    {
+                                      if ((inportb ((unsigned short) (baseport + 2)) & 8) == 0) // select=0
+                                        return 0;
+                                      i++;
+                                    }
+                                }
+                              i++;
+                            }
+                        }
+                      i++;
+                    }
+                }
+              i++;
+            }
+          i++;
+        }
+      i++;
+    }
+  outportb ((unsigned short) (baseport + 2), 4);
+  return 1;
+}
+
+
+int
+initCommunication (unsigned int port)
+{
+  int i;
+  for (i = 0; i < SYNC_MAX_TRY; i++)
+    {
+      if (syncHeader (port) == 0)
+        break;
+    }
+  if (i >= SYNC_MAX_TRY)
+    return -1;
+  return 0;
+}
+
+
+int
+checkSync (unsigned int baseport)
+{
+  int i, j;
+
+  for (i = 0; i < SYNC_MAX_CNT; i++)
+    {
+      if (((inportb ((unsigned short) (baseport + 2)) & 3) == 3)
+          || ((inportb ((unsigned short) (baseport + 2)) & 3) == 0))
+        {
+          outportb ((unsigned short) baseport, 0); // ~strobe, auto feed
+          for (j = 0; j < SYNC_MAX_CNT; j++)
+            {
+              if ((inportb ((unsigned short) (baseport + 1)) & 0x80) == 0) // wait for ~busy=0
+                {
+                  return 0;
+                }
+            }
+          return 1;
+        }
+    }
+  return 1;
+}
+
+
+int
+sendFilename (unsigned int baseport, char name[])
+{
+  int i;
+  char *c, mname[12];
+
+  memset (mname, ' ', 11);
+  c = (strrchr (name, FILE_SEPARATOR));
+  if (c == NULL)
+    {
+      c = name;
+    }
+  else
+    {
+      c++;
+    }
+  for (i = 0; i < 8 && *c != '.' && *c != '\0'; i++, c++)
+    mname[i] = toupper (*c);
+  c = strrchr (c, '.');
+  if (c != NULL)
+    {
+      c++;
+      for (i = 8; i < 11 && *c != '\0'; i++, c++)
+        mname[i] = toupper (*c);
+    }
+
+  return parport_write (mname, 11, baseport);
+}
+
+
+int
+sendUploadHeader (unsigned int baseport, char name[], int len)
+{
+  char mname[12], lenbuffer[4];
+  static char protocolId[] = "GD6R\1";
+
+  if (parport_write (protocolId, strlen (protocolId), baseport) != 0)
+    return 1;
+
+  lenbuffer[0] = (char) len;
+  lenbuffer[1] = (char) (len >> 8);
+  lenbuffer[2] = (char) (len >> 16);
+  lenbuffer[3] = (char) (len >> 24);
+  if (parport_write (lenbuffer, 4, baseport) != 0)
+    return 1;
+
+  memset (mname, ' ', 11);
+  if (sendFilename (baseport, name) != 0)
+    return 1;
+  return 0;
+}
+
+
+int
+sendDownloadHeader (unsigned int baseport, int *len)
+{
+  char mname[12];
+  static char protocolId[] = "GD6W";
+  unsigned char recbuffer[15];
+
+  if (parport_write (protocolId, strlen (protocolId), baseport) != 0)
+    return 1;
+  memset (mname, ' ', 11);
+  if (parport_write (mname, 11, baseport) != 0)
+    return 1;
+  if (checkSync (baseport) != 0)
+    return 1;
+
+  if (parport_read ((char *) recbuffer, 1, baseport) != 0)
+    return 1;
+  if (recbuffer[0] != 1)
+    return -1;
+  if (parport_read ((char *) recbuffer, 15, baseport) != 0)
+    return 1;
+  *len = (int) recbuffer[0] |
+         ((int) recbuffer[1] << 8) |
+         ((int) recbuffer[2] << 16) |
+         ((int) recbuffer[3] << 24);
+  return 0;
+}
+
+
+int
+doctor64_read (const char *filename, unsigned int parport)
+{
+  char buf[MAXBUFSIZE];
+  FILE *fh;
+  int size, inittime, bytesreceived = 0;
+
+  parport_print_info ();
+  if (initCommunication (parport) == -1)
+    {
+      fprintf (stderr, ucon64_msg[PARPORT_ERROR]);
+      exit (1);
+    }
+
+  inittime = time (0);
+
+  if (sendDownloadHeader (parport, &size) != 0)
+    {
+      fprintf (stderr, ucon64_msg[PARPORT_ERROR]);
+      exit (1);
+    }
+  if (!(fh = fopen (filename, "wb")))
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  for (;;)
+    {
+      if (parport_read (buf, sizeof buf, parport) != 0)
+        {
+          fclose (fh);
+          return 0;
+        }
+      bytesreceived += sizeof buf;
+      fwrite (buf, 1, sizeof buf, fh);
+      ucon64_gauge (inittime, bytesreceived, size);
+    }
+  sync ();
+  fclose (fh);
+  return 0;
+}
+
+
+int
+doctor64_write (const char *filename, int start, int len, unsigned int parport)
+{
+  char buf[MAXBUFSIZE];
+  FILE *fh;
+  unsigned int size, inittime, pos, bytessend = 0;
+
+  parport_print_info ();
+  size = len - start;
+  if (initCommunication (parport) == -1)
+    {
+      fprintf (stderr, ucon64_msg[PARPORT_ERROR]);
+      exit (1);
+    }
+  inittime = time (0);
+
+  strcpy (buf, filename);
+  if (sendUploadHeader (parport, buf, size) != 0)
+    {
+      fprintf (stderr, ucon64_msg[PARPORT_ERROR]);
+      exit (1);
+    }
+
+  if (!(fh = fopen (filename, "rb")))
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+
+  printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  for (;;)
+    {
+      if (!(pos = fread (buf, 1, sizeof buf, fh)))
+        break;
+      if (parport_write (buf, pos, parport) != 0)
+        break;
+      bytessend += sizeof buf;
+      ucon64_gauge (inittime, bytessend, size);
+    }
+  fclose (fh);
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 31 - 0
ucon64/2.0/src/backup/doctor64.h

@@ -0,0 +1,31 @@
+/*
+doctor64.h - Bung Doctor V64 support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef DOCTOR64_H
+#define DOCTOR64_H
+
+extern const st_getopt2_t doctor64_usage[];
+
+#ifdef  USE_PARALLEL
+extern int doctor64_read (const char *filename, unsigned int parport);
+extern int doctor64_write (const char *filename, int start, int len, unsigned int parport);
+#endif
+
+#endif

+ 587 - 0
ucon64/2.0/src/backup/doctor64jr.c

@@ -0,0 +1,587 @@
+/*
+doctor64jr.c - Bung Doctor V64 Junior support for uCON64
+
+Copyright (c) 1999 - 2002 NoisyB <noisyb@gmx.net>
+Copyright (c) 2004        dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+drjr transfer protocol
+
+
+DB25  pin name
+p2~p9 pd[7:0]  XXXXXXXXXXX  ai  XXXXX  data  XXXX
+p1    nwrite   ~~~~~~~~~|_____________________|~~
+p14   ndstb    ~~~~~~~~~~~~~~~~~~~~~~~~~|_|~~~~~~
+p17   nastb    ~~~~~~~~~~~~~|_|~~~~~~~~~~~~~~~~~~
+
+
+ai[]=0	r/w a[7..0]
+ai[]=1	r/w a[15..8]
+ai[]=2	r/w a[23..16]
+ai[]=3	w a[28..24]
+ai[]=3  r (rst,wdf,wcf,a[28..24])
+ai[]=4	r/w data
+ai[]=5	w mode
+ai[]=6	w en_1
+ai[]=7	w en_0
+*remark
+        a[8..1] support page count up
+
+        ai[3]d7:0=N64 power off, 1=N64 power on
+             d6:0=no dram data written, 1=dram data written
+             d5:0=no data write in b4000000~b7ffffff, 1=some data written in b4000000~b7ffffff
+
+        mode d0:0=dram read only and clear wdf, 1=dram write enable
+             d1:0=disable cartridge read and clear wcf flag,
+                1=enable cartridge read(write b4000000~b7ffffff will switch off dram and cartridge will present at b0000000~b3ffffff)
+
+        en_0=05 and en_1=0a is enable port control
+
+
+mode:q0              0                   1                  0                    1
+mode:q1              0                   0                  1                    1
+b7ff ffff
+b400 0000      dram read only         dram r/w        cartridge read     cartridge read(* write this area will switch off dram)
+
+b3ff ffff
+b000 0000      dram read only         dram r/w        dram read only        dram r/w
+
+
+eg:enable port control
+
+DB25  pin name
+p2~p9 pd[7:0]  XXXXXXXXXXX 07 XX 05 XXXX 06 XX 0a XXXXXXXXXXXX
+p1    nwrite   ~~~~~~~~~|_____________________________|~~~~~~~
+p14   ndstb    ~~~~~~~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~
+p17   nastb    ~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~~~~~~~
+                            en_0=05       en_1=0a
+
+
+eg:write adr $b0123456, data $a55a,$1234..
+
+DB25  pin name
+p2~p9 pd[7:0]  XXXXXXXXXXX 00 XX 56 XXXX 01 XX 34 XXXX 02 XX 12 XXXX 03 XX b0 XXXXXX 04 XX 5a XX a5 XX 34 XX 12 XXXXXXXXXXX
+p1    nwrite   ~~~~~~~~~|_______________________________________________________________________________________|~~~~~~~~~~
+p14   ndstb    ~~~~~~~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~~|_|~~~|_|~~~|_|~~~|_|~~~~~~~~~~~
+p17   nastb    ~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~~|_|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                              set adr word low            set adr word high                wdata a55a  wdata 1234 (after write adr=b012345a)
+
+
+eg:read adr $b0123400~$b01235ff, 512 data
+
+DB25  pin name
+p2~p9 pd[7:0]  XXXXXXXXXXX 00 XX 00 XXXX 01 XX 34 XXXX 02 XX 12 XXXX 03 XX b0 XXXXXX 04 XX data0 XX data1 X ... X data510 XX data511 XXXXX
+p1    nwrite   ~~~~~~~~~|________________________________________________________________~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+p14   ndstb    ~~~~~~~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~~~|_|~~~~~~|_|~~~ ~~~ ~~~~|_|~~~~~~~~|_|~~~~~~~~
+p17   nastb    ~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~~|_|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                              set adr word low            set adr word high                (after 512 read adr=b0123400)
+
+
+eg:dram write protect, disable N64 access to cartridge and disable port control
+
+DB25  pin name
+p2~p9 pd[7:0]  XXXXXXXXXXX 05 XX 00 XXXX 07 XX 00 XXXX 06 XX 00 XXXXXXXXXXXX
+p1    nwrite   ~~~~~~~~~|________________________________________|~~~~~~~~~~
+p14   ndstb    ~~~~~~~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~
+p17   nastb    ~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~~~~~~~
+                            mode=00       en_0=00       en_1=00
+
+
+simple backup rountine for N64
+
+void writePI(unsigned long addr, unsigned long value)
+{
+  do {} while (*(volatile unsigned long *) (0xa4600010) & 3);     // check parallel interface not busy
+  addr &=0xbffffffc;
+  *(unsigned long *)(addr)=value;
+}
+
+unsigned long readPI(unsigned long addr)
+{
+  do {} while (*(volatile unsigned long *) (0xa4600010) & 3);     // check parallel interface not busy
+  addr &=0xbffffffc;
+  return *(unsigned long *)(addr);
+}
+
+// MAIN -- START OF USER CODE
+void mainproc(void *arg) {
+    u32 base_adr;
+    for (base_adr=0;base_adr<0x1000000;base_adr++){                     // backup 128Mbits
+       writePI(0xb0000000+base_adr,readPI(0xb4000000 + base_adr));      // write data
+    }
+}
+*/
+
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "misc/misc.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/parallel.h"
+#include "misc/file.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "doctor64jr.h"
+
+
+const st_getopt2_t doctor64jr_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Doctor V64 Junior"/*"19XX Bung Enterprises Ltd http://www.bung.com.hk"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xdjr", 0, 0, UCON64_XDJR,
+      NULL, "send ROM to Doctor V64 Junior; " OPTION_LONG_S "port=PORT",
+      &ucon64_wf[WF_OBJ_N64_DEFAULT_STOP_NO_ROM]
+    },
+#if 0
+    {
+      "xdjrs", 0, 0, UCON64_XDJRS,
+      NULL, "send/receive SRAM to/from Doctor V64 Junior; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_N64_DEFAULT_STOP_NO_ROM]
+    },
+#endif
+#endif // USE_PARALLEL
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef USE_PARALLEL
+
+#define BUFFERSIZE 32768
+//#define set_ai_write outportb (port_a, 5);    // ninit=1, nwrite=0
+#define set_data_write outportb (port_a, 1);    // ninit=0, nwrite=0
+#define set_data_read outportb (port_a, 0);     // ninit=0, nwrite=1
+//#define set_normal outportb (port_a, 4);      // ninit=1, nwrite=1
+
+static unsigned short int port_8, port_9, port_a, port_b, port_c,
+                          *buffer;
+static int wv_mode;
+
+
+static void
+set_ai (unsigned char ai)
+{
+  outportb (port_a, 5);                         // ninit=1, nwrite=0
+  outportb (port_b, ai);
+}
+
+
+static void
+set_ai_data (unsigned char ai, unsigned char data)
+{
+  set_ai (ai);
+  set_data_write                                // ninit=0, nwrite=0
+  outportb (port_c, data);
+}
+
+
+static void
+init_port (int enable_write)
+{
+#ifndef USE_PPDEV
+  outportb (port_9, 1);                         // clear EPP time flag
+#endif
+  set_ai_data (6, 0x0a);
+  set_ai_data (7, 0x05);                        // 6==0x0a, 7==0x05 is pc_control mode
+//  set_ai (5);
+//  set_data_read
+//  enable_write = inportb (port_c);
+  set_ai_data (5, (unsigned char) enable_write); // d0=0 is write protect mode
+}
+
+
+static void
+end_port (int enable_write)
+{
+  set_ai_data (5, (unsigned char) enable_write); // d0=0 is write protect mode
+  set_ai_data (7, 0);                           // release pc mode
+  set_ai_data (6, 0);                           // 6==0x0a, 7==0x05 is pc_control mode
+  outportb (port_a, 4);                         // ninit=1, nwrite=1
+}
+
+
+static int
+check_card (void)
+{
+  set_ai_data (3, 0x12);
+  set_ai_data (2, 0x34);
+  set_ai_data (1, 0x56);
+  set_ai_data (0, 0x78);
+
+  set_ai (3);
+  set_data_read                                 // ninit=0, nwrite=1
+  if ((inportb (port_c) & 0x1f) != 0x12)
+    return 1;
+
+  set_ai (2);
+  set_data_read
+  if (inportb (port_c) != 0x34)
+    return 1;
+
+  set_ai (1);
+  set_data_read
+  if (inportb (port_c) != 0x56)
+    return 1;
+
+  set_ai (0);
+  set_data_read
+  if (inportb (port_c) != 0x78)
+    return 1;
+
+  return 0;
+}
+
+
+static int
+write_32k (unsigned short int hi_word, unsigned short int lo_word)
+{
+  unsigned char unpass, pass1;
+  unsigned short int i, j, fix;
+
+  set_ai_data (3, (unsigned char) (0x10 | (hi_word >> 8)));
+  set_ai_data (2, (unsigned char) hi_word);
+  for (i = 0; i < 0x40; i++)
+    {
+      unpass = 3;
+      while (unpass)
+        {
+          set_ai_data (1, (unsigned char) ((i << 1) | lo_word));
+          set_ai_data (0, 0);
+          set_ai (4);                           // set address index=4
+          set_data_write                        // ninit=0, nwrite=0
+          fix = i << 8;
+          for (j = 0; j < 256; j++)
+            outportw (port_c, buffer[j + fix]);
+          set_data_read                         // ninit=0, nwrite=1
+          if (wv_mode)
+            {
+              for (j = 0; j < 256; j++)
+                if (inportw (port_c) != buffer[j + fix])
+                  break;
+            }
+          else
+            {
+              pass1 = 1;
+              for (j = 0; j < 4; j++)
+                if (inportw (port_c) != buffer[j + fix])
+                  {
+                    pass1 = 0;
+                    break;
+                  }
+              if (pass1)
+                {
+                  set_ai_data (1, (unsigned char) ((i << 1) | lo_word | 1));
+                  set_ai_data (0, 0xf8);
+                  set_ai (4);
+                  set_data_read                 // ninit=0, nwrite=1
+                  for (j = 252; j < 256; j++)
+                    if (inportw (port_c) != buffer[j + fix])
+                      break;
+                }
+            }
+          set_ai (0);
+          set_data_read                         // ninit=0, nwrite=1
+          if (inportb (port_c) != 0)
+            {
+              unpass--;
+              outportb (port_a, 0x0b);          // set all pin=0 for debug
+              set_ai_data (3, (unsigned char) (0x10 | (hi_word >> 8)));
+              set_ai_data (2, (unsigned char) hi_word);
+              if (unpass == 0)
+                return 1;
+            }
+          else
+            unpass = 0;
+        }
+    }
+
+/*
+  outportb (ai, 0);
+  printf ("\na[7..0]=%02x\n", inportb (data));
+  outportb (ai, 1);
+  printf ("a[15..8]=%02x\n", inportb (data));
+*/
+  return 0;
+}
+
+
+#if 0 // not used
+static int
+verify_32k (unsigned short int hi_word, unsigned short int lo_word)
+{
+  char unpass;
+  unsigned short int i, j, fix;
+
+  set_ai_data (3, (unsigned char) (0x10 | (hi_word >> 8)));
+  set_ai_data (2, (unsigned char) hi_word);
+  for (i = 0; i < 0x40; i++)
+    {
+      unpass = 3;
+      while (unpass)
+        {
+          set_ai_data (1, (unsigned char) ((i << 1) | lo_word));
+          set_ai_data (0, 0);
+          set_ai (4);
+          set_data_read                         // ninit=0, nwrite=1
+          fix = i << 8;
+          for (j = 0; j < 256; j++)
+            {
+              if (inportw (port_c) != buffer[j + fix])
+                {
+                  outportb (port_a, 0x0b);      // all pin=0 for debug
+                  set_ai_data (3, (unsigned char) (0x10 | (hi_word >> 8)));
+                  set_ai_data (2, (unsigned char) hi_word);
+                  unpass--;
+                  if (unpass == 0)
+                    return 1;
+                  else
+                    break;
+                }
+            }
+          if (j == 256)
+            break;
+        }
+    }
+
+/*
+  outportb (ai,0);
+  printf ("\na[7..0]=%02x\n", inportb (data));
+  outportb (ai, 1);
+  printf ("a[15..8]=%02x\n", inportb (data));
+*/
+  return 0;
+}
+
+
+static void
+gen_pat_32k (unsigned short int offset)
+{
+  int i;
+
+  for (i = 0; i < 0x4000; i++)
+    buffer[i] = i + offset;
+}
+
+
+static unsigned short int
+test_dram (void)
+{
+  int n_pages = 0, page;
+
+  gen_pat_32k (0x0000);
+  write_32k (0, 0);
+
+  gen_pat_32k (0x8000);
+  write_32k (0x100, 0);
+
+  gen_pat_32k (0x0000);
+  if (verify_32k (0, 0) == 0)                   // find lower 128 Mbits
+    n_pages = 0x100;
+  gen_pat_32k (0x8000);
+  if (verify_32k (0x100, 0) == 0)               // find upper 128 Mbits
+    n_pages = 0x200;
+
+  printf ("Testing DRAM...\n");
+
+  for (page = 0; page < n_pages; page++)
+    {
+      gen_pat_32k ((unsigned short int) (page * 2));
+      if (write_32k (page, 0))
+        return 0;
+      else
+        {
+          fputc ('w', stdout);
+          fflush (stdout);
+        }
+
+      gen_pat_32k ((unsigned short int) (page * 2 + 1));
+      if (write_32k (page, 0x80))
+        return 0;
+      else
+        {
+          fputc ('w', stdout);
+          fflush (stdout);
+        }
+    }
+
+  fputc ('\n', stdout);
+  for (page = 0; page < n_pages; page++)
+    {
+      gen_pat_32k ((unsigned short int) (page * 2));
+      if (verify_32k (page, 0))
+        return 0;
+      else
+        {
+          fputc ('v', stdout);
+          fflush (stdout);
+        }
+      gen_pat_32k ((unsigned short int) (page * 2 + 1));
+      if (verify_32k (page, 0x80))
+        return 0;
+      else
+        {
+          fputc ('v', stdout);
+          fflush (stdout);
+        }
+    }
+
+  return n_pages;
+}
+#endif
+
+
+static unsigned long int
+get_address (void)
+{
+  unsigned long int address;
+
+  set_ai_data (6, 0x0a);                        // enable pc mode
+  set_ai_data (7, 0x05);                        // enable pc mode
+
+  set_ai (3);
+  set_data_read                                 // ninit=0, nwrite=1
+  address = inportb (port_c) << 24;
+
+  set_ai (2);
+  set_data_read
+  address |= inportb (port_c) << 16;
+
+  set_ai (1);
+  set_data_read
+  address |= inportb (port_c) << 8;
+
+  set_ai (0);
+  set_data_read
+  address |= inportb (port_c);
+
+  return address;
+}
+
+
+int
+doctor64jr_read (const char *filename, unsigned int parport)
+{
+  (void) filename;
+  (void) parport;
+  return fprintf (stderr, "ERROR: The function for dumping a cartridge is not yet implemented for the\n"
+                          "       Doctor V64 Junior\n");
+}
+
+
+int
+doctor64jr_write (const char *filename, unsigned int parport)
+{
+  unsigned int enable_write = 0, init_time, size, bytesread, bytessend = 0,
+               n_pages;
+  unsigned short int page;
+  FILE *file;
+
+  parport_print_info ();
+
+  port_8 = parport;
+  port_9 = parport + 1;
+  port_a = parport + 2;
+  port_b = parport + 3;
+  port_c = parport + 4;
+
+  init_port (enable_write);
+
+  if (check_card () != 0)
+    {
+      fprintf (stderr, "ERROR: No Doctor V64 Junior card present\n");
+      end_port (enable_write);
+      exit (1);
+    }
+
+  wv_mode = 0;
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned short int *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = fsizeof (filename);
+  printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+#if 0
+  if (dram_test)
+    {
+      dram_size = test_dram ();
+      if (dram_size)
+        printf ("\nDRAM size=%dMbits\n", (dram_size / 2));
+      else
+        fprintf (stderr, "\nERROR: DRAM test failed\n");
+      return 0;
+    }
+#endif
+
+  n_pages = (size + (64 * 1024 - 1)) / (64 * 1024); // "+ (64 * 1024 - 1)" to round up
+  init_time = time (0);
+  for (page = 0; page < n_pages; page++)
+    {
+      bytesread = fread ((unsigned char *) buffer, 1, BUFFERSIZE, file);
+      if (write_32k (page, 0))
+        {
+          fprintf (stderr, "ERROR: Transfer failed at address 0x%8lx", get_address ());
+          break;
+        }
+
+      bytesread += fread ((unsigned char *) buffer, 1, BUFFERSIZE, file);
+      if (write_32k (page, 0x80))
+        {
+          fprintf (stderr, "ERROR: Transfer failed at address 0x%8lx", get_address ());
+          break;
+        }
+
+      bytessend += bytesread;
+      ucon64_gauge (init_time, bytessend, size);
+    }
+  fputc ('\n', stdout);
+
+  if (enable_write)                             // 1 or 3
+    printf ("DRAM write protect disabled\n");
+  if (enable_write & 2)                         // 3
+    printf ("Run cartridge enabled\n");
+
+//  set_ai_data(5, enable_write);                 // d0=0 is write protect mode
+  end_port (enable_write);
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 31 - 0
ucon64/2.0/src/backup/doctor64jr.h

@@ -0,0 +1,31 @@
+/*
+doctor64jr.h - Bung Doctor V64 Junior support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef DOCTOR64JR_H
+#define DOCTOR64JR_H
+
+extern const st_getopt2_t doctor64jr_usage[];
+
+#ifdef USE_PARALLEL
+extern int doctor64jr_read (const char *filename, unsigned int parport);
+extern int doctor64jr_write (const char *filename, unsigned int parport);
+#endif
+
+#endif

+ 1637 - 0
ucon64/2.0/src/backup/f2a.c

@@ -0,0 +1,1637 @@
+/*
+f2a.c - Flash 2 Advance support for uCON64
+
+Copyright (c) 2003        Ulrich Hecht <uli@emulinks.de>
+Copyright (c) 2003 - 2004 David Voswinkel <d.voswinkel@netcologne.de>
+Copyright (c) 2004        NoisyB <noisyb@gmx.net>
+Copyright (c) 2004        dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if     defined _WIN32 || defined __MSDOS__
+#include <sys/timeb.h>
+#else
+#include <sys/time.h>
+#endif
+#ifdef  HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include "misc/bswap.h"
+#include "misc/misc.h"
+#include "misc/file.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "f2a.h"
+#include "console/gba.h"
+#ifdef  USE_USB
+#include <stdarg.h>
+#include <sys/wait.h>
+#include <sys/utsname.h>
+#include "misc/usb.h"
+#endif
+#ifdef  USE_PARALLEL
+#include "misc/parallel.h"
+#endif
+#include "misc/property.h"
+
+
+const st_getopt2_t f2a_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Flash 2 Advance (Ultra)"/*"2003 Flash2Advance http://www.flash2advance.com"*/,
+      NULL
+    },
+#if     defined USE_PARALLEL || defined USE_USB
+    {
+      "xf2a", 0, 0, UCON64_XF2A,
+      NULL, "send/receive ROM to/from Flash 2 Advance (Ultra); " OPTION_LONG_S "port=PORT\n"
+      "receives automatically (32 Mbits) when ROM does not exist",
+      &ucon64_wf[WF_OBJ_GBA_DEFAULT_STOP_NO_ROM]
+    },
+    {
+      "xf2amulti", 1, 0, UCON64_XF2AMULTI, // send only
+      "SIZE", "send multiple ROMs to Flash 2 Advance (Ultra); specify a\n"
+      "loader in the configuration file; " OPTION_LONG_S "port=PORT",
+      &ucon64_wf[WF_OBJ_GBA_DEFAULT_STOP]
+    },
+    {
+      "xf2ac", 1, 0, UCON64_XF2AC,
+      "N", "receive N Mbits of ROM from Flash 2 Advance (Ultra);\n" OPTION_LONG_S "port=PORT",
+      &ucon64_wf[WF_OBJ_GBA_STOP_NO_ROM]
+    },
+    {
+      "xf2as", 0, 0, UCON64_XF2AS,
+      NULL, "send/receive SRAM to/from Flash 2 Advance (Ultra); " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_GBA_STOP_NO_ROM]
+    },
+    {
+      "xf2ab", 1, 0, UCON64_XF2AB,
+      "BANK", "send/receive SRAM to/from Flash 2 Advance (Ultra) BANK\n"
+      "BANK should be a number >= 1; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_GBA_STOP_NO_ROM]
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+};
+
+
+#ifdef  USE_USB
+
+#define EZDEV             "/proc/ezusb/dev0"
+#define F2A_FIRM_SIZE     23053
+#define CMD_GETINF        0x05          // get info on the system status
+#define CMD_MULTIBOOT1    0xff          // boot up the GBA stage 1, no parameters
+#define CMD_MULTIBOOT2    0             // boot up the GBA stage 2, f2a_sendmsg_t.size has to be set
+
+#define SENDMSG_SIZE      63            // (sizeof (f2a_sendmsg_t) - 1)
+
+typedef struct
+{
+  unsigned int command;                 // command to execute, see below
+  unsigned int size;                    // size of data block to read/write
+  unsigned int pad1[2];
+  unsigned int magic;                   // magic number, see below
+  unsigned int pad2[3];
+  unsigned int unknown;                 // no idea what this is for, seems to have to be 0xa for write, 0x7 for read
+  unsigned int address;                 // base address for read/write
+  unsigned int sizekb;                  // size of data block to read/write in kB
+  unsigned char pad3[5 * 4 /*-1*/];
+  /*
+    For some reason the original software uses a 63 bytes structure for outgoing
+    messages, not 64 as it does for incoming messages, hence the "-1". It all
+    seems to work fine with 64 bytes, too, and I therefore suspect this to be a
+    bug in the original software.
+  */
+  // we use SENDMSG_SIZE to solve the problem - dbjh
+} /*__attribute__ ((packed))*/ f2a_sendmsg_t;
+
+typedef struct
+{
+  unsigned char data[64];
+} f2a_recvmsg_t;
+
+static int f2a_init_usb (void);
+static int f2a_connect_usb (void);
+static int f2a_info (f2a_recvmsg_t *rm);
+static int f2a_boot_usb (const char *ilclient_fname);
+static int f2a_read_usb (int address, int size, const char *filename);
+static int f2a_write_usb (int n_files, char **files, int address);
+
+static usb_dev_handle *f2a_handle;
+#endif // USE_USB
+
+
+#ifdef  USE_PARALLEL
+
+#define LOGO_ADDR         0x06000000
+#define EXEC_STUB         0x03002000
+#define ERASE_STUB        0x03000c00
+#define LOGO_SIZE         76800
+#define BOOT_SIZE         18432
+
+#define FLIP              1
+#define HEAD              1
+#define EXEC              1
+
+#define PP_CMD_WRITEROM   0x0a
+#define PP_CMD_ERASE      0x0b
+#define PP_HEAD_BOOT      0x01
+
+typedef struct
+{
+  unsigned int pad1[3];
+  unsigned int magic;
+  unsigned int command;
+  unsigned int address;
+  unsigned int sizekb;
+  unsigned int pad2;
+  unsigned int exec;
+  unsigned int exec_stub;
+  unsigned char pad3[984];
+} /*__attribute__ ((packed))*/ f2a_msg_cmd_t; // packed attribute is not necessary
+
+static int f2a_boot_par (const char *ilclient2_fname, const char *illogo_fname);
+static int f2a_write_par (int n_files, char **files, unsigned int address);
+static int f2a_read_par (unsigned int start, unsigned int size,
+                         const char *filename);
+//static int f2a_erase_par (unsigned int start, unsigned int size);
+static int f2a_send_buffer_par (int cmd, int address,
+                                int size, const unsigned char *resource, int head,
+                                int flip, unsigned int exec, int mode);
+static int f2a_send_cmd_par (int cmd, int address, int size);
+static int f2a_exec_cmd_par (int cmd, int address, int size);
+static int f2a_receive_data_par (int cmd, int address, int size,
+                                 const char *filename, int flip);
+static int f2a_send_head_par (int cmd, int size);
+static int f2a_send_raw_par (unsigned char *buffer, int len);
+static int f2a_receive_raw_par (unsigned char *buffer, int len);
+static int f2a_wait_par ();
+static int parport_init (int port, int target_delay);
+static int parport_init_delay (int target);
+static void parport_out31 (unsigned char val);
+static void parport_out91 (unsigned char val);
+static void parport_nop ();
+
+static int f2a_pport;
+#ifdef  DEBUG
+static int parport_debug = 1;
+#else
+static int parport_debug = 0;
+#endif
+static int parport_nop_cntr;
+#endif // USE_PARALLEL
+
+
+#if     defined USE_PARALLEL || defined USE_USB
+
+#define LOADER_SIZE       32768
+
+#define CMD_WRITEDATA     0x06          // write data to RAM/ROM(USB)/SRAM
+#define CMD_READDATA      0x07          // read data from RAM/ROM(USB)/SRAM
+#define MAGIC_NUMBER      0xa46e5b91    // needs to be properly set for almost all commands
+
+enum
+{
+  UPLOAD_FAILED = 0,
+  CANNOT_GET_FILE_SIZE,
+  UPLOAD_FILE
+};
+
+static time_t starttime = 0;
+static const char *f2a_msg[] = {
+  "ERROR: Upload failed\n",
+  "ERROR: Can't determine size of file \"%s\"\n",
+  "Uploading %s, %d kB, padded to %d kB\n",
+  NULL
+};
+#endif
+
+
+#ifdef  USE_USB
+
+int
+exec (const char *program, int argc, ...)
+/*
+  This function is needed in order to execute a program with the same
+  permissions as we have. In this way a suid root uCON64 executable can execute
+  programs that require root privileges. We cannot use system(), because it
+  might drop privileges.
+  argc should be the number of _arguments_ (excluding the program name itself).
+  DON'T move this function to misc.c. It's only necessary under Linux 2.5 (and
+  later) for the USB version of the F2A. It's hard to be more system dependent
+  than that. - dbjh
+*/
+{
+  va_list argptr;
+  int status;
+
+  argc++;
+  va_start (argptr, argc);
+  if (fork () == 0)
+    {
+      int n, size;
+      char *argv[argc + 1], *arg;
+
+      size = strlen (program) + 1;
+      if ((argv[0] = (char *) malloc (size)) == NULL)
+        {
+          fprintf (stderr, ucon64_msg[BUFFER_ERROR], size);
+          exit (1);
+        }
+      strcpy (argv[0], program);
+
+      for (n = 1; n < argc; n++)
+        {
+          arg = (char *) va_arg (argptr, char *); // get next argument
+          size = strlen (arg) + 1;
+          if ((argv[n] = (char *) malloc (size)) == NULL)
+            {
+              fprintf (stderr, ucon64_msg[BUFFER_ERROR], size);
+              exit (1);
+            }
+          strcpy (argv[n], arg);
+        }
+      argv[n] = 0;
+
+      setuid (geteuid ());
+      setgid (getegid ());
+      if (execv (argv[0], (char **) argv))
+        { // error in info page: return value can be != 1
+//          fprintf (stderr, "ERROR: %s\n", strerror (errno));
+          exit (1);
+        }
+    }
+  wait (&status);
+  va_end (argptr);
+
+  return status;
+}
+
+
+static int
+f2a_init_usb (void)
+{
+  f2a_recvmsg_t rm;
+  char iclientu_fname[FILENAME_MAX];
+
+  if (sizeof (f2a_recvmsg_t) != 64)
+    {
+      fprintf (stderr, "ERROR: The size of f2a_recvmsg_t is not 64 bytes.\n"
+                       "       Please correct the source code or send a bug report\n");
+      exit (1);
+    }
+  if (sizeof (f2a_sendmsg_t) != 64)
+    {
+      fprintf (stderr, "ERROR: The size of f2a_sendmsg_t is not 64 bytes.\n"
+                       "       Please correct the source code or send a bug report\n");
+      exit (1);
+    }
+
+  memset (&rm, 0, sizeof (rm));
+
+  if (f2a_connect_usb ())
+    {
+      fprintf (stderr, "ERROR: Could not connect to F2A USB linker\n");
+      exit (1);                                 // fatal
+    }
+  f2a_info (&rm);
+  if (rm.data[0] == 0)
+    {
+      get_property_fname (ucon64.configfile, "iclientu", iclientu_fname, "iclientu.bin");
+      if (f2a_boot_usb (iclientu_fname))
+        {
+          fprintf (stderr, "ERROR: Booting GBA client binary was not successful\n");
+          exit (1);                             // fatal
+        }
+      f2a_info (&rm);
+    }
+  return 0;
+}
+
+
+static int
+f2a_connect_usb (void)
+{
+  int fp, result, firmware_loaded = 0;
+  unsigned char f2afirmware[F2A_FIRM_SIZE];
+  char f2afirmware_fname[FILENAME_MAX];
+  struct usb_bus *bus;
+  struct usb_device *dev, *f2adev = NULL;
+
+  get_property_fname (ucon64.configfile, "f2afirmware", f2afirmware_fname, "f2afirm.hex");
+  if (ucon64_fread (f2afirmware, 0, F2A_FIRM_SIZE, f2afirmware_fname) <= 0)
+    {
+      fprintf (stderr, "ERROR: Could not load F2A firmware (%s)\n", f2afirmware_fname);
+      exit (1);                                 // fatal
+    }
+
+  usb_init ();
+  usb_find_busses ();
+  usb_find_devices ();
+  for (bus = usb_busses; bus; bus = bus->next) // usb_busses is present in libusb
+    {
+      for (dev = bus->devices; dev; dev = dev->next)
+        {
+          if (dev->descriptor.idVendor == 0x547 && dev->descriptor.idProduct == 0x2131)
+            {
+              struct utsname info;
+              int version;
+
+              if (uname (&info) == -1)
+                {
+                  fputs ("ERROR: Could not determine version of the running kernel\n", stderr);
+                  return -1;
+                }
+
+              version = strtol (&info.release[0], NULL, 10) * 10 +
+                        strtol (&info.release[2], NULL, 10);
+              // example contents of info.release: "2.4.18-14custom"
+              if (version >= 25)                // Linux kernel 2.5 or later
+                {
+                  // use fxload to upload the F2A firmware
+                  char device_path[160];
+                  int exitstatus;
+
+                  snprintf (device_path, 160, "/proc/bus/usb/%s/%s",
+                            bus->dirname, dev->filename);
+                  exitstatus = exec ("/sbin/fxload", 7, "-D", device_path, "-I",
+                                     f2afirmware_fname, "-t", "an21",
+                                     ucon64.quiet < 0 ? "-vv" : "-v");
+                  if (WEXITSTATUS (exitstatus))
+                    {
+                      char cmd[10 * 80];
+
+                      snprintf (cmd, 10 * 80, ucon64.quiet < 0 ?
+                                  "/sbin/fxload -D %s -I %s -t an21 -vv" :
+                                  "/sbin/fxload -D %s -I %s -t an21 -v",
+                                device_path, f2afirmware_fname);
+                      fprintf (stderr, "ERROR: Could not upload EZUSB firmware using fxload. Command:\n"
+                                       "       %s\n", cmd);
+                      return -1;
+                    }
+                }
+              else
+                {
+                  int wrote, w;
+
+                  // Linux kernel version 2.4 or older (2.2.16 is supported by
+                  //  the EZUSB2131 driver). It is possible to use fxload under
+                  //  (later versions of?) Linux 2.4...
+                  if ((fp = open (EZDEV, O_WRONLY)) == -1)
+                    {
+                      fprintf (stderr, "ERROR: Could not upload EZUSB firmware (opening "
+                                 EZDEV": %s)\n", strerror (errno));
+                      return -1;
+                    }
+
+                  // The EZUSB2131 driver (version 1.0) only accepts one line of
+                  //  an Intel hex record file at a time...
+                  for (wrote = 0; wrote < F2A_FIRM_SIZE; wrote += w)
+                    {
+                      if ((w = write (fp, f2afirmware + wrote, F2A_FIRM_SIZE - wrote)) == -1)
+                        {
+                          fprintf (stderr, "ERROR: Could not upload EZUSB firmware (writing "
+                                     EZDEV": %s)\n", strerror (errno));
+                          return -1;
+                        }
+                      if (ucon64.quiet < 0)
+                        printf ("Wrote %d bytes (%d-%d of %d) to "EZDEV"\n",
+                                w, wrote, wrote + w, F2A_FIRM_SIZE);
+                    }
+                  close (fp);
+                }
+              firmware_loaded = 1;
+              wait2 (2000);                     // give the EZUSB some time to renumerate
+              break;
+            }
+        }
+      if (firmware_loaded)
+        break;
+    }
+
+  usb_find_devices ();
+  for (bus = usb_busses; bus; bus = bus->next)
+    {
+      for (dev = bus->devices; dev; dev = dev->next)
+        {
+          if (dev->descriptor.idVendor == 0x547 && dev->descriptor.idProduct == 0x1002)
+            {
+              f2adev = dev;
+              break;
+            }
+        }
+      if (f2adev)
+        break;
+    }
+
+  if (f2adev == NULL)
+    {
+      fprintf (stderr, "ERROR: Could not find F2A attached to USB\n");
+      return -1;
+    }
+
+  f2a_handle = usbport_open (f2adev);
+
+  result = usb_claim_interface (f2a_handle, 0x4);
+  if (result == -1)
+    {
+      fprintf (stderr, "ERROR: Could not claim USB interface\n"
+                       "       %s\n", usb_strerror ());
+      return -1;
+    }
+  result = usb_claim_interface (f2a_handle, 0x83);
+  if (result == -1)
+    {
+      fprintf (stderr, "ERROR: Could not claim USB interface\n"
+                       "       %s\n", usb_strerror ());
+      return -1;
+    }
+
+  return 0;
+}
+
+
+static int
+f2a_info (f2a_recvmsg_t *rm)
+{
+  f2a_sendmsg_t sm;
+
+  memset (&sm, 0, sizeof (f2a_sendmsg_t));
+  memset (rm, 0, sizeof (f2a_recvmsg_t));
+
+  sm.command = me2le_32 (CMD_GETINF);
+
+  if (usbport_write (f2a_handle, (char *) &sm, SENDMSG_SIZE) == -1)
+    {
+      fprintf (stderr, "ERROR: Could not send info request\n");
+      exit (1);
+    }
+  if (usbport_read (f2a_handle, (char *) rm, sizeof (f2a_recvmsg_t)) == -1)
+    {
+      fprintf (stderr, "ERROR: Did not receive info request\n");
+      exit (1);
+    }
+
+#if 0
+  {
+    unsigned int i;
+    for (i = 0; i < (sizeof (f2a_sendmsg_t) / 4); i++)
+      printf ("%-2x %08X\n", i, *(((unsigned int *) (&sm)) + i));
+
+    if (ucon64.quiet < 0)
+      {
+        printf ("info:");
+        for (i = 0; i < (sizeof (f2a_sendmsg_t) / 4); i++)
+          printf (" %08X", *(((unsigned int *) (rm)) + i));
+        fputc ('\n', stdout);
+      }
+  }
+#endif
+
+  return 0;
+}
+
+
+static int
+f2a_boot_usb (const char *ilclient_fname)
+{
+  f2a_sendmsg_t sm;
+  unsigned int ack[16], i;
+  char ilclient[16 * 1024];
+
+  printf ("Booting GBA\n"
+          "Uploading iLinker client\n"
+          "Please turn OFF, then ON your GBA with SELECT and START held down\n");
+
+  if (ucon64_fread (ilclient, 0, 16 * 1024, ilclient_fname) <= 0)
+    {
+      fprintf (stderr, "ERROR: Could not load GBA client binary (%s)\n", ilclient_fname);
+      return -1;
+    }
+
+  // boot the GBA
+  memset (&sm, 0, sizeof (f2a_sendmsg_t));
+  sm.command = me2le_32 (CMD_MULTIBOOT1);
+  usbport_write (f2a_handle, (char *) &sm, SENDMSG_SIZE);
+  sm.command = me2le_32 (CMD_MULTIBOOT2);
+  sm.size = me2le_32 (16 * 1024);
+  usbport_write (f2a_handle, (char *) &sm, SENDMSG_SIZE);
+
+  // send the multiboot image
+  if (usbport_write (f2a_handle, ilclient, 16 * 1024) == -1)
+    {
+      fprintf (stderr, f2a_msg[UPLOAD_FAILED]);
+      return -1;
+    }
+
+  if (usbport_read (f2a_handle, (char *) ack, 16 * 4) == -1)
+    return -1;
+
+  if (ucon64.quiet < 0)
+    {
+      printf ("post-boot:");
+      for (i = 0; i < 16; i++)
+        printf (" %08X", ack[i]);
+      fputc ('\n', stdout);
+    }
+
+  return 0;
+}
+
+
+static int
+f2a_read_usb (int address, int size, const char *filename)
+{
+  FILE *file;
+  int i;
+  f2a_sendmsg_t sm;
+  char buffer[1024];
+
+  memset (&sm, 0, sizeof (f2a_sendmsg_t));
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      //exit (1); for now, return, although registering usbport_close() is better
+      return -1;
+    }
+
+  sm.command = me2le_32 (CMD_READDATA);
+  sm.magic = me2le_32 (MAGIC_NUMBER);
+  sm.unknown = me2le_32 (7);
+  sm.address = me2le_32 (address);
+  sm.size = me2le_32 (size);
+  sm.sizekb = me2le_32 (size / 1024);
+  if (usbport_write (f2a_handle, (char *) &sm, SENDMSG_SIZE) == -1)
+    return -1;
+
+  for (i = 0; i < size; i += 1024)
+    {
+      if (usbport_read (f2a_handle, buffer, 1024) == -1)
+        {
+          fclose (file);
+          return -1;
+        }
+      if (!fwrite (buffer, 1024, 1, file))      // note order of arguments
+        {
+          fprintf (stderr, ucon64_msg[WRITE_ERROR], filename);
+          fclose (file);
+          return -1;                            // see comment for fopen() call
+        }
+      ucon64_gauge (starttime, i + 1024, size);
+    }
+  fclose (file);
+  return 0;
+}
+
+
+static int
+f2a_write_usb (int n_files, char **files, int address)
+{
+  f2a_sendmsg_t sm;
+  int i, j, fsize, size, n, is_sram_data = address >= 0xe000000 ? 1 : 0;
+  char buffer[1024], loader_fname[FILENAME_MAX];
+  unsigned char loader[LOADER_SIZE];
+  FILE *file;
+
+  // initialize command buffer
+  memset (&sm, 0, sizeof (f2a_sendmsg_t));
+  sm.command = me2le_32 (CMD_WRITEDATA);
+  sm.magic = me2le_32 (MAGIC_NUMBER);
+  sm.unknown = me2le_32 (is_sram_data ? 0x06 : 0x0a); // SRAM => 0x06, ROM => 0x0a
+
+  if (n_files > 1 && !is_sram_data)
+    {
+      printf ("Uploading multiloader\n");
+      get_property_fname (ucon64.configfile, "gbaloader", loader_fname, "loader.bin");
+      if (ucon64_fread (loader, 0, LOADER_SIZE, loader_fname) <= 0)
+        {
+          fprintf (stderr, "ERROR: Could not load loader binary (%s)\n", loader_fname);
+          return -1;
+        }
+#if 0 // just use a correct loader file - dbjh
+      ((int *) loader)[0] = me2be_32 (0x2e0000ea); // start address
+#endif
+      memcpy (loader + 4, gba_logodata, GBA_LOGODATA_LEN); // + 4 for start address
+
+      sm.size = me2le_32 (LOADER_SIZE);
+      sm.address = me2le_32 (address);
+      sm.sizekb = me2le_32 (LOADER_SIZE / 1024);
+
+      if (usbport_write (f2a_handle, (char *) &sm, SENDMSG_SIZE) == -1)
+        return -1;
+
+      if (usbport_write (f2a_handle, (char *) loader, LOADER_SIZE) == -1)
+        {
+          fprintf (stderr, f2a_msg[UPLOAD_FAILED]);
+          return -1;
+        }
+      address += LOADER_SIZE;
+    }
+  for (j = 0; j < n_files; j++)
+    {
+      if ((fsize = fsizeof (files[j])) == -1)
+        {
+          fprintf (stderr, f2a_msg[CANNOT_GET_FILE_SIZE], files[j]);
+          return -1;
+        }
+      // Round up to 32 kB. FIXME: This has to be 128 kB for Turbo carts
+      size = fsize;
+      if (size & (32768 - 1))
+        size += 32768;
+      size &= ~(32768 - 1);
+      printf (f2a_msg[UPLOAD_FILE], files[j], fsize / 1024, size / 1024);
+
+      if ((file = fopen (files[j], "rb")) == NULL)
+        {
+          fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], files[j]);
+          //exit (1); for now, return, although registering usbport_close() is better
+          return -1;
+        }
+      clearerr (file);
+
+      sm.size = me2le_32 (size);
+      sm.address = me2le_32 (address);
+      sm.sizekb = me2le_32 (size / 1024);
+
+      if (usbport_write (f2a_handle, (char *) &sm, SENDMSG_SIZE) == -1)
+        return -1;
+
+      for (i = 0; i < size; i += 1024)
+        {
+          //printf ("writing chunk %d\n", i);
+          n = fread (buffer, 1, 1024, file);
+          memset (buffer + n, 0, 1024 - n);
+          if (ferror (file))
+            {
+              fputc ('\n', stderr);
+              fprintf (stderr, ucon64_msg[READ_ERROR], files[j]);
+              fclose (file);
+              return -1;                        // see comment for fopen() call
+            }
+          if (usbport_write (f2a_handle, buffer, 1024) == -1)
+            return -1;
+          ucon64_gauge (starttime, i + 1024, size);
+        }
+      fputc ('\n', stdout);                     // start new gauge on new line
+
+      fclose (file);
+      address += fsize;
+    }
+
+  return 0;
+}
+
+#endif // USE_USB
+
+
+#ifdef  USE_PARALLEL
+
+static int
+f2a_init_par (int parport, int parport_delay)
+{
+  char iclientp_fname[FILENAME_MAX], ilogo_fname[FILENAME_MAX];
+
+  if (parport_init (parport, parport_delay))
+    {
+      fprintf (stderr, "ERROR: Could not connect to F2A parport linker\n");
+      exit (1);                                 // fatal
+    }
+
+  get_property_fname (ucon64.configfile, "iclientp", iclientp_fname, "iclientp.bin");
+  get_property_fname (ucon64.configfile, "ilogo", ilogo_fname, ""); // "ilogo.bin"
+  if (f2a_boot_par (iclientp_fname, ilogo_fname))
+    {
+      fprintf (stderr, "ERROR: Booting GBA client binary was not successful\n");
+      exit (1);                                 // fatal
+    }
+  return 0;
+}
+
+
+int
+parport_init (int port, int target_delay)
+{
+  f2a_pport = port;
+  parport_nop_cntr = parport_init_delay (target_delay);
+
+  parport_print_info ();
+
+#ifndef USE_PPDEV
+  outportb ((unsigned short) (f2a_pport + PARPORT_STATUS), 0x01); // clear EPP time flag
+#endif
+
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x04);
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_DATA), 0x04);
+
+  parport_out91 (0x47);
+  parport_out31 (0x02);
+  parport_out91 (0x12);
+  parport_out31 (0x01);
+  parport_out91 (0x34);
+  parport_out31 (0x00);
+  parport_out91 (0x56);
+
+  // not parport_out31 (0x02), because extra write to control register
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x03);
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_DATA), 0x02);
+
+  // not parport_out91 (0x00), because no write to data register
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x09);
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x00);
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x00);
+
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x02);
+  inportb ((unsigned short) (f2a_pport + PARPORT_STATUS));
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x06);
+  inportb ((unsigned short) (f2a_pport + PARPORT_STATUS));
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x00);
+
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_EADDRESS), 0x04);
+  outportb ((unsigned short) (f2a_pport + PARPORT_EDATA), 0x07);
+
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_EADDRESS), 0x02);
+  outportb ((unsigned short) (f2a_pport + PARPORT_EDATA), 0x12);
+
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_EADDRESS), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_EDATA), 0x34);
+
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_EADDRESS), 0x00);
+  outportb ((unsigned short) (f2a_pport + PARPORT_EDATA), 0x56);
+
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_EADDRESS), 0x02);
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x00);
+  inportb ((unsigned short) (f2a_pport + PARPORT_EDATA));
+
+  return 0;
+}
+
+
+static int
+parport_init_delay (int n_micros)
+/*
+  We only have millisecond accuracy on DOS, while we have to determine the
+  correct initial loop counter value for a number of microseconds. Luckily, the
+  function of time against the initial loop counter value is linear (provided
+  that the initial counter value is large enough), so we can just divide the
+  found loop value by 1000. Of course, in reality we don't get millisecond
+  accuracy...
+  TODO: Find the equivalent of gettimeofday() for MinGW and Visual C++
+*/
+{
+#define N_CHECKS  10
+#define N_HITSMAX 10
+#if     defined _WIN32 || defined __MSDOS__
+  struct timeb t0, t1;
+#else
+  struct timeval t0, t1;
+#endif
+  int n_ticks = 0, n, n_hits = 0, loop = 10000, loop_sum = 0;
+  volatile int m;                               // volatile is necessary for Visual C++...
+
+  printf ("Determining delay loop value for %d microseconds...", n_micros);
+  fflush (stdout);
+  while (n_hits < N_HITSMAX)
+    {
+      n_ticks = 0;
+      for (n = 0; n < N_CHECKS; n++)
+        {
+          m = loop;
+#if     defined _WIN32 || defined __MSDOS__
+          ftime (&t0);
+#else
+          gettimeofday (&t0, NULL);
+#endif
+          while (m--)
+            ;
+#if     defined _WIN32 || defined __MSDOS__
+          ftime (&t1);
+          n_ticks += (t1.time * 1000 + t1.millitm) - (t0.time * 1000 + t0.millitm);
+#else
+          gettimeofday (&t1, NULL);
+          n_ticks += (t1.tv_sec * 1000000 + t1.tv_usec) - (t0.tv_sec * 1000000 + t0.tv_usec);
+#endif
+        }
+      n_ticks /= N_CHECKS;
+
+#ifndef DJGPP
+      if (n_ticks - n_micros == 0)              // we are aiming at microsecond accuracy...
+#else // DJGPP's runtime system is quite inaccurate under Windows XP
+      n = n_ticks - n_micros;
+      if (n < 0)
+        n = -n;
+      if (n <= 1)                               // allow a deviation of 1 ms?
+#endif
+        {
+          n_hits++;
+          loop_sum += loop;
+          loop -= loop >> 3;                    // force "variation" in hope of better accuracy
+          continue;
+        }
+
+      if (n_ticks == 0)
+        loop <<= 1;
+      else
+        loop = (int) (n_micros / ((float) n_ticks / loop));
+    }
+
+#if     defined _WIN32 || defined __MSDOS__
+  n = loop_sum / (1000 * N_HITSMAX);            // divide by 1000
+#else
+  n = loop_sum / N_HITSMAX;                     // we summed N_HITSMAX loop values
+#endif
+  printf ("done (%d)\n", n);
+  return n;
+}
+
+
+static void
+parport_nop ()
+{
+  volatile int i = parport_nop_cntr;            // volatile is necessary for Visual C++...
+  while (i--)
+    ;
+}
+
+
+static void
+parport_out31 (unsigned char val)
+{
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x03);
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_DATA), val);
+}
+
+
+static void
+parport_out91 (unsigned char val)
+{
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x09);
+  outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01);
+  outportb ((unsigned short) (f2a_pport + PARPORT_DATA), val);
+}
+
+
+int
+f2a_boot_par (const char *iclientp_fname, const char *ilogo_fname)
+{
+  unsigned char recv[4], iclientp[BOOT_SIZE];
+
+  printf ("Booting GBA\n"
+          "Please turn OFF, then ON your GBA with SELECT and START held down\n");
+
+  if (f2a_send_head_par (PP_HEAD_BOOT, 1))
+    return -1;
+  if (f2a_receive_raw_par (recv, 4))
+    return -1;
+
+  if (ilogo_fname[0] != 0)
+    {
+      unsigned char ilogo[LOGO_SIZE];
+
+      printf ("Uploading iLinker logo\n");
+      if (ucon64_fread (ilogo, 0, LOGO_SIZE, ilogo_fname) <= 0)
+        {
+          fprintf (stderr, "ERROR: Could not load logo file (%s)\n", ilogo_fname);
+          return -1;
+        }
+      if (f2a_send_buffer_par (CMD_WRITEDATA, LOGO_ADDR, LOGO_SIZE, ilogo,
+                               0, 0, 0, 0))
+        {
+          fprintf (stderr, f2a_msg[UPLOAD_FAILED]);
+          return -1;
+        }
+    }
+
+  printf ("Uploading iLinker client\n");
+  if (ucon64_fread (iclientp, 0, BOOT_SIZE, iclientp_fname) <= 0)
+    {
+      fprintf (stderr, "ERROR: Could not load GBA client binary (%s)\n", iclientp_fname);
+      return -1;
+    }
+  if (f2a_send_buffer_par (CMD_WRITEDATA, EXEC_STUB, BOOT_SIZE, iclientp,
+                           HEAD, FLIP, EXEC, 0))
+    {
+      fprintf (stderr, f2a_msg[UPLOAD_FAILED]);
+      return -1;
+    }
+  return 0;
+}
+
+
+int
+f2a_write_par (int n_files, char **files, unsigned int address)
+{
+  int j, fsize, size, is_sram_data = address >= 0xe000000 ? 1 : 0;
+  char loader_fname[FILENAME_MAX];
+  unsigned char loader[LOADER_SIZE];
+
+  if (n_files > 1 && !is_sram_data)
+    {
+      printf ("Uploading multiloader\n");
+      get_property_fname (ucon64.configfile, "gbaloader", loader_fname, "loader.bin");
+      if (ucon64_fread (loader, 0, LOADER_SIZE, loader_fname) <= 0)
+        {
+          fprintf (stderr, "ERROR: Could not load loader binary (%s)\n", loader_fname);
+          return -1;
+        }
+#if 0 // just use a correct loader file - dbjh
+      ((int *) loader)[0] = me2le_32 (0x2e0000ea); // start address
+#endif
+      if (f2a_send_buffer_par (PP_CMD_WRITEROM, address, LOADER_SIZE, loader,
+                               HEAD, FLIP, 0, 0))
+        {
+          fprintf (stderr, f2a_msg[UPLOAD_FAILED]);
+          return -1;
+        }
+      address += LOADER_SIZE;
+    }
+  for (j = 0; j < n_files; j++)
+    {
+      if ((fsize = fsizeof (files[j])) == -1)
+        {
+          fprintf (stderr, f2a_msg[CANNOT_GET_FILE_SIZE], files[j]);
+          return -1;
+        }
+      size = fsize;
+      if (size & (32768 - 1))
+        size += 32768;
+      size &= ~(32768 - 1);
+      printf (f2a_msg[UPLOAD_FILE], files[j], fsize / 1024, size / 1024);
+      if (f2a_send_buffer_par (PP_CMD_WRITEROM, address, size,
+                               (unsigned char *) files[j], HEAD, FLIP, 0, 1))
+        {
+          fprintf (stderr, f2a_msg[UPLOAD_FAILED]);
+          return -1;
+        }
+
+      address += size;
+    }
+  return 0;
+}
+
+
+int
+f2a_erase_par (unsigned int start, unsigned int size)
+{
+  int end, address;
+
+  f2a_exec_cmd_par (CMD_READDATA, ERASE_STUB, 1024);
+  end = start + (size);
+
+  printf ("Erase cart start=0x%08x end=0x%08x\n", start, end);
+  for (address = start; address < end; address += 0x40000)
+    f2a_send_cmd_par (PP_CMD_ERASE, address, 1024);
+  return 0;
+}
+
+
+int
+f2a_read_par (unsigned int start, unsigned int size, const char *filename)
+{
+  f2a_exec_cmd_par (CMD_READDATA, ERASE_STUB, 1024);
+  printf ("Reading from cart start=0x%08x size=0x%08x\n", start, size);
+  f2a_receive_data_par (CMD_READDATA, start, size, filename, FLIP);
+  return 0;
+}
+
+
+#if 0
+typedef struct
+{
+  unsigned char header[16];
+  unsigned char command;
+  unsigned char unknown;
+  unsigned int size;
+  unsigned char pad[58];
+} __attribute__ ((packed)) f2a_msg_head_t;
+#endif
+
+static int
+f2a_send_head_par (int cmd, int size)
+{
+  unsigned char trans[] = { 0xa, 0x8, 0xe, 0xc, 0x2, 0x0, 0x6, 0x4 },
+                msg_header[80] = { 0x49, 0x2d, 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72,
+                                   0x2e, 0x31, 0x30, 0x30, 0x00, 0x00, 0x01, 0xe8 };
+//  f2a_msg_head_t msg_header;                  // Don't use structs with misaligned
+  unsigned short int s;                         //  members for data streams (we don't
+                                                //  want compiler-specific stuff)
+//  memcpy (&msg_head, header, 16);             // .head
+  msg_header[16] = cmd;                         // .command
+  s = size / 1024;
+  msg_header[17] =                              // .unknown
+    (trans[((s & 255) / 32)] << 4) | (((1023 - (s & 1023)) / 256) & 0x0f);
+// msg_header.unknown = 0x82;
+  msg_header[18] = (unsigned char) s;           // .size
+  msg_header[19] = (unsigned char) (s >> 8);
+  memset (&msg_header[20], 0, 80 - 20);
+
+  if (f2a_send_raw_par (msg_header, 80))
+    return -1;
+  return 0;
+}
+
+
+static int
+f2a_exec_cmd_par (int cmd, int address, int size)
+{
+  unsigned char *buffer;
+  f2a_msg_cmd_t msg_cmd;
+
+  memset (&msg_cmd, 0, sizeof (f2a_msg_cmd_t));
+  msg_cmd.magic = me2be_32 (MAGIC_NUMBER);
+  msg_cmd.command = me2be_32 (cmd);
+  msg_cmd.address = me2be_32 (address);
+  msg_cmd.sizekb = me2be_32 (size / 1024);
+
+  msg_cmd.exec_stub = me2be_32 (EXEC_STUB);
+  msg_cmd.exec = me2be_32 (0x08);
+  f2a_send_head_par (CMD_READDATA, size);
+  f2a_wait_par ();
+
+  if (parport_debug)
+    fprintf (stderr,
+             "sending msg_cmd cmd='0x%08x' address='0x%08x' size='0x%08x' %d bytes\n",
+             msg_cmd.command, msg_cmd.address, msg_cmd.sizekb,
+             (int) sizeof (f2a_msg_cmd_t));
+
+
+  f2a_send_raw_par ((unsigned char *) &msg_cmd, sizeof (f2a_msg_cmd_t));
+//  f2a_wait_par ();
+  if ((buffer = (unsigned char *) malloc (size)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[BUFFER_ERROR], size);
+      exit (1);                                 // not return, caller doesn't handle it
+    }
+  f2a_receive_raw_par (buffer, size);
+  free (buffer);
+
+  return 0;
+}
+
+
+static int
+f2a_receive_data_par (int cmd, int address, int size, const char *filename, int flip)
+{
+  unsigned char buffer[1024], recv[4]; //, *mbuffer;
+  int i, j;
+  f2a_msg_cmd_t msg_cmd;
+  FILE *file;
+
+  memset (&msg_cmd, 0, sizeof (f2a_msg_cmd_t));
+  msg_cmd.magic = me2be_32 (MAGIC_NUMBER);
+  msg_cmd.command = me2be_32 (cmd);
+  msg_cmd.address = me2be_32 (address);
+  msg_cmd.sizekb = me2be_32 (size / 1024);
+
+  if (f2a_send_head_par (CMD_READDATA, size))
+    return -1;
+
+  if (f2a_receive_raw_par (recv, 4))
+    return -1;
+
+  if (parport_debug)
+    fprintf (stderr,
+             "sending msg_cmd cmd='0x%08x' address='0x%08x' size='0x%08x' %d bytes\n",
+             msg_cmd.command, msg_cmd.address, msg_cmd.sizekb,
+             (int) sizeof (f2a_msg_cmd_t));
+
+  f2a_send_raw_par ((unsigned char *) &msg_cmd, sizeof (f2a_msg_cmd_t));
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      //exit (1); return, because the other code does it too...
+      return -1;
+    }
+
+#if 0
+  if ((mbuffer = (unsigned char *) malloc (size)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[BUFFER_ERROR], size);
+      //exit (1); see comment for fopen() call
+      return -1;
+    }
+  f2a_receive_raw_par (mbuffer, size);
+  if (flip)
+    for (j = 0; j < size / 4; j++)
+      ((int *) mbuffer)[j] = bswap_32 (((int *) mbuffer)[j]);
+
+  if (!fwrite (mbuffer, size, 1, file))         // note order of arguments
+    {
+      fprintf (stderr, ucon64_msg[WRITE_ERROR], filename);
+      fclose (file);
+      free (mbuffer);
+      return -1;                                // see comment for fopen() call
+    }
+  free (mbuffer);
+#else
+  for (i = 0; i < size; i += 1024)
+    {
+      f2a_receive_raw_par (buffer, 1024);
+      if (flip)
+        for (j = 0; j < 256; j++)
+          ((int *) buffer)[j] = bswap_32 (((int *) buffer)[j]);
+
+      if (!fwrite (buffer, 1024, 1, file))      // note order of arguments
+        {
+          fprintf (stderr, ucon64_msg[WRITE_ERROR], filename);
+          fclose (file);
+          return -1;                            // see comment for fopen() call
+        }
+
+      if (parport_debug)
+        fprintf (stderr, "reading chunk %d of %d\n", (int) (i / 1024) + 1,
+                 (int) (size / 1024));
+      else
+        ucon64_gauge (starttime, i + 1024, size);
+    }
+  if (!parport_debug)
+    fputc ('\n', stdout);
+  fclose (file);
+#endif
+
+  return 0;
+}
+
+
+static int
+f2a_send_cmd_par (int cmd, int address, int size)
+{
+  unsigned char recv[4];
+  f2a_msg_cmd_t msg_cmd;
+
+  memset (&msg_cmd, 0, sizeof (f2a_msg_cmd_t));
+  msg_cmd.magic = me2be_32 (MAGIC_NUMBER);
+  msg_cmd.command = me2be_32 (cmd);
+  msg_cmd.address = me2be_32 (address);
+  msg_cmd.sizekb = me2be_32 (size / 1024);
+
+  if (f2a_send_head_par (CMD_WRITEDATA, size))
+    return -1;
+
+  if (f2a_receive_raw_par (recv, 4))
+    return -1;
+
+  if (parport_debug)
+    fprintf (stderr,
+             "parport_send_cmd cmd='0x%08x' address='0x%08x' size='0x%08x' %d bytes\n",
+             msg_cmd.command, msg_cmd.address, msg_cmd.sizekb,
+             (int) sizeof (f2a_msg_cmd_t));
+
+  if (f2a_send_raw_par ((unsigned char *) &msg_cmd, sizeof (f2a_msg_cmd_t)))
+    return -1;
+  return 0;
+}
+
+
+static int
+f2a_send_buffer_par (int cmd, int address, int size, const unsigned char *resource,
+                     int head, int flip, unsigned int exec, int mode)
+{
+  unsigned char recv[4], buffer[1024];
+  int i, j;
+  f2a_msg_cmd_t msg_cmd;
+  FILE *file = NULL;
+
+  memset (&msg_cmd, 0, sizeof (f2a_msg_cmd_t));
+  msg_cmd.magic = me2be_32 (MAGIC_NUMBER);
+  msg_cmd.command = me2be_32 (cmd);
+  msg_cmd.address = me2be_32 (address);
+  msg_cmd.sizekb = me2be_32 (size / 1024);
+  if (exec)
+    {
+      msg_cmd.exec_stub = me2be_32 (EXEC_STUB);
+      msg_cmd.exec = me2be_32 (0x08);
+    }
+  if (f2a_send_head_par (CMD_WRITEDATA, size))
+    return -1;
+  if (f2a_receive_raw_par (recv, 4))
+    return -1;
+
+  if (parport_debug)
+    fprintf (stderr,
+             "parport_send_buffer cmd='0x%08x' address='0x%08x' size='0x%08x' %d bytes\n",
+             msg_cmd.command, msg_cmd.address, msg_cmd.sizekb,
+             (int) sizeof (f2a_msg_cmd_t));
+
+  if (f2a_send_raw_par ((unsigned char *) &msg_cmd, sizeof (f2a_msg_cmd_t)))
+    return -1;
+
+  if (mode == 1)
+    {
+      if ((file = fopen ((char *) resource, "rb")) == NULL)
+        {
+          fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], (char *) resource);
+          //exit (1); return, because the other code does it too...
+          return -1;
+        }
+      clearerr (file);
+    }
+
+  for (i = 0; i < size; i += 1024)
+    {
+      if (mode == 1)
+        {
+          j = fread (buffer, 1, 1024, file);
+          memset (buffer + j, 0, 1024 - j);
+          if (ferror (file))
+            {
+              fputc ('\n', stderr);
+              fprintf (stderr, ucon64_msg[READ_ERROR], (char *) resource);
+              fclose (file);
+              return -1;
+            }
+        }
+      else
+        memcpy (buffer, resource, 1024);
+
+      if (flip)
+        for (j = 0; j < 256; j++)
+          ((int *) buffer)[j] = bswap_32 (((int *) buffer)[j]);
+
+      if (!i && head)
+        for (j = 1; j < GBA_LOGODATA_LEN / 4 + 1; j++) // + 1 for start address
+          ((int *) buffer)[j] = bswap_32 (((int *) gba_logodata)[j - 1]);
+
+      if (parport_debug)
+        fprintf (stderr, "sending chunk %d of %d\n", (int) (i / 1024) + 1,
+                 (int) (size / 1024));
+      else
+        ucon64_gauge (starttime, i + 1024, size);
+      f2a_send_raw_par (buffer, 1024);
+      if (mode == 0)
+        resource += 1024;
+    }
+  if (!parport_debug)
+    fputc ('\n', stdout);                       // start new gauge on new line
+
+  if (mode == 1)
+    fclose (file);
+
+  return 0;
+}
+
+
+#ifdef  DEBUG
+static void
+parport_dump_byte (unsigned char byte)
+{
+  char i;
+
+  for (i = 7; i >= 0; i--)
+    {
+      if ((byte >> i) & 1)
+        fprintf (stderr, "1");
+      else
+        fprintf (stderr, "0");
+    }
+  fputc ('\n', stderr);
+}
+#endif
+
+
+static int
+f2a_receive_raw_par (unsigned char *buffer, int len)
+{
+  int err, i;
+  unsigned char *ptr, nibble;
+
+  ptr = buffer;
+  if (parport_debug)
+    fprintf (stderr, "\nreceive:\n%04x: ", 0);
+
+  *ptr = 0;
+  for (err = 0, i = 0; i < len * 2; i++)
+    {
+      nibble = 0;
+      outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x04);
+      parport_nop ();
+      while (inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)) & PARPORT_IBUSY)
+        ;
+      outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x05);
+      nibble = inportb ((unsigned short) (f2a_pport + PARPORT_STATUS));
+      while (!(inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)) & PARPORT_IBUSY))
+        ;
+      if (i % 2)
+        {
+          *ptr |= (nibble >> 3) & 0x0f;
+          if (parport_debug)
+            {
+              fprintf (stderr, "%02x ", (unsigned char) *ptr);
+              if (!(((i / 2) + 1) % 32) && i && (i / 2) < len - 1)
+                fprintf (stderr, "\n%04x: ", (i / 2) + 1);
+            }
+          *ptr = 0;
+          ptr++;
+        }
+      else
+        *ptr |= ((nibble >> 3) & 0xf) << 4;
+    }
+  if (parport_debug)
+    fputc ('\n', stderr);
+
+  return err;
+}
+
+
+static int
+f2a_send_raw_par (unsigned char *buffer, int len)
+{
+  int timeout, i;
+  unsigned char *pc;
+
+  pc = buffer;
+  if (parport_debug)
+    fprintf (stderr, "\nsend:\n%04x: ", 0);
+  for (i = 0; i < len; i++)
+    {
+      timeout = 2000;
+      if (parport_debug)
+        {
+          fprintf (stderr, "%02x ", (unsigned char) *pc);
+          if (!((i + 1) % 32) && i && i < len - 1)
+            fprintf (stderr, "\n%04x: ", i + 1);
+        }
+      outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x04);
+      parport_nop ();
+      while ((inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)) & PARPORT_IBUSY) &&
+             (timeout--) > 0)
+        wait2 (1);
+      outportb ((unsigned short) (f2a_pport + PARPORT_DATA), *pc);
+      parport_nop ();
+      while ((inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)) & PARPORT_IBUSY) &&
+             (timeout--) > 0)
+        wait2 (1);
+      outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x05);
+      parport_nop ();
+      while ((!(inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)) & PARPORT_IBUSY)) &&
+             (timeout--) > 0)
+        wait2 (1);
+      pc++;
+      if (timeout < 0)
+        {
+          fprintf (stderr, "\nERROR: Time-out\n");
+          return -1;
+        }
+    }
+  if (parport_debug)
+    fputc ('\n', stderr);
+
+  return 0;
+}
+
+
+static int
+f2a_wait_par (void)
+{
+  int stat;
+
+  while (1)
+    {
+      outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x04);
+      parport_nop ();
+      stat = inportb ((unsigned short) (f2a_pport + PARPORT_STATUS));
+      if (stat & PARPORT_IBUSY)
+        break;
+      outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x05);
+      parport_nop ();
+      inportb ((unsigned short) (f2a_pport + PARPORT_STATUS));
+    }
+  return 0;
+}
+#endif // USE_PARALLEL
+
+
+#if     defined USE_PARALLEL || defined USE_USB
+int
+f2a_read_rom (const char *filename, int size)
+{
+  int offset = 0;
+
+  starttime = time (NULL);
+#ifdef  USE_USB
+  if (ucon64.usbport)
+    {
+      f2a_init_usb ();
+      f2a_read_usb (0x8000000 + offset * MBIT, size * MBIT, filename);
+      usbport_close (f2a_handle);
+    }
+#endif
+#if     defined USE_PARALLEL && defined USE_USB
+  else
+#endif
+#ifdef  USE_PARALLEL
+    {
+      f2a_init_par (ucon64.parport, 10);
+      f2a_read_par (0x08000000 + offset * MBIT, size * MBIT, filename);
+    }
+#endif
+  return 0;
+}
+
+
+int
+f2a_write_rom (const char *filename, int size)
+{
+  int offset = 0, n, n_files, n_files_max = 0, fsize, totalsize = LOADER_SIZE;
+  char **files = NULL, *file_mem[1];
+  struct stat fstate;
+
+  if (filename)                                 // -xf2a
+    {
+      files = file_mem;
+      files[0] = (char *) filename;
+      n_files = 1;
+    }
+  else                                          // -xf2amulti=SIZE
+    {
+      n_files = 0;
+      for (n = 1; n < ucon64.argc; n++)
+        {
+          if (access (ucon64.argv[n], F_OK))
+            continue;                           // "file" does not exist (option)
+          stat (ucon64.argv[n], &fstate);
+          if (!S_ISREG (fstate.st_mode))
+            continue;
+
+          if (n_files == n_files_max)
+            {
+              n_files_max += 20;                // allocate mem for 20 extra pointers
+              if ((files = (char **) realloc (files, n_files_max * 4)) == NULL)
+                {
+                  fprintf (stderr, ucon64_msg[BUFFER_ERROR], n_files_max * 4);
+                  exit (1);
+                }
+            }
+
+          fsize = fsizeof (ucon64.argv[n]);
+          if (totalsize + fsize > size)
+            {
+              printf ("WARNING: The sum of the sizes of the files is larger than the specified flash\n"
+                      "         card size (%d Mbit). Skipping files, starting with\n"
+                      "         %s\n",
+                      size / MBIT, ucon64.argv[n]);
+              break;
+            }
+          totalsize += fsize;
+
+          files[n_files] = ucon64.argv[n];
+          n_files++;
+        }
+      if (n_files == 0)
+        return -1;
+    }
+
+  starttime = time (NULL);
+#ifdef  USE_USB
+  if (ucon64.usbport)
+    {
+      f2a_init_usb ();
+      f2a_write_usb (n_files, files, 0x8000000 + offset * MBIT);
+      usbport_close (f2a_handle);
+    }
+#endif
+#if     defined USE_PARALLEL && defined USE_USB
+  else
+#endif
+#ifdef  USE_PARALLEL
+    {
+      f2a_init_par (ucon64.parport, 10);
+      //f2a_erase_par (0x08000000, size * MBIT);
+      f2a_write_par (n_files, files, 0x8000000 + offset * MBIT);
+    }
+#endif
+
+  if (!filename)
+    free (files);
+
+  return 0;
+}
+
+
+int
+f2a_read_sram (const char *filename, int bank)
+{
+  int size;
+
+  if (bank == UCON64_UNKNOWN)
+    {
+      bank = 1;
+      size = 256 * 1024;
+    }
+  else
+    {
+      if (bank < 1)
+        {
+          fprintf (stderr, "ERROR: Bank must be a number larger than or equal to 1\n");
+          exit (1);
+        }
+      size = 64 * 1024;
+    }
+  bank--;
+
+  starttime = time (NULL);
+#ifdef  USE_USB
+  if (ucon64.usbport)
+    {
+      f2a_init_usb ();
+      f2a_read_usb (0xe000000 + bank * 64 * 1024, size, filename);
+      usbport_close (f2a_handle);
+    }
+#endif
+#if     defined USE_PARALLEL && defined USE_USB
+  else
+#endif
+#ifdef  USE_PARALLEL
+    {
+      f2a_init_par (ucon64.parport, 10);
+      f2a_read_par (0xe000000 + bank * 64 * 1024, size, filename);
+    }
+#endif
+  return 0;
+}
+
+
+int
+f2a_write_sram (const char *filename, int bank)
+{
+  char *files[1] = { (char *) filename };
+
+  // define one bank as a 64 kilobyte unit
+  if (bank == UCON64_UNKNOWN)
+    bank = 1;
+  else
+    if (bank < 1)
+      {
+        fprintf (stderr, "ERROR: Bank must be a number larger than or equal to 1\n");
+        exit (1);
+      }
+  bank--;
+
+  starttime = time (NULL);
+#ifdef  USE_USB
+  if (ucon64.usbport)
+    {
+      f2a_init_usb ();
+      f2a_write_usb (1, files, 0xe000000 + bank * 64 * 1024);
+      usbport_close (f2a_handle);
+    }
+#endif
+#if     defined USE_PARALLEL && defined USE_USB
+  else
+#endif
+#ifdef  USE_PARALLEL
+    {
+      f2a_init_par (ucon64.parport, 10);
+      //f2a_erase_par (0xe000000, size * MBIT);
+      f2a_write_par (1, files, 0xe000000 + bank * 64 * 1024);
+    }
+#endif
+  return 0;
+}
+
+#endif // defined USE_PARALLEL || defined USE_USB

+ 34 - 0
ucon64/2.0/src/backup/f2a.h

@@ -0,0 +1,34 @@
+/*
+f2a.h - Flash 2 Advance support for uCON64
+
+Copyright (c) 2003 Ulrich Hecht <uli@emulinks.de>
+Copyright (c) 2004 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef F2A_H
+#define F2A_H
+
+extern const st_getopt2_t f2a_usage[];
+
+#if     defined USE_PARALLEL || defined USE_USB
+extern int f2a_read_rom (const char *filename, int size);
+extern int f2a_write_rom (const char *filename, int size);
+extern int f2a_read_sram (const char *filename, int bank);
+extern int f2a_write_sram (const char *filename, int bank);
+#endif
+
+#endif

+ 1671 - 0
ucon64/2.0/src/backup/fal.c

@@ -0,0 +1,1671 @@
+/*
+fal.c - Flash Linker Advance support for uCON64
+
+Copyright (c) 2001        Jeff Frohwein
+Copyright (c) 2001        NoisyB <noisyb@gmx.net>
+Copyright (c) 2001 - 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "misc/misc.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "fal.h"
+#include "misc/parallel.h"
+#include "console/gba.h"
+
+
+const st_getopt2_t fal_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Flash Advance Linker"/*"2001 Visoly http://www.visoly.com"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xfal", 0, 0, UCON64_XFAL,
+      NULL, "send/receive ROM to/from Flash Advance Linker; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically (32 Mbits) when ROM does not exist",
+      &ucon64_wf[WF_OBJ_GBA_DEFAULT_STOP_NO_ROM]
+    },
+    {
+      "xfalmulti", 1, 0, UCON64_XFALMULTI, // send only
+      "SIZE", "send multiple ROMs to Flash Advance Linker (makes temporary\n"
+      "multi-game file truncated to SIZE Mbit); specify a loader in\n"
+      "the configuration file; " OPTION_LONG_S "port=PORT",
+      &ucon64_wf[WF_OBJ_GBA_DEFAULT_STOP]
+    },
+    {
+      "xfalc", 1, 0, UCON64_XFALC,
+      "N", "receive N Mbits of ROM from Flash Advance Linker; " OPTION_LONG_S "port=PORT\n"
+      "N can be 8, 16, 32, 64, 128 or 256",
+      &ucon64_wf[WF_OBJ_GBA_STOP_NO_ROM]
+    },
+    {
+      "xfals", 0, 0, UCON64_XFALS,
+      NULL, "send/receive SRAM to/from Flash Advance Linker; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_GBA_STOP_NO_ROM]
+    },
+    {
+      "xfalb", 1, 0, UCON64_XFALB,
+      "BANK", "send/receive SRAM to/from Flash Advance Linker BANK\n"
+      "BANK can be 1, 2, 3 or 4; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_GBA_STOP_NO_ROM]
+    },
+    {
+      "xfalm", 0, 0, UCON64_XFALM,
+      NULL, "try to enable EPP mode, default is SPP mode",
+      &ucon64_wf[WF_OBJ_GBA_SWITCH]
+    },
+#endif // USE_PARALLEL
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+};
+
+
+#ifdef USE_PARALLEL
+
+/********************************************************/
+/* Flash Linker Advance                                 */
+/*   by Jeff Frohwein, 2001-Jun-28                      */
+/* Compiled with DJGPP & linux                          */
+/********************************************************/
+// V1.0  - 01/06/28 - Original release
+// V1.1  - 01/06/29 - Add -w option to slow down I/O transfer for some.
+// V1.11 - 01/06/30 - Set ECP chipsets to proper parallel port mode.
+// V1.2  - 01/07/23 - Fixed programming bug for Visoly carts.
+//                  - Only the first block was getting erased.
+//                  - -v option now appears on help (-h) menu.
+//                  - -v & -s options now work properly after fixing a bug.
+// V1.3  - 01/07/24 - Added support for Visoly turbo carts.
+// V1.4  - 01/07/27 - Added support for longer filenames.
+//                  - Fixed bug where files the size of the cart overwrite
+//                     the first 16 half-words of the cart. Thnx goes to
+//                     Richard W for the code fix. Thanks Richard!
+//                    Fixed random lockup bug when programming Turbo carts.
+//                    Added -n option. Header is now repaired by default.
+// V1.5  - 01/09/25 - Added error retries/checking for older Visoly carts.
+//                  - Odd length files no longer give a verify error on last byte+1 location.
+// V1.6  - 01/11/11 - Made IRQ 7 instead of 5 and DMA 3 instead of 1 default
+//                  - linux values. (Thanks to Massimiliano Marsiglietti.)
+//                  - Added -D & -I to allow linux to change IRQ & DMA defaults.
+//                  - Added LPT3 support.
+//                  - Added error checking for space between switch & parameters.
+//                  - Added -2 options for faster operation for some EPP ports.
+// V1.7  - 01/11/13 - Added -b option to backup game save SRAM or game save Flash.
+//                  - Added -r option to restore game save SRAM. (No flash support.)
+// V1.71 - 01/11/23 - Fixed bug introduced in v1.7 where -d option printed out twice.
+// V1.72 - 01/12/12 - Force 0x96 at location 0xb2 in header since it's required.
+
+// To compile source on linux:
+//   cc -o fl fl.c -O2
+// You must have root access to run this under linux.
+//
+// NOTE: This file is filled with cr+lf line terminators. This may
+// lead to unhelpful and weird error messages with gcc for linux.
+// Strip out the cr characters to prevent this problem. The following
+// unix command line will work for that:
+//   tr -d \\r < dosfile > unixfile
+// On some unix distributions you can also use the following command:
+//   dos2unix
+// (Thanks to Massimiliano Marsiglietti for locating this problem.)
+
+// RAM Detect notes for dev. (Just ignore!)
+//-----------------------------------------
+// To detect backup type.
+//  1. First check for eeprom.
+//  2. Read byte from 0xe000000.
+//  3. Write new byte to 0xe000000.
+//  4. If diff, write back data & exit with SRAM detect flag.
+//  5. If no diff get device Manuf ID for flash.
+//  6. If no Manuf ID detected then report no cart backup available.
+
+#define outpb(p, v) outportb((unsigned short) (p), (unsigned char) (v)); iodelay()
+#define inpb(p)     inportb((unsigned short) (p))
+#define outpw(p, v) outportw((unsigned short) (p), (unsigned char) (v)); iodelay()
+#define inpw(p)     inportw((unsigned short) (p))
+
+//#define HEADER_LENGTH 0xc0
+//#define OUTBUFLEN 256                   // Must be a multiple of 2! (ex:64,128,256...)
+
+#define INTEL28F_BLOCKERASE 0x20
+#define INTEL28F_CLEARSR    0x50
+#define INTEL28F_CONFIRM    0xD0
+#define INTEL28F_QUIRY      0x98
+#define INTEL28F_READARRAY  0xff
+#define INTEL28F_READSR     0x70
+#define INTEL28F_RIC        0x90
+#define INTEL28F_WRTOBUF    0xe8
+
+#define SHARP28F_BLOCKERASE 0x20
+#define SHARP28F_CONFIRM    0xD0
+#define SHARP28F_READARRAY  0xff
+#define SHARP28F_WORDWRITE  0x10
+
+#define u8      unsigned char
+#define u16     unsigned short int
+#define u32     unsigned int
+#define u64     unsigned long long int
+// u64 is needed to compile cartlib.c without warnings on Linux/x86_64
+#define CONST_U8 const unsigned char
+
+
+// ***Global Variables ***
+
+//int WaitDelay,WaitNCDelay;
+unsigned SPPDataPort;
+unsigned SPPStatPort;
+unsigned SPPCtrlPort;
+unsigned EPPAddrPort;
+unsigned EPPDataPort;
+unsigned ECPRegECR;
+
+// prototypes
+void WriteFlash (int addr, int data);
+int ReadFlash (int addr);
+void iodelay (void);
+int PPReadWord (void);
+void PPWriteWord (int i);
+void SetCartAddr (int addr);
+void l4021d0 (int i);
+void l40226c (void);
+
+#define FLINKER 1
+
+#include "cartlib.c"
+
+
+static int debug, verbose, DataSize16, Device, EPPMode, RepairHeader,
+           VisolyTurbo, WaitDelay, FileHeader[0xc0], HeaderBad, Complement = 0;
+
+
+void
+iodelay (void)
+{
+  volatile int i;
+  for (i = 0; i < WaitDelay; i++)
+    {
+      i++;
+      i--;
+    }
+}
+
+
+void
+ProgramExit (int code)
+{
+  exit (code);
+}
+
+
+#if 0
+void
+usage (char *name)
+{
+  char _small[255];
+  char smaller[255];
+  int i = 0;
+
+  strcpy (_small, name);
+
+#if 0
+  if (strchr (name, '.') != NULL)
+    _small[strlen (_small) - 4] = 0;    /* remove trailing file type */
+#endif
+
+  while ((_small[strlen (_small) - i] != 0x2f) &&       /* loop until we find a / */
+         ((strlen (_small) - i) > 0))
+    i++;
+
+  if ((strlen (_small) - i) == 0)
+    i++;
+
+  strcpy (smaller, (char *) (&_small[strlen (_small) - i + 1]));
+
+  fprintf (stderr, "GBA FLinker v1.72 by Jeff F.\n");
+  fprintf (stderr, "Usage: %s [options]\n", smaller);
+
+  fprintf (stderr,
+           "\t-2      Use 16bit EPP data path for faster operation (default=8bit)\n");
+  fprintf (stderr,
+           "\t-b o s file   Backup game save SRAM or Flash to file\n");
+  fprintf (stderr, "\t               (o = Bank Number [1-4])\n");
+  fprintf (stderr, "\t               (s=1 - Backup 32K bytes to file.)\n");
+  fprintf (stderr, "\t               (s=2 - Backup 64K bytes to file.)\n");
+  fprintf (stderr, "\t               (s=3 - Backup 128K bytes to file.)\n");
+  fprintf (stderr, "\t               (s=4 - Backup 256K bytes to file.)\n");
+  fprintf (stderr,
+           "\t-c n    Specify chip size in mbits (8,16,32,64,128,256) (default=32)\n");
+  fprintf (stderr,
+           "\t-d n    Dump 256 bytes of ROM to screen (default: n=0)\n");
+  fprintf (stderr, "\t-h      This help screen\n");
+  fprintf (stderr,
+           "\t-l n    Specify the parallel port to use (default is 1 = LPT1)\n");
+//   fprintf (stderr, "\t-m\tSet Standard Parallel Port (SPP) mode (default = EPP)\n");
+  fprintf (stderr,
+           "\t-n      Do not repair incorrect header (default = repair header)\n");
+  fprintf (stderr, "\t-p file Program flash cart with file\n");
+  fprintf (stderr,
+           "\t-r o file     Restore game save SRAM from file (No save flash support)\n");
+  fprintf (stderr, "\t               (o = Bank Number [1-4])\n");
+  fprintf (stderr,
+           "\t-s file Save the cart into a file (Use -c to specify size)\n");
+  fprintf (stderr, "\t-v file Verify flash cart with file\n");
+  fprintf (stderr, "\t-w n    Add delay to make transfer more reliable\n");
+}
+#endif
+
+
+void
+InitPort (int port)
+{
+  // uCON64 comment: see the comment in fal_main() where port is initialised
+  SPPDataPort = port;
+  SPPStatPort = SPPDataPort + 1;
+  SPPCtrlPort = SPPDataPort + 2;
+  EPPAddrPort = SPPDataPort + 3;
+  EPPDataPort = SPPDataPort + 4;
+  ECPRegECR = SPPDataPort + 0x402;
+}
+
+
+void
+l4020a4 (int reg)
+{
+  outpb (SPPCtrlPort, 1);
+  outpb (SPPDataPort, reg);
+  outpb (SPPCtrlPort, 9);
+  outpb (SPPCtrlPort, 1);
+}
+
+
+void
+SPPWriteByte (int i)            // l4020dc
+{
+  outpb (SPPDataPort, i);
+  outpb (SPPCtrlPort, 3);
+  outpb (SPPCtrlPort, 1);
+}
+
+
+void
+l402108 (int reg, int adr)
+{
+  l4020a4 (reg);
+  SPPWriteByte (adr);
+}
+
+
+int
+SPPReadByte (void)              // 402124
+{
+  int v;
+
+  outpb (SPPCtrlPort, 0);
+  outpb (SPPCtrlPort, 2);
+  v = ((inpb (SPPStatPort)) >> 3) & 0xf;
+  outpb (SPPCtrlPort, 6);
+  v += (((inpb (SPPStatPort)) << 1) & 0xf0);
+  outpb (SPPCtrlPort, 0);
+
+  return v;
+}
+
+
+void
+l402188 (int reg)
+{
+  outpb (SPPCtrlPort, 1);
+  outpb (EPPAddrPort, reg);
+}
+
+
+void
+l4021a8 (int reg, int adr)
+{
+  l402188 (reg);
+  outpb (SPPCtrlPort, 1);
+  outpb (EPPDataPort, adr);
+}
+
+
+void
+l4021d0 (int i)
+{
+  outpb (SPPCtrlPort, 1);
+
+  if (EPPMode)
+    l402188 (i);
+  else
+    l4020a4 (i);
+}
+
+
+void
+l402200 (int reg, int adr)
+{
+  if (EPPMode)
+    l4021a8 (reg, adr);
+  else
+    l402108 (reg, adr);
+}
+
+
+void
+l402234 (void)
+{
+  if (EPPMode)
+    l402200 (4, 0x83);
+  else
+    l402200 (4, 0xc3);
+}
+
+
+void
+l40226c (void)
+{
+  if (EPPMode)
+    l402200 (4, 7);
+  else
+    l402200 (4, 0x47);
+}
+
+
+void
+PPWriteByte (int i)             // 4022d0
+{
+  if (EPPMode)
+    {
+      outpb (EPPDataPort, i);
+    }
+  else
+    SPPWriteByte (i);
+}
+
+
+void
+PPWriteWord (int i)             // 4022d0
+{
+  if (EPPMode)
+    {
+      if (DataSize16)
+        {
+          outpw (EPPDataPort, i);
+        }
+      else
+        {
+          outpb (EPPDataPort, i);
+          outpb (EPPDataPort, (i >> 8));
+        }
+    }
+  else
+    {
+      SPPWriteByte (i);
+      SPPWriteByte (i >> 8);
+    }
+}
+
+
+int
+PPReadByte (void)               // 40234c
+{
+  int v;
+
+  if (EPPMode)
+    v = inpb (EPPDataPort);
+  else
+    v = SPPReadByte ();
+
+  return v;
+}
+
+
+int
+PPReadWord (void)               // 402368  // ReadFlash
+{
+  int v = 0;
+
+  if (EPPMode)
+    {
+      if (DataSize16)
+        v = inpw (EPPDataPort);
+      else
+        {
+          v = inpb (EPPDataPort);
+          v += (inpb (EPPDataPort) << 8);
+        }
+    }
+  else
+    {
+      v = SPPReadByte ();       //402124
+      v += (SPPReadByte () << 8);
+    }
+  return v;
+}
+
+
+void
+SetCartAddr (int addr)          // 4023cc
+{
+  l402200 (2, addr >> 16);
+  l402200 (1, addr >> 8);
+  l402200 (0, addr);
+}
+
+
+void
+WriteFlash (int addr, int data) // 402414
+{
+  SetCartAddr (addr);
+  l4021d0 (3);
+  PPWriteWord (data);
+}
+
+
+int
+ReadFlash (int addr)
+{
+  SetCartAddr (addr);
+  l4021d0 (3);
+  outpb (SPPCtrlPort, 0);
+  return PPReadWord ();
+}
+
+
+void
+l402684 (void)
+{
+#ifndef USE_PPDEV
+  outpb (SPPStatPort, 1);                       // clear EPP time flag
+#endif
+  l40226c ();
+}
+
+
+int
+LookForLinker (void)            // 4026a8
+{
+  l402684 ();
+  l402200 (2, 0x12);
+  l402200 (1, 0x34);
+  l402200 (0, 0x56);
+  l4021d0 (2);
+  outpb (SPPCtrlPort, 0);
+  if (PPReadByte () != 0x12)    // 40234c
+    return 0;
+
+  l4021d0 (1);
+  outpb (SPPCtrlPort, 0);
+  if (PPReadByte () != 0x34)
+    return 0;
+
+  l4021d0 (0);
+  outpb (SPPCtrlPort, 0);
+  if (PPReadByte () != 0x56)
+    return 0;
+
+  outpb (SPPCtrlPort, 4);
+  return 1;
+}
+
+
+void
+LinkerInit (void)               // 4027c4
+{
+  int linker_found = 0;
+
+  /*
+    uCON64 comment:
+    Accessing I/O ports with addresses higher than 0x3ff causes an access
+    violation under Windows XP (NT/2000) for _Windows_ executables without the
+    use of an appropriate I/O port driver. UserPort is an example of an
+    *inappropriate* I/O port driver, because it enables access to I/O ports up
+    to 0x3ff. For some (ridiculous) reason, DOS executables are allowed to
+    _access_ at least the ECP register this code uses. That doesn't mean it
+    will result in the expected behaviour like enabling EPP.
+  */
+  if (EPPMode)
+    {
+      /*
+        Writing to the ECP register seems to have no effect on my PC (which
+        supports ECP, used appropriate BIOS setting). Tested under Windows XP
+        with Windows executables (Cygwin, VC++ and MinGW) - dbjh
+      */
+#ifndef USE_PPDEV
+      outpb (ECPRegECR, 4);                     // set EPP mode for ECP chipsets
+#endif
+      if (LookForLinker ())
+        {
+          // Linker found using EPP mode.
+          linker_found = 1;
+          puts ("Linker found. EPP found");
+
+          if (SPPDataPort == 0x3bc)
+            return;
+          outpb (SPPCtrlPort, 4);
+        }
+    }
+  if (!linker_found)
+    {
+      // Look for linker in SPP mode.
+#ifndef USE_PPDEV
+      if (EPPMode)
+        outpb (ECPRegECR, 0);                   // set SPP mode for ECP chipsets
+#endif
+
+      EPPMode = 0;
+      if (LookForLinker ())
+        puts ("Linker found. EPP not found or not enabled - SPP used");
+      else
+        {
+          fputs ("ERROR: Flash Advance Linker not found or not turned on\n",
+                 stderr);
+          ProgramExit (1);
+        }
+    }
+}
+
+
+int
+ReadStatusRegister (int addr)   // 402dd8
+{
+  int v;
+  WriteFlash (addr, INTEL28F_READSR);
+  outpb (SPPCtrlPort, 0);
+  v = PPReadWord ();            // & 0xff;
+  v = PPReadWord ();            // & 0xff;
+  return v;
+}
+
+
+// StartOffSet: 1 = 0, 2 = 64k, 3 = 128k, 4 = 192k
+// Size: 1 = 32k, 2 = 64k, 3 = 128k, 4 = 256k
+void
+BackupSRAM (FILE *fp, int StartOS, int Size)   // 4046f4
+{
+  int j, k, v;
+  int m;
+  int n = 1 << (Size - 1);
+  int size = n * 32 * 1024, bytesread = 0;
+  time_t starttime;
+
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  starttime = time (NULL);
+  for (m = ((StartOS - 1) << 1); m < (((StartOS - 1) << 1) + n); m++)
+    {
+      if ((m & 1) == 0)
+        {
+          SetVisolyBackupRWMode (m >> 1);
+          l402234 ();
+        }
+
+      // Backup a 32k byte chunk
+      for (j = 0; j < 0x80; j++)
+        {
+          l402200 (0, 0);
+          l402200 (1, j + (0x80 * (m & 1)));
+          l402200 (2, 0);
+          l4021d0 (3);
+          outpb (SPPCtrlPort, 0);
+
+          for (k = 0; k < 0x100; k++)
+            {
+              v = PPReadByte ();
+              fputc (v, fp);
+            }
+          bytesread += 256;
+          if ((bytesread & 0x1fff) == 0)        // call ucon64_gauge() after receiving 8 kB
+            ucon64_gauge (starttime, bytesread, size);
+        }
+    }
+}
+
+
+// StartOffSet: 1 = 0, 2 = 64k, 3 = 128k, 4 = 192k
+void
+RestoreSRAM (FILE *fp, int StartOS)
+{
+  int i;
+  int j, k;
+  int m = ((StartOS - 1) << 1);
+  int byteswritten = 0;
+  time_t starttime;
+
+  printf ("Send: %d Bytes (%.4f Mb)\n\n", ucon64.file_size,
+          (float) ucon64.file_size / MBIT);
+
+  starttime = time (NULL);
+  i = fgetc (fp);
+  while (!feof (fp))
+    {
+      if ((m & 1) == 0)
+        {
+          SetVisolyBackupRWMode (m >> 1);
+          l402234 ();
+        }
+
+      // Restore a 32k byte chunk
+      for (j = 0; j < 0x80; j++)
+        {
+          l402200 (0, 0);
+          l402200 (1, j + (0x80 * (m & 1)));
+          l402200 (2, 0);
+          l4021d0 (3);
+          outpb (SPPCtrlPort, 0);
+
+          for (k = 0; k < 0x100; k++)
+            {
+              PPWriteByte (i);
+              i = fgetc (fp);
+            }
+          byteswritten += 256;
+          if ((byteswritten & 0x1fff) == 0)     // call ucon64_gauge() after sending 8 kB
+            ucon64_gauge (starttime, byteswritten, ucon64.file_size);
+        }
+      m++;
+    }
+}
+
+
+void
+BackupROM (FILE *fp, int SizekW)
+{
+  u16 valw;
+  u32 i, j;
+  int size = SizekW << 1, bytesread = 0;
+  time_t starttime;
+
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+  
+  WriteFlash (0, INTEL28F_READARRAY);   // Set flash (intel 28F640J3A) Read Mode
+
+  starttime = time (NULL);
+  for (i = 0; i < (u32) (SizekW >> 8); i++)
+    {
+      SetCartAddr (i << 8);     // Set cart base addr to 0
+      l4021d0 (3);
+
+      outpb (SPPCtrlPort, 0);
+
+      for (j = 0; j < 256; j++)
+        {
+          valw = PPReadWord ();
+          fputc (valw & 0xff, fp);
+          fputc (valw >> 8, fp);
+        }
+      bytesread += 256 << 1;    // 256 words
+      if ((bytesread & 0xffff) == 0)    // call ucon64_gauge() after receiving 64 kB
+        ucon64_gauge (starttime, bytesread, size);
+    }
+}
+
+
+void
+dump (u8 BaseAdr)
+{
+//   unsigned char low, high;
+  int i;
+  u8 First = 1;
+  u16 v;
+  u8 val1, val2;
+  u8 Display[17] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+  WriteFlash (0, INTEL28F_READARRAY);   // Set flash (intel 28F640J3A) Read Mode
+
+  SetCartAddr (BaseAdr << 7);   // Set cart base addr to read
+  l4021d0 (3);
+
+  outpb (SPPCtrlPort, 0);
+
+  for (i = 0; i < 128; i++)
+    {
+      if (First == 1)
+        {
+          if (i * 2 < 256)
+            fputc ('0', stdout);
+          if (i * 2 < 16)
+            fputc ('0', stdout);
+          printf ("%hx - ", (i * 2));
+          First = 0;
+        }
+
+      v = PPReadWord ();
+      val2 = v >> 8;
+      val1 = v & 255;
+
+      if ((val1 > 31) & (val1 < 127))
+        Display[(i & 7) * 2] = val1;
+      else
+        Display[(i & 7) * 2] = 46;
+
+      if (val1 < 16)
+        fputc ('0', stdout);
+      printf ("%hx ", val1);
+
+      if ((val2 > 31) & (val2 < 127))
+
+        Display[(i & 7) * 2 + 1] = val2;
+      else
+        Display[(i & 7) * 2 + 1] = 46;
+
+      if (val2 < 16)
+        fputc ('0', stdout);
+      printf ("%hx ", val2);
+
+      if ((i & 7) == 7)
+        {
+          First = 1;
+          printf ("   %3s\n", (&Display[0]));
+        }
+    }
+}
+
+
+void
+CheckForFC (void)
+{
+  LinkerInit ();
+
+  SetVisolyFlashRWMode ();
+  Device = CartTypeDetect ();
+  VisolyTurbo = 0;
+
+  printf ("Device ID = 0x%x: ", Device);
+
+  switch (Device)
+    {
+    case 0x16:
+      fputs ("FA 32M (i28F320J3A)", stdout);
+      break;
+    case 0x17:
+      fputs ("FA 64M (i28F640J3A)", stdout);
+      break;
+    case 0x18:
+      fputs ("FA 128M (i28F128J3A)", stdout);
+      break;
+    case 0x2e:
+      fputs ("Standard ROM", stdout);
+      break;
+    case 0x96:
+      fputs ("Turbo FA 64M (2 x i28F320J3A)", stdout);
+      VisolyTurbo = 1;
+      break;
+    case 0x97:
+      fputs ("Turbo FA 128M (2 x i28F640J3A)", stdout);
+      VisolyTurbo = 1;
+      break;
+    case 0x98:
+      fputs ("Turbo FA 256M (2 x i28F128J3A", stdout);
+      VisolyTurbo = 1;
+      break;
+    case 0xdc:
+      fputs ("Hudson", stdout);
+      break;
+    case 0xe2:
+      fputs ("Nintendo Flash Card (LH28F320BJE)", stdout);
+      break;
+    default:
+      fputs ("Unknown", stdout);
+      break;
+    }
+  fputc ('\n', stdout);
+}
+
+
+int
+GetFileByte (FILE *fp)
+{
+  static int FilePos = 0;
+  int i = 0;
+
+  if (RepairHeader)
+    {
+      // Set file pointer just past header
+      if (FilePos == 0)
+        for (i = 0; i < 0xa0; i++)
+          (void) fgetc (fp);
+
+      if (FilePos < 0xa0)
+        {
+          if ((HeaderBad) && (FilePos > 3))
+            i = gba_logodata[FilePos - 4]; // GoodHeader[FilePos];
+          else
+            i = FileHeader[FilePos];
+        }
+      else if ((FilePos == 0xb2) || (FilePos == 0xbd))
+        {
+          if (FilePos == 0xb2)
+            i = 0x96;           // Required
+          else
+            i = Complement;
+          (void) fgetc (fp);    // Discard file value
+        }
+      else
+        i = fgetc (fp);
+    }
+  else
+    i = fgetc (fp);
+
+  FilePos++;
+  return i;
+}
+
+
+int
+GetFileSize2 (FILE *fp)
+/*
+  The name "GetFileSize" conflicts with a Windows function.
+  For some odd reason Jeff Frohwein returned the file size minus 1. I (dbjh)
+  guess that's a bug. I disabled the (terribly inefficient) file size code
+  because uCON64 already determined the file size at this point.
+*/
+{
+  int FileSize;
+
+  if (RepairHeader)
+    {
+      int i;
+      int j;
+
+      FileSize = 0;
+      while (!feof (fp) && (FileSize < 0xc0))
+        FileHeader[FileSize++] = fgetc (fp);
+
+      if (feof (fp))
+        {
+          fputs ("ERROR: File must be 192 bytes or larger\n", stderr);
+          ProgramExit (1);
+        }
+
+      HeaderBad = 0;
+      i = 4;
+      while (i < 0xa0)          //9c)
+        {
+          if (FileHeader[i] != gba_logodata[i - 4]) // GoodHeader[i]
+            HeaderBad = 1;
+          i++;
+        }
+      if (HeaderBad)
+        puts ("NOTE: Fixing logo area");
+
+      Complement = 0;
+      FileHeader[0xb2] = 0x96;  // Required
+      for (j = 0xa0; j < 0xbd; j++)
+        Complement += FileHeader[j];
+      Complement = (0 - (0x19 + Complement)) & 0xff;
+      //printf("[Complement = 0x%x]", (int)Complement);
+      //printf("[HeaderComp = 0x%x]", (int)FileHeader[0xbd]);
+      if (FileHeader[0xbd] != Complement)
+        puts ("NOTE: Fixing complement check");
+
+      rewind (fp);
+    }
+  else
+    rewind (fp);
+
+  return ucon64.file_size;
+}
+
+
+// Program older (non-Turbo) Visoly flash card
+// (Single flash chip)
+void
+ProgramNonTurboIntelFlash (FILE *fp)
+{
+  int i, j, k;
+  int addr = 0;
+  int FileSize;
+  int Ready = 0;
+  int Timeout;
+  time_t starttime;
+
+  // Get file size
+  FileSize = GetFileSize2 (fp);
+
+  puts ("Erasing Visoly non-turbo flash card...");
+
+  // Erase as many 128k blocks as are required
+  Ready = EraseNonTurboFABlocks (0, ((FileSize - 1) >> 17) + 1);
+
+  clear_line ();                                // remove "erase gauge"
+  if (Ready)
+    {
+      printf ("Send: %d Bytes (%.4f Mb)\n\n", FileSize, (float) FileSize / MBIT);
+
+      //403018
+      starttime = time (NULL);
+      j = GetFileByte (fp);
+
+      while (!feof (fp))
+        {
+          Ready = 0;
+          Timeout = 0x4000;
+
+          while ((Ready == 0) && (Timeout != 0))
+            {
+              WriteFlash (addr, INTEL28F_WRTOBUF);
+              outpb (SPPCtrlPort, 0);
+              Ready = PPReadWord () & 0x80;
+
+              Timeout--;
+            }
+
+          if (Ready)
+            {
+              WriteFlash (addr, 15);    // Write 15+1 16bit words
+
+              SetCartAddr (addr);       // Set cart base addr to 0
+              l4021d0 (3);
+
+              for (i = 0; i < 16; i++)
+                {
+                  k = j;
+                  if (j != EOF)
+                    j = GetFileByte (fp);
+                  k += (j << 8);
+                  PPWriteWord (k);
+
+                  if (j != EOF)
+                    j = GetFileByte (fp);
+                }
+
+              addr += 16;
+              if ((addr & 0x3fff) == 0) // call ucon64_gauge() after sending 32 kB
+                ucon64_gauge (starttime, addr << 1, FileSize);
+
+              PPWriteWord (INTEL28F_CONFIRM);   // Comfirm block write
+
+              Ready = 0;
+              Timeout = 0x4000;
+
+              while ((Ready == 0) && (Timeout != 0))
+                {
+                  WriteFlash (0, INTEL28F_READSR);
+                  outpb (SPPCtrlPort, 0);
+                  i = PPReadWord () & 0xff;
+                  Ready = i & 0x80;
+
+                  Timeout--;
+                }
+
+              if (Ready)
+                {
+                  if (i & 0x7f)
+                    {
+                      // One or more status register error bits are set
+                      outpb (SPPCtrlPort, 1);
+                      WriteFlash (0, INTEL28F_CLEARSR);
+                      Ready = 0;
+                      break;
+                    }
+                }
+              else
+                {
+                  outpb (SPPCtrlPort, 1);
+                  WriteFlash (0, INTEL28F_CLEARSR);
+                  break;
+                }
+            }
+          else
+            break;
+        }
+
+      clear_line ();                            // remove last gauge
+      ucon64_gauge (starttime, addr << 1, FileSize);   // make gauge reach 100% when size % 32 k != 0
+      WriteFlash (0, INTEL28F_READARRAY);
+      outpb (SPPCtrlPort, 0);
+
+      if (Ready)
+        ;
+      else
+        fputs ("\nERROR: Flash card write failed\n", stderr);
+    }
+  else
+    fputs ("\nERROR: Flash card erase failed\n", stderr);
+}
+
+
+// Program newer (Turbo) Visoly flash card
+// (Dual chip / Interleave)
+void
+ProgramTurboIntelFlash (FILE *fp)
+{
+  int i, j = 0;
+  int k;                        //z;
+  int addr = 0;
+  int done1, done2;
+  int FileSize;
+  int Timeout;
+  int Ready;                    //= 0;
+  time_t starttime;
+
+  // Get file size
+  FileSize = GetFileSize2 (fp);
+
+  puts ("Erasing Visoly turbo flash card...");
+
+  // Erase as many 256k blocks as are required
+  Ready = EraseTurboFABlocks (0, ((FileSize - 1) >> 18) + 1);
+
+  clear_line ();                                // remove "erase gauge"
+  if (Ready)
+    {
+      printf ("Send: %d Bytes (%.4f Mb)\n\n", FileSize, (float) FileSize / MBIT);
+
+      //403018
+      starttime = time (NULL);
+      j = GetFileByte (fp);
+
+      while (!feof (fp))
+        {
+          done1 = 0;
+          done2 = 0;
+          Ready = 0;
+          Timeout = 0x4000;
+
+          while ((!Ready) && (Timeout != 0))
+            {
+              if (done1 == 0)
+                WriteFlash (addr + 0, INTEL28F_WRTOBUF);
+              if (done2 == 0)
+                WriteFlash (addr + 1, INTEL28F_WRTOBUF);
+
+              SetCartAddr (addr);       // Set cart base addr
+              l4021d0 (3);
+              outpb (SPPCtrlPort, 0);
+
+              done1 = PPReadWord () & 0x80;
+              done2 = PPReadWord () & 0x80;
+              Ready = ((done1 + done2) == 0x100);
+
+              Timeout--;
+            }
+
+          if (Ready)
+            {
+              WriteFlash (addr, 15);    // Write 15+1 16bit words
+              PPWriteWord (15);
+
+              SetCartAddr (addr);       // Set cart base addr
+              l4021d0 (3);
+
+              for (i = 0; i < 32; i++)
+                {
+                  k = j;
+                  if (j != EOF)
+                    j = GetFileByte (fp);
+                  k += (j << 8);
+                  PPWriteWord (k);
+
+                  if (j != EOF)
+                    j = GetFileByte (fp);
+                }
+              addr += 32;
+              if ((addr & 0x3fff) == 0) // call ucon64_gauge() after sending 32 kB
+                ucon64_gauge (starttime, addr << 1, FileSize);
+              PPWriteWord (INTEL28F_CONFIRM);   // Comfirm block write
+              PPWriteWord (INTEL28F_CONFIRM);   // Comfirm block write
+
+              Ready = 0;
+              Timeout = 0x4000;
+              k = 0;
+
+              while (((k & 0x8080) != 0x8080) && (Timeout != 0))
+                {
+                  outpb (SPPCtrlPort, 0);
+                  k = PPReadWord () & 0xff;
+                  k += ((PPReadWord () & 0xff) << 8);
+                  Ready = (k == 0x8080);
+
+                  Timeout--;
+                }
+
+              if (!Ready)
+                break;
+            }
+          else
+            break;
+        }
+
+      clear_line ();                            // remove last gauge
+      ucon64_gauge (starttime, addr << 1, FileSize);   // make gauge reach 100% when size % 32 k != 0
+      WriteFlash (0, INTEL28F_READARRAY);
+      outpb (SPPCtrlPort, 0);
+      WriteFlash (1, INTEL28F_READARRAY);
+      outpb (SPPCtrlPort, 0);
+
+      if (Ready)
+        ;
+      else
+        {
+          WriteFlash (0, INTEL28F_CLEARSR);
+          PPWriteWord (INTEL28F_CLEARSR);
+          fputs ("\nERROR: Flash card write failed\n", stderr);
+        }
+    }
+  else
+    fputs ("\nERROR: Flash card erase failed\n", stderr);
+}
+
+
+// Program official Nintendo flash card
+void
+ProgramSharpFlash (FILE *fp)
+{
+  int i, j;
+  int k = 0;
+  int addr = 0;
+  int FileSize;
+  int Ready;
+  time_t starttime;
+
+  // Get file size
+  FileSize = GetFileSize2 (fp);
+
+  puts ("Erasing Nintendo flash card...");
+
+  // Erase as many 64k blocks as are required
+  Ready = EraseNintendoFlashBlocks (0, ((FileSize - 1) >> 16) + 1);
+
+  clear_line ();                                // remove "erase gauge"
+  if (Ready)
+    {
+      printf ("Send: %d Bytes (%.4f Mb)\n\n", FileSize, (float) FileSize / MBIT);
+
+      starttime = time (NULL);
+      j = GetFileByte (fp);
+
+      while (!feof (fp))
+        {
+          if (j != EOF)
+            k = GetFileByte (fp);
+
+          i = ((k & 0xff) << 8) + (j & 0xff);
+
+          while ((ReadStatusRegister (0) & 0x80) == 0)
+            ;
+
+          WriteFlash (addr, SHARP28F_WORDWRITE);
+          WriteFlash (addr, i);
+          addr += 1;
+
+          j = GetFileByte (fp);
+          if ((addr & 0x3fff) == 0)     // call ucon64_gauge() after sending 32 kB
+            ucon64_gauge (starttime, addr << 1, FileSize);
+        }
+
+      clear_line ();                            // remove last gauge
+      ucon64_gauge (starttime, addr << 1, FileSize);   // make gauge reach 100% when size % 32 k != 0
+      WriteFlash (0, INTEL28F_READARRAY);
+      outpb (SPPCtrlPort, 0);
+    }
+  else
+    fputs ("\nERROR: Flash card erase failed\n", stderr);
+}
+
+
+#if 0 // not used
+void
+VerifyFlash (FILE *fp)
+{
+  int addr = 0;
+  int CompareFail = 0;
+  int k = 0;
+  int i, j, m, n;
+
+  WriteFlash (0, INTEL28F_READARRAY);   // Set flash (intel 28F640J3A) Read Mode
+
+  j = 0;
+  while (!feof (fp))
+    {
+      j = fgetc (fp);
+      if (j != EOF)
+        {
+          if ((addr & 0x1ff) == 0)
+            {
+              SetCartAddr (addr >> 1);  // Set cart base addr to read
+              l4021d0 (3);
+
+              outpb (SPPCtrlPort, 0);
+            }
+
+          k = fgetc (fp);
+
+          i = PPReadWord ();
+          m = i & 0xff;
+          n = i >> 8;
+
+          if (m != j)
+            {
+              printf ("Address %x - Cartridge %x: File %hx\n", addr, m, j);
+              CompareFail = 1;
+            }
+          if ((n != k) && (k != EOF))
+            {
+              printf ("Address %x - Cartridge %x: File %hx\n", addr + 1, n,
+                      k);
+              CompareFail = 1;
+            }
+          addr += 2;
+        }
+    }
+
+  // Correct verify length if at EOF
+  if (k == EOF)
+    addr--;
+
+  if (CompareFail == 0)
+    printf ("%d bytes compared OK\n", addr);
+}
+#endif
+
+
+void
+SpaceCheck (char c)
+{
+  if (c != 0)
+    {
+      fputs ("ERROR: Space required between option and parameter\n", stderr);
+      ProgramExit (1);
+    }
+}
+
+
+int
+fal_main (int argc, char **argv)
+{
+  int arg, i;
+  u8 Base = 0;
+  FILE *fp;
+  char fname[128], fname2[128];
+  int OptB = 0;
+  int OptD = 0;
+  int OptP = 0;
+  int OptR = 0;
+  int OptS = 0;
+  int OptV = 0;
+  int OptZ = 0;
+  int port = 0x378;
+  int ChipSize = 32;
+  int BackupMemOffset = 0;
+  int BackupMemSize = 0;
+
+  if (argc < 2)
+    {
+//      usage (argv[0]);
+      ProgramExit (1);
+    }
+
+  debug = 0;
+  verbose = 1;
+  EPPMode = 0;                                  // uCON64 comment: use the most compatible setting as default
+  DataSize16 = 0;
+  WaitDelay = 0;
+  VisolyTurbo = 0;
+  RepairHeader = 1;
+
+  for (arg = 1; arg < argc; arg++)
+    {
+      if (argv[arg][0] != '-')
+        {
+//          usage (argv[0]);
+          ProgramExit (1);
+        }
+
+      switch (argv[arg][1])
+        {
+        case 'b':
+          SpaceCheck (argv[arg][2]);
+          BackupMemOffset = *(char *) argv[++arg] - 0x30;
+          SpaceCheck (argv[arg][1]);
+          BackupMemSize = *(char *) argv[++arg] - 0x30;
+
+          if ((BackupMemSize < 1) || (BackupMemSize > 4) ||
+              (BackupMemOffset < 1) || (BackupMemOffset > 4))
+            {
+              fputs ("ERROR: -b parameter values must be between 1-4\n", stderr);
+              ProgramExit (1);
+            }
+          SpaceCheck (argv[arg][1]);
+          strcpy (fname, argv[++arg]);
+          OptB = 1;
+          break;
+        case '2':
+          // 16-bit EPP support enable (doesn't work with
+          //  "Turbo FA 128M (2 x i28F640J3A)" - dbjh)
+          DataSize16 = 1;
+          break;
+        case 'c':
+          // Set cart size
+          SpaceCheck (argv[arg][2]);
+          ChipSize = (u16) (atoi (argv[++arg]));
+          if ((ChipSize != 8) &&
+              (ChipSize != 16) &&
+              (ChipSize != 32) &&
+              (ChipSize != 64) && (ChipSize != 128) && (ChipSize != 256))
+            {
+              fputs ("ERROR: Chip size must be 8,16,32,64,128 or 256\n", stderr);
+              ProgramExit (1);
+            }
+          break;
+        case 'd':
+          // Dump 256 bytes to screen
+          SpaceCheck (argv[arg][2]);
+          if (argv[++arg] != NULL)
+            Base = (u8) (atoi (argv[arg]));
+          printf ("Base address: %hx\n", Base * 256);
+          OptD = 1;
+          break;
+        case 's':
+          // Backup flash cart
+          SpaceCheck (argv[arg][2]);
+          strcpy (fname, argv[++arg]);
+          OptS = 1;
+          break;
+        case 'p':
+          // Program flash cart
+          SpaceCheck (argv[arg][2]);
+          strcpy (fname, argv[++arg]);
+          OptP = 1;
+          break;
+        case 'r':
+          SpaceCheck (argv[arg][2]);
+          BackupMemOffset = *(char *) argv[++arg] - 0x30;
+          if ((BackupMemOffset < 1) || (BackupMemOffset > 4))
+            {
+              fputs ("ERROR: -r parameter value must be between 1-4\n", stderr);
+              ProgramExit (1);
+            }
+          SpaceCheck (argv[arg][1]);
+          strcpy (fname, argv[++arg]);
+          OptR = 1;
+          break;
+        case 'v':
+          // Verify flash cart
+          SpaceCheck (argv[arg][2]);
+          strcpy (fname2, argv[++arg]);
+          OptV = 1;
+          break;
+        case 'l':
+          SpaceCheck (argv[arg][2]);
+          i = atoi (argv[++arg]);
+          /*
+            uCON64 comment: we want to support non-standard parallel port
+            addresses too. So, instead of passing a number from 1 to 4, we pass
+            the address itself
+          */
+          port = i;
+          break;
+        case 'm':
+          // uCON64 comment: See comment in LinkerInit(). Note that we reverse
+          //  the meaning compared to the original code.
+          EPPMode = 1;                          // Set EPP mode
+          break;
+        case 'n':
+          // Don't repair header
+          RepairHeader = 0;
+          break;
+        case 'w':
+          SpaceCheck (argv[arg][2]);
+          WaitDelay = atoi (argv[++arg]);
+          break;
+        case 'z':
+          OptZ = 1;
+          break;
+        default:
+//          usage (argv[0]);
+          ProgramExit (1);
+        }
+    }
+
+  InitPort (port);
+
+  CheckForFC ();
+
+  if (OptB)
+    {
+      //DumpSRAM ();
+      if ((fp = fopen (fname, "wb")) == NULL)
+        {
+          fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], fname);
+          ProgramExit (1);
+        }
+
+      BackupSRAM (fp, BackupMemOffset, BackupMemSize);
+      fputc ('\n', stdout);
+      fclose (fp);
+    }
+
+  if (OptD)
+    dump (Base);
+
+  if ((OptP) && ((Device == 0) || (Device == 0x2e) || (Device == 0xff)))
+    {
+      fputs ("ERROR: Device type not recognized as programmable\n", stderr);
+      ProgramExit (1);
+    }
+
+  if (OptR)
+    {
+      if ((fp = fopen (fname, "rb")) == NULL)
+        {
+          fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], fname);
+          ProgramExit (1);
+        }
+
+      RestoreSRAM (fp, BackupMemOffset);
+      fputc ('\n', stdout);
+      fclose (fp);
+    }
+
+  if (OptP)
+    {
+      if ((fp = fopen (fname, "rb")) == NULL)
+        {
+          fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], fname);
+          ProgramExit (1);
+        }
+
+      if (Device == 0xe2)
+        ProgramSharpFlash (fp);
+      else
+        {
+          if (VisolyTurbo)
+            ProgramTurboIntelFlash (fp);
+          else
+            ProgramNonTurboIntelFlash (fp);
+        }
+      fputc ('\n', stdout);
+      fclose (fp);
+    }
+
+  if (OptS)
+    {
+      if ((fp = fopen (fname, "wb")) == NULL)
+        {
+          fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], fname);
+          ProgramExit (1);
+        }
+
+      BackupROM (fp, ChipSize << 16);
+      fputc ('\n', stdout);
+      fclose (fp);
+    }
+
+#if 0
+  if (OptV)
+    {
+      if ((fp = fopen (fname2, "rb")) == NULL)
+        {
+          fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], fname2);
+          ProgramExit (1);
+        }
+
+      VerifyFlash (fp);
+      fclose (fp);
+    }
+#endif
+
+  ProgramExit (0);
+  exit (0);
+}
+
+
+/*
+  It will save you some work if you don't fully integrate the code above with
+  uCON64's code, because it is a project separate from the uCON64 project.
+*/
+int fal_argc = 0;
+char *fal_argv[128];
+
+void
+fal_args (unsigned int parport)
+{
+  char parport_str[80];
+
+  parport_print_info ();
+
+  fal_argv[fal_argc++] = "fl";
+  fal_argv[fal_argc++] = "-l";
+  sprintf (parport_str, "%d", parport); // don't use %x, as Jeff Frohwein uses atoi()
+  fal_argv[fal_argc++] = parport_str;
+
+  if (ucon64.parport_mode == UCON64_EPP)
+    fal_argv[fal_argc++] = "-m";
+}
+
+
+int
+fal_read_rom (const char *filename, unsigned int parport, int size)
+{
+  char size_str[80];
+
+  fal_args (parport);
+
+  fal_argv[fal_argc++] = "-c";
+  if (size != 8 && size != 16 && size != 32 && size != 64 && size != 128 &&
+      size != 256)
+    {
+      fputs ("ERROR: Invalid argument for -xfalc=n\n"
+             "       n can be 8, 16, 32, 64, 128 or 256\n", stderr);
+      exit (1);
+    }
+  sprintf (size_str, "%d", size);
+  fal_argv[fal_argc++] = size_str;
+  fal_argv[fal_argc++] = "-s";
+  fal_argv[fal_argc++] = (char *) filename;     // this is safe (FAL code
+                                                //  doesn't modify argv)
+  if (!fal_main (fal_argc, fal_argv))
+    return 0;
+
+  return -1;
+}
+
+
+int
+fal_write_rom (const char *filename, unsigned int parport)
+{
+  fal_args (parport);
+
+  fal_argv[fal_argc++] = "-p";
+  fal_argv[fal_argc++] = (char *) filename;
+
+  if (!fal_main (fal_argc, fal_argv))
+    return 0;
+
+  return -1;
+}
+
+
+int
+fal_read_sram (const char *filename, unsigned int parport, int bank)
+{
+  char bank_str[2];
+
+  fal_args (parport);
+
+  fal_argv[fal_argc++] = "-b";
+  if (bank == UCON64_UNKNOWN)
+    {
+      fal_argv[fal_argc++] = "1";
+      fal_argv[fal_argc++] = "4";               // 256 kB
+    }
+  else
+    {
+      if (bank < 1 || bank > 4)
+        {
+          fputs ("ERROR: Bank must be 1, 2, 3 or 4\n", stderr);
+          exit (1);
+        }
+      bank_str[0] = '0' + bank;
+      bank_str[1] = 0;                          // terminate string
+      fal_argv[fal_argc++] = bank_str;
+      fal_argv[fal_argc++] = "2";               // 64 kB
+    }
+  fal_argv[fal_argc++] = (char *) filename;
+
+  if (!fal_main (fal_argc, fal_argv))
+    return 0;
+
+  return -1;
+}
+
+
+int
+fal_write_sram (const char *filename, unsigned int parport, int bank)
+{
+  char bank_str[2];
+
+  fal_args (parport);
+
+  fal_argv[fal_argc++] = "-r";
+  if (bank == UCON64_UNKNOWN)
+    fal_argv[fal_argc++] = "1";
+  else
+    {
+      if (bank < 1 || bank > 4)
+        {
+          fputs ("ERROR: Bank must be 1, 2, 3 or 4\n", stderr);
+          exit (1);
+        }
+      bank_str[0] = '0' + bank;
+      bank_str[1] = 0;                          // terminate string
+      fal_argv[fal_argc++] = bank_str;
+    }
+  fal_argv[fal_argc++] = (char *) filename;
+
+  if (!fal_main (fal_argc, fal_argv))
+    return 0;
+
+  return -1;
+}
+
+#endif // USE_PARALLEL

+ 35 - 0
ucon64/2.0/src/backup/fal.h

@@ -0,0 +1,35 @@
+/*
+fal.h - Flash Linker Advance support for uCON64
+
+Copyright (c) 2001        Jeff Frohwein
+Copyright (c) 2001        NoisyB <noisyb@gmx.net>
+Copyright (c) 2001 - 2002 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef FAL_H
+#define FAL_H
+
+extern const st_getopt2_t fal_usage[];
+
+#ifdef USE_PARALLEL
+extern int fal_read_rom (const char *filename, unsigned int parport, int size);
+extern int fal_write_rom (const char *filename, unsigned int parport);
+extern int fal_read_sram (const char *filename, unsigned int parport, int bank);
+extern int fal_write_sram (const char *filename, unsigned int parport, int bank);
+#endif
+
+#endif

+ 316 - 0
ucon64/2.0/src/backup/ffe.c

@@ -0,0 +1,316 @@
+/*
+ffe.c - General Front Far East copier routines for uCON64
+
+Copyright (c) 2002 - 2004 dbjh
+Copyright (c) 2003        JohnDie
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "misc/misc.h"                          // kbhit(), getch()
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "ffe.h"
+#include "misc/parallel.h"
+
+
+#ifdef  USE_PARALLEL
+
+#define N_TRY_MAX          65536                // # times to test if copier ready
+
+
+static void ffe_sendb (unsigned char byte);
+static unsigned char ffe_wait_while_busy (void);
+
+static int ffe_port;
+
+
+void
+ffe_init_io (unsigned int port)
+/*
+  - sets global `ffe_port'. Then the send/receive functions don't need to pass `port' all
+    the way to ffe_sendb()/ffe_receiveb().
+  - calls init_conio(). Necessary for kbhit() and DOS-like behaviour of getch().
+*/
+{
+  ffe_port = port;
+#if 0 // we want to support non-standard parallel port addresses
+  if (ffe_port != 0x3bc && ffe_port != 0x378 && ffe_port != 0x278)
+    {
+      fprintf (stderr, "ERROR: PORT must be 0x3bc, 0x378 or 0x278\n");
+      exit (1);
+    }
+#endif
+
+#if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
+  init_conio ();
+#endif
+
+  parport_print_info ();
+}
+
+
+void
+ffe_deinit_io (void)
+{
+#if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
+  deinit_conio ();
+#endif
+}
+
+
+void
+ffe_send_block (unsigned short address, unsigned char *buffer, int len)
+{
+  int n;
+  unsigned char checksum = 0x81;
+
+  ffe_send_command (0, address, (unsigned short) len);
+  for (n = 0; n < len; n++)
+    {
+      ffe_sendb (buffer[n]);
+      checksum ^= buffer[n];
+    }
+  ffe_sendb (checksum);
+}
+
+
+void
+ffe_send_block2 (unsigned short address, unsigned char *buffer, int len)
+{
+  int n;
+  unsigned char checksum = 0x81;
+
+  ffe_send_command (2, address, (unsigned short) len);
+  for (n = 0; n < len; n++)
+    {
+      ffe_sendb (buffer[n]);
+      checksum ^= buffer[n];
+    }
+  ffe_sendb (checksum);
+}
+
+
+void
+ffe_send_command0 (unsigned short address, unsigned char byte)
+// command 0 for 1 byte
+{
+  ffe_send_command (0, address, 1);
+  ffe_sendb (byte);
+  ffe_sendb ((unsigned char) (0x81 ^ byte));
+}
+
+
+unsigned char
+ffe_send_command1 (unsigned short address)
+// command 1 for 1 byte
+{
+  unsigned char byte;
+
+  ffe_send_command (1, address, 1);
+  byte = ffe_receiveb ();
+  if ((0x81 ^ byte) != ffe_receiveb ())
+    puts ("received data is corrupt");
+
+  return byte;
+}
+
+
+void
+ffe_send_command (unsigned char command_code, unsigned short a, unsigned short l)
+{
+  ffe_sendb (0xd5);
+  ffe_sendb (0xaa);
+  ffe_sendb (0x96);
+  ffe_sendb (command_code);
+  ffe_sendb ((unsigned char) a);                // low byte
+  ffe_sendb ((unsigned char) (a >> 8));         // high byte
+  ffe_sendb ((unsigned char) l);                // low byte
+  ffe_sendb ((unsigned char) (l >> 8));         // high byte
+  ffe_sendb ((unsigned char) (0x81 ^ command_code ^ a ^ (a >> 8) ^ l ^ (l >> 8))); // check sum
+}
+
+
+void
+ffe_sendb (unsigned char byte)
+{
+  ffe_wait_for_ready ();
+  outportb ((unsigned short) (ffe_port + PARPORT_DATA), byte);
+  outportb ((unsigned short) (ffe_port + PARPORT_CONTROL),
+            (unsigned char) (inportb ((unsigned short) // invert strobe
+                                      (ffe_port + PARPORT_CONTROL)) ^ PARPORT_STROBE));
+  ffe_wait_for_ready ();                        // necessary if followed by ffe_receiveb()
+}
+
+
+void
+ffe_receive_block (unsigned short address, unsigned char *buffer, int len)
+{
+  volatile int n;
+  int n_try = 0;
+  unsigned char checksum1, checksum2;
+
+  do
+    {
+      checksum1 = 0x81;
+      ffe_send_command (1, address, (unsigned short) len);
+      for (n = 0; n < len; n++)
+        {
+          buffer[n] = ffe_receiveb ();
+          checksum1 ^= buffer[n];
+        }
+      checksum2 = ffe_receiveb ();
+
+      for (n = 0; n < 65536; n++)               // a delay is necessary here
+        ;
+
+      n_try++;
+    }
+  while ((checksum1 != checksum2) && (n_try < N_TRY_MAX));
+
+  if (checksum1 != checksum2)
+    puts ("\nreceived data is corrupt");
+}
+
+
+void
+ffe_receive_block2 (unsigned short address, unsigned char *buffer, int len)
+{
+  volatile int n;
+  int n_try = 0;
+  unsigned char checksum1, checksum2;
+
+  do
+    {
+      checksum1 = 0x81;
+      ffe_send_command (3, address, (unsigned short) len);
+      for (n = 0; n < len; n++)
+        {
+          buffer[n] = ffe_receiveb ();
+          checksum1 ^= buffer[n];
+        }
+      checksum2 = ffe_receiveb ();
+
+      for (n = 0; n < 65536; n++)               // a delay is necessary here
+        ;
+
+      n_try++;
+    }
+  while ((checksum1 != checksum2) && (n_try < N_TRY_MAX));
+
+  if (checksum1 != checksum2)
+    puts ("\nreceived data is corrupt");
+}
+
+
+unsigned char
+ffe_receiveb (void)
+{
+  unsigned char byte;
+
+  byte = (unsigned char) ((ffe_wait_while_busy () & PARPORT_INPUT_MASK) >> 3); // receive low nibble
+  outportb ((unsigned short) (ffe_port + PARPORT_CONTROL),
+            (unsigned char) (inportb ((unsigned short) // invert strobe
+                                      (ffe_port + PARPORT_CONTROL)) ^ PARPORT_STROBE));
+  byte |= (unsigned char) ((ffe_wait_while_busy () & PARPORT_INPUT_MASK) << 1); // receive high nibble
+  outportb ((unsigned short) (ffe_port + PARPORT_CONTROL),
+            (unsigned char) (inportb ((unsigned short) // invert strobe
+                                      (ffe_port + PARPORT_CONTROL)) ^ PARPORT_STROBE));
+
+  return byte;
+}
+
+
+unsigned char
+ffe_wait_while_busy (void)
+{
+  unsigned char input;
+  int n_try = 0;
+
+  do
+    {
+      input = inportb ((unsigned short) (ffe_port + PARPORT_STATUS));
+      n_try++;
+    }
+  while (input & PARPORT_IBUSY && n_try < N_TRY_MAX);
+
+#if 0
+/*
+  VGS doesn't check for this, and it seems to happen quite regularly, so it
+  is currently commented out
+*/
+  if (n_try >= N_TRY_MAX)
+    {
+      fputs ("ERROR: The copier is not ready\n" // yes, "ready" :-)
+             "       Turn it off for a few seconds then turn it on and try again\n",
+             stderr);
+      exit (1);
+    }
+#endif
+
+  // read port again to let data settle down and to delay a little bit - JohnDie
+  return inportb ((unsigned short) (ffe_port + PARPORT_STATUS));
+}
+
+
+void
+ffe_wait_for_ready (void)
+{
+  unsigned char input;
+  int n_try = 0;
+
+  do
+    {
+      input = inportb ((unsigned short) (ffe_port + PARPORT_STATUS));
+      n_try++;
+    }
+  while (!(input & PARPORT_IBUSY) && n_try < N_TRY_MAX);
+
+#if 0
+  if (n_try >= N_TRY_MAX)
+    {
+      fputs ("ERROR: The copier is not ready\n"
+             "       Turn it off for a few seconds then turn it on and try again\n",
+             stderr);
+      exit (1);
+    }
+#endif
+}
+
+
+void
+ffe_checkabort (int status)
+{
+  if (((!ucon64.frontend) ? kbhit () : 0) && getch () == 'q')
+    {
+//      ffe_send_command (5, 0, 0);               // VGS: when sending/receiving a SNES ROM
+      puts ("\nProgram aborted");
+      exit (status);
+    }
+}
+
+#endif // USE_PARALLEL

+ 80 - 0
ucon64/2.0/src/backup/ffe.h

@@ -0,0 +1,80 @@
+/*
+ffe.h - General Front Far East copier routines for uCON64
+
+Copyright (c) 2002 - 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef FFE_H
+#define FFE_H
+
+/*
+    0      - Low byte of 8 kB page count
+             SMD: 16 kB page count
+    1      - High byte of 8 kB page count
+             SMD: File ID code 0 (3, not: high byte of 16 kB page count)
+             Magic Griffin: Emulation mode select, first byte?
+    2      - Emulation mode select (SWC/SMC/Magic Griffin, second byte?)
+             Bit 7 6 5 4 3 2 1 0
+                 x               : 1 = Run in mode 0 (JMP $8000) (higher precedence than bit 1)
+                   x             : 0 = Last file of the ROM dump (multi-file)
+                                 : 1 = Multi-file (there is another file to follow)
+                     x           : SWC & SMC:
+                                     0 = SRAM mapping mode 1 (LoROM)
+                                     1 = mode 2 (HiROM)
+                       x         : SWC & SMC:
+                                     0 = DRAM mapping mode 20 (LoROM)
+                                     1 = mode 21 (HiROM)
+                         x x     : SWC & SMC (SRAM size):
+                                     00 = 256 kb, 01 = 64 kb, 10 = 16 kb, 11 = no SRAM
+                             x   : SWC & SMC:
+                                     0 = Run in mode 3
+                                     1 = Run in mode 2 (JMP RESET)
+                               x : 0 = Disable, 1 = Enable external cartridge memory
+                                   image at bank 20-5F,A0-DF in system mode 2/3)
+    3-7    - 0, reserved
+    8      - File ID code 1 (0xaa)
+    9      - File ID code 2 (0xbb)
+    10     - File type; check this byte only if ID 1 & 2 match
+             1 : Super Magic Card saver data
+             2 : Magic Griffin program (PC-Engine)
+             3 : Magic Griffin SRAM data
+             4 : SNES program
+             5 : SWC & SMC password, SRAM data
+             6 : Mega Drive program
+             7 : SMD SRAM data
+             8 : SWC & SMC saver data
+    11-511 - 0, reserved
+*/
+
+#ifdef USE_PARALLEL
+
+extern void ffe_init_io (unsigned int port);
+extern void ffe_deinit_io (void);
+extern void ffe_send_block (unsigned short address, unsigned char *buffer, int len);
+extern void ffe_send_block2 (unsigned short address, unsigned char *buffer, int len);
+extern void ffe_send_command0 (unsigned short address, unsigned char byte);
+extern unsigned char ffe_send_command1 (unsigned short address);
+extern void ffe_send_command (unsigned char command_code, unsigned short a, unsigned short l);
+extern void ffe_receive_block (unsigned short address, unsigned char *buffer, int len);
+extern void ffe_receive_block2 (unsigned short address, unsigned char *buffer, int len);
+extern unsigned char ffe_receiveb (void);
+extern void ffe_wait_for_ready (void);
+extern void ffe_checkabort (int status);
+
+#endif // USE_PARALLEL
+
+#endif // FFE_H

+ 739 - 0
ucon64/2.0/src/backup/fig.c

@@ -0,0 +1,739 @@
+/*
+fig.c - Super PRO Fighter support for uCON64
+
+Copyright (c) 1999 - 2002 NoisyB <noisyb@gmx.net>
+Copyright (c) 2001 - 2004 dbjh
+Copyright (c) 2003 - 2004 JohnDie
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "misc/misc.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/parallel.h"
+#include "misc/file.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "ffe.h"
+#include "fig.h"
+#include "console/snes.h"                       // for snes_get_snes_hirom()
+
+
+const st_getopt2_t fig_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Super Pro Fighter (Q/Q+)/Pro Fighter X (Turbo 2)/Double Pro Fighter (X Turbo)"
+      /*"1993/1994/19XX China Coach Limited/CCL http://www.ccltw.com.tw"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xfig", 0, 0, UCON64_XFIG,
+      NULL, "send/receive ROM to/from *Pro Fighter*/FIG; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when ROM does not exist",
+      &ucon64_wf[WF_OBJ_SNES_DEFAULT_STOP_NO_SPLIT_NO_ROM]
+    },
+    {
+      "xfigs", 0, 0, UCON64_XFIGS,
+      NULL, "send/receive SRAM to/from *Pro Fighter*/FIG; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM]
+    },
+    {
+      "xfigc", 0, 0, UCON64_XFIGC, NULL,
+      "send/receive SRAM to/from cartridge in *Pro Fighter*/FIG;\n" OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+//      "Press q to abort; ^C might cause invalid state of backup unit"
+      &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM]
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef USE_PARALLEL
+
+#define BUFFERSIZE 8192                         // don't change, only 8192 works!
+
+
+static int receive_rom_info (unsigned char *buffer);
+static int get_rom_size (unsigned char *info_block);
+static int check1 (unsigned char *info_block, int index);
+static int check2 (unsigned char *info_block, int index, unsigned char value);
+static int check3 (unsigned char *info_block, int index1, int index2, int size);
+static void handle_swc_header (unsigned char *header);
+
+static int hirom;
+
+
+#if BUFFERSIZE < 512
+#error receive_rom_info() and fig_read_sram() expect BUFFERSIZE to be at least \
+       512 bytes.
+#endif
+int
+receive_rom_info (unsigned char *buffer)
+/*
+  - returns size of ROM in Mb (128 kB) units
+  - sets global `hirom'
+*/
+{
+  int n, size;
+  volatile int m;
+  unsigned char byte;
+
+  ffe_send_command0 (0xe00c, 0);
+
+  if (UCON64_ISSET (ucon64.snes_hirom))
+    hirom = ucon64.snes_hirom ? 1 : 0;
+  else
+    {
+      ffe_send_command (5, 3, 0);
+      byte = ffe_send_command1 (0xbfd5);
+      if ((byte & 1 && byte != 0x23) || byte == 0x3a) // & 1 => 0x21, 0x31, 0x35
+        hirom = 1;
+    }
+
+  for (n = 0; n < (int) FIG_HEADER_LEN; n++)
+    {
+      for (m = 0; m < 65536; m++)               // a delay is necessary here
+        ;
+      ffe_send_command (5, (unsigned short) (0x200 + n), 0);
+      buffer[n] = ffe_send_command1 (0xa0a0);
+    }
+
+  size = get_rom_size (buffer);
+  if (hirom)
+    size <<= 1;
+
+  return size;
+}
+
+
+int
+get_rom_size (unsigned char *info_block)
+// returns size of ROM in Mb units
+{
+  if (check1 (info_block, 0))
+    return 0;
+  if (check2 (info_block, 0x10, 0x84))
+    return 0;
+  if (check3 (info_block, 0, 0x20, 0x20))
+    return 2;
+  if (check3 (info_block, 0, 0x40, 0x20))
+    return 4;
+  if (check3 (info_block, 0x40, 0x60, 0x20))
+    return 6;
+  if (check3 (info_block, 0, 0x80, 0x10))
+    return 8;
+  if (check1 (info_block, 0x80))
+    return 8;
+  if (check3 (info_block, 0x80, 0x90, 0x10))
+    return 8;
+  if (check2 (info_block, 0x80, 0xa0))
+    return 8;
+  if (check3 (info_block, 0x80, 0xa0, 0x20))
+    return 0xa;
+  if (check1 (info_block, 0xc0))
+    return 0xc;
+  if (check2 (info_block, 0xc0, 0xb0))
+    return 0xc;
+  if (check3 (info_block, 0x80, 0xc0, 0x20))
+    return 0xc;
+  if (check3 (info_block, 0x100, 0, 0x10))
+    return 0x10;
+  if (check2 (info_block, 0x100, 0xc0))
+    return 0x10;
+  if (check3 (info_block, 0x100, 0x120, 0x10))
+    return 0x12;
+  if (check3 (info_block, 0x100, 0x140, 0x10))
+    return 0x14;
+  if (check2 (info_block, 0x140, 0xd0))
+    return 0x14;
+  if (check3 (info_block, 0x100, 0x180, 0x10))
+    return 0x18;
+  if (check2 (info_block, 0x180, 0xe0))
+    return 0x18;
+  if (check3 (info_block, 0x180, 0x1c0, 0x10))
+    return 0x1c;
+  if (check3 (info_block, 0x1f0, 0x1f0, 0x10))
+    return 0x20;
+
+  return 0;
+}
+
+
+int
+check1 (unsigned char *info_block, int index)
+{
+  int n;
+
+  for (n = 0; n < 16; n++)
+    if (info_block[n + index] != info_block[index])
+      return 0;
+
+  return 1;
+}
+
+
+int
+check2 (unsigned char *info_block, int index, unsigned char value)
+{
+  int n;
+
+  for (n = 0; n < 4; n++)
+    if (info_block[n + index] != value)
+      return 0;
+
+  return 1;
+}
+
+
+int
+check3 (unsigned char *info_block, int index1, int index2, int size)
+{
+  int n;
+
+  for (n = 0; n < size; n++)
+    if (info_block[n + index1] != info_block[n + index2])
+      return 0;
+
+  return 1;
+}
+
+
+void
+handle_swc_header (unsigned char *header)
+{
+  if ((header[2] & 0x10) == 0x10)
+    {                                            // HiROM
+      header[3] |= 0x80;
+
+      if ((header[2] & 0x0c) == 0x0c)            // 0 Kbit SRAM
+        {
+          header[4] = 0x77;
+          header[5] = 0x83;
+        }
+      else if (((header[2] & 0x0c) == 0x08) ||   // 16 *or* 64 Kbit SRAM
+               ((header[2] & 0x0c) == 0x04))
+        {
+          header[4] = 0xdd;
+          header[5] = 0x82;
+        }
+      else                                       // 256 Kbit SRAM
+        {
+          header[4] = 0xdd;
+          header[5] = 0x02;
+        }
+    }
+  else
+    {                                            // LoROM
+      header[3] &= 0x7f;
+
+      if ((header[2] & 0x0c) == 0x0c)            // 0 Kbit SRAM
+        {
+          header[4] = 0x77;
+          header[5] = 0x83;
+        }
+      else if (((header[2] & 0x0c) == 0x08) ||   // 16 *or* 64 Kbit SRAM
+               ((header[2] & 0x0c) == 0x04))
+        {
+          header[4] = 0x00;
+          header[5] = 0x80;
+        }
+      else                                       // 256 Kbit SRAM
+        {
+          header[4] = 0x00;
+          header[5] = 0x00;
+        }
+    }
+}
+
+
+int
+fig_read_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int n, size, blocksleft, bytesreceived = 0;
+  unsigned short address1, address2;
+  time_t starttime;
+  st_rominfo_t rominfo;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = receive_rom_info (buffer);
+  if (size == 0)
+    {
+      fprintf (stderr, "ERROR: There is no cartridge present in the Pro Fighter\n");
+      fclose (file);
+      remove (filename);
+      exit (1);
+    }
+  blocksleft = size * 16;                       // 1 Mb (128 kB) unit == 16 8 kB units
+  printf ("Receive: %d Bytes (%.4f Mb)\n", size * MBIT, (float) size);
+  size *= MBIT;                                 // size in bytes for ucon64_gauge() below
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_command0 (0xe00c, 0);
+  ffe_send_command0 (0xe003, 0);
+//  byte = ffe_send_command1 (0xbfd8);
+
+  memset (buffer, 0, FIG_HEADER_LEN);
+  fwrite (buffer, 1, FIG_HEADER_LEN, file);     // write temporary empty header
+
+  if (hirom)
+    blocksleft >>= 1;
+
+  printf ("Press q to abort\n\n");              // print here, NOT before first FIG I/O,
+                                                //  because if we get here q works ;-)
+  address1 = 0x300;                             // address1 = 0x100, address2 = 0 should
+  address2 = 0x200;                             //  also work
+  starttime = time (NULL);
+  while (blocksleft > 0)
+    {
+      if (hirom)
+        for (n = 0; n < 4; n++)
+          {
+            ffe_send_command (5, address1, 0);
+            ffe_receive_block (0x2000, buffer, BUFFERSIZE);
+            address1++;
+            fwrite (buffer, 1, BUFFERSIZE, file);
+
+            bytesreceived += BUFFERSIZE;
+            ucon64_gauge (starttime, bytesreceived, size);
+            ffe_checkabort (2);
+          }
+
+      for (n = 0; n < 4; n++)
+        {
+          ffe_send_command (5, address2, 0);
+          ffe_receive_block (0xa000, buffer, BUFFERSIZE);
+          blocksleft--;
+          address2++;
+          fwrite (buffer, 1, BUFFERSIZE, file);
+
+          bytesreceived += BUFFERSIZE;
+          ucon64_gauge (starttime, bytesreceived, size);
+          ffe_checkabort (2);
+        }
+    }
+  ffe_send_command (5, 0, 0);
+
+  // Create a correct header. We can't obtain the header from the Pro Fighter
+  //  unless a (the same) cartridge was just dumped to diskette...
+  ucon64.rom = filename;
+  ucon64.file_size = size + FIG_HEADER_LEN;
+  // override everything we know for sure
+  ucon64.console = UCON64_SNES;
+  ucon64.buheader_len = FIG_HEADER_LEN;
+  ucon64.split = 0;
+  ucon64.snes_hirom = hirom ? SNES_HIROM : 0;
+  ucon64.interleaved = 0;
+  memset (&rominfo, 0, sizeof (st_rominfo_t));
+
+  fflush (file);
+  snes_init (&rominfo);
+  memset (buffer, 0, FIG_HEADER_LEN);
+  snes_set_fig_header (&rominfo, (st_fig_header_t *) buffer);
+  fseek (file, 0, SEEK_SET);
+  fwrite (buffer, 1, FIG_HEADER_LEN, file);     // write correct header
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+fig_write_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int bytesread = 0, bytessend, totalblocks, blocksdone = 0, blocksleft, fsize,
+      n, emu_mode_select;
+  unsigned short address1, address2;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  fsize = fsizeof (filename);
+  printf ("Send: %d Bytes (%.4f Mb)\n", fsize, (float) fsize / MBIT);
+
+  ffe_send_command0 (0xc008, 0);
+  fread (buffer, 1, FIG_HEADER_LEN, file);
+
+  if (snes_get_file_type () == SWC)
+    handle_swc_header (buffer);
+  emu_mode_select = buffer[2];                  // this byte is needed later
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_block (0x400, buffer, FIG_HEADER_LEN); // send header
+  bytessend = FIG_HEADER_LEN;
+
+  hirom = snes_get_snes_hirom ();
+  if (hirom)
+    ffe_send_command0 (0xe00f, 0);              // seems to enable HiROM mode,
+                                                //  value doesn't seem to matter
+  printf ("Press q to abort\n\n");              // print here, NOT before first FIG I/O,
+                                                //  because if we get here q works ;-)
+  totalblocks = (fsize - FIG_HEADER_LEN + BUFFERSIZE - 1) / BUFFERSIZE; // round up
+  blocksleft = totalblocks;
+  address1 = 0x300;
+  address2 = 0x200;
+  starttime = time (NULL);
+  while (blocksleft > 0)
+    {
+      if (hirom)
+        for (n = 0; n < 4; n++)
+          {
+            bytesread = fread (buffer, 1, BUFFERSIZE, file);
+            ffe_send_command0 ((unsigned short) 0xc010, (unsigned char) (blocksdone >> 9));
+            ffe_send_command (5, address1, 0);
+            ffe_send_block (0x0000, buffer, bytesread);
+            address1++;
+            blocksleft--;
+            blocksdone++;
+
+            bytessend += bytesread;
+            ucon64_gauge (starttime, bytessend, fsize);
+            ffe_checkabort (2);
+          }
+
+      for (n = 0; n < 4; n++)
+        {
+          bytesread = fread (buffer, 1, BUFFERSIZE, file);
+          ffe_send_command0 ((unsigned short) 0xc010, (unsigned char) (blocksdone >> 9));
+          ffe_send_command (5, address2, 0);
+          ffe_send_block (0x8000, buffer, bytesread);
+          address2++;
+          blocksleft--;
+          blocksdone++;
+
+          bytessend += bytesread;
+          ucon64_gauge (starttime, bytessend, fsize);
+          ffe_checkabort (2);
+        }
+    }
+
+  if (blocksdone > 0x200)                       // ROM dump > 512 8 kB blocks (=32 Mb (=4 MB))
+    ffe_send_command0 (0xc010, 2);
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_command (6, (unsigned short) (1 | (emu_mode_select << 8)), 0);
+
+  ffe_wait_for_ready ();
+  outportb ((unsigned short) (parport + PARPORT_DATA), 0);
+  outportb ((unsigned short) (parport + PARPORT_CONTROL),
+            (unsigned char) (inportb ((unsigned short) // invert strobe
+                                      (parport + PARPORT_CONTROL)) ^ PARPORT_STROBE));
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+fig_read_sram (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int blocksleft, bytesreceived = 0;
+  unsigned short address;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  printf ("Receive: %d Bytes\n", 32 * 1024);
+  memset (buffer, 0, FIG_HEADER_LEN);
+#if 0 // Not needed for FIG, as size is always 4 blocks
+  buffer[0] = 4;                                // 32 kB == 4*8 kB, "size_high" is already 0
+#endif
+  fwrite (buffer, 1, FIG_HEADER_LEN, file);
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_command0 (0xe00d, 0);
+  ffe_send_command0 (0xc008, 0);
+
+  printf ("Press q to abort\n\n");              // print here, NOT before first FIG I/O,
+                                                //  because if we get here q works ;-)
+  blocksleft = 4;                               // SRAM is 4*8 kB
+  address = 0x100;
+  starttime = time (NULL);
+  while (blocksleft > 0)
+    {
+      ffe_send_command (5, address, 0);
+      ffe_receive_block (0x2000, buffer, BUFFERSIZE);
+      blocksleft--;
+      address++;
+      fwrite (buffer, 1, BUFFERSIZE, file);
+
+      bytesreceived += BUFFERSIZE;
+      ucon64_gauge (starttime, bytesreceived, 32 * 1024);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+fig_write_sram (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int bytesread, bytessend = 0, size;
+  unsigned short address;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = fsizeof (filename) - FIG_HEADER_LEN;   // FIG SRAM is 4*8 kB, emu SRAM often not
+  printf ("Send: %d Bytes\n", size);
+  fseek (file, FIG_HEADER_LEN, SEEK_SET);       // skip the header
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_command0 (0xe00d, 0);
+  ffe_send_command0 (0xc008, 0);
+
+  printf ("Press q to abort\n\n");              // print here, NOT before first FIG I/O,
+                                                //  because if we get here q works ;-)
+  address = 0x100;
+  starttime = time (NULL);
+  while ((bytesread = fread (buffer, 1, BUFFERSIZE, file)))
+    {
+      ffe_send_command (5, address, 0);
+      ffe_send_block (0x2000, buffer, bytesread);
+      address++;
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+fig_read_cart_sram (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer, byte;
+  int bytesreceived = 0, size;
+  unsigned short address;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = receive_rom_info (buffer);
+  if (size == 0)
+    {
+      fprintf (stderr, "ERROR: There is no cartridge present in the Pro Fighter\n");
+      fclose (file);
+      remove (filename);
+      exit (1);
+    }
+
+  ffe_send_command (5, 3, 0);                   // detect cartridge SRAM size because
+  ffe_send_command0 (0xe00c, 0);                //  we don't want to read too few data
+  byte = ffe_send_command1 (0xbfd8);
+
+  size = MAX ((byte ? 1 << (byte + 10) : 0), 32 * 1024);
+  printf ("Receive: %d Bytes\n", size);
+
+  memset (buffer, 0, FIG_HEADER_LEN);
+#if 0 // Not needed for FIG, as size is always 4 blocks
+  buffer[0] = 4;                                // 32 kB == 4*8 kB, "size_high" is already 0
+#endif
+  fwrite (buffer, 1, FIG_HEADER_LEN, file);
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_command0 (0xe00c, 0);
+//  ffe_send_command0 (0xc008, 0);
+
+  printf ("Press q to abort\n\n");              // print here, NOT before first FIG I/O,
+                                                //  because if we get here q works ;-)
+  address = hirom ? 0x2c3 : 0x1c0;
+
+  starttime = time (NULL);
+  while (bytesreceived < size)
+    {
+      ffe_send_command (5, address, 0);
+      ffe_receive_block ((unsigned short) (hirom ? 0x6000 : 0x2000), buffer, BUFFERSIZE);
+      fwrite (buffer, 1, BUFFERSIZE, file);
+      address += hirom ? 4 : 1;
+
+      bytesreceived += BUFFERSIZE;
+      ucon64_gauge (starttime, bytesreceived, size);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+fig_write_cart_sram (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer, byte;
+  int bytesread, bytessend = 0, size;
+  unsigned short address;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = receive_rom_info (buffer);
+  if (size == 0)
+    {
+      fprintf (stderr, "ERROR: There is no cartridge present in the Pro Fighter\n");
+      fclose (file);
+      remove (filename);
+      exit (1);
+    }
+
+  ffe_send_command (5, 3, 0);                   // detect cartridge SRAM size because we don't
+  ffe_send_command0 (0xe00c, 0);                //  want to write more data than necessary
+  byte = ffe_send_command1 (0xbfd8);
+
+  size = fsizeof (filename) - FIG_HEADER_LEN;   // FIG SRAM is 4*8 kB, emu SRAM often not
+  size = MIN ((byte ? 1 << (byte + 10) : 0), size);
+
+  printf ("Send: %d Bytes\n", size);
+  fseek (file, FIG_HEADER_LEN, SEEK_SET);       // skip the header
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_command0 (0xe00c, 0);
+//  ffe_send_command0 (0xc008, 0);
+
+  printf ("Press q to abort\n\n");              // print here, NOT before first FIG I/O,
+                                                //  because if we get here q works ;-)
+  address = hirom ? 0x2c3 : 0x1c0;
+
+  starttime = time (NULL);
+  while ((bytessend < size) && (bytesread = fread (buffer, 1, MIN (size, BUFFERSIZE), file)))
+    {
+      ffe_send_command (5, address, 0);
+      ffe_send_block ((unsigned short) (hirom ? 0x6000 : 0x2000), buffer, bytesread);
+      address += hirom ? 4 : 1;
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 100 - 0
ucon64/2.0/src/backup/fig.h

@@ -0,0 +1,100 @@
+/*
+fig.h - Super PRO Fighter support for uCON64
+
+Copyright (c) 1999 - 2002 NoisyB <noisyb@gmx.net>
+Copyright (c) 2001 - 2003 dbjh
+Copyright (c) 2003        JohnDie
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef FIG_H
+#define FIG_H
+
+extern const st_getopt2_t fig_usage[];
+
+#ifdef USE_PARALLEL
+#endif // USE_PARALLEL
+
+/*
+Super Pro Fighter (FIG) Header Format
+Last edited: 19.06.2002
+
+Offset       |  Content
+-------------+------------------------------------
+$0000        |  Lo-Byte of 8K-Block#
+-------------+------------------------------------
+$0001        |  Hi-Byte of 8K-Block#
+-------------+------------------------------------
+$0002        |  $00 = Last File
+             |  $40 = More Files Present
+-------------+------------------------------------
+$0003        |  $00 = LoROM
+             |  $80 = HiROM
+-------------+------------------------------------
+$0004-$0005  |  $77 $83 = No SRAM  (LoROM)
+             |  $00 $80 = 16 KBit  (LoROM)
+             |  $00 $80 = 64 KBit  (LoROM)
+             |  $00 $00 = 256 KBit (LoROM)
+             |  $47 $83 = No SRAM  (LoROM) (DSP)
+             |  $11 $02 = 256 KBit (LoROM) (SFX)
+             |  $77 $83 = No SRAM  (HiROM)
+             |  $DD $82 = 16 KBit  (HiROM)
+             |  $DD $82 = 64 KBit  (HiROM)
+             |  $DD $02 = 256 KBit (HiROM)
+             |  $F7 $83 = No SRAM  (HiROM) (DSP)
+             |  $FD $82 = 16 KBit  (HiROM) (DSP)
+-------------+------------------------------------
+$0006-$01FF  |  Reserved (=$00)
+
+
+
+NOTE 1: The Super Pro Fighter does not distinguish between 16 KBit SRAM
+        and 64 KBit SRAM.
+
+NOTE 2: When splitting files, the SPF writes all relevant header fields
+        to all files. So each file has the same header with exception of
+        the last one, because it has $0002 set to $00 to indicate that it
+        is the last file.
+*/
+typedef struct st_fig_header
+{
+/*
+  Don't create fields that are larger than one byte! For example size_low and size_high
+  could be combined in one unsigned short int. However, this gives problems with little
+  endian vs. big endian machines (e.g. writing the header to disk).
+*/
+  unsigned char size_low;
+  unsigned char size_high;
+  unsigned char multi;
+  unsigned char hirom;
+  unsigned char emulation1;
+  unsigned char emulation2;
+  unsigned char pad[506];
+} st_fig_header_t;
+
+#define FIG_HEADER_START 0
+#define FIG_HEADER_LEN (sizeof (st_fig_header_t))
+
+#ifdef USE_PARALLEL
+extern int fig_read_rom (const char *filename, unsigned int parport);
+extern int fig_write_rom (const char *filename, unsigned int parport);
+extern int fig_read_sram (const char *filename, unsigned int parport);
+extern int fig_write_sram (const char *filename, unsigned int parport);
+extern int fig_read_cart_sram (const char *filename, unsigned int parport);
+extern int fig_write_cart_sram (const char *filename, unsigned int parport);
+#endif
+
+#endif // FIG_H

+ 1991 - 0
ucon64/2.0/src/backup/gbx.c

@@ -0,0 +1,1991 @@
+/*
+gbx.c - Game Boy Xchanger/GBDoctor support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2001 - 2004 dbjh
+Based on gbt15.c - Copyright (c) Bung Enterprises
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/********************************************************************
+*  use parallel EPP/SPP port to r/w game boy cartridge              *
+*                                                                   *
+* ai[]=0 w a[7..0]                                                  *
+* ai[]=1 w a[15..8]                                                 *
+* ai[]=2 w control d7=rs,d6=spp,d1=xwe_en,d0=cs_en                  *
+* ai[]=3 r/w data                                                   *
+*                                                                   *
+* MBC1                                                              *
+*   R/W A000~BFFF   RAM SWITCHING BANK(256Kbit) 4 BANKS OF 8Kbyte   *
+*   R 4000~7FFF     ROM SWITCHING BANK(4Mbit) 32 BANKS OF 128Kbit   *
+*   W 2000~3FFF     SET ROM BANK (5 BIT)                            *
+*   R 0000~3FFF     FIX ROM BANK 0                                  *
+*   W 4000~5FFF     SET RAM BANK (2 BIT)                            *
+*   W 0000~1FFF     SET 0A ENABLE RAM BANK                          *
+*                                                                   *
+* MBC2                                                              *
+*   R/W A000~BFFF   512 X 4 BIT RAM                                 *
+*   R 4000~7FFF     ROM SWITCHING BANK(2Mbit) 16 BANKS OF 128Kbit   *
+*   W 2100          SET ROM BANK (4 BIT)                            *
+*   R 0000~3FFF     FIX ROM BANK 0                                  *
+*   W 0000          SET 0A ENABLE RAM BANK                          *
+*                                                                   *
+* MBC5                                                              *
+*   R/W A000~BFFF   RAM SWITCHING BANK(1Mbit) 16 BANKS OF 64 Kbit   *
+*   R 4000~7FFF     ROM SWITCHING BANK(64Mbit) 512 BANKS OF 128Kbit *
+*   W 3000~3FFF     SET ROM BANK1(BANK Q8)  TOTAL 9 BIT             *
+*   W 2000~2FFF     SET ROM BANK0(BANK Q7~Q0)                       *
+*   R 0000~3FFF     FIX ROM BANK 0                                  *
+*   W 4000~7FFF     SET RAM BANK (4 BIT)                            *
+*   W 0000~1FFF     SET 0A ENABLE RAM BANK                          *
+*                                                                   *
+********************************************************************/
+
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include "misc/misc.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/parallel.h"
+#include "misc/file.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "gbx.h"
+#include "console/gb.h"                         // gb_logodata, rocket_logodata
+
+
+const st_getopt2_t gbx_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Game Boy Xchanger/GBDoctor"/*"19XX Bung Enterprises Ltd http://www.bung.com.hk"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xgbx", 0, 0, UCON64_XGBX,
+      NULL, "send/receive ROM to/from GB Xchanger; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when ROM does not exist",
+      &ucon64_wf[WF_OBJ_GB_DEFAULT_STOP_NO_ROM]
+    },
+    {
+      "xgbxs", 0, 0, UCON64_XGBXS,
+      NULL, "send/receive SRAM to/from GB Xchanger; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_GB_STOP_NO_ROM]
+    },
+    {
+      "xgbxb", 1, 0, UCON64_XGBXB,
+      "BANK", "send/receive 64 kbits SRAM to/from GB Xchanger BANK\n"
+      "BANK can be a number from 0 to 15; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when ROM does not exist",
+      &ucon64_wf[WF_OBJ_GB_STOP_NO_ROM]
+    },
+    {
+      "xgbxm", 0, 0, UCON64_XGBXM,
+      NULL, "try to enable EPP mode, default is SPP mode",
+      &ucon64_wf[WF_OBJ_GB_SWITCH]
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef USE_PARALLEL
+
+//#define set_ai_write outportb (port_a, 5);    // ninit=1, nwrite=0
+#define set_data_read outportb (port_a, 0);     // ninit=0, nastb=1, nib_sel=0, ndstb=1, nwrite=1
+#define set_data_write outportb (port_a, 1);    // ninit=0, nastb=1, nib_sel=0, ndstb=1, nwrite=0
+//#define set_normal outportb (port_a, 4);      // ninit=1, nwrite=1
+
+typedef enum { UNKNOWN_MBC, BUNG, ROM, MBC1, MBC2, MBC3, MBC5, CAMERA, ROCKET } mbc_t;
+typedef enum { UNKNOWN_EEPROM, WINBOND, MX, INTEL } eeprom_t;
+
+static unsigned short int port_8, port_9, port_a, port_b, port_c, rocket_game_no;
+static unsigned char buffer[32768];
+static mbc_t mbc_type;
+static eeprom_t eeprom_type;
+static parport_mode_t port_mode;
+
+
+static volatile void
+delay_us (int n_us)
+// Hmmm, usleep() can be used under UNIX, but default under DOS is delay() which
+//  waits for a number of milliseconds... Better use the same code on all platforms.
+{
+  volatile int n;
+  int n_max;
+
+  // it's probably best to use the original strange loop values
+  if (n_us == 10)
+    n_max = 0x2000;
+  else if (n_us == 100)
+    n_max = 0x10000;
+  else if (n_us == 20000)
+    n_max = 0xfffff;
+  else
+    n_max = n_us * 1000;
+
+  for (n = 0; n < n_max; n++)
+    ;
+}
+
+
+static void
+spp_set_ai (unsigned char ai)
+{
+  set_data_write
+//  outportb (port_a, 1);                         // nastb=1,nib_sel=0,ndstb=1,nwrite=0
+  outportb (port_8, ai);                        // put ai at data bus
+  outportb (port_a, 9);                         // nastb=0,nib_sel=0,ndstb=1,nwrite=0
+  outportb (port_a, 1);                         // nastb=1,nib_sel=0,ndstb=1,nwrite=0
+  // nastb ~~~~|___|~~~~
+}
+
+
+static void
+spp_write_data (unsigned char data)
+{
+//  outportb (port_a, 1);                         // nastb=1,nib_sel=0,ndstb=1,nwrite=0
+  outportb (port_8, data);                      // put data at data bus
+  outportb (port_a, 3);                         // nastb=1,nib_sel=0,ndstb=0,nwrite=0
+  outportb (port_a, 1);                         // nastb=1,nib_sel=0,ndstb=1,nwrite=0
+  // ndstb ~~~~|___|~~~~
+}
+
+
+static void
+spp_set_ai_data (unsigned char ai, unsigned char data)
+{
+  spp_set_ai (ai);
+  spp_write_data (data);
+}
+
+
+static unsigned char
+spp_read_data (void)
+{
+  unsigned char byte;
+
+  set_data_read
+  outportb (port_a, 2);                         // nastb=1,nib_sel=0,ndstb=0,nwrite=1
+  byte = (inportb (port_9) >> 3) & 0x0f;
+  outportb (port_a, 6);                         // nastb=1,nib_sel=1,ndstb=0,nwrite=1
+  byte |= (inportb (port_9) << 1) & 0xf0;
+  outportb (port_a, 0);                         // nastb=1,nib_sel=0,ndstb=1,nwrite=1
+  // nibble_sel ___|~~~ and ndstb ~~~~|___|~~~~
+
+  return byte;
+}
+
+
+static void
+epp_set_ai (unsigned char ai)
+{
+  set_data_write
+  outportb (port_b, ai);
+}
+
+
+static void
+epp_set_ai_data (unsigned char ai, unsigned char data)
+{
+  epp_set_ai (ai);
+  set_data_write
+  outportb (port_c, data);
+}
+
+
+static void
+set_ai (unsigned char ai)
+{
+  set_data_write
+  if (port_mode == UCON64_SPP)
+    spp_set_ai (ai);
+  else
+    epp_set_ai (ai);
+}
+
+
+static void
+set_ai_data (unsigned char ai, unsigned char data)
+{
+  if (port_mode == UCON64_SPP)
+    spp_set_ai_data (ai, data);                 // SPP mode
+  else
+    epp_set_ai_data (ai, data);                 // EPP mode
+}
+
+
+static void
+write_data (unsigned char data)
+{
+  if (port_mode == UCON64_SPP)
+    spp_write_data (data);                      // SPP write data
+  else
+    outportb (port_c, data);                    // EPP write data
+}
+
+
+static unsigned char
+read_data (void)
+{
+  if (port_mode == UCON64_SPP)
+    return spp_read_data ();                    // SPP read data
+  else
+    return inportb (port_c);                    // EPP read data
+}
+
+
+static void
+init_port (void)
+{
+#ifndef USE_PPDEV
+  outportb (port_9, 1);                         // clear EPP time flag
+#endif
+  set_ai_data ((unsigned char) 2, 0);           // rst=0, wei=0(dis.), rdi=0(dis.)
+  set_ai_data ((unsigned char) 2, 0x80);        // rst=1, wei=0(dis.), rdi=0(dis.)
+}
+
+
+static void
+end_port (void)
+{
+  set_ai_data ((unsigned char) 2, 0);           // rst=0, wei=0(dis.), rdi=0(dis.)
+  outportb (port_a, 4);                         // ninit=1, nwrite=1
+}
+
+
+static void
+set_adr (unsigned int adr)
+{
+  set_ai_data ((unsigned char)  0, (unsigned char) adr); // a[7..0]
+  set_ai_data ((unsigned char)  1, (unsigned char) (adr >> 8)); // a[15..8]
+  set_ai (3);
+  set_data_read                                 // ninit=0, nwrite=1
+}
+
+
+static void
+set_adr_long (unsigned int adr, int ignore_xh)  // real address
+{
+  unsigned char xh, h, m, l;
+
+  set_ai_data ((unsigned char) 2, 0x80);        // disable wr/rd inc.
+  l = (unsigned char) adr;                      // a7-a0
+  m = (unsigned char) (adr >> 8) & 0x3f;        // a13-a8
+  h = (unsigned char) (adr >> 14) & 0xff;       // a21-a13
+  if (h)
+    m |= 0x40;                                  // > bank 0
+
+  if (!ignore_xh)
+    {
+      xh = (unsigned char) (adr >> 22) & 0x7;   // max. 256Mbit
+      if (xh)
+        m |= 0x40;                              // > bank 0
+      set_adr (0x3000);                         // write 3000:xh
+      set_data_write
+      write_data (xh);                          // set ROM bank extend value
+    }
+
+  set_adr (0x2000);                             // write 2000:h
+  set_data_write
+  write_data (h);                               // set ROM bank value
+  set_ai_data ((unsigned char) 1, m);           // a[15..8]
+  set_ai_data ((unsigned char) 0, l);           // a[7..0]
+}
+
+
+static void
+set_bank (unsigned int adr, unsigned char bank)
+{
+  set_ai_data ((unsigned char) 2, 0x80);        // disable inc
+  set_ai_data ((unsigned char) 0, (unsigned char) adr); // a[7..0]
+  set_ai_data ((unsigned char) 1, (unsigned char) ((adr >> 8) & 0x7f)); // a[15..8]
+  set_ai_data ((unsigned char) 3, bank);        // write bank no
+  set_data_read                                 // ninit=0, nwrite=1
+}
+
+
+static void
+out_byte_eeprom (unsigned char data)
+{
+  set_ai_data ((unsigned char) 2, 0x82);        // wei enable
+  set_ai (3);                                   // default write mode
+//  set_data_read                                 // ninit=0, nwrite=1
+  set_data_write
+  write_data (data);                            // out data
+  set_ai_data ((unsigned char) 2, 0x80);        // wei disable
+  set_ai (3);                                   // default write mode
+}
+
+
+static void
+out_byte (unsigned char data)
+{
+  set_ai (3);
+//  set_data_read                                 // ninit=0, nwrite=1
+  set_data_write
+  write_data (data);                            // out data
+}
+
+
+static void
+out_data (unsigned char h, unsigned char m, unsigned char l, unsigned char data)
+{
+  // ai[]=2 w control d7=rs,d1=xwe_en,d0=cs_en
+  h = ((h << 2) | (m >> 6)) & 0x1f;             // maximum bank is 1f
+  if (h)
+    m = (m & 0x3f) | 0x40;                      // > bank 0
+  else
+    m = m & 0x3f;                               // bank 0
+
+  set_adr (0x2000);                             // write 2000:h
+  set_data_write
+  write_data (h);                               // set ROM bank value
+  set_ai_data ((unsigned char) 1, m);           // a[15..8]
+  set_ai_data ((unsigned char) 0, l);           // a[7..0]
+  out_byte_eeprom (data);                       // write data to EEPROM
+}
+
+
+static void
+out_adr2_data (unsigned int adr, unsigned char data) // address shift 1 bit
+{
+  set_adr_long (adr << 1, 1);                   // adr x 2
+  out_byte_eeprom (data);                       // write data to EEPROM
+}
+
+
+static void
+out_adr_data (unsigned int adr, unsigned char data) // real address
+{
+  set_adr_long (adr, 0);
+  out_byte_eeprom (data);                       // write data to EEPROM
+}
+
+
+static void
+out_adr_data_32k (unsigned int adr, unsigned char data)
+{
+  set_adr (adr);
+  out_byte_eeprom (data);                       // write data to EEPROM
+}
+
+
+static unsigned char
+read_byte (void)
+{
+  set_ai (3);                                   // default write mode
+  set_data_read                                 // ninit=0, nwrite=1
+  return read_data ();
+}
+
+
+static void
+enable_protection (void)
+{
+//  set_bank (0x2000,0);                          // set bank 0
+  out_data (0, 0x55, 0x55, 0xaa);               // adr2,adr1,adr0,data 05555:aa
+  out_data (0, 0x2a, 0xaa, 0x55);
+  out_data (0, 0x55, 0x55, 0xa0);
+}
+
+
+/*
+static void
+disable_protection (void)
+{
+  out_data (0, 0x55, 0x55, 0xaa);               // adr2,adr1,adr0,data 05555:aa
+  out_data (0, 0x2a, 0xaa, 0x55);
+  out_data (0, 0x55, 0x55, 0x80);
+  out_data (0, 0x55, 0x55, 0xaa);
+  out_data (0, 0x2a, 0xaa, 0x55);
+  out_data (0, 0x55, 0x55, 0x20);
+  delay_us (20000);
+}
+*/
+
+
+#if 0
+static int
+data_polling_data (unsigned char last_data)
+{
+  unsigned int timeout = 0;
+
+  while (timeout++ < 0x07ffffff)
+    if (((read_byte () ^ last_data) & 0x80) == 0)
+      return 0;
+  return 1;                                     // ready to exit the while loop
+}
+#endif
+
+
+static int
+data_polling (void)
+{
+  unsigned char predata, currdata;
+  unsigned int timeout = 0;
+
+  predata = read_byte () & 0x40;
+  while (timeout++ < 0x07ffffff)
+    {
+      currdata = read_byte () & 0x40;
+      if (predata == currdata)
+        return 0;
+      predata = currdata;
+    }
+  return 1;
+}
+
+
+static void
+reset_to_read (void)                            // return to read mode
+{
+  out_adr2_data (0x5555, 0xaa);                 // 5555:aa adr2,adr1,adr0,data
+  out_adr2_data (0x2aaa, 0x55);                 // 2aaa:55
+  out_adr2_data (0x5555, 0xf0);                 // 5555:f0
+}
+
+
+/*
+static void
+read_status_reg_cmd (void)
+{
+  out_adr2_data (0x5555, 0xaa);                 // 5555:aa adr2,adr1,adr0,data
+  out_adr2_data (0x2aaa, 0x55);                 // 2aaa:55
+  out_adr2_data (0x5555, 0x70);                 // 5555:70
+}
+*/
+
+
+static int
+wait_status (void)
+{
+  unsigned temp = read_byte ();                 // read first status byte
+
+  while ((temp & 0xfc) != 0x80)
+    {
+      if ((temp & 0x20) == 0x20 && port_mode == UCON64_SPP)
+        {
+          fputs ("\nERROR: Erase failed\n", stderr);
+          return -1;
+        }
+      if ((temp & 0x10) == 0x10)
+        {
+          fputs ("\nERROR: Programming failed\n", stderr);
+          return -2;
+        }
+      temp = read_data ();
+    }
+//  reset_to_read ();
+  return 0;
+}
+
+
+static int
+mx_erase (void)
+{
+  int retval;
+
+  out_adr2_data (0x5555, 0xaa);                 // 5555:aa adr2,adr1,adr0,data
+  out_adr2_data (0x2aaa, 0x55);                 // 2aaa:55
+  out_adr2_data (0x5555, 0x80);                 // 5555:80
+  out_adr2_data (0x5555, 0xaa);                 // 5555:aa
+  out_adr2_data (0x2aaa, 0x55);                 // 2aaa:55
+  out_adr2_data (0x5555, 0x10);                 // 5555:10
+
+  delay_us (100);
+//  read_status_reg_cmd ();                       // send read status reg. cmd
+  retval = wait_status () ? -1 : 0;
+  reset_to_read ();
+  return retval;
+}
+
+
+/*
+static int
+win_erase (void)
+{
+  out_data (0, 0x55, 0x55, 0xaa);               // adr2,adr1,adr0,data 05555:aa
+  out_data (0, 0x2a, 0xaa, 0x55);
+  out_data (0, 0x55, 0x55, 0x80);
+  out_data (0, 0x55, 0x55, 0xaa);
+  out_data (0, 0x2a, 0xaa, 0x55);
+  out_data (0, 0x55, 0x55, 0x10);
+
+  delay_us (20000);
+  if (data_polling ())
+    {
+      fputs ("ERROR: Erase failed\n", stderr);
+      return -1;
+    }
+  else
+    return 0;
+}
+*/
+
+
+static unsigned char
+intel_read_status (void)
+{
+  out_adr_data (0, 0x70);                       // read status command
+  return read_byte ();
+}
+
+
+static int
+intel_check_status (void)
+{
+  unsigned int time_out = 0x8000;
+
+  while (!(intel_read_status () & 0x80))
+    {
+      time_out--;
+      if (time_out == 0)
+        {
+          fprintf (stderr, "\nERROR: Intel read status time out\n"
+                             "       Status = 0x%02x\n", intel_read_status ());
+          out_adr_data (0, 0x50);               // clear status register
+          return -1;
+        }
+    }
+  return 0;
+}
+
+
+static int
+intel_block_erase (unsigned int block)
+{
+  unsigned int time_out = 0x8000;
+
+  while ((intel_read_status ()) != 0x80)
+    {
+      time_out--;
+      if (time_out == 0)
+        {
+          fprintf (stderr, "\nERROR: Intel block erase time out\n"
+                             "       Status = 0x%02x\n", intel_read_status ());
+          return -1;
+        }
+    }
+  out_adr_data (block, 0x20);                   // block erase
+  out_adr_data (block, 0xd0);                   // write confirm
+  time_out = 0x8000;
+  while (!(intel_read_status () & 0x80))
+    {
+      time_out--;
+      if (time_out == 0)
+        {
+          fprintf (stderr, "\nERROR: Intel block erase time out at 0x%x\n"
+                             "       Status = 0x%02x\n", block, intel_read_status ());
+          out_adr_data (block, 0x50);           // clear status register
+          fprintf (stderr, "       Status = 0x%02x\n", intel_read_status ());
+          return -1;
+        }
+    }
+
+  if ((intel_read_status ()) == 0x80)
+    return 0;
+  else
+    {
+      fprintf (stderr, "\nERROR: Intel block erase error at 0x%x\n"
+                         "       Status = 0x%02x\n", block, intel_read_status ());
+      out_adr_data (block, 0x50);               // clear status register
+      fprintf (stderr, "       Status = 0x%02x\n", intel_read_status ());
+      out_adr_data (0x0000, 0xff);              // read array
+      return -1;
+    }
+}
+
+
+/*
+static int
+intel_erase (void)
+{
+  unsigned int block;
+  for (block = 0; block < 64; block++)
+    if (intel_block_erase (block * 0x20000))
+      return -1;
+  return 0;
+}
+
+
+static int
+erase (void)
+{
+  if (eeprom_type == WINBOND)
+    return win_erase ();
+  else if (eeprom_type == MX)
+    return mx_erase ();
+  else if (eeprom_type == INTEL)
+    return intel_erase ();
+
+  fputs ("ERROR: Unknown EEPROM type\n", stderr);
+  return -1;
+}
+*/
+
+
+static int
+check_eeprom (void)
+{
+  int i;
+
+  // check 4M flash
+  out_adr_data_32k (0x5555, 0xaa);              // software product ID entry
+  out_adr_data_32k (0x2aaa, 0x55);
+  out_adr_data_32k (0x5555, 0x80);
+  out_adr_data_32k (0x5555, 0xaa);
+  out_adr_data_32k (0x2aaa, 0x55);
+  out_adr_data_32k (0x5555, 0x60);
+
+  delay_us (10);
+  set_adr (0);                                  // adr2,adr1,adr0
+  if (read_byte () == 0xda)                     // manufacturer code
+    {
+      set_adr (1);                              // adr2,adr1,adr0
+      if (read_byte () == 0x46)                 // device code
+        {
+          out_adr_data_32k (0x5555, 0xaa);      // software product ID exit
+          out_adr_data_32k (0x2aaa, 0x55);      // adr2,adr1,adr0,data
+          out_adr_data_32k (0x5555, 0xf0);      // adr2,adr1,adr0,data
+          eeprom_type = WINBOND;                // Winbond 4 Mbit flash
+          return 0;
+        }
+    }
+
+  // check 16M flash
+  out_adr2_data (0x5555, 0xaa);                 // 5555:aa software product ID entry
+  out_adr2_data (0x2aaa, 0x55);                 // 2aaa:55 adr2,adr1,adr0,data
+  out_adr2_data (0x5555, 0x90);                 // 5555:90 adr2,adr1,adr0,data
+
+  set_adr (0);                                  // adr2,adr1,adr0
+  if (read_byte () == 0xc2)                     // manufacturer code
+    {
+      set_adr (2);                              // adr2,adr1,adr0
+      if (read_byte () == 0xf1)                 // device code
+        {
+          reset_to_read ();                     // reset to read mode
+          eeprom_type = MX;                     // MX 16 Mbit flash
+          return 0;
+        }
+    }
+
+  // check 64M flash
+  reset_to_read ();
+  init_port ();
+  out_adr_data (0x0000, 0x98);                  // read query
+  for (i = 0; i < 128; i += 2)
+    {
+      set_adr (i);                              // adr2,adr1,adr0
+      buffer[i / 2] = read_byte ();
+    }
+//  dumper (stdout, buffer, 64, 0, DUMPER_HEX);
+
+  if (buffer[0] == 0x89 && buffer[1] == 0x15
+      && buffer[0x10] == 'Q' && buffer[0x11] == 'R' && buffer[0x12] == 'Y')
+    {
+      out_adr_data (0x0000, 0xff);              // read array
+      eeprom_type = INTEL;                      // Intel 64 Mbit flash
+      return 0;
+    }
+
+//  eeprom_type = UNKNOWN_EEPROM;
+  return 1;
+}
+
+
+static void
+check_mbc (void)
+// determine memory bank controller type
+{
+  if (eeprom_type != UNKNOWN_EEPROM)
+    mbc_type = BUNG;
+  else
+    {
+      unsigned char rom_type = buffer[0x47];
+
+      if (memcmp (buffer + 4, rocket_logodata, GB_LOGODATA_LEN) == 0)
+        mbc_type = ROCKET;                      // rom_type == 0x97 || rom_type == 0x99
+      else if (rom_type == 0)
+        mbc_type = ROM;
+      else if ((rom_type >= 1 && rom_type <= 3) || rom_type == 0xff)
+        mbc_type = MBC1;
+      else if (rom_type == 5 || rom_type == 6)
+        mbc_type = MBC2;
+      else if (rom_type >= 0x0f && rom_type <= 0x13)
+        mbc_type = MBC3;
+      else if (rom_type >= 0x19 && rom_type <= 0x1e)
+        mbc_type = MBC5;
+      else if (rom_type == 0x1f)
+        mbc_type = CAMERA;
+      else
+        mbc_type = UNKNOWN_MBC;
+    }
+}
+
+
+static int
+check_card (void)
+/*
+  16 kB ROM = 1 bank
+  8 kB RAM = 1 bank
+*/
+{
+  unsigned char sum = 0;
+  char game_name[16];
+  int i;
+
+  puts ("Checking ROM data...");
+
+  if (mbc_type == ROCKET)
+    puts ("NOTE: Rocket Games cartridge detected");
+  else if (memcmp (buffer + 4, gb_logodata, GB_LOGODATA_LEN) != 0)
+    {
+      puts ("WARNING: Cartridge does not contain official Nintendo logo data");
+      dumper (stdout, buffer, 0x50, 0x100, DUMPER_HEX);
+    }
+
+  memcpy (game_name, buffer + 0x34, 15);
+  game_name[15] = 0;
+  for (i = 0; i < 15; i++)
+    if (!isprint ((int) game_name[i]) && game_name[i] != 0)
+      game_name[i] = '.';
+  printf ("Game name: \"%s\"\n", game_name);
+
+  if (buffer[0x48] > 8)                         // ROM size
+    printf ("NOTE: Strange ROM size byte value in header (0x%02x)\n", buffer[0x48]);
+
+  if (buffer[0x49] > 5)                         // SRAM size
+    printf ("NOTE: Strange RAM size byte value in header (0x%02x)\n", buffer[0x49]);
+
+/*
+  // [47] = ROM type
+  if (buffer[0x47] > 0 && buffer[0x47] < 4 && buffer[0x48] > 4)
+    mbc1_exp = 1;                               // MBC1 8 Mbit/16 Mbit
+*/
+
+  for (i = 0x34; i < 0x4d; i++)
+    sum += ~buffer[i];
+  if (buffer[0x4d] != sum)
+    printf ("NOTE: Incorrect header checksum (0x%02x), should be 0x%02x\n",
+            buffer[0x4d], sum);
+
+  return (1 << (buffer[0x48] > 8 ? 9 : buffer[0x48])) * 32 * 1024;
+}
+
+
+static void
+set_sram_bank (unsigned char bank)
+// original code only did "set_adr (0x4000); out_byte (bank);"
+{
+  if (eeprom_type != UNKNOWN_EEPROM)
+    {                                               // flash card
+      set_adr (0x4000);                             // set SRAM adr
+      out_byte (bank);                              // SRAM bank 0
+      // this should be equivalent to the code above: set_bank (0x4000, bank)
+    }
+  else
+    {                                               // game cartridge
+      switch (mbc_type)
+        {
+        case MBC1:
+          set_bank (0x6000, 1);
+          set_bank (0x4000, (unsigned char) (bank & 3));
+          break;
+        case MBC3:
+        case MBC5:
+          set_bank (0x4000, bank);
+          break;
+        default:
+          break;
+        }
+    }
+}
+
+
+static void
+set_bank2 (unsigned int bank)
+{
+  switch (mbc_type)
+    {
+    case BUNG:
+    case UNKNOWN_MBC:
+    case MBC5:
+      set_bank (0x2000, (unsigned char) bank);
+      if (eeprom_type != WINBOND)
+        set_bank (0x3000, (unsigned char) (bank >> 8));
+      break;
+    case MBC1:
+      set_bank (0x6000, 0);
+      set_bank (0x2000, (unsigned char) (bank & 0x1f));
+      set_bank (0x4000, (unsigned char) ((bank >> 5) & 3)); // (bank & 0x60) >> 5
+      break;
+    case MBC2:
+      set_bank (0x2100, (unsigned char) (bank & 0x0f));
+      break;
+    case MBC3:
+      set_bank (0x2000, (unsigned char) bank);
+      break;
+    case CAMERA:
+      set_bank (0x4000, (unsigned char) bank);
+      break;
+    case ROCKET:
+      set_bank (0x3f00, (unsigned char) bank);
+      break;
+    default:
+      break;
+    }
+}
+
+
+static void
+read_rom_16k (unsigned int bank)                // ROM or EEPROM
+{
+  int idx = 0, i, j;
+  char game_name[16];
+
+  set_bank2 (bank);
+  for (j = 0; j < 64; j++)
+    {                                           // 16k bytes = 64 x 256 bytes
+      if (bank)
+        set_ai_data ((unsigned char) 1, (unsigned char) (j | 0x40)); // set adr[15..8]
+      else
+        set_ai_data ((unsigned char) 1, (unsigned char) j); // a[15..0]
+      set_ai_data ((unsigned char) 0, 0);       // a[7..0]
+      set_ai_data ((unsigned char) 2, 0x81);    // enable read inc.
+      set_ai (3);                               // read/write data
+      set_data_read
+      for (i = 0; i < 256; i++)                 // page = 256
+        buffer[idx + i] = read_data ();
+      idx += 256;
+
+      /*
+        One can select the game in 2-in-1 cartridges by writing the game number
+        to 0x3fc0. This has been verified for 2 cartridges with ROM type byte
+        value 0x99. Maybe there exist other Rocket Games n-in-1 games with a
+        different ROM type byte value. That's why we don't check for that
+        specific ROM type byte value.
+      */
+      if (mbc_type == ROCKET && j == 1)
+        if (memcmp (buffer + 0x104, rocket_logodata, GB_LOGODATA_LEN) == 0)
+          {
+            set_adr (0x3fc0);
+            out_byte ((unsigned char) rocket_game_no++);
+            if (bank)
+              {
+                // Reread the last two pages, because the data came from the
+                //  previously selected game (data is "mirrored"). This does not
+                //  apply to the first game.
+                int k;
+
+                idx = 0;
+                for (k = 0; k < 2; k++)
+                  {
+                    set_ai_data ((unsigned char) 1, (unsigned char) (k | 0x40));
+                    set_ai_data ((unsigned char) 0, 0);
+                    set_ai_data ((unsigned char) 2, 0x81);
+                    set_ai (3);
+                    set_data_read
+                    for (i = 0; i < 256; i++)
+                      buffer[idx + i] = read_data ();
+                    idx += 256;
+                  }
+
+                clear_line ();                  // remove last gauge
+                memcpy (game_name, buffer + 0x134, 15);
+                game_name[15] = 0;
+                for (i = 0; i < 15; i++)
+                  if (!isprint ((int) game_name[i]) && game_name[i] != 0)
+                    game_name[i] = '.';
+                printf ("Found another game: \"%s\"\n\n", game_name);
+              }
+          }
+    }
+}
+
+
+static int
+verify_rom_16k (unsigned int bank)              // ROM or EEPROM
+{
+  int idx = 0, i, j;
+
+  set_bank2 (bank);
+  for (j = 0; j < 64; j++)
+    {                                           // 16k bytes = 64 x 256 bytes
+      if (bank)
+        set_ai_data ((unsigned char) 1, (unsigned char) (j | 0x40)); // set adr[15..8]
+      else
+        set_ai_data ((unsigned char) 1, (unsigned char) j);
+      set_ai_data ((unsigned char) 0, 0);       // a[7..0]
+      set_ai_data ((unsigned char) 2, 0x81);    // enable read inc.
+      set_ai (3);                               // read/write data
+      set_data_read
+      for (i = 0; i < 256; i++)
+        if (read_data () != buffer[idx + i])
+          {
+            printf ("\nWARNING: Verify error at 0x%x\n",
+                    (bank * 16384) + (j * 256) + i);
+            return -1;
+          }
+      idx += 256;
+    }
+
+  return 0;
+}
+
+
+static int
+win_write_eeprom_16k (unsigned int bank)
+{
+  int wr_done, err_cnt, idx = 0, i, j;
+
+//  disable_protection ();
+
+  for (j = 0; j < 64; j++)
+    {                                           // 16k bytes = 64 x 256 bytes
+      err_cnt = 16;                             // retry write counter
+      wr_done = 1;
+      while (wr_done)
+        {
+          enable_protection ();
+          // write 256 bytes
+          set_bank (0x2000, (unsigned char) bank); // for MCB1 16k bank
+          if (bank)
+            set_ai_data ((unsigned char) 1, (unsigned char) (j | 0x40)); // set adr[15..8]
+          else
+            set_ai_data ((unsigned char) 1, (unsigned char) j);
+
+          set_ai_data ((unsigned char) 0, 0);   // a[7..0]
+//          set_ai_data ((unsigned char) 2, 0x82); // enable flash write
+          set_ai_data ((unsigned char) 2, 0x83); // enable flash write inc.
+          set_ai (3);                           // read/write data
+//          set_ai_data ((unsigned char) 2, 0x80);
+
+          for (i = 0; i < 256; i++)
+            write_data (buffer[idx + i]);       // write data to EEPROM
+          set_ai_data ((unsigned char) 2, 0x80); // disable wr/rd inc.
+          set_ai_data ((unsigned char) 0, 0xff); // point to xxff
+          if (data_polling ())
+            puts ("\nWARNING: Write error");    // was: "Write error check (d6)"
+
+          wr_done = 0;
+
+          // verify 256 bytes
+          set_ai_data ((unsigned char) 0, 0);   // a[7..0]
+          set_ai_data ((unsigned char) 2, 0x81); // enable read inc.
+          set_ai (3);                           // read/write data
+          set_data_read
+          for (i = 0; i < 256; i++)
+            if (read_data () != buffer[idx + i])
+              {
+                err_cnt--;
+                wr_done = 1;
+                break;
+              }
+          if (err_cnt == 0)
+            {
+              fputs ("\nERROR: Programming failed after retry\n", stderr);
+              return -1;
+            }
+        }
+      idx += 256;
+    }
+/*
+  enable_protection();
+  disable_protection();
+  delay_us (20000);
+*/
+  return verify_rom_16k (bank);
+}
+
+
+static void
+set_page_write (void)                           // start page write command
+{
+  out_adr2_data (0x5555, 0xaa);                 // 5555:aa adr2,adr1,adr0,data
+  out_adr2_data (0x2aaa, 0x55);                 // 2aaa:55
+  out_adr2_data (0x5555, 0xa0);                 // 5555:a0
+}
+
+
+static int
+page_write_128 (unsigned int bank, unsigned char hi_lo, int j, int idx)
+{
+  int retry = 3, verify_ok, i;
+
+  while (retry)
+    {
+      set_page_write ();                        // each page is 128 bytes
+      set_bank (0x2000, (unsigned char) bank);  // for MCB1 16k bank
+      if (bank)
+        set_ai_data ((unsigned char) 1, (unsigned char) (j | 0x40)); // set adr[15..8]
+      else
+        set_ai_data ((unsigned char) 1, (unsigned char) j);
+
+      set_ai_data ((unsigned char) 0, hi_lo);   // a[7..0]
+      set_ai_data ((unsigned char) 2, 0x83);    // enable flash write inc.
+      set_ai (3);                               // read/write data
+      for (i = 0; i < 128; i++)
+        write_data (buffer[idx + i]);           // write data to EEPROM
+      set_ai_data ((unsigned char) 2, 0x80);    // disable wr/rd inc.
+      delay_us (10);                            // delay is large enough? - dbjh
+      if (wait_status ())
+        return -1;                              // wait_status() prints error message
+
+      // verify data
+      reset_to_read ();                         // return to read mode
+      verify_ok = 1;                            // verify ok
+      set_bank (0x2000, (unsigned char) bank);  // for MCB1 16k bank
+      if (bank)
+        set_ai_data ((unsigned char) 1, (unsigned char) (j | 0x40)); // set adr[15..8]
+      else
+        set_ai_data ((unsigned char) 1, (unsigned char) j);
+
+      set_ai_data ((unsigned char) 0, hi_lo);   // a[7..0]
+      set_ai_data ((unsigned char) 2, 0x81);    // enable inc.
+      set_ai (3);                               // read/write data
+      set_data_read
+      for (i = 0; i < 128; i++)                 // page = 128
+        if (read_data () != buffer[idx + i])
+          {
+            verify_ok = 0;                      // verify error
+            break;
+          }
+      if (verify_ok)
+        break;
+      else
+        {
+          retry--;
+          if (retry == 0)
+            {
+              fputs ("\nERROR: Programming failed after retry\n", stderr);
+              return -1;
+            }
+        }
+    }
+  return 0;
+}
+
+
+static int
+mx_write_eeprom_16k (unsigned int bank)
+{
+  int idx = 0, j;
+
+  for (j = 0; j < 64; j++)
+    {                                           // 16k bytes = 64 x 256 bytes
+      if (page_write_128 (bank, 0, j, idx))     // write first 128 bytes
+        return -1;
+      idx += 128;
+      if (page_write_128 (bank, 0x80, j, idx))  // write second 128 bytes
+        return -1;
+      idx += 128;
+    }
+  reset_to_read ();                             // return to read mode
+  return verify_rom_16k (bank);
+}
+
+
+/*
+static void
+dump_intel_data (void)
+{
+  int i;
+
+  out_adr_data (0, 0xff);                       // read array command
+  for (i = 0; i < 64; i++)
+    {                                           // read 0x100-0x150 to buffer
+      set_adr (i);
+      buffer[i] = read_data ();
+    }
+  dumper (stdout, buffer, 64, 0, DUMPER_HEX);
+}
+
+
+static int
+intel_byte_write_32 (unsigned int block_adr, int idx)
+{
+  int time_out, i;
+
+  for (i = 0; i < 32; i++)
+    {
+      out_adr_data (block_adr + idx + i, 0x40); // Write byte command
+      out_adr_data (block_adr + idx + i, buffer[idx + i]); // Write data
+      time_out = 0x8000;
+
+      if (intel_check_status ())
+        {
+          fprintf (stderr, "\nERROR: Intel byte write command time out\n"
+                             "       Status = 0x%02x\n", intel_read_status ());
+//          dump_intel_data ();
+          return -1;
+        }
+    }
+  if (intel_read_status () == 0x80)
+    return 0;
+  else
+    return -1;                                  // error
+}
+*/
+
+
+static int
+intel_buffer_write_32 (unsigned int block_adr, int idx)
+{
+  int time_out = 0x80000, i;
+
+  out_adr_data (block_adr + idx, 0xe8);         // Write buffer command
+  set_ai_data ((unsigned char) 2, 0x82);        // wei enable
+  set_ai (3);                                   // default write mode
+//  write_data (0xe8);                            // Write buffer command
+  set_data_read                                 // ninit=0, nwrite=1
+  while (!(read_data () & 0x80))
+    {
+      time_out--;
+      if (time_out == 0)
+        {
+          fprintf (stderr, "\nERROR: Intel buffer write command time out\n"
+                             "       Status = 0x%02x\n", intel_read_status ());
+//          dump_intel_data ();
+          return -1;
+        }
+      set_data_write
+      write_data (0xe8);                        // out data
+      set_data_read
+//      out_byte_eeprom (0xe8);                   // write buffer command
+    }
+
+//  out_byte_eeprom (0x1f);                       // set write byte count command
+  write_data (0x1f);                            // out data
+
+  set_ai_data ((unsigned char) 2, 0x83);        // enable flash write inc.
+  set_ai (3);
+  for (i = 0; i < 32; i++)
+    write_data (buffer[idx + i]);               // write data to EEPROM
+  write_data (0xd0);                            // write confirm command
+
+  return 0;
+}
+
+
+static int
+intel_write_eeprom_16k (unsigned int bank)
+{
+  unsigned int block_adr = bank << 14;          // convert to real address
+  int idx, j;
+
+  if ((bank & 0x07) == 0)
+    if (intel_block_erase (block_adr))
+      return -1;
+
+//  set_adr_long (block_adr, 0);                  // set real address
+  for (j = 0; j < 512; j++)
+    {                                           // 16k bytes = 512 x 32 bytes
+      idx = j * 32;
+//      if (intel_byte_write_32 (block_adr, idx)) return -1;
+      if (intel_buffer_write_32 (block_adr, idx))
+        {
+          fprintf (stderr, "\nERROR: Write error\n"
+                             "       Status = 0x%02x\n", intel_read_status ());
+          return -1;
+        }
+    }
+
+  if (intel_check_status ())
+    {
+      fprintf (stderr, "\nERROR: Intel buffer write command error\n"
+                         "       Status = 0x%02x\n", intel_read_status ());
+//      dump_intel_data();
+      return -1;
+    }
+  if (intel_read_status () != 0x80)
+    return -1;                                  // error
+
+  out_adr_data (0, 0xff);                       // read array
+  set_data_read
+  return verify_rom_16k (bank);
+}
+
+
+static int
+write_eeprom_16k (unsigned int bank)
+{
+  if (eeprom_type == WINBOND)                   // Winbond 4 Mbits EEPROM
+    return win_write_eeprom_16k (bank);
+  if (eeprom_type == MX)                        // MX 16 Mbits EEPROM
+    return mx_write_eeprom_16k (bank);
+  if (eeprom_type == INTEL)                     // Intel 64 Mbits EEPROM
+    return intel_write_eeprom_16k (bank);
+  return -1;
+}
+
+
+static void
+enable_sram_bank (void)
+{
+  init_port ();
+  set_adr (0x0000);                             // write 0x0000:0x0a default read mode
+  out_byte (0x0a);                              // enable SRAM
+  out_byte (0xc0);                              // disable SRAM
+  set_adr (0xa000);
+  out_byte (0xa0);                              // ctr index
+  set_adr (0xa100);
+//  out_byte(0x00);                               // ram_off,ram_bank_disable,MBC1
+  out_byte (0xc0);                              // ram_on,ram_bank_enable,MBC1
+
+  set_adr (0x0000);                             // write 0x0000:0x0a
+  out_byte (0x0a);                              // enable SRAM
+}
+
+
+/*
+static void
+usage (char *progname)
+{
+  fprintf (stderr, "Usage: %s [-option] <Filename>\n", progname);
+  fprintf (stderr, "-l   : load ROM file to GB Card.\n");
+  fprintf (stderr, "-lsa : load 256k/1Mbits sram from PC to GB Card.\n");
+  fprintf (stderr,
+           "-lsn : load 64kbits sram file from PC to specific sram bank(-n) in GB card.\n");
+  fprintf (stderr, "-lsc : load 1Mbits sram file from PC to Pocket Camera.\n");
+  fprintf (stderr, "-b   : auto-detect size and backup entire GB Card to PC.\n");
+  fprintf (stderr, "-ba  : backup full 4Mbits/16Mbits GB Card to PC.\n");
+  fprintf (stderr, "-bsa : retrieve all sram data (256k/1Mbits) from GB Card to PC.\n");
+  fprintf (stderr,
+           "-bsn : retrieve specific bank(-n) sram data(64kbits) from GB Card to PC.\n");
+  fprintf (stderr, "-bsc : retrieve 1Mbits sram from Pocket Camera to PC.\n");
+  fprintf (stderr, "-v   : verify file in PC with GB Card.\n");
+  fprintf (stderr, "-e   : erase Flash rom.\n");
+  fprintf (stderr, "-c   : check ROM file header.\n");
+  exit (2);
+}
+*/
+
+
+static int
+check_port_mode (void)
+{
+  init_port ();
+  set_ai_data ((unsigned char) 1, 0x12);
+  set_ai_data ((unsigned char) 0, 0x34);
+  set_ai (1);
+  set_data_read                                 // ninit=0, nwrite=1
+  if (read_data () != 0x12)
+    return 1;
+  set_ai (0);
+  set_data_read                                 // ninit=0, nwrite=1
+  if (read_data () != 0x34)
+    return 1;
+  end_port ();
+  return 0;
+}
+
+
+static int
+check_port (void)
+{
+  if (ucon64.parport_mode == UCON64_EPP && port_8 != 0x3bc)
+    port_mode = UCON64_EPP;                     // if port == 0x3bc => no EPP available
+  else
+    port_mode = UCON64_SPP;
+
+  if (check_port_mode ())
+    {
+      port_mode = UCON64_SPP;
+      if (check_port_mode ())
+        return 1;
+      else
+        end_port ();
+    }
+
+  // If we get here, a GBX was detected
+  if (port_mode == UCON64_EPP)
+    puts ("GBX found. EPP found");
+  else
+    puts ("GBX found. EPP not found or not enabled - SPP used");
+
+  return 0;
+}
+
+
+#if 0 // not used
+static void
+win_id (void)
+{
+  out_data (0, 0x55, 0x55, 0xaa);               // software product ID entry
+  out_data (0, 0x2a, 0xaa, 0x55);               // adr2,adr1,adr0,data
+  out_data (0, 0x55, 0x55, 0x80);               // adr2,adr1,adr0,data
+  out_data (0, 0x55, 0x55, 0xaa);               // adr2,adr1,adr0,data
+  out_data (0, 0x2a, 0xaa, 0x55);               // adr2,adr1,adr0,data
+  out_data (0, 0x55, 0x55, 0x60);               // adr2,adr1,adr0,data
+
+  delay_us (10);
+  set_adr (0);                                  // adr2,adr1,adr0
+  printf ("Manufacturer code: 0x%02x\n", read_byte ());
+  set_adr (1);                                  // adr2,adr1,adr0
+  printf ("Device code: 0x%02x\n", read_byte ());
+/*
+  set_adr (2);                                  // adr2,adr1,adr0
+  printf ("First 16 k protection code: 0x%02x\n", read_byte ());
+  set_bank (0x2000, 0x1f);
+  set_adr (0x7ff2);                             // adr2,adr1,adr0=0x7fff2
+  printf("Last 16 k protection code: 0x%02x\n", read_byte ());
+*/
+
+  out_data (0, 0x55, 0x55, 0xaa);               // software product ID exit
+  out_data (0, 0x2a, 0xaa, 0x55);               // adr2,adr1,adr0,data
+  out_data (0, 0x55, 0x55, 0xf0);               // adr2,adr1,adr0,data
+}
+
+
+static void
+mx_id (void)
+{
+  out_adr2_data (0x5555, 0xaa);                 // software product ID entry
+  out_adr2_data (0x2aaa, 0x55);                 // adr2,adr1,adr0,data
+  out_adr2_data (0x5555, 0x90);                 // adr2,adr1,adr0,data
+
+  set_adr (0);                                  // adr2,adr1,adr0
+  printf ("Manufacturer code: 0x%02x\n", read_byte ());
+  set_adr (2);                                  // adr2,adr1,adr0
+  printf ("Device code: 0x%02x\n", read_byte ());
+  set_adr (4);                                  // adr2,adr1,adr0
+  printf ("First 16 k protection code: 0x%02x\n", read_byte ());
+  reset_to_read ();                             // reset to read mode
+}
+
+
+static void
+intel_id (void)
+{
+  int i;
+
+  out_adr_data (0, 0x98);                       // read query
+  for (i = 0; i < 128; i += 2)
+    {
+      set_adr (i);                              // adr2,adr1,adr0
+      buffer[i / 2] = read_byte ();
+    }
+//  dumper (stdout, buffer, 64, 0, DUMPER_HEX);
+
+  printf ("Manufacture code = 0x%02x\n", buffer[0]);
+  printf ("Device code = 0x%02x\n", buffer[1]);
+}
+
+
+static void
+disp_id (void)
+{
+  if (eeprom_type == WINBOND)
+    win_id ();
+  if (eeprom_type == MX)
+    mx_id ();
+  if (eeprom_type == INTEL)
+    intel_id ();
+}
+
+
+static void
+gen_pat (unsigned int offset)
+{
+  int i;
+
+  for (i = 0; i < 0x2000; i++)                  // 8 k words = 16 k bytes
+    ((unsigned short int *) buffer)[i] = i + offset;
+}
+
+
+static int
+test_sram_v (int n_banks)
+{
+  int idx, i, j, bank;
+
+  enable_sram_bank ();
+  for (bank = 0; bank < n_banks; bank++)
+    {
+      idx = 0;
+      set_sram_bank (bank);
+      gen_pat (bank);
+      for (j = 0; j < 0x20; j++)
+        {                                       // 32 x 256 = 8192 (8 kbytes)
+          set_ai_data ((unsigned char) 1, (unsigned char) (0xa0 + j)); // SRAM at 0xa000-0xbfff
+          set_ai_data ((unsigned char) 0, 0);   // a[7..0]=0
+          set_ai_data ((unsigned char) 2, 0x81); // enable inc
+          set_ai (3);                           // point to data r/w port
+          set_data_read
+          for (i = 0; i < 256; i++)
+            if (read_data () != buffer[i + idx])
+              {
+                fputs ("ERROR: SRAM verify error\n", stderr);
+                return -1;
+              }
+          set_ai_data ((unsigned char) 2, 0x80); // disable inc
+          idx += 256;
+        }
+    }
+  return 0;
+}
+
+
+static int
+test_sram_wv (int n_banks)
+{
+  int idx, i, j, bank;
+
+  enable_sram_bank ();
+  for (bank = 0; bank < n_banks; bank++)
+    {
+      idx = 0;
+      set_sram_bank (bank);
+      gen_pat (bank);
+//      dumper (stdout, buffer, 0x10, 0, DUMPER_HEX);
+      for (j = 0; j < 0x20; j++)
+        {                                       // 32 x 256 = 8192(8kbytes)
+          set_ai_data ((unsigned char) 1, (unsigned char) (0xa0 + j)); // SRAM at 0xa000-0xbfff
+          set_ai_data ((unsigned char) 0, 0);   // a[7..0]=0
+          set_ai_data ((unsigned char) 2, 0x81); // enable inc
+          set_ai (3);                           // point to data r/w port
+          set_data_write
+          for (i = 0; i < 256; i++)
+            write_data (buffer[i + idx]);
+          set_ai_data ((unsigned char) 2, 0x80); // disable inc
+          idx += 256;
+        }
+    }
+  return test_sram_v (n_banks);
+}
+
+
+int cart_type = 0;                              // should be set to value of ROM[0x147]
+
+static void
+set_rom_bank (unsigned char bank)
+{
+  // cart_type < 4 is MCB1, other is MCB2
+  if (cart_type < 4)
+    set_bank (0x2000, bank);                    // for MCB1
+  else
+    set_bank (0x2100, bank);                    // for MCB2
+}
+
+
+static void
+try_read (void)
+{
+  int i;
+
+  set_ai_data ((unsigned char) 0, 0);           // a[7..0]=0
+  set_ai_data ((unsigned char) 1, 0x40);
+  set_ai_data ((unsigned char) 2, 0x81);        // enable inc
+  set_ai (3);                                   // point to data r/w port
+  for (i = 0; i < 16; i++)
+    buffer[i] = read_data ();
+  dumper (stdout, buffer, 16, 0, DUMPER_HEX);
+}
+
+
+static void
+try_read0 (void)
+{
+  int j;
+
+  set_rom_bank (1);
+  for (j = 0; j < 4; j++)
+    {
+      set_sram_bank ((unsigned char) j);
+      try_read ();
+    }
+  set_bank (0x6000, (unsigned char) 1);
+  puts ("6000:1");
+  for (j = 0; j < 4; j++)
+    {
+      set_sram_bank ((unsigned char) j);
+      try_read ();
+    }
+  set_bank (0x6000, (unsigned char) 0);
+  puts ("6000:0");
+  for (j = 0; j < 4; j++)
+    {
+      set_sram_bank ((unsigned char) j);
+      try_read ();
+    }
+}
+
+
+static void
+test_intel (void)
+{
+  int i;
+
+  out_adr2_data (0x0000, 0x90);                 // software product ID entry
+  for (i = 0; i < 128; i += 2)
+    {
+      set_adr (i);                              // adr2,adr1,adr0
+      buffer[i / 2] = read_byte ();
+    }
+  dumper (stdout, buffer, 64, 0, DUMPER_HEX);
+
+  out_adr2_data (0x0000, 0x70);                 // read status register
+  printf ("Status register = 0x%02x\n", read_byte ());
+}
+
+
+static void gbx_init (unsigned int parport, int read_header);
+
+static int
+verify_card_from_file (const char *filename, unsigned int parport)
+{
+  unsigned int bank, n_banks, filesize;
+  FILE *file;
+  time_t starttime;
+
+  gbx_init (parport, 1);
+  filesize = fsizeof (filename);
+  if ((filesize < 0x8000) || (filesize & 0x7fff) || (filesize > 64 * MBIT))
+    {
+      fputs ("ERROR: File size error\n", stderr);
+      exit (1);
+    }
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+
+  n_banks = (filesize / 0x8000) * 2;            // how many 16k banks (rounded
+  starttime = time (NULL);                      //  down to 32k boundary)
+  for (bank = 0; bank < n_banks; bank++)
+    {
+      if (fread (buffer, 1, 0x4000, file) != 0x4000)
+        {
+          fprintf (stderr, ucon64_msg[READ_ERROR], filename);
+          fclose (file);
+          exit (1);
+        }
+/*
+      if (bank == 0)
+        {
+          // [0x147] = ROM type; [0x148] = ROM size
+          if (buffer[0x147] > 0 && buffer[0x147] < 4 && buffer[0x148] > 4)
+            mbc1_exp = 1;                       // MBC1 8 Mbit/16 Mbit
+        }
+*/
+      if (verify_eeprom_16k (bank))
+        {
+          printf ("Verify card error at bank 0x%x\n", bank);
+          fclose (file);
+          exit (1);
+        }
+      ucon64_gauge (starttime, (bank + 1) * 0x4000, filesize);
+    }
+  fclose (file);
+  puts ("\nVerify card OK");
+
+  return 0;
+}
+#endif
+
+
+static void
+gbx_init (unsigned int parport, int read_header)
+{
+  int i;
+
+  eeprom_type = UNKNOWN_EEPROM;
+  rocket_game_no = 0;
+
+  port_8 = parport;
+  port_9 = parport + 1;
+  port_a = parport + 2;
+  port_b = parport + 3;
+  port_c = parport + 4;
+
+  parport_print_info ();
+
+  if (check_port () != 0)
+    {
+      fputs ("ERROR: GBX not found or not turned on\n", stderr);
+      exit (1);
+    }
+  init_port ();
+  check_eeprom ();
+
+  if (read_header)
+    for (i = 0x100; i < 0x150; i++)
+      {                                         // read 0x100-0x150 to buffer
+        set_adr (i);
+        buffer[i - 0x100] = read_data ();
+      }
+  /*
+    buffer is undefined if read_header == 0. This is not a problem as
+    read_header is only 0 if a flash card should be programmed
+    (gbx_write_rom()). In that case check_mbc() won't use buffer.
+  */
+  check_mbc ();
+}
+
+
+int
+gbx_read_rom (const char *filename, unsigned int parport)
+{
+  unsigned int bank, n_banks, rom_size, n_bytes = 0, totalbytes;
+  time_t starttime;
+  FILE *file;
+
+  gbx_init (parport, 1);
+  rom_size = check_card ();
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      end_port ();
+      exit (1);
+    }
+
+  /*
+    0  256 kbit = 32 kB  = 2 banks
+    1  512 kbit = 64 kB  = 4 banks
+    2  1 Mbit   = 128 kB = 8 banks
+    3  2 Mbit   = 256 kB = 16 banks
+    4  4 Mbit   = 512 kB = 32 banks
+    5  8 Mbit   = 1 MB   = 64 banks
+    6  16 Mbit  = 2 MB   = 128 banks
+  */
+  n_banks = rom_size / (16 * 1024);
+  if (eeprom_type == WINBOND)
+    n_banks = 32;                               // backup 4 Mbit
+  if (eeprom_type == MX)
+    n_banks = 128;                              // backup 16 Mbit
+  if (eeprom_type == INTEL)
+    n_banks = 512;                              // backup 64 Mbit
+
+  totalbytes = n_banks * 16 * 1024;
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", totalbytes, (float) totalbytes / MBIT);
+
+  starttime = time (NULL);
+  for (bank = 0; bank < n_banks; bank++)
+    {
+      read_rom_16k (bank);
+      if (verify_rom_16k (bank))
+        printf ("Verify card error at bank 0x%x\n", bank);
+
+      fwrite (buffer, 1, 0x4000, file);
+      n_bytes += 16 * 1024;
+      ucon64_gauge (starttime, n_bytes, totalbytes);
+    }
+
+  fclose (file);
+  end_port ();
+
+  return 0;
+}
+
+
+int
+gbx_write_rom (const char *filename, unsigned int parport)
+{
+  int bank, n_banks, n_bytes = 0, filesize;
+  time_t starttime;
+  FILE *file;
+
+  gbx_init (parport, 0);
+  if (eeprom_type == UNKNOWN_EEPROM)
+    {
+      fputs ("ERROR: Unknown EEPROM type\n", stderr);
+      end_port ();
+      exit (1);
+    }
+
+  filesize = fsizeof (filename);
+  if ((filesize < 0x8000) || (filesize & 0x7fff) || (filesize > 64 * MBIT))
+    {
+      fputs ("ERROR: File size error\n", stderr);
+      exit (1);
+    }
+  n_banks = (filesize / 0x8000) * 2;            // how many 16k banks (rounded
+                                                //  down to 32k boundary)
+  if (eeprom_type == MX)
+    if (mx_erase ())                            // erase 16M flash
+      {
+        // wait_status() prints error message
+        end_port ();
+        exit (1);
+      }
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      end_port ();
+      exit (1);
+    }
+
+  printf ("Send: %d Bytes (%.4f Mb)\n\n",
+          n_banks * 0x4000, (float) (n_banks * 0x4000) / MBIT);
+
+  starttime = time (NULL);
+  for (bank = 0; bank < n_banks; bank++)
+    {
+      if (fread (buffer, 1, 0x4000, file) != 0x4000)
+        {
+          fprintf (stderr, ucon64_msg[READ_ERROR], filename);
+          fclose (file);
+          end_port ();
+          exit (1);
+        }
+      if (write_eeprom_16k (bank))
+        {
+          fprintf (stderr, "ERROR: Write card error at bank 0x%x\n", bank);
+          fclose (file);
+          end_port ();
+          exit (1);
+        }
+      n_bytes += 0x4000;
+      ucon64_gauge (starttime, n_bytes, filesize);
+    }
+
+#if 0 // write_eeprom_16k() already calls verify_rom_16k() (indirectly)...
+  clear_line ();                                // remove last gauge
+  puts ("Verifying card...\n");
+  fseek (file, 0, SEEK_SET);
+  n_bytes = 0;
+  starttime = time (NULL);
+  for (bank = 0; bank < n_banks; bank++)
+    {
+      if (fread (buffer, 1, 0x4000, file) != 0x4000)
+        {
+          fprintf (stderr, ucon64_msg[READ_ERROR], filename);
+          fclose (file);
+          end_port ();
+          exit (1);
+        }
+      if (verify_rom_16k (bank))
+        {
+          fprintf (stderr, "ERROR: Verify card error at bank 0x%x\n", bank);
+          fclose (file);
+          end_port ();
+          exit (1);
+        }
+      n_bytes += 0x4000;
+      ucon64_gauge (starttime, n_bytes, filesize);
+    }
+#endif
+
+  fclose (file);
+  end_port ();
+
+  return 0;
+}
+
+
+static int
+sram_size_banks (int pocket_camera, unsigned char sram_size_byte)
+{
+  int n_banks;
+
+  if (eeprom_type == UNKNOWN_EEPROM)
+    {
+      // it seems reasonable that eeprom_type == UNKNOWN_EEPROM when a pocket
+      //  camera (1 Mbit SRAM) is plugged in the GBX
+      if (pocket_camera)
+        n_banks = 16;
+      else // no flash card, must be game cartridge
+        {
+          int x = (sram_size_byte & 7) << 1;
+          if (x > 5)
+            x = 6;
+          if (x)
+            x = 1 << (x - 1);                   // SRAM size in kB
+          n_banks = (x + 8192 - 1) / 8192;      // round up to 1 bank if it's 2 kB
+        }
+    }
+  else
+    {
+      // if eeprom_type != UNKNOWN_EEPROM, it has to be WINBOND, MX or INTEL (no
+      //  need to set n_banks to a default value)
+      if (eeprom_type == WINBOND)
+        n_banks = 4;                            // 4 x 8 kB = 32 kB
+      else // if (eeprom_type == MX || eeprom_type == INTEL)
+        n_banks = 16;                           // 16 x 8 kB = 128 kB
+    }
+
+  return n_banks;
+}
+
+
+int
+gbx_read_sram (const char *filename, unsigned int parport, int start_bank)
+{
+  int bank, n_banks, n_bytes = 0, totalbytes, idx, i, j;
+  time_t starttime;
+  FILE *file;
+
+  gbx_init (parport, 1);
+
+  n_banks = sram_size_banks (buffer[0x47] == 0x1f, buffer[0x49]);
+  if (!n_banks)
+    {
+      fputs ("ERROR: No SRAM available\n", stderr);
+      end_port ();
+      exit (1);
+    }
+
+  if (start_bank == -1)
+    start_bank = 0;
+  else
+    {
+      if (start_bank >= n_banks)
+        {
+          fprintf (stderr, "ERROR: Bank must be a value 0 - %d (for this card)\n",
+                   n_banks - 1);
+          end_port ();
+          exit (1);
+        }
+      n_banks = 1;
+    }
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      end_port ();
+      exit (1);
+    }
+
+  memset (buffer, 0, 8192);
+  totalbytes = n_banks * 8192;
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", totalbytes, (float) totalbytes / MBIT);
+
+  enable_sram_bank ();
+  starttime = time (NULL);
+  for (bank = start_bank; bank < start_bank + n_banks; bank++)
+    {
+      idx = 0;
+      set_sram_bank ((unsigned char) bank);
+      for (j = 0; j < 32; j++)
+        {                                       // 32 x 256 = 8192 (8 kbytes)
+          set_ai_data ((unsigned char) 1, (unsigned char) (0xa0 + j)); // SRAM at 0xa000-0xbfff
+          set_ai_data ((unsigned char) 0, 0);   // a[7..0]=0
+          set_ai_data ((unsigned char) 2, 0x81); // enable inc
+          set_ai (3);                           // point to data r/w port
+          set_data_read
+          for (i = 0; i < 256; i++)
+            buffer[idx + i] = read_data ();
+          set_ai_data ((unsigned char) 2, 0x80); // disable inc
+          idx += 256;
+        }
+      fwrite (buffer, 1, 8192, file);
+      n_bytes += 8192;
+      ucon64_gauge (starttime, n_bytes, totalbytes);
+    }
+  fclose (file);
+  end_port ();
+
+  return 0;
+}
+
+
+int
+gbx_write_sram (const char *filename, unsigned int parport, int start_bank)
+{
+  int bank, n_banks, n_bytes = 0, totalbytes, idx, i, j;
+  time_t starttime;
+  FILE *file;
+
+  gbx_init (parport, 1);
+
+  n_banks = sram_size_banks (buffer[0x47] == 0x1f, buffer[0x49]);
+  if (!n_banks)
+    {
+      fputs ("ERROR: No SRAM to write to\n", stderr);
+      end_port ();
+      exit (1);
+    }
+
+  if (start_bank == -1)
+    {
+      start_bank = 0;
+      i = fsizeof (filename);
+      n_banks = MIN (n_banks, (i + 8192 - 1) / 8192); // "+ 8192 - 1" to round up
+    }
+  else
+    {
+      if (start_bank >= n_banks)
+        {
+          fprintf (stderr, "ERROR: Bank must be a value 0 - %d (for this card)\n",
+                   n_banks - 1);
+          end_port ();
+          exit (1);
+        }
+      n_banks = 1;
+    }
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      end_port ();
+      exit (1);
+    }
+
+  memset (buffer, 0, 8192);
+  totalbytes = n_banks * 8192;                  // yes, we _send_ totalbytes bytes
+  printf ("Send: %d Bytes (%.4f Mb)\n\n", totalbytes, (float) totalbytes / MBIT);
+
+  enable_sram_bank ();
+  starttime = time (NULL);
+  for (bank = start_bank; bank < start_bank + n_banks; bank++)
+    {
+      idx = 0;
+      if (!fread (buffer, 1, 8192, file))
+        { // handle/allow files that are not an exact multiple of 8 kB
+          fprintf (stderr, ucon64_msg[READ_ERROR], filename);
+          fclose (file);
+          end_port ();
+          exit (1);
+        }
+      set_sram_bank ((unsigned char) bank);
+      for (j = 0; j < 32; j++)
+        {                                       // 32 x 256 = 8192 (8 kbytes)
+          set_ai_data ((unsigned char) 1, (unsigned char) (0xa0 + j)); // SRAM at 0xa000-0xbfff
+          set_ai_data ((unsigned char) 0, 0);   // a[7..0]=0
+          set_ai_data ((unsigned char) 2, 0x81); // enable inc
+          set_ai (3);                           // point to data r/w port
+          set_data_write
+          for (i = 0; i < 256; i++)
+            write_data (buffer[idx + i]);
+          set_ai_data ((unsigned char) 2, 0x80); // disable inc
+          idx += 256;
+        }
+      n_bytes += 8192;
+      ucon64_gauge (starttime, n_bytes, totalbytes);
+    }
+  fclose (file);
+  end_port ();
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 34 - 0
ucon64/2.0/src/backup/gbx.h

@@ -0,0 +1,34 @@
+/*
+gbx.h - Game Boy Xchanger support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2001 - 2002 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef GBX_H
+#define GBX_H
+
+extern const st_getopt2_t gbx_usage[];
+
+#ifdef USE_PARALLEL
+extern int gbx_read_rom (const char *filename, unsigned int parport);
+extern int gbx_write_rom (const char *filename, unsigned int parport);
+extern int gbx_read_sram (const char *filename, unsigned int parport, int bank);
+extern int gbx_write_sram (const char *filename, unsigned int parport, int bank);
+#endif
+
+#endif // GBX_H

+ 1144 - 0
ucon64/2.0/src/backup/gd.c

@@ -0,0 +1,1144 @@
+/*
+gd.c - Game Doctor support for uCON64
+
+Copyright (c) 2002 - 2003 John Weidman
+Copyright (c) 2002 - 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include "misc/string.h"
+#include "misc/misc.h"
+#include "misc/file.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "gd.h"
+#include "console/snes.h"                       // for snes_make_gd_names() &
+#include "misc/parallel.h"                      //  snes_get_snes_hirom()
+
+
+const st_getopt2_t gd_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Game Doctor SF3(SF6/SF7)/Professor SF(SF II)"/*"19XX Bung Enterprises Ltd http://www.bung.com.hk"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xgd3", 0, 0, UCON64_XGD3, // supports split files
+      NULL, "send ROM to Game Doctor SF3/SF6/SF7; " OPTION_LONG_S "port=PORT\n"
+      "this option uses the Game Doctor SF3 protocol",
+      &ucon64_wf[WF_OBJ_SNES_DEFAULT_STOP_NO_ROM]
+    },
+    {
+      "xgd6", 0, 0, UCON64_XGD6,
+#if 1 // dumping is not yet supported
+      NULL, "send ROM to Game Doctor SF6/SF7; " OPTION_LONG_S "port=PORT\n" // supports split files
+#else
+      NULL, "send/receive ROM to/from Game Doctor SF6/SF7; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when ROM does not exist\n"
+#endif
+      "this option uses the Game Doctor SF6 protocol",
+      &ucon64_wf[WF_OBJ_SNES_DEFAULT_STOP_NO_ROM]
+    },
+    {
+      "xgd3s", 0, 0, UCON64_XGD3S,
+      NULL, "send SRAM to Game Doctor SF3/SF6/SF7; " OPTION_LONG_S "port=PORT",
+      &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM]
+    },
+    // --xgd3r should remain hidden until receiving works
+    {
+      "xgd3r", 0, 0, UCON64_XGD3R,
+      NULL, NULL,
+      &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM]
+    },
+    {
+      "xgd6s", 0, 0, UCON64_XGD6S,
+      NULL, "send/receive SRAM to/from Game Doctor SF6/SF7; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM]
+    },
+    {
+      "xgd6r", 0, 0, UCON64_XGD6R,
+      NULL, "send/receive saver (RTS) data to/from Game Doctor SF6/SF7;\n" OPTION_LONG_S "port=PORT\n"
+      "receives automatically when saver file does not exist",
+      &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM]
+    },
+#endif // USE_PARALLEL
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef  USE_PARALLEL
+
+#define BUFFERSIZE 8192
+#define GD_OK 0
+#define GD_ERROR 1
+#define GD3_PROLOG_STRING "DSF3"
+#define GD6_READ_PROLOG_STRING "GD6R"           // GD reading, PC writing
+#define GD6_WRITE_PROLOG_STRING "GD6W"          // GD writing, PC reading
+#define GD6_TIMEOUT_ATTEMPTS 0x4000
+#define GD6_RX_SYNC_TIMEOUT_ATTEMPTS 0x2000
+#define GD6_SYNC_RETRIES 16
+
+#ifdef  _MSC_VER
+// Visual C++ doesn't allow inline in C source code
+#define inline __inline
+#endif
+
+
+static void init_io (unsigned int port);
+static void deinit_io (void);
+static void io_error (void);
+static void gd_checkabort (int status);
+static void remove_destfile (void);
+static int gd3_send_prolog_byte (unsigned char data);
+static int gd3_send_prolog_bytes (unsigned char *data, int len);
+static void gd3_send_byte (unsigned char data);
+static int gd3_send_bytes (unsigned char *data, int len);
+static int gd6_sync_hardware (void);
+static int gd6_sync_receive_start (void);
+static inline int gd6_send_byte_helper (unsigned char data, unsigned int timeout);
+static int gd6_send_prolog_byte (unsigned char data);
+static int gd6_send_prolog_bytes (unsigned char *data, int len);
+static int gd6_send_bytes (unsigned char *data, int len);
+static int gd6_receive_bytes (unsigned char *buffer, int len);
+static int gd_send_unit_prolog (int header, unsigned size);
+static int gd_write_rom (const char *filename, unsigned int parport,
+                         st_rominfo_t *rominfo, const char *prolog_str);
+static int gd_write_sram (const char *filename, unsigned int parport,
+                          const char *prolog_str);
+static int gd_write_saver (const char *filename, unsigned int parport,
+                           const char *prolog_str);
+
+
+typedef struct st_gd3_memory_unit
+{
+  char name[12];                                // Exact size is 11 chars but I'll
+//  unsigned char *data;                        //  add one extra for string terminator
+  unsigned int size;                            // Usually either 0x100000 or 0x80000
+} st_gd3_memory_unit_t;
+
+static int (*gd_send_prolog_byte) (unsigned char data);
+static int (*gd_send_prolog_bytes) (unsigned char *data, int len);
+static int (*gd_send_bytes) (unsigned char *data, int len);
+static st_gd3_memory_unit_t gd3_dram_unit[GD3_MAX_UNITS];
+static int gd_port, gd_bytessend, gd_fsize, gd_name_i = 0;
+static time_t gd_starttime;
+static char **gd_names;
+static unsigned char gd6_send_toggle;
+static const char *gd_destfname = NULL;
+static FILE *gd_destfile;
+
+
+void
+init_io (unsigned int port)
+/*
+  - sets global `gd_port'. Then the send/receive functions don't need to pass
+    `port' to all the I/O functions.
+  - calls init_conio(). Necessary for kbhit() and DOS-like behaviour of getch().
+*/
+{
+  gd_port = port;
+#if 0 // we want to support non-standard parallel port addresses
+  if (gd_port != 0x3bc && gd_port != 0x378 && gd_port != 0x278)
+    {
+      fputs ("ERROR: PORT must be 0x3bc, 0x378 or 0x278\n", stderr);
+      exit (1);
+    }
+#endif
+
+#if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
+  init_conio ();
+#endif
+
+  parport_print_info ();
+}
+
+
+void
+deinit_io (void)
+{
+// Put possible transfer cleanup stuff here
+#if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
+  deinit_conio ();
+#endif
+}
+
+
+void
+io_error (void)
+// This function could be changed to take a string argument that describes the
+//  error. Or take an integer code that we can interpret here.
+{
+  fflush (stdout);
+  fputs ("ERROR: Communication with Game Doctor failed\n", stderr);
+  fflush (stderr);
+  // calling fflush() seems to be necessary under Msys in order to make the
+  //  error message be displayed before the "Removing: <filename>" message
+  exit (1);
+}
+
+
+void
+gd_checkabort (int status)
+{
+  if (((!ucon64.frontend) ? kbhit () : 0) && getch () == 'q')
+    {
+      puts ("\nProgram aborted");
+      exit (status);
+    }
+}
+
+
+static void
+remove_destfile (void)
+{
+  if (gd_destfname)
+    {
+      printf ("Removing: %s\n", gd_destfname);
+      fclose (gd_destfile);
+      remove (gd_destfname);
+      gd_destfname = NULL;
+    }
+}
+
+
+int
+gd3_send_prolog_byte (unsigned char data)
+/*
+  Prolog specific data output routine
+  We could probably get away with using the general routine but the
+  transfer program I (JW) traced to analyze the protocol did this for
+  the bytes used to set up the transfer so here it is.
+*/
+{
+  // Wait until SF3 is not busy
+  do
+    {
+      if ((inportb ((unsigned short) (gd_port + PARPORT_STATUS)) & 0x08) == 0)
+        return GD_ERROR;
+    }
+  while ((inportb ((unsigned short) (gd_port + PARPORT_STATUS)) & 0x80) == 0);
+
+  outportb ((unsigned short) gd_port, data);    // set data
+  outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 5); // Clock data out to SF3
+  outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 4);
+
+  return GD_OK;
+}
+
+
+int
+gd3_send_prolog_bytes (unsigned char *data, int len)
+{
+  int i;
+
+  for (i = 0; i < len; i++)
+    if (gd3_send_prolog_byte (data[i]) == GD_ERROR)
+      return GD_ERROR;
+  return GD_OK;
+}
+
+
+int
+gd_send_unit_prolog (int header, unsigned size)
+{
+  if (gd_send_prolog_byte (0x00) == GD_ERROR)
+    return GD_ERROR;
+  if (gd_send_prolog_byte ((unsigned char) ((header != 0) ? 0x02 : 0x00)) == GD_ERROR)
+    return GD_ERROR;
+  if (gd_send_prolog_byte ((unsigned char) (size >> 16)) == GD_ERROR) // 0x10 = 8 Mbit
+    return GD_ERROR;
+  if (gd_send_prolog_byte (0x00) == GD_ERROR)
+    return GD_ERROR;
+  return GD_OK;
+}
+
+
+void
+gd3_send_byte (unsigned char data)
+/*
+  General data output routine
+  Use this routine for sending ROM data bytes to the Game Doctor SF3 (SF6/SF7
+  too).
+*/
+{
+  // Wait until SF3 is not busy
+  while ((inportb ((unsigned short) (gd_port + PARPORT_STATUS)) & 0x80) == 0)
+    ;
+
+  outportb ((unsigned short) gd_port, data);    // set data
+  outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 5); // Clock data out to SF3
+  outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 4);
+}
+
+
+int
+gd3_send_bytes (unsigned char *data, int len)
+{
+  int i;
+
+  for (i = 0; i < len; i++)
+    {
+      gd3_send_byte (data[i]);
+      gd_bytessend++;
+      if ((gd_bytessend - GD_HEADER_LEN) % 8192 == 0)
+        {
+          ucon64_gauge (gd_starttime, gd_bytessend, gd_fsize);
+          gd_checkabort (2);                    // 2 to return something other than 1
+        }
+    }
+  return GD_OK;
+}
+
+
+int
+gd6_sync_hardware (void)
+// Sets the SF7 up for an SF6/SF7 protocol transfer
+{
+  int timeout, retries;
+  volatile int delay;
+
+  for (retries = GD6_SYNC_RETRIES; retries > 0; retries--)
+    {
+      timeout = GD6_TIMEOUT_ATTEMPTS;
+
+      outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 4);
+      outportb ((unsigned short) gd_port, 0);
+      outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 4);
+
+      for (delay = 0x1000; delay > 0; delay--)  // A delay may not be necessary here
+        ;
+
+      outportb ((unsigned short) gd_port, 0xaa);
+      outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 0);
+
+      while ((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x08) == 0)
+        if (--timeout == 0)
+          break;
+      if (timeout == 0)
+        continue;
+
+      outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 4);
+
+      while ((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x08) != 0)
+        if (--timeout == 0)
+          break;
+      if (timeout == 0)
+        continue;
+
+      outportb ((unsigned short) gd_port, 0x55);
+      outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 0);
+
+      while ((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x08) == 0)
+        if (--timeout == 0)
+          break;
+      if (timeout == 0)
+        continue;
+
+      outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 4);
+
+      while ((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x08) != 0)
+        if (--timeout == 0)
+          break;
+      if (timeout == 0)
+        continue;
+
+      return GD_OK;
+    }
+  return GD_ERROR;
+}
+
+
+int
+gd6_sync_receive_start (void)
+// Sync with the start of the received data
+{
+  int timeout = GD6_RX_SYNC_TIMEOUT_ATTEMPTS;
+
+  while (1)
+    {
+      if (((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x03) == 0x03) ||
+          ((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x03) == 0))
+        break;
+
+      if (--timeout == 0)
+        return GD_ERROR;
+    }
+
+  outportb ((unsigned short) gd_port, 0);
+
+  timeout = GD6_RX_SYNC_TIMEOUT_ATTEMPTS;
+  while ((inportb ((unsigned short) (gd_port + PARPORT_STATUS)) & 0x80) != 0)
+    if (--timeout == 0)
+      return GD_ERROR;
+
+  return GD_OK;
+}
+
+
+inline int
+gd6_send_byte_helper (unsigned char data, unsigned int timeout)
+{
+  while ((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x02) != gd6_send_toggle)
+    if (--timeout == 0)
+      return GD_ERROR;
+
+  gd6_send_toggle = ~gd6_send_toggle & 0x02;
+  outportb ((unsigned short) gd_port, data);
+  outportb ((unsigned short) (gd_port + PARPORT_CONTROL), (unsigned char) (4 | (gd6_send_toggle >> 1)));
+
+  return GD_OK;
+}
+
+
+int
+gd6_send_prolog_byte (unsigned char data)
+{
+  unsigned int timeout = 0x100000;
+
+  gd6_send_toggle = (inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x01) << 1;
+  return gd6_send_byte_helper (data, timeout);
+}
+
+
+int
+gd6_send_prolog_bytes (unsigned char *data, int len)
+{
+  int i;
+  unsigned int timeout = 0x1e00000;
+
+  gd6_send_toggle = (inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x01) << 1;
+  for (i = 0; i < len; i++)
+    if (gd6_send_byte_helper (data[i], timeout) != GD_OK)
+      return GD_ERROR;
+  return GD_OK;
+}
+
+
+int
+gd6_send_bytes (unsigned char *data, int len)
+{
+  int i;
+  unsigned int timeout = 0x1e0000;
+
+  gd6_send_toggle = (inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x01) << 1;
+  for (i = 0; i < len; i++)
+    {
+      if (gd6_send_byte_helper (data[i], timeout) != GD_OK)
+        return GD_ERROR;
+
+      gd_bytessend++;
+      if ((gd_bytessend - GD_HEADER_LEN) % 8192 == 0)
+        {
+          ucon64_gauge (gd_starttime, gd_bytessend, gd_fsize);
+          gd_checkabort (2);                    // 2 to return something other than 1
+        }
+    }
+  return GD_OK;
+}
+
+
+int
+gd6_receive_bytes (unsigned char *buffer, int len)
+{
+  int i;
+  unsigned char nibble1, nibble2;
+  unsigned int timeout = 0x1e0000;
+
+  outportb ((unsigned short) gd_port, 0x80);    // Signal the SF6/SF7 to send the next nibble
+  for (i = 0; i < len; i++)
+    {
+      while ((inportb ((unsigned short) (gd_port + PARPORT_STATUS)) & 0x80) == 0)
+        if (--timeout == 0)
+          return GD_ERROR;
+
+      nibble1 = (inportb ((unsigned short) (gd_port + PARPORT_STATUS)) >> 3) & 0x0f;
+      outportb ((unsigned short) gd_port, 0x00); // Signal the SF6/SF7 to send the next nibble
+
+      while ((inportb ((unsigned short) (gd_port + PARPORT_STATUS)) & 0x80) != 0)
+        if (--timeout == 0)
+          return GD_ERROR;
+
+      nibble2 = (inportb ((unsigned short) (gd_port + PARPORT_STATUS)) << 1) & 0xf0;
+      buffer[i] = nibble2 | nibble1;
+      outportb ((unsigned short) gd_port, 0x80);
+    }
+  return GD_OK;
+}
+
+
+int
+gd_add_filename (const char *filename)
+{
+  char buf[FILENAME_MAX], *p;
+
+  if (gd_name_i < GD3_MAX_UNITS)
+    {
+      strcpy (buf, filename);
+      p = strrchr (buf, '.');
+      if (p)
+        *p = 0;
+      strncpy (gd_names[gd_name_i], basename2 (buf), 11);
+      gd_names[gd_name_i][11] = 0;
+      gd_name_i++;
+    }
+  return 0;
+}
+
+
+int
+gd3_read_rom (const char *filename, unsigned int parport)
+{
+  (void) filename;                              // warning remover
+  (void) parport;                               // warning remover
+  return fputs ("ERROR: The function for dumping a cartridge is not yet implemented for the SF3\n", stderr);
+}
+
+
+int
+gd6_read_rom (const char *filename, unsigned int parport)
+{
+#if 0
+  FILE *file;
+  unsigned char *buffer;
+
+  init_io (parport);
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  free (buffer);
+  fclose (file);
+  deinit_io ();
+
+  return 0;
+#else
+  (void) filename;                              // warning remover
+  (void) parport;                               // warning remover
+  return fputs ("ERROR: The function for dumping a cartridge is not yet implemented for the SF6\n", stderr);
+#endif
+}
+
+
+int
+gd3_write_rom (const char *filename, unsigned int parport, st_rominfo_t *rominfo)
+{
+  gd_send_prolog_byte = gd3_send_prolog_byte;   // for gd_send_unit_prolog()
+  gd_send_prolog_bytes = gd3_send_prolog_bytes;
+  gd_send_bytes = gd3_send_bytes;
+
+  return gd_write_rom (filename, parport, rominfo, GD3_PROLOG_STRING);
+}
+
+
+int
+gd6_write_rom (const char *filename, unsigned int parport, st_rominfo_t *rominfo)
+{
+  gd_send_prolog_byte = gd6_send_prolog_byte;   // for gd_send_unit_prolog()
+  gd_send_prolog_bytes = gd6_send_prolog_bytes;
+  gd_send_bytes = gd6_send_bytes;
+
+  return gd_write_rom (filename, parport, rominfo, GD6_READ_PROLOG_STRING);
+}
+
+
+/*
+  Note: On most Game Doctor's the way you enter link mode to be able to upload
+        the ROM to the unit is to hold down the R key on the controller while
+        resetting the SNES. You will see the Game Doctor menu has a message that
+        says "LINKING..."
+*/
+int
+gd_write_rom (const char *filename, unsigned int parport, st_rominfo_t *rominfo,
+              const char *prolog_str)
+{
+  FILE *file = NULL;
+  unsigned char *buffer;
+  char *names[GD3_MAX_UNITS], names_mem[GD3_MAX_UNITS][12],
+       *filenames[GD3_MAX_UNITS], dir[FILENAME_MAX];
+  int num_units, i, send_header, x, split = 1, hirom = snes_get_snes_hirom();
+
+  init_io (parport);
+
+  // We don't want to malloc() ridiculously small chunks (of 12 bytes)
+  for (i = 0; i < GD3_MAX_UNITS; i++)
+    names[i] = names_mem[i];
+
+  gd_names = (char **) names;
+  ucon64_testsplit_callback = gd_add_filename;
+  num_units = ucon64.split = ucon64_testsplit (filename); // this will call gd_add_filename()
+  ucon64_testsplit_callback = NULL;
+  if (!ucon64.split)
+    {
+      split = 0;
+      num_units = snes_make_gd_names (filename, rominfo, (char **) names);
+    }
+
+  dirname2 (filename, dir);
+  gd_fsize = 0;
+  for (i = 0; i < num_units; i++)
+    {
+      // No suffix is necessary but the name entry must be upper case and MUST
+      //  be 11 characters long, padded at the end with spaces if necessary.
+      memset (gd3_dram_unit[i].name, ' ', 11);  // "pad" with spaces
+      gd3_dram_unit[i].name[11] = 0;            // terminate string so we can print it (debug)
+      // Use memcpy() instead of strcpy() so that the string terminator in
+      //  names[i] won't be copied.
+      memcpy (gd3_dram_unit[i].name, strupr (names[i]), strlen (names[i]));
+
+      x = strlen (dir) + strlen (names[i]) + 6; // file sep., suffix, ASCII-z => 6
+      if ((filenames[i] = (char *) malloc (x)) == NULL)
+        {
+          fprintf (stderr, ucon64_msg[BUFFER_ERROR], x);
+          exit (1);
+        }
+      sprintf (filenames[i], "%s" FILE_SEPARATOR_S "%s.078", dir, names[i]); // should match with what code of -s does
+
+      if (split)
+        {
+          x = fsizeof (filenames[i]);
+          gd_fsize += x;
+          gd3_dram_unit[i].size = x;
+          if (i == 0)                           // Correct for header of first file
+            gd3_dram_unit[i].size -= GD_HEADER_LEN;
+        }
+      else
+        {
+          if (!gd_fsize)                        // Don't call fsizeof() more
+            gd_fsize = fsizeof (filename);      //  often than necessary
+          if (hirom)
+            gd3_dram_unit[i].size = (gd_fsize - GD_HEADER_LEN) / num_units;
+          else
+            {
+              if ((i + 1) * 8 * MBIT <= gd_fsize - GD_HEADER_LEN)
+                gd3_dram_unit[i].size = 8 * MBIT;
+              else
+                gd3_dram_unit[i].size = gd_fsize - GD_HEADER_LEN - i * 8 * MBIT;
+            }
+        }
+    }
+
+  if ((buffer = (unsigned char *) malloc (8 * MBIT)) == NULL)
+    { // a DRAM unit can hold 8 Mbit at maximum
+      fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], 8 * MBIT);
+      exit (1);
+    }
+
+  printf ("Send: %d Bytes (%.4f Mb)\n", gd_fsize, (float) gd_fsize / MBIT);
+
+  // Put this just before the real transfer begins or else the ETA won't be
+  //  correct.
+  gd_starttime = time (NULL);
+
+  // Send the ROM to the hardware
+  if (memcmp (prolog_str, GD6_READ_PROLOG_STRING, 4) == 0)
+    if (gd6_sync_hardware () == GD_ERROR)
+      io_error ();
+  memcpy (buffer, prolog_str, 4);
+  buffer[4] = num_units;
+  if (gd_send_prolog_bytes (buffer, 5) == GD_ERROR)
+    io_error ();
+
+  puts ("Press q to abort\n");
+  for (i = 0; i < num_units; i++)
+    {
+#ifdef  DEBUG
+      printf ("\nfilename (%d): \"%s\", ", split, (split ? (char *) filenames[i] : filename));
+      printf ("name: \"%s\", size: %d\n", gd3_dram_unit[i].name, gd3_dram_unit[i].size);
+#endif
+      if (split)
+        {
+          if ((file = fopen (filenames[i], "rb")) == NULL)
+            {
+              fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filenames[i]);
+              exit (1);
+            }
+        }
+      else
+        if (file == NULL)                     // don't open the file more than once
+          if ((file = fopen (filename, "rb")) == NULL)
+            {
+              fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+              exit (1);
+            }
+
+      send_header = i == 0 ? 1 : 0;
+      if (gd_send_unit_prolog (send_header, gd3_dram_unit[i].size) == GD_ERROR)
+        io_error ();
+      if (gd_send_prolog_bytes ((unsigned char *) gd3_dram_unit[i].name, 11) == GD_ERROR)
+        io_error ();
+      if (send_header)
+        {
+          // Send the Game Doctor 512 byte header
+          fread (buffer, 1, GD_HEADER_LEN, file);
+          if (gd_send_prolog_bytes (buffer, GD_HEADER_LEN) == GD_ERROR)
+            io_error ();
+          gd_bytessend += GD_HEADER_LEN;
+        }
+      if (split == 0)                           // Not pre-split -- have to split it ourselves
+        {
+          if (hirom)
+            fseek (file, i * gd3_dram_unit[0].size + GD_HEADER_LEN, SEEK_SET);
+          else
+            fseek (file, i * 8 * MBIT + GD_HEADER_LEN, SEEK_SET);
+        }
+      fread (buffer, 1, gd3_dram_unit[i].size, file);
+      if (gd_send_bytes (buffer, gd3_dram_unit[i].size) == GD_ERROR)
+        io_error ();
+
+      if (split || i == num_units - 1)
+        fclose (file);
+    }
+
+  for (i = 0; i < num_units; i++)
+    free (filenames[i]);
+  free (buffer);
+  deinit_io ();
+
+  return 0;
+}
+
+
+int
+gd3_read_sram (const char *filename, unsigned int parport)
+{
+  (void) filename;                              // warning remover
+  (void) parport;                               // warning remover
+  return fputs ("ERROR: The function for dumping SRAM is not yet implemented for the SF3\n", stderr);
+}
+
+
+int
+gd6_read_sram (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer, gdfilename[12];
+  int len, bytesreceived = 0, transfer_size;
+  time_t starttime;
+
+  init_io (parport);
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  // Be nice to the user and automatically remove the file on an error (or abortion)
+  gd_destfname = filename;
+  gd_destfile = file;
+  register_func (remove_destfile);
+
+  if (gd6_sync_hardware () == GD_ERROR)
+    io_error ();
+  if (gd6_send_prolog_bytes ((unsigned char *) GD6_WRITE_PROLOG_STRING, 4) == GD_ERROR)
+    io_error ();
+
+  /*
+    The BRAM (SRAM) filename doesn't have to exactly match any game loaded in
+    the SF7. It needs to match any valid Game Doctor file name AND have an
+    extension of .B## (where # is a digit from 0-9)
+  */
+  strcpy ((char *) gdfilename, "SF16497 B00"); // TODO: We might need to make a GD file name from the real one
+  if (gd6_send_prolog_bytes (gdfilename, 11) == GD_ERROR)
+    io_error ();
+
+  if (gd6_sync_receive_start () == GD_ERROR)
+    io_error ();
+
+  if (gd6_receive_bytes (buffer, 16) == GD_ERROR)
+    io_error ();
+
+  transfer_size = buffer[1] | (buffer[2] << 8) | (buffer[3] << 16) | (buffer[4] << 24);
+  if (transfer_size != 0x8000)
+    {
+      fprintf (stderr, "ERROR: SRAM transfer size from Game Doctor != 0x8000 bytes\n");
+      exit (1);
+    }
+
+  printf ("Receive: %d Bytes\n", transfer_size);
+  puts ("Press q to abort\n");
+
+  starttime = time (NULL);
+  while (bytesreceived < transfer_size)
+    {
+      if (transfer_size - bytesreceived >= BUFFERSIZE)
+        len = BUFFERSIZE;
+      else
+        len = transfer_size - bytesreceived;
+
+      if (gd6_receive_bytes (buffer, len) == GD_ERROR)
+        io_error ();
+      fwrite (buffer, 1, len, file);
+
+      bytesreceived += len;
+      ucon64_gauge (starttime, bytesreceived, 32 * 1024);
+      gd_checkabort (2);
+    }
+
+  unregister_func (remove_destfile);
+  free (buffer);
+  fclose (file);
+  deinit_io ();
+
+  return 0;
+}
+
+
+int
+gd3_write_sram (const char *filename, unsigned int parport)
+{
+  gd_send_prolog_bytes = gd3_send_prolog_bytes;
+  gd_send_bytes = gd3_send_bytes;
+
+  return gd_write_sram (filename, parport, GD3_PROLOG_STRING);
+}
+
+
+int
+gd6_write_sram (const char *filename, unsigned int parport)
+{
+  gd_send_prolog_bytes = gd6_send_prolog_bytes;
+  gd_send_bytes = gd6_send_bytes;
+
+  return gd_write_sram (filename, parport, GD6_READ_PROLOG_STRING);
+}
+
+
+int
+gd_write_sram (const char *filename, unsigned int parport, const char *prolog_str)
+{
+  FILE *file;
+  unsigned char *buffer, gdfilename[12];
+  int bytesread, bytessend = 0, size, header_size;
+  time_t starttime;
+
+  init_io (parport);
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = fsizeof (filename);                    // GD SRAM is 4*8 KB, emu SRAM often not
+
+  if (size == 0x8000)
+    header_size = 0;
+  else if (size == 0x8200)
+    {
+      header_size = 0x200;
+      size = 0x8000;
+    }
+  else
+    {
+      fputs ("ERROR: GD SRAM file size must be 32768 or 33280 bytes\n", stderr);
+      exit (1);
+    }
+
+  printf ("Send: %d Bytes\n", size);
+  fseek (file, header_size, SEEK_SET);          // skip the header
+
+  if (memcmp (prolog_str, GD6_READ_PROLOG_STRING, 4) == 0)
+    if (gd6_sync_hardware () == GD_ERROR)
+      io_error ();
+  memcpy (buffer, prolog_str, 4);
+  buffer[4] = 1;
+  if (gd_send_prolog_bytes (buffer, 5) == GD_ERROR)
+    io_error ();
+
+  buffer[0] = 0x00;
+  buffer[1] = 0x80;
+  buffer[2] = 0x00;
+  buffer[3] = 0x00;
+  if (gd_send_prolog_bytes (buffer, 4) == GD_ERROR)
+    io_error ();
+
+  /*
+    The BRAM (SRAM) filename doesn't have to exactly match any game loaded in
+    the SF7. It needs to match any valid Game Doctor file name AND have an
+    extension of .B## (where # is a digit from 0-9)
+  */
+  strcpy ((char *) gdfilename, "SF8123  B00"); // TODO: We might need to make a GD file name from the real one
+  if (gd_send_prolog_bytes (gdfilename, 11) == GD_ERROR)
+    io_error ();
+
+  puts ("Press q to abort\n");                  // print here, NOT before first GD I/O,
+                                                //  because if we get here q works ;-)
+  starttime = time (NULL);
+  while ((bytesread = fread (buffer, 1, BUFFERSIZE, file)))
+    {
+      if (gd_send_bytes (buffer, bytesread) == GD_ERROR)
+        io_error ();
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      gd_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  deinit_io ();
+
+  return 0;
+}
+
+
+int
+gd3_read_saver (const char *filename, unsigned int parport)
+{
+  (void) filename;                              // warning remover
+  (void) parport;                               // warning remover
+  return fputs ("ERROR: The function for dumping saver data is not yet implemented for the SF3\n", stderr);
+}
+
+
+int
+gd6_read_saver (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer, gdfilename[12];
+  int len, bytesreceived = 0, transfer_size;
+  time_t starttime;
+
+  init_io (parport);
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  // Be nice to the user and automatically remove the file on an error (or abortion)
+  gd_destfname = filename;
+  gd_destfile = file;
+  register_func (remove_destfile);
+
+  if (gd6_sync_hardware () == GD_ERROR)
+    io_error ();
+  if (gd6_send_prolog_bytes ((unsigned char *) GD6_WRITE_PROLOG_STRING, 4) == GD_ERROR)
+    io_error ();
+
+  /*
+    TODO: Graceful handling of an abort because of a name error?
+          Currently we fail with a generic error.
+
+    TODO: We could make a GD file name from the real one but a valid dummy name
+          seems to work OK here. The user must have the proper game selected in
+          the SF7 menu even if the real name is used.
+  */
+  strcpy ((char *) gdfilename, "SF16497 S00");
+  if (gd6_send_prolog_bytes (gdfilename, 11) == GD_ERROR)
+    io_error ();
+
+  if (gd6_sync_receive_start () == GD_ERROR)
+    io_error ();
+
+  if (gd6_receive_bytes (buffer, 16) == GD_ERROR)
+    io_error ();
+
+  transfer_size = buffer[1] | (buffer[2] << 8) | (buffer[3] << 16) | (buffer[4] << 24);
+  if (transfer_size != 0x38000)
+    {
+      fputs ("ERROR: Saver transfer size from Game Doctor != 0x38000 bytes\n", stderr);
+      exit (1);
+    }
+
+  printf ("Receive: %d Bytes\n", transfer_size);
+  puts ("Press q to abort\n");
+
+  starttime = time (NULL);
+  while (bytesreceived < transfer_size)
+    {
+      if (transfer_size - bytesreceived >= BUFFERSIZE)
+        len = BUFFERSIZE;
+      else
+        len = transfer_size - bytesreceived;
+
+      if (gd6_receive_bytes (buffer, len) == GD_ERROR)
+        io_error ();
+      fwrite (buffer, 1, len, file);
+
+      bytesreceived += len;
+      ucon64_gauge (starttime, bytesreceived, 32 * 1024);
+      gd_checkabort (2);
+    }
+
+  unregister_func (remove_destfile);
+  free (buffer);
+  fclose (file);
+  deinit_io ();
+
+  return 0;
+}
+
+
+int
+gd3_write_saver (const char *filename, unsigned int parport)
+{
+  gd_send_prolog_bytes = gd3_send_prolog_bytes;
+  gd_send_bytes = gd3_send_bytes;
+
+  return gd_write_saver (filename, parport, GD3_PROLOG_STRING);
+}
+
+
+int
+gd6_write_saver (const char *filename, unsigned int parport)
+{
+  gd_send_prolog_bytes = gd6_send_prolog_bytes;
+  gd_send_bytes = gd6_send_bytes;
+
+  return gd_write_saver (filename, parport, GD6_READ_PROLOG_STRING);
+}
+
+
+int
+gd_write_saver (const char *filename, unsigned int parport, const char *prolog_str)
+{
+  FILE *file;
+  unsigned char *buffer, gdfilename[12];
+  const char *p;
+  int bytesread, bytessend = 0, size, fn_length;
+  time_t starttime;
+
+  init_io (parport);
+
+  /*
+    Check that filename is a valid Game Doctor saver filename.
+    It should start with SF, followed by the game ID, followed by the extension.
+    The extension is of the form .S## (where # is a digit from 0-9).
+    E.g., SF16123.S00
+    The saver base filename must match the base name of the game (loaded in the
+    Game Doctor) that you are loading the saver data for.
+  */
+
+  // Strip the path out of filename for use in the GD
+  p = basename2 (filename);
+  fn_length = strlen (p);
+
+  if (fn_length < 6 || fn_length > 11 // 7 ("base") + 1 (period) + 3 (extension)
+      || toupper (p[0]) != 'S' || toupper (p[1]) != 'F'
+      || p[fn_length - 4] != '.' || toupper (p[fn_length - 3]) != 'S')
+    {
+      fprintf (stderr, "ERROR: Filename (%s) is not a saver filename (SF*.S##)\n", p);
+      exit (1);
+    }
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = fsizeof (filename);
+  if (size != 0x38000)                  // GD saver size is always 0x38000 bytes -- no header
+    {
+      fputs ("ERROR: GD saver file size must be 229376 bytes\n", stderr);
+      exit (1);
+    }
+
+  // Make a GD file name from the real one
+  memset (gdfilename, ' ', 11);                 // "pad" with spaces
+  gdfilename[11] = 0;                           // terminate string
+  memcpy (gdfilename, p, fn_length - 4);        // copy name except extension
+  memcpy (&gdfilename[8], "S00", 3);            // copy extension S00
+  strupr ((char *) gdfilename);
+
+  printf ("Send: %d Bytes\n", size);
+  fseek (file, 0, SEEK_SET);
+
+  if (memcmp (prolog_str, GD6_READ_PROLOG_STRING, 4) == 0)
+    if (gd6_sync_hardware () == GD_ERROR)
+      io_error ();
+  memcpy (buffer, prolog_str, 4);
+  buffer[4] = 1;
+  if (gd_send_prolog_bytes (buffer, 5) == GD_ERROR)
+    io_error ();
+
+  // Transfer 0x38000 bytes
+  buffer[0] = 0x00;
+  buffer[1] = 0x80;
+  buffer[2] = 0x03;
+  buffer[3] = 0x00;
+  if (gd_send_prolog_bytes (buffer, 4) == GD_ERROR)
+    io_error ();
+
+  if (gd_send_prolog_bytes (gdfilename, 11) == GD_ERROR)
+    io_error ();
+
+  puts ("Press q to abort\n");                  // print here, NOT before first GD I/O,
+                                                //  because if we get here q works ;-)
+  starttime = time (NULL);
+  while ((bytesread = fread (buffer, 1, BUFFERSIZE, file)))
+    {
+      if (gd_send_bytes (buffer, bytesread) == GD_ERROR)
+        io_error ();
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      gd_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  deinit_io ();
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 49 - 0
ucon64/2.0/src/backup/gd.h

@@ -0,0 +1,49 @@
+/*
+gd.h - Game Doctor support for uCON64
+
+Copyright (c) 2002 John Weidman
+Copyright (c) 2002 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef GD_H
+#define GD_H
+
+extern const st_getopt2_t gd_usage[];
+
+#define GD_HEADER_START 0
+#define GD_HEADER_LEN 512
+#define GD3_MAX_UNITS 16                        // Maximum that the hardware supports
+// Each logical memory unit is 8 Mbit in size (internally it's 2*4 Mbit)
+
+#ifdef USE_PARALLEL
+extern int gd3_read_rom (const char *filename, unsigned int parport);
+extern int gd3_write_rom (const char *filename, unsigned int parport,
+                          st_rominfo_t *rominfo);
+extern int gd6_read_rom (const char *filename, unsigned int parport);
+extern int gd6_write_rom (const char *filename, unsigned int parport,
+                          st_rominfo_t *rominfo);
+extern int gd3_read_sram (const char *filename, unsigned int parport);
+extern int gd3_write_sram (const char *filename, unsigned int parport);
+extern int gd6_read_sram (const char *filename, unsigned int parport);
+extern int gd6_write_sram (const char *filename, unsigned int parport);
+extern int gd3_read_saver (const char *filename, unsigned int parport);
+extern int gd3_write_saver (const char *filename, unsigned int parport);
+extern int gd6_read_saver (const char *filename, unsigned int parport);
+extern int gd6_write_saver (const char *filename, unsigned int parport);
+#endif // USE_PARALLEL
+
+#endif // GD_H

+ 46 - 0
ucon64/2.0/src/backup/interceptor.c

@@ -0,0 +1,46 @@
+/*
+interceptor.c - Mega Disk/Super Disk Interceptor support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "misc/misc.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "interceptor.h"
+
+
+const st_getopt2_t interceptor_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Mega Disk/Super Disk (Interceptor),"/*"19XX Taiwan Sang Ting Co. Ltd."*/,
+      NULL
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };

+ 31 - 0
ucon64/2.0/src/backup/interceptor.h

@@ -0,0 +1,31 @@
+/*
+interceptor.h - Mega Disk/Super Disk Interceptor support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef INTERCEPTOR_H
+#define INTERCEPTOR_H
+
+extern const st_getopt2_t interceptor_usage[];
+
+typedef struct st_interceptor_header
+{
+  char pad[512];
+} st_interceptor_header_t;
+
+#endif

+ 169 - 0
ucon64/2.0/src/backup/libcd64/Makefile

@@ -0,0 +1,169 @@
+CC=gcc
+
+ifdef DEBUG
+# I think we only use gnu99 instead of c99 due to va_args extensions.
+CFLAGS=-I. -Wall -W -pg -g -pedantic -ansi -DDEBUG
+else
+CFLAGS=-I. -Wall -W -O6 -funroll-loops -fexpensive-optimizations
+endif
+
+ifndef DJGPP
+# uname is not available by default under DOS
+OSTYPE=$(shell uname -s)
+else
+OSTYPE=DJGPP
+endif
+
+GCC_WIN=0
+ifeq ($(findstring MINGW,$(OSTYPE)),MINGW)
+GCC_WIN=1
+endif
+ifeq ($(findstring CYGWIN,$(OSTYPE)),CYGWIN)
+GCC_WIN=1
+endif
+
+ifdef DJGPP
+LDFLAGS=
+else
+ifeq ($(findstring BeOS,$(OSTYPE)),BeOS)
+LDFLAGS=-nostart
+else # Unix or Win GCC
+LDFLAGS=-shared
+endif
+endif
+
+ifeq ($(findstring DJGPP,$(OSTYPE)),)
+ifneq ($(GCC_WIN),1)
+CFLAGS+=-fPIC
+else
+# Cygwin and MinGW need an import library for a DLL
+LDFLAGS+=-Wl,--out-implib,libcd64dll.a
+endif
+endif
+
+# The next check is not really specific to FreeBSD or OpenBSD -- the version of
+#  gcc I use is just old.
+ifeq ($(findstring FreeBSD,$(OSTYPE)),)
+ifeq ($(findstring OpenBSD,$(OSTYPE)),)
+CFLAGS+=-std=gnu99
+endif
+endif
+
+
+DEFAULT_BUILD=1
+
+# If the user passed anything, we are not a default build.
+
+ifdef LIBIEEE1284
+DEFAULT_BUILD=0
+else
+ifdef PPDEV
+DEFAULT_BUILD=0
+else
+ifdef PORTDEV
+DEFAULT_BUILD=0
+else
+ifdef RAWIO
+DEFAULT_BUILD=0
+endif
+endif
+endif
+endif
+
+ifeq ($(DEFAULT_BUILD),1)
+# Put default build options for each OS here
+
+ifeq ($(findstring DJGPP,$(OSTYPE)),DJGPP)
+RAWIO=1
+endif
+
+ifeq ($(findstring MINGW,$(OSTYPE)),MINGW)
+RAWIO=1
+endif
+
+ifeq ($(findstring CYGWIN,$(OSTYPE)),CYGWIN)
+RAWIO=1
+endif
+
+ifeq ($(findstring BeOS,$(OSTYPE)),BeOS)
+RAWIO=1
+endif
+
+ifeq ($(findstring OpenBSD,$(OSTYPE)),OpenBSD)
+# i386_iopl() is located in libi386.a (note the .a)
+LIBS+=-L/usr/lib -li386
+RAWIO=1
+endif
+
+ifeq ($(findstring FreeBSD,$(OSTYPE)),FreeBSD)
+RAWIO=1
+endif
+
+ifeq ($(findstring Linux,$(OSTYPE)),Linux)
+ifeq ($(shell if test -r /usr/include/ieee1284.h; then echo 1; else echo 0; fi),1)
+LIBIEEE1284=1
+endif
+ifeq ($(shell if test -r /usr/include/linux/ppdev.h; then echo 1; else echo 0; fi),1)
+PPDEV=1
+endif
+PORTDEV=1
+RAWIO=1
+endif
+
+endif # DEFAULT_BUILD = 1
+
+# Now for backend-specific defs
+
+ifdef LIBIEEE1284
+CFLAGS+=-DCD64_USE_LIBIEEE1284
+LIBS+=-lieee1284
+endif
+
+ifdef PPDEV
+CFLAGS+=-DCD64_USE_PPDEV
+endif
+
+ifdef PORTDEV
+CFLAGS+=-DCD64_USE_PORTDEV
+endif
+
+ifdef RAWIO
+CFLAGS+=-DCD64_USE_RAWIO
+endif
+
+default: all
+
+ifeq ($(findstring DJGPP,$(OSTYPE)),DJGPP)
+all:	libcd64.a
+else
+ifeq ($(GCC_WIN),1)
+all:	libcd64.a cd64.dll
+else
+all:	libcd64.a libcd64.so
+endif # GCC_WIN
+endif # DJGPP
+
+# libcd64 stuff
+
+cd64io.o: cd64io.c
+	$(CC) $(CFLAGS) $^ -c -o $@
+
+cd64lib.o: cd64lib.c
+	$(CC) $(CFLAGS) $^ -c -o $@
+
+libcd64.a: cd64lib.o cd64io.o
+	ld -r $^ $(LIBS) -o $*.o
+#	rm -f $@
+	ar crs $@ $*.o
+
+LDFLAGS+=$(LIBS)
+ifeq ($(GCC_WIN),1)
+cd64.dll: cd64lib.o cd64io.o
+else
+libcd64.so: cd64lib.o cd64io.o
+endif
+#	rm -f $@
+	$(CC) $^ $(LDFLAGS) -o $@
+
+clean:
+	rm -f *.o *.so *.dll *.a a.out

+ 62 - 0
ucon64/2.0/src/backup/libcd64/Makefile.vc6

@@ -0,0 +1,62 @@
+CC=cl.exe
+
+!ifdef DEBUG
+CFLAGS=/nologo /I. /Zi /Oi /GZ /W3 /DDEBUG
+!else
+CFLAGS=/nologo /I. /W3 /O2
+!endif
+
+DEFAULT_BUILD=1
+
+# If the user passed anything, we are not a default build.
+
+!ifdef LIBIEEE1284
+DEFAULT_BUILD=0
+!else
+!ifdef RAWIO
+DEFAULT_BUILD=0
+!endif
+!endif
+
+!if $(DEFAULT_BUILD)==1
+# Put default build options here
+
+RAWIO=1
+
+!endif # DEFAULT_BUILD = 1
+
+# Now for backend-specific defs
+
+!ifdef LIBIEEE1284
+CFLAGS=$(CFLAGS) /DCD64_USE_LIBIEEE1284
+LIBS=$(LIBS) ieee1284.lib
+!endif
+
+!ifdef RAWIO
+CFLAGS=$(CFLAGS) /DCD64_USE_RAWIO
+!endif
+
+default: all
+
+all:	cd64.lib cd64.dll
+
+# libcd64 stuff
+
+cd64io.obj: cd64io.c
+	$(CC) $(CFLAGS) $** /c /Fo$@
+
+cd64lib.obj: cd64lib.c
+	$(CC) $(CFLAGS) $** /c /Fo$@
+
+cd64.lib: cd64lib.obj cd64io.obj
+	lib.exe /NOLOGO $** $(LIBS) /OUT:$@
+
+cd64.dll: cd64lib.obj cd64io.obj
+	link.exe /NOLOGO /DLL $** $(LIBS) /DEF:cd64.def /IMPLIB:cd64dll.lib /OUT:$@
+
+clean:
+	del *.obj
+	del *.exp
+	del *.lib
+	del *.dll
+	del *.pdb

+ 28 - 0
ucon64/2.0/src/backup/libcd64/cd64.def

@@ -0,0 +1,28 @@
+EXPORTS
+cd64_create
+cd64_send_byte
+cd64_send_dword
+cd64_grab_byte
+cd64_grab_dword
+cd64_trade_bytes
+cd64_bios_grab
+cd64_bios_send
+cd64_ghemor_grab
+cd64_ghemor_send
+cd64_upload_dram
+cd64_upload_ram
+cd64_upload_bootemu
+cd64_upload_sram
+cd64_upload_flashram
+cd64_upload_eeprom
+cd64_upload_mempak
+cd64_download_cart
+cd64_download_dram
+;cd64_download_ram
+cd64_download_sram
+cd64_download_flashram
+cd64_download_eeprom
+cd64_download_mempak
+cd64_run_dram
+cd64_run_cart
+cd64_download_header

+ 1005 - 0
ucon64/2.0/src/backup/libcd64/cd64io.c

@@ -0,0 +1,1005 @@
+/*
+ *
+ * cd64io.c
+ *
+ * I/O routines for CD64 device
+ *
+ * (c) 2004 Ryan Underwood
+ * Portions (c) 2004 Daniel Horchner (OpenBSD, FreeBSD, BeOS, Win32, DOS)
+ *
+ * May be distributed under the terms of the GNU Lesser/Library General Public
+ * License, or any later version of the same, as published by the Free Software
+ * Foundation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if defined __unix__ || defined __BEOS__ /* ioctl() */
+#include <unistd.h>
+#endif
+
+#include <ultra64/host/cd64lib.h>
+#include "cd64io.h"
+
+#define DEBUG_LOWLEVEL 0
+#define BUSY_THRESHOLD 10000
+#define MAX_TRIES 5
+
+#ifdef CD64_USE_RAWIO
+
+#if defined _WIN32 || defined __CYGWIN__
+#ifdef __CYGWIN__
+#define FILE_SEPARATOR_S "/"
+#else
+#define snprintf _snprintf
+#define FILE_SEPARATOR_S "\\"
+#endif
+
+static void *io_driver = NULL;
+static int io_driver_found = 0;
+/* io.dll */
+static char (WINAPI *PortIn)(short int) = NULL;
+static void (WINAPI *PortOut)(short int, char) = NULL;
+static short int (WINAPI *IsDriverInstalled)() = NULL;
+/* DlPortIO.dll */
+static unsigned char (__stdcall *DlPortReadPortUchar)(unsigned long) = NULL;
+static void (__stdcall *DlPortWritePortUchar)(unsigned long, unsigned char) = NULL;
+
+INLINE uint8_t inb(uint16_t);
+INLINE void outb(uint8_t, uint16_t);
+static uint8_t (*input_byte)(uint16_t) = inb;
+static void (*output_byte)(uint8_t, uint16_t) = outb;
+#endif
+
+#ifdef  __BEOS__
+static int io_portfd;
+
+typedef struct st_ioport {
+	unsigned int port;
+	unsigned char data8;
+	unsigned short data16;
+} st_ioport_t;
+#endif
+
+#endif /* CD64_USE_RAWIO */
+
+int cd64_send_byte(struct cd64_t *cd64, uint8_t what) {
+	return cd64->xfer(cd64, &what, NULL, 0);
+}
+
+int cd64_send_dword(struct cd64_t *cd64, uint32_t what) {
+
+	int ret = 1;
+	ret &= cd64_send_byte(cd64, (uint8_t) (what>>24));
+	ret &= cd64_send_byte(cd64, (uint8_t) (what>>16));
+	ret &= cd64_send_byte(cd64, (uint8_t) (what>>8));
+	ret &= cd64_send_byte(cd64, (uint8_t) what);
+	return ret;
+}
+
+int cd64_grab_byte(struct cd64_t *cd64, uint8_t *val) {
+	return cd64->xfer(cd64, NULL, val, 0);
+}
+
+int cd64_grab_dword(struct cd64_t *cd64, uint32_t *val) {
+
+	int ret = 1;
+	uint8_t grab;
+	if (val == NULL) return 0;
+	*val = 0;
+
+	ret &= cd64_grab_byte(cd64, &grab);
+	*val |= grab << 24;
+	ret &= cd64_grab_byte(cd64, &grab);
+	*val |= grab << 16;
+	ret &= cd64_grab_byte(cd64, &grab);
+	*val |= grab << 8;
+	ret &= cd64_grab_byte(cd64, &grab);
+	*val |= grab;
+	return ret;
+}
+
+int cd64_trade_bytes(struct cd64_t *cd64, uint8_t give, uint8_t *recv) {
+	return cd64->xfer(cd64, &give, recv, 0);
+}
+
+/* Backend-specific defs go down here. */
+
+#ifdef CD64_USE_LIBIEEE1284
+
+int cd64_open_ieee1284(struct cd64_t *cd64) {
+
+	struct parport_list pplist;
+	int ppflags = F1284_EXCL;
+	int ppcaps = 0;
+	int i;
+	int opened = 0;
+
+	if (cd64->ppdev || !cd64->using_ppa) return 0;
+
+	if (ieee1284_find_ports(&pplist, 0) < 0) {
+		cd64->notice_callback2("couldn't get port list\n");
+		return 0;
+	}
+
+	if (cd64->port < pplist.portc) {
+		/* Just use it as an index. */
+		cd64->ppdev = pplist.portv[cd64->port];
+	}
+	else {
+		/* Search for the ppdev matching its base address. */
+		for (i = 0; i < pplist.portc; i++) {
+			if (cd64->port == (int) pplist.portv[i]->base_addr) {
+				cd64->ppdev = pplist.portv[i];
+			}
+		}
+	}
+
+	if (cd64->ppdev) {
+		if (ieee1284_open(cd64->ppdev, ppflags, &ppcaps) < 0) {
+			cd64->notice_callback2("failed opening ieee1284 port %d\n", cd64->port);
+			cd64->ppdev = NULL;
+		}
+		else {
+			opened = 1;
+		}
+	}
+
+	ieee1284_free_ports(&pplist);
+
+	if (opened && ieee1284_claim(cd64->ppdev) < 0) return 0;
+	else return opened;
+}
+
+int cd64_close_ieee1284(struct cd64_t *cd64) {
+
+	int ret;
+
+	if (cd64->ppdev == NULL) return 1;
+
+	ieee1284_release(cd64->ppdev);
+	ret = ieee1284_close(cd64->ppdev);
+	if (ret < 0) ret = 0;
+	else {
+		cd64->ppdev = NULL;
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static INLINE int cd64_wait_ieee(struct cd64_t *cd64) {
+
+	/* With ppdev, could we use an interrupt instead?  The PPA
+	 * could be modified... */
+
+	int i = 0;
+	int reset_tries = 0;
+	while (i < 10000) i++; /* FIXME is this necessary? */
+	i = 0;
+
+	while((ieee1284_read_status(cd64->ppdev)^S1284_INVERTED) & S1284_BUSY) {
+		i++;
+		if (i >= BUSY_THRESHOLD) {
+			/* The PPA is in a weird state.
+			 * Try to knock some sense into it. */
+			ieee1284_write_control(cd64->ppdev, (C1284_NINIT|C1284_NAUTOFD)^C1284_INVERTED);
+			ieee1284_write_control(cd64->ppdev, C1284_NINIT^C1284_INVERTED);
+			ieee1284_write_control(cd64->ppdev, (C1284_NINIT|C1284_NSTROBE)^C1284_INVERTED);
+			ieee1284_write_control(cd64->ppdev, C1284_NINIT^C1284_INVERTED);
+			reset_tries++;
+			i = 0;
+			USLEEP(1);
+		}
+		if (reset_tries > MAX_TRIES) break;
+		if (cd64->abort) return 0;
+	}
+
+	return (reset_tries < MAX_TRIES);
+}
+
+int cd64_xfer_ieee1284(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms) {
+
+	if (!cd64_wait_ieee(cd64)) { return 0; }
+
+	if (delayms) USLEEP(delayms);
+	ieee1284_data_dir(cd64->ppdev, 1);
+	if (delayms) USLEEP(delayms);
+	ieee1284_write_control(cd64->ppdev, (C1284_NINIT|C1284_NAUTOFD)^C1284_INVERTED);
+	if (delayms) USLEEP(delayms);
+	if (rd) {
+		*rd = ieee1284_read_data(cd64->ppdev);
+#if DEBUG_LOWLEVEL
+		printf("got %xh", *rd);
+		if (*rd > 0x20) printf(" (%c)", *rd);
+		printf("\n");
+#endif
+	}
+
+	if (delayms) USLEEP(delayms);
+	ieee1284_data_dir(cd64->ppdev, 0);
+	if (delayms) USLEEP(delayms);
+	ieee1284_write_control(cd64->ppdev, C1284_NINIT^C1284_INVERTED);
+	if (delayms) USLEEP(delayms);
+	if (wr) {
+		ieee1284_write_data(cd64->ppdev, *wr);
+#if DEBUG_LOWLEVEL
+		printf("put %xh", *wr);
+		if (*wr > 0x20) printf(" (%c)", *wr);
+		printf("\n");
+#endif
+	}
+	if (delayms) USLEEP(delayms);
+	ieee1284_write_control(cd64->ppdev, (C1284_NINIT|C1284_NSTROBE)^C1284_INVERTED);
+	if (delayms) USLEEP(delayms);
+	ieee1284_write_control(cd64->ppdev, C1284_NINIT^C1284_INVERTED);
+
+	return 1;
+}
+
+#endif /* CD64_USE_LIBIEEE1284 */
+
+
+#ifdef CD64_USE_PPDEV
+
+int cd64_open_ppdev(struct cd64_t *cd64) {
+
+	char *device = "/dev/parport%d";
+	char realdev[128+1];
+
+	if (cd64->ppdevfd || !cd64->using_ppa) return 0;
+	/* This should be a port number only, not an address */
+	if (cd64->port > PARPORT_MAX) return 0;
+
+	snprintf(realdev, 128, device, cd64->port);
+
+	if ((cd64->ppdevfd = open(realdev, O_RDWR)) == -1) {
+		cd64->notice_callback2("open: %s", strerror(errno));
+		cd64->ppdevfd = 0;
+		return 0;
+	}
+
+	if (ioctl(cd64->ppdevfd, PPEXCL) != 0) {
+		cd64->notice_callback2("PPEXCL: %s", strerror(errno));
+		close(cd64->ppdevfd);
+		cd64->ppdevfd = 0;
+		return 0;
+	}
+
+	if (ioctl(cd64->ppdevfd, PPCLAIM) != 0) {
+		cd64->notice_callback2("PPCLAIM: %s", strerror(errno));
+		close(cd64->ppdevfd);
+		cd64->ppdevfd = 0;
+		return 0;
+	}
+
+	return 1;
+}
+
+int cd64_close_ppdev(struct cd64_t *cd64) {
+
+	int ret = 1;
+
+	if (cd64->ppdevfd == 0) return 1;
+
+	if (ioctl(cd64->ppdevfd, PPRELEASE) != 0) {
+		cd64->notice_callback2("PPRELEASE: %s", strerror(errno));
+		ret = 0;
+	}
+
+	close(cd64->ppdevfd);
+	cd64->ppdevfd = 0;
+	return ret;
+}
+
+static INLINE int cd64_wait_ppdev(struct cd64_t *cd64) {
+
+	/* With ppdev, could we use an interrupt instead?  The PPA
+	 * could be modified... */
+
+	int i = 0;
+	int reset_tries = 0;
+	uint8_t status;
+	int dir;
+	i = 0;
+
+	if (ioctl(cd64->ppdevfd, PPRSTATUS, &status) != 0) cd64->notice_callback2("PPRSTATUS: %s", strerror(errno));
+
+	while(status & 0x80) {
+		i++;
+		if (i >= BUSY_THRESHOLD) {
+			/* The PPA is in a weird state.
+			 * Try to knock some sense into it. */
+			dir = 1;
+			if (ioctl(cd64->ppdevfd, PPDATADIR, &dir) != 0) cd64->notice_callback2("PPDATADIR: %s", strerror(errno));
+			status = PARPORT_CONTROL_INIT | PARPORT_CONTROL_AUTOFD; /* 0x26 */
+			if (ioctl(cd64->ppdevfd, PPWCONTROL, &status) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
+
+			dir = 0;
+			if (ioctl(cd64->ppdevfd, PPDATADIR, &dir) != 0) cd64->notice_callback2("PPDATADIR: %s", strerror(errno));
+			status = PARPORT_CONTROL_INIT; /* 0x04 */
+			if (ioctl(cd64->ppdevfd, PPWCONTROL, &status) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
+			status = PARPORT_CONTROL_INIT | PARPORT_CONTROL_STROBE; /* 0x05 */
+			if (ioctl(cd64->ppdevfd, PPWCONTROL, &status) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
+			status = PARPORT_CONTROL_INIT; /* 0x04 */
+			if (ioctl(cd64->ppdevfd, PPWCONTROL, &status) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
+			reset_tries++;
+			i = 0;
+			USLEEP(1);
+		}
+		if (cd64->abort) return 0;
+		if (reset_tries > MAX_TRIES) break;
+
+		if (ioctl(cd64->ppdevfd, PPRSTATUS, &status) != 0) cd64->notice_callback2("PPRSTATUS: %s", strerror(errno));
+	}
+
+	return (reset_tries < MAX_TRIES);
+}
+
+int cd64_xfer_ppdev(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms) {
+
+	uint8_t ctl;
+	int dir;
+
+	if (!cd64_wait_ppdev(cd64)) { return 0; }
+
+	if (delayms) USLEEP(delayms);
+	dir = 1;
+	if (ioctl(cd64->ppdevfd, PPDATADIR, &dir) != 0) cd64->notice_callback2("PPDATADIR: %s", strerror(errno));
+	if (delayms) USLEEP(delayms);
+	ctl = PARPORT_CONTROL_INIT | PARPORT_CONTROL_AUTOFD;
+	if (ioctl(cd64->ppdevfd, PPWCONTROL, &ctl) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
+	if (delayms) USLEEP(delayms);
+	if (rd) {
+		if (ioctl(cd64->ppdevfd, PPRDATA, rd) != 0) cd64->notice_callback2("PPRDATA: %s", strerror(errno));
+#if DEBUG_LOWLEVEL
+		printf("got %xh", *rd);
+		if (*rd > 0x20) printf(" (%c)", *rd);
+		printf("\n");
+#endif
+	}
+
+	if (delayms) USLEEP(delayms);
+	dir = 0;
+	if (ioctl(cd64->ppdevfd, PPDATADIR, &dir) != 0) cd64->notice_callback2("PPDATADIR: %s", strerror(errno));
+	if (delayms) USLEEP(delayms);
+	ctl = PARPORT_CONTROL_INIT;
+	if (ioctl(cd64->ppdevfd, PPWCONTROL, &ctl) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
+	if (delayms) USLEEP(delayms);
+	if (wr) {
+		if (ioctl(cd64->ppdevfd, PPWDATA, wr) != 0) cd64->notice_callback2("PPWDATA: %s", strerror(errno));
+#if DEBUG_LOWLEVEL
+		printf("put %xh", *wr);
+		if (*wr > 0x20) printf(" (%c)", *wr);
+		printf("\n");
+#endif
+	}
+	if (delayms) USLEEP(delayms);
+	ctl = PARPORT_CONTROL_INIT | PARPORT_CONTROL_STROBE;
+	if (ioctl(cd64->ppdevfd, PPWCONTROL, &ctl) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
+	if (delayms) USLEEP(delayms);
+	ctl = PARPORT_CONTROL_INIT;
+	if (ioctl(cd64->ppdevfd, PPWCONTROL, &ctl) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
+
+	return 1;
+}
+
+#endif
+
+#ifdef CD64_USE_PORTDEV
+
+int cd64_open_portdev(struct cd64_t *cd64) {
+
+	if (cd64->portdevfd || cd64->port == 0) return 0;
+
+	if ((cd64->portdevfd = open("/dev/port", O_RDWR)) == -1) {
+		cd64->notice_callback2("open: %s", strerror(errno));
+		cd64->notice_callback2("portdev requires CAP_SYS_RAWIO capability");
+		cd64->portdevfd = 0;
+		return 0;
+	}
+
+	return 1;
+}
+
+int cd64_close_portdev(struct cd64_t *cd64) {
+
+	if (cd64->portdevfd == 0) return 1;
+
+	if (close(cd64->portdevfd) == -1) {
+		cd64->notice_callback2("close: %s", strerror(errno));
+		return 0;
+	}
+	cd64->portdevfd = 0;
+	return 1;
+}
+
+static INLINE int cd64_wait_portdev(struct cd64_t *cd64) {
+
+	int i = 0;
+	int reset_tries = 0;
+	uint8_t status;
+	int dir;
+	i = 0;
+
+	if (cd64->using_ppa) {
+		lseek(cd64->portdevfd, cd64->port+1, SEEK_SET);
+		read(cd64->portdevfd, &status, 1);
+
+		while(status & 0x80) {
+			i++;
+			if (i >= BUSY_THRESHOLD) {
+				/* The PPA is in a weird state.
+				 * Try to knock some sense into it. */
+				dir = 1;
+				status = 0x06 | (dir << 5);
+				lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
+				write(cd64->portdevfd, &status, 1);
+
+				dir = 0;
+				status = 0x04 | (dir << 5);
+				lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
+				write(cd64->portdevfd, &status, 1);
+				status = 0x05 | (dir << 5);
+				lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
+				write(cd64->portdevfd, &status, 1);
+				status = 0x04 | (dir << 5);
+				lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
+				write(cd64->portdevfd, &status, 1);
+
+				reset_tries++;
+				i = 0;
+				USLEEP(1);
+			}
+			if (cd64->abort) return 0;
+			if (reset_tries > MAX_TRIES) {
+				break;
+			}
+
+			lseek(cd64->portdevfd, cd64->port+1, SEEK_SET);
+			read(cd64->portdevfd, &status, 1);
+		}
+	}
+	else { /* Comms link */
+		lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
+		read(cd64->portdevfd, &status, 1);
+		while (status & 1) {
+			/* Do we need to handle a stuck situation here? */
+			lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
+			read(cd64->portdevfd, &status, 1);
+		}
+	}
+
+	return (reset_tries < MAX_TRIES);
+}
+
+int cd64_xfer_portdev(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms) {
+
+	uint8_t ctl;
+	int dir;
+
+	if (cd64->using_ppa) {
+
+		if (!cd64_wait_portdev(cd64)) { return 0; }
+
+		if (delayms) USLEEP(delayms);
+		dir = 1;
+		ctl = 0x06 | (dir << 5);
+		lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
+		write(cd64->portdevfd, &ctl, 1);
+		if (delayms) USLEEP(delayms);
+		if (rd) {
+			lseek(cd64->portdevfd, cd64->port, SEEK_SET);
+			read(cd64->portdevfd, rd, 1);
+#if DEBUG_LOWLEVEL
+			printf("got %xh", *rd);
+			if (*rd > 0x20) printf(" (%c)", *rd);
+			printf("\n");
+#endif
+		}
+
+		if (delayms) USLEEP(delayms);
+		dir = 0;
+		ctl = 0x04 | (dir << 5);
+		lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
+		write(cd64->portdevfd, &ctl, 1);
+		if (delayms) USLEEP(delayms);
+		if (wr) {
+			lseek(cd64->portdevfd, cd64->port, SEEK_SET);
+			write(cd64->portdevfd, wr, 1);
+#if DEBUG_LOWLEVEL
+			printf("put %xh", *wr);
+			if (*wr > 0x20) printf(" (%c)", *wr);
+			printf("\n");
+#endif
+		}
+		if (delayms) USLEEP(delayms);
+		ctl = 0x05 | (dir << 5);
+		lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
+		write(cd64->portdevfd, &ctl, 1);
+		if (delayms) USLEEP(delayms);
+		ctl = 0x04 | (dir << 5);
+		lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
+		write(cd64->portdevfd, &ctl, 1);
+	}
+	else { /* Comms link */
+		lseek(cd64->portdevfd, cd64->port, SEEK_SET);
+		write(cd64->portdevfd, wr, 1);
+		if (!cd64_wait_portdev(cd64)) { return 0; }
+		lseek(cd64->portdevfd, cd64->port, SEEK_SET);
+		read(cd64->portdevfd, rd, 1);
+	}
+
+	return 1;
+}
+
+#endif
+
+
+#ifdef CD64_USE_RAWIO
+
+#if defined _WIN32 || defined __CYGWIN__
+
+static void *open_module(char *module_name, struct cd64_t *cd64) {
+
+	void *handle = LoadLibrary(module_name);
+	if (handle == NULL) {
+		LPTSTR strptr;
+
+		cd64->notice_callback2("LoadLibrary: %s", strerror(errno));
+		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+		              FORMAT_MESSAGE_FROM_SYSTEM |
+		              FORMAT_MESSAGE_IGNORE_INSERTS,
+		              NULL, GetLastError(),
+		              MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+		              (LPTSTR) &strptr, 0, NULL);
+		cd64->notice_callback2(strptr);
+		LocalFree(strptr);
+		exit(1);
+	}
+	return handle;
+}
+
+static void close_module(void *handle, struct cd64_t *cd64) {
+
+	if (!FreeLibrary((HINSTANCE) handle)) {
+		LPTSTR strptr;
+
+		cd64->notice_callback2("FreeLibrary: %s", strerror(errno));
+		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+		              FORMAT_MESSAGE_FROM_SYSTEM |
+		              FORMAT_MESSAGE_IGNORE_INSERTS,
+		              NULL, GetLastError(),
+		              MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+		              (LPTSTR) &strptr, 0, NULL);
+		cd64->notice_callback2(strptr);
+		LocalFree(strptr);
+		exit(1);
+	}
+}
+
+static void *get_symbol(void *handle, char *symbol_name, struct cd64_t *cd64) {
+
+	void *symptr = (void *) GetProcAddress((HINSTANCE) handle, symbol_name);
+	if (symptr == NULL) {
+		LPTSTR strptr;
+
+		cd64->notice_callback2("GetProcAddress: %s", strerror(errno));
+		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+		              FORMAT_MESSAGE_FROM_SYSTEM |
+		              FORMAT_MESSAGE_IGNORE_INSERTS,
+		              NULL, GetLastError(),
+		              MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+		              (LPTSTR) &strptr, 0, NULL);
+		cd64->notice_callback2(strptr);
+		LocalFree(strptr);
+		exit(1);
+	}
+	return symptr;
+}
+
+/* io.dll */
+static uint8_t io_input_byte(uint16_t port) {
+	return PortIn(port);
+}
+
+static void io_output_byte(uint8_t byte, uint16_t port) {
+	PortOut(port, byte);
+}
+
+/* DlPortIO.dll */
+static uint8_t dlportio_input_byte(uint16_t port) {
+	return DlPortReadPortUchar(port);
+}
+
+static void dlportio_output_byte(uint8_t byte, uint16_t port) {
+	DlPortWritePortUchar(port, byte);
+}
+
+#define NODRIVER_MSG "ERROR: No (working) I/O port driver\n"
+
+#ifdef __CYGWIN__
+static int new_exception_handler(PEXCEPTION_RECORD exception_record,
+                                 void *establisher_frame, PCONTEXT context_record,
+                                 void *dispatcher_context) {
+
+	(void) establisher_frame;
+	(void) context_record;
+	(void) dispatcher_context;
+	if (exception_record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION) {
+		fputs(NODRIVER_MSG, stderr);
+		exit(1);
+	}
+	return EXCEPTION_CONTINUE_SEARCH;
+}
+#elif defined _WIN32
+static LONG new_exception_filter(LPEXCEPTION_POINTERS exception_pointers) {
+
+	if (exception_pointers->ExceptionRecord->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION) {
+		fputs(NODRIVER_MSG, stderr);
+		exit(1);
+	}
+	return EXCEPTION_CONTINUE_SEARCH;
+}
+#endif
+#endif /* _WIN32 || __CYGWIN__ */
+
+#if ((defined _WIN32 || defined __CYGWIN__ || defined __BEOS__ || \
+     defined __MSDOS__) && \
+    (defined __i386__ || defined __x86_64__)) || defined _MSC_VER
+INLINE uint8_t inb(uint16_t port) {
+
+#ifdef __MSDOS__
+	return inportb(port);
+#elif defined __BEOS__
+	st_ioport_t temp;
+
+	temp.port = port;
+	ioctl(io_portfd, 'r', &temp, 0);
+
+	return temp.data8;
+#else /* Win32 */
+	if (io_driver_found) {
+		return input_byte(port);
+	}
+	else {
+#ifdef _MSC_VER
+		return (unsigned char) inp(port);
+#else
+		unsigned char byte;
+		__asm__ __volatile__
+		("inb %1, %0"
+		  : "=a" (byte)
+		  : "d" (port)
+		);
+		return byte;
+#endif
+	}
+#endif
+}
+
+INLINE void outb(uint8_t byte, uint16_t port) {
+
+#ifdef __MSDOS__
+	outportb(port, byte);
+#elif defined __BEOS__
+	st_ioport_t temp;
+
+	temp.port = port;
+	temp.data8 = byte;
+	ioctl(io_portfd, 'w', &temp, 0);
+#else /* Win32 */
+	if (io_driver_found) {
+		output_byte(byte, port);
+	}
+	else {
+#ifdef _MSC_VER
+		outp(port, byte);
+#else
+		__asm__ __volatile__
+		("outb %1, %0"
+		  :
+		  : "d" (port), "a" (byte)
+		);
+#endif
+	}
+#endif
+}
+#endif /* inb/outb defs */
+
+int cd64_open_rawio(struct cd64_t *cd64) {
+
+	int ret;
+	(void) ret;
+
+	/* NOTE: we will soon be able to use ioperm on the entire
+	 * 16-bit port range.  Find out what Linux kernels support it. */
+
+	if (cd64->port < 0x200) {
+		cd64->notice_callback2("Erroneous port %xh", cd64->port);
+		return 0;
+	}
+
+#ifdef __linux__
+	if (cd64->port < 0x3fd) {
+		if (cd64->using_ppa) {
+			ret = ioperm(cd64->port, 3, 1);
+		}
+		else {
+			ret = ioperm(cd64->port, 1, 1);
+			ret |= ioperm(cd64->port+2, 1, 1);
+		}
+
+		if (ret == -1) {
+			cd64->notice_callback2("ioperm: %s", strerror(errno));
+			cd64->notice_callback2("rawio requires CAP_SYS_RAWIO capability");
+			return 0;
+		}
+	}
+	else {
+		ret = iopl(3);
+		if (ret == -1) {
+			cd64->notice_callback2("iopl: %s", strerror(errno));
+			cd64->notice_callback2("rawio requires CAP_SYS_RAWIO capability");
+			return 0;
+		}
+	}
+#elif defined __OpenBSD__
+	/* I cannot test i386_set_ioperm(), so I only use i386_iopl() */
+	ret = i386_iopl(3);
+	if (ret == -1) {
+		cd64->notice_callback2("i386_iopl: %s", strerror(errno));
+		return 0;
+	}
+#elif defined __FreeBSD__
+	cd64->portdevfd = open("/dev/io", O_RDWR);
+	if (cd64->portdevfd == -1) {
+		cd64->portdevfd = 0;
+		cd64->notice_callback2("open: %s", strerror(errno));
+		cd64->notice_callback2("Could not open I/O port device (/dev/io)");
+		return 0;
+	}
+#elif defined __BEOS__
+	io_portfd = open("/dev/misc/ioport", O_RDWR | O_NONBLOCK);
+	if (io_portfd == -1) {
+		io_portfd = 0;
+		cd64->notice_callback2("open: %s", strerror(errno));
+		cd64->notice_callback2("Could not open I/O port device (no driver)");
+		exit(1);
+	}
+#elif defined _WIN32 || defined __CYGWIN__
+	{
+		char fname[FILENAME_MAX];
+		io_driver_found = 0;
+
+		if (!cd64->io_driver_dir[0]) strcpy(cd64->io_driver_dir, ".");
+		snprintf (fname, FILENAME_MAX, "%s" FILE_SEPARATOR_S "%s",
+		          cd64->io_driver_dir, "dlportio.dll");
+		if (access(fname, F_OK) == 0) {
+			io_driver = open_module(fname, cd64);
+
+			io_driver_found = 1;
+			DlPortReadPortUchar = (unsigned char (__stdcall *) (unsigned long))
+			                      get_symbol(io_driver, "DlPortReadPortUchar", cd64);
+			DlPortWritePortUchar = (void (__stdcall *) (unsigned long, unsigned char))
+			                       get_symbol(io_driver, "DlPortWritePortUchar", cd64);
+			input_byte = dlportio_input_byte;
+			output_byte = dlportio_output_byte;
+		}
+
+		if (!io_driver_found) {
+			snprintf (fname, FILENAME_MAX, "%s" FILE_SEPARATOR_S "%s",
+			          cd64->io_driver_dir, "io.dll");
+			if (access(fname, F_OK) == 0) {
+				io_driver = open_module(fname, cd64);
+
+				IsDriverInstalled = (short int (WINAPI *) ())
+				                    get_symbol(io_driver, "IsDriverInstalled", cd64);
+				if (IsDriverInstalled()) {
+					io_driver_found = 1;
+					PortIn = (char (WINAPI *) (short int))
+					         get_symbol(io_driver, "PortIn", cd64);
+					PortOut = (void (WINAPI *) (short int, char))
+					          get_symbol(io_driver, "PortOut", cd64);
+					input_byte = io_input_byte;
+					output_byte = io_output_byte;
+				}
+			}
+		}
+	}
+
+	{
+		/* __try and __except are not supported by MinGW and Cygwin. MinGW has
+		 * __try1 and __except1, but using them requires more code than we
+		 * currently have. Cygwin does something stupid which breaks
+		 * SetUnhandledExceptionFilter()... */
+#ifdef __CYGWIN__                               /* Cygwin */
+		exception_list list;
+		exception_handler *org_handler;
+		cygwin_internal(CW_INIT_EXCEPTIONS, &list);
+		org_handler = list.handler;
+		list.handler = new_exception_handler;
+		input_byte(0x378);
+		list.handler = org_handler;
+#elif defined _WIN32                            /* MinGW & Visual C++ */
+		LPTOP_LEVEL_EXCEPTION_FILTER org_exception_filter =
+		  SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) new_exception_filter);
+		input_byte(0x378);                          /* 0x378 is okay */
+
+		/* if we get here accessing I/O port 0x378 did not cause an exception */
+		SetUnhandledExceptionFilter(org_exception_filter);
+#endif
+	}
+#endif /* _WIN32 || __CYGWIN__ */
+
+	return 1;
+}
+
+int cd64_close_rawio(struct cd64_t *cd64) {
+
+	int ret;
+	(void) ret;
+	(void) cd64;
+
+#ifdef __linux__
+	if (cd64->port < 0x3fd) {
+		if (cd64->using_ppa) {
+			ret = ioperm(cd64->port, 3, 0);
+		}
+		else {
+			ret = ioperm(cd64->port, 1, 0);
+			ret |= ioperm(cd64->port+2, 1, 0);
+		}
+
+		if (ret == -1) {
+			cd64->notice_callback2("ioperm: %s", strerror(errno));
+			return 0;
+		}
+	}
+	else {
+		ret = iopl(0);
+		if (ret == -1) {
+			cd64->notice_callback2("iopl: %s", strerror(errno));
+			return 0;
+		}
+	}
+#elif defined __OpenBSD__
+	/* I cannot test i386_set_ioperm(), so I only use i386_iopl() */
+	ret = i386_iopl(0);
+	if (ret == -1) {
+		cd64->notice_callback2("i386_iopl: %s", strerror(errno));
+		return 0;
+	}
+#elif defined __FreeBSD__
+	if (close(cd64->portdevfd) == -1) {
+		cd64->notice_callback2("close: %s", strerror(errno));
+		return 0;
+	}
+	cd64->portdevfd = 0;
+#elif defined __BEOS__
+	if (close(io_portfd) == -1) {
+		cd64->notice_callback2("close: %s", strerror(errno));
+		return 0;
+	}
+	io_portfd = 0;
+#elif defined _WIN32 || defined __CYGWIN__
+	close_module(io_driver, cd64);
+	io_driver = NULL;
+	io_driver_found = 0;
+	input_byte = inb;
+	output_byte = outb;
+#endif
+
+	return 1;
+}
+
+static INLINE int cd64_wait_rawio(struct cd64_t *cd64) {
+
+	int i = 0;
+	int reset_tries = 0;
+	uint8_t status;
+	int dir;
+	i = 0;
+
+	if (cd64->using_ppa) {
+		status = inb((uint16_t) (cd64->port+1));
+
+		while(status & 0x80) {
+			i++;
+			if (i >= BUSY_THRESHOLD) {
+				/* The PPA is in a weird state.
+				 * Try to knock some sense into it. */
+				dir = 1;
+				status = 0x06 | (dir << 5);
+				outb(status, (uint16_t) (cd64->port+2));
+
+				dir = 0;
+				status = 0x04 | (dir << 5);
+				outb(status, (uint16_t) (cd64->port+2));
+				status = 0x05 | (dir << 5);
+				outb(status, (uint16_t) (cd64->port+2));
+				status = 0x04 | (dir << 5);
+				outb(status, (uint16_t) (cd64->port+2));
+
+				reset_tries++;
+				i = 0;
+				USLEEP(1);
+			}
+			if (cd64->abort) return 0;
+			if (reset_tries > MAX_TRIES) {
+				break;
+			}
+
+			status = inb((uint16_t) (cd64->port+1));
+		}
+	}
+	else { /* Comms link */
+		status = inb((uint16_t) (cd64->port+2));
+		while (status & 1) {
+			/* Do we need to handle a stuck situation here? */
+			status = inb((uint16_t) (cd64->port+2));
+		}
+	}
+
+	return (reset_tries < MAX_TRIES);
+}
+
+int cd64_xfer_rawio(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms) {
+
+	uint8_t ctl;
+	int dir;
+
+	if (cd64->using_ppa) {
+
+		if (!cd64_wait_rawio(cd64)) { return 0; }
+
+		if (delayms) USLEEP(delayms);
+		dir = 1;
+		ctl = 0x06 | (dir << 5);
+		outb(ctl, (uint16_t) (cd64->port+2));
+		if (delayms) USLEEP(delayms);
+		if (rd) {
+			*rd = inb((uint16_t) cd64->port);
+#if DEBUG_LOWLEVEL
+			printf("got %xh", *rd);
+			if (*rd > 0x20) printf(" (%c)", *rd);
+			printf("\n");
+#endif
+		}
+
+		if (delayms) USLEEP(delayms);
+		dir = 0;
+		ctl = 0x04 | (dir << 5);
+		outb(ctl, (uint16_t) (cd64->port+2));
+		if (delayms) USLEEP(delayms);
+		if (wr) {
+			outb(*wr, (uint16_t) cd64->port);
+#if DEBUG_LOWLEVEL
+			printf("put %xh", *wr);
+			if (*wr > 0x20) printf(" (%c)", *wr);
+			printf("\n");
+#endif
+		}
+		if (delayms) USLEEP(delayms);
+		ctl = 0x05 | (dir << 5);
+		outb(ctl, (uint16_t) (cd64->port+2));
+		if (delayms) USLEEP(delayms);
+		ctl = 0x04 | (dir << 5);
+		outb(ctl, (uint16_t) (cd64->port+2));
+	}
+	else { /* Comms link */
+		outb(*wr, (uint16_t) cd64->port);
+		if (!cd64_wait_rawio(cd64)) { return 0; }
+		*rd = inb((uint16_t) cd64->port);
+	}
+
+	return 1;
+}
+
+#endif

+ 114 - 0
ucon64/2.0/src/backup/libcd64/cd64io.h

@@ -0,0 +1,114 @@
+#ifndef __CD64IO_H__
+#define __CD64IO_H__
+
+#ifdef CD64_USE_LIBIEEE1284
+#include <ieee1284.h>
+int cd64_open_ieee1284(struct cd64_t *cd64);
+int cd64_close_ieee1284(struct cd64_t *cd64);
+int cd64_xfer_ieee1284(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms);
+#endif
+
+#ifdef CD64_USE_PPDEV
+#ifndef __linux__
+#error ppdev can only be used on Linux
+#endif
+#include <sys/ioctl.h>
+#include <linux/parport.h>
+#include <linux/ppdev.h>
+int cd64_open_ppdev(struct cd64_t *cd64);
+int cd64_close_ppdev(struct cd64_t *cd64);
+int cd64_xfer_ppdev(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms);
+#endif
+
+#ifdef CD64_USE_PORTDEV
+#ifndef __linux__
+#error portdev can only be used on Linux
+#endif
+int cd64_open_portdev(struct cd64_t *cd64);
+int cd64_close_portdev(struct cd64_t *cd64);
+int cd64_xfer_portdev(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms);
+#endif
+
+#ifdef CD64_USE_RAWIO
+/* #define REALLY_SLOW_IO */
+#if defined __linux__ && (defined __i386__ || defined __x86_64__)
+#include <sys/io.h>
+#endif
+#ifdef __OpenBSD__
+#include <sys/types.h>
+#include <machine/sysarch.h>
+#include <i386/pio.h>
+/* pio.h defines several I/O functions & macros, including the macros inb() and
+ * outb(). This shows that using a bit of inline assembly is not such a bad idea
+ * at all. */
+#undef inb
+#define inb(port) __inb(port)
+#undef outb
+#define outb(data, port) __outb(port, data)
+#endif
+#ifdef __FreeBSD__
+#include <fcntl.h>
+#include <machine/cpufunc.h>
+/* Almost the same story as under OpenBSD. cpufunc.h defines the macros inb()
+ * and outb(). We redefine them. Be sure _POSIX_SOURCE is not defined before
+ * including <machine/cpufunc.h>. */
+#undef inb
+#define inb(port) inbv(port)
+#undef outb
+#define outb(data, port) outbv(port, data)
+#endif
+#ifdef __BEOS__
+#include <fcntl.h>
+#endif
+#ifdef _MSC_VER
+#include <conio.h>                              /* inp() & outp() */
+#include <io.h>                                 /* access() */
+#define F_OK 0
+#endif
+#ifdef __MSDOS__
+#include <pc.h>                                 /* inportb() & outportb() */
+#endif
+#if defined _WIN32 || defined __CYGWIN__
+#include <windows.h>                            /* defines _WIN32 (checks for   */
+#include <string.h>                             /*  __CYGWIN__ must come first) */
+#ifdef __CYGWIN__
+#include <exceptions.h>
+#include <dlfcn.h>
+#include <sys/cygwin.h>
+#endif
+#endif /* _WIN32 || __CYGWIN__ */
+
+int cd64_open_rawio(struct cd64_t *cd64);
+int cd64_close_rawio(struct cd64_t *cd64);
+int cd64_xfer_rawio(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms);
+#endif
+
+#if defined _WIN32 && !defined __CYGWIN__
+/* milliseconds */
+#include <windows.h>
+#define USLEEP(x) Sleep(x)
+#elif defined __MSDOS__
+/* milliseconds */
+#include <dos.h>
+#define USLEEP(x) delay(x)
+#elif defined __BEOS__
+/* microseconds */
+#include <OS.h>
+#define USLEEP(x) snooze(x)
+#else                                           /* Unix & Cygwin */
+/* microseconds */
+#include <unistd.h>
+#define USLEEP(x) usleep(x)
+#endif
+
+#if __STDC_VERSION__ >= 19990L && !defined DEBUG
+/* If DEBUG is defined the keyword inline is not recognised (syntax error). */
+#define INLINE inline
+#elif defined _MSC_VER
+/* Visual C++ doesn't allow inline in C source code */
+#define INLINE __inline
+#else
+#define INLINE
+#endif
+
+#endif

+ 990 - 0
ucon64/2.0/src/backup/libcd64/cd64lib.c

@@ -0,0 +1,990 @@
+/*
+ *
+ * cd64lib.c
+ *
+ * Library routines for CD64 handling
+ *
+ * (c) 2004 Ryan Underwood
+ * Portions (c) 2004 Daniel Horchner (Win32, read/write/seek callbacks)
+ *
+ * May be distributed under the terms of the GNU Lesser/Library General
+ * Public License, or any later version of the same, as published by the Free
+ * Software Foundation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if defined __unix__ || defined __BEOS__
+#include <unistd.h>
+#endif
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+#include <sys/timeb.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include <ultra64/host/cd64lib.h>
+#include <ultra64/rom.h>
+#include <ultra64/cartmem.h>
+
+#include "cd64io.h"
+
+static uint8_t *cd64_tmp_buf;
+static uint32_t cd64_tmp_buf_offset;
+
+static int cd64_notice_helper(FILE *file, const char *format, va_list argptr) {
+
+	int n_chars;
+
+	fputs("libcd64: ", file);
+	n_chars = vfprintf(file, format, argptr);
+	fputc('\n', file);
+	fflush(file);
+
+	return n_chars;
+}
+
+int cd64_notice(const char *format, ...) {
+
+	va_list argptr;
+	int n_chars;
+
+	va_start(argptr, format);
+	n_chars = cd64_notice_helper(stdout, format, argptr);
+	va_end(argptr);
+
+	return n_chars;
+}
+
+int cd64_notice2(const char *format, ...) {
+
+	va_list argptr;
+	int n_chars;
+
+	va_start(argptr, format);
+	n_chars = cd64_notice_helper(stderr, format, argptr);
+	va_end(argptr);
+
+	return n_chars;
+}
+
+int cd64_read(void *io_id, void *buffer, uint32_t size) {
+	return fread(buffer, 1, size, (FILE *) io_id);
+}
+
+int cd64_write(void *io_id, void *buffer, uint32_t size) {
+	return fwrite(buffer, 1, size, (FILE *) io_id);
+}
+
+int32_t cd64_tell(void *io_id) {
+	return (int32_t) ftell((FILE *) io_id);
+}
+
+int cd64_seek(void *io_id, int32_t offset, int whence) {
+	return fseek((FILE *) io_id, offset, whence);
+}
+
+int cd64_create(struct cd64_t *cd64, method_t method,
+			uint16_t port, protocol_t protocol, int ppa) {
+
+	cd64->using_ppa = (ppa)? 1 : 0;
+	cd64->protocol = protocol;
+	cd64->port = port;
+
+	if (!cd64->notice_callback) cd64->notice_callback = cd64_notice;
+	if (!cd64->notice_callback2) cd64->notice_callback2 = cd64_notice2;
+
+	cd64->read_callback = cd64_read;
+	cd64->write_callback = cd64_write;
+	cd64->tell_callback = cd64_tell;
+	cd64->seek_callback = cd64_seek;
+
+#ifdef CD64_USE_LIBIEEE1284
+	if (method == LIBIEEE1284) {
+		cd64->devopen = cd64_open_ieee1284;
+		cd64->xfer = cd64_xfer_ieee1284;
+		cd64->devclose = cd64_close_ieee1284;
+		return 1;
+	}
+	else
+#endif
+
+#ifdef CD64_USE_PPDEV
+	if (method == PPDEV) {
+		cd64->devopen = cd64_open_ppdev;
+		cd64->xfer = cd64_xfer_ppdev;
+		cd64->devclose = cd64_close_ppdev;
+		return 1;
+	}
+	else
+#endif
+
+#ifdef CD64_USE_PORTDEV
+	if (method == PORTDEV) {
+		cd64->devopen = cd64_open_portdev;
+		cd64->xfer = cd64_xfer_portdev;
+		cd64->devclose = cd64_close_portdev;
+		return 1;
+	}
+	else
+#endif
+
+#ifdef CD64_USE_RAWIO
+	if (method == RAWIO) {
+		cd64->devopen = cd64_open_rawio;
+		cd64->xfer = cd64_xfer_rawio;
+		cd64->devclose = cd64_close_rawio;
+		return 1;
+	}
+	else
+#endif
+
+	{
+		switch (method) {
+			case LIBIEEE1284:
+				cd64->notice_callback2("libieee1284 not supported.");
+				break;
+			case PPDEV:
+				cd64->notice_callback2("ppdev not supported.");
+				break;
+			case PORTDEV:
+				cd64->notice_callback2("portdev not supported.");
+				break;
+			case RAWIO:
+				cd64->notice_callback2("rawio not supported.");
+				break;
+			default:
+				cd64->notice_callback2("unknown hw access method.");
+				break;
+		}
+		return 0;
+	}
+
+}
+
+/* CD64 BIOS routines */
+
+static int cd64_bios_sync(struct cd64_t *cd64) {
+
+	unsigned char ret1 = 0, ret2 = 0;
+
+	cd64->notice_callback("Waiting for CD64 comms to come online...");
+
+	while (!(ret1 == 'R' && ret2 == 'W')) {
+		if (cd64->abort) return 0;
+                /* approximation here */
+		cd64_send_byte(cd64, 'W');
+		cd64_send_byte(cd64, 'B');
+		cd64_trade_bytes(cd64, 'B', &ret1);
+		cd64_trade_bytes(cd64, 'B', &ret2);
+	}
+	return 1;
+}
+
+int cd64_bios_grab(struct cd64_t *cd64, void *io_id, uint32_t addr,
+		uint32_t length, int *elapsed) {
+
+	unsigned int i;
+	unsigned short pc_csum = 0, n64_csum = 0;
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+	struct timeb tb;
+#else
+	struct timeval tv;
+#endif
+	unsigned long int sec = 0, usec = 0;
+	uint8_t send, recv;
+	uint8_t cmd = 0xff;
+	uint8_t tmp;
+
+	if (!length || length&0x00000003 || addr&0x00000003) return 0;
+	if (addr <= 0xa03fffff && addr+length <= 0xa03fffff) {
+		cmd = BIOS_DUMP_N64;
+	}
+	else if (addr >= 0xa8000000 && addr <= 0xbfffffff
+			&& addr+length <= 0xbfffffff) {
+		cmd = BIOS_DUMP_PI;
+	}
+
+	if (cmd == 0xff) {
+		cd64->notice_callback2("Invalid memory range %lxh-%lxh for operation.",
+		                       (long unsigned int) addr, (long unsigned int) addr+length);
+		return 0;
+	}
+
+	/* Try to get in sync with the CD64
+	 * We use a delay here to give the PPA a chance to power up. */
+	send = 0xff;
+	cd64->xfer(cd64, &send, NULL, 1000);
+	cd64_bios_sync(cd64);
+
+	if (elapsed != NULL) {
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+		ftime(&tb);
+		sec = tb.time;
+		usec = tb.millitm*1000;
+#else
+		gettimeofday(&tv, 0);
+		sec = tv.tv_sec;
+		usec = tv.tv_usec;
+#endif
+	}
+
+	cd64_send_byte(cd64, cmd);
+	cd64_send_dword(cd64, addr);
+	cd64_send_dword(cd64, length);
+
+	/* dummy exchange, needed for some reason */
+	cd64_grab_byte(cd64, &recv);
+
+	for (i = 1; i <= length; i++) {
+
+		if (cd64->abort) return 0;
+		if (!cd64_grab_byte(cd64, &tmp)) return 0;
+		if (!cd64->write_callback(io_id, &tmp, 1)) {
+			cd64->notice_callback2("Error writing to output.");
+			return 0;
+		}
+		pc_csum += tmp;
+		if ((i % CD64_BUFFER_SIZE == 0) && cd64->progress_callback) {
+			cd64->progress_callback(i, length);
+		}
+	}
+
+	if (cd64->progress_callback) {
+		cd64->progress_callback(i, length);
+	}
+
+	if (elapsed != NULL) {
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+		ftime(&tb);
+		*elapsed = ((tb.time - sec)*1000000) + ((tb.millitm*1000) - usec);
+#else
+		gettimeofday(&tv, 0);
+		*elapsed = ((tv.tv_sec - sec)*1000000) + (tv.tv_usec - usec);
+#endif
+	}
+
+	pc_csum &= 0xfff;
+	cd64_trade_bytes(cd64, 0, &recv);
+	n64_csum = recv << 8;
+	cd64_trade_bytes(cd64, 0, &recv);
+	n64_csum += recv;
+	n64_csum &= 0xfff;
+
+/*	debug("\nVerifying checksum: pcsum = %d, n64sum = %d\n", pc_csum, n64_csum); */
+	if (pc_csum^n64_csum) return -1;
+	else return 1;
+}
+
+int cd64_bios_send(struct cd64_t *cd64, void *io_id, uint32_t addr,
+		uint32_t length, int *elapsed, int cmd) {
+
+	unsigned int i;
+	uint16_t pc_csum = 0;
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+	struct timeb tb;
+#else
+	struct timeval tv;
+#endif
+	unsigned long int sec = 0, usec = 0;
+	uint8_t send;
+	uint8_t buf[4];
+	int valid = 1;
+	uint8_t send_bogus_csum = 0;
+	uint8_t tmp;
+	int32_t pos;
+
+	if (!io_id || !length || length&0x00000003 || addr&0x00000003) return 0;
+	if (cmd != BIOS_TRANSFER_PI && cmd != BIOS_EXECUTE_PI && cmd != BIOS_TRANSFER_N64
+			&& !(cd64->protocol == GHEMOR && cmd == GHEMOR_TRANSFER_PROGRAM)) return 0;
+
+	if (cmd == BIOS_TRANSFER_PI || cmd == BIOS_EXECUTE_PI) {
+		if (addr < 0xa8000000 || addr > 0xbfffffff ||
+				addr+length > 0xbfffffff) valid = 0;
+	}
+	else if (cmd == BIOS_TRANSFER_N64) {
+		if (addr < 0xa0000000 || addr > 0xa03fffff
+				|| addr+length > 0xa03fffff) valid = 0;
+
+		if (addr != BIOS_TEMP_RAM || length > 0x80000) {
+			/* FIXME: is 0x80000 (512Kbit) really all the CD64
+			 * BIOS will copy from a mempak?  See if it copies
+			 * 1Mbit from a 4x linear card.  */
+
+			/* We are sending somewhere in RAM besides the CD64's
+			 * scratch area.  We will send a bogus checksum so the
+			 * CD64 doesn't try to run it or copy it to a mempak.
+			 */
+			send_bogus_csum = 1;
+		}
+	}
+
+	if (!valid) {
+		cd64->notice_callback2("Invalid address %lxh for operation.",
+		                       (long unsigned int) addr);
+		return 0;
+	}
+
+	if (cd64->protocol == GHEMOR && addr != 0xb4000000 &&
+			(cmd == BIOS_TRANSFER_PI || cmd == BIOS_EXECUTE_PI)) {
+		/* They might try to send to Ghemor in BIOS mode, but
+		 * oh well.  Warn them if we know it's Ghemor. */
+		cd64->notice_callback("Ignoring address %lxh != 0xb4000000 for Ghemor.",
+		                      (long unsigned int) addr);
+	}
+	if (cmd == GHEMOR_TRANSFER_PROGRAM) {
+		cd64->notice_callback("Ghemor ignores this command currently...");
+	}
+
+
+	/* Try to get in sync with the CD64
+	 * We use a delay here to give the PPA a chance to power up. */
+	send = 0xff;
+	cd64->xfer(cd64, &send, NULL, 1000);
+	cd64_bios_sync(cd64);
+
+	if (elapsed != NULL) {
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+		ftime(&tb);
+		sec = tb.time;
+		usec = tb.millitm*1000;
+#else
+		gettimeofday(&tv, 0);
+		sec = tv.tv_sec;
+		usec = tv.tv_usec;
+#endif
+	}
+
+	cd64_send_byte(cd64, (uint8_t) cmd);
+	cd64_send_dword(cd64, addr); /* Ghemor ignores this */
+	cd64_send_dword(cd64, length);
+
+	pos = cd64->tell_callback(io_id);
+
+	i = 0;
+	while (i < length) {
+
+		if (cd64->abort) return 0;
+		cd64->read_callback(io_id, &tmp, 1);
+		pc_csum += tmp;
+		pc_csum &= 0xfff;
+		if (!cd64_send_byte(cd64, tmp)) {
+			if (cd64->protocol == CD64BIOS) {
+				/* Probably the BIOS was stupid and dropped a
+				 * send as it likes to.  Try to recover from
+				 * a convenient point. */
+				while (i % 4 != 0) i--;
+				cd64->seek_callback(io_id, pos+i, SEEK_SET);
+				cd64->read_callback(io_id, &tmp, 1);
+				cd64->notice_callback("Trying to recover dropped send, waiting for CD64...");
+				cd64_bios_sync(cd64);
+				cd64_send_byte(cd64, (uint8_t) cmd);
+				cd64_send_dword(cd64, addr+i);
+				cd64_send_dword(cd64, length-i);
+
+				if (!cd64_send_byte(cd64, tmp)) {
+					/* Oh well, at least we tried. */
+					return -1;
+				}
+
+				/* Unfortunately we can only calculate a checksum
+				 * from _this_ point onward. */
+				pc_csum = tmp;
+
+				/* Now continue as if nothing ever happened. */
+			}
+			else {
+				cd64->notice_callback2("Send dropped, unable to recover.");
+				return 0;
+			}
+		}
+
+		if ((i % CD64_BUFFER_SIZE == 0) && cd64->progress_callback) {
+			cd64->progress_callback(i, length);
+		}
+		i++;
+	}
+	if (cd64->progress_callback) {
+		cd64->progress_callback(i, length);
+	}
+
+	if (elapsed != NULL) {
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+		ftime(&tb);
+		*elapsed = ((tb.time - sec)*1000000) + ((tb.millitm*1000) - usec);
+#else
+		gettimeofday(&tv, 0);
+		*elapsed = ((tv.tv_sec - sec)*1000000) + (tv.tv_usec - usec);
+#endif
+	}
+
+/*	debug("checksum: 0x%x\n",pc_csum) */
+	cd64_send_byte(cd64, (unsigned char)((pc_csum & 0xff00) >> 8));
+/*	debug("Sent checksum high byte: 0x%x\n",(unsigned char)pc_csum>>8); */
+	cd64_send_byte(cd64, (uint8_t) ((pc_csum & 0xff) + send_bogus_csum));
+/*	debug("Sent checksum low byte: 0x%x\n",pc_csum); */
+	cd64_grab_byte(cd64, &buf[2]);
+	cd64_trade_bytes(cd64, 0, &buf[0]);
+	cd64_trade_bytes(cd64, 0, &buf[1]);
+
+/*	debug("Got: (dummy) 0x%x,  0x%x (%c), 0x%x (%c)",buf[2], buf[0], buf[0], buf[1],buf[1]); */
+	if (buf[0]=='O' && buf[1]=='K') return 1;
+	else if (send_bogus_csum) {
+		cd64->notice_callback("No way to verify integrity of data in N64 RAM.");
+		return 1;
+	}
+	return -1;
+}
+
+int cd64_ghemor_grab(struct cd64_t *cd64, void *io_id, uint8_t slow, int *elapsed) {
+
+	int ret;
+	uint8_t tmp;
+	int sec = 0, usec = 0;
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+	struct timeb tb;
+#else
+	struct timeval tv;
+#endif
+	uint32_t len;
+	uint16_t mycsum = 0;
+	uint16_t cd64csum;
+	unsigned int i;
+
+	if (slow) {
+		cd64->notice_callback2("Ghemor slow receive feature not supported.");
+		return 0;
+	}
+	if (elapsed != NULL) {
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+		ftime(&tb);
+		sec = tb.time;
+		usec = tb.millitm*1000;
+#else
+		gettimeofday(&tv, 0);
+		sec = tv.tv_sec;
+		usec = tv.tv_usec;
+#endif
+	}
+
+	cd64_send_byte(cd64, slow);
+	i = 0;
+	while (cd64_grab_byte(cd64, &tmp) && tmp != 1) {
+		i++;
+		if (i > 25) {
+			cd64->notice_callback2("Ghemor was not ready.");
+			return 0;
+		}
+	}
+
+	cd64_grab_dword(cd64, &len);
+	cd64->notice_callback("Downloading %lu megabits via Ghemor.",
+	                      (long unsigned int) len/BYTES_IN_MBIT);
+	for (i = 0; i < len; i++) {
+		if (!cd64_grab_byte(cd64, &tmp)) return 0;
+		if (!cd64->write_callback(io_id, &tmp, 1)) {
+			cd64->notice_callback2("Error writing to output.");
+			return 0;
+		}
+		mycsum += tmp;
+		mycsum &= 0xfff;
+		if ((i % CD64_BUFFER_SIZE == 0) && cd64->progress_callback) {
+			cd64->progress_callback(i, len);
+		}
+	}
+	if (cd64->progress_callback) {
+		cd64->progress_callback(i, len);
+	}
+
+	cd64_grab_byte(cd64, &tmp);
+	cd64csum = tmp << 8;
+	cd64_grab_byte(cd64, &tmp);
+	cd64csum |= tmp;
+	if (mycsum^cd64csum) ret = -1;
+	else ret = 1;
+	if (elapsed != NULL) {
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+		ftime(&tb);
+		*elapsed = ((tb.time - sec)*1000000) + ((tb.millitm*1000) - usec);
+#else
+		gettimeofday(&tv, 0);
+		*elapsed = ((tv.tv_sec - sec)*1000000) + (tv.tv_usec - usec);
+#endif
+	}
+
+	return ret;
+}
+
+int cd64_ghemor_send(struct cd64_t *cd64, void *io_id, uint32_t length,
+		int *elapsed) {
+
+	int sec = 0, usec = 0;
+	uint16_t mycsum = 0;
+	unsigned int i;
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+	struct timeb tb;
+#else
+	struct timeval tv;
+#endif
+	uint8_t tmp;
+
+	if (elapsed != NULL) {
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+		ftime(&tb);
+		sec = tb.time;
+		usec = tb.millitm*1000;
+#else
+		gettimeofday(&tv, 0);
+		sec = tv.tv_sec;
+		usec = tv.tv_usec;
+#endif
+	}
+
+	cd64_send_byte(cd64, 0); /* No slow mode for sends */
+	i = 0;
+	while (cd64_grab_byte(cd64, &tmp) && tmp != 1) {
+		i++;
+		if (i > 25) {
+			cd64->notice_callback2("Ghemor was not ready.");
+			return 0;
+		}
+	}
+
+	cd64_send_dword(cd64, length);
+	for (i = 0; i < length; i++) {
+		if (!cd64->read_callback(io_id, &tmp, 1)) {
+			cd64->notice_callback2("Error reading from input.");
+			return 0;
+		}
+		if (!cd64_send_byte(cd64, tmp)) return 0;
+		mycsum += tmp;
+		mycsum &= 0xfff;
+		if ((i % CD64_BUFFER_SIZE == 0) && cd64->progress_callback) {
+			cd64->progress_callback(i, length);
+		}
+	}
+	if (cd64->progress_callback) {
+		cd64->progress_callback(i, length);
+	}
+	cd64_send_byte(cd64, (uint8_t)((mycsum << 8) & 0xff));
+	cd64_send_byte(cd64, (uint8_t)(mycsum & 0xff));
+	if (elapsed != NULL) {
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__
+		ftime(&tb);
+		*elapsed = ((tb.time - sec)*1000000) + ((tb.millitm*1000) - usec);
+#else
+		gettimeofday(&tv, 0);
+		*elapsed = ((tv.tv_sec - sec)*1000000) + (tv.tv_usec - usec);
+#endif
+	}
+
+	return 1;
+}
+
+
+/* Generic API functions */
+
+int cd64_upload_dram(struct cd64_t *cd64, FILE *source, uint32_t length,
+			int *elapsed, int exec) {
+
+	if (cd64->protocol == CD64BIOS || cd64->protocol == GHEMOR) {
+		int cmd;
+		if (exec == 1) cmd = BIOS_EXECUTE_PI;
+		else cmd = BIOS_TRANSFER_PI;
+
+		if (cd64->protocol == CD64BIOS && length == 0) {
+			cd64->notice_callback2("CD64 BIOS needs a file length.");
+			return 0;
+		}
+		if (cd64->protocol == CD64BIOS) {
+			cd64->notice_callback("Choose CD64 Tools->Pro Comms Link.");
+		}
+
+		return cd64_bios_send(cd64, source, 0xb4000000, length, elapsed, cmd);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_upload_bootemu(struct cd64_t *cd64, FILE *infile, uint32_t length, int *elapsed) {
+
+	if (cd64->protocol == CD64BIOS) {
+
+		if (cd64->protocol == CD64BIOS && length == 0) {
+			cd64->notice_callback2("CD64 BIOS needs a file length.\n");
+			return 0;
+		}
+
+		cd64->notice_callback("Choose CD64 Tools->Pro Comms Link.");
+		return cd64_bios_send(cd64, infile, BIOS_TEMP_RAM, length, elapsed,
+		                      BIOS_TRANSFER_N64);
+	}
+	else if (cd64->protocol == GHEMOR) {
+		cd64_bios_sync(cd64);
+		cd64_send_byte(cd64, GHEMOR_EXECUTE_BOOTEMU);
+		return cd64_ghemor_send(cd64, infile, length, elapsed);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_upload_ram(struct cd64_t *cd64, FILE *infile, uint32_t length,
+		int *elapsed, uint32_t address) {
+
+	if (cd64->protocol == CD64BIOS) {
+		cd64->notice_callback("Choose CD64 Tools->Pro Comms Link.");
+		return cd64_bios_send(cd64, infile, address, length,
+		                      elapsed, BIOS_TRANSFER_N64);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_upload_mempak(struct cd64_t *cd64, FILE *infile, int8_t which) {
+
+	int32_t len;
+	int32_t pos = cd64->tell_callback(infile);
+	cd64->seek_callback(infile, 0, SEEK_END);
+	len = cd64->tell_callback(infile);
+	cd64->seek_callback(infile, pos, SEEK_SET);
+	if (len != CONTROLLER_MEMPAK_LENGTH) {
+		cd64->notice_callback("Mempack file must be %d bytes, not %d.",
+		                      CONTROLLER_MEMPAK_LENGTH, len);
+	}
+
+	if (cd64->protocol == CD64BIOS) {
+		if (which != -1) {
+			cd64->notice_callback2("CD64 BIOS doesn't let mempak index be chosen.");
+			return 0;
+		}
+		cd64->notice_callback("Choose Memory Manager->Up/Download Pak.");
+		return cd64_bios_send(cd64, infile, BIOS_TEMP_RAM, CONTROLLER_MEMPAK_LENGTH,
+		                      NULL, BIOS_TRANSFER_N64);
+	}
+	else if (cd64->protocol == GHEMOR) {
+		cd64_bios_sync(cd64);
+		cd64_send_byte(cd64, GHEMOR_RESTORE_MEMPAK);
+		cd64_send_byte(cd64, which);
+		return cd64_ghemor_send(cd64, infile, CONTROLLER_MEMPAK_LENGTH, NULL);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_upload_sram(struct cd64_t *cd64, FILE *infile) {
+
+	if (cd64->protocol == CD64BIOS) {
+		cd64->notice_callback("Choose CD64 Tools->Pro Comms Link.");
+		return cd64_bios_send(cd64, infile, 0xa8000000, CART_SRAM_LENGTH,
+		                      NULL, BIOS_TRANSFER_PI);
+	}
+	else if (cd64->protocol == GHEMOR) {
+		cd64_bios_sync(cd64);
+		cd64_send_byte(cd64, GHEMOR_RESTORE_SRAM);
+		return cd64_ghemor_send(cd64, infile, CART_SRAM_LENGTH, NULL);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_upload_flashram(struct cd64_t *cd64, FILE *infile) {
+
+	/* Urm, we need to figure out if this really works.  Probably, CTR
+	 * needs to release a new Ghemor version.  Maybe it works with
+	 * CD64 BIOS but probably not. */
+
+	if (cd64->protocol == CD64BIOS) {
+		cd64->notice_callback("Choose CD64 Tools->Pro Comms Link.");
+		return cd64_bios_send(cd64, infile, 0xa8000000, CART_FLASHRAM_LENGTH,
+		                      NULL, BIOS_TRANSFER_PI);
+	}
+	else if (cd64->protocol == GHEMOR) {
+		cd64_bios_sync(cd64);
+		cd64_send_byte(cd64, GHEMOR_RESTORE_FLASHRAM);
+		return cd64_ghemor_send(cd64, infile, CART_FLASHRAM_LENGTH, NULL);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_upload_eeprom(struct cd64_t *cd64, FILE *infile) {
+
+	/* Check the size of the EEPROM data first */
+
+	int32_t origpos = cd64->tell_callback(infile);
+	int32_t length;
+
+	if (cd64->protocol == CD64BIOS) {
+		cd64->notice_callback2("CD64 BIOS can only transfer EEPROM through BRAM Manager.");
+		return 0;
+	}
+
+	cd64->seek_callback(infile, 0, SEEK_END);
+	length = cd64->tell_callback(infile);
+	cd64->seek_callback(infile, origpos, SEEK_SET);
+
+	if (length != CART_EEPROM_LENGTH && length != CART_2XEEPROM_LENGTH) {
+		cd64->notice_callback2("Wrong length of EEPROM data: %d bytes", (int) length);
+		return 0;
+	}
+	else if (cd64->protocol == GHEMOR) {
+		cd64_bios_sync(cd64);
+		cd64_send_byte(cd64, GHEMOR_RESTORE_EEPROM);
+		return cd64_ghemor_send(cd64, infile, length, NULL);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+static int cd64_write_mem(void *dummy, void *buffer, uint32_t size) {
+
+	(void) dummy;
+	memcpy (cd64_tmp_buf + cd64_tmp_buf_offset, buffer, size);
+	cd64_tmp_buf_offset += size;
+	return size;
+}
+
+int cd64_download_header(struct cd64_t *cd64, n64header_t *head, uint32_t location) {
+
+	if (cd64->protocol == CD64BIOS) {
+		int size = sizeof(n64header_t);
+		int ret;
+		int (*org_write_cb)(void *, void *, uint32_t) = cd64->write_callback;
+
+		while (size % 4 != 0) size++;
+		if (!head) return 0;
+		cd64_tmp_buf = (uint8_t *) head;
+		cd64_tmp_buf_offset = 0;
+		cd64->write_callback = cd64_write_mem;
+		ret = cd64_bios_grab(cd64, (void *) -1, location, size, NULL); /* -1 is just a random (non-zero) value */
+		cd64->write_callback = org_write_cb; /* restore original callback */
+		return 1;
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_download_cart(struct cd64_t *cd64, FILE *outfile, uint32_t length,
+			int *elapsed) {
+
+	if (cd64->protocol == CD64BIOS) {
+		int ret;
+		unsigned int i;
+		int32_t curpos = 0;
+		int32_t origpos = cd64->tell_callback(outfile);
+		if (length == 0) {
+			cd64->notice_callback2("CD64 BIOS needs a file length.");
+			return 0;
+		}
+		cd64->notice_callback("Choose CD64 Tools->Pro Comms Link.");
+
+		ret = cd64_bios_grab(cd64, outfile, 0xb2000000, length, elapsed);
+		/* Scan through the file at 8MBit intervals to
+		 * see if we overdumped.  If we did, truncate the
+		 * file.  */
+		i = 0;
+		cd64->seek_callback(outfile, origpos, SEEK_SET);
+		cd64->notice_callback("Checking for overdump...");
+		while (i < length) {
+			int j = 0;
+			int overdump = 1;
+			uint8_t buf[4];
+
+			curpos = cd64->tell_callback(outfile);
+
+			while(i+j < length) {
+				cd64->read_callback(outfile, &buf, 4);
+
+				/* To elaborate on what we are checking here:
+				 * When the CD64 accesses an address which is not
+				 * decoded, in each 32-bit word is the lower 16 bits
+				 * of the address of that 32-bit word, repeated twice.
+				 * The pattern therefore looks like:
+				 * 00 00 00 00 00 04 00 04 00 08 00 08 00 0c 00 0c
+				 * and continues on like that.  This pattern is what
+				 * we are looking for here.  It is possible, but
+				 * extremely unlikely, that this pattern appears in a
+				 * actual game and begins on a 8Mbit boundary too. */
+
+				if (
+					   ((uint8_t*)buf)[0] != ((j >> 8) & 0xff)
+					|| ((uint8_t*)buf)[1] != (j & 0xff)
+					|| ((uint8_t*)buf)[2] != ((j >> 8) & 0xff)
+					|| ((uint8_t*)buf)[3] != (j & 0xff)
+				) {
+					overdump = 0;
+					break;
+				}
+				else {
+					j+=4;
+				}
+			}
+
+			if (overdump) {
+				break;
+			}
+			i += 0x100000;
+			cd64->seek_callback(outfile, curpos+0x100000, SEEK_SET);
+		}
+
+		if (i < length) {
+			cd64->notice_callback("File apparently overdumped.");
+#if (!defined _WIN32 || defined __CYGWIN__)
+			/* Don't call ftruncate() if the user installed a callback, because
+			 * outfile may not be a real FILE *. */
+			if (cd64->read_callback == cd64_read) {
+				cd64->notice_callback("Truncating to %dMbits.", i/BYTES_IN_MBIT);
+				ftruncate(fileno(outfile), curpos+i);
+			}
+#endif
+		}
+		return ret;
+	}
+	else if (cd64->protocol == GHEMOR) {
+		cd64_bios_sync(cd64);
+		cd64_send_byte(cd64, GHEMOR_DUMP_CART);
+		return cd64_ghemor_grab(cd64, outfile, 0, elapsed);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_download_dram(struct cd64_t *cd64, FILE *outfile, uint32_t start,
+			uint32_t end, int *elapsed) {
+
+	if (cd64->protocol == CD64BIOS) {
+		cd64->notice_callback("Choose CD64 Tools->Pro Comms Link.");
+		return cd64_bios_grab(cd64, outfile, 0xb4000000, end-start, elapsed);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_download_sram(struct cd64_t *cd64, FILE *outfile) {
+
+	if (cd64->protocol == CD64BIOS) {
+		cd64->notice_callback("Choose CD64 Tools->Pro Comms Link.");
+		return cd64_bios_grab(cd64, outfile, CART_SRAM_ADDR, CART_SRAM_LENGTH, NULL);
+	}
+	else if (cd64->protocol == GHEMOR) {
+		cd64_bios_sync(cd64);
+		cd64_send_byte(cd64, GHEMOR_DUMP_SRAM);
+		return cd64_ghemor_grab(cd64, outfile, 0, NULL);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_download_flashram(struct cd64_t *cd64, FILE *outfile) {
+
+	/* We might be able to support CD64 BIOS here.  Needs testing. */
+	if (cd64->protocol == GHEMOR) {
+		cd64_bios_sync(cd64);
+		cd64_send_byte(cd64, GHEMOR_DUMP_FLASH);
+		return cd64_ghemor_grab(cd64, outfile, 0, NULL);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_download_eeprom(struct cd64_t *cd64, FILE *outfile) {
+
+	if (cd64->protocol == GHEMOR) {
+		cd64_bios_sync(cd64);
+		cd64_send_byte(cd64, GHEMOR_DUMP_EEPROM);
+		return cd64_ghemor_grab(cd64, outfile, 0, NULL);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_download_mempak(struct cd64_t *cd64, FILE *outfile, int8_t which) {
+
+	if (cd64->protocol == CD64BIOS) {
+		if (which != -1) {
+			cd64->notice_callback2("CD64 BIOS doesn't let mempak index be chosen.");
+			return 0;
+		}
+		cd64->notice_callback("Choose Memory Manager->Up/Download Pak.");
+		return cd64_bios_grab(cd64, outfile, BIOS_TEMP_RAM, CONTROLLER_MEMPAK_LENGTH, NULL);
+	}
+	else if (cd64->protocol == GHEMOR) {
+		cd64_bios_sync(cd64);
+		cd64_send_byte(cd64, GHEMOR_DUMP_MEMPAK);
+		cd64_send_byte(cd64, which);
+		return cd64_ghemor_grab(cd64, outfile, 0, NULL);
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+static int cd64_read_mem(void *dummy, void *buffer, uint32_t size) {
+
+	(void) dummy;
+	memcpy (buffer, cd64_tmp_buf + cd64_tmp_buf_offset, size);
+	cd64_tmp_buf_offset += size;
+	return size;
+}
+
+static int32_t cd64_tell_mem(void *dummy) {
+
+	(void) dummy;
+	return cd64_tmp_buf_offset;
+}
+
+static int cd64_seek_mem(void *dummy, int32_t offset, int whence) {
+
+	(void) dummy;
+	(void) whence; /* only called with SEEK_SET */
+	cd64_tmp_buf_offset = offset;
+	return 0;
+}
+
+int cd64_run_dram(struct cd64_t *cd64) {
+
+	if (cd64->protocol == GHEMOR) {
+		cd64_bios_sync(cd64);
+		cd64_send_byte(cd64, GHEMOR_RESET_DRAM);
+		return 1;
+	}
+	else if (cd64->protocol == CD64BIOS) {
+		/* Heh. Write some dummy bytes to the cart area. We
+		 * can't just send a zero length because the CD64
+		 * BIOS gives "File length error". */
+		uint8_t dummy[4] = { 0, 0, 0, 0 };
+		int ret;
+		int (*org_read_cb)(void *, void *, uint32_t) = cd64->read_callback;
+		int32_t (*org_tell_cb)(void *) = cd64->tell_callback;
+		int (*org_seek_cb)(void *, int32_t, int) = cd64->seek_callback;
+
+		cd64->notice_callback("Choose CD64 Tools->Pro Comms Link.");
+		cd64_tmp_buf = dummy;
+		cd64_tmp_buf_offset = 0;
+		cd64->read_callback = cd64_read_mem;
+		cd64->tell_callback = cd64_tell_mem;
+		cd64->seek_callback = cd64_seek_mem;
+		ret = cd64_bios_send(cd64, (void *) -1, 0xb2000000, 4, NULL, BIOS_EXECUTE_PI); /* -1 is just a random (non-zero) value */
+		cd64->read_callback = org_read_cb; /* restore original callbacks */
+		cd64->tell_callback = org_tell_cb;
+		cd64->seek_callback = org_seek_cb;
+		return ret;
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}
+
+int cd64_run_cart(struct cd64_t *cd64) {
+
+	if (cd64->protocol == GHEMOR) {
+		cd64_bios_sync(cd64);
+		cd64_send_byte(cd64, GHEMOR_RESET_CART);
+		return 1;
+	}
+	cd64->notice_callback2("Operation not supported by protocol.");
+	return 0;
+}

+ 13 - 0
ucon64/2.0/src/backup/libcd64/ultra64/cartmem.h

@@ -0,0 +1,13 @@
+#ifndef __CARTMEM_H__
+#define __CARTMEM_H__
+
+#define CART_EEPROM_LENGTH 0x200 /* 512 bytes */
+#define CART_2XEEPROM_LENGTH 0x800 /* 2048 bytes */
+#define CART_SRAM_LENGTH 0x8000  /* 32KB */
+#define CART_FLASHRAM_LENGTH 0x20000  /* 128KB */
+#define CONTROLLER_MEMPAK_LENGTH 0x8000 /* 32KB */
+
+#define CART_SRAM_ADDR 0xa8000000
+#define CART_FLASHRAM_ADDR CART_SRAM_ADDR
+
+#endif

+ 8 - 0
ucon64/2.0/src/backup/libcd64/ultra64/host/cartinfo.h

@@ -0,0 +1,8 @@
+#ifndef __ULTRA64__HOST__CARTINFO_H__
+#define __ULTRA64__HOST__CARTINFO_H__
+
+#include <ultra64/rom.h>
+
+void ultra64_header_info(n64header_t *carthead);
+
+#endif

+ 189 - 0
ucon64/2.0/src/backup/libcd64/ultra64/host/cd64lib.h

@@ -0,0 +1,189 @@
+#ifndef __CD64LIB_H__
+#define __CD64LIB_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CD64_BUFFER_SIZE 32768
+
+/* This is the only public header file for cd64lib. */
+
+#if __STDC_VERSION >= 19990L
+#include <stdint.h>
+#else
+#if !(defined __MSDOS__ || defined _MSC_VER)
+#include <inttypes.h>
+#else
+#ifndef OWN_INTTYPES
+#define OWN_INTTYPES /* signal that these are defined */
+typedef unsigned char uint8_t;
+typedef unsigned short int uint16_t;
+typedef unsigned int uint32_t;
+#ifndef _MSC_VER /* _WIN32 */
+typedef unsigned long long int uint64_t;
+#else
+typedef unsigned __int64 uint64_t;
+#endif
+typedef signed char int8_t;
+typedef signed short int int16_t;
+typedef signed int int32_t;
+#ifndef _MSC_VER /* _WIN32 */
+typedef signed long long int int64_t;
+#else
+typedef signed __int64 int64_t;
+#endif
+#endif /* OWN_INTTYPES */
+#endif /* __MSDOS__ || _MSC_VER */
+#endif /* STDC_VERSION */
+
+#include <stdio.h> /* FILE, FILENAME_MAX */
+#include <ultra64/rom.h>
+
+typedef enum {
+	CD64BIOS = 0,
+	GHEMOR = 1,
+	ULTRALINK = 2
+} protocol_t;
+
+typedef enum {
+	LIBIEEE1284 = 1,
+	PPDEV = 2,
+	PORTDEV = 3,
+	RAWIO = 4
+} method_t;
+
+/* When using this structure, be sure to calloc it or otherwise set it to
+ * zero before setting values or calling library functions. */
+
+struct cd64_t {
+	int using_ppa;
+	protocol_t protocol;
+
+	struct parport *ppdev;	/* libieee1284 */
+	int ppdevfd;		/* ppdev */
+	int portdevfd;		/* /dev/port */
+
+	/* If using inb/outb or /dev/port, this is the I/O address
+	 * Otherwise it is the parport* number */
+	int port;
+
+	/* Directory with io.dll or dlportio.dll. Used by the Windows ports. */
+	char io_driver_dir[FILENAME_MAX];
+
+	/* A flag that can be set/read to determine whether
+	 * the current operation should be canceled. */
+	int abort;
+
+	int (*devopen)(struct cd64_t *cd64);
+	int (*xfer)(struct cd64_t *cd64, uint8_t *write, uint8_t *read, int delayms);
+	int (*devclose)(struct cd64_t *cd64);
+
+	/* Progress callback is responsible for printing header info if the
+	 * user wants it */
+
+	void (*progress_callback)(uint32_t curbyte, uint32_t totalbytes);
+	int (*notice_callback)(const char *format, ...);
+	int (*notice_callback2)(const char *format, ...);
+
+	/* Callbacks for read, write and seek operations. By default they point to
+	 * callbacks in the library which just call fread(), fwrite(), ftell() and
+	 * fseek(). You can change them so that the library doesn't read from or
+	 * write to a FILE * (io_id). For example, a client can install its own
+	 * callback to make it possible to read from .zip files. */
+	int (*read_callback)(void *io_id, void *buffer, uint32_t size);
+	int (*write_callback)(void *io_id, void *buffer, uint32_t size);
+	int32_t (*tell_callback)(void *io_id);
+	int (*seek_callback)(void *io_id, int32_t offset, int whence);
+};
+
+/* This function must be called and return successful before any of the
+ * other functions may be used. */
+
+int cd64_create(struct cd64_t *cd64, method_t method,
+			uint16_t port, protocol_t protocol, int ppa);
+
+/* The following five functions are wrappers above the I/O abstraction.
+ * Use them to write code that works regardless of the underlying
+ * transport. */
+
+int cd64_send_byte(struct cd64_t *cd64, uint8_t what);
+int cd64_send_dword(struct cd64_t *cd64, uint32_t what);
+int cd64_grab_byte(struct cd64_t *cd64, uint8_t *val);
+int cd64_grab_dword(struct cd64_t *cd64, uint32_t *val);
+int cd64_trade_bytes(struct cd64_t *cd64, uint8_t give, uint8_t *recv);
+
+/* Generic protocol handlers */
+
+int cd64_bios_grab(struct cd64_t *cd64, void *io_id, uint32_t addr, uint32_t length,
+		                int *elapsed);
+int cd64_bios_send(struct cd64_t *cd64, void *io_id, uint32_t addr,
+		                uint32_t length, int *elapsed, int cmd);
+
+int cd64_ghemor_grab(struct cd64_t *cd64, void *io_id, uint8_t slow, int *elapsed);
+int cd64_ghemor_send(struct cd64_t *cd64, void *io_id, uint32_t length,
+		                int *elapsed);
+
+/* Functions for sending files to CD64 */
+int cd64_upload_dram(struct cd64_t *cd64, FILE *infile, uint32_t length,
+		                        int *elapsed, int exec);
+int cd64_upload_ram(struct cd64_t *cd64, FILE *infile, uint32_t length,
+		                        int *elapsed, uint32_t address);
+
+int cd64_upload_bootemu(struct cd64_t *cd64, FILE *infile, uint32_t length, int *elapsed);
+
+int cd64_upload_sram(struct cd64_t *cd64, FILE *infile);
+int cd64_upload_flashram(struct cd64_t *cd64, FILE *infile);
+int cd64_upload_eeprom(struct cd64_t *cd64, FILE *infile);
+int cd64_upload_mempak(struct cd64_t *cd64, FILE *infile, int8_t which);
+
+/* Functions for receiving files from CD64 */
+int cd64_download_cart(struct cd64_t *cd64, FILE *outfile, uint32_t length,
+		                        int *elapsed);
+int cd64_download_dram(struct cd64_t *cd64, FILE *outfile, uint32_t start,
+		                        uint32_t end, int *elapsed);
+int cd64_download_ram(struct cd64_t *cd64, FILE *outfile, uint32_t length,
+		                        int *elapsed, uint32_t address);
+
+int cd64_download_sram(struct cd64_t *cd64, FILE *outfile);
+int cd64_download_flashram(struct cd64_t *cd64, FILE *outfile);
+int cd64_download_eeprom(struct cd64_t *cd64, FILE *outfile);
+int cd64_download_mempak(struct cd64_t *cd64, FILE *outfile, int8_t which);
+
+/* Remote control functions */
+int cd64_run_dram(struct cd64_t *cd64);
+int cd64_run_cart(struct cd64_t *cd64);
+
+/* This function simply gets the header from the cart and can be displayed
+ * using ultra64_header_info() */
+
+int cd64_download_header(struct cd64_t *cd64, n64header_t *head, uint32_t location);
+
+#ifdef __cplusplus
+}
+#endif
+
+#define BIOS_TEMP_RAM 0xa0300000
+
+#define BIOS_DUMP_N64 'D'
+#define BIOS_TRANSFER_N64 'B'
+
+#define BIOS_DUMP_PI 'G'
+#define BIOS_TRANSFER_PI 'T'
+#define BIOS_EXECUTE_PI 'X'
+
+#define GHEMOR_RESTORE_MEMPAK 1
+#define GHEMOR_RESTORE_EEPROM 2
+#define GHEMOR_RESTORE_SRAM 3
+#define GHEMOR_RESTORE_FLASHRAM 4
+#define GHEMOR_EXECUTE_BOOTEMU 5
+#define GHEMOR_TRANSFER_PROGRAM 6
+#define GHEMOR_DUMP_CART 7
+#define GHEMOR_DUMP_MEMPAK 8
+#define GHEMOR_DUMP_EEPROM 9
+#define GHEMOR_DUMP_SRAM 10
+#define GHEMOR_DUMP_FLASH 11
+#define GHEMOR_RESET_DRAM 12
+#define GHEMOR_RESET_CART 13
+
+#endif

+ 60 - 0
ucon64/2.0/src/backup/libcd64/ultra64/rom.h

@@ -0,0 +1,60 @@
+
+#ifndef __ROM_H__
+#define __ROM_H__
+
+/* Based on Daedalus header */
+
+#define N64HEADER_SIZE 0x40
+#define BYTES_IN_MBIT 0x20000
+
+#define SwapEndian(x) \
+	  ((x >> 24)&0x000000FF) \
+	| ((x >> 8 )&0x0000FF00) \
+	| ((x << 8 )&0x00FF0000) \
+	| ((x << 24)&0xFF000000)
+
+typedef enum {
+	UNKNOWN = 0,
+	EEP4K = 1,
+	EEP16K = 2,
+	SRAM = 3,
+	FLASHRAM = 4
+} savetype_t;
+
+typedef struct {	/* From Daedalus */
+	unsigned char x1; /* initial PI_BSB_DOM1_LAT_REG value */
+	unsigned char x2; /* initial PI_BSB_DOM1_PGS_REG value */
+	unsigned char x3; /* initial PI_BSB_DOM1_PWD_REG value */
+	unsigned char x4; /* initial PI_BSB_DOM1_RLS_REG value */
+
+	unsigned long int ClockRate;
+	unsigned long int BootAddress;
+	unsigned long int Release;
+	unsigned long int CRC1;
+	unsigned long int CRC2;
+	unsigned long int Unknown0;
+	unsigned long int Unknown1;
+	char Name[20];
+	unsigned long int Unknown2;
+	unsigned short int Unknown3;
+	unsigned char Unknown4;
+	unsigned char Manufacturer;
+	unsigned short int CartID;
+	char CountryID;
+	unsigned char Unknown5;
+} n64header_t;
+
+typedef enum {
+	OS_TV_NTSC = 0,
+	OS_TV_PAL,
+	OS_TV_MPAL
+} tv_type_t;
+
+typedef struct
+{
+	char nCountryID;
+	char szName[15];
+	unsigned long int nTvType;
+} CountryIDInfo_t;
+
+#endif

+ 1055 - 0
ucon64/2.0/src/backup/lynxit.c

@@ -0,0 +1,1055 @@
+/*
+lynxit.c - lynxit support for uCON64
+
+Copyright (c) 1997 - ???? K. Wilkins
+Copyright (c) 2002        NoisyB <noisyb@gmx.net>
+Copyright (c) 2004        dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef  HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "misc/misc.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "console/lynx.h"
+#include "lynxit.h"
+#include "misc/parallel.h"
+
+
+const st_getopt2_t lynxit_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Lynxit (Lynx cartridge backup board)"/*"1997 K.Wilkins (selfmade)"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xlit", 0, 0, UCON64_XLIT,
+      NULL, "receive ROM from Lynxit interface; " OPTION_LONG_S "port=PORT",
+//    "                  receives automatically when ROM does not exist\n"
+      &ucon64_wf[WF_OBJ_LYNX_STOP_NO_ROM]
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef USE_PARALLEL
+
+#define MAGIC_STRING           "LYNX"
+#define FILE_FORMAT_VERSION    0x0001
+
+
+//
+// Some basic cartidge definitions
+//
+
+#define MAX_PAGE_SIZE  0x1000   // Must be 2x largest page
+
+#define CART_PAGE_64K  0x100    // 256 Bytes per page
+#define CART_PAGE_128K 0x200    // 512 Bytes per page
+#define CART_PAGE_256K 0x400    // 1024 Bytes per page
+#define CART_PAGE_512K 0x800    // 2048 Bytes per page
+
+#define BANK0  0
+#define BANK1  1
+
+
+//
+// DEFINE CENTRONICS PORT MASKS & PORTS
+//
+
+#define    PORT_BASE   0x80     // Keep the power on 8-)
+#define    CTRL_STB    0x01
+#define    DATA_OUT    0x02
+#define    DATA_CLK    0x04
+#define    DATA_STB    0x08
+#define    DATA_OE     0x10
+#define    DATA_LOAD   0x20
+#define    PAGE_STB    0x40
+
+unsigned int printer_port = 1, print_data = 0, print_ctrl = 0, print_stat = 0;
+char cartname[32], manufname[16];
+
+//
+// CONTROL REGISTER DEFINITIONS
+//
+
+
+#define    CTRL_BANK0B     0x01
+#define    CTRL_BANK1B     0x02
+#define    CTRL_WR_EN      0x04
+#define    CTRL_CTR_CLKB   0x40
+#define    CTRL_CTR_RST    0x80
+
+#define    CTRL_INACTIVE   (CTRL_BANK0B|CTRL_BANK1B|CTRL_CTR_CLKB)
+
+
+// Make cart strobes inactive & counter clock & reset inactive 
+
+unsigned char control_register = CTRL_INACTIVE;
+static void lynxit_write_control (unsigned char data);
+
+
+//
+// Global control vars
+//
+
+static int debug = FALSE, quiet = FALSE, verify = TRUE;
+
+
+#define  MESSAGE(body) printf body
+
+#define  INPUT(port)        inportb((unsigned short) (port))
+#define  OUTPUT(port, data) outportb((unsigned short) (port), (unsigned char) (data))
+
+
+#if 0
+void
+usage (void)
+{
+  MESSAGE (("\nUsage: lynxit [-pX] [-d] [-q] <command> <filename> [cartname] [manuf]\n"));
+  MESSAGE (("\n"));
+  MESSAGE (("   Commands = read/write/verify/test\n"));
+  MESSAGE (("        -pX = Use printer port LPTX: (Default LPT1:)\n"));
+  MESSAGE (("        -d  = Debug mode enable\n"));
+  MESSAGE (("        -q  = Quiet mode\n"));
+  MESSAGE (("        -n  = Don't verify read/write operations\n"));
+  MESSAGE (("        -h  = Print this text\n"));
+}
+#endif
+
+
+int
+ptr_port_init (unsigned int port)
+{
+#if 0
+  if (port < 1 && port > 4)
+    return FALSE;
+
+  print_data = *(unsigned int far *) MK_FP (0x0040, 6 + (2 * port));
+
+  if (!print_data)
+    return FALSE;
+#else
+  (void) port;                                  // warning remover
+#endif
+  print_stat = print_data + 1;
+  print_ctrl = print_data + 2;
+
+#if 0
+  DEBUG (("\nPrinter port initialised OK: LPT%d\n", port));
+  DEBUG (("Data    I/O 0x%04x\n", print_data));
+  DEBUG (("Status  I/O 0x%04x\n", print_stat));
+  DEBUG (("Control I/O 0x%04x\n\n", print_ctrl));
+#endif
+
+  lynxit_write_control (control_register);
+
+  return TRUE;
+}
+
+
+void
+lynxit_shift_out_byte (unsigned char data)
+{
+  unsigned int loop;
+  unsigned char outbyte, dbgdata;
+
+  dbgdata = data;
+
+  OUTPUT (print_data, PORT_BASE);       // Set inactive
+
+  for (loop = 0; loop < 8; loop++)
+    {
+      outbyte = PORT_BASE;
+      outbyte |= (data & 0x80) ? DATA_OUT : 0;
+      OUTPUT (print_data, outbyte);     // Output data; clock low
+      data = data << 1;
+      outbyte |= DATA_CLK;
+      OUTPUT (print_data, outbyte);     // clock high
+    }
+
+  OUTPUT (print_data, PORT_BASE);       // Leave outputs low
+
+#if 0
+  DEBUG (("lynxit_shift_out_byte() - Wrote %02x\n", dbgdata));
+#endif
+}
+
+
+unsigned char
+lynxit_shift_in_byte (void)
+{
+  unsigned int loop;
+  unsigned char data = 0;
+
+  OUTPUT (print_data, PORT_BASE);       // Set inactive
+
+  for (loop = 0; loop < 8; loop++)
+    {
+      data |= (((INPUT (print_stat)) & 0x80) ? 0 : 1) << (7 - loop);
+#if 0
+      DEBUG (("Status port returned %02x\n", INPUT (print_stat)));
+#endif
+      OUTPUT (print_data, PORT_BASE | DATA_CLK);        // clock high
+      OUTPUT (print_data, PORT_BASE);   // clock low
+    }
+
+#if 0
+  DEBUG (("lynxit_shift_in_byte() - Read %02x\n", data));
+#endif
+  return (data);
+}
+
+
+void
+lynxit_write_control (unsigned char data)
+{
+#if 0
+  DEBUG (("lynxit_write_control()  - Set to %02x\n", data));
+#endif
+  lynxit_shift_out_byte (data);
+
+  OUTPUT (print_data, PORT_BASE);       // clock low; strobe low
+  OUTPUT (print_data, PORT_BASE | CTRL_STB);    // clock low; strobe high
+  OUTPUT (print_data, PORT_BASE);       // clock low; strobe low
+
+  control_register = data;
+}
+
+
+void
+lynxit_write_page (unsigned char page)
+{
+#if 0
+  DEBUG (("lynxit_write_page()  - Set to %02x\n", page));
+#endif
+  lynxit_shift_out_byte (page);
+  lynxit_shift_out_byte (0);
+
+  OUTPUT (print_data, PORT_BASE);       // clock low; strobe low
+  OUTPUT (print_data, PORT_BASE | PAGE_STB);    // clock low; strobe high
+  OUTPUT (print_data, PORT_BASE);       // clock low; strobe low
+}
+
+
+void
+lynxit_counter_reset (void)
+{
+  lynxit_write_control ((unsigned char) (control_register | CTRL_CTR_RST));
+  lynxit_write_control ((unsigned char) (control_register & (CTRL_CTR_RST ^ 0xff)));
+}
+
+
+void
+lynxit_counter_increment (void)
+{
+  lynxit_write_control ((unsigned char) (control_register & (CTRL_CTR_CLKB ^ 0xff)));
+  lynxit_write_control ((unsigned char) (control_register | CTRL_CTR_CLKB));
+}
+
+
+unsigned char
+cart_read_byte (unsigned int cart)
+{
+  unsigned char data;
+
+  // Clear relevant cart strobe to activate read
+
+  if (cart == BANK0)
+    {
+      lynxit_write_control ((unsigned char) (control_register & (0xff ^ CTRL_BANK0B)));
+    }
+  else
+    {
+      lynxit_write_control ((unsigned char) (control_register & (0xff ^ CTRL_BANK1B)));
+    }
+
+  // Clock byte into shift register with load
+  //
+  // Note 500ns read cycle for ROM == 5x125ns out cycles
+
+
+  OUTPUT (print_data, PORT_BASE);       // clock low
+  OUTPUT (print_data, PORT_BASE);       // clock low
+  OUTPUT (print_data, PORT_BASE);       // clock low
+  OUTPUT (print_data, PORT_BASE);       // clock low
+  OUTPUT (print_data, PORT_BASE | DATA_LOAD);   // Parallel load
+  OUTPUT (print_data, PORT_BASE | DATA_LOAD);   // Parallel load
+  OUTPUT (print_data, PORT_BASE);       // Clear load
+
+  // This must be done before strobe is cleared as data will be
+  // destoryed bye setting control reg
+
+  data = lynxit_shift_in_byte ();
+
+  // Clear the cartridge strobe
+
+  if (cart == BANK0)
+    {
+      lynxit_write_control ((unsigned char) (control_register | CTRL_BANK0B));
+    }
+  else
+    {
+      lynxit_write_control ((unsigned char) (control_register | CTRL_BANK1B));
+    }
+
+#if 0
+  DEBUG (("cart_read_byte() - Returning %02x\n", data));
+#endif
+
+//   MESSAGE(("%c",data));
+
+  return (data);
+}
+
+
+void
+cart_write_byte (unsigned int cart, unsigned char data)
+{
+#if 0
+  DEBUG (("cart_write_byte() - Set to %02x\n", data));
+#endif
+
+  // Shift data to correct position
+
+  lynxit_shift_out_byte (data);
+  lynxit_shift_out_byte (0);
+  lynxit_shift_out_byte (0);
+
+  // Strobe byte to be written into the data register
+
+  OUTPUT (print_data, PORT_BASE);       // clock low; strobe low
+  OUTPUT (print_data, PORT_BASE | DATA_STB);    // clock low; strobe high
+  OUTPUT (print_data, PORT_BASE);       // clock low; strobe low
+
+  // Assert write enable (active low)
+
+  lynxit_write_control ((unsigned char) (control_register & (0xff ^ CTRL_WR_EN)));
+
+  // Assert output enable
+
+  OUTPUT (print_data, PORT_BASE | DATA_OE);
+
+  // Assert cartridge strobe LOW to write then HIGH
+
+  if (cart == BANK0)
+    {
+      lynxit_write_control ((unsigned char) (control_register & (0xff ^ CTRL_BANK0B)));
+      lynxit_write_control ((unsigned char) (control_register | CTRL_BANK0B));
+    }
+  else
+    {
+      lynxit_write_control ((unsigned char) (control_register & (0xff ^ CTRL_BANK1B)));
+      lynxit_write_control ((unsigned char) (control_register | CTRL_BANK1B));
+    }
+
+  // Clear output enable
+
+  OUTPUT (print_data, PORT_BASE);
+
+  // Clear write enable
+
+  lynxit_write_control ((unsigned char) (control_register | CTRL_WR_EN));
+
+}
+
+
+void
+cart_read_page (unsigned int cart, unsigned int page_number,
+                unsigned int page_size, unsigned char *page_ptr)
+{
+  unsigned int loop;
+
+  lynxit_write_page ((unsigned char) page_number);
+  lynxit_counter_reset ();
+
+  for (loop = 0; loop < page_size; loop++)
+    {
+      *page_ptr = cart_read_byte (cart);
+      page_ptr++;
+      lynxit_counter_increment ();
+    }
+}
+
+
+#if 0
+void
+cart_write_page (unsigned int cart, unsigned int page_number,
+                 unsigned int page_size, unsigned char *page_ptr)
+{
+}
+#endif
+
+
+int
+cart_analyse (int cart)
+{
+  unsigned char image[MAX_PAGE_SIZE];
+  unsigned int page = 0;
+  unsigned char test = 0;
+  unsigned int loop = 0;
+
+  MESSAGE (("ANALYSE  : BANK%d ", cart));
+
+  for (;;)
+    {
+      // Read a page - start at zero, try a max of 8 pages
+
+      if (page > 8)
+        {
+          MESSAGE (("N/A\n"));
+          return FALSE;
+        }
+
+      cart_read_page (cart, page++, CART_PAGE_512K * 2, image);
+
+      // Explicit check for no bank
+
+      test = 0xff;
+      for (loop = 0; loop < CART_PAGE_512K; loop++)
+        test &= image[loop];
+
+      if (test == 0xff)
+        {
+          MESSAGE (("N/A\n"));
+          return FALSE;
+        }
+
+      // Check bytes are not all same
+
+      for (loop = 1; loop < CART_PAGE_512K; loop++)
+        {
+          if (image[loop] != image[0])
+            break;
+        }
+
+      // If we are at end of loop then buffer is all same
+
+      if (loop != CART_PAGE_512K)
+        break;
+    }
+
+//   {
+//       FILE *fp;
+//       fp=fopen("TEST.IMG","wb");
+//       fwrite(image,sizeof(unsigned char),MAX_PAGE_SIZE,fp);
+//       fclose(fp);
+//   {
+
+  // Check for 64K Cart
+
+  for (loop = 0; loop < CART_PAGE_64K; loop++)
+    {
+      if (image[loop] != image[loop + CART_PAGE_64K])
+        break;
+    }
+
+  if (loop == CART_PAGE_64K)
+    {
+      MESSAGE (("64K\n"));
+      return CART_PAGE_64K;
+    }
+
+  // Check for 128K Cart
+
+  for (loop = 0; loop < CART_PAGE_128K; loop++)
+    {
+      if (image[loop] != image[loop + CART_PAGE_128K])
+        break;
+    }
+
+  if (loop == CART_PAGE_128K)
+    {
+      MESSAGE (("128K\n"));
+      return CART_PAGE_128K;
+    }
+
+  // Check for 256K Cart
+
+  for (loop = 0; loop < CART_PAGE_256K; loop++)
+    {
+      if (image[loop] != image[loop + CART_PAGE_256K])
+        break;
+    }
+
+  if (loop == CART_PAGE_256K)
+    {
+      MESSAGE (("256K\n"));
+      return CART_PAGE_256K;
+    }
+
+  // Check for 512K Cart
+
+  for (loop = 0; loop < CART_PAGE_512K; loop++)
+    {
+      if (image[loop] != image[loop + CART_PAGE_512K])
+        break;
+    }
+
+  if (loop == CART_PAGE_512K)
+    {
+      MESSAGE (("512K\n"));
+      return CART_PAGE_512K;
+    }
+
+  // Must be no cart situation -  floating data !!!)
+
+  MESSAGE (("Bad cartridge\n"));
+
+  return FALSE;
+}
+
+
+#define  MAX_ERRORS    16
+
+int
+cart_verify (char *filename)
+{
+  unsigned char image1[MAX_PAGE_SIZE], image2[MAX_PAGE_SIZE];
+  int offset = 0;
+  unsigned int loop = 0;
+  FILE *fp;
+  st_lnx_header_t header;
+  unsigned int result0 = MAX_ERRORS, result1 = MAX_ERRORS;
+
+
+#if 0
+  DEBUG (("cart_verify() called with <%s>\n\n", filename));
+#endif
+
+  if ((fp = fopen (filename, "rb")) == NULL)
+    {
+//      MESSAGE (("ERROR    : Could not open %s\n", filename));
+      MESSAGE ((ucon64_msg[OPEN_READ_ERROR], filename));
+      return FALSE;
+    }
+
+  if (fread (&header, sizeof (st_lnx_header_t), 1, fp) != 1)
+    {
+      MESSAGE (("ERROR    : Disk read operation failed on %s\n", filename));
+      fclose (fp);
+      return FALSE;
+    }
+
+  if (strcmp (header.magic, "LYNX") != 0)
+    {
+      MESSAGE (("ERROR    : %s is not a lynx image\n", filename));
+      fclose (fp);
+      return FALSE;
+    }
+
+  if (header.version != FILE_FORMAT_VERSION)
+    {
+      MESSAGE (("ERROR    : %s has wrong version information\n", filename));
+      fclose (fp);
+      return FALSE;
+    }
+
+  if (header.page_size_bank0 != cart_analyse (BANK0))
+    {
+      MESSAGE (("ERROR    : Cartridge BANK0 size mismatch\n"));
+      fclose (fp);
+      return FALSE;
+    }
+
+  if (header.page_size_bank1 != cart_analyse (BANK1))
+    {
+      MESSAGE (("ERROR    : Cartridge BANK1 size mismatch\n"));
+      fclose (fp);
+      return FALSE;
+    }
+
+
+  if (header.page_size_bank0)
+    {
+      for (loop = 0; loop < 256; loop++)
+        {
+          MESSAGE (("Verifying BANK0: Page <%03d> of <256>", loop + 1));
+          cart_read_page (BANK0, loop, header.page_size_bank0, image1);
+          MESSAGE (("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"));
+          if (fread (image2, sizeof (unsigned char), header.page_size_bank0, fp)
+              != (size_t) header.page_size_bank0)
+            {
+              MESSAGE (("\nERROR    : Disk read operation failed on %s\n",
+                        filename));
+              fclose (fp);
+              return FALSE;
+            }
+
+          for (offset = 0; offset < header.page_size_bank0; offset++)
+            {
+              if (image1[offset] != image2[offset])
+                {
+                  MESSAGE (("VERIFY   : Mismatch in BANK<0>:PAGE<%02x>:OFFSET<%03x>\n", loop, offset));
+                  if (!(--result0))
+                    {
+                      MESSAGE (("VERIFY   : Too many errors in BANK0, aborting\n"));
+                      loop = 256;
+                      break;
+                    }
+                }
+            }
+        }
+    }
+
+  if (header.page_size_bank1)
+    {
+      for (loop = 0; loop < 256; loop++)
+        {
+          MESSAGE (("Verifying BANK1: Page <%03d> of <256>", loop + 1));
+          cart_read_page (BANK1, loop, header.page_size_bank1, image1);
+          MESSAGE (("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"));
+          if (fread (image2, sizeof (unsigned char), header.page_size_bank1, fp)
+              != (size_t) header.page_size_bank1)
+            {
+              MESSAGE (("\nERROR    : Disk read operation failed on %s\n",
+                        filename));
+              fclose (fp);
+              return FALSE;
+            }
+
+          for (offset = 0; offset < header.page_size_bank0; offset++)
+            {
+              if (image1[offset] != image2[offset])
+                {
+                  MESSAGE (("VERIFY   : Mismatch in BANK<1>:PAGE<%02x>:OFFSET<%03x>\n", loop, offset));
+                  if (!(--result1))
+                    {
+                      MESSAGE (("VERIFY   : Too many errors in BANK1, aborting\n"));
+                      break;
+                    }
+                }
+            }
+        }
+    }
+
+  fclose (fp);
+
+  if (result0 != MAX_ERRORS || result1 != MAX_ERRORS)
+    {
+      MESSAGE (("VERIFY   : FAILED                   \n"));
+      return FALSE;
+    }
+  else
+    {
+      MESSAGE (("VERIFY   : OK                       \n"));
+      return TRUE;
+    }
+}
+
+
+int
+cart_read (char *filename)
+{
+  unsigned char image[MAX_PAGE_SIZE];
+//  unsigned int page = 0;
+  unsigned int loop = 0;
+  FILE *fp;
+  st_lnx_header_t header;
+
+#if 0
+  DEBUG (("read_cart() called with <%s>\n\n", filename));
+#endif
+
+  memset (&header, 0, sizeof (st_lnx_header_t));
+  strcpy (header.magic, MAGIC_STRING);
+  strcpy (header.cartname, cartname);
+  strcpy (header.manufname, manufname);
+  header.page_size_bank0 = cart_analyse (BANK0);
+  header.page_size_bank1 = cart_analyse (BANK1);
+  header.version = FILE_FORMAT_VERSION;
+
+  if ((fp = fopen (filename, "wb")) == NULL)
+    return FALSE;
+
+  if (fwrite (&header, sizeof (st_lnx_header_t), 1, fp) != 1)
+    {
+      MESSAGE (("ERROR    : Disk write operation failed on %s\n", filename));
+      fclose (fp);
+      return FALSE;
+    }
+
+  if (header.page_size_bank0)
+    {
+      for (loop = 0; loop < 256; loop++)
+        {
+          MESSAGE (("Reading BANK0: Page <%03d> of <256>", loop + 1));
+          cart_read_page (BANK0, loop, header.page_size_bank0, image);
+          MESSAGE (("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"));
+          if (fwrite (image, sizeof (unsigned char), header.page_size_bank0, fp)
+              != (size_t) header.page_size_bank0)
+            {
+              MESSAGE (("\nERROR    : Disk write operation failed on %s\n",
+                        filename));
+              fclose (fp);
+              return FALSE;
+            }
+        }
+    }
+
+  if (header.page_size_bank1)
+    {
+      for (loop = 0; loop < 256; loop++)
+        {
+          MESSAGE (("Reading BANK1: Page <%03d> of <256>", loop + 1));
+          cart_read_page (BANK1, loop, header.page_size_bank1, image);
+          MESSAGE (("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"));
+          if (fwrite (image, sizeof (unsigned char), header.page_size_bank1, fp)
+              != (size_t) header.page_size_bank1)
+            {
+              MESSAGE (("\nERROR    : Disk write operation failed on %s\n",
+                        filename));
+              fclose (fp);
+              return FALSE;
+            }
+        }
+    }
+
+  MESSAGE (("READ     : OK                         \n"));
+
+  fclose (fp);
+
+  if (verify)
+    return (cart_verify (filename));
+  else
+    return TRUE;
+}
+
+
+#if 0
+int
+cart_write (char *filename)
+{
+  DEBUG (("write_cart() called with <%s>\n\n", filename));
+  return TRUE;
+}
+#endif
+
+
+int
+perform_test (char *testname)
+{
+  if (strcmp (testname, "LOOPBACK") == 0)
+    {
+      unsigned int loop;
+
+      for (loop = 0; loop < 256; loop++)
+        {
+          lynxit_shift_out_byte ((unsigned char) loop);
+          lynxit_shift_out_byte (0);
+          lynxit_shift_out_byte (0);
+          lynxit_shift_out_byte (0);
+
+          if (lynxit_shift_in_byte () != (unsigned char) loop)
+            {
+              MESSAGE (("LOOPBACK : FAILED\n"));
+              return FALSE;
+            }
+        }
+      MESSAGE (("LOOPBACK : OK\n"));
+      return TRUE;
+    }
+  else if (strncmp (testname, "SIZE", 4) == 0)
+    {
+      if (perform_test ("LOOPBACK") == FALSE)
+        return FALSE;
+      cart_analyse (BANK0);
+      cart_analyse (BANK1);
+      return TRUE;
+    }
+  else if (strncmp (testname, "CART", 4) == 0)
+    {
+      unsigned int page, offset;
+      unsigned char image1[CART_PAGE_128K];
+      unsigned char image2[CART_PAGE_128K];
+      int result = TRUE;
+
+      if (perform_test ("LOOPBACK") == FALSE)
+        return FALSE;
+      cart_analyse (BANK0);
+
+      for (page = 0; page < 256; page++)
+        {
+          MESSAGE (("Testing BANK0: Page <%03d> of <256>", page + 1));
+
+          cart_read_page (BANK0, page, CART_PAGE_128K, image1);
+          cart_read_page (BANK0, page, CART_PAGE_128K, image2);
+
+          MESSAGE (("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"));
+
+          for (offset = 0; offset < CART_PAGE_128K; offset++)
+            {
+              if (image1[offset] != image2[offset])
+                {
+                  MESSAGE (("CARTRIDGE: Bad read on PAGE %02x               \n", page));
+                  result = FALSE;
+                  break;
+                }
+            }
+        }
+      if (!result)
+        {
+          MESSAGE (("CARTRIDGE: FAILED                         \n"));
+          return FALSE;
+        }
+      else
+        {
+          MESSAGE (("CARTRIDGE: OK                             \n"));
+          return TRUE;
+        }
+    }
+  else if (strcmp (testname, "INPORT") == 0)
+    {
+      do
+        {
+          MESSAGE (("INPORT   : Read %02x\n", INPUT (print_stat)));
+        }
+      while (!kbhit ());
+      return TRUE;
+    }
+  else if (strcmp (testname, "PAGE") == 0)
+    {
+      do
+        {
+          lynxit_write_page (0xaa);
+          MESSAGE (("PAGE     : Wrote 0xAA to page\n"));
+          getch ();
+          lynxit_write_page (0x55);
+          MESSAGE (("PAGE     : Wrote 0x55 to page\n"));
+        }
+      while (getch () != 'q');
+      return TRUE;
+    }
+  else if (strcmp (testname, "COUNTER") == 0)
+    {
+      int stepmode = TRUE;
+
+      MESSAGE (("\nPress <space> key to step counter U5\n\n"));
+      MESSAGE (("  'q' - Quit to DOS\n"));
+      MESSAGE (("  's' - Step mode (default)\n"));
+      MESSAGE (("  'r' - Run mode\n"));
+      MESSAGE (("  'c' - Clear counter\n\n"));
+
+      lynxit_counter_reset ();
+      for (;;)
+        {
+          lynxit_counter_increment ();
+
+          MESSAGE (("COUNTER  : increment\n"));
+
+          if (kbhit () || stepmode)
+            {
+              switch (getch ())
+                {
+                case 'q':
+                  return TRUE;
+                case 's':
+                  stepmode = TRUE;
+                  break;
+                case 'r':
+                  stepmode = FALSE;
+                  break;
+                case 'c':
+                  lynxit_counter_reset ();
+                  MESSAGE (("COUNTER  : reset\n"));
+                  break;
+                default:
+                  break;
+                }
+            }
+        }
+      return TRUE;
+    }
+  else
+    {
+      return FALSE;
+    }
+}
+
+
+
+//
+//
+// MAIN CODE
+//
+//
+
+static char buf[FILENAME_MAX];
+
+
+int
+lynxit_main (int argc, char **argv)
+{
+  int loop;
+
+  // Handle argument list
+#if 0
+  for (loop = 0; loop < argc; loop++)
+    strupr (argv[loop]);
+#endif
+
+  for (loop = 1; loop < argc; loop++)
+    {
+      if (argv[loop][0] != '-')
+        {
+          break;
+        }
+
+      if (strlen (argv[loop]) > 3)
+        {
+//          usage ();
+          printf ("\nInvalid argument %d <%s>\n", loop, argv[loop]);
+          exit (FALSE);
+        }
+
+      switch (argv[loop][1])
+        {
+        case 'P':
+          printer_port = (unsigned int) argv[loop][2] - '0';
+          break;
+        case 'D':
+          debug = TRUE;
+          break;
+        case 'Q':
+          quiet = FALSE;
+          break;
+        case 'N':
+          verify = FALSE;
+          break;
+        case 'H':
+//          usage ();
+          exit (FALSE);
+        default:
+//          usage ();
+          printf ("\nUnrecognised argument %d <%s>\n", loop, argv[loop]);
+          exit (FALSE);
+        }
+    }
+
+  // Check there are 2 spare arguments
+
+  if (loop + 1 >= argc)
+    {
+//      usage ();
+      MESSAGE (("\nERROR    : Missing command/filename\n"));
+      exit (FALSE);
+    }
+
+  // Initialise the printer port
+
+  if (!ptr_port_init (printer_port))
+    {
+      MESSAGE (("ERROR    : Couldn't initialise printer port LPT%d\n",
+                printer_port));
+      exit (FALSE);
+    }
+
+  // Perform loopback tests to prove operation
+
+  if (!debug && strcmp (argv[loop], "TEST") != 0
+      && !perform_test ("LOOPBACK"))
+    {
+      MESSAGE (("ERROR    : LYNXIT doesn't appear to be working ????\n"));
+      MESSAGE (("ERROR    : (Check its plugged in and switched on)\n"));
+      exit (FALSE);
+    }
+
+  if (strcmp (argv[loop], "READ") == 0)
+    {
+      if (loop + 3 >= argc)
+        {
+          MESSAGE (("ERROR    : Missing Cartname/Manufacturer arguments\n"));
+          exit (FALSE);
+        }
+      strcpy (cartname, argv[argc - 2]);
+      strcpy (manufname, argv[argc - 1]);
+      if (strlen (cartname) > 32 || strlen (manufname) > 16)
+        {
+          MESSAGE (("ERROR    : Cartname/Manufacturer arguments too long (32/16)\n"));
+          exit (FALSE);
+        }
+
+      strcpy (buf, argv[++loop]);
+      if (cart_read (buf) == FALSE)
+        {
+          MESSAGE (("ERROR    : Cartridge read failed\n"));
+          exit (FALSE);
+        }
+    }
+  else if (strcmp (argv[loop], "WRITE") == 0)
+    {
+      strcpy (buf, argv[++loop]);
+//      cart_write (buf);                       // warning remover
+    }
+  else if (strcmp (argv[loop], "VERIFY") == 0)
+    {
+      cart_verify (argv[++loop]);
+    }
+  else if (strcmp (argv[loop], "TEST") == 0)
+    {
+      perform_test (argv[++loop]);
+    }
+  else
+    {
+//      usage ();
+      printf ("\nInvalid command argument - Use READ/WRITE/VERIFY/TEST\n");
+      exit (FALSE);
+    }
+
+  exit (TRUE);
+}
+
+
+int
+lynxit_read_rom (const char *filename, unsigned int parport)
+{
+  char *argv[128];
+
+  print_data = parport;
+  parport_print_info ();
+
+  argv[0] = "ucon64";
+  argv[1] = "READ";
+  strcpy (buf, filename);
+  argv[2] = buf;
+
+  if (lynxit_main (3, argv) != 0)
+    {
+      fprintf (stderr, ucon64_msg[PARPORT_ERROR]);
+      exit (1);
+    }
+  
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 31 - 0
ucon64/2.0/src/backup/lynxit.h

@@ -0,0 +1,31 @@
+/*
+lynxit.h - lynxit support for uCON64
+
+Copyright (c) 1997 - ???? K. Wilkins
+Copyright (c) 2002        NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef LYNXIT_H
+#define LYNXIT_H
+
+extern const st_getopt2_t lynxit_usage[];
+
+#ifdef USE_PARALLEL
+extern int lynxit_read_rom (const char *filename, unsigned int parport);
+#endif
+
+#endif

+ 158 - 0
ucon64/2.0/src/backup/mccl.c

@@ -0,0 +1,158 @@
+/*
+mccl.c - Mad Catz Camera Link (Game Boy Camera) support for uCON64
+
+Copyright (c) 2002 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+This cable is made by Mad Catz, Inc. and has a Game Boy link connector on one
+end and a parallel port connector on the other. It is designed to interface
+with the Game Boy Camera cart and comes with included software for this. It
+works by simulating the Game Boy Printer with a PIC chip inside the parallel
+connector shell. It doesn't do a particularly good job at that so it pretty
+much only works with the Game Boy Camera.
+
+Mad Catz Camera Link Communications Protocol
+
+Printer IO Ports:
+Base+0: Data Port
+Base+1: Status Port
+Base+2: Control
+
+Reset Procedure:
+1. Output 0x24 to control (tristate data and set control to 0100)
+2. Wait for bit 5 of status port to become 1
+3. Read lower 4 bits of data port
+4. If read data != 4, then go to step 1.
+5. (Useless read of control port?)
+6. Output 0x22 to control (tristate data and set control to 0010)
+7. Wait for bit 5 of status port to become 0
+8. Output 0x26 to control (tristate data and set control to 0110)
+
+Data Read Procedure:
+1. Output 0x26 to control (tristate data and set control to 0110)
+2. Wait for bit 5 of status port to become 1
+3. Read lower 4 bits of data port, store to lower 4 bits of received byte
+4. (Useless read of control port?)
+5. Output 0x22 to control (tristate data and set control to 0010)
+6. Wait for bit 5 of status port to become 0
+7. Output 0x26 to control (tristate data and set control to 0110)
+8. Wait for bit 5 of status port to become 1
+9. Read lower 4 bits of data port, store to upper 4 bits of received byte
+10. (Useless read of control port?)
+11. Output 0x22 to control (tristate data and set control to 0010)
+12. Wait for bit 5 of status port to become 0
+13. Go to step 1
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include "misc/misc.h"
+#include "misc/file.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "mccl.h"
+#include "misc/parallel.h"
+
+
+const st_getopt2_t mccl_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Mad Catz Camera Link (Game Boy Camera)"/*"XXXX Mad Catz Inc. http://www.madcatz.com"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xmccl", 0, 0, UCON64_XMCCL,
+      NULL, "receives from Mad Catz Camera Link; " OPTION_LONG_S "port=PORT",
+      &ucon64_wf[WF_OBJ_GB_DEFAULT_STOP_NO_ROM]
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef USE_PARALLEL
+
+#define DATA ((unsigned short) (parport + PARPORT_DATA))
+#define STATUS ((unsigned short) (parport + PARPORT_STATUS))
+#define CONTROL ((unsigned short) (parport + PARPORT_CONTROL))
+
+
+int
+mccl_read (const char *filename, unsigned int parport)
+{
+  unsigned char buffer[0x1760];
+  char dest_name[FILENAME_MAX];
+  int inbyte, count = 0;
+  time_t starttime;
+
+  parport_print_info ();
+  puts ("Resetting device");
+  do
+    {
+      outportb (CONTROL, 0x24);
+      while ((inportb (STATUS) & 0x20) == 0)
+        ;
+    }
+  while ((inportw (DATA) & 0xf) != 4);
+  outportb (CONTROL, 0x22);
+  while ((inportb (STATUS) & 0x20) != 0)
+    ;
+  outportb (CONTROL, 0x26);
+
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", 0x1760, (float) 0x1760 / MBIT);
+  starttime = time (NULL);
+  do
+    {
+      outportb (CONTROL, 0x26);
+      while ((inportb (STATUS) & 0x20) == 0)
+        ;
+      inbyte = inportw (DATA) & 0xf;
+      outportb (CONTROL, 0x22);
+      while ((inportb (STATUS) & 0x20) != 0)
+        ;
+      outportb (CONTROL, 0x26);
+      while ((inportb (STATUS) & 0x20) == 0)
+        ;
+      inbyte |= (inportw (DATA) & 0xf) << 4;
+      outportb (CONTROL, 0x22);
+      while ((inportb (STATUS) & 0x20) != 0)
+        ;
+      buffer[count++] = inbyte;
+      if ((count & 0x1f) == 0)
+        ucon64_gauge (starttime, count, 0x1760);
+    }
+  while (count < 0x1760);
+
+  strcpy (dest_name, filename);
+  ucon64_file_handler (dest_name, NULL, 0);
+  ucon64_fwrite (buffer, 0, count, dest_name, "wb");
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 30 - 0
ucon64/2.0/src/backup/mccl.h

@@ -0,0 +1,30 @@
+/*
+mccl.h - Mad Catz Camera Link (Game Boy Camera) support for uCON64
+
+Copyright (c) 2002 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef MCCL_H
+#define MCCL_H
+
+extern const st_getopt2_t mccl_usage[];
+
+#ifdef USE_PARALLEL
+extern int mccl_read (const char *filename, unsigned int parport);
+#endif
+
+#endif // MCCL_H

BIN
ucon64/2.0/src/backup/mccl.png


+ 144 - 0
ucon64/2.0/src/backup/mcd.c

@@ -0,0 +1,144 @@
+/*
+mcd.c - Mike Pavone's Genesis/Sega CD transfer cable support for uCON64
+
+Copyright (c) 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "misc/misc.h"
+#include "misc/parallel.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "mcd.h"
+
+
+const st_getopt2_t mcd_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Mike Pavone's Genesis/Sega CD transfer cable",
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xmcd", 0, 0, UCON64_XMCD,
+      NULL, "receive ROM from Genesis/Sega CD; " OPTION_LONG_S "port=PORT",
+      &ucon64_wf[WF_OBJ_GEN_DEFAULT_STOP_NO_SPLIT_NO_ROM]
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef  USE_PARALLEL
+
+#define BUFFERSIZE 8192
+
+
+static void
+checkabort (int status)
+{
+  if (((!ucon64.frontend) ? kbhit () : 0) && getch () == 'q')
+    {
+      puts ("\nProgram aborted");
+      exit (status);
+    }
+}
+
+
+static void
+read_block (unsigned char *buffer, int size, unsigned short parport)
+{
+  int i;
+
+  for (i = 0; i < size; i++)
+    {
+      while (inportb ((unsigned short) (parport + PARPORT_STATUS)) & 0x08)
+        ;
+      outportb ((unsigned short) (parport + PARPORT_CONTROL), 0xa2);
+
+      while (!(inportb ((unsigned short) (parport + PARPORT_STATUS)) & 0x08))
+        ;
+      buffer[i] = inportb ((unsigned short) (parport + PARPORT_DATA)) & 0x0f;
+      outportb ((unsigned short) (parport + PARPORT_CONTROL), 0xa0);
+
+      while (inportb ((unsigned short) (parport + PARPORT_STATUS)) & 0x08)
+        ;
+      outportb ((unsigned short) (parport + PARPORT_CONTROL), 0xa2);
+
+      while (!(inportb ((unsigned short) (parport + PARPORT_STATUS)) & 0x08))
+        ;
+      buffer[i] |=
+        (inportb ((unsigned short) (parport + PARPORT_DATA)) & 0x0f) << 4;
+      outportb ((unsigned short) (parport + PARPORT_CONTROL), 0xa0);
+    }
+}
+
+
+int
+mcd_read_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char buffer[BUFFERSIZE];
+  int n_bytes = 0, size;
+  time_t starttime;
+
+#if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
+  init_conio ();
+#endif
+  parport_print_info ();
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+
+  read_block (buffer, 1, (unsigned short) parport);
+  size = (buffer[0] + 1) * 64 * 1024;
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+  puts ("Press q to abort\n");
+
+  starttime = time (NULL);
+  while (n_bytes < size)
+    {
+      read_block (buffer, BUFFERSIZE, (unsigned short) parport);
+      fwrite (buffer, 1, BUFFERSIZE, file);
+      n_bytes += BUFFERSIZE;
+      ucon64_gauge (starttime, n_bytes, size);
+      checkabort (2);
+    }
+
+  fclose (file);
+#if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
+  deinit_conio ();
+#endif
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 30 - 0
ucon64/2.0/src/backup/mcd.h

@@ -0,0 +1,30 @@
+/*
+mcd.h - Mike Pavone's Genesis/Sega CD transfer cable support for uCON64
+
+Copyright (c) 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef MCD_H
+#define MCD_H
+
+extern const st_getopt2_t mcd_usage[];
+
+#ifdef USE_PARALLEL
+extern int mcd_read_rom (const char *filename, unsigned int parport);
+#endif
+
+#endif

+ 395 - 0
ucon64/2.0/src/backup/md-pro.c

@@ -0,0 +1,395 @@
+/*
+md-pro.c - MD-PRO flash card programmer support for uCON64
+
+Copyright (c) 2003 - 2004 dbjh
+Copyright (c) 2003        NoisyB <noisyb@gmx.net>
+
+Based on Delphi source code by ToToTEK Multi Media. Information in that source
+code has been used with permission. However, ToToTEK Multi Media explicitly
+stated that the information in that source code may be freely distributed.
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "misc/parallel.h"
+#include "misc/itypes.h"
+#include "misc/misc.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/file.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "tototek.h"
+#include "md-pro.h"
+
+
+const st_getopt2_t mdpro_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "MD-PRO flash card programmer"/*"2003 ToToTEK Multi Media http://www.tototek.com"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xmd", 0, 0, UCON64_XMD,
+      NULL, "send/receive ROM to/from MD-PRO flash card programmer\n" OPTION_LONG_S "port=PORT\n"
+      "receives automatically (32/64 Mbits) when ROM does not exist",
+      &ucon64_wf[WF_OBJ_GEN_DEFAULT_STOP_NO_SPLIT_NO_ROM]
+    },
+    {
+      "xmds", 0, 0, UCON64_XMDS,
+      NULL, "send/receive SRAM to/from MD-PRO flash card programmer\n" OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_GEN_STOP_NO_ROM]
+    },
+    {
+      "xmdb", 1, 0, UCON64_XMDB,
+      "BANK", "send/receive SRAM to/from MD-PRO BANK\n"
+      "BANK can be a number from 1 to 4; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_GEN_STOP_NO_ROM]
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef  USE_PARALLEL
+
+static void eep_reset (void);
+static void write_rom_by_byte (int *addr, unsigned char *buf);
+static void write_rom_by_page (int *addr, unsigned char *buf);
+static void write_ram_by_byte (int *addr, unsigned char *buf);
+static void write_ram_by_page (int *addr, unsigned char *buf);
+
+static unsigned short int md_id;
+
+
+void
+eep_reset (void)
+{
+  ttt_rom_enable ();
+  ttt_write_mem (0x400000, 0xff);               // reset EEP chip 2
+  ttt_write_mem (0, 0xff);                      // reset EEP chip 1
+  ttt_write_mem (0x600000, 0xff);               // reset EEP chip 2
+  ttt_write_mem (0x200000, 0xff);               // reset EEP chip 1
+  ttt_rom_disable ();
+}
+
+
+void
+write_rom_by_byte (int *addr, unsigned char *buf)
+{
+  int x;
+
+  for (x = 0; x < 0x4000; x++)
+    {
+      if (md_id == 0xb0d0)
+        ttt_write_byte_sharp (*addr, buf[*addr & 0x3fff]);
+      else if (md_id == 0x8916 || md_id == 0x8917)
+        ttt_write_byte_intel (*addr, buf[*addr & 0x3fff]);
+      (*addr)++;
+    }
+}
+
+
+void
+write_rom_by_page (int *addr, unsigned char *buf)
+{
+  int x;
+
+  for (x = 0; x < 0x200; x++)
+    {
+      ttt_write_page_rom (*addr, buf);
+      (*addr) += 0x20;
+    }
+}
+
+
+void
+write_ram_by_byte (int *addr, unsigned char *buf)
+{
+  int x, i = *addr & 0x3fff;
+
+  for (x = 0; x < 0x4000; x++, i = (i + 1) & 0x3fff)
+    {
+      ttt_write_byte_ram (*addr, buf[i]);
+      (*addr)++;
+      // Send the same byte again => SRAM files needn't store redundant data
+      ttt_write_byte_ram (*addr, buf[i]);
+      (*addr)++;
+    }
+}
+
+
+void
+write_ram_by_page (int *addr, unsigned char *buf)
+{
+  int x;
+
+  for (x = 0; x < 0x80; x++)
+    {
+      ttt_write_page_ram2 (*addr, buf);
+      (*addr) += 0x80;
+    }
+}
+
+
+int
+md_read_rom (const char *filename, unsigned int parport, int size)
+{
+  FILE *file;
+  unsigned char buffer[0x100];
+  int blocksleft, address = 0, id;
+  time_t starttime;
+  void (*read_block) (int, unsigned char *) = ttt_read_rom_w; // ttt_read_rom_b
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  ttt_init_io (parport);
+
+  eep_reset ();
+  id = ttt_get_id ();
+  if ((id == 0xb0d0 || id == 0x8916) && size > 32 * MBIT)
+    size = 32 * MBIT;                           // Sharp or Intel 32 Mbit flash card
+#if 0
+  // size is set to 64 * MBIT "by default" (in ucon64_opts.c)
+  else if (id == 0x8917 && size > 64 * MBIT)
+    size = 64 * MBIT;                           // Intel 64 Mbit flash card
+#endif
+
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  blocksleft = size >> 8;
+  eep_reset ();
+  ttt_rom_enable ();
+  if (read_block == ttt_read_rom_w)
+    ttt_set_ai_data (6, 0x94);          // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS
+  starttime = time (NULL);
+  while (blocksleft-- > 0)
+    {
+      read_block (address, buffer);             // 0x100 bytes read
+      if (read_block == ttt_read_rom_b)
+        ucon64_bswap16_n (buffer, 0x100);
+      fwrite (buffer, 1, 0x100, file);
+      address += 0x100;
+      if ((address & 0x3fff) == 0)
+        ucon64_gauge (starttime, address, size);
+    }
+  // original code doesn't call ttt_rom_disable() when byte-size function is
+  //  used (ttt_read_rom_b() calls it)
+  if (read_block == ttt_read_rom_w)
+    ttt_rom_disable ();
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+
+int
+md_write_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char buffer[0x4000];
+  int size, address = 0, bytesread, bytessend = 0;
+  time_t starttime;
+  void (*write_block) (int *, unsigned char *) = write_rom_by_page; // write_rom_by_byte
+  (void) write_rom_by_byte;
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  ttt_init_io (parport);
+
+  size = fsizeof (filename);
+  printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  eep_reset ();
+  md_id = ttt_get_id ();
+  if ((md_id != 0xb0d0) && (md_id != 0x8916) && (md_id != 0x8917)) // Sharp 32M, Intel 64J3
+    {
+      fputs ("ERROR: MD-PRO flash card (programmer) not detected\n", stderr);
+      fclose (file);
+      ttt_deinit_io ();
+      exit (1);
+    }
+
+  starttime = time (NULL);
+  eep_reset ();
+  while ((bytesread = fread (buffer, 1, 0x4000, file)))
+    {
+      ucon64_bswap16_n (buffer, 0x4000);
+      if ((((address & 0xffff) == 0) && (md_id == 0xb0d0)) ||
+          (((address & 0x1ffff) == 0) && (md_id == 0x8916 || md_id == 0x8917)))
+        ttt_erase_block (address);
+      write_block (&address, buffer);
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+    }
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+
+int
+md_read_sram (const char *filename, unsigned int parport, int start_bank)
+/*
+  The MD-PRO has 256 kB of SRAM. However, the SRAM dumps of all games that have
+  been tested had each byte doubled. In order to make it possible to easily
+  obtain the SRAM data for use in an emulator, or to send an emulator SRAM file
+  to the MD-PRO, we remove the redundant data when receiving/dumping and double
+  the data when sending.
+  It could be that this approach causes trouble for some games. However, when
+  looking at ToToTEK's own code in ttt_write_page_ram2() this seems unlikely
+  (data is doubled in that function). Note that write_sram_by_byte() is a
+  function written by us, and so does the doubling of data, but it doesn't mean
+  it should work for all games.
+*/
+{
+  FILE *file;
+  unsigned char buffer[0x100];
+  int blocksleft, address, bytesreceived = 0, size, i;
+  time_t starttime;
+  void (*read_block) (int, unsigned char *) = ttt_read_ram_b; // ttt_read_ram_w
+  // This function does not seem to work if ttt_read_ram_w() is used, but see
+  //  note below
+
+  if (start_bank == -1)
+    {
+      address = 0;
+      size = 128 * 1024;
+    }
+  else
+    {
+      if (start_bank < 1 || start_bank > 4)
+        {
+          fputs ("ERROR: Bank must be a value 1 - 4\n", stderr);
+          exit (1);
+        }
+      address = (start_bank - 1) * 32 * 1024;
+      size = 32 * 1024;
+    }
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+
+  ttt_init_io (parport);
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  if (read_block == ttt_read_ram_w)
+    {
+//      address *= 2;
+      ttt_ram_enable ();
+#if 0
+      // According to JohnDie, disabling this statement should make it possible
+      //  to use ttt_read_ram_w().
+      ttt_set_ai_data (6, 0x98);        // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS
+#endif
+    }
+//  else
+//    ttt_set_ai_data (6, 0x94);          // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS
+
+  blocksleft = size >> 7;
+  starttime = time (NULL);
+  while (blocksleft-- > 0)
+    {
+      read_block (address, buffer);             // 0x100 bytes read
+      for (i = 0; i < 0x80; i++)
+        buffer[i] = buffer[2 * i];              // data is doubled => no problems with endianess
+      fwrite (buffer, 1, 0x80, file);
+      address += 0x100;
+      bytesreceived += 0x80;
+      if ((address & 0x3fff) == 0)
+        ucon64_gauge (starttime, bytesreceived, size);
+    }
+  if (read_block == ttt_read_ram_w)
+    ttt_ram_disable ();
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+
+int
+md_write_sram (const char *filename, unsigned int parport, int start_bank)
+{
+  FILE *file;
+  unsigned char buffer[0x4000];
+  int size, bytesread, bytessend = 0, address;
+  time_t starttime;
+  void (*write_block) (int *, unsigned char *) = write_ram_by_byte; // write_ram_by_page
+  (void) write_ram_by_page;
+
+  size = fsizeof (filename);
+  if (start_bank == -1)
+    address = 0;
+  else
+    {
+      if (start_bank < 1 || start_bank > 4)
+        {
+          fputs ("ERROR: Bank must be a value 1 - 4\n", stderr);
+          exit (1);
+        }
+      address = (start_bank - 1) * 32 * 1024;
+    }
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+
+  ttt_init_io (parport);
+  printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  starttime = time (NULL);
+  while ((bytesread = fread (buffer, 1, 0x4000, file)))
+    {
+      write_block (&address, buffer);
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+    }
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 37 - 0
ucon64/2.0/src/backup/md-pro.h

@@ -0,0 +1,37 @@
+/*
+md-pro.h - MD-PRO flash card programmer support for uCON64
+
+Copyright (c) 2003 dbjh
+
+Based on Delphi source code by ToToTEK Multi Media. Information in that source
+code has been used with permission. However, ToToTEK Multi Media explicitly
+stated that the information in that source code may be freely distributed.
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef MD_PRO_H
+#define MD_PRO_H
+
+extern const st_getopt2_t mdpro_usage[];
+
+#ifdef USE_PARALLEL
+extern int md_read_rom (const char *filename, unsigned int parport, int size);
+extern int md_write_rom (const char *filename, unsigned int parport);
+extern int md_read_sram (const char *filename, unsigned int parport, int start_bank);
+extern int md_write_sram (const char *filename, unsigned int parport, int start_bank);
+#endif
+
+#endif

+ 466 - 0
ucon64/2.0/src/backup/mgd.c

@@ -0,0 +1,466 @@
+/*
+mgd.c - Multi Game Doctor/Hunter support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2001 - 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "misc/misc.h"
+#include "misc/file.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "misc/string.h"
+#include "mgd.h"
+
+
+const st_getopt2_t mgd_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Multi Game Doctor (2)/Multi Game Hunter/MGH"
+      /*"19XX Bung Enterprises Ltd http://www.bung.com.hk\n" "?Makko Toys Co., Ltd.?"*/,
+      NULL
+    },
+#if 0
+    {
+      "xmgd", 0, 0, UCON64_XMGD,
+      NULL, "(TODO) send/receive ROM to/from Multi Game* /MGD2/MGH; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when " OPTION_LONG_S "rom does not exist",
+      NULL
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+// the following four functions are used by non-transfer code in genesis.c
+void
+mgd_interleave (unsigned char **buffer, int size)
+{
+  int n;
+  unsigned char *src = *buffer;
+
+  if (!(*buffer = (unsigned char *) malloc (size)))
+    {
+      fprintf (stderr, ucon64_msg[BUFFER_ERROR], size);
+      exit (1);
+    }
+  for (n = 0; n < size / 2; n++)
+    {
+      (*buffer)[n] = src[n * 2 + 1];
+      (*buffer)[size / 2 + n] = src[n * 2];
+    }
+  free (src);
+}
+
+
+void
+mgd_deinterleave (unsigned char **buffer, int data_size, int buffer_size)
+{
+  int n = 0, offset;
+  unsigned char *src = *buffer;
+
+  if (!(*buffer = (unsigned char *) malloc (buffer_size)))
+    {
+      fprintf (stderr, ucon64_msg[BUFFER_ERROR], buffer_size);
+      exit (1);
+    }
+  for (offset = 0; offset < data_size / 2; offset++)
+    {
+      (*buffer)[n++] = src[data_size / 2 + offset];
+      (*buffer)[n++] = src[offset];
+    }
+  free (src);
+}
+
+
+int
+fread_mgd (void *buffer, size_t size, size_t number, FILE *fh)
+/*
+  This function is used to handle a Genesis MGD file as if it wasn't
+  interleaved, without the overhead of reading the entire file into memory.
+  This is important for genesis_init(). When the file turns out to be a Genesis
+  dump in MGD format it is much more efficient for compressed files to read the
+  entire file into memory and then deinterleave it (as load_rom() does).
+  In order to speed this function up a bit ucon64.file_size is used. That means
+  it can't be used for an arbitrary file.
+*/
+{
+  int n = 0, bpos = 0, fpos, fpos_org, block_size, bytesread = 0,
+      len = number * size, fsize = ucon64.file_size /* fsizeof (filename) */;
+  unsigned char tmp1[MAXBUFSIZE], tmp2[MAXBUFSIZE];
+
+  fpos = fpos_org = ftell (fh);
+  if (fpos >= fsize)
+    return 0;
+
+  if (len == 0)
+    return 0;
+  else if (len == 1)
+    {
+      if (fpos_org & 1)
+        {
+          fseek (fh, fpos / 2, SEEK_SET);
+          *((unsigned char *) buffer) = fgetc (fh);
+        }
+      else
+        {
+          fseek (fh, fpos / 2 + fsize / 2, SEEK_SET);
+          *((unsigned char *) buffer) = fgetc (fh);
+        }
+      fseek (fh, fpos_org + 1, SEEK_SET);
+      return 1;
+    }
+
+  while (len > 0 && !feof (fh))
+    {
+      block_size = len > MAXBUFSIZE ? MAXBUFSIZE : len;
+
+      fseek (fh, fpos / 2, SEEK_SET);
+      bytesread += fread (tmp1, 1, block_size / 2, fh); // read odd bytes
+      fseek (fh, (fpos + 1) / 2 + fsize / 2, SEEK_SET);
+      bytesread += fread (tmp2, 1, block_size / 2, fh); // read even bytes
+
+      if (fpos_org & 1)
+        for (n = 0; n < block_size / 2; n++)
+          {
+            ((unsigned char *) buffer)[bpos + n * 2] = tmp1[n];
+            ((unsigned char *) buffer)[bpos + n * 2 + 1] = tmp2[n];
+          }
+      else
+        for (n = 0; n < block_size / 2; n++)
+          {
+            ((unsigned char *) buffer)[bpos + n * 2] = tmp2[n];
+            ((unsigned char *) buffer)[bpos + n * 2 + 1] = tmp1[n];
+          }
+      fpos += block_size;
+      bpos += block_size;
+      len -= block_size;
+    }
+  fseek (fh, fpos_org + bytesread, SEEK_SET);
+  return bytesread / size;
+}
+
+
+int
+q_fread_mgd (void *buffer, size_t start, size_t len, const char *filename)
+{
+  int result;
+  FILE *fh;
+
+  if ((fh = fopen (filename, "rb")) == NULL)
+    return -1;
+  fseek (fh, start, SEEK_SET);
+  result = (int) fread_mgd (buffer, 1, len, fh);
+  fclose (fh);
+
+  return result;
+}
+
+
+static void
+remove_mgd_id (char *name, const char *id)
+{
+  char *p = name;
+  while ((p = strstr (p, id)))
+    {
+      *p = 'X';
+      *(p + 1) = 'X';
+      p += 2;
+    }
+}
+
+
+void
+mgd_make_name (const char *filename, int console, int size, char *name)
+// these characters are also valid in MGD file names: !@#$%^&_
+{
+  char *prefix = 0, *p, *size_str = 0, *suffix = 0;
+  const char *fname;
+  int n;
+
+  switch (console)
+    {
+    default:                                    // falling through
+    case UCON64_SNES:
+      prefix = "SF";
+      suffix = ".048";
+      if (size <= 1 * MBIT)
+        size_str = "1";
+      else if (size <= 2 * MBIT)
+        size_str = "2";
+      else if (size <= 4 * MBIT)
+        size_str = "4";
+      else if (size <= 8 * MBIT)
+        {
+          size_str = "8";
+          suffix = ".058";
+        }
+      else
+        {
+          suffix = ".078";
+          if (size <= 10 * MBIT)
+            size_str = "10";
+          else if (size <= 12 * MBIT)
+            size_str = "12";
+          else if (size <= 16 * MBIT)
+            size_str = "16";
+          else if (size <= 20 * MBIT)
+            size_str = "20";
+          else if (size <= 24 * MBIT)
+            size_str = "24";
+          else // MGD supports SNES games with sizes up to 32 Mbit
+            size_str = "32";
+        }
+      break;
+    case UCON64_GEN:
+      prefix = "MD";
+      suffix = ".000";
+      if (size <= 1 * MBIT)
+        size_str = "1";
+      else if (size <= 2 * MBIT)
+        size_str = "2";
+      else if (size <= 4 * MBIT)
+        size_str = "4";
+      else
+        {
+          if (size <= 8 * MBIT)
+            {
+              size_str = "8";
+              suffix = ".008";
+            }
+          else if (size <= 16 * MBIT)
+            {
+              size_str = "16";
+              suffix = ".018";
+            }
+          else
+            {
+              suffix = ".038";
+              if (size <= 20 * MBIT)
+                size_str = "20";
+              else if (size <= 24 * MBIT)
+                size_str = "24";
+              else // MGD supports Genesis games with sizes up to 32 Mbit
+                size_str = "32";
+            }
+        }
+      break;
+    case UCON64_PCE:
+      prefix = "PC";
+      suffix = ".040";
+      if (size <= 1 * MBIT)
+        size_str = "1";
+      else if (size <= 2 * MBIT)
+        size_str = "2";
+      else if (size <= 3 * MBIT)
+        {
+          size_str = "3";
+          suffix = ".030";
+        }
+      else if (size <= 4 * MBIT)
+        {
+          size_str = "4";
+          suffix = ".048";
+        }
+      else
+        {
+          suffix = ".058";
+          if (size <= 6 * MBIT)
+            size_str = "6";
+          else // MGD supports PC-Engine games with sizes up to 8 Mbit
+            size_str = "8";
+        }
+      break;
+    case UCON64_SMS:
+      prefix = "GG";
+      suffix = ".060";
+      if (size < 1 * MBIT)
+        size_str = "0";
+      else if (size == 1 * MBIT)
+        size_str = "1";
+      else if (size <= 2 * MBIT)
+        size_str = "2";
+      else
+        {
+          suffix = ".078";
+          if (size <= 3 * MBIT)
+            size_str = "3";
+          else if (size <= 4 * MBIT)
+            size_str = "4";
+          else if (size <= 6 * MBIT)
+            size_str = "6";
+          else // MGD supports Sega Master System games with sizes up to 8 Mbit
+            size_str = "8";
+        }
+      break;
+    case UCON64_GAMEGEAR:
+      prefix = "GG";
+      suffix = ".040";
+      if (size < 1 * MBIT)
+        size_str = "0";
+      else if (size == 1 * MBIT)
+        size_str = "1";
+      else if (size <= 2 * MBIT)
+        size_str = "2";
+      else
+        {
+          suffix = ".048";
+          if (size <= 3 * MBIT)
+            size_str = "3";
+          else if (size <= 4 * MBIT)
+            size_str = "4";
+          else
+            {
+              suffix = ".078";
+              if (size <= 6 * MBIT)
+                size_str = "6";
+              else // MGD supports Game Gear games with sizes up to 8 Mbit
+                size_str = "8";
+            }
+        }
+      break;
+    case UCON64_GB:
+      prefix = "GB";
+      /*
+        What is the maximum game size the MGD2 supports for GB (color) games?
+        At least one 64 Mbit game exists, Densha De Go! 2 (J) [C][!].
+      */
+      suffix = ".040";
+      if (size < 1 * MBIT)
+        size_str = "0";
+      else if (size == 1 * MBIT)
+        size_str = "1";
+      else if (size <= 2 * MBIT)
+        size_str = "2";
+      else if (size <= 3 * MBIT)
+        {
+          size_str = "3";
+          suffix = ".030";
+        }
+      else if (size <= 4 * MBIT)
+        {
+          size_str = "4";
+          suffix = ".048";
+        }
+      else
+        {
+          suffix = ".058";
+          if (size <= 6 * MBIT)
+            size_str = "6";
+          else
+            size_str = "8";
+        }
+      break;
+    }
+
+  fname = basename2 (filename);
+  // Do NOT mess with prefix (strupr()/strlwr()). See below (remove_mgd_id()).
+  sprintf (name, "%s%s%s", prefix, size_str, fname);
+  if (size >= 10 * MBIT)
+    {
+      if (!strnicmp (name, fname, 4))
+        strcpy (name, fname);
+    }
+  else
+    {
+      if (!strnicmp (name, fname, 3))
+        strcpy (name, fname);
+    }
+  if ((p = strchr (name, '.')))
+    *p = 0;
+  n = strlen (name);
+  if (size >= 10 * MBIT)
+    {
+      if (n < 7)
+        strcat (name, "XXX");                   // in case fname is 1 character long
+      n = 7;
+    }
+  else
+    {
+      if (n < 6)
+        strcat (name, "XX");
+      n = 6;
+    }
+  name[n] = '0';                                // last character must be a number
+  name[n + 1] = 0;
+  for (n = 3; n < 8; n++)                       // we can skip the prefix
+    if (name[n] == ' ')
+      name[n] = 'X';
+
+  /*
+    the transfer program "pclink" contains a bug in that it looks at the
+    entire file name for an ID string (it should look only at the first 2
+    characters).
+  */
+  strupr (name);
+  remove_mgd_id (name + 3, "SF");
+  remove_mgd_id (name + 3, "MD");
+  remove_mgd_id (name + 3, "PC");
+  remove_mgd_id (name + 3, "GG");
+  remove_mgd_id (name + 3, "GB");
+
+  set_suffix (name, suffix);
+}
+
+
+void
+mgd_write_index_file (void *ptr, int n_names)
+{
+  char buf[100 * 10], *p, name[16], dest_name[FILENAME_MAX];
+  // one line in the index file takes 10 bytes at max (name (8) + "\r\n" (2)),
+  //  so buf is large enough for 44 files of 1/4 Mbit (max for 1 diskette)
+
+  if (n_names == 1)
+    {
+      strcpy (name, (char *) ptr);
+      if ((p = strrchr (name, '.')))
+        *p = 0;
+      sprintf (buf, "%s\r\n", name);            // DOS text file format
+    }
+  else if (n_names > 1)
+    {
+      int n = 0, offset = 0;
+
+      for (; n < n_names; n++)
+        {
+          strcpy (name, ((char **) ptr)[n]);
+          if ((p = strrchr (name, '.')))
+            *p = 0;
+          sprintf (buf + offset, "%s\r\n", name);
+          offset += strlen (name) + 2;          // + 2 for "\r\n"
+        }
+    }
+  else // n_names <= 0
+    return;
+
+  strcpy (dest_name, "MULTI-GD");
+  ucon64_file_handler (dest_name, NULL, OF_FORCE_BASENAME);
+  ucon64_fwrite (buf, 0, strlen (buf), dest_name, "wb");
+  printf (ucon64_msg[WROTE], dest_name);
+}

+ 185 - 0
ucon64/2.0/src/backup/mgd.h

@@ -0,0 +1,185 @@
+/*
+mgd.h - Multi Game Doctor/Hunter support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2002 - 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef MGD_H
+#define MGD_H
+
+extern const st_getopt2_t mgd_usage[];
+
+/*
+The MGD2 only accepts certain filenames, and these filenames
+must be specified in an index file, "MULTI-GD", otherwise the
+MGD2 will not recognize the file. In the case of multiple games
+being stored in a single disk, simply enter its corresponding
+MULTI-GD index into the "MULTI-GD" file.
+
+Thanks to acem77 for the (verified) list below.
+
+Super Famicom:
+
+game size       # of files      names           MULTI-GD
+================================================================
+1M              1               SF1XXX#.048     SF1XXX#
+2M              1               SF2XXX#.048     SF2XXX#
+4M              1               SF4XXX#.048     SF4XXX#
+4M              2               SF4XXXxA.078    SF4XXXxA
+                                SF4XXXxB.078    SF4XXXxB
+8M              1               SF8XXX#.058     SF8XXX#
+                2               SF8XXXxA.078    SF8XXXxA
+                                SF8XXXxB.078    SF8XXXxB
+10M             1               SF10XXX#.078    SF10XXX#
+                2               SF10XXX#.078    SF10XXX#
+                                SF10XXX#.078    SF10XXX#
+12M             1               SF12XXX#.078    SF12XXX#
+                2               SF12XXX#A.078   SF12XXX#B
+                                SF12XXX#B.078   SF12XXX#A
+16M             1               SF16XXX#.078    SF16XXX#
+16M             2               SF16XXX#A.078   SF16XXX#A
+                                SF16XXX#B.078   SF16XXX#B
+20M             1               SF20XXX#.078    SF20XXX#
+                3               SF20XXX#A.078   SF20XXX#A
+                                SF20XXX#B.078   SF20XXX#B
+                                SF20XXX#C.078   SF20XXX#C
+24M             1               SF24XXX#.078    SF24XXX#
+24M             3               SF24XXX#A.078   SF24XXX#A
+                                SF24XXX#B.078   SF24XXX#B
+                                SF24XXX#C.078   SF24XXX#C
+32M             1               SF32XXX#.078    SF32XXX#
+32M             4               SF32XXX#A.078   SF32XXX#A
+                                SF32XXX#B.078   SF32XXX#B
+                                SF32XXX#C.078   SF32XXX#C
+                                SF32XXX#D.078   SF32XXX#D
+
+Mega Drive:
+
+game size       # of files      names           MULTI-GD
+================================================================
+1M              1               MD1XXX#.000     MD1XXX#
+2M              1               MD2XXX#.000     MD2XXX#
+4M              1               MD4XXX#.000     MD4XXX#
+8M              1               MD8XXX#.008     MD8XXX#
+16M             2               MD16XXX#A.018   MD16XXX#A
+                                MD16XXX#B.018   MD16XXX#B
+20M             3               MD20XXX#A.038   MD20XXX#A
+                                MD20XXX#B.038   MD20XXX#B
+                                MD20XXX#C.038   MD20XXX#C
+24M             3               MD24XXX#A.038   MD24XXX#A
+                                MD24XXX#B.038   MD24XXX#B
+                                MD24XXX#C.038   MD24XXX#C
+32M             4               MD32XXX#A.038   MD32XXX#A
+                                MD32XXX#B.038   MD32XXX#B
+                                MD32XXX#C.038   MD32XXX#C
+                                MD32XXX#D.038   MD32XXX#D
+
+PC-Engine:
+
+game size       # of files      names           MULTI-GD
+================================================================
+1M              1               PC1XXX#.040     PC1XXX#
+2M              1               PC2XXX#.040     PC2XXX#
+3M              1               PC3XXX#.030     PC2XXX#
+4M              1               PC4XXX#.048     PC4XXX#
+6M              1               PC6XXX#.058     PC6XXX#
+8M              1               PC8XXX#.058     PC8XXX#
+
+Sega Master System:
+
+game size       # of files      names           MULTI-GD
+================================================================
+less than 1M    1               GG0XXX#.060     GG0XXX#
+1M              1               GG1XXX#.060     GG1XXX#
+2M              1               GG2XXX#.060/70  GG2XXX#
+3M              1               GG3XXX#.078     GG2XXX#
+4M              1               GG4XXX#.078/68  GG4XXX#
+6M              1               GG6XXX#.078     GG6XXX#
+8M              1               GG8XXX#.078     GG8XXX#
+
+Game Gear:
+
+game size       # of files      names           MULTI-GD
+================================================================
+less than 1M    1               GG0XXX#.040     GG0XXX#
+1M              1               GG1XXX#.040     GG1XXX#
+2M              1               GG2XXX#.050/40  GG2XXX#
+3M              1               GG3XXX#.048     GG2XXX#
+4M              1               GG4XXX#.058/48  GG4XXX#
+6M              1               GG6XXX#.078     GG6XXX#
+8M              1               GG8XXX#.078     GG8XXX#
+
+Game Boy:
+
+game size       # of files      names           MULTI-GD
+================================================================
+less than 1M    1               GB0XXX#.040     GB0XXX#
+1M              1               GB1XXX#.040     GB1XXX#
+2M              1               GB2XXX#.040/60  GB2XXX#
+3M              1               GB3XXX#.030     GB2XXX#
+4M              1               GB4XXX#.048     GB4XXX#
+6M              1               GB6XXX#.058     GB6XXX#
+8M              1               GB8XXX#.058     GB8XXX#
+
+
+Contrary to popular belief the Game Doctor SF3/SF6/SF7 *does*
+use a 512 byte header like the SWC, but it also accepts
+headerless files.
+A header is necessary when things like SRAM size must be made
+known to the Game Doctor. The Game Doctor also uses specially
+designed filenames to distinguish between multi files.
+
+Usually, the filename is in the format of: SFXXYYYZ.078
+
+Where SF means Super Famicom, XX refers to the size of the
+image in Mbit. If the size is only one character (i.e. 2, 4 or
+8 Mbit) then no leading "0" is inserted.
+
+YYY refers to a catalogue number in Hong Kong shops
+identifying the game title. (0 is Super Mario World, 1 is F-
+Zero, etc). I was told that the Game Doctor copier produces a
+random number when backing up games.
+
+Z indicates a multi file. Like XX, if it isn't used it's
+ignored.
+
+A would indicate the first file, B the second, etc. I am told
+078 is not needed, but is placed on the end of the filename by
+systems in Asia.
+
+e.g. The first 8 Mbit file of Donkey Kong Country (assuming it
+is cat. no. 475) would look like: SF32475A.078
+*/
+
+#ifdef USE_PARALLEL
+#endif // USE_PARALLEL
+
+// the following four functions are used by non-transfer code in genesis.c
+extern void mgd_interleave (unsigned char **buffer, int size);
+extern void mgd_deinterleave (unsigned char **buffer, int data_size,
+                              int buffer_size);
+extern int fread_mgd (void *buffer, size_t size, size_t number, FILE *fh);
+extern int q_fread_mgd (void *buffer, size_t start, size_t len,
+                        const char *filename);
+extern void mgd_make_name (const char *filename, int console, int size,
+                           char *name);
+extern void mgd_write_index_file (void *ptr, int n_names);
+
+#define MGD_HEADER_START 0
+#define MGD_HEADER_LEN 512
+#endif // MGD_H

+ 250 - 0
ucon64/2.0/src/backup/msg.c

@@ -0,0 +1,250 @@
+/*
+msg.c - Magic Super Griffin support for uCON64
+
+Copyright (c) 2003 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/file.h"
+#include "misc/misc.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "ffe.h"
+#include "msg.h"
+
+
+const st_getopt2_t msg_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Magic Super Griffin/MSG"/*"1993/1994/1995/19XX Front Far East/FFE http://www.front.com.tw"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xmsg", 0, 0, UCON64_XMSG,
+      NULL, "send/receive ROM to/from Magic Super Griffin/MSG; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when ROM does not exist",
+      &ucon64_wf[WF_OBJ_PCE_DEFAULT_STOP_NO_SPLIT_NO_ROM]
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef  USE_PARALLEL
+
+#define BUFFERSIZE 8192                         // don't change, only 8192 works!
+
+
+static void set_header (unsigned char *buffer);
+static int check (unsigned char *info_block, int index1, int index2, int size);
+
+
+#if BUFFERSIZE < 512
+#error receive_rom_info() expects BUFFERSIZE to be at least 512 bytes.
+#endif
+void
+set_header (unsigned char *buffer)
+{
+  int n, m = 0;
+  unsigned char sizes[] = "\x10\x20\x30\x30\x40\x40\x60\x80";
+
+  for (n = 0; n < 128; n++)
+    {
+      ffe_send_command (5, (unsigned short) n, 0);
+      buffer[n] = ffe_send_command1 (0xa0a0);
+      wait2 (1);
+      if (buffer[n] != 0xff)
+        m = 1;
+    }
+  if (m == 0)
+    {                                           // no cartridge in Magic Super Griffin
+      buffer[0] = 0;
+      return;
+    }
+
+  m = 0;
+  if (!check (buffer, 0, 0x40, 0x20))
+    m |= 2;
+  if (check (buffer, 0, 0x20, 0x20))
+    {
+      if (!check (buffer, 0, 0x10, 0x10))
+        m |= 1;
+    }
+  else
+    {
+      m |= 4;
+      if (!check (buffer, 0x40, 0x60, 0x20))
+        m |= 1;
+    }
+
+  memset (buffer, 0, MSG_HEADER_LEN);
+  buffer[0] = sizes[m];
+  if (buffer[0] == 0x30)
+    buffer[1] = 1;
+  buffer[8] = 0xaa;
+  buffer[9] = 0xbb;
+  buffer[10] = 2;
+}
+
+
+int
+check (unsigned char *info_block, int index1, int index2, int size)
+{
+  int n;
+
+  for (n = 0; n < size; n++)
+    if (info_block[n + index1] != info_block[n + index2])
+      return 0;
+
+  return 1;
+}
+
+
+int
+msg_read_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int size, blocksleft, blocksdone = 0, bytesreceived = 0, emu_mode_select;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  set_header (buffer);
+  if (buffer[0] == 0)
+    {
+      fprintf (stderr, "ERROR: There is no cartridge present in the Magic Super Griffin\n");
+      fclose (file);
+      remove (filename);
+      exit (1);
+    }
+  blocksleft = buffer[0];
+  size = buffer[0] * 8192;
+  printf ("Receive: %d Bytes (%.4f Mb)\n", size, (float) size / MBIT);
+
+  fwrite (buffer, 1, MSG_HEADER_LEN, file);     // write header
+  emu_mode_select = buffer[1];
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_command0 (0xbff0, 0);
+
+  printf ("Press q to abort\n\n");
+
+  starttime = time (NULL);
+  while (blocksleft > 0)
+    {
+      ffe_send_command (5, (unsigned short) blocksdone, 0);
+      if (emu_mode_select && blocksdone >= 32)
+        ffe_send_command (5, (unsigned short) (blocksdone + 32), 0);
+      ffe_receive_block (0xa000, buffer, BUFFERSIZE);
+      // vgs aborts if the checksum doesn't match the data, we let the user decide
+      blocksleft--;
+      blocksdone++;
+      fwrite (buffer, 1, BUFFERSIZE, file);
+
+      bytesreceived += BUFFERSIZE;
+      ucon64_gauge (starttime, bytesreceived, size);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+msg_write_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int bytesread, bytessend = 0, blocksdone = 0, emu_mode_select, size;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = fsizeof (filename) - MSG_HEADER_LEN;
+  printf ("Send: %d Bytes (%.4f Mb)\n", size, (float) size / MBIT);
+
+  fread (buffer, 1, MSG_HEADER_LEN, file);
+  emu_mode_select = buffer[1];                  // this byte is needed later
+
+  ffe_send_command0 (0xe008, 0);
+  printf ("Press q to abort\n\n");
+
+  starttime = time (NULL);
+  while ((bytesread = fread (buffer, 1, BUFFERSIZE, file)))
+    {
+      ffe_send_command (5, (unsigned short) blocksdone, 0);
+      ffe_send_block (0x8000, buffer, bytesread);
+      blocksdone++;
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      ffe_checkabort (2);
+    }
+
+  if (emu_mode_select & 1)
+    ffe_send_command (4, 0xff00, 0);
+  else
+    ffe_send_command (4, 0xff03, 0);
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 46 - 0
ucon64/2.0/src/backup/msg.h

@@ -0,0 +1,46 @@
+/*
+msg.h - Magic Super Griffin support for uCON64
+
+Copyright (c) 2003 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef MSG_H
+#define MSG_H
+
+extern const st_getopt2_t msg_usage[];
+
+// For the header format, see ffe.h
+typedef struct st_msg_header
+{
+  unsigned char size;
+  unsigned char emulation;
+  unsigned char pad[6];
+  unsigned char id1;
+  unsigned char id2;
+  unsigned char type;
+  unsigned char pad2[501];
+} st_msg_header_t;
+
+#define MSG_HEADER_START 0
+#define MSG_HEADER_LEN (sizeof (st_msg_header_t))
+
+#ifdef USE_PARALLEL
+extern int msg_read_rom (const char *filename, unsigned int parport);
+extern int msg_write_rom (const char *filename, unsigned int parport);
+#endif
+
+#endif // MSG_H

+ 204 - 0
ucon64/2.0/src/backup/pce-pro.c

@@ -0,0 +1,204 @@
+/*
+pce-pro.c - PCE-PRO flash card programmer support for uCON64
+
+Copyright (c) 2004 dbjh
+
+Based on Delphi source code by ToToTEK Multi Media. Information in that source
+code has been used with permission. However, ToToTEK Multi Media explicitly
+stated that the information in that source code may be freely distributed.
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "misc/parallel.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/file.h"
+#include "misc/misc.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "tototek.h"
+#include "pce-pro.h"
+
+
+const st_getopt2_t pcepro_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "PCE-PRO flash card programmer"/*"2004 ToToTEK Multi Media http://www.tototek.com"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xpce", 0, 0, UCON64_XPCE,
+      NULL, "send/receive ROM to/from PCE-PRO flash card programmer\n" OPTION_LONG_S "port=PORT\n"
+      "receives automatically (32 Mbits) when ROM does not exist",
+      &ucon64_wf[WF_OBJ_PCE_DEFAULT_STOP_NO_SPLIT_NO_ROM]
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef  USE_PARALLEL
+
+static void eep_reset (void);
+static void write_rom_by_byte (int *addr, unsigned char *buf);
+static void write_rom_by_page (int *addr, unsigned char *buf);
+
+
+void
+eep_reset (void)
+{
+  ttt_rom_enable ();
+  ttt_write_mem (0x000000, 0xff);               // reset EEP
+  ttt_write_mem (0x200000, 0xff);               // reset EEP
+  ttt_rom_disable ();
+}
+
+
+void
+write_rom_by_byte (int *addr, unsigned char *buf)
+{
+  int x;
+
+  for (x = 0; x < 0x4000; x++)
+    {
+      ttt_write_byte_sharp (*addr, buf[*addr & 0x3fff]);
+      (*addr)++;
+    }
+}
+
+
+void
+write_rom_by_page (int *addr, unsigned char *buf)
+{
+  int x;
+
+  for (x = 0; x < 0x200; x++)
+    {
+      ttt_write_page_rom (*addr, buf);
+      (*addr) += 0x20;
+    }
+}
+
+
+int
+pce_read_rom (const char *filename, unsigned int parport, int size)
+{
+  FILE *file;
+  unsigned char buffer[0x100];
+  int blocksleft, address = 0;
+  time_t starttime;
+  void (*read_block) (int, unsigned char *) = ttt_read_rom_w; // ttt_read_rom_b
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  ttt_init_io (parport);
+
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  blocksleft = size >> 8;
+  eep_reset ();
+  ttt_rom_enable ();
+  if (read_block == ttt_read_rom_w)
+    ttt_set_ai_data (6, 0x94);          // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS
+  starttime = time (NULL);
+  while (blocksleft-- > 0)
+    {
+      read_block (address, buffer);             // 0x100 bytes read
+      fwrite (buffer, 1, 0x100, file);
+      address += 0x100;
+      if ((address & 0x3fff) == 0)
+        ucon64_gauge (starttime, address, size);
+    }
+  // original code doesn't call ttt_rom_disable() when byte-size function is
+  //  used (ttt_read_rom_b() calls it)
+  if (read_block == ttt_read_rom_w)
+    ttt_rom_disable ();
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+
+int
+pce_write_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char buffer[0x4000];
+  int size, fsize, address = 0, bytesread, bytessend = 0;
+  time_t starttime;
+  void (*write_block) (int *, unsigned char *) = write_rom_by_page; // write_rom_by_byte
+  (void) write_rom_by_byte;
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  ttt_init_io (parport);
+
+  size = fsize = fsizeof (filename);
+  if (fsize == 4 * MBIT)
+    size += 2 * MBIT;
+  printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  eep_reset ();
+  if (ttt_get_id () != 0xb0d0)
+    {
+      fputs ("ERROR: PCE-PRO flash card (programmer) not detected\n", stderr);
+      fclose (file);
+      ttt_deinit_io ();
+      exit (1);
+    }
+
+  starttime = time (NULL);
+  eep_reset ();
+  while ((bytesread = fread (buffer, 1, 0x4000, file)))
+    {
+      if ((address & 0xffff) == 0)
+        ttt_erase_block (address);
+      write_block (&address, buffer);
+      if ((fsize == 3 * MBIT) && (address == 2 * MBIT))
+        address += 2 * MBIT;
+      if ((fsize == 4 * MBIT) && (address == 4 * MBIT))
+        fseek (file, 2 * MBIT, SEEK_SET);
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+    }
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 35 - 0
ucon64/2.0/src/backup/pce-pro.h

@@ -0,0 +1,35 @@
+/*
+pce-pro.h - PCE-PRO flash card programmer support for uCON64
+
+Copyright (c) 2004 dbjh
+
+Based on Delphi source code by ToToTEK Multi Media. Information in that source
+code has been used with permission. However, ToToTEK Multi Media explicitly
+stated that the information in that source code may be freely distributed.
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef PCE_PRO_H
+#define PCE_PRO_H
+
+extern const st_getopt2_t pcepro_usage[];
+
+#ifdef USE_PARALLEL
+extern int pce_read_rom (const char *filename, unsigned int parport, int size);
+extern int pce_write_rom (const char *filename, unsigned int parport);
+#endif
+
+#endif

+ 782 - 0
ucon64/2.0/src/backup/pl.c

@@ -0,0 +1,782 @@
+/*
+pl.c - Pocket Linker support for uCON64
+
+Copyright (c) 2004 Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+Partly based on PokeLink - Copyright (c) Dark Fader / BlackThunder
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/misc.h"
+#include "misc/parallel.h"
+#include "misc/file.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "pl.h"
+
+
+const st_getopt2_t pl_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Pocket Linker"/*"19XX Bung Enterprises Ltd"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xpl", 0, 0, UCON64_XPL,
+      NULL, "send/receive ROM to/from Pocket Linker; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when ROM does not exist",
+      &ucon64_wf[WF_OBJ_NGP_DEFAULT_STOP_NO_ROM]
+    },
+    {
+      "xpli", 0, 0, UCON64_XPLI,
+      NULL, "show information about inserted cartridge; " OPTION_LONG_S "port=PORT",
+      &ucon64_wf[WF_OBJ_NGP_STOP_NO_ROM]
+    },
+    {
+      "xplm", 0, 0, UCON64_XPLM,
+      NULL, "try to enable EPP mode, default is SPP mode",
+      &ucon64_wf[WF_OBJ_NGP_SWITCH]
+    },
+#endif // USE_PARALLEL
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef  USE_PARALLEL
+
+// flash unlock addresses with chip select
+#define fx0002aa(A) (((A) & 0x200000) | 0x0002aa)
+#define fx000555(A) (((A) & 0x200000) | 0x000555)
+#define fx000aaa(A) (((A) & 0x200000) | 0x000aaa)
+#define fx002aaa(A) (((A) & 0x200000) | 0x002aaa)
+#define fx005555(A) (((A) & 0x200000) | 0x005555)
+
+#define set_data_read outportb (port_a, 0);
+#define set_data_write outportb (port_a, 1);
+#define reset_port outportb (port_a, 4);
+#define clear_timeout outportb (port_9, 1);
+
+#define CMD_READ       0xf0
+#define CMD_INFO       0x90
+#define CMD_ERASE      0x80
+
+#define TYPE_BW        0x00
+#define TYPE_COLOR     0x01
+#define TYPE_MULTI     0x02
+
+static short int port_8, port_9, port_a, port_b, port_c;
+static parport_mode_t port_mode;
+static int current_ai;
+static unsigned char ai_value[4];
+
+
+static void
+epp_write_data (unsigned char data)
+{
+  outportb (port_c, data);
+}
+
+
+static void
+spp_write_data (unsigned char data)
+{
+  outportb (port_8, data);
+  outportb (port_a, 3);
+  outportb (port_a, 1);
+}
+
+
+static void
+write_data (unsigned char data)
+{
+  ai_value[current_ai] = data;
+  if (port_mode == UCON64_SPP)
+    spp_write_data (data);
+  else
+    epp_write_data (data);
+}
+
+
+static unsigned char
+epp_read_data (void)
+{
+  return inportb (port_c);
+}
+
+
+static unsigned char
+spp_read_data (void)
+{
+  unsigned char byte;
+
+  outportb (port_a, 2);
+  byte = (inportb (port_9) >> 3) & 0x0f;
+  outportb (port_a, 6);
+  byte |= (inportb (port_9) << 1) & 0xf0;
+  outportb (port_a, 0);
+
+  return byte;
+}
+
+
+static unsigned char
+read_data (void)
+{
+  if (port_mode == UCON64_SPP)
+    return spp_read_data ();
+  else
+    return epp_read_data ();
+}
+
+
+static void
+epp_set_ai (unsigned char ai)
+{
+  current_ai = ai;
+  outportb (port_b, ai);
+}
+
+
+static void
+spp_set_ai (unsigned char ai)
+{
+  current_ai = ai;
+  outportb (port_8, ai);
+  outportb (port_a, 9);
+  outportb (port_a, 1);
+}
+
+
+static void
+set_ai (unsigned char ai)
+{
+  if (port_mode == UCON64_SPP)
+    spp_set_ai (ai);
+  else
+    epp_set_ai (ai);
+}
+
+
+static void
+epp_set_ai_data (unsigned char ai, unsigned char data)
+{
+  epp_set_ai (ai);
+  epp_write_data (data);
+}
+
+
+static void
+spp_set_ai_data (unsigned char ai, unsigned char data)
+{
+  spp_set_ai (ai);
+  spp_write_data (data);
+}
+
+
+static void
+set_ai_data (unsigned char ai, unsigned char data)
+{
+  if (port_mode == UCON64_SPP)
+    spp_set_ai_data (ai, data);
+  else
+    epp_set_ai_data (ai, data);
+}
+
+
+static void
+init_port (void)
+{
+#ifndef USE_PPDEV
+  clear_timeout
+#endif
+  set_data_write
+  set_ai_data (2, 0);
+}
+
+
+static void
+end_port (void)
+{
+  set_data_write
+  set_ai_data (2, 0);
+  reset_port
+}
+
+
+static int
+detect_linker (void)
+{
+  init_port ();
+  set_data_write
+  set_ai_data (1, 0x12);
+  set_ai_data (0, 0x34);
+  set_ai (1);
+  set_data_read
+  if (read_data () != 0x12)
+    return 0;
+  set_data_write
+  set_ai (0);
+  set_data_read
+  if (read_data () != 0x34)
+    return 0;
+  end_port ();
+  return 1;
+}
+
+
+static void
+select_address (unsigned int addr, int inc)
+{
+  unsigned char data = (((addr >> 14) & 0x3c) | ((addr >> 13) & 0x80) |
+                        (inc ? 0x01 : 0x00));   // a[20..16], auto-increment
+  if (data != ai_value[2])
+    set_ai_data (2, data);
+  set_ai_data (1, (unsigned char) ((addr >> 8) & 0xff)); // a[15..8]
+  set_ai_data (0, (unsigned char) (addr & 0xff)); // a[7..0]
+}
+
+
+static void
+write_address_data (unsigned int addr, unsigned char data)
+{
+  select_address (addr, 0);
+  set_ai_data (3, data);
+}
+
+
+static void
+send_command (unsigned char cmd)
+{
+  set_data_write
+  write_address_data (0x5555, 0xaa);
+  write_address_data (0x2aaa, 0x55);
+  write_address_data (0x5555, cmd);
+}
+
+
+static void
+reset_read (void)
+{
+  send_command (CMD_READ);
+}
+
+
+static unsigned char
+read_ai3_data (void)
+{
+  set_ai (3);
+  set_data_read
+  return read_data ();
+}
+
+
+static int
+detect_chip (void)
+{
+  int ai;
+  unsigned char info[4];
+
+  reset_read ();
+  reset_read ();
+  reset_read ();
+  send_command (CMD_INFO);
+  for (ai = 0; ai < 4; ai++)
+    {
+      set_data_write
+      select_address (ai, 0);
+      info[ai] = read_ai3_data ();
+    }
+  reset_read ();
+  if (((info[0] & 0x90) == 0x90) && (info[2] == 0x01) && (info[3] & 0x80))
+    return 1;
+  else
+    return 0;
+}
+
+
+static void
+select_chip (unsigned int addr)
+{
+  set_data_write
+  set_ai_data (2, 2);
+  set_ai_data (3, (unsigned char) ((addr & 0x200000) ? 1 : 2));
+  set_ai_data (2, 0);
+}
+
+
+static unsigned int
+cart_size (void)
+{
+  select_chip (0x000000);
+  if (!detect_chip ())
+    return 0;                                   // no cartridge found
+  select_chip (0x200000);
+  if (!detect_chip ())
+    return 0x200000;                            // 16 megabit
+  return 0x400000;                              // 32 megabit
+}
+
+
+static void
+read_blocks (unsigned int addr, unsigned char *buf, int blocks)
+{
+  int block, i, offset = 0;
+
+  select_chip (addr);
+  for (block = 0; block < blocks; block++)
+    {
+      set_data_write
+      select_address (addr | (block << 8), 1);
+      set_ai (3);
+      set_data_read
+      for (i = 0; i < 0x100; i++)
+        buf[offset++] = read_data ();
+    }
+}
+
+
+static int
+is_erased (unsigned char *buf, unsigned int len)
+{
+  unsigned int i;
+
+  for (i = 0; i < len; i++)
+    if (buf[i] != 0xff)
+      return 0;
+
+  return 1;
+}
+
+
+static int
+is_header (unsigned char *buf)
+{
+  char msg[0x1d];
+
+  strncpy (msg, (char *) buf, 0x1c);
+  msg[0x1c] = '\0';
+
+  if (strstr (msg, "COPYRIGHT") || strstr (msg, "SNK") ||
+      strstr (msg, "LICENSED") || strstr (msg, "CORPORATION"))
+    return 1;                                   // header found
+
+  return 0;                                     // other data found
+}
+
+
+static int
+same_header (unsigned char *header, unsigned char *buf)
+{
+  return (!memcmp (header, buf, 0x100));
+}
+
+
+static unsigned int
+game_info (unsigned int cart_size, char name[13], int *type)
+{
+  unsigned char header[0x100];
+  unsigned char buf[0x8000];
+
+  select_chip (0x000000);
+  read_blocks (0x000000, header, 1);
+
+  if (!is_header (header))
+    return 0;                                   // no game found
+
+  if (name)
+    {
+      strncpy (name, (char *) (header + 0x24), 12);
+      name[12] = '\0';
+    }
+
+  if (type)
+    {
+      if (strstr (name, "Multi"))
+        *type = TYPE_MULTI;
+      else if (header[0x23] == 0x10)
+        *type = TYPE_COLOR;
+      else
+        *type = TYPE_BW;
+    }
+
+  read_blocks (0x080000, buf, 128);
+  if ((same_header (buf, header)) || is_erased (buf, 0x8000))
+    return 0x080000;                            // 4 megabit
+
+  read_blocks (0x100000, buf, 128);
+  if ((same_header (buf, header)) || is_erased (buf, 0x8000))
+    return 0x100000;                            // 8 megabit
+
+  if (cart_size == 0x400000)
+    {
+      read_blocks (0x200000, buf, 128);
+      if (is_erased (buf, 0x8000))
+        return 0x200000;                        // 16 megabit
+      return 0x400000;                          // 32 megabit
+    }
+  return 0x200000;                              // 16 megabit
+}
+
+
+static void
+deinit_io (void)
+{
+  end_port ();
+}
+
+
+static void
+init_io (unsigned int port)
+{
+#ifndef USE_PPDEV
+  outportb ((unsigned short) (port_8 + 0x402), 0); // Set EPP mode for ECP chipsets
+#endif
+
+  port_8 = port;
+  port_9 = port + 1;
+  port_a = port + 2;
+  port_b = port + 3;
+  port_c = port + 4;
+
+  parport_print_info ();
+
+  if (ucon64.parport_mode == UCON64_EPP && port_8 != 0x3bc)
+    port_mode = UCON64_EPP;                     // if port == 0x3bc => no EPP available
+  else
+    port_mode = UCON64_SPP;
+
+  if (!detect_linker ())
+    {
+      port_mode = UCON64_SPP;
+      if (!detect_linker ())
+        {
+          fputs ("ERROR: Pocket Linker not found or not turned on\n", stderr);
+          deinit_io ();
+          exit (1);
+        }
+    }
+
+  // If we get here, a Pocket Linker was detected
+  if (port_mode == UCON64_EPP)
+    puts ("Pocket Linker found. EPP found");
+  else
+    puts ("Pocket Linker found. EPP not found or not enabled - SPP used");
+}
+
+
+static void
+set_address (unsigned int addr, int inc)
+{
+  set_ai_data (0, (unsigned char) (addr & 0xff)); // a[7..0]
+  set_ai_data (1, (unsigned char) ((addr >> 8) & 0xff)); // a[15..8]
+  set_ai_data (2, 0x02);                        // latch chip select
+  set_ai_data (3, (unsigned char) ~(1 << (addr >> 21))); // a[23..21]
+  set_ai_data (2, (unsigned char) ((((addr >> 16) & 0x0f) << 2) | // a[20..16], auto-increment
+                                   (((addr >> 20) & 0x01) << 7) | 
+                                   (inc ? 0x01 : 0x00)));
+  set_ai (3);
+}
+
+
+static int
+program (unsigned int addr, unsigned char data, int retries)
+{
+  int to = 10000;
+
+  set_data_write
+  set_address (fx005555 (addr), 0);             // program byte
+  write_data (0xaa);
+  set_address (fx002aaa (addr), 0);
+  write_data (0x55);
+  set_address (fx005555 (addr), 0);
+  write_data (0xa0);
+  set_address (addr, 0);
+  write_data (data);
+
+  set_data_read
+  while (to--)
+    {
+      unsigned char s = read_data ();
+      if ((s & 128) == 0)
+        return 0;                               // ok
+      if (s & 32)
+        {
+          int s = read_data ();
+          if ((s & 128) == 0)
+            return 0;                           // ok
+          if (data == read_data ())
+            return 0;                           // ok
+        }
+    }
+
+  set_data_write
+  set_address (addr, 0);
+  set_data_read
+  if (data == read_data ())
+    return 0;                                   // ok
+  if (retries == 0)
+    return 1;                                   // error
+  return program (addr, data, retries - 1);
+}
+
+
+static int
+write_block (unsigned int addr, unsigned char *buf)
+{
+  int count;
+  select_address (addr, 1);
+  for (count = 0; count < 0x100; count++)
+    {
+      if (buf[count] == 0xff)
+        continue;                               // nothing to write
+      if (program (addr + count, buf[count], 3))
+        {
+          select_address (addr + count, 0);
+          set_data_read
+          fprintf (stderr, "\nERROR: Programming failed at 0x%06x (w:0x%02x, "
+                           "r:0x%02x)\n", addr + count, buf[count], read_data ());
+          return 1;
+        }
+    }
+  return 0;
+}
+
+
+#if 0
+static int
+wait_erased (void)
+{
+  int i = 0;
+  unsigned char cur_byte, prev_byte;
+
+  prev_byte = read_ai3_data () & 0x40;
+  while (++i < 0x7ffffff)
+    {
+      cur_byte = read_data () & 0x40;
+      if (cur_byte == prev_byte)
+        return 0;                               // ok
+      prev_byte = cur_byte;
+    }
+  return 1;                                     // erase error
+}
+
+
+static int
+erase_chip (void)
+{
+  reset_read ();
+  send_command (CMD_ERASE);
+  write_address_data (0x5555, 0xaa);
+  write_address_data (0x2aaa, 0x55);
+  write_address_data (0x5555, 0x10);
+  return wait_erased ();
+}
+
+
+static int
+erase_cart (unsigned int size)
+{
+  unsigned int addr;
+  unsigned char buf[0x8000];
+
+  for (addr = 0x000000; addr < size; addr += 0x200000)
+    {
+      select_chip (addr);
+      if (erase_chip ())
+        {
+          fprintf (stderr, "ERROR: Erase chip %d failed\n", addr >> 21);
+          return 1;
+        }
+
+      read_blocks (0x000000, buf, 128);
+      if (!is_erased (buf, 0x8000))
+        {
+          fprintf (stderr, "ERROR: Erase chip %d verify failed\n", addr >> 21);
+          return 1;
+        }
+    }
+}
+#endif
+
+
+static int
+erase (void)
+{
+  unsigned int addr;
+
+  for (addr = 0; addr < 0x400000; addr += 0x200000)
+    {
+      set_data_write
+      set_address (fx005555 (addr), 0);
+      write_data (0xaa);
+      set_address (fx002aaa (addr), 0);
+      write_data (0x55);
+      set_address (fx005555 (addr), 0);
+      write_data (0x80);
+      set_address (fx005555 (addr), 0);
+      write_data (0xaa);
+      set_address (fx002aaa (addr), 0);
+      write_data (0x55);
+      set_address (fx005555 (addr), 0);
+      write_data (0x10);
+
+      set_data_read
+      while (~read_data () & 0x80)              // wait for completion
+        ;
+    }
+  return 0;
+}
+
+
+int
+pl_read_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned int blocksleft, c_size, size, address = 0;
+  unsigned char buffer[0x8000];
+  time_t starttime;
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  init_io (parport);
+
+  c_size = cart_size ();
+
+  if (c_size == 0x00)                           // Check for cartridge
+    {
+      fputs ("ERROR: No cartridge detected\n", stderr);
+      deinit_io ();
+      exit (1);
+    }
+
+  size = game_info (c_size, NULL, NULL);
+
+  if (size == 0x00)
+    {
+      fputs ("ERROR: No game detected\n", stderr);
+      deinit_io ();
+      exit (1);
+    }
+
+  reset_read ();
+
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  blocksleft = size >> 15;
+  starttime = time (NULL);
+
+  while (blocksleft-- > 0)
+    {
+      read_blocks (address, buffer, 128);
+      fwrite (buffer, 1, 0x8000, file);
+      address += 0x8000;
+      ucon64_gauge (starttime, address, size);
+    }
+
+  fclose (file);
+  deinit_io ();
+
+  return 0;
+}
+
+
+int
+pl_write_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned int size, bytesread, bytessent = 0, address = 0;
+  unsigned char buffer[0x100];
+  time_t starttime;
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  init_io (parport);
+  size = fsizeof (filename);
+
+  erase ();
+  reset_read ();
+
+  printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  starttime = time (NULL);
+
+  while ((bytesread = fread (buffer, 1, 0x100, file)))
+    {
+      if (write_block (address, buffer))
+        break;                                  // write failed
+      bytessent += bytesread;
+      address += 0x100;
+      ucon64_gauge (starttime, bytessent, size);
+    }
+
+  fclose (file);
+  deinit_io ();
+
+  return 0;
+}
+
+
+int
+pl_info (unsigned int parport)
+{
+  unsigned int c_size, g_size;
+  int g_type;
+  char g_name[13];
+
+  init_io (parport);
+
+  c_size = cart_size ();
+  if (c_size == 0)
+    {
+      printf ("No cartridge found\n");
+      deinit_io ();
+      return 0;
+    }
+
+  g_size = game_info (c_size, g_name, &g_type);
+
+  if (g_size == 0)
+    printf ("No game found\n");
+  else
+    {
+      printf ("Game name: \"%s\"\n", g_name);
+      printf ("Game type: %s\n", (g_type == TYPE_COLOR) ? "Color" : "B&W");
+      printf ("Game size: %d Bytes (%.4f Mb)\n", g_size, (float) g_size / MBIT);
+    }
+  deinit_io ();
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 33 - 0
ucon64/2.0/src/backup/pl.h

@@ -0,0 +1,33 @@
+/*
+pl.h - Pocket Linker support for uCON64
+
+Copyright (c) 2004 Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+Partly based on PokeLink - Copyright (c) Dark Fader / BlackThunder
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef PL_H
+#define PL_H
+
+extern const st_getopt2_t pl_usage[];
+
+#ifdef  USE_PARALLEL
+extern int pl_info (unsigned int parport);
+extern int pl_read_rom (const char *filename, unsigned int parport);
+extern int pl_write_rom (const char *filename, unsigned int parport);
+#endif
+
+#endif

+ 1068 - 0
ucon64/2.0/src/backup/psxpblib.c

@@ -0,0 +1,1068 @@
+/*
+ *
+ * PSX Peripheral Bus Library v1.4 17/01/00 Richard Davies
+ * <mailto:richard@debaser.force9.co.uk>
+ *
+ * Revision History:
+ * v1.4 - 17/01/00 Win32 / Win32 DLL support, rewrite and bug fixes
+ * v1.3 - 21/12/99 Linux support and bug fixes
+ * v1.1 - 26/09/99 Minor Controller detection improvements.
+ * v1.0 - 17/07/99 Initial release (based on PSXTest v1.1 by me).
+ *
+ * see psxpblib.h for details.
+ *
+ */
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <fcntl.h>
+#include <ctype.h>
+#ifdef  HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef  HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "misc/misc.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "psxpblib.h"
+#include "misc/parallel.h"
+
+
+#ifdef  USE_PARALLEL
+
+static unsigned char psx_parallel_out_0 = 0xff;
+static unsigned char psx_parallel_out_2 = 0x00;
+
+
+/*
+ *
+ * sets clock for conport connected to parallel port base
+ *
+ */
+void
+psx_clk (int base, int conport, int on)
+{
+  if (conport == 8)
+    {
+      if (on)
+        {
+          /* set controller clock high */
+          psx_parallel_out_2 |= LPT_STR;
+        }
+      else
+        {
+          /* set controller clock low */
+          psx_parallel_out_2 &= ~LPT_STR;
+        }
+
+      psx_outportb (base + 2, psx_parallel_out_2);
+    }
+  else
+    {
+      if (on)
+        {
+          /* set controller clock high */
+          psx_parallel_out_0 |= LPT_D2;
+        }
+      else
+        {
+          /* set controller clock low */
+          psx_parallel_out_0 &= ~LPT_D2;
+        }
+
+      psx_outportb (base + 0, psx_parallel_out_0);
+    }
+}
+
+
+/*
+ *
+ * sets att for conport connected to parallel port base
+ *
+ */
+void
+psx_att (int base, int conport, int on)
+{
+  /* bits 3-7 base + 0 (pins 5 to 9 parallel port) */
+  const int power = LPT_D3 | LPT_D4 | LPT_D5 | LPT_D6 | LPT_D7;
+  /* bits 1-6 base + 0 (pins 3, 5, 6, 7 and 8 parallel port) */
+  unsigned char att_array[] =
+    { LPT_D1, LPT_D1, LPT_D3, LPT_D4, LPT_D5, LPT_D6, LPT_D7 };
+  unsigned char att;
+
+  if (conport == 8)
+    {
+      if (on)
+        {
+          /* set controller att high */
+          psx_parallel_out_2 |= LPT_INI;
+        }
+      else
+        {
+          /* set controller att low */
+          psx_parallel_out_2 &= ~LPT_INI;
+        }
+
+      psx_outportb (base + 2, psx_parallel_out_2);
+    }
+  else
+    {
+      /* powers up all parallel port driven conports */
+      psx_parallel_out_0 |= power;
+
+      att = att_array[conport - 1];
+
+      if (on)
+        {
+          /* set controller att high */
+          psx_parallel_out_0 |= att;
+        }
+      else
+        {
+          /* set controller att low */
+          psx_parallel_out_0 &= ~att;
+        }
+
+      psx_outportb (base + 0, psx_parallel_out_0);
+    }
+}
+
+
+/*
+ *
+ * sets command for conport connected to parallel port base
+ *
+ */
+void
+psx_cmd (int base, int conport, int on)
+{
+  if (conport == 8)
+    {
+      if (on)
+        {
+          /* set controller cmd high */
+          psx_parallel_out_2 |= LPT_AUT;
+        }
+      else
+        {
+          /* set controller cmd low */
+          psx_parallel_out_2 &= ~LPT_AUT;
+        }
+
+      psx_outportb (base + 2, psx_parallel_out_2);
+    }
+  else
+    {
+      if (on)
+        {
+          /* set controller cmd high */
+          psx_parallel_out_0 |= LPT_D0;
+        }
+      else
+        {
+          /* set controller cmd low */
+          psx_parallel_out_0 &= ~LPT_D0;
+        }
+
+      psx_outportb (base + 0, psx_parallel_out_0);
+    }
+}
+
+
+/*
+ *
+ * tests data for conport connected to parallel port base, returns 1 if high
+ *
+ */
+int
+psx_dat (int base, int conport)
+{
+  if (conport == 2)
+    {
+      if (psx_inportb (base + 1) & LPT_SEL)
+        {
+          return 1;
+        }
+      else
+        {
+          return 0;
+        }
+    }
+  else
+    {
+      if (psx_inportb (base + 1) & LPT_ACK)
+        {
+          return 1;
+        }
+      else
+        {
+          return 0;
+        }
+    }
+}
+
+
+/*
+ *
+ * tests ack for conport connected to parallel port base, returns 1 if high
+ *
+ */
+int
+psx_ack (int base, int conport)
+{
+  if (conport == 2)
+    {
+      if (psx_inportb (base + 2) & LPT_ERR)
+        {
+          return 1;
+        }
+      else
+        {
+          return 0;
+        }
+    }
+  else if (conport == 8)
+    {
+      if (psx_inportb (base + 1) & LPT_SEL)
+        {
+          return 1;
+        }
+      else
+        {
+          return 0;
+        }
+    }
+  else
+    {
+      if (psx_inportb (base + 1) & LPT_PAP)
+        {
+          return 1;
+        }
+      else
+        {
+          return 0;
+        }
+    }
+}
+
+
+/*
+ *
+ * wait for delay * (psx_outportb() execution time) 
+ *
+ */
+void
+psx_delay (int base, int delay)
+{
+  int i;
+
+  for (i = 0; i < delay; i++)
+    {
+      psx_outportb (base + 0, psx_parallel_out_0);
+    }
+}
+
+
+/*
+ *
+ * send byte as a command to conport connected to parallel port base
+ * assumes clock high and the attention for conport
+ *
+ */
+unsigned char
+psx_sendbyte (int base, int conport, int delay, unsigned char byte, int wait)
+{
+  int i;
+  unsigned char data;
+
+  data = 0;
+
+  /* for each bit in byte */
+  for (i = 0; i < 8; i++)
+    {
+      psx_delay (base, delay);
+      psx_cmd (base, conport, byte & (1 << i)); /* send the (i+1)th bit of byte to any listening controller */
+      psx_clk (base, conport, 0);       /* clock low */
+      psx_delay (base, delay);
+      data |= (psx_dat (base, conport) ? (1 << i) : 0); /* read the (i+1)th bit of data from conport */
+      psx_clk (base, conport, 1);       /* clock high */
+    }
+
+  /* wait for controller ack */
+  for (i = 0; wait && i < 10240 && psx_ack (base, conport); i++)
+    ;
+
+  return data;
+}
+
+
+/*
+ *
+ * sets clock high and gets the attention of conport, use before psx_sendbyte()
+ *
+ */
+void
+psx_sendinit (int base, int conport, int delay)
+{
+//  psx_obtain_io_permission (base);// uCON64 already enabled access to I/O ports
+  psx_att (base, conport, 1);   /* set att on for conport */
+  psx_clk (base, conport, 1);   /* clock high */
+  psx_cmd (base, conport, 1);   /* set command on for conport */
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_att (base, conport, 0);   /* set att off for conport */
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+}
+
+
+/*
+ *
+ * use after psx_sendbyte()
+ *
+ */
+void
+psx_sendclose (int base, int conport, int delay)
+{
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_att (base, conport, 1);   /* set att on for conport */
+  psx_cmd (base, conport, 1);   /* set command on for conport */
+  psx_clk (base, conport, 1);   /* clock high */
+  psx_delay (base, delay);
+}
+
+
+/*
+ *
+ * send string as a series of commands to conport connected to parallel port base
+ *
+ */
+void
+psx_sendstring (int base, int conport, int delay, int string[])
+{
+  int i;
+
+  psx_sendinit (base, conport, delay);
+
+  /* for each byte in string */
+  for (i = 0; string[i + 1] != -1; i++)
+    {
+      /* send byte i and wait for conport ack */
+      psx_sendbyte (base, conport, delay, (unsigned char) string[i], 0);
+      psx_delay (base, delay);
+    }
+
+  /* send the last byte in string and don't wait for ack */
+  psx_sendbyte (base, conport, delay, (unsigned char) string[i], 0);
+
+  psx_sendclose (base, conport, delay);
+}
+
+
+/*
+ *
+ * tests for the presence of a controller on conport:tap connected to base
+ * returns the type if present, otherwise -1
+ *
+ */
+int
+psx_controller_detect (int base, int conport, int tap, int delay)
+{
+  unsigned char ack;
+  int type, length;
+
+  psx_sendinit (base, conport, delay);
+
+  psx_sendbyte (base, conport, delay, (unsigned char) tap, 0);
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  ack = psx_sendbyte (base, conport, delay, 0x42, 0);
+  psx_delay (base, delay);
+
+  psx_sendclose (base, conport, delay);
+
+  type = (ack & 0xf0) >> 4;
+  length = 2 * (ack & 0x0f);
+
+  /* check the controller has a legal packet length */
+  if (!((length > 0) && (length < PSX_MAX_DATA)))
+    return -1;
+
+  /* check the controller has a legal id */
+  if (!((type > 0) && (type < 0x0f) && (type != 0x08)))
+    return -1;
+
+  return type;
+}
+
+
+/*
+ *
+ * reads a controller on conport:tap connected to base returns the data
+ * if present, otherwise -1
+ *
+ */
+PSX_CON_BUF *
+psx_controller_read (int base, int conport, int tap, int delay)
+{
+  unsigned char ack;
+  int i;
+  static PSX_CON_BUF psx_con_buf;
+
+  psx_sendinit (base, conport, delay);
+
+  psx_sendbyte (base, conport, delay, (unsigned char) tap, 0);
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  ack = psx_sendbyte (base, conport, delay, 0x42, 0);
+  psx_delay (base, delay);
+
+  psx_con_buf.type = (ack & 0xf0) >> 4;
+  psx_con_buf.length = 2 * (ack & 0x0f);
+
+  /* check the controller has a legal packet length */
+  if (!((psx_con_buf.length > 0) && (psx_con_buf.length < PSX_MAX_DATA)))
+    {
+      psx_sendclose (base, conport, delay);
+      return NULL;
+    }
+
+  /* check the controller has a legal id */
+  if (!
+      ((psx_con_buf.type > 0) && (psx_con_buf.type < 0x0f)
+       && (psx_con_buf.type != 0x08)))
+    {
+      psx_sendclose (base, conport, delay);
+      return NULL;
+    }
+
+  psx_sendbyte (base, conport, delay, 0x00, 0);
+  psx_delay (base, delay);
+  for (i = 0; i < psx_con_buf.length; i++)
+    {
+      psx_con_buf.data[i] = psx_sendbyte (base, conport, delay, 0x00, 0);
+      psx_delay (base, delay);
+    }
+  psx_sendclose (base, conport, delay);
+
+  return &psx_con_buf;
+}
+
+
+/*
+ *
+ * sends force feedback/shock init command sequence to conport:tap on port base
+ * (also initialises crash protection for some controllers)
+ *
+ */
+void
+psx_controller_vinit (int base, int conport, int tap, int delay)
+{
+  int i, vibrate_init_string[3][11] =
+    {
+      {tap, 0x43, 0x00, 0x01, 0x00, 0x01, -1},
+      {tap, 0x4d, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01, -1},
+      {tap, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -1},
+    };
+
+  for (i = 0; i < 3; i++)
+    {
+      psx_delay (base, delay);
+      psx_delay (base, delay);
+      psx_delay (base, delay);
+      psx_sendstring (base, conport, delay, vibrate_init_string[i]);
+    }
+}
+
+
+/*
+ *
+ * sends the dual shock command sequence to conport:tap on port base
+ *
+ */
+void
+psx_controller_vshock (int base, int conport, int tap, int delay, int shock,
+                       int rumble)
+{
+  int dualshock_string[7] = { tap, 0x42, 0x00, shock, rumble, 0x01, -1 };
+
+  psx_controller_vinit (base, conport, tap, delay);
+
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_delay (base, delay);
+  psx_sendstring (base, conport, delay, dualshock_string);
+}
+
+
+/*
+ *
+ * Reads a single frame (128 bytes) from Memory Card on conport base:tap
+ *
+ */
+unsigned char *
+psx_memcard_read_frame (int base, int conport, int tap, int delay, int frame)
+{
+  int i, xor_val;
+  static unsigned char data[128], c_data;
+  unsigned char cmd_rstring_hdr[4] = { (0x80 + (unsigned char) tap), 0x52, 0x00, 0x00 },
+                chk_rstring_hdr[2] = { 0x5a, 0x5d },
+                cmd_rstring_adr[2] = { (frame >> 8) & 0xff, frame & 0xff },
+                chk_rstring_ack[1] = { 0x5c },
+                chk_rstring_sfl[1] = { 0x5d },
+                chk_rstring_efl[1] = { 0x47 };
+
+  psx_sendinit (base, conport, delay);
+
+  /* send header */
+  for (i = 0; i < 2; i++)
+    {
+      psx_sendbyte (base, conport, delay, cmd_rstring_hdr[i], 0);
+      psx_delay (base, delay);
+      psx_delay (base, delay);
+    }
+
+  for (; i < 4; i++)
+    {
+      c_data = psx_sendbyte (base, conport, delay, cmd_rstring_hdr[i], 0);
+      psx_delay (base, delay);
+      psx_delay (base, delay);
+
+      if (c_data != chk_rstring_hdr[i - 2])
+        {
+          psx_sendclose (base, conport, delay);
+          return NULL;
+        }
+    }
+
+  /* send read address */
+  for (i = 0; i < 2; i++)
+    {
+      psx_sendbyte (base, conport, delay, cmd_rstring_adr[i], 0);
+      psx_delay (base, delay);
+    }
+
+  /* receive command ack (have to wait for this) */
+  c_data = psx_sendbyte (base, conport, delay, 0x00, 1);
+
+  if (c_data != chk_rstring_ack[0])
+    {
+      psx_sendclose (base, conport, delay);
+      return NULL;
+    }
+
+  /* receive start of data flag */
+  i = 0;
+
+  while (c_data != chk_rstring_sfl[0])
+    {
+      c_data = psx_sendbyte (base, conport, delay, 0x00, 0);
+      psx_delay (base, delay);
+
+      i++;
+
+      if (i > 255)
+        {
+          psx_sendclose (base, conport, delay);
+          return NULL;
+        }
+    }
+
+  /* receive read address */
+  for (i = 0; i < 2; i++)
+    {
+      c_data = psx_sendbyte (base, conport, delay, 0x00, 0);
+      psx_delay (base, delay);
+
+      if (c_data != cmd_rstring_adr[i])
+        {
+          psx_sendclose (base, conport, delay);
+          return NULL;
+        }
+    }
+
+  /* receive data */
+  for (i = 0; i < 128; i++)
+    {
+      data[i] = psx_sendbyte (base, conport, delay, 0x00, 0);
+      psx_delay (base, delay);
+    }
+
+  /* receive xor */
+  c_data = psx_sendbyte (base, conport, delay, 0x00, 0);
+  psx_delay (base, delay);
+
+  /* test xor */
+  xor_val = 0;
+
+  xor_val ^= cmd_rstring_adr[0];
+  xor_val ^= cmd_rstring_adr[1];
+
+  for (i = 0; i < 128; i++)
+    xor_val ^= data[i];
+
+  if (xor_val != c_data)
+    {
+      psx_sendclose (base, conport, delay);
+      return NULL;
+    }
+
+  /* receive end of data flag */
+  c_data = psx_sendbyte (base, conport, delay, 0x00, 0);
+  psx_delay (base, delay);
+
+  if (c_data != chk_rstring_efl[0])
+    {
+      psx_sendclose (base, conport, delay);
+      return NULL;
+    }
+
+  psx_sendclose (base, conport, delay);
+
+  return data;
+}
+
+
+/*
+ *
+ * Writes a single frame (128 bytes) to Memory Card on conport base:tap
+ *
+ */
+int
+psx_memcard_write_frame (int base, int conport, int tap, int delay, int frame,
+                         unsigned char *data_f)
+{
+  int i, xor_val;
+  unsigned char c_data,
+                cmd_wstring_hdr[4] = { (0x80 + (unsigned char) tap), 0x57, 0x00, 0x00 },
+                chk_wstring_hdr[2] = { 0x5a, 0x5d },
+                cmd_wstring_adr[2] = { (frame >> 8) & 0xff, frame & 0xff },
+                chk_wstring_emk[2] = { 0x5c, 0x5d },
+                chk_wstring_efl[1] = { 0x47 };
+
+  psx_sendinit (base, conport, delay);
+
+  /* send header (have to wait for this) */
+  for (i = 0; i < 2; i++)
+    {
+      psx_sendbyte (base, conport, delay, cmd_wstring_hdr[i], 1);
+    }
+
+  for (; i < 4; i++)
+    {
+      c_data = psx_sendbyte (base, conport, delay, cmd_wstring_hdr[i], 1);
+
+      if (c_data != chk_wstring_hdr[i - 2])
+        {
+          psx_sendclose (base, conport, delay);
+          return -1;
+        }
+    }
+
+  /* send write address */
+  for (i = 0; i < 2; i++)
+    {
+      psx_sendbyte (base, conport, delay, cmd_wstring_adr[i], 0);
+      psx_delay (base, delay);
+    }
+  /* send data */
+  for (i = 0; i < 128; i++)
+    {
+      psx_sendbyte (base, conport, delay, data_f[i], 0);
+      psx_delay (base, delay);
+    }
+
+  /* calculate xor */
+  xor_val = 0;
+
+  xor_val ^= cmd_wstring_adr[0];
+  xor_val ^= cmd_wstring_adr[1];
+
+  for (i = 0; i < 128; i++)
+    xor_val ^= data_f[i];
+
+  /* send xor */
+  psx_sendbyte (base, conport, delay, (unsigned char) xor_val, 0);
+  psx_delay (base, delay);
+
+  /* receive end mark */
+  for (i = 0; i < 2; i++)
+    {
+      c_data = psx_sendbyte (base, conport, delay, 0x00, 1);
+
+      if (c_data != chk_wstring_emk[i])
+        {
+          psx_sendclose (base, conport, delay);
+          return -1;
+        }
+    }
+
+  /* receive end of data flag */
+  c_data = psx_sendbyte (base, conport, delay, 0x00, 1);
+
+  if (c_data != chk_wstring_efl[0])
+    {
+      psx_sendclose (base, conport, delay);
+      return -1;
+    }
+
+  psx_sendclose (base, conport, delay);
+
+  return (int) ((unsigned char) xor_val);
+}
+
+
+/*
+ *
+ * Reads a single block (64 frames) from Memory Card on conport base:tap
+ *
+ */
+unsigned char *
+psx_memcard_read_block (int base, int conport, int tap, int delay, int block)
+{
+  int i, j;
+  static unsigned char data_b[8192], *data_f;
+
+  for (i = 0; i < 64; i++)
+    {
+      data_f =
+        psx_memcard_read_frame (base, conport, tap, delay, (block * 64) + i);
+
+      if (data_f != NULL)
+        {
+          for (j = 0; j < 128; j++)
+            data_b[(i * 128) + j] = data_f[j];
+        }
+      else
+        {
+          return NULL;
+        }
+    }
+
+  return data_b;
+}
+
+
+/*
+ *
+ * Writes a single block (64 frames) to Memory Card on conport base:tap
+ *
+ */
+int
+psx_memcard_write_block (int base, int conport, int tap, int delay, int block,
+                         unsigned char *data_b)
+{
+  int i, xor_val;
+
+  for (i = 0; i < 64; i++)
+    {
+      xor_val =
+        psx_memcard_write_frame (base, conport, tap, delay, (block * 64) + i,
+                                 &(data_b[128 * i]));
+
+      if (xor_val == -1)
+        {
+          return -1;
+        }
+    }
+
+  return 1;
+}
+
+
+/*
+ *
+ * Reads the info associated with block from the directory
+ *
+ */
+PSX_MCB_INFO_DIR *
+psx_mcb_read_dir (int base, int conport, int tap, int delay, int block)
+{
+  int i, xor_val;
+  unsigned char *data_f;
+  static PSX_MCB_INFO_DIR mcb_info_dir;
+
+  mcb_info_dir.read = 1;
+
+  /* check memory card state */
+  data_f = psx_memcard_read_frame (base, conport, tap, delay, 0);
+
+  if (data_f == NULL)
+    {
+      mcb_info_dir.read = 0;
+      return &mcb_info_dir;
+    }
+
+  if ((data_f[0] != 'M') && (data_f[1] != 'C'))
+    {
+      mcb_info_dir.read = 0;
+      return &mcb_info_dir;
+    }
+
+  for (i = 2; i < 127; i++)
+    {
+      if (data_f[i] != 0x00)
+        {
+          mcb_info_dir.read = 0;
+          return &mcb_info_dir;
+        }
+    }
+
+  if (data_f[127] != 0x0e)
+    {
+      mcb_info_dir.read = 0;
+      return &mcb_info_dir;
+    }
+
+  /* read block's directory */
+  data_f = psx_memcard_read_frame (base, conport, tap, delay, block);
+
+  if (data_f == NULL)
+    {
+      mcb_info_dir.read = 0;
+      return &mcb_info_dir;
+    }
+
+  xor_val = 0;
+
+  for (i = 0; i < 127; i++)
+    xor_val ^= data_f[i];
+
+  if (xor_val != data_f[127])
+    {
+      mcb_info_dir.read = 0;
+      return &mcb_info_dir;
+    }
+
+  mcb_info_dir.linktype = data_f[0] & 0x0f;
+
+  /* Only test if first link */
+  if (mcb_info_dir.linktype == PSX_MCB_LTYPE_FIRST)
+    {
+      if (data_f[10] != 'B')
+        {
+          mcb_info_dir.read = 0;
+          return &mcb_info_dir;
+        }
+    }
+
+  mcb_info_dir.state = (data_f[0] >> 4) & 0x0f;
+
+  mcb_info_dir.bytes = (data_f[4] << 16) + (data_f[5] << 8) + data_f[6];
+
+  mcb_info_dir.next = data_f[8];        /* 0 to 14 */
+
+  mcb_info_dir.territory = data_f[11];  /* E, A or I */
+
+  for (i = 0; i < 10; i++)
+    mcb_info_dir.code[i] = (char) data_f[12 + i];
+
+  mcb_info_dir.code[i] = '\0';
+
+  for (i = 0; i < 8; i++)
+    mcb_info_dir.filename[i] = data_f[22 + i];
+
+  mcb_info_dir.filename[i] = '\0';
+
+  return &mcb_info_dir;
+}
+
+
+/*
+ *
+ * Reads the info associated with block from it's data
+ *
+ */
+PSX_MCB_INFO_DAT *
+psx_mcb_read_dat (int base, int conport, int tap, int delay, int block)
+{
+  int i, j;
+  unsigned char *data_f;
+  static PSX_MCB_INFO_DAT mcb_info_dat;
+
+  mcb_info_dat.read = 1;
+
+  if ((block < 1) || (block > 15))
+    {
+      mcb_info_dat.read = 0;
+      return &mcb_info_dat;
+    }
+
+  data_f =
+    psx_memcard_read_frame (base, conport, tap, delay, (block * 64) + 0);
+
+  if (!data_f)
+    {
+      mcb_info_dat.read = 0;
+      return &mcb_info_dat;
+    }
+
+  if ((data_f[0] != 'S') || (data_f[1] != 'C'))
+    {
+      mcb_info_dat.read = 0;
+      return &mcb_info_dat;
+    }
+
+  mcb_info_dat.icon_valid = (data_f[2] >> 4) & 0x0f;
+  mcb_info_dat.icon_frames = data_f[2] & 0x0f;
+  mcb_info_dat.blocks = data_f[3];
+
+  /* bodged character set conversion */
+  j = 0;
+  for (i = 0; i < 91; i += 2)
+    {
+      if (data_f[4 + i] != 0x00)
+        {
+          if (data_f[4 + i] == 0x81)
+            {
+              if (data_f[5 + i] == 0x7c)
+                mcb_info_dat.name[j] = '-';
+              else if (data_f[5 + i] == 0x40)
+                mcb_info_dat.name[j] = ' ';
+              else if (data_f[5 + i] == 0x46)
+                mcb_info_dat.name[j] = ':';
+              else if (data_f[5 + i] == 0x5e)
+                mcb_info_dat.name[j] = '/';
+              else if (data_f[5 + i] == 0x49)
+                mcb_info_dat.name[j] = '!';
+              else if (data_f[5 + i] == 0x93)
+                mcb_info_dat.name[j] = '%';
+              else if (data_f[5 + i] == 0x68)
+                mcb_info_dat.name[j] = '\"';
+              else if (data_f[5 + i] == 0x44)
+                mcb_info_dat.name[j] = '.';
+              else if (data_f[5 + i] == 0x6d)
+                mcb_info_dat.name[j] = '[';
+              else if (data_f[5 + i] == 0x6e)
+                mcb_info_dat.name[j] = ']';
+              else if (data_f[5 + i] == 0x69)
+                mcb_info_dat.name[j] = '(';
+              else if (data_f[5 + i] == 0x6a)
+                mcb_info_dat.name[j] = ')';
+              else
+                mcb_info_dat.name[j] = '?';
+            }
+          else if (data_f[4 + i] == 0x82)
+            {
+              if ((data_f[5 + i] > 0x4e) && (data_f[5 + i] < 0x80))
+                mcb_info_dat.name[j] = data_f[5 + i] - 0x1f;
+              else if ((data_f[4 + i] > 0x80) && (data_f[5 + i] < 0x9a))
+                mcb_info_dat.name[j] = data_f[5 + i] - 0x20;
+              else
+                mcb_info_dat.name[j] = '?';
+            }
+          else
+            {
+              mcb_info_dat.name[j] = data_f[4 + i];
+
+              j++;
+              if (data_f[5 + i] != 0x00)
+                mcb_info_dat.name[j] = data_f[5 + i];
+              else
+                {
+                  mcb_info_dat.name[j] = '\0';
+                  i = 91;
+                }
+            }
+        }
+      else
+        {
+          mcb_info_dat.name[j] = '\0';
+          i = 91;
+        }
+      j++;
+    }
+
+  return &mcb_info_dat;
+}
+
+
+/*
+ *
+ * Merges the info associated with block from the directory and it's data
+ *
+ */
+PSX_MCB_INFO *
+psx_mcb_info_merge (PSX_MCB_INFO_DIR mcb_info_dir,
+                    PSX_MCB_INFO_DAT mcb_info_dat, PSX_MCB_INFO * mcb_info)
+{
+  mcb_info->read = 1;
+
+  if (!mcb_info_dir.read)
+    {
+      mcb_info->read = 0;
+      return mcb_info;
+    }
+
+  if ((mcb_info_dir.linktype == PSX_MCB_LTYPE_FIRST) && (!mcb_info_dat.read))
+    {
+      mcb_info->read = 0;
+      return mcb_info;
+    }
+
+  strcpy (mcb_info->filename, mcb_info_dir.filename);
+  strcpy (mcb_info->code, mcb_info_dir.code);
+  mcb_info->territory = mcb_info_dir.territory;
+  mcb_info->bytes = mcb_info_dir.bytes;
+  mcb_info->state = mcb_info_dir.state;
+  mcb_info->linktype = mcb_info_dir.linktype;
+  mcb_info->next = mcb_info_dir.next;
+  if (mcb_info_dir.linktype == PSX_MCB_LTYPE_FIRST)
+    {
+      strcpy (mcb_info->name, mcb_info_dat.name);
+      mcb_info->blocks = mcb_info_dat.blocks;
+      mcb_info->icon_valid = mcb_info_dat.icon_valid;
+      mcb_info->icon_frames = mcb_info_dat.icon_frames;
+    }
+  else
+    {
+      mcb_info->name[0] = '\0';
+      mcb_info->blocks = 0;
+      mcb_info->icon_valid = 0;
+      mcb_info->icon_frames = 0;
+    }
+
+  return mcb_info;
+}
+
+
+/*
+ *
+ * Reads the info associated with block from the directory and it's data
+ *
+ */
+PSX_MCB_INFO *
+psx_mcb_read_info (int base, int conport, int tap, int delay, int block)
+{
+  PSX_MCB_INFO_DIR *mcb_info_dir;
+  PSX_MCB_INFO_DAT *mcb_info_dat;
+  static PSX_MCB_INFO mcb_info;
+
+  mcb_info_dir = psx_mcb_read_dir (base, conport, tap, delay, block);
+  mcb_info_dat = psx_mcb_read_dat (base, conport, tap, delay, block);
+  return psx_mcb_info_merge (*mcb_info_dir, *mcb_info_dat, &mcb_info);
+}
+
+#endif // USE_PARALLEL

+ 208 - 0
ucon64/2.0/src/backup/psxpblib.h

@@ -0,0 +1,208 @@
+/*
+ *
+ * PSX Peripheral Bus Library v1.4 17/01/00 Richard Davies
+ * <http://www.debaser.force9.co.uk/>
+ * <mailto:richard@debaser.force9.co.uk>
+ *
+ * Revision History:
+ * v1.4 - 17/01/00 Win32 / Win32 DLL support and bug fixes
+ * v1.3 - 21/12/99 Linux support and bug fixes
+ * v1.1 - 26/09/99 Minor Controller detection improvements.
+ * v1.0 - 17/07/99 Initial release (based on PSXTest v1.1 by me).
+ *
+ * see psxpblib.txt for details.
+ *
+ */
+
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef USE_PARALLEL
+
+// outportb() and inportb() are only present in uCON64 if USE_PARALLEL is defined
+#define psx_outportb(P, B) outportb((unsigned short) (P), (unsigned char) (B))
+#define psx_inportb(P) inportb((unsigned short) (P))
+
+#ifdef  __MSDOS__
+#include <stdlib.h>
+#include <pc.h>
+#endif
+
+#define LPT1_BASE 0x378
+#define LPT2_BASE 0x278
+#define LPT3_BASE 0x3bc
+
+#define LPT_D0  0x01            /* pin 2 */
+#define LPT_D1  0x02            /* pin 3 */
+#define LPT_D2  0x04            /* pin 4 */
+#define LPT_D3  0x08            /* pin 5 */
+#define LPT_D4  0x10            /* pin 6 */
+#define LPT_D5  0x20            /* pin 7 */
+#define LPT_D6  0x40            /* pin 8 */
+#define LPT_D7  0x80            /* pin 9 */
+#define LPT_AUT 0x08            /* pin 14 */
+#define LPT_SEL 0x10            /* pin 13 */
+#define LPT_PAP 0x20            /* pin 12 */
+#define LPT_ACK 0x40            /* pin 10 */
+#define LPT_ERR 0x02            /* pin 15 */
+#define LPT_INI 0x04            /* pin 16 */
+#define LPT_STR 0x08            /* pin 1 */
+
+#define PSX_MAX_CONPORTS 8
+#define PSX_MAX_TAPS     4
+#define PSX_MAX_DELAY    10
+
+#define PSX_MAX_DATA     30     /* maximum possible length of controller packet in bytes */
+
+                                /* JAP code  EUR code    Name */
+
+#define PSX_MOUSE        1      /* SCPH-1030 SCPH-????   Mouse */
+#define PSX_NEGCON       2      /* SLPH-0001 SLEH-0003   Namco neGcon
+                                   SLPH-0007             Nasca Pachinco Handle (untested); Twist = Twist, TW = B
+                                   SLPH-0015             ? Volume Controller (untested); Rotation = Twist, A = Start, B = A
+                                   Puchi Carat paddle controller (not working!); Rotation = Twist, A = Start, B = A
+                                   SLPH-???? SLEH-0005   MadKatz Steering Wheel (twitchy) */
+#define PSX_KONAMIGUN    3      /* SLPH-???? SLPH-????   Konami Lightgun (untested) */
+#define PSX_DIGITAL      4      /* SCPH-1010 SCPH 1080 E Controller
+                                   SCPH-1110 SCPH-????   Analog Joystick - Digital Mode
+                                   SCPH-???? SCPH-1180 E Analog Controller - Digital Mode
+                                   SCPH-1150 SCPH-1200 E Dual Shock Analog Controller - Digital Mode
+                                   SLPH-???? SLEH-0011   Ascii Resident Evil Pad
+                                   SLPH-???? SLEH-0004   Namco Arcade Stick (untested)
+                                   Twin Stick
+                                   Blaze Pro-Shock Arcade Stick */
+#define PSX_ANALOG_GREEN 5      /* SCPH-1110 SCPH-????   Analog Joystick - Analog Mode (untested)
+                                   SCPH-???? SCPH-1180 E Analog Controller - Analog Green Mode */
+#define PSX_GUNCON       6      /* SLPH-???? SLEH-0007   Namco G-con45 */
+#define PSX_ANALOG_RED   7      /* SCPH-1150 SCPH-1200 E Dual Shock Analog Controller - Analog Red Mode */
+                                /* SCPH-???? SCPH-1180 E Analog Controller - Analog Red Mode */
+#define PSX_JOGCON       14     /* SLPH-???? SLEH-0020   Namco Jogcon */
+/*#define PSX_MULTITAP   17    SCPH-1070 SCPH-1070 E Multi tap */
+
+#define PSX_MCB_STATE_OK       0x05
+#define PSX_MCB_STATE_DELETED  0x0a
+#define PSX_MCB_STATE_RESERVED 0x0f
+
+#define PSX_MCB_LTYPE_NONE     0x00
+#define PSX_MCB_LTYPE_FIRST    0x01
+#define PSX_MCB_LTYPE_MIDDLE   0x02
+#define PSX_MCB_LTYPE_LAST     0x03
+#define PSX_MCB_LTYPE_RESERVED 0x0f
+
+#define PSX_MCB_ICON_VALID     0x01
+
+
+
+typedef struct
+{
+  unsigned char type;
+  unsigned char length;
+  unsigned char data[PSX_MAX_DATA];
+} PSX_CON_BUF;
+
+typedef struct
+{
+  unsigned char read;
+  char filename[9];
+  char code[11];
+  char territory;               /* E, A or I */
+  int bytes;
+  unsigned char state;          /* PSX_MCB_STAT_* or unknown */
+  unsigned char linktype;       /* PSX_MCB_LTYPE_* or unknowm */
+  unsigned char next;           /* 0 to 14 */
+} PSX_MCB_INFO_DIR;
+
+typedef struct
+{
+  unsigned char read;
+  char name[92];
+  unsigned char blocks;
+  unsigned char icon_valid;
+  unsigned char icon_frames;
+} PSX_MCB_INFO_DAT;
+
+typedef struct
+{
+  unsigned char scanned;
+  unsigned char read;
+  char name[92];
+  char filename[9];
+  char code[11];
+  char territory;
+  unsigned char state;
+  unsigned char blocks;
+  int bytes;
+  unsigned char linktype;
+  unsigned char next;
+  unsigned char icon_valid;
+  unsigned char icon_frames;
+} PSX_MCB_INFO;
+
+
+/* sets clock for conport connected to parallel port base */
+void psx_clk (int base, int conport, int on);
+/* sets att for conport connected to parallel port base */
+void psx_att (int base, int conport, int on);
+/* sets command for conport connected to parallel port base */
+void psx_cmd (int base, int conport, int on);
+/* tests data for conport connected to parallel port base, returns 1 if high */
+int psx_dat (int base, int conport);
+/* tests ack for conport connected to parallel port base, returns 1 if high */
+int psx_ack (int base, int conport);
+/* wait for delay * (outportb() execution time) */
+void psx_delay (int base, int delay);
+
+/* send byte as a command to conport connected to parallel port base
+ * assumes clock high and the attention of conport */
+unsigned char psx_sendbyte (int base, int conport, int delay,
+                            unsigned char byte, int wait);
+/* get io permissions under linux*/
+int psx_obtain_io_permission (int base);
+/* sets clock high and gets the attention of conport, use before psx_sendbyte() */
+void psx_sendinit (int base, int conport, int delay);
+/* use after psx_sendbyte() */
+void psx_sendclose (int base, int conport, int delay);
+/* send string as a series of commands to conport connected to parallel port base */
+void psx_sendstring (int base, int conport, int delay, int string[]);
+
+/* tests for the presence of a controller on conport:tap connected to base
+ * returns the type if present, otherwise -1 */
+int psx_controller_detect (int base, int conport, int tap, int delay);
+/* reads a controller on conport:tap connected to base returns the data
+ * if present, otherwise -1 */
+PSX_CON_BUF *psx_controller_read (int base, int conport, int tap, int delay);
+/* sends force feedback/shock init command sequence to conport:tap on port base
+ * (also initialises crash protection for some controllers) */
+void psx_controller_vinit (int base, int conport, int tap, int delay);
+/* sends the dual shock command sequence to conport:tap on port base */
+void psx_controller_vshock (int base, int conport, int tap, int delay,
+                            int shock, int rumble);
+
+/* Reads a single frame (128 bytes) from Memory Card on conport base:tap */
+unsigned char *psx_memcard_read_frame (int base, int conport, int tap, int delay,
+                                       int frame);
+/* Writes a single frame (128 bytes) to Memory Card on conport base:tap */
+int psx_memcard_write_frame (int base, int conport, int tap, int delay,
+                             int frame, unsigned char *data_f);
+/* Reads a single block (64 frames) from Memory Card on conport base:tap */
+unsigned char *psx_memcard_read_block (int base, int conport, int tap, int delay,
+                                       int block);
+/* Writes a single block (64 frames) to Memory Card on conport base:tap */
+int psx_memcard_write_block (int base, int conport, int tap, int delay,
+                             int block, unsigned char *data_b);
+
+/* Reads the info associated with block from the directory */
+PSX_MCB_INFO_DIR *psx_mcb_read_dir (int base, int conport, int tap, int delay,
+                                    int block);
+/* Prints the info associated with block from it's data */
+PSX_MCB_INFO_DAT *psx_mcb_read_dat (int base, int conport, int tap, int delay,
+                                    int block);
+/* Merges the info associated with block from the directory and it's data */
+PSX_MCB_INFO *psx_mcb_info_merge (PSX_MCB_INFO_DIR mcb_info_dir,
+                                  PSX_MCB_INFO_DAT mcb_info_dat,
+                                  PSX_MCB_INFO * mcb_info);
+/* Reads the info associated with block from the directory and it's data */
+PSX_MCB_INFO *psx_mcb_read_info (int base, int conport, int tap, int delay,
+                                 int block);
+
+#endif // USE_PARALLEL

+ 371 - 0
ucon64/2.0/src/backup/sflash.c

@@ -0,0 +1,371 @@
+/*
+sflash.h - Super Flash flash card programmer support for uCON64
+
+Copyright (c) 2004 JohnDie
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "misc/parallel.h"
+#include "misc/itypes.h"
+#include "misc/misc.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/file.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "tototek.h"
+#include "sflash.h"
+
+
+const st_getopt2_t sflash_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Super Flash flash card programmer"/*"2004 ToToTEK Multi Media http://www.tototek.com"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xsf", 0, 0, UCON64_XSF,
+      NULL, "send/receive ROM to/from Super Flash flash card programmer\n" OPTION_LONG_S "port=PORT\n"
+      "receives automatically (64 Mbits) when ROM does not exist",
+      &ucon64_wf[WF_OBJ_SNES_DEFAULT_STOP_NO_SPLIT_NO_ROM]
+    },
+    {
+      "xsfs", 0, 0, UCON64_XSFS,
+      NULL, "send/receive SRAM to/from Super Flash flash card programmer\n" OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM]
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef  USE_PARALLEL
+
+static void eep_reset (void);
+static void write_rom_by_byte (int *addr, unsigned char *buf);
+static void write_rom_by_page (int *addr, unsigned char *buf);
+static void write_ram_by_byte (int *addr, unsigned char *buf);
+static void write_ram_by_page (int *addr, unsigned char *buf);
+
+
+void
+eep_reset (void)
+{
+  ttt_rom_enable ();
+  ttt_write_mem (0x400000, 0xff);               // reset EEP chip 2
+  ttt_write_mem (0, 0xff);                      // reset EEP chip 1
+  ttt_write_mem (0x600000, 0xff);               // reset EEP chip 2
+  ttt_write_mem (0x200000, 0xff);               // reset EEP chip 1
+  ttt_rom_disable ();
+}
+
+
+void
+write_rom_by_byte (int *addr, unsigned char *buf)
+{
+  int x;
+
+  for (x = 0; x < 0x4000; x++)
+    {
+      ttt_write_byte_intel (*addr, buf[*addr & 0x3fff]);
+      (*addr)++;
+    }
+}
+
+
+void
+write_rom_by_page (int *addr, unsigned char *buf)
+{
+  int x;
+
+  for (x = 0; x < 0x200; x++)
+    {
+      ttt_write_page_rom (*addr, buf);
+      (*addr) += 0x20;
+    }
+}
+
+
+void
+write_ram_by_byte (int *addr, unsigned char *buf)
+{
+  int x, i = *addr & 0x3fff;
+
+  for (x = 0; x < 0x4000; x++, i = (i + 1) & 0x3fff)
+    {
+      ttt_write_byte_ram (*addr, buf[i]);
+      (*addr)++;
+    }
+}
+
+
+void
+write_ram_by_page (int *addr, unsigned char *buf)
+{
+  int x;
+
+  for (x = 0; x < 0x40; x++)
+    {
+      ttt_write_page_ram (*addr, buf);
+      (*addr) += 0x100;
+    }
+}
+
+
+int
+sf_read_rom (const char *filename, unsigned int parport, int size)
+{
+  FILE *file;
+  unsigned char buffer[0x100];
+  int blocksleft, address = 0;
+  time_t starttime;
+  void (*read_block) (int, unsigned char *) = ttt_read_rom_w; // ttt_read_rom_b
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  ttt_init_io (parport);
+
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  blocksleft = size >> 8;
+  eep_reset ();
+  ttt_rom_enable ();
+  if (read_block == ttt_read_rom_w)
+    ttt_set_ai_data (6, 0x94);          // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS
+  starttime = time (NULL);
+  while (blocksleft-- > 0)
+    {
+      read_block (address, buffer);             // 0x100 bytes read
+      if (read_block == ttt_read_rom_b)
+        ucon64_bswap16_n (buffer, 0x100);
+      fwrite (buffer, 1, 0x100, file);
+      address += 0x100;
+      if ((address & 0x3fff) == 0)
+        ucon64_gauge (starttime, address, size);
+    }
+  // ttt_read_rom_b() calls ttt_rom_disable()
+  if (read_block == ttt_read_rom_w)
+    ttt_rom_disable ();
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+
+int
+sf_write_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char buffer[0x4000], game_table[0x80];
+  int game_no, romsize, size, address = 0, bytesread, bytessend = 0;
+  time_t starttime;
+  void (*write_block) (int *, unsigned char *) = write_rom_by_page; // write_rom_by_byte
+  (void) write_rom_by_byte;
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  ttt_init_io (parport);
+
+  size = fsizeof (filename);
+  printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  eep_reset ();
+  if (ttt_get_id () != 0x8917)                  // Intel 64J3
+    {
+      fputs ("ERROR: Super Flash flash card (programmer) not detected\n", stderr);
+      fclose (file);
+      ttt_deinit_io ();
+      exit (1);
+    }
+
+  starttime = time (NULL);
+
+  // Erase last block now, because we have to write to it anyway later. Erasing
+  //  it later could erase part of a game.
+  ttt_erase_block (0x7e0000);
+
+  fseek (file, 0x4000, SEEK_SET);
+  bytesread = fread (game_table, 1, 0x80, file);
+  if (bytesread != 0x80)
+    {
+      fputs ("ERROR: Could not read game table from file\n", stderr);
+      fclose (file);
+      ttt_deinit_io ();
+      return 0;
+    }
+
+  fseek (file, 0x8000, SEEK_SET);
+
+  for (game_no = 0; game_no < 4; game_no++)
+    {
+      if (game_table[game_no * 0x20] == 0)
+        continue;
+
+      romsize = game_table[game_no * 0x20 + 0x1f] * 0x8000;
+
+      switch (game_table[game_no * 0x20 + 0x1d] & 0x60)
+        {
+        case 0x00:
+          address = 0x000000;
+          break;
+        case 0x40:
+          address = 0x200000;
+          break;
+        case 0x20:
+          address = 0x400000;
+          break;
+        case 0x60:
+          address = 0x600000;
+          break;
+        // no default case because we handled all possible cases
+        }
+
+      eep_reset ();
+
+      while (romsize && (bytesread = fread (buffer, 1, 0x4000, file)))
+        {
+          if ((address & 0x1ffff) == 0)
+            ttt_erase_block (address);
+          if (address < 0x7f8000)               // We mustn't write to the loader space
+            write_block (&address, buffer);
+          bytessend += bytesread;
+          ucon64_gauge (starttime, bytessend, size);
+          romsize -= 0x4000;
+        }
+    }
+
+  fseek (file, 0, SEEK_SET);
+  romsize = 0x8000;
+  address = 0x7f8000;
+  while (romsize && (bytesread = fread (buffer, 1, 0x4000, file)))
+    {
+      write_block (&address, buffer);
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      romsize -= 0x4000;
+    }
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+
+int
+sf_read_sram (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char buffer[0x100];
+  int blocksleft, address, bytesreceived = 0, size;
+  time_t starttime;
+  void (*read_block) (int, unsigned char *) = ttt_read_ram_w; // ttt_read_ram_w
+
+  address = 0xfe0000;                           // SRAM is stored at 0xfe0000
+  size = 0x020000;                              // SRAM size is 1024 kbit
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+
+  ttt_init_io (parport);
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+//  ttt_ram_enable ();          // The next ttt_set_ai_data also enables ram access
+  ttt_set_ai_data (6, 0x98);  // Enable cartridge access, auto address increment
+
+  blocksleft = size >> 8;
+  starttime = time (NULL);
+  while (blocksleft-- > 0)
+    {
+      read_block (address, buffer);             // 0x100 bytes read
+      fwrite (buffer, 1, 0x100, file);
+      address += 0x100;
+      bytesreceived += 0x100;
+      if ((address & 0x3fff) == 0)
+        ucon64_gauge (starttime, bytesreceived, size);
+    }
+
+  ttt_ram_disable ();
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+
+int
+sf_write_sram (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char buffer[0x4000];
+  int size, bytesread, bytessend = 0, address;
+  time_t starttime;
+  void (*write_block) (int *, unsigned char *) = write_ram_by_byte; // write_ram_by_page
+  (void) write_ram_by_page;
+
+  size = fsizeof (filename);
+  address = 0xfe0000;
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+
+  ttt_init_io (parport);
+  printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  ttt_ram_enable ();
+
+  starttime = time (NULL);
+  while ((bytesread = fread (buffer, 1, 0x4000, file)))
+    {
+      write_block (&address, buffer);             // 0x4000 bytes write
+      bytessend += bytesread;
+      if ((address & 0x3fff) == 0)
+        ucon64_gauge (starttime, bytessend, size);
+    }
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 33 - 0
ucon64/2.0/src/backup/sflash.h

@@ -0,0 +1,33 @@
+/*
+sflash.h - Super Flash flash card programmer support for uCON64
+
+Copyright (c) 2004 JohnDie
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef SFLASH_H
+#define SFLASH_H
+
+extern const st_getopt2_t sflash_usage[];
+
+#ifdef USE_PARALLEL
+extern int sf_read_rom (const char *filename, unsigned int parport, int size);
+extern int sf_write_rom (const char *filename, unsigned int parport);
+extern int sf_read_sram (const char *filename, unsigned int parport);
+extern int sf_write_sram (const char *filename, unsigned int parport);
+#endif
+
+#endif

+ 407 - 0
ucon64/2.0/src/backup/smc.c

@@ -0,0 +1,407 @@
+/*
+smc.c - Super Magic Card support for uCON64
+
+Copyright (c) 2003 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "misc/misc.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/file.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "ffe.h"
+#include "smc.h"
+
+
+const st_getopt2_t smc_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Super Magic Card"/*"1993/1994/1995/19XX Front Far East/FFE http://www.front.com.tw"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xsmc", 0, 0, UCON64_XSMC, // send only
+      NULL, "send ROM (in FFE format) to Super Magic Card; " OPTION_LONG_S "port=PORT",
+      &ucon64_wf[WF_OBJ_NES_DEFAULT_STOP_NO_SPLIT]
+    },
+    {
+      "xsmcr", 0, 0, UCON64_XSMCR,
+      NULL, "send/receive RTS data to/from Super Magic Card; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when RTS file does not exist",
+      &ucon64_wf[WF_OBJ_NES_STOP_NO_ROM]
+    },
+#endif
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef USE_PARALLEL
+
+#define BUFFERSIZE 8192
+
+
+static int get_blocks1 (unsigned char *header);
+static int get_blocks2 (unsigned char *header);
+static int get_blocks3 (unsigned char *header);
+
+
+int
+get_blocks1 (unsigned char *header)
+{
+  if (header[7] == 0xaa)
+    return header[3];
+  if (header[0] & 0x30)
+    return header[0] & 0x20 ? 32 : 16;          // 0x10 => 16; 0x20/0x30 => 32
+  else
+    switch (header[1] >> 5)
+      {
+      case 0:
+      case 4:
+        return 16;
+      case 1:
+      case 2:
+      case 3:
+        return 32;
+      default:                                  // 5/6/7
+        return 4;
+      }
+}
+
+
+int
+get_blocks2 (unsigned char *header)
+{
+  if (header[0] & 0x30)
+    return header[0] & 0x10 ? 32 : 16;          // 0x10/0x30 => 32; 0x20 => 16
+  else
+    return 0;
+}
+
+
+int
+get_blocks3 (unsigned char *header)
+{
+  if (header[7] == 0xaa)
+    return header[4];
+  if (header[0] & 0x30)
+    return 0;
+  else
+    switch (header[1] >> 5)
+      {
+      default:                                  // 0/1/2/3
+        return 0;
+      case 4:
+      case 5:
+        return 4;
+      case 6:
+        return 2;
+      case 7:
+        return 1;
+      }
+}
+
+
+int
+smc_write_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int bytesread, bytessend, size, offset, n_blocks1, n_blocks2, n_blocks3, n;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  ffe_send_command0 (0x4500, 2);
+  ffe_send_command0 (0x42fd, 0x20);
+  ffe_send_command0 (0x43fc, 0);
+
+  fread (buffer, 1, SMC_HEADER_LEN, file);
+
+  n_blocks1 = get_blocks1 (buffer);
+  n_blocks2 = get_blocks2 (buffer);
+  n_blocks3 = get_blocks3 (buffer);
+
+  size = (n_blocks1 + n_blocks2 + n_blocks3) * 8 * 1024 + 8 +
+         (buffer[0] & SMC_TRAINER ? 512 : 0);
+  printf ("Send: %d Bytes (%.4f Mb)\n", size, (float) size / MBIT);
+
+  ffe_send_block (0x5020, buffer, 8);           // send "file control block"
+  bytessend = 8;
+
+  if (buffer[1] >> 5 > 4)
+    offset = 12;
+  else
+    offset = 0;
+
+  if (buffer[0] & SMC_TRAINER)                  // send trainer if present
+    {
+      fread (buffer, 1, 512, file);
+      ffe_send_block (0x600, buffer, 512);
+      bytessend += 512;
+    }
+
+  printf ("Press q to abort\n\n");
+  starttime = time (NULL);
+
+  for (n = 0; n < n_blocks1; n++)
+    {
+      ffe_send_command0 (0x4507, (unsigned char) (n + offset));
+      if ((bytesread = fread (buffer, 1, BUFFERSIZE, file)) == 0)
+        break;
+      ffe_send_block (0x6000, buffer, bytesread);
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      ffe_checkabort (2);
+    }
+
+  for (n = 0; n < n_blocks2; n++)
+    {
+      ffe_send_command0 (0x4507, (unsigned char) (n + 32));
+      if ((bytesread = fread (buffer, 1, BUFFERSIZE, file)) == 0)
+        break;
+      ffe_send_block (0x6000, buffer, bytesread);
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      ffe_checkabort (2);
+    }
+
+  ffe_send_command0 (0x2001, 0);
+
+  for (n = 0; n < n_blocks3; n++)
+    {
+      if (n == 0)
+        {
+          ffe_send_command0 (0x4500, 0x22);
+          ffe_send_command0 (0x42ff, 0x30);
+          if ((bytesread = fread (buffer, 1, BUFFERSIZE, file)) == 0)
+            break;
+          ffe_send_block (0x6000, buffer, bytesread);
+        }
+      else
+        {
+          int m;
+
+          ffe_send_command0 (0x4500, 7);
+          for (m = 0; m < 8; m++)
+            ffe_send_command0 ((unsigned short) (0x4510 + m), (unsigned char) (n * 8 + m));
+          if ((bytesread = fread (buffer, 1, BUFFERSIZE, file)) == 0)
+            break;
+          ffe_send_block2 (0, buffer, bytesread);
+        }
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      ffe_checkabort (2);
+    }
+
+  for (n = 0x4504; n < 0x4508; n++)
+    ffe_send_command0 ((unsigned short) n, 0);
+  for (n = 0x4510; n < 0x451c; n++)
+    ffe_send_command0 ((unsigned short) n, 0);
+
+  ffe_send_command (5, 1, 0);
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+smc_read_rts (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int bytesreceived = 0, size, n;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = 0x68 + 4 * 1024 + 5 * 8 * 1024;
+  printf ("Receive: %d Bytes\n", size);
+  memset (buffer, 0, SMC_HEADER_LEN);
+  buffer[8] = 0xaa;
+  buffer[9] = 0xbb;
+  buffer[10] = 1;
+
+  printf ("Press q to abort\n\n");
+  starttime = time (NULL);
+
+  ffe_send_command (5, 3, 0);
+  ffe_receive_block (0x5840, buffer + 0x100, 0x68);
+  fwrite (buffer, 1, SMC_HEADER_LEN, file);
+  bytesreceived += 0x68;
+
+  ffe_send_command0 (0x4500, 0x32);
+  ffe_send_command0 (0x42ff, 0x30);
+  ffe_receive_block (0x6000, buffer, BUFFERSIZE / 2); // 0x1000
+  fwrite (buffer, 1, BUFFERSIZE / 2, file);
+
+  bytesreceived += BUFFERSIZE / 2;
+  ucon64_gauge (starttime, bytesreceived, size);
+  ffe_checkabort (2);
+
+  for (n = 2; n <= 0x22; n += 0x20)
+    {
+      ffe_send_command0 (0x4500, (unsigned char) n);
+      ffe_receive_block (0x6000, buffer, BUFFERSIZE); // 0x2000
+      fwrite (buffer, 1, BUFFERSIZE, file);
+
+      bytesreceived += BUFFERSIZE;
+      ucon64_gauge (starttime, bytesreceived, size);
+      ffe_checkabort (2);
+    }
+
+  for (n = 1; n <= 3; n++)
+    {
+      ffe_send_command0 (0x43fc, (unsigned char) n);
+      if (n == 1)
+        ffe_send_command0 (0x2001, 0);
+      ffe_receive_block2 (0, buffer, BUFFERSIZE); // 0x2000
+      fwrite (buffer, 1, BUFFERSIZE, file);
+
+      bytesreceived += BUFFERSIZE;
+      ucon64_gauge (starttime, bytesreceived, size);
+      ffe_checkabort (2);
+    }
+
+  ffe_send_command0 (0x43fc, 0);
+  ffe_send_command0 (0x2001, 0x6b);
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+smc_write_rts (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int bytessend = 0, size, n;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = 0x68 + 4 * 1024 + 5 * 8 * 1024;
+  printf ("Send: %d Bytes\n", size);
+  fread (buffer, 1, SMC_HEADER_LEN, file);
+
+  printf ("Press q to abort\n\n");
+  starttime = time (NULL);
+
+  ffe_send_command (5, 3, 0);
+  ffe_send_block (0x5840, buffer + 0x100, 0x68);
+  bytessend += 0x68;
+
+  ffe_send_command0 (0x4500, 0x32);
+  ffe_send_command0 (0x42ff, 0x30);
+  fread (buffer, 1, BUFFERSIZE / 2, file);
+  ffe_send_block (0x6000, buffer, BUFFERSIZE / 2); // 0x1000
+
+  bytessend += BUFFERSIZE / 2;
+  ucon64_gauge (starttime, bytessend, size);
+  ffe_checkabort (2);
+
+  for (n = 2; n <= 0x22; n += 0x20)
+    {
+      ffe_send_command0 (0x4500, (unsigned char) n);
+      fread (buffer, 1, BUFFERSIZE, file);
+      ffe_send_block (0x6000, buffer, BUFFERSIZE); // 0x2000
+
+      bytessend += BUFFERSIZE;
+      ucon64_gauge (starttime, bytessend, size);
+      ffe_checkabort (2);
+    }
+
+  for (n = 1; n <= 3; n++)
+    {
+      ffe_send_command0 (0x43fc, (unsigned char) n);
+      if (n == 1)
+        ffe_send_command0 (0x2001, 0);
+      fread (buffer, 1, BUFFERSIZE, file);
+      ffe_send_block2 (0, buffer, BUFFERSIZE); // 0x2000
+
+      bytessend += BUFFERSIZE;
+      ucon64_gauge (starttime, bytessend, size);
+      ffe_checkabort (2);
+    }
+
+  ffe_send_command0 (0x43fc, 0);
+  ffe_send_command0 (0x2001, 0x6b);
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 47 - 0
ucon64/2.0/src/backup/smc.h

@@ -0,0 +1,47 @@
+/*
+smc.h - Super Magic Card support for uCON64
+
+Copyright (c) 2003 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef SMC_H
+#define SMC_H
+
+extern const st_getopt2_t smc_usage[];
+
+typedef struct st_smc_header
+{
+  unsigned char emulation1;
+  unsigned char emulation2;
+  unsigned char pad[6];
+  unsigned char id1;
+  unsigned char id2;
+  unsigned char type;
+  unsigned char pad2[501];
+} st_smc_header_t;
+
+#define SMC_HEADER_START 0
+#define SMC_HEADER_LEN (sizeof (st_smc_header_t))
+#define SMC_TRAINER 0x40
+
+#ifdef USE_PARALLEL
+extern int smc_write_rom (const char *filename, unsigned int parport);
+extern int smc_read_rts (const char *filename, unsigned int parport);
+extern int smc_write_rts (const char *filename, unsigned int parport);
+#endif
+
+#endif // SMC_H

+ 343 - 0
ucon64/2.0/src/backup/smd.c

@@ -0,0 +1,343 @@
+/*
+smd.c - Super Magic Drive support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2001 - 2003 dbjh
+
+           
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "misc/misc.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/file.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "ffe.h"
+#include "smd.h"
+
+
+const st_getopt2_t smd_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Super Com Pro/Super Magic Drive/SMD"/*"19XX Front Far East/FFE http://www.front.com.tw"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xsmd", 0, 0, UCON64_XSMD,
+      NULL, "send/receive ROM to/from Super Magic Drive/SMD; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when ROM does not exist",
+      &ucon64_wf[WF_OBJ_GEN_DEFAULT_STOP_NO_SPLIT_NO_ROM]
+    },
+    {
+      "xsmds", 0, 0, UCON64_XSMDS,
+      NULL, "send/receive SRAM to/from Super Magic Drive/SMD; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_GEN_STOP_NO_ROM]
+    },
+#endif // USE_PARALLEL
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+// the following two functions are used by non-transfer code in genesis.c and sms.c
+void
+smd_interleave (unsigned char *buffer, int size)
+// Convert binary data to the SMD interleaved format
+{
+  int count, offset;
+  unsigned char block[16384];
+
+  for (count = 0; count < size / 16384; count++)
+    {
+      memcpy (block, &buffer[count * 16384], 16384);
+      for (offset = 0; offset < 8192; offset++)
+        {
+          buffer[(count * 16384) + 8192 + offset] = block[offset << 1];
+          buffer[(count * 16384) + offset] = block[(offset << 1) + 1];
+        }
+    }
+}
+
+
+void
+smd_deinterleave (unsigned char *buffer, int size)
+{
+  int count, offset;
+  unsigned char block[16384];
+
+  for (count = 0; count < size / 16384; count++)
+    {
+      memcpy (block, &buffer[count * 16384], 16384);
+      for (offset = 0; offset < 8192; offset++)
+        {
+          buffer[(count * 16384) + (offset << 1)] = block[offset + 8192];
+          buffer[(count * 16384) + (offset << 1) + 1] = block[offset];
+        }
+    }
+}
+
+
+#ifdef  USE_PARALLEL
+
+#define BUFFERSIZE      16384
+
+
+int
+smd_read_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer, byte;
+  int size, blocksdone = 0, blocksleft, bytesreceived = 0;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  ffe_send_command (1, 0xdff1, 1);
+  byte = ffe_receiveb ();
+  if ((0x81 ^ byte) != ffe_receiveb ())
+    printf ("received data is corrupt\n");
+
+  blocksleft = 8 * byte;
+  if (blocksleft == 0)
+    {
+      fprintf (stderr, "ERROR: There is no cartridge present in the Super Magic Drive\n");
+      fclose (file);
+      remove (filename);
+      exit (1);
+    }
+
+  memset (buffer, 0, SMD_HEADER_LEN);
+  buffer[0] = blocksleft;
+  buffer[1] = 3;
+  buffer[8] = 0xaa;
+  buffer[9] = 0xbb;
+  buffer[10] = 6;
+  fwrite (buffer, 1, SMD_HEADER_LEN, file);     // write header
+
+  size = blocksleft * 16384;                    // size in bytes for ucon64_gauge() below
+  printf ("Receive: %d Bytes (%.4f Mb)\n", size, (float) size / MBIT);
+
+  wait2 (32);
+  ffe_send_command0 (0x2001, 0);
+
+  printf ("Press q to abort\n\n");
+
+  starttime = time (NULL);
+  while (blocksleft > 0)
+    {
+      ffe_send_command (5, (unsigned short) blocksdone, 0);
+      ffe_receive_block (0x4000, buffer, BUFFERSIZE);
+      blocksdone++;
+      blocksleft--;
+      fwrite (buffer, 1, BUFFERSIZE, file);
+
+      bytesreceived += BUFFERSIZE;
+      ucon64_gauge (starttime, bytesreceived, size);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+smd_write_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int bytesread, bytessend, blocksdone = 0, fsize;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  fsize = fsizeof (filename);
+  printf ("Send: %d Bytes (%.4f Mb)\n", fsize, (float) fsize / MBIT);
+
+  fread (buffer, 1, SMD_HEADER_LEN, file);
+  ffe_send_block (0xdc00, buffer, SMD_HEADER_LEN); // send header
+  bytessend = SMD_HEADER_LEN;
+
+  ffe_send_command0 (0x2001, 0);
+
+  printf ("Press q to abort\n\n");
+
+  starttime = time (NULL);
+  while ((bytesread = fread (buffer, 1, BUFFERSIZE, file)))
+    {
+      ffe_send_command (5, (unsigned short) blocksdone, 0);
+      ffe_send_block (0x8000, buffer, bytesread);
+      blocksdone++;
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, fsize);
+      ffe_checkabort (2);
+    }
+
+  // ROM dump > 128 16 KB blocks? (=16 Mb (=2 MB))
+  ffe_send_command0 (0x2001, (unsigned char) (blocksdone > 0x80 ? 7 : 3));
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+smd_read_sram (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int blocksleft, bytesreceived = 0;
+  unsigned short address;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  printf ("Receive: %d Bytes\n", 32 * 1024);
+  memset (buffer, 0, SMD_HEADER_LEN);
+  buffer[8] = 0xaa;
+  buffer[9] = 0xbb;
+  buffer[10] = 7;
+  fwrite (buffer, 1, SMD_HEADER_LEN, file);
+
+  ffe_send_command0 (0x2001, 4);
+
+  printf ("Press q to abort\n\n");
+
+  blocksleft = 2;                               // SRAM is 2*16 KB
+  address = 0x4000;
+  starttime = time (NULL);
+  while (blocksleft > 0)
+    {
+      ffe_receive_block (address, buffer, BUFFERSIZE);
+      blocksleft--;
+      address += 0x4000;
+      fwrite (buffer, 1, BUFFERSIZE, file);
+
+      bytesreceived += BUFFERSIZE;
+      ucon64_gauge (starttime, bytesreceived, 32 * 1024);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+smd_write_sram (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int bytesread, bytessend = 0, size;
+  unsigned short address;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = fsizeof (filename) - SMD_HEADER_LEN;
+  printf ("Send: %d Bytes\n", size);
+  fseek (file, SMD_HEADER_LEN, SEEK_SET);       // skip the header
+
+  ffe_send_command0 (0x2001, 4);
+
+  printf ("Press q to abort\n\n");
+
+  address = 0x4000;
+  starttime = time (NULL);
+  while ((bytesread = fread (buffer, 1, BUFFERSIZE, file)))
+    {
+      ffe_send_block (address, buffer, bytesread);
+      address += 0x4000;
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 53 - 0
ucon64/2.0/src/backup/smd.h

@@ -0,0 +1,53 @@
+/*
+smd.h - Super Magic Drive support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2002 - 2003 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef SMD_H
+#define SMD_H
+
+extern const st_getopt2_t smd_usage[];
+
+// For the header format, see ffe.h
+typedef struct st_smd_header
+{
+  unsigned char size;
+  unsigned char id0;
+  unsigned char split;
+  char pad[5];
+  unsigned char id1;
+  unsigned char id2;
+  unsigned char type;
+  char pad2[501];
+} st_smd_header_t;
+
+
+#ifdef USE_PARALLEL
+extern int smd_read_rom (const char *filename, unsigned int parport);
+extern int smd_write_rom (const char *filename, unsigned int parport);
+extern int smd_read_sram (const char *filename, unsigned int parport);
+extern int smd_write_sram (const char *filename, unsigned int parport);
+#endif
+// the following two functions are used by non-transfer code in genesis.c and sms.c
+extern void smd_interleave (unsigned char *buffer, int size);
+extern void smd_deinterleave (unsigned char *buffer, int size);
+
+#define SMD_HEADER_LEN (sizeof (st_smd_header_t))
+
+#endif // SMD_H

+ 348 - 0
ucon64/2.0/src/backup/smsgg-pro.c

@@ -0,0 +1,348 @@
+/*
+smsgg-pro.c - SMS-PRO/GG-PRO flash card programmer support for uCON64
+
+Copyright (c) 2004 dbjh
+
+Based on Delphi source code by ToToTEK Multi Media. Information in that source
+code has been used with permission. However, ToToTEK Multi Media explicitly
+stated that the information in that source code may be freely distributed.
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "misc/parallel.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/file.h"
+#include "misc/misc.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "tototek.h"
+#include "smsgg-pro.h"
+
+
+const st_getopt2_t smsggpro_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "SMS-PRO/GG-PRO flash card programmer"/*"2004 ToToTEK Multi Media http://www.tototek.com"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xgg", 0, 0, UCON64_XGG,
+      NULL, "send/receive ROM to/from SMS-PRO/GG-PRO flash card programmer\n" OPTION_LONG_S "port=PORT\n"
+      "receives automatically (32 Mbits) when ROM does not exist",
+      &ucon64_wf[WF_OBJ_SMS_DEFAULT_STOP_NO_SPLIT_NO_ROM]
+    },
+    {
+      "xggs", 0, 0, UCON64_XGGS,
+      NULL, "send/receive SRAM to/from SMS-PRO/GG-PRO flash card programmer\n" OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_SMS_STOP_NO_ROM]
+    },
+    {
+      "xggb", 1, 0, UCON64_XGGB,
+      "BANK", "send/receive SRAM to/from SMS-PRO/GG-PRO BANK\n"
+      "BANK can be a number from 1 to 4; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_SMS_STOP_NO_ROM]
+    },
+#endif // USE_PARALLEL
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+#ifdef  USE_PARALLEL
+
+static void eep_reset (void);
+static void write_rom_by_byte (int *addr, unsigned char *buf);
+static void write_rom_by_page (int *addr, unsigned char *buf);
+static void write_ram_by_byte (int *addr, unsigned char *buf);
+static void write_ram_by_page (int *addr, unsigned char *buf);
+
+
+void
+eep_reset (void)
+{
+  ttt_rom_enable ();
+  ttt_write_mem (0x000000, 0xff);               // reset EEP
+  ttt_write_mem (0x200000, 0xff);               // reset EEP
+  ttt_rom_disable ();
+}
+
+
+void
+write_rom_by_byte (int *addr, unsigned char *buf)
+{
+  int x;
+
+  for (x = 0; x < 0x4000; x++)
+    {
+      ttt_write_byte_sharp (*addr, buf[*addr & 0x3fff]);
+      (*addr)++;
+    }
+}
+
+
+void
+write_rom_by_page (int *addr, unsigned char *buf)
+{
+  int x;
+
+  for (x = 0; x < 0x200; x++)
+    {
+      ttt_write_page_rom (*addr, buf);
+      (*addr) += 0x20;
+    }
+}
+
+
+void
+write_ram_by_byte (int *addr, unsigned char *buf)
+{
+  int x;
+
+  for (x = 0; x < 0x4000; x++)
+    {
+      ttt_write_byte_ram (*addr, buf[*addr & 0x3fff]);
+      (*addr)++;
+    }
+}
+
+
+void
+write_ram_by_page (int *addr, unsigned char *buf)
+{
+  int x;
+
+  for (x = 0; x < 0x40; x++)
+    {
+      ttt_write_page_ram (*addr, buf);
+      (*addr) += 0x100;
+    }
+}
+
+
+int
+smsgg_read_rom (const char *filename, unsigned int parport, int size)
+{
+  FILE *file;
+  unsigned char buffer[0x100];
+  int blocksleft, address = 0;
+  time_t starttime;
+  void (*read_block) (int, unsigned char *) = ttt_read_rom_w; // ttt_read_rom_b
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  ttt_init_io (parport);
+
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  blocksleft = size >> 8;
+  eep_reset ();
+  ttt_rom_enable ();
+  if (read_block == ttt_read_rom_w)
+    ttt_set_ai_data (6, 0x94);          // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS
+  starttime = time (NULL);
+  while (blocksleft-- > 0)
+    {
+      read_block (address, buffer);             // 0x100 bytes read
+      fwrite (buffer, 1, 0x100, file);
+      address += 0x100;
+      if ((address & 0x3fff) == 0)
+        ucon64_gauge (starttime, address, size);
+    }
+  // original code doesn't call ttt_rom_disable() when byte-size function is
+  //  used (ttt_read_rom_b() calls it)
+  if (read_block == ttt_read_rom_w)
+    ttt_rom_disable ();
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+
+int
+smsgg_write_rom (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char buffer[0x4000];
+  int size, address = 0, bytesread, bytessend = 0;
+  time_t starttime;
+  void (*write_block) (int *, unsigned char *) = write_rom_by_page; // write_rom_by_byte
+  (void) write_rom_by_byte;
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  ttt_init_io (parport);
+
+  size = fsizeof (filename);
+  printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  eep_reset ();
+  if (ttt_get_id () != 0xb0d0)
+    {
+      fputs ("ERROR: SMS-PRO/GG-PRO flash card (programmer) not detected\n", stderr);
+      fclose (file);
+      ttt_deinit_io ();
+      exit (1);
+    }
+
+  starttime = time (NULL);
+  eep_reset ();
+  while ((bytesread = fread (buffer, 1, 0x4000, file)))
+    {
+      if ((address & 0xffff) == 0)
+        ttt_erase_block (address);
+      write_block (&address, buffer);
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+    }
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+
+int
+smsgg_read_sram (const char *filename, unsigned int parport, int start_bank)
+{
+  FILE *file;
+  unsigned char buffer[0x100];
+  int blocksleft, address, size, bytesreceived = 0;
+  time_t starttime;
+  void (*read_block) (int, unsigned char *) = ttt_read_ram_b; // ttt_read_ram_w
+
+  if (start_bank == -1)
+    {
+      address = 0;
+      size = 128 * 1024;
+    }
+  else
+    {
+      if (start_bank < 1 || start_bank > 4)
+        {
+          fputs ("ERROR: Bank must be a value 1 - 4\n", stderr);
+          exit (1);
+        }
+      address = (start_bank - 1) * 32 * 1024;
+      size = 32 * 1024;
+    }
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+
+  ttt_init_io (parport);
+  printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  if (read_block == ttt_read_ram_w)
+    {
+      ttt_ram_enable ();
+      ttt_set_ai_data (6, 0x98);        // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS
+    }
+//  else
+//    ttt_set_ai_data (6, 0x94);          // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS
+
+  blocksleft = size >> 8;
+  starttime = time (NULL);
+  while (blocksleft-- > 0)
+    {
+      read_block (address, buffer);             // 0x100 bytes read
+      fwrite (buffer, 1, 0x100, file);
+      address += 0x100;
+      bytesreceived += 0x100;
+      if ((address & 0x3fff) == 0)
+        ucon64_gauge (starttime, bytesreceived, size);
+    }
+  if (read_block == ttt_read_ram_w)
+    ttt_ram_disable ();
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+
+int
+smsgg_write_sram (const char *filename, unsigned int parport, int start_bank)
+{
+  FILE *file;
+  unsigned char buffer[0x4000];
+  int size, bytesread, bytessend = 0, address;
+  time_t starttime;
+  void (*write_block) (int *, unsigned char *) = write_ram_by_byte; // write_ram_by_page
+  (void) write_ram_by_page;
+
+  size = fsizeof (filename);
+  if (start_bank == -1)
+    address = 0;
+  else
+    {
+      if (start_bank < 1 || start_bank > 4)
+        {
+          fputs ("ERROR: Bank must be a value 1 - 4\n", stderr);
+          exit (1);
+        }
+      address = (start_bank - 1) * 32 * 1024;
+    }
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+
+  ttt_init_io (parport);
+  printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT);
+
+  starttime = time (NULL);
+  while ((bytesread = fread (buffer, 1, 0x4000, file)))
+    {
+      write_block (&address, buffer);
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+    }
+
+  fclose (file);
+  ttt_deinit_io ();
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 37 - 0
ucon64/2.0/src/backup/smsgg-pro.h

@@ -0,0 +1,37 @@
+/*
+smsgg-pro.h - SMS-PRO/GG-PRO flash card programmer support for uCON64
+
+Copyright (c) 2004 dbjh
+
+Based on Delphi source code by ToToTEK Multi Media. Information in that source
+code has been used with permission. However, ToToTEK Multi Media explicitly
+stated that the information in that source code may be freely distributed.
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef SMSGG_PRO_H
+#define SMSGG_PRO_H
+
+extern const st_getopt2_t smsggpro_usage[];
+
+#ifdef USE_PARALLEL
+extern int smsgg_read_rom (const char *filename, unsigned int parport, int size);
+extern int smsgg_write_rom (const char *filename, unsigned int parport);
+extern int smsgg_read_sram (const char *filename, unsigned int parport, int start_bank);
+extern int smsgg_write_sram (const char *filename, unsigned int parport, int start_bank);
+#endif
+
+#endif

+ 46 - 0
ucon64/2.0/src/backup/ssc.c

@@ -0,0 +1,46 @@
+/*
+ssc.c - support for Super Smart Card
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "misc/itypes.h"
+#include "misc/misc.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ssc.h"
+
+
+const st_getopt2_t ssc_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Super Smart Card/SSC",
+      NULL
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };

+ 34 - 0
ucon64/2.0/src/backup/ssc.h

@@ -0,0 +1,34 @@
+/*
+ssc.h - minimal support for Super Smart Card/SSC
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef SCC_H
+#define SCC_H
+
+extern const st_getopt2_t ssc_usage[];
+
+typedef struct st_ssc_header
+{
+  char pad[512];
+} st_ssc_header_t;
+
+
+#define SSC_HEADER_LEN (sizeof (st_ssc_header_t))
+
+#endif

+ 1358 - 0
ucon64/2.0/src/backup/swc.c

@@ -0,0 +1,1358 @@
+/*
+swc.c - Super Wild Card support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2001 - 2004 dbjh
+Copyright (c) 2001        Caz
+Copyright (c) 2003        John Weidman
+Copyright (c) 2004        JohnDie
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "misc/misc.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/file.h"
+#include "misc/parallel.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "ffe.h"
+#include "swc.h"
+#include "console/snes.h"                       // for snes_get_file_type ()
+
+
+const st_getopt2_t swc_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Super Com Pro/Super Magicom/SMC/Super Wild Card (1.6XC/2.7CC/2.8CC/DX/DX2)/SWC"
+      /*"1993/1994/1995/19XX Front Far East/FFE http://www.front.com.tw"*/,
+      NULL
+    },
+#ifdef  USE_PARALLEL
+    {
+      "xswc", 0, 0, UCON64_XSWC,
+      NULL, "send/receive ROM to/from Super Wild Card*/SWC; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when ROM does not exist",
+      &ucon64_wf[WF_OBJ_SNES_DEFAULT_STOP_NO_SPLIT_NO_ROM]
+    },
+    {
+      "xswc2", 0, 0, UCON64_XSWC2,
+      NULL, "same as " OPTION_LONG_S "xswc, but enables Real Time Save mode (SWC only)",
+      &ucon64_wf[WF_OBJ_SNES_DEFAULT_STOP_NO_SPLIT_NO_ROM]
+    },
+#if 1
+/*
+  The following help text used to be hidden, because we wanted to avoid people
+  to "accidentally" create overdumps, bad dumps or report bugs that aren't bugs
+  (SA-1). However, now that ucon64.io_mode is useful for -xswcc I guess the
+  help should be complete. - dbjh
+*/
+    {
+      "xswc-io", 1, 0, UCON64_XSWC_IO,
+      "MODE", "specify SWC I/O mode; use with -xswc or -xswcc\n"
+      "MODE=0x001 force 32 Mbit dump\n"
+      "MODE=0x002 use alternative method for determining ROM size\n"
+      "MODE=0x004 Super FX\n"
+      "MODE=0x008 S-DD1\n"
+      "MODE=0x010 SA-1\n"
+      "MODE=0x020 SPC7110\n"
+      "MODE=0x040 DX2 trick (might work with other SWC models)\n"
+      "MODE=0x080 Mega Man X 2\n"
+      "MODE=0x100 dump BIOS\n"
+      "It is possible to combine flags. MODE=0x44 makes it possible\n"
+      "to dump for example Yoshi's Island",
+      &ucon64_wf[WF_OBJ_SNES_SWITCH]
+    },
+#endif
+    {
+      "xswcs", 0, 0, UCON64_XSWCS,
+      NULL,
+      "send/receive SRAM to/from Super Wild Card*/SWC; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM]
+    },
+    {
+      "xswcc", 0, 0, UCON64_XSWCC,
+      NULL, "send/receive SRAM to/from cartridge in Super Wild Card*/SWC;\n"
+      OPTION_LONG_S "port=PORT\n" "receives automatically when SRAM does not exist",
+      &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM]
+    },
+    {
+      "xswcr", 0, 0, UCON64_XSWCR,
+      NULL, "send/receive RTS data to/from Super Wild Card*/SWC; " OPTION_LONG_S "port=PORT\n"
+      "receives automatically when RTS file does not exist",
+      &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM]
+    },
+#endif // USE_PARALLEL
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+#ifdef  USE_PARALLEL
+
+#define BUFFERSIZE 8192                         // don't change, only 8192 works!
+
+/*
+  Some notes about dumping special chip cartridges (JohnDie):
+  The following defines enable code to dump cartridges containing special chips
+  like the SA-1, the S-DD1, the SPC7110 and the C4. However, enabling these
+  options is not all there is to successfully dump these cartridges.
+  The SPC7110 and SA-1 need several attempts to "boot up" in the copier. This
+  involves powering off and on until the chip comes out of reset and allows
+  access to the cartridge. And you need to make sure that pin 1 of the SNES
+  adapter of the SWC is not cut! The standard SWC DX2 adapter has this trace
+  cut, so you have to reconnect it somehow.
+  Dumping Super FX 2 cartridges is only possible when enabling the DX2 trick.
+  Otherwise uCON64 will not detect the cartridge. This is because of the way the
+  Super FX 2 cartridges have their ROM mapped into the SNES address space.
+*/
+#define DUMP_MMX2
+#define DUMP_SA1
+#define DUMP_SDD1
+#define DUMP_SPC7110
+
+static int receive_rom_info (unsigned char *buffer, int io_mode);
+static int get_rom_size (unsigned char *info_block);
+static int check1 (unsigned char *info_block, int index);
+static int check2 (unsigned char *info_block, int index, unsigned char value);
+static int check3 (unsigned char *info_block, int index1, int index2, int size);
+static unsigned char get_emu_mode_select (unsigned char byte, int size);
+static void handle_fig_header (unsigned char *header);
+static void set_bank_and_page (unsigned char bank, unsigned char page);
+static void read_cartridge (unsigned int address, unsigned char *buffer,
+                            unsigned int length);
+static unsigned char read_cartridge1 (unsigned int address);
+static void write_cartridge (unsigned int address, unsigned char *buffer,
+                             unsigned int length);
+static void write_cartridge1 (unsigned int address, unsigned char byte);
+static void dump_rom (FILE *file, int size, int numblocks, unsigned int mask1,
+                      unsigned int mask2, unsigned int address);
+static void dump_bios (FILE *file);
+static int sub (void);
+static int mram_helper (int x);
+static int mram (void);
+
+static int hirom;                               // `hirom' was `special'
+static int dx2_trick = 0;
+
+#ifdef  DUMP_SA1
+static void set_sa1_map (unsigned short chunk);
+static int snes_sa1 = 0;
+#endif
+#ifdef  DUMP_SDD1
+static void set_sdd1_map (unsigned short chunk);
+static int snes_sdd1 = 0;
+#endif
+#ifdef  DUMP_SPC7110
+static void set_spc7110_map (unsigned short chunk);
+static int snes_spc7110 = 0;
+#endif
+
+
+#if BUFFERSIZE < 512
+#error receive_rom_info() and swc_read_sram() expect BUFFERSIZE to be at least \
+       512 bytes.
+#endif
+int
+receive_rom_info (unsigned char *buffer, int io_mode)
+/*
+  - returns size of ROM in Mb (128 kB) units
+  - returns ROM header in buffer (index 2 (emulation mode select) is not yet
+    filled in)
+  - sets global `hirom'
+*/
+{
+  int n, size;
+  volatile int m;
+  unsigned char byte;
+
+#ifdef  DUMP_MMX2
+  if (io_mode & SWC_IO_MMX2)
+    {
+      /*
+        MMX2 can be dumped after writing a 0 to SNES register 0x7f52. Before we can
+        write to that register we have to enable cartridge page mapping. That is
+        done by writing to SWC register 0xe00c. When cartridge page mapping is
+        enabled we can access SNES registers by reading or writing to the SWC
+        address range 0x2000-0x3fff. Before reading or writing to an address in that
+        range we have to "announce" the address to the SWC (via command 5). Because
+        we access a SNES register we only set the page number bits (0-1).
+      */
+      unsigned short address = 0x7f52;
+      ffe_send_command0 (0xe00c, 0);
+
+      ffe_send_command (5, (unsigned short) (address / 0x2000), 0);
+      ffe_receive_block ((unsigned short) ((address & 0x1fff) + 0x2000), buffer, 8);
+      dumper (stdout, buffer, 8, address, DUMPER_HEX);
+
+      ffe_send_command (5, (unsigned short) (address / 0x2000), 0);
+      ffe_send_command0 ((unsigned short) ((address & 0x1fff) + 0x2000), 0);
+
+      ffe_send_command (5, (unsigned short) (address / 0x2000), 0);
+      ffe_receive_block ((unsigned short) ((address & 0x1fff) + 0x2000), buffer, 8);
+      dumper (stdout, buffer, 8, address, DUMPER_HEX);
+    }
+#endif
+
+  ffe_send_command0 (0xe00c, 0);
+
+  if (UCON64_ISSET (ucon64.snes_hirom))
+    hirom = ucon64.snes_hirom ? 1 : 0;
+  else
+    {
+      byte = read_cartridge1 (0x00ffd5);
+      hirom = ((byte & 1 && byte != 0x23) || byte == 0x3a) ? 1 : 0; // & 1 => 0x21, 0x31, 0x35
+    }
+
+  for (n = 0; n < (int) SWC_HEADER_LEN; n++)
+    {
+      for (m = 0; m < 65536; m++)               // a delay is necessary here
+        ;
+      ffe_send_command (5, (unsigned short) (0x200 + n), 0);
+      buffer[n] = ffe_send_command1 (0xa0a0);
+    }
+
+  if (io_mode & SWC_IO_FORCE_32MBIT)
+    {
+      if (!UCON64_ISSET (ucon64.snes_hirom))
+        hirom = 1;                              // default to super HiROM dump
+      size = 32;                                // dump 32 Mbit
+    }
+  else
+    {
+      size = get_rom_size (buffer);
+#ifdef  DUMP_SA1
+      if (!snes_sa1)
+#endif
+      if (hirom)
+        size <<= 1;
+    }
+
+  // Fix up ROM size for Super FX 2 cartridge, because get_rom_size() fails for
+  //  Super FX 2 cartridges and returns 0.
+  if (io_mode & SWC_IO_SUPER_FX)
+    // 00:303b returns the GSU revision and is non-zero if there is a GSU
+    if (size == 0 && read_cartridge1 (0x00303b) != 0)
+      size = 16;
+
+#ifdef  DUMP_SDD1
+  // Adjust size to 48 Mbit for Star Ocean
+  if (snes_sdd1 && size == 32)
+    {
+      byte = read_cartridge1 (0x00ffd7);
+      if (byte == 0x0d)
+        size = 48;
+    }
+#endif
+
+#ifdef  DUMP_SA1
+  // Fix up size for SA-1 chips
+  if (snes_sa1)
+    {
+      byte = read_cartridge1 (0x00ffd7);
+      switch (byte)
+        {
+        case 0x09:
+          size = 4;
+          break;
+        case 0x0a:
+          size = 8;
+          break;
+        case 0x0b:
+          size = 16;
+          break;
+        case 0x0c:
+          size = 32;
+          break;
+        default:
+          break;
+        }
+    }
+#endif
+
+#ifdef  DUMP_SPC7110
+  // Fix up size for SPC7110 chips
+  if (snes_spc7110)
+    {
+      byte = read_cartridge1 (0x00ffd7);
+      switch (byte)
+        {
+        case 0x0c:
+          size = 24;
+          break;
+        case 0x0d:
+          size = 40;
+          break;
+        default:
+          break;
+        }
+    }
+#endif
+
+  memset (buffer, 0, SWC_HEADER_LEN);
+  buffer[0] = size << 4;                        // *16 for 8 kB units; low byte
+  buffer[1] = size >> 4;                        // *16 for 8 kB units /256 for high byte
+  buffer[8] = 0xaa;
+  buffer[9] = 0xbb;
+  buffer[10] = 4;
+
+  return size;
+}
+
+
+int
+get_rom_size (unsigned char *info_block)
+// returns size of ROM in Mb units
+{
+  if (check1 (info_block, 0))
+    return 0;
+  if (check2 (info_block, 0x10, 0x84))
+    return 0;
+  if (check3 (info_block, 0, 0x20, 0x20))
+    return 2;
+  if (check3 (info_block, 0, 0x40, 0x20))
+    return 4;
+  if (check3 (info_block, 0x40, 0x60, 0x20))
+    return 6;
+  if (check3 (info_block, 0, 0x80, 0x10))
+    return 8;
+  if (check1 (info_block, 0x80))
+    return 8;
+  if (check3 (info_block, 0x80, 0x90, 0x10))
+    return 8;
+  if (check2 (info_block, 0x80, 0xa0))
+    return 8;
+  if (check3 (info_block, 0x80, 0xa0, 0x20))
+    return 10;
+  if (check1 (info_block, 0xc0))
+    return 12;
+  if (check2 (info_block, 0xc0, 0xb0))
+    return 12;
+  if (check3 (info_block, 0x80, 0xc0, 0x20))
+    return 12;
+  if (check3 (info_block, 0x100, 0, 0x10))
+    return 16;
+  if (check2 (info_block, 0x100, 0xc0))
+    return 16;
+  if (check3 (info_block, 0x100, 0x120, 0x10))
+    return 18;
+  if (check3 (info_block, 0x100, 0x140, 0x10))
+    return 20;
+  if (check2 (info_block, 0x140, 0xd0))
+    return 20;
+  if (check3 (info_block, 0x100, 0x180, 0x10))
+    return 24;
+  if (check2 (info_block, 0x180, 0xe0))
+    return 24;
+  if (check3 (info_block, 0x180, 0x1c0, 0x10))
+    return 28;
+  if (check3 (info_block, 0x1f0, 0x1f0, 0x10))
+    return 32;
+
+  return 0;
+}
+
+
+int
+check1 (unsigned char *info_block, int index)
+{
+  int n;
+
+  for (n = 0; n < 16; n++)
+    if (info_block[n + index] != info_block[index])
+      return 0;
+
+  return 1;
+}
+
+
+int
+check2 (unsigned char *info_block, int index, unsigned char value)
+{
+  int n;
+
+  for (n = 0; n < 4; n++)
+    if (info_block[n + index] != value)
+      return 0;
+
+  return 1;
+}
+
+
+int
+check3 (unsigned char *info_block, int index1, int index2, int size)
+{
+  int n;
+
+  for (n = 0; n < size; n++)
+    if (info_block[n + index1] != info_block[n + index2])
+      return 0;
+
+  return 1;
+}
+
+
+unsigned char
+get_emu_mode_select (unsigned char byte, int size)
+{
+  int x;
+  unsigned char ems;
+
+  if (byte == 0)
+    x = 0xc;
+  else if (byte == 1)
+    x = 8;
+  else if (byte == 3)
+    x = 4;
+  else
+    x = 0;
+
+  if (hirom)
+    {
+      if (x == 0xc && size <= 0x1c)
+        ems = 0x1c;
+      else
+        ems = x + 0x30;
+    }
+  else
+    {
+      if (x == 0xc)
+        ems = 0x2c;
+      else
+        ems = x;
+
+//      if (size <= 8)                          // This bit should always be 0 - JohnDie
+//        ems++;
+    }
+
+  return ems;
+}
+
+
+void
+handle_fig_header (unsigned char *header)
+{
+  if ((header[4] == 0x77 && header[5] == 0x83) ||
+      (header[4] == 0xf7 && header[5] == 0x83) ||
+      (header[4] == 0x47 && header[5] == 0x83))
+    header[2] = 0x0c;                           // 0 kB
+  else if (header[4] == 0xfd && header[5] == 0x82)
+    header[2] = 0x08;                           // 2 kB
+  else if ((header[4] == 0xdd && header[5] == 0x82) ||
+           (header[4] == 0x00 && header[5] == 0x80))
+    /*
+      8 kB *or* 2 kB (shortcoming of FIG header format). We give the emu mode
+      select byte a value as if the game uses 8 kB. At least this makes games
+      that use 8 kB work.
+      Users should not complain if the game doesn't work because of a SRAM
+      protection, because they should have converted the ROM to SWC format in
+      the first place.
+    */
+    header[2] = 0x04;
+  else // if ((header[4] == 0xdd && header[5] == 0x02) ||
+       //     (header[4] == 0x00 && header[5] == 0x00) ||
+       //     (header[4] == 0x11 && header[5] == 0x02))
+    header[2] = 0;                              // 32 kB
+
+  if (header[3] & 0x80)                         // Pro Fighter (FIG) HiROM dump
+    header[2] |= 0x30;                          // set bit 5&4 (SRAM & DRAM mem map mode 21)
+}
+
+
+void
+swc_unlock (unsigned int parport)
+/*
+  "Unlock" the SWC. However, just starting to send, then stopping with ^C,
+  gives the same result.
+*/
+{
+  ffe_init_io (parport);
+  ffe_send_command (6, 0, 0);
+  ffe_deinit_io ();
+}
+
+
+#ifdef  DUMP_SA1
+void
+set_sa1_map (unsigned short chunk)
+{
+  volatile int m;
+
+  // map the 8 Mbit ROM chunk specified by chunk into the F0 bank
+  write_cartridge1 (0x002223, (unsigned char) ((chunk & 0x07) | 0x80));
+  for (m = 0; m < 65536; m++)
+    ;
+}
+#endif
+
+
+#ifdef  DUMP_SDD1
+void
+set_sdd1_map (unsigned short chunk)
+{
+  volatile int m;
+
+  // map the 8 Mbit ROM chunk specified by chunk into the F0 bank
+  write_cartridge1 (0x004807, (unsigned char) (chunk & 0x07));
+  for (m = 0; m < 65536; m++)
+    ;
+}
+#endif
+
+
+#ifdef  DUMP_SPC7110
+void
+set_spc7110_map (unsigned short chunk)
+{
+  volatile int m;
+
+  // map the 8 Mbit ROM chunk specified by chunk into the F0 bank
+  write_cartridge1 (0x004834, 0xff);
+  write_cartridge1 (0x004833, (unsigned char) (chunk & 0x07));
+  for (m = 0; m < 65536; m++)
+    ;
+}
+#endif
+
+
+void
+set_bank_and_page (unsigned char bank, unsigned char page)
+{
+  static unsigned char currentbank = 0, currentpage = 4; // Force update on first call
+
+  page &= 3;
+
+#if 0
+  // We only send a command to the SWC if the bank or page differs.
+  /*
+    In order to avoid problems with that no other code should change the bank or
+    page number. So, no calls to ffe_send_command(5, ...). I prefer to be able
+    to call ffe_send_command(5, ...) without breaking this function. Besides,
+    the benefit of this optimisation is rather small. - dbjh
+  */
+  if (bank != currentbank || page != currentpage)
+#endif
+    {
+      currentbank = bank;
+      currentpage = page;
+
+      if (dx2_trick)
+        {
+          /*
+            The SWC DX2 does not allow to access banks 00-7f. But this is needed
+            to dump some cartridges (ToP, DKJM2 and Super FX 2). This trick
+            avoids using ffe_send_command(5, ...) to set the bank value and
+            writes the bank value directly into the SNES RAM where the DX2 BIOS
+            would store it. Note that this hack is specific to the SWC DX2 and
+            will probably not work with other copiers. - JohnDie
+          */
+          ffe_send_command (5, currentpage, 0);
+          ffe_send_command0 (0x0007, currentbank);
+        }
+      else
+        ffe_send_command (5, (unsigned short) ((currentbank << 2) | currentpage), 0);
+    }
+}
+
+
+void
+read_cartridge (unsigned int address, unsigned char *buffer, unsigned int length)
+{
+  address &= 0xffffff;
+  set_bank_and_page ((unsigned char) (address >> 16),
+                     (unsigned char) ((address & 0x7fff) / 0x2000));
+
+  if ((address & 0x00ffff) < 0x8000)
+    ffe_receive_block ((unsigned short) (((address & 0x7fffff) < 0x400000 ?
+                         0x6000 : 0x2000) + (address & 0x001fff)), buffer, length);
+  else
+    ffe_receive_block ((unsigned short) (0xa000 + (address & 0x001fff)),
+                       buffer, length);
+}
+
+
+unsigned char
+read_cartridge1 (unsigned int address)
+{
+  unsigned char byte;
+
+  read_cartridge (address, &byte, 1);
+
+  return byte;
+}
+
+
+void
+write_cartridge (unsigned int address, unsigned char *buffer, unsigned int length)
+{
+  address &= 0xffffff;
+  set_bank_and_page ((unsigned char) (address >> 16),
+                     (unsigned char) ((address & 0x7fff) / 0x2000));
+
+  if ((address & 0x00ffff) < 0x8000)
+    ffe_send_block ((unsigned short) (((address & 0x7fffff) < 0x400000 ?
+                      0x6000 : 0x2000) + (address & 0x001fff)), buffer, length);
+  else
+    ffe_send_block ((unsigned short) (0xa000 + (address & 0x001fff)),
+                    buffer, length);
+}
+
+
+void
+write_cartridge1 (unsigned int address, unsigned char byte)
+{
+  write_cartridge (address, &byte, 1);
+}
+
+
+void
+dump_rom (FILE *file, int size, int numblocks, unsigned int mask1,
+          unsigned int mask2, unsigned int address)
+{
+  int i, bytesreceived = 0;
+  unsigned char *buffer;
+  time_t starttime;
+#if     defined DUMP_SA1 || defined DUMP_SDD1 || defined DUMP_SPC7110
+  unsigned short chunk_num = 0;         // 0 = 1st 8 Mb ROM chunk, 1 = 2nd 8 Mb, ...
+#endif
+
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  starttime = time (NULL);
+
+  for (i = 0; i < numblocks; i++)
+    {
+      address |= mask1;                         // make sure to stay in ROM areas
+      address &= mask2;
+
+#ifdef  DUMP_SA1
+      if (snes_sa1 && address == 0xf00000)
+        set_sa1_map (chunk_num++);
+#endif
+#ifdef  DUMP_SDD1
+      if (snes_sdd1 && address == 0xf00000)
+        set_sdd1_map (chunk_num++);
+#endif
+#ifdef  DUMP_SPC7110
+      if (snes_spc7110 && address == 0xf00000)
+        set_spc7110_map (chunk_num++);
+#endif
+
+      read_cartridge (address, buffer, BUFFERSIZE);
+
+      fwrite (buffer, 1, BUFFERSIZE, file);
+      address += BUFFERSIZE;
+
+      bytesreceived += BUFFERSIZE;
+      ucon64_gauge (starttime, bytesreceived, size);
+      ffe_checkabort (2);
+    }
+
+  ffe_send_command (5, 0, 0);
+
+  free (buffer);
+}
+
+
+void
+dump_bios (FILE *file)
+{
+  unsigned short int address;
+  int bytesreceived = 0;
+  unsigned char *buffer;
+  time_t starttime;
+
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  starttime = time (NULL);
+  for (address = 0; address < 0x080; address += 4) // banks 00-1f
+    {
+      ffe_send_command (5, address, 0);
+      ffe_receive_block (0xe000, buffer, BUFFERSIZE);
+      fwrite (buffer, 1, BUFFERSIZE, file);
+
+      bytesreceived += BUFFERSIZE;
+      ucon64_gauge (starttime, bytesreceived, 0x20 * 0x2000);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+}
+
+
+int
+swc_read_rom (const char *filename, unsigned int parport, int io_mode)
+{
+  FILE *file;
+  unsigned char buffer[SWC_HEADER_LEN], byte;
+  int size, blocksleft;
+
+  ffe_init_io (parport);
+
+#ifdef  DUMP_SA1
+  if (io_mode & SWC_IO_SA1)
+    snes_sa1 = 1;
+#endif
+#ifdef  DUMP_SDD1
+  if (io_mode & SWC_IO_SDD1)
+    snes_sdd1 = 1;
+#endif
+#ifdef  DUMP_SPC7110
+  if (io_mode & SWC_IO_SPC7110)
+    snes_spc7110 = 1;
+#endif
+  if (io_mode & SWC_IO_DX2_TRICK)
+    dx2_trick = 1;
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+
+  if (io_mode & SWC_IO_DUMP_BIOS)
+    {
+      puts ("Press q to abort\n");
+
+      dump_bios (file);
+
+      ffe_deinit_io ();
+      fclose (file);
+      return 0;                                 // skip the other code in this function
+    }
+
+  size = receive_rom_info (buffer, io_mode);
+  if (size == 0)
+    {
+      fputs ("ERROR: There is no cartridge present in the Super Wild Card\n", stderr);
+      fclose (file);
+      remove (filename);
+      exit (1);
+    }
+  blocksleft = size * 16;                       // 1 Mb (128 kB) unit == 16 8 kB units
+  printf ("Receive: %d Bytes (%.4f Mb)\n", size * MBIT, (float) size);
+#ifdef  DUMP_SA1
+  if (snes_sa1)
+    puts ("NOTE: Dumping SA-1 cartridge");
+#endif
+#ifdef  DUMP_SDD1
+  if (snes_sdd1)
+    puts ("NOTE: Dumping S-DD1 cartridge");
+#endif
+#ifdef  DUMP_SPC7110
+  if (snes_spc7110)
+    puts ("NOTE: Dumping SPC7110 cartridge");
+#endif
+  size *= MBIT;                                 // size in bytes for ucon64_gauge() below
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_command0 (0xe00c, 0);
+  byte = ffe_send_command1 (0xbfd8);
+  buffer[2] = get_emu_mode_select (byte, blocksleft / 16);
+  fwrite (buffer, 1, SWC_HEADER_LEN, file);     // write header (other necessary fields are
+                                                //  filled in by receive_rom_info())
+
+  puts ("Press q to abort\n");                  // print here, NOT before first SWC I/O,
+                                                //  because if we get here q works ;-)
+#ifdef  DUMP_SA1
+  if (snes_sa1)
+    dump_rom (file, size, blocksleft, 0xf00000, 0xffffff, 0xf00000);
+  else
+#endif
+#ifdef  DUMP_SDD1
+  if (snes_sdd1)
+    dump_rom (file, size, blocksleft, 0xf00000, 0xffffff, 0xf00000);
+  else
+#endif
+#ifdef  DUMP_SPC7110
+  if (snes_spc7110)
+    {
+      // First dump the 8 MBit P-ROM (program ROM)
+      dump_rom (file, 8 * MBIT, 8 * 16, 0xc00000, 0xcfffff, 0xc00000);
+      // Then dump the remaining amount of D-ROM (data ROM)
+      dump_rom (file, size - 8 * MBIT, blocksleft - 8 * 16, 0xf00000, 0xffffff,
+                0xf00000);
+    }
+  else
+#endif
+  if (io_mode & SWC_IO_SUPER_FX)
+    dump_rom (file, size, blocksleft, 0x008000, 0x7fffff, 0x008000);
+  else if (hirom)
+    dump_rom (file, size, blocksleft, 0x400000, 0xffffff, 0xc00000);
+  else
+    dump_rom (file, size, blocksleft, 0x008000, 0xffffff, 0x808000);
+
+#ifdef  DUMP_SA1
+  if (snes_sa1)
+    set_sa1_map (3);
+#endif
+#ifdef  DUMP_SDD1
+  if (snes_sdd1)
+    set_sdd1_map (3);
+#endif
+  ffe_send_command (5, 0, 0);
+
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+swc_write_rom (const char *filename, unsigned int parport, int enableRTS)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int bytesread, bytessend, totalblocks, blocksdone = 0, emu_mode_select, fsize;
+  unsigned short address;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  fsize = fsizeof (filename);
+  printf ("Send: %d Bytes (%.4f Mb)\n", fsize, (float) fsize / MBIT);
+
+  ffe_send_command0 (0xc008, 0);
+  fread (buffer, 1, SWC_HEADER_LEN, file);
+
+  if (snes_get_file_type () == FIG)
+    handle_fig_header (buffer);
+#if 1
+  /*
+    0x0c == no SRAM & LoROM; we use the header, so that the user can override this
+    bit 4 == 0 => DRAM mode 20 (LoROM); disable SRAM by setting SRAM mem map mode 2
+  */
+  if ((buffer[2] & 0x1c) == 0x0c)
+    buffer[2] |= 0x20;
+#else
+  // The code below doesn't work for some HiROM games that don't use SRAM.
+  if ((buffer[2] & 0x0c) == 0x0c)               // 0x0c == no SRAM; we use the header, so
+    {                                           //  that the user can override this
+      if (buffer[2] & 0x10)                     // bit 4 == 1 => DRAM mode 21 (HiROM)
+        buffer[2] &= ~0x20;                     // disable SRAM by setting SRAM mem map mode 1
+      else                                      // bit 4 == 0 => DRAM mode 20 (LoROM)
+        buffer[2] |= 0x20;                      // disable SRAM by setting SRAM mem map mode 2
+    }
+#endif
+  emu_mode_select = buffer[2];                  // this byte is needed later
+
+#if 1                                           // sending the header is not required
+  ffe_send_command (5, 0, 0);
+  ffe_send_block (0x400, buffer, SWC_HEADER_LEN); // send header
+#endif
+  bytessend = SWC_HEADER_LEN;
+
+  puts ("Press q to abort\n");                  // print here, NOT before first SWC I/O,
+                                                //  because if we get here q works ;-)
+  address = 0x200;                              // VGS '00 uses 0x200, VGS '96 uses 0,
+  starttime = time (NULL);                      //  but then some ROMs don't work
+  while ((bytesread = fread (buffer, 1, BUFFERSIZE, file)))
+    {
+      ffe_send_command0 ((unsigned short) 0xc010, (unsigned char) (blocksdone >> 9));
+      ffe_send_command (5, address, 0);
+      ffe_send_block (0x8000, buffer, bytesread);
+      address++;
+      blocksdone++;
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, fsize);
+      ffe_checkabort (2);
+    }
+
+  if (blocksdone > 0x200)                       // ROM dump > 512 8 kB blocks (=32 Mb (=4 MB))
+    ffe_send_command0 (0xc010, 2);
+
+  ffe_send_command (5, 0, 0);
+  totalblocks = (fsize - SWC_HEADER_LEN + BUFFERSIZE - 1) / BUFFERSIZE; // round up
+  ffe_send_command (6, (unsigned short) (5 | (totalblocks << 8)), (unsigned short) (totalblocks >> 8)); // bytes: 6, 5, #8 K L, #8 K H, 0
+  ffe_send_command (6, (unsigned short) (1 | (emu_mode_select << 8)), (unsigned short) enableRTS); // last arg = 1 enables RTS
+                                                               //  mode, 0 disables it
+  ffe_wait_for_ready ();
+  outportb ((unsigned short) (parport + PARPORT_DATA), 0);
+  outportb ((unsigned short) (parport + PARPORT_CONTROL),
+            (unsigned char) (inportb ((unsigned short) // invert strobe
+                                      (parport + PARPORT_CONTROL)) ^ PARPORT_STROBE));
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+swc_read_sram (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int blocksleft, bytesreceived = 0;
+  unsigned short address;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  printf ("Receive: %d Bytes\n", 32 * 1024);
+  memset (buffer, 0, SWC_HEADER_LEN);
+  buffer[8] = 0xaa;
+  buffer[9] = 0xbb;
+  buffer[10] = 5;
+  fwrite (buffer, 1, SWC_HEADER_LEN, file);
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_command0 (0xe00d, 0);
+  ffe_send_command0 (0xc008, 0);
+
+  puts ("Press q to abort\n");                  // print here, NOT before first SWC I/O,
+                                                //  because if we get here q works ;-)
+  blocksleft = 4;                               // SRAM is 4*8 kB
+  address = 0x100;
+  starttime = time (NULL);
+  while (blocksleft > 0)
+    {
+      ffe_send_command (5, address, 0);
+      ffe_receive_block (0x2000, buffer, BUFFERSIZE);
+      blocksleft--;
+      address++;
+      fwrite (buffer, 1, BUFFERSIZE, file);
+
+      bytesreceived += BUFFERSIZE;
+      ucon64_gauge (starttime, bytesreceived, 32 * 1024);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+swc_write_sram (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int bytesread, bytessend = 0, size;
+  unsigned short address;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = fsizeof (filename) - SWC_HEADER_LEN;   // SWC SRAM is 4*8 kB, emu SRAM often not
+  printf ("Send: %d Bytes\n", size);
+  fseek (file, SWC_HEADER_LEN, SEEK_SET);       // skip the header
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_command0 (0xe00d, 0);
+  ffe_send_command0 (0xc008, 0);
+
+  puts ("Press q to abort\n");                  // print here, NOT before first SWC I/O,
+                                                //  because if we get here q works ;-)
+  address = 0x100;
+  starttime = time (NULL);
+  while ((bytesread = fread (buffer, 1, BUFFERSIZE, file)))
+    {
+      ffe_send_command (5, address, 0);
+      ffe_send_block (0x2000, buffer, bytesread);
+      address++;
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+sub (void)
+{
+  ffe_send_command (5, 7 * 4, 0);
+  ffe_send_command0 (0xe00d, 0);
+  ffe_send_command0 (0xe003, 0);
+
+  if (ffe_send_command1 (0xb080) != 'S')
+    return 0;
+  if (ffe_send_command1 (0xb081) != 'U')
+    return 0;
+  if (ffe_send_command1 (0xb082) != 'B')
+    return 0;
+
+  return 1;
+}
+
+
+int
+mram_helper (int x)
+{
+  ffe_send_command (5, (unsigned short) x, 0);
+  x = ffe_send_command1 (0x8000);
+  ffe_send_command0 (0x8000, (unsigned char) (x ^ 0xff));
+  if (ffe_send_command1 (0x8000) != (unsigned char) (x ^ 0xff))
+    return 0;
+
+  ffe_send_command0 (0x8000, (unsigned char) x);
+  return 1;
+}
+
+
+int
+mram (void)
+{
+  if (mram_helper (0x76 * 4))
+    return 0x76 * 4;
+  if (mram_helper (0x56 * 4))
+    return 0x56 * 4;
+  if (mram_helper (0x36 * 4))
+    return 0x36 * 4;
+  return 0x16 * 4;
+}
+
+
+int
+swc_read_rts (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int blocksleft, bytesreceived = 0;
+  unsigned short address1, address2;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  printf ("Receive: %d Bytes\n", 256 * 1024);
+  memset (buffer, 0, SWC_HEADER_LEN);
+  buffer[8] = 0xaa;
+  buffer[9] = 0xbb;
+  buffer[10] = 8;
+  fwrite (buffer, 1, SWC_HEADER_LEN, file);
+
+  puts ("Press q to abort\n");
+  blocksleft = 32;                              // RTS data is 32*8 kB
+
+  if (sub ())
+    {
+      address1 = 0;
+      address2 = 0xa000;
+    }
+  else
+    {
+      address1 = mram ();
+      address2 = 0x8000;
+    }
+
+  starttime = time (NULL);
+  while (blocksleft > 0)
+    {
+      ffe_send_command (5, address1, 0);
+      if (address2 == 0x8000)
+        ffe_send_command0 (0xc010, 1);
+      ffe_receive_block (address2, buffer, BUFFERSIZE);
+
+      blocksleft--;
+      address1++;
+      fwrite (buffer, 1, BUFFERSIZE, file);
+
+      bytesreceived += BUFFERSIZE;
+      ucon64_gauge (starttime, bytesreceived, 256 * 1024);
+      ffe_checkabort (2);
+    }
+  ffe_send_command (6, 3, 0);
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+swc_write_rts (const char *filename, unsigned int parport)
+{
+  FILE *file;
+  unsigned char *buffer;
+  int bytesread, bytessend = 0, size;
+  unsigned short address1, address2;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = fsizeof (filename) - SWC_HEADER_LEN;
+  printf ("Send: %d Bytes\n", size);
+  fseek (file, SWC_HEADER_LEN, SEEK_SET);       // skip the header
+
+  puts ("Press q to abort\n");
+  if (sub ())
+    {
+      address1 = 0;
+      address2 = 0xa000;
+    }
+  else
+    {
+      address1 = mram ();
+      address2 = 0x8000;
+    }
+
+  starttime = time (NULL);
+  while ((bytesread = fread (buffer, 1, BUFFERSIZE, file)))
+    {
+      ffe_send_command (5, address1, 0);
+      if (address2 == 0x8000)
+        ffe_send_command0 (0xc010, 1);
+      ffe_send_block (address2, buffer, bytesread);
+      address1++;
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      ffe_checkabort (2);
+    }
+  ffe_send_command (6, 3, 0);
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+swc_read_cart_sram (const char *filename, unsigned int parport, int io_mode)
+{
+  FILE *file;
+  unsigned char *buffer, byte;
+  int bytesreceived = 0, size;
+  unsigned short address;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if (io_mode & SWC_IO_DX2_TRICK)
+    dx2_trick = 1;
+
+  if ((file = fopen (filename, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = receive_rom_info (buffer, io_mode);
+  if (size == 0)
+    {
+      fputs ("ERROR: There is no cartridge present in the Super Wild Card\n", stderr);
+      fclose (file);
+      remove (filename);
+      exit (1);
+    }
+
+  ffe_send_command (5, 3, 0);                   // detect cartridge SRAM size because
+  ffe_send_command0 (0xe00c, 0);                //  we don't want to read too few data
+  byte = read_cartridge1 (io_mode & SWC_IO_SUPER_FX ? 0x00ffbd : 0x00ffd8);
+  size = MAX ((byte ? 1 << (byte + 10) : 0), 32 * 1024);
+  printf ("Receive: %d Bytes\n", size);
+
+  memset (buffer, 0, SWC_HEADER_LEN);
+  buffer[8] = 0xaa;
+  buffer[9] = 0xbb;
+  buffer[10] = 5;
+  fwrite (buffer, 1, SWC_HEADER_LEN, file);
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_command0 (0xe00c, 0);
+//  ffe_send_command0 (0xc008, 0);
+
+  puts ("Press q to abort\n");                  // print here, NOT before first SWC I/O,
+                                                //  because if we get here q works ;-)
+  address = hirom ? 0x2c3 : 0x1c0;
+
+  starttime = time (NULL);
+  while (bytesreceived < size)
+    {
+      set_bank_and_page ((unsigned char) (address >> 2), (unsigned char) (address & 3));
+      ffe_receive_block ((unsigned short) (hirom ? 0x6000 : 0x2000), buffer, BUFFERSIZE);
+      fwrite (buffer, 1, BUFFERSIZE, file);
+      address += hirom ? 4 : 1;
+
+      bytesreceived += BUFFERSIZE;
+      ucon64_gauge (starttime, bytesreceived, size);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+
+int
+swc_write_cart_sram (const char *filename, unsigned int parport, int io_mode)
+{
+  FILE *file;
+  unsigned char *buffer, byte;
+  int bytesread, bytessend = 0, size;
+  unsigned short address;
+  time_t starttime;
+
+  ffe_init_io (parport);
+
+  if (io_mode & SWC_IO_DX2_TRICK)
+    dx2_trick = 1;
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
+      exit (1);
+    }
+  if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
+      exit (1);
+    }
+
+  size = receive_rom_info (buffer, io_mode);
+  if (size == 0)
+    {
+      fputs ("ERROR: There is no cartridge present in the Super Wild Card\n", stderr);
+      fclose (file);
+      exit (1);
+    }
+
+  ffe_send_command (5, 3, 0);                   // detect cartridge SRAM size because we don't
+  ffe_send_command0 (0xe00c, 0);                //  want to write more data than necessary
+  byte = read_cartridge1 (io_mode & SWC_IO_SUPER_FX ? 0x00ffbd : 0x00ffd8);
+  size = fsizeof (filename) - SWC_HEADER_LEN;   // SWC SRAM is 4*8 kB, emu SRAM often not
+  size = MIN ((byte ? 1 << (byte + 10) : 0), size);
+
+  printf ("Send: %d Bytes\n", size);
+  fseek (file, SWC_HEADER_LEN, SEEK_SET);       // skip the header
+
+  ffe_send_command (5, 0, 0);
+  ffe_send_command0 (0xe00c, 0);
+//  ffe_send_command0 (0xc008, 0);
+
+  puts ("Press q to abort\n");                  // print here, NOT before first SWC I/O,
+                                                //  because if we get here q works ;-)
+  address = hirom ? 0x2c3 : 0x1c0;
+
+  starttime = time (NULL);
+  while ((bytessend < size) && (bytesread = fread (buffer, 1, MIN (size, BUFFERSIZE), file)))
+    {
+      set_bank_and_page ((unsigned char) (address >> 2), (unsigned char) (address & 3));
+      ffe_send_block ((unsigned short) (hirom ? 0x6000 : 0x2000), buffer, bytesread);
+      address += hirom ? 4 : 1;
+
+      bytessend += bytesread;
+      ucon64_gauge (starttime, bytessend, size);
+      ffe_checkabort (2);
+    }
+
+  free (buffer);
+  fclose (file);
+  ffe_deinit_io ();
+
+  return 0;
+}
+
+#endif // USE_PARALLEL

+ 72 - 0
ucon64/2.0/src/backup/swc.h

@@ -0,0 +1,72 @@
+/*
+swc.h - Super Wild Card support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2001 - 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef SWC_H
+#define SWC_H
+
+#define SWC_IO_FORCE_32MBIT 0x001
+#define SWC_IO_ALT_ROM_SIZE 0x002
+#define SWC_IO_SUPER_FX     0x004
+#define SWC_IO_SDD1         0x008
+#define SWC_IO_SA1          0x010
+#define SWC_IO_SPC7110      0x020
+#define SWC_IO_DX2_TRICK    0x040
+#define SWC_IO_MMX2         0x080
+#define SWC_IO_DUMP_BIOS    0x100
+
+#define SWC_IO_MAX          0x1ff               // highest valid dumping mode value
+
+extern const st_getopt2_t swc_usage[];
+
+// For the header format, see ffe.h
+typedef struct st_swc_header
+{
+/*
+  Don't create fields that are larger than one byte! For example size_low and size_high
+  could be combined in one unsigned short int. However, this gives problems with little
+  endian vs. big endian machines (e.g. writing the header to disk).
+*/
+  unsigned char size_low;
+  unsigned char size_high;
+  unsigned char emulation;
+  unsigned char pad[5];
+  unsigned char id1;
+  unsigned char id2;
+  unsigned char type;
+  unsigned char pad2[501];
+} st_swc_header_t;
+
+#define SWC_HEADER_START 0
+#define SWC_HEADER_LEN (sizeof (st_swc_header_t))
+
+#ifdef USE_PARALLEL
+extern int swc_read_rom (const char *filename, unsigned int parport, int io_mode);
+extern int swc_write_rom (const char *filename, unsigned int parport, int enableRTS);
+extern int swc_read_sram (const char *filename, unsigned int parport);
+extern int swc_write_sram (const char *filename, unsigned int parport);
+extern int swc_read_rts (const char *filename, unsigned int parport);
+extern int swc_write_rts (const char *filename, unsigned int parport);
+extern int swc_read_cart_sram (const char *filename, unsigned int parport, int io_mode);
+extern int swc_write_cart_sram (const char *filename, unsigned int parport, int io_mode);
+extern void swc_unlock (unsigned int parport);
+#endif
+
+#endif // SWC_H

+ 428 - 0
ucon64/2.0/src/backup/tototek.c

@@ -0,0 +1,428 @@
+/*
+tototek.c - General ToToTEK flash card programmer routines for uCON64
+
+Copyright (c) 2004 dbjh
+
+Based on Delphi source code by ToToTEK Multi Media. Information in that source
+code has been used with permission. However, ToToTEK Multi Media explicitly
+stated that the information in that source code may be freely distributed.
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include "misc/bswap.h"
+#include "misc/misc.h"
+#include "misc/parallel.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "tototek.h"
+
+
+#ifdef USE_PARALLEL
+
+static void set_data_read (void);
+//static void set_data_write (void);
+static void set_ai (unsigned char ai);
+static void init_port (void);
+static void deinit_port (void);
+static void end_port (void);
+static void set_addr_read (int addr);
+static void set_addr_write (int addr);
+static unsigned char get_id_byte (unsigned char addr);
+
+static short int port_8, port_9, port_a, port_b, port_c;
+
+
+void
+ttt_init_io (unsigned int port)
+{
+#if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
+  init_conio ();
+#endif
+
+  port_8 = port;                                // original code used 0x378 for port_8
+  port_9 = port + 1;
+  port_a = port + 2;
+  port_b = port + 3;
+  port_c = port + 4;
+
+  parport_print_info ();
+
+  init_port ();
+}
+
+
+void
+ttt_deinit_io (void)
+{
+#if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
+  deinit_conio ();
+#endif
+
+  end_port ();
+  deinit_port ();
+}
+
+
+void
+set_data_read (void)                            // original name: set_data_read
+{
+  outportb (port_a, 0);                         // nastb=1,nib_sel=0,ndstb=1,nwrite=1
+}
+
+
+#if 0
+void
+set_data_write (void)                           // original name: set_data_write
+{
+  outportb (port_a, 1);                         // nastb=1,nib_sel=0,ndstb=1,nwrite=0
+}
+#endif
+
+
+void
+set_ai (unsigned char ai)                       // original name: {epp_}set_ai
+{
+  outportb (port_a, 1);                         // set_data_write ()
+  outportb (port_b, ai);
+}
+
+
+void
+ttt_set_ai_data (unsigned char ai, unsigned char data) // original name: set_ai_data
+{
+  set_ai (ai);
+  outportb (port_a, 1);                         // set_data_write ()
+  outportb (port_c, data);
+}
+
+
+void
+init_port (void)                                // original name: init_port
+{
+#ifndef USE_PPDEV
+  outportb ((unsigned short) (port_8 + 0x402),
+            (unsigned char) ((inportb ((unsigned short) (port_8 + 0x402)) & 0x1f) | 0x80));
+  outportb (port_9, 1);                         // clear EPP time flag
+#endif
+  ttt_set_ai_data (6, 0);                       // rst=0, wei=0(dis.), rdi=0(dis.)
+  ttt_set_ai_data (6, 0x84);                    // rst=1, wei=0(dis.), rdi=0(dis.)
+}
+
+
+void
+deinit_port (void)                              // original name: Close_end_port
+{
+  outportb (port_a, 1);
+  outportb (port_b, 0);
+  outportb (port_a, 4);                         // port normal now
+}
+
+
+void
+end_port (void)                                 // original name: end_port
+{
+  ttt_set_ai_data (6, 0);                       // rst=0, wei=0(dis.), rdi=0(dis.)
+  outportb (port_a, 4);                         // set_normal ninit=1, nwrite=1
+}
+
+
+void
+ttt_rom_enable (void)                           // original name: romCS_on
+{
+  ttt_set_ai_data (6, 0x84);
+}
+
+
+void
+ttt_rom_disable (void)                          // original name: romCS_off
+{
+  ttt_set_ai_data (6, 0x80);
+}
+
+
+void
+ttt_ram_enable (void)                           // original name: ramCS_on
+{
+  ttt_set_ai_data (6, 0x88);
+}
+
+
+void
+ttt_ram_disable (void)                          // original name: ramCS_off
+{
+  ttt_set_ai_data (6, 0x80);
+}
+
+
+void
+set_addr_write (int addr)                       // original name: set_Long_adrw
+{
+  ttt_set_ai_data (0, (unsigned char) (addr & 0xff)); // a[7..0]
+  addr >>= 8;
+  ttt_set_ai_data (1, (unsigned char) (addr & 0xff)); // a[15..8]
+  addr >>= 8;
+  ttt_set_ai_data (2, (unsigned char) (addr & 0xff)); // a[23..16]
+  set_ai (3);
+}
+
+
+void
+set_addr_read (int addr)                        // original name: set_Long_adr
+{
+  set_addr_write (addr);
+  set_data_read ();                             // ninit=0, nwrite=1
+}
+
+
+void
+ttt_write_mem (int addr, unsigned char b)       // original name: WriteMEMb
+{
+  set_addr_write (addr);
+  outportb (port_c, b);
+}
+
+
+unsigned char
+get_id_byte (unsigned char addr)                // original name: GETID
+{
+  unsigned char byte;
+
+  ttt_rom_enable ();
+  ttt_set_ai_data (0, addr);                    // a[7..0] = 0
+  set_ai (3);
+  outportb (port_c, 0x90);                      // write_data ()
+
+  set_ai (3);
+  set_data_read ();                             // ninit=0, nwrite=1
+  byte = inportb (port_c);                      // read_data ()
+  ttt_rom_disable ();
+
+  return byte;
+}
+
+
+unsigned short int
+ttt_get_id (void)                               // original name: getIDword
+{
+  unsigned short int word;
+
+//  eep_reset ();
+  word = get_id_byte (0);                       // msg
+  word <<= 8;
+  word |= get_id_byte (2);                      // ID
+
+  return word;
+}
+
+
+void
+ttt_read_rom_b (int addr, unsigned char *buf)   // original name: read_buffb
+{
+  int count;
+
+  ttt_rom_enable ();
+  for (count = 0; count < 0x100; count++)
+    {
+      set_addr_read (addr + count);
+      buf[count] = inportb (port_c);            // read_data ()
+    }
+  ttt_rom_disable ();
+}
+
+
+void
+ttt_read_rom_w (int addr, unsigned char *buf)   // original name: read_buff
+{
+  int count;
+
+  set_addr_read (addr);
+  for (count = 0; count < 0x80; count++)
+#ifdef  WORDS_BIGENDIAN
+    ((unsigned short int *) buf)[count] = bswap_16 (inportw (port_c)); // read_dataw ()
+#else
+    ((unsigned short int *) buf)[count] = inportw (port_c); // read_dataw ()
+#endif
+}
+
+
+void
+ttt_read_ram_b (int addr, unsigned char *buf)   // original name: read_rambuff
+{
+  int count;
+
+  ttt_ram_enable ();
+  for (count = 0; count < 0x100; count++)
+    {
+      set_addr_read (addr + count);
+      buf[count] = inportb (port_c);            // read_data ()
+    }
+  ttt_ram_disable ();
+}
+
+
+void
+ttt_read_ram_w (int addr, unsigned char *buf)   // original name: readpagerambuff
+{
+  int count;
+
+  set_addr_read (addr);
+  for (count = 0; count < 0x80; count++)
+    ((unsigned short int *) buf)[count] = inportw (port_c);
+  // read_dataw (); data is doubled for MD-PRO => no problems with endianess
+}
+
+
+// Sharp function
+void
+ttt_erase_block (int addr)                      // original name: EraseBLOCK
+{
+  ttt_rom_enable ();
+  ttt_write_mem (addr, 0x50);
+  ttt_rom_disable ();
+
+  ttt_rom_enable ();
+  ttt_write_mem (addr, 0x20);
+  outportb (port_c, 0xd0);
+  ttt_rom_disable ();
+
+  ttt_rom_enable ();
+  set_ai (3);
+  set_data_read ();                             // set read mode
+  while (inportb (port_c) < 0x80)
+    ;
+  ttt_rom_disable ();
+}
+
+
+void
+ttt_write_byte_sharp (int addr, unsigned char b) // original name: writeEEPDataB
+{
+  ttt_rom_enable ();
+  ttt_write_mem (addr, 0x40);
+  outportb (port_c, b);
+  ttt_rom_disable ();
+}
+
+
+void
+ttt_write_byte_intel (int addr, unsigned char b) // original name: writeIntelEEPDataB
+{                                               // MD-PRO function
+  ttt_rom_enable ();
+  ttt_write_mem (addr, 0x40);
+  outportb (port_c, b);
+
+  set_data_read ();
+  while (inportb (port_c) < 0x80)
+    ;
+  ttt_rom_disable ();
+}
+
+
+void
+ttt_write_page_rom (int addr, unsigned char *buf) // original name: writeEEPDataPAGE
+{
+  int i;
+
+  // send command 0xe8
+  ttt_rom_enable ();
+  ttt_write_mem (addr, 0xe8);
+
+  // read status
+  set_ai (3);
+  set_data_read ();
+  while (inportb (port_c) < 0x80)
+    ;
+
+  set_ai (3);
+  outportb (port_c, 0x1f);
+  ttt_set_ai_data (6, 0x94);
+  set_addr_write (addr);
+  for (i = 0; i <= 0x1f; i++)
+    outportb (port_c, buf[(addr + i) & 0x3fff]);
+  ttt_set_ai_data (6, 0x84);
+  set_ai (3);
+  outportb (port_c, 0xd0);
+
+  // read status
+  set_ai (3);
+  set_data_read ();
+  while (inportb (port_c) < 0x80)
+    ;
+  ttt_rom_disable ();
+}
+
+
+void
+ttt_write_byte_ram (int addr, unsigned char b)  // original name: writeRAMDataB
+{
+  ttt_ram_enable ();
+  ttt_write_mem (addr, b);
+  ttt_ram_disable ();
+}
+
+
+void
+ttt_write_page_ram (int addr, unsigned char *buf) // original name: writeRAMDataPAGE
+{
+  int i;
+
+  ttt_ram_enable ();
+  ttt_set_ai_data (6, 0x98);
+  set_addr_write (addr);
+  for (i = 0; i < 0x100; i++)
+    outportb (port_c, buf[(addr + i) & 0x3fff]);
+  ttt_ram_disable ();
+}
+
+
+void
+ttt_write_page_ram2 (int addr, unsigned char *buf) // original name: writeRAMDBDataPAGE
+{                                               // MD-PRO function
+  int i;
+
+  ttt_ram_enable ();
+  ttt_set_ai_data (6, 0x98);
+  set_addr_write (addr * 2);
+  for (i = 0; i < 0x80; i++)
+    {
+      outportb (port_c, buf[(addr + i) & 0x3fff]);
+      outportb (port_c, buf[(addr + i) & 0x3fff]);
+    }
+  ttt_ram_disable ();
+}
+
+
+#if 0
+void
+readstatus (void)
+{                                               // MD-PRO function
+  // send command 0xe8
+  ttt_rom_enable ();
+  set_ai (3);
+  set_data_read ();
+  while (inportb (port_c) < 0x80)
+    ;
+  ttt_rom_disable ();
+}
+#endif
+
+#endif // USE_PARALLEL

+ 47 - 0
ucon64/2.0/src/backup/tototek.h

@@ -0,0 +1,47 @@
+/*
+tototek.h - General ToToTEK flash card programmer routines for uCON64
+
+Copyright (c) 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef TOTOTEK_H
+#define TOTOTEK_H
+
+#ifdef USE_PARALLEL
+extern void ttt_init_io (unsigned int port);
+extern void ttt_deinit_io (void);
+extern void ttt_set_ai_data (unsigned char ai, unsigned char data);
+extern void ttt_rom_enable (void);
+extern void ttt_rom_disable (void);
+extern void ttt_ram_enable (void);
+extern void ttt_ram_disable (void);
+extern void ttt_write_mem (int addr, unsigned char b);
+extern unsigned short int ttt_get_id (void);
+extern void ttt_read_rom_b (int addr, unsigned char *buf);
+extern void ttt_read_rom_w (int addr, unsigned char *buf);
+extern void ttt_read_ram_b (int addr, unsigned char *buf);
+extern void ttt_read_ram_w (int addr, unsigned char *buf);
+extern void ttt_erase_block (int addr);
+extern void ttt_write_byte_sharp (int addr, unsigned char b);
+extern void ttt_write_byte_intel (int addr, unsigned char b);
+extern void ttt_write_page_rom (int addr, unsigned char *buf);
+extern void ttt_write_byte_ram (int addr, unsigned char b);
+extern void ttt_write_page_ram (int addr, unsigned char *buf);
+extern void ttt_write_page_ram2 (int addr, unsigned char *buf);
+#endif // USE_PARALLEL
+
+#endif // TOTOTEK_H

+ 45 - 0
ucon64/2.0/src/backup/ufo.c

@@ -0,0 +1,45 @@
+/*
+ufo.c - Super UFO support for uCON64
+
+Copyright (c) 2003 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ufo.h"
+
+
+const st_getopt2_t ufo_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Super UFO Pro 8"/*"UFO Enterprises"*/,
+      NULL
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };

+ 144 - 0
ucon64/2.0/src/backup/ufo.h

@@ -0,0 +1,144 @@
+/*
+ufo.h - Super UFO for uCON64
+
+Copyright (c) 2003 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef UFO_H
+#define UFO_H
+
+extern const st_getopt2_t ufo_usage[];
+
+/*
+Super UFO Pro 8 Header Format (researched by John Weidman)
+
+Byte-Offset  Function
+-----------  ---------------------------------------------------
+00-01        Chunk size: 04 00 == 4 Mbit (same format as the DX2)
+02           LoROM games: 0x40 == continue loading another chunk after this one.
+                          0x00 == This is the last chunk.
+             HiROM games: 0x40 == more work to figure this out -- maybe interleave info
+                          0x10 == more work to figure this out -- maybe interleave info
+                          0x00 == This is the last chunk.
+03-07        0x00
+08-0F        53 55 50 45 52 55 46 4F  (SUPERUFO)
+10           0x01 == This file is a ROM image file
+11           ROM size: 04 == 4 Mb, 0x18 == 24 Mb, 0x20 == 32 Mb, etc.
+12           ROM format:  00 == HiROM,  01 == LoROM
+
+====  Start SRAM address mapping config ===============
+
+13          SRAM Size:
+               00:  0Kb
+               01:  16Kb
+               02:  64Kb
+               03:  256Kb
+               04-07: Not used
+               08: XXXKb (Used for sram sizes above 256Kb, like 1024Kb)
+14          SRAM A15 control:
+               00: A15 not used for SRAM control?
+                   Use this for HiROM games
+                   LoROM: Use this if SRAM size = 0Kb (no SRAM)
+               01: A15=X selects SRAM
+               02: A15=0 selects SRAM
+               03: A15=1 selects SRAM
+15          SRAM A20 and A21 control:
+             Bits 3:2
+               00: A21=x selects SRAM
+               01: Not used?
+               10: A21=0 selects SRAM
+               11: A21=1 selects SRAM
+             Bits 1:0
+               00: A20=x selects SRAM
+               01: Not used?
+               10: A20=0 selects SRAM
+               11: A20=1 selects SRAM
+16          SRAM A22 and A23 control:
+             Bits 3:2
+               00: A23=x selects SRAM
+               01: Not used?
+               10: A23=0 selects SRAM
+               11: A23=1 selects SRAM
+             Bits 1:0
+               00: A22=x selects SRAM
+               01: Not used?
+               10: A22=0 selects SRAM
+               11: A22=1 selects SRAM
+17          SRAM type
+               0x00: Linear (HiROM)
+               0x03: Skip (LoROM)
+
+====  End SRAM address mapping config  ================
+
+18-1FF      00
+
+LoROM SRAM header
+=========================================================
+
+The SRAM mapping I would try first for LoROM games is:
+
+0Kb SRAM
+0012-0017       01 00 00 00 02 00
+0Kb LoROM DSP
+0012-0017       01 00 01 0C 00 03
+
+Note: LoROM DSPs with SRAM don't seem to work on the Super UFO
+      (For reference, no LoROM DSP carts work on the SWC DX2)
+
+Non 0 SRAM - default map (map low halves of banks 7x to SRAM)
+0012-0017       01 ss 02 0F 03 03
+Non 0 SRAM - alternate map (map all of banks 7x to SRAM -- will not work for > 28 Mbit
+                            games )
+0012-0017       01 ss 01 0F 03 03
+
+HiROM SRAM header
+==========================================================
+
+0Kb SRAM
+0012-0017       00 00 00 00 02 00
+Non 0 SRAM
+0012-0017       00 ss 00 0C 02 00   (Hopefully this will work for everything?)
+
+If you find an SRAM protected game that doesn't work with the above mapping try:
+0012-0017       00 ss 00 03 02 00   (seen in a couple of games but should work with above
+                                     mapping too)
+--
+For Tales of Phantasia or Dai Kaijyu Monogatari II
+
+0012-0017       00 ss 00 00 0E 00   (Unverified)
+*/
+typedef struct st_ufo_header
+{
+  unsigned char size_low;
+  unsigned char size_high;
+  unsigned char multi;
+  unsigned char pad[5];
+  unsigned char id[8];                          // "SUPERUFO"
+  unsigned char isrom;
+  unsigned char size;
+  unsigned char banktype;
+  unsigned char sram_size;
+  unsigned char sram_a15;
+  unsigned char sram_a20_a21;
+  unsigned char sram_a22_a23;
+  unsigned char sram_type;
+  unsigned char pad2[488];
+} st_ufo_header_t;
+
+#define UFO_HEADER_LEN (sizeof (st_ufo_header_t))
+
+#endif // UFO_H

+ 46 - 0
ucon64/2.0/src/backup/yoko.c

@@ -0,0 +1,46 @@
+/*
+yoko.c - support for Yoko backup unit (Atari 2600, etc.)
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "misc/misc.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "yoko.h"
+
+
+const st_getopt2_t yoko_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "YOKO backup unit",
+      NULL
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };

+ 26 - 0
ucon64/2.0/src/backup/yoko.h

@@ -0,0 +1,26 @@
+/*
+yoko.h - minimal support for Yoko backup unit (Atari 2600, etc.)
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef YOKO_H
+#define YOKO_H
+
+extern const st_getopt2_t yoko_usage[];
+
+#endif

+ 46 - 0
ucon64/2.0/src/backup/z64.c

@@ -0,0 +1,46 @@
+/*
+z64.c - Mr. Backup Z64 support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "misc/misc.h"
+#include "misc/itypes.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "z64.h"
+
+
+const st_getopt2_t z64_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Mr. Backup Z64",
+      NULL
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };

+ 26 - 0
ucon64/2.0/src/backup/z64.h

@@ -0,0 +1,26 @@
+/*
+z64.h - Z64 support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef Z64_H
+#define Z64_H
+
+extern const st_getopt2_t z64_usage[];
+
+#endif

+ 128 - 0
ucon64/2.0/src/config.h.in

@@ -0,0 +1,128 @@
+/* config.h.in.  Generated from configure.in by autoheader.  */
+
+/* enable debug output (default: no) */
+#undef DEBUG
+
+/* enable dynamic loading of add-on libraries (default: yes) */
+#undef DLOPEN
+
+/* Define to 1 if you have the <byteswap.h> header file. */
+#undef HAVE_BYTESWAP_H
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+   */
+#undef HAVE_DIRENT_H
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+#undef HAVE_DOPRNT
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+#undef HAVE_NDIR_H
+
+/* Define to 1 if you have the `realpath' function. */
+#undef HAVE_REALPATH
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strftime' function. */
+#undef HAVE_STRFTIME
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+   */
+#undef HAVE_SYS_DIR_H
+
+/* Define to 1 if you have the <sys/io.h> header file. */
+#undef HAVE_SYS_IO_H
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+   */
+#undef HAVE_SYS_NDIR_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the `vprintf' function. */
+#undef HAVE_VPRINTF
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* enable usage of color (default: yes) */
+#undef USE_ANSI_COLOR
+
+/* enable libdiscmage (default: yes) */
+#undef USE_DISCMAGE
+
+/* enable libcd64 (default: no) */
+#undef USE_LIBCD64
+
+/* enable support for parallel port backup units (default: yes) */
+#undef USE_PARALLEL
+
+/* use ppdev for parallel port I/O (default: no) */
+#undef USE_PPDEV
+
+/* build with (lib)usb support (default: no) */
+#undef USE_USB
+
+/* build with gzip and zip support (default: yes) */
+#undef USE_ZLIB
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_t
+
+/* Define as `__inline' if that's what the C compiler calls it, or to nothing
+   if it is not supported. */
+#undef inline
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef uid_t

+ 129 - 0
ucon64/2.0/src/config.h.orig

@@ -0,0 +1,129 @@
+/* config.h.  Generated by configure.  */
+/* config.h.in.  Generated from configure.in by autoheader.  */
+
+/* enable debug output (default: no) */
+/* #undef DEBUG */
+
+/* enable dynamic loading of add-on libraries (default: yes) */
+#define DLOPEN 1
+
+/* Define to 1 if you have the <byteswap.h> header file. */
+/* #undef HAVE_BYTESWAP_H */
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+   */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+/* #undef HAVE_INTTYPES_H */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+/* #undef HAVE_NDIR_H */
+
+/* Define to 1 if you have the `realpath' function. */
+/* #undef HAVE_REALPATH */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+/* #undef HAVE_STDINT_H */
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define to 1 if you have the <sys/io.h> header file. */
+/* #undef HAVE_SYS_IO_H */
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `vprintf' function. */
+#define HAVE_VPRINTF 1
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "noisyb@gmx.net"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "uCON64"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "uCON64 2.0.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "ucon64"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.0.0"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* enable usage of color (default: yes) */
+#define USE_ANSI_COLOR 1
+
+/* enable libdiscmage (default: yes) */
+#define USE_DISCMAGE 1
+
+/* enable libcd64 (default: no) */
+#define USE_LIBCD64 1
+
+/* enable support for parallel port backup units (default: yes) */
+#define USE_PARALLEL 1
+
+/* use ppdev for parallel port I/O (default: no) */
+/* #undef USE_PPDEV */
+
+/* build with (lib)usb support (default: no) */
+/* #undef USE_USB */
+
+/* build with gzip and zip support (default: yes) */
+//#define USE_ZLIB 1
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Define as `__inline' if that's what the C compiler calls it, or to nothing
+   if it is not supported. */
+/* #undef inline */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */

+ 129 - 0
ucon64/2.0/src/config.h.vc6

@@ -0,0 +1,129 @@
+/* config.h.  Generated by configure.  */
+/* config.h.in.  Generated from configure.in by autoheader.  */
+
+/* enable debug output (default: no) */
+/* #undef DEBUG */
+
+/* enable dynamic loading of add-on libraries (default: yes) */
+#define DLOPEN 1
+
+/* Define to 1 if you have the <byteswap.h> header file. */
+/* #undef HAVE_BYTESWAP_H */
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_DIRENT_H */
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+/* #undef HAVE_INTTYPES_H */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+/* #undef HAVE_NDIR_H */
+
+/* Define to 1 if you have the `realpath' function. */
+/* #undef HAVE_REALPATH */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+/* #undef HAVE_STDINT_H */
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+/* #undef HAVE_STRINGS_H */
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define to 1 if you have the <sys/io.h> header file. */
+/* #undef HAVE_SYS_IO_H */
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+/* #undef HAVE_UNISTD_H */
+
+/* Define to 1 if you have the `vprintf' function. */
+#define HAVE_VPRINTF 1
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "noisyb@gmx.net"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "uCON64"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "uCON64 2.0.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "ucon64"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.0.0"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* enable usage of color (default: yes) */
+#define USE_ANSI_COLOR 1
+
+/* enable libdiscmage (default: yes) */
+#define USE_DISCMAGE 1
+
+/* enable libcd64 (default: no) */
+#define USE_LIBCD64 1
+
+/* enable support for parallel port backup units (default: yes) */
+#define USE_PARALLEL 1
+
+/* use ppdev for parallel port I/O (default: no) */
+/* #undef USE_PPDEV */
+
+/* build with (lib)usb support (default: no) */
+/* #undef USE_USB */
+
+/* build with gzip and zip support (default: yes) */
+//#define USE_ZLIB 1
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Define as `__inline' if that's what the C compiler calls it, or to nothing
+   if it is not supported. */
+/* #undef inline */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 312 - 0
ucon64/2.0/src/configure


+ 183 - 0
ucon64/2.0/src/configure.in

@@ -0,0 +1,183 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(uCON64, 2.0.0, noisyb@gmx.net)
+AC_CONFIG_HEADER(config.h libdiscmage/config.h)
+
+
+dnl Checks for programs.
+AC_PROG_CC(gcc egcs cc)
+AC_PROG_GCC_TRADITIONAL
+
+
+AC_MSG_CHECKING(whether debug output is enabled)
+AC_ARG_ENABLE(debug,
+              AC_HELP_STRING([--enable-debug],
+                             [enable debug output (default: no)]),
+              enable_debug=$enableval,
+              enable_debug=no)
+if test $enable_debug = yes; then
+  AC_DEFINE(DEBUG, 1, [enable debug output (default: no)])
+fi
+AC_MSG_RESULT($enable_debug)
+
+
+AC_MSG_CHECKING(whether support for parallel port backup units is enabled)
+AC_ARG_ENABLE(parallel,
+              AC_HELP_STRING([--enable-parallel],
+                             [enable support for parallel port backup units (default: yes)]),
+              enable_parallel=$enableval,
+              enable_parallel=yes)
+if test $enable_parallel = yes; then
+  AC_DEFINE(USE_PARALLEL, 1, [enable support for parallel port backup units (default: yes)])
+fi
+AC_MSG_RESULT($enable_parallel)
+
+
+dnl AC_MSG_CHECKING(whether support for serial port backup units is enabled)
+dnl AC_ARG_ENABLE(serial,
+dnl              AC_HELP_STRING([--enable-serial],
+dnl                              [enable support for serial port backup units (default: yes)]),
+dnl               enable_serial=$enableval,
+dnl               enable_serial=yes)
+dnl if test $enable_serial = yes; then
+dnl   AC_DEFINE(USE_SERIAL, 1, [enable support for serial port backup units (default: yes)])
+dnl fi
+dnl AC_MSG_RESULT($enable_serial)
+
+
+AC_MSG_CHECKING(whether to use ppdev)
+AC_ARG_ENABLE(ppdev,
+              AC_HELP_STRING([--enable-ppdev],
+                             [use ppdev for parallel port I/O (default: no)]),
+              enable_ppdev=$enableval,
+              enable_ppdev=no)
+if test $enable_ppdev = yes; then
+  AC_TRY_COMPILE([#include <linux/ppdev.h>], , enable_ppdev=yes, enable_ppdev=no)
+fi
+if test $enable_ppdev = yes; then
+  AC_DEFINE(USE_PPDEV, 1, [use ppdev for parallel port I/O (default: no)])
+fi
+AC_MSG_RESULT($enable_ppdev)
+
+
+AC_MSG_CHECKING(whether the use of color is enabled)
+AC_ARG_ENABLE(ansi_color,
+              AC_HELP_STRING([--enable-ansi-color],
+                             [enable usage of color (default: yes)]),
+              enable_ansi_color=$enableval,
+              enable_ansi_color=yes)
+if test $enable_ansi_color = yes; then
+  AC_DEFINE(USE_ANSI_COLOR, 1, [enable usage of color (default: yes)])
+fi
+AC_MSG_RESULT($enable_ansi_color)
+
+
+AC_MSG_CHECKING(whether add-on libraries are dynamically loaded)
+AC_ARG_ENABLE(dload,
+              AC_HELP_STRING([--enable-dload],
+                             [enable dynamic loading of add-on libraries (default: yes)]),
+              enable_dload=$enableval,
+              enable_dload=yes)
+if test $enable_dload = yes; then
+  AC_DEFINE(DLOPEN, 1, [enable dynamic loading of add-on libraries (default: yes)])
+  DEFINE_DLOPEN_MAKE="DLOPEN=1"
+fi
+AC_MSG_RESULT($enable_dload)
+AC_SUBST(DEFINE_DLOPEN_MAKE)
+
+
+AC_MSG_CHECKING(whether libdiscmage is enabled)
+AC_ARG_ENABLE(discmage,
+              AC_HELP_STRING([--enable-discmage],
+                             [enable libdiscmage (default: yes)]),
+              enable_discmage=$enableval,
+              enable_discmage=yes)
+if test $enable_discmage = yes; then
+  AC_DEFINE(USE_DISCMAGE, 1, [enable libdiscmage (default: yes)])
+  DEFINE_DISCMAGE_MAKE="USE_DISCMAGE=1"
+fi
+AC_MSG_RESULT($enable_discmage)
+AC_SUBST(DEFINE_DISCMAGE_MAKE)
+
+
+dnl This belongs here, not at the checks for libraries. We don't have to check
+dnl  if libcd64 is present as it is part of our source tree.
+AC_MSG_CHECKING(whether libcd64 is enabled)
+AC_ARG_ENABLE(libcd64,
+              AC_HELP_STRING([--enable-libcd64],
+                             [enable libcd64 (default: no)]),
+              enable_libcd64=$enableval,
+              enable_libcd64=no)
+if test $enable_libcd64 = yes; then
+  AC_DEFINE(USE_LIBCD64, 1, [enable libcd64 (default: no)])
+  DEFINE_LIBCD64_MAKE="USE_LIBCD64=1"
+else
+  dnl libi386 is necessary under OpenBSD, but only if libcd64 isn't enabled. The
+  dnl  reason is that libcd64 already includes libi386.
+  LIBI386_MAKE="-li386"
+fi
+AC_MSG_RESULT($enable_libcd64)
+AC_SUBST(DEFINE_LIBCD64_MAKE)
+AC_SUBST(LIBI386_MAKE)
+
+
+dnl Checks for libraries.
+AC_MSG_CHECKING(for zlib)
+AC_ARG_WITH(zlib,
+            AC_HELP_STRING([--with-zlib],
+                           [build with gzip and zip support (default: yes)]),
+            with_zlib=$withval,
+            with_zlib=yes)
+if test $with_zlib = yes; then
+  AC_TRY_COMPILE([#include <zlib.h>], , with_zlib=yes, with_zlib=no)
+fi
+if test $with_zlib = yes; then
+  AC_DEFINE(USE_ZLIB, 1, [build with gzip and zip support (default: yes)])
+  DEFINE_ZLIB_MAKE="USE_ZLIB=1"
+fi
+AC_MSG_RESULT($with_zlib)
+AC_SUBST(DEFINE_ZLIB_MAKE)
+
+
+AC_MSG_CHECKING(for libusb)
+AC_ARG_WITH(libusb,
+            AC_HELP_STRING([--with-libusb],
+                           [build with (lib)usb support (default: no)]),
+            with_libusb=$withval,
+            with_libusb=no)
+if test $with_libusb = yes; then
+  AC_TRY_COMPILE([#include <usb.h>], , with_libusb=yes, with_libusb=no)
+fi
+if test $with_libusb = yes; then
+  AC_DEFINE(USE_USB, 1, [build with (lib)usb support (default: no)])
+  DEFINE_USB_MAKE="USE_USB=1"
+fi
+AC_MSG_RESULT($with_libusb)
+AC_SUBST(DEFINE_USB_MAKE)
+
+
+dnl Checks for header files.
+AC_HEADER_DIRENT
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h unistd.h byteswap.h inttypes.h sys/io.h)
+dnl NOT zlib.h! Or else --with[out]-zlib gets overrriden in config.h.
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_UID_T
+AC_C_INLINE
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+
+dnl Checks for library functions.
+AC_FUNC_MEMCMP
+AC_FUNC_STRFTIME
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS(realpath)
+
+AC_PROG_RANLIB
+AC_PROG_INSTALL
+
+AC_OUTPUT(Makefile libdiscmage/Makefile)
+
+echo
+echo NOTE: On non-Linux systems you might need to use gmake instead of make

+ 65 - 0
ucon64/2.0/src/console/boardnames

@@ -0,0 +1,65 @@
+351258: UNROM
+351298: UNROM
+351908
+352026: TLROM (w/ LS32 for VROM enable control)
+51555: Acclaim, MMC3B mapper, PRG ROM, CHR ROM
+53361
+54425
+55741
+56504
+AMROM: LS161, VRAM, PRG-ROM
+ANROM: LS161+LS02 mapper, PRG-ROM, CHR-RAM
+AOROM: LS161 mapper, PRG-ROM, CHR-ROM
+BNROM: LS161, VRAM, PRG-ROM (Different LS161 bits?  Only used on Deadly Towers)
+CNROM: LS161 mapper, PRG-ROM, CHR-ROM?/CHR-RAM
+COB:   "Glop Top" style board
+CPROM: LS04, LS08, LS161, 32K ROM, 16K VRAM (bankswitched, Videomation only)
+DEIROM
+DEROM
+DRROM: MMC3, 4K of nametable RAM (for 4-screen), PRG-ROM, CHR-ROM (only in Gauntlet)
+EKROM
+ELROM: MMC5, PRG-ROM, CHR-ROM
+ETROM: MMC5, PRG-ROM, CHR-ROM, 2x 8k optionnal RAM (battery)
+EWROM: MMC5, PRG-ROM, CHR-ROM, 32k optionnal RAM (battery)
+GNROM: LS161 mapper, PRG ROM, CHR ROM
+HKROM: MMC6B, PRG-ROM, CHR-ROM, Battery
+MHROM: LS161 mapper, black blob chips. Mario Bros / Duck Hunt multi
+NES-B4: Same as TLROM
+NES-BTR: Sunsoft FME7 mapper, PRG ROM, CHR ROM, 8k optionnal RAM 
+NES-QJ:
+NES-RROM: Same as NROM (Only used in Clu Clu land)
+NROM: No mapper, PRG-ROM, CHR-ROM
+PNROM: MMC2, PRG-ROM, CHR-ROM
+SAROM: MMC1B, PRG ROM, CHR ROM, optional 8k of RAM (battery)
+SBROM: MMC1A, PRG ROM, CHR ROM (only 32K of CHR ROM)
+SCEOROM
+SC1ROM: MMC1B, PRG ROM, CHR ROM
+SCROM: LS161, LS02, VRAM, PRG-ROM (Similar to UNROM)
+SEROM: MMC1B, PRG ROM, CHR ROM
+SFROM
+SGROM: MMC1B, PRG ROM, 8k CHR RAM
+SHROM
+SJROM
+SKROM: MMC1B, PRG ROM, CHR ROM, 8k optional RAM (battery)
+SL1ROM: MMC3, PRG ROM, CHR ROM, LS32 (for 128K 28 pin CHR ROMs)
+SL2ROM
+SL3ROM
+SLROM: MMC1A, PRG ROM, CHR ROM
+SLRROM
+SN1-ROM AW (Overlord only)
+SNROM: MMC1A, PRG ROM, CHR ROM/RAM ?, 8k optional RAM (battery)  
+SOROM: MMC1B2, PRG ROM, VRAM, 16K of WRAM (Battery) Only 8K battery-backed
+SVROM: MMC1B2, PRG ROM, VRAM, WRAM (Battery)
+SUROM: MMC1B2, PRG ROM, CHR RAM/(ROM?), 8k battery-backed RAM (DW4???)
+TEROM: MMC3A, PRG ROM, CHR ROM, (32k ROMs)
+TFROM: MMC3B, PRG ROM, CHR ROM (64K of CHR only)
+TGROM: MMC3C, PRG ROM, VRAM (512K of PRG)
+TKROM: MMC3A, PRG ROM, CHR ROM, 8k optional RAM (battery)
+TL1ROM: Same as TLROM
+TLROM: MMC3B, PRG ROM, CHR ROM
+TLSROM: Same as TLROM
+TQROM: MMC3B+74HC32, PRG ROM, CHR ROM + 8k of CHR-RAM
+TSROM: MMC3A, PRG ROM, CHR ROM, 8k optionnal RAM
+TVROM: MMC3B, PRG ROM, CHR ROM, 4K of Nametable RAM (4-screen)
+UNROM: 74LS32+74LS161 mapper, 128k PRG, 8k CHR-RAM
+UOROM

+ 782 - 0
ucon64/2.0/src/console/boardtable.txt

@@ -0,0 +1,782 @@
+Name                                  |Maker               |RR|Board           
+--------------------------------------+--------------------+--+----------------
+10 Yard Fight                         |Nintendo            |E |NROM            
+110 in 1 (Canada?)                    |Supervision         |  |                
+1942                                  |Capcom              |C+|NROM            
+1943                                  |Capcom              |C+|UNROM           
+6 in 1                                |Caltron             |A |                
+720                                   |Mindscape           |C |SGROM           
+8 Eyes                                |Taxan               |C-|TLROM           
+A Boy and His Blob                    |Absolute            |C |SLROM           
+Abadox                                |Milton Bradley      |C |SLROM           
+Action 52                             |Active Enterprises  |A |                
+Addams Family                         |Ocean               |B |SLROM           
+Adv of Bayou Billy                    |Konami              |C-|SLROM           
+Adv of Dino Riki                      |Hudson              |C |CNROM           
+Adv of Lolo                           |HAL                 |C+|SEROM           
+Adv of Lolo 2                         |HAL                 |B-|TEROM           
+Adv of Lolo 3                         |HAL                 |B+|SLROM           
+Adv of Tom Saywer                     |Seta                |B |SLROM           
+Adventure Island                      |Hudson              |D |CNROM           
+Adventure Island 2                    |Hudson              |B-|TLROM           
+Adventure Island 3                    |Hudson              |B |TLROM           
+After Burner                          |Tengen              |C+|                
+Air Fortress                          |HAL                 |C |SJROM           
+Airwolf                               |Acclaim             |C |SHROM           
+Al Unser Racing                       |Data East           |C |SKROM           
+Aladdin Deck Enhancer                 |Camerica            |A+|                
+Alfred Chicken                        |Mindscape           |B+|UNROM           
+Alien 3                               |LJN                 |B+|55741           
+Alien Syndrome                        |Tengen              |C+|                
+All Pro Basketball                    |Vic Tokai           |C |SLROM           
+Alpha Mission                         |SNK                 |C |CNROM           
+Amagon                                |American Sammy      |C |UNROM           
+American Gladiators                   |Gametek             |B-|SLROM           
+Anticipation                          |Nintendo            |C |SEROM           
+Arch Rivals                           |Acclaim             |C |AMROM           
+Archon                                |Activision          |B |UNROM           
+Arkanoid                              |Taito               |B+|CNROM           
+Arkanoid Controller                   |Taito               |A |                
+Arkistas Ring                         |American Sammy      |B |CNROM           
+Astyanax                              |Jaleco              |C |TLROM           
+Athena                                |SNK                 |C |UNROM           
+Athletic World                        |Bandai              |B-|CNROM           
+Attack/Killer Tomato                  |T*HQ                |B-|SLROM           
+Baby Boomer                           |Color Dreams        |B-|                
+Back to the Future                    |LJN                 |D |CNROM           
+Back to the Future 2 & 3              |LJN                 |C |SLROM           
+Bad Dudes                             |Data East           |C |TLROM           
+Bad News Baseball                     |Tecmo               |B |SLROM           
+Bad Street Brawler                    |Mattel              |C |SGROM           
+Balloon Fight                         |Nintendo            |C |NROM            
+Bandit Kings/China                    |Koei                |B-|ETROM           
+Barbie                                |Hi Tech             |B |SLROM           
+Bard's Tale                           |FCI                 |B-|SNROM           
+Base Wars                             |Ultra               |C |TKROM           
+Baseball                              |Nintendo            |E |NROM            
+Baseball Simulator                    |Culture Brain       |C+|SKROM           
+Baseball Stars                        |SNK                 |C+|SKROM           
+Baseball Stars 2                      |Romstar             |B+|TKROM           
+Bases Loaded                          |Jaleco              |E |SFROM           
+Bases Loaded 2                        |Jaleco              |D |SL3ROM          
+Bases Loaded 3                        |Jaleco              |C |TLROM           
+Bases Loaded 4                        |Jaleco              |C+|TLROM           
+Batman                                |Sunsoft             |D |NES_B4          
+Batman Return/Joker                   |Sunsoft             |B-|NES_BTR         
+Batman Returns                        |Konami              |B+|TLROM           
+Battle Chess                          |Data East           |B+|SKROM           
+Battle of Olympus                     |Broderbund          |C |SGROM           
+Battle Tank                           |Absolute            |B |CNROM           
+Battle Toads                          |Tradewest           |D |AOROM           
+Battle Toads & Double Dragon          |Tradewest           |B+|AOROM           
+Battleship                            |Mindscape           |B |CNROM           
+Bee 52                                |Camerica            |B+|                
+Beetlejuice                           |LJN                 |C+|AMROM           
+Best of the Best                      |Electro Brain       |B+|UOROM           
+Bible Adventures                      |Wisdom Tree         |B+|                
+Bible Adventures (blue label)         |Wisdom Tree         |B+|                
+Bible Buffet                          |Wisdom Tree         |B+|                
+Big Bird Hide & Speak                 |Hi Tech             |B |SLROM           
+Big Foot                              |Acclaim             |B-|SLROM           
+Big Nose Freaks Out                   |Camerica            |A |                
+Big Nose Freaks Out (Aladdin Cart)    |Camerica            |A+|                
+Big Nose the Caveman                  |Camerica            |B |                
+Bill & Ted Adventure                  |LJN                 |C+|SLROM           
+Bill Elliot NASCAR                    |Konami              |C+|TSROM           
+Bionic Commando                       |Capcom              |C |SGROM           
+Black Bass                            |Hot B               |B |UNROM           
+Blackjack                             |American Video      |A-|                
+Blades of Steel                       |Konami              |D |UNROM           
+Blaster Master                        |Sunsoft             |D |SL2ROM          
+Blue Marlin                           |Hot B               |B+|TLROM           
+Blues Brothers                        |Titus               |B |UNROM           
+Bo Jackson Baseball                   |Data East           |C+|TSROM           
+Bomberman                             |Hudson              |C |NROM            
+Bomberman 2                           |Hudson              |B |SNROM           
+Bonks Adventure                       |Hudson              |B+|TLROM           
+Boulder Dash                          |JVC                 |B+|SEROM           
+Break Time                            |FCI                 |B+|SFROM           
+Breakthru                             |Data East           |C |(SLROM)         
+Bubble Bath Babes                     |Panesian            |A+|                
+Bubble Bobble                         |Taito               |D |SFROM           
+Bubble Bobble 2                       |Taito               |B |TLROM           
+Bucky O'Hare                          |Konami              |B |TLROM           
+Bugs Bunny Birthday Blowout           |Kemco               |B |TSROM           
+Bugs Bunny Castle                     |Kemco               |B+|SBROM           
+Bump & Jump                           |Vic Tokai           |B-|CNROM           
+Burai Fighter                         |Taxan               |B-|TEROM           
+Burger Time                           |Data East           |B-|NROM            
+Cabal                                 |Milton Bradley      |C |AMROM           
+Caesars Palace                        |Virgin              |B |UNROM           
+California Games                      |Milton Bradley      |C |UNROM           
+Captain America                       |Data East           |B |TLROM           
+Captain Comic                         |Color Dreams        |B |                
+Captain Planet                        |Mindscape           |B |TLROM           
+Captain Skyhawk                       |Milton Bradley      |C |AMROM           
+Casino Kid                            |Sofel               |C+|UNROM           
+Casino Kid 2                          |Sofel               |B+|UNROM           
+Castelian                             |Triffix             |B |UNROM           
+Castle of Deceit                      |Bunch Games         |B |                
+Castle of Dragon                      |Seta                |B |UNROM           
+Castlequest                           |Nexoft              |B |CNROM           
+Castlevania                           |Konami              |E |UNROM           
+Castlevania 2                         |Konami              |D |SLROM           
+Castlevania 3                         |Konami              |C-|ELROM           
+Caveman Games                         |Data East           |B-|SLROM           
+Challenge of the Dragon               |Color Dreams        |B |                
+Championship Bowling                  |Romstar             |B |CNROM           
+Championship Pool                     |Mindscape           |B+|UNROM           
+Cheetahman II                         |Active Enterprises  |A |                
+Chessmaster                           |Hi Tech             |B |SJROM           
+Chiller                               |American Game Carts Inc|B+|                
+Chubby Cherub                         |Bandai              |B |NROM            
+Circus Caper                          |Toho                |B |SLROM           
+City Connection                       |Jaleco              |B |CNROM           
+Clash at Demonhead                    |Vic Tokai           |B-|SLROM           
+Classic Concentration                 |Gametek             |B-|UNROM           
+Cliffhanger                           |Imagesoft           |B |TLROM           
+Clu Clu Land                          |Nintendo            |B-|NES_RROM        
+Cobra Command                         |Data East           |C |SLROM           
+Cobra Triangle                        |Nintendo            |B-|ANROM           
+Codename Viper                        |Capcom              |C |TLROM           
+Color A Dinosaur                      |Virgin              |A-|UNROM           
+Commando                              |Capcom              |C |UNROM           
+Conan                                 |Mindscape           |B |UNROM           
+Conflict                              |Vic Tokai           |C+|SKROM           
+Conquest Crystal Palace               |Asmik               |C+|TLROM           
+Contra                                |Konami              |E |UNROM           
+Contra Force                          |Konami              |B |TLROM           
+Cool World                            |Ocean               |B |SLROM           
+Cowboy Kid                            |Romstar             |A-|TLROM           
+Crash & the Boys Street Challange     |American Technos    |B |TLROM           
+Crash Dummies                         |LJN                 |B |55741           
+Crystal Mines                         |Color Dreams        |B+|                
+Crystalis                             |SNK                 |C |TKROM           
+Cyberball                             |Jaleco              |B |TLROM           
+Cybernoid                             |Acclaim             |C |CNROM           
+Dance Aerobics                        |Nintendo            |C+|SBROM           
+Darkman                               |Ocean               |B |SLROM           
+Darkwing Duck                         |Capcom              |B+|SLROM           
+Dash Galaxy                           |Data East           |C |CNROM           
+Day Dreamin' Davey                    |HAL                 |B+|SLROM           
+Days of Thunder                       |Mindscape           |C+|TLROM           
+Deadly Towers                         |Broderbund          |C+|BNROM           
+Death Race                            |American Game Carts Inc|B+|                
+Deathbots                             |American Video      |B+|                
+Defender 2                            |HAL                 |B |NROM            
+Defender of the Crown                 |Ultra               |C+|SGROM           
+Defenders of Dynacron City            |JVC                 |B |TLROM           
+Deja Vu                               |Kemco               |C+|TKROM           
+Demon Sword                           |Taito               |C+|SL1ROM          
+Desert Commander                      |Kemco               |C-|SKROM           
+Destination Earthstar                 |Acclaim             |C |CNROM           
+Destiny/Emporer                       |Capcom              |B-|SNROM           
+Dick Tracy                            |Bandai              |C+|UNROM           
+Die Hard                              |Activision          |B+|SLROM           
+Dig Dug 2                             |Bandai              |B |NROM            
+Digger                                |Milton Bradley      |B+|AMROM           
+Dirty Harry                           |Mindscape           |C+|TLROM           
+Disney Adventure                      |Capcom              |B |SLROM           
+Dizzy the Adventurer (Aladdin Cart)   |Camerica            |A+|                
+Donkey Kong                           |Nintendo            |B-|NROM            
+Donkey Kong 3                         |Nintendo            |B-|NROM            
+Donkey Kong Classics                  |Nintendo            |B-|CNROM           
+Donkey Kong Jr                        |Nintendo            |B-|NROM            
+Donkey Kong Jr Math                   |Nintendo            |A-|NROM            
+Double Dare                           |Gametek             |B |AOROM           
+Double Dragon                         |Tradewest           |E |SLROM           
+Double Dragon 2                       |Acclaim             |D |TL1ROM          
+Double Dragon 3                       |Acclaim             |C+|TLROM           
+Double Dribble                        |Konami              |D |UNROM           
+Double Strike                         |American Video      |B+|                
+Dr. Chaos                             |FCI                 |C |UNROM           
+Dr. Jeckyl/Mr. Hyde                   |Bandai              |C+|SFROM (SFDOROM) 
+Dr. Mario                             |Nintendo            |D |SEROM           
+Dracula                               |Imagesoft           |B |TSROM           
+Dragon Fighter                        |Sofel               |B |SLROM           
+Dragon Power                          |Bandai              |C |GNROM           
+Dragon Spirit                         |Bandai              |B |TLROM           
+Dragon Strike                         |FCI                 |C |TLROM           
+Dragon Warrior                        |Nintendo            |E |SAROM           
+Dragon Warrior 2                      |Enix                |C+|SNROM           
+Dragon Warrior 3                      |Enix                |B+|SVROM           
+Dragon Warrior 4                      |Enix                |B |SVROM           
+Dragon's Lair                         |Imagesoft           |B |UNROM           
+Duck Hunt                             |Nintendo            |F |NROM            
+Duck Tales                            |Capcom              |C+|UNROM           
+Duck Tales 2                          |Capcom              |B+|UNROM           
+Dudes with Attitude                   |American Video      |B+|                
+Dungeon Magic                         |Taito               |C |SKROM           
+Dusty Diamond All Star Softball       |Broderbund          |B-|SLROM           
+Dyno Warz                             |Bandai              |C+|SLROM           
+Elevator Action                       |Taito               |B-|NROM            
+Eliminator Boat Duel                  |Electro Brain       |C+|SLROM           
+Empire Strikes Back                   |JVC                 |B |TLROM           
+Excitebike                            |Nintendo            |E |NROM            
+Exodus                                |Wisdom Tree         |B+|                
+F 117 Stealth                         |Microprose          |B+|TLROM           
+F 15 City War                         |American Video      |B |                
+F 15 Strike Eagle                     |Microprose          |B+|TLROM           
+F1 Built to Win                       |Seta                |B+|SKROM           
+Family Feud                           |Gametek             |B+|SHROM           
+Fantastic Adv Dizzy (Aladdin Cart)    |Camerica            |A+|                
+Fantastic Adventures of Dizzy         |Camerica            |B-|                
+Fantasy Zone                          |Tengen              |C |                
+Faria                                 |Nexoft              |B+|SKROM           
+Fast Break                            |Tradewest           |C |SCROM           
+Faxanadu                              |Nintendo            |C-|SGROM           
+Felix the Cat                         |Hudson              |B+|TSROM           
+Ferrari Grand Prix                    |Acclaim             |B |SLROM           
+Fester's Quest                        |Sunsoft             |C |SLROM           
+Fighting Golf                         |SNK                 |C |SLROM           
+Final Fantasy                         |Nintendo            |C+|SNROM           
+Fire & Ice                            |Tecmo               |A-|TLROM           
+Fire Hawk                             |Camerica            |B |                
+Firehouse Rescue                      |Gametek             |B |CNROM           
+Fist of the North Star                |Taxan               |C+|UNROM           
+Flight of the Intruder                |Mindscape           |B |UNROM           
+Flintstones                           |Taito               |C+|TLROM           
+Flintstones 2                         |Taito               |A+|                
+Flying Dragon                         |Culture Brain       |C |UNROM           
+Flying Warriors                       |Culture Brain       |C |SLROM           
+Frankenstein                          |Bandai              |B+|SLRROM          
+Freedom Force                         |Sunsoft             |D |SLROM           
+Friday The 13th                       |LJN                 |C-|CNROM           
+Fun House                             |Hi Tech             |B+|UNROM           
+G I Joe                               |Taxan               |B+|TLROM           
+G I Joe Atlantis Factor               |Capcom              |C+|TLROM           
+Galactic Crusader                     |Bunch Games         |B |                
+Galaga                                |Bandai              |B |NROM            
+Galaxy 5000                           |Activision          |A-|TLROM           
+Game Action Replay                    |STD                 |A-|                
+Game Genie                            |Galoob              |D |                
+Gargoyle's Quest 2                    |Capcom              |B+|TLROM           
+Gauntlet (licensed)                   |Tengen              |C |DRROM           
+Gauntlet (unlicensed)                 |Tengen              |C |                
+Gauntlet 2                            |Mindscape           |C |TSROM           
+Gemfire                               |Koei                |B-|EKROM           
+Genghis Kahn                          |Koei                |C+|SOROM           
+George Forman                         |Acclaim             |B |55741           
+Ghost & Goblins                       |Capcom              |C |UNROM           
+Ghost Lion                            |Kemco               |B |SKROM           
+Ghostbusters                          |Activision          |B-|CNROM           
+Ghostbusters 2                        |Activision          |B |SLROM           
+Ghoul School                          |Electro Brain       |B-|SLROM           
+Gilligans Island                      |Bandai              |B+|UNROM           
+Goal                                  |Jaleco              |D |SL3ROM          
+Goal 2                                |Jaleco              |B-|TLSROM          
+Godzilla                              |Toho                |C+|SLROM           
+Godzilla 2                            |Toho                |B |SLROM           
+Gold Metal Challenge                  |Capcom              |B+|TKROM           
+Golf                                  |Nintendo            |E |NROM            
+Golf Grand Slam                       |Atlus               |B+|SLROM           
+Golf Power                            |Virgin              |B+|SNROM           
+Golgo 13 Top Secret Episode           |Vic Tokai           |D |SLROM           
+Goonies 2                             |Konami              |C+|351258          
+Gotcha                                |LJN                 |C |CNROM           
+Gradius                               |Konami              |C-|CNROM           
+Great Waldo Search                    |T*HQ                |B-|SLROM           
+Gremlins 2                            |Sunsoft             |B-|TLROM           
+Guardian Legend                       |Broderbund          |C+|UNROM           
+Guerrilla War                         |SNK                 |C |SLROM           
+Gum Shoe                              |Nintendo            |C+|GNROM           
+Gun Nac                               |Ascii               |B |TLROM           
+Gunsmoke                              |Capcom              |B-|UNROM           
+Gyromite                              |Nintendo            |E |NROM            
+Gyruss                                |Ultra               |C+|CNROM           
+Harlem Globetrotters                  |Gametek             |B |SLROM           
+Hatris                                |Bullet Proof        |A |SNROM           
+Heavy Barrel                          |Data East           |C |TLROM           
+Heavy Shreddin'                       |Parker Brothers     |B-|SLROM           
+Heroes of the Lance                   |FCI                 |C |SKROM           
+High Speed                            |Tradewest           |B-|TQROM           
+Hillsfar                              |FCI                 |B |SNROM           
+Hogan's Alley                         |Nintendo            |D |NROM            
+Hollywood Squares                     |Gametek             |B+|UNROM           
+Home Alone                            |T*HQ                |B-|TSROM           
+Home Alone 2                          |T*HQ                |B-|TLROM           
+Hook                                  |Imagesoft           |B |SLROM           
+Hoops                                 |Jaleco              |D |SLROM           
+Hot Slots                             |Panesian            |A+|                
+Hudson Hawk                           |Imagesoft           |B |SLROM           
+Hunt for Red October                  |Hi Tech             |C |TLROM           
+Hydlide                               |FCI                 |D |NROM            
+I Can Remember                        |Gametek             |B |CNROM           
+Ice Climber                           |Nintendo            |C+|NROM            
+Ice Hockey                            |Nintendo            |D |NROM            
+Ikari Warriors                        |SNK                 |C-|UNROM           
+Ikari Warriors 2                      |SNK                 |C-|SGROM           
+Ikari Warriors 3                      |SNK                 |B-|SLROM           
+Image Fight                           |Irem                |B |TSROM           
+Immortal                              |Electronic Arts     |B |TLROM           
+Impossible Mission 2                  |American Video      |A-|                
+Impossible Mission 2                  |SEI                 |B+|                
+Indiana Jones Last Crusade            |UBI Soft            |B+|UNROM           
+Indiana Jones Last Crusade            |Taito               |B+|SGROM           
+Indiana Jones Temple of Doom          |Tengen              |B-|                
+Indiana Jones Temple of Doom          |Mindscape           |C |TFROM           
+Indy Heat                             |Tradewest           |B |AMROM           
+Infiltrator                           |Mindscape           |B |TLROM           
+Iron Tank                             |SNK                 |C |SLROM           
+Isolated Warrior                      |NTVIC               |B |TLROM           
+Jack Nicklaus Golf                    |Konami              |B-|351258          
+Jackal                                |Konami              |C |UNROM           
+Jackie Chan Kung Fu                   |Hudson              |B |TLROM           
+James Bond Jr                         |T*HQ                |B-|TLROM           
+Jaws                                  |LJN                 |D |CNROM           
+Jeopardy                              |Gametek             |C |AOROM           
+Jeopardy 25th Anniversary             |Gametek             |C+|ANROM           
+Jeopardy Jr                           |Gametek             |C |ANROM           
+Jeopardy Super                        |Gametek             |B |SLROM           
+Jetsons                               |Taito               |B+|TLROM           
+Jimmy Connors Tennis                  |UBI Soft            |B+|UNROM           
+Joe & Mac                             |Data East           |B+|TLROM           
+Jordan vs. Bird                       |Milton Bradley      |C |UNROM           
+Joshua                                |Wisdom Tree         |B+|                
+Journey to Silius                     |Sunsoft             |C+|SLROM           
+Joust                                 |HAL                 |B |CNROM           
+Jungle Book                           |Virgin              |A-|TLROM           
+Jurassic Park                         |Ocean               |B |TSROM           
+Karate Champ                          |Data East           |C |CNROM           
+Karate Kid                            |LJN                 |C |CNROM           
+Karnov                                |Data East           |C |DEIROM          
+Kick Master                           |Taito               |C+|TLROM           
+Kickle Cubicle                        |Irem                |B+|TLROM           
+Kid Icarus                            |Nintendo            |C-|SNROM           
+Kid Klown                             |Kemco               |B |TSROM           
+Kid Kool                              |Vic Tokai           |C+|UNROM           
+Kid Niki                              |Data East           |C |SGROM           
+King Neptune's Adventure              |Color Dreams        |B+|                
+King of Kings                         |Wisdom Tree         |B+|                
+King of Kings (cartoon mule)          |Wisdom Tree         |B+|                
+King of the Ring                      |Acclaim             |B+|55741           
+Kings Knight                          |Square              |C+|CNROM           
+Kings of the Beach                    |Ultra               |C |CNROM           
+Kings Quest 5                         |Konami              |B+|TSROM           
+Kirbys Adventure                      |Nintendo            |B-|TKROM           
+Kiwi Kraze                            |Taito               |B+|TLROM           
+Klash Ball                            |Sofel               |B-|UNROM           
+Klax                                  |Tengen              |B+|                
+Knight Rider                          |Acclaim             |B-|SCIROM          
+Krazy Kreatures                       |American Video      |B+|                
+Krion Conquest                        |Vic Tokai           |B |TLROM           
+Krusty's Fun House                    |Acclaim             |C+|TLROM           
+Kung Fu                               |Nintendo            |E |NROM            
+Kung Fu Heroes                        |Culture Brain       |C |CNROM           
+L'Empereur                            |Koei                |B |ETROM           
+Laser Invasion                        |Konami              |B-|ELROM           
+Last Action Hero                      |Imagesoft           |B+|TLROM           
+Last Ninja                            |Jaleco              |B-|TLROM           
+Last Starfighter                      |Mindscape           |C+|CNROM           
+Legacy/Wizard                         |Broderbund          |C |TFROM           
+Legend of Kage                        |Taito               |C-|CNROM           
+Legendary Wings                       |Capcom              |B |UNROM           
+Legends/Diamond                       |Bandai              |B+|TLROM           
+Lemmings                              |Sunsoft             |B-|SLROM           
+Lethal Weapon                         |Ocean               |B |SLROM           
+Life Force                            |Konami              |C |351258          
+Linus Spacehead                       |Camerica            |B |                
+Linus Spacehead (Aladdin Cart)        |Camerica            |A+|                
+Little League Baseball                |SNK                 |B-|SLROM           
+Little Mermaid                        |Capcom              |B |UNROM           
+Little Nemo                           |Capcom              |B+|TLROM           
+Little Ninja Brothers                 |Culture Brain       |C |TLROM           
+Little Sampson                        |Taito               |B |TLROM           
+Lode Runner                           |Broderbund          |B |NROM            
+Lone Ranger                           |Konami              |B-|TLROM           
+Loopz                                 |Mindscape           |B+|UNROM           
+Low G Man                             |Taxan               |C |TLROM           
+Lunar Pool                            |FCI                 |C+|NROM            
+M C Kids                              |Virgin              |C+|TSROM           
+M.U.L.E.                              |Mindscape           |C+|SNROM           
+Mach Rider                            |Nintendo            |C-|NROM            
+Mad Max                               |Mindscape           |C+|TLROM           
+Mafat Conspiracy                      |Vic Tokai           |C |TLROM           
+Magic Darts                           |Romstar             |B |SLRROM          
+Magic of Scheherazade                 |Culture Brain       |C-|SLROM           
+Magician                              |Taxan               |A-|TKROM           
+Magmax                                |FCI                 |C-|NROM            
+Major League Baseball                 |LJN                 |E |CNROM           
+Maniac Mansion                        |Jaleco              |C+|SNROM           
+Mappyland                             |Taxan               |C+|TFROM           
+Marble Madness                        |Milton Bradley      |C+|ANROM           
+Mario Brothers                        |Nintendo            |B-|NROM            
+Mario Is Missing                      |Mindscape           |B |TLROM           
+Mario Time Machine                    |Mindscape           |A-|TLROM           
+Marvel's X-Men                        |LJN                 |B-|UNROM           
+Master Chu & the Drunkard Hu          |Color Dreams        |B |                
+Maxi 15                               |American Video      |A |                
+Mechanized Attack                     |SNK                 |C |SCROM           
+Mega Man                              |Capcom              |B-|UNROM           
+Mega Man 2                            |Capcom              |C |SGROM           
+Mega Man 3                            |Capcom              |C |TLROM           
+Mega Man 4                            |Capcom              |B-|TGROM           
+Mega Man 5                            |Capcom              |B |TLROM           
+Mega Man 6                            |Nintendo            |B+|TGROM           
+Menace Beach                          |Color Dreams        |A-|                
+Mendel Palace                         |Hudson              |B |TLROM           
+Mermaids of Atlantis                  |American Video      |B+|                
+Metal Fighter                         |Color Dreams        |B |                
+Metal Gear                            |Ultra               |D |UNROM           
+Metal Mech                            |Jaleco              |B-|SLROM           
+Metal Storm                           |Irem                |B+|TLROM           
+Metroid                               |Nintendo            |D |SNROM           
+Michael Andretti World GP             |American Sammy      |B-|TLROM           
+Mickey Mousecapade                    |Capcom              |C+|CNROM           
+Mickey Numbers                        |Hi Tech             |B+|TLROM           
+Mickey's Safari in Letterland         |Hi Tech             |B+|55741           
+Micro Machines                        |Camerica            |A-|                
+Micro Machines (Aladdin Cart)         |Camerica            |A+|                
+Mig 29                                |Camerica            |C+|                
+Might & Magic                         |American Sammy      |B+|TKROM           
+Mighty Bombjack                       |Tecmo               |B |CNROM           
+Mighty Final Fight                    |Capcom              |B+|TLROM           
+Mike Tyson's Punch Out                |Nintendo            |D |PNROM           
+Millipede                             |HAL                 |B |NROM            
+Milon's Secret Castle                 |Hudson              |C |CNROM           
+Miracle Piano                         |Mindscape           |A-|SJROM           
+Mission Cobra                         |Bunch Games         |A-|                
+Mission Impossible                    |Ultra               |C+|352026          
+Monopoly                              |Parker Brothers     |B-|SLROM           
+Monster in my Pocket                  |Konami              |B+|TLROM           
+Monster Party                         |Bandai              |B |SLROM           
+Monster Truck Rally                   |INTV                |B |CNROM           
+Moon Ranger                           |Bunch Games         |C |                
+Motor City Patrol                     |Matchbox            |C+|SLROM           
+Ms. Pacman                            |Namco               |A |NROM            
+Ms. Pacman                            |Tengen              |B+|                
+Muppet Adventure                      |Hi Tech             |B |SGROM           
+Muscle                                |Bandai              |B-|NROM            
+Mutant Virus                          |American Software   |B+|SLROM           
+Mystery Quest                         |Taxan               |C+|CNROM           
+NARC                                  |Acclaim             |C |AMROM           
+NES Open Golf                         |Nintendo            |B-|SNROM           
+NFL Football                          |LJN                 |C |UNROM           
+Nigel Mansell                         |Gametek             |B+|SLROM           
+Nightmare on Elm Street               |LJN                 |B |AMROM           
+Nightshade                            |Ultra               |C+|TLROM           
+Ninja Crusaders                       |American Sammy      |B |TGROM           
+Ninja Gaiden                          |Tecmo               |E |SLROM           
+Ninja Gaiden 2                        |Tecmo               |C-|TLROM           
+Ninja Gaiden 3                        |Tecmo               |C+|TLROM           
+Ninja Kid                             |Bandai              |C+|CNROM           
+Nombunagas Ambition                   |Koei                |C |SOROM           
+Nombunagas Ambition 2                 |Koei                |B |ETROM           
+North & South                         |Kemco               |B+|TSROM           
+Operation Secret Storm                |Color Dreams        |A |                
+Operation Wolf                        |Taito               |D |SLROM           
+ORB-3D                                |Hi Tech             |C |SCROM           
+Othello                               |Acclaim             |C |NROM            
+Overlord                              |Virgin              |B-|SN1             
+P'radikus Conflict                    |Color Dreams        |B |                
+Pac Man                               |Namco               |A |56504           
+Pac Man (licensed)                    |Tengen              |B-|NROM            
+Pac Man (unlicensed)                  |Tengen              |B-|                
+Pac Mania                             |Tengen              |A |                
+Palamedes                             |Hot B               |B+|SEROM           
+Panic Resturant                       |Taito               |B |TLROM           
+Paperboy                              |Mindscape           |C |CNROM           
+Paperboy 2                            |Mindscape           |B |UOROM           
+Parodius (England)                    |Palcom              |  |                
+Pebble Beach Golf                     |Bandai              |C+|CNROM           
+Peek A Boo Poker                      |Panesian            |A+|                
+Perfect Fit                           |Gametek             |B |CNROM           
+Pesterminator                         |Color Dreams        |B |                
+Peter Pan & the Pirates               |T*HQ                |B-|SFROM           
+Phantom Fighter                       |FCI                 |C+|SGROM           
+Pictionary                            |LJN                 |B-|SLROM           
+Pinball                               |Nintendo            |D |NROM            
+Pinball Quest                         |Jaleco              |B |SLROM           
+Pinbot                                |Nintendo            |B |TQROM           
+Pipe Dream                            |Bullet Proof        |B |CNROM           
+Pirates                               |Ultra               |B |SKROM           
+Platoon                               |Sunsoft             |D |SLROM           
+Play Action Football                  |Nintendo            |D |TLSROM          
+Pool of Radiance                      |FCI                 |B+|TKROM           
+Popeye                                |Nintendo            |B-|NROM            
+POW                                   |SNK                 |C |SLROM           
+Power Blade                           |Taito               |B-|TLROM           
+Power Blade 2                         |Taito               |B-|TLROM           
+Power Punch 2                         |American Softworks  |B |TLROM           
+Predator                              |Activision          |C+|SLROM           
+Prince of Persia                      |Virgin              |C+|UNROM           
+Princess Tomato                       |Hudson              |B+|SGROM           
+Pro Am                                |Nintendo            |D |SEROM           
+Pro Am 2                              |Tradewest           |B |AOROM           
+Pro Sport Hockey                      |Jaleco              |B+|TLSROM          
+Pro Wrestling                         |Nintendo            |D |UNROM           
+Puggsly's Scavenger Hunt              |Ocean               |B |SLROM           
+Punch Out                             |Nintendo            |C+|PNROM           
+Punisher                              |LJN                 |B |TLROM           
+Puss N Boots                          |Electro Brain       |B-|UNROM           
+Puzzle                                |American Video      |A-|                
+Puzznic                               |Taito               |B+|CNROM           
+Pyramid                               |American Video      |A-|                
+Q*Bert                                |Ultra               |B-|CNROM           
+Qix                                   |Taito               |A |SNROM           
+Quantum Fighter                       |HAL                 |B |TLROM           
+Quarterback                           |Tradewest           |D |CNROM           
+Quattro Adventure                     |Camerica            |B+|                
+Quattro Adventure (Aladdin Cart)      |Camerica            |A+|                
+Quattro Arcade                        |Camerica            |A-|                
+Quattro Sports                        |Camerica            |B |                
+Quattro Sports (Aladdin Cart)         |Camerica            |A+|                
+Race America                          |Absolute            |B+|SLROM           
+Racket Attack                         |Jaleco              |C-|SLROM           
+Rad Gravity                           |Activision          |B-|SLROM           
+Rad Racer                             |Nintendo            |D |SGROM           
+Rad Racer 2                           |Square              |B-|TVROM           
+Rad Racket                            |American Video      |A-|                
+Raid 2020                             |Color Dreams        |B |                
+Raid on Bungling Bay                  |Broderbund          |B |NROM            
+Rainbow Island                        |Taito               |B+|UNROM           
+Rally Bike                            |Romstar             |B |UNROM           
+Rambo                                 |Acclaim             |E |UNROM           
+Rampage                               |Data East           |C+|TFROM           
+Rampart                               |Jaleco              |B |TLROM           
+RBI Baseball (licensed)               |Tengen              |C+|DEROM           
+RBI Baseball (unlicensed)             |Tengen              |C |                
+RBI Baseball 2                        |Tengen              |C+|                
+RBI Baseball 3                        |Tengen              |B-|                
+Remote Control                        |Hi Tech             |C |SLROM           
+Ren + Stimpy Buckaroos                |T*HQ                |B-|TLROM           
+Renegade                              |Taito               |C-|UNROM           
+Rescue                                |Kemco               |C-|SLROM           
+Rescue Rangers                        |Capcom              |C+|SLROM           
+Rescue Rangers 2                      |Capcom              |B+|SLROM           
+Ring King                             |Data East           |C |DEROM           
+River City Ransom                     |American Technos    |C |TLROM           
+Road Blasters                         |Mindscape           |C+|SLROM           
+Road Runner                           |Tengen              |B+|                
+Robin Hood                            |Virgin              |C+|SGROM           
+Robo Cop                              |Data East           |C |TL1ROM          
+Robo Cop 2                            |Data East           |B-|SLROM           
+Robo Cop 3                            |Ocean               |B |SLROM           
+Robo Demons                           |Color Dreams        |B |                
+Robo Warrior                          |Jaleco              |B |UNROM           
+Rock N Ball                           |NTVIC               |B |TFROM           
+Rocket Ranger                         |Kemco               |B-|SGROM           
+Rocketeer                             |Bandai              |B-|SGROM           
+Rockin Kats                           |Atlus               |B+|TLROM           
+Rocky & Bullwinkle                    |T*HQ                |B-|TLROM           
+Roger Clemens                         |LJN                 |C |53361           
+Roller Games                          |Ultra               |B-|TLROM           
+Rollerball                            |HAL                 |B |SFROM           
+Rollerblade Racer                     |Hi Tech             |B |53361           
+Rolling Thunder                       |Tengen              |C+|                
+Romance/3 Kingdoms                    |Koei                |C |SOROM           
+Romance/3 Kingdoms 2                  |Koei                |B |EWROM           
+Roundball                             |Mindscape           |B-|TSROM           
+Rush N Attack                         |Konami              |D |UNROM           
+Rygar                                 |Tecmo               |C |UNROM           
+SCAT                                  |Natsume             |B |SLROM           
+Secret Scout                          |Color Dreams        |A |                
+Section Z                             |Capcom              |C |UNROM           
+Seicross                              |FCI                 |D |NROM            
+Sesame Street 1-2-3                   |Hi Tech             |C+|SEROM (SCROROM) 
+Sesame Street 123/ABC                 |Hi Tech             |B |SLROM           
+Sesame Street A-B-C                   |Hi Tech             |C+|SEROM           
+Sesame Street Countdown               |Hi Tech             |A-|SLROM           
+Shadow of the Ninja                   |Natsume             |B |TLROM           
+Shadowgate                            |Kemco               |C+|TKROM           
+Shatterhand                           |Jaleco              |B-|TLROM           
+Shingen the Ruler                     |Hot B               |C |SNROM           
+Shinobi                               |Tengen              |C+|                
+Shockwave                             |American Game Carts Inc|B |                
+Shooting Range                        |Bandai              |B |CNROM           
+Short Order/Eggsplode                 |Nintendo            |B+|SBROM           
+Side Pocket                           |Data East           |B |UNROM           
+Silent Assault                        |Color Dreams        |B |                
+Silent Service                        |Ultra               |C-|351258          
+Silk Worm                             |American Sammy      |C |SLROM           
+Silver Surfer                         |Arcadia             |B |TSROM           
+Simpsons Bart Meets Radioactive Man   |Acclaim             |B+|55741           
+Simpsons Bart Vs Space Mutants        |Acclaim             |D |SLROM           
+Simpsons Bart Vs World                |Acclaim             |B-|53361           
+Skate or Die                          |Ultra               |D |351258          
+Skate or Die 2                        |Electronic Arts     |B |SLROM           
+Ski or Die                            |Ultra               |B-|351908          
+Skull & Crossbones                    |Tengen              |B |                
+Sky Kid                               |Sunsoft             |B-|SCEOROM         
+Sky Shark                             |Taito               |D |SL1ROM          
+Slalom                                |Nintendo            |B-|NROM            
+Smash TV                              |Acclaim             |C+|51555           
+Snake Rattle & Roll                   |Nintendo            |C+|SEROM           
+Snakes Revenge                        |Ultra               |C |SLROM           
+Snoopy Silly Sports                   |Kemco               |B+|SLROM           
+Snow Brothers                         |Capcom              |B |SLROM           
+Soccer                                |Nintendo            |D |NROM            
+Solar Jetman                          |Tradewest           |C |AOROM           
+Solitaire                             |American Video      |A-|                
+Soloman's Key                         |Tecmo               |C+|CNROM           
+Solstice                              |Imagesoft           |C |ANROM           
+Space Shuttle                         |Absolute            |B+|SGROM           
+Spelunker                             |Broderbund          |C+|NROM            
+Spiderman                             |LJN                 |B-|53361           
+Spiritual Warfare                     |Wisdom Tree         |B+|                
+Spot                                  |Arcadia             |B-|SNROM           
+Spy Hunter                            |Sunsoft             |C |CNROM           
+Spy vs. Spy                           |Kemco               |B-|NROM            
+Sqoon                                 |Irem                |B+|NROM            
+Stack Up                              |Nintendo            |A-|HVC             
+Stadium Events                        |Bandai              |B |                
+Stanley                               |Electro Brain       |B |TLROM           
+Star Force                            |Tecmo               |C+|CNROM           
+Star Soldier                          |Taxan               |C-|CNROM           
+Star Trek 25th Anniversary            |Ultra               |B |TLROM           
+Star Trek: The Next Generation        |Absolute            |A-|UNROM           
+Star Tropics                          |Nintendo            |C-|HKROM           
+Star Voyager                          |Acclaim             |C+|CNROM           
+Star Wars                             |JVC                 |B |TSROM           
+Starship Hector                       |Hudson              |B-|UNROM           
+Stealth ATF                           |Activision          |C+|SLROM           
+Stinger                               |Konami              |B-|UNROM           
+Street Cop                            |Bandai              |A-|SLROM           
+Street Fighter 2010                   |Capcom              |B |TLROM           
+Strider                               |Capcom              |C |SGROM           
+Stunt Kids                            |Camerica            |A-|                
+Sunday Funday                         |Wisdom Tree         |B+|                
+Super C                               |Konami              |C |352026          
+Super Cars                            |Electro Brain       |B+|UNROM           
+Super Dodge Ball                      |Imagesoft           |C |SLROM           
+Super Glove Ball                      |Mattel              |C |UNROM           
+Super Mario Brothers                  |Nintendo            |F |NROM            
+Super Mario Brothers 2                |Nintendo            |D |TSROM           
+Super Mario Brothers 3                |Nintendo            |D |TSROM           
+Super Mario/Duck Hunt                 |Nintendo            |F |MH              
+Super Mario/Duck Hunt/Track Meet      |Nintendo            |C |COB             
+Super Off Road                        |Tradewest           |C-|AMROM           
+Super Pitfall                         |Activision          |C+|UNROM           
+Super Spike V'Ball                    |Nintendo            |C-|TLROM           
+Super Spike/World Cup                 |Nintendo            |B-|COB             
+Super Sprint                          |Tengen              |C |COB             
+Super Spy Hunter                      |Sunsoft             |B+|TLROM           
+Super Team Games                      |Nintendo            |C-|CNROM           
+Superman                              |Kemco               |B+|SLROM           
+Swamp Thing                           |T*HQ                |C+|SLROM           
+Sword Master                          |Activision          |A-|TLROM           
+Swords & Serpents                     |Acclaim             |B+|UNROM           
+T&C Surf Design                       |LJN                 |E |CNROM           
+Taboo                                 |Tradewest           |C+|SEROM           
+Tag Team Wrestling                    |Data East           |C |NROM            
+Taggin Dragon                         |Bunch Games         |B |                
+Talespin                              |Capcom              |B |SLROM           
+Target Renegade                       |Taito               |C+|SLROM           
+Tecmo Baseball                        |Tecmo               |C |SGROM           
+Tecmo Basketball                      |Tecmo               |C+|TKROM           
+Tecmo Bowl                            |Tecmo               |E |SLROM           
+Tecmo Cup Soccer                      |Tecmo               |B+|SLROM           
+Tecmo Super Bowl                      |Tecmo               |C-|TKROM           
+Tecmo Wrestling                       |Tecmo               |C |SLROM           
+Teenage Turtles                       |Ultra               |E |351908          
+Teenage Turtles 2                     |Ultra               |D |TLROM           
+Teenage Turtles 3                     |Konami              |B-|TLROM           
+Teenage Turtles Tournament Fighters   |Konami              |B+|TLROM           
+Tennis                                |Nintendo            |D |NROM            
+Terminator                            |Mindscape           |B+|TLROM           
+Terminator 2 Judgement Day            |LJN                 |B-|53361           
+Terra Cresta                          |Vic Tokai           |B+|UNROM           
+Tetris                                |Tengen              |A-|                
+Tetris                                |Nintendo            |D |SEROM           
+Tetris 2                              |Nintendo            |B-|TSROM           
+Three Stooges                         |Activision          |C+|SLROM           
+Thrilla Safari                        |LJN                 |B |53361           
+Thunder & Lightning                   |Romstar             |B+|GNROM           
+Thunderbirds                          |Activision          |C+|SLROM           
+Thundercade                           |American Sammy      |C+|UNROM           
+Tiger Heli                            |Acclaim             |C+|CNROM           
+Tiles of Fate                         |American Video      |B+|                
+Time Lord                             |Milton Bradley      |C |AMROM           
+Times of Lore                         |Toho                |B-|UNROM           
+Tiny Toon                             |Konami              |C+|TLROM           
+Tiny Toon Cartoon Workshop            |Konami              |B+|TSROM           
+Tiny Toons 2                          |Konami              |B+|TLROM           
+To The Earth                          |Nintendo            |C+|TEROM           
+Toki                                  |Taito               |B+|TLROM           
+Tom & Jerry                           |Hi Tech             |B+|TLROM           
+Tombs & Treasures                     |Infocom             |A-|SGROM           
+Toobin                                |Tengen              |B |                
+Top Gun                               |Konami              |D |351298          
+Top Gun 2                             |Konami              |D |352026          
+Top Players Tennis                    |Asmik               |C+|SLROM           
+Total Recall                          |Acclaim             |D |UNROM           
+Totally Rad                           |Jaleco              |B-|TLROM           
+Touchdown Fever                       |SNK                 |B+|SFROM           
+Toxic Crusader                        |Bandai              |B |TLROM           
+Track & Field                         |Konami              |D |CNROM           
+Track & Field 2                       |Konami              |D |SLROM           
+Treasure Master                       |American Softworks  |A-|SLROM           
+Trick Shooting                        |Nintendo            |C+|SCROM           
+Trog                                  |Acclaim             |B+|UNROM           
+Trojan                                |Capcom              |C |UNROM           
+Trolls on Treasure Island             |American Video      |A-|                
+Twin Cobra                            |American Sammy      |C+|TLROM           
+Twin Eagle                            |Romstar             |C+|UNROM           
+Ultima/Exodus                         |FCI                 |C-|SNROM           
+Ultima/Quest Avatar                   |FCI                 |B |SNROM           
+Ultima/War Destiny                    |FCI                 |B+|SNROM           
+Ultimate Air Combat                   |Activision          |B+|TLROM           
+Ultimate Basketball                   |American Sammy      |C |TLROM           
+Ultimate League Soccer                |American Video      |A-|                
+Ultimate Stuntman                     |Camerica            |C+|                
+Uncharted Waters                      |Koei                |B-|ETROM           
+Uninvited                             |Kemco               |B+|TKROM           
+Untouchables                          |Ocean               |B-|SLROM           
+Urban Champion                        |Nintendo            |D |NROM            
+Vegas Dream                           |HAL                 |B |SKROM           
+Venice Beach Volleyball               |American Video      |A-|                
+Vice Project Doom                     |American Sammy      |C+|TLROM           
+Videomation                           |T*HQ                |B+|CPROM           
+Vindicators                           |Tengen              |C |                
+Volleyball                            |Nintendo            |C |NROM            
+Wacky Races                           |Atlus               |B+|TLROM           
+Wall Street Kid                       |Sofel               |B |UNROM           
+Wally Bear and the No Gang            |American Video      |A-|                
+Wario Woods                           |Nintendo            |B+|TKROM           
+Wayne Gretzky                         |T*HQ                |C+|UNROM           
+Wayne's World                         |T*HQ                |B |TLROM           
+Werewolf                              |Data East           |C |TLROM           
+Wheel/Fortune                         |Gametek             |C |AOROM           
+Wheel/Fortune/Family                  |Gametek             |C |ANROM           
+Wheel/Fortune/Junior                  |Gametek             |C |ANROM           
+Wheel/Fortune/Vanna                   |Gametek             |B |AOROM           
+Where in Time is Carmen               |Konami              |B |TSROM           
+Where's Waldo                         |T*HQ                |C+|TSROM           
+Who Framed Roger Rabbit               |LJN                 |B-|ANROM           
+Whomp Em                              |Jaleco              |B |TLROM           
+Widget                                |Atlus               |B |TLROM           
+Wild Gunman                           |Nintendo            |C |NROM            
+Willow                                |Capcom              |B-|SLROM           
+Win Lose or Draw                      |Hi Tech             |C |SGROM           
+Winter Games                          |Acclaim             |B-|UNROM           
+Wizardry                              |Nexoft              |C+|SKROM           
+Wizardry 2 Knight of Diamonds         |Ascii               |B+|TKROM           
+Wizards & Warriors                    |Acclaim             |D |ANROM           
+Wizards & Warriors 2 - Ironsword      |Acclaim             |D |AOROM           
+Wizards & Warriors 3                  |Acclaim             |C-|54425           
+Wolverine                             |LJN                 |B-|TLROM           
+World Champ                           |Romstar             |B+|TLROM           
+World Championship Wrestling          |FCI                 |D |TLROM           
+World Class Track Meet                |Nintendo            |D |CNROM           
+World Cup Soccer                      |Nintendo            |C-|TLROM           
+World Games                           |Milton Bradley      |B-|ANROM           
+World Runner 3D                       |Acclaim             |C-|UNROM           
+Wrath of the Black Manta              |Taito               |C+|SLROM           
+Wrecking Crew                         |Nintendo            |C+|NROM            
+Wrestlemania                          |Acclaim             |D |ANROM           
+Wrestlemania Challenge                |LJN                 |C-|UNROM           
+Wrestlemania Steel Cage               |LJN                 |C+|53361           
+Wurm                                  |Asmik               |B-|TLROM           
+Xenophobe                             |Sunsoft             |B-|SFROM           
+Xevious                               |Bandai              |C+|NROM            
+Xexyz                                 |Hudson              |C+|SLROM           
+Yo Noid                               |Capcom              |C+|SLROM           
+Yoshi                                 |Nintendo            |C-|SFROM           
+Yoshi's Cookie                        |Nintendo            |B-|TFROM           
+Young Indy                            |Jaleco              |B+|TLROM           
+Zanac                                 |FCI                 |C+|UNROM           
+Zelda                                 |Nintendo            |E |SNROM           
+Zelda 2                               |Nintendo            |D |SKROM           
+Zen                                   |Konami              |B |TLROM           
+Zoda's Revenge, Startropics 2         |Nintendo            |B-|HKROM           
+Zombie Nation                         |Meldac              |B |TLROM           

+ 38 - 0
ucon64/2.0/src/console/console.h

@@ -0,0 +1,38 @@
+/*
+console.h - single header for all console functions
+
+Copyright (c) 2003 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef CONSOLE_H
+#define CONSOLE_H
+#include "dc.h"
+#include "gb.h"
+#include "gba.h"
+#include "genesis.h"
+#include "jaguar.h"
+#include "lynx.h"
+#include "n64.h"
+#include "neogeo.h"
+#include "nes.h"
+#include "ngp.h"
+#include "pce.h"
+#include "psx.h"
+#include "sms.h"
+#include "snes.h"
+#include "swan.h"
+#endif // CONSOLE_H

+ 646 - 0
ucon64/2.0/src/console/dc.c

@@ -0,0 +1,646 @@
+/*
+dc.c - Dreamcast support for uCON64
+
+Copyright (c) 2004 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef  HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "misc/file.h"
+#include "misc/misc.h"
+#include "misc/property.h"
+#include "misc/string.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "dc.h"
+
+
+const st_getopt2_t dc_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Dreamcast" /* "1998 SEGA http://www.sega.com" */,
+      NULL
+    },
+    {
+      "dc", 0, 0, UCON64_DC,
+      NULL, "force recognition",
+      &ucon64_wf[WF_OBJ_DC_SWITCH]
+    },
+#if 0
+    {
+      "vms", 1, 0, UCON64_VMS,
+      "SAV", "convert NES SAV file to a VMS file for use with NesterDC",
+      NULL
+    },
+#endif
+    {
+      "scr", 0, 0, UCON64_SCR,
+      NULL, "scramble 1ST_READ.BIN for selfboot CDs",
+      &ucon64_wf[WF_OBJ_DC_DEFAULT]
+    },
+    {
+      "unscr", 0, 0, UCON64_UNSCR,
+      NULL, "unscramble 1ST_READ.BIN for non-selfboot CDs",
+      &ucon64_wf[WF_OBJ_DC_DEFAULT]
+    },
+#if 0
+    {
+      "ip", 1, 0, UCON64_IP,
+      "FILE", "extract ip.bin FILE from IMAGE; " OPTION_LONG_S "rom=IMAGE",
+      NULL
+    },
+#endif
+    {
+      "mkip", 0, 0, UCON64_MKIP,
+      NULL, "generate IP.BIN file with default values",
+      &ucon64_wf[WF_OBJ_DC_NO_ROM]
+    },
+    {
+      "parse", 1, 0, UCON64_PARSE,
+      "TEMPLATE", "parse TEMPLATE file into a IP.BIN;\n"
+      "creates an empty template when TEMPLATE does not exist",
+      &ucon64_wf[WF_OBJ_DC_NO_ROM]
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+static int
+calc_crc (const unsigned char *buf, int size)
+{
+  int i, c, n = 0xffff;
+
+  for (i = 0; i < size; i++)
+    {
+      n ^= (buf[i] << 8);
+      for (c = 0; c < 8; c++)
+        if (n & 0x8000)
+          n = (n << 1) ^ 4129;
+        else
+          n = (n << 1);
+    }
+  return n & 0xffff;
+}
+
+
+static void
+update_crc (char *ip)
+{
+  int n = calc_crc ((unsigned char *) (ip + 0x40), 16);
+  char buf[5];
+
+  sprintf (buf, "%04X", n);
+  if (memcmp (buf, ip + 0x20, 4))
+    {
+      printf ("Setting CRC to %s (was %.4s)\n", buf, ip + 0x20);
+      memcpy (ip + 0x20, buf, 4);
+    }
+}
+
+
+static unsigned int seed;
+
+static void
+dc_srand (unsigned int n)
+{
+  seed = n & 0xffff;
+}
+
+
+static unsigned int
+dc_rand ()
+{
+  seed = (seed * 2109 + 9273) & 0x7fff;
+  return (seed + 0xc000) & 0xffff;
+}
+
+
+#if 0
+// header for SAV -> VMS conversion
+static const uint8_t nstrsave_bin[1024] = {
+  0x4e, 0x45, 0x53, 0x52, 0x4f, 0x4d, 0x2e, 0x4e, // 0x8 (8)
+  0x45, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10 (16)
+  0x4e, 0x65, 0x73, 0x74, 0x65, 0x72, 0x44, 0x43, // 0x18 (24)
+  0x20, 0x53, 0x61, 0x76, 0x65, 0x52, 0x61, 0x6d, // 0x20 (32)
+  0x20, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, // 0x28 (40)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x30 (48)
+  0x2a, 0x2a, 0x2a, 0x4e, 0x65, 0x73, 0x74, 0x65, // 0x38 (56)
+  0x72, 0x44, 0x43, 0x2a, 0x2a, 0x2a, 0x00, 0x00, // 0x40 (64)
+  0x01, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x00, // 0x48 (72)
+  0x80, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 (80)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x58 (88)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x60 (96)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x68 (104)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x70 (112)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x78 (120)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80 (128)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88 (136)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x90 (144)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x98 (152)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xa0 (160)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xa8 (168)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xb0 (176)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xb8 (184)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xc0 (192)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xc8 (200)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xd0 (208)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xd8 (216)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xe0 (224)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xe8 (232)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xf0 (240)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xf8 (248)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x100 (256)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x108 (264)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x110 (272)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x118 (280)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x120 (288)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x128 (296)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x130 (304)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x138 (312)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x140 (320)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x148 (328)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x150 (336)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x158 (344)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x160 (352)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x168 (360)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x170 (368)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x178 (376)
+  0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x180 (384)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x188 (392)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x190 (400)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x198 (408)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1a0 (416)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1a8 (424)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1b0 (432)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1b8 (440)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1c0 (448)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1c8 (456)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1d0 (464)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1d8 (472)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1e0 (480)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1e8 (488)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1f0 (496)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1f8 (504)
+  0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x200 (512)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x208 (520)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x210 (528)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x218 (536)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x220 (544)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x228 (552)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x230 (560)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x238 (568)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x240 (576)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x248 (584)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x250 (592)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x258 (600)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x260 (608)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x268 (616)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x270 (624)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x278 (632)
+  0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x280 (640)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x288 (648)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x290 (656)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x298 (664)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2a0 (672)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2a8 (680)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2b0 (688)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2b8 (696)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2c0 (704)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2c8 (712)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2d0 (720)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2d8 (728)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2e0 (736)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2e8 (744)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2f0 (752)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2f8 (760)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x300 (768)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x308 (776)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x310 (784)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x318 (792)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x320 (800)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x328 (808)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x330 (816)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x338 (824)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x340 (832)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x348 (840)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x350 (848)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x358 (856)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x360 (864)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x368 (872)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x370 (880)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x378 (888)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x380 (896)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x388 (904)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x390 (912)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x398 (920)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3a0 (928)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3a8 (936)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3b0 (944)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3b8 (952)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3c0 (960)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3c8 (968)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3d0 (976)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3d8 (984)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3e0 (992)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3e8 (1000)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3f0 (1008)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3f8 (1016)
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 0x400 (1024)
+};
+#endif
+
+
+int
+dc_init (st_rominfo_t *rominfo)
+{
+  int result = -1;
+
+  rominfo->console_usage = dc_usage[0].help;
+
+  return result;
+}
+
+
+static int
+check_areasym (char *ptr, int len)
+{
+  int i, a = 0;
+
+  for (i = 0; i < len; i++)
+    switch (ptr[i])
+      {
+      case 'J':
+        a |= (1<<0);
+        break;
+      case 'U':
+        a |= (1<<1);
+        break;
+      case 'E':
+        a |= (1<<2);
+        break;
+      case ' ':
+        break;
+      default:
+        fprintf (stderr, "ERROR: Unknown area symbol '%c'\n", ptr[i]);
+        return -1;
+      }
+  for (i = 0; i < len; i++)
+    if ((a & (1<<i)) == 0)
+      ptr[i] = ' ';
+    else
+      ptr[i] = "JUE"[i];
+
+  return 0;
+}
+
+
+typedef struct
+{
+  char *name;
+  int pos;
+  int len;
+  int (*extra_check) (char *, int);
+  char *def;
+  char *comment;
+} st_templ_t;
+
+
+st_templ_t templ[] =
+  {
+    {"hardware_id",   0x0, 0x10,  NULL,          "SEGA SEGAKATANA",        "Hardware ID (always \"SEGA SEGAKATANA\")"},
+    {"maker_id",      0x10, 0x10, NULL,          "SEGA ENTERPRISES",       "Maker ID (always \"SEGA ENTERPRISES\")"},
+    {"device_info",   0x20, 0x10, NULL,          "0000 CD-ROM1/1",         "Device Information"},
+    {"area_symbols",  0x30, 0x8,  check_areasym, "JUE",                    "Area Symbols"},
+    {"peripherals",   0x38, 0x8,  NULL,          "E000F10",                "Peripherals"},
+    {"product_no",    0x40, 0xa,  NULL,          "T0000",                  "Product number (\"HDR-nnnn\" etc.)"},
+    {"version",       0x4a, 0x6,  NULL,          "V1.000",                 "Product version"},
+    {"release_date",  0x50, 0x10, NULL,          "20000627",               "Release date (YYYYMMDD)"},
+    {"boot_filename", 0x60, 0x10, NULL,          "1ST_READ.BIN",           "Boot filename (usually \"1ST_READ.BIN\")"},
+    {"sw_maker_name", 0x70, 0x10, NULL,          "YOUR NAME HERE",         "Name of the company that produced the disc"},
+    {"game_title",    0x80, 0x80, NULL,          "TITLE OF THE SOFTWARE",  "Name of the software"},
+    {NULL,            0,    0,    NULL,          NULL,                     NULL}
+  };
+
+
+static int
+parse_templ (const char *templ_file, char *ip)
+{
+  int filled_in[MAXBUFSIZE];
+  static char buf[MAXBUFSIZE];
+  int i;
+
+  memset (filled_in, 0, sizeof (filled_in));
+  for (i = 0; templ[i].name; i++)
+    {
+      char *p = buf;
+      get_property (templ_file, templ[i].name, p, templ[i].def);
+
+      strtriml (strtrimr (p));
+
+      if (!(*p))
+        continue;
+
+      memset (ip + templ[i].pos, ' ', templ[i].len);
+
+      if ((int) strlen (p) > templ[i].len)
+        {
+          fprintf (stderr, "ERROR: Data for field \"%s\" is too long... stripping to %d chars\n",
+                   templ[i].name, templ[i].len);
+          p[templ[i].len] = 0;
+        }
+
+      memcpy (ip + templ[i].pos, p, strlen (p));
+
+      if (templ[i].extra_check)
+        if (templ[i].extra_check (ip + templ[i].pos, templ[i].len) == -1)
+          return -1;
+
+      filled_in[i] = 1;
+    }
+
+  for (i = 0; templ[i].name; i++)
+    if (!filled_in[i])
+      {
+        fprintf (stderr, "ERROR: Missing value for \"%s\"\n", templ[i].name);
+        return -1;
+      }
+
+  return 0;
+}
+
+
+int
+dc_parse (const char *templ_file)
+{
+  char ip[0x8000], dest_name[FILENAME_MAX];
+
+  if (access (templ_file, F_OK) == -1)
+    {
+      int i = 0;
+
+      printf ("Creating empty template file: \"%s\"\n", templ_file);
+
+      for (i = 0; templ[i].name; i++)
+        set_property (templ_file, templ[i].name, templ[i].def, templ[i].comment);
+
+      printf (ucon64_msg[WROTE], templ_file);
+    }
+
+  if (parse_templ (templ_file, ip) == -1)
+    return -1;
+
+  update_crc (ip);
+
+  strcpy (dest_name, "ip.bin");
+  ucon64_file_handler (dest_name, NULL, 0);
+
+  ucon64_fwrite (ip, 0, 0x8000, dest_name, "wb");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+dc_mkip (void)
+{
+  dc_parse ("default");
+
+  return 0;
+}
+
+
+static int
+load_chunk (FILE * fh, unsigned char *ptr, int32_t sz)
+{
+#define MAXCHUNK (2048*1024)
+  static int idx[MAXCHUNK / 32];
+  int32_t i;
+
+  /* Convert chunk size to number of slices */
+  sz /= 32;
+
+  /* Initialize index table with unity,
+     so that each slice gets loaded exactly once */
+  for (i = 0; i < sz; i++)
+    idx[i] = i;
+
+  for (i = sz - 1; i >= 0; --i)
+    {
+      /* Select a replacement index */
+      int x = (dc_rand () * i) >> 16;
+
+      /* Swap */
+      int tmp = idx[i];
+      idx[i] = idx[x];
+      idx[x] = tmp;
+
+      /* Load resulting slice */
+      if (fread (ptr + 32 * idx[i], 1, 32, fh) != 32)
+        return -1;
+    }
+  return 0;
+}
+
+
+static int
+load_file (FILE * fh, unsigned char *ptr, uint32_t filesz)
+{
+  uint32_t chunksz;
+
+  dc_srand (filesz);
+
+  /* Descramble 2 meg blocks for as long as possible, then
+     gradually reduce the window down to 32 bytes (1 slice) */
+  for (chunksz = MAXCHUNK; chunksz >= 32; chunksz >>= 1)
+    while (filesz >= chunksz)
+      {
+        load_chunk (fh, ptr, chunksz);
+        filesz -= chunksz;
+        ptr += chunksz;
+      }
+
+  /* Load final incomplete slice */
+  if (filesz)
+    if (fread (ptr, 1, filesz, fh) != filesz)
+      return -1;
+
+  return 0;
+}
+
+
+static int
+save_chunk (FILE * fh, unsigned char *ptr, int32_t sz)
+{
+  static int idx[MAXCHUNK / 32];
+  int32_t i;
+
+  /* Convert chunk size to number of slices */
+  sz /= 32;
+
+  /* Initialize index table with unity,
+     so that each slice gets saved exactly once */
+  for (i = 0; i < sz; i++)
+    idx[i] = i;
+
+  for (i = sz - 1; i >= 0; --i)
+    {
+      /* Select a replacement index */
+      int x = (dc_rand () * i) >> 16;
+
+      /* Swap */
+      int tmp = idx[i];
+      idx[i] = idx[x];
+      idx[x] = tmp;
+
+      /* Save resulting slice */
+      if (fwrite (ptr + 32 * idx[i], 1, 32, fh) != 32)
+        return -1;
+    }
+
+  return 0;
+}
+
+
+static int
+save_file (FILE * fh, unsigned char *ptr, uint32_t filesz)
+{
+  uint32_t chunksz;
+
+  dc_srand (filesz);
+
+  /* Descramble 2 meg blocks for as long as possible, then
+     gradually reduce the window down to 32 bytes (1 slice) */
+  for (chunksz = MAXCHUNK; chunksz >= 32; chunksz >>= 1)
+    while (filesz >= chunksz)
+      {
+        save_chunk (fh, ptr, chunksz);
+        filesz -= chunksz;
+        ptr += chunksz;
+      }
+
+  /* Save final incomplete slice */
+  if (filesz)
+    if (fwrite (ptr, 1, filesz, fh) != filesz)
+      return -1;
+
+  return 0;
+}
+
+
+static int
+descramble (const char *src, char *dst)
+{
+  unsigned char *ptr = NULL;
+  uint32_t sz = 0;
+  FILE *fh;
+
+  if (!(fh = fopen (src, "rb")))
+    return -1;
+
+  sz = fsizeof (src);
+  if (!(ptr = (unsigned char *) malloc (sz)))
+    return -1;
+
+  load_file (fh, ptr, sz);
+  fclose (fh);
+
+  if (!(fh = fopen (dst, "wb")))
+    return -1;
+
+  if (fwrite (ptr, 1, sz, fh) != sz)
+    return -1;
+
+  fclose (fh);
+  free (ptr);
+  return 0;
+}
+
+
+static int
+scramble (const char *src, char *dst)
+{
+  unsigned char *ptr = NULL;
+  uint32_t sz = 0;
+  FILE *fh;
+
+  if (!(fh = fopen (src, "rb")))
+    return -1;
+
+  sz = fsizeof (src);
+
+  if (!(ptr = (unsigned char *) malloc (sz)))
+    return -1;
+
+  if (fread (ptr, 1, sz, fh) != sz)
+    return -1;
+
+  fclose (fh);
+
+  if (!(fh == fopen (dst, "wb")))
+    return -1;
+  save_file (fh, ptr, sz);
+
+  fclose (fh);
+
+  free (ptr);
+  return 0;
+}
+
+
+int
+dc_scramble (void)
+{
+  char dest_name[FILENAME_MAX];
+
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+
+  if (!scramble (ucon64.rom, dest_name))
+    printf (ucon64_msg[WROTE], dest_name);
+  else
+    fprintf (stderr, ucon64_msg[WRITE_ERROR], dest_name);
+  return 0;
+}
+
+
+int
+dc_unscramble (void)
+{
+  char dest_name[FILENAME_MAX];
+
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+
+  if (!descramble (ucon64.rom, dest_name))
+    printf (ucon64_msg[WROTE], dest_name);
+  else
+    fprintf (stderr, ucon64_msg[WRITE_ERROR], dest_name);
+  return 0;
+}

+ 29 - 0
ucon64/2.0/src/console/dc.h

@@ -0,0 +1,29 @@
+/*
+dc.h - Dreamcast support for uCON64
+
+Copyright (c) 2004 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef DC_H
+#define DC_H
+extern int dc_init (st_rominfo_t *rominfo);
+extern const st_getopt2_t dc_usage[];
+extern int dc_parse (const char *template_file);
+extern int dc_mkip (void);
+extern int dc_scramble (void);
+extern int dc_unscramble (void);
+#endif

+ 703 - 0
ucon64/2.0/src/console/gb.c

@@ -0,0 +1,703 @@
+/*
+gb.c - Game Boy support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2001 - 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef  HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include "misc/file.h"
+#include "misc/misc.h"
+#include "misc/string.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "console/nes.h"
+#include "backup/gbx.h"
+#include "backup/mgd.h"
+#include "backup/ssc.h"
+#include "patch/ips.h"
+#include "patch/bsl.h"
+#include "gb.h"
+
+
+#define GAMEBOY_HEADER_START 0x100
+#define GAMEBOY_HEADER_LEN (sizeof (st_gameboy_header_t))
+#define GB_NAME_LEN 15
+
+const st_getopt2_t gameboy_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Game Boy/(Super GB)/GB Pocket/Color GB/(GB Advance)"
+      /*"1989/1994/1996/1998/2001 Nintendo http://www.nintendo.com"*/,
+      NULL
+    },
+    {
+      "gb", 0, 0, UCON64_GB,
+      NULL, "force recognition",
+      &ucon64_wf[WF_OBJ_GB_SWITCH]
+    },
+    {
+      "n", 1, 0, UCON64_N,
+      "NEW_NAME", "change internal ROM name to NEW_NAME",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT]
+    },
+    {
+      "logo", 0, 0, UCON64_LOGO,
+      NULL, "restore ROM logo character data (offset: 0x104-0x134)",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT]
+    },
+    {
+      "mgd", 0, 0, UCON64_MGD,
+      NULL, "convert to Multi Game*/MGD2/RAW",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT]
+    },
+    {
+      "ssc", 0, 0, UCON64_SSC,
+      NULL, "convert to Super Smart Card/SSC",
+      &ucon64_wf[WF_OBJ_GB_DEFAULT]
+    },
+    {
+      "sgb", 0, 0, UCON64_SGB,
+      NULL, "convert from GB Xchanger/GB/GBC to Super Backup Card/GX/GBX",
+      &ucon64_wf[WF_OBJ_GB_DEFAULT]
+    },
+    {
+      "gbx", 0, 0, UCON64_GBX,
+      NULL, "convert from Super Backup Card/GX/GBX to GB Xchanger/GB/GBC",
+      &ucon64_wf[WF_OBJ_GB_DEFAULT]
+    },
+    {
+      "n2gb", 1, 0, UCON64_N2GB,
+      "NESROM", "KAMI's FC EMUlator (NES emulator);\n"
+      "ROM should be KAMI's FC Emulator ROM image\n"
+      "NESROM should contain 16 kB of PRG data and 8 kB of CHR data",
+      &ucon64_wf[WF_OBJ_GB_DEFAULT]
+    },
+    {
+      "chk", 0, 0, UCON64_CHK,
+      NULL, "fix ROM checksum",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT]
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+/*
+0148       ROM size:
+           0 - 256kBit =  32kB =  2 banks
+           1 - 512kBit =  64kB =  4 banks
+           2 -   1Mb = 128kB =  8 banks
+           3 -   2Mb = 256kB = 16 banks
+           4 -   4Mb = 512kB = 32 banks
+0149       RAM size:
+           0 - None
+           1 -  16kBit =  2kB = 1 bank
+           2 -  64kBit =  8kB = 1 bank
+           3 - 256kBit = 32kB = 4 banks
+*/
+typedef struct st_gameboy_header
+{
+  unsigned char opcode1;                        // 0x00 usually 0x00, NOP
+  unsigned char opcode2;                        // 0x01 usually 0xc3, JP
+  unsigned char start_low;                      // 0x02 first byte of parameter of JP
+  unsigned char start_high;                     // 0x03 second byte
+  unsigned char logo[GB_LOGODATA_LEN];          // 0x04
+  unsigned char name[GB_NAME_LEN];              // 0x34
+  unsigned char gb_type;                        // 0x43
+  unsigned char maker_high;                     // 0x44
+  unsigned char maker_low;                      // 0x45
+  unsigned char pad;
+  unsigned char rom_type;                       // 0x47
+  unsigned char rom_size;                       // 0x48
+  unsigned char sram_size;                      // 0x49
+  unsigned char country;                        // 0x4a
+  unsigned char maker;                          // 0x4b
+  unsigned char version;                        // 0x4c
+  unsigned char header_checksum;                // 0x4d
+  unsigned char checksum_high;                  // 0x4e
+  unsigned char checksum_low;                   // 0x4f
+} st_gameboy_header_t;
+
+static st_gameboy_header_t gameboy_header;
+const unsigned char gb_logodata[] =             // Note: not a static variable
+  {
+    0xce, 0xed, 0x66, 0x66, 0xcc, 0x0d, 0x00, 0x0b,
+    0x03, 0x73, 0x00, 0x83, 0x00, 0x0c, 0x00, 0x0d,
+    0x00, 0x08, 0x11, 0x1f, 0x88, 0x89, 0x00, 0x0e,
+    0xdc, 0xcc, 0x6e, 0xe6, 0xdd, 0xdd, 0xd9, 0x99,
+    0xbb, 0xbb, 0x67, 0x63, 0x6e, 0x0e, 0xec, 0xcc,
+    0xdd, 0xdc, 0x99, 0x9f, 0xbb, 0xb9, 0x33, 0x3e
+  },
+  rocket_logodata[] =                           // idem
+  {
+    0x11, 0x23, 0xf1, 0x1e, 0x01, 0x22, 0xf0, 0x00,
+    0x08, 0x99, 0x78, 0x00, 0x08, 0x11, 0x9a, 0x48,
+    0x11, 0x23, 0xf0, 0x0e, 0x70, 0x01, 0xf8, 0x80,
+    0x22, 0x44, 0x44, 0x22, 0x22, 0x21, 0x00, 0x1e,
+    0x99, 0x10, 0x00, 0x1e, 0x19, 0x22, 0x44, 0x22,
+    0x22, 0x47, 0x00, 0x0e, 0x11, 0x22, 0x00, 0x00
+  };
+
+typedef struct st_gameboy_chksum
+{
+  unsigned short value;
+  unsigned char header;
+} st_gameboy_chksum_t;
+
+static st_gameboy_chksum_t checksum;
+static st_gameboy_chksum_t gameboy_chksum (st_rominfo_t *rominfo);
+
+
+int
+gameboy_logo (st_rominfo_t *rominfo)
+{
+  char dest_name[FILENAME_MAX];
+
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
+  ucon64_fwrite ((unsigned char *)
+    ((gameboy_header.rom_type >= 0x97 && gameboy_header.rom_type <= 0x99) ?
+      rocket_logodata : gb_logodata),
+    rominfo->buheader_len + GAMEBOY_HEADER_START + 4, GB_LOGODATA_LEN,
+    dest_name, "r+b");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+gameboy_n2gb (st_rominfo_t *rominfo, const char *nesrom)
+{
+#define EMULATOR_LEN 0x10000
+  st_ines_header_t ines_header;
+  int n = 0, crc = 0;
+  unsigned char *buf;
+  char dest_name[FILENAME_MAX];
+
+  if (ucon64.file_size - rominfo->buheader_len != EMULATOR_LEN)
+    {
+      fprintf (stderr, "ERROR: %s does not appear to be KAMI's FC emulator\n", ucon64.rom);
+      return -1;
+    }
+
+  ucon64_fread (&ines_header, 0, INES_HEADER_LEN, nesrom);
+  if (memcmp (ines_header.signature, INES_SIG_S, 4))
+    {
+      fprintf (stderr, "ERROR: Only NES ROMs with iNES header are supported\n");
+      return -1;
+    }
+  if (ines_header.prg_size != 1 || ines_header.chr_size != 1)
+    {
+      fprintf (stderr,
+               "ERROR: Only NES ROMs with 0.1250 Mb of ROM (PRG) and 0.0625 Mb of VROM (CHR)\n"
+               "       are supported by KAMI's FC emulator\n");
+      return -1;
+    }
+
+  if (!(buf = (unsigned char *) malloc (ucon64.file_size)))
+    {
+      fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], ucon64.file_size);
+      return -1;
+    }
+  ucon64_fread (buf, 0, ucon64.file_size, ucon64.rom);
+  ucon64_fread (rominfo->buheader_len + buf + 0x4000, INES_HEADER_LEN,
+                0x4000 + 0x2000, nesrom);       // read PRG & CHR data
+
+  for (n = 0; n < ucon64.file_size - rominfo->buheader_len; n++)
+    {
+      if ((n == GAMEBOY_HEADER_START + 0x4e) || (n == GAMEBOY_HEADER_START + 0x4f))
+        continue;
+      else
+        crc += buf[rominfo->buheader_len + n];
+    }
+
+  buf[rominfo->buheader_len + GAMEBOY_HEADER_START + 0x4e] = crc >> 8;
+  buf[rominfo->buheader_len + GAMEBOY_HEADER_START + 0x4f] = crc;
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+  ucon64_fwrite (buf, 0, ucon64.file_size, dest_name, "wb");
+
+  free (buf);
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+static int
+gameboy_convert_data (st_rominfo_t *rominfo, unsigned char *conversion_table,
+                      const char *suffix)
+{
+  char dest_name[FILENAME_MAX], src_name[FILENAME_MAX];
+  unsigned char buf[MAXBUFSIZE];
+  int x, n, n_bytes;
+
+  strcpy (src_name, ucon64.rom);
+  strcpy (dest_name, ucon64.rom);
+  set_suffix (dest_name, suffix);
+  ucon64_file_handler (dest_name, src_name, 0);
+
+  x = rominfo->buheader_len;
+  while ((n_bytes = ucon64_fread (buf, x, MAXBUFSIZE, src_name)))
+    {
+      for (n = 0; n < n_bytes; n++)
+        buf[n] = conversion_table[(int) buf[n]];
+      ucon64_fwrite (buf, x, n_bytes, dest_name, x == rominfo->buheader_len ? "wb" : "ab");
+      x += n_bytes;
+    }
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+gameboy_gbx (st_rominfo_t *rominfo)
+{
+  unsigned char gbx2gbc[] =
+    {
+      0xB4, 0xBC, 0xA4, 0xAC, 0x94, 0x9C, 0x84, 0x8C,
+      0xF4, 0xFC, 0xE4, 0xEC, 0xD4, 0xDC, 0xC4, 0xCC,
+      0x34, 0x3C, 0x24, 0x2C, 0x14, 0x1C, 0x04, 0x0C,
+      0x74, 0x7C, 0x64, 0x6C, 0x54, 0x5C, 0x44, 0x4C,
+      0xB5, 0xBD, 0xA5, 0xAD, 0x95, 0x9D, 0x85, 0x8D,
+      0xF5, 0xFD, 0xE5, 0xED, 0xD5, 0xDD, 0xC5, 0xCD,
+      0x35, 0x3D, 0x25, 0x2D, 0x15, 0x1D, 0x05, 0x0D,
+      0x75, 0x7D, 0x65, 0x6D, 0x55, 0x5D, 0x45, 0x4D,
+      0xB6, 0xBE, 0xA6, 0xAE, 0x96, 0x9E, 0x86, 0x8E,
+      0xF6, 0xFE, 0xE6, 0xEE, 0xD6, 0xDE, 0xC6, 0xCE,
+      0x36, 0x3E, 0x26, 0x2E, 0x16, 0x1E, 0x06, 0x0E,
+      0x76, 0x7E, 0x66, 0x6E, 0x56, 0x5E, 0x46, 0x4E,
+      0xB7, 0xBF, 0xA7, 0xAF, 0x97, 0x9F, 0x87, 0x8F,
+      0xF7, 0xFF, 0xE7, 0xEF, 0xD7, 0xDF, 0xC7, 0xCF,
+      0x37, 0x3F, 0x27, 0x2F, 0x17, 0x1F, 0x07, 0x0F,
+      0x77, 0x7F, 0x67, 0x6F, 0x57, 0x5F, 0x47, 0x4F,
+      0xB0, 0xB8, 0xA0, 0xA8, 0x90, 0x98, 0x80, 0x88,
+      0xF0, 0xF8, 0xE0, 0xE8, 0xD0, 0xD8, 0xC0, 0xC8,
+      0x30, 0x38, 0x20, 0x28, 0x10, 0x18, 0x00, 0x08,
+      0x70, 0x78, 0x60, 0x68, 0x50, 0x58, 0x40, 0x48,
+      0xB1, 0xB9, 0xA1, 0xA9, 0x91, 0x99, 0x81, 0x89,
+      0xF1, 0xF9, 0xE1, 0xE9, 0xD1, 0xD9, 0xC1, 0xC9,
+      0x31, 0x39, 0x21, 0x29, 0x11, 0x19, 0x01, 0x09,
+      0x71, 0x79, 0x61, 0x69, 0x51, 0x59, 0x41, 0x49,
+      0xB2, 0xBA, 0xA2, 0xAA, 0x92, 0x9A, 0x82, 0x8A,
+      0xF2, 0xFA, 0xE2, 0xEA, 0xD2, 0xDA, 0xC2, 0xCA,
+      0x32, 0x3A, 0x22, 0x2A, 0x12, 0x1A, 0x02, 0x0A,
+      0x72, 0x7A, 0x62, 0x6A, 0x52, 0x5A, 0x42, 0x4A,
+      0xB3, 0xBB, 0xA3, 0xAB, 0x93, 0x9B, 0x83, 0x8B,
+      0xF3, 0xFB, 0xE3, 0xEB, 0xD3, 0xDB, 0xC3, 0xCB,
+      0x33, 0x3B, 0x23, 0x2B, 0x13, 0x1B, 0x03, 0x0B,
+      0x73, 0x7B, 0x63, 0x6B, 0x53, 0x5B, 0x43, 0x4B
+    };
+  const char *old_suffix = get_suffix (ucon64.rom), *new_suffix;
+  new_suffix = stricmp (old_suffix, ".GBX") ? ".GB" : ".GBC";
+  return gameboy_convert_data (rominfo, gbx2gbc, new_suffix);
+}
+
+
+int
+gameboy_sgb (st_rominfo_t *rominfo)
+{
+  unsigned char gbc2gbx[] =
+    {
+      0x96, 0xB6, 0xD6, 0xF6, 0x16, 0x36, 0x56, 0x76,
+      0x97, 0xB7, 0xD7, 0xF7, 0x17, 0x37, 0x57, 0x77,
+      0x94, 0xB4, 0xD4, 0xF4, 0x14, 0x34, 0x54, 0x74,
+      0x95, 0xB5, 0xD5, 0xF5, 0x15, 0x35, 0x55, 0x75,
+      0x92, 0xB2, 0xD2, 0xF2, 0x12, 0x32, 0x52, 0x72,
+      0x93, 0xB3, 0xD3, 0xF3, 0x13, 0x33, 0x53, 0x73,
+      0x90, 0xB0, 0xD0, 0xF0, 0x10, 0x30, 0x50, 0x70,
+      0x91, 0xB1, 0xD1, 0xF1, 0x11, 0x31, 0x51, 0x71,
+      0x9E, 0xBE, 0xDE, 0xFE, 0x1E, 0x3E, 0x5E, 0x7E,
+      0x9F, 0xBF, 0xDF, 0xFF, 0x1F, 0x3F, 0x5F, 0x7F,
+      0x9C, 0xBC, 0xDC, 0xFC, 0x1C, 0x3C, 0x5C, 0x7C,
+      0x9D, 0xBD, 0xDD, 0xFD, 0x1D, 0x3D, 0x5D, 0x7D,
+      0x9A, 0xBA, 0xDA, 0xFA, 0x1A, 0x3A, 0x5A, 0x7A,
+      0x9B, 0xBB, 0xDB, 0xFB, 0x1B, 0x3B, 0x5B, 0x7B,
+      0x98, 0xB8, 0xD8, 0xF8, 0x18, 0x38, 0x58, 0x78,
+      0x99, 0xB9, 0xD9, 0xF9, 0x19, 0x39, 0x59, 0x79,
+      0x86, 0xA6, 0xC6, 0xE6, 0x06, 0x26, 0x46, 0x66,
+      0x87, 0xA7, 0xC7, 0xE7, 0x07, 0x27, 0x47, 0x67,
+      0x84, 0xA4, 0xC4, 0xE4, 0x04, 0x24, 0x44, 0x64,
+      0x85, 0xA5, 0xC5, 0xE5, 0x05, 0x25, 0x45, 0x65,
+      0x82, 0xA2, 0xC2, 0xE2, 0x02, 0x22, 0x42, 0x62,
+      0x83, 0xA3, 0xC3, 0xE3, 0x03, 0x23, 0x43, 0x63,
+      0x80, 0xA0, 0xC0, 0xE0, 0x00, 0x20, 0x40, 0x60,
+      0x81, 0xA1, 0xC1, 0xE1, 0x01, 0x21, 0x41, 0x61,
+      0x8E, 0xAE, 0xCE, 0xEE, 0x0E, 0x2E, 0x4E, 0x6E,
+      0x8F, 0xAF, 0xCF, 0xEF, 0x0F, 0x2F, 0x4F, 0x6F,
+      0x8C, 0xAC, 0xCC, 0xEC, 0x0C, 0x2C, 0x4C, 0x6C,
+      0x8D, 0xAD, 0xCD, 0xED, 0x0D, 0x2D, 0x4D, 0x6D,
+      0x8A, 0xAA, 0xCA, 0xEA, 0x0A, 0x2A, 0x4A, 0x6A,
+      0x8B, 0xAB, 0xCB, 0xEB, 0x0B, 0x2B, 0x4B, 0x6B,
+      0x88, 0xA8, 0xC8, 0xE8, 0x08, 0x28, 0x48, 0x68,
+      0x89, 0xA9, 0xC9, 0xE9, 0x09, 0x29, 0x49, 0x69
+    };
+  const char *old_suffix = get_suffix (ucon64.rom), *new_suffix;
+  new_suffix = stricmp (old_suffix, ".GBC") ? ".GX" : ".GBX";
+  return gameboy_convert_data (rominfo, gbc2gbx, new_suffix);
+}
+
+
+int
+gameboy_n (st_rominfo_t *rominfo, const char *name)
+{
+  char buf[GB_NAME_LEN], dest_name[FILENAME_MAX];
+
+  memset (buf, 0, GB_NAME_LEN);
+  strncpy (buf, name, GB_NAME_LEN);
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
+  ucon64_fwrite (buf, rominfo->buheader_len + GAMEBOY_HEADER_START + 0x34,
+                 GB_NAME_LEN, dest_name, "r+b");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+gameboy_chk (st_rominfo_t *rominfo)
+{
+  char buf[4], dest_name[FILENAME_MAX];
+
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
+
+  buf[0] = checksum.header;
+  buf[1] = rominfo->current_internal_crc >> 8;
+  buf[2] = rominfo->current_internal_crc;
+  ucon64_fwrite (buf, rominfo->buheader_len + GAMEBOY_HEADER_START + 0x4d, 3,
+                 dest_name, "r+b");
+
+  dumper (stdout, buf, 3, GAMEBOY_HEADER_START + rominfo->buheader_len + 0x4d, DUMPER_HEX);
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+gameboy_mgd (st_rominfo_t *rominfo)
+// TODO: convert the ROM data
+{
+  char src_name[FILENAME_MAX], dest_name[FILENAME_MAX];
+  int size = ucon64.file_size - rominfo->buheader_len;
+
+  strcpy (src_name, ucon64.rom);
+  mgd_make_name (ucon64.rom, UCON64_GB, size, dest_name);
+  ucon64_file_handler (dest_name, src_name, OF_FORCE_BASENAME);
+
+  fcopy (src_name, rominfo->buheader_len, size, dest_name, "wb");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  remove_temp_file ();
+
+  mgd_write_index_file ((char *) basename2 (dest_name), 1);
+  return 0;
+}
+
+
+int
+gameboy_ssc (st_rominfo_t *rominfo)
+// TODO: convert the ROM data
+{
+  st_unknown_header_t header;
+  char src_name[FILENAME_MAX], dest_name[FILENAME_MAX];
+  const char *p = NULL;
+  int size = ucon64.file_size - rominfo->buheader_len;
+
+  memset (&header, 0, UNKNOWN_HEADER_LEN);
+
+  header.size_low = size / 8192;
+  header.size_high = size / 8192 >> 8;
+  header.id1 = 0xaa;
+  header.id2 = 0xbb;
+#if 0 // TODO: find out correct value. 2 is used for Magic Super Griffin
+  header.type = 2;
+#endif
+
+  strcpy (src_name, ucon64.rom);
+  p = basename2 (ucon64.rom);
+  // TODO: find out if this is correct (giving the file name a prefix)
+  if ((p[0] == 'G' || p[0] == 'g') && (p[1] == 'B' || p[1] == 'b'))
+    strcpy (dest_name, p);
+  else
+    sprintf (dest_name, "gb%s", p);
+  set_suffix (dest_name, ".gb");
+
+  ucon64_file_handler (dest_name, src_name, 0);
+  ucon64_fwrite (&header, 0, UNKNOWN_HEADER_LEN, dest_name, "wb");
+  fcopy (src_name, rominfo->buheader_len, size, dest_name, "ab");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  remove_temp_file ();
+  return 0;
+}
+
+
+int
+gameboy_init (st_rominfo_t *rominfo)
+{
+  int result = -1, value, x;
+  char buf[MAXBUFSIZE];
+  static const char *gameboy_romtype1[0x20] =
+    {
+      "ROM only",
+      "ROM + MBC1",
+      "ROM + MBC1 + RAM",
+      "ROM + MBC1 + RAM + Battery",
+      NULL,
+      "ROM + MBC2",
+      "ROM + MBC2 + Battery",
+      NULL,
+      "ROM + RAM",                              // correct? - dbjh
+      "ROM + RAM + Battery",                    // correct? - dbjh
+      NULL,
+      "ROM + MMM01",
+      "ROM + MMM01 + SRAM",
+      "ROM + MMM01 + SRAM + Battery",
+      NULL,
+      "ROM + MBC3 + Battery + Timer",
+      "ROM + MBC3 + RAM + Battery + Timer",
+      "ROM + MBC3",
+      "ROM + MBC3 + RAM",
+      "ROM + MBC3 + RAM + Battery",
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      "ROM + MBC5",
+      "ROM + MBC5 + RAM",
+      "ROM + MBC5 + RAM + Battery",
+      "ROM + MBC5 + Rumble",
+      "ROM + MBC5 + SRAM + Rumble",
+      "ROM + MBC5 + SRAM + Battery + Rumble",
+      "Nintendo Pocket Camera"
+    },
+    *gameboy_romtype2[3] =
+    {
+      "Rocket Games",
+      NULL,
+      "Rocket Games 2-in-1"
+    },
+    *gameboy_romtype3[3] =
+    {
+      "Bandai TAMA5",
+      "Hudson HuC-3",
+      "Hudson HuC-1"
+    },
+    *str;
+
+  rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? ucon64.buheader_len : 0;
+
+  ucon64_fread (&gameboy_header, rominfo->buheader_len + GAMEBOY_HEADER_START,
+                GAMEBOY_HEADER_LEN, ucon64.rom);
+  if (gameboy_header.opcode1 == 0x00 && gameboy_header.opcode2 == 0xc3)
+    result = 0;
+  else
+    {
+      rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ?
+        ucon64.buheader_len : (int) SSC_HEADER_LEN;
+
+      ucon64_fread (&gameboy_header, rominfo->buheader_len + GAMEBOY_HEADER_START,
+                    GAMEBOY_HEADER_LEN, ucon64.rom);
+      if (gameboy_header.opcode1 == 0x00 && gameboy_header.opcode2 == 0xc3)
+        result = 0;
+      else
+        result = -1;
+    }
+  if (ucon64.console == UCON64_GB)
+    result = 0;
+
+  rominfo->header_start = GAMEBOY_HEADER_START;
+  rominfo->header_len = GAMEBOY_HEADER_LEN;
+  rominfo->header = &gameboy_header;
+
+  // internal ROM name
+  strncpy (rominfo->name, (const char *) gameboy_header.name, GB_NAME_LEN);
+  rominfo->name[GB_NAME_LEN] = 0;               // terminate string
+
+  // ROM maker
+  if (gameboy_header.maker == 0x33)
+    {
+      int ih = gameboy_header.maker_high <= '9' ?
+                 gameboy_header.maker_high - '0' : gameboy_header.maker_high - 'A' + 10,
+          il = gameboy_header.maker_low <= '9' ?
+                 gameboy_header.maker_low - '0' : gameboy_header.maker_low - 'A' + 10;
+      x = ih * 36 + il;
+    }
+  else
+    x = (gameboy_header.maker >> 4) * 36 + (gameboy_header.maker & 0x0f);
+
+  /*
+    I added the first if statement, because I didn't want to expand
+    nintendo_maker by a large amount for only one publisher and because index 0
+    is used when the publisher code is unknown (so it shouldn't be set to
+    "Rocket Games"). - dbjh
+  */
+  if (x == 33 * 36 + 33 || x == 0)              // publisher code XX/00
+    x = 2;                                      // Rocket Games
+  else if (x < 0 || x >= NINTENDO_MAKER_LEN)
+    x = 0;
+  rominfo->maker = NULL_TO_UNKNOWN_S (nintendo_maker[x]);
+
+  // ROM country
+  rominfo->country = gameboy_header.country == 0 ? "Japan" : "U.S.A. & Europe";
+
+  // misc stuff
+  // Don't move division by 4 to shift parameter (gameboy_header.rom_size can be < 2)
+  sprintf (buf, "Internal size: %.4f Mb\n", (1 << gameboy_header.rom_size) / 4.0f);
+  strcat (rominfo->misc, buf);
+
+  if (gameboy_header.rom_type <= 0x1f)
+    str = NULL_TO_UNKNOWN_S (gameboy_romtype1[gameboy_header.rom_type]);
+  else if (gameboy_header.rom_type >= 0x97 && gameboy_header.rom_type <= 0x99)
+    str = gameboy_romtype2[gameboy_header.rom_type - 0x97];
+  else if (gameboy_header.rom_type >= 0xfd)
+    str = gameboy_romtype3[gameboy_header.rom_type - 0xfd];
+  else
+    str = "Unknown";
+  sprintf (buf, "ROM type: %s\n", str);
+  strcat (rominfo->misc, buf);
+
+  if (!gameboy_header.sram_size)
+    sprintf (buf, "Save RAM: No\n");
+  else
+    {
+      value = (gameboy_header.sram_size & 7) << 1; // 0/1/2/4/5
+      if (value)
+        value = 1 << (value - 1);
+
+      sprintf (buf, "Save RAM: Yes, %d kBytes\n", value);
+    }
+  strcat (rominfo->misc, buf);
+
+  sprintf (buf, "Version: 1.%d\n", gameboy_header.version);
+  strcat (rominfo->misc, buf);
+
+  sprintf (buf, "Game Boy type: %s\n",
+           (gameboy_header.gb_type == 0x80) ? "Color" :
+//           (OFFSET (gameboy_header, 0x46) == 0x03) ? "Super" :
+           "Standard (4 colors)");
+  strcat (rominfo->misc, buf);
+
+  value = gameboy_header.start_high << 8;
+  value += gameboy_header.start_low;
+  sprintf (buf, "Start address: 0x%04x\n", value);
+  strcat (rominfo->misc, buf);
+
+  strcat (rominfo->misc, "Logo data: ");
+  if (memcmp (gameboy_header.logo,
+              (gameboy_header.rom_type >= 0x97 && gameboy_header.rom_type <= 0x99) ?
+                rocket_logodata : gb_logodata,
+              GB_LOGODATA_LEN) == 0)
+    {
+#ifdef  USE_ANSI_COLOR
+      if (ucon64.ansi_color)
+        strcat (rominfo->misc, "\x1b[01;32mOk\x1b[0m");
+      else
+#endif
+        strcat (rominfo->misc, "Ok");
+    }
+  else
+    {
+#ifdef  USE_ANSI_COLOR
+      if (ucon64.ansi_color)
+        strcat (rominfo->misc, "\x1b[01;31mBad\x1b[0m");
+      else
+#endif
+        strcat (rominfo->misc, "Bad");
+    }
+
+  if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0)
+    {
+      rominfo->has_internal_crc = 1;
+      rominfo->internal_crc_len = 2;
+      checksum = gameboy_chksum (rominfo);
+      rominfo->current_internal_crc = checksum.value;
+
+      rominfo->internal_crc = (gameboy_header.checksum_high << 8) +
+                              gameboy_header.checksum_low;
+
+      x = gameboy_header.header_checksum;
+      sprintf (rominfo->internal_crc2,
+               "Header checksum: %s, 0x%02x (calculated) %c= 0x%02x (internal)",
+#ifdef  USE_ANSI_COLOR
+               ucon64.ansi_color ?
+                 ((checksum.header == x) ?
+                   "\x1b[01;32mOk\x1b[0m" : "\x1b[01;31mBad\x1b[0m")
+                 :
+                 ((checksum.header == x) ? "Ok" : "Bad"),
+#else
+               (checksum.header == x) ? "Ok" : "Bad",
+#endif
+               checksum.header, (checksum.header == x) ? '=' : '!', x);
+    }
+
+  rominfo->console_usage = gameboy_usage[0].help;
+  rominfo->copier_usage = (!rominfo->buheader_len ? mgd_usage[0].help : ssc_usage[0].help);
+
+  return result;
+}
+
+
+st_gameboy_chksum_t
+gameboy_chksum (st_rominfo_t *rominfo)
+{
+  st_gameboy_chksum_t sum = { 0, 0 };
+  unsigned char *rom_buffer;
+  int size = ucon64.file_size - rominfo->buheader_len, i;
+
+  if (!(rom_buffer = (unsigned char *) malloc (size)))
+    {
+      fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size);
+      return sum;
+    }
+  ucon64_fread (rom_buffer, rominfo->buheader_len, size, ucon64.rom);
+
+  for (i = GAMEBOY_HEADER_START + 0x34; i < GAMEBOY_HEADER_START + 0x4d; i++)
+    sum.header += ~rom_buffer[i];
+  for (i = 0; i < size; i++)
+    sum.value += rom_buffer[i];
+  sum.value -= (rom_buffer[GAMEBOY_HEADER_START + 0x4d] - sum.header) +
+               rom_buffer[GAMEBOY_HEADER_START + 0x4e] +
+               rom_buffer[GAMEBOY_HEADER_START + 0x4f];
+
+  free (rom_buffer);
+
+  return sum;
+}

+ 39 - 0
ucon64/2.0/src/console/gb.h

@@ -0,0 +1,39 @@
+/*
+gb.h - Game Boy support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef GB_H
+#define GB_H
+
+#define GB_LOGODATA_LEN 48
+
+extern const st_getopt2_t gameboy_usage[];
+extern const unsigned char gb_logodata[], rocket_logodata[];
+
+extern int gameboy_chk (st_rominfo_t *rominfo);
+extern int gameboy_gbx (st_rominfo_t *rominfo);
+extern int gameboy_mgd (st_rominfo_t *rominfo);
+extern int gameboy_n (st_rominfo_t *rominfo, const char *name);
+extern int gameboy_n2gb (st_rominfo_t *rominfo, const char *emu_rom);
+extern int gameboy_sgb (st_rominfo_t *rominfo);
+extern int gameboy_ssc (st_rominfo_t *rominfo);
+extern int gameboy_init (st_rominfo_t *rominfo);
+extern int gameboy_logo (st_rominfo_t *rominfo);
+
+#endif

+ 850 - 0
ucon64/2.0/src/console/gba.c

@@ -0,0 +1,850 @@
+/*
+gba.c - Game Boy Advance support for uCON64
+
+Copyright (c) 2001        NoisyB <noisyb@gmx.net>
+Copyright (c) 2001 - 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef  HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include "misc/file.h"
+#include "misc/misc.h"
+#include "misc/property.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "misc/string.h"
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "gba.h"
+#include "backup/fal.h"
+
+
+#define GBA_NAME_LEN 12
+#define GBA_HEADER_START 0
+#define GBA_HEADER_LEN (sizeof (st_gba_header_t))
+
+
+static int gba_chksum (void);
+
+const st_getopt2_t gba_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Game Boy Advance"/*"2001 Nintendo http://www.nintendo.com"*/,
+      NULL
+    },
+    {
+      "gba", 0, 0, UCON64_GBA,
+      NULL, "force recognition",
+      &ucon64_wf[WF_OBJ_GBA_SWITCH]
+    },
+    {
+      "n", 1, 0, UCON64_N,
+      "NEW_NAME", "change internal ROM name to NEW_NAME",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT]
+    },
+    {
+      "logo", 0, 0, UCON64_LOGO,
+      NULL, "restore ROM logo character data (offset: 0x04-0x9F)",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT]
+    },
+    {
+      "chk", 0, 0, UCON64_CHK,
+      NULL, "fix ROM header checksum",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT]
+    },
+    {
+      "sram", 0, 0, UCON64_SRAM,
+      NULL, "patch ROM for SRAM saving",
+      &ucon64_wf[WF_OBJ_GBA_DEFAULT]
+    },
+    {
+      "crp", 1, 0, UCON64_CRP,
+      "WAIT_TIME", "slow down ROM access (\"crash patch\");\n"
+      "WAIT_TIME=0  default in most crash patches\n"
+      "WAIT_TIME=4  faster than 0, slower than 8\n"
+      "WAIT_TIME=8  faster than 4, slower than 28\n"
+      "WAIT_TIME=12 slowest cartridge access speed\n"
+      "WAIT_TIME=16 faster than 28, but slower than 20\n"
+      "WAIT_TIME=20 default in most original cartridges\n"
+      "WAIT_TIME=24 fastest cartridge access speed\n"
+      "WAIT_TIME=28 faster than 8 but slower than 16",
+      &ucon64_wf[WF_OBJ_GBA_DEFAULT]
+    },
+//  "n 0 and 28, with a stepping of 4. I.e. 0, 4, 8, 12 ...\n"
+    {
+      "multi", 1, 0, UCON64_MULTI,
+      "SIZE", "make multi-game file for use with FAL/F2A flash card, truncated\n"
+      "to SIZE Mbit; file with loader must be specified first, then\n"
+      "all the ROMs, multi-game file to create last",
+      &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_STOP]
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+
+/*
+Offset 00h-03h - Start  Address - A 32 bit ARM B command with jump destination
+                 to  the  start  address of the program, cannot be manipulated
+                 with this tool, there's no reason.
+
+Offset 04h-9fh - Nintendo logo character data - The fix Nintendo logo graphics
+                 needed  to  start a ROM on the real machine as it is verified
+                 by it.
+
+Offset a0h-abh - Game  title  -  The game title is an ASCII string, officially
+                 can  use only ASCII characters between the ASCII code 20h and
+                 60h.  Although it is not a strict rule for hobby programmers,
+                 it  is  fun  to  follow such a rules in my opinion. As I know
+                 developers  can  choose  their own game title here describing
+                 the product in short.
+
+Offset ach-afh - Game  code  -  The  4 bytes long code of the game is an ASCII
+                 string  too, officially can use only ASCII characters between
+                 the ASCII code 20h and 60h. The first letter is always A as I
+                 know,  probably  stands  for GBA, so it won't change unless a
+                 higher   hardware   with  backwards  compatibility  won't  be
+                 introduced  and  this letter could hold some more infos about
+                 it. The second and third letters are the shortened version of
+                 the  name of the game. And the fourth letter is the territory
+                 code. Don't afraid, there's no territory lockout, this is for
+                 information  purposes  only.  So  far  as I know J stands for
+                 Japan  and  Asia,  E  stands  for  USA and the whole American
+                 continent  and  P  stands  for  Europe,  Australia and Africa
+                 (probably  came  from  that  these are the PAL video standard
+                 territories,  but  I  could  be  wrong). Although it is not a
+                 strict rule for hobby programmers, it is fun to follow such a
+                 rules  in my opinion. Developers get this 4 letter code right
+                 from Nintendo and they have to use that.
+
+Offset b0h-b1h - Maker  code  - The 2 bytes long code of the developer company
+                 is  an  ASCII  string  too,  officially  can  use  only ASCII
+                 characters between the ASCII code 20h and 60h. Although it is
+                 not  a strict rule for hobby programmers, it is fun to follow
+                 such a rules in my opinion. Developers get this 2 letter code
+                 right from Nintendo and they have to use that.
+
+Offset b2h-b2h - 96h - Fixed 96h byte without any useful information.
+
+Offset b3h-b3h - Main  unit  code  -  This hexadecimal byte is the destination
+                 hardware  code.  It  is always 00h at the moment as it stands
+                 for  Game Boy Advance, so it won't change in the future either
+                 unless  a  higher hardware with backwards compatibility won't
+                 be  introduced and this byte could hold some more infos about
+                 it.  There's  no  reason  to  change  this or write something
+                 different than 00h into it.
+
+Offset b4h-b4h - Device type -  This hexadecimal byte is the device type code.
+                 It  is always 00h as the only other possible value stands for
+                 a  debugger  cart  what  I  assume  won't be available on the
+                 streets  and  I  assume even if a developer works with such a
+                 hardware, he or she doesn't have to change this byte, however
+                 he  or  she  easily  can  of  course. So there's no reason to
+                 change this or write something different than 00h into it.
+
+Offset b5h-bbh - Reserved  area  -  Fixed,  00h filled area without any useful
+                 information.
+
+Offset bch-bch - Mask  ROM  version  number  - This hexadecimal byte holds the
+                 version  number  of  the ROM. As I know it works somehow that
+                 way,  the  first  published  (and released on the streets) is
+                 always  the first version and for that 00h is stored here. In
+                 the  case it is getting updated, so in the same territory the
+                 very  same  game with the very same title is getting replaced
+                 with a new version, what is happening rarely, the number here
+                 is  getting  increased by one. So usually this byte holds 00h
+                 and  there  isn't too much reason to write something here and
+                 something else than 00h.
+
+Offset bdh-bdh - Complement   check   -  This  hexadecimal  byte  have  to  be
+                 calculated  automatically,  when  the  whole header is in its
+                 final  state,  so nothing will change inside of it. (Manually
+                 it  would be hard to calculate.) Add the bytes between offset
+                 a0h  and bch together, take the number's two's complement and
+                 add  19h  to  the  result.  Store  the lowest 8 bits here. Or
+                 calculate   automatically   with   GBARM.   The  hardware  is
+                 verifying  this  byte  just  like the Nintendo logo character
+                 data  and  in the case it isn't correct, the game won't start
+                 on the real machine.
+
+Offset beh-bfh - Reserved  area  -  Fixed,  00h filled area without any useful
+                 information.
+*/
+typedef struct st_gba_header
+{
+  unsigned char start[4];                       // 0x00
+  unsigned char logo[GBA_LOGODATA_LEN];         // 0x04
+  unsigned char name[GBA_NAME_LEN];             // 0xa0
+  unsigned char game_id_prefix;                 // 0xac
+  unsigned char game_id_low;                    // 0xad
+  unsigned char game_id_high;                   // 0xae
+  unsigned char game_id_country;                // 0xaf
+  unsigned char maker_high;                     // 0xb0
+  unsigned char maker_low;                      // 0xb1
+  unsigned char pad1;
+  unsigned char gba_type;                       // 0xb3
+  unsigned char device_type;                    // 0xb4
+  unsigned char pad2[7];
+  unsigned char version;                        // 0xbc
+  unsigned char checksum;                       // 0xbd
+  unsigned char pad3[2];
+} st_gba_header_t;
+
+static st_gba_header_t gba_header;
+const unsigned char gba_logodata[] =            // Note: not a static variable
+  {
+                            0x24, 0xff, 0xae, 0x51,
+    0x69, 0x9a, 0xa2, 0x21, 0x3d, 0x84, 0x82, 0x0a,
+    0x84, 0xe4, 0x09, 0xad, 0x11, 0x24, 0x8b, 0x98,
+    0xc0, 0x81, 0x7f, 0x21, 0xa3, 0x52, 0xbe, 0x19,
+    0x93, 0x09, 0xce, 0x20, 0x10, 0x46, 0x4a, 0x4a,
+    0xf8, 0x27, 0x31, 0xec, 0x58, 0xc7, 0xe8, 0x33,
+    0x82, 0xe3, 0xce, 0xbf, 0x85, 0xf4, 0xdf, 0x94,
+    0xce, 0x4b, 0x09, 0xc1, 0x94, 0x56, 0x8a, 0xc0,
+    0x13, 0x72, 0xa7, 0xfc, 0x9f, 0x84, 0x4d, 0x73,
+    0xa3, 0xca, 0x9a, 0x61, 0x58, 0x97, 0xa3, 0x27,
+    0xfc, 0x03, 0x98, 0x76, 0x23, 0x1d, 0xc7, 0x61,
+    0x03, 0x04, 0xae, 0x56, 0xbf, 0x38, 0x84, 0x00,
+    0x40, 0xa7, 0x0e, 0xfd, 0xff, 0x52, 0xfe, 0x03,
+    0x6f, 0x95, 0x30, 0xf1, 0x97, 0xfb, 0xc0, 0x85,
+    0x60, 0xd6, 0x80, 0x25, 0xa9, 0x63, 0xbe, 0x03,
+    0x01, 0x4e, 0x38, 0xe2, 0xf9, 0xa2, 0x34, 0xff,
+    0xbb, 0x3e, 0x03, 0x44, 0x78, 0x00, 0x90, 0xcb,
+    0x88, 0x11, 0x3a, 0x94, 0x65, 0xc0, 0x7c, 0x63,
+    0x87, 0xf0, 0x3c, 0xaf, 0xd6, 0x25, 0xe4, 0x8b,
+    0x38, 0x0a, 0xac, 0x72, 0x21, 0xd4, 0xf8, 0x07
+  };
+
+
+int
+gba_n (st_rominfo_t *rominfo, const char *name)
+{
+  char buf[GBA_NAME_LEN], dest_name[FILENAME_MAX];
+
+  memset (buf, 0, GBA_NAME_LEN);
+  strncpy (buf, name, GBA_NAME_LEN);
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
+  ucon64_fwrite (buf, GBA_HEADER_START + rominfo->buheader_len + 0xa0, GBA_NAME_LEN,
+            dest_name, "r+b");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+gba_logo (st_rominfo_t *rominfo)
+{
+  char dest_name[FILENAME_MAX];
+
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
+  ucon64_fwrite (gba_logodata, GBA_HEADER_START + rominfo->buheader_len + 0x04,
+            GBA_LOGODATA_LEN, dest_name, "r+b");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+gba_chk (st_rominfo_t *rominfo)
+{
+  char buf, dest_name[FILENAME_MAX];
+
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
+
+  buf = rominfo->current_internal_crc;
+  ucon64_fputc (dest_name, GBA_HEADER_START + rominfo->buheader_len + 0xbd,
+    buf, "r+b");
+
+  dumper (stdout, &buf, 1, GBA_HEADER_START + rominfo->buheader_len + 0xbd, DUMPER_HEX);
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+gba_sram (void)
+// This function is based on Omar Kilani's gbautil 1.1
+{
+  unsigned char st_orig[2][10] =
+    {
+      { 0x0E, 0x48, 0x39, 0x68, 0x01, 0x60, 0x0E, 0x48, 0x79, 0x68 },
+      { 0x13, 0x4B, 0x18, 0x60, 0x13, 0x48, 0x01, 0x60, 0x13, 0x49 }
+    },
+    st_repl[2][10] =
+    {
+      { 0x00, 0x48, 0x00, 0x47, 0x01, 0xFF, 0xFF, 0x08, 0x79, 0x68 },
+      { 0x01, 0x4C, 0x20, 0x47, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x08 }
+    },
+    fl_orig[2][24] =
+    {
+      {
+        0xD0, 0x20, 0x00, 0x05, 0x01, 0x88, 0x01, 0x22, 0x08, 0x1C,
+        0x10, 0x40, 0x02, 0x1C, 0x11, 0x04, 0x08, 0x0C, 0x00, 0x28,
+        0x01, 0xD0, 0x1B, 0xE0
+      },
+      {
+        0xD0, 0x21, 0x09, 0x05, 0x01, 0x23, 0x0C, 0x4A, 0x08, 0x88,
+        0x18, 0x40, 0x00, 0x28, 0x08, 0xD1, 0x10, 0x78, 0x00, 0x28,
+        0xF8, 0xD0, 0x08, 0x88
+      }
+    },
+    fl_repl[2][24] =
+    {
+      {
+        0xE0, 0x20, 0x00, 0x05, 0x01, 0x88, 0x01, 0x22, 0x08, 0x1C,
+        0x10, 0x40, 0x02, 0x1C, 0x11, 0x04, 0x08, 0x0C, 0x00, 0x28,
+        0x01, 0xD0, 0x1B, 0xE0
+      },
+      {
+        0xE0, 0x21, 0x09, 0x05, 0x01, 0x23, 0x0C, 0x4A, 0x08, 0x88,
+        0x18, 0x40, 0x00, 0x28, 0x08, 0xD1, 0x10, 0x78, 0x00, 0x28,
+        0xF8, 0xD0, 0x08, 0x88
+      }
+    },
+    p_repl[2][188] =
+    {
+      {
+        0x39, 0x68, 0x27, 0x48, 0x81, 0x42, 0x23, 0xD0, 0x89, 0x1C,
+        0x08, 0x88, 0x01, 0x28, 0x02, 0xD1, 0x24, 0x48, 0x78, 0x60,
+        0x33, 0xE0, 0x00, 0x23, 0x00, 0x22, 0x89, 0x1C, 0x10, 0xB4,
+        0x01, 0x24, 0x08, 0x68, 0x20, 0x40, 0x5B, 0x00, 0x03, 0x43,
+        0x89, 0x1C, 0x52, 0x1C, 0x06, 0x2A, 0xF7, 0xD1, 0x10, 0xBC,
+        0x39, 0x60, 0xDB, 0x01, 0x02, 0x20, 0x00, 0x02, 0x1B, 0x18,
+        0x0E, 0x20, 0x00, 0x06, 0x1B, 0x18, 0x7B, 0x60, 0x39, 0x1C,
+        0x08, 0x31, 0x08, 0x88, 0x09, 0x38, 0x08, 0x80, 0x16, 0xE0,
+        0x15, 0x49, 0x00, 0x23, 0x00, 0x22, 0x10, 0xB4, 0x01, 0x24,
+        0x08, 0x68, 0x20, 0x40, 0x5B, 0x00, 0x03, 0x43, 0x89, 0x1C,
+        0x52, 0x1C, 0x06, 0x2A, 0xF7, 0xD1, 0x10, 0xBC, 0xDB, 0x01,
+        0x02, 0x20, 0x00, 0x02, 0x1B, 0x18, 0x0E, 0x20, 0x00, 0x06,
+        0x1B, 0x18, 0x08, 0x3B, 0x3B, 0x60, 0x0B, 0x48, 0x39, 0x68,
+        0x01, 0x60, 0x0A, 0x48, 0x79, 0x68, 0x01, 0x60, 0x0A, 0x48,
+        0x39, 0x1C, 0x08, 0x31, 0x0A, 0x88, 0x80, 0x21, 0x09, 0x06,
+        0x0A, 0x43, 0x02, 0x60, 0x07, 0x48, 0x00, 0x47, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0E, 0x04, 0x00,
+        0x00, 0x0E, 0xD4, 0x00, 0x00, 0x04, 0xD8, 0x00, 0x00, 0x04,
+        0xDC, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0x08
+      },
+      {
+        0x22, 0x4C, 0x84, 0x42, 0x20, 0xD0, 0x80, 0x1C, 0x04, 0x88,
+        0x01, 0x25, 0x2C, 0x40, 0x01, 0x2C, 0x02, 0xD1, 0x80, 0x1E,
+        0x1E, 0x49, 0x2E, 0xE0, 0x00, 0x23, 0x00, 0x24, 0x80, 0x1C,
+        0x40, 0xB4, 0x01, 0x26, 0x05, 0x68, 0x35, 0x40, 0x5B, 0x00,
+        0x2B, 0x43, 0x80, 0x1C, 0x64, 0x1C, 0x06, 0x2C, 0xF7, 0xD1,
+        0x40, 0xBC, 0xDB, 0x01, 0x02, 0x24, 0x24, 0x02, 0x1B, 0x19,
+        0x0E, 0x24, 0x24, 0x06, 0x1B, 0x19, 0x19, 0x1C, 0x09, 0x3A,
+        0x16, 0xE0, 0x12, 0x48, 0x00, 0x23, 0x00, 0x24, 0x40, 0xB4,
+        0x01, 0x26, 0x05, 0x68, 0x35, 0x40, 0x5B, 0x00, 0x2B, 0x43,
+        0x80, 0x1C, 0x64, 0x1C, 0x06, 0x2C, 0xF7, 0xD1, 0x40, 0xBC,
+        0xDB, 0x01, 0x02, 0x24, 0x24, 0x02, 0x1B, 0x19, 0x0E, 0x24,
+        0x24, 0x06, 0x1B, 0x19, 0x08, 0x3B, 0x18, 0x1C, 0x08, 0x4C,
+        0x20, 0x60, 0x08, 0x4C, 0x21, 0x60, 0x08, 0x49, 0x80, 0x20,
+        0x00, 0x06, 0x02, 0x43, 0x0A, 0x60, 0x06, 0x4C, 0x20, 0x47,
+        0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0E, 0x04, 0x00,
+        0x00, 0x0E, 0xD4, 0x00, 0x00, 0x04, 0xD8, 0x00, 0x00, 0x04,
+        0xDC, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0x08
+      }
+    },
+    major, minor, micro, *buffer, *bufferptr, *ptr, value;
+  char dest_name[FILENAME_MAX];
+  int p_size[2] = { 188, 168 }, p_off, st_off;
+  unsigned int fsize = ucon64.file_size;
+  FILE *destfile;
+
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
+
+  if ((destfile = fopen (dest_name, "rb+")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name);
+      return -1;
+    }
+  if (!(buffer = (unsigned char *) malloc (fsize)))
+    {
+      fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], fsize);
+      fclose (destfile);
+      exit (1);
+    }
+  if (fread (buffer, 1, fsize, destfile) != fsize)
+    {
+      fprintf (stderr, ucon64_msg[READ_ERROR], dest_name);
+      free (buffer);
+      fclose (destfile);
+      return -1;
+    }
+
+  bufferptr = buffer + 160 + 12 + 4;
+
+  ptr = (unsigned char *) memmem2 (bufferptr, fsize, "EEPROM_", 7, 0);
+  if (ptr == 0)
+    {
+      printf ("This ROM does not appear to use EEPROM saving\n");
+      free (buffer);
+      fclose (destfile);
+      return -1;
+    }
+  major = ptr[8] - '0';
+  minor = ptr[9] - '0';
+  micro = ptr[10] - '0';
+  if (ucon64.quiet < 0)
+    printf ("version: %d.%d.%d; offset: 0x%08x\n",
+            major, minor, micro, (int) (ptr - buffer));
+  if (minor > 2)
+    {
+      fputs ("ERROR: ROMs with an EEPROM minor version higher than 2 are not supported\n", stderr);
+      free (buffer);
+      fclose (destfile);
+      return -1;
+    }
+
+  ptr = (unsigned char *) memmem2 (bufferptr, fsize,
+                                   fl_orig[minor - 1], sizeof (fl_orig[minor - 1]), 0);
+  if (ptr == 0)
+    {
+      fputs ("ERROR: Could not find fl pattern. Perhaps this file is already patched?\n", stderr);
+      free (buffer);
+      fclose (destfile);
+      return -1;
+    }
+  if (ucon64.quiet < 0)
+    printf ("fl offset: 0x%08x\n", (int) (ptr - buffer));
+  fseek (destfile, ptr - buffer, SEEK_SET);
+  fwrite (fl_repl[minor - 1], 1, sizeof (fl_repl[minor - 1]), destfile);
+
+  ptr = buffer + fsize - 1;
+  value = *ptr;
+  do
+    ptr--;
+  while (*ptr == value && ptr - buffer > 0);
+
+  p_off = (ptr - buffer + 0xff) & ~0xff;        // align at 256 byte boundary
+  if (ucon64.quiet < 0)
+    printf ("p_off: 0x%08x\n", p_off);
+  // if the SRAM function won't fit at the end of the ROM, abort
+  if ((minor == 1 && (int) (fsize - 188) < p_off) ||
+      (minor == 2 && (int) (fsize - 168) < p_off))
+    {
+      fputs ("ERROR: Not enough room for SRAM function at end of ROM\n", stderr);
+      free (buffer);
+      fclose (destfile);
+      return -1;
+    }
+
+  ptr = (unsigned char *) memmem2 (bufferptr, fsize,
+                                   st_orig[minor - 1], sizeof (st_orig[minor - 1]), 0);
+  if (ptr == 0)
+    {
+      fputs ("ERROR: Could not find st pattern\n", stderr);
+      free (buffer);
+      fclose (destfile);
+      return -1;
+    }
+  st_off = ptr - buffer;
+  if (ucon64.quiet < 0)
+    printf ("st offset: 0x%08x\n", st_off);
+
+  bufferptr = buffer + p_off;
+  switch (minor)
+    {
+    case 1:
+      // these are the offsets to the caller function, it handles all saving and
+      //  is at st_off
+      p_repl[minor - 1][184] = (unsigned char) (st_off + 0x21);
+      p_repl[minor - 1][186] = (unsigned char) (st_off >> 16);
+
+      if (*(bufferptr - 1) == 0xff)
+        p_repl[minor - 1][185] = (unsigned char) (st_off >> 8);
+      else
+        {
+          st_off += 0x1f;
+          p_repl[minor - 1][185] = (unsigned char) (st_off >> 8);
+        }
+
+      // tell the calling function where the SRAM function is (p_off)
+      st_repl[minor - 1][5] = (unsigned char) (p_off >> 8);
+      st_repl[minor - 1][6] = (unsigned char) (p_off >> 16);
+      break;
+    case 2:
+      // offsets to the caller function
+      p_repl[minor - 1][164] = (unsigned char) (st_off + 0x13);
+      p_repl[minor - 1][165] = (unsigned char) (st_off >> 8);
+      p_repl[minor - 1][166] = (unsigned char) (st_off >> 16);
+
+      // tell the calling function where the SRAM function is
+      st_repl[minor - 1][7] = (unsigned char) (p_off >> 8);
+      st_repl[minor - 1][8] = (unsigned char) (p_off >> 16);
+      break;
+    }
+  fseek (destfile, st_off, SEEK_SET);
+  fwrite (st_repl[minor - 1], 1, sizeof (st_repl[minor - 1]), destfile);
+  fseek (destfile, p_off, SEEK_SET);
+  fwrite (p_repl[minor - 1], 1, p_size[minor - 1], destfile);
+
+  free (buffer);
+  fclose (destfile);
+
+  puts ("SRAM patch applied");
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+gba_crp (st_rominfo_t *rominfo, const char *value)
+{
+  FILE *srcfile, *destfile;
+  int bytesread, n = 0;
+  char buffer[32 * 1024], src_name[FILENAME_MAX], dest_name[FILENAME_MAX],
+       replace[2], wait_time = atoi (value);
+
+  if (wait_time % 4 != 0 || wait_time > 28 || wait_time < 0)
+    {
+      fprintf (stderr, "ERROR: You specified an incorrect WAIT_TIME value\n");
+      return -1;
+    }
+
+  puts ("Applying crash patch...");
+
+  strcpy (src_name, ucon64.rom);
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, src_name, 0);
+  if ((srcfile = fopen (src_name, "rb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], src_name);
+      return -1;
+    }
+  if ((destfile = fopen (dest_name, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name);
+      return -1;
+    }
+  if (rominfo->buheader_len)                    // copy header (if present)
+    {
+      fread (buffer, 1, rominfo->buheader_len, srcfile);
+      fwrite (buffer, 1, rominfo->buheader_len, destfile);
+    }
+
+  replace[0] = wait_time;
+  replace[1] = 0x40;
+  while ((bytesread = fread (buffer, 1, 32 * 1024, srcfile)))
+    {                           // '!' == ASCII 33 (\x21), '*' == 42 (\x2a)
+      n += change_mem (buffer, bytesread, "\x04\x02\x00\x04\x14\x40", 6, '*', '!', replace, 1, -1);
+      n += change_mem (buffer, bytesread, "\x02\x00\x04\x14\x40\x00", 6, '*', '!', replace, 1, -2);
+      n += change_mem (buffer, bytesread, "\x04\x02\x00\x04\xB4\x45", 6, '*', '!', replace, 2, -1);
+      n += change_mem (buffer, bytesread, "\x3E\xE0\x00\x00\xB4\x45", 6, '*', '!', replace, 2, -1);
+      n += change_mem (buffer, bytesread, "\x04\x02\x00\x04\x94\x44", 6, '*', '!', replace, 2, -1);
+
+      fwrite (buffer, 1, bytesread, destfile);
+    }
+  fclose (srcfile);
+  fclose (destfile);
+
+  printf ("Found %d pattern%s\n", n, n != 1 ? "s" : "");
+  printf (ucon64_msg[WROTE], dest_name);
+  remove_temp_file ();
+  return 0;
+}
+
+
+int
+gba_init (st_rominfo_t *rominfo)
+{
+  int result = -1, value;
+  char buf[MAXBUFSIZE];
+
+  rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ?
+    ucon64.buheader_len : 0;
+
+  ucon64_fread (&gba_header, GBA_HEADER_START +
+           rominfo->buheader_len, GBA_HEADER_LEN, ucon64.rom);
+  if (/*gba_header.game_id_prefix == 'A' && */ // 'B' in Mario vs. Donkey Kong
+      gba_header.start[3] == 0xea && gba_header.pad1 == 0x96 && gba_header.gba_type == 0)
+    result = 0;
+  else
+    {
+#if 0 // AFAIK (dbjh) GBA ROMs never have a header
+      rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ?
+        ucon64.buheader_len : UNKNOWN_HEADER_LEN;
+
+      ucon64_fread (&gba_header, GBA_HEADER_START +
+               rominfo->buheader_len, GBA_HEADER_LEN, ucon64.rom);
+      if (gba_header.game_id_prefix == 'A' && gba_header.gba_type == 0)
+        result = 0;
+      else
+#endif
+        result = -1;
+    }
+  if (ucon64.console == UCON64_GBA)
+    result = 0;
+
+  rominfo->header_start = GBA_HEADER_START;
+  rominfo->header_len = GBA_HEADER_LEN;
+  rominfo->header = &gba_header;
+
+  // internal ROM name
+  strncpy (rominfo->name, (const char *) gba_header.name, GBA_NAME_LEN);
+  rominfo->name[GBA_NAME_LEN] = 0;
+
+  // ROM maker
+  {
+    int ih = gba_header.maker_high <= '9' ?
+               gba_header.maker_high - '0' : gba_header.maker_high - 'A' + 10,
+        il = gba_header.maker_low <= '9' ?
+               gba_header.maker_low - '0' : gba_header.maker_low - 'A' + 10;
+    value = ih * 36 + il;
+  }
+  if (value < 0 || value >= NINTENDO_MAKER_LEN)
+    value = 0;
+  rominfo->maker = NULL_TO_UNKNOWN_S (nintendo_maker[value]);
+
+  // ROM country
+  rominfo->country =
+    (gba_header.game_id_country == 'J') ? "Japan/Asia" :
+    (gba_header.game_id_country == 'E') ? "U.S.A." :
+    (gba_header.game_id_country == 'P') ? "Europe, Australia and Africa" :
+    "Unknown country";
+
+  // misc stuff
+  sprintf (buf, "Version: %d\n", gba_header.version);
+  strcat (rominfo->misc, buf);
+
+  sprintf (buf, "Device type: 0x%02x\n", gba_header.device_type);
+  strcat (rominfo->misc, buf);
+
+  /*
+    start address = current address + (parameter of B instruction * 4) + 8
+    gba_header.start[3] is opcode of B instruction (0xea)
+  */
+  value = 0x8000008 +
+          (gba_header.start[2] << 18 | gba_header.start[1] << 10 | gba_header.start[0] << 2);
+  sprintf (buf, "Start address: 0x%08x\n", value);
+  strcat (rominfo->misc, buf);
+
+  strcat (rominfo->misc, "Logo data: ");
+  if (memcmp (gba_header.logo, gba_logodata, GBA_LOGODATA_LEN) == 0)
+    {
+#ifdef  USE_ANSI_COLOR
+      if (ucon64.ansi_color)
+        strcat (rominfo->misc, "\x1b[01;32mOk\x1b[0m");
+      else
+#endif
+        strcat (rominfo->misc, "Ok");
+    }
+  else
+    {
+#ifdef  USE_ANSI_COLOR
+      if (ucon64.ansi_color)
+        strcat (rominfo->misc, "\x1b[01;31mBad\x1b[0m");
+      else
+#endif
+        strcat (rominfo->misc, "Bad");
+    }
+
+  // internal ROM crc
+  if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0)
+    {
+      rominfo->has_internal_crc = 1;
+      rominfo->internal_crc_len = 1;
+      rominfo->current_internal_crc = gba_chksum ();
+
+      rominfo->internal_crc = gba_header.checksum;
+      rominfo->internal_crc2[0] = 0;
+    }
+
+  rominfo->console_usage = gba_usage[0].help;
+  // We use fal_usage, but we could just as well use f2a_usage
+  rominfo->copier_usage = (!rominfo->buheader_len ? fal_usage[0].help : unknown_usage[0].help);
+
+  return result;
+}
+
+
+int
+gba_chksum (void)
+// Note that this function only calculates the checksum of the internal header
+{
+  unsigned char sum = 0x19, *ptr = (unsigned char *) &gba_header + 0xa0;
+
+  while (ptr < (unsigned char *) &gba_header + 0xbd)
+    sum += *ptr++;
+  sum = -sum;
+
+  return sum;
+}
+
+
+int
+gba_multi (int truncate_size, char *multi_fname)
+// TODO: Check if 1024 Mbit multi-game files are supported by the FAL code
+{
+#define BUFSIZE (32 * 1024)
+  int n, n_files, file_no, bytestowrite, byteswritten, totalsize = 0, done,
+      truncated = 0, size_pow2_lesser = 1, size_pow2 = 1, truncate_size_ispow2 = 0;
+  struct stat fstate;
+  FILE *srcfile, *destfile;
+  char buffer[BUFSIZE], fname[FILENAME_MAX], *fname_ptr;
+
+  if (truncate_size == 0)
+    {
+      fprintf (stderr, "ERROR: Can't make multi-game file of 0 bytes\n");
+      return -1;
+    }
+
+#if 0
+  if (truncate_size != 64 * MBIT && truncate_size != 128 * MBIT &&
+      truncate_size != 256 * MBIT && truncate_size != 512 * MBIT &&
+      truncate_size != 1024 * MBIT)
+    {
+      fprintf (stderr, "ERROR: Truncate size must be 64, 128, 256, 512 or 1024\n");
+      return -1;
+    }
+#endif
+
+  if (multi_fname != NULL)                      // -xfalmulti
+    {
+      strcpy (fname, multi_fname);
+      n_files = ucon64.argc;
+    }
+  else                                          // -multi
+    {
+      strcpy (fname, ucon64.argv[ucon64.argc - 1]);
+      n_files = ucon64.argc - 1;
+    }
+
+  ucon64_file_handler (fname, NULL, OF_FORCE_BASENAME);
+  if ((destfile = fopen (fname, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], fname);
+      return -1;
+    }
+  printf ("Creating multi-game file for FAL(/F2A): %s\n", fname);
+
+  file_no = 0;
+  for (n = 1; n < n_files; n++)
+    {
+      if (access (ucon64.argv[n], F_OK))
+        continue;                               // "file" does not exist (option)
+      stat (ucon64.argv[n], &fstate);
+      if (!S_ISREG (fstate.st_mode))
+        continue;
+
+      if (file_no == 0)
+        {
+          if (multi_fname != NULL)              // -xfalmulti
+            {
+              get_property_fname (ucon64.configfile, "gbaloader", fname, "loader.bin");
+              if (access (fname, F_OK))
+                {
+                  fprintf (stderr, "ERROR: Cannot open loader binary (%s)\n", fname);
+                  return -1;
+                }
+              fname_ptr = fname;
+              // NOTE: loop counter is modified, because we have to insert
+              //       loader in the file list
+              n--;
+            }
+          else                                  // -multi
+            fname_ptr = ucon64.argv[n];
+
+          printf ("Loader: %s\n", fname_ptr);
+          if (fsizeof (fname_ptr) > 64 * 1024)
+            printf ("WARNING: Are you sure %s is a loader binary?\n", fname_ptr);
+        }
+      else
+        {
+          fname_ptr = ucon64.argv[n];
+          printf ("ROM%d: %s\n", file_no, fname_ptr);
+        }
+
+      if ((srcfile = fopen (fname_ptr, "rb")) == NULL)
+        {
+          fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], fname_ptr);
+          continue;
+        }
+      done = 0;
+      byteswritten = 0;                         // # of bytes written per file
+      while (!done)
+        {
+          bytestowrite = fread (buffer, 1, BUFSIZE, srcfile);
+          if (totalsize + bytestowrite > truncate_size)
+            {
+              bytestowrite = truncate_size - totalsize;
+              done = 1;
+              truncated = 1;
+              printf ("Output file is %d Mbit, truncating %s, skipping %d bytes\n",
+                      truncate_size / MBIT, fname_ptr,
+                      fsizeof (fname_ptr) - (byteswritten + bytestowrite));
+              // DON'T use fstate.st_size, because file could be compressed
+            }
+          totalsize += bytestowrite;
+          if (bytestowrite == 0)
+            done = 1;
+          fwrite (buffer, 1, bytestowrite, destfile);
+          byteswritten += bytestowrite;
+        }
+      fclose (srcfile);
+      if (truncated)
+        break;
+      file_no++;
+    }
+  fclose (destfile);
+
+  /*
+    Display a notification if a truncate size was specified that is not exactly
+    the size of one of the flash card sizes.
+  */
+  n = truncate_size;
+  while (n >>= 1)
+    size_pow2 <<= 1;
+  if (truncate_size == size_pow2)
+    truncate_size_ispow2 = 1;
+
+  n = totalsize - 1;
+  while (n >>= 1)
+    size_pow2_lesser <<= 1;
+
+  size_pow2 = size_pow2_lesser << 1;
+
+  if (totalsize > 64 * MBIT && !truncate_size_ispow2)
+    printf("\n"
+           "NOTE: This multi-game file can only be written to a card >= %d Mbit.\n"
+           "      Use -multi=%d to create a file truncated to %d Mbit.\n"
+           "      Current size is %.5f Mbit\n", // 5 digits to have 1 byte resolution
+           size_pow2 / MBIT, size_pow2_lesser / MBIT, size_pow2_lesser / MBIT,
+           totalsize / (float) MBIT);
+
+  return 0;
+}

+ 37 - 0
ucon64/2.0/src/console/gba.h

@@ -0,0 +1,37 @@
+/*
+gba.h - Game Boy Advance support for uCON64
+
+Copyright (c) 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef GBA_H
+#define GBA_H
+
+#define GBA_LOGODATA_LEN 156
+
+extern const st_getopt2_t gba_usage[];
+extern const unsigned char gba_logodata[];
+
+extern int gba_chk (st_rominfo_t *rominfo);
+extern int gba_crp (st_rominfo_t *rominfo, const char *value);
+extern int gba_init (st_rominfo_t *rominfo);
+extern int gba_logo (st_rominfo_t *rominfo);
+extern int gba_n (st_rominfo_t *rominfo, const char *name);
+extern int gba_sram (void);
+extern int gba_multi (int truncate_size, char *fname);
+
+#endif

+ 1728 - 0
ucon64/2.0/src/console/genesis.c

@@ -0,0 +1,1728 @@
+/*
+genesis.c - Sega Genesis/Mega Drive support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2002 - 2004 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+// NOTE: The people at Sega refer to their company as Sega in normal text (not as SEGA)
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef  HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include "misc/misc.h"
+#include "misc/chksum.h"
+#include "misc/file.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "console/genesis.h"
+#include "backup/mgd.h"
+#include "backup/smd.h"
+
+
+#define GENESIS_HEADER_START 256
+#define GENESIS_HEADER_LEN (sizeof (st_genesis_header_t))
+#define GENESIS_NAME_LEN 48
+
+static int genesis_chksum (unsigned char *rom_buffer);
+static unsigned char *load_rom (st_rominfo_t *rominfo, const char *name,
+                                unsigned char *rom_buffer);
+static int save_rom (st_rominfo_t *rominfo, const char *name,
+                     unsigned char **buffer, int size);
+
+
+const st_getopt2_t genesis_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Genesis/Sega Mega Drive/Sega CD/32X/Nomad"/*"1989/19XX/19XX Sega http://www.sega.com"*/,
+      NULL
+    },
+    {
+      "gen", 0, 0, UCON64_GEN,
+      NULL, "force recognition",
+      &ucon64_wf[WF_OBJ_GEN_SWITCH]
+    },
+    {
+      "int", 0, 0, UCON64_INT,
+      NULL, "force ROM is in interleaved format (SMD)",
+      &ucon64_wf[WF_OBJ_ALL_SWITCH]
+    },
+    {
+      "int2", 0, 0, UCON64_INT2,
+      NULL, "force ROM is in interleaved format 2 (MGD)",
+      &ucon64_wf[WF_OBJ_ALL_SWITCH]
+    },
+    {
+      "nint", 0, 0, UCON64_NINT,
+      NULL, "force ROM is not in interleaved format (BIN/RAW)",
+      &ucon64_wf[WF_OBJ_ALL_SWITCH]
+    },
+    {
+      "n", 1, 0, UCON64_N,
+      "NEW_NAME", "change foreign ROM name to NEW_NAME",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT]
+    },
+    {
+      "n2", 1, 0, UCON64_N2,
+      "NEW_NAME", "change Japanese ROM name to NEW_NAME",
+      &ucon64_wf[WF_OBJ_GEN_DEFAULT]
+    },
+    {
+      "smd", 0, 0, UCON64_SMD,
+      NULL, "convert to Super Magic Drive/SMD",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT]
+    },
+    {
+      "smds", 0, 0, UCON64_SMDS,
+      NULL, "convert emulator (*.srm) SRAM to Super Magic Drive/SMD",
+      NULL
+    },
+    {
+      "bin", 0, 0, UCON64_BIN,
+      NULL, "convert to Magicom/BIN/RAW",
+      &ucon64_wf[WF_OBJ_GEN_DEFAULT_NO_SPLIT]
+    },
+    {
+      "mgd", 0, 0, UCON64_MGD,
+      NULL, "convert to Multi Game*/MGD2/MGH",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT]
+    },
+#if 0
+    {
+      "gf", 0, 0, UCON64_GF,
+      NULL, "convert Sega CD country code to Europe; ROM=$CD_IMAGE",
+      NULL
+    },
+    {
+      "ga", 0, 0, UCON64_GA,
+      NULL, "convert Sega CD country code to U.S.A.; ROM=$CD_IMAGE",
+      NULL
+    },
+    {
+      "gym", 0, 0, UCON64_GYM,
+      NULL, "convert GYM (Genecyst) sound to WAV; " OPTION_LONG_S "rom=GYMFILE",
+      NULL
+    },
+    {
+      "cym", 0, 0, UCON64_CYM,
+      NULL, "convert CYM (Callus emulator) sound to WAV; " OPTION_LONG_S "rom=CYMFILE",
+      NULL
+    },
+#endif
+    {
+      "stp", 0, 0, UCON64_STP,
+      NULL, "convert SRAM from backup unit for use with an emulator\n"
+      OPTION_LONG_S "stp just strips the first 512 bytes",
+      NULL
+    },
+    {
+      "j", 0, 0, UCON64_J,
+      NULL, "join split ROM",
+      &ucon64_wf[WF_OBJ_ALL_INIT_PROBE]
+    },
+    {
+      "s", 0, 0, UCON64_S,
+      NULL, "split ROM; default part size is 8 Mb (4 Mb for SMD)",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT]
+    },
+    {
+      "ssize", 1, 0, UCON64_SSIZE,
+      "SIZE", "specify split part size in Mbit",
+      &ucon64_wf[WF_OBJ_ALL_SWITCH]
+    },
+    {
+      "f", 0, 0, UCON64_F,
+      NULL, "remove NTSC/PAL protection",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT]
+    },
+    {
+      "chk", 0, 0, UCON64_CHK,
+      NULL, "fix ROM checksum",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT]
+    },
+    {
+      "1991", 0, 0, UCON64_1991,
+      NULL, "fix old third party ROMs to work with consoles build after\n"
+      "October 1991 by inserting \"(C) SEGA\" and \"(C)SEGA\"",
+      &ucon64_wf[WF_OBJ_GEN_DEFAULT]
+    },
+    {
+      "multi", 1, 0, UCON64_MULTI,
+      "SIZE", "make multi-game file for use with MD-PRO flash card, truncated\n"
+      "to SIZE Mbit; file with loader must be specified first, then\n"
+      "all the ROMs, multi-game file to create last",
+      &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_STOP]
+    },
+    {
+      "region", 1, 0, UCON64_REGION,
+      "CODE", "enable region function; use with -multi\n"
+      "CODE=0 force NTSC/Japan for all games\n"
+      "CODE=1 force NTSC/U.S.A. for all games\n"
+      "CODE=2 force PAL for all games\n"
+      "CODE=x use whatever setting games expect",
+      &ucon64_wf[WF_OBJ_GEN_SWITCH]
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+const st_getopt2_t bin_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Magicom/BIN/RAW",
+      NULL
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+  };
+
+typedef struct st_genesis_header
+{
+  char pad[256];
+} st_genesis_header_t;
+
+static st_genesis_header_t genesis_header;
+static genesis_file_t type;
+static int genesis_rom_size, genesis_has_ram, genesis_tv_standard, genesis_japanese;
+
+
+genesis_file_t
+genesis_get_file_type (void)
+{
+  return type;
+}
+
+
+int
+genesis_smd (st_rominfo_t *rominfo)
+{
+  st_smd_header_t header;
+  char dest_name[FILENAME_MAX];
+  unsigned char *rom_buffer = NULL;
+
+  if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
+    return -1;
+
+  memset (&header, 0, SMD_HEADER_LEN);
+  header.size = genesis_rom_size / 16384;
+  header.id0 = 3;
+  header.id1 = 0xaa;
+  header.id2 = 0xbb;
+  header.type = 6;
+
+  strcpy (dest_name, ucon64.rom);
+  set_suffix (dest_name, ".smd");
+  ucon64_file_handler (dest_name, NULL, 0);
+
+  ucon64_fwrite (&header, 0, SMD_HEADER_LEN, dest_name, "wb");
+  smd_interleave (rom_buffer, genesis_rom_size);
+  ucon64_fwrite (rom_buffer, SMD_HEADER_LEN, genesis_rom_size, dest_name, "ab");
+
+  free (rom_buffer);
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+genesis_smds (void)
+{
+  char dest_name[FILENAME_MAX];
+  unsigned char buf[32768];
+  st_smd_header_t header;
+
+  memset (&header, 0, SMD_HEADER_LEN);
+  memset (&buf, 0, 32768);
+  ucon64_fread (buf, 0, ucon64.file_size, ucon64.rom);
+
+  header.id1 = 0xaa;
+  header.id2 = 0xbb;
+  header.type = 7;                              // SRAM file
+
+  strcpy (dest_name, ucon64.rom);
+  set_suffix (dest_name, ".sav");
+  ucon64_file_handler (dest_name, NULL, 0);
+
+  ucon64_fwrite (&header, 0, SMD_HEADER_LEN, dest_name, "wb");
+  smd_interleave (buf, 32768);                  // SMD SRAM files are interleaved (Are they? - dbjh)
+  ucon64_fwrite (buf, SMD_HEADER_LEN, 32768, dest_name, "ab");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+genesis_bin (st_rominfo_t *rominfo)
+{
+  char dest_name[FILENAME_MAX];
+  unsigned char *rom_buffer = NULL;
+
+  if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
+    return -1;
+
+  strcpy (dest_name, ucon64.rom);
+  set_suffix (dest_name, ".bin");
+  ucon64_file_handler (dest_name, NULL, 0);
+
+  ucon64_fwrite (rom_buffer, 0, genesis_rom_size, dest_name, "wb");
+
+  free (rom_buffer);
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+// see src/backup/mgd.h for the file naming scheme
+int
+genesis_mgd (st_rominfo_t *rominfo)
+{
+#define CC (const char)
+  unsigned char *rom_buffer = NULL;
+  char dest_name[FILENAME_MAX];
+#if 0 // TODO: We need more info about the Multi Game Hunter
+  int x, y;
+  char mgh[512], buf[FILENAME_MAX];
+  const char mghcharset[1024] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    /* ! */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    /* & */ 0x3c, 0x66, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x02,
+    /* ' */ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+    /* ( */ 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x00,
+    /* ) */ 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    /* , */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x08,
+    /* - */ 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+    /* . */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
+    /* / */ 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x00,
+    /* 0 */ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+    /* 1 */ 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
+    /* 2 */ 0x3c, 0x66, 0x06, 0x06, 0x7c, 0x60, 0x7e, 0x00,
+    /* 3 */ 0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00,
+    /* 4 */ 0x18, 0x38, 0x58, 0x7c, 0x18, 0x18, 0x3c, 0x00,
+    /* 5 */ 0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00,
+    /* 6 */ 0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00,
+    /* 7 */ 0x7e, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x00,
+    /* 8 */ 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00,
+    /* 9 */ 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00,
+    /* : */ 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00,
+    /* ; */ 0x00, 0x00, 0x18, 0x00, 0x18, 0x08, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    /* = */ 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    /* A */ 0x18, 0x3c, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x00,
+    /* B */ 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00,
+    /* C */ 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00,
+    /* D */ 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00,
+    /* E */ 0x7e, 0x60, 0x60, 0x7e, 0x60, 0x60, 0x7e, 0x00,
+    /* F */ 0x7e, 0x60, 0x60, 0x7e, 0x60, 0x60, 0x60, 0x00,
+    /* G */ 0x3e, 0x60, 0x60, 0x6e, 0x66, 0x66, 0x3e, 0x00,
+    /* H */ 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00,
+    /* I */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+    /* J */ 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00,
+    /* K */ 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00,
+    /* L */ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00,
+    /* M */ CC 0xc6, CC 0xee, CC 0xfe, CC 0xd6, CC 0xc6, CC 0xc6, CC 0xc6, 0x00,
+    /* N */ 0x66, 0x76, 0x7e, 0x7e, 0x6e, 0x66, 0x66, 0x00,
+    /* O */ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+    /* P */ 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00,
+    /* Q */ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x6e, 0x3e, 0x00,
+    /* R */ 0x7c, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x00,
+    /* S */ 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00,
+    /* T */ 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+    /* U */ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00,
+    /* V */ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x3c, 0x00,
+    /* W */ 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x2c, 0x00,
+    /* X */ 0x66, 0x66, 0x3c, 0x10, 0x3c, 0x66, 0x66, 0x00,
+    /* Y */ 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00,
+    /* Z */ 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    /* _ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    /* a */ 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
+    /* b */ 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x00,
+    /* c */ 0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x00,
+    /* d */ 0x06, 0x06, 0x6e, 0x66, 0x66, 0x66, 0x3e, 0x00,
+    /* e */ 0x00, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+    /* f */ 0x0c, 0x18, 0x18, 0x3c, 0x18, 0x18, 0x3c, 0x00,
+    /* g */ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x7c,
+    /* h */ 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x00,
+    /* i */ 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
+    /* j */ 0x00, 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0x6c, 0x38,
+    /* k */ 0x60, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0x00,
+    /* l */ 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
+    /* m */ 0x00, 0x00, 0x5c, 0x76, 0x56, 0x56, 0x56, 0x00,
+    /* n */ 0x00, 0x00, 0x6c, 0x7e, 0x66, 0x66, 0x66, 0x00,
+    /* o */ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00,
+    /* p */ 0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60,
+    /* q */ 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x06,
+    /* r */ 0x00, 0x00, 0x18, 0x1a, 0x18, 0x18, 0x3c, 0x00,
+    /* s */ 0x00, 0x00, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x00,
+    /* t */ 0x00, 0x18, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00,
+    /* u */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3a, 0x00,
+    /* v */ 0x00, 0x00, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x00,
+    /* w */ 0x00, 0x00, 0x56, 0x56, 0x56, 0x56, 0x7e, 0x00,
+    /* x */ 0x00, 0x00, 0x66, 0x66, 0x18, 0x66, 0x66, 0x00,
+    /* y */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x7c,
+    /* z */ 0x00, 0x00, 0x7e, 0x0c, 0x18, 0x30, 0x7e, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    /* | */ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  };
+#endif
+
+  // pad the file to the next valid MGD2 size if it doesn't have a valid size
+  if (genesis_rom_size <= 1 * MBIT)
+    genesis_rom_size = 1 * MBIT;
+  else if (genesis_rom_size <= 2 * MBIT)
+    genesis_rom_size = 2 * MBIT;
+  else if (genesis_rom_size <= 4 * MBIT)
+    genesis_rom_size = 4 * MBIT;
+  else if (genesis_rom_size <= 8 * MBIT)
+    genesis_rom_size = 8 * MBIT;
+  else if (genesis_rom_size <= 16 * MBIT)
+    genesis_rom_size = 16 * MBIT;
+  else if (genesis_rom_size <= 20 * MBIT)
+    genesis_rom_size = 20 * MBIT;
+  else if (genesis_rom_size <= 24 * MBIT)
+    genesis_rom_size = 24 * MBIT;
+  else
+    genesis_rom_size = 32 * MBIT;
+
+  if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
+    return -1;
+
+  mgd_make_name (ucon64.rom, UCON64_GEN, genesis_rom_size, dest_name);
+  ucon64_file_handler (dest_name, NULL, OF_FORCE_BASENAME);
+
+  mgd_interleave (&rom_buffer, genesis_rom_size);
+  ucon64_fwrite (rom_buffer, 0, genesis_rom_size, dest_name, "wb");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  free (rom_buffer);
+
+#if 0 // TODO: We need more info about the Multi Game Hunter (and a new option)
+  // automatically create MGH name file
+  memset (mgh, 0, sizeof (mgh));
+  mgh[0] = 'M';
+  mgh[1] = 'G';
+  mgh[2] = 'H';
+  mgh[3] = 0x1a;
+  mgh[4] = 0x06;
+  mgh[5] = (char) 0xf0;
+  mgh[31] = (char) 0xff;
+
+  // In addition to the above, uCON also does "memcpy (mgh + 16, "MGH By uCON/chp", 15);"
+
+  memcpy (buf, rominfo->name, 15);              // copy first 15 bytes (don't use strlen() or strcpy())
+  for (x = 0; x < 15; x++)
+    {
+      for (y = 0; y < 4; y++)
+        mgh[((x + 2) * 16) + y + 4] = mghcharset[(((unsigned char *) buf)[x] * 8) + y];
+      for (y = 4; y < 8; y++)
+        mgh[((x + 2) * 16) + y + 244] = mghcharset[(((unsigned char *) buf)[x] * 8) + y];
+    }
+
+  set_suffix (dest_name, ".mgh");
+  ucon64_output_fname (dest_name, OF_FORCE_BASENAME);
+  /*
+    If a backup would be created it would overwrite the backup of the ROM. The
+    ROM backup is more important, so we don't write a backup of the MGH file.
+  */
+  ucon64_fwrite (mgh, 0, sizeof (mgh), dest_name, "wb");
+#endif
+  mgd_write_index_file ((char *) basename2 (dest_name), 1);
+
+  return 0;
+}
+
+
+int
+genesis_s (st_rominfo_t *rominfo)
+{
+  st_smd_header_t smd_header;
+  char dest_name[FILENAME_MAX], *p;
+  int x, nparts, surplus, size = ucon64.file_size - rominfo->buheader_len,
+      part_size;
+
+  if (UCON64_ISSET (ucon64.part_size))
+    {
+      part_size = ucon64.part_size;
+      // Don't allow too small part sizes, see src/console/snes.c (snes_s())
+      if (part_size < 4 * MBIT)
+        {
+          fprintf (stderr,
+            "ERROR: Split part size must be larger than or equal to 4 Mbit\n");
+          return -1;
+        }
+    }
+  else
+    {
+      if (type == SMD)
+        part_size = 4 * MBIT;                   // SMD uses 4 Mb parts
+      else
+        part_size = 8 * MBIT;                   // MGD and Magicom ("BIN") use
+    }                                           //  8 Mb parts
+
+  if (size <= part_size)
+    {
+      printf (
+        "NOTE: ROM size is smaller than or equal to %d Mbit -- won't be split\n",
+        part_size / MBIT);
+      return -1;
+    }
+
+  nparts = size / part_size;
+  surplus = size % part_size;
+
+  if (type == SMD)
+    {
+      ucon64_fread (&smd_header, 0, SMD_HEADER_LEN, ucon64.rom);
+
+      strcpy (dest_name, ucon64.rom);
+      set_suffix (dest_name, ".1");
+      ucon64_output_fname (dest_name, 0);
+      p = strrchr (dest_name, '.') + 1;
+
+      smd_header.size = part_size / 16384;
+      smd_header.id0 = 3;
+      // if smd_header.split bit 6 == 0 -> last file of the ROM
+      smd_header.split |= 0x40;
+      for (x = 0; x < nparts; x++)
+        {
+          if (surplus == 0 && x == nparts - 1)
+            smd_header.split &= ~0x40;          // last file -> clear bit 6
+
+          // don't write backups of parts, because one name is used
+          ucon64_fwrite (&smd_header, 0, SMD_HEADER_LEN, dest_name, "wb");
+          fcopy (ucon64.rom, x * part_size + rominfo->buheader_len, part_size, dest_name, "ab");
+          printf (ucon64_msg[WROTE], dest_name);
+
+          (*p)++;
+        }
+
+      if (surplus != 0)
+        {
+          smd_header.size = surplus / 16384;
+          smd_header.split &= ~0x40;            // last file -> clear bit 6
+
+          // don't write backups of parts, because one name is used
+          ucon64_fwrite (&smd_header, 0, SMD_HEADER_LEN, dest_name, "wb");
+          fcopy (ucon64.rom, x * part_size + rominfo->buheader_len, surplus, dest_name, "ab");
+          printf (ucon64_msg[WROTE], dest_name);
+        }
+    }
+  else if (type == MGD_GEN)
+    {
+      char suffix[5], *p_index, name[9], *names[16], names_mem[16][9];
+      int n, offset, size, name_i = 0;
+
+      for (n = 0; n < 16; n++)
+        names[n] = names_mem[n];
+
+      mgd_make_name (ucon64.rom, UCON64_GEN, genesis_rom_size, dest_name);
+      strcpy (suffix, (char *) get_suffix (dest_name));
+      if ((p = strchr (dest_name, '.')))
+        *p = 0;
+      n = strlen (dest_name);
+      if (n > 7)
+        n = 7;
+      dest_name[n] = 'A';
+      dest_name[n + 1] = 0;
+
+      strcpy (name, dest_name);
+      p_index = &name[n];
+
+      strcat (dest_name, suffix);
+      ucon64_output_fname (dest_name, OF_FORCE_BASENAME);
+      p = strrchr (dest_name, '.') - 1;
+
+      if (surplus)
+        nparts++;
+      for (x = 0; x < nparts; x++)
+        {
+          offset = x * (part_size / 2);
+          size = part_size / 2;
+          if (offset + size > ucon64.file_size / 2)
+            size = ucon64.file_size / 2 - offset;
+          // don't write backups of parts, because one name is used
+          // write first half of file
+          fcopy (ucon64.rom, offset, size, dest_name, "wb");
+          // write second half of file; don't do: "(nparts / 2) * part_size"!
+          fcopy (ucon64.rom, ucon64.file_size / 2 + offset, size, dest_name, "ab");
+          printf (ucon64_msg[WROTE], dest_name);
+
+          (*p)++;
+
+          strcpy (names[name_i], name);
+          name_i++;
+          (*p_index)++;
+        }
+      mgd_write_index_file (names, name_i);
+    }
+  else // type == BIN
+    {
+      strcpy (dest_name, ucon64.rom);
+      set_suffix (dest_name, ".r01");
+      ucon64_output_fname (dest_name, 0);
+      p = strrchr (dest_name, '.') + 3;
+
+      for (x = 0; x < nparts; x++)
+        {
+          if (surplus == 0 && x == nparts - 1)
+            *p = '0';                           // last file should have suffix ".r00"
+          fcopy (ucon64.rom, x * part_size + rominfo->buheader_len, part_size, dest_name, "wb");
+          printf (ucon64_msg[WROTE], dest_name);
+          (*p)++;
+        }
+
+      if (surplus != 0)
+        {
+          *p = '0';
+          fcopy (ucon64.rom, x * part_size + rominfo->buheader_len, surplus, dest_name, "wb");
+          printf (ucon64_msg[WROTE], dest_name);
+        }
+    }
+
+  return 0;
+}
+
+
+int
+genesis_j (st_rominfo_t *rominfo)
+{
+  char src_name[FILENAME_MAX], dest_name[FILENAME_MAX], *p;
+  int block_size, total_size = 0;
+
+  if (type == SMD)
+    {
+      unsigned char buf[3];
+
+      strcpy (dest_name, ucon64.rom);
+      set_suffix (dest_name, ".smd");
+      ucon64_file_handler (dest_name, NULL, 0);
+
+      strcpy (src_name, ucon64.rom);
+      p = strrchr (src_name, '.') + 1;
+      fcopy (src_name, 0, rominfo->buheader_len, dest_name, "wb");
+      block_size = ucon64.file_size - rominfo->buheader_len;
+      while (fcopy (src_name, rominfo->buheader_len, block_size, dest_name, "ab") != -1)
+        {
+          printf ("Joined: %s\n", src_name);
+          total_size += block_size;
+          (*p)++;
+          block_size = fsizeof (src_name) - rominfo->buheader_len;
+        }
+
+      if (rominfo->buheader_len)
+        {                                       // fix header
+          buf[0] = total_size / 16384;          // # 16 kB blocks
+          buf[1] = 3;                           // ID 0
+          buf[2] = 0;                           // last file -> clear bit 6
+          ucon64_fwrite (buf, 0, 3, dest_name, "r+b");
+          buf[0] = 0xaa;                        // ID 1
+          buf[1] = 0xbb;                        // ID 2
+          buf[2] = 6;                           // type Genesis
+          ucon64_fwrite (buf, 8, 3, dest_name, "r+b");
+        }
+
+      printf (ucon64_msg[WROTE], dest_name);
+    }
+  else if (type == MGD_GEN)
+    {
+      /*
+        file1 file2 file3 file4
+        1/2   3/4   5/6   7/8 (1st half/2nd half)
+        joined file
+        1/3/5/7/2/4/6/8
+      */
+      strcpy (dest_name, ucon64.rom);
+      set_suffix (dest_name, ".tmp");           // users should use -mgd to get
+      ucon64_file_handler (dest_name, NULL, 0); //  a correct name
+      remove (dest_name);
+
+      strcpy (src_name, ucon64.rom);
+      p = strrchr (src_name, '.') - 1;
+      block_size = (ucon64.file_size - rominfo->buheader_len) / 2;
+      while (fcopy (src_name, rominfo->buheader_len, block_size, dest_name, "ab") != -1)
+        {
+          (*p)++;
+          // BUG ALERT: Assume all parts have the same header length
+          block_size = (fsizeof (src_name) - rominfo->buheader_len) / 2;
+        }
+
+      strcpy (src_name, ucon64.rom);
+      p = strrchr (src_name, '.') - 1;
+      block_size = (ucon64.file_size - rominfo->buheader_len) / 2;
+      while (fcopy (src_name, rominfo->buheader_len + block_size,
+             block_size, dest_name, "ab") != -1)
+        {
+          printf ("Joined: %s\n", src_name);    // print this here, not in the
+          (*p)++;                               //  previous loop
+          // BUG ALERT: Assume all parts have the same header length
+          block_size = (fsizeof (src_name) - rominfo->buheader_len) / 2;
+        }
+
+      printf (ucon64_msg[WROTE], dest_name);
+    }
+  else if (type == BIN)
+    {
+      int tried_r00 = 0;
+
+      strcpy (dest_name, ucon64.rom);
+      set_suffix (dest_name, ".bin");
+      ucon64_file_handler (dest_name, NULL, 0);
+      remove (dest_name);
+
+      strcpy (src_name, ucon64.rom);
+      p = strrchr (src_name, '.') + 3;
+      *(p - 1) = '0';                           // be user friendly and avoid confusion
+      *p = '1';                                 //  (.r01 is first file, not .r00)
+      block_size = ucon64.file_size - rominfo->buheader_len;
+      while (fcopy (src_name, rominfo->buheader_len, block_size, dest_name, "ab") != -1)
+        {
+          printf ("Joined: %s\n", src_name);
+          if (tried_r00)
+            break;                              // quit after joining last file
+          (*p)++;
+          if (!tried_r00 && access (src_name, F_OK) != 0)
+            {                                   // file does not exist -> try .r00
+              *p = '0';
+              tried_r00 = 1;
+            }
+          block_size = fsizeof (src_name) - rominfo->buheader_len;
+        }
+
+      printf (ucon64_msg[WROTE], dest_name);
+    }
+
+  return 0;
+}
+
+
+static int
+genesis_name (st_rominfo_t *rominfo, const char *name1, const char *name2)
+{
+  unsigned char *rom_buffer = NULL;
+  char buf[FILENAME_MAX];
+
+  if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
+    return -1;
+
+  if (name1)
+    {
+      memset (buf, ' ', GENESIS_NAME_LEN);
+      strncpy (buf, name1, strlen (name1) > GENESIS_NAME_LEN ?
+        GENESIS_NAME_LEN : strlen (name1));
+      memcpy (&rom_buffer[GENESIS_HEADER_START + 32 + GENESIS_NAME_LEN], buf,
+        GENESIS_NAME_LEN);
+    }
+  if (name2)
+    {
+      memset (buf, ' ', GENESIS_NAME_LEN);
+      strncpy (buf, name2, strlen (name2) > GENESIS_NAME_LEN ?
+        GENESIS_NAME_LEN : strlen (name2));
+      memcpy (&rom_buffer[GENESIS_HEADER_START + 32], buf, GENESIS_NAME_LEN);
+    }
+
+  strcpy (buf, ucon64.rom);
+  ucon64_file_handler (buf, NULL, 0);
+  save_rom (rominfo, buf, &rom_buffer, genesis_rom_size);
+
+  free (rom_buffer);
+  printf (ucon64_msg[WROTE], buf);
+  return 0;
+}
+
+
+int
+genesis_n (st_rominfo_t *rominfo, const char *name)
+{
+  return genesis_name (rominfo, name, NULL);
+}
+
+
+int
+genesis_n2 (st_rominfo_t *rominfo, const char *name)
+{
+  return genesis_name (rominfo, NULL, name);
+}
+
+
+int
+genesis_1991 (st_rominfo_t *rominfo)
+{
+  return genesis_name (rominfo, "(C)SEGA", "(C) SEGA");
+}
+
+
+int
+genesis_chk (st_rominfo_t *rominfo)
+{
+  unsigned char *rom_buffer = NULL;
+  char dest_name[FILENAME_MAX];
+
+  if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
+    return -1;
+
+  rom_buffer[GENESIS_HEADER_START + 143] = rominfo->current_internal_crc;      // low byte of checksum
+  rom_buffer[GENESIS_HEADER_START + 142] = rominfo->current_internal_crc >> 8; // high byte of checksum
+
+  dumper (stdout, &rom_buffer[GENESIS_HEADER_START + 0x8e], 2, GENESIS_HEADER_START + 0x8e, DUMPER_HEX);
+
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+  save_rom (rominfo, dest_name, &rom_buffer, genesis_rom_size);
+
+  free (rom_buffer);
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+static int
+genesis_fix_pal_protection (st_rominfo_t *rominfo)
+/*
+  This function searches for PAL protection codes. If it finds one it will
+  fix the code so that the game will run on a Genesis (NTSC).
+*/
+{
+  char fname[FILENAME_MAX];
+  unsigned char *rom_buffer = NULL;
+  int offset = 0, block_size, n = 0, n_extra_patterns, n2;
+  st_cm_pattern_t *patterns = NULL;
+
+  strcpy (fname, "genpal.txt");
+  // First try the current directory, then the configuration directory
+  if (access (fname, F_OK | R_OK) == -1)
+    sprintf (fname, "%s" FILE_SEPARATOR_S "genpal.txt", ucon64.configdir);
+  n_extra_patterns = build_cm_patterns (&patterns, fname, ucon64.quiet == -1 ? 1 : 0);
+  if (n_extra_patterns >= 0)
+    printf ("Found %d additional code%s in %s\n",
+            n_extra_patterns, n_extra_patterns != 1 ? "s" : "", fname);
+
+  puts ("Attempting to fix PAL protection code...");
+
+  if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
+    return -1;
+
+  while ((n2 = ucon64.file_size - offset) > 0)
+    {
+      block_size = n2 >= 16 * 1024 ? 16 * 1024 : n2;
+      for (n2 = 0; n2 < n_extra_patterns; n2++)
+        n += change_mem2 ((char *) rom_buffer + offset, block_size,
+                          patterns[n2].search,
+                          patterns[n2].search_size,
+                          patterns[n2].wildcard,
+                          patterns[n2].escape,
+                          patterns[n2].replace,
+                          patterns[n2].replace_size,
+                          patterns[n2].offset,
+                          patterns[n2].sets);
+      offset += 16 * 1024;
+    }
+  cleanup_cm_patterns (&patterns, n_extra_patterns);
+
+  strcpy (fname, ucon64.rom);
+  ucon64_file_handler (fname, NULL, 0);
+  save_rom (rominfo, fname, &rom_buffer, genesis_rom_size);
+
+  free (rom_buffer);
+  printf ("Found %d pattern%s\n", n, n != 1 ? "s" : "");
+  printf (ucon64_msg[WROTE], fname);
+  return n;
+}
+
+
+static int
+genesis_fix_ntsc_protection (st_rominfo_t *rominfo)
+/*
+  This function searches for NTSC protection codes. If it finds one it will
+  fix the code so that the game will run on a Mega Drive (PAL).
+*/
+{
+  char fname[FILENAME_MAX];
+  unsigned char *rom_buffer = NULL;
+  int offset = 0, block_size, n = 0, n_extra_patterns, n2;
+  st_cm_pattern_t *patterns = NULL;
+
+  strcpy (fname, "mdntsc.txt");
+  // First try the current directory, then the configuration directory
+  if (access (fname, F_OK | R_OK) == -1)
+    sprintf (fname, "%s" FILE_SEPARATOR_S "mdntsc.txt", ucon64.configdir);
+  n_extra_patterns = build_cm_patterns (&patterns, fname, ucon64.quiet == -1 ? 1 : 0);
+  if (n_extra_patterns >= 0)
+    printf ("Found %d additional code%s in %s\n",
+            n_extra_patterns, n_extra_patterns != 1 ? "s" : "", fname);
+
+  puts ("Attempting to fix NTSC protection code...");
+
+  if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
+    return -1;
+
+  while ((n2 = ucon64.file_size - offset) > 0)
+    {
+      block_size = n2 >= 16 * 1024 ? 16 * 1024 : n2;
+      for (n2 = 0; n2 < n_extra_patterns; n2++)
+        n += change_mem2 ((char *) rom_buffer + offset, block_size,
+                          patterns[n2].search,
+                          patterns[n2].search_size,
+                          patterns[n2].wildcard,
+                          patterns[n2].escape,
+                          patterns[n2].replace,
+                          patterns[n2].replace_size,
+                          patterns[n2].offset,
+                          patterns[n2].sets);
+      offset += 16 * 1024;
+    }
+  cleanup_cm_patterns (&patterns, n_extra_patterns);
+
+  strcpy (fname, ucon64.rom);
+  ucon64_file_handler (fname, NULL, 0);
+  save_rom (rominfo, fname, &rom_buffer, genesis_rom_size);
+
+  free (rom_buffer);
+  printf ("Found %d pattern%s\n", n, n != 1 ? "s" : "");
+  printf (ucon64_msg[WROTE], fname);
+  return n;
+}
+
+
+int
+genesis_f (st_rominfo_t *rominfo)
+{
+  /*
+    In the Philipines the television standard is NTSC, but do games made
+    for the Philipines exist?
+    Just like with SNES we don't guarantee anything for files that needn't be
+    fixed/cracked/patched.
+  */
+  if (genesis_tv_standard == 0)               // NTSC (Japan, U.S.A. or Brazil ('4'))
+    return genesis_fix_ntsc_protection (rominfo);
+  else
+    return genesis_fix_pal_protection (rominfo);
+}
+
+
+unsigned char *
+load_rom (st_rominfo_t *rominfo, const char *name, unsigned char *rom_buffer)
+{
+  FILE *file;
+  int bytesread;
+
+  if ((file = fopen (name, "rb")) == NULL)
+    return NULL;
+  if (!(rom_buffer = (unsigned char *) malloc (genesis_rom_size)))
+    {
+      fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], genesis_rom_size);
+      fclose (file);
+      return NULL;
+    }
+  fseek (file, rominfo->buheader_len, SEEK_SET); // don't do this only for SMD!
+  bytesread = fread (rom_buffer, 1, genesis_rom_size, file);
+  if (bytesread < genesis_rom_size)
+    memset (rom_buffer + bytesread, 0, genesis_rom_size - bytesread);
+
+  if (type != BIN)
+    {
+      if (ucon64.fcrc32 == 0)
+        ucon64.fcrc32 = crc32 (ucon64.fcrc32, rom_buffer, genesis_rom_size);
+
+      if (type == SMD)
+        smd_deinterleave (rom_buffer, bytesread);
+      else // type == MGD_GEN
+        mgd_deinterleave (&rom_buffer, bytesread, genesis_rom_size);
+    }
+
+  if (ucon64.crc32 == 0)                        // calculate the CRC32 only once
+    ucon64.crc32 = crc32 (0, rom_buffer, bytesread);
+
+  fclose (file);
+  return rom_buffer;
+}
+
+
+int
+save_rom (st_rominfo_t *rominfo, const char *name, unsigned char **buffer, int size)
+{
+  if (type == SMD)
+    {
+      int n;
+
+      /*
+        Copy the complete backup unit header length, no matter how strange or
+        how large the size. fcopy() doesn't copy if source and destination are
+        one and the same. Copying is necessary if source and destination are
+        different so that we can use "r+b" when writing the contents of buffer
+        to disk.
+      */
+      fcopy (ucon64.rom, 0, rominfo->buheader_len, name, "wb");
+
+      smd_interleave (*buffer, size);
+      n = ucon64_fwrite (*buffer, rominfo->buheader_len, size, name, "r+b");
+      if (size < (int) (ucon64.file_size - rominfo->buheader_len))
+        truncate2 (name, rominfo->buheader_len + size);
+      return n;
+    }
+  else if (type == MGD_GEN)
+    {
+      mgd_interleave (buffer, size);            // allocates new buffer
+      return ucon64_fwrite (*buffer, 0, size, name, "wb");
+    }
+  else // type == BIN
+    return ucon64_fwrite (*buffer, 0, size, name, "wb");
+}
+
+
+static void
+write_game_table_entry (FILE *destfile, int file_no, st_rominfo_t *rominfo,
+                        int totalsize)
+{
+  static int sram_page = 0, file_no_sram = 0;
+  int n;
+  unsigned char name[0x1c], flags = 0; // SRAM/region flags: F, D (reserved), E, P, V, T, S1, S0
+
+  fseek (destfile, 0x8000 + (file_no - 1) * 0x20, SEEK_SET);
+  fputc (0xff, destfile);                       // 0x0 = 0xff
+  memcpy (name, rominfo->name, 0x1c);
+  for (n = 0; n < 0x1c; n++)
+    {
+      if (!isprint ((int) name[n]))
+        name[n] = '.';
+      else
+        name[n] = toupper (name[n]);            // according to Leo, MDPACKU4.BIN
+    }                                           //  only supports upper case characters
+  fwrite (name, 1, 0x1c, destfile);             // 0x1 - 0x1c = name
+  fputc (0, destfile);                          // 0x1d = 0
+  fputc (totalsize / (2 * MBIT), destfile);     // 0x1e = bank code
+
+  flags = 0x80;                                 // set F (?, default)
+  if (genesis_has_ram)
+    {
+      if (sram_page == 3)
+        file_no_sram = file_no;
+      else if (sram_page > 3)
+        {
+          printf ("WARNING: This ROM will share SRAM with ROM %d\n", file_no_sram);
+          sram_page = 3;
+        }
+      flags |= sram_page++;
+      if ((ucon64.file_size - rominfo->buheader_len) <= 16 * MBIT)
+        flags &= ~0x80;                         // clear F (<=16 Mb & SRAM)
+    }
+  else
+    flags |= 4;                                 // set T (no SRAM)
+
+  /*
+    J(apan) would probably be a more logical name for bit E(urope). I (dbjh)
+    base that on information of SamIAm. According to him there are three types
+    of the console:
+    American Genesis                  NTSC/no Japanese switch
+    Japanese Genesis                  NTSC/Japanese switch
+    Mega Drive (rest of the world?)   PAL/no Japanese switch
+    So, besides PAL and NTSC there is the variable whether the console contains
+    a Japanese "switch". The region function is used to make the game "think"
+    it's running on another type of console. For example, a Japanese game
+    running on a (European) Mega Drive should have the P and E bit set to 0.
+  */
+  if (UCON64_ISSET (ucon64.region))
+    {
+      if (!genesis_japanese)
+        flags |= 0x20;                          // set E(urope)
+      if (genesis_tv_standard == 1)
+        flags |= 0x10;                          // set P(AL)
+
+      flags |= 8;                               // set V (enable region function)
+    }
+
+  fputc (flags, destfile);                      // 0x1f = flags
+}
+
+
+int
+genesis_multi (int truncate_size, char *fname)
+{
+#define BUFSIZE (32 * 1024)                     // must be a multiple of 16 kB
+  int n, n_files, file_no, bytestowrite, byteswritten, totalsize = 0, done,
+      truncated = 0, paddedsize, org_do_not_calc_crc = ucon64.do_not_calc_crc;
+  struct stat fstate;
+  FILE *srcfile, *destfile;
+  char destname[FILENAME_MAX];
+  unsigned char buffer[BUFSIZE];
+
+  if (truncate_size == 0)
+    {
+      fprintf (stderr, "ERROR: Can't make multi-game file of 0 bytes\n");
+      return -1;
+    }
+
+  if (fname != NULL)
+    {
+      strcpy(destname, fname);
+      n_files = ucon64.argc;
+    }
+  else
+    {
+      strcpy(destname, ucon64.argv[ucon64.argc - 1]);
+      n_files = ucon64.argc - 1;
+    }
+
+  ucon64_file_handler (destname, NULL, OF_FORCE_BASENAME);
+  if ((destfile = fopen (destname, "wb")) == NULL)
+    {
+      fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], destname);
+      return -1;
+    }
+
+  // do this check here, because one error message is enough (not for every game)
+  if (UCON64_ISSET (ucon64.region))
+    switch (ucon64.region)
+      {
+      case 0:                                   // NTSC/Japan
+      case 1:                                   // NTSC/U.S.A.
+      case 2:                                   // PAL
+      case 256:
+        break;
+      default:
+        printf ("WARNING: Invalid region code specified, using values games expect\n");
+      }
+
+  printf ("Creating multi-game file for MD-PRO: %s\n", destname);
+
+  file_no = 0;
+  for (n = 1; n < n_files; n++)
+    {
+      if (access (ucon64.argv[n], F_OK))
+        continue;                               // "file" does not exist (option)
+      stat (ucon64.argv[n], &fstate);
+      if (!S_ISREG (fstate.st_mode))
+        continue;
+      if (file_no == 32)                        // loader + 31 games
+        {
+          printf ("WARNING: A multi-game file can contain a maximum of 31 games. The other files\n"
+                  "         are ignored.\n");
+          break;
+        }
+
+      ucon64.console = UCON64_UNKNOWN;
+      ucon64.rom = ucon64.argv[n];
+      ucon64.file_size = fsizeof (ucon64.rom);
+      // DON'T use fstate.st_size, because file could be compressed
+      ucon64.rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ?
+                                       ucon64.buheader_len : 0;
+      ucon64.rominfo->interleaved = UCON64_ISSET (ucon64.interleaved) ?
+                                       ucon64.interleaved : 0;
+      ucon64.do_not_calc_crc = 1;
+      if (genesis_init (ucon64.rominfo) != 0)
+        printf ("WARNING: %s does not appear to be a Genesis ROM\n", ucon64.rom);
+      /*
+        NOTE: This is NOT the place to mess with ucon64.console. When this
+              function is entered ucon64.console must have been UCON64_GEN. We
+              modify ucon64.console temporarily only to be able to help detect
+              problems with incorrect files.
+      */
+      if (UCON64_ISSET (ucon64.region))
+        switch (ucon64.region)
+          {
+          case 0:                               // NTSC/Japan
+            genesis_tv_standard = 0;
+            genesis_japanese = 1;
+            break;
+          case 1:                               // NTSC/U.S.A.
+            genesis_tv_standard = 0;
+            genesis_japanese = 0;
+            break;
+          case 2:                               // PAL
+            genesis_tv_standard = 1;
+            genesis_japanese = 0;
+            break;
+          case 256:
+            // Do nothing. Use whatever values we found for genesis_tv_standard and
+            //  genesis_japanese.
+            break;
+          // no default case, because we already checked value of ucon64.region
+          }
+
+      if ((srcfile = fopen (ucon64.rom, "rb")) == NULL)
+        {
+          fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ucon64.rom);
+          continue;
+        }
+      if (ucon64.rominfo->buheader_len)
+        fseek (srcfile, ucon64.rominfo->buheader_len, SEEK_SET);
+
+      if (file_no == 0)
+        {
+          printf ("Loader: %s\n", ucon64.rom);
+          if (ucon64.file_size - ucon64.rominfo->buheader_len != MBIT)
+            printf ("WARNING: Are you sure %s is a loader binary?\n", ucon64.rom);
+        }
+      else
+        {
+          printf ("ROM%d: %s\n", file_no, ucon64.rom);
+          write_game_table_entry (destfile, file_no, ucon64.rominfo, totalsize);
+          fseek (destfile, totalsize, SEEK_SET); // restore file pointer
+        }
+      file_no++;
+
+      done = 0;
+      byteswritten = 0;                         // # of bytes written per file
+      while (!done)
+        {
+          if (ucon64.rominfo->interleaved == 2)
+            bytestowrite = fread_mgd (buffer, 1, BUFSIZE, srcfile);
+          else
+            {
+              bytestowrite = fread (buffer, 1, BUFSIZE, srcfile);
+              if (ucon64.rominfo->interleaved)
+                smd_deinterleave (buffer, BUFSIZE);
+                // yes, BUFSIZE. bytestowrite might not be n * 16 kB
+            }
+          if (totalsize + bytestowrite > truncate_size)
+            {
+              bytestowrite = truncate_size - totalsize;
+              done = 1;
+              truncated = 1;
+              printf ("Output file is %d Mbit, truncating %s, skipping %d bytes\n",
+                      truncate_size / MBIT, ucon64.rom, ucon64.file_size -
+                        ucon64.rominfo->buheader_len - (byteswritten + bytestowrite));
+            }
+          totalsize += bytestowrite;
+          if (bytestowrite == 0)
+            done = 1;
+          fwrite (buffer, 1, bytestowrite, destfile);
+          byteswritten += bytestowrite;
+        }
+      fclose (srcfile);
+      if (truncated)
+        break;
+
+      // games have to be aligned to (start at) a 2 Mbit boundary
+      paddedsize = (totalsize + 2 * MBIT - 1) & ~(2 * MBIT - 1);
+//      printf ("paddedsize: %d (%f); totalsize: %d (%f)\n",
+//              paddedsize, paddedsize / (1.0 * MBIT), totalsize, totalsize / (1.0 * MBIT));
+      if (paddedsize > totalsize)
+        {
+          // I (dbjh) don't think it is really necessary to pad to the truncate
+          //  size, but it won't hurt
+          if (paddedsize > truncate_size)
+            {
+              truncated = 1;                    // not *really* truncated
+              paddedsize = truncate_size;
+            }
+
+          memset (buffer, 0, BUFSIZE);
+          while (totalsize < paddedsize)
+            {
+              bytestowrite = paddedsize - totalsize > BUFSIZE ?
+                              BUFSIZE : paddedsize - totalsize;
+              fwrite (buffer, 1, bytestowrite, destfile);
+              totalsize += bytestowrite;
+            }
+        }
+      if (truncated)
+        break;
+    }
+  // fill the next game table entry
+  fseek (destfile, 0x8000 + (file_no - 1) * 0x20, SEEK_SET);
+  fputc (0, destfile);                          // indicate no next game
+  fclose (destfile);
+  ucon64.console = UCON64_GEN;
+  ucon64.do_not_calc_crc = org_do_not_calc_crc;
+
+  return 0;
+}
+
+
+static int
+genesis_testinterleaved (st_rominfo_t *rominfo)
+{
+  unsigned char buf[16384] = { 0 };
+
+  ucon64_fread (buf, rominfo->buheader_len, 8192 + (GENESIS_HEADER_START + 4) / 2, ucon64.rom);
+  if (!memcmp (buf + GENESIS_HEADER_START, "SEGA", 4))
+    return 0;
+
+  smd_deinterleave (buf, 16384);
+  if (!memcmp (buf + GENESIS_HEADER_START, "SEGA", 4))
+    return 1;
+
+  q_fread_mgd (buf, rominfo->buheader_len + GENESIS_HEADER_START, 4, ucon64.rom);
+  if (!memcmp (buf, "SEGA", 4))
+    return 2;
+
+  return 0;                                     // unknown, act as if it's BIN
+}
+
+
+int
+genesis_init (st_rominfo_t *rominfo)
+{
+  int result = -1, value = 0, x, y;
+  unsigned char *rom_buffer = NULL, buf[MAXBUFSIZE], name[GENESIS_NAME_LEN + 1],
+                smd_header_split;
+  static char maker[9], country[200]; // 200 characters should be enough for 5 country names
+  static const char *genesis_maker[0x100] =
+    {
+      NULL, "Accolade/Infogrames", "Virgin Games", "Parker Brothers", "Westone",
+      NULL, NULL, NULL, NULL, "Westone",
+      "Takara", "Taito/Accolade", "Capcom", "Data East", "Namco/Tengen",
+      "Sunsoft", "Bandai", "Dempa", "Technosoft", "Technosoft",
+      "Asmik", NULL, "Extreme/Micronet", "Vic Tokai", "American Sammy",
+      "NCS", "Sigma Enterprises", "Toho", NULL, "Kyugo",
+      NULL, NULL, "Wolfteam", "Kaneko", NULL,
+      "Toaplan", "Tecmo", NULL, NULL, NULL,
+      "Toaplan", "Unipac", "UFL Company Ltd.", "Human", NULL,
+      "Game Arts", "Hot-B", "Sage's Creation", "Tengen/Time Warner",
+        "Renovation/Telenet",
+      "Electronic Arts", NULL, NULL, NULL, NULL,
+      "Psygnosis", "Razorsoft", NULL, "Mentrix", NULL,
+      "JVC/Victor Musical Industries", NULL, NULL, NULL, "IGS Corp.",
+      NULL, NULL, "CRI/Home Data", "Arena", "Virgin Games",
+      NULL, "Nichibutsu", NULL, "Soft Vision", "Palsoft",
+      NULL, "KOEI", NULL, NULL, "U.S. Gold",
+      NULL, "Acclaim/Flying Edge", NULL, "Gametek", NULL,
+      NULL, "Absolute", "Mindscape", "Domark", "Parker Brothers",
+      NULL, NULL, NULL, "Sony Imagesoft", "Sony Imagesoft",
+      "Konami", NULL, "Tradewest/Williams", NULL, "Codemasters",
+      "T*HQ Software", "TecMagik", NULL, "Takara", NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, "Hi Tech Entertainment/Designer Software", "Psygnosis", NULL,
+      NULL, NULL, NULL, NULL, "Accolade",
+      "Code Masters", NULL, NULL, NULL, "Spectrum HoloByte",
+      "Interplay", NULL, NULL, NULL, NULL,
+      "Activision", NULL, "Shiny & Playmates", NULL, NULL,
+      NULL, NULL, NULL, NULL, "Viacom International",
+      NULL, NULL, NULL, NULL, "Atlus",
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, "Infogrames", NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, "Fox Interactive", NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, "Psygnosis",
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, "Disney Interactive",
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL
+    },
+#define GENESIS_COUNTRY_MAX 0x57
+    *genesis_country[GENESIS_COUNTRY_MAX] =
+    {
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, "Brazil", NULL, NULL,         // Brazil NTSC
+      NULL, "Hong Kong", NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      "Asia", "Brazil", NULL, NULL, "Europe",   // Brazil PAL
+      "France", NULL, NULL, NULL, "Japan",
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      "U.S.A.", NULL
+    },
+#define GENESIS_IO_MAX 0x58
+    *genesis_io[GENESIS_IO_MAX] =
+    {
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, "Joystick for MS", NULL,
+      NULL, NULL, "Team Play", NULL, "6 Button Pad",
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, NULL, NULL, NULL, NULL,
+      NULL, "Control Ball", "CD-ROM", NULL, NULL,
+      "Floppy Disk Drive", NULL, NULL, NULL, "3 Button Pad",
+      "Keyboard", "Activator", "Mega Mouse", NULL, NULL,
+      "Printer", NULL, "Serial RS232C", NULL, "Tablet",
+      NULL, "Paddle Controller", NULL
+    };
+
+  ucon64_fread (buf, 0, 11, ucon64.rom);
+  smd_header_split = buf[2];
+  if (buf[8] == 0xaa && buf[9] == 0xbb && buf[10] == 7)
+    {
+      rominfo->buheader_len = SMD_HEADER_LEN;
+      strcpy (rominfo->name, "Name: N/A");
+      rominfo->console_usage = NULL;
+      rominfo->copier_usage = smd_usage[0].help;
+      rominfo->maker = "Publisher: You?";
+      rominfo->country = "Country: Your country?";
+      rominfo->has_internal_crc = 0;
+      strcat (rominfo->misc, "Type: Super Magic Drive SRAM file\n");
+      ucon64.split = 0;                         // SRAM files are never split
+      type = SMD;
+      return 0;                                 // rest is nonsense for SRAM file
+    }
+
+  if (buf[8] == 0xaa && buf[9] == 0xbb && buf[10] == 6)
+    {
+      type = SMD;
+      rominfo->buheader_len = SMD_HEADER_LEN;
+    }
+
+  if (UCON64_ISSET (ucon64.buheader_len))       // -hd, -nhd or -hdn option was specified
+    rominfo->buheader_len = ucon64.buheader_len;
+
+  rominfo->interleaved = UCON64_ISSET (ucon64.interleaved) ?
+    ucon64.interleaved : genesis_testinterleaved (rominfo);
+
+  if (rominfo->interleaved == 0)
+    type = BIN;
+  else if (rominfo->interleaved == 1)
+    type = SMD;
+  else if (rominfo->interleaved == 2)
+    type = MGD_GEN;
+
+  if (type == SMD)
+    {
+      genesis_rom_size = ((ucon64.file_size - rominfo->buheader_len) / 16384) * 16384;
+      if (genesis_rom_size != ucon64.file_size - rominfo->buheader_len)
+        rominfo->data_size = genesis_rom_size;
+
+      memset (buf, 0, 16384);
+      ucon64_fread (buf, rominfo->buheader_len,
+        8192 + (GENESIS_HEADER_START + GENESIS_HEADER_LEN) / 2, ucon64.rom);
+      smd_deinterleave (buf, 16384);            // buf will contain the deinterleaved data
+      memcpy (&genesis_header, buf + GENESIS_HEADER_START, GENESIS_HEADER_LEN);
+    }
+  else if (type == MGD_GEN)
+    {
+      // We use rominfo->buheader_len to make it user definable. Normally it
+      //  should be 0 for MGD_GEN.
+      genesis_rom_size = ucon64.file_size - rominfo->buheader_len;
+      q_fread_mgd (&genesis_header, rominfo->buheader_len + GENESIS_HEADER_START,
+        GENESIS_HEADER_LEN, ucon64.rom);
+    }
+  else // type == BIN
+    {
+      // We use rominfo->buheader_len to make it user definable.
+      genesis_rom_size = ucon64.file_size - rominfo->buheader_len;
+      ucon64_fread (&genesis_header, rominfo->buheader_len + GENESIS_HEADER_START,
+        GENESIS_HEADER_LEN, ucon64.rom);
+    }
+
+  if (!UCON64_ISSET (ucon64.split))
+    {
+      if (type == SMD)
+        {
+          // This code does not work for the last part of a file
+          int split = 0;
+          if (smd_header_split & 0x40)
+            split = ucon64_testsplit (ucon64.rom);
+          ucon64.split = split;                 // force displayed info to be correct
+        }                                       //  if not split (see ucon64.c)
+      else if (type == BIN)
+        {
+          // This code isn't fool-proof, but it's not that important
+          const char *ptr = get_suffix (ucon64.rom);
+          if (strlen (ptr) == 4 && ptr[1] == 'r' &&
+              ptr[2] >= '0' && ptr[2] <= '9' && ptr[3] >= '0' && ptr[3] <= '9')
+            {
+              int n_parts = 0;
+              char fname[FILENAME_MAX], *digits;
+
+              strcpy (fname, ucon64.rom);
+              digits = fname + strlen (fname) - 2;
+              do
+                {
+                  sprintf (digits, "%02d", n_parts);
+                  if (access (fname, F_OK) == 0)
+                    n_parts++;
+                  else
+                    break;
+                }
+              while (n_parts < 100);
+
+              if (n_parts)
+                ucon64.split = n_parts;
+            }
+        }
+    }
+
+  if (!memcmp (&OFFSET (genesis_header, 0), "SEGA", 4) ||
+      ucon64.console == UCON64_GEN)
+    result = 0;
+  else
+    result = -1;
+
+  rominfo->header_start = GENESIS_HEADER_START;
+  rominfo->header_len = GENESIS_HEADER_LEN;
+  rominfo->header = &genesis_header;
+
+  // internal ROM name
+  memcpy (rominfo->name, &OFFSET (genesis_header, 80), GENESIS_NAME_LEN);
+  rominfo->name[GENESIS_NAME_LEN] = 0;
+
+  // ROM maker
+  memcpy (maker, &OFFSET (genesis_header, 16), 8);
+  if (maker[3] == 'T' && maker[4] == '-')
+    {
+      sscanf (&maker[5], "%03d", &value);
+      rominfo->maker = NULL_TO_UNKNOWN_S (genesis_maker[value & 0xff]);
+    }
+  else
+    {
+      // Don't use genesis_maker here. If it would be corrected/updated an
+      //  incorrect publisher name would be displayed.
+      rominfo->maker =
+        (!strncmp (maker, "(C)ACLD", 7)) ? "Ballistic" :
+        (!strncmp (maker, "(C)AESI", 7)) ? "ASCII" :
+        (!strncmp (maker, "(C)ASCI", 7)) ? "ASCII" :
+        (!strncmp (maker, "(C)KANEKO", 9)) ? "Kaneko" :
+        (!strncmp (maker, "(C)PPP", 6)) ? "Gametek" :
+        (!strncmp (maker, "(C)RSI", 6)) ? "Razorsoft" : // or is it "(C)1RSI"?
+        (!strncmp (maker, "(C)SEGA", 7)) ? "Sega" :
+        (!strncmp (maker, "(C)TREC", 7)) ? "Treco" :
+        (!strncmp (maker, "(C)VRGN", 7)) ? "Virgin Games" :
+        (!strncmp (maker, "(C)WADN", 7)) ? "Parker Brothers" :
+        (!strncmp (maker, "(C)WSTN", 7)) ? "Westone" : NULL;
+      if (!rominfo->maker)
+        {
+          maker[8] = 0;
+          rominfo->maker = maker;
+        }
+    }
+
+  genesis_tv_standard = 1;              // default to PAL; NTSC has higher precedence
+  genesis_japanese = 0;
+
+  country[0] = 0;
+  // ROM country
+  for (x = 0; x < 5; x++)
+    {
+      int country_code = OFFSET (genesis_header, 240 + x);
+
+      if ((x > 0 && country_code == 0) || country_code == ' ')
+        continue;
+      if (country_code == 'J')
+        genesis_japanese = 1;
+      if (genesis_japanese || country_code == 'U' || country_code == '4')
+        genesis_tv_standard = 0;        // Japan, the U.S.A. and Brazil ('4') use NTSC
+      strcat (country, NULL_TO_UNKNOWN_S
+               (genesis_country[MIN (country_code, GENESIS_COUNTRY_MAX - 1)]));
+      strcat (country, ", ");
+    }
+  x = strlen (country);
+  if (x >= 2 && country[x - 2] == ',' && country[x - 1] == ' ')
+    country[x - 2] = 0;
+  rominfo->country = country;
+
+  // misc stuff
+  memcpy (name, &OFFSET (genesis_header, 32), GENESIS_NAME_LEN);
+  name[GENESIS_NAME_LEN] = 0;
+  sprintf ((char *) buf, "Japanese game name: %s\n", name);
+  strcat (rominfo->misc, (char *) buf);
+
+  sprintf ((char *) buf, "Date: %.8s\n", &OFFSET (genesis_header, 24));
+  strcat (rominfo->misc, (char *) buf);
+
+  x = (OFFSET (genesis_header, 160) << 24) +
+      (OFFSET (genesis_header, 161) << 16) +
+      (OFFSET (genesis_header, 162) << 8) +
+       OFFSET (genesis_header, 163);
+  y = (OFFSET (genesis_header, 164) << 24) +
+      (OFFSET (genesis_header, 165) << 16) +
+      (OFFSET (genesis_header, 166) << 8) +
+       OFFSET (genesis_header, 167);
+  sprintf ((char *) buf, "Internal size: %.4f Mb\n", (float) (y - x + 1) / MBIT);
+  strcat (rominfo->misc, (char *) buf);
+
+  sprintf ((char *) buf, "ROM start: %08x\n", x);
+  strcat (rominfo->misc, (char *) buf);
+
+  sprintf ((char *) buf, "ROM end: %08x\n", y);
+  strcat (rominfo->misc, (char *) buf);
+
+  genesis_has_ram = OFFSET (genesis_header, 176) == 'R' &&
+                    OFFSET (genesis_header, 177) == 'A';
+  if (genesis_has_ram)
+    {
+      x = (OFFSET (genesis_header, 180) << 24) +
+          (OFFSET (genesis_header, 181) << 16) +
+          (OFFSET (genesis_header, 182) << 8) +
+           OFFSET (genesis_header, 183);
+      y = (OFFSET (genesis_header, 184) << 24) +
+          (OFFSET (genesis_header, 185) << 16) +
+          (OFFSET (genesis_header, 186) << 8) +
+           OFFSET (genesis_header, 187);
+      sprintf ((char *) buf, "Cartridge RAM: Yes, %d kBytes (%s)\n",
+               (y - x + 1) >> 10,
+               OFFSET (genesis_header, 178) & 0x40 ? "backup" : "non-backup");
+      strcat (rominfo->misc, (char *) buf);
+
+      sprintf ((char *) buf, "RAM start: %08x\n", x);
+      strcat (rominfo->misc, (char *) buf);
+
+      sprintf ((char *) buf, "RAM end: %08x\n", y);
+      strcat (rominfo->misc, (char *) buf);
+    }
+  else
+    strcat (rominfo->misc, "Cartridge RAM: No\n");
+
+  /*
+    Only checking for 'G' seems to give better results than checking for "GM".
+    "Officially" "GM" indicates it's a game and "Al" that it's educational.
+  */
+  sprintf ((char *) buf, "Product type: %s\n",
+           (OFFSET (genesis_header, 128) == 'G') ? "Game" : "Educational");
+  strcat (rominfo->misc, (char *) buf);
+
+  sprintf ((char *) buf, "I/O device(s): %s",
+    NULL_TO_UNKNOWN_S (genesis_io[MIN ((int) OFFSET (genesis_header, 144), GENESIS_IO_MAX - 1)]));
+  for (x = 0; x < 3; x++)
+    {
+      const char *io_device = genesis_io[MIN (OFFSET (genesis_header, 145 + x), GENESIS_IO_MAX - 1)];
+      if (!io_device)
+        continue;
+      strcat ((char *) buf, ", ");
+      strcat ((char *) buf, io_device);
+    }
+  strcat ((char *) buf, "\n");
+  strcat (rominfo->misc, (char *) buf);
+
+  sprintf ((char *) buf, "Modem data: %.10s\n", &OFFSET (genesis_header, 188));
+  strcat (rominfo->misc, (char *) buf);
+
+  sprintf ((char *) buf, "Memo: %.40s\n", &OFFSET (genesis_header, 200));
+  strcat (rominfo->misc, (char *) buf);
+
+  sprintf ((char *) buf, "Product code: %.8s\n", &OFFSET (genesis_header, 131));
+  strcat (rominfo->misc, (char *) buf);
+
+  sprintf ((char *) buf, "Version: 1.%c%c", OFFSET (genesis_header, 140), OFFSET (genesis_header, 141));
+  strcat (rominfo->misc, (char *) buf);
+
+  // internal ROM crc
+  if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0)
+    {
+      if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL)
+        return -1;
+
+      rominfo->has_internal_crc = 1;
+      rominfo->internal_crc_len = 2;
+
+      rominfo->current_internal_crc = genesis_chksum (rom_buffer);
+      rominfo->internal_crc = OFFSET (genesis_header, 143);          // low byte of checksum
+      rominfo->internal_crc += (OFFSET (genesis_header, 142)) << 8;  // high byte of checksum
+
+      rominfo->internal_crc2[0] = 0;
+      free (rom_buffer);
+    }
+  rominfo->console_usage = genesis_usage[0].help;
+  if (type == SMD)
+    rominfo->copier_usage = smd_usage[0].help;
+  else if (type == MGD_GEN)
+    rominfo->copier_usage = mgd_usage[0].help;
+  else // type == BIN
+    rominfo->copier_usage = bin_usage[0].help;
+
+  return result;
+}
+
+
+int
+genesis_chksum (unsigned char *rom_buffer)
+{
+  int i, len = genesis_rom_size - 2;
+  unsigned short checksum = 0;
+
+  for (i = 512; i <= len; i += 2)
+    checksum += (rom_buffer[i] << 8) + (rom_buffer[i + 1]);
+
+  return checksum;
+}

+ 43 - 0
ucon64/2.0/src/console/genesis.h

@@ -0,0 +1,43 @@
+/*
+genesis.h - Sega Genesis/Mega Drive support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2002 - 2003 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef GENESIS_H
+#define GENESIS_H
+
+typedef enum { SMD = 1, BIN, MGD_GEN } genesis_file_t;
+
+extern const st_getopt2_t genesis_usage[];
+
+extern genesis_file_t genesis_get_file_type (void);
+extern int genesis_1991 (st_rominfo_t *rominfo);
+extern int genesis_chk (st_rominfo_t *rominfo);
+extern int genesis_j (st_rominfo_t *rominfo);
+extern int genesis_n (st_rominfo_t *rominfo, const char *name);
+extern int genesis_n2 (st_rominfo_t *rominfo, const char *name);
+extern int genesis_s (st_rominfo_t *rominfo);
+extern int genesis_smd (st_rominfo_t *rominfo);
+extern int genesis_smds (void);
+extern int genesis_bin (st_rominfo_t *rominfo);
+extern int genesis_mgd (st_rominfo_t *rominfo);
+extern int genesis_multi (int truncate_size, char *fname);
+extern int genesis_init (st_rominfo_t *rominfo);
+extern int genesis_f (st_rominfo_t *rominfo);
+#endif

+ 109 - 0
ucon64/2.0/src/console/jaguar.c

@@ -0,0 +1,109 @@
+/*
+jaguar.c - Atari Jaguar support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef  HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include "misc/misc.h"
+#include "misc/file.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "jaguar.h"
+
+
+const st_getopt2_t jaguar_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Panther(32bit prototype)/Jaguar64/Jaguar64 CD"/*"1989 Flare2/1993 Atari/1995 Atari"*/,
+      NULL
+    },
+    {
+      "jag", 0, 0, UCON64_JAG,
+      NULL, "force recognition",
+      &ucon64_wf[WF_OBJ_JAG_SWITCH]
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+};
+
+
+typedef struct st_jaguar
+{
+  char pad[16];
+} st_jaguar_t;
+#define JAGUAR_HEADER_START 0x400
+#define JAGUAR_HEADER_LEN (sizeof (st_jaguar_t))
+
+st_jaguar_t jaguar_header;
+
+
+int
+jaguar_init (st_rominfo_t *rominfo)
+{
+  int result = -1, x, value;
+
+  rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ?
+    ucon64.buheader_len : 0;
+
+  ucon64_fread (&jaguar_header, JAGUAR_HEADER_START +
+    rominfo->buheader_len, JAGUAR_HEADER_LEN, ucon64.rom);
+  value = 0;
+  for (x = 0; x < 12; x++)
+    value += OFFSET (jaguar_header, x);
+  if (value == 0xb0)
+    result = 0;
+  else
+    {
+      rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ?
+        ucon64.buheader_len : (int) UNKNOWN_HEADER_LEN;
+
+      ucon64_fread (&jaguar_header, JAGUAR_HEADER_START +
+          rominfo->buheader_len, JAGUAR_HEADER_LEN, ucon64.rom);
+      value = 0;
+      for (x = 0; x < 12; x++)
+        value += OFFSET (jaguar_header, x);
+
+      if (value == 0xb0)
+        result = 0;
+      else
+        result = -1;
+    }
+  if (ucon64.console == UCON64_JAG)
+    result = 0;
+
+  rominfo->header_start = JAGUAR_HEADER_START;
+  rominfo->header_len = JAGUAR_HEADER_LEN;
+  rominfo->header = &jaguar_header;
+
+  rominfo->console_usage = jaguar_usage[0].help;
+  rominfo->copier_usage = unknown_usage[0].help;
+
+  return result;
+}

+ 27 - 0
ucon64/2.0/src/console/jaguar.h

@@ -0,0 +1,27 @@
+/*
+jaguar.h - Atari Jaguar support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef JAGUAR_H
+#define JAGUAR_H
+
+extern const st_getopt2_t jaguar_usage[];
+extern int jaguar_init (st_rominfo_t *rominfo);
+
+#endif

+ 357 - 0
ucon64/2.0/src/console/lynx.c

@@ -0,0 +1,357 @@
+/*
+lynx.c - Atari Lynx support for uCON64
+
+Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
+Copyright (c) 2002 - 2003 dbjh
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifdef  HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef  HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "misc/bswap.h"
+#include "misc/file.h"
+#include "misc/misc.h"
+#ifdef  USE_ZLIB
+#include "misc/archive.h"
+#endif
+#include "misc/getopt2.h"                       // st_getopt2_t
+#include "ucon64.h"
+#include "ucon64_misc.h"
+#include "lynx.h"
+
+
+const st_getopt2_t lynx_usage[] =
+  {
+    {
+      NULL, 0, 0, 0,
+      NULL, "Handy (prototype)/Lynx/Lynx II"/*"1987 Epyx/1989 Atari/1991 Atari"*/,
+      NULL
+    },
+    {
+      "lynx", 0, 0, UCON64_LYNX,
+      NULL, "force recognition",
+      &ucon64_wf[WF_OBJ_LYNX_SWITCH]
+    },
+    {
+      "lyx", 0, 0, UCON64_LYX,
+      NULL, "convert to LYX/RAW (strip 64 Bytes LNX header)",
+      &ucon64_wf[WF_OBJ_LYNX_DEFAULT]
+    },
+    {
+      "lnx", 0, 0, UCON64_LNX,
+      NULL, "convert to LNX (uses default values for the header);\n"
+      "adjust the LNX header with the following options",
+      &ucon64_wf[WF_OBJ_LYNX_DEFAULT]
+    },
+    {
+      "n", 1, 0, UCON64_N,
+      "NEW_NAME", "change internal ROM name to NEW_NAME (LNX only)",
+      &ucon64_wf[WF_OBJ_ALL_DEFAULT]
+    },
+    {
+      "nrot", 0, 0, UCON64_NROT,
+      NULL, "set no rotation (LNX only)",
+      &ucon64_wf[WF_OBJ_LYNX_DEFAULT]
+    },
+    {
+      "rotl", 0, 0, UCON64_ROTL,
+      NULL, "set rotation left (LNX only)",
+      &ucon64_wf[WF_OBJ_LYNX_DEFAULT]
+    },
+    {
+      "rotr", 0, 0, UCON64_ROTR,
+      NULL, "set rotation right (LNX only)",
+      &ucon64_wf[WF_OBJ_LYNX_DEFAULT]
+    },
+    {
+      "b0", 1, 0, UCON64_B0,
+      "N", "change Bank0 kBytes size to N={0,64,128,256,512} (LNX only)",
+      &ucon64_wf[WF_OBJ_LYNX_DEFAULT]
+    },
+    {
+      "b1", 1, 0, UCON64_B1,
+      "N", "change Bank1 kBytes size to N={0,64,128,256,512} (LNX only)",
+      &ucon64_wf[WF_OBJ_LYNX_DEFAULT]
+    },
+    {NULL, 0, 0, 0, NULL, NULL, NULL}
+};
+
+const char *lynx_lyx_desc = "convert to LYX/RAW (strip 64 Bytes LNX header)";
+
+//static const char *lnx_usage[] = "LNX header";
+#define LNX_HEADER_START 0
+#define LNX_HEADER_LEN (sizeof (st_lnx_header_t))
+
+st_lnx_header_t lnx_header;
+
+
+int
+lynx_lyx (st_rominfo_t *rominfo)
+{
+  char dest_name[FILENAME_MAX];
+
+  if (!rominfo->buheader_len)
+    {
+      fprintf (stderr, "ERROR: This is no LNX file\n\n");
+      return -1;
+    }
+
+  strcpy (dest_name, ucon64.rom);
+  set_suffix (dest_name, ".lyx");
+
+  ucon64_file_handler (dest_name, NULL, 0);
+  fcopy (ucon64.rom, rominfo->buheader_len, ucon64.file_size, dest_name, "wb");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+lynx_lnx (st_rominfo_t *rominfo)
+{
+  st_lnx_header_t header;
+  char dest_name[FILENAME_MAX];
+  int size = ucon64.file_size;
+
+  if (rominfo->buheader_len != 0)
+    {
+      fprintf (stderr, "ERROR: This seems to already be an LNX file\n\n");
+      return -1;
+    }
+
+  header.page_size_bank0 = size > 4 * MBIT ? 4 * MBIT / 256 : size / 256;
+  header.page_size_bank1 = size > 4 * MBIT ? (size - (4 * MBIT)) / 256 : 0;
+#ifdef  WORDS_BIGENDIAN
+  header.page_size_bank0 = bswap_16 (header.page_size_bank0);
+  header.page_size_bank1 = bswap_16 (header.page_size_bank1);
+#endif
+
+  memset (header.cartname, 0, sizeof (header.cartname));
+  memset (header.manufname, 0, sizeof (header.manufname));
+  memset (header.spare, 0, sizeof (header.spare));
+
+#ifdef  WORDS_BIGENDIAN
+  header.version = bswap_16 (1);
+#else
+  header.version = 1;
+#endif
+
+  memcpy (header.magic, "LYNX", 4);
+  header.rotation = 0;
+  strncpy (header.cartname, ucon64.rom, sizeof (header.cartname));
+  strcpy (header.manufname, "Atari");
+
+  strcpy (dest_name, ucon64.rom);
+  set_suffix (dest_name, ".lnx");
+
+  ucon64_file_handler (dest_name, NULL, 0);
+  ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "wb");
+  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "ab");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+static int
+lynx_rot (st_rominfo_t *rominfo, int rotation)
+{
+  st_lnx_header_t header;
+  char dest_name[FILENAME_MAX];
+
+  if (!rominfo->buheader_len)
+    {
+      fprintf (stderr, "ERROR: This is no LNX file\n\n");
+      return -1;
+    }
+
+  ucon64_fread (&header, 0, sizeof (st_lnx_header_t), ucon64.rom);
+
+  header.rotation = rotation;                   // header.rotation is an 8-bit field
+
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
+  ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "r+b");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+lynx_nrot (st_rominfo_t *rominfo)
+{
+  return lynx_rot (rominfo, 0);                 // no rotation
+}
+
+
+int
+lynx_rotl (st_rominfo_t *rominfo)
+{
+  return lynx_rot (rominfo, 1);                 // rotate left
+}
+
+
+int
+lynx_rotr (st_rominfo_t *rominfo)
+{
+  return lynx_rot (rominfo, 2);                 // rotate right
+}
+
+
+int
+lynx_n (st_rominfo_t *rominfo, const char *name)
+{
+  st_lnx_header_t header;
+  char dest_name[FILENAME_MAX];
+
+  if (!rominfo->buheader_len)
+    {
+      fprintf (stderr, "ERROR: This is no LNX file\n\n");
+      return -1;
+    }
+
+  ucon64_fread (&header, 0, sizeof (st_lnx_header_t), ucon64.rom);
+
+  memset (header.cartname, 0, sizeof (header.cartname));
+  strncpy (header.cartname, name, sizeof (header.cartname));
+
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
+  ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "r+b");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+static int
+lynx_b (st_rominfo_t *rominfo, int bank, const char *value)
+{
+  st_lnx_header_t header;
+  short int *bankvar;
+  char dest_name[FILENAME_MAX];
+
+  if (!rominfo->buheader_len)
+    {
+      fprintf (stderr, "ERROR: This is no LNX file\n\n");
+      return -1;
+    }
+
+  ucon64_fread (&header, 0, sizeof (st_lnx_header_t), ucon64.rom);
+
+  bankvar = (bank == 0 ? &header.page_size_bank0 : &header.page_size_bank1);
+  if ((atol (value) % 64) != 0 || (atol (value) > 512))
+    *bankvar = 0;
+  else
+#ifdef  WORDS_BIGENDIAN
+    *bankvar = bswap_16 (atol (value) * 4);
+#else
+    *bankvar = atol (value) * 4;
+#endif
+
+  strcpy (dest_name, ucon64.rom);
+  ucon64_file_handler (dest_name, NULL, 0);
+  fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb");
+  ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "r+b");
+
+  printf (ucon64_msg[WROTE], dest_name);
+  return 0;
+}
+
+
+int
+lynx_b0 (st_rominfo_t *rominfo, const char *value)
+{
+  return lynx_b (rominfo, 0, value);
+}
+
+
+int
+lynx_b1 (st_rominfo_t *rominfo, const char *value)
+{
+  return lynx_b (rominfo, 1, value);
+}
+
+
+int
+lynx_init (st_rominfo_t *rominfo)
+{
+  int result = -1;
+
+  rominfo->console_usage = lynx_usage[0].help;
+  rominfo->copier_usage = unknown_usage[0].help;
+
+  ucon64_fread (&lnx_header, 0, LNX_HEADER_LEN, ucon64.rom);
+  if (!strncmp (lnx_header.magic, "LYNX", 4))
+    result = 0;
+  else
+    result = -1;
+  if (ucon64.console == UCON64_LYNX)
+    result = 0;
+
+  if (!strncmp (lnx_header.magic, "LYNX", 4))
+    {
+      rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ?
+        ucon64.buheader_len : (int) LNX_HEADER_LEN;
+
+      if (UCON64_ISSET (ucon64.buheader_len) && !ucon64.buheader_len)
+        return ucon64.console == UCON64_LYNX ? 0 : result;
+
+      ucon64_fread (&lnx_header, 0, LNX_HEADER_LEN, ucon64.rom);
+      rominfo->buheader = &lnx_header;
+
+      // internal ROM name
+      strcpy (rominfo->name, lnx_header.cartname);
+
+      // ROM maker
+      rominfo->maker = lnx_header.manufname;
+
+      // misc stuff
+      sprintf (rominfo->misc,
+        "Internal Size: Bank0 %hd Bytes (%.4f Mb)\n"
+        "               Bank1 %hd Bytes (%.4f Mb)\n"
+        "Version: %hd\n"
+        "Rotation: %s",
+#ifdef  WORDS_BIGENDIAN
+        bswap_16 (lnx_header.page_size_bank0) * 256,
+        TOMBIT_F (bswap_16 (lnx_header.page_size_bank0) * 256),
+        bswap_16 (lnx_header.page_size_bank1) * 256,
+        TOMBIT_F (bswap_16 (lnx_header.page_size_bank1) * 256),
+        bswap_16 (lnx_header.version),
+#else
+        lnx_header.page_size_bank0 * 256,
+        TOMBIT_F (lnx_header.page_size_bank0 * 256),
+        lnx_header.page_size_bank1 * 256,
+        TOMBIT_F (lnx_header.page_size_bank1 * 256),
+        lnx_header.version,
+#endif
+        (!lnx_header.rotation) ? "No" : ((lnx_header.rotation == 1) ? "Left" : "Right"));
+    }
+
+  return result;
+}

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels