Compare commits
85 Commits
master
...
gtk3-gb-sr
Author | SHA1 | Date | |
---|---|---|---|
d57efec2d3 | |||
d63560d3c1 | |||
a39937aeb2 | |||
7ce7c8c404 | |||
6a15ff582e | |||
f74b1cd3d9 | |||
825786210a | |||
accaedbdac | |||
7dbd0e18f9 | |||
7e9ebde585 | |||
45e62a2f26 | |||
1d7034fb88 | |||
add54953c6 | |||
6ea36bbc39 | |||
f6838a901c | |||
bdb22e7034 | |||
2887110e99 | |||
116ea7d0d7 | |||
f7beeb9c98 | |||
f46d35528b | |||
65fe7f2f9c | |||
008db16fb5 | |||
b6c949fa82 | |||
8c3154a061 | |||
3fe57f976c | |||
4042b7f38c | |||
4075a13b61 | |||
4ac7c0dd0d | |||
6594edf1bf | |||
89080ef47e | |||
a08e1d1c83 | |||
8780aafe99 | |||
15d949e338 | |||
c00946ea2e | |||
e42d16290d | |||
9b360ef292 | |||
836cb024b5 | |||
b142227577 | |||
5b79094293 | |||
53934aedec | |||
16c040d2d2 | |||
8f9d24c1e8 | |||
b428995126 | |||
a53d7f1e92 | |||
29ce04ff8a | |||
a1b464bd23 | |||
9224b3f2ac | |||
6a9ca41970 | |||
df9189448d | |||
10ac1bd0a5 | |||
e1a1c3efbd | |||
e38b0e40fa | |||
ba82da5f49 | |||
94e1b397ca | |||
36299d124a | |||
bf2dc12fb9 | |||
0b3ad8bcfd | |||
3de0d8fb6a | |||
0270ed336e | |||
cf0bfc6076 | |||
a032722e84 | |||
bf8ac1f00d | |||
517ad73e64 | |||
8e8e576460 | |||
8d0526fd1f | |||
c27e266cda | |||
8ce3b02833 | |||
a83d6eca5b | |||
6c5dfece40 | |||
a243325b8c | |||
023d43e893 | |||
90c0447703 | |||
0d07a389ca | |||
fc81729533 | |||
1f7cd57df8 | |||
3984359008 | |||
959f0cc88e | |||
6e263cd22a | |||
f3f1bf293a | |||
d6a1555290 | |||
f1d0344672 | |||
62bfe0cef9 | |||
6e65945c35 | |||
4d4d272a5c | |||
bba05ac5b9 |
4
.gitattributes
vendored
@ -1,7 +1,3 @@
|
|||||||
# Always use LF line endings for shaders
|
|
||||||
*.fsh text eol=lf
|
|
||||||
*.metal text eol=lf
|
|
||||||
|
|
||||||
HexFiend/* linguist-vendored
|
HexFiend/* linguist-vendored
|
||||||
*.inc linguist-language=C
|
*.inc linguist-language=C
|
||||||
Core/*.h linguist-language=C
|
Core/*.h linguist-language=C
|
||||||
|
12
.github/actions/sanity_tests.sh
vendored
@ -1,10 +1,10 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
./build/bin/tester/sameboy_tester --jobs 5 \
|
./build/bin/tester/sameboy_tester --jobs 5 \
|
||||||
--length 45 .github/actions/cgb_sound.gb \
|
--length 40 .github/actions/cgb_sound.gb \
|
||||||
--length 10 .github/actions/cgb-acid2.gbc \
|
--length 10 .github/actions/cgb-acid2.gbc \
|
||||||
--length 10 .github/actions/dmg-acid2.gb \
|
--length 10 .github/actions/dmg-acid2.gb \
|
||||||
--dmg --length 45 .github/actions/dmg_sound-2.gb \
|
--dmg --length 40 .github/actions/dmg_sound-2.gb \
|
||||||
--dmg --length 20 .github/actions/oam_bug-2.gb
|
--dmg --length 20 .github/actions/oam_bug-2.gb
|
||||||
|
|
||||||
mv .github/actions/dmg{,-mode}-acid2.bmp
|
mv .github/actions/dmg{,-mode}-acid2.bmp
|
||||||
@ -15,18 +15,18 @@ mv .github/actions/dmg{,-mode}-acid2.bmp
|
|||||||
set +e
|
set +e
|
||||||
|
|
||||||
FAILED_TESTS=`
|
FAILED_TESTS=`
|
||||||
shasum .github/actions/*.bmp | grep -E -v \(\
|
shasum .github/actions/*.bmp | grep -q -E -v \(\
|
||||||
5283564df0cf5bb78a7a90aff026c1a4692fd39e\ \ .github/actions/cgb-acid2.bmp\|\
|
44ce0c7d49254df0637849c9155080ac7dc3ef3d\ \ .github/actions/cgb-acid2.bmp\|\
|
||||||
dbcc438dcea13b5d1b80c5cd06bda2592cc5d9e0\ \ .github/actions/cgb_sound.bmp\|\
|
dbcc438dcea13b5d1b80c5cd06bda2592cc5d9e0\ \ .github/actions/cgb_sound.bmp\|\
|
||||||
0caadf9634e40247ae9c15ff71992e8f77bbf89e\ \ .github/actions/dmg-acid2.bmp\|\
|
0caadf9634e40247ae9c15ff71992e8f77bbf89e\ \ .github/actions/dmg-acid2.bmp\|\
|
||||||
a732077f98f43d9231453b1764d9f797a836924d\ \ .github/actions/dmg-mode-acid2.bmp\|\
|
c50daed36c57a8170ff362042694786676350997\ \ .github/actions/dmg-mode-acid2.bmp\|\
|
||||||
c9e944b7e01078bdeba1819bc2fa9372b111f52d\ \ .github/actions/dmg_sound-2.bmp\|\
|
c9e944b7e01078bdeba1819bc2fa9372b111f52d\ \ .github/actions/dmg_sound-2.bmp\|\
|
||||||
f0172cc91867d3343fbd113a2bb98100074be0de\ \ .github/actions/oam_bug-2.bmp\
|
f0172cc91867d3343fbd113a2bb98100074be0de\ \ .github/actions/oam_bug-2.bmp\
|
||||||
\)`
|
\)`
|
||||||
|
|
||||||
if [ -n "$FAILED_TESTS" ] ; then
|
if [ -n "$FAILED_TESTS" ] ; then
|
||||||
echo "Failed the following tests:"
|
echo "Failed the following tests:"
|
||||||
echo $FAILED_TESTS | tr " " "\n" | grep -o -E "[^/]+\.bmp" | sed s/.bmp// | sort
|
echo $FAILED_TESTS | tr " " "\n" | grep -q -o -E "[^/]+\.bmp" | sed s/.bmp// | sort
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
2
.github/workflows/sanity.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-latest, ubuntu-latest, ubuntu-18.04]
|
os: [macos-latest, ubuntu-latest, ubuntu-16.04]
|
||||||
cc: [gcc, clang]
|
cc: [gcc, clang]
|
||||||
include:
|
include:
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
|
9
.gitignore
vendored
@ -1 +1,10 @@
|
|||||||
build
|
build
|
||||||
|
|
||||||
|
# intermediate source files generated at build time
|
||||||
|
gtk3/sameboy-gtk3-resources.c
|
||||||
|
gtk3/resources/gtk3/
|
||||||
|
gtk3/resources/ui/#*#
|
||||||
|
|
||||||
|
# temporary backup file
|
||||||
|
*.*~
|
||||||
|
|
||||||
|
217
BESS.md
@ -1,217 +0,0 @@
|
|||||||
# BESS – Best Effort Save State 1.0
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
BESS is a save state format specification designed to allow different emulators, as well as majorly different versions of the same emulator, to import save states from other BESS-compliant save states. BESS works by appending additional, implementation-agnostic information about the emulation state. This allows a single save state file to be read as both a fully-featured, implementation specific save state which includes detailed timing information; and as a portable "best effort" save state that represents a state accurately enough to be restored in casual use-cases.
|
|
||||||
|
|
||||||
## Specification
|
|
||||||
|
|
||||||
Every integer used in the BESS specification is stored in Little Endian encoding.
|
|
||||||
|
|
||||||
### BESS footer
|
|
||||||
|
|
||||||
BESS works by appending a detectable footer at the end of an existing save state format. The footer uses the following format:
|
|
||||||
|
|
||||||
| Offset from end of file | Content |
|
|
||||||
|-------------------------|-------------------------------------------------------|
|
|
||||||
| -8 | Offset to the first BESS Block, from the file's start |
|
|
||||||
| -4 | The ASCII string 'BESS' |
|
|
||||||
|
|
||||||
### BESS blocks
|
|
||||||
|
|
||||||
BESS uses a block format where each block contains the following header:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|---------------------------------------|
|
|
||||||
| 0 | A four-letter ASCII identifier |
|
|
||||||
| 4 | Length of the block, excluding header |
|
|
||||||
|
|
||||||
Every block is followed by another block, until the END block is reached. If an implementation encounters an unsupported block, it should be completely ignored (Should not have any effect and should not trigger a failure).
|
|
||||||
|
|
||||||
#### NAME block
|
|
||||||
|
|
||||||
The NAME block uses the `'NAME'` identifier, and is an optional block that contains the name of the emulator that created this save state. While optional, it is highly recommended to be included in every implementation – it allows the user to know which emulator and version is compatible with the native save state format contained in this file. When used, this block should come first.
|
|
||||||
|
|
||||||
The length of the NAME block is variable, and it only contains the name and version of the originating emulator in ASCII.
|
|
||||||
|
|
||||||
|
|
||||||
#### INFO block
|
|
||||||
|
|
||||||
The INFO block uses the `'INFO'` identifier, and is an optional block that contains information about the ROM this save state originates from. When used, this block should come before `CORE` but after `NAME`. This block is 0x12 bytes long, and it follows this structure:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|--------------------------------------------------|
|
|
||||||
| 0x00 | Bytes 0x134-0x143 from the ROM (Title) |
|
|
||||||
| 0x10 | Bytes 0x14E-0x14F from the ROM (Global checksum) |
|
|
||||||
|
|
||||||
#### CORE block
|
|
||||||
|
|
||||||
The CORE block uses the `'CORE'` identifier, and is a required block that contains both core state information, as well as basic information about the BESS version used. This block must be the first block, unless the `NAME` or `INFO` blocks exist then it must come directly after them. An implementation should not enforce block order on blocks unknown to it for future compatibility.
|
|
||||||
|
|
||||||
The length of the CORE block is 0xD0 bytes, but implementations are expected to ignore any excess bytes. Following the BESS block header, the structure is as follows:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|----------------------------------------|
|
|
||||||
| 0x00 | Major BESS version as a 16-bit integer |
|
|
||||||
| 0x02 | Minor BESS version as a 16-bit integer |
|
|
||||||
|
|
||||||
Both major and minor versions should be 1. Implementations are expected to reject incompatible majors, but still attempt to read newer minor versions.
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|----------------------------------------|
|
|
||||||
| 0x04 | A four-character ASCII model identifier |
|
|
||||||
|
|
||||||
BESS uses a four-character string to identify Game Boy models:
|
|
||||||
|
|
||||||
* The first letter represents mutually-incompatible families of models and is required. The allowed values are `'G'` for the original Game Boy family, `'S'` for the Super Game Boy family, and `'C'` for the Game Boy Color and Advance family.
|
|
||||||
* The second letter represents a specific model within the family, and is optional (If an implementation does not distinguish between specific models in a family, a space character may be used). The allowed values for family G are `'D'` for DMG and `'M'` for MGB; the allowed values for family S are `'N'` for NTSC, `'P'` for PAL, and `'2'` for SGB2; and the allowed values for family C are `'C'` for CGB, and `'A'` for the various GBA line models.
|
|
||||||
* The third letter represents a specific CPU revision within a model, and is optional (If an implementation does not distinguish between revisions, a space character may be used). The allowed values for model GD (DMG) are `'0'` and `'A'`, through `'C'`; the allowed values for model CC (CGB) are `'0'` and `'A'`, through `'E'`; the allowed values for model CA (AGB, AGS, GBP) are `'0'`, `'A'` and `'B'`; and for every other model this value must be a space character.
|
|
||||||
* The last character is used for padding and must be a space character.
|
|
||||||
|
|
||||||
For example; `'GD '` represents a DMG of an unspecified revision, `'S '` represents some model of the SGB family, and `'CCE '` represent a CGB using CPU revision E.
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|--------------------------------------------------------|
|
|
||||||
| 0x08 | The value of the PC register |
|
|
||||||
| 0x0A | The value of the AF register |
|
|
||||||
| 0x0C | The value of the BC register |
|
|
||||||
| 0x0E | The value of the DE register |
|
|
||||||
| 0x10 | The value of the HL register |
|
|
||||||
| 0x12 | The value of the SP register |
|
|
||||||
| 0x14 | The value of IME (0 or 1) |
|
|
||||||
| 0x15 | The value of the IE register |
|
|
||||||
| 0x16 | Execution state (0 = running; 1 = halted; 2 = stopped) |
|
|
||||||
| 0x17 | Reserved, must be 0 |
|
|
||||||
| 0x18 | The values of every memory-mapped register (128 bytes) |
|
|
||||||
|
|
||||||
The values of memory-mapped registers should be written 'as-is' to memory as if the actual ROM wrote them, with the following exceptions and note:
|
|
||||||
* Unused registers have Don't-Care values which should be ignored
|
|
||||||
* Unused register bits have Don't-Care values which should be ignored
|
|
||||||
* If the model is CGB or newer, the value of KEY0 (FF4C) must be valid as it determines DMG mode
|
|
||||||
* Bit 2 determines DMG mode. A value of 0x04 usually denotes DMG mode, while a value of `0x80` usually denotes CGB mode.
|
|
||||||
* Object priority is derived from KEY0 (FF4C) instead of OPRI (FF6C) because OPRI can be modified after booting, but only the value of OPRI during boot ROM execution takes effect
|
|
||||||
* If a register doesn't exist on the emulated model (For example, KEY0 (FF4C) on a DMG), its value should be ignored.
|
|
||||||
* BANK (FF50) should be 0 if the boot ROM is still mapped, and 1 otherwise, and must be valid.
|
|
||||||
* Implementations should not start a serial transfer when writing the value of SB
|
|
||||||
* Similarly, no value of NRx4 should trigger a sound pulse on save state load
|
|
||||||
* And similarly again, implementations should not trigger DMA transfers when writing the values of DMA or HDMA5
|
|
||||||
* The value store for DIV will be used to set the internal divisor to `DIV << 8`
|
|
||||||
* Implementation should apply care when ordering the write operations (For example, writes to NR52 must come before writes to the other APU registers)
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|--------------------------------------------------------------------|
|
|
||||||
| 0x98 | The size of RAM (32-bit integer) |
|
|
||||||
| 0x9C | The offset of RAM from file start (32-bit integer) |
|
|
||||||
| 0xA0 | The size of VRAM (32-bit integer) |
|
|
||||||
| 0xA4 | The offset of VRAM from file start (32-bit integer) |
|
|
||||||
| 0xA8 | The size of MBC RAM (32-bit integer) |
|
|
||||||
| 0xAC | The offset of MBC RAM from file start (32-bit integer) |
|
|
||||||
| 0xB0 | The size of OAM (=0xA0, 32-bit integer) |
|
|
||||||
| 0xB4 | The offset of OAM from file start (32-bit integer) |
|
|
||||||
| 0xB8 | The size of HRAM (=0x7F, 32-bit integer) |
|
|
||||||
| 0xBC | The offset of HRAM from file start (32-bit integer) |
|
|
||||||
| 0xC0 | The size of background palettes (=0x40 or 0, 32-bit integer) |
|
|
||||||
| 0xC4 | The offset of background palettes from file start (32-bit integer) |
|
|
||||||
| 0xC8 | The size of object palettes (=0x40 or 0, 32-bit integer) |
|
|
||||||
| 0xCC | The offset of object palettes from file start (32-bit integer) |
|
|
||||||
|
|
||||||
The contents of large buffers are stored outside of BESS structure so data from an implementation's native save state format can be reused. The offsets are absolute offsets from the save state file's start. Background and object palette sizes must be 0 for models prior to Game Boy Color.
|
|
||||||
|
|
||||||
An implementation needs handle size mismatches gracefully. For example, if too large MBC RAM size is specified, the superfluous data should be ignored. On the other hand, if a too small VRAM size is specified (For example, if it's a save state from an emulator emulating a CGB in DMG mode, and it didn't save the second CGB VRAM bank), the implementation is expected to set that extra bank to all zeros.
|
|
||||||
|
|
||||||
#### XOAM block
|
|
||||||
|
|
||||||
The XOAM block uses the `'XOAM'` identifier, and is an optional block that contains the data of extra OAM (addresses `0xFEA0-0xFEFF`). This block length must be `0x60`. Implementations that do not emulate this extra range are free to ignore the excess bytes, and to not create this block.
|
|
||||||
|
|
||||||
|
|
||||||
#### MBC block
|
|
||||||
|
|
||||||
The MBC block uses the `'MBC '` identifier, and is an optional block that is only used when saving states of ROMs that use an MBC. The length of this block is variable and must be divisible by 3.
|
|
||||||
|
|
||||||
This block contains an MBC-specific number of 3-byte-long pairs that represent the values of each MBC register. For example, for MBC5 the contents would look like:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|---------------------------------------|
|
|
||||||
| 0x0 | The value 0x0000 as a 16-bit integer |
|
|
||||||
| 0x2 | 0x0A if RAM is enabled, 0 otherwise |
|
|
||||||
| 0x3 | The value 0x2000 as a 16-bit integer |
|
|
||||||
| 0x5 | The lower 8 bits of the ROM bank |
|
|
||||||
| 0x6 | The value 0x3000 as a 16-bit integer |
|
|
||||||
| 0x8 | The bit 9 of the ROM bank |
|
|
||||||
| 0x9 | The value 0x4000 as a 16-bit integer |
|
|
||||||
| 0xB | The current RAM bank |
|
|
||||||
|
|
||||||
An implementation should parse this block as a series of writes to be made. Values outside the `0x0000-0x7FFF` and `0xA000-0xBFFF` ranges are not allowed. Implementations must perform the writes in order (i.e. not reverse, sorted, or any other transformation on their order)
|
|
||||||
|
|
||||||
#### RTC block
|
|
||||||
The RTC block uses the `'RTC '` identifier, and is an optional block that is used while emulating an MBC3 with an RTC. The contents of this block are identical to 64-bit RTC saves from VBA, which are also used by SameBoy and different emulators such as BGB.
|
|
||||||
|
|
||||||
The length of this block is 0x30 bytes long and it follows the following structure:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|------------------------------------------------------------------------|
|
|
||||||
| 0x00 | Current seconds (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x04 | Current minutes (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x08 | Current hours (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x0C | Current days (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x10 | Current high/overflow/running (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x14 | Latched seconds (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x18 | Latched minutes (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x1C | Latched hours (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x20 | Latched days (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x24 | Latched high/overflow/running (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x28 | UNIX timestamp at the time of the save state (64-bit) |
|
|
||||||
|
|
||||||
#### HUC3 block
|
|
||||||
The HUC3 block uses the `'HUC3'` identifier, and is an optional block that is used while emulating an HuC3 cartridge to store RTC and alarm information. The contents of this block are identical to HuC3 RTC saves from SameBoy.
|
|
||||||
|
|
||||||
The length of this block is 0x11 bytes long and it follows the following structure:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|-------------------------------------------------------|
|
|
||||||
| 0x00 | UNIX timestamp at the time of the save state (64-bit) |
|
|
||||||
| 0x08 | RTC minutes (16-bit) |
|
|
||||||
| 0x0A | RTC days (16-bit) |
|
|
||||||
| 0x0C | Scheduled alarm time minutes (16-bit) |
|
|
||||||
| 0x0E | Scheduled alarm time days (16-bit) |
|
|
||||||
| 0x10 | Alarm enabled flag (8-bits, either 0 or 1) |
|
|
||||||
|
|
||||||
#### SGB block
|
|
||||||
|
|
||||||
The SGB block uses the `'SGB '` identifier, and is an optional block that is only used while emulating an SGB or SGB2 *and* SGB commands enabled. Implementations must not save this block on other models or when SGB commands are disabled, and should assume SGB commands are disabled if this block is missing.
|
|
||||||
|
|
||||||
The length of this block is 0x39 bytes, but implementations should allow and ignore excess data in this block for extensions. The block follows the following structure:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|--------------------------------------------------------------------------------------------------------------------------|
|
|
||||||
| 0x00 | The size of the border tile data (=0x2000, 32-bit integer) |
|
|
||||||
| 0x04 | The offset of the border tile data (SNES tile format, 32-bit integer) |
|
|
||||||
| 0x08 | The size of the border tilemap (=0x800, 32-bit integer) |
|
|
||||||
| 0x0C | The offset of the border tilemap (LE 16-bit sequences, 32-bit integer) |
|
|
||||||
| 0x10 | The size of the border palettes (=0x80, 32-bit integer) |
|
|
||||||
| 0x14 | The offset of the border palettes (LE 16-bit sequences, 32-bit integer) |
|
|
||||||
| 0x18 | The size of active colorization palettes (=0x20, 32-bit integer) |
|
|
||||||
| 0x1C | The offset of the active colorization palettes (LE 16-bit sequences, 32-bit integer) |
|
|
||||||
| 0x20 | The size of RAM colorization palettes (=0x1000, 32-bit integer) |
|
|
||||||
| 0x24 | The offset of the RAM colorization palettes (LE 16-bit sequences, 32-bit integer) |
|
|
||||||
| 0x28 | The size of the attribute map (=0x168, 32-bit integer) |
|
|
||||||
| 0x2C | The offset of the attribute map (32-bit integer) |
|
|
||||||
| 0x30 | The size of the attribute files (=0xfd2, 32-bit integer) |
|
|
||||||
| 0x34 | The offset of the attribute files (32-bit integer) |
|
|
||||||
| 0x38 | Multiplayer status (1 byte); high nibble is player count (1, 2 or 4), low nibble is current player (Where Player 1 is 0) |
|
|
||||||
|
|
||||||
If only some of the size-offset pairs are available (for example, partial HLE SGB implementation), missing fields are allowed to have 0 as their size, and implementations are expected to fall back to a sane default.
|
|
||||||
|
|
||||||
#### END block
|
|
||||||
The END block uses the `'END '` identifier, and is a required block that marks the end of BESS data. Naturally, it must be the last block. The length of the END block must be 0.
|
|
||||||
|
|
||||||
## Validation and Failures
|
|
||||||
|
|
||||||
Other than previously specified required fail conditions, an implementation is free to decide what format errors should abort the loading of a save file. Structural errors (e.g. a block with an invalid length, a file offset that is outside the file's range, or a missing END block) should be considered as irrecoverable errors. Other errors that are considered fatal by SameBoy's implementation:
|
|
||||||
* Duplicate CORE block
|
|
||||||
* A known block, other than NAME, appearing before CORE
|
|
||||||
* An invalid length for the XOAM, RTC, SGB or HUC3 blocks
|
|
||||||
* An invalid length of MBC (not a multiple of 3)
|
|
||||||
* A write outside the $0000-$7FFF and $A000-$BFFF ranges in the MBC block
|
|
||||||
* An SGB block on a save state targeting another model
|
|
||||||
* An END block with non-zero length
|
|
@ -1,2 +0,0 @@
|
|||||||
CGB0 EQU 1
|
|
||||||
include "cgb_boot.asm"
|
|
@ -23,16 +23,13 @@ Start:
|
|||||||
dec c
|
dec c
|
||||||
jr nz, .clearOAMLoop
|
jr nz, .clearOAMLoop
|
||||||
|
|
||||||
IF !DEF(CGB0)
|
|
||||||
; Init waveform
|
; Init waveform
|
||||||
ld c, $10
|
ld c, $10
|
||||||
ld hl, $FF30
|
|
||||||
.waveformLoop
|
.waveformLoop
|
||||||
ldi [hl], a
|
ldi [hl], a
|
||||||
cpl
|
cpl
|
||||||
dec c
|
dec c
|
||||||
jr nz, .waveformLoop
|
jr nz, .waveformLoop
|
||||||
ENDC
|
|
||||||
|
|
||||||
; Clear chosen input palette
|
; Clear chosen input palette
|
||||||
ldh [InputPalette], a
|
ldh [InputPalette], a
|
||||||
@ -47,6 +44,7 @@ ENDC
|
|||||||
ldh [$25], a
|
ldh [$25], a
|
||||||
ld a, $77
|
ld a, $77
|
||||||
ldh [$24], a
|
ldh [$24], a
|
||||||
|
ld hl, $FF30
|
||||||
|
|
||||||
; Init BG palette
|
; Init BG palette
|
||||||
ld a, $fc
|
ld a, $fc
|
||||||
@ -192,9 +190,10 @@ ENDC
|
|||||||
IF !DEF(FAST)
|
IF !DEF(FAST)
|
||||||
call DoIntroAnimation
|
call DoIntroAnimation
|
||||||
|
|
||||||
ld a, 48 ; frames to wait after playing the chime
|
ld a, 45
|
||||||
ldh [WaitLoopCounter], a
|
ldh [WaitLoopCounter], a
|
||||||
ld b, 4 ; frames to wait before playing the chime
|
; Wait ~0.75 seconds
|
||||||
|
ld b, a
|
||||||
call WaitBFrames
|
call WaitBFrames
|
||||||
|
|
||||||
; Play first sound
|
; Play first sound
|
||||||
@ -272,7 +271,7 @@ TitleChecksums:
|
|||||||
db $A2 ; STAR WARS-NOA
|
db $A2 ; STAR WARS-NOA
|
||||||
db $49 ;
|
db $49 ;
|
||||||
db $4E ; WAVERACE
|
db $4E ; WAVERACE
|
||||||
db $43 ;
|
db $43 | $80 ;
|
||||||
db $68 ; LOLO2
|
db $68 ; LOLO2
|
||||||
db $E0 ; YOSHI'S COOKIE
|
db $E0 ; YOSHI'S COOKIE
|
||||||
db $8B ; MYSTIC QUEST
|
db $8B ; MYSTIC QUEST
|
||||||
@ -330,103 +329,101 @@ FirstChecksumWithDuplicate:
|
|||||||
ChecksumsEnd:
|
ChecksumsEnd:
|
||||||
|
|
||||||
PalettePerChecksum:
|
PalettePerChecksum:
|
||||||
palette_index: MACRO ; palette, flags
|
; | $80 means game requires DMG boot tilemap
|
||||||
db ((\1)) | (\2) ; | $80 means game requires DMG boot tilemap
|
db 0 ; Default Palette
|
||||||
ENDM
|
db 4 ; ALLEY WAY
|
||||||
palette_index 0, 0 ; Default Palette
|
db 5 ; YAKUMAN
|
||||||
palette_index 4, 0 ; ALLEY WAY
|
db 35 ; BASEBALL, (Game and Watch 2)
|
||||||
palette_index 5, 0 ; YAKUMAN
|
db 34 ; TENNIS
|
||||||
palette_index 35, 0 ; BASEBALL, (Game and Watch 2)
|
db 3 ; TETRIS
|
||||||
palette_index 34, 0 ; TENNIS
|
db 31 ; QIX
|
||||||
palette_index 3, 0 ; TETRIS
|
db 15 ; DR.MARIO
|
||||||
palette_index 31, 0 ; QIX
|
db 10 ; RADARMISSION
|
||||||
palette_index 15, 0 ; DR.MARIO
|
db 5 ; F1RACE
|
||||||
palette_index 10, 0 ; RADARMISSION
|
db 19 ; YOSSY NO TAMAGO
|
||||||
palette_index 5, 0 ; F1RACE
|
db 36 ;
|
||||||
palette_index 19, 0 ; YOSSY NO TAMAGO
|
db 7 | $80 ; X
|
||||||
palette_index 36, 0 ;
|
db 37 ; MARIOLAND2
|
||||||
palette_index 7, $80 ; X
|
db 30 ; YOSSY NO COOKIE
|
||||||
palette_index 37, 0 ; MARIOLAND2
|
db 44 ; ZELDA
|
||||||
palette_index 30, 0 ; YOSSY NO COOKIE
|
db 21 ;
|
||||||
palette_index 44, 0 ; ZELDA
|
db 32 ;
|
||||||
palette_index 21, 0 ;
|
db 31 ; TETRIS FLASH
|
||||||
palette_index 32, 0 ;
|
db 20 ; DONKEY KONG
|
||||||
palette_index 31, 0 ; TETRIS FLASH
|
db 5 ; MARIO'S PICROSS
|
||||||
palette_index 20, 0 ; DONKEY KONG
|
db 33 ;
|
||||||
palette_index 5, 0 ; MARIO'S PICROSS
|
db 13 ; POKEMON RED, (GAMEBOYCAMERA G)
|
||||||
palette_index 33, 0 ;
|
db 14 ; POKEMON GREEN
|
||||||
palette_index 13, 0 ; POKEMON RED, (GAMEBOYCAMERA G)
|
db 5 ; PICROSS 2
|
||||||
palette_index 14, 0 ; POKEMON GREEN
|
db 29 ; YOSSY NO PANEPON
|
||||||
palette_index 5, 0 ; PICROSS 2
|
db 5 ; KIRAKIRA KIDS
|
||||||
palette_index 29, 0 ; YOSSY NO PANEPON
|
db 18 ; GAMEBOY GALLERY
|
||||||
palette_index 5, 0 ; KIRAKIRA KIDS
|
db 9 ; POCKETCAMERA
|
||||||
palette_index 18, 0 ; GAMEBOY GALLERY
|
db 3 ;
|
||||||
palette_index 9, 0 ; POCKETCAMERA
|
db 2 ; BALLOON KID
|
||||||
palette_index 3, 0 ;
|
db 26 ; KINGOFTHEZOO
|
||||||
palette_index 2, 0 ; BALLOON KID
|
db 25 ; DMG FOOTBALL
|
||||||
palette_index 26, 0 ; KINGOFTHEZOO
|
db 25 ; WORLD CUP
|
||||||
palette_index 25, 0 ; DMG FOOTBALL
|
db 41 ; OTHELLO
|
||||||
palette_index 25, 0 ; WORLD CUP
|
db 42 ; SUPER RC PRO-AM
|
||||||
palette_index 41, 0 ; OTHELLO
|
db 26 ; DYNABLASTER
|
||||||
palette_index 42, 0 ; SUPER RC PRO-AM
|
db 45 ; BOY AND BLOB GB2
|
||||||
palette_index 26, 0 ; DYNABLASTER
|
db 42 ; MEGAMAN
|
||||||
palette_index 45, 0 ; BOY AND BLOB GB2
|
db 45 ; STAR WARS-NOA
|
||||||
palette_index 42, 0 ; MEGAMAN
|
db 36 ;
|
||||||
palette_index 45, 0 ; STAR WARS-NOA
|
db 38 ; WAVERACE
|
||||||
palette_index 36, 0 ;
|
db 26 ;
|
||||||
palette_index 38, 0 ; WAVERACE
|
db 42 ; LOLO2
|
||||||
palette_index 26, $80 ;
|
db 30 ; YOSHI'S COOKIE
|
||||||
palette_index 42, 0 ; LOLO2
|
db 41 ; MYSTIC QUEST
|
||||||
palette_index 30, 0 ; YOSHI'S COOKIE
|
db 34 ;
|
||||||
palette_index 41, 0 ; MYSTIC QUEST
|
db 34 ; TOPRANKINGTENNIS
|
||||||
palette_index 34, 0 ;
|
db 5 ; MANSELL
|
||||||
palette_index 34, 0 ; TOPRANKINGTENNIS
|
db 42 ; MEGAMAN3
|
||||||
palette_index 5, 0 ; MANSELL
|
db 6 ; SPACE INVADERS
|
||||||
palette_index 42, 0 ; MEGAMAN3
|
db 5 ; GAME&WATCH
|
||||||
palette_index 6, 0 ; SPACE INVADERS
|
db 33 ; DONKEYKONGLAND95
|
||||||
palette_index 5, 0 ; GAME&WATCH
|
db 25 ; ASTEROIDS/MISCMD
|
||||||
palette_index 33, 0 ; DONKEYKONGLAND95
|
db 42 ; STREET FIGHTER 2
|
||||||
palette_index 25, 0 ; ASTEROIDS/MISCMD
|
db 42 ; DEFENDER/JOUST
|
||||||
palette_index 42, 0 ; STREET FIGHTER 2
|
db 40 ; KILLERINSTINCT95
|
||||||
palette_index 42, 0 ; DEFENDER/JOUST
|
db 2 ; TETRIS BLAST
|
||||||
palette_index 40, 0 ; KILLERINSTINCT95
|
db 16 ; PINOCCHIO
|
||||||
palette_index 2, 0 ; TETRIS BLAST
|
db 25 ;
|
||||||
palette_index 16, 0 ; PINOCCHIO
|
db 42 ; BA.TOSHINDEN
|
||||||
palette_index 25, 0 ;
|
db 42 ; NETTOU KOF 95
|
||||||
palette_index 42, 0 ; BA.TOSHINDEN
|
db 5 ;
|
||||||
palette_index 42, 0 ; NETTOU KOF 95
|
db 0 ; TETRIS PLUS
|
||||||
palette_index 5, 0 ;
|
db 39 ; DONKEYKONGLAND 3
|
||||||
palette_index 0, 0 ; TETRIS PLUS
|
db 36 ;
|
||||||
palette_index 39, 0 ; DONKEYKONGLAND 3
|
db 22 ; SUPER MARIOLAND
|
||||||
palette_index 36, 0 ;
|
db 25 ; GOLF
|
||||||
palette_index 22, 0 ; SUPER MARIOLAND
|
db 6 ; SOLARSTRIKER
|
||||||
palette_index 25, 0 ; GOLF
|
db 32 ; GBWARS
|
||||||
palette_index 6, 0 ; SOLARSTRIKER
|
db 12 ; KAERUNOTAMENI
|
||||||
palette_index 32, 0 ; GBWARS
|
db 36 ;
|
||||||
palette_index 12, 0 ; KAERUNOTAMENI
|
db 11 ; POKEMON BLUE
|
||||||
palette_index 36, 0 ;
|
db 39 ; DONKEYKONGLAND
|
||||||
palette_index 11, 0 ; POKEMON BLUE
|
db 18 ; GAMEBOY GALLERY2
|
||||||
palette_index 39, 0 ; DONKEYKONGLAND
|
db 39 ; DONKEYKONGLAND 2
|
||||||
palette_index 18, 0 ; GAMEBOY GALLERY2
|
db 24 ; KID ICARUS
|
||||||
palette_index 39, 0 ; DONKEYKONGLAND 2
|
db 31 ; TETRIS2
|
||||||
palette_index 24, 0 ; KID ICARUS
|
db 50 ;
|
||||||
palette_index 31, 0 ; TETRIS2
|
db 17 ; MOGURANYA
|
||||||
palette_index 50, 0 ;
|
db 46 ;
|
||||||
palette_index 17, 0 ; MOGURANYA
|
db 6 ; GALAGA&GALAXIAN
|
||||||
palette_index 46, 0 ;
|
db 27 ; BT2RAGNAROKWORLD
|
||||||
palette_index 6, 0 ; GALAGA&GALAXIAN
|
db 0 ; KEN GRIFFEY JR
|
||||||
palette_index 27, 0 ; BT2RAGNAROKWORLD
|
db 47 ;
|
||||||
palette_index 0, 0 ; KEN GRIFFEY JR
|
db 41 ; MAGNETIC SOCCER
|
||||||
palette_index 47, 0 ;
|
db 41 ; VEGAS STAKES
|
||||||
palette_index 41, 0 ; MAGNETIC SOCCER
|
db 0 ;
|
||||||
palette_index 41, 0 ; VEGAS STAKES
|
db 0 ; MILLI/CENTI/PEDE
|
||||||
palette_index 0, 0 ;
|
db 19 ; MARIO & YOSHI
|
||||||
palette_index 0, 0 ; MILLI/CENTI/PEDE
|
db 34 ; SOCCER
|
||||||
palette_index 19, 0 ; MARIO & YOSHI
|
db 23 ; POKEBOM
|
||||||
palette_index 34, 0 ; SOCCER
|
db 18 ; G&W GALLERY
|
||||||
palette_index 23, 0 ; POKEBOM
|
db 29 ; TETRIS ATTACK
|
||||||
palette_index 18, 0 ; G&W GALLERY
|
|
||||||
palette_index 29, 0 ; TETRIS ATTACK
|
|
||||||
|
|
||||||
Dups4thLetterArray:
|
Dups4thLetterArray:
|
||||||
db "BEFAARBEKEK R-URAR INAILICE R"
|
db "BEFAARBEKEK R-URAR INAILICE R"
|
||||||
@ -476,7 +473,7 @@ ENDM
|
|||||||
palette_comb 17, 4, 13
|
palette_comb 17, 4, 13
|
||||||
raw_palette_comb 28 * 4 - 1, 0 * 4, 14 * 4
|
raw_palette_comb 28 * 4 - 1, 0 * 4, 14 * 4
|
||||||
raw_palette_comb 28 * 4 - 1, 4 * 4, 15 * 4
|
raw_palette_comb 28 * 4 - 1, 4 * 4, 15 * 4
|
||||||
raw_palette_comb 19 * 4, 23 * 4 - 1, 9 * 4
|
palette_comb 19, 22, 9
|
||||||
palette_comb 16, 28, 10
|
palette_comb 16, 28, 10
|
||||||
palette_comb 4, 23, 28
|
palette_comb 4, 23, 28
|
||||||
palette_comb 17, 22, 2
|
palette_comb 17, 22, 2
|
||||||
@ -919,10 +916,7 @@ EmulateDMG:
|
|||||||
call GetPaletteIndex
|
call GetPaletteIndex
|
||||||
bit 7, a
|
bit 7, a
|
||||||
call nz, LoadDMGTilemap
|
call nz, LoadDMGTilemap
|
||||||
res 7, a
|
and $7F
|
||||||
ld b, a
|
|
||||||
add b
|
|
||||||
add b
|
|
||||||
ld b, a
|
ld b, a
|
||||||
ldh a, [InputPalette]
|
ldh a, [InputPalette]
|
||||||
and a
|
and a
|
||||||
@ -982,7 +976,7 @@ GetPaletteIndex:
|
|||||||
|
|
||||||
; We might have a match, Do duplicate/4th letter check
|
; We might have a match, Do duplicate/4th letter check
|
||||||
ld a, l
|
ld a, l
|
||||||
sub FirstChecksumWithDuplicate - TitleChecksums + 1
|
sub FirstChecksumWithDuplicate - TitleChecksums
|
||||||
jr c, .match ; Does not have a duplicate, must be a match!
|
jr c, .match ; Does not have a duplicate, must be a match!
|
||||||
; Has a duplicate; check 4th letter
|
; Has a duplicate; check 4th letter
|
||||||
push hl
|
push hl
|
||||||
@ -1188,7 +1182,7 @@ ChangeAnimationPalette:
|
|||||||
call WaitFrame
|
call WaitFrame
|
||||||
call LoadPalettesFromHRAM
|
call LoadPalettesFromHRAM
|
||||||
; Delay the wait loop while the user is selecting a palette
|
; Delay the wait loop while the user is selecting a palette
|
||||||
ld a, 48
|
ld a, 45
|
||||||
ldh [WaitLoopCounter], a
|
ldh [WaitLoopCounter], a
|
||||||
pop de
|
pop de
|
||||||
pop bc
|
pop bc
|
||||||
|
@ -115,11 +115,7 @@ Start:
|
|||||||
call WaitBFrames
|
call WaitBFrames
|
||||||
|
|
||||||
; Set registers to match the original DMG boot
|
; Set registers to match the original DMG boot
|
||||||
IF DEF(MGB)
|
|
||||||
ld hl, $FFB0
|
|
||||||
ELSE
|
|
||||||
ld hl, $01B0
|
ld hl, $01B0
|
||||||
ENDC
|
|
||||||
push hl
|
push hl
|
||||||
pop af
|
pop af
|
||||||
ld hl, $014D
|
ld hl, $014D
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
MGB EQU 1
|
|
||||||
include "dmg_boot.asm"
|
|
@ -13,18 +13,6 @@ void opts(uint8_t byte, uint8_t *options)
|
|||||||
*(options++) = byte & (byte >> 1);
|
*(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()
|
int main()
|
||||||
{
|
{
|
||||||
static uint8_t source[0x4000];
|
static uint8_t source[0x4000];
|
||||||
@ -88,15 +76,15 @@ int main()
|
|||||||
if (bits >= 8) {
|
if (bits >= 8) {
|
||||||
uint8_t outctl = control >> (bits - 8);
|
uint8_t outctl = control >> (bits - 8);
|
||||||
assert(outctl != 1);
|
assert(outctl != 1);
|
||||||
write_all(STDOUT_FILENO, &outctl, 1);
|
write(STDOUT_FILENO, &outctl, 1);
|
||||||
write_all(STDOUT_FILENO, literals, literals_size);
|
write(STDOUT_FILENO, literals, literals_size);
|
||||||
bits -= 8;
|
bits -= 8;
|
||||||
control &= (1 << bits) - 1;
|
control &= (1 << bits) - 1;
|
||||||
literals_size = 0;
|
literals_size = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint8_t end_byte = 1;
|
uint8_t end_byte = 1;
|
||||||
write_all(STDOUT_FILENO, &end_byte, 1);
|
write(STDOUT_FILENO, &end_byte, 1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
# SameBoy Coding and Contribution Guidelines
|
|
||||||
|
|
||||||
## Issues
|
|
||||||
|
|
||||||
GitHub Issues are the most effective way to report a bug or request a feature in SameBoy. When reporting a bug, make sure you use the latest stable release, and make sure you mention the SameBoy frontend (Cocoa, SDL, Libretro) and operating system you're using. If you're using Linux/BSD/etc, or you build your own copy of SameBoy for another reason, give as much details as possible on your environment.
|
|
||||||
|
|
||||||
If your bug involves a crash, please attach a crash log or a core dump. If you're using Linux/BSD/etc, or if you're using the Libretro core, please attach the `sameboy` binary (or `libretro_sameboy` library) in that case.
|
|
||||||
|
|
||||||
If your bug is a regression, it'd be extremely helpful if you can report the the first affected version. You get extra credits if you use `git bisect` to point the exact breaking commit.
|
|
||||||
|
|
||||||
If your bug is an emulation bug (Such as a failing test ROM), and you have access to a Game Boy you can test on, please confirm SameBoy is indeed behaving differently from hardware, and report both the emulated model and revision in SameBoy, and the hardware revision you're testing on.
|
|
||||||
|
|
||||||
If your issue is a feature request, demonstrating use cases can help me better prioritize it.
|
|
||||||
|
|
||||||
## Pull Requests
|
|
||||||
|
|
||||||
To allow quicker integration into SameBoy's master branch, contributors are asked to follow SameBoy's style and coding guidelines. Keep in mind that despite the seemingly strict guidelines, all pull requests are welcome – not following the guidelines does not mean your pull request will not be accepted, but it will require manual tweaks from my side for integrating.
|
|
||||||
|
|
||||||
### Languages and Compilers
|
|
||||||
|
|
||||||
SameBoy's core, SDL frontend, Libretro frontend, and automatic tester (Folders `Core`, `SDL` & `OpenDialog`, `libretro`, and `Tester`; respectively) are all written in C11. The Cocoa frontend, SameBoy's fork of Hex Fiend, JoyKit and the Quick Look previewer (Folders `Cocoa`, `HexFiend`, `JoyKit` and `QuickLook`; respectively) are all written in ARC-enabled Objective-C. The SameBoot ROMs (Under `BootROMs`) are written in rgbds-flavor SM83 assembly, with build tools in C11. The shaders (inside `Shaders`) are written in a polyglot GLSL and Metal style, with a few GLSL- and Metal-specific sources. The build system uses standalone Make, in the GNU flavor. Avoid adding new languages (C++, Swift, Python, CMake...) to any of the existing sub-projects.
|
|
||||||
|
|
||||||
SameBoy's main target compiler is Clang, but GCC is also supported when targeting Linux and Libretro. Other compilers (e.g. MSVC) are not supported, and unless there's a good reason, there's no need to go out of your way to add specific support for them. Extensions that are supported by both compilers (Such as `typeof`) may be used if it makes sense. It's OK if you can't test one of these compilers yourself; once you push a commit, the CI bot will let you know if you broke something.
|
|
||||||
|
|
||||||
### Third Party Libraries and Tools
|
|
||||||
|
|
||||||
Avoid adding new required dependencies; run-time and compile-time dependencies alike. Most importantly, avoid linking against GPL licensed libraries (LGPL libraries are fine), so SameBoy can retain its MIT license.
|
|
||||||
|
|
||||||
### Spacing, Indentation and Formatting
|
|
||||||
|
|
||||||
In all files and languages (Other than Makefiles when required), 4 spaces are used for indentation. Unix line endings (`\n`) are used exclusively, even in Windows-specific source files. (`\r` and `\t` shouldn't appear in any source file). Opening braces belong on the same line as their control flow directive, and on their own line when following a function prototype. The `else` keyword always starts on its own line. The `case` keyword is indented relative to its `switch` block, and the code inside a `case` is indented relative to its label. A control flow keyword should have a space between it and the following `(`, commas should follow a space, and operator (except `.` and `->`) should be surrounded by spaces.
|
|
||||||
|
|
||||||
Control flow statements must use `{}`, with the exception of `if` statements that only contain a single `break`, `continue`, or trivial `return` statements. If `{}`s are omitted, the statement must be on the same line as the `if` condition. Functions that do not have any argument must be specified as `(void)`, as mandated by the C standard. The `sizeof` and `typeof` operators should be used as if they're functions (With `()`). `*`, when used to declare pointer types (including functions that return pointers), and when used to dereference a pointer, is attached to the right side (The variable name) – not to the left, and not with spaces on both sides.
|
|
||||||
|
|
||||||
No strict limitations on a line's maximum width, but use your best judgement if you think a statement would benefit from an additional line break.
|
|
||||||
|
|
||||||
Well formatted code example:
|
|
||||||
|
|
||||||
```
|
|
||||||
static void my_function(void)
|
|
||||||
{
|
|
||||||
GB_something_t *thing = GB_function(&gb, GB_FLAG_ONE | GB_FLAG_TWO, sizeof(thing));
|
|
||||||
if (GB_is_thing(thing)) return;
|
|
||||||
|
|
||||||
switch (*thing) {
|
|
||||||
case GB_QUACK:
|
|
||||||
// Something
|
|
||||||
case GB_DUCK:
|
|
||||||
// Something else
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Badly formatted code example:
|
|
||||||
```
|
|
||||||
static void my_function(){
|
|
||||||
GB_something_t* thing=GB_function(&gb , GB_FLAG_ONE|GB_FLAG_TWO , sizeof thing);
|
|
||||||
if( GB_is_thing ( thing ) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch(* thing)
|
|
||||||
{
|
|
||||||
case GB_QUACK:
|
|
||||||
// Something
|
|
||||||
case GB_DUCK:
|
|
||||||
// Something else
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Other Coding Conventions
|
|
||||||
|
|
||||||
The primitive types to be used in SameBoy are `unsigned` and `signed` (Without the `int` keyword), the `(u)int*_t` types, `char *` for UTF-8 strings, `double` for non-integer numbers, and `bool` for booleans (Including in Objective-C code, avoid `BOOL`). As long as it's not mandated by a 3rd-party API (e.g. `int` when using file descriptors), avoid using other primitive types. Use `const` whenever possible.
|
|
||||||
|
|
||||||
Most C names should be `lower_case_snake_case`. Constants and macros use `UPPER_CASE_SNAKE_CASE`. Type definitions use a `_t` suffix. Type definitions, as well as non-static (exported) core symbols, should be prefixed with `GB_` (SameBoy's core is intended to be used as a library, so it shouldn't contaminate the global namespace without prefixes). Exported symbols that are only meant to be used by other parts of the core should still get the `GB_` prefix, but their header definition should be inside `#ifdef GB_INTERNAL`.
|
|
||||||
|
|
||||||
For Objective-C naming conventions, use Apple's conventions (Some old Objective-C code mixes these with the C naming convention; new code should use Apple's convention exclusively). The name prefix for SameBoy classes and constants is `GB`. JoyKit's prefix is `JOY`, and Hex Fiend's prefix is `HF`.
|
|
||||||
|
|
||||||
In all languages, prefer long, unambiguous names over short ambiguous ones.
|
|
@ -1,25 +1,15 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import <WebKit/WebKit.h>
|
|
||||||
|
|
||||||
@interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate, NSMenuDelegate, WebUIDelegate, WebPolicyDelegate, WebFrameLoadDelegate>
|
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||||
|
|
||||||
@property (nonatomic, strong) IBOutlet NSWindow *preferencesWindow;
|
@property IBOutlet NSWindow *preferencesWindow;
|
||||||
@property (nonatomic, strong) IBOutlet NSView *graphicsTab;
|
@property (strong) IBOutlet NSView *graphicsTab;
|
||||||
@property (nonatomic, strong) IBOutlet NSView *emulationTab;
|
@property (strong) IBOutlet NSView *emulationTab;
|
||||||
@property (nonatomic, strong) IBOutlet NSView *audioTab;
|
@property (strong) IBOutlet NSView *audioTab;
|
||||||
@property (nonatomic, strong) IBOutlet NSView *controlsTab;
|
@property (strong) IBOutlet NSView *controlsTab;
|
||||||
@property (nonatomic, strong) IBOutlet NSView *updatesTab;
|
|
||||||
- (IBAction)showPreferences: (id) sender;
|
- (IBAction)showPreferences: (id) sender;
|
||||||
- (IBAction)toggleDeveloperMode:(id)sender;
|
- (IBAction)toggleDeveloperMode:(id)sender;
|
||||||
- (IBAction)switchPreferencesTab:(id)sender;
|
- (IBAction)switchPreferencesTab:(id)sender;
|
||||||
@property (nonatomic, weak) IBOutlet NSMenuItem *linkCableMenuItem;
|
|
||||||
@property (nonatomic, strong) IBOutlet NSWindow *updateWindow;
|
|
||||||
@property (nonatomic, strong) IBOutlet WebView *updateChanges;
|
|
||||||
@property (nonatomic, strong) IBOutlet NSProgressIndicator *updatesSpinner;
|
|
||||||
@property (strong) IBOutlet NSButton *updatesButton;
|
|
||||||
@property (strong) IBOutlet NSTextField *updateProgressLabel;
|
|
||||||
@property (strong) IBOutlet NSButton *updateProgressButton;
|
|
||||||
@property (strong) IBOutlet NSWindow *updateProgressWindow;
|
|
||||||
@property (strong) IBOutlet NSProgressIndicator *updateProgressSpinner;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -4,33 +4,11 @@
|
|||||||
#include <Core/gb.h>
|
#include <Core/gb.h>
|
||||||
#import <Carbon/Carbon.h>
|
#import <Carbon/Carbon.h>
|
||||||
#import <JoyKit/JoyKit.h>
|
#import <JoyKit/JoyKit.h>
|
||||||
#import <WebKit/WebKit.h>
|
|
||||||
|
|
||||||
#define UPDATE_SERVER "https://sameboy.github.io"
|
|
||||||
|
|
||||||
static uint32_t color_to_int(NSColor *color)
|
|
||||||
{
|
|
||||||
color = [color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
|
|
||||||
return (((unsigned)(color.redComponent * 0xFF)) << 16) |
|
|
||||||
(((unsigned)(color.greenComponent * 0xFF)) << 8) |
|
|
||||||
((unsigned)(color.blueComponent * 0xFF));
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
{
|
{
|
||||||
NSWindow *preferences_window;
|
NSWindow *preferences_window;
|
||||||
NSArray<NSView *> *preferences_tabs;
|
NSArray<NSView *> *preferences_tabs;
|
||||||
NSString *_lastVersion;
|
|
||||||
NSString *_updateURL;
|
|
||||||
NSURLSessionDownloadTask *_updateTask;
|
|
||||||
enum {
|
|
||||||
UPDATE_DOWNLOADING,
|
|
||||||
UPDATE_EXTRACTING,
|
|
||||||
UPDATE_WAIT_INSTALL,
|
|
||||||
UPDATE_INSTALLING,
|
|
||||||
UPDATE_FAILED,
|
|
||||||
} _updateState;
|
|
||||||
NSString *_downloadDirectory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) applicationDidFinishLaunching:(NSNotification *)notification
|
- (void) applicationDidFinishLaunching:(NSNotification *)notification
|
||||||
@ -66,31 +44,12 @@ static uint32_t color_to_int(NSColor *color)
|
|||||||
@"GBCGBModel": @(GB_MODEL_CGB_E),
|
@"GBCGBModel": @(GB_MODEL_CGB_E),
|
||||||
@"GBSGBModel": @(GB_MODEL_SGB2),
|
@"GBSGBModel": @(GB_MODEL_SGB2),
|
||||||
@"GBRumbleMode": @(GB_RUMBLE_CARTRIDGE_ONLY),
|
@"GBRumbleMode": @(GB_RUMBLE_CARTRIDGE_ONLY),
|
||||||
|
|
||||||
@"GBVolume": @(1.0),
|
|
||||||
|
|
||||||
@"GBMBC7JoystickOverride": @NO,
|
|
||||||
@"GBMBC7AllowMouse": @YES,
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[JOYController startOnRunLoop:[NSRunLoop currentRunLoop] withOptions:@{
|
[JOYController startOnRunLoop:[NSRunLoop currentRunLoop] withOptions:@{
|
||||||
JOYAxes2DEmulateButtonsKey: @YES,
|
JOYAxes2DEmulateButtonsKey: @YES,
|
||||||
JOYHatsEmulateButtonsKey: @YES,
|
JOYHatsEmulateButtonsKey: @YES,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBNotificationsUsed"]) {
|
|
||||||
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self askAutoUpdates];
|
|
||||||
|
|
||||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBAutoUpdatesEnabled"]) {
|
|
||||||
[self checkForUpdates];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([[NSProcessInfo processInfo].arguments containsObject:@"--update-launch"]) {
|
|
||||||
[NSApp activateIgnoringOtherApps:true];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleDeveloperMode:(id)sender
|
- (IBAction)toggleDeveloperMode:(id)sender
|
||||||
@ -109,7 +68,7 @@ static uint32_t color_to_int(NSColor *color)
|
|||||||
NSRect new = [_preferencesWindow frameRectForContentRect:tab.frame];
|
NSRect new = [_preferencesWindow frameRectForContentRect:tab.frame];
|
||||||
new.origin.x = old.origin.x;
|
new.origin.x = old.origin.x;
|
||||||
new.origin.y = old.origin.y + (old.size.height - new.size.height);
|
new.origin.y = old.origin.y + (old.size.height - new.size.height);
|
||||||
[_preferencesWindow setFrame:new display:true animate:_preferencesWindow.visible];
|
[_preferencesWindow setFrame:new display:YES animate:_preferencesWindow.visible];
|
||||||
[_preferencesWindow.contentView addSubview:tab];
|
[_preferencesWindow.contentView addSubview:tab];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,28 +78,9 @@ static uint32_t color_to_int(NSColor *color)
|
|||||||
[(NSMenuItem *)anItem setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"DeveloperMode"]];
|
[(NSMenuItem *)anItem setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"DeveloperMode"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anItem == self.linkCableMenuItem) {
|
|
||||||
return [[NSDocumentController sharedDocumentController] documents].count > 1;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)menuNeedsUpdate:(NSMenu *)menu
|
|
||||||
{
|
|
||||||
NSMutableArray *items = [NSMutableArray array];
|
|
||||||
NSDocument *currentDocument = [[NSDocumentController sharedDocumentController] currentDocument];
|
|
||||||
|
|
||||||
for (NSDocument *document in [[NSDocumentController sharedDocumentController] documents]) {
|
|
||||||
if (document == currentDocument) continue;
|
|
||||||
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:document.displayName action:@selector(connectLinkCable:) keyEquivalent:@""];
|
|
||||||
item.representedObject = document;
|
|
||||||
item.image = [[NSWorkspace sharedWorkspace] iconForFile:document.fileURL.path];
|
|
||||||
[item.image setSize:NSMakeSize(16, 16)];
|
|
||||||
[items addObject:item];
|
|
||||||
}
|
|
||||||
menu.itemArray = items;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction) showPreferences: (id) sender
|
- (IBAction) showPreferences: (id) sender
|
||||||
{
|
{
|
||||||
NSArray *objects;
|
NSArray *objects;
|
||||||
@ -148,306 +88,17 @@ static uint32_t color_to_int(NSColor *color)
|
|||||||
[[NSBundle mainBundle] loadNibNamed:@"Preferences" owner:self topLevelObjects:&objects];
|
[[NSBundle mainBundle] loadNibNamed:@"Preferences" owner:self topLevelObjects:&objects];
|
||||||
NSToolbarItem *first_toolbar_item = [_preferencesWindow.toolbar.items firstObject];
|
NSToolbarItem *first_toolbar_item = [_preferencesWindow.toolbar.items firstObject];
|
||||||
_preferencesWindow.toolbar.selectedItemIdentifier = [first_toolbar_item itemIdentifier];
|
_preferencesWindow.toolbar.selectedItemIdentifier = [first_toolbar_item itemIdentifier];
|
||||||
preferences_tabs = @[self.emulationTab, self.graphicsTab, self.audioTab, self.controlsTab, self.updatesTab];
|
preferences_tabs = @[self.emulationTab, self.graphicsTab, self.audioTab, self.controlsTab];
|
||||||
[self switchPreferencesTab:first_toolbar_item];
|
[self switchPreferencesTab:first_toolbar_item];
|
||||||
[_preferencesWindow center];
|
[_preferencesWindow center];
|
||||||
#ifndef UPDATE_SUPPORT
|
|
||||||
[_preferencesWindow.toolbar removeItemAtIndex:4];
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
[_preferencesWindow makeKeyAndOrderFront:self];
|
[_preferencesWindow makeKeyAndOrderFront:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender
|
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender
|
||||||
{
|
{
|
||||||
[self askAutoUpdates];
|
|
||||||
/* Bring an existing panel to the foreground */
|
|
||||||
for (NSWindow *window in [[NSApplication sharedApplication] windows]) {
|
|
||||||
if ([window isKindOfClass:[NSOpenPanel class]]) {
|
|
||||||
[(NSOpenPanel *)window makeKeyAndOrderFront:nil];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[[NSDocumentController sharedDocumentController] openDocument:self];
|
[[NSDocumentController sharedDocumentController] openDocument:self];
|
||||||
return true;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
|
|
||||||
{
|
|
||||||
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfFile:notification.identifier display:true];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)updateFound
|
|
||||||
{
|
|
||||||
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@UPDATE_SERVER "/raw_changes"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
||||||
|
|
||||||
NSColor *linkColor = [NSColor colorWithRed:0.125 green:0.325 blue:1.0 alpha:1.0];
|
|
||||||
if (@available(macOS 10.10, *)) {
|
|
||||||
linkColor = [NSColor linkColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *changes = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
||||||
NSRange cutoffRange = [changes rangeOfString:@"<!--(" GB_VERSION ")-->"];
|
|
||||||
if (cutoffRange.location != NSNotFound) {
|
|
||||||
changes = [changes substringToIndex:cutoffRange.location];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *html = [NSString stringWithFormat:@"<!DOCTYPE html><html><head><title></title>"
|
|
||||||
"<style>html {background-color:transparent; color: #%06x; line-height:1.5} a:link, a:visited{color:#%06x; text-decoration:none}</style>"
|
|
||||||
"</head><body>%@</body></html>",
|
|
||||||
color_to_int([NSColor textColor]),
|
|
||||||
color_to_int(linkColor),
|
|
||||||
changes];
|
|
||||||
|
|
||||||
if ([(NSHTTPURLResponse *)response statusCode] == 200) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
NSArray *objects;
|
|
||||||
[[NSBundle mainBundle] loadNibNamed:@"UpdateWindow" owner:self topLevelObjects:&objects];
|
|
||||||
self.updateChanges.preferences.standardFontFamily = [NSFont systemFontOfSize:0].familyName;
|
|
||||||
self.updateChanges.preferences.fixedFontFamily = @"Menlo";
|
|
||||||
self.updateChanges.drawsBackground = false;
|
|
||||||
[self.updateChanges.mainFrame loadHTMLString:html baseURL:nil];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}] resume];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems
|
|
||||||
{
|
|
||||||
// Disable reload context menu
|
|
||||||
if ([defaultMenuItems count] <= 2) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
return defaultMenuItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
|
|
||||||
{
|
|
||||||
static dispatch_once_t onceToken;
|
|
||||||
dispatch_once(&onceToken, ^{
|
|
||||||
sender.mainFrame.frameView.documentView.enclosingScrollView.drawsBackground = true;
|
|
||||||
sender.mainFrame.frameView.documentView.enclosingScrollView.backgroundColor = [NSColor textBackgroundColor];
|
|
||||||
sender.policyDelegate = self;
|
|
||||||
[self.updateWindow center];
|
|
||||||
[self.updateWindow makeKeyAndOrderFront:nil];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener
|
|
||||||
{
|
|
||||||
[listener ignore];
|
|
||||||
[[NSWorkspace sharedWorkspace] openURL:[request URL]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)checkForUpdates
|
|
||||||
{
|
|
||||||
#ifdef UPDATE_SUPPORT
|
|
||||||
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@UPDATE_SERVER "/latest_version"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[self.updatesSpinner stopAnimation:nil];
|
|
||||||
[self.updatesButton setEnabled:true];
|
|
||||||
});
|
|
||||||
if ([(NSHTTPURLResponse *)response statusCode] == 200) {
|
|
||||||
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
||||||
NSArray <NSString *> *components = [string componentsSeparatedByString:@"|"];
|
|
||||||
if (components.count != 2) return;
|
|
||||||
_lastVersion = components[0];
|
|
||||||
_updateURL = components[1];
|
|
||||||
if (![@GB_VERSION isEqualToString:_lastVersion] &&
|
|
||||||
![[[NSUserDefaults standardUserDefaults] stringForKey:@"GBSkippedVersion"] isEqualToString:_lastVersion]) {
|
|
||||||
[self updateFound];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}] resume];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)userCheckForUpdates:(id)sender
|
|
||||||
{
|
|
||||||
if (self.updateWindow) {
|
|
||||||
[self.updateWindow makeKeyAndOrderFront:sender];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:nil forKey:@"GBSkippedVersion"];
|
|
||||||
[self checkForUpdates];
|
|
||||||
[sender setEnabled:false];
|
|
||||||
[self.updatesSpinner startAnimation:sender];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)askAutoUpdates
|
|
||||||
{
|
|
||||||
#ifdef UPDATE_SUPPORT
|
|
||||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBAskedAutoUpdates"]) {
|
|
||||||
NSAlert *alert = [[NSAlert alloc] init];
|
|
||||||
alert.messageText = @"Should SameBoy check for updates when launched?";
|
|
||||||
alert.informativeText = @"SameBoy is frequently updated with new features, accuracy improvements, and bug fixes. This setting can always be changed in the preferences window.";
|
|
||||||
[alert addButtonWithTitle:@"Check on Launch"];
|
|
||||||
[alert addButtonWithTitle:@"Don't Check on Launch"];
|
|
||||||
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:[alert runModal] == NSAlertFirstButtonReturn forKey:@"GBAutoUpdatesEnabled"];
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:true forKey:@"GBAskedAutoUpdates"];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)skipVersion:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:_lastVersion forKey:@"GBSkippedVersion"];
|
|
||||||
[self.updateWindow performClose:sender];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)installUpdate:(id)sender
|
|
||||||
{
|
|
||||||
[self.updateProgressSpinner startAnimation:nil];
|
|
||||||
self.updateProgressButton.title = @"Cancel";
|
|
||||||
self.updateProgressButton.enabled = true;
|
|
||||||
self.updateProgressLabel.stringValue = @"Downloading update...";
|
|
||||||
_updateState = UPDATE_DOWNLOADING;
|
|
||||||
_updateTask = [[NSURLSession sharedSession] downloadTaskWithURL: [NSURL URLWithString:_updateURL] completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
|
|
||||||
_updateTask = nil;
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Extracting update...";
|
|
||||||
_updateState = UPDATE_EXTRACTING;
|
|
||||||
});
|
|
||||||
|
|
||||||
_downloadDirectory = [[[NSFileManager defaultManager] URLForDirectory:NSItemReplacementDirectory
|
|
||||||
inDomain:NSUserDomainMask
|
|
||||||
appropriateForURL:[[NSBundle mainBundle] bundleURL]
|
|
||||||
create:true
|
|
||||||
error:nil] path];
|
|
||||||
NSTask *unzipTask;
|
|
||||||
if (!_downloadDirectory) {
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Failed to extract update.";
|
|
||||||
_updateState = UPDATE_FAILED;
|
|
||||||
self.updateProgressButton.title = @"Close";
|
|
||||||
self.updateProgressButton.enabled = true;
|
|
||||||
[self.updateProgressSpinner stopAnimation:nil];
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
unzipTask = [[NSTask alloc] init];
|
|
||||||
unzipTask.launchPath = @"/usr/bin/unzip";
|
|
||||||
unzipTask.arguments = @[location.path, @"-d", _downloadDirectory];
|
|
||||||
[unzipTask launch];
|
|
||||||
[unzipTask waitUntilExit];
|
|
||||||
if (unzipTask.terminationStatus != 0 || unzipTask.terminationReason != NSTaskTerminationReasonExit) {
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Failed to extract update.";
|
|
||||||
_updateState = UPDATE_FAILED;
|
|
||||||
self.updateProgressButton.title = @"Close";
|
|
||||||
self.updateProgressButton.enabled = true;
|
|
||||||
[self.updateProgressSpinner stopAnimation:nil];
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Update ready, save your game progress and click Install.";
|
|
||||||
_updateState = UPDATE_WAIT_INSTALL;
|
|
||||||
self.updateProgressButton.title = @"Install";
|
|
||||||
self.updateProgressButton.enabled = true;
|
|
||||||
[self.updateProgressSpinner stopAnimation:nil];
|
|
||||||
});
|
|
||||||
}];
|
|
||||||
[_updateTask resume];
|
|
||||||
|
|
||||||
self.updateProgressWindow.preventsApplicationTerminationWhenModal = false;
|
|
||||||
[self.updateWindow beginSheet:self.updateProgressWindow completionHandler:^(NSModalResponse returnCode) {
|
|
||||||
[self.updateWindow close];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)performUpgrade
|
|
||||||
{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Instaling update...";
|
|
||||||
_updateState = UPDATE_INSTALLING;
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
[self.updateProgressSpinner startAnimation:nil];
|
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
||||||
NSString *executablePath = [[NSBundle mainBundle] executablePath];
|
|
||||||
NSString *contentsPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Contents"];
|
|
||||||
NSString *contentsTempPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"TempContents"];
|
|
||||||
NSString *updateContentsPath = [_downloadDirectory stringByAppendingPathComponent:@"SameBoy.app/Contents"];
|
|
||||||
NSError *error = nil;
|
|
||||||
[[NSFileManager defaultManager] moveItemAtPath:contentsPath toPath:contentsTempPath error:&error];
|
|
||||||
if (error) {
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
|
|
||||||
_downloadDirectory = nil;
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Failed to install update.";
|
|
||||||
_updateState = UPDATE_FAILED;
|
|
||||||
self.updateProgressButton.title = @"Close";
|
|
||||||
self.updateProgressButton.enabled = true;
|
|
||||||
[self.updateProgressSpinner stopAnimation:nil];
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[[NSFileManager defaultManager] moveItemAtPath:updateContentsPath toPath:contentsPath error:&error];
|
|
||||||
if (error) {
|
|
||||||
[[NSFileManager defaultManager] moveItemAtPath:contentsTempPath toPath:contentsPath error:nil];
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
|
|
||||||
_downloadDirectory = nil;
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Failed to install update.";
|
|
||||||
_updateState = UPDATE_FAILED;
|
|
||||||
self.updateProgressButton.title = @"Close";
|
|
||||||
self.updateProgressButton.enabled = true;
|
|
||||||
[self.updateProgressSpinner stopAnimation:nil];
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:contentsTempPath error:nil];
|
|
||||||
_downloadDirectory = nil;
|
|
||||||
atexit_b(^{
|
|
||||||
execl(executablePath.UTF8String, executablePath.UTF8String, "--update-launch", NULL);
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[NSApp terminate:nil];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)updateAction:(id)sender
|
|
||||||
{
|
|
||||||
switch (_updateState) {
|
|
||||||
case UPDATE_DOWNLOADING:
|
|
||||||
[_updateTask cancelByProducingResumeData:nil];
|
|
||||||
_updateTask = nil;
|
|
||||||
[self.updateProgressWindow close];
|
|
||||||
break;
|
|
||||||
case UPDATE_WAIT_INSTALL:
|
|
||||||
[self performUpgrade];
|
|
||||||
break;
|
|
||||||
case UPDATE_EXTRACTING:
|
|
||||||
case UPDATE_INSTALLING:
|
|
||||||
break;
|
|
||||||
case UPDATE_FAILED:
|
|
||||||
[self.updateProgressWindow close];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
if (_downloadDirectory) {
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)nop:(id)sender
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#ifndef BigSurToolbar_h
|
|
||||||
#define BigSurToolbar_h
|
|
||||||
|
|
||||||
/* Backport the toolbarStyle property to allow compilation with older SDKs*/
|
|
||||||
#ifndef __MAC_10_16
|
|
||||||
typedef NS_ENUM(NSInteger, NSWindowToolbarStyle) {
|
|
||||||
// The default value. The style will be determined by the window's given configuration
|
|
||||||
NSWindowToolbarStyleAutomatic,
|
|
||||||
// The toolbar will appear below the window title
|
|
||||||
NSWindowToolbarStyleExpanded,
|
|
||||||
// The toolbar will appear below the window title and the items in the toolbar will attempt to have equal widths when possible
|
|
||||||
NSWindowToolbarStylePreference,
|
|
||||||
// The window title will appear inline with the toolbar when visible
|
|
||||||
NSWindowToolbarStyleUnified,
|
|
||||||
// Same as NSWindowToolbarStyleUnified, but with reduced margins in the toolbar allowing more focus to be on the contents of the window
|
|
||||||
NSWindowToolbarStyleUnifiedCompact
|
|
||||||
} API_AVAILABLE(macos(11.0));
|
|
||||||
|
|
||||||
@interface NSWindow (toolbarStyle)
|
|
||||||
@property (nonatomic) NSWindowToolbarStyle toolbarStyle API_AVAILABLE(macos(11.0));
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface NSImage (SFSymbols)
|
|
||||||
+ (instancetype)imageWithSystemSymbolName:(NSString *)symbolName accessibilityDescription:(NSString *)description API_AVAILABLE(macos(11.0));
|
|
||||||
@end
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -2,60 +2,45 @@
|
|||||||
#include "GBView.h"
|
#include "GBView.h"
|
||||||
#include "GBImageView.h"
|
#include "GBImageView.h"
|
||||||
#include "GBSplitView.h"
|
#include "GBSplitView.h"
|
||||||
#include "GBVisualizerView.h"
|
|
||||||
#include "GBOSDView.h"
|
|
||||||
|
|
||||||
@class GBCheatWindowController;
|
@class GBCheatWindowController;
|
||||||
|
|
||||||
@interface Document : NSDocument <NSWindowDelegate, GBImageViewDelegate, NSTableViewDataSource, NSTableViewDelegate, NSSplitViewDelegate>
|
@interface Document : NSDocument <NSWindowDelegate, GBImageViewDelegate, NSTableViewDataSource, NSTableViewDelegate, NSSplitViewDelegate>
|
||||||
@property (nonatomic, readonly) GB_gameboy_t *gb;
|
@property (strong) IBOutlet GBView *view;
|
||||||
@property (nonatomic, strong) IBOutlet GBView *view;
|
@property (strong) IBOutlet NSTextView *consoleOutput;
|
||||||
@property (nonatomic, strong) IBOutlet NSTextView *consoleOutput;
|
@property (strong) IBOutlet NSPanel *consoleWindow;
|
||||||
@property (nonatomic, strong) IBOutlet NSPanel *consoleWindow;
|
@property (strong) IBOutlet NSTextField *consoleInput;
|
||||||
@property (nonatomic, strong) IBOutlet NSTextField *consoleInput;
|
@property (strong) IBOutlet NSWindow *mainWindow;
|
||||||
@property (nonatomic, strong) IBOutlet NSWindow *mainWindow;
|
@property (strong) IBOutlet NSView *memoryView;
|
||||||
@property (nonatomic, strong) IBOutlet NSView *memoryView;
|
@property (strong) IBOutlet NSPanel *memoryWindow;
|
||||||
@property (nonatomic, strong) IBOutlet NSPanel *memoryWindow;
|
@property (readonly) GB_gameboy_t *gameboy;
|
||||||
@property (nonatomic, readonly) GB_gameboy_t *gameboy;
|
@property (strong) IBOutlet NSTextField *memoryBankInput;
|
||||||
@property (nonatomic, strong) IBOutlet NSTextField *memoryBankInput;
|
@property (strong) IBOutlet NSToolbarItem *memoryBankItem;
|
||||||
@property (nonatomic, strong) IBOutlet NSToolbarItem *memoryBankItem;
|
@property (strong) IBOutlet GBImageView *tilesetImageView;
|
||||||
@property (nonatomic, strong) IBOutlet GBImageView *tilesetImageView;
|
@property (strong) IBOutlet NSPopUpButton *tilesetPaletteButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *tilesetPaletteButton;
|
@property (strong) IBOutlet GBImageView *tilemapImageView;
|
||||||
@property (nonatomic, strong) IBOutlet GBImageView *tilemapImageView;
|
@property (strong) IBOutlet NSPopUpButton *tilemapPaletteButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *tilemapPaletteButton;
|
@property (strong) IBOutlet NSPopUpButton *tilemapMapButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *tilemapMapButton;
|
@property (strong) IBOutlet NSPopUpButton *TilemapSetButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *TilemapSetButton;
|
@property (strong) IBOutlet NSButton *gridButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSButton *gridButton;
|
@property (strong) IBOutlet NSTabView *vramTabView;
|
||||||
@property (nonatomic, strong) IBOutlet NSTabView *vramTabView;
|
@property (strong) IBOutlet NSPanel *vramWindow;
|
||||||
@property (nonatomic, strong) IBOutlet NSPanel *vramWindow;
|
@property (strong) IBOutlet NSTextField *vramStatusLabel;
|
||||||
@property (nonatomic, strong) IBOutlet NSTextField *vramStatusLabel;
|
@property (strong) IBOutlet NSTableView *paletteTableView;
|
||||||
@property (nonatomic, strong) IBOutlet NSTableView *paletteTableView;
|
@property (strong) IBOutlet NSTableView *spritesTableView;
|
||||||
@property (nonatomic, strong) IBOutlet NSTableView *objectsTableView;
|
@property (strong) IBOutlet NSPanel *printerFeedWindow;
|
||||||
@property (nonatomic, strong) IBOutlet NSPanel *printerFeedWindow;
|
@property (strong) IBOutlet NSImageView *feedImageView;
|
||||||
@property (nonatomic, strong) IBOutlet NSImageView *feedImageView;
|
@property (strong) IBOutlet NSButton *feedSaveButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSTextView *debuggerSideViewInput;
|
@property (strong) IBOutlet NSTextView *debuggerSideViewInput;
|
||||||
@property (nonatomic, strong) IBOutlet NSTextView *debuggerSideView;
|
@property (strong) IBOutlet NSTextView *debuggerSideView;
|
||||||
@property (nonatomic, strong) IBOutlet GBSplitView *debuggerSplitView;
|
@property (strong) IBOutlet GBSplitView *debuggerSplitView;
|
||||||
@property (nonatomic, strong) IBOutlet NSBox *debuggerVerticalLine;
|
@property (strong) IBOutlet NSBox *debuggerVerticalLine;
|
||||||
@property (nonatomic, strong) IBOutlet NSPanel *cheatsWindow;
|
@property (strong) IBOutlet NSPanel *cheatsWindow;
|
||||||
@property (nonatomic, strong) IBOutlet GBCheatWindowController *cheatWindowController;
|
@property (strong) IBOutlet GBCheatWindowController *cheatWindowController;
|
||||||
@property (nonatomic, readonly) Document *partner;
|
|
||||||
@property (nonatomic, readonly) bool isSlave;
|
|
||||||
@property (strong) IBOutlet NSView *gbsPlayerView;
|
|
||||||
@property (strong) IBOutlet NSTextField *gbsTitle;
|
|
||||||
@property (strong) IBOutlet NSTextField *gbsAuthor;
|
|
||||||
@property (strong) IBOutlet NSTextField *gbsCopyright;
|
|
||||||
@property (strong) IBOutlet NSPopUpButton *gbsTracks;
|
|
||||||
@property (strong) IBOutlet NSButton *gbsPlayPauseButton;
|
|
||||||
@property (strong) IBOutlet NSButton *gbsRewindButton;
|
|
||||||
@property (strong) IBOutlet NSSegmentedControl *gbsNextPrevButton;
|
|
||||||
@property (strong) IBOutlet GBVisualizerView *gbsVisualizer;
|
|
||||||
@property (strong) IBOutlet GBOSDView *osdView;
|
|
||||||
|
|
||||||
-(uint8_t) readMemory:(uint16_t) addr;
|
-(uint8_t) readMemory:(uint16_t) addr;
|
||||||
-(void) writeMemory:(uint16_t) addr value:(uint8_t)value;
|
-(void) writeMemory:(uint16_t) addr value:(uint8_t)value;
|
||||||
-(void) performAtomicBlock: (void (^)())block;
|
-(void) performAtomicBlock: (void (^)())block;
|
||||||
-(void) connectLinkCable:(NSMenuItem *)sender;
|
|
||||||
-(int)loadStateFile:(const char *)path noErrorOnNotFound:(bool)noErrorOnFileNotFound;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
853
Cocoa/Document.m
@ -19,16 +19,16 @@
|
|||||||
<outlet property="debuggerSplitView" destination="pUc-ZN-vl5" id="0sG-0D-cID"/>
|
<outlet property="debuggerSplitView" destination="pUc-ZN-vl5" id="0sG-0D-cID"/>
|
||||||
<outlet property="debuggerVerticalLine" destination="7bR-gM-1At" id="rfy-7Z-388"/>
|
<outlet property="debuggerVerticalLine" destination="7bR-gM-1At" id="rfy-7Z-388"/>
|
||||||
<outlet property="feedImageView" destination="Ar0-nN-eop" id="wHa-St-o4G"/>
|
<outlet property="feedImageView" destination="Ar0-nN-eop" id="wHa-St-o4G"/>
|
||||||
|
<outlet property="feedSaveButton" destination="RLc-0I-sYZ" id="Yy9-dG-xXY"/>
|
||||||
<outlet property="gridButton" destination="fL6-2S-Rgd" id="jtV-jh-GHC"/>
|
<outlet property="gridButton" destination="fL6-2S-Rgd" id="jtV-jh-GHC"/>
|
||||||
<outlet property="mainWindow" destination="xOd-HO-29H" id="h8M-YB-vcC"/>
|
<outlet property="mainWindow" destination="xOd-HO-29H" id="h8M-YB-vcC"/>
|
||||||
<outlet property="memoryBankInput" destination="rdV-q6-hc6" id="KBx-9T-2mX"/>
|
<outlet property="memoryBankInput" destination="rdV-q6-hc6" id="KBx-9T-2mX"/>
|
||||||
<outlet property="memoryBankItem" destination="bWC-FW-IYP" id="Lf2-dh-z32"/>
|
<outlet property="memoryBankItem" destination="bWC-FW-IYP" id="Lf2-dh-z32"/>
|
||||||
<outlet property="memoryView" destination="8hr-8o-3rN" id="fF0-rh-8ND"/>
|
<outlet property="memoryView" destination="8hr-8o-3rN" id="fF0-rh-8ND"/>
|
||||||
<outlet property="memoryWindow" destination="mRm-dL-mCj" id="VPR-lu-vtI"/>
|
<outlet property="memoryWindow" destination="mRm-dL-mCj" id="VPR-lu-vtI"/>
|
||||||
<outlet property="objectsTableView" destination="TOc-XJ-w9w" id="O4R-4Z-9hU"/>
|
|
||||||
<outlet property="osdView" destination="MX4-l2-7NE" id="Am7-fq-uvu"/>
|
|
||||||
<outlet property="paletteTableView" destination="gfC-d3-dmq" id="fTC-eL-Qg3"/>
|
<outlet property="paletteTableView" destination="gfC-d3-dmq" id="fTC-eL-Qg3"/>
|
||||||
<outlet property="printerFeedWindow" destination="NdE-0B-WCf" id="yVK-cS-NOJ"/>
|
<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="tilemapImageView" destination="LlK-tV-bjv" id="nSY-Xd-BjZ"/>
|
||||||
<outlet property="tilemapMapButton" destination="YIJ-Qc-SIZ" id="BB7-Gg-7XP"/>
|
<outlet property="tilemapMapButton" destination="YIJ-Qc-SIZ" id="BB7-Gg-7XP"/>
|
||||||
<outlet property="tilemapPaletteButton" destination="loB-0k-Qff" id="2Or-7l-6vn"/>
|
<outlet property="tilemapPaletteButton" destination="loB-0k-Qff" id="2Or-7l-6vn"/>
|
||||||
@ -60,16 +60,9 @@
|
|||||||
<view fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uqf-pe-VAF" customClass="GBView">
|
<view fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uqf-pe-VAF" customClass="GBView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
|
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<connections>
|
|
||||||
<outlet property="document" destination="-2" id="Fvh-rD-z4r"/>
|
|
||||||
</connections>
|
|
||||||
</view>
|
</view>
|
||||||
</subviews>
|
</subviews>
|
||||||
</customView>
|
</customView>
|
||||||
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MX4-l2-7NE" customClass="GBOSDView">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
</customView>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
@ -251,9 +244,9 @@
|
|||||||
<toolbarItem implicitItemIdentifier="4F6AAE25-1E9D-4111-9E5B-91F0792E56CD" label="Address Space" paletteLabel="Address Space" id="VTy-lj-K0H">
|
<toolbarItem implicitItemIdentifier="4F6AAE25-1E9D-4111-9E5B-91F0792E56CD" label="Address Space" paletteLabel="Address Space" id="VTy-lj-K0H">
|
||||||
<nil key="toolTip"/>
|
<nil key="toolTip"/>
|
||||||
<size key="minSize" width="100" height="25"/>
|
<size key="minSize" width="100" height="25"/>
|
||||||
<size key="maxSize" width="130" height="25"/>
|
<size key="maxSize" width="100" height="25"/>
|
||||||
<popUpButton key="view" verticalHuggingPriority="750" id="vfJ-vu-gqJ">
|
<popUpButton key="view" verticalHuggingPriority="750" id="vfJ-vu-gqJ">
|
||||||
<rect key="frame" x="0.0" y="14" width="128" height="25"/>
|
<rect key="frame" x="0.0" y="14" width="100" height="25"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<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" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bpD-j9-omo">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -502,7 +495,7 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
</view>
|
</view>
|
||||||
</tabViewItem>
|
</tabViewItem>
|
||||||
<tabViewItem label="Objects" identifier="" id="a08-eg-Maw">
|
<tabViewItem label="Sprites" identifier="" id="a08-eg-Maw">
|
||||||
<view key="view" id="EiO-p0-3xn">
|
<view key="view" id="EiO-p0-3xn">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
|
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
@ -514,7 +507,7 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
|
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<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">
|
<tableView appearanceType="vibrantLight" 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"/>
|
<rect key="frame" x="0.0" y="0.0" width="512" height="391"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<size key="intercellSpacing" width="3" height="2"/>
|
<size key="intercellSpacing" width="3" height="2"/>
|
||||||
@ -770,7 +763,7 @@
|
|||||||
<segments>
|
<segments>
|
||||||
<segment label="Tileset" selected="YES"/>
|
<segment label="Tileset" selected="YES"/>
|
||||||
<segment label="Tilemap" tag="1"/>
|
<segment label="Tilemap" tag="1"/>
|
||||||
<segment label="Objects"/>
|
<segment label="Sprites"/>
|
||||||
<segment label="Palettes"/>
|
<segment label="Palettes"/>
|
||||||
</segments>
|
</segments>
|
||||||
</segmentedCell>
|
</segmentedCell>
|
||||||
@ -793,10 +786,9 @@
|
|||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
|
||||||
<rect key="contentRect" x="0.0" y="0.0" width="320" height="288"/>
|
<rect key="contentRect" x="0.0" y="0.0" width="320" height="288"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||||
<value key="minSize" type="size" width="320" height="16"/>
|
|
||||||
<view key="contentView" id="RRS-aa-bPT">
|
<view key="contentView" id="RRS-aa-bPT">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="288"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="288"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ar0-nN-eop" customClass="GBImageView">
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ar0-nN-eop" customClass="GBImageView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="288"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="288"/>
|
||||||
@ -805,25 +797,20 @@
|
|||||||
</imageView>
|
</imageView>
|
||||||
</subviews>
|
</subviews>
|
||||||
</view>
|
</view>
|
||||||
<toolbar key="toolbar" implicitIdentifier="1FF86A2B-6637-4EE6-A25A-7298D79AE84E" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconAndLabel" sizeMode="regular" id="gH3-SH-7il">
|
|
||||||
<allowedToolbarItems>
|
|
||||||
<toolbarItem implicitItemIdentifier="15EB8D49-8C6E-42F2-9F7F-F7D7A0BBDAAF" label="Save" paletteLabel="Save" tag="-1" image="NSFolder" id="CBz-1N-o0Q">
|
|
||||||
<connections>
|
|
||||||
<action selector="savePrinterFeed:" target="-2" id="Dm3-h0-ch4"/>
|
|
||||||
</connections>
|
|
||||||
</toolbarItem>
|
|
||||||
<toolbarItem implicitItemIdentifier="NSToolbarPrintItem" explicitItemIdentifier="Print" id="mtd-zS-DXa"/>
|
|
||||||
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="AoG-LH-J4b"/>
|
|
||||||
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="Q0x-n5-Q2Y"/>
|
|
||||||
</allowedToolbarItems>
|
|
||||||
<defaultToolbarItems>
|
|
||||||
<toolbarItem reference="CBz-1N-o0Q"/>
|
|
||||||
<toolbarItem reference="Q0x-n5-Q2Y"/>
|
|
||||||
<toolbarItem reference="mtd-zS-DXa"/>
|
|
||||||
</defaultToolbarItems>
|
|
||||||
</toolbar>
|
|
||||||
<point key="canvasLocation" x="-159" y="356"/>
|
<point key="canvasLocation" x="-159" y="356"/>
|
||||||
</window>
|
</window>
|
||||||
|
<button verticalHuggingPriority="750" id="RLc-0I-sYZ">
|
||||||
|
<rect key="frame" x="0.5" y="0.0" width="48" height="25"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="roundTextured" title="Save" bezelStyle="texturedRounded" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="shw-MJ-B3T">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="miniSystem"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="savePrinterFeed:" target="-2" id="Y3g-fU-2te"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="-507" y="397"/>
|
||||||
|
</button>
|
||||||
<window title="Cheats" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="4Yb-Np-JrF" customClass="NSPanel">
|
<window title="Cheats" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="4Yb-Np-JrF" customClass="NSPanel">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||||
<rect key="contentRect" x="0.0" y="0.0" width="692" height="272"/>
|
<rect key="contentRect" x="0.0" y="0.0" width="692" height="272"/>
|
||||||
@ -909,7 +896,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<button verticalHuggingPriority="750" id="C3V-Ep-bMj">
|
<button verticalHuggingPriority="750" id="C3V-Ep-bMj">
|
||||||
<rect key="frame" x="202.5" y="12" width="83" height="23"/>
|
<rect key="frame" x="202.5" y="12" width="82" height="23"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<buttonCell key="cell" type="roundTextured" title="Import" bezelStyle="texturedRounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="mMP-KW-YNy">
|
<buttonCell key="cell" type="roundTextured" title="Import" bezelStyle="texturedRounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="mMP-KW-YNy">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -983,7 +970,7 @@
|
|||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" heightSizable="YES"/>
|
||||||
<clipView key="contentView" id="mzf-yu-RID">
|
<clipView key="contentView" id="mzf-yu-RID">
|
||||||
<rect key="frame" x="1" y="0.0" width="398" height="274"/>
|
<rect key="frame" x="1" y="0.0" width="398" height="274"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<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">
|
<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"/>
|
<rect key="frame" x="0.0" y="0.0" width="398" height="249"/>
|
||||||
@ -1004,7 +991,7 @@
|
|||||||
</buttonCell>
|
</buttonCell>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
</tableColumn>
|
</tableColumn>
|
||||||
<tableColumn width="60" minWidth="40" maxWidth="1000" id="9DZ-oW-Scx">
|
<tableColumn width="50" minWidth="40" maxWidth="1000" id="9DZ-oW-Scx">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Enabled">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Enabled">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -1029,7 +1016,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
</tableColumn>
|
</tableColumn>
|
||||||
<tableColumn editable="NO" width="134" minWidth="40" maxWidth="1000" id="ACq-gU-K36">
|
<tableColumn editable="NO" width="144" minWidth="40" maxWidth="1000" id="ACq-gU-K36">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Action">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Action">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -1082,7 +1069,6 @@
|
|||||||
</customObject>
|
</customObject>
|
||||||
</objects>
|
</objects>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="NSFolder" width="32" height="32"/>
|
|
||||||
<image name="NSStopProgressFreestandingTemplate" width="14" height="14"/>
|
<image name="NSStopProgressFreestandingTemplate" width="14" height="14"/>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
#import <Core/gb.h>
|
#import <Core/gb.h>
|
||||||
|
|
||||||
@interface GBAudioClient : NSObject
|
@interface GBAudioClient : NSObject
|
||||||
@property (nonatomic, strong) void (^renderBlock)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer);
|
@property (strong) void (^renderBlock)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer);
|
||||||
@property (nonatomic, readonly) UInt32 rate;
|
@property (readonly) UInt32 rate;
|
||||||
@property (nonatomic, readonly, getter=isPlaying) bool playing;
|
@property (readonly, getter=isPlaying) bool playing;
|
||||||
-(void) start;
|
-(void) start;
|
||||||
-(void) stop;
|
-(void) stop;
|
||||||
-(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer)) block
|
-(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer)) block
|
||||||
|
@ -90,7 +90,7 @@ static OSStatus render(
|
|||||||
{
|
{
|
||||||
OSErr err = AudioOutputUnitStart(audioUnit);
|
OSErr err = AudioOutputUnitStart(audioUnit);
|
||||||
NSAssert1(err == noErr, @"Error starting unit: %hd", err);
|
NSAssert1(err == noErr, @"Error starting unit: %hd", err);
|
||||||
_playing = true;
|
_playing = YES;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ static OSStatus render(
|
|||||||
-(void) stop
|
-(void) stop
|
||||||
{
|
{
|
||||||
AudioOutputUnitStop(audioUnit);
|
AudioOutputUnitStop(audioUnit);
|
||||||
_playing = false;
|
_playing = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) dealloc
|
-(void) dealloc
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
|
|
||||||
- (void)awakeFromNib
|
- (void)awakeFromNib
|
||||||
{
|
{
|
||||||
self.wantsLayer = true;
|
self.wantsLayer = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)wantsUpdateLayer
|
- (BOOL)wantsUpdateLayer
|
||||||
{
|
{
|
||||||
return true;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateLayer
|
- (void)updateLayer
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
@interface GBCheatTextFieldCell : NSTextFieldCell
|
@interface GBCheatTextFieldCell : NSTextFieldCell
|
||||||
@property (nonatomic) bool usesAddressFormat;
|
@property bool usesAddressFormat;
|
||||||
@end
|
@end
|
||||||
|
@ -114,7 +114,7 @@
|
|||||||
return _fieldEditor;
|
return _fieldEditor;
|
||||||
}
|
}
|
||||||
_fieldEditor = [[GBCheatTextView alloc] initWithFrame:controlView.frame];
|
_fieldEditor = [[GBCheatTextView alloc] initWithFrame:controlView.frame];
|
||||||
_fieldEditor.fieldEditor = true;
|
_fieldEditor.fieldEditor = YES;
|
||||||
_fieldEditor.usesAddressFormat = self.usesAddressFormat;
|
_fieldEditor.usesAddressFormat = self.usesAddressFormat;
|
||||||
return _fieldEditor;
|
return _fieldEditor;
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,15 @@
|
|||||||
#import "Document.h"
|
#import "Document.h"
|
||||||
|
|
||||||
@interface GBCheatWindowController : NSObject <NSTableViewDelegate, NSTableViewDataSource, NSTextFieldDelegate>
|
@interface GBCheatWindowController : NSObject <NSTableViewDelegate, NSTableViewDataSource, NSTextFieldDelegate>
|
||||||
@property (nonatomic, weak) IBOutlet NSTableView *cheatsTable;
|
@property (weak) IBOutlet NSTableView *cheatsTable;
|
||||||
@property (nonatomic, weak) IBOutlet NSTextField *addressField;
|
@property (weak) IBOutlet NSTextField *addressField;
|
||||||
@property (nonatomic, weak) IBOutlet NSTextField *valueField;
|
@property (weak) IBOutlet NSTextField *valueField;
|
||||||
@property (nonatomic, weak) IBOutlet NSTextField *oldValueField;
|
@property (weak) IBOutlet NSTextField *oldValueField;
|
||||||
@property (nonatomic, weak) IBOutlet NSButton *oldValueCheckbox;
|
@property (weak) IBOutlet NSButton *oldValueCheckbox;
|
||||||
@property (nonatomic, weak) IBOutlet NSTextField *descriptionField;
|
@property (weak) IBOutlet NSTextField *descriptionField;
|
||||||
@property (nonatomic, weak) IBOutlet NSTextField *importCodeField;
|
@property (weak) IBOutlet NSTextField *importCodeField;
|
||||||
@property (nonatomic, weak) IBOutlet NSTextField *importDescriptionField;
|
@property (weak) IBOutlet NSTextField *importDescriptionField;
|
||||||
@property (nonatomic, weak) IBOutlet Document *document;
|
@property (weak) IBOutlet Document *document;
|
||||||
- (void)cheatsUpdated;
|
- (void)cheatsUpdated;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
if (row >= cheatCount) {
|
if (row >= cheatCount) {
|
||||||
switch (columnIndex) {
|
switch (columnIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
return @YES;
|
return @(YES);
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
return @NO;
|
return @NO;
|
||||||
@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
switch (columnIndex) {
|
switch (columnIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
return @NO;
|
return @(NO);
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
return @(cheats[row]->enabled);
|
return @(cheats[row]->enabled);
|
||||||
|
@ -14,13 +14,7 @@ static inline double scale_channel(uint8_t x)
|
|||||||
- (void)setObjectValue:(id)objectValue
|
- (void)setObjectValue:(id)objectValue
|
||||||
{
|
{
|
||||||
_integerValue = [objectValue integerValue];
|
_integerValue = [objectValue integerValue];
|
||||||
uint8_t r = _integerValue & 0x1F,
|
super.objectValue = [NSString stringWithFormat:@"$%04x", (uint16_t)(_integerValue & 0x7FFF)];
|
||||||
g = (_integerValue >> 5) & 0x1F,
|
|
||||||
b = (_integerValue >> 10) & 0x1F;
|
|
||||||
super.objectValue = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"$%04x", (uint16_t)(_integerValue & 0x7FFF)] attributes:@{
|
|
||||||
NSForegroundColorAttributeName: r * 3 + g * 4 + b * 2 > 120? [NSColor blackColor] : [NSColor whiteColor],
|
|
||||||
NSFontAttributeName: [NSFont userFixedPitchFontOfSize:12]
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)integerValue
|
- (NSInteger)integerValue
|
||||||
@ -36,14 +30,13 @@ static inline double scale_channel(uint8_t x)
|
|||||||
|
|
||||||
- (NSColor *) backgroundColor
|
- (NSColor *) backgroundColor
|
||||||
{
|
{
|
||||||
/* Todo: color correction */
|
|
||||||
uint16_t color = self.integerValue;
|
uint16_t color = self.integerValue;
|
||||||
return [NSColor colorWithRed:scale_channel(color) green:scale_channel(color >> 5) blue:scale_channel(color >> 10) alpha:1.0];
|
return [NSColor colorWithRed:scale_channel(color) green:scale_channel(color >> 5) blue:scale_channel(color >> 10) alpha:1.0];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)drawsBackground
|
- (BOOL)drawsBackground
|
||||||
{
|
{
|
||||||
return true;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
@interface NSSlider (GBHueSlider)
|
|
||||||
-(NSColor *)colorValue;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface GBHueSliderCell : NSSliderCell
|
|
||||||
-(NSColor *)colorValue;
|
|
||||||
@end
|
|
@ -1,113 +0,0 @@
|
|||||||
#import "GBHueSliderCell.h"
|
|
||||||
|
|
||||||
@interface NSSliderCell(privateAPI)
|
|
||||||
- (double)_normalizedDoubleValue;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation GBHueSliderCell
|
|
||||||
{
|
|
||||||
bool _drawingTrack;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSColor *)colorValue
|
|
||||||
{
|
|
||||||
double hue = self.doubleValue / 360.0;
|
|
||||||
double r = 0, g = 0, b =0 ;
|
|
||||||
double t = fmod(hue * 6, 1);
|
|
||||||
switch ((int)(hue * 6) % 6) {
|
|
||||||
case 0:
|
|
||||||
r = 1;
|
|
||||||
g = t;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
r = 1 - t;
|
|
||||||
g = 1;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
g = 1;
|
|
||||||
b = t;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
g = 1 - t;
|
|
||||||
b = 1;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
b = 1;
|
|
||||||
r = t;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
b = 1 - t;
|
|
||||||
r = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return [NSColor colorWithRed:r green:g blue:b alpha:1.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)drawKnob:(NSRect)knobRect
|
|
||||||
{
|
|
||||||
[super drawKnob:knobRect];
|
|
||||||
NSRect peekRect = knobRect;
|
|
||||||
peekRect.size.width /= 2;
|
|
||||||
peekRect.size.height = peekRect.size.width;
|
|
||||||
peekRect.origin.x += peekRect.size.width / 2;
|
|
||||||
peekRect.origin.y += peekRect.size.height / 2;
|
|
||||||
NSColor *color = self.colorValue;
|
|
||||||
if (!self.enabled) {
|
|
||||||
color = [color colorWithAlphaComponent:0.5];
|
|
||||||
}
|
|
||||||
[color setFill];
|
|
||||||
NSBezierPath *path = [NSBezierPath bezierPathWithOvalInRect:peekRect];
|
|
||||||
[path fill];
|
|
||||||
[[NSColor colorWithWhite:0 alpha:0.25] setStroke];
|
|
||||||
[path setLineWidth:0.5];
|
|
||||||
[path stroke];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(double)_normalizedDoubleValue
|
|
||||||
{
|
|
||||||
if (_drawingTrack) return 0;
|
|
||||||
return [super _normalizedDoubleValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)drawBarInside:(NSRect)rect flipped:(BOOL)flipped
|
|
||||||
{
|
|
||||||
if (!self.enabled) {
|
|
||||||
[super drawBarInside:rect flipped:flipped];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_drawingTrack = true;
|
|
||||||
[super drawBarInside:rect flipped:flipped];
|
|
||||||
_drawingTrack = false;
|
|
||||||
|
|
||||||
NSGradient *gradient = [[NSGradient alloc] initWithColors:@[
|
|
||||||
[NSColor redColor],
|
|
||||||
[NSColor yellowColor],
|
|
||||||
[NSColor greenColor],
|
|
||||||
[NSColor cyanColor],
|
|
||||||
[NSColor blueColor],
|
|
||||||
[NSColor magentaColor],
|
|
||||||
[NSColor redColor],
|
|
||||||
]];
|
|
||||||
|
|
||||||
rect.origin.y += rect.size.height / 2 - 0.5;
|
|
||||||
rect.size.height = 1;
|
|
||||||
rect.size.width -= 2;
|
|
||||||
rect.origin.x += 1;
|
|
||||||
[[NSColor redColor] set];
|
|
||||||
NSRectFill(rect);
|
|
||||||
|
|
||||||
rect.size.width -= self.knobThickness + 2;
|
|
||||||
rect.origin.x += self.knobThickness / 2 - 1;
|
|
||||||
|
|
||||||
[gradient drawInRect:rect angle:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation NSSlider (GBHueSlider)
|
|
||||||
- (NSColor *)colorValue
|
|
||||||
{
|
|
||||||
return ((GBHueSliderCell *)self.cell).colorValue;
|
|
||||||
}
|
|
||||||
@end
|
|
@ -3,17 +3,17 @@
|
|||||||
@protocol GBImageViewDelegate;
|
@protocol GBImageViewDelegate;
|
||||||
|
|
||||||
@interface GBImageViewGridConfiguration : NSObject
|
@interface GBImageViewGridConfiguration : NSObject
|
||||||
@property (nonatomic, strong) NSColor *color;
|
@property NSColor *color;
|
||||||
@property (nonatomic) NSUInteger size;
|
@property NSUInteger size;
|
||||||
- (instancetype) initWithColor: (NSColor *) color size: (NSUInteger) size;
|
- (instancetype) initWithColor: (NSColor *) color size: (NSUInteger) size;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GBImageView : NSImageView
|
@interface GBImageView : NSImageView
|
||||||
@property (nonatomic, strong) NSArray<GBImageViewGridConfiguration *> *horizontalGrids;
|
@property (nonatomic) NSArray *horizontalGrids;
|
||||||
@property (nonatomic, strong) NSArray<GBImageViewGridConfiguration *> *verticalGrids;
|
@property (nonatomic) NSArray *verticalGrids;
|
||||||
@property (nonatomic) bool displayScrollRect;
|
@property (nonatomic) bool displayScrollRect;
|
||||||
@property NSRect scrollRect;
|
@property NSRect scrollRect;
|
||||||
@property (nonatomic, weak) IBOutlet id<GBImageViewDelegate> delegate;
|
@property (weak) IBOutlet id<GBImageViewDelegate> delegate;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@protocol GBImageViewDelegate <NSObject>
|
@protocol GBImageViewDelegate <NSObject>
|
||||||
|
@ -12,6 +12,6 @@ typedef enum {
|
|||||||
|
|
||||||
@interface GBMemoryByteArray : HFByteArray
|
@interface GBMemoryByteArray : HFByteArray
|
||||||
- (instancetype) initWithDocument:(Document *)document;
|
- (instancetype) initWithDocument:(Document *)document;
|
||||||
@property (nonatomic) uint16_t selectedBank;
|
@property uint16_t selectedBank;
|
||||||
@property (nonatomic) GB_memory_mode_t mode;
|
@property GB_memory_mode_t mode;
|
||||||
@end
|
@end
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
@interface GBOSDView : NSView
|
|
||||||
@property bool usesSGBScale;
|
|
||||||
- (void)displayText:(NSString *)text;
|
|
||||||
@end
|
|
@ -1,104 +0,0 @@
|
|||||||
#import "GBOSDView.h"
|
|
||||||
|
|
||||||
@implementation GBOSDView
|
|
||||||
{
|
|
||||||
bool _usesSGBScale;
|
|
||||||
NSString *_text;
|
|
||||||
double _animation;
|
|
||||||
NSTimer *_timer;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setUsesSGBScale:(bool)usesSGBScale
|
|
||||||
{
|
|
||||||
_usesSGBScale = usesSGBScale;
|
|
||||||
[self setNeedsDisplay:true];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (bool)usesSGBScale
|
|
||||||
{
|
|
||||||
return _usesSGBScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)displayText:(NSString *)text
|
|
||||||
{
|
|
||||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBOSDEnabled"]) return;
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
if (![_text isEqualToString:text]) {
|
|
||||||
[self setNeedsDisplay:true];
|
|
||||||
}
|
|
||||||
_text = text;
|
|
||||||
self.alphaValue = 1.0;
|
|
||||||
_animation = 2.5;
|
|
||||||
// Longer strings should appear longer
|
|
||||||
if ([_text rangeOfString:@"\n"].location != NSNotFound) {
|
|
||||||
_animation += 4;
|
|
||||||
}
|
|
||||||
[_timer invalidate];
|
|
||||||
self.hidden = false;
|
|
||||||
_timer = [NSTimer scheduledTimerWithTimeInterval:0.025 target:self selector:@selector(animate) userInfo:nil repeats:true];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)animate
|
|
||||||
{
|
|
||||||
_animation -= 0.1;
|
|
||||||
if (_animation < 1.0) {
|
|
||||||
self.alphaValue = _animation;
|
|
||||||
};
|
|
||||||
if (_animation == 0) {
|
|
||||||
self.hidden = true;
|
|
||||||
[_timer invalidate];
|
|
||||||
_text = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(NSRect)dirtyRect
|
|
||||||
{
|
|
||||||
[super drawRect:dirtyRect];
|
|
||||||
if (!_text.length) return;
|
|
||||||
|
|
||||||
double fontSize = 8;
|
|
||||||
NSSize size = self.frame.size;
|
|
||||||
if (_usesSGBScale) {
|
|
||||||
fontSize *= MIN(size.width / 256, size.height / 224);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fontSize *= MIN(size.width / 160, size.height / 144);
|
|
||||||
}
|
|
||||||
|
|
||||||
NSFont *font = [NSFont boldSystemFontOfSize:fontSize];
|
|
||||||
|
|
||||||
/* The built in stroke attribute uses an inside stroke, which is typographically terrible.
|
|
||||||
We'll use a naïve manual stroke instead which looks better. */
|
|
||||||
|
|
||||||
NSDictionary *attributes = @{
|
|
||||||
NSFontAttributeName: font,
|
|
||||||
NSForegroundColorAttributeName: [NSColor blackColor],
|
|
||||||
};
|
|
||||||
|
|
||||||
NSAttributedString *text = [[NSAttributedString alloc] initWithString:_text attributes:attributes];
|
|
||||||
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize + 1, fontSize + 0)];
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize - 1, fontSize + 0)];
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize + 0, fontSize + 1)];
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize + 0, fontSize - 1)];
|
|
||||||
|
|
||||||
// The uses of sqrt(2)/2, which is more correct, results in severe ugly-looking rounding errors
|
|
||||||
if (self.window.screen.backingScaleFactor > 1) {
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize + 0.5, fontSize + 0.5)];
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize - 0.5, fontSize + 0.5)];
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize - 0.5, fontSize - 0.5)];
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize + 0.5, fontSize - 0.5)];
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes = @{
|
|
||||||
NSFontAttributeName: font,
|
|
||||||
NSForegroundColorAttributeName: [NSColor whiteColor],
|
|
||||||
};
|
|
||||||
|
|
||||||
text = [[NSAttributedString alloc] initWithString:_text attributes:attributes];
|
|
||||||
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize, fontSize)];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -2,5 +2,5 @@
|
|||||||
#import "GBGLShader.h"
|
#import "GBGLShader.h"
|
||||||
|
|
||||||
@interface GBOpenGLView : NSOpenGLView
|
@interface GBOpenGLView : NSOpenGLView
|
||||||
@property (nonatomic) GBGLShader *shader;
|
@property GBGLShader *shader;
|
||||||
@end
|
@end
|
||||||
|
@ -34,6 +34,6 @@
|
|||||||
- (void) filterChanged
|
- (void) filterChanged
|
||||||
{
|
{
|
||||||
self.shader = nil;
|
self.shader = nil;
|
||||||
[self setNeedsDisplay:true];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
#import <AppKit/AppKit.h>
|
|
||||||
#import <Core/gb.h>
|
|
||||||
|
|
||||||
@interface GBPaletteEditorController : NSObject<NSTableViewDataSource, NSTableViewDelegate>
|
|
||||||
@property (weak) IBOutlet NSColorWell *colorWell0;
|
|
||||||
@property (weak) IBOutlet NSColorWell *colorWell1;
|
|
||||||
@property (weak) IBOutlet NSColorWell *colorWell2;
|
|
||||||
@property (weak) IBOutlet NSColorWell *colorWell3;
|
|
||||||
@property (weak) IBOutlet NSColorWell *colorWell4;
|
|
||||||
@property (weak) IBOutlet NSButton *disableLCDColorCheckbox;
|
|
||||||
@property (weak) IBOutlet NSButton *manualModeCheckbox;
|
|
||||||
@property (weak) IBOutlet NSSlider *brightnessSlider;
|
|
||||||
@property (weak) IBOutlet NSSlider *hueSlider;
|
|
||||||
@property (weak) IBOutlet NSSlider *hueStrengthSlider;
|
|
||||||
@property (weak) IBOutlet NSTableView *themesList;
|
|
||||||
@property (weak) IBOutlet NSMenu *menu;
|
|
||||||
+ (const GB_palette_t *)userPalette;
|
|
||||||
@end
|
|
@ -1,378 +0,0 @@
|
|||||||
#import "GBPaletteEditorController.h"
|
|
||||||
#import "GBHueSliderCell.h"
|
|
||||||
#import <Core/gb.h>
|
|
||||||
|
|
||||||
#define MAGIC 'SBPL'
|
|
||||||
|
|
||||||
typedef struct __attribute__ ((packed)) {
|
|
||||||
uint32_t magic;
|
|
||||||
bool manual:1;
|
|
||||||
bool disabled_lcd_color:1;
|
|
||||||
unsigned padding:6;
|
|
||||||
struct GB_color_s colors[5];
|
|
||||||
int32_t brightness_bias;
|
|
||||||
uint32_t hue_bias;
|
|
||||||
uint32_t hue_bias_strength;
|
|
||||||
} theme_t;
|
|
||||||
|
|
||||||
static double blend(double from, double to, double position)
|
|
||||||
{
|
|
||||||
return from * (1 - position) + to * position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation NSColor (GBColor)
|
|
||||||
|
|
||||||
- (struct GB_color_s)gbColor
|
|
||||||
{
|
|
||||||
NSColor *sRGB = [self colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
|
|
||||||
return (struct GB_color_s){round(sRGB.redComponent * 255), round(sRGB.greenComponent * 255), round(sRGB.blueComponent * 255)};
|
|
||||||
}
|
|
||||||
|
|
||||||
- (uint32_t)intValue
|
|
||||||
{
|
|
||||||
struct GB_color_s color = self.gbColor;
|
|
||||||
return (color.r << 0) | (color.g << 8) | (color.b << 16) | 0xFF000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation GBPaletteEditorController
|
|
||||||
|
|
||||||
- (NSArray<NSColorWell *> *)colorWells
|
|
||||||
{
|
|
||||||
return @[_colorWell0, _colorWell1, _colorWell2, _colorWell3, _colorWell4];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)updateEnabledControls
|
|
||||||
{
|
|
||||||
if (self.manualModeCheckbox.state) {
|
|
||||||
_brightnessSlider.enabled = false;
|
|
||||||
_hueSlider.enabled = false;
|
|
||||||
_hueStrengthSlider.enabled = false;
|
|
||||||
_colorWell1.enabled = true;
|
|
||||||
_colorWell2.enabled = true;
|
|
||||||
_colorWell3.enabled = true;
|
|
||||||
if (!(_colorWell4.enabled = self.disableLCDColorCheckbox.state)) {
|
|
||||||
_colorWell4.color = _colorWell3.color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_colorWell1.enabled = false;
|
|
||||||
_colorWell2.enabled = false;
|
|
||||||
_colorWell3.enabled = false;
|
|
||||||
_colorWell4.enabled = true;
|
|
||||||
_brightnessSlider.enabled = true;
|
|
||||||
_hueSlider.enabled = true;
|
|
||||||
_hueStrengthSlider.enabled = true;
|
|
||||||
[self updateAutoColors];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSColor *)autoColorAtPositon:(double)position
|
|
||||||
{
|
|
||||||
NSColor *first = [_colorWell0.color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
|
|
||||||
NSColor *second = [_colorWell4.color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
|
|
||||||
double brightness = 1 / pow(4, (_brightnessSlider.doubleValue - 128) / 128.0);
|
|
||||||
position = pow(position, brightness);
|
|
||||||
NSColor *hue = _hueSlider.colorValue;
|
|
||||||
double bias = _hueStrengthSlider.doubleValue / 256.0;
|
|
||||||
double red = 1 / pow(4, (hue.redComponent * 2 - 1) * bias);
|
|
||||||
double green = 1 / pow(4, (hue.greenComponent * 2 - 1) * bias);
|
|
||||||
double blue = 1 / pow(4, (hue.blueComponent * 2 - 1) * bias);
|
|
||||||
NSColor *ret = [NSColor colorWithRed:blend(first.redComponent, second.redComponent, pow(position, red))
|
|
||||||
green:blend(first.greenComponent, second.greenComponent, pow(position, green))
|
|
||||||
blue:blend(first.blueComponent, second.blueComponent, pow(position, blue))
|
|
||||||
alpha:1.0];
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)updateAutoColors:(id)sender
|
|
||||||
{
|
|
||||||
if (!self.manualModeCheckbox.state) {
|
|
||||||
[self updateAutoColors];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[self savePalette:sender];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)updateAutoColors
|
|
||||||
{
|
|
||||||
if (_disableLCDColorCheckbox.state) {
|
|
||||||
_colorWell1.color = [self autoColorAtPositon:8 / 25.0];
|
|
||||||
_colorWell2.color = [self autoColorAtPositon:16 / 25.0];
|
|
||||||
_colorWell3.color = [self autoColorAtPositon:24 / 25.0];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_colorWell1.color = [self autoColorAtPositon:1 / 3.0];
|
|
||||||
_colorWell2.color = [self autoColorAtPositon:2 / 3.0];
|
|
||||||
_colorWell3.color = _colorWell4.color;
|
|
||||||
}
|
|
||||||
[self savePalette:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)disabledLCDColorCheckboxChanged:(id)sender
|
|
||||||
{
|
|
||||||
[self updateEnabledControls];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)manualModeChanged:(id)sender
|
|
||||||
{
|
|
||||||
[self updateEnabledControls];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)updateColor4:(id)sender
|
|
||||||
{
|
|
||||||
if (!self.disableLCDColorCheckbox.state) {
|
|
||||||
self.colorWell4.color = self.colorWell3.color;
|
|
||||||
}
|
|
||||||
[self savePalette:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
|
|
||||||
if (themes.count == 0) {
|
|
||||||
[defaults setObject:@"Untitled Palette" forKey:@"GBCurrentTheme"];
|
|
||||||
[self savePalette:nil];
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return themes.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
|
||||||
{
|
|
||||||
NSString *oldName = [self tableView:tableView objectValueForTableColumn:tableColumn row:row];
|
|
||||||
if ([oldName isEqualToString:object]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSMutableDictionary *themes = [[defaults dictionaryForKey:@"GBThemes"] ?: @{} mutableCopy];
|
|
||||||
NSString *newName = object;
|
|
||||||
unsigned i = 2;
|
|
||||||
if (!newName.length) {
|
|
||||||
newName = @"Untitled Palette";
|
|
||||||
}
|
|
||||||
while (themes[newName]) {
|
|
||||||
newName = [NSString stringWithFormat:@"%@ %d", object, i];
|
|
||||||
}
|
|
||||||
themes[newName] = themes[oldName];
|
|
||||||
[themes removeObjectForKey:oldName];
|
|
||||||
if ([oldName isEqualToString:[defaults stringForKey:@"GBCurrentTheme"]]) {
|
|
||||||
[defaults setObject:newName forKey:@"GBCurrentTheme"];
|
|
||||||
}
|
|
||||||
[defaults setObject:themes forKey:@"GBThemes"];
|
|
||||||
[tableView reloadData];
|
|
||||||
[self awakeFromNib];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)deleteTheme:(id)sender
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSString *name = [defaults stringForKey:@"GBCurrentTheme"];
|
|
||||||
NSMutableDictionary *themes = [[defaults dictionaryForKey:@"GBThemes"] ?: @{} mutableCopy];
|
|
||||||
[themes removeObjectForKey:name];
|
|
||||||
[defaults setObject:themes forKey:@"GBThemes"];
|
|
||||||
[_themesList reloadData];
|
|
||||||
[self awakeFromNib];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tableViewSelectionDidChange:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
NSString *name = [self tableView:nil objectValueForTableColumn:nil row:_themesList.selectedRow];
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:name forKey:@"GBCurrentTheme"];
|
|
||||||
[self loadPalette];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tableViewSelectionIsChanging:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
[self tableViewSelectionDidChange:notification];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)awakeFromNib
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
|
|
||||||
NSString *theme = [defaults stringForKey:@"GBCurrentTheme"];
|
|
||||||
if (theme && themes[theme]) {
|
|
||||||
unsigned index = [[themes.allKeys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] indexOfObject:theme];
|
|
||||||
[_themesList selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:false];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[_themesList selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:false];
|
|
||||||
}
|
|
||||||
[self tableViewSelectionDidChange:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)addTheme:(id)sender
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
|
|
||||||
NSString *newName = @"Untitled Palette";
|
|
||||||
unsigned i = 2;
|
|
||||||
while (themes[newName]) {
|
|
||||||
newName = [NSString stringWithFormat:@"Untitled Palette %d", i++];
|
|
||||||
}
|
|
||||||
[defaults setObject:newName forKey:@"GBCurrentTheme"];
|
|
||||||
[self savePalette:sender];
|
|
||||||
[_themesList reloadData];
|
|
||||||
[self awakeFromNib];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
|
|
||||||
return [themes.allKeys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)][row];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)loadPalette
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *theme = [defaults dictionaryForKey:@"GBThemes"][[defaults stringForKey:@"GBCurrentTheme"]];
|
|
||||||
NSArray *colors = theme[@"Colors"];
|
|
||||||
if (colors.count == 5) {
|
|
||||||
unsigned i = 0;
|
|
||||||
for (NSNumber *color in colors) {
|
|
||||||
uint32_t c = [color unsignedIntValue];
|
|
||||||
self.colorWells[i++].color = [NSColor colorWithRed:(c & 0xFF) / 255.0
|
|
||||||
green:((c >> 8) & 0xFF) / 255.0
|
|
||||||
blue:((c >> 16) & 0xFF) / 255.0
|
|
||||||
alpha:1.0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_disableLCDColorCheckbox.state = [theme[@"DisabledLCDColor"] boolValue];
|
|
||||||
_manualModeCheckbox.state = [theme[@"Manual"] boolValue];
|
|
||||||
_brightnessSlider.doubleValue = [theme[@"BrightnessBias"] doubleValue] * 128 + 128;
|
|
||||||
_hueSlider.doubleValue = [theme[@"HueBias"] doubleValue] * 360;
|
|
||||||
_hueStrengthSlider.doubleValue = [theme[@"HueBiasStrength"] doubleValue] * 256;
|
|
||||||
[self updateEnabledControls];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)savePalette:(id)sender
|
|
||||||
{
|
|
||||||
NSDictionary *theme = @{
|
|
||||||
@"Colors":
|
|
||||||
@[@(_colorWell0.color.intValue),
|
|
||||||
@(_colorWell1.color.intValue),
|
|
||||||
@(_colorWell2.color.intValue),
|
|
||||||
@(_colorWell3.color.intValue),
|
|
||||||
@(_colorWell4.color.intValue)],
|
|
||||||
@"DisabledLCDColor": _disableLCDColorCheckbox.state? @YES : @NO,
|
|
||||||
@"Manual": _manualModeCheckbox.state? @YES : @NO,
|
|
||||||
@"BrightnessBias": @((_brightnessSlider.doubleValue - 128) / 128.0),
|
|
||||||
@"HueBias": @(_hueSlider.doubleValue / 360.0),
|
|
||||||
@"HueBiasStrength": @(_hueStrengthSlider.doubleValue / 256.0)
|
|
||||||
};
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSMutableDictionary *themes = [[defaults dictionaryForKey:@"GBThemes"] ?: @{} mutableCopy];
|
|
||||||
themes[[defaults stringForKey:@"GBCurrentTheme"]] = theme;
|
|
||||||
[defaults setObject:themes forKey:@"GBThemes"];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (const GB_palette_t *)userPalette
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
switch ([defaults integerForKey:@"GBColorPalette"]) {
|
|
||||||
case 1: return &GB_PALETTE_DMG;
|
|
||||||
case 2: return &GB_PALETTE_MGB;
|
|
||||||
case 3: return &GB_PALETTE_GBL;
|
|
||||||
default: return &GB_PALETTE_GREY;
|
|
||||||
case -1: {
|
|
||||||
static GB_palette_t customPalette;
|
|
||||||
NSArray *colors = [defaults dictionaryForKey:@"GBThemes"][[defaults stringForKey:@"GBCurrentTheme"]][@"Colors"];
|
|
||||||
if (colors.count == 5) {
|
|
||||||
unsigned i = 0;
|
|
||||||
for (NSNumber *color in colors) {
|
|
||||||
uint32_t c = [color unsignedIntValue];
|
|
||||||
customPalette.colors[i++] = (struct GB_color_s) {c, c >> 8, c >> 16};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &customPalette;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)export:(id)sender
|
|
||||||
{
|
|
||||||
NSSavePanel *savePanel = [NSSavePanel savePanel];
|
|
||||||
[savePanel setAllowedFileTypes:@[@"sbp"]];
|
|
||||||
savePanel.nameFieldStringValue = [NSString stringWithFormat:@"%@.sbp", [[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"]];
|
|
||||||
if ([savePanel runModal] == NSModalResponseOK) {
|
|
||||||
theme_t theme = {0,};
|
|
||||||
theme.magic = MAGIC;
|
|
||||||
theme.manual = _manualModeCheckbox.state;
|
|
||||||
theme.disabled_lcd_color = _disableLCDColorCheckbox.state;
|
|
||||||
unsigned i = 0;
|
|
||||||
for (NSColorWell *well in self.colorWells) {
|
|
||||||
theme.colors[i++] = well.color.gbColor;
|
|
||||||
}
|
|
||||||
theme.brightness_bias = (_brightnessSlider.doubleValue - 128) * (0x40000000 / 128);
|
|
||||||
theme.hue_bias = round(_hueSlider.doubleValue * (0x80000000 / 360.0));
|
|
||||||
theme.hue_bias_strength = (_hueStrengthSlider.doubleValue) * (0x80000000 / 256);
|
|
||||||
size_t size = sizeof(theme);
|
|
||||||
if (theme.manual) {
|
|
||||||
size = theme.disabled_lcd_color? 5 + 5 * sizeof(theme.colors[0]) : 5 + 4 * sizeof(theme.colors[0]);
|
|
||||||
}
|
|
||||||
[[NSData dataWithBytes:&theme length:size] writeToURL:savePanel.URL atomically:false];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)import:(id)sender
|
|
||||||
{
|
|
||||||
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
|
||||||
[openPanel setAllowedFileTypes:@[@"sbp"]];
|
|
||||||
if ([openPanel runModal] == NSModalResponseOK) {
|
|
||||||
NSData *data = [NSData dataWithContentsOfURL:openPanel.URL];
|
|
||||||
theme_t theme = {0,};
|
|
||||||
memcpy(&theme, data.bytes, MIN(sizeof(theme), data.length));
|
|
||||||
if (theme.magic != MAGIC) {
|
|
||||||
NSBeep();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_manualModeCheckbox.state = theme.manual;
|
|
||||||
_disableLCDColorCheckbox.state = theme.disabled_lcd_color;
|
|
||||||
unsigned i = 0;
|
|
||||||
for (NSColorWell *well in self.colorWells) {
|
|
||||||
well.color = [NSColor colorWithRed:theme.colors[i].r / 255.0
|
|
||||||
green:theme.colors[i].g / 255.0
|
|
||||||
blue:theme.colors[i].b / 255.0
|
|
||||||
alpha:1.0];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (!theme.disabled_lcd_color) {
|
|
||||||
_colorWell4.color = _colorWell3.color;
|
|
||||||
}
|
|
||||||
_brightnessSlider.doubleValue = theme.brightness_bias / (0x40000000 / 128.0) + 128;
|
|
||||||
_hueSlider.doubleValue = theme.hue_bias / (0x80000000 / 360.0);
|
|
||||||
_hueStrengthSlider.doubleValue = theme.hue_bias_strength / (0x80000000 / 256.0);
|
|
||||||
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
|
|
||||||
NSString *baseName = openPanel.URL.lastPathComponent.stringByDeletingPathExtension;
|
|
||||||
NSString *newName = baseName;
|
|
||||||
i = 2;
|
|
||||||
while (themes[newName]) {
|
|
||||||
newName = [NSString stringWithFormat:@"%@ %d", baseName, i++];
|
|
||||||
}
|
|
||||||
[defaults setObject:newName forKey:@"GBCurrentTheme"];
|
|
||||||
[self savePalette:sender];
|
|
||||||
[self awakeFromNib];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)done:(NSButton *)sender
|
|
||||||
{
|
|
||||||
[sender.window.sheetParent endSheet:sender.window];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)init
|
|
||||||
{
|
|
||||||
static id singleton = nil;
|
|
||||||
if (singleton) return singleton;
|
|
||||||
return (singleton = [super init]);
|
|
||||||
}
|
|
||||||
@end
|
|
@ -1,37 +1,27 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import <JoyKit/JoyKit.h>
|
#import <JoyKit/JoyKit.h>
|
||||||
#import "GBPaletteEditorController.h"
|
|
||||||
|
|
||||||
@interface GBPreferencesWindow : NSWindow <NSTableViewDelegate, NSTableViewDataSource, JOYListener>
|
@interface GBPreferencesWindow : NSWindow <NSTableViewDelegate, NSTableViewDataSource, JOYListener>
|
||||||
@property (nonatomic, strong) IBOutlet NSTableView *controlsTableView;
|
@property IBOutlet NSTableView *controlsTableView;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *graphicsFilterPopupButton;
|
@property IBOutlet NSPopUpButton *graphicsFilterPopupButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSButton *analogControlsCheckbox;
|
@property (strong) IBOutlet NSButton *analogControlsCheckbox;
|
||||||
@property (nonatomic, strong) IBOutlet NSButton *aspectRatioCheckbox;
|
@property (strong) IBOutlet NSButton *aspectRatioCheckbox;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *frameBlendingModePopupButton;
|
@property (strong) IBOutlet NSPopUpButton *frameBlendingModePopupButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *colorPalettePopupButton;
|
@property (strong) IBOutlet NSPopUpButton *colorPalettePopupButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *displayBorderPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *displayBorderPopupButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *rewindPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *rewindPopupButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *rtcPopupButton;
|
@property (strong) IBOutlet NSButton *configureJoypadButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSButton *configureJoypadButton;
|
@property (strong) IBOutlet NSButton *skipButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSButton *skipButton;
|
@property (strong) IBOutlet NSMenuItem *bootROMsFolderItem;
|
||||||
@property (nonatomic, strong) IBOutlet NSMenuItem *bootROMsFolderItem;
|
@property (strong) IBOutlet NSPopUpButtonCell *bootROMsButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButtonCell *bootROMsButton;
|
@property (strong) IBOutlet NSPopUpButton *rumbleModePopupButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *rumbleModePopupButton;
|
|
||||||
@property (nonatomic, weak) IBOutlet NSSlider *temperatureSlider;
|
@property (weak) IBOutlet NSPopUpButton *dmgPopupButton;
|
||||||
@property (nonatomic, weak) IBOutlet NSSlider *interferenceSlider;
|
@property (weak) IBOutlet NSPopUpButton *sgbPopupButton;
|
||||||
@property (nonatomic, weak) IBOutlet NSPopUpButton *dmgPopupButton;
|
@property (weak) IBOutlet NSPopUpButton *cgbPopupButton;
|
||||||
@property (nonatomic, weak) IBOutlet NSPopUpButton *sgbPopupButton;
|
@property (weak) IBOutlet NSPopUpButton *preferredJoypadButton;
|
||||||
@property (nonatomic, weak) IBOutlet NSPopUpButton *cgbPopupButton;
|
@property (weak) IBOutlet NSPopUpButton *playerListButton;
|
||||||
@property (nonatomic, weak) IBOutlet NSPopUpButton *preferredJoypadButton;
|
|
||||||
@property (nonatomic, weak) IBOutlet NSPopUpButton *playerListButton;
|
|
||||||
@property (nonatomic, weak) IBOutlet NSButton *autoUpdatesCheckbox;
|
|
||||||
@property (weak) IBOutlet NSSlider *volumeSlider;
|
|
||||||
@property (weak) IBOutlet NSButton *OSDCheckbox;
|
|
||||||
@property (weak) IBOutlet NSButton *screenshotFilterCheckbox;
|
|
||||||
@property (weak) IBOutlet GBPaletteEditorController *paletteEditorController;
|
|
||||||
@property (strong) IBOutlet NSWindow *paletteEditor;
|
|
||||||
@property (weak) IBOutlet NSButton *joystickMBC7Checkbox;
|
|
||||||
@property (weak) IBOutlet NSButton *mouseMBC7Checkbox;
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
#import "GBPreferencesWindow.h"
|
#import "GBPreferencesWindow.h"
|
||||||
#import "NSString+StringForKey.h"
|
#import "NSString+StringForKey.h"
|
||||||
#import "GBButtons.h"
|
#import "GBButtons.h"
|
||||||
#import "BigSurToolbar.h"
|
|
||||||
#import "GBViewMetal.h"
|
|
||||||
#import <Carbon/Carbon.h>
|
#import <Carbon/Carbon.h>
|
||||||
|
|
||||||
@implementation GBPreferencesWindow
|
@implementation GBPreferencesWindow
|
||||||
@ -20,7 +18,6 @@
|
|||||||
NSPopUpButton *_colorPalettePopupButton;
|
NSPopUpButton *_colorPalettePopupButton;
|
||||||
NSPopUpButton *_displayBorderPopupButton;
|
NSPopUpButton *_displayBorderPopupButton;
|
||||||
NSPopUpButton *_rewindPopupButton;
|
NSPopUpButton *_rewindPopupButton;
|
||||||
NSPopUpButton *_rtcPopupButton;
|
|
||||||
NSButton *_aspectRatioCheckbox;
|
NSButton *_aspectRatioCheckbox;
|
||||||
NSButton *_analogControlsCheckbox;
|
NSButton *_analogControlsCheckbox;
|
||||||
NSEventModifierFlags previousModifiers;
|
NSEventModifierFlags previousModifiers;
|
||||||
@ -28,14 +25,6 @@
|
|||||||
NSPopUpButton *_dmgPopupButton, *_sgbPopupButton, *_cgbPopupButton;
|
NSPopUpButton *_dmgPopupButton, *_sgbPopupButton, *_cgbPopupButton;
|
||||||
NSPopUpButton *_preferredJoypadButton;
|
NSPopUpButton *_preferredJoypadButton;
|
||||||
NSPopUpButton *_rumbleModePopupButton;
|
NSPopUpButton *_rumbleModePopupButton;
|
||||||
NSSlider *_temperatureSlider;
|
|
||||||
NSSlider *_interferenceSlider;
|
|
||||||
NSSlider *_volumeSlider;
|
|
||||||
NSButton *_autoUpdatesCheckbox;
|
|
||||||
NSButton *_OSDCheckbox;
|
|
||||||
NSButton *_screenshotFilterCheckbox;
|
|
||||||
NSButton *_joystickMBC7Checkbox;
|
|
||||||
NSButton *_mouseMBC7Checkbox;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSArray *)filterList
|
+ (NSArray *)filterList
|
||||||
@ -63,16 +52,11 @@
|
|||||||
return filters;
|
return filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSWindowToolbarStyle)toolbarStyle
|
|
||||||
{
|
|
||||||
return NSWindowToolbarStylePreference;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)close
|
- (void)close
|
||||||
{
|
{
|
||||||
joystick_configuration_state = -1;
|
joystick_configuration_state = -1;
|
||||||
[self.configureJoypadButton setEnabled:true];
|
[self.configureJoypadButton setEnabled:YES];
|
||||||
[self.skipButton setEnabled:false];
|
[self.skipButton setEnabled:NO];
|
||||||
[self.configureJoypadButton setTitle:@"Configure Controller"];
|
[self.configureJoypadButton setTitle:@"Configure Controller"];
|
||||||
[super close];
|
[super close];
|
||||||
}
|
}
|
||||||
@ -101,45 +85,11 @@
|
|||||||
[_colorCorrectionPopupButton selectItemAtIndex:mode];
|
[_colorCorrectionPopupButton selectItemAtIndex:mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSPopUpButton *)colorCorrectionPopupButton
|
- (NSPopUpButton *)colorCorrectionPopupButton
|
||||||
{
|
{
|
||||||
return _colorCorrectionPopupButton;
|
return _colorCorrectionPopupButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setTemperatureSlider:(NSSlider *)temperatureSlider
|
|
||||||
{
|
|
||||||
_temperatureSlider = temperatureSlider;
|
|
||||||
[temperatureSlider setDoubleValue:[[NSUserDefaults standardUserDefaults] doubleForKey:@"GBLightTemperature"] * 256];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSSlider *)temperatureSlider
|
|
||||||
{
|
|
||||||
return _temperatureSlider;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setInterferenceSlider:(NSSlider *)interferenceSlider
|
|
||||||
{
|
|
||||||
_interferenceSlider = interferenceSlider;
|
|
||||||
[interferenceSlider setDoubleValue:[[NSUserDefaults standardUserDefaults] doubleForKey:@"GBInterferenceVolume"] * 256];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSSlider *)interferenceSlider
|
|
||||||
{
|
|
||||||
return _interferenceSlider;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setVolumeSlider:(NSSlider *)volumeSlider
|
|
||||||
{
|
|
||||||
_volumeSlider = volumeSlider;
|
|
||||||
[volumeSlider setDoubleValue:[[NSUserDefaults standardUserDefaults] doubleForKey:@"GBVolume"] * 256];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSSlider *)volumeSlider
|
|
||||||
{
|
|
||||||
return _volumeSlider;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFrameBlendingModePopupButton:(NSPopUpButton *)frameBlendingModePopupButton
|
- (void)setFrameBlendingModePopupButton:(NSPopUpButton *)frameBlendingModePopupButton
|
||||||
{
|
{
|
||||||
_frameBlendingModePopupButton = frameBlendingModePopupButton;
|
_frameBlendingModePopupButton = frameBlendingModePopupButton;
|
||||||
@ -155,14 +105,8 @@
|
|||||||
- (void)setColorPalettePopupButton:(NSPopUpButton *)colorPalettePopupButton
|
- (void)setColorPalettePopupButton:(NSPopUpButton *)colorPalettePopupButton
|
||||||
{
|
{
|
||||||
_colorPalettePopupButton = colorPalettePopupButton;
|
_colorPalettePopupButton = colorPalettePopupButton;
|
||||||
[self updatePalettesMenu];
|
|
||||||
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"];
|
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"];
|
||||||
if (mode >= 0) {
|
[_colorPalettePopupButton selectItemAtIndex:mode];
|
||||||
[_colorPalettePopupButton selectItemWithTag:mode];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[_colorPalettePopupButton selectItemWithTitle:[[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"] ?: @""];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSPopUpButton *)colorPalettePopupButton
|
- (NSPopUpButton *)colorPalettePopupButton
|
||||||
@ -206,18 +150,6 @@
|
|||||||
return _rewindPopupButton;
|
return _rewindPopupButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSPopUpButton *)rtcPopupButton
|
|
||||||
{
|
|
||||||
return _rtcPopupButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setRtcPopupButton:(NSPopUpButton *)rtcPopupButton
|
|
||||||
{
|
|
||||||
_rtcPopupButton = rtcPopupButton;
|
|
||||||
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRTCMode"];
|
|
||||||
[_rtcPopupButton selectItemAtIndex:mode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setHighpassFilterPopupButton:(NSPopUpButton *)highpassFilterPopupButton
|
- (void)setHighpassFilterPopupButton:(NSPopUpButton *)highpassFilterPopupButton
|
||||||
{
|
{
|
||||||
_highpassFilterPopupButton = highpassFilterPopupButton;
|
_highpassFilterPopupButton = highpassFilterPopupButton;
|
||||||
@ -232,20 +164,6 @@
|
|||||||
return GBGameBoyButtonCount;
|
return GBGameBoyButtonCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (unsigned) usesForKey:(unsigned) key
|
|
||||||
{
|
|
||||||
unsigned ret = 0;
|
|
||||||
for (unsigned player = 4; player--;) {
|
|
||||||
for (unsigned button = player == 0? GBButtonCount:GBGameBoyButtonCount; button--;) {
|
|
||||||
NSNumber *other = [[NSUserDefaults standardUserDefaults] valueForKey:button_to_preference_name(button, player)];
|
|
||||||
if (other && [other unsignedIntValue] == key) {
|
|
||||||
ret++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
||||||
{
|
{
|
||||||
if ([tableColumn.identifier isEqualToString:@"keyName"]) {
|
if ([tableColumn.identifier isEqualToString:@"keyName"]) {
|
||||||
@ -258,12 +176,6 @@
|
|||||||
|
|
||||||
NSNumber *key = [[NSUserDefaults standardUserDefaults] valueForKey:button_to_preference_name(row, self.playerListButton.selectedTag)];
|
NSNumber *key = [[NSUserDefaults standardUserDefaults] valueForKey:button_to_preference_name(row, self.playerListButton.selectedTag)];
|
||||||
if (key) {
|
if (key) {
|
||||||
if ([self usesForKey:[key unsignedIntValue]] > 1) {
|
|
||||||
return [[NSAttributedString alloc] initWithString:[NSString displayStringForKeyCode: [key unsignedIntegerValue]]
|
|
||||||
attributes:@{NSForegroundColorAttributeName: [NSColor colorWithRed:0.9375 green:0.25 blue:0.25 alpha:1.0],
|
|
||||||
NSFontAttributeName: [NSFont boldSystemFontOfSize:[NSFont systemFontSize]]
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
return [NSString displayStringForKeyCode: [key unsignedIntegerValue]];
|
return [NSString displayStringForKeyCode: [key unsignedIntegerValue]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,12 +188,12 @@
|
|||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
is_button_being_modified = true;
|
is_button_being_modified = true;
|
||||||
button_being_modified = row;
|
button_being_modified = row;
|
||||||
tableView.enabled = false;
|
tableView.enabled = NO;
|
||||||
self.playerListButton.enabled = false;
|
self.playerListButton.enabled = NO;
|
||||||
[tableView reloadData];
|
[tableView reloadData];
|
||||||
[self makeFirstResponder:self];
|
[self makeFirstResponder:self];
|
||||||
});
|
});
|
||||||
return false;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)keyDown:(NSEvent *)theEvent
|
-(void)keyDown:(NSEvent *)theEvent
|
||||||
@ -297,8 +209,8 @@
|
|||||||
|
|
||||||
[[NSUserDefaults standardUserDefaults] setInteger:theEvent.keyCode
|
[[NSUserDefaults standardUserDefaults] setInteger:theEvent.keyCode
|
||||||
forKey:button_to_preference_name(button_being_modified, self.playerListButton.selectedTag)];
|
forKey:button_to_preference_name(button_being_modified, self.playerListButton.selectedTag)];
|
||||||
self.controlsTableView.enabled = true;
|
self.controlsTableView.enabled = YES;
|
||||||
self.playerListButton.enabled = true;
|
self.playerListButton.enabled = YES;
|
||||||
[self.controlsTableView reloadData];
|
[self.controlsTableView reloadData];
|
||||||
[self makeFirstResponder:self.controlsTableView];
|
[self makeFirstResponder:self.controlsTableView];
|
||||||
}
|
}
|
||||||
@ -326,19 +238,6 @@
|
|||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBHighpassFilterChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBHighpassFilterChanged" object:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (IBAction)changeMBC7JoystickOverride:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] == NSOnState
|
|
||||||
forKey:@"GBMBC7JoystickOverride"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)changeMBC7AllowMouse:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] == NSOnState
|
|
||||||
forKey:@"GBMBC7AllowMouse"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)changeAnalogControls:(id)sender
|
- (IBAction)changeAnalogControls:(id)sender
|
||||||
{
|
{
|
||||||
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] == NSOnState
|
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] == NSOnState
|
||||||
@ -359,27 +258,6 @@
|
|||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorCorrectionChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorCorrectionChanged" object:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)lightTemperatureChanged:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender doubleValue] / 256.0)
|
|
||||||
forKey:@"GBLightTemperature"];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBLightTemperatureChanged" object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)interferenceVolumeChanged:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender doubleValue] / 256.0)
|
|
||||||
forKey:@"GBInterferenceVolume"];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBInterferenceVolumeChanged" object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)volumeChanged:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender doubleValue] / 256.0)
|
|
||||||
forKey:@"GBVolume"];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBVolumeChanged" object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)franeBlendingModeChanged:(id)sender
|
- (IBAction)franeBlendingModeChanged:(id)sender
|
||||||
{
|
{
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
||||||
@ -388,51 +266,10 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updatePalettesMenu
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
|
|
||||||
NSMenu *menu = _colorPalettePopupButton.menu;
|
|
||||||
while (menu.itemArray.count != 4) {
|
|
||||||
[menu removeItemAtIndex:4];
|
|
||||||
}
|
|
||||||
[menu addItem:[NSMenuItem separatorItem]];
|
|
||||||
for (NSString *name in [themes.allKeys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]) {
|
|
||||||
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:name action:nil keyEquivalent:@""];
|
|
||||||
item.tag = -2;
|
|
||||||
[menu addItem:item];
|
|
||||||
}
|
|
||||||
if (themes) {
|
|
||||||
[menu addItem:[NSMenuItem separatorItem]];
|
|
||||||
}
|
|
||||||
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@"Custom…" action:nil keyEquivalent:@""];
|
|
||||||
item.tag = -1;
|
|
||||||
[menu addItem:item];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)colorPaletteChanged:(id)sender
|
- (IBAction)colorPaletteChanged:(id)sender
|
||||||
{
|
{
|
||||||
signed tag = [sender selectedItem].tag;
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
||||||
if (tag == -2) {
|
forKey:@"GBColorPalette"];
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@(-1)
|
|
||||||
forKey:@"GBColorPalette"];
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:[sender selectedItem].title
|
|
||||||
forKey:@"GBCurrentTheme"];
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (tag == -1) {
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@(-1)
|
|
||||||
forKey:@"GBColorPalette"];
|
|
||||||
[_paletteEditorController awakeFromNib];
|
|
||||||
[self beginSheet:_paletteEditor completionHandler:^(NSModalResponse returnCode) {
|
|
||||||
[self updatePalettesMenu];
|
|
||||||
[_colorPalettePopupButton selectItemWithTitle:[[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"] ?: @""];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedItem].tag)
|
|
||||||
forKey:@"GBColorPalette"];
|
|
||||||
}
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,24 +294,10 @@
|
|||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBRewindLengthChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBRewindLengthChanged" object:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)rtcModeChanged:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
|
||||||
forKey:@"GBRTCMode"];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBRTCModeChanged" object:nil];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)changeAutoUpdates:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] == NSOnState
|
|
||||||
forKey:@"GBAutoUpdatesEnabled"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction) configureJoypad:(id)sender
|
- (IBAction) configureJoypad:(id)sender
|
||||||
{
|
{
|
||||||
[self.configureJoypadButton setEnabled:false];
|
[self.configureJoypadButton setEnabled:NO];
|
||||||
[self.skipButton setEnabled:true];
|
[self.skipButton setEnabled:YES];
|
||||||
joystick_being_configured = nil;
|
joystick_being_configured = nil;
|
||||||
[self advanceConfigurationStateMachine];
|
[self advanceConfigurationStateMachine];
|
||||||
}
|
}
|
||||||
@ -495,8 +318,8 @@
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
joystick_configuration_state = -1;
|
joystick_configuration_state = -1;
|
||||||
[self.configureJoypadButton setEnabled:true];
|
[self.configureJoypadButton setEnabled:YES];
|
||||||
[self.skipButton setEnabled:false];
|
[self.skipButton setEnabled:NO];
|
||||||
[self.configureJoypadButton setTitle:@"Configure Joypad"];
|
[self.configureJoypadButton setTitle:@"Configure Joypad"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -543,36 +366,30 @@
|
|||||||
|
|
||||||
|
|
||||||
static const unsigned gb_to_joykit[] = {
|
static const unsigned gb_to_joykit[] = {
|
||||||
[GBRight] = JOYButtonUsageDPadRight,
|
[GBRight]=JOYButtonUsageDPadRight,
|
||||||
[GBLeft] = JOYButtonUsageDPadLeft,
|
[GBLeft]=JOYButtonUsageDPadLeft,
|
||||||
[GBUp] = JOYButtonUsageDPadUp,
|
[GBUp]=JOYButtonUsageDPadUp,
|
||||||
[GBDown] = JOYButtonUsageDPadDown,
|
[GBDown]=JOYButtonUsageDPadDown,
|
||||||
[GBA] = JOYButtonUsageA,
|
[GBA]=JOYButtonUsageA,
|
||||||
[GBB] = JOYButtonUsageB,
|
[GBB]=JOYButtonUsageB,
|
||||||
[GBSelect] = JOYButtonUsageSelect,
|
[GBSelect]=JOYButtonUsageSelect,
|
||||||
[GBStart] = JOYButtonUsageStart,
|
[GBStart]=JOYButtonUsageStart,
|
||||||
[GBTurbo] = JOYButtonUsageL1,
|
[GBTurbo]=JOYButtonUsageL1,
|
||||||
[GBRewind] = JOYButtonUsageL2,
|
[GBRewind]=JOYButtonUsageL2,
|
||||||
[GBUnderclock] = JOYButtonUsageR1,
|
[GBUnderclock]=JOYButtonUsageR1,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (joystick_configuration_state == GBUnderclock) {
|
if (joystick_configuration_state == GBUnderclock) {
|
||||||
mapping[@"AnalogUnderclock"] = nil;
|
|
||||||
double max = 0;
|
|
||||||
for (JOYAxis *axis in controller.axes) {
|
for (JOYAxis *axis in controller.axes) {
|
||||||
if ((axis.value > 0.5 || (axis.equivalentButtonUsage == button.usage)) && axis.value >= max) {
|
if (axis.value > 0.5) {
|
||||||
mapping[@"AnalogUnderclock"] = @(axis.uniqueID);
|
mapping[@"AnalogUnderclock"] = @(axis.uniqueID);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (joystick_configuration_state == GBTurbo) {
|
if (joystick_configuration_state == GBTurbo) {
|
||||||
mapping[@"AnalogTurbo"] = nil;
|
|
||||||
double max = 0;
|
|
||||||
for (JOYAxis *axis in controller.axes) {
|
for (JOYAxis *axis in controller.axes) {
|
||||||
if ((axis.value > 0.5 || (axis.equivalentButtonUsage == button.usage)) && axis.value >= max) {
|
if (axis.value > 0.5) {
|
||||||
max = axis.value;
|
|
||||||
mapping[@"AnalogTurbo"] = @(axis.uniqueID);
|
mapping[@"AnalogTurbo"] = @(axis.uniqueID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -587,28 +404,6 @@
|
|||||||
[self advanceConfigurationStateMachine];
|
[self advanceConfigurationStateMachine];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSButton *)joystickMBC7Checkbox
|
|
||||||
{
|
|
||||||
return _joystickMBC7Checkbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setJoystickMBC7Checkbox:(NSButton *)joystickMBC7Checkbox
|
|
||||||
{
|
|
||||||
_joystickMBC7Checkbox = joystickMBC7Checkbox;
|
|
||||||
[_joystickMBC7Checkbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7JoystickOverride"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSButton *)mouseMBC7Checkbox
|
|
||||||
{
|
|
||||||
return _mouseMBC7Checkbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMouseMBC7Checkbox:(NSButton *)mouseMBC7Checkbox
|
|
||||||
{
|
|
||||||
_mouseMBC7Checkbox = mouseMBC7Checkbox;
|
|
||||||
[_mouseMBC7Checkbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7AllowMouse"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSButton *)analogControlsCheckbox
|
- (NSButton *)analogControlsCheckbox
|
||||||
{
|
{
|
||||||
return _analogControlsCheckbox;
|
return _analogControlsCheckbox;
|
||||||
@ -649,8 +444,8 @@
|
|||||||
- (IBAction)selectOtherBootROMFolder:(id)sender
|
- (IBAction)selectOtherBootROMFolder:(id)sender
|
||||||
{
|
{
|
||||||
NSOpenPanel *panel = [[NSOpenPanel alloc] init];
|
NSOpenPanel *panel = [[NSOpenPanel alloc] init];
|
||||||
[panel setCanChooseDirectories:true];
|
[panel setCanChooseDirectories:YES];
|
||||||
[panel setCanChooseFiles:false];
|
[panel setCanChooseFiles:NO];
|
||||||
[panel setPrompt:@"Select"];
|
[panel setPrompt:@"Select"];
|
||||||
[panel setDirectoryURL:[[NSUserDefaults standardUserDefaults] URLForKey:@"GBBootROMsFolder"]];
|
[panel setDirectoryURL:[[NSUserDefaults standardUserDefaults] URLForKey:@"GBBootROMsFolder"]];
|
||||||
[panel beginSheetModalForWindow:self completionHandler:^(NSModalResponse result) {
|
[panel beginSheetModalForWindow:self completionHandler:^(NSModalResponse result) {
|
||||||
@ -674,12 +469,12 @@
|
|||||||
[self.bootROMsFolderItem setTitle:[url lastPathComponent]];
|
[self.bootROMsFolderItem setTitle:[url lastPathComponent]];
|
||||||
NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:[url path]];
|
NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:[url path]];
|
||||||
[icon setSize:NSMakeSize(16, 16)];
|
[icon setSize:NSMakeSize(16, 16)];
|
||||||
[self.bootROMsFolderItem setHidden:false];
|
[self.bootROMsFolderItem setHidden:NO];
|
||||||
[self.bootROMsFolderItem setImage:icon];
|
[self.bootROMsFolderItem setImage:icon];
|
||||||
[self.bootROMsButton selectItemAtIndex:1];
|
[self.bootROMsButton selectItemAtIndex:1];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
[self.bootROMsFolderItem setHidden:true];
|
[self.bootROMsFolderItem setHidden:YES];
|
||||||
[self.bootROMsButton selectItemAtIndex:0];
|
[self.bootROMsButton selectItemAtIndex:0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -823,56 +618,4 @@
|
|||||||
}
|
}
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:default_joypads forKey:@"JoyKitDefaultControllers"];
|
[[NSUserDefaults standardUserDefaults] setObject:default_joypads forKey:@"JoyKitDefaultControllers"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSButton *)autoUpdatesCheckbox
|
|
||||||
{
|
|
||||||
return _autoUpdatesCheckbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAutoUpdatesCheckbox:(NSButton *)autoUpdatesCheckbox
|
|
||||||
{
|
|
||||||
_autoUpdatesCheckbox = autoUpdatesCheckbox;
|
|
||||||
[_autoUpdatesCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAutoUpdatesEnabled"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSButton *)OSDCheckbox
|
|
||||||
{
|
|
||||||
return _OSDCheckbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setOSDCheckbox:(NSButton *)OSDCheckbox
|
|
||||||
{
|
|
||||||
_OSDCheckbox = OSDCheckbox;
|
|
||||||
[_OSDCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBOSDEnabled"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)changeOSDEnabled:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:[(NSButton *)sender state] == NSOnState
|
|
||||||
forKey:@"GBOSDEnabled"];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)changeFilterScreenshots:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:[(NSButton *)sender state] == NSOnState
|
|
||||||
forKey:@"GBFilterScreenshots"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSButton *)screenshotFilterCheckbox
|
|
||||||
{
|
|
||||||
return _screenshotFilterCheckbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setScreenshotFilterCheckbox:(NSButton *)screenshotFilterCheckbox
|
|
||||||
{
|
|
||||||
_screenshotFilterCheckbox = screenshotFilterCheckbox;
|
|
||||||
if (![GBViewMetal isSupported]) {
|
|
||||||
[_screenshotFilterCheckbox setEnabled:false];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[_screenshotFilterCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBFilterScreenshots"]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
128
Cocoa/GBS.xib
@ -1,128 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="macosx"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<objects>
|
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="Document">
|
|
||||||
<connections>
|
|
||||||
<outlet property="gbsAuthor" destination="gaD-ZH-Beh" id="2i7-BD-bJ2"/>
|
|
||||||
<outlet property="gbsCopyright" destination="2dl-dH-E3J" id="LnT-Vb-pN6"/>
|
|
||||||
<outlet property="gbsNextPrevButton" destination="SRS-M5-VVL" id="YEN-01-wRX"/>
|
|
||||||
<outlet property="gbsPlayPauseButton" destination="qxJ-pH-d0y" id="qk8-8I-9u5"/>
|
|
||||||
<outlet property="gbsPlayerView" destination="c22-O7-iKe" id="A1w-e5-EQE"/>
|
|
||||||
<outlet property="gbsRewindButton" destination="0yD-Sp-Ilo" id="FgR-xd-JW5"/>
|
|
||||||
<outlet property="gbsTitle" destination="H3v-X3-48q" id="DCl-wL-oy8"/>
|
|
||||||
<outlet property="gbsTracks" destination="I1T-VS-Vse" id="Vk4-GP-RjB"/>
|
|
||||||
<outlet property="gbsVisualizer" destination="Q3o-bK-DIN" id="1YC-C5-Je6"/>
|
|
||||||
</connections>
|
|
||||||
</customObject>
|
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
|
||||||
<customView id="c22-O7-iKe">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="332" height="221"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="H3v-X3-48q">
|
|
||||||
<rect key="frame" x="18" y="192" width="296" height="19"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Title" id="BwZ-Zj-sP6">
|
|
||||||
<font key="font" metaFont="systemBold" size="16"/>
|
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="gaD-ZH-Beh">
|
|
||||||
<rect key="frame" x="18" y="166" width="296" height="16"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Author" id="IgT-r1-T38">
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qxJ-pH-d0y">
|
|
||||||
<rect key="frame" x="61.5" y="127" width="39" height="23"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="Play" imagePosition="only" alignment="center" alternateImage="Pause" state="on" borderStyle="border" focusRingType="none" inset="2" id="3ZK-br-UrS">
|
|
||||||
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="togglePause:" target="-2" id="AUe-I7-nOK"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0yD-Sp-Ilo">
|
|
||||||
<rect key="frame" x="19.5" y="127" width="38" height="23"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="Rewind" imagePosition="only" alignment="center" state="on" borderStyle="border" focusRingType="none" inset="2" id="ZIF-TP-Fqn">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="changeGBSTrack:" target="-2" id="jug-AS-bW7"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="I1T-VS-Vse">
|
|
||||||
<rect key="frame" x="106" y="127" width="131" height="23"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="YJh-dI-A5D">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="menu"/>
|
|
||||||
<menu key="menu" id="Knp-Ok-Pb4"/>
|
|
||||||
</popUpButtonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="changeGBSTrack:" target="-2" id="HET-AT-CfQ"/>
|
|
||||||
</connections>
|
|
||||||
</popUpButton>
|
|
||||||
<segmentedControl verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SRS-M5-VVL">
|
|
||||||
<rect key="frame" x="240.5" y="127" width="72" height="23"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<segmentedCell key="cell" borderStyle="border" alignment="left" style="texturedRounded" trackingMode="momentary" id="cmq-I8-cFL">
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<segments>
|
|
||||||
<segment toolTip="Previous Track" image="Previous" width="33"/>
|
|
||||||
<segment toolTip="Next Track" image="Next" width="32" tag="1"/>
|
|
||||||
</segments>
|
|
||||||
</segmentedCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="gbsNextPrevPushed:" target="-2" id="roN-Iy-tDQ"/>
|
|
||||||
</connections>
|
|
||||||
</segmentedControl>
|
|
||||||
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="b9A-cd-ias">
|
|
||||||
<rect key="frame" x="0.0" y="117" width="332" height="5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
</box>
|
|
||||||
<customView appearanceType="darkAqua" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tRy-Gw-IaG" customClass="GBOptionalVisualEffectView">
|
|
||||||
<rect key="frame" x="0.0" y="24" width="332" height="95"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Q3o-bK-DIN" customClass="GBVisualizerView">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="332" height="95"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
</customView>
|
|
||||||
</subviews>
|
|
||||||
</customView>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="2dl-dH-E3J">
|
|
||||||
<rect key="frame" x="18" y="5" width="296" height="14"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
|
|
||||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="center" title="Copyright" id="nM9-oF-OV9">
|
|
||||||
<font key="font" metaFont="smallSystem"/>
|
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
|
||||||
<point key="canvasLocation" x="67" y="292.5"/>
|
|
||||||
</customView>
|
|
||||||
</objects>
|
|
||||||
<resources>
|
|
||||||
<image name="Next" width="16" height="10"/>
|
|
||||||
<image name="Pause" width="10" height="10"/>
|
|
||||||
<image name="Play" width="10" height="10"/>
|
|
||||||
<image name="Previous" width="16" height="10"/>
|
|
||||||
<image name="Rewind" width="10" height="10"/>
|
|
||||||
</resources>
|
|
||||||
</document>
|
|
@ -8,7 +8,7 @@
|
|||||||
- (void)setDividerColor:(NSColor *)color
|
- (void)setDividerColor:(NSColor *)color
|
||||||
{
|
{
|
||||||
_dividerColor = color;
|
_dividerColor = color;
|
||||||
[self setNeedsDisplay:true];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSColor *)dividerColor
|
- (NSColor *)dividerColor
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#include <Core/gb.h>
|
|
||||||
|
|
||||||
@interface GBTerminalTextFieldCell : NSTextFieldCell
|
@interface GBTerminalTextFieldCell : NSTextFieldCell
|
||||||
@property (nonatomic) GB_gameboy_t *gb;
|
|
||||||
@end
|
@end
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#import "GBTerminalTextFieldCell.h"
|
#import "GBTerminalTextFieldCell.h"
|
||||||
|
|
||||||
@interface GBTerminalTextView : NSTextView
|
@interface GBTerminalTextView : NSTextView
|
||||||
@property GB_gameboy_t *gb;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation GBTerminalTextFieldCell
|
@implementation GBTerminalTextFieldCell
|
||||||
@ -13,12 +12,10 @@
|
|||||||
- (NSTextView *)fieldEditorForView:(NSView *)controlView
|
- (NSTextView *)fieldEditorForView:(NSView *)controlView
|
||||||
{
|
{
|
||||||
if (field_editor) {
|
if (field_editor) {
|
||||||
field_editor.gb = self.gb;
|
|
||||||
return field_editor;
|
return field_editor;
|
||||||
}
|
}
|
||||||
field_editor = [[GBTerminalTextView alloc] init];
|
field_editor = [[GBTerminalTextView alloc] init];
|
||||||
[field_editor setFieldEditor:true];
|
[field_editor setFieldEditor:YES];
|
||||||
field_editor.gb = self.gb;
|
|
||||||
return field_editor;
|
return field_editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,8 +26,6 @@
|
|||||||
NSMutableOrderedSet *lines;
|
NSMutableOrderedSet *lines;
|
||||||
NSUInteger current_line;
|
NSUInteger current_line;
|
||||||
bool reverse_search_mode;
|
bool reverse_search_mode;
|
||||||
NSRange auto_complete_range;
|
|
||||||
uintptr_t auto_complete_context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
@ -109,7 +104,7 @@
|
|||||||
[self updateReverseSearch];
|
[self updateReverseSearch];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
[self setNeedsDisplay:true];
|
[self setNeedsDisplay:YES];
|
||||||
reverse_search_mode = true;
|
reverse_search_mode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +170,6 @@
|
|||||||
-(void)setSelectedRanges:(NSArray<NSValue *> *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag
|
-(void)setSelectedRanges:(NSArray<NSValue *> *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag
|
||||||
{
|
{
|
||||||
reverse_search_mode = false;
|
reverse_search_mode = false;
|
||||||
auto_complete_context = 0;
|
|
||||||
[super setSelectedRanges:ranges affinity:affinity stillSelecting:stillSelectingFlag];
|
[super setSelectedRanges:ranges affinity:affinity stillSelecting:stillSelectingFlag];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,38 +188,6 @@
|
|||||||
[attributes setObject:color forKey:NSForegroundColorAttributeName];
|
[attributes setObject:color forKey:NSForegroundColorAttributeName];
|
||||||
[[[NSAttributedString alloc] initWithString:@"Reverse search..." attributes:attributes] drawAtPoint:NSMakePoint(2, 0)];
|
[[[NSAttributedString alloc] initWithString:@"Reverse search..." attributes:attributes] drawAtPoint:NSMakePoint(2, 0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Todo: lazy design, make it use a delegate instead of having a gb reference*/
|
|
||||||
|
|
||||||
- (void)insertTab:(id)sender
|
|
||||||
{
|
|
||||||
if (auto_complete_context == 0) {
|
|
||||||
NSRange selection = self.selectedRange;
|
|
||||||
if (selection.length) {
|
|
||||||
[self delete:nil];
|
|
||||||
}
|
|
||||||
auto_complete_range = NSMakeRange(selection.location, 0);
|
|
||||||
}
|
|
||||||
char *substring = strdup([self.string substringToIndex:auto_complete_range.location].UTF8String);
|
|
||||||
uintptr_t context = auto_complete_context;
|
|
||||||
char *completion = GB_debugger_complete_substring(self.gb, substring, &context);
|
|
||||||
free(substring);
|
|
||||||
if (completion) {
|
|
||||||
NSString *ns_completion = @(completion);
|
|
||||||
free(completion);
|
|
||||||
if (!ns_completion) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
self.selectedRange = auto_complete_range;
|
|
||||||
auto_complete_range.length = ns_completion.length;
|
|
||||||
[self replaceCharactersInRange:self.selectedRange withString:ns_completion];
|
|
||||||
auto_complete_context = context;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
error:
|
|
||||||
auto_complete_context = context;
|
|
||||||
NSBeep();
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#include <Core/gb.h>
|
#include <Core/gb.h>
|
||||||
#import <JoyKit/JoyKit.h>
|
#import <JoyKit/JoyKit.h>
|
||||||
#import "GBOSDView.h"
|
|
||||||
@class Document;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GB_FRAME_BLENDING_MODE_DISABLED,
|
GB_FRAME_BLENDING_MODE_DISABLED,
|
||||||
@ -15,17 +13,14 @@ typedef enum {
|
|||||||
@interface GBView : NSView<JOYListener>
|
@interface GBView : NSView<JOYListener>
|
||||||
- (void) flip;
|
- (void) flip;
|
||||||
- (uint32_t *) pixels;
|
- (uint32_t *) pixels;
|
||||||
@property (nonatomic, weak) IBOutlet Document *document;
|
@property GB_gameboy_t *gb;
|
||||||
@property (nonatomic) GB_gameboy_t *gb;
|
|
||||||
@property (nonatomic) GB_frame_blending_mode_t frameBlendingMode;
|
@property (nonatomic) GB_frame_blending_mode_t frameBlendingMode;
|
||||||
@property (nonatomic, getter=isMouseHidingEnabled) bool mouseHidingEnabled;
|
@property (getter=isMouseHidingEnabled) BOOL mouseHidingEnabled;
|
||||||
@property (nonatomic) bool isRewinding;
|
@property bool isRewinding;
|
||||||
@property (nonatomic, strong) NSView *internalView;
|
@property NSView *internalView;
|
||||||
@property (weak) GBOSDView *osdView;
|
|
||||||
- (void) createInternalView;
|
- (void) createInternalView;
|
||||||
- (uint32_t *)currentBuffer;
|
- (uint32_t *)currentBuffer;
|
||||||
- (uint32_t *)previousBuffer;
|
- (uint32_t *)previousBuffer;
|
||||||
- (void)screenSizeChanged;
|
- (void)screenSizeChanged;
|
||||||
- (void)setRumble: (double)amp;
|
- (void)setRumble: (double)amp;
|
||||||
- (NSImage *)renderToImage;
|
|
||||||
@end
|
@end
|
||||||
|
414
Cocoa/GBView.m
@ -1,114 +1,20 @@
|
|||||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||||
#import <Carbon/Carbon.h>
|
|
||||||
#import "GBView.h"
|
#import "GBView.h"
|
||||||
#import "GBViewGL.h"
|
#import "GBViewGL.h"
|
||||||
#import "GBViewMetal.h"
|
#import "GBViewMetal.h"
|
||||||
#import "GBButtons.h"
|
#import "GBButtons.h"
|
||||||
#import "NSString+StringForKey.h"
|
#import "NSString+StringForKey.h"
|
||||||
#import "Document.h"
|
|
||||||
|
|
||||||
#define JOYSTICK_HIGH 0x4000
|
#define JOYSTICK_HIGH 0x4000
|
||||||
#define JOYSTICK_LOW 0x3800
|
#define JOYSTICK_LOW 0x3800
|
||||||
|
|
||||||
static const uint8_t workboy_ascii_to_key[] = {
|
|
||||||
['0'] = GB_WORKBOY_0,
|
|
||||||
['`'] = GB_WORKBOY_UMLAUT,
|
|
||||||
['1'] = GB_WORKBOY_1,
|
|
||||||
['2'] = GB_WORKBOY_2,
|
|
||||||
['3'] = GB_WORKBOY_3,
|
|
||||||
['4'] = GB_WORKBOY_4,
|
|
||||||
['5'] = GB_WORKBOY_5,
|
|
||||||
['6'] = GB_WORKBOY_6,
|
|
||||||
['7'] = GB_WORKBOY_7,
|
|
||||||
['8'] = GB_WORKBOY_8,
|
|
||||||
['9'] = GB_WORKBOY_9,
|
|
||||||
|
|
||||||
['\r'] = GB_WORKBOY_ENTER,
|
|
||||||
[3] = GB_WORKBOY_ENTER,
|
|
||||||
|
|
||||||
['!'] = GB_WORKBOY_EXCLAMATION_MARK,
|
|
||||||
['$'] = GB_WORKBOY_DOLLAR,
|
|
||||||
['#'] = GB_WORKBOY_HASH,
|
|
||||||
['~'] = GB_WORKBOY_TILDE,
|
|
||||||
['*'] = GB_WORKBOY_ASTERISK,
|
|
||||||
['+'] = GB_WORKBOY_PLUS,
|
|
||||||
['-'] = GB_WORKBOY_MINUS,
|
|
||||||
['('] = GB_WORKBOY_LEFT_PARENTHESIS,
|
|
||||||
[')'] = GB_WORKBOY_RIGHT_PARENTHESIS,
|
|
||||||
[';'] = GB_WORKBOY_SEMICOLON,
|
|
||||||
[':'] = GB_WORKBOY_COLON,
|
|
||||||
['%'] = GB_WORKBOY_PERCENT,
|
|
||||||
['='] = GB_WORKBOY_EQUAL,
|
|
||||||
[','] = GB_WORKBOY_COMMA,
|
|
||||||
['<'] = GB_WORKBOY_LT,
|
|
||||||
['.'] = GB_WORKBOY_DOT,
|
|
||||||
['>'] = GB_WORKBOY_GT,
|
|
||||||
['/'] = GB_WORKBOY_SLASH,
|
|
||||||
['?'] = GB_WORKBOY_QUESTION_MARK,
|
|
||||||
[' '] = GB_WORKBOY_SPACE,
|
|
||||||
['\''] = GB_WORKBOY_QUOTE,
|
|
||||||
['@'] = GB_WORKBOY_AT,
|
|
||||||
|
|
||||||
['q'] = GB_WORKBOY_Q,
|
|
||||||
['w'] = GB_WORKBOY_W,
|
|
||||||
['e'] = GB_WORKBOY_E,
|
|
||||||
['r'] = GB_WORKBOY_R,
|
|
||||||
['t'] = GB_WORKBOY_T,
|
|
||||||
['y'] = GB_WORKBOY_Y,
|
|
||||||
['u'] = GB_WORKBOY_U,
|
|
||||||
['i'] = GB_WORKBOY_I,
|
|
||||||
['o'] = GB_WORKBOY_O,
|
|
||||||
['p'] = GB_WORKBOY_P,
|
|
||||||
['a'] = GB_WORKBOY_A,
|
|
||||||
['s'] = GB_WORKBOY_S,
|
|
||||||
['d'] = GB_WORKBOY_D,
|
|
||||||
['f'] = GB_WORKBOY_F,
|
|
||||||
['g'] = GB_WORKBOY_G,
|
|
||||||
['h'] = GB_WORKBOY_H,
|
|
||||||
['j'] = GB_WORKBOY_J,
|
|
||||||
['k'] = GB_WORKBOY_K,
|
|
||||||
['l'] = GB_WORKBOY_L,
|
|
||||||
['z'] = GB_WORKBOY_Z,
|
|
||||||
['x'] = GB_WORKBOY_X,
|
|
||||||
['c'] = GB_WORKBOY_C,
|
|
||||||
['v'] = GB_WORKBOY_V,
|
|
||||||
['b'] = GB_WORKBOY_B,
|
|
||||||
['n'] = GB_WORKBOY_N,
|
|
||||||
['m'] = GB_WORKBOY_M,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint8_t workboy_vk_to_key[] = {
|
|
||||||
[kVK_F1] = GB_WORKBOY_CLOCK,
|
|
||||||
[kVK_F2] = GB_WORKBOY_TEMPERATURE,
|
|
||||||
[kVK_F3] = GB_WORKBOY_MONEY,
|
|
||||||
[kVK_F4] = GB_WORKBOY_CALCULATOR,
|
|
||||||
[kVK_F5] = GB_WORKBOY_DATE,
|
|
||||||
[kVK_F6] = GB_WORKBOY_CONVERSION,
|
|
||||||
[kVK_F7] = GB_WORKBOY_RECORD,
|
|
||||||
[kVK_F8] = GB_WORKBOY_WORLD,
|
|
||||||
[kVK_F9] = GB_WORKBOY_PHONE,
|
|
||||||
[kVK_F10] = GB_WORKBOY_UNKNOWN,
|
|
||||||
[kVK_Delete] = GB_WORKBOY_BACKSPACE,
|
|
||||||
[kVK_Shift] = GB_WORKBOY_SHIFT_DOWN,
|
|
||||||
[kVK_RightShift] = GB_WORKBOY_SHIFT_DOWN,
|
|
||||||
[kVK_UpArrow] = GB_WORKBOY_UP,
|
|
||||||
[kVK_DownArrow] = GB_WORKBOY_DOWN,
|
|
||||||
[kVK_LeftArrow] = GB_WORKBOY_LEFT,
|
|
||||||
[kVK_RightArrow] = GB_WORKBOY_RIGHT,
|
|
||||||
[kVK_Escape] = GB_WORKBOY_ESCAPE,
|
|
||||||
[kVK_ANSI_KeypadDecimal] = GB_WORKBOY_DECIMAL_POINT,
|
|
||||||
[kVK_ANSI_KeypadClear] = GB_WORKBOY_M,
|
|
||||||
[kVK_ANSI_KeypadMultiply] = GB_WORKBOY_H,
|
|
||||||
[kVK_ANSI_KeypadDivide] = GB_WORKBOY_J,
|
|
||||||
};
|
|
||||||
|
|
||||||
@implementation GBView
|
@implementation GBView
|
||||||
{
|
{
|
||||||
uint32_t *image_buffers[3];
|
uint32_t *image_buffers[3];
|
||||||
unsigned char current_buffer;
|
unsigned char current_buffer;
|
||||||
bool mouse_hidden;
|
BOOL mouse_hidden;
|
||||||
NSTrackingArea *tracking_area;
|
NSTrackingArea *tracking_area;
|
||||||
bool _mouseHidingEnabled;
|
BOOL _mouseHidingEnabled;
|
||||||
bool axisActive[2];
|
bool axisActive[2];
|
||||||
bool underclockKeyDown;
|
bool underclockKeyDown;
|
||||||
double clockMultiplier;
|
double clockMultiplier;
|
||||||
@ -117,8 +23,6 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
NSEventModifierFlags previousModifiers;
|
NSEventModifierFlags previousModifiers;
|
||||||
JOYController *lastController;
|
JOYController *lastController;
|
||||||
GB_frame_blending_mode_t _frameBlendingMode;
|
GB_frame_blending_mode_t _frameBlendingMode;
|
||||||
bool _turbo;
|
|
||||||
bool _mouseControlEnabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)alloc
|
+ (instancetype)alloc
|
||||||
@ -144,11 +48,9 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
|
|
||||||
- (void) _init
|
- (void) _init
|
||||||
{
|
{
|
||||||
[self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ratioKeepingChanged) name:@"GBAspectChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ratioKeepingChanged) name:@"GBAspectChanged" object:nil];
|
||||||
tracking_area = [ [NSTrackingArea alloc] initWithRect:(NSRect){}
|
tracking_area = [ [NSTrackingArea alloc] initWithRect:(NSRect){}
|
||||||
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseMoved
|
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect
|
||||||
owner:self
|
owner:self
|
||||||
userInfo:nil];
|
userInfo:nil];
|
||||||
[self addTrackingArea:tracking_area];
|
[self addTrackingArea:tracking_area];
|
||||||
@ -157,7 +59,6 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
[self addSubview:self.internalView];
|
[self addSubview:self.internalView];
|
||||||
self.internalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
self.internalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||||
[JOYController registerListener:self];
|
[JOYController registerListener:self];
|
||||||
_mouseControlEnabled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)screenSizeChanged
|
- (void)screenSizeChanged
|
||||||
@ -185,7 +86,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
- (void) setFrameBlendingMode:(GB_frame_blending_mode_t)frameBlendingMode
|
- (void) setFrameBlendingMode:(GB_frame_blending_mode_t)frameBlendingMode
|
||||||
{
|
{
|
||||||
_frameBlendingMode = frameBlendingMode;
|
_frameBlendingMode = frameBlendingMode;
|
||||||
[self setNeedsDisplay:true];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -214,7 +115,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
[NSCursor unhide];
|
[NSCursor unhide];
|
||||||
}
|
}
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
[self setRumble:0];
|
[lastController setRumbleAmplitude:0];
|
||||||
[JOYController unregisterListener:self];
|
[JOYController unregisterListener:self];
|
||||||
}
|
}
|
||||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||||
@ -262,45 +163,21 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
- (void) flip
|
- (void) flip
|
||||||
{
|
{
|
||||||
if (analogClockMultiplierValid && [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAnalogControls"]) {
|
if (analogClockMultiplierValid && [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAnalogControls"]) {
|
||||||
clockMultiplier = 1.0;
|
|
||||||
GB_set_clock_multiplier(_gb, analogClockMultiplier);
|
GB_set_clock_multiplier(_gb, analogClockMultiplier);
|
||||||
if (self.document.partner) {
|
|
||||||
GB_set_clock_multiplier(self.document.partner.gb, analogClockMultiplier);
|
|
||||||
}
|
|
||||||
if (analogClockMultiplier == 1.0) {
|
if (analogClockMultiplier == 1.0) {
|
||||||
analogClockMultiplierValid = false;
|
analogClockMultiplierValid = false;
|
||||||
}
|
}
|
||||||
if (analogClockMultiplier < 2.0 && analogClockMultiplier > 1.0) {
|
|
||||||
GB_set_turbo_mode(_gb, false, false);
|
|
||||||
if (self.document.partner) {
|
|
||||||
GB_set_turbo_mode(self.document.partner.gb, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (underclockKeyDown && clockMultiplier > 0.5) {
|
if (underclockKeyDown && clockMultiplier > 0.5) {
|
||||||
clockMultiplier -= 1.0/16;
|
clockMultiplier -= 1.0/16;
|
||||||
GB_set_clock_multiplier(_gb, clockMultiplier);
|
GB_set_clock_multiplier(_gb, clockMultiplier);
|
||||||
if (self.document.partner) {
|
|
||||||
GB_set_clock_multiplier(self.document.partner.gb, clockMultiplier);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!underclockKeyDown && clockMultiplier < 1.0) {
|
if (!underclockKeyDown && clockMultiplier < 1.0) {
|
||||||
clockMultiplier += 1.0/16;
|
clockMultiplier += 1.0/16;
|
||||||
GB_set_clock_multiplier(_gb, clockMultiplier);
|
GB_set_clock_multiplier(_gb, clockMultiplier);
|
||||||
if (self.document.partner) {
|
|
||||||
GB_set_clock_multiplier(self.document.partner.gb, clockMultiplier);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((!analogClockMultiplierValid && clockMultiplier > 1) ||
|
|
||||||
_turbo || (analogClockMultiplierValid && analogClockMultiplier > 1)) {
|
|
||||||
[self.osdView displayText:@"Fast forwarding..."];
|
|
||||||
}
|
|
||||||
else if ((!analogClockMultiplierValid && clockMultiplier < 1) ||
|
|
||||||
(analogClockMultiplierValid && analogClockMultiplier < 1)) {
|
|
||||||
[self.osdView displayText:@"Slow motion..."];
|
|
||||||
}
|
|
||||||
current_buffer = (current_buffer + 1) % self.numberOfBuffers;
|
current_buffer = (current_buffer + 1) % self.numberOfBuffers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,27 +188,11 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
|
|
||||||
-(void)keyDown:(NSEvent *)theEvent
|
-(void)keyDown:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
if ([theEvent type] != NSEventTypeFlagsChanged && theEvent.isARepeat) return;
|
|
||||||
unsigned short keyCode = theEvent.keyCode;
|
unsigned short keyCode = theEvent.keyCode;
|
||||||
if (GB_workboy_is_enabled(_gb)) {
|
|
||||||
if (theEvent.keyCode < sizeof(workboy_vk_to_key) && workboy_vk_to_key[theEvent.keyCode]) {
|
|
||||||
GB_workboy_set_key(_gb, workboy_vk_to_key[theEvent.keyCode]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unichar c = [theEvent type] != NSEventTypeFlagsChanged? [theEvent.charactersIgnoringModifiers.lowercaseString characterAtIndex:0] : 0;
|
|
||||||
if (c < sizeof(workboy_ascii_to_key) && workboy_ascii_to_key[c]) {
|
|
||||||
GB_workboy_set_key(_gb, workboy_ascii_to_key[c]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||||
unsigned player_count = GB_get_player_count(_gb);
|
unsigned player_count = GB_get_player_count(_gb);
|
||||||
if (self.document.partner) {
|
|
||||||
player_count = 2;
|
|
||||||
}
|
|
||||||
for (unsigned player = 0; player < player_count; player++) {
|
for (unsigned player = 0; player < player_count; player++) {
|
||||||
for (GBButton button = 0; button < GBButtonCount; button++) {
|
for (GBButton button = 0; button < GBButtonCount; button++) {
|
||||||
NSNumber *key = [defaults valueForKey:button_to_preference_name(button, player)];
|
NSNumber *key = [defaults valueForKey:button_to_preference_name(button, player)];
|
||||||
@ -341,22 +202,13 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
handled = true;
|
handled = true;
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case GBTurbo:
|
case GBTurbo:
|
||||||
if (self.document.isSlave) {
|
GB_set_turbo_mode(_gb, true, self.isRewinding);
|
||||||
GB_set_turbo_mode(self.document.partner.gb, true, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_turbo_mode(_gb, true, self.isRewinding);
|
|
||||||
}
|
|
||||||
_turbo = true;
|
|
||||||
analogClockMultiplierValid = false;
|
analogClockMultiplierValid = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GBRewind:
|
case GBRewind:
|
||||||
if (!self.document.partner) {
|
self.isRewinding = true;
|
||||||
self.isRewinding = true;
|
GB_set_turbo_mode(_gb, false, false);
|
||||||
GB_set_turbo_mode(_gb, false, false);
|
|
||||||
_turbo = false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GBUnderclock:
|
case GBUnderclock:
|
||||||
@ -365,17 +217,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (self.document.partner) {
|
GB_set_key_state_for_player(_gb, (GB_key_t)button, player, true);
|
||||||
if (player == 0) {
|
|
||||||
GB_set_key_state_for_player(_gb, (GB_key_t)button, 0, true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_key_state_for_player(self.document.partner.gb, (GB_key_t)button, 0, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_key_state_for_player(_gb, (GB_key_t)button, player, true);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -390,22 +232,10 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
-(void)keyUp:(NSEvent *)theEvent
|
-(void)keyUp:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
unsigned short keyCode = theEvent.keyCode;
|
unsigned short keyCode = theEvent.keyCode;
|
||||||
if (GB_workboy_is_enabled(_gb)) {
|
|
||||||
if (keyCode == kVK_Shift || keyCode == kVK_RightShift) {
|
|
||||||
GB_workboy_set_key(_gb, GB_WORKBOY_SHIFT_UP);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_workboy_set_key(_gb, GB_WORKBOY_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||||
unsigned player_count = GB_get_player_count(_gb);
|
unsigned player_count = GB_get_player_count(_gb);
|
||||||
if (self.document.partner) {
|
|
||||||
player_count = 2;
|
|
||||||
}
|
|
||||||
for (unsigned player = 0; player < player_count; player++) {
|
for (unsigned player = 0; player < player_count; player++) {
|
||||||
for (GBButton button = 0; button < GBButtonCount; button++) {
|
for (GBButton button = 0; button < GBButtonCount; button++) {
|
||||||
NSNumber *key = [defaults valueForKey:button_to_preference_name(button, player)];
|
NSNumber *key = [defaults valueForKey:button_to_preference_name(button, player)];
|
||||||
@ -415,13 +245,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
handled = true;
|
handled = true;
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case GBTurbo:
|
case GBTurbo:
|
||||||
if (self.document.isSlave) {
|
GB_set_turbo_mode(_gb, false, false);
|
||||||
GB_set_turbo_mode(self.document.partner.gb, false, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_turbo_mode(_gb, false, false);
|
|
||||||
}
|
|
||||||
_turbo = false;
|
|
||||||
analogClockMultiplierValid = false;
|
analogClockMultiplierValid = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -435,17 +259,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (self.document.partner) {
|
GB_set_key_state_for_player(_gb, (GB_key_t)button, player, false);
|
||||||
if (player == 0) {
|
|
||||||
GB_set_key_state_for_player(_gb, (GB_key_t)button, 0, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_key_state_for_player(self.document.partner.gb, (GB_key_t)button, 0, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_key_state_for_player(_gb, (GB_key_t)button, player, false);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -461,22 +275,8 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
[lastController setRumbleAmplitude:amp];
|
[lastController setRumbleAmplitude:amp];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (bool)shouldControllerUseJoystickForMotion:(JOYController *)controller
|
|
||||||
{
|
|
||||||
if (!_gb) return false;
|
|
||||||
if (!GB_has_accelerometer(_gb)) return false;
|
|
||||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7JoystickOverride"]) return true;
|
|
||||||
for (JOYAxes3D *axes in controller.axes3D) {
|
|
||||||
if (axes.usage == JOYAxes3DUsageOrientation || axes.usage == JOYAxes3DUsageAcceleration) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)controller:(JOYController *)controller movedAxis:(JOYAxis *)axis
|
- (void)controller:(JOYController *)controller movedAxis:(JOYAxis *)axis
|
||||||
{
|
{
|
||||||
if (!_gb) return;
|
|
||||||
if (![self.window isMainWindow]) return;
|
if (![self.window isMainWindow]) return;
|
||||||
|
|
||||||
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
|
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
|
||||||
@ -486,60 +286,27 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
|
|
||||||
if ((axis.usage == JOYAxisUsageR1 && !mapping) ||
|
if ((axis.usage == JOYAxisUsageR1 && !mapping) ||
|
||||||
axis.uniqueID == [mapping[@"AnalogUnderclock"] unsignedLongValue]){
|
axis.uniqueID == [mapping[@"AnalogUnderclock"] unsignedLongValue]){
|
||||||
analogClockMultiplier = MIN(MAX(1 - axis.value + 0.05, 1.0 / 3), 1.0);
|
analogClockMultiplier = MIN(MAX(1 - axis.value + 0.2, 1.0 / 3), 1.0);
|
||||||
analogClockMultiplierValid = true;
|
analogClockMultiplierValid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if ((axis.usage == JOYAxisUsageL1 && !mapping) ||
|
else if ((axis.usage == JOYAxisUsageL1 && !mapping) ||
|
||||||
axis.uniqueID == [mapping[@"AnalogTurbo"] unsignedLongValue]){
|
axis.uniqueID == [mapping[@"AnalogTurbo"] unsignedLongValue]){
|
||||||
analogClockMultiplier = MIN(MAX(axis.value * 3 + 0.95, 1.0), 3.0);
|
analogClockMultiplier = MIN(MAX(axis.value * 3 + 0.8, 1.0), 3.0);
|
||||||
analogClockMultiplierValid = true;
|
analogClockMultiplierValid = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)controller:(JOYController *)controller movedAxes2D:(JOYAxes2D *)axes
|
|
||||||
{
|
|
||||||
if (!_gb) return;
|
|
||||||
if ([self shouldControllerUseJoystickForMotion:controller]) {
|
|
||||||
if (!self.mouseControlsActive) {
|
|
||||||
GB_set_accelerometer_values(_gb, -axes.value.x, -axes.value.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)controller:(JOYController *)controller movedAxes3D:(JOYAxes3D *)axes
|
|
||||||
{
|
|
||||||
if (!_gb) return;
|
|
||||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7JoystickOverride"]) return;
|
|
||||||
if (self.mouseControlsActive) return;
|
|
||||||
|
|
||||||
if (axes.usage == JOYAxes3DUsageOrientation) {
|
|
||||||
for (JOYAxes3D *axes in controller.axes3D) {
|
|
||||||
// Only use orientation if there's no acceleration axes
|
|
||||||
if (axes.usage == JOYAxes3DUsageAcceleration) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JOYPoint3D point = axes.normalizedValue;
|
|
||||||
GB_set_accelerometer_values(_gb, point.x, point.z);
|
|
||||||
}
|
|
||||||
else if (axes.usage == JOYAxes3DUsageAcceleration) {
|
|
||||||
JOYPoint3D point = axes.gUnitsValue;
|
|
||||||
GB_set_accelerometer_values(_gb, point.x, point.z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)button
|
- (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)button
|
||||||
{
|
{
|
||||||
if (!_gb) return;
|
|
||||||
if (![self.window isMainWindow]) return;
|
if (![self.window isMainWindow]) return;
|
||||||
_mouseControlEnabled = false;
|
if (controller != lastController) {
|
||||||
if (button.type == JOYButtonTypeAxes2DEmulated && [self shouldControllerUseJoystickForMotion:controller]) return;
|
[lastController setRumbleAmplitude:0];
|
||||||
|
lastController = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned player_count = GB_get_player_count(_gb);
|
unsigned player_count = GB_get_player_count(_gb);
|
||||||
if (self.document.partner) {
|
|
||||||
player_count = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
IOPMAssertionID assertionID;
|
IOPMAssertionID assertionID;
|
||||||
IOPMAssertionDeclareUserActivity(CFSTR(""), kIOPMUserActiveLocal, &assertionID);
|
IOPMAssertionDeclareUserActivity(CFSTR(""), kIOPMUserActiveLocal, &assertionID);
|
||||||
@ -552,9 +319,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
![preferred_joypad isEqualToString:controller.uniqueID]) {
|
![preferred_joypad isEqualToString:controller.uniqueID]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
[controller setPlayerLEDs:1 << player];
|
||||||
[controller setPlayerLEDs:[controller LEDMaskForPlayer:player]];
|
|
||||||
});
|
|
||||||
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
|
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
|
||||||
if (!mapping) {
|
if (!mapping) {
|
||||||
mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitNameMapping"][controller.deviceName];
|
mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitNameMapping"][controller.deviceName];
|
||||||
@ -565,68 +330,33 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
usage = (const JOYButtonUsage[]){JOYButtonUsageY, JOYButtonUsageA, JOYButtonUsageB, JOYButtonUsageX}[(usage - JOYButtonUsageGeneric0) & 3];
|
usage = (const JOYButtonUsage[]){JOYButtonUsageY, JOYButtonUsageA, JOYButtonUsageB, JOYButtonUsageX}[(usage - JOYButtonUsageGeneric0) & 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_gameboy_t *effectiveGB = _gb;
|
|
||||||
unsigned effectivePlayer = player;
|
|
||||||
|
|
||||||
if (player && self.document.partner) {
|
|
||||||
effectiveGB = self.document.partner.gb;
|
|
||||||
effectivePlayer = 0;
|
|
||||||
if (controller != self.document.partner.view->lastController) {
|
|
||||||
[self setRumble:0];
|
|
||||||
self.document.partner.view->lastController = controller;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (controller != lastController) {
|
|
||||||
[self setRumble:0];
|
|
||||||
lastController = controller;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (usage) {
|
switch (usage) {
|
||||||
|
|
||||||
case JOYButtonUsageNone: break;
|
case JOYButtonUsageNone: break;
|
||||||
case JOYButtonUsageA: GB_set_key_state_for_player(effectiveGB, GB_KEY_A, effectivePlayer, button.isPressed); break;
|
case JOYButtonUsageA: GB_set_key_state_for_player(_gb, GB_KEY_A, player, button.isPressed); break;
|
||||||
case JOYButtonUsageB: GB_set_key_state_for_player(effectiveGB, GB_KEY_B, effectivePlayer, button.isPressed); break;
|
case JOYButtonUsageB: GB_set_key_state_for_player(_gb, GB_KEY_B, player, button.isPressed); break;
|
||||||
case JOYButtonUsageC: break;
|
case JOYButtonUsageC: break;
|
||||||
case JOYButtonUsageStart:
|
case JOYButtonUsageStart:
|
||||||
case JOYButtonUsageX: GB_set_key_state_for_player(effectiveGB, GB_KEY_START, effectivePlayer, button.isPressed); break;
|
case JOYButtonUsageX: GB_set_key_state_for_player(_gb, GB_KEY_START, player, button.isPressed); break;
|
||||||
case JOYButtonUsageSelect:
|
case JOYButtonUsageSelect:
|
||||||
case JOYButtonUsageY: GB_set_key_state_for_player(effectiveGB, GB_KEY_SELECT, effectivePlayer, button.isPressed); break;
|
case JOYButtonUsageY: GB_set_key_state_for_player(_gb, GB_KEY_SELECT, player, button.isPressed); break;
|
||||||
case JOYButtonUsageR2:
|
case JOYButtonUsageR2:
|
||||||
case JOYButtonUsageL2:
|
case JOYButtonUsageL2:
|
||||||
case JOYButtonUsageZ: {
|
case JOYButtonUsageZ: {
|
||||||
self.isRewinding = button.isPressed;
|
self.isRewinding = button.isPressed;
|
||||||
if (button.isPressed) {
|
if (button.isPressed) {
|
||||||
if (self.document.isSlave) {
|
GB_set_turbo_mode(_gb, false, false);
|
||||||
GB_set_turbo_mode(self.document.partner.gb, false, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_turbo_mode(_gb, false, false);
|
|
||||||
}
|
|
||||||
_turbo = false;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JOYButtonUsageL1: {
|
case JOYButtonUsageL1: GB_set_turbo_mode(_gb, button.isPressed, button.isPressed && self.isRewinding); break;
|
||||||
if (!analogClockMultiplierValid || analogClockMultiplier == 1.0 || !button.isPressed) {
|
|
||||||
if (self.document.isSlave) {
|
|
||||||
GB_set_turbo_mode(self.document.partner.gb, button.isPressed, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_turbo_mode(_gb, button.isPressed, button.isPressed && self.isRewinding);
|
|
||||||
}
|
|
||||||
_turbo = button.isPressed;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case JOYButtonUsageR1: underclockKeyDown = button.isPressed; break;
|
case JOYButtonUsageR1: underclockKeyDown = button.isPressed; break;
|
||||||
case JOYButtonUsageDPadLeft: GB_set_key_state_for_player(effectiveGB, GB_KEY_LEFT, effectivePlayer, button.isPressed); break;
|
case JOYButtonUsageDPadLeft: GB_set_key_state_for_player(_gb, GB_KEY_LEFT, player, button.isPressed); break;
|
||||||
case JOYButtonUsageDPadRight: GB_set_key_state_for_player(effectiveGB, GB_KEY_RIGHT, effectivePlayer, button.isPressed); break;
|
case JOYButtonUsageDPadRight: GB_set_key_state_for_player(_gb, GB_KEY_RIGHT, player, button.isPressed); break;
|
||||||
case JOYButtonUsageDPadUp: GB_set_key_state_for_player(effectiveGB, GB_KEY_UP, effectivePlayer, button.isPressed); break;
|
case JOYButtonUsageDPadUp: GB_set_key_state_for_player(_gb, GB_KEY_UP, player, button.isPressed); break;
|
||||||
case JOYButtonUsageDPadDown: GB_set_key_state_for_player(effectiveGB, GB_KEY_DOWN, effectivePlayer, button.isPressed); break;
|
case JOYButtonUsageDPadDown: GB_set_key_state_for_player(_gb, GB_KEY_DOWN, player, button.isPressed); break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -636,21 +366,14 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
|
|
||||||
- (BOOL)acceptsFirstResponder
|
- (BOOL)acceptsFirstResponder
|
||||||
{
|
{
|
||||||
return true;
|
return YES;
|
||||||
}
|
|
||||||
|
|
||||||
- (bool)mouseControlsActive
|
|
||||||
{
|
|
||||||
return _gb && GB_is_inited(_gb) && GB_has_accelerometer(_gb) &&
|
|
||||||
_mouseControlEnabled && [[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7AllowMouse"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseEntered:(NSEvent *)theEvent
|
- (void)mouseEntered:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
if (!mouse_hidden) {
|
if (!mouse_hidden) {
|
||||||
mouse_hidden = true;
|
mouse_hidden = true;
|
||||||
if (_mouseHidingEnabled &&
|
if (_mouseHidingEnabled) {
|
||||||
!self.mouseControlsActive) {
|
|
||||||
[NSCursor hide];
|
[NSCursor hide];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -668,47 +391,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
[super mouseExited:theEvent];
|
[super mouseExited:theEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDown:(NSEvent *)event
|
- (void)setMouseHidingEnabled:(BOOL)mouseHidingEnabled
|
||||||
{
|
|
||||||
_mouseControlEnabled = true;
|
|
||||||
if (self.mouseControlsActive) {
|
|
||||||
if (event.type == NSEventTypeLeftMouseDown) {
|
|
||||||
GB_set_key_state(_gb, GB_KEY_A, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseUp:(NSEvent *)event
|
|
||||||
{
|
|
||||||
if (self.mouseControlsActive) {
|
|
||||||
if (event.type == NSEventTypeLeftMouseUp) {
|
|
||||||
GB_set_key_state(_gb, GB_KEY_A, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseMoved:(NSEvent *)event
|
|
||||||
{
|
|
||||||
if (self.mouseControlsActive) {
|
|
||||||
NSPoint point = [self convertPoint:[event locationInWindow] toView:nil];
|
|
||||||
|
|
||||||
point.x /= self.frame.size.width;
|
|
||||||
point.x *= 2;
|
|
||||||
point.x -= 1;
|
|
||||||
|
|
||||||
point.y /= self.frame.size.height;
|
|
||||||
point.y *= 2;
|
|
||||||
point.y -= 1;
|
|
||||||
|
|
||||||
if (GB_get_screen_width(_gb) != 160) { // has border
|
|
||||||
point.x *= 256 / 160.0;
|
|
||||||
point.y *= 224 / 114.0;
|
|
||||||
}
|
|
||||||
GB_set_accelerometer_values(_gb, -point.x, point.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMouseHidingEnabled:(bool)mouseHidingEnabled
|
|
||||||
{
|
{
|
||||||
if (mouseHidingEnabled == _mouseHidingEnabled) return;
|
if (mouseHidingEnabled == _mouseHidingEnabled) return;
|
||||||
|
|
||||||
@ -723,7 +406,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (bool)isMouseHidingEnabled
|
- (BOOL)isMouseHidingEnabled
|
||||||
{
|
{
|
||||||
return _mouseHidingEnabled;
|
return _mouseHidingEnabled;
|
||||||
}
|
}
|
||||||
@ -750,35 +433,4 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
return image_buffers[(current_buffer + 2) % self.numberOfBuffers];
|
return image_buffers[(current_buffer + 2) % self.numberOfBuffers];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
|
|
||||||
{
|
|
||||||
NSPasteboard *pboard = [sender draggingPasteboard];
|
|
||||||
|
|
||||||
if ( [[pboard types] containsObject:NSURLPboardType] ) {
|
|
||||||
NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
|
|
||||||
if (GB_is_save_state(fileURL.fileSystemRepresentation)) {
|
|
||||||
return NSDragOperationGeneric;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NSDragOperationNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(BOOL)performDragOperation:(id<NSDraggingInfo>)sender
|
|
||||||
{
|
|
||||||
NSPasteboard *pboard = [sender draggingPasteboard];
|
|
||||||
|
|
||||||
if ( [[pboard types] containsObject:NSURLPboardType] ) {
|
|
||||||
NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
|
|
||||||
return [_document loadStateFile:fileURL.fileSystemRepresentation noErrorOnNotFound:false];
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSImage *)renderToImage;
|
|
||||||
{
|
|
||||||
/* Not going to support this on OpenGL, OpenGL is too much of a terrible API for me
|
|
||||||
to bother figuring out how the hell something so trivial can be done. */
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
@end
|
@end
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
|
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
|
||||||
|
|
||||||
self.internalView = [[GBOpenGLView alloc] initWithFrame:self.frame pixelFormat:pf];
|
self.internalView = [[GBOpenGLView alloc] initWithFrame:self.frame pixelFormat:pf];
|
||||||
((GBOpenGLView *)self.internalView).wantsBestResolutionOpenGLSurface = true;
|
((GBOpenGLView *)self.internalView).wantsBestResolutionOpenGLSurface = YES;
|
||||||
((GBOpenGLView *)self.internalView).openGLContext = context;
|
((GBOpenGLView *)self.internalView).openGLContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,8 +27,8 @@
|
|||||||
{
|
{
|
||||||
[super flip];
|
[super flip];
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.internalView setNeedsDisplay:true];
|
[self.internalView setNeedsDisplay:YES];
|
||||||
[self setNeedsDisplay:true];
|
[self setNeedsDisplay:YES];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
#import <CoreImage/CoreImage.h>
|
|
||||||
#import "GBViewMetal.h"
|
#import "GBViewMetal.h"
|
||||||
#pragma clang diagnostic ignored "-Wpartial-availability"
|
#pragma clang diagnostic ignored "-Wpartial-availability"
|
||||||
|
|
||||||
@ -52,9 +51,8 @@ static const vector_float2 rect[] =
|
|||||||
MTKView *view = [[MTKView alloc] initWithFrame:self.frame device:(device = MTLCreateSystemDefaultDevice())];
|
MTKView *view = [[MTKView alloc] initWithFrame:self.frame device:(device = MTLCreateSystemDefaultDevice())];
|
||||||
view.delegate = self;
|
view.delegate = self;
|
||||||
self.internalView = view;
|
self.internalView = view;
|
||||||
view.paused = true;
|
view.paused = YES;
|
||||||
view.enableSetNeedsDisplay = true;
|
view.enableSetNeedsDisplay = YES;
|
||||||
view.framebufferOnly = false;
|
|
||||||
|
|
||||||
vertices = [device newBufferWithBytes:rect
|
vertices = [device newBufferWithBytes:rect
|
||||||
length:sizeof(rect)
|
length:sizeof(rect)
|
||||||
@ -94,7 +92,7 @@ static const vector_float2 rect[] =
|
|||||||
withString:scaler_source];
|
withString:scaler_source];
|
||||||
|
|
||||||
MTLCompileOptions *options = [[MTLCompileOptions alloc] init];
|
MTLCompileOptions *options = [[MTLCompileOptions alloc] init];
|
||||||
options.fastMathEnabled = true;
|
options.fastMathEnabled = YES;
|
||||||
id<MTLLibrary> library = [device newLibraryWithSource:shader_source
|
id<MTLLibrary> library = [device newLibraryWithSource:shader_source
|
||||||
options:options
|
options:options
|
||||||
error:&error];
|
error:&error];
|
||||||
@ -125,7 +123,7 @@ static const vector_float2 rect[] =
|
|||||||
command_queue = [device newCommandQueue];
|
command_queue = [device newCommandQueue];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size
|
- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size
|
||||||
{
|
{
|
||||||
output_resolution = (vector_float2){size.width, size.height};
|
output_resolution = (vector_float2){size.width, size.height};
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
@ -133,7 +131,7 @@ static const vector_float2 rect[] =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)drawInMTKView:(MTKView *)view
|
- (void)drawInMTKView:(nonnull MTKView *)view
|
||||||
{
|
{
|
||||||
if (!(view.window.occlusionState & NSWindowOcclusionStateVisible)) return;
|
if (!(view.window.occlusionState & NSWindowOcclusionStateVisible)) return;
|
||||||
if (!self.gb) return;
|
if (!self.gb) return;
|
||||||
@ -210,23 +208,8 @@ static const vector_float2 rect[] =
|
|||||||
{
|
{
|
||||||
[super flip];
|
[super flip];
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[(MTKView *)self.internalView setNeedsDisplay:true];
|
[(MTKView *)self.internalView setNeedsDisplay:YES];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSImage *)renderToImage
|
|
||||||
{
|
|
||||||
CIImage *ciImage = [CIImage imageWithMTLTexture:[[(MTKView *)self.internalView currentDrawable] texture]
|
|
||||||
options:@{
|
|
||||||
kCIImageColorSpace: (__bridge_transfer id)CGColorSpaceCreateDeviceRGB()
|
|
||||||
}];
|
|
||||||
ciImage = [ciImage imageByApplyingTransform:CGAffineTransformTranslate(CGAffineTransformMakeScale(1, -1),
|
|
||||||
0, ciImage.extent.size.height)];
|
|
||||||
CIContext *context = [CIContext context];
|
|
||||||
CGImageRef cgImage = [context createCGImage:ciImage fromRect:ciImage.extent];
|
|
||||||
NSImage *ret = [[NSImage alloc] initWithCGImage:cgImage size:self.internalView.bounds.size];
|
|
||||||
CGImageRelease(cgImage);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#include <Core/gb.h>
|
|
||||||
|
|
||||||
@interface GBVisualizerView : NSView
|
|
||||||
- (void)addSample:(GB_sample_t *)sample;
|
|
||||||
@end
|
|
@ -1,71 +0,0 @@
|
|||||||
#import "GBVisualizerView.h"
|
|
||||||
#import "GBPaletteEditorController.h"
|
|
||||||
#include <Core/gb.h>
|
|
||||||
|
|
||||||
#define SAMPLE_COUNT 1024
|
|
||||||
|
|
||||||
static NSColor *color_to_effect_color(typeof(GB_PALETTE_DMG.colors[0]) color)
|
|
||||||
{
|
|
||||||
if (@available(macOS 10.10, *)) {
|
|
||||||
double tint = MAX(color.r, MAX(color.g, color.b)) + 64;
|
|
||||||
|
|
||||||
return [NSColor colorWithRed:color.r / tint
|
|
||||||
green:color.g / tint
|
|
||||||
blue:color.b / tint
|
|
||||||
alpha:tint/(255 + 64)];
|
|
||||||
|
|
||||||
}
|
|
||||||
return [NSColor colorWithRed:color.r / 255.0
|
|
||||||
green:color.g / 255.0
|
|
||||||
blue:color.b / 255.0
|
|
||||||
alpha:1.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation GBVisualizerView
|
|
||||||
{
|
|
||||||
GB_sample_t _samples[SAMPLE_COUNT];
|
|
||||||
size_t _position;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(NSRect)dirtyRect
|
|
||||||
{
|
|
||||||
const GB_palette_t *palette = [GBPaletteEditorController userPalette];
|
|
||||||
NSSize size = self.bounds.size;
|
|
||||||
|
|
||||||
[color_to_effect_color(palette->colors[0]) setFill];
|
|
||||||
NSRectFill(self.bounds);
|
|
||||||
|
|
||||||
NSBezierPath *line = [NSBezierPath bezierPath];
|
|
||||||
[line moveToPoint:NSMakePoint(0, size.height / 2)];
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < SAMPLE_COUNT; i++) {
|
|
||||||
GB_sample_t *sample = _samples + ((i + _position) % SAMPLE_COUNT);
|
|
||||||
double volume = ((signed)sample->left + (signed)sample->right) / 32768.0;
|
|
||||||
[line lineToPoint:NSMakePoint(size.width * (i + 0.5) / SAMPLE_COUNT,
|
|
||||||
(volume + 1) * size.height / 2)];
|
|
||||||
}
|
|
||||||
|
|
||||||
[line lineToPoint:NSMakePoint(size.width, size.height / 2)];
|
|
||||||
[line setLineWidth:1.0];
|
|
||||||
|
|
||||||
[color_to_effect_color(palette->colors[2]) setFill];
|
|
||||||
[line fill];
|
|
||||||
|
|
||||||
[color_to_effect_color(palette->colors[1]) setFill];
|
|
||||||
NSRectFill(NSMakeRect(0, size.height / 2 - 0.5, size.width, 1));
|
|
||||||
|
|
||||||
[color_to_effect_color(palette->colors[3]) setStroke];
|
|
||||||
[line stroke];
|
|
||||||
|
|
||||||
[super drawRect:dirtyRect];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)addSample:(GB_sample_t *)sample
|
|
||||||
{
|
|
||||||
_samples[_position++] = *sample;
|
|
||||||
if (_position == SAMPLE_COUNT) {
|
|
||||||
_position = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -10,7 +10,7 @@ static GBWarningPopover *lastPopover;
|
|||||||
lastPopover = [[self alloc] init];
|
lastPopover = [[self alloc] init];
|
||||||
|
|
||||||
[lastPopover setBehavior:NSPopoverBehaviorApplicationDefined];
|
[lastPopover setBehavior:NSPopoverBehaviorApplicationDefined];
|
||||||
[lastPopover setAnimates:true];
|
[lastPopover setAnimates:YES];
|
||||||
lastPopover.contentViewController = [[NSViewController alloc] initWithNibName:@"PopoverView" bundle:nil];
|
lastPopover.contentViewController = [[NSViewController alloc] initWithNibName:@"PopoverView" bundle:nil];
|
||||||
NSTextField *field = (NSTextField *)lastPopover.contentViewController.view;
|
NSTextField *field = (NSTextField *)lastPopover.contentViewController.view;
|
||||||
[field setStringValue:contents];
|
[field setStringValue:contents];
|
||||||
@ -20,7 +20,7 @@ static GBWarningPopover *lastPopover;
|
|||||||
[lastPopover setContentSize:textSize];
|
[lastPopover setContentSize:textSize];
|
||||||
|
|
||||||
if (!view.window.isVisible) {
|
if (!view.window.isVisible) {
|
||||||
[view.window setIsVisible:true];
|
[view.window setIsVisible:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
[lastPopover showRelativeToRect:view.bounds
|
[lastPopover showRelativeToRect:view.bounds
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleTypeExtensions</key>
|
<key>CFBundleTypeExtensions</key>
|
||||||
<array>
|
<array>
|
||||||
<string>isx</string>
|
<string>gbc</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleTypeIconFile</key>
|
<key>CFBundleTypeIconFile</key>
|
||||||
<string>ColorCartridge</string>
|
<string>ColorCartridge</string>
|
||||||
@ -68,26 +68,6 @@
|
|||||||
<key>NSDocumentClass</key>
|
<key>NSDocumentClass</key>
|
||||||
<string>Document</string>
|
<string>Document</string>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>gbs</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeIconFile</key>
|
|
||||||
<string>ColorCartridge</string>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Game Boy Sound File</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Viewer</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.github.liji32.sameboy.gbs</string>
|
|
||||||
</array>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>NSDocumentClass</key>
|
|
||||||
<string>Document</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>SameBoy</string>
|
<string>SameBoy</string>
|
||||||
@ -112,7 +92,7 @@
|
|||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>10.9</string>
|
<string>10.9</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2015-2021 Lior Halphon</string>
|
<string>Copyright © 2015-2020 Lior Halphon</string>
|
||||||
<key>NSMainNibFile</key>
|
<key>NSMainNibFile</key>
|
||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
@ -176,25 +156,6 @@
|
|||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Game Boy Sound File</string>
|
|
||||||
<key>UTTypeIconFile</key>
|
|
||||||
<string>ColorCartridge</string>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>com.github.liji32.sameboy.gbs</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>gbs</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</array>
|
</array>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>SameBoy needs to access your camera to emulate the Game Boy Camera</string>
|
<string>SameBoy needs to access your camera to emulate the Game Boy Camera</string>
|
||||||
|
Before Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 7.0 KiB |
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
<h1>SameBoy</h1>
|
<h1>SameBoy</h1>
|
||||||
<h2>MIT License</h2>
|
<h2>MIT License</h2>
|
||||||
<h3>Copyright © 2015-2021 Lior Halphon</h3>
|
<h3>Copyright © 2015-2020 Lior Halphon</h3>
|
||||||
|
|
||||||
<p>Permission is hereby granted, free of charge, to any person obtaining a copy
|
<p>Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -12,11 +12,7 @@
|
|||||||
</customObject>
|
</customObject>
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate">
|
<customObject id="Voe-Tx-rLC" customClass="AppDelegate"/>
|
||||||
<connections>
|
|
||||||
<outlet property="linkCableMenuItem" destination="V4S-Fo-xJK" id="KL9-3K-64i"/>
|
|
||||||
</connections>
|
|
||||||
</customObject>
|
|
||||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||||
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||||
<items>
|
<items>
|
||||||
@ -316,35 +312,12 @@
|
|||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="5GS-tt-E0a"/>
|
<menuItem isSeparatorItem="YES" id="5GS-tt-E0a"/>
|
||||||
<menuItem title="Save Screenshot" keyEquivalent="s" id="0J3-yf-iXs">
|
|
||||||
<connections>
|
|
||||||
<action selector="saveScreenshot:" target="-1" id="gJd-ml-J8p"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Save Screenshot As…" alternate="YES" keyEquivalent="s" id="98X-Fp-Uny">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="saveScreenshotAs:" target="-1" id="Cxc-Gx-ql1"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Copy Screenshot" keyEquivalent="S" id="vbX-pB-QC8">
|
|
||||||
<connections>
|
|
||||||
<action selector="copyScreenshot:" target="-1" id="XJC-EB-HNl"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem isSeparatorItem="YES" id="zk7-gf-LXN"/>
|
|
||||||
<menuItem title="Game Boy" tag="1" id="g7C-LA-VAr">
|
<menuItem title="Game Boy" tag="1" id="g7C-LA-VAr">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="reset:" target="-1" id="rxG-cz-s1S"/>
|
<action selector="reset:" target="-1" id="rxG-cz-s1S"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Game Boy Pocket/Light" tag="5" id="1bM-CT-hoW">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="reset:" target="-1" id="U7l-BM-kB1"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Super Game Boy" tag="4" id="vc7-yy-ARW">
|
<menuItem title="Super Game Boy" tag="4" id="vc7-yy-ARW">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
@ -400,29 +373,12 @@
|
|||||||
<action selector="disconnectAllAccessories:" target="-1" id="5hY-9U-nRn"/>
|
<action selector="disconnectAllAccessories:" target="-1" id="5hY-9U-nRn"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Game Link Cable & Infrared" id="V4S-Fo-xJK">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<menu key="submenu" title="Game Link Cable & Infrared" id="6sJ-Wz-QLj">
|
|
||||||
<connections>
|
|
||||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="PMY-5j-25T"/>
|
|
||||||
</connections>
|
|
||||||
</menu>
|
|
||||||
<connections>
|
|
||||||
<action selector="nop:" target="Voe-Tx-rLC" id="Bpa-0C-lkN"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Game Boy Printer" id="zHR-Ha-pOR">
|
<menuItem title="Game Boy Printer" id="zHR-Ha-pOR">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="connectPrinter:" target="-1" id="tl1-CL-tAw"/>
|
<action selector="connectPrinter:" target="-1" id="tl1-CL-tAw"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Workboy" id="lo9-CX-BJj">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="connectWorkboy:" target="-1" id="6vS-bq-wAX"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
</items>
|
</items>
|
||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
@ -456,19 +412,6 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="M6n-8G-LZS"/>
|
<menuItem isSeparatorItem="YES" id="M6n-8G-LZS"/>
|
||||||
<menuItem title="Show Background and Window" state="on" id="yfD-Qd-zoz">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="toggleDisplayBackground:" target="-1" id="p5b-1n-SPR"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Show Objects" state="on" id="OWx-a0-vQk">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="toggleDisplayObjects:" target="-1" id="8ie-ey-739"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem isSeparatorItem="YES" id="afI-BR-65k"/>
|
|
||||||
<menuItem title="Show Memory" id="UIa-n7-LSa">
|
<menuItem title="Show Memory" id="UIa-n7-LSa">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
#import <AppKit/AppKit.h>
|
|
||||||
#import <objc/runtime.h>
|
|
||||||
|
|
||||||
@interface NSImageRep(PrivateAPI)
|
|
||||||
@property(setter=_setAppearanceName:) NSString *_appearanceName;
|
|
||||||
@end
|
|
||||||
|
|
||||||
static NSImage * (*imageNamed)(Class self, SEL _cmd, NSString *name);
|
|
||||||
|
|
||||||
@implementation NSImage(DarkHooks)
|
|
||||||
|
|
||||||
+ (NSImage *)imageNamedWithDark:(NSImageName)name
|
|
||||||
{
|
|
||||||
NSImage *light = imageNamed(self, _cmd, name);
|
|
||||||
if (@available(macOS 10.14, *)) {
|
|
||||||
NSImage *dark = imageNamed(self, _cmd, [name stringByAppendingString:@"~dark"]);
|
|
||||||
if (!dark) {
|
|
||||||
return light;
|
|
||||||
}
|
|
||||||
NSImage *ret = [[NSImage alloc] initWithSize:light.size];
|
|
||||||
for (NSImageRep *rep in light.representations) {
|
|
||||||
[rep _setAppearanceName:NSAppearanceNameAqua];
|
|
||||||
[ret addRepresentation:rep];
|
|
||||||
}
|
|
||||||
for (NSImageRep *rep in dark.representations) {
|
|
||||||
[rep _setAppearanceName:NSAppearanceNameDarkAqua];
|
|
||||||
[ret addRepresentation:rep];
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return light;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(void)load
|
|
||||||
{
|
|
||||||
if (@available(macOS 10.14, *)) {
|
|
||||||
imageNamed = (void *)[self methodForSelector:@selector(imageNamed:)];
|
|
||||||
method_setImplementation(class_getClassMethod(self, @selector(imageNamed:)),
|
|
||||||
[self methodForSelector:@selector(imageNamedWithDark:)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@end
|
|
BIN
Cocoa/Next.png
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.7 KiB |
BIN
Cocoa/Pause.png
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 4.3 KiB |
BIN
Cocoa/Play.png
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 4.5 KiB |
BIN
Cocoa/Rewind.png
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 5.9 KiB |
@ -1,139 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="macosx"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
|
||||||
<plugIn identifier="com.apple.WebKitIBPlugin" version="14868"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<objects>
|
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="AppDelegate">
|
|
||||||
<connections>
|
|
||||||
<outlet property="updateChanges" destination="ATt-VM-cb5" id="hJj-Nd-FBv"/>
|
|
||||||
<outlet property="updateProgressButton" destination="7nO-AA-WmG" id="wTa-9l-cOG"/>
|
|
||||||
<outlet property="updateProgressLabel" destination="wIm-GX-c6B" id="URp-JG-6wR"/>
|
|
||||||
<outlet property="updateProgressSpinner" destination="fqq-Nb-THz" id="4vC-m5-ysO"/>
|
|
||||||
<outlet property="updateProgressWindow" destination="2Gy-QG-FoA" id="RXw-50-DQh"/>
|
|
||||||
<outlet property="updateWindow" destination="QvC-M9-y7g" id="iwP-kC-tmG"/>
|
|
||||||
</connections>
|
|
||||||
</customObject>
|
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
|
||||||
<window title="Update Available" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g">
|
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
|
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
|
||||||
<rect key="contentRect" x="196" y="240" width="480" height="360"/>
|
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
|
||||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xZt-UI-Wl2">
|
|
||||||
<rect key="frame" x="338" y="13" width="128" height="32"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
|
||||||
<buttonCell key="cell" type="push" title="Install Update" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Mav-rY-sJo">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<string key="keyEquivalent" base64-UTF8="YES">
|
|
||||||
DQ
|
|
||||||
</string>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="installUpdate:" target="-2" id="jJc-CY-4vz"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<button verticalHuggingPriority="750" id="b3S-YN-iDZ">
|
|
||||||
<rect key="frame" x="242" y="13" width="96" height="32"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
|
||||||
<buttonCell key="cell" type="push" title="Not Now" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="QsJ-cv-hwF">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="performClose:" target="QvC-M9-y7g" id="8qO-ac-k7U"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<button verticalHuggingPriority="750" id="uOb-4Q-S8o">
|
|
||||||
<rect key="frame" x="14" y="13" width="128" height="32"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
|
||||||
<buttonCell key="cell" type="push" title="Skip Version" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ai5-6n-dvH">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="skipVersion:" target="-2" id="Jf8-Qe-6X0"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Swh-S6-6XA">
|
|
||||||
<rect key="frame" x="18" y="313" width="444" height="27"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="A new version of SameBoy is available with the following changes:" id="WsO-pC-VO7">
|
|
||||||
<font key="font" usesAppearanceFont="YES"/>
|
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="9Tu-Q3-l40">
|
|
||||||
<rect key="frame" x="0.0" y="302" width="480" height="5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
|
||||||
</box>
|
|
||||||
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="dva-Ay-nnl">
|
|
||||||
<rect key="frame" x="0.0" y="58" width="480" height="4"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
|
||||||
</box>
|
|
||||||
<webView maintainsBackForwardList="NO" id="ATt-VM-cb5">
|
|
||||||
<rect key="frame" x="0.0" y="61" width="480" height="243"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<webPreferences key="preferences" defaultFontSize="13" defaultFixedFontSize="13" minimumFontSize="0" plugInsEnabled="NO" javaEnabled="NO" javaScriptEnabled="NO" javaScriptCanOpenWindowsAutomatically="NO" loadsImagesAutomatically="NO" allowsAnimatedImages="NO" allowsAnimatedImageLooping="NO">
|
|
||||||
<nil key="identifier"/>
|
|
||||||
</webPreferences>
|
|
||||||
<connections>
|
|
||||||
<outlet property="UIDelegate" destination="-2" id="xQ1-eY-1hu"/>
|
|
||||||
<outlet property="frameLoadDelegate" destination="-2" id="BOf-df-5LR"/>
|
|
||||||
</connections>
|
|
||||||
</webView>
|
|
||||||
</subviews>
|
|
||||||
</view>
|
|
||||||
<point key="canvasLocation" x="217" y="267"/>
|
|
||||||
</window>
|
|
||||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="2Gy-QG-FoA">
|
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
|
||||||
<rect key="contentRect" x="283" y="305" width="512" height="61"/>
|
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
|
||||||
<view key="contentView" id="jjT-Z5-15Q">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="512" height="61"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<progressIndicator wantsLayer="YES" fixedFrame="YES" maxValue="100" displayedWhenStopped="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="fqq-Nb-THz">
|
|
||||||
<rect key="frame" x="20" y="15" width="32" height="32"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
</progressIndicator>
|
|
||||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7nO-AA-WmG">
|
|
||||||
<rect key="frame" x="417" y="13" width="82" height="32"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
|
||||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6l6-qX-gsr">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<string key="keyEquivalent" base64-UTF8="YES">
|
|
||||||
Gw
|
|
||||||
</string>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="updateAction:" target="-2" id="geO-Gk-xrs"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wIm-GX-c6B">
|
|
||||||
<rect key="frame" x="58" y="15" width="359" height="24"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Downloading update..." id="qmF-X1-v5B">
|
|
||||||
<font key="font" usesAppearanceFont="YES"/>
|
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
|
||||||
</view>
|
|
||||||
<point key="canvasLocation" x="11" y="-203.5"/>
|
|
||||||
</window>
|
|
||||||
</objects>
|
|
||||||
</document>
|
|
1022
Core/apu.c
76
Core/apu.h
@ -3,7 +3,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include "defs.h"
|
#include "gb_struct_def.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
/* Speed = 1 / Length (in seconds) */
|
/* Speed = 1 / Length (in seconds) */
|
||||||
@ -45,19 +46,12 @@ enum GB_CHANNELS {
|
|||||||
GB_N_CHANNELS
|
GB_N_CHANNELS
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
bool locked:1;
|
|
||||||
bool clock:1; // Represents FOSY on channel 4
|
|
||||||
unsigned padding:6;
|
|
||||||
} GB_envelope_clock_t;
|
|
||||||
|
|
||||||
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample);
|
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample);
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
bool global_enable;
|
bool global_enable;
|
||||||
uint16_t apu_cycles;
|
uint8_t apu_cycles;
|
||||||
|
|
||||||
uint8_t samples[GB_N_CHANNELS];
|
uint8_t samples[GB_N_CHANNELS];
|
||||||
bool is_active[GB_N_CHANNELS];
|
bool is_active[GB_N_CHANNELS];
|
||||||
@ -70,24 +64,23 @@ typedef struct
|
|||||||
|
|
||||||
uint8_t square_sweep_countdown; // In 128Hz
|
uint8_t square_sweep_countdown; // In 128Hz
|
||||||
uint8_t square_sweep_calculate_countdown; // In 2 MHz
|
uint8_t square_sweep_calculate_countdown; // In 2 MHz
|
||||||
uint16_t sweep_length_addend;
|
uint16_t new_sweep_sample_length;
|
||||||
uint16_t shadow_sweep_sample_length;
|
uint16_t shadow_sweep_sample_length;
|
||||||
bool unshifted_sweep;
|
bool sweep_enabled;
|
||||||
bool enable_zombie_calculate_stepping;
|
bool sweep_decreasing;
|
||||||
|
|
||||||
uint8_t channel_1_restart_hold;
|
|
||||||
uint16_t channel1_completed_addend;
|
|
||||||
struct {
|
struct {
|
||||||
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
|
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
|
||||||
uint8_t current_volume; // Reloaded from NRX2
|
uint8_t current_volume; // Reloaded from NRX2
|
||||||
uint8_t volume_countdown; // Reloaded from NRX2
|
uint8_t volume_countdown; // Reloaded from NRX2
|
||||||
uint8_t current_sample_index;
|
uint8_t current_sample_index; /* For save state compatibility,
|
||||||
bool sample_surpressed;
|
highest bit is reused (See NR14/NR24's
|
||||||
|
write code)*/
|
||||||
|
|
||||||
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
|
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
|
||||||
uint16_t sample_length; // From NRX3, NRX4, in APU ticks
|
uint16_t sample_length; // From NRX3, NRX4, in APU ticks
|
||||||
bool length_enabled; // NRX4
|
bool length_enabled; // NRX4
|
||||||
GB_envelope_clock_t envelope_clock;
|
|
||||||
} square_channels[2];
|
} square_channels[2];
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -99,10 +92,10 @@ typedef struct
|
|||||||
|
|
||||||
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
|
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
|
||||||
uint8_t current_sample_index;
|
uint8_t current_sample_index;
|
||||||
uint8_t current_sample_byte; // Current sample byte.
|
uint8_t current_sample; // Current sample before shifting.
|
||||||
|
|
||||||
|
int8_t wave_form[32];
|
||||||
bool wave_form_just_read;
|
bool wave_form_just_read;
|
||||||
bool pulsed;
|
|
||||||
uint8_t bugged_read_countdown;
|
|
||||||
} wave_channel;
|
} wave_channel;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -112,24 +105,20 @@ typedef struct
|
|||||||
uint16_t lfsr;
|
uint16_t lfsr;
|
||||||
bool narrow;
|
bool narrow;
|
||||||
|
|
||||||
uint8_t counter_countdown; // Counts from 0-7 to 0 to tick counter (Scaled from 512KHz to 2MHz)
|
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length)
|
||||||
uint16_t counter; // A bit from this 14-bit register ticks LFSR
|
uint16_t sample_length; // From NR43, in APU ticks
|
||||||
bool length_enabled; // NR44
|
bool length_enabled; // NR44
|
||||||
|
|
||||||
uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of
|
uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of
|
||||||
// 1MHz. This variable keeps track of the alignment.
|
// 1MHz. This variable keeps track of the alignment.
|
||||||
bool current_lfsr_sample;
|
|
||||||
int8_t delta;
|
|
||||||
bool countdown_reloaded;
|
|
||||||
uint8_t dmg_delayed_start;
|
|
||||||
GB_envelope_clock_t envelope_clock;
|
|
||||||
} noise_channel;
|
} noise_channel;
|
||||||
|
|
||||||
enum {
|
#define GB_SKIP_DIV_EVENT_INACTIVE 0
|
||||||
GB_SKIP_DIV_EVENT_INACTIVE,
|
#define GB_SKIP_DIV_EVENT_SKIPPED 1
|
||||||
GB_SKIP_DIV_EVENT_SKIPPED,
|
#define GB_SKIP_DIV_EVENT_SKIP 2
|
||||||
GB_SKIP_DIV_EVENT_SKIP,
|
uint8_t skip_div_event;
|
||||||
} skip_div_event:8;
|
bool current_lfsr_sample;
|
||||||
uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch
|
uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch
|
||||||
} GB_apu_t;
|
} GB_apu_t;
|
||||||
|
|
||||||
@ -143,7 +132,8 @@ typedef enum {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned sample_rate;
|
unsigned sample_rate;
|
||||||
|
|
||||||
unsigned sample_cycles; // Counts by sample_rate until it reaches the clock frequency
|
double sample_cycles; // In 8 MHz units
|
||||||
|
double cycles_per_sample;
|
||||||
|
|
||||||
// Samples are NOT normalized to MAX_CH_AMP * 4 at this stage!
|
// Samples are NOT normalized to MAX_CH_AMP * 4 at this stage!
|
||||||
unsigned cycles_since_render;
|
unsigned cycles_since_render;
|
||||||
@ -158,24 +148,22 @@ typedef struct {
|
|||||||
|
|
||||||
GB_sample_callback_t sample_callback;
|
GB_sample_callback_t sample_callback;
|
||||||
|
|
||||||
double interference_volume;
|
bool rate_set_in_clocks;
|
||||||
double interference_highpass;
|
|
||||||
} GB_apu_output_t;
|
} GB_apu_output_t;
|
||||||
|
|
||||||
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
|
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
|
||||||
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */
|
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */
|
||||||
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
|
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
|
||||||
void GB_set_interference_volume(GB_gameboy_t *gb, double volume);
|
|
||||||
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
|
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
internal bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
|
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
|
||||||
internal void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
|
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
|
||||||
internal uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg);
|
uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg);
|
||||||
internal void GB_apu_div_event(GB_gameboy_t *gb);
|
void GB_apu_div_event(GB_gameboy_t *gb);
|
||||||
internal void GB_apu_div_secondary_event(GB_gameboy_t *gb);
|
void GB_apu_init(GB_gameboy_t *gb);
|
||||||
internal void GB_apu_init(GB_gameboy_t *gb);
|
void GB_apu_run(GB_gameboy_t *gb);
|
||||||
internal void GB_apu_run(GB_gameboy_t *gb, bool force);
|
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb);
|
||||||
|
void GB_borrow_sgb_border(GB_gameboy_t *gb);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* apu_h */
|
#endif /* apu_h */
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
|
|
||||||
static uint32_t noise_seed = 0;
|
static signed noise_seed = 0;
|
||||||
|
|
||||||
/* This is not a complete emulation of the camera chip. Only the features used by the Game Boy Camera ROMs are supported.
|
/* This is not a complete emulation of the camera chip. Only the features used by the GameBoy Camera ROMs are supported.
|
||||||
We also do not emulate the timing of the real cart, as it might be actually faster than the webcam. */
|
We also do not emulate the timing of the real cart, as it might be actually faster than the webcam. */
|
||||||
|
|
||||||
static uint8_t generate_noise(uint8_t x, uint8_t y)
|
static uint8_t generate_noise(uint8_t x, uint8_t y)
|
||||||
{
|
{
|
||||||
uint32_t value = (x * 151 + y * 149) ^ noise_seed;
|
signed value = (x + y * 128 + noise_seed);
|
||||||
uint32_t hash = 0;
|
uint8_t *data = (uint8_t *) &value;
|
||||||
|
unsigned hash = 0;
|
||||||
|
|
||||||
while (value) {
|
while ((signed *) data != &value + 1) {
|
||||||
|
hash ^= (*data << 8);
|
||||||
|
if (hash & 0x8000) {
|
||||||
|
hash ^= 0x8a00;
|
||||||
|
hash ^= *data;
|
||||||
|
}
|
||||||
|
data++;
|
||||||
hash <<= 1;
|
hash <<= 1;
|
||||||
if (hash & 0x100) {
|
|
||||||
hash ^= 0x101;
|
|
||||||
}
|
|
||||||
if (value & 0x80000000) {
|
|
||||||
hash ^= 0xA1;
|
|
||||||
}
|
|
||||||
value <<= 1;
|
|
||||||
}
|
}
|
||||||
return hash;
|
return (hash >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static long get_processed_color(GB_gameboy_t *gb, uint8_t x, uint8_t y)
|
static long get_processed_color(GB_gameboy_t *gb, uint8_t x, uint8_t y)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef camera_h
|
#ifndef camera_h
|
||||||
#define camera_h
|
#define camera_h
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "defs.h"
|
#include "gb_struct_def.h"
|
||||||
|
|
||||||
typedef uint8_t (*GB_camera_get_pixel_callback_t)(GB_gameboy_t *gb, uint8_t x, uint8_t y);
|
typedef uint8_t (*GB_camera_get_pixel_callback_t)(GB_gameboy_t *gb, uint8_t x, uint8_t y);
|
||||||
typedef void (*GB_camera_update_request_callback_t)(GB_gameboy_t *gb);
|
typedef void (*GB_camera_update_request_callback_t)(GB_gameboy_t *gb);
|
||||||
|
@ -32,11 +32,10 @@ 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)
|
void GB_apply_cheat(GB_gameboy_t *gb, uint16_t address, uint8_t *value)
|
||||||
{
|
{
|
||||||
if (likely(!gb->cheat_enabled)) return;
|
if (!gb->cheat_enabled) return;
|
||||||
if (likely(gb->cheat_count == 0)) return; // Optimization
|
if (!gb->boot_rom_finished) return;
|
||||||
if (unlikely(!gb->boot_rom_finished)) return;
|
|
||||||
const GB_cheat_hash_t *hash = gb->cheat_hash[hash_addr(address)];
|
const GB_cheat_hash_t *hash = gb->cheat_hash[hash_addr(address)];
|
||||||
if (unlikely(hash)) {
|
if (hash) {
|
||||||
for (unsigned i = 0; i < hash->size; i++) {
|
for (unsigned i = 0; i < hash->size; i++) {
|
||||||
GB_cheat_t *cheat = hash->cheats[i];
|
GB_cheat_t *cheat = hash->cheats[i];
|
||||||
if (cheat->address == address && cheat->enabled && (!cheat->use_old_value || cheat->old_value == *value)) {
|
if (cheat->address == address && cheat->enabled && (!cheat->use_old_value || cheat->old_value == *value)) {
|
||||||
@ -70,7 +69,7 @@ void GB_add_cheat(GB_gameboy_t *gb, const char *description, uint16_t address, u
|
|||||||
cheat->enabled = enabled;
|
cheat->enabled = enabled;
|
||||||
strncpy(cheat->description, description, sizeof(cheat->description));
|
strncpy(cheat->description, description, sizeof(cheat->description));
|
||||||
cheat->description[sizeof(cheat->description) - 1] = 0;
|
cheat->description[sizeof(cheat->description) - 1] = 0;
|
||||||
gb->cheats = realloc(gb->cheats, (++gb->cheat_count) * sizeof(gb->cheats[0]));
|
gb->cheats = realloc(gb->cheats, (++gb->cheat_count) * sizeof(*cheat));
|
||||||
gb->cheats[gb->cheat_count - 1] = cheat;
|
gb->cheats[gb->cheat_count - 1] = cheat;
|
||||||
|
|
||||||
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(address)];
|
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(address)];
|
||||||
@ -101,7 +100,7 @@ void GB_remove_cheat(GB_gameboy_t *gb, const GB_cheat_t *cheat)
|
|||||||
gb->cheats = NULL;
|
gb->cheats = NULL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gb->cheats = realloc(gb->cheats, gb->cheat_count * sizeof(gb->cheats[0]));
|
gb->cheats = realloc(gb->cheats, gb->cheat_count * sizeof(*cheat));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -110,7 +109,7 @@ void GB_remove_cheat(GB_gameboy_t *gb, const GB_cheat_t *cheat)
|
|||||||
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(cheat->address)];
|
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(cheat->address)];
|
||||||
for (unsigned i = 0; i < (*hash)->size; i++) {
|
for (unsigned i = 0; i < (*hash)->size; i++) {
|
||||||
if ((*hash)->cheats[i] == cheat) {
|
if ((*hash)->cheats[i] == cheat) {
|
||||||
(*hash)->cheats[i] = (*hash)->cheats[--(*hash)->size];
|
(*hash)->cheats[i] = (*hash)->cheats[(*hash)->size--];
|
||||||
if ((*hash)->size == 0) {
|
if ((*hash)->size == 0) {
|
||||||
free(*hash);
|
free(*hash);
|
||||||
*hash = NULL;
|
*hash = NULL;
|
||||||
@ -201,7 +200,7 @@ void GB_update_cheat(GB_gameboy_t *gb, const GB_cheat_t *_cheat, const char *des
|
|||||||
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(cheat->address)];
|
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(cheat->address)];
|
||||||
for (unsigned i = 0; i < (*hash)->size; i++) {
|
for (unsigned i = 0; i < (*hash)->size; i++) {
|
||||||
if ((*hash)->cheats[i] == cheat) {
|
if ((*hash)->cheats[i] == cheat) {
|
||||||
(*hash)->cheats[i] = (*hash)->cheats[--(*hash)->size];
|
(*hash)->cheats[i] = (*hash)->cheats[(*hash)->size--];
|
||||||
if ((*hash)->size == 0) {
|
if ((*hash)->size == 0) {
|
||||||
free(*hash);
|
free(*hash);
|
||||||
*hash = NULL;
|
*hash = NULL;
|
||||||
@ -223,7 +222,7 @@ void GB_update_cheat(GB_gameboy_t *gb, const GB_cheat_t *_cheat, const char *des
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
(*hash)->size++;
|
(*hash)->size++;
|
||||||
*hash = realloc(*hash, sizeof(GB_cheat_hash_t) + sizeof(cheat) * (*hash)->size);
|
*hash = malloc(sizeof(GB_cheat_hash_t) + sizeof(cheat) * (*hash)->size);
|
||||||
(*hash)->cheats[(*hash)->size - 1] = cheat;
|
(*hash)->cheats[(*hash)->size - 1] = cheat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,7 +250,7 @@ void GB_load_cheats(GB_gameboy_t *gb, const char *path)
|
|||||||
uint32_t struct_size = 0;
|
uint32_t struct_size = 0;
|
||||||
fread(&magic, sizeof(magic), 1, f);
|
fread(&magic, sizeof(magic), 1, f);
|
||||||
fread(&struct_size, sizeof(struct_size), 1, f);
|
fread(&struct_size, sizeof(struct_size), 1, f);
|
||||||
if (magic != LE32(CHEAT_MAGIC) && magic != BE32(CHEAT_MAGIC)) {
|
if (magic != CHEAT_MAGIC && magic != __builtin_bswap32(CHEAT_MAGIC)) {
|
||||||
GB_log(gb, "The file is not a SameBoy cheat database");
|
GB_log(gb, "The file is not a SameBoy cheat database");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -268,7 +267,7 @@ void GB_load_cheats(GB_gameboy_t *gb, const char *path)
|
|||||||
|
|
||||||
GB_cheat_t cheat;
|
GB_cheat_t cheat;
|
||||||
while (fread(&cheat, sizeof(cheat), 1, f)) {
|
while (fread(&cheat, sizeof(cheat), 1, f)) {
|
||||||
if (magic != CHEAT_MAGIC) {
|
if (magic == __builtin_bswap32(CHEAT_MAGIC)) {
|
||||||
cheat.address = __builtin_bswap16(cheat.address);
|
cheat.address = __builtin_bswap16(cheat.address);
|
||||||
cheat.bank = __builtin_bswap16(cheat.bank);
|
cheat.bank = __builtin_bswap16(cheat.bank);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#ifndef cheats_h
|
#ifndef cheats_h
|
||||||
#define cheats_h
|
#define cheats_h
|
||||||
#include "defs.h"
|
#include "gb_struct_def.h"
|
||||||
|
|
||||||
#define GB_CHEAT_ANY_BANK 0xFFFF
|
#define GB_CHEAT_ANY_BANK 0xFFFF
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ int GB_save_cheats(GB_gameboy_t *gb, const char *path);
|
|||||||
#ifdef GB_DISABLE_CHEATS
|
#ifdef GB_DISABLE_CHEATS
|
||||||
#define GB_apply_cheat(...)
|
#define GB_apply_cheat(...)
|
||||||
#else
|
#else
|
||||||
internal void GB_apply_cheat(GB_gameboy_t *gb, uint16_t address, uint8_t *value);
|
void GB_apply_cheat(GB_gameboy_t *gb, uint16_t address, uint8_t *value);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
484
Core/debugger.c
@ -131,25 +131,30 @@ static const char *value_to_string(GB_gameboy_t *gb, uint16_t value, bool prefer
|
|||||||
symbol = NULL;
|
symbol = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Avoid overflow */
|
||||||
|
if (symbol && strlen(symbol->name) > 240) {
|
||||||
|
symbol = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (!symbol) {
|
if (!symbol) {
|
||||||
snprintf(output, sizeof(output), "$%04x", value);
|
sprintf(output, "$%04x", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (symbol->addr == value) {
|
else if (symbol->addr == value) {
|
||||||
if (prefer_name) {
|
if (prefer_name) {
|
||||||
snprintf(output, sizeof(output), "%s ($%04x)", symbol->name, value);
|
sprintf(output, "%s ($%04x)", symbol->name, value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
snprintf(output, sizeof(output), "$%04x (%s)", value, symbol->name);
|
sprintf(output, "$%04x (%s)", value, symbol->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
if (prefer_name) {
|
if (prefer_name) {
|
||||||
snprintf(output, sizeof(output), "%s+$%03x ($%04x)", symbol->name, value - symbol->addr, value);
|
sprintf(output, "%s+$%03x ($%04x)", symbol->name, value - symbol->addr, value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
snprintf(output, sizeof(output), "$%04x (%s+$%03x)", value, symbol->name, value - symbol->addr);
|
sprintf(output, "$%04x (%s+$%03x)", value, symbol->name, value - symbol->addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
@ -166,25 +171,30 @@ static const char *debugger_value_to_string(GB_gameboy_t *gb, value_t value, boo
|
|||||||
symbol = NULL;
|
symbol = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Avoid overflow */
|
||||||
|
if (symbol && strlen(symbol->name) > 240) {
|
||||||
|
symbol = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (!symbol) {
|
if (!symbol) {
|
||||||
snprintf(output, sizeof(output), "$%02x:$%04x", value.bank, value.value);
|
sprintf(output, "$%02x:$%04x", value.bank, value.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (symbol->addr == value.value) {
|
else if (symbol->addr == value.value) {
|
||||||
if (prefer_name) {
|
if (prefer_name) {
|
||||||
snprintf(output, sizeof(output), "%s ($%02x:$%04x)", symbol->name, value.bank, value.value);
|
sprintf(output, "%s ($%02x:$%04x)", symbol->name, value.bank, value.value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
snprintf(output, sizeof(output), "$%02x:$%04x (%s)", value.bank, value.value, symbol->name);
|
sprintf(output, "$%02x:$%04x (%s)", value.bank, value.value, symbol->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
if (prefer_name) {
|
if (prefer_name) {
|
||||||
snprintf(output, sizeof(output), "%s+$%03x ($%02x:$%04x)", symbol->name, value.value - symbol->addr, value.bank, value.value);
|
sprintf(output, "%s+$%03x ($%02x:$%04x)", symbol->name, value.value - symbol->addr, value.bank, value.value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
snprintf(output, sizeof(output), "$%02x:$%04x (%s+$%03x)", value.bank, value.value, symbol->name, value.value - symbol->addr);
|
sprintf(output, "$%02x:$%04x (%s+$%03x)", value.bank, value.value, symbol->name, value.value - symbol->addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
@ -428,23 +438,23 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
|
|||||||
if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) {
|
if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) {
|
||||||
if (length == 1) {
|
if (length == 1) {
|
||||||
switch (string[0]) {
|
switch (string[0]) {
|
||||||
case 'a': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->af};
|
case 'a': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_AF]};
|
||||||
case 'f': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->af};
|
case 'f': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_AF]};
|
||||||
case 'b': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->bc};
|
case 'b': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_BC]};
|
||||||
case 'c': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->bc};
|
case 'c': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_BC]};
|
||||||
case 'd': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->de};
|
case 'd': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_DE]};
|
||||||
case 'e': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->de};
|
case 'e': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_DE]};
|
||||||
case 'h': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->hl};
|
case 'h': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_HL]};
|
||||||
case 'l': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->hl};
|
case 'l': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_HL]};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (length == 2) {
|
else if (length == 2) {
|
||||||
switch (string[0]) {
|
switch (string[0]) {
|
||||||
case 'a': if (string[1] == 'f') return (lvalue_t){LVALUE_REG16, .register_address = &gb->af};
|
case 'a': if (string[1] == 'f') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_AF]};
|
||||||
case 'b': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->bc};
|
case 'b': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_BC]};
|
||||||
case 'd': if (string[1] == 'e') return (lvalue_t){LVALUE_REG16, .register_address = &gb->de};
|
case 'd': if (string[1] == 'e') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_DE]};
|
||||||
case 'h': if (string[1] == 'l') return (lvalue_t){LVALUE_REG16, .register_address = &gb->hl};
|
case 'h': if (string[1] == 'l') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_HL]};
|
||||||
case 's': if (string[1] == 'p') return (lvalue_t){LVALUE_REG16, .register_address = &gb->sp};
|
case 's': if (string[1] == 'p') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_SP]};
|
||||||
case 'p': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->pc};
|
case 'p': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->pc};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -463,7 +473,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
|||||||
size_t length, bool *error,
|
size_t length, bool *error,
|
||||||
uint16_t *watchpoint_address, uint8_t *watchpoint_new_value)
|
uint16_t *watchpoint_address, uint8_t *watchpoint_new_value)
|
||||||
{
|
{
|
||||||
/* Disable watchpoints while evaluating expressions */
|
/* Disable watchpoints while evaulating expressions */
|
||||||
uint16_t n_watchpoints = gb->n_watchpoints;
|
uint16_t n_watchpoints = gb->n_watchpoints;
|
||||||
gb->n_watchpoints = 0;
|
gb->n_watchpoints = 0;
|
||||||
|
|
||||||
@ -606,23 +616,23 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
|||||||
if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) {
|
if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) {
|
||||||
if (length == 1) {
|
if (length == 1) {
|
||||||
switch (string[0]) {
|
switch (string[0]) {
|
||||||
case 'a': ret = VALUE_16(gb->af >> 8); goto exit;
|
case 'a': ret = VALUE_16(gb->registers[GB_REGISTER_AF] >> 8); goto exit;
|
||||||
case 'f': ret = VALUE_16(gb->af & 0xFF); goto exit;
|
case 'f': ret = VALUE_16(gb->registers[GB_REGISTER_AF] & 0xFF); goto exit;
|
||||||
case 'b': ret = VALUE_16(gb->bc >> 8); goto exit;
|
case 'b': ret = VALUE_16(gb->registers[GB_REGISTER_BC] >> 8); goto exit;
|
||||||
case 'c': ret = VALUE_16(gb->bc & 0xFF); goto exit;
|
case 'c': ret = VALUE_16(gb->registers[GB_REGISTER_BC] & 0xFF); goto exit;
|
||||||
case 'd': ret = VALUE_16(gb->de >> 8); goto exit;
|
case 'd': ret = VALUE_16(gb->registers[GB_REGISTER_DE] >> 8); goto exit;
|
||||||
case 'e': ret = VALUE_16(gb->de & 0xFF); goto exit;
|
case 'e': ret = VALUE_16(gb->registers[GB_REGISTER_DE] & 0xFF); goto exit;
|
||||||
case 'h': ret = VALUE_16(gb->hl >> 8); goto exit;
|
case 'h': ret = VALUE_16(gb->registers[GB_REGISTER_HL] >> 8); goto exit;
|
||||||
case 'l': ret = VALUE_16(gb->hl & 0xFF); goto exit;
|
case 'l': ret = VALUE_16(gb->registers[GB_REGISTER_HL] & 0xFF); goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (length == 2) {
|
else if (length == 2) {
|
||||||
switch (string[0]) {
|
switch (string[0]) {
|
||||||
case 'a': if (string[1] == 'f') {ret = VALUE_16(gb->af); goto exit;}
|
case 'a': if (string[1] == 'f') {ret = VALUE_16(gb->registers[GB_REGISTER_AF]); goto exit;}
|
||||||
case 'b': if (string[1] == 'c') {ret = VALUE_16(gb->bc); goto exit;}
|
case 'b': if (string[1] == 'c') {ret = VALUE_16(gb->registers[GB_REGISTER_BC]); goto exit;}
|
||||||
case 'd': if (string[1] == 'e') {ret = VALUE_16(gb->de); goto exit;}
|
case 'd': if (string[1] == 'e') {ret = VALUE_16(gb->registers[GB_REGISTER_DE]); goto exit;}
|
||||||
case 'h': if (string[1] == 'l') {ret = VALUE_16(gb->hl); goto exit;}
|
case 'h': if (string[1] == 'l') {ret = VALUE_16(gb->registers[GB_REGISTER_HL]); goto exit;}
|
||||||
case 's': if (string[1] == 'p') {ret = VALUE_16(gb->sp); goto exit;}
|
case 's': if (string[1] == 'p') {ret = VALUE_16(gb->registers[GB_REGISTER_SP]); goto exit;}
|
||||||
case 'p': if (string[1] == 'c') {ret = (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}; goto exit;}
|
case 'p': if (string[1] == 'c') {ret = (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}; goto exit;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -679,7 +689,6 @@ exit:
|
|||||||
|
|
||||||
struct debugger_command_s;
|
struct debugger_command_s;
|
||||||
typedef bool debugger_command_imp_t(GB_gameboy_t *gb, char *arguments, char *modifiers, const struct debugger_command_s *command);
|
typedef bool debugger_command_imp_t(GB_gameboy_t *gb, char *arguments, char *modifiers, const struct debugger_command_s *command);
|
||||||
typedef char *debugger_completer_imp_t(GB_gameboy_t *gb, const char *string, uintptr_t *context);
|
|
||||||
|
|
||||||
typedef struct debugger_command_s {
|
typedef struct debugger_command_s {
|
||||||
const char *command;
|
const char *command;
|
||||||
@ -688,8 +697,6 @@ typedef struct debugger_command_s {
|
|||||||
const char *help_string; // Null if should not appear in help
|
const char *help_string; // Null if should not appear in help
|
||||||
const char *arguments_format; // For usage message
|
const char *arguments_format; // For usage message
|
||||||
const char *modifiers_format; // For usage message
|
const char *modifiers_format; // For usage message
|
||||||
debugger_completer_imp_t *argument_completer;
|
|
||||||
debugger_completer_imp_t *modifiers_completer;
|
|
||||||
} debugger_command_t;
|
} debugger_command_t;
|
||||||
|
|
||||||
static const char *lstrip(const char *str)
|
static const char *lstrip(const char *str)
|
||||||
@ -811,50 +818,20 @@ static bool registers(GB_gameboy_t *gb, char *arguments, char *modifiers, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GB_log(gb, "AF = $%04x (%c%c%c%c)\n", gb->af, /* AF can't really be an address */
|
GB_log(gb, "AF = $%04x (%c%c%c%c)\n", gb->registers[GB_REGISTER_AF], /* AF can't really be an address */
|
||||||
(gb->f & GB_CARRY_FLAG)? 'C' : '-',
|
(gb->f & GB_CARRY_FLAG)? 'C' : '-',
|
||||||
(gb->f & GB_HALF_CARRY_FLAG)? 'H' : '-',
|
(gb->f & GB_HALF_CARRY_FLAG)? 'H' : '-',
|
||||||
(gb->f & GB_SUBTRACT_FLAG)? 'N' : '-',
|
(gb->f & GB_SUBTRACT_FLAG)? 'N' : '-',
|
||||||
(gb->f & GB_ZERO_FLAG)? 'Z' : '-');
|
(gb->f & GB_ZERO_FLAG)? 'Z' : '-');
|
||||||
GB_log(gb, "BC = %s\n", value_to_string(gb, gb->bc, false));
|
GB_log(gb, "BC = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_BC], false));
|
||||||
GB_log(gb, "DE = %s\n", value_to_string(gb, gb->de, false));
|
GB_log(gb, "DE = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_DE], false));
|
||||||
GB_log(gb, "HL = %s\n", value_to_string(gb, gb->hl, false));
|
GB_log(gb, "HL = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_HL], false));
|
||||||
GB_log(gb, "SP = %s\n", value_to_string(gb, gb->sp, false));
|
GB_log(gb, "SP = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_SP], false));
|
||||||
GB_log(gb, "PC = %s\n", value_to_string(gb, gb->pc, false));
|
GB_log(gb, "PC = %s\n", value_to_string(gb, gb->pc, false));
|
||||||
GB_log(gb, "IME = %s\n", gb->ime? "Enabled" : "Disabled");
|
GB_log(gb, "IME = %s\n", gb->ime? "Enabled" : "Disabled");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *on_off_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
|
|
||||||
{
|
|
||||||
size_t length = strlen(string);
|
|
||||||
const char *suggestions[] = {"on", "off"};
|
|
||||||
while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
|
|
||||||
if (memcmp(string, suggestions[*context], length) == 0) {
|
|
||||||
return strdup(suggestions[(*context)++] + length);
|
|
||||||
}
|
|
||||||
(*context)++;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enable or disable software breakpoints */
|
|
||||||
static bool softbreak(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
|
||||||
{
|
|
||||||
NO_MODIFIERS
|
|
||||||
if (strcmp(lstrip(arguments), "on") == 0 || !strlen(lstrip(arguments))) {
|
|
||||||
gb->has_software_breakpoints = true;
|
|
||||||
}
|
|
||||||
else if (strcmp(lstrip(arguments), "off") == 0) {
|
|
||||||
gb->has_software_breakpoints = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
print_usage(gb, command);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find the index of the closest breakpoint equal or greater to addr */
|
/* Find the index of the closest breakpoint equal or greater to addr */
|
||||||
static uint16_t find_breakpoint(GB_gameboy_t *gb, value_t addr)
|
static uint16_t find_breakpoint(GB_gameboy_t *gb, value_t addr)
|
||||||
{
|
{
|
||||||
@ -879,65 +856,6 @@ static uint16_t find_breakpoint(GB_gameboy_t *gb, value_t addr)
|
|||||||
return (uint16_t) min;
|
return (uint16_t) min;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool is_legal_symbol_char(char c)
|
|
||||||
{
|
|
||||||
if (c >= '0' && c <= '9') return true;
|
|
||||||
if (c >= 'A' && c <= 'Z') return true;
|
|
||||||
if (c >= 'a' && c <= 'z') return true;
|
|
||||||
if (c == '_') return true;
|
|
||||||
if (c == '.') return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *symbol_completer(GB_gameboy_t *gb, const char *string, uintptr_t *_context)
|
|
||||||
{
|
|
||||||
const char *symbol_prefix = string;
|
|
||||||
while (*string) {
|
|
||||||
if (!is_legal_symbol_char(*string)) {
|
|
||||||
symbol_prefix = string + 1;
|
|
||||||
}
|
|
||||||
string++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*symbol_prefix == '$') {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct {
|
|
||||||
uint16_t bank;
|
|
||||||
uint32_t symbol;
|
|
||||||
} *context = (void *)_context;
|
|
||||||
|
|
||||||
|
|
||||||
size_t length = strlen(symbol_prefix);
|
|
||||||
while (context->bank < 0x200) {
|
|
||||||
if (gb->bank_symbols[context->bank] == NULL ||
|
|
||||||
context->symbol >= gb->bank_symbols[context->bank]->n_symbols) {
|
|
||||||
context->bank++;
|
|
||||||
context->symbol = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const char *candidate = gb->bank_symbols[context->bank]->symbols[context->symbol++].name;
|
|
||||||
if (memcmp(symbol_prefix, candidate, length) == 0) {
|
|
||||||
return strdup(candidate + length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *j_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
|
|
||||||
{
|
|
||||||
size_t length = strlen(string);
|
|
||||||
const char *suggestions[] = {"j"};
|
|
||||||
while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
|
|
||||||
if (memcmp(string, suggestions[*context], length) == 0) {
|
|
||||||
return strdup(suggestions[(*context)++] + length);
|
|
||||||
}
|
|
||||||
(*context)++;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||||
{
|
{
|
||||||
bool is_jump_to = true;
|
bool is_jump_to = true;
|
||||||
@ -1105,19 +1023,6 @@ static uint16_t find_watchpoint(GB_gameboy_t *gb, value_t addr)
|
|||||||
return (uint16_t) min;
|
return (uint16_t) min;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *rw_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
|
|
||||||
{
|
|
||||||
size_t length = strlen(string);
|
|
||||||
const char *suggestions[] = {"r", "rw", "w"};
|
|
||||||
while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
|
|
||||||
if (memcmp(string, suggestions[*context], length) == 0) {
|
|
||||||
return strdup(suggestions[(*context)++] + length);
|
|
||||||
}
|
|
||||||
(*context)++;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool watch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
static bool watch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||||
{
|
{
|
||||||
if (strlen(lstrip(arguments)) == 0) {
|
if (strlen(lstrip(arguments)) == 0) {
|
||||||
@ -1355,19 +1260,6 @@ static bool should_break(GB_gameboy_t *gb, uint16_t addr, bool jump_to)
|
|||||||
return _should_break(gb, full_addr, jump_to);
|
return _should_break(gb, full_addr, jump_to);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *format_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
|
|
||||||
{
|
|
||||||
size_t length = strlen(string);
|
|
||||||
const char *suggestions[] = {"a", "b", "d", "o", "x"};
|
|
||||||
while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
|
|
||||||
if (memcmp(string, suggestions[*context], length) == 0) {
|
|
||||||
return strdup(suggestions[(*context)++] + length);
|
|
||||||
}
|
|
||||||
(*context)++;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||||
{
|
{
|
||||||
if (strlen(lstrip(arguments)) == 0) {
|
if (strlen(lstrip(arguments)) == 0) {
|
||||||
@ -1451,7 +1343,7 @@ static bool examine(GB_gameboy_t *gb, char *arguments, char *modifiers, const de
|
|||||||
while (count) {
|
while (count) {
|
||||||
GB_log(gb, "%02x:%04x: ", addr.bank, addr.value);
|
GB_log(gb, "%02x:%04x: ", addr.bank, addr.value);
|
||||||
for (unsigned i = 0; i < 16 && count; i++) {
|
for (unsigned i = 0; i < 16 && count; i++) {
|
||||||
GB_log(gb, "%02x ", GB_safe_read_memory(gb, addr.value + i));
|
GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i));
|
||||||
count--;
|
count--;
|
||||||
}
|
}
|
||||||
addr.value += 16;
|
addr.value += 16;
|
||||||
@ -1464,7 +1356,7 @@ static bool examine(GB_gameboy_t *gb, char *arguments, char *modifiers, const de
|
|||||||
while (count) {
|
while (count) {
|
||||||
GB_log(gb, "%04x: ", addr.value);
|
GB_log(gb, "%04x: ", addr.value);
|
||||||
for (unsigned i = 0; i < 16 && count; i++) {
|
for (unsigned i = 0; i < 16 && count; i++) {
|
||||||
GB_log(gb, "%02x ", GB_safe_read_memory(gb, addr.value + i));
|
GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i));
|
||||||
count--;
|
count--;
|
||||||
}
|
}
|
||||||
addr.value += 16;
|
addr.value += 16;
|
||||||
@ -1523,9 +1415,7 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
const GB_cartridge_t *cartridge = gb->cartridge_type;
|
const GB_cartridge_t *cartridge = gb->cartridge_type;
|
||||||
|
|
||||||
if (cartridge->has_ram) {
|
if (cartridge->has_ram) {
|
||||||
bool has_battery = gb->cartridge_type->has_battery &&
|
GB_log(gb, "Cartrdige includes%s RAM: $%x bytes\n", cartridge->has_battery? " battery-backed": "", gb->mbc_ram_size);
|
||||||
(gb->cartridge_type->mbc_type != GB_TPP1 || (gb->rom[0x153] & 8));
|
|
||||||
GB_log(gb, "Cartridge includes%s RAM: $%x bytes\n", has_battery? " battery-backed": "", gb->mbc_ram_size);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GB_log(gb, "No cartridge RAM\n");
|
GB_log(gb, "No cartridge RAM\n");
|
||||||
@ -1541,7 +1431,6 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
[GB_MBC2] = "MBC2",
|
[GB_MBC2] = "MBC2",
|
||||||
[GB_MBC3] = "MBC3",
|
[GB_MBC3] = "MBC3",
|
||||||
[GB_MBC5] = "MBC5",
|
[GB_MBC5] = "MBC5",
|
||||||
[GB_MBC7] = "MBC7",
|
|
||||||
[GB_HUC1] = "HUC-1",
|
[GB_HUC1] = "HUC-1",
|
||||||
[GB_HUC3] = "HUC-3",
|
[GB_HUC3] = "HUC-3",
|
||||||
};
|
};
|
||||||
@ -1551,7 +1440,7 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
if (cartridge->has_ram) {
|
if (cartridge->has_ram) {
|
||||||
GB_log(gb, "Current mapped RAM bank: %x\n", gb->mbc_ram_bank);
|
GB_log(gb, "Current mapped RAM bank: %x\n", gb->mbc_ram_bank);
|
||||||
if (gb->cartridge_type->mbc_type != GB_HUC1) {
|
if (gb->cartridge_type->mbc_type != GB_HUC1) {
|
||||||
GB_log(gb, "RAM is currently %s\n", gb->mbc_ram_enable? "enabled" : "disabled");
|
GB_log(gb, "RAM is curently %s\n", gb->mbc_ram_enable? "enabled" : "disabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_STANDARD_MBC1_WIRING) {
|
if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_STANDARD_MBC1_WIRING) {
|
||||||
@ -1568,8 +1457,7 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
GB_log(gb, "No MBC\n");
|
GB_log(gb, "No MBC\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->cartridge_type->has_rumble &&
|
if (cartridge->has_rumble) {
|
||||||
(gb->cartridge_type->mbc_type != GB_TPP1 || (gb->rom[0x153] & 1))) {
|
|
||||||
GB_log(gb, "Cart contains a Rumble Pak\n");
|
GB_log(gb, "Cart contains a Rumble Pak\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1607,12 +1495,8 @@ static bool ticks(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_log(gb, "T-cycles: %llu\n", (unsigned long long)gb->debugger_ticks);
|
GB_log(gb, "Ticks: %llu. (Resetting)\n", (unsigned long long)gb->debugger_ticks);
|
||||||
GB_log(gb, "M-cycles: %llu\n", (unsigned long long)gb->debugger_ticks / 4);
|
|
||||||
GB_log(gb, "Absolute 8MHz ticks: %llu\n", (unsigned long long)gb->absolute_debugger_ticks);
|
|
||||||
GB_log(gb, "Tick count reset.\n");
|
|
||||||
gb->debugger_ticks = 0;
|
gb->debugger_ticks = 0;
|
||||||
gb->absolute_debugger_ticks = 0;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1639,9 +1523,9 @@ static bool palettes(GB_gameboy_t *gb, char *arguments, char *modifiers, const d
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_log(gb, "Object palettes: \n");
|
GB_log(gb, "Sprites palettes: \n");
|
||||||
for (unsigned i = 0; i < 32; i++) {
|
for (unsigned i = 0; i < 32; i++) {
|
||||||
GB_log(gb, "%04x ", ((uint16_t *)&gb->object_palettes_data)[i]);
|
GB_log(gb, "%04x ", ((uint16_t *)&gb->sprite_palettes_data)[i]);
|
||||||
if (i % 4 == 3) {
|
if (i % 4 == 3) {
|
||||||
GB_log(gb, "\n");
|
GB_log(gb, "\n");
|
||||||
}
|
}
|
||||||
@ -1659,7 +1543,7 @@ static bool lcd(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
}
|
}
|
||||||
GB_log(gb, "LCDC:\n");
|
GB_log(gb, "LCDC:\n");
|
||||||
GB_log(gb, " LCD enabled: %s\n",(gb->io_registers[GB_IO_LCDC] & 128)? "Enabled" : "Disabled");
|
GB_log(gb, " LCD enabled: %s\n",(gb->io_registers[GB_IO_LCDC] & 128)? "Enabled" : "Disabled");
|
||||||
GB_log(gb, " %s: %s\n", (gb->cgb_mode? "Object priority flags" : "Background and Window"),
|
GB_log(gb, " %s: %s\n", (gb->cgb_mode? "Sprite priority flags" : "Background and Window"),
|
||||||
(gb->io_registers[GB_IO_LCDC] & 1)? "Enabled" : "Disabled");
|
(gb->io_registers[GB_IO_LCDC] & 1)? "Enabled" : "Disabled");
|
||||||
GB_log(gb, " Objects: %s\n", (gb->io_registers[GB_IO_LCDC] & 2)? "Enabled" : "Disabled");
|
GB_log(gb, " Objects: %s\n", (gb->io_registers[GB_IO_LCDC] & 2)? "Enabled" : "Disabled");
|
||||||
GB_log(gb, " Object size: %s\n", (gb->io_registers[GB_IO_LCDC] & 4)? "8x16" : "8x8");
|
GB_log(gb, " Object size: %s\n", (gb->io_registers[GB_IO_LCDC] & 4)? "8x16" : "8x8");
|
||||||
@ -1772,21 +1656,14 @@ static bool apu(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
GB_log(gb, " Duty cycle %s%% (%s), current index %u/8%s\n",
|
GB_log(gb, " Duty cycle %s%% (%s), current index %u/8%s\n",
|
||||||
duty > 3? "" : (const char *[]){"12.5", " 25", " 50", " 75"}[duty],
|
duty > 3? "" : (const char *[]){"12.5", " 25", " 50", " 75"}[duty],
|
||||||
duty > 3? "" : (const char *[]){"_______-", "-______-", "-____---", "_------_"}[duty],
|
duty > 3? "" : (const char *[]){"_______-", "-______-", "-____---", "_------_"}[duty],
|
||||||
gb->apu.square_channels[channel].current_sample_index,
|
gb->apu.square_channels[channel].current_sample_index & 0x7f,
|
||||||
gb->apu.square_channels[channel].sample_surpressed ? " (suppressed)" : "");
|
gb->apu.square_channels[channel].current_sample_index >> 7 ? " (suppressed)" : "");
|
||||||
|
|
||||||
if (channel == GB_SQUARE_1) {
|
if (channel == GB_SQUARE_1) {
|
||||||
GB_log(gb, " Frequency sweep %s and %s\n",
|
GB_log(gb, " Frequency sweep %s and %s (next in %u APU ticks)\n",
|
||||||
((gb->io_registers[GB_IO_NR10] & 0x7) && (gb->io_registers[GB_IO_NR10] & 0x70))? "active" : "inactive",
|
gb->apu.sweep_enabled? "active" : "inactive",
|
||||||
(gb->io_registers[GB_IO_NR10] & 0x8) ? "decreasing" : "increasing");
|
gb->apu.sweep_decreasing? "decreasing" : "increasing",
|
||||||
if (gb->apu.square_sweep_calculate_countdown) {
|
gb->apu.square_sweep_calculate_countdown);
|
||||||
GB_log(gb, " On going frequency calculation will be ready in %u APU ticks\n",
|
|
||||||
gb->apu.square_sweep_calculate_countdown);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_log(gb, " Shadow frequency register: 0x%03x\n", gb->apu.shadow_sweep_sample_length);
|
|
||||||
GB_log(gb, " Sweep addend register: 0x%03x\n", gb->apu.sweep_length_addend);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->apu.square_channels[channel].length_enabled) {
|
if (gb->apu.square_channels[channel].length_enabled) {
|
||||||
@ -1798,9 +1675,8 @@ static bool apu(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
|
|
||||||
GB_log(gb, "\nCH3:\n");
|
GB_log(gb, "\nCH3:\n");
|
||||||
GB_log(gb, " Wave:");
|
GB_log(gb, " Wave:");
|
||||||
for (uint8_t i = 0; i < 16; i++) {
|
for (uint8_t i = 0; i < 32; i++) {
|
||||||
GB_log(gb, "%s%X", i % 2? "" : " ", gb->io_registers[GB_IO_WAV_START + i] >> 4);
|
GB_log(gb, "%s%X", i%4?"":" ", gb->apu.wave_channel.wave_form[i]);
|
||||||
GB_log(gb, "%X", gb->io_registers[GB_IO_WAV_START + i] & 0xF);
|
|
||||||
}
|
}
|
||||||
GB_log(gb, "\n");
|
GB_log(gb, "\n");
|
||||||
GB_log(gb, " Current position: %u\n", gb->apu.wave_channel.current_sample_index);
|
GB_log(gb, " Current position: %u\n", gb->apu.wave_channel.current_sample_index);
|
||||||
@ -1820,10 +1696,10 @@ static bool apu(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
|
|
||||||
|
|
||||||
GB_log(gb, "\nCH4:\n");
|
GB_log(gb, "\nCH4:\n");
|
||||||
GB_log(gb, " Current volume: %u, current internal counter: 0x%04x (next increase in %u ticks)\n",
|
GB_log(gb, " Current volume: %u, current sample length: %u APU ticks (next in %u ticks)\n",
|
||||||
gb->apu.noise_channel.current_volume,
|
gb->apu.noise_channel.current_volume,
|
||||||
gb->apu.noise_channel.counter,
|
gb->apu.noise_channel.sample_length * 4 + 3,
|
||||||
gb->apu.noise_channel.counter_countdown);
|
gb->apu.noise_channel.sample_countdown);
|
||||||
|
|
||||||
GB_log(gb, " %u 256 Hz ticks till next volume %screase (out of %u)\n",
|
GB_log(gb, " %u 256 Hz ticks till next volume %screase (out of %u)\n",
|
||||||
gb->apu.noise_channel.volume_countdown,
|
gb->apu.noise_channel.volume_countdown,
|
||||||
@ -1847,19 +1723,6 @@ static bool apu(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *wave_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
|
|
||||||
{
|
|
||||||
size_t length = strlen(string);
|
|
||||||
const char *suggestions[] = {"c", "f", "l"};
|
|
||||||
while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
|
|
||||||
if (memcmp(string, suggestions[*context], length) == 0) {
|
|
||||||
return strdup(suggestions[(*context)++] + length);
|
|
||||||
}
|
|
||||||
(*context)++;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool wave(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
static bool wave(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||||
{
|
{
|
||||||
if (strlen(lstrip(arguments)) || (modifiers && !strchr("fcl", modifiers[0]))) {
|
if (strlen(lstrip(arguments)) || (modifiers && !strchr("fcl", modifiers[0]))) {
|
||||||
@ -1882,14 +1745,11 @@ static bool wave(GB_gameboy_t *gb, char *arguments, char *modifiers, const debug
|
|||||||
|
|
||||||
for (int8_t cur_val = 0xf & mask; cur_val >= 0; cur_val -= shift_amount) {
|
for (int8_t cur_val = 0xf & mask; cur_val >= 0; cur_val -= shift_amount) {
|
||||||
for (uint8_t i = 0; i < 32; i++) {
|
for (uint8_t i = 0; i < 32; i++) {
|
||||||
uint8_t sample = i & 1?
|
if ((gb->apu.wave_channel.wave_form[i] & mask) == cur_val) {
|
||||||
(gb->io_registers[GB_IO_WAV_START + i / 2] & 0xF) :
|
GB_log(gb, "%X", gb->apu.wave_channel.wave_form[i]);
|
||||||
(gb->io_registers[GB_IO_WAV_START + i / 2] >> 4);
|
|
||||||
if ((sample & mask) == cur_val) {
|
|
||||||
GB_log(gb, "%X", sample);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GB_log(gb, "%c", i % 4 == 2 ? '-' : ' ');
|
GB_log(gb, "%c", i%4 == 2 ? '-' : ' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GB_log(gb, "\n");
|
GB_log(gb, "\n");
|
||||||
@ -1898,29 +1758,6 @@ static bool wave(GB_gameboy_t *gb, char *arguments, char *modifiers, const debug
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool undo(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
|
||||||
{
|
|
||||||
NO_MODIFIERS
|
|
||||||
if (strlen(lstrip(arguments))) {
|
|
||||||
print_usage(gb, command);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gb->undo_label) {
|
|
||||||
GB_log(gb, "No undo state available\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
uint16_t pc = gb->pc;
|
|
||||||
GB_load_state_from_buffer(gb, gb->undo_state, GB_get_save_state_size_no_bess(gb));
|
|
||||||
GB_log(gb, "Reverted a \"%s\" command.\n", gb->undo_label);
|
|
||||||
if (pc != gb->pc) {
|
|
||||||
GB_cpu_disassemble(gb, gb->pc, 5);
|
|
||||||
}
|
|
||||||
gb->undo_label = NULL;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool help(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command);
|
static bool help(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command);
|
||||||
|
|
||||||
#define HELP_NEWLINE "\n "
|
#define HELP_NEWLINE "\n "
|
||||||
@ -1931,10 +1768,9 @@ static const debugger_command_t commands[] = {
|
|||||||
{"next", 1, next, "Run the next instruction, skipping over function calls"},
|
{"next", 1, next, "Run the next instruction, skipping over function calls"},
|
||||||
{"step", 1, step, "Run the next instruction, stepping into function calls"},
|
{"step", 1, step, "Run the next instruction, stepping into function calls"},
|
||||||
{"finish", 1, finish, "Run until the current function returns"},
|
{"finish", 1, finish, "Run until the current function returns"},
|
||||||
{"undo", 1, undo, "Reverts the last command"},
|
|
||||||
{"backtrace", 2, backtrace, "Displays the current call stack"},
|
{"backtrace", 2, backtrace, "Displays the current call stack"},
|
||||||
{"bt", 2, }, /* Alias */
|
{"bt", 2, }, /* Alias */
|
||||||
{"sld", 3, stack_leak_detection, "Like finish, but stops if a stack leak is detected"},
|
{"sld", 3, stack_leak_detection, "Like finish, but stops if a stack leak is detected (Experimental)"},
|
||||||
{"ticks", 2, ticks, "Displays the number of CPU ticks since the last time 'ticks' was" HELP_NEWLINE
|
{"ticks", 2, ticks, "Displays the number of CPU ticks since the last time 'ticks' was" HELP_NEWLINE
|
||||||
"used"},
|
"used"},
|
||||||
{"registers", 1, registers, "Print values of processor registers and other important registers"},
|
{"registers", 1, registers, "Print values of processor registers and other important registers"},
|
||||||
@ -1943,33 +1779,29 @@ static const debugger_command_t commands[] = {
|
|||||||
{"apu", 3, apu, "Displays information about the current state of the audio chip"},
|
{"apu", 3, apu, "Displays information about the current state of the audio chip"},
|
||||||
{"wave", 3, wave, "Prints a visual representation of the wave RAM." HELP_NEWLINE
|
{"wave", 3, wave, "Prints a visual representation of the wave RAM." HELP_NEWLINE
|
||||||
"Modifiers can be used for a (f)ull print (the default)," HELP_NEWLINE
|
"Modifiers can be used for a (f)ull print (the default)," HELP_NEWLINE
|
||||||
"a more (c)ompact one, or a one-(l)iner", "", "(f|c|l)", .modifiers_completer = wave_completer},
|
"a more (c)ompact one, or a one-(l)iner", "", "(f|c|l)"},
|
||||||
{"lcd", 3, lcd, "Displays information about the current state of the LCD controller"},
|
{"lcd", 3, lcd, "Displays information about the current state of the LCD controller"},
|
||||||
{"palettes", 3, palettes, "Displays the current CGB palettes"},
|
{"palettes", 3, palettes, "Displays the current CGB palettes"},
|
||||||
{"softbreak", 2, softbreak, "Enables or disables software breakpoints", "(on|off)", .argument_completer = on_off_completer},
|
|
||||||
{"breakpoint", 1, breakpoint, "Add a new breakpoint at the specified address/expression" HELP_NEWLINE
|
{"breakpoint", 1, breakpoint, "Add a new breakpoint at the specified address/expression" HELP_NEWLINE
|
||||||
"Can also modify the condition of existing breakpoints." HELP_NEWLINE
|
"Can also modify the condition of existing breakpoints." HELP_NEWLINE
|
||||||
"If the j modifier is used, the breakpoint will occur just before" HELP_NEWLINE
|
"If the j modifier is used, the breakpoint will occur just before" HELP_NEWLINE
|
||||||
"jumping to the target.",
|
"jumping to the target.",
|
||||||
"<expression>[ if <condition expression>]", "j",
|
"<expression>[ if <condition expression>]", "j"},
|
||||||
.argument_completer = symbol_completer, .modifiers_completer = j_completer},
|
{"delete", 2, delete, "Delete a breakpoint by its address, or all breakpoints", "[<expression>]"},
|
||||||
{"delete", 2, delete, "Delete a breakpoint by its address, or all breakpoints", "[<expression>]", .argument_completer = symbol_completer},
|
|
||||||
{"watch", 1, watch, "Add a new watchpoint at the specified address/expression." HELP_NEWLINE
|
{"watch", 1, watch, "Add a new watchpoint at the specified address/expression." HELP_NEWLINE
|
||||||
"Can also modify the condition and type of existing watchpoints." HELP_NEWLINE
|
"Can also modify the condition and type of existing watchpoints." HELP_NEWLINE
|
||||||
"Default watchpoint type is write-only.",
|
"Default watchpoint type is write-only.",
|
||||||
"<expression>[ if <condition expression>]", "(r|w|rw)",
|
"<expression>[ if <condition expression>]", "(r|w|rw)"},
|
||||||
.argument_completer = symbol_completer, .modifiers_completer = rw_completer
|
{"unwatch", 3, unwatch, "Delete a watchpoint by its address, or all watchpoints", "[<expression>]"},
|
||||||
},
|
|
||||||
{"unwatch", 3, unwatch, "Delete a watchpoint by its address, or all watchpoints", "[<expression>]", .argument_completer = symbol_completer},
|
|
||||||
{"list", 1, list, "List all set breakpoints and watchpoints"},
|
{"list", 1, list, "List all set breakpoints and watchpoints"},
|
||||||
{"print", 1, print, "Evaluate and print an expression" HELP_NEWLINE
|
{"print", 1, print, "Evaluate and print an expression" HELP_NEWLINE
|
||||||
"Use modifier to format as an address (a, default) or as a number in" HELP_NEWLINE
|
"Use modifier to format as an address (a, default) or as a number in" HELP_NEWLINE
|
||||||
"decimal (d), hexadecimal (x), octal (o) or binary (b).",
|
"decimal (d), hexadecimal (x), octal (o) or binary (b).",
|
||||||
"<expression>", "format", .argument_completer = symbol_completer, .modifiers_completer = format_completer},
|
"<expression>", "format"},
|
||||||
{"eval", 2, }, /* Alias */
|
{"eval", 2, }, /* Alias */
|
||||||
{"examine", 2, examine, "Examine values at address", "<expression>", "count", .argument_completer = symbol_completer},
|
{"examine", 2, examine, "Examine values at address", "<expression>", "count"},
|
||||||
{"x", 1, }, /* Alias */
|
{"x", 1, }, /* Alias */
|
||||||
{"disassemble", 1, disassemble, "Disassemble instructions at address", "<expression>", "count", .argument_completer = symbol_completer},
|
{"disassemble", 1, disassemble, "Disassemble instructions at address", "<expression>", "count"},
|
||||||
|
|
||||||
|
|
||||||
{"help", 1, help, "List available commands or show help for the specified command", "[<command>]"},
|
{"help", 1, help, "List available commands or show help for the specified command", "[<command>]"},
|
||||||
@ -2044,7 +1876,7 @@ void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr)
|
|||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gb->sp_for_call_depth[gb->debug_call_depth] = gb->sp;
|
gb->sp_for_call_depth[gb->debug_call_depth] = gb->registers[GB_REGISTER_SP];
|
||||||
gb->addr_for_call_depth[gb->debug_call_depth] = gb->pc;
|
gb->addr_for_call_depth[gb->debug_call_depth] = gb->pc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2052,7 +1884,7 @@ void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr)
|
|||||||
if (gb->backtrace_size < sizeof(gb->backtrace_sps) / sizeof(gb->backtrace_sps[0])) {
|
if (gb->backtrace_size < sizeof(gb->backtrace_sps) / sizeof(gb->backtrace_sps[0])) {
|
||||||
|
|
||||||
while (gb->backtrace_size) {
|
while (gb->backtrace_size) {
|
||||||
if (gb->backtrace_sps[gb->backtrace_size - 1] < gb->sp) {
|
if (gb->backtrace_sps[gb->backtrace_size - 1] < gb->registers[GB_REGISTER_SP]) {
|
||||||
gb->backtrace_size--;
|
gb->backtrace_size--;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -2060,7 +1892,7 @@ void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->backtrace_sps[gb->backtrace_size] = gb->sp;
|
gb->backtrace_sps[gb->backtrace_size] = gb->registers[GB_REGISTER_SP];
|
||||||
gb->backtrace_returns[gb->backtrace_size].bank = bank_for_addr(gb, call_addr);
|
gb->backtrace_returns[gb->backtrace_size].bank = bank_for_addr(gb, call_addr);
|
||||||
gb->backtrace_returns[gb->backtrace_size].addr = call_addr;
|
gb->backtrace_returns[gb->backtrace_size].addr = call_addr;
|
||||||
gb->backtrace_size++;
|
gb->backtrace_size++;
|
||||||
@ -2081,9 +1913,9 @@ void GB_debugger_ret_hook(GB_gameboy_t *gb)
|
|||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (gb->sp != gb->sp_for_call_depth[gb->debug_call_depth]) {
|
if (gb->registers[GB_REGISTER_SP] != gb->sp_for_call_depth[gb->debug_call_depth]) {
|
||||||
GB_log(gb, "Stack leak detected for function %s!\n", value_to_string(gb, gb->addr_for_call_depth[gb->debug_call_depth], true));
|
GB_log(gb, "Stack leak detected for function %s!\n", value_to_string(gb, gb->addr_for_call_depth[gb->debug_call_depth], true));
|
||||||
GB_log(gb, "SP is $%04x, should be $%04x.\n", gb->sp,
|
GB_log(gb, "SP is $%04x, should be $%04x.\n", gb->registers[GB_REGISTER_SP],
|
||||||
gb->sp_for_call_depth[gb->debug_call_depth]);
|
gb->sp_for_call_depth[gb->debug_call_depth]);
|
||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
}
|
}
|
||||||
@ -2091,7 +1923,7 @@ void GB_debugger_ret_hook(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (gb->backtrace_size) {
|
while (gb->backtrace_size) {
|
||||||
if (gb->backtrace_sps[gb->backtrace_size - 1] <= gb->sp) {
|
if (gb->backtrace_sps[gb->backtrace_size - 1] <= gb->registers[GB_REGISTER_SP]) {
|
||||||
gb->backtrace_size--;
|
gb->backtrace_size--;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -2197,9 +2029,6 @@ bool GB_debugger_execute_command(GB_gameboy_t *gb, char *input)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_display_sync(gb);
|
|
||||||
GB_apu_run(gb, true);
|
|
||||||
|
|
||||||
char *command_string = input;
|
char *command_string = input;
|
||||||
char *arguments = strchr(input, ' ');
|
char *arguments = strchr(input, ' ');
|
||||||
if (arguments) {
|
if (arguments) {
|
||||||
@ -2220,30 +2049,7 @@ bool GB_debugger_execute_command(GB_gameboy_t *gb, char *input)
|
|||||||
|
|
||||||
const debugger_command_t *command = find_command(command_string);
|
const debugger_command_t *command = find_command(command_string);
|
||||||
if (command) {
|
if (command) {
|
||||||
uint8_t *old_state = malloc(GB_get_save_state_size_no_bess(gb));
|
return command->implementation(gb, arguments, modifiers, command);
|
||||||
GB_save_state_to_buffer_no_bess(gb, old_state);
|
|
||||||
bool ret = command->implementation(gb, arguments, modifiers, command);
|
|
||||||
if (!ret) { // Command continues, save state in any case
|
|
||||||
free(gb->undo_state);
|
|
||||||
gb->undo_state = old_state;
|
|
||||||
gb->undo_label = command->command;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
uint8_t *new_state = malloc(GB_get_save_state_size_no_bess(gb));
|
|
||||||
GB_save_state_to_buffer_no_bess(gb, new_state);
|
|
||||||
if (memcmp(new_state, old_state, GB_get_save_state_size_no_bess(gb)) != 0) {
|
|
||||||
// State changed, save the old state as the new undo state
|
|
||||||
free(gb->undo_state);
|
|
||||||
gb->undo_state = old_state;
|
|
||||||
gb->undo_label = command->command;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Nothing changed, just free the old state
|
|
||||||
free(old_state);
|
|
||||||
}
|
|
||||||
free(new_state);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GB_log(gb, "%s: no such command.\n", command_string);
|
GB_log(gb, "%s: no such command.\n", command_string);
|
||||||
@ -2251,62 +2057,6 @@ bool GB_debugger_execute_command(GB_gameboy_t *gb, char *input)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char *GB_debugger_complete_substring(GB_gameboy_t *gb, char *input, uintptr_t *context)
|
|
||||||
{
|
|
||||||
char *command_string = input;
|
|
||||||
char *arguments = strchr(input, ' ');
|
|
||||||
if (arguments) {
|
|
||||||
/* Actually "split" the string. */
|
|
||||||
arguments[0] = 0;
|
|
||||||
arguments++;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *modifiers = strchr(command_string, '/');
|
|
||||||
if (modifiers) {
|
|
||||||
/* Actually "split" the string. */
|
|
||||||
modifiers[0] = 0;
|
|
||||||
modifiers++;
|
|
||||||
}
|
|
||||||
|
|
||||||
const debugger_command_t *command = find_command(command_string);
|
|
||||||
if (command && command->implementation == help && arguments) {
|
|
||||||
command_string = arguments;
|
|
||||||
arguments = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No commands and no modifiers, complete the command */
|
|
||||||
if (!arguments && !modifiers) {
|
|
||||||
size_t length = strlen(command_string);
|
|
||||||
if (*context >= sizeof(commands) / sizeof(commands[0])) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
for (const debugger_command_t *command = &commands[*context]; command->command; command++) {
|
|
||||||
(*context)++;
|
|
||||||
if (memcmp(command->command, command_string, length) == 0) { /* Is a substring? */
|
|
||||||
return strdup(command->command + length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command) {
|
|
||||||
if (arguments) {
|
|
||||||
if (command->argument_completer) {
|
|
||||||
return command->argument_completer(gb, arguments, context);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modifiers) {
|
|
||||||
if (command->modifiers_completer) {
|
|
||||||
return command->modifiers_completer(gb, modifiers, context);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
JUMP_TO_NONE,
|
JUMP_TO_NONE,
|
||||||
JUMP_TO_BREAK,
|
JUMP_TO_BREAK,
|
||||||
@ -2319,11 +2069,6 @@ void GB_debugger_run(GB_gameboy_t *gb)
|
|||||||
{
|
{
|
||||||
if (gb->debug_disable) return;
|
if (gb->debug_disable) return;
|
||||||
|
|
||||||
if (!gb->undo_state) {
|
|
||||||
gb->undo_state = malloc(GB_get_save_state_size_no_bess(gb));
|
|
||||||
GB_save_state_to_buffer_no_bess(gb, gb->undo_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *input = NULL;
|
char *input = NULL;
|
||||||
if (gb->debug_next_command && gb->debug_call_depth <= 0 && !gb->halted) {
|
if (gb->debug_next_command && gb->debug_call_depth <= 0 && !gb->halted) {
|
||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
@ -2369,9 +2114,9 @@ next_command:
|
|||||||
}
|
}
|
||||||
else if (jump_to_result == JUMP_TO_NONTRIVIAL) {
|
else if (jump_to_result == JUMP_TO_NONTRIVIAL) {
|
||||||
if (!gb->nontrivial_jump_state) {
|
if (!gb->nontrivial_jump_state) {
|
||||||
gb->nontrivial_jump_state = malloc(GB_get_save_state_size_no_bess(gb));
|
gb->nontrivial_jump_state = malloc(GB_get_save_state_size(gb));
|
||||||
}
|
}
|
||||||
GB_save_state_to_buffer_no_bess(gb, gb->nontrivial_jump_state);
|
GB_save_state_to_buffer(gb, gb->nontrivial_jump_state);
|
||||||
gb->non_trivial_jump_breakpoint_occured = false;
|
gb->non_trivial_jump_breakpoint_occured = false;
|
||||||
should_delete_state = false;
|
should_delete_state = false;
|
||||||
}
|
}
|
||||||
@ -2430,6 +2175,29 @@ void GB_debugger_add_symbol(GB_gameboy_t *gb, uint16_t bank, uint16_t address, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GB_debugger_load_symbol_file_from_buffer(GB_gameboy_t *gb, const char *buffer, size_t size)
|
||||||
|
{
|
||||||
|
const char *str_ptr = buffer;
|
||||||
|
unsigned length = 0;
|
||||||
|
|
||||||
|
while (str_ptr && size--) {
|
||||||
|
length++;
|
||||||
|
char c = (char) *str_ptr++;
|
||||||
|
|
||||||
|
if (c == '\n' || size == 0) {
|
||||||
|
const char *line = str_ptr - length--;
|
||||||
|
unsigned bank, address;
|
||||||
|
char symbol[length];
|
||||||
|
|
||||||
|
if (sscanf(line, "%x:%x %s", &bank, &address, symbol) == 3) {
|
||||||
|
GB_debugger_add_symbol(gb, bank, address, symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path)
|
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path)
|
||||||
{
|
{
|
||||||
FILE *f = fopen(path, "r");
|
FILE *f = fopen(path, "r");
|
||||||
@ -2545,7 +2313,7 @@ static bool is_in_trivial_memory(uint16_t addr)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef uint16_t opcode_address_getter_t(GB_gameboy_t *gb, uint8_t opcode);
|
typedef uint16_t GB_opcode_address_getter_t(GB_gameboy_t *gb, uint8_t opcode);
|
||||||
|
|
||||||
uint16_t trivial_1(GB_gameboy_t *gb, uint8_t opcode)
|
uint16_t trivial_1(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
@ -2571,13 +2339,13 @@ static bool condition_code(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
{
|
{
|
||||||
switch ((opcode >> 3) & 0x3) {
|
switch ((opcode >> 3) & 0x3) {
|
||||||
case 0:
|
case 0:
|
||||||
return !(gb->af & GB_ZERO_FLAG);
|
return !(gb->registers[GB_REGISTER_AF] & GB_ZERO_FLAG);
|
||||||
case 1:
|
case 1:
|
||||||
return (gb->af & GB_ZERO_FLAG);
|
return (gb->registers[GB_REGISTER_AF] & GB_ZERO_FLAG);
|
||||||
case 2:
|
case 2:
|
||||||
return !(gb->af & GB_CARRY_FLAG);
|
return !(gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG);
|
||||||
case 3:
|
case 3:
|
||||||
return (gb->af & GB_CARRY_FLAG);
|
return (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -2594,8 +2362,8 @@ static uint16_t jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
|
|
||||||
static uint16_t ret(GB_gameboy_t *gb, uint8_t opcode)
|
static uint16_t ret(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
return GB_read_memory(gb, gb->sp) |
|
return GB_read_memory(gb, gb->registers[GB_REGISTER_SP]) |
|
||||||
(GB_read_memory(gb, gb->sp + 1) << 8);
|
(GB_read_memory(gb, gb->registers[GB_REGISTER_SP] + 1) << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2635,7 +2403,7 @@ static uint16_t jp_hl(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
return gb->hl;
|
return gb->hl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static opcode_address_getter_t *opcodes[256] = {
|
static GB_opcode_address_getter_t *opcodes[256] = {
|
||||||
/* X0 X1 X2 X3 X4 X5 X6 X7 */
|
/* X0 X1 X2 X3 X4 X5 X6 X7 */
|
||||||
/* X8 X9 Xa Xb Xc Xd Xe Xf */
|
/* X8 X9 Xa Xb Xc Xd Xe Xf */
|
||||||
trivial_1, trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, /* 0X */
|
trivial_1, trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, /* 0X */
|
||||||
@ -2677,7 +2445,7 @@ static jump_to_return_t test_jump_to_breakpoints(GB_gameboy_t *gb, uint16_t *add
|
|||||||
if (!gb->has_jump_to_breakpoints) return JUMP_TO_NONE;
|
if (!gb->has_jump_to_breakpoints) return JUMP_TO_NONE;
|
||||||
|
|
||||||
if (!is_in_trivial_memory(gb->pc) || !is_in_trivial_memory(gb->pc + 2) ||
|
if (!is_in_trivial_memory(gb->pc) || !is_in_trivial_memory(gb->pc + 2) ||
|
||||||
!is_in_trivial_memory(gb->sp) || !is_in_trivial_memory(gb->sp + 1)) {
|
!is_in_trivial_memory(gb->registers[GB_REGISTER_SP]) || !is_in_trivial_memory(gb->registers[GB_REGISTER_SP] + 1)) {
|
||||||
return JUMP_TO_NONTRIVIAL;
|
return JUMP_TO_NONTRIVIAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2713,7 +2481,7 @@ static jump_to_return_t test_jump_to_breakpoints(GB_gameboy_t *gb, uint16_t *add
|
|||||||
return JUMP_TO_NONE;
|
return JUMP_TO_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
opcode_address_getter_t *getter = opcodes[opcode];
|
GB_opcode_address_getter_t *getter = opcodes[opcode];
|
||||||
if (!getter) {
|
if (!getter) {
|
||||||
gb->n_watchpoints = n_watchpoints;
|
gb->n_watchpoints = n_watchpoints;
|
||||||
return JUMP_TO_NONE;
|
return JUMP_TO_NONE;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#define debugger_h
|
#define debugger_h
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "defs.h"
|
#include "gb_struct_def.h"
|
||||||
#include "symbol_hash.h"
|
#include "symbol_hash.h"
|
||||||
|
|
||||||
|
|
||||||
@ -17,14 +17,14 @@
|
|||||||
#define GB_debugger_add_symbol(gb, bank, address, symbol) ((void)bank, (void)address, (void)symbol)
|
#define GB_debugger_add_symbol(gb, bank, address, symbol) ((void)bank, (void)address, (void)symbol)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
internal void GB_debugger_run(GB_gameboy_t *gb);
|
void GB_debugger_run(GB_gameboy_t *gb);
|
||||||
internal void GB_debugger_handle_async_commands(GB_gameboy_t *gb);
|
void GB_debugger_handle_async_commands(GB_gameboy_t *gb);
|
||||||
internal void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr);
|
void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr);
|
||||||
internal void GB_debugger_ret_hook(GB_gameboy_t *gb);
|
void GB_debugger_ret_hook(GB_gameboy_t *gb);
|
||||||
internal void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
||||||
internal void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr);
|
void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr);
|
||||||
internal const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr);
|
const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr);
|
||||||
internal void GB_debugger_add_symbol(GB_gameboy_t *gb, uint16_t bank, uint16_t address, const char *symbol);
|
void GB_debugger_add_symbol(GB_gameboy_t *gb, uint16_t bank, uint16_t address, const char *symbol);
|
||||||
#endif /* GB_DISABLE_DEBUGGER */
|
#endif /* GB_DISABLE_DEBUGGER */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -34,8 +34,8 @@ bool /* Returns true if debugger waits for more commands. Not relevant for non-G
|
|||||||
void
|
void
|
||||||
#endif
|
#endif
|
||||||
GB_debugger_execute_command(GB_gameboy_t *gb, char *input); /* Destroys input. */
|
GB_debugger_execute_command(GB_gameboy_t *gb, char *input); /* Destroys input. */
|
||||||
char *GB_debugger_complete_substring(GB_gameboy_t *gb, char *input, uintptr_t *context); /* Destroys input, result requires free */
|
|
||||||
|
|
||||||
|
void GB_debugger_load_symbol_file_from_buffer(GB_gameboy_t *gb, const char *buffer, size_t size);
|
||||||
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path);
|
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path);
|
||||||
const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr);
|
const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr);
|
||||||
bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank); /* result_bank is -1 if unused. */
|
bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank); /* result_bank is -1 if unused. */
|
||||||
|
46
Core/defs.h
@ -1,46 +0,0 @@
|
|||||||
#ifndef defs_h
|
|
||||||
#define defs_h
|
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
|
||||||
|
|
||||||
// "Keyword" definitions
|
|
||||||
#define likely(x) __builtin_expect((bool)(x), 1)
|
|
||||||
#define unlikely(x) __builtin_expect((bool)(x), 0)
|
|
||||||
|
|
||||||
#define internal __attribute__((visibility("internal")))
|
|
||||||
|
|
||||||
#if __clang__
|
|
||||||
#define unrolled _Pragma("unroll")
|
|
||||||
#elif __GNUC__ >= 8
|
|
||||||
#define unrolled _Pragma("GCC unroll 8")
|
|
||||||
#else
|
|
||||||
#define unrolled
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define unreachable() __builtin_unreachable();
|
|
||||||
#define nodefault default: unreachable()
|
|
||||||
|
|
||||||
#ifdef GB_BIG_ENDIAN
|
|
||||||
#define LE16(x) __builtin_bswap16(x)
|
|
||||||
#define LE32(x) __builtin_bswap32(x)
|
|
||||||
#define LE64(x) __builtin_bswap64(x)
|
|
||||||
#define BE16(x) (x)
|
|
||||||
#define BE32(x) (x)
|
|
||||||
#define BE64(x) (x)
|
|
||||||
#else
|
|
||||||
#define LE16(x) (x)
|
|
||||||
#define LE32(x) (x)
|
|
||||||
#define LE64(x) (x)
|
|
||||||
#define BE16(x) __builtin_bswap16(x)
|
|
||||||
#define BE32(x) __builtin_bswap32(x)
|
|
||||||
#define BE64(x) __builtin_bswap64(x)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)
|
|
||||||
#define __builtin_bswap16(x) ({ typeof(x) _x = (x); _x >> 8 | _x << 8; })
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct GB_gameboy_s;
|
|
||||||
typedef struct GB_gameboy_s GB_gameboy_t;
|
|
||||||
#endif
|
|
862
Core/display.c
@ -6,14 +6,13 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
internal void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force);
|
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles);
|
||||||
internal void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index);
|
void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index);
|
||||||
internal void GB_STAT_update(GB_gameboy_t *gb);
|
void GB_STAT_update(GB_gameboy_t *gb);
|
||||||
internal void GB_lcd_off(GB_gameboy_t *gb);
|
void GB_lcd_off(GB_gameboy_t *gb);
|
||||||
internal void GB_display_vblank(GB_gameboy_t *gb);
|
|
||||||
#define GB_display_sync(gb) GB_display_run(gb, 0, true)
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
GB_OBJECT_PRIORITY_UNDEFINED, // For save state compatibility
|
||||||
GB_OBJECT_PRIORITY_X,
|
GB_OBJECT_PRIORITY_X,
|
||||||
GB_OBJECT_PRIORITY_INDEX,
|
GB_OBJECT_PRIORITY_INDEX,
|
||||||
};
|
};
|
||||||
@ -52,21 +51,12 @@ typedef enum {
|
|||||||
GB_COLOR_CORRECTION_EMULATE_HARDWARE,
|
GB_COLOR_CORRECTION_EMULATE_HARDWARE,
|
||||||
GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS,
|
GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS,
|
||||||
GB_COLOR_CORRECTION_REDUCE_CONTRAST,
|
GB_COLOR_CORRECTION_REDUCE_CONTRAST,
|
||||||
GB_COLOR_CORRECTION_LOW_CONTRAST,
|
|
||||||
} GB_color_correction_mode_t;
|
} GB_color_correction_mode_t;
|
||||||
|
|
||||||
void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index);
|
void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index);
|
||||||
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);
|
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);
|
||||||
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *object_height);
|
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height);
|
||||||
uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color, bool for_border);
|
uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color, bool for_border);
|
||||||
void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode);
|
void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode);
|
||||||
void GB_set_light_temperature(GB_gameboy_t *gb, double temperature);
|
|
||||||
bool GB_is_odd_frame(GB_gameboy_t *gb);
|
bool GB_is_odd_frame(GB_gameboy_t *gb);
|
||||||
|
|
||||||
void GB_set_object_rendering_disabled(GB_gameboy_t *gb, bool disabled);
|
|
||||||
void GB_set_background_rendering_disabled(GB_gameboy_t *gb, bool disabled);
|
|
||||||
bool GB_is_object_rendering_disabled(GB_gameboy_t *gb);
|
|
||||||
bool GB_is_background_rendering_disabled(GB_gameboy_t *gb);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* display_h */
|
#endif /* display_h */
|
||||||
|
326
Core/gb.h
@ -3,10 +3,9 @@
|
|||||||
#define typeof __typeof__
|
#define typeof __typeof__
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdalign.h>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "defs.h"
|
#include "gb_struct_def.h"
|
||||||
#include "save_state.h"
|
#include "save_state.h"
|
||||||
|
|
||||||
#include "apu.h"
|
#include "apu.h"
|
||||||
@ -24,17 +23,26 @@
|
|||||||
#include "sgb.h"
|
#include "sgb.h"
|
||||||
#include "cheats.h"
|
#include "cheats.h"
|
||||||
#include "rumble.h"
|
#include "rumble.h"
|
||||||
#include "workboy.h"
|
|
||||||
#include "random.h"
|
|
||||||
|
|
||||||
#define GB_STRUCT_VERSION 14
|
#define GB_STRUCT_VERSION 13
|
||||||
|
|
||||||
#define GB_MODEL_FAMILY_MASK 0xF00
|
#define GB_MODEL_FAMILY_MASK 0xF00
|
||||||
#define GB_MODEL_DMG_FAMILY 0x000
|
#define GB_MODEL_DMG_FAMILY 0x000
|
||||||
#define GB_MODEL_MGB_FAMILY 0x100
|
#define GB_MODEL_MGB_FAMILY 0x100
|
||||||
#define GB_MODEL_CGB_FAMILY 0x200
|
#define GB_MODEL_CGB_FAMILY 0x200
|
||||||
#define GB_MODEL_PAL_BIT 0x40
|
#define GB_MODEL_PAL_BIT 0x1000
|
||||||
#define GB_MODEL_NO_SFC_BIT 0x80
|
#define GB_MODEL_NO_SFC_BIT 0x2000
|
||||||
|
|
||||||
|
#ifdef GB_INTERNAL
|
||||||
|
#if __clang__
|
||||||
|
#define UNROLL _Pragma("unroll")
|
||||||
|
#elif __GNUC__ >= 8
|
||||||
|
#define UNROLL _Pragma("GCC unroll 8")
|
||||||
|
#else
|
||||||
|
#define UNROLL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
#define GB_BIG_ENDIAN
|
#define GB_BIG_ENDIAN
|
||||||
@ -44,9 +52,8 @@
|
|||||||
#error Unable to detect endianess
|
#error Unable to detect endianess
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct GB_color_s {
|
struct {
|
||||||
uint8_t r, g, b;
|
uint8_t r, g, b;
|
||||||
} colors[5];
|
} colors[5];
|
||||||
} GB_palette_t;
|
} GB_palette_t;
|
||||||
@ -64,24 +71,9 @@ typedef union {
|
|||||||
uint8_t days;
|
uint8_t days;
|
||||||
uint8_t high;
|
uint8_t high;
|
||||||
};
|
};
|
||||||
struct {
|
|
||||||
uint8_t seconds;
|
|
||||||
uint8_t minutes;
|
|
||||||
uint8_t hours:5;
|
|
||||||
uint8_t weekday:3;
|
|
||||||
uint8_t weeks;
|
|
||||||
} tpp1;
|
|
||||||
uint8_t data[5];
|
uint8_t data[5];
|
||||||
} GB_rtc_time_t;
|
} GB_rtc_time_t;
|
||||||
|
|
||||||
typedef struct __attribute__((packed)) {
|
|
||||||
uint64_t last_rtc_second;
|
|
||||||
uint16_t minutes;
|
|
||||||
uint16_t days;
|
|
||||||
uint16_t alarm_minutes, alarm_days;
|
|
||||||
uint8_t alarm_enabled;
|
|
||||||
} GB_huc3_rtc_time_t;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
// GB_MODEL_DMG_0 = 0x000,
|
// GB_MODEL_DMG_0 = 0x000,
|
||||||
// GB_MODEL_DMG_A = 0x001,
|
// GB_MODEL_DMG_A = 0x001,
|
||||||
@ -93,14 +85,14 @@ typedef enum {
|
|||||||
GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
|
GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
|
||||||
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC,
|
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC,
|
||||||
GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT,
|
GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT,
|
||||||
GB_MODEL_MGB = 0x100,
|
// GB_MODEL_MGB = 0x100,
|
||||||
GB_MODEL_SGB2 = 0x101,
|
GB_MODEL_SGB2 = 0x101,
|
||||||
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
|
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
|
||||||
GB_MODEL_CGB_0 = 0x200,
|
// GB_MODEL_CGB_0 = 0x200,
|
||||||
GB_MODEL_CGB_A = 0x201,
|
// GB_MODEL_CGB_A = 0x201,
|
||||||
GB_MODEL_CGB_B = 0x202,
|
// GB_MODEL_CGB_B = 0x202,
|
||||||
GB_MODEL_CGB_C = 0x203,
|
GB_MODEL_CGB_C = 0x203,
|
||||||
GB_MODEL_CGB_D = 0x204,
|
// GB_MODEL_CGB_D = 0x204,
|
||||||
GB_MODEL_CGB_E = 0x205,
|
GB_MODEL_CGB_E = 0x205,
|
||||||
GB_MODEL_AGB = 0x206,
|
GB_MODEL_AGB = 0x206,
|
||||||
} GB_model_t;
|
} GB_model_t;
|
||||||
@ -111,7 +103,6 @@ enum {
|
|||||||
GB_REGISTER_DE,
|
GB_REGISTER_DE,
|
||||||
GB_REGISTER_HL,
|
GB_REGISTER_HL,
|
||||||
GB_REGISTER_SP,
|
GB_REGISTER_SP,
|
||||||
GB_REGISTER_PC,
|
|
||||||
GB_REGISTERS_16_BIT /* Count */
|
GB_REGISTERS_16_BIT /* Count */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -129,6 +120,8 @@ typedef enum {
|
|||||||
GB_BORDER_ALWAYS,
|
GB_BORDER_ALWAYS,
|
||||||
} GB_border_mode_t;
|
} GB_border_mode_t;
|
||||||
|
|
||||||
|
#define GB_MAX_IR_QUEUE 256
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
/* Joypad and Serial */
|
/* Joypad and Serial */
|
||||||
GB_IO_JOYP = 0x00, // Joypad (R/W)
|
GB_IO_JOYP = 0x00, // Joypad (R/W)
|
||||||
@ -190,7 +183,10 @@ enum {
|
|||||||
GB_IO_OBP1 = 0x49, // Object Palette 1 Data (R/W) - Non CGB Mode Only
|
GB_IO_OBP1 = 0x49, // Object Palette 1 Data (R/W) - Non CGB Mode Only
|
||||||
GB_IO_WY = 0x4a, // Window Y Position (R/W)
|
GB_IO_WY = 0x4a, // Window Y Position (R/W)
|
||||||
GB_IO_WX = 0x4b, // Window X Position minus 7 (R/W)
|
GB_IO_WX = 0x4b, // Window X Position minus 7 (R/W)
|
||||||
// Controls DMG mode and PGB mode
|
// Has some undocumented compatibility flags written at boot.
|
||||||
|
// Unfortunately it is not readable or writable after boot has finished, so research of this
|
||||||
|
// register is quite limited. The value written to this register, however, can be controlled
|
||||||
|
// in some cases.
|
||||||
GB_IO_KEY0 = 0x4c,
|
GB_IO_KEY0 = 0x4c,
|
||||||
|
|
||||||
/* General CGB features */
|
/* General CGB features */
|
||||||
@ -216,19 +212,20 @@ enum {
|
|||||||
/* CGB Paletts */
|
/* CGB Paletts */
|
||||||
GB_IO_BGPI = 0x68, // CGB Mode Only - Background Palette Index
|
GB_IO_BGPI = 0x68, // CGB Mode Only - Background Palette Index
|
||||||
GB_IO_BGPD = 0x69, // CGB Mode Only - Background Palette Data
|
GB_IO_BGPD = 0x69, // CGB Mode Only - Background Palette Data
|
||||||
GB_IO_OBPI = 0x6a, // CGB Mode Only - Object Palette Index
|
GB_IO_OBPI = 0x6a, // CGB Mode Only - Sprite Palette Index
|
||||||
GB_IO_OBPD = 0x6b, // CGB Mode Only - Object Palette Data
|
GB_IO_OBPD = 0x6b, // CGB Mode Only - Sprite Palette Data
|
||||||
GB_IO_OPRI = 0x6c, // Affects object priority (X based or index based)
|
GB_IO_OPRI = 0x6c, // Affects object priority (X based or index based)
|
||||||
|
|
||||||
/* Missing */
|
/* Missing */
|
||||||
|
|
||||||
GB_IO_SVBK = 0x70, // CGB Mode Only - WRAM Bank
|
GB_IO_SVBK = 0x70, // CGB Mode Only - WRAM Bank
|
||||||
GB_IO_PSWX = 0x72, // X position of the palette switching window
|
GB_IO_UNKNOWN2 = 0x72, // (00h) - Bit 0-7 (Read/Write)
|
||||||
GB_IO_PSWY = 0x73, // Y position of the palette switching window
|
GB_IO_UNKNOWN3 = 0x73, // (00h) - Bit 0-7 (Read/Write)
|
||||||
GB_IO_PSW = 0x74, // Key combo to trigger the palette switching window
|
GB_IO_UNKNOWN4 = 0x74, // (00h) - Bit 0-7 (Read/Write) - CGB Mode Only
|
||||||
GB_IO_UNKNOWN5 = 0x75, // (8Fh) - Bit 4-6 (Read/Write)
|
GB_IO_UNKNOWN5 = 0x75, // (8Fh) - Bit 4-6 (Read/Write)
|
||||||
GB_IO_PCM12 = 0x76, // Channels 1 and 2 amplitudes
|
GB_IO_PCM_12 = 0x76, // Channels 1 and 2 amplitudes
|
||||||
GB_IO_PCM34 = 0x77, // Channels 3 and 4 amplitudes
|
GB_IO_PCM_34 = 0x77, // Channels 3 and 4 amplitudes
|
||||||
|
GB_IO_UNKNOWN8 = 0x7F, // Unknown, write only
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -239,12 +236,12 @@ typedef enum {
|
|||||||
} GB_log_attributes;
|
} GB_log_attributes;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GB_BOOT_ROM_DMG_0,
|
GB_BOOT_ROM_DMG0,
|
||||||
GB_BOOT_ROM_DMG,
|
GB_BOOT_ROM_DMG,
|
||||||
GB_BOOT_ROM_MGB,
|
GB_BOOT_ROM_MGB,
|
||||||
GB_BOOT_ROM_SGB,
|
GB_BOOT_ROM_SGB,
|
||||||
GB_BOOT_ROM_SGB2,
|
GB_BOOT_ROM_SGB2,
|
||||||
GB_BOOT_ROM_CGB_0,
|
GB_BOOT_ROM_CGB0,
|
||||||
GB_BOOT_ROM_CGB,
|
GB_BOOT_ROM_CGB,
|
||||||
GB_BOOT_ROM_AGB,
|
GB_BOOT_ROM_AGB,
|
||||||
} GB_boot_rom_t;
|
} GB_boot_rom_t;
|
||||||
@ -255,6 +252,7 @@ typedef enum {
|
|||||||
#define SGB_NTSC_FREQUENCY (21477272 / 5)
|
#define SGB_NTSC_FREQUENCY (21477272 / 5)
|
||||||
#define SGB_PAL_FREQUENCY (21281370 / 5)
|
#define SGB_PAL_FREQUENCY (21281370 / 5)
|
||||||
#define DIV_CYCLES (0x100)
|
#define DIV_CYCLES (0x100)
|
||||||
|
#define INTERNAL_DIV_CYCLES (0x40000)
|
||||||
|
|
||||||
#if !defined(MIN)
|
#if !defined(MIN)
|
||||||
#define MIN(A, B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })
|
#define MIN(A, B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })
|
||||||
@ -269,7 +267,7 @@ typedef void (*GB_vblank_callback_t)(GB_gameboy_t *gb);
|
|||||||
typedef void (*GB_log_callback_t)(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes);
|
typedef void (*GB_log_callback_t)(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes);
|
||||||
typedef char *(*GB_input_callback_t)(GB_gameboy_t *gb);
|
typedef char *(*GB_input_callback_t)(GB_gameboy_t *gb);
|
||||||
typedef uint32_t (*GB_rgb_encode_callback_t)(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b);
|
typedef uint32_t (*GB_rgb_encode_callback_t)(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b);
|
||||||
typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on);
|
typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on, uint64_t cycles_since_last_update);
|
||||||
typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, double rumble_amplitude);
|
typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, double rumble_amplitude);
|
||||||
typedef void (*GB_serial_transfer_bit_start_callback_t)(GB_gameboy_t *gb, bool bit_to_send);
|
typedef void (*GB_serial_transfer_bit_start_callback_t)(GB_gameboy_t *gb, bool bit_to_send);
|
||||||
typedef bool (*GB_serial_transfer_bit_end_callback_t)(GB_gameboy_t *gb);
|
typedef bool (*GB_serial_transfer_bit_end_callback_t)(GB_gameboy_t *gb);
|
||||||
@ -280,8 +278,10 @@ typedef void (*GB_icd_hreset_callback_t)(GB_gameboy_t *gb);
|
|||||||
typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb);
|
typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb);
|
||||||
typedef void (*GB_boot_rom_load_callback_t)(GB_gameboy_t *gb, GB_boot_rom_t type);
|
typedef void (*GB_boot_rom_load_callback_t)(GB_gameboy_t *gb, GB_boot_rom_t type);
|
||||||
|
|
||||||
typedef void (*GB_execution_callback_t)(GB_gameboy_t *gb, uint16_t address, uint8_t opcode);
|
typedef struct {
|
||||||
typedef void (*GB_lcd_line_callback_t)(GB_gameboy_t *gb, uint8_t line);
|
bool state;
|
||||||
|
uint64_t delay;
|
||||||
|
} GB_ir_queue_item_t;
|
||||||
|
|
||||||
struct GB_breakpoint_s;
|
struct GB_breakpoint_s;
|
||||||
struct GB_watchpoint_s;
|
struct GB_watchpoint_s;
|
||||||
@ -289,8 +289,8 @@ struct GB_watchpoint_s;
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t pixel; // Color, 0-3
|
uint8_t pixel; // Color, 0-3
|
||||||
uint8_t palette; // Palette, 0 - 7 (CGB); 0-1 in DMG (or just 0 for BG)
|
uint8_t palette; // Palette, 0 - 7 (CGB); 0-1 in DMG (or just 0 for BG)
|
||||||
uint8_t priority; // Object priority – 0 in DMG, OAM index in CGB
|
uint8_t priority; // Sprite priority – 0 in DMG, OAM index in CGB
|
||||||
bool bg_priority; // For object FIFO – the BG priority bit. For the BG FIFO – the CGB attributes priority bit
|
bool bg_priority; // For sprite FIFO – the BG priority bit. For the BG FIFO – the CGB attributes priority bit
|
||||||
} GB_fifo_item_t;
|
} GB_fifo_item_t;
|
||||||
|
|
||||||
#define GB_FIFO_LENGTH 16
|
#define GB_FIFO_LENGTH 16
|
||||||
@ -300,55 +300,6 @@ typedef struct {
|
|||||||
uint8_t write_end;
|
uint8_t write_end;
|
||||||
} GB_fifo_t;
|
} GB_fifo_t;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t magic;
|
|
||||||
uint8_t track_count;
|
|
||||||
uint8_t first_track;
|
|
||||||
uint16_t load_address;
|
|
||||||
uint16_t init_address;
|
|
||||||
uint16_t play_address;
|
|
||||||
uint16_t sp;
|
|
||||||
uint8_t TMA;
|
|
||||||
uint8_t TAC;
|
|
||||||
char title[32];
|
|
||||||
char author[32];
|
|
||||||
char copyright[32];
|
|
||||||
} GB_gbs_header_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t track_count;
|
|
||||||
uint8_t first_track;
|
|
||||||
char title[33];
|
|
||||||
char author[33];
|
|
||||||
char copyright[33];
|
|
||||||
} GB_gbs_info_t;
|
|
||||||
|
|
||||||
/* Duplicated so it can remain anonymous in GB_gameboy_t */
|
|
||||||
typedef union {
|
|
||||||
uint16_t registers[GB_REGISTERS_16_BIT];
|
|
||||||
struct {
|
|
||||||
uint16_t af,
|
|
||||||
bc,
|
|
||||||
de,
|
|
||||||
hl,
|
|
||||||
sp,
|
|
||||||
pc;
|
|
||||||
};
|
|
||||||
struct {
|
|
||||||
#ifdef GB_BIG_ENDIAN
|
|
||||||
uint8_t a, f,
|
|
||||||
b, c,
|
|
||||||
d, e,
|
|
||||||
h, l;
|
|
||||||
#else
|
|
||||||
uint8_t f, a,
|
|
||||||
c, b,
|
|
||||||
e, d,
|
|
||||||
l, h;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
} GB_registers_t;
|
|
||||||
|
|
||||||
/* When state saving, each section is dumped independently of other sections.
|
/* When state saving, each section is dumped independently of other sections.
|
||||||
This allows adding data to the end of the section without worrying about future compatibility.
|
This allows adding data to the end of the section without worrying about future compatibility.
|
||||||
Some other changes might be "safe" as well.
|
Some other changes might be "safe" as well.
|
||||||
@ -372,30 +323,31 @@ struct GB_gameboy_internal_s {
|
|||||||
|
|
||||||
GB_SECTION(core_state,
|
GB_SECTION(core_state,
|
||||||
/* Registers */
|
/* Registers */
|
||||||
union {
|
uint16_t pc;
|
||||||
uint16_t registers[GB_REGISTERS_16_BIT];
|
union {
|
||||||
struct {
|
uint16_t registers[GB_REGISTERS_16_BIT];
|
||||||
uint16_t af,
|
struct {
|
||||||
bc,
|
uint16_t af,
|
||||||
de,
|
bc,
|
||||||
hl,
|
de,
|
||||||
sp,
|
hl,
|
||||||
pc;
|
sp;
|
||||||
};
|
};
|
||||||
struct {
|
struct {
|
||||||
#ifdef GB_BIG_ENDIAN
|
#ifdef GB_BIG_ENDIAN
|
||||||
uint8_t a, f,
|
uint8_t a, f,
|
||||||
b, c,
|
b, c,
|
||||||
d, e,
|
d, e,
|
||||||
h, l;
|
h, l;
|
||||||
#else
|
#else
|
||||||
uint8_t f, a,
|
uint8_t f, a,
|
||||||
c, b,
|
c, b,
|
||||||
e, d,
|
e, d,
|
||||||
l, h;
|
l, h;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
};
|
||||||
uint8_t ime;
|
uint8_t ime;
|
||||||
uint8_t interrupt_enable;
|
uint8_t interrupt_enable;
|
||||||
uint8_t cgb_ram_bank;
|
uint8_t cgb_ram_bank;
|
||||||
@ -416,11 +368,6 @@ struct GB_gameboy_internal_s {
|
|||||||
GB_printer_t printer;
|
GB_printer_t printer;
|
||||||
uint8_t extra_oam[0xff00 - 0xfea0];
|
uint8_t extra_oam[0xff00 - 0xfea0];
|
||||||
uint32_t ram_size; // Different between CGB and DMG
|
uint32_t ram_size; // Different between CGB and DMG
|
||||||
GB_workboy_t workboy;
|
|
||||||
|
|
||||||
int32_t ir_sensor;
|
|
||||||
bool effective_ir_input;
|
|
||||||
uint16_t address_bus;
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* DMA and HDMA */
|
/* DMA and HDMA */
|
||||||
@ -436,8 +383,6 @@ struct GB_gameboy_internal_s {
|
|||||||
uint16_t dma_current_src;
|
uint16_t dma_current_src;
|
||||||
int16_t dma_cycles;
|
int16_t dma_cycles;
|
||||||
bool is_dma_restarting;
|
bool is_dma_restarting;
|
||||||
uint8_t dma_and_pattern;
|
|
||||||
bool dma_skip_write;
|
|
||||||
uint8_t last_opcode_read; /* Required to emulte HDMA reads from Exxx */
|
uint8_t last_opcode_read; /* Required to emulte HDMA reads from Exxx */
|
||||||
bool hdma_starting;
|
bool hdma_starting;
|
||||||
);
|
);
|
||||||
@ -462,7 +407,6 @@ struct GB_gameboy_internal_s {
|
|||||||
struct {
|
struct {
|
||||||
uint8_t rom_bank:8;
|
uint8_t rom_bank:8;
|
||||||
uint8_t ram_bank:3;
|
uint8_t ram_bank:3;
|
||||||
bool rtc_mapped:1;
|
|
||||||
} mbc3;
|
} mbc3;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -471,54 +415,30 @@ struct GB_gameboy_internal_s {
|
|||||||
uint8_t ram_bank:4;
|
uint8_t ram_bank:4;
|
||||||
} mbc5;
|
} mbc5;
|
||||||
|
|
||||||
struct {
|
|
||||||
uint8_t rom_bank;
|
|
||||||
uint16_t x_latch;
|
|
||||||
uint16_t y_latch;
|
|
||||||
bool latch_ready:1;
|
|
||||||
bool eeprom_do:1;
|
|
||||||
bool eeprom_di:1;
|
|
||||||
bool eeprom_clk:1;
|
|
||||||
bool eeprom_cs:1;
|
|
||||||
uint16_t eeprom_command:11;
|
|
||||||
uint16_t read_bits;
|
|
||||||
uint8_t bits_countdown:5;
|
|
||||||
bool secondary_ram_enable:1;
|
|
||||||
bool eeprom_write_enabled:1;
|
|
||||||
} mbc7;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint8_t bank_low:6;
|
uint8_t bank_low:6;
|
||||||
uint8_t bank_high:3;
|
uint8_t bank_high:3;
|
||||||
bool mode:1;
|
uint8_t mode:1;
|
||||||
bool ir_mode:1;
|
|
||||||
} huc1;
|
} huc1;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint8_t rom_bank:7;
|
uint8_t rom_bank:7;
|
||||||
uint8_t padding:1;
|
uint8_t padding:1;
|
||||||
uint8_t ram_bank:4;
|
uint8_t ram_bank:4;
|
||||||
uint8_t mode;
|
|
||||||
uint8_t access_index;
|
|
||||||
uint16_t minutes, days;
|
|
||||||
uint16_t alarm_minutes, alarm_days;
|
|
||||||
bool alarm_enabled;
|
|
||||||
uint8_t read;
|
|
||||||
uint8_t access_flags;
|
|
||||||
} huc3;
|
} huc3;
|
||||||
|
|
||||||
struct {
|
|
||||||
uint16_t rom_bank;
|
|
||||||
uint8_t ram_bank;
|
|
||||||
uint8_t mode;
|
|
||||||
} tpp1;
|
|
||||||
};
|
};
|
||||||
uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */
|
uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */
|
||||||
bool camera_registers_mapped;
|
bool camera_registers_mapped;
|
||||||
uint8_t camera_registers[0x36];
|
uint8_t camera_registers[0x36];
|
||||||
uint8_t rumble_strength;
|
bool rumble_state;
|
||||||
bool cart_ir;
|
bool cart_ir;
|
||||||
|
|
||||||
|
// TODO: move to huc3 struct when breaking save compat
|
||||||
|
uint8_t huc3_mode;
|
||||||
|
uint8_t huc3_access_index;
|
||||||
|
uint16_t huc3_minutes, huc3_days;
|
||||||
|
uint8_t huc3_read;
|
||||||
|
uint8_t huc3_access_flags;
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@ -538,14 +458,6 @@ struct GB_gameboy_internal_s {
|
|||||||
uint16_t serial_length;
|
uint16_t serial_length;
|
||||||
uint8_t double_speed_alignment;
|
uint8_t double_speed_alignment;
|
||||||
uint8_t serial_count;
|
uint8_t serial_count;
|
||||||
int32_t speed_switch_halt_countdown;
|
|
||||||
uint8_t speed_switch_countdown; // To compensate for the lack of pipeline emulation
|
|
||||||
uint8_t speed_switch_freeze; // Solely for realigning the PPU, should be removed when the odd modes are implemented
|
|
||||||
/* For timing of the vblank callback */
|
|
||||||
uint32_t cycles_since_vblank_callback;
|
|
||||||
bool lcd_disabled_outside_of_vblank;
|
|
||||||
int32_t allowed_pending_cycles;
|
|
||||||
uint16_t mode3_batching_length;
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* APU */
|
/* APU */
|
||||||
@ -557,17 +469,16 @@ struct GB_gameboy_internal_s {
|
|||||||
GB_SECTION(rtc,
|
GB_SECTION(rtc,
|
||||||
GB_rtc_time_t rtc_real, rtc_latched;
|
GB_rtc_time_t rtc_real, rtc_latched;
|
||||||
uint64_t last_rtc_second;
|
uint64_t last_rtc_second;
|
||||||
uint32_t rtc_cycles;
|
bool rtc_latch;
|
||||||
uint8_t tpp1_mr4;
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Video Display */
|
/* Video Display */
|
||||||
GB_SECTION(video,
|
GB_SECTION(video,
|
||||||
uint32_t vram_size; // Different between CGB and DMG
|
uint32_t vram_size; // Different between CGB and DMG
|
||||||
bool cgb_vram_bank;
|
uint8_t cgb_vram_bank;
|
||||||
uint8_t oam[0xA0];
|
uint8_t oam[0xA0];
|
||||||
uint8_t background_palettes_data[0x40];
|
uint8_t background_palettes_data[0x40];
|
||||||
uint8_t object_palettes_data[0x40];
|
uint8_t sprite_palettes_data[0x40];
|
||||||
uint8_t position_in_line;
|
uint8_t position_in_line;
|
||||||
bool stat_interrupt_line;
|
bool stat_interrupt_line;
|
||||||
uint8_t effective_scx;
|
uint8_t effective_scx;
|
||||||
@ -592,6 +503,7 @@ struct GB_gameboy_internal_s {
|
|||||||
uint8_t current_line;
|
uint8_t current_line;
|
||||||
uint16_t ly_for_comparison;
|
uint16_t ly_for_comparison;
|
||||||
GB_fifo_t bg_fifo, oam_fifo;
|
GB_fifo_t bg_fifo, oam_fifo;
|
||||||
|
uint8_t fetcher_x;
|
||||||
uint8_t fetcher_y;
|
uint8_t fetcher_y;
|
||||||
uint16_t cycles_for_line;
|
uint16_t cycles_for_line;
|
||||||
uint8_t current_tile;
|
uint8_t current_tile;
|
||||||
@ -606,11 +518,12 @@ struct GB_gameboy_internal_s {
|
|||||||
uint8_t n_visible_objs;
|
uint8_t n_visible_objs;
|
||||||
uint8_t oam_search_index;
|
uint8_t oam_search_index;
|
||||||
uint8_t accessed_oam_row;
|
uint8_t accessed_oam_row;
|
||||||
uint8_t extra_penalty_for_object_at_0;
|
uint8_t extra_penalty_for_sprite_at_0;
|
||||||
uint8_t mode_for_interrupt;
|
uint8_t mode_for_interrupt;
|
||||||
bool lyc_interrupt_line;
|
bool lyc_interrupt_line;
|
||||||
bool cgb_palettes_blocked;
|
bool cgb_palettes_blocked;
|
||||||
uint8_t current_lcd_line; // The LCD can go out of sync since the vsync signal is skipped in some cases.
|
uint8_t current_lcd_line; // The LCD can go out of sync since the vsync signal is skipped in some cases.
|
||||||
|
uint32_t cycles_in_stop_mode;
|
||||||
uint8_t object_priority;
|
uint8_t object_priority;
|
||||||
bool oam_ppu_blocked;
|
bool oam_ppu_blocked;
|
||||||
bool vram_ppu_blocked;
|
bool vram_ppu_blocked;
|
||||||
@ -622,10 +535,6 @@ struct GB_gameboy_internal_s {
|
|||||||
uint8_t window_tile_x;
|
uint8_t window_tile_x;
|
||||||
uint8_t lcd_x; // The LCD can go out of sync since the push signal is skipped in some cases.
|
uint8_t lcd_x; // The LCD can go out of sync since the push signal is skipped in some cases.
|
||||||
bool is_odd_frame;
|
bool is_odd_frame;
|
||||||
uint16_t last_tile_data_address;
|
|
||||||
uint16_t last_tile_index_address;
|
|
||||||
bool cgb_repeated_a_frame;
|
|
||||||
uint8_t data_for_sel_glitch;
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
|
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
|
||||||
@ -651,28 +560,18 @@ struct GB_gameboy_internal_s {
|
|||||||
/* I/O */
|
/* I/O */
|
||||||
uint32_t *screen;
|
uint32_t *screen;
|
||||||
uint32_t background_palettes_rgb[0x20];
|
uint32_t background_palettes_rgb[0x20];
|
||||||
uint32_t object_palettes_rgb[0x20];
|
uint32_t sprite_palettes_rgb[0x20];
|
||||||
const GB_palette_t *dmg_palette;
|
const GB_palette_t *dmg_palette;
|
||||||
GB_color_correction_mode_t color_correction_mode;
|
GB_color_correction_mode_t color_correction_mode;
|
||||||
double light_temperature;
|
|
||||||
bool keys[4][GB_KEY_MAX];
|
bool keys[4][GB_KEY_MAX];
|
||||||
double accelerometer_x, accelerometer_y;
|
|
||||||
GB_border_mode_t border_mode;
|
GB_border_mode_t border_mode;
|
||||||
GB_sgb_border_t borrowed_border;
|
GB_sgb_border_t borrowed_border;
|
||||||
bool tried_loading_sgb_border;
|
bool tried_loading_sgb_border;
|
||||||
bool has_sgb_border;
|
bool has_sgb_border;
|
||||||
bool objects_disabled;
|
|
||||||
bool background_disabled;
|
|
||||||
bool joyp_accessed;
|
|
||||||
bool illegal_inputs_allowed;
|
|
||||||
|
|
||||||
/* Timing */
|
/* Timing */
|
||||||
uint64_t last_sync;
|
uint64_t last_sync;
|
||||||
uint64_t cycles_since_last_sync; // In 8MHz units
|
uint64_t cycles_since_last_sync; // In 8MHz units
|
||||||
GB_rtc_mode_t rtc_mode;
|
|
||||||
uint32_t rtc_second_length;
|
|
||||||
uint32_t clock_rate;
|
|
||||||
uint32_t unmultiplied_clock_rate;
|
|
||||||
|
|
||||||
/* Audio */
|
/* Audio */
|
||||||
GB_apu_output_t apu_output;
|
GB_apu_output_t apu_output;
|
||||||
@ -696,13 +595,13 @@ struct GB_gameboy_internal_s {
|
|||||||
GB_icd_vreset_callback_t icd_hreset_callback;
|
GB_icd_vreset_callback_t icd_hreset_callback;
|
||||||
GB_icd_vreset_callback_t icd_vreset_callback;
|
GB_icd_vreset_callback_t icd_vreset_callback;
|
||||||
GB_read_memory_callback_t read_memory_callback;
|
GB_read_memory_callback_t read_memory_callback;
|
||||||
GB_write_memory_callback_t write_memory_callback;
|
|
||||||
GB_boot_rom_load_callback_t boot_rom_load_callback;
|
GB_boot_rom_load_callback_t boot_rom_load_callback;
|
||||||
GB_print_image_callback_t printer_callback;
|
|
||||||
GB_workboy_set_time_callback workboy_set_time_callback;
|
/* IR */
|
||||||
GB_workboy_get_time_callback workboy_get_time_callback;
|
uint64_t cycles_since_ir_change; // In 8MHz units
|
||||||
GB_execution_callback_t execution_callback;
|
uint64_t cycles_since_input_ir_change; // In 8MHz units
|
||||||
GB_lcd_line_callback_t lcd_line_callback;
|
GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE];
|
||||||
|
size_t ir_queue_length;
|
||||||
|
|
||||||
/*** Debugger ***/
|
/*** Debugger ***/
|
||||||
volatile bool debug_stopped, debug_disable;
|
volatile bool debug_stopped, debug_disable;
|
||||||
@ -711,7 +610,7 @@ struct GB_gameboy_internal_s {
|
|||||||
/* Breakpoints */
|
/* Breakpoints */
|
||||||
uint16_t n_breakpoints;
|
uint16_t n_breakpoints;
|
||||||
struct GB_breakpoint_s *breakpoints;
|
struct GB_breakpoint_s *breakpoints;
|
||||||
bool has_jump_to_breakpoints, has_software_breakpoints;
|
bool has_jump_to_breakpoints;
|
||||||
void *nontrivial_jump_state;
|
void *nontrivial_jump_state;
|
||||||
bool non_trivial_jump_breakpoint_occured;
|
bool non_trivial_jump_breakpoint_occured;
|
||||||
|
|
||||||
@ -739,11 +638,6 @@ struct GB_gameboy_internal_s {
|
|||||||
|
|
||||||
/* Ticks command */
|
/* Ticks command */
|
||||||
uint64_t debugger_ticks;
|
uint64_t debugger_ticks;
|
||||||
uint64_t absolute_debugger_ticks;
|
|
||||||
|
|
||||||
/* Undo */
|
|
||||||
uint8_t *undo_state;
|
|
||||||
const char *undo_label;
|
|
||||||
|
|
||||||
/* Rewind */
|
/* Rewind */
|
||||||
#define GB_REWIND_FRAMES_PER_KEY 255
|
#define GB_REWIND_FRAMES_PER_KEY 255
|
||||||
@ -782,16 +676,12 @@ struct GB_gameboy_internal_s {
|
|||||||
|
|
||||||
/* Temporary state */
|
/* Temporary state */
|
||||||
bool wx_just_changed;
|
bool wx_just_changed;
|
||||||
bool tile_sel_glitch;
|
|
||||||
bool disable_oam_corruption; // For safe memory reads
|
|
||||||
|
|
||||||
GB_gbs_header_t gbs_header;
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef GB_INTERNAL
|
#ifndef GB_INTERNAL
|
||||||
struct GB_gameboy_s {
|
struct GB_gameboy_s {
|
||||||
alignas(struct GB_gameboy_internal_s) uint8_t __internal[sizeof(struct GB_gameboy_internal_s)];
|
char __internal[sizeof(struct GB_gameboy_internal_s)];
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -804,8 +694,7 @@ __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
|
|||||||
|
|
||||||
void GB_init(GB_gameboy_t *gb, GB_model_t model);
|
void GB_init(GB_gameboy_t *gb, GB_model_t model);
|
||||||
bool GB_is_inited(GB_gameboy_t *gb);
|
bool GB_is_inited(GB_gameboy_t *gb);
|
||||||
bool GB_is_cgb(const GB_gameboy_t *gb);
|
bool GB_is_cgb(GB_gameboy_t *gb);
|
||||||
bool GB_is_cgb_in_cgb_mode(GB_gameboy_t *gb);
|
|
||||||
bool GB_is_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2
|
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
|
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
|
||||||
GB_model_t GB_get_model(GB_gameboy_t *gb);
|
GB_model_t GB_get_model(GB_gameboy_t *gb);
|
||||||
@ -835,19 +724,17 @@ typedef enum {
|
|||||||
/* Returns a mutable pointer to various hardware memories. If that memory is banked, the current bank
|
/* Returns a mutable pointer to various hardware memories. If that memory is banked, the current bank
|
||||||
is returned at *bank, even if only a portion of the memory is banked. */
|
is returned at *bank, even if only a portion of the memory is banked. */
|
||||||
void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank);
|
void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank);
|
||||||
GB_registers_t *GB_get_registers(GB_gameboy_t *gb);
|
|
||||||
|
|
||||||
void *GB_get_user_data(GB_gameboy_t *gb);
|
void *GB_get_user_data(GB_gameboy_t *gb);
|
||||||
void GB_set_user_data(GB_gameboy_t *gb, void *data);
|
void GB_set_user_data(GB_gameboy_t *gb, void *data);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int GB_load_boot_rom(GB_gameboy_t *gb, const char *path);
|
int GB_load_boot_rom(GB_gameboy_t *gb, const char *path);
|
||||||
void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size);
|
void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size);
|
||||||
int GB_load_rom(GB_gameboy_t *gb, const char *path);
|
int GB_load_rom(GB_gameboy_t *gb, const char *path);
|
||||||
void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
|
void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
|
||||||
int GB_load_isx(GB_gameboy_t *gb, const char *path);
|
int GB_load_isx(GB_gameboy_t *gb, const char *path);
|
||||||
int GB_load_gbs_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size, GB_gbs_info_t *info);
|
|
||||||
int GB_load_gbs(GB_gameboy_t *gb, const char *path, GB_gbs_info_t *info);
|
|
||||||
void GB_gbs_switch_track(GB_gameboy_t *gb, uint8_t track);
|
|
||||||
|
|
||||||
int GB_save_battery_size(GB_gameboy_t *gb);
|
int GB_save_battery_size(GB_gameboy_t *gb);
|
||||||
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size);
|
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size);
|
||||||
@ -866,6 +753,7 @@ void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output);
|
|||||||
void GB_set_border_mode(GB_gameboy_t *gb, GB_border_mode_t border_mode);
|
void GB_set_border_mode(GB_gameboy_t *gb, GB_border_mode_t border_mode);
|
||||||
|
|
||||||
void GB_set_infrared_input(GB_gameboy_t *gb, bool state);
|
void GB_set_infrared_input(GB_gameboy_t *gb, bool state);
|
||||||
|
void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, uint64_t cycles_after_previous_change); /* In 8MHz units*/
|
||||||
|
|
||||||
void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback);
|
void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback);
|
||||||
void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback);
|
void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback);
|
||||||
@ -878,11 +766,7 @@ void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_ca
|
|||||||
/* Called when a new boot ROM is needed. The callback should call GB_load_boot_rom or GB_load_boot_rom_from_buffer */
|
/* Called when a new boot ROM is needed. The callback should call GB_load_boot_rom or GB_load_boot_rom_from_buffer */
|
||||||
void GB_set_boot_rom_load_callback(GB_gameboy_t *gb, GB_boot_rom_load_callback_t callback);
|
void GB_set_boot_rom_load_callback(GB_gameboy_t *gb, GB_boot_rom_load_callback_t callback);
|
||||||
|
|
||||||
void GB_set_execution_callback(GB_gameboy_t *gb, GB_execution_callback_t callback);
|
|
||||||
void GB_set_lcd_line_callback(GB_gameboy_t *gb, GB_lcd_line_callback_t callback);
|
|
||||||
|
|
||||||
void GB_set_palette(GB_gameboy_t *gb, const GB_palette_t *palette);
|
void GB_set_palette(GB_gameboy_t *gb, const GB_palette_t *palette);
|
||||||
const GB_palette_t *GB_get_palette(GB_gameboy_t *gb);
|
|
||||||
|
|
||||||
/* These APIs are used when using internal clock */
|
/* These APIs are used when using internal clock */
|
||||||
void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback);
|
void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback);
|
||||||
@ -894,23 +778,15 @@ void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data);
|
|||||||
|
|
||||||
void GB_disconnect_serial(GB_gameboy_t *gb);
|
void GB_disconnect_serial(GB_gameboy_t *gb);
|
||||||
|
|
||||||
/* For cartridges with an alarm clock */
|
|
||||||
unsigned GB_time_to_alarm(GB_gameboy_t *gb); // 0 if no alarm
|
|
||||||
|
|
||||||
/* For cartridges motion controls */
|
|
||||||
bool GB_has_accelerometer(GB_gameboy_t *gb);
|
|
||||||
// In units of g (gravity's acceleration).
|
|
||||||
// Values within ±4 recommended
|
|
||||||
void GB_set_accelerometer_values(GB_gameboy_t *gb, double x, double y);
|
|
||||||
|
|
||||||
/* For integration with SFC/SNES emulators */
|
/* For integration with SFC/SNES emulators */
|
||||||
void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback);
|
void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback);
|
||||||
void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback);
|
void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback);
|
||||||
void GB_set_icd_hreset_callback(GB_gameboy_t *gb, GB_icd_hreset_callback_t callback);
|
void GB_set_icd_hreset_callback(GB_gameboy_t *gb, GB_icd_hreset_callback_t callback);
|
||||||
void GB_set_icd_vreset_callback(GB_gameboy_t *gb, GB_icd_vreset_callback_t callback);
|
void GB_set_icd_vreset_callback(GB_gameboy_t *gb, GB_icd_vreset_callback_t callback);
|
||||||
|
|
||||||
|
#ifdef GB_INTERNAL
|
||||||
uint32_t GB_get_clock_rate(GB_gameboy_t *gb);
|
uint32_t GB_get_clock_rate(GB_gameboy_t *gb);
|
||||||
uint32_t GB_get_unmultiplied_clock_rate(GB_gameboy_t *gb);
|
#endif
|
||||||
void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier);
|
void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier);
|
||||||
|
|
||||||
unsigned GB_get_screen_width(GB_gameboy_t *gb);
|
unsigned GB_get_screen_width(GB_gameboy_t *gb);
|
||||||
@ -918,14 +794,4 @@ unsigned GB_get_screen_height(GB_gameboy_t *gb);
|
|||||||
double GB_get_usual_frame_rate(GB_gameboy_t *gb);
|
double GB_get_usual_frame_rate(GB_gameboy_t *gb);
|
||||||
unsigned GB_get_player_count(GB_gameboy_t *gb);
|
unsigned GB_get_player_count(GB_gameboy_t *gb);
|
||||||
|
|
||||||
/* Handy ROM info APIs */
|
|
||||||
// `title` must be at least 17 bytes in size
|
|
||||||
void GB_get_rom_title(GB_gameboy_t *gb, char *title);
|
|
||||||
uint32_t GB_get_rom_crc32(GB_gameboy_t *gb);
|
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
|
||||||
internal void GB_borrow_sgb_border(GB_gameboy_t *gb);
|
|
||||||
internal void GB_update_clock_rate(GB_gameboy_t *gb);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* GB_h */
|
#endif /* GB_h */
|
||||||
|
5
Core/gb_struct_def.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#ifndef gb_struct_def_h
|
||||||
|
#define gb_struct_def_h
|
||||||
|
struct GB_gameboy_s;
|
||||||
|
typedef struct GB_gameboy_s GB_gameboy_t;
|
||||||
|
#endif
|
@ -1,477 +0,0 @@
|
|||||||
static const uint16_t palette[] = {
|
|
||||||
0x0000, 0x0000, 0x0011, 0x001A, 0x39CE, 0x6B5A, 0x739C, 0x5265,
|
|
||||||
0x3DC5, 0x2924, 0x18A4, 0x20E6, 0x2D49, 0x1484, 0x5694, 0x20EC,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static const uint16_t tilemap[] = {
|
|
||||||
0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x0010, 0x0011, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012,
|
|
||||||
0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012,
|
|
||||||
0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012,
|
|
||||||
0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x4011, 0x4010, 0x000F,
|
|
||||||
0x000F, 0x0013, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014,
|
|
||||||
0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014,
|
|
||||||
0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014,
|
|
||||||
0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x4013, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0016, 0x0017, 0x0017,
|
|
||||||
0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017,
|
|
||||||
0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017,
|
|
||||||
0x0017, 0x0017, 0x4016, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0019, 0x001A, 0x4019, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x8019, 0x001B, 0xC019, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x0018, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
||||||
0x0000, 0x0000, 0x4018, 0x0014, 0x0014, 0x0014, 0x4015, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x0014, 0x0014, 0x001C, 0x001D, 0x001D,
|
|
||||||
0x001D, 0x001D, 0x001D, 0x001D, 0x001D, 0x001D, 0x001D, 0x001D,
|
|
||||||
0x001D, 0x001D, 0x001D, 0x001D, 0x001D, 0x001D, 0x001D, 0x001D,
|
|
||||||
0x001D, 0x001D, 0x401C, 0x0014, 0x0014, 0x0014, 0x001E, 0x000F,
|
|
||||||
0x000F, 0x0015, 0x0014, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023,
|
|
||||||
0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B,
|
|
||||||
0x002C, 0x002D, 0x002E, 0x002F, 0x0014, 0x0014, 0x0014, 0x0014,
|
|
||||||
0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0030, 0x0031, 0x000F,
|
|
||||||
0x000F, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038,
|
|
||||||
0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x0040,
|
|
||||||
0x0041, 0x0042, 0x0043, 0x0044, 0x0014, 0x0014, 0x0014, 0x0014,
|
|
||||||
0x0014, 0x0014, 0x0014, 0x0014, 0x0045, 0x0046, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x0047, 0x0048, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049,
|
|
||||||
0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049,
|
|
||||||
0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049,
|
|
||||||
0x0049, 0x0049, 0x004A, 0x004B, 0x004C, 0x000F, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F,
|
|
||||||
0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F, 0x000F,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const uint8_t tiles[] = {
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x3D,
|
|
||||||
0x42, 0x7F, 0x81, 0xFF, 0x01, 0xFD, 0x01,
|
|
||||||
0xFD, 0x01, 0xFF, 0x03, 0xFF, 0x03, 0xFF,
|
|
||||||
0xFF, 0xBC, 0x7F, 0xFD, 0xFE, 0xFD, 0xFE,
|
|
||||||
0xFD, 0xFE, 0xFD, 0xFE, 0xFF, 0xFC, 0xFF,
|
|
||||||
0xFC, 0xFF, 0x00, 0xBF, 0x41, 0xFE, 0xC0,
|
|
||||||
0xBF, 0xC1, 0xFF, 0x81, 0x7D, 0x03, 0x7F,
|
|
||||||
0x01, 0x7F, 0x01, 0xFF, 0xFF, 0x3E, 0xFF,
|
|
||||||
0xBE, 0x7F, 0xBF, 0x7E, 0xFF, 0x7E, 0x7D,
|
|
||||||
0xFE, 0x7D, 0xFE, 0x7D, 0xFE, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xBF, 0x83, 0xBF, 0x87, 0xFC,
|
|
||||||
0x8D, 0xED, 0x8E, 0xDB, 0xF8, 0xBF, 0xD8,
|
|
||||||
0xFF, 0xFF, 0x3E, 0xFF, 0xBB, 0x7C, 0xB7,
|
|
||||||
0x78, 0xAC, 0x73, 0xAD, 0x73, 0x9B, 0x67,
|
|
||||||
0x9B, 0x67, 0xFF, 0x00, 0xB7, 0x08, 0xFF,
|
|
||||||
0xF8, 0x3F, 0x38, 0xFF, 0x08, 0xFE, 0x01,
|
|
||||||
0x87, 0x00, 0xFB, 0x78, 0xFF, 0xFF, 0x07,
|
|
||||||
0xFF, 0xFB, 0x07, 0x3B, 0xC7, 0xE7, 0xFF,
|
|
||||||
0xFE, 0xFF, 0x82, 0xFF, 0xFA, 0x87, 0xFF,
|
|
||||||
0x00, 0xFE, 0x81, 0x5F, 0x40, 0xDE, 0xC0,
|
|
||||||
0xFE, 0xC0, 0xE0, 0xDE, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0x1E, 0xFF, 0x5E, 0xBF,
|
|
||||||
0xDE, 0x3F, 0xDE, 0x3F, 0xC0, 0x3F, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xCF, 0x30,
|
|
||||||
0xDF, 0xEF, 0xFF, 0xCF, 0xFF, 0xC1, 0xBD,
|
|
||||||
0x81, 0xBD, 0x81, 0xFF, 0x83, 0xFF, 0xFF,
|
|
||||||
0x00, 0xFF, 0xCF, 0x30, 0xEF, 0x30, 0xFD,
|
|
||||||
0x3E, 0xBD, 0x7E, 0xBD, 0x7E, 0xBF, 0x7C,
|
|
||||||
0xFF, 0x00, 0xFF, 0x08, 0xF7, 0xF0, 0xFF,
|
|
||||||
0xF0, 0xBF, 0xC0, 0xFF, 0x80, 0x7F, 0x00,
|
|
||||||
0x7F, 0x00, 0xFF, 0xFF, 0x07, 0xFF, 0xF7,
|
|
||||||
0x0F, 0xF7, 0x0F, 0xBF, 0x7F, 0xFF, 0x7F,
|
|
||||||
0x7F, 0xFF, 0x7F, 0xFF, 0xFB, 0x07, 0xFF,
|
|
||||||
0x03, 0xFF, 0x03, 0xFB, 0x03, 0xFB, 0x03,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFB,
|
|
||||||
0xFC, 0xFB, 0xFC, 0xFB, 0xFC, 0xFB, 0xFC,
|
|
||||||
0xFB, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFD, 0x01, 0xFD, 0x81, 0xFF, 0x0B,
|
|
||||||
0xF7, 0xF3, 0xFB, 0xF7, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0x7D, 0xFE, 0x7D, 0xFE,
|
|
||||||
0x07, 0xFC, 0xF7, 0x0C, 0xF3, 0x0C, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x98,
|
|
||||||
0x7B, 0x38, 0x7C, 0x1D, 0xFF, 0x0F, 0xFB,
|
|
||||||
0x0B, 0xFD, 0x03, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xDB, 0x67, 0x5B, 0xE7, 0x7C, 0xE3, 0x6F,
|
|
||||||
0xF0, 0x73, 0xFC, 0xFC, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0x9E, 0x18, 0xFE, 0x3C, 0x5A,
|
|
||||||
0xDC, 0xFF, 0xF9, 0xED, 0xE3, 0xBF, 0xC0,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0x9A, 0xE7, 0xDA,
|
|
||||||
0xE7, 0x1A, 0xE7, 0xFF, 0x06, 0xE5, 0x1E,
|
|
||||||
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3,
|
|
||||||
0xFD, 0xBF, 0x81, 0xBF, 0x81, 0xBD, 0x81,
|
|
||||||
0xFD, 0x81, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xC1, 0x3E, 0xBD, 0x7E, 0xBD, 0x7E,
|
|
||||||
0xBD, 0x7E, 0xBD, 0x7E, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xBB, 0xC7,
|
|
||||||
0xFF, 0x83, 0xFF, 0x83, 0x7B, 0x03, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xBF, 0x7C,
|
|
||||||
0xBB, 0x7C, 0xFB, 0x7C, 0xFB, 0x7C, 0x7B,
|
|
||||||
0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7F,
|
|
||||||
0x80, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
|
|
||||||
0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFE, 0x00, 0xF9, 0x00,
|
|
||||||
0xF7, 0x00, 0xEE, 0x00, 0xDD, 0x04, 0xDF,
|
|
||||||
0x04, 0xBF, 0x08, 0xFF, 0x00, 0xFF, 0x01,
|
|
||||||
0xFF, 0x07, 0xFF, 0x0F, 0xFF, 0x1F, 0xFB,
|
|
||||||
0x3F, 0xFB, 0x3F, 0xF7, 0x7F, 0x80, 0x00,
|
|
||||||
0x7F, 0x00, 0xFF, 0x00, 0x80, 0x00, 0x7F,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF,
|
|
||||||
0x08, 0xFF, 0x10, 0xFF, 0x10, 0xFF, 0x10,
|
|
||||||
0xFF, 0x10, 0xFF, 0x10, 0xFF, 0x10, 0xFF,
|
|
||||||
0x10, 0xF7, 0x7F, 0xEF, 0x7F, 0xEF, 0x7F,
|
|
||||||
0xEF, 0x7F, 0xEF, 0x7F, 0xEF, 0x7F, 0xEF,
|
|
||||||
0x7F, 0xEF, 0x7F, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0x10, 0xFF, 0x10, 0xFF, 0x10, 0xFF,
|
|
||||||
0x10, 0xFF, 0x10, 0xFF, 0x10, 0xFF, 0x10,
|
|
||||||
0xFF, 0x10, 0xEF, 0x7F, 0xEF, 0x7F, 0xEF,
|
|
||||||
0x7F, 0xEF, 0x7F, 0xEF, 0x7F, 0xEF, 0x7F,
|
|
||||||
0xEF, 0x7F, 0xEF, 0x7F, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
|
||||||
0xFE, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
|
||||||
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
|
||||||
0xFE, 0xFE, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x01,
|
|
||||||
0xFF, 0x01, 0xFF, 0x01, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xC3, 0x7E,
|
|
||||||
0xBD, 0xFF, 0x66, 0xFF, 0x7E, 0xFF, 0x7E,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xC3, 0xFF, 0x81, 0xC3, 0x18, 0x81, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x7E, 0xFF, 0x3C, 0xFF,
|
|
||||||
0x00, 0x7E, 0x81, 0xBD, 0xC3, 0x42, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x81, 0x00, 0xC3, 0x81, 0xFF,
|
|
||||||
0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0x01, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0x08, 0xFD, 0x12, 0xFD, 0x12,
|
|
||||||
0xFD, 0x12, 0xFD, 0x12, 0xFB, 0x24, 0xFB,
|
|
||||||
0x24, 0xFB, 0x24, 0xF7, 0xFE, 0xEF, 0xFC,
|
|
||||||
0xEF, 0xFC, 0xEF, 0xFC, 0xEF, 0xFC, 0xDF,
|
|
||||||
0xF8, 0xDF, 0xF8, 0xDF, 0xF8, 0xFF, 0x00,
|
|
||||||
0xF0, 0x1E, 0xC0, 0x3F, 0x8D, 0x72, 0x0E,
|
|
||||||
0xF3, 0x8F, 0xF0, 0x01, 0xFC, 0xA0, 0x1E,
|
|
||||||
0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xE0, 0xFF,
|
|
||||||
0xC0, 0x7C, 0x9F, 0x7F, 0x9F, 0x7F, 0x87,
|
|
||||||
0xFF, 0xC0, 0xFF, 0x00, 0xFC, 0x00, 0x78,
|
|
||||||
0x87, 0x78, 0x87, 0xF0, 0x07, 0xF8, 0x07,
|
|
||||||
0xE2, 0x0F, 0xE2, 0x1C, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFE, 0xFB, 0xFC, 0x7F, 0xFC, 0xFF, 0xF8,
|
|
||||||
0xFF, 0xF2, 0xFD, 0xF3, 0xFF, 0xE6, 0xFF,
|
|
||||||
0x00, 0x7C, 0x02, 0xFC, 0x01, 0x3C, 0xC3,
|
|
||||||
0x3C, 0x83, 0x3E, 0xC1, 0x3C, 0xC3, 0x18,
|
|
||||||
0xC7, 0xFF, 0xFF, 0xFD, 0xFE, 0xFF, 0x7C,
|
|
||||||
0xBF, 0x7E, 0xFF, 0x3E, 0xFF, 0x7C, 0xFF,
|
|
||||||
0x3C, 0xFB, 0x3C, 0xFF, 0x00, 0x1E, 0x01,
|
|
||||||
0x1E, 0xE1, 0x1E, 0xE3, 0x1C, 0xF3, 0x0C,
|
|
||||||
0xE7, 0x08, 0xF7, 0x19, 0x6F, 0xFF, 0xFF,
|
|
||||||
0xFE, 0x3F, 0xFF, 0x3F, 0xFD, 0x1E, 0xEF,
|
|
||||||
0x1E, 0xFB, 0x8C, 0xFF, 0xDD, 0xF6, 0x49,
|
|
||||||
0xFF, 0x00, 0x18, 0x14, 0x08, 0xE3, 0x08,
|
|
||||||
0xE3, 0x18, 0xF7, 0x1D, 0xE2, 0x18, 0xE7,
|
|
||||||
0xB8, 0x47, 0xFF, 0xFF, 0xEB, 0x1C, 0xFF,
|
|
||||||
0x0C, 0xFF, 0x18, 0xEF, 0x1C, 0xFF, 0x98,
|
|
||||||
0xFF, 0x98, 0xFF, 0x18, 0xFF, 0x00, 0x06,
|
|
||||||
0x05, 0x02, 0xF8, 0x02, 0xF9, 0xFE, 0x01,
|
|
||||||
0xFF, 0x00, 0x06, 0xF9, 0x04, 0xF3, 0xFF,
|
|
||||||
0xFF, 0xFA, 0x07, 0xFF, 0x02, 0xFF, 0x07,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x0E, 0xFD,
|
|
||||||
0x06, 0xFF, 0x00, 0x03, 0x00, 0x01, 0xFF,
|
|
||||||
0x30, 0xCF, 0x70, 0x8F, 0x30, 0xC6, 0x21,
|
|
||||||
0xDC, 0x01, 0xFE, 0xFF, 0xFF, 0xFF, 0x0F,
|
|
||||||
0xFE, 0x01, 0xFF, 0x01, 0xF7, 0x39, 0xFF,
|
|
||||||
0x79, 0xFF, 0x01, 0xFF, 0x03, 0xFF, 0x00,
|
|
||||||
0xF8, 0x01, 0xF0, 0x1F, 0xE1, 0x3E, 0x83,
|
|
||||||
0x78, 0x87, 0x30, 0xCF, 0x30, 0x8F, 0x70,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFD, 0xEF, 0xF8, 0xDF,
|
|
||||||
0xE0, 0xBF, 0xC3, 0xFF, 0x87, 0xFF, 0x8F,
|
|
||||||
0xFF, 0x9F, 0xFF, 0x00, 0x3C, 0xA0, 0x0C,
|
|
||||||
0xE1, 0x07, 0xF0, 0x86, 0x38, 0xC7, 0x3C,
|
|
||||||
0xC3, 0x18, 0xC7, 0x3C, 0xFF, 0xFF, 0xDF,
|
|
||||||
0xBE, 0xFF, 0x0C, 0xFF, 0x06, 0xFF, 0x87,
|
|
||||||
0xFB, 0xE7, 0xFF, 0xC7, 0xFB, 0xE7, 0xFF,
|
|
||||||
0x00, 0x7C, 0x40, 0x78, 0x83, 0x39, 0xEE,
|
|
||||||
0x19, 0xE7, 0x81, 0x7C, 0x03, 0x78, 0xC7,
|
|
||||||
0x38, 0xFF, 0xFF, 0xBF, 0x7E, 0xFF, 0x38,
|
|
||||||
0xD7, 0x38, 0xFE, 0x31, 0xFF, 0x13, 0xFF,
|
|
||||||
0x83, 0xFF, 0x8F, 0xFF, 0x00, 0x3C, 0x43,
|
|
||||||
0x7C, 0x83, 0xFC, 0x03, 0xFC, 0x03, 0xFC,
|
|
||||||
0x03, 0xFD, 0x02, 0xFC, 0x03, 0xFF, 0xFF,
|
|
||||||
0xBF, 0x7C, 0xFF, 0xFC, 0xFF, 0xFC, 0xFF,
|
|
||||||
0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xFF, 0xFD,
|
|
||||||
0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x04,
|
|
||||||
0xA3, 0xFD, 0xB6, 0x8C, 0x3A, 0x8B, 0x7C,
|
|
||||||
0x99, 0x62, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0xF8, 0x4B, 0xFC, 0xF7, 0xCC,
|
|
||||||
0xF3, 0xCD, 0xFF, 0xCB, 0xFF, 0x00, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x90, 0x3F, 0xD8, 0x46,
|
|
||||||
0x09, 0xF6, 0x0D, 0xF1, 0x12, 0xF4, 0xFF,
|
|
||||||
0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xCF, 0x78,
|
|
||||||
0xBF, 0xD9, 0x7F, 0x9F, 0xFE, 0x9B, 0xEF,
|
|
||||||
0x9B, 0xFF, 0x00, 0x00, 0xFC, 0x02, 0xFC,
|
|
||||||
0x46, 0x59, 0x13, 0xAC, 0x82, 0x68, 0x07,
|
|
||||||
0xFC, 0x14, 0xE8, 0xFF, 0xFF, 0xFF, 0x03,
|
|
||||||
0xFF, 0x02, 0xBF, 0x73, 0x5F, 0xB6, 0xFF,
|
|
||||||
0x23, 0xFB, 0x07, 0xFF, 0x26, 0xFF, 0x00,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x03, 0x9F, 0x21,
|
|
||||||
0x55, 0x48, 0xB7, 0x8F, 0x60, 0x80, 0x6F,
|
|
||||||
0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFC,
|
|
||||||
0x27, 0xBA, 0xCD, 0x7F, 0x9D, 0xFF, 0x8F,
|
|
||||||
0xF7, 0xD8, 0xFF, 0x00, 0x00, 0xFF, 0x0C,
|
|
||||||
0xF3, 0x18, 0xF3, 0xD8, 0x2F, 0x90, 0x67,
|
|
||||||
0xB0, 0x4F, 0x10, 0xEF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0x08, 0xFF, 0x18, 0xEF, 0xBC, 0xF7, 0xBC,
|
|
||||||
0xFF, 0xD0, 0xFF, 0xD8, 0xFF, 0x30, 0xFF,
|
|
||||||
0x00, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
|
|
||||||
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F,
|
|
||||||
0x80, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0x7F,
|
|
||||||
0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF,
|
|
||||||
0x7F, 0xFF, 0x7F, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xFF,
|
|
||||||
0x02, 0xFF, 0x04, 0xFF, 0x08, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
|
||||||
0xFF, 0xFD, 0xFF, 0xFB, 0xFF, 0xF7, 0xFF,
|
|
||||||
0xF7, 0x48, 0xF7, 0x48, 0xEF, 0x90, 0xEF,
|
|
||||||
0x90, 0xDF, 0x20, 0xBF, 0x40, 0xBF, 0x40,
|
|
||||||
0x7F, 0x80, 0xBF, 0xF0, 0xBF, 0xF0, 0x7F,
|
|
||||||
0xE0, 0x7F, 0xE0, 0xFF, 0xC0, 0xFF, 0x80,
|
|
||||||
0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x10, 0xFF,
|
|
||||||
0x10, 0xFF, 0x10, 0xFF, 0x10, 0xFF, 0x10,
|
|
||||||
0xFF, 0x10, 0xFF, 0x10, 0xBF, 0x48, 0xEF,
|
|
||||||
0x7F, 0xEF, 0x7F, 0xEF, 0x7F, 0xEF, 0x7F,
|
|
||||||
0xEF, 0x7F, 0xEF, 0x7F, 0xEF, 0x7F, 0xF7,
|
|
||||||
0x3F, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFE, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x0F,
|
|
||||||
0xF8, 0x07, 0x00, 0x57, 0x01, 0xFF, 0x05,
|
|
||||||
0xF8, 0x87, 0x48, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0xF8, 0xFF, 0xFC, 0xEF, 0x99, 0xFE,
|
|
||||||
0x01, 0xFF, 0x83, 0xB7, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xC0, 0x3F, 0x80, 0x7F, 0x80,
|
|
||||||
0x7F, 0x0F, 0x70, 0x8F, 0x70, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xE7, 0xBF,
|
|
||||||
0xC0, 0xFF, 0xCF, 0xFF, 0x9F, 0xEF, 0x1F,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x18,
|
|
||||||
0xE3, 0x18, 0xE3, 0x98, 0x77, 0x08, 0x67,
|
|
||||||
0x1D, 0xE2, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x3C, 0xFF, 0x18, 0xEF, 0x1C,
|
|
||||||
0xFF, 0x0C, 0x7F, 0x88, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0x01, 0x3E, 0xC2, 0xFF,
|
|
||||||
0xC2, 0x3C, 0xE2, 0x18, 0xC6, 0x39, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x8B,
|
|
||||||
0x3C, 0xC3, 0xFF, 0xC7, 0xFF, 0xC6, 0xFF,
|
|
||||||
0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0x18, 0xEF, 0x10, 0xE6, 0x10, 0xC6, 0x30,
|
|
||||||
0xEF, 0x30, 0xCF, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xF7, 0x39, 0xFF, 0x39, 0xFF,
|
|
||||||
0x10, 0xDF, 0x38, 0xFF, 0x38, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0x0C, 0x09, 0xFC,
|
|
||||||
0x01, 0x0C, 0x0B, 0x04, 0xFB, 0x06, 0xF1,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xF7,
|
|
||||||
0x0E, 0xFF, 0xFC, 0xF7, 0x0E, 0xFF, 0x0E,
|
|
||||||
0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0x00, 0xCE, 0x70, 0xCF, 0x70, 0xFE,
|
|
||||||
0x01, 0xFE, 0x0B, 0xF0, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x69, 0xB7, 0x79,
|
|
||||||
0x8F, 0x79, 0xFF, 0x03, 0xFF, 0x03, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x70,
|
|
||||||
0x87, 0x78, 0x88, 0x72, 0x80, 0x7F, 0xC0,
|
|
||||||
0x3F, 0xFC, 0x05, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x9F, 0xF7, 0x8F, 0xFD, 0xC7, 0xBF,
|
|
||||||
0xC0, 0xDF, 0xF0, 0xFA, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xE7, 0x18, 0x87, 0x38, 0x17,
|
|
||||||
0xE8, 0x1F, 0xF0, 0x3F, 0xC0, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xC7, 0xFF,
|
|
||||||
0x8F, 0xF7, 0x8F, 0xEF, 0x1F, 0xFF, 0x7F,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87,
|
|
||||||
0x78, 0x8F, 0x70, 0xDF, 0x20, 0x8F, 0x70,
|
|
||||||
0x8F, 0x70, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xF7, 0xCF, 0xFF, 0xCF, 0xFF, 0x8F,
|
|
||||||
0xFF, 0x9F, 0xFF, 0x9F, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFC, 0x02, 0xFD, 0x03,
|
|
||||||
0xFD, 0x02, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFC,
|
|
||||||
0xFE, 0xFD, 0xFF, 0xFD, 0xFF, 0xFD, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0x9F, 0x30, 0xA0, 0x9E, 0x00, 0x7F, 0x00,
|
|
||||||
0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xEF, 0xF9, 0x6F, 0xF8, 0xFF,
|
|
||||||
0x00, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0x90, 0xCF, 0xE1,
|
|
||||||
0x1E, 0x40, 0xBF, 0x00, 0xFF, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7C,
|
|
||||||
0xDB, 0xFF, 0xF3, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0x3D, 0x82, 0x86, 0x79, 0x80, 0x7F,
|
|
||||||
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0xA6, 0xBF, 0xEC,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x27,
|
|
||||||
0xD6, 0xA0, 0x00, 0xFF, 0x00, 0xFF, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFE, 0xDF, 0x7F, 0xCE, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0x18, 0x47, 0x10, 0xC7, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x10, 0xFF,
|
|
||||||
0x18, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F,
|
|
||||||
0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
|
|
||||||
0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xFF,
|
|
||||||
0x02, 0xFF, 0x0C, 0xFF, 0x10, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
|
||||||
0xFF, 0xFD, 0xFF, 0xF3, 0xFF, 0xEF, 0xFF,
|
|
||||||
0xFE, 0x11, 0xFD, 0x22, 0xFB, 0x44, 0xF7,
|
|
||||||
0x88, 0xEF, 0x10, 0xDF, 0x20, 0xBF, 0x40,
|
|
||||||
0x7F, 0x80, 0xEF, 0xFE, 0xDF, 0xFC, 0xBF,
|
|
||||||
0xF8, 0x7F, 0xF0, 0xFF, 0xE0, 0xFF, 0xC0,
|
|
||||||
0xFF, 0x80, 0xFF, 0x00, 0xBF, 0x48, 0xDF,
|
|
||||||
0x24, 0xDF, 0x22, 0xEF, 0x11, 0xF7, 0x08,
|
|
||||||
0xF9, 0x06, 0xFE, 0x01, 0xFF, 0x00, 0xF7,
|
|
||||||
0x3F, 0xFB, 0x1F, 0xFD, 0x1F, 0xFE, 0x0F,
|
|
||||||
0xFF, 0x07, 0xFF, 0x01, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x80, 0xFF, 0x7F, 0xFF, 0x00, 0x7F,
|
|
||||||
0x80, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0x7F, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x03, 0xFF, 0xFC, 0xFF, 0x00,
|
|
||||||
0xFC, 0x03, 0x03, 0xFC, 0xFF, 0xFF, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x03, 0xFF,
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x03, 0xFF, 0x1C, 0xFF, 0xE0,
|
|
||||||
0xFC, 0x03, 0xE3, 0x1C, 0x1F, 0xE0, 0xFF,
|
|
||||||
0x00, 0xFF, 0xFF, 0xFC, 0xFF, 0xE3, 0xFF,
|
|
||||||
0x1F, 0xFF, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFC, 0xE3, 0xF3, 0x0C,
|
|
||||||
0xEF, 0x10, 0x1F, 0xE0, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0x1F, 0xFC,
|
|
||||||
0xFF, 0xF0, 0xFF, 0xE0, 0xFF, 0x00, 0xFF,
|
|
||||||
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
|
||||||
};
|
|
@ -12,7 +12,7 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
|||||||
previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
|
previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
|
||||||
key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3;
|
key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3;
|
||||||
gb->io_registers[GB_IO_JOYP] &= 0xF0;
|
gb->io_registers[GB_IO_JOYP] &= 0xF0;
|
||||||
uint8_t current_player = gb->sgb? gb->sgb->current_player : 0;
|
uint8_t current_player = gb->sgb? (gb->sgb->current_player & (gb->sgb->player_count - 1) & 3) : 0;
|
||||||
switch (key_selection) {
|
switch (key_selection) {
|
||||||
case 3:
|
case 3:
|
||||||
if (gb->sgb && gb->sgb->player_count > 1) {
|
if (gb->sgb && gb->sgb->player_count > 1) {
|
||||||
@ -30,13 +30,11 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
|||||||
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[current_player][i]) << i;
|
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[current_player][i]) << i;
|
||||||
}
|
}
|
||||||
/* Forbid pressing two opposing keys, this breaks a lot of games; even if it's somewhat possible. */
|
/* Forbid pressing two opposing keys, this breaks a lot of games; even if it's somewhat possible. */
|
||||||
if (likely(!gb->illegal_inputs_allowed)) {
|
if (!(gb->io_registers[GB_IO_JOYP] & 1)) {
|
||||||
if (!(gb->io_registers[GB_IO_JOYP] & 1)) {
|
gb->io_registers[GB_IO_JOYP] |= 2;
|
||||||
gb->io_registers[GB_IO_JOYP] |= 2;
|
}
|
||||||
}
|
if (!(gb->io_registers[GB_IO_JOYP] & 4)) {
|
||||||
if (!(gb->io_registers[GB_IO_JOYP] & 4)) {
|
gb->io_registers[GB_IO_JOYP] |= 8;
|
||||||
gb->io_registers[GB_IO_JOYP] |= 8;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -53,7 +51,8 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
nodefault;
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Todo: This assumes the keys *always* bounce, which is incorrect when emulating an SGB */
|
/* Todo: This assumes the keys *always* bounce, which is incorrect when emulating an SGB */
|
||||||
@ -91,48 +90,3 @@ void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned play
|
|||||||
gb->keys[player][index] = pressed;
|
gb->keys[player][index] = pressed;
|
||||||
GB_update_joyp(gb);
|
GB_update_joyp(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GB_set_key_mask(GB_gameboy_t *gb, GB_key_mask_t mask)
|
|
||||||
{
|
|
||||||
memset(gb->keys, 0, sizeof(gb->keys));
|
|
||||||
bool *key = &gb->keys[0][0];
|
|
||||||
while (mask) {
|
|
||||||
if (mask & 1) {
|
|
||||||
*key = true;
|
|
||||||
}
|
|
||||||
mask >>= 1;
|
|
||||||
key++;
|
|
||||||
}
|
|
||||||
|
|
||||||
GB_update_joyp(gb);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GB_set_key_mask_for_player(GB_gameboy_t *gb, GB_key_mask_t mask, unsigned player)
|
|
||||||
{
|
|
||||||
memset(gb->keys[player], 0, sizeof(gb->keys[player]));
|
|
||||||
bool *key = gb->keys[player];
|
|
||||||
while (mask) {
|
|
||||||
if (mask & 1) {
|
|
||||||
*key = true;
|
|
||||||
}
|
|
||||||
mask >>= 1;
|
|
||||||
key++;
|
|
||||||
}
|
|
||||||
|
|
||||||
GB_update_joyp(gb);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GB_get_joyp_accessed(GB_gameboy_t *gb)
|
|
||||||
{
|
|
||||||
return gb->joyp_accessed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GB_clear_joyp_accessed(GB_gameboy_t *gb)
|
|
||||||
{
|
|
||||||
gb->joyp_accessed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GB_set_allow_illegal_inputs(GB_gameboy_t *gb, bool allow)
|
|
||||||
{
|
|
||||||
gb->illegal_inputs_allowed = allow;
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#ifndef joypad_h
|
#ifndef joypad_h
|
||||||
#define joypad_h
|
#define joypad_h
|
||||||
#include "defs.h"
|
#include "gb_struct_def.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -15,31 +15,11 @@ typedef enum {
|
|||||||
GB_KEY_MAX
|
GB_KEY_MAX
|
||||||
} GB_key_t;
|
} GB_key_t;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
GB_KEY_RIGHT_MASK = 1 << GB_KEY_RIGHT,
|
|
||||||
GB_KEY_LEFT_MASK = 1 << GB_KEY_LEFT,
|
|
||||||
GB_KEY_UP_MASK = 1 << GB_KEY_UP,
|
|
||||||
GB_KEY_DOWN_MASK = 1 << GB_KEY_DOWN,
|
|
||||||
GB_KEY_A_MASK = 1 << GB_KEY_A,
|
|
||||||
GB_KEY_B_MASK = 1 << GB_KEY_B,
|
|
||||||
GB_KEY_SELECT_MASK = 1 << GB_KEY_SELECT,
|
|
||||||
GB_KEY_START_MASK = 1 << GB_KEY_START,
|
|
||||||
} GB_key_mask_t;
|
|
||||||
|
|
||||||
// For example, for player 2's (0-based; logical player 3) A button, use GB_MASK_FOR_PLAYER(GB_KEY_A_MASK, 2)
|
|
||||||
#define GB_MASK_FOR_PLAYER(mask, player) ((x) << (player * 8))
|
|
||||||
|
|
||||||
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed);
|
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed);
|
||||||
void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed);
|
void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed);
|
||||||
void GB_set_key_mask(GB_gameboy_t *gb, GB_key_mask_t mask);
|
|
||||||
void GB_set_key_mask_for_player(GB_gameboy_t *gb, GB_key_mask_t mask, unsigned player);
|
|
||||||
void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value);
|
void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value);
|
||||||
bool GB_get_joyp_accessed(GB_gameboy_t *gb);
|
|
||||||
void GB_clear_joyp_accessed(GB_gameboy_t *gb);
|
|
||||||
void GB_set_allow_illegal_inputs(GB_gameboy_t *gb, bool allow);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
internal void GB_update_joyp(GB_gameboy_t *gb);
|
void GB_update_joyp(GB_gameboy_t *gb);
|
||||||
#endif
|
#endif
|
||||||
#endif /* joypad_h */
|
#endif /* joypad_h */
|
||||||
|
49
Core/mbc.c
@ -34,8 +34,6 @@ const GB_cartridge_t GB_cart_defs[256] = {
|
|||||||
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, true }, // 1Ch MBC5+RUMBLE
|
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, true }, // 1Ch MBC5+RUMBLE
|
||||||
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM
|
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM
|
||||||
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, true }, // 1Eh MBC5+RUMBLE+RAM+BATTERY
|
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, true }, // 1Eh MBC5+RUMBLE+RAM+BATTERY
|
||||||
[0x22] =
|
|
||||||
{ GB_MBC7 , GB_STANDARD_MBC, true, true, false, false}, // 22h MBC7+ACCEL+EEPROM
|
|
||||||
[0xFC] =
|
[0xFC] =
|
||||||
{ GB_MBC5 , GB_CAMERA , true , true , false, false}, // FCh POCKET CAMERA
|
{ GB_MBC5 , GB_CAMERA , true , true , false, false}, // FCh POCKET CAMERA
|
||||||
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // FDh BANDAI TAMA5 (Todo: Not supported)
|
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // FDh BANDAI TAMA5 (Todo: Not supported)
|
||||||
@ -77,7 +75,6 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb)
|
|||||||
gb->mbc_rom_bank++;
|
gb->mbc_rom_bank++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
nodefault;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GB_MBC2:
|
case GB_MBC2:
|
||||||
@ -91,6 +88,7 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb)
|
|||||||
gb->mbc_ram_bank = gb->mbc3.ram_bank;
|
gb->mbc_ram_bank = gb->mbc3.ram_bank;
|
||||||
if (!gb->is_mbc30) {
|
if (!gb->is_mbc30) {
|
||||||
gb->mbc_rom_bank &= 0x7F;
|
gb->mbc_rom_bank &= 0x7F;
|
||||||
|
gb->mbc_ram_bank &= 0x3;
|
||||||
}
|
}
|
||||||
if (gb->mbc_rom_bank == 0) {
|
if (gb->mbc_rom_bank == 0) {
|
||||||
gb->mbc_rom_bank = 1;
|
gb->mbc_rom_bank = 1;
|
||||||
@ -100,9 +98,6 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb)
|
|||||||
gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8);
|
gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8);
|
||||||
gb->mbc_ram_bank = gb->mbc5.ram_bank;
|
gb->mbc_ram_bank = gb->mbc5.ram_bank;
|
||||||
break;
|
break;
|
||||||
case GB_MBC7:
|
|
||||||
gb->mbc_rom_bank = gb->mbc7.rom_bank;
|
|
||||||
break;
|
|
||||||
case GB_HUC1:
|
case GB_HUC1:
|
||||||
if (gb->huc1.mode == 0) {
|
if (gb->huc1.mode == 0) {
|
||||||
gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6);
|
gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6);
|
||||||
@ -117,25 +112,12 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb)
|
|||||||
gb->mbc_rom_bank = gb->huc3.rom_bank;
|
gb->mbc_rom_bank = gb->huc3.rom_bank;
|
||||||
gb->mbc_ram_bank = gb->huc3.ram_bank;
|
gb->mbc_ram_bank = gb->huc3.ram_bank;
|
||||||
break;
|
break;
|
||||||
case GB_TPP1:
|
|
||||||
gb->mbc_rom_bank = gb->tpp1.rom_bank;
|
|
||||||
gb->mbc_ram_bank = gb->tpp1.ram_bank;
|
|
||||||
gb->mbc_ram_enable = (gb->tpp1.mode == 2) || (gb->tpp1.mode == 3);
|
|
||||||
break;
|
|
||||||
nodefault;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GB_configure_cart(GB_gameboy_t *gb)
|
void GB_configure_cart(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]];
|
gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]];
|
||||||
if (gb->rom[0x147] == 0xbc &&
|
|
||||||
gb->rom[0x149] == 0xc1 &&
|
|
||||||
gb->rom[0x14a] == 0x65) {
|
|
||||||
static const GB_cartridge_t tpp1 = {GB_TPP1, GB_STANDARD_MBC, true, true, true, true};
|
|
||||||
gb->cartridge_type = &tpp1;
|
|
||||||
gb->tpp1.rom_bank = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) {
|
if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) {
|
||||||
GB_log(gb, "ROM header reports no MBC, but file size is over 32Kb. Assuming cartridge uses MBC3.\n");
|
GB_log(gb, "ROM header reports no MBC, but file size is over 32Kb. Assuming cartridge uses MBC3.\n");
|
||||||
@ -145,34 +127,17 @@ void GB_configure_cart(GB_gameboy_t *gb)
|
|||||||
GB_log(gb, "Cartridge type %02x is not yet supported.\n", gb->rom[0x147]);
|
GB_log(gb, "Cartridge type %02x is not yet supported.\n", gb->rom[0x147]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->mbc_ram) {
|
|
||||||
free(gb->mbc_ram);
|
|
||||||
gb->mbc_ram = NULL;
|
|
||||||
gb->mbc_ram_size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gb->cartridge_type->has_ram) {
|
if (gb->cartridge_type->has_ram) {
|
||||||
if (gb->cartridge_type->mbc_type == GB_MBC2) {
|
if (gb->cartridge_type->mbc_type == GB_MBC2) {
|
||||||
gb->mbc_ram_size = 0x200;
|
gb->mbc_ram_size = 0x200;
|
||||||
}
|
}
|
||||||
else if (gb->cartridge_type->mbc_type == GB_MBC7) {
|
|
||||||
gb->mbc_ram_size = 0x100;
|
|
||||||
}
|
|
||||||
else if (gb->cartridge_type->mbc_type == GB_TPP1) {
|
|
||||||
if (gb->rom[0x152] >= 1 && gb->rom[0x152] <= 9) {
|
|
||||||
gb->mbc_ram_size = 0x2000 << (gb->rom[0x152] - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
static const unsigned ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000};
|
static const unsigned ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000};
|
||||||
gb->mbc_ram_size = ram_sizes[gb->rom[0x149]];
|
gb->mbc_ram_size = ram_sizes[gb->rom[0x149]];
|
||||||
}
|
}
|
||||||
|
gb->mbc_ram = malloc(gb->mbc_ram_size);
|
||||||
|
|
||||||
if (gb->mbc_ram_size) {
|
/* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridges types? */
|
||||||
gb->mbc_ram = malloc(gb->mbc_ram_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridge types? */
|
|
||||||
memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size);
|
memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,12 +162,4 @@ void GB_configure_cart(GB_gameboy_t *gb)
|
|||||||
if (gb->cartridge_type->mbc_type == GB_MBC5) {
|
if (gb->cartridge_type->mbc_type == GB_MBC5) {
|
||||||
gb->mbc5.rom_bank_low = 1;
|
gb->mbc5.rom_bank_low = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initial MBC7 state */
|
|
||||||
if (gb->cartridge_type->mbc_type == GB_MBC7) {
|
|
||||||
gb->mbc7.x_latch = gb->mbc7.y_latch = 0x8000;
|
|
||||||
gb->mbc7.latch_ready = true;
|
|
||||||
gb->mbc7.read_bits = -1;
|
|
||||||
gb->mbc7.eeprom_do = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#ifndef MBC_h
|
#ifndef MBC_h
|
||||||
#define MBC_h
|
#define MBC_h
|
||||||
#include "defs.h"
|
#include "gb_struct_def.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -10,10 +10,8 @@ typedef struct {
|
|||||||
GB_MBC2,
|
GB_MBC2,
|
||||||
GB_MBC3,
|
GB_MBC3,
|
||||||
GB_MBC5,
|
GB_MBC5,
|
||||||
GB_MBC7,
|
|
||||||
GB_HUC1,
|
GB_HUC1,
|
||||||
GB_HUC3,
|
GB_HUC3,
|
||||||
GB_TPP1,
|
|
||||||
} mbc_type;
|
} mbc_type;
|
||||||
enum {
|
enum {
|
||||||
GB_STANDARD_MBC,
|
GB_STANDARD_MBC,
|
||||||
@ -27,8 +25,8 @@ typedef struct {
|
|||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
extern const GB_cartridge_t GB_cart_defs[256];
|
extern const GB_cartridge_t GB_cart_defs[256];
|
||||||
internal void GB_update_mbc_mappings(GB_gameboy_t *gb);
|
void GB_update_mbc_mappings(GB_gameboy_t *gb);
|
||||||
internal void GB_configure_cart(GB_gameboy_t *gb);
|
void GB_configure_cart(GB_gameboy_t *gb);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* MBC_h */
|
#endif /* MBC_h */
|
||||||
|
1086
Core/memory.c
@ -1,20 +1,18 @@
|
|||||||
#ifndef memory_h
|
#ifndef memory_h
|
||||||
#define memory_h
|
#define memory_h
|
||||||
#include "defs.h"
|
#include "gb_struct_def.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef uint8_t (*GB_read_memory_callback_t)(GB_gameboy_t *gb, uint16_t addr, uint8_t data);
|
typedef uint8_t (*GB_read_memory_callback_t)(GB_gameboy_t *gb, uint16_t addr, uint8_t data);
|
||||||
typedef bool (*GB_write_memory_callback_t)(GB_gameboy_t *gb, uint16_t addr, uint8_t data); // Return false to prevent the write
|
|
||||||
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback);
|
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback);
|
||||||
void GB_set_write_memory_callback(GB_gameboy_t *gb, GB_write_memory_callback_t callback);
|
|
||||||
|
|
||||||
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr);
|
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr);
|
||||||
uint8_t GB_safe_read_memory(GB_gameboy_t *gb, uint16_t addr); // Without side effects
|
|
||||||
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
internal void GB_dma_run(GB_gameboy_t *gb);
|
void GB_dma_run(GB_gameboy_t *gb);
|
||||||
internal void GB_hdma_run(GB_gameboy_t *gb);
|
void GB_hdma_run(GB_gameboy_t *gb);
|
||||||
internal void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address);
|
void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address);
|
||||||
|
void GB_trigger_oam_bug_read_increase(GB_gameboy_t *gb, uint16_t address);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* memory_h */
|
#endif /* memory_h */
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
static void handle_command(GB_gameboy_t *gb)
|
static void handle_command(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
|
|
||||||
switch (gb->printer.command_id) {
|
switch (gb->printer.command_id) {
|
||||||
case GB_PRINTER_INIT_COMMAND:
|
case GB_PRINTER_INIT_COMMAND:
|
||||||
gb->printer.status = 0;
|
gb->printer.status = 0;
|
||||||
@ -30,8 +31,8 @@ static void handle_command(GB_gameboy_t *gb)
|
|||||||
image[i] = colors[(palette >> (gb->printer.image[i] * 2)) & 3];
|
image[i] = colors[(palette >> (gb->printer.image[i] * 2)) & 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->printer_callback) {
|
if (gb->printer.callback) {
|
||||||
gb->printer_callback(gb, image, gb->printer.image_offset / 160,
|
gb->printer.callback(gb, image, gb->printer.image_offset / 160,
|
||||||
gb->printer.command_data[1] >> 4, gb->printer.command_data[1] & 7,
|
gb->printer.command_data[1] >> 4, gb->printer.command_data[1] & 7,
|
||||||
gb->printer.command_data[3] & 0x7F);
|
gb->printer.command_data[3] & 0x7F);
|
||||||
}
|
}
|
||||||
@ -70,7 +71,7 @@ static void handle_command(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void byte_recieve_completed(GB_gameboy_t *gb, uint8_t byte_received)
|
static void byte_reieve_completed(GB_gameboy_t *gb, uint8_t byte_received)
|
||||||
{
|
{
|
||||||
gb->printer.byte_to_send = 0;
|
gb->printer.byte_to_send = 0;
|
||||||
switch (gb->printer.command_state) {
|
switch (gb->printer.command_state) {
|
||||||
@ -188,18 +189,13 @@ static void byte_recieve_completed(GB_gameboy_t *gb, uint8_t byte_received)
|
|||||||
|
|
||||||
static void serial_start(GB_gameboy_t *gb, bool bit_received)
|
static void serial_start(GB_gameboy_t *gb, bool bit_received)
|
||||||
{
|
{
|
||||||
if (gb->printer.idle_time > GB_get_unmultiplied_clock_rate(gb)) {
|
gb->printer.byte_being_recieved <<= 1;
|
||||||
gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
|
gb->printer.byte_being_recieved |= bit_received;
|
||||||
gb->printer.bits_received = 0;
|
gb->printer.bits_recieved++;
|
||||||
}
|
if (gb->printer.bits_recieved == 8) {
|
||||||
gb->printer.idle_time = 0;
|
byte_reieve_completed(gb, gb->printer.byte_being_recieved);
|
||||||
gb->printer.byte_being_received <<= 1;
|
gb->printer.bits_recieved = 0;
|
||||||
gb->printer.byte_being_received |= bit_received;
|
gb->printer.byte_being_recieved = 0;
|
||||||
gb->printer.bits_received++;
|
|
||||||
if (gb->printer.bits_received == 8) {
|
|
||||||
byte_recieve_completed(gb, gb->printer.byte_being_received);
|
|
||||||
gb->printer.bits_received = 0;
|
|
||||||
gb->printer.byte_being_received = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,5 +212,5 @@ void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback)
|
|||||||
memset(&gb->printer, 0, sizeof(gb->printer));
|
memset(&gb->printer, 0, sizeof(gb->printer));
|
||||||
GB_set_serial_transfer_bit_start_callback(gb, serial_start);
|
GB_set_serial_transfer_bit_start_callback(gb, serial_start);
|
||||||
GB_set_serial_transfer_bit_end_callback(gb, serial_end);
|
GB_set_serial_transfer_bit_end_callback(gb, serial_end);
|
||||||
gb->printer_callback = callback;
|
gb->printer.callback = callback;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#define printer_h
|
#define printer_h
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "defs.h"
|
#include "gb_struct_def.h"
|
||||||
#define GB_PRINTER_MAX_COMMAND_LENGTH 0x280
|
#define GB_PRINTER_MAX_COMMAND_LENGTH 0x280
|
||||||
#define GB_PRINTER_DATA_SIZE 0x280
|
#define GB_PRINTER_DATA_SIZE 0x280
|
||||||
|
|
||||||
@ -48,13 +48,13 @@ typedef struct
|
|||||||
uint8_t image[160 * 200];
|
uint8_t image[160 * 200];
|
||||||
uint16_t image_offset;
|
uint16_t image_offset;
|
||||||
|
|
||||||
uint64_t idle_time;
|
GB_print_image_callback_t callback;
|
||||||
|
|
||||||
uint8_t compression_run_lenth;
|
uint8_t compression_run_lenth;
|
||||||
bool compression_run_is_compressed;
|
bool compression_run_is_compressed;
|
||||||
|
|
||||||
uint8_t bits_received;
|
uint8_t bits_recieved;
|
||||||
uint8_t byte_being_received;
|
uint8_t byte_being_recieved;
|
||||||
bool bit_to_send;
|
bool bit_to_send;
|
||||||
} GB_printer_t;
|
} GB_printer_t;
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ static void state_decompress(const uint8_t *prev, uint8_t *data, uint8_t *dest,
|
|||||||
|
|
||||||
void GB_rewind_push(GB_gameboy_t *gb)
|
void GB_rewind_push(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
const size_t save_size = GB_get_save_state_size_no_bess(gb);
|
const size_t save_size = GB_get_save_state_size(gb);
|
||||||
if (!gb->rewind_sequences) {
|
if (!gb->rewind_sequences) {
|
||||||
if (gb->rewind_buffer_length) {
|
if (gb->rewind_buffer_length) {
|
||||||
gb->rewind_sequences = malloc(sizeof(*gb->rewind_sequences) * gb->rewind_buffer_length);
|
gb->rewind_sequences = malloc(sizeof(*gb->rewind_sequences) * gb->rewind_buffer_length);
|
||||||
@ -140,11 +140,11 @@ void GB_rewind_push(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
if (!gb->rewind_sequences[gb->rewind_pos].key_state) {
|
if (!gb->rewind_sequences[gb->rewind_pos].key_state) {
|
||||||
gb->rewind_sequences[gb->rewind_pos].key_state = malloc(save_size);
|
gb->rewind_sequences[gb->rewind_pos].key_state = malloc(save_size);
|
||||||
GB_save_state_to_buffer_no_bess(gb, gb->rewind_sequences[gb->rewind_pos].key_state);
|
GB_save_state_to_buffer(gb, gb->rewind_sequences[gb->rewind_pos].key_state);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
uint8_t *save_state = malloc(save_size);
|
uint8_t *save_state = malloc(save_size);
|
||||||
GB_save_state_to_buffer_no_bess(gb, save_state);
|
GB_save_state_to_buffer(gb, save_state);
|
||||||
gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos++] =
|
gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos++] =
|
||||||
state_compress(gb->rewind_sequences[gb->rewind_pos].key_state, save_state, save_size);
|
state_compress(gb->rewind_sequences[gb->rewind_pos].key_state, save_state, save_size);
|
||||||
free(save_state);
|
free(save_state);
|
||||||
@ -158,7 +158,7 @@ bool GB_rewind_pop(GB_gameboy_t *gb)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t save_size = GB_get_save_state_size_no_bess(gb);
|
const size_t save_size = GB_get_save_state_size(gb);
|
||||||
if (gb->rewind_sequences[gb->rewind_pos].pos == 0) {
|
if (gb->rewind_sequences[gb->rewind_pos].pos == 0) {
|
||||||
GB_load_state_from_buffer(gb, gb->rewind_sequences[gb->rewind_pos].key_state, save_size);
|
GB_load_state_from_buffer(gb, gb->rewind_sequences[gb->rewind_pos].key_state, save_size);
|
||||||
free(gb->rewind_sequences[gb->rewind_pos].key_state);
|
free(gb->rewind_sequences[gb->rewind_pos].key_state);
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
#define rewind_h
|
#define rewind_h
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "defs.h"
|
#include "gb_struct_def.h"
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
internal void GB_rewind_push(GB_gameboy_t *gb);
|
void GB_rewind_push(GB_gameboy_t *gb);
|
||||||
internal void GB_rewind_free(GB_gameboy_t *gb);
|
void GB_rewind_free(GB_gameboy_t *gb);
|
||||||
#endif
|
#endif
|
||||||
bool GB_rewind_pop(GB_gameboy_t *gb);
|
bool GB_rewind_pop(GB_gameboy_t *gb);
|
||||||
void GB_set_rewind_length(GB_gameboy_t *gb, double seconds);
|
void GB_set_rewind_length(GB_gameboy_t *gb, double seconds);
|
||||||
|
@ -15,8 +15,7 @@ void GB_handle_rumble(GB_gameboy_t *gb)
|
|||||||
if (gb->rumble_mode == GB_RUMBLE_DISABLED) {
|
if (gb->rumble_mode == GB_RUMBLE_DISABLED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (gb->cartridge_type->has_rumble &&
|
if (gb->cartridge_type->has_rumble) {
|
||||||
(gb->cartridge_type->mbc_type != GB_TPP1 || (gb->rom[0x153] & 1))) {
|
|
||||||
if (gb->rumble_on_cycles + gb->rumble_off_cycles) {
|
if (gb->rumble_on_cycles + gb->rumble_off_cycles) {
|
||||||
gb->rumble_callback(gb, gb->rumble_on_cycles / (double)(gb->rumble_on_cycles + gb->rumble_off_cycles));
|
gb->rumble_callback(gb, gb->rumble_on_cycles / (double)(gb->rumble_on_cycles + gb->rumble_off_cycles));
|
||||||
gb->rumble_on_cycles = gb->rumble_off_cycles = 0;
|
gb->rumble_on_cycles = gb->rumble_off_cycles = 0;
|
||||||
@ -26,17 +25,14 @@ void GB_handle_rumble(GB_gameboy_t *gb)
|
|||||||
unsigned volume = (gb->io_registers[GB_IO_NR50] & 7) + 1 + ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1;
|
unsigned volume = (gb->io_registers[GB_IO_NR50] & 7) + 1 + ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1;
|
||||||
unsigned ch4_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 8) + !!(gb->io_registers[GB_IO_NR51] & 0x80));
|
unsigned ch4_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 8) + !!(gb->io_registers[GB_IO_NR51] & 0x80));
|
||||||
unsigned ch1_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 1) + !!(gb->io_registers[GB_IO_NR51] & 0x10));
|
unsigned ch1_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 1) + !!(gb->io_registers[GB_IO_NR51] & 0x10));
|
||||||
unsigned ch4_divisor = (gb->io_registers[GB_IO_NR43] & 0x07) << 1;
|
|
||||||
if (!ch4_divisor) ch4_divisor = 1;
|
|
||||||
unsigned ch4_sample_length = (ch4_divisor << (gb->io_registers[GB_IO_NR43] >> 4)) - 1;
|
|
||||||
|
|
||||||
double ch4_rumble = (MIN(ch4_sample_length * (gb->apu.noise_channel.narrow? 8 : 1) , 4096) * ((signed) gb->apu.noise_channel.current_volume * gb->apu.noise_channel.current_volume * ch4_volume / 32.0 - 50) - 2048) / 2048.0;
|
double ch4_rumble = (MIN(gb->apu.noise_channel.sample_length * (gb->apu.noise_channel.narrow? 8 : 1) , 4096) * ((signed) gb->apu.noise_channel.current_volume * gb->apu.noise_channel.current_volume * ch4_volume / 32.0 - 50) - 2048) / 2048.0;
|
||||||
|
|
||||||
ch4_rumble = MIN(ch4_rumble, 1.0);
|
ch4_rumble = MIN(ch4_rumble, 1.0);
|
||||||
ch4_rumble = MAX(ch4_rumble, 0.0);
|
ch4_rumble = MAX(ch4_rumble, 0.0);
|
||||||
|
|
||||||
double ch1_rumble = 0;
|
double ch1_rumble = 0;
|
||||||
if ((gb->io_registers[GB_IO_NR10] & 0x7) && (gb->io_registers[GB_IO_NR10] & 0x70)) {
|
if (gb->apu.sweep_enabled && ((gb->io_registers[GB_IO_NR10] >> 4) & 7)) {
|
||||||
double sweep_speed = (gb->io_registers[GB_IO_NR10] & 7) / (double)((gb->io_registers[GB_IO_NR10] >> 4) & 7);
|
double sweep_speed = (gb->io_registers[GB_IO_NR10] & 7) / (double)((gb->io_registers[GB_IO_NR10] >> 4) & 7);
|
||||||
ch1_rumble = gb->apu.square_channels[GB_SQUARE_1].current_volume * ch1_volume / 32.0 * sweep_speed / 8.0 - 0.5;
|
ch1_rumble = gb->apu.square_channels[GB_SQUARE_1].current_volume * ch1_volume / 32.0 * sweep_speed / 8.0 - 0.5;
|
||||||
ch1_rumble = MIN(ch1_rumble, 1.0);
|
ch1_rumble = MIN(ch1_rumble, 1.0);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef rumble_h
|
#ifndef rumble_h
|
||||||
#define rumble_h
|
#define rumble_h
|
||||||
|
|
||||||
#include "defs.h"
|
#include "gb_struct_def.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GB_RUMBLE_DISABLED,
|
GB_RUMBLE_DISABLED,
|
||||||
@ -10,7 +10,7 @@ typedef enum {
|
|||||||
} GB_rumble_mode_t;
|
} GB_rumble_mode_t;
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
internal void GB_handle_rumble(GB_gameboy_t *gb);
|
void GB_handle_rumble(GB_gameboy_t *gb);
|
||||||
#endif
|
#endif
|
||||||
void GB_set_rumble_mode(GB_gameboy_t *gb, GB_rumble_mode_t mode);
|
void GB_set_rumble_mode(GB_gameboy_t *gb, GB_rumble_mode_t mode);
|
||||||
|
|
||||||
|
1448
Core/save_state.c
@ -5,16 +5,10 @@
|
|||||||
|
|
||||||
#define GB_PADDING(type, old_usage) type old_usage##__do_not_use
|
#define GB_PADDING(type, old_usage) type old_usage##__do_not_use
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#define GB_SECTION(name, ...) __attribute__ ((aligned (8))) struct {} name##_section_start; __VA_ARGS__; struct {} name##_section_end
|
||||||
/* For bsnes integration. C++ code does not need section information, and throws a fit over certain types such
|
|
||||||
as anonymous enums inside unions */
|
|
||||||
#define GB_SECTION(name, ...) __attribute__ ((aligned (8))) __VA_ARGS__
|
|
||||||
#else
|
|
||||||
#define GB_SECTION(name, ...) union __attribute__ ((aligned (8))) {uint8_t name##_section_start; struct {__VA_ARGS__};}; uint8_t name##_section_end[0]
|
|
||||||
#define GB_SECTION_OFFSET(name) (offsetof(GB_gameboy_t, name##_section_start))
|
#define GB_SECTION_OFFSET(name) (offsetof(GB_gameboy_t, name##_section_start))
|
||||||
#define GB_SECTION_SIZE(name) (offsetof(GB_gameboy_t, name##_section_end) - offsetof(GB_gameboy_t, name##_section_start))
|
#define GB_SECTION_SIZE(name) (offsetof(GB_gameboy_t, name##_section_end) - offsetof(GB_gameboy_t, name##_section_start))
|
||||||
#define GB_GET_SECTION(gb, name) ((void*)&((gb)->name##_section_start))
|
#define GB_GET_SECTION(gb, name) ((void*)&((gb)->name##_section_start))
|
||||||
#endif
|
|
||||||
|
|
||||||
#define GB_aligned_double __attribute__ ((aligned (8))) double
|
#define GB_aligned_double __attribute__ ((aligned (8))) double
|
||||||
|
|
||||||
@ -27,17 +21,4 @@ void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer);
|
|||||||
|
|
||||||
int GB_load_state(GB_gameboy_t *gb, const char *path);
|
int GB_load_state(GB_gameboy_t *gb, const char *path);
|
||||||
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length);
|
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length);
|
||||||
bool GB_is_save_state(const char *path);
|
|
||||||
#ifdef GB_INTERNAL
|
|
||||||
static inline uint32_t state_magic(void)
|
|
||||||
{
|
|
||||||
if (sizeof(bool) == 1) return 'SAME';
|
|
||||||
return 'S4ME';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* For internal in-memory save states (rewind, debugger) that do not need BESS */
|
|
||||||
internal size_t GB_get_save_state_size_no_bess(GB_gameboy_t *gb);
|
|
||||||
internal void GB_save_state_to_buffer_no_bess(GB_gameboy_t *gb, uint8_t *buffer);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* save_state_h */
|
#endif /* save_state_h */
|
||||||
|
261
Core/sgb.c
@ -7,6 +7,8 @@
|
|||||||
#define M_PI 3.14159265358979323846
|
#define M_PI 3.14159265358979323846
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define INTRO_ANIMATION_LENGTH 200
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
PAL01 = 0x00,
|
PAL01 = 0x00,
|
||||||
PAL23 = 0x01,
|
PAL23 = 0x01,
|
||||||
@ -47,14 +49,14 @@ 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[0] = gb->sgb->effective_palettes[4] =
|
||||||
gb->sgb->effective_palettes[8] = gb->sgb->effective_palettes[12] =
|
gb->sgb->effective_palettes[8] = gb->sgb->effective_palettes[12] =
|
||||||
*(uint16_t *)&gb->sgb->command[1];
|
gb->sgb->command[1] | (gb->sgb->command[2] << 8);
|
||||||
|
|
||||||
for (unsigned i = 0; i < 3; i++) {
|
for (unsigned i = 0; i < 3; i++) {
|
||||||
gb->sgb->effective_palettes[first * 4 + i + 1] = *(uint16_t *)&gb->sgb->command[3 + i * 2];
|
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++) {
|
for (unsigned i = 0; i < 3; i++) {
|
||||||
gb->sgb->effective_palettes[second * 4 + i + 1] = *(uint16_t *)&gb->sgb->command[9 + i * 2];
|
gb->sgb->effective_palettes[second * 4 + i + 1] = gb->sgb->command[9 + i * 2] | (gb->sgb->command[10 + i * 2] << 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,10 +172,10 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
gb->sgb->disable_commands = true;
|
gb->sgb->disable_commands = true;
|
||||||
for (unsigned i = 0; i < sizeof(palette_assignments) / sizeof(palette_assignments[0]); i++) {
|
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) {
|
if (memcmp(palette_assignments[i].name, &gb->sgb->received_header[0x30], sizeof(palette_assignments[i].name)) == 0) {
|
||||||
gb->sgb->effective_palettes[0] = LE16(built_in_palettes[palette_assignments[i].palette_index * 4 - 4]);
|
gb->sgb->effective_palettes[0] = built_in_palettes[palette_assignments[i].palette_index * 4 - 4];
|
||||||
gb->sgb->effective_palettes[1] = LE16(built_in_palettes[palette_assignments[i].palette_index * 4 + 1 - 4]);
|
gb->sgb->effective_palettes[1] = built_in_palettes[palette_assignments[i].palette_index * 4 + 1 - 4];
|
||||||
gb->sgb->effective_palettes[2] = LE16(built_in_palettes[palette_assignments[i].palette_index * 4 + 2 - 4]);
|
gb->sgb->effective_palettes[2] = built_in_palettes[palette_assignments[i].palette_index * 4 + 2 - 4];
|
||||||
gb->sgb->effective_palettes[3] = LE16(built_in_palettes[palette_assignments[i].palette_index * 4 + 3 - 4]);
|
gb->sgb->effective_palettes[3] = built_in_palettes[palette_assignments[i].palette_index * 4 + 3 - 4];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,10 +264,12 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
} *command = (void *)(gb->sgb->command + 1);
|
} *command = (void *)(gb->sgb->command + 1);
|
||||||
|
|
||||||
uint16_t count = command->length;
|
uint16_t count = command->length;
|
||||||
count = LE16(count);
|
#ifdef GB_BIG_ENDIAN
|
||||||
|
count = __builtin_bswap16(count);
|
||||||
|
#endif
|
||||||
uint8_t x = command->x;
|
uint8_t x = command->x;
|
||||||
uint8_t y = command->y;
|
uint8_t y = command->y;
|
||||||
if (x >= 20 || y >= 18) {
|
if (x >= 20 || y >= 18 || (count + 3) / 4 > sizeof(gb->sgb->command) - sizeof(*command) - 1) {
|
||||||
/* TODO: Verify with the SFC BIOS */
|
/* TODO: Verify with the SFC BIOS */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -279,7 +283,7 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
x++;
|
x++;
|
||||||
y = 0;
|
y = 0;
|
||||||
if (x == 20) {
|
if (x == 20) {
|
||||||
break;
|
x = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,7 +293,7 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
y++;
|
y++;
|
||||||
x = 0;
|
x = 0;
|
||||||
if (y == 18) {
|
if (y == 18) {
|
||||||
break;
|
y = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -373,36 +377,39 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PAL_TRN:
|
case PAL_TRN:
|
||||||
gb->sgb->vram_transfer_countdown = 3;
|
gb->sgb->vram_transfer_countdown = 2;
|
||||||
gb->sgb->transfer_dest = TRANSFER_PALETTES;
|
gb->sgb->transfer_dest = TRANSFER_PALETTES;
|
||||||
break;
|
break;
|
||||||
case DATA_SND:
|
case DATA_SND:
|
||||||
// Not supported, but used by almost all SGB games for hot patching, so let's mute the warning for this
|
// Not supported, but used by almost all SGB games for hot patching, so let's mute the warning for this
|
||||||
break;
|
break;
|
||||||
case MLT_REQ:
|
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,
|
gb->sgb->player_count = (gb->sgb->command[1] & 3) + 1; /* Todo: When breaking save state comaptibility,
|
||||||
fix this to be 0 based. */
|
fix this to be 0 based. */
|
||||||
if (gb->sgb->player_count == 3) {
|
if (gb->sgb->player_count == 3) {
|
||||||
gb->sgb->player_count++;
|
gb->sgb->current_player++;
|
||||||
}
|
}
|
||||||
gb->sgb->current_player &= (gb->sgb->player_count - 1);
|
gb->sgb->mlt_lock = true;
|
||||||
break;
|
break;
|
||||||
case CHR_TRN:
|
case CHR_TRN:
|
||||||
gb->sgb->vram_transfer_countdown = 3;
|
gb->sgb->vram_transfer_countdown = 2;
|
||||||
gb->sgb->transfer_dest = (gb->sgb->command[1] & 1)? TRANSFER_HIGH_TILES : TRANSFER_LOW_TILES;
|
gb->sgb->transfer_dest = (gb->sgb->command[1] & 1)? TRANSFER_HIGH_TILES : TRANSFER_LOW_TILES;
|
||||||
break;
|
break;
|
||||||
case PCT_TRN:
|
case PCT_TRN:
|
||||||
gb->sgb->vram_transfer_countdown = 3;
|
gb->sgb->vram_transfer_countdown = 2;
|
||||||
gb->sgb->transfer_dest = TRANSFER_BORDER_DATA;
|
gb->sgb->transfer_dest = TRANSFER_BORDER_DATA;
|
||||||
break;
|
break;
|
||||||
case ATTR_TRN:
|
case ATTR_TRN:
|
||||||
gb->sgb->vram_transfer_countdown = 3;
|
gb->sgb->vram_transfer_countdown = 2;
|
||||||
gb->sgb->transfer_dest = TRANSFER_ATTRIBUTES;
|
gb->sgb->transfer_dest = TRANSFER_ATTRIBUTES;
|
||||||
break;
|
break;
|
||||||
case ATTR_SET:
|
case ATTR_SET:
|
||||||
load_attribute_file(gb, gb->sgb->command[1] & 0x3F);
|
load_attribute_file(gb, gb->sgb->command[0] & 0x3F);
|
||||||
|
|
||||||
if (gb->sgb->command[1] & 0x40) {
|
if (gb->sgb->command[0] & 0x40) {
|
||||||
gb->sgb->mask_mode = MASK_DISABLED;
|
gb->sgb->mask_mode = MASK_DISABLED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -432,22 +439,27 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (gb->sgb->disable_commands) 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;
|
uint16_t command_size = (gb->sgb->command[0] & 7 ?: 1) * SGB_PACKET_SIZE * 8;
|
||||||
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
|
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
|
||||||
command_size = SGB_PACKET_SIZE * 8;
|
command_size = SGB_PACKET_SIZE * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((value & 0x20) != 0 && (gb->io_registers[GB_IO_JOYP] & 0x20) == 0) {
|
if ((value & 0x20) == 0 && (gb->io_registers[GB_IO_JOYP] & 0x20) != 0) {
|
||||||
if ((gb->sgb->player_count & 1) == 0) {
|
gb->sgb->mlt_lock ^= true;
|
||||||
gb->sgb->current_player++;
|
|
||||||
gb->sgb->current_player &= (gb->sgb->player_count - 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ((value >> 4) & 3) {
|
switch ((value >> 4) & 3) {
|
||||||
case 3:
|
case 3:
|
||||||
gb->sgb->ready_for_pulse = true;
|
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;
|
break;
|
||||||
|
|
||||||
case 2: // Zero
|
case 2: // Zero
|
||||||
@ -463,12 +475,10 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
|||||||
gb->sgb->ready_for_stop = false;
|
gb->sgb->ready_for_stop = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (gb->sgb->command_write_index < sizeof(gb->sgb->command) * 8) {
|
gb->sgb->command_write_index++;
|
||||||
gb->sgb->command_write_index++;
|
gb->sgb->ready_for_pulse = false;
|
||||||
gb->sgb->ready_for_pulse = false;
|
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
|
||||||
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
|
gb->sgb->ready_for_stop = true;
|
||||||
gb->sgb->ready_for_stop = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -482,13 +492,11 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
|||||||
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
|
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (gb->sgb->command_write_index < sizeof(gb->sgb->command) * 8) {
|
gb->sgb->command[gb->sgb->command_write_index / 8] |= 1 << (gb->sgb->command_write_index & 7);
|
||||||
gb->sgb->command[gb->sgb->command_write_index / 8] |= 1 << (gb->sgb->command_write_index & 7);
|
gb->sgb->command_write_index++;
|
||||||
gb->sgb->command_write_index++;
|
gb->sgb->ready_for_pulse = false;
|
||||||
gb->sgb->ready_for_pulse = false;
|
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
|
||||||
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
|
gb->sgb->ready_for_stop = true;
|
||||||
gb->sgb->ready_for_stop = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -531,6 +539,7 @@ static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_
|
|||||||
return GB_convert_rgb15(gb, color, false);
|
return GB_convert_rgb15(gb, color, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
static void render_boot_animation (GB_gameboy_t *gb)
|
static void render_boot_animation (GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
#include "graphics/sgb_animation_logo.inc"
|
#include "graphics/sgb_animation_logo.inc"
|
||||||
@ -547,8 +556,8 @@ static void render_boot_animation (GB_gameboy_t *gb)
|
|||||||
else if (gb->sgb->intro_animation < 80) {
|
else if (gb->sgb->intro_animation < 80) {
|
||||||
fade_blue = 80 - gb->sgb->intro_animation;
|
fade_blue = 80 - gb->sgb->intro_animation;
|
||||||
}
|
}
|
||||||
else if (gb->sgb->intro_animation > GB_SGB_INTRO_ANIMATION_LENGTH - 32) {
|
else if (gb->sgb->intro_animation > INTRO_ANIMATION_LENGTH - 32) {
|
||||||
fade_red = fade_blue = gb->sgb->intro_animation - GB_SGB_INTRO_ANIMATION_LENGTH + 32;
|
fade_red = fade_blue = gb->sgb->intro_animation - INTRO_ANIMATION_LENGTH + 32;
|
||||||
}
|
}
|
||||||
uint32_t colors[] = {
|
uint32_t colors[] = {
|
||||||
convert_rgb15(gb, 0),
|
convert_rgb15(gb, 0),
|
||||||
@ -598,73 +607,74 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
render_jingle(gb, gb->apu_output.sample_rate / GB_get_usual_frame_rate(gb));
|
render_jingle(gb, gb->apu_output.sample_rate / GB_get_usual_frame_rate(gb));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->sgb->intro_animation < GB_SGB_INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++;
|
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++;
|
||||||
|
|
||||||
if (gb->sgb->vram_transfer_countdown) {
|
if (gb->sgb->vram_transfer_countdown) {
|
||||||
if (--gb->sgb->vram_transfer_countdown == 0) {
|
if (--gb->sgb->vram_transfer_countdown == 0) {
|
||||||
unsigned size = 0;
|
if (gb->sgb->transfer_dest == TRANSFER_LOW_TILES || gb->sgb->transfer_dest == TRANSFER_HIGH_TILES) {
|
||||||
uint16_t *data = NULL;
|
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++) {
|
||||||
switch (gb->sgb->transfer_dest) {
|
unsigned tile_x = (tile % 10) * 16;
|
||||||
case TRANSFER_LOW_TILES:
|
unsigned tile_y = (tile / 10) * 8;
|
||||||
size = 0x100;
|
for (unsigned y = 0; y < 0x8; y++) {
|
||||||
data = (uint16_t *)gb->sgb->pending_border.tiles;
|
for (unsigned x = 0; x < 0x8; x++) {
|
||||||
break;
|
base[tile * 8 * 8 + y * 8 + x] = gb->sgb->screen_buffer[(tile_x + x) + (tile_y + y) * 160] +
|
||||||
case TRANSFER_HIGH_TILES:
|
gb->sgb->screen_buffer[(tile_x + x + 8) + (tile_y + y) * 160] * 4;
|
||||||
size = 0x100;
|
}
|
||||||
data = (uint16_t *)gb->sgb->pending_border.tiles + 0x800;
|
|
||||||
break;
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
*data = LE16(*data);
|
}
|
||||||
data++;
|
|
||||||
|
}
|
||||||
|
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->sgb->transfer_dest == TRANSFER_BORDER_DATA) {
|
|
||||||
gb->sgb->border_animation = 105; // Measured on an SGB2, but might be off by ±2
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gb->screen || !gb->rgb_encode_callback || gb->disable_rendering) {
|
if (!gb->screen || !gb->rgb_encode_callback || gb->disable_rendering) return;
|
||||||
if (gb->sgb->border_animation > 32) {
|
|
||||||
gb->sgb->border_animation--;
|
|
||||||
}
|
|
||||||
else if (gb->sgb->border_animation != 0) {
|
|
||||||
gb->sgb->border_animation--;
|
|
||||||
}
|
|
||||||
if (gb->sgb->border_animation == 32) {
|
|
||||||
memcpy(&gb->sgb->border, &gb->sgb->pending_border, sizeof(gb->sgb->border));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t colors[4 * 4];
|
uint32_t colors[4 * 4];
|
||||||
for (unsigned i = 0; i < 4 * 4; i++) {
|
for (unsigned i = 0; i < 4 * 4; i++) {
|
||||||
colors[i] = convert_rgb15(gb, LE16(gb->sgb->effective_palettes[i]));
|
colors[i] = convert_rgb15(gb, gb->sgb->effective_palettes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->sgb->mask_mode != MASK_FREEZE) {
|
if (gb->sgb->mask_mode != MASK_FREEZE) {
|
||||||
@ -673,7 +683,7 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
sizeof(gb->sgb->effective_screen_buffer));
|
sizeof(gb->sgb->effective_screen_buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->sgb->intro_animation < GB_SGB_INTRO_ANIMATION_LENGTH) {
|
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) {
|
||||||
render_boot_animation(gb);
|
render_boot_animation(gb);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -725,24 +735,21 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t border_colors[16 * 4];
|
uint32_t border_colors[16 * 4];
|
||||||
if (gb->sgb->border_animation == 0 || gb->sgb->border_animation > 64 || gb->sgb->intro_animation < GB_SGB_INTRO_ANIMATION_LENGTH) {
|
if (gb->sgb->border_animation == 0 || gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) {
|
||||||
if (gb->sgb->border_animation != 0) {
|
|
||||||
gb->sgb->border_animation--;
|
|
||||||
}
|
|
||||||
for (unsigned i = 0; i < 16 * 4; i++) {
|
for (unsigned i = 0; i < 16 * 4; i++) {
|
||||||
border_colors[i] = convert_rgb15(gb, LE16(gb->sgb->border.palette[i]));
|
border_colors[i] = convert_rgb15(gb, gb->sgb->border.palette[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (gb->sgb->border_animation > 32) {
|
else if (gb->sgb->border_animation > 32) {
|
||||||
gb->sgb->border_animation--;
|
gb->sgb->border_animation--;
|
||||||
for (unsigned i = 0; i < 16 * 4; i++) {
|
for (unsigned i = 0; i < 16 * 4; i++) {
|
||||||
border_colors[i] = convert_rgb15_with_fade(gb, LE16(gb->sgb->border.palette[i]), 64 - gb->sgb->border_animation);
|
border_colors[i] = convert_rgb15_with_fade(gb, gb->sgb->border.palette[i], 64 - gb->sgb->border_animation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gb->sgb->border_animation--;
|
gb->sgb->border_animation--;
|
||||||
for (unsigned i = 0; i < 16 * 4; i++) {
|
for (unsigned i = 0; i < 16 * 4; i++) {
|
||||||
border_colors[i] = convert_rgb15_with_fade(gb, LE16(gb->sgb->border.palette[i]), gb->sgb->border_animation);
|
border_colors[i] = convert_rgb15_with_fade(gb, gb->sgb->border.palette[i], gb->sgb->border_animation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,20 +767,13 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
else if (gb->border_mode == GB_BORDER_NEVER) {
|
else if (gb->border_mode == GB_BORDER_NEVER) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
uint16_t tile = LE16(gb->sgb->border.map[tile_x + tile_y * 32]);
|
uint16_t tile = gb->sgb->border.map[tile_x + tile_y * 32];
|
||||||
if (tile & 0x300) continue; // Unused tile
|
uint8_t flip_x = (tile & 0x4000)? 0x7 : 0;
|
||||||
uint8_t flip_x = (tile & 0x4000)? 0:7;
|
uint8_t flip_y = (tile & 0x8000)? 0x7 : 0;
|
||||||
uint8_t flip_y = (tile & 0x8000)? 7:0;
|
|
||||||
uint8_t palette = (tile >> 10) & 3;
|
uint8_t palette = (tile >> 10) & 3;
|
||||||
for (unsigned y = 0; y < 8; y++) {
|
for (unsigned y = 0; y < 8; y++) {
|
||||||
unsigned base = (tile & 0xFF) * 32 + (y ^ flip_y) * 2;
|
|
||||||
for (unsigned x = 0; x < 8; x++) {
|
for (unsigned x = 0; x < 8; x++) {
|
||||||
uint8_t bit = 1 << (x ^ flip_x);
|
uint8_t color = gb->sgb->border.tiles[(tile & 0xFF) * 64 + (x ^ flip_x) + (y ^ flip_y) * 8] & 0xF;
|
||||||
uint8_t color = ((gb->sgb->border.tiles[base] & bit) ? 1: 0) |
|
|
||||||
((gb->sgb->border.tiles[base + 1] & bit) ? 2: 0) |
|
|
||||||
((gb->sgb->border.tiles[base + 16] & bit) ? 4: 0) |
|
|
||||||
((gb->sgb->border.tiles[base + 17] & bit) ? 8: 0);
|
|
||||||
|
|
||||||
uint32_t *output = gb->screen;
|
uint32_t *output = gb->screen;
|
||||||
if (gb->border_mode == GB_BORDER_NEVER) {
|
if (gb->border_mode == GB_BORDER_NEVER) {
|
||||||
output += (tile_x - 6) * 8 + x + ((tile_y - 5) * 8 + y) * 160;
|
output += (tile_x - 6) * 8 + x + ((tile_y - 5) * 8 + y) * 160;
|
||||||
@ -799,18 +799,21 @@ void GB_sgb_load_default_data(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
#include "graphics/sgb_border.inc"
|
#include "graphics/sgb_border.inc"
|
||||||
|
|
||||||
#ifdef GB_BIG_ENDIAN
|
|
||||||
for (unsigned i = 0; i < sizeof(tilemap) / 2; i++) {
|
|
||||||
gb->sgb->border.map[i] = LE16(tilemap[i]);
|
|
||||||
}
|
|
||||||
for (unsigned i = 0; i < sizeof(palette) / 2; i++) {
|
|
||||||
gb->sgb->border.palette[i] = LE16(palette[i]);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
memcpy(gb->sgb->border.map, tilemap, sizeof(tilemap));
|
memcpy(gb->sgb->border.map, tilemap, sizeof(tilemap));
|
||||||
memcpy(gb->sgb->border.palette, palette, sizeof(palette));
|
memcpy(gb->sgb->border.palette, palette, sizeof(palette));
|
||||||
#endif
|
|
||||||
memcpy(gb->sgb->border.tiles, tiles, sizeof(tiles));
|
/* 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) {
|
if (gb->model != GB_MODEL_SGB2) {
|
||||||
/* Delete the "2" */
|
/* Delete the "2" */
|
||||||
@ -822,10 +825,10 @@ void GB_sgb_load_default_data(GB_gameboy_t *gb)
|
|||||||
/* Re-center */
|
/* 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]));
|
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] = LE16(built_in_palettes[0]);
|
gb->sgb->effective_palettes[0] = built_in_palettes[0];
|
||||||
gb->sgb->effective_palettes[1] = LE16(built_in_palettes[1]);
|
gb->sgb->effective_palettes[1] = built_in_palettes[1];
|
||||||
gb->sgb->effective_palettes[2] = LE16(built_in_palettes[2]);
|
gb->sgb->effective_palettes[2] = built_in_palettes[2];
|
||||||
gb->sgb->effective_palettes[3] = LE16(built_in_palettes[3]);
|
gb->sgb->effective_palettes[3] = built_in_palettes[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
static double fm_synth(double phase)
|
static double fm_synth(double phase)
|
||||||
@ -871,7 +874,7 @@ static void render_jingle(GB_gameboy_t *gb, size_t count)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->sgb->intro_animation >= GB_SGB_INTRO_ANIMATION_LENGTH) return;
|
if (gb->sgb->intro_animation >= INTRO_ANIMATION_LENGTH) return;
|
||||||
|
|
||||||
signed jingle_stage = (gb->sgb->intro_animation - 64) / 3;
|
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_cutoff_ratio = 2000.0 * pow(2, gb->sgb->intro_animation / 20.0) / gb->apu_output.sample_rate;
|
||||||
@ -889,7 +892,7 @@ static void render_jingle(GB_gameboy_t *gb, size_t count)
|
|||||||
gb->sgb_intro_jingle_phases[f] += frequencies[f] / gb->apu_output.sample_rate;
|
gb->sgb_intro_jingle_phases[f] += frequencies[f] / gb->apu_output.sample_rate;
|
||||||
}
|
}
|
||||||
if (gb->sgb->intro_animation > 100) {
|
if (gb->sgb->intro_animation > 100) {
|
||||||
sample *= pow((GB_SGB_INTRO_ANIMATION_LENGTH - gb->sgb->intro_animation) / (GB_SGB_INTRO_ANIMATION_LENGTH - 100.0), 3);
|
sample *= pow((INTRO_ANIMATION_LENGTH - gb->sgb->intro_animation) / (INTRO_ANIMATION_LENGTH - 100.0), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->sgb->intro_animation < 120) {
|
if (gb->sgb->intro_animation < 120) {
|
||||||
|
18
Core/sgb.h
@ -1,12 +1,12 @@
|
|||||||
#ifndef sgb_h
|
#ifndef sgb_h
|
||||||
#define sgb_h
|
#define sgb_h
|
||||||
#include "defs.h"
|
#include "gb_struct_def.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
typedef struct GB_sgb_s GB_sgb_t;
|
typedef struct GB_sgb_s GB_sgb_t;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t tiles[0x100 * 8 * 4];
|
uint8_t tiles[0x100 * 8 * 8]; /* High nibble not used*/
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
uint16_t map[32 * 32];
|
uint16_t map[32 * 32];
|
||||||
@ -17,8 +17,6 @@ typedef struct {
|
|||||||
} GB_sgb_border_t;
|
} GB_sgb_border_t;
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
#define GB_SGB_INTRO_ANIMATION_LENGTH 200
|
|
||||||
|
|
||||||
struct GB_sgb_s {
|
struct GB_sgb_s {
|
||||||
uint8_t command[16 * 7];
|
uint8_t command[16 * 7];
|
||||||
uint16_t command_write_index;
|
uint16_t command_write_index;
|
||||||
@ -48,19 +46,21 @@ struct GB_sgb_s {
|
|||||||
uint16_t effective_palettes[4 * 4];
|
uint16_t effective_palettes[4 * 4];
|
||||||
uint16_t ram_palettes[4 * 512];
|
uint16_t ram_palettes[4 * 512];
|
||||||
uint8_t attribute_map[20 * 18];
|
uint8_t attribute_map[20 * 18];
|
||||||
uint8_t attribute_files[0xFD2];
|
uint8_t attribute_files[0xFE0];
|
||||||
uint8_t attribute_files_padding[0xFE0 - 0xFD2];
|
|
||||||
|
|
||||||
/* Intro */
|
/* Intro */
|
||||||
int16_t intro_animation;
|
int16_t intro_animation;
|
||||||
|
|
||||||
/* GB Header */
|
/* GB Header */
|
||||||
uint8_t received_header[0x54];
|
uint8_t received_header[0x54];
|
||||||
|
|
||||||
|
/* Multiplayer (cont) */
|
||||||
|
bool mlt_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
internal void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
|
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
|
||||||
internal void GB_sgb_render(GB_gameboy_t *gb);
|
void GB_sgb_render(GB_gameboy_t *gb);
|
||||||
internal void GB_sgb_load_default_data(GB_gameboy_t *gb);
|
void GB_sgb_load_default_data(GB_gameboy_t *gb);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|