Emulate GB printer timer, letting one emulate the mini game in Hello Kitty Pocket Camera. Closes #290

This commit is contained in:
Lior Halphon 2023-01-30 22:42:03 +02:00
parent c06e320b95
commit d5ff93af3b
7 changed files with 61 additions and 18 deletions

View File

@ -35,6 +35,7 @@
@property (nonatomic, strong) IBOutlet GBPaletteView *paletteView; @property (nonatomic, strong) IBOutlet GBPaletteView *paletteView;
@property (nonatomic, strong) IBOutlet GBObjectView *objectView; @property (nonatomic, strong) IBOutlet GBObjectView *objectView;
@property (nonatomic, strong) IBOutlet NSPanel *printerFeedWindow; @property (nonatomic, strong) IBOutlet NSPanel *printerFeedWindow;
@property (nonatomic, strong) IBOutlet NSProgressIndicator *printerSpinner;
@property (nonatomic, strong) IBOutlet NSImageView *feedImageView; @property (nonatomic, strong) IBOutlet NSImageView *feedImageView;
@property (nonatomic, strong) IBOutlet NSTextView *debuggerSideViewInput; @property (nonatomic, strong) IBOutlet NSTextView *debuggerSideViewInput;
@property (nonatomic, strong) IBOutlet NSTextView *debuggerSideView; @property (nonatomic, strong) IBOutlet NSTextView *debuggerSideView;

View File

@ -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]; [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) static void setWorkboyTime(GB_gameboy_t *gb, time_t t)
{ {
[[NSUserDefaults standardUserDefaults] setInteger:time(NULL) - t forKey:@"GBWorkboyTimeOffset"]; [[NSUserDefaults standardUserDefaults] setInteger:time(NULL) - t forKey:@"GBWorkboyTimeOffset"];
@ -2060,7 +2066,7 @@ static bool is_path_writeable(const char *path)
[self reloadVRAMData: nil]; [self reloadVRAMData: nil];
} }
- (void) printImage:(uint32_t *)imageBytes height:(unsigned) height - (void)printImage:(uint32_t *)imageBytes height:(unsigned) height
topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin
exposure:(unsigned) exposure exposure:(unsigned) exposure
{ {
@ -2073,6 +2079,7 @@ static bool is_path_writeable(const char *path)
[currentPrinterImageData appendBytes:paddedImage length:sizeof(paddedImage)]; [currentPrinterImageData appendBytes:paddedImage length:sizeof(paddedImage)];
/* UI related code must run on main thread. */ /* UI related code must run on main thread. */
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[_printerSpinner startAnimation:nil];
self.feedImageView.image = [Document imageFromData:currentPrinterImageData self.feedImageView.image = [Document imageFromData:currentPrinterImageData
width:160 width:160
height:currentPrinterImageData.length / 160 / sizeof(imageBytes[0]) 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 - (void)printDocument:(id)sender
{ {
if (self.feedImageView.image.size.height == 0) { if (self.feedImageView.image.size.height == 0) {
@ -2135,7 +2149,7 @@ static bool is_path_writeable(const char *path)
[self disconnectLinkCable]; [self disconnectLinkCable];
[self performAtomicBlock:^{ [self performAtomicBlock:^{
accessory = GBAccessoryPrinter; accessory = GBAccessoryPrinter;
GB_connect_printer(&gb, printImage); GB_connect_printer(&gb, printImage, printDone);
}]; }];
} }

View File

@ -35,6 +35,7 @@
<outlet property="osdView" destination="MX4-l2-7NE" id="Am7-fq-uvu"/> <outlet property="osdView" destination="MX4-l2-7NE" id="Am7-fq-uvu"/>
<outlet property="paletteView" destination="ZuP-AU-0pA" id="ef6-27-Bci"/> <outlet property="paletteView" destination="ZuP-AU-0pA" id="ef6-27-Bci"/>
<outlet property="printerFeedWindow" destination="NdE-0B-WCf" id="yVK-cS-NOJ"/> <outlet property="printerFeedWindow" destination="NdE-0B-WCf" id="yVK-cS-NOJ"/>
<outlet property="printerSpinner" destination="rrz-Uh-Nae" id="CI8-Y2-s3l"/>
<outlet property="tilemapImageView" destination="LlK-tV-bjv" id="nSY-Xd-BjZ"/> <outlet property="tilemapImageView" destination="LlK-tV-bjv" id="nSY-Xd-BjZ"/>
<outlet property="tilemapMapButton" destination="YIJ-Qc-SIZ" id="BB7-Gg-7XP"/> <outlet property="tilemapMapButton" destination="YIJ-Qc-SIZ" id="BB7-Gg-7XP"/>
<outlet property="tilemapPaletteButton" destination="loB-0k-Qff" id="2Or-7l-6vn"/> <outlet property="tilemapPaletteButton" destination="loB-0k-Qff" id="2Or-7l-6vn"/>
@ -680,11 +681,19 @@
<toolbarItem implicitItemIdentifier="NSToolbarPrintItem" explicitItemIdentifier="Print" id="mtd-zS-DXa"/> <toolbarItem implicitItemIdentifier="NSToolbarPrintItem" explicitItemIdentifier="Print" id="mtd-zS-DXa"/>
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="AoG-LH-J4b"/> <toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="AoG-LH-J4b"/>
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="Q0x-n5-Q2Y"/> <toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="Q0x-n5-Q2Y"/>
<toolbarItem implicitItemIdentifier="E8F74F8F-6DE2-4774-A690-F71D92CD932E" label="" paletteLabel="" tag="-1" sizingBehavior="auto" id="CJX-Ff-7iQ">
<nil key="toolTip"/>
<progressIndicator key="view" wantsLayer="YES" maxValue="100" displayedWhenStopped="NO" indeterminate="YES" style="spinning" id="rrz-Uh-Nae">
<rect key="frame" x="0.0" y="14" width="32" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</progressIndicator>
</toolbarItem>
</allowedToolbarItems> </allowedToolbarItems>
<defaultToolbarItems> <defaultToolbarItems>
<toolbarItem reference="CBz-1N-o0Q"/> <toolbarItem reference="CBz-1N-o0Q"/>
<toolbarItem reference="Q0x-n5-Q2Y"/>
<toolbarItem reference="mtd-zS-DXa"/> <toolbarItem reference="mtd-zS-DXa"/>
<toolbarItem reference="Q0x-n5-Q2Y"/>
<toolbarItem reference="CJX-Ff-7iQ"/>
</defaultToolbarItems> </defaultToolbarItems>
</toolbar> </toolbar>
<point key="canvasLocation" x="-159" y="356"/> <point key="canvasLocation" x="-159" y="356"/>

View File

@ -710,6 +710,7 @@ struct GB_gameboy_internal_s {
GB_write_memory_callback_t write_memory_callback; GB_write_memory_callback_t write_memory_callback;
GB_boot_rom_load_callback_t boot_rom_load_callback; GB_boot_rom_load_callback_t boot_rom_load_callback;
GB_print_image_callback_t printer_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_set_time_callback workboy_set_time_callback;
GB_workboy_get_time_callback workboy_get_time_callback; GB_workboy_get_time_callback workboy_get_time_callback;
GB_execution_callback_t execution_callback; GB_execution_callback_t execution_callback;

View File

@ -5,7 +5,7 @@
have my own GB Printer to figure it out myself. have my own GB Printer to figure it out myself.
It also does not currently emulate communication timeout, which means that a bug 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. */ 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]; 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) { if (gb->printer_callback) {
gb->printer_callback(gb, image, gb->printer.image_offset / 160, gb->printer_callback(gb, image, gb->printer.image_offset / 160,
gb->printer.command_data[1] >> 4, gb->printer.command_data[1] & 7, 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; gb->printer.byte_to_send = 0;
switch (gb->printer.command_state) { 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; gb->printer.byte_to_send = 0;
} }
else { else {
if (gb->printer.status == 6 && gb->printer.time_remaining == 0) {
gb->printer.status = 4; /* Done */
}
gb->printer.byte_to_send = gb->printer.status; gb->printer.byte_to_send = gb->printer.status;
} }
break; break;
case GB_PRINTER_COMMAND_STATUS: 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; gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
handle_command(gb); handle_command(gb);
return; 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.byte_being_received |= bit_received;
gb->printer.bits_received++; gb->printer.bits_received++;
if (gb->printer.bits_received == 8) { 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.bits_received = 0;
gb->printer.byte_being_received = 0; gb->printer.byte_being_received = 0;
} }
@ -211,10 +211,11 @@ static bool serial_end(GB_gameboy_t *gb)
return ret; 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)); memset(&gb->printer, 0, sizeof(gb->printer));
GB_set_serial_transfer_bit_start_callback(gb, serial_start); GB_set_serial_transfer_bit_start_callback(gb, serial_start);
GB_set_serial_transfer_bit_end_callback(gb, serial_end); GB_set_serial_transfer_bit_end_callback(gb, serial_end);
gb->printer_callback = callback; gb->printer_callback = callback;
gb->printer_done_callback = done_callback;
} }

View File

@ -13,6 +13,8 @@ typedef void (*GB_print_image_callback_t)(GB_gameboy_t *gb,
uint8_t bottom_margin, uint8_t bottom_margin,
uint8_t exposure); uint8_t exposure);
typedef void (*GB_printer_done_callback_t)(GB_gameboy_t *gb);
typedef struct typedef struct
{ {
@ -56,8 +58,9 @@ typedef struct
uint8_t bits_received; uint8_t bits_received;
uint8_t byte_being_received; uint8_t byte_being_received;
bool bit_to_send; bool bit_to_send;
uint64_t time_remaining;
} GB_printer_t; } 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 #endif

View File

@ -165,8 +165,22 @@ static void increase_tima(GB_gameboy_t *gb)
void GB_serial_master_edge(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))) { if (gb->printer_callback) {
gb->printer.idle_time += 1 << gb->serial_mask; 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; gb->serial_master_clock ^= true;