Strip irrelevant features

This commit is contained in:
Lior Halphon 2020-11-15 21:43:07 +02:00
parent dcb59d4cd4
commit 6610180717
27 changed files with 73 additions and 3752 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 B

View File

@ -1,2 +0,0 @@
AGB EQU 1
include "cgb_boot.asm"

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
FAST EQU 1
include "cgb_boot.asm"

View File

@ -1,177 +0,0 @@
; SameBoy DMG bootstrap ROM
; Todo: use friendly names for HW registers instead of magic numbers
SECTION "BootCode", ROM0[$0]
Start:
; Init stack pointer
ld sp, $fffe
; Clear memory VRAM
ld hl, $8000
.clearVRAMLoop
ldi [hl], a
bit 5, h
jr z, .clearVRAMLoop
; Init Audio
ld a, $80
ldh [$26], a
ldh [$11], a
ld a, $f3
ldh [$12], a
ldh [$25], a
ld a, $77
ldh [$24], a
; Init BG palette
ld a, $54
ldh [$47], a
; Load logo from ROM.
; A nibble represents a 4-pixels line, 2 bytes represent a 4x4 tile, scaled to 8x8.
; Tiles are ordered left to right, top to bottom.
ld de, $104 ; Logo start
ld hl, $8010 ; This is where we load the tiles in VRAM
.loadLogoLoop
ld a, [de] ; Read 2 rows
ld b, a
call DoubleBitsAndWriteRow
call DoubleBitsAndWriteRow
inc de
ld a, e
xor $34 ; End of logo
jr nz, .loadLogoLoop
; Load trademark symbol
ld de, TrademarkSymbol
ld c,$08
.loadTrademarkSymbolLoop:
ld a,[de]
inc de
ldi [hl],a
inc hl
dec c
jr nz, .loadTrademarkSymbolLoop
; Set up tilemap
ld a,$19 ; Trademark symbol
ld [$9910], a ; ... put in the superscript position
ld hl,$992f ; Bottom right corner of the logo
ld c,$c ; Tiles in a logo row
.tilemapLoop
dec a
jr z, .tilemapDone
ldd [hl], a
dec c
jr nz, .tilemapLoop
ld l,$0f ; Jump to top row
jr .tilemapLoop
.tilemapDone
ld a, 30
ldh [$ff42], a
; Turn on LCD
ld a, $91
ldh [$40], a
ld d, (-119) & $FF
ld c, 15
.animate
call WaitFrame
ld a, d
sra a
sra a
ldh [$ff42], a
ld a, d
add c
ld d, a
ld a, c
cp 8
jr nz, .noPaletteChange
ld a, $A8
ldh [$47], a
.noPaletteChange
dec c
jr nz, .animate
ld a, $fc
ldh [$47], a
; Play first sound
ld a, $83
call PlaySound
ld b, 5
call WaitBFrames
; Play second sound
ld a, $c1
call PlaySound
; Wait ~1 second
ld b, 60
call WaitBFrames
; Set registers to match the original DMG boot
ld hl, $01B0
push hl
pop af
ld hl, $014D
ld bc, $0013
ld de, $00D8
; Boot the game
jp BootGame
DoubleBitsAndWriteRow:
; Double the most significant 4 bits, b is shifted by 4
ld a, 4
ld c, 0
.doubleCurrentBit
sla b
push af
rl c
pop af
rl c
dec a
jr nz, .doubleCurrentBit
ld a, c
; Write as two rows
ldi [hl], a
inc hl
ldi [hl], a
inc hl
ret
WaitFrame:
push hl
ld hl, $FF0F
res 0, [hl]
.wait
bit 0, [hl]
jr z, .wait
pop hl
ret
WaitBFrames:
call WaitFrame
dec b
jr nz, WaitBFrames
ret
PlaySound:
ldh [$13], a
ld a, $87
ldh [$14], a
ret
TrademarkSymbol:
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
SECTION "BootGame", ROM0[$fe]
BootGame:
ldh [$50], a

View File

@ -1,102 +0,0 @@
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
void opts(uint8_t byte, uint8_t *options)
{
*(options++) = byte | ((byte << 1) & 0xff);
*(options++) = byte & (byte << 1);
*(options++) = byte | ((byte >> 1) & 0xff);
*(options++) = byte & (byte >> 1);
}
void write_all(int fd, const void *buf, size_t count) {
while (count) {
ssize_t written = write(fd, buf, count);
if (written < 0) {
fprintf(stderr, "write");
exit(1);
}
count -= written;
buf += written;
}
}
int main()
{
static uint8_t source[0x4000];
size_t size = read(STDIN_FILENO, &source, sizeof(source));
unsigned pos = 0;
assert(size <= 0x4000);
while (size && source[size - 1] == 0) {
size--;
}
uint8_t literals[8];
size_t literals_size = 0;
unsigned bits = 0;
unsigned control = 0;
unsigned prev[2] = {-1, -1}; // Unsigned to allow "not set" values
while (true) {
uint8_t byte = 0;
if (pos == size){
if (bits == 0) break;
}
else {
byte = source[pos++];
}
if (byte == prev[0] || byte == prev[1]) {
bits += 2;
control <<= 1;
control |= 1;
control <<= 1;
if (byte == prev[1]) {
control |= 1;
}
}
else {
bits += 2;
control <<= 2;
uint8_t options[4];
opts(prev[1], options);
bool found = false;
for (unsigned i = 0; i < 4; i++) {
if (options[i] == byte) {
// 01 = modify
control |= 1;
bits += 2;
control <<= 2;
control |= i;
found = true;
break;
}
}
if (!found) {
literals[literals_size++] = byte;
}
}
prev[0] = prev[1];
prev[1] = byte;
if (bits >= 8) {
uint8_t outctl = control >> (bits - 8);
assert(outctl != 1);
write_all(STDOUT_FILENO, &outctl, 1);
write_all(STDOUT_FILENO, literals, literals_size);
bits -= 8;
control &= (1 << bits) - 1;
literals_size = 0;
}
}
uint8_t end_byte = 1;
write_all(STDOUT_FILENO, &end_byte, 1);
return 0;
}

View File

@ -1,2 +0,0 @@
SGB2 EQU 1
include "sgb_boot.asm"

View File

@ -1,213 +0,0 @@
; SameBoy SGB bootstrap ROM
; Todo: use friendly names for HW registers instead of magic numbers
SECTION "BootCode", ROM0[$0]
Start:
; Init stack pointer
ld sp, $fffe
; Clear memory VRAM
ld hl, $8000
.clearVRAMLoop
ldi [hl], a
bit 5, h
jr z, .clearVRAMLoop
; Init Audio
ld a, $80
ldh [$26], a
ldh [$11], a
ld a, $f3
ldh [$12], a
ldh [$25], a
ld a, $77
ldh [$24], a
; Init BG palette to white
ld a, $0
ldh [$47], a
; Load logo from ROM.
; A nibble represents a 4-pixels line, 2 bytes represent a 4x4 tile, scaled to 8x8.
; Tiles are ordered left to right, top to bottom.
ld de, $104 ; Logo start
ld hl, $8010 ; This is where we load the tiles in VRAM
.loadLogoLoop
ld a, [de] ; Read 2 rows
ld b, a
call DoubleBitsAndWriteRow
call DoubleBitsAndWriteRow
inc de
ld a, e
xor $34 ; End of logo
jr nz, .loadLogoLoop
; Load trademark symbol
ld de, TrademarkSymbol
ld c,$08
.loadTrademarkSymbolLoop:
ld a,[de]
inc de
ldi [hl],a
inc hl
dec c
jr nz, .loadTrademarkSymbolLoop
; Set up tilemap
ld a,$19 ; Trademark symbol
ld [$9910], a ; ... put in the superscript position
ld hl,$992f ; Bottom right corner of the logo
ld c,$c ; Tiles in a logo row
.tilemapLoop
dec a
jr z, .tilemapDone
ldd [hl], a
dec c
jr nz, .tilemapLoop
ld l,$0f ; Jump to top row
jr .tilemapLoop
.tilemapDone
; Turn on LCD
ld a, $91
ldh [$40], a
ld a, $f1 ; Packet magic, increases by 2 for every packet
ldh [$80], a
ld hl, $104 ; Header start
xor a
ld c, a ; JOYP
.sendCommand
xor a
ld [c], a
ld a, $30
ld [c], a
ldh a, [$80]
call SendByte
push hl
ld b, $e
ld d, 0
.checksumLoop
call ReadHeaderByte
add d
ld d, a
dec b
jr nz, .checksumLoop
; Send checksum
call SendByte
pop hl
ld b, $e
.sendLoop
call ReadHeaderByte
call SendByte
dec b
jr nz, .sendLoop
; Done bit
ld a, $20
ld [c], a
ld a, $30
ld [c], a
; Update command
ldh a, [$80]
add 2
ldh [$80], a
ld a, $58
cp l
jr nz, .sendCommand
; Write to sound registers for DMG compatibility
ld c, $13
ld a, $c1
ld [c], a
inc c
ld a, 7
ld [c], a
; Init BG palette
ld a, $fc
ldh [$47], a
; Set registers to match the original SGB boot
IF DEF(SGB2)
ld a, $FF
ELSE
ld a, 1
ENDC
ld hl, $c060
; Boot the game
jp BootGame
ReadHeaderByte:
ld a, $4F
cp l
jr c, .zero
ld a, [hli]
ret
.zero:
inc hl
xor a
ret
SendByte:
ld e, a
ld d, 8
.loop
ld a, $10
rr e
jr c, .zeroBit
add a ; 10 -> 20
.zeroBit
ld [c], a
ld a, $30
ld [c], a
dec d
ret z
jr .loop
DoubleBitsAndWriteRow:
; Double the most significant 4 bits, b is shifted by 4
ld a, 4
ld c, 0
.doubleCurrentBit
sla b
push af
rl c
pop af
rl c
dec a
jr nz, .doubleCurrentBit
ld a, c
; Write as two rows
ldi [hl], a
inc hl
ldi [hl], a
inc hl
ret
WaitFrame:
push hl
ld hl, $FF0F
res 0, [hl]
.wait
bit 0, [hl]
jr z, .wait
pop hl
ret
TrademarkSymbol:
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
SECTION "BootGame", ROM0[$fe]
BootGame:
ldh [$50], a

View File

@ -546,17 +546,6 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
- (void) loadBootROM: (GB_boot_rom_t)type
{
static NSString *const names[] = {
[GB_BOOT_ROM_DMG0] = @"dmg0_boot",
[GB_BOOT_ROM_DMG] = @"dmg_boot",
[GB_BOOT_ROM_MGB] = @"mgb_boot",
[GB_BOOT_ROM_SGB] = @"sgb_boot",
[GB_BOOT_ROM_SGB2] = @"sgb2_boot",
[GB_BOOT_ROM_CGB0] = @"cgb0_boot",
[GB_BOOT_ROM_CGB] = @"cgb_boot",
[GB_BOOT_ROM_AGB] = @"agb_boot",
};
GB_load_boot_rom(&gb, [[self bootROMPathForName:names[type]] UTF8String]);
}
- (IBAction)reset:(id)sender

View File

@ -25,14 +25,11 @@
<outlet property="memoryBankItem" destination="bWC-FW-IYP" id="Lf2-dh-z32"/>
<outlet property="memoryView" destination="8hr-8o-3rN" id="fF0-rh-8ND"/>
<outlet property="memoryWindow" destination="mRm-dL-mCj" id="VPR-lu-vtI"/>
<outlet property="paletteTableView" destination="gfC-d3-dmq" id="fTC-eL-Qg3"/>
<outlet property="printerFeedWindow" destination="NdE-0B-WCf" id="yVK-cS-NOJ"/>
<outlet property="spritesTableView" destination="TOc-XJ-w9w" id="O4R-4Z-9hU"/>
<outlet property="tilemapImageView" destination="LlK-tV-bjv" id="nSY-Xd-BjZ"/>
<outlet property="tilemapMapButton" destination="YIJ-Qc-SIZ" id="BB7-Gg-7XP"/>
<outlet property="tilemapPaletteButton" destination="loB-0k-Qff" id="2Or-7l-6vn"/>
<outlet property="tilesetImageView" destination="QcQ-ex-36R" id="8SY-Ag-eIf"/>
<outlet property="tilesetPaletteButton" destination="TLv-xS-X5K" id="uE2-K1-5sj"/>
<outlet property="view" destination="uqf-pe-VAF" id="HMP-rf-Yqk"/>
<outlet property="vramStatusLabel" destination="6vK-IP-PmP" id="A2d-dI-R3J"/>
<outlet property="vramTabView" destination="AZz-Mh-rPA" id="ZCG-SP-Lfw"/>
@ -250,7 +247,7 @@
<popUpButton key="view" verticalHuggingPriority="750" id="vfJ-vu-gqJ">
<rect key="frame" x="0.0" y="14" width="128" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bpD-j9-omo">
<popUpButtonCell key="cell" type="roundTextured" title="Entire Space" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" selectedItem="Zp5-J9-dd3" id="bpD-j9-omo">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="gTX-6Z-mOH">
@ -258,7 +255,6 @@
<menuItem title="Entire Space" id="Zp5-J9-dd3"/>
<menuItem title="ROM" tag="1" id="ANn-pq-5SA"/>
<menuItem title="Video RAM" tag="2" id="m35-GY-cKE"/>
<menuItem title="Cartridge RAM" tag="3" id="AOc-Up-PNm"/>
<menuItem title="RAM" tag="4" id="dg4-zf-bo4"/>
</items>
</menu>
@ -341,7 +337,7 @@
<font key="font" metaFont="system"/>
<tabViewItems>
<tabViewItem label="Tileset" identifier="1" id="pXb-od-Wb1">
<view key="view" ambiguous="YES" id="lCG-Gt-XMF">
<view key="view" id="lCG-Gt-XMF">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
@ -354,38 +350,6 @@
<outlet property="delegate" destination="-2" id="xoo-Uo-872"/>
</connections>
</imageView>
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TLv-xS-X5K">
<rect key="frame" x="4" y="388" width="128" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundRect" title="None" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="G8p-CH-PlV" id="1jI-s4-4YY">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="miniSystem"/>
<menu key="menu" id="S6C-6Y-U3e">
<items>
<menuItem title="None" state="on" id="G8p-CH-PlV"/>
<menuItem title="Background Palette 0" id="Vf5-1r-wB0"/>
<menuItem title="Background Palette 1" id="Hnm-oM-jIq"/>
<menuItem title="Background Palette 2" id="IBU-wD-IfI"/>
<menuItem title="Background Palette 3" id="Pje-be-VHT"/>
<menuItem title="Background Palette 4" id="E1K-4g-hvR"/>
<menuItem title="Background Palette 5" id="juQ-bJ-lIt"/>
<menuItem title="Background Palette 6" id="T1z-Jc-wIg"/>
<menuItem title="Background Palette 7" id="oxC-qV-3EG"/>
<menuItem title="Object Palette 0" id="nnZ-qm-fjY"/>
<menuItem title="Object Palette 1" id="B9u-1l-0j9"/>
<menuItem title="Object Palette 2" id="XVN-y3-Wp5"/>
<menuItem title="Object Palette 3" id="bDp-1h-ShO"/>
<menuItem title="Object Palette 4" id="Idr-Th-IVi"/>
<menuItem title="Object Palette 5" id="NRU-gL-bIa"/>
<menuItem title="Object Palette 6" id="HtN-zW-2PB"/>
<menuItem title="Object Palette 7" id="HWA-Tl-Egd"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="reloadVRAMData:" target="-2" id="Qtf-p4-Rqh"/>
</connections>
</popUpButton>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fL6-2S-Rgd">
<rect key="frame" x="452" y="388" width="56" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
@ -425,41 +389,8 @@
<outlet property="delegate" destination="-2" id="EAG-Zh-oRi"/>
</connections>
</imageView>
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="loB-0k-Qff">
<rect key="frame" x="4" y="388" width="128" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundRect" title="Effective Palettes" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="oUH-Sa-Ec1" id="Eij-Cp-URa">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="miniSystem"/>
<menu key="menu" id="KYk-85-hF3">
<items>
<menuItem title="None" id="BhZ-lk-ned"/>
<menuItem title="Effective Palettes" state="on" id="oUH-Sa-Ec1"/>
<menuItem title="Background Palette 0" id="q5g-HD-mPF"/>
<menuItem title="Background Palette 1" id="1qY-bS-27N"/>
<menuItem title="Background Palette 2" id="4JV-8v-9q6"/>
<menuItem title="Background Palette 3" id="58P-TR-RjD"/>
<menuItem title="Background Palette 4" id="voq-Pf-ZKh"/>
<menuItem title="Background Palette 5" id="e7w-HT-Q5N"/>
<menuItem title="Background Palette 6" id="Tgx-oy-4YI"/>
<menuItem title="Background Palette 7" id="3lv-Bj-iqf"/>
<menuItem title="Object Palette 0" id="z29-eH-Mq8"/>
<menuItem title="Object Palette 1" id="2Ef-30-IRL"/>
<menuItem title="Object Palette 2" id="Rrs-nT-Zpe"/>
<menuItem title="Object Palette 3" id="C1P-ei-elS"/>
<menuItem title="Object Palette 4" id="DeX-Ey-45n"/>
<menuItem title="Object Palette 5" id="luQ-S6-lpg"/>
<menuItem title="Object Palette 6" id="pWP-64-0lY"/>
<menuItem title="Object Palette 7" id="qGs-73-oac"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="reloadVRAMData:" target="-2" id="BmB-JE-W8g"/>
</connections>
</popUpButton>
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YIJ-Qc-SIZ">
<rect key="frame" x="135" y="388" width="96" height="17"/>
<rect key="frame" x="4" y="388" width="96" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundRect" title="Effective Tilemap" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="XRF-Vj-3gs" id="3W1-Db-wDn">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@ -477,7 +408,7 @@
</connections>
</popUpButton>
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k4c-Vg-MBu">
<rect key="frame" x="235" y="388" width="96" height="17"/>
<rect key="frame" x="104" y="388" width="96" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundRect" title="Effective Tileset" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="CRe-dX-rzY" id="h53-sb-Odg">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@ -498,7 +429,7 @@
</view>
</tabViewItem>
<tabViewItem label="Sprites" identifier="" id="a08-eg-Maw">
<view key="view" id="EiO-p0-3xn">
<view key="view" ambiguous="YES" id="EiO-p0-3xn">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
@ -507,7 +438,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="3VT-AA-xVT">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="18" headerView="of1-KC-dXC" id="TOc-XJ-w9w">
<rect key="frame" x="0.0" y="0.0" width="512" height="391"/>
@ -641,111 +572,6 @@
</subviews>
</view>
</tabViewItem>
<tabViewItem label="Palettes" identifier="" id="vLb-Nh-UYE">
<view key="view" id="qIg-ci-WTA">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="iSs-Ow-wwb">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" ambiguous="YES" id="bP9-su-zQw">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="18" id="gfC-d3-dmq">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<tableViewGridLines key="gridStyleMask" dashed="YES"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="125" minWidth="40" maxWidth="1000" id="Aje-FQ-fUw">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" title="Text Cell" id="Ygb-xo-X8v">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="93" minWidth="40" maxWidth="1000" id="4EN-0j-lda">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" title="Text Cell" id="3a2-A4-XQW" customClass="GBColorCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="syl-os-nSf">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" alignment="left" title="Text Cell" id="3ql-bn-AxP" customClass="GBColorCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="Qw3-u2-c1s">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" title="Text Cell" id="9at-mH-PWc" customClass="GBColorCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="gTl-gN-qLn">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" alignment="left" title="Text Cell" id="dY8-Zu-d4t" customClass="GBColorCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="-2" id="F2f-nK-QTN"/>
<outlet property="delegate" destination="-2" id="7hn-MM-DDD"/>
</connections>
</tableView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="OS3-sw-bjv">
<rect key="frame" x="-100" y="-100" width="510" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="4HA-2m-8TZ">
<rect key="frame" x="-100" y="-100" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
</view>
</tabViewItem>
</tabViewItems>
</tabView>
</subviews>
@ -755,10 +581,10 @@
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="hnI-48-dYt"/>
<toolbarItem implicitItemIdentifier="B9D3CF98-8020-4389-9372-F99361440794" label="" paletteLabel="" id="fmK-Jl-Koj">
<nil key="toolTip"/>
<size key="minSize" width="100" height="25"/>
<size key="maxSize" width="268" height="25"/>
<size key="minSize" width="100" height="24"/>
<size key="maxSize" width="213" height="25"/>
<segmentedControl key="view" verticalHuggingPriority="750" id="Aul-vO-dCK">
<rect key="frame" x="0.0" y="14" width="268" height="25"/>
<rect key="frame" x="0.0" y="14" width="213" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="texturedSquare" trackingMode="selectOne" id="HhR-ky-5NN">
<font key="font" metaFont="system"/>
@ -766,7 +592,6 @@
<segment label="Tileset" selected="YES"/>
<segment label="Tilemap" tag="1"/>
<segment label="Sprites"/>
<segment label="Palettes"/>
</segments>
</segmentedCell>
</segmentedControl>
@ -978,7 +803,7 @@
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" heightSizable="YES"/>
<clipView key="contentView" id="mzf-yu-RID">
<rect key="frame" x="1" y="0.0" width="398" height="274"/>
<autoresizingMask key="autoresizingMask"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" headerView="pvX-uJ-qK5" id="tA3-8T-bxb">
<rect key="frame" x="0.0" y="0.0" width="398" height="249"/>

View File

@ -316,31 +316,6 @@
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="5GS-tt-E0a"/>
<menuItem title="Game Boy" tag="1" id="g7C-LA-VAr">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="rxG-cz-s1S"/>
</connections>
</menuItem>
<menuItem title="Super Game Boy" tag="4" id="vc7-yy-ARW">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="E4M-QG-ua9"/>
</connections>
</menuItem>
<menuItem title="Game Boy Color" tag="2" id="hdG-Bl-8nJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="xAz-cr-0u2"/>
</connections>
</menuItem>
<menuItem title="Game Boy Advance" tag="3" id="7jw-B1-tf5">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="xQk-4e-kd7"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="DPb-Sh-5tg"/>
<menuItem title="Mute Sound" keyEquivalent="m" id="1UK-8n-QPP">
<connections>
<action selector="mute:" target="-1" id="YE5-mi-Yzd"/>
@ -377,9 +352,9 @@
<action selector="disconnectAllAccessories:" target="-1" id="5hY-9U-nRn"/>
</connections>
</menuItem>
<menuItem title="Game Link Cable &amp; Infrared" id="V4S-Fo-xJK">
<menuItem title="Game Link Cable" id="V4S-Fo-xJK">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Game Link Cable &amp; Infrared" id="6sJ-Wz-QLj">
<menu key="submenu" title="Game Link Cable" id="6sJ-Wz-QLj">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="PMY-5j-25T"/>
</connections>
@ -394,12 +369,6 @@
<action selector="connectPrinter:" target="-1" id="tl1-CL-tAw"/>
</connections>
</menuItem>
<menuItem title="Workboy" id="lo9-CX-BJj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="connectWorkboy:" target="-1" id="6vS-bq-wAX"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>

View File

@ -60,16 +60,10 @@
<connections>
<outlet property="analogControlsCheckbox" destination="RuW-Db-dzW" id="FRE-hI-mnU"/>
<outlet property="aspectRatioCheckbox" destination="Vfj-tg-7OP" id="Yw0-xS-DBr"/>
<outlet property="bootROMsButton" destination="T3Y-Ln-Onl" id="tdL-Yv-E2K"/>
<outlet property="bootROMsFolderItem" destination="Dzv-Gc-zoL" id="yhV-ZI-avD"/>
<outlet property="cgbPopupButton" destination="dlD-sk-SHO" id="4tg-SR-e17"/>
<outlet property="colorCorrectionPopupButton" destination="VEz-N4-uP6" id="EO2-Vt-JFJ"/>
<outlet property="colorPalettePopupButton" destination="Iwr-eI-SD1" id="Xzc-RZ-JtV"/>
<outlet property="configureJoypadButton" destination="Qa7-Z7-yfO" id="RaX-P3-oCX"/>
<outlet property="controlsTableView" destination="UDd-IJ-fxX" id="a1D-Md-yXv"/>
<outlet property="delegate" destination="-2" id="ASc-vN-Zbq"/>
<outlet property="displayBorderPopupButton" destination="R9D-FV-bpd" id="VfO-b4-gqH"/>
<outlet property="dmgPopupButton" destination="LFw-Uk-cPR" id="KDw-i2-k4u"/>
<outlet property="frameBlendingModePopupButton" destination="lxk-db-Sxv" id="wzt-uo-TE6"/>
<outlet property="graphicsFilterPopupButton" destination="6pP-kK-EEC" id="LS7-HY-kHC"/>
<outlet property="highpassFilterPopupButton" destination="T69-6N-dhT" id="0p6-4m-hb1"/>
@ -77,17 +71,16 @@
<outlet property="preferredJoypadButton" destination="0Az-0R-oNw" id="7JM-tw-BhK"/>
<outlet property="rewindPopupButton" destination="7fg-Ww-JjR" id="Ka2-TP-B1x"/>
<outlet property="rumbleModePopupButton" destination="Ogs-xG-b4b" id="vuw-VN-MTp"/>
<outlet property="sgbPopupButton" destination="dza-T7-RkX" id="B0o-Nb-pIH"/>
<outlet property="skipButton" destination="d2I-jU-sLb" id="udX-8K-0sK"/>
</connections>
<point key="canvasLocation" x="183" y="354"/>
</window>
<customView id="sRK-wO-K6R">
<rect key="frame" x="0.0" y="0.0" width="292" height="323"/>
<rect key="frame" x="0.0" y="0.0" width="292" height="215"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T91-rh-rRp">
<rect key="frame" x="18" y="286" width="256" height="17"/>
<rect key="frame" x="18" y="178" width="256" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Graphics filter:" id="pXg-WY-8Q5">
<font key="font" metaFont="system"/>
@ -96,7 +89,7 @@
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6pP-kK-EEC">
<rect key="frame" x="30" y="254" width="234" height="26"/>
<rect key="frame" x="30" y="146" width="234" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Nearest neighbor (Pixelated)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="neN-eo-LA7" id="I1w-05-lGl">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -132,39 +125,8 @@
<action selector="graphicFilterChanged:" target="QvC-M9-y7g" id="n87-t4-fbV"/>
</connections>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wc3-2K-6CD">
<rect key="frame" x="18" y="232" width="256" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color correction:" id="5Si-hz-EK3">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VEz-N4-uP6">
<rect key="frame" x="30" y="200" width="234" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="D2J-wV-1vu" id="fNJ-Fi-yOm">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="6go-Lb-a4m">
<items>
<menuItem title="Disabled" state="on" id="D2J-wV-1vu">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Correct color curves" id="B3Q-x1-VRl"/>
<menuItem title="Emulate hardware" id="XBL-hS-7ke"/>
<menuItem title="Preserve brightness" id="tlx-WB-Bkw"/>
<menuItem title="Reduce contrast" id="wuO-Xv-0mQ"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="colorCorrectionChanged:" target="QvC-M9-y7g" id="Oq4-B5-nO6"/>
</connections>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MLC-Rx-FgO">
<rect key="frame" x="20" y="178" width="252" height="17"/>
<rect key="frame" x="20" y="124" width="252" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Frame blending" id="UCa-EO-tzh">
<font key="font" metaFont="system"/>
@ -173,7 +135,7 @@
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lxk-db-Sxv">
<rect key="frame" x="32" y="149" width="229" height="22"/>
<rect key="frame" x="32" y="95" width="229" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="iHP-Yz-fiH" id="aQ6-HN-7Aj">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -193,16 +155,16 @@
</connections>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8fG-zm-hpr">
<rect key="frame" x="18" y="122" width="252" height="17"/>
<rect key="frame" x="18" y="68" width="252" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color palette for monochrome models:" id="LAN-8Y-T7H">
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color palette:" id="LAN-8Y-T7H">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Iwr-eI-SD1">
<rect key="frame" x="30" y="93" width="234" height="22"/>
<rect key="frame" x="30" y="39" width="234" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Greyscale" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Ajr-5r-iIk" id="rEU-jh-m3j">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -222,37 +184,8 @@
<action selector="colorPaletteChanged:" target="QvC-M9-y7g" id="ui3-rg-PTs"/>
</connections>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3Kz-cf-5X6">
<rect key="frame" x="18" y="71" width="248" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Display border:" id="HZd-qi-yyk">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="R9D-FV-bpd">
<rect key="frame" x="30" y="39" width="234" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Never" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="heL-AV-0az" id="DY9-2D-h1L">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="Rgf-mF-K9q">
<items>
<menuItem title="Never" state="on" tag="1" id="heL-AV-0az">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Super Game Boy only" id="bBJ-Vn-5rk"/>
<menuItem title="Always" tag="2" id="JUs-gW-qcM"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="displayBorderChanged:" target="QvC-M9-y7g" id="GoA-BU-v3h"/>
</connections>
</popUpButton>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vfj-tg-7OP">
<rect key="frame" x="18" y="18" width="256" height="18"/>
<rect key="frame" x="20" y="18" width="256" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Keep aspect ratio" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="lsj-rC-Eo6">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -263,14 +196,14 @@
</connections>
</button>
</subviews>
<point key="canvasLocation" x="-176" y="667.5"/>
<point key="canvasLocation" x="-176" y="613.5"/>
</customView>
<customView id="ymk-46-SX7">
<rect key="frame" x="0.0" y="0.0" width="292" height="320"/>
<rect key="frame" x="0.0" y="0.0" width="292" height="86"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7fg-Ww-JjR">
<rect key="frame" x="30" y="193" width="234" height="26"/>
<rect key="frame" x="30" y="17" width="234" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="lxQ-4n-kEv" id="lvb-QF-0Ht">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -294,7 +227,7 @@
</connections>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="w9w-yX-KxB">
<rect key="frame" x="18" y="225" width="256" height="17"/>
<rect key="frame" x="18" y="49" width="256" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Rewinding duration:" id="JaO-5h-ugl">
<font key="font" metaFont="system"/>
@ -302,130 +235,8 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MI2-ql-f6M">
<rect key="frame" x="18" y="283" width="256" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Boot ROMs location:" id="nj0-Cb-gEA">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wC4-aJ-mhQ">
<rect key="frame" x="30" y="251" width="234" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Use built-in boot ROMs" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Tnm-SR-ZEm" id="T3Y-Ln-Onl">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="edo-AN-gRh">
<items>
<menuItem title="Use built-in boot ROMs" state="on" id="Tnm-SR-ZEm">
<connections>
<action selector="useBuiltinBootROMs:" target="QvC-M9-y7g" id="Kmo-wz-ZtB"/>
</connections>
</menuItem>
<menuItem id="Dzv-Gc-zoL"/>
<menuItem isSeparatorItem="YES" id="BqG-uI-xqE"/>
<menuItem title="Other..." id="ikb-cd-hZe">
<connections>
<action selector="selectOtherBootROMFolder:" target="QvC-M9-y7g" id="ofn-ll-0bo"/>
</connections>
</menuItem>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wg8-hJ-df9">
<rect key="frame" x="18" y="157" width="256" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Game Boy revision:" id="GIA-ep-SBi">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LFw-Uk-cPR">
<rect key="frame" x="30" y="125" width="234" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="DMG-CPU B" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="2" imageScaling="proportionallyDown" inset="2" autoenablesItems="NO" selectedItem="aXT-sE-m5Z" id="FuX-Hc-uO7">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" autoenablesItems="NO" id="R0h-k6-Q5l">
<items>
<menuItem title="DMG-CPU 0" enabled="NO" id="pvp-Mo-9S8"/>
<menuItem title="DMG-CPU A" tag="1" enabled="NO" id="QLn-5i-Vlg"/>
<menuItem title="DMG-CPU B" state="on" tag="2" id="aXT-sE-m5Z"/>
<menuItem title="DMG-CPU C" tag="3" enabled="NO" id="Ecl-YM-oAA"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="dmgModelChanged:" target="QvC-M9-y7g" id="FIe-Wz-aSc"/>
</connections>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MAq-1X-Gpo">
<rect key="frame" x="18" y="49" width="256" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Game Boy Color revision:" id="edD-t7-vwk">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dlD-sk-SHO">
<rect key="frame" x="30" y="17" width="234" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="CPU-CGB E" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="517" imageScaling="proportionallyDown" inset="2" autoenablesItems="NO" selectedItem="3lF-1Q-2SS" id="Edt-1S-dXz">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" autoenablesItems="NO" id="bbF-hB-Hv7">
<items>
<menuItem title="CPU-CGB 0" tag="512" enabled="NO" id="2Uk-u3-6Gw"/>
<menuItem title="CPU-CGB A" tag="513" enabled="NO" id="axv-yk-RWM"/>
<menuItem title="CPU-CGB B" tag="514" enabled="NO" id="NtJ-oo-IM2"/>
<menuItem title="CPU-CGB C (Experimental)" tag="515" id="9YL-u8-12z"/>
<menuItem title="CPU-CGB D" tag="516" enabled="NO" id="c76-oF-fkU"/>
<menuItem title="CPU-CGB E" state="on" tag="517" id="3lF-1Q-2SS"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="cgbModelChanged:" target="QvC-M9-y7g" id="SY1-oj-VWQ"/>
</connections>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tAa-0A-0fP">
<rect key="frame" x="18" y="103" width="256" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Super Game Boy model:" id="d0g-rk-FK0">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="mdm-eW-ia1">
<rect key="frame" x="12" y="180" width="268" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dza-T7-RkX">
<rect key="frame" x="30" y="71" width="234" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Super Game Boy (NTSC)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="4" imageScaling="proportionallyDown" inset="2" autoenablesItems="NO" selectedItem="x5A-7f-ef9" id="2Mt-ci-bB0">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" autoenablesItems="NO" id="czw-Hi-jyM">
<items>
<menuItem title="Super Game Boy (NTSC)" state="on" tag="4" id="x5A-7f-ef9"/>
<menuItem title="Super Game Boy (PAL)" tag="4100" id="Cix-n2-l4L"/>
<menuItem title="Super Game Boy 2" tag="257" id="gZG-1g-KF0"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="sgbModelChanged:" target="QvC-M9-y7g" id="ybk-EV-WPS"/>
</connections>
</popUpButton>
</subviews>
<point key="canvasLocation" x="-176" y="848"/>
<point key="canvasLocation" x="-176" y="731"/>
</customView>
<customView id="Zn1-Y5-RbR">
<rect key="frame" x="0.0" y="0.0" width="292" height="86"/>
@ -477,9 +288,9 @@
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DZu-ts-deW">
<rect key="frame" x="18" y="87" width="105" height="17"/>
<rect key="frame" x="18" y="87" width="114" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Enable rumble:" id="QMX-3p-s1Z">
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Controller rumble:" id="QMX-3p-s1Z">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
@ -603,14 +414,13 @@
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ogs-xG-b4b">
<rect key="frame" x="30" y="58" width="245" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Never" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="jki-7x-bnM" id="o9b-MH-8kd">
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="jki-7x-bnM" id="o9b-MH-8kd">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="8p7-je-0Fh">
<items>
<menuItem title="Never" state="on" id="jki-7x-bnM"/>
<menuItem title="For rumble-enabled Game Paks" tag="1" id="58e-Tp-TWd"/>
<menuItem title="Always" tag="2" id="qVe-2b-W1P"/>
<menuItem title="Disabled" state="on" id="jki-7x-bnM"/>
<menuItem title="Enabled" tag="2" id="qVe-2b-W1P"/>
</items>
</menu>
</popUpButtonCell>

View File

@ -33,7 +33,6 @@ static uint16_t bank_for_addr(GB_gameboy_t *gb, uint16_t addr)
void GB_apply_cheat(GB_gameboy_t *gb, uint16_t address, uint8_t *value)
{
if (!gb->cheat_enabled) return;
if (!gb->boot_rom_finished) return;
const GB_cheat_hash_t *hash = gb->cheat_hash[hash_addr(address)];
if (hash) {
for (unsigned i = 0; i < hash->size; i++) {

View File

@ -115,11 +115,6 @@ static void display_vblank(GB_gameboy_t *gb)
{
gb->vblank_just_occured = true;
/* TODO: Slow in turbo mode! */
if (GB_is_hle_sgb(gb)) {
GB_sgb_render(gb);
}
if (gb->turbo) {
if (GB_timing_sync_turbo(gb)) {
return;
@ -155,51 +150,6 @@ static void display_vblank(GB_gameboy_t *gb)
}
}
if (gb->border_mode == GB_BORDER_ALWAYS && !GB_is_sgb(gb)) {
GB_borrow_sgb_border(gb);
uint32_t border_colors[16 * 4];
if (!gb->has_sgb_border && GB_is_cgb(gb) && gb->model != GB_MODEL_AGB) {
static uint16_t colors[] = {
0x2095, 0x5129, 0x1EAF, 0x1EBA, 0x4648,
0x30DA, 0x69AD, 0x2B57, 0x2B5D, 0x632C,
0x1050, 0x3C84, 0x0E07, 0x0E18, 0x2964,
};
unsigned index = gb->rom? gb->rom[0x14e] % 5 : 0;
gb->borrowed_border.palette[0] = colors[index];
gb->borrowed_border.palette[10] = colors[5 + index];
gb->borrowed_border.palette[14] = colors[10 + index];
}
for (unsigned i = 0; i < 16 * 4; i++) {
border_colors[i] = GB_convert_rgb15(gb, gb->borrowed_border.palette[i], true);
}
for (unsigned tile_y = 0; tile_y < 28; tile_y++) {
for (unsigned tile_x = 0; tile_x < 32; tile_x++) {
if (tile_x >= 6 && tile_x < 26 && tile_y >= 5 && tile_y < 23) {
continue;
}
uint16_t tile = gb->borrowed_border.map[tile_x + tile_y * 32];
uint8_t flip_x = (tile & 0x4000)? 0x7 : 0;
uint8_t flip_y = (tile & 0x8000)? 0x7 : 0;
uint8_t palette = (tile >> 10) & 3;
for (unsigned y = 0; y < 8; y++) {
for (unsigned x = 0; x < 8; x++) {
uint8_t color = gb->borrowed_border.tiles[(tile & 0xFF) * 64 + (x ^ flip_x) + (y ^ flip_y) * 8] & 0xF;
uint32_t *output = gb->screen + tile_x * 8 + x + (tile_y * 8 + y) * 256;
if (color == 0) {
*output = border_colors[0];
}
else {
*output = border_colors[color + palette * 16];
}
}
}
}
}
}
GB_handle_rumble(gb);
if (gb->vblank_callback) {
@ -489,17 +439,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
if (!gb->cgb_mode) {
pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3);
}
if (gb->sgb) {
if (gb->current_lcd_line < LINES) {
gb->sgb->screen_buffer[gb->lcd_x + gb->current_lcd_line * WIDTH] = gb->stopped? 0 : pixel;
}
}
else if (gb->model & GB_MODEL_NO_SFC_BIT) {
if (gb->icd_pixel_callback) {
icd_pixel = pixel;
}
}
else if (gb->cgb_palettes_ppu_blocked) {
if (gb->cgb_palettes_ppu_blocked) {
*dest = gb->rgb_encode_callback(gb, 0, 0, 0);
}
else {
@ -513,18 +453,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
/* Todo: Verify access timings */
pixel = ((gb->io_registers[oam_fifo_item->palette? GB_IO_OBP1 : GB_IO_OBP0] >> (pixel << 1)) & 3);
}
if (gb->sgb) {
if (gb->current_lcd_line < LINES) {
gb->sgb->screen_buffer[gb->lcd_x + gb->current_lcd_line * WIDTH] = gb->stopped? 0 : pixel;
}
}
else if (gb->model & GB_MODEL_NO_SFC_BIT) {
if (gb->icd_pixel_callback) {
icd_pixel = pixel;
//gb->icd_pixel_callback(gb, pixel);
}
}
else if (gb->cgb_palettes_ppu_blocked) {
if (gb->cgb_palettes_ppu_blocked) {
*dest = gb->rgb_encode_callback(gb, 0, 0, 0);
}
else {
@ -1304,25 +1233,10 @@ abort_fetching_object:
void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index)
{
uint32_t none_palette[4];
uint32_t *palette = NULL;
switch (GB_is_cgb(gb)? palette_type : GB_PALETTE_NONE) {
default:
case GB_PALETTE_NONE:
none_palette[0] = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
none_palette[1] = gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA);
none_palette[2] = gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55);
none_palette[3] = gb->rgb_encode_callback(gb, 0, 0, 0 );
palette = none_palette;
break;
case GB_PALETTE_BACKGROUND:
palette = gb->background_palettes_rgb + (4 * (palette_index & 7));
break;
case GB_PALETTE_OAM:
palette = gb->sprite_palettes_rgb + (4 * (palette_index & 7));
break;
}
palette = gb->background_palettes_rgb;
for (unsigned y = 0; y < 192; y++) {
for (unsigned x = 0; x < 256; x++) {
@ -1354,28 +1268,9 @@ void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette
void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type)
{
uint32_t none_palette[4];
uint32_t *palette = NULL;
uint32_t *palette = gb->background_palettes_rgb;
uint16_t map = 0x1800;
switch (GB_is_cgb(gb)? palette_type : GB_PALETTE_NONE) {
case GB_PALETTE_NONE:
none_palette[0] = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
none_palette[1] = gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA);
none_palette[2] = gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55);
none_palette[3] = gb->rgb_encode_callback(gb, 0, 0, 0 );
palette = none_palette;
break;
case GB_PALETTE_BACKGROUND:
palette = gb->background_palettes_rgb + (4 * (palette_index & 7));
break;
case GB_PALETTE_OAM:
palette = gb->sprite_palettes_rgb + (4 * (palette_index & 7));
break;
case GB_PALETTE_AUTO:
break;
}
if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && gb->io_registers[GB_IO_LCDC] & 0x04)) {
map = 0x1c00;
}

176
Core/gb.c
View File

@ -108,44 +108,9 @@ static char *default_async_input_callback(GB_gameboy_t *gb)
}
#endif
static void load_default_border(GB_gameboy_t *gb)
{
if (gb->has_sgb_border) return;
#define LOAD_BORDER() do { \
memcpy(gb->borrowed_border.map, tilemap, sizeof(tilemap));\
memcpy(gb->borrowed_border.palette, palette, sizeof(palette));\
\
/* Expand tileset */\
for (unsigned tile = 0; tile < sizeof(tiles) / 32; tile++) {\
for (unsigned y = 0; y < 8; y++) {\
for (unsigned x = 0; x < 8; x++) {\
gb->borrowed_border.tiles[tile * 8 * 8 + y * 8 + x] =\
(tiles[tile * 32 + y * 2 + 0] & (1 << (7 ^ x)) ? 1 : 0) |\
(tiles[tile * 32 + y * 2 + 1] & (1 << (7 ^ x)) ? 2 : 0) |\
(tiles[tile * 32 + y * 2 + 16] & (1 << (7 ^ x)) ? 4 : 0) |\
(tiles[tile * 32 + y * 2 + 17] & (1 << (7 ^ x)) ? 8 : 0);\
}\
}\
}\
} while (false);
if (gb->model == GB_MODEL_AGB) {
#include "graphics/agb_border.inc"
LOAD_BORDER();
}
else if (GB_is_cgb(gb)) {
#include "graphics/cgb_border.inc"
LOAD_BORDER();
}
else {
#include "graphics/dmg_border.inc"
LOAD_BORDER();
}
}
void GB_init(GB_gameboy_t *gb, GB_model_t model)
{
model = GB_MODEL_DMG_B;
memset(gb, 0, sizeof(*gb));
gb->model = model;
if (GB_is_cgb(gb)) {
@ -169,7 +134,6 @@ void GB_init(GB_gameboy_t *gb, GB_model_t model)
}
GB_reset(gb);
load_default_border(gb);
}
GB_model_t GB_get_model(GB_gameboy_t *gb)
@ -192,9 +156,6 @@ void GB_free(GB_gameboy_t *gb)
if (gb->breakpoints) {
free(gb->breakpoints);
}
if (gb->sgb) {
free(gb->sgb);
}
if (gb->nontrivial_jump_state) {
free(gb->nontrivial_jump_state);
}
@ -212,62 +173,11 @@ void GB_free(GB_gameboy_t *gb)
int GB_load_boot_rom(GB_gameboy_t *gb, const char *path)
{
FILE *f = fopen(path, "rb");
if (!f) {
GB_log(gb, "Could not open boot ROM: %s.\n", strerror(errno));
return errno;
}
fread(gb->boot_rom, sizeof(gb->boot_rom), 1, f);
fclose(f);
return 0;
}
void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size)
{
if (size > sizeof(gb->boot_rom)) {
size = sizeof(gb->boot_rom);
}
memset(gb->boot_rom, 0xFF, sizeof(gb->boot_rom));
memcpy(gb->boot_rom, buffer, size);
}
void GB_borrow_sgb_border(GB_gameboy_t *gb)
{
if (GB_is_sgb(gb)) return;
if (gb->border_mode != GB_BORDER_ALWAYS) return;
if (gb->tried_loading_sgb_border) return;
gb->tried_loading_sgb_border = true;
if (gb->rom && gb->rom[0x146] != 3) return; // Not an SGB game, nothing to borrow
if (!gb->boot_rom_load_callback) return; // Can't borrow a border without this callback
GB_gameboy_t sgb;
GB_init(&sgb, GB_MODEL_SGB);
sgb.rom = gb->rom;
sgb.rom_size = gb->rom_size;
sgb.turbo = true;
sgb.turbo_dont_skip = true;
// sgb.disable_rendering = true;
/* Load the boot ROM using the existing gb object */
typeof(gb->boot_rom) boot_rom_backup;
memcpy(boot_rom_backup, gb->boot_rom, sizeof(gb->boot_rom));
gb->boot_rom_load_callback(gb, GB_BOOT_ROM_SGB);
memcpy(sgb.boot_rom, gb->boot_rom, sizeof(gb->boot_rom));
memcpy(gb->boot_rom, boot_rom_backup, sizeof(gb->boot_rom));
sgb.sgb->intro_animation = -1;
for (unsigned i = 600; i--;) {
GB_run_frame(&sgb);
if (sgb.sgb->border_animation) {
gb->has_sgb_border = true;
memcpy(&gb->borrowed_border, &sgb.sgb->pending_border, sizeof(gb->borrowed_border));
gb->borrowed_border.palette[0] = sgb.sgb->effective_palettes[0];
break;
}
}
sgb.rom = NULL;
sgb.rom_size = 0;
GB_free(&sgb);
}
int GB_load_rom(GB_gameboy_t *gb, const char *path)
@ -579,18 +489,6 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path)
uint8_t GB_run(GB_gameboy_t *gb)
{
gb->vblank_just_occured = false;
if (gb->sgb && gb->sgb->intro_animation < 140) {
/* On the SGB, the GB is halted after finishing the boot ROM.
Then, after the boot animation is almost done, it's reset.
Since the SGB HLE does not perform any header validity checks,
we just halt the CPU (with hacky code) until the correct time.
This ensures the Nintendo logo doesn't flash on screen, and
the game does "run in background" while the animation is playing. */
GB_display_run(gb, 228);
gb->cycles_since_last_sync += 228;
return 228;
}
GB_debugger_run(gb);
gb->cycles_since_run = 0;
@ -772,21 +670,6 @@ bool GB_is_inited(GB_gameboy_t *gb)
return gb->magic == state_magic();
}
bool GB_is_cgb(GB_gameboy_t *gb)
{
return (gb->model & GB_MODEL_FAMILY_MASK) == GB_MODEL_CGB_FAMILY;
}
bool GB_is_sgb(GB_gameboy_t *gb)
{
return (gb->model & ~GB_MODEL_PAL_BIT & ~GB_MODEL_NO_SFC_BIT) == GB_MODEL_SGB || (gb->model & ~GB_MODEL_NO_SFC_BIT) == GB_MODEL_SGB2;
}
bool GB_is_hle_sgb(GB_gameboy_t *gb)
{
return (gb->model & ~GB_MODEL_PAL_BIT) == GB_MODEL_SGB || gb->model == GB_MODEL_SGB2;
}
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip)
{
gb->turbo = on;
@ -962,32 +845,7 @@ static void reset_ram(GB_gameboy_t *gb)
static void request_boot_rom(GB_gameboy_t *gb)
{
if (gb->boot_rom_load_callback) {
GB_boot_rom_t type = 0;
switch (gb->model) {
case GB_MODEL_DMG_B:
type = GB_BOOT_ROM_DMG;
break;
case GB_MODEL_SGB_NTSC:
case GB_MODEL_SGB_PAL:
case GB_MODEL_SGB_NTSC_NO_SFC:
case GB_MODEL_SGB_PAL_NO_SFC:
type = GB_BOOT_ROM_SGB;
break;
case GB_MODEL_SGB2:
case GB_MODEL_SGB2_NO_SFC:
type = GB_BOOT_ROM_SGB2;
break;
case GB_MODEL_CGB_C:
case GB_MODEL_CGB_E:
type = GB_BOOT_ROM_CGB;
break;
case GB_MODEL_AGB:
type = GB_BOOT_ROM_AGB;
break;
}
gb->boot_rom_load_callback(gb, type);
}
}
void GB_reset(GB_gameboy_t *gb)
@ -1027,28 +885,6 @@ void GB_reset(GB_gameboy_t *gb)
gb->accessed_oam_row = -1;
if (GB_is_hle_sgb(gb)) {
if (!gb->sgb) {
gb->sgb = malloc(sizeof(*gb->sgb));
}
memset(gb->sgb, 0, sizeof(*gb->sgb));
memset(gb->sgb_intro_jingle_phases, 0, sizeof(gb->sgb_intro_jingle_phases));
gb->sgb_intro_sweep_phase = 0;
gb->sgb_intro_sweep_previous_sample = 0;
gb->sgb->intro_animation = -10;
gb->sgb->player_count = 1;
GB_sgb_load_default_data(gb);
}
else {
if (gb->sgb) {
free(gb->sgb);
gb->sgb = NULL;
}
}
/* Todo: Ugly, fixme, see comment in the timer state machine */
gb->div_state = 3;
@ -1065,6 +901,7 @@ void GB_reset(GB_gameboy_t *gb)
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model)
{
model = GB_MODEL_DMG_B;
gb->model = model;
if (GB_is_cgb(gb)) {
gb->ram = realloc(gb->ram, gb->ram_size = 0x1000 * 8);
@ -1076,7 +913,6 @@ void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model)
}
GB_rewind_free(gb);
GB_reset(gb);
load_default_border(gb);
}
void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank)
@ -1119,9 +955,9 @@ void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *
*bank = 0;
return &gb->io_registers;
case GB_DIRECT_ACCESS_BOOTROM:
*size = GB_is_cgb(gb)? sizeof(gb->boot_rom) : 0x100;
*size = 0;
*bank = 0;
return &gb->boot_rom;
return NULL;
case GB_DIRECT_ACCESS_OAM:
*size = sizeof(gb->oam);
*bank = 0;
@ -1196,7 +1032,7 @@ unsigned GB_get_screen_height(GB_gameboy_t *gb)
unsigned GB_get_player_count(GB_gameboy_t *gb)
{
return GB_is_hle_sgb(gb)? gb->sgb->player_count : 1;
return 1;
}
void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback)

View File

@ -19,7 +19,6 @@
#include "rewind.h"
#include "sm83_cpu.h"
#include "symbol_hash.h"
#include "sgb.h"
#include "cheats.h"
#include "rumble.h"
#include "workboy.h"
@ -348,7 +347,6 @@ struct GB_gameboy_internal_s {
bool cgb_double_speed;
bool halted;
bool stopped;
bool boot_rom_finished;
bool ime_toggle; /* ei has delayed a effect.*/
bool halt_bug;
bool just_halted;
@ -553,7 +551,6 @@ struct GB_gameboy_internal_s {
GB_color_correction_mode_t color_correction_mode;
bool keys[4][GB_KEY_MAX];
GB_border_mode_t border_mode;
GB_sgb_border_t borrowed_border;
bool tried_loading_sgb_border;
bool has_sgb_border;
@ -639,10 +636,7 @@ struct GB_gameboy_internal_s {
unsigned pos;
} *rewind_sequences; // lasts about 4 seconds
size_t rewind_pos;
/* SGB - saved and allocated optionally */
GB_sgb_t *sgb;
double sgb_intro_jingle_phases[7];
double sgb_intro_sweep_phase;
double sgb_intro_sweep_previous_sample;
@ -657,7 +651,6 @@ struct GB_gameboy_internal_s {
bool turbo;
bool turbo_dont_skip;
bool disable_rendering;
uint8_t boot_rom[0x900];
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units
double clock_multiplier;
@ -667,6 +660,9 @@ struct GB_gameboy_internal_s {
/* Temporary state */
bool wx_just_changed;
/* For SmaeBoy-SameDuck source camptibility*/
const void *sgb;
);
};
@ -685,9 +681,9 @@ __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
void GB_init(GB_gameboy_t *gb, GB_model_t model);
bool GB_is_inited(GB_gameboy_t *gb);
bool GB_is_cgb(GB_gameboy_t *gb);
bool GB_is_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2
bool GB_is_hle_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 and the SFC/SNES side is HLE'd
#define GB_is_cgb(x) false
#define GB_is_sgb(x) false
#define GB_is_hle_sgb(x) false
GB_model_t GB_get_model(GB_gameboy_t *gb);
void GB_free(GB_gameboy_t *gb);
void GB_reset(GB_gameboy_t *gb);

View File

@ -12,16 +12,11 @@ void GB_update_joyp(GB_gameboy_t *gb)
previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3;
gb->io_registers[GB_IO_JOYP] &= 0xF0;
uint8_t current_player = gb->sgb? (gb->sgb->current_player & (gb->sgb->player_count - 1) & 3) : 0;
uint8_t current_player = 0;
switch (key_selection) {
case 3:
if (gb->sgb && gb->sgb->player_count > 1) {
gb->io_registers[GB_IO_JOYP] |= 0xF - current_player;
}
else {
/* Nothing is wired, all up */
gb->io_registers[GB_IO_JOYP] |= 0x0F;
}
/* Nothing is wired, all up */
gb->io_registers[GB_IO_JOYP] |= 0x0F;
break;
case 2:

View File

@ -338,7 +338,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
case GB_IO_BGPD:
case GB_IO_OBPD:
{
if (!gb->cgb_mode && gb->boot_rom_finished) {
if (!gb->cgb_mode) {
return 0xFF;
}
if (gb->cgb_palettes_blocked) {
@ -580,7 +580,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
gb->io_registers[addr & 0xFF] = value;
return;
case GB_IO_OPRI:
if ((!gb->boot_rom_finished || (gb->io_registers[GB_IO_KEY0] & 8)) && GB_is_cgb(gb)) {
if (((gb->io_registers[GB_IO_KEY0] & 8)) && GB_is_cgb(gb)) {
gb->io_registers[addr & 0xFF] = value;
gb->object_priority = (value & 1) ? GB_OBJECT_PRIORITY_X : GB_OBJECT_PRIORITY_INDEX;
}
@ -695,18 +695,16 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
GB_update_joyp(gb);
}
else if ((gb->io_registers[GB_IO_JOYP] & 0x30) != (value & 0x30)) {
GB_sgb_write(gb, value);
gb->io_registers[GB_IO_JOYP] = (value & 0xF0) | (gb->io_registers[GB_IO_JOYP] & 0x0F);
GB_update_joyp(gb);
}
return;
case GB_IO_BANK:
gb->boot_rom_finished = true;
return;
case GB_IO_KEY0:
if (GB_is_cgb(gb) && !gb->boot_rom_finished) {
if (GB_is_cgb(gb)) {
gb->cgb_mode = !(value & 0xC); /* The real "contents" of this register aren't quite known yet. */
gb->io_registers[GB_IO_KEY0] = value;
}
@ -751,7 +749,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
return;
case GB_IO_BGPD:
case GB_IO_OBPD:
if (!gb->cgb_mode && gb->boot_rom_finished) {
if (!gb->cgb_mode) {
/* Todo: Due to the behavior of a broken Game & Watch Gallery 2 ROM on a real CGB. A proper test ROM
is required. */
return;

View File

@ -36,10 +36,6 @@ int GB_save_state(GB_gameboy_t *gb, const char *path)
if (!DUMP_SECTION(gb, f, rtc )) goto error;
if (!DUMP_SECTION(gb, f, video )) goto error;
if (GB_is_hle_sgb(gb)) {
if (!dump_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
}
if (fwrite(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
goto error;
}
@ -68,7 +64,6 @@ size_t GB_get_save_state_size(GB_gameboy_t *gb)
+ GB_SECTION_SIZE(apu ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(rtc ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(video ) + sizeof(uint32_t)
+ (GB_is_hle_sgb(gb)? sizeof(*gb->sgb) + sizeof(uint32_t) : 0)
+ gb->ram_size
+ gb->vram_size;
}
@ -99,10 +94,6 @@ void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer)
DUMP_SECTION(gb, buffer, rtc );
DUMP_SECTION(gb, buffer, video );
if (GB_is_hle_sgb(gb)) {
buffer_dump_section(&buffer, gb->sgb, sizeof(*gb->sgb));
}
buffer_write(gb->ram, gb->ram_size, &buffer);
buffer_write(gb->vram, gb->vram_size, &buffer);
@ -250,10 +241,6 @@ int GB_load_state(GB_gameboy_t *gb, const char *path)
errno = -1;
goto error;
}
if (GB_is_hle_sgb(gb)) {
if (!read_section(f, gb->sgb, sizeof(*gb->sgb), false)) goto error;
}
if (fread(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
fclose(f);
@ -364,10 +351,6 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le
if (!verify_and_update_state_compatibility(gb, &save)) {
return -1;
}
if (GB_is_hle_sgb(gb)) {
if (!buffer_read_section(&buffer, &length, gb->sgb, sizeof(*gb->sgb), false)) return -1;
}
if (buffer_read(gb->ram, gb->ram_size, &buffer, &length) != gb->ram_size) {
return -1;

View File

@ -1,913 +0,0 @@
#include "gb.h"
#include "random.h"
#include <math.h>
#include <assert.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define INTRO_ANIMATION_LENGTH 200
enum {
PAL01 = 0x00,
PAL23 = 0x01,
PAL03 = 0x02,
PAL12 = 0x03,
ATTR_BLK = 0x04,
ATTR_LIN = 0x05,
ATTR_DIV = 0x06,
ATTR_CHR = 0x07,
PAL_SET = 0x0A,
PAL_TRN = 0x0B,
DATA_SND = 0x0F,
MLT_REQ = 0x11,
CHR_TRN = 0x13,
PCT_TRN = 0x14,
ATTR_TRN = 0x15,
ATTR_SET = 0x16,
MASK_EN = 0x17,
};
typedef enum {
MASK_DISABLED,
MASK_FREEZE,
MASK_BLACK,
MASK_COLOR_0,
} mask_mode_t;
typedef enum {
TRANSFER_LOW_TILES,
TRANSFER_HIGH_TILES,
TRANSFER_BORDER_DATA,
TRANSFER_PALETTES,
TRANSFER_ATTRIBUTES,
} transfer_dest_t;
#define SGB_PACKET_SIZE 16
static inline void pal_command(GB_gameboy_t *gb, unsigned first, unsigned second)
{
gb->sgb->effective_palettes[0] = gb->sgb->effective_palettes[4] =
gb->sgb->effective_palettes[8] = gb->sgb->effective_palettes[12] =
gb->sgb->command[1] | (gb->sgb->command[2] << 8);
for (unsigned i = 0; i < 3; i++) {
gb->sgb->effective_palettes[first * 4 + i + 1] = gb->sgb->command[3 + i * 2] | (gb->sgb->command[4 + i * 2] << 8);
}
for (unsigned i = 0; i < 3; i++) {
gb->sgb->effective_palettes[second * 4 + i + 1] = gb->sgb->command[9 + i * 2] | (gb->sgb->command[10 + i * 2] << 8);
}
}
static inline void load_attribute_file(GB_gameboy_t *gb, unsigned file_index)
{
if (file_index > 0x2C) return;
uint8_t *output = gb->sgb->attribute_map;
for (unsigned i = 0; i < 90; i++) {
uint8_t byte = gb->sgb->attribute_files[file_index * 90 + i];
for (unsigned j = 4; j--;) {
*(output++) = byte >> 6;
byte <<= 2;
}
}
}
static const uint16_t built_in_palettes[] =
{
0x67BF, 0x265B, 0x10B5, 0x2866,
0x637B, 0x3AD9, 0x0956, 0x0000,
0x7F1F, 0x2A7D, 0x30F3, 0x4CE7,
0x57FF, 0x2618, 0x001F, 0x006A,
0x5B7F, 0x3F0F, 0x222D, 0x10EB,
0x7FBB, 0x2A3C, 0x0015, 0x0900,
0x2800, 0x7680, 0x01EF, 0x2FFF,
0x73BF, 0x46FF, 0x0110, 0x0066,
0x533E, 0x2638, 0x01E5, 0x0000,
0x7FFF, 0x2BBF, 0x00DF, 0x2C0A,
0x7F1F, 0x463D, 0x74CF, 0x4CA5,
0x53FF, 0x03E0, 0x00DF, 0x2800,
0x433F, 0x72D2, 0x3045, 0x0822,
0x7FFA, 0x2A5F, 0x0014, 0x0003,
0x1EED, 0x215C, 0x42FC, 0x0060,
0x7FFF, 0x5EF7, 0x39CE, 0x0000,
0x4F5F, 0x630E, 0x159F, 0x3126,
0x637B, 0x121C, 0x0140, 0x0840,
0x66BC, 0x3FFF, 0x7EE0, 0x2C84,
0x5FFE, 0x3EBC, 0x0321, 0x0000,
0x63FF, 0x36DC, 0x11F6, 0x392A,
0x65EF, 0x7DBF, 0x035F, 0x2108,
0x2B6C, 0x7FFF, 0x1CD9, 0x0007,
0x53FC, 0x1F2F, 0x0E29, 0x0061,
0x36BE, 0x7EAF, 0x681A, 0x3C00,
0x7BBE, 0x329D, 0x1DE8, 0x0423,
0x739F, 0x6A9B, 0x7293, 0x0001,
0x5FFF, 0x6732, 0x3DA9, 0x2481,
0x577F, 0x3EBC, 0x456F, 0x1880,
0x6B57, 0x6E1B, 0x5010, 0x0007,
0x0F96, 0x2C97, 0x0045, 0x3200,
0x67FF, 0x2F17, 0x2230, 0x1548,
};
static const struct {
char name[16];
unsigned palette_index;
} palette_assignments[] =
{
{"ZELDA", 5},
{"SUPER MARIOLAND", 6},
{"MARIOLAND2", 0x14},
{"SUPERMARIOLAND3", 2},
{"KIRBY DREAM LAND", 0xB},
{"HOSHINOKA-BI", 0xB},
{"KIRBY'S PINBALL", 3},
{"YOSSY NO TAMAGO", 0xC},
{"MARIO & YOSHI", 0xC},
{"YOSSY NO COOKIE", 4},
{"YOSHI'S COOKIE", 4},
{"DR.MARIO", 0x12},
{"TETRIS", 0x11},
{"YAKUMAN", 0x13},
{"METROID2", 0x1F},
{"KAERUNOTAMENI", 9},
{"GOLF", 0x18},
{"ALLEY WAY", 0x16},
{"BASEBALL", 0xF},
{"TENNIS", 0x17},
{"F1RACE", 0x1E},
{"KID ICARUS", 0xE},
{"QIX", 0x19},
{"SOLARSTRIKER", 7},
{"X", 0x1C},
{"GBWARS", 0x15},
};
static void command_ready(GB_gameboy_t *gb)
{
/* SGB header commands are used to send the contents of the header to the SNES CPU.
A header command looks like this:
Command ID: 0b1111xxx1, where xxx is the packet index. (e.g. F1 for [0x104, 0x112), F3 for [0x112, 0x120))
Checksum: Simple one byte sum for the following content bytes
0xE content bytes. The last command, FB, is padded with zeros, so information past the header is not sent. */
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
if (gb->boot_rom_finished) return;
uint8_t checksum = 0;
for (unsigned i = 2; i < 0x10; i++) {
checksum += gb->sgb->command[i];
}
if (checksum != gb->sgb->command[1]) {
GB_log(gb, "Failed checksum for SGB header command, disabling SGB features\n");
gb->sgb->disable_commands = true;
return;
}
unsigned index = (gb->sgb->command[0] >> 1) & 7;
if (index > 5) {
return;
}
memcpy(&gb->sgb->received_header[index * 14], &gb->sgb->command[2], 14);
if (gb->sgb->command[0] == 0xfb) {
if (gb->sgb->received_header[0x42] != 3 || gb->sgb->received_header[0x47] != 0x33) {
gb->sgb->disable_commands = true;
for (unsigned i = 0; i < sizeof(palette_assignments) / sizeof(palette_assignments[0]); i++) {
if (memcmp(palette_assignments[i].name, &gb->sgb->received_header[0x30], sizeof(palette_assignments[i].name)) == 0) {
gb->sgb->effective_palettes[0] = built_in_palettes[palette_assignments[i].palette_index * 4 - 4];
gb->sgb->effective_palettes[1] = built_in_palettes[palette_assignments[i].palette_index * 4 + 1 - 4];
gb->sgb->effective_palettes[2] = built_in_palettes[palette_assignments[i].palette_index * 4 + 2 - 4];
gb->sgb->effective_palettes[3] = built_in_palettes[palette_assignments[i].palette_index * 4 + 3 - 4];
break;
}
}
}
}
return;
}
/* Ignore malformed commands (0 length)*/
if ((gb->sgb->command[0] & 7) == 0) return;
switch (gb->sgb->command[0] >> 3) {
case PAL01:
pal_command(gb, 0, 1);
break;
case PAL23:
pal_command(gb, 2, 3);
break;
case PAL03:
pal_command(gb, 0, 3);
break;
case PAL12:
pal_command(gb, 1, 2);
break;
case ATTR_BLK: {
struct {
uint8_t count;
struct {
uint8_t control;
uint8_t palettes;
uint8_t left, top, right, bottom;
} data[];
} *command = (void *)(gb->sgb->command + 1);
if (command->count > 0x12) return;
for (unsigned i = 0; i < command->count; i++) {
bool inside = command->data[i].control & 1;
bool middle = command->data[i].control & 2;
bool outside = command->data[i].control & 4;
uint8_t inside_palette = command->data[i].palettes & 0x3;
uint8_t middle_palette = (command->data[i].palettes >> 2) & 0x3;
uint8_t outside_palette = (command->data[i].palettes >> 4) & 0x3;
if (inside && !middle && !outside) {
middle = true;
middle_palette = inside_palette;
}
else if (outside && !middle && !inside) {
middle = true;
middle_palette = outside_palette;
}
command->data[i].left &= 0x1F;
command->data[i].top &= 0x1F;
command->data[i].right &= 0x1F;
command->data[i].bottom &= 0x1F;
for (unsigned y = 0; y < 18; y++) {
for (unsigned x = 0; x < 20; x++) {
if (x < command->data[i].left || x > command->data[i].right ||
y < command->data[i].top || y > command->data[i].bottom) {
if (outside) {
gb->sgb->attribute_map[x + 20 * y] = outside_palette;
}
}
else if (x > command->data[i].left && x < command->data[i].right &&
y > command->data[i].top && y < command->data[i].bottom) {
if (inside) {
gb->sgb->attribute_map[x + 20 * y] = inside_palette;
}
}
else if (middle) {
gb->sgb->attribute_map[x + 20 * y] = middle_palette;
}
}
}
}
break;
}
case ATTR_CHR: {
struct __attribute__((packed)) {
uint8_t x, y;
uint16_t length;
uint8_t direction;
uint8_t data[];
} *command = (void *)(gb->sgb->command + 1);
uint16_t count = command->length;
#ifdef GB_BIG_ENDIAN
count = __builtin_bswap16(count);
#endif
uint8_t x = command->x;
uint8_t y = command->y;
if (x >= 20 || y >= 18 || (count + 3) / 4 > sizeof(gb->sgb->command) - sizeof(*command) - 1) {
/* TODO: Verify with the SFC BIOS */
break;
}
for (unsigned i = 0; i < count; i++) {
uint8_t palette = (command->data[i / 4] >> (((~i) & 3) << 1)) & 3;
gb->sgb->attribute_map[x + 20 * y] = palette;
if (command->direction) {
y++;
if (y == 18) {
x++;
y = 0;
if (x == 20) {
x = 0;
}
}
}
else {
x++;
if (x == 20) {
y++;
x = 0;
if (y == 18) {
y = 0;
}
}
}
}
break;
}
case ATTR_LIN: {
struct {
uint8_t count;
uint8_t data[];
} *command = (void *)(gb->sgb->command + 1);
if (command->count > sizeof(gb->sgb->command) - 2) return;
for (unsigned i = 0; i < command->count; i++) {
bool horizontal = command->data[i] & 0x80;
uint8_t palette = (command->data[i] >> 5) & 0x3;
uint8_t line = (command->data[i]) & 0x1F;
if (horizontal) {
if (line > 18) continue;
for (unsigned x = 0; x < 20; x++) {
gb->sgb->attribute_map[x + 20 * line] = palette;
}
}
else {
if (line > 20) continue;
for (unsigned y = 0; y < 18; y++) {
gb->sgb->attribute_map[line + 20 * y] = palette;
}
}
}
break;
}
case ATTR_DIV: {
uint8_t high_palette = gb->sgb->command[1] & 3;
uint8_t low_palette = (gb->sgb->command[1] >> 2) & 3;
uint8_t middle_palette = (gb->sgb->command[1] >> 4) & 3;
bool horizontal = gb->sgb->command[1] & 0x40;
uint8_t line = gb->sgb->command[2] & 0x1F;
for (unsigned y = 0; y < 18; y++) {
for (unsigned x = 0; x < 20; x++) {
if ((horizontal? y : x) < line) {
gb->sgb->attribute_map[x + 20 * y] = low_palette;
}
else if ((horizontal? y : x) == line) {
gb->sgb->attribute_map[x + 20 * y] = middle_palette;
}
else {
gb->sgb->attribute_map[x + 20 * y] = high_palette;
}
}
}
break;
}
case PAL_SET:
memcpy(&gb->sgb->effective_palettes[0],
&gb->sgb->ram_palettes[4 * (gb->sgb->command[1] + (gb->sgb->command[2] & 1) * 0x100)],
8);
memcpy(&gb->sgb->effective_palettes[4],
&gb->sgb->ram_palettes[4 * (gb->sgb->command[3] + (gb->sgb->command[4] & 1) * 0x100)],
8);
memcpy(&gb->sgb->effective_palettes[8],
&gb->sgb->ram_palettes[4 * (gb->sgb->command[5] + (gb->sgb->command[6] & 1) * 0x100)],
8);
memcpy(&gb->sgb->effective_palettes[12],
&gb->sgb->ram_palettes[4 * (gb->sgb->command[7] + (gb->sgb->command[8] & 1) * 0x100)],
8);
gb->sgb->effective_palettes[12] = gb->sgb->effective_palettes[8] =
gb->sgb->effective_palettes[4] = gb->sgb->effective_palettes[0];
if (gb->sgb->command[9] & 0x80) {
load_attribute_file(gb, gb->sgb->command[9] & 0x3F);
}
if (gb->sgb->command[9] & 0x40) {
gb->sgb->mask_mode = MASK_DISABLED;
}
break;
case PAL_TRN:
gb->sgb->vram_transfer_countdown = 2;
gb->sgb->transfer_dest = TRANSFER_PALETTES;
break;
case DATA_SND:
// Not supported, but used by almost all SGB games for hot patching, so let's mute the warning for this
break;
case MLT_REQ:
if (gb->sgb->player_count == 1) {
gb->sgb->current_player = 0;
}
gb->sgb->player_count = (gb->sgb->command[1] & 3) + 1; /* Todo: When breaking save state comaptibility,
fix this to be 0 based. */
if (gb->sgb->player_count == 3) {
gb->sgb->current_player++;
}
gb->sgb->mlt_lock = true;
break;
case CHR_TRN:
gb->sgb->vram_transfer_countdown = 2;
gb->sgb->transfer_dest = (gb->sgb->command[1] & 1)? TRANSFER_HIGH_TILES : TRANSFER_LOW_TILES;
break;
case PCT_TRN:
gb->sgb->vram_transfer_countdown = 2;
gb->sgb->transfer_dest = TRANSFER_BORDER_DATA;
break;
case ATTR_TRN:
gb->sgb->vram_transfer_countdown = 2;
gb->sgb->transfer_dest = TRANSFER_ATTRIBUTES;
break;
case ATTR_SET:
load_attribute_file(gb, gb->sgb->command[0] & 0x3F);
if (gb->sgb->command[0] & 0x40) {
gb->sgb->mask_mode = MASK_DISABLED;
}
break;
case MASK_EN:
gb->sgb->mask_mode = gb->sgb->command[1] & 3;
break;
default:
if ((gb->sgb->command[0] >> 3) == 8 &&
(gb->sgb->command[1] & ~0x80) == 0 &&
(gb->sgb->command[2] & ~0x80) == 0) {
/* Mute/dummy sound commands, ignore this command as it's used by many games at startup */
break;
}
GB_log(gb, "Unimplemented SGB command %x: ", gb->sgb->command[0] >> 3);
for (unsigned i = 0; i < gb->sgb->command_write_index / 8; i++) {
GB_log(gb, "%02x ", gb->sgb->command[i]);
}
GB_log(gb, "\n");
}
}
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
{
if (!GB_is_sgb(gb)) return;
if (!GB_is_hle_sgb(gb)) {
/* Notify via callback */
return;
}
if (gb->sgb->disable_commands) return;
if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) {
return;
}
uint16_t command_size = (gb->sgb->command[0] & 7 ?: 1) * SGB_PACKET_SIZE * 8;
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
command_size = SGB_PACKET_SIZE * 8;
}
if ((value & 0x20) == 0 && (gb->io_registers[GB_IO_JOYP] & 0x20) != 0) {
gb->sgb->mlt_lock ^= true;
}
switch ((value >> 4) & 3) {
case 3:
gb->sgb->ready_for_pulse = true;
if ((gb->sgb->player_count & 1) == 0 && !gb->sgb->mlt_lock) {
gb->sgb->current_player++;
gb->sgb->current_player &= 3;
gb->sgb->mlt_lock = true;
}
break;
case 2: // Zero
if (!gb->sgb->ready_for_pulse || !gb->sgb->ready_for_write) return;
if (gb->sgb->ready_for_stop) {
if (gb->sgb->command_write_index == command_size) {
command_ready(gb);
gb->sgb->command_write_index = 0;
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
}
gb->sgb->ready_for_pulse = false;
gb->sgb->ready_for_write = false;
gb->sgb->ready_for_stop = false;
}
else {
gb->sgb->command_write_index++;
gb->sgb->ready_for_pulse = false;
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
gb->sgb->ready_for_stop = true;
}
}
break;
case 1: // One
if (!gb->sgb->ready_for_pulse || !gb->sgb->ready_for_write) return;
if (gb->sgb->ready_for_stop) {
GB_log(gb, "Corrupt SGB command.\n");
gb->sgb->ready_for_pulse = false;
gb->sgb->ready_for_write = false;
gb->sgb->command_write_index = 0;
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
}
else {
gb->sgb->command[gb->sgb->command_write_index / 8] |= 1 << (gb->sgb->command_write_index & 7);
gb->sgb->command_write_index++;
gb->sgb->ready_for_pulse = false;
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
gb->sgb->ready_for_stop = true;
}
}
break;
case 0:
if (!gb->sgb->ready_for_pulse) return;
gb->sgb->ready_for_write = true;
gb->sgb->ready_for_pulse = false;
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) != 0 ||
gb->sgb->command_write_index == 0 ||
gb->sgb->ready_for_stop) {
gb->sgb->command_write_index = 0;
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
gb->sgb->ready_for_stop = false;
}
break;
default:
break;
}
}
static uint32_t convert_rgb15(GB_gameboy_t *gb, uint16_t color)
{
return GB_convert_rgb15(gb, color, false);
}
static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_t fade)
{
uint8_t r = ((color) & 0x1F) - fade;
uint8_t g = ((color >> 5) & 0x1F) - fade;
uint8_t b = ((color >> 10) & 0x1F) - fade;
if (r >= 0x20) r = 0;
if (g >= 0x20) g = 0;
if (b >= 0x20) b = 0;
color = r | (g << 5) | (b << 10);
return GB_convert_rgb15(gb, color, false);
}
#include <stdio.h>
static void render_boot_animation (GB_gameboy_t *gb)
{
#include "graphics/sgb_animation_logo.inc"
uint32_t *output = gb->screen;
if (gb->border_mode != GB_BORDER_NEVER) {
output += 48 + 40 * 256;
}
uint8_t *input = animation_logo;
unsigned fade_blue = 0;
unsigned fade_red = 0;
if (gb->sgb->intro_animation < 80 - 32) {
fade_blue = 32;
}
else if (gb->sgb->intro_animation < 80) {
fade_blue = 80 - gb->sgb->intro_animation;
}
else if (gb->sgb->intro_animation > INTRO_ANIMATION_LENGTH - 32) {
fade_red = fade_blue = gb->sgb->intro_animation - INTRO_ANIMATION_LENGTH + 32;
}
uint32_t colors[] = {
convert_rgb15(gb, 0),
convert_rgb15_with_fade(gb, 0x14A5, fade_blue),
convert_rgb15_with_fade(gb, 0x54E0, fade_blue),
convert_rgb15_with_fade(gb, 0x0019, fade_red),
convert_rgb15(gb, 0x0011),
convert_rgb15(gb, 0x0009),
};
unsigned y_min = (144 - animation_logo_height) / 2;
unsigned y_max = y_min + animation_logo_height;
for (unsigned y = 0; y < 144; y++) {
for (unsigned x = 0; x < 160; x++) {
if (y < y_min || y >= y_max) {
*(output++) = colors[0];
}
else {
uint8_t color = *input;
if (color >= 3) {
if (color == gb->sgb->intro_animation / 2 - 3) {
color = 5;
}
else if (color == gb->sgb->intro_animation / 2 - 4) {
color = 4;
}
else if (color < gb->sgb->intro_animation / 2 - 4) {
color = 3;
}
else {
color = 0;
}
}
*(output++) = colors[color];
input++;
}
}
if (gb->border_mode != GB_BORDER_NEVER) {
output += 256 - 160;
}
}
}
static void render_jingle(GB_gameboy_t *gb, size_t count);
void GB_sgb_render(GB_gameboy_t *gb)
{
if (gb->apu_output.sample_rate) {
render_jingle(gb, gb->apu_output.sample_rate / GB_get_usual_frame_rate(gb));
}
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++;
if (gb->sgb->vram_transfer_countdown) {
if (--gb->sgb->vram_transfer_countdown == 0) {
if (gb->sgb->transfer_dest == TRANSFER_LOW_TILES || gb->sgb->transfer_dest == TRANSFER_HIGH_TILES) {
uint8_t *base = &gb->sgb->pending_border.tiles[gb->sgb->transfer_dest == TRANSFER_HIGH_TILES ? 0x80 * 8 * 8 : 0];
for (unsigned tile = 0; tile < 0x80; tile++) {
unsigned tile_x = (tile % 10) * 16;
unsigned tile_y = (tile / 10) * 8;
for (unsigned y = 0; y < 0x8; y++) {
for (unsigned x = 0; x < 0x8; x++) {
base[tile * 8 * 8 + y * 8 + x] = gb->sgb->screen_buffer[(tile_x + x) + (tile_y + y) * 160] +
gb->sgb->screen_buffer[(tile_x + x + 8) + (tile_y + y) * 160] * 4;
}
}
}
}
else {
unsigned size = 0;
uint16_t *data = NULL;
switch (gb->sgb->transfer_dest) {
case TRANSFER_PALETTES:
size = 0x100;
data = gb->sgb->ram_palettes;
break;
case TRANSFER_BORDER_DATA:
size = 0x88;
data = gb->sgb->pending_border.raw_data;
break;
case TRANSFER_ATTRIBUTES:
size = 0xFE;
data = (uint16_t *)gb->sgb->attribute_files;
break;
default:
return; // Corrupt state?
}
for (unsigned tile = 0; tile < size; tile++) {
unsigned tile_x = (tile % 20) * 8;
unsigned tile_y = (tile / 20) * 8;
for (unsigned y = 0; y < 0x8; y++) {
static const uint16_t pixel_to_bits[4] = {0x0000, 0x0080, 0x8000, 0x8080};
*data = 0;
for (unsigned x = 0; x < 8; x++) {
*data |= pixel_to_bits[gb->sgb->screen_buffer[(tile_x + x) + (tile_y + y) * 160] & 3] >> x;
}
#ifdef GB_BIG_ENDIAN
if (gb->sgb->transfer_dest == TRANSFER_ATTRIBUTES) {
*data = __builtin_bswap16(*data);
}
#endif
data++;
}
}
if (gb->sgb->transfer_dest == TRANSFER_BORDER_DATA) {
gb->sgb->border_animation = 64;
}
}
}
}
if (!gb->screen || !gb->rgb_encode_callback || gb->disable_rendering) return;
uint32_t colors[4 * 4];
for (unsigned i = 0; i < 4 * 4; i++) {
colors[i] = convert_rgb15(gb, gb->sgb->effective_palettes[i]);
}
if (gb->sgb->mask_mode != MASK_FREEZE) {
memcpy(gb->sgb->effective_screen_buffer,
gb->sgb->screen_buffer,
sizeof(gb->sgb->effective_screen_buffer));
}
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) {
render_boot_animation(gb);
}
else {
uint32_t *output = gb->screen;
if (gb->border_mode != GB_BORDER_NEVER) {
output += 48 + 40 * 256;
}
uint8_t *input = gb->sgb->effective_screen_buffer;
switch ((mask_mode_t) gb->sgb->mask_mode) {
case MASK_DISABLED:
case MASK_FREEZE: {
for (unsigned y = 0; y < 144; y++) {
for (unsigned x = 0; x < 160; x++) {
uint8_t palette = gb->sgb->attribute_map[x / 8 + y / 8 * 20] & 3;
*(output++) = colors[(*(input++) & 3) + palette * 4];
}
if (gb->border_mode != GB_BORDER_NEVER) {
output += 256 - 160;
}
}
break;
}
case MASK_BLACK:
{
uint32_t black = convert_rgb15(gb, 0);
for (unsigned y = 0; y < 144; y++) {
for (unsigned x = 0; x < 160; x++) {
*(output++) = black;
}
if (gb->border_mode != GB_BORDER_NEVER) {
output += 256 - 160;
}
}
break;
}
case MASK_COLOR_0:
{
for (unsigned y = 0; y < 144; y++) {
for (unsigned x = 0; x < 160; x++) {
*(output++) = colors[0];
}
if (gb->border_mode != GB_BORDER_NEVER) {
output += 256 - 160;
}
}
break;
}
}
}
uint32_t border_colors[16 * 4];
if (gb->sgb->border_animation == 0 || gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) {
for (unsigned i = 0; i < 16 * 4; i++) {
border_colors[i] = convert_rgb15(gb, gb->sgb->border.palette[i]);
}
}
else if (gb->sgb->border_animation > 32) {
gb->sgb->border_animation--;
for (unsigned i = 0; i < 16 * 4; i++) {
border_colors[i] = convert_rgb15_with_fade(gb, gb->sgb->border.palette[i], 64 - gb->sgb->border_animation);
}
}
else {
gb->sgb->border_animation--;
for (unsigned i = 0; i < 16 * 4; i++) {
border_colors[i] = convert_rgb15_with_fade(gb, gb->sgb->border.palette[i], gb->sgb->border_animation);
}
}
if (gb->sgb->border_animation == 32) {
memcpy(&gb->sgb->border, &gb->sgb->pending_border, sizeof(gb->sgb->border));
}
for (unsigned tile_y = 0; tile_y < 28; tile_y++) {
for (unsigned tile_x = 0; tile_x < 32; tile_x++) {
bool gb_area = false;
if (tile_x >= 6 && tile_x < 26 && tile_y >= 5 && tile_y < 23) {
gb_area = true;
}
else if (gb->border_mode == GB_BORDER_NEVER) {
continue;
}
uint16_t tile = gb->sgb->border.map[tile_x + tile_y * 32];
uint8_t flip_x = (tile & 0x4000)? 0x7 : 0;
uint8_t flip_y = (tile & 0x8000)? 0x7 : 0;
uint8_t palette = (tile >> 10) & 3;
for (unsigned y = 0; y < 8; y++) {
for (unsigned x = 0; x < 8; x++) {
uint8_t color = gb->sgb->border.tiles[(tile & 0xFF) * 64 + (x ^ flip_x) + (y ^ flip_y) * 8] & 0xF;
uint32_t *output = gb->screen;
if (gb->border_mode == GB_BORDER_NEVER) {
output += (tile_x - 6) * 8 + x + ((tile_y - 5) * 8 + y) * 160;
}
else {
output += tile_x * 8 + x + (tile_y * 8 + y) * 256;
}
if (color == 0) {
if (gb_area) continue;
*output = colors[0];
}
else {
*output = border_colors[color + palette * 16];
}
}
}
}
}
}
void GB_sgb_load_default_data(GB_gameboy_t *gb)
{
#include "graphics/sgb_border.inc"
memcpy(gb->sgb->border.map, tilemap, sizeof(tilemap));
memcpy(gb->sgb->border.palette, palette, sizeof(palette));
/* Expand tileset */
for (unsigned tile = 0; tile < sizeof(tiles) / 32; tile++) {
for (unsigned y = 0; y < 8; y++) {
for (unsigned x = 0; x < 8; x++) {
gb->sgb->border.tiles[tile * 8 * 8 + y * 8 + x] =
(tiles[tile * 32 + y * 2 + 0] & (1 << (7 ^ x)) ? 1 : 0) |
(tiles[tile * 32 + y * 2 + 1] & (1 << (7 ^ x)) ? 2 : 0) |
(tiles[tile * 32 + y * 2 + 16] & (1 << (7 ^ x)) ? 4 : 0) |
(tiles[tile * 32 + y * 2 + 17] & (1 << (7 ^ x)) ? 8 : 0);
}
}
}
if (gb->model != GB_MODEL_SGB2) {
/* Delete the "2" */
gb->sgb->border.map[25 * 32 + 25] = gb->sgb->border.map[25 * 32 + 26] =
gb->sgb->border.map[26 * 32 + 25] = gb->sgb->border.map[26 * 32 + 26] =
gb->sgb->border.map[27 * 32 + 25] = gb->sgb->border.map[27 * 32 + 26] =
gb->sgb->border.map[0];
/* Re-center */
memmove(&gb->sgb->border.map[25 * 32 + 1], &gb->sgb->border.map[25 * 32], (32 * 3 - 1) * sizeof(gb->sgb->border.map[0]));
}
gb->sgb->effective_palettes[0] = built_in_palettes[0];
gb->sgb->effective_palettes[1] = built_in_palettes[1];
gb->sgb->effective_palettes[2] = built_in_palettes[2];
gb->sgb->effective_palettes[3] = built_in_palettes[3];
}
static double fm_synth(double phase)
{
return (sin(phase * M_PI * 2) +
sin(phase * M_PI * 2 + sin(phase * M_PI * 2)) +
sin(phase * M_PI * 2 + sin(phase * M_PI * 3)) +
sin(phase * M_PI * 2 + sin(phase * M_PI * 4))) / 4;
}
static double fm_sweep(double phase)
{
double ret = 0;
for (unsigned i = 0; i < 8; i++) {
ret += sin((phase * M_PI * 2 + sin(phase * M_PI * 8) / 4) * pow(1.25, i)) * (8 - i) / 36;
}
return ret;
}
static double random_double(void)
{
return ((signed)(GB_random32() % 0x10001) - 0x8000) / (double) 0x8000;
}
static void render_jingle(GB_gameboy_t *gb, size_t count)
{
const double frequencies[7] = {
466.16, // Bb4
587.33, // D5
698.46, // F5
830.61, // Ab5
1046.50, // C6
1244.51, // Eb6
1567.98, // G6
};
assert(gb->apu_output.sample_callback);
if (gb->sgb->intro_animation < 0) {
GB_sample_t sample = {0, 0};
for (unsigned i = 0; i < count; i++) {
gb->apu_output.sample_callback(gb, &sample);
}
return;
}
if (gb->sgb->intro_animation >= INTRO_ANIMATION_LENGTH) return;
signed jingle_stage = (gb->sgb->intro_animation - 64) / 3;
double sweep_cutoff_ratio = 2000.0 * pow(2, gb->sgb->intro_animation / 20.0) / gb->apu_output.sample_rate;
double sweep_phase_shift = 1000.0 * pow(2, gb->sgb->intro_animation / 40.0) / gb->apu_output.sample_rate;
if (sweep_cutoff_ratio > 1) {
sweep_cutoff_ratio = 1;
}
GB_sample_t stereo;
for (unsigned i = 0; i < count; i++) {
double sample = 0;
for (signed f = 0; f < 7 && f < jingle_stage; f++) {
sample += fm_synth(gb->sgb_intro_jingle_phases[f]) *
(0.75 * pow(0.5, jingle_stage - f) + 0.25) / 5.0;
gb->sgb_intro_jingle_phases[f] += frequencies[f] / gb->apu_output.sample_rate;
}
if (gb->sgb->intro_animation > 100) {
sample *= pow((INTRO_ANIMATION_LENGTH - gb->sgb->intro_animation) / (INTRO_ANIMATION_LENGTH - 100.0), 3);
}
if (gb->sgb->intro_animation < 120) {
double next = fm_sweep(gb->sgb_intro_sweep_phase) * 0.3 + random_double() * 0.7;
gb->sgb_intro_sweep_phase += sweep_phase_shift;
gb->sgb_intro_sweep_previous_sample = next * (sweep_cutoff_ratio) +
gb->sgb_intro_sweep_previous_sample * (1 - sweep_cutoff_ratio);
sample += gb->sgb_intro_sweep_previous_sample * pow((120 - gb->sgb->intro_animation) / 120.0, 2) * 0.8;
}
stereo.left = stereo.right = sample * 0x7000;
gb->apu_output.sample_callback(gb, &stereo);
}
return;
}

View File

@ -1,67 +0,0 @@
#ifndef sgb_h
#define sgb_h
#include "gb_struct_def.h"
#include <stdint.h>
#include <stdbool.h>
typedef struct GB_sgb_s GB_sgb_t;
typedef struct {
uint8_t tiles[0x100 * 8 * 8]; /* High nibble not used*/
union {
struct {
uint16_t map[32 * 32];
uint16_t palette[16 * 4];
};
uint16_t raw_data[0x440];
};
} GB_sgb_border_t;
#ifdef GB_INTERNAL
struct GB_sgb_s {
uint8_t command[16 * 7];
uint16_t command_write_index;
bool ready_for_pulse;
bool ready_for_write;
bool ready_for_stop;
bool disable_commands;
/* Screen buffer */
uint8_t screen_buffer[160 * 144]; // Live image from the Game Boy
uint8_t effective_screen_buffer[160 * 144]; // Image actually rendered to the screen
/* Multiplayer Input */
uint8_t player_count, current_player;
/* Mask */
uint8_t mask_mode;
/* Data Transfer */
uint8_t vram_transfer_countdown, transfer_dest;
/* Border */
GB_sgb_border_t border, pending_border;
uint8_t border_animation;
/* Colorization */
uint16_t effective_palettes[4 * 4];
uint16_t ram_palettes[4 * 512];
uint8_t attribute_map[20 * 18];
uint8_t attribute_files[0xFE0];
/* Intro */
int16_t intro_animation;
/* GB Header */
uint8_t received_header[0x54];
/* Multiplayer (cont) */
bool mlt_lock;
};
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
void GB_sgb_render(GB_gameboy_t *gb);
void GB_sgb_load_default_data(GB_gameboy_t *gb);
#endif
#endif

View File

@ -188,9 +188,8 @@ endif
cocoa: $(BIN)/SameDuck.app
quicklook: $(BIN)/SameDuck.qlgenerator
sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/agb_boot.bin $(BIN)/SDL/sgb_boot.bin $(BIN)/SDL/sgb2_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp $(BIN)/SDL/Shaders
bootroms: $(BIN)/BootROMs/agb_boot.bin $(BIN)/BootROMs/cgb_boot.bin $(BIN)/BootROMs/dmg_boot.bin $(BIN)/BootROMs/sgb_boot.bin $(BIN)/BootROMs/sgb2_boot.bin
tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin $(BIN)/tester/agb_boot.bin $(BIN)/tester/sgb_boot.bin $(BIN)/tester/sgb2_boot.bin
sdl: $(SDL_TARGET) $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp $(BIN)/SDL/Shaders
tester: $(TESTER_TARGET)
all: cocoa sdl tester libretro
# Get a list of our source files and their respective object file targets
@ -267,11 +266,6 @@ $(BIN)/SameDuck.app: $(BIN)/SameDuck.app/Contents/MacOS/SameDuck \
Cocoa/License.html \
Cocoa/Info.plist \
Misc/registers.sym \
$(BIN)/SameDuck.app/Contents/Resources/dmg_boot.bin \
$(BIN)/SameDuck.app/Contents/Resources/cgb_boot.bin \
$(BIN)/SameDuck.app/Contents/Resources/agb_boot.bin \
$(BIN)/SameDuck.app/Contents/Resources/sgb_boot.bin \
$(BIN)/SameDuck.app/Contents/Resources/sgb2_boot.bin \
$(patsubst %.xib,%.nib,$(addprefix $(BIN)/SameDuck.app/Contents/Resources/Base.lproj/,$(shell cd Cocoa;ls *.xib))) \
$(BIN)/SameDuck.qlgenerator \
Shaders
@ -298,8 +292,7 @@ $(BIN)/SameDuck.app/Contents/Resources/Base.lproj/%.nib: Cocoa/%.xib
$(BIN)/SameDuck.qlgenerator: $(BIN)/SameDuck.qlgenerator/Contents/MacOS/SameDuckQL \
$(shell ls QuickLook/*.png) \
QuickLook/Info.plist \
$(BIN)/SameDuck.qlgenerator/Contents/Resources/cgb_boot_fast.bin
QuickLook/Info.plist
$(MKDIR) -p $(BIN)/SameDuck.qlgenerator/Contents/Resources
cp QuickLook/*.png $(BIN)/SameDuck.qlgenerator/Contents/Resources/
sed s/@VERSION/$(VERSION)/ < QuickLook/Info.plist > $(BIN)/SameDuck.qlgenerator/Contents/Info.plist
@ -309,12 +302,6 @@ $(BIN)/SameDuck.qlgenerator: $(BIN)/SameDuck.qlgenerator/Contents/MacOS/SameDuck
$(BIN)/SameDuck.qlgenerator/Contents/MacOS/SameDuckQL: $(CORE_OBJECTS) $(QUICKLOOK_OBJECTS)
-@$(MKDIR) -p $(dir $@)
$(CC) $^ -o $@ $(LDFLAGS) $(FAT_FLAGS) -Wl,-exported_symbols_list,QuickLook/exports.sym -bundle -framework Cocoa -framework Quicklook
# cgb_boot_fast.bin is not a standard boot ROM, we don't expect it to exist in the user-provided
# boot ROM directory.
$(BIN)/SameDuck.qlgenerator/Contents/Resources/cgb_boot_fast.bin: $(BIN)/BootROMs/cgb_boot_fast.bin
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
# SDL Port
@ -366,18 +353,6 @@ $(BIN)/tester/sameduck_tester.exe: $(CORE_OBJECTS) $(SDL_OBJECTS)
-@$(MKDIR) -p $(dir $@)
$(CC) $^ -o $@ $(LDFLAGS) -Wl,/subsystem:console
$(BIN)/SDL/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
$(BIN)/tester/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
$(BIN)/SameDuck.app/Contents/Resources/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
$(BIN)/SDL/LICENSE: LICENSE
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
@ -394,29 +369,6 @@ $(BIN)/SDL/Shaders: Shaders
-@$(MKDIR) -p $@
cp -rf Shaders/*.fsh $@
# Boot ROMs
$(OBJ)/%.2bpp: %.png
-@$(MKDIR) -p $(dir $@)
rgbgfx -h -u -o $@ $<
$(OBJ)/BootROMs/SameDuckLogo.pb12: $(OBJ)/BootROMs/SameDuckLogo.2bpp $(PB12_COMPRESS)
$(realpath $(PB12_COMPRESS)) < $< > $@
$(PB12_COMPRESS): BootROMs/pb12.c
$(NATIVE_CC) -std=c99 -Wall -Werror $< -o $@
$(BIN)/BootROMs/agb_boot.bin: BootROMs/cgb_boot.asm
$(BIN)/BootROMs/cgb_boot_fast.bin: BootROMs/cgb_boot.asm
$(BIN)/BootROMs/sgb2_boot: BootROMs/sgb_boot.asm
$(BIN)/BootROMs/%.bin: BootROMs/%.asm $(OBJ)/BootROMs/SameDuckLogo.pb12
-@$(MKDIR) -p $(dir $@)
rgbasm -i $(OBJ)/BootROMs/ -i BootROMs/ -o $@.tmp $<
rgblink -o $@.tmp2 $@.tmp
dd if=$@.tmp2 of=$@ count=1 bs=$(if $(findstring dmg,$@)$(findstring sgb,$@),256,2304) 2> $(NULL)
@rm $@.tmp $@.tmp2
# Libretro Core (uses its own build system)
libretro:
CFLAGS="$(WARNINGS)" $(MAKE) -C libretro

125
SDL/gui.c
View File

@ -324,56 +324,6 @@ static void return_to_root_menu(unsigned index)
scroll = 0;
}
static void cycle_model(unsigned index)
{
configuration.model++;
if (configuration.model == MODEL_MAX) {
configuration.model = 0;
}
pending_command = GB_SDL_RESET_COMMAND;
}
static void cycle_model_backwards(unsigned index)
{
if (configuration.model == 0) {
configuration.model = MODEL_MAX;
}
configuration.model--;
pending_command = GB_SDL_RESET_COMMAND;
}
const char *current_model_string(unsigned index)
{
return (const char *[]){"Game Boy", "Game Boy Color", "Game Boy Advance", "Super Game Boy"}
[configuration.model];
}
static void cycle_sgb_revision(unsigned index)
{
configuration.sgb_revision++;
if (configuration.sgb_revision == SGB_MAX) {
configuration.sgb_revision = 0;
}
pending_command = GB_SDL_RESET_COMMAND;
}
static void cycle_sgb_revision_backwards(unsigned index)
{
if (configuration.sgb_revision == 0) {
configuration.sgb_revision = SGB_MAX;
}
configuration.sgb_revision--;
pending_command = GB_SDL_RESET_COMMAND;
}
const char *current_sgb_revision_string(unsigned index)
{
return (const char *[]){"Super Game Boy NTSC", "Super Game Boy PAL", "Super Game Boy 2"}
[configuration.sgb_revision];
}
static const uint32_t rewind_lengths[] = {0, 10, 30, 60, 60 * 2, 60 * 5, 60 * 10};
static const char *rewind_strings[] = {"Disabled",
"10 Seconds",
@ -421,8 +371,6 @@ const char *current_rewind_string(unsigned index)
}
static const struct menu_item emulation_menu[] = {
{"Emulated Model:", cycle_model, current_model_string, cycle_model_backwards},
{"SGB Revision:", cycle_sgb_revision, current_sgb_revision_string, cycle_sgb_revision_backwards},
{"Rewind Length:", cycle_rewind, current_rewind_string, cycle_rewind_backwards},
{"Back", return_to_root_menu},
{NULL,}
@ -447,24 +395,12 @@ const char *current_default_scale(unsigned index)
[configuration.default_scale - 1];
}
const char *current_color_correction_mode(unsigned index)
{
return (const char *[]){"Disabled", "Correct Color Curves", "Emulate Hardware", "Preserve Brightness", "Reduce Contrast"}
[configuration.color_correction_mode];
}
const char *current_palette(unsigned index)
{
return (const char *[]){"Greyscale", "Lime (Game Boy)", "Olive (Pocket)", "Teal (Light)"}
[configuration.dmg_palette];
}
const char *current_border_mode(unsigned index)
{
return (const char *[]){"SGB Only", "Never", "Always"}
[configuration.border_mode];
}
void cycle_scaling(unsigned index)
{
configuration.scaling_mode++;
@ -513,26 +449,6 @@ void cycle_default_scale_backwards(unsigned index)
update_viewport();
}
static void cycle_color_correction(unsigned index)
{
if (configuration.color_correction_mode == GB_COLOR_CORRECTION_REDUCE_CONTRAST) {
configuration.color_correction_mode = GB_COLOR_CORRECTION_DISABLED;
}
else {
configuration.color_correction_mode++;
}
}
static void cycle_color_correction_backwards(unsigned index)
{
if (configuration.color_correction_mode == GB_COLOR_CORRECTION_DISABLED) {
configuration.color_correction_mode = GB_COLOR_CORRECTION_REDUCE_CONTRAST;
}
else {
configuration.color_correction_mode--;
}
}
static void cycle_palette(unsigned index)
{
if (configuration.dmg_palette == 3) {
@ -553,26 +469,6 @@ static void cycle_palette_backwards(unsigned index)
}
}
static void cycle_border_mode(unsigned index)
{
if (configuration.border_mode == GB_BORDER_ALWAYS) {
configuration.border_mode = GB_BORDER_SGB;
}
else {
configuration.border_mode++;
}
}
static void cycle_border_mode_backwards(unsigned index)
{
if (configuration.border_mode == GB_BORDER_SGB) {
configuration.border_mode = GB_BORDER_ALWAYS;
}
else {
configuration.border_mode--;
}
}
struct shader_name {
const char *file_name;
const char *display_name;
@ -683,10 +579,8 @@ static const struct menu_item graphics_menu[] = {
{"Scaling Mode:", cycle_scaling, current_scaling_mode, cycle_scaling_backwards},
{"Default Window Scale:", cycle_default_scale, current_default_scale, cycle_default_scale_backwards},
{"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_backwards},
{"Color Correction:", cycle_color_correction, current_color_correction_mode, cycle_color_correction_backwards},
{"Frame Blending:", cycle_blending_mode, blending_mode_string, cycle_blending_mode_backwards},
{"Mono Palette:", cycle_palette, current_palette, cycle_palette_backwards},
{"Display Border:", cycle_border_mode, current_border_mode, cycle_border_mode_backwards},
{"Palette:", cycle_palette, current_palette, cycle_palette_backwards},
{"Back", return_to_root_menu},
{NULL,}
};
@ -896,27 +790,22 @@ static void detect_joypad_layout(unsigned index)
static void cycle_rumble_mode(unsigned index)
{
if (configuration.rumble_mode == GB_RUMBLE_ALL_GAMES) {
configuration.rumble_mode = GB_RUMBLE_DISABLED;
if (configuration.rumble_mode == GB_RUMBLE_DISABLED) {
configuration.rumble_mode = GB_RUMBLE_ALL_GAMES;
}
else {
configuration.rumble_mode++;
configuration.rumble_mode = GB_RUMBLE_DISABLED;
}
}
static void cycle_rumble_mode_backwards(unsigned index)
{
if (configuration.rumble_mode == GB_RUMBLE_DISABLED) {
configuration.rumble_mode = GB_RUMBLE_ALL_GAMES;
}
else {
configuration.rumble_mode--;
}
cycle_rumble_mode(index);
}
const char *current_rumble_mode(unsigned index)
{
return (const char *[]){"Disabled", "Rumble Game Paks Only", "All Games"}
return (const char *[]){"Disabled", "Rumble Game Paks Only", "Enabled"}
[configuration.rumble_mode];
}
@ -1333,7 +1222,7 @@ void run_gui(bool is_running)
switch (gui_state) {
case SHOWING_DROP_MESSAGE:
draw_text_centered(pixels, width, height, 8 + y_offset, "Press ESC for menu", gui_palette_native[3], gui_palette_native[0], false);
draw_text_centered(pixels, width, height, 116 + y_offset, "Drop a GB or GBC", gui_palette_native[3], gui_palette_native[0], false);
draw_text_centered(pixels, width, height, 116 + y_offset, "Drop a BIN", gui_palette_native[3], gui_palette_native[0], false);
draw_text_centered(pixels, width, height, 128 + y_offset, "file to play", gui_palette_native[3], gui_palette_native[0], false);
break;
case SHOWING_MENU:

View File

@ -448,20 +448,6 @@ static bool handle_pending_command(void)
static void load_boot_rom(GB_gameboy_t *gb, GB_boot_rom_t type)
{
bool error = false;
start_capturing_logs();
static const char *const names[] = {
[GB_BOOT_ROM_DMG0] = "dmg0_boot.bin",
[GB_BOOT_ROM_DMG] = "dmg_boot.bin",
[GB_BOOT_ROM_MGB] = "mgb_boot.bin",
[GB_BOOT_ROM_SGB] = "sgb_boot.bin",
[GB_BOOT_ROM_SGB2] = "sgb2_boot.bin",
[GB_BOOT_ROM_CGB0] = "cgb0_boot.bin",
[GB_BOOT_ROM_CGB] = "cgb_boot.bin",
[GB_BOOT_ROM_AGB] = "agb_boot.bin",
};
GB_load_boot_rom(gb, resource_path(names[type]));
end_capturing_logs(true, error);
}
static void run(void)

View File

@ -324,22 +324,6 @@ CFLAGS += -D__LIBRETRO__ $(fpic) $(INCFLAGS) -std=gnu11 -D_GNU_SOURCE -D_USE_M
all: $(TARGET)
$(CORE_DIR)/libretro/%_boot.c: $(CORE_DIR)/build/bin/BootROMs/%_boot.bin
echo "/* AUTO-GENERATED */" > $@
echo "const unsigned char $(notdir $(@:%.c=%))[] = {" >> $@
ifneq ($(findstring Haiku,$(shell uname -s)),)
# turns out od is posix, hexdump is not hence is less portable
# this is still rather ugly and could be done better I guess
od -A none -t x1 -v $< | sed -e 's/^\ /0x/' -e 's/\ /,\ 0x/g' -e 's/$$/,/g' | tr '\n' ' ' >> $@
else
hexdump -v -e '/1 "0x%02x, "' $< >> $@
endif
echo "};" >> $@
echo "const unsigned $(notdir $(@:%.c=%))_length = sizeof($(notdir $(@:%.c=%)));" >> $@
$(CORE_DIR)/build/bin/BootROMs/%_boot.bin:
$(MAKE) -C $(CORE_DIR) $(patsubst $(CORE_DIR)/%,%,$@)
$(TARGET): $(OBJECTS)
-@$(MKDIR) -p $(dir $@)
ifeq ($(STATIC_LINKING), 1)

View File

@ -3,7 +3,6 @@ include $(CORE_DIR)/version.mk
INCFLAGS := -I$(CORE_DIR)
SOURCES_C := $(CORE_DIR)/Core/gb.c \
$(CORE_DIR)/Core/sgb.c \
$(CORE_DIR)/Core/apu.c \
$(CORE_DIR)/Core/memory.c \
$(CORE_DIR)/Core/timing.c \
@ -15,11 +14,6 @@ SOURCES_C := $(CORE_DIR)/Core/gb.c \
$(CORE_DIR)/Core/save_state.c \
$(CORE_DIR)/Core/random.c \
$(CORE_DIR)/Core/rumble.c \
$(CORE_DIR)/libretro/agb_boot.c \
$(CORE_DIR)/libretro/cgb_boot.c \
$(CORE_DIR)/libretro/dmg_boot.c \
$(CORE_DIR)/libretro/sgb_boot.c \
$(CORE_DIR)/libretro/sgb2_boot.c \
$(CORE_DIR)/libretro/libretro.c
CFLAGS += -DGB_DISABLE_TIMEKEEPING -DGB_DISABLE_REWIND -DGB_DISABLE_DEBUGGER -DGB_DISABLE_CHEATS

View File

@ -23,12 +23,6 @@
#include <Core/gb.h>
#include "libretro.h"
#ifdef _WIN32
static const char slash = '\\';
#else
static const char slash = '/';
#endif
#define MAX_VIDEO_WIDTH 256
#define MAX_VIDEO_HEIGHT 224
#define MAX_VIDEO_PIXELS (MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT)
@ -104,8 +98,6 @@ char retro_game_path[4096];
GB_gameboy_t gameboy[2];
extern const unsigned char dmg_boot[], cgb_boot[], agb_boot[], sgb_boot[], sgb2_boot[];
extern const unsigned dmg_boot_length, cgb_boot_length, agb_boot_length, sgb_boot_length, sgb2_boot_length;
bool vblank1_occurred = false, vblank2_occurred = false;
static void fallback_log(enum retro_log_level level, const char *fmt, ...)
@ -224,9 +216,7 @@ static retro_environment_t environ_cb;
static const struct retro_variable vars_single[] = {
{ "sameduck_color_correction_mode", "Color correction; emulate hardware|preserve brightness|reduce contrast|off|correct curves" },
{ "sameduck_high_pass_filter_mode", "High-pass filter; accurate|remove dc offset|off" },
{ "sameduck_model", "Emulated model (Restart game); Auto|Game Boy|Game Boy Color|Game Boy Advance|Super Game Boy|Super Game Boy 2" },
{ "sameduck_border", "Display border; Super Game Boy only|always|never" },
{ "sameduck_rumble", "Enable rumble; rumble-enabled games|all games|never" },
{ "sameduck_rumble", "Enable rumble; all games|never" },
{ NULL }
};
@ -235,15 +225,11 @@ static const struct retro_variable vars_dual[] = {
{ "sameduck_link", "Link cable emulation; enabled|disabled" },
/*{ "sameduck_ir", "Infrared Sensor Emulation; disabled|enabled" },*/
{ "sameduck_screen_layout", "Screen layout; top-down|left-right" },
{ "sameduck_audio_output", "Audio output; Game Boy #1|Game Boy #2" },
{ "sameduck_model_1", "Emulated model for Game Boy #1 (Restart game); Auto|Game Boy|Game Boy Color|Game Boy Advance" },
{ "sameduck_model_2", "Emulated model for Game Boy #2 (Restart game); Auto|Game Boy|Game Boy Color|Game Boy Advance" },
{ "sameduck_color_correction_mode_1", "Color correction for Game Boy #1; emulate hardware|preserve brightness|reduce contrast|off|correct curves" },
{ "sameduck_color_correction_mode_2", "Color correction for Game Boy #2; emulate hardware|preserve brightness|reduce contrast|off|correct curves" },
{ "sameduck_high_pass_filter_mode_1", "High-pass filter for Game Boy #1; accurate|remove dc offset|off" },
{ "sameduck_high_pass_filter_mode_2", "High-pass filter for Game Boy #2; accurate|remove dc offset|off" },
{ "sameduck_rumble_1", "Enable rumble for Game Boy #1; rumble-enabled games|all games|never" },
{ "sameduck_rumble_2", "Enable rumble for Game Boy #2; rumble-enabled games|all games|never" },
{ "sameduck_audio_output", "Audio output; Mega Duck #1|Mega Duck #2" },
{ "sameduck_high_pass_filter_mode_1", "High-pass filter for Mega Duck #1; accurate|remove dc offset|off" },
{ "sameduck_high_pass_filter_mode_2", "High-pass filter for Mega Duck #2; accurate|remove dc offset|off" },
{ "sameduck_rumble_1", "Enable rumble for Mega Duck #1; all games|never" },
{ "sameduck_rumble_2", "Enable rumble for Mega Duck #2; all games|never" },
{ NULL }
};
@ -263,7 +249,7 @@ static const struct retro_subsystem_rom_info gb_roms[] = {
};
static const struct retro_subsystem_info subsystems[] = {
{ "2 Player Game Boy Link", "gb_link_2p", gb_roms, 2, RETRO_GAME_TYPE_GAMEBOY_LINK_2P },
{ "2 Player Mega Duck Link", "gb_link_2p", gb_roms, 2, RETRO_GAME_TYPE_GAMEBOY_LINK_2P },
{ NULL },
};
@ -360,52 +346,6 @@ static void set_link_cable_state(bool state)
}
}
static void boot_rom_load(GB_gameboy_t *gb, GB_boot_rom_t type)
{
const char *model_name = (char *[]){
[GB_BOOT_ROM_DMG0] = "dmg0",
[GB_BOOT_ROM_DMG] = "dmg",
[GB_BOOT_ROM_MGB] = "mgb",
[GB_BOOT_ROM_SGB] = "sgb",
[GB_BOOT_ROM_SGB2] = "sgb2",
[GB_BOOT_ROM_CGB0] = "cgb0",
[GB_BOOT_ROM_CGB] = "cgb",
[GB_BOOT_ROM_AGB] = "agb",
}[type];
const uint8_t *boot_code = (const unsigned char *[])
{
[GB_BOOT_ROM_DMG0] = dmg_boot, // dmg0 not implemented yet
[GB_BOOT_ROM_DMG] = dmg_boot,
[GB_BOOT_ROM_MGB] = dmg_boot, // mgb not implemented yet
[GB_BOOT_ROM_SGB] = sgb_boot,
[GB_BOOT_ROM_SGB2] = sgb2_boot,
[GB_BOOT_ROM_CGB0] = cgb_boot, // cgb0 not implemented yet
[GB_BOOT_ROM_CGB] = cgb_boot,
[GB_BOOT_ROM_AGB] = agb_boot,
}[type];
unsigned boot_length = (unsigned []){
[GB_BOOT_ROM_DMG0] = dmg_boot_length, // dmg0 not implemented yet
[GB_BOOT_ROM_DMG] = dmg_boot_length,
[GB_BOOT_ROM_MGB] = dmg_boot_length, // mgb not implemented yet
[GB_BOOT_ROM_SGB] = sgb_boot_length,
[GB_BOOT_ROM_SGB2] = sgb2_boot_length,
[GB_BOOT_ROM_CGB0] = cgb_boot_length, // cgb0 not implemented yet
[GB_BOOT_ROM_CGB] = cgb_boot_length,
[GB_BOOT_ROM_AGB] = agb_boot_length,
}[type];
char buf[256];
snprintf(buf, sizeof(buf), "%s%c%s_boot.bin", retro_system_directory, slash, model_name);
log_cb(RETRO_LOG_INFO, "Initializing as model: %s\n", model_name);
log_cb(RETRO_LOG_INFO, "Loading boot image: %s\n", buf);
if (GB_load_boot_rom(gb, buf)) {
GB_load_boot_rom_from_buffer(gb, boot_code, boot_length);
}
}
static void retro_set_memory_maps(void)
{
struct retro_memory_descriptor descs[11];
@ -491,8 +431,6 @@ static void init_for_current_model(unsigned id)
GB_init(&gameboy[i], libretro_to_internal_model[effective_model]);
}
GB_set_boot_rom_load_callback(&gameboy[i], boot_rom_load);
/* When running multiple devices they are assumed to use the same resolution */
GB_set_pixels_output(&gameboy[i],