pizza boy at c7bc6ee376028b3766de8d7a02e60ab794841f45
This commit is contained in:
parent
c33271e534
commit
cbbc922cf7
Binary file not shown.
|
@ -0,0 +1,18 @@
|
|||
CFLAGS=-O3
|
||||
ifeq ($(OS),Windows_NT)
|
||||
LIBS=-lrt `sdl2-config --libs`
|
||||
CFLAGS+=-w
|
||||
else
|
||||
LIBS=-lrt -lSDL2 -pthread
|
||||
endif
|
||||
|
||||
all: libpizza.a
|
||||
gcc $(CFLAGS) pizza.c -I lib lib/libpizza.a -o emu-pizza $(LIBS)
|
||||
|
||||
libpizza.a:
|
||||
make -C lib
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
make -C cpu clean
|
||||
make -C system clean
|
|
@ -0,0 +1,49 @@
|
|||
# Emu-pizza
|
||||
A new born Gameboy Classic/Color emulator....
|
||||
|
||||
Requirements
|
||||
-----------
|
||||
Emu-pizza requires libSDL2 to compile and run Space Invaders and Gameboy games. To install it
|
||||
|
||||
on an APT based distro:
|
||||
```
|
||||
sudo apt-get install libsdl2-dev
|
||||
```
|
||||
|
||||
on a YUM based distro:
|
||||
```
|
||||
sudo yum install SDL2-devel
|
||||
```
|
||||
|
||||
Compile
|
||||
-------
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
```
|
||||
emu-pizza [gameboy rom]
|
||||
```
|
||||
|
||||
Gameboy keys
|
||||
-------------------
|
||||
* Arrows -- Arrows (rly?)
|
||||
* Enter -- Start
|
||||
* Space -- Select
|
||||
* Z/X -- A/B buttons
|
||||
* Q -- Exit
|
||||
|
||||
Supported ROMS
|
||||
--------------
|
||||
* Almost totality of Gameboy roms
|
||||
|
||||
Todo
|
||||
----
|
||||
* Serial cable emulation
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
Thanks to [Emulator 101](http://www.emulator101.com), the source of all my current knowledge on 8080 emulation
|
|
@ -0,0 +1,9 @@
|
|||
SRCS=$(wildcard *.c)
|
||||
CFLAGS=-I.. -c -pthread -O3 -Wall
|
||||
|
||||
all:
|
||||
gcc $(CFLAGS) $(SRCS)
|
||||
ar rcs libpizza.a *.o
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "global.h"
|
||||
#include "mmu.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* buffer big enough to contain the largest possible ROM */
|
||||
uint8_t rom[2 << 24];
|
||||
|
||||
/* battery backed RAM & RTC*/
|
||||
char file_sav[1024];
|
||||
char file_rtc[1024];
|
||||
|
||||
/* internal use prototype */
|
||||
int __mkdirp (char *path, mode_t omode);
|
||||
|
||||
|
||||
/* guess what */
|
||||
/* return values */
|
||||
/* 0: OK */
|
||||
/* 1: Can't open/read file */
|
||||
/* 2: Unknown cartridge */
|
||||
|
||||
char cartridge_load(char *file_gb)
|
||||
{
|
||||
FILE *fp;
|
||||
int i,z = 0;
|
||||
|
||||
/* open ROM file */
|
||||
if ((fp = fopen(file_gb, "r")) == NULL)
|
||||
return 1;
|
||||
|
||||
/* read all the content into rom buffer */
|
||||
size_t sz = fread(rom, 1, (2 << 24), fp);
|
||||
|
||||
/* check for errors */
|
||||
if (sz < 1)
|
||||
return 1;
|
||||
|
||||
/* close */
|
||||
fclose(fp);
|
||||
|
||||
/* gameboy color? */
|
||||
if (rom[0x143] == 0xC0 || rom[0x143] == 0x80)
|
||||
{
|
||||
utils_log("Gameboy Color cartridge\n");
|
||||
global_cgb = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
utils_log("Gameboy Classic cartridge\n");
|
||||
global_cgb = 0;
|
||||
}
|
||||
|
||||
/* get cartridge infos */
|
||||
uint8_t mbc = rom[0x147];
|
||||
|
||||
utils_log("Cartridge code: %02x\n", mbc);
|
||||
|
||||
switch (mbc)
|
||||
{
|
||||
case 0x00: utils_log("ROM ONLY\n"); break;
|
||||
case 0x01: utils_log("MBC1\n"); break;
|
||||
case 0x02: utils_log("MBC1 + RAM\n"); break;
|
||||
case 0x03: utils_log("MBC1 + RAM + BATTERY\n"); break;
|
||||
case 0x05: utils_log("MBC2\n"); break;
|
||||
case 0x06: mmu_init_ram(512); utils_log("MBC2 + BATTERY\n"); break;
|
||||
case 0x10: utils_log("MBC3 + TIMER + RAM + BATTERY\n"); break;
|
||||
case 0x11: utils_log("MBC3\n"); break;
|
||||
case 0x12: utils_log("MBC3 + RAM\n"); break;
|
||||
case 0x13: utils_log("MBC3 + RAM + BATTERY\n"); break;
|
||||
case 0x19: utils_log("MBC5\n"); break;
|
||||
case 0x1A: utils_log("MBC5 + RAM\n"); break;
|
||||
case 0x1B: utils_log("MBC5 + RAM + BATTERY\n"); break;
|
||||
case 0x1C: global_rumble = 1;
|
||||
utils_log("MBC5 + RUMBLE\n");
|
||||
break;
|
||||
case 0x1D: global_rumble = 1;
|
||||
utils_log("MBC5 + RUMBLE + RAM\n");
|
||||
break;
|
||||
case 0x1E: global_rumble = 1;
|
||||
utils_log("MBC5 + RUMBLE + RAM + BATTERY\n");
|
||||
break;
|
||||
|
||||
default: utils_log("Unknown cartridge type: %02x\n", mbc);
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* title */
|
||||
for (i=0x134; i<0x143; i++)
|
||||
if (rom[i] > 0x40 && rom[i] < 0x5B)
|
||||
global_cart_name[z++] = rom[i];
|
||||
|
||||
global_cart_name[z] = '\0';
|
||||
|
||||
utils_log("%s\n", global_cart_name);
|
||||
|
||||
/* get ROM banks */
|
||||
uint8_t byte = rom[0x148];
|
||||
|
||||
utils_log("ROM: ");
|
||||
|
||||
switch (byte)
|
||||
{
|
||||
case 0x00: utils_log("0 banks\n"); break;
|
||||
case 0x01: utils_log("4 banks\n"); break;
|
||||
case 0x02: utils_log("8 banks\n"); break;
|
||||
case 0x03: utils_log("16 banks\n"); break;
|
||||
case 0x04: utils_log("32 banks\n"); break;
|
||||
case 0x05: utils_log("64 banks\n"); break;
|
||||
case 0x06: utils_log("128 banks\n"); break;
|
||||
case 0x07: utils_log("256 banks\n"); break;
|
||||
case 0x52: utils_log("72 banks\n"); break;
|
||||
case 0x53: utils_log("80 banks\n"); break;
|
||||
case 0x54: utils_log("96 banks\n"); break;
|
||||
}
|
||||
|
||||
/* init MMU */
|
||||
mmu_init(mbc, byte);
|
||||
|
||||
/* get RAM banks */
|
||||
byte = rom[0x149];
|
||||
|
||||
utils_log("RAM: ");
|
||||
|
||||
switch (byte)
|
||||
{
|
||||
case 0x00: utils_log("NO RAM\n"); break;
|
||||
case 0x01: mmu_init_ram(1 << 11); utils_log("2 kB\n"); break;
|
||||
case 0x02:
|
||||
/* MBC5 got bigger values */
|
||||
if (mbc >= 0x19 && mbc <= 0x1E)
|
||||
{
|
||||
mmu_init_ram(1 << 16);
|
||||
utils_log("64 kB\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
mmu_init_ram(1 << 13);
|
||||
utils_log("8 kB\n");
|
||||
}
|
||||
break;
|
||||
case 0x03: mmu_init_ram(1 << 15); utils_log("32 kB\n"); break;
|
||||
case 0x04: mmu_init_ram(1 << 17); utils_log("128 kB\n"); break;
|
||||
case 0x05: mmu_init_ram(1 << 16); utils_log("64 kB\n"); break;
|
||||
}
|
||||
|
||||
/* save base name of the rom */
|
||||
strncpy(global_rom_name, basename(file_gb), 256);
|
||||
|
||||
/* build file.sav */
|
||||
snprintf(file_sav, sizeof(file_sav), "%s/%s.sav",
|
||||
global_save_folder, global_rom_name);
|
||||
|
||||
/* build file.rtc */
|
||||
snprintf(file_rtc, sizeof(file_rtc), "%s/%s.rtc",
|
||||
global_save_folder, global_rom_name);
|
||||
|
||||
/* restore saved RAM if it's the case */
|
||||
mmu_restore_ram(file_sav);
|
||||
|
||||
/* restore saved RTC if it's the case */
|
||||
mmu_restore_rtc(file_rtc);
|
||||
|
||||
/* load FULL ROM at 0x0000 address of system memory */
|
||||
mmu_load_cartridge(rom, sz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cartridge_term()
|
||||
{
|
||||
/* save persistent data (battery backed RAM and RTC clock) */
|
||||
mmu_save_ram(file_sav);
|
||||
mmu_save_rtc(file_rtc);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __CARTRIDGE_HDR__
|
||||
#define __CARTRIDGE_HDR__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* prototypes */
|
||||
char cartridge_load(char *file_nm);
|
||||
void cartridge_term();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "cycles.h"
|
||||
#include "global.h"
|
||||
#include "gpu.h"
|
||||
#include "mmu.h"
|
||||
#include "serial.h"
|
||||
#include "sound.h"
|
||||
#include "timer.h"
|
||||
#include "interrupt.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* timer stuff */
|
||||
struct itimerspec cycles_timer;
|
||||
timer_t cycles_timer_id = 0;
|
||||
struct sigevent cycles_te;
|
||||
struct sigaction cycles_sa;
|
||||
|
||||
interrupts_flags_t *cycles_if;
|
||||
|
||||
/* instance of the main struct */
|
||||
cycles_t cycles = { 0, 0, 0, 0 };
|
||||
|
||||
#define CYCLES_PAUSES 256
|
||||
|
||||
/* sync timing */
|
||||
struct timespec deadline;
|
||||
|
||||
/* hard sync stuff (for remote connection) */
|
||||
uint8_t cycles_hs_mode = 0;
|
||||
|
||||
/* type of next */
|
||||
typedef enum
|
||||
{
|
||||
CYCLES_NEXT_TYPE_CYCLES,
|
||||
CYCLES_NEXT_TYPE_CYCLES_HS,
|
||||
CYCLES_NEXT_TYPE_DMA,
|
||||
} cycles_next_type_enum_e;
|
||||
|
||||
/* closest next and its type */
|
||||
uint_fast32_t cycles_very_next;
|
||||
cycles_next_type_enum_e cycles_next_type;
|
||||
|
||||
/* set hard sync mode. sync is given by the remote peer + local timer */
|
||||
void cycles_start_hs()
|
||||
{
|
||||
utils_log("Hard sync mode ON\n");
|
||||
|
||||
/* boolean set to on */
|
||||
cycles_hs_mode = 1;
|
||||
}
|
||||
|
||||
void cycles_stop_hs()
|
||||
{
|
||||
utils_log("Hard sync mode OFF\n");
|
||||
|
||||
/* boolean set to on */
|
||||
cycles_hs_mode = 0;
|
||||
}
|
||||
|
||||
/* set double or normal speed */
|
||||
void cycles_set_speed(char dbl)
|
||||
{
|
||||
/* set global */
|
||||
global_cpu_double_speed = dbl;
|
||||
|
||||
/* update clock */
|
||||
if (global_cpu_double_speed)
|
||||
cycles.clock = 4194304 * 2;
|
||||
else
|
||||
cycles.clock = 4194304;
|
||||
|
||||
/* calculate the mask */
|
||||
cycles_change_emulation_speed();
|
||||
}
|
||||
|
||||
/* set emulation speed */
|
||||
void cycles_change_emulation_speed()
|
||||
{
|
||||
switch (global_emulation_speed)
|
||||
{
|
||||
case GLOBAL_EMULATION_SPEED_QUARTER:
|
||||
cycles.step = ((4194304 / CYCLES_PAUSES) / 4
|
||||
<< global_cpu_double_speed);
|
||||
break;
|
||||
case GLOBAL_EMULATION_SPEED_HALF:
|
||||
cycles.step = ((4194304 / CYCLES_PAUSES) / 2
|
||||
<< global_cpu_double_speed);
|
||||
break;
|
||||
case GLOBAL_EMULATION_SPEED_NORMAL:
|
||||
cycles.step = ((4194304 / CYCLES_PAUSES)
|
||||
<< global_cpu_double_speed);
|
||||
break;
|
||||
case GLOBAL_EMULATION_SPEED_DOUBLE:
|
||||
cycles.step = ((4194304 / CYCLES_PAUSES) * 2
|
||||
<< global_cpu_double_speed);
|
||||
break;
|
||||
case GLOBAL_EMULATION_SPEED_4X:
|
||||
cycles.step = ((4194304 / CYCLES_PAUSES) * 4
|
||||
<< global_cpu_double_speed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void cycles_closest_next()
|
||||
{
|
||||
int_fast32_t diff = cycles.cnt - cycles.next;
|
||||
|
||||
/* init */
|
||||
cycles_very_next = cycles.next;
|
||||
cycles_next_type = CYCLES_NEXT_TYPE_CYCLES;
|
||||
|
||||
int_fast32_t diff_new = cycles.cnt - mmu.dma_next;
|
||||
|
||||
/* DMA? */
|
||||
if (diff_new < diff)
|
||||
{
|
||||
/* this is the new lowest */
|
||||
cycles_very_next = mmu.dma_next;
|
||||
cycles_next_type = CYCLES_NEXT_TYPE_DMA;
|
||||
}
|
||||
}
|
||||
|
||||
/* this function is gonna be called every M-cycle = 4 ticks of CPU */
|
||||
void cycles_step()
|
||||
{
|
||||
cycles.cnt += 4;
|
||||
|
||||
/*
|
||||
while (cycles.cnt >= cycles_very_next)
|
||||
{
|
||||
switch (cycles_next_type)
|
||||
{
|
||||
case CYCLES_NEXT_TYPE_CYCLES:
|
||||
|
||||
deadline.tv_nsec += 1000000000 / CYCLES_PAUSES;
|
||||
|
||||
if (deadline.tv_nsec > 1000000000)
|
||||
{
|
||||
deadline.tv_sec += 1;
|
||||
deadline.tv_nsec -= 1000000000;
|
||||
}
|
||||
|
||||
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
|
||||
&deadline, NULL);
|
||||
|
||||
cycles.next += cycles.step;
|
||||
|
||||
if (cycles.cnt % cycles.clock == 0)
|
||||
cycles.seconds++;
|
||||
|
||||
break;
|
||||
|
||||
case CYCLES_NEXT_TYPE_DMA:
|
||||
|
||||
memcpy(&mmu.memory[0xFE00], &mmu.memory[mmu.dma_address], 160);
|
||||
|
||||
mmu.dma_address = 0x0000;
|
||||
|
||||
mmu.dma_next = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
cycles_closest_next();
|
||||
}
|
||||
*/
|
||||
|
||||
/* 65536 == cpu clock / CYCLES_PAUSES pauses every second */
|
||||
if (cycles.cnt == cycles.next)
|
||||
{
|
||||
deadline.tv_nsec += 1000000000 / CYCLES_PAUSES;
|
||||
|
||||
if (deadline.tv_nsec > 1000000000)
|
||||
{
|
||||
deadline.tv_sec += 1;
|
||||
deadline.tv_nsec -= 1000000000;
|
||||
}
|
||||
|
||||
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, NULL);
|
||||
|
||||
cycles.next += cycles.step;
|
||||
|
||||
/* update current running seconds */
|
||||
if (cycles.cnt % cycles.clock == 0)
|
||||
cycles.seconds++;
|
||||
}
|
||||
|
||||
/* hard sync next step */
|
||||
if (cycles.cnt == cycles.hs_next)
|
||||
{
|
||||
/* set cycles for hard sync */
|
||||
cycles.hs_next += ((4096 * 4) << global_cpu_double_speed);
|
||||
|
||||
/* hard sync is on? */
|
||||
if (cycles_hs_mode)
|
||||
{
|
||||
/* send my status and wait for peer status back */
|
||||
serial_send_byte();
|
||||
|
||||
/* wait for reply */
|
||||
serial_wait_data();
|
||||
|
||||
/* verify if we need to trigger an interrupt */
|
||||
serial_verify_intr();
|
||||
}
|
||||
}
|
||||
|
||||
/* DMA */
|
||||
if (mmu.dma_next == cycles.cnt)
|
||||
{
|
||||
memcpy(&mmu.memory[0xFE00], &mmu.memory[mmu.dma_address], 160);
|
||||
|
||||
/* reset address */
|
||||
mmu.dma_address = 0x0000;
|
||||
|
||||
/* reset */
|
||||
mmu.dma_next = 1;
|
||||
}
|
||||
|
||||
/* update GPU state */
|
||||
if (gpu.next == cycles.cnt)
|
||||
gpu_step();
|
||||
|
||||
/* fs clock */
|
||||
if (sound.fs_cycles_next == cycles.cnt)
|
||||
sound_step_fs();
|
||||
|
||||
/* channel one */
|
||||
if (sound.channel_one.duty_cycles_next == cycles.cnt)
|
||||
sound_step_ch1();
|
||||
|
||||
/* channel two */
|
||||
if (sound.channel_two.duty_cycles_next == cycles.cnt)
|
||||
sound_step_ch2();
|
||||
|
||||
/* channel three */
|
||||
if (sound.channel_three.cycles_next <= cycles.cnt)
|
||||
sound_step_ch3();
|
||||
|
||||
/* channel four */
|
||||
if (sound.channel_four.cycles_next == cycles.cnt)
|
||||
sound_step_ch4();
|
||||
|
||||
/* time to generate a sample? */
|
||||
if (sound.sample_cycles_next_rounded == cycles.cnt)
|
||||
sound_step_sample();
|
||||
|
||||
/* update timer state */
|
||||
if (cycles.cnt == timer.next)
|
||||
{
|
||||
timer.next += 256;
|
||||
timer.div++;
|
||||
}
|
||||
|
||||
/* timer is on? */
|
||||
if (timer.sub_next == cycles.cnt)
|
||||
{
|
||||
timer.sub_next += timer.threshold;
|
||||
timer.cnt++;
|
||||
|
||||
/* cnt value > 255? trigger an interrupt */
|
||||
if (timer.cnt > 255)
|
||||
{
|
||||
timer.cnt = timer.mod;
|
||||
|
||||
/* trigger timer interrupt */
|
||||
cycles_if->timer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* update serial state */
|
||||
if (serial.next == cycles.cnt)
|
||||
{
|
||||
/* nullize serial next */
|
||||
serial.next -= 1;
|
||||
|
||||
/* reset counter */
|
||||
serial.bits_sent = 0;
|
||||
|
||||
/* gotta reply with 0xff when asking for ff01 */
|
||||
serial.data = 0xFF;
|
||||
|
||||
/* reset transfer_start flag to yell I'M DONE */
|
||||
serial.transfer_start = 0;
|
||||
|
||||
/* if not connected, trig the fucking interrupt */
|
||||
cycles_if->serial_io = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* things to do when vsync kicks in */
|
||||
void cycles_vblank()
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/* stuff tied to entering into hblank state */
|
||||
void cycles_hdma()
|
||||
{
|
||||
/* HDMA (only CGB) */
|
||||
if (mmu.hdma_to_transfer)
|
||||
{
|
||||
/* hblank transfer */
|
||||
if (mmu.hdma_transfer_mode)
|
||||
{
|
||||
/* transfer when line is changed and we're into HBLANK phase */
|
||||
if (mmu.memory[0xFF44] < 143 &&
|
||||
mmu.hdma_current_line != mmu.memory[0xFF44] &&
|
||||
(mmu.memory[0xFF41] & 0x03) == 0x00)
|
||||
{
|
||||
/* update current line */
|
||||
mmu.hdma_current_line = mmu.memory[0xFF44];
|
||||
|
||||
/* copy 0x10 bytes */
|
||||
if (mmu.vram_idx)
|
||||
memcpy(mmu_addr_vram1() + mmu.hdma_dst_address - 0x8000,
|
||||
&mmu.memory[mmu.hdma_src_address], 0x10);
|
||||
else
|
||||
memcpy(mmu_addr_vram0() + mmu.hdma_dst_address - 0x8000,
|
||||
&mmu.memory[mmu.hdma_src_address], 0x10);
|
||||
|
||||
/* decrease bytes to transfer */
|
||||
mmu.hdma_to_transfer -= 0x10;
|
||||
|
||||
/* increase pointers */
|
||||
mmu.hdma_dst_address += 0x10;
|
||||
mmu.hdma_src_address += 0x10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char cycles_init()
|
||||
{
|
||||
/* CLOCK */
|
||||
clock_gettime(CLOCK_MONOTONIC, &deadline);
|
||||
|
||||
cycles.inited = 1;
|
||||
|
||||
/* interrupt registers */
|
||||
cycles_if = mmu_addr(0xFF0F);
|
||||
|
||||
/* init clock and counter */
|
||||
cycles.clock = 4194304;
|
||||
cycles.cnt = 0;
|
||||
cycles.hs_next = 70224;
|
||||
|
||||
/* mask for pauses cycles fast calc */
|
||||
cycles.step = 4194304 / CYCLES_PAUSES;
|
||||
cycles.next = 4194304 / CYCLES_PAUSES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char cycles_start_timer()
|
||||
{
|
||||
/* just pick new time reference */
|
||||
clock_gettime(CLOCK_MONOTONIC, &deadline);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cycles_stop_timer()
|
||||
{
|
||||
if (cycles.inited == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
void cycles_term()
|
||||
{
|
||||
}
|
||||
|
||||
void cycles_save_stat(FILE *fp)
|
||||
{
|
||||
fwrite(&cycles, 1, sizeof(cycles_t), fp);
|
||||
}
|
||||
|
||||
void cycles_restore_stat(FILE *fp)
|
||||
{
|
||||
fread(&cycles, 1, sizeof(cycles_t), fp);
|
||||
|
||||
/* recalc speed stuff */
|
||||
cycles_change_emulation_speed();
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __CYCLES_HDR__
|
||||
#define __CYCLES_HDR__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct cycles_s
|
||||
{
|
||||
/* am i init'ed? */
|
||||
uint_fast32_t inited;
|
||||
|
||||
/* ticks counter */
|
||||
uint_fast32_t cnt;
|
||||
|
||||
/* CPU clock */
|
||||
uint_fast32_t clock;
|
||||
|
||||
/* handy for calculation */
|
||||
uint_fast32_t next;
|
||||
|
||||
/* step varying on cpu and emulation speed */
|
||||
uint_fast32_t step;
|
||||
|
||||
/* total running seconds */
|
||||
uint_fast32_t seconds;
|
||||
|
||||
/* 2 spares */
|
||||
uint_fast32_t hs_next;
|
||||
|
||||
|
||||
uint_fast32_t spare2;
|
||||
|
||||
} cycles_t;
|
||||
|
||||
extern cycles_t cycles;
|
||||
|
||||
// extern uint8_t cycles_hs_local_cnt;
|
||||
// extern uint8_t cycles_hs_peer_cnt;
|
||||
|
||||
/* callback function */
|
||||
typedef void (*cycles_send_cb_t) (uint32_t v);
|
||||
|
||||
/* prototypes */
|
||||
void cycles_change_emulation_speed();
|
||||
void cycles_hdma();
|
||||
char cycles_init();
|
||||
void cycles_restore_stat(FILE *fp);
|
||||
void cycles_save_stat(FILE *fp);
|
||||
void cycles_set_speed(char dbl);
|
||||
void cycles_start_hs();
|
||||
char cycles_start_timer();
|
||||
void cycles_step();
|
||||
void cycles_stop_hs();
|
||||
void cycles_stop_timer();
|
||||
void cycles_term();
|
||||
void cycles_vblank();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <semaphore.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include "cartridge.h"
|
||||
#include "sound.h"
|
||||
#include "mmu.h"
|
||||
#include "cycles.h"
|
||||
#include "gpu.h"
|
||||
#include "global.h"
|
||||
#include "input.h"
|
||||
#include "timer.h"
|
||||
#include "serial.h"
|
||||
#include "utils.h"
|
||||
#include "z80_gameboy_regs.h"
|
||||
#include "z80_gameboy.h"
|
||||
|
||||
/* semaphore for pauses */
|
||||
sem_t gameboy_sem;
|
||||
|
||||
char gameboy_inited = 0;
|
||||
|
||||
|
||||
void gameboy_init()
|
||||
{
|
||||
/* init global values */
|
||||
// global_init();
|
||||
|
||||
/* init z80 */
|
||||
z80_init();
|
||||
|
||||
/* init cycles syncronizer */
|
||||
cycles_init();
|
||||
|
||||
/* init input */
|
||||
input_init();
|
||||
|
||||
/* init timer */
|
||||
timer_init();
|
||||
|
||||
/* init serial */
|
||||
serial_init();
|
||||
|
||||
/* init sound (this will start audio thread) */
|
||||
sound_init();
|
||||
|
||||
/* reset GPU counters */
|
||||
gpu_reset();
|
||||
|
||||
/* reset to default values */
|
||||
mmu_write_no_cyc(0xFF05, 0x00);
|
||||
mmu_write_no_cyc(0xFF06, 0x00);
|
||||
mmu_write_no_cyc(0xFF07, 0x00);
|
||||
mmu_write_no_cyc(0xFF10, 0x80);
|
||||
mmu_write_no_cyc(0xFF11, 0xBF);
|
||||
mmu_write_no_cyc(0xFF12, 0xF3);
|
||||
mmu_write_no_cyc(0xFF14, 0xBF);
|
||||
mmu_write_no_cyc(0xFF16, 0x3F);
|
||||
mmu_write_no_cyc(0xFF17, 0x00);
|
||||
mmu_write_no_cyc(0xFF19, 0xBF);
|
||||
mmu_write_no_cyc(0xFF1A, 0x7F);
|
||||
mmu_write_no_cyc(0xFF1B, 0xFF);
|
||||
mmu_write_no_cyc(0xFF1C, 0x9F);
|
||||
mmu_write_no_cyc(0xFF1E, 0xBF);
|
||||
mmu_write_no_cyc(0xFF20, 0xFF);
|
||||
mmu_write_no_cyc(0xFF21, 0x00);
|
||||
mmu_write_no_cyc(0xFF22, 0x00);
|
||||
mmu_write_no_cyc(0xFF23, 0xBF);
|
||||
mmu_write_no_cyc(0xFF24, 0x77);
|
||||
mmu_write_no_cyc(0xFF25, 0xF3);
|
||||
mmu_write_no_cyc(0xFF26, 0xF1);
|
||||
mmu_write_no_cyc(0xFF40, 0x91);
|
||||
mmu_write_no_cyc(0xFF41, 0x80);
|
||||
mmu_write_no_cyc(0xFF42, 0x00);
|
||||
mmu_write_no_cyc(0xFF43, 0x00);
|
||||
mmu_write_no_cyc(0xFF44, 0x00);
|
||||
mmu_write_no_cyc(0xFF45, 0x00);
|
||||
mmu_write_no_cyc(0xFF47, 0xFC);
|
||||
mmu_write_no_cyc(0xFF48, 0xFF);
|
||||
mmu_write_no_cyc(0xFF49, 0xFF);
|
||||
mmu_write_no_cyc(0xFF4A, 0x00);
|
||||
mmu_write_no_cyc(0xFF4B, 0x00);
|
||||
mmu_write_no_cyc(0xFF98, 0xDC);
|
||||
mmu_write_no_cyc(0xFFFF, 0x00);
|
||||
mmu_write_no_cyc(0xC000, 0x08);
|
||||
mmu_write_no_cyc(0xFFFE, 0x69);
|
||||
|
||||
if (global_cgb)
|
||||
state.a = 0x11;
|
||||
else
|
||||
state.a = 0x00;
|
||||
|
||||
state.b = 0x00;
|
||||
state.c = 0x13;
|
||||
state.d = 0x00;
|
||||
state.e = 0xd8;
|
||||
state.h = 0x01;
|
||||
state.l = 0x4d;
|
||||
state.pc = 0x0100;
|
||||
state.sp = 0xFFFE;
|
||||
*state.f = 0xB0;
|
||||
|
||||
/* init semaphore for pauses */
|
||||
sem_init(&gameboy_sem, 0, 0);
|
||||
|
||||
/* mark as inited */
|
||||
gameboy_inited = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void gameboy_set_pause(char pause)
|
||||
{
|
||||
if (!gameboy_inited)
|
||||
return;
|
||||
|
||||
if (pause == global_pause)
|
||||
return;
|
||||
|
||||
global_pause = pause;
|
||||
|
||||
if (pause)
|
||||
{
|
||||
/* wait a bit - i need the main cycle fall into global_pause check */
|
||||
usleep(100000);
|
||||
|
||||
/* stop timer */
|
||||
cycles_stop_timer();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* restart timer */
|
||||
cycles_start_timer();
|
||||
|
||||
/* wake up */
|
||||
sem_post(&gameboy_sem);
|
||||
}
|
||||
}
|
||||
|
||||
void gameboy_run()
|
||||
{
|
||||
uint8_t op;
|
||||
|
||||
/* reset counter */
|
||||
cycles.cnt = 0;
|
||||
|
||||
/* get interrupt flags and interrupt enables */
|
||||
uint8_t *int_e;
|
||||
uint8_t *int_f;
|
||||
|
||||
/* pointers to memory location of interrupt enables/flags */
|
||||
int_e = mmu_addr(0xFFFF);
|
||||
int_f = mmu_addr(0xFF0F);
|
||||
|
||||
/* start at normal speed */
|
||||
global_cpu_double_speed = 0;
|
||||
|
||||
/* run stuff! */
|
||||
/* mechanism is simple. */
|
||||
/* 1) execute instruction 2) update cycles counter 3) check interrupts */
|
||||
/* and repeat forever */
|
||||
while (!global_quit)
|
||||
{
|
||||
/*if (global_slow_down)
|
||||
{
|
||||
usleep(100000);
|
||||
global_slow_down = 0;
|
||||
}*/
|
||||
|
||||
/* pause? */
|
||||
while (global_pause)
|
||||
sem_wait(&gameboy_sem);
|
||||
|
||||
/* get op */
|
||||
op = mmu_read(state.pc);
|
||||
|
||||
/* print out CPU state if enabled by debug flag */
|
||||
if (global_debug)
|
||||
{
|
||||
utils_log("OP: %02x F: %02x PC: %04x:%02x:%02x SP: %04x:%02x:%02x ",
|
||||
op, *state.f & 0xd0, state.pc,
|
||||
mmu_read_no_cyc(state.pc + 1),
|
||||
mmu_read_no_cyc(state.pc + 2), state.sp,
|
||||
mmu_read_no_cyc(state.sp),
|
||||
mmu_read_no_cyc(state.sp + 1));
|
||||
|
||||
|
||||
utils_log("A: %02x BC: %04x DE: %04x HL: %04x FF41: %02x "
|
||||
"FF44: %02x ENAB: %02x INTE: %02x INTF: %02x\n",
|
||||
state.a, *state.bc,
|
||||
*state.de, *state.hl,
|
||||
mmu_read_no_cyc(0xFF41),
|
||||
mmu_read_no_cyc(0xFF44),
|
||||
state.int_enable,
|
||||
*int_e, *int_f);
|
||||
}
|
||||
|
||||
/* execute instruction by the GB Z80 version */
|
||||
z80_execute(op);
|
||||
|
||||
/* if last op was Interrupt Enable (0xFB) */
|
||||
/* we need to check for INTR on next cycle */
|
||||
if (op == 0xFB)
|
||||
continue;
|
||||
|
||||
/* interrupts filtered by enable flags */
|
||||
uint8_t int_r = (*int_f & *int_e);
|
||||
|
||||
/* check for interrupts */
|
||||
if ((state.int_enable || op == 0x76) && (int_r != 0))
|
||||
{
|
||||
/* discard useless bits */
|
||||
if ((int_r & 0x1F) == 0x00)
|
||||
continue;
|
||||
|
||||
/* beware of instruction that doesn't move PC! */
|
||||
/* like HALT (0x76) */
|
||||
if (op == 0x76)
|
||||
{
|
||||
state.pc++;
|
||||
|
||||
if (state.int_enable == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* reset int-enable flag, it will be restored after a RETI op */
|
||||
state.int_enable = 0;
|
||||
|
||||
if ((int_r & 0x01) == 0x01)
|
||||
{
|
||||
/* vblank interrupt triggers RST 5 */
|
||||
|
||||
/* reset flag */
|
||||
*int_f &= 0xFE;
|
||||
|
||||
/* handle the interrupt */
|
||||
z80_intr(0x0040);
|
||||
}
|
||||
else if ((int_r & 0x02) == 0x02)
|
||||
{
|
||||
/* LCD Stat interrupt */
|
||||
|
||||
/* reset flag */
|
||||
*int_f &= 0xFD;
|
||||
|
||||
/* handle the interrupt! */
|
||||
z80_intr(0x0048);
|
||||
}
|
||||
else if ((int_r & 0x04) == 0x04)
|
||||
{
|
||||
/* timer interrupt */
|
||||
|
||||
/* reset flag */
|
||||
*int_f &= 0xFB;
|
||||
|
||||
/* handle the interrupt! */
|
||||
z80_intr(0x0050);
|
||||
}
|
||||
else if ((int_r & 0x08) == 0x08)
|
||||
{
|
||||
/* serial interrupt */
|
||||
|
||||
/* reset flag */
|
||||
*int_f &= 0xF7;
|
||||
|
||||
/* handle the interrupt! */
|
||||
z80_intr(0x0058);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* terminate all the stuff */
|
||||
cartridge_term();
|
||||
sound_term();
|
||||
mmu_term();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void gameboy_stop()
|
||||
{
|
||||
global_quit = 1;
|
||||
|
||||
/* wake up */
|
||||
if (global_pause)
|
||||
{
|
||||
global_pause = 0;
|
||||
sem_post(&gameboy_sem);
|
||||
}
|
||||
|
||||
/* unlock threads stuck during reading */
|
||||
sound_term();
|
||||
|
||||
/* shutdown semaphore limitator */
|
||||
cycles_term();
|
||||
}
|
||||
|
||||
char gameboy_restore_stat(int idx)
|
||||
{
|
||||
char path[256];
|
||||
char buf[6];
|
||||
|
||||
/* ensure i'm in pause */
|
||||
gameboy_set_pause(1);
|
||||
|
||||
/* build output file name */
|
||||
snprintf(path, sizeof(path), "%s/%s.%d.stat", global_save_folder,
|
||||
global_rom_name, idx);
|
||||
|
||||
FILE *fp = fopen(path, "r+");
|
||||
|
||||
if (fp == NULL)
|
||||
{
|
||||
utils_log("Cannot open stat file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* read version */
|
||||
fread(buf, 1, 6, fp);
|
||||
|
||||
if (memcmp(buf, "000001", 6))
|
||||
{
|
||||
utils_log("Version of stat file doesnt match\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* restore CPU status */
|
||||
fread(&state, 1, sizeof(z80_state_t), fp);
|
||||
|
||||
state.f = (uint8_t *) &state.flags;
|
||||
state.bc = (uint16_t *) &state.c;
|
||||
state.de = (uint16_t *) &state.e;
|
||||
state.hl = (uint16_t *) &state.l;
|
||||
|
||||
/* dump every module */
|
||||
cycles_restore_stat(fp);
|
||||
sound_restore_stat(fp);
|
||||
gpu_restore_stat(fp);
|
||||
serial_restore_stat(fp);
|
||||
mmu_restore_stat(fp);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char gameboy_save_stat(int idx)
|
||||
{
|
||||
char path[256];
|
||||
|
||||
/* ensure i'm in pause */
|
||||
gameboy_set_pause(1);
|
||||
|
||||
/* build output file name */
|
||||
snprintf(path, sizeof(path), "%s/%s.%d.stat", global_save_folder,
|
||||
global_rom_name, idx);
|
||||
|
||||
FILE *fp = fopen(path, "w+");
|
||||
|
||||
if (fp == NULL)
|
||||
return 1;
|
||||
|
||||
/* dump current version */
|
||||
fwrite("000001", 1, 6, fp);
|
||||
|
||||
/* dump cpu status */
|
||||
fwrite(&state, 1, sizeof(z80_state_t), fp);
|
||||
|
||||
/* dump every module */
|
||||
cycles_save_stat(fp);
|
||||
sound_save_stat(fp);
|
||||
gpu_save_stat(fp);
|
||||
serial_save_stat(fp);
|
||||
mmu_save_stat(fp);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
/* now dump raw data of frame buffer */
|
||||
snprintf(path, sizeof(path), "%s/%s.%d.fb", global_save_folder,
|
||||
global_rom_name, idx);
|
||||
|
||||
fp = fopen(path, "w+");
|
||||
|
||||
if (fp == NULL)
|
||||
return 1;
|
||||
|
||||
/* dump frame buffer pixels */
|
||||
gpu_save_fb(fp);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __GAMEBOY_HDR__
|
||||
#define __GAMEBOY_HDR__
|
||||
|
||||
/* prototypes */
|
||||
void gameboy_init();
|
||||
void gameboy_run();
|
||||
char gameboy_restore_stat(int idx);
|
||||
char gameboy_save_stat(int idx);
|
||||
void gameboy_set_pause(char pause);
|
||||
void gameboy_stop();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "global.h"
|
||||
|
||||
char global_cart_name[256];
|
||||
char global_cgb;
|
||||
char global_cpu_double_speed;
|
||||
char global_debug;
|
||||
char global_emulation_speed;
|
||||
char global_next_frame;
|
||||
char global_pause;
|
||||
char global_quit;
|
||||
char global_record_audio;
|
||||
char global_rom_name[256];
|
||||
char global_rumble;
|
||||
char global_slow_down;
|
||||
char global_save_folder[256];
|
||||
char global_window;
|
||||
|
||||
void global_init()
|
||||
{
|
||||
global_quit = 0;
|
||||
global_pause = 0;
|
||||
global_window = 1;
|
||||
global_debug = 0;
|
||||
global_cgb = 0;
|
||||
global_cpu_double_speed = 0;
|
||||
global_slow_down = 0;
|
||||
global_record_audio = 0;
|
||||
global_next_frame = 0;
|
||||
global_rumble = 0;
|
||||
global_emulation_speed = GLOBAL_EMULATION_SPEED_NORMAL;
|
||||
// bzero(global_save_folder, 256);
|
||||
bzero(global_rom_name, 256);
|
||||
sprintf(global_cart_name, "NOCARTIRDGE");
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __GLOBAL__
|
||||
#define __GLOBAL__
|
||||
|
||||
/* defines */
|
||||
enum {
|
||||
GLOBAL_EMULATION_SPEED_QUARTER,
|
||||
GLOBAL_EMULATION_SPEED_HALF,
|
||||
GLOBAL_EMULATION_SPEED_NORMAL,
|
||||
GLOBAL_EMULATION_SPEED_DOUBLE,
|
||||
GLOBAL_EMULATION_SPEED_4X
|
||||
};
|
||||
|
||||
extern char global_quit;
|
||||
extern char global_pause;
|
||||
extern char global_window;
|
||||
extern char global_debug;
|
||||
extern char global_cgb;
|
||||
extern char global_next_frame;
|
||||
// extern char global_started;
|
||||
extern char global_cpu_double_speed;
|
||||
extern char global_slow_down;
|
||||
extern char global_record_audio;
|
||||
extern char global_emulation_speed;
|
||||
extern char global_rumble;
|
||||
extern char global_save_folder[256];
|
||||
extern char global_rom_name[256];
|
||||
extern char global_cart_name[256];
|
||||
|
||||
/* prototypes */
|
||||
void global_init();
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __GPU_HDR__
|
||||
#define __GPU_HDR__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* callback function */
|
||||
typedef void (*gpu_frame_ready_cb_t) ();
|
||||
|
||||
/* prototypes */
|
||||
void gpu_dump_oam();
|
||||
uint16_t *gpu_get_frame_buffer();
|
||||
void gpu_init(gpu_frame_ready_cb_t cb);
|
||||
void gpu_reset();
|
||||
void gpu_restore_stat(FILE *fp);
|
||||
void gpu_save_stat(FILE *fp);
|
||||
void gpu_save_fb(FILE *fp);
|
||||
void gpu_set_speed(char speed);
|
||||
void gpu_step();
|
||||
void gpu_toggle(uint8_t state);
|
||||
void gpu_write_reg(uint16_t a, uint8_t v);
|
||||
uint8_t gpu_read_reg(uint16_t a);
|
||||
|
||||
|
||||
/* Gameboy LCD Control - R/W accessing 0xFF40 address */
|
||||
typedef struct gpu_lcd_ctrl_s
|
||||
{
|
||||
uint8_t bg:1; /* 0 = BG off, 1 = BG on */
|
||||
uint8_t sprites:1; /* ??? */
|
||||
uint8_t sprites_size:1; /* 0 = 8x8, 1 = 8x16 */
|
||||
uint8_t bg_tiles_map:1; /* 0 = 9800-9BFF, 1 = 9C00-9FFF */
|
||||
uint8_t bg_tiles:1; /* 0 = 8800-97FF, 1 = 8000-8FFF */
|
||||
uint8_t window:1; /* 0 = window off, 1 = on */
|
||||
uint8_t window_tiles_map:1; /* 0 = 9800-9BFF, 1 = 9C00-9FFF */
|
||||
uint8_t display:1; /* 0 = LCD off, 1 = LCD on */
|
||||
} gpu_lcd_ctrl_t;
|
||||
|
||||
/* Gameboy LCD Status - R/W accessing 0xFF41 address */
|
||||
typedef struct gpu_lcd_status_s
|
||||
{
|
||||
uint8_t mode:2;
|
||||
uint8_t ly_coincidence:1;
|
||||
uint8_t ir_mode_00:1;
|
||||
uint8_t ir_mode_01:1;
|
||||
uint8_t ir_mode_10:1;
|
||||
uint8_t ir_ly_coincidence:1;
|
||||
uint8_t spare:1;
|
||||
} gpu_lcd_status_t;
|
||||
|
||||
/* RGB color */
|
||||
typedef struct rgb_s
|
||||
{
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
} rgb_t;
|
||||
|
||||
/* Gameboy GPU status */
|
||||
typedef struct gpu_s
|
||||
{
|
||||
gpu_lcd_ctrl_t *lcd_ctrl;
|
||||
gpu_lcd_status_t *lcd_status;
|
||||
|
||||
/* scroll positions */
|
||||
uint8_t *scroll_x;
|
||||
uint8_t *scroll_y;
|
||||
|
||||
/* window position */
|
||||
uint8_t *window_x;
|
||||
uint8_t *window_y;
|
||||
|
||||
/* current scanline and it's compare values */
|
||||
uint8_t *ly;
|
||||
uint8_t *lyc;
|
||||
|
||||
/* clocks counter */
|
||||
uint_fast32_t next;
|
||||
|
||||
/* gpu step span */
|
||||
uint_fast32_t step;
|
||||
|
||||
/* window last drawn lines */
|
||||
uint8_t window_last_ly;
|
||||
uint8_t window_skipped_lines;
|
||||
uint16_t spare;
|
||||
|
||||
/* frame counter */
|
||||
uint_fast16_t frame_counter;
|
||||
|
||||
/* BG palette */
|
||||
uint16_t bg_palette[4];
|
||||
|
||||
/* Obj palette 0/1 */
|
||||
uint16_t obj_palette_0[4];
|
||||
uint16_t obj_palette_1[4];
|
||||
|
||||
/* CGB palette for background */
|
||||
uint16_t cgb_palette_bg_rgb565[0x20];
|
||||
uint16_t cgb_palette_bg[0x20];
|
||||
uint8_t cgb_palette_bg_idx;
|
||||
uint8_t cgb_palette_bg_autoinc;
|
||||
uint16_t spare2;
|
||||
|
||||
/* CGB palette for sprites */
|
||||
uint16_t cgb_palette_oam_rgb565[0x20];
|
||||
uint16_t cgb_palette_oam[0x20];
|
||||
uint8_t cgb_palette_oam_idx;
|
||||
uint8_t cgb_palette_oam_autoinc;
|
||||
uint16_t spare3;
|
||||
|
||||
/* frame buffer */
|
||||
uint16_t frame_buffer_prev[160 * 144];
|
||||
uint16_t frame_buffer[160 * 144];
|
||||
uint8_t priority[160 * 144];
|
||||
uint8_t palette_idx[160 * 144];
|
||||
|
||||
uint_fast32_t spare4;
|
||||
uint_fast32_t spare5;
|
||||
|
||||
} gpu_t;
|
||||
|
||||
extern gpu_t gpu;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* button states */
|
||||
char input_key_left;
|
||||
char input_key_right;
|
||||
char input_key_up;
|
||||
char input_key_down;
|
||||
char input_key_a;
|
||||
char input_key_b;
|
||||
char input_key_select;
|
||||
char input_key_start;
|
||||
|
||||
uint8_t input_init()
|
||||
{
|
||||
input_key_left = 0;
|
||||
input_key_right = 0;
|
||||
input_key_up = 0;
|
||||
input_key_down = 0;
|
||||
input_key_a = 0;
|
||||
input_key_b = 0;
|
||||
input_key_select = 0;
|
||||
input_key_start = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t input_get_keys(uint8_t line)
|
||||
{
|
||||
uint8_t v = line | 0x0f;
|
||||
|
||||
if ((line & 0x30) == 0x20)
|
||||
{
|
||||
/* RIGHT pressed? */
|
||||
if (input_key_right)
|
||||
v ^= 0x01;
|
||||
|
||||
/* LEFT pressed? */
|
||||
if (input_key_left)
|
||||
v ^= 0x02;
|
||||
|
||||
/* UP pressed? */
|
||||
if (input_key_up)
|
||||
v ^= 0x04;
|
||||
|
||||
/* DOWN pressed? */
|
||||
if (input_key_down)
|
||||
v ^= 0x08;
|
||||
}
|
||||
|
||||
if ((line & 0x30) == 0x10)
|
||||
{
|
||||
/* A pressed? */
|
||||
if (input_key_a)
|
||||
v ^= 0x01;
|
||||
|
||||
/* B pressed? */
|
||||
if (input_key_b)
|
||||
v ^= 0x02;
|
||||
|
||||
/* SELECT pressed? */
|
||||
if (input_key_select)
|
||||
v ^= 0x04;
|
||||
|
||||
/* START pressed? */
|
||||
if (input_key_start)
|
||||
v ^= 0x08;
|
||||
}
|
||||
|
||||
return (v | 0xc0);
|
||||
}
|
||||
|
||||
void input_set_key_right(char state) { input_key_right = state; }
|
||||
void input_set_key_left(char state) { input_key_left = state; }
|
||||
void input_set_key_up(char state) { input_key_up = state; }
|
||||
void input_set_key_down(char state) { input_key_down = state; }
|
||||
void input_set_key_a(char state) { input_key_a = state; }
|
||||
void input_set_key_b(char state) { input_key_b = state; }
|
||||
void input_set_key_select(char state) { input_key_select = state; }
|
||||
void input_set_key_start(char state) { input_key_start = state; }
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __INPUT_HDR__
|
||||
#define __INPUT_HDR__
|
||||
|
||||
/* prototypes */
|
||||
uint8_t input_get_keys(uint8_t line);
|
||||
uint8_t input_init();
|
||||
void input_set_key_left(char state);
|
||||
void input_set_key_right(char state);
|
||||
void input_set_key_up(char state);
|
||||
void input_set_key_down(char state);
|
||||
void input_set_key_a(char state);
|
||||
void input_set_key_b(char state);
|
||||
void input_set_key_select(char state);
|
||||
void input_set_key_start(char state);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __INTERRUPTS_HDR__
|
||||
#define __INTERRUPTS_HDR__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct interrupts_flags_s
|
||||
{
|
||||
uint8_t lcd_vblank:1;
|
||||
uint8_t lcd_ctrl:1;
|
||||
uint8_t timer:1;
|
||||
uint8_t serial_io:1;
|
||||
uint8_t pins1013:1;
|
||||
uint8_t spare:3;
|
||||
} interrupts_flags_t;
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __MMU_HDR__
|
||||
#define __MMU_HDR__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
typedef struct mmu_gamegenie_s {
|
||||
|
||||
/* data necessary */
|
||||
uint16_t address;
|
||||
uint8_t old_value;
|
||||
uint8_t new_value;
|
||||
|
||||
} mmu_gamegenie_t;
|
||||
|
||||
typedef struct mmu_gameshark_s {
|
||||
|
||||
/* data necessary */
|
||||
uint16_t address;
|
||||
uint8_t ram_bank;
|
||||
uint8_t new_value;
|
||||
|
||||
} mmu_gameshark_t;
|
||||
|
||||
#define MMU_GAMEGENIE_MAX 4
|
||||
#define MMU_GAMESHARK_MAX 32
|
||||
|
||||
typedef struct mmu_s {
|
||||
|
||||
/* main 64K of memory */
|
||||
uint8_t memory[65536];
|
||||
|
||||
/* vram in standby */
|
||||
uint8_t vram0[0x2000];
|
||||
uint8_t vram1[0x2000];
|
||||
|
||||
/* vram current idx */
|
||||
uint8_t vram_idx;
|
||||
uint8_t spare;
|
||||
uint16_t spare2;
|
||||
|
||||
/* internal RAM */
|
||||
uint8_t ram_internal[0x2000];
|
||||
uint8_t ram_external_enabled;
|
||||
uint8_t ram_current_bank;
|
||||
|
||||
/* cartridge type */
|
||||
uint8_t carttype;
|
||||
|
||||
/* number of switchable roms */
|
||||
uint8_t roms;
|
||||
|
||||
/* current ROM bank */
|
||||
uint8_t rom_current_bank;
|
||||
|
||||
/* type of banking */
|
||||
uint8_t banking;
|
||||
|
||||
/* working RAM (only CGB) */
|
||||
uint8_t wram[0x8000];
|
||||
|
||||
/* current WRAM bank (only CGB) */
|
||||
uint8_t wram_current_bank;
|
||||
uint8_t spare3;
|
||||
uint16_t spare4;
|
||||
|
||||
/* DMA transfer stuff */
|
||||
uint_fast16_t dma_address;
|
||||
uint_fast16_t dma_cycles;
|
||||
|
||||
/* HDMA transfer stuff */
|
||||
uint16_t hdma_src_address;
|
||||
uint16_t hdma_dst_address;
|
||||
uint16_t hdma_to_transfer;
|
||||
uint8_t hdma_transfer_mode;
|
||||
uint8_t hdma_current_line;
|
||||
|
||||
/* RTC stuff */
|
||||
uint8_t rtc_mode;
|
||||
uint8_t spare5;
|
||||
uint16_t spare6;
|
||||
time_t rtc_time;
|
||||
time_t rtc_latch_time;
|
||||
|
||||
/* Gamegenie */
|
||||
uint8_t gg_count;
|
||||
mmu_gamegenie_t gg_array[MMU_GAMEGENIE_MAX];
|
||||
|
||||
/* Gameshark */
|
||||
uint8_t gs_count;
|
||||
mmu_gameshark_t gs_array[MMU_GAMESHARK_MAX];
|
||||
|
||||
uint_fast32_t dma_next;
|
||||
uint_fast32_t spare8;
|
||||
|
||||
} mmu_t;
|
||||
|
||||
extern mmu_t mmu;
|
||||
|
||||
/* callback function */
|
||||
typedef void (*mmu_rumble_cb_t) (uint8_t onoff);
|
||||
|
||||
/* functions prototypes */
|
||||
void *mmu_addr(uint16_t a);
|
||||
void *mmu_addr_vram0();
|
||||
void *mmu_addr_vram1();
|
||||
void mmu_apply_gg();
|
||||
void mmu_apply_gs();
|
||||
void mmu_dump_all();
|
||||
void mmu_init(uint8_t c, uint8_t rn);
|
||||
void mmu_init_ram(uint32_t c);
|
||||
void mmu_load(uint8_t *data, size_t sz, uint16_t a);
|
||||
void mmu_load_cartridge(uint8_t *data, size_t sz);
|
||||
void mmu_move(uint16_t d, uint16_t s);
|
||||
uint8_t mmu_read_no_cyc(uint16_t a);
|
||||
uint8_t mmu_read(uint16_t a);
|
||||
unsigned int mmu_read_16(uint16_t a);
|
||||
void mmu_restore_ram(char *fn);
|
||||
void mmu_restore_rtc(char *fn);
|
||||
void mmu_restore_stat(FILE *fp);
|
||||
void mmu_save_ram(char *fn);
|
||||
void mmu_save_rtc(char *fn);
|
||||
void mmu_save_stat(FILE *fp);
|
||||
char mmu_set_cheat(char *cheat);
|
||||
void mmu_set_rumble_cb(mmu_rumble_cb_t cb);
|
||||
void mmu_step();
|
||||
void mmu_term();
|
||||
void mmu_write_no_cyc(uint16_t a, uint8_t v);
|
||||
void mmu_write(uint16_t a, uint8_t v);
|
||||
void mmu_write_16(uint16_t a, uint16_t v);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cycles.h"
|
||||
#include "global.h"
|
||||
#include "network.h"
|
||||
#include "serial.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
/* network special binary semaphore */
|
||||
/* typedef struct network_sem_s {
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cvar;
|
||||
int v;
|
||||
} network_sem_t; */
|
||||
|
||||
/* network sockets */
|
||||
int network_sock_broad = -1;
|
||||
int network_sock_bound = -1;
|
||||
|
||||
/* peer addr */
|
||||
struct sockaddr_in network_peer_addr;
|
||||
|
||||
/* uuid to identify myself */
|
||||
unsigned int network_uuid;
|
||||
|
||||
/* uuid to identify peer */
|
||||
unsigned int network_peer_uuid;
|
||||
|
||||
/* progressive number (debug purposes) */
|
||||
uint8_t network_prog_recv = 0;
|
||||
uint8_t network_prog_sent = 0;
|
||||
|
||||
/* track that network is running */
|
||||
unsigned char network_running = 0;
|
||||
|
||||
/* broadcast address */
|
||||
char network_broadcast_addr[16];
|
||||
|
||||
/* network thread */
|
||||
pthread_t network_thread;
|
||||
|
||||
/* semaphorone */
|
||||
// network_sem_t network_sem;
|
||||
|
||||
/* function to call when connected to another Pizza Boy */
|
||||
network_cb_t network_connected_cb;
|
||||
network_cb_t network_disconnected_cb;
|
||||
|
||||
/* timeout to declare peer disconnected */
|
||||
uint8_t network_timeout = 10;
|
||||
|
||||
uint8_t prot = 0, pret = 0;
|
||||
|
||||
/* prototypes */
|
||||
void network_send_data(uint8_t v, uint8_t clock, uint8_t transfer_start);
|
||||
void *network_start_thread(void *args);
|
||||
|
||||
/* is network running? */
|
||||
char network_is_running()
|
||||
{
|
||||
return network_running;
|
||||
}
|
||||
|
||||
/* start network thread */
|
||||
void network_start(network_cb_t connected_cb, network_cb_t disconnected_cb,
|
||||
char *broadcast_addr)
|
||||
{
|
||||
/* init semaphore */
|
||||
// network_sem_init(&network_sem);
|
||||
|
||||
/* reset bool */
|
||||
network_running = 0;
|
||||
|
||||
/* set callback */
|
||||
network_connected_cb = connected_cb;
|
||||
network_disconnected_cb = disconnected_cb;
|
||||
|
||||
/* save broadcast addr */
|
||||
strncpy(network_broadcast_addr, broadcast_addr, 16);
|
||||
|
||||
/* start thread! */
|
||||
pthread_create(&network_thread, NULL, network_start_thread, NULL);
|
||||
}
|
||||
|
||||
/* stop network thread */
|
||||
void network_stop()
|
||||
{
|
||||
/* already stopped? */
|
||||
if (network_running == 0)
|
||||
return;
|
||||
|
||||
/* tell thread to stop */
|
||||
network_running = 0;
|
||||
|
||||
/* wait for it to exit */
|
||||
pthread_join(network_thread, NULL);
|
||||
}
|
||||
|
||||
void *network_start_thread(void *args)
|
||||
{
|
||||
utils_log("Starting network thread\n");
|
||||
|
||||
/* open socket sending broadcast messages */
|
||||
network_sock_broad = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
/* exit on error */
|
||||
if (network_sock_broad < 1)
|
||||
{
|
||||
utils_log("Error opening broadcast socket");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* open socket sending/receiving serial cable data */
|
||||
network_sock_bound = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
/* exit on error */
|
||||
if (network_sock_bound < 1)
|
||||
{
|
||||
utils_log("Error opening serial-link socket");
|
||||
close (network_sock_broad);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* enable to broadcast */
|
||||
int enable=1;
|
||||
setsockopt(network_sock_broad, SOL_SOCKET, SO_BROADCAST,
|
||||
&enable, sizeof(enable));
|
||||
|
||||
/* prepare dest stuff */
|
||||
struct sockaddr_in broadcast_addr;
|
||||
struct sockaddr_in bound_addr;
|
||||
struct sockaddr_in addr_from;
|
||||
socklen_t addr_from_len = sizeof(addr_from);
|
||||
|
||||
memset(&broadcast_addr, 0, sizeof(broadcast_addr));
|
||||
broadcast_addr.sin_family = AF_INET;
|
||||
// broadcast_addr.sin_addr.s_addr = INADDR_BROADCAST;
|
||||
// inet_aton("239.255.0.37",
|
||||
// (struct in_addr *) &broadcast_addr.sin_addr.s_addr);
|
||||
// inet_aton("192.168.100.255",
|
||||
inet_aton(network_broadcast_addr,
|
||||
(struct in_addr *) &broadcast_addr.sin_addr.s_addr);
|
||||
broadcast_addr.sin_port = htons(64333);
|
||||
|
||||
/* setup listening socket */
|
||||
memset(&bound_addr, 0, sizeof(bound_addr));
|
||||
bound_addr.sin_family = AF_INET;
|
||||
bound_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
bound_addr.sin_port = htons(64333);
|
||||
|
||||
/* bind to selected port */
|
||||
if (bind(network_sock_bound, (struct sockaddr *) &bound_addr,
|
||||
sizeof(bound_addr)))
|
||||
{
|
||||
utils_log("Error binding to port 64333");
|
||||
|
||||
/* close sockets and exit */
|
||||
close(network_sock_broad);
|
||||
close(network_sock_bound);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* assign it to our multicast group */
|
||||
/* struct ip_mreq mreq;
|
||||
mreq.imr_multiaddr.s_addr=inet_addr("239.255.0.37");
|
||||
mreq.imr_interface.s_addr=htonl(INADDR_ANY);
|
||||
|
||||
if (setsockopt(network_sock_bound, IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
||||
&mreq, sizeof(mreq)) < 0)
|
||||
{
|
||||
utils_log("Error joining multicast network");
|
||||
|
||||
close(network_sock_broad);
|
||||
close(network_sock_bound);
|
||||
|
||||
return NULL;
|
||||
}*/
|
||||
|
||||
fd_set rfds;
|
||||
char buf[64];
|
||||
int ret;
|
||||
ssize_t recv_ret;
|
||||
struct timeval tv;
|
||||
int timeouts = 4;
|
||||
// unsigned int v, clock, prog;
|
||||
|
||||
/* message parts */
|
||||
char msg_type;
|
||||
unsigned int msg_uuid;
|
||||
char msg_content[64];
|
||||
|
||||
/* generate a random uuid */
|
||||
srand(time(NULL));
|
||||
network_uuid = rand() & 0xFFFFFFFF;
|
||||
|
||||
/* set callback in case of data to send */
|
||||
serial_set_send_cb(&network_send_data);
|
||||
|
||||
/* declare network is running */
|
||||
network_running = 1;
|
||||
|
||||
utils_log("Network thread started\n");
|
||||
|
||||
/* loop forever */
|
||||
while (network_running)
|
||||
{
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(network_sock_bound, &rfds);
|
||||
|
||||
/* wait one second */
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
/* one second timeout OR something received */
|
||||
ret = select(network_sock_bound + 1, &rfds, NULL, NULL, &tv);
|
||||
|
||||
/* error! */
|
||||
if (ret == -1)
|
||||
break;
|
||||
|
||||
/* ret 0 = timeout */
|
||||
if (ret == 0)
|
||||
{
|
||||
if (++timeouts == 3)
|
||||
{
|
||||
/* build output message */
|
||||
sprintf(buf, "B%08x%s", network_uuid, global_cart_name);
|
||||
|
||||
/* send broadcast message */
|
||||
sendto(network_sock_broad, buf, strlen(buf), 0,
|
||||
(struct sockaddr *) &broadcast_addr,
|
||||
sizeof(broadcast_addr));
|
||||
|
||||
utils_log("Sending broadcast message %s\n", buf);
|
||||
|
||||
timeouts = 0;
|
||||
}
|
||||
|
||||
if (serial.peer_connected)
|
||||
{
|
||||
if (--network_timeout == 0)
|
||||
{
|
||||
/* notify serial module */
|
||||
serial.peer_connected = 0;
|
||||
|
||||
/* stop Hard Sync mode */
|
||||
cycles_stop_hs();
|
||||
|
||||
/* notify by the cb */
|
||||
if (network_disconnected_cb)
|
||||
(*network_disconnected_cb) ();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* reset message content */
|
||||
bzero(buf, sizeof(buf));
|
||||
bzero(msg_content, sizeof(msg_content));
|
||||
|
||||
/* exit if an error occour */
|
||||
if ((recv_ret = recvfrom(network_sock_bound, buf, 64, 0,
|
||||
(struct sockaddr *) &addr_from,
|
||||
(socklen_t *) &addr_from_len)) < 1)
|
||||
break;
|
||||
|
||||
/* extract message type (1st byte) */
|
||||
msg_type = buf[0];
|
||||
|
||||
/* is it broadcast? */
|
||||
//if (sscanf(buf, "%c%08x%s",
|
||||
// &msg_type, &msg_uuid, msg_content) == 3)
|
||||
// {
|
||||
/* was it send by myself? */
|
||||
// if (msg_uuid != network_uuid)
|
||||
// {
|
||||
|
||||
/* is it a serial data message? */
|
||||
if (msg_type == 'M')
|
||||
{
|
||||
network_prog_recv = (uint8_t) buf[3];
|
||||
|
||||
/* buf[1] contains value - buf[2] contains serial clock */
|
||||
/* tell serial module something has arrived */
|
||||
serial_recv_byte((uint8_t) buf[1], (uint8_t) buf[2], buf[4]);
|
||||
}
|
||||
else if (msg_type == 'B')
|
||||
{
|
||||
/* extract parts from broadcast message */
|
||||
sscanf(buf, "%c%08x%s", &msg_type, &msg_uuid, msg_content);
|
||||
|
||||
/* myself? */
|
||||
if (network_uuid == msg_uuid)
|
||||
continue;
|
||||
|
||||
/* not the same game? */
|
||||
if (strcmp(msg_content, global_cart_name) != 0)
|
||||
continue;
|
||||
|
||||
/* someone is claiming is playing with the same game? */
|
||||
if (serial.peer_connected == 0)
|
||||
{
|
||||
/* save peer uuid */
|
||||
network_peer_uuid = msg_uuid;
|
||||
|
||||
/* refresh timeout */
|
||||
network_timeout = 10;
|
||||
|
||||
/* save sender */
|
||||
memcpy(&network_peer_addr, &addr_from,
|
||||
sizeof(struct sockaddr_in));
|
||||
|
||||
/* just change dst port */
|
||||
network_peer_addr.sin_port = htons(64333);
|
||||
|
||||
/* notify the other peer by sending a b message */
|
||||
sprintf(buf, "B%08x%s", network_uuid,
|
||||
global_cart_name);
|
||||
|
||||
/* send broadcast message */
|
||||
sendto(network_sock_broad, buf, strlen(buf), 0,
|
||||
(struct sockaddr *) &network_peer_addr,
|
||||
sizeof(network_peer_addr));
|
||||
|
||||
/* log that peer is connected */
|
||||
utils_log("Peer connected: %s\n",
|
||||
inet_ntoa(network_peer_addr.sin_addr));
|
||||
|
||||
/* YEAH */
|
||||
serial.peer_connected = 1;
|
||||
|
||||
/* notify by the cb */
|
||||
if (network_connected_cb)
|
||||
(*network_connected_cb) ();
|
||||
|
||||
/* start hard sync */
|
||||
cycles_start_hs();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* refresh timeout */
|
||||
if (network_peer_uuid == msg_uuid)
|
||||
network_timeout = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* free serial */
|
||||
serial.peer_connected = 0;
|
||||
|
||||
/* stop hard sync mode */
|
||||
cycles_stop_hs();
|
||||
|
||||
/* close sockets */
|
||||
close(network_sock_broad);
|
||||
close(network_sock_bound);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void network_send_data(uint8_t v, uint8_t clock, uint8_t transfer_start)
|
||||
{
|
||||
char msg[5];
|
||||
|
||||
/* format message */
|
||||
network_prog_sent = ((network_prog_sent + 1) & 0xff);
|
||||
|
||||
msg[0] = 'M';
|
||||
msg[1] = v;
|
||||
msg[2] = clock;
|
||||
msg[3] = network_prog_sent;
|
||||
msg[4] = transfer_start;
|
||||
|
||||
if (network_prog_sent != network_prog_recv &&
|
||||
network_prog_sent != (uint8_t) (network_prog_recv + 1))
|
||||
global_quit = 1;
|
||||
|
||||
/* send */
|
||||
sendto(network_sock_bound, msg, 5, 0,
|
||||
(struct sockaddr *) &network_peer_addr, sizeof(network_peer_addr));
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __NETWORK_HDR__
|
||||
#define __NETWORK_HDR__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* callback function */
|
||||
typedef void (*network_cb_t) ();
|
||||
|
||||
/* prototypes */
|
||||
char network_is_running();
|
||||
void network_start(network_cb_t connected_cb,
|
||||
network_cb_t disconnected_cb,
|
||||
char *broadcast_addr);
|
||||
void network_stop();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "cycles.h"
|
||||
#include "interrupt.h"
|
||||
#include "mmu.h"
|
||||
#include "serial.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* main variable */
|
||||
serial_t serial;
|
||||
|
||||
/* function to call when frame is ready */
|
||||
serial_data_send_cb_t serial_data_send_cb;
|
||||
|
||||
interrupts_flags_t *serial_if;
|
||||
|
||||
/* mutexes for serial sync */
|
||||
pthread_cond_t serial_cond;
|
||||
pthread_mutex_t serial_mutex;
|
||||
|
||||
/* second message before the first was handled? */
|
||||
uint8_t serial_second_set = 0;
|
||||
uint8_t serial_second_data = 0;
|
||||
uint8_t serial_second_clock = 0;
|
||||
uint8_t serial_second_transfer_start = 0;
|
||||
uint8_t serial_waiting_data = 0;
|
||||
|
||||
void serial_verify_intr()
|
||||
{
|
||||
if (serial.data_recv && serial.data_sent)
|
||||
{
|
||||
serial.data_recv = 0;
|
||||
serial.data_sent = 0;
|
||||
|
||||
/* valid couple of messages for a serial interrupt? */
|
||||
if ((serial.data_recv_clock != serial.data_sent_clock) &&
|
||||
serial.data_recv_transfer_start &&
|
||||
serial.data_sent_transfer_start)
|
||||
{
|
||||
/* put received data into 0xFF01 (serial.data) */
|
||||
/* and notify with an interrupt */
|
||||
serial.transfer_start = 0;
|
||||
serial.data = serial.data_to_recv;
|
||||
|
||||
serial_if->serial_io = 1;
|
||||
}
|
||||
|
||||
/* a message is already on queue? */
|
||||
if (serial_second_set)
|
||||
{
|
||||
serial_second_set = 0;
|
||||
serial.data_recv = 1;
|
||||
serial.data_to_recv = serial_second_data;
|
||||
serial.data_recv_clock = serial_second_clock;
|
||||
serial.data_recv_transfer_start = serial_second_transfer_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void serial_init()
|
||||
{
|
||||
/* pointer to interrupt flags */
|
||||
serial_if = mmu_addr(0xFF0F);
|
||||
|
||||
/* init counters */
|
||||
serial.bits_sent = 0;
|
||||
|
||||
/* start as not connected */
|
||||
serial.peer_connected = 0;
|
||||
|
||||
/* init semaphore for sync */
|
||||
pthread_mutex_init(&serial_mutex, NULL);
|
||||
pthread_cond_init(&serial_cond, NULL);
|
||||
}
|
||||
|
||||
void serial_save_stat(FILE *fp)
|
||||
{
|
||||
fwrite(&serial, 1, sizeof(serial_t), fp);
|
||||
}
|
||||
|
||||
void serial_restore_stat(FILE *fp)
|
||||
{
|
||||
fread(&serial, 1, sizeof(serial_t), fp);
|
||||
}
|
||||
|
||||
void serial_write_reg(uint16_t a, uint8_t v)
|
||||
{
|
||||
/* lock the serial */
|
||||
pthread_mutex_lock(&serial_mutex);
|
||||
|
||||
switch (a)
|
||||
{
|
||||
case 0xFF01:
|
||||
serial.data = v; goto end;
|
||||
case 0xFF02:
|
||||
serial.clock = v & 0x01;
|
||||
serial.speed = (v & 0x02) ? 0x01 : 0x00;
|
||||
serial.spare = ((v >> 2) & 0x1F);
|
||||
serial.transfer_start = (v & 0x80) ? 0x01 : 0x00;
|
||||
|
||||
/* reset? */
|
||||
serial.data_sent = 0;
|
||||
}
|
||||
|
||||
if (serial.transfer_start &&
|
||||
!serial.peer_connected &&
|
||||
serial.clock)
|
||||
{
|
||||
if (serial.speed)
|
||||
serial.next = cycles.cnt + 8 * 8;
|
||||
else
|
||||
serial.next = cycles.cnt + 256 * 8;
|
||||
}
|
||||
|
||||
end:
|
||||
/* unlock the serial */
|
||||
pthread_mutex_unlock(&serial_mutex);
|
||||
}
|
||||
|
||||
uint8_t serial_read_reg(uint16_t a)
|
||||
{
|
||||
uint8_t v = 0xFF;
|
||||
|
||||
switch (a)
|
||||
{
|
||||
case 0xFF01: v = serial.data; break;
|
||||
case 0xFF02: v = ((serial.clock) ? 0x01 : 0x00) |
|
||||
((serial.speed) ? 0x02 : 0x00) |
|
||||
(serial.spare << 2) |
|
||||
((serial.transfer_start) ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void serial_recv_byte(uint8_t v, uint8_t clock, uint8_t transfer_start)
|
||||
{
|
||||
/* lock the serial */
|
||||
pthread_mutex_lock(&serial_mutex);
|
||||
|
||||
/* second message during same span time? */
|
||||
if (serial.data_recv)
|
||||
{
|
||||
/* store it. handle it later */
|
||||
serial_second_set = 1;
|
||||
serial_second_data = v;
|
||||
serial_second_clock = clock;
|
||||
serial_second_transfer_start = transfer_start;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* received side OK */
|
||||
serial.data_recv = 1;
|
||||
serial.data_recv_clock = clock;
|
||||
serial.data_to_recv = v;
|
||||
serial.data_recv_transfer_start = transfer_start;
|
||||
|
||||
/* notify main thread in case it's waiting */
|
||||
if (serial_waiting_data)
|
||||
pthread_cond_signal(&serial_cond);
|
||||
|
||||
end:
|
||||
|
||||
/* unlock the serial */
|
||||
pthread_mutex_unlock(&serial_mutex);
|
||||
}
|
||||
|
||||
void serial_send_byte()
|
||||
{
|
||||
/* lock the serial */
|
||||
pthread_mutex_lock(&serial_mutex);
|
||||
|
||||
serial.data_sent = 1;
|
||||
serial.data_to_send = serial.data;
|
||||
serial.data_sent_clock = serial.clock;
|
||||
serial.data_sent_transfer_start = serial.transfer_start;
|
||||
|
||||
if (serial_data_send_cb)
|
||||
(*serial_data_send_cb) (serial.data, serial.clock,
|
||||
serial.transfer_start);
|
||||
|
||||
/* unlock the serial */
|
||||
pthread_mutex_unlock(&serial_mutex);
|
||||
}
|
||||
|
||||
void serial_set_send_cb(serial_data_send_cb_t cb)
|
||||
{
|
||||
serial_data_send_cb = cb;
|
||||
}
|
||||
|
||||
void serial_wait_data()
|
||||
{
|
||||
/* lock the serial */
|
||||
pthread_mutex_lock(&serial_mutex);
|
||||
|
||||
if (serial.data_sent && serial.data_recv == 0)
|
||||
{
|
||||
/* wait max 3 seconds */
|
||||
struct timespec wait;
|
||||
|
||||
wait.tv_sec = time(NULL) + 3;
|
||||
|
||||
/* this is very important to avoid EINVAL return! */
|
||||
wait.tv_nsec = 0;
|
||||
|
||||
/* declare i'm waiting for data */
|
||||
serial_waiting_data = 1;
|
||||
|
||||
/* notify something has arrived */
|
||||
pthread_cond_timedwait(&serial_cond, &serial_mutex, &wait);
|
||||
|
||||
/* not waiting anymore */
|
||||
serial_waiting_data = 0;
|
||||
}
|
||||
|
||||
/* unlock the serial */
|
||||
pthread_mutex_unlock(&serial_mutex);
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __SERIAL_HDR__
|
||||
#define __SERIAL_HDR__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct serial_ctrl_s
|
||||
{
|
||||
uint8_t clock;
|
||||
uint8_t speed;
|
||||
uint8_t spare;
|
||||
uint8_t transfer_start;
|
||||
} serial_ctrl_t;
|
||||
|
||||
typedef struct serial_s {
|
||||
|
||||
/* pointer to serial controller register */
|
||||
// serial_ctrl_t ctrl;
|
||||
|
||||
uint8_t clock;
|
||||
uint8_t speed;
|
||||
uint8_t spare;
|
||||
uint8_t transfer_start;
|
||||
|
||||
/* pointer to FF01 data */
|
||||
uint8_t data;
|
||||
|
||||
/* sent bits */
|
||||
uint8_t bits_sent;
|
||||
|
||||
/* data to send */
|
||||
uint8_t data_to_send;
|
||||
|
||||
/* peer clock */
|
||||
uint8_t data_to_recv;
|
||||
|
||||
/* counter */
|
||||
uint_fast32_t next;
|
||||
|
||||
/* peer connected? */
|
||||
uint8_t peer_connected:1;
|
||||
uint8_t data_sent:1;
|
||||
uint8_t data_sent_clock:1;
|
||||
uint8_t data_sent_transfer_start:1;
|
||||
uint8_t data_recv:1;
|
||||
uint8_t data_recv_clock:1;
|
||||
uint8_t data_recv_transfer_start:1;
|
||||
uint8_t spare10:1;
|
||||
|
||||
uint8_t spare2;
|
||||
uint8_t spare3;
|
||||
uint8_t spare4;
|
||||
|
||||
uint_fast32_t last_send_cnt;
|
||||
|
||||
} serial_t;
|
||||
|
||||
extern serial_t serial;
|
||||
|
||||
/* callback when receive something on serial */
|
||||
typedef void (*serial_data_send_cb_t) (uint8_t v, uint8_t clock,
|
||||
uint8_t transfer_start);
|
||||
|
||||
/* prototypes */
|
||||
void serial_init();
|
||||
void serial_lock();
|
||||
void serial_write_reg(uint16_t a, uint8_t v);
|
||||
void serial_verify_intr();
|
||||
uint8_t serial_read_reg(uint16_t a);
|
||||
void serial_recv_byte(uint8_t v, uint8_t clock, uint8_t transfer_start);
|
||||
void serial_recv_clock();
|
||||
void serial_save_stat(FILE *fp);
|
||||
void serial_send_byte();
|
||||
void serial_set_send_cb(serial_data_send_cb_t cb);
|
||||
void serial_restore_stat(FILE *fp);
|
||||
void serial_unlock();
|
||||
void serial_wait_data();
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __SOUND_HDR__
|
||||
#define __SOUND_HDR__
|
||||
|
||||
#define SOUND_FREQ_MAX 48000
|
||||
#define SOUND_SAMPLES 4096
|
||||
#define SOUND_BUF_SZ (SOUND_SAMPLES * 3)
|
||||
#define SOUND_BUF_TMP_SZ (SOUND_SAMPLES / 2)
|
||||
|
||||
typedef struct nr10_s
|
||||
{
|
||||
uint8_t shift:3;
|
||||
uint8_t negate:1;
|
||||
uint8_t sweep_period:3;
|
||||
uint8_t spare:1;
|
||||
|
||||
} nr10_t;
|
||||
|
||||
typedef struct nr11_s
|
||||
{
|
||||
uint8_t length_load:6;
|
||||
uint8_t duty:2;
|
||||
|
||||
} nr11_t;
|
||||
|
||||
typedef struct nr12_s
|
||||
{
|
||||
uint8_t period:3;
|
||||
uint8_t add:1;
|
||||
uint8_t volume:4;
|
||||
|
||||
} nr12_t;
|
||||
|
||||
typedef struct nr13_s
|
||||
{
|
||||
uint8_t frequency_lsb;
|
||||
|
||||
} nr13_t;
|
||||
|
||||
typedef struct nr14_s
|
||||
{
|
||||
uint8_t frequency_msb:3;
|
||||
uint8_t spare:3;
|
||||
uint8_t length_enable:1;
|
||||
uint8_t trigger:1;
|
||||
|
||||
} nr14_t;
|
||||
|
||||
typedef struct nr21_s
|
||||
{
|
||||
uint8_t length_load:6;
|
||||
uint8_t duty:2;
|
||||
|
||||
} nr21_t;
|
||||
|
||||
typedef struct nr22_s
|
||||
{
|
||||
uint8_t period:3;
|
||||
uint8_t add:1;
|
||||
uint8_t volume:4;
|
||||
|
||||
} nr22_t;
|
||||
|
||||
typedef struct nr23_s
|
||||
{
|
||||
uint8_t frequency_lsb;
|
||||
|
||||
} nr23_t;
|
||||
|
||||
typedef struct nr24_s
|
||||
{
|
||||
uint8_t frequency_msb:3;
|
||||
uint8_t spare:3;
|
||||
uint8_t length_enable:1;
|
||||
uint8_t trigger:1;
|
||||
|
||||
} nr24_t;
|
||||
|
||||
|
||||
typedef struct nr30_s
|
||||
{
|
||||
uint8_t spare:7;
|
||||
uint8_t dac:1;
|
||||
|
||||
} nr30_t;
|
||||
|
||||
typedef struct nr31_s
|
||||
{
|
||||
uint8_t length_load;
|
||||
|
||||
} nr31_t;
|
||||
|
||||
typedef struct nr32_s
|
||||
{
|
||||
uint8_t spare:5;
|
||||
uint8_t volume_code:2;
|
||||
uint8_t spare2:1;
|
||||
|
||||
} nr32_t;
|
||||
|
||||
typedef struct nr33_s
|
||||
{
|
||||
uint8_t frequency_lsb;
|
||||
|
||||
} nr33_t;
|
||||
|
||||
typedef struct nr34_s
|
||||
{
|
||||
uint8_t frequency_msb:3;
|
||||
uint8_t spare:3;
|
||||
uint8_t length_enable:1;
|
||||
uint8_t trigger:1;
|
||||
|
||||
} nr34_t;
|
||||
|
||||
typedef struct nr41_s
|
||||
{
|
||||
uint8_t length_load:6;
|
||||
uint8_t spare:2;
|
||||
|
||||
} nr41_t;
|
||||
|
||||
typedef struct nr42_s
|
||||
{
|
||||
uint8_t period:3;
|
||||
uint8_t add:1;
|
||||
uint8_t volume:4;
|
||||
|
||||
} nr42_t;
|
||||
|
||||
typedef struct nr43_s
|
||||
{
|
||||
uint8_t divisor:3;
|
||||
uint8_t width:1;
|
||||
uint8_t shift:4;
|
||||
|
||||
} nr43_t;
|
||||
|
||||
typedef struct nr44_s
|
||||
{
|
||||
uint8_t spare:6;
|
||||
uint8_t length_enable:1;
|
||||
uint8_t trigger:1;
|
||||
|
||||
} nr44_t;
|
||||
|
||||
typedef struct nr50_s
|
||||
{
|
||||
uint8_t so1_volume:3;
|
||||
uint8_t vin_to_so1:1;
|
||||
uint8_t so2_volume:3;
|
||||
uint8_t vin_to_so2:1;
|
||||
} nr50_t;
|
||||
|
||||
typedef struct nr51_s
|
||||
{
|
||||
uint8_t ch1_to_so1:1;
|
||||
uint8_t ch2_to_so1:1;
|
||||
uint8_t ch3_to_so1:1;
|
||||
uint8_t ch4_to_so1:1;
|
||||
uint8_t ch1_to_so2:1;
|
||||
uint8_t ch2_to_so2:1;
|
||||
uint8_t ch3_to_so2:1;
|
||||
uint8_t ch4_to_so2:1;
|
||||
} nr51_t;
|
||||
|
||||
typedef struct nr52_s
|
||||
{
|
||||
uint8_t spare:7;
|
||||
uint8_t power:1;
|
||||
} nr52_t;
|
||||
|
||||
typedef struct channel_square_s
|
||||
{
|
||||
uint8_t active;
|
||||
uint8_t duty;
|
||||
uint8_t duty_idx;
|
||||
uint8_t envelope_cnt;
|
||||
uint_fast16_t duty_cycles;
|
||||
uint_fast16_t duty_cycles_next;
|
||||
uint_fast32_t length;
|
||||
uint_fast32_t frequency;
|
||||
int16_t sample;
|
||||
int16_t spare;
|
||||
uint_fast16_t sweep_active;
|
||||
uint_fast16_t sweep_cnt;
|
||||
uint_fast16_t sweep_neg;
|
||||
uint_fast16_t sweep_next;
|
||||
int16_t volume;
|
||||
int16_t spare2;
|
||||
uint32_t sweep_shadow_frequency;
|
||||
|
||||
} channel_square_t;
|
||||
|
||||
typedef struct channel_wave_s
|
||||
{
|
||||
uint8_t active;
|
||||
uint8_t index;
|
||||
uint16_t ram_access;
|
||||
int16_t sample;
|
||||
int16_t spare;
|
||||
int16_t wave[32];
|
||||
uint_fast32_t cycles;
|
||||
uint_fast32_t cycles_next;
|
||||
uint_fast32_t ram_access_next;
|
||||
uint_fast32_t length;
|
||||
|
||||
} channel_wave_t;
|
||||
|
||||
typedef struct channel_noise_s
|
||||
{
|
||||
uint8_t active;
|
||||
uint8_t envelope_cnt;
|
||||
uint16_t spare;
|
||||
uint_fast32_t length;
|
||||
uint_fast16_t period_lfsr;
|
||||
uint_fast32_t cycles_next;
|
||||
int16_t volume;
|
||||
int16_t sample;
|
||||
uint16_t reg;
|
||||
uint16_t spare2;
|
||||
|
||||
} channel_noise_t;
|
||||
|
||||
typedef struct sound_s
|
||||
{
|
||||
nr10_t *nr10;
|
||||
nr11_t *nr11;
|
||||
nr12_t *nr12;
|
||||
nr13_t *nr13;
|
||||
nr14_t *nr14;
|
||||
|
||||
nr21_t *nr21;
|
||||
nr22_t *nr22;
|
||||
nr23_t *nr23;
|
||||
nr24_t *nr24;
|
||||
|
||||
nr30_t *nr30;
|
||||
nr31_t *nr31;
|
||||
nr32_t *nr32;
|
||||
nr33_t *nr33;
|
||||
nr34_t *nr34;
|
||||
|
||||
nr41_t *nr41;
|
||||
nr42_t *nr42;
|
||||
nr43_t *nr43;
|
||||
nr44_t *nr44;
|
||||
|
||||
nr50_t *nr50;
|
||||
nr51_t *nr51;
|
||||
nr52_t *nr52;
|
||||
|
||||
uint8_t *wave_table;
|
||||
|
||||
channel_square_t channel_one;
|
||||
channel_square_t channel_two;
|
||||
channel_wave_t channel_three;
|
||||
channel_noise_t channel_four;
|
||||
|
||||
/* emulation speed stuff */
|
||||
uint_fast16_t frame_counter;
|
||||
uint_fast16_t frame_multiplier;
|
||||
|
||||
/* circular audio buffer stuff */
|
||||
uint_fast16_t buf_rd;
|
||||
uint_fast16_t buf_wr;
|
||||
uint_fast16_t buf_available;
|
||||
uint_fast16_t buf_empty;
|
||||
uint_fast16_t buf_full;
|
||||
int16_t buf[SOUND_BUF_SZ];
|
||||
int16_t buf_tmp[SOUND_BUF_TMP_SZ];
|
||||
uint_fast16_t buf_tmp_wr;
|
||||
|
||||
/* output rate */
|
||||
uint_fast32_t output_rate;
|
||||
|
||||
/* CPU cycles to internal cycles counters */
|
||||
uint_fast32_t fs_cycles;
|
||||
uint_fast32_t fs_cycles_idx;
|
||||
uint_fast32_t fs_cycles_next;
|
||||
uint_fast32_t sample_cycles;
|
||||
uint_fast32_t sample_cycles_remainder;
|
||||
uint_fast32_t sample_cycles_next;
|
||||
uint_fast32_t sample_cycles_next_rounded;
|
||||
|
||||
/* steps length */
|
||||
uint_fast32_t step_int;
|
||||
uint_fast32_t step_int1000;
|
||||
|
||||
uint_fast32_t spare;
|
||||
uint_fast32_t spare2;
|
||||
|
||||
} sound_t;
|
||||
|
||||
extern sound_t sound;
|
||||
|
||||
/* prototypes */
|
||||
void sound_change_emulation_speed();
|
||||
int sound_get_samples();
|
||||
void sound_init();
|
||||
void sound_read_buffer(void *userdata, uint8_t *stream, int snd_len);
|
||||
uint8_t sound_read_reg(uint16_t a, uint8_t v);
|
||||
void sound_restore_stat(FILE *fp);
|
||||
void sound_save_stat(FILE *fp);
|
||||
void sound_set_speed(char dbl);
|
||||
void sound_set_output_rate(int freq);
|
||||
void sound_step_fs();
|
||||
void sound_step_ch1();
|
||||
void sound_step_ch2();
|
||||
void sound_step_ch3();
|
||||
void sound_step_ch4();
|
||||
void sound_step_sample();
|
||||
void sound_term();
|
||||
void sound_write_reg(uint16_t a, uint8_t v);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,8 @@
|
|||
//
|
||||
// Created by DVDN on 14/07/2016.
|
||||
//
|
||||
|
||||
#ifndef PIZZABOY_TESTONE_H
|
||||
#define PIZZABOY_TESTONE_H
|
||||
|
||||
#endif //PIZZABOY_TESTONE_H
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include "cycles.h"
|
||||
#include "interrupt.h"
|
||||
#include "mmu.h"
|
||||
#include "timer.h"
|
||||
|
||||
/* pointer to interrupt flags (handy) */
|
||||
interrupts_flags_t *timer_if;
|
||||
|
||||
|
||||
void timer_init()
|
||||
{
|
||||
/* reset values */
|
||||
timer.next = 256;
|
||||
timer.sub = 0;
|
||||
|
||||
/* pointer to interrupt flags */
|
||||
timer_if = mmu_addr(0xFF0F);
|
||||
}
|
||||
|
||||
void timer_write_reg(uint16_t a, uint8_t v)
|
||||
{
|
||||
switch (a)
|
||||
{
|
||||
case 0xFF04: timer.div = 0; return;
|
||||
case 0xFF05: timer.cnt = v; return;
|
||||
case 0xFF06: timer.mod = v; return;
|
||||
case 0xFF07: timer.ctrl = v;
|
||||
}
|
||||
|
||||
if (timer.ctrl & 0x04)
|
||||
timer.active = 1;
|
||||
else
|
||||
timer.active = 0;
|
||||
|
||||
switch (timer.ctrl & 0x03)
|
||||
{
|
||||
case 0x00: timer.threshold = 1024; break;
|
||||
case 0x01: timer.threshold = 16; break;
|
||||
case 0x02: timer.threshold = 64; break;
|
||||
case 0x03: timer.threshold = 256; break;
|
||||
}
|
||||
|
||||
if (timer.active)
|
||||
timer.sub_next = cycles.cnt + timer.threshold;
|
||||
}
|
||||
|
||||
uint8_t timer_read_reg(uint16_t a)
|
||||
{
|
||||
switch (a)
|
||||
{
|
||||
case 0xFF04: return timer.div;
|
||||
case 0xFF05: return timer.cnt;
|
||||
case 0xFF06: return timer.mod;
|
||||
case 0xFF07: return timer.ctrl;
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __TIMER_HDR__
|
||||
#define __TIMER_HDR__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* timer status */
|
||||
typedef struct timer_gb_s
|
||||
{
|
||||
/* is it active? */
|
||||
uint8_t active;
|
||||
|
||||
/* divider - 0xFF04 */
|
||||
uint8_t div;
|
||||
|
||||
/* modulo - 0xFF06 */
|
||||
uint8_t mod;
|
||||
|
||||
/* control - 0xFF07 */
|
||||
uint8_t ctrl;
|
||||
|
||||
/* counter - 0xFF05 */
|
||||
uint_fast32_t cnt;
|
||||
|
||||
/* threshold */
|
||||
uint32_t threshold;
|
||||
|
||||
/* current value */
|
||||
uint_fast32_t sub;
|
||||
uint_fast32_t next;
|
||||
|
||||
/* spare */
|
||||
uint_fast32_t sub_next;
|
||||
uint_fast32_t spare2;
|
||||
|
||||
} timer_gb_t;
|
||||
|
||||
/* global status of timer */
|
||||
timer_gb_t timer;
|
||||
|
||||
/* prototypes */
|
||||
void timer_init();
|
||||
void timer_step();
|
||||
void timer_write_reg(uint16_t a, uint8_t v);
|
||||
uint8_t timer_read_reg(uint16_t a);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "cycles.h"
|
||||
#include "gpu.h"
|
||||
#include "utils.h"
|
||||
|
||||
uint32_t prev_cycles = 0;
|
||||
|
||||
void utils_log(const char *format, ...)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
vsnprintf(buf, 256, format, args);
|
||||
__android_log_write(ANDROID_LOG_INFO, "Pizza", buf);
|
||||
|
||||
#else
|
||||
|
||||
vsnprintf(buf, 256, format, args);
|
||||
printf(buf);
|
||||
|
||||
#endif
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
void utils_log_urgent(const char *format, ...)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
vsnprintf(buf, 256, format, args);
|
||||
__android_log_write(ANDROID_LOG_INFO, "Pizza", buf);
|
||||
|
||||
#else
|
||||
|
||||
vsnprintf(buf, 256, format, args);
|
||||
printf(buf);
|
||||
|
||||
#endif
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void utils_ts_log(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
char buf[256];
|
||||
struct timeval tv;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
vsnprintf(buf, 256, format, args);
|
||||
__android_log_write(ANDROID_LOG_INFO, "Pizza", buf);
|
||||
|
||||
#else
|
||||
|
||||
vsprintf(buf, format, args);
|
||||
gettimeofday(&tv, NULL);
|
||||
// printf("%ld - %s\n", tv.tv_sec, buf);
|
||||
printf("LINE %u - CYCLES %u - DIFF %u - %ld:%06ld - %s",
|
||||
*(gpu.ly), cycles.cnt, cycles.cnt - prev_cycles,
|
||||
tv.tv_sec, tv.tv_usec, buf);
|
||||
|
||||
prev_cycles = cycles.cnt;
|
||||
|
||||
#endif
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void utils_binary_sem_init(utils_binary_sem_t *p)
|
||||
{
|
||||
pthread_mutex_init(&p->mutex, NULL);
|
||||
pthread_cond_init(&p->cvar, NULL);
|
||||
p->v = 0;
|
||||
}
|
||||
|
||||
void utils_binary_sem_post(utils_binary_sem_t *p)
|
||||
{
|
||||
pthread_mutex_lock(&p->mutex);
|
||||
p->v = 1;
|
||||
pthread_cond_signal(&p->cvar);
|
||||
pthread_mutex_unlock(&p->mutex);
|
||||
}
|
||||
|
||||
void utils_binary_sem_wait(utils_binary_sem_t *p, unsigned int nanosecs)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
ts.tv_sec = time(NULL) + nanosecs / 1000000000;
|
||||
ts.tv_nsec = nanosecs % 1000000000;
|
||||
|
||||
pthread_mutex_lock(&p->mutex);
|
||||
while (!p->v)
|
||||
if (pthread_cond_timedwait(&p->cvar, &p->mutex, &ts) == ETIMEDOUT)
|
||||
break;
|
||||
p->v = 0;
|
||||
pthread_mutex_unlock(&p->mutex);
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __UTILS_HDR__
|
||||
#define __UTILS_HDR__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/* binary semaphore */
|
||||
typedef struct utils_binary_sem_s
|
||||
{
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cvar;
|
||||
int v;
|
||||
|
||||
} utils_binary_sem_t;
|
||||
|
||||
/* prototypes */
|
||||
void utils_binary_sem_init(utils_binary_sem_t *p);
|
||||
void utils_binary_sem_post(utils_binary_sem_t *p);
|
||||
void utils_binary_sem_wait(utils_binary_sem_t *p, unsigned int nanosecs);
|
||||
void utils_log(const char *format, ...);
|
||||
void utils_log_urgent(const char *format, ...);
|
||||
void utils_ts_log(const char *format, ...);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef Z80_REGS_H
|
||||
#define Z80_REGS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* structs emulating z80 registers and flags */
|
||||
typedef struct z80_flags_s
|
||||
{
|
||||
uint8_t spare:4;
|
||||
uint8_t cy:1;
|
||||
uint8_t ac:1;
|
||||
uint8_t n:1;
|
||||
uint8_t z:1;
|
||||
} z80_flags_t;
|
||||
|
||||
|
||||
/* flags offsets */
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
|
||||
#define FLAG_OFFSET_CY 4
|
||||
#define FLAG_OFFSET_AC 5
|
||||
#define FLAG_OFFSET_N 6
|
||||
#define FLAG_OFFSET_Z 7
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
|
||||
This file is part of Emu-Pizza
|
||||
|
||||
Emu-Pizza is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Emu-Pizza is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "cartridge.h"
|
||||
#include "cycles.h"
|
||||
#include "gameboy.h"
|
||||
#include "global.h"
|
||||
#include "gpu.h"
|
||||
#include "input.h"
|
||||
#include "network.h"
|
||||
#include "sound.h"
|
||||
#include "serial.h"
|
||||
|
||||
/* proto */
|
||||
void cb();
|
||||
void connected_cb();
|
||||
void disconnected_cb();
|
||||
void rumble_cb(uint8_t rumble);
|
||||
void network_send_data(uint8_t v);
|
||||
void *start_thread(void *args);
|
||||
void *start_thread_network(void *args);
|
||||
|
||||
/* frame buffer pointer */
|
||||
uint16_t *fb;
|
||||
|
||||
/* magnify rate */
|
||||
float magnify_rate = 1.f;
|
||||
|
||||
/* emulator thread */
|
||||
pthread_t thread;
|
||||
|
||||
/* SDL video stuff */
|
||||
SDL_Window *window;
|
||||
SDL_Surface *screenSurface;
|
||||
SDL_Surface *windowSurface;
|
||||
|
||||
/* cartridge name */
|
||||
char cart_name[64];
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* SDL variables */
|
||||
SDL_Event e;
|
||||
SDL_AudioSpec desired;
|
||||
SDL_AudioSpec obtained;
|
||||
|
||||
/* init global variables */
|
||||
global_init();
|
||||
|
||||
/* set global folder */
|
||||
snprintf(global_save_folder, sizeof(global_save_folder), "/tmp/str/save/");
|
||||
__mkdirp(global_save_folder, S_IRWXU);
|
||||
|
||||
/* first, load cartridge */
|
||||
char ret = cartridge_load(argv[1]);
|
||||
|
||||
if (ret != 0)
|
||||
return 1;
|
||||
|
||||
/* apply cheat */
|
||||
|
||||
/* tetris */
|
||||
/* mmu_set_cheat("00063D6E9");
|
||||
mmu_set_cheat("3E064D5D0");
|
||||
mmu_set_cheat("04065D087"); */
|
||||
|
||||
/* samurai shodown */
|
||||
// mmu_set_cheat("11F86E3B6");
|
||||
//)
|
||||
// mmu_set_cheat("3EB60D7F1");
|
||||
//
|
||||
|
||||
/* gameshark aladdin */
|
||||
// mmu_set_cheat("01100ADC");
|
||||
|
||||
/* gameshark wario land */
|
||||
// mmu_set_cheat("809965A9");
|
||||
|
||||
// mmu_apply_gg();
|
||||
|
||||
/* initialize SDL video */
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0 )
|
||||
{
|
||||
printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
window = SDL_CreateWindow("Emu Pizza - Gameboy",
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
160 * magnify_rate, 144 * magnify_rate,
|
||||
SDL_WINDOW_SHOWN);
|
||||
|
||||
/* get window surface */
|
||||
windowSurface = SDL_GetWindowSurface(window);
|
||||
screenSurface = SDL_ConvertSurfaceFormat(windowSurface,
|
||||
SDL_PIXELFORMAT_RGB565,
|
||||
0);
|
||||
|
||||
gameboy_init();
|
||||
|
||||
/* initialize SDL audio */
|
||||
SDL_Init(SDL_INIT_AUDIO);
|
||||
desired.freq = 44100;
|
||||
desired.samples = SOUND_SAMPLES / 2;
|
||||
desired.format = AUDIO_S16SYS;
|
||||
desired.channels = 2;
|
||||
desired.callback = sound_read_buffer;
|
||||
desired.userdata = NULL;
|
||||
|
||||
/* Open audio */
|
||||
if (SDL_OpenAudio(&desired, &obtained) == 0)
|
||||
SDL_PauseAudio(0);
|
||||
else
|
||||
{
|
||||
printf("Cannot open audio device!!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* init GPU */
|
||||
gpu_init(&cb);
|
||||
|
||||
/* set sound output rate */
|
||||
sound_set_output_rate(44100);
|
||||
|
||||
/* set rumble cb */
|
||||
mmu_set_rumble_cb(&rumble_cb);
|
||||
|
||||
/* get frame buffer reference */
|
||||
fb = gpu_get_frame_buffer();
|
||||
|
||||
/* start thread! */
|
||||
pthread_create(&thread, NULL, start_thread, NULL);
|
||||
|
||||
/* start network thread! */
|
||||
network_start(&connected_cb, &disconnected_cb, "192.168.100.255");
|
||||
|
||||
/* loop forever */
|
||||
while (!global_quit)
|
||||
{
|
||||
/* aaaaaaaaaaaaaand finally, check for SDL events */
|
||||
|
||||
/* SDL_WaitEvent should be better but somehow, */
|
||||
/* it interfer with my cycles timer */
|
||||
if (SDL_PollEvent(&e) == 0)
|
||||
{
|
||||
usleep(100000);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (e.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
global_quit = 1;
|
||||
break;
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
switch (e.key.keysym.sym)
|
||||
{
|
||||
case (SDLK_1): gameboy_set_pause(1);
|
||||
gameboy_save_stat(0);
|
||||
gameboy_set_pause(0);
|
||||
break;
|
||||
case (SDLK_2): gameboy_set_pause(1);
|
||||
gameboy_restore_stat(0);
|
||||
gameboy_set_pause(0);
|
||||
break;
|
||||
case (SDLK_9): network_start(&connected_cb,
|
||||
&disconnected_cb,
|
||||
"192.168.100.255"); break;
|
||||
case (SDLK_0): network_stop(); break;
|
||||
case (SDLK_q): global_quit = 1; break;
|
||||
case (SDLK_d): global_debug ^= 0x01; break;
|
||||
case (SDLK_s): global_slow_down = 1; break;
|
||||
case (SDLK_w): global_window ^= 0x01; break;
|
||||
case (SDLK_n): gameboy_set_pause(0);
|
||||
global_next_frame = 1; break;
|
||||
case (SDLK_PLUS):
|
||||
if (global_emulation_speed !=
|
||||
GLOBAL_EMULATION_SPEED_4X)
|
||||
{
|
||||
global_emulation_speed++;
|
||||
cycles_change_emulation_speed();
|
||||
sound_change_emulation_speed();
|
||||
}
|
||||
|
||||
break;
|
||||
case (SDLK_MINUS):
|
||||
if (global_emulation_speed !=
|
||||
GLOBAL_EMULATION_SPEED_QUARTER)
|
||||
{
|
||||
global_emulation_speed--;
|
||||
cycles_change_emulation_speed();
|
||||
sound_change_emulation_speed();
|
||||
}
|
||||
|
||||
break;
|
||||
case (SDLK_p): gameboy_set_pause(global_pause ^ 0x01);
|
||||
break;
|
||||
case (SDLK_m): mmu_dump_all(); break;
|
||||
case (SDLK_SPACE): input_set_key_select(1); break;
|
||||
case (SDLK_RETURN): input_set_key_start(1); break;
|
||||
case (SDLK_UP): input_set_key_up(1); break;
|
||||
case (SDLK_DOWN): input_set_key_down(1); break;
|
||||
case (SDLK_RIGHT): input_set_key_right(1); break;
|
||||
case (SDLK_LEFT): input_set_key_left(1); break;
|
||||
case (SDLK_z): input_set_key_b(1); break;
|
||||
case (SDLK_x): input_set_key_a(1); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_KEYUP:
|
||||
switch (e.key.keysym.sym)
|
||||
{
|
||||
case (SDLK_SPACE): input_set_key_select(0); break;
|
||||
case (SDLK_RETURN): input_set_key_start(0); break;
|
||||
case (SDLK_UP): input_set_key_up(0); break;
|
||||
case (SDLK_DOWN): input_set_key_down(0); break;
|
||||
case (SDLK_RIGHT): input_set_key_right(0); break;
|
||||
case (SDLK_LEFT): input_set_key_left(0); break;
|
||||
case (SDLK_z): input_set_key_b(0); break;
|
||||
case (SDLK_x): input_set_key_a(0); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* join emulation thread */
|
||||
pthread_join(thread, NULL);
|
||||
|
||||
/* stop network thread! */
|
||||
network_stop();
|
||||
|
||||
utils_log("Total cycles %d\n", cycles.cnt);
|
||||
utils_log("Total running seconds %d\n", cycles.seconds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *start_thread(void *args)
|
||||
{
|
||||
/* run until break or global_quit is set */
|
||||
gameboy_run();
|
||||
|
||||
/* tell main thread it's over */
|
||||
global_quit = 1;
|
||||
}
|
||||
|
||||
void cb()
|
||||
{
|
||||
uint16_t *pixel = screenSurface->pixels;
|
||||
|
||||
/* magnify! */
|
||||
if (magnify_rate > 1)
|
||||
{
|
||||
int x,y,p;
|
||||
float px, py = 0;
|
||||
|
||||
uint16_t *line = malloc(sizeof(uint16_t) * 160 * magnify_rate);
|
||||
|
||||
for (y=0; y<144; y++)
|
||||
{
|
||||
px = 0;
|
||||
|
||||
for (x=0; x<160; x++)
|
||||
{
|
||||
for (; px<magnify_rate; px++)
|
||||
line[(int) (px + (x * magnify_rate))] =
|
||||
fb[x + (y * 160)];
|
||||
|
||||
px -= magnify_rate;
|
||||
}
|
||||
|
||||
for (; py<magnify_rate; py++)
|
||||
memcpy(&pixel[(int) (((y * magnify_rate) + py) *
|
||||
160 * magnify_rate)],
|
||||
line, sizeof(uint16_t) * 160 * magnify_rate);
|
||||
|
||||
py -= magnify_rate;
|
||||
}
|
||||
|
||||
free(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* just copy GPU frame buffer into SDL frame buffer */
|
||||
memcpy(pixel, fb, 160 * 144 * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
SDL_ConvertPixels(screenSurface->w, screenSurface->h,
|
||||
screenSurface->format->format,
|
||||
screenSurface->pixels, screenSurface->pitch,
|
||||
SDL_PIXELFORMAT_ARGB8888,
|
||||
windowSurface->pixels, windowSurface->pitch);
|
||||
|
||||
/* Update the surface */
|
||||
SDL_UpdateWindowSurface(window);
|
||||
}
|
||||
|
||||
void connected_cb()
|
||||
{
|
||||
utils_log("Connected\n");
|
||||
}
|
||||
|
||||
void disconnected_cb()
|
||||
{
|
||||
utils_log("Disconnected\n");
|
||||
}
|
||||
|
||||
void rumble_cb(uint8_t rumble)
|
||||
{
|
||||
if (rumble)
|
||||
printf("RUMBLE\n");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns 1 if a directory has been created,
|
||||
* 2 if it already existed, and 0 on failure.
|
||||
*/
|
||||
int __mkdirp (char *path, mode_t omode)
|
||||
{
|
||||
struct stat sb;
|
||||
mode_t numask, oumask;
|
||||
int first, last, retval;
|
||||
char *p;
|
||||
|
||||
p = path;
|
||||
oumask = 0;
|
||||
retval = 1;
|
||||
|
||||
if (p[0] == '/') /* Skip leading '/'. */
|
||||
++p;
|
||||
|
||||
for (first = 1, last = 0; !last ; ++p)
|
||||
{
|
||||
if (p[0] == '\0')
|
||||
last = 1;
|
||||
else if (p[0] != '/')
|
||||
continue;
|
||||
|
||||
*p = '\0';
|
||||
|
||||
if (!last && p[1] == '\0')
|
||||
last = 1;
|
||||
|
||||
if (first)
|
||||
{
|
||||
oumask = umask(0);
|
||||
numask = oumask & ~(S_IWUSR | S_IXUSR);
|
||||
(void) umask(numask);
|
||||
first = 0;
|
||||
}
|
||||
|
||||
if (last)
|
||||
(void) umask(oumask);
|
||||
|
||||
if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0)
|
||||
{
|
||||
if (errno == EEXIST || errno == EISDIR)
|
||||
{
|
||||
if (stat(path, &sb) < 0)
|
||||
{
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
else if (!S_ISDIR(sb.st_mode))
|
||||
{
|
||||
if (last)
|
||||
errno = EEXIST;
|
||||
else
|
||||
errno = ENOTDIR;
|
||||
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (last)
|
||||
retval = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!last)
|
||||
*p = '/';
|
||||
}
|
||||
|
||||
if (!first && !last)
|
||||
(void) umask(oumask);
|
||||
|
||||
return retval;
|
||||
}
|
Loading…
Reference in New Issue