ソースを参照

Merge branch 'master' of git@github.com:optixx/quickdev16

optixx 14 年 前
コミット
485a7831db
100 ファイル変更22810 行追加245 行削除
  1. 29 10
      avr/bootloader/bootloader.c
  2. 2 1
      avr/usbload/sram.h
  3. 6 13
      tools/Quickdev16/AppController.h
  4. 61 67
      tools/Quickdev16/AppController.m
  5. 18 0
      tools/Quickdev16/CommandWrapper.h
  6. 56 0
      tools/Quickdev16/CommandWrapper.m
  7. 96 88
      tools/Quickdev16/English.lproj/MainMenu.xib
  8. 55 32
      tools/Quickdev16/Quickdev16.xcodeproj/david.mode1v3
  9. 558 24
      tools/Quickdev16/Quickdev16.xcodeproj/david.pbxuser
  10. 6 10
      tools/Quickdev16/Quickdev16.xcodeproj/project.pbxproj
  11. BIN
      tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/categories.pbxbtree
  12. BIN
      tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/cdecls.pbxbtree
  13. BIN
      tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/decls.pbxbtree
  14. BIN
      tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/files.pbxbtree
  15. BIN
      tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/imports.pbxbtree
  16. BIN
      tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/pbxindex.header
  17. BIN
      tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/protocols.pbxbtree
  18. BIN
      tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/refs.pbxbtree
  19. BIN
      tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/strings.pbxstrings/control
  20. BIN
      tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/strings.pbxstrings/strings
  21. BIN
      tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/subclasses.pbxbtree
  22. BIN
      tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/symbols0.pbxsymbols
  23. 11 0
      tools/ucon64-2.0.0-src/FILE_ID.DIZ
  24. 1 0
      tools/ucon64-2.0.0-src/VERSION
  25. 783 0
      tools/ucon64-2.0.0-src/changes.html
  26. 590 0
      tools/ucon64-2.0.0-src/developers.html
  27. 1889 0
      tools/ucon64-2.0.0-src/faq.html
  28. 210 0
      tools/ucon64-2.0.0-src/hardware.html
  29. BIN
      tools/ucon64-2.0.0-src/images/bg.jpg
  30. BIN
      tools/ucon64-2.0.0-src/images/cameralink.png
  31. BIN
      tools/ucon64-2.0.0-src/images/cd_rw.png
  32. BIN
      tools/ucon64-2.0.0-src/images/dexdrive.png
  33. BIN
      tools/ucon64-2.0.0-src/images/f2a.png
  34. BIN
      tools/ucon64-2.0.0-src/images/fal.png
  35. BIN
      tools/ucon64-2.0.0-src/images/fig.png
  36. BIN
      tools/ucon64-2.0.0-src/images/gbx.png
  37. BIN
      tools/ucon64-2.0.0-src/images/ggpro.png
  38. BIN
      tools/ucon64-2.0.0-src/images/hr.png
  39. BIN
      tools/ucon64-2.0.0-src/images/icon.png
  40. BIN
      tools/ucon64-2.0.0-src/images/logo.png
  41. BIN
      tools/ucon64-2.0.0-src/images/mdpro.png
  42. BIN
      tools/ucon64-2.0.0-src/images/msg.png
  43. BIN
      tools/ucon64-2.0.0-src/images/pcepro.png
  44. BIN
      tools/ucon64-2.0.0-src/images/sf3.png
  45. BIN
      tools/ucon64-2.0.0-src/images/smc.png
  46. BIN
      tools/ucon64-2.0.0-src/images/smd.png
  47. BIN
      tools/ucon64-2.0.0-src/images/smspro.png
  48. BIN
      tools/ucon64-2.0.0-src/images/swc.png
  49. BIN
      tools/ucon64-2.0.0-src/images/v64.png
  50. BIN
      tools/ucon64-2.0.0-src/images/v64jr.png
  51. 5 0
      tools/ucon64-2.0.0-src/install.html
  52. 372 0
      tools/ucon64-2.0.0-src/libieee1284.diff
  53. 343 0
      tools/ucon64-2.0.0-src/license.html
  54. 594 0
      tools/ucon64-2.0.0-src/readme.html
  55. 173 0
      tools/ucon64-2.0.0-src/src/GoodCodes.txt
  56. 380 0
      tools/ucon64-2.0.0-src/src/Makefile.in
  57. 380 0
      tools/ucon64-2.0.0-src/src/Makefile.orig
  58. 223 0
      tools/ucon64-2.0.0-src/src/Makefile.vc6
  59. 118 0
      tools/ucon64-2.0.0-src/src/backup/NTSC-PAL notes.txt
  60. 419 0
      tools/ucon64-2.0.0-src/src/backup/SWC-compatibility.txt
  61. 62 0
      tools/ucon64-2.0.0-src/src/backup/backup.h
  62. 768 0
      tools/ucon64-2.0.0-src/src/backup/cartlib.c
  63. 504 0
      tools/ucon64-2.0.0-src/src/backup/cd64.c
  64. 41 0
      tools/ucon64-2.0.0-src/src/backup/cd64.h
  65. 985 0
      tools/ucon64-2.0.0-src/src/backup/cmc.c
  66. 32 0
      tools/ucon64-2.0.0-src/src/backup/cmc.h
  67. 139 0
      tools/ucon64-2.0.0-src/src/backup/dex.c
  68. 34 0
      tools/ucon64-2.0.0-src/src/backup/dex.h
  69. 399 0
      tools/ucon64-2.0.0-src/src/backup/doctor64.c
  70. 31 0
      tools/ucon64-2.0.0-src/src/backup/doctor64.h
  71. 587 0
      tools/ucon64-2.0.0-src/src/backup/doctor64jr.c
  72. 31 0
      tools/ucon64-2.0.0-src/src/backup/doctor64jr.h
  73. 1637 0
      tools/ucon64-2.0.0-src/src/backup/f2a.c
  74. 34 0
      tools/ucon64-2.0.0-src/src/backup/f2a.h
  75. 1671 0
      tools/ucon64-2.0.0-src/src/backup/fal.c
  76. 35 0
      tools/ucon64-2.0.0-src/src/backup/fal.h
  77. 316 0
      tools/ucon64-2.0.0-src/src/backup/ffe.c
  78. 80 0
      tools/ucon64-2.0.0-src/src/backup/ffe.h
  79. 739 0
      tools/ucon64-2.0.0-src/src/backup/fig.c
  80. 100 0
      tools/ucon64-2.0.0-src/src/backup/fig.h
  81. 1991 0
      tools/ucon64-2.0.0-src/src/backup/gbx.c
  82. 34 0
      tools/ucon64-2.0.0-src/src/backup/gbx.h
  83. 1144 0
      tools/ucon64-2.0.0-src/src/backup/gd.c
  84. 49 0
      tools/ucon64-2.0.0-src/src/backup/gd.h
  85. 46 0
      tools/ucon64-2.0.0-src/src/backup/interceptor.c
  86. 31 0
      tools/ucon64-2.0.0-src/src/backup/interceptor.h
  87. 169 0
      tools/ucon64-2.0.0-src/src/backup/libcd64/Makefile
  88. 62 0
      tools/ucon64-2.0.0-src/src/backup/libcd64/Makefile.vc6
  89. 28 0
      tools/ucon64-2.0.0-src/src/backup/libcd64/cd64.def
  90. 1005 0
      tools/ucon64-2.0.0-src/src/backup/libcd64/cd64io.c
  91. 114 0
      tools/ucon64-2.0.0-src/src/backup/libcd64/cd64io.h
  92. 990 0
      tools/ucon64-2.0.0-src/src/backup/libcd64/cd64lib.c
  93. 13 0
      tools/ucon64-2.0.0-src/src/backup/libcd64/ultra64/cartmem.h
  94. 8 0
      tools/ucon64-2.0.0-src/src/backup/libcd64/ultra64/host/cartinfo.h
  95. 189 0
      tools/ucon64-2.0.0-src/src/backup/libcd64/ultra64/host/cd64lib.h
  96. 60 0
      tools/ucon64-2.0.0-src/src/backup/libcd64/ultra64/rom.h
  97. 1055 0
      tools/ucon64-2.0.0-src/src/backup/lynxit.c
  98. 31 0
      tools/ucon64-2.0.0-src/src/backup/lynxit.h
  99. 158 0
      tools/ucon64-2.0.0-src/src/backup/mccl.c
  100. 30 0
      tools/ucon64-2.0.0-src/src/backup/mccl.h

+ 29 - 10
avr/bootloader/bootloader.c

@@ -33,6 +33,10 @@
 #include "config.h"
 #include "usbdrv/usbdrv.c"
 
+
+
+
+
 /*
  * USBasp requests, taken from the original USBasp sourcecode 
  */
@@ -74,6 +78,10 @@
                   (LED_DIR ^= (1 << LED_PIN)));}
 
 
+#define AVR_BTLDR_EN_PORT	    PORTC
+#define AVR_BTLDR_EN_DIR	    DDRC
+#define AVR_BTLDR_EN_PIN	    PC1
+#define AVR_BTLDR_EN_IN	        PINC
 
 
 /*
@@ -449,20 +457,13 @@ int __attribute__ ((noreturn, OS_main)) main(void)
     uint16_t delay = 0;
     timeout = TIMEOUT;
 
+    DDRC &= ~(1 << AVR_BTLDR_EN_PIN);	    
+    PORTC &= ~(1 << AVR_BTLDR_EN_PIN);	    
 
-    /*
-     * if power-on reset, quit bootloader via watchdog reset 
-     */
-    if (reset & _BV(PORF)) {
-        banner();
-        uart_puts("Found power on reset\n\r");
-        MCUSR = 0;
-        leave_bootloader();
-    }
     /*
      * if watchdog reset, disable watchdog and jump to app 
      */
-    else if (reset & _BV(WDRF)) {
+    if (reset & _BV(WDRF)) {
         uart_puts("Found watchdog reset\n\r");
         MCUSR = 0;
         wdt_disable();
@@ -470,6 +471,24 @@ int __attribute__ ((noreturn, OS_main)) main(void)
         jump_to_app();
     }
 
+
+    if ((AVR_BTLDR_EN_IN & ( 1 << AVR_BTLDR_EN_PIN)) == 0){
+        banner();
+        uart_puts("Bootloader flashing is disabled\n\r");
+        MCUSR = 0;
+        leave_bootloader();
+        
+    }
+
+    /*
+     * if power-on reset, quit bootloader via watchdog reset 
+     */
+    if (reset & _BV(PORF)) {
+        banner();
+        uart_puts("Found power on reset\n\r");
+        MCUSR = 0;
+        leave_bootloader();
+    }
     banner();
     uart_puts("Enter programming mode\n\r");
     /*

+ 2 - 1
avr/usbload/sram.h

@@ -151,7 +151,8 @@
                                 (AVR_CS_DIR |= (1 << AVR_CS_PIN)))
 
 #define snes_bus_active()	    ((AVR_SNES_SW_PORT |= (1 << AVR_SNES_SW_PIN)),\
-                                (AVR_CS_DIR &= ~(1 << AVR_CS_PIN)))
+                                (AVR_CS_DIR &= ~(1 << AVR_CS_PIN)),\
+                                (AVR_CS_PORT |= (1 << AVR_CS_PIN)))
 
 #define HI_LOROM_SW_PORT	    PORTD
 #define HI_LOROM_SW_DIR	        DDRD

+ 6 - 13
tools/Quickdev16/AppController.h

@@ -1,19 +1,12 @@
 #import <Cocoa/Cocoa.h>
 
 @interface AppController : NSObject  {
-    IBOutlet NSTextField *textField;
-	IBOutlet NSColorWell *colorWell;
-	IBOutlet NSButton *stopButton;
-	IBOutlet NSTableView *tableView;
-	NSSpeechSynthesizer *speechSynth;
+    IBOutlet NSTextField *textFieldLog;
+	IBOutlet NSTextField *textFieldInfo;
+
 }
-- (IBAction)sayIt:(id)sender;
-- (IBAction)stopIt:(id)sender;
-- (IBAction)changeTextColor:(id)sender;
-- (void)speechSynthesizer:(NSSpeechSynthesizer *)send
-		didFinishSpeaking:(BOOL)finishedSpeaking;
-- (int)numberOfRowsInTableView:(NSTableView *) aTableView;
--(id)tableView:(NSTableView*) aTableView
-objectValueForTableColumn:(NSTableColumn *) aTableColumn row:(int)row;
+- (IBAction)romInfo:(id)sender;
+- (IBAction)romUpload:(id)sender;
+
 
 @end

+ 61 - 67
tools/Quickdev16/AppController.m

@@ -1,89 +1,84 @@
 #import "AppController.h"
+#import "CommandWrapper.h"
+
+#define UCON64 "/usr/local/bin/ucon64"
+#define ROMFILE "/Users/david/Devel/arch/avr/code/quickdev16/roms/mrdo.smc"
+
 
 @implementation AppController
 - (id) init {
 	[super init];
 	NSLog(@"init");
-	speechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil];
-	[speechSynth setDelegate:self]; 
 	return self;
 }
 
-- (void)awakeFromNib{
-	NSColor *initialColor = [ textField textColor];
-	NSLog(@"setting init color %@",initialColor);
-	[colorWell setColor:initialColor];
-	NSString *defaultVoice = [NSSpeechSynthesizer defaultVoice];
-	NSArray *voices = [NSSpeechSynthesizer availableVoices];
-	int defaultRow = [voices indexOfObject:defaultVoice];
-	[tableView selectRow:defaultRow byExtendingSelection:NO];
-	[tableView scrollRowToVisible:defaultRow];
-	 
-}
-- (IBAction)sayIt:(id)sender {
-	NSString *string = [textField stringValue];
-	if ( [string length] == 0) {
-		NSLog(@"No message");
-		return;
-	}
-	[speechSynth startSpeakingString:string];
-	NSLog(@"Have started speaking: %@", string);
-    [stopButton setEnabled:YES];
-}
+- (void)awakeFromNib {
+    [textFieldLog setStringValue:@"Log field"];
+	[textFieldInfo setStringValue:@"Info field"];
 
-- (IBAction)stopIt:(id)sender {
-	NSLog(@"stopping");
-	[speechSynth stopSpeaking];
-    
+	NSLog(@"awakeFromNib");
 }
 
-- (void)speechSynthesizer:(NSSpeechSynthesizer *)send
-		didFinishSpeaking:(BOOL)finishedSpeaking
-{
-	NSLog(@"didFinished=%d",finishedSpeaking);
-    [stopButton setEnabled:NO];
-
+- (IBAction)romUpload:(id)sender {
+	NSLog(@"romUpload:");
+    NSTask *ls=[[NSTask alloc] init];
+    NSPipe *pipe=[[NSPipe alloc] init];
+    NSFileHandle *handle;
+    
+    [ls setLaunchPath:@UCON64];
+    [ls setArguments:[NSArray arrayWithObjects:@"-smc",@"--port=usb",@"--xquickdev16",@ROMFILE,nil]];
+    [ls setStandardOutput:pipe];
+    handle=[pipe fileHandleForReading];
+    
+    [ls launch];
+    
+    [NSThread detachNewThreadSelector:@selector(copyData:)
+							 toTarget:self withObject:handle];
+    
+    [pipe release];
+    [ls release];
 }
 
 
-- (IBAction)changeTextColor:(id)sender{
-	NSColor *newColor = [sender color];
-	NSLog(@"Change Color %@",newColor);
-	[textField setTextColor:newColor];
+- (void)copyData:(NSFileHandle*)handle {
+    NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
+    NSData *data;
+	
+    while([data=[handle availableData] length]) { // until EOF (check reference)
+        NSString *string=[[NSString alloc] initWithData:data
+											   encoding:NSASCIIStringEncoding];
+        
+		[textFieldLog setStringValue:string];
+		[string release];
+    }
 	
+    [pool release];
 }
 
-- (int)numberOfRowsInTableView:(NSTableView *) aTableView{
-	NSLog(@"numberOfRowsInTableView %d", [[NSSpeechSynthesizer availableVoices] count]);
 
-	return [[NSSpeechSynthesizer availableVoices] count];
-}
--(id)tableView:(NSTableView*) aTableView
-			objectValueForTableColumn:(NSTableColumn *) aTableColumn row:(int)row{
-	NSString *voice = [[NSSpeechSynthesizer availableVoices] objectAtIndex:row ];
-	return [[NSSpeechSynthesizer attributesForVoice:voice] valueForKey:NSVoiceName];
-}
 
-- (void)tableViewSelectionDidChange:(NSNotification *)nofication{
-	NSArray *availableVoices = [NSSpeechSynthesizer availableVoices];
-	int row = [tableView selectedRow];
-	if ( row == -1) {
-		return;
-	}
+- (IBAction)romInfo:(id)sender {
+    NSTask *ls=[[NSTask alloc] init];
+    NSPipe *pipe=[[NSPipe alloc] init];
+    NSFileHandle *handle;
+    NSString *string;
+	NSLog(@"romInfo");
+    
+    [ls setLaunchPath:@"/usr/local/bin/ucon64"];
+    [ls setArguments:[NSArray arrayWithObjects:@ROMFILE,nil]];
+    [ls setStandardOutput:pipe];
+    handle=[pipe fileHandleForReading];
+    
+    [ls launch];
+    
+    string=[[NSString alloc] initWithData:[handle readDataToEndOfFile]
+								 encoding:NSASCIIStringEncoding]; // convert NSData -> NSString
 	
-	NSString *selectedVoice = [availableVoices objectAtIndex:row];
-	[speechSynth setVoice:selectedVoice];
-	NSLog(@"new voice=%@",selectedVoice);
-}
-
--(BOOL)selectionShouldChangeInTableView:(NSTableView *) aTableView
-{
-	if ([speechSynth isSpeaking]){
-		NSBeep();
-		return NO;
-	} else {
-		return YES;
-	}
+ 	NSLog(@"romInfo: %@", string);
+    [textFieldInfo setStringValue:string];
+    [string retain];
+    [pipe release];
+    [ls release];
 }
 
 -(NSSize)windowWillResize:(NSWindow *)send toSize:(NSSize) framesize;
@@ -98,8 +93,7 @@
 }
 
 - (void) dealloc {
-	NSLog(@"dealloc");
-	[speechSynth release];
+	NSLog(@"dealloc");	
 	[super dealloc];
 	
 }

+ 18 - 0
tools/Quickdev16/CommandWrapper.h

@@ -0,0 +1,18 @@
+//
+//  CommandWrapper.h
+//  Quickdev16
+//
+//  Created by David Voswinkel on 09-09-01.
+//  Copyright 2009 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface CommandWrapper : NSObject {
+}
+- (void)doCommand;
+- (NSString *)doPipedCommand;
+- (void)doThreadedCommand;
+
+@end

+ 56 - 0
tools/Quickdev16/CommandWrapper.m

@@ -0,0 +1,56 @@
+//
+//  CommandWrapper.m
+//  Quickdev16
+//
+//  Created by David Voswinkel on 09-09-01.
+//  Copyright 2009 __MyCompanyName__. All rights reserved.
+//
+
+#import "CommandWrapper.h"
+
+
+@implementation CommandWrapper
+
+
+- (void)awakeFromNib {
+	NSLog(@"awakeFromNib");
+}
+
+- (void)doCommand {
+	NSTask *command=[[NSTask alloc] init];
+    [command setLaunchPath:@"/bin/ls"];
+    [command setArguments:[NSArray arrayWithObjects:@"-l",@"/System",nil]];
+    [command launch];
+    [command release];
+}
+
+- (NSString *)doPipedCommand {
+    NSTask *ls=[[NSTask alloc] init];
+    NSPipe *pipe=[[NSPipe alloc] init];
+    NSFileHandle *handle;
+    NSString *string;
+    
+    [ls setLaunchPath:@"/usr/local/bin/ucon64"];
+    [ls setArguments:[NSArray arrayWithObjects:@"/Users/david/Devel/arch/avr/code/quickdev16/roms/super01.smc",nil]];
+    [ls setStandardOutput:pipe];
+    handle=[pipe fileHandleForReading];
+    
+    [ls launch];
+    
+    string=[[NSString alloc] initWithData:[handle readDataToEndOfFile]
+								 encoding:NSASCIIStringEncoding]; // convert NSData -> NSString
+   
+ 	NSLog(@"doPipedCommand: %@", string);
+    //[string retain];
+    [pipe release];
+    [ls release];
+	return string;
+}
+
+
+@end
+
+
+
+
+

+ 96 - 88
tools/Quickdev16/English.lproj/MainMenu.xib

@@ -12,6 +12,7 @@
 		</object>
 		<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
 			<bool key="EncodedWithXMLCoder">YES</bool>
+			<integer value="537"/>
 			<integer value="29"/>
 		</object>
 		<object class="NSArray" key="IBDocument.PluginDependencies">
@@ -1317,21 +1318,21 @@
 			<object class="NSWindowTemplate" id="59616572">
 				<int key="NSWindowStyleMask">263</int>
 				<int key="NSWindowBacking">2</int>
-				<string key="NSWindowRect">{{157, -335}, {504, 796}}</string>
+				<string key="NSWindowRect">{{157, 186}, {480, 275}}</string>
 				<int key="NSWTFlags">611844096</int>
 				<string key="NSWindowTitle">Quickdev16</string>
 				<string key="NSWindowClass">NSWindow</string>
 				<nil key="NSViewClass"/>
 				<string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string>
 				<object class="NSView" key="NSWindowView" id="986949344">
-					<nil key="NSNextResponder"/>
+					<reference key="NSNextResponder"/>
 					<int key="NSvFlags">256</int>
 					<object class="NSMutableArray" key="NSSubviews">
 						<bool key="EncodedWithXMLCoder">YES</bool>
 						<object class="NSTabView" id="77078538">
 							<reference key="NSNextResponder" ref="986949344"/>
 							<int key="NSvFlags">12</int>
-							<string key="NSFrame">{{13, 531}, {454, 213}}</string>
+							<string key="NSFrame">{{13, 10}, {454, 213}}</string>
 							<reference key="NSSuperview" ref="986949344"/>
 							<object class="NSMutableArray" key="NSTabViewItems">
 								<bool key="EncodedWithXMLCoder">YES</bool>
@@ -1342,22 +1343,22 @@
 										<int key="NSvFlags">256</int>
 										<object class="NSMutableArray" key="NSSubviews">
 											<bool key="EncodedWithXMLCoder">YES</bool>
-											<object class="NSTextField" id="596474216">
+											<object class="NSTextField" id="890520319">
 												<reference key="NSNextResponder" ref="832954608"/>
 												<int key="NSvFlags">268</int>
-												<string key="NSFrame">{{-3, -10}, {440, 174}}</string>
+												<string key="NSFrame">{{-3, -3}, {440, 167}}</string>
 												<reference key="NSSuperview" ref="832954608"/>
 												<bool key="NSEnabled">YES</bool>
-												<object class="NSTextFieldCell" key="NSCell" id="151982012">
-													<int key="NSCellFlags">-1805517311</int>
+												<object class="NSTextFieldCell" key="NSCell" id="530085329">
+													<int key="NSCellFlags">-2073952767</int>
 													<int key="NSCellFlags2">272629760</int>
-													<string key="NSContents">Log Field</string>
+													<string key="NSContents"/>
 													<object class="NSFont" key="NSSupport" id="358509667">
 														<string key="NSName">LucidaGrande</string>
 														<double key="NSSize">13</double>
 														<int key="NSfFlags">1044</int>
 													</object>
-													<reference key="NSControlView" ref="596474216"/>
+													<reference key="NSControlView" ref="890520319"/>
 													<bool key="NSDrawsBackground">YES</bool>
 													<object class="NSColor" key="NSBackgroundColor" id="1042038888">
 														<int key="NSColorSpace">6</int>
@@ -1401,18 +1402,18 @@
 										<int key="NSvFlags">256</int>
 										<object class="NSMutableArray" key="NSSubviews">
 											<bool key="EncodedWithXMLCoder">YES</bool>
-											<object class="NSTextField" id="792718861">
+											<object class="NSTextField" id="755945108">
 												<reference key="NSNextResponder" ref="628086759"/>
 												<int key="NSvFlags">268</int>
-												<string key="NSFrame">{{-3, -3}, {440, 174}}</string>
+												<string key="NSFrame">{{-3, -3}, {440, 167}}</string>
 												<reference key="NSSuperview" ref="628086759"/>
 												<bool key="NSEnabled">YES</bool>
-												<object class="NSTextFieldCell" key="NSCell" id="421645926">
+												<object class="NSTextFieldCell" key="NSCell" id="53456849">
 													<int key="NSCellFlags">-1805517311</int>
 													<int key="NSCellFlags2">272629760</int>
-													<string key="NSContents">Info</string>
+													<string key="NSContents"/>
 													<reference key="NSSupport" ref="358509667"/>
-													<reference key="NSControlView" ref="792718861"/>
+													<reference key="NSControlView" ref="755945108"/>
 													<bool key="NSDrawsBackground">YES</bool>
 													<reference key="NSBackgroundColor" ref="1042038888"/>
 													<reference key="NSTextColor" ref="307380714"/>
@@ -1440,7 +1441,7 @@
 						<object class="NSButton" id="595445563">
 							<reference key="NSNextResponder" ref="986949344"/>
 							<int key="NSvFlags">268</int>
-							<string key="NSFrame">{{20, 774}, {106, 23}}</string>
+							<string key="NSFrame">{{20, 253}, {106, 23}}</string>
 							<reference key="NSSuperview" ref="986949344"/>
 							<bool key="NSEnabled">YES</bool>
 							<object class="NSButtonCell" key="NSCell" id="773521235">
@@ -1460,7 +1461,7 @@
 						<object class="NSButton" id="744367094">
 							<reference key="NSNextResponder" ref="986949344"/>
 							<int key="NSvFlags">268</int>
-							<string key="NSFrame">{{20, 745}, {106, 23}}</string>
+							<string key="NSFrame">{{20, 224}, {106, 23}}</string>
 							<reference key="NSSuperview" ref="986949344"/>
 							<bool key="NSEnabled">YES</bool>
 							<object class="NSButtonCell" key="NSCell" id="1047137768">
@@ -1480,7 +1481,7 @@
 						<object class="NSButton" id="784593682">
 							<reference key="NSNextResponder" ref="986949344"/>
 							<int key="NSvFlags">268</int>
-							<string key="NSFrame">{{354, 774}, {106, 23}}</string>
+							<string key="NSFrame">{{354, 253}, {106, 23}}</string>
 							<reference key="NSSuperview" ref="986949344"/>
 							<bool key="NSEnabled">YES</bool>
 							<object class="NSButtonCell" key="NSCell" id="71987855">
@@ -1498,20 +1499,21 @@
 							</object>
 						</object>
 					</object>
-					<string key="NSFrameSize">{504, 796}</string>
+					<string key="NSFrameSize">{480, 275}</string>
+					<reference key="NSSuperview"/>
 				</object>
 				<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
 				<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
 			</object>
-			<object class="NSCustomObject" id="976324537">
-				<string key="NSClassName">Quickdev16AppDelegate</string>
-			</object>
 			<object class="NSCustomObject" id="755631768">
 				<string key="NSClassName">NSFontManager</string>
 			</object>
 			<object class="NSCustomObject" id="885823689">
 				<string key="NSClassName">AppController</string>
 			</object>
+			<object class="NSCustomObject" id="180404883">
+				<string key="NSClassName">CommandWrapper</string>
+			</object>
 		</object>
 		<object class="IBObjectContainer" key="IBDocument.Objects">
 			<object class="NSMutableArray" key="connectionRecords">
@@ -2076,14 +2078,6 @@
 					</object>
 					<int key="connectionID">493</int>
 				</object>
-				<object class="IBConnectionRecord">
-					<object class="IBOutletConnection" key="connection">
-						<string key="label">delegate</string>
-						<reference key="source" ref="1021"/>
-						<reference key="destination" ref="976324537"/>
-					</object>
-					<int key="connectionID">495</int>
-				</object>
 				<object class="IBConnectionRecord">
 					<object class="IBActionConnection" key="connection">
 						<string key="label">alignCenter:</string>
@@ -2190,11 +2184,43 @@
 				</object>
 				<object class="IBConnectionRecord">
 					<object class="IBActionConnection" key="connection">
-						<string key="label">sayIt:</string>
+						<string key="label">romUpload:</string>
+						<reference key="source" ref="885823689"/>
+						<reference key="destination" ref="1047137768"/>
+					</object>
+					<int key="connectionID">593</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">romInfo:</string>
+						<reference key="source" ref="885823689"/>
+						<reference key="destination" ref="784593682"/>
+					</object>
+					<int key="connectionID">604</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">textFieldLog</string>
+						<reference key="source" ref="180404883"/>
+						<reference key="destination" ref="755945108"/>
+					</object>
+					<int key="connectionID">614</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">textFieldLog</string>
+						<reference key="source" ref="885823689"/>
+						<reference key="destination" ref="890520319"/>
+					</object>
+					<int key="connectionID">619</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">textFieldInfo</string>
 						<reference key="source" ref="885823689"/>
-						<reference key="destination" ref="71987855"/>
+						<reference key="destination" ref="755945108"/>
 					</object>
-					<int key="connectionID">592</int>
+					<int key="connectionID">620</int>
 				</object>
 			</object>
 			<object class="IBMutableOrderedSet" key="objectRecords">
@@ -3075,11 +3101,6 @@
 						<reference key="object" ref="105068016"/>
 						<reference key="parent" ref="992780483"/>
 					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">494</int>
-						<reference key="object" ref="976324537"/>
-						<reference key="parent" ref="0"/>
-					</object>
 					<object class="IBObjectRecord">
 						<int key="objectID">496</int>
 						<reference key="object" ref="215659978"/>
@@ -3277,7 +3298,7 @@
 						<reference key="object" ref="628086759"/>
 						<object class="NSMutableArray" key="children">
 							<bool key="EncodedWithXMLCoder">YES</bool>
-							<reference ref="792718861"/>
+							<reference ref="755945108"/>
 						</object>
 						<reference key="parent" ref="202585700"/>
 					</object>
@@ -3286,7 +3307,7 @@
 						<reference key="object" ref="832954608"/>
 						<object class="NSMutableArray" key="children">
 							<bool key="EncodedWithXMLCoder">YES</bool>
-							<reference ref="596474216"/>
+							<reference ref="890520319"/>
 						</object>
 						<reference key="parent" ref="1019619514"/>
 					</object>
@@ -3333,37 +3354,42 @@
 						<reference key="parent" ref="744367094"/>
 					</object>
 					<object class="IBObjectRecord">
-						<int key="objectID">587</int>
-						<reference key="object" ref="596474216"/>
+						<int key="objectID">591</int>
+						<reference key="object" ref="885823689"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">595</int>
+						<reference key="object" ref="180404883"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">608</int>
+						<reference key="object" ref="890520319"/>
 						<object class="NSMutableArray" key="children">
 							<bool key="EncodedWithXMLCoder">YES</bool>
-							<reference ref="151982012"/>
+							<reference ref="530085329"/>
 						</object>
 						<reference key="parent" ref="832954608"/>
 					</object>
 					<object class="IBObjectRecord">
-						<int key="objectID">588</int>
-						<reference key="object" ref="151982012"/>
-						<reference key="parent" ref="596474216"/>
+						<int key="objectID">609</int>
+						<reference key="object" ref="530085329"/>
+						<reference key="parent" ref="890520319"/>
 					</object>
 					<object class="IBObjectRecord">
-						<int key="objectID">589</int>
-						<reference key="object" ref="792718861"/>
+						<int key="objectID">611</int>
+						<reference key="object" ref="755945108"/>
 						<object class="NSMutableArray" key="children">
 							<bool key="EncodedWithXMLCoder">YES</bool>
-							<reference ref="421645926"/>
+							<reference ref="53456849"/>
 						</object>
 						<reference key="parent" ref="628086759"/>
 					</object>
 					<object class="IBObjectRecord">
-						<int key="objectID">590</int>
-						<reference key="object" ref="421645926"/>
-						<reference key="parent" ref="792718861"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">591</int>
-						<reference key="object" ref="885823689"/>
-						<reference key="parent" ref="0"/>
+						<int key="objectID">612</int>
+						<reference key="object" ref="53456849"/>
+						<reference key="parent" ref="755945108"/>
 					</object>
 				</object>
 			</object>
@@ -3604,10 +3630,10 @@
 					<string>57.editorWindowContentRectSynchronizationRect</string>
 					<string>58.IBPluginDependency</string>
 					<string>58.ImportedFromIB2</string>
-					<string>587.IBPluginDependency</string>
-					<string>588.IBPluginDependency</string>
-					<string>589.IBPluginDependency</string>
-					<string>590.IBPluginDependency</string>
+					<string>608.IBPluginDependency</string>
+					<string>609.IBPluginDependency</string>
+					<string>611.IBPluginDependency</string>
+					<string>612.IBPluginDependency</string>
 					<string>72.IBPluginDependency</string>
 					<string>72.ImportedFromIB2</string>
 					<string>73.IBPluginDependency</string>
@@ -3846,9 +3872,9 @@
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>{{936, 60}, {504, 796}}</string>
+					<string>{{334, 517}, {480, 275}}</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>{{936, 60}, {504, 796}}</string>
+					<string>{{334, 517}, {480, 275}}</string>
 					<boolean value="YES"/>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
 					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -3918,7 +3944,7 @@
 				</object>
 			</object>
 			<nil key="sourceID"/>
-			<int key="maxID">592</int>
+			<int key="maxID">620</int>
 		</object>
 		<object class="IBClassDescriber" key="IBDocument.Classes">
 			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -3930,31 +3956,25 @@
 						<bool key="EncodedWithXMLCoder">YES</bool>
 						<object class="NSArray" key="dict.sortedKeys">
 							<bool key="EncodedWithXMLCoder">YES</bool>
-							<string>changeTextColor:</string>
-							<string>sayIt:</string>
-							<string>stopIt:</string>
+							<string>romInfo:</string>
+							<string>romUpload:</string>
 						</object>
 						<object class="NSMutableArray" key="dict.values">
 							<bool key="EncodedWithXMLCoder">YES</bool>
 							<string>id</string>
 							<string>id</string>
-							<string>id</string>
 						</object>
 					</object>
 					<object class="NSMutableDictionary" key="outlets">
 						<bool key="EncodedWithXMLCoder">YES</bool>
 						<object class="NSArray" key="dict.sortedKeys">
 							<bool key="EncodedWithXMLCoder">YES</bool>
-							<string>colorWell</string>
-							<string>stopButton</string>
-							<string>tableView</string>
-							<string>textField</string>
+							<string>textFieldInfo</string>
+							<string>textFieldLog</string>
 						</object>
 						<object class="NSMutableArray" key="dict.values">
 							<bool key="EncodedWithXMLCoder">YES</bool>
-							<string>NSColorWell</string>
-							<string>NSButton</string>
-							<string>NSTableView</string>
+							<string>NSTextField</string>
 							<string>NSTextField</string>
 						</object>
 					</object>
@@ -3964,15 +3984,11 @@
 					</object>
 				</object>
 				<object class="IBPartialClassDescription">
-					<string key="className">Quickdev16AppDelegate</string>
+					<string key="className">CommandWrapper</string>
 					<string key="superclassName">NSObject</string>
-					<object class="NSMutableDictionary" key="outlets">
-						<string key="NS.key.0">window</string>
-						<string key="NS.object.0">NSWindow</string>
-					</object>
 					<object class="IBClassDescriptionSource" key="sourceIdentifier">
 						<string key="majorKey">IBProjectSource</string>
-						<string key="minorKey">Quickdev16AppDelegate.h</string>
+						<string key="minorKey">CommandWrapper.h</string>
 					</object>
 				</object>
 			</object>
@@ -4061,14 +4077,6 @@
 						<string key="minorKey">AppKit.framework/Headers/NSCell.h</string>
 					</object>
 				</object>
-				<object class="IBPartialClassDescription">
-					<string key="className">NSColorWell</string>
-					<string key="superclassName">NSControl</string>
-					<object class="IBClassDescriptionSource" key="sourceIdentifier">
-						<string key="majorKey">IBFrameworkSource</string>
-						<string key="minorKey">AppKit.framework/Headers/NSColorWell.h</string>
-					</object>
-				</object>
 				<object class="IBPartialClassDescription">
 					<string key="className">NSControl</string>
 					<string key="superclassName">NSView</string>

+ 55 - 32
tools/Quickdev16/Quickdev16.xcodeproj/david.mode1v3

@@ -264,14 +264,17 @@
 							<key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
 							<array>
 								<string>29B97314FDCFA39411CA2CEA</string>
-								<string>29B97323FDCFA39411CA2CEA</string>
+								<string>080E96DDFE201D6D7F000001</string>
+								<string>29B97315FDCFA39411CA2CEA</string>
+								<string>29B97317FDCFA39411CA2CEA</string>
 								<string>19C28FACFE9D520D11CA2CBB</string>
 								<string>1C37FABC05509CD000000102</string>
 							</array>
 							<key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
 							<array>
 								<array>
-									<integer>3</integer>
+									<integer>2</integer>
+									<integer>1</integer>
 									<integer>0</integer>
 								</array>
 							</array>
@@ -306,12 +309,14 @@
 					<key>Dock</key>
 					<array>
 						<dict>
+							<key>BecomeActive</key>
+							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
 								<key>PBXProjectModuleGUID</key>
 								<string>1CE0B20306471E060097A5F4</string>
 								<key>PBXProjectModuleLabel</key>
-								<string></string>
+								<string>AppController.m</string>
 								<key>PBXSplitModuleInNavigatorKey</key>
 								<dict>
 									<key>Split0</key>
@@ -319,7 +324,19 @@
 										<key>PBXProjectModuleGUID</key>
 										<string>1CE0B20406471E060097A5F4</string>
 										<key>PBXProjectModuleLabel</key>
-										<string></string>
+										<string>AppController.m</string>
+										<key>_historyCapacity</key>
+										<integer>0</integer>
+										<key>bookmark</key>
+										<string>63F767641054DD5C00ACB8E1</string>
+										<key>history</key>
+										<array>
+											<string>63F766F41052D56400ACB8E1</string>
+											<string>63F766F51052D56400ACB8E1</string>
+											<string>63F7670F1052D93A00ACB8E1</string>
+											<string>63F767561054069800ACB8E1</string>
+											<string>63F767581054069800ACB8E1</string>
+										</array>
 									</dict>
 									<key>SplitCount</key>
 									<string>1</string>
@@ -330,18 +347,16 @@
 							<key>GeometryConfiguration</key>
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 0}, {654, 0}}</string>
+								<string>{{0, 0}, {654, 502}}</string>
 								<key>RubberWindowFrame</key>
 								<string>0 115 936 763 0 0 1440 878 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXNavigatorGroup</string>
 							<key>Proportion</key>
-							<string>0pt</string>
+							<string>502pt</string>
 						</dict>
 						<dict>
-							<key>BecomeActive</key>
-							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
 								<key>PBXProjectModuleGUID</key>
@@ -352,14 +367,14 @@
 							<key>GeometryConfiguration</key>
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 5}, {654, 717}}</string>
+								<string>{{0, 507}, {654, 215}}</string>
 								<key>RubberWindowFrame</key>
 								<string>0 115 936 763 0 0 1440 878 </string>
 							</dict>
 							<key>Module</key>
 							<string>XCDetailModule</string>
 							<key>Proportion</key>
-							<string>717pt</string>
+							<string>215pt</string>
 						</dict>
 					</array>
 					<key>Proportion</key>
@@ -378,9 +393,9 @@
 			</array>
 			<key>TableOfContents</key>
 			<array>
-				<string>631A5172104CF32500E73564</string>
+				<string>63F766B41052CDD000ACB8E1</string>
 				<string>1CE0B1FE06471DED0097A5F4</string>
-				<string>631A5173104CF32500E73564</string>
+				<string>63F766B51052CDD000ACB8E1</string>
 				<string>1CE0B20306471E060097A5F4</string>
 				<string>1CE0B20506471E060097A5F4</string>
 			</array>
@@ -518,8 +533,12 @@
 	<integer>5</integer>
 	<key>WindowOrderList</key>
 	<array>
+		<string>63F766BF1052CDD000ACB8E1</string>
+		<string>63F766C01052CDD000ACB8E1</string>
+		<string>1CD10A99069EF8BA00B06720</string>
 		<string>631E1885104C5D80001A8B18</string>
 		<string>/Users/david/Devel/arch/avr/code/quickdev16/tools/Quickdev16/Quickdev16.xcodeproj</string>
+		<string>1C78EAAD065D492600B07095</string>
 	</array>
 	<key>WindowString</key>
 	<string>0 115 936 763 0 0 1440 878 </string>
@@ -543,7 +562,7 @@
 								<key>PBXProjectModuleGUID</key>
 								<string>1CD0528F0623707200166675</string>
 								<key>PBXProjectModuleLabel</key>
-								<string></string>
+								<string>AppController.m</string>
 								<key>StatusBarVisibility</key>
 								<true/>
 							</dict>
@@ -560,6 +579,8 @@
 							<string>218pt</string>
 						</dict>
 						<dict>
+							<key>BecomeActive</key>
+							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
 								<key>PBXProjectModuleGUID</key>
@@ -599,7 +620,7 @@
 			<key>TableOfContents</key>
 			<array>
 				<string>631E1885104C5D80001A8B18</string>
-				<string>631A5174104CF32500E73564</string>
+				<string>63F766B61052CDD000ACB8E1</string>
 				<string>1CD0528F0623707200166675</string>
 				<string>XCMainBuildResultsModuleGUID</string>
 			</array>
@@ -643,8 +664,8 @@
 										<string>yes</string>
 										<key>sizes</key>
 										<array>
-											<string>{{0, 0}, {316, 185}}</string>
-											<string>{{316, 0}, {378, 185}}</string>
+											<string>{{0, 0}, {316, 201}}</string>
+											<string>{{316, 0}, {378, 201}}</string>
 										</array>
 									</dict>
 									<key>VerticalSplitView</key>
@@ -659,8 +680,8 @@
 										<string>yes</string>
 										<key>sizes</key>
 										<array>
-											<string>{{0, 0}, {694, 185}}</string>
-											<string>{{0, 185}, {694, 196}}</string>
+											<string>{{0, 0}, {694, 201}}</string>
+											<string>{{0, 201}, {694, 180}}</string>
 										</array>
 									</dict>
 								</dict>
@@ -693,7 +714,7 @@
 										<real>148</real>
 									</array>
 									<key>Frame</key>
-									<string>{{316, 0}, {378, 185}}</string>
+									<string>{{316, 0}, {378, 201}}</string>
 									<key>RubberWindowFrame</key>
 									<string>21 433 694 422 0 0 1440 878 </string>
 								</dict>
@@ -721,13 +742,13 @@
 			<key>TableOfContents</key>
 			<array>
 				<string>1CD10A99069EF8BA00B06720</string>
-				<string>631E189F104CF2FD001A8B18</string>
+				<string>63F766B71052CDD000ACB8E1</string>
 				<string>1C162984064C10D400B95A72</string>
-				<string>631E18A0104CF2FD001A8B18</string>
-				<string>631E18A1104CF2FD001A8B18</string>
-				<string>631E18A2104CF2FD001A8B18</string>
-				<string>631E18A3104CF2FD001A8B18</string>
-				<string>631E18A4104CF2FD001A8B18</string>
+				<string>63F766B81052CDD000ACB8E1</string>
+				<string>63F766B91052CDD000ACB8E1</string>
+				<string>63F766BA1052CDD000ACB8E1</string>
+				<string>63F766BB1052CDD000ACB8E1</string>
+				<string>63F766BC1052CDD000ACB8E1</string>
 			</array>
 			<key>ToolbarConfiguration</key>
 			<string>xcode.toolbar.config.debugV3</string>
@@ -854,6 +875,8 @@
 					<key>Dock</key>
 					<array>
 						<dict>
+							<key>BecomeActive</key>
+							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
 								<key>PBXProjectModuleGUID</key>
@@ -864,18 +887,18 @@
 							<key>GeometryConfiguration</key>
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 0}, {650, 209}}</string>
+								<string>{{0, 0}, {504, 721}}</string>
 								<key>RubberWindowFrame</key>
-								<string>21 605 650 250 0 0 1440 878 </string>
+								<string>936 116 504 762 0 0 1440 878 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXDebugCLIModule</string>
 							<key>Proportion</key>
-							<string>209pt</string>
+							<string>721pt</string>
 						</dict>
 					</array>
 					<key>Proportion</key>
-					<string>209pt</string>
+					<string>721pt</string>
 				</dict>
 			</array>
 			<key>Name</key>
@@ -889,17 +912,17 @@
 			<key>TableOfContents</key>
 			<array>
 				<string>1C78EAAD065D492600B07095</string>
-				<string>631E18A5104CF2FD001A8B18</string>
+				<string>63F766BD1052CDD000ACB8E1</string>
 				<string>1C78EAAC065D492600B07095</string>
 			</array>
 			<key>ToolbarConfiguration</key>
 			<string>xcode.toolbar.config.consoleV3</string>
 			<key>WindowString</key>
-			<string>21 605 650 250 0 0 1440 878 </string>
+			<string>936 116 504 762 0 0 1440 878 </string>
 			<key>WindowToolGUID</key>
 			<string>1C78EAAD065D492600B07095</string>
 			<key>WindowToolIsVisible</key>
-			<false/>
+			<true/>
 		</dict>
 		<dict>
 			<key>Identifier</key>

+ 558 - 24
tools/Quickdev16/Quickdev16.xcodeproj/david.pbxuser

@@ -1,20 +1,5 @@
 // !$*UTF8*$!
 {
-	256AC3D80F4B6AC300CF3369 /* Quickdev16AppDelegate.h */ = {
-		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {593, 472}}";
-			sepNavSelRange = "{353, 0}";
-			sepNavVisRange = "{0, 353}";
-			sepNavWindowFrame = "{{15, 60}, {992, 818}}";
-		};
-	};
-	256AC3D90F4B6AC300CF3369 /* Quickdev16AppDelegate.m */ = {
-		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {593, 472}}";
-			sepNavSelRange = "{0, 0}";
-			sepNavVisRange = "{0, 384}";
-		};
-	};
 	256AC3F00F4B6AF500CF3369 /* Quickdev16_Prefix.pch */ = {
 		uiCtxt = {
 			sepNavIntBoundsRect = "{{0, 0}, {656, 183}}";
@@ -23,12 +8,17 @@
 		};
 	};
 	29B97313FDCFA39411CA2CEA /* Project object */ = {
+		activeArchitecturePreference = i386;
 		activeBuildConfigurationName = Debug;
 		activeExecutable = 631E1874104C5D72001A8B18 /* Quickdev16 */;
+		activeSDKPreference = macosx10.6;
 		activeTarget = 8D1107260486CEB800E47090 /* Quickdev16 */;
 		addToTargets = (
 			8D1107260486CEB800E47090 /* Quickdev16 */,
 		);
+		breakpoints = (
+			63F766C41052D19000ACB8E1 /* CommandWrapper.h:13 */,
+		);
 		codeSenseManager = 631E1888104C5D80001A8B18 /* Code sense */;
 		executables = (
 			631E1874104C5D72001A8B18 /* Quickdev16 */,
@@ -56,25 +46,90 @@
 					PBXFileDataSource_Target_ColumnID,
 				);
 			};
-			PBXPerProjectTemplateStateSaveDate = 273478436;
-			PBXWorkspaceStateSaveDate = 273478436;
+			PBXPerProjectTemplateStateSaveDate = 273862070;
+			PBXWorkspaceStateSaveDate = 273862070;
+		};
+		perUserProjectItems = {
+			63B6D2051050696F002672A2 = 63B6D2051050696F002672A2 /* PBXTextBookmark */;
+			63B6D2061050696F002672A2 = 63B6D2061050696F002672A2 /* PBXTextBookmark */;
+			63B6D2071050696F002672A2 = 63B6D2071050696F002672A2 /* PBXTextBookmark */;
+			63B6D2081050696F002672A2 = 63B6D2081050696F002672A2 /* PBXTextBookmark */;
+			63B6D21D10517F9F002672A2 = 63B6D21D10517F9F002672A2 /* PBXTextBookmark */;
+			63F766B31052CDD000ACB8E1 /* PBXTextBookmark */ = 63F766B31052CDD000ACB8E1 /* PBXTextBookmark */;
+			63F766C91052D1D800ACB8E1 /* XCBuildMessageTextBookmark */ = 63F766C91052D1D800ACB8E1 /* XCBuildMessageTextBookmark */;
+			63F766CA1052D1D800ACB8E1 /* PBXTextBookmark */ = 63F766CA1052D1D800ACB8E1 /* PBXTextBookmark */;
+			63F766CD1052D1F500ACB8E1 /* PBXTextBookmark */ = 63F766CD1052D1F500ACB8E1 /* PBXTextBookmark */;
+			63F766CE1052D1F500ACB8E1 /* XCBuildMessageTextBookmark */ = 63F766CE1052D1F500ACB8E1 /* XCBuildMessageTextBookmark */;
+			63F766CF1052D1F500ACB8E1 /* PBXTextBookmark */ = 63F766CF1052D1F500ACB8E1 /* PBXTextBookmark */;
+			63F766D21052D20200ACB8E1 /* PBXTextBookmark */ = 63F766D21052D20200ACB8E1 /* PBXTextBookmark */;
+			63F766D31052D20200ACB8E1 /* PBXTextBookmark */ = 63F766D31052D20200ACB8E1 /* PBXTextBookmark */;
+			63F766D41052D20200ACB8E1 /* PBXTextBookmark */ = 63F766D41052D20200ACB8E1 /* PBXTextBookmark */;
+			63F766D51052D20200ACB8E1 /* XCBuildMessageTextBookmark */ = 63F766D51052D20200ACB8E1 /* XCBuildMessageTextBookmark */;
+			63F766D61052D20200ACB8E1 /* PBXTextBookmark */ = 63F766D61052D20200ACB8E1 /* PBXTextBookmark */;
+			63F766D91052D24400ACB8E1 /* PBXTextBookmark */ = 63F766D91052D24400ACB8E1 /* PBXTextBookmark */;
+			63F766DA1052D24400ACB8E1 /* PBXTextBookmark */ = 63F766DA1052D24400ACB8E1 /* PBXTextBookmark */;
+			63F766DB1052D24400ACB8E1 /* PBXTextBookmark */ = 63F766DB1052D24400ACB8E1 /* PBXTextBookmark */;
+			63F766E51052D2C200ACB8E1 /* PBXTextBookmark */ = 63F766E51052D2C200ACB8E1 /* PBXTextBookmark */;
+			63F766E61052D2C200ACB8E1 /* PBXTextBookmark */ = 63F766E61052D2C200ACB8E1 /* PBXTextBookmark */;
+			63F766E71052D2C200ACB8E1 /* PBXTextBookmark */ = 63F766E71052D2C200ACB8E1 /* PBXTextBookmark */;
+			63F766E81052D2C200ACB8E1 /* PBXTextBookmark */ = 63F766E81052D2C200ACB8E1 /* PBXTextBookmark */;
+			63F766F31052D56400ACB8E1 /* PBXTextBookmark */ = 63F766F31052D56400ACB8E1 /* PBXTextBookmark */;
+			63F766F41052D56400ACB8E1 /* PBXTextBookmark */ = 63F766F41052D56400ACB8E1 /* PBXTextBookmark */;
+			63F766F51052D56400ACB8E1 /* PBXTextBookmark */ = 63F766F51052D56400ACB8E1 /* PBXTextBookmark */;
+			63F766F61052D56400ACB8E1 /* PBXTextBookmark */ = 63F766F61052D56400ACB8E1 /* PBXTextBookmark */;
+			63F766F71052D56400ACB8E1 /* PBXTextBookmark */ = 63F766F71052D56400ACB8E1 /* PBXTextBookmark */;
+			63F766FD1052D57C00ACB8E1 /* PBXTextBookmark */ = 63F766FD1052D57C00ACB8E1 /* PBXTextBookmark */;
+			63F767011052D6A000ACB8E1 /* PBXTextBookmark */ = 63F767011052D6A000ACB8E1 /* PBXTextBookmark */;
+			63F767021052D6D700ACB8E1 /* PBXTextBookmark */ = 63F767021052D6D700ACB8E1 /* PBXTextBookmark */;
+			63F767051052D70400ACB8E1 /* PBXTextBookmark */ = 63F767051052D70400ACB8E1 /* PBXTextBookmark */;
+			63F767061052D70400ACB8E1 /* PBXTextBookmark */ = 63F767061052D70400ACB8E1 /* PBXTextBookmark */;
+			63F767071052D70400ACB8E1 /* PBXTextBookmark */ = 63F767071052D70400ACB8E1 /* PBXTextBookmark */;
+			63F7670F1052D93A00ACB8E1 /* PBXTextBookmark */ = 63F7670F1052D93A00ACB8E1 /* PBXTextBookmark */;
+			63F767101052D93A00ACB8E1 /* PBXTextBookmark */ = 63F767101052D93A00ACB8E1 /* PBXTextBookmark */;
+			63F767111052D93A00ACB8E1 /* PBXTextBookmark */ = 63F767111052D93A00ACB8E1 /* PBXTextBookmark */;
+			63F767171052DA7500ACB8E1 /* PBXTextBookmark */ = 63F767171052DA7500ACB8E1 /* PBXTextBookmark */;
+			63F7671B1052DAA800ACB8E1 /* PBXTextBookmark */ = 63F7671B1052DAA800ACB8E1 /* PBXTextBookmark */;
+			63F767461052DC9E00ACB8E1 /* PBXTextBookmark */ = 63F767461052DC9E00ACB8E1 /* PBXTextBookmark */;
+			63F7674E1052DCDE00ACB8E1 /* PBXTextBookmark */ = 63F7674E1052DCDE00ACB8E1 /* PBXTextBookmark */;
+			63F767531053FF8F00ACB8E1 /* PBXTextBookmark */ = 63F767531053FF8F00ACB8E1 /* PBXTextBookmark */;
+			63F767561054069800ACB8E1 /* PBXTextBookmark */ = 63F767561054069800ACB8E1 /* PBXTextBookmark */;
+			63F767581054069800ACB8E1 /* PBXTextBookmark */ = 63F767581054069800ACB8E1 /* PBXTextBookmark */;
+			63F767591054069800ACB8E1 /* PBXTextBookmark */ = 63F767591054069800ACB8E1 /* PBXTextBookmark */;
+			63F767641054DD5C00ACB8E1 /* PBXTextBookmark */ = 63F767641054DD5C00ACB8E1 /* PBXTextBookmark */;
 		};
 		sourceControlManager = 631E1887104C5D80001A8B18 /* Source Control */;
 		userBuildSettings = {
 		};
 	};
+	630AE5C8104D62CE0091039C /* CommandWrapper.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {593, 447}}";
+			sepNavSelRange = "{215, 0}";
+			sepNavVisRange = "{0, 298}";
+			sepNavWindowFrame = "{{15, 60}, {992, 818}}";
+		};
+	};
+	630AE5C9104D62CE0091039C /* CommandWrapper.m */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {831, 689}}";
+			sepNavSelRange = "{517, 646}";
+			sepNavVisRange = "{210, 971}";
+			sepNavWindowFrame = "{{15, 60}, {992, 818}}";
+		};
+	};
 	631A51A6104CF59900E73564 /* AppController.h */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {593, 449}}";
-			sepNavSelRange = "{0, 0}";
-			sepNavVisRange = "{0, 614}";
+			sepNavIntBoundsRect = "{{0, 0}, {933, 667}}";
+			sepNavSelRange = "{77, 11}";
+			sepNavVisRange = "{0, 218}";
+			sepNavWindowFrame = "{{15, 60}, {992, 818}}";
 		};
 	};
 	631A51A9104CF5B700E73564 /* AppController.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {933, 1261}}";
-			sepNavSelRange = "{782, 0}";
-			sepNavVisRange = "{597, 1402}";
+			sepNavIntBoundsRect = "{{0, 0}, {719, 1235}}";
+			sepNavSelRange = "{1356, 0}";
+			sepNavVisRange = "{913, 928}";
 			sepNavWindowFrame = "{{15, 60}, {992, 818}}";
 		};
 	};
@@ -85,7 +140,7 @@
 		argumentStrings = (
 		);
 		autoAttachOnCrash = 1;
-		breakpointsEnabled = 0;
+		breakpointsEnabled = 1;
 		configStateDict = {
 		};
 		customDataFormattersEnabled = 1;
@@ -107,6 +162,8 @@
 		showTypeColumn = 0;
 		sourceDirectories = (
 		);
+		variableFormatDictionary = {
+		};
 	};
 	631E1887104C5D80001A8B18 /* Source Control */ = {
 		isa = PBXSourceControlManager;
@@ -122,6 +179,483 @@
 		isa = PBXCodeSenseManager;
 		indexTemplatePath = "";
 	};
+	63B6D2051050696F002672A2 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 11";
+		rLen = 146;
+		rLoc = 264;
+		rType = 0;
+		vrLen = 934;
+		vrLoc = 0;
+	};
+	63B6D2061050696F002672A2 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A6104CF59900E73564 /* AppController.h */;
+		name = "AppController.h: 8";
+		rLen = 0;
+		rLoc = 211;
+		rType = 0;
+		vrLen = 176;
+		vrLoc = 0;
+	};
+	63B6D2071050696F002672A2 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 630AE5C8104D62CE0091039C /* CommandWrapper.h */;
+		name = "CommandWrapper.h: 13";
+		rLen = 0;
+		rLoc = 214;
+		rType = 0;
+		vrLen = 338;
+		vrLoc = 0;
+	};
+	63B6D2081050696F002672A2 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		comments = "'textField' undeclared (first use in this function)";
+		fRef = 630AE5C9104D62CE0091039C /* CommandWrapper.m */;
+		rLen = 1;
+		rLoc = 15;
+		rType = 1;
+	};
+	63B6D21D10517F9F002672A2 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 630AE5C9104D62CE0091039C /* CommandWrapper.m */;
+		name = "CommandWrapper.m: 16";
+		rLen = 0;
+		rLoc = 234;
+		rType = 0;
+		vrLen = 1566;
+		vrLoc = 564;
+	};
+	63F766B31052CDD000ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 630AE5C9104D62CE0091039C /* CommandWrapper.m */;
+		name = "CommandWrapper.m: 77";
+		rLen = 0;
+		rLoc = 1183;
+		rType = 0;
+		vrLen = 1576;
+		vrLoc = 533;
+	};
+	63F766C41052D19000ACB8E1 /* CommandWrapper.h:13 */ = {
+		isa = PBXFileBreakpoint;
+		actions = (
+		);
+		breakpointStyle = 0;
+		continueAfterActions = 0;
+		countType = 0;
+		delayBeforeContinue = 0;
+		fileReference = 630AE5C8104D62CE0091039C /* CommandWrapper.h */;
+		hitCount = 0;
+		ignoreCount = 0;
+		lineNumber = 13;
+		modificationTime = 273942772.37427;
+		originalNumberOfMultipleMatches = 1;
+		state = 2;
+	};
+	63F766C91052D1D800ACB8E1 /* XCBuildMessageTextBookmark */ = {
+		isa = PBXTextBookmark;
+		comments = "'textFieldLog' undeclared (first use in this function)";
+		fRef = 630AE5C9104D62CE0091039C /* CommandWrapper.m */;
+		fallbackIsa = XCBuildMessageTextBookmark;
+		rLen = 1;
+		rLoc = 15;
+		rType = 1;
+	};
+	63F766CA1052D1D800ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 630AE5C9104D62CE0091039C /* CommandWrapper.m */;
+		name = "CommandWrapper.m: 16";
+		rLen = 0;
+		rLoc = 234;
+		rType = 0;
+		vrLen = 336;
+		vrLoc = 149;
+	};
+	63F766CD1052D1F500ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 630AE5C9104D62CE0091039C /* CommandWrapper.m */;
+		name = "CommandWrapper.m: 16";
+		rLen = 0;
+		rLoc = 234;
+		rType = 0;
+		vrLen = 311;
+		vrLoc = 149;
+	};
+	63F766CE1052D1F500ACB8E1 /* XCBuildMessageTextBookmark */ = {
+		isa = PBXTextBookmark;
+		comments = "Expected ']' before 'setStringValue'";
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		fallbackIsa = XCBuildMessageTextBookmark;
+		rLen = 1;
+		rLoc = 16;
+		rType = 1;
+	};
+	63F766CF1052D1F500ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 13";
+		rLen = 0;
+		rLoc = 335;
+		rType = 0;
+		vrLen = 278;
+		vrLoc = 98;
+	};
+	63F766D21052D20200ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A6104CF59900E73564 /* AppController.h */;
+		name = "AppController.h: 6";
+		rLen = 0;
+		rLoc = 142;
+		rType = 0;
+		vrLen = 214;
+		vrLoc = 0;
+	};
+	63F766D31052D20200ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 630AE5C8104D62CE0091039C /* CommandWrapper.h */;
+		name = "CommandWrapper.h: 13";
+		rLen = 0;
+		rLoc = 215;
+		rType = 0;
+		vrLen = 298;
+		vrLoc = 0;
+	};
+	63F766D41052D20200ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 630AE5C9104D62CE0091039C /* CommandWrapper.m */;
+		name = "CommandWrapper.m: 16";
+		rLen = 0;
+		rLoc = 234;
+		rType = 0;
+		vrLen = 1151;
+		vrLoc = 42;
+	};
+	63F766D51052D20200ACB8E1 /* XCBuildMessageTextBookmark */ = {
+		isa = PBXTextBookmark;
+		comments = "Expected ']' before 'setStringValue'";
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		fallbackIsa = XCBuildMessageTextBookmark;
+		rLen = 1;
+		rLoc = 16;
+		rType = 1;
+	};
+	63F766D61052D20200ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 12";
+		rLen = 0;
+		rLoc = 334;
+		rType = 0;
+		vrLen = 1195;
+		vrLoc = 0;
+	};
+	63F766D91052D24400ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A6104CF59900E73564 /* AppController.h */;
+		name = "AppController.h: 5";
+		rLen = 0;
+		rLoc = 140;
+		rType = 0;
+		vrLen = 218;
+		vrLoc = 0;
+	};
+	63F766DA1052D24400ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 13";
+		rLen = 0;
+		rLoc = 350;
+		rType = 0;
+		vrLen = 1199;
+		vrLoc = 0;
+	};
+	63F766DB1052D24400ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 44";
+		rLen = 0;
+		rLoc = 1373;
+		rType = 0;
+		vrLen = 1062;
+		vrLoc = 657;
+	};
+	63F766E51052D2C200ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A6104CF59900E73564 /* AppController.h */;
+		name = "AppController.h: 5";
+		rLen = 0;
+		rLoc = 140;
+		rType = 0;
+		vrLen = 218;
+		vrLoc = 0;
+	};
+	63F766E61052D2C200ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 630AE5C8104D62CE0091039C /* CommandWrapper.h */;
+		name = "CommandWrapper.h: 13";
+		rLen = 0;
+		rLoc = 215;
+		rType = 0;
+		vrLen = 298;
+		vrLoc = 0;
+	};
+	63F766E71052D2C200ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 44";
+		rLen = 0;
+		rLoc = 1373;
+		rType = 0;
+		vrLen = 1158;
+		vrLoc = 0;
+	};
+	63F766E81052D2C200ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 44";
+		rLen = 0;
+		rLoc = 1373;
+		rType = 0;
+		vrLen = 801;
+		vrLoc = 0;
+	};
+	63F766F31052D56400ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 630AE5C9104D62CE0091039C /* CommandWrapper.m */;
+		name = "CommandWrapper.m: 33";
+		rLen = 21;
+		rLoc = 672;
+		rType = 0;
+		vrLen = 970;
+		vrLoc = 209;
+	};
+	63F766F41052D56400ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 630AE5C8104D62CE0091039C /* CommandWrapper.h */;
+		name = "CommandWrapper.h: 13";
+		rLen = 0;
+		rLoc = 215;
+		rType = 0;
+		vrLen = 298;
+		vrLoc = 0;
+	};
+	63F766F51052D56400ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A6104CF59900E73564 /* AppController.h */;
+		name = "AppController.h: 5";
+		rLen = 0;
+		rLoc = 140;
+		rType = 0;
+		vrLen = 218;
+		vrLoc = 0;
+	};
+	63F766F61052D56400ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 24";
+		rLen = 0;
+		rLoc = 601;
+		rType = 0;
+		vrLen = 807;
+		vrLoc = 0;
+	};
+	63F766F71052D56400ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 27";
+		rLen = 0;
+		rLoc = 607;
+		rType = 0;
+		vrLen = 869;
+		vrLoc = 329;
+	};
+	63F766FD1052D57C00ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 4";
+		rLen = 0;
+		rLoc = 92;
+		rType = 0;
+		vrLen = 801;
+		vrLoc = 0;
+	};
+	63F767011052D6A000ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 28";
+		rLen = 0;
+		rLoc = 668;
+		rType = 0;
+		vrLen = 866;
+		vrLoc = 94;
+	};
+	63F767021052D6D700ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 4";
+		rLen = 0;
+		rLoc = 71;
+		rType = 0;
+		vrLen = 860;
+		vrLoc = 0;
+	};
+	63F767051052D70400ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 630AE5C9104D62CE0091039C /* CommandWrapper.m */;
+		name = "CommandWrapper.m: 34";
+		rLen = 60;
+		rLoc = 746;
+		rType = 0;
+		vrLen = 977;
+		vrLoc = 211;
+	};
+	63F767061052D70400ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 4";
+		rLen = 0;
+		rLoc = 71;
+		rType = 0;
+		vrLen = 854;
+		vrLoc = 26;
+	};
+	63F767071052D70400ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 29";
+		rLen = 0;
+		rLoc = 704;
+		rType = 0;
+		vrLen = 968;
+		vrLoc = 290;
+	};
+	63F7670F1052D93A00ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 630AE5C9104D62CE0091039C /* CommandWrapper.m */;
+		name = "CommandWrapper.m: 28";
+		rLen = 646;
+		rLoc = 517;
+		rType = 0;
+		vrLen = 971;
+		vrLoc = 210;
+	};
+	63F767101052D93A00ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 22";
+		rLen = 0;
+		rLoc = 432;
+		rType = 0;
+		vrLen = 779;
+		vrLoc = 172;
+	};
+	63F767111052D93A00ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 65";
+		rLen = 0;
+		rLoc = 1662;
+		rType = 0;
+		vrLen = 879;
+		vrLoc = 1181;
+	};
+	63F767171052DA7500ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 29";
+		rLen = 0;
+		rLoc = 660;
+		rType = 0;
+		vrLen = 904;
+		vrLoc = 413;
+	};
+	63F7671B1052DAA800ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 5";
+		rLen = 0;
+		rLoc = 163;
+		rType = 0;
+		vrLen = 871;
+		vrLoc = 0;
+	};
+	63F767461052DC9E00ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 50";
+		rLen = 0;
+		rLoc = 1280;
+		rType = 0;
+		vrLen = 940;
+		vrLoc = 807;
+	};
+	63F7674E1052DCDE00ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 50";
+		rLen = 0;
+		rLoc = 1280;
+		rType = 0;
+		vrLen = 945;
+		vrLoc = 807;
+	};
+	63F767531053FF8F00ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 17";
+		rLen = 0;
+		rLoc = 335;
+		rType = 0;
+		vrLen = 329;
+		vrLoc = 54;
+	};
+	63F767561054069800ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 63F767571054069800ACB8E1 /* AppController.s */;
+		name = "AppController.s: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 512;
+		vrLoc = 17448;
+	};
+	63F767571054069800ACB8E1 /* AppController.s */ = {
+		isa = PBXFileReference;
+		name = AppController.s;
+		path = "/Users/david/Devel/arch/avr/code/quickdev16/tools/Quickdev16/build/Quickdev16.build/Debug/Quickdev16.build/Objects-normal/i386/AppController.s";
+		sourceTree = "<absolute>";
+	};
+	63F767581054069800ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 52";
+		rLen = 0;
+		rLoc = 1283;
+		rType = 0;
+		vrLen = 936;
+		vrLoc = 938;
+	};
+	63F767591054069800ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 55";
+		rLen = 0;
+		rLoc = 1356;
+		rType = 0;
+		vrLen = 928;
+		vrLoc = 913;
+	};
+	63F767641054DD5C00ACB8E1 /* PBXTextBookmark */ = {
+		isa = PBXTextBookmark;
+		fRef = 631A51A9104CF5B700E73564 /* AppController.m */;
+		name = "AppController.m: 55";
+		rLen = 0;
+		rLoc = 1356;
+		rType = 0;
+		vrLen = 928;
+		vrLoc = 913;
+	};
 	8D1107260486CEB800E47090 /* Quickdev16 */ = {
 		activeExec = 0;
 		executables = (

+ 6 - 10
tools/Quickdev16/Quickdev16.xcodeproj/project.pbxproj

@@ -8,9 +8,8 @@
 
 /* Begin PBXBuildFile section */
 		1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; };
-		256AC3DA0F4B6AC300CF3369 /* Quickdev16AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 256AC3D90F4B6AC300CF3369 /* Quickdev16AppDelegate.m */; };
+		630AE5CA104D62CE0091039C /* CommandWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 630AE5C9104D62CE0091039C /* CommandWrapper.m */; };
 		631A51AA104CF5B700E73564 /* AppController.m in Sources */ = {isa = PBXBuildFile; fileRef = 631A51A9104CF5B700E73564 /* AppController.m */; };
-		631A5252104CF9FB00E73564 /* BGHUDAppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 631A5251104CF9FB00E73564 /* BGHUDAppKit.framework */; };
 		8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
 		8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
 		8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
@@ -21,15 +20,14 @@
 		1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
 		13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
 		1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = "<group>"; };
-		256AC3D80F4B6AC300CF3369 /* Quickdev16AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Quickdev16AppDelegate.h; sourceTree = "<group>"; };
-		256AC3D90F4B6AC300CF3369 /* Quickdev16AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Quickdev16AppDelegate.m; sourceTree = "<group>"; };
 		256AC3F00F4B6AF500CF3369 /* Quickdev16_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Quickdev16_Prefix.pch; sourceTree = "<group>"; };
 		29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
 		29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
 		29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+		630AE5C8104D62CE0091039C /* CommandWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommandWrapper.h; sourceTree = "<group>"; };
+		630AE5C9104D62CE0091039C /* CommandWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommandWrapper.m; sourceTree = "<group>"; };
 		631A51A6104CF59900E73564 /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppController.h; sourceTree = "<group>"; };
 		631A51A9104CF5B700E73564 /* AppController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppController.m; sourceTree = "<group>"; };
-		631A5251104CF9FB00E73564 /* BGHUDAppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BGHUDAppKit.framework; path = /Users/david/Devel/arch/osx/code/BGHUDAppKit/build/Release/BGHUDAppKit.framework; sourceTree = "<absolute>"; };
 		8D1107310486CEB800E47090 /* Quickdev16-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Quickdev16-Info.plist"; sourceTree = "<group>"; };
 		8D1107320486CEB800E47090 /* Quickdev16.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Quickdev16.app; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
@@ -40,7 +38,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
-				631A5252104CF9FB00E73564 /* BGHUDAppKit.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -52,8 +49,8 @@
 			children = (
 				631A51A9104CF5B700E73564 /* AppController.m */,
 				631A51A6104CF59900E73564 /* AppController.h */,
-				256AC3D80F4B6AC300CF3369 /* Quickdev16AppDelegate.h */,
-				256AC3D90F4B6AC300CF3369 /* Quickdev16AppDelegate.m */,
+				630AE5C8104D62CE0091039C /* CommandWrapper.h */,
+				630AE5C9104D62CE0091039C /* CommandWrapper.m */,
 			);
 			name = Classes;
 			sourceTree = "<group>";
@@ -120,7 +117,6 @@
 			children = (
 				1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
 				1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
-				631A5251104CF9FB00E73564 /* BGHUDAppKit.framework */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -181,8 +177,8 @@
 			buildActionMask = 2147483647;
 			files = (
 				8D11072D0486CEB800E47090 /* main.m in Sources */,
-				256AC3DA0F4B6AC300CF3369 /* Quickdev16AppDelegate.m in Sources */,
 				631A51AA104CF5B700E73564 /* AppController.m in Sources */,
+				630AE5CA104D62CE0091039C /* CommandWrapper.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

BIN
tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/categories.pbxbtree


BIN
tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/cdecls.pbxbtree


BIN
tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/decls.pbxbtree


BIN
tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/files.pbxbtree


BIN
tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/imports.pbxbtree


BIN
tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/pbxindex.header


BIN
tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/protocols.pbxbtree


BIN
tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/refs.pbxbtree


BIN
tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/strings.pbxstrings/control


BIN
tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/strings.pbxstrings/strings


BIN
tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/subclasses.pbxbtree


BIN
tools/Quickdev16/build/Quickdev16.build/Quickdev16.pbxindex/symbols0.pbxsymbols


+ 11 - 0
tools/ucon64-2.0.0-src/FILE_ID.DIZ

@@ -0,0 +1,11 @@
+   .:_______________________/\ucon64.sf.net
+  __/ _____/ __  /_/ /  /__   \  /____ ___/\
+_/_/  /  /   /__/ \ /  / \    / /   /  /\_\/
+\ \_____/______/______/__/___/_____/__/ /  .
+ \/_____\______\______\__\___\_____\__\/   .
+.                                          :
+:               uCON64 2.0.0               |
+|                                          |
+|Back To Wipe Away All Bullshit/Proprietary|
+|Console Tools w/o Sources, Style And Love!|
+`-------[UNIX/Windows/DOS/BeOS/Amiga/Mac]--'

+ 1 - 0
tools/ucon64-2.0.0-src/VERSION

@@ -0,0 +1 @@
+2.0.0

+ 783 - 0
tools/ucon64-2.0.0-src/changes.html

@@ -0,0 +1,783 @@
+<html><head>
+<link rel="icon" href="images/icon.png" type="image/png">
+<title>uCON64 - Changes</title></head><body bgcolor="#ffffff"><tt><br>
+The original uCON for SNES and Genesis was written by Chicken &amp; chp in
+1993-1995<br>
+<br>
+<p>
+<a name="190">1.9.0</a><br>
+- completely re-written<br>
+- this is the first real version with support for the most consoles<br>
+<p>
+<a name="191">1.9.1</a><br>
+- bug fixes<br>
+- fixed the -xv64 routine for receive and emulation on N64<br>
+- cosmetical changes<br>
+- removed the stupid curses GUI<br>
+- optimized noisette.h for speed<br>
+- added more OPTIONS and CONSOLE SYSTEMS<br>
+<p>
+<a name="192">1.9.2</a><br>
+- started to add missing Sega System16(A/B)/System 18/dual 68000 support<br>
+- -xv64 shows now the filename on the Doctor instead of "UCON64" (sorry)<br>
+- removed -xmp3 option for SongBoy (latest information says that it will
+  be a USB device with copyprotected MPE standard instead of MP3.. HAHAHAHA!!!)<br>
+- TODO:  -xmp3 send MP3 file(s) to SongBoy (MP3 player for Game Boy)<br>
+- removed options which were added by mistake (like parallel transfer for Z64)<br>
+- added -ispad (looks for repeating bytes at the end of a probably padded ROM)<br>
+- added -rn (some people might find it useful)<br>
+- added -mg, -bin and 'show ROM' support for PC-Engine<br>
+- added -usms for Jos Kwanten's ultraSMS an Sega Master System/Game Gear
+  emulator for the Nintendo64<br>
+- fixed aps (GRRNNNNN!!!)<br>
+- added an universal frontend (in Java) for all platforms and all programs that
+  need a filename<br>
+- added CD32, CD-i and Real 3DO support<br>
+- added -bin and -ffe option for Sega Master System/Game Gear<br>
+- added database for NES roms (that enlarged the ucon64 executable by 190kb
+  grnnn...) "You gotta do what you gotta do." (Futurama)<br>
+- added -db and -dbv database commands... now everyone can check if the
+  uCON64 version he uses has new database entries for a console system<br>
+<p>
+<a name="193">1.9.3</a><br>
+- added -mka create APS patch support<br>
+- added -na change APS description<br>
+- added -hd, -nhd switches for SMS/Game Gear<br>
+- added auto-detection and some information for Jaguar ROMs<br>
+- added -ssc convert to Super Smart Card (Game Boy)<br>
+- fixed/added -chk fix checksum (Game Boy)<br>
+- added -gg Game Genie support for SNES<br>
+- cleaned up the usage output<br>
+- added -chk fix checksum for Super NES/Super Famicom<br>
+- removed most CDROM based consoles from usage since uCON64 could support
+  them only by showing some informations about the CDROM itself (option -cdrom)<br>
+- added -gd3 convert to Professor SF(2) Game Doctor SF3/6/7 (autojoin)<br>
+- added -gdf fix Professor SF(2) Game Doctor SF3/6/7 savegame problems<br>
+<p>
+<a name="194">1.9.4</a><br>
+- included all databases for every console system that has ROMs without a
+  header with text<br>
+- added 324 entries to SMS database<br>
+- added -sam convert SAM/M.A.M.E. sound to WAV; --rom=SAMFILE<br>
+- added -b2i convert BIN/RAW(2352)/Mode2(2336) to ISO; --rom=BIN_IMAGE<br>
+- added -ppf apply PPF patch (&#60;=2.0); --rom=ISO_IMAGE --file=PATCHFILE<br>
+- added -nppf change PPF description; --rom=PATCHFILE --file=DESCRIPTION<br>
+- added -mkppf create PPF patch; --rom=ISO_IMAGE --file=CHANGED_IMAGE<br>
+- added -ip extract ip0000.bin (GD-ROM header/256 bytes); --rom=/dev/&#60;cdrom&#62;<br>
+- added -fds convert Famicom Disk System file (diskimage) from FAM to FDS<br>
+- changed the FAQ a bit<br>
+- overworked the whole tool to make it easier to develop<br>
+- searched a new site for it (<a href="http://ucon64.sourceforge.net" target="_blank">http://ucon64.sourceforge.net</a>)<br>
+<p>
+<a name="195">1.9.5</a><br>
+- after the update release of 1.9.4 I had a closer look at my sources
+  and realized that my style heavily changed since 1.9.3 the result is
+  that I had to make more changes in the source starting from ucon64.c<br>
+- Gilligan wrote a nice routine to read out NeoGeo Pocket roms (added)<br>
+- reorganized the whole source<br>
+- added Game Boy Advance support<br>
+- added (GBA) -n change ROM name; --file=NEWNAME<br>
+- added (GBA) -logo restore ROM logo character data 0x04-0x9F<br>
+- added (GBA) -chk fix ROM checksum<br>
+- added (GB/Game Gear/NES) -gg apply Game Genie code (permanent)<br>
+- added (GB/Game Gear/NES/GEN/SNES) -gge encode Game Genie code<br>
+- added (GB/Game Gear/NES/GEN/SNES) -ggd decode Game Genie code<br>
+- added (LYNX) -lyx convert to LYX/RAW (strip 64 Bytes LNX header)<br>
+- added (LYNX) -lnx convert to LNX (uses default values for the header)<br>
+- added (LYNX) -n change ROM name (LNX only); --file=NEWNAME<br>
+- added (LYNX) -nrot set no rotation (LNX only)<br>
+- added (LYNX) -rotl set rotation left (LNX only)<br>
+- added (LYNX) -rotr set rotation right (LNX only)<br>
+- added (LYNX) -b0 change Bank0 kBytes size (LNX only); --file={0,64,128,256,512}<br>
+- added (LYNX) -b1 change Bank1 kBytes size (LNX only); --file={0,64,128,256,512}<br>
+- added -idppf change PPF FILE_ID.DIZ (2.0); --rom=PATCHFILE --file=FILE_ID.DIZ<br>
+- added -strip strip Bytes from end of ROM; --file=VALUE<br>
+- added -e emulate/run ROM (check INSTALL and $HOME/.ucon64rc for more)<br>
+- added (GBA) -sram patch ROM for SRAM saving<br>
+- added (GBA) -crp slow down Flash Advance Linker access for ROM (crash patch)<br>
+- added (GBA) -strip strip Bytes from end of ROM (use -ispad before); --file=VALUE<br>
+- added (GBA) -xfal send/receive to/from Flash Advance Linker; --file=PORT<br>
+<p>
+<a name="196">1.9.6</a><br>
+- this release brings you full support for the Super Wild Card<br>
+- added (SNES) -xswc send/receive to/from Super Wild Card*/(all)SWC; --file=PORT
+  (by dbjh)<br>
+- added (SNES) -xswcs send/receive SRAM to/from Super Wild Card*/(all)SWC;
+  --file=PORT (by dbjh)<br>
+- added (GB) -sgb convert from GB Xchanger/GB/GBC to Super Backup Card/GX/GBX
+  (by NoisyB)<br>
+- added (GB) -gbx convert from Super Backup Card/GX/GBX to GB Xchanger/GB/GBC
+  (by NoisyB)<br>
+- made a few small bug fixes and code cleanups (by NoisyB; thanks to dbjh)<br>
+- DOS port of uCON64 available (by dbjh)<br>
+- BeOS port of uCON64 available (by dbjh)<br>
+- added wildcard support to -find find string in ROM (by NoisyB)<br>
+- added (SNES) some new NTSC/PAL and SlowROM fix codes (by NoisyB)<br>
+- added (SNES) -swcs convert emulator *.srm to Super Wild Card*/(all)SWC (by
+  NoisyB)<br>
+<p>
+<a name="197">1.9.7</a><br>
+- added (NES) -fdsl list Famicom Disk System/FDS (diskimage) contents (by
+  NoisyB)<br>
+- updated (GBA) -xfal send/receive ROM to/from Flash Advance Linker; --file=PORT
+  (by dbjh)<br>
+- added (GBA) -xfalc&#60;n&#62; specify chip size in Mbits of ROM in Flash
+  Advance Linker when receiving (by dbjh)<br>
+- added (GBA) -xfalm use SPP mode, default is EPP (by dbjh)<br>
+- added (Neo Geo) -bios convert NeoCD Bios to work with NeoCD emulator (by
+  NoisyB; thanks to Caz)<br>
+- changed the whole internal ROM database code (by NoisyB)<br>
+- added -dbs search ROM database (all entries) by CRC32; --rom=0xCRC32 (by
+  NoisyB)<br>
+- added (Virtual Boy) show info (by NoisyB)<br>
+- added (WonderSwan) show info (by NoisyB)<br>
+- added (ColecoVision) show info (by NoisyB)<br>
+- added (Vectrex) show info (by NoisyB)<br>
+- added ROMs to database for Virtual Boy, WonderSwan, ColecoVision and Vectrex
+  (by NoisyB)<br>
+- added (Intellivision) show info (by NoisyB)<br>
+- made the internal ROM Database optional; compiled without DB uCON64 will be
+  only ~250kB in size! (by NoisyB)<br>
+- updated (all) fixed -cs (file compare) (by dbjh)<br>
+- updated (SNES) fixed dumping of `special' SNES cartridges (for the meaning of
+  special, see src/backup/swc.c) (by dbjh; thanks to Caz)<br>
+- updated (SNES) fixed -k (crack) (by dbjh)<br>
+- updated (SNES) fixed -f (NTSC/PAL fix) (by dbjh)<br>
+- uCON64 will recognize *ANY* kind of ROM or Console System via DB now...<br>
+  give it a Atari2600 ROM and it will just recognize it :) (by NoisyB)<br>
+- updated the internal DB; it can recognize 12387 different ROMs (by NoisyB)<br>
+- updated (SNES) fixed -s (split ROM) (by dbjh)<br>
+- updated (Genesis) fixed -s (by NoisyB)<br>
+- updated (all) fixed -swc &amp; -ins (and others) for files with a size a
+  multiple of 32kB (by dbjh)<br>
+- updated (SNES) fixed -swc when ROM file has a name that already has the
+  extension .swc fixed the same problem for -smc and -fig (by dbjh)<br>
+- added -lsv like -ls but more verbose; --rom=DIRECTORY (by NoisyB)<br>
+- updated (SNES) fixed checksum output (by NoisyB)<br>
+- updated (SNES) fixed -j (join multi files) (by dbjh)<br>
+- updated (SNES) fixed checksum output for Broadcast Satellaview ROMs (by dbjh;
+  thanks to Cowering)<br>
+- added (SNES) -figs convert emulator *.srm (SRAM) to *Pro Fighter*/(all)FIG (by
+  dbjh; thanks to Caz)<br>
+- added (SNES) -ufos convert emulator *.srm (SRAM) to Super UFO (by dbjh; thanks
+  to Caz)<br>
+- added (SNES) -int, -int2 &amp; -nint force ROM is (not) in interleaved format
+  (2) (by dbjh)<br>
+- added (SNES) -dint convert ROM to non-interleaved format (by dbjh)<br>
+- added 2830 new ROMs to internal ROM database (by NoisyB)<br>
+- added (Genesis) -xsmd send/receive ROM to/from Super Magic Drive/SMD;
+  --file=PORT (by NoisyB)<br>
+- added (Genesis) -xsmds send/receive SRAM to/from Super Magic Drive/SMD;
+  --file=PORT (by NoisyB)<br>
+- added support for CD based consoles again; uCON64 now uses cdrdao as burn
+  engine (by NoisyB)<br>
+- added Dreamcast, PSX (one), PS2, Real3DO, CD-i, CD32 and Saturn support (by
+  NoisyB)<br>
+- added (Genesis) -smds convert emulator (*.srm) SRAM to Super Magic Drive/SMD
+  (by NoisyB)<br>
+- added -r2i convert RAW to ISO9660; --rom=RAW_IMAGE (by NoisyB)<br>
+<p>
+<a name="198-1">1.9.8-1</a><br>
+- added more verbose WonderSwan ROM info (by NoisyB; thanks to dox)<br>
+- updated (GBA) used Jeff Frohwein's Flash Advance Linker code V1.72 (by dbjh)<br>
+- added (GBA) -xfals send/receive SRAM to/from Flash Advance Linker; --file=PORT
+  (by dbjh)<br>
+- added (GBA) -xfalb&#60;n&#62; send/receive SRAM to/from Flash Advance Linker
+  bank n; --file=PORT (by dbjh)<br>
+- removed (GBA) -xfalm (use SPP mode), because it is not needed anymore (by
+  dbjh)<br>
+- updated (SNES) fixed checksum function for Far East of Eden Zero (J) (by dbjh)<br>
+- added (GBA) -multi{1,2,3} make multirom for Flash Advance Linker (by dbjh)<br>
+- updated/added (SNES) Game Doctor ROM file support (-gd3 &amp; -s) (by John
+  Weidman)<br>
+- updated (all) fixed DOS specific bug in IPS code (by dbjh)<br>
+- updated (SNES) fixed DOS specific problem for -swc, -fig and -smc (by dbjh)<br>
+- updated (all) fixed DOS specific bug in -ins code (by dbjh)<br>
+- introducing config file updates without overwrites of old values for more
+  comfort (by NoisyB)<br>
+- added (WS/WSC) -chk fix ROM checksum (for WonderSwan(Color)) (by NoisyB;
+  thanks to dox)<br>
+- updated (SNES) changed -swcs, -figs and -ufos to always create a file of 32.5
+  kB. This solves some problems where SRAM files created with an emulator would
+  not work on a copier (by dbjh; thanks to Caz for investigating the problem and
+  providing a solution)<br>
+- added -mkcue generate CUE file; --rom=CD_IMAGE --file=TRACK_MODE (by NoisyB)<br>
+- ported the whole project to FreeBSD (by NoisyB)<br>
+<!-- beta2 -->
+- updated (GBA) changed -xfalb&#60;n&#62; to -xfalb &#60;n&#62; (space between
+  option and argument) (by dbjh). Did the same thing for -xfalc&#60;n&#62;<br>
+- updated (GB) changed -xgbxb&#60;n&#62; to -xgbxb &#60;n&#62; (space between
+  option and argument) (by dbjh)<br>
+- switched internal workflow to getopt() and made numerous cosmetical
+  changes/clean ups (by NoisyB)<br>
+- added -hdn=BYTES force ROM has backup unit/emulator header with BYTES size (by
+  NoisyB)<br>
+  detects the correct TRACK_MODE of IMAGES w/o *.cue file<br>
+- added --rrom rename all ROMs in DIR to their internal names; --rom=DIR (by
+  NoisyB)<br>
+- added --rr83 like --rrom but with 8.3 filenames; --rom=DIR (by NoisyB)<br>
+- added --stpn=N strip N Bytes from ROM beginning (by NoisyB)<br>
+- added --insn=N insert N Bytes (0x00) before ROM (by NoisyB)<br>
+- added --xmccl send/receive BYTES to/from Mad Catz Camera Link (experimental)
+  (by NoisyB)<br>
+- speed --rrom, --rr83 and --ls have been sped up significantly<br>
+- updated (N64) added 512 Mb ROM support for transfers to Doctor V64 Junior
+  (by NoisyB; thanks to TenOfTen (tenoften@jahej.com))<br>
+- updated (SNES) fixed -fig to give the same output as a Super Pro Fighter (by
+  dbjh; thanks to JohnDie for the information)<br>
+- updated (all) parallel port is only accessed for copier I/O (by dbjh)<br>
+- added (SNES) -bs &amp; -nbs force ROM is a Broadcast Satellaview dump/regular
+  cartridge dump (by dbjh)<br>
+- added (NES) -unif convert to UNIF format/UNF {iNES, UNIF}->UNIF (by dbjh)<br>
+- updated (NES) -ines convert to iNES format {iNES, UNIF, Pasofami}->iNES (by
+  dbjh)<br>
+- added (NES) -dint convert ROM to non-interleaved format (by dbjh)<br>
+- added (NES) -pasofami convert to Pasofami format iNES->Pasofami (by dbjh)<br>
+- updated (NES) ROM information for iNES, UNIF, FFE and Pasofami (by dbjh)<br>
+- updated (NES) fixed -j so that it converts properly from Pasofami to iNES (by
+  dbjh)<br>
+- updated (NES) fixed -s so that it converts properly from iNES to Pasofami (by
+  dbjh)<br>
+- updated (NES) fixed -ineshd (extract iNES header to .hdr file) (by dbjh)<br>
+- added (NES) -ctrl set controller type for ROM (UNIF only) (by dbjh)<br>
+- added (NES) -ntsc set TV compatibility to NTSC (UNIF only) (by dbjh)<br>
+- added (NES) -pal set TV compatibility to PAL (UNIF only) (by dbjh)<br>
+- added (NES) -bat set battery backup (by dbjh)<br>
+- added (NES) -nbat set no battery backup (by dbjh)<br>
+- added (NES) -vram set VRAM override (UNIF only) (by dbjh)<br>
+- added (NES) -nvram set no VRAM override (UNIF only) (by dbjh)<br>
+- added (NES) -mirr set mirroring type (by dbjh)<br>
+- added (NES) -n change internal ROM name (UNIF only) (by dbjh)<br>
+- added (NES) -mapr=NAME specify board name or mapper number for conversion
+  options (by dbjh)<br>
+- added (NES) -dumpinfo use dumper info when converting to UNIF (by dbjh)<br>
+<!-- beta3 -->
+- updated (NES) UNIF code works also on big endian machines (by dbjh)<br>
+- updated (GBA) changed meaning of -xfalc. In order to dump N megabits of a
+  cartridge specifying -xfalc=N &#60;filename&#62; is enough (by dbjh)<br>
+- updated (NES) fixed some minor format conversion problems (by dbjh)<br>
+- updated (LYNX) fixed some problems with endianess (by dbjh)<br>
+- added (LYNX) --lynxit send/receive ROM to/from Lynxit interface (by NoisyB)<br>
+- updated (SNES) fixed checksum function for Momotaro Dentetsu Happy (J) (by
+  dbjh)<br>
+- updated (SNES) fixed SNES info for Dai Kaiju Monogatari 2 (J) (by dbjh)<br>
+- updated (SNES) improved SNES chip type info (by dbjh)<br>
+- added gzip support (by dbjh)<br>
+- updated (Genesis) fixed several bugs (header is correct after -j/-s, correct
+  behaviour if switch -nbak is active, -smd/-mgd if already in SMD/MGD format,
+  -lsv) (by dbjh)<br>
+- added --padn=N pad ROM to N Bytes (put Bytes with value 0x00 after end) (by
+  dbjh)<br>
+<!-- beta4 -->
+- updated (NES) FDS images are recognized without -fdsl (by dbjh)<br>
+- added (GBA) -xfalmulti send multiple ROMs to Flash Advance Linker (by dbjh)<br>
+- updated (GBA) changed -multi so that it takes a size argument (by dbjh)<br>
+- removed (GBA) -multi{1,2,3}, -multi can be used instead (by dbjh)<br>
+- some code clean ups (by NoisyB)<br>
+- added read-only zip support (by dbjh) Note that only the first file inside the
+  zip archive will be looked at. Use -ls{v} to see info about all files in the
+  zip archive<br>
+- added (SNES) NSRT header support (by dbjh)<br>
+- updated (SNES) fixed an SWC problem for HiROM games without SRAM (by dbjh)<br>
+- updated (SNES) -k also cracks Killer Instinct (by dbjh)<br>
+- updated (SNES) -smc writes a correct SMC header (by dbjh)<br>
+- added (SNES) -ctrl{2} specify type of controller in port 1/2 for emu when
+  converting (by dbjh)<br>
+- updated (Genesis) replaced Charles MacDonald's SMD code with our own (by dbjh)<br>
+- added experimental (PSX/N64) --xdex send/receive BLOCK N to/from DexDrive (by
+  NoisyB)<br>
+- added 664 new ROMs to the internal database (by NoisyB)<br>
+- updated (SNES) replaced Cowering's BS detection code with John Weidman's (by
+  dbjh)<br>
+<!-- beta5 -->
+- updated (all) fixed two bugs in IPS creation code (by dbjh)<br>
+- updated (all) -mki generates RLE compressed IPS files (by dbjh)<br>
+- updated (SNES) fixed split file detection for SWC and FIG dumps (by dbjh)<br>
+- updated (Genesis) fixed -s for RAW images (by dbjh)<br>
+- updated (SNES) added crack for Donkey Kong Country, Diddy's Kong Quest &amp;
+  Mega Man X (by dbjh; many thanks to CL of NSRT)<br>
+- updated (SNES) fixed bug in Game Genie code (by dbjh)<br>
+- updated (all) fixed subtle bug in function that is used for -k, -f, -l &amp;
+  -crp (by dbjh)<br>
+- updated (SNES) added crack for BS The Legend of Zelda Remix &amp; Demon's
+  Crest (by dbjh, based on info of John Weidman)<br>
+<!-- beta6 -->
+- updated (SNES) added crack for at least the following games: Breath of Fire II,
+  Kirby's Dream Course (many thanks to John Weidman), Lufia II - Rise of the
+  Sinistrals and Earthbound (thanks to CL) (by dbjh)<br>
+- updated (SNES) crack for Mega Man X should work (better) (by dbjh)<br>
+- updated (SNES) added NTSC/PAL fix for at least the following games:
+  Final Fight Guy, Gokujyou Parodius, Kirby No Kira Kizzu, Robotrek and
+  Seiken Densetsu 3 (by dbjh)<br>
+- updated (SNES) added crack for Uniracers/Unirally and NTSC/PAL fix for
+  Seiken Densetsu 2 and Secret of Mana (by dbjh; thanks to John Weidman)<br>
+- updated (all) fixed problem with reading environment variables under DOS and
+  Windows (by dbjh)<br>
+- updated (all) environment variables take precedence over configuration file
+  variables (by dbjh)<br>
+- updated (SNES) fixed some problems with joining GD3 files (by dbjh)<br>
+- added (SNES) -xgd3 send ROM to Game Doctor SF3(SF6/SF7); --file=PORT
+  (by John Weidman and dbjh)<br>
+- added (SNES) -dbuh display backup unit header and see if it matches with what
+  uCON64 found while analyzing the ROM data (by dbjh, based on an idea by
+  JohnDie)<br>
+- removed (SNES) -gdf (fix Game Doctor savegame problems), because -gd3 already
+  provides this functionality (by dbjh)<br>
+- updated (SNES) fixed some -xgd3 problems, uppercase filenames sent to unit,
+  buffer data correct for all files, transfer file size fixed (by John)<br>
+- updated (SNES) checksums and deinterleaves for HiROM 24 Mbit Game Doctor
+  format should work (by John)<br>
+- added (SNES) -xswc2 send ROM to Super Wild Card and enable Real Time Save
+  mode (by dbjh)<br>
+- updated (SNES) added NTSC/PAL fix for Live-a-Live (by dbjh)<br>
+- updated (all) added full wildcard support ("ucon64 -fig *.swc"). Don't forget
+  to read the FAQ (by dbjh)<br>
+- added (all) -q be quiet (don't show ROM info) (by dbjh)<br>
+- updated (all) uCON64 handles multiple options on the same command line (by
+  dbjh)<br>
+- added (SNES) --ssize=SIZE specify split part size in Mbit (not for Game
+  Doctor SF3) (by dbjh)<br>
+<!-- beta 7 -->
+- updated (SNES) added NTSC/PAL fix for Terranigma (by dbjh; thanks to John)<br>
+- added (all) full support for all existing DAT files (GoodXXXX, RomCenter)
+  they'll be used to identify (test) or rename your ROMs or any other purpose
+  you might need a ROM manager for (by NoisyB)<br>
+- updated (SNES) fixed BS ROM detection affecting BS Zootte Mahjong! IVT (by
+  John)<br>
+- added (all) --lsd to list all ROMs using RomCenter or GoodXXXX DAT files (by
+  NoisyB)<br>
+- updated (all) improved support for zip files that contain more than one file
+  (by dbjh)<br>
+- updated (SNES) added NTSC/PAL fix and crack for Mario no Super Picross (by
+  dbjh; thanks to John)<br>
+- renamed (N64) --sram to --lsram (by NoisyB)<br>
+- replaced --file with option arguments for the following options: --strip,
+  --find, -c, --cs, --dbs, -b, -i, --mki, -a, --mka, --na, --ppf, --mkppf,
+  --nppf, --idppf, --crp, -n, --bot, --lsram, --usms, --n2, --n2gb, --b0, --b1,
+  --sam, --bios and --dumpinfo (by NoisyB)<br>
+- updated (PSX) PPF code works correctly and generates correct patches on
+  big-endian machines (by dbjh)<br>
+- added (patch) --patch option to specify the patch file for APS, IPS, BSL and
+  PPF (by NoisyB)<br>
+- updated (SNES) added NTSC/PAL fix for Dual Orb 2 (thanks to John), Dr. Mario,
+  Dragon - The Bruce Lee Story (U) and Wolfenstein 3D (U) (by dbjh)<br>
+- updated (N64) APS code works correctly and generates correct patches on
+  big-endian machines (by dbjh)<br>
+- added (all) -o specify output directory. Note that now uCON64 by default
+  writes its output files to the current directory (by NoisyB and dbjh)<br>
+- added (all) --poke=OFF:V change byte at file offset OFF to value V (by dbjh)<br>
+<!--
+- tested (Genesis) -xsmd and -xsmds with my "new" Super Magic Drive and they
+  worked as we expected (by NoisyB; thanks to dbjh)<br>
+Of course we test things... Should be put in a file like testsdone.html ;-) (dbjh)
+-->
+- updated (SNES) fixed problem where it was necessary to run uCON64 twice on a
+  ROM with the option -chk, before the checksum was really fixed (by dbjh)<br>
+- added (all) --rename (by NoisyB and dbjh)<br>
+- added (all) --scan like: GoodXXXX scan ... (by NoisyB)<br>
+- added (SNES) uCON64 is able to use an "addendum file" for the options -k
+  (snescopy.txt), -f (snesntsc.txt or snespal.txt) and -l (snesslow.txt). You
+  can add new search patterns to those files if the latest binary doesn't
+  support them yet. See the FAQ for a more elaborate explanation (by dbjh)<br>
+- added Visual C++ build support (including libdiscmage) (by dbjh)<br>
+- updated (GBA) fixed a bug that would crash FAL code on compressed files (by
+  dbjh)<br>
+- (re)added (GBA) -xfalm (try to enable EPP mode). Note that the meaning is
+  reversed. Enabling EPP mode only when this switch is specified makes it
+  possible to use the Windows versions of uCON64 under Windows XP (NT/2000)
+  (by dbjh)<br>
+- added support for the I/O port driver inpout32.dll to the Windows ports. See
+  the FAQ for more information (by dbjh)<br>
+- uCON64 can also be compiled with g++; configure with "CC=g++ ./configure"
+  (by dbjh)<br>
+- updated (SNES) uCON64 uses one maker/publisher list for all SNES games (by
+  dbjh; thanks to FluBBa for the help)<br>
+- updated (GB &amp; GBA) fixed maker info (by dbjh; thanks to FluBBa for the
+  info)<br>
+- updated (SNES, GB &amp; GBA) fixed huge mistake in the way the Nintendo
+  publisher list was used (resulting in incorrect maker info) (by dbjh)<br>
+- updated (SNES) fixed problem with splitting HiROM Game Doctor files smaller
+  than or equal to 8 Mbit (by dbjh)<br>
+- replaced internal handling/conversion of DiscJuggler and Nero images with
+  included libdiscmage. The recognition of ISO, BIN, CDI and NRG images works
+  in 80% of the cases. Every other function like ripping/converting is still
+  flakey and therefore disabled in this beta release (by NoisyB)<br>
+<!-- beta 8 -->
+- updated (all) fixed problems with -rename &amp; -rrom for files without a
+  suffix (by dbjh)<br>
+- updated (all) links (including symbolic links) are handled correctly for
+  -rename &amp; -rrom (by dbjh)<br>
+- added support for I/O port driver io.dll to the Windows ports. See the FAQ
+  for more information (by dbjh)<br>
+- added (SNES) -xfig send/receive ROM to/from Pro Fighter; --file=PORT (by
+  JohnDie and dbjh)<br>
+- added (SNES) -xfigs send/receive SRAM to/from Pro Fighter; --file=PORT (by
+  JohnDie and dbjh)<br>
+- added (all) --mkdat=DATFILE create DAT file based on a directory/list of ROMs
+  (by dbjh)<br>
+- updated (N64) fixed -usms (by dbjh)<br>
+- updated (all) fixed problem with -rename when output directory is on another
+  file system (M$ speak: drive) (by dbjh)<br>
+- added MinGW build support (including libdiscmage) (by dbjh)<br>
+- changed name of two property variables: now configdir is named
+  ucon64_configdir and datdir is named ucon64_datdir (by dbjh)<br>
+- updated (SNES) changed checksum calculation function so that it is stricter
+  (by dbjh; thanks to Nach)<br>
+- updated (SNES) added more verbose BS info (by dbjh, based on info of Derrick
+  Sobodash)<br>
+- updated (GB) fixed -n2gb, -gbx &amp; -sgb (by dbjh)<br>
+- updated (SNES) replaced -ehi with -erom (force ROM is "Extended") in order to
+  support the two Extended LoROM dumps in the known universe (by dbjh)<br>
+- updated (SNES) added initial support for Sufami Turbo ROMs (by dbjh; thanks
+  to Nach for some info)<br>
+- added support for GameCube images to libdiscmage (by NoisyB; thanks to
+  gc-nfo.com)<br>
+- updated (SNES) improved detection of interleaved ROMs (by dbjh)<br>
+- updated (SNES) -smc, -swc, -fig, -mgd &amp; -gd3 (if it's a LoROM dump)
+  automatically deinterleave the ROM data if the ROM is detected as interleaved
+  (by dbjh)<br>
+- updated (SNES) -gd3 can convert Tales of Phantasia and Dai Kaiju Monogatari 2
+  (by dbjh)<br>
+- updated (SNES) interleaved extended HiROM dumps are handled correctly (by
+  dbjh)<br>
+- updated (SNES) uCON64 follows the "uCONRST standard" for calculating the
+  "search CRC32" of BS dumps (by dbjh)<br>
+- updated (all) both the search CRC32 and the "data CRC32" are displayed if
+  they differ (by dbjh)<br>
+- updated (SNES) applied anomie's GD patch which solved a problem with one-part
+  split files (by dbjh)<br>
+- updated (SNES) replaced John Weidman's BS detection code with Nach's (by
+  dbjh)<br>
+- updated (SNES) uCON64 now recognises a complete GoodSNES 0.999.5 set (by
+  JohnDie and dbjh)<br>
+- added (SNES) -dmirr strip mirrored block from end of ROM (by dbjh)<br>
+- added (SNES) support for native Super UFO file format (by John and dbjh) Use
+  -ufo to convert a file to Super UFO format, use -s to split the resulting
+  file (required for HiROM games)<br>
+- updated (SMS) -mgd is recognised again, -smd won't crash uCON64 anymore and
+  -smds calls the SMS function when -sms is specified (by dbjh)<br>
+- updated (SMS) -sms in no longer required for SMD files, they are
+  automatically detected (by dbjh)<br>
+- updated (SNES) fixed -b (apply Baseline patch) (by dbjh)<br>
+- updated (all) added support for non-standard parallel port addresses (by
+  dbjh)<br>
+- updated (SNES) added crack for Tetris Attack (by dbjh; thanks to CL)<br>
+<p>
+<a name="198-2">1.9.8-2</a><br>
+- updated (SNES) -gd3 produces a more logical format for Daikaijuu Monogatari 2
+  (by dbjh; thanks to Nach for the idea)<br>
+- updated (SMS) -smd interleaves SMS files (by dbjh; thanks to nori-p for
+  testing and feedback)<br>
+- updated (GBA) fixed stupid bug introduced in 1.9.8beta8 that caused
+  -xfalmulti to hang (-multi and -xfal still worked) (by dbjh)<br>
+- updated (SNES) hopefully fixed last problem with splitting GD3 files (by
+  dbjh)<br>
+- updated (SNES) added crack for Dixie Kong's Double Trouble (by dbjh, based on
+  IPS patch by Black Hole of Infernal Affairs)<br>
+- files of 0 bytes won't be detected as WonderSwan files anymore (by dbjh)<br>
+- removed (NES) support for bad FDS DAT files (by dbjh) If you really want to
+  use FDS DAT files created by tools that erroneously include the header in the
+  CRC32 calculation, use the switch -nhd. Someone with a complete FDS
+  collection should create a new DAT file<br>
+- updated (SNES) fixed crashing on first split file of Tales of Phantasia and
+  Daikaijuu Monogatari 2 (by dbjh)<br>
+- added (SNES) -xgd6 send ROM to Game Doctor SF6/SF7. This probably solves the
+  problems people are having with -xgd3 (by John)<br>
+- added (SNES) -xgd6s send/receive SRAM to/from Game Doctor SF6/SF7 (by John)<br>
+- added (SNES) -xgd3s send SRAM to Game Doctor SF3/SF6/SF7 (by John)<br>
+- added (Genesis) -multi can create a multi-game file for use on the MD-PRO
+  flash card. The downside: creating a multirom for the FAL doesn't work
+  without -gba anymore (by dbjh; thanks to Enemy and acem77 for testing)<br>
+- added (Genesis) -f can fix region protections, addendum files are genpal.txt
+  and mdntsc.txt (by dbjh)<br>
+- added (SNES) -id force -gd3 to produce a unique file name. This switch
+  doesn't show in the help, because it "fails" for 130 dumps of the GoodSNES
+  "0.999.6--" set. 128 IDs are each shared by 2 games, 2 IDs are shared by 3
+  games (by dbjh)<br>
+- updated (all) the patch generation options (-mka, -mki, -mkppf) use the
+  modified ROM name for the patch to create. They used to use the name of the
+  original ROM (by dbjh)<br>
+- updated (N64) fixed major bug in APS creation code (by dbjh)<br>
+- updated (SNES) added NTSC/PAL fix for Magical Pop'n (J) and Metal Warriors
+  (U) (by dbjh)<br>
+- added (Genesis) recognition of <i>real</i> MGD files (by dbjh; thanks to
+  acem77 for the feedback)<br>
+- updated (Genesis) -mgd now produces proper MGD files (by dbjh)<br>
+- added (Genesis) -bin convert to binary format. This option produces files in
+  the format that uCON64 1.9.8-1 produces with the option -mgd (by dbjh)<br>
+- added (Genesis) -xmd &amp; -xmds send/receive ROM/SRAM to/from MD-PRO flash
+  card programmer (by dbjh and NoisyB with many thanks to timofonic and of
+  course ToToTEK Multi Media for providing us with the necessary information.
+  Special thanks go to Leo of ToToTEK for being very helpful and willing to
+  answer our questions)<br>
+- added (Genesis) -ssize=SIZE specify split part size in Mbit (by dbjh)<br>
+- updated (SNES, SMS, PC-Engine &amp; Genesis) -mgd produces correct file names
+  (by dbjh; thanks to acem77 for testing and feedback)<br>
+- updated (Genesis) -mgd automatically pads files to the right size (by dbjh)<br>
+- added (PC-Engine) -swap invert/swap bits of all bytes of a file. This
+  effectively converts PC-Engine games between Japan and U.S.A. "mode" (by
+  dbjh; thanks to acem77 for the information)<br>
+- added (SMS) backup unit header length detection (by dbjh; thanks to acem77
+  for pointing this out) This made a problem with the current SMS DAT file
+  surface: some CRC32 values are the result of including a backup unit header
+  in the calculation. Someone with a complete GoodSMS collection should create
+  a new DAT file<br>
+- updated (PC-Engine) auto-detection of bit-swapped files (by dbjh; thanks to
+  Cowering for the info)<br>
+- updated (PC-Engine) -mgd automatically bit-swaps the file if it is detected
+  as being in Japan "mode" (by dbjh)<br>
+- updated (PC-Engine) -smg automatically bit-swaps the file if it is detected
+  as being in U.S.A. "mode" (by dbjh) I don't know if this correct. Please give
+  feedback<br>
+- added (GB) -logo restore ROM logo character data (offset: 0x104-0x134)
+  (by NoisyB)<br>
+<p>
+<a name="198-3">1.9.8-3</a><br>
+- added (SNES) -xswc-super option to force 32 Mbit dump (by John Weidman)<br>
+- updated (SNES) enabled -hi &amp; -nhi for -xswc-super, -xswc (dumping) and
+  -xfig (dumping) (by dbjh)<br>
+  You should explicitly specify the parallel port address when you use -hi or
+  -nhi in combination with one of the mentioned dump options<br>
+- updated (Genesis) fixed problem with -multi that caused problems with some
+  games when run on the MD-PRO (by dbjh)<br>
+- updated (SNES, Genesis, PC-Engine, SMS, Game Gear &amp; GB) -mgd now produces
+  correct file names. This time for real! :-) (by dbjh; thanks to acem77 for
+  testing and feedback)<br>
+- added (SNES) -xswcr send/receive RTS data to/from Super Wild Card*/(all)SWC
+  (by dbjh)<br>
+- updated (PC-Engine) reversed how -mgd &amp; -smg work. -mgd bit-swaps a file
+  if it is detected as not being bit-swapped ("Japan mode"/"unencrypted"). -smg
+  bit-swaps a file if it is detected as being bit-swapped
+  ("U.S.A. mode"/"encypted") (by dbjh; thanks to kyuusaku for the feedback)<br>
+  Note that if you want to play a game with an MGD2 on a PC-Engine that you
+  have to manually bit-swap the file with -swap (or specify -int while
+  converting).<br>
+- updated (SMS) improved detection of SMS and Game Gear files. In most cases
+  specifying -sms is no longer necessary (by dbjh)<br>
+- added (SMS) -chk fix checksum (by dbjh)<br>
+- updated (PC-Engine) it's Magic Super Griffin, not Super Magic Griffin. Thus
+  the option -smg has been renamed to -msg (by dbjh; thanks to kyuusaku for the
+  info)<br>
+- added (PC-Engine) -xmsg send/receive to/from Magic Super Griffin (by dbjh;
+  thanks to kyuusaku for testing)<br>
+- added (PC-Engine) -f fix region protection (by dbjh; thanks to kyuusaku for
+  testing)<br>
+- updated (NES) -ffe writes correct headers for the NES games that actually run
+  on the Super Magic Card (by dbjh; thanks to kyuusaku for the info and
+  testing)<br>
+- added (NES) -xsmc send to Super Magic Card (by dbjh; thanks to kyuusaku for
+  testing)<br>
+- added (NES) -xsmcr send/receive RTS data to/from Super Magic Card (by dbjh)<br>
+- added (all) -pattern=FILE change ROM based on patterns specified in FILE (by
+  dbjh)<br>
+- added (N64) -swap2 word-swap ROM (1234 &#60;-&#62; 3412) (by dbjh)<br>
+- added (SNES) -xgd3r &amp; -xgd6r send/receive RTS data to/from Game Doctor
+  SF3/6/7 (by John Weidman) -xgd3r can currently only be used for sending<br>
+- added (SNES) -gd3s convert emulator *.srm (SRAM) to GD BRAM format (by dbjh)<br>
+- updated (SNES) added crack for Front Mission - Gun Hazard (by dbjh; many
+  thanks to John) This crack is only necessay when the game is modified, for
+  example by -f.<br>
+- updated (SNES) added NTSC/PAL fix for Pop'n Twinbee (by dbjh)<br>
+- updated (SNES, Genesis, PC-Engine &amp; NES) (dumping) functions for FFE
+  copiers and Pro Fighter should work correctly with very fast PCs (by JohnDie)<br>
+- updated (N64) added work-around for old bug in DJGPP and Solaris. Now -swap,
+  -swap2, -v64 and -z64 work correctly for large files (larger than 8 or 12 MB
+  respectively) when using the DOS or Solaris port of uCON64 (by dbjh)<br>
+- updated (Genesis) replaced -pal with -region=CODE. Now it should be possible
+  to create multi-game files for the MD-PRO that work on all three console
+  types (by dbjh; many thanks to SamIAm for the info that brought me up the
+  idea)<br>
+<p>
+<a name="198-4">1.9.8-4</a><br>
+- updated (all) fixed 2 bugs in code of -pattern (by dbjh)<br>
+- added (SNES) -xfigc and -xswcc option to transfer SRAM to/from cartridge in
+  *Pro Fighter* and Super Wild Card copiers (by JohnDie)<br>
+- added support for Linux/PPC (by dbjh; thanks to Tobias Pflug and Misty De Meo)<br>
+- updated (SNES, Genesis, PC-Engine &amp; NES) fixed subtle bug in code of FFE
+  dumping routines (by dbjh; thanks to Neill Corlett for pointing this out)<br>
+- updated (SNES) implemented SRAM size display for Super FX ROMs (by JohnDie)<br>
+- updated (SMS) fixed bug that caused -chk not to work for uninterleaved files
+  with a header (by dbjh)<br>
+- added support for Mac OS X (including libdiscmage) (by dbjh; many thanks to
+  Steve Paige for his help)<br>
+- added (SNES) -xswc-dm=MODE specify SWC dump mode (by dbjh)<br>
+- removed (SNES) -xswc-super, use -xswc -xswc-dm=1 instead (by dbjh)<br>
+- updated (all) uCON64 won't accept a parallel port address without using --port
+  anymore (by dbjh)<br>
+  This used to be possible:<br>
+  ucon64 &#60;transfer option&#62; &#60;rom&#62; &#60;parallel port address&#62;<br>
+  Now you should use a command like this:<br>
+  ucon64 &#60;transfer option&#62; &#60;rom&#62; --port=&#60;parallel port address&#62;<br>
+  Or of course, if uCON64 automatically detects the right port or if you have
+  specified the correct port in the configuration file, a command like this:<br>
+  ucon64 &#60;transfer option&#62; &#60;rom&#62;<br>
+  This change removes the requirement of specifying the parallel port address
+  when using additional command line options (switches) like -xfalm or -xswc-dm.
+  It also prevents uCON64 from accessing I/O ports not associated to the
+  parallel port if you specify additional transfer switches while &#60;rom&#62;
+  has a name that starts with a number<br>
+- updated (SNES, Genesis, PC-Engine, SMS, Game Gear &amp; GB) -mgd produces
+  correct index files (MULTI-GD) (by dbjh)<br>
+- updated (SNES &amp; Genesis) -s produces an index file if the ROM dump is in
+  MGD format (by dbjh)<br>
+- added support for ppdev (GNU/Linux-only) (by dbjh)<br>
+  This solves two problems:<br>
+  1.) It's no longer necessary for uCON64 to be setuid root in order to
+  communicate with a backup unit. Just make sure /dev/parport&#60;n&#62; has the
+  right permissions or get yourself added to the correct group<br>
+  2.) Non-standard parallel port addresses do not have to be specified. uCON64
+  still uses the first parallel port by default. It's possible to override that
+  by setting parport_dev in the configuration file or by setting an environment
+  variable<br>
+  Use the configure option --enable-ppdev to enable the code<br>
+- added (all) -md5 calculate MD5 checksum of ROM (by NoisyB)<br>
+- added (all) -sha1 calculate SHA-1 checksum of ROM (by NoisyB)<br>
+- added (GBA) support for the Flash 2 Advance (Ultra); options are -xf2a
+  (transfer ROM data), -xf2ac=N (dump N Mbits), -xf2as (transfer 256 kB of SRAM
+  data) and -xf2ab=BANK (transfer 64 kB of SRAM data to "bank" BANK) (by NoisyB
+  and dbjh, based on source code of Ulrich Hecht and David Voswinkel; many
+  thanks to David Voswinkel for testing the parallel port code)<br>
+- added (GBA) -xfa2multi=SIZE send multiple ROMs to the F2A. You should not
+  specify a loader file on the command line as a file argument. Specify one in
+  the configuration file or set an environment variable (by dbjh)<br>
+- added (SNES) -dnsrt restore name, region and checksum from NSRT header and
+  remove it (by dbjh)<br>
+- updated (SNES) fixed shameful bug that caused -chk and -n not to work
+  correctly for interleaved ROM dumps (by dbjh)<br>
+- updated (SNES) added/updated code to dump special-chip cartridges. Now it's
+  possible to dump Super FX (2), S-DD1, SA-1 and SPC7110 cartridges, but see the
+  comment in swc.c! (by JohnDie)<br>
+- updated (SNES) made -xswc-dm active for -xswcc so that SRAM data can be
+  transferred to and from Super FX 2 cartridges (by dbjh)<br>
+- updated (SNES) renamed -xswc-dm to -xswc-io, because it can also be used for
+  sending an SRAM file (by dbjh)<br>
+- updated (GBA) made -xfalmulti use gbaloader to be consistent with -xf2amulti.
+  So, don't specify a loader as file argument anymore (by dbjh)<br>
+<p>
+<a name="200">2.0.0</a><br> <!-- 1.9.8-5 -->
+- updated (GBA) fixed F2A USB send code (by dbjh; many thanks to David Gauchard
+  for feedback, testing and sending a patch to fix the problem)<br>
+- updated (N64) cleaned up Doctor V64 Junior transfer source code (by dbjh;
+  thanks to TenOfTen and GameCopierUser0666 for testing)<br>
+- updated (SNES) added new SWC "I/O mode" 0x100, dump BIOS (by dbjh)<br>
+- updated (GB) improved ROM info (by dbjh)<br>
+- updated (GB) fixed problem with dumping "Rocket Games" cartridges with the
+  Game Boy Xchanger (by dbjh; thanks to Anna Kubisty for reporting the problem
+  and testing, and thanks to Cowering for his help and providing us with the
+  necessary information)<br>
+- updated (GB) cleaned up Game Boy Xchanger transfer source code (by dbjh; many
+  thanks to Anna Kubisty for testing)<br>
+- added (DC) --scr scramble 1ST_READ.BIN for selfboot CDs (by NoisyB)<br>
+- added (DC) --unscr unscramble 1ST_READ.BIN for non-selfboot CDs (by NoisyB)<br>
+- added (DC) --mkip generate IP.BIN file with default values (by NoisyB)<br>
+- added (DC) --parse=TEMPLATE parse TEMPLATE file into a IP.BIN (by NoisyB)<br>
+- updated (SNES) corrected output of -dbuh (by dbjh)<br>
+- added (GB) --xgbxm try to enable EPP mode (by dbjh)<br>
+- added (SMS/Game Gear) --xgg &amp; --xggs send/receive ROM/SRAM to/from
+  SMS-PRO/GG-PRO flash card programmer (by dbjh; thanks to timofonic and Leo of
+  ToToTEK Multi Media for providing us with the necessary information, many
+  thanks to Walter van Niftrik for testing)<br>
+- added (PCE) --xpce send/receive ROM to/from PCE-PRO flash card programmer (by
+  dbjh; thanks to timofonic and Leo of ToToTEK Multi Media for providing us with
+  the necessary information)<br>
+- added (PCE) --multi create a multi-game file for use on a PCE-PRO flash card
+  (by dbjh)<br>
+- added (SMS/Game Gear) --multi create a multi-game file for use on an SMS-PRO
+  or GG-PRO flash card (by dbjh; thanks to Walter van Niftrik for his patch and
+  for testing)<br>
+- added (SMS/Game Gear) --xggb=BANK send/receive SRAM to/from SMS-PRO/GG-PRO
+  BANK (by dbjh; thanks to Walter van Niftrik for the suggestion and testing)<br>
+- added (Genesis) --xmdb=BANK send/receive SRAM to/from MD-PRO BANK (by dbjh)<br>
+- added support for I/O port driver DlPortIO to the Windows ports. See the FAQ
+  for more information (by dbjh)<br>
+- the Windows versions of uCON64 won't crash anymore without a (working) I/O
+  port driver under Windows NT/2000/XP. They display a helpful error message
+  instead (by dbjh)<br>
+- updated (GBA) improved recognition of GBA files and fixed start address
+  information (by dbjh)<br>
+- updated (GBA) added support for Linux 2.5 (and later) to F2A USB code (by
+  dbjh; thanks to colle for sending a patch and testing)<br>
+- added support for OpenBSD (by dbjh; thanks to timofonic for testing)<br>
+- added (N64) --xcd64, --xcd64b, --xcd64s, --xcd64f, --xcd64e, --xcd64m &amp;
+  --xcd64p transfer ROM/boot emu/SRAM/flash RAM/EEPROM/memory pack data to/from
+  CD64 (by dbjh; thanks to Ryan Underwood for libcd64 and testing)<br>
+- added (Genesis) support for Mike Pavone's Genesis/Sega CD transfer cable (by
+  dbjh; thanks to Mike Pavone for the information and thanks to timofonic for
+  his PR work ;-)<br>
+- updated (SNES) -gd3 does not produce 28 Mbit HiROM files anymore, it
+  automatically pads them to 32 Mbit (by dbjh)<br>
+- added (Genesis) support for Cyan's Megadrive ROM copier (by Cyan Helkaraxe and
+  dbjh; thanks to timofonic for his PR work)<br>
+- moved and split some files in the codebase and changed the workflow. Adding a
+  new option/function now requires only two changes in the code (by NoisyB)<br>
+- added --dual show ROM as dualdump (by NoisyB)<br>
+- added --code show ROM as code (by NoisyB)<br>
+- added --print show ROM in printable characters (by NoisyB)<br>
+- added --findi find STRING in ROM and ignore case of alpha bytes (by NoisyB)<br>
+- added --findr find STRING in ROM but look also for shifted/relative
+  similarities (by NoisyB)<br>
+- added (NeoGeo Pocket) --xpl send/receive ROM to/from Pocket Linker, --xpli
+  show information about inserted cartridge &amp; --xplm try to enable EPP mode
+  (by Walter van Niftrik)<br>
+- added (SNES) --multi create a multi-game file for use on a Super Flash flash
+  card (by JohnDie)<br>
+- added (SNES) --xsf send/receive ROM to/from Super Flash card, --xsfs to
+  send/receive SRAM to/from Super Flash card (by JohnDie)<br>
+- updated (Genesis) added support for 32 Mbit Intel MD-PRO flash card (by dbjh;
+  thanks to Marc Haisenko for sending a patch and testing)<br>
+- updated (Genesis) fixed -n and -n2 (-n did what -n2 should do and vice versa)
+  (by dbjh)<br>
+- added (Genesis) support for splitting and joining binary (Magicom) files (by
+  dbjh; thanks to T. Reid for the information)<br>
+- updated (NES) fixed -dumpinfo so that the year field is read correctly from
+  the dumper info file if it has more than 2 digits (by dbjh)<br>
+- updated (SNES) --swcs, --figs, --ufos &amp; --gd3s now also work for backup
+  unit SRAM files, i.e., files with a header (by dbjh)<br>
+- updated (SNES) fixed a bug that caused several headerless dumps to be detected
+  as SWC RTS or SWC SRAM files (by dbjh; many thanks to Dweezledap for reporting
+  this bug and for testing)<br>
+- updated (N64) added support for 6105 boot codes to check sum routine (by dbjh)<br>
+- updated (SNES) fixed some other issues with auto-detection (by dbjh; many
+  thanks to Dweezledap for reporting these problems and for testing)<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+</tt></body></html>

+ 590 - 0
tools/ucon64-2.0.0-src/developers.html

@@ -0,0 +1,590 @@
+<html><head>
+<link rel="icon" href="images/icon.png" type="image/png">
+<title>uCON64 - Developers</title></head><body bgcolor="#ffffff"><tt><br>
+Q1: <a href="#1">Who develops uCON64?</a><br>
+<br>
+Q2: <a href="#2">How can I take part?</a><br>
+<br>
+Q3: <a href="#3">How do I get the source of uCON64, using CVS as an anonymous user?</a><br>
+<br>
+Q4: <a href="#4">How do I connect to the CVS repository as developer with read/write access?</a><br>
+<br>
+Q5: <a href="#5">How do I create a patch from the CVS repository?</a><br>
+<br>
+Q6: <a href="#6">How is the source of uCON64 organized?</a><br>
+<br>
+Q7: <a href="#7">What coding style is preferred?</a><br>
+<br>
+<br>
+<p><!-- <p> instead of <br> to make lynx display a blank line between questions -->
+<a name="1"><b>Q1</b>: Who develops uCON64?</a><br>
+<b>A</b>: Written/modified by:<br>
+   1999 - 2004 NoisyB (Dirk) <a href="mailto:noisyb AT gmx.net">&#60;noisyb AT gmx.net&#62;</a><br>
+   2001 - 2004 dbjh<br>
+<br>
+   Additional developers:<br>
+   2002 - 2004 John Weidman <a href="mailto:jweidman AT sonic.net">&#60;jweidman AT sonic.net&#62;</a><br>
+   Contributed several SNES-related features like Game Doctor &amp; Super UFO
+   support, the second version of the BS (Broadcast Satellaview) detection code,
+   SNES cracks &amp; NTSC/PAL fixes.<br>
+<br>
+   2002 - 2004 Jan-Erik Karlsson<br>
+   Maintainer of the Amiga ports.<br>
+<br>
+   2003 - 2004 JohnDie<br><!--  It's a nick name, no space between John and Die -->
+   Also contributed several SNES-related features like Pro Fighter support and
+   Super Wild Card transfer code improvements.<br>
+<br>
+<br>
+   uCON64 uses code (or at least ideas) from:<br>
+<br>
+<table>
+<tr>
+  <td><tt>Chicken&nbsp;&amp;&nbsp;chp</tt></td>
+  <td><a href="mailto:ronaldm AT netcom.com"><tt>ronaldm AT netcom.com</tt></a><tt> (chp)</tt></td>
+  <td><tt>original uCON</tt></td>
+</tr>
+<tr>
+  <td><tt>Silo&nbsp;/&nbsp;BlackBag</tt></td>
+  <td></td>
+  <td><tt>APS patcher</tt></td>
+</tr>
+<tr>
+  <td><tt>madman</tt></td>
+  <td></td>
+  <td><tt>IPS patcher</tt></td>
+</tr>
+<tr>
+  <td><tt>The&nbsp;White&nbsp;Knight&nbsp;/ ATX</tt></td><!-- yes, breaking space -->
+  <td></td>
+  <td><tt>BSL patcher</tt></td>
+</tr>
+<tr>
+  <td><tt>Icarus&nbsp;/&nbsp;Paradox</tt></td>
+  <td></td>
+  <td><tt>PPF patcher</tt></td>
+</tr>
+<tr>
+  <td><tt>WyrmCorp</tt></td>
+  <td><a href="http://wyrmcorp.com" target="_blank"><tt>http://wyrmcorp.com</tt></a></td>
+  <td><tt>Game Genie "codec"</tt></td>
+</tr>
+<tr>
+  <td><tt>Marat&nbsp;Fayzullin</tt></td>
+  <td><a href="mailto:fms AT cs.umd.edu"><tt>fms AT cs.umd.edu</tt></a><br>
+      <a href="http://www.komkon.org/fms/" target="_blank">
+        <tt>http://www.komkon.org/fms/</tt></a></td>
+  <td><tt>some Famicom Disk System routines and NESLIST</tt></td>
+</tr>
+<tr>
+  <td><tt>Andreas&nbsp;Sterbenz</tt></td>
+  <td><a href="mailto:stan AT sbox.tu-graz.ac.at"><tt>stan AT sbox.tu-graz.ac.at</tt></a></td>
+  <td><tt>N64 checksum algorithm</tt></td>
+</tr>
+<tr>
+  <td><tt>Kami</tt></td>
+  <td><a href="mailto:m-kami AT da2.so-net.ne.jp"><tt>m-kami AT da2.so-net.ne.jp</tt></a><br>
+      <a href="http://www.playoffline.com" target="_blank">
+        <tt>http://www.playoffline.com</tt></a></td>
+  <td><tt>NES emulator for Game Boy</tt></td>
+</tr>
+<tr>
+  <td><tt>Gilligan&nbsp;/&nbsp;DC-S</tt></td>
+  <td></td>
+  <td><tt>show info for Neogeo Pocket</tt></td>
+</tr>
+<tr>
+  <td><tt>LaC</tt></td>
+  <td></td>
+  <td><tt>N64 makesram</tt></td>
+</tr>
+<tr>
+  <td><tt>Jeff&nbsp;Frohwein</tt></td>
+  <td><a href="mailto:info AT devrs.com"><tt>info AT devrs.com</tt></a><br>
+      <a href="http://www.devrs.com" target="_blank">
+        <tt>http://www.devrs.com</tt></a></td>
+  <td><tt>Flash Advance Linker transfer code</tt></td>
+</tr>
+<tr>
+  <td><tt>Omar&nbsp;Kilani</tt></td>
+  <td><a href="mailto:gbautil AT aurore.net"><tt>gbautil AT aurore.net</tt></a></td>
+  <td><tt>Game Boy Advance SRAM patching and "crash patching" code</tt></td>
+</tr>
+<tr>
+  <td><tt>Richard Davies</tt></td>
+  <td><a href="mailto:richard AT debaser.force9.co.uk"><tt>richard AT debaser.force9.co.uk</tt></a><br>
+      <a href="http://www.debaser.force9.co.uk" target="_blank">
+        <tt>http://www.debaser.force9.co.uk</tt></a></td>
+  <td><tt>PSX memory card code</tt></td>
+</tr>
+<tr>
+  <td><tt>Caz</tt></td>
+  <td><a href="mailto:turok2 AT currantbun.com"><tt>turok2 AT currantbun.com</tt></a><br>
+      <a href="http://www.infernal.currantbun.com" target="_blank">
+        <tt>http://www.infernal.currantbun.com</tt></a></td>
+  <td><tt>I/O port driver for BeOS, SWC help and Super UFO SRAM header format</tt></td>
+</tr>
+<tr>
+  <td><tt>Cowering</tt></td>
+  <td><a href="mailto:hotemu AT hotmail.com"><tt>hotemu AT hotmail.com</tt></a></td>
+  <td><tt>old BS ROM detection, BS checksum, some PC-Engine info &amp; code and more</tt></td>
+</tr>
+<tr>
+  <td><tt>Jerremy&nbsp;Koot</tt></td>
+  <td><a href="mailto:jkoot AT snes9x.com"><tt>jkoot AT snes9x.com</tt></a><br>
+      <a href="http://www.snes9x.com" target="_blank">
+        <tt>http://www.snes9x.com</tt></a></td>
+  <td><tt>old interleave detection and old deinterleave code (SNES)</tt></td>
+</tr>
+<tr>
+  <td><tt>Gary&nbsp;Henderson</tt></td>
+  <td><a href="mailto:gary AT snes9x.com"><tt>gary AT snes9x.com</tt></a><br>
+      <a href="http://www.snes9x.com" target="_blank">
+        <tt>http://www.snes9x.com</tt></a></td>
+  <td><tt>old interleave detection and old deinterleave code (SNES)</tt></td>
+</tr>
+<tr>
+  <td><tt>Evan&nbsp;Teran</tt></td>
+  <td><a href="mailto:emt3734 AT ritvax.isc.rit.edu"><tt>emt3734 AT ritvax.isc.rit.edu</tt></a></td>
+  <td><tt>lib_unif (NES)</tt></td>
+</tr>
+<tr>
+  <td><tt>NSRT&nbsp;authors</tt></td>
+  <td><a href="mailto:joecool22us AT yahoo.com"><tt>joecool22us AT yahoo.com</tt></a><br>
+      <a href="http://nsrt.edgeemu.com" target="_blank"><tt>http://nsrt.edgeemu.com</tt></a></td>
+  <td><tt>checksum algorithm for Momotaro Dentetsu Happy, their SNES add-on
+          chip information page, SNES cracks (thanks to CL), third version of
+          BS detection code (thanks to Nach) and general help and ideas</tt></td>
+</tr>
+<tr>
+  <td><tt>Gilles&nbsp;Vollant</tt></td>
+  <td><a href="mailto:info AT winimage.com"><tt>info AT winimage.com</tt></a></td>
+  <td><tt>unzip.c & unzip.h</tt></td>
+</tr>
+<tr>
+  <td><tt>Tiptonium</tt></td>
+  <td><tt><a href="http://www.tiptonium.com" target="_blank">http://www.tiptonium.com</a></td>
+  <td><tt>donated a Super Magic Drive (SMD)</tt></td>
+</tr>
+<tr>
+  <td><tt>FluBBa</tt></td>
+  <td></td><!-- private -->
+  <td><tt>maker (publisher) info for SNES, GB, CGB & GBA</tt></td>
+</tr>
+<tr>
+  <td><tt>gc-nfo.com</tt></td>
+  <td></td><!-- private -->
+  <td><tt>GameCube image specs</tt></td>
+</tr>
+<tr>
+  <td><tt>ToToTEK Multi Media</tt></td>
+  <td><a href="mailto:rayman AT tototek.com"><tt>rayman AT tototek.com</tt></a><br>
+      <a href="http://www.tototek.com" target="_blank">
+        <tt>http://www.tototek.com</tt></a></td>
+  <td><tt>MD-PRO, SMS-PRO/GG-PRO &amp; PCE-PRO transfer code (Delphi) and help
+          with that code</tt></td>
+</tr>
+<tr>
+  <td><tt>timofonic</tt></td>
+  <td></td><!-- private? -->
+  <td><tt>uCON64 PR work ;-)</tt></td>
+</tr>
+<tr>
+  <td><tt>Felipe&nbsp;XnaK</tt></td>
+  <td><a href="mailto:felipe AT ComPorts.com"><tt>felipe AT ComPorts.com</tt></a><br>
+      <a href="http://www.classicgaming.com/launchtool" target="_blank">
+        <tt>http://www.classicgaming.com/launchtool</tt></a></td>
+  <td><tt>some info from his Genesis ROM format document</tt></td>
+</tr>
+<tr>
+  <td><tt>Ulrich&nbsp;Hecht</tt></td>
+  <td><a href="mailto:uli AT emulinks.de"><tt>uli AT emulinks.de</tt></a><br>
+      <a href="http://www.emulinks.de/software.html" target="_blank">
+        <tt>http://www.emulinks.de/software.html</tt></a></td>
+  <td><tt>F2A (USB) code</tt></td>
+</tr>
+<tr>
+  <td><tt>David&nbsp;Voswinkel</tt></td>
+  <td><a href="mailto:d.voswinkel AT netcologne.de"><tt>d.voswinkel AT netcologne.de</tt></a></td>
+  <td><tt>F2A parallel port code</tt></td>
+</tr>
+<tr>
+  <td><tt>Ryan&nbsp;Underwood</tt></td>
+  <td></td><!-- private? -->
+  <td><tt>libcd64</tt></td>
+</tr>
+<tr>
+  <td><tt>Mike&nbsp;Pavone</tt></td>
+  <td><a href="mailto:mask_of_destiny AT hotmail.com"><tt>mask_of_destiny AT hotmail.com</tt></a><br>
+      <a href="http://www.retrodev.com" target="_blank">
+        <tt>http://www.retrodev.com</tt></a></td>
+  <td><tt>Genesis/Sega CD transfer code</tt></td>
+</tr>
+<tr>
+  <td><tt>Cyan&nbsp;Helkaraxe</tt></td>
+  <td><a href="mailto:cyan AT emulationzone.org"><tt>cyan AT emulationzone.org</tt></a><br>
+      <a href="http://www.emulationzone.org/projects/cyan/docs" target="_blank">
+        <tt>http://www.emulationzone.org/projects/cyan/docs</tt></a></td>
+  <td><tt>Cyan's Megadrive ROM copier transfer code</tt></td>
+</tr>
+</table><br>
+   and others...<br>
+<br>
+<p>
+<a name="2"><b>Q2</b>: How can I take part?</a><br>
+<b>A</b>: Install <a href="http://www.cvshome.org" target="_blank">CVS</a> and
+   checkout the latest version from
+   <a href="http://ucon64.sourceforge.net" target="_blank">http://ucon64.sourceforge.net</a>.
+   Then make your changes, email <a href="mailto:noisyb AT gmx.net">noisyb AT gmx.net</a>
+   and you will be named in this file and in the sources of course. Just the
+   typical way of open source development.<br>
+<br>
+<p>
+<a name="3"><b>Q3</b>: How do I get the source of uCON64, using CVS as an
+   anonymous user?</a><br>
+<b>A</b>: For read-only access you must have CVS installed. First you have to
+   login. To login, type:<br>
+   &nbsp;&nbsp; cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ucon64 login<br>
+   Then CVS will ask you for a password. Since you are logged in as anonymous
+   you do not have a password, so just press the enter key at the password
+   prompt. To download the uCON64 source, type:<br>
+   &nbsp;&nbsp; cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ucon64 checkout -PR ucon64<br>
+   The uCON64 source will then be downloaded to the directory ./ucon64.<br>
+   It can be very busy at SourceForge which may result in a message like:<br>
+   &nbsp;&nbsp; cvs [login aborted]: recv() from server cvs.sourceforge.net: EOF<br>
+   or<br>
+   &nbsp;&nbsp; cvs [checkout aborted]: recv() from server cvs.sourceforge.net: EOF<br>
+   If you get that message simply try again.<br>
+   You can speed up the download by using the option -z with a compression level
+   parameter. For example:<br>
+   &nbsp;&nbsp; cvs -z 9 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ucon64 checkout -PR ucon64<br>
+   If you want to get up to date with the newest source code later, enter the
+   directory ucon64 and type:<br>
+   &nbsp;&nbsp; cvs update -PRd<br>
+<br>
+<p>
+<a name="4"><b>Q4</b>: How do I connect to the CVS repository as developer with
+   read/write access?</a><br>
+<b>A</b>: For read/write access you must have CVS and SSH installed and set the
+   environment variable CVS_RSH=ssh. To download the source code, type:<br>
+   &nbsp;&nbsp; cvs -z 9 -d:ext:&#60;name&#62;@cvs.sourceforge.net:/cvsroot/ucon64 checkout -PR ucon64<br>
+   &#60;enter password at prompt&#62;<br>
+   If you never logged on to cvs.sourceforge.net before you may get a message
+   (from ssh) like:<br>
+   &nbsp;&nbsp; The authenticity of host 'cvs.sourceforge.net (66.35.250.207)' can't be established.<br>
+   &nbsp;&nbsp; DSA key fingerprint is 02:ab:7c:aa:49:ed:0b:a8:50:13:10:c2:3e:92:0f:42.<br>
+   &nbsp;&nbsp; Are you sure you want to continue connecting (yes/no)?<br>
+   Type yes (the whole word). Ssh will respond with a message like:<br>
+   &nbsp;&nbsp; Warning: Permanently added 'cvs.sourceforge.net' (DSA) to the list of known hosts.<br>
+   Now all files will be downloaded. Afterwards you can simply cd into the top
+   level directory and continue by just typing:<br>
+   &nbsp;&nbsp; cvs update -PRd<br>
+   &nbsp;&nbsp; cvs commit -R<br>
+   &nbsp;&nbsp; cvs add &#60;file&#62;<br>
+   &nbsp;&nbsp; cvs remove -f &#60;file&#62;<br>
+   You don't need to specify
+   -d:ext:&#60;name&#62;@cvs.sourceforge.net:/cvsroot/ucon64 anymore, because
+   that information is stored in the CVS directories then.<br>
+   If you use the update command in combination with (-)d, never forget to also
+   use the option -P or you will get a lot of crap.<br>
+<br>
+<p>
+<a name="5"><b>Q5</b>: How do I create a patch from the CVS repository?</a><br>
+<b>A</b>: As developer:<br>
+   cvs -d:ext:&#60;name&#62;@cvs.sourceforge.net:/cvsroot/ucon64 diff -u &#62;mychanges.patch<br>
+   &#60;enter password at prompt&#62;<br>
+   As anonymous:<br>
+   &nbsp;&nbsp; cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ucon64 login<br>
+   &#60;just press enter at password prompt&#62;<br>
+   &nbsp;&nbsp; cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ucon64 diff -u &#62;mychanges.patch<br>
+<br>
+<p>
+<a name="6"><b>Q6</b>: How is the source of uCON64 organized?</a><br>
+<b>A</b>: The heart of uCON64 is ucon64.h/st_ucon64_t. This <i>struct</i> is
+   designed to hold all workflow relevant informations and it has pointers
+   to different structs which will be != NULL depending on which type or
+   format uCON64 did detect for the current ROM. libdiscmage (if compiled)
+   is involved in this process as well.<br><br>
+   uCON64 starts with the name of a ROM and possibly some arguments. Then it
+   checks if a configuration file exists and creates one if that is not the
+   case. For example the value of
+   ucon64.parport, the variable that contains the I/O address of the parallel
+   port, will initially be set to the value that is found in the configuration
+   file.<br>
+   After these initial functions every ROM will be processed.<br><br>
+   Processing a ROM.<br>
+   The command line will be scanned for switches. Switches are command line
+   arguments that will not result in an action by themselves, but they
+   influence the other type of command line arguments, options. <br>
+   uCON64 sets some other values in ucon64.h/st_ucon64_t, based on the
+   switches. This code can be found in ucon64_opts.c/ucon64_switches().
+   This is also the place where ucon64.c/ucon64.quiet is set. This variable
+   determines whether or not uCON64 displays ROM information.<br>
+   Then uCON64 starts scanning the command line for options. This code can be
+   found in ucon64_opts.c/ucon64_options(). From this code almost all
+   operations of uCON64 are executed. Under Unix this is also the point where
+   root privileges are given up with a call to misc.c/drop_privileges().<br>
+   First uCON64 tries to find out what console system the ROM belongs to using
+   for example the init function for SNES (console/snes.c/snes_init() or
+   console/nes.c/nes_init() for NES). Each init function tries to determine if the
+   ROM file belongs to its console type. If that is the case it returns 0,
+   otherwise -1.<br>
+   Some ROM file types contain information such as the internal name, country
+   and creator of the game that can be used to fill ucon64.c/main()/rom. SNES
+   ROM files are an example of this type of file. For other ROM file types this
+   information is not present inside the ROM file. NES ROMs are an example of
+   that type. In that case uCON64 uses the DAT files in the data file
+   directory. That makes it possible to display the full name of the game.
+   ucon64.c/ucon64_rom_handling() calls the function
+   ucon64_dat.c/ucon64_dat_search() that uses the CRC32 of the ROM file data to
+   find the required information in the DAT files. If the information could be
+   found ucon64_dat.c/ucon64_dat_search() fills ucon64.c/ucon64.dat with it.
+   ucon64.c/ucon64_rom_handling() copies the name to ucon64.rominfo->name.<br>
+   After that if ucon64.c/ucon64.quiet has a value smaller than 1 uCON64 will
+   call ucon64.c/ucon64_nfo() which displays the values and strings of ucon64.
+   For some options it doesn't make sense to display ROM info. If one of those
+   options has been specified on the command line
+   (ucon64.c/ucon64.flags & WF_NFO) will be zero.<br>
+   After all this the option will be executed (ucon64_opts.c/ucon64_options()).<br>
+<br>
+<p>
+<a name="7"><b>Q7</b>: What coding style is preferred?</a><br>
+<b>A</b>:<br>
+<br>
+   <b>Keywords</b><br>
+   Always put a space after a keyword. That includes the keyword <i>sizeof</i>.<br>
+<br>
+   <b>Indentation</b><br>
+   Two spaces instead of a real tab. The curly braces of functions should not
+   be indented.<br>
+   If an <i>if</i> statement has an <i>else</i> part, that part should be
+   aligned with the <i>if</i> part. For a <i>do-while</i> compound statement,
+   <i>do</i> and <i>while</i> should also be aligned.<br>
+   If <i>else</i> is immediately followed by an <i>if</i> statement, the
+   <i>if</i> keyword together with the condition statement should be put on the
+   same line as the <i>else</i> keyword. Example: <br>
+<pre>
+   if (...)
+     {
+       ...
+     }
+   else if (...)
+     {
+       ...
+     }
+   else
+     {
+       ...
+     }
+</pre>
+   Note that this doesn't apply to the case where <i>else</i> is followed by a
+   curly brace.<br>
+<br>
+   <b>New lines</b><br>
+   The statement(s) for <i>if</i>, <i>while</i> and <i>for</i> should always
+   start on a new line. That also applies to one-line statements.<br>
+<br>
+   <b>Compound statements</b><br>
+   The curly braces should be indented and the code of the compound statement
+   should be indented again. The only exception is the <i>switch</i> statement.
+   The labels inside a <i>switch</i> statement should be aligned with the curly
+   braces. Example:<br>
+<pre>
+   int
+   function (int value)
+   {
+     if (value &#60; 0)
+       {
+         printf ("Invalid argument: %d\n", value);
+         exit (1);
+       }
+     switch (value)
+       {
+       case 0:
+       case 1:
+       case 2:                                     // falling through
+         puts ("lesser than or equal to 2\n");
+       case 3:
+       case 4:
+         puts ("lesser than or equal to 4\n");
+         break;
+       default:
+         puts ("greater than 4\n");
+         break;
+       }
+
+     return 0;
+   }
+</pre>
+   A <i>do-while</i> compound statement should have this layout:<br>
+<pre>
+   do
+     {
+       ...
+     }
+   while (...);
+</pre>
+<br>
+   <b>Functions</b><br>
+   Function calls should contain one space after the name of the function.
+   Every parameter of the function should be separated from a previous one with
+   a space after the comma.<br>
+   For a function implementation the return type should be specified on its own
+   line. This doesn't apply to function prototypes. If a function needn't be
+   known at global scope it should be declared or defined as <i>static</i>.
+   Function implementations should be separated from each other by two new
+   lines.<br>
+<br>
+   <b>Variables</b><br>
+   Variables of the same type should be defined in one group. Example:<br>
+<pre>
+   int x, y = 0, z;
+   char str1[] = "xyz", *str2;
+</pre>
+   Variables that need to be known at file scope but not at global scope should
+   be defined as <i>static</i>. Variables that do need to be known at global
+   scope should be defined in a source file, not in a header file. The header
+   file should contain an <i>extern</i> statement for the variable.<br>
+<br>
+   <b>Types (<i>struct</i>s)</b><br>
+   A <i>struct</i> tag of a <i>struct</i> should have the prefix "st_". A new
+   type based on a <i>struct</i> should have the same prefix and the suffix
+   "_t". Example:<br>
+<pre>
+   typedef struct st_unif_chunk
+   {
+     unsigned long id;
+     unsigned long length;
+     void *data;
+   } st_unif_chunk_t;
+</pre>
+<br>
+   <b>Line length</b><br>
+   The length of lines shouldn't be much longer than 90 characters. This
+   doesn't necessarily include the length of comment. Longer lines should be
+   broken into several shorter ones. Example:<br>
+<pre>
+   static const int unif_prg_ids[] = {PRG0_ID, PRG1_ID, PRG2_ID, PRG3_ID,
+                                      PRG4_ID, PRG5_ID, PRG6_ID, PRG7_ID,
+                                      PRG8_ID, PRG9_ID, PRGA_ID, PRGB_ID,
+                                      PRGC_ID, PRGD_ID, PRGE_ID, PRGF_ID};
+</pre>
+<br>
+   <b>Comment</b><br>
+   Single or two-line comments should use //. Comments that need more lines
+   should use the pair /* and */. A two-line comment should signify that it is
+   one two-line comment instead of two one-line comments by putting one extra
+   space after the //. Single or two-line comments should start at column 49 if
+   that's possible. Example:<br>
+<pre>
+   char buf[SIZE_INFO], number[80];              // 4 should be enough, but don't
+                                                 //  be too sensitive to errors
+</pre>
+   Long comments should use the same indentation as the code that follows it:<br>
+<pre>
+   if (deinterleave)
+     /*
+       This is a bit of a hack, i.e., putting the following code in this
+       ...
+     */
+     {
+       unsigned char *data;
+       int size, n = 0, prg = 0, chr = 0;
+       ...
+</pre>
+<br>
+   <b>Preprocessor directives</b><br>
+   Preprocessor directives should not be indented. This does not only apply to
+   #include, but to all preprocessor directives.<br>
+   For #if and #ifdef blocks that are complex or enclose many lines of code the
+   corresponding #else, #elif or #endif should contain a comment that signifies
+   to which #if or #ifdef it belongs. <a name="multiplatform">Example:</a><br>
+<pre>
+   #ifdef  __unix__                                // wait 32 milliseconds
+     usleep (32000);
+     ...
+   #elif   defined __MSDOS__                       // __unix__
+     delay (32);
+     ...
+   #elif   defined __BEOS__                        // __MSDOS__
+     snooze (32000);
+     ...
+   #endif                                          // __BEOS__
+</pre>
+   Standard system header files should be included by specifying the name of
+   the header file between angle brackets. Non-standard header files should be
+   included with the name between double quotes. Example:<br>
+<pre>
+   #include &#60;stdio.h&#62;
+   #ifdef  __unix__
+   #include &#60;unistd.h&#62;
+   #endif
+   #include "ucon64.h"
+</pre>
+<br>
+   <b>Header files</b><br>
+   Header files should be protected against being included multiple times by
+   putting the entire file inside an #ifndef block. Define the constant for
+   which the #ifndef checks inside the #ifndef block. For example, for the file
+   swc.h:<br>
+<pre>
+   #ifndef SWC_H
+   #define SWC_H
+   ...
+   #endif // SWC_H
+</pre>
+   Header files should only contain information about their corresponding
+   source file that may be needed by other source files. They should not
+   include information about variables or function definitions that are used
+   only inside the source file.<br>
+<br>
+   <b>Portability</b><br>
+   Platform-specific function calls, variable types and #includes of header
+   files should be put inside #ifdef or #if blocks. For an example see the
+   <a href="#multiplatform">first code example</a> in the section Preprocessor
+   directives.<br>
+<br>
+<p>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br></tt></body></html>

+ 1889 - 0
tools/ucon64-2.0.0-src/faq.html

@@ -0,0 +1,1889 @@
+<html><head>
+<link rel="icon" href="images/icon.png" type="image/png">
+<title>uCON64 - FAQ</title></head><body bgcolor="#ffffff"><!-- text="#000000" link="#0000ee" vlink="#0000ee" alink="#ffffff"-->
+<!--style type="text/css">
+<!--
+a:hover{color:#ffffff;background-color:#0000ee;}
+img:hover{color:#0000ee;}
+a{text-decoration:none}
+-->
+</style--><tt>
+Q1: <a href="#1">Why uCON64?</a><br>
+<br>
+Q2: <a href="#2">How do I get started using uCON64?</a><br>
+<br>
+Q3: <a href="#3">How do I compile/build uCON64?</a><br>
+<br>
+Q4: <a href="#4">How do I install uCON64?</a><br>
+<br>
+Q5: <a href="#5">Sometimes my terminal seems to be locked after "ucon64 -xswc
+   &#60;file&#62; | more"</a><br>
+<br>
+Q6: <a href="#6">How do I make uCON64 only display information about a ROM?</a><br>
+<br>
+Q7: <a href="#7">I have two parallel ports in my PC and my Flash Advance Linker
+   is connected to the second. However, when I try to use uCON64 to send a demo
+   to the FAL something goes wrong. What happened?</a><br>
+<br>
+Q8: <a href="#8">I tried to dump a SNES cartridge using a Super Wild Card
+   connected to my PC with an interlink cable, but it did not work. What is
+   wrong?</a><br>
+<br>
+Q9: <a href="#9">I have a SNES ROM of which GoodSNES says it is good, but
+   uCON64 says it is bad. Who is right?</a><br>
+<br>
+Q10: <a href="#10">I have a SNES ROM of which Snes9x and ZSNES say it is bad,
+   but uCON64 says it is good. Who is right?</a><br>
+<br>
+Q11: <a href="#11">I have a SNES ROM of which uCON64 said it was bad, so I used
+   the -chk option. Now uCON64, Snes9x and ZSNES all say the ROM is good, but
+   GoodSNES still says it is bad. How can that be, I did fix the ROM, right?</a><br>
+<br>
+Q12: <a href="#12">Some SNES games do not work on my Super Wild Card. What can
+   I do about that?</a><br>
+<br>
+Q13: <a href="#13">Which backup devices are supported by uCON64?</a><br>
+<br>
+Q14: <a href="#14">I use Windows&nbsp;XP (NT/2000). When I try to run
+   ucon64.exe under command.com (start -&#62; Run... -&#62; command) uCON64
+   crashes. What do I do wrong?</a><br>
+<br>
+Q15: <a href="#15">I do not like the command line. Are there graphical
+   frontends available?</a><br>
+<br>
+Q16: <a href="#16">Where do I get PPF, APS or IPS patches?</a><br>
+<br>
+Q17: <a href="#17">Where do I get ROMs/Games?</a><br>
+<br>
+Q18: <a href="#18">What does "[ROM|IMAGE|SRAM|FILE|DIR|ARCHIVE]..." mean in the
+   help?</a><br>
+<br>
+Q19: <a href="#19">Why is my backup unit (still) not supported?</a><br>
+<br>
+Q20: <a href="#20">How do I specify a bank number for the GB Xchanger?</a><br>
+<br>
+Q21: <a href="#21">What is the difference between the Flash Advance Linker
+   options -xfals and -xfalb &#60;n&#62;?</a><br>
+<br>
+Q22: <a href="#22">uCON64 exits with the message "ERROR: Flash card erase
+   failed" when I try to write a multi-game file to my flash card. What is
+   wrong?</a><br>
+<br>
+Q23: <a href="#23">Where can I get that loader or "game pack" file?</a><br>
+<br>
+Q24: <a href="#24">What is the meaning of the -col option (SNES)?</a><br>
+<br>
+Q25: <a href="#25">Why does uCON64 create ucon64.cfg (DOS executable) or
+   .ucon64rc (Win32 executable) files all over the place under DOS and
+   Windows&nbsp;9x (command.com)?</a><br>
+<br>
+Q26: <a href="#26">When I try to create a multi-game file under Windows I get
+   the message "The system cannot execute the specified program." (or something
+   similar). What does that mean?</a><br>
+<br>
+Q27: <a href="#27">I tried to run a Win32 executable of uCON64 but Windows gave
+   an error message that cygwin1.dll, cygz.dll or zlib.dll could not be
+   found.</a><br>
+<br>
+Q28: <a href="#28">How do I make uCON64 display one help screen at a time?</a><br>
+<br>
+Q29: <a href="#29">I tried to send a ROM dump to my copier under
+   Windows&nbsp;XP (NT/2000), but uCON64 crashed. What is wrong?</a><br>
+<br>
+Q30: <a href="#30">I want to convert a NES ROM in iNES format to UNIF. How do I
+   know what board name to use?</a><br>
+<br>
+Q31: <a href="#31">How do I convert a NES ROM from Pasofami to FFE? Or from
+   UNIF to Pasofami?</a><br>
+<br>
+Q32: <a href="#32">How do I specify dumper information when converting to UNIF?</a><br>
+<br>
+Q33: <a href="#33">How do I specify that a NES game in UNIF format supports
+   multiple controller types?</a><br>
+<br>
+Q34: <a href="#34">How do I enable or disable colors in the display output of
+   uCON64?</a><br>
+<br>
+Q35: <a href="#35">When I try to convert a large number of files using
+   wildcards, uCON64 will only convert the first file. Is this a bug?</a><br>
+<br>
+Q36: <a href="#36">Does uCON64 support DAT (RomCenter/GoodXXXX) files?</a><br>
+<br>
+Q37: <a href="#37">Some SNES games do not work on my Super Pro Fighter Q(+).
+   What can I do about that?</a><br>
+<br>
+Q38: <a href="#38">uCON64 displays too many lines for my DOS-box. What can I do
+   about that?</a><br>
+<br>
+Q39: <a href="#39">When will the next version of uCON64 be released? I have
+   heard the next version is able to crack my favourite SNES game, but I do not
+   know how to use CVS or a compiler.</a><br>
+<br>
+Q40: <a href="#40">What is the format of the snes*.txt files?</a><br>
+<br>
+Q41: <a href="#41">Is it possible to force uCON64 to send or dump (an) SRAM
+   (file) instead of that it depends on whether the file exists?</a><br>
+<br>
+Q42: <a href="#42">Why does uCON64 support DAT files?</a><br>
+<br>
+Q43: <a href="#43">How should the option --mkdat be used?</a><br>
+<br>
+Q44: <a href="#44">What tools do you recommend besides uCON64?</a><br>
+<br>
+Q45: <a href="#45">What is an interleaved ROM?</a><br>
+<br>
+Q46: <a href="#46">The pre-compiled GNU/Linux binary does not work on my
+   system, while a binary compiled by me works fine. How can that be?</a><br>
+<br>
+Q47: <a href="#47">I use Windows XP (NT/2000) and every time I run uCON64 I get
+   this error message about ntuser.dat. What does it mean?</a><br>
+<br>
+Q48: <a href="#48">Is there any way to make uCON64 convert a ROM dump to Game
+   Doctor SF3/SF6/SF7 format and split it, in one command?</a><br>
+<br>
+Q49: <a href="#49">How do I use the command line?</a><br>
+<br>
+Q50: <a href="#50">I configured uCON64 to use ppdev and tried to send a file to
+   my backup unit as a regular user. I got an error message that the parallel
+   port device could not be opened. What did I do wrong?</a><br>
+<br>
+Q51: <a href="#51">What do I need to do before I can upload files to my Flash 2
+   Advance?</a><br>
+<br>
+<br>
+<p><!-- <p> instead of <br> to make lynx display a blank line between questions -->
+<a name="1"><b>Q1</b>: Why uCON64?</a><br>
+<b>A</b>: uCON64 is designed for people who want to play all the games on the
+   original hardware. Here you got a powerful tool to backup, restore games and
+   for many other operations. It is just a keep-old-hardware-alive tool :-)<br>
+   uCON64 is also useful for people without backup units as it can be used to
+   manage ROM collections (it can be used to identify, rename, patch or convert
+   files) and it <i>can</i> operate as an intelligent frontend for every
+   available emulator.<br>
+<br>
+<p>
+<a name="2"><b>Q2</b>: How do I get started using uCON64?</a><br>
+<b>A</b>: On the uCON64 homepage you can find a link to a
+   <a href="http://sourceforge.net/project/showfiles.php?group_id=12381" target="_blank">
+   download section</a>. There are two types of packages, binary and source.
+   You can tell a package is a binary package if it has "bin" (without the
+   quotes) in its name. The source code packages have "src" in their name.
+   Unless you are a software developer you probably do not need to download any
+   of the source packages.<br>
+   Choose a binary package for your system. GNU/Linux users should choose a
+   package with "linux" in its name, BeOS users a package with "beos" in its
+   name and DOS users a package with "dos" in its name. Windows users can
+   choose any or all of the packages with "win32" or "dos" in their name.
+   Normally, they should choose a package with "win32" in its name. The DOS
+   version also runs under Windows, but has some limitations. For example, the
+   DOS version cannot handle long command lines. The Cygwin version does not
+   have any serious limitations compared to the "normal" Win32 versions, but
+   behaves a bit more Unix-like than Windows users might want. Users accustomed
+   to Unix might prefer the Cygwin version. The Cygwin version needs some
+   additional files, though. See <a href="#27">question 27</a>.<br>
+   For installation instructions (for all versions) see
+   <a href="#4">question 4</a>.<br>
+<br>
+<p>
+<a name="3"><b>Q3</b>: How do I compile/build uCON64?</a><br>
+<b>A</b>: Unless you are a software developer you probably do not need to
+   compile it. Just download one of the pre-built binary distributions and
+   proceed to <a href="#4">question 4</a>.<br>
+   To compile uCON64 you need a compiler :-) You can use either GCC (GNU
+   Compiler Collection) or the Visual C++ compiler. GCC can be freely
+   downloaded from <a href="http://www.gnu.org/order/ftp.html" target="_blank">
+   http://www.gnu.org/order/ftp.html</a>. Visual C++ can be bought for about 60
+   euro (at 15 May 2003) in most computer shops.<br>
+   uCON64 can be compiled for Unix (GNU/Linux, Solaris, FreeBSD, OpenBSD), DOS,
+   BeOS, Windows (95/98/ME/NT/2000/XP), AmigaOS (PPC/68K) and Mac OS X.<br>
+   A DOS port of GCC (and other GNU development tools) named DJGPP is
+   available from <a href="http://www.delorie.com/djgpp/" target="_blank">
+   http://www.delorie.com/djgpp/</a>. A Win32 port of GCC (and many other GNU
+   tools and libraries) named Cygwin is available from
+   <a href="http://www.cygwin.com" target="_blank">http://www.cygwin.com</a>.
+   Another Win32 port of GCC (and other tools) named MinGW is available from
+   <a href="http://www.mingw.org" target="_blank">http://www.mingw.org</a>.<br>
+<br>
+   <b>Configuring uCON64</b><br>
+   Some features of uCON64 are configurable (only) at compile time. They are:<br>
+   - whether uCON64 will produce debug output (default: no)<br>
+   - whether uCON64 will have support for parallel port backup units (default:
+   yes)<br>
+   - whether uCON64 will use the ppdev interface for parallel port I/O (default:
+   no)<br>
+   - whether uCON64 will be able to use color in its display output (default:
+   yes)<br>
+   - whether add-on libraries will be dynamically loaded or linked (default:
+   loaded)<br>
+   - whether uCON64 will be able to use the discmage library (default: yes)<br>
+   - whether uCON64 will have support for the CD64 (default: no)<br>
+   - whether uCON64 will be able to read .gz and .zip files (default: not if a
+   Makefile "template" is used, see below)<br>
+   - whether uCON64 will use libusb/have support for USB backup units (default:
+   no)<br>
+<br>
+   The presence or settings of these features is controlled by the header file
+   config.h in combination with the makefiles. There are two ways to create a
+   config.h and makefiles for uCON64:<br>
+   (Before executing one of these steps read the section below concerning gzip
+   &amp; zip support)<br>
+   1.) Run the configure script "configure".<br>
+   If you want default settings type:<br>
+   &nbsp;&nbsp; ./configure<br>
+   Otherwise you have to pass options to the configure script. To see which
+   features can be controlled with the configure script, type:<br>
+   &nbsp;&nbsp; ./configure --help<br>
+   2.) Copy a tried-and-tested config.h "template" to a file config.h and copy
+   a Makefile "template" to a file Makefile.<br>
+<br>
+   The first way does not work under command.com or cmd.exe as these shells
+   cannot (directly) execute Bash shell scripts. command.com is the default
+   shell on Windows&nbsp;9x, cmd.exe is the default shell on Windows&nbsp;XP
+   (NT/2000).<br>
+   So, for DOS (DJGPP) copy config.h.orig to config.h and to
+   libdiscmage/config.h, Makefile.orig to Makefile and
+   libdiscmage/Makefile.orig to libdiscmage/Makefile. You can do the same for
+   Cygwin, but in Cygwin you can get the same effect much easier by just
+   running the configure script.<br>
+   For Windows (Visual C++) copy config.h.vc6 to config.h and to
+   libdiscmage/config.h. Do not copy any of the Makefile.vc6 files. You may do
+   so, but do not complain if these instructions do not seem to be right after
+   having done that.<br>
+   To save us some work, for MinGW we only support configuring and building
+   uCON64 from within MSYS (MinGW's POSIX build environment, a port of Bash).<br>
+<br>
+   <b>Support for files in gzip (.gz) or zip format (.zip)</b><br>
+   For gzip &amp; zip support you need zlib. If you do not have zlib you can
+   get it from <a href="http://www.gzip.org/zlib/" target="_blank">
+   http://www.gzip.org/zlib/</a>. There are two ways to get gzip &amp; zip
+   support for uCON64:<br>
+   1.) When using the configure script.<br>
+   By default gzip &amp; zip support will be enabled if configure can find zlib
+   on your system. If you want to disable the feature, pass the option
+   --without-zlib to the configure script.<br>
+   2.) When using config.h and Makefile "templates".<br>
+   By default gzip &amp; zip support is disabled. If you want to enable it you
+   have to define the constant HAVE_ZLIB_H in config.h and libdiscmage/config.h
+   (remove the "//" in front of the line that defines it) <i>and</i> ZLIB in
+   Makefile and libdiscmage/Makefile (remove the "#" in front of the line that
+   defines it).<br>
+<br>
+   If you want to change other things in config.h or the makefiles do so before
+   proceeding to the next step.<br>
+   You have finished the configuration steps now. The next instructions will
+   tell you how to compile uCON64 with the settings you just made.<br>
+<br>
+   <b>Compiling uCON64</b><br>
+   By default the library discmage will also be compiled. Discmage is used for
+   several CD-related functions in uCON64, but is not required for non
+   CD-related functions. For example, you will not need discmage for any of the
+   SNES functions. Skip the next step if you do not want uCON64 to use
+   discmage.<br>
+<br>
+   If you do not have a reason not to compile discmage and you are using DJGPP,
+   Cygwin or MinGW you need the library libgcc.a. This library comes with those
+   compilers but is usually not located in the standard library directory.
+   However, the compiler needs to know where to find it. There are two possible
+   ways to solve this problem:<br>
+   1.) Copy the file libgcc.a to the standard library directory.<br>
+   2.) Edit the file libdiscmage/Makefile so that the variable GCCA_DIR points
+   to the directory containing libgcc.a.<br>
+   The first option (copying the file) is preferred.<br>
+<br>
+   Now read the relevant section for the compiler you are using.<br>
+<br>
+   <b>GCC</b> (including DJGPP and Cygwin)<br>
+   If you use Bash you may have to take one extra step before running make.
+   Because of a strange design decision made by the authors of Bash, the
+   environment variable OSTYPE is not exported on all platforms. Because of
+   that, on several platforms (like FreeBSD and Mac OS X), you have to export it
+   manually before running make. You can do that with the following command:<br>
+   &nbsp;&nbsp; export OSTYPE<br>
+<br>
+   If you have compiled uCON64 in the same directory before, first type:<br>
+   &nbsp;&nbsp; make clean<br>
+<br>
+   If you want to compile the source code including discmage, type:<br>
+   &nbsp;&nbsp; make<br>
+   If you do not want to compile discmage the last command should look like
+   this for DJGPP, Cygwin and MinGW:<br>
+   &nbsp;&nbsp; make ucon64.exe<br>
+   Otherwise it should look like this:<br>
+   &nbsp;&nbsp; make ucon64<br>
+<br>
+   If you use Mac OS X and get an error message that dlfcn.h cannot be found,
+   please install the library
+   <a href="http://www.opendarwin.org/projects/dlcompat/" target="_blank">dlcompat</a>
+   and try again.<br>
+   On Unix systems other than GNU/Linux you might need to use "gmake" (GNU
+   make) instead of "make". If the last make command has been successfully
+   executed you can find the executable in the directory where you ran make.
+   For DOS, Cygwin and MinGW you are now ready to proceed to
+   <a href="#4">question 4</a>. For the other platforms one extra command is
+   needed (or simply handy):<br>
+   &nbsp;&nbsp; make install<br>
+<br>
+   Under Unix this command will ask for root's password, because uCON64 needs
+   to be setuid root for parallel port access and because regular users usually
+   do not have write access in the default installation directory,
+   /usr/local/bin. If you configured uCON64 without support for parallel port
+   backup units or if you configured it to use ppdev, uCON64 need not be setuid
+   root. Just copy ucon64 to any directory you like.<br>
+   Under Mac OS X it is usual to install programs in /usr/bin. You can override
+   the default destination directory /usr/local/bin by setting the environment
+   variable DESTDIR. So, in order to install uCON64 in the directory /usr/bin
+   use a command like:<br>
+   &nbsp;&nbsp; DESTDIR=/usr/bin make install<br>
+   This works also on Unix.<br>
+   "make install" also creates a configuration directory for uCON64 and copies
+   discmage.so (Unix and BeOS) or discmage.dylib (Mac OS X) to it, so you may
+   find it easier to use it.<br>
+   Under BeOS this command will open a dialog window. Just follow the
+   instructions.<br>
+<br>
+   <b>Visual C++</b><br>
+   Start a command shell or "prompt" and go to the correct directory. If you
+   have compiled uCON64 in the same directory before, first type:<br>
+   &nbsp;&nbsp; nmake /f Makefile.vc6 clean<br>
+<br>
+   If you want to compile the source code including discmage, type:<br>
+   &nbsp;&nbsp; nmake /f Makefile.vc6<br>
+   If you do not want to compile discmage the last command should look like
+   this:<br>
+   &nbsp;&nbsp; nmake /f Makefile.vc6 ucon64.exe<br>
+<br>
+   Under GNU/Linux, DOS and Windows you could use UPX to compress the
+   executable. UPX is available from
+   <a href="http://upx.sourceforge.net" target="_blank">
+   http://upx.sourceforge.net</a>.<br>
+<br>
+<p>
+<a name="4"><b>Q4</b>: How do I install uCON64?</a><br>
+<b>A</b>: It depends on what operating system you use. First read the
+   information specific for your operating system. Then continue to the section
+   <a href="#configbin">"How to configure the uCON64 executable"</a>.<br>
+<br>
+   <b>Unix (GNU/Linux, FreeBSD, Solaris), BeOS &amp; Mac OS X</b><br>
+   Start a command line shell like Bash. Then extract the binary package.
+   To extract or unpack a package in .tar.gz format use a command line like
+   this:<br>
+   &nbsp;&nbsp; tar xvzf ucon64-1.9.8-1-linux-bin.tar.gz<br>
+   Old versions of tar might not support the option z. In that case use a
+   command line like:<br>
+   &nbsp;&nbsp; gunzip -c ucon64-1.9.8-1-linux-bin.tar.gz | tar xvf -<br>
+   To unzip a .zip file use a command line like this:<br>
+   &nbsp;&nbsp; unzip ucon64-1.9.8-1-beos-bin.zip<br>
+   You should replace the file name with the name of the file you downloaded
+   for your operating system. Unpacking or extracting a package does not have
+   to be done on the command line. You could also use a program with a GUI
+   (Graphical User Interface) like KDE's ark or a similar program.<br>
+   After you have extracted the package install uCON64 by running the install
+   script. On Unix, if you do not have root access, copy the uCON64 executable
+   to a directory in your PATH. If you do have root access (i.e., know the root
+   password), run the shell script install.sh. On BeOS just run the script
+   install_beos.sh. For Unix do something like this:<br>
+   &nbsp;&nbsp; cd ucon64-1.9.8-1-linux-bin<br>
+   &nbsp;&nbsp; ./install.sh<br>
+   By default install.sh will try to copy ucon64 to /usr/local/bin. You can
+   specify another directory by setting the environment variable DESTDIR.
+   On Mac OS X programs are usually installed in the directory /usr/bin. Users
+   of that OS might want to install uCON64 with a command like:<br>
+   &nbsp;&nbsp; DESTDIR=/usr/bin ./install.sh<br>
+<br>
+   For copier I/O BeOS users will need Caz's driver inside ioport.zip or
+   available at <a href="http://www.infernal.currantbun.com" target="_blank">
+   http://www.infernal.currantbun.com</a>. You only need this driver if you
+   will use uCON64 for communication with a backup unit. After you have decided
+   whether to download ioport.zip continue on BeOS with:<br>
+   &nbsp;&nbsp; cd ucon64-1.9.8-1-beos-bin<br>
+   &nbsp;&nbsp; ./install_beos.sh<br>
+   After the last command a dialog window will come up. Simply follow the
+   instructions.<br>
+<br>
+   Now continue to the section
+   <a href="#configbin">"How to configure the uCON64 executable"</a>.<br>
+<br>
+   <b>Windows (95/98/ME/NT/2000/XP) &amp; DOS</b><br>
+   On Windows, unpack the package with a program like
+   <a href="http://www.winzip.com/" target="_blank">WinZip</a>,
+   <a href="http://www.rarlab.com/" target="_blank">WinRAR</a> or
+   <a href="http://www.powerarchiver.com/" target="_blank">Power Archiver</a>.
+   On plain DOS (DOS without Windows) use a program like
+   <a href="http://www.pkware.com/" target="_blank">PKUNZIP</a>.<br>
+   We recommend to extract the package to a directory (or folder) of its own.
+   The zip file contains a directory, but you need not use that directory.<br>
+   Windows&nbsp;NT/2000/XP users are likely to need a driver for copier I/O. You
+   do not need a driver if you use Windows&nbsp;95, 98 or ME. A driver is also
+   not needed when you will not use uCON64 to communicate with a backup unit.<br>
+   You could use a driver like UserPort, DlPortIO, io.dll or inpout32.dll.
+<!-- UserPort doesn't seem to have an official homepage. -->
+   You can download DlPortIO at
+   <a href="http://www.driverlinx.com/DownLoad/dnload.htm#Windows%2095/NT%20Port%20I/O%20Driver" target="_blank">
+   http://www.driverlinx.com/DownLoad/dnload.htm#Windows%2095/NT%20Port%20I/O%20Driver</a>, directly at
+   <a href="http://www.driverlinx.com/DownLoad/DlPortIO.htm" target="_blank">
+   http://www.driverlinx.com/DownLoad/DlPortIO.htm</a> or from
+   <a href="http://ucon64.sourceforge.net/" target="_blank">the uCON64 homepage</a>.
+   You can find io.dll in a file named io.zip at
+   <a href="http://www.geekhideout.com/iodll.shtml" target="_blank">
+   http://www.geekhideout.com/iodll.shtml</a> and you can find inpout32.dll in
+   a file named inpout32_source_and_bins.zip at
+   <a href="http://www.logix4u.net" target="_blank">http://www.logix4u.net</a>.
+   UserPort and io.dll are included with the binary release. We recommend
+   DlPortIO and io.dll. To install DlPortIO simply start port95nt.exe. After the
+   installation is completed, copy DLPORTIO.dll from the Windows system(32)
+   directory to uCON64's configuration directory. To install io.dll or
+   inpout32.dll, put them in uCON64's configuration directory. See the section
+   <a href="#configbin">"How to configure the uCON64 executable"</a> for more
+   information about the configuration directory.<br>
+   uCON64 displays a message if it finds DLPORTIO.dll, io.dll or inpout32.dll
+   before communicating with a copier. It displays this message before it
+   displays file information, so you might want to use the command line switch
+   -q. Only the Windows versions of uCON64 are able to use DLPORTIO.dll, io.dll
+   or inpout32.dll. If more than one I/O driver is present in the configuration
+   directory, uCON64 will first try to load DLPORTIO.dll. If that fails it will
+   try to load io.dll. If that also fails it will try to load inpout32.dll.<br>
+   It <i>is</i> possible to use the DOS version of uCON64 as a transfer tool
+   under Windows&nbsp;XP without UserPort. We have received reports from people
+   who were able to send and receive ROMs under Windows&nbsp;XP as a regular
+   user (no Administrator rights and not "Power Users").<br>
+<br>
+<a name="cmdline">The command line environment<br></a>
+   uCON64 is a command line program and is usually started from a program like
+   command.com (Windows&nbsp;9x/ME) or cmd.exe (Windows&nbsp;NT/2000/XP). You
+   can start command.com or cmd.exe by choosing the option "Run..." from the
+   start menu. You have to type the word command.com or cmd.exe yourself.
+   Command.com and cmd.exe are command line interpreters. That means that they
+   will try to interpret any command you type followed by a press on the enter
+   or return key. When you type a command, the command line interpreter will
+   first look in the current directory and if it cannot find a program file
+   with the correct name it will search the so-called "path". This means that
+   the program file of uCON64 (ucon64.exe) must either be present in the
+   current directory or in one of the directories of your path. It is really
+   comfortable to have ucon64.exe present in your path, because you can simply
+   use the name ucon64 from any directory without first having to copy
+   ucon64.exe to that directory.<br>
+   The path is defined by the environment variable PATH. You can check the
+   current value of that variable with the command:<br>
+   &nbsp;&nbsp; echo %PATH%<br>
+   You could copy uCON64 to one of those directories. However, we recommend
+   putting ucon64.exe in a directory of its own (on Windows). So, you should
+   modify the value of PATH. On Windows&nbsp;9x/ME you should do that in the
+   following way. Edit the file c:\autoexec.bat and add a line like:<br>
+   &nbsp;&nbsp; set PATH=%PATH%;c:\ucon64<br>
+   Replace c:\ucon64 with the drive and directory where you extracted the
+   uCON64 files to. Save the modified c:\autoexec.bat and reboot.
+   On Windows&nbsp;XP (NT/2000?) you should press the buttons or icons start
+   -&#62; Control Panel -&#62; System -&#62; Advanced -&#62; Environment
+   Variables and edit the value of path in the section System variables or
+   else in the section User variables. You have to start a new instance of
+   cmd.exe before the changes take effect.
+   After you have made your changes (and have rebooted your PC if you are using
+   Windows&nbsp;9x/ME or DOS) check the value of PATH again. It should now list
+   the directory you just added.<br>
+   For information on how to use the command line see
+   <a href="#49">question 49</a>.<br>
+<br>
+<a name="configbin"><b>How to configure the uCON64 executable</b></a><br>
+   First run uCON64 once. Use a command like:<br>
+   &nbsp;&nbsp; ucon64 -version<br>
+   If you have not run uCON64 before you should see a message that it created
+   a file with a name like ucon64.cfg (DOS version) or .ucon64rc (all other
+   versions). If you are using GNU/Linux and uCON64 crashes see
+   <a href="#46">question 46</a>.<br>
+   Then search for the line that starts with "configuration file". It should
+   say "present" between the parentheses. The file name after the colon is the
+   name of the configuration file. The configuration file may contain names of
+   variables that influence the behaviour of uCON64. Here follows a table of
+   the variable names uCON64 recognises:<br>
+<br>
+<table border="1">
+  <tr>
+    <th><tt>variable name</tt></th>
+    <th><tt>meaning</tt></th>
+  </tr>
+  <tr>
+    <td><tt>version</tt></td>
+    <td><tt>version of configuration file</tt></td>
+  </tr>
+  <tr>
+    <td><tt>backups</tt></td>
+    <td><tt>uCON64 will create backups of the files it modifies if it is 1</tt></td>
+  </tr>
+  <tr>
+    <td><tt>ansi_color</tt></td>
+    <td><tt>uCON64 will use color in its display output if it is 1</tt></td>
+  </tr>
+  <tr>
+    <td><tt>parport_dev</tt></td>
+    <td><tt>name of parallel port device (AmigaOS or Linux with ppdev)</tt></td>
+  </tr>
+  <tr>
+    <td><tt>parport</tt></td>
+    <td><tt>hardware address of parallel port (AmigaOS: port number)</tt></td>
+  </tr>
+  <tr>
+    <td><tt>discmage_path</tt></td>
+    <td><tt>path to discmage DLL</tt></td>
+  </tr>
+  <tr>
+    <td><tt>ucon64_configdir</tt></td>
+    <td><tt>name of configuration directory of uCON64</tt></td>
+  </tr>
+  <tr>
+    <td><tt>ucon64_datdir</tt></td>
+    <td><tt>name of DAT file directory of uCON64</tt></td>
+  </tr>
+  <tr>
+    <td><tt>emulate_&#60;console&#62;</tt></td>
+    <td><tt>command line to use to start emulator for &#60;console&#62;</tt></td>
+  </tr>
+  <tr>
+    <td><tt>emulate_&#60;CRC32&#62;</tt></td>
+    <td><tt>command line to use to start emulator for file with that CRC32</tt></td>
+  </tr>
+  <tr>
+    <td><tt>emulate_0x&#60;CRC32&#62;</tt></td>
+    <td><tt>command line to use to start emulator for file with that CRC32</tt></td>
+  </tr>
+  <tr>
+    <td><tt>f2afirmware</tt></td>
+    <td><tt>path to F2A USB firmware</tt></td>
+  </tr>
+  <tr>
+    <td><tt>iclientu</tt></td>
+    <td><tt>path to GBA client binary (for USB code)</tt></td>
+  </tr>
+  <tr>
+    <td><tt>iclientp</tt></td>
+    <td><tt>path to GBA client binary (for parallel port code)</tt></td>
+  </tr>
+  <tr>
+    <td><tt>ilogo</tt></td>
+    <td><tt>path to iLinker logo file</tt></td>
+  </tr>
+  <tr>
+    <td><tt>gbaloader</tt></td>
+    <td><tt>path to GBA multi-game loader</tt></td>
+  </tr>
+</table><br>
+   Each of these variables can also be set in the command line environment. If
+   they are they take precedence over the values set in the configuration file.
+   For example, if you set the environment variable ansi_color to 0 while the
+   configuration file sets it to 1 uCON64 will not use color. In command.com or
+   cmd.exe you can set an environment variable with the command "set", in Bash
+   you should do it with the command "export". For example:<br>
+   &nbsp;&nbsp; export ansi_color=0<br>
+   Some variables can be overruled with command line switches. For example, to
+   disable the use of color even if both the configuration file and the
+   environment have the variable ansi_color set to 1, use the switch -ncol:<br>
+   &nbsp;&nbsp; ucon64 "Rock Fall (PD).zip" -ncol<br>
+   The variable ucon64_configdir is only really important for the Windows
+   versions of uCON64. To install the I/O port driver io.dll or inpout32.dll
+   place it (or both) in the directory that uCON64 lists as its configuration
+   directory.<br>
+   If you use DOS or Windows&nbsp;9x and do not want uCON64 to create
+   ucon64.cfg or .ucon64rc files all over the place (current directories) see
+   <a href="#25">question 25</a>.<br>
+   You might also want to make use of DAT files. See
+   <a href="#36">question 36</a> for more information.<br>
+<br>
+<p>
+<a name="5"><b>Q5</b>: Sometimes my terminal seems to be locked after "ucon64
+   -xswc &#60;file&#62; | more"</a><br>
+<b>A</b>: You are right, it <u>seems</u> to be locked, but the only thing that
+   happened is that the character echoing of the terminal is disabled by more.
+   Just do not do that ;-)<br>
+<br>
+<p>
+<a name="6"><b>Q6</b>: How do I make uCON64 only display information about a
+   ROM?</a><br>
+<b>A</b>: If you want to display information about a ROM just run uCON64 with
+   the name of the ROM as the only argument. Sometimes you will have to specify
+   the console type for uCON64, because the ROM dump is damaged or simply not
+   detected correctly by uCON64. For example:<br>
+   &nbsp;&nbsp; ucon64 -snes "Super Aleste (J) [t1].swc"<br>
+<br>
+<p>
+<a name="7"><b>Q7</b>: I have two parallel ports in my PC and my Flash Advance
+   Linker is connected to the second. However, when I try to use uCON64 to send
+   a demo to the FAL something goes wrong. What happened?</a><br>
+<b>A</b>: Well, many things could have gone wrong, but uCON64 only detects your
+   first parallel port (it stops probing for a parallel port if it finds one).
+   So, you should specify the address of the second parallel port on the
+   command line. For example:<br>
+   &nbsp;&nbsp; ucon64 -xfal demo.gba -port 0x278<br>
+<br>
+   The I/O address of the parallel port could be 0x3bc, 0x378 or 0x278. If you
+   do not want to type the address of the parallel port every time you want to
+   transfer something to your FAL (or another copier) edit .ucon64rc or
+   ucon64.cfg (for the DOS version) and remove the hash symbol in front of the
+   line that starts with "parport=" (without the quotes). If that line is not
+   present add one. You do not have to use the prefix 0x. For example:<br>
+   &nbsp;&nbsp; parport=278<br>
+   You can also set an environment variable with the name parport to the right
+   value. The value of the environment variable takes precedence over the value
+   in the configuration file.<br>
+<br>
+<p>
+<a name="8"><b>Q8</b>: I tried to dump a SNES cartridge using a Super Wild Card
+   connected to my PC with an interlink cable, but it did not work. What is
+   wrong?</a><br>
+<b>A</b>: You should not use an interlink cable, but a standard bidirectional
+   parallel cable, i.e., a cable with male DB-25 connectors at both ends where
+   the pins are connected in the following way:<br>
+   pin 1&nbsp; &#60;-&#62; pin 1<br>
+   pin 2&nbsp; &#60;-&#62; pin 2<br>
+   pin 3&nbsp; &#60;-&#62; pin 3<br>
+   pin 4&nbsp; &#60;-&#62; pin 4<br>
+   pin 5&nbsp; &#60;-&#62; pin 5<br>
+   pin 6&nbsp; &#60;-&#62; pin 6<br>
+   pin 7&nbsp; &#60;-&#62; pin 7<br>
+   pin 8&nbsp; &#60;-&#62; pin 8<br>
+   pin 9&nbsp; &#60;-&#62; pin 9<br>
+   pin 10 &#60;-&#62; pin 10<br>
+   pin 11 &#60;-&#62; pin 11<br>
+   pin 12 &#60;-&#62; pin 12<br>
+   pin 13 &#60;-&#62; pin 13<br>
+   pin 15 &#60;-&#62; pin 15<br>
+   pin 25 &#60;-&#62; pin 25<br>
+   The other pins may be left unconnected. Pins 2-9 are used for output, pins
+   10-13 &amp; 15 for input and pin 25 is ground.<br>
+<br>
+<p>
+<a name="9"><b>Q9</b>: I have a SNES ROM of which GoodSNES says it is good, but
+   uCON64 says it is bad. Who is right?</a><br>
+<b>A</b>: It depends.<br>
+   If uCON64 displays the text "Checksum: Bad" GoodSNES might be right.<br>
+   There may be several reasons why uCON64 reports that the check sum is bad.
+   It could have made a mistake while determining if the ROM is a HiROM or a
+   LoROM dump. Try the switches -hi and -nhi and see if uCON64 reports the
+   check sum differently. Sometimes ROM dumps are detected as not being
+   interleaved while they actually are. Try the switches -int and -int2 and see
+   if you get better results. It could also be that uCON64 detects the ROM as
+   interleaved while it is not. The switch -nint might help in that case. If
+   the check sum is reported as good it is likely that the ROM is really good.
+   Alternatively, you may want to convert the ROM to a non-interleaved format
+   with -dint and run uCON64 on the converted ROM. Luckily you will not find
+   many interleaved ROM dumps that uCON64 cannot handle, probably because
+   Snes9x and ZSNES often cannot handle them either.<br>
+   Perhaps "Ok" looks nicer than "Bad", but it is far more important that
+   uCON64 displays DAT file information. If you installed a SNES DAT file (see
+   <a href="#36">question 36</a>) and uCON64 does not display a line with the
+   text "DAT info:" then the ROM dump could not be identified. It could be that
+   you  have an unknown ROM dump, but it is more likely that you have a modified
+   (i.e. bad) dump or an overdump. GoodSNES 0.999.5 does not use the CRC32 to
+   identify files, so if uCON64 does not display DAT info, but GoodSNES
+   identified it as good then uCON64 is right. For reasons see
+   <a href="#42">question 42</a>.<br>
+<br>
+<p>
+<a name="10"><b>Q10</b>: I have a SNES ROM of which Snes9x and ZSNES say it is
+   bad, but uCON64 says it is good ("Checksum: Ok"). Who is right?</a><br>
+<b>A</b>: uCON64, probably :-)<br>
+   Neither Snes9x 1.39 nor ZSNES 1.36 calculate the check sum correctly for BS
+   ROMs, so you can be pretty sure the ROM is good if it is a BS ROM. See
+   previous answer.<br>
+<br>
+<p>
+<a name="11"><b>Q11</b>: I have a SNES ROM of which uCON64 said it was bad, so
+   I used the -chk option. Now uCON64, Snes9x and ZSNES all say the ROM is
+   good, but GoodSNES still says it is bad. How can that be, I did fix the ROM,
+   right?</a><br>
+<b>A</b>: No, you did not. When you use -chk uCON64 changes four bytes in the
+   ROM dump, so that they match with the calculated check sum. However, the ROM
+   stays just as bad as it was.<br>
+   Use -chk with care. -chk might be useful if you want to store a ROM
+   somewhere without using a database with its check sum. You can then later
+   check if the ROM is still the same as it was when you stored it by using the
+   check sum that is stored in the ROM.<br>
+   That said, it is a <i>much</i> better idea to create a DAT file from your
+   ROM collection if you plan to store it somewhere. See
+   <a href="#42">question 42</a> for reasons and <a href="#43">question 43</a>
+   for information on how to create a DAT file.<br>
+<br>
+<p>
+<a name="12"><b>Q12</b>: Some SNES games do not work on my Super Wild Card.
+   What can I do about that?</a><br>
+<b>A</b>: You can do several things. First make sure the original game
+   cartridge does not contain any special chips that your Super Wild Card
+   does not support (look at the ROM type information). For example, if you are
+   trying to run a game like Super Mario Kart (which uses a DSP chip) your
+   Super Wild Card should have an extension with the correct DSP chip (or have
+   the right cartridge with that chip plugged in your SWC). Then make sure you
+   are using a good dump (look at the check sum information). After you have
+   done that, check if the file is interleaved (look at the line that starts
+   with "Interleaved/Swapped:"). If the file is interleaved convert it to Super
+   Wild Card format (deinterleaved) by using the option -swc. Also verify that
+   the backup unit header is correct. The option -dbuh can be helpful with
+   that. You can make uCON64 rewrite the header also with -swc. uCON64 uses the
+   information in the ROM dump's header when sending the file to the Super Wild
+   Card, so this is important! You can also try to rewrite the header while
+   using one of the switches -hi or -nhi to force uCON64 to handle it as a
+   HiROM or a LoROM dump. Additionally you could use one of the switches -hd,
+   -nhd or -hdn to specify the backup unit header size. For example:<br>
+   &nbsp;&nbsp; ucon64 -swc -nhi -hd "Batman Revenge of the Joker (U).swc"<br>
+   There are many (good) ROMs on the internet with incorrect headers, so this
+   might well solve the problem.<br>
+   Several games contain a copy protection that prevents them from running on
+   a backup unit, so this can also be a reason why the game does not work. You
+   can try -k to remove that protection. Several games also check if they are
+   running on a PAL or an NTSC SNES. Use -f to remove that form of copy
+   protection. Some other games check the ROM speed. You can use -l for those.
+   Some games have more than one type of copy protection, so it could be
+   necessary to use uCON64 several times with a different option.<br>
+   See <a href="#39">question 39</a> and <a href="#40">question 40</a> for more
+   information about a new feature in uCON64 1.9.8beta8 that can be used to
+   update or improve the options -k, -f and -l without having to (re)compile
+   the source code.<br>
+<br>
+   Some games seem to work, but after a while it becomes clear that they do not
+   run as was intended. Two examples of such games are Demon's Crest and Mega
+   Man X. Here follows an explanation on how to get them to work. All credits
+   go to Gideon Zhi for sharing this info on cherryroms. Start with ROM images
+   in SWC format.<br>
+<br>
+   Demon's Crest:<br>
+   &nbsp;&nbsp; ucon64 -stp "Demon's Crest.swc"<br>
+   &nbsp;&nbsp; cat "Demon's Crest.swc" "Demon's Crest.swc" &#62; "Demon's Crest (SWC-fix).swc"<br>
+   &nbsp;&nbsp; ucon64 -swc "Demon's Crest (SWC-fix).swc"<br>
+<br>
+   Mega Man X:<br>
+   &nbsp;&nbsp; ucon64 -stp "Mega Man X.swc"<br>
+   &nbsp;&nbsp; ucon64 -padn=2097152 "Mega Man X.swc"<br>
+   &nbsp;&nbsp; cat "Mega Man X.swc" "Mega Man X.swc" &#62; "Mega Man X (SWC-fix).swc"<br>
+   &nbsp;&nbsp; ucon64 -swc "Mega Man X (SWC-fix).swc"<br>
+<br>
+   WinDOS users without the program cat should use the command copy. For
+   example:<br>
+   &nbsp;&nbsp; copy /b "Mega Man X.swc" + "Mega Man X.swc" "Mega Man X (SWC-fix).swc"<br>
+<br>
+   However, starting from version 1.9.8beta6 the method described above is not
+   necessary anymore for Demon's Crest. Use the option -k instead. For
+   Mega Man X you will need version 1.9.8beta7 or later.<br>
+<br>
+   Finally, you could have a look at an incomplete
+<!-- Keep next URL absolute. -->
+   <a href="http://ucon64.sourceforge.net/ucon64/SWC-compatibility.txt" target="_blank">
+   Super Wild Card compatibility list</a> on the uCON64 homepage (it can also be
+   found in the source package) to see if it is possible to run a certain game
+   on a Super Wild Card <u>2.8cc 32 Mbit PAL</u>.<br>
+<br>
+<p>
+<a name="13"><b>Q13</b>: Which backup devices are supported by uCON64?</a><br>
+<b>A</b>: See <a href="hardware.html" target="_blank">hardware.html</a>.<br>
+<br>
+<p>
+<a name="14"><b>Q14</b>: I use Windows&nbsp;XP (NT/2000). When I try to run
+   ucon64.exe under command.com (start -&#62; Run... -&#62; command) uCON64
+   crashes. What do I do wrong?</a><br>
+<b>A</b>: You should not use command, use cmd instead. However, starting from
+   version 1.9.8 uCON64 should run fine under command.com.<br>
+<br>
+<p>
+<a name="15"><a name="frontend"><b>Q15</b>: I do not like the command line. Are
+   there graphical frontends available?</a><br>
+<b>A</b>: Yes, have a look at
+   <a href="http://ucon64.sourceforge.net/index2.html#ucon64gui" target="_blank">
+   http://ucon64.sourceforge.net/index2.html#ucon64gui</a><br>
+<br>
+<p>
+<a name="16"><b>Q16</b>: Where do I get PPF, APS or IPS patches?</a><br>
+<b>A</b>:<br>
+   <form method=GET action="http://www.google.com/search" target="_blank">
+   <table bgcolor="#FFFFFF" border="0">
+   <tr><td><br>
+   <a href="http://www.google.com/" target="_blank">
+     <img src="http://www.google.com/logos/Logo_25wht.gif" alt="Google" align="absmiddle">
+   </a><br><br>
+   <input type=text name=q maxlength=255 value="ppf aps ips patch"><br><br>
+   <input type=submit name=btnG value="Google Search"><br>
+   </td></tr>
+   </table>
+   </form>
+   <br>
+<br>
+<p>
+<a name="17"><b>Q17</b>: Where do I get ROMs/Games?</a><br>
+<b>A</b>: <a href="http://www.ebay.com">www.ebay.com</a><br>
+<br>
+<p>
+<a name="18"><b>Q18</b>: What does "[ROM|IMAGE|SRAM|FILE|DIR|ARCHIVE]..." mean
+   in the help?</a><br>
+<b>A</b>: The pipe symbol ('|') should be read as "or".<br>
+   The square brackets indicate that "ROM|IMAGE|SRAM|FILE|DIR|ARCHIVE" is
+   optional. For many options you have to specify either a ROM or a CD image or
+   an SRAM file or some other file or a directory or an archive. However, there
+   are a few options that do not need any of those arguments.<br>
+   The ellipses indicate that you may specify one or more ROMs, CD images etc.
+   Several options even support combinations of ROMs, directories and
+   archives.<br>
+   For the current version of uCON64, archive should be read as "ZIP file".<br>
+<br>
+<p>
+<a name="19"><b>Q19</b>: Why is my backup unit (still) not supported?</a><br>
+<b>A</b>: Get us the protocol or the sources of existing transfer tools and you
+   will see what happens. uCON64 already supports all important backup units
+   which were and are available. Manufacturers of devices which are still not
+   supported are welcome to contact us.<br>
+<br>
+<p>
+<a name="20"><b>Q20</b>: How do I specify a bank number for the GB Xchanger?</a><br>
+<b>A</b>: Separate the bank number from the option -xgbxb with either a space or
+   an equal sign. For example:<br>
+   &nbsp;&nbsp; ucon64 -xgbxb 0 "Pokemon (Green).sav"<br>
+   or:<br>
+   &nbsp;&nbsp; ucon64 -xgbxb=0 "Pokemon (Green).sav"<br>
+<br>
+<p>
+<a name="21"><b>Q21</b>: What is the difference between the Flash Advance
+   Linker options -xfals and -xfalb &#60;n&#62;?</a><br>
+<b>A</b>: When saving -xfals saves all 256 kB while -xfalb &#60;n&#62; saves
+   only the 64 kB of bank n. When restoring -xfals starts restoring the SRAM in
+   bank 1 while -xfalb &#60;n&#62; starts in bank n. For example, if you have
+   Super Mario Advance 2 (J) on your flash card in "ROM bank" 4 and would like
+   to save/restore SRAM bank 4 you should do something like:<br>
+   &nbsp;&nbsp; ucon64 -xfalb 4 "Super Mario Advance 2 (J).sav"<br>
+   The file size determines how many bytes are restored, even when you use
+   -xfalb &#60;n&#62;. So, if the file is greater than 64 kB more than one bank
+   will be written to.<br>
+<br>
+<p>
+<a name="22"><b>Q22</b>: uCON64 exits with the message "ERROR: Flash card erase
+   failed" when I try to write a multi-game file to my flash card. What is
+   wrong?</a><br>
+<b>A</b>: The multi-game file you created is too large for your flash card. When
+   you make a multi-game file do not forget that the loader or "game pack" file
+   also needs some space (32 kB) on the flash card. Do not pass a size to -multi
+   that is larger than your flash card. Passing a smaller size is correct
+   though. For example, to create a multi-game file that fits on a 128 Mb (16
+   MB) card use a command line like this:<br>
+   &nbsp;&nbsp; ucon64 -gba -multi=128 loader.bin rom1.gba rom2.gba rom3.gba rom4.gba multi.bin<br>
+   You can also send it directly by using -xfalmulti:<br>
+   &nbsp;&nbsp; ucon64 -xfalmulti=128 rom1.gba rom2.gba rom3.gba rom4.gba<br>
+   Please note that when using -multi you have to specify a loader and the name
+   of the multi-game file to create, while when using -xfalmulti you must omit
+   them. -xfalmulti uses the variable gbaloader to find the loader. See
+   <a href="#configbin">"How to configure the uCON64 executable"</a>.<br>
+   Just like sometimes happens with Visoly's tool it is possible to get the
+   same message when the multi-game file is small enough. It will probably work when
+   you try again (with the same multi-game file).<br>
+<br>
+<p>
+<a name="23"><b>Q23</b>: Where can I get that loader or "game pack" file?</a><br>
+<b>A</b>: You can get it from <a href="http://www.visoly.com" target="_blank">
+   Visoly's site</a>. It is in a file with a name like
+   <a href="http://www.visoly.com/download/preboot32.zip">preboot32.zip</a>.
+   You can find it also on the uCON64 homepage.<br>
+<br>
+<p>
+<a name="24"><b>Q24</b>: What is the meaning of the -col option (SNES)?</a><br>
+<b>A</b>: Back in the days stupid people released games with green blood.
+   You can use a GFX or HTML editor to find an identical green color and
+   write down the color values in HTML style (#RRGGBB). Now you run
+   "ucon64 -col #RRGGBB" and it will calculate the hex value for the
+   corresponding SNES color. Ok? No? Then you do not need this option :-)<br>
+<br>
+<p>
+<a name="25"><b>Q25</b>: Why does uCON64 create ucon64.cfg (DOS executable) or
+   .ucon64rc (Win32 executable) files all over the place under DOS and
+   Windows&nbsp;9x (command.com)?</a><br>
+<b>A</b>: uCON64 needs to know where it can find the configuration file or a
+   directory where it can create one. It checks the environment for a variable
+   with the name UCON64_HOME, HOME, USERPROFILE or HOMEDRIVE and HOMEPATH (in
+   that order). Under Unix HOME is normally set. Under Windows&nbsp;XP and 2000
+   (NT?) USERPROFILE, HOMEDRIVE and HOMEPATH are usually set. If uCON64 cannot
+   find one of those environment variables it will look for the configuration
+   file in the current directory. If it cannot find a configuration file it
+   will create one. Under DOS and Windows&nbsp;9x none of those environment
+   variables are usually set. So, if you run uCON64 under DOS or
+   Windows&nbsp;9x in a directory where no configuration file exists it will
+   create one there. If you do not like this behaviour you should set one of
+   the mentioned environment variables yourself. You can do that by adding the
+   following line to the end of the file c:\autoexec.bat:<br>
+   &nbsp;&nbsp; set UCON64_HOME=c:\ucon64<br>
+   and reboot. You can also type that line on the command line. The directory
+   c:\ucon64 must exist, of course. If you want uCON64 to use another directory
+   you should change c:\ucon64 to a directory of your choice. You should not
+   let the last character of the environment variable be a forward or backward
+   slash.<br>
+   If you have Cygwin installed, the Cygwin runtime system will create an
+   internal environment for the uCON64 executable with HOME set to Cygwin's
+   home directory if it has not already been set. You can override this value
+   with the method mentioned earlier. However, if you set UCON64_HOME this does
+   not matter as the value of HOME will not be checked if UCON64_HOME is set.<br>
+   You can check which configuration file uCON64 uses by specifying the option
+   -version on the command line.<br>
+<br>
+<p>
+<a name="26"><b>Q26</b>: When I try to create a multi-game file under Windows I
+   get the message "The system cannot execute the specified program." (or
+   something similar). What does that mean?</a><br>
+<b>A</b>: You can get that message with the DOS executable if you pass more
+   than 127 characters as argument to uCON64. There are two solutions:<br>
+   1.) Use a Win32 executable (download one from
+   <a href="http://ucon64.sourceforge.net/" target="_blank">the uCON64
+   homepage</a>).<br>
+   2.) Rename the ROMs so that their combined names are short enough to fit
+   into 127 characters.<br>
+<br>
+<p>
+<a name="27"><b>Q27</b>: I tried to run a Win32 executable of uCON64 but Windows
+   gave an error message that cygwin1.dll, cygz.dll or zlib.dll could
+   not be found.</a><br>
+<b>A</b>: The Cygwin-compiled Win32 executable of uCON64 needs cygwin1.dll and
+   cygz.dll in order to run. The Visual C++-compiled Win32 executable needs
+   zlib.dll. You can download these files from
+   <a href="http://ucon64.sourceforge.net/" target="_blank">the uCON64 homepage</a>.
+   Copy the file(s) to the same directory as the uCON64 executable or to a
+   directory in your PATH.<br>
+<br>
+<p>
+<a name="28"><b>Q28</b>: How do I make uCON64 display one help screen at a
+   time?</a><br>
+<b>A</b>: Just as is stated in the last few lines of the help, pass the output
+   of uCON64 to a program that waits for a key after one screen of text. You
+   could use the program "more". In order to pass the output of uCON64 to more
+   you have to use the pipe symbol, '|'. On an international keyboard you can
+   type this symbol with the same key you use for the backslash ('\'). For
+   example:<br>
+   &nbsp;&nbsp; ucon64 -help|more<br>
+   To get only help on the options for a specific console, specify the console
+   on the command line. For example, to see only the help on Game Boy Advance
+   options:<br>
+   &nbsp;&nbsp; ucon64 -help -gba|more<br>
+   Press the space bar to see the next help screen, the enter key to see the
+   next line or q to quit.<br>
+<br>
+<p>
+<a name="29"><b>Q29</b>: I tried to send a ROM dump to my copier under
+   Windows&nbsp;XP (NT/2000), but uCON64 crashed. What is wrong?</a><br>
+<b>A</b>: If the message looks like this (for the Win32 (Cygwin) executable):<br>
+   &nbsp;&nbsp; 0 [main] ucon64 356 handle_exceptions: Exception: STATUS_PRIVILEGED_INSTRUCTION<br>
+   5570 [main] ucon64 356 open_stackdumpfile: Dumping stack trace to ucon64.exe.stackdump<br>
+   or like this:<br>
+   &nbsp;&nbsp; Illegal instruction (core dumped)<br>
+   then you did not install an I/O driver (correctly). In this case "install"
+   means that the I/O driver is running at the time uCON64 tries to access the
+   parallel port. See <a href="#4">question 4</a>.<br>
+   You will also see this message (with a Windows version of uCON64) if you
+   tried to enable EPP mode for the FAL under Windows XP (NT/2000) , i.e., you
+   specified the switch -xfalm, without an appropriate I/O port driver.
+   UserPort is <i>inappropriate</i> for this, because it enables access to I/O
+   ports up to 0x3ff. However, in order to enable EPP mode the FAL code tries
+   to access an I/O port with an address higher than 0x3ff. If you want to be
+   able to use EPP mode try io.dll or inpout32.dll.<br>
+   Starting with version 2.0.0 uCON64 will not crash anymore if it cannot
+   access the parallel port at start up (unless you use UserPort in combination
+   with a Windows version of uCON64).<br>
+<br>
+<p>
+<a name="30"><b>Q30</b>: I want to convert a NES ROM in iNES format to UNIF.
+   How do I know what board name to use?</a><br>
+<b>A</b>: The file
+   <a href="http://cvs.sourceforge.net/viewcvs.py/ucon64/ucon64/src/console/boardtable.txt?view=markup" target="_blank">
+   boardtable.txt</a> contains a table where you can find what game uses what
+   board. The file
+   <a href="http://cvs.sourceforge.net/viewcvs.py/ucon64/ucon64/src/console/boardnames?view=markup" target="_blank">
+   boardnames</a> contains a list of all known board names.<br>
+<br>
+<p>
+<a name="31"><b>Q31</b>: How do I convert a NES ROM from Pasofami to FFE? Or
+   from UNIF to Pasofami?</a><br>
+<b>A</b>: Use iNES as intermediate format. So, to do the conversion Pasofami
+   -&#62; FFE, do this conversion instead: first Pasofami -&#62; iNES and then
+   iNES -&#62; FFE.<br>
+   To answer the second question, to do the conversion UNIF -&#62; Pasofami do
+   this: UNIF -&#62; iNES and then iNES -&#62; Pasofami.<br>
+<br>
+<p>
+<a name="32"><b>Q32</b>: How do I specify dumper information when converting to
+   UNIF?</a><br>
+<b>A</b>: Create a text file with three lines. The first line should contain
+   the name (or "handle") of the person who dumped the cartridge. This line may
+   be 99 characters long at maximum. The next line should contain the date when
+   the cartridge was dumped. The format is dd-mm-yyyy. Instead of the hyphen
+   ('-') you may also use the slash ('/'). If day or month is smaller than 10
+   you may use 1 or 2 digits, for example:<br>
+   &nbsp;&nbsp; 02/02/2002<br>
+   or<br>
+   &nbsp;&nbsp; 2-2-2002<br>
+   The third and last line should contain information about the dumping means
+   that was used. This line may also contain 99 characters at maximum. Here is
+   an example info file:<br>
+   &nbsp;&nbsp; Dirk &#60;noisyb@gmx.net&#62;<br>
+   &nbsp;&nbsp; 26-7-2002<br>
+   &nbsp;&nbsp; Custom built hardware<br>
+   To get this information in a UNIF file, you should use the -dumpinfo switch
+   in combination with the -unif option. Say you store the dumper information
+   in the file info.txt. This would get the info in a UNIF file:<br>
+   &nbsp;&nbsp; ucon64 -unif smb.nes -mapr NROM -dumpinfo info.txt<br>
+   If you already have a UNIF file the command line looks almost the same:<br>
+   &nbsp;&nbsp; ucon64 -unif smb.unf -dumpinfo info.txt<br>
+   For an existing UNIF file you need not specify the board name, but you might
+   want to specify it if you want to change the board name in the file.
+   Afterwards run uCON64 on the newly written ROM to see if you were
+   successful. uCON64's display output should contain a dump info section then.<br>
+<br>
+<p>
+<a name="33"><b>Q33</b>: How do I specify that a NES game in UNIF format
+   supports multiple controller types?</a><br>
+<b>A</b>: Use the -ctrl switch multiple times on the <i>same</i> command line.
+   For example, to specify that a game supports the controller types "regular
+   joypad" and "power pad" you should use a command line like this:<br>
+   &nbsp;&nbsp; ucon64 -unif smb.nes -mapr NROM -ctrl 0 -ctrl 4<br>
+<br>
+<p>
+<a name="34"><b>Q34</b>: How do I enable or disable colors in the display
+   output of uCON64?</a><br>
+<b>A</b>: For the Windows and Unix executables colors are enabled by default.
+   For the DOS executable ANSI.SYS must be loaded. Under DOS and
+   Windows&nbsp;9x ANSI.SYS can be loaded by adding a line to the end of
+   c:\config.sys of the format "device=&#60;full path to ANSI.SYS&#62;".
+   Say ANSI.SYS is located in the directory c:\windows\command, then the line
+   would have to look like this:<br>
+   &nbsp;&nbsp; device=c:\windows\command\ansi.sys<br>
+   Changes made to config.sys under DOS and Windows&nbsp;9x will become active
+   only after a reboot.<br>
+   Under Windows&nbsp;XP/NT you should add a similar line to your
+   %SystemRoot%\system32\CONFIG.NT:<br>
+   &nbsp;&nbsp; device=%SystemRoot%\system32\ansi.sys<br>
+   The use of color can be disabled by specifying the switch -ncol on the
+   command line. You can also add the following line to the configuration file:<br>
+   &nbsp;&nbsp; ansi_color=0<br>
+<br>
+<p>
+<a name="35"><b>Q35</b>: When I try to convert a large number of files using
+   wildcards, uCON64 will only convert the first file. Is this a bug?</a><br>
+<b>A</b>: No, it is not. uCON64 versions before 1.9.8beta7 just do not support
+   wildcards. For example, to convert all files with the suffix (M$ speak:
+   extension) .mgd to Super Wild Card format, try this in Bash:<br>
+   &nbsp;&nbsp; find -name \*.mgd -exec ucon64 --snes --swc {} \;<br>
+   or this in command.com or cmd.exe:<br>
+   &nbsp;&nbsp; for %f in (*.mgd) do ucon64 --snes --swc "%f"<br>
+   However, this functionality has been added to uCON64 1.9.8beta7. With that
+   version you can do the same thing with:<br>
+   &nbsp;&nbsp; ucon64 --snes --swc *.mgd<br>
+   In case something goes terribly wrong and uCON64 crashes it might still be
+   necessary to use one of the previous methods, because every file will be
+   processed and not only the files before the crash.<br>
+<br>
+<p>
+<a name="36"><b>Q36</b>: Does uCON64 support DAT (RomCenter/GoodXXXX) files?</a><br>
+<b>A</b>: Yes, starting with version 1.9.8beta8 it does. First use the option
+   -version to see which configuration file uCON64 uses
+   (see <a href="#25">question 25</a>). Then open that file in an editor and
+   look for a line that starts with "ucon64_datdir=" (without the quotes). If
+   that line is not present add one. For example on Unix:<br>
+   &nbsp;&nbsp; ucon64_datdir=~/.ucon64/dat<br>
+   or on Windows:<br>
+   &nbsp;&nbsp; ucon64_datdir=c:\ucon64\dat<br>
+   You can use the tilde ('~') also on DOS and Windows. It will be interpreted
+   by uCON64 as the "home directory". The home directory is the directory
+   specified by the environment variable HOME or USERPROFILE or HOMEDRIVE and
+   HOMEPATH. If none of those environment variables are set, the current
+   directory will be handled as the home directory. Again, see
+   <a href="#25">question 25</a>.<br>
+   You can also set an environment variable with the name ucon64_datdir. The
+   value of the environment variable takes precedence over the value in the
+   configuration file.<br>
+   Then copy all DAT files you have into that directory. You can download DAT
+   files from sites like
+   <a href="http://emulationrealm.com/rcdat.php#Cowering_GoodTools" target="_blank">
+   http://emulationrealm.com/</a>,
+   <a href="http://www.romcenter.com/" target="_blank">
+   http://www.romcenter.com/</a> or
+   <a href="http://www.rommanager.com/" target="_blank">
+   http://www.rommanager.com/</a>. You can also find some DAT files on
+   <a href="http://ucon64.sourceforge.net/index.html#ucon64dat" target="_blank">
+   the uCON64 homepage</a>.<br>
+   uCON64 will automatically create index files which speed up the access to
+   the DAT files dramatically. Now uCON64 uses the information inside the DAT
+   files to identify or rename ROMs without an internal header.<br>
+   Starting with version 1.9.8beta8 uCON64 does not have an internal database
+   anymore, so you will <b>need</b> DAT files for files like NES ROM dumps.<br>
+   You can check which DAT files are used by uCON64 with the option -db. You
+   can view or list all DAT entries with the option -dbv. You can search for a
+   specific CRC32 value with the option -dbs. The force-console-type options
+   can be used in combination with -db, -dbv and -dbs. For example, to see
+   which DAT files are used to identify SNES ROM dumps, type:<br>
+   &nbsp;&nbsp; ucon64 -db -snes<br>
+   If uCON64 reports that it found 0 DAT files you did not install the DAT
+   file(s) correctly.<br>
+<br>
+<p>
+<a name="37"><b>Q37</b>: Some SNES games do not work on my Super Pro Fighter
+   Q(+). What can I do about that?</a><br>
+<b>A</b>: First read the answer to <a href="#12">question 12</a>.
+   Starting with versions after 1.9.8beta8 you can send (and dump) games with
+   the option -xfig. It could be that some games need to be in Super Wild Card
+   format in order for them to work correctly what SRAM concerns if you load
+   them from diskette. So, instead of converting them with -fig you have to use
+   the option -swc. For example Earthbound and "Lufia &amp; The Fortress of
+   Doom" have all sorts of problems when they are in FIG format, but not when
+   they are in SWC format. Other games work best when you convert them with
+   -fig. For example Seiken Densetsu 3 will run best when converted with -fig.<br>
+   There seems to be a bit of an overheating problem with some Super Pro
+   Fighter Q(+) models. After playing half an hour or so, this may result in
+   reboots or other strange behaviour. We have received a report of one user
+   who solved this by removing the case of the Super Pro Fighter Q(+) and
+   placing a CPU fan on top of the SPFQ's RAM chips.<br>
+<br>
+<p>
+<a name="38"><b>Q38</b>: uCON64 displays too many lines for my DOS-box. What
+   can I do about that?</a><br>
+<b>A</b>: You can do several things:<br>
+   1.) Change the number of lines that can be displayed at once with this
+   command:<br>
+   &nbsp;&nbsp; mode con lines=50<br>
+   2.) Use the program "more". See <a href="#28">question 28</a>.<br>
+   3.) Redirect the output of uCON64 to a file. For example:<br>
+   &nbsp;&nbsp; ucon64 rom.swc &#62; output.txt<br>
+   You can then open the file output.txt in an editor.<br>
+<br>
+<p>
+<a name="39"><b>Q39</b>: When will the next version of uCON64 be released? I
+   have heard the next version is able to crack my favourite SNES game, but I
+   do not know how to use CVS or a compiler.</a><br>
+<b>A</b>: We know when we will release a new version only just before we do so.
+   But starting with uCON64 1.9.8beta8 this is not a problem for you at all.
+   The only requirement is that you are able to read and use a text editor ;-)<br>
+   First visit the uCON64 homepage and from there follow a link to the
+   <a href="http://sourceforge.net/projects/ucon64" target="_blank">
+   uCON64 SourceForge page</a>. You should be able to find a page where you can
+   browse the CVS repository. Download the latest version of the file snes.c.
+   Open the file in an editor and search for the text snes_k. If you are
+   looking for NTSC or PAL fixes search either for snes_fix_pal_protection or
+   snes_fix_ntsc_protection, depending on what you need. For example, you will
+   have to search for snes_fix_pal_protection, if you want to fix a game so
+   that it will run on an NTSC SNES.<br>
+   Then read the comment to see what "code" your game requires. However, surely
+   not every game will be mentioned in the comments. In that case search for
+   the corresponding "line" a bit below. For example, for Mega Man X you will
+   find this text in the comments:<br>
+   &nbsp;&nbsp; af/bf XX 80 00 cf/df XX 80 40 f0<br>
+   =&#62; af/bf XX 80 00 cf/df XX 80 40 80<br>
+   Then you could search for the sequence \x80\x40\xf0. It is just a subset of
+   the search pattern with each element prefixed with "\x". Searching for
+   \xaf/\xbf will not bring you far. Searching for \xXX\x80 neither. It takes a
+   bit too much text to explain all the details, so just follow the directions.
+   Note that I did not advise to search for \x80\x40\x80, because that is what
+   the sequence should be after uCON64 has cracked or fixed the game.<br>
+   For Mega Man X you will find the lines:<br>
+   &nbsp;&nbsp; n += change_mem (buffer, bytesread, "!*\x80\x00!*\x80\x40\xf0", 9, '*', '!', "\x80", 1, 0,<br>
+   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+                                 "\xaf\xbf", 2, "\xcf\xdf", 2);<br>
+   Now open the file snescopy.txt. If you are trying to get uCON64 support new
+   NTSC or PAL cracks open snesntsc.txt or snespal.txt respectively. Then copy
+   the parts "!*\x80\x00!*\x80\x40\xf0", '*', '!', "\x80", 0, "\xaf\xbf" and
+   "\xcf\xdf" to a new line in that file. Note that 0 is the 9th parameter.
+   Also note that "\xaf\xbf" and "\xcf\xdf" are all parameters between double
+   quotes after the 9th parameter.<br>
+   Remove the double and single quotes and separate the parts by a colon. Then
+   replace the prefix \x with a space. If you followed the directions the line
+   will look like this:<br>
+   &nbsp;&nbsp; !* 80 00!* 80 40 f0: *: !:  80: 0:  af bf:  cf df<br>
+   You are almost done now. Only one thing has to be done. Look at what the
+   "wildcard" and "escape" symbols are. They are defined by the 5th and 6th
+   parameter of change_mem(), respectively. In our example the 5th parameter is
+   '*', so the wildcard character is: *. The 6th parameter is '!', so the
+   escape character is: !. And now the last step: replace the wildcard and
+   escape characters with a (hexadecimal) number that does not occur in the
+   rest of the character sequence (except other wildcards and escape symbols or
+   values). Be sure to have at least one space between the numbers. In our
+   example we could choose the values 1 and 2. In that case the finished line
+   would look like this:<br>
+   &nbsp;&nbsp; 2 1 80 00 2 1 80 40 f0: 1: 2:  80: 0:  af bf:  cf df<br>
+   If for example the 5th parameter would have been \x01, the wildcard would be
+   01. In that case you would not have to change it, because it is already a
+   number.<br>
+   Now save the changed file and place it either in the directory from where
+   you will run uCON64 (this is not necessarily the same directory as the one
+   where the uCON64 executable might be located!) or in uCON64's configuration
+   directory. Use the option -version to find out which directory that is. Then
+   try if uCON64 is able to crack your game. uCON64 will display a warning
+   message if it detects an error in your new line. If you made a mistake with
+   escape symbols uCON64 might very well crash. You can use the switch -v (in
+   combination with -k, -f or -l) to make uCON64 display what information it
+   reads from the file.<br>
+   If you cannot find which line you need you will either have to add a line to
+   snescopy.txt, snespal.txt or snesntsc.txt for every code that is not already
+   mentioned in those files or ask someone else to do it for you. For example
+   on an emulation web forum like
+   the <a href="http://www.cherryroms.com/" target="_blank">cherryroms</a>
+   <a href="http://forums.cherryroms.com/viewforum.php?f=2" target="_blank">
+   copier and hardware forum</a>.<br>
+<br>
+<p>
+<a name="40"><b>Q40</b>: What is the format of the snes*.txt files?</a><br>
+<b>A</b>: Zero or more <u>lines</u> of the following format:<br>
+   &#60;search&#62; : &#60;wc&#62; : &#60;esc&#62; : &#60;new&#62; : &#60;off&#62;
+   [: &#60;set&#62;]*<br>
+   Where <i>search</i> is the search pattern, <i>wc</i> the wildcard value,
+   <i>esc</i> the escape value, <i>new</i> the replacement byte sequence,
+   <i>off</i> the offset and <i>set</i> a set of bytes for each escape value in
+   the search pattern. uCON64 does not accept this information to be specified
+   across several lines. All values should be specified in hexadecimal, except
+   the offset.<br>
+   If the search pattern does not contain any escape bytes, you need not specify
+   a set as it will not be used. If it does contain escape bytes you will have
+   to specify a set for each one of them. Take for example the pattern:<br>
+   &nbsp;&nbsp; 01 00 01 00 42 84 ff 75 : ff : 00 : 02 03 : -6 : 22 11 : 19 75<br>
+   This will match amongst 1020 others with the following byte sequences:<br>
+   &nbsp;&nbsp; 01 22 01 19 42 84 19 75<br>
+   &nbsp;&nbsp; 01 11 01 19 42 84 91 75<br>
+   &nbsp;&nbsp; 01 22 01 75 42 84 00 75<br>
+   &nbsp;&nbsp; 01 11 01 75 42 84 ff 75<br>
+   A wildcard value in the search pattern matches with any value.<br>
+   Using the above search pattern, uCON64 will search and replace two bytes six
+   bytes "back" from the end of any matched byte sequence.
+   For example, any occurence of the byte sequence 01 22 01 19 42 84 19 75 will
+   be changed into 01 02 03 19 42 84 19 75.<br>
+<br>
+<p>
+<a name="41"><b>Q41</b>: Is it possible to force uCON64 to send or dump (an)
+   SRAM (file) instead of that it depends on whether the file exists?</a><br>
+<b>A</b>: No, but you could use a simple script or batch file to force sending
+   or dumping. Here is an example batch file to force dumping SRAM with a Pro
+   Fighter:<br>
+   &nbsp;&nbsp;@echo off<br>
+   &nbsp;&nbsp;if exist %1 goto error<br>
+   &nbsp;&nbsp;ucon64 -xfigs %1<br>
+   &nbsp;&nbsp;goto exit<br>
+   &nbsp;&nbsp;:error<br>
+   &nbsp;&nbsp;echo %1 already exists, use a different name<br>
+   &nbsp;&nbsp;:exit<br>
+   Save that text to a file that ends with ".bat" (without the quotes). Say you
+   save it to a file named dumpsram.bat. Then you can dump the SRAM of the Pro
+   Fighter with a command line like:<br>
+   &nbsp;&nbsp;dumpsram save1.sav<br>
+   You do not have to check if save1.sav exists anymore as the batch file will
+   do it for you.<br>
+   Here is the Bash script equivalent of the above batch file:<br>
+   &nbsp;&nbsp;#! /bin/bash<br>
+   &nbsp;&nbsp;if [ -e "$1" ]; then<br>
+   &nbsp;&nbsp;echo $1 already exists, use a different name<br>
+   &nbsp;&nbsp;exit<br>
+   &nbsp;&nbsp;fi<br>
+   &nbsp;&nbsp;ucon64 -xfigs "$1"<br>
+   You do not have to give the file a specific name. Just do not forget to make
+   the file executable. Say you name it dumpsram, then you could make it
+   executable with:<br>
+   &nbsp;&nbsp;chmod +x dumpsram<br>
+   Now you can dump the SRAM with:<br>
+   &nbsp;&nbsp;./dumpsram save1.sav<br>
+<br>
+<p>
+<a name="42"><b>Q42</b>: Why does uCON64 support DAT files?</a><br>
+<b>A</b>: uCON64 supports DAT files for two reasons:<br>
+   1.) In order to reliably identify ROM dumps.<br>
+   2.) In order to rename ROM dumps to their "official" name.<br>
+   Some ROM dump formats contain enough information to identify them, but none
+   of the formats supported by uCON64 can be <i>reliably</i> identified.<br>
+   The check sum algorithms that are used to calculate the internal check sum
+   of the files that have one is a simple addition of a range of bytes. So,
+   several bytes can be swapped and still yield the same check sum. Even the
+   range can be different and produce the same check sum. The latter case often
+   occurs with overdumps. Some ROM dump formats do not even use the entire file
+   for the check sum calculation...<br>
+   uCON64 solves these problems as it uses the CRC32 algorithm for file
+   identification. The CRC32 algorithm will produce a different check sum if
+   bytes are swapped or if the range is different. When uCON64 is run on a ROM
+   dump it first calculates the CRC32 of that file and then searches in its DAT
+   files for that CRC32. If the CRC32 is present uCON64 will display the
+   information it found in the DAT file. If uCON64 could not find the CRC32 it
+   will not display DAT information.<br>
+   At the risk of labouring the obvious I might add that even if all formats
+   used a check sum algorithm like CRC32, DAT files would still be necessary as
+   it is not possible to reliably identify a file without an external
+   reference.<br>
+<br>
+<p>
+<a name="43"><b>Q43</b>: How should the option --mkdat be used?</a><br>
+<b>A</b>: With care ;-)<br>
+   Seriously, let me first state that it would not be helpful if people would
+   start creating DAT files from their ROM collections and publish them without
+   some form of coordination. At the time of this writing (2 June 2003)
+   Cowering's GoodXXXX utilities are the standard for most consoles.<br>
+   In order to create a DAT file first make sure that all the ROMs you want to
+   create a DAT file from, have the name you want them to have. This is
+   important as DAT files are used not only for identification, but also for
+   renaming files (when using the option --rename). Also make sure all the
+   files have a suffix (or "extension"). Then start uCON64 with the --mkdat
+   option. For example, to create a DAT file from all the SNES ROM dumps in the
+   directory c:\snesrom use a command line like:<br>
+   &nbsp;&nbsp;ucon64 --mkdat=snes-02062003.dat c:\snesrom<br>
+   In this case a DAT file named snes-02062003.dat will be created. When you
+   copy this file to uCON64's DAT directory uCON64 will use it to identify SNES
+   ROMs. Note that the name ends with ".dat". uCON64 will only see DAT files if
+   they have a name that ends with ".dat". Note also that the name starts with
+   "snes". uCON64 will use all DAT files (in its DAT directory) that start with
+   "snes" to identify SNES ROM dumps. The letter case is not important, so you
+   could also use a name that starts with "SNES" or "Snes". If you use an
+   incorrect name uCON64 will not use the DAT file, but will print a warning
+   message each time you run uCON64 on a ROM dump. Here follows a table to see
+   which name uCON64 uses for what console:<br>
+<br>
+<table border="1">
+  <tr>
+    <th><tt>console</tt></th>
+    <th><tt>DAT file name prefix</tt></th>
+  </tr>
+  <tr>
+    <td><tt>"Atari hardware"</tt></td>
+    <td><tt>Good2600, Good5200, Good7800, 2600, 5200 or 7800</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Coleco</tt></td>
+    <td><tt>GoodCOL or Coleco</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Game Boy Advance</tt></td>
+    <td><tt>GoodGBA or GBA</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Game Boy (Color)</tt></td>
+    <td><tt>GoodGBX or GBX</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Genesis or Mega Drive</tt></td>
+    <td><tt>GoodGEN or GEN</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Intellivision</tt></td>
+    <td><tt>GoodINTV or Intelli</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Jaguar</tt></td>
+    <td><tt>GoodJAG or JAG</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Lynx</tt></td>
+    <td><tt>GoodLynx or Lynx</tt></td>
+  </tr>
+  <tr>
+    <td><tt>"M.A.M.E. hardware"</tt></td>
+    <td><tt>MAME</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Neo Geo</tt></td>
+    <td><tt>Neo-Geo</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Neo Geo Pocket</tt></td>
+    <td><tt>GoodNGPX or NGP</tt></td>
+  </tr>
+  <tr>
+    <td><tt>NES</tt></td>
+    <td><tt>GoodNES, NES or FDS</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Nintendo 64</tt></td>
+    <td><tt>GoodN64 or N64</tt></td>
+  </tr>
+  <tr>
+    <td><tt>PC-Engine</tt></td>
+    <td><tt>GoodPCE or PCE</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Sega Master System and Game Gear</tt></td>
+    <td><tt>GoodSMS, GoodGG, SMS or GG</tt></td>
+  </tr>
+  <tr>
+    <td><tt>SNES</tt></td>
+    <td><tt>GoodSNES or SNES</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Vectrex</tt></td>
+    <td><tt>GoodVECT or Vectrex</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Virtual Boy</tt></td>
+    <td><tt>GoodVBOY or VBOY</tt></td>
+  </tr>
+  <tr>
+    <td><tt>WonderSwan</tt></td>
+    <td><tt>GoodWSX or swan</tt></td>
+  </tr>
+<!--
+  <tr>
+    <td><tt>3DO</tt></td>
+    <td><tt>3do</tt></td>
+  </tr>
+  <tr>
+    <td><tt>CD32</tt></td>
+    <td><tt>CD32</tt></td>
+  </tr>
+  <tr>
+    <td><tt>CDi</tt></td>
+    <td><tt>CDi</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Dreamcast</tt></td>
+    <td><tt>Dreamcast</tt></td>
+  </tr>
+  <tr>
+    <td><tt>Saturn</tt></td>
+    <td><tt>Saturn</tt></td>
+  </tr>
+  <tr>
+    <td><tt>XBox</tt></td>
+    <td><tt>XBox</tt></td>
+  </tr>
+-->
+</table>
+<br>
+   You can create a DAT file from a subset of the files in a directory by
+   explicitly specifying the files on the command line:<br>
+   &nbsp;&nbsp;ucon64 --mkdat=snes-02062003.dat c:\snesrom\*.swc<br>
+   uCON64 uses the first file to determine for which console a DAT file is
+   meant. So, if you want to create a DAT file from a set of ROMs of several
+   consoles, the first file uCON64 encounters will determine the DAT file type.
+   You can do two things in such a case to be sure for which console the
+   resulting DAT file will be meant:<br>
+   1.) Specify one extra ROM dump as first file on the command line. For
+   example:<br>
+   &nbsp;&nbsp;ucon64 --mkdat=n64-02062003.dat c:\rom\mario64.v64 c:\rom<br>
+   2.) Use a force recognition switch. For example:<br>
+   &nbsp;&nbsp;ucon64 --mkdat=sms-02062003.dat c:\rom --sms<br>
+<br>
+   The second option should only be used when you are sure all the files are
+   for a certain console and uCON64 fails to recognise some.<br>
+   You can control what information uCON64 displays when creating a DAT file
+   with the switches -v and -q. --mkdat uses three levels of verbosity:<br>
+   1.) When the switch -v is specified uCON64 will display a warning if it
+   could not determine the console type for a file. uCON64 will also display a
+   warning if it encounters a file that has the same CRC32 as a file uCON64
+   already processed (a "duplicate").<br>
+   2.) When neither -v nor -q is specified uCON64 will display a warning only
+   if it finds a duplicate or if the console type of the first file could not
+   be determined.<br>
+   3.) When -q is specified uCON64 will display a warning only if the console
+   type of the first file could not be determined.<br>
+<br>
+   If you had to specify a first file you could suppress the warning about a
+   duplicate by using the switch -q. For example:<br>
+   &nbsp;&nbsp;ucon64 --mkdat=n64-02062003.dat c:\rom\mario64.v64 c:\rom -q<br>
+<br>
+<p>
+<a name="44"><b>Q44</b>: What tools do you recommend besides uCON64?</a><br>
+<b>A</b>: For SNES, only
+   <a href="http://nsrt.edgeemu.com" target="_blank">NSRT</a> (Nach's SNES ROM
+   Tools). That is, starting from version 3.0 Release Candidate 1. NSRT has some
+   features that uCON64 does not have. The two most interesting features are
+   arguably that it is able to fix some hacked ROM dumps and that it has an
+   internal database which is spend a lot of effort on to have it contain only
+   correct entries (as opposed to GoodSNES and the GoodSNES DAT files). What the
+   second feature is concerned let us remind you that the quality of uCON64's
+   ROM database support stands or falls with the quality of the DAT files. So,
+   if you would create a DAT file of a ROM dump collection processed with NSRT
+   uCON64 would get a SNES database that is similar to the one in NSRT. See
+   <a href="#43">question 43</a>. Another advantage NSRT has over uCON64 (as
+   seen from a SNES user's viewpoint) is that it is written only for SNES. NSRT
+   also handles some strange formats that uCON64 does not handle.<br>
+<br>
+<p>
+<a name="45"><b>Q45</b>: What is an interleaved ROM?</a><br>
+<b>A</b>: Strictly speaking, interleaved ROMs do not exist. Interleaved ROM
+   dumps do. Or perhaps it is better to say ROM dumps in interleaved format.
+   The meaning of "interleaved" depends on the console type. Interleaved
+   formats exist for NES, Sega Master System, Game Gear, PC-Engine, Genesis,
+   SNES and Nintendo 64.<br>
+   Deinterleaving is the opposite of interleaving a ROM dump. It is the process
+   of converting the data to "binary" format. Binary in the sense as how the
+   data (in a cartridge) is presented to the CPU.<br>
+   ROM dumps can be interleaved (not for NES) and deinterleaved with uCON64.<br>
+<br>
+   NES<br>
+   NES dumps can contain two types of data, "PRG data" and "CHR data".
+   Interleaved NES dumps have the PRG data stored at the even offsets in the
+   file and the CHR data at the odd offsets. Non-interleaved dumps first
+   contain all the PRG data followed by the CHR data (if present).<br>
+   Cannot be interleaved with uCON64.<br>
+   Can be deinterleaved with uCON64 using the option -dint.<br>
+<br>
+   Sega Master System, Game Gear and Genesis<br>
+   Interleaved Sega Master System, Game Gear and Genesis dumps are produced by
+   the Super Magic Drive and the Multi Game Doctor 2.<br>
+   For the SMD: for each block of 16384 bytes of the ROM all bytes at even
+   offsets are stored in the upper half of the dumped block. The bytes at odd
+   offsets are stored in the lower half. So, each dumped block first contains
+   8192 "odd bytes" then 8192 "even bytes".<br>
+   For the MGD2: all odd bytes of the file are stored in the first half of the
+   file, all even bytes in the second half.<br>
+   Can be interleaved with uCON64 using the option -smd or -mgd.<br>
+   Can be deinterleaved with uCON64 using the option -bin.<br>
+<br>
+   PC-Engine<br>
+   Interleaved PC-Engine or TurboGrafx-16 dumps are not produced by a specific
+   copier. It depends on the HuCARD (PC-Engine/TurboGrafx-16 game cartridge).
+   American HuCARDs produce an interleaved dump, because the 8 data pins are
+   reversed compared to Japanese HuCARDs. Perhaps "bit-swapped" is a better
+   word to describe the format, but using the word interleaved prevents
+   confusion (as bit-swapped implies a non bit-swapped reference).<br>
+   Can be interleaved with uCON64 using the option -mgd, making the game
+   suitable for a TurboGrafx-16. You can use the option -swap afterwards if you
+   want to play the game on a PC-Engine.<br>
+   Can be deinterleaved with uCON64 using the option -msg.<br>
+<br>
+   SNES<br>
+   Interleaved SNES dumps are produced by the Game Doctor and the Super UFO
+   when dumping a HiROM cartridge. The simplest interleaved format first
+   contains all upper halfs of each 64 kB block of the ROM (HiROM "banks"),
+   then all lower halfs. There are several interleaved formats, but except for
+   one they all share the principle of dividing each 64 kB block in two.<br>
+   SNES ROMs contain a block of 48 bytes, some call it the internal SNES
+   header, that contains information about the ROM. Information like game name,
+   check sum, size and the bank type. There are two bank types, HiROM and LoROM
+   (as far as we know these are terms not used by Nintendo). HiROM ROMs have
+   banks of 64 kB, LoROM ROMs have banks of 32 kB. The first bank contains the
+   internal SNES header at a fixed offset relative to the end of the bank.
+   However, because the bank sizes differ for HiROM and LoROM the absolute
+   locations differ for the two in non-interleaved dumps. If dumps are
+   interleaved the absolute locations are te same.<br>
+   The interleaved format was probably introduced so that a copier <i>can</i>
+   (the Game Doctor does only if the dump has no header) always check the same
+   location in a dump to tell whether it should handle it as a LoROM or a HiROM
+   dump.<br>
+   Can be interleaved with uCON64 using the option -gd3 or -ufo.<br>
+   Can be deinterleaved with uCON64 using the option -dint, but it is
+   better to use one of the regular conversion options -smc, -swc, -fig or
+   -mgd. Some SNES tools erroneously interleave LoROM dumps. These dumps
+   can be deinterleaved with the options -gd3 and -ufo as well as with the
+   aformentioned (SNES) options.<br>
+<br>
+   Nintendo 64<br>
+   Interleaved Nintendo 64 dumps are produced by the Doctor V64 and the Doctor
+   V64 Junior. Perhaps "byte-swapped" is a better term for Nintendo 64 dumps.
+   For each two bytes the first and the second byte are swapped.<br>
+   Can be interleaved with uCON64 using the option -v64.<br>
+   Can be deinterleaved with uCON64 using the option -dint, but it is better to
+   use the regular conversion option -z64.<br>
+<br>
+<p>
+<a name="46"><b>Q46</b>: The pre-compiled GNU/Linux binary does not work on my
+   system, while a binary compiled by me works fine. How can that be?</a><br>
+<b>A</b>: If you get this output on the command line:<br>
+   &nbsp;&nbsp;libgcc_s.so.1: cannot open shared object file: No such file or directory<br>
+   Or when the command "ldd discmage.so" gives output that looks like this:<br>
+   &nbsp;&nbsp;libz.so.1 => /usr/lib/libz.so.1 (0x4002b000)<br>
+   &nbsp;&nbsp;libgcc_s.so.1 => not found<br>
+   &nbsp;&nbsp;libc.so.6 => /lib/libc.so.6 (0x40039000)<br>
+   &nbsp;&nbsp;/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x80000000)<br>
+   (note the text "not found") then you are experiencing a binary
+   incompatibility problem with the add-on discmage library. You can either
+   remove the discmage library from the directory where uCON64 looks for it
+   (see <a href="#4">question 4</a>) or compile the library yourself (see
+   <a href="#3">question 3</a>).<br>
+<br>
+<p>
+<a name="47"><b>Q47</b>: I use Windows XP (NT/2000) and every time I run uCON64
+   I get this error message about ntuser.dat. What does it mean?</a><br>
+<b>A</b>: If you see lines in the display output of uCON64 that look like
+   these:<br>
+   &nbsp;&nbsp;Create: ntuser.idx<br>
+   &nbsp;&nbsp;ERROR: Can't open "C:\Documents and Settings\Daniël\ntuser.dat" for reading<br>
+   you should modify the configuration file so that ucon64_datdir points to
+   another directory.<br>
+   By default uCON64 will use the home directory as the directory where it
+   searches for DAT files (the "DAT file directory"). uCON64 handles all files
+   in the DAT file directory that have a name that ends with ".dat" as DAT
+   files. ntuser.dat seems to be a standard file in a Windows XP user's home
+   directory (and should not be removed), but it is not a DAT file for use with
+   uCON64. See <a href="#36">question 36</a> for more information on how to
+   configure uCON64 so that it uses "real" DAT files. If you only want to get
+   rid of that error message either make ucon64_datdir in the configuration
+   file point to an existing directory that does not contain any files with a
+   name that ends with ".dat" or make an environment variable with the name
+   ucon64_datdir point to such a directory. Do not forget that the environment
+   variable will not be set the next time you start a DOS session.<br>
+<br>
+<p>
+<a name="48"><b>Q48</b>: Is there any way to make uCON64 convert a ROM dump to
+   Game Doctor SF3/SF6/SF7 format and split it, in one command?</a><br>
+<b>A</b>: Not directly, but it can be done with the help of a shell script or
+   batch file. See <a href="#41">question 41</a> for some information on how to
+   make a shell script or batch file run. Here is a Bash script:<br>
+   &nbsp;&nbsp;#! /bin/bash<br>
+<br>
+   &nbsp;&nbsp;usage ()<br>
+   &nbsp;&nbsp;{<br>
+   &nbsp;&nbsp;  echo "Usage: $0 file_to_convert_and_split"<br>
+   &nbsp;&nbsp;  exit<br>
+   &nbsp;&nbsp;}<br>
+<br>
+   &nbsp;&nbsp;GD3DIR="convert"<br>
+   &nbsp;&nbsp;SPLITDIR="split"<br>
+<br>
+   &nbsp;&nbsp;if [ ! -n "$1" ]; then usage; fi<br>
+   &nbsp;&nbsp;if [ ! -e "$1" ]; then usage; fi<br>
+<br>
+   &nbsp;&nbsp;if [ ! -e "$GD3DIR" ]; then mkdir "$GD3DIR"; fi<br>
+   &nbsp;&nbsp;if [ ! -e "$SPLITDIR" ]; then mkdir "$SPLITDIR"; fi<br>
+<br>
+   &nbsp;&nbsp;rm -f "$GD3DIR"/*<br>
+   &nbsp;&nbsp;rm -f "$SPLITDIR"/*<br>
+<br>
+   &nbsp;&nbsp;ucon64 -q -gd3 "$1" -o "$GD3DIR"<br>
+   &nbsp;&nbsp;ucon64 -q -s "$GD3DIR"/* -o "$SPLITDIR"<br>
+   <br>
+   And here is a batch file:<br>
+   &nbsp;&nbsp;@echo off<br>
+<br>
+   &nbsp;&nbsp;set GD3DIR="convert"<br>
+   &nbsp;&nbsp;set SPLITDIR="split"<br>
+<br>
+   &nbsp;&nbsp;if .==.%1 goto usage<br>
+   &nbsp;&nbsp;if not exist %1 goto usage<br>
+<br>
+   &nbsp;&nbsp;if not exist %GD3DIR% mkdir %GD3DIR%<br>
+   &nbsp;&nbsp;if not exist %SPLITDIR% mkdir %SPLITDIR%<br>
+<br>
+   &nbsp;&nbsp;del /q %GD3DIR%\*<br>
+   &nbsp;&nbsp;del /q %SPLITDIR%\*<br>
+<br>
+   &nbsp;&nbsp;ucon64 -q -gd3 %1 -o %GD3DIR%<br>
+   &nbsp;&nbsp;ucon64 -q -s %GD3DIR%\* -o %SPLITDIR%<br>
+   &nbsp;&nbsp;goto exit<br>
+<br>
+   &nbsp;&nbsp;:usage<br>
+   &nbsp;&nbsp;echo Usage: %0 file_to_convert_and_split<br>
+<br>
+   &nbsp;&nbsp;:exit<br>
+<br>
+   Say you saved the batch file to the name cs.bat, then you can convert a
+   file to Game Doctor format and split it with:<br>
+   &nbsp;&nbsp;cs somegame.swc<br>
+   Afterwards you can find a file in Game Doctor format in the directory
+   convert and the split parts in the directory split. Modify the scripts above
+   if you want them to make uCON64 write its output to other directories. Do
+   <i>not</i> make GD3DIR or SPLITDIR point to the current directory or to your
+   SNES ROM directory as the scripts will remove all files in the directories
+   pointed to by GD3DIR or SPLITDIR. Starting with empty directories is the
+   main reason why the scripts work (actually, this is only true for GD3DIR).<br>
+<br>
+<p>
+<a name="49"><b>Q49</b>: How do I use the command line?</a><br>
+<b>A</b>: There are some commands that are very common when using uCON64. I
+   will only explain how to use those. Search the internet if you want to know
+   more.<br>
+   First read the section <a href="#cmdline">"The command line
+   environment"</a>.
+   The first principle you should understand is how the space on your
+   harddisk(s) is organised. Before an operating system is able to use the
+   harddisk it needs to have a <i>file system</i>. A harddisk can contain
+   several file systems. When you are using DOS or Windows each file system is
+   called a "<i>drive</i>" and gets assigned a drive letter. A file system can
+   store files and <i>directories</i>. Directories can be seen as special files
+   that themselves "contain" files and directories. By using directories it is
+   possible to quickly oversee the structure of a file system and keep it
+   organised. Under GNU/Linux file systems are visible in the directory
+   structure (e.g. /mnt/windows). With Windows XP it is possible to make
+   file systems visible/accessible in the same manner.<br>
+   When using the command line there is something called the <i>current
+   directory</i>.
+   The current directory is like an implicit directory. Several commands and
+   programs use the current directory when a directory is not explicitly
+   specified. For example, the command <b>dir</b> (Unix: <b>ls</b>) lists the
+   contents of a directory. When you do not specify a directory it will list
+   the contents of the current directory. You can change the current directory
+   with the command <b>cd</b>. For example, to change the current directory to
+   "roms" on drive C: type:<br>
+   &nbsp;&nbsp;cd c:\roms<br>
+   On DOS and Windows there is also a <i>current drive</i>. You can change the
+   current drive by specifying the drive letter followed by a colon. For
+   example to change the current drive to D: type:<br>
+   &nbsp;&nbsp;d:<br>
+   Under DOS and Windows the current directory is a bit different than it is on
+   Unix: each drive has its own current directory. It is possible to change the
+   current directory of a drive while the current directory of the current
+   drive is not changed. The first example with <b>cd</b> does just that when
+   C: is not the current drive. When C: was the current drive you would not
+   have to specify the drive letter as the command line interpreter would
+   assume you intended to change the current directory of C:. When a text
+   refers to the current directory under DOS and Windows the author usually
+   means the combination of current drive and current directory. In other
+   words, when directed to change the current directory to c:\ucon64 you
+   actually have to change the current drive to C: and the current directory on
+   C: to ucon64.<br>
+   Let us assume you have the following directory structure:<br>
+   &nbsp;&nbsp;C:<br>
+   &nbsp;&nbsp;+--roms<br>
+   &nbsp;&nbsp;|&nbsp;+--snes<br>
+   &nbsp;&nbsp;|&nbsp;|&nbsp;+--gd3<br>
+   &nbsp;&nbsp;|&nbsp;+--n64<br>
+   &nbsp;&nbsp;+--tmp<br>
+   &nbsp;&nbsp;D:<br>
+   &nbsp;&nbsp;+--roms<br>
+   &nbsp;&nbsp;&nbsp;&nbsp;+--sms<br>
+<br>
+   To change the current directory (of drive C:) to c:\roms\snes\gd3 type:<br>
+   &nbsp;&nbsp;cd c:\roms\snes\gd3<br>
+   You can see that each directory is separated from its parent by a backslash
+   ('\'). Under Unix (or Bash under Windows) you would refer to the same
+   directory by replacing the backslashes with forward slashes and perhaps
+   replacing "c:" with the appropriate mount point (e.g.,
+   /mnt/windows/roms/snes/gd3). Note that "c:" is a valid directory name on a
+   GNU/Linux file system.<br>
+   You can refer to the other directories (and files) in a relative manner. Say
+   c:\roms\snes contains the file
+   Secret&nbsp;of&nbsp;Mana&nbsp;(U).swc. You can make uCON64 display
+   information about that file with:<br>
+   &nbsp;&nbsp;ucon64 "c:\roms\snes\Secret&nbsp;of&nbsp;Mana&nbsp;(U).swc"<br>
+   but also with:<br>
+   &nbsp;&nbsp;ucon64 "..\Secret&nbsp;of&nbsp;Mana&nbsp;(U).swc"<br>
+   The quotes are necessary or else uCON64 would handle Secret, of, Mana and
+   (U).swc as separate files. This is usual behaviour for command line
+   programs, it is not specific to uCON64.<br>
+   ".." is a special directory. Each directory contains two special
+   directories, "." and "..". "." refers to the current directory, ".." to the
+   directory one level higher in the directory structure. For example, to see
+   the contents of the directory "roms" you could type:<br>
+   &nbsp;&nbsp;dir c:\roms<br>
+   but also:<br>
+   &nbsp;&nbsp;dir ..\..<br>
+   Or on Unix:<br>
+   &nbsp;&nbsp;ls ../..<br>
+   c:\roms\snes is called a <i>path</i>. To be more specific, an <i>absolute
+   path</i>. It is the path to follow in the directory structure to get to the
+   file Secret&nbsp;of&nbsp;Mana&nbsp;(U).swc. "..\.." is a <i>relative
+   path</i>. In c:\roms\snes "..\.." refers to another directory than in
+   c:\roms\snes\gd3 (c:\ and c:\roms respectively). To change the current
+   directory to c:\roms\snes type:<br>
+   &nbsp;&nbsp;cd ..<br>
+   Say you want to convert Secret&nbsp;of&nbsp;Mana&nbsp;(U).swc to
+   Game Doctor format, place the converted file in c:\roms\snes\gd3, split that
+   file in pieces, place those pieces in c:\tmp and transfer the pieces to a
+   Game Doctor SF7. You could type the following commands:<br>
+   &nbsp;&nbsp;ucon64 -gd3 "Secret&nbsp;of&nbsp;Mana&nbsp;(U).swc" -o .\gd3<br>
+   &nbsp;&nbsp;ucon64 -s gd3\sf16Sec -o c:\tmp<br>
+   &nbsp;&nbsp;ucon64 -xgd3 ..\..\tmp\SF16SECA.078<br>
+<br>
+   Besides <b>dir</b> (Unix: <b>ls</b>) and <b>cd</b> there are some other
+   commands that you may find useful:<br>
+   <b>del</b> (Unix: <b>rm</b>) to delete or remove a file<br>
+   <b>xcopy</b> (Unix: <b>cp</b>) to copy a file<br>
+   <b>md</b> (Unix (and Windows): <b>mkdir</b>) to create a directory<br>
+   <b>rd</b> (Unix (and Windows): <b>rmdir</b>) to remove a directory<br>
+   <b>set</b> (Unix (Bash): <b>export</b>) to set (and export) an environment
+   variable<br>
+   <b>rmdir</b> cannot remove a directory if there are still files (or
+   directories) in it. Under DOS and Windows you can get more information about
+   these commands by using the option /?. Under Unix the equivalent is --help.
+   For example, if you want to get more information about <b>del</b> you could
+   type:<br>
+   &nbsp;&nbsp;del /?<br>
+<br>
+   Here follows a final example that uses the commands mentioned above:<br>
+   &nbsp;&nbsp;cd \<br>
+   &nbsp;&nbsp;md test<br>
+   &nbsp;&nbsp;cd test<br>
+   &nbsp;&nbsp;xcopy "c:\roms\snes\Secret&nbsp;of&nbsp;Mana&nbsp;(U).swc" mana.swc<br>
+   &nbsp;&nbsp;set parport=378<br>
+   &nbsp;&nbsp;ucon64 -xswc mana.swc<br>
+   &nbsp;&nbsp;del mana.swc<br>
+   &nbsp;&nbsp;cd ..<br>
+   &nbsp;&nbsp;rd test<br>
+<br>
+   First the current directory is changed to c:\. Then the directory "test" is
+   created. Then the current directory is changed to "test". Then the file
+   c:\roms\snes\Secret&nbsp;of&nbsp;Mana&nbsp;(U).swc is copied to the file
+   mana.swc. Then an environment variable with the name parport is set to the
+   value 378. Then uCON64 is used to transfer mana.swc to a Super Wild Card
+   while using parallel port address 0x378. Then the file mana.swc is removed.
+   Finally the directory "test" is removed.<br>
+<br>
+<p>
+<a name="50"><b>Q50</b>: I configured uCON64 to use ppdev and tried to send a
+   file to my backup unit as a regular user. I got an error message that the
+   parallel port device could not be opened. What did I do wrong?</a><br>
+<b>A</b>: There are two possibilities:<br>
+   1.) You specified an incorrect parallel port device in the configuration
+   file.<br>
+   In most cases the correct device is /dev/parport0. Only change the device
+   name if you are sure /dev/parport0 is not the device associated with the
+   parallel port your backup unit is connected to. Try for example /dev/parport1
+   or /dev/parport2.<br>
+   2.) You do not have the required privileges.<br>
+   It is true that regular users can transfer files to and from their backup
+   units (without the executable being setuid root), but that does not mean
+   <i>any</i> user can do that. It is quite common that only users in the group
+   "lp" have access to /dev/parport&#60;n&#62;. So, you should add yourself to
+   that group. Say your login name is helanren. Root could use the following
+   command to give you the privilege to use /dev/parport0:<br>
+   &nbsp;&nbsp;usermod -G `echo \`id -Gn helanren\` | sed "s/ /,/g"`,lp helanren<br>
+   Explanation:<br>
+   <b>usermod -G &#60;groups&#62; helanren</b> is used to make helanren a member
+   of those groups.<br>
+   <b>id -Gn helanren</b> lists the groups helanren is currently a member of,
+   separated by spaces.<br>
+   <b>sed "s/ /,/g"</b> replaces all spaces with commas.<br>
+<br>
+<p>
+<a name="51"><b>Q51</b>: What do I need to do before I can upload files to my
+   Flash 2 Advance?</a><br>
+<b>A</b>: Download the package that contains the support files from
+   <a href="http://ucon64.sourceforge.net/" target="_blank">the uCON64 homepage</a>.
+   Extract the package and update the configuration file. See
+   <a href="#configbin">"How to configure the uCON64 executable"</a>.
+   If you want to use the parallel port version of the F2A, you should set at
+   least the variable iclientp. If you want to send multiple files to your F2A,
+   also set gbaloader. If you want to see a different background while
+   uploading, set ilogo.<br>
+   The USB version of the F2A is currently only supported under GNU/Linux. If
+   you want to use the USB version, you should set at least the variables
+   f2afirmware and iclientu. If you want to send multiple files to your F2A, set
+   gbaloader. Additional requirements are:<br>
+   - <a href="http://libusb.sourceforge.net/" target="_blank">libusb</a> (included in recent
+   GNU/Linux distributions)<br>
+   - usbdevfs has to be mounted (most (?) distributions have usbdevfs mounted by
+   default, but you can do it manually with "mount -t usbdevfs none /proc/bus/usb")<br>
+   - if you use Linux 2.4 or older then the EZUSB2131 firmware upload driver has
+   to be loaded, see <a href="http://ezusb2131.sourceforge.net/" target="_blank">http://ezusb2131.sourceforge.net/</a><br>
+   - if you use Linux 2.5 or later fxload should be present in /sbin<br>
+   - you need to have read/write access to /proc/ezusb and /proc/bus/usb, so you
+   have to run uCON64 as root (or setuid root) for the USB version of the F2A<br>
+<br>
+   By default, uCON64 tries to access a parallel port to communicate with an
+   F2A. In order to make uCON64 communicate with an F2A connected to a USB port,
+   use the switch -port. For example:<br>
+   &nbsp;&nbsp;ucon64 -xf2a "Wario Ware Inc.zip" -port=usb<br>
+   uCON64 detects to which USB port the F2A is connected, so you do not have to
+   specify a specific port.<br>
+<br>
+<p>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+</tt></body></html>

+ 210 - 0
tools/ucon64-2.0.0-src/hardware.html

@@ -0,0 +1,210 @@
+<html><head>
+<link rel="icon" href="images/icon.png" type="image/png">
+<title>uCON64 - Hardware</title></head><body bgcolor="#ffffff"><tt>
+The following backup units are currently supported by <img src="images/logo.png" height="50"><br>
+<br>
+If your backup unit is NOT supported yet, you might find what you need at <a href="http://mfm.subport.org" target="_blank">http://mfm.subport.org</a><br>
+For more information about backup units check <a href="http://www.robwebb.clara.co.uk/backup/" target="_blank">http://www.robwebb.clara.co.uk/backup/</a><br>
+
+<tt><br>
+<br>
+<center>
+<table width="640">
+  <tr>
+    <td><tt>
+<img src="images/swc.png"><br>
+Super Wild Card 1.6XC/Super Wild Card 2.8CC/Super Wild Card DX(2)/SWC<br>
+19XX Front Far East/FFE <a href="http://www.front.com.tw">http://www.front.com.tw</a><br>
+(uCON64 options: --xswc, --xswc2, --xswcs, --xswcr, --xswcc, -xswc-io)<br>
+<br>
+<br>
+<br>
+<img src="images/fig.png"><br>
+Super Pro Fighter (Q/Q+)/Pro Fighter X (Turbo 2)/Double Pro Fighter (X Turbo)<br>
+19XX China Coach Limited/CCL <a href="http://www.ccltw.com.tw">http://www.ccltw.com.tw</a><br>
+(uCON64 options: --xfig, --xfigs, --xfigc)<br>
+<br>
+<br>
+<br>
+<img src="images/sf3.png"><br>
+Game Doctor SF3(SF6/SF7)/Professor SF(SF II)<br>
+19XX Bung Enterprises Ltd <a href="http://www.bung.com.hk">http://www.bung.com.hk</a><br>
+(uCON64 options: --xgd3, --xgd6, --xgd3s, --xgd6s, --xgd3r, --xgd6r)<br>
+<br>
+<br>
+<br>
+<img src="images/smd.png"><br>
+Super Com Pro (HK)/Super Magic Drive/SMD<br>
+19XX Front Far East/FFE <a href="http://www.front.com.tw">http://www.front.com.tw</a><br>
+(uCON64 options: --xsmd, --xsmds)<br>
+<br>
+<br>
+<br>
+<img src="images/smc.png"><br>
+Super Magic Card<br>
+19XX Front Far East/FFE <a href="http://www.front.com.tw">http://www.front.com.tw</a><br>
+(uCON64 options: --xsmc, --xsmcr)<br>
+<br>
+<br>
+<br>
+<img src="images/msg.png"><br>
+Magic Super Griffin/MSG<br>
+19XX Front Far East/FFE <a href="http://www.front.com.tw">http://www.front.com.tw</a><br>
+(uCON64 option: --xmsg)<br>
+<br>
+<br>
+<br>
+<img src="images/v64.png"><br>
+Doctor V64<br>
+19XX Bung Enterprises Ltd <a href="http://www.bung.com.hk">http://www.bung.com.hk</a><br>
+(uCON64 option: --xv64)<br>
+<br>
+<br>
+<br>
+<img src="images/v64jr.png"><br>
+Doctor V64 Junior<br>
+19XX Bung Enterprises Ltd <a href="http://www.bung.com.hk">http://www.bung.com.hk</a><br>
+(uCON64 option: --xdjr)<br>
+<br>
+<br>
+<br>
+<img src="images/dexdrive.png"><br>
+DexDrive (PSX and Nintendo 64 SRAMs)<br>
+19XX InterAct <a href="http://www.dexdrive.de">http://www.dexdrive.de</a><br>
+(uCON64 option: --xdex)<br>
+<br>
+<br>
+<br>
+    </td>
+    <td><tt>
+<img src="images/gbx.png"><br>
+Game Boy Xchanger<br>
+19XX Bung Enterprises Ltd <a href="http://www.bung.com.hk">http://www.bung.com.hk</a><br>
+(uCON64 options: --xgbx, --xgbxs, --xgbxb, --xgbxm)<br>
+<br>
+<br>
+<br>
+<img src="images/cameralink.png"><br>
+Mad Catz Camera Link (Game Boy Camera)<br>
+XXXX Mad Catz Inc. <a href="http://www.madcatz.com">http://www.madcatz.com</a><br>
+(uCON64 option: --xmccl)<br>
+<br>
+<br>
+<br>
+<img src="images/fal.png"><br>
+Flash Advance Linker<br>
+2001 Visoly <a href="http://www.visoly.com">http://www.visoly.com</a><br>
+(uCON64 options: --xfal, --xfalmulti, --xfalc, --xfals, --xfalb, --xfalm)<br>
+<br>
+<br>
+<br>
+<img src="images/f2a.png"><br>
+Flash 2 Advance (Ultra)<br>
+2003 Flash2Advance <a href="http://www.flash2advance.com">http://www.flash2advance.com</a><br>
+(uCON64 options: --xf2a, --xf2amulti, --xf2ac, --xf2as, --xf2ab)<br>
+<br>
+<br>
+<br>
+Lynxit (Lynx cartridge backup board)<br>
+1997 K. Wilkins (custom selfmade)<br>
+(uCON64 option: --xlit)<br>
+<br>
+<br>
+<br>
+<img src="images/cd_rw.png" height="50"><br>
+(uCON64 options: --mksheet, --cdirip, and --nrgrip)<br>
+<br>
+<br>
+<br>
+<img src="images/mdpro.png"><br>
+MD-PRO flash card programmer<br>
+200X ToToTEK <a href="http://www.tototek.com">http://www.tototek.com</a><br>
+(uCON64 options: --xmd, --xmds)<br>
+<br>
+<br>
+<br>
+<img src="images/pcepro.png"><br>
+PCE-PRO flash card programmer<br>
+200X ToToTEK <a href="http://www.tototek.com">http://www.tototek.com</a><br>
+(uCON64 option: --xpce)<br>
+<br>
+<br>
+<br>
+<img src="images/smspro.png"><br>
+SMS-PRO flash card programmer<br>
+200X ToToTEK <a href="http://www.tototek.com">http://www.tototek.com</a><br>
+(uCON64 options: --xgg, --xggs)<br><!-- yes, the same as for the GG-PRO -->
+<br>
+<br>
+<br>
+<img src="images/ggpro.png"><br>
+GG-PRO flash card programmer<br>
+200X ToToTEK <a href="http://www.tototek.com">http://www.tototek.com</a><br>
+(uCON64 options: --xgg, --xggs)<br>
+<br>
+<br>
+<br>
+<!--img src="images/"><br-->
+Cyan's Megadrive Copier<br>
+1999-2004 Cyan Helkaraxe<br>
+(uCON64 options: --xcmc, --xcmct, --xcmcm)<br>
+<br>
+<br>
+<br>
+<!--img src="images/"><br-->
+Mike Pavone's Genesis/Sega CD transfer cable<br>
+200X Mike Pavone<br> 
+(uCON64 options: --xmcd)<br>
+<br>
+<br>
+<br>
+    </td>
+  </tr>
+</table>
+</center>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+<br>
+</tt></body></html>

BIN
tools/ucon64-2.0.0-src/images/bg.jpg


BIN
tools/ucon64-2.0.0-src/images/cameralink.png


BIN
tools/ucon64-2.0.0-src/images/cd_rw.png


BIN
tools/ucon64-2.0.0-src/images/dexdrive.png


BIN
tools/ucon64-2.0.0-src/images/f2a.png


BIN
tools/ucon64-2.0.0-src/images/fal.png


BIN
tools/ucon64-2.0.0-src/images/fig.png


BIN
tools/ucon64-2.0.0-src/images/gbx.png


BIN
tools/ucon64-2.0.0-src/images/ggpro.png


BIN
tools/ucon64-2.0.0-src/images/hr.png


BIN
tools/ucon64-2.0.0-src/images/icon.png


BIN
tools/ucon64-2.0.0-src/images/logo.png


BIN
tools/ucon64-2.0.0-src/images/mdpro.png


BIN
tools/ucon64-2.0.0-src/images/msg.png


BIN
tools/ucon64-2.0.0-src/images/pcepro.png


BIN
tools/ucon64-2.0.0-src/images/sf3.png


BIN
tools/ucon64-2.0.0-src/images/smc.png


BIN
tools/ucon64-2.0.0-src/images/smd.png


BIN
tools/ucon64-2.0.0-src/images/smspro.png


BIN
tools/ucon64-2.0.0-src/images/swc.png


BIN
tools/ucon64-2.0.0-src/images/v64.png


BIN
tools/ucon64-2.0.0-src/images/v64jr.png


+ 5 - 0
tools/ucon64-2.0.0-src/install.html

@@ -0,0 +1,5 @@
+<html><head>
+<link rel="icon" href="images/icon.png" type="image/png">
+<title>uCON64 - Install</title></head><body bgcolor="#ffffff"><tt>
+What you expect here can be found in <a href="faq.html">faq.html</a>
+</tt></body></html>

+ 372 - 0
tools/ucon64-2.0.0-src/libieee1284.diff

@@ -0,0 +1,372 @@
+--- parallel.c	2004-09-02 19:09:23.000000000 +0200
++++ parallel.c.libieee1284	2004-09-04 03:07:38.000000000 +0200
+@@ -25,6 +25,11 @@
+ #include "config.h"
+ #endif
+ 
++#define USE_LIBIEEE1284
++#if     defined USE_LIBIEEE1284 && defined USE_PPDEV
++#undef  USE_PPDEV
++#endif
++
+ #ifdef  USE_PARALLEL
+ 
+ #include <stdio.h>
+@@ -40,7 +45,9 @@
+ #include <sys/time.h>
+ #include <linux/ppdev.h>
+ #include <linux/parport.h>
+-#elif   defined __linux__ && defined __GLIBC__  // USE_PPDEV
++#elif   defined USE_LIBIEEE1284                 // USE_PPDEV
++#include <ieee1284.h>
++#elif   defined __linux__ && defined __GLIBC__  // USE_LIBIEEE1284
+ #ifdef  HAVE_SYS_IO_H                           // necessary for some Linux/PPC configs
+ #include <sys/io.h>                             // ioperm() (glibc), in{b, w}(), out{b, w}()
+ #else
+@@ -78,8 +85,12 @@
+ #include "getopt2.h"
+ #include "ucon64.h"
+ 
++#if     defined USE_LIBIEEE1284 && defined USE_PPDEV
++#undef  USE_PPDEV
++#endif
++
+ 
+-#if     defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__ || defined AMIGA
++#if     defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__ || defined AMIGA || defined USE_LIBIEEE1284
+ static void close_io_port (void);
+ #endif
+ #if     defined __i386__ || defined __x86_64__  // GCC && x86
+@@ -92,8 +103,12 @@
+ 
+ #if     defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__
+ static int parport_io_fd;
+-#ifdef  USE_PPDEV
++#endif
++#if     defined USE_PPDEV || defined USE_LIBIEEE1284
+ static enum { FORWARD = 0, REVERSE } parport_io_direction;
++#ifdef  USE_LIBIEEE1284
++struct parport *libieee1284_port;
++#else
+ static int parport_io_mode;
+ #endif
+ #endif
+@@ -113,7 +128,7 @@
+ #endif
+ 
+ 
+-#if     defined _WIN32 || defined __CYGWIN__
++#if     (defined _WIN32 || defined __CYGWIN__) && !defined USE_LIBIEEE1284
+ 
+ #define NODRIVER_MSG "ERROR: No (working) I/O port driver. Please see the FAQ, question 4\n"
+ 
+@@ -172,7 +187,7 @@
+ static void (*output_word) (unsigned short, unsigned short) = outpw_func;
+ 
+ #endif
+-#endif // _WIN32 || __CYGWIN__
++#endif // (_WIN32 || __CYGWIN__) && !USE_LIBIEEE1284
+ 
+ 
+ #if     defined __i386__ || defined __x86_64__  // GCC && x86
+@@ -275,6 +290,43 @@
+       exit (1);
+     }
+   return byte;
++#elif   defined USE_LIBIEEE1284
++  int ppreg = port - ucon64.parport;
++  unsigned char byte;
++
++  switch (ppreg)
++    {
++    case 0:                                     // data
++      if (parport_io_direction == FORWARD)      // dir is forward?
++        {
++          parport_io_direction = REVERSE;       // change it to reverse
++          ieee1284_data_dir (libieee1284_port, parport_io_direction);
++        }
++      byte = (unsigned char) ieee1284_read_data (libieee1284_port);
++      break;
++    case 1:                                     // status
++      byte = (unsigned char) (ieee1284_read_status (libieee1284_port) ^ S1284_INVERTED);
++      break;
++    case 2:                                     // control
++      byte = (unsigned char) (ieee1284_read_control (libieee1284_port) ^ C1284_INVERTED);
++      break;
++    case 3:                                     // EPP/ECP address
++      ieee1284_epp_read_addr (libieee1284_port, F1284_NONBLOCK, (char *) &byte, 1);
++      break;
++    case 4:                                     // EPP/ECP data
++      ieee1284_epp_read_data (libieee1284_port, F1284_NONBLOCK, (char *) &byte, 1);
++      break;
++    case 0x402:                                 // ECP register
++      printf ("WARNING: Ignored read from ECP register, returning 0\n");
++      byte = 0;
++      break;
++    default:
++      fprintf (stderr,
++               "ERROR: inportb() tried to read from an unsupported port (0x%x)\n",
++               port);
++      exit (1);
++    }
++  return byte;
+ #elif   defined __BEOS__
+   st_ioport_t temp;
+ 
+@@ -358,6 +410,26 @@
+       exit (1);
+     }
+   return buf[0] | buf[1] << 8;                  // words are read in little endian format
++#elif   defined USE_LIBIEEE1284
++  int ppreg = port - ucon64.parport;
++  unsigned char buf[2];
++
++  switch (ppreg)
++    {
++    case 3:                                     // EPP/ECP address
++      ieee1284_epp_read_addr (libieee1284_port, F1284_NONBLOCK | F1284_FASTEPP, (char *) buf, 2);
++      break;
++    case 4:                                     // EPP/ECP data
++      ieee1284_epp_read_data (libieee1284_port, F1284_NONBLOCK | F1284_FASTEPP, (char *) buf, 2);
++      break;
++    // the data, status, control and ECP registers should only be accessed in "8-bit mode"
++    default:
++      fprintf (stderr,
++               "ERROR: inportw() tried to read from an unsupported port (0x%x)\n",
++               port);
++      exit (1);
++    }
++  return buf[0] | buf[1] << 8;                  // words are read in little endian format
+ #elif   defined __BEOS__
+   st_ioport_t temp;
+ 
+@@ -434,6 +506,37 @@
+                port);
+       exit (1);
+     }
++#elif   defined USE_LIBIEEE1284
++  int ppreg = port - ucon64.parport;
++
++  switch (ppreg)
++    {
++    case 0:                                     // data
++      if (parport_io_direction == REVERSE)      // dir is reverse?
++        {
++          parport_io_direction = FORWARD;       // change it to forward
++          ieee1284_data_dir (libieee1284_port, parport_io_direction);
++        }
++      ieee1284_write_data (libieee1284_port, byte);
++      break;
++    case 2:                                     // control
++      ieee1284_write_control (libieee1284_port, (unsigned char) (byte ^ C1284_INVERTED));
++      break;
++    case 3:                                     // EPP/ECP address
++      ieee1284_epp_write_addr (libieee1284_port, F1284_NONBLOCK, (char *) &byte, 1);
++      break;
++    case 4:                                     // EPP/ECP data
++      ieee1284_epp_write_data (libieee1284_port, F1284_NONBLOCK, (char *) &byte, 1);
++      break;
++    case 0x402:                                 // ECP register
++      printf ("WARNING: Ignored write to ECP register\n");
++      break;
++    default:
++      fprintf (stderr,
++               "ERROR: outportb() tried to write to an unsupported port (0x%x)\n",
++               port);
++      exit (1);
++    }
+ #elif   defined __BEOS__
+   st_ioport_t temp;
+ 
+@@ -499,6 +602,28 @@
+                port);
+       exit (1);
+     }
++#elif   defined USE_LIBIEEE1284
++  int ppreg = port - ucon64.parport;
++  unsigned char buf[2];
++
++  // words are written in little endian format
++  buf[0] = (unsigned char) word;
++  buf[1] = word >> 8;
++  switch (ppreg)
++    {
++    case 3:                                     // EPP/ECP address
++      ieee1284_epp_write_addr (libieee1284_port, F1284_NONBLOCK | F1284_FASTEPP, (char *) buf, 2);
++      break;
++    case 4:                                     // EPP/ECP data
++      ieee1284_epp_write_data (libieee1284_port, F1284_NONBLOCK | F1284_FASTEPP, (char *) buf, 2);
++      break;
++    // the data, control and ECP registers should only be accessed in "8-bit mode"
++    default:
++      fprintf (stderr,
++               "ERROR: outportw() tried to write to an unsupported port (0x%x)\n",
++               port);
++      exit (1);
++    }
+ #elif   defined __BEOS__
+   st_ioport_t temp;
+ 
+@@ -529,7 +654,7 @@
+ }
+ 
+ 
+-#if     (defined __i386__ || defined __x86_64__ || defined _WIN32) && !defined USE_PPDEV
++#if     (defined __i386__ || defined __x86_64__ || defined _WIN32) && !defined USE_PPDEV && !defined USE_LIBIEEE1284
+ #define DETECT_MAX_CNT 1000
+ static int
+ parport_probe (unsigned int port)
+@@ -557,6 +682,7 @@
+ #endif
+ 
+ 
++#ifndef USE_LIBIEEE1284
+ #ifdef  _WIN32
+ static LONG
+ new_exception_filter (LPEXCEPTION_POINTERS exception_pointers)
+@@ -584,6 +710,7 @@
+   return EXCEPTION_CONTINUE_SEARCH;
+ }
+ #endif
++#endif
+ 
+ 
+ int
+@@ -637,6 +764,48 @@
+ 
+   parport_io_direction = FORWARD;               // set forward direction as default
+   ioctl (parport_io_fd, PPDATADIR, &parport_io_direction);
++#elif   defined USE_LIBIEEE1284
++  struct parport_list list;
++  int capabilities, ucon64_parport;
++
++  if (port == PARPORT_UNKNOWN)
++    port = 0;
++
++  if (ieee1284_find_ports (&list, 0) != E1284_OK)
++    {
++      fputs ("ERROR: Could not get port list\n", stderr);
++      exit (1);
++    }
++
++  if (port < list.portc)
++    libieee1284_port = list.portv[port];
++  else
++    {
++      fprintf (stderr, "ERROR: libieee1284 port %d not available\n", port);
++      exit (1);
++    }
++
++  if (ieee1284_open (libieee1284_port, F1284_EXCL, &capabilities) != E1284_OK)
++    {
++      fprintf (stderr, "ERROR: Could not open libieee1284 port %d\n", port);
++      exit (1);
++    }
++  // TODO: Handle ECP mode correctly
++  if (ucon64.parport_mode == UCON64_EPP || ucon64.parport_mode == UCON64_ECP)
++    if ((capabilities & (CAP1284_EPP | CAP1284_ECP)) == 0)
++      printf ("WARNING: EPP or ECP mode was requested, but not available\n");
++
++  ieee1284_free_ports (&list);
++
++  if (ieee1284_claim (libieee1284_port) != E1284_OK)
++    {
++      fprintf (stderr, "ERROR: Could not claim libieee1284 port %d\n", port);
++      ieee1284_close (libieee1284_port);
++      exit (1);
++    }
++
++  parport_io_direction = FORWARD;
++  ieee1284_data_dir (libieee1284_port, parport_io_direction);
+ #elif   defined __BEOS__
+   parport_io_fd = open ("/dev/misc/ioport", O_RDWR | O_NONBLOCK);
+   if (parport_io_fd == -1)
+@@ -710,16 +879,18 @@
+     }
+ #endif
+ 
+-#if     defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__
++#if     defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__ || defined USE_LIBIEEE1284
+   if (register_func (close_io_port) == -1)
+     {
++#ifndef USE_LIBIEEE1284
+       close (parport_io_fd);
++#endif
+       fprintf (stderr, "ERROR: Could not register function with register_func()\n");
+       exit(1);
+     }
+ #endif
+ 
+-#if     defined __linux__ && (defined __i386__ || defined __x86_64__) && !defined USE_PPDEV
++#if     defined __linux__ && (defined __i386__ || defined __x86_64__) && !defined USE_PPDEV && !defined USE_LIBIEEE1284
+   /*
+     Some code needs us to switch to the real uid and gid. However, other code
+     needs access to I/O ports other than the standard printer port registers.
+@@ -751,7 +922,7 @@
+     }
+ #endif
+ 
+-#if     (defined __i386__ || defined __x86_64__ || defined _WIN32) && !defined USE_PPDEV
++#if     (defined __i386__ || defined __x86_64__ || defined _WIN32) && !defined USE_PPDEV && !defined USE_LIBIEEE1284
+ 
+ #if     defined _WIN32 || defined __CYGWIN__
+   /*
+@@ -914,9 +1085,9 @@
+           exit (1);
+         }
+     }
+-#endif // (__i386__ || __x86_64__ || _WIN32) && !USE_PPDEV
++#endif // (__i386__ || __x86_64__ || _WIN32) && !USE_PPDEV && !USE_LIBIEEE1284
+ 
+-#ifdef  USE_PPDEV
++#if     defined USE_PPDEV || defined USE_LIBIEEE1284
+   // the following two calls need a valid value for ucon64.parport
+   ucon64_parport = ucon64.parport;
+   ucon64.parport = port;
+@@ -924,7 +1095,7 @@
+   outportb ((unsigned short) (port + PARPORT_CONTROL),
+             (unsigned char) (inportb ((unsigned short) (port + PARPORT_CONTROL)) & 0x0f));
+   // bit 4 = 0 -> IRQ disable for ACK, bit 5-7 unused
+-#ifdef  USE_PPDEV
++#if     defined USE_PPDEV || defined USE_LIBIEEE1284
+   ucon64.parport = ucon64_parport;
+ #endif
+ 
+@@ -932,7 +1103,7 @@
+ }
+ 
+ 
+-#if     defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__ || defined AMIGA
++#if     defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__ || defined AMIGA || defined USE_LIBIEEE1284
+ void
+ close_io_port (void)
+ {
+@@ -947,6 +1118,9 @@
+   //  it ppdev will do it for us...
+   ioctl (parport_io_fd, PPNEGOT, &parport_io_mode);
+   ioctl (parport_io_fd, PPRELEASE);
++#elif   defined USE_LIBIEEE1284
++  ieee1284_release (libieee1284_port);
++  ieee1284_close (libieee1284_port);
+ #else                                           // __BEOS__ || __FreeBSD__
+   close (parport_io_fd);
+ #endif
+@@ -958,7 +1132,7 @@
+ parport_close (int parport)
+ {
+   (void) parport;
+-#if     defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__ || defined AMIGA
++#if     defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__ || defined AMIGA || defined USE_LIBIEEE1284
+   if (unregister_func (close_io_port) == 0)     // call func only if it can be removed!
+     close_io_port ();                           //  (or else it will be called twice)
+ #elif   defined _WIN32 || defined __CYGWIN__
+@@ -976,6 +1150,9 @@
+ {
+ #ifdef  USE_PPDEV
+   printf ("Using parallel port device: %s\n", ucon64.parport_dev);
++#elif   defined USE_LIBIEEE1284
++  printf ("Using parallel port device: %s, base address 0x%lx\n",
++          libieee1284_port->filename, libieee1284_port->base_addr); // ->name
+ #elif   defined AMIGA
+   printf ("Using parallel port device: %s, port %d\n", ucon64.parport_dev, ucon64.parport);
+ #else

+ 343 - 0
tools/ucon64-2.0.0-src/license.html

@@ -0,0 +1,343 @@
+<html><head>
+<link rel="icon" href="images/icon.png" type="image/png">
+<title>uCON64 - License</title></head><body bgcolor="#ffffff"><tt>
+		    GNU GENERAL PUBLIC LICENSE<br>
+		       Version 2, June 1991<br>
+<br>
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.<br>
+                          675 Mass Ave, Cambridge, MA 02139, USA<br>
+ Everyone is permitted to copy and distribute verbatim copies<br>
+ of this license document, but changing it is not allowed.<br>
+<br>
+			    Preamble<br>
+<br>
+  The licenses for most software are designed to take away your<br>
+freedom to share and change it.  By contrast, the GNU General Public<br>
+License is intended to guarantee your freedom to share and change free<br>
+software--to make sure the software is free for all its users.  This<br>
+General Public License applies to most of the Free Software<br>
+Foundation's software and to any other program whose authors commit to<br>
+using it.  (Some other Free Software Foundation software is covered by<br>
+the GNU Library General Public License instead.)  You can apply it to<br>
+your programs, too.<br>
+<br>
+  When we speak of free software, we are referring to freedom, not<br>
+price.  Our General Public Licenses are designed to make sure that you<br>
+have the freedom to distribute copies of free software (and charge for<br>
+this service if you wish), that you receive source code or can get it<br>
+if you want it, that you can change the software or use pieces of it<br>
+in new free programs; and that you know you can do these things.<br>
+<br>
+  To protect your rights, we need to make restrictions that forbid<br>
+anyone to deny you these rights or to ask you to surrender the rights.<br>
+These restrictions translate to certain responsibilities for you if you<br>
+distribute copies of the software, or if you modify it.<br>
+<br>
+  For example, if you distribute copies of such a program, whether<br>
+gratis or for a fee, you must give the recipients all the rights that<br>
+you have.  You must make sure that they, too, receive or can get the<br>
+source code.  And you must show them these terms so they know their<br>
+rights.<br>
+<br>
+  We protect your rights with two steps: (1) copyright the software, and<br>
+(2) offer you this license which gives you legal permission to copy,<br>
+distribute and/or modify the software.<br>
+<br>
+  Also, for each author's protection and ours, we want to make certain<br>
+that everyone understands that there is no warranty for this free<br>
+software.  If the software is modified by someone else and passed on, we<br>
+want its recipients to know that what they have is not the original, so<br>
+that any problems introduced by others will not reflect on the original<br>
+authors' reputations.<br>
+<br>
+  Finally, any free program is threatened constantly by software<br>
+patents.  We wish to avoid the danger that redistributors of a free<br>
+program will individually obtain patent licenses, in effect making the<br>
+program proprietary.  To prevent this, we have made it clear that any<br>
+patent must be licensed for everyone's free use or not licensed at all.<br>
+<br>
+  The precise terms and conditions for copying, distribution and<br>
+modification follow.<br>
+<br>
+		    GNU GENERAL PUBLIC LICENSE<br>
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION<br>
+<br>
+  0. This License applies to any program or other work which contains<br>
+a notice placed by the copyright holder saying it may be distributed<br>
+under the terms of this General Public License.  The "Program", below,<br>
+refers to any such program or work, and a "work based on the Program"<br>
+means either the Program or any derivative work under copyright law:<br>
+that is to say, a work containing the Program or a portion of it,<br>
+either verbatim or with modifications and/or translated into another<br>
+language.  (Hereinafter, translation is included without limitation in<br>
+the term "modification".)  Each licensee is addressed as "you".<br>
+<br>
+Activities other than copying, distribution and modification are not<br>
+covered by this License; they are outside its scope.  The act of<br>
+running the Program is not restricted, and the output from the Program<br>
+is covered only if its contents constitute a work based on the<br>
+Program (independent of having been made by running the Program).<br>
+Whether that is true depends on what the Program does.<br>
+<br>
+  1. You may copy and distribute verbatim copies of the Program's<br>
+source code as you receive it, in any medium, provided that you<br>
+conspicuously and appropriately publish on each copy an appropriate<br>
+copyright notice and disclaimer of warranty; keep intact all the<br>
+notices that refer to this License and to the absence of any warranty;<br>
+and give any other recipients of the Program a copy of this License<br>
+along with the Program.<br>
+<br>
+You may charge a fee for the physical act of transferring a copy, and<br>
+you may at your option offer warranty protection in exchange for a fee.<br>
+<br>
+  2. You may modify your copy or copies of the Program or any portion<br>
+of it, thus forming a work based on the Program, and copy and<br>
+distribute such modifications or work under the terms of Section 1<br>
+above, provided that you also meet all of these conditions:<br>
+<br>
+    a) You must cause the modified files to carry prominent notices<br>
+    stating that you changed the files and the date of any change.<br>
+<br>
+    b) You must cause any work that you distribute or publish, that in<br>
+    whole or in part contains or is derived from the Program or any<br>
+    part thereof, to be licensed as a whole at no charge to all third<br>
+    parties under the terms of this License.<br>
+<br>
+    c) If the modified program normally reads commands interactively<br>
+    when run, you must cause it, when started running for such<br>
+    interactive use in the most ordinary way, to print or display an<br>
+    announcement including an appropriate copyright notice and a<br>
+    notice that there is no warranty (or else, saying that you provide<br>
+    a warranty) and that users may redistribute the program under<br>
+    these conditions, and telling the user how to view a copy of this<br>
+    License.  (Exception: if the Program itself is interactive but<br>
+    does not normally print such an announcement, your work based on<br>
+    the Program is not required to print an announcement.)<br>
+<br>
+These requirements apply to the modified work as a whole.  If<br>
+identifiable sections of that work are not derived from the Program,<br>
+and can be reasonably considered independent and separate works in<br>
+themselves, then this License, and its terms, do not apply to those<br>
+sections when you distribute them as separate works.  But when you<br>
+distribute the same sections as part of a whole which is a work based<br>
+on the Program, the distribution of the whole must be on the terms of<br>
+this License, whose permissions for other licensees extend to the<br>
+entire whole, and thus to each and every part regardless of who wrote it.<br>
+<br>
+Thus, it is not the intent of this section to claim rights or contest<br>
+your rights to work written entirely by you; rather, the intent is to<br>
+exercise the right to control the distribution of derivative or<br>
+collective works based on the Program.<br>
+<br>
+In addition, mere aggregation of another work not based on the Program<br>
+with the Program (or with a work based on the Program) on a volume of<br>
+a storage or distribution medium does not bring the other work under<br>
+the scope of this License.<br>
+<br>
+  3. You may copy and distribute the Program (or a work based on it,<br>
+under Section 2) in object code or executable form under the terms of<br>
+Sections 1 and 2 above provided that you also do one of the following:<br>
+<br>
+    a) Accompany it with the complete corresponding machine-readable<br>
+    source code, which must be distributed under the terms of Sections<br>
+    1 and 2 above on a medium customarily used for software interchange; or,<br>
+<br>
+    b) Accompany it with a written offer, valid for at least three<br>
+    years, to give any third party, for a charge no more than your<br>
+    cost of physically performing source distribution, a complete<br>
+    machine-readable copy of the corresponding source code, to be<br>
+    distributed under the terms of Sections 1 and 2 above on a medium<br>
+    customarily used for software interchange; or,<br>
+<br>
+    c) Accompany it with the information you received as to the offer<br>
+    to distribute corresponding source code.  (This alternative is<br>
+    allowed only for noncommercial distribution and only if you<br>
+    received the program in object code or executable form with such<br>
+    an offer, in accord with Subsection b above.)<br>
+<br>
+The source code for a work means the preferred form of the work for<br>
+making modifications to it.  For an executable work, complete source<br>
+code means all the source code for all modules it contains, plus any<br>
+associated interface definition files, plus the scripts used to<br>
+control compilation and installation of the executable.  However, as a<br>
+special exception, the source code distributed need not include<br>
+anything that is normally distributed (in either source or binary<br>
+form) with the major components (compiler, kernel, and so on) of the<br>
+operating system on which the executable runs, unless that component<br>
+itself accompanies the executable.<br>
+<br>
+If distribution of executable or object code is made by offering<br>
+access to copy from a designated place, then offering equivalent<br>
+access to copy the source code from the same place counts as<br>
+distribution of the source code, even though third parties are not<br>
+compelled to copy the source along with the object code.<br>
+<br>
+  4. You may not copy, modify, sublicense, or distribute the Program<br>
+except as expressly provided under this License.  Any attempt<br>
+otherwise to copy, modify, sublicense or distribute the Program is<br>
+void, and will automatically terminate your rights under this License.<br>
+However, parties who have received copies, or rights, from you under<br>
+this License will not have their licenses terminated so long as such<br>
+parties remain in full compliance.<br>
+<br>
+  5. You are not required to accept this License, since you have not<br>
+signed it.  However, nothing else grants you permission to modify or<br>
+distribute the Program or its derivative works.  These actions are<br>
+prohibited by law if you do not accept this License.  Therefore, by<br>
+modifying or distributing the Program (or any work based on the<br>
+Program), you indicate your acceptance of this License to do so, and<br>
+all its terms and conditions for copying, distributing or modifying<br>
+the Program or works based on it.<br>
+<br>
+  6. Each time you redistribute the Program (or any work based on the<br>
+Program), the recipient automatically receives a license from the<br>
+original licensor to copy, distribute or modify the Program subject to<br>
+these terms and conditions.  You may not impose any further<br>
+restrictions on the recipients' exercise of the rights granted herein.<br>
+You are not responsible for enforcing compliance by third parties to<br>
+this License.<br>
+<br>
+  7. If, as a consequence of a court judgment or allegation of patent<br>
+infringement or for any other reason (not limited to patent issues),<br>
+conditions are imposed on you (whether by court order, agreement or<br>
+otherwise) that contradict the conditions of this License, they do not<br>
+excuse you from the conditions of this License.  If you cannot<br>
+distribute so as to satisfy simultaneously your obligations under this<br>
+License and any other pertinent obligations, then as a consequence you<br>
+may not distribute the Program at all.  For example, if a patent<br>
+license would not permit royalty-free redistribution of the Program by<br>
+all those who receive copies directly or indirectly through you, then<br>
+the only way you could satisfy both it and this License would be to<br>
+refrain entirely from distribution of the Program.<br>
+<br>
+If any portion of this section is held invalid or unenforceable under<br>
+any particular circumstance, the balance of the section is intended to<br>
+apply and the section as a whole is intended to apply in other<br>
+circumstances.<br>
+<br>
+It is not the purpose of this section to induce you to infringe any<br>
+patents or other property right claims or to contest validity of any<br>
+such claims; this section has the sole purpose of protecting the<br>
+integrity of the free software distribution system, which is<br>
+implemented by public license practices.  Many people have made<br>
+generous contributions to the wide range of software distributed<br>
+through that system in reliance on consistent application of that<br>
+system; it is up to the author/donor to decide if he or she is willing<br>
+to distribute software through any other system and a licensee cannot<br>
+impose that choice.<br>
+<br>
+This section is intended to make thoroughly clear what is believed to<br>
+be a consequence of the rest of this License.<br>
+<br>
+  8. If the distribution and/or use of the Program is restricted in<br>
+certain countries either by patents or by copyrighted interfaces, the<br>
+original copyright holder who places the Program under this License<br>
+may add an explicit geographical distribution limitation excluding<br>
+those countries, so that distribution is permitted only in or among<br>
+countries not thus excluded.  In such case, this License incorporates<br>
+the limitation as if written in the body of this License.<br>
+<br>
+  9. The Free Software Foundation may publish revised and/or new versions<br>
+of the General Public License from time to time.  Such new versions will<br>
+be similar in spirit to the present version, but may differ in detail to<br>
+address new problems or concerns.<br>
+<br>
+Each version is given a distinguishing version number.  If the Program<br>
+specifies a version number of this License which applies to it and "any<br>
+later version", you have the option of following the terms and conditions<br>
+either of that version or of any later version published by the Free<br>
+Software Foundation.  If the Program does not specify a version number of<br>
+this License, you may choose any version ever published by the Free Software<br>
+Foundation.<br>
+<br>
+  10. If you wish to incorporate parts of the Program into other free<br>
+programs whose distribution conditions are different, write to the author<br>
+to ask for permission.  For software which is copyrighted by the Free<br>
+Software Foundation, write to the Free Software Foundation; we sometimes<br>
+make exceptions for this.  Our decision will be guided by the two goals<br>
+of preserving the free status of all derivatives of our free software and<br>
+of promoting the sharing and reuse of software generally.<br>
+<br>
+			    NO WARRANTY<br>
+<br>
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY<br>
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN<br>
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES<br>
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED<br>
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF<br>
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS<br>
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE<br>
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,<br>
+REPAIR OR CORRECTION.<br>
+<br>
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING<br>
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR<br>
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,<br>
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING<br>
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED<br>
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY<br>
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER<br>
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE<br>
+POSSIBILITY OF SUCH DAMAGES.<br>
+<br>
+		     END OF TERMS AND CONDITIONS<br>
+<br>
+	Appendix: How to Apply These Terms to Your New Programs<br>
+<br>
+  If you develop a new program, and you want it to be of the greatest<br>
+possible use to the public, the best way to achieve this is to make it<br>
+free software which everyone can redistribute and change under these terms.<br>
+<br>
+  To do so, attach the following notices to the program.  It is safest<br>
+to attach them to the start of each source file to most effectively<br>
+convey the exclusion of warranty; and each file should have at least<br>
+the "copyright" line and a pointer to where the full notice is found.<br>
+<br>
+    <one line to give the program's name and a brief idea of what it does.><br>
+    Copyright (C) 19yy  <name of author><br>
+<br>
+    This program is free software; you can redistribute it and/or modify<br>
+    it under the terms of the GNU General Public License as published by<br>
+    the Free Software Foundation; either version 2 of the License, or<br>
+    (at your option) any later version.<br>
+<br>
+    This program is distributed in the hope that it will be useful,<br>
+    but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
+    GNU General Public License for more details.<br>
+<br>
+    You should have received a copy of the GNU General Public License<br>
+    along with this program; if not, write to the Free Software<br>
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.<br>
+<br>
+Also add information on how to contact you by electronic and paper mail.<br>
+<br>
+If the program is interactive, make it output a short notice like this<br>
+when it starts in an interactive mode:<br>
+<br>
+    Gnomovision version 69, Copyright (C) 19yy name of author<br>
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.<br>
+    This is free software, and you are welcome to redistribute it<br>
+    under certain conditions; type `show c' for details.<br>
+<br>
+The hypothetical commands `show w' and `show c' should show the appropriate<br>
+parts of the General Public License.  Of course, the commands you use may<br>
+be called something other than `show w' and `show c'; they could even be<br>
+mouse-clicks or menu items--whatever suits your program.<br>
+<br>
+You should also get your employer (if you work as a programmer) or your<br>
+school, if any, to sign a "copyright disclaimer" for the program, if<br>
+necessary.  Here is a sample; alter the names:<br>
+<br>
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program<br>
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.<br>
+<br>
+  <signature of Ty Coon>, 1 April 1989<br>
+  Ty Coon, President of Vice<br>
+<br>
+This General Public License does not permit incorporating your program into<br>
+proprietary programs.  If your program is a subroutine library, you may<br>
+consider it more useful to permit linking proprietary applications with the<br>
+library.  If this is what you want to do, use the GNU Library General<br>
+Public License instead of this License.<br>
+</tt></body></html>

+ 594 - 0
tools/ucon64-2.0.0-src/readme.html

@@ -0,0 +1,594 @@
+<html><head>
+<link rel="icon" href="images/icon.png" type="image/png">
+<title>uCON64 - ReadMe</title></head><body bgcolor="#ffffff"><tt><pre>
+uCON64 - THE tool for all your video game console concerns.
+
+Its main features are:
+- It is Open Source Software
+- (Therefore) support for MANY platforms like: Unix, Win32, MSDOS, etc...
+- Support for 14+ different cartridge and disc-based video game console systems
+- Support for ALL common patch file formats like: IPS *), APS, BSL, PPF and
+  Game Genie
+  *) with RLE compression<!--
+- Support of DAT file formats like: RomCenter Data, GoodXXXX
+RomCenter Data
+MAME Listinfo
+RomCenter 2 Data
+Title List
+RAINE Gameinfo
+Callus Gamelist
+Retrocade Romlist
+Sparcade RAT File
+Mimic GCS
+Nebula Driver
+-->
+- Support for 10+ different backup units to backup/restore ROM(s), disc(s) and
+  SRAM(s) like: Flash Advance Linker, Flash 2 Advance, Doctor V64 (Junior),
+  Super Wild Card, Game Doctor, Dexdrive, Super Magic Drive, Game Boy Xchanger,
+  Mad Catz Camera Link, Lynxit, MD-PRO, SMS-PRO, PCE-PRO, GG-PRO, Super Flash
+  and your CD-writer.
+- Finally 150+(!) different options/features for every kind of ROM
+  handling/management known to man :-)
+<!-- 120 options, 62 switches last time I counted them -->
+  Including:
+  *VERY* verbose ROM information for ALL systems, detection of bad dumps using
+  internal checksums or DAT files, CRC32 calculation, ROM comparison,
+  modification (strip, pad, etc...), SRAM conversion (emulator &#60;-&#62; backup
+  unit), DiscJuggler and Nero image conversions, complete gzip and zip support
+  for all file-related funtions, etc...
+- Always up-to-date with support for new shit, trends, formats, and headers
+  like UNIF (NES), NSRT (SNES), new DAT file formats, etc...
+
+For console specific options check the following usage:
+
+uCON64 2.0.0 Unix (Linux) 1999-2005
+Uses code from various people. See 'developers.html' for more!
+This may be freely redistributed under the terms of the GNU Public License
+
+Usage: ucon64 [OPTION]... [ROM|IMAGE|SRAM|FILE|DIR|ARCHIVE]...
+
+Options
+   -o=DIRECTORY specify output directory
+  --nbak        prevents backup files (*.BAK)
+  --ncol        disable ANSI colors in output
+  --port=PORT   specify USB or parallel PORT={USB0, USB1, 3bc, 378, 278, ...}
+  --hdn=N       force ROM has backup unit/emulator header with size of N Bytes
+  --hd          same as --hdn=512
+                  most backup units use a header with a size of 512 Bytes
+  --nhd         force ROM has no backup unit/emulator header
+  --ns          force ROM is not split
+   -e           emulate/run ROM (check .ucon64rc for all Emulator settings)
+  --crc         show CRC32 value of ROM
+  --sha1        show SHA1 value of ROM
+  --md5         show MD5 value of ROM
+  --ls          generate ROM list for all recognized ROMs
+  --lsv         like --ls but more verbose
+  --hex[=ST]    show ROM as hexdump; use "ucon64 --hex ...|less"
+                  ST is the optional start value in bytes
+  --dual[=ST]   show ROM as dualdump; use "ucon64 --dual ...|less"
+  --code[=ST]   show ROM as code; use "ucon64 --code ...|less"
+  --print[=ST]  show ROM in printable characters; use "ucon64 --print ...|less"
+  --find=STRING find STRING in ROM (wildcard: '?')
+  --findi=STR   like --find but ignores the case of alpha bytes
+  --findr=STR   like --find but looks also for shifted/relative similarities
+                  (wildcard: disabled)
+   -c=FILE      compare FILE with ROM for differences
+  --cs=FILE     compare FILE with ROM for similarities
+  --help        display this help and exit
+  --version     output version information and exit
+   -q           be quiet (don't show ROM info)
+   -v           be more verbose (show backup unit headers also)
+
+Padding
+  --ispad       check if ROM is padded
+  --pad         pad ROM to next Mb
+   -p           same as --pad
+  --padn=N      pad ROM to N Bytes (put Bytes with value 0x00 after end)
+  --strip=N     strip N Bytes from end of ROM
+  --stpn=N      strip N Bytes from start of ROM
+  --stp         same as --stpn=512
+                  most backup units use a header with a size of 512 Bytes
+  --insn=N      insert N Bytes (0x00) before ROM
+  --ins         same as --insn=512
+                  most backup units use a header with a size of 512 Bytes
+
+DATabase (support for DAT files)
+  --db          DATabase statistics
+  --dbv         like --db but more verbose
+  --dbs=CRC32   search ROM with CRC32 in DATabase
+  --scan        generate ROM list for all ROMs using DATabase
+                  like: GoodXXXX scan ...
+  --lsd         same as --scan
+  --mkdat=DATFILE create DAT file; use -o to specify an output directory
+  --rrom        rename ROMs to their internal names
+  --rename      rename ROMs to their DATabase names
+                  use -o to specify an output directory
+  --rr83        force to rename to 8.3 filenames
+  --force63     force to rename all filenames into Joliet CD format
+                  like: GoodXXXX rename inplace force63 ...
+                  TIP: using --nes would process only NES ROMs
+  --rl          rename ROMs to lowercase
+  --ru          rename ROMs to uppercase
+
+Patching
+  --poke=OFF:V  change byte at file offset OFF to value V (both in hexadecimal)
+  --pattern=FILE change ROM based on patterns specified in FILE
+  --patch=PATCH specify the PATCH for the following options
+                  use this option or uCON64 expects the last commandline
+                  argument to be the name of the PATCH file
+   -b           apply Baseline/BSL PATCH to ROM
+   -i           apply IPS PATCH to ROM (IPS&#60;=v1.2)
+  --mki=ORG_ROM create IPS patch; ROM should be the modified ROM
+   -a           apply APS PATCH to ROM (APS&#60;=v1.2)
+  --mka=ORG_ROM create APS patch; ROM should be the modified ROM
+  --na=DESC     change APS single line DESCRIPTION
+  --ppf         apply PPF PATCH to IMAGE (PPF&#60;=v2.0); ROM should be an IMAGE
+  --mkppf=ORG_IMG create PPF patch; ROM should be the modified IMAGE
+  --nppf=DESC   change PPF single line DESCRIPTION
+  --idppf=FILE_ID.DIZ change FILE_ID.DIZ of PPF PATCH (PPF v2.0)
+  --gge=CODE    encode and display Game Genie code
+                  example: --gge=CODE --sms or --gge=CODE --gb
+                  CODE='AAAA:VV' or CODE='AAAA:VV:CC'
+                  --gge=CODE --gen
+                  CODE='AAAAAA:VVVV'
+                  --gge=CODE --nes
+                  CODE='AAAA:VV' or CODE='AAAA:VV:CC'
+                  --gge=CODE --snes
+                  CODE='AAAAAA:VV'
+  --ggd=GG_CODE decode Game Genie code
+                  example: --ggd=GG_CODE --sms or --ggd=GG_CODE --gb
+                  GG_CODE='XXX-XXX' or GG_CODE='XXX-XXX-XXX'
+                  --ggd=GG_CODE --gen
+                  GG_CODE='XXXX-XXXX'
+                  --ggd=GG_CODE --nes
+                  GG_CODE='XXXXXX' or GG_CODE='XXXXXXXX'
+                  --ggd=GG_CODE --snes
+                  GG_CODE='XXXX-XXXX'
+  --gg=GG_CODE  apply Game Genie code (permanently)
+                  example: like above but a ROM is required
+                  supported are:
+                  Game Boy/(Super GB)/GB Pocket/Color GB/(GB Advance),
+                  Sega Master System(II/III)/Game Gear (Handheld),
+                  Genesis/Sega Mega Drive/Sega CD/32X/Nomad,
+                  Nintendo Entertainment System/NES/Famicom/Game Axe (Redant),
+                  Super Nintendo Entertainment System/SNES/Super Famicom
+
+All disc-based consoles
+  --disc        force recognition; NEEDED
+  --rip=N       rip/dump track N from IMAGE
+  --bin2iso=N   convert track N to ISO (if possible) by resizing
+                  sectors to 2048 Bytes
+  --isofix=N    fix corrupted track N (if possible)
+                  if PVD points to a bad DR offset it will add padding data
+                  so actual DR gets located in right absolute address
+  --mkcue       generate CUE sheet for IMAGE or existing TOC sheet
+  --mktoc       generate TOC sheet for IMAGE or existing CUE sheet
+
+Dreamcast
+  --dc          force recognition
+  --scr         scramble 1ST_READ.BIN for selfboot CDs
+  --unscr       unscramble 1ST_READ.BIN for non-selfboot CDs
+  --mkip        generate IP.BIN file with default values
+  --parse=TEMPLATE parse TEMPLATE file into a IP.BIN;
+                  creates an empty template when TEMPLATE does not exist
+
+Playstation (One)/Playstation 2 (CD only)
+  --psx         force recognition
+DexDrive
+  --xdex=N      send/receive Block N to/from DexDrive; --port=PORT
+                  receives automatically when SRAM does not exist
+
+Game Boy Advance
+  --gba         force recognition
+   -n=NEW_NAME  change internal ROM name to NEW_NAME
+  --logo        restore ROM logo character data (offset: 0x04-0x9F)
+  --chk         fix ROM header checksum
+  --sram        patch ROM for SRAM saving
+  --crp=WAIT_TIME slow down ROM access ("crash patch");
+                  WAIT_TIME=0  default in most crash patches
+                  WAIT_TIME=4  faster than 0, slower than 8
+                  WAIT_TIME=8  faster than 4, slower than 28
+                  WAIT_TIME=12 slowest cartridge access speed
+                  WAIT_TIME=16 faster than 28, but slower than 20
+                  WAIT_TIME=20 default in most original cartridges
+                  WAIT_TIME=24 fastest cartridge access speed
+                  WAIT_TIME=28 faster than 8 but slower than 16
+  --multi=SIZE  make multi-game file for use with FAL/F2A flash card, truncated
+                  to SIZE Mbit; file with loader must be specified first, then
+                  all the ROMs, multi-game file to create last
+Flash 2 Advance (Ultra)
+  --xf2a        send/receive ROM to/from Flash 2 Advance (Ultra); --port=PORT
+                  receives automatically (32 Mbits) when ROM does not exist
+  --xf2amulti=SIZE send multiple ROMs to Flash 2 Advance (Ultra); specify a
+                  loader in the configuration file; --port=PORT
+  --xf2ac=N     receive N Mbits of ROM from Flash 2 Advance (Ultra);
+                  --port=PORT
+  --xf2as       send/receive SRAM to/from Flash 2 Advance (Ultra); --port=PORT
+                  receives automatically when SRAM does not exist
+  --xf2ab=BANK  send/receive SRAM to/from Flash 2 Advance (Ultra) BANK
+                  BANK should be a number &#62;= 1; --port=PORT
+                  receives automatically when SRAM does not exist
+Flash Advance Linker
+  --xfal        send/receive ROM to/from Flash Advance Linker; --port=PORT
+                  receives automatically (32 Mbits) when ROM does not exist
+  --xfalmulti=SIZE send multiple ROMs to Flash Advance Linker (makes temporary
+                  multi-game file truncated to SIZE Mbit); specify a loader in
+                  the configuration file; --port=PORT
+  --xfalc=N     receive N Mbits of ROM from Flash Advance Linker; --port=PORT
+                  N can be 8, 16, 32, 64, 128 or 256
+  --xfals       send/receive SRAM to/from Flash Advance Linker; --port=PORT
+                  receives automatically when SRAM does not exist
+  --xfalb=BANK  send/receive SRAM to/from Flash Advance Linker BANK
+                  BANK can be 1, 2, 3 or 4; --port=PORT
+                  receives automatically when SRAM does not exist
+  --xfalm       try to enable EPP mode, default is SPP mode
+
+Nintendo 64
+  --n64         force recognition
+  --int         force ROM is in interleaved format (2143, V64)
+  --nint        force ROM is not in interleaved format (1234, Z64)
+   -n=NEW_NAME  change internal ROM name to NEW_NAME
+  --v64         convert to Doctor V64 (and compatibles/interleaved)
+  --z64         convert to Mr. Backup Z64 (not interleaved)
+  --dint        convert ROM to (non-)interleaved format (1234 &#60;-&#62; 2143)
+  --swap        same as --dint, byte-swap ROM
+  --swap2       word-swap ROM (1234 &#60;-&#62; 3412)
+  --bot=BOOTCODE replace/extract BOOTCODE (4032 Bytes) in/from ROM;
+                  extracts automatically if BOOTCODE does not exist
+  --lsram=SRAM  LaC's SRAM upload tool; ROM should be LaC's ROM image
+                  the SRAM must have a size of 512 Bytes
+                  this option generates a ROM which can be used to transfer
+                  SRAMs to your cartridge's SRAM (EEPROM)
+  --usms=SMSROM Jos Kwanten's UltraSMS (Sega Master System/Game Gear emulator);
+                  ROM should be Jos Kwanten's UltraSMS ROM image
+                  works only for SMS ROMs which are &#60;= 4 Mb in size
+  --chk         fix ROM checksum
+                  supports only 6102 and 6105 boot codes
+Doctor V64
+  --xv64        send/receive ROM to/from Doctor V64; --port=PORT
+                  receives automatically when ROM does not exist
+Doctor V64 Junior
+  --xdjr        send ROM to Doctor V64 Junior; --port=PORT
+CD64
+  --xcd64       send/receive ROM to/from CD64; --port=PORT
+                  receives automatically (64 Mbits) when ROM does not exist
+  --xcd64c=N    receive N Mbits of ROM from CD64; --port=PORT
+  --xcd64b      send boot emu to CD64; --port=PORT
+  --xcd64s      send/receive SRAM to/from CD64; --port=PORT
+                  receives automatically when SRAM file does not exist
+  --xcd64f      send/receive flash RAM to/from CD64; --port=PORT
+                  receives automatically when flash RAM file does not exist
+  --xcd64e      send/receive EEPROM data to/from CD64; --port=PORT
+                  receives automatically when EEPROM file does not exist
+  --xcd64m=INDEX send/receive memory pack data to/from CD64; --port=PORT
+                  INDEX is ignored for CD64 BIOS protocol
+                  receives automatically when memory pack file does not exist
+  --xcd64p=PROT use protocol PROT when communicating with CD64; --port=PORT
+                  PROT=0 CD64 BIOS
+                  PROT=1 Ghemor
+                  PROT=2 UltraLink
+DexDrive
+  --xdex=N      send/receive Block N to/from DexDrive; --port=PORT
+                  receives automatically when SRAM does not exist
+
+Super Nintendo Entertainment System/SNES/Super Famicom
+  --snes        force recognition
+  --hi          force ROM is HiROM
+  --nhi         force ROM is not HiROM
+  --erom        force ROM is "Extended" (combine with -hi for Extended HiROM)
+  --int         force ROM is in interleaved format (GD3/UFO)
+  --int2        force ROM is in interleaved format 2 (SFX)
+  --nint        force ROM is not in interleaved format
+  --bs          force ROM is a Broadcast Satellaview dump
+  --nbs         force ROM is a regular cartridge dump
+   -n=NEW_NAME  change internal ROM name to NEW_NAME
+  --fig         convert to *Pro Fighter*/FIG
+  --figs        convert emulator *.srm (SRAM) to *Pro Fighter*/FIG
+  --gd3         convert to Game Doctor SF3(SF6/SF7)/Professor SF(SF II)
+  --gd3s        convert emulator *.srm (SRAM) to GD SF3(SF6/SF7)/Professor SF*
+  --mgd         convert to Multi Game*/MGD2/MGH/RAW
+  --smc         convert to Super Magicom/SMC
+  --swc         convert to Super Wild Card*/SWC
+  --swcs        convert emulator *.srm (SRAM) to Super Wild Card*/SWC
+  --ufo         convert to Super UFO
+  --ufos        convert emulator *.srm (SRAM) to Super UFO
+  --stp         convert SRAM from backup unit for use with an emulator
+                  --stp just strips the first 512 bytes
+  --dbuh        display (relevant part of) backup unit header
+  --dint        deinterleave ROM (regardless whether the ROM is interleaved)
+  --ctrl=TYPE   specify type of controller in port 1 for emu when converting
+                  TYPE=0 gamepad
+                  TYPE=1 mouse
+                  TYPE=2 mouse / gamepad
+                  TYPE=6 multitap
+  --ctrl2=TYPE  specify type of controller in port 2 for emu when converting
+                  TYPE=0 gamepad
+                  TYPE=1 mouse
+                  TYPE=2 mouse / gamepad
+                  TYPE=3 super scope
+                  TYPE=4 super scope / gamepad
+                  TYPE=5 Konami's justifier
+                  TYPE=6 multitap
+                  TYPE=7 mouse / super scope / gamepad
+  --col=0xCOLOR convert 0xRRGGBB (HTML) &#60;-&#62; 0xXXXX (SNES)
+   -j           join split ROM
+   -s           split ROM; default part size is 8 Mb
+  --ssize=SIZE  specify split part size in Mbit (not for Game Doctor SF3)
+   -k           remove protection (crack)
+   -f           remove NTSC/PAL protection
+   -l           remove SlowROM checks
+  --chk         fix ROM checksum
+  --multi=SIZE  make multi-game file for use with Super Flash flash card,
+                  truncated to SIZE Mbit; file with loader must be specified
+                  first, then all the ROMs, multi-game file to create last
+  --dmirr       "de-mirror" ROM (strip mirrored block from end of ROM)
+  --dnsrt       "de-NSRT" ROM (restore name and checksum from NSRT header)
+Super Com Pro/Super Magicom/SMC/Super Wild Card (1.6XC/2.7CC/2.8CC/DX/DX2)/SWC
+  --xswc        send/receive ROM to/from Super Wild Card*/SWC; --port=PORT
+                  receives automatically when ROM does not exist
+  --xswc2       same as --xswc, but enables Real Time Save mode (SWC only)
+  --xswc-io=MODE specify SWC I/O mode; use with -xswc or -xswcc
+                  MODE=0x001 force 32 Mbit dump
+                  MODE=0x002 use alternative method for determining ROM size
+                  MODE=0x004 Super FX
+                  MODE=0x008 S-DD1
+                  MODE=0x010 SA-1
+                  MODE=0x020 SPC7110
+                  MODE=0x040 DX2 trick (might work with other SWC models)
+                  MODE=0x080 Mega Man X 2
+                  MODE=0x100 dump BIOS
+                  It is possible to combine flags. MODE=0x44 makes it possible
+                  to dump for example Yoshi's Island
+  --xswcs       send/receive SRAM to/from Super Wild Card*/SWC; --port=PORT
+                  receives automatically when SRAM does not exist
+  --xswcc       send/receive SRAM to/from cartridge in Super Wild Card*/SWC;
+                  --port=PORT
+                  receives automatically when SRAM does not exist
+  --xswcr       send/receive RTS data to/from Super Wild Card*/SWC; --port=PORT
+                  receives automatically when RTS file does not exist
+Game Doctor SF3(SF6/SF7)/Professor SF(SF II)
+  --xgd3        send ROM to Game Doctor SF3/SF6/SF7; --port=PORT
+                  this option uses the Game Doctor SF3 protocol
+  --xgd6        send ROM to Game Doctor SF6/SF7; --port=PORT
+                  this option uses the Game Doctor SF6 protocol
+  --xgd3s       send SRAM to Game Doctor SF3/SF6/SF7; --port=PORT
+  --xgd6s       send/receive SRAM to/from Game Doctor SF6/SF7; --port=PORT
+                  receives automatically when SRAM does not exist
+  --xgd6r       send/receive saver (RTS) data to/from Game Doctor SF6/SF7;
+                  --port=PORT
+                  receives automatically when saver file does not exist
+Super Pro Fighter (Q/Q+)/Pro Fighter X (Turbo 2)/Double Pro Fighter (X Turbo)
+  --xfig        send/receive ROM to/from *Pro Fighter*/FIG; --port=PORT
+                  receives automatically when ROM does not exist
+  --xfigs       send/receive SRAM to/from *Pro Fighter*/FIG; --port=PORT
+                  receives automatically when SRAM does not exist
+  --xfigc       send/receive SRAM to/from cartridge in *Pro Fighter*/FIG;
+                  --port=PORT
+                  receives automatically when SRAM does not exist
+Super Flash flash card programmer
+  --xsf         send/receive ROM to/from Super Flash flash card programmer
+                  --port=PORT
+                  receives automatically (64 Mbits) when ROM does not exist
+  --xsfs        send/receive SRAM to/from Super Flash flash card programmer
+                  --port=PORT
+                  receives automatically when SRAM does not exist
+
+Neo Geo/Neo Geo CD(Z)/MVS
+  --ng          force recognition
+  --bios=BIOS   convert NeoCD BIOS to work with NeoCD emulator
+  --sam=SAMFILE convert SAM/M.A.M.E. sound to WAV
+
+Genesis/Sega Mega Drive/Sega CD/32X/Nomad
+  --gen         force recognition
+  --int         force ROM is in interleaved format (SMD)
+  --int2        force ROM is in interleaved format 2 (MGD)
+  --nint        force ROM is not in interleaved format (BIN/RAW)
+   -n=NEW_NAME  change foreign ROM name to NEW_NAME
+  --n2=NEW_NAME change Japanese ROM name to NEW_NAME
+  --smd         convert to Super Magic Drive/SMD
+  --smds        convert emulator (*.srm) SRAM to Super Magic Drive/SMD
+  --bin         convert to Magicom/BIN/RAW
+  --mgd         convert to Multi Game*/MGD2/MGH
+  --stp         convert SRAM from backup unit for use with an emulator
+                  --stp just strips the first 512 bytes
+   -j           join split ROM
+   -s           split ROM; default part size is 8 Mb (4 Mb for SMD)
+  --ssize=SIZE  specify split part size in Mbit
+   -f           remove NTSC/PAL protection
+  --chk         fix ROM checksum
+  --1991        fix old third party ROMs to work with consoles build after
+                  October 1991 by inserting "(C) SEGA" and "(C)SEGA"
+  --multi=SIZE  make multi-game file for use with MD-PRO flash card, truncated
+                  to SIZE Mbit; file with loader must be specified first, then
+                  all the ROMs, multi-game file to create last
+  --region=CODE enable region function; use with -multi
+                  CODE=0 force NTSC/Japan for all games
+                  CODE=1 force NTSC/U.S.A. for all games
+                  CODE=2 force PAL for all games
+                  CODE=x use whatever setting games expect
+Super Com Pro/Super Magic Drive/SMD
+  --xsmd        send/receive ROM to/from Super Magic Drive/SMD; --port=PORT
+                  receives automatically when ROM does not exist
+  --xsmds       send/receive SRAM to/from Super Magic Drive/SMD; --port=PORT
+                  receives automatically when SRAM does not exist
+MD-PRO flash card programmer
+  --xmd         send/receive ROM to/from MD-PRO flash card programmer
+                  --port=PORT
+                  receives automatically (32/64 Mbits) when ROM does not exist
+  --xmds        send/receive SRAM to/from MD-PRO flash card programmer
+                  --port=PORT
+                  receives automatically when SRAM does not exist
+  --xmdb=BANK   send/receive SRAM to/from MD-PRO BANK
+                  BANK can be a number from 1 to 4; --port=PORT
+                  receives automatically when SRAM does not exist
+Mike Pavone's Genesis/Sega CD transfer cable
+  --xmcd        receive ROM from Genesis/Sega CD; --port=PORT
+Cyan's Megadrive ROM copier
+  --xcmc        receive ROM from Cyan's Megadrive ROM copier; --port=PORT
+  --xcmct=TEST  run test TEST
+                  TEST=1 burn-in reliability test (specify speed)
+                  TEST=2 testbench mode (experts only)
+  --xcmcm=SPEED specify transfer speed
+                  SPEED=1 slow (debug)
+                  SPEED=2 medium
+                  SPEED=3 fast (default)
+                  SPEED=4 full speed (risky)
+
+Game Boy/(Super GB)/GB Pocket/Color GB/(GB Advance)
+  --gb          force recognition
+   -n=NEW_NAME  change internal ROM name to NEW_NAME
+  --logo        restore ROM logo character data (offset: 0x104-0x134)
+  --mgd         convert to Multi Game*/MGD2/RAW
+  --ssc         convert to Super Smart Card/SSC
+  --sgb         convert from GB Xchanger/GB/GBC to Super Backup Card/GX/GBX
+  --gbx         convert from Super Backup Card/GX/GBX to GB Xchanger/GB/GBC
+  --n2gb=NESROM KAMI's FC EMUlator (NES emulator);
+                  ROM should be KAMI's FC Emulator ROM image
+                  NESROM should contain 16 kB of PRG data and 8 kB of CHR data
+  --chk         fix ROM checksum
+Game Boy Xchanger/GBDoctor
+  --xgbx        send/receive ROM to/from GB Xchanger; --port=PORT
+                  receives automatically when ROM does not exist
+  --xgbxs       send/receive SRAM to/from GB Xchanger; --port=PORT
+                  receives automatically when SRAM does not exist
+  --xgbxb=BANK  send/receive 64 kbits SRAM to/from GB Xchanger BANK
+                  BANK can be a number from 0 to 15; --port=PORT
+                  receives automatically when ROM does not exist
+  --xgbxm       try to enable EPP mode, default is SPP mode
+Mad Catz Camera Link (Game Boy Camera)
+  --xmccl       receives from Mad Catz Camera Link; --port=PORT
+
+Handy (prototype)/Lynx/Lynx II
+  --lynx        force recognition
+  --lyx         convert to LYX/RAW (strip 64 Bytes LNX header)
+  --lnx         convert to LNX (uses default values for the header);
+                  adjust the LNX header with the following options
+   -n=NEW_NAME  change internal ROM name to NEW_NAME (LNX only)
+  --nrot        set no rotation (LNX only)
+  --rotl        set rotation left (LNX only)
+  --rotr        set rotation right (LNX only)
+  --b0=N        change Bank0 kBytes size to N={0,64,128,256,512} (LNX only)
+  --b1=N        change Bank1 kBytes size to N={0,64,128,256,512} (LNX only)
+Lynxit (Lynx cartridge backup board)
+  --xlit        receive ROM from Lynxit interface; --port=PORT
+
+PC-Engine (CD Unit/Core Grafx(II)/Shuttle/GT/LT/Super CDROM/DUO(-R(X)))
+Super Grafx/Turbo (Grafx(16)/CD/DUO/Express)
+  --pce         force recognition
+  --int         force ROM is in interleaved (bit-swapped) format
+  --nint        force ROM is not in interleaved (bit-swapped) format
+  --msg         convert to Magic Super Griffin/MSG
+  --mgd         convert to Multi Game Doctor*/MGD2/RAW
+  --swap        swap bits of all bytes in file (TurboGrafx-16 &#60;-&#62; PC-Engine)
+   -f           fix region protection
+  --multi=SIZE  make multi-game file for use with PCE-PRO flash card, truncated
+                  to SIZE Mbit; file with loader must be specified first, then
+                  all the ROMs, multi-game file to create last
+Magic Super Griffin/MSG
+  --xmsg        send/receive ROM to/from Magic Super Griffin/MSG; --port=PORT
+                  receives automatically when ROM does not exist
+PCE-PRO flash card programmer
+  --xpce        send/receive ROM to/from PCE-PRO flash card programmer
+                  --port=PORT
+                  receives automatically (32 Mbits) when ROM does not exist
+
+Nintendo Entertainment System/NES/Famicom/Game Axe (Redant)
+  --nes         force recognition
+   -n=NEW_NAME  change internal ROM name to NEW_NAME (UNIF only)
+  --unif        convert to UNIF format/UNF (uses default values)
+  --ines        convert to iNES format/NES (uses default values)
+  --ineshd      extract iNES header from ROM (16 Bytes)
+   -j           join Pasofami/PRM/700/PRG/CHR/split ROM (Pasofami -&#62; iNES)
+  --pasofami    convert to Pasofami/PRM/700/PRG/CHR
+   -s           convert/split to Pasofami/PRM/700/PRG/CHR (iNES -&#62; Pasofami)
+  --ffe         convert to FFE format (Super Magic Card)
+  --mapr=MAPR   specify board name or mapper number for conversion options
+                  MAPR must be a board name for UNIF or a number for Pasofami
+                  and iNES
+  --dint        deinterleave ROM (regardless whether the ROM is interleaved)
+  --ctrl=TYPE   specify controller type (UNIF only)
+                  TYPE=0 regular joypad
+                  TYPE=1 zapper
+                  TYPE=2 R.O.B.
+                  TYPE=3 Arkanoid controller
+                  TYPE=4 powerpad
+                  TYPE=5 four-score adapter
+  --ntsc        specify TV standard is NTSC (UNIF only)
+  --pal         specify TV standard is PAL (UNIF only)
+  --bat         specify battery is present
+  --nbat        specify battery is not present
+  --vram        specify VRAM override (UNIF only)
+  --nvram       specify no VRAM override (UNIF only)
+  --mirr=MTYPE  specify mirroring type
+                  MTYPE=0 horizontal mirroring
+                  MTYPE=1 vertical mirroring
+                  MTYPE=2 mirror all pages from $2000 (UNIF only)
+                  MTYPE=3 mirror all pages from $2400 (UNIF only)
+                  MTYPE=4 four screens of VRAM
+                  MTYPE=5 mirroring controlled by mapper hardware (UNIF only)
+  --cmnt=TEXT   specify that TEXT should be used as comment (UNIF only)
+  --dumpinfo=FILE use dumper info from FILE when converting to UNIF
+  --fds         convert Famicom Disk System file (diskimage) from FAM to FDS
+  --fdsl        list Famicom Disk System/FDS (diskimage) contents
+Super Magic Card
+  --xsmc        send ROM (in FFE format) to Super Magic Card; --port=PORT
+  --xsmcr       send/receive RTS data to/from Super Magic Card; --port=PORT
+                  receives automatically when RTS file does not exist
+
+Sega Master System(II/III)/Game Gear (Handheld)
+  --sms         force recognition
+  --int         force ROM is in interleaved format (SMD)
+  --nint        force ROM is not in interleaved format (RAW)
+  --mgd         convert to Multi Game*/MGD2/MGH/RAW (gives SMS name)
+  --mgdgg       same as --mgd, but gives GG name
+  --smd         convert to Super Magic Drive/SMD
+  --smds        convert emulator (*.srm) SRAM to Super Magic Drive/SMD
+  --chk         fix ROM checksum (SMS only)
+  --multi=SIZE  make multi-game file for use with SMS-PRO/GG-PRO flash card,
+                  truncated to SIZE Mbit; file with loader must be specified
+                  first, then all the ROMs, multi-game file to create last
+SMS-PRO/GG-PRO flash card programmer
+  --xgg         send/receive ROM to/from SMS-PRO/GG-PRO flash card programmer
+                  --port=PORT
+                  receives automatically (32 Mbits) when ROM does not exist
+  --xggs        send/receive SRAM to/from SMS-PRO/GG-PRO flash card programmer
+                  --port=PORT
+                  receives automatically when SRAM does not exist
+  --xggb=BANK   send/receive SRAM to/from SMS-PRO/GG-PRO BANK
+                  BANK can be a number from 1 to 4; --port=PORT
+                  receives automatically when SRAM does not exist
+
+WonderSwan/WonderSwan Color/SwanCrystal
+  --swan        force recognition
+  --chk         fix ROM checksum
+
+Panther(32bit prototype)/Jaguar64/Jaguar64 CD
+  --jag         force recognition
+
+Neo Geo Pocket/Neo Geo Pocket Color
+  --ngp         force recognition
+Pocket Linker
+  --xpl         send/receive ROM to/from Pocket Linker; --port=PORT
+                  receives automatically when ROM does not exist
+  --xpli        show information about inserted cartridge; --port=PORT
+  --xplm        try to enable EPP mode, default is SPP mode
+
+Atari VCS 2600(aka Stella)/Atari 5200 SuperSystem/Atari CX7800/Atari 2600 Jr
+  --ata         force recognition
+
+DATabase: 32626 known ROMs (DAT files: /mnt/hd2-1/home/daniel/.ucon64/dat)
+
+NOTE: You only need to specify PORT if uCON64 doesn't detect the (right)
+      parallel port. If that is the case give a hardware address. For example:
+        ucon64 --xswc "rom.swc" --port=0x378
+      In order to connect a copier to a PC's parallel port you need a standard
+      bidirectional parallel cable
+
+TIP: ucon64 --help --snes (would show only SNES related help)
+     ucon64 --help|less (to see everything in less)
+     Give the force recognition switch a try if something went wrong
+
+Please report any problems/ideas/fixes to noisyb@gmx.net or
+ucon64-announce@lists.sf.net or visit http://ucon64.sf.net
+
+</pre></tt></body></html>

+ 173 - 0
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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
tools/ucon64-2.0.0-src/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

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません