Merge remote-tracking branch 'origin/master' into wasm
This commit is contained in:
commit
8f03312ad6
25
.github/actions/LICENSE
vendored
Normal file
25
.github/actions/LICENSE
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
Blargg's Test ROMs by Shay Green <gblargg@gmail.com>
|
||||||
|
|
||||||
|
Acid2 tests by Matt Currie under MIT:
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Matt Currie
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
BIN
.github/actions/cgb-acid2.gbc
vendored
Normal file
BIN
.github/actions/cgb-acid2.gbc
vendored
Normal file
Binary file not shown.
BIN
.github/actions/cgb_sound.gb
vendored
Normal file
BIN
.github/actions/cgb_sound.gb
vendored
Normal file
Binary file not shown.
BIN
.github/actions/dmg-acid2.gb
vendored
Normal file
BIN
.github/actions/dmg-acid2.gb
vendored
Normal file
Binary file not shown.
BIN
.github/actions/dmg_sound-2.gb
vendored
Executable file
BIN
.github/actions/dmg_sound-2.gb
vendored
Executable file
Binary file not shown.
23
.github/actions/install_deps.sh
vendored
Executable file
23
.github/actions/install_deps.sh
vendored
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
case `echo $1 | cut -d '-' -f 1` in
|
||||||
|
ubuntu)
|
||||||
|
sudo apt-get -qq update
|
||||||
|
sudo apt-get install -yq bison libpng-dev pkg-config libsdl2-dev
|
||||||
|
(
|
||||||
|
cd `mktemp -d`
|
||||||
|
curl -L https://github.com/rednex/rgbds/archive/v0.4.0.zip > rgbds.zip
|
||||||
|
unzip rgbds.zip
|
||||||
|
cd rgbds-*
|
||||||
|
make -sj
|
||||||
|
sudo make install
|
||||||
|
cd ..
|
||||||
|
rm -rf *
|
||||||
|
)
|
||||||
|
;;
|
||||||
|
macos)
|
||||||
|
brew install rgbds sdl2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported OS"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
BIN
.github/actions/oam_bug-2.gb
vendored
Executable file
BIN
.github/actions/oam_bug-2.gb
vendored
Executable file
Binary file not shown.
33
.github/actions/sanity_tests.sh
vendored
Executable file
33
.github/actions/sanity_tests.sh
vendored
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
set -e
|
||||||
|
|
||||||
|
./build/bin/tester/sameboy_tester --jobs 5 \
|
||||||
|
--length 40 .github/actions/cgb_sound.gb \
|
||||||
|
--length 10 .github/actions/cgb-acid2.gbc \
|
||||||
|
--length 10 .github/actions/dmg-acid2.gb \
|
||||||
|
--dmg --length 40 .github/actions/dmg_sound-2.gb \
|
||||||
|
--dmg --length 20 .github/actions/oam_bug-2.gb
|
||||||
|
|
||||||
|
mv .github/actions/dmg{,-mode}-acid2.bmp
|
||||||
|
|
||||||
|
./build/bin/tester/sameboy_tester \
|
||||||
|
--dmg --length 10 .github/actions/dmg-acid2.gb
|
||||||
|
|
||||||
|
set +e
|
||||||
|
|
||||||
|
FAILED_TESTS=`
|
||||||
|
shasum .github/actions/*.bmp | grep -q -E -v \(\
|
||||||
|
44ce0c7d49254df0637849c9155080ac7dc3ef3d\ \ .github/actions/cgb-acid2.bmp\|\
|
||||||
|
dbcc438dcea13b5d1b80c5cd06bda2592cc5d9e0\ \ .github/actions/cgb_sound.bmp\|\
|
||||||
|
0caadf9634e40247ae9c15ff71992e8f77bbf89e\ \ .github/actions/dmg-acid2.bmp\|\
|
||||||
|
c50daed36c57a8170ff362042694786676350997\ \ .github/actions/dmg-mode-acid2.bmp\|\
|
||||||
|
c9e944b7e01078bdeba1819bc2fa9372b111f52d\ \ .github/actions/dmg_sound-2.bmp\|\
|
||||||
|
f0172cc91867d3343fbd113a2bb98100074be0de\ \ .github/actions/oam_bug-2.bmp\
|
||||||
|
\)`
|
||||||
|
|
||||||
|
if [ -n "$FAILED_TESTS" ] ; then
|
||||||
|
echo "Failed the following tests:"
|
||||||
|
echo $FAILED_TESTS | tr " " "\n" | grep -q -o -E "[^/]+\.bmp" | sed s/.bmp// | sort
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo Passed all tests
|
36
.github/workflows/sanity.yml
vendored
Normal file
36
.github/workflows/sanity.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
name: "Bulidability and Sanity"
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sanity:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest, ubuntu-16.04]
|
||||||
|
cc: [gcc, clang]
|
||||||
|
include:
|
||||||
|
- os: macos-latest
|
||||||
|
cc: clang
|
||||||
|
extra_target: cocoa
|
||||||
|
exclude:
|
||||||
|
- os: macos-latest
|
||||||
|
cc: gcc
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install deps
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
./.github/actions/install_deps.sh ${{ matrix.os }}
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
${{ matrix.cc }} -v; (make -j sdl tester libretro ${{ matrix.extra_target }} CONF=release CC=${{ matrix.cc }} || (echo "==== Build Failed ==="; make sdl tester libretro ${{ matrix.extra_target }} CONF=release CC=${{ matrix.cc }}))
|
||||||
|
- name: Sanity tests
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
./.github/actions/sanity_tests.sh
|
||||||
|
- name: Upload binaries
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: sameboy-canary-${{ matrix.os }}-${{ matrix.cc }}
|
||||||
|
path: build/bin
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 479 B |
@ -6,22 +6,15 @@ Start:
|
|||||||
ld sp, $fffe
|
ld sp, $fffe
|
||||||
|
|
||||||
; Clear memory VRAM
|
; Clear memory VRAM
|
||||||
ld hl, $8000
|
call ClearMemoryPage8000
|
||||||
call ClearMemoryPage
|
|
||||||
ld a, 2
|
ld a, 2
|
||||||
ld c, $70
|
ld c, $70
|
||||||
ld [c], a
|
ld [c], a
|
||||||
; Clear RAM Bank 2 (Like the original boot ROM
|
; Clear RAM Bank 2 (Like the original boot ROM)
|
||||||
ld h, $D0
|
ld h, $D0
|
||||||
xor a
|
|
||||||
call ClearMemoryPage
|
call ClearMemoryPage
|
||||||
ld [c], a
|
ld [c], a
|
||||||
|
|
||||||
; Clear chosen input palette
|
|
||||||
ldh [InputPalette], a
|
|
||||||
; Clear title checksum
|
|
||||||
ldh [TitleChecksum], a
|
|
||||||
|
|
||||||
; Clear OAM
|
; Clear OAM
|
||||||
ld h, $fe
|
ld h, $fe
|
||||||
ld c, $a0
|
ld c, $a0
|
||||||
@ -30,7 +23,19 @@ Start:
|
|||||||
dec c
|
dec c
|
||||||
jr nz, .clearOAMLoop
|
jr nz, .clearOAMLoop
|
||||||
|
|
||||||
; Init Audio
|
; Init waveform
|
||||||
|
ld c, $10
|
||||||
|
.waveformLoop
|
||||||
|
ldi [hl], a
|
||||||
|
cpl
|
||||||
|
dec c
|
||||||
|
jr nz, .waveformLoop
|
||||||
|
|
||||||
|
; Clear chosen input palette
|
||||||
|
ldh [InputPalette], a
|
||||||
|
; Clear title checksum
|
||||||
|
ldh [TitleChecksum], a
|
||||||
|
|
||||||
ld a, $80
|
ld a, $80
|
||||||
ldh [$26], a
|
ldh [$26], a
|
||||||
ldh [$11], a
|
ldh [$11], a
|
||||||
@ -39,8 +44,7 @@ Start:
|
|||||||
ldh [$25], a
|
ldh [$25], a
|
||||||
ld a, $77
|
ld a, $77
|
||||||
ldh [$24], a
|
ldh [$24], a
|
||||||
|
ld hl, $FF30
|
||||||
call InitWaveform
|
|
||||||
|
|
||||||
; Init BG palette
|
; Init BG palette
|
||||||
ld a, $fc
|
ld a, $fc
|
||||||
@ -67,9 +71,7 @@ Start:
|
|||||||
; Clear the second VRAM bank
|
; Clear the second VRAM bank
|
||||||
ld a, 1
|
ld a, 1
|
||||||
ldh [$4F], a
|
ldh [$4F], a
|
||||||
xor a
|
call ClearMemoryPage8000
|
||||||
ld hl, $8000
|
|
||||||
call ClearMemoryPage
|
|
||||||
call LoadTileset
|
call LoadTileset
|
||||||
|
|
||||||
ld b, 3
|
ld b, 3
|
||||||
@ -87,20 +89,24 @@ ELSE
|
|||||||
|
|
||||||
.tilemapRowLoop
|
.tilemapRowLoop
|
||||||
|
|
||||||
|
call .write_with_palette
|
||||||
|
|
||||||
|
; Repeat the 3 tiles common between E and B. This saves 27 bytes after
|
||||||
|
; compression, with a cost of 17 bytes of code.
|
||||||
push af
|
push af
|
||||||
; Switch to second VRAM Bank
|
sub $20
|
||||||
ld a, 1
|
sub $3
|
||||||
ldh [$4F], a
|
jr nc, .notspecial
|
||||||
ld [hl], 8
|
add $20
|
||||||
; Switch to back first VRAM Bank
|
call .write_with_palette
|
||||||
xor a
|
dec c
|
||||||
ldh [$4F], a
|
.notspecial
|
||||||
pop af
|
pop af
|
||||||
ldi [hl], a
|
|
||||||
add d
|
add d ; d = 3 for SameBoy logo, d = 1 for Nintendo logo
|
||||||
dec c
|
dec c
|
||||||
jr nz, .tilemapRowLoop
|
jr nz, .tilemapRowLoop
|
||||||
sub 47
|
sub 44
|
||||||
push de
|
push de
|
||||||
ld de, $10
|
ld de, $10
|
||||||
add hl, de
|
add hl, de
|
||||||
@ -116,6 +122,19 @@ ELSE
|
|||||||
ld l, $a7
|
ld l, $a7
|
||||||
ld bc, $0107
|
ld bc, $0107
|
||||||
jr .tilemapRowLoop
|
jr .tilemapRowLoop
|
||||||
|
|
||||||
|
.write_with_palette
|
||||||
|
push af
|
||||||
|
; Switch to second VRAM Bank
|
||||||
|
ld a, 1
|
||||||
|
ldh [$4F], a
|
||||||
|
ld [hl], 8
|
||||||
|
; Switch to back first VRAM Bank
|
||||||
|
xor a
|
||||||
|
ldh [$4F], a
|
||||||
|
pop af
|
||||||
|
ldi [hl], a
|
||||||
|
ret
|
||||||
.endTilemap
|
.endTilemap
|
||||||
ENDC
|
ENDC
|
||||||
|
|
||||||
@ -125,43 +144,44 @@ ENDC
|
|||||||
ld hl, BgPalettes
|
ld hl, BgPalettes
|
||||||
xor a
|
xor a
|
||||||
.expandPalettesLoop:
|
.expandPalettesLoop:
|
||||||
IF !DEF(FAST)
|
|
||||||
cpl
|
cpl
|
||||||
ENDC
|
|
||||||
; One white
|
; One white
|
||||||
ldi [hl], a
|
ld [hli], a
|
||||||
ldi [hl], a
|
ld [hli], a
|
||||||
|
|
||||||
IF DEF(FAST)
|
; Mixed with white
|
||||||
; 3 more whites
|
|
||||||
ldi [hl], a
|
|
||||||
ldi [hl], a
|
|
||||||
ldi [hl], a
|
|
||||||
ldi [hl], a
|
|
||||||
ldi [hl], a
|
|
||||||
ldi [hl], a
|
|
||||||
ELSE
|
|
||||||
; The actual color
|
|
||||||
ld a, [de]
|
ld a, [de]
|
||||||
inc de
|
inc e
|
||||||
ldi [hl], a
|
or $20
|
||||||
|
ld b, a
|
||||||
|
|
||||||
ld a, [de]
|
ld a, [de]
|
||||||
inc de
|
dec e
|
||||||
ldi [hl], a
|
or $84
|
||||||
|
rra
|
||||||
|
rr b
|
||||||
|
ld [hl], b
|
||||||
|
inc l
|
||||||
|
ld [hli], a
|
||||||
|
|
||||||
|
; One black
|
||||||
|
xor a
|
||||||
|
ld [hli], a
|
||||||
|
ld [hli], a
|
||||||
|
|
||||||
|
; One color
|
||||||
|
ld a, [de]
|
||||||
|
inc e
|
||||||
|
ld [hli], a
|
||||||
|
ld a, [de]
|
||||||
|
inc e
|
||||||
|
ld [hli], a
|
||||||
|
|
||||||
xor a
|
xor a
|
||||||
; Two blacks
|
|
||||||
ldi [hl], a
|
|
||||||
ldi [hl], a
|
|
||||||
ldi [hl], a
|
|
||||||
ldi [hl], a
|
|
||||||
ENDC
|
|
||||||
|
|
||||||
dec c
|
dec c
|
||||||
jr nz, .expandPalettesLoop
|
jr nz, .expandPalettesLoop
|
||||||
|
|
||||||
ld hl, BgPalettes
|
call LoadPalettesFromHRAM
|
||||||
call LoadBGPalettes64
|
|
||||||
|
|
||||||
; Turn on LCD
|
; Turn on LCD
|
||||||
ld a, $91
|
ld a, $91
|
||||||
@ -170,8 +190,10 @@ ENDC
|
|||||||
IF !DEF(FAST)
|
IF !DEF(FAST)
|
||||||
call DoIntroAnimation
|
call DoIntroAnimation
|
||||||
|
|
||||||
|
ld a, 45
|
||||||
|
ldh [WaitLoopCounter], a
|
||||||
; Wait ~0.75 seconds
|
; Wait ~0.75 seconds
|
||||||
ld b, 45
|
ld b, a
|
||||||
call WaitBFrames
|
call WaitBFrames
|
||||||
|
|
||||||
; Play first sound
|
; Play first sound
|
||||||
@ -183,10 +205,6 @@ IF !DEF(FAST)
|
|||||||
ld a, $c1
|
ld a, $c1
|
||||||
call PlaySound
|
call PlaySound
|
||||||
|
|
||||||
; Wait ~0.5 seconds
|
|
||||||
ld a, 30
|
|
||||||
ldh [WaitLoopCounter], a
|
|
||||||
|
|
||||||
.waitLoop
|
.waitLoop
|
||||||
call GetInputPaletteIndex
|
call GetInputPaletteIndex
|
||||||
call WaitFrame
|
call WaitFrame
|
||||||
@ -198,6 +216,9 @@ ELSE
|
|||||||
call PlaySound
|
call PlaySound
|
||||||
ENDC
|
ENDC
|
||||||
call Preboot
|
call Preboot
|
||||||
|
IF DEF(AGB)
|
||||||
|
ld b, 1
|
||||||
|
ENDC
|
||||||
|
|
||||||
; Will be filled with NOPs
|
; Will be filled with NOPs
|
||||||
|
|
||||||
@ -206,7 +227,6 @@ BootGame:
|
|||||||
ldh [$50], a
|
ldh [$50], a
|
||||||
|
|
||||||
SECTION "MoreStuff", ROM0[$200]
|
SECTION "MoreStuff", ROM0[$200]
|
||||||
|
|
||||||
; Game Palettes Data
|
; Game Palettes Data
|
||||||
TitleChecksums:
|
TitleChecksums:
|
||||||
db $00 ; Default
|
db $00 ; Default
|
||||||
@ -509,30 +529,31 @@ Palettes:
|
|||||||
dw $7FFF, $7FEA, $7D5F, $0000 ; CGA 1
|
dw $7FFF, $7FEA, $7D5F, $0000 ; CGA 1
|
||||||
dw $4778, $3290, $1D87, $0861 ; DMG LCD
|
dw $4778, $3290, $1D87, $0861 ; DMG LCD
|
||||||
|
|
||||||
KeyCombinationPalettes
|
KeyCombinationPalettes:
|
||||||
db 1 ; Right
|
db 1 * 3 ; Right
|
||||||
db 48 ; Left
|
db 48 * 3 ; Left
|
||||||
db 5 ; Up
|
db 5 * 3 ; Up
|
||||||
db 8 ; Down
|
db 8 * 3 ; Down
|
||||||
db 0 ; Right + A
|
db 0 * 3 ; Right + A
|
||||||
db 40 ; Left + A
|
db 40 * 3 ; Left + A
|
||||||
db 43 ; Up + A
|
db 43 * 3 ; Up + A
|
||||||
db 3 ; Down + A
|
db 3 * 3 ; Down + A
|
||||||
db 6 ; Right + B
|
db 6 * 3 ; Right + B
|
||||||
db 7 ; Left + B
|
db 7 * 3 ; Left + B
|
||||||
db 28 ; Up + B
|
db 28 * 3 ; Up + B
|
||||||
db 49 ; Down + B
|
db 49 * 3 ; Down + B
|
||||||
; SameBoy "Exclusives"
|
; SameBoy "Exclusives"
|
||||||
db 51 ; Right + A + B
|
db 51 * 3 ; Right + A + B
|
||||||
db 52 ; Left + A + B
|
db 52 * 3 ; Left + A + B
|
||||||
db 53 ; Up + A + B
|
db 53 * 3 ; Up + A + B
|
||||||
db 54 ; Down + A + B
|
db 54 * 3 ; Down + A + B
|
||||||
|
|
||||||
TrademarkSymbol:
|
TrademarkSymbol:
|
||||||
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
|
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
|
||||||
|
|
||||||
SameBoyLogo:
|
SameBoyLogo:
|
||||||
incbin "SameBoyLogo.rle"
|
incbin "SameBoyLogo.pb12"
|
||||||
|
|
||||||
|
|
||||||
AnimationColors:
|
AnimationColors:
|
||||||
dw $7FFF ; White
|
dw $7FFF ; White
|
||||||
@ -545,9 +566,6 @@ AnimationColors:
|
|||||||
dw $7102 ; Blue
|
dw $7102 ; Blue
|
||||||
AnimationColorsEnd:
|
AnimationColorsEnd:
|
||||||
|
|
||||||
DMGPalettes:
|
|
||||||
dw $7FFF, $32BF, $00D0, $0000
|
|
||||||
|
|
||||||
; Helper Functions
|
; Helper Functions
|
||||||
DoubleBitsAndWriteRowTwice:
|
DoubleBitsAndWriteRowTwice:
|
||||||
call .twice
|
call .twice
|
||||||
@ -594,8 +612,11 @@ PlaySound:
|
|||||||
ldh [$14], a
|
ldh [$14], a
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
ClearMemoryPage8000:
|
||||||
|
ld hl, $8000
|
||||||
; Clear from HL to HL | 0x2000
|
; Clear from HL to HL | 0x2000
|
||||||
ClearMemoryPage:
|
ClearMemoryPage:
|
||||||
|
xor a
|
||||||
ldi [hl], a
|
ldi [hl], a
|
||||||
bit 5, h
|
bit 5, h
|
||||||
jr z, ClearMemoryPage
|
jr z, ClearMemoryPage
|
||||||
@ -634,43 +655,82 @@ ReadCGBLogoHalfTile:
|
|||||||
ld a, e
|
ld a, e
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
; LoadTileset using PB12 codec, 2020 Jakub Kądziołka
|
||||||
|
; (based on PB8 codec, 2019 Damian Yerrick)
|
||||||
|
|
||||||
|
SameBoyLogo_dst = $8080
|
||||||
|
SameBoyLogo_length = (128 * 24) / 64
|
||||||
|
|
||||||
LoadTileset:
|
LoadTileset:
|
||||||
; Copy SameBoy Logo
|
ld hl, SameBoyLogo
|
||||||
ld de, SameBoyLogo
|
ld de, SameBoyLogo_dst - 1
|
||||||
ld hl, $8080
|
ld c, SameBoyLogo_length
|
||||||
.sameboyLogoLoop
|
.refill
|
||||||
ld a, [de]
|
; Register map for PB12 decompression
|
||||||
inc de
|
; HL: source address in boot ROM
|
||||||
|
; DE: destination address in VRAM
|
||||||
ld b, a
|
; A: Current literal value
|
||||||
and $0f
|
; B: Repeat bits, terminated by 1000...
|
||||||
jr z, .skipLiteral
|
; Source address in HL lets the repeat bits go straight to B,
|
||||||
ld c, a
|
; bypassing A and avoiding spilling registers to the stack.
|
||||||
|
ld b, [hl]
|
||||||
.literalLoop
|
dec b
|
||||||
ld a, [de]
|
|
||||||
ldi [hl], a
|
|
||||||
inc hl
|
|
||||||
inc de
|
|
||||||
dec c
|
|
||||||
jr nz, .literalLoop
|
|
||||||
.skipLiteral
|
|
||||||
swap b
|
|
||||||
ld a, b
|
|
||||||
and $0f
|
|
||||||
jr z, .sameboyLogoEnd
|
jr z, .sameboyLogoEnd
|
||||||
|
inc b
|
||||||
|
inc hl
|
||||||
|
|
||||||
|
; Shift a 1 into lower bit of shift value. Once this bit
|
||||||
|
; reaches the carry, B becomes 0 and the byte is over
|
||||||
|
scf
|
||||||
|
rl b
|
||||||
|
|
||||||
|
.loop
|
||||||
|
; If not a repeat, load a literal byte
|
||||||
|
jr c, .simple_repeat
|
||||||
|
sla b
|
||||||
|
jr c, .shifty_repeat
|
||||||
|
ld a, [hli]
|
||||||
|
jr .got_byte
|
||||||
|
.shifty_repeat
|
||||||
|
sla b
|
||||||
|
jr nz, .no_refill_during_shift
|
||||||
|
ld b, [hl] ; see above. Also, no, factoring it out into a callable
|
||||||
|
inc hl ; routine doesn't save bytes, even with conditional calls
|
||||||
|
scf
|
||||||
|
rl b
|
||||||
|
.no_refill_during_shift
|
||||||
ld c, a
|
ld c, a
|
||||||
|
jr nc, .shift_left
|
||||||
|
srl a
|
||||||
|
db $fe ; eat the add a with cp d8
|
||||||
|
.shift_left
|
||||||
|
add a
|
||||||
|
sla b
|
||||||
|
jr c, .go_and
|
||||||
|
or c
|
||||||
|
db $fe ; eat the and c with cp d8
|
||||||
|
.go_and
|
||||||
|
and c
|
||||||
|
jr .got_byte
|
||||||
|
.simple_repeat
|
||||||
|
sla b
|
||||||
|
jr c, .got_byte
|
||||||
|
; far repeat
|
||||||
|
dec de
|
||||||
ld a, [de]
|
ld a, [de]
|
||||||
inc de
|
inc de
|
||||||
|
.got_byte
|
||||||
|
inc de
|
||||||
|
ld [de], a
|
||||||
|
sla b
|
||||||
|
jr nz, .loop
|
||||||
|
jr .refill
|
||||||
|
|
||||||
.repeatLoop
|
; End PB12 decoding. The rest uses HL as the destination
|
||||||
ldi [hl], a
|
|
||||||
inc hl
|
|
||||||
dec c
|
|
||||||
jr nz, .repeatLoop
|
|
||||||
jr .sameboyLogoLoop
|
|
||||||
|
|
||||||
.sameboyLogoEnd
|
.sameboyLogoEnd
|
||||||
|
ld h, d
|
||||||
|
ld l, $80
|
||||||
|
|
||||||
; Copy (unresized) ROM logo
|
; Copy (unresized) ROM logo
|
||||||
ld de, $104
|
ld de, $104
|
||||||
.CGBROMLogoLoop
|
.CGBROMLogoLoop
|
||||||
@ -697,29 +757,6 @@ ReadTrademarkSymbol:
|
|||||||
jr nz, .loadTrademarkSymbolLoop
|
jr nz, .loadTrademarkSymbolLoop
|
||||||
ret
|
ret
|
||||||
|
|
||||||
LoadObjPalettes:
|
|
||||||
ld c, $6A
|
|
||||||
jr LoadPalettes
|
|
||||||
|
|
||||||
LoadBGPalettes64:
|
|
||||||
ld d, 64
|
|
||||||
|
|
||||||
LoadBGPalettes:
|
|
||||||
ld e, 0
|
|
||||||
ld c, $68
|
|
||||||
|
|
||||||
LoadPalettes:
|
|
||||||
ld a, $80
|
|
||||||
or e
|
|
||||||
ld [c], a
|
|
||||||
inc c
|
|
||||||
.loop
|
|
||||||
ld a, [hli]
|
|
||||||
ld [c], a
|
|
||||||
dec d
|
|
||||||
jr nz, .loop
|
|
||||||
ret
|
|
||||||
|
|
||||||
DoIntroAnimation:
|
DoIntroAnimation:
|
||||||
; Animate the intro
|
; Animate the intro
|
||||||
ld a, 1
|
ld a, 1
|
||||||
@ -759,39 +796,82 @@ IF !DEF(FAST)
|
|||||||
ld hl, BgPalettes
|
ld hl, BgPalettes
|
||||||
.frameLoop
|
.frameLoop
|
||||||
push bc
|
push bc
|
||||||
call BrightenColor
|
|
||||||
|
; Brighten Color
|
||||||
|
ld a, [hli]
|
||||||
|
ld e, a
|
||||||
|
ld a, [hld]
|
||||||
|
ld d, a
|
||||||
|
; RGB(1,1,1)
|
||||||
|
ld bc, $421
|
||||||
|
|
||||||
|
; Is blue maxed?
|
||||||
|
ld a, e
|
||||||
|
and $1F
|
||||||
|
cp $1F
|
||||||
|
jr nz, .blueNotMaxed
|
||||||
|
dec c
|
||||||
|
.blueNotMaxed
|
||||||
|
|
||||||
|
; Is green maxed?
|
||||||
|
ld a, e
|
||||||
|
cp $E0
|
||||||
|
jr c, .greenNotMaxed
|
||||||
|
ld a, d
|
||||||
|
and $3
|
||||||
|
cp $3
|
||||||
|
jr nz, .greenNotMaxed
|
||||||
|
res 5, c
|
||||||
|
.greenNotMaxed
|
||||||
|
|
||||||
|
; Is red maxed?
|
||||||
|
ld a, d
|
||||||
|
and $7C
|
||||||
|
cp $7C
|
||||||
|
jr nz, .redNotMaxed
|
||||||
|
res 2, b
|
||||||
|
.redNotMaxed
|
||||||
|
|
||||||
|
; add de, bc
|
||||||
|
; ld [hli], de
|
||||||
|
ld a, e
|
||||||
|
add c
|
||||||
|
ld [hli], a
|
||||||
|
ld a, d
|
||||||
|
adc b
|
||||||
|
ld [hli], a
|
||||||
pop bc
|
pop bc
|
||||||
|
|
||||||
dec c
|
dec c
|
||||||
jr nz, .frameLoop
|
jr nz, .frameLoop
|
||||||
|
|
||||||
call WaitFrame
|
call WaitFrame
|
||||||
|
call LoadPalettesFromHRAM
|
||||||
call WaitFrame
|
call WaitFrame
|
||||||
ld hl, BgPalettes
|
|
||||||
call LoadBGPalettes64
|
|
||||||
dec b
|
dec b
|
||||||
jr nz, .fadeLoop
|
jr nz, .fadeLoop
|
||||||
ENDC
|
ENDC
|
||||||
|
ld a, 1
|
||||||
call ClearVRAMViaHDMA
|
call ClearVRAMViaHDMA
|
||||||
; Select the first bank
|
call _ClearVRAMViaHDMA
|
||||||
xor a
|
call ClearVRAMViaHDMA ; A = $40, so it's bank 0
|
||||||
ldh [$4F], a
|
ld a, $ff
|
||||||
cpl
|
|
||||||
ldh [$00], a
|
ldh [$00], a
|
||||||
call ClearVRAMViaHDMA
|
|
||||||
|
|
||||||
; Final values for CGB mode
|
; Final values for CGB mode
|
||||||
ld de, $ff56
|
ld d, a
|
||||||
|
ld e, c
|
||||||
ld l, $0d
|
ld l, $0d
|
||||||
|
|
||||||
ld a, [$143]
|
ld a, [$143]
|
||||||
bit 7, a
|
bit 7, a
|
||||||
call z, EmulateDMG
|
call z, EmulateDMG
|
||||||
bit 7, a
|
bit 7, a
|
||||||
|
|
||||||
ldh [$4C], a
|
ldh [$4C], a
|
||||||
ldh a, [TitleChecksum]
|
ldh a, [TitleChecksum]
|
||||||
ld b, a
|
ld b, a
|
||||||
|
|
||||||
jr z, .skipDMGForCGBCheck
|
jr z, .skipDMGForCGBCheck
|
||||||
ldh a, [InputPalette]
|
ldh a, [InputPalette]
|
||||||
and a
|
and a
|
||||||
@ -804,7 +884,7 @@ IF DEF(AGB)
|
|||||||
ld c, a
|
ld c, a
|
||||||
add a, $11
|
add a, $11
|
||||||
ld h, c
|
ld h, c
|
||||||
ld b, 1
|
; B is set to 1 after ret
|
||||||
ELSE
|
ELSE
|
||||||
; Set registers to match the original CGB boot
|
; Set registers to match the original CGB boot
|
||||||
; AF = $1180, C = 0
|
; AF = $1180, C = 0
|
||||||
@ -822,6 +902,14 @@ ENDC
|
|||||||
ld a, $1
|
ld a, $1
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
GetKeyComboPalette:
|
||||||
|
ld hl, KeyCombinationPalettes - 1 ; Return value is 1-based, 0 means nothing down
|
||||||
|
ld c, a
|
||||||
|
ld b, 0
|
||||||
|
add hl, bc
|
||||||
|
ld a, [hl]
|
||||||
|
ret
|
||||||
|
|
||||||
EmulateDMG:
|
EmulateDMG:
|
||||||
ld a, 1
|
ld a, 1
|
||||||
ldh [$6C], a ; DMG Emulation
|
ldh [$6C], a ; DMG Emulation
|
||||||
@ -833,11 +921,7 @@ EmulateDMG:
|
|||||||
ldh a, [InputPalette]
|
ldh a, [InputPalette]
|
||||||
and a
|
and a
|
||||||
jr z, .nothingDown
|
jr z, .nothingDown
|
||||||
ld hl, KeyCombinationPalettes - 1 ; Return value is 1-based, 0 means nothing down
|
call GetKeyComboPalette
|
||||||
ld c ,a
|
|
||||||
ld b, 0
|
|
||||||
add hl, bc
|
|
||||||
ld a, [hl]
|
|
||||||
jr .paletteFromKeys
|
jr .paletteFromKeys
|
||||||
.nothingDown
|
.nothingDown
|
||||||
ld a, b
|
ld a, b
|
||||||
@ -846,8 +930,7 @@ EmulateDMG:
|
|||||||
call LoadPalettesFromIndex
|
call LoadPalettesFromIndex
|
||||||
ld a, 4
|
ld a, 4
|
||||||
; Set the final values for DMG mode
|
; Set the final values for DMG mode
|
||||||
ld d, 0
|
ld de, 8
|
||||||
ld e, $8
|
|
||||||
ld l, $7c
|
ld l, $7c
|
||||||
ret
|
ret
|
||||||
|
|
||||||
@ -920,16 +1003,16 @@ GetPaletteIndex:
|
|||||||
xor a
|
xor a
|
||||||
ret
|
ret
|
||||||
|
|
||||||
LoadPalettesFromIndex: ; a = index of combination
|
; optimizations in callers rely on this returning with b = 0
|
||||||
ld b, a
|
GetPaletteCombo:
|
||||||
; Multiply by 3
|
|
||||||
add b
|
|
||||||
add b
|
|
||||||
|
|
||||||
ld hl, PaletteCombinations
|
ld hl, PaletteCombinations
|
||||||
ld b, 0
|
ld b, 0
|
||||||
ld c, a
|
ld c, a
|
||||||
add hl, bc
|
add hl, bc
|
||||||
|
ret
|
||||||
|
|
||||||
|
LoadPalettesFromIndex: ; a = index of combination
|
||||||
|
call GetPaletteCombo
|
||||||
|
|
||||||
; Obj Palettes
|
; Obj Palettes
|
||||||
ld e, 0
|
ld e, 0
|
||||||
@ -937,11 +1020,12 @@ LoadPalettesFromIndex: ; a = index of combination
|
|||||||
ld a, [hli]
|
ld a, [hli]
|
||||||
push hl
|
push hl
|
||||||
ld hl, Palettes
|
ld hl, Palettes
|
||||||
ld b, 0
|
; b is already 0
|
||||||
ld c, a
|
ld c, a
|
||||||
add hl, bc
|
add hl, bc
|
||||||
ld d, 8
|
ld d, 8
|
||||||
call LoadObjPalettes
|
ld c, $6A
|
||||||
|
call LoadPalettes
|
||||||
pop hl
|
pop hl
|
||||||
bit 3, e
|
bit 3, e
|
||||||
jr nz, .loadBGPalette
|
jr nz, .loadBGPalette
|
||||||
@ -949,84 +1033,48 @@ LoadPalettesFromIndex: ; a = index of combination
|
|||||||
jr .loadObjPalette
|
jr .loadObjPalette
|
||||||
.loadBGPalette
|
.loadBGPalette
|
||||||
;BG Palette
|
;BG Palette
|
||||||
ld a, [hli]
|
ld c, [hl]
|
||||||
|
; b is already 0
|
||||||
ld hl, Palettes
|
ld hl, Palettes
|
||||||
ld b, 0
|
|
||||||
ld c, a
|
|
||||||
add hl, bc
|
add hl, bc
|
||||||
ld d, 8
|
ld d, 8
|
||||||
jp LoadBGPalettes
|
jr LoadBGPalettes
|
||||||
|
|
||||||
BrightenColor:
|
LoadPalettesFromHRAM:
|
||||||
|
ld hl, BgPalettes
|
||||||
|
ld d, 64
|
||||||
|
|
||||||
|
LoadBGPalettes:
|
||||||
|
ld e, 0
|
||||||
|
ld c, $68
|
||||||
|
|
||||||
|
LoadPalettes:
|
||||||
|
ld a, $80
|
||||||
|
or e
|
||||||
|
ld [c], a
|
||||||
|
inc c
|
||||||
|
.loop
|
||||||
ld a, [hli]
|
ld a, [hli]
|
||||||
ld e, a
|
ld [c], a
|
||||||
ld a, [hld]
|
dec d
|
||||||
ld d, a
|
jr nz, .loop
|
||||||
; RGB(1,1,1)
|
|
||||||
ld bc, $421
|
|
||||||
|
|
||||||
; Is blue maxed?
|
|
||||||
ld a, e
|
|
||||||
and $1F
|
|
||||||
cp $1F
|
|
||||||
jr nz, .blueNotMaxed
|
|
||||||
res 0, c
|
|
||||||
.blueNotMaxed
|
|
||||||
|
|
||||||
; Is green maxed?
|
|
||||||
ld a, e
|
|
||||||
and $E0
|
|
||||||
cp $E0
|
|
||||||
jr nz, .greenNotMaxed
|
|
||||||
ld a, d
|
|
||||||
and $3
|
|
||||||
cp $3
|
|
||||||
jr nz, .greenNotMaxed
|
|
||||||
res 5, c
|
|
||||||
.greenNotMaxed
|
|
||||||
|
|
||||||
; Is red maxed?
|
|
||||||
ld a, d
|
|
||||||
and $7C
|
|
||||||
cp $7C
|
|
||||||
jr nz, .redNotMaxed
|
|
||||||
res 2, b
|
|
||||||
.redNotMaxed
|
|
||||||
|
|
||||||
; Add de to bc
|
|
||||||
push hl
|
|
||||||
ld h, d
|
|
||||||
ld l, e
|
|
||||||
add hl, bc
|
|
||||||
ld d, h
|
|
||||||
ld e, l
|
|
||||||
pop hl
|
|
||||||
|
|
||||||
ld a, e
|
|
||||||
ld [hli], a
|
|
||||||
ld a, d
|
|
||||||
ld [hli], a
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ClearVRAMViaHDMA:
|
ClearVRAMViaHDMA:
|
||||||
ld hl, $FF51
|
ldh [$4F], a
|
||||||
|
ld hl, HDMAData
|
||||||
; Src
|
_ClearVRAMViaHDMA:
|
||||||
ld a, $88
|
ld c, $51
|
||||||
ld [hli], a
|
ld b, 5
|
||||||
xor a
|
.loop
|
||||||
ld [hli], a
|
ld a, [hli]
|
||||||
|
ldh [c], a
|
||||||
; Dest
|
inc c
|
||||||
ld a, $98
|
dec b
|
||||||
ld [hli], a
|
jr nz, .loop
|
||||||
ld a, $A0
|
|
||||||
ld [hli], a
|
|
||||||
|
|
||||||
; Do it
|
|
||||||
ld [hl], $12
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
; clobbers AF and HL
|
||||||
GetInputPaletteIndex:
|
GetInputPaletteIndex:
|
||||||
ld a, $20 ; Select directions
|
ld a, $20 ; Select directions
|
||||||
ldh [$00], a
|
ldh [$00], a
|
||||||
@ -1034,11 +1082,10 @@ GetInputPaletteIndex:
|
|||||||
cpl
|
cpl
|
||||||
and $F
|
and $F
|
||||||
ret z ; No direction keys pressed, no palette
|
ret z ; No direction keys pressed, no palette
|
||||||
push bc
|
|
||||||
ld c, 0
|
|
||||||
|
|
||||||
|
ld l, 0
|
||||||
.directionLoop
|
.directionLoop
|
||||||
inc c
|
inc l
|
||||||
rra
|
rra
|
||||||
jr nc, .directionLoop
|
jr nc, .directionLoop
|
||||||
|
|
||||||
@ -1051,40 +1098,24 @@ GetInputPaletteIndex:
|
|||||||
rla
|
rla
|
||||||
rla
|
rla
|
||||||
and $C
|
and $C
|
||||||
add c
|
add l
|
||||||
ld b, a
|
ld l, a
|
||||||
ldh a, [InputPalette]
|
ldh a, [InputPalette]
|
||||||
ld c, a
|
cp l
|
||||||
ld a, b
|
|
||||||
ldh [InputPalette], a
|
|
||||||
cp c
|
|
||||||
pop bc
|
|
||||||
ret z ; No change, don't load
|
ret z ; No change, don't load
|
||||||
|
ld a, l
|
||||||
|
ldh [InputPalette], a
|
||||||
; Slide into change Animation Palette
|
; Slide into change Animation Palette
|
||||||
|
|
||||||
ChangeAnimationPalette:
|
ChangeAnimationPalette:
|
||||||
push af
|
|
||||||
push hl
|
|
||||||
push bc
|
push bc
|
||||||
push de
|
push de
|
||||||
ld hl, KeyCombinationPalettes - 1 ; Input palettes are 1-based, 0 means nothing down
|
call GetKeyComboPalette
|
||||||
ld c ,a
|
call GetPaletteCombo
|
||||||
ld b, 0
|
inc l
|
||||||
add hl, bc
|
inc l
|
||||||
ld a, [hl]
|
ld c, [hl]
|
||||||
ld b, a
|
|
||||||
; Multiply by 3
|
|
||||||
add b
|
|
||||||
add b
|
|
||||||
|
|
||||||
ld hl, PaletteCombinations + 2; Background Palette
|
|
||||||
ld b, 0
|
|
||||||
ld c, a
|
|
||||||
add hl, bc
|
|
||||||
ld a, [hl]
|
|
||||||
ld hl, Palettes + 1
|
ld hl, Palettes + 1
|
||||||
ld b, 0
|
|
||||||
ld c, a
|
|
||||||
add hl, bc
|
add hl, bc
|
||||||
ld a, [hld]
|
ld a, [hld]
|
||||||
cp $7F ; Is white color?
|
cp $7F ; Is white color?
|
||||||
@ -1094,58 +1125,83 @@ ChangeAnimationPalette:
|
|||||||
.isWhite
|
.isWhite
|
||||||
push af
|
push af
|
||||||
ld a, [hli]
|
ld a, [hli]
|
||||||
|
|
||||||
push hl
|
push hl
|
||||||
ld hl, BgPalettes ; First color, all palette
|
ld hl, BgPalettes ; First color, all palettes
|
||||||
|
call ReplaceColorInAllPalettes
|
||||||
|
ld l, LOW(BgPalettes + 2) ; Second color, all palettes
|
||||||
call ReplaceColorInAllPalettes
|
call ReplaceColorInAllPalettes
|
||||||
pop hl
|
pop hl
|
||||||
ldh [BgPalettes + 2], a ; Second color, first palette
|
ldh [BgPalettes + 6], a ; Fourth color, first palette
|
||||||
|
|
||||||
ld a, [hli]
|
ld a, [hli]
|
||||||
push hl
|
push hl
|
||||||
ld hl, BgPalettes + 1 ; First color, all palette
|
ld hl, BgPalettes + 1 ; First color, all palettes
|
||||||
|
call ReplaceColorInAllPalettes
|
||||||
|
ld l, LOW(BgPalettes + 3) ; Second color, all palettes
|
||||||
call ReplaceColorInAllPalettes
|
call ReplaceColorInAllPalettes
|
||||||
pop hl
|
pop hl
|
||||||
ldh [BgPalettes + 3], a ; Second color, first palette
|
ldh [BgPalettes + 7], a ; Fourth color, first palette
|
||||||
|
|
||||||
pop af
|
pop af
|
||||||
jr z, .isNotWhite
|
jr z, .isNotWhite
|
||||||
inc hl
|
inc hl
|
||||||
inc hl
|
inc hl
|
||||||
.isNotWhite
|
.isNotWhite
|
||||||
|
; Mixing code by ISSOtm
|
||||||
|
ldh a, [BgPalettes + 7 * 8 + 2]
|
||||||
|
and ~$21
|
||||||
|
ld b, a
|
||||||
ld a, [hli]
|
ld a, [hli]
|
||||||
ldh [BgPalettes + 7 * 8 + 2], a ; Second color, 7th palette
|
and ~$21
|
||||||
|
add a, b
|
||||||
|
ld b, a
|
||||||
|
ld a, [BgPalettes + 7 * 8 + 3]
|
||||||
|
res 2, a ; and ~$04, but not touching carry
|
||||||
|
ld c, [hl]
|
||||||
|
res 2, c ; and ~$04, but not touching carry
|
||||||
|
adc a, c
|
||||||
|
rra ; Carry sort of "extends" the accumulator, we're bringing that bit back home
|
||||||
|
ld [BgPalettes + 7 * 8 + 3], a
|
||||||
|
ld a, b
|
||||||
|
rra
|
||||||
|
ld [BgPalettes + 7 * 8 + 2], a
|
||||||
|
dec l
|
||||||
|
|
||||||
ld a, [hli]
|
ld a, [hli]
|
||||||
ldh [BgPalettes + 7 * 8 + 3], a ; Second color, 7th palette
|
ldh [BgPalettes + 7 * 8 + 6], a ; Fourth color, 7th palette
|
||||||
|
ld a, [hli]
|
||||||
|
ldh [BgPalettes + 7 * 8 + 7], a ; Fourth color, 7th palette
|
||||||
|
|
||||||
ld a, [hli]
|
ld a, [hli]
|
||||||
ldh [BgPalettes + 4], a ; Third color, first palette
|
ldh [BgPalettes + 4], a ; Third color, first palette
|
||||||
ld a, [hl]
|
ld a, [hli]
|
||||||
ldh [BgPalettes + 5], a ; Third color, first palette
|
ldh [BgPalettes + 5], a ; Third color, first palette
|
||||||
|
|
||||||
|
|
||||||
call WaitFrame
|
call WaitFrame
|
||||||
ld hl, BgPalettes
|
call LoadPalettesFromHRAM
|
||||||
call LoadBGPalettes64
|
|
||||||
; Delay the wait loop while the user is selecting a palette
|
; Delay the wait loop while the user is selecting a palette
|
||||||
ld a, 30
|
ld a, 45
|
||||||
ldh [WaitLoopCounter], a
|
ldh [WaitLoopCounter], a
|
||||||
pop de
|
pop de
|
||||||
pop bc
|
pop bc
|
||||||
pop hl
|
|
||||||
pop af
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ReplaceColorInAllPalettes:
|
ReplaceColorInAllPalettes:
|
||||||
ld de, 8
|
ld de, 8
|
||||||
ld c, 8
|
ld c, e
|
||||||
.loop
|
.loop
|
||||||
ld [hl], a
|
ld [hl], a
|
||||||
add hl, de
|
add hl, de
|
||||||
dec c
|
dec c
|
||||||
jr nz, .loop
|
jr nz, .loop
|
||||||
ret
|
ret
|
||||||
|
|
||||||
LoadDMGTilemap:
|
LoadDMGTilemap:
|
||||||
push af
|
push af
|
||||||
call WaitFrame
|
call WaitFrame
|
||||||
ld a,$19 ; Trademark symbol
|
ld a, $19 ; Trademark symbol
|
||||||
ld [$9910], a ; ... put in the superscript position
|
ld [$9910], a ; ... put in the superscript position
|
||||||
ld hl,$992f ; Bottom right corner of the logo
|
ld hl,$992f ; Bottom right corner of the logo
|
||||||
ld c,$c ; Tiles in a logo row
|
ld c,$c ; Tiles in a logo row
|
||||||
@ -1155,27 +1211,20 @@ LoadDMGTilemap:
|
|||||||
ldd [hl], a
|
ldd [hl], a
|
||||||
dec c
|
dec c
|
||||||
jr nz, .tilemapLoop
|
jr nz, .tilemapLoop
|
||||||
ld l,$0f ; Jump to top row
|
ld l, $0f ; Jump to top row
|
||||||
jr .tilemapLoop
|
jr .tilemapLoop
|
||||||
.tilemapDone
|
.tilemapDone
|
||||||
pop af
|
pop af
|
||||||
ret
|
ret
|
||||||
|
|
||||||
InitWaveform:
|
HDMAData:
|
||||||
ld hl, $FF30
|
db $88, $00, $98, $A0, $12
|
||||||
; Init waveform
|
db $88, $00, $80, $00, $40
|
||||||
xor a
|
|
||||||
ld c, $10
|
|
||||||
.waveformLoop
|
|
||||||
ldi [hl], a
|
|
||||||
cpl
|
|
||||||
dec c
|
|
||||||
jr nz, .waveformLoop
|
|
||||||
ret
|
|
||||||
|
|
||||||
SECTION "ROMMax", ROM0[$900]
|
BootEnd:
|
||||||
; Prevent us from overflowing
|
IF BootEnd > $900
|
||||||
ds 1
|
FAIL "BootROM overflowed: {BootEnd}"
|
||||||
|
ENDC
|
||||||
|
|
||||||
SECTION "HRAM", HRAM[$FF80]
|
SECTION "HRAM", HRAM[$FF80]
|
||||||
TitleChecksum:
|
TitleChecksum:
|
||||||
|
@ -24,7 +24,7 @@ Start:
|
|||||||
ldh [$24], a
|
ldh [$24], a
|
||||||
|
|
||||||
; Init BG palette
|
; Init BG palette
|
||||||
ld a, $fc
|
ld a, $54
|
||||||
ldh [$47], a
|
ldh [$47], a
|
||||||
|
|
||||||
; Load logo from ROM.
|
; Load logo from ROM.
|
||||||
@ -69,14 +69,36 @@ Start:
|
|||||||
jr .tilemapLoop
|
jr .tilemapLoop
|
||||||
.tilemapDone
|
.tilemapDone
|
||||||
|
|
||||||
|
ld a, 30
|
||||||
|
ldh [$ff42], a
|
||||||
|
|
||||||
; Turn on LCD
|
; Turn on LCD
|
||||||
ld a, $91
|
ld a, $91
|
||||||
ldh [$40], a
|
ldh [$40], a
|
||||||
|
|
||||||
; Wait ~0.75 seconds
|
ld d, (-119) & $FF
|
||||||
ld b, 45
|
ld c, 15
|
||||||
call WaitBFrames
|
|
||||||
|
.animate
|
||||||
|
call WaitFrame
|
||||||
|
ld a, d
|
||||||
|
sra a
|
||||||
|
sra a
|
||||||
|
ldh [$ff42], a
|
||||||
|
ld a, d
|
||||||
|
add c
|
||||||
|
ld d, a
|
||||||
|
ld a, c
|
||||||
|
cp 8
|
||||||
|
jr nz, .noPaletteChange
|
||||||
|
ld a, $A8
|
||||||
|
ldh [$47], a
|
||||||
|
.noPaletteChange
|
||||||
|
dec c
|
||||||
|
jr nz, .animate
|
||||||
|
ld a, $fc
|
||||||
|
ldh [$47], a
|
||||||
|
|
||||||
; Play first sound
|
; Play first sound
|
||||||
ld a, $83
|
ld a, $83
|
||||||
call PlaySound
|
call PlaySound
|
||||||
@ -85,9 +107,11 @@ Start:
|
|||||||
; Play second sound
|
; Play second sound
|
||||||
ld a, $c1
|
ld a, $c1
|
||||||
call PlaySound
|
call PlaySound
|
||||||
|
|
||||||
|
|
||||||
; Wait ~1.15 seconds
|
|
||||||
ld b, 70
|
; Wait ~1 second
|
||||||
|
ld b, 60
|
||||||
call WaitBFrames
|
call WaitBFrames
|
||||||
|
|
||||||
; Set registers to match the original DMG boot
|
; Set registers to match the original DMG boot
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
#include <assert.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <io.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void pair(size_t count, uint8_t byte)
|
|
||||||
{
|
|
||||||
static size_t unique_count = 0;
|
|
||||||
static uint8_t unique_data[15];
|
|
||||||
if (count == 1) {
|
|
||||||
unique_data[unique_count++] = byte;
|
|
||||||
assert(unique_count <= 15);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
assert(count <= 15);
|
|
||||||
uint8_t control = (count << 4) | unique_count;
|
|
||||||
putchar(control);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < unique_count; i++) {
|
|
||||||
putchar(unique_data[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count != 0) {
|
|
||||||
putchar(byte);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
assert(control == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
unique_count = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
size_t count = 1;
|
|
||||||
uint8_t byte = getchar();
|
|
||||||
int new;
|
|
||||||
size_t position = 0;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
_setmode(0,_O_BINARY);
|
|
||||||
_setmode(1,_O_BINARY);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while ((new = getchar()) != EOF) {
|
|
||||||
if (byte == new) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pair(count, byte);
|
|
||||||
byte = new;
|
|
||||||
count = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pair(count, byte);
|
|
||||||
pair(0, 0);
|
|
||||||
}
|
|
90
BootROMs/pb12.c
Normal file
90
BootROMs/pb12.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
void opts(uint8_t byte, uint8_t *options)
|
||||||
|
{
|
||||||
|
*(options++) = byte | ((byte << 1) & 0xff);
|
||||||
|
*(options++) = byte & (byte << 1);
|
||||||
|
*(options++) = byte | ((byte >> 1) & 0xff);
|
||||||
|
*(options++) = byte & (byte >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
static uint8_t source[0x4000];
|
||||||
|
size_t size = read(STDIN_FILENO, &source, sizeof(source));
|
||||||
|
unsigned pos = 0;
|
||||||
|
assert(size <= 0x4000);
|
||||||
|
while (size && source[size - 1] == 0) {
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t literals[8];
|
||||||
|
size_t literals_size = 0;
|
||||||
|
unsigned bits = 0;
|
||||||
|
unsigned control = 0;
|
||||||
|
unsigned prev[2] = {-1, -1}; // Unsigned to allow "not set" values
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
uint8_t byte = 0;
|
||||||
|
if (pos == size){
|
||||||
|
if (bits == 0) break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
byte = source[pos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte == prev[0] || byte == prev[1]) {
|
||||||
|
bits += 2;
|
||||||
|
control <<= 1;
|
||||||
|
control |= 1;
|
||||||
|
control <<= 1;
|
||||||
|
if (byte == prev[1]) {
|
||||||
|
control |= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bits += 2;
|
||||||
|
control <<= 2;
|
||||||
|
uint8_t options[4];
|
||||||
|
opts(prev[1], options);
|
||||||
|
bool found = false;
|
||||||
|
for (unsigned i = 0; i < 4; i++) {
|
||||||
|
if (options[i] == byte) {
|
||||||
|
// 01 = modify
|
||||||
|
control |= 1;
|
||||||
|
|
||||||
|
bits += 2;
|
||||||
|
control <<= 2;
|
||||||
|
control |= i;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
literals[literals_size++] = byte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prev[0] = prev[1];
|
||||||
|
prev[1] = byte;
|
||||||
|
if (bits >= 8) {
|
||||||
|
uint8_t outctl = control >> (bits - 8);
|
||||||
|
assert(outctl != 1);
|
||||||
|
write(STDOUT_FILENO, &outctl, 1);
|
||||||
|
write(STDOUT_FILENO, literals, literals_size);
|
||||||
|
bits -= 8;
|
||||||
|
control &= (1 << bits) - 1;
|
||||||
|
literals_size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint8_t end_byte = 1;
|
||||||
|
write(STDOUT_FILENO, &end_byte, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
@interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate>
|
||||||
|
|
||||||
@property IBOutlet NSWindow *preferencesWindow;
|
@property IBOutlet NSWindow *preferencesWindow;
|
||||||
@property (strong) IBOutlet NSView *graphicsTab;
|
@property (strong) IBOutlet NSView *graphicsTab;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
#include "GBButtons.h"
|
#include "GBButtons.h"
|
||||||
|
#include "GBView.h"
|
||||||
#include <Core/gb.h>
|
#include <Core/gb.h>
|
||||||
#import <Carbon/Carbon.h>
|
#import <Carbon/Carbon.h>
|
||||||
|
#import <JoyKit/JoyKit.h>
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
{
|
{
|
||||||
@ -36,11 +38,20 @@
|
|||||||
@"GBColorCorrection": @(GB_COLOR_CORRECTION_EMULATE_HARDWARE),
|
@"GBColorCorrection": @(GB_COLOR_CORRECTION_EMULATE_HARDWARE),
|
||||||
@"GBHighpassFilter": @(GB_HIGHPASS_REMOVE_DC_OFFSET),
|
@"GBHighpassFilter": @(GB_HIGHPASS_REMOVE_DC_OFFSET),
|
||||||
@"GBRewindLength": @(10),
|
@"GBRewindLength": @(10),
|
||||||
|
@"GBFrameBlendingMode": @([defaults boolForKey:@"DisableFrameBlending"]? GB_FRAME_BLENDING_MODE_DISABLED : GB_FRAME_BLENDING_MODE_ACCURATE),
|
||||||
|
|
||||||
@"GBDMGModel": @(GB_MODEL_DMG_B),
|
@"GBDMGModel": @(GB_MODEL_DMG_B),
|
||||||
@"GBCGBModel": @(GB_MODEL_CGB_E),
|
@"GBCGBModel": @(GB_MODEL_CGB_E),
|
||||||
@"GBSGBModel": @(GB_MODEL_SGB2),
|
@"GBSGBModel": @(GB_MODEL_SGB2),
|
||||||
|
@"GBRumbleMode": @(GB_RUMBLE_CARTRIDGE_ONLY),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
[JOYController startOnRunLoop:[NSRunLoop currentRunLoop] withOptions:@{
|
||||||
|
JOYAxes2DEmulateButtonsKey: @YES,
|
||||||
|
JOYHatsEmulateButtonsKey: @YES,
|
||||||
|
}];
|
||||||
|
|
||||||
|
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleDeveloperMode:(id)sender
|
- (IBAction)toggleDeveloperMode:(id)sender
|
||||||
@ -92,4 +103,8 @@
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
|
||||||
|
{
|
||||||
|
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfFile:notification.identifier display:YES];
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#include "GBView.h"
|
#include "GBView.h"
|
||||||
#include "GBImageView.h"
|
#include "GBImageView.h"
|
||||||
|
#include "GBSplitView.h"
|
||||||
|
|
||||||
@interface Document : NSDocument <NSWindowDelegate, GBImageViewDelegate, NSTableViewDataSource, NSTableViewDelegate>
|
@class GBCheatWindowController;
|
||||||
|
|
||||||
|
@interface Document : NSDocument <NSWindowDelegate, GBImageViewDelegate, NSTableViewDataSource, NSTableViewDelegate, NSSplitViewDelegate>
|
||||||
@property (strong) IBOutlet GBView *view;
|
@property (strong) IBOutlet GBView *view;
|
||||||
@property (strong) IBOutlet NSTextView *consoleOutput;
|
@property (strong) IBOutlet NSTextView *consoleOutput;
|
||||||
@property (strong) IBOutlet NSPanel *consoleWindow;
|
@property (strong) IBOutlet NSPanel *consoleWindow;
|
||||||
@ -30,6 +33,10 @@
|
|||||||
@property (strong) IBOutlet NSButton *feedSaveButton;
|
@property (strong) IBOutlet NSButton *feedSaveButton;
|
||||||
@property (strong) IBOutlet NSTextView *debuggerSideViewInput;
|
@property (strong) IBOutlet NSTextView *debuggerSideViewInput;
|
||||||
@property (strong) IBOutlet NSTextView *debuggerSideView;
|
@property (strong) IBOutlet NSTextView *debuggerSideView;
|
||||||
|
@property (strong) IBOutlet GBSplitView *debuggerSplitView;
|
||||||
|
@property (strong) IBOutlet NSBox *debuggerVerticalLine;
|
||||||
|
@property (strong) IBOutlet NSPanel *cheatsWindow;
|
||||||
|
@property (strong) IBOutlet GBCheatWindowController *cheatWindowController;
|
||||||
|
|
||||||
-(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;
|
||||||
|
316
Cocoa/Document.m
316
Cocoa/Document.m
@ -7,6 +7,7 @@
|
|||||||
#include "HexFiend/HexFiend.h"
|
#include "HexFiend/HexFiend.h"
|
||||||
#include "GBMemoryByteArray.h"
|
#include "GBMemoryByteArray.h"
|
||||||
#include "GBWarningPopover.h"
|
#include "GBWarningPopover.h"
|
||||||
|
#include "GBCheatWindowController.h"
|
||||||
|
|
||||||
/* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */
|
/* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */
|
||||||
/* Todo: Split into category files! This is so messy!!! */
|
/* Todo: Split into category files! This is so messy!!! */
|
||||||
@ -61,6 +62,8 @@ enum model {
|
|||||||
size_t audioBufferSize;
|
size_t audioBufferSize;
|
||||||
size_t audioBufferPosition;
|
size_t audioBufferPosition;
|
||||||
size_t audioBufferNeeded;
|
size_t audioBufferNeeded;
|
||||||
|
|
||||||
|
bool borderModeChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property GBAudioClient *audioClient;
|
@property GBAudioClient *audioClient;
|
||||||
@ -74,8 +77,16 @@ enum model {
|
|||||||
topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin
|
topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin
|
||||||
exposure:(unsigned) exposure;
|
exposure:(unsigned) exposure;
|
||||||
- (void) gotNewSample:(GB_sample_t *)sample;
|
- (void) gotNewSample:(GB_sample_t *)sample;
|
||||||
|
- (void) rumbleChanged:(double)amp;
|
||||||
|
- (void) loadBootROM:(GB_boot_rom_t)type;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
static void boot_rom_load(GB_gameboy_t *gb, GB_boot_rom_t type)
|
||||||
|
{
|
||||||
|
Document *self = (__bridge Document *)GB_get_user_data(gb);
|
||||||
|
[self loadBootROM: type];
|
||||||
|
}
|
||||||
|
|
||||||
static void vblank(GB_gameboy_t *gb)
|
static void vblank(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
Document *self = (__bridge Document *)GB_get_user_data(gb);
|
Document *self = (__bridge Document *)GB_get_user_data(gb);
|
||||||
@ -131,6 +142,12 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
[self gotNewSample:sample];
|
[self gotNewSample:sample];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
||||||
|
{
|
||||||
|
Document *self = (__bridge Document *)GB_get_user_data(gb);
|
||||||
|
[self rumbleChanged:amp];
|
||||||
|
}
|
||||||
|
|
||||||
@implementation Document
|
@implementation Document
|
||||||
{
|
{
|
||||||
GB_gameboy_t gb;
|
GB_gameboy_t gb;
|
||||||
@ -140,7 +157,8 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
NSMutableArray *debugger_input_queue;
|
NSMutableArray *debugger_input_queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)init {
|
- (instancetype)init
|
||||||
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
has_debugger_input = [[NSConditionLock alloc] initWithCondition:0];
|
has_debugger_input = [[NSConditionLock alloc] initWithCondition:0];
|
||||||
@ -184,26 +202,82 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) updatePalette
|
||||||
|
{
|
||||||
|
switch ([[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"]) {
|
||||||
|
case 1:
|
||||||
|
GB_set_palette(&gb, &GB_PALETTE_DMG);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
GB_set_palette(&gb, &GB_PALETTE_MGB);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
GB_set_palette(&gb, &GB_PALETTE_GBL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
GB_set_palette(&gb, &GB_PALETTE_GREY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) updateBorderMode
|
||||||
|
{
|
||||||
|
borderModeChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) updateRumbleMode
|
||||||
|
{
|
||||||
|
GB_set_rumble_mode(&gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRumbleMode"]);
|
||||||
|
}
|
||||||
|
|
||||||
- (void) initCommon
|
- (void) initCommon
|
||||||
{
|
{
|
||||||
GB_init(&gb, [self internalModel]);
|
GB_init(&gb, [self internalModel]);
|
||||||
GB_set_user_data(&gb, (__bridge void *)(self));
|
GB_set_user_data(&gb, (__bridge void *)(self));
|
||||||
|
GB_set_boot_rom_load_callback(&gb, (GB_boot_rom_load_callback_t)boot_rom_load);
|
||||||
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
||||||
GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog);
|
GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog);
|
||||||
GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput);
|
GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput);
|
||||||
GB_set_async_input_callback(&gb, (GB_input_callback_t) asyncConsoleInput);
|
GB_set_async_input_callback(&gb, (GB_input_callback_t) asyncConsoleInput);
|
||||||
GB_set_color_correction_mode(&gb, (GB_color_correction_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorCorrection"]);
|
GB_set_color_correction_mode(&gb, (GB_color_correction_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorCorrection"]);
|
||||||
|
GB_set_border_mode(&gb, (GB_border_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBBorderMode"]);
|
||||||
|
[self updatePalette];
|
||||||
GB_set_rgb_encode_callback(&gb, rgbEncode);
|
GB_set_rgb_encode_callback(&gb, rgbEncode);
|
||||||
GB_set_camera_get_pixel_callback(&gb, cameraGetPixel);
|
GB_set_camera_get_pixel_callback(&gb, cameraGetPixel);
|
||||||
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
|
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
|
||||||
GB_set_highpass_filter_mode(&gb, (GB_highpass_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBHighpassFilter"]);
|
GB_set_highpass_filter_mode(&gb, (GB_highpass_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBHighpassFilter"]);
|
||||||
GB_set_rewind_length(&gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRewindLength"]);
|
GB_set_rewind_length(&gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRewindLength"]);
|
||||||
GB_apu_set_sample_callback(&gb, audioCallback);
|
GB_apu_set_sample_callback(&gb, audioCallback);
|
||||||
|
GB_set_rumble_callback(&gb, rumbleCallback);
|
||||||
|
[self updateRumbleMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) updateMinSize
|
||||||
|
{
|
||||||
|
self.mainWindow.contentMinSize = NSMakeSize(GB_get_screen_width(&gb), GB_get_screen_height(&gb));
|
||||||
|
if (self.mainWindow.contentView.bounds.size.width < GB_get_screen_width(&gb) ||
|
||||||
|
self.mainWindow.contentView.bounds.size.width < GB_get_screen_height(&gb)) {
|
||||||
|
[self.mainWindow zoom:nil];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) vblank
|
- (void) vblank
|
||||||
{
|
{
|
||||||
[self.view flip];
|
[self.view flip];
|
||||||
|
if (borderModeChanged) {
|
||||||
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||||
|
size_t previous_width = GB_get_screen_width(&gb);
|
||||||
|
GB_set_border_mode(&gb, (GB_border_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBBorderMode"]);
|
||||||
|
if (GB_get_screen_width(&gb) != previous_width) {
|
||||||
|
[self.view screenSizeChanged];
|
||||||
|
[self updateMinSize];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
borderModeChanged = false;
|
||||||
|
}
|
||||||
GB_set_pixels_output(&gb, self.view.pixels);
|
GB_set_pixels_output(&gb, self.view.pixels);
|
||||||
if (self.vramWindow.isVisible) {
|
if (self.vramWindow.isVisible) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
@ -244,6 +318,11 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
[audioLock unlock];
|
[audioLock unlock];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)rumbleChanged:(double)amp
|
||||||
|
{
|
||||||
|
[_view setRumble:amp];
|
||||||
|
}
|
||||||
|
|
||||||
- (void) run
|
- (void) run
|
||||||
{
|
{
|
||||||
running = true;
|
running = true;
|
||||||
@ -257,6 +336,12 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
[audioLock wait];
|
[audioLock wait];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stopping) {
|
||||||
|
memset(buffer, 0, nFrames * sizeof(*buffer));
|
||||||
|
[audioLock unlock];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (audioBufferPosition >= nFrames && audioBufferPosition < nFrames + 4800) {
|
if (audioBufferPosition >= nFrames && audioBufferPosition < nFrames + 4800) {
|
||||||
memcpy(buffer, audioBuffer, nFrames * sizeof(*buffer));
|
memcpy(buffer, audioBuffer, nFrames * sizeof(*buffer));
|
||||||
memmove(audioBuffer, audioBuffer + nFrames, (audioBufferPosition - nFrames) * sizeof(*buffer));
|
memmove(audioBuffer, audioBuffer + nFrames, (audioBufferPosition - nFrames) * sizeof(*buffer));
|
||||||
@ -273,6 +358,25 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
}
|
}
|
||||||
NSTimer *hex_timer = [NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(reloadMemoryView) userInfo:nil repeats:YES];
|
NSTimer *hex_timer = [NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(reloadMemoryView) userInfo:nil repeats:YES];
|
||||||
[[NSRunLoop mainRunLoop] addTimer:hex_timer forMode:NSDefaultRunLoopMode];
|
[[NSRunLoop mainRunLoop] addTimer:hex_timer forMode:NSDefaultRunLoopMode];
|
||||||
|
|
||||||
|
/* Clear pending alarms, don't play alarms while playing */
|
||||||
|
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBNotificationsUsed"]) {
|
||||||
|
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
||||||
|
for (NSUserNotification *notification in [center scheduledNotifications]) {
|
||||||
|
if ([notification.identifier isEqualToString:self.fileName]) {
|
||||||
|
[center removeScheduledNotification:notification];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (NSUserNotification *notification in [center deliveredNotifications]) {
|
||||||
|
if ([notification.identifier isEqualToString:self.fileName]) {
|
||||||
|
[center removeDeliveredNotification:notification];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
if (rewind) {
|
if (rewind) {
|
||||||
rewind = false;
|
rewind = false;
|
||||||
@ -295,6 +399,25 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
self.audioClient = nil;
|
self.audioClient = nil;
|
||||||
self.view.mouseHidingEnabled = NO;
|
self.view.mouseHidingEnabled = NO;
|
||||||
GB_save_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
|
GB_save_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
|
||||||
|
GB_save_cheats(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"cht"] UTF8String]);
|
||||||
|
unsigned time_to_alarm = GB_time_to_alarm(&gb);
|
||||||
|
|
||||||
|
if (time_to_alarm) {
|
||||||
|
NSUserNotification *notification = [[NSUserNotification alloc] init];
|
||||||
|
NSString *friendlyName = [[self.fileName lastPathComponent] stringByDeletingPathExtension];
|
||||||
|
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\([^)]+\\)|\\[[^\\]]+\\]" options:0 error:nil];
|
||||||
|
friendlyName = [regex stringByReplacingMatchesInString:friendlyName options:0 range:NSMakeRange(0, [friendlyName length]) withTemplate:@""];
|
||||||
|
friendlyName = [friendlyName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||||
|
|
||||||
|
notification.title = [NSString stringWithFormat:@"%@ Played an Alarm", friendlyName];
|
||||||
|
notification.informativeText = [NSString stringWithFormat:@"%@ requested your attention by playing a scheduled alarm", friendlyName];
|
||||||
|
notification.identifier = self.fileName;
|
||||||
|
notification.deliveryDate = [NSDate dateWithTimeIntervalSinceNow:time_to_alarm];
|
||||||
|
notification.soundName = NSUserNotificationDefaultSoundName;
|
||||||
|
[[NSUserNotificationCenter defaultUserNotificationCenter] scheduleNotification:notification];
|
||||||
|
[[NSUserDefaults standardUserDefaults] setBool:true forKey:@"GBNotificationsUsed"];
|
||||||
|
}
|
||||||
|
[_view setRumble:0];
|
||||||
stopping = false;
|
stopping = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,21 +435,32 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
if (GB_debugger_is_stopped(&gb)) {
|
if (GB_debugger_is_stopped(&gb)) {
|
||||||
[self interruptDebugInputRead];
|
[self interruptDebugInputRead];
|
||||||
}
|
}
|
||||||
|
[audioLock lock];
|
||||||
stopping = true;
|
stopping = true;
|
||||||
|
[audioLock signal];
|
||||||
|
[audioLock unlock];
|
||||||
running = false;
|
running = false;
|
||||||
while (stopping);
|
while (stopping) {
|
||||||
|
[audioLock lock];
|
||||||
|
[audioLock signal];
|
||||||
|
[audioLock unlock];
|
||||||
|
}
|
||||||
GB_debugger_set_disabled(&gb, false);
|
GB_debugger_set_disabled(&gb, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) loadBootROM
|
- (void) loadBootROM: (GB_boot_rom_t)type
|
||||||
{
|
{
|
||||||
static NSString * const boot_names[] = {@"dmg_boot", @"cgb_boot", @"agb_boot", @"sgb_boot"};
|
static NSString *const names[] = {
|
||||||
if ([self internalModel] == GB_MODEL_SGB2) {
|
[GB_BOOT_ROM_DMG0] = @"dmg0_boot",
|
||||||
GB_load_boot_rom(&gb, [[self bootROMPathForName:@"sgb2_boot"] UTF8String]);
|
[GB_BOOT_ROM_DMG] = @"dmg_boot",
|
||||||
}
|
[GB_BOOT_ROM_MGB] = @"mgb_boot",
|
||||||
else {
|
[GB_BOOT_ROM_SGB] = @"sgb_boot",
|
||||||
GB_load_boot_rom(&gb, [[self bootROMPathForName:boot_names[current_model - 1]] UTF8String]);
|
[GB_BOOT_ROM_SGB2] = @"sgb2_boot",
|
||||||
}
|
[GB_BOOT_ROM_CGB0] = @"cgb0_boot",
|
||||||
|
[GB_BOOT_ROM_CGB] = @"cgb_boot",
|
||||||
|
[GB_BOOT_ROM_AGB] = @"agb_boot",
|
||||||
|
};
|
||||||
|
GB_load_boot_rom(&gb, [[self bootROMPathForName:names[type]] UTF8String]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)reset:(id)sender
|
- (IBAction)reset:(id)sender
|
||||||
@ -338,8 +472,6 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
current_model = (enum model)[sender tag];
|
current_model = (enum model)[sender tag];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self loadBootROM];
|
|
||||||
|
|
||||||
if (!modelsChanging && [sender tag] == MODEL_NONE) {
|
if (!modelsChanging && [sender tag] == MODEL_NONE) {
|
||||||
GB_reset(&gb);
|
GB_reset(&gb);
|
||||||
}
|
}
|
||||||
@ -351,11 +483,7 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
[self.view screenSizeChanged];
|
[self.view screenSizeChanged];
|
||||||
}
|
}
|
||||||
|
|
||||||
self.mainWindow.contentMinSize = NSMakeSize(GB_get_screen_width(&gb), GB_get_screen_height(&gb));
|
[self updateMinSize];
|
||||||
if (self.mainWindow.contentView.bounds.size.width < GB_get_screen_width(&gb) ||
|
|
||||||
self.mainWindow.contentView.bounds.size.width < GB_get_screen_height(&gb)) {
|
|
||||||
[self.mainWindow zoom:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([sender tag] != 0) {
|
if ([sender tag] != 0) {
|
||||||
/* User explictly selected a model, save the preference */
|
/* User explictly selected a model, save the preference */
|
||||||
@ -389,6 +517,7 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
[cameraSession stopRunning];
|
[cameraSession stopRunning];
|
||||||
|
self.view.gb = NULL;
|
||||||
GB_free(&gb);
|
GB_free(&gb);
|
||||||
if (cameraImage) {
|
if (cameraImage) {
|
||||||
CVBufferRelease(cameraImage);
|
CVBufferRelease(cameraImage);
|
||||||
@ -398,9 +527,11 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowControllerDidLoadNib:(NSWindowController *)aController {
|
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
|
||||||
|
{
|
||||||
[super windowControllerDidLoadNib:aController];
|
[super windowControllerDidLoadNib:aController];
|
||||||
|
// Interface Builder bug?
|
||||||
|
[self.consoleWindow setContentSize:self.consoleWindow.minSize];
|
||||||
/* Close Open Panels, if any */
|
/* Close Open Panels, if any */
|
||||||
for (NSWindow *window in [[NSApplication sharedApplication] windows]) {
|
for (NSWindow *window in [[NSApplication sharedApplication] windows]) {
|
||||||
if ([window isKindOfClass:[NSOpenPanel class]]) {
|
if ([window isKindOfClass:[NSOpenPanel class]]) {
|
||||||
@ -422,7 +553,7 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
|
|
||||||
self.consoleOutput.textContainerInset = NSMakeSize(4, 4);
|
self.consoleOutput.textContainerInset = NSMakeSize(4, 4);
|
||||||
[self.view becomeFirstResponder];
|
[self.view becomeFirstResponder];
|
||||||
self.view.shouldBlendFrameWithPrevious = ![[NSUserDefaults standardUserDefaults] boolForKey:@"DisableFrameBlending"];
|
self.view.frameBlendingMode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBFrameBlendingMode"];
|
||||||
CGRect window_frame = self.mainWindow.frame;
|
CGRect window_frame = self.mainWindow.frame;
|
||||||
window_frame.size.width = MAX([[NSUserDefaults standardUserDefaults] integerForKey:@"LastWindowWidth"],
|
window_frame.size.width = MAX([[NSUserDefaults standardUserDefaults] integerForKey:@"LastWindowWidth"],
|
||||||
window_frame.size.width);
|
window_frame.size.width);
|
||||||
@ -435,6 +566,7 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
[self.feedSaveButton removeFromSuperview];
|
[self.feedSaveButton removeFromSuperview];
|
||||||
|
|
||||||
self.consoleWindow.title = [NSString stringWithFormat:@"Debug Console – %@", [[self.fileURL path] lastPathComponent]];
|
self.consoleWindow.title = [NSString stringWithFormat:@"Debug Console – %@", [[self.fileURL path] lastPathComponent]];
|
||||||
|
self.debuggerSplitView.dividerColor = [NSColor clearColor];
|
||||||
|
|
||||||
/* contentView.superview.subviews.lastObject is the titlebar view */
|
/* contentView.superview.subviews.lastObject is the titlebar view */
|
||||||
NSView *titleView = self.printerFeedWindow.contentView.superview.subviews.lastObject;
|
NSView *titleView = self.printerFeedWindow.contentView.superview.subviews.lastObject;
|
||||||
@ -451,6 +583,26 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
name:@"GBColorCorrectionChanged"
|
name:@"GBColorCorrectionChanged"
|
||||||
object:nil];
|
object:nil];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(updateFrameBlendingMode)
|
||||||
|
name:@"GBFrameBlendingModeChanged"
|
||||||
|
object:nil];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(updatePalette)
|
||||||
|
name:@"GBColorPaletteChanged"
|
||||||
|
object:nil];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(updateBorderMode)
|
||||||
|
name:@"GBBorderModeChanged"
|
||||||
|
object:nil];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(updateRumbleMode)
|
||||||
|
name:@"GBRumbleModeChanged"
|
||||||
|
object:nil];
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
selector:@selector(updateRewindLength)
|
selector:@selector(updateRewindLength)
|
||||||
name:@"GBRewindLengthChanged"
|
name:@"GBRewindLengthChanged"
|
||||||
@ -536,11 +688,13 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
self.memoryBankItem.enabled = false;
|
self.memoryBankItem.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (BOOL)autosavesInPlace {
|
+ (BOOL)autosavesInPlace
|
||||||
|
{
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)windowNibName {
|
- (NSString *)windowNibName
|
||||||
|
{
|
||||||
// Override returning the nib file name of the document
|
// Override returning the nib file name of the document
|
||||||
// If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
|
// If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
|
||||||
return @"Document";
|
return @"Document";
|
||||||
@ -554,9 +708,18 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
- (void) loadROM
|
- (void) loadROM
|
||||||
{
|
{
|
||||||
NSString *rom_warnings = [self captureOutputForBlock:^{
|
NSString *rom_warnings = [self captureOutputForBlock:^{
|
||||||
GB_load_rom(&gb, [self.fileName UTF8String]);
|
|
||||||
GB_load_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
|
|
||||||
GB_debugger_clear_symbols(&gb);
|
GB_debugger_clear_symbols(&gb);
|
||||||
|
if ([[self.fileType pathExtension] isEqualToString:@"isx"]) {
|
||||||
|
GB_load_isx(&gb, [self.fileName UTF8String]);
|
||||||
|
GB_load_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"ram"] UTF8String]);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_load_rom(&gb, [self.fileName UTF8String]);
|
||||||
|
}
|
||||||
|
GB_load_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
|
||||||
|
GB_load_cheats(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"cht"] UTF8String]);
|
||||||
|
[self.cheatWindowController cheatsUpdated];
|
||||||
GB_debugger_load_symbol_file(&gb, [[[NSBundle mainBundle] pathForResource:@"registers" ofType:@"sym"] UTF8String]);
|
GB_debugger_load_symbol_file(&gb, [[[NSBundle mainBundle] pathForResource:@"registers" ofType:@"sym"] UTF8String]);
|
||||||
GB_debugger_load_symbol_file(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sym"] UTF8String]);
|
GB_debugger_load_symbol_file(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sym"] UTF8String]);
|
||||||
}];
|
}];
|
||||||
@ -597,15 +760,9 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
[[NSUserDefaults standardUserDefaults] setBool:!self.audioClient.isPlaying forKey:@"Mute"];
|
[[NSUserDefaults standardUserDefaults] setBool:!self.audioClient.isPlaying forKey:@"Mute"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleBlend:(id)sender
|
|
||||||
{
|
|
||||||
self.view.shouldBlendFrameWithPrevious ^= YES;
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:!self.view.shouldBlendFrameWithPrevious forKey:@"DisableFrameBlending"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)anItem
|
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)anItem
|
||||||
{
|
{
|
||||||
if([anItem action] == @selector(mute:)) {
|
if ([anItem action] == @selector(mute:)) {
|
||||||
[(NSMenuItem*)anItem setState:!self.audioClient.isPlaying];
|
[(NSMenuItem*)anItem setState:!self.audioClient.isPlaying];
|
||||||
}
|
}
|
||||||
else if ([anItem action] == @selector(togglePause:)) {
|
else if ([anItem action] == @selector(togglePause:)) {
|
||||||
@ -615,9 +772,6 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
else if ([anItem action] == @selector(reset:) && anItem.tag != MODEL_NONE) {
|
else if ([anItem action] == @selector(reset:) && anItem.tag != MODEL_NONE) {
|
||||||
[(NSMenuItem*)anItem setState:anItem.tag == current_model];
|
[(NSMenuItem*)anItem setState:anItem.tag == current_model];
|
||||||
}
|
}
|
||||||
else if ([anItem action] == @selector(toggleBlend:)) {
|
|
||||||
[(NSMenuItem*)anItem setState:self.view.shouldBlendFrameWithPrevious];
|
|
||||||
}
|
|
||||||
else if ([anItem action] == @selector(interrupt:)) {
|
else if ([anItem action] == @selector(interrupt:)) {
|
||||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"DeveloperMode"]) {
|
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"DeveloperMode"]) {
|
||||||
return false;
|
return false;
|
||||||
@ -629,6 +783,9 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
else if ([anItem action] == @selector(connectPrinter:)) {
|
else if ([anItem action] == @selector(connectPrinter:)) {
|
||||||
[(NSMenuItem*)anItem setState:accessory == GBAccessoryPrinter];
|
[(NSMenuItem*)anItem setState:accessory == GBAccessoryPrinter];
|
||||||
}
|
}
|
||||||
|
else if ([anItem action] == @selector(toggleCheats:)) {
|
||||||
|
[(NSMenuItem*)anItem setState:GB_cheats_enabled(&gb)];
|
||||||
|
}
|
||||||
return [super validateUserInterfaceItem:anItem];
|
return [super validateUserInterfaceItem:anItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -655,8 +812,8 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
|
|
||||||
NSRect rect = window.contentView.frame;
|
NSRect rect = window.contentView.frame;
|
||||||
|
|
||||||
int titlebarSize = window.contentView.superview.frame.size.height - rect.size.height;
|
unsigned titlebarSize = window.contentView.superview.frame.size.height - rect.size.height;
|
||||||
int step = width / [[window screen] backingScaleFactor];
|
unsigned step = width / [[window screen] backingScaleFactor];
|
||||||
|
|
||||||
rect.size.width = floor(rect.size.width / step) * step + step;
|
rect.size.width = floor(rect.size.width / step) * step + step;
|
||||||
rect.size.height = rect.size.width * height / width + titlebarSize;
|
rect.size.height = rect.size.width * height / width + titlebarSize;
|
||||||
@ -737,9 +894,7 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (![console_output_timer isValid]) {
|
if (![console_output_timer isValid]) {
|
||||||
console_output_timer = [NSTimer timerWithTimeInterval:(NSTimeInterval)0.05 repeats:NO block:^(NSTimer * _Nonnull timer) {
|
console_output_timer = [NSTimer timerWithTimeInterval:(NSTimeInterval)0.05 target:self selector:@selector(appendPendingOutput) userInfo:nil repeats:NO];
|
||||||
[self appendPendingOutput];
|
|
||||||
}];
|
|
||||||
[[NSRunLoop mainRunLoop] addTimer:console_output_timer forMode:NSDefaultRunLoopMode];
|
[[NSRunLoop mainRunLoop] addTimer:console_output_timer forMode:NSDefaultRunLoopMode];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -754,7 +909,8 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
[self.consoleWindow orderBack:nil];
|
[self.consoleWindow orderBack:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)consoleInput:(NSTextField *)sender {
|
- (IBAction)consoleInput:(NSTextField *)sender
|
||||||
|
{
|
||||||
NSString *line = [sender stringValue];
|
NSString *line = [sender stringValue];
|
||||||
if ([line isEqualToString:@""] && lastConsoleInput) {
|
if ([line isEqualToString:@""] && lastConsoleInput) {
|
||||||
line = lastConsoleInput;
|
line = lastConsoleInput;
|
||||||
@ -932,7 +1088,7 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
{
|
{
|
||||||
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef) data);
|
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef) data);
|
||||||
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
|
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
|
||||||
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
|
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast;
|
||||||
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
|
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
|
||||||
|
|
||||||
CGImageRef iref = CGImageCreate(width,
|
CGImageRef iref = CGImageCreate(width,
|
||||||
@ -1170,6 +1326,23 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
@try {
|
@try {
|
||||||
if (!cameraSession) {
|
if (!cameraSession) {
|
||||||
|
if (@available(macOS 10.14, *)) {
|
||||||
|
switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) {
|
||||||
|
case AVAuthorizationStatusAuthorized:
|
||||||
|
break;
|
||||||
|
case AVAuthorizationStatusNotDetermined: {
|
||||||
|
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
|
||||||
|
[self cameraRequestUpdate];
|
||||||
|
}];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case AVAuthorizationStatusDenied:
|
||||||
|
case AVAuthorizationStatusRestricted:
|
||||||
|
GB_camera_updated(&gb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NSError *error;
|
NSError *error;
|
||||||
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
|
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
|
||||||
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice: device error: &error];
|
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice: device error: &error];
|
||||||
@ -1375,7 +1548,7 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
NSUInteger columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
|
NSUInteger columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
|
||||||
if (tableView == self.paletteTableView) {
|
if (tableView == self.paletteTableView) {
|
||||||
if (columnIndex == 0) {
|
if (columnIndex == 0) {
|
||||||
return [NSString stringWithFormat:@"%s %d", row >=8 ? "Object" : "Background", (int)(row & 7)];
|
return [NSString stringWithFormat:@"%s %u", row >= 8 ? "Object" : "Background", (unsigned)(row & 7)];
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *palette_data = GB_get_direct_access(&gb, row >= 8? GB_DIRECT_ACCESS_OBP : GB_DIRECT_ACCESS_BGP, NULL, NULL);
|
uint8_t *palette_data = GB_get_direct_access(&gb, row >= 8? GB_DIRECT_ACCESS_OBP : GB_DIRECT_ACCESS_BGP, NULL, NULL);
|
||||||
@ -1393,9 +1566,9 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
height:oamHeight
|
height:oamHeight
|
||||||
scale:16.0/oamHeight];
|
scale:16.0/oamHeight];
|
||||||
case 1:
|
case 1:
|
||||||
return @((int)oamInfo[row].x - 8);
|
return @((unsigned)oamInfo[row].x - 8);
|
||||||
case 2:
|
case 2:
|
||||||
return @((int)oamInfo[row].y - 16);
|
return @((unsigned)oamInfo[row].y - 16);
|
||||||
case 3:
|
case 3:
|
||||||
return [NSString stringWithFormat:@"$%02x", oamInfo[row].tile];
|
return [NSString stringWithFormat:@"$%02x", oamInfo[row].tile];
|
||||||
case 4:
|
case 4:
|
||||||
@ -1472,7 +1645,7 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
[self stop];
|
[self stop];
|
||||||
NSSavePanel * savePanel = [NSSavePanel savePanel];
|
NSSavePanel * savePanel = [NSSavePanel savePanel];
|
||||||
[savePanel setAllowedFileTypes:@[@"png"]];
|
[savePanel setAllowedFileTypes:@[@"png"]];
|
||||||
[savePanel beginSheetModalForWindow:self.printerFeedWindow completionHandler:^(NSInteger result){
|
[savePanel beginSheetModalForWindow:self.printerFeedWindow completionHandler:^(NSInteger result) {
|
||||||
if (result == NSFileHandlingPanelOKButton) {
|
if (result == NSFileHandlingPanelOKButton) {
|
||||||
[savePanel orderOut:self];
|
[savePanel orderOut:self];
|
||||||
CGImageRef cgRef = [self.feedImageView.image CGImageForProposedRect:NULL
|
CGImageRef cgRef = [self.feedImageView.image CGImageForProposedRect:NULL
|
||||||
@ -1520,6 +1693,11 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) updateFrameBlendingMode
|
||||||
|
{
|
||||||
|
self.view.frameBlendingMode = (GB_frame_blending_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBFrameBlendingMode"];
|
||||||
|
}
|
||||||
|
|
||||||
- (void) updateRewindLength
|
- (void) updateRewindLength
|
||||||
{
|
{
|
||||||
[self performAtomicBlock:^{
|
[self performAtomicBlock:^{
|
||||||
@ -1563,4 +1741,52 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)splitView:(GBSplitView *)splitView canCollapseSubview:(NSView *)subview;
|
||||||
|
{
|
||||||
|
if ([[splitView arrangedSubviews] lastObject] == subview) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)splitView:(GBSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex
|
||||||
|
{
|
||||||
|
return 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)splitView:(GBSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex
|
||||||
|
{
|
||||||
|
return splitView.frame.size.width - 321;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)splitView:(GBSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)view
|
||||||
|
{
|
||||||
|
if ([[splitView arrangedSubviews] lastObject] == view) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)splitViewDidResizeSubviews:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
GBSplitView *splitview = notification.object;
|
||||||
|
if ([[[splitview arrangedSubviews] firstObject] frame].size.width < 600) {
|
||||||
|
[splitview setPosition:600 ofDividerAtIndex:0];
|
||||||
|
}
|
||||||
|
/* NSSplitView renders its separator without the proper vibrancy, so we made it transparent and move an
|
||||||
|
NSBox-based separator that renders properly so it acts like the split view's separator. */
|
||||||
|
NSRect rect = self.debuggerVerticalLine.frame;
|
||||||
|
rect.origin.x = [[[splitview arrangedSubviews] firstObject] frame].size.width - 1;
|
||||||
|
self.debuggerVerticalLine.frame = rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)showCheats:(id)sender
|
||||||
|
{
|
||||||
|
[self.cheatsWindow makeKeyAndOrderFront:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)toggleCheats:(id)sender
|
||||||
|
{
|
||||||
|
GB_set_cheats_enabled(&gb, !GB_cheats_enabled(&gb));
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="Document">
|
<customObject id="-2" userLabel="File's Owner" customClass="Document">
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="TilemapSetButton" destination="k4c-Vg-MBu" id="Ak1-6d-B1N"/>
|
<outlet property="TilemapSetButton" destination="k4c-Vg-MBu" id="Ak1-6d-B1N"/>
|
||||||
|
<outlet property="cheatWindowController" destination="v7q-gT-jHT" id="UNn-g4-2kP"/>
|
||||||
|
<outlet property="cheatsWindow" destination="4Yb-Np-JrF" id="YCZ-cj-gn5"/>
|
||||||
<outlet property="consoleInput" destination="l22-S8-uji" id="Heu-am-YgB"/>
|
<outlet property="consoleInput" destination="l22-S8-uji" id="Heu-am-YgB"/>
|
||||||
<outlet property="consoleOutput" destination="doS-dM-hnl" id="Gn5-ju-Wb0"/>
|
<outlet property="consoleOutput" destination="doS-dM-hnl" id="Gn5-ju-Wb0"/>
|
||||||
<outlet property="consoleWindow" destination="21F-Ah-yHX" id="eQ4-ug-LsT"/>
|
<outlet property="consoleWindow" destination="21F-Ah-yHX" id="eQ4-ug-LsT"/>
|
||||||
<outlet property="debuggerSideView" destination="JgV-7E-iwp" id="RaA-fw-3i1"/>
|
<outlet property="debuggerSideView" destination="JgV-7E-iwp" id="RaA-fw-3i1"/>
|
||||||
<outlet property="debuggerSideViewInput" destination="w0g-eK-jM4" id="GBf-WK-ryI"/>
|
<outlet property="debuggerSideViewInput" destination="w0g-eK-jM4" id="GBf-WK-ryI"/>
|
||||||
|
<outlet property="debuggerSplitView" destination="pUc-ZN-vl5" id="0sG-0D-cID"/>
|
||||||
|
<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="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"/>
|
||||||
@ -39,22 +43,21 @@
|
|||||||
</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"/>
|
||||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="xOd-HO-29H" userLabel="Window">
|
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="xOd-HO-29H" userLabel="Window">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
|
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<rect key="contentRect" x="0.0" y="0.0" width="160" height="144"/>
|
||||||
<rect key="contentRect" x="133" y="235" width="160" height="144"/>
|
|
||||||
<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="160" height="144"/>
|
<value key="minSize" type="size" width="160" height="144"/>
|
||||||
<view key="contentView" id="gIp-Ho-8D9">
|
<view key="contentView" id="gIp-Ho-8D9">
|
||||||
<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"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<customView id="KTk-4M-J7t" customClass="GBBorderView">
|
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KTk-4M-J7t" customClass="GBBorderView">
|
||||||
<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"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view 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"/>
|
||||||
</view>
|
</view>
|
||||||
@ -67,49 +70,17 @@
|
|||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="293" y="347"/>
|
<point key="canvasLocation" x="293" y="347"/>
|
||||||
</window>
|
</window>
|
||||||
<window title="Debug Console" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="21F-Ah-yHX" customClass="NSPanel">
|
<window title="Debug Console" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="21F-Ah-yHX" customClass="NSPanel">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" utility="YES" HUD="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" utility="YES" HUD="YES"/>
|
||||||
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<rect key="contentRect" x="0.0" y="0.0" width="921" height="400"/>
|
||||||
<rect key="contentRect" x="272" y="172" width="921" height="400"/>
|
|
||||||
<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="921" height="400"/>
|
<value key="minSize" type="size" width="921" height="400"/>
|
||||||
<view key="contentView" id="dCP-E5-7Fi">
|
<view key="contentView" id="dCP-E5-7Fi">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="921" height="400"/>
|
<rect key="frame" x="0.0" y="0.0" width="921" height="400"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<scrollView misplaced="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="oTo-zx-o6N">
|
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="l22-S8-uji">
|
||||||
<rect key="frame" x="0.0" y="25" width="600" height="376"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="EQe-Ad-L7S">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="376"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<textView editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" id="doS-dM-hnl">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="376"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
|
||||||
<color key="backgroundColor" red="0.14901960784313725" green="0.14901960784313725" blue="0.14901960784313725" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<size key="minSize" width="600" height="376"/>
|
|
||||||
<size key="maxSize" width="1160" height="10000000"/>
|
|
||||||
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<allowedInputSourceLocales>
|
|
||||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
|
||||||
</allowedInputSourceLocales>
|
|
||||||
</textView>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" red="0.16470588235294117" green="0.16470588235294117" blue="0.16470588235294117" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
</clipView>
|
|
||||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="3fZ-tl-Zi7">
|
|
||||||
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</scroller>
|
|
||||||
<scroller key="verticalScroller" verticalHuggingPriority="750" horizontal="NO" id="cwi-6E-rbh">
|
|
||||||
<rect key="frame" x="584" y="0.0" width="16" height="376"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</scroller>
|
|
||||||
</scrollView>
|
|
||||||
<textField focusRingType="none" verticalHuggingPriority="750" mirrorLayoutDirectionWhenInternationalizing="never" id="l22-S8-uji">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="921" height="24"/>
|
<rect key="frame" x="0.0" y="0.0" width="921" height="24"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" focusRingType="none" id="p3j-nS-44f" customClass="GBTerminalTextFieldCell">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" focusRingType="none" id="p3j-nS-44f" customClass="GBTerminalTextFieldCell">
|
||||||
@ -124,91 +95,147 @@
|
|||||||
<action selector="consoleInput:" target="-2" id="ylQ-vw-ARS"/>
|
<action selector="consoleInput:" target="-2" id="ylQ-vw-ARS"/>
|
||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<box verticalHuggingPriority="750" boxType="separator" id="960-dL-7ZY">
|
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="960-dL-7ZY">
|
||||||
<rect key="frame" x="0.0" y="23" width="921" height="5"/>
|
<rect key="frame" x="0.0" y="23" width="921" height="5"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
</box>
|
</box>
|
||||||
<scrollView misplaced="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="vts-CC-ZjQ">
|
<box horizontalHuggingPriority="750" boxType="separator" id="7bR-gM-1At">
|
||||||
<rect key="frame" x="601" y="25" width="320" height="328"/>
|
<rect key="frame" x="590" y="25" width="5" height="376"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||||
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="Cs9-3x-ATg">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="328"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<textView editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" spellingCorrection="YES" id="JgV-7E-iwp">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="328"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" red="0.14901960780000001" green="0.14901960780000001" blue="0.14901960780000001" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<size key="minSize" width="320" height="328"/>
|
|
||||||
<size key="maxSize" width="1160" height="10000000"/>
|
|
||||||
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<allowedInputSourceLocales>
|
|
||||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
|
||||||
</allowedInputSourceLocales>
|
|
||||||
</textView>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" red="0.1647058824" green="0.1647058824" blue="0.1647058824" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
</clipView>
|
|
||||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="J2i-lz-QJW">
|
|
||||||
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</scroller>
|
|
||||||
<scroller key="verticalScroller" verticalHuggingPriority="750" horizontal="NO" id="4jm-Gm-D2R">
|
|
||||||
<rect key="frame" x="304" y="0.0" width="16" height="328"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</scroller>
|
|
||||||
</scrollView>
|
|
||||||
<scrollView misplaced="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" scrollerKnobStyle="dark" id="V9U-Ua-F4z">
|
|
||||||
<rect key="frame" x="601" y="354" width="320" height="47"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
|
||||||
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="YHx-TM-zIC">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="47"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<textView drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" allowsCharacterPickerTouchBarItem="NO" allowsUndo="YES" allowsNonContiguousLayout="YES" textCompletion="NO" spellingCorrection="YES" id="w0g-eK-jM4">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="47"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
|
||||||
<size key="minSize" width="320" height="47"/>
|
|
||||||
<size key="maxSize" width="463" height="10000000"/>
|
|
||||||
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<allowedInputSourceLocales>
|
|
||||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
|
||||||
</allowedInputSourceLocales>
|
|
||||||
</textView>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
|
||||||
</clipView>
|
|
||||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="24d-1i-uBk">
|
|
||||||
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</scroller>
|
|
||||||
<scroller key="verticalScroller" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="qgN-F8-fdB">
|
|
||||||
<rect key="frame" x="304" y="0.0" width="16" height="47"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</scroller>
|
|
||||||
</scrollView>
|
|
||||||
<box verticalHuggingPriority="750" misplaced="YES" boxType="separator" id="5qI-qZ-nkh">
|
|
||||||
<rect key="frame" x="603" y="352" width="318" height="5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
|
||||||
</box>
|
|
||||||
<box horizontalHuggingPriority="750" misplaced="YES" boxType="separator" id="Onx-Oe-fBx">
|
|
||||||
<rect key="frame" x="600" y="25" width="5" height="376"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" heightSizable="YES"/>
|
|
||||||
</box>
|
</box>
|
||||||
|
<splitView dividerStyle="thin" vertical="YES" id="pUc-ZN-vl5" customClass="GBSplitView">
|
||||||
|
<rect key="frame" x="0.0" y="25" width="921" height="376"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<customView fixedFrame="YES" id="2rj-7i-kxc">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="591" height="376"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oTo-zx-o6N">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="591" height="376"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" copiesOnScroll="NO" id="EQe-Ad-L7S">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="591" height="376"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<textView ambiguous="YES" editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" id="doS-dM-hnl">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="591" height="376"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<color key="backgroundColor" red="0.14901960784313725" green="0.14901960784313725" blue="0.14901960784313725" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<size key="minSize" width="591" height="376"/>
|
||||||
|
<size key="maxSize" width="1160" height="10000000"/>
|
||||||
|
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" red="0.16470588235294117" green="0.16470588235294117" blue="0.16470588235294117" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="3fZ-tl-Zi7">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="cwi-6E-rbh">
|
||||||
|
<rect key="frame" x="575" y="0.0" width="16" height="376"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
</subviews>
|
||||||
|
</customView>
|
||||||
|
<customView fixedFrame="YES" id="4Z2-33-dYY">
|
||||||
|
<rect key="frame" x="592" y="0.0" width="329" height="376"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" scrollerKnobStyle="dark" translatesAutoresizingMaskIntoConstraints="NO" id="V9U-Ua-F4z">
|
||||||
|
<rect key="frame" x="0.0" y="329" width="329" height="47"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" copiesOnScroll="NO" id="YHx-TM-zIC">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="329" height="47"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<textView ambiguous="YES" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" allowsCharacterPickerTouchBarItem="NO" allowsUndo="YES" allowsNonContiguousLayout="YES" textCompletion="NO" spellingCorrection="YES" id="w0g-eK-jM4">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="329" height="47"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<size key="minSize" width="329" height="47"/>
|
||||||
|
<size key="maxSize" width="463" height="10000000"/>
|
||||||
|
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="24d-1i-uBk">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="qgN-F8-fdB">
|
||||||
|
<rect key="frame" x="313" y="0.0" width="16" height="47"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="5qI-qZ-nkh">
|
||||||
|
<rect key="frame" x="0.0" y="327" width="327" height="5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
</box>
|
||||||
|
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vts-CC-ZjQ">
|
||||||
|
<rect key="frame" x="0.0" y="2" width="329" height="328"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" copiesOnScroll="NO" id="Cs9-3x-ATg">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="329" height="328"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<textView ambiguous="YES" editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" spellingCorrection="YES" id="JgV-7E-iwp">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="329" height="328"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" red="0.14901960780000001" green="0.14901960780000001" blue="0.14901960780000001" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<size key="minSize" width="329" height="328"/>
|
||||||
|
<size key="maxSize" width="1160" height="10000000"/>
|
||||||
|
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" red="0.1647058824" green="0.1647058824" blue="0.1647058824" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="J2i-lz-QJW">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="4jm-Gm-D2R">
|
||||||
|
<rect key="frame" x="313" y="0.0" width="16" height="328"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
</subviews>
|
||||||
|
</customView>
|
||||||
|
</subviews>
|
||||||
|
<holdingPriorities>
|
||||||
|
<real value="250"/>
|
||||||
|
<real value="250"/>
|
||||||
|
</holdingPriorities>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="-2" id="c8R-36-VRD"/>
|
||||||
|
</connections>
|
||||||
|
</splitView>
|
||||||
</subviews>
|
</subviews>
|
||||||
</view>
|
</view>
|
||||||
<point key="canvasLocation" x="347.5" y="-29"/>
|
<point key="canvasLocation" x="347.5" y="-29"/>
|
||||||
</window>
|
</window>
|
||||||
<window title="Memory" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="mRm-dL-mCj" customClass="NSPanel">
|
<window title="Memory" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="mRm-dL-mCj" customClass="NSPanel">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
|
||||||
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<rect key="contentRect" x="0.0" y="0.0" width="528" height="320"/>
|
||||||
<rect key="contentRect" x="272" y="172" width="528" height="320"/>
|
|
||||||
<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"/>
|
||||||
<view key="contentView" id="8hr-8o-3rN">
|
<view key="contentView" id="8hr-8o-3rN">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="528" height="320"/>
|
<rect key="frame" x="0.0" y="0.0" width="528" height="320"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
</view>
|
</view>
|
||||||
<toolbar key="toolbar" implicitIdentifier="D857E961-E523-4295-83F8-0849316E827C" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconAndLabel" sizeMode="regular" id="82v-uB-RPi">
|
<toolbar key="toolbar" implicitIdentifier="D857E961-E523-4295-83F8-0849316E827C" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconAndLabel" sizeMode="regular" id="82v-uB-RPi">
|
||||||
<allowedToolbarItems>
|
<allowedToolbarItems>
|
||||||
@ -248,7 +275,7 @@
|
|||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" id="JCn-Y1-eHS">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" id="JCn-Y1-eHS">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
<connections>
|
<connections>
|
||||||
@ -265,7 +292,7 @@
|
|||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" bezelStyle="round" id="vg5-Nn-abb">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" bezelStyle="round" id="vg5-Nn-abb">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
<connections>
|
<connections>
|
||||||
@ -284,21 +311,20 @@
|
|||||||
<point key="canvasLocation" x="-185" y="61"/>
|
<point key="canvasLocation" x="-185" y="61"/>
|
||||||
</window>
|
</window>
|
||||||
<menuItem title="Cartridge RAM" id="ylM-ah-PNQ"/>
|
<menuItem title="Cartridge RAM" id="ylM-ah-PNQ"/>
|
||||||
<window title="VRAM Viewer" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="mbr-db-iZh" customClass="NSPanel">
|
<window title="VRAM Viewer" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="mbr-db-iZh" customClass="NSPanel">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||||
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<rect key="contentRect" x="0.0" y="0.0" width="512" height="432"/>
|
||||||
<rect key="contentRect" x="283" y="305" width="512" height="432"/>
|
|
||||||
<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"/>
|
||||||
<view key="contentView" id="GYW-dv-Um1">
|
<view key="contentView" id="GYW-dv-Um1">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
|
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<box verticalHuggingPriority="750" boxType="separator" id="ucG-cD-wfs">
|
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="ucG-cD-wfs">
|
||||||
<rect key="frame" x="0.0" y="406" width="512" height="5"/>
|
<rect key="frame" x="0.0" y="406" width="512" height="5"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
</box>
|
</box>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="6vK-IP-PmP">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6vK-IP-PmP">
|
||||||
<rect key="frame" x="-2" y="4" width="516" height="14"/>
|
<rect key="frame" x="-2" y="4" width="516" height="14"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" id="umk-4r-VNg">
|
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" id="umk-4r-VNg">
|
||||||
@ -307,17 +333,17 @@
|
|||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<tabView drawsBackground="NO" type="noTabsNoBorder" initialItem="pXb-od-Wb1" id="AZz-Mh-rPA">
|
<tabView fixedFrame="YES" drawsBackground="NO" type="noTabsNoBorder" initialItem="pXb-od-Wb1" translatesAutoresizingMaskIntoConstraints="NO" id="AZz-Mh-rPA">
|
||||||
<rect key="frame" x="0.0" y="24" width="512" height="408"/>
|
<rect key="frame" x="0.0" y="24" width="512" height="408"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<tabViewItems>
|
<tabViewItems>
|
||||||
<tabViewItem label="Tileset" identifier="1" id="pXb-od-Wb1">
|
<tabViewItem label="Tileset" identifier="1" id="pXb-od-Wb1">
|
||||||
<view key="view" id="lCG-Gt-XMF">
|
<view key="view" ambiguous="YES" id="lCG-Gt-XMF">
|
||||||
<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"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" id="QcQ-ex-36R" customClass="GBImageView">
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QcQ-ex-36R" customClass="GBImageView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="512" height="384"/>
|
<rect key="frame" x="0.0" y="0.0" width="512" height="384"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" id="pXc-O8-jg5"/>
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" id="pXc-O8-jg5"/>
|
||||||
@ -326,7 +352,7 @@
|
|||||||
<outlet property="delegate" destination="-2" id="xoo-Uo-872"/>
|
<outlet property="delegate" destination="-2" id="xoo-Uo-872"/>
|
||||||
</connections>
|
</connections>
|
||||||
</imageView>
|
</imageView>
|
||||||
<popUpButton focusRingType="none" verticalHuggingPriority="750" id="TLv-xS-X5K">
|
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TLv-xS-X5K">
|
||||||
<rect key="frame" x="4" y="388" width="128" height="17"/>
|
<rect key="frame" x="4" y="388" width="128" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="roundRect" title="None" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="G8p-CH-PlV" id="1jI-s4-4YY">
|
<popUpButtonCell key="cell" type="roundRect" title="None" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="G8p-CH-PlV" id="1jI-s4-4YY">
|
||||||
@ -358,7 +384,7 @@
|
|||||||
<action selector="reloadVRAMData:" target="-2" id="Qtf-p4-Rqh"/>
|
<action selector="reloadVRAMData:" target="-2" id="Qtf-p4-Rqh"/>
|
||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
<button verticalHuggingPriority="750" id="fL6-2S-Rgd">
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fL6-2S-Rgd">
|
||||||
<rect key="frame" x="452" y="388" width="56" height="17"/>
|
<rect key="frame" x="452" y="388" width="56" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||||
<buttonCell key="cell" type="roundRect" title="Grid" bezelStyle="roundedRect" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="pDn-9a-Se6">
|
<buttonCell key="cell" type="roundRect" title="Grid" bezelStyle="roundedRect" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="pDn-9a-Se6">
|
||||||
@ -377,7 +403,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" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button verticalHuggingPriority="750" misplaced="YES" id="DhM-Em-hj7">
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DhM-Em-hj7">
|
||||||
<rect key="frame" x="385" y="388" width="63" height="17"/>
|
<rect key="frame" x="385" y="388" width="63" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||||
<buttonCell key="cell" type="roundRect" title="Scrolling" bezelStyle="roundedRect" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="P2E-5t-BN9">
|
<buttonCell key="cell" type="roundRect" title="Scrolling" bezelStyle="roundedRect" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="P2E-5t-BN9">
|
||||||
@ -388,7 +414,7 @@
|
|||||||
<action selector="toggleScrollingDisplay:" target="-2" id="VhQ-9W-sjU"/>
|
<action selector="toggleScrollingDisplay:" target="-2" id="VhQ-9W-sjU"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" id="LlK-tV-bjv" customClass="GBImageView">
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LlK-tV-bjv" customClass="GBImageView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="512" height="384"/>
|
<rect key="frame" x="0.0" y="0.0" width="512" height="384"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" id="RvP-El-ILj"/>
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" id="RvP-El-ILj"/>
|
||||||
@ -397,7 +423,7 @@
|
|||||||
<outlet property="delegate" destination="-2" id="EAG-Zh-oRi"/>
|
<outlet property="delegate" destination="-2" id="EAG-Zh-oRi"/>
|
||||||
</connections>
|
</connections>
|
||||||
</imageView>
|
</imageView>
|
||||||
<popUpButton focusRingType="none" verticalHuggingPriority="750" misplaced="YES" id="loB-0k-Qff">
|
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="loB-0k-Qff">
|
||||||
<rect key="frame" x="4" y="388" width="128" height="17"/>
|
<rect key="frame" x="4" y="388" width="128" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="roundRect" title="Effective Palettes" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="oUH-Sa-Ec1" id="Eij-Cp-URa">
|
<popUpButtonCell key="cell" type="roundRect" title="Effective Palettes" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="oUH-Sa-Ec1" id="Eij-Cp-URa">
|
||||||
@ -430,7 +456,7 @@
|
|||||||
<action selector="reloadVRAMData:" target="-2" id="BmB-JE-W8g"/>
|
<action selector="reloadVRAMData:" target="-2" id="BmB-JE-W8g"/>
|
||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
<popUpButton focusRingType="none" verticalHuggingPriority="750" misplaced="YES" id="YIJ-Qc-SIZ">
|
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YIJ-Qc-SIZ">
|
||||||
<rect key="frame" x="135" y="388" width="96" height="17"/>
|
<rect key="frame" x="135" y="388" width="96" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="roundRect" title="Effective Tilemap" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="XRF-Vj-3gs" id="3W1-Db-wDn">
|
<popUpButtonCell key="cell" type="roundRect" title="Effective Tilemap" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="XRF-Vj-3gs" id="3W1-Db-wDn">
|
||||||
@ -448,7 +474,7 @@
|
|||||||
<action selector="reloadVRAMData:" target="-2" id="xwp-Ju-p00"/>
|
<action selector="reloadVRAMData:" target="-2" id="xwp-Ju-p00"/>
|
||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
<popUpButton focusRingType="none" verticalHuggingPriority="750" misplaced="YES" id="k4c-Vg-MBu">
|
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k4c-Vg-MBu">
|
||||||
<rect key="frame" x="235" y="388" width="96" height="17"/>
|
<rect key="frame" x="235" y="388" width="96" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="roundRect" title="Effective Tileset" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="CRe-dX-rzY" id="h53-sb-Odg">
|
<popUpButtonCell key="cell" type="roundRect" title="Effective Tileset" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="CRe-dX-rzY" id="h53-sb-Odg">
|
||||||
@ -474,10 +500,10 @@
|
|||||||
<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"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="krD-gH-o5I">
|
<scrollView fixedFrame="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="krD-gH-o5I">
|
||||||
<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"/>
|
||||||
<clipView key="contentView" drawsBackground="NO" id="3VT-AA-xVT">
|
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="3VT-AA-xVT">
|
||||||
<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>
|
||||||
@ -597,11 +623,11 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
<nil key="backgroundColor"/>
|
<nil key="backgroundColor"/>
|
||||||
</clipView>
|
</clipView>
|
||||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="n53-qA-NpY">
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="n53-qA-NpY">
|
||||||
<rect key="frame" x="0.0" y="392" width="512" height="16"/>
|
<rect key="frame" x="0.0" y="392" width="512" height="16"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="mqp-NY-g8d">
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="mqp-NY-g8d">
|
||||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
@ -618,12 +644,12 @@
|
|||||||
<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"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<scrollView borderType="none" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" id="iSs-Ow-wwb">
|
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="iSs-Ow-wwb">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
|
<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"/>
|
||||||
<clipView key="contentView" id="bP9-su-zQw">
|
<clipView key="contentView" ambiguous="YES" id="bP9-su-zQw">
|
||||||
<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"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="18" id="gfC-d3-dmq">
|
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="18" id="gfC-d3-dmq">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
|
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
|
||||||
@ -659,7 +685,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
</tableColumn>
|
</tableColumn>
|
||||||
<tableColumn identifier="" width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="syl-os-nSf">
|
<tableColumn width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="syl-os-nSf">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||||
<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"/>
|
||||||
@ -672,7 +698,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
</tableColumn>
|
</tableColumn>
|
||||||
<tableColumn identifier="" width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="Qw3-u2-c1s">
|
<tableColumn width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="Qw3-u2-c1s">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||||
<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"/>
|
||||||
@ -685,7 +711,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
</tableColumn>
|
</tableColumn>
|
||||||
<tableColumn identifier="" width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="gTl-gN-qLn">
|
<tableColumn width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="gTl-gN-qLn">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||||
<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"/>
|
||||||
@ -706,11 +732,11 @@
|
|||||||
</tableView>
|
</tableView>
|
||||||
</subviews>
|
</subviews>
|
||||||
</clipView>
|
</clipView>
|
||||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="OS3-sw-bjv">
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="OS3-sw-bjv">
|
||||||
<rect key="frame" x="-100" y="-100" width="510" height="16"/>
|
<rect key="frame" x="-100" y="-100" width="510" height="16"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="4HA-2m-8TZ">
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="4HA-2m-8TZ">
|
||||||
<rect key="frame" x="-100" y="-100" width="15" height="102"/>
|
<rect key="frame" x="-100" y="-100" width="15" height="102"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
@ -756,16 +782,15 @@
|
|||||||
<contentBorderThickness minY="24"/>
|
<contentBorderThickness minY="24"/>
|
||||||
<point key="canvasLocation" x="182" y="760"/>
|
<point key="canvasLocation" x="182" y="760"/>
|
||||||
</window>
|
</window>
|
||||||
<window title="Printer Feed" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="NdE-0B-WCf" customClass="NSPanel">
|
<window title="Printer Feed" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="NdE-0B-WCf" customClass="NSPanel">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<rect key="contentRect" x="0.0" y="0.0" width="320" height="288"/>
|
||||||
<rect key="contentRect" x="272" y="172" 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"/>
|
||||||
<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"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" 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"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageAlignment="topLeft" id="sff-hk-4nM"/>
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageAlignment="topLeft" id="sff-hk-4nM"/>
|
||||||
@ -775,7 +800,7 @@
|
|||||||
<point key="canvasLocation" x="-159" y="356"/>
|
<point key="canvasLocation" x="-159" y="356"/>
|
||||||
</window>
|
</window>
|
||||||
<button verticalHuggingPriority="750" id="RLc-0I-sYZ">
|
<button verticalHuggingPriority="750" id="RLc-0I-sYZ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="48" height="25"/>
|
<rect key="frame" x="0.5" y="0.0" width="48" height="25"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
<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">
|
<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"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -786,5 +811,264 @@
|
|||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="-507" y="397"/>
|
<point key="canvasLocation" x="-507" y="397"/>
|
||||||
</button>
|
</button>
|
||||||
|
<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"/>
|
||||||
|
<rect key="contentRect" x="0.0" y="0.0" width="692" height="272"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||||
|
<view key="contentView" id="gBP-5p-BTh">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="692" height="272"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<view id="fWr-0i-K1d" customClass="GBOptionalVisualEffectView">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="294" height="272"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="hqi-ob-NW9">
|
||||||
|
<rect key="frame" x="16" y="174" width="154" height="19"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="To value:" id="Ycx-oE-aA4">
|
||||||
|
<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>
|
||||||
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kq8-6F-9GK">
|
||||||
|
<rect key="frame" x="16" y="142" width="154" height="19"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="check" title="Only if old value was: " bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="LkB-WQ-9Qd">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="updateCheat:" target="v7q-gT-jHT" id="kNc-cj-bmF"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<box verticalHuggingPriority="750" boxType="separator" id="D6k-Pe-23u">
|
||||||
|
<rect key="frame" x="10" y="129" width="274" height="5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
</box>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="r5T-ol-Dod">
|
||||||
|
<rect key="frame" x="16" y="107" width="270" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="Import GameShark or GameGenie cheat:" id="0mf-EN-cKc">
|
||||||
|
<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>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="X7K-nJ-alF">
|
||||||
|
<rect key="frame" x="39" y="78" width="233" height="21"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" focusRingType="none" placeholderString="Code" drawsBackground="YES" usesSingleLineMode="YES" id="2bz-dT-7Fi">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</textFieldCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="selectText:" target="KHj-uX-Wbk" id="11z-0U-tMA"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KHj-uX-Wbk">
|
||||||
|
<rect key="frame" x="39" y="47" width="233" height="21"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" focusRingType="none" placeholderString="Description" drawsBackground="YES" usesSingleLineMode="YES" id="50d-va-Cen">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="performClick:" target="C3V-Ep-bMj" id="kIN-jl-A8d"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C6E-oI-hDC">
|
||||||
|
<rect key="frame" x="20" y="233" width="252" height="21"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" focusRingType="none" placeholderString="Description" drawsBackground="YES" usesSingleLineMode="YES" id="2uR-9N-hBb">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="v7q-gT-jHT" id="zyw-h0-hRP"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" id="C3V-Ep-bMj">
|
||||||
|
<rect key="frame" x="202.5" y="12" width="82" height="23"/>
|
||||||
|
<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">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="importCheat:" target="v7q-gT-jHT" id="lkX-N5-wD1"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="qHx-1z-daR">
|
||||||
|
<rect key="frame" x="176" y="204" width="96" height="21"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" focusRingType="none" drawsBackground="YES" usesSingleLineMode="YES" id="edq-46-JeP" customClass="GBCheatTextFieldCell">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</textFieldCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="consoleInput:" target="-2" id="sMf-aM-OvR"/>
|
||||||
|
<outlet property="delegate" destination="v7q-gT-jHT" id="79v-33-R1X"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="N3I-PP-X85">
|
||||||
|
<rect key="frame" x="176" y="174" width="96" height="21"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" focusRingType="none" drawsBackground="YES" usesSingleLineMode="YES" id="CV2-D9-WsB" customClass="GBCheatTextFieldCell">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</textFieldCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="consoleInput:" target="-2" id="SYC-cW-RjC"/>
|
||||||
|
<outlet property="delegate" destination="v7q-gT-jHT" id="P69-nT-oOt"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="S6O-LB-gSj">
|
||||||
|
<rect key="frame" x="176" y="144" width="96" height="21"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" focusRingType="none" drawsBackground="YES" usesSingleLineMode="YES" id="tpM-ys-MEO" customClass="GBCheatTextFieldCell">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</textFieldCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="consoleInput:" target="-2" id="io6-ha-QNb"/>
|
||||||
|
<outlet property="delegate" destination="v7q-gT-jHT" id="6RH-dg-SL7"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="uFo-ly-Veq">
|
||||||
|
<rect key="frame" x="16" y="204" width="152" height="19"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="Change byte at address:" id="xwa-TF-eY1">
|
||||||
|
<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>
|
||||||
|
<scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="6rU-Xg-KHc">
|
||||||
|
<rect key="frame" x="293" y="-1" width="400" height="275"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" heightSizable="YES"/>
|
||||||
|
<clipView key="contentView" id="mzf-yu-RID">
|
||||||
|
<rect key="frame" x="1" y="0.0" width="398" height="274"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" headerView="pvX-uJ-qK5" id="tA3-8T-bxb">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="398" height="249"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<size key="intercellSpacing" width="3" height="2"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<tableColumns>
|
||||||
|
<tableColumn width="32" minWidth="32" maxWidth="1000" id="JNd-HW-LvS">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<buttonCell key="dataCell" type="bevel" bezelStyle="regularSquare" image="NSStopProgressFreestandingTemplate" imagePosition="only" inset="2" id="5xh-hN-jHH">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn width="50" minWidth="40" maxWidth="1000" id="9DZ-oW-Scx">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Enabled">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<buttonCell key="dataCell" type="check" bezelStyle="regularSquare" imagePosition="only" inset="2" id="SdJ-Xw-UAG">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn editable="NO" width="160" minWidth="40" maxWidth="1000" id="4Qa-FQ-QWY">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Description">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Description" id="1hX-Sr-bGz">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn editable="NO" width="144" minWidth="40" maxWidth="1000" id="ACq-gU-K36">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Action">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Action" id="8Sq-h9-eV7">
|
||||||
|
<font key="font" metaFont="fixedUser" size="11"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
</tableColumn>
|
||||||
|
</tableColumns>
|
||||||
|
<connections>
|
||||||
|
<outlet property="dataSource" destination="v7q-gT-jHT" id="3ns-bk-yQI"/>
|
||||||
|
<outlet property="delegate" destination="v7q-gT-jHT" id="fVI-Z9-gTJ"/>
|
||||||
|
</connections>
|
||||||
|
</tableView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="3Hg-LL-VqH">
|
||||||
|
<rect key="frame" x="1" y="258" width="398" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="zET-KH-qF4">
|
||||||
|
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<tableHeaderView key="headerView" id="pvX-uJ-qK5">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="398" height="25"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</tableHeaderView>
|
||||||
|
</scrollView>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<point key="canvasLocation" x="254" y="-463"/>
|
||||||
|
</window>
|
||||||
|
<customObject id="v7q-gT-jHT" customClass="GBCheatWindowController">
|
||||||
|
<connections>
|
||||||
|
<outlet property="addressField" destination="qHx-1z-daR" id="FWo-4u-Qse"/>
|
||||||
|
<outlet property="cheatsTable" destination="tA3-8T-bxb" id="Z2r-AQ-5th"/>
|
||||||
|
<outlet property="descriptionField" destination="C6E-oI-hDC" id="tc0-gI-FBa"/>
|
||||||
|
<outlet property="document" destination="-2" id="5NX-N4-5Rt"/>
|
||||||
|
<outlet property="importCodeField" destination="X7K-nJ-alF" id="ni0-zH-XDU"/>
|
||||||
|
<outlet property="importDescriptionField" destination="KHj-uX-Wbk" id="W0E-ec-BXk"/>
|
||||||
|
<outlet property="oldValueCheckbox" destination="Kq8-6F-9GK" id="A4a-nz-KMN"/>
|
||||||
|
<outlet property="oldValueField" destination="S6O-LB-gSj" id="fAc-OW-ZC9"/>
|
||||||
|
<outlet property="valueField" destination="N3I-PP-X85" id="0zP-9x-4LQ"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
</objects>
|
</objects>
|
||||||
|
<resources>
|
||||||
|
<image name="NSStopProgressFreestandingTemplate" width="14" height="14"/>
|
||||||
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
@ -26,8 +26,7 @@ static OSStatus render(
|
|||||||
-(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer)) block
|
-(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer)) block
|
||||||
andSampleRate:(UInt32) rate
|
andSampleRate:(UInt32) rate
|
||||||
{
|
{
|
||||||
if(!(self = [super init]))
|
if (!(self = [super init])) {
|
||||||
{
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +101,8 @@ static OSStatus render(
|
|||||||
_playing = NO;
|
_playing = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) dealloc {
|
-(void) dealloc
|
||||||
|
{
|
||||||
[self stop];
|
[self stop];
|
||||||
AudioUnitUninitialize(audioUnit);
|
AudioUnitUninitialize(audioUnit);
|
||||||
AudioComponentInstanceDispose(audioUnit);
|
AudioComponentInstanceDispose(audioUnit);
|
||||||
|
@ -19,6 +19,11 @@ typedef enum : NSUInteger {
|
|||||||
|
|
||||||
extern NSString const *GBButtonNames[GBButtonCount];
|
extern NSString const *GBButtonNames[GBButtonCount];
|
||||||
|
|
||||||
|
static inline NSString *n2s(uint64_t number)
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat:@"%llx", number];
|
||||||
|
}
|
||||||
|
|
||||||
static inline NSString *button_to_preference_name(GBButton button, unsigned player)
|
static inline NSString *button_to_preference_name(GBButton button, unsigned player)
|
||||||
{
|
{
|
||||||
if (player) {
|
if (player) {
|
||||||
|
5
Cocoa/GBCheatTextFieldCell.h
Normal file
5
Cocoa/GBCheatTextFieldCell.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface GBCheatTextFieldCell : NSTextFieldCell
|
||||||
|
@property bool usesAddressFormat;
|
||||||
|
@end
|
121
Cocoa/GBCheatTextFieldCell.m
Normal file
121
Cocoa/GBCheatTextFieldCell.m
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#import "GBCheatTextFieldCell.h"
|
||||||
|
|
||||||
|
@interface GBCheatTextView : NSTextView
|
||||||
|
@property bool usesAddressFormat;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation GBCheatTextView
|
||||||
|
|
||||||
|
- (bool)_insertText:(NSString *)string replacementRange:(NSRange)range
|
||||||
|
{
|
||||||
|
if (range.location == NSNotFound) {
|
||||||
|
range = self.selectedRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *new = [self.string stringByReplacingCharactersInRange:range withString:string];
|
||||||
|
if (!self.usesAddressFormat) {
|
||||||
|
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^(\\$[0-9A-Fa-f]{1,2}|[0-9]{1,3})$" options:0 error:NULL];
|
||||||
|
if ([regex numberOfMatchesInString:new options:0 range:NSMakeRange(0, new.length)]) {
|
||||||
|
[super insertText:string replacementRange:range];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ([regex numberOfMatchesInString:[@"$" stringByAppendingString:new] options:0 range:NSMakeRange(0, new.length + 1)]) {
|
||||||
|
[super insertText:string replacementRange:range];
|
||||||
|
[super insertText:@"$" replacementRange:NSMakeRange(0, 0)];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ([new isEqualToString:@"$"] || [string length] == 0) {
|
||||||
|
self.string = @"$00";
|
||||||
|
self.selectedRange = NSMakeRange(1, 2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^(\\$[0-9A-Fa-f]{1,3}:)?\\$[0-9a-fA-F]{1,4}$" options:0 error:NULL];
|
||||||
|
if ([regex numberOfMatchesInString:new options:0 range:NSMakeRange(0, new.length)]) {
|
||||||
|
[super insertText:string replacementRange:range];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ([string length] == 0) {
|
||||||
|
NSUInteger index = [new rangeOfString:@":"].location;
|
||||||
|
if (index != NSNotFound) {
|
||||||
|
if (range.location > index) {
|
||||||
|
self.string = [[new componentsSeparatedByString:@":"] firstObject];
|
||||||
|
self.selectedRange = NSMakeRange(self.string.length, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
self.string = [[new componentsSeparatedByString:@":"] lastObject];
|
||||||
|
self.selectedRange = NSMakeRange(0, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ([[self.string substringWithRange:range] isEqualToString:@":"]) {
|
||||||
|
self.string = [[self.string componentsSeparatedByString:@":"] lastObject];
|
||||||
|
self.selectedRange = NSMakeRange(0, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ([new isEqualToString:@"$"] || [string length] == 0) {
|
||||||
|
self.string = @"$0000";
|
||||||
|
self.selectedRange = NSMakeRange(1, 4);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (([string isEqualToString:@"$"] || [string isEqualToString:@":"]) && range.length == 0 && range.location == 0) {
|
||||||
|
if ([self _insertText:@"$00:" replacementRange:range]) {
|
||||||
|
self.selectedRange = NSMakeRange(1, 2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ([string isEqualToString:@":"] && range.length + range.location == self.string.length) {
|
||||||
|
if ([self _insertText:@":$0" replacementRange:range]) {
|
||||||
|
self.selectedRange = NSMakeRange(self.string.length - 2, 2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ([string isEqualToString:@"$"]) {
|
||||||
|
if ([self _insertText:@"$0" replacementRange:range]) {
|
||||||
|
self.selectedRange = NSMakeRange(range.location + 1, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUndoManager *)undoManager
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
||||||
|
{
|
||||||
|
if (![self _insertText:string replacementRange:replacementRange]) {
|
||||||
|
NSBeep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Private API, don't tell the police! */
|
||||||
|
- (void)_userReplaceRange:(NSRange)range withString:(NSString *)string
|
||||||
|
{
|
||||||
|
[self insertText:string replacementRange:range];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation GBCheatTextFieldCell
|
||||||
|
{
|
||||||
|
bool _drawing, _editing;
|
||||||
|
GBCheatTextView *_fieldEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSTextView *)fieldEditorForView:(NSView *)controlView
|
||||||
|
{
|
||||||
|
if (_fieldEditor) {
|
||||||
|
_fieldEditor.usesAddressFormat = self.usesAddressFormat;
|
||||||
|
return _fieldEditor;
|
||||||
|
}
|
||||||
|
_fieldEditor = [[GBCheatTextView alloc] initWithFrame:controlView.frame];
|
||||||
|
_fieldEditor.fieldEditor = YES;
|
||||||
|
_fieldEditor.usesAddressFormat = self.usesAddressFormat;
|
||||||
|
return _fieldEditor;
|
||||||
|
}
|
||||||
|
@end
|
17
Cocoa/GBCheatWindowController.h
Normal file
17
Cocoa/GBCheatWindowController.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
#import "Document.h"
|
||||||
|
|
||||||
|
@interface GBCheatWindowController : NSObject <NSTableViewDelegate, NSTableViewDataSource, NSTextFieldDelegate>
|
||||||
|
@property (weak) IBOutlet NSTableView *cheatsTable;
|
||||||
|
@property (weak) IBOutlet NSTextField *addressField;
|
||||||
|
@property (weak) IBOutlet NSTextField *valueField;
|
||||||
|
@property (weak) IBOutlet NSTextField *oldValueField;
|
||||||
|
@property (weak) IBOutlet NSButton *oldValueCheckbox;
|
||||||
|
@property (weak) IBOutlet NSTextField *descriptionField;
|
||||||
|
@property (weak) IBOutlet NSTextField *importCodeField;
|
||||||
|
@property (weak) IBOutlet NSTextField *importDescriptionField;
|
||||||
|
@property (weak) IBOutlet Document *document;
|
||||||
|
- (void)cheatsUpdated;
|
||||||
|
@end
|
||||||
|
|
240
Cocoa/GBCheatWindowController.m
Normal file
240
Cocoa/GBCheatWindowController.m
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
#import "GBCheatWindowController.h"
|
||||||
|
#import "GBWarningPopover.h"
|
||||||
|
#import "GBCheatTextFieldCell.h"
|
||||||
|
|
||||||
|
@implementation GBCheatWindowController
|
||||||
|
|
||||||
|
+ (NSString *)addressStringFromCheat:(const GB_cheat_t *)cheat
|
||||||
|
{
|
||||||
|
if (cheat->bank != GB_CHEAT_ANY_BANK) {
|
||||||
|
return [NSString stringWithFormat:@"$%x:$%04x", cheat->bank, cheat->address];
|
||||||
|
}
|
||||||
|
return [NSString stringWithFormat:@"$%04x", cheat->address];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)actionDescriptionForCheat:(const GB_cheat_t *)cheat
|
||||||
|
{
|
||||||
|
if (cheat->use_old_value) {
|
||||||
|
return [NSString stringWithFormat:@"[%@]($%02x) = $%02x", [self addressStringFromCheat:cheat], cheat->old_value, cheat->value];
|
||||||
|
}
|
||||||
|
return [NSString stringWithFormat:@"[%@] = $%02x", [self addressStringFromCheat:cheat], cheat->value];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
|
||||||
|
{
|
||||||
|
GB_gameboy_t *gb = self.document.gameboy;
|
||||||
|
if (!gb) return 0;
|
||||||
|
size_t cheatCount;
|
||||||
|
GB_get_cheats(gb, &cheatCount);
|
||||||
|
return cheatCount + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
||||||
|
{
|
||||||
|
GB_gameboy_t *gb = self.document.gameboy;
|
||||||
|
if (!gb) return nil;
|
||||||
|
size_t cheatCount;
|
||||||
|
GB_get_cheats(gb, &cheatCount);
|
||||||
|
NSUInteger columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
|
||||||
|
if (row >= cheatCount && columnIndex == 0) {
|
||||||
|
return [[NSCell alloc] init];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable id)tableView:(NSTableView *)tableView objectValueForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row
|
||||||
|
{
|
||||||
|
size_t cheatCount;
|
||||||
|
GB_gameboy_t *gb = self.document.gameboy;
|
||||||
|
if (!gb) return nil;
|
||||||
|
const GB_cheat_t *const *cheats = GB_get_cheats(gb, &cheatCount);
|
||||||
|
NSUInteger columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
|
||||||
|
if (row >= cheatCount) {
|
||||||
|
switch (columnIndex) {
|
||||||
|
case 0:
|
||||||
|
return @(YES);
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return @NO;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
return @"Add Cheat...";
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
return @"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (columnIndex) {
|
||||||
|
case 0:
|
||||||
|
return @(NO);
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return @(cheats[row]->enabled);
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
return @(cheats[row]->description);
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
return [GBCheatWindowController actionDescriptionForCheat:cheats[row]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)importCheat:(id)sender
|
||||||
|
{
|
||||||
|
GB_gameboy_t *gb = self.document.gameboy;
|
||||||
|
if (!gb) return;
|
||||||
|
|
||||||
|
[self.document performAtomicBlock:^{
|
||||||
|
if (GB_import_cheat(gb,
|
||||||
|
self.importCodeField.stringValue.UTF8String,
|
||||||
|
self.importDescriptionField.stringValue.UTF8String,
|
||||||
|
true)) {
|
||||||
|
self.importCodeField.stringValue = @"";
|
||||||
|
self.importDescriptionField.stringValue = @"";
|
||||||
|
[self.cheatsTable reloadData];
|
||||||
|
[self tableViewSelectionDidChange:nil];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NSBeep();
|
||||||
|
[GBWarningPopover popoverWithContents:@"This code is not a valid GameShark or GameGenie code" onView:self.importCodeField];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
||||||
|
{
|
||||||
|
GB_gameboy_t *gb = self.document.gameboy;
|
||||||
|
if (!gb) return;
|
||||||
|
size_t cheatCount;
|
||||||
|
const GB_cheat_t *const *cheats = GB_get_cheats(gb, &cheatCount);
|
||||||
|
NSUInteger columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
|
||||||
|
[self.document performAtomicBlock:^{
|
||||||
|
if (columnIndex == 1) {
|
||||||
|
if (row >= cheatCount) {
|
||||||
|
GB_add_cheat(gb, "New Cheat", 0, 0, 0, 0, false, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_update_cheat(gb, cheats[row], cheats[row]->description, cheats[row]->address, cheats[row]->bank, cheats[row]->value, cheats[row]->old_value, cheats[row]->use_old_value, !cheats[row]->enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (row < cheatCount) {
|
||||||
|
GB_remove_cheat(gb, cheats[row]);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
[self.cheatsTable reloadData];
|
||||||
|
[self tableViewSelectionDidChange:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tableViewSelectionDidChange:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
GB_gameboy_t *gb = self.document.gameboy;
|
||||||
|
if (!gb) return;
|
||||||
|
|
||||||
|
size_t cheatCount;
|
||||||
|
const GB_cheat_t *const *cheats = GB_get_cheats(gb, &cheatCount);
|
||||||
|
unsigned row = self.cheatsTable.selectedRow;
|
||||||
|
const GB_cheat_t *cheat = NULL;
|
||||||
|
if (row >= cheatCount) {
|
||||||
|
static const GB_cheat_t template = {
|
||||||
|
.address = 0,
|
||||||
|
.bank = 0,
|
||||||
|
.value = 0,
|
||||||
|
.old_value = 0,
|
||||||
|
.use_old_value = false,
|
||||||
|
.enabled = false,
|
||||||
|
.description = "New Cheat",
|
||||||
|
};
|
||||||
|
cheat = &template;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cheat = cheats[row];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addressField.stringValue = [GBCheatWindowController addressStringFromCheat:cheat];
|
||||||
|
self.valueField.stringValue = [NSString stringWithFormat:@"$%02x", cheat->value];
|
||||||
|
self.oldValueField.stringValue = [NSString stringWithFormat:@"$%02x", cheat->old_value];
|
||||||
|
self.oldValueCheckbox.state = cheat->use_old_value;
|
||||||
|
self.descriptionField.stringValue = @(cheat->description);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)awakeFromNib
|
||||||
|
{
|
||||||
|
[self tableViewSelectionDidChange:nil];
|
||||||
|
((GBCheatTextFieldCell *)self.addressField.cell).usesAddressFormat = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)controlTextDidChange:(NSNotification *)obj
|
||||||
|
{
|
||||||
|
[self updateCheat:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)updateCheat:(id)sender
|
||||||
|
{
|
||||||
|
GB_gameboy_t *gb = self.document.gameboy;
|
||||||
|
if (!gb) return;
|
||||||
|
|
||||||
|
uint16_t address = 0;
|
||||||
|
uint16_t bank = GB_CHEAT_ANY_BANK;
|
||||||
|
if ([self.addressField.stringValue rangeOfString:@":"].location != NSNotFound) {
|
||||||
|
sscanf(self.addressField.stringValue.UTF8String, "$%hx:$%hx", &bank, &address);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sscanf(self.addressField.stringValue.UTF8String, "$%hx", &address);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t value = 0;
|
||||||
|
if ([self.valueField.stringValue characterAtIndex:0] == '$') {
|
||||||
|
sscanf(self.valueField.stringValue.UTF8String, "$%02hhx", &value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sscanf(self.valueField.stringValue.UTF8String, "%hhd", &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t oldValue = 0;
|
||||||
|
if ([self.oldValueField.stringValue characterAtIndex:0] == '$') {
|
||||||
|
sscanf(self.oldValueField.stringValue.UTF8String, "$%02hhx", &oldValue);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sscanf(self.oldValueField.stringValue.UTF8String, "%hhd", &oldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cheatCount;
|
||||||
|
const GB_cheat_t *const *cheats = GB_get_cheats(gb, &cheatCount);
|
||||||
|
unsigned row = self.cheatsTable.selectedRow;
|
||||||
|
|
||||||
|
[self.document performAtomicBlock:^{
|
||||||
|
if (row >= cheatCount) {
|
||||||
|
GB_add_cheat(gb,
|
||||||
|
self.descriptionField.stringValue.UTF8String,
|
||||||
|
address,
|
||||||
|
bank,
|
||||||
|
value,
|
||||||
|
oldValue,
|
||||||
|
self.oldValueCheckbox.state,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_update_cheat(gb,
|
||||||
|
cheats[row],
|
||||||
|
self.descriptionField.stringValue.UTF8String,
|
||||||
|
address,
|
||||||
|
bank,
|
||||||
|
value,
|
||||||
|
oldValue,
|
||||||
|
self.oldValueCheckbox.state,
|
||||||
|
cheats[row]->enabled);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
[self.cheatsTable reloadData];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cheatsUpdated
|
||||||
|
{
|
||||||
|
[self.cheatsTable reloadData];
|
||||||
|
[self tableViewSelectionDidChange:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -1,6 +1,7 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "GBView.h"
|
||||||
|
|
||||||
@interface GBGLShader : NSObject
|
@interface GBGLShader : NSObject
|
||||||
- (instancetype)initWithName:(NSString *) shaderName;
|
- (instancetype)initWithName:(NSString *) shaderName;
|
||||||
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale;
|
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale withBlendingMode: (GB_frame_blending_mode_t)blendingMode;
|
||||||
@end
|
@end
|
||||||
|
@ -21,7 +21,7 @@ void main(void) {\n\
|
|||||||
GLuint resolution_uniform;
|
GLuint resolution_uniform;
|
||||||
GLuint texture_uniform;
|
GLuint texture_uniform;
|
||||||
GLuint previous_texture_uniform;
|
GLuint previous_texture_uniform;
|
||||||
GLuint mix_previous_uniform;
|
GLuint frame_blending_mode_uniform;
|
||||||
|
|
||||||
GLuint position_attribute;
|
GLuint position_attribute;
|
||||||
GLuint texture;
|
GLuint texture;
|
||||||
@ -70,7 +70,7 @@ void main(void) {\n\
|
|||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
previous_texture_uniform = glGetUniformLocation(program, "previous_image");
|
previous_texture_uniform = glGetUniformLocation(program, "previous_image");
|
||||||
|
|
||||||
mix_previous_uniform = glGetUniformLocation(program, "mix_previous");
|
frame_blending_mode_uniform = glGetUniformLocation(program, "frame_blending_mode");
|
||||||
|
|
||||||
// Configure OpenGL
|
// Configure OpenGL
|
||||||
[self configureOpenGL];
|
[self configureOpenGL];
|
||||||
@ -79,7 +79,7 @@ void main(void) {\n\
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale
|
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale withBlendingMode:(GB_frame_blending_mode_t)blendingMode
|
||||||
{
|
{
|
||||||
glUseProgram(program);
|
glUseProgram(program);
|
||||||
glUniform2f(resolution_uniform, dstSize.width * scale, dstSize.height * scale);
|
glUniform2f(resolution_uniform, dstSize.width * scale, dstSize.height * scale);
|
||||||
@ -87,8 +87,8 @@ void main(void) {\n\
|
|||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcSize.width, srcSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcSize.width, srcSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
|
||||||
glUniform1i(texture_uniform, 0);
|
glUniform1i(texture_uniform, 0);
|
||||||
glUniform1i(mix_previous_uniform, previous != NULL);
|
glUniform1i(frame_blending_mode_uniform, blendingMode);
|
||||||
if (previous) {
|
if (blendingMode) {
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
glBindTexture(GL_TEXTURE_2D, previous_texture);
|
glBindTexture(GL_TEXTURE_2D, previous_texture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcSize.width, srcSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcSize.width, srcSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous);
|
||||||
@ -169,7 +169,7 @@ void main(void) {\n\
|
|||||||
+ (GLuint)shaderWithContents:(NSString*)contents type:(GLenum)type
|
+ (GLuint)shaderWithContents:(NSString*)contents type:(GLenum)type
|
||||||
{
|
{
|
||||||
|
|
||||||
const GLchar* source = [contents UTF8String];
|
const GLchar *source = [contents UTF8String];
|
||||||
// Create the shader object
|
// Create the shader object
|
||||||
GLuint shader = glCreateShader(type);
|
GLuint shader = glCreateShader(type);
|
||||||
// Load the shader source
|
// Load the shader source
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
@implementation GBImageCell
|
@implementation GBImageCell
|
||||||
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
|
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
|
||||||
{
|
{
|
||||||
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
|
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
|
||||||
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
|
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
|
||||||
[super drawWithFrame:cellFrame inView:controlView];
|
[super drawWithFrame:cellFrame inView:controlView];
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
}
|
}
|
||||||
- (void)drawRect:(NSRect)dirtyRect
|
- (void)drawRect:(NSRect)dirtyRect
|
||||||
{
|
{
|
||||||
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
|
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
|
||||||
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
|
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
|
||||||
[super drawRect:dirtyRect];
|
[super drawRect:dirtyRect];
|
||||||
CGFloat y_ratio = self.frame.size.height / self.image.size.height;
|
CGFloat y_ratio = self.frame.size.height / self.image.size.height;
|
||||||
@ -93,7 +93,7 @@
|
|||||||
|
|
||||||
- (void)updateTrackingAreas
|
- (void)updateTrackingAreas
|
||||||
{
|
{
|
||||||
if(trackingArea != nil) {
|
if (trackingArea != nil) {
|
||||||
[self removeTrackingArea:trackingArea];
|
[self removeTrackingArea:trackingArea];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
#import <AppKit/AppKit.h>
|
|
||||||
|
|
||||||
@protocol GBJoystickListener <NSObject>
|
|
||||||
|
|
||||||
- (void) joystick:(NSString *)joystick_name button: (unsigned)button changedState: (bool) state;
|
|
||||||
- (void) joystick:(NSString *)joystick_name axis: (unsigned)axis movedTo: (signed) value;
|
|
||||||
- (void) joystick:(NSString *)joystick_name hat: (unsigned)hat changedState: (int8_t) value;
|
|
||||||
|
|
||||||
@end
|
|
@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
@implementation GBOpenGLView
|
@implementation GBOpenGLView
|
||||||
|
|
||||||
- (void)drawRect:(NSRect)dirtyRect {
|
- (void)drawRect:(NSRect)dirtyRect
|
||||||
|
{
|
||||||
if (!self.shader) {
|
if (!self.shader) {
|
||||||
self.shader = [[GBGLShader alloc] initWithName:[[NSUserDefaults standardUserDefaults] objectForKey:@"GBFilter"]];
|
self.shader = [[GBGLShader alloc] initWithName:[[NSUserDefaults standardUserDefaults] objectForKey:@"GBFilter"]];
|
||||||
}
|
}
|
||||||
@ -13,11 +14,14 @@
|
|||||||
double scale = self.window.backingScaleFactor;
|
double scale = self.window.backingScaleFactor;
|
||||||
glViewport(0, 0, self.bounds.size.width * scale, self.bounds.size.height * scale);
|
glViewport(0, 0, self.bounds.size.width * scale, self.bounds.size.height * scale);
|
||||||
|
|
||||||
[self.shader renderBitmap:gbview.currentBuffer
|
if (gbview.gb) {
|
||||||
previous:gbview.shouldBlendFrameWithPrevious? gbview.previousBuffer : NULL
|
[self.shader renderBitmap:gbview.currentBuffer
|
||||||
sized:NSMakeSize(GB_get_screen_width(gbview.gb), GB_get_screen_height(gbview.gb))
|
previous:gbview.frameBlendingMode? gbview.previousBuffer : NULL
|
||||||
inSize:self.bounds.size
|
sized:NSMakeSize(GB_get_screen_width(gbview.gb), GB_get_screen_height(gbview.gb))
|
||||||
scale:scale];
|
inSize:self.bounds.size
|
||||||
|
scale:scale
|
||||||
|
withBlendingMode:gbview.frameBlendingMode];
|
||||||
|
}
|
||||||
glFlush();
|
glFlush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
Cocoa/GBOptionalVisualEffectView.h
Normal file
6
Cocoa/GBOptionalVisualEffectView.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
/* Fake interface so the compiler assumes it conforms to NSVisualEffectView */
|
||||||
|
@interface GBOptionalVisualEffectView : NSVisualEffectView
|
||||||
|
|
||||||
|
@end
|
18
Cocoa/GBOptionalVisualEffectView.m
Normal file
18
Cocoa/GBOptionalVisualEffectView.m
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface GBOptionalVisualEffectView : NSView
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation GBOptionalVisualEffectView
|
||||||
|
|
||||||
|
+ (instancetype)allocWithZone:(struct _NSZone *)zone
|
||||||
|
{
|
||||||
|
Class NSVisualEffectView = NSClassFromString(@"NSVisualEffectView");
|
||||||
|
if (NSVisualEffectView) {
|
||||||
|
return (id)[NSVisualEffectView alloc];
|
||||||
|
}
|
||||||
|
return [super allocWithZone:zone];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -1,17 +1,22 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "GBJoystickListener.h"
|
#import <JoyKit/JoyKit.h>
|
||||||
|
|
||||||
@interface GBPreferencesWindow : NSWindow <NSTableViewDelegate, NSTableViewDataSource, GBJoystickListener>
|
@interface GBPreferencesWindow : NSWindow <NSTableViewDelegate, NSTableViewDataSource, JOYListener>
|
||||||
@property IBOutlet NSTableView *controlsTableView;
|
@property IBOutlet NSTableView *controlsTableView;
|
||||||
@property IBOutlet NSPopUpButton *graphicsFilterPopupButton;
|
@property IBOutlet NSPopUpButton *graphicsFilterPopupButton;
|
||||||
|
@property (strong) IBOutlet NSButton *analogControlsCheckbox;
|
||||||
@property (strong) IBOutlet NSButton *aspectRatioCheckbox;
|
@property (strong) IBOutlet NSButton *aspectRatioCheckbox;
|
||||||
@property (strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
|
||||||
@property (strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
|
||||||
|
@property (strong) IBOutlet NSPopUpButton *frameBlendingModePopupButton;
|
||||||
|
@property (strong) IBOutlet NSPopUpButton *colorPalettePopupButton;
|
||||||
|
@property (strong) IBOutlet NSPopUpButton *displayBorderPopupButton;
|
||||||
@property (strong) IBOutlet NSPopUpButton *rewindPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *rewindPopupButton;
|
||||||
@property (strong) IBOutlet NSButton *configureJoypadButton;
|
@property (strong) IBOutlet NSButton *configureJoypadButton;
|
||||||
@property (strong) IBOutlet NSButton *skipButton;
|
@property (strong) IBOutlet NSButton *skipButton;
|
||||||
@property (strong) IBOutlet NSMenuItem *bootROMsFolderItem;
|
@property (strong) IBOutlet NSMenuItem *bootROMsFolderItem;
|
||||||
@property (strong) IBOutlet NSPopUpButtonCell *bootROMsButton;
|
@property (strong) IBOutlet NSPopUpButtonCell *bootROMsButton;
|
||||||
|
@property (strong) IBOutlet NSPopUpButton *rumbleModePopupButton;
|
||||||
|
|
||||||
@property (weak) IBOutlet NSPopUpButton *dmgPopupButton;
|
@property (weak) IBOutlet NSPopUpButton *dmgPopupButton;
|
||||||
@property (weak) IBOutlet NSPopUpButton *sgbPopupButton;
|
@property (weak) IBOutlet NSPopUpButton *sgbPopupButton;
|
||||||
|
@ -9,17 +9,22 @@
|
|||||||
NSInteger button_being_modified;
|
NSInteger button_being_modified;
|
||||||
signed joystick_configuration_state;
|
signed joystick_configuration_state;
|
||||||
NSString *joystick_being_configured;
|
NSString *joystick_being_configured;
|
||||||
signed last_axis;
|
bool joypad_wait;
|
||||||
|
|
||||||
NSPopUpButton *_graphicsFilterPopupButton;
|
NSPopUpButton *_graphicsFilterPopupButton;
|
||||||
NSPopUpButton *_highpassFilterPopupButton;
|
NSPopUpButton *_highpassFilterPopupButton;
|
||||||
NSPopUpButton *_colorCorrectionPopupButton;
|
NSPopUpButton *_colorCorrectionPopupButton;
|
||||||
|
NSPopUpButton *_frameBlendingModePopupButton;
|
||||||
|
NSPopUpButton *_colorPalettePopupButton;
|
||||||
|
NSPopUpButton *_displayBorderPopupButton;
|
||||||
NSPopUpButton *_rewindPopupButton;
|
NSPopUpButton *_rewindPopupButton;
|
||||||
NSButton *_aspectRatioCheckbox;
|
NSButton *_aspectRatioCheckbox;
|
||||||
|
NSButton *_analogControlsCheckbox;
|
||||||
NSEventModifierFlags previousModifiers;
|
NSEventModifierFlags previousModifiers;
|
||||||
|
|
||||||
NSPopUpButton *_dmgPopupButton, *_sgbPopupButton, *_cgbPopupButton;
|
NSPopUpButton *_dmgPopupButton, *_sgbPopupButton, *_cgbPopupButton;
|
||||||
NSPopUpButton *_preferredJoypadButton;
|
NSPopUpButton *_preferredJoypadButton;
|
||||||
|
NSPopUpButton *_rumbleModePopupButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSArray *)filterList
|
+ (NSArray *)filterList
|
||||||
@ -31,6 +36,7 @@
|
|||||||
@"NearestNeighbor",
|
@"NearestNeighbor",
|
||||||
@"Bilinear",
|
@"Bilinear",
|
||||||
@"SmoothBilinear",
|
@"SmoothBilinear",
|
||||||
|
@"MonoLCD",
|
||||||
@"LCD",
|
@"LCD",
|
||||||
@"CRT",
|
@"CRT",
|
||||||
@"Scale2x",
|
@"Scale2x",
|
||||||
@ -51,7 +57,7 @@
|
|||||||
joystick_configuration_state = -1;
|
joystick_configuration_state = -1;
|
||||||
[self.configureJoypadButton setEnabled:YES];
|
[self.configureJoypadButton setEnabled:YES];
|
||||||
[self.skipButton setEnabled:NO];
|
[self.skipButton setEnabled:NO];
|
||||||
[self.configureJoypadButton setTitle:@"Configure Joypad"];
|
[self.configureJoypadButton setTitle:@"Configure Controller"];
|
||||||
[super close];
|
[super close];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +90,54 @@
|
|||||||
return _colorCorrectionPopupButton;
|
return _colorCorrectionPopupButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setFrameBlendingModePopupButton:(NSPopUpButton *)frameBlendingModePopupButton
|
||||||
|
{
|
||||||
|
_frameBlendingModePopupButton = frameBlendingModePopupButton;
|
||||||
|
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBFrameBlendingMode"];
|
||||||
|
[_frameBlendingModePopupButton selectItemAtIndex:mode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSPopUpButton *)frameBlendingModePopupButton
|
||||||
|
{
|
||||||
|
return _frameBlendingModePopupButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setColorPalettePopupButton:(NSPopUpButton *)colorPalettePopupButton
|
||||||
|
{
|
||||||
|
_colorPalettePopupButton = colorPalettePopupButton;
|
||||||
|
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"];
|
||||||
|
[_colorPalettePopupButton selectItemAtIndex:mode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSPopUpButton *)colorPalettePopupButton
|
||||||
|
{
|
||||||
|
return _colorPalettePopupButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setDisplayBorderPopupButton:(NSPopUpButton *)displayBorderPopupButton
|
||||||
|
{
|
||||||
|
_displayBorderPopupButton = displayBorderPopupButton;
|
||||||
|
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBBorderMode"];
|
||||||
|
[_displayBorderPopupButton selectItemWithTag:mode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSPopUpButton *)displayBorderPopupButton
|
||||||
|
{
|
||||||
|
return _displayBorderPopupButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setRumbleModePopupButton:(NSPopUpButton *)rumbleModePopupButton
|
||||||
|
{
|
||||||
|
_rumbleModePopupButton = rumbleModePopupButton;
|
||||||
|
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRumbleMode"];
|
||||||
|
[_rumbleModePopupButton selectItemWithTag:mode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSPopUpButton *)rumbleModePopupButton
|
||||||
|
{
|
||||||
|
return _rumbleModePopupButton;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setRewindPopupButton:(NSPopUpButton *)rewindPopupButton
|
- (void)setRewindPopupButton:(NSPopUpButton *)rewindPopupButton
|
||||||
{
|
{
|
||||||
_rewindPopupButton = rewindPopupButton;
|
_rewindPopupButton = rewindPopupButton;
|
||||||
@ -184,6 +238,12 @@
|
|||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBHighpassFilterChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBHighpassFilterChanged" object:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (IBAction)changeAnalogControls:(id)sender
|
||||||
|
{
|
||||||
|
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] == NSOnState
|
||||||
|
forKey:@"GBAnalogControls"];
|
||||||
|
}
|
||||||
|
|
||||||
- (IBAction)changeAspectRatio:(id)sender
|
- (IBAction)changeAspectRatio:(id)sender
|
||||||
{
|
{
|
||||||
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] != NSOnState
|
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] != NSOnState
|
||||||
@ -196,7 +256,35 @@
|
|||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
||||||
forKey:@"GBColorCorrection"];
|
forKey:@"GBColorCorrection"];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorCorrectionChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorCorrectionChanged" object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)franeBlendingModeChanged:(id)sender
|
||||||
|
{
|
||||||
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
||||||
|
forKey:@"GBFrameBlendingMode"];
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBFrameBlendingModeChanged" object:nil];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)colorPaletteChanged:(id)sender
|
||||||
|
{
|
||||||
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
||||||
|
forKey:@"GBColorPalette"];
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)displayBorderChanged:(id)sender
|
||||||
|
{
|
||||||
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedItem].tag)
|
||||||
|
forKey:@"GBBorderMode"];
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBBorderModeChanged" object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)rumbleModeChanged:(id)sender
|
||||||
|
{
|
||||||
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedItem].tag)
|
||||||
|
forKey:@"GBRumbleMode"];
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBRumbleModeChanged" object:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)rewindLengthChanged:(id)sender
|
- (IBAction)rewindLengthChanged:(id)sender
|
||||||
@ -212,7 +300,6 @@
|
|||||||
[self.skipButton setEnabled:YES];
|
[self.skipButton setEnabled:YES];
|
||||||
joystick_being_configured = nil;
|
joystick_being_configured = nil;
|
||||||
[self advanceConfigurationStateMachine];
|
[self advanceConfigurationStateMachine];
|
||||||
last_axis = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction) skipButton:(id)sender
|
- (IBAction) skipButton:(id)sender
|
||||||
@ -223,11 +310,11 @@
|
|||||||
- (void) advanceConfigurationStateMachine
|
- (void) advanceConfigurationStateMachine
|
||||||
{
|
{
|
||||||
joystick_configuration_state++;
|
joystick_configuration_state++;
|
||||||
if (joystick_configuration_state < GBButtonCount) {
|
if (joystick_configuration_state == GBUnderclock) {
|
||||||
[self.configureJoypadButton setTitle:[NSString stringWithFormat:@"Press Button for %@", GBButtonNames[joystick_configuration_state]]];
|
[self.configureJoypadButton setTitle:@"Press Button for Slo-Mo"]; // Full name is too long :<
|
||||||
}
|
}
|
||||||
else if (joystick_configuration_state == GBButtonCount) {
|
else if (joystick_configuration_state < GBButtonCount) {
|
||||||
[self.configureJoypadButton setTitle:@"Move the Analog Stick"];
|
[self.configureJoypadButton setTitle:[NSString stringWithFormat:@"Press Button for %@", GBButtonNames[joystick_configuration_state]]];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
joystick_configuration_state = -1;
|
joystick_configuration_state = -1;
|
||||||
@ -237,112 +324,95 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) joystick:(NSString *)joystick_name button: (unsigned)button changedState: (bool) state
|
- (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)button
|
||||||
{
|
{
|
||||||
if (!state) return;
|
/* Debounce */
|
||||||
|
if (joypad_wait) return;
|
||||||
|
joypad_wait = true;
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||||
|
joypad_wait = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!button.isPressed) return;
|
||||||
if (joystick_configuration_state == -1) return;
|
if (joystick_configuration_state == -1) return;
|
||||||
if (joystick_configuration_state == GBButtonCount) return;
|
if (joystick_configuration_state == GBButtonCount) return;
|
||||||
if (!joystick_being_configured) {
|
if (!joystick_being_configured) {
|
||||||
joystick_being_configured = joystick_name;
|
joystick_being_configured = controller.uniqueID;
|
||||||
}
|
}
|
||||||
else if (![joystick_being_configured isEqualToString:joystick_name]) {
|
else if (![joystick_being_configured isEqualToString:controller.uniqueID]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSMutableDictionary *all_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"] mutableCopy];
|
NSMutableDictionary *instance_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"] mutableCopy];
|
||||||
|
|
||||||
if (!all_mappings) {
|
NSMutableDictionary *name_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitNameMapping"] mutableCopy];
|
||||||
all_mappings = [[NSMutableDictionary alloc] init];
|
|
||||||
|
|
||||||
|
if (!instance_mappings) {
|
||||||
|
instance_mappings = [[NSMutableDictionary alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSMutableDictionary *mapping = [[all_mappings objectForKey:joystick_name] mutableCopy];
|
if (!name_mappings) {
|
||||||
|
name_mappings = [[NSMutableDictionary alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
if (!mapping) {
|
NSMutableDictionary *mapping = nil;
|
||||||
|
if (joystick_configuration_state != 0) {
|
||||||
|
mapping = [instance_mappings[controller.uniqueID] mutableCopy];
|
||||||
|
}
|
||||||
|
else {
|
||||||
mapping = [[NSMutableDictionary alloc] init];
|
mapping = [[NSMutableDictionary alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mapping[GBButtonNames[joystick_configuration_state]] = @(button);
|
static const unsigned gb_to_joykit[] = {
|
||||||
|
[GBRight]=JOYButtonUsageDPadRight,
|
||||||
|
[GBLeft]=JOYButtonUsageDPadLeft,
|
||||||
|
[GBUp]=JOYButtonUsageDPadUp,
|
||||||
|
[GBDown]=JOYButtonUsageDPadDown,
|
||||||
|
[GBA]=JOYButtonUsageA,
|
||||||
|
[GBB]=JOYButtonUsageB,
|
||||||
|
[GBSelect]=JOYButtonUsageSelect,
|
||||||
|
[GBStart]=JOYButtonUsageStart,
|
||||||
|
[GBTurbo]=JOYButtonUsageL1,
|
||||||
|
[GBRewind]=JOYButtonUsageL2,
|
||||||
|
[GBUnderclock]=JOYButtonUsageR1,
|
||||||
|
};
|
||||||
|
|
||||||
all_mappings[joystick_name] = mapping;
|
if (joystick_configuration_state == GBUnderclock) {
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:all_mappings forKey:@"GBJoypadMappings"];
|
for (JOYAxis *axis in controller.axes) {
|
||||||
[self refreshJoypadMenu:nil];
|
if (axis.value > 0.5) {
|
||||||
|
mapping[@"AnalogUnderclock"] = @(axis.uniqueID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (joystick_configuration_state == GBTurbo) {
|
||||||
|
for (JOYAxis *axis in controller.axes) {
|
||||||
|
if (axis.value > 0.5) {
|
||||||
|
mapping[@"AnalogTurbo"] = @(axis.uniqueID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping[n2s(button.uniqueID)] = @(gb_to_joykit[joystick_configuration_state]);
|
||||||
|
|
||||||
|
instance_mappings[controller.uniqueID] = mapping;
|
||||||
|
name_mappings[controller.deviceName] = mapping;
|
||||||
|
[[NSUserDefaults standardUserDefaults] setObject:instance_mappings forKey:@"JoyKitInstanceMapping"];
|
||||||
|
[[NSUserDefaults standardUserDefaults] setObject:name_mappings forKey:@"JoyKitNameMapping"];
|
||||||
[self advanceConfigurationStateMachine];
|
[self advanceConfigurationStateMachine];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) joystick:(NSString *)joystick_name axis: (unsigned)axis movedTo: (signed) value
|
- (NSButton *)analogControlsCheckbox
|
||||||
{
|
{
|
||||||
if (abs(value) < 0x4000) return;
|
return _analogControlsCheckbox;
|
||||||
if (joystick_configuration_state != GBButtonCount) return;
|
|
||||||
if (!joystick_being_configured) {
|
|
||||||
joystick_being_configured = joystick_name;
|
|
||||||
}
|
|
||||||
else if (![joystick_being_configured isEqualToString:joystick_name]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last_axis == -1) {
|
|
||||||
last_axis = axis;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (axis == last_axis) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableDictionary *all_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"] mutableCopy];
|
|
||||||
|
|
||||||
if (!all_mappings) {
|
|
||||||
all_mappings = [[NSMutableDictionary alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableDictionary *mapping = [[all_mappings objectForKey:joystick_name] mutableCopy];
|
|
||||||
|
|
||||||
if (!mapping) {
|
|
||||||
mapping = [[NSMutableDictionary alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping[@"XAxis"] = @(MIN(axis, last_axis));
|
|
||||||
mapping[@"YAxis"] = @(MAX(axis, last_axis));
|
|
||||||
|
|
||||||
all_mappings[joystick_name] = mapping;
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:all_mappings forKey:@"GBJoypadMappings"];
|
|
||||||
[self advanceConfigurationStateMachine];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) joystick:(NSString *)joystick_name hat: (unsigned)hat changedState: (int8_t) state
|
- (void)setAnalogControlsCheckbox:(NSButton *)analogControlsCheckbox
|
||||||
{
|
{
|
||||||
/* Hats are always mapped to the D-pad, ignore them on non-Dpad keys and skip the D-pad configuration if used*/
|
_analogControlsCheckbox = analogControlsCheckbox;
|
||||||
if (!state) return;
|
[_analogControlsCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAnalogControls"]];
|
||||||
if (joystick_configuration_state == -1) return;
|
|
||||||
if (joystick_configuration_state > GBDown) return;
|
|
||||||
if (!joystick_being_configured) {
|
|
||||||
joystick_being_configured = joystick_name;
|
|
||||||
}
|
|
||||||
else if (![joystick_being_configured isEqualToString:joystick_name]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableDictionary *all_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"] mutableCopy];
|
|
||||||
|
|
||||||
if (!all_mappings) {
|
|
||||||
all_mappings = [[NSMutableDictionary alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableDictionary *mapping = [[all_mappings objectForKey:joystick_name] mutableCopy];
|
|
||||||
|
|
||||||
if (!mapping) {
|
|
||||||
mapping = [[NSMutableDictionary alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (joystick_configuration_state = 0;; joystick_configuration_state++) {
|
|
||||||
[mapping removeObjectForKey:GBButtonNames[joystick_configuration_state]];
|
|
||||||
if (joystick_configuration_state == GBDown) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
all_mappings[joystick_name] = mapping;
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:all_mappings forKey:@"GBJoypadMappings"];
|
|
||||||
[self refreshJoypadMenu:nil];
|
|
||||||
[self advanceConfigurationStateMachine];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSButton *)aspectRatioCheckbox
|
- (NSButton *)aspectRatioCheckbox
|
||||||
@ -361,10 +431,13 @@
|
|||||||
[super awakeFromNib];
|
[super awakeFromNib];
|
||||||
[self updateBootROMFolderButton];
|
[self updateBootROMFolderButton];
|
||||||
[[NSDistributedNotificationCenter defaultCenter] addObserver:self.controlsTableView selector:@selector(reloadData) name:(NSString*)kTISNotifySelectedKeyboardInputSourceChanged object:nil];
|
[[NSDistributedNotificationCenter defaultCenter] addObserver:self.controlsTableView selector:@selector(reloadData) name:(NSString*)kTISNotifySelectedKeyboardInputSourceChanged object:nil];
|
||||||
|
[JOYController registerListener:self];
|
||||||
|
joystick_configuration_state = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
|
[JOYController unregisterListener:self];
|
||||||
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self.controlsTableView];
|
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self.controlsTableView];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,21 +556,47 @@
|
|||||||
return _preferredJoypadButton;
|
return _preferredJoypadButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)controllerConnected:(JOYController *)controller
|
||||||
|
{
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||||
|
[self refreshJoypadMenu:nil];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)controllerDisconnected:(JOYController *)controller
|
||||||
|
{
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||||
|
[self refreshJoypadMenu:nil];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
- (IBAction)refreshJoypadMenu:(id)sender
|
- (IBAction)refreshJoypadMenu:(id)sender
|
||||||
{
|
{
|
||||||
NSArray *joypads = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"] allKeys];
|
bool preferred_is_connected = false;
|
||||||
for (NSString *joypad in joypads) {
|
NSString *player_string = n2s(self.playerListButton.selectedTag);
|
||||||
if ([self.preferredJoypadButton indexOfItemWithTitle:joypad] == -1) {
|
NSString *selected_controller = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"][player_string];
|
||||||
[self.preferredJoypadButton addItemWithTitle:joypad];
|
|
||||||
|
[self.preferredJoypadButton removeAllItems];
|
||||||
|
[self.preferredJoypadButton addItemWithTitle:@"None"];
|
||||||
|
for (JOYController *controller in [JOYController allControllers]) {
|
||||||
|
[self.preferredJoypadButton addItemWithTitle:[NSString stringWithFormat:@"%@ (%@)", controller.deviceName, controller.uniqueID]];
|
||||||
|
|
||||||
|
self.preferredJoypadButton.lastItem.identifier = controller.uniqueID;
|
||||||
|
|
||||||
|
if ([controller.uniqueID isEqualToString:selected_controller]) {
|
||||||
|
preferred_is_connected = true;
|
||||||
|
[self.preferredJoypadButton selectItem:self.preferredJoypadButton.lastItem];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *player_string = [NSString stringWithFormat: @"%ld", (long)self.playerListButton.selectedTag];
|
if (!preferred_is_connected && selected_controller) {
|
||||||
NSString *selected_joypad = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBDefaultJoypads"][player_string];
|
[self.preferredJoypadButton addItemWithTitle:[NSString stringWithFormat:@"Unavailable Controller (%@)", selected_controller]];
|
||||||
if (selected_joypad && [self.preferredJoypadButton indexOfItemWithTitle:selected_joypad] != -1) {
|
self.preferredJoypadButton.lastItem.identifier = selected_controller;
|
||||||
[self.preferredJoypadButton selectItemWithTitle:selected_joypad];
|
[self.preferredJoypadButton selectItem:self.preferredJoypadButton.lastItem];
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
|
||||||
|
if (!selected_controller) {
|
||||||
[self.preferredJoypadButton selectItemWithTitle:@"None"];
|
[self.preferredJoypadButton selectItemWithTitle:@"None"];
|
||||||
}
|
}
|
||||||
[self.controlsTableView reloadData];
|
[self.controlsTableView reloadData];
|
||||||
@ -505,18 +604,18 @@
|
|||||||
|
|
||||||
- (IBAction)changeDefaultJoypad:(id)sender
|
- (IBAction)changeDefaultJoypad:(id)sender
|
||||||
{
|
{
|
||||||
NSMutableDictionary *default_joypads = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBDefaultJoypads"] mutableCopy];
|
NSMutableDictionary *default_joypads = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"] mutableCopy];
|
||||||
if (!default_joypads) {
|
if (!default_joypads) {
|
||||||
default_joypads = [[NSMutableDictionary alloc] init];
|
default_joypads = [[NSMutableDictionary alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *player_string = [NSString stringWithFormat: @"%ld", self.playerListButton.selectedTag];
|
NSString *player_string = n2s(self.playerListButton.selectedTag);
|
||||||
if ([[sender titleOfSelectedItem] isEqualToString:@"None"]) {
|
if ([[sender titleOfSelectedItem] isEqualToString:@"None"]) {
|
||||||
[default_joypads removeObjectForKey:player_string];
|
[default_joypads removeObjectForKey:player_string];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
default_joypads[player_string] = [sender titleOfSelectedItem];
|
default_joypads[player_string] = [[sender selectedItem] identifier];
|
||||||
}
|
}
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:default_joypads forKey:@"GBDefaultJoypads"];
|
[[NSUserDefaults standardUserDefaults] setObject:default_joypads forKey:@"JoyKitDefaultControllers"];
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
7
Cocoa/GBSplitView.h
Normal file
7
Cocoa/GBSplitView.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface GBSplitView : NSSplitView
|
||||||
|
|
||||||
|
-(void) setDividerColor:(NSColor *)color;
|
||||||
|
- (NSArray<NSView *> *)arrangedSubviews;
|
||||||
|
@end
|
33
Cocoa/GBSplitView.m
Normal file
33
Cocoa/GBSplitView.m
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#import "GBSplitView.h"
|
||||||
|
|
||||||
|
@implementation GBSplitView
|
||||||
|
{
|
||||||
|
NSColor *_dividerColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setDividerColor:(NSColor *)color
|
||||||
|
{
|
||||||
|
_dividerColor = color;
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSColor *)dividerColor
|
||||||
|
{
|
||||||
|
if (_dividerColor) {
|
||||||
|
return _dividerColor;
|
||||||
|
}
|
||||||
|
return [super dividerColor];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mavericks comaptibility */
|
||||||
|
- (NSArray<NSView *> *)arrangedSubviews
|
||||||
|
{
|
||||||
|
if (@available(macOS 10.11, *)) {
|
||||||
|
return [super arrangedSubviews];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return [self subviews];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -173,7 +173,8 @@
|
|||||||
[super setSelectedRanges:ranges affinity:affinity stillSelecting:stillSelectingFlag];
|
[super setSelectedRanges:ranges affinity:affinity stillSelecting:stillSelectingFlag];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)resignFirstResponder {
|
- (BOOL)resignFirstResponder
|
||||||
|
{
|
||||||
reverse_search_mode = false;
|
reverse_search_mode = false;
|
||||||
return [super resignFirstResponder];
|
return [super resignFirstResponder];
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#include <Core/gb.h>
|
#include <Core/gb.h>
|
||||||
#import "GBJoystickListener.h"
|
#import <JoyKit/JoyKit.h>
|
||||||
|
|
||||||
@interface GBView<GBJoystickListener> : NSView
|
typedef enum {
|
||||||
|
GB_FRAME_BLENDING_MODE_DISABLED,
|
||||||
|
GB_FRAME_BLENDING_MODE_SIMPLE,
|
||||||
|
GB_FRAME_BLENDING_MODE_ACCURATE,
|
||||||
|
GB_FRAME_BLENDING_MODE_ACCURATE_EVEN = GB_FRAME_BLENDING_MODE_ACCURATE,
|
||||||
|
GB_FRAME_BLENDING_MODE_ACCURATE_ODD,
|
||||||
|
} GB_frame_blending_mode_t;
|
||||||
|
|
||||||
|
@interface GBView : NSView<JOYListener>
|
||||||
- (void) flip;
|
- (void) flip;
|
||||||
- (uint32_t *) pixels;
|
- (uint32_t *) pixels;
|
||||||
@property GB_gameboy_t *gb;
|
@property GB_gameboy_t *gb;
|
||||||
@property (nonatomic) BOOL shouldBlendFrameWithPrevious;
|
@property (nonatomic) GB_frame_blending_mode_t frameBlendingMode;
|
||||||
@property (getter=isMouseHidingEnabled) BOOL mouseHidingEnabled;
|
@property (getter=isMouseHidingEnabled) BOOL mouseHidingEnabled;
|
||||||
@property bool isRewinding;
|
@property bool isRewinding;
|
||||||
@property NSView *internalView;
|
@property NSView *internalView;
|
||||||
@ -14,4 +22,5 @@
|
|||||||
- (uint32_t *)currentBuffer;
|
- (uint32_t *)currentBuffer;
|
||||||
- (uint32_t *)previousBuffer;
|
- (uint32_t *)previousBuffer;
|
||||||
- (void)screenSizeChanged;
|
- (void)screenSizeChanged;
|
||||||
|
- (void)setRumble: (double)amp;
|
||||||
@end
|
@end
|
||||||
|
251
Cocoa/GBView.m
251
Cocoa/GBView.m
@ -1,4 +1,4 @@
|
|||||||
#import <Carbon/Carbon.h>
|
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||||
#import "GBView.h"
|
#import "GBView.h"
|
||||||
#import "GBViewGL.h"
|
#import "GBViewGL.h"
|
||||||
#import "GBViewMetal.h"
|
#import "GBViewMetal.h"
|
||||||
@ -18,7 +18,11 @@
|
|||||||
bool axisActive[2];
|
bool axisActive[2];
|
||||||
bool underclockKeyDown;
|
bool underclockKeyDown;
|
||||||
double clockMultiplier;
|
double clockMultiplier;
|
||||||
|
double analogClockMultiplier;
|
||||||
|
bool analogClockMultiplierValid;
|
||||||
NSEventModifierFlags previousModifiers;
|
NSEventModifierFlags previousModifiers;
|
||||||
|
JOYController *lastController;
|
||||||
|
GB_frame_blending_mode_t _frameBlendingMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)alloc
|
+ (instancetype)alloc
|
||||||
@ -43,8 +47,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void) _init
|
- (void) _init
|
||||||
{
|
{
|
||||||
_shouldBlendFrameWithPrevious = 1;
|
|
||||||
[[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
|
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect
|
||||||
@ -55,6 +58,7 @@
|
|||||||
[self createInternalView];
|
[self createInternalView];
|
||||||
[self addSubview:self.internalView];
|
[self addSubview:self.internalView];
|
||||||
self.internalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
self.internalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||||
|
[JOYController registerListener:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)screenSizeChanged
|
- (void)screenSizeChanged
|
||||||
@ -65,9 +69,9 @@
|
|||||||
|
|
||||||
size_t buffer_size = sizeof(image_buffers[0][0]) * GB_get_screen_width(_gb) * GB_get_screen_height(_gb);
|
size_t buffer_size = sizeof(image_buffers[0][0]) * GB_get_screen_width(_gb) * GB_get_screen_height(_gb);
|
||||||
|
|
||||||
image_buffers[0] = malloc(buffer_size);
|
image_buffers[0] = calloc(1, buffer_size);
|
||||||
image_buffers[1] = malloc(buffer_size);
|
image_buffers[1] = calloc(1, buffer_size);
|
||||||
image_buffers[2] = malloc(buffer_size);
|
image_buffers[2] = calloc(1, buffer_size);
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self setFrame:self.superview.frame];
|
[self setFrame:self.superview.frame];
|
||||||
@ -79,15 +83,26 @@
|
|||||||
[self setFrame:self.superview.frame];
|
[self setFrame:self.superview.frame];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setShouldBlendFrameWithPrevious:(BOOL)shouldBlendFrameWithPrevious
|
- (void) setFrameBlendingMode:(GB_frame_blending_mode_t)frameBlendingMode
|
||||||
{
|
{
|
||||||
_shouldBlendFrameWithPrevious = shouldBlendFrameWithPrevious;
|
_frameBlendingMode = frameBlendingMode;
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (GB_frame_blending_mode_t)frameBlendingMode
|
||||||
|
{
|
||||||
|
if (_frameBlendingMode == GB_FRAME_BLENDING_MODE_ACCURATE) {
|
||||||
|
if (!_gb || GB_is_sgb(_gb)) {
|
||||||
|
return GB_FRAME_BLENDING_MODE_SIMPLE;
|
||||||
|
}
|
||||||
|
return GB_is_odd_frame(_gb)? GB_FRAME_BLENDING_MODE_ACCURATE_ODD : GB_FRAME_BLENDING_MODE_ACCURATE_EVEN;
|
||||||
|
}
|
||||||
|
return _frameBlendingMode;
|
||||||
|
}
|
||||||
- (unsigned char) numberOfBuffers
|
- (unsigned char) numberOfBuffers
|
||||||
{
|
{
|
||||||
return _shouldBlendFrameWithPrevious? 3 : 2;
|
return _frameBlendingMode? 3 : 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
@ -100,11 +115,12 @@
|
|||||||
[NSCursor unhide];
|
[NSCursor unhide];
|
||||||
}
|
}
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
[self setRumble:0];
|
||||||
|
[JOYController unregisterListener:self];
|
||||||
}
|
}
|
||||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||||
{
|
{
|
||||||
if (!(self = [super initWithCoder:coder]))
|
if (!(self = [super initWithCoder:coder])) {
|
||||||
{
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
[self _init];
|
[self _init];
|
||||||
@ -113,8 +129,7 @@
|
|||||||
|
|
||||||
- (instancetype)initWithFrame:(NSRect)frameRect
|
- (instancetype)initWithFrame:(NSRect)frameRect
|
||||||
{
|
{
|
||||||
if (!(self = [super initWithFrame:frameRect]))
|
if (!(self = [super initWithFrame:frameRect])) {
|
||||||
{
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
[self _init];
|
[self _init];
|
||||||
@ -147,13 +162,21 @@
|
|||||||
|
|
||||||
- (void) flip
|
- (void) flip
|
||||||
{
|
{
|
||||||
if (underclockKeyDown && clockMultiplier > 0.5) {
|
if (analogClockMultiplierValid && [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAnalogControls"]) {
|
||||||
clockMultiplier -= 1.0/16;
|
GB_set_clock_multiplier(_gb, analogClockMultiplier);
|
||||||
GB_set_clock_multiplier(_gb, clockMultiplier);
|
if (analogClockMultiplier == 1.0) {
|
||||||
|
analogClockMultiplierValid = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!underclockKeyDown && clockMultiplier < 1.0) {
|
else {
|
||||||
clockMultiplier += 1.0/16;
|
if (underclockKeyDown && clockMultiplier > 0.5) {
|
||||||
GB_set_clock_multiplier(_gb, clockMultiplier);
|
clockMultiplier -= 1.0/16;
|
||||||
|
GB_set_clock_multiplier(_gb, clockMultiplier);
|
||||||
|
}
|
||||||
|
if (!underclockKeyDown && clockMultiplier < 1.0) {
|
||||||
|
clockMultiplier += 1.0/16;
|
||||||
|
GB_set_clock_multiplier(_gb, clockMultiplier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
current_buffer = (current_buffer + 1) % self.numberOfBuffers;
|
current_buffer = (current_buffer + 1) % self.numberOfBuffers;
|
||||||
}
|
}
|
||||||
@ -180,6 +203,7 @@
|
|||||||
switch (button) {
|
switch (button) {
|
||||||
case GBTurbo:
|
case GBTurbo:
|
||||||
GB_set_turbo_mode(_gb, true, self.isRewinding);
|
GB_set_turbo_mode(_gb, true, self.isRewinding);
|
||||||
|
analogClockMultiplierValid = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GBRewind:
|
case GBRewind:
|
||||||
@ -189,6 +213,7 @@
|
|||||||
|
|
||||||
case GBUnderclock:
|
case GBUnderclock:
|
||||||
underclockKeyDown = true;
|
underclockKeyDown = true;
|
||||||
|
analogClockMultiplierValid = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -221,6 +246,7 @@
|
|||||||
switch (button) {
|
switch (button) {
|
||||||
case GBTurbo:
|
case GBTurbo:
|
||||||
GB_set_turbo_mode(_gb, false, false);
|
GB_set_turbo_mode(_gb, false, false);
|
||||||
|
analogClockMultiplierValid = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GBRewind:
|
case GBRewind:
|
||||||
@ -229,6 +255,7 @@
|
|||||||
|
|
||||||
case GBUnderclock:
|
case GBUnderclock:
|
||||||
underclockKeyDown = false;
|
underclockKeyDown = false;
|
||||||
|
analogClockMultiplierValid = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -243,123 +270,99 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) joystick:(NSString *)joystick_name button: (unsigned)button changedState: (bool) state
|
- (void)setRumble:(double)amp
|
||||||
{
|
{
|
||||||
unsigned player_count = GB_get_player_count(_gb);
|
[lastController setRumbleAmplitude:amp];
|
||||||
|
|
||||||
UpdateSystemActivity(UsrActivity);
|
|
||||||
for (unsigned player = 0; player < player_count; player++) {
|
|
||||||
NSString *preferred_joypad = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBDefaultJoypads"]
|
|
||||||
objectForKey:[NSString stringWithFormat:@"%u", player]];
|
|
||||||
if (player_count != 1 && // Single player, accpet inputs from all joypads
|
|
||||||
!(player == 0 && !preferred_joypad) && // Multiplayer, but player 1 has no joypad configured, so it takes inputs from all joypads
|
|
||||||
![preferred_joypad isEqualToString:joystick_name]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"][joystick_name];
|
|
||||||
|
|
||||||
for (GBButton i = 0; i < GBButtonCount; i++) {
|
|
||||||
NSNumber *mapped_button = [mapping objectForKey:GBButtonNames[i]];
|
|
||||||
if (mapped_button && [mapped_button integerValue] == button) {
|
|
||||||
switch (i) {
|
|
||||||
case GBTurbo:
|
|
||||||
GB_set_turbo_mode(_gb, state, state && self.isRewinding);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GBRewind:
|
|
||||||
self.isRewinding = state;
|
|
||||||
if (state) {
|
|
||||||
GB_set_turbo_mode(_gb, false, false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GBUnderclock:
|
|
||||||
underclockKeyDown = state;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
GB_set_key_state_for_player(_gb, (GB_key_t)i, player, state);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) joystick:(NSString *)joystick_name axis: (unsigned)axis movedTo: (signed) value
|
- (void)controller:(JOYController *)controller movedAxis:(JOYAxis *)axis
|
||||||
{
|
{
|
||||||
unsigned player_count = GB_get_player_count(_gb);
|
if (![self.window isMainWindow]) return;
|
||||||
|
|
||||||
UpdateSystemActivity(UsrActivity);
|
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
|
||||||
for (unsigned player = 0; player < player_count; player++) {
|
if (!mapping) {
|
||||||
NSString *preferred_joypad = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBDefaultJoypads"]
|
mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitNameMapping"][controller.deviceName];
|
||||||
objectForKey:[NSString stringWithFormat:@"%u", player]];
|
|
||||||
if (player_count != 1 && // Single player, accpet inputs from all joypads
|
|
||||||
!(player == 0 && !preferred_joypad) && // Multiplayer, but player 1 has no joypad configured, so it takes inputs from all joypads
|
|
||||||
![preferred_joypad isEqualToString:joystick_name]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"][joystick_name];
|
|
||||||
NSNumber *x_axis = [mapping objectForKey:@"XAxis"];
|
|
||||||
NSNumber *y_axis = [mapping objectForKey:@"YAxis"];
|
|
||||||
|
|
||||||
if (axis == [x_axis integerValue]) {
|
|
||||||
if (value > JOYSTICK_HIGH) {
|
|
||||||
axisActive[0] = true;
|
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_RIGHT, player, true);
|
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_LEFT, player, false);
|
|
||||||
}
|
|
||||||
else if (value < -JOYSTICK_HIGH) {
|
|
||||||
axisActive[0] = true;
|
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_RIGHT, player, false);
|
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_LEFT, player, true);
|
|
||||||
}
|
|
||||||
else if (axisActive[0] && value < JOYSTICK_LOW && value > -JOYSTICK_LOW) {
|
|
||||||
axisActive[0] = false;
|
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_RIGHT, player, false);
|
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_LEFT, player, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (axis == [y_axis integerValue]) {
|
|
||||||
if (value > JOYSTICK_HIGH) {
|
|
||||||
axisActive[1] = true;
|
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_DOWN, player, true);
|
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_UP, player, false);
|
|
||||||
}
|
|
||||||
else if (value < -JOYSTICK_HIGH) {
|
|
||||||
axisActive[1] = true;
|
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_DOWN, player, false);
|
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_UP, player, true);
|
|
||||||
}
|
|
||||||
else if (axisActive[1] && value < JOYSTICK_LOW && value > -JOYSTICK_LOW) {
|
|
||||||
axisActive[1] = false;
|
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_DOWN, player, false);
|
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_UP, player, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
- (void) joystick:(NSString *)joystick_name hat: (unsigned)hat changedState: (int8_t) state
|
|
||||||
{
|
|
||||||
unsigned player_count = GB_get_player_count(_gb);
|
|
||||||
|
|
||||||
UpdateSystemActivity(UsrActivity);
|
if ((axis.usage == JOYAxisUsageR1 && !mapping) ||
|
||||||
|
axis.uniqueID == [mapping[@"AnalogUnderclock"] unsignedLongValue]){
|
||||||
|
analogClockMultiplier = MIN(MAX(1 - axis.value + 0.2, 1.0 / 3), 1.0);
|
||||||
|
analogClockMultiplierValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((axis.usage == JOYAxisUsageL1 && !mapping) ||
|
||||||
|
axis.uniqueID == [mapping[@"AnalogTurbo"] unsignedLongValue]){
|
||||||
|
analogClockMultiplier = MIN(MAX(axis.value * 3 + 0.8, 1.0), 3.0);
|
||||||
|
analogClockMultiplierValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)button
|
||||||
|
{
|
||||||
|
if (![self.window isMainWindow]) return;
|
||||||
|
if (controller != lastController) {
|
||||||
|
[self setRumble:0];
|
||||||
|
lastController = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned player_count = GB_get_player_count(_gb);
|
||||||
|
|
||||||
|
IOPMAssertionID assertionID;
|
||||||
|
IOPMAssertionDeclareUserActivity(CFSTR(""), kIOPMUserActiveLocal, &assertionID);
|
||||||
|
|
||||||
for (unsigned player = 0; player < player_count; player++) {
|
for (unsigned player = 0; player < player_count; player++) {
|
||||||
NSString *preferred_joypad = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBDefaultJoypads"]
|
NSString *preferred_joypad = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"]
|
||||||
objectForKey:[NSString stringWithFormat:@"%u", player]];
|
objectForKey:n2s(player)];
|
||||||
if (player_count != 1 && // Single player, accpet inputs from all joypads
|
if (player_count != 1 && // Single player, accpet inputs from all joypads
|
||||||
!(player == 0 && !preferred_joypad) && // Multiplayer, but player 1 has no joypad configured, so it takes inputs from all joypads
|
!(player == 0 && !preferred_joypad) && // Multiplayer, but player 1 has no joypad configured, so it takes inputs from all joypads
|
||||||
![preferred_joypad isEqualToString:joystick_name]) {
|
![preferred_joypad isEqualToString:controller.uniqueID]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
assert(state + 1 < 9);
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
/* - N NE E SE S SW W NW */
|
[controller setPlayerLEDs:1 << player];
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_UP, player, (bool []){0, 1, 1, 0, 0, 0, 0, 0, 1}[state + 1]);
|
});
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_RIGHT, player, (bool []){0, 0, 1, 1, 1, 0, 0, 0, 0}[state + 1]);
|
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_DOWN, player, (bool []){0, 0, 0, 0, 1, 1, 1, 0, 0}[state + 1]);
|
if (!mapping) {
|
||||||
GB_set_key_state_for_player(_gb, GB_KEY_LEFT, player, (bool []){0, 0, 0, 0, 0, 0, 1, 1, 1}[state + 1]);
|
mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitNameMapping"][controller.deviceName];
|
||||||
|
}
|
||||||
|
|
||||||
|
JOYButtonUsage usage = ((JOYButtonUsage)[mapping[n2s(button.uniqueID)] unsignedIntValue]) ?: button.usage;
|
||||||
|
if (!mapping && usage >= JOYButtonUsageGeneric0) {
|
||||||
|
usage = (const JOYButtonUsage[]){JOYButtonUsageY, JOYButtonUsageA, JOYButtonUsageB, JOYButtonUsageX}[(usage - JOYButtonUsageGeneric0) & 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (usage) {
|
||||||
|
|
||||||
|
case JOYButtonUsageNone: 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(_gb, GB_KEY_B, player, button.isPressed); break;
|
||||||
|
case JOYButtonUsageC: break;
|
||||||
|
case JOYButtonUsageStart:
|
||||||
|
case JOYButtonUsageX: GB_set_key_state_for_player(_gb, GB_KEY_START, player, button.isPressed); break;
|
||||||
|
case JOYButtonUsageSelect:
|
||||||
|
case JOYButtonUsageY: GB_set_key_state_for_player(_gb, GB_KEY_SELECT, player, button.isPressed); break;
|
||||||
|
case JOYButtonUsageR2:
|
||||||
|
case JOYButtonUsageL2:
|
||||||
|
case JOYButtonUsageZ: {
|
||||||
|
self.isRewinding = button.isPressed;
|
||||||
|
if (button.isPressed) {
|
||||||
|
GB_set_turbo_mode(_gb, false, false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case JOYButtonUsageL1: GB_set_turbo_mode(_gb, button.isPressed, button.isPressed && self.isRewinding); break;
|
||||||
|
|
||||||
|
case JOYButtonUsageR1: underclockKeyDown = 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(_gb, GB_KEY_RIGHT, player, 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(_gb, GB_KEY_DOWN, player, button.isPressed); break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#import "GBViewMetal.h"
|
#import "GBViewMetal.h"
|
||||||
|
#pragma clang diagnostic ignored "-Wpartial-availability"
|
||||||
|
|
||||||
|
|
||||||
static const vector_float2 rect[] =
|
static const vector_float2 rect[] =
|
||||||
{
|
{
|
||||||
@ -15,7 +17,7 @@ static const vector_float2 rect[] =
|
|||||||
id<MTLBuffer> vertices;
|
id<MTLBuffer> vertices;
|
||||||
id<MTLRenderPipelineState> pipeline_state;
|
id<MTLRenderPipelineState> pipeline_state;
|
||||||
id<MTLCommandQueue> command_queue;
|
id<MTLCommandQueue> command_queue;
|
||||||
id<MTLBuffer> mix_previous_buffer;
|
id<MTLBuffer> frame_blending_mode_buffer;
|
||||||
id<MTLBuffer> output_resolution_buffer;
|
id<MTLBuffer> output_resolution_buffer;
|
||||||
vector_float2 output_resolution;
|
vector_float2 output_resolution;
|
||||||
}
|
}
|
||||||
@ -50,15 +52,16 @@ static const vector_float2 rect[] =
|
|||||||
view.delegate = self;
|
view.delegate = self;
|
||||||
self.internalView = view;
|
self.internalView = view;
|
||||||
view.paused = YES;
|
view.paused = YES;
|
||||||
|
view.enableSetNeedsDisplay = YES;
|
||||||
|
|
||||||
vertices = [device newBufferWithBytes:rect
|
vertices = [device newBufferWithBytes:rect
|
||||||
length:sizeof(rect)
|
length:sizeof(rect)
|
||||||
options:MTLResourceStorageModeShared];
|
options:MTLResourceStorageModeShared];
|
||||||
|
|
||||||
static const bool default_mix_value = false;
|
static const GB_frame_blending_mode_t default_blending_mode = GB_FRAME_BLENDING_MODE_DISABLED;
|
||||||
mix_previous_buffer = [device newBufferWithBytes:&default_mix_value
|
frame_blending_mode_buffer = [device newBufferWithBytes:&default_blending_mode
|
||||||
length:sizeof(default_mix_value)
|
length:sizeof(default_blending_mode)
|
||||||
options:MTLResourceStorageModeShared];
|
options:MTLResourceStorageModeShared];
|
||||||
|
|
||||||
output_resolution_buffer = [device newBufferWithBytes:&output_resolution
|
output_resolution_buffer = [device newBufferWithBytes:&output_resolution
|
||||||
length:sizeof(output_resolution)
|
length:sizeof(output_resolution)
|
||||||
@ -131,6 +134,7 @@ static const vector_float2 rect[] =
|
|||||||
- (void)drawInMTKView:(nonnull MTKView *)view
|
- (void)drawInMTKView:(nonnull MTKView *)view
|
||||||
{
|
{
|
||||||
if (!(view.window.occlusionState & NSWindowOcclusionStateVisible)) return;
|
if (!(view.window.occlusionState & NSWindowOcclusionStateVisible)) return;
|
||||||
|
if (!self.gb) return;
|
||||||
if (texture.width != GB_get_screen_width(self.gb) ||
|
if (texture.width != GB_get_screen_width(self.gb) ||
|
||||||
texture.height != GB_get_screen_height(self.gb)) {
|
texture.height != GB_get_screen_height(self.gb)) {
|
||||||
[self allocateTextures];
|
[self allocateTextures];
|
||||||
@ -145,7 +149,7 @@ static const vector_float2 rect[] =
|
|||||||
mipmapLevel:0
|
mipmapLevel:0
|
||||||
withBytes:[self currentBuffer]
|
withBytes:[self currentBuffer]
|
||||||
bytesPerRow:texture.width * 4];
|
bytesPerRow:texture.width * 4];
|
||||||
if ([self shouldBlendFrameWithPrevious]) {
|
if ([self frameBlendingMode]) {
|
||||||
[previous_texture replaceRegion:region
|
[previous_texture replaceRegion:region
|
||||||
mipmapLevel:0
|
mipmapLevel:0
|
||||||
withBytes:[self previousBuffer]
|
withBytes:[self previousBuffer]
|
||||||
@ -155,9 +159,8 @@ static const vector_float2 rect[] =
|
|||||||
MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor;
|
MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor;
|
||||||
id<MTLCommandBuffer> command_buffer = [command_queue commandBuffer];
|
id<MTLCommandBuffer> command_buffer = [command_queue commandBuffer];
|
||||||
|
|
||||||
if(render_pass_descriptor != nil)
|
if (render_pass_descriptor != nil) {
|
||||||
{
|
*(GB_frame_blending_mode_t *)[frame_blending_mode_buffer contents] = [self frameBlendingMode];
|
||||||
*(bool *)[mix_previous_buffer contents] = [self shouldBlendFrameWithPrevious];
|
|
||||||
*(vector_float2 *)[output_resolution_buffer contents] = output_resolution;
|
*(vector_float2 *)[output_resolution_buffer contents] = output_resolution;
|
||||||
|
|
||||||
id<MTLRenderCommandEncoder> render_encoder =
|
id<MTLRenderCommandEncoder> render_encoder =
|
||||||
@ -174,7 +177,7 @@ static const vector_float2 rect[] =
|
|||||||
offset:0
|
offset:0
|
||||||
atIndex:0];
|
atIndex:0];
|
||||||
|
|
||||||
[render_encoder setFragmentBuffer:mix_previous_buffer
|
[render_encoder setFragmentBuffer:frame_blending_mode_buffer
|
||||||
offset:0
|
offset:0
|
||||||
atIndex:0];
|
atIndex:0];
|
||||||
|
|
||||||
@ -205,7 +208,7 @@ static const vector_float2 rect[] =
|
|||||||
{
|
{
|
||||||
[super flip];
|
[super flip];
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[(MTKView *)self.internalView draw];
|
[(MTKView *)self.internalView setNeedsDisplay:YES];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>SameBoy</string>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<key>CFBundleDocumentTypes</key>
|
<key>CFBundleDocumentTypes</key>
|
||||||
@ -14,7 +16,7 @@
|
|||||||
<key>CFBundleTypeIconFile</key>
|
<key>CFBundleTypeIconFile</key>
|
||||||
<string>Cartridge</string>
|
<string>Cartridge</string>
|
||||||
<key>CFBundleTypeName</key>
|
<key>CFBundleTypeName</key>
|
||||||
<string>GameBoy Game</string>
|
<string>Game Boy Game</string>
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>Viewer</string>
|
<string>Viewer</string>
|
||||||
<key>LSItemContentTypes</key>
|
<key>LSItemContentTypes</key>
|
||||||
@ -34,7 +36,7 @@
|
|||||||
<key>CFBundleTypeIconFile</key>
|
<key>CFBundleTypeIconFile</key>
|
||||||
<string>ColorCartridge</string>
|
<string>ColorCartridge</string>
|
||||||
<key>CFBundleTypeName</key>
|
<key>CFBundleTypeName</key>
|
||||||
<string>GameBoy Color Game</string>
|
<string>Game Boy Color Game</string>
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>Viewer</string>
|
<string>Viewer</string>
|
||||||
<key>LSItemContentTypes</key>
|
<key>LSItemContentTypes</key>
|
||||||
@ -46,6 +48,26 @@
|
|||||||
<key>NSDocumentClass</key>
|
<key>NSDocumentClass</key>
|
||||||
<string>Document</string>
|
<string>Document</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeExtensions</key>
|
||||||
|
<array>
|
||||||
|
<string>gbc</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeIconFile</key>
|
||||||
|
<string>ColorCartridge</string>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>Game Boy ISX File</string>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>com.github.liji32.sameboy.isx</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>
|
||||||
@ -70,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-2019 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>
|
||||||
@ -83,7 +105,7 @@
|
|||||||
<string>public.data</string>
|
<string>public.data</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UTTypeDescription</key>
|
<key>UTTypeDescription</key>
|
||||||
<string>GameBoy Game</string>
|
<string>Game Boy Game</string>
|
||||||
<key>UTTypeIconFile</key>
|
<key>UTTypeIconFile</key>
|
||||||
<string>Cartridge</string>
|
<string>Cartridge</string>
|
||||||
<key>UTTypeIdentifier</key>
|
<key>UTTypeIdentifier</key>
|
||||||
@ -102,7 +124,7 @@
|
|||||||
<string>public.data</string>
|
<string>public.data</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UTTypeDescription</key>
|
<key>UTTypeDescription</key>
|
||||||
<string>GameBoy Color Game</string>
|
<string>Game Boy Color Game</string>
|
||||||
<key>UTTypeIconFile</key>
|
<key>UTTypeIconFile</key>
|
||||||
<string>ColorCartridge</string>
|
<string>ColorCartridge</string>
|
||||||
<key>UTTypeIdentifier</key>
|
<key>UTTypeIdentifier</key>
|
||||||
@ -115,7 +137,28 @@
|
|||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>Game Boy ISX File</string>
|
||||||
|
<key>UTTypeIconFile</key>
|
||||||
|
<string>ColorCartridge</string>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>com.github.liji32.sameboy.isx</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>isx</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>SameBoy needs to access your camera to emulate the Game Boy Camera</string>
|
||||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
<h1>SameBoy</h1>
|
<h1>SameBoy</h1>
|
||||||
<h2>MIT License</h2>
|
<h2>MIT License</h2>
|
||||||
<h3>Copyright © 2015-2019 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
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||||
@ -342,11 +342,22 @@
|
|||||||
<action selector="mute:" target="-1" id="YE5-mi-Yzd"/>
|
<action selector="mute:" target="-1" id="YE5-mi-Yzd"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="YIZ-pz-N4V"/>
|
</items>
|
||||||
<menuItem title="Blend Frames" id="AWj-r8-L6U">
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Cheats" id="8ld-Ad-nvc">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Cheats" id="Ucc-Hm-TVA">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Enable Cheats" keyEquivalent="C" id="vtx-LG-v6y">
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleCheats:" target="-1" id="gsw-UY-fhu"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Show Cheats" id="LZV-QK-YXi">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="toggleBlend:" target="-1" id="TjO-ce-UxL"/>
|
<action selector="showCheats:" target="-1" id="tfr-qM-q8X"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
</items>
|
</items>
|
||||||
@ -371,9 +382,9 @@
|
|||||||
</items>
|
</items>
|
||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Developer" id="IwX-DJ-dBk">
|
<menuItem title="Develop" id="IwX-DJ-dBk">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<menu key="submenu" title="Developer" id="UVb-cc-at0">
|
<menu key="submenu" title="Develop" id="UVb-cc-at0">
|
||||||
<items>
|
<items>
|
||||||
<menuItem title="Developer Mode" id="Qx6-Tt-zZR">
|
<menuItem title="Developer Mode" id="Qx6-Tt-zZR">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
@ -454,6 +465,7 @@
|
|||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
</items>
|
</items>
|
||||||
|
<point key="canvasLocation" x="140" y="260"/>
|
||||||
</menu>
|
</menu>
|
||||||
</objects>
|
</objects>
|
||||||
</document>
|
</document>
|
||||||
|
7
Cocoa/NSObject+MavericksCompat.m
Normal file
7
Cocoa/NSObject+MavericksCompat.m
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
@implementation NSObject (MavericksCompat)
|
||||||
|
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||||
|
{
|
||||||
|
return [self init];
|
||||||
|
}
|
||||||
|
@end
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
@ -17,7 +17,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"/>
|
||||||
<window title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="GBPreferencesWindow">
|
<window title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="GBPreferencesWindow">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||||
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
@ -58,53 +58,59 @@
|
|||||||
</defaultToolbarItems>
|
</defaultToolbarItems>
|
||||||
</toolbar>
|
</toolbar>
|
||||||
<connections>
|
<connections>
|
||||||
|
<outlet property="analogControlsCheckbox" destination="RuW-Db-dzW" id="FRE-hI-mnU"/>
|
||||||
<outlet property="aspectRatioCheckbox" destination="Vfj-tg-7OP" id="Yw0-xS-DBr"/>
|
<outlet property="aspectRatioCheckbox" destination="Vfj-tg-7OP" id="Yw0-xS-DBr"/>
|
||||||
<outlet property="bootROMsButton" destination="T3Y-Ln-Onl" id="tdL-Yv-E2K"/>
|
<outlet property="bootROMsButton" destination="T3Y-Ln-Onl" id="tdL-Yv-E2K"/>
|
||||||
<outlet property="bootROMsFolderItem" destination="Dzv-Gc-zoL" id="yhV-ZI-avD"/>
|
<outlet property="bootROMsFolderItem" destination="Dzv-Gc-zoL" id="yhV-ZI-avD"/>
|
||||||
<outlet property="cgbPopupButton" destination="dlD-sk-SHO" id="4tg-SR-e17"/>
|
<outlet property="cgbPopupButton" destination="dlD-sk-SHO" id="4tg-SR-e17"/>
|
||||||
<outlet property="colorCorrectionPopupButton" destination="VEz-N4-uP6" id="EO2-Vt-JFJ"/>
|
<outlet property="colorCorrectionPopupButton" destination="VEz-N4-uP6" id="EO2-Vt-JFJ"/>
|
||||||
|
<outlet property="colorPalettePopupButton" destination="Iwr-eI-SD1" id="Xzc-RZ-JtV"/>
|
||||||
<outlet property="configureJoypadButton" destination="Qa7-Z7-yfO" id="RaX-P3-oCX"/>
|
<outlet property="configureJoypadButton" destination="Qa7-Z7-yfO" id="RaX-P3-oCX"/>
|
||||||
<outlet property="controlsTableView" destination="UDd-IJ-fxX" id="a1D-Md-yXv"/>
|
<outlet property="controlsTableView" destination="UDd-IJ-fxX" id="a1D-Md-yXv"/>
|
||||||
<outlet property="delegate" destination="-2" id="ASc-vN-Zbq"/>
|
<outlet property="delegate" destination="-2" id="ASc-vN-Zbq"/>
|
||||||
|
<outlet property="displayBorderPopupButton" destination="R9D-FV-bpd" id="VfO-b4-gqH"/>
|
||||||
<outlet property="dmgPopupButton" destination="LFw-Uk-cPR" id="KDw-i2-k4u"/>
|
<outlet property="dmgPopupButton" destination="LFw-Uk-cPR" id="KDw-i2-k4u"/>
|
||||||
|
<outlet property="frameBlendingModePopupButton" destination="lxk-db-Sxv" id="wzt-uo-TE6"/>
|
||||||
<outlet property="graphicsFilterPopupButton" destination="6pP-kK-EEC" id="LS7-HY-kHC"/>
|
<outlet property="graphicsFilterPopupButton" destination="6pP-kK-EEC" id="LS7-HY-kHC"/>
|
||||||
<outlet property="highpassFilterPopupButton" destination="T69-6N-dhT" id="0p6-4m-hb1"/>
|
<outlet property="highpassFilterPopupButton" destination="T69-6N-dhT" id="0p6-4m-hb1"/>
|
||||||
<outlet property="playerListButton" destination="gWx-7h-0xq" id="zo6-82-JId"/>
|
<outlet property="playerListButton" destination="gWx-7h-0xq" id="zo6-82-JId"/>
|
||||||
<outlet property="preferredJoypadButton" destination="0Az-0R-oNw" id="7JM-tw-BhK"/>
|
<outlet property="preferredJoypadButton" destination="0Az-0R-oNw" id="7JM-tw-BhK"/>
|
||||||
<outlet property="rewindPopupButton" destination="7fg-Ww-JjR" id="Ka2-TP-B1x"/>
|
<outlet property="rewindPopupButton" destination="7fg-Ww-JjR" id="Ka2-TP-B1x"/>
|
||||||
|
<outlet property="rumbleModePopupButton" destination="Ogs-xG-b4b" id="vuw-VN-MTp"/>
|
||||||
<outlet property="sgbPopupButton" destination="dza-T7-RkX" id="B0o-Nb-pIH"/>
|
<outlet property="sgbPopupButton" destination="dza-T7-RkX" id="B0o-Nb-pIH"/>
|
||||||
<outlet property="skipButton" destination="d2I-jU-sLb" id="udX-8K-0sK"/>
|
<outlet property="skipButton" destination="d2I-jU-sLb" id="udX-8K-0sK"/>
|
||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="183" y="354"/>
|
<point key="canvasLocation" x="183" y="354"/>
|
||||||
</window>
|
</window>
|
||||||
<customView id="sRK-wO-K6R">
|
<customView id="sRK-wO-K6R">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="292" height="166"/>
|
<rect key="frame" x="0.0" y="0.0" width="292" height="323"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T91-rh-rRp">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T91-rh-rRp">
|
||||||
<rect key="frame" x="18" y="129" width="256" height="17"/>
|
<rect key="frame" x="18" y="286" width="256" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Graphics Filter:" id="pXg-WY-8Q5">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Graphics filter:" id="pXg-WY-8Q5">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6pP-kK-EEC">
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6pP-kK-EEC">
|
||||||
<rect key="frame" x="30" y="97" width="234" height="26"/>
|
<rect key="frame" x="30" y="254" width="234" height="26"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="push" title="Nearest Neighbor (Pixelated)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="neN-eo-LA7" id="I1w-05-lGl">
|
<popUpButtonCell key="cell" type="push" title="Nearest neighbor (Pixelated)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="neN-eo-LA7" id="I1w-05-lGl">
|
||||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="menu"/>
|
<font key="font" metaFont="menu"/>
|
||||||
<menu key="menu" id="xDC-0T-Qg9">
|
<menu key="menu" id="xDC-0T-Qg9">
|
||||||
<items>
|
<items>
|
||||||
<menuItem title="Nearest Neighbor (Pixelated)" state="on" id="neN-eo-LA7">
|
<menuItem title="Nearest neighbor (Pixelated)" state="on" id="neN-eo-LA7">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Bilinear (Blurry)" id="iDe-si-atu"/>
|
<menuItem title="Bilinear (Blurry)" id="iDe-si-atu"/>
|
||||||
<menuItem title="Smooth Bilinear (Less blurry)" id="1jN-pO-1iD"/>
|
<menuItem title="Smooth bilinear (Less blurry)" id="1jN-pO-1iD"/>
|
||||||
<menuItem title="LCD Display" id="b8u-LZ-UQf"/>
|
<menuItem title="Monochrome LCD display" id="b8u-LZ-UQf"/>
|
||||||
<menuItem title="CRT Display" id="FT9-FT-RZu"/>
|
<menuItem title="LCD display" id="pj3-Jt-bNU"/>
|
||||||
|
<menuItem title="CRT display" id="FT9-FT-RZu"/>
|
||||||
<menuItem title="Scale2x" id="C1I-L2-Up1"/>
|
<menuItem title="Scale2x" id="C1I-L2-Up1"/>
|
||||||
<menuItem title="Scale4x" id="uWA-Zp-JY9"/>
|
<menuItem title="Scale4x" id="uWA-Zp-JY9"/>
|
||||||
<menuItem title="Anti-aliased Scale2x" id="iP6-DJ-CVH"/>
|
<menuItem title="Anti-aliased Scale2x" id="iP6-DJ-CVH"/>
|
||||||
@ -127,16 +133,16 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wc3-2K-6CD">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wc3-2K-6CD">
|
||||||
<rect key="frame" x="18" y="75" width="256" height="17"/>
|
<rect key="frame" x="18" y="232" width="256" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color Correction:" id="5Si-hz-EK3">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color correction:" id="5Si-hz-EK3">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VEz-N4-uP6">
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VEz-N4-uP6">
|
||||||
<rect key="frame" x="30" y="43" width="234" height="26"/>
|
<rect key="frame" x="30" y="200" width="234" height="26"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="D2J-wV-1vu" id="fNJ-Fi-yOm">
|
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="D2J-wV-1vu" id="fNJ-Fi-yOm">
|
||||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -146,9 +152,10 @@
|
|||||||
<menuItem title="Disabled" state="on" id="D2J-wV-1vu">
|
<menuItem title="Disabled" state="on" id="D2J-wV-1vu">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Correct Color Curves" id="B3Q-x1-VRl"/>
|
<menuItem title="Correct color curves" id="B3Q-x1-VRl"/>
|
||||||
<menuItem title="Emulate Hardware" id="XBL-hS-7ke"/>
|
<menuItem title="Emulate hardware" id="XBL-hS-7ke"/>
|
||||||
<menuItem title="Preserve Brightness" id="tlx-WB-Bkw"/>
|
<menuItem title="Preserve brightness" id="tlx-WB-Bkw"/>
|
||||||
|
<menuItem title="Reduce contrast" id="wuO-Xv-0mQ"/>
|
||||||
</items>
|
</items>
|
||||||
</menu>
|
</menu>
|
||||||
</popUpButtonCell>
|
</popUpButtonCell>
|
||||||
@ -156,10 +163,98 @@
|
|||||||
<action selector="colorCorrectionChanged:" target="QvC-M9-y7g" id="Oq4-B5-nO6"/>
|
<action selector="colorCorrectionChanged:" target="QvC-M9-y7g" id="Oq4-B5-nO6"/>
|
||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MLC-Rx-FgO">
|
||||||
|
<rect key="frame" x="20" y="178" width="252" height="17"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Frame blending" id="UCa-EO-tzh">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lxk-db-Sxv">
|
||||||
|
<rect key="frame" x="32" y="149" width="229" height="22"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="iHP-Yz-fiH" id="aQ6-HN-7Aj">
|
||||||
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="menu"/>
|
||||||
|
<menu key="menu" id="Q9L-qo-kF4">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Disabled" state="on" id="iHP-Yz-fiH">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Simple" id="Hxy-jw-x6E"/>
|
||||||
|
<menuItem title="Accurate" id="Aaq-uy-Csa"/>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</popUpButtonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="franeBlendingModeChanged:" target="QvC-M9-y7g" id="kE1-pm-MIp"/>
|
||||||
|
</connections>
|
||||||
|
</popUpButton>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8fG-zm-hpr">
|
||||||
|
<rect key="frame" x="18" y="122" width="252" height="17"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color palette for monochrome models:" id="LAN-8Y-T7H">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Iwr-eI-SD1">
|
||||||
|
<rect key="frame" x="30" y="93" width="234" height="22"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<popUpButtonCell key="cell" type="push" title="Greyscale" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Ajr-5r-iIk" id="rEU-jh-m3j">
|
||||||
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="menu"/>
|
||||||
|
<menu key="menu" id="dHJ-3R-Ora">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Greyscale" state="on" id="Ajr-5r-iIk">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Lime (Game Boy)" id="snU-ht-fQq"/>
|
||||||
|
<menuItem title="Olive (Pocket)" id="MQi-yt-nsT"/>
|
||||||
|
<menuItem title="Teal (Light)" id="xlg-6i-Fhl"/>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</popUpButtonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="colorPaletteChanged:" target="QvC-M9-y7g" id="ui3-rg-PTs"/>
|
||||||
|
</connections>
|
||||||
|
</popUpButton>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3Kz-cf-5X6">
|
||||||
|
<rect key="frame" x="18" y="71" width="248" height="17"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Display border:" id="HZd-qi-yyk">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="R9D-FV-bpd">
|
||||||
|
<rect key="frame" x="30" y="39" width="234" height="25"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<popUpButtonCell key="cell" type="push" title="Never" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="heL-AV-0az" id="DY9-2D-h1L">
|
||||||
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="menu"/>
|
||||||
|
<menu key="menu" id="Rgf-mF-K9q">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Never" state="on" tag="1" id="heL-AV-0az">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Super Game Boy only" id="bBJ-Vn-5rk"/>
|
||||||
|
<menuItem title="Always" tag="2" id="JUs-gW-qcM"/>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</popUpButtonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="displayBorderChanged:" target="QvC-M9-y7g" id="GoA-BU-v3h"/>
|
||||||
|
</connections>
|
||||||
|
</popUpButton>
|
||||||
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vfj-tg-7OP">
|
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vfj-tg-7OP">
|
||||||
<rect key="frame" x="18" y="18" width="256" height="18"/>
|
<rect key="frame" x="18" y="18" width="256" height="18"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<buttonCell key="cell" type="check" title="Keep Aspect Ratio" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="lsj-rC-Eo6">
|
<buttonCell key="cell" type="check" title="Keep aspect ratio" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="lsj-rC-Eo6">
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
@ -168,7 +263,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
</subviews>
|
</subviews>
|
||||||
<point key="canvasLocation" x="-176" y="589"/>
|
<point key="canvasLocation" x="-176" y="667.5"/>
|
||||||
</customView>
|
</customView>
|
||||||
<customView id="ymk-46-SX7">
|
<customView id="ymk-46-SX7">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="292" height="320"/>
|
<rect key="frame" x="0.0" y="0.0" width="292" height="320"/>
|
||||||
@ -201,7 +296,7 @@
|
|||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="w9w-yX-KxB">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="w9w-yX-KxB">
|
||||||
<rect key="frame" x="18" y="225" width="256" height="17"/>
|
<rect key="frame" x="18" y="225" width="256" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Rewinding Duration:" id="JaO-5h-ugl">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Rewinding duration:" id="JaO-5h-ugl">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -219,12 +314,12 @@
|
|||||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wC4-aJ-mhQ">
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wC4-aJ-mhQ">
|
||||||
<rect key="frame" x="30" y="251" width="234" height="26"/>
|
<rect key="frame" x="30" y="251" width="234" height="26"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="push" title="Use Built-in Boot ROMs" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Tnm-SR-ZEm" id="T3Y-Ln-Onl">
|
<popUpButtonCell key="cell" type="push" title="Use built-in boot ROMs" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Tnm-SR-ZEm" id="T3Y-Ln-Onl">
|
||||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="menu"/>
|
<font key="font" metaFont="menu"/>
|
||||||
<menu key="menu" id="edo-AN-gRh">
|
<menu key="menu" id="edo-AN-gRh">
|
||||||
<items>
|
<items>
|
||||||
<menuItem title="Use Built-in Boot ROMs" state="on" id="Tnm-SR-ZEm">
|
<menuItem title="Use built-in boot ROMs" state="on" id="Tnm-SR-ZEm">
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="useBuiltinBootROMs:" target="QvC-M9-y7g" id="Kmo-wz-ZtB"/>
|
<action selector="useBuiltinBootROMs:" target="QvC-M9-y7g" id="Kmo-wz-ZtB"/>
|
||||||
</connections>
|
</connections>
|
||||||
@ -243,7 +338,7 @@
|
|||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wg8-hJ-df9">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wg8-hJ-df9">
|
||||||
<rect key="frame" x="18" y="157" width="256" height="17"/>
|
<rect key="frame" x="18" y="157" width="256" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Game Boy Revision:" id="GIA-ep-SBi">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Game Boy revision:" id="GIA-ep-SBi">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -271,7 +366,7 @@
|
|||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MAq-1X-Gpo">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MAq-1X-Gpo">
|
||||||
<rect key="frame" x="18" y="49" width="256" height="17"/>
|
<rect key="frame" x="18" y="49" width="256" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Game Boy Color Revision:" id="edD-t7-vwk">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Game Boy Color revision:" id="edD-t7-vwk">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -301,7 +396,7 @@
|
|||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tAa-0A-0fP">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tAa-0A-0fP">
|
||||||
<rect key="frame" x="18" y="103" width="256" height="17"/>
|
<rect key="frame" x="18" y="103" width="256" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Super Game Boy Model:" id="d0g-rk-FK0">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Super Game Boy model:" id="d0g-rk-FK0">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -339,16 +434,16 @@
|
|||||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T69-6N-dhT">
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T69-6N-dhT">
|
||||||
<rect key="frame" x="30" y="17" width="233" height="26"/>
|
<rect key="frame" x="30" y="17" width="233" height="26"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="push" title="Disabled (Keep DC Offset)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Fgo-0S-zUG" id="om2-Bn-43B">
|
<popUpButtonCell key="cell" type="push" title="Disabled (Keep DC offset)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Fgo-0S-zUG" id="om2-Bn-43B">
|
||||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="menu"/>
|
<font key="font" metaFont="menu"/>
|
||||||
<menu key="menu" id="VCM-zy-2Dd">
|
<menu key="menu" id="VCM-zy-2Dd">
|
||||||
<items>
|
<items>
|
||||||
<menuItem title="Disabled (Keep DC Offset)" state="on" id="Fgo-0S-zUG">
|
<menuItem title="Disabled (Keep DC offset)" state="on" id="Fgo-0S-zUG">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Accurate (Emulate Hardware)" id="82j-Vv-nE6"/>
|
<menuItem title="Accurate (Emulate hardware)" id="82j-Vv-nE6"/>
|
||||||
<menuItem title="Preserve Waveform" id="iUF-c2-fgt"/>
|
<menuItem title="Preserve waveform" id="iUF-c2-fgt"/>
|
||||||
</items>
|
</items>
|
||||||
</menu>
|
</menu>
|
||||||
</popUpButtonCell>
|
</popUpButtonCell>
|
||||||
@ -359,7 +454,7 @@
|
|||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WU3-oV-KHO">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WU3-oV-KHO">
|
||||||
<rect key="frame" x="18" y="49" width="256" height="17"/>
|
<rect key="frame" x="18" y="49" width="256" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="High-pass Filter:" id="YLF-RL-b2D">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="High-pass filter:" id="YLF-RL-b2D">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -369,22 +464,11 @@
|
|||||||
<point key="canvasLocation" x="-176" y="890"/>
|
<point key="canvasLocation" x="-176" y="890"/>
|
||||||
</customView>
|
</customView>
|
||||||
<customView id="8TU-6J-NCg">
|
<customView id="8TU-6J-NCg">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="292" height="376"/>
|
<rect key="frame" x="0.0" y="0.0" width="292" height="467"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Qa7-Z7-yfO">
|
|
||||||
<rect key="frame" x="20" y="9" width="188" height="32"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<buttonCell key="cell" type="push" title="Configure a Joypad" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="GdK-tQ-Wim">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="configureJoypad:" target="QvC-M9-y7g" id="IfY-Kc-PKU"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Utu-t4-cLx">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Utu-t4-cLx">
|
||||||
<rect key="frame" x="10" y="339" width="122" height="17"/>
|
<rect key="frame" x="10" y="430" width="122" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Control settings for" id="YqW-Ds-VIC">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Control settings for" id="YqW-Ds-VIC">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@ -392,8 +476,17 @@
|
|||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DZu-ts-deW">
|
||||||
|
<rect key="frame" x="18" y="87" width="105" height="17"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Enable rumble:" id="QMX-3p-s1Z">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
<scrollView focusRingType="none" fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="PBp-dj-EIa">
|
<scrollView focusRingType="none" fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="PBp-dj-EIa">
|
||||||
<rect key="frame" x="32" y="117" width="240" height="211"/>
|
<rect key="frame" x="32" y="208" width="240" height="211"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
|
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
|
||||||
<rect key="frame" x="1" y="1" width="238" height="209"/>
|
<rect key="frame" x="1" y="1" width="238" height="209"/>
|
||||||
@ -441,28 +534,28 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
<nil key="backgroundColor"/>
|
<nil key="backgroundColor"/>
|
||||||
</clipView>
|
</clipView>
|
||||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="31h-at-Znm">
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="31h-at-Znm">
|
||||||
<rect key="frame" x="-100" y="-100" width="210" height="16"/>
|
<rect key="frame" x="-100" y="-100" width="210" height="16"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="JkP-U1-jdy">
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="JkP-U1-jdy">
|
||||||
<rect key="frame" x="-100" y="-100" width="15" height="102"/>
|
<rect key="frame" x="-100" y="-100" width="15" height="102"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
</scrollView>
|
</scrollView>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fcF-wc-KwM">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fcF-wc-KwM">
|
||||||
<rect key="frame" x="30" y="92" width="187" height="17"/>
|
<rect key="frame" x="30" y="183" width="203" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Joypad for multiplayer games:" id="AJA-9b-VKI">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Controller for multiplayer games:" id="AJA-9b-VKI">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0Az-0R-oNw">
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0Az-0R-oNw">
|
||||||
<rect key="frame" x="42" y="61" width="208" height="26"/>
|
<rect key="frame" x="42" y="152" width="208" height="26"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="push" title="None" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="hy8-cr-RrE" id="uEC-vN-8Jq">
|
<popUpButtonCell key="cell" type="push" title="None" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingMiddle" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="hy8-cr-RrE" id="uEC-vN-8Jq">
|
||||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="menu"/>
|
<font key="font" metaFont="menu"/>
|
||||||
<menu key="menu" id="vzY-GQ-t9J">
|
<menu key="menu" id="vzY-GQ-t9J">
|
||||||
@ -476,11 +569,11 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="VEc-Ed-Z6f">
|
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="VEc-Ed-Z6f">
|
||||||
<rect key="frame" x="12" y="48" width="268" height="5"/>
|
<rect key="frame" x="12" y="139" width="268" height="5"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
</box>
|
</box>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ReM-uo-H0r">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ReM-uo-H0r">
|
||||||
<rect key="frame" x="215" y="339" width="8" height="17"/>
|
<rect key="frame" x="215" y="430" width="8" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title=":" id="VhO-3T-glt">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title=":" id="VhO-3T-glt">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@ -489,7 +582,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="gWx-7h-0xq">
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="gWx-7h-0xq">
|
||||||
<rect key="frame" x="131" y="332" width="87" height="26"/>
|
<rect key="frame" x="131" y="423" width="87" height="26"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="push" title="Player 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="TO3-R7-9HN" id="pbt-Lr-bU1">
|
<popUpButtonCell key="cell" type="push" title="Player 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="TO3-R7-9HN" id="pbt-Lr-bU1">
|
||||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -507,10 +600,39 @@
|
|||||||
<action selector="refreshJoypadMenu:" target="QvC-M9-y7g" id="5hY-tg-9VE"/>
|
<action selector="refreshJoypadMenu:" target="QvC-M9-y7g" id="5hY-tg-9VE"/>
|
||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="d2I-jU-sLb">
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ogs-xG-b4b">
|
||||||
<rect key="frame" x="212" y="9" width="60" height="32"/>
|
<rect key="frame" x="30" y="58" width="245" height="22"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<buttonCell key="cell" type="push" title="Skip" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="sug-xy-tbw">
|
<popUpButtonCell key="cell" type="push" title="Never" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="jki-7x-bnM" id="o9b-MH-8kd">
|
||||||
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="menu"/>
|
||||||
|
<menu key="menu" id="8p7-je-0Fh">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Never" state="on" id="jki-7x-bnM"/>
|
||||||
|
<menuItem title="For rumble-enabled Game Paks" tag="1" id="58e-Tp-TWd"/>
|
||||||
|
<menuItem title="Always" tag="2" id="qVe-2b-W1P"/>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</popUpButtonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="rumbleModeChanged:" target="QvC-M9-y7g" id="AQe-vQ-mSl"/>
|
||||||
|
</connections>
|
||||||
|
</popUpButton>
|
||||||
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="RuW-Db-dzW">
|
||||||
|
<rect key="frame" x="18" y="110" width="264" height="25"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="check" title="Analog turbo and slow-motion controls" bezelStyle="regularSquare" imagePosition="left" lineBreakMode="charWrapping" inset="2" id="Mvp-oc-N3t">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="changeAnalogControls:" target="QvC-M9-y7g" id="1xR-gY-WKo"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="d2I-jU-sLb">
|
||||||
|
<rect key="frame" x="206" y="13" width="72" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="push" title="Clear" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="sug-xy-tbw">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
@ -518,8 +640,19 @@
|
|||||||
<action selector="skipButton:" target="QvC-M9-y7g" id="aw8-sw-yJw"/>
|
<action selector="skipButton:" target="QvC-M9-y7g" id="aw8-sw-yJw"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Qa7-Z7-yfO">
|
||||||
|
<rect key="frame" x="18" y="13" width="188" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="push" title="Configure a Controller" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="GdK-tQ-Wim">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="configureJoypad:" target="QvC-M9-y7g" id="IfY-Kc-PKU"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
</subviews>
|
</subviews>
|
||||||
<point key="canvasLocation" x="-159" y="1116"/>
|
<point key="canvasLocation" x="-159" y="1161.5"/>
|
||||||
</customView>
|
</customView>
|
||||||
</objects>
|
</objects>
|
||||||
<resources>
|
<resources>
|
||||||
|
748
Cocoa/joypad.m
748
Cocoa/joypad.m
@ -1,748 +0,0 @@
|
|||||||
/*
|
|
||||||
Joypad support is based on a stripped-down version of SDL's Darwin implementation
|
|
||||||
of the Joystick API, under the following license:
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
|
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied
|
|
||||||
warranty. In no event will the authors be held liable for any damages
|
|
||||||
arising from the use of this software.
|
|
||||||
|
|
||||||
Permission is granted to anyone to use this software for any purpose,
|
|
||||||
including commercial applications, and to alter it and redistribute it
|
|
||||||
freely, subject to the following restrictions:
|
|
||||||
|
|
||||||
1. The origin of this software must not be misrepresented; you must not
|
|
||||||
claim that you wrote the original software. If you use this software
|
|
||||||
in a product, an acknowledgment in the product documentation would be
|
|
||||||
appreciated but is not required.
|
|
||||||
2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
misrepresented as being the original software.
|
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <AppKit/AppKit.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <IOKit/hid/IOHIDLib.h>
|
|
||||||
#include "GBJoystickListener.h"
|
|
||||||
|
|
||||||
typedef signed SDL_JoystickID;
|
|
||||||
typedef struct _SDL_Joystick SDL_Joystick;
|
|
||||||
|
|
||||||
typedef struct _SDL_JoystickAxisInfo
|
|
||||||
{
|
|
||||||
int16_t initial_value; /* Initial axis state */
|
|
||||||
int16_t value; /* Current axis state */
|
|
||||||
int16_t zero; /* Zero point on the axis (-32768 for triggers) */
|
|
||||||
bool has_initial_value; /* Whether we've seen a value on the axis yet */
|
|
||||||
bool sent_initial_value; /* Whether we've sent the initial axis value */
|
|
||||||
} SDL_JoystickAxisInfo;
|
|
||||||
|
|
||||||
struct _SDL_Joystick
|
|
||||||
{
|
|
||||||
SDL_JoystickID instance_id; /* Device instance, monotonically increasing from 0 */
|
|
||||||
char *name; /* Joystick name - system dependent */
|
|
||||||
|
|
||||||
int naxes; /* Number of axis controls on the joystick */
|
|
||||||
SDL_JoystickAxisInfo *axes;
|
|
||||||
|
|
||||||
int nbuttons; /* Number of buttons on the joystick */
|
|
||||||
uint8_t *buttons; /* Current button states */
|
|
||||||
|
|
||||||
int nhats;
|
|
||||||
uint8_t *hats;
|
|
||||||
|
|
||||||
struct joystick_hwdata *hwdata; /* Driver dependent information */
|
|
||||||
|
|
||||||
int ref_count; /* Reference count for multiple opens */
|
|
||||||
|
|
||||||
bool is_game_controller;
|
|
||||||
bool force_recentering; /* SDL_TRUE if this device needs to have its state reset to 0 */
|
|
||||||
struct _SDL_Joystick *next; /* pointer to next joystick we have allocated */
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t data[16];
|
|
||||||
} SDL_JoystickGUID;
|
|
||||||
|
|
||||||
struct recElement
|
|
||||||
{
|
|
||||||
IOHIDElementRef elementRef;
|
|
||||||
IOHIDElementCookie cookie;
|
|
||||||
uint32_t usagePage, usage; /* HID usage */
|
|
||||||
SInt32 min; /* reported min value possible */
|
|
||||||
SInt32 max; /* reported max value possible */
|
|
||||||
|
|
||||||
/* runtime variables used for auto-calibration */
|
|
||||||
SInt32 minReport; /* min returned value */
|
|
||||||
SInt32 maxReport; /* max returned value */
|
|
||||||
|
|
||||||
struct recElement *pNext; /* next element in list */
|
|
||||||
};
|
|
||||||
typedef struct recElement recElement;
|
|
||||||
|
|
||||||
struct joystick_hwdata
|
|
||||||
{
|
|
||||||
IOHIDDeviceRef deviceRef; /* HIDManager device handle */
|
|
||||||
io_service_t ffservice; /* Interface for force feedback, 0 = no ff */
|
|
||||||
|
|
||||||
char product[256]; /* name of product */
|
|
||||||
uint32_t usage; /* usage page from IOUSBHID Parser.h which defines general usage */
|
|
||||||
uint32_t usagePage; /* usage within above page from IOUSBHID Parser.h which defines specific usage */
|
|
||||||
|
|
||||||
int axes; /* number of axis (calculated, not reported by device) */
|
|
||||||
int buttons; /* number of buttons (calculated, not reported by device) */
|
|
||||||
int hats;
|
|
||||||
int elements; /* number of total elements (should be total of above) (calculated, not reported by device) */
|
|
||||||
|
|
||||||
recElement *firstAxis;
|
|
||||||
recElement *firstButton;
|
|
||||||
recElement *firstHat;
|
|
||||||
|
|
||||||
bool removed;
|
|
||||||
|
|
||||||
int instance_id;
|
|
||||||
SDL_JoystickGUID guid;
|
|
||||||
|
|
||||||
SDL_Joystick joystick;
|
|
||||||
};
|
|
||||||
typedef struct joystick_hwdata recDevice;
|
|
||||||
|
|
||||||
/* The base object of the HID Manager API */
|
|
||||||
static IOHIDManagerRef hidman = NULL;
|
|
||||||
|
|
||||||
/* static incrementing counter for new joystick devices seen on the system. Devices should start with index 0 */
|
|
||||||
static int s_joystick_instance_id = -1;
|
|
||||||
|
|
||||||
#define SDL_JOYSTICK_AXIS_MAX 32767
|
|
||||||
|
|
||||||
void SDL_PrivateJoystickAxis(SDL_Joystick * joystick, uint8_t axis, int16_t value)
|
|
||||||
{
|
|
||||||
/* Make sure we're not getting garbage or duplicate events */
|
|
||||||
if (axis >= joystick->naxes) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!joystick->axes[axis].has_initial_value) {
|
|
||||||
joystick->axes[axis].initial_value = value;
|
|
||||||
joystick->axes[axis].value = value;
|
|
||||||
joystick->axes[axis].zero = value;
|
|
||||||
joystick->axes[axis].has_initial_value = true;
|
|
||||||
}
|
|
||||||
if (value == joystick->axes[axis].value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!joystick->axes[axis].sent_initial_value) {
|
|
||||||
/* Make sure we don't send motion until there's real activity on this axis */
|
|
||||||
const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; /* ShanWan PS3 controller needed 96 */
|
|
||||||
if (abs(value - joystick->axes[axis].value) <= MAX_ALLOWED_JITTER) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
joystick->axes[axis].sent_initial_value = true;
|
|
||||||
joystick->axes[axis].value = value; /* Just so we pass the check above */
|
|
||||||
SDL_PrivateJoystickAxis(joystick, axis, joystick->axes[axis].initial_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update internal joystick state */
|
|
||||||
joystick->axes[axis].value = value;
|
|
||||||
|
|
||||||
NSResponder<GBJoystickListener> *responder = (typeof(responder)) [[NSApp keyWindow] firstResponder];
|
|
||||||
while (responder) {
|
|
||||||
if ([responder respondsToSelector:@selector(joystick:axis:movedTo:)]) {
|
|
||||||
[responder joystick:@(joystick->name) axis:axis movedTo:value];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
responder = (typeof(responder)) [responder nextResponder];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_PrivateJoystickButton(SDL_Joystick *joystick, uint8_t button, uint8_t state)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Make sure we're not getting garbage or duplicate events */
|
|
||||||
if (button >= joystick->nbuttons) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state == joystick->buttons[button]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update internal joystick state */
|
|
||||||
joystick->buttons[button] = state;
|
|
||||||
|
|
||||||
NSResponder<GBJoystickListener> *responder = (typeof(responder)) [[NSApp keyWindow] firstResponder];
|
|
||||||
while (responder) {
|
|
||||||
if ([responder respondsToSelector:@selector(joystick:button:changedState:)]) {
|
|
||||||
[responder joystick:@(joystick->name) button:button changedState:state];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
responder = (typeof(responder)) [responder nextResponder];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_PrivateJoystickHat(SDL_Joystick *joystick, uint8_t hat, uint8_t state)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Make sure we're not getting garbage or duplicate events */
|
|
||||||
if (hat >= joystick->nhats) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state == joystick->hats[hat]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update internal joystick state */
|
|
||||||
joystick->hats[hat] = state;
|
|
||||||
|
|
||||||
NSResponder<GBJoystickListener> *responder = (typeof(responder)) [[NSApp keyWindow] firstResponder];
|
|
||||||
while (responder) {
|
|
||||||
if ([responder respondsToSelector:@selector(joystick:button:changedState:)]) {
|
|
||||||
[responder joystick:@(joystick->name) hat:hat changedState:state];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
responder = (typeof(responder)) [responder nextResponder];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
FreeElementList(recElement *pElement)
|
|
||||||
{
|
|
||||||
while (pElement) {
|
|
||||||
recElement *pElementNext = pElement->pNext;
|
|
||||||
free(pElement);
|
|
||||||
pElement = pElementNext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static recDevice *
|
|
||||||
FreeDevice(recDevice *removeDevice)
|
|
||||||
{
|
|
||||||
recDevice *pDeviceNext = NULL;
|
|
||||||
if (removeDevice) {
|
|
||||||
if (removeDevice->deviceRef) {
|
|
||||||
IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
|
||||||
removeDevice->deviceRef = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* free element lists */
|
|
||||||
FreeElementList(removeDevice->firstAxis);
|
|
||||||
FreeElementList(removeDevice->firstButton);
|
|
||||||
FreeElementList(removeDevice->firstHat);
|
|
||||||
|
|
||||||
free(removeDevice);
|
|
||||||
}
|
|
||||||
return pDeviceNext;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SInt32
|
|
||||||
GetHIDElementState(recDevice *pDevice, recElement *pElement)
|
|
||||||
{
|
|
||||||
SInt32 value = 0;
|
|
||||||
|
|
||||||
if (pDevice && pElement) {
|
|
||||||
IOHIDValueRef valueRef;
|
|
||||||
if (IOHIDDeviceGetValue(pDevice->deviceRef, pElement->elementRef, &valueRef) == kIOReturnSuccess) {
|
|
||||||
value = (SInt32) IOHIDValueGetIntegerValue(valueRef);
|
|
||||||
|
|
||||||
/* record min and max for auto calibration */
|
|
||||||
if (value < pElement->minReport) {
|
|
||||||
pElement->minReport = value;
|
|
||||||
}
|
|
||||||
if (value > pElement->maxReport) {
|
|
||||||
pElement->maxReport = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SInt32
|
|
||||||
GetHIDScaledCalibratedState(recDevice * pDevice, recElement * pElement, SInt32 min, SInt32 max)
|
|
||||||
{
|
|
||||||
const float deviceScale = max - min;
|
|
||||||
const float readScale = pElement->maxReport - pElement->minReport;
|
|
||||||
const SInt32 value = GetHIDElementState(pDevice, pElement);
|
|
||||||
if (readScale == 0) {
|
|
||||||
return value; /* no scaling at all */
|
|
||||||
}
|
|
||||||
return ((value - pElement->minReport) * deviceScale / readScale) + min;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
JoystickDeviceWasRemovedCallback(void *ctx, IOReturn result, void *sender)
|
|
||||||
{
|
|
||||||
recDevice *device = (recDevice *) ctx;
|
|
||||||
device->removed = true;
|
|
||||||
device->deviceRef = NULL; // deviceRef was invalidated due to the remove
|
|
||||||
FreeDevice(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void AddHIDElement(const void *value, void *parameter);
|
|
||||||
|
|
||||||
/* Call AddHIDElement() on all elements in an array of IOHIDElementRefs */
|
|
||||||
static void
|
|
||||||
AddHIDElements(CFArrayRef array, recDevice *pDevice)
|
|
||||||
{
|
|
||||||
const CFRange range = { 0, CFArrayGetCount(array) };
|
|
||||||
CFArrayApplyFunction(array, range, AddHIDElement, pDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
ElementAlreadyAdded(const IOHIDElementCookie cookie, const recElement *listitem) {
|
|
||||||
while (listitem) {
|
|
||||||
if (listitem->cookie == cookie) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
listitem = listitem->pNext;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See if we care about this HID element, and if so, note it in our recDevice. */
|
|
||||||
static void
|
|
||||||
AddHIDElement(const void *value, void *parameter)
|
|
||||||
{
|
|
||||||
recDevice *pDevice = (recDevice *) parameter;
|
|
||||||
IOHIDElementRef refElement = (IOHIDElementRef) value;
|
|
||||||
const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;
|
|
||||||
|
|
||||||
if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
|
|
||||||
const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
|
|
||||||
const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
|
|
||||||
const uint32_t usage = IOHIDElementGetUsage(refElement);
|
|
||||||
recElement *element = NULL;
|
|
||||||
recElement **headElement = NULL;
|
|
||||||
|
|
||||||
/* look at types of interest */
|
|
||||||
switch (IOHIDElementGetType(refElement)) {
|
|
||||||
case kIOHIDElementTypeInput_Misc:
|
|
||||||
case kIOHIDElementTypeInput_Button:
|
|
||||||
case kIOHIDElementTypeInput_Axis: {
|
|
||||||
switch (usagePage) { /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
|
|
||||||
case kHIDPage_GenericDesktop:
|
|
||||||
switch (usage) {
|
|
||||||
case kHIDUsage_GD_X:
|
|
||||||
case kHIDUsage_GD_Y:
|
|
||||||
case kHIDUsage_GD_Z:
|
|
||||||
case kHIDUsage_GD_Rx:
|
|
||||||
case kHIDUsage_GD_Ry:
|
|
||||||
case kHIDUsage_GD_Rz:
|
|
||||||
case kHIDUsage_GD_Slider:
|
|
||||||
case kHIDUsage_GD_Dial:
|
|
||||||
case kHIDUsage_GD_Wheel:
|
|
||||||
if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
|
|
||||||
element = (recElement *) calloc(1, sizeof (recElement));
|
|
||||||
if (element) {
|
|
||||||
pDevice->axes++;
|
|
||||||
headElement = &(pDevice->firstAxis);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case kHIDUsage_GD_Hatswitch:
|
|
||||||
if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) {
|
|
||||||
element = (recElement *) calloc(1, sizeof (recElement));
|
|
||||||
if (element) {
|
|
||||||
pDevice->hats++;
|
|
||||||
headElement = &(pDevice->firstHat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kHIDUsage_GD_DPadUp:
|
|
||||||
case kHIDUsage_GD_DPadDown:
|
|
||||||
case kHIDUsage_GD_DPadRight:
|
|
||||||
case kHIDUsage_GD_DPadLeft:
|
|
||||||
case kHIDUsage_GD_Start:
|
|
||||||
case kHIDUsage_GD_Select:
|
|
||||||
case kHIDUsage_GD_SystemMainMenu:
|
|
||||||
if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
|
|
||||||
element = (recElement *) calloc(1, sizeof (recElement));
|
|
||||||
if (element) {
|
|
||||||
pDevice->buttons++;
|
|
||||||
headElement = &(pDevice->firstButton);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kHIDPage_Simulation:
|
|
||||||
switch (usage) {
|
|
||||||
case kHIDUsage_Sim_Rudder:
|
|
||||||
case kHIDUsage_Sim_Throttle:
|
|
||||||
case kHIDUsage_Sim_Accelerator:
|
|
||||||
case kHIDUsage_Sim_Brake:
|
|
||||||
if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
|
|
||||||
element = (recElement *) calloc(1, sizeof (recElement));
|
|
||||||
if (element) {
|
|
||||||
pDevice->axes++;
|
|
||||||
headElement = &(pDevice->firstAxis);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kHIDPage_Button:
|
|
||||||
case kHIDPage_Consumer: /* e.g. 'pause' button on Steelseries MFi gamepads. */
|
|
||||||
if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
|
|
||||||
element = (recElement *) calloc(1, sizeof (recElement));
|
|
||||||
if (element) {
|
|
||||||
pDevice->buttons++;
|
|
||||||
headElement = &(pDevice->firstButton);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kIOHIDElementTypeCollection: {
|
|
||||||
CFArrayRef array = IOHIDElementGetChildren(refElement);
|
|
||||||
if (array) {
|
|
||||||
AddHIDElements(array, pDevice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element && headElement) { /* add to list */
|
|
||||||
recElement *elementPrevious = NULL;
|
|
||||||
recElement *elementCurrent = *headElement;
|
|
||||||
while (elementCurrent && usage >= elementCurrent->usage) {
|
|
||||||
elementPrevious = elementCurrent;
|
|
||||||
elementCurrent = elementCurrent->pNext;
|
|
||||||
}
|
|
||||||
if (elementPrevious) {
|
|
||||||
elementPrevious->pNext = element;
|
|
||||||
} else {
|
|
||||||
*headElement = element;
|
|
||||||
}
|
|
||||||
|
|
||||||
element->elementRef = refElement;
|
|
||||||
element->usagePage = usagePage;
|
|
||||||
element->usage = usage;
|
|
||||||
element->pNext = elementCurrent;
|
|
||||||
|
|
||||||
element->minReport = element->min = (SInt32) IOHIDElementGetLogicalMin(refElement);
|
|
||||||
element->maxReport = element->max = (SInt32) IOHIDElementGetLogicalMax(refElement);
|
|
||||||
element->cookie = IOHIDElementGetCookie(refElement);
|
|
||||||
|
|
||||||
pDevice->elements++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
|
|
||||||
{
|
|
||||||
const uint16_t BUS_USB = 0x03;
|
|
||||||
const uint16_t BUS_BLUETOOTH = 0x05;
|
|
||||||
int32_t vendor = 0;
|
|
||||||
int32_t product = 0;
|
|
||||||
int32_t version = 0;
|
|
||||||
CFTypeRef refCF = NULL;
|
|
||||||
CFArrayRef array = NULL;
|
|
||||||
uint16_t *guid16 = (uint16_t *)pDevice->guid.data;
|
|
||||||
|
|
||||||
/* get usage page and usage */
|
|
||||||
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
|
|
||||||
if (refCF) {
|
|
||||||
CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
|
|
||||||
}
|
|
||||||
if (pDevice->usagePage != kHIDPage_GenericDesktop) {
|
|
||||||
return false; /* Filter device list to non-keyboard/mouse stuff */
|
|
||||||
}
|
|
||||||
|
|
||||||
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
|
|
||||||
if (refCF) {
|
|
||||||
CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((pDevice->usage != kHIDUsage_GD_Joystick &&
|
|
||||||
pDevice->usage != kHIDUsage_GD_GamePad &&
|
|
||||||
pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
|
|
||||||
return false; /* Filter device list to non-keyboard/mouse stuff */
|
|
||||||
}
|
|
||||||
|
|
||||||
pDevice->deviceRef = hidDevice;
|
|
||||||
|
|
||||||
/* get device name */
|
|
||||||
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
|
|
||||||
if (!refCF) {
|
|
||||||
/* Maybe we can't get "AwesomeJoystick2000", but we can get "Logitech"? */
|
|
||||||
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
|
|
||||||
}
|
|
||||||
if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
|
|
||||||
strlcpy(pDevice->product, "Unidentified joystick", sizeof (pDevice->product));
|
|
||||||
}
|
|
||||||
|
|
||||||
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
|
|
||||||
if (refCF) {
|
|
||||||
CFNumberGetValue(refCF, kCFNumberSInt32Type, &vendor);
|
|
||||||
}
|
|
||||||
|
|
||||||
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
|
|
||||||
if (refCF) {
|
|
||||||
CFNumberGetValue(refCF, kCFNumberSInt32Type, &product);
|
|
||||||
}
|
|
||||||
|
|
||||||
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVersionNumberKey));
|
|
||||||
if (refCF) {
|
|
||||||
CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(pDevice->guid.data, 0, sizeof(pDevice->guid.data));
|
|
||||||
|
|
||||||
if (vendor && product) {
|
|
||||||
*guid16++ = BUS_USB;
|
|
||||||
*guid16++ = 0;
|
|
||||||
*guid16++ = vendor;
|
|
||||||
*guid16++ = 0;
|
|
||||||
*guid16++ = product;
|
|
||||||
*guid16++ = 0;
|
|
||||||
*guid16++ = version;
|
|
||||||
*guid16++ = 0;
|
|
||||||
} else {
|
|
||||||
*guid16++ = BUS_BLUETOOTH;
|
|
||||||
*guid16++ = 0;
|
|
||||||
strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
array = IOHIDDeviceCopyMatchingElements(hidDevice, NULL, kIOHIDOptionsTypeNone);
|
|
||||||
if (array) {
|
|
||||||
AddHIDElements(array, pDevice);
|
|
||||||
CFRelease(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
|
|
||||||
{
|
|
||||||
recDevice *device = joystick->hwdata;
|
|
||||||
recElement *element;
|
|
||||||
SInt32 value;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!device) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device->removed) { /* device was unplugged; ignore it. */
|
|
||||||
if (joystick->hwdata) {
|
|
||||||
joystick->force_recentering = true;
|
|
||||||
joystick->hwdata = NULL;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
element = device->firstAxis;
|
|
||||||
i = 0;
|
|
||||||
while (element) {
|
|
||||||
value = GetHIDScaledCalibratedState(device, element, -32768, 32767);
|
|
||||||
SDL_PrivateJoystickAxis(joystick, i, value);
|
|
||||||
element = element->pNext;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
element = device->firstButton;
|
|
||||||
i = 0;
|
|
||||||
while (element) {
|
|
||||||
value = GetHIDElementState(device, element);
|
|
||||||
if (value > 1) { /* handle pressure-sensitive buttons */
|
|
||||||
value = 1;
|
|
||||||
}
|
|
||||||
SDL_PrivateJoystickButton(joystick, i, value);
|
|
||||||
element = element->pNext;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
element = device->firstHat;
|
|
||||||
i = 0;
|
|
||||||
while (element) {
|
|
||||||
signed range = (element->max - element->min + 1);
|
|
||||||
value = GetHIDElementState(device, element) - element->min;
|
|
||||||
if (range == 4) { /* 4 position hatswitch - scale up value */
|
|
||||||
value *= 2;
|
|
||||||
} else if (range != 8) { /* Neither a 4 nor 8 positions - fall back to default position (centered) */
|
|
||||||
value = -1;
|
|
||||||
}
|
|
||||||
if ((unsigned)value >= 8) {
|
|
||||||
value = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_PrivateJoystickHat(joystick, i, value);
|
|
||||||
|
|
||||||
element = element->pNext;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void JoystickInputCallback(
|
|
||||||
SDL_Joystick * joystick,
|
|
||||||
IOReturn result,
|
|
||||||
void * _Nullable sender,
|
|
||||||
IOHIDReportType type,
|
|
||||||
uint32_t reportID,
|
|
||||||
uint8_t * report,
|
|
||||||
CFIndex reportLength)
|
|
||||||
{
|
|
||||||
SDL_SYS_JoystickUpdate(joystick);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject)
|
|
||||||
{
|
|
||||||
recDevice *device;
|
|
||||||
io_service_t ioservice;
|
|
||||||
|
|
||||||
if (res != kIOReturnSuccess) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
device = (recDevice *) calloc(1, sizeof(recDevice));
|
|
||||||
|
|
||||||
if (!device) {
|
|
||||||
abort();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
|
|
||||||
free(device);
|
|
||||||
return; /* not a device we care about, probably. */
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Joystick *joystick = &device->joystick;
|
|
||||||
|
|
||||||
joystick->instance_id = device->instance_id;
|
|
||||||
joystick->hwdata = device;
|
|
||||||
joystick->name = device->product;
|
|
||||||
|
|
||||||
joystick->naxes = device->axes;
|
|
||||||
joystick->nbuttons = device->buttons;
|
|
||||||
joystick->nhats = device->hats;
|
|
||||||
|
|
||||||
if (joystick->naxes > 0) {
|
|
||||||
joystick->axes = (SDL_JoystickAxisInfo *) calloc(joystick->naxes, sizeof(SDL_JoystickAxisInfo));
|
|
||||||
}
|
|
||||||
if (joystick->nbuttons > 0) {
|
|
||||||
joystick->buttons = (uint8_t *) calloc(joystick->nbuttons, 1);
|
|
||||||
}
|
|
||||||
if (joystick->nhats > 0) {
|
|
||||||
joystick->hats = (uint8_t *) calloc(joystick->nhats, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get notified when this device is disconnected. */
|
|
||||||
IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
|
|
||||||
static uint8_t junk[80];
|
|
||||||
IOHIDDeviceRegisterInputReportCallback(ioHIDDeviceObject, junk, sizeof(junk), (IOHIDReportCallback) JoystickInputCallback, joystick);
|
|
||||||
IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
|
||||||
|
|
||||||
/* Allocate an instance ID for this device */
|
|
||||||
device->instance_id = ++s_joystick_instance_id;
|
|
||||||
|
|
||||||
/* We have to do some storage of the io_service_t for SDL_HapticOpenFromJoystick */
|
|
||||||
ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
ConfigHIDManager(CFArrayRef matchingArray)
|
|
||||||
{
|
|
||||||
CFRunLoopRef runloop = CFRunLoopGetCurrent();
|
|
||||||
|
|
||||||
if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
|
|
||||||
IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL);
|
|
||||||
IOHIDManagerScheduleWithRunLoop(hidman, runloop, kCFRunLoopDefaultMode);
|
|
||||||
|
|
||||||
return true; /* good to go. */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static CFDictionaryRef
|
|
||||||
CreateHIDDeviceMatchDictionary(const UInt32 page, const UInt32 usage, int *okay)
|
|
||||||
{
|
|
||||||
CFDictionaryRef retval = NULL;
|
|
||||||
CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
|
|
||||||
CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
|
|
||||||
const void *keys[2] = { (void *) CFSTR(kIOHIDDeviceUsagePageKey), (void *) CFSTR(kIOHIDDeviceUsageKey) };
|
|
||||||
const void *vals[2] = { (void *) pageNumRef, (void *) usageNumRef };
|
|
||||||
|
|
||||||
if (pageNumRef && usageNumRef) {
|
|
||||||
retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pageNumRef) {
|
|
||||||
CFRelease(pageNumRef);
|
|
||||||
}
|
|
||||||
if (usageNumRef) {
|
|
||||||
CFRelease(usageNumRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!retval) {
|
|
||||||
*okay = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
CreateHIDManager(void)
|
|
||||||
{
|
|
||||||
bool retval = false;
|
|
||||||
int okay = 1;
|
|
||||||
const void *vals[] = {
|
|
||||||
(void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
|
|
||||||
(void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
|
|
||||||
(void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
|
|
||||||
};
|
|
||||||
const size_t numElements = sizeof(vals) / sizeof(vals[0]);
|
|
||||||
CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) : NULL;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < numElements; i++) {
|
|
||||||
if (vals[i]) {
|
|
||||||
CFRelease((CFTypeRef) vals[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array) {
|
|
||||||
hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
|
|
||||||
if (hidman != NULL) {
|
|
||||||
retval = ConfigHIDManager(array);
|
|
||||||
}
|
|
||||||
CFRelease(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void __attribute__((constructor)) SDL_SYS_JoystickInit(void)
|
|
||||||
{
|
|
||||||
if (!CreateHIDManager()) {
|
|
||||||
fprintf(stderr, "Joystick: Couldn't initialize HID Manager");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
int main(int argc, const char * argv[]) {
|
int main(int argc, const char * argv[])
|
||||||
|
{
|
||||||
return NSApplicationMain(argc, argv);
|
return NSApplicationMain(argc, argv);
|
||||||
}
|
}
|
||||||
|
138
Core/apu.c
138
Core/apu.c
@ -48,6 +48,23 @@ bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t agb_bias_for_channel(GB_gameboy_t *gb, unsigned index)
|
||||||
|
{
|
||||||
|
if (!gb->apu.is_active[index]) return 0;
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case GB_SQUARE_1:
|
||||||
|
return gb->apu.square_channels[GB_SQUARE_1].current_volume;
|
||||||
|
case GB_SQUARE_2:
|
||||||
|
return gb->apu.square_channels[GB_SQUARE_2].current_volume;
|
||||||
|
case GB_WAVE:
|
||||||
|
return 0;
|
||||||
|
case GB_NOISE:
|
||||||
|
return gb->apu.noise_channel.current_volume;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsigned cycles_offset)
|
static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsigned cycles_offset)
|
||||||
{
|
{
|
||||||
if (gb->model >= GB_MODEL_AGB) {
|
if (gb->model >= GB_MODEL_AGB) {
|
||||||
@ -66,15 +83,17 @@ static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsign
|
|||||||
}
|
}
|
||||||
|
|
||||||
GB_sample_t output;
|
GB_sample_t output;
|
||||||
|
uint8_t bias = agb_bias_for_channel(gb, index);
|
||||||
|
|
||||||
if (gb->io_registers[GB_IO_NR51] & (1 << index)) {
|
if (gb->io_registers[GB_IO_NR51] & (1 << index)) {
|
||||||
output.right = (0xf - value * 2) * right_volume;
|
output.right = (0xf - value * 2 + bias) * right_volume;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
output.right = 0xf * right_volume;
|
output.right = 0xf * right_volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) {
|
if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) {
|
||||||
output.left = (0xf - value * 2) * left_volume;
|
output.left = (0xf - value * 2 + bias) * left_volume;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
output.left = 0xf * left_volume;
|
output.left = 0xf * left_volume;
|
||||||
@ -120,7 +139,7 @@ static double smooth(double x)
|
|||||||
|
|
||||||
static void render(GB_gameboy_t *gb)
|
static void render(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
GB_sample_t output = {0,0};
|
GB_sample_t output = {0, 0};
|
||||||
|
|
||||||
UNROLL
|
UNROLL
|
||||||
for (unsigned i = 0; i < GB_N_CHANNELS; i++) {
|
for (unsigned i = 0; i < GB_N_CHANNELS; i++) {
|
||||||
@ -211,13 +230,13 @@ static void render(GB_gameboy_t *gb)
|
|||||||
gb->apu_output.sample_callback(gb, &filtered_output);
|
gb->apu_output.sample_callback(gb, &filtered_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t new_sweep_sample_legnth(GB_gameboy_t *gb)
|
static uint16_t new_sweep_sample_length(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
uint16_t delta = gb->apu.shadow_sweep_sample_legnth >> (gb->io_registers[GB_IO_NR10] & 7);
|
uint16_t delta = gb->apu.shadow_sweep_sample_length >> (gb->io_registers[GB_IO_NR10] & 7);
|
||||||
if (gb->io_registers[GB_IO_NR10] & 8) {
|
if (gb->io_registers[GB_IO_NR10] & 8) {
|
||||||
return gb->apu.shadow_sweep_sample_legnth - delta;
|
return gb->apu.shadow_sweep_sample_length - delta;
|
||||||
}
|
}
|
||||||
return gb->apu.shadow_sweep_sample_legnth + delta;
|
return gb->apu.shadow_sweep_sample_length + delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_square_sample(GB_gameboy_t *gb, unsigned index)
|
static void update_square_sample(GB_gameboy_t *gb, unsigned index)
|
||||||
@ -264,6 +283,15 @@ static void tick_square_envelope(GB_gameboy_t *gb, enum GB_CHANNELS index)
|
|||||||
|
|
||||||
if (gb->apu.square_channels[index].volume_countdown || (nrx2 & 7)) {
|
if (gb->apu.square_channels[index].volume_countdown || (nrx2 & 7)) {
|
||||||
if (!gb->apu.square_channels[index].volume_countdown || !--gb->apu.square_channels[index].volume_countdown) {
|
if (!gb->apu.square_channels[index].volume_countdown || !--gb->apu.square_channels[index].volume_countdown) {
|
||||||
|
if (gb->cgb_double_speed) {
|
||||||
|
if (index == GB_SQUARE_1) {
|
||||||
|
gb->apu.pcm_mask[0] &= gb->apu.square_channels[GB_SQUARE_1].current_volume | 0xF1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->apu.pcm_mask[0] &= (gb->apu.square_channels[GB_SQUARE_2].current_volume << 2) | 0x1F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((nrx2 & 8) && gb->apu.square_channels[index].current_volume < 0xF) {
|
if ((nrx2 & 8) && gb->apu.square_channels[index].current_volume < 0xF) {
|
||||||
gb->apu.square_channels[index].current_volume++;
|
gb->apu.square_channels[index].current_volume++;
|
||||||
}
|
}
|
||||||
@ -286,7 +314,10 @@ static void tick_noise_envelope(GB_gameboy_t *gb)
|
|||||||
uint8_t nr42 = gb->io_registers[GB_IO_NR42];
|
uint8_t nr42 = gb->io_registers[GB_IO_NR42];
|
||||||
|
|
||||||
if (gb->apu.noise_channel.volume_countdown || (nr42 & 7)) {
|
if (gb->apu.noise_channel.volume_countdown || (nr42 & 7)) {
|
||||||
if (!--gb->apu.noise_channel.volume_countdown) {
|
if (!gb->apu.noise_channel.volume_countdown || !--gb->apu.noise_channel.volume_countdown) {
|
||||||
|
if (gb->cgb_double_speed) {
|
||||||
|
gb->apu.pcm_mask[0] &= (gb->apu.noise_channel.current_volume << 2) | 0x1F;
|
||||||
|
}
|
||||||
if ((nr42 & 8) && gb->apu.noise_channel.current_volume < 0xF) {
|
if ((nr42 & 8) && gb->apu.noise_channel.current_volume < 0xF) {
|
||||||
gb->apu.noise_channel.current_volume++;
|
gb->apu.noise_channel.current_volume++;
|
||||||
}
|
}
|
||||||
@ -310,11 +341,16 @@ static void tick_noise_envelope(GB_gameboy_t *gb)
|
|||||||
void GB_apu_div_event(GB_gameboy_t *gb)
|
void GB_apu_div_event(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
if (!gb->apu.global_enable) return;
|
if (!gb->apu.global_enable) return;
|
||||||
if (gb->apu.skip_div_event) {
|
if (gb->apu.skip_div_event == GB_SKIP_DIV_EVENT_SKIP) {
|
||||||
gb->apu.skip_div_event = false;
|
gb->apu.skip_div_event = GB_SKIP_DIV_EVENT_SKIPPED;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gb->apu.div_divider++;
|
if (gb->apu.skip_div_event == GB_SKIP_DIV_EVENT_SKIPPED) {
|
||||||
|
gb->apu.skip_div_event = GB_SKIP_DIV_EVENT_INACTIVE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->apu.div_divider++;
|
||||||
|
}
|
||||||
|
|
||||||
if ((gb->apu.div_divider & 1) == 0) {
|
if ((gb->apu.div_divider & 1) == 0) {
|
||||||
for (unsigned i = GB_SQUARE_2 + 1; i--;) {
|
for (unsigned i = GB_SQUARE_2 + 1; i--;) {
|
||||||
@ -376,8 +412,8 @@ void GB_apu_div_event(GB_gameboy_t *gb)
|
|||||||
if (!--gb->apu.square_sweep_countdown) {
|
if (!--gb->apu.square_sweep_countdown) {
|
||||||
if ((gb->io_registers[GB_IO_NR10] & 0x70) && (gb->io_registers[GB_IO_NR10] & 0x07)) {
|
if ((gb->io_registers[GB_IO_NR10] & 0x70) && (gb->io_registers[GB_IO_NR10] & 0x07)) {
|
||||||
gb->apu.square_channels[GB_SQUARE_1].sample_length =
|
gb->apu.square_channels[GB_SQUARE_1].sample_length =
|
||||||
gb->apu.shadow_sweep_sample_legnth =
|
gb->apu.shadow_sweep_sample_length =
|
||||||
gb->apu.new_sweep_sample_legnth;
|
gb->apu.new_sweep_sample_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->io_registers[GB_IO_NR10] & 0x70) {
|
if (gb->io_registers[GB_IO_NR10] & 0x70) {
|
||||||
@ -399,7 +435,7 @@ void GB_apu_run(GB_gameboy_t *gb)
|
|||||||
uint8_t cycles = gb->apu.apu_cycles >> 2;
|
uint8_t cycles = gb->apu.apu_cycles >> 2;
|
||||||
gb->apu.apu_cycles = 0;
|
gb->apu.apu_cycles = 0;
|
||||||
if (!cycles) return;
|
if (!cycles) return;
|
||||||
|
|
||||||
if (likely(!gb->stopped || GB_is_cgb(gb))) {
|
if (likely(!gb->stopped || GB_is_cgb(gb))) {
|
||||||
/* To align the square signal to 1MHz */
|
/* To align the square signal to 1MHz */
|
||||||
gb->apu.lf_div ^= cycles & 1;
|
gb->apu.lf_div ^= cycles & 1;
|
||||||
@ -411,8 +447,8 @@ void GB_apu_run(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* APU bug: sweep frequency is checked after adding the sweep delta twice */
|
/* APU bug: sweep frequency is checked after adding the sweep delta twice */
|
||||||
gb->apu.new_sweep_sample_legnth = new_sweep_sample_legnth(gb);
|
gb->apu.new_sweep_sample_length = new_sweep_sample_length(gb);
|
||||||
if (gb->apu.new_sweep_sample_legnth > 0x7ff) {
|
if (gb->apu.new_sweep_sample_length > 0x7ff) {
|
||||||
gb->apu.is_active[GB_SQUARE_1] = false;
|
gb->apu.is_active[GB_SQUARE_1] = false;
|
||||||
update_sample(gb, GB_SQUARE_1, 0, gb->apu.square_sweep_calculate_countdown - cycles);
|
update_sample(gb, GB_SQUARE_1, 0, gb->apu.square_sweep_calculate_countdown - cycles);
|
||||||
gb->apu.sweep_enabled = false;
|
gb->apu.sweep_enabled = false;
|
||||||
@ -431,6 +467,9 @@ void GB_apu_run(GB_gameboy_t *gb)
|
|||||||
gb->apu.square_channels[i].sample_countdown = (gb->apu.square_channels[i].sample_length ^ 0x7FF) * 2 + 1;
|
gb->apu.square_channels[i].sample_countdown = (gb->apu.square_channels[i].sample_length ^ 0x7FF) * 2 + 1;
|
||||||
gb->apu.square_channels[i].current_sample_index++;
|
gb->apu.square_channels[i].current_sample_index++;
|
||||||
gb->apu.square_channels[i].current_sample_index &= 0x7;
|
gb->apu.square_channels[i].current_sample_index &= 0x7;
|
||||||
|
if (cycles_left == 0 && gb->apu.samples[i] == 0) {
|
||||||
|
gb->apu.pcm_mask[0] &= i == GB_SQUARE_1? 0xF0 : 0x0F;
|
||||||
|
}
|
||||||
|
|
||||||
update_square_sample(gb, i);
|
update_square_sample(gb, i);
|
||||||
}
|
}
|
||||||
@ -469,7 +508,6 @@ void GB_apu_run(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
/* Step LFSR */
|
/* Step LFSR */
|
||||||
unsigned high_bit_mask = gb->apu.noise_channel.narrow ? 0x4040 : 0x4000;
|
unsigned high_bit_mask = gb->apu.noise_channel.narrow ? 0x4040 : 0x4000;
|
||||||
/* Todo: is this formula is different on a GBA? */
|
|
||||||
bool new_high_bit = (gb->apu.noise_channel.lfsr ^ (gb->apu.noise_channel.lfsr >> 1) ^ 1) & 1;
|
bool new_high_bit = (gb->apu.noise_channel.lfsr ^ (gb->apu.noise_channel.lfsr >> 1) ^ 1) & 1;
|
||||||
gb->apu.noise_channel.lfsr >>= 1;
|
gb->apu.noise_channel.lfsr >>= 1;
|
||||||
|
|
||||||
@ -483,6 +521,10 @@ void GB_apu_run(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
gb->apu.current_lfsr_sample = gb->apu.noise_channel.lfsr & 1;
|
gb->apu.current_lfsr_sample = gb->apu.noise_channel.lfsr & 1;
|
||||||
|
|
||||||
|
if (cycles_left == 0 && gb->apu.samples[GB_NOISE] == 0) {
|
||||||
|
gb->apu.pcm_mask[1] &= 0x0F;
|
||||||
|
}
|
||||||
|
|
||||||
update_sample(gb, GB_NOISE,
|
update_sample(gb, GB_NOISE,
|
||||||
gb->apu.current_lfsr_sample ?
|
gb->apu.current_lfsr_sample ?
|
||||||
gb->apu.noise_channel.current_volume : 0,
|
gb->apu.noise_channel.current_volume : 0,
|
||||||
@ -506,11 +548,17 @@ void GB_apu_run(GB_gameboy_t *gb)
|
|||||||
void GB_apu_init(GB_gameboy_t *gb)
|
void GB_apu_init(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
memset(&gb->apu, 0, sizeof(gb->apu));
|
memset(&gb->apu, 0, sizeof(gb->apu));
|
||||||
|
/* Restore the wave form */
|
||||||
|
for (unsigned reg = GB_IO_WAV_START; reg <= GB_IO_WAV_END; reg++) {
|
||||||
|
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = gb->io_registers[reg] >> 4;
|
||||||
|
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = gb->io_registers[reg] & 0xF;
|
||||||
|
}
|
||||||
gb->apu.lf_div = 1;
|
gb->apu.lf_div = 1;
|
||||||
/* APU glitch: When turning the APU on while DIV's bit 4 (or 5 in double speed mode) is on,
|
/* APU glitch: When turning the APU on while DIV's bit 4 (or 5 in double speed mode) is on,
|
||||||
the first DIV/APU event is skipped. */
|
the first DIV/APU event is skipped. */
|
||||||
if (gb->div_counter & (gb->cgb_double_speed? 0x2000 : 0x1000)) {
|
if (gb->div_counter & (gb->cgb_double_speed? 0x2000 : 0x1000)) {
|
||||||
gb->apu.skip_div_event = true;
|
gb->apu.skip_div_event = GB_SKIP_DIV_EVENT_SKIP;
|
||||||
|
gb->apu.div_divider = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,7 +566,7 @@ uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg)
|
|||||||
{
|
{
|
||||||
if (reg == GB_IO_NR52) {
|
if (reg == GB_IO_NR52) {
|
||||||
uint8_t value = 0;
|
uint8_t value = 0;
|
||||||
for (int i = 0; i < GB_N_CHANNELS; i++) {
|
for (unsigned i = 0; i < GB_N_CHANNELS; i++) {
|
||||||
value >>= 1;
|
value >>= 1;
|
||||||
if (gb->apu.is_active[i]) {
|
if (gb->apu.is_active[i]) {
|
||||||
value |= 0x8;
|
value |= 0x8;
|
||||||
@ -556,14 +604,14 @@ uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg)
|
|||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
if (!gb->apu.global_enable && reg != GB_IO_NR52 && (GB_is_cgb(gb) ||
|
if (!gb->apu.global_enable && reg != GB_IO_NR52 && reg < GB_IO_WAV_START && (GB_is_cgb(gb) ||
|
||||||
(
|
(
|
||||||
reg != GB_IO_NR11 &&
|
reg != GB_IO_NR11 &&
|
||||||
reg != GB_IO_NR21 &&
|
reg != GB_IO_NR21 &&
|
||||||
reg != GB_IO_NR31 &&
|
reg != GB_IO_NR31 &&
|
||||||
reg != GB_IO_NR41
|
reg != GB_IO_NR41
|
||||||
)
|
)
|
||||||
)) {
|
)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -676,11 +724,26 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|||||||
case GB_IO_NR14:
|
case GB_IO_NR14:
|
||||||
case GB_IO_NR24: {
|
case GB_IO_NR24: {
|
||||||
unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1;
|
unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1;
|
||||||
|
|
||||||
|
/* TODO: When the sample length changes right before being updated, the countdown should change to the
|
||||||
|
old length, but the current sample should not change. Because our write timing isn't accurate to
|
||||||
|
the T-cycle, we hack around it by stepping the sample index backwards. */
|
||||||
|
if ((value & 0x80) == 0 && gb->apu.is_active[index]) {
|
||||||
|
/* On an AGB, as well as on CGB C and earlier (TODO: Tested: 0, B and C), it behaves slightly different on
|
||||||
|
double speed. */
|
||||||
|
if (gb->model == GB_MODEL_CGB_E /* || gb->model == GB_MODEL_CGB_D */ || gb->apu.square_channels[index].sample_countdown & 1) {
|
||||||
|
if (gb->apu.square_channels[index].sample_countdown >> 1 == (gb->apu.square_channels[index].sample_length ^ 0x7FF)) {
|
||||||
|
gb->apu.square_channels[index].current_sample_index--;
|
||||||
|
gb->apu.square_channels[index].current_sample_index &= 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gb->apu.square_channels[index].sample_length &= 0xFF;
|
gb->apu.square_channels[index].sample_length &= 0xFF;
|
||||||
gb->apu.square_channels[index].sample_length |= (value & 7) << 8;
|
gb->apu.square_channels[index].sample_length |= (value & 7) << 8;
|
||||||
if (index == GB_SQUARE_1) {
|
if (index == GB_SQUARE_1) {
|
||||||
gb->apu.shadow_sweep_sample_legnth =
|
gb->apu.shadow_sweep_sample_length =
|
||||||
gb->apu.new_sweep_sample_legnth =
|
gb->apu.new_sweep_sample_length =
|
||||||
gb->apu.square_channels[0].sample_length;
|
gb->apu.square_channels[0].sample_length;
|
||||||
}
|
}
|
||||||
if (value & 0x80) {
|
if (value & 0x80) {
|
||||||
@ -862,7 +925,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|||||||
gb->apu.is_active[GB_NOISE] = false;
|
gb->apu.is_active[GB_NOISE] = false;
|
||||||
update_sample(gb, GB_NOISE, 0, 0);
|
update_sample(gb, GB_NOISE, 0, 0);
|
||||||
}
|
}
|
||||||
else if (gb->apu.is_active[GB_NOISE]){
|
else if (gb->apu.is_active[GB_NOISE]) {
|
||||||
nrx2_glitch(&gb->apu.noise_channel.current_volume, value, gb->io_registers[reg]);
|
nrx2_glitch(&gb->apu.noise_channel.current_volume, value, gb->io_registers[reg]);
|
||||||
update_sample(gb, GB_NOISE,
|
update_sample(gb, GB_NOISE,
|
||||||
gb->apu.current_lfsr_sample ?
|
gb->apu.current_lfsr_sample ?
|
||||||
@ -965,9 +1028,23 @@ void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate)
|
|||||||
if (sample_rate) {
|
if (sample_rate) {
|
||||||
gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate);
|
gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate);
|
||||||
}
|
}
|
||||||
|
gb->apu_output.rate_set_in_clocks = false;
|
||||||
GB_apu_update_cycles_per_sample(gb);
|
GB_apu_update_cycles_per_sample(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (cycles_per_sample == 0) {
|
||||||
|
GB_set_sample_rate(gb, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gb->apu_output.cycles_per_sample = cycles_per_sample;
|
||||||
|
gb->apu_output.sample_rate = GB_get_clock_rate(gb) / cycles_per_sample * 2;
|
||||||
|
gb->apu_output.highpass_rate = pow(0.999958, cycles_per_sample);
|
||||||
|
gb->apu_output.rate_set_in_clocks = true;
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
gb->apu_output.sample_callback = callback;
|
gb->apu_output.sample_callback = callback;
|
||||||
@ -980,6 +1057,7 @@ void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode)
|
|||||||
|
|
||||||
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb)
|
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
|
if (gb->apu_output.rate_set_in_clocks) return;
|
||||||
if (gb->apu_output.sample_rate) {
|
if (gb->apu_output.sample_rate) {
|
||||||
gb->apu_output.cycles_per_sample = 2 * GB_get_clock_rate(gb) / (double)gb->apu_output.sample_rate; /* 2 * because we use 8MHz units */
|
gb->apu_output.cycles_per_sample = 2 * GB_get_clock_rate(gb) / (double)gb->apu_output.sample_rate; /* 2 * because we use 8MHz units */
|
||||||
}
|
}
|
||||||
|
14
Core/apu.h
14
Core/apu.h
@ -64,8 +64,8 @@ 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 new_sweep_sample_legnth;
|
uint16_t new_sweep_sample_length;
|
||||||
uint16_t shadow_sweep_sample_legnth;
|
uint16_t shadow_sweep_sample_length;
|
||||||
bool sweep_enabled;
|
bool sweep_enabled;
|
||||||
bool sweep_decreasing;
|
bool sweep_decreasing;
|
||||||
|
|
||||||
@ -114,8 +114,12 @@ typedef struct
|
|||||||
|
|
||||||
} noise_channel;
|
} noise_channel;
|
||||||
|
|
||||||
bool skip_div_event;
|
#define GB_SKIP_DIV_EVENT_INACTIVE 0
|
||||||
|
#define GB_SKIP_DIV_EVENT_SKIPPED 1
|
||||||
|
#define GB_SKIP_DIV_EVENT_SKIP 2
|
||||||
|
uint8_t skip_div_event;
|
||||||
bool current_lfsr_sample;
|
bool current_lfsr_sample;
|
||||||
|
uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch
|
||||||
} GB_apu_t;
|
} GB_apu_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -143,9 +147,12 @@ typedef struct {
|
|||||||
GB_double_sample_t highpass_diff;
|
GB_double_sample_t highpass_diff;
|
||||||
|
|
||||||
GB_sample_callback_t sample_callback;
|
GB_sample_callback_t sample_callback;
|
||||||
|
|
||||||
|
bool rate_set_in_clocks;
|
||||||
} 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_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_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
|
||||||
@ -156,6 +163,7 @@ void GB_apu_div_event(GB_gameboy_t *gb);
|
|||||||
void GB_apu_init(GB_gameboy_t *gb);
|
void GB_apu_init(GB_gameboy_t *gb);
|
||||||
void GB_apu_run(GB_gameboy_t *gb);
|
void GB_apu_run(GB_gameboy_t *gb);
|
||||||
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb);
|
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,17 +1,17 @@
|
|||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
|
|
||||||
static int noise_seed = 0;
|
static signed noise_seed = 0;
|
||||||
|
|
||||||
/* This is not a complete emulation of the camera chip. Only the features used by the GameBoy 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)
|
||||||
{
|
{
|
||||||
int value = (x + y * 128 + noise_seed);
|
signed value = (x + y * 128 + noise_seed);
|
||||||
uint8_t *data = (uint8_t *) &value;
|
uint8_t *data = (uint8_t *) &value;
|
||||||
unsigned hash = 0;
|
unsigned hash = 0;
|
||||||
|
|
||||||
while ((int *) data != &value + 1) {
|
while ((signed *) data != &value + 1) {
|
||||||
hash ^= (*data << 8);
|
hash ^= (*data << 8);
|
||||||
if (hash & 0x8000) {
|
if (hash & 0x8000) {
|
||||||
hash ^= 0x8a00;
|
hash ^= 0x8a00;
|
||||||
|
313
Core/cheats.c
Normal file
313
Core/cheats.c
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
#include "gb.h"
|
||||||
|
#include "cheats.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
static inline uint8_t hash_addr(uint16_t addr)
|
||||||
|
{
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t bank_for_addr(GB_gameboy_t *gb, uint16_t addr)
|
||||||
|
{
|
||||||
|
if (addr < 0x4000) {
|
||||||
|
return gb->mbc_rom0_bank;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr < 0x8000) {
|
||||||
|
return gb->mbc_rom_bank;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr < 0xD000) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr < 0xE000) {
|
||||||
|
return gb->cgb_ram_bank;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_apply_cheat(GB_gameboy_t *gb, uint16_t address, uint8_t *value)
|
||||||
|
{
|
||||||
|
if (!gb->cheat_enabled) return;
|
||||||
|
if (!gb->boot_rom_finished) return;
|
||||||
|
const GB_cheat_hash_t *hash = gb->cheat_hash[hash_addr(address)];
|
||||||
|
if (hash) {
|
||||||
|
for (unsigned i = 0; i < hash->size; i++) {
|
||||||
|
GB_cheat_t *cheat = hash->cheats[i];
|
||||||
|
if (cheat->address == address && cheat->enabled && (!cheat->use_old_value || cheat->old_value == *value)) {
|
||||||
|
if (cheat->bank == GB_CHEAT_ANY_BANK || cheat->bank == bank_for_addr(gb, address)) {
|
||||||
|
*value = cheat->value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GB_cheats_enabled(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
return gb->cheat_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_set_cheats_enabled(GB_gameboy_t *gb, bool enabled)
|
||||||
|
{
|
||||||
|
gb->cheat_enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_add_cheat(GB_gameboy_t *gb, const char *description, uint16_t address, uint16_t bank, uint8_t value, uint8_t old_value, bool use_old_value, bool enabled)
|
||||||
|
{
|
||||||
|
GB_cheat_t *cheat = malloc(sizeof(*cheat));
|
||||||
|
cheat->address = address;
|
||||||
|
cheat->bank = bank;
|
||||||
|
cheat->value = value;
|
||||||
|
cheat->old_value = old_value;
|
||||||
|
cheat->use_old_value = use_old_value;
|
||||||
|
cheat->enabled = enabled;
|
||||||
|
strncpy(cheat->description, description, sizeof(cheat->description));
|
||||||
|
cheat->description[sizeof(cheat->description) - 1] = 0;
|
||||||
|
gb->cheats = realloc(gb->cheats, (++gb->cheat_count) * sizeof(*cheat));
|
||||||
|
gb->cheats[gb->cheat_count - 1] = cheat;
|
||||||
|
|
||||||
|
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(address)];
|
||||||
|
if (!*hash) {
|
||||||
|
*hash = malloc(sizeof(GB_cheat_hash_t) + sizeof(cheat));
|
||||||
|
(*hash)->size = 1;
|
||||||
|
(*hash)->cheats[0] = cheat;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(*hash)->size++;
|
||||||
|
*hash = realloc(*hash, sizeof(GB_cheat_hash_t) + sizeof(cheat) * (*hash)->size);
|
||||||
|
(*hash)->cheats[(*hash)->size - 1] = cheat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GB_cheat_t *const *GB_get_cheats(GB_gameboy_t *gb, size_t *size)
|
||||||
|
{
|
||||||
|
*size = gb->cheat_count;
|
||||||
|
return (void *)gb->cheats;
|
||||||
|
}
|
||||||
|
void GB_remove_cheat(GB_gameboy_t *gb, const GB_cheat_t *cheat)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < gb->cheat_count; i++) {
|
||||||
|
if (gb->cheats[i] == cheat) {
|
||||||
|
gb->cheats[i] = gb->cheats[--gb->cheat_count];
|
||||||
|
if (gb->cheat_count == 0) {
|
||||||
|
free(gb->cheats);
|
||||||
|
gb->cheats = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->cheats = realloc(gb->cheats, gb->cheat_count * sizeof(*cheat));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(cheat->address)];
|
||||||
|
for (unsigned i = 0; i < (*hash)->size; i++) {
|
||||||
|
if ((*hash)->cheats[i] == cheat) {
|
||||||
|
(*hash)->cheats[i] = (*hash)->cheats[(*hash)->size--];
|
||||||
|
if ((*hash)->size == 0) {
|
||||||
|
free(*hash);
|
||||||
|
*hash = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*hash = malloc(sizeof(GB_cheat_hash_t) + sizeof(cheat) * (*hash)->size);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free((void *)cheat);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GB_import_cheat(GB_gameboy_t *gb, const char *cheat, const char *description, bool enabled)
|
||||||
|
{
|
||||||
|
uint8_t dummy;
|
||||||
|
/* GameShark */
|
||||||
|
{
|
||||||
|
uint8_t bank;
|
||||||
|
uint8_t value;
|
||||||
|
uint16_t address;
|
||||||
|
if (sscanf(cheat, "%02hhx%02hhx%04hx%c", &bank, &value, &address, &dummy) == 3) {
|
||||||
|
if (bank >= 0x80) {
|
||||||
|
bank &= 0xF;
|
||||||
|
}
|
||||||
|
GB_add_cheat(gb, description, address, bank, value, 0, false, enabled);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GameGenie */
|
||||||
|
{
|
||||||
|
char stripped_cheat[10] = {0,};
|
||||||
|
for (unsigned i = 0; i < 9 && *cheat; i++) {
|
||||||
|
stripped_cheat[i] = *(cheat++);
|
||||||
|
while (*cheat == '-') {
|
||||||
|
cheat++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the 7th character;
|
||||||
|
stripped_cheat[7] = stripped_cheat[8];
|
||||||
|
stripped_cheat[8] = 0;
|
||||||
|
|
||||||
|
uint8_t old_value;
|
||||||
|
uint8_t value;
|
||||||
|
uint16_t address;
|
||||||
|
if (sscanf(stripped_cheat, "%02hhx%04hx%02hhx%c", &value, &address, &old_value, &dummy) == 3) {
|
||||||
|
address = (uint16_t)(address >> 4) | (uint16_t)(address << 12);
|
||||||
|
address ^= 0xF000;
|
||||||
|
if (address > 0x7FFF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
old_value = (uint8_t)(old_value >> 2) | (uint8_t)(old_value << 6);
|
||||||
|
old_value ^= 0xBA;
|
||||||
|
GB_add_cheat(gb, description, address, GB_CHEAT_ANY_BANK, value, old_value, true, enabled);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sscanf(stripped_cheat, "%02hhx%04hx%c", &value, &address, &dummy) == 2) {
|
||||||
|
address = (uint16_t)(address >> 4) | (uint16_t)(address << 12);
|
||||||
|
address ^= 0xF000;
|
||||||
|
if (address > 0x7FFF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GB_add_cheat(gb, description, address, GB_CHEAT_ANY_BANK, value, false, true, enabled);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_update_cheat(GB_gameboy_t *gb, const GB_cheat_t *_cheat, const char *description, uint16_t address, uint16_t bank, uint8_t value, uint8_t old_value, bool use_old_value, bool enabled)
|
||||||
|
{
|
||||||
|
GB_cheat_t *cheat = NULL;
|
||||||
|
for (unsigned i = 0; i < gb->cheat_count; i++) {
|
||||||
|
if (gb->cheats[i] == _cheat) {
|
||||||
|
cheat = gb->cheats[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(cheat);
|
||||||
|
|
||||||
|
if (cheat->address != address) {
|
||||||
|
/* Remove from old bucket */
|
||||||
|
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(cheat->address)];
|
||||||
|
for (unsigned i = 0; i < (*hash)->size; i++) {
|
||||||
|
if ((*hash)->cheats[i] == cheat) {
|
||||||
|
(*hash)->cheats[i] = (*hash)->cheats[(*hash)->size--];
|
||||||
|
if ((*hash)->size == 0) {
|
||||||
|
free(*hash);
|
||||||
|
*hash = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*hash = malloc(sizeof(GB_cheat_hash_t) + sizeof(cheat) * (*hash)->size);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cheat->address = address;
|
||||||
|
|
||||||
|
/* Add to new bucket */
|
||||||
|
hash = &gb->cheat_hash[hash_addr(address)];
|
||||||
|
if (!*hash) {
|
||||||
|
*hash = malloc(sizeof(GB_cheat_hash_t) + sizeof(cheat));
|
||||||
|
(*hash)->size = 1;
|
||||||
|
(*hash)->cheats[0] = cheat;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(*hash)->size++;
|
||||||
|
*hash = malloc(sizeof(GB_cheat_hash_t) + sizeof(cheat) * (*hash)->size);
|
||||||
|
(*hash)->cheats[(*hash)->size - 1] = cheat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cheat->bank = bank;
|
||||||
|
cheat->value = value;
|
||||||
|
cheat->old_value = old_value;
|
||||||
|
cheat->use_old_value = use_old_value;
|
||||||
|
cheat->enabled = enabled;
|
||||||
|
if (description != cheat->description) {
|
||||||
|
strncpy(cheat->description, description, sizeof(cheat->description));
|
||||||
|
cheat->description[sizeof(cheat->description) - 1] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHEAT_MAGIC 'SBCh'
|
||||||
|
|
||||||
|
void GB_load_cheats(GB_gameboy_t *gb, const char *path)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path, "rb");
|
||||||
|
if (!f) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t magic = 0;
|
||||||
|
uint32_t struct_size = 0;
|
||||||
|
fread(&magic, sizeof(magic), 1, f);
|
||||||
|
fread(&struct_size, sizeof(struct_size), 1, f);
|
||||||
|
if (magic != CHEAT_MAGIC && magic != __builtin_bswap32(CHEAT_MAGIC)) {
|
||||||
|
GB_log(gb, "The file is not a SameBoy cheat database");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (struct_size != sizeof(GB_cheat_t)) {
|
||||||
|
GB_log(gb, "This cheat database is not compatible with this version of SameBoy");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all cheats first
|
||||||
|
while (gb->cheats) {
|
||||||
|
GB_remove_cheat(gb, gb->cheats[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_cheat_t cheat;
|
||||||
|
while (fread(&cheat, sizeof(cheat), 1, f)) {
|
||||||
|
if (magic == __builtin_bswap32(CHEAT_MAGIC)) {
|
||||||
|
cheat.address = __builtin_bswap16(cheat.address);
|
||||||
|
cheat.bank = __builtin_bswap16(cheat.bank);
|
||||||
|
}
|
||||||
|
cheat.description[sizeof(cheat.description) - 1] = 0;
|
||||||
|
GB_add_cheat(gb, cheat.description, cheat.address, cheat.bank, cheat.value, cheat.old_value, cheat.use_old_value, cheat.enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GB_save_cheats(GB_gameboy_t *gb, const char *path)
|
||||||
|
{
|
||||||
|
if (!gb->cheat_count) return 0; // Nothing to save.
|
||||||
|
FILE *f = fopen(path, "wb");
|
||||||
|
if (!f) {
|
||||||
|
GB_log(gb, "Could not dump cheat database: %s.\n", strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t magic = CHEAT_MAGIC;
|
||||||
|
uint32_t struct_size = sizeof(GB_cheat_t);
|
||||||
|
|
||||||
|
if (fwrite(&magic, sizeof(magic), 1, f) != 1) {
|
||||||
|
fclose(f);
|
||||||
|
return EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(&struct_size, sizeof(struct_size), 1, f) != 1) {
|
||||||
|
fclose(f);
|
||||||
|
return EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i <gb->cheat_count; i++) {
|
||||||
|
if (fwrite(gb->cheats[i], sizeof(*gb->cheats[i]), 1, f) != 1) {
|
||||||
|
fclose(f);
|
||||||
|
return EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
fclose(f);
|
||||||
|
return errno;
|
||||||
|
}
|
42
Core/cheats.h
Normal file
42
Core/cheats.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#ifndef cheats_h
|
||||||
|
#define cheats_h
|
||||||
|
#include "gb_struct_def.h"
|
||||||
|
|
||||||
|
#define GB_CHEAT_ANY_BANK 0xFFFF
|
||||||
|
|
||||||
|
typedef struct GB_cheat_s GB_cheat_t;
|
||||||
|
|
||||||
|
void GB_add_cheat(GB_gameboy_t *gb, const char *description, uint16_t address, uint16_t bank, uint8_t value, uint8_t old_value, bool use_old_value, bool enabled);
|
||||||
|
void GB_update_cheat(GB_gameboy_t *gb, const GB_cheat_t *cheat, const char *description, uint16_t address, uint16_t bank, uint8_t value, uint8_t old_value, bool use_old_value, bool enabled);
|
||||||
|
bool GB_import_cheat(GB_gameboy_t *gb, const char *cheat, const char *description, bool enabled);
|
||||||
|
const GB_cheat_t *const *GB_get_cheats(GB_gameboy_t *gb, size_t *size);
|
||||||
|
void GB_remove_cheat(GB_gameboy_t *gb, const GB_cheat_t *cheat);
|
||||||
|
bool GB_cheats_enabled(GB_gameboy_t *gb);
|
||||||
|
void GB_set_cheats_enabled(GB_gameboy_t *gb, bool enabled);
|
||||||
|
void GB_load_cheats(GB_gameboy_t *gb, const char *path);
|
||||||
|
int GB_save_cheats(GB_gameboy_t *gb, const char *path);
|
||||||
|
|
||||||
|
#ifdef GB_INTERNAL
|
||||||
|
#ifdef GB_DISABLE_CHEATS
|
||||||
|
#define GB_apply_cheat(...)
|
||||||
|
#else
|
||||||
|
void GB_apply_cheat(GB_gameboy_t *gb, uint16_t address, uint8_t *value);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
GB_cheat_t *cheats[];
|
||||||
|
} GB_cheat_hash_t;
|
||||||
|
|
||||||
|
struct GB_cheat_s {
|
||||||
|
uint16_t address;
|
||||||
|
uint16_t bank;
|
||||||
|
uint8_t value;
|
||||||
|
uint8_t old_value;
|
||||||
|
bool use_old_value;
|
||||||
|
bool enabled;
|
||||||
|
char description[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
195
Core/debugger.c
195
Core/debugger.c
@ -220,7 +220,8 @@ static value_t read_lvalue(GB_gameboy_t *gb, lvalue_t lvalue)
|
|||||||
banking_state_t state;
|
banking_state_t state;
|
||||||
save_banking_state(gb, &state);
|
save_banking_state(gb, &state);
|
||||||
switch_banking_state(gb, lvalue.memory_address.bank);
|
switch_banking_state(gb, lvalue.memory_address.bank);
|
||||||
value_t r = VALUE_16(GB_read_memory(gb, lvalue.memory_address.value));
|
value_t r = VALUE_16(GB_read_memory(gb, lvalue.memory_address.value) |
|
||||||
|
(GB_read_memory(gb, lvalue.memory_address.value + 1) * 0x100));
|
||||||
restore_banking_state(gb, &state);
|
restore_banking_state(gb, &state);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -261,6 +262,7 @@ static void write_lvalue(GB_gameboy_t *gb, lvalue_t lvalue, uint16_t value)
|
|||||||
save_banking_state(gb, &state);
|
save_banking_state(gb, &state);
|
||||||
switch_banking_state(gb, lvalue.memory_address.bank);
|
switch_banking_state(gb, lvalue.memory_address.bank);
|
||||||
GB_write_memory(gb, lvalue.memory_address.value, value);
|
GB_write_memory(gb, lvalue.memory_address.value, value);
|
||||||
|
GB_write_memory(gb, lvalue.memory_address.value + 1, value >> 8);
|
||||||
restore_banking_state(gb, &state);
|
restore_banking_state(gb, &state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -296,13 +298,15 @@ static void write_lvalue(GB_gameboy_t *gb, lvalue_t lvalue, uint16_t value)
|
|||||||
static value_t add(value_t a, value_t b) {return FIX_BANK(a.value + b.value);}
|
static value_t add(value_t a, value_t b) {return FIX_BANK(a.value + b.value);}
|
||||||
static value_t sub(value_t a, value_t b) {return FIX_BANK(a.value - b.value);}
|
static value_t sub(value_t a, value_t b) {return FIX_BANK(a.value - b.value);}
|
||||||
static value_t mul(value_t a, value_t b) {return FIX_BANK(a.value * b.value);}
|
static value_t mul(value_t a, value_t b) {return FIX_BANK(a.value * b.value);}
|
||||||
static value_t _div(value_t a, value_t b) {
|
static value_t _div(value_t a, value_t b)
|
||||||
|
{
|
||||||
if (b.value == 0) {
|
if (b.value == 0) {
|
||||||
return FIX_BANK(0);
|
return FIX_BANK(0);
|
||||||
}
|
}
|
||||||
return FIX_BANK(a.value / b.value);
|
return FIX_BANK(a.value / b.value);
|
||||||
};
|
};
|
||||||
static value_t mod(value_t a, value_t b) {
|
static value_t mod(value_t a, value_t b)
|
||||||
|
{
|
||||||
if (b.value == 0) {
|
if (b.value == 0) {
|
||||||
return FIX_BANK(0);
|
return FIX_BANK(0);
|
||||||
}
|
}
|
||||||
@ -332,7 +336,7 @@ static value_t bank(value_t a, value_t b) {return (value_t) {true, a.value, b.va
|
|||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
const char *string;
|
const char *string;
|
||||||
char priority;
|
int8_t priority;
|
||||||
value_t (*operator)(value_t, value_t);
|
value_t (*operator)(value_t, value_t);
|
||||||
value_t (*lvalue_operator)(GB_gameboy_t *, lvalue_t, uint16_t);
|
value_t (*lvalue_operator)(GB_gameboy_t *, lvalue_t, uint16_t);
|
||||||
} operators[] =
|
} operators[] =
|
||||||
@ -350,15 +354,15 @@ static struct {
|
|||||||
{"&", 1, and},
|
{"&", 1, and},
|
||||||
{"^", 1, xor},
|
{"^", 1, xor},
|
||||||
{"<<", 2, shleft},
|
{"<<", 2, shleft},
|
||||||
{"<=", -1, lower_equals},
|
{"<=", 3, lower_equals},
|
||||||
{"<", -1, lower},
|
{"<", 3, lower},
|
||||||
{">>", 2, shright},
|
{">>", 2, shright},
|
||||||
{">=", -1, greater_equals},
|
{">=", 3, greater_equals},
|
||||||
{">", -1, greater},
|
{">", 3, greater},
|
||||||
{"==", -1, equals},
|
{"==", 3, equals},
|
||||||
{"=", -2, NULL, assign},
|
{"=", -1, NULL, assign},
|
||||||
{"!=", -1, different},
|
{"!=", 3, different},
|
||||||
{":", 3, bank},
|
{":", 4, bank},
|
||||||
};
|
};
|
||||||
|
|
||||||
value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
||||||
@ -378,16 +382,15 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
|
|||||||
while (length && (string[length-1] == ' ' || string[length-1] == '\n' || string[length-1] == '\r' || string[length-1] == '\t')) {
|
while (length && (string[length-1] == ' ' || string[length-1] == '\n' || string[length-1] == '\r' || string[length-1] == '\t')) {
|
||||||
length--;
|
length--;
|
||||||
}
|
}
|
||||||
if (length == 0)
|
if (length == 0) {
|
||||||
{
|
|
||||||
GB_log(gb, "Expected expression.\n");
|
GB_log(gb, "Expected expression.\n");
|
||||||
*error = true;
|
*error = true;
|
||||||
return (lvalue_t){0,};
|
return (lvalue_t){0,};
|
||||||
}
|
}
|
||||||
if (string[0] == '(' && string[length - 1] == ')') {
|
if (string[0] == '(' && string[length - 1] == ')') {
|
||||||
// Attempt to strip parentheses
|
// Attempt to strip parentheses
|
||||||
signed int depth = 0;
|
signed depth = 0;
|
||||||
for (int i = 0; i < length; i++) {
|
for (unsigned i = 0; i < length; i++) {
|
||||||
if (string[i] == '(') depth++;
|
if (string[i] == '(') depth++;
|
||||||
if (depth == 0) {
|
if (depth == 0) {
|
||||||
// First and last are not matching
|
// First and last are not matching
|
||||||
@ -400,8 +403,8 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
|
|||||||
}
|
}
|
||||||
else if (string[0] == '[' && string[length - 1] == ']') {
|
else if (string[0] == '[' && string[length - 1] == ']') {
|
||||||
// Attempt to strip square parentheses (memory dereference)
|
// Attempt to strip square parentheses (memory dereference)
|
||||||
signed int depth = 0;
|
signed depth = 0;
|
||||||
for (int i = 0; i < length; i++) {
|
for (unsigned i = 0; i < length; i++) {
|
||||||
if (string[i] == '[') depth++;
|
if (string[i] == '[') depth++;
|
||||||
if (depth == 0) {
|
if (depth == 0) {
|
||||||
// First and last are not matching
|
// First and last are not matching
|
||||||
@ -416,8 +419,8 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
|
|||||||
}
|
}
|
||||||
else if (string[0] == '{' && string[length - 1] == '}') {
|
else if (string[0] == '{' && string[length - 1] == '}') {
|
||||||
// Attempt to strip curly parentheses (memory dereference)
|
// Attempt to strip curly parentheses (memory dereference)
|
||||||
signed int depth = 0;
|
signed depth = 0;
|
||||||
for (int i = 0; i < length; i++) {
|
for (unsigned i = 0; i < length; i++) {
|
||||||
if (string[i] == '{') depth++;
|
if (string[i] == '{') depth++;
|
||||||
if (depth == 0) {
|
if (depth == 0) {
|
||||||
// First and last are not matching
|
// First and last are not matching
|
||||||
@ -485,16 +488,15 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
|||||||
while (length && (string[length-1] == ' ' || string[length-1] == '\n' || string[length-1] == '\r' || string[length-1] == '\t')) {
|
while (length && (string[length-1] == ' ' || string[length-1] == '\n' || string[length-1] == '\r' || string[length-1] == '\t')) {
|
||||||
length--;
|
length--;
|
||||||
}
|
}
|
||||||
if (length == 0)
|
if (length == 0) {
|
||||||
{
|
|
||||||
GB_log(gb, "Expected expression.\n");
|
GB_log(gb, "Expected expression.\n");
|
||||||
*error = true;
|
*error = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (string[0] == '(' && string[length - 1] == ')') {
|
if (string[0] == '(' && string[length - 1] == ')') {
|
||||||
// Attempt to strip parentheses
|
// Attempt to strip parentheses
|
||||||
signed int depth = 0;
|
signed depth = 0;
|
||||||
for (int i = 0; i < length; i++) {
|
for (unsigned i = 0; i < length; i++) {
|
||||||
if (string[i] == '(') depth++;
|
if (string[i] == '(') depth++;
|
||||||
if (depth == 0) {
|
if (depth == 0) {
|
||||||
// First and last are not matching
|
// First and last are not matching
|
||||||
@ -510,8 +512,8 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
|||||||
}
|
}
|
||||||
else if (string[0] == '[' && string[length - 1] == ']') {
|
else if (string[0] == '[' && string[length - 1] == ']') {
|
||||||
// Attempt to strip square parentheses (memory dereference)
|
// Attempt to strip square parentheses (memory dereference)
|
||||||
signed int depth = 0;
|
signed depth = 0;
|
||||||
for (int i = 0; i < length; i++) {
|
for (unsigned i = 0; i < length; i++) {
|
||||||
if (string[i] == '[') depth++;
|
if (string[i] == '[') depth++;
|
||||||
if (depth == 0) {
|
if (depth == 0) {
|
||||||
// First and last are not matching
|
// First and last are not matching
|
||||||
@ -537,8 +539,8 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
|||||||
}
|
}
|
||||||
else if (string[0] == '{' && string[length - 1] == '}') {
|
else if (string[0] == '{' && string[length - 1] == '}') {
|
||||||
// Attempt to strip curly parentheses (memory dereference)
|
// Attempt to strip curly parentheses (memory dereference)
|
||||||
signed int depth = 0;
|
signed depth = 0;
|
||||||
for (int i = 0; i < length; i++) {
|
for (unsigned i = 0; i < length; i++) {
|
||||||
if (string[i] == '{') depth++;
|
if (string[i] == '{') depth++;
|
||||||
if (depth == 0) {
|
if (depth == 0) {
|
||||||
// First and last are not matching
|
// First and last are not matching
|
||||||
@ -563,24 +565,24 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Search for lowest priority operator
|
// Search for lowest priority operator
|
||||||
signed int depth = 0;
|
signed depth = 0;
|
||||||
unsigned operator_index = -1;
|
unsigned operator_index = -1;
|
||||||
unsigned operator_pos = 0;
|
unsigned operator_pos = 0;
|
||||||
for (int i = 0; i < length; i++) {
|
for (unsigned i = 0; i < length; i++) {
|
||||||
if (string[i] == '(') depth++;
|
if (string[i] == '(') depth++;
|
||||||
else if (string[i] == ')') depth--;
|
else if (string[i] == ')') depth--;
|
||||||
else if (string[i] == '[') depth++;
|
else if (string[i] == '[') depth++;
|
||||||
else if (string[i] == ']') depth--;
|
else if (string[i] == ']') depth--;
|
||||||
else if (depth == 0) {
|
else if (depth == 0) {
|
||||||
for (int j = 0; j < sizeof(operators) / sizeof(operators[0]); j++) {
|
for (unsigned j = 0; j < sizeof(operators) / sizeof(operators[0]); j++) {
|
||||||
if (strlen(operators[j].string) > length - i) continue; // Operator too big.
|
unsigned operator_length = strlen(operators[j].string);
|
||||||
// Priority higher than what we already have.
|
if (operator_length > length - i) continue; // Operator too long
|
||||||
unsigned long operator_length = strlen(operators[j].string);
|
|
||||||
if (memcmp(string + i, operators[j].string, operator_length) == 0) {
|
if (memcmp(string + i, operators[j].string, operator_length) == 0) {
|
||||||
if (operator_index != -1 && operators[operator_index].priority < operators[j].priority) {
|
if (operator_index != -1 && operators[operator_index].priority < operators[j].priority) {
|
||||||
/* for supporting = vs ==, etc*/
|
/* for supporting = vs ==, etc*/
|
||||||
i += operator_length - 1;
|
i += operator_length - 1;
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
// Found an operator!
|
// Found an operator!
|
||||||
operator_pos = i;
|
operator_pos = i;
|
||||||
@ -667,7 +669,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *end;
|
char *end;
|
||||||
int base = 10;
|
unsigned base = 10;
|
||||||
if (string[0] == '$') {
|
if (string[0] == '$') {
|
||||||
string++;
|
string++;
|
||||||
base = 16;
|
base = 16;
|
||||||
@ -816,16 +818,34 @@ static bool registers(GB_gameboy_t *gb, char *arguments, char *modifiers, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GB_log(gb, "AF = $%04x (%c%c%c%c)\n", gb->registers[GB_REGISTER_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_SUBSTRACT_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->registers[GB_REGISTER_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->registers[GB_REGISTER_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->registers[GB_REGISTER_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->registers[GB_REGISTER_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");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -838,8 +858,8 @@ static uint16_t find_breakpoint(GB_gameboy_t *gb, value_t addr)
|
|||||||
|
|
||||||
uint32_t key = BP_KEY(addr);
|
uint32_t key = BP_KEY(addr);
|
||||||
|
|
||||||
int min = 0;
|
unsigned min = 0;
|
||||||
int max = gb->n_breakpoints;
|
unsigned max = gb->n_breakpoints;
|
||||||
while (min < max) {
|
while (min < max) {
|
||||||
uint16_t pivot = (min + max) / 2;
|
uint16_t pivot = (min + max) / 2;
|
||||||
if (gb->breakpoints[pivot].key == key) return pivot;
|
if (gb->breakpoints[pivot].key == key) return pivot;
|
||||||
@ -1005,8 +1025,8 @@ static uint16_t find_watchpoint(GB_gameboy_t *gb, value_t addr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
uint32_t key = WP_KEY(addr);
|
uint32_t key = WP_KEY(addr);
|
||||||
int min = 0;
|
unsigned min = 0;
|
||||||
int max = gb->n_watchpoints;
|
unsigned max = gb->n_watchpoints;
|
||||||
while (min < max) {
|
while (min < max) {
|
||||||
uint16_t pivot = (min + max) / 2;
|
uint16_t pivot = (min + max) / 2;
|
||||||
if (gb->watchpoints[pivot].key == key) return pivot;
|
if (gb->watchpoints[pivot].key == key) return pivot;
|
||||||
@ -1164,7 +1184,7 @@ static bool unwatch(GB_gameboy_t *gb, char *arguments, char *modifiers, const de
|
|||||||
|
|
||||||
memmove(&gb->watchpoints[index], &gb->watchpoints[index + 1], (gb->n_watchpoints - index - 1) * sizeof(gb->watchpoints[0]));
|
memmove(&gb->watchpoints[index], &gb->watchpoints[index + 1], (gb->n_watchpoints - index - 1) * sizeof(gb->watchpoints[0]));
|
||||||
gb->n_watchpoints--;
|
gb->n_watchpoints--;
|
||||||
gb->watchpoints = realloc(gb->watchpoints, gb->n_watchpoints* sizeof(gb->watchpoints[0]));
|
gb->watchpoints = realloc(gb->watchpoints, gb->n_watchpoints *sizeof(gb->watchpoints[0]));
|
||||||
|
|
||||||
GB_log(gb, "Watchpoint removed from %s\n", debugger_value_to_string(gb, result, true));
|
GB_log(gb, "Watchpoint removed from %s\n", debugger_value_to_string(gb, result, true));
|
||||||
return true;
|
return true;
|
||||||
@ -1213,7 +1233,7 @@ static bool list(GB_gameboy_t *gb, char *arguments, char *modifiers, const debug
|
|||||||
gb->watchpoints[i].condition);
|
gb->watchpoints[i].condition);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GB_log(gb, " %d. %s (%c%c)\n", i + 1, debugger_value_to_string(gb,addr, addr.has_bank),
|
GB_log(gb, " %d. %s (%c%c)\n", i + 1, debugger_value_to_string(gb, addr, addr.has_bank),
|
||||||
(gb->watchpoints[i].flags & GB_WATCHPOINT_R)? 'r' : '-',
|
(gb->watchpoints[i].flags & GB_WATCHPOINT_R)? 'r' : '-',
|
||||||
(gb->watchpoints[i].flags & GB_WATCHPOINT_W)? 'w' : '-');
|
(gb->watchpoints[i].flags & GB_WATCHPOINT_W)? 'w' : '-');
|
||||||
}
|
}
|
||||||
@ -1339,7 +1359,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 (int i = 0; i < 16 && count; i++) {
|
for (unsigned i = 0; i < 16 && count; i++) {
|
||||||
GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i));
|
GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i));
|
||||||
count--;
|
count--;
|
||||||
}
|
}
|
||||||
@ -1352,7 +1372,7 @@ static bool examine(GB_gameboy_t *gb, char *arguments, char *modifiers, const de
|
|||||||
else {
|
else {
|
||||||
while (count) {
|
while (count) {
|
||||||
GB_log(gb, "%04x: ", addr.value);
|
GB_log(gb, "%04x: ", addr.value);
|
||||||
for (int i = 0; i < 16 && count; i++) {
|
for (unsigned i = 0; i < 16 && count; i++) {
|
||||||
GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i));
|
GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i));
|
||||||
count--;
|
count--;
|
||||||
}
|
}
|
||||||
@ -1419,19 +1439,26 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cartridge->mbc_type) {
|
if (cartridge->mbc_type) {
|
||||||
static const char * const mapper_names[] = {
|
if (gb->is_mbc30) {
|
||||||
[GB_MBC1] = "MBC1",
|
GB_log(gb, "MBC30\n");
|
||||||
[GB_MBC2] = "MBC2",
|
}
|
||||||
[GB_MBC3] = "MBC3",
|
else {
|
||||||
[GB_MBC5] = "MBC5",
|
static const char *const mapper_names[] = {
|
||||||
[GB_HUC1] = "HUC1",
|
[GB_MBC1] = "MBC1",
|
||||||
[GB_HUC3] = "HUC3",
|
[GB_MBC2] = "MBC2",
|
||||||
};
|
[GB_MBC3] = "MBC3",
|
||||||
GB_log(gb, "%s\n", mapper_names[cartridge->mbc_type]);
|
[GB_MBC5] = "MBC5",
|
||||||
|
[GB_HUC1] = "HUC-1",
|
||||||
|
[GB_HUC3] = "HUC-3",
|
||||||
|
};
|
||||||
|
GB_log(gb, "%s\n", mapper_names[cartridge->mbc_type]);
|
||||||
|
}
|
||||||
GB_log(gb, "Current mapped ROM bank: %x\n", gb->mbc_rom_bank);
|
GB_log(gb, "Current mapped ROM bank: %x\n", gb->mbc_rom_bank);
|
||||||
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);
|
||||||
GB_log(gb, "RAM is curently %s\n", gb->mbc_ram_enable? "enabled" : "disabled");
|
if (gb->cartridge_type->mbc_type != GB_HUC1) {
|
||||||
|
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) {
|
||||||
GB_log(gb, "MBC1 banking mode is %s\n", gb->mbc1.mode == 1 ? "RAM" : "ROM");
|
GB_log(gb, "MBC1 banking mode is %s\n", gb->mbc1.mode == 1 ? "RAM" : "ROM");
|
||||||
@ -1448,7 +1475,7 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cartridge->has_rumble) {
|
if (cartridge->has_rumble) {
|
||||||
GB_log(gb, "Cart contains a rumble pak\n");
|
GB_log(gb, "Cart contains a Rumble Pak\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cartridge->has_rtc) {
|
if (cartridge->has_rtc) {
|
||||||
@ -1485,7 +1512,7 @@ static bool ticks(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_log(gb, "Ticks: %lu. (Resetting)\n", gb->debugger_ticks);
|
GB_log(gb, "Ticks: %llu. (Resetting)\n", (unsigned long long)gb->debugger_ticks);
|
||||||
gb->debugger_ticks = 0;
|
gb->debugger_ticks = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1573,7 +1600,7 @@ static bool lcd(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
|||||||
}
|
}
|
||||||
GB_log(gb, "LY: %d\n", gb->io_registers[GB_IO_LY]);
|
GB_log(gb, "LY: %d\n", gb->io_registers[GB_IO_LY]);
|
||||||
GB_log(gb, "LYC: %d\n", gb->io_registers[GB_IO_LYC]);
|
GB_log(gb, "LYC: %d\n", gb->io_registers[GB_IO_LYC]);
|
||||||
GB_log(gb, "Window position: %d, %d\n", (signed) gb->io_registers[GB_IO_WX] - 7 , gb->io_registers[GB_IO_WY]);
|
GB_log(gb, "Window position: %d, %d\n", (signed) gb->io_registers[GB_IO_WX] - 7, gb->io_registers[GB_IO_WY]);
|
||||||
GB_log(gb, "Interrupt line: %s\n", gb->stat_interrupt_line? "On" : "Off");
|
GB_log(gb, "Interrupt line: %s\n", gb->stat_interrupt_line? "On" : "Off");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1722,7 +1749,7 @@ static bool wave(GB_gameboy_t *gb, char *arguments, char *modifiers, const debug
|
|||||||
|
|
||||||
uint8_t shift_amount = 1, mask;
|
uint8_t shift_amount = 1, mask;
|
||||||
if (modifiers) {
|
if (modifiers) {
|
||||||
switch(modifiers[0]) {
|
switch (modifiers[0]) {
|
||||||
case 'c':
|
case 'c':
|
||||||
shift_amount = 2;
|
shift_amount = 2;
|
||||||
break;
|
break;
|
||||||
@ -1758,10 +1785,11 @@ 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"},
|
||||||
{"backtrace", 2, backtrace, "Display 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 (Experimental)"},
|
{"sld", 3, stack_leak_detection, "Like finish, but stops if a stack leak is detected (Experimental)"},
|
||||||
{"ticks", 2, ticks, "Display the number of CPU ticks since the last time 'ticks' was used"},
|
{"ticks", 2, ticks, "Displays the number of CPU ticks since the last time 'ticks' was" HELP_NEWLINE
|
||||||
|
"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"},
|
||||||
{"cartridge", 2, mbc, "Displays information about the MBC and cartridge"},
|
{"cartridge", 2, mbc, "Displays information about the MBC and cartridge"},
|
||||||
{"mbc", 3, }, /* Alias */
|
{"mbc", 3, }, /* Alias */
|
||||||
@ -1771,6 +1799,7 @@ static const debugger_command_t commands[] = {
|
|||||||
"a more (c)ompact one, or a one-(l)iner", "", "(f|c|l)"},
|
"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)"},
|
||||||
{"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
|
||||||
@ -1817,7 +1846,7 @@ static const debugger_command_t *find_command(const char *string)
|
|||||||
static void print_command_shortcut(GB_gameboy_t *gb, const debugger_command_t *command)
|
static void print_command_shortcut(GB_gameboy_t *gb, const debugger_command_t *command)
|
||||||
{
|
{
|
||||||
GB_attributed_log(gb, GB_LOG_BOLD | GB_LOG_UNDERLINE, "%.*s", command->min_length, command->command);
|
GB_attributed_log(gb, GB_LOG_BOLD | GB_LOG_UNDERLINE, "%.*s", command->min_length, command->command);
|
||||||
GB_attributed_log(gb, GB_LOG_BOLD , "%s", command->command + command->min_length);
|
GB_attributed_log(gb, GB_LOG_BOLD, "%s", command->command + command->min_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_command_description(GB_gameboy_t *gb, const debugger_command_t *command)
|
static void print_command_description(GB_gameboy_t *gb, const debugger_command_t *command)
|
||||||
@ -2059,7 +2088,7 @@ void GB_debugger_run(GB_gameboy_t *gb)
|
|||||||
if (gb->debug_disable) return;
|
if (gb->debug_disable) return;
|
||||||
|
|
||||||
char *input = NULL;
|
char *input = NULL;
|
||||||
if (gb->debug_next_command && gb->debug_call_depth <= 0) {
|
if (gb->debug_next_command && gb->debug_call_depth <= 0 && !gb->halted) {
|
||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
}
|
}
|
||||||
if (gb->debug_fin_command && gb->debug_call_depth == -1) {
|
if (gb->debug_fin_command && gb->debug_call_depth == -1) {
|
||||||
@ -2151,6 +2180,19 @@ void GB_debugger_handle_async_commands(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GB_debugger_add_symbol(GB_gameboy_t *gb, uint16_t bank, uint16_t address, const char *symbol)
|
||||||
|
{
|
||||||
|
bank &= 0x1FF;
|
||||||
|
|
||||||
|
if (!gb->bank_symbols[bank]) {
|
||||||
|
gb->bank_symbols[bank] = GB_map_alloc();
|
||||||
|
}
|
||||||
|
GB_bank_symbol_t *allocated_symbol = GB_map_add_symbol(gb->bank_symbols[bank], address, symbol);
|
||||||
|
if (allocated_symbol) {
|
||||||
|
GB_reversed_map_add_symbol(&gb->reversed_symbol_map, bank, allocated_symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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");
|
||||||
@ -2172,15 +2214,8 @@ void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path)
|
|||||||
unsigned bank, address;
|
unsigned bank, address;
|
||||||
char symbol[length];
|
char symbol[length];
|
||||||
|
|
||||||
if (sscanf(line, "%02x:%04x %s", &bank, &address, symbol) == 3) {
|
if (sscanf(line, "%x:%x %s", &bank, &address, symbol) == 3) {
|
||||||
bank &= 0x1FF;
|
GB_debugger_add_symbol(gb, bank, address, symbol);
|
||||||
if (!gb->bank_symbols[bank]) {
|
|
||||||
gb->bank_symbols[bank] = GB_map_alloc();
|
|
||||||
}
|
|
||||||
GB_bank_symbol_t *allocated_symbol = GB_map_add_symbol(gb->bank_symbols[bank], address, symbol);
|
|
||||||
if (allocated_symbol) {
|
|
||||||
GB_reversed_map_add_symbol(&gb->reversed_symbol_map, bank, allocated_symbol);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(line);
|
free(line);
|
||||||
@ -2189,13 +2224,13 @@ void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path)
|
|||||||
|
|
||||||
void GB_debugger_clear_symbols(GB_gameboy_t *gb)
|
void GB_debugger_clear_symbols(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
for (int i = sizeof(gb->bank_symbols) / sizeof(gb->bank_symbols[0]); i--;) {
|
for (unsigned i = sizeof(gb->bank_symbols) / sizeof(gb->bank_symbols[0]); i--;) {
|
||||||
if (gb->bank_symbols[i]) {
|
if (gb->bank_symbols[i]) {
|
||||||
GB_map_free(gb->bank_symbols[i]);
|
GB_map_free(gb->bank_symbols[i]);
|
||||||
gb->bank_symbols[i] = 0;
|
gb->bank_symbols[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = sizeof(gb->reversed_symbol_map.buckets) / sizeof(gb->reversed_symbol_map.buckets[0]); i--;) {
|
for (unsigned i = sizeof(gb->reversed_symbol_map.buckets) / sizeof(gb->reversed_symbol_map.buckets[0]); i--;) {
|
||||||
while (gb->reversed_symbol_map.buckets[i]) {
|
while (gb->reversed_symbol_map.buckets[i]) {
|
||||||
GB_symbol_t *next = gb->reversed_symbol_map.buckets[i]->next;
|
GB_symbol_t *next = gb->reversed_symbol_map.buckets[i]->next;
|
||||||
free(gb->reversed_symbol_map.buckets[i]);
|
free(gb->reversed_symbol_map.buckets[i]);
|
||||||
|
@ -7,13 +7,15 @@
|
|||||||
|
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
#ifdef DISABLE_DEBUGGER
|
#ifdef GB_DISABLE_DEBUGGER
|
||||||
#define GB_debugger_run(gb) (void)0
|
#define GB_debugger_run(gb) (void)0
|
||||||
#define GB_debugger_handle_async_commands(gb) (void)0
|
#define GB_debugger_handle_async_commands(gb) (void)0
|
||||||
#define GB_debugger_ret_hook(gb) (void)0
|
#define GB_debugger_ret_hook(gb) (void)0
|
||||||
#define GB_debugger_call_hook(gb, addr) (void)addr
|
#define GB_debugger_call_hook(gb, addr) (void)addr
|
||||||
#define GB_debugger_test_write_watchpoint(gb, addr, value) ((void)addr, (void)value)
|
#define GB_debugger_test_write_watchpoint(gb, addr, value) ((void)addr, (void)value)
|
||||||
#define GB_debugger_test_read_watchpoint(gb, addr) (void)addr
|
#define GB_debugger_test_read_watchpoint(gb, addr) (void)addr
|
||||||
|
#define GB_debugger_add_symbol(gb, bank, address, symbol) ((void)bank, (void)address, (void)symbol)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
void GB_debugger_run(GB_gameboy_t *gb);
|
void GB_debugger_run(GB_gameboy_t *gb);
|
||||||
void GB_debugger_handle_async_commands(GB_gameboy_t *gb);
|
void GB_debugger_handle_async_commands(GB_gameboy_t *gb);
|
||||||
@ -22,7 +24,8 @@ void GB_debugger_ret_hook(GB_gameboy_t *gb);
|
|||||||
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);
|
||||||
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);
|
||||||
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);
|
||||||
#endif /* DISABLE_DEBUGGER */
|
void GB_debugger_add_symbol(GB_gameboy_t *gb, uint16_t bank, uint16_t address, const char *symbol);
|
||||||
|
#endif /* GB_DISABLE_DEBUGGER */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
|
615
Core/display.c
615
Core/display.c
@ -99,6 +99,8 @@ static void fifo_overlay_object_row(GB_fifo_t *fifo, uint8_t lower, uint8_t uppe
|
|||||||
#define LINE_LENGTH (456)
|
#define LINE_LENGTH (456)
|
||||||
#define LINES (144)
|
#define LINES (144)
|
||||||
#define WIDTH (160)
|
#define WIDTH (160)
|
||||||
|
#define BORDERED_WIDTH 256
|
||||||
|
#define BORDERED_HEIGHT 224
|
||||||
#define FRAME_LENGTH (LCDC_PERIOD)
|
#define FRAME_LENGTH (LCDC_PERIOD)
|
||||||
#define VIRTUAL_LINES (FRAME_LENGTH / LINE_LENGTH) // = 154
|
#define VIRTUAL_LINES (FRAME_LENGTH / LINE_LENGTH) // = 154
|
||||||
|
|
||||||
@ -109,22 +111,12 @@ typedef struct __attribute__((packed)) {
|
|||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
} GB_object_t;
|
} GB_object_t;
|
||||||
|
|
||||||
static bool window_enabled(GB_gameboy_t *gb)
|
|
||||||
{
|
|
||||||
if ((gb->io_registers[GB_IO_LCDC] & 0x1) == 0) {
|
|
||||||
if (!gb->cgb_mode) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (gb->io_registers[GB_IO_LCDC] & 0x20) && gb->io_registers[GB_IO_WX] < 167;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void display_vblank(GB_gameboy_t *gb)
|
static void display_vblank(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
gb->vblank_just_occured = true;
|
gb->vblank_just_occured = true;
|
||||||
|
|
||||||
/* TODO: Slow in trubo mode! */
|
/* TODO: Slow in turbo mode! */
|
||||||
if (GB_is_sgb(gb)) {
|
if (GB_is_hle_sgb(gb)) {
|
||||||
GB_sgb_render(gb);
|
GB_sgb_render(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,25 +126,85 @@ static void display_vblank(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || gb->stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) {
|
bool is_ppu_stopped = !GB_is_cgb(gb) && gb->stopped && gb->io_registers[GB_IO_LCDC] & 0x80;
|
||||||
|
|
||||||
|
if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || is_ppu_stopped) || gb->cgb_repeated_a_frame)) {
|
||||||
/* LCD is off, set screen to white or black (if LCD is on in stop mode) */
|
/* LCD is off, set screen to white or black (if LCD is on in stop mode) */
|
||||||
if (gb->sgb) {
|
if (!GB_is_sgb(gb)) {
|
||||||
uint8_t color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped && GB_is_cgb(gb) ? 0x3 : 0x0;
|
uint32_t color = 0;
|
||||||
for (unsigned i = 0; i < WIDTH * LINES; i++) {
|
if (GB_is_cgb(gb)) {
|
||||||
gb->sgb->screen_buffer[i] = color;
|
color = GB_convert_rgb15(gb, 0x7FFF, false);
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
else {
|
color = is_ppu_stopped ?
|
||||||
uint32_t color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped && GB_is_cgb(gb) ?
|
gb->background_palettes_rgb[0] :
|
||||||
gb->rgb_encode_callback(gb, 0, 0, 0) :
|
gb->background_palettes_rgb[4];
|
||||||
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
|
}
|
||||||
for (unsigned i = 0; i < WIDTH * LINES; i++) {
|
if (gb->border_mode == GB_BORDER_ALWAYS) {
|
||||||
gb ->screen[i] = color;
|
for (unsigned y = 0; y < LINES; y++) {
|
||||||
|
for (unsigned x = 0; x < WIDTH; x++) {
|
||||||
|
gb ->screen[x + y * BORDERED_WIDTH + (BORDERED_WIDTH - WIDTH) / 2 + (BORDERED_HEIGHT - LINES) / 2 * BORDERED_WIDTH] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (unsigned i = 0; i < WIDTH * LINES; i++) {
|
||||||
|
gb ->screen[i] = color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gb->border_mode == GB_BORDER_ALWAYS && !GB_is_sgb(gb)) {
|
||||||
|
GB_borrow_sgb_border(gb);
|
||||||
|
uint32_t border_colors[16 * 4];
|
||||||
|
|
||||||
|
if (!gb->has_sgb_border && GB_is_cgb(gb) && gb->model != GB_MODEL_AGB) {
|
||||||
|
static uint16_t colors[] = {
|
||||||
|
0x2095, 0x5129, 0x1EAF, 0x1EBA, 0x4648,
|
||||||
|
0x30DA, 0x69AD, 0x2B57, 0x2B5D, 0x632C,
|
||||||
|
0x1050, 0x3C84, 0x0E07, 0x0E18, 0x2964,
|
||||||
|
};
|
||||||
|
unsigned index = gb->rom? gb->rom[0x14e] % 5 : 0;
|
||||||
|
gb->borrowed_border.palette[0] = colors[index];
|
||||||
|
gb->borrowed_border.palette[10] = colors[5 + index];
|
||||||
|
gb->borrowed_border.palette[14] = colors[10 + index];
|
||||||
|
|
||||||
gb->vblank_callback(gb);
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 16 * 4; i++) {
|
||||||
|
border_colors[i] = GB_convert_rgb15(gb, gb->borrowed_border.palette[i], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned tile_y = 0; tile_y < 28; tile_y++) {
|
||||||
|
for (unsigned tile_x = 0; tile_x < 32; tile_x++) {
|
||||||
|
if (tile_x >= 6 && tile_x < 26 && tile_y >= 5 && tile_y < 23) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint16_t tile = gb->borrowed_border.map[tile_x + tile_y * 32];
|
||||||
|
uint8_t flip_x = (tile & 0x4000)? 0x7 : 0;
|
||||||
|
uint8_t flip_y = (tile & 0x8000)? 0x7 : 0;
|
||||||
|
uint8_t palette = (tile >> 10) & 3;
|
||||||
|
for (unsigned y = 0; y < 8; y++) {
|
||||||
|
for (unsigned x = 0; x < 8; x++) {
|
||||||
|
uint8_t color = gb->borrowed_border.tiles[(tile & 0xFF) * 64 + (x ^ flip_x) + (y ^ flip_y) * 8] & 0xF;
|
||||||
|
uint32_t *output = gb->screen + tile_x * 8 + x + (tile_y * 8 + y) * 256;
|
||||||
|
if (color == 0) {
|
||||||
|
*output = border_colors[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*output = border_colors[color + palette * 16];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GB_handle_rumble(gb);
|
||||||
|
|
||||||
|
if (gb->vblank_callback) {
|
||||||
|
gb->vblank_callback(gb);
|
||||||
|
}
|
||||||
GB_timing_sync(gb);
|
GB_timing_sync(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,29 +215,68 @@ static inline uint8_t scale_channel(uint8_t x)
|
|||||||
|
|
||||||
static inline uint8_t scale_channel_with_curve(uint8_t x)
|
static inline uint8_t scale_channel_with_curve(uint8_t x)
|
||||||
{
|
{
|
||||||
return (uint8_t[]){0,2,4,7,12,18,25,34,42,52,62,73,85,97,109,121,134,146,158,170,182,193,203,213,221,230,237,243,248,251,253,255,}[x];
|
return (uint8_t[]){0,5,8,11,16,22,28,36,43,51,59,67,77,87,97,107,119,130,141,153,166,177,188,200,209,221,230,238,245,249,252,255}[x];
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color)
|
static inline uint8_t scale_channel_with_curve_agb(uint8_t x)
|
||||||
|
{
|
||||||
|
return (uint8_t[]){0,2,5,10,15,20,26,32,38,45,52,60,68,76,84,92,101,110,119,128,138,148,158,168,178,189,199,210,221,232,244,255}[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t scale_channel_with_curve_sgb(uint8_t x)
|
||||||
|
{
|
||||||
|
return (uint8_t[]){0,2,5,9,15,20,27,34,42,50,58,67,76,85,94,104,114,123,133,143,153,163,173,182,192,202,211,220,229,238,247,255}[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color, bool for_border)
|
||||||
{
|
{
|
||||||
uint8_t r = (color) & 0x1F;
|
uint8_t r = (color) & 0x1F;
|
||||||
uint8_t g = (color >> 5) & 0x1F;
|
uint8_t g = (color >> 5) & 0x1F;
|
||||||
uint8_t b = (color >> 10) & 0x1F;
|
uint8_t b = (color >> 10) & 0x1F;
|
||||||
|
|
||||||
if (gb->color_correction_mode == GB_COLOR_CORRECTION_DISABLED) {
|
if (gb->color_correction_mode == GB_COLOR_CORRECTION_DISABLED || (for_border && !gb->has_sgb_border)) {
|
||||||
r = scale_channel(r);
|
r = scale_channel(r);
|
||||||
g = scale_channel(g);
|
g = scale_channel(g);
|
||||||
b = scale_channel(b);
|
b = scale_channel(b);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
r = scale_channel_with_curve(r);
|
if (GB_is_sgb(gb) || for_border) {
|
||||||
g = scale_channel_with_curve(g);
|
return gb->rgb_encode_callback(gb,
|
||||||
b = scale_channel_with_curve(b);
|
scale_channel_with_curve_sgb(r),
|
||||||
|
scale_channel_with_curve_sgb(g),
|
||||||
|
scale_channel_with_curve_sgb(b));
|
||||||
|
}
|
||||||
|
bool agb = gb->model == GB_MODEL_AGB;
|
||||||
|
r = agb? scale_channel_with_curve_agb(r) : scale_channel_with_curve(r);
|
||||||
|
g = agb? scale_channel_with_curve_agb(g) : scale_channel_with_curve(g);
|
||||||
|
b = agb? scale_channel_with_curve_agb(b) : scale_channel_with_curve(b);
|
||||||
|
|
||||||
if (gb->color_correction_mode != GB_COLOR_CORRECTION_CORRECT_CURVES) {
|
if (gb->color_correction_mode != GB_COLOR_CORRECTION_CORRECT_CURVES) {
|
||||||
uint8_t new_g = (g * 3 + b) / 4;
|
uint8_t new_r, new_g, new_b;
|
||||||
uint8_t new_r = r, new_b = b;
|
if (agb) {
|
||||||
if (gb->color_correction_mode == GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS) {
|
new_g = (g * 6 + b * 1) / 7;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
new_g = (g * 3 + b) / 4;
|
||||||
|
}
|
||||||
|
new_r = r;
|
||||||
|
new_b = b;
|
||||||
|
if (gb->color_correction_mode == GB_COLOR_CORRECTION_REDUCE_CONTRAST) {
|
||||||
|
r = new_r;
|
||||||
|
g = new_r;
|
||||||
|
b = new_r;
|
||||||
|
|
||||||
|
new_r = new_r * 7 / 8 + ( g + b) / 16;
|
||||||
|
new_g = new_g * 7 / 8 + (r + b) / 16;
|
||||||
|
new_b = new_b * 7 / 8 + (r + g ) / 16;
|
||||||
|
|
||||||
|
|
||||||
|
new_r = new_r * (224 - 32) / 255 + 32;
|
||||||
|
new_g = new_g * (220 - 36) / 255 + 36;
|
||||||
|
new_b = new_b * (216 - 40) / 255 + 40;
|
||||||
|
}
|
||||||
|
else if (gb->color_correction_mode == GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS) {
|
||||||
uint8_t old_max = MAX(r, MAX(g, b));
|
uint8_t old_max = MAX(r, MAX(g, b));
|
||||||
uint8_t new_max = MAX(new_r, MAX(new_g, new_b));
|
uint8_t new_max = MAX(new_r, MAX(new_g, new_b));
|
||||||
|
|
||||||
@ -201,7 +292,7 @@ uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color)
|
|||||||
if (new_min != 0xff) {
|
if (new_min != 0xff) {
|
||||||
new_r = 0xff - (0xff - new_r) * (0xff - old_min) / (0xff - new_min);
|
new_r = 0xff - (0xff - new_r) * (0xff - old_min) / (0xff - new_min);
|
||||||
new_g = 0xff - (0xff - new_g) * (0xff - old_min) / (0xff - new_min);
|
new_g = 0xff - (0xff - new_g) * (0xff - old_min) / (0xff - new_min);
|
||||||
new_b = 0xff - (0xff - new_b) * (0xff - old_min) / (0xff - new_min);;
|
new_b = 0xff - (0xff - new_b) * (0xff - old_min) / (0xff - new_min);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r = new_r;
|
r = new_r;
|
||||||
@ -219,7 +310,7 @@ void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index
|
|||||||
uint8_t *palette_data = background_palette? gb->background_palettes_data : gb->sprite_palettes_data;
|
uint8_t *palette_data = background_palette? gb->background_palettes_data : gb->sprite_palettes_data;
|
||||||
uint16_t color = palette_data[index & ~1] | (palette_data[index | 1] << 8);
|
uint16_t color = palette_data[index & ~1] | (palette_data[index | 1] << 8);
|
||||||
|
|
||||||
(background_palette? gb->background_palettes_rgb : gb->sprite_palettes_rgb)[index / 2] = GB_convert_rgb15(gb, color);
|
(background_palette? gb->background_palettes_rgb : gb->sprite_palettes_rgb)[index / 2] = GB_convert_rgb15(gb, color, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
@ -302,13 +393,11 @@ void GB_lcd_off(GB_gameboy_t *gb)
|
|||||||
gb->vram_write_blocked = false;
|
gb->vram_write_blocked = false;
|
||||||
gb->cgb_palettes_blocked = false;
|
gb->cgb_palettes_blocked = false;
|
||||||
|
|
||||||
/* Reset window rendering state */
|
|
||||||
gb->wy_diff = 0;
|
|
||||||
gb->window_disabled_while_active = false;
|
|
||||||
gb->current_line = 0;
|
gb->current_line = 0;
|
||||||
gb->ly_for_comparison = 0;
|
gb->ly_for_comparison = 0;
|
||||||
|
|
||||||
gb->accessed_oam_row = -1;
|
gb->accessed_oam_row = -1;
|
||||||
|
gb->wy_triggered = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
|
static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
|
||||||
@ -319,6 +408,10 @@ static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
|
|||||||
if (gb->dma_steps_left && (gb->dma_cycles >= 0 || gb->is_dma_restarting)) {
|
if (gb->dma_steps_left && (gb->dma_cycles >= 0 || gb->is_dma_restarting)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gb->oam_ppu_blocked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* This reverse sorts the visible objects by location and priority */
|
/* This reverse sorts the visible objects by location and priority */
|
||||||
GB_object_t *objects = (GB_object_t *) &gb->oam;
|
GB_object_t *objects = (GB_object_t *) &gb->oam;
|
||||||
@ -344,30 +437,30 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
|||||||
bool draw_oam = false;
|
bool draw_oam = false;
|
||||||
bool bg_enabled = true, bg_priority = false;
|
bool bg_enabled = true, bg_priority = false;
|
||||||
|
|
||||||
if (!gb->bg_fifo_paused) {
|
if (fifo_size(&gb->bg_fifo)) {
|
||||||
fifo_item = fifo_pop(&gb->bg_fifo);
|
fifo_item = fifo_pop(&gb->bg_fifo);
|
||||||
bg_priority = fifo_item->bg_priority;
|
bg_priority = fifo_item->bg_priority;
|
||||||
}
|
|
||||||
|
if (fifo_size(&gb->oam_fifo)) {
|
||||||
if (!gb->oam_fifo_paused && fifo_size(&gb->oam_fifo)) {
|
oam_fifo_item = fifo_pop(&gb->oam_fifo);
|
||||||
oam_fifo_item = fifo_pop(&gb->oam_fifo);
|
if (oam_fifo_item->pixel && (gb->io_registers[GB_IO_LCDC] & 2)) {
|
||||||
/* Todo: Verify access timings */
|
draw_oam = true;
|
||||||
if (oam_fifo_item->pixel && (gb->io_registers[GB_IO_LCDC] & 2)) {
|
bg_priority |= oam_fifo_item->bg_priority;
|
||||||
draw_oam = true;
|
}
|
||||||
bg_priority |= oam_fifo_item->bg_priority;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!fifo_item) return;
|
||||||
|
|
||||||
/* Drop pixels for scrollings */
|
/* Drop pixels for scrollings */
|
||||||
if (gb->position_in_line >= 160 || gb->disable_rendering) {
|
if (gb->position_in_line >= 160 || (gb->disable_rendering && !gb->sgb)) {
|
||||||
gb->position_in_line++;
|
gb->position_in_line++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (gb->bg_fifo_paused) return;
|
|
||||||
|
|
||||||
/* Mixing */
|
/* Mixing */
|
||||||
|
|
||||||
/* Todo: Verify access timings */
|
|
||||||
if ((gb->io_registers[GB_IO_LCDC] & 0x1) == 0) {
|
if ((gb->io_registers[GB_IO_LCDC] & 0x1) == 0) {
|
||||||
if (gb->cgb_mode) {
|
if (gb->cgb_mode) {
|
||||||
bg_priority = false;
|
bg_priority = false;
|
||||||
@ -376,6 +469,17 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
|||||||
bg_enabled = false;
|
bg_enabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t icd_pixel = 0;
|
||||||
|
uint32_t *dest = NULL;
|
||||||
|
if (!gb->sgb) {
|
||||||
|
if (gb->border_mode != GB_BORDER_ALWAYS) {
|
||||||
|
dest = gb->screen + gb->lcd_x + gb->current_line * WIDTH;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dest = gb->screen + gb->lcd_x + gb->current_line * BORDERED_WIDTH + (BORDERED_WIDTH - WIDTH) / 2 + (BORDERED_HEIGHT - LINES) / 2 * BORDERED_WIDTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
uint8_t pixel = bg_enabled? fifo_item->pixel : 0;
|
uint8_t pixel = bg_enabled? fifo_item->pixel : 0;
|
||||||
@ -387,11 +491,19 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
if (gb->sgb) {
|
if (gb->sgb) {
|
||||||
if (gb->current_lcd_line < LINES) {
|
if (gb->current_lcd_line < LINES) {
|
||||||
gb->sgb->screen_buffer[gb->position_in_line + gb->current_lcd_line * WIDTH] = pixel;
|
gb->sgb->screen_buffer[gb->lcd_x + gb->current_lcd_line * WIDTH] = gb->stopped? 0 : pixel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (gb->model & GB_MODEL_NO_SFC_BIT) {
|
||||||
|
if (gb->icd_pixel_callback) {
|
||||||
|
icd_pixel = pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (gb->cgb_palettes_ppu_blocked) {
|
||||||
|
*dest = gb->rgb_encode_callback(gb, 0, 0, 0);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->background_palettes_rgb[fifo_item->palette * 4 + pixel];
|
*dest = gb->background_palettes_rgb[fifo_item->palette * 4 + pixel];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,15 +515,32 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
if (gb->sgb) {
|
if (gb->sgb) {
|
||||||
if (gb->current_lcd_line < LINES) {
|
if (gb->current_lcd_line < LINES) {
|
||||||
gb->sgb->screen_buffer[gb->position_in_line + gb->current_lcd_line * WIDTH] = pixel;
|
gb->sgb->screen_buffer[gb->lcd_x + gb->current_lcd_line * WIDTH] = gb->stopped? 0 : pixel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (gb->model & GB_MODEL_NO_SFC_BIT) {
|
||||||
|
if (gb->icd_pixel_callback) {
|
||||||
|
icd_pixel = pixel;
|
||||||
|
//gb->icd_pixel_callback(gb, pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (gb->cgb_palettes_ppu_blocked) {
|
||||||
|
*dest = gb->rgb_encode_callback(gb, 0, 0, 0);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel];
|
*dest = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gb->model & GB_MODEL_NO_SFC_BIT) {
|
||||||
|
if (gb->icd_pixel_callback) {
|
||||||
|
gb->icd_pixel_callback(gb, icd_pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->position_in_line++;
|
gb->position_in_line++;
|
||||||
|
gb->lcd_x++;
|
||||||
|
gb->window_is_being_fetched = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* All verified CGB timings are based on CGB CPU E. CGB CPUs >= D are known to have
|
/* All verified CGB timings are based on CGB CPU E. CGB CPUs >= D are known to have
|
||||||
@ -421,7 +550,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
static inline uint8_t fetcher_y(GB_gameboy_t *gb)
|
static inline uint8_t fetcher_y(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
return gb->current_line + (gb->in_window? - gb->io_registers[GB_IO_WY] - gb->wy_diff : gb->io_registers[GB_IO_SCY]);
|
return gb->wx_triggered? gb->window_y : gb->current_line + gb->io_registers[GB_IO_SCY];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
||||||
@ -441,36 +570,52 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
|||||||
GB_FETCHER_GET_TILE_DATA_LOWER,
|
GB_FETCHER_GET_TILE_DATA_LOWER,
|
||||||
GB_FETCHER_SLEEP,
|
GB_FETCHER_SLEEP,
|
||||||
GB_FETCHER_GET_TILE_DATA_HIGH,
|
GB_FETCHER_GET_TILE_DATA_HIGH,
|
||||||
GB_FETCHER_SLEEP,
|
GB_FETCHER_PUSH,
|
||||||
GB_FETCHER_PUSH,
|
GB_FETCHER_PUSH,
|
||||||
};
|
};
|
||||||
|
switch (fetcher_state_machine[gb->fetcher_state & 7]) {
|
||||||
switch (fetcher_state_machine[gb->fetcher_state]) {
|
|
||||||
case GB_FETCHER_GET_TILE: {
|
case GB_FETCHER_GET_TILE: {
|
||||||
uint16_t map = 0x1800;
|
uint16_t map = 0x1800;
|
||||||
|
|
||||||
|
if (!(gb->io_registers[GB_IO_LCDC] & 0x20)) {
|
||||||
|
gb->wx_triggered = false;
|
||||||
|
gb->wx166_glitch = false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
|
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
|
||||||
if (gb->io_registers[GB_IO_LCDC] & 0x08 && !gb->in_window) {
|
if (gb->io_registers[GB_IO_LCDC] & 0x08 && !gb->wx_triggered) {
|
||||||
map = 0x1C00;
|
map = 0x1C00;
|
||||||
}
|
}
|
||||||
else if (gb->io_registers[GB_IO_LCDC] & 0x40 && gb->in_window) {
|
else if (gb->io_registers[GB_IO_LCDC] & 0x40 && gb->wx_triggered) {
|
||||||
map = 0x1C00;
|
map = 0x1C00;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
|
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
|
||||||
uint8_t y = fetcher_y(gb);
|
uint8_t y = fetcher_y(gb);
|
||||||
|
uint8_t x = 0;
|
||||||
|
if (gb->wx_triggered) {
|
||||||
|
x = gb->window_tile_x;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
x = ((gb->io_registers[GB_IO_SCX] / 8) + gb->fetcher_x) & 0x1F;
|
||||||
|
}
|
||||||
if (gb->model > GB_MODEL_CGB_C) {
|
if (gb->model > GB_MODEL_CGB_C) {
|
||||||
/* This value is cached on the CGB-D and newer, so it cannot be used to mix tiles together */
|
/* This value is cached on the CGB-D and newer, so it cannot be used to mix tiles together */
|
||||||
gb->fetcher_y = y;
|
gb->fetcher_y = y;
|
||||||
}
|
}
|
||||||
gb->current_tile = gb->vram[map + gb->fetcher_x + y / 8 * 32];
|
gb->last_tile_index_address = map + x + y / 8 * 32;
|
||||||
|
gb->current_tile = gb->vram[gb->last_tile_index_address];
|
||||||
|
if (gb->vram_ppu_blocked) {
|
||||||
|
gb->current_tile = 0xFF;
|
||||||
|
}
|
||||||
if (GB_is_cgb(gb)) {
|
if (GB_is_cgb(gb)) {
|
||||||
/* The CGB actually accesses both the tile index AND the attributes in the same T-cycle.
|
/* The CGB actually accesses both the tile index AND the attributes in the same T-cycle.
|
||||||
This probably means the CGB has a 16-bit data bus for the VRAM. */
|
This probably means the CGB has a 16-bit data bus for the VRAM. */
|
||||||
gb->current_tile_attributes = gb->vram[map + gb->fetcher_x + y / 8 * 32 + 0x2000];
|
gb->current_tile_attributes = gb->vram[gb->last_tile_index_address + 0x2000];
|
||||||
|
if (gb->vram_ppu_blocked) {
|
||||||
|
gb->current_tile_attributes = 0xFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gb->fetcher_x++;
|
|
||||||
gb->fetcher_x &= 0x1f;
|
|
||||||
}
|
}
|
||||||
gb->fetcher_state++;
|
gb->fetcher_state++;
|
||||||
break;
|
break;
|
||||||
@ -494,7 +639,10 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
|||||||
y_flip = 0x7;
|
y_flip = 0x7;
|
||||||
}
|
}
|
||||||
gb->current_tile_data[0] =
|
gb->current_tile_data[0] =
|
||||||
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2];
|
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2];
|
||||||
|
if (gb->vram_ppu_blocked) {
|
||||||
|
gb->current_tile_data[0] = 0xFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gb->fetcher_state++;
|
gb->fetcher_state++;
|
||||||
break;
|
break;
|
||||||
@ -520,19 +668,34 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
|||||||
if (gb->current_tile_attributes & 0x40) {
|
if (gb->current_tile_attributes & 0x40) {
|
||||||
y_flip = 0x7;
|
y_flip = 0x7;
|
||||||
}
|
}
|
||||||
|
gb->last_tile_data_address = tile_address + ((y & 7) ^ y_flip) * 2 + 1;
|
||||||
gb->current_tile_data[1] =
|
gb->current_tile_data[1] =
|
||||||
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2 + 1];
|
gb->vram[gb->last_tile_data_address];
|
||||||
|
if (gb->vram_ppu_blocked) {
|
||||||
|
gb->current_tile_data[1] = 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (gb->wx_triggered) {
|
||||||
|
gb->window_tile_x++;
|
||||||
|
gb->window_tile_x &= 0x1f;
|
||||||
}
|
}
|
||||||
gb->fetcher_state++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
// fallthrough
|
||||||
case GB_FETCHER_PUSH: {
|
case GB_FETCHER_PUSH: {
|
||||||
|
if (gb->fetcher_state == 6) {
|
||||||
|
/* The background map index increase at this specific point. If this state is not reached,
|
||||||
|
it will simply not increase. */
|
||||||
|
gb->fetcher_x++;
|
||||||
|
gb->fetcher_x &= 0x1f;
|
||||||
|
}
|
||||||
|
if (gb->fetcher_state < 7) {
|
||||||
|
gb->fetcher_state++;
|
||||||
|
}
|
||||||
if (fifo_size(&gb->bg_fifo) > 0) break;
|
if (fifo_size(&gb->bg_fifo) > 0) break;
|
||||||
|
|
||||||
fifo_push_bg_row(&gb->bg_fifo, gb->current_tile_data[0], gb->current_tile_data[1],
|
fifo_push_bg_row(&gb->bg_fifo, gb->current_tile_data[0], gb->current_tile_data[1],
|
||||||
gb->current_tile_attributes & 7, gb->current_tile_attributes & 0x80, gb->current_tile_attributes & 0x20);
|
gb->current_tile_attributes & 7, gb->current_tile_attributes & 0x80, gb->current_tile_attributes & 0x20);
|
||||||
gb->bg_fifo_paused = false;
|
gb->fetcher_state = 0;
|
||||||
gb->oam_fifo_paused = false;
|
|
||||||
gb->fetcher_state++;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -542,8 +705,30 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t get_object_line_address(GB_gameboy_t *gb, const GB_object_t *object)
|
||||||
|
{
|
||||||
|
/* TODO: what does the PPU read if DMA is active? */
|
||||||
|
if (gb->oam_ppu_blocked) {
|
||||||
|
static const GB_object_t blocked = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
object = &blocked;
|
||||||
|
}
|
||||||
|
|
||||||
gb->fetcher_state &= 7;
|
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0; /* Todo: Which T-cycle actually reads this? */
|
||||||
|
uint8_t tile_y = (gb->current_line - object->y) & (height_16? 0xF : 7);
|
||||||
|
|
||||||
|
if (object->flags & 0x40) { /* Flip Y */
|
||||||
|
tile_y ^= height_16? 0xF : 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Todo: I'm not 100% sure an access to OAM can't trigger the OAM bug while we're accessing this */
|
||||||
|
uint16_t line_address = (height_16? object->tile & 0xFE : object->tile) * 0x10 + tile_y * 2;
|
||||||
|
|
||||||
|
if (gb->cgb_mode && (object->flags & 0x8)) { /* Use VRAM bank 2 */
|
||||||
|
line_address += 0x2000;
|
||||||
|
}
|
||||||
|
return line_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -601,23 +786,38 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
GB_STATE(gb, display, 36);
|
GB_STATE(gb, display, 36);
|
||||||
GB_STATE(gb, display, 37);
|
GB_STATE(gb, display, 37);
|
||||||
GB_STATE(gb, display, 38);
|
GB_STATE(gb, display, 38);
|
||||||
|
GB_STATE(gb, display, 39);
|
||||||
|
GB_STATE(gb, display, 40);
|
||||||
|
GB_STATE(gb, display, 41);
|
||||||
|
GB_STATE(gb, display, 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
||||||
while (true) {
|
while (true) {
|
||||||
GB_SLEEP(gb, display, 1, LCDC_PERIOD);
|
GB_SLEEP(gb, display, 1, LCDC_PERIOD);
|
||||||
display_vblank(gb);
|
display_vblank(gb);
|
||||||
|
gb->cgb_repeated_a_frame = true;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gb->is_odd_frame = false;
|
||||||
|
|
||||||
if (!GB_is_cgb(gb)) {
|
if (!GB_is_cgb(gb)) {
|
||||||
GB_SLEEP(gb, display, 23, 1);
|
GB_SLEEP(gb, display, 23, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle mode 2 on the very first line 0 */
|
/* Handle mode 2 on the very first line 0 */
|
||||||
gb->current_line = 0;
|
gb->current_line = 0;
|
||||||
|
gb->window_y = -1;
|
||||||
|
/* Todo: verify timings */
|
||||||
|
if (gb->io_registers[GB_IO_WY] == 0) {
|
||||||
|
gb->wy_triggered = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->wy_triggered = false;
|
||||||
|
}
|
||||||
|
|
||||||
gb->ly_for_comparison = 0;
|
gb->ly_for_comparison = 0;
|
||||||
gb->io_registers[GB_IO_STAT] &= ~3;
|
gb->io_registers[GB_IO_STAT] &= ~3;
|
||||||
gb->mode_for_interrupt = -1;
|
gb->mode_for_interrupt = -1;
|
||||||
@ -654,17 +854,25 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
GB_SLEEP(gb, display, 37, 2);
|
GB_SLEEP(gb, display, 37, 2);
|
||||||
|
|
||||||
gb->cgb_palettes_blocked = true;
|
gb->cgb_palettes_blocked = true;
|
||||||
gb->cycles_for_line += 2;
|
gb->cycles_for_line += (GB_is_cgb(gb) && gb->model <= GB_MODEL_CGB_C)? 2 : 3;
|
||||||
GB_SLEEP(gb, display, 38, 2);
|
GB_SLEEP(gb, display, 38, (GB_is_cgb(gb) && gb->model <= GB_MODEL_CGB_C)? 2 : 3);
|
||||||
|
|
||||||
gb->vram_read_blocked = true;
|
gb->vram_read_blocked = true;
|
||||||
gb->vram_write_blocked = true;
|
gb->vram_write_blocked = true;
|
||||||
|
gb->wx_triggered = false;
|
||||||
|
gb->wx166_glitch = false;
|
||||||
goto mode_3_start;
|
goto mode_3_start;
|
||||||
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
/* Lines 0 - 143 */
|
/* Lines 0 - 143 */
|
||||||
|
gb->window_y = -1;
|
||||||
for (; gb->current_line < LINES; gb->current_line++) {
|
for (; gb->current_line < LINES; gb->current_line++) {
|
||||||
|
/* Todo: verify timings */
|
||||||
|
if ((gb->io_registers[GB_IO_WY] == gb->current_line ||
|
||||||
|
(gb->current_line != 0 && gb->io_registers[GB_IO_WY] == gb->current_line - 1))) {
|
||||||
|
gb->wy_triggered = true;
|
||||||
|
}
|
||||||
|
|
||||||
gb->oam_write_blocked = GB_is_cgb(gb) && !gb->cgb_double_speed;
|
gb->oam_write_blocked = GB_is_cgb(gb) && !gb->cgb_double_speed;
|
||||||
gb->accessed_oam_row = 0;
|
gb->accessed_oam_row = 0;
|
||||||
|
|
||||||
@ -750,20 +958,80 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
fifo_push_bg_row(&gb->bg_fifo, 0, 0, 0, false, false);
|
fifo_push_bg_row(&gb->bg_fifo, 0, 0, 0, false, false);
|
||||||
/* Todo: find out actual access time of SCX */
|
/* Todo: find out actual access time of SCX */
|
||||||
gb->position_in_line = - (gb->io_registers[GB_IO_SCX] & 7) - 8;
|
gb->position_in_line = - (gb->io_registers[GB_IO_SCX] & 7) - 8;
|
||||||
gb->current_lcd_line++; // Todo: unverified timing
|
gb->lcd_x = 0;
|
||||||
if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) {
|
|
||||||
display_vblank(gb);
|
gb->fetcher_x = 0;
|
||||||
}
|
|
||||||
gb->fetcher_x = ((gb->io_registers[GB_IO_SCX]) / 8) & 0x1f;
|
|
||||||
gb->extra_penalty_for_sprite_at_0 = (gb->io_registers[GB_IO_SCX] & 7);
|
gb->extra_penalty_for_sprite_at_0 = (gb->io_registers[GB_IO_SCX] & 7);
|
||||||
|
|
||||||
|
|
||||||
/* The actual rendering cycle */
|
/* The actual rendering cycle */
|
||||||
gb->fetcher_state = 0;
|
gb->fetcher_state = 0;
|
||||||
gb->bg_fifo_paused = false;
|
|
||||||
gb->oam_fifo_paused = false;
|
|
||||||
gb->in_window = false;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
/* Handle window */
|
||||||
|
/* TODO: It appears that WX checks if the window begins *next* pixel, not *this* pixel. For this reason,
|
||||||
|
WX=167 has no effect at all (It checks if the PPU X position is 161, which never happens) and WX=166
|
||||||
|
has weird artifacts (It appears to activate the window during HBlank, as PPU X is temporarily 160 at
|
||||||
|
that point. The code should be updated to represent this, and this will fix the time travel hack in
|
||||||
|
WX's access conflict code. */
|
||||||
|
|
||||||
|
if (!gb->wx_triggered && gb->wy_triggered && (gb->io_registers[GB_IO_LCDC] & 0x20)) {
|
||||||
|
bool should_activate_window = false;
|
||||||
|
if (gb->io_registers[GB_IO_WX] == 0) {
|
||||||
|
static const uint8_t scx_to_wx0_comparisons[] = {-7, -9, -10, -11, -12, -13, -14, -14};
|
||||||
|
if (gb->position_in_line == scx_to_wx0_comparisons[gb->io_registers[GB_IO_SCX] & 7]) {
|
||||||
|
should_activate_window = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (gb->wx166_glitch) {
|
||||||
|
static const uint8_t scx_to_wx166_comparisons[] = {-8, -9, -10, -11, -12, -13, -14, -15};
|
||||||
|
if (gb->position_in_line == scx_to_wx166_comparisons[gb->io_registers[GB_IO_SCX] & 7]) {
|
||||||
|
should_activate_window = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (gb->io_registers[GB_IO_WX] < 166 + GB_is_cgb(gb)) {
|
||||||
|
if (gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 7)) {
|
||||||
|
should_activate_window = true;
|
||||||
|
}
|
||||||
|
else if (gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 6) && !gb->wx_just_changed) {
|
||||||
|
should_activate_window = true;
|
||||||
|
/* LCD-PPU horizontal desync! It only appears to happen on DMGs, but not all of them.
|
||||||
|
This doesn't seem to be CPU revision dependent, but most revisions */
|
||||||
|
if ((gb->model & GB_MODEL_FAMILY_MASK) == GB_MODEL_DMG_FAMILY && !GB_is_sgb(gb)) {
|
||||||
|
if (gb->lcd_x > 0) {
|
||||||
|
gb->lcd_x--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_activate_window) {
|
||||||
|
gb->window_y++;
|
||||||
|
/* TODO: Verify fetcher access timings in this case */
|
||||||
|
if (gb->io_registers[GB_IO_WX] == 0 && (gb->io_registers[GB_IO_SCX] & 7)) {
|
||||||
|
gb->cycles_for_line++;
|
||||||
|
GB_SLEEP(gb, display, 42, 1);
|
||||||
|
}
|
||||||
|
gb->wx_triggered = true;
|
||||||
|
gb->window_tile_x = 0;
|
||||||
|
fifo_clear(&gb->bg_fifo);
|
||||||
|
gb->fetcher_state = 0;
|
||||||
|
gb->window_is_being_fetched = true;
|
||||||
|
}
|
||||||
|
else if (!GB_is_cgb(gb) && gb->io_registers[GB_IO_WX] == 166 && gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 7)) {
|
||||||
|
gb->window_y++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: What happens when WX=0? */
|
||||||
|
if (!GB_is_cgb(gb) && gb->wx_triggered && !gb->window_is_being_fetched &&
|
||||||
|
gb->fetcher_state == 0 && gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 7) ) {
|
||||||
|
// Insert a pixel right at the FIFO's end
|
||||||
|
gb->bg_fifo.read_end--;
|
||||||
|
gb->bg_fifo.read_end &= GB_FIFO_LENGTH - 1;
|
||||||
|
gb->bg_fifo.fifo[gb->bg_fifo.read_end] = (GB_fifo_item_t){0,};
|
||||||
|
gb->window_is_being_fetched = false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle objects */
|
/* Handle objects */
|
||||||
/* When the sprite enabled bit is off, this proccess is skipped entirely on the DMG, but not on the CGB.
|
/* When the sprite enabled bit is off, this proccess is skipped entirely on the DMG, but not on the CGB.
|
||||||
On the CGB, this bit is checked only when the pixel is actually popped from the FIFO. */
|
On the CGB, this bit is checked only when the pixel is actually popped from the FIFO. */
|
||||||
@ -773,14 +1041,19 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
gb->obj_comparators[gb->n_visible_objs - 1] < (uint8_t)(gb->position_in_line + 8)) {
|
gb->obj_comparators[gb->n_visible_objs - 1] < (uint8_t)(gb->position_in_line + 8)) {
|
||||||
gb->n_visible_objs--;
|
gb->n_visible_objs--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gb->during_object_fetch = true;
|
||||||
while (gb->n_visible_objs != 0 &&
|
while (gb->n_visible_objs != 0 &&
|
||||||
(gb->io_registers[GB_IO_LCDC] & 2 || GB_is_cgb(gb)) &&
|
(gb->io_registers[GB_IO_LCDC] & 2 || GB_is_cgb(gb)) &&
|
||||||
gb->obj_comparators[gb->n_visible_objs - 1] == (uint8_t)(gb->position_in_line + 8)) {
|
gb->obj_comparators[gb->n_visible_objs - 1] == (uint8_t)(gb->position_in_line + 8)) {
|
||||||
|
|
||||||
while (gb->fetcher_state < 5) {
|
while (gb->fetcher_state < 5 || fifo_size(&gb->bg_fifo) == 0) {
|
||||||
advance_fetcher_state_machine(gb);
|
advance_fetcher_state_machine(gb);
|
||||||
gb->cycles_for_line++;
|
gb->cycles_for_line++;
|
||||||
GB_SLEEP(gb, display, 27, 1);
|
GB_SLEEP(gb, display, 27, 1);
|
||||||
|
if (gb->object_fetch_aborted) {
|
||||||
|
goto abort_fetching_object;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Todo: Measure if penalty occurs before or after waiting for the fetcher. */
|
/* Todo: Measure if penalty occurs before or after waiting for the fetcher. */
|
||||||
@ -789,56 +1062,63 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
gb->cycles_for_line += gb->extra_penalty_for_sprite_at_0;
|
gb->cycles_for_line += gb->extra_penalty_for_sprite_at_0;
|
||||||
GB_SLEEP(gb, display, 28, gb->extra_penalty_for_sprite_at_0);
|
GB_SLEEP(gb, display, 28, gb->extra_penalty_for_sprite_at_0);
|
||||||
gb->extra_penalty_for_sprite_at_0 = 0;
|
gb->extra_penalty_for_sprite_at_0 = 0;
|
||||||
|
if (gb->object_fetch_aborted) {
|
||||||
|
goto abort_fetching_object;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->cycles_for_line += 6;
|
/* TODO: Can this be deleted? { */
|
||||||
GB_SLEEP(gb, display, 20, 6);
|
advance_fetcher_state_machine(gb);
|
||||||
/* TODO: what does the PPU read if DMA is active? */
|
gb->cycles_for_line++;
|
||||||
GB_object_t *object = &objects[gb->visible_objs[gb->n_visible_objs - 1]];
|
GB_SLEEP(gb, display, 41, 1);
|
||||||
|
if (gb->object_fetch_aborted) {
|
||||||
|
goto abort_fetching_object;
|
||||||
|
}
|
||||||
|
/* } */
|
||||||
|
|
||||||
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0; /* Todo: Which T-cycle actually reads this? */
|
advance_fetcher_state_machine(gb);
|
||||||
uint8_t tile_y = (gb->current_line - object->y) & (height_16? 0xF : 7);
|
|
||||||
|
|
||||||
if (object->flags & 0x40) { /* Flip Y */
|
gb->cycles_for_line += 3;
|
||||||
tile_y ^= height_16? 0xF : 7;
|
GB_SLEEP(gb, display, 20, 3);
|
||||||
|
if (gb->object_fetch_aborted) {
|
||||||
|
goto abort_fetching_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Todo: I'm not 100% sure an access to OAM can't trigger the OAM bug while we're accessing this */
|
gb->object_low_line_address = get_object_line_address(gb, &objects[gb->visible_objs[gb->n_visible_objs - 1]]);
|
||||||
uint16_t line_address = (height_16? object->tile & 0xFE : object->tile) * 0x10 + tile_y * 2;
|
|
||||||
|
|
||||||
if (gb->cgb_mode && (object->flags & 0x8)) { /* Use VRAM bank 2 */
|
gb->cycles_for_line++;
|
||||||
line_address += 0x2000;
|
GB_SLEEP(gb, display, 39, 1);
|
||||||
|
if (gb->object_fetch_aborted) {
|
||||||
|
goto abort_fetching_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gb->during_object_fetch = false;
|
||||||
|
gb->cycles_for_line++;
|
||||||
|
GB_SLEEP(gb, display, 40, 1);
|
||||||
|
|
||||||
|
const GB_object_t *object = &objects[gb->visible_objs[gb->n_visible_objs - 1]];
|
||||||
|
|
||||||
|
uint16_t line_address = get_object_line_address(gb, object);
|
||||||
|
|
||||||
uint8_t palette = (object->flags & 0x10) ? 1 : 0;
|
uint8_t palette = (object->flags & 0x10) ? 1 : 0;
|
||||||
if (gb->cgb_mode) {
|
if (gb->cgb_mode) {
|
||||||
palette = object->flags & 0x7;
|
palette = object->flags & 0x7;
|
||||||
}
|
}
|
||||||
|
|
||||||
fifo_overlay_object_row(&gb->oam_fifo,
|
fifo_overlay_object_row(&gb->oam_fifo,
|
||||||
gb->vram[line_address],
|
gb->vram_ppu_blocked? 0xFF : gb->vram[gb->object_low_line_address],
|
||||||
gb->vram[line_address + 1],
|
gb->vram_ppu_blocked? 0xFF : gb->vram[line_address + 1],
|
||||||
palette,
|
palette,
|
||||||
object->flags & 0x80,
|
object->flags & 0x80,
|
||||||
gb->cgb_mode? gb->visible_objs[gb->n_visible_objs - 1] : 0,
|
gb->object_priority == GB_OBJECT_PRIORITY_INDEX? gb->visible_objs[gb->n_visible_objs - 1] : 0,
|
||||||
object->flags & 0x20);
|
object->flags & 0x20);
|
||||||
|
|
||||||
gb->n_visible_objs--;
|
gb->n_visible_objs--;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle window */
|
abort_fetching_object:
|
||||||
/* Todo: Timing (Including penalty and access timings) not verified by test ROM */
|
gb->object_fetch_aborted = false;
|
||||||
if (!gb->in_window && window_enabled(gb) &&
|
gb->during_object_fetch = false;
|
||||||
gb->current_line >= gb->io_registers[GB_IO_WY] + gb->wy_diff &&
|
|
||||||
(uint8_t)(gb->position_in_line + 7) == gb->io_registers[GB_IO_WX]) {
|
|
||||||
gb->in_window = true;
|
|
||||||
fifo_clear(&gb->bg_fifo);
|
|
||||||
gb->bg_fifo_paused = true;
|
|
||||||
gb->oam_fifo_paused = true;
|
|
||||||
gb->fetcher_x = 0;
|
|
||||||
gb->fetcher_state = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
render_pixel_if_possible(gb);
|
render_pixel_if_possible(gb);
|
||||||
advance_fetcher_state_machine(gb);
|
advance_fetcher_state_machine(gb);
|
||||||
@ -848,6 +1128,29 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
GB_SLEEP(gb, display, 21, 1);
|
GB_SLEEP(gb, display, 21, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (gb->lcd_x != 160 && !gb->disable_rendering && gb->screen && !gb->sgb) {
|
||||||
|
/* Oh no! The PPU and LCD desynced! Fill the rest of the line whith white. */
|
||||||
|
uint32_t *dest = NULL;
|
||||||
|
if (gb->border_mode != GB_BORDER_ALWAYS) {
|
||||||
|
dest = gb->screen + gb->lcd_x + gb->current_line * WIDTH;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dest = gb->screen + gb->lcd_x + gb->current_line * BORDERED_WIDTH + (BORDERED_WIDTH - WIDTH) / 2 + (BORDERED_HEIGHT - LINES) / 2 * BORDERED_WIDTH;
|
||||||
|
}
|
||||||
|
*dest = gb->background_palettes_rgb[0];
|
||||||
|
gb->lcd_x++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Verify timing */
|
||||||
|
if (!GB_is_cgb(gb) && gb->wy_triggered && (gb->io_registers[GB_IO_LCDC] & 0x20) && gb->io_registers[GB_IO_WX] == 166) {
|
||||||
|
gb->wx166_glitch = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->wx166_glitch = false;
|
||||||
|
}
|
||||||
|
gb->wx_triggered = false;
|
||||||
|
|
||||||
if (GB_is_cgb(gb) && gb->model <= GB_MODEL_CGB_C) {
|
if (GB_is_cgb(gb) && gb->model <= GB_MODEL_CGB_C) {
|
||||||
gb->cycles_for_line++;
|
gb->cycles_for_line++;
|
||||||
GB_SLEEP(gb, display, 30, 1);
|
GB_SLEEP(gb, display, 30, 1);
|
||||||
@ -890,8 +1193,18 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
}
|
}
|
||||||
GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line);
|
GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line);
|
||||||
gb->mode_for_interrupt = 2;
|
gb->mode_for_interrupt = 2;
|
||||||
|
|
||||||
|
// Todo: unverified timing
|
||||||
|
gb->current_lcd_line++;
|
||||||
|
if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) {
|
||||||
|
display_vblank(gb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gb->icd_hreset_callback) {
|
||||||
|
gb->icd_hreset_callback(gb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
gb->wx166_glitch = false;
|
||||||
/* Lines 144 - 152 */
|
/* Lines 144 - 152 */
|
||||||
for (; gb->current_line < VIRTUAL_LINES - 1; gb->current_line++) {
|
for (; gb->current_line < VIRTUAL_LINES - 1; gb->current_line++) {
|
||||||
gb->io_registers[GB_IO_LY] = gb->current_line;
|
gb->io_registers[GB_IO_LY] = gb->current_line;
|
||||||
@ -915,16 +1228,30 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
GB_STAT_update(gb);
|
GB_STAT_update(gb);
|
||||||
|
|
||||||
if (gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON) {
|
if (gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON) {
|
||||||
if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) {
|
if (GB_is_cgb(gb)) {
|
||||||
display_vblank(gb);
|
GB_timing_sync(gb);
|
||||||
|
gb->frame_skip_state = GB_FRAMESKIP_FIRST_FRAME_SKIPPED;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) {
|
||||||
|
gb->is_odd_frame ^= true;
|
||||||
|
display_vblank(gb);
|
||||||
|
}
|
||||||
|
gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED;
|
||||||
}
|
}
|
||||||
gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED;
|
|
||||||
if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) {
|
if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) {
|
||||||
|
gb->is_odd_frame ^= true;
|
||||||
display_vblank(gb);
|
display_vblank(gb);
|
||||||
}
|
}
|
||||||
|
if (gb->frame_skip_state == GB_FRAMESKIP_FIRST_FRAME_SKIPPED) {
|
||||||
|
gb->cgb_repeated_a_frame = true;
|
||||||
|
gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->cgb_repeated_a_frame = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -957,11 +1284,21 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
GB_SLEEP(gb, display, 17, LINE_LENGTH - 24);
|
GB_SLEEP(gb, display, 17, LINE_LENGTH - 24);
|
||||||
|
|
||||||
|
|
||||||
/* Reset window rendering state */
|
|
||||||
gb->wy_diff = 0;
|
|
||||||
gb->window_disabled_while_active = false;
|
|
||||||
gb->current_line = 0;
|
gb->current_line = 0;
|
||||||
gb->current_lcd_line = -1; // TODO: not the correct timing
|
/* Todo: verify timings */
|
||||||
|
if ((gb->io_registers[GB_IO_LCDC] & 0x20) &&
|
||||||
|
(gb->io_registers[GB_IO_WY] == 0)) {
|
||||||
|
gb->wy_triggered = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->wy_triggered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: not the correct timing
|
||||||
|
gb->current_lcd_line = 0;
|
||||||
|
if (gb->icd_vreset_callback) {
|
||||||
|
gb->icd_vreset_callback(gb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1094,7 +1431,7 @@ uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_h
|
|||||||
GB_object_t *sprite = (GB_object_t *) &gb->oam;
|
GB_object_t *sprite = (GB_object_t *) &gb->oam;
|
||||||
uint8_t sprites_in_line = 0;
|
uint8_t sprites_in_line = 0;
|
||||||
for (uint8_t i = 0; i < 40; i++, sprite++) {
|
for (uint8_t i = 0; i < 40; i++, sprite++) {
|
||||||
int sprite_y = sprite->y - 16;
|
signed sprite_y = sprite->y - 16;
|
||||||
bool obscured = false;
|
bool obscured = false;
|
||||||
// Is sprite not in this line?
|
// Is sprite not in this line?
|
||||||
if (sprite_y > y || sprite_y + *sprite_height <= y) continue;
|
if (sprite_y > y || sprite_y + *sprite_height <= y) continue;
|
||||||
@ -1143,30 +1480,8 @@ uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_h
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called when a write might enable or disable the window */
|
|
||||||
void GB_window_related_write(GB_gameboy_t *gb, uint8_t addr, uint8_t value)
|
bool GB_is_odd_frame(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
bool before = window_enabled(gb);
|
return gb->is_odd_frame;
|
||||||
gb->io_registers[addr] = value;
|
|
||||||
bool after = window_enabled(gb);
|
|
||||||
|
|
||||||
if (before != after && gb->current_line < LINES) {
|
|
||||||
/* Window was disabled or enabled outside of vblank */
|
|
||||||
if (gb->current_line >= gb->io_registers[GB_IO_WY]) {
|
|
||||||
if (after) {
|
|
||||||
if (!gb->window_disabled_while_active) {
|
|
||||||
/* Window was turned on for the first time this frame while LY > WY,
|
|
||||||
should start window in the next line */
|
|
||||||
gb->wy_diff = gb->current_line + 1 - gb->io_registers[GB_IO_WY];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
gb->wy_diff += gb->current_line;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
gb->wy_diff -= gb->current_line;
|
|
||||||
gb->window_disabled_while_active = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,15 @@
|
|||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles);
|
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles);
|
||||||
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);
|
||||||
void GB_window_related_write(GB_gameboy_t *gb, uint8_t addr, uint8_t value);
|
|
||||||
void GB_STAT_update(GB_gameboy_t *gb);
|
void GB_STAT_update(GB_gameboy_t *gb);
|
||||||
void GB_lcd_off(GB_gameboy_t *gb);
|
void GB_lcd_off(GB_gameboy_t *gb);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GB_OBJECT_PRIORITY_UNDEFINED, // For save state compatibility
|
||||||
|
GB_OBJECT_PRIORITY_X,
|
||||||
|
GB_OBJECT_PRIORITY_INDEX,
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -44,11 +50,13 @@ typedef enum {
|
|||||||
GB_COLOR_CORRECTION_CORRECT_CURVES,
|
GB_COLOR_CORRECTION_CORRECT_CURVES,
|
||||||
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_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 *sprite_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);
|
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);
|
||||||
|
bool GB_is_odd_frame(GB_gameboy_t *gb);
|
||||||
#endif /* display_h */
|
#endif /* display_h */
|
||||||
|
187
Core/gb.h
187
Core/gb.h
@ -1,5 +1,6 @@
|
|||||||
#ifndef GB_h
|
#ifndef GB_h
|
||||||
#define GB_h
|
#define GB_h
|
||||||
|
#define typeof __typeof__
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@ -20,19 +21,22 @@
|
|||||||
#include "sm83_cpu.h"
|
#include "sm83_cpu.h"
|
||||||
#include "symbol_hash.h"
|
#include "symbol_hash.h"
|
||||||
#include "sgb.h"
|
#include "sgb.h"
|
||||||
|
#include "cheats.h"
|
||||||
|
#include "rumble.h"
|
||||||
|
|
||||||
#define GB_STRUCT_VERSION 13
|
#define GB_STRUCT_VERSION 13
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
|
||||||
#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 0x1000
|
#define GB_MODEL_PAL_BIT 0x1000
|
||||||
|
#define GB_MODEL_NO_SFC_BIT 0x2000
|
||||||
|
|
||||||
|
#ifdef GB_INTERNAL
|
||||||
#if __clang__
|
#if __clang__
|
||||||
#define UNROLL _Pragma("unroll")
|
#define UNROLL _Pragma("unroll")
|
||||||
#elif __GNUC__
|
#elif __GNUC__ >= 8
|
||||||
#define UNROLL _Pragma("GCC unroll 8")
|
#define UNROLL _Pragma("GCC unroll 8")
|
||||||
#else
|
#else
|
||||||
#define UNROLL
|
#define UNROLL
|
||||||
@ -48,6 +52,17 @@
|
|||||||
#error Unable to detect endianess
|
#error Unable to detect endianess
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
uint8_t r, g, b;
|
||||||
|
} colors[5];
|
||||||
|
} GB_palette_t;
|
||||||
|
|
||||||
|
extern const GB_palette_t GB_PALETTE_GREY;
|
||||||
|
extern const GB_palette_t GB_PALETTE_DMG;
|
||||||
|
extern const GB_palette_t GB_PALETTE_MGB;
|
||||||
|
extern const GB_palette_t GB_PALETTE_GBL;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint8_t seconds;
|
uint8_t seconds;
|
||||||
@ -59,7 +74,6 @@ typedef union {
|
|||||||
uint8_t data[5];
|
uint8_t data[5];
|
||||||
} GB_rtc_time_t;
|
} GB_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,
|
||||||
@ -67,9 +81,13 @@ typedef enum {
|
|||||||
// GB_MODEL_DMG_C = 0x003,
|
// GB_MODEL_DMG_C = 0x003,
|
||||||
GB_MODEL_SGB = 0x004,
|
GB_MODEL_SGB = 0x004,
|
||||||
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
|
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
|
||||||
GB_MODEL_SGB_PAL = 0x1004,
|
GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_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_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_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,
|
||||||
@ -92,10 +110,16 @@ enum {
|
|||||||
enum {
|
enum {
|
||||||
GB_CARRY_FLAG = 16,
|
GB_CARRY_FLAG = 16,
|
||||||
GB_HALF_CARRY_FLAG = 32,
|
GB_HALF_CARRY_FLAG = 32,
|
||||||
GB_SUBSTRACT_FLAG = 64,
|
GB_SUBTRACT_FLAG = 64,
|
||||||
GB_ZERO_FLAG = 128,
|
GB_ZERO_FLAG = 128,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GB_BORDER_SGB,
|
||||||
|
GB_BORDER_NEVER,
|
||||||
|
GB_BORDER_ALWAYS,
|
||||||
|
} GB_border_mode_t;
|
||||||
|
|
||||||
#define GB_MAX_IR_QUEUE 256
|
#define GB_MAX_IR_QUEUE 256
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -163,7 +187,7 @@ enum {
|
|||||||
// Unfortunately it is not readable or writable after boot has finished, so research of this
|
// 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
|
// register is quite limited. The value written to this register, however, can be controlled
|
||||||
// in some cases.
|
// in some cases.
|
||||||
GB_IO_DMG_EMULATION = 0x4c,
|
GB_IO_KEY0 = 0x4c,
|
||||||
|
|
||||||
/* General CGB features */
|
/* General CGB features */
|
||||||
GB_IO_KEY1 = 0x4d, // CGB Mode Only - Prepare Speed Switch
|
GB_IO_KEY1 = 0x4d, // CGB Mode Only - Prepare Speed Switch
|
||||||
@ -171,7 +195,7 @@ enum {
|
|||||||
/* Missing */
|
/* Missing */
|
||||||
|
|
||||||
GB_IO_VBK = 0x4f, // CGB Mode Only - VRAM Bank
|
GB_IO_VBK = 0x4f, // CGB Mode Only - VRAM Bank
|
||||||
GB_IO_BIOS = 0x50, // Write to disable the BIOS mapping
|
GB_IO_BANK = 0x50, // Write to disable the BIOS mapping
|
||||||
|
|
||||||
/* CGB DMA */
|
/* CGB DMA */
|
||||||
GB_IO_HDMA1 = 0x51, // CGB Mode Only - New DMA Source, High
|
GB_IO_HDMA1 = 0x51, // CGB Mode Only - New DMA Source, High
|
||||||
@ -190,9 +214,7 @@ enum {
|
|||||||
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 - Sprite Palette Index
|
GB_IO_OBPI = 0x6a, // CGB Mode Only - Sprite Palette Index
|
||||||
GB_IO_OBPD = 0x6b, // CGB Mode Only - Sprite Palette Data
|
GB_IO_OBPD = 0x6b, // CGB Mode Only - Sprite Palette Data
|
||||||
|
GB_IO_OPRI = 0x6c, // Affects object priority (X based or index based)
|
||||||
// 1 is written for DMG ROMs on a CGB. Does not appear to have an effect.
|
|
||||||
GB_IO_DMG_EMULATION_INDICATION = 0x6c, // (FEh) Bit 0 (Read/Write)
|
|
||||||
|
|
||||||
/* Missing */
|
/* Missing */
|
||||||
|
|
||||||
@ -213,6 +235,17 @@ typedef enum {
|
|||||||
GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE
|
GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE
|
||||||
} GB_log_attributes;
|
} GB_log_attributes;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GB_BOOT_ROM_DMG0,
|
||||||
|
GB_BOOT_ROM_DMG,
|
||||||
|
GB_BOOT_ROM_MGB,
|
||||||
|
GB_BOOT_ROM_SGB,
|
||||||
|
GB_BOOT_ROM_SGB2,
|
||||||
|
GB_BOOT_ROM_CGB0,
|
||||||
|
GB_BOOT_ROM_CGB,
|
||||||
|
GB_BOOT_ROM_AGB,
|
||||||
|
} GB_boot_rom_t;
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
#define LCDC_PERIOD 70224
|
#define LCDC_PERIOD 70224
|
||||||
#define CPU_FREQUENCY 0x400000
|
#define CPU_FREQUENCY 0x400000
|
||||||
@ -222,11 +255,11 @@ typedef enum {
|
|||||||
#define INTERNAL_DIV_CYCLES (0x40000)
|
#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; })
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(MAX)
|
#if !defined(MAX)
|
||||||
#define MAX(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; })
|
#define MAX(A, B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; })
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -234,15 +267,20 @@ 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, long cycles_since_last_update);
|
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, bool rumble_on);
|
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);
|
||||||
typedef void (*GB_update_input_hint_callback_t)(GB_gameboy_t *gb);
|
typedef void (*GB_update_input_hint_callback_t)(GB_gameboy_t *gb);
|
||||||
|
typedef void (*GB_joyp_write_callback_t)(GB_gameboy_t *gb, uint8_t value);
|
||||||
|
typedef void (*GB_icd_pixel_callback_t)(GB_gameboy_t *gb, uint8_t row);
|
||||||
|
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_boot_rom_load_callback_t)(GB_gameboy_t *gb, GB_boot_rom_t type);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool state;
|
bool state;
|
||||||
long delay;
|
uint64_t delay;
|
||||||
} GB_ir_queue_item_t;
|
} GB_ir_queue_item_t;
|
||||||
|
|
||||||
struct GB_breakpoint_s;
|
struct GB_breakpoint_s;
|
||||||
@ -268,10 +306,6 @@ typedef struct {
|
|||||||
This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64
|
This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64
|
||||||
bit platforms. */
|
bit platforms. */
|
||||||
|
|
||||||
/* We make sure bool is 1 for cross-platform save state compatibility. */
|
|
||||||
/* Todo: We might want to typedef our own bool if this prevents SameBoy from working on specific platforms. */
|
|
||||||
_Static_assert(sizeof(bool) == 1, "sizeof(bool) != 1");
|
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
struct GB_gameboy_s {
|
struct GB_gameboy_s {
|
||||||
#else
|
#else
|
||||||
@ -371,9 +405,8 @@ struct GB_gameboy_internal_s {
|
|||||||
} mbc2;
|
} mbc2;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint8_t rom_bank:7;
|
uint8_t rom_bank:8;
|
||||||
uint8_t padding:1;
|
uint8_t ram_bank:3;
|
||||||
uint8_t ram_bank:4;
|
|
||||||
} mbc3;
|
} mbc3;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -385,18 +418,30 @@ struct GB_gameboy_internal_s {
|
|||||||
struct {
|
struct {
|
||||||
uint8_t bank_low:6;
|
uint8_t bank_low:6;
|
||||||
uint8_t bank_high:3;
|
uint8_t bank_high:3;
|
||||||
uint8_t mode:1;
|
bool mode:1;
|
||||||
|
bool ir_mode:1;
|
||||||
} huc1;
|
} huc1;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint8_t rom_bank;
|
uint8_t rom_bank:7;
|
||||||
uint8_t ram_bank;
|
uint8_t padding:1;
|
||||||
|
uint8_t ram_bank:4;
|
||||||
} huc3;
|
} huc3;
|
||||||
};
|
};
|
||||||
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];
|
||||||
bool rumble_state;
|
bool rumble_state;
|
||||||
|
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;
|
||||||
|
uint16_t huc3_alarm_minutes, huc3_alarm_days;
|
||||||
|
bool huc3_alarm_enabled;
|
||||||
|
uint8_t huc3_read;
|
||||||
|
uint8_t huc3_access_flags;
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@ -440,11 +485,12 @@ struct GB_gameboy_internal_s {
|
|||||||
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;
|
||||||
uint8_t wy_diff;
|
uint8_t window_y;
|
||||||
/* The LCDC will skip the first frame it renders after turning it on.
|
/* The LCDC will skip the first frame it renders after turning it on.
|
||||||
On the CGB, a frame is not skipped if the previous frame was skipped as well.
|
On the CGB, a frame is not skipped if the previous frame was skipped as well.
|
||||||
See https://www.reddit.com/r/EmuDev/comments/6exyxu/ */
|
See https://www.reddit.com/r/EmuDev/comments/6exyxu/ */
|
||||||
/* TODO: Drop this and properly emulate the dropped vreset signal*/
|
|
||||||
|
/* TODO: Drop this and properly emulate the dropped vreset signal*/
|
||||||
enum {
|
enum {
|
||||||
GB_FRAMESKIP_LCD_TURNED_ON, // On a DMG, the LCD renders a blank screen during this state,
|
GB_FRAMESKIP_LCD_TURNED_ON, // On a DMG, the LCD renders a blank screen during this state,
|
||||||
// on a CGB, the previous frame is repeated (which might be
|
// on a CGB, the previous frame is repeated (which might be
|
||||||
@ -456,7 +502,7 @@ struct GB_gameboy_internal_s {
|
|||||||
bool vram_read_blocked;
|
bool vram_read_blocked;
|
||||||
bool oam_write_blocked;
|
bool oam_write_blocked;
|
||||||
bool vram_write_blocked;
|
bool vram_write_blocked;
|
||||||
bool window_disabled_while_active;
|
bool fifo_insertion_glitch;
|
||||||
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;
|
||||||
@ -467,9 +513,9 @@ struct GB_gameboy_internal_s {
|
|||||||
uint8_t current_tile_attributes;
|
uint8_t current_tile_attributes;
|
||||||
uint8_t current_tile_data[2];
|
uint8_t current_tile_data[2];
|
||||||
uint8_t fetcher_state;
|
uint8_t fetcher_state;
|
||||||
bool bg_fifo_paused;
|
bool window_is_being_fetched;
|
||||||
bool oam_fifo_paused;
|
bool wx166_glitch;
|
||||||
bool in_window;
|
bool wx_triggered;
|
||||||
uint8_t visible_objs[10];
|
uint8_t visible_objs[10];
|
||||||
uint8_t obj_comparators[10];
|
uint8_t obj_comparators[10];
|
||||||
uint8_t n_visible_objs;
|
uint8_t n_visible_objs;
|
||||||
@ -481,6 +527,20 @@ struct GB_gameboy_internal_s {
|
|||||||
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;
|
uint32_t cycles_in_stop_mode;
|
||||||
|
uint8_t object_priority;
|
||||||
|
bool oam_ppu_blocked;
|
||||||
|
bool vram_ppu_blocked;
|
||||||
|
bool cgb_palettes_ppu_blocked;
|
||||||
|
bool object_fetch_aborted;
|
||||||
|
bool during_object_fetch;
|
||||||
|
uint16_t object_low_line_address;
|
||||||
|
bool wy_triggered;
|
||||||
|
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.
|
||||||
|
bool is_odd_frame;
|
||||||
|
uint16_t last_tile_data_address;
|
||||||
|
uint16_t last_tile_index_address;
|
||||||
|
bool cgb_repeated_a_frame;
|
||||||
);
|
);
|
||||||
|
|
||||||
/* 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 */
|
||||||
@ -494,6 +554,7 @@ struct GB_gameboy_internal_s {
|
|||||||
GB_STANDARD_MBC1_WIRING,
|
GB_STANDARD_MBC1_WIRING,
|
||||||
GB_MBC1M_WIRING,
|
GB_MBC1M_WIRING,
|
||||||
} mbc1_wiring;
|
} mbc1_wiring;
|
||||||
|
bool is_mbc30;
|
||||||
|
|
||||||
unsigned pending_cycles;
|
unsigned pending_cycles;
|
||||||
|
|
||||||
@ -506,8 +567,13 @@ struct GB_gameboy_internal_s {
|
|||||||
uint32_t *screen;
|
uint32_t *screen;
|
||||||
uint32_t background_palettes_rgb[0x20];
|
uint32_t background_palettes_rgb[0x20];
|
||||||
uint32_t sprite_palettes_rgb[0x20];
|
uint32_t sprite_palettes_rgb[0x20];
|
||||||
|
const GB_palette_t *dmg_palette;
|
||||||
GB_color_correction_mode_t color_correction_mode;
|
GB_color_correction_mode_t color_correction_mode;
|
||||||
bool keys[4][GB_KEY_MAX];
|
bool keys[4][GB_KEY_MAX];
|
||||||
|
GB_border_mode_t border_mode;
|
||||||
|
GB_sgb_border_t borrowed_border;
|
||||||
|
bool tried_loading_sgb_border;
|
||||||
|
bool has_sgb_border;
|
||||||
|
|
||||||
/* Timing */
|
/* Timing */
|
||||||
uint64_t last_sync;
|
uint64_t last_sync;
|
||||||
@ -530,10 +596,16 @@ struct GB_gameboy_internal_s {
|
|||||||
GB_serial_transfer_bit_start_callback_t serial_transfer_bit_start_callback;
|
GB_serial_transfer_bit_start_callback_t serial_transfer_bit_start_callback;
|
||||||
GB_serial_transfer_bit_end_callback_t serial_transfer_bit_end_callback;
|
GB_serial_transfer_bit_end_callback_t serial_transfer_bit_end_callback;
|
||||||
GB_update_input_hint_callback_t update_input_hint_callback;
|
GB_update_input_hint_callback_t update_input_hint_callback;
|
||||||
|
GB_joyp_write_callback_t joyp_write_callback;
|
||||||
|
GB_icd_pixel_callback_t icd_pixel_callback;
|
||||||
|
GB_icd_vreset_callback_t icd_hreset_callback;
|
||||||
|
GB_icd_vreset_callback_t icd_vreset_callback;
|
||||||
|
GB_read_memory_callback_t read_memory_callback;
|
||||||
|
GB_boot_rom_load_callback_t boot_rom_load_callback;
|
||||||
|
|
||||||
/* IR */
|
/* IR */
|
||||||
long cycles_since_ir_change; // In 8MHz units
|
uint64_t cycles_since_ir_change; // In 8MHz units
|
||||||
long cycles_since_input_ir_change; // In 8MHz units
|
uint64_t cycles_since_input_ir_change; // In 8MHz units
|
||||||
GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE];
|
GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE];
|
||||||
size_t ir_queue_length;
|
size_t ir_queue_length;
|
||||||
|
|
||||||
@ -544,13 +616,13 @@ 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;
|
bool has_jump_to_breakpoints, has_software_breakpoints;
|
||||||
void *nontrivial_jump_state;
|
void *nontrivial_jump_state;
|
||||||
bool non_trivial_jump_breakpoint_occured;
|
bool non_trivial_jump_breakpoint_occured;
|
||||||
|
|
||||||
/* SLD (Todo: merge with backtrace) */
|
/* SLD (Todo: merge with backtrace) */
|
||||||
bool stack_leak_detection;
|
bool stack_leak_detection;
|
||||||
int debug_call_depth;
|
signed debug_call_depth;
|
||||||
uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */
|
uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */
|
||||||
uint16_t addr_for_call_depth[0x200];
|
uint16_t addr_for_call_depth[0x200];
|
||||||
|
|
||||||
@ -571,7 +643,7 @@ struct GB_gameboy_internal_s {
|
|||||||
GB_reversed_symbol_map_t reversed_symbol_map;
|
GB_reversed_symbol_map_t reversed_symbol_map;
|
||||||
|
|
||||||
/* Ticks command */
|
/* Ticks command */
|
||||||
unsigned long debugger_ticks;
|
uint64_t debugger_ticks;
|
||||||
|
|
||||||
/* Rewind */
|
/* Rewind */
|
||||||
#define GB_REWIND_FRAMES_PER_KEY 255
|
#define GB_REWIND_FRAMES_PER_KEY 255
|
||||||
@ -589,6 +661,12 @@ struct GB_gameboy_internal_s {
|
|||||||
double sgb_intro_jingle_phases[7];
|
double sgb_intro_jingle_phases[7];
|
||||||
double sgb_intro_sweep_phase;
|
double sgb_intro_sweep_phase;
|
||||||
double sgb_intro_sweep_previous_sample;
|
double sgb_intro_sweep_previous_sample;
|
||||||
|
|
||||||
|
/* Cheats */
|
||||||
|
bool cheat_enabled;
|
||||||
|
size_t cheat_count;
|
||||||
|
GB_cheat_t **cheats;
|
||||||
|
GB_cheat_hash_t *cheat_hash[256];
|
||||||
|
|
||||||
/* Misc */
|
/* Misc */
|
||||||
bool turbo;
|
bool turbo;
|
||||||
@ -598,6 +676,12 @@ struct GB_gameboy_internal_s {
|
|||||||
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
|
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
|
||||||
uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units
|
uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units
|
||||||
double clock_multiplier;
|
double clock_multiplier;
|
||||||
|
GB_rumble_mode_t rumble_mode;
|
||||||
|
uint32_t rumble_on_cycles;
|
||||||
|
uint32_t rumble_off_cycles;
|
||||||
|
|
||||||
|
/* Temporary state */
|
||||||
|
bool wx_just_changed;
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -617,13 +701,14 @@ __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(GB_gameboy_t *gb);
|
bool GB_is_cgb(GB_gameboy_t *gb);
|
||||||
bool GB_is_sgb(GB_gameboy_t *gb);
|
bool GB_is_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2
|
||||||
|
bool GB_is_hle_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 and the SFC/SNES side is HLE'd
|
||||||
GB_model_t GB_get_model(GB_gameboy_t *gb);
|
GB_model_t GB_get_model(GB_gameboy_t *gb);
|
||||||
void GB_free(GB_gameboy_t *gb);
|
void GB_free(GB_gameboy_t *gb);
|
||||||
void GB_reset(GB_gameboy_t *gb);
|
void GB_reset(GB_gameboy_t *gb);
|
||||||
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model);
|
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model);
|
||||||
|
|
||||||
/* Returns the time passed, in 4MHz ticks. */
|
/* Returns the time passed, in 8MHz ticks. */
|
||||||
uint8_t GB_run(GB_gameboy_t *gb);
|
uint8_t GB_run(GB_gameboy_t *gb);
|
||||||
/* Returns the time passed since the last frame, in nanoseconds */
|
/* Returns the time passed since the last frame, in nanoseconds */
|
||||||
uint64_t GB_run_frame(GB_gameboy_t *gb);
|
uint64_t GB_run_frame(GB_gameboy_t *gb);
|
||||||
@ -654,8 +739,14 @@ 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);
|
||||||
|
int GB_load_isx(GB_gameboy_t *gb, const char *path);
|
||||||
|
|
||||||
|
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(GB_gameboy_t *gb, const char *path);
|
int GB_save_battery(GB_gameboy_t *gb, const char *path);
|
||||||
|
|
||||||
|
void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
|
||||||
void GB_load_battery(GB_gameboy_t *gb, const char *path);
|
void GB_load_battery(GB_gameboy_t *gb, const char *path);
|
||||||
|
|
||||||
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip);
|
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip);
|
||||||
@ -665,9 +756,10 @@ void GB_log(GB_gameboy_t *gb, const char *fmt, ...) __printflike(2, 3);
|
|||||||
void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) __printflike(3, 4);
|
void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) __printflike(3, 4);
|
||||||
|
|
||||||
void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output);
|
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_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, long cycles_after_previous_change); /* In 8MHz units*/
|
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);
|
||||||
@ -677,6 +769,10 @@ void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callb
|
|||||||
void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback);
|
void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback);
|
||||||
void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback);
|
void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback);
|
||||||
void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback);
|
void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback);
|
||||||
|
/* 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_palette(GB_gameboy_t *gb, const GB_palette_t *palette);
|
||||||
|
|
||||||
/* 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);
|
||||||
@ -687,7 +783,16 @@ bool GB_serial_get_data_bit(GB_gameboy_t *gb);
|
|||||||
void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data);
|
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 integration with SFC/SNES emulators */
|
||||||
|
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_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);
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
uint32_t GB_get_clock_rate(GB_gameboy_t *gb);
|
uint32_t GB_get_clock_rate(GB_gameboy_t *gb);
|
||||||
#endif
|
#endif
|
||||||
|
522
Core/graphics/agb_border.inc
Normal file
522
Core/graphics/agb_border.inc
Normal file
@ -0,0 +1,522 @@
|
|||||||
|
static const uint16_t palette[] = {
|
||||||
|
0x410A, 0x0421, 0x35AD, 0x4A52, 0x7FFF, 0x2D49, 0x0C42, 0x1484,
|
||||||
|
0x18A5, 0x20C6, 0x6718, 0x5D6E, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint16_t tilemap[] = {
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0001, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004,
|
||||||
|
0x0004, 0x0004, 0x0004, 0x0004, 0x0005, 0x0005, 0x0005, 0x0005,
|
||||||
|
0x0005, 0x0005, 0x0005, 0x0005, 0x0004, 0x0004, 0x0004, 0x0004,
|
||||||
|
0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x4002, 0x4001, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0007, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
|
||||||
|
0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
|
||||||
|
0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
|
||||||
|
0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x4007, 0x4006, 0x0000,
|
||||||
|
0x0000, 0x0009, 0x0008, 0x0008, 0x0008, 0x000A, 0x000B, 0x000B,
|
||||||
|
0x000B, 0x000B, 0x000B, 0x000B, 0x000B, 0x000B, 0x000B, 0x000B,
|
||||||
|
0x000B, 0x000B, 0x000B, 0x000B, 0x000B, 0x000B, 0x000B, 0x000B,
|
||||||
|
0x000B, 0x000B, 0x400A, 0x0008, 0x0008, 0x0008, 0xC009, 0x0000,
|
||||||
|
0x0000, 0x000C, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x400C, 0x0000,
|
||||||
|
0x0000, 0x000E, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC00E, 0x0000,
|
||||||
|
0x0000, 0x000F, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x400F, 0x0000,
|
||||||
|
0x0000, 0x0010, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC010, 0x0000,
|
||||||
|
0x0000, 0x0010, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC010, 0x0000,
|
||||||
|
0x0000, 0x0011, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC011, 0x0000,
|
||||||
|
0x0000, 0x0011, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC011, 0x0000,
|
||||||
|
0x0000, 0x0012, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x4012, 0x0000,
|
||||||
|
0x0000, 0x0013, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC013, 0x0000,
|
||||||
|
0x0014, 0x0015, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x4015, 0x4014,
|
||||||
|
0x0016, 0x0017, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC017, 0xC016,
|
||||||
|
0x0016, 0x0017, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC017, 0xC016,
|
||||||
|
0x0018, 0x0019, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x4019, 0x4018,
|
||||||
|
0x001A, 0x001B, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0xC01B, 0xC01A,
|
||||||
|
0x001C, 0x001D, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x401D, 0x401C,
|
||||||
|
0x001E, 0x0008, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x0008, 0xC01E,
|
||||||
|
0x001E, 0x0008, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x0008, 0xC01E,
|
||||||
|
0x001E, 0x0008, 0x0008, 0x0008, 0x0008, 0x000D, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC00D, 0x0008, 0x0008, 0x0008, 0x0008, 0xC01E,
|
||||||
|
0x001F, 0x801D, 0x0008, 0x0008, 0x0008, 0x0020, 0x0021, 0x0022,
|
||||||
|
0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A,
|
||||||
|
0x002B, 0x002C, 0x002D, 0x002D, 0x002D, 0x002D, 0x002D, 0x002D,
|
||||||
|
0x002E, 0x0021, 0x4020, 0x0008, 0x0008, 0x0008, 0xC01D, 0x401F,
|
||||||
|
0x002F, 0x0030, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0031,
|
||||||
|
0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039,
|
||||||
|
0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041,
|
||||||
|
0x0042, 0x0043, 0x0008, 0x0008, 0x0008, 0x0008, 0x4030, 0x402F,
|
||||||
|
0x0044, 0x0045, 0x0046, 0x0047, 0x0008, 0x0008, 0x0048, 0x0049,
|
||||||
|
0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051,
|
||||||
|
0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059,
|
||||||
|
0x005A, 0x005B, 0x0008, 0x0008, 0x4047, 0x4046, 0x4045, 0x4044,
|
||||||
|
0x0000, 0x0000, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061,
|
||||||
|
0x0061, 0x0062, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063,
|
||||||
|
0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x4062, 0x0061,
|
||||||
|
0x0061, 0x4060, 0x405F, 0x405E, 0x405D, 0x405C, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1F, 0x01,
|
||||||
|
0x7F, 0x1F, 0xFF, 0x7E, 0xFF, 0xE1, 0xFF, 0x9F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x1E,
|
||||||
|
0x1F, 0x60, 0x7F, 0x80, 0xFF, 0x00, 0xBF, 0x40,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x03, 0x01, 0x07, 0x03, 0x07, 0x03, 0x07, 0x06,
|
||||||
|
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
|
||||||
|
0x01, 0x02, 0x03, 0x04, 0x03, 0x04, 0x06, 0x01,
|
||||||
|
0x06, 0x01, 0x06, 0x01, 0x06, 0x01, 0x06, 0x01,
|
||||||
|
0xFF, 0xBF, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xBF, 0x40, 0x7F, 0x80, 0x7F, 0x80, 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, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E,
|
||||||
|
0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E,
|
||||||
|
0x0E, 0x01, 0x0E, 0x01, 0x0E, 0x01, 0x0E, 0x01,
|
||||||
|
0x0E, 0x01, 0x0E, 0x01, 0x0E, 0x01, 0x0E, 0x01,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFE, 0x00,
|
||||||
|
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, 0xFF, 0x00, 0x00, 0x00,
|
||||||
|
0x0F, 0x0E, 0x0F, 0x0E, 0x1F, 0x1B, 0x1F, 0x1B,
|
||||||
|
0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B,
|
||||||
|
0x0E, 0x01, 0x0E, 0x01, 0x1B, 0x04, 0x1B, 0x04,
|
||||||
|
0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04,
|
||||||
|
0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
|
||||||
|
0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
|
||||||
|
0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00,
|
||||||
|
0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00,
|
||||||
|
0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B,
|
||||||
|
0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B,
|
||||||
|
0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04,
|
||||||
|
0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04,
|
||||||
|
0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B, 0x1F, 0x1B,
|
||||||
|
0x1F, 0x1B, 0x3F, 0x37, 0x3F, 0x37, 0x3F, 0x37,
|
||||||
|
0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04, 0x1B, 0x04,
|
||||||
|
0x1B, 0x04, 0x37, 0x08, 0x37, 0x08, 0x37, 0x08,
|
||||||
|
0x3F, 0x37, 0x3F, 0x37, 0x3F, 0x37, 0x3F, 0x37,
|
||||||
|
0x3F, 0x37, 0x3F, 0x37, 0x3F, 0x37, 0x3F, 0x37,
|
||||||
|
0x37, 0x08, 0x37, 0x08, 0x37, 0x08, 0x37, 0x08,
|
||||||
|
0x37, 0x08, 0x37, 0x08, 0x37, 0x08, 0x37, 0x08,
|
||||||
|
0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F,
|
||||||
|
0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F,
|
||||||
|
0x6F, 0x10, 0x6F, 0x10, 0x6F, 0x10, 0x6F, 0x10,
|
||||||
|
0x6F, 0x10, 0x6F, 0x10, 0x6F, 0x10, 0x6F, 0x10,
|
||||||
|
0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0xFF, 0xDF,
|
||||||
|
0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF,
|
||||||
|
0x6F, 0x10, 0x6F, 0x10, 0x6F, 0x10, 0xDF, 0x20,
|
||||||
|
0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20,
|
||||||
|
0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF,
|
||||||
|
0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF,
|
||||||
|
0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20,
|
||||||
|
0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
|
||||||
|
0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xDF,
|
||||||
|
0xFF, 0xDF, 0xFF, 0xDF, 0xFF, 0xBF, 0xFF, 0xBF,
|
||||||
|
0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20, 0xDF, 0x20,
|
||||||
|
0xDF, 0x20, 0xDF, 0x20, 0xBF, 0x40, 0xBF, 0x40,
|
||||||
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
|
||||||
|
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
|
||||||
|
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
|
||||||
|
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
|
||||||
|
0xBF, 0x40, 0xBF, 0x40, 0xBF, 0x40, 0xBF, 0x40,
|
||||||
|
0xBF, 0x40, 0xBF, 0x40, 0xBF, 0x40, 0xBF, 0x40,
|
||||||
|
0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||||
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||||
|
0x01, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
|
||||||
|
0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
|
||||||
|
0xFF, 0xBF, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
|
||||||
|
0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
|
||||||
|
0xBF, 0x40, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
|
||||||
|
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
|
||||||
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||||
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||||
|
0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
|
||||||
|
0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
|
||||||
|
0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
|
||||||
|
0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
|
||||||
|
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
|
||||||
|
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
|
||||||
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||||
|
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
|
||||||
|
0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
|
||||||
|
0x06, 0x01, 0x06, 0x01, 0x06, 0x01, 0x06, 0x01,
|
||||||
|
0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
|
||||||
|
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
|
||||||
|
0x06, 0x01, 0x06, 0x01, 0x06, 0x01, 0x06, 0x01,
|
||||||
|
0x06, 0x01, 0x06, 0x01, 0x06, 0x01, 0x06, 0x01,
|
||||||
|
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
|
||||||
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||||
|
0x06, 0x01, 0x06, 0x01, 0x06, 0x01, 0x06, 0x01,
|
||||||
|
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
|
||||||
|
0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFE, 0x01, 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, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xF8, 0xFF, 0xE7, 0xF8, 0xDF, 0xE3,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xF8, 0x00, 0xE4, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x3F, 0xFF, 0xCE, 0x3F, 0xF5, 0x8E,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x3F, 0x00, 0x4E, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x1F, 0xFF, 0xEE, 0x1F, 0xB5, 0x4E,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x1F, 0x00, 0x0E, 0xA0,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x07, 0xFF, 0xFB, 0x07, 0x04, 0x73,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x07, 0x00, 0x03, 0x88,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x80, 0xFF, 0x7F, 0x80, 0x82, 0x39,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x80, 0x00, 0x01, 0x44,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x01, 0xFE,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x83, 0x7C,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x42, 0x01,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFE, 0xFF, 0xBB, 0x7C, 0x4F, 0xB0,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x7C, 0x00, 0xB1, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x07, 0xFF, 0xF9, 0x06, 0xE7, 0xF8,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x06, 0x00, 0x08, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x0E, 0xFF, 0xF5, 0x0E, 0x9B, 0x74,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x0E, 0x00, 0x94, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x1F, 0xFF, 0xF7, 0x0F, 0xBF, 0x47,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x0F, 0x00, 0xA7, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 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, 0x01, 0xFF,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||||
|
0x07, 0x07, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01,
|
||||||
|
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
|
||||||
|
0x03, 0x04, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01,
|
||||||
|
0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F,
|
||||||
|
0xFF, 0x7F, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xDF,
|
||||||
|
0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80, 0x7F, 0x80,
|
||||||
|
0x7F, 0x80, 0xBF, 0x40, 0xBF, 0x40, 0xDF, 0x20,
|
||||||
|
0xB0, 0xD8, 0xA0, 0xD3, 0x67, 0x84, 0x47, 0xA4,
|
||||||
|
0x61, 0x81, 0xA0, 0xD0, 0xB4, 0xCA, 0x7E, 0x81,
|
||||||
|
0xD7, 0x08, 0xCC, 0x13, 0x98, 0x20, 0x98, 0x00,
|
||||||
|
0x9E, 0x20, 0xCF, 0x00, 0xCD, 0x02, 0x80, 0x01,
|
||||||
|
0x32, 0x2D, 0x13, 0x6D, 0x34, 0x48, 0xFC, 0x02,
|
||||||
|
0x7C, 0x00, 0x78, 0x05, 0x30, 0x49, 0x20, 0x50,
|
||||||
|
0xCD, 0x00, 0xAC, 0x40, 0x49, 0x82, 0x01, 0x02,
|
||||||
|
0x07, 0x80, 0xC2, 0x05, 0x86, 0x41, 0x9F, 0x40,
|
||||||
|
0x15, 0x2E, 0x09, 0x06, 0x09, 0x16, 0x0B, 0xD4,
|
||||||
|
0xC6, 0x49, 0x8E, 0x40, 0xCF, 0xC8, 0x06, 0x01,
|
||||||
|
0xCE, 0x20, 0xE6, 0x10, 0xE6, 0x00, 0x24, 0xD0,
|
||||||
|
0x39, 0x80, 0x38, 0x01, 0x31, 0x00, 0xF8, 0x00,
|
||||||
|
0x0C, 0x8B, 0x85, 0x8A, 0x03, 0x84, 0x27, 0x20,
|
||||||
|
0x22, 0x35, 0x12, 0x34, 0x20, 0x12, 0x10, 0x20,
|
||||||
|
0x73, 0x00, 0x72, 0x08, 0x7C, 0x80, 0xDC, 0x01,
|
||||||
|
0xC8, 0x11, 0xC9, 0x06, 0xCD, 0x22, 0xEF, 0x10,
|
||||||
|
0x83, 0x44, 0x86, 0x01, 0x03, 0x85, 0x26, 0x21,
|
||||||
|
0x46, 0x69, 0x46, 0x68, 0x8E, 0xCA, 0x86, 0x88,
|
||||||
|
0x39, 0x40, 0x78, 0x84, 0x7C, 0x80, 0xD8, 0x01,
|
||||||
|
0x90, 0x29, 0xD1, 0x28, 0x73, 0x00, 0xB3, 0x40,
|
||||||
|
0x00, 0x01, 0x01, 0x00, 0x3F, 0x00, 0x3F, 0x40,
|
||||||
|
0x03, 0x02, 0x01, 0x02, 0x41, 0x7C, 0x7F, 0x00,
|
||||||
|
0xFE, 0x00, 0xFF, 0x00, 0xC0, 0x00, 0x80, 0x40,
|
||||||
|
0xFC, 0x00, 0xFC, 0x00, 0x80, 0x02, 0xC0, 0x00,
|
||||||
|
0xC0, 0x00, 0x80, 0x4C, 0xCC, 0x43, 0x8E, 0x52,
|
||||||
|
0x80, 0x4C, 0x80, 0x00, 0x12, 0x1E, 0x9E, 0x00,
|
||||||
|
0x7F, 0x00, 0x33, 0x0C, 0x32, 0x01, 0x23, 0x50,
|
||||||
|
0x33, 0x4C, 0x7F, 0x00, 0x61, 0x80, 0xF1, 0x00,
|
||||||
|
0x7C, 0x02, 0x30, 0x48, 0x31, 0x40, 0x61, 0x50,
|
||||||
|
0x87, 0xE4, 0xE3, 0x84, 0x23, 0x44, 0x43, 0x44,
|
||||||
|
0x85, 0x42, 0x87, 0x40, 0x8F, 0x50, 0x8C, 0x12,
|
||||||
|
0x78, 0x00, 0x18, 0x20, 0xB8, 0x00, 0x98, 0x24,
|
||||||
|
0x03, 0x04, 0x03, 0xE0, 0xF1, 0x12, 0xF0, 0x09,
|
||||||
|
0xF9, 0x09, 0xF9, 0x08, 0xE1, 0x12, 0xF1, 0x12,
|
||||||
|
0xF8, 0x00, 0x1E, 0xE0, 0x0C, 0x02, 0x07, 0x08,
|
||||||
|
0x07, 0x00, 0x06, 0x00, 0x1C, 0x02, 0x0C, 0x00,
|
||||||
|
0x9F, 0x91, 0x86, 0x88, 0xC4, 0x4C, 0x80, 0x4C,
|
||||||
|
0xE1, 0x20, 0xC1, 0x22, 0x23, 0xD4, 0x22, 0xD5,
|
||||||
|
0x60, 0x00, 0xFB, 0x00, 0x37, 0x00, 0x73, 0x0C,
|
||||||
|
0x1F, 0x00, 0x3C, 0x00, 0xC8, 0x14, 0xC9, 0x14,
|
||||||
|
0x16, 0x2F, 0x76, 0x4F, 0x2D, 0xDE, 0xDD, 0xBE,
|
||||||
|
0xBA, 0x7D, 0x7A, 0xFD, 0x7A, 0xFD, 0xF4, 0xF8,
|
||||||
|
0xCF, 0x00, 0x8F, 0x00, 0x5E, 0x80, 0xBE, 0x00,
|
||||||
|
0x7D, 0x00, 0xFC, 0x00, 0xFC, 0x01, 0xF9, 0x02,
|
||||||
|
0xFF, 0x00, 0xBF, 0x78, 0x86, 0x09, 0x86, 0x89,
|
||||||
|
0x06, 0x25, 0x02, 0x25, 0x42, 0x45, 0x60, 0x11,
|
||||||
|
0x00, 0x00, 0x09, 0x00, 0x70, 0x81, 0x70, 0x09,
|
||||||
|
0xDC, 0x21, 0xD8, 0x01, 0x98, 0x25, 0xCC, 0x13,
|
||||||
|
0xFF, 0x00, 0xF3, 0xF8, 0x02, 0x03, 0x01, 0x30,
|
||||||
|
0x39, 0x09, 0x30, 0x09, 0x31, 0x09, 0x20, 0x19,
|
||||||
|
0x00, 0x00, 0x01, 0x04, 0xFC, 0x00, 0xCF, 0x30,
|
||||||
|
0xE6, 0x00, 0xE6, 0x01, 0xE6, 0x00, 0xF6, 0x08,
|
||||||
|
0xFF, 0x00, 0xFA, 0xC7, 0x18, 0x21, 0x09, 0x10,
|
||||||
|
0x88, 0x99, 0x93, 0x1A, 0x83, 0x11, 0xC2, 0x41,
|
||||||
|
0x00, 0x00, 0x00, 0x20, 0xC6, 0x21, 0xFF, 0x00,
|
||||||
|
0x67, 0x00, 0xE4, 0x08, 0x6F, 0x10, 0x3C, 0x00,
|
||||||
|
0xFD, 0x02, 0xB5, 0x3A, 0xC7, 0x44, 0x03, 0x84,
|
||||||
|
0x83, 0x24, 0x21, 0xB0, 0x21, 0x12, 0x21, 0x02,
|
||||||
|
0x02, 0x00, 0x02, 0x40, 0x3C, 0x00, 0xF8, 0x00,
|
||||||
|
0xD8, 0x24, 0x4C, 0x92, 0xEC, 0x00, 0xCC, 0x12,
|
||||||
|
0xFF, 0x00, 0xFF, 0xF3, 0x1C, 0x14, 0x0C, 0x04,
|
||||||
|
0x00, 0x0C, 0x04, 0x24, 0x00, 0x24, 0x10, 0x30,
|
||||||
|
0x00, 0x00, 0x10, 0x04, 0xE3, 0x00, 0xFB, 0x00,
|
||||||
|
0xF3, 0x08, 0xDB, 0x20, 0xDB, 0x04, 0xCF, 0x00,
|
||||||
|
0xFF, 0x00, 0xEC, 0x3E, 0xC1, 0x01, 0x01, 0x8E,
|
||||||
|
0x8F, 0x10, 0x0F, 0x90, 0x0F, 0x90, 0x0D, 0x09,
|
||||||
|
0x00, 0x00, 0x20, 0x01, 0x7E, 0x00, 0xF1, 0x0E,
|
||||||
|
0xE0, 0x10, 0x60, 0x10, 0x60, 0x10, 0x79, 0x82,
|
||||||
|
0xFF, 0x00, 0x7F, 0xFC, 0x03, 0x82, 0x01, 0x9E,
|
||||||
|
0x13, 0x80, 0x03, 0x80, 0x03, 0x9C, 0x0F, 0x90,
|
||||||
|
0x00, 0x00, 0x02, 0x00, 0x7C, 0x80, 0x60, 0x9C,
|
||||||
|
0x60, 0x9C, 0x7C, 0x80, 0x60, 0x9C, 0x70, 0x80,
|
||||||
|
0xFF, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF,
|
||||||
|
0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xEF, 0xFF, 0xF7, 0x7F, 0x7B, 0x3F, 0x3C,
|
||||||
|
0x1F, 0x1F, 0x0F, 0x0F, 0x03, 0x03, 0x00, 0x00,
|
||||||
|
0xEF, 0x10, 0x77, 0x88, 0x3B, 0x44, 0x1C, 0x23,
|
||||||
|
0x0F, 0x10, 0x03, 0x0C, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0x3F, 0xFF, 0xC3, 0xFF, 0xFC, 0xFF, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x3F, 0xC0, 0xC3, 0x3C, 0xFC, 0x03, 0x3F, 0xC0,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xC1,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x3F, 0xC0, 0xC1, 0x3E,
|
||||||
|
0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 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, 0xFF, 0x00,
|
||||||
|
0xEA, 0x14, 0xC0, 0x00, 0x80, 0x21, 0x7F, 0x92,
|
||||||
|
0x9F, 0xE0, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x27, 0x18, 0x7F, 0x00, 0x1E, 0x61, 0x9A, 0x04,
|
||||||
|
0xE0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x73, 0x53, 0x47, 0x44, 0x46, 0x25, 0xFD, 0x03,
|
||||||
|
0xF9, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x8C, 0x00, 0xD8, 0x20, 0x1D, 0xA0, 0x03, 0x00,
|
||||||
|
0x07, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xC7, 0xE1, 0xE6, 0x05, 0x42, 0xA5, 0xBF, 0xC0,
|
||||||
|
0x9F, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x18, 0x24, 0x38, 0x01, 0xB8, 0x05, 0xC0, 0x00,
|
||||||
|
0xE0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x21, 0x11, 0x31, 0x49, 0x33, 0x4A, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xDE, 0x00, 0x87, 0x48, 0x84, 0x48, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xCC, 0x02, 0x8E, 0x4A, 0xCC, 0x42, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x71, 0x08, 0x39, 0x00, 0x31, 0x02, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x3D, 0x40, 0x03, 0x02, 0x03, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xBC, 0x02, 0xFC, 0x00, 0xFE, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x12, 0x82, 0x80, 0x80, 0x01, 0x83, 0xFF, 0x00,
|
||||||
|
0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x61, 0x1C, 0x7F, 0x00, 0x7C, 0x82, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x22, 0x52, 0x30, 0xC0, 0x58, 0xA4, 0x8F, 0x72,
|
||||||
|
0x73, 0xFC, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x8C, 0x11, 0x4F, 0x90, 0xA3, 0x0C, 0x73, 0x00,
|
||||||
|
0xFC, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x23, 0xA4, 0x06, 0x0D, 0x05, 0x1B, 0xBB, 0x07,
|
||||||
|
0xE7, 0x1F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x98, 0x44, 0xF5, 0x08, 0xEB, 0x00, 0x87, 0x40,
|
||||||
|
0x1F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x66, 0x85, 0xE2, 0xA5, 0x66, 0x81, 0xBF, 0xC1,
|
||||||
|
0x99, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x99, 0x00, 0xB9, 0x00, 0x9D, 0x20, 0xC1, 0x00,
|
||||||
|
0xE7, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xF6, 0xFA, 0xFC, 0xF2, 0xF7, 0xF8, 0xFB, 0xFC,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xF9, 0x00, 0xF1, 0x02, 0xF8, 0x00, 0xFC, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x52, 0x53, 0x30, 0x23, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x8C, 0x21, 0xCC, 0x13, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0x03, 0x03, 0x06, 0xFE, 0x01, 0xF9, 0x07,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFD, 0x02, 0xFA, 0x04, 0x01, 0x00, 0x07, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x86, 0x05, 0x46, 0xA0, 0x5F, 0xB8, 0xBF, 0xC0,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x38, 0x41, 0x99, 0x26, 0xB8, 0x00, 0xC0, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x30, 0x28, 0x09, 0x09, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xC6, 0x09, 0xE6, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x20, 0x38, 0x38, 0x20, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xD7, 0x08, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x80, 0x41, 0xA1, 0x61, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x3E, 0x40, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x01, 0x82, 0x01, 0x82, 0xFF, 0x00, 0xFC, 0x03,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x7C, 0x82, 0x7C, 0x82, 0x00, 0x00, 0x03, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 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, 0xFF, 0x00,
|
||||||
|
0x3F, 0x3F, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0x3C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xFE, 0xFF, 0xFF, 0x3F, 0x3F, 0x01, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFE, 0x01, 0x3F, 0xC0, 0x01, 0x3E, 0x00, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x3F, 0xC0, 0xC0, 0x3F, 0xFF, 0x00, 0x3F, 0xC0,
|
||||||
|
0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0x00, 0x3F, 0xC0, 0xC0, 0x3F, 0xFF, 0x00,
|
||||||
|
0x3F, 0xC0, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFC,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0x00, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x03, 0xFC, 0xFC, 0x03,
|
||||||
|
0xFF, 0x00, 0x03, 0xFC, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03,
|
||||||
|
0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x03, 0xFC,
|
||||||
|
0xFC, 0x03, 0xFF, 0x00, 0x03, 0xFC, 0x00, 0x03,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF,
|
||||||
|
};
|
446
Core/graphics/cgb_border.inc
Normal file
446
Core/graphics/cgb_border.inc
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
static const uint16_t palette[] = {
|
||||||
|
0x7C1A, 0x0000, 0x0011, 0x3DEF, 0x6318, 0x7FFF, 0x1EBA, 0x19AF,
|
||||||
|
0x1EAF, 0x4648, 0x7FC0, 0x2507, 0x1484, 0x5129, 0x5010, 0x2095,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint16_t tilemap[] = {
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0001, 0x0002, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003,
|
||||||
|
0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003,
|
||||||
|
0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003,
|
||||||
|
0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x4002, 0x4001, 0x0000,
|
||||||
|
0x0000, 0x0004, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
|
||||||
|
0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
|
||||||
|
0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
|
||||||
|
0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x4004, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0007, 0x0008, 0x0008,
|
||||||
|
0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
|
||||||
|
0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
|
||||||
|
0x0008, 0x0008, 0x4007, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x000A, 0x000B, 0x400A, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x800A, 0x000C, 0xC00A, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x0006, 0x0005, 0x0005, 0x0005, 0x0009, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC009, 0x0005, 0x0005, 0x0005, 0xC006, 0x0000,
|
||||||
|
0x0000, 0x000D, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x000E,
|
||||||
|
0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016,
|
||||||
|
0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E,
|
||||||
|
0x001F, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x400D, 0x0000,
|
||||||
|
0x0000, 0x0020, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0021,
|
||||||
|
0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029,
|
||||||
|
0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031,
|
||||||
|
0x0032, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x4020, 0x0000,
|
||||||
|
0x0000, 0x0033, 0x0034, 0x0035, 0x0036, 0x0005, 0x0005, 0x0037,
|
||||||
|
0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
|
||||||
|
0x0040, 0x0005, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046,
|
||||||
|
0x0047, 0x0005, 0x0005, 0x4036, 0x4035, 0x4034, 0x4033, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D,
|
||||||
|
0x004E, 0x004E, 0x004F, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050,
|
||||||
|
0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x404F, 0x004E, 0x004E,
|
||||||
|
0x404D, 0x004C, 0x404B, 0x404A, 0x4049, 0x4048, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08,
|
||||||
|
0x01, 0x11, 0x06, 0x26, 0x04, 0x24, 0x08, 0x48,
|
||||||
|
0x00, 0x00, 0x01, 0x01, 0x07, 0x07, 0x0F, 0x0F,
|
||||||
|
0x1E, 0x1F, 0x39, 0x3F, 0x3B, 0x3F, 0x77, 0x7F,
|
||||||
|
0x00, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x7F, 0x7F,
|
||||||
|
0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF,
|
||||||
|
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x08, 0x48, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||||
|
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||||
|
0x77, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F,
|
||||||
|
0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||||
|
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||||
|
0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F,
|
||||||
|
0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
|
||||||
|
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
|
||||||
|
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||||
|
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42,
|
||||||
|
0xBD, 0xBD, 0x7E, 0x66, 0x7E, 0xFF, 0x7E, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD, 0xFF,
|
||||||
|
0x7E, 0xFF, 0xFF, 0xE7, 0x7E, 0x7E, 0x7E, 0x7E,
|
||||||
|
0x7E, 0xFF, 0x3C, 0xFF, 0x81, 0xFF, 0xC3, 0xFF,
|
||||||
|
0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x7E, 0x7E, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x81,
|
||||||
|
0x81, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||||
|
0x08, 0x48, 0x08, 0x48, 0x08, 0x48, 0x08, 0x48,
|
||||||
|
0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F, 0x6F, 0x7F,
|
||||||
|
0x37, 0x7F, 0x37, 0x7F, 0x37, 0x7F, 0x37, 0x7F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFE,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0x3F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x70,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0xDF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x78,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xC7,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xE3,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x9F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0xE1,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xD8,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x27, 0x8F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x70,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x8F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x70,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x9F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xC2,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x10,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x3F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xE0,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x84,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x08,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xFF,
|
||||||
|
0x08, 0x48, 0x08, 0x48, 0x08, 0x48, 0x08, 0x48,
|
||||||
|
0x08, 0x48, 0x04, 0x24, 0x04, 0x24, 0x02, 0x12,
|
||||||
|
0x37, 0x7F, 0x37, 0x7F, 0x37, 0x7F, 0x37, 0x7F,
|
||||||
|
0x37, 0x7F, 0x1B, 0x3F, 0x1B, 0x3F, 0x0D, 0x1F,
|
||||||
|
0x0F, 0x08, 0x0E, 0x00, 0x1E, 0x12, 0x1E, 0x12,
|
||||||
|
0x1F, 0x10, 0x0F, 0x08, 0x02, 0x02, 0x00, 0x00,
|
||||||
|
0xF7, 0xF8, 0xFF, 0xF0, 0xED, 0xE3, 0xED, 0xE1,
|
||||||
|
0xEF, 0xE0, 0xF7, 0xF0, 0xFD, 0xFC, 0xFF, 0xFF,
|
||||||
|
0xF0, 0x10, 0x40, 0x00, 0x41, 0x41, 0x00, 0x00,
|
||||||
|
0x83, 0x82, 0xE3, 0x20, 0xC7, 0x04, 0xC7, 0x00,
|
||||||
|
0xEF, 0x1F, 0xFF, 0x1F, 0xBE, 0xFF, 0xFF, 0xFE,
|
||||||
|
0x7D, 0x7E, 0xDF, 0x3C, 0xFB, 0x18, 0xFF, 0x18,
|
||||||
|
0x60, 0x00, 0x70, 0x00, 0xF8, 0x08, 0xB0, 0x00,
|
||||||
|
0xD8, 0x40, 0x3C, 0x24, 0x5C, 0x44, 0xFC, 0x00,
|
||||||
|
0xFF, 0x8F, 0xFF, 0x0F, 0xF7, 0x07, 0xFF, 0x07,
|
||||||
|
0xBF, 0x47, 0xDB, 0x47, 0xBB, 0x03, 0xFF, 0x03,
|
||||||
|
0x3C, 0x04, 0x78, 0x00, 0x78, 0x00, 0xEC, 0x80,
|
||||||
|
0xFE, 0x92, 0xE7, 0x83, 0xE5, 0x80, 0x4F, 0x08,
|
||||||
|
0xFB, 0x83, 0xFF, 0x83, 0xFF, 0x83, 0x7F, 0x83,
|
||||||
|
0x6D, 0x93, 0x7C, 0x10, 0x7F, 0x10, 0xF7, 0x10,
|
||||||
|
0x3C, 0x00, 0x7C, 0x40, 0x78, 0x00, 0xC8, 0x80,
|
||||||
|
0xFC, 0x24, 0xBC, 0x24, 0xFD, 0x65, 0x3D, 0x25,
|
||||||
|
0xFF, 0xC3, 0xBF, 0x83, 0xFF, 0x83, 0x7F, 0x03,
|
||||||
|
0xDB, 0x23, 0xDB, 0x23, 0x9A, 0x67, 0xDA, 0x47,
|
||||||
|
0xFF, 0x80, 0xFF, 0x80, 0xE0, 0x80, 0x40, 0x00,
|
||||||
|
0xFF, 0x01, 0xFF, 0x01, 0xDF, 0x1F, 0xE0, 0x20,
|
||||||
|
0x7F, 0x80, 0x7F, 0x00, 0x7F, 0x1F, 0xFF, 0x1F,
|
||||||
|
0xFE, 0x00, 0xFE, 0x00, 0xE0, 0x01, 0xDF, 0x3F,
|
||||||
|
0xBF, 0xA0, 0xB9, 0xA0, 0x10, 0x00, 0x11, 0x01,
|
||||||
|
0x3B, 0x00, 0x3F, 0x00, 0x7E, 0x4E, 0x78, 0x48,
|
||||||
|
0x5F, 0x40, 0x5F, 0xC0, 0xFF, 0xC7, 0xFE, 0xC7,
|
||||||
|
0xFF, 0xC0, 0xFF, 0xC0, 0xB1, 0xC4, 0xB7, 0x8F,
|
||||||
|
0xE3, 0x22, 0xC7, 0x04, 0xCE, 0x08, 0xE6, 0x20,
|
||||||
|
0xCE, 0x42, 0xDE, 0x52, 0xFE, 0x32, 0xFC, 0x30,
|
||||||
|
0xDD, 0x3E, 0xFB, 0x18, 0xF7, 0x18, 0xDF, 0x31,
|
||||||
|
0xBD, 0x31, 0xAD, 0x23, 0xCD, 0x31, 0xCF, 0x11,
|
||||||
|
0xFE, 0x02, 0x9E, 0x00, 0x86, 0x80, 0x06, 0x00,
|
||||||
|
0x03, 0x00, 0x02, 0x00, 0x07, 0x01, 0x07, 0x01,
|
||||||
|
0xFD, 0x03, 0xFF, 0x01, 0x7F, 0xF0, 0xFF, 0xF8,
|
||||||
|
0xFF, 0xF8, 0xFF, 0xF8, 0xFE, 0xF8, 0xFE, 0xF1,
|
||||||
|
0x38, 0x08, 0x71, 0x41, 0x1F, 0x06, 0x39, 0x20,
|
||||||
|
0x0F, 0x00, 0x0F, 0x01, 0x04, 0x00, 0x0C, 0x00,
|
||||||
|
0xF7, 0x87, 0xBE, 0xC6, 0xF9, 0xC6, 0xDF, 0xE0,
|
||||||
|
0xFF, 0xE0, 0xFE, 0xF1, 0xFF, 0xF1, 0xFF, 0xF1,
|
||||||
|
0x70, 0x10, 0xE0, 0x20, 0x80, 0x00, 0x80, 0x00,
|
||||||
|
0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xEF, 0x1F, 0xDF, 0x1F, 0xFF, 0x3F, 0xFF, 0x7F,
|
||||||
|
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x3F, 0x3F, 0x7F, 0x7F, 0x78, 0x78, 0xF0, 0xF0,
|
||||||
|
0xF0, 0xF0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF0,
|
||||||
|
0xDF, 0xFF, 0xBD, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF,
|
||||||
|
0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF,
|
||||||
|
0xE7, 0xE0, 0xFF, 0xF0, 0xFE, 0xF0, 0x1C, 0x00,
|
||||||
|
0x3C, 0x20, 0x3C, 0x24, 0x3C, 0x24, 0x3C, 0x20,
|
||||||
|
0xFF, 0xFF, 0xEF, 0xFF, 0x6F, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xDF, 0xFF, 0xDB, 0xFF, 0xDB, 0xFF, 0xDF, 0xFF,
|
||||||
|
0xF8, 0x00, 0xFC, 0xC0, 0x1F, 0x03, 0x1F, 0x13,
|
||||||
|
0x1F, 0x13, 0x1E, 0x02, 0x1E, 0x02, 0x3E, 0x02,
|
||||||
|
0xFF, 0xFF, 0x3F, 0xFF, 0xFC, 0xFF, 0xEC, 0xFF,
|
||||||
|
0xED, 0xFE, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0xFF,
|
||||||
|
0x90, 0x90, 0x00, 0x00, 0x00, 0x00, 0x21, 0x21,
|
||||||
|
0x20, 0x21, 0x00, 0x01, 0x00, 0x01, 0x40, 0x41,
|
||||||
|
0x8F, 0x7F, 0x1F, 0xFF, 0x1F, 0xFF, 0x3F, 0xDE,
|
||||||
|
0x1F, 0xFE, 0x3F, 0xFE, 0x3F, 0xFE, 0x7F, 0xBE,
|
||||||
|
0x40, 0x7F, 0x84, 0xFF, 0x11, 0xF1, 0x20, 0xE0,
|
||||||
|
0x20, 0xE0, 0x01, 0xC1, 0x01, 0xC1, 0x22, 0xE3,
|
||||||
|
0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x0E, 0xFF, 0x1F,
|
||||||
|
0xDF, 0x3F, 0xFE, 0x3F, 0xFF, 0x3E, 0xFD, 0x1E,
|
||||||
|
0x47, 0xC0, 0x27, 0xE0, 0x2F, 0xE8, 0x0F, 0xE9,
|
||||||
|
0x0F, 0xE1, 0x0F, 0xE0, 0x3F, 0xF0, 0x3F, 0xF1,
|
||||||
|
0xF8, 0x3F, 0xF8, 0x1F, 0xF0, 0x1F, 0xF0, 0x1F,
|
||||||
|
0xF0, 0x1F, 0xF0, 0x1F, 0xE0, 0x1F, 0xC0, 0x3F,
|
||||||
|
0xFC, 0x00, 0xFE, 0xE2, 0x1E, 0x12, 0x1E, 0x12,
|
||||||
|
0x3E, 0x22, 0xFC, 0x00, 0xF8, 0x08, 0xF0, 0x10,
|
||||||
|
0x03, 0xFF, 0x01, 0xFF, 0xE1, 0xFF, 0xE1, 0xFF,
|
||||||
|
0xC1, 0xFF, 0x03, 0xFF, 0x07, 0xFF, 0x0F, 0xFF,
|
||||||
|
0x01, 0x11, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02,
|
||||||
|
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0E, 0x1F, 0x07, 0x0F, 0x03, 0x07, 0x01, 0x03,
|
||||||
|
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x80, 0x80, 0x40, 0x40, 0x30, 0x30,
|
||||||
|
0x0C, 0x0C, 0x03, 0xC3, 0x00, 0x30, 0x00, 0x0C,
|
||||||
|
0xFF, 0xFF, 0x7F, 0xFF, 0xBF, 0xFF, 0xCF, 0xFF,
|
||||||
|
0xF3, 0xFF, 0x3C, 0xFF, 0x0F, 0x3F, 0x03, 0x0F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xC0, 0xC0, 0x3C, 0x3C, 0x03, 0x03,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x3F, 0xFF, 0xC3, 0xFF, 0xFC, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE0,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF,
|
||||||
|
0x15, 0x15, 0x3F, 0x20, 0x2F, 0x20, 0x06, 0x06,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xEA, 0xE6, 0xDF, 0xC0, 0xDF, 0xE0, 0xF9, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xE6, 0x20, 0x9E, 0x12, 0x0C, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xDF, 0x30, 0xED, 0x31, 0xFF, 0x63, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x0E, 0x02, 0x1E, 0x12, 0x0D, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFD, 0x03, 0xED, 0xE1, 0xFE, 0xF1, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x4F, 0x08, 0xE6, 0x20, 0xE7, 0x21, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xF7, 0x18, 0xDF, 0x18, 0xDE, 0x18, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xB9, 0xA1, 0x11, 0x01, 0x10, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x5E, 0x46, 0xFE, 0xC6, 0xFF, 0xC6, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0x3F, 0xFF, 0x01, 0xFF, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xC0, 0x01, 0xFE, 0x00, 0xFE, 0x00, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x7E, 0x4E, 0x3F, 0x00, 0x3E, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xB1, 0x8E, 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xEE, 0x20, 0x8F, 0x08, 0x85, 0x84, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xDF, 0x30, 0xF7, 0x38, 0x7B, 0x7C, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xAE, 0xA2, 0xF8, 0x00, 0xE8, 0x08, 0xC0, 0xC0,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x5D, 0xE1, 0xFF, 0x03, 0xF7, 0x0F, 0x3F, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x0E, 0x02, 0x1E, 0x12, 0x1E, 0x12, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFD, 0xF1, 0xED, 0xF1, 0xED, 0xE3, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFB, 0xFB, 0x7F, 0x7F, 0x3F, 0x3F, 0x0C, 0x0C,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x75, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xDF, 0xC1, 0xDF, 0xD0, 0x8F, 0x88, 0x01, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFE, 0xFF, 0xEF, 0xFF, 0x77, 0xFF, 0xFE, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFA, 0x82, 0xF8, 0x08, 0xE0, 0x00, 0x81, 0x81,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x7E, 0xFD, 0xF4, 0xFF, 0xFC, 0xFF, 0x7E, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x7D, 0x7D, 0x02, 0x02, 0x02, 0x02, 0xFC, 0xFC,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0xFE, 0x01, 0xFF, 0x01, 0xFF, 0x03, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x1C, 0xFF, 0x00, 0xFF, 0x41, 0x7F, 0x18, 0x18,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xF7, 0x08, 0xFF, 0x00, 0xFF, 0x80, 0xE7, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x5E, 0xC2, 0x9C, 0x80, 0x1C, 0x04, 0x08, 0x08,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xE1, 0x3F, 0xE3, 0x7F, 0xE3, 0xFF, 0xF7, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xF0, 0x80, 0x78, 0x08, 0x78, 0x48, 0x10, 0x10,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0F, 0xFF, 0x87, 0xFF, 0x87, 0xFF, 0xEF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xC0, 0x00, 0x3C, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x3F, 0xFF, 0x03, 0x3F, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x1C, 0x1C, 0x03, 0x03, 0x00, 0xE0, 0x00, 0x1C,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xE3, 0xFF, 0xFC, 0xFF, 0x1F, 0xFF, 0x03, 0x1F,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xFC, 0xFC, 0x03, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0xFC, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0x03, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x03, 0xFF, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x3F, 0x3F,
|
||||||
|
0x00, 0x00, 0x00, 0xC0, 0x00, 0x3F, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xC0, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x3F, 0xFF, 0x00, 0x3F, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0,
|
||||||
|
0x3F, 0x3F, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x3F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF,
|
||||||
|
0xC0, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0x00, 0x3F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF
|
||||||
|
};
|
558
Core/graphics/dmg_border.inc
Normal file
558
Core/graphics/dmg_border.inc
Normal file
@ -0,0 +1,558 @@
|
|||||||
|
static const uint16_t palette[] = {
|
||||||
|
0x0000, 0x0011, 0x18C6, 0x001A, 0x318C, 0x39CE, 0x5294, 0x5AD6,
|
||||||
|
0x739C, 0x45A8, 0x4520, 0x18A5, 0x4A32, 0x2033, 0x20EC, 0x0000,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint16_t tilemap[] = {
|
||||||
|
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
|
||||||
|
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
|
||||||
|
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
|
||||||
|
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
|
||||||
|
0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
|
||||||
|
0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
|
||||||
|
0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
|
||||||
|
0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
|
||||||
|
0x0001, 0x0003, 0x0004, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
|
||||||
|
0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
|
||||||
|
0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
|
||||||
|
0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x4004, 0x4003, 0x0001,
|
||||||
|
0x0001, 0x0006, 0x0007, 0x0007, 0x0007, 0x0008, 0x0009, 0x000A,
|
||||||
|
0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012,
|
||||||
|
0x0013, 0x0014, 0x0015, 0x000E, 0x0016, 0x0017, 0x0018, 0x0019,
|
||||||
|
0x001A, 0x001B, 0x001C, 0x0007, 0x0007, 0x0007, 0x4006, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x001E, 0x001E, 0x001E, 0x001F, 0x0020, 0x0021,
|
||||||
|
0x0022, 0x0023, 0x0024, 0x0025, 0x4024, 0x0026, 0x0025, 0x0025,
|
||||||
|
0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E,
|
||||||
|
0x002F, 0x0030, 0x0031, 0x001E, 0x001E, 0x001E, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0034, 0x0035, 0x4034, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x8034, 0x0036, 0xC034, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0xC01D, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0x0037, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0032, 0x0038, 0x0001,
|
||||||
|
0x0001, 0x001D, 0x0032, 0x0032, 0x0032, 0x0033, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0000, 0xC033, 0x0032, 0x0032, 0x0039, 0x003A, 0x0001,
|
||||||
|
0x0001, 0x003B, 0x003C, 0x0032, 0x0032, 0xC03C, 0x003D, 0x003D,
|
||||||
|
0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D,
|
||||||
|
0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D,
|
||||||
|
0x003D, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041, 0x0001, 0x0001,
|
||||||
|
0x0001, 0x0042, 0x0043, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044,
|
||||||
|
0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044,
|
||||||
|
0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044,
|
||||||
|
0x0044, 0x0044, 0x0045, 0x0046, 0x0001, 0x0001, 0x0001, 0x0001,
|
||||||
|
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
|
||||||
|
0x0001, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D,
|
||||||
|
0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0001,
|
||||||
|
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
|
||||||
|
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
|
||||||
|
0x0001, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B,
|
||||||
|
0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0001, 0x0001,
|
||||||
|
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
|
||||||
|
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
|
||||||
|
0x0001, 0x0001, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||||
|
0x0068, 0x0069, 0x006A, 0x006B, 0x0001, 0x006C, 0x0001, 0x0001,
|
||||||
|
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0xFF, 0x01, 0xFE, 0x06, 0xF9, 0x08, 0xF7,
|
||||||
|
0x11, 0xEF, 0x22, 0xDB, 0x20, 0xDB, 0x40, 0xB7,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFE, 0x00, 0xF8, 0x00,
|
||||||
|
0xF1, 0x00, 0xE6, 0x04, 0xE4, 0x00, 0xC8, 0x00,
|
||||||
|
0x7F, 0x80, 0x80, 0x7F, 0x00, 0xFF, 0x7F, 0xFF,
|
||||||
|
0x80, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0x80, 0x00, 0x00, 0x00, 0x7F, 0x00,
|
||||||
|
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x40, 0xB7, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF,
|
||||||
|
0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF,
|
||||||
|
0xC8, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00,
|
||||||
|
0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x03, 0xFF, 0x02, 0xFF,
|
||||||
|
0x02, 0xFF, 0x02, 0xFF, 0x02, 0xFF, 0x02, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
|
||||||
|
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0xC1, 0xDD, 0x00, 0xC9,
|
||||||
|
0x14, 0xFF, 0x14, 0xFF, 0x14, 0xFF, 0x00, 0xC9,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x36, 0x22,
|
||||||
|
0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x36, 0x22,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0xC7, 0xDF, 0x01, 0xCF,
|
||||||
|
0x11, 0xFF, 0x11, 0xFF, 0x11, 0xFF, 0x01, 0xCF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x31, 0x20,
|
||||||
|
0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x31, 0x20,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0xC2, 0xFF, 0x03, 0xFF,
|
||||||
|
0x02, 0xFE, 0x02, 0xFE, 0x02, 0xFF, 0x02, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x03, 0x00,
|
||||||
|
0x03, 0x01, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x08, 0xFF, 0x18, 0xFF,
|
||||||
|
0x08, 0x4E, 0x08, 0x4E, 0x09, 0x1F, 0x08, 0x1C,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x00,
|
||||||
|
0xB9, 0x10, 0xB9, 0xA1, 0xE9, 0xA0, 0xEB, 0x41,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x4F, 0xFF, 0x02, 0x1F,
|
||||||
|
0x02, 0x4F, 0x02, 0x4F, 0xF2, 0xFF, 0x02, 0xE7,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x4F, 0x00, 0xE2, 0xA0,
|
||||||
|
0xB2, 0xA0, 0xB2, 0x10, 0xF2, 0x00, 0x1A, 0x10,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0xBC, 0xFD, 0x22, 0xFB,
|
||||||
|
0x22, 0xFB, 0x3C, 0xFD, 0x24, 0xFF, 0x26, 0xF9,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x26, 0x00,
|
||||||
|
0x26, 0x00, 0x3E, 0x00, 0x24, 0x00, 0x26, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x50, 0xFF, 0x49, 0xEF,
|
||||||
|
0x49, 0xF0, 0x46, 0xFF, 0x49, 0xF0, 0x49, 0xEF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x59, 0x00,
|
||||||
|
0x4F, 0x06, 0x46, 0x00, 0x4F, 0x06, 0x59, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x88, 0xFF, 0x00, 0x72,
|
||||||
|
0x00, 0xF2, 0x05, 0xFF, 0x00, 0xF8, 0x00, 0x78,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x8D, 0x08,
|
||||||
|
0x0D, 0x05, 0x05, 0x00, 0x07, 0x05, 0x87, 0x02,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x8A, 0xFF, 0x02, 0x27,
|
||||||
|
0x02, 0x27, 0x52, 0xFF, 0x02, 0x8F, 0x02, 0x8F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x8A, 0x00, 0xDA, 0x88,
|
||||||
|
0xDA, 0x50, 0x52, 0x00, 0x72, 0x50, 0x72, 0x20,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0xFA, 0xFF, 0x22, 0xFF,
|
||||||
|
0x22, 0xFF, 0x23, 0xFF, 0x22, 0xFF, 0x22, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x22, 0x00,
|
||||||
|
0x22, 0x00, 0x23, 0x00, 0x22, 0x00, 0x22, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x20, 0xFF, 0x20, 0xFF,
|
||||||
|
0x20, 0xFF, 0xE0, 0xFF, 0x20, 0xFF, 0x20, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00,
|
||||||
|
0x20, 0x00, 0xE0, 0x00, 0x20, 0x00, 0x20, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x33, 0x37, 0x00, 0x77,
|
||||||
|
0x80, 0xFF, 0x20, 0x27, 0x08, 0xFF, 0x00, 0x77,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xFB, 0x40, 0x88, 0x88,
|
||||||
|
0x80, 0x00, 0xF8, 0x50, 0x08, 0x00, 0x88, 0x88,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0xEF, 0xFF, 0x88, 0xFF,
|
||||||
|
0x88, 0xFF, 0x8F, 0xFF, 0x88, 0xFF, 0x88, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x88, 0x00,
|
||||||
|
0x88, 0x00, 0x8F, 0x00, 0x88, 0x00, 0x88, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0xF9, 0xFD, 0x80, 0xF9,
|
||||||
|
0x84, 0xFF, 0xF4, 0xFF, 0x84, 0xFF, 0x80, 0xF9,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x86, 0x02,
|
||||||
|
0x84, 0x00, 0xF4, 0x00, 0x84, 0x00, 0x86, 0x02,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0xC0, 0xDF, 0x00, 0xCF,
|
||||||
|
0x10, 0xFF, 0x10, 0xFF, 0x10, 0xFF, 0x00, 0xCF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x30, 0x20,
|
||||||
|
0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x30, 0x20,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x30, 0x36, 0x00, 0x74,
|
||||||
|
0x82, 0xFF, 0x22, 0x27, 0x0A, 0xFF, 0x00, 0x74,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xF9, 0x40, 0x8B, 0x89,
|
||||||
|
0x82, 0x00, 0xFA, 0x50, 0x0A, 0x00, 0x8B, 0x89,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0xE2, 0xEF, 0x02, 0xE7,
|
||||||
|
0x0A, 0xFF, 0x0A, 0xFF, 0x0A, 0xFF, 0x00, 0xE4,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x1A, 0x10,
|
||||||
|
0x0A, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x1B, 0x12,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x14, 0xFF, 0x16, 0xFF,
|
||||||
|
0x14, 0xFC, 0x15, 0xFE, 0x14, 0xFF, 0x04, 0xCF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x16, 0x00,
|
||||||
|
0x17, 0x01, 0x15, 0x00, 0x14, 0x00, 0x34, 0x10,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x2F, 0xFF, 0x28, 0xFF,
|
||||||
|
0x28, 0xFF, 0xA8, 0x7F, 0x28, 0x3F, 0x68, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x28, 0x00,
|
||||||
|
0x28, 0x00, 0xA8, 0x00, 0xE8, 0x80, 0x68, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x3F,
|
||||||
|
0x40, 0xFF, 0x40, 0xFF, 0x40, 0xFF, 0x00, 0x3F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xC0, 0x80,
|
||||||
|
0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xC0, 0x80,
|
||||||
|
0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF,
|
||||||
|
0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF,
|
||||||
|
0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00,
|
||||||
|
0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xFF,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||||
|
0xC1, 0xDD, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0xC1, 0xDF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x02, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x4A, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x0A, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x22, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x82, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x20, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x60, 0x67, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xF8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x8F, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0xA2, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0xF9, 0xFD, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0xC0, 0xDF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x60, 0x66, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xF9, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0xE0, 0xEE, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xF1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0xC4, 0xDF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xE4, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x2F, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0xFF,
|
||||||
|
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF,
|
||||||
|
0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF,
|
||||||
|
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
|
||||||
|
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x3C, 0xFF,
|
||||||
|
0x7E, 0xFF, 0xE7, 0xE7, 0xFF, 0x7E, 0xFF, 0x7E,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x7E,
|
||||||
|
0x81, 0xC3, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0x7E, 0xFF, 0x3C, 0xFF, 0x00, 0x7E, 0x81,
|
||||||
|
0x3C, 0xC3, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0xC3, 0x81,
|
||||||
|
0x7E, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xF7, 0x00, 0xF7, 0x00, 0xF7, 0x00, 0xF7,
|
||||||
|
0x00, 0xF7, 0x00, 0xED, 0x00, 0xED, 0x00, 0xED,
|
||||||
|
0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00,
|
||||||
|
0x09, 0x00, 0x11, 0x02, 0x11, 0x02, 0x11, 0x02,
|
||||||
|
0x00, 0xED, 0x00, 0xDB, 0x00, 0xDB, 0x00, 0xDB,
|
||||||
|
0x00, 0xB7, 0x00, 0xB7, 0x00, 0x6F, 0x00, 0x6F,
|
||||||
|
0x11, 0x02, 0x23, 0x04, 0x23, 0x04, 0x23, 0x04,
|
||||||
|
0x47, 0x08, 0x47, 0x08, 0x8F, 0x10, 0x8F, 0x10,
|
||||||
|
0x00, 0xFE, 0x00, 0xFD, 0x00, 0xFB, 0x00, 0xF7,
|
||||||
|
0x00, 0xEE, 0x00, 0xDD, 0x00, 0xBB, 0x00, 0x77,
|
||||||
|
0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00,
|
||||||
|
0x10, 0x01, 0x21, 0x02, 0x43, 0x04, 0x87, 0x08,
|
||||||
|
0x00, 0xDF, 0x00, 0xBF, 0x00, 0xBF, 0x00, 0x7F,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x1F, 0x20, 0x3F, 0x40, 0x3F, 0x40, 0x7F, 0x80,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xB7,
|
||||||
|
0x00, 0xB7, 0x00, 0xDB, 0x00, 0xDD, 0x00, 0xEE,
|
||||||
|
0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x88, 0x40,
|
||||||
|
0x88, 0x40, 0xC4, 0x20, 0xC2, 0x20, 0xE1, 0x10,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFC,
|
||||||
|
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFC, 0x00, 0xE3, 0x00, 0x1F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x03, 0x00, 0x1C, 0x00, 0xE0, 0x00,
|
||||||
|
0x00, 0xFE, 0x00, 0xFD, 0x00, 0xF3, 0x00, 0xEF,
|
||||||
|
0x00, 0x1C, 0x00, 0xF3, 0x00, 0xEF, 0x00, 0x1F,
|
||||||
|
0x01, 0x00, 0x02, 0x00, 0x0C, 0x00, 0x10, 0x00,
|
||||||
|
0xE0, 0x03, 0x03, 0x0C, 0x0F, 0x10, 0x1F, 0xE0,
|
||||||
|
0x00, 0xEF, 0x00, 0xDF, 0x00, 0xBF, 0x00, 0x7F,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x0F, 0x10, 0x1F, 0x20, 0x3F, 0x40, 0x7F, 0x80,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0xF7, 0x00, 0xF9, 0x00, 0xFE, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xF0, 0x08, 0xF8, 0x06, 0xFE, 0x01, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0x80, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x80,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x7F, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x7F,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0xFF, 0x00, 0xFC, 0x00, 0x03,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFC, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xFC,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0xFC, 0x00, 0xE3, 0x00, 0x1F, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0x03, 0x03, 0x1C, 0x1F, 0xE0, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x21, 0xDE, 0x00, 0x7F,
|
||||||
|
0x0C, 0xF3, 0x19, 0xE0, 0x10, 0xEE, 0x08, 0xF7,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xC0, 0x3F, 0x80, 0xFF,
|
||||||
|
0x00, 0xFF, 0x0E, 0xF7, 0x1F, 0xE1, 0x07, 0xF8,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0xBF,
|
||||||
|
0x40, 0xBE, 0x80, 0x3F, 0x02, 0xFD, 0x00, 0xFB,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7F, 0xC0,
|
||||||
|
0x7F, 0x81, 0xFE, 0x41, 0xFC, 0x03, 0xFC, 0x07,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x08, 0xF7, 0x00, 0xFF,
|
||||||
|
0x00, 0xFB, 0x04, 0xFB, 0x24, 0xDB, 0x64, 0x9B,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x87, 0x78, 0x07, 0xF8,
|
||||||
|
0x07, 0xFC, 0x07, 0xF8, 0x03, 0xFC, 0x43, 0xBC,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x01, 0xDE, 0x20, 0xDF,
|
||||||
|
0x20, 0xDF, 0x00, 0xFF, 0x04, 0xFB, 0x04, 0xFB,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xE0, 0x3F, 0xC0, 0x3F,
|
||||||
|
0xC0, 0x3F, 0xC0, 0x3F, 0xC0, 0x3F, 0xC0, 0x3F,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFB, 0x00, 0xFF,
|
||||||
|
0x00, 0x77, 0x00, 0x7F, 0x80, 0x6F, 0x82, 0x7D,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFC, 0x07, 0xF8, 0x07,
|
||||||
|
0xF8, 0x8F, 0xF0, 0x8F, 0x70, 0x9F, 0x60, 0x9F,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFB, 0x24, 0xCB,
|
||||||
|
0x24, 0xDB, 0x20, 0xDF, 0x20, 0xDF, 0x00, 0xDF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x1C, 0xE7, 0x18, 0xF7,
|
||||||
|
0x18, 0xE7, 0x18, 0xE7, 0x38, 0xC7, 0x38, 0xE7,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x02, 0xFC,
|
||||||
|
0x7E, 0x81, 0x80, 0x01, 0x80, 0x7F, 0xF8, 0x03,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x01, 0xFE, 0x01, 0xFF,
|
||||||
|
0x01, 0xFE, 0x7F, 0xFE, 0x7F, 0x80, 0x07, 0xFC,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xBF, 0x40, 0xBF,
|
||||||
|
0x47, 0xB8, 0x08, 0xF0, 0x08, 0xF7, 0x0E, 0xF1,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xC0, 0x7F, 0x80, 0x7F,
|
||||||
|
0x80, 0x7F, 0x87, 0x7F, 0x87, 0x78, 0x80, 0x7F,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x40, 0x9F, 0x00, 0xFF,
|
||||||
|
0x10, 0xEF, 0x90, 0x6F, 0x10, 0xEB, 0x14, 0xEB,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x3F, 0xE0, 0x1F, 0xE0,
|
||||||
|
0x0E, 0xF1, 0x0C, 0xF3, 0x0C, 0xF7, 0x18, 0xE7,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x20, 0x9F, 0x00, 0xFF,
|
||||||
|
0x0C, 0xF3, 0x31, 0xC0, 0x60, 0x9F, 0x40, 0xBF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xC0, 0x7F, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x1E, 0xEF, 0x3F, 0xC0, 0x7F, 0x80,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x80, 0x77, 0x04, 0xDB,
|
||||||
|
0x00, 0xFB, 0x10, 0xEF, 0x00, 0xFD, 0x80, 0x77,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x78, 0x8F, 0x38, 0xE7,
|
||||||
|
0x1C, 0xE7, 0x0C, 0xF3, 0x0E, 0xF3, 0x0E, 0xF9,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7F, 0x00, 0xFF,
|
||||||
|
0x40, 0xB7, 0x00, 0xEF, 0x01, 0xDE, 0x02, 0xFC,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x7C, 0x83, 0x78, 0x87,
|
||||||
|
0x38, 0xCF, 0x30, 0xDF, 0x21, 0xFE, 0x03, 0xFD,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xDF, 0x60, 0x9F,
|
||||||
|
0xC0, 0x3F, 0x80, 0x7F, 0x00, 0x7F, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0x3F, 0xE0, 0x3F, 0xC0,
|
||||||
|
0x7F, 0x80, 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFB, 0x01, 0xFC,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFE, 0x01, 0xFC, 0x03, 0xFC, 0x07, 0xFE, 0x03,
|
||||||
|
0x00, 0xFF, 0x40, 0x3F, 0x30, 0x8F, 0x00, 0xF7,
|
||||||
|
0x80, 0x7F, 0x30, 0xCF, 0x01, 0xFE, 0x87, 0x78,
|
||||||
|
0x01, 0xFE, 0x80, 0xFF, 0xE0, 0x5F, 0xF8, 0x0F,
|
||||||
|
0x78, 0x87, 0x00, 0xFF, 0x00, 0xFF, 0x03, 0xFC,
|
||||||
|
0x00, 0xFF, 0x08, 0xF7, 0x80, 0x6F, 0x80, 0x7F,
|
||||||
|
0x80, 0x5F, 0x87, 0x78, 0x04, 0x7B, 0x08, 0x73,
|
||||||
|
0xF8, 0x07, 0xF0, 0x0F, 0x70, 0x9F, 0x60, 0x9F,
|
||||||
|
0x60, 0xBF, 0xC3, 0x3C, 0x87, 0xF8, 0x87, 0xFC,
|
||||||
|
0xA0, 0x1F, 0x80, 0x7D, 0xE2, 0x1D, 0x02, 0xFD,
|
||||||
|
0x02, 0xFD, 0xF0, 0x0F, 0x10, 0xEE, 0x11, 0xEE,
|
||||||
|
0x43, 0xFC, 0xE3, 0x1E, 0x03, 0xFC, 0x01, 0xFE,
|
||||||
|
0x01, 0xFE, 0xE1, 0x1E, 0xE1, 0x1F, 0xE1, 0x1E,
|
||||||
|
0x44, 0xBB, 0x48, 0xB3, 0x48, 0xB7, 0x08, 0xF7,
|
||||||
|
0x0A, 0xF5, 0x02, 0xF5, 0x80, 0x77, 0x90, 0x67,
|
||||||
|
0x84, 0x7B, 0x84, 0x7F, 0x84, 0x7B, 0x84, 0x7B,
|
||||||
|
0x8C, 0x73, 0x8C, 0x7B, 0x0E, 0xF9, 0x0E, 0xF9,
|
||||||
|
0x86, 0x59, 0x06, 0xF9, 0x48, 0xB3, 0x08, 0xF7,
|
||||||
|
0x10, 0xE7, 0x14, 0xEB, 0x24, 0xCB, 0x20, 0xDF,
|
||||||
|
0x60, 0xBF, 0x44, 0xBB, 0x04, 0xFF, 0x0C, 0xF3,
|
||||||
|
0x0C, 0xFB, 0x18, 0xE7, 0x18, 0xF7, 0x38, 0xC7,
|
||||||
|
0x08, 0xD7, 0x48, 0x97, 0x48, 0xB7, 0x41, 0xBE,
|
||||||
|
0x41, 0xBE, 0x01, 0xBE, 0x10, 0xAF, 0x90, 0x2F,
|
||||||
|
0x30, 0xEF, 0x30, 0xEF, 0x30, 0xCF, 0x30, 0xCF,
|
||||||
|
0x70, 0x8F, 0x70, 0xCF, 0x60, 0xDF, 0x60, 0xDF,
|
||||||
|
0x04, 0xFB, 0x04, 0xFB, 0xFC, 0x03, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0xF8, 0x02, 0x05, 0xFA, 0x05, 0xFA,
|
||||||
|
0x03, 0xFC, 0x03, 0xFC, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0x07, 0xFD, 0x02, 0xFD, 0x06, 0xF9,
|
||||||
|
0x80, 0x7F, 0x80, 0x7F, 0x0F, 0xF0, 0x10, 0xE7,
|
||||||
|
0x10, 0xEE, 0x1E, 0xE1, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x0E, 0xF1, 0x0F, 0xF8,
|
||||||
|
0x0F, 0xF1, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x60, 0x8F, 0x00, 0xDF, 0x00, 0xFF, 0x00, 0xEF,
|
||||||
|
0x04, 0xEB, 0x20, 0xCF, 0x22, 0xDD, 0xC1, 0x1E,
|
||||||
|
0x38, 0xD7, 0x38, 0xE7, 0x18, 0xE7, 0x18, 0xF7,
|
||||||
|
0x18, 0xF7, 0x1C, 0xF3, 0x3E, 0xC1, 0x7F, 0xA0,
|
||||||
|
0x80, 0x3F, 0x80, 0x7F, 0x80, 0x7F, 0x01, 0xFE,
|
||||||
|
0x00, 0xBD, 0x18, 0xE7, 0x00, 0xFF, 0x83, 0x7C,
|
||||||
|
0x7F, 0xC0, 0x7F, 0x80, 0x7F, 0x80, 0x7E, 0x81,
|
||||||
|
0x7E, 0xC3, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x81, 0x76, 0x80, 0x77, 0x10, 0xE7, 0x10, 0xEF,
|
||||||
|
0x10, 0xEF, 0x21, 0xCE, 0x41, 0x9E, 0x81, 0x3E,
|
||||||
|
0x0E, 0xF9, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xF0,
|
||||||
|
0x1F, 0xE0, 0x3E, 0xD1, 0x7E, 0xA1, 0xFE, 0x41,
|
||||||
|
0x04, 0xF9, 0x08, 0xF3, 0x18, 0xE7, 0x10, 0xEF,
|
||||||
|
0x10, 0xEF, 0x10, 0xEF, 0x00, 0xEF, 0x20, 0xCF,
|
||||||
|
0x07, 0xFA, 0x07, 0xFC, 0x0F, 0xF0, 0x0F, 0xF0,
|
||||||
|
0x0F, 0xF0, 0x1F, 0xE0, 0x1F, 0xF0, 0x1F, 0xF0,
|
||||||
|
0x7C, 0x01, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x82, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x78, 0x87, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x0F, 0xF0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x70, 0x8E, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x01, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xC3, 0x18, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x24, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x8F, 0x70, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xF8, 0x03, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x04, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0x3E, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0xC1, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xE0, 0x1F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
|
||||||
|
};
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
void GB_update_joyp(GB_gameboy_t *gb)
|
void GB_update_joyp(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
|
if (gb->model & GB_MODEL_NO_SFC_BIT) return;
|
||||||
|
|
||||||
uint8_t key_selection = 0;
|
uint8_t key_selection = 0;
|
||||||
uint8_t previous_state = 0;
|
uint8_t previous_state = 0;
|
||||||
|
|
||||||
@ -10,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) {
|
||||||
@ -53,14 +55,27 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Todo: This assumes the keys *always* bounce, which is incorrect when emulating an SGB */
|
||||||
if (previous_state != (gb->io_registers[GB_IO_JOYP] & 0xF)) {
|
if (previous_state != (gb->io_registers[GB_IO_JOYP] & 0xF)) {
|
||||||
/* The joypad interrupt DOES occur on CGB (Tested on CGB-CPU-06), unlike what some documents say. */
|
/* The joypad interrupt DOES occur on CGB (Tested on CGB-E), unlike what some documents say. */
|
||||||
gb->io_registers[GB_IO_IF] |= 0x10;
|
gb->io_registers[GB_IO_IF] |= 0x10;
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->io_registers[GB_IO_JOYP] |= 0xC0;
|
gb->io_registers[GB_IO_JOYP] |= 0xC0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value)
|
||||||
|
{
|
||||||
|
uint8_t previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
|
||||||
|
gb->io_registers[GB_IO_JOYP] &= 0xF0;
|
||||||
|
gb->io_registers[GB_IO_JOYP] |= value & 0xF;
|
||||||
|
|
||||||
|
if (previous_state & ~(gb->io_registers[GB_IO_JOYP] & 0xF)) {
|
||||||
|
gb->io_registers[GB_IO_IF] |= 0x10;
|
||||||
|
}
|
||||||
|
gb->io_registers[GB_IO_JOYP] |= 0xC0;
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
assert(index >= 0 && index < GB_KEY_MAX);
|
assert(index >= 0 && index < GB_KEY_MAX);
|
||||||
|
@ -17,6 +17,7 @@ typedef enum {
|
|||||||
|
|
||||||
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_icd_set_joyp(GB_gameboy_t *gb, uint8_t value);
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
void GB_update_joyp(GB_gameboy_t *gb);
|
void GB_update_joyp(GB_gameboy_t *gb);
|
||||||
|
17
Core/mbc.c
17
Core/mbc.c
@ -37,8 +37,8 @@ const GB_cartridge_t GB_cart_defs[256] = {
|
|||||||
[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)
|
||||||
{ GB_HUC3 , GB_STANDARD_MBC, true , true , false, false}, // FEh HuC3 (Todo: Mapper support only)
|
{ GB_HUC3 , GB_STANDARD_MBC, true , true , true, false}, // FEh HuC3
|
||||||
{ GB_HUC1 , GB_STANDARD_MBC, true , true , false, false}, // FFh HuC1+RAM+BATTERY (Todo: No IR bindings)
|
{ GB_HUC1 , GB_STANDARD_MBC, true , true , false, false}, // FFh HuC1+RAM+BATTERY
|
||||||
};
|
};
|
||||||
|
|
||||||
void GB_update_mbc_mappings(GB_gameboy_t *gb)
|
void GB_update_mbc_mappings(GB_gameboy_t *gb)
|
||||||
@ -86,6 +86,10 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb)
|
|||||||
case GB_MBC3:
|
case GB_MBC3:
|
||||||
gb->mbc_rom_bank = gb->mbc3.rom_bank;
|
gb->mbc_rom_bank = gb->mbc3.rom_bank;
|
||||||
gb->mbc_ram_bank = gb->mbc3.ram_bank;
|
gb->mbc_ram_bank = gb->mbc3.ram_bank;
|
||||||
|
if (!gb->is_mbc30) {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
@ -128,7 +132,7 @@ void GB_configure_cart(GB_gameboy_t *gb)
|
|||||||
gb->mbc_ram_size = 0x200;
|
gb->mbc_ram_size = 0x200;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
static const int 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);
|
gb->mbc_ram = malloc(gb->mbc_ram_size);
|
||||||
@ -147,6 +151,13 @@ void GB_configure_cart(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Detect MBC30 */
|
||||||
|
if (gb->cartridge_type->mbc_type == GB_MBC3) {
|
||||||
|
if (gb->rom_size > 0x200000 || gb->mbc_ram_size > 0x8000) {
|
||||||
|
gb->is_mbc30 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Set MBC5's bank to 1 correctly */
|
/* Set MBC5's bank to 1 correctly */
|
||||||
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;
|
||||||
|
@ -10,7 +10,7 @@ typedef struct {
|
|||||||
GB_MBC2,
|
GB_MBC2,
|
||||||
GB_MBC3,
|
GB_MBC3,
|
||||||
GB_MBC5,
|
GB_MBC5,
|
||||||
GB_HUC1, /* Todo: HUC1 features are not emulated. Should be unified with the CGB IR sensor API. */
|
GB_HUC1,
|
||||||
GB_HUC3,
|
GB_HUC3,
|
||||||
} mbc_type;
|
} mbc_type;
|
||||||
enum {
|
enum {
|
||||||
|
288
Core/memory.c
288
Core/memory.c
@ -113,6 +113,11 @@ static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr)
|
|||||||
return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src);
|
return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool effective_ir_input(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
return gb->infrared_input || (gb->io_registers[GB_IO_RP] & 1) || gb->cart_ir;
|
||||||
|
}
|
||||||
|
|
||||||
static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr)
|
static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr)
|
||||||
{
|
{
|
||||||
if (addr < 0x100 && !gb->boot_rom_finished) {
|
if (addr < 0x100 && !gb->boot_rom_finished) {
|
||||||
@ -141,16 +146,56 @@ static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr)
|
|||||||
if (gb->vram_read_blocked) {
|
if (gb->vram_read_blocked) {
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
|
if (gb->display_state == 22 && GB_is_cgb(gb) && !gb->cgb_double_speed) {
|
||||||
|
if (addr & 0x1000) {
|
||||||
|
addr = gb->last_tile_index_address;
|
||||||
|
}
|
||||||
|
else if (gb->last_tile_data_address & 0x1000) {
|
||||||
|
/* TODO: This is case is more complicated then the rest and differ between revisions
|
||||||
|
It's probably affected by how VRAM is layed out, might be easier after a decap is done*/
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addr = gb->last_tile_data_address;
|
||||||
|
}
|
||||||
|
}
|
||||||
return gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000];
|
return gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000];
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
|
static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
|
||||||
{
|
{
|
||||||
|
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||||
|
switch (gb->huc3_mode) {
|
||||||
|
case 0xC: // RTC read
|
||||||
|
if (gb->huc3_access_flags == 0x2) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return gb->huc3_read;
|
||||||
|
case 0xD: // RTC status
|
||||||
|
return 1;
|
||||||
|
case 0xE: // IR mode
|
||||||
|
return effective_ir_input(gb); // TODO: What are the other bits?
|
||||||
|
default:
|
||||||
|
GB_log(gb, "Unsupported HuC-3 mode %x read: %04x\n", gb->huc3_mode, addr);
|
||||||
|
return 1; // TODO: What happens in this case?
|
||||||
|
case 0: // TODO: R/O RAM? (or is it disabled?)
|
||||||
|
case 0xA: // RAM
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((!gb->mbc_ram_enable || !gb->mbc_ram_size) &&
|
if ((!gb->mbc_ram_enable || !gb->mbc_ram_size) &&
|
||||||
gb->cartridge_type->mbc_subtype != GB_CAMERA &&
|
gb->cartridge_type->mbc_subtype != GB_CAMERA &&
|
||||||
gb->cartridge_type->mbc_type != GB_HUC1) return 0xFF;
|
gb->cartridge_type->mbc_type != GB_HUC1 &&
|
||||||
|
gb->cartridge_type->mbc_type != GB_HUC3) {
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) {
|
if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) {
|
||||||
|
return 0xc0 | effective_ir_input(gb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gb->cartridge_type->has_rtc && gb->cartridge_type->mbc_type != GB_HUC3 &&
|
||||||
|
gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) {
|
||||||
/* RTC read */
|
/* RTC read */
|
||||||
gb->rtc_latched.high |= ~0xC1; /* Not all bytes in RTC high are used. */
|
gb->rtc_latched.high |= ~0xC1; /* Not all bytes in RTC high are used. */
|
||||||
return gb->rtc_latched.data[gb->mbc_ram_bank - 8];
|
return gb->rtc_latched.data[gb->mbc_ram_bank - 8];
|
||||||
@ -273,8 +318,11 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
|||||||
case GB_MODEL_DMG_B:
|
case GB_MODEL_DMG_B:
|
||||||
case GB_MODEL_SGB_NTSC:
|
case GB_MODEL_SGB_NTSC:
|
||||||
case GB_MODEL_SGB_PAL:
|
case GB_MODEL_SGB_PAL:
|
||||||
|
case GB_MODEL_SGB_NTSC_NO_SFC:
|
||||||
|
case GB_MODEL_SGB_PAL_NO_SFC:
|
||||||
case GB_MODEL_SGB2:
|
case GB_MODEL_SGB2:
|
||||||
;
|
case GB_MODEL_SGB2_NO_SFC:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,20 +340,20 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
|||||||
return gb->io_registers[GB_IO_TAC] | 0xF8;
|
return gb->io_registers[GB_IO_TAC] | 0xF8;
|
||||||
case GB_IO_STAT:
|
case GB_IO_STAT:
|
||||||
return gb->io_registers[GB_IO_STAT] | 0x80;
|
return gb->io_registers[GB_IO_STAT] | 0x80;
|
||||||
case GB_IO_DMG_EMULATION_INDICATION:
|
case GB_IO_OPRI:
|
||||||
if (!gb->cgb_mode) {
|
if (!GB_is_cgb(gb)) {
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
return gb->io_registers[GB_IO_DMG_EMULATION_INDICATION] | 0xFE;
|
return gb->io_registers[GB_IO_OPRI] | 0xFE;
|
||||||
|
|
||||||
case GB_IO_PCM_12:
|
case GB_IO_PCM_12:
|
||||||
if (!GB_is_cgb(gb)) return 0xFF;
|
if (!GB_is_cgb(gb)) return 0xFF;
|
||||||
return (gb->apu.is_active[GB_SQUARE_2] ? (gb->apu.samples[GB_SQUARE_2] << 4) : 0) |
|
return ((gb->apu.is_active[GB_SQUARE_2] ? (gb->apu.samples[GB_SQUARE_2] << 4) : 0) |
|
||||||
(gb->apu.is_active[GB_SQUARE_1] ? (gb->apu.samples[GB_SQUARE_1]) : 0);
|
(gb->apu.is_active[GB_SQUARE_1] ? (gb->apu.samples[GB_SQUARE_1]) : 0)) & (gb->model <= GB_MODEL_CGB_C? gb->apu.pcm_mask[0] : 0xFF);
|
||||||
case GB_IO_PCM_34:
|
case GB_IO_PCM_34:
|
||||||
if (!GB_is_cgb(gb)) return 0xFF;
|
if (!GB_is_cgb(gb)) return 0xFF;
|
||||||
return (gb->apu.is_active[GB_NOISE] ? (gb->apu.samples[GB_NOISE] << 4) : 0) |
|
return ((gb->apu.is_active[GB_NOISE] ? (gb->apu.samples[GB_NOISE] << 4) : 0) |
|
||||||
(gb->apu.is_active[GB_WAVE] ? (gb->apu.samples[GB_WAVE]) : 0);
|
(gb->apu.is_active[GB_WAVE] ? (gb->apu.samples[GB_WAVE]) : 0)) & (gb->model <= GB_MODEL_CGB_C? gb->apu.pcm_mask[1] : 0xFF);
|
||||||
case GB_IO_JOYP:
|
case GB_IO_JOYP:
|
||||||
GB_timing_sync(gb);
|
GB_timing_sync(gb);
|
||||||
case GB_IO_TMA:
|
case GB_IO_TMA:
|
||||||
@ -376,9 +424,8 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
|||||||
case GB_IO_RP: {
|
case GB_IO_RP: {
|
||||||
if (!gb->cgb_mode) return 0xFF;
|
if (!gb->cgb_mode) return 0xFF;
|
||||||
/* You will read your own IR LED if it's on. */
|
/* You will read your own IR LED if it's on. */
|
||||||
bool read_value = gb->infrared_input || (gb->io_registers[GB_IO_RP] & 1);
|
|
||||||
uint8_t ret = (gb->io_registers[GB_IO_RP] & 0xC1) | 0x3C;
|
uint8_t ret = (gb->io_registers[GB_IO_RP] & 0xC1) | 0x3C;
|
||||||
if ((gb->io_registers[GB_IO_RP] & 0xC0) == 0xC0 && read_value) {
|
if ((gb->io_registers[GB_IO_RP] & 0xC0) == 0xC0 && effective_ir_input(gb)) {
|
||||||
ret |= 2;
|
ret |= 2;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@ -419,6 +466,11 @@ static GB_read_function_t * const read_map[] =
|
|||||||
read_ram, read_high_memory, /* EXXX FXXX */
|
read_ram, read_high_memory, /* EXXX FXXX */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback)
|
||||||
|
{
|
||||||
|
gb->read_memory_callback = 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)
|
||||||
{
|
{
|
||||||
if (gb->n_watchpoints) {
|
if (gb->n_watchpoints) {
|
||||||
@ -427,7 +479,12 @@ uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
|
|||||||
if (is_addr_in_dma_use(gb, addr)) {
|
if (is_addr_in_dma_use(gb, addr)) {
|
||||||
addr = gb->dma_current_src;
|
addr = gb->dma_current_src;
|
||||||
}
|
}
|
||||||
return read_map[addr >> 12](gb, addr);
|
uint8_t data = read_map[addr >> 12](gb, addr);
|
||||||
|
GB_apply_cheat(gb, addr, &data);
|
||||||
|
if (gb->read_memory_callback) {
|
||||||
|
data = gb->read_memory_callback(gb, addr, data);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||||
@ -443,9 +500,9 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GB_MBC2:
|
case GB_MBC2:
|
||||||
switch (addr & 0xF000) {
|
switch (addr & 0x4100) {
|
||||||
case 0x0000: case 0x1000: if (!(addr & 0x100)) gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
case 0x0000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
||||||
case 0x2000: case 0x3000: if ( addr & 0x100) gb->mbc2.rom_bank = value; break;
|
case 0x0100: gb->mbc2.rom_bank = value; break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GB_MBC3:
|
case GB_MBC3:
|
||||||
@ -470,9 +527,6 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
if (gb->cartridge_type->has_rumble) {
|
if (gb->cartridge_type->has_rumble) {
|
||||||
if (!!(value & 8) != gb->rumble_state) {
|
if (!!(value & 8) != gb->rumble_state) {
|
||||||
gb->rumble_state = !gb->rumble_state;
|
gb->rumble_state = !gb->rumble_state;
|
||||||
if (gb->rumble_callback) {
|
|
||||||
gb->rumble_callback(gb, gb->rumble_state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
value &= 7;
|
value &= 7;
|
||||||
}
|
}
|
||||||
@ -483,7 +537,7 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
break;
|
break;
|
||||||
case GB_HUC1:
|
case GB_HUC1:
|
||||||
switch (addr & 0xF000) {
|
switch (addr & 0xF000) {
|
||||||
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
case 0x0000: case 0x1000: gb->huc1.ir_mode = (value & 0xF) == 0xE; break;
|
||||||
case 0x2000: case 0x3000: gb->huc1.bank_low = value; break;
|
case 0x2000: case 0x3000: gb->huc1.bank_low = value; break;
|
||||||
case 0x4000: case 0x5000: gb->huc1.bank_high = value; break;
|
case 0x4000: case 0x5000: gb->huc1.bank_high = value; break;
|
||||||
case 0x6000: case 0x7000: gb->huc1.mode = value; break;
|
case 0x6000: case 0x7000: gb->huc1.mode = value; break;
|
||||||
@ -491,7 +545,10 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
break;
|
break;
|
||||||
case GB_HUC3:
|
case GB_HUC3:
|
||||||
switch (addr & 0xF000) {
|
switch (addr & 0xF000) {
|
||||||
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
case 0x0000: case 0x1000:
|
||||||
|
gb->huc3_mode = value & 0xF;
|
||||||
|
gb->mbc_ram_enable = gb->huc3_mode == 0xA;
|
||||||
|
break;
|
||||||
case 0x2000: case 0x3000: gb->huc3.rom_bank = value; break;
|
case 0x2000: case 0x3000: gb->huc3.rom_bank = value; break;
|
||||||
case 0x4000: case 0x5000: gb->huc3.ram_bank = value; break;
|
case 0x4000: case 0x5000: gb->huc3.ram_bank = value; break;
|
||||||
}
|
}
|
||||||
@ -506,17 +563,135 @@ static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
//GB_log(gb, "Wrote %02x to %04x (VRAM) during mode 3\n", value, addr);
|
//GB_log(gb, "Wrote %02x to %04x (VRAM) during mode 3\n", value, addr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
/* TODO: not verified */
|
||||||
|
if (gb->display_state == 22 && GB_is_cgb(gb) && !gb->cgb_double_speed) {
|
||||||
|
if (addr & 0x1000) {
|
||||||
|
addr = gb->last_tile_index_address;
|
||||||
|
}
|
||||||
|
else if (gb->last_tile_data_address & 0x1000) {
|
||||||
|
/* TODO: This is case is more complicated then the rest and differ between revisions
|
||||||
|
It's probably affected by how VRAM is layed out, might be easier after a decap is done */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addr = gb->last_tile_data_address;
|
||||||
|
}
|
||||||
|
}
|
||||||
gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000] = value;
|
gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool huc3_write(GB_gameboy_t *gb, uint8_t value)
|
||||||
|
{
|
||||||
|
switch (gb->huc3_mode) {
|
||||||
|
case 0xB: // RTC Write
|
||||||
|
switch (value >> 4) {
|
||||||
|
case 1:
|
||||||
|
if (gb->huc3_access_index < 3) {
|
||||||
|
gb->huc3_read = (gb->huc3_minutes >> (gb->huc3_access_index * 4)) & 0xF;
|
||||||
|
}
|
||||||
|
else if (gb->huc3_access_index < 7) {
|
||||||
|
gb->huc3_read = (gb->huc3_days >> ((gb->huc3_access_index - 3) * 4)) & 0xF;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// GB_log(gb, "Attempting to read from unsupported HuC-3 register: %03x\n", gb->huc3_access_index);
|
||||||
|
}
|
||||||
|
gb->huc3_access_index++;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
if (gb->huc3_access_index < 3) {
|
||||||
|
gb->huc3_minutes &= ~(0xF << (gb->huc3_access_index * 4));
|
||||||
|
gb->huc3_minutes |= ((value & 0xF) << (gb->huc3_access_index * 4));
|
||||||
|
}
|
||||||
|
else if (gb->huc3_access_index < 7) {
|
||||||
|
gb->huc3_days &= ~(0xF << ((gb->huc3_access_index - 3) * 4));
|
||||||
|
gb->huc3_days |= ((value & 0xF) << ((gb->huc3_access_index - 3) * 4));
|
||||||
|
}
|
||||||
|
else if (gb->huc3_access_index >= 0x58 && gb->huc3_access_index <= 0x5a) {
|
||||||
|
gb->huc3_alarm_minutes &= ~(0xF << ((gb->huc3_access_index - 0x58) * 4));
|
||||||
|
gb->huc3_alarm_minutes |= ((value & 0xF) << ((gb->huc3_access_index - 0x58) * 4));
|
||||||
|
}
|
||||||
|
else if (gb->huc3_access_index >= 0x5b && gb->huc3_access_index <= 0x5e) {
|
||||||
|
gb->huc3_alarm_days &= ~(0xF << ((gb->huc3_access_index - 0x5b) * 4));
|
||||||
|
gb->huc3_alarm_days |= ((value & 0xF) << ((gb->huc3_access_index - 0x5b) * 4));
|
||||||
|
}
|
||||||
|
else if (gb->huc3_access_index == 0x5f) {
|
||||||
|
gb->huc3_alarm_enabled = value & 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// GB_log(gb, "Attempting to write %x to unsupported HuC-3 register: %03x\n", value & 0xF, gb->huc3_access_index);
|
||||||
|
}
|
||||||
|
if ((value >> 4) == 3) {
|
||||||
|
gb->huc3_access_index++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
gb->huc3_access_index &= 0xF0;
|
||||||
|
gb->huc3_access_index |= value & 0xF;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
gb->huc3_access_index &= 0x0F;
|
||||||
|
gb->huc3_access_index |= (value & 0xF) << 4;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
gb->huc3_access_flags = (value & 0xF);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
case 0xD: // RTC status
|
||||||
|
// Not sure what writes here mean, they're always 0xFE
|
||||||
|
return true;
|
||||||
|
case 0xE: { // IR mode
|
||||||
|
bool old_input = effective_ir_input(gb);
|
||||||
|
gb->cart_ir = value & 1;
|
||||||
|
bool new_input = effective_ir_input(gb);
|
||||||
|
if (new_input != old_input) {
|
||||||
|
if (gb->infrared_callback) {
|
||||||
|
gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change);
|
||||||
|
}
|
||||||
|
gb->cycles_since_ir_change = 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 0xC:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
case 0: // Disabled
|
||||||
|
case 0xA: // RAM
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||||
{
|
{
|
||||||
|
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||||
|
if (huc3_write(gb, value)) return;
|
||||||
|
}
|
||||||
|
|
||||||
if (gb->camera_registers_mapped) {
|
if (gb->camera_registers_mapped) {
|
||||||
GB_camera_write_register(gb, addr, value);
|
GB_camera_write_register(gb, addr, value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gb->mbc_ram_enable || !gb->mbc_ram_size) return;
|
if ((!gb->mbc_ram_enable || !gb->mbc_ram_size)
|
||||||
|
&& gb->cartridge_type->mbc_type != GB_HUC1) return;
|
||||||
|
|
||||||
|
if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) {
|
||||||
|
bool old_input = effective_ir_input(gb);
|
||||||
|
gb->cart_ir = value & 1;
|
||||||
|
bool new_input = effective_ir_input(gb);
|
||||||
|
if (new_input != old_input) {
|
||||||
|
if (gb->infrared_callback) {
|
||||||
|
gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change);
|
||||||
|
}
|
||||||
|
gb->cycles_since_ir_change = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) {
|
if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) {
|
||||||
gb->rtc_latched.data[gb->mbc_ram_bank - 8] = gb->rtc_real.data[gb->mbc_ram_bank - 8] = value;
|
gb->rtc_latched.data[gb->mbc_ram_bank - 8] = gb->rtc_real.data[gb->mbc_ram_bank - 8] = value;
|
||||||
@ -584,7 +759,10 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
case GB_MODEL_DMG_B:
|
case GB_MODEL_DMG_B:
|
||||||
case GB_MODEL_SGB_NTSC:
|
case GB_MODEL_SGB_NTSC:
|
||||||
case GB_MODEL_SGB_PAL:
|
case GB_MODEL_SGB_PAL:
|
||||||
|
case GB_MODEL_SGB_NTSC_NO_SFC:
|
||||||
|
case GB_MODEL_SGB_PAL_NO_SFC:
|
||||||
case GB_MODEL_SGB2:
|
case GB_MODEL_SGB2:
|
||||||
|
case GB_MODEL_SGB2_NO_SFC:
|
||||||
case GB_MODEL_CGB_E:
|
case GB_MODEL_CGB_E:
|
||||||
case GB_MODEL_AGB:
|
case GB_MODEL_AGB:
|
||||||
break;
|
break;
|
||||||
@ -629,24 +807,34 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
if (addr < 0xFF80) {
|
if (addr < 0xFF80) {
|
||||||
/* Hardware registers */
|
/* Hardware registers */
|
||||||
switch (addr & 0xFF) {
|
switch (addr & 0xFF) {
|
||||||
|
case GB_IO_WY:
|
||||||
|
if (value == gb->current_line) {
|
||||||
|
gb->wy_triggered = true;
|
||||||
|
}
|
||||||
case GB_IO_WX:
|
case GB_IO_WX:
|
||||||
GB_window_related_write(gb, addr & 0xFF, value);
|
|
||||||
break;
|
|
||||||
case GB_IO_IF:
|
case GB_IO_IF:
|
||||||
case GB_IO_SCX:
|
case GB_IO_SCX:
|
||||||
case GB_IO_SCY:
|
case GB_IO_SCY:
|
||||||
case GB_IO_BGP:
|
case GB_IO_BGP:
|
||||||
case GB_IO_OBP0:
|
case GB_IO_OBP0:
|
||||||
case GB_IO_OBP1:
|
case GB_IO_OBP1:
|
||||||
case GB_IO_WY:
|
|
||||||
case GB_IO_SB:
|
case GB_IO_SB:
|
||||||
case GB_IO_DMG_EMULATION_INDICATION:
|
|
||||||
case GB_IO_UNKNOWN2:
|
case GB_IO_UNKNOWN2:
|
||||||
case GB_IO_UNKNOWN3:
|
case GB_IO_UNKNOWN3:
|
||||||
case GB_IO_UNKNOWN4:
|
case GB_IO_UNKNOWN4:
|
||||||
case GB_IO_UNKNOWN5:
|
case GB_IO_UNKNOWN5:
|
||||||
gb->io_registers[addr & 0xFF] = value;
|
gb->io_registers[addr & 0xFF] = value;
|
||||||
return;
|
return;
|
||||||
|
case GB_IO_OPRI:
|
||||||
|
if ((!gb->boot_rom_finished || (gb->io_registers[GB_IO_KEY0] & 8)) && GB_is_cgb(gb)) {
|
||||||
|
gb->io_registers[addr & 0xFF] = value;
|
||||||
|
gb->object_priority = (value & 1) ? GB_OBJECT_PRIORITY_X : GB_OBJECT_PRIORITY_INDEX;
|
||||||
|
}
|
||||||
|
else if (gb->cgb_mode) {
|
||||||
|
gb->io_registers[addr & 0xFF] = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
return;
|
||||||
case GB_IO_LYC:
|
case GB_IO_LYC:
|
||||||
|
|
||||||
/* TODO: Probably completely wrong in double speed mode */
|
/* TODO: Probably completely wrong in double speed mode */
|
||||||
@ -663,7 +851,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
/* These are the states when LY changes, let the display routine call GB_STAT_update for use
|
/* These are the states when LY changes, let the display routine call GB_STAT_update for use
|
||||||
so it correctly handles T-cycle accurate LYC writes */
|
so it correctly handles T-cycle accurate LYC writes */
|
||||||
if (!GB_is_cgb(gb) || (
|
if (!GB_is_cgb(gb) || (
|
||||||
gb->display_state != 6 &&
|
gb->display_state != 35 &&
|
||||||
gb->display_state != 26 &&
|
gb->display_state != 26 &&
|
||||||
gb->display_state != 15 &&
|
gb->display_state != 15 &&
|
||||||
gb->display_state != 16)) {
|
gb->display_state != 16)) {
|
||||||
@ -715,8 +903,19 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
GB_timing_sync(gb);
|
GB_timing_sync(gb);
|
||||||
GB_lcd_off(gb);
|
GB_lcd_off(gb);
|
||||||
}
|
}
|
||||||
/* Writing to LCDC might enable to disable the window, so we write it via GB_window_related_write */
|
/* Handle disabling objects while already fetching an object */
|
||||||
GB_window_related_write(gb, addr & 0xFF, value);
|
if ((gb->io_registers[GB_IO_LCDC] & 2) && !(value & 2)) {
|
||||||
|
if (gb->during_object_fetch) {
|
||||||
|
gb->cycles_for_line += gb->display_cycles;
|
||||||
|
gb->display_cycles = 0;
|
||||||
|
gb->object_fetch_aborted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gb->io_registers[GB_IO_LCDC] = value;
|
||||||
|
if (!(value & 0x20)) {
|
||||||
|
gb->wx_triggered = false;
|
||||||
|
gb->wx166_glitch = false;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case GB_IO_STAT:
|
case GB_IO_STAT:
|
||||||
@ -737,18 +936,25 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case GB_IO_JOYP:
|
case GB_IO_JOYP:
|
||||||
GB_sgb_write(gb, value);
|
if (gb->joyp_write_callback) {
|
||||||
gb->io_registers[GB_IO_JOYP] = value & 0xF0;
|
gb->joyp_write_callback(gb, value);
|
||||||
GB_update_joyp(gb);
|
GB_update_joyp(gb);
|
||||||
|
}
|
||||||
|
else if ((gb->io_registers[GB_IO_JOYP] & 0x30) != (value & 0x30)) {
|
||||||
|
GB_sgb_write(gb, value);
|
||||||
|
gb->io_registers[GB_IO_JOYP] = (value & 0xF0) | (gb->io_registers[GB_IO_JOYP] & 0x0F);
|
||||||
|
GB_update_joyp(gb);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case GB_IO_BIOS:
|
case GB_IO_BANK:
|
||||||
gb->boot_rom_finished = true;
|
gb->boot_rom_finished = true;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case GB_IO_DMG_EMULATION:
|
case GB_IO_KEY0:
|
||||||
if (GB_is_cgb(gb) && !gb->boot_rom_finished) {
|
if (GB_is_cgb(gb) && !gb->boot_rom_finished) {
|
||||||
gb->cgb_mode = !(value & 0xC); /* The real "contents" of this register aren't quite known yet. */
|
gb->cgb_mode = !(value & 0xC); /* The real "contents" of this register aren't quite known yet. */
|
||||||
|
gb->io_registers[GB_IO_KEY0] = value;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -890,13 +1096,15 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
if (!GB_is_cgb(gb)) {
|
if (!GB_is_cgb(gb)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((value & 1) != (gb->io_registers[GB_IO_RP] & 1)) {
|
bool old_input = effective_ir_input(gb);
|
||||||
if (gb->infrared_callback) {
|
|
||||||
gb->infrared_callback(gb, value & 1, gb->cycles_since_ir_change);
|
|
||||||
gb->cycles_since_ir_change = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gb->io_registers[GB_IO_RP] = value;
|
gb->io_registers[GB_IO_RP] = value;
|
||||||
|
bool new_input = effective_ir_input(gb);
|
||||||
|
if (new_input != old_input) {
|
||||||
|
if (gb->infrared_callback) {
|
||||||
|
gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change);
|
||||||
|
}
|
||||||
|
gb->cycles_since_ir_change = 0;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
#include "gb_struct_def.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);
|
||||||
|
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_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);
|
||||||
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
|
||||||
|
53
Core/rumble.c
Normal file
53
Core/rumble.c
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#include "rumble.h"
|
||||||
|
#include "gb.h"
|
||||||
|
|
||||||
|
void GB_set_rumble_mode(GB_gameboy_t *gb, GB_rumble_mode_t mode)
|
||||||
|
{
|
||||||
|
gb->rumble_mode = mode;
|
||||||
|
if (gb->rumble_callback) {
|
||||||
|
gb->rumble_callback(gb, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_handle_rumble(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
if (gb->rumble_callback) {
|
||||||
|
if (gb->rumble_mode == GB_RUMBLE_DISABLED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gb->cartridge_type->has_rumble) {
|
||||||
|
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_on_cycles = gb->rumble_off_cycles = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (gb->rumble_mode == GB_RUMBLE_ALL_GAMES) {
|
||||||
|
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 ch1_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 1) + !!(gb->io_registers[GB_IO_NR51] & 0x10));
|
||||||
|
|
||||||
|
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 = MAX(ch4_rumble, 0.0);
|
||||||
|
|
||||||
|
double ch1_rumble = 0;
|
||||||
|
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);
|
||||||
|
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 = MAX(ch1_rumble, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gb->apu.is_active[GB_NOISE]) {
|
||||||
|
ch4_rumble = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gb->apu.is_active[GB_SQUARE_1]) {
|
||||||
|
ch1_rumble = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb->rumble_callback(gb, MIN(MAX(ch1_rumble / 2 + ch4_rumble, 0.0), 1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
Core/rumble.h
Normal file
17
Core/rumble.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef rumble_h
|
||||||
|
#define rumble_h
|
||||||
|
|
||||||
|
#include "gb_struct_def.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GB_RUMBLE_DISABLED,
|
||||||
|
GB_RUMBLE_CARTRIDGE_ONLY,
|
||||||
|
GB_RUMBLE_ALL_GAMES
|
||||||
|
} GB_rumble_mode_t;
|
||||||
|
|
||||||
|
#ifdef GB_INTERNAL
|
||||||
|
void GB_handle_rumble(GB_gameboy_t *gb);
|
||||||
|
#endif
|
||||||
|
void GB_set_rumble_mode(GB_gameboy_t *gb, GB_rumble_mode_t mode);
|
||||||
|
|
||||||
|
#endif /* rumble_h */
|
@ -36,7 +36,7 @@ int GB_save_state(GB_gameboy_t *gb, const char *path)
|
|||||||
if (!DUMP_SECTION(gb, f, rtc )) goto error;
|
if (!DUMP_SECTION(gb, f, rtc )) goto error;
|
||||||
if (!DUMP_SECTION(gb, f, video )) goto error;
|
if (!DUMP_SECTION(gb, f, video )) goto error;
|
||||||
|
|
||||||
if (GB_is_sgb(gb)) {
|
if (GB_is_hle_sgb(gb)) {
|
||||||
if (!dump_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
|
if (!dump_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ size_t GB_get_save_state_size(GB_gameboy_t *gb)
|
|||||||
+ GB_SECTION_SIZE(apu ) + sizeof(uint32_t)
|
+ GB_SECTION_SIZE(apu ) + sizeof(uint32_t)
|
||||||
+ GB_SECTION_SIZE(rtc ) + sizeof(uint32_t)
|
+ GB_SECTION_SIZE(rtc ) + sizeof(uint32_t)
|
||||||
+ GB_SECTION_SIZE(video ) + sizeof(uint32_t)
|
+ GB_SECTION_SIZE(video ) + sizeof(uint32_t)
|
||||||
+ (GB_is_sgb(gb)? sizeof(*gb->sgb) + sizeof(uint32_t) : 0)
|
+ (GB_is_hle_sgb(gb)? sizeof(*gb->sgb) + sizeof(uint32_t) : 0)
|
||||||
+ gb->mbc_ram_size
|
+ gb->mbc_ram_size
|
||||||
+ gb->ram_size
|
+ gb->ram_size
|
||||||
+ gb->vram_size;
|
+ gb->vram_size;
|
||||||
@ -105,7 +105,7 @@ void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer)
|
|||||||
DUMP_SECTION(gb, buffer, rtc );
|
DUMP_SECTION(gb, buffer, rtc );
|
||||||
DUMP_SECTION(gb, buffer, video );
|
DUMP_SECTION(gb, buffer, video );
|
||||||
|
|
||||||
if (GB_is_sgb(gb)) {
|
if (GB_is_hle_sgb(gb)) {
|
||||||
buffer_dump_section(&buffer, gb->sgb, sizeof(*gb->sgb));
|
buffer_dump_section(&buffer, gb->sgb, sizeof(*gb->sgb));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,8 +161,8 @@ static bool verify_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GB_is_sgb(gb) != GB_is_sgb(save)) {
|
if (GB_is_hle_sgb(gb) != GB_is_hle_sgb(save)) {
|
||||||
GB_log(gb, "The save state is %sfor a Super Game Boy. Try changing the emulated model.\n", GB_is_sgb(save)? "" : "not ");
|
GB_log(gb, "The save state is %sfor a Super Game Boy. Try changing the emulated model.\n", GB_is_hle_sgb(save)? "" : "not ");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,6 +180,28 @@ static bool verify_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sanitize_state(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < 32; i++) {
|
||||||
|
GB_palette_changed(gb, false, i * 2);
|
||||||
|
GB_palette_changed(gb, true, i * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb->bg_fifo.read_end &= 0xF;
|
||||||
|
gb->bg_fifo.write_end &= 0xF;
|
||||||
|
gb->oam_fifo.read_end &= 0xF;
|
||||||
|
gb->oam_fifo.write_end &= 0xF;
|
||||||
|
gb->object_low_line_address &= gb->vram_size & ~1;
|
||||||
|
gb->fetcher_x &= 0x1f;
|
||||||
|
if (gb->lcd_x > gb->position_in_line) {
|
||||||
|
gb->lcd_x = gb->position_in_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gb->object_priority == GB_OBJECT_PRIORITY_UNDEFINED) {
|
||||||
|
gb->object_priority = gb->cgb_mode? GB_OBJECT_PRIORITY_INDEX : GB_OBJECT_PRIORITY_X;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define READ_SECTION(gb, f, section) read_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
|
#define READ_SECTION(gb, f, section) read_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
|
||||||
|
|
||||||
int GB_load_state(GB_gameboy_t *gb, const char *path)
|
int GB_load_state(GB_gameboy_t *gb, const char *path)
|
||||||
@ -223,7 +245,7 @@ int GB_load_state(GB_gameboy_t *gb, const char *path)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GB_is_sgb(gb)) {
|
if (GB_is_hle_sgb(gb)) {
|
||||||
if (!read_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
|
if (!read_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,19 +274,7 @@ int GB_load_state(GB_gameboy_t *gb, const char *path)
|
|||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|
||||||
if (gb->cartridge_type->has_rumble && gb->rumble_callback) {
|
sanitize_state(gb);
|
||||||
gb->rumble_callback(gb, gb->rumble_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < 32; i++) {
|
|
||||||
GB_palette_changed(gb, false, i * 2);
|
|
||||||
GB_palette_changed(gb, true, i * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
gb->bg_fifo.read_end &= 0xF;
|
|
||||||
gb->bg_fifo.write_end &= 0xF;
|
|
||||||
gb->oam_fifo.read_end &= 0xF;
|
|
||||||
gb->oam_fifo.write_end &= 0xF;
|
|
||||||
|
|
||||||
error:
|
error:
|
||||||
fclose(f);
|
fclose(f);
|
||||||
@ -334,7 +344,7 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GB_is_sgb(gb)) {
|
if (GB_is_hle_sgb(gb)) {
|
||||||
if (!buffer_read_section(&buffer, &length, gb->sgb, sizeof(*gb->sgb))) return -1;
|
if (!buffer_read_section(&buffer, &length, gb->sgb, sizeof(*gb->sgb))) return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,7 +357,7 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer_read(gb->vram,gb->vram_size, &buffer, &length) != gb->vram_size) {
|
if (buffer_read(gb->vram, gb->vram_size, &buffer, &length) != gb->vram_size) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,19 +367,7 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le
|
|||||||
|
|
||||||
memcpy(gb, &save, sizeof(save));
|
memcpy(gb, &save, sizeof(save));
|
||||||
|
|
||||||
if (gb->cartridge_type->has_rumble && gb->rumble_callback) {
|
sanitize_state(gb);
|
||||||
gb->rumble_callback(gb, gb->rumble_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < 32; i++) {
|
|
||||||
GB_palette_changed(gb, false, i * 2);
|
|
||||||
GB_palette_changed(gb, true, i * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
gb->bg_fifo.read_end &= 0xF;
|
|
||||||
gb->bg_fifo.write_end &= 0xF;
|
|
||||||
gb->oam_fifo.read_end &= 0xF;
|
|
||||||
gb->oam_fifo.write_end &= 0xF;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
271
Core/sgb.c
271
Core/sgb.c
@ -3,6 +3,10 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
#define INTRO_ANIMATION_LENGTH 200
|
#define INTRO_ANIMATION_LENGTH 200
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -13,6 +17,7 @@ enum {
|
|||||||
ATTR_BLK = 0x04,
|
ATTR_BLK = 0x04,
|
||||||
ATTR_LIN = 0x05,
|
ATTR_LIN = 0x05,
|
||||||
ATTR_DIV = 0x06,
|
ATTR_DIV = 0x06,
|
||||||
|
ATTR_CHR = 0x07,
|
||||||
PAL_SET = 0x0A,
|
PAL_SET = 0x0A,
|
||||||
PAL_TRN = 0x0B,
|
PAL_TRN = 0x0B,
|
||||||
DATA_SND = 0x0F,
|
DATA_SND = 0x0F,
|
||||||
@ -68,6 +73,75 @@ static inline void load_attribute_file(GB_gameboy_t *gb, unsigned file_index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const uint16_t built_in_palettes[] =
|
||||||
|
{
|
||||||
|
0x67BF, 0x265B, 0x10B5, 0x2866,
|
||||||
|
0x637B, 0x3AD9, 0x0956, 0x0000,
|
||||||
|
0x7F1F, 0x2A7D, 0x30F3, 0x4CE7,
|
||||||
|
0x57FF, 0x2618, 0x001F, 0x006A,
|
||||||
|
0x5B7F, 0x3F0F, 0x222D, 0x10EB,
|
||||||
|
0x7FBB, 0x2A3C, 0x0015, 0x0900,
|
||||||
|
0x2800, 0x7680, 0x01EF, 0x2FFF,
|
||||||
|
0x73BF, 0x46FF, 0x0110, 0x0066,
|
||||||
|
0x533E, 0x2638, 0x01E5, 0x0000,
|
||||||
|
0x7FFF, 0x2BBF, 0x00DF, 0x2C0A,
|
||||||
|
0x7F1F, 0x463D, 0x74CF, 0x4CA5,
|
||||||
|
0x53FF, 0x03E0, 0x00DF, 0x2800,
|
||||||
|
0x433F, 0x72D2, 0x3045, 0x0822,
|
||||||
|
0x7FFA, 0x2A5F, 0x0014, 0x0003,
|
||||||
|
0x1EED, 0x215C, 0x42FC, 0x0060,
|
||||||
|
0x7FFF, 0x5EF7, 0x39CE, 0x0000,
|
||||||
|
0x4F5F, 0x630E, 0x159F, 0x3126,
|
||||||
|
0x637B, 0x121C, 0x0140, 0x0840,
|
||||||
|
0x66BC, 0x3FFF, 0x7EE0, 0x2C84,
|
||||||
|
0x5FFE, 0x3EBC, 0x0321, 0x0000,
|
||||||
|
0x63FF, 0x36DC, 0x11F6, 0x392A,
|
||||||
|
0x65EF, 0x7DBF, 0x035F, 0x2108,
|
||||||
|
0x2B6C, 0x7FFF, 0x1CD9, 0x0007,
|
||||||
|
0x53FC, 0x1F2F, 0x0E29, 0x0061,
|
||||||
|
0x36BE, 0x7EAF, 0x681A, 0x3C00,
|
||||||
|
0x7BBE, 0x329D, 0x1DE8, 0x0423,
|
||||||
|
0x739F, 0x6A9B, 0x7293, 0x0001,
|
||||||
|
0x5FFF, 0x6732, 0x3DA9, 0x2481,
|
||||||
|
0x577F, 0x3EBC, 0x456F, 0x1880,
|
||||||
|
0x6B57, 0x6E1B, 0x5010, 0x0007,
|
||||||
|
0x0F96, 0x2C97, 0x0045, 0x3200,
|
||||||
|
0x67FF, 0x2F17, 0x2230, 0x1548,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
char name[16];
|
||||||
|
unsigned palette_index;
|
||||||
|
} palette_assignments[] =
|
||||||
|
{
|
||||||
|
{"ZELDA", 5},
|
||||||
|
{"SUPER MARIOLAND", 6},
|
||||||
|
{"MARIOLAND2", 0x14},
|
||||||
|
{"SUPERMARIOLAND3", 2},
|
||||||
|
{"KIRBY DREAM LAND", 0xB},
|
||||||
|
{"HOSHINOKA-BI", 0xB},
|
||||||
|
{"KIRBY'S PINBALL", 3},
|
||||||
|
{"YOSSY NO TAMAGO", 0xC},
|
||||||
|
{"MARIO & YOSHI", 0xC},
|
||||||
|
{"YOSSY NO COOKIE", 4},
|
||||||
|
{"YOSHI'S COOKIE", 4},
|
||||||
|
{"DR.MARIO", 0x12},
|
||||||
|
{"TETRIS", 0x11},
|
||||||
|
{"YAKUMAN", 0x13},
|
||||||
|
{"METROID2", 0x1F},
|
||||||
|
{"KAERUNOTAMENI", 9},
|
||||||
|
{"GOLF", 0x18},
|
||||||
|
{"ALLEY WAY", 0x16},
|
||||||
|
{"BASEBALL", 0xF},
|
||||||
|
{"TENNIS", 0x17},
|
||||||
|
{"F1RACE", 0x1E},
|
||||||
|
{"KID ICARUS", 0xE},
|
||||||
|
{"QIX", 0x19},
|
||||||
|
{"SOLARSTRIKER", 7},
|
||||||
|
{"X", 0x1C},
|
||||||
|
{"GBWARS", 0x15},
|
||||||
|
};
|
||||||
|
|
||||||
static void command_ready(GB_gameboy_t *gb)
|
static void command_ready(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
/* SGB header commands are used to send the contents of the header to the SNES CPU.
|
/* SGB header commands are used to send the contents of the header to the SNES CPU.
|
||||||
@ -77,6 +151,8 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
0xE content bytes. The last command, FB, is padded with zeros, so information past the header is not sent. */
|
0xE content bytes. The last command, FB, is padded with zeros, so information past the header is not sent. */
|
||||||
|
|
||||||
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
|
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
|
||||||
|
if (gb->boot_rom_finished) return;
|
||||||
|
|
||||||
uint8_t checksum = 0;
|
uint8_t checksum = 0;
|
||||||
for (unsigned i = 2; i < 0x10; i++) {
|
for (unsigned i = 2; i < 0x10; i++) {
|
||||||
checksum += gb->sgb->command[i];
|
checksum += gb->sgb->command[i];
|
||||||
@ -86,14 +162,23 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
gb->sgb->disable_commands = true;
|
gb->sgb->disable_commands = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (gb->sgb->command[0] == 0xf9) {
|
unsigned index = (gb->sgb->command[0] >> 1) & 7;
|
||||||
if (gb->sgb->command[0xc] != 3) { // SGB Flag
|
if (index > 5) {
|
||||||
gb->sgb->disable_commands = true;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (gb->sgb->command[0] == 0xfb) {
|
memcpy(&gb->sgb->received_header[index * 14], &gb->sgb->command[2], 14);
|
||||||
if (gb->sgb->command[0x3] != 0x33) { // Old licensee code
|
if (gb->sgb->command[0] == 0xfb) {
|
||||||
|
if (gb->sgb->received_header[0x42] != 3 || gb->sgb->received_header[0x47] != 0x33) {
|
||||||
gb->sgb->disable_commands = true;
|
gb->sgb->disable_commands = true;
|
||||||
|
for (unsigned i = 0; i < sizeof(palette_assignments) / sizeof(palette_assignments[0]); i++) {
|
||||||
|
if (memcmp(palette_assignments[i].name, &gb->sgb->received_header[0x30], sizeof(palette_assignments[i].name)) == 0) {
|
||||||
|
gb->sgb->effective_palettes[0] = built_in_palettes[palette_assignments[i].palette_index * 4 - 4];
|
||||||
|
gb->sgb->effective_palettes[1] = built_in_palettes[palette_assignments[i].palette_index * 4 + 1 - 4];
|
||||||
|
gb->sgb->effective_palettes[2] = built_in_palettes[palette_assignments[i].palette_index * 4 + 2 - 4];
|
||||||
|
gb->sgb->effective_palettes[3] = built_in_palettes[palette_assignments[i].palette_index * 4 + 3 - 4];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -162,7 +247,7 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
gb->sgb->attribute_map[x + 20 * y] = inside_palette;
|
gb->sgb->attribute_map[x + 20 * y] = inside_palette;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(middle) {
|
else if (middle) {
|
||||||
gb->sgb->attribute_map[x + 20 * y] = middle_palette;
|
gb->sgb->attribute_map[x + 20 * y] = middle_palette;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,6 +255,52 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ATTR_CHR: {
|
||||||
|
struct __attribute__((packed)) {
|
||||||
|
uint8_t x, y;
|
||||||
|
uint16_t length;
|
||||||
|
uint8_t direction;
|
||||||
|
uint8_t data[];
|
||||||
|
} *command = (void *)(gb->sgb->command + 1);
|
||||||
|
|
||||||
|
uint16_t count = command->length;
|
||||||
|
#ifdef GB_BIG_ENDIAN
|
||||||
|
count = __builtin_bswap16(count);
|
||||||
|
#endif
|
||||||
|
uint8_t x = command->x;
|
||||||
|
uint8_t y = command->y;
|
||||||
|
if (x >= 20 || y >= 18 || (count + 3) / 4 > sizeof(gb->sgb->command) - sizeof(*command) - 1) {
|
||||||
|
/* TODO: Verify with the SFC BIOS */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < count; i++) {
|
||||||
|
uint8_t palette = (command->data[i / 4] >> (((~i) & 3) << 1)) & 3;
|
||||||
|
gb->sgb->attribute_map[x + 20 * y] = palette;
|
||||||
|
if (command->direction) {
|
||||||
|
y++;
|
||||||
|
if (y == 18) {
|
||||||
|
x++;
|
||||||
|
y = 0;
|
||||||
|
if (x == 20) {
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
x++;
|
||||||
|
if (x == 20) {
|
||||||
|
y++;
|
||||||
|
x = 0;
|
||||||
|
if (y == 18) {
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ATTR_LIN: {
|
case ATTR_LIN: {
|
||||||
struct {
|
struct {
|
||||||
uint8_t count;
|
uint8_t count;
|
||||||
@ -253,8 +384,15 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
// 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:
|
||||||
gb->sgb->player_count = (uint8_t[]){1, 2, 1, 4}[gb->sgb->command[1] & 3];
|
if (gb->sgb->player_count == 1) {
|
||||||
gb->sgb->current_player = gb->sgb->player_count - 1;
|
gb->sgb->current_player = 0;
|
||||||
|
}
|
||||||
|
gb->sgb->player_count = (gb->sgb->command[1] & 3) + 1; /* Todo: When breaking save state comaptibility,
|
||||||
|
fix this to be 0 based. */
|
||||||
|
if (gb->sgb->player_count == 3) {
|
||||||
|
gb->sgb->current_player++;
|
||||||
|
}
|
||||||
|
gb->sgb->mlt_lock = true;
|
||||||
break;
|
break;
|
||||||
case CHR_TRN:
|
case CHR_TRN:
|
||||||
gb->sgb->vram_transfer_countdown = 2;
|
gb->sgb->vram_transfer_countdown = 2;
|
||||||
@ -294,23 +432,33 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
||||||
{
|
{
|
||||||
if (!GB_is_sgb(gb)) return;
|
if (!GB_is_sgb(gb)) return;
|
||||||
|
if (!GB_is_hle_sgb(gb)) {
|
||||||
|
/* Notify via callback */
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (gb->sgb->disable_commands) return;
|
if (gb->sgb->disable_commands) return;
|
||||||
if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) 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) {
|
||||||
|
gb->sgb->mlt_lock ^= true;
|
||||||
|
}
|
||||||
|
|
||||||
switch ((value >> 4) & 3) {
|
switch ((value >> 4) & 3) {
|
||||||
case 3:
|
case 3:
|
||||||
gb->sgb->ready_for_pulse = true;
|
gb->sgb->ready_for_pulse = true;
|
||||||
/* TODO: This is the logic used by BGB which *should* work for most/all games, but a proper test ROM is needed */
|
if ((gb->sgb->player_count & 1) == 0 && !gb->sgb->mlt_lock) {
|
||||||
if (gb->sgb->player_count > 1 && (gb->io_registers[GB_IO_JOYP] & 0x30) == 0x10) {
|
|
||||||
gb->sgb->current_player++;
|
gb->sgb->current_player++;
|
||||||
gb->sgb->current_player &= gb->sgb->player_count - 1;
|
gb->sgb->current_player &= 3;
|
||||||
|
gb->sgb->mlt_lock = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -371,22 +519,9 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint8_t scale_channel(uint8_t x)
|
|
||||||
{
|
|
||||||
return (x << 3) | (x >> 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t convert_rgb15(GB_gameboy_t *gb, uint16_t color)
|
static uint32_t convert_rgb15(GB_gameboy_t *gb, uint16_t color)
|
||||||
{
|
{
|
||||||
uint8_t r = (color) & 0x1F;
|
return GB_convert_rgb15(gb, color, false);
|
||||||
uint8_t g = (color >> 5) & 0x1F;
|
|
||||||
uint8_t b = (color >> 10) & 0x1F;
|
|
||||||
|
|
||||||
r = scale_channel(r);
|
|
||||||
g = scale_channel(g);
|
|
||||||
b = scale_channel(b);
|
|
||||||
|
|
||||||
return gb->rgb_encode_callback(gb, r, g, b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_t fade)
|
static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_t fade)
|
||||||
@ -399,18 +534,19 @@ static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_
|
|||||||
if (g >= 0x20) g = 0;
|
if (g >= 0x20) g = 0;
|
||||||
if (b >= 0x20) b = 0;
|
if (b >= 0x20) b = 0;
|
||||||
|
|
||||||
r = scale_channel(r);
|
color = r | (g << 5) | (b << 10);
|
||||||
g = scale_channel(g);
|
|
||||||
b = scale_channel(b);
|
|
||||||
|
|
||||||
return gb->rgb_encode_callback(gb, r, g, b);
|
return GB_convert_rgb15(gb, color, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
static void render_boot_animation (GB_gameboy_t *gb)
|
static void render_boot_animation (GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
#include "sgb_animation_logo.inc"
|
#include "graphics/sgb_animation_logo.inc"
|
||||||
uint32_t *output = &gb->screen[48 + 40 * 256];
|
uint32_t *output = gb->screen;
|
||||||
|
if (gb->border_mode != GB_BORDER_NEVER) {
|
||||||
|
output += 48 + 40 * 256;
|
||||||
|
}
|
||||||
uint8_t *input = animation_logo;
|
uint8_t *input = animation_logo;
|
||||||
unsigned fade_blue = 0;
|
unsigned fade_blue = 0;
|
||||||
unsigned fade_red = 0;
|
unsigned fade_red = 0;
|
||||||
@ -458,7 +594,9 @@ static void render_boot_animation (GB_gameboy_t *gb)
|
|||||||
input++;
|
input++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output += 256 - 160;
|
if (gb->border_mode != GB_BORDER_NEVER) {
|
||||||
|
output += 256 - 160;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,14 +608,6 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++;
|
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++;
|
||||||
|
|
||||||
if (!gb->screen || !gb->rgb_encode_callback) return;
|
|
||||||
|
|
||||||
if (gb->sgb->mask_mode != MASK_FREEZE) {
|
|
||||||
memcpy(gb->sgb->effective_screen_buffer,
|
|
||||||
gb->sgb->screen_buffer,
|
|
||||||
sizeof(gb->sgb->effective_screen_buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gb->sgb->vram_transfer_countdown) {
|
if (gb->sgb->vram_transfer_countdown) {
|
||||||
if (--gb->sgb->vram_transfer_countdown == 0) {
|
if (--gb->sgb->vram_transfer_countdown == 0) {
|
||||||
@ -540,16 +670,27 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!gb->screen || !gb->rgb_encode_callback || gb->disable_rendering) 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, gb->sgb->effective_palettes[i]);
|
colors[i] = convert_rgb15(gb, gb->sgb->effective_palettes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gb->sgb->mask_mode != MASK_FREEZE) {
|
||||||
|
memcpy(gb->sgb->effective_screen_buffer,
|
||||||
|
gb->sgb->screen_buffer,
|
||||||
|
sizeof(gb->sgb->effective_screen_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) {
|
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) {
|
||||||
render_boot_animation(gb);
|
render_boot_animation(gb);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
uint32_t *output = &gb->screen[48 + 40 * 256];
|
uint32_t *output = gb->screen;
|
||||||
|
if (gb->border_mode != GB_BORDER_NEVER) {
|
||||||
|
output += 48 + 40 * 256;
|
||||||
|
}
|
||||||
uint8_t *input = gb->sgb->effective_screen_buffer;
|
uint8_t *input = gb->sgb->effective_screen_buffer;
|
||||||
switch ((mask_mode_t) gb->sgb->mask_mode) {
|
switch ((mask_mode_t) gb->sgb->mask_mode) {
|
||||||
case MASK_DISABLED:
|
case MASK_DISABLED:
|
||||||
@ -559,7 +700,9 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
uint8_t palette = gb->sgb->attribute_map[x / 8 + y / 8 * 20] & 3;
|
uint8_t palette = gb->sgb->attribute_map[x / 8 + y / 8 * 20] & 3;
|
||||||
*(output++) = colors[(*(input++) & 3) + palette * 4];
|
*(output++) = colors[(*(input++) & 3) + palette * 4];
|
||||||
}
|
}
|
||||||
output += 256 - 160;
|
if (gb->border_mode != GB_BORDER_NEVER) {
|
||||||
|
output += 256 - 160;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -570,7 +713,9 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
for (unsigned x = 0; x < 160; x++) {
|
for (unsigned x = 0; x < 160; x++) {
|
||||||
*(output++) = black;
|
*(output++) = black;
|
||||||
}
|
}
|
||||||
output += 256 - 160;
|
if (gb->border_mode != GB_BORDER_NEVER) {
|
||||||
|
output += 256 - 160;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -580,7 +725,9 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
for (unsigned x = 0; x < 160; x++) {
|
for (unsigned x = 0; x < 160; x++) {
|
||||||
*(output++) = colors[0];
|
*(output++) = colors[0];
|
||||||
}
|
}
|
||||||
output += 256 - 160;
|
if (gb->border_mode != GB_BORDER_NEVER) {
|
||||||
|
output += 256 - 160;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -617,6 +764,9 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
if (tile_x >= 6 && tile_x < 26 && tile_y >= 5 && tile_y < 23) {
|
if (tile_x >= 6 && tile_x < 26 && tile_y >= 5 && tile_y < 23) {
|
||||||
gb_area = true;
|
gb_area = true;
|
||||||
}
|
}
|
||||||
|
else if (gb->border_mode == GB_BORDER_NEVER) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
uint16_t tile = gb->sgb->border.map[tile_x + tile_y * 32];
|
uint16_t tile = gb->sgb->border.map[tile_x + tile_y * 32];
|
||||||
uint8_t flip_x = (tile & 0x4000)? 0x7 : 0;
|
uint8_t flip_x = (tile & 0x4000)? 0x7 : 0;
|
||||||
uint8_t flip_y = (tile & 0x8000)? 0x7 : 0;
|
uint8_t flip_y = (tile & 0x8000)? 0x7 : 0;
|
||||||
@ -624,12 +774,19 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
for (unsigned y = 0; y < 8; y++) {
|
for (unsigned y = 0; y < 8; y++) {
|
||||||
for (unsigned x = 0; x < 8; x++) {
|
for (unsigned x = 0; x < 8; x++) {
|
||||||
uint8_t color = gb->sgb->border.tiles[(tile & 0xFF) * 64 + (x ^ flip_x) + (y ^ flip_y) * 8] & 0xF;
|
uint8_t color = gb->sgb->border.tiles[(tile & 0xFF) * 64 + (x ^ flip_x) + (y ^ flip_y) * 8] & 0xF;
|
||||||
if (color == 0) {
|
uint32_t *output = gb->screen;
|
||||||
if (gb_area) continue;
|
if (gb->border_mode == GB_BORDER_NEVER) {
|
||||||
gb->screen[tile_x * 8 + x + (tile_y * 8 + y) * 0x100] = colors[0];
|
output += (tile_x - 6) * 8 + x + ((tile_y - 5) * 8 + y) * 160;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gb->screen[tile_x * 8 + x + (tile_y * 8 + y) * 0x100] = border_colors[color + palette * 16];
|
output += tile_x * 8 + x + (tile_y * 8 + y) * 256;
|
||||||
|
}
|
||||||
|
if (color == 0) {
|
||||||
|
if (gb_area) continue;
|
||||||
|
*output = colors[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*output = border_colors[color + palette * 16];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -640,12 +797,12 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
|||||||
void GB_sgb_load_default_data(GB_gameboy_t *gb)
|
void GB_sgb_load_default_data(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
|
|
||||||
#include "sgb_border.inc"
|
#include "graphics/sgb_border.inc"
|
||||||
|
|
||||||
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));
|
||||||
|
|
||||||
/* Expend tileset */
|
/* Expand tileset */
|
||||||
for (unsigned tile = 0; tile < sizeof(tiles) / 32; tile++) {
|
for (unsigned tile = 0; tile < sizeof(tiles) / 32; tile++) {
|
||||||
for (unsigned y = 0; y < 8; y++) {
|
for (unsigned y = 0; y < 8; y++) {
|
||||||
for (unsigned x = 0; x < 8; x++) {
|
for (unsigned x = 0; x < 8; x++) {
|
||||||
@ -668,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] = 0x639E;
|
gb->sgb->effective_palettes[0] = built_in_palettes[0];
|
||||||
gb->sgb->effective_palettes[1] = 0x263A;
|
gb->sgb->effective_palettes[1] = built_in_palettes[1];
|
||||||
gb->sgb->effective_palettes[2] = 0x10D4;
|
gb->sgb->effective_palettes[2] = built_in_palettes[2];
|
||||||
gb->sgb->effective_palettes[3] = 0x2866;
|
gb->sgb->effective_palettes[3] = built_in_palettes[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
static double fm_synth(double phase)
|
static double fm_synth(double phase)
|
||||||
|
27
Core/sgb.h
27
Core/sgb.h
@ -5,6 +5,16 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
typedef struct GB_sgb_s GB_sgb_t;
|
typedef struct GB_sgb_s GB_sgb_t;
|
||||||
|
typedef struct {
|
||||||
|
uint8_t tiles[0x100 * 8 * 8]; /* High nibble not used*/
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint16_t map[32 * 32];
|
||||||
|
uint16_t palette[16 * 4];
|
||||||
|
};
|
||||||
|
uint16_t raw_data[0x440];
|
||||||
|
};
|
||||||
|
} GB_sgb_border_t;
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
struct GB_sgb_s {
|
struct GB_sgb_s {
|
||||||
@ -29,16 +39,7 @@ struct GB_sgb_s {
|
|||||||
uint8_t vram_transfer_countdown, transfer_dest;
|
uint8_t vram_transfer_countdown, transfer_dest;
|
||||||
|
|
||||||
/* Border */
|
/* Border */
|
||||||
struct {
|
GB_sgb_border_t border, pending_border;
|
||||||
uint8_t tiles[0x100 * 8 * 8]; /* High nibble not used*/
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
uint16_t map[32 * 32];
|
|
||||||
uint16_t palette[16 * 4];
|
|
||||||
};
|
|
||||||
uint16_t raw_data[0x440];
|
|
||||||
};
|
|
||||||
} border, pending_border;
|
|
||||||
uint8_t border_animation;
|
uint8_t border_animation;
|
||||||
|
|
||||||
/* Colorization */
|
/* Colorization */
|
||||||
@ -49,6 +50,12 @@ struct GB_sgb_s {
|
|||||||
|
|
||||||
/* Intro */
|
/* Intro */
|
||||||
int16_t intro_animation;
|
int16_t intro_animation;
|
||||||
|
|
||||||
|
/* GB Header */
|
||||||
|
uint8_t received_header[0x54];
|
||||||
|
|
||||||
|
/* Multiplayer (cont) */
|
||||||
|
bool mlt_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
|
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
|
||||||
|
205
Core/sm83_cpu.c
205
Core/sm83_cpu.c
@ -18,6 +18,9 @@ typedef enum {
|
|||||||
GB_CONFLICT_STAT_DMG,
|
GB_CONFLICT_STAT_DMG,
|
||||||
GB_CONFLICT_PALETTE_DMG,
|
GB_CONFLICT_PALETTE_DMG,
|
||||||
GB_CONFLICT_PALETTE_CGB,
|
GB_CONFLICT_PALETTE_CGB,
|
||||||
|
GB_CONFLICT_DMG_LCDC,
|
||||||
|
GB_CONFLICT_SGB_LCDC,
|
||||||
|
GB_CONFLICT_WX,
|
||||||
} GB_conflict_t;
|
} GB_conflict_t;
|
||||||
|
|
||||||
/* Todo: How does double speed mode affect these? */
|
/* Todo: How does double speed mode affect these? */
|
||||||
@ -29,25 +32,42 @@ static const GB_conflict_t cgb_conflict_map[0x80] = {
|
|||||||
[GB_IO_OBP0] = GB_CONFLICT_PALETTE_CGB,
|
[GB_IO_OBP0] = GB_CONFLICT_PALETTE_CGB,
|
||||||
[GB_IO_OBP1] = GB_CONFLICT_PALETTE_CGB,
|
[GB_IO_OBP1] = GB_CONFLICT_PALETTE_CGB,
|
||||||
|
|
||||||
|
|
||||||
/* Todo: most values not verified, and probably differ between revisions */
|
/* Todo: most values not verified, and probably differ between revisions */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Todo: verify on an MGB */
|
||||||
static const GB_conflict_t dmg_conflict_map[0x80] = {
|
static const GB_conflict_t dmg_conflict_map[0x80] = {
|
||||||
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
|
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
|
||||||
[GB_IO_LYC] = GB_CONFLICT_READ_OLD,
|
[GB_IO_LYC] = GB_CONFLICT_READ_OLD,
|
||||||
[GB_IO_LCDC] = GB_CONFLICT_READ_NEW,
|
[GB_IO_LCDC] = GB_CONFLICT_DMG_LCDC,
|
||||||
[GB_IO_SCY] = GB_CONFLICT_READ_NEW,
|
[GB_IO_SCY] = GB_CONFLICT_READ_NEW,
|
||||||
[GB_IO_STAT] = GB_CONFLICT_STAT_DMG,
|
[GB_IO_STAT] = GB_CONFLICT_STAT_DMG,
|
||||||
|
|
||||||
/* Todo: these are GB_CONFLICT_READ_NEW on MGB/SGB2 */
|
|
||||||
[GB_IO_BGP] = GB_CONFLICT_PALETTE_DMG,
|
[GB_IO_BGP] = GB_CONFLICT_PALETTE_DMG,
|
||||||
[GB_IO_OBP0] = GB_CONFLICT_PALETTE_DMG,
|
[GB_IO_OBP0] = GB_CONFLICT_PALETTE_DMG,
|
||||||
[GB_IO_OBP1] = GB_CONFLICT_PALETTE_DMG,
|
[GB_IO_OBP1] = GB_CONFLICT_PALETTE_DMG,
|
||||||
|
[GB_IO_WY] = GB_CONFLICT_READ_OLD,
|
||||||
|
[GB_IO_WX] = GB_CONFLICT_WX,
|
||||||
|
|
||||||
|
/* Todo: these were not verified at all */
|
||||||
|
[GB_IO_SCX] = GB_CONFLICT_READ_NEW,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Todo: Verify on an SGB1 */
|
||||||
|
static const GB_conflict_t sgb_conflict_map[0x80] = {
|
||||||
|
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
|
||||||
|
[GB_IO_LYC] = GB_CONFLICT_READ_OLD,
|
||||||
|
[GB_IO_LCDC] = GB_CONFLICT_SGB_LCDC,
|
||||||
|
[GB_IO_SCY] = GB_CONFLICT_READ_NEW,
|
||||||
|
[GB_IO_STAT] = GB_CONFLICT_STAT_DMG,
|
||||||
|
|
||||||
|
[GB_IO_BGP] = GB_CONFLICT_READ_NEW,
|
||||||
|
[GB_IO_OBP0] = GB_CONFLICT_READ_NEW,
|
||||||
|
[GB_IO_OBP1] = GB_CONFLICT_READ_NEW,
|
||||||
|
[GB_IO_WY] = GB_CONFLICT_READ_OLD,
|
||||||
|
[GB_IO_WX] = GB_CONFLICT_WX,
|
||||||
|
|
||||||
/* Todo: these were not verified at all */
|
/* Todo: these were not verified at all */
|
||||||
[GB_IO_WY] = GB_CONFLICT_READ_NEW,
|
|
||||||
[GB_IO_WX] = GB_CONFLICT_READ_NEW,
|
|
||||||
[GB_IO_SCX] = GB_CONFLICT_READ_NEW,
|
[GB_IO_SCX] = GB_CONFLICT_READ_NEW,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -92,7 +112,17 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
assert(gb->pending_cycles);
|
assert(gb->pending_cycles);
|
||||||
GB_conflict_t conflict = GB_CONFLICT_READ_OLD;
|
GB_conflict_t conflict = GB_CONFLICT_READ_OLD;
|
||||||
if ((addr & 0xFF80) == 0xFF00) {
|
if ((addr & 0xFF80) == 0xFF00) {
|
||||||
conflict = (GB_is_cgb(gb)? cgb_conflict_map : dmg_conflict_map)[addr & 0x7F];
|
const GB_conflict_t *map = NULL;
|
||||||
|
if (GB_is_cgb(gb)) {
|
||||||
|
map = cgb_conflict_map;
|
||||||
|
}
|
||||||
|
else if (GB_is_sgb(gb)) {
|
||||||
|
map = sgb_conflict_map;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
map = dmg_conflict_map;
|
||||||
|
}
|
||||||
|
conflict = map[addr & 0x7F];
|
||||||
}
|
}
|
||||||
switch (conflict) {
|
switch (conflict) {
|
||||||
case GB_CONFLICT_READ_OLD:
|
case GB_CONFLICT_READ_OLD:
|
||||||
@ -164,6 +194,53 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
gb->pending_cycles = 6;
|
gb->pending_cycles = 6;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case GB_CONFLICT_DMG_LCDC: {
|
||||||
|
/* Similar to the palette registers, these interact directly with the LCD, so they appear to be affected by it. Both my DMG (B, blob) and Game Boy Light behave this way though.
|
||||||
|
|
||||||
|
Additionally, LCDC.1 is very nasty because on the it is read both by the FIFO when popping pixels,
|
||||||
|
and the sprite-fetching state machine, and both behave differently when it comes to access conflicts.
|
||||||
|
Hacks ahead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t old_value = GB_read_memory(gb, addr);
|
||||||
|
GB_advance_cycles(gb, gb->pending_cycles - 2);
|
||||||
|
|
||||||
|
if (/* gb->model != GB_MODEL_MGB && */ gb->position_in_line == 0 && (old_value & 2) && !(value & 2)) {
|
||||||
|
old_value &= ~2;
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_write_memory(gb, addr, old_value | (value & 1));
|
||||||
|
GB_advance_cycles(gb, 1);
|
||||||
|
GB_write_memory(gb, addr, value);
|
||||||
|
gb->pending_cycles = 5;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case GB_CONFLICT_SGB_LCDC: {
|
||||||
|
/* Simplified version of the above */
|
||||||
|
|
||||||
|
uint8_t old_value = GB_read_memory(gb, addr);
|
||||||
|
GB_advance_cycles(gb, gb->pending_cycles - 2);
|
||||||
|
/* Hack to force aborting object fetch */
|
||||||
|
GB_write_memory(gb, addr, value);
|
||||||
|
GB_write_memory(gb, addr, old_value);
|
||||||
|
GB_advance_cycles(gb, 1);
|
||||||
|
GB_write_memory(gb, addr, value);
|
||||||
|
gb->pending_cycles = 5;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case GB_CONFLICT_WX:
|
||||||
|
GB_advance_cycles(gb, gb->pending_cycles);
|
||||||
|
GB_write_memory(gb, addr, value);
|
||||||
|
gb->wx_just_changed = true;
|
||||||
|
GB_advance_cycles(gb, 1);
|
||||||
|
gb->wx_just_changed = false;
|
||||||
|
gb->pending_cycles = 3;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,6 +285,26 @@ static void nop(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void enter_stop_mode(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
gb->stopped = true;
|
||||||
|
gb->oam_ppu_blocked = !gb->oam_read_blocked;
|
||||||
|
gb->vram_ppu_blocked = !gb->vram_read_blocked;
|
||||||
|
gb->cgb_palettes_ppu_blocked = !gb->cgb_palettes_blocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void leave_stop_mode(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
/* The CPU takes more time to wake up then the other components */
|
||||||
|
for (unsigned i = 0x200; i--;) {
|
||||||
|
GB_advance_cycles(gb, 0x10);
|
||||||
|
}
|
||||||
|
gb->stopped = false;
|
||||||
|
gb->oam_ppu_blocked = false;
|
||||||
|
gb->vram_ppu_blocked = false;
|
||||||
|
gb->cgb_palettes_ppu_blocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
static void stop(GB_gameboy_t *gb, uint8_t opcode)
|
static void stop(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
if (gb->io_registers[GB_IO_KEY1] & 0x1) {
|
if (gb->io_registers[GB_IO_KEY1] & 0x1) {
|
||||||
@ -224,9 +321,8 @@ static void stop(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
gb->cgb_double_speed ^= true;
|
gb->cgb_double_speed ^= true;
|
||||||
gb->io_registers[GB_IO_KEY1] = 0;
|
gb->io_registers[GB_IO_KEY1] = 0;
|
||||||
|
|
||||||
for (unsigned i = 0x800; i--;) {
|
enter_stop_mode(gb);
|
||||||
GB_advance_cycles(gb, 0x40);
|
leave_stop_mode(gb);
|
||||||
}
|
|
||||||
|
|
||||||
if (!needs_alignment) {
|
if (!needs_alignment) {
|
||||||
GB_advance_cycles(gb, 0x4);
|
GB_advance_cycles(gb, 0x4);
|
||||||
@ -242,7 +338,7 @@ static void stop(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
gb->halted = true;
|
gb->halted = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gb->stopped = true;
|
enter_stop_mode(gb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +386,7 @@ static void inc_hr(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
uint8_t register_id;
|
uint8_t register_id;
|
||||||
register_id = ((opcode >> 4) + 1) & 0x03;
|
register_id = ((opcode >> 4) + 1) & 0x03;
|
||||||
gb->registers[register_id] += 0x100;
|
gb->registers[register_id] += 0x100;
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
|
|
||||||
if ((gb->registers[register_id] & 0x0F00) == 0) {
|
if ((gb->registers[register_id] & 0x0F00) == 0) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
@ -306,7 +402,7 @@ static void dec_hr(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
register_id = ((opcode >> 4) + 1) & 0x03;
|
register_id = ((opcode >> 4) + 1) & 0x03;
|
||||||
gb->registers[register_id] -= 0x100;
|
gb->registers[register_id] -= 0x100;
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_SUBTRACT_FLAG;
|
||||||
|
|
||||||
if ((gb->registers[register_id] & 0x0F00) == 0xF00) {
|
if ((gb->registers[register_id] & 0x0F00) == 0xF00) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
@ -356,7 +452,7 @@ static void ld_da16_sp(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
addr = cycle_read_inc_oam_bug(gb, gb->pc++);
|
addr = cycle_read_inc_oam_bug(gb, gb->pc++);
|
||||||
addr |= cycle_read_inc_oam_bug(gb, gb->pc++) << 8;
|
addr |= cycle_read_inc_oam_bug(gb, gb->pc++) << 8;
|
||||||
cycle_write(gb, addr, gb->registers[GB_REGISTER_SP] & 0xFF);
|
cycle_write(gb, addr, gb->registers[GB_REGISTER_SP] & 0xFF);
|
||||||
cycle_write(gb, addr+1, gb->registers[GB_REGISTER_SP] >> 8);
|
cycle_write(gb, addr + 1, gb->registers[GB_REGISTER_SP] >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode)
|
static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
@ -368,14 +464,14 @@ static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
register_id = (opcode >> 4) + 1;
|
register_id = (opcode >> 4) + 1;
|
||||||
rr = gb->registers[register_id];
|
rr = gb->registers[register_id];
|
||||||
gb->registers[GB_REGISTER_HL] = hl + rr;
|
gb->registers[GB_REGISTER_HL] = hl + rr;
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_CARRY_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBTRACT_FLAG | GB_CARRY_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
|
|
||||||
/* The meaning of the Half Carry flag is really hard to track -_- */
|
/* The meaning of the Half Carry flag is really hard to track -_- */
|
||||||
if (((hl & 0xFFF) + (rr & 0xFFF)) & 0x1000) {
|
if (((hl & 0xFFF) + (rr & 0xFFF)) & 0x1000) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ((unsigned long) hl + (unsigned long) rr) & 0x10000) {
|
if ( ((unsigned) hl + (unsigned) rr) & 0x10000) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -404,7 +500,7 @@ static void inc_lr(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
value = (gb->registers[register_id] & 0xFF) + 1;
|
value = (gb->registers[register_id] & 0xFF) + 1;
|
||||||
gb->registers[register_id] = (gb->registers[register_id] & 0xFF00) | value;
|
gb->registers[register_id] = (gb->registers[register_id] & 0xFF00) | value;
|
||||||
|
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
|
|
||||||
if ((gb->registers[register_id] & 0x0F) == 0) {
|
if ((gb->registers[register_id] & 0x0F) == 0) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
@ -424,7 +520,7 @@ static void dec_lr(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
gb->registers[register_id] = (gb->registers[register_id] & 0xFF00) | value;
|
gb->registers[register_id] = (gb->registers[register_id] & 0xFF00) | value;
|
||||||
|
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_SUBTRACT_FLAG;
|
||||||
|
|
||||||
if ((gb->registers[register_id] & 0x0F) == 0xF) {
|
if ((gb->registers[register_id] & 0x0F) == 0xF) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
@ -505,7 +601,7 @@ static void daa(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
|
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(0xFF00 | GB_ZERO_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(0xFF00 | GB_ZERO_FLAG);
|
||||||
|
|
||||||
if (gb->registers[GB_REGISTER_AF] & GB_SUBSTRACT_FLAG) {
|
if (gb->registers[GB_REGISTER_AF] & GB_SUBTRACT_FLAG) {
|
||||||
if (gb->registers[GB_REGISTER_AF] & GB_HALF_CARRY_FLAG) {
|
if (gb->registers[GB_REGISTER_AF] & GB_HALF_CARRY_FLAG) {
|
||||||
result = (result - 0x06) & 0xFF;
|
result = (result - 0x06) & 0xFF;
|
||||||
}
|
}
|
||||||
@ -539,19 +635,19 @@ static void daa(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
static void cpl(GB_gameboy_t *gb, uint8_t opcode)
|
static void cpl(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
gb->registers[GB_REGISTER_AF] ^= 0xFF00;
|
gb->registers[GB_REGISTER_AF] ^= 0xFF00;
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG | GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG | GB_SUBTRACT_FLAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void scf(GB_gameboy_t *gb, uint8_t opcode)
|
static void scf(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_HALF_CARRY_FLAG | GB_SUBSTRACT_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_HALF_CARRY_FLAG | GB_SUBTRACT_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ccf(GB_gameboy_t *gb, uint8_t opcode)
|
static void ccf(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
gb->registers[GB_REGISTER_AF] ^= GB_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] ^= GB_CARRY_FLAG;
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_HALF_CARRY_FLAG | GB_SUBSTRACT_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_HALF_CARRY_FLAG | GB_SUBTRACT_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode)
|
static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
@ -582,7 +678,7 @@ static void inc_dhl(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
value = cycle_read(gb, gb->registers[GB_REGISTER_HL]) + 1;
|
value = cycle_read(gb, gb->registers[GB_REGISTER_HL]) + 1;
|
||||||
cycle_write(gb, gb->registers[GB_REGISTER_HL], value);
|
cycle_write(gb, gb->registers[GB_REGISTER_HL], value);
|
||||||
|
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
if ((value & 0x0F) == 0) {
|
if ((value & 0x0F) == 0) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
@ -599,7 +695,7 @@ static void dec_dhl(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
cycle_write(gb, gb->registers[GB_REGISTER_HL], value);
|
cycle_write(gb, gb->registers[GB_REGISTER_HL], value);
|
||||||
|
|
||||||
gb->registers[GB_REGISTER_AF] &= ~( GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~( GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_SUBTRACT_FLAG;
|
||||||
if ((value & 0x0F) == 0x0F) {
|
if ((value & 0x0F) == 0x0F) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
@ -693,6 +789,13 @@ LD_X_Y(l,b) LD_X_Y(l,c) LD_X_Y(l,d) LD_X_Y(l,e) LD_X_Y(l,h) LD_X_DHL
|
|||||||
LD_DHL_Y(b) LD_DHL_Y(c) LD_DHL_Y(d) LD_DHL_Y(e) LD_DHL_Y(h) LD_DHL_Y(l) LD_DHL_Y(a)
|
LD_DHL_Y(b) LD_DHL_Y(c) LD_DHL_Y(d) LD_DHL_Y(e) LD_DHL_Y(h) LD_DHL_Y(l) LD_DHL_Y(a)
|
||||||
LD_X_Y(a,b) LD_X_Y(a,c) LD_X_Y(a,d) LD_X_Y(a,e) LD_X_Y(a,h) LD_X_Y(a,l) LD_X_DHL(a)
|
LD_X_Y(a,b) LD_X_Y(a,c) LD_X_Y(a,d) LD_X_Y(a,e) LD_X_Y(a,h) LD_X_Y(a,l) LD_X_DHL(a)
|
||||||
|
|
||||||
|
// fire the debugger if software breakpoints are enabled
|
||||||
|
static void ld_b_b(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
if (gb->has_software_breakpoints) {
|
||||||
|
gb->debug_stopped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void add_a_r(GB_gameboy_t *gb, uint8_t opcode)
|
static void add_a_r(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
@ -706,7 +809,7 @@ static void add_a_r(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
if ((a & 0xF) + (value & 0xF) > 0x0F) {
|
if ((a & 0xF) + (value & 0xF) > 0x0F) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
if (((unsigned long) a) + ((unsigned long) value) > 0xFF) {
|
if (((unsigned) a) + ((unsigned) value) > 0xFF) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -725,7 +828,7 @@ static void adc_a_r(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
if ((a & 0xF) + (value & 0xF) + carry > 0x0F) {
|
if ((a & 0xF) + (value & 0xF) + carry > 0x0F) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
if (((unsigned long) a) + ((unsigned long) value) + carry > 0xFF) {
|
if (((unsigned) a) + ((unsigned) value) + carry > 0xFF) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -735,7 +838,7 @@ static void sub_a_r(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
uint8_t value, a;
|
uint8_t value, a;
|
||||||
value = get_src_value(gb, opcode);
|
value = get_src_value(gb, opcode);
|
||||||
a = gb->registers[GB_REGISTER_AF] >> 8;
|
a = gb->registers[GB_REGISTER_AF] >> 8;
|
||||||
gb->registers[GB_REGISTER_AF] = ((a - value) << 8) | GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] = ((a - value) << 8) | GB_SUBTRACT_FLAG;
|
||||||
if (a == value) {
|
if (a == value) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
||||||
}
|
}
|
||||||
@ -753,7 +856,7 @@ static void sbc_a_r(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
value = get_src_value(gb, opcode);
|
value = get_src_value(gb, opcode);
|
||||||
a = gb->registers[GB_REGISTER_AF] >> 8;
|
a = gb->registers[GB_REGISTER_AF] >> 8;
|
||||||
carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0;
|
carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0;
|
||||||
gb->registers[GB_REGISTER_AF] = ((a - value - carry) << 8) | GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] = ((a - value - carry) << 8) | GB_SUBTRACT_FLAG;
|
||||||
|
|
||||||
if ((uint8_t) (a - value - carry) == 0) {
|
if ((uint8_t) (a - value - carry) == 0) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
||||||
@ -761,7 +864,7 @@ static void sbc_a_r(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
if ((a & 0xF) < (value & 0xF) + carry) {
|
if ((a & 0xF) < (value & 0xF) + carry) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
if (((unsigned long) a) - ((unsigned long) value) - carry > 0xFF) {
|
if (((unsigned) a) - ((unsigned) value) - carry > 0xFF) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -805,7 +908,7 @@ static void cp_a_r(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
value = get_src_value(gb, opcode);
|
value = get_src_value(gb, opcode);
|
||||||
a = gb->registers[GB_REGISTER_AF] >> 8;
|
a = gb->registers[GB_REGISTER_AF] >> 8;
|
||||||
gb->registers[GB_REGISTER_AF] &= 0xFF00;
|
gb->registers[GB_REGISTER_AF] &= 0xFF00;
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_SUBTRACT_FLAG;
|
||||||
if (a == value) {
|
if (a == value) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
||||||
}
|
}
|
||||||
@ -821,10 +924,7 @@ static void halt(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
{
|
{
|
||||||
assert(gb->pending_cycles == 4);
|
assert(gb->pending_cycles == 4);
|
||||||
gb->pending_cycles = 0;
|
gb->pending_cycles = 0;
|
||||||
GB_advance_cycles(gb, 1);
|
GB_advance_cycles(gb, 4);
|
||||||
GB_advance_cycles(gb, 1);
|
|
||||||
GB_advance_cycles(gb, 1);
|
|
||||||
GB_advance_cycles(gb, 1);
|
|
||||||
|
|
||||||
gb->halted = true;
|
gb->halted = true;
|
||||||
/* Despite what some online documentations say, the HALT bug also happens on a CGB, in both CGB and DMG modes. */
|
/* Despite what some online documentations say, the HALT bug also happens on a CGB, in both CGB and DMG modes. */
|
||||||
@ -905,7 +1005,7 @@ static void add_a_d8(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
if ((a & 0xF) + (value & 0xF) > 0x0F) {
|
if ((a & 0xF) + (value & 0xF) > 0x0F) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
if (((unsigned long) a) + ((unsigned long) value) > 0xFF) {
|
if (((unsigned) a) + ((unsigned) value) > 0xFF) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -924,7 +1024,7 @@ static void adc_a_d8(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
if ((a & 0xF) + (value & 0xF) + carry > 0x0F) {
|
if ((a & 0xF) + (value & 0xF) + carry > 0x0F) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
if (((unsigned long) a) + ((unsigned long) value) + carry > 0xFF) {
|
if (((unsigned) a) + ((unsigned) value) + carry > 0xFF) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -934,7 +1034,7 @@ static void sub_a_d8(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
uint8_t value, a;
|
uint8_t value, a;
|
||||||
value = cycle_read_inc_oam_bug(gb, gb->pc++);
|
value = cycle_read_inc_oam_bug(gb, gb->pc++);
|
||||||
a = gb->registers[GB_REGISTER_AF] >> 8;
|
a = gb->registers[GB_REGISTER_AF] >> 8;
|
||||||
gb->registers[GB_REGISTER_AF] = ((a - value) << 8) | GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] = ((a - value) << 8) | GB_SUBTRACT_FLAG;
|
||||||
if (a == value) {
|
if (a == value) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
||||||
}
|
}
|
||||||
@ -952,7 +1052,7 @@ static void sbc_a_d8(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
value = cycle_read_inc_oam_bug(gb, gb->pc++);
|
value = cycle_read_inc_oam_bug(gb, gb->pc++);
|
||||||
a = gb->registers[GB_REGISTER_AF] >> 8;
|
a = gb->registers[GB_REGISTER_AF] >> 8;
|
||||||
carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0;
|
carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0;
|
||||||
gb->registers[GB_REGISTER_AF] = ((a - value - carry) << 8) | GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] = ((a - value - carry) << 8) | GB_SUBTRACT_FLAG;
|
||||||
|
|
||||||
if ((uint8_t) (a - value - carry) == 0) {
|
if ((uint8_t) (a - value - carry) == 0) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
||||||
@ -960,7 +1060,7 @@ static void sbc_a_d8(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
if ((a & 0xF) < (value & 0xF) + carry) {
|
if ((a & 0xF) < (value & 0xF) + carry) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
if (((unsigned long) a) - ((unsigned long) value) - carry > 0xFF) {
|
if (((unsigned) a) - ((unsigned) value) - carry > 0xFF) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1004,7 +1104,7 @@ static void cp_a_d8(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
value = cycle_read_inc_oam_bug(gb, gb->pc++);
|
value = cycle_read_inc_oam_bug(gb, gb->pc++);
|
||||||
a = gb->registers[GB_REGISTER_AF] >> 8;
|
a = gb->registers[GB_REGISTER_AF] >> 8;
|
||||||
gb->registers[GB_REGISTER_AF] &= 0xFF00;
|
gb->registers[GB_REGISTER_AF] &= 0xFF00;
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_SUBTRACT_FLAG;
|
||||||
if (a == value) {
|
if (a == value) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
||||||
}
|
}
|
||||||
@ -1126,7 +1226,7 @@ static void ld_a_da16(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
uint16_t addr;
|
uint16_t addr;
|
||||||
gb->registers[GB_REGISTER_AF] &= 0xFF;
|
gb->registers[GB_REGISTER_AF] &= 0xFF;
|
||||||
addr = cycle_read_inc_oam_bug(gb, gb->pc++);
|
addr = cycle_read_inc_oam_bug(gb, gb->pc++);
|
||||||
addr |= cycle_read_inc_oam_bug(gb, gb->pc++) << 8 ;
|
addr |= cycle_read_inc_oam_bug(gb, gb->pc++) << 8;
|
||||||
gb->registers[GB_REGISTER_AF] |= cycle_read(gb, addr) << 8;
|
gb->registers[GB_REGISTER_AF] |= cycle_read(gb, addr) << 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1314,10 +1414,10 @@ static void bit_r(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((opcode & 0xC0) == 0x80) { /* res */
|
else if ((opcode & 0xC0) == 0x80) { /* res */
|
||||||
set_src_value(gb, opcode, value & ~bit) ;
|
set_src_value(gb, opcode, value & ~bit);
|
||||||
}
|
}
|
||||||
else if ((opcode & 0xC0) == 0xC0) { /* set */
|
else if ((opcode & 0xC0) == 0xC0) { /* set */
|
||||||
set_src_value(gb, opcode, value | bit) ;
|
set_src_value(gb, opcode, value | bit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1366,7 +1466,7 @@ static GB_opcode_t *opcodes[256] = {
|
|||||||
jr_cc_r8, add_hl_rr, ld_a_dhli, dec_rr, inc_lr, dec_lr, ld_lr_d8, cpl,
|
jr_cc_r8, add_hl_rr, ld_a_dhli, dec_rr, inc_lr, dec_lr, ld_lr_d8, cpl,
|
||||||
jr_cc_r8, ld_rr_d16, ld_dhld_a, inc_rr, inc_dhl, dec_dhl, ld_dhl_d8, scf, /* 3X */
|
jr_cc_r8, ld_rr_d16, ld_dhld_a, inc_rr, inc_dhl, dec_dhl, ld_dhl_d8, scf, /* 3X */
|
||||||
jr_cc_r8, add_hl_rr, ld_a_dhld, dec_rr, inc_hr, dec_hr, ld_hr_d8, ccf,
|
jr_cc_r8, add_hl_rr, ld_a_dhld, dec_rr, inc_hr, dec_hr, ld_hr_d8, ccf,
|
||||||
nop, ld_b_c, ld_b_d, ld_b_e, ld_b_h, ld_b_l, ld_b_dhl, ld_b_a, /* 4X */
|
ld_b_b, ld_b_c, ld_b_d, ld_b_e, ld_b_h, ld_b_l, ld_b_dhl, ld_b_a, /* 4X */
|
||||||
ld_c_b, nop, ld_c_d, ld_c_e, ld_c_h, ld_c_l, ld_c_dhl, ld_c_a,
|
ld_c_b, nop, ld_c_d, ld_c_e, ld_c_h, ld_c_l, ld_c_dhl, ld_c_a,
|
||||||
ld_d_b, ld_d_c, nop, ld_d_e, ld_d_h, ld_d_l, ld_d_dhl, ld_d_a, /* 5X */
|
ld_d_b, ld_d_c, nop, ld_d_e, ld_d_h, ld_d_l, ld_d_dhl, ld_d_a, /* 5X */
|
||||||
ld_e_b, ld_e_c, ld_e_d, nop, ld_e_h, ld_e_l, ld_e_dhl, ld_e_a,
|
ld_e_b, ld_e_c, ld_e_d, nop, ld_e_h, ld_e_l, ld_e_dhl, ld_e_a,
|
||||||
@ -1401,11 +1501,7 @@ void GB_cpu_run(GB_gameboy_t *gb)
|
|||||||
GB_timing_sync(gb);
|
GB_timing_sync(gb);
|
||||||
GB_advance_cycles(gb, 4);
|
GB_advance_cycles(gb, 4);
|
||||||
if ((gb->io_registers[GB_IO_JOYP] & 0xF) != 0xF) {
|
if ((gb->io_registers[GB_IO_JOYP] & 0xF) != 0xF) {
|
||||||
gb->stopped = false;
|
leave_stop_mode(gb);
|
||||||
/* The CPU takes more time to wake up then the other components */
|
|
||||||
for (unsigned i = 0x800; i--;) {
|
|
||||||
GB_advance_cycles(gb, 0x40);
|
|
||||||
}
|
|
||||||
GB_advance_cycles(gb, 8);
|
GB_advance_cycles(gb, 8);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -1426,19 +1522,19 @@ void GB_cpu_run(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
gb->just_halted = false;
|
gb->just_halted = false;
|
||||||
|
|
||||||
bool effecitve_ime = gb->ime;
|
bool effective_ime = gb->ime;
|
||||||
if (gb->ime_toggle) {
|
if (gb->ime_toggle) {
|
||||||
gb->ime = !gb->ime;
|
gb->ime = !gb->ime;
|
||||||
gb->ime_toggle = false;
|
gb->ime_toggle = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wake up from HALT mode without calling interrupt code. */
|
/* Wake up from HALT mode without calling interrupt code. */
|
||||||
if (gb->halted && !effecitve_ime && interrupt_queue) {
|
if (gb->halted && !effective_ime && interrupt_queue) {
|
||||||
gb->halted = false;
|
gb->halted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call interrupt */
|
/* Call interrupt */
|
||||||
else if (effecitve_ime && interrupt_queue) {
|
else if (effective_ime && interrupt_queue) {
|
||||||
gb->halted = false;
|
gb->halted = false;
|
||||||
uint16_t call_addr = gb->pc;
|
uint16_t call_addr = gb->pc;
|
||||||
|
|
||||||
@ -1475,7 +1571,7 @@ void GB_cpu_run(GB_gameboy_t *gb)
|
|||||||
GB_debugger_call_hook(gb, call_addr);
|
GB_debugger_call_hook(gb, call_addr);
|
||||||
}
|
}
|
||||||
/* Run mode */
|
/* Run mode */
|
||||||
else if(!gb->halted) {
|
else if (!gb->halted) {
|
||||||
gb->last_opcode_read = cycle_read_inc_oam_bug(gb, gb->pc++);
|
gb->last_opcode_read = cycle_read_inc_oam_bug(gb, gb->pc++);
|
||||||
if (gb->halt_bug) {
|
if (gb->halt_bug) {
|
||||||
gb->pc--;
|
gb->pc--;
|
||||||
@ -1484,10 +1580,11 @@ void GB_cpu_run(GB_gameboy_t *gb)
|
|||||||
opcodes[gb->last_opcode_read](gb, gb->last_opcode_read);
|
opcodes[gb->last_opcode_read](gb, gb->last_opcode_read);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flush_pending_cycles(gb);
|
||||||
|
|
||||||
if (gb->hdma_starting) {
|
if (gb->hdma_starting) {
|
||||||
gb->hdma_starting = false;
|
gb->hdma_starting = false;
|
||||||
gb->hdma_on = true;
|
gb->hdma_on = true;
|
||||||
gb->hdma_cycles = -8;
|
gb->hdma_cycles = -8;
|
||||||
}
|
}
|
||||||
flush_pending_cycles(gb);
|
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,8 @@ static void rla(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
|||||||
GB_log(gb, "RLA\n");
|
GB_log(gb, "RLA\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ld_da16_sp(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc){
|
static void ld_da16_sp(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
|
{
|
||||||
uint16_t addr;
|
uint16_t addr;
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
addr = GB_read_memory(gb, (*pc)++);
|
addr = GB_read_memory(gb, (*pc)++);
|
||||||
|
@ -28,8 +28,6 @@ GB_bank_symbol_t *GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const c
|
|||||||
{
|
{
|
||||||
size_t index = GB_map_find_symbol_index(map, addr);
|
size_t index = GB_map_find_symbol_index(map, addr);
|
||||||
|
|
||||||
if (index < map->n_symbols && map->symbols[index].addr == addr) return NULL;
|
|
||||||
|
|
||||||
map->symbols = realloc(map->symbols, (map->n_symbols + 1) * sizeof(map->symbols[0]));
|
map->symbols = realloc(map->symbols, (map->n_symbols + 1) * sizeof(map->symbols[0]));
|
||||||
memmove(&map->symbols[index + 1], &map->symbols[index], (map->n_symbols - index) * sizeof(map->symbols[0]));
|
memmove(&map->symbols[index + 1], &map->symbols[index], (map->n_symbols - index) * sizeof(map->symbols[0]));
|
||||||
map->symbols[index].addr = addr;
|
map->symbols[index].addr = addr;
|
||||||
@ -71,9 +69,9 @@ void GB_map_free(GB_symbol_map_t *map)
|
|||||||
free(map);
|
free(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hash_name(const char *name)
|
static unsigned hash_name(const char *name)
|
||||||
{
|
{
|
||||||
int r = 0;
|
unsigned r = 0;
|
||||||
while (*name) {
|
while (*name) {
|
||||||
r <<= 1;
|
r <<= 1;
|
||||||
if (r & 0x400) {
|
if (r & 0x400) {
|
||||||
@ -87,7 +85,7 @@ static int hash_name(const char *name)
|
|||||||
|
|
||||||
void GB_reversed_map_add_symbol(GB_reversed_symbol_map_t *map, uint16_t bank, GB_bank_symbol_t *bank_symbol)
|
void GB_reversed_map_add_symbol(GB_reversed_symbol_map_t *map, uint16_t bank, GB_bank_symbol_t *bank_symbol)
|
||||||
{
|
{
|
||||||
int hash = hash_name(bank_symbol->name);
|
unsigned hash = hash_name(bank_symbol->name);
|
||||||
GB_symbol_t *symbol = malloc(sizeof(*symbol));
|
GB_symbol_t *symbol = malloc(sizeof(*symbol));
|
||||||
symbol->name = bank_symbol->name;
|
symbol->name = bank_symbol->name;
|
||||||
symbol->addr = bank_symbol->addr;
|
symbol->addr = bank_symbol->addr;
|
||||||
@ -98,7 +96,7 @@ void GB_reversed_map_add_symbol(GB_reversed_symbol_map_t *map, uint16_t bank, GB
|
|||||||
|
|
||||||
const GB_symbol_t *GB_reversed_map_find_symbol(GB_reversed_symbol_map_t *map, const char *name)
|
const GB_symbol_t *GB_reversed_map_find_symbol(GB_reversed_symbol_map_t *map, const char *name)
|
||||||
{
|
{
|
||||||
int hash = hash_name(name);
|
unsigned hash = hash_name(name);
|
||||||
GB_symbol_t *symbol = map->buckets[hash];
|
GB_symbol_t *symbol = map->buckets[hash];
|
||||||
|
|
||||||
while (symbol) {
|
while (symbol) {
|
||||||
|
@ -3,14 +3,14 @@
|
|||||||
#ifndef _WIN32_WINNT
|
#ifndef _WIN32_WINNT
|
||||||
#define _WIN32_WINNT 0x0500
|
#define _WIN32_WINNT 0x0500
|
||||||
#endif
|
#endif
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
#else
|
#else
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const unsigned GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128};
|
static const unsigned GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128};
|
||||||
|
|
||||||
#ifndef DISABLE_TIMEKEEPING
|
#ifndef GB_DISABLE_TIMEKEEPING
|
||||||
static int64_t get_nanoseconds(void)
|
static int64_t get_nanoseconds(void)
|
||||||
{
|
{
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
@ -139,6 +139,11 @@ static void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value)
|
|||||||
|
|
||||||
static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles)
|
static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||||
{
|
{
|
||||||
|
if (gb->stopped) {
|
||||||
|
gb->apu.apu_cycles += 4 << !gb->cgb_double_speed;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
GB_STATE_MACHINE(gb, div, cycles, 1) {
|
GB_STATE_MACHINE(gb, div, cycles, 1) {
|
||||||
GB_STATE(gb, div, 1);
|
GB_STATE(gb, div, 1);
|
||||||
GB_STATE(gb, div, 2);
|
GB_STATE(gb, div, 2);
|
||||||
@ -209,12 +214,13 @@ static void advance_serial(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
|
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
|
||||||
{
|
{
|
||||||
|
gb->apu.pcm_mask[0] = gb->apu.pcm_mask[1] = 0xFF; // Sort of hacky, but too many cross-component interactions to do it right
|
||||||
// Affected by speed boost
|
// Affected by speed boost
|
||||||
gb->dma_cycles += cycles;
|
gb->dma_cycles += cycles;
|
||||||
|
|
||||||
|
GB_timers_run(gb, cycles);
|
||||||
if (!gb->stopped) {
|
if (!gb->stopped) {
|
||||||
GB_timers_run(gb, cycles);
|
|
||||||
advance_serial(gb, cycles); // TODO: Verify what happens in STOP mode
|
advance_serial(gb, cycles); // TODO: Verify what happens in STOP mode
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,6 +238,14 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
gb->cycles_since_input_ir_change += cycles;
|
gb->cycles_since_input_ir_change += cycles;
|
||||||
gb->cycles_since_last_sync += cycles;
|
gb->cycles_since_last_sync += cycles;
|
||||||
gb->cycles_since_run += cycles;
|
gb->cycles_since_run += cycles;
|
||||||
|
|
||||||
|
if (gb->rumble_state) {
|
||||||
|
gb->rumble_on_cycles++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->rumble_off_cycles++;
|
||||||
|
}
|
||||||
|
|
||||||
if (!gb->stopped) { // TODO: Verify what happens in STOP mode
|
if (!gb->stopped) { // TODO: Verify what happens in STOP mode
|
||||||
GB_dma_run(gb);
|
GB_dma_run(gb);
|
||||||
GB_hdma_run(gb);
|
GB_hdma_run(gb);
|
||||||
@ -265,23 +279,30 @@ void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac)
|
|||||||
|
|
||||||
void GB_rtc_run(GB_gameboy_t *gb)
|
void GB_rtc_run(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
|
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||||
|
time_t current_time = time(NULL);
|
||||||
|
while (gb->last_rtc_second / 60 < current_time / 60) {
|
||||||
|
gb->last_rtc_second += 60;
|
||||||
|
gb->huc3_minutes++;
|
||||||
|
if (gb->huc3_minutes == 60 * 24) {
|
||||||
|
gb->huc3_days++;
|
||||||
|
gb->huc3_minutes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ((gb->rtc_real.high & 0x40) == 0) { /* is timer running? */
|
if ((gb->rtc_real.high & 0x40) == 0) { /* is timer running? */
|
||||||
time_t current_time = time(NULL);
|
time_t current_time = time(NULL);
|
||||||
while (gb->last_rtc_second < current_time) {
|
while (gb->last_rtc_second < current_time) {
|
||||||
gb->last_rtc_second++;
|
gb->last_rtc_second++;
|
||||||
if (++gb->rtc_real.seconds == 60)
|
if (++gb->rtc_real.seconds == 60) {
|
||||||
{
|
|
||||||
gb->rtc_real.seconds = 0;
|
gb->rtc_real.seconds = 0;
|
||||||
if (++gb->rtc_real.minutes == 60)
|
if (++gb->rtc_real.minutes == 60) {
|
||||||
{
|
|
||||||
gb->rtc_real.minutes = 0;
|
gb->rtc_real.minutes = 0;
|
||||||
if (++gb->rtc_real.hours == 24)
|
if (++gb->rtc_real.hours == 24) {
|
||||||
{
|
|
||||||
gb->rtc_real.hours = 0;
|
gb->rtc_real.hours = 0;
|
||||||
if (++gb->rtc_real.days == 0)
|
if (++gb->rtc_real.days == 0) {
|
||||||
{
|
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
|
||||||
if (gb->rtc_real.high & 1) /* Bit 8 of days*/
|
|
||||||
{
|
|
||||||
gb->rtc_real.high |= 0x80; /* Overflow bit */
|
gb->rtc_real.high |= 0x80; /* Overflow bit */
|
||||||
}
|
}
|
||||||
gb->rtc_real.high ^= 1;
|
gb->rtc_real.high ^= 1;
|
||||||
|
@ -15,7 +15,6 @@ enum {
|
|||||||
GB_TIMA_RELOADED = 2
|
GB_TIMA_RELOADED = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GB_HALT_VALUE (0xFFFF)
|
|
||||||
|
|
||||||
#define GB_SLEEP(gb, unit, state, cycles) do {\
|
#define GB_SLEEP(gb, unit, state, cycles) do {\
|
||||||
(gb)->unit##_cycles -= (cycles) * __state_machine_divisor; \
|
(gb)->unit##_cycles -= (cycles) * __state_machine_divisor; \
|
||||||
@ -26,12 +25,10 @@ enum {
|
|||||||
}\
|
}\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define GB_HALT(gb, unit) (gb)->unit##_cycles = GB_HALT_VALUE
|
|
||||||
|
|
||||||
#define GB_STATE_MACHINE(gb, unit, cycles, divisor) \
|
#define GB_STATE_MACHINE(gb, unit, cycles, divisor) \
|
||||||
static const int __state_machine_divisor = divisor;\
|
static const int __state_machine_divisor = divisor;\
|
||||||
(gb)->unit##_cycles += cycles; \
|
(gb)->unit##_cycles += cycles; \
|
||||||
if ((gb)->unit##_cycles <= 0 || (gb)->unit##_cycles == GB_HALT_VALUE) {\
|
if ((gb)->unit##_cycles <= 0) {\
|
||||||
return;\
|
return;\
|
||||||
}\
|
}\
|
||||||
switch ((gb)->unit##_state)
|
switch ((gb)->unit##_state)
|
||||||
|
2
HexFiend/HFRepresenterTextViewCallout.m
vendored
2
HexFiend/HFRepresenterTextViewCallout.m
vendored
@ -432,7 +432,7 @@ static double distanceMod1(double a, double b) {
|
|||||||
// Compute the vertical offset
|
// Compute the vertical offset
|
||||||
CGFloat textYOffset = (glyphCount == 1 ? 4 : 5);
|
CGFloat textYOffset = (glyphCount == 1 ? 4 : 5);
|
||||||
// LOL
|
// LOL
|
||||||
if ([_label isEqualToString:@"6"] || [_label isEqualToString:@"7"] == 7) textYOffset -= 1;
|
if ([_label isEqualToString:@"6"]) textYOffset -= 1;
|
||||||
|
|
||||||
|
|
||||||
// Apply this text matrix
|
// Apply this text matrix
|
||||||
|
477
JoyKit/ControllerConfiguration.inc
Normal file
477
JoyKit/ControllerConfiguration.inc
Normal file
@ -0,0 +1,477 @@
|
|||||||
|
#define BUTTON(x) @(JOYButtonUsageGeneric0 + (x))
|
||||||
|
#define AXIS(x) @(JOYAxisUsageGeneric0 + (x))
|
||||||
|
#define AXES2D(x) @(JOYAxes2DUsageGeneric0 + (x))
|
||||||
|
|
||||||
|
hacksByManufacturer = @{
|
||||||
|
@(0x045E): @{ // Microsoft
|
||||||
|
/* Generally untested, but Microsoft goes by the book when it comes to HID report descriptors, so
|
||||||
|
it should work out of the box. The hack is only here for automatic mapping */
|
||||||
|
|
||||||
|
JOYAxisGroups: @{
|
||||||
|
@(kHIDUsage_GD_X): @(0),
|
||||||
|
@(kHIDUsage_GD_Y): @(0),
|
||||||
|
@(kHIDUsage_GD_Z): @(2),
|
||||||
|
@(kHIDUsage_GD_Rx): @(1),
|
||||||
|
@(kHIDUsage_GD_Ry): @(1),
|
||||||
|
@(kHIDUsage_GD_Rz): @(3),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYButtonUsageMapping: @{
|
||||||
|
BUTTON(1): @(JOYButtonUsageA),
|
||||||
|
BUTTON(2): @(JOYButtonUsageB),
|
||||||
|
BUTTON(3): @(JOYButtonUsageX),
|
||||||
|
BUTTON(4): @(JOYButtonUsageY),
|
||||||
|
BUTTON(5): @(JOYButtonUsageL1),
|
||||||
|
BUTTON(6): @(JOYButtonUsageR1),
|
||||||
|
BUTTON(7): @(JOYButtonUsageLStick),
|
||||||
|
BUTTON(8): @(JOYButtonUsageRStick),
|
||||||
|
BUTTON(9): @(JOYButtonUsageStart),
|
||||||
|
BUTTON(10): @(JOYButtonUsageSelect),
|
||||||
|
BUTTON(11): @(JOYButtonUsageHome),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxisUsageMapping: @{
|
||||||
|
AXIS(3): @(JOYAxisUsageL1),
|
||||||
|
AXIS(6): @(JOYAxisUsageR1),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxes2DUsageMapping: @{
|
||||||
|
AXES2D(1): @(JOYAxes2DUsageLeftStick),
|
||||||
|
AXES2D(4): @(JOYAxes2DUsageRightStick),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
@(0x054C): @{ // Sony
|
||||||
|
/* Generally untested, but should work */
|
||||||
|
|
||||||
|
JOYAxisGroups: @{
|
||||||
|
@(kHIDUsage_GD_X): @(0),
|
||||||
|
@(kHIDUsage_GD_Y): @(0),
|
||||||
|
@(kHIDUsage_GD_Z): @(1),
|
||||||
|
@(kHIDUsage_GD_Rx): @(2),
|
||||||
|
@(kHIDUsage_GD_Ry): @(3),
|
||||||
|
@(kHIDUsage_GD_Rz): @(1),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYButtonUsageMapping: @{
|
||||||
|
BUTTON(1): @(JOYButtonUsageY),
|
||||||
|
BUTTON(2): @(JOYButtonUsageB),
|
||||||
|
BUTTON(3): @(JOYButtonUsageA),
|
||||||
|
BUTTON(4): @(JOYButtonUsageX),
|
||||||
|
BUTTON(5): @(JOYButtonUsageL1),
|
||||||
|
BUTTON(6): @(JOYButtonUsageR1),
|
||||||
|
BUTTON(7): @(JOYButtonUsageL2),
|
||||||
|
BUTTON(8): @(JOYButtonUsageR2),
|
||||||
|
BUTTON(9): @(JOYButtonUsageSelect),
|
||||||
|
BUTTON(10): @(JOYButtonUsageStart),
|
||||||
|
BUTTON(11): @(JOYButtonUsageLStick),
|
||||||
|
BUTTON(12): @(JOYButtonUsageRStick),
|
||||||
|
BUTTON(13): @(JOYButtonUsageHome),
|
||||||
|
BUTTON(14): @(JOYButtonUsageMisc),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxisUsageMapping: @{
|
||||||
|
AXIS(4): @(JOYAxisUsageL1),
|
||||||
|
AXIS(5): @(JOYAxisUsageR1),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxes2DUsageMapping: @{
|
||||||
|
AXES2D(1): @(JOYAxes2DUsageLeftStick),
|
||||||
|
AXES2D(4): @(JOYAxes2DUsageRightStick),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
hacksByName = @{
|
||||||
|
@"WUP-028": @{ // Nintendo GameCube Controller Adapter
|
||||||
|
JOYReportIDFilters: @[@[@1], @[@2], @[@3], @[@4]],
|
||||||
|
JOYButtonUsageMapping: @{
|
||||||
|
BUTTON(1): @(JOYButtonUsageA),
|
||||||
|
BUTTON(2): @(JOYButtonUsageB),
|
||||||
|
BUTTON(3): @(JOYButtonUsageX),
|
||||||
|
BUTTON(4): @(JOYButtonUsageY),
|
||||||
|
BUTTON(5): @(JOYButtonUsageStart),
|
||||||
|
BUTTON(6): @(JOYButtonUsageZ),
|
||||||
|
BUTTON(7): @(JOYButtonUsageR1),
|
||||||
|
BUTTON(8): @(JOYButtonUsageL1),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxisUsageMapping: @{
|
||||||
|
AXIS(3): @(JOYAxisUsageL1),
|
||||||
|
AXIS(6): @(JOYAxisUsageR1),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxes2DUsageMapping: @{
|
||||||
|
AXES2D(1): @(JOYAxes2DUsageLeftStick),
|
||||||
|
AXES2D(4): @(JOYAxes2DUsageRightStick),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxisGroups: @{
|
||||||
|
@(kHIDUsage_GD_X): @(0),
|
||||||
|
@(kHIDUsage_GD_Y): @(0),
|
||||||
|
@(kHIDUsage_GD_Z): @(2),
|
||||||
|
@(kHIDUsage_GD_Rx): @(1),
|
||||||
|
@(kHIDUsage_GD_Ry): @(1),
|
||||||
|
@(kHIDUsage_GD_Rz): @(3),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYRumbleUsage: @1,
|
||||||
|
JOYRumbleUsagePage: @0xFF00,
|
||||||
|
|
||||||
|
JOYConnectedUsage: @2,
|
||||||
|
JOYConnectedUsagePage: @0xFF00,
|
||||||
|
|
||||||
|
JOYActivationReport: [NSData dataWithBytes:(uint8_t[]){0x13} length:1],
|
||||||
|
|
||||||
|
JOYCustomReports: @{
|
||||||
|
|
||||||
|
// Rumble
|
||||||
|
@(-17): @[
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@0, @"usagePage":@(0xFF00), @"usage":@1, @"min": @0, @"max": @1},
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@8, @"usagePage":@(0xFF00), @"usage":@1, @"min": @0, @"max": @1},
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@16, @"usagePage":@(0xFF00), @"usage":@1, @"min": @0, @"max": @1},
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@24, @"usagePage":@(0xFF00), @"usage":@1, @"min": @0, @"max": @1},
|
||||||
|
],
|
||||||
|
|
||||||
|
@(33): @[
|
||||||
|
|
||||||
|
// Player 1
|
||||||
|
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@4, @"usagePage":@(0xFF00), @"usage":@2, @"min": @0, @"max": @1},
|
||||||
|
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@8, @"usagePage":@(kHIDPage_Button), @"usage":@1},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@9, @"usagePage":@(kHIDPage_Button), @"usage":@2},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@10,@"usagePage":@(kHIDPage_Button), @"usage":@3},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@11,@"usagePage":@(kHIDPage_Button), @"usage":@4},
|
||||||
|
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@12, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadLeft)},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@13, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadRight)},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@14, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadDown)},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@15, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadUp)},
|
||||||
|
|
||||||
|
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@16, @"usagePage":@(kHIDPage_Button), @"usage":@5},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@17, @"usagePage":@(kHIDPage_Button), @"usage":@6},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@18, @"usagePage":@(kHIDPage_Button), @"usage":@7},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@19, @"usagePage":@(kHIDPage_Button), @"usage":@8},
|
||||||
|
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@24, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_X), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@32, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Y), @"min": @255, @"max": @0},
|
||||||
|
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@40, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Rx), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@48, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Ry), @"min": @255, @"max": @0},
|
||||||
|
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@56, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Z), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@64, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Rz), @"min": @0, @"max": @255},
|
||||||
|
|
||||||
|
// Player 2
|
||||||
|
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(4 + 72), @"usagePage":@(0xFF00), @"usage":@2, @"min": @0, @"max": @1},
|
||||||
|
|
||||||
|
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(8 + 72), @"usagePage":@(kHIDPage_Button), @"usage":@1},
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(9 + 72), @"usagePage":@(kHIDPage_Button), @"usage":@2},
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(10 + 72), @"usagePage":@(kHIDPage_Button), @"usage":@3},
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(11 + 72), @"usagePage":@(kHIDPage_Button), @"usage":@4},
|
||||||
|
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(12 + 72), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadLeft)},
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(13 + 72), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadRight)},
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(14 + 72), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadDown)},
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(15 + 72), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadUp)},
|
||||||
|
|
||||||
|
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(16 + 72), @"usagePage":@(kHIDPage_Button), @"usage":@5},
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(17 + 72), @"usagePage":@(kHIDPage_Button), @"usage":@6},
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(18 + 72), @"usagePage":@(kHIDPage_Button), @"usage":@7},
|
||||||
|
@{@"reportID": @(2), @"size":@1, @"offset":@(19 + 72), @"usagePage":@(kHIDPage_Button), @"usage":@8},
|
||||||
|
|
||||||
|
@{@"reportID": @(2), @"size":@8, @"offset":@(24 + 72), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_X), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(2), @"size":@8, @"offset":@(32 + 72), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Y), @"min": @255, @"max": @0},
|
||||||
|
|
||||||
|
@{@"reportID": @(2), @"size":@8, @"offset":@(40 + 72), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Rx), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(2), @"size":@8, @"offset":@(48 + 72), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Ry), @"min": @255, @"max": @0},
|
||||||
|
|
||||||
|
@{@"reportID": @(2), @"size":@8, @"offset":@(56 + 72), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Z), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(2), @"size":@8, @"offset":@(64 + 72), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Rz), @"min": @0, @"max": @255},
|
||||||
|
|
||||||
|
// Player 3
|
||||||
|
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(4 + 144), @"usagePage":@(0xFF00), @"usage":@2, @"min": @0, @"max": @1},
|
||||||
|
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(8 + 144), @"usagePage":@(kHIDPage_Button), @"usage":@1},
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(9 + 144), @"usagePage":@(kHIDPage_Button), @"usage":@2},
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(10 + 144), @"usagePage":@(kHIDPage_Button), @"usage":@3},
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(11 + 144), @"usagePage":@(kHIDPage_Button), @"usage":@4},
|
||||||
|
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(12 + 144), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadLeft)},
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(13 + 144), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadRight)},
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(14 + 144), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadDown)},
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(15 + 144), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadUp)},
|
||||||
|
|
||||||
|
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(16 + 144), @"usagePage":@(kHIDPage_Button), @"usage":@5},
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(17 + 144), @"usagePage":@(kHIDPage_Button), @"usage":@6},
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(18 + 144), @"usagePage":@(kHIDPage_Button), @"usage":@7},
|
||||||
|
@{@"reportID": @(3), @"size":@1, @"offset":@(19 + 144), @"usagePage":@(kHIDPage_Button), @"usage":@8},
|
||||||
|
|
||||||
|
@{@"reportID": @(3), @"size":@8, @"offset":@(24 + 144), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_X), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(3), @"size":@8, @"offset":@(32 + 144), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Y), @"min": @255, @"max": @0},
|
||||||
|
|
||||||
|
@{@"reportID": @(3), @"size":@8, @"offset":@(40 + 144), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Rx), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(3), @"size":@8, @"offset":@(48 + 144), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Ry), @"min": @255, @"max": @0},
|
||||||
|
|
||||||
|
@{@"reportID": @(3), @"size":@8, @"offset":@(56 + 144), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Z), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(3), @"size":@8, @"offset":@(64 + 144), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Rz), @"min": @0, @"max": @255},
|
||||||
|
|
||||||
|
// Player 4
|
||||||
|
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(4 + 216), @"usagePage":@(0xFF00), @"usage":@2, @"min": @0, @"max": @1},
|
||||||
|
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(8 + 216), @"usagePage":@(kHIDPage_Button), @"usage":@1},
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(9 + 216), @"usagePage":@(kHIDPage_Button), @"usage":@2},
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(10 + 216), @"usagePage":@(kHIDPage_Button), @"usage":@3},
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(11 + 216), @"usagePage":@(kHIDPage_Button), @"usage":@4},
|
||||||
|
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(12 + 216), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadLeft)},
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(13 + 216), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadRight)},
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(14 + 216), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadDown)},
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(15 + 216), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadUp)},
|
||||||
|
|
||||||
|
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(16 + 216), @"usagePage":@(kHIDPage_Button), @"usage":@5},
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(17 + 216), @"usagePage":@(kHIDPage_Button), @"usage":@6},
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(18 + 216), @"usagePage":@(kHIDPage_Button), @"usage":@7},
|
||||||
|
@{@"reportID": @(4), @"size":@1, @"offset":@(19 + 216), @"usagePage":@(kHIDPage_Button), @"usage":@8},
|
||||||
|
|
||||||
|
@{@"reportID": @(4), @"size":@8, @"offset":@(24 + 216), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_X), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(4), @"size":@8, @"offset":@(32 + 216), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Y), @"min": @255, @"max": @0},
|
||||||
|
|
||||||
|
@{@"reportID": @(4), @"size":@8, @"offset":@(40 + 216), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Rx), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(4), @"size":@8, @"offset":@(48 + 216), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Ry), @"min": @255, @"max": @0},
|
||||||
|
|
||||||
|
@{@"reportID": @(4), @"size":@8, @"offset":@(56 + 216), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Z), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(4), @"size":@8, @"offset":@(64 + 216), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Rz), @"min": @0, @"max": @255},
|
||||||
|
|
||||||
|
|
||||||
|
]},
|
||||||
|
},
|
||||||
|
|
||||||
|
@"GameCube Controller Adapter": @{ // GameCube Controller PC Adapter
|
||||||
|
JOYAxisGroups: @{
|
||||||
|
@(kHIDUsage_GD_X): @(0),
|
||||||
|
@(kHIDUsage_GD_Y): @(0),
|
||||||
|
@(kHIDUsage_GD_Z): @(1),
|
||||||
|
@(kHIDUsage_GD_Rx): @(2),
|
||||||
|
@(kHIDUsage_GD_Ry): @(3),
|
||||||
|
@(kHIDUsage_GD_Rz): @(1),
|
||||||
|
},
|
||||||
|
JOYReportIDFilters: @[@[@1], @[@2], @[@3], @[@4]],
|
||||||
|
JOYButtonUsageMapping: @{
|
||||||
|
BUTTON(1): @(JOYButtonUsageX),
|
||||||
|
BUTTON(2): @(JOYButtonUsageA),
|
||||||
|
BUTTON(3): @(JOYButtonUsageB),
|
||||||
|
BUTTON(4): @(JOYButtonUsageY),
|
||||||
|
BUTTON(5): @(JOYButtonUsageL1),
|
||||||
|
BUTTON(6): @(JOYButtonUsageR1),
|
||||||
|
BUTTON(8): @(JOYButtonUsageZ),
|
||||||
|
BUTTON(10): @(JOYButtonUsageStart),
|
||||||
|
BUTTON(13): @(JOYButtonUsageDPadUp),
|
||||||
|
BUTTON(14): @(JOYButtonUsageDPadRight),
|
||||||
|
BUTTON(15): @(JOYButtonUsageDPadDown),
|
||||||
|
BUTTON(16): @(JOYButtonUsageDPadLeft),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxisUsageMapping: @{
|
||||||
|
AXIS(4): @(JOYAxisUsageL1),
|
||||||
|
AXIS(5): @(JOYAxisUsageR1),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxes2DUsageMapping: @{
|
||||||
|
AXES2D(1): @(JOYAxes2DUsageLeftStick),
|
||||||
|
AXES2D(3): @(JOYAxes2DUsageRightStick),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYRumbleUsage: @1,
|
||||||
|
JOYRumbleUsagePage: @0xFF00,
|
||||||
|
JOYRumbleMin: @0,
|
||||||
|
JOYRumbleMax: @255,
|
||||||
|
JOYSwapZRz: @YES,
|
||||||
|
},
|
||||||
|
|
||||||
|
@"Twin USB Joystick": @{ // DualShock PC Adapter
|
||||||
|
JOYAxisGroups: @{
|
||||||
|
@(kHIDUsage_GD_X): @(0),
|
||||||
|
@(kHIDUsage_GD_Y): @(0),
|
||||||
|
@(kHIDUsage_GD_Z): @(1),
|
||||||
|
@(kHIDUsage_GD_Rx): @(2),
|
||||||
|
@(kHIDUsage_GD_Ry): @(2),
|
||||||
|
@(kHIDUsage_GD_Rz): @(1),
|
||||||
|
},
|
||||||
|
JOYReportIDFilters: @[@[@1], @[@2]],
|
||||||
|
JOYButtonUsageMapping: @{
|
||||||
|
BUTTON(1): @(JOYButtonUsageX),
|
||||||
|
BUTTON(2): @(JOYButtonUsageA),
|
||||||
|
BUTTON(3): @(JOYButtonUsageB),
|
||||||
|
BUTTON(4): @(JOYButtonUsageY),
|
||||||
|
BUTTON(5): @(JOYButtonUsageL2),
|
||||||
|
BUTTON(6): @(JOYButtonUsageR2),
|
||||||
|
BUTTON(7): @(JOYButtonUsageL1),
|
||||||
|
BUTTON(8): @(JOYButtonUsageR1),
|
||||||
|
BUTTON(9): @(JOYButtonUsageSelect),
|
||||||
|
BUTTON(10): @(JOYButtonUsageStart),
|
||||||
|
BUTTON(11): @(JOYButtonUsageLStick),
|
||||||
|
BUTTON(12): @(JOYButtonUsageRStick),
|
||||||
|
BUTTON(13): @(JOYButtonUsageDPadUp),
|
||||||
|
BUTTON(14): @(JOYButtonUsageDPadRight),
|
||||||
|
BUTTON(15): @(JOYButtonUsageDPadDown),
|
||||||
|
BUTTON(16): @(JOYButtonUsageDPadLeft),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxes2DUsageMapping: @{
|
||||||
|
AXES2D(1): @(JOYAxes2DUsageLeftStick),
|
||||||
|
AXES2D(6): @(JOYAxes2DUsageRightStick),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYSwapZRz: @YES,
|
||||||
|
},
|
||||||
|
|
||||||
|
@"Pro Controller": @{ // Switch Pro Controller
|
||||||
|
JOYIsSwitch: @YES,
|
||||||
|
JOYAxisGroups: @{
|
||||||
|
@(kHIDUsage_GD_X): @(0),
|
||||||
|
@(kHIDUsage_GD_Y): @(0),
|
||||||
|
@(kHIDUsage_GD_Z): @(0),
|
||||||
|
@(kHIDUsage_GD_Rx): @(1),
|
||||||
|
@(kHIDUsage_GD_Ry): @(1),
|
||||||
|
@(kHIDUsage_GD_Rz): @(1),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYButtonUsageMapping: @{
|
||||||
|
BUTTON(1): @(JOYButtonUsageB),
|
||||||
|
BUTTON(2): @(JOYButtonUsageA),
|
||||||
|
BUTTON(3): @(JOYButtonUsageY),
|
||||||
|
BUTTON(4): @(JOYButtonUsageX),
|
||||||
|
BUTTON(5): @(JOYButtonUsageL1),
|
||||||
|
BUTTON(6): @(JOYButtonUsageR1),
|
||||||
|
BUTTON(7): @(JOYButtonUsageL2),
|
||||||
|
BUTTON(8): @(JOYButtonUsageR2),
|
||||||
|
BUTTON(9): @(JOYButtonUsageSelect),
|
||||||
|
BUTTON(10): @(JOYButtonUsageStart),
|
||||||
|
BUTTON(11): @(JOYButtonUsageLStick),
|
||||||
|
BUTTON(12): @(JOYButtonUsageRStick),
|
||||||
|
BUTTON(13): @(JOYButtonUsageHome),
|
||||||
|
BUTTON(14): @(JOYButtonUsageMisc),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxes2DUsageMapping: @{
|
||||||
|
AXES2D(1): @(JOYAxes2DUsageLeftStick),
|
||||||
|
AXES2D(4): @(JOYAxes2DUsageRightStick),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYCustomReports: @{
|
||||||
|
@(0x30): @[
|
||||||
|
|
||||||
|
// For USB mode, which uses the wrong report descriptor
|
||||||
|
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@16, @"usagePage":@(kHIDPage_Button), @"usage":@3},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@17, @"usagePage":@(kHIDPage_Button), @"usage":@4},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@18, @"usagePage":@(kHIDPage_Button), @"usage":@1},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@19, @"usagePage":@(kHIDPage_Button), @"usage":@2},
|
||||||
|
|
||||||
|
// SR and SL not used on the Pro Controller
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@22, @"usagePage":@(kHIDPage_Button), @"usage":@6},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@23, @"usagePage":@(kHIDPage_Button), @"usage":@8},
|
||||||
|
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@24, @"usagePage":@(kHIDPage_Button), @"usage":@9},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@25, @"usagePage":@(kHIDPage_Button), @"usage":@10},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@26, @"usagePage":@(kHIDPage_Button), @"usage":@12},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@27, @"usagePage":@(kHIDPage_Button), @"usage":@11},
|
||||||
|
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@28, @"usagePage":@(kHIDPage_Button), @"usage":@13},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@29, @"usagePage":@(kHIDPage_Button), @"usage":@14},
|
||||||
|
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@32, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadDown)},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@33, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadUp)},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@34, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadRight)},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@35, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_DPadLeft)},
|
||||||
|
|
||||||
|
// SR and SL not used on the Pro Controller
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@38, @"usagePage":@(kHIDPage_Button), @"usage":@5},
|
||||||
|
@{@"reportID": @(1), @"size":@1, @"offset":@39, @"usagePage":@(kHIDPage_Button), @"usage":@7},
|
||||||
|
|
||||||
|
/* Sticks */
|
||||||
|
@{@"reportID": @(1), @"size":@12, @"offset":@40, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_X), @"min": @0, @"max": @0xFFF},
|
||||||
|
@{@"reportID": @(1), @"size":@12, @"offset":@52, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Y), @"min": @0xFFF, @"max": @0},
|
||||||
|
|
||||||
|
@{@"reportID": @(1), @"size":@12, @"offset":@64, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Rx), @"min": @0, @"max": @0xFFF},
|
||||||
|
@{@"reportID": @(1), @"size":@12, @"offset":@76, @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Ry), @"min": @0xFFF, @"max": @0},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYIgnoredReports: @(0x30), // Ignore the real 0x30 report as it's broken
|
||||||
|
|
||||||
|
@"PLAYSTATION(R)3 Controller": @{ // DualShock 3
|
||||||
|
JOYAxisGroups: @{
|
||||||
|
@(kHIDUsage_GD_X): @(0),
|
||||||
|
@(kHIDUsage_GD_Y): @(0),
|
||||||
|
@(kHIDUsage_GD_Z): @(1),
|
||||||
|
@(kHIDUsage_GD_Rx): @(2),
|
||||||
|
@(kHIDUsage_GD_Ry): @(3),
|
||||||
|
@(kHIDUsage_GD_Rz): @(1),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYButtonUsageMapping: @{
|
||||||
|
BUTTON(1): @(JOYButtonUsageSelect),
|
||||||
|
BUTTON(2): @(JOYButtonUsageL3),
|
||||||
|
BUTTON(3): @(JOYButtonUsageR3),
|
||||||
|
BUTTON(4): @(JOYButtonUsageStart),
|
||||||
|
BUTTON(5): @(JOYButtonUsageDPadUp),
|
||||||
|
BUTTON(6): @(JOYButtonUsageDPadRight),
|
||||||
|
BUTTON(7): @(JOYButtonUsageDPadDown),
|
||||||
|
BUTTON(8): @(JOYButtonUsageDPadLeft),
|
||||||
|
BUTTON(9): @(JOYButtonUsageL2),
|
||||||
|
BUTTON(10): @(JOYButtonUsageR2),
|
||||||
|
BUTTON(11): @(JOYButtonUsageL1),
|
||||||
|
BUTTON(12): @(JOYButtonUsageR1),
|
||||||
|
BUTTON(13): @(JOYButtonUsageX),
|
||||||
|
BUTTON(14): @(JOYButtonUsageA),
|
||||||
|
BUTTON(15): @(JOYButtonUsageB),
|
||||||
|
BUTTON(16): @(JOYButtonUsageY),
|
||||||
|
BUTTON(17): @(JOYButtonUsageHome),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxisUsageMapping: @{
|
||||||
|
AXIS(4): @(JOYAxisUsageL1),
|
||||||
|
AXIS(5): @(JOYAxisUsageR1),
|
||||||
|
AXIS(8): @(JOYAxisUsageL2),
|
||||||
|
AXIS(9): @(JOYAxisUsageR2),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYAxes2DUsageMapping: @{
|
||||||
|
AXES2D(1): @(JOYAxes2DUsageLeftStick),
|
||||||
|
AXES2D(3): @(JOYAxes2DUsageRightStick),
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYCustomReports: @{
|
||||||
|
@(0x01): @[
|
||||||
|
/* Pressure sensitive inputs */
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@(13 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@(14 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@(15 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@(16 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@(17 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Dial), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@(18 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Wheel), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@(19 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Rx), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@(20 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Ry), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@(21 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@(22 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@(23 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255},
|
||||||
|
@{@"reportID": @(1), @"size":@8, @"offset":@(24 * 8), @"usagePage":@(kHIDPage_GenericDesktop), @"usage":@(kHIDUsage_GD_Slider), @"min": @0, @"max": @255},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
JOYIsDualShock3: @YES,
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
24
JoyKit/JOYAxes2D.h
Normal file
24
JoyKit/JOYAxes2D.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
JOYAxes2DUsageNone,
|
||||||
|
JOYAxes2DUsageLeftStick,
|
||||||
|
JOYAxes2DUsageRightStick,
|
||||||
|
JOYAxes2DUsageMiddleStick,
|
||||||
|
JOYAxes2DUsagePointer,
|
||||||
|
JOYAxes2DUsageNonGenericMax,
|
||||||
|
|
||||||
|
JOYAxes2DUsageGeneric0 = 0x10000,
|
||||||
|
} JOYAxes2DUsage;
|
||||||
|
|
||||||
|
@interface JOYAxes2D : NSObject
|
||||||
|
- (NSString *)usageString;
|
||||||
|
+ (NSString *)usageToString: (JOYAxes2DUsage) usage;
|
||||||
|
- (uint64_t)uniqueID;
|
||||||
|
- (double)distance;
|
||||||
|
- (double)angle;
|
||||||
|
- (NSPoint)value;
|
||||||
|
@property JOYAxes2DUsage usage;
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
181
JoyKit/JOYAxes2D.m
Normal file
181
JoyKit/JOYAxes2D.m
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
#import "JOYAxes2D.h"
|
||||||
|
#import "JOYElement.h"
|
||||||
|
|
||||||
|
@implementation JOYAxes2D
|
||||||
|
{
|
||||||
|
JOYElement *_element1, *_element2;
|
||||||
|
double _state1, _state2;
|
||||||
|
int32_t initialX, initialY;
|
||||||
|
int32_t minX, minY;
|
||||||
|
int32_t maxX, maxY;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)usageToString: (JOYAxes2DUsage) usage
|
||||||
|
{
|
||||||
|
if (usage < JOYAxes2DUsageNonGenericMax) {
|
||||||
|
return (NSString *[]) {
|
||||||
|
@"None",
|
||||||
|
@"Left Stick",
|
||||||
|
@"Right Stick",
|
||||||
|
@"Middle Stick",
|
||||||
|
@"Pointer",
|
||||||
|
}[usage];
|
||||||
|
}
|
||||||
|
if (usage >= JOYAxes2DUsageGeneric0) {
|
||||||
|
return [NSString stringWithFormat:@"Generic 2D Analog Control %d", usage - JOYAxes2DUsageGeneric0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [NSString stringWithFormat:@"Unknown Usage 2D Axes %d", usage];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)usageString
|
||||||
|
{
|
||||||
|
return [self.class usageToString:_usage];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (uint64_t)uniqueID
|
||||||
|
{
|
||||||
|
return _element1.uniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)description
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat:@"<%@: %p, %@ (%llu); State: %.2f%%, %.2f degrees>", self.className, self, self.usageString, self.uniqueID, self.distance * 100, self.angle];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithFirstElement:(JOYElement *)element1 secondElement:(JOYElement *)element2
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (!self) return self;
|
||||||
|
|
||||||
|
_element1 = element1;
|
||||||
|
_element2 = element2;
|
||||||
|
|
||||||
|
|
||||||
|
if (element1.usagePage == kHIDPage_GenericDesktop) {
|
||||||
|
uint16_t usage = element1.usage;
|
||||||
|
_usage = JOYAxes2DUsageGeneric0 + usage - kHIDUsage_GD_X + 1;
|
||||||
|
}
|
||||||
|
initialX = 0;
|
||||||
|
initialY = 0;
|
||||||
|
minX = element1.max;
|
||||||
|
minY = element2.max;
|
||||||
|
maxX = element1.min;
|
||||||
|
maxY = element2.min;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSPoint)value
|
||||||
|
{
|
||||||
|
return NSMakePoint(_state1, _state2);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(int32_t) effectiveMinX
|
||||||
|
{
|
||||||
|
int32_t rawMin = _element1.min;
|
||||||
|
int32_t rawMax = _element1.max;
|
||||||
|
if (initialX == 0) return rawMin;
|
||||||
|
if (minX <= (rawMin * 2 + initialX) / 3 && maxX >= (rawMax * 2 + initialX) / 3 ) return minX;
|
||||||
|
if ((initialX - rawMin) < (rawMax - initialX)) return rawMin;
|
||||||
|
return initialX - (rawMax - initialX);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(int32_t) effectiveMinY
|
||||||
|
{
|
||||||
|
int32_t rawMin = _element2.min;
|
||||||
|
int32_t rawMax = _element2.max;
|
||||||
|
if (initialY == 0) return rawMin;
|
||||||
|
if (minX <= (rawMin * 2 + initialY) / 3 && maxY >= (rawMax * 2 + initialY) / 3 ) return minY;
|
||||||
|
if ((initialY - rawMin) < (rawMax - initialY)) return rawMin;
|
||||||
|
return initialY - (rawMax - initialY);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(int32_t) effectiveMaxX
|
||||||
|
{
|
||||||
|
int32_t rawMin = _element1.min;
|
||||||
|
int32_t rawMax = _element1.max;
|
||||||
|
if (initialX == 0) return rawMax;
|
||||||
|
if (minX <= (rawMin * 2 + initialX) / 3 && maxX >= (rawMax * 2 + initialX) / 3 ) return maxX;
|
||||||
|
if ((initialX - rawMin) > (rawMax - initialX)) return rawMax;
|
||||||
|
return initialX + (initialX - rawMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(int32_t) effectiveMaxY
|
||||||
|
{
|
||||||
|
int32_t rawMin = _element2.min;
|
||||||
|
int32_t rawMax = _element2.max;
|
||||||
|
if (initialY == 0) return rawMax;
|
||||||
|
if (minX <= (rawMin * 2 + initialY) / 3 && maxY >= (rawMax * 2 + initialY) / 3 ) return maxY;
|
||||||
|
if ((initialY - rawMin) > (rawMax - initialY)) return rawMax;
|
||||||
|
return initialY + (initialY - rawMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)updateState
|
||||||
|
{
|
||||||
|
int32_t x = [_element1 value];
|
||||||
|
int32_t y = [_element2 value];
|
||||||
|
if (x == 0 && y == 0) return false;
|
||||||
|
|
||||||
|
if (initialX == 0 && initialY == 0) {
|
||||||
|
initialX = x;
|
||||||
|
initialY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
double old1 = _state1, old2 = _state2;
|
||||||
|
{
|
||||||
|
int32_t value = x;
|
||||||
|
|
||||||
|
if (initialX != 0) {
|
||||||
|
minX = MIN(value, minX);
|
||||||
|
maxX = MAX(value, maxX);
|
||||||
|
}
|
||||||
|
|
||||||
|
double min = [self effectiveMinX];
|
||||||
|
double max = [self effectiveMaxX];
|
||||||
|
if (min == max) return false;
|
||||||
|
|
||||||
|
_state1 = (value - min) / (max - min) * 2 - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int32_t value = y;
|
||||||
|
|
||||||
|
if (initialY != 0) {
|
||||||
|
minY = MIN(value, minY);
|
||||||
|
maxY = MAX(value, maxY);
|
||||||
|
}
|
||||||
|
|
||||||
|
double min = [self effectiveMinY];
|
||||||
|
double max = [self effectiveMaxY];
|
||||||
|
if (min == max) return false;
|
||||||
|
|
||||||
|
_state2 = (value - min) / (max - min) * 2 - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_state1 < -1 || _state1 > 1 ||
|
||||||
|
_state2 < -1 || _state2 > 1) {
|
||||||
|
// Makes no sense, recalibrate
|
||||||
|
_state1 = _state2 = 0;
|
||||||
|
initialX = initialY = 0;
|
||||||
|
minX = _element1.max;
|
||||||
|
minY = _element2.max;
|
||||||
|
maxX = _element1.min;
|
||||||
|
maxY = _element2.min;
|
||||||
|
}
|
||||||
|
|
||||||
|
return old1 != _state1 || old2 != _state2;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (double)distance
|
||||||
|
{
|
||||||
|
return MIN(sqrt(_state1 * _state1 + _state2 * _state2), 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (double)angle {
|
||||||
|
double temp = atan2(_state2, _state1) * 180 / M_PI;
|
||||||
|
if (temp >= 0) return temp;
|
||||||
|
return temp + 360;
|
||||||
|
}
|
||||||
|
@end
|
29
JoyKit/JOYAxis.h
Normal file
29
JoyKit/JOYAxis.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
JOYAxisUsageNone,
|
||||||
|
JOYAxisUsageL1,
|
||||||
|
JOYAxisUsageL2,
|
||||||
|
JOYAxisUsageL3,
|
||||||
|
JOYAxisUsageR1,
|
||||||
|
JOYAxisUsageR2,
|
||||||
|
JOYAxisUsageR3,
|
||||||
|
JOYAxisUsageWheel,
|
||||||
|
JOYAxisUsageRudder,
|
||||||
|
JOYAxisUsageThrottle,
|
||||||
|
JOYAxisUsageAccelerator,
|
||||||
|
JOYAxisUsageBrake,
|
||||||
|
JOYAxisUsageNonGenericMax,
|
||||||
|
|
||||||
|
JOYAxisUsageGeneric0 = 0x10000,
|
||||||
|
} JOYAxisUsage;
|
||||||
|
|
||||||
|
@interface JOYAxis : NSObject
|
||||||
|
- (NSString *)usageString;
|
||||||
|
+ (NSString *)usageToString: (JOYAxisUsage) usage;
|
||||||
|
- (uint64_t)uniqueID;
|
||||||
|
- (double)value;
|
||||||
|
@property JOYAxisUsage usage;
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
90
JoyKit/JOYAxis.m
Normal file
90
JoyKit/JOYAxis.m
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#import "JOYAxis.h"
|
||||||
|
#import "JOYElement.h"
|
||||||
|
|
||||||
|
@implementation JOYAxis
|
||||||
|
{
|
||||||
|
JOYElement *_element;
|
||||||
|
double _state;
|
||||||
|
double _min;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)usageToString: (JOYAxisUsage) usage
|
||||||
|
{
|
||||||
|
if (usage < JOYAxisUsageNonGenericMax) {
|
||||||
|
return (NSString *[]) {
|
||||||
|
@"None",
|
||||||
|
@"Analog L1",
|
||||||
|
@"Analog L2",
|
||||||
|
@"Analog L3",
|
||||||
|
@"Analog R1",
|
||||||
|
@"Analog R2",
|
||||||
|
@"Analog R3",
|
||||||
|
@"Wheel",
|
||||||
|
@"Rudder",
|
||||||
|
@"Throttle",
|
||||||
|
@"Accelerator",
|
||||||
|
@"Brake",
|
||||||
|
}[usage];
|
||||||
|
}
|
||||||
|
if (usage >= JOYAxisUsageGeneric0) {
|
||||||
|
return [NSString stringWithFormat:@"Generic Analog Control %d", usage - JOYAxisUsageGeneric0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [NSString stringWithFormat:@"Unknown Usage Axis %d", usage];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)usageString
|
||||||
|
{
|
||||||
|
return [self.class usageToString:_usage];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (uint64_t)uniqueID
|
||||||
|
{
|
||||||
|
return _element.uniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)description
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat:@"<%@: %p, %@ (%llu); State: %f%%>", self.className, self, self.usageString, self.uniqueID, _state * 100];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithElement:(JOYElement *)element
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (!self) return self;
|
||||||
|
|
||||||
|
_element = element;
|
||||||
|
|
||||||
|
|
||||||
|
if (element.usagePage == kHIDPage_GenericDesktop) {
|
||||||
|
uint16_t usage = element.usage;
|
||||||
|
_usage = JOYAxisUsageGeneric0 + usage - kHIDUsage_GD_X + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_min = 1.0;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (double) value
|
||||||
|
{
|
||||||
|
return _state;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)updateState
|
||||||
|
{
|
||||||
|
double min = _element.min;
|
||||||
|
double max = _element.max;
|
||||||
|
if (min == max) return false;
|
||||||
|
double old = _state;
|
||||||
|
double unnormalized = ([_element value] - min) / (max - min);
|
||||||
|
if (unnormalized < _min) {
|
||||||
|
_min = unnormalized;
|
||||||
|
}
|
||||||
|
if (_min != 1) {
|
||||||
|
_state = (unnormalized - _min) / (1 - _min);
|
||||||
|
}
|
||||||
|
return old != _state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
42
JoyKit/JOYButton.h
Normal file
42
JoyKit/JOYButton.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
JOYButtonUsageNone,
|
||||||
|
JOYButtonUsageA,
|
||||||
|
JOYButtonUsageB,
|
||||||
|
JOYButtonUsageC,
|
||||||
|
JOYButtonUsageX,
|
||||||
|
JOYButtonUsageY,
|
||||||
|
JOYButtonUsageZ,
|
||||||
|
JOYButtonUsageStart,
|
||||||
|
JOYButtonUsageSelect,
|
||||||
|
JOYButtonUsageHome,
|
||||||
|
JOYButtonUsageMisc,
|
||||||
|
JOYButtonUsageLStick,
|
||||||
|
JOYButtonUsageRStick,
|
||||||
|
JOYButtonUsageL1,
|
||||||
|
JOYButtonUsageL2,
|
||||||
|
JOYButtonUsageL3,
|
||||||
|
JOYButtonUsageR1,
|
||||||
|
JOYButtonUsageR2,
|
||||||
|
JOYButtonUsageR3,
|
||||||
|
JOYButtonUsageDPadLeft,
|
||||||
|
JOYButtonUsageDPadRight,
|
||||||
|
JOYButtonUsageDPadUp,
|
||||||
|
JOYButtonUsageDPadDown,
|
||||||
|
JOYButtonUsageNonGenericMax,
|
||||||
|
|
||||||
|
JOYButtonUsageGeneric0 = 0x10000,
|
||||||
|
} JOYButtonUsage;
|
||||||
|
|
||||||
|
@interface JOYButton : NSObject
|
||||||
|
- (NSString *)usageString;
|
||||||
|
+ (NSString *)usageToString: (JOYButtonUsage) usage;
|
||||||
|
- (uint64_t)uniqueID;
|
||||||
|
- (bool) isPressed;
|
||||||
|
@property JOYButtonUsage usage;
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
102
JoyKit/JOYButton.m
Normal file
102
JoyKit/JOYButton.m
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#import "JOYButton.h"
|
||||||
|
#import "JOYElement.h"
|
||||||
|
|
||||||
|
@implementation JOYButton
|
||||||
|
{
|
||||||
|
JOYElement *_element;
|
||||||
|
bool _state;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)usageToString: (JOYButtonUsage) usage
|
||||||
|
{
|
||||||
|
if (usage < JOYButtonUsageNonGenericMax) {
|
||||||
|
return (NSString *[]) {
|
||||||
|
@"None",
|
||||||
|
@"A",
|
||||||
|
@"B",
|
||||||
|
@"C",
|
||||||
|
@"X",
|
||||||
|
@"Y",
|
||||||
|
@"Z",
|
||||||
|
@"Start",
|
||||||
|
@"Select",
|
||||||
|
@"Home",
|
||||||
|
@"Misc",
|
||||||
|
@"Left Stick",
|
||||||
|
@"Right Stick",
|
||||||
|
@"L1",
|
||||||
|
@"L2",
|
||||||
|
@"L3",
|
||||||
|
@"R1",
|
||||||
|
@"R2",
|
||||||
|
@"R3",
|
||||||
|
@"D-Pad Left",
|
||||||
|
@"D-Pad Right",
|
||||||
|
@"D-Pad Up",
|
||||||
|
@"D-Pad Down",
|
||||||
|
}[usage];
|
||||||
|
}
|
||||||
|
if (usage >= JOYButtonUsageGeneric0) {
|
||||||
|
return [NSString stringWithFormat:@"Generic Button %d", usage - JOYButtonUsageGeneric0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [NSString stringWithFormat:@"Unknown Usage Button %d", usage];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)usageString
|
||||||
|
{
|
||||||
|
return [self.class usageToString:_usage];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (uint64_t)uniqueID
|
||||||
|
{
|
||||||
|
return _element.uniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)description
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat:@"<%@: %p, %@ (%llu); State: %s>", self.className, self, self.usageString, self.uniqueID, _state? "Presssed" : "Released"];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithElement:(JOYElement *)element
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (!self) return self;
|
||||||
|
|
||||||
|
_element = element;
|
||||||
|
|
||||||
|
if (element.usagePage == kHIDPage_Button) {
|
||||||
|
uint16_t usage = element.usage;
|
||||||
|
_usage = JOYButtonUsageGeneric0 + usage;
|
||||||
|
}
|
||||||
|
else if (element.usagePage == kHIDPage_GenericDesktop) {
|
||||||
|
switch (element.usage) {
|
||||||
|
case kHIDUsage_GD_DPadUp: _usage = JOYButtonUsageDPadUp; break;
|
||||||
|
case kHIDUsage_GD_DPadDown: _usage = JOYButtonUsageDPadDown; break;
|
||||||
|
case kHIDUsage_GD_DPadRight: _usage = JOYButtonUsageDPadRight; break;
|
||||||
|
case kHIDUsage_GD_DPadLeft: _usage = JOYButtonUsageDPadLeft; break;
|
||||||
|
case kHIDUsage_GD_Start: _usage = JOYButtonUsageStart; break;
|
||||||
|
case kHIDUsage_GD_Select: _usage = JOYButtonUsageSelect; break;
|
||||||
|
case kHIDUsage_GD_SystemMainMenu: _usage = JOYButtonUsageHome; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool) isPressed
|
||||||
|
{
|
||||||
|
return _state;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)updateState
|
||||||
|
{
|
||||||
|
bool state = [_element value];
|
||||||
|
if (_state != state) {
|
||||||
|
_state = state;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
41
JoyKit/JOYController.h
Normal file
41
JoyKit/JOYController.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "JOYButton.h"
|
||||||
|
#import "JOYAxis.h"
|
||||||
|
#import "JOYAxes2D.h"
|
||||||
|
#import "JOYHat.h"
|
||||||
|
|
||||||
|
static NSString const *JOYAxesEmulateButtonsKey = @"JOYAxesEmulateButtons";
|
||||||
|
static NSString const *JOYAxes2DEmulateButtonsKey = @"JOYAxes2DEmulateButtons";
|
||||||
|
static NSString const *JOYHatsEmulateButtonsKey = @"JOYHatsEmulateButtons";
|
||||||
|
|
||||||
|
@class JOYController;
|
||||||
|
|
||||||
|
@protocol JOYListener <NSObject>
|
||||||
|
|
||||||
|
@optional
|
||||||
|
-(void) controllerConnected:(JOYController *)controller;
|
||||||
|
-(void) controllerDisconnected:(JOYController *)controller;
|
||||||
|
-(void) controller:(JOYController *)controller buttonChangedState:(JOYButton *)button;
|
||||||
|
-(void) controller:(JOYController *)controller movedAxis:(JOYAxis *)axis;
|
||||||
|
-(void) controller:(JOYController *)controller movedAxes2D:(JOYAxes2D *)axes;
|
||||||
|
-(void) controller:(JOYController *)controller movedHat:(JOYHat *)hat;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface JOYController : NSObject
|
||||||
|
+ (void)startOnRunLoop:(NSRunLoop *)runloop withOptions: (NSDictionary *)options;
|
||||||
|
+ (NSArray<JOYController *> *) allControllers;
|
||||||
|
+ (void) registerListener:(id<JOYListener>)listener;
|
||||||
|
+ (void) unregisterListener:(id<JOYListener>)listener;
|
||||||
|
- (NSString *)deviceName;
|
||||||
|
- (NSString *)uniqueID;
|
||||||
|
- (NSArray<JOYButton *> *) buttons;
|
||||||
|
- (NSArray<JOYAxis *> *) axes;
|
||||||
|
- (NSArray<JOYAxes2D *> *) axes2D;
|
||||||
|
- (NSArray<JOYHat *> *) hats;
|
||||||
|
- (void)setRumbleAmplitude:(double)amp;
|
||||||
|
- (void)setPlayerLEDs:(uint8_t)mask;
|
||||||
|
@property (readonly, getter=isConnected) bool connected;
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
913
JoyKit/JOYController.m
Normal file
913
JoyKit/JOYController.m
Normal file
@ -0,0 +1,913 @@
|
|||||||
|
#import "JOYController.h"
|
||||||
|
#import "JOYMultiplayerController.h"
|
||||||
|
#import "JOYElement.h"
|
||||||
|
#import "JOYSubElement.h"
|
||||||
|
#import "JOYFullReportElement.h"
|
||||||
|
|
||||||
|
#import "JOYEmulatedButton.h"
|
||||||
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
|
||||||
|
#define PWM_RESOLUTION 16
|
||||||
|
|
||||||
|
static NSString const *JOYAxisGroups = @"JOYAxisGroups";
|
||||||
|
static NSString const *JOYReportIDFilters = @"JOYReportIDFilters";
|
||||||
|
static NSString const *JOYButtonUsageMapping = @"JOYButtonUsageMapping";
|
||||||
|
static NSString const *JOYAxisUsageMapping = @"JOYAxisUsageMapping";
|
||||||
|
static NSString const *JOYAxes2DUsageMapping = @"JOYAxes2DUsageMapping";
|
||||||
|
static NSString const *JOYCustomReports = @"JOYCustomReports";
|
||||||
|
static NSString const *JOYIsSwitch = @"JOYIsSwitch";
|
||||||
|
static NSString const *JOYRumbleUsage = @"JOYRumbleUsage";
|
||||||
|
static NSString const *JOYRumbleUsagePage = @"JOYRumbleUsagePage";
|
||||||
|
static NSString const *JOYConnectedUsage = @"JOYConnectedUsage";
|
||||||
|
static NSString const *JOYConnectedUsagePage = @"JOYConnectedUsagePage";
|
||||||
|
static NSString const *JOYRumbleMin = @"JOYRumbleMin";
|
||||||
|
static NSString const *JOYRumbleMax = @"JOYRumbleMax";
|
||||||
|
static NSString const *JOYSwapZRz = @"JOYSwapZRz";
|
||||||
|
static NSString const *JOYActivationReport = @"JOYActivationReport";
|
||||||
|
static NSString const *JOYIgnoredReports = @"JOYIgnoredReports";
|
||||||
|
static NSString const *JOYIsDualShock3 = @"JOYIsDualShock3";
|
||||||
|
|
||||||
|
static NSMutableDictionary<id, JOYController *> *controllers; // Physical controllers
|
||||||
|
static NSMutableArray<JOYController *> *exposedControllers; // Logical controllers
|
||||||
|
|
||||||
|
static NSDictionary *hacksByName = nil;
|
||||||
|
static NSDictionary *hacksByManufacturer = nil;
|
||||||
|
|
||||||
|
static NSMutableSet<id<JOYListener>> *listeners = nil;
|
||||||
|
|
||||||
|
static bool axesEmulateButtons = false;
|
||||||
|
static bool axes2DEmulateButtons = false;
|
||||||
|
static bool hatsEmulateButtons = false;
|
||||||
|
|
||||||
|
@interface JOYController ()
|
||||||
|
+ (void)controllerAdded:(IOHIDDeviceRef) device;
|
||||||
|
+ (void)controllerRemoved:(IOHIDDeviceRef) device;
|
||||||
|
- (void)elementChanged:(IOHIDElementRef) element;
|
||||||
|
- (void)gotReport:(NSData *)report;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface JOYButton ()
|
||||||
|
- (instancetype)initWithElement:(JOYElement *)element;
|
||||||
|
- (bool)updateState;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface JOYAxis ()
|
||||||
|
- (instancetype)initWithElement:(JOYElement *)element;
|
||||||
|
- (bool)updateState;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface JOYHat ()
|
||||||
|
- (instancetype)initWithElement:(JOYElement *)element;
|
||||||
|
- (bool)updateState;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface JOYAxes2D ()
|
||||||
|
- (instancetype)initWithFirstElement:(JOYElement *)element1 secondElement:(JOYElement *)element2;
|
||||||
|
- (bool)updateState;
|
||||||
|
@end
|
||||||
|
|
||||||
|
static NSDictionary *CreateHIDDeviceMatchDictionary(const UInt32 page, const UInt32 usage)
|
||||||
|
{
|
||||||
|
return @{
|
||||||
|
@kIOHIDDeviceUsagePageKey: @(page),
|
||||||
|
@kIOHIDDeviceUsageKey: @(usage),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HIDDeviceAdded(void *context, IOReturn result, void *sender, IOHIDDeviceRef device)
|
||||||
|
{
|
||||||
|
[JOYController controllerAdded:device];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HIDDeviceRemoved(void *context, IOReturn result, void *sender, IOHIDDeviceRef device)
|
||||||
|
{
|
||||||
|
[JOYController controllerRemoved:device];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HIDInput(void *context, IOReturn result, void *sender, IOHIDValueRef value)
|
||||||
|
{
|
||||||
|
[(__bridge JOYController *)context elementChanged:IOHIDValueGetElement(value)];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HIDReport(void *context, IOReturn result, void *sender, IOHIDReportType type,
|
||||||
|
uint32_t reportID, uint8_t *report, CFIndex reportLength)
|
||||||
|
{
|
||||||
|
if (reportLength) {
|
||||||
|
[(__bridge JOYController *)context gotReport:[[NSData alloc] initWithBytesNoCopy:report length:reportLength freeWhenDone:NO]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
uint8_t reportID;
|
||||||
|
uint8_t sequence;
|
||||||
|
uint8_t rumbleData[8];
|
||||||
|
uint8_t command;
|
||||||
|
uint8_t commandData[26];
|
||||||
|
} JOYSwitchPacket;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
uint8_t reportID;
|
||||||
|
uint8_t padding;
|
||||||
|
uint8_t rumbleRightDuration;
|
||||||
|
uint8_t rumbleRightStrength;
|
||||||
|
uint8_t rumbleLeftDuration;
|
||||||
|
uint8_t rumbleLeftStrength;
|
||||||
|
uint32_t padding2;
|
||||||
|
uint8_t ledsEnabled;
|
||||||
|
struct {
|
||||||
|
uint8_t timeEnabled;
|
||||||
|
uint8_t dutyLength;
|
||||||
|
uint8_t enabled;
|
||||||
|
uint8_t dutyOff;
|
||||||
|
uint8_t dutyOn;
|
||||||
|
} __attribute__((packed)) led[5];
|
||||||
|
uint8_t padding3[13];
|
||||||
|
} JOYDualShock3Output;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
JOYSwitchPacket switchPacket;
|
||||||
|
JOYDualShock3Output ds3Output;
|
||||||
|
} JOYVendorSpecificOutput;
|
||||||
|
|
||||||
|
@implementation JOYController
|
||||||
|
{
|
||||||
|
IOHIDDeviceRef _device;
|
||||||
|
NSMutableDictionary<JOYElement *, JOYButton *> *_buttons;
|
||||||
|
NSMutableDictionary<JOYElement *, JOYAxis *> *_axes;
|
||||||
|
NSMutableDictionary<JOYElement *, JOYAxes2D *> *_axes2D;
|
||||||
|
NSMutableDictionary<JOYElement *, JOYHat *> *_hats;
|
||||||
|
NSMutableDictionary<NSNumber *, JOYFullReportElement *> *_fullReportElements;
|
||||||
|
NSMutableDictionary<JOYFullReportElement *, NSArray<JOYElement *> *> *_multiElements;
|
||||||
|
|
||||||
|
// Button emulation
|
||||||
|
NSMutableDictionary<NSNumber *, JOYEmulatedButton *> *_axisEmulatedButtons;
|
||||||
|
NSMutableDictionary<NSNumber *, NSArray <JOYEmulatedButton *> *> *_axes2DEmulatedButtons;
|
||||||
|
NSMutableDictionary<NSNumber *, NSArray <JOYEmulatedButton *> *> *_hatEmulatedButtons;
|
||||||
|
|
||||||
|
JOYElement *_rumbleElement;
|
||||||
|
JOYElement *_connectedElement;
|
||||||
|
NSMutableDictionary<NSValue *, JOYElement *> *_iokitToJOY;
|
||||||
|
NSString *_serialSuffix;
|
||||||
|
bool _isSwitch; // Does this controller use the Switch protocol?
|
||||||
|
bool _isDualShock3; // Does this controller use DS3 outputs?
|
||||||
|
JOYVendorSpecificOutput _lastVendorSpecificOutput;
|
||||||
|
volatile double _rumbleAmplitude;
|
||||||
|
bool _physicallyConnected;
|
||||||
|
bool _logicallyConnected;
|
||||||
|
|
||||||
|
NSDictionary *_hacks;
|
||||||
|
NSMutableData *_lastReport;
|
||||||
|
|
||||||
|
// Used when creating inputs
|
||||||
|
JOYElement *_previousAxisElement;
|
||||||
|
|
||||||
|
uint8_t _playerLEDs;
|
||||||
|
double _sentRumbleAmp;
|
||||||
|
unsigned _rumbleCounter;
|
||||||
|
bool _deviceCantSendReports;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithDevice:(IOHIDDeviceRef) device hacks:(NSDictionary *)hacks
|
||||||
|
{
|
||||||
|
return [self initWithDevice:device reportIDFilter:nil serialSuffix:nil hacks:hacks];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)createOutputForElement:(JOYElement *)element
|
||||||
|
{
|
||||||
|
uint16_t rumbleUsagePage = (uint16_t)[_hacks[JOYRumbleUsagePage] unsignedIntValue];
|
||||||
|
uint16_t rumbleUsage = (uint16_t)[_hacks[JOYRumbleUsage] unsignedIntValue];
|
||||||
|
|
||||||
|
if (!_rumbleElement && rumbleUsage && rumbleUsagePage && element.usage == rumbleUsage && element.usagePage == rumbleUsagePage) {
|
||||||
|
if (_hacks[JOYRumbleMin]) {
|
||||||
|
element.min = [_hacks[JOYRumbleMin] unsignedIntValue];
|
||||||
|
}
|
||||||
|
if (_hacks[JOYRumbleMax]) {
|
||||||
|
element.max = [_hacks[JOYRumbleMax] unsignedIntValue];
|
||||||
|
}
|
||||||
|
_rumbleElement = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)createInputForElement:(JOYElement *)element
|
||||||
|
{
|
||||||
|
uint16_t connectedUsagePage = (uint16_t)[_hacks[JOYConnectedUsagePage] unsignedIntValue];
|
||||||
|
uint16_t connectedUsage = (uint16_t)[_hacks[JOYConnectedUsage] unsignedIntValue];
|
||||||
|
|
||||||
|
if (!_connectedElement && connectedUsage && connectedUsagePage && element.usage == connectedUsage && element.usagePage == connectedUsagePage) {
|
||||||
|
_connectedElement = element;
|
||||||
|
_logicallyConnected = element.value != element.min;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.usagePage == kHIDPage_Button) {
|
||||||
|
button: {
|
||||||
|
JOYButton *button = [[JOYButton alloc] initWithElement: element];
|
||||||
|
[_buttons setObject:button forKey:element];
|
||||||
|
NSNumber *replacementUsage = _hacks[JOYButtonUsageMapping][@(button.usage)];
|
||||||
|
if (replacementUsage) {
|
||||||
|
button.usage = [replacementUsage unsignedIntValue];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (element.usagePage == kHIDPage_GenericDesktop) {
|
||||||
|
NSDictionary *axisGroups = @{
|
||||||
|
@(kHIDUsage_GD_X): @(0),
|
||||||
|
@(kHIDUsage_GD_Y): @(0),
|
||||||
|
@(kHIDUsage_GD_Z): @(1),
|
||||||
|
@(kHIDUsage_GD_Rx): @(2),
|
||||||
|
@(kHIDUsage_GD_Ry): @(2),
|
||||||
|
@(kHIDUsage_GD_Rz): @(1),
|
||||||
|
};
|
||||||
|
|
||||||
|
axisGroups = _hacks[JOYAxisGroups] ?: axisGroups;
|
||||||
|
|
||||||
|
switch (element.usage) {
|
||||||
|
case kHIDUsage_GD_X:
|
||||||
|
case kHIDUsage_GD_Y:
|
||||||
|
case kHIDUsage_GD_Z:
|
||||||
|
case kHIDUsage_GD_Rx:
|
||||||
|
case kHIDUsage_GD_Ry:
|
||||||
|
case kHIDUsage_GD_Rz: {
|
||||||
|
|
||||||
|
JOYElement *other = _previousAxisElement;
|
||||||
|
_previousAxisElement = element;
|
||||||
|
if (!other) goto single;
|
||||||
|
if (other.usage >= element.usage) goto single;
|
||||||
|
if (other.reportID != element.reportID) goto single;
|
||||||
|
if (![axisGroups[@(other.usage)] isEqualTo: axisGroups[@(element.usage)]]) goto single;
|
||||||
|
if (other.parentID != element.parentID) goto single;
|
||||||
|
|
||||||
|
JOYAxes2D *axes = nil;
|
||||||
|
if (other.usage == kHIDUsage_GD_Z && element.usage == kHIDUsage_GD_Rz && [_hacks[JOYSwapZRz] boolValue]) {
|
||||||
|
axes = [[JOYAxes2D alloc] initWithFirstElement:element secondElement:other];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
axes = [[JOYAxes2D alloc] initWithFirstElement:other secondElement:element];
|
||||||
|
}
|
||||||
|
NSNumber *replacementUsage = _hacks[JOYAxes2DUsageMapping][@(axes.usage)];
|
||||||
|
if (replacementUsage) {
|
||||||
|
axes.usage = [replacementUsage unsignedIntValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
[_axisEmulatedButtons removeObjectForKey:@(_axes[other].uniqueID)];
|
||||||
|
[_axes removeObjectForKey:other];
|
||||||
|
_previousAxisElement = nil;
|
||||||
|
_axes2D[other] = axes;
|
||||||
|
_axes2D[element] = axes;
|
||||||
|
|
||||||
|
if (axes2DEmulateButtons) {
|
||||||
|
_axes2DEmulatedButtons[@(axes.uniqueID)] = @[
|
||||||
|
[[JOYEmulatedButton alloc] initWithUsage:JOYButtonUsageDPadLeft uniqueID:axes.uniqueID | 0x100000000L],
|
||||||
|
[[JOYEmulatedButton alloc] initWithUsage:JOYButtonUsageDPadRight uniqueID:axes.uniqueID | 0x200000000L],
|
||||||
|
[[JOYEmulatedButton alloc] initWithUsage:JOYButtonUsageDPadUp uniqueID:axes.uniqueID | 0x300000000L],
|
||||||
|
[[JOYEmulatedButton alloc] initWithUsage:JOYButtonUsageDPadDown uniqueID:axes.uniqueID | 0x400000000L],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
for (NSArray *group in axes2d) {
|
||||||
|
break;
|
||||||
|
IOHIDElementRef first = (__bridge IOHIDElementRef)group[0];
|
||||||
|
IOHIDElementRef second = (__bridge IOHIDElementRef)group[1];
|
||||||
|
if (IOHIDElementGetUsage(first) > element.usage) continue;
|
||||||
|
if (IOHIDElementGetUsage(second) > element.usage) continue;
|
||||||
|
if (IOHIDElementGetReportID(first) != IOHIDElementGetReportID(element)) continue;
|
||||||
|
if ((IOHIDElementGetUsage(first) - kHIDUsage_GD_X) / 3 != (element.usage - kHIDUsage_GD_X) / 3) continue;
|
||||||
|
if (IOHIDElementGetParent(first) != IOHIDElementGetParent(element)) continue;
|
||||||
|
|
||||||
|
[axes2d removeObject:group];
|
||||||
|
[axes3d addObject:@[(__bridge id)first, (__bridge id)second, _element]];
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
single:
|
||||||
|
case kHIDUsage_GD_Slider:
|
||||||
|
case kHIDUsage_GD_Dial:
|
||||||
|
case kHIDUsage_GD_Wheel: {
|
||||||
|
JOYAxis *axis = [[JOYAxis alloc] initWithElement: element];
|
||||||
|
[_axes setObject:axis forKey:element];
|
||||||
|
|
||||||
|
NSNumber *replacementUsage = _hacks[JOYAxisUsageMapping][@(axis.usage)];
|
||||||
|
if (replacementUsage) {
|
||||||
|
axis.usage = [replacementUsage unsignedIntValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (axesEmulateButtons && axis.usage >= JOYAxisUsageL1 && axis.usage <= JOYAxisUsageR3) {
|
||||||
|
_axisEmulatedButtons[@(axis.uniqueID)] =
|
||||||
|
[[JOYEmulatedButton alloc] initWithUsage:axis.usage - JOYAxisUsageL1 + JOYButtonUsageL1 uniqueID:axis.uniqueID];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (axesEmulateButtons && axis.usage >= JOYAxisUsageGeneric0) {
|
||||||
|
_axisEmulatedButtons[@(axis.uniqueID)] =
|
||||||
|
[[JOYEmulatedButton alloc] initWithUsage:axis.usage - JOYAxisUsageGeneric0 + JOYButtonUsageGeneric0 uniqueID:axis.uniqueID];
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kHIDUsage_GD_DPadUp:
|
||||||
|
case kHIDUsage_GD_DPadDown:
|
||||||
|
case kHIDUsage_GD_DPadRight:
|
||||||
|
case kHIDUsage_GD_DPadLeft:
|
||||||
|
case kHIDUsage_GD_Start:
|
||||||
|
case kHIDUsage_GD_Select:
|
||||||
|
case kHIDUsage_GD_SystemMainMenu:
|
||||||
|
goto button;
|
||||||
|
|
||||||
|
case kHIDUsage_GD_Hatswitch: {
|
||||||
|
JOYHat *hat = [[JOYHat alloc] initWithElement: element];
|
||||||
|
[_hats setObject:hat forKey:element];
|
||||||
|
if (hatsEmulateButtons) {
|
||||||
|
_hatEmulatedButtons[@(hat.uniqueID)] = @[
|
||||||
|
[[JOYEmulatedButton alloc] initWithUsage:JOYButtonUsageDPadLeft uniqueID:hat.uniqueID | 0x100000000L],
|
||||||
|
[[JOYEmulatedButton alloc] initWithUsage:JOYButtonUsageDPadRight uniqueID:hat.uniqueID | 0x200000000L],
|
||||||
|
[[JOYEmulatedButton alloc] initWithUsage:JOYButtonUsageDPadUp uniqueID:hat.uniqueID | 0x300000000L],
|
||||||
|
[[JOYEmulatedButton alloc] initWithUsage:JOYButtonUsageDPadDown uniqueID:hat.uniqueID | 0x400000000L],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithDevice:(IOHIDDeviceRef)device reportIDFilter:(NSArray <NSNumber *> *) filter serialSuffix:(NSString *)suffix hacks:(NSDictionary *)hacks
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (!self) return self;
|
||||||
|
|
||||||
|
_physicallyConnected = true;
|
||||||
|
_logicallyConnected = true;
|
||||||
|
_device = (IOHIDDeviceRef)CFRetain(device);
|
||||||
|
_serialSuffix = suffix;
|
||||||
|
_playerLEDs = -1;
|
||||||
|
|
||||||
|
IOHIDDeviceRegisterInputValueCallback(device, HIDInput, (void *)self);
|
||||||
|
IOHIDDeviceScheduleWithRunLoop(device, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||||
|
|
||||||
|
NSArray *array = CFBridgingRelease(IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone));
|
||||||
|
_buttons = [NSMutableDictionary dictionary];
|
||||||
|
_axes = [NSMutableDictionary dictionary];
|
||||||
|
_axes2D = [NSMutableDictionary dictionary];
|
||||||
|
_hats = [NSMutableDictionary dictionary];
|
||||||
|
_axisEmulatedButtons = [NSMutableDictionary dictionary];
|
||||||
|
_axes2DEmulatedButtons = [NSMutableDictionary dictionary];
|
||||||
|
_hatEmulatedButtons = [NSMutableDictionary dictionary];
|
||||||
|
_iokitToJOY = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
|
|
||||||
|
//NSMutableArray *axes3d = [NSMutableArray array];
|
||||||
|
|
||||||
|
_hacks = hacks;
|
||||||
|
_isSwitch = [_hacks[JOYIsSwitch] boolValue];
|
||||||
|
_isDualShock3 = [_hacks[JOYIsDualShock3] boolValue];
|
||||||
|
|
||||||
|
NSDictionary *customReports = hacks[JOYCustomReports];
|
||||||
|
_lastReport = [NSMutableData dataWithLength:MAX(
|
||||||
|
MAX(
|
||||||
|
[(__bridge NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDMaxInputReportSizeKey)) unsignedIntValue],
|
||||||
|
[(__bridge NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDMaxOutputReportSizeKey)) unsignedIntValue]
|
||||||
|
),
|
||||||
|
[(__bridge NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDMaxFeatureReportSizeKey)) unsignedIntValue]
|
||||||
|
)];
|
||||||
|
IOHIDDeviceRegisterInputReportCallback(device, _lastReport.mutableBytes, _lastReport.length, HIDReport, (void *)self);
|
||||||
|
|
||||||
|
if (hacks[JOYCustomReports]) {
|
||||||
|
_multiElements = [NSMutableDictionary dictionary];
|
||||||
|
_fullReportElements = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
|
|
||||||
|
for (NSNumber *_reportID in customReports) {
|
||||||
|
signed reportID = [_reportID intValue];
|
||||||
|
bool isOutput = false;
|
||||||
|
if (reportID < 0) {
|
||||||
|
isOutput = true;
|
||||||
|
reportID = -reportID;
|
||||||
|
}
|
||||||
|
|
||||||
|
JOYFullReportElement *element = [[JOYFullReportElement alloc] initWithDevice:device reportID:reportID];
|
||||||
|
NSMutableArray *elements = [NSMutableArray array];
|
||||||
|
for (NSDictionary <NSString *,NSNumber *> *subElementDef in customReports[_reportID]) {
|
||||||
|
if (filter && subElementDef[@"reportID"] && ![filter containsObject:subElementDef[@"reportID"]]) continue;
|
||||||
|
JOYSubElement *subElement = [[JOYSubElement alloc] initWithRealElement:element
|
||||||
|
size:subElementDef[@"size"].unsignedLongValue
|
||||||
|
offset:subElementDef[@"offset"].unsignedLongValue + 8 // Compensate for the reportID
|
||||||
|
usagePage:subElementDef[@"usagePage"].unsignedLongValue
|
||||||
|
usage:subElementDef[@"usage"].unsignedLongValue
|
||||||
|
min:subElementDef[@"min"].unsignedIntValue
|
||||||
|
max:subElementDef[@"max"].unsignedIntValue];
|
||||||
|
[elements addObject:subElement];
|
||||||
|
if (isOutput) {
|
||||||
|
[self createOutputForElement:subElement];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[self createInputForElement:subElement];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_multiElements[element] = elements;
|
||||||
|
if (!isOutput) {
|
||||||
|
_fullReportElements[@(reportID)] = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
id previous = nil;
|
||||||
|
NSSet *ignoredReports = nil;
|
||||||
|
if (hacks[ignoredReports]) {
|
||||||
|
ignoredReports = [NSSet setWithArray:hacks[ignoredReports]];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (id _element in array) {
|
||||||
|
if (_element == previous) continue; // Some elements are reported twice for some reason
|
||||||
|
previous = _element;
|
||||||
|
JOYElement *element = [[JOYElement alloc] initWithElement:(__bridge IOHIDElementRef)_element];
|
||||||
|
|
||||||
|
bool isOutput = false;
|
||||||
|
if (filter && ![filter containsObject:@(element.reportID)]) continue;
|
||||||
|
|
||||||
|
switch (IOHIDElementGetType((__bridge IOHIDElementRef)_element)) {
|
||||||
|
/* Handled */
|
||||||
|
case kIOHIDElementTypeInput_Misc:
|
||||||
|
case kIOHIDElementTypeInput_Button:
|
||||||
|
case kIOHIDElementTypeInput_Axis:
|
||||||
|
break;
|
||||||
|
case kIOHIDElementTypeOutput:
|
||||||
|
isOutput = true;
|
||||||
|
break;
|
||||||
|
/* Ignored */
|
||||||
|
default:
|
||||||
|
case kIOHIDElementTypeInput_ScanCodes:
|
||||||
|
case kIOHIDElementTypeInput_NULL:
|
||||||
|
case kIOHIDElementTypeFeature:
|
||||||
|
case kIOHIDElementTypeCollection:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((!isOutput && [ignoredReports containsObject:@(element.reportID)]) ||
|
||||||
|
(isOutput && [ignoredReports containsObject:@(-element.reportID)])) continue;
|
||||||
|
|
||||||
|
|
||||||
|
if (IOHIDElementIsArray((__bridge IOHIDElementRef)_element)) continue;
|
||||||
|
|
||||||
|
if (isOutput) {
|
||||||
|
[self createOutputForElement:element];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[self createInputForElement:element];
|
||||||
|
}
|
||||||
|
|
||||||
|
_iokitToJOY[@(IOHIDElementGetCookie((__bridge IOHIDElementRef)_element))] = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
[exposedControllers addObject:self];
|
||||||
|
if (_logicallyConnected) {
|
||||||
|
for (id<JOYListener> listener in listeners) {
|
||||||
|
if ([listener respondsToSelector:@selector(controllerConnected:)]) {
|
||||||
|
[listener controllerConnected:self];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_hacks[JOYActivationReport]) {
|
||||||
|
[self sendReport:hacks[JOYActivationReport]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isSwitch) {
|
||||||
|
[self sendReport:[NSData dataWithBytes:(uint8_t[]){0x80, 0x04} length:2]];
|
||||||
|
[self sendReport:[NSData dataWithBytes:(uint8_t[]){0x80, 0x02} length:2]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isDualShock3) {
|
||||||
|
_lastVendorSpecificOutput.ds3Output = (JOYDualShock3Output){
|
||||||
|
.reportID = 1,
|
||||||
|
.led = {
|
||||||
|
{.timeEnabled = 0xff, .dutyLength = 0x27, .enabled = 0x10, .dutyOff = 0, .dutyOn = 0x32},
|
||||||
|
{.timeEnabled = 0xff, .dutyLength = 0x27, .enabled = 0x10, .dutyOff = 0, .dutyOn = 0x32},
|
||||||
|
{.timeEnabled = 0xff, .dutyLength = 0x27, .enabled = 0x10, .dutyOff = 0, .dutyOn = 0x32},
|
||||||
|
{.timeEnabled = 0xff, .dutyLength = 0x27, .enabled = 0x10, .dutyOff = 0, .dutyOn = 0x32},
|
||||||
|
{.timeEnabled = 0, .dutyLength = 0, .enabled = 0, .dutyOff = 0, .dutyOn = 0},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)deviceName
|
||||||
|
{
|
||||||
|
if (!_device) return nil;
|
||||||
|
return IOHIDDeviceGetProperty(_device, CFSTR(kIOHIDProductKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)uniqueID
|
||||||
|
{
|
||||||
|
if (!_device) return nil;
|
||||||
|
NSString *serial = (__bridge NSString *)IOHIDDeviceGetProperty(_device, CFSTR(kIOHIDSerialNumberKey));
|
||||||
|
if (!serial || [(__bridge NSString *)IOHIDDeviceGetProperty(_device, CFSTR(kIOHIDTransportKey)) isEqualToString:@"USB"]) {
|
||||||
|
serial = [NSString stringWithFormat:@"%04x%04x%08x",
|
||||||
|
[(__bridge NSNumber *)IOHIDDeviceGetProperty(_device, CFSTR(kIOHIDVendorIDKey)) unsignedIntValue],
|
||||||
|
[(__bridge NSNumber *)IOHIDDeviceGetProperty(_device, CFSTR(kIOHIDProductIDKey)) unsignedIntValue],
|
||||||
|
[(__bridge NSNumber *)IOHIDDeviceGetProperty(_device, CFSTR(kIOHIDLocationIDKey)) unsignedIntValue]];
|
||||||
|
}
|
||||||
|
if (_serialSuffix) {
|
||||||
|
return [NSString stringWithFormat:@"%@-%@", serial, _serialSuffix];
|
||||||
|
}
|
||||||
|
return serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)description
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat:@"<%@: %p, %@, %@>", self.className, self, self.deviceName, self.uniqueID];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<JOYButton *> *)buttons
|
||||||
|
{
|
||||||
|
NSMutableArray *ret = [[_buttons allValues] mutableCopy];
|
||||||
|
[ret addObjectsFromArray:_axisEmulatedButtons.allValues];
|
||||||
|
for (NSArray *array in _axes2DEmulatedButtons.allValues) {
|
||||||
|
[ret addObjectsFromArray:array];
|
||||||
|
}
|
||||||
|
for (NSArray *array in _hatEmulatedButtons.allValues) {
|
||||||
|
[ret addObjectsFromArray:array];
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<JOYAxis *> *)axes
|
||||||
|
{
|
||||||
|
return [_axes allValues];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<JOYAxes2D *> *)axes2D
|
||||||
|
{
|
||||||
|
return [[NSSet setWithArray:[_axes2D allValues]] allObjects];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<JOYHat *> *)hats
|
||||||
|
{
|
||||||
|
return [_hats allValues];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)gotReport:(NSData *)report
|
||||||
|
{
|
||||||
|
JOYFullReportElement *element = _fullReportElements[@(*(uint8_t *)report.bytes)];
|
||||||
|
if (element) {
|
||||||
|
[element updateValue:report];
|
||||||
|
|
||||||
|
NSArray<JOYElement *> *subElements = _multiElements[element];
|
||||||
|
if (subElements) {
|
||||||
|
for (JOYElement *subElement in subElements) {
|
||||||
|
[self _elementChanged:subElement];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[self updateRumble];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)elementChanged:(IOHIDElementRef)element
|
||||||
|
{
|
||||||
|
JOYElement *_element = _iokitToJOY[@(IOHIDElementGetCookie(element))];
|
||||||
|
if (_element) {
|
||||||
|
[self _elementChanged:_element];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//NSLog(@"Unhandled usage %x (Cookie: %x, Usage: %x)", IOHIDElementGetUsage(element), IOHIDElementGetCookie(element), IOHIDElementGetUsage(element));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_elementChanged:(JOYElement *)element
|
||||||
|
{
|
||||||
|
if (element == _connectedElement) {
|
||||||
|
bool old = self.connected;
|
||||||
|
_logicallyConnected = _connectedElement.value != _connectedElement.min;
|
||||||
|
if (!old && self.connected) {
|
||||||
|
for (id<JOYListener> listener in listeners) {
|
||||||
|
if ([listener respondsToSelector:@selector(controllerConnected:)]) {
|
||||||
|
[listener controllerConnected:self];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (old && !self.connected) {
|
||||||
|
for (id<JOYListener> listener in listeners) {
|
||||||
|
if ([listener respondsToSelector:@selector(controllerDisconnected:)]) {
|
||||||
|
[listener controllerDisconnected:self];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.connected) return;
|
||||||
|
{
|
||||||
|
JOYButton *button = _buttons[element];
|
||||||
|
if (button) {
|
||||||
|
if ([button updateState]) {
|
||||||
|
for (id<JOYListener> listener in listeners) {
|
||||||
|
if ([listener respondsToSelector:@selector(controller:buttonChangedState:)]) {
|
||||||
|
[listener controller:self buttonChangedState:button];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
JOYAxis *axis = _axes[element];
|
||||||
|
if (axis) {
|
||||||
|
if ([axis updateState]) {
|
||||||
|
for (id<JOYListener> listener in listeners) {
|
||||||
|
if ([listener respondsToSelector:@selector(controller:movedAxis:)]) {
|
||||||
|
[listener controller:self movedAxis:axis];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JOYEmulatedButton *button = _axisEmulatedButtons[@(axis.uniqueID)];
|
||||||
|
if ([button updateStateFromAxis:axis]) {
|
||||||
|
for (id<JOYListener> listener in listeners) {
|
||||||
|
if ([listener respondsToSelector:@selector(controller:buttonChangedState:)]) {
|
||||||
|
[listener controller:self buttonChangedState:button];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
JOYAxes2D *axes = _axes2D[element];
|
||||||
|
if (axes) {
|
||||||
|
if ([axes updateState]) {
|
||||||
|
for (id<JOYListener> listener in listeners) {
|
||||||
|
if ([listener respondsToSelector:@selector(controller:movedAxes2D:)]) {
|
||||||
|
[listener controller:self movedAxes2D:axes];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NSArray <JOYEmulatedButton *> *buttons = _axes2DEmulatedButtons[@(axes.uniqueID)];
|
||||||
|
for (JOYEmulatedButton *button in buttons) {
|
||||||
|
if ([button updateStateFromAxes2D:axes]) {
|
||||||
|
for (id<JOYListener> listener in listeners) {
|
||||||
|
if ([listener respondsToSelector:@selector(controller:buttonChangedState:)]) {
|
||||||
|
[listener controller:self buttonChangedState:button];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
JOYHat *hat = _hats[element];
|
||||||
|
if (hat) {
|
||||||
|
if ([hat updateState]) {
|
||||||
|
for (id<JOYListener> listener in listeners) {
|
||||||
|
if ([listener respondsToSelector:@selector(controller:movedHat:)]) {
|
||||||
|
[listener controller:self movedHat:hat];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSArray <JOYEmulatedButton *> *buttons = _hatEmulatedButtons[@(hat.uniqueID)];
|
||||||
|
for (JOYEmulatedButton *button in buttons) {
|
||||||
|
if ([button updateStateFromHat:hat]) {
|
||||||
|
for (id<JOYListener> listener in listeners) {
|
||||||
|
if ([listener respondsToSelector:@selector(controller:buttonChangedState:)]) {
|
||||||
|
[listener controller:self buttonChangedState:button];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)disconnected
|
||||||
|
{
|
||||||
|
if (_logicallyConnected && [exposedControllers containsObject:self]) {
|
||||||
|
for (id<JOYListener> listener in listeners) {
|
||||||
|
if ([listener respondsToSelector:@selector(controllerDisconnected:)]) {
|
||||||
|
[listener controllerDisconnected:self];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_physicallyConnected = false;
|
||||||
|
[exposedControllers removeObject:self];
|
||||||
|
[self setRumbleAmplitude:0];
|
||||||
|
[self updateRumble];
|
||||||
|
_device = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sendReport:(NSData *)report
|
||||||
|
{
|
||||||
|
if (!report.length) return;
|
||||||
|
if (!_device) return;
|
||||||
|
if (_deviceCantSendReports) return;
|
||||||
|
/* Some Macs fail to send reports to some devices, specifically the DS3, returning the bogus(?) error code 1 after
|
||||||
|
freezing for 5 seconds. Stop sending reports if that's the case. */
|
||||||
|
if (IOHIDDeviceSetReport(_device, kIOHIDReportTypeOutput, *(uint8_t *)report.bytes, report.bytes, report.length) == 1) {
|
||||||
|
_deviceCantSendReports = true;
|
||||||
|
NSLog(@"This Mac appears to be incapable of sending output reports to %@", self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setPlayerLEDs:(uint8_t)mask
|
||||||
|
{
|
||||||
|
mask &= 0xF;
|
||||||
|
if (mask == _playerLEDs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_playerLEDs = mask;
|
||||||
|
if (_isSwitch) {
|
||||||
|
_lastVendorSpecificOutput.switchPacket.reportID = 0x1; // Rumble and LEDs
|
||||||
|
_lastVendorSpecificOutput.switchPacket.sequence++;
|
||||||
|
_lastVendorSpecificOutput.switchPacket.sequence &= 0xF;
|
||||||
|
_lastVendorSpecificOutput.switchPacket.command = 0x30; // LED
|
||||||
|
_lastVendorSpecificOutput.switchPacket.commandData[0] = mask;
|
||||||
|
[self sendReport:[NSData dataWithBytes:&_lastVendorSpecificOutput.switchPacket length:sizeof(_lastVendorSpecificOutput.switchPacket)]];
|
||||||
|
}
|
||||||
|
else if (_isDualShock3) {
|
||||||
|
_lastVendorSpecificOutput.ds3Output.reportID = 1;
|
||||||
|
_lastVendorSpecificOutput.ds3Output.ledsEnabled = mask << 1;
|
||||||
|
[self sendReport:[NSData dataWithBytes:&_lastVendorSpecificOutput.ds3Output length:sizeof(_lastVendorSpecificOutput.ds3Output)]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateRumble
|
||||||
|
{
|
||||||
|
if (!self.connected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_rumbleElement && !_isSwitch && !_isDualShock3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_rumbleElement.max == 1 && _rumbleElement.min == 0) {
|
||||||
|
double ampToSend = _rumbleCounter < round(_rumbleAmplitude * PWM_RESOLUTION);
|
||||||
|
if (ampToSend != _sentRumbleAmp) {
|
||||||
|
[_rumbleElement setValue:ampToSend];
|
||||||
|
_sentRumbleAmp = ampToSend;
|
||||||
|
}
|
||||||
|
_rumbleCounter += round(_rumbleAmplitude * PWM_RESOLUTION);
|
||||||
|
if (_rumbleCounter >= PWM_RESOLUTION) {
|
||||||
|
_rumbleCounter -= PWM_RESOLUTION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (_rumbleAmplitude == _sentRumbleAmp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_sentRumbleAmp = _rumbleAmplitude;
|
||||||
|
if (_isSwitch) {
|
||||||
|
double frequency = 144;
|
||||||
|
double amp = _rumbleAmplitude;
|
||||||
|
|
||||||
|
uint8_t highAmp = amp * 0x64;
|
||||||
|
uint8_t lowAmp = amp * 0x32 + 0x40;
|
||||||
|
if (frequency < 0) frequency = 0;
|
||||||
|
if (frequency > 1252) frequency = 1252;
|
||||||
|
uint8_t encodedFrequency = (uint8_t)round(log2(frequency / 10.0) * 32.0);
|
||||||
|
|
||||||
|
uint16_t highFreq = (encodedFrequency - 0x60) * 4;
|
||||||
|
uint8_t lowFreq = encodedFrequency - 0x40;
|
||||||
|
|
||||||
|
//if (frequency < 82 || frequency > 312) {
|
||||||
|
if (amp) {
|
||||||
|
highAmp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frequency < 40 || frequency > 626) {
|
||||||
|
lowAmp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastVendorSpecificOutput.switchPacket.rumbleData[0] = _lastVendorSpecificOutput.switchPacket.rumbleData[4] = highFreq & 0xFF;
|
||||||
|
_lastVendorSpecificOutput.switchPacket.rumbleData[1] = _lastVendorSpecificOutput.switchPacket.rumbleData[5] = (highAmp << 1) + ((highFreq >> 8) & 0x1);
|
||||||
|
_lastVendorSpecificOutput.switchPacket.rumbleData[2] = _lastVendorSpecificOutput.switchPacket.rumbleData[6] = lowFreq;
|
||||||
|
_lastVendorSpecificOutput.switchPacket.rumbleData[3] = _lastVendorSpecificOutput.switchPacket.rumbleData[7] = lowAmp;
|
||||||
|
|
||||||
|
|
||||||
|
_lastVendorSpecificOutput.switchPacket.reportID = 0x10; // Rumble only
|
||||||
|
_lastVendorSpecificOutput.switchPacket.sequence++;
|
||||||
|
_lastVendorSpecificOutput.switchPacket.sequence &= 0xF;
|
||||||
|
_lastVendorSpecificOutput.switchPacket.command = 0; // LED
|
||||||
|
[self sendReport:[NSData dataWithBytes:&_lastVendorSpecificOutput.switchPacket length:sizeof(_lastVendorSpecificOutput.switchPacket)]];
|
||||||
|
}
|
||||||
|
else if (_isDualShock3) {
|
||||||
|
_lastVendorSpecificOutput.ds3Output.reportID = 1;
|
||||||
|
_lastVendorSpecificOutput.ds3Output.rumbleLeftDuration = _lastVendorSpecificOutput.ds3Output.rumbleRightDuration = _rumbleAmplitude? 0xff : 0;
|
||||||
|
_lastVendorSpecificOutput.ds3Output.rumbleLeftStrength = _lastVendorSpecificOutput.ds3Output.rumbleRightStrength = round(_rumbleAmplitude * 0xff);
|
||||||
|
[self sendReport:[NSData dataWithBytes:&_lastVendorSpecificOutput.ds3Output length:sizeof(_lastVendorSpecificOutput.ds3Output)]];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[_rumbleElement setValue:_rumbleAmplitude * (_rumbleElement.max - _rumbleElement.min) + _rumbleElement.min];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setRumbleAmplitude:(double)amp /* andFrequency: (double)frequency */
|
||||||
|
{
|
||||||
|
if (amp < 0) amp = 0;
|
||||||
|
if (amp > 1) amp = 1;
|
||||||
|
_rumbleAmplitude = amp;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)isConnected
|
||||||
|
{
|
||||||
|
return _logicallyConnected && _physicallyConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)controllerAdded:(IOHIDDeviceRef) device
|
||||||
|
{
|
||||||
|
NSString *name = (__bridge NSString *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
|
||||||
|
NSDictionary *hacks = hacksByName[name];
|
||||||
|
if (!hacks) {
|
||||||
|
hacks = hacksByManufacturer[(__bridge NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey))];
|
||||||
|
}
|
||||||
|
NSArray *filters = hacks[JOYReportIDFilters];
|
||||||
|
JOYController *controller = nil;
|
||||||
|
if (filters) {
|
||||||
|
controller = [[JOYMultiplayerController alloc] initWithDevice:device
|
||||||
|
reportIDFilters:filters
|
||||||
|
hacks:hacks];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
controller = [[JOYController alloc] initWithDevice:device hacks:hacks];
|
||||||
|
}
|
||||||
|
|
||||||
|
[controllers setObject:controller forKey:[NSValue valueWithPointer:device]];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)controllerRemoved:(IOHIDDeviceRef) device
|
||||||
|
{
|
||||||
|
[[controllers objectForKey:[NSValue valueWithPointer:device]] disconnected];
|
||||||
|
[controllers removeObjectForKey:[NSValue valueWithPointer:device]];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSArray<JOYController *> *)allControllers
|
||||||
|
{
|
||||||
|
return exposedControllers;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)load
|
||||||
|
{
|
||||||
|
#include "ControllerConfiguration.inc"
|
||||||
|
}
|
||||||
|
|
||||||
|
+(void)registerListener:(id<JOYListener>)listener
|
||||||
|
{
|
||||||
|
[listeners addObject:listener];
|
||||||
|
}
|
||||||
|
|
||||||
|
+(void)unregisterListener:(id<JOYListener>)listener
|
||||||
|
{
|
||||||
|
[listeners removeObject:listener];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)startOnRunLoop:(NSRunLoop *)runloop withOptions: (NSDictionary *)options
|
||||||
|
{
|
||||||
|
axesEmulateButtons = [options[JOYAxesEmulateButtonsKey] boolValue];
|
||||||
|
axes2DEmulateButtons = [options[JOYAxes2DEmulateButtonsKey] boolValue];
|
||||||
|
hatsEmulateButtons = [options[JOYHatsEmulateButtonsKey] boolValue];
|
||||||
|
|
||||||
|
controllers = [NSMutableDictionary dictionary];
|
||||||
|
exposedControllers = [NSMutableArray array];
|
||||||
|
NSArray *array = @[
|
||||||
|
CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick),
|
||||||
|
CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad),
|
||||||
|
CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController),
|
||||||
|
@{@kIOHIDDeviceUsagePageKey: @(kHIDPage_Game)},
|
||||||
|
];
|
||||||
|
|
||||||
|
listeners = [NSMutableSet set];
|
||||||
|
static IOHIDManagerRef manager = nil;
|
||||||
|
if (manager) {
|
||||||
|
CFRelease(manager); // Stop the previous session
|
||||||
|
}
|
||||||
|
manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
|
||||||
|
|
||||||
|
if (!manager) return;
|
||||||
|
if (IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone)) {
|
||||||
|
CFRelease(manager);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IOHIDManagerSetDeviceMatchingMultiple(manager, (__bridge CFArrayRef)array);
|
||||||
|
IOHIDManagerRegisterDeviceMatchingCallback(manager, HIDDeviceAdded, NULL);
|
||||||
|
IOHIDManagerRegisterDeviceRemovalCallback(manager, HIDDeviceRemoved, NULL);
|
||||||
|
IOHIDManagerScheduleWithRunLoop(manager, [runloop getCFRunLoop], kCFRunLoopDefaultMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
if (_device) {
|
||||||
|
CFRelease(_device);
|
||||||
|
_device = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end
|
20
JoyKit/JOYElement.h
Normal file
20
JoyKit/JOYElement.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
|
||||||
|
@interface JOYElement : NSObject<NSCopying>
|
||||||
|
- (instancetype)initWithElement:(IOHIDElementRef)element;
|
||||||
|
- (int32_t)value;
|
||||||
|
- (NSData *)dataValue;
|
||||||
|
- (IOReturn)setValue:(uint32_t)value;
|
||||||
|
- (IOReturn)setDataValue:(NSData *)value;
|
||||||
|
@property (readonly) uint16_t usage;
|
||||||
|
@property (readonly) uint16_t usagePage;
|
||||||
|
@property (readonly) uint32_t uniqueID;
|
||||||
|
@property int32_t min;
|
||||||
|
@property int32_t max;
|
||||||
|
@property (readonly) int32_t reportID;
|
||||||
|
@property (readonly) int32_t parentID;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
133
JoyKit/JOYElement.m
Normal file
133
JoyKit/JOYElement.m
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#import "JOYElement.h"
|
||||||
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
#include <objc/runtime.h>
|
||||||
|
|
||||||
|
@implementation JOYElement
|
||||||
|
{
|
||||||
|
id _element;
|
||||||
|
IOHIDDeviceRef _device;
|
||||||
|
int32_t _min, _max;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int32_t)min
|
||||||
|
{
|
||||||
|
return MIN(_min, _max);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int32_t)max
|
||||||
|
{
|
||||||
|
return MAX(_max, _min);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)setMin:(int32_t)min
|
||||||
|
{
|
||||||
|
_min = min;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setMax:(int32_t)max
|
||||||
|
{
|
||||||
|
_max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ugly hack because IOHIDDeviceCopyMatchingElements is slow */
|
||||||
|
+ (NSArray *) cookiesToSkipForDevice:(IOHIDDeviceRef)device
|
||||||
|
{
|
||||||
|
id _device = (__bridge id)device;
|
||||||
|
NSMutableArray *ret = objc_getAssociatedObject(_device, _cmd);
|
||||||
|
if (ret) return ret;
|
||||||
|
|
||||||
|
ret = [NSMutableArray array];
|
||||||
|
NSArray *nones = CFBridgingRelease(IOHIDDeviceCopyMatchingElements(device,
|
||||||
|
(__bridge CFDictionaryRef)@{@(kIOHIDElementTypeKey): @(kIOHIDElementTypeInput_NULL)},
|
||||||
|
0));
|
||||||
|
for (id none in nones) {
|
||||||
|
[ret addObject:@(IOHIDElementGetCookie((__bridge IOHIDElementRef)none))];
|
||||||
|
}
|
||||||
|
objc_setAssociatedObject(_device, _cmd, ret, OBJC_ASSOCIATION_RETAIN);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithElement:(IOHIDElementRef)element
|
||||||
|
{
|
||||||
|
if ((self = [super init])) {
|
||||||
|
_element = (__bridge id)element;
|
||||||
|
_usage = IOHIDElementGetUsage(element);
|
||||||
|
_usagePage = IOHIDElementGetUsagePage(element);
|
||||||
|
_uniqueID = (uint32_t)IOHIDElementGetCookie(element);
|
||||||
|
_min = (int32_t) IOHIDElementGetLogicalMin(element);
|
||||||
|
_max = (int32_t) IOHIDElementGetLogicalMax(element);
|
||||||
|
_reportID = IOHIDElementGetReportID(element);
|
||||||
|
IOHIDElementRef parent = IOHIDElementGetParent(element);
|
||||||
|
_parentID = parent? (uint32_t)IOHIDElementGetCookie(parent) : -1;
|
||||||
|
_device = IOHIDElementGetDevice(element);
|
||||||
|
|
||||||
|
/* Catalina added a new input type in a way that breaks cookie consistency across macOS versions,
|
||||||
|
we shall adjust our cookies to to compensate */
|
||||||
|
unsigned cookieShift = 0, parentCookieShift = 0;
|
||||||
|
|
||||||
|
for (NSNumber *none in [JOYElement cookiesToSkipForDevice:_device]) {
|
||||||
|
if (none.unsignedIntValue < _uniqueID) {
|
||||||
|
cookieShift++;
|
||||||
|
}
|
||||||
|
if (none.unsignedIntValue < (int32_t)_parentID) {
|
||||||
|
parentCookieShift++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_uniqueID -= cookieShift;
|
||||||
|
_parentID -= parentCookieShift;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int32_t)value
|
||||||
|
{
|
||||||
|
IOHIDValueRef value = NULL;
|
||||||
|
IOHIDDeviceGetValue(_device, (__bridge IOHIDElementRef)_element, &value);
|
||||||
|
if (!value) return 0;
|
||||||
|
CFRelease(CFRetain(value)); // For some reason, this is required to prevent leaks.
|
||||||
|
return (int32_t)IOHIDValueGetIntegerValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSData *)dataValue
|
||||||
|
{
|
||||||
|
IOHIDValueRef value = NULL;
|
||||||
|
IOHIDDeviceGetValue(_device, (__bridge IOHIDElementRef)_element, &value);
|
||||||
|
if (!value) return 0;
|
||||||
|
CFRelease(CFRetain(value)); // For some reason, this is required to prevent leaks.
|
||||||
|
return [NSData dataWithBytes:IOHIDValueGetBytePtr(value) length:IOHIDValueGetLength(value)];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IOReturn)setValue:(uint32_t)value
|
||||||
|
{
|
||||||
|
IOHIDValueRef ivalue = IOHIDValueCreateWithIntegerValue(NULL, (__bridge IOHIDElementRef)_element, 0, value);
|
||||||
|
IOReturn ret = IOHIDDeviceSetValue(_device, (__bridge IOHIDElementRef)_element, ivalue);
|
||||||
|
CFRelease(ivalue);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IOReturn)setDataValue:(NSData *)value
|
||||||
|
{
|
||||||
|
IOHIDValueRef ivalue = IOHIDValueCreateWithBytes(NULL, (__bridge IOHIDElementRef)_element, 0, value.bytes, value.length);
|
||||||
|
IOReturn ret = IOHIDDeviceSetValue(_device, (__bridge IOHIDElementRef)_element, ivalue);
|
||||||
|
CFRelease(ivalue);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For use as a dictionary key */
|
||||||
|
|
||||||
|
- (NSUInteger)hash
|
||||||
|
{
|
||||||
|
return self.uniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isEqual:(id)object
|
||||||
|
{
|
||||||
|
return self->_element == object;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)copyWithZone:(nullable NSZone *)zone;
|
||||||
|
{
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
@end
|
11
JoyKit/JOYEmulatedButton.h
Normal file
11
JoyKit/JOYEmulatedButton.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#import "JOYButton.h"
|
||||||
|
#import "JOYAxis.h"
|
||||||
|
#import "JOYAxes2D.h"
|
||||||
|
#import "JOYHat.h"
|
||||||
|
|
||||||
|
@interface JOYEmulatedButton : JOYButton
|
||||||
|
- (instancetype)initWithUsage:(JOYButtonUsage)usage uniqueID:(uint64_t)uniqueID;
|
||||||
|
- (bool)updateStateFromAxis:(JOYAxis *)axis;
|
||||||
|
- (bool)updateStateFromAxes2D:(JOYAxes2D *)axes;
|
||||||
|
- (bool)updateStateFromHat:(JOYHat *)hat;
|
||||||
|
@end
|
91
JoyKit/JOYEmulatedButton.m
Normal file
91
JoyKit/JOYEmulatedButton.m
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#import "JOYEmulatedButton.h"
|
||||||
|
|
||||||
|
@interface JOYButton ()
|
||||||
|
{
|
||||||
|
@public bool _state;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation JOYEmulatedButton
|
||||||
|
{
|
||||||
|
uint64_t _uniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithUsage:(JOYButtonUsage)usage uniqueID:(uint64_t)uniqueID;
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
self.usage = usage;
|
||||||
|
_uniqueID = uniqueID;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (uint64_t)uniqueID
|
||||||
|
{
|
||||||
|
return _uniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)updateStateFromAxis:(JOYAxis *)axis
|
||||||
|
{
|
||||||
|
bool old = _state;
|
||||||
|
_state = [axis value] > 0.5;
|
||||||
|
return _state != old;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)updateStateFromAxes2D:(JOYAxes2D *)axes
|
||||||
|
{
|
||||||
|
bool old = _state;
|
||||||
|
if (axes.distance < 0.5) {
|
||||||
|
_state = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unsigned direction = ((unsigned)round(axes.angle / 360 * 8)) & 7;
|
||||||
|
switch (self.usage) {
|
||||||
|
case JOYButtonUsageDPadLeft:
|
||||||
|
_state = direction >= 3 && direction <= 5;
|
||||||
|
break;
|
||||||
|
case JOYButtonUsageDPadRight:
|
||||||
|
_state = direction <= 1 || direction == 7;
|
||||||
|
break;
|
||||||
|
case JOYButtonUsageDPadUp:
|
||||||
|
_state = direction >= 5;
|
||||||
|
break;
|
||||||
|
case JOYButtonUsageDPadDown:
|
||||||
|
_state = direction <= 3 && direction >= 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _state != old;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)updateStateFromHat:(JOYHat *)hat
|
||||||
|
{
|
||||||
|
bool old = _state;
|
||||||
|
if (!hat.pressed) {
|
||||||
|
_state = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unsigned direction = ((unsigned)round(hat.angle / 360 * 8)) & 7;
|
||||||
|
switch (self.usage) {
|
||||||
|
case JOYButtonUsageDPadLeft:
|
||||||
|
_state = direction >= 3 && direction <= 5;
|
||||||
|
break;
|
||||||
|
case JOYButtonUsageDPadRight:
|
||||||
|
_state = direction <= 1 || direction == 7;
|
||||||
|
break;
|
||||||
|
case JOYButtonUsageDPadUp:
|
||||||
|
_state = direction >= 5;
|
||||||
|
break;
|
||||||
|
case JOYButtonUsageDPadDown:
|
||||||
|
_state = direction <= 3 && direction >= 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _state != old;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
10
JoyKit/JOYFullReportElement.h
Normal file
10
JoyKit/JOYFullReportElement.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
#include "JOYElement.h"
|
||||||
|
|
||||||
|
@interface JOYFullReportElement : JOYElement<NSCopying>
|
||||||
|
- (instancetype)initWithDevice:(IOHIDDeviceRef) device reportID:(unsigned)reportID;
|
||||||
|
- (void)updateValue:(NSData *)value;
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
73
JoyKit/JOYFullReportElement.m
Normal file
73
JoyKit/JOYFullReportElement.m
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#import "JOYFullReportElement.h"
|
||||||
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
|
||||||
|
@implementation JOYFullReportElement
|
||||||
|
{
|
||||||
|
IOHIDDeviceRef _device;
|
||||||
|
NSData *_data;
|
||||||
|
unsigned _reportID;
|
||||||
|
size_t _capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (uint32_t)uniqueID
|
||||||
|
{
|
||||||
|
return _reportID ^ 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithDevice:(IOHIDDeviceRef) device reportID:(unsigned)reportID
|
||||||
|
{
|
||||||
|
if ((self = [super init])) {
|
||||||
|
_data = [[NSMutableData alloc] initWithLength:[(__bridge NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDMaxOutputReportSizeKey)) unsignedIntValue]];
|
||||||
|
*(uint8_t *)(((NSMutableData *)_data).mutableBytes) = reportID;
|
||||||
|
_reportID = reportID;
|
||||||
|
_device = device;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int32_t)value
|
||||||
|
{
|
||||||
|
[self doesNotRecognizeSelector:_cmd];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSData *)dataValue
|
||||||
|
{
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IOReturn)setValue:(uint32_t)value
|
||||||
|
{
|
||||||
|
[self doesNotRecognizeSelector:_cmd];
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IOReturn)setDataValue:(NSData *)value
|
||||||
|
{
|
||||||
|
|
||||||
|
[self updateValue:value];
|
||||||
|
return IOHIDDeviceSetReport(_device, kIOHIDReportTypeOutput, _reportID, [_data bytes], [_data length]);;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateValue:(NSData *)value
|
||||||
|
{
|
||||||
|
_data = [value copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For use as a dictionary key */
|
||||||
|
|
||||||
|
- (NSUInteger)hash
|
||||||
|
{
|
||||||
|
return self.uniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isEqual:(id)object
|
||||||
|
{
|
||||||
|
return self.uniqueID == self.uniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)copyWithZone:(nullable NSZone *)zone;
|
||||||
|
{
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
@end
|
11
JoyKit/JOYHat.h
Normal file
11
JoyKit/JOYHat.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@interface JOYHat : NSObject
|
||||||
|
- (uint64_t)uniqueID;
|
||||||
|
- (double)angle;
|
||||||
|
- (unsigned)resolution;
|
||||||
|
@property (readonly, getter=isPressed) bool pressed;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
60
JoyKit/JOYHat.m
Normal file
60
JoyKit/JOYHat.m
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#import "JOYHat.h"
|
||||||
|
#import "JOYElement.h"
|
||||||
|
|
||||||
|
@implementation JOYHat
|
||||||
|
{
|
||||||
|
JOYElement *_element;
|
||||||
|
double _state;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (uint64_t)uniqueID
|
||||||
|
{
|
||||||
|
return _element.uniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)description
|
||||||
|
{
|
||||||
|
if (self.isPressed) {
|
||||||
|
return [NSString stringWithFormat:@"<%@: %p (%llu); State: %f degrees>", self.className, self, self.uniqueID, self.angle];
|
||||||
|
}
|
||||||
|
return [NSString stringWithFormat:@"<%@: %p (%llu); State: released>", self.className, self, self.uniqueID];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithElement:(JOYElement *)element
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (!self) return self;
|
||||||
|
|
||||||
|
_element = element;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)isPressed
|
||||||
|
{
|
||||||
|
return _state >= 0 && _state < 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (double)angle
|
||||||
|
{
|
||||||
|
if (self.isPressed) return fmod((_state + 270), 360);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (unsigned)resolution
|
||||||
|
{
|
||||||
|
return _element.max - _element.min + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)updateState
|
||||||
|
{
|
||||||
|
unsigned state = ([_element value] - _element.min) * 360.0 / self.resolution;
|
||||||
|
if (_state != state) {
|
||||||
|
_state = state;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
8
JoyKit/JOYMultiplayerController.h
Normal file
8
JoyKit/JOYMultiplayerController.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#import "JOYController.h"
|
||||||
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
|
||||||
|
@interface JOYMultiplayerController : JOYController
|
||||||
|
- (instancetype)initWithDevice:(IOHIDDeviceRef) device reportIDFilters:(NSArray <NSArray <NSNumber *> *>*) reportIDFilters hacks:hacks;
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
50
JoyKit/JOYMultiplayerController.m
Normal file
50
JoyKit/JOYMultiplayerController.m
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#import "JOYMultiplayerController.h"
|
||||||
|
|
||||||
|
@interface JOYController ()
|
||||||
|
- (instancetype)initWithDevice:(IOHIDDeviceRef)device reportIDFilter:(NSArray <NSNumber *> *) filter serialSuffix:(NSString *)suffix hacks:(NSDictionary *)hacks;
|
||||||
|
- (void)elementChanged:(IOHIDElementRef) element toValue:(IOHIDValueRef) value;
|
||||||
|
- (void)disconnected;
|
||||||
|
- (void)sendReport:(NSData *)report;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation JOYMultiplayerController
|
||||||
|
{
|
||||||
|
NSMutableArray <JOYController *> *_children;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithDevice:(IOHIDDeviceRef) device reportIDFilters:(NSArray <NSArray <NSNumber *> *>*) reportIDFilters hacks:(NSDictionary *)hacks;
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (!self) return self;
|
||||||
|
|
||||||
|
_children = [NSMutableArray array];
|
||||||
|
|
||||||
|
unsigned index = 1;
|
||||||
|
for (NSArray *filter in reportIDFilters) {
|
||||||
|
JOYController *controller = [[JOYController alloc] initWithDevice:device reportIDFilter:filter serialSuffix:[NSString stringWithFormat:@"%d", index] hacks:hacks];
|
||||||
|
[_children addObject:controller];
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)elementChanged:(IOHIDElementRef) element toValue:(IOHIDValueRef) value
|
||||||
|
{
|
||||||
|
for (JOYController *child in _children) {
|
||||||
|
[child elementChanged:element toValue:value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)disconnected
|
||||||
|
{
|
||||||
|
for (JOYController *child in _children) {
|
||||||
|
[child disconnected];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sendReport:(NSData *)report
|
||||||
|
{
|
||||||
|
[[_children firstObject] sendReport:report];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user