diff --git a/Cocoa/Document.h b/Cocoa/Document.h
index eaf285d..2dd5e0c 100644
--- a/Cocoa/Document.h
+++ b/Cocoa/Document.h
@@ -35,6 +35,7 @@
@property (nonatomic, strong) IBOutlet GBPaletteView *paletteView;
@property (nonatomic, strong) IBOutlet GBObjectView *objectView;
@property (nonatomic, strong) IBOutlet NSPanel *printerFeedWindow;
+@property (nonatomic, strong) IBOutlet NSProgressIndicator *printerSpinner;
@property (nonatomic, strong) IBOutlet NSImageView *feedImageView;
@property (nonatomic, strong) IBOutlet NSTextView *debuggerSideViewInput;
@property (nonatomic, strong) IBOutlet NSTextView *debuggerSideView;
diff --git a/Cocoa/Document.m b/Cocoa/Document.m
index 4ee7ed0..ca7eeb0 100644
--- a/Cocoa/Document.m
+++ b/Cocoa/Document.m
@@ -180,6 +180,12 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
[self printImage:image height:height topMargin:top_margin bottomMargin:bottom_margin exposure:exposure];
}
+static void printDone(GB_gameboy_t *gb)
+{
+ Document *self = (__bridge Document *)GB_get_user_data(gb);
+ [self printDone];
+}
+
static void setWorkboyTime(GB_gameboy_t *gb, time_t t)
{
[[NSUserDefaults standardUserDefaults] setInteger:time(NULL) - t forKey:@"GBWorkboyTimeOffset"];
@@ -2060,9 +2066,9 @@ static bool is_path_writeable(const char *path)
[self reloadVRAMData: nil];
}
-- (void) printImage:(uint32_t *)imageBytes height:(unsigned) height
- topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin
- exposure:(unsigned) exposure
+- (void)printImage:(uint32_t *)imageBytes height:(unsigned) height
+ topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin
+ exposure:(unsigned) exposure
{
uint32_t paddedImage[160 * (topMargin + height + bottomMargin)];
memset(paddedImage, 0xFF, sizeof(paddedImage));
@@ -2073,6 +2079,7 @@ static bool is_path_writeable(const char *path)
[currentPrinterImageData appendBytes:paddedImage length:sizeof(paddedImage)];
/* UI related code must run on main thread. */
dispatch_async(dispatch_get_main_queue(), ^{
+ [_printerSpinner startAnimation:nil];
self.feedImageView.image = [Document imageFromData:currentPrinterImageData
width:160
height:currentPrinterImageData.length / 160 / sizeof(imageBytes[0])
@@ -2087,6 +2094,13 @@ static bool is_path_writeable(const char *path)
}
+- (void)printDone
+{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [_printerSpinner stopAnimation:nil];
+ });
+}
+
- (void)printDocument:(id)sender
{
if (self.feedImageView.image.size.height == 0) {
@@ -2135,7 +2149,7 @@ static bool is_path_writeable(const char *path)
[self disconnectLinkCable];
[self performAtomicBlock:^{
accessory = GBAccessoryPrinter;
- GB_connect_printer(&gb, printImage);
+ GB_connect_printer(&gb, printImage, printDone);
}];
}
diff --git a/Cocoa/Document.xib b/Cocoa/Document.xib
index bbdaf7e..8cc6fe0 100644
--- a/Cocoa/Document.xib
+++ b/Cocoa/Document.xib
@@ -35,6 +35,7 @@
+
@@ -680,11 +681,19 @@
+
+
+
+
+
+
+
-
+
+
diff --git a/Core/gb.h b/Core/gb.h
index 3e8d915..34e2291 100644
--- a/Core/gb.h
+++ b/Core/gb.h
@@ -710,6 +710,7 @@ struct GB_gameboy_internal_s {
GB_write_memory_callback_t write_memory_callback;
GB_boot_rom_load_callback_t boot_rom_load_callback;
GB_print_image_callback_t printer_callback;
+ GB_printer_done_callback_t printer_done_callback;
GB_workboy_set_time_callback workboy_set_time_callback;
GB_workboy_get_time_callback workboy_get_time_callback;
GB_execution_callback_t execution_callback;
diff --git a/Core/printer.c b/Core/printer.c
index 1394a6a..dfd6f08 100644
--- a/Core/printer.c
+++ b/Core/printer.c
@@ -5,7 +5,7 @@
have my own GB Printer to figure it out myself.
It also does not currently emulate communication timeout, which means that a bug
- might prevent the printer operation until the GameBoy is restarted.
+ might prevent the printer operation until the Game Boy is restarted.
Also, field mask values are assumed. */
@@ -30,6 +30,9 @@ static void handle_command(GB_gameboy_t *gb)
image[i] = colors[(palette >> (gb->printer.image[i] * 2)) & 3];
}
+ // One second per 8-pixel row
+ gb->printer.time_remaining = gb->printer.image_offset / 160 * GB_get_unmultiplied_clock_rate(gb) / 256 / 8;
+
if (gb->printer_callback) {
gb->printer_callback(gb, image, gb->printer.image_offset / 160,
gb->printer.command_data[1] >> 4, gb->printer.command_data[1] & 7,
@@ -70,7 +73,7 @@ static void handle_command(GB_gameboy_t *gb)
}
-static void byte_recieve_completed(GB_gameboy_t *gb, uint8_t byte_received)
+static void byte_receive_completed(GB_gameboy_t *gb, uint8_t byte_received)
{
gb->printer.byte_to_send = 0;
switch (gb->printer.command_state) {
@@ -156,16 +159,13 @@ static void byte_recieve_completed(GB_gameboy_t *gb, uint8_t byte_received)
gb->printer.byte_to_send = 0;
}
else {
+ if (gb->printer.status == 6 && gb->printer.time_remaining == 0) {
+ gb->printer.status = 4; /* Done */
+ }
gb->printer.byte_to_send = gb->printer.status;
}
break;
case GB_PRINTER_COMMAND_STATUS:
-
- /* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */
- if (gb->printer.status == 6) {
- gb->printer.status = 4; /* Done */
- }
-
gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
handle_command(gb);
return;
@@ -197,7 +197,7 @@ static void serial_start(GB_gameboy_t *gb, bool bit_received)
gb->printer.byte_being_received |= bit_received;
gb->printer.bits_received++;
if (gb->printer.bits_received == 8) {
- byte_recieve_completed(gb, gb->printer.byte_being_received);
+ byte_receive_completed(gb, gb->printer.byte_being_received);
gb->printer.bits_received = 0;
gb->printer.byte_being_received = 0;
}
@@ -211,10 +211,11 @@ static bool serial_end(GB_gameboy_t *gb)
return ret;
}
-void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback)
+void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback, GB_printer_done_callback_t done_callback)
{
memset(&gb->printer, 0, sizeof(gb->printer));
GB_set_serial_transfer_bit_start_callback(gb, serial_start);
GB_set_serial_transfer_bit_end_callback(gb, serial_end);
gb->printer_callback = callback;
+ gb->printer_done_callback = done_callback;
}
diff --git a/Core/printer.h b/Core/printer.h
index f4ccfe4..5216ea7 100644
--- a/Core/printer.h
+++ b/Core/printer.h
@@ -13,6 +13,8 @@ typedef void (*GB_print_image_callback_t)(GB_gameboy_t *gb,
uint8_t bottom_margin,
uint8_t exposure);
+typedef void (*GB_printer_done_callback_t)(GB_gameboy_t *gb);
+
typedef struct
{
@@ -56,8 +58,9 @@ typedef struct
uint8_t bits_received;
uint8_t byte_being_received;
bool bit_to_send;
+ uint64_t time_remaining;
} GB_printer_t;
-void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback);
+void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback, GB_printer_done_callback_t done_callback);
#endif
diff --git a/Core/timing.c b/Core/timing.c
index a0609a9..7d2ca94 100644
--- a/Core/timing.c
+++ b/Core/timing.c
@@ -165,8 +165,22 @@ static void increase_tima(GB_gameboy_t *gb)
void GB_serial_master_edge(GB_gameboy_t *gb)
{
- if (unlikely(gb->printer_callback && (gb->printer.command_state || gb->printer.bits_received))) {
- gb->printer.idle_time += 1 << gb->serial_mask;
+ if (gb->printer_callback) {
+ unsigned ticks = 1 << gb->serial_mask;
+ if (unlikely((gb->printer.command_state || gb->printer.bits_received))) {
+ gb->printer.idle_time +=ticks;
+ }
+ if (unlikely(gb->printer.time_remaining)) {
+ if (gb->printer.time_remaining <= ticks) {
+ gb->printer.time_remaining = 0;
+ if (gb->printer_done_callback) {
+ gb->printer_done_callback(gb);
+ }
+ }
+ else {
+ gb->printer.time_remaining -= ticks;
+ }
+ }
}
gb->serial_master_clock ^= true;