Merge branch 'master' into bsnes_integration

This commit is contained in:
Lior Halphon 2019-07-19 15:51:10 +03:00
commit ea7dec4e88
10 changed files with 432 additions and 115 deletions

View File

@ -532,7 +532,7 @@ TrademarkSymbol:
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
SameBoyLogo:
incbin "SameBoyLogo.rle"
incbin "SameBoyLogo.pb8"
AnimationColors:
dw $7FFF ; White
@ -634,41 +634,55 @@ ReadCGBLogoHalfTile:
ld a, e
ret
; LoadTileset using PB8 codec, 2019 Damian Yerrick
;
; The logo is compressed using PB8, a form of RLE with unary-coded
; run lengths. Each block representing 8 bytes consists of a control
; byte, where each bit (MSB to LSB) is 0 for literal or 1 for repeat
; previous, followed by the literals in that block.
SameBoyLogo_dst = $8080
SameBoyLogo_length = (128 * 24) / 64
LoadTileset:
; Copy SameBoy Logo
ld de, SameBoyLogo
ld hl, $8080
.sameboyLogoLoop
ld a, [de]
inc de
ld b, a
and $0f
jr z, .skipLiteral
ld c, a
.literalLoop
ld a, [de]
ldi [hl], a
ld hl, SameBoyLogo
ld de, SameBoyLogo_dst
ld c, SameBoyLogo_length
.pb8BlockLoop:
; Register map for PB8 decompression
; HL: source address in boot ROM
; DE: destination address in VRAM
; A: Current literal value
; B: Repeat bits, terminated by 1000...
; C: Number of 8-byte blocks left in this block
; Source address in HL lets the repeat bits go straight to B,
; bypassing A and avoiding spilling registers to the stack.
ld b, [hl]
inc hl
inc de
dec c
jr nz, .literalLoop
.skipLiteral
swap b
ld a, b
and $0f
jr z, .sameboyLogoEnd
ld c, a
ld a, [de]
inc de
.repeatLoop
ldi [hl], a
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
.pb8BitLoop:
; If not a repeat, load a literal byte
jr c,.pb8Repeat
ld a, [hli]
.pb8Repeat:
; Decompressed data uses colors 0 and 1, so write once, inc twice
ld [de], a
inc de
inc de
sla b
jr nz, .pb8BitLoop
dec c
jr nz, .repeatLoop
jr .sameboyLogoLoop
jr nz, .pb8BlockLoop
; End PB8 decoding. The rest uses HL as the destination
ld h, d
ld l, e
.sameboyLogoEnd
; Copy (unresized) ROM logo

View File

@ -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);
}

330
BootROMs/pb8.c Normal file
View File

@ -0,0 +1,330 @@
/*
PB8 compressor and decompressor
Copyright 2019 Damian Yerrick
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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
// For setting stdin/stdout to binary mode
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include <unistd.h>
#define fd_isatty isatty
#elif defined (_WIN32)
#include <io.h>
#include <fcntl.h>
#define fd_isatty _isatty
#endif
/*
; The logo is compressed using PB8, a form of RLE with unary-coded
; run lengths. Each block representing 8 bytes consists of a control
; byte, where each bit (MSB to LSB) is 0 for literal or 1 for repeat
; previous, followed by the literals in that block.
SameBoyLogo_dst = $8080
SameBoyLogo_length = (128 * 24) / 64
LoadTileset:
ld hl, SameBoyLogo
ld de, SameBoyLogo_dst
ld c, SameBoyLogo_length
.pb8BlockLoop:
; Register map for PB8 decompression
; HL: source address in boot ROM
; DE: destination address in VRAM
; A: Current literal value
; B: Repeat bits, terminated by 1000...
; C: Number of 8-byte blocks left in this block
; Source address in HL lets the repeat bits go straight to B,
; bypassing A and avoiding spilling registers to the stack.
ld b, [hl]
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
.pb8BitLoop:
; If not a repeat, load a literal byte
jr c,.pb8Repeat
ld a, [hli]
.pb8Repeat:
; Decompressed data uses colors 0 and 1, so write once, inc twice
ld [de], a
inc de
inc de
sla b
jr nz, .pb8BitLoop
dec c
jr nz, .pb8BlockLoop
ret
*/
/* Compressor and decompressor *************************************/
/**
* Compresses an input stream to PB8 data on an output stream.
* @param infp input stream
* @param outfp output stream
* @param blocklength size of an independent input block in bytes
* @return 0 for reaching infp end of file, or EOF for error
*/
int pb8(FILE *infp, FILE *outfp, size_t blocklength) {
blocklength >>= 3; // convert bytes to blocks
assert(blocklength > 0);
while (1) {
int last_byte = EOF; // value that never occurs in a file
for (size_t blkleft = blocklength; blkleft > 0; --blkleft) {
unsigned int control_byte = 0x0001;
unsigned char literals[8];
size_t nliterals = 0;
while (control_byte < 0x100) {
int c = fgetc(infp);
if (c == EOF) break;
control_byte <<= 1;
if (c == last_byte) {
control_byte |= 0x01;
} else {
literals[nliterals++] = last_byte = c;
}
}
if (control_byte > 1) {
// Fill partial block with repeats
while (control_byte < 0x100) {
control_byte = (control_byte << 1) | 1;
}
// Write control byte and check for write failure
int ok = fputc(control_byte & 0xFF, outfp);
if (ok == EOF) return EOF;
size_t ok2 = fwrite(literals, 1, nliterals, outfp);
if (ok2 < nliterals) return EOF;
}
// If finished, return success or failure
if (ferror(infp) || ferror(outfp)) return EOF;
if (feof(infp)) return 0;
} // End 8-byte block
} // End packet, resetting last_byte
}
/**
* Decompresses PB8 data on an input stream to an output stream.
* @param infp input stream
* @param outfp output stream
* @return 0 for reaching infp end of file, or EOF for error
*/
int unpb8(FILE *infp, FILE *outfp) {
int last_byte = 0;
while (1) {
int control_byte = fgetc(infp);
if (control_byte == EOF) {
return feof(infp) ? 0 : EOF;
}
control_byte &= 0xFF;
for (size_t bytesleft = 8; bytesleft > 0; --bytesleft) {
if (!(control_byte & 0x80)) {
last_byte = fgetc(infp);
if (last_byte == EOF) return EOF; // read error
}
control_byte <<= 1;
int ok = fputc(last_byte, outfp);
if (ok == EOF) return EOF;
}
}
}
/* CLI frontend ****************************************************/
static inline void set_fd_binary(unsigned int fd) {
#ifdef _WIN32
_setmode(fd, _O_BINARY);
#else
(void) fd;
#endif
}
static const char *usage_msg =
"usage: pb8 [-d] [-l blocklength] [infile [outfile]]\n"
"Compresses a file using RLE with unary run and literal lengths.\n"
"\n"
"options:\n"
" -d decompress\n"
" -l blocklength allow RLE packets to span up to blocklength\n"
" input bytes (multiple of 8; default 8)\n"
" -h, -?, --help show this usage page\n"
" --version show copyright info\n"
"\n"
"If infile is - or missing, it is standard input.\n"
"If outfile is - or missing, it is standard output.\n"
"You cannot compress to or decompress from a terminal.\n"
;
static const char *version_msg =
"PB8 compressor (C version) v0.01\n"
"Copyright 2019 Damian Yerrick <https://pineight.com/contact/>\n"
"This software is provided 'as-is', without any express or implied\n"
"warranty.\n"
;
static const char *toomanyfilenames_msg =
"pb8: too many filenames; try pb8 --help\n";
int main(int argc, char **argv) {
const char *infilename = NULL;
const char *outfilename = NULL;
bool decompress = false;
size_t blocklength = 8;
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '-' && argv[i][1] != 0) {
if (!strcmp(argv[i], "--help")) {
fputs(usage_msg, stdout);
return 0;
}
if (!strcmp(argv[i], "--version")) {
fputs(version_msg, stdout);
return 0;
}
// -t1 or -t 1
int argtype = argv[i][1];
switch (argtype) {
case 'h':
case '?':
fputs(usage_msg, stdout);
return 0;
case 'd':
decompress = true;
break;
case 'l': {
const char *argvalue = argv[i][2] ? argv[i] + 2 : argv[++i];
const char *endptr = NULL;
unsigned long tvalue = strtoul(argvalue, (char **)&endptr, 10);
if (endptr == argvalue || tvalue == 0 || tvalue > SIZE_MAX) {
fprintf(stderr, "pb8: block length %s not a positive integer\n",
argvalue);
return EXIT_FAILURE;
}
if (tvalue % 8 != 0) {
fprintf(stderr, "pb8: block length %s not a multiple of 8\n",
argvalue);
return EXIT_FAILURE;
}
blocklength = tvalue;
} break;
default:
fprintf(stderr, "pb8: unknown option -%c\n", argtype);
return EXIT_FAILURE;
}
} else if (!infilename) {
infilename = argv[i];
} else if (!outfilename) {
outfilename = argv[i];
} else {
fputs(toomanyfilenames_msg, stderr);
return EXIT_FAILURE;
}
}
if (infilename && !strcmp(infilename, "-")) {
infilename = NULL;
}
if (!infilename && decompress && fd_isatty(0)) {
fputs("pb8: cannot decompress from terminal; try redirecting stdin\n",
stderr);
return EXIT_FAILURE;
}
if (outfilename && !strcmp(outfilename, "-")) {
outfilename = NULL;
}
if (!outfilename && !decompress && fd_isatty(1)) {
fputs("pb8: cannot compress to terminal; try redirecting stdout or pb8 --help\n",
stderr);
return EXIT_FAILURE;
}
FILE *infp = NULL;
if (infilename) {
infp = fopen(infilename, "rb");
if (!infp) {
fprintf(stderr, "pb8: error opening %s ", infilename);
perror("for reading");
return EXIT_FAILURE;
}
} else {
infp = stdin;
set_fd_binary(0);
}
FILE *outfp = NULL;
if (outfilename) {
outfp = fopen(outfilename, "wb");
if (!outfp) {
fprintf(stderr, "pb8: error opening %s ", outfilename);
perror("for writing");
fclose(infp);
return EXIT_FAILURE;
}
} else {
outfp = stdout;
set_fd_binary(1);
}
int compfailed = 0;
int has_ferror = 0;
if (decompress) {
compfailed = unpb8(infp, outfp);
} else {
compfailed = pb8(infp, outfp, blocklength);
}
fflush(outfp);
if (ferror(infp)) {
fprintf(stderr, "pb8: error reading %s\n",
infilename ? infilename : "<stdin>");
has_ferror = EOF;
}
fclose(infp);
if (ferror(outfp)) {
fprintf(stderr, "pb8: error writing %s\n",
outfilename ? outfilename : "<stdout>");
has_ferror = EOF;
}
fclose(outfp);
if (compfailed && !has_ferror) {
fputs("pb8: unknown compression failure\n", stderr);
}
return (compfailed || has_ferror) ? EXIT_FAILURE : EXIT_SUCCESS;
}

View File

@ -389,6 +389,7 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
- (void)dealloc
{
[cameraSession stopRunning];
self.view.gb = NULL;
GB_free(&gb);
if (cameraImage) {
CVBufferRelease(cameraImage);

View File

@ -131,6 +131,7 @@ static const vector_float2 rect[] =
- (void)drawInMTKView:(nonnull MTKView *)view
{
if (!(view.window.occlusionState & NSWindowOcclusionStateVisible)) return;
if (!self.gb) return;
if (texture.width != GB_get_screen_width(self.gb) ||
texture.height != GB_get_screen_height(self.gb)) {
[self allocateTextures];

View File

@ -33,6 +33,7 @@ static const GB_conflict_t cgb_conflict_map[0x80] = {
/* Todo: most values not verified, and probably differ between revisions */
};
/* Todo: verify on an MGB */
static const GB_conflict_t dmg_conflict_map[0x80] = {
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
[GB_IO_LYC] = GB_CONFLICT_READ_OLD,
@ -40,7 +41,6 @@ static const GB_conflict_t dmg_conflict_map[0x80] = {
[GB_IO_SCY] = GB_CONFLICT_READ_NEW,
[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_OBP0] = GB_CONFLICT_PALETTE_DMG,
[GB_IO_OBP1] = GB_CONFLICT_PALETTE_DMG,
@ -51,6 +51,24 @@ static const GB_conflict_t dmg_conflict_map[0x80] = {
[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_READ_NEW,
[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,
/* 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,
};
static uint8_t cycle_read(GB_gameboy_t *gb, uint16_t addr)
{
if (gb->pending_cycles) {
@ -92,7 +110,17 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
assert(gb->pending_cycles);
GB_conflict_t conflict = GB_CONFLICT_READ_OLD;
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) {
case GB_CONFLICT_READ_OLD:

View File

@ -13,13 +13,15 @@ ifneq ($(findstring MSYS,$(PLATFORM)),)
PLATFORM := windows32
endif
LOGO_COMPRESS := build/logo-compress
ifeq ($(PLATFORM),windows32)
_ := $(shell chcp 65001)
LOGO_COMPRESS := build/logo-compress.exe
EXESUFFIX:=.exe
else
EXESUFFIX:=
endif
PB8_COMPRESS := build/pb8$(EXESUFFIX)
ifeq ($(PLATFORM),Darwin)
DEFAULT := cocoa
else
@ -79,7 +81,7 @@ OPEN_DIALOG = OpenDialog/cocoa.m
endif
CFLAGS += -Werror -Wall -Wno-strict-aliasing -Wno-unknown-warning -Wno-unknown-warning-option -Wno-multichar -Wno-int-in-bool-context -std=gnu11 -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES
CFLAGS += -Werror -Wall -Wno-unused-result -Wno-strict-aliasing -Wno-unknown-warning -Wno-unknown-warning-option -Wno-multichar -Wno-int-in-bool-context -std=gnu11 -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES
SDL_LDFLAGS := -lSDL2 -lGL
ifeq ($(PLATFORM),windows32)
CFLAGS += -IWindows -Drandom=rand
@ -302,11 +304,11 @@ $(BIN)/tester/sameboy_tester.exe: $(CORE_OBJECTS) $(SDL_OBJECTS)
$(BIN)/SDL/%.bin $(BIN)/tester/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
$(BIN)/SameBoy.app/Contents/Resources/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
$(BIN)/SDL/LICENSE: LICENSE
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
@ -314,7 +316,7 @@ $(BIN)/SDL/LICENSE: LICENSE
$(BIN)/SDL/registers.sym: Misc/registers.sym
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
$(BIN)/SDL/background.bmp: SDL/background.bmp
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
@ -329,17 +331,17 @@ $(OBJ)/%.1bpp: %.png
-@$(MKDIR) -p $(dir $@)
rgbgfx -d 1 -h -o $@ $<
$(OBJ)/BootROMs/SameBoyLogo.rle: $(OBJ)/BootROMs/SameBoyLogo.1bpp $(LOGO_COMPRESS)
$(realpath $(LOGO_COMPRESS)) < $< > $@
$(OBJ)/BootROMs/SameBoyLogo.pb8: $(OBJ)/BootROMs/SameBoyLogo.1bpp $(PB8_COMPRESS)
$(realpath $(PB8_COMPRESS)) -l 384 $< $@
$(LOGO_COMPRESS): BootROMs/logo-compress.c
$(PB8_COMPRESS): BootROMs/pb8.c
$(CC) $< -o $@
$(BIN)/BootROMs/agb_boot.bin: BootROMs/cgb_boot.asm
$(BIN)/BootROMs/cgb_boot_fast.bin: BootROMs/cgb_boot.asm
$(BIN)/BootROMs/sgb2_boot: BootROMs/sgb_boot.asm
$(BIN)/BootROMs/%.bin: BootROMs/%.asm $(OBJ)/BootROMs/SameBoyLogo.rle
$(BIN)/BootROMs/%.bin: BootROMs/%.asm $(OBJ)/BootROMs/SameBoyLogo.pb8
-@$(MKDIR) -p $(dir $@)
rgbasm -i $(OBJ)/BootROMs/ -i BootROMs/ -o $@.tmp $<
rgblink -o $@.tmp2 $@.tmp
@ -349,7 +351,7 @@ $(BIN)/BootROMs/%.bin: BootROMs/%.asm $(OBJ)/BootROMs/SameBoyLogo.rle
# Libretro Core (uses its own build system)
libretro:
$(MAKE) -C libretro
# Clean
clean:
rm -rf build

View File

@ -23,6 +23,11 @@
#define AUDIO_FREQUENCY 48000
#endif
/* Compatibility with older SDL versions */
#ifndef SDL_AUDIO_ALLOW_SAMPLES_CHANGE
#define SDL_AUDIO_ALLOW_SAMPLES_CHANGE 0
#endif
GB_gameboy_t gb;
static bool paused = false;
static uint32_t pixel_buffer_1[256 * 224], pixel_buffer_2[256 * 224];
@ -464,7 +469,7 @@ restart:
start_capturing_logs();
const char * const boot_roms[] = {"dmg_boot.bin", "cgb_boot.bin", "agb_boot.bin", "sgb_boot.bin"};
const char *boot_rom = boot_roms[configuration.model];
if (configuration.model == GB_MODEL_SGB && configuration.sgb_revision == SGB_2) {
if (configuration.model == MODEL_SGB && configuration.sgb_revision == SGB_2) {
boot_rom = "sgb2_boot.bin";
}
error = GB_load_boot_rom(&gb, resource_path(boot_rom));

View File

@ -1,2 +1 @@
APP_STL := gnustl_static
APP_ABI := all

View File

@ -84,7 +84,7 @@ static struct retro_log_callback logging;
static retro_log_printf_t log_cb;
static retro_video_refresh_t video_cb;
static retro_audio_sample_batch_t audio_batch_cb;
static retro_audio_sample_t audio_sample_cb;
static retro_input_poll_t input_poll_cb;
static retro_input_state_t input_state_cb;
@ -152,7 +152,7 @@ static void audio_callback(GB_gameboy_t *gb, GB_sample_t *sample)
{
if ((audio_out == GB_1 && gb == &gameboy[0]) ||
(audio_out == GB_2 && gb == &gameboy[1])) {
audio_batch_cb((void*)sample, 1);
audio_sample_cb(sample->left, sample->right);
}
}
@ -772,11 +772,11 @@ void retro_set_environment(retro_environment_t cb)
void retro_set_audio_sample(retro_audio_sample_t cb)
{
audio_sample_cb = cb;
}
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
{
audio_batch_cb = cb;
}
void retro_set_input_poll(retro_input_poll_t cb)
@ -850,8 +850,7 @@ void retro_run(void)
}
else
{
int x = GB_run_frame(&gameboy[0]);
log_cb(RETRO_LOG_DEBUG, "%d\n", x);
GB_run_frame(&gameboy[0]);
}
if (emulated_devices == 2)