- [CHG] Tot el codi mogut a la carpeta "source"

This commit is contained in:
2026-06-03 18:07:16 +02:00
parent 88a02d49f7
commit 2fe0ce6152
32 changed files with 4 additions and 5 deletions
+434
View File
@@ -0,0 +1,434 @@
#include "apu.h"
#include <SDL2/SDL.h>
//#include "audio_viewer.h"
namespace apu
{
#define SAMPLING_FREQ 44100
#define AUDIO_BUFFER_SIZE 2048
const float cycles_per_sample = 4194304.0f / SAMPLING_FREQ;
SDL_AudioDeviceID sdlAudioDevice;
uint8_t sound_buffer[AUDIO_BUFFER_SIZE];
uint16_t sound_pos=0;
uint16_t sound_start=0;
float t_sound = 0.0f;
uint32_t samples_generated=0;
#define CH1 channels[0]
#define CH2 channels[1]
#define CH3 channels[2]
#define CH4 channels[3]
#define apu_enabled (NR52&0x80)
#define DAC1_enabled ((NR12&0xf8)!=0)
#define DAC2_enabled ((NR22&0xf8)!=0)
#define DAC3_enabled ((NR30&0x80)!=0)
#define DAC4_enabled ((NR42&0xf8)!=0)
uint8_t duty_cycles[4][8] = {
{1, 1, 1, 1, 1, 1, 1, 0},
{1, 1, 1, 1, 1, 1, 0, 0},
{1, 1, 1, 1, 0, 0, 0, 0},
{1, 1, 0, 0, 0, 0, 0, 0},
};
struct channel_t
{
bool enabled = false;
uint8_t length_timer = 0;
bool length_enable = false;
uint16_t period_divider = 0;
uint8_t duty_cycle=0;
uint8_t duty_step = 0;
uint8_t volume = 0;
uint8_t envelope_sweep_timer=0;
};
channel_t channels[4];
uint8_t DIVAPU = 0;
uint8_t NR10 = 0; // 0xff10 - Sound channel 1 sweep
uint8_t NR11 = 0; // 0xff11 - Sound channel 1 length timer & duty cycle
uint8_t NR12 = 0; // 0xff12 - Sound channel 1 volume & envelope
uint8_t NR13 = 0; // 0xff13 - Sound channel 1 period low
uint8_t NR14 = 0; // 0xff14 - Sound channel 1 period high & control
// 0xff15 - Not used?
uint8_t NR21 = 0; // 0xff16 - Sound channel 2 length timer & duty cycle
uint8_t NR22 = 0; // 0xff17 - Sound channel 2 volume & envelope
uint8_t NR23 = 0; // 0xff18 - Sound channel 2 period low
uint8_t NR24 = 0; // 0xff19 - Sound channel 2 period high & control
uint8_t NR30 = 0; // 0xff1a - Sound channel 3 DAC enable
uint8_t NR31 = 0; // 0xff1b - Sound channel 3 length timer
uint8_t NR32 = 0; // 0xff1c - Sound channel 3 output level
uint8_t NR33 = 0; // 0xff1d - Sound channel 3 period low
uint8_t NR34 = 0; // 0xff1e - Sound channel 3 period high & control
// 0xff1f - Not used?
uint8_t NR41 = 0; // 0xff20 - Sound channel 4 length timer
uint8_t NR42 = 0; // 0xff21 - Sound channel 4 volume & envelope
uint8_t NR43 = 0; // 0xff22 - Sound channel 4 frequency & randomness
uint8_t NR44 = 0; // 0xff23 - Sound channel 4 control
uint8_t NR50 = 0; // 0xff24 - Master volume & VIN panning
uint8_t NR51 = 0; // 0xff25 - Sound panning
uint8_t NR52 = 0; // 0xff26 - Sound on/off
// 0xff27-0xff2f - Not used?
uint8_t WaveRAM[16]; // 0xff30-0xff3f
uint16_t LFSR = 0;
void silence()
{
SDL_PauseAudioDevice(sdlAudioDevice, 1);
}
void resume()
{
SDL_PauseAudioDevice(sdlAudioDevice, 0);
}
void init()
{
SDL_AudioSpec audioSpec{SAMPLING_FREQ, AUDIO_U8, 1, 0, AUDIO_BUFFER_SIZE, 0, 0, NULL, NULL};
sdlAudioDevice = SDL_OpenAudioDevice(NULL, 0, &audioSpec, NULL, 0);
resume();
}
void reset()
{
NR10 = 0;
NR11 = 0;
NR12 = 0;
NR13 = 0;
NR14 = 0;
NR21 = 0;
NR22 = 0;
NR23 = 0;
NR24 = 0;
NR30 = 0;
NR31 = 0;
NR32 = 0;
NR33 = 0;
NR34 = 0;
NR41 = 0;
NR42 = 0;
NR43 = 0;
NR44 = 0;
NR50 = 0;
NR51 = 0;
NR52 = 0;
CH1.duty_cycle = 0;
LFSR = 0;
for (int i=0; i<16; ++i) WaveRAM[i]=0;
}
uint8_t getLFSR()
{
LFSR = (LFSR&0x7fff) | ( (LFSR&1)==((LFSR>>1)&1) ? 0x8000 : 0x0000 );
if (NR43&0x08) LFSR = (LFSR&0xff7f) | ((LFSR>>8)&0x0080);
LFSR=LFSR>>1;
return LFSR&1;
}
void triggerCH1()
{
CH1.enabled = true;
CH1.length_timer=NR11&0x3f;
CH1.period_divider = NR13 | ((NR14 &0x7)<<8);
CH1.envelope_sweep_timer = 0;
CH1.volume = NR12>>4;
// sweep does several things (check documentation)
}
void triggerCH2()
{
CH2.enabled = true;
CH2.length_timer=NR21&0x3f;
CH2.period_divider = NR23 | ((NR24 &0x7)<<8);
CH2.envelope_sweep_timer = 0;
CH2.volume = NR22>>4;
// sweep does several things (check documentation)
}
void triggerCH3()
{
CH3.enabled = true;
CH3.length_timer=NR31;
CH3.period_divider = NR33 | ((NR34 &0x7)<<8);
CH3.envelope_sweep_timer = 0;
CH3.volume = (NR32>>5)&0x3;
// sweep does several things (check documentation)
}
void triggerCH4()
{
CH4.enabled = true;
CH4.length_timer=NR41&0x3f;
uint8_t clock_shift = ((NR43&0xf0)>>4);// if (clock_shift==0) clock_shift = 1;
CH4.period_divider = clock_shift << (NR43&0x07);
CH4.envelope_sweep_timer = 0;
CH4.volume = NR42>>4;
// sweep does several things (check documentation)
}
uint8_t readRegister(uint16_t address)
{
switch(address)
{
case 0xff10: return NR10; break;
case 0xff11: return NR11 & 0xc0; break;
case 0xff12: return NR12; break;
case 0xff13: return 0x00; break;
case 0xff14: return NR14 & 0x40; break;
case 0xff16: return NR21 & 0xc0; break;
case 0xff17: return NR22; break;
case 0xff18: return 0x00; break;
case 0xff19: return NR24 & 0x40; break;
case 0xff1a: return NR30; break;
case 0xff1b: return 0x00; break;
case 0xff1c: return NR32; break;
case 0xff1d: return 0x00; break;
case 0xff1e: return NR34 & 0x40; break;
case 0xff20: return 0x00; break;
case 0xff21: return NR42; break;
case 0xff22: return NR43; break;
case 0xff23: return NR44 & 0x40; break;
case 0xff24: return NR50; break;
case 0xff25: return NR51; break;
case 0xff26: return NR52; break;
case 0xff30: return WaveRAM[0]; break;
case 0xff31: return WaveRAM[1]; break;
case 0xff32: return WaveRAM[2]; break;
case 0xff33: return WaveRAM[3]; break;
case 0xff34: return WaveRAM[4]; break;
case 0xff35: return WaveRAM[5]; break;
case 0xff36: return WaveRAM[6]; break;
case 0xff37: return WaveRAM[7]; break;
case 0xff38: return WaveRAM[8]; break;
case 0xff39: return WaveRAM[9]; break;
case 0xff3a: return WaveRAM[10]; break;
case 0xff3b: return WaveRAM[11]; break;
case 0xff3c: return WaveRAM[12]; break;
case 0xff3d: return WaveRAM[13]; break;
case 0xff3e: return WaveRAM[14]; break;
case 0xff3f: return WaveRAM[15]; break;
}
return 0x00;
}
void writeRegister(uint16_t address, uint8_t value)
{
if (!apu_enabled && (address!=0xff26)) return;
switch(address)
{
case 0xff10: NR10 = value; break;
case 0xff11: NR11 = value; CH1.length_timer=NR11&0x3f; CH1.duty_cycle = NR11>>6; break;
case 0xff12: NR12 = value; break;
case 0xff13: NR13 = value; break;
case 0xff14: NR14 = value; CH1.length_enable=(value&0x40); if (value&0x80) triggerCH1(); break;
case 0xff16: NR21 = value; CH2.length_timer=NR21&0x3f; CH2.duty_cycle = NR21>>6; break;
case 0xff17: NR22 = value; break;
case 0xff18: NR23 = value; break;
case 0xff19: NR24 = value; CH2.length_enable=(value&0x40); if (value&0x80) triggerCH2(); break;
case 0xff1a: NR30 = value; break;
case 0xff1b: NR31 = value; CH3.length_timer=NR31; break;
case 0xff1c: NR32 = value; break;
case 0xff1d: NR33 = value; break;
case 0xff1e: NR34 = value; CH3.length_enable=(value&0x40); if (value&0x80) triggerCH3(); break;
case 0xff20: NR41 = value; CH1.length_timer=NR11&0x3f; CH1.duty_cycle = NR11>>6; break;
case 0xff21: NR42 = value; break;
case 0xff22: NR43 = value; break;
case 0xff23: NR44 = value; CH4.length_enable=(value&0x40); if (value&0x80) triggerCH4(); break;
case 0xff24: NR50 = value; break;
case 0xff25: NR51 = value; break;
case 0xff26: if (value&0x80) reset(); NR52 = (value&0x80) | (NR52 & 0x0f); break;
case 0xff30: WaveRAM[0] = value; break;
case 0xff31: WaveRAM[1] = value; break;
case 0xff32: WaveRAM[2] = value; break;
case 0xff33: WaveRAM[3] = value; break;
case 0xff34: WaveRAM[4] = value; break;
case 0xff35: WaveRAM[5] = value; break;
case 0xff36: WaveRAM[6] = value; break;
case 0xff37: WaveRAM[7] = value; break;
case 0xff38: WaveRAM[8] = value; break;
case 0xff39: WaveRAM[9] = value; break;
case 0xff3a: WaveRAM[10] = value; break;
case 0xff3b: WaveRAM[11] = value; break;
case 0xff3c: WaveRAM[12] = value; break;
case 0xff3d: WaveRAM[13] = value; break;
case 0xff3e: WaveRAM[14] = value; break;
case 0xff3f: WaveRAM[15] = value; break;
}
}
uint8_t DIVAPU_envelope_sweep = 0;
uint8_t DIVAPU_length = 0;
uint8_t DIVAPU_CH1_freq_sweep = 0;
void incDIVAPU()
{
DIVAPU++;
DIVAPU_length++;
if (DIVAPU_length==2) {
DIVAPU_length=0;
if (CH1.enabled && CH1.length_enable) {
if (CH1.length_timer==63) {
CH1.enabled = false;
CH1.length_timer=0;
} else {
CH1.length_timer++;
}
}
if (CH2.enabled && CH2.length_enable) {
if (CH2.length_timer==63) {
CH2.enabled = false;
CH2.length_timer=0;
} else {
CH2.length_timer++;
}
}
if (CH3.enabled && CH3.length_enable) {
if (CH3.length_timer==255) {
CH3.enabled = false;
CH3.length_timer = 0;
} else {
CH3.length_timer++;
}
}
if (CH4.enabled && CH4.length_enable) {
if (CH4.length_timer==63) {
CH4.enabled = false;
CH4.length_timer=0;
} else {
CH4.length_timer++;
}
}
}
DIVAPU_CH1_freq_sweep++;
if (DIVAPU_CH1_freq_sweep==4) {
DIVAPU_CH1_freq_sweep=0;
// Do the freq sweep thing
}
DIVAPU_envelope_sweep++;
if (DIVAPU_envelope_sweep==8) {
DIVAPU_envelope_sweep=0;
if ( CH1.enabled && (NR12&0x7) ) { // If sweep pace != 0, envelope sweep is enabled
CH1.envelope_sweep_timer++;
if ( CH1.envelope_sweep_timer == (NR12&0x07) ) { // if timer == envelope sweep, increase or decrease volume
CH1.envelope_sweep_timer=0;
if (NR12&0x8) { // bit set increases, reset decreases
if (CH1.volume<0x0f) CH1.volume++;
} else {
if (CH1.volume>0) CH1.volume--;
}
}
}
if ( CH2.enabled && (NR22&0x7) ) { // If sweep pace != 0, envelope sweep is enabled
CH2.envelope_sweep_timer++;
if ( CH2.envelope_sweep_timer == (NR22&0x07) ) { // if timer == envelope sweep, increase or decrease volume
CH2.envelope_sweep_timer=0;
if (NR22&0x8) { // bit set increases, reset decreases
if (CH2.volume<0x0f) CH2.volume++;
} else {
if (CH2.volume>0) CH2.volume--;
}
}
}
if ( CH4.enabled && (NR42&0x7) ) { // If sweep pace != 0, envelope sweep is enabled
CH4.envelope_sweep_timer++;
if ( CH4.envelope_sweep_timer == (NR42&0x07) ) { // if timer == envelope sweep, increase or decrease volume
CH4.envelope_sweep_timer=0;
if (NR42&0x8) { // bit set increases, reset decreases
if (CH4.volume<0x0f) CH4.volume++;
} else {
if (CH4.volume>0) CH4.volume--;
}
}
}
}
}
uint32_t dots = 0;
uint32_t dotsCH3 = 0;
uint32_t dotsCH4 = 0;
void update(uint32_t dt)
{
dots += dt;
while (dots>=4) {
dots -= 4;
CH1.period_divider++;
if (CH1.period_divider==2048) {
CH1.period_divider = NR13 | ((NR14 &0x7)<<8);
CH1.duty_step++;
if (CH1.duty_step==8) CH1.duty_step=0;
}
CH2.period_divider++;
if (CH2.period_divider==2048) {
CH2.period_divider = NR23 | ((NR24 &0x7)<<8);
CH2.duty_step++;
if (CH2.duty_step==8) CH2.duty_step=0;
}
}
dotsCH3 += dt;
while (dotsCH3>=2) {
dotsCH3 -= 2;
CH3.period_divider++;
if (CH3.period_divider==2048) {
CH3.period_divider = NR33 | ((NR34 &0x7)<<8);
CH3.duty_step++;
if (CH3.duty_step==32) CH3.duty_step=0;
}
}
dotsCH4 += dt;
while (dotsCH4>=64) {
dotsCH4 -= 64;
CH4.period_divider--;
if (CH4.period_divider==0) {
uint8_t clock_shift = ((NR43&0xf0)>>3); if (clock_shift==0) clock_shift = 1;
CH4.period_divider = clock_shift << (NR43&0x07);
CH4.duty_step=getLFSR();
}
}
t_sound += dt;
if (t_sound>=cycles_per_sample) {
t_sound-=cycles_per_sample;
uint16_t sampleCH1 = 0;
if (apu_enabled && DAC1_enabled) sampleCH1 = (duty_cycles[CH1.duty_cycle][CH1.duty_step]*CH1.volume)<<2;
uint16_t sampleCH2 = 0;
if (apu_enabled && DAC2_enabled) sampleCH2 = (duty_cycles[CH2.duty_cycle][CH2.duty_step]*CH2.volume)<<2;
uint16_t sampleCH3 = 0;
if (apu_enabled && DAC3_enabled) {
uint8_t step = CH3.duty_step>>1;
uint8_t actual_sample = (CH3.duty_step&1 ? WaveRAM[step]&0x0f : WaveRAM[step]>>4)<<2;
sampleCH3 = CH3.volume==0 ? 0 : CH3.volume==1 ? actual_sample : CH3.volume==2 ? actual_sample>>1 : actual_sample>>2;
}
uint16_t sampleCH4 = 0;
if (apu_enabled && DAC4_enabled) sampleCH4 = (CH4.duty_step*CH4.volume)<<2;
uint8_t sample = (sampleCH1+sampleCH2+sampleCH3+sampleCH4)&0xff;
sound_buffer[(sound_pos++)&(AUDIO_BUFFER_SIZE-1)] = sample;
//audio_viewer::addsample(sample);
}
if (sound_pos>=1000) {
SDL_QueueAudio(sdlAudioDevice, sound_buffer, sound_pos);
sound_pos = 0;
while (SDL_GetQueuedAudioSize(sdlAudioDevice) > 4096 ) {}
}
}
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
namespace apu
{
uint8_t readRegister(uint16_t address);
void writeRegister(uint16_t address, uint8_t value);
void init();
void reset();
void silence();
void resume();
void incDIVAPU();
void update(uint32_t dt);
}
+39
View File
@@ -0,0 +1,39 @@
#include "audio_viewer.h"
#include <SDL2/SDL.h>
#define NUM_SAMPLES 256
#define MAX_VOLUME 256
namespace audio_viewer
{
SDL_Window *win = nullptr;
SDL_Renderer *ren = nullptr;
//SDL_Texture *tex = nullptr;
uint8_t buffer[NUM_SAMPLES];
uint8_t pos = 0;
uint8_t screen[NUM_SAMPLES*MAX_VOLUME];
void init()
{
win = SDL_CreateWindow("Audio viewer", 1, 1, 256, 256, SDL_WINDOW_SHOWN);
ren = SDL_CreateRenderer(win, -1, 0);
//tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 256, 256);
for (int i=0; i<NUM_SAMPLES; ++i) buffer[i] = 0;
}
void addsample(uint8_t sample)
{
buffer[pos] = sample;
pos = (pos+1)%NUM_SAMPLES;
}
void refresh()
{
SDL_SetRenderDrawColor(ren, 0, 0, 0, 0);
SDL_RenderClear(ren);
SDL_SetRenderDrawColor(ren, 255, 255, 255, 255);
for (int i=0; i<NUM_SAMPLES;++i) {
SDL_RenderDrawLine(ren, i, buffer[(pos+i)%NUM_SAMPLES], i+1, buffer[(pos+i+1)%NUM_SAMPLES]);
}
SDL_RenderPresent(ren);
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <stdint.h>
namespace audio_viewer
{
void init();
void addsample(uint8_t sample);
void refresh();
}
+1165
View File
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
#pragma once
#include <stdint.h>
namespace debug
{
void init();
void show();
void focus();
void hide();
void pause();
void stop();
void cont();
const bool debugging();
const bool paused();
void setmemmodified(const uint16_t addr);
void refresh();
void sendToConsole(const char* text);
void sendToConsoleLog(const char *text);
void sendMoreToConsoleLog(const char *text);
void DeleteCharConsole();
void executeConsole();
const bool isbreak(const uint16_t address, const uint8_t type=1);
uint32_t next();
uint32_t stepout();
void savestate(const char *filename);
void loadstate(const char *filename);
void loadngo(const char* filename, const char* addr);
void setcursor(const uint16_t address);
void cursorfwd();
void cursorback();
void useOpcode(const uint8_t opcode, const uint8_t base);
void clearUsedOpcodes();
void markUsedOpcodes();
const int getNumOpcodesUsed();
void printOpcodesUsed();
void search(const char *seq=nullptr);
namespace history
{
void store();
void gototop();
void goforward();
void goback();
}
}
+216
View File
@@ -0,0 +1,216 @@
#include "display.h"
#include <cstring>
#include "sm83.h"
#include "ppu.h"
#include <SDL2/SDL.h>
#include "ui_window.h"
#include "debug.h"
#include "ui.h"
namespace display
{
uint32_t palette[2][4] = {
{ 0xFFFFFF, 0xAAAAAA, 0x555555, 0x000000 },
{ 0xA0AA05, 0x749527, 0x487F49, 0x2E4326/*0x1D6A6B*/ }
};
SDL_Window *win = nullptr;
SDL_Renderer *ren = nullptr;
SDL_Texture *tex = nullptr;
SDL_Texture *uitex = nullptr;
uint8_t zoom = 3;
uint8_t use_palette=1;
bool fullscreen = false;
int fullscreen_scale = 1;
SDL_Rect dest_rect;
bool eventHandler(SDL_Event *e)
{
if (e->type==SDL_WINDOWEVENT) {
if (e->window.event==SDL_WINDOWEVENT_CLOSE) {
return false;
} else if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) {
redraw();
}
}
if (!debug::debugging()) {
if (debug::paused()) {
if (e->type == SDL_KEYDOWN) {
if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) {
const uint8_t dt = sm83::step();
debug::cont();
ppu::refresh(dt);
}
}
} else {
if (e->type == SDL_KEYDOWN) {
if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) {
debug::pause();
display::redraw();
} else if (e->key.keysym.scancode==SDL_SCANCODE_F1) {
display::decZoom();
} else if (e->key.keysym.scancode==SDL_SCANCODE_F2) {
display::incZoom();
} else if (e->key.keysym.scancode==SDL_SCANCODE_F3) {
display::toggleFullscreen();
} else if (e->key.keysym.scancode==SDL_SCANCODE_F6) {
//zx_tape::play();
} else if (e->key.keysym.scancode==SDL_SCANCODE_F7) {
//zx_tape::rewind();
} else if (e->key.keysym.scancode==SDL_SCANCODE_Q && e->key.keysym.mod & KMOD_CTRL) {
return false;
}
}
}
}
if (e->type == SDL_MOUSEMOTION) {
SDL_ShowCursor(true);
}
return true;
}
void reinit()
{
if (win) ui::window::unregisterWindow(SDL_GetWindowID(win));
if (tex) SDL_DestroyTexture(tex);
if (uitex) SDL_DestroyTexture(uitex);
if (ren) SDL_DestroyRenderer(ren);
if (win) SDL_DestroyWindow(win);
const int z = fullscreen ? 1 : zoom;
win = SDL_CreateWindow("Gameboy Screen", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 160*z, 144*z, fullscreen?SDL_WINDOW_FULLSCREEN_DESKTOP:SDL_WINDOW_SHOWN);
ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 160, 144);
uitex = ui::createtexture(ren);
ui::window::registerWindow(SDL_GetWindowID(win), eventHandler);
if (fullscreen)
{
int w, h;
SDL_GetWindowSize(win, &w, &h);
fullscreen_scale = h/144;
dest_rect.w = 160 * fullscreen_scale;
dest_rect.h = 144 * fullscreen_scale;
dest_rect.x = (w - dest_rect.w)/2;
dest_rect.y = (h - dest_rect.h)/2;
}
else
{
dest_rect.x = dest_rect.y = 0;
dest_rect.w = 160 * zoom;
dest_rect.h = 144 * zoom;
}
focus();
}
void init()
{
reinit();
}
void focus()
{
if (win)
{
SDL_RaiseWindow(win);
redraw();
}
}
void redraw(const bool present)
{
//if (zx_tape::getplaying() && zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD)) return;
ui::setrenderer(ren, uitex);
const uint8_t *ppu_pixels = ppu::getpixels();
Uint32* pixels;
int pitch;
SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch);
for (int i=0; i<160*144;++i) *(pixels++) = palette[use_palette][ppu_pixels[i]];
SDL_UnlockTexture(tex);
if (fullscreen)
{
SDL_SetRenderDrawColor(ren, 0, 0, 0, 255);
SDL_RenderClear(ren);
}
// Pintem la textura a pantalla
SDL_RenderCopy(ren, tex, NULL, &dest_rect);
// Pintem les ralles dels pixels
if (zoom>2 || fullscreen) {
SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(ren, 160, 170, 5, 96);
const float scale = fullscreen ? fullscreen_scale : zoom;
for (int i=0;i<144;++i) SDL_RenderDrawLine(ren, dest_rect.x, dest_rect.y+i*scale, dest_rect.x+159*scale, dest_rect.y+i*scale);
for (int i=0;i<160;++i) SDL_RenderDrawLine(ren, dest_rect.x+i*scale, dest_rect.y, dest_rect.x+i*scale, dest_rect.y+143*scale);
}
if (present)
SDL_RenderPresent(ren);
else
{
SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(ren, 0, 0, 0, 128);
SDL_Rect rect {0,0,160*zoom,144*zoom};
if (fullscreen) SDL_GetWindowSize(win, &rect.w, &rect.h);
SDL_RenderFillRect(ren, &rect);
}
}
void present()
{
SDL_RenderPresent(ren);
}
void setTitle(const char* title)
{
char tmp[256];
strcpy(tmp, "Gameboy Screen");
strcat(tmp, title);
SDL_SetWindowTitle(win, tmp);
}
void setZoom(const int value)
{
if (fullscreen) return;
if (value < 1) return;
SDL_DisplayMode dm;
SDL_GetCurrentDisplayMode(0, &dm);
if (160*value > dm.w) return;
if (144*value > dm.h) return;
zoom = value;
reinit();
}
void incZoom()
{
if (fullscreen) return;
setZoom(zoom+1);
}
void decZoom()
{
if (fullscreen) return;
setZoom(zoom-1);
}
void toggleFullscreen()
{
fullscreen = !fullscreen;
reinit();
}
const bool getFullscreen()
{
return fullscreen;
}
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
namespace display
{
void init();
void reinit();
void focus();
void redraw(const bool present=true);
void present();
void setTitle(const char* title);
void incZoom();
void decZoom();
void toggleFullscreen();
const bool getFullscreen();
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#define INTERRUPT_VBLANK 0x01
#define INTERRUPT_LCD 0x02
#define INTERRUPT_TIMER 0x04
#define INTERRUPT_SERIAL 0x08
#define INTERRUPT_JOYPAD 0x10
+213
View File
@@ -0,0 +1,213 @@
#include <stdint.h>
#include <stdio.h>
#include "sm83.h"
#include "mem.h"
#include "ppu.h"
#include "sm83dis.h"
#include "debug.h"
#include "display.h"
#include <SDL2/SDL.h>
#include <string.h>
#include "ui.h"
#include "ui_menu.h"
#include "ui_window.h"
uint32_t time = 0;
uint32_t t_states = 0;
namespace actions
{
void exitMenu()
{
const uint8_t dt = sm83::step();
debug::cont();
ppu::refresh(dt);
}
int decZoom(int value)
{
display::decZoom();
return 0;
}
int incZoom(int value)
{
display::incZoom();
return 0;
}
int fullscreen(int value)
{
display::toggleFullscreen();
return display::getFullscreen();
}
int showAnalyzer(int value)
{
//z80analyze::show();
return 0;
}
}
int main(int argc, char *argv[])
{
if (argc < 2) { printf("ABORTING: No rom specified.\n"); exit(1); }
const uint32_t clock = 4194304;
const uint32_t update_freq = clock >> 3;
SDL_Init(SDL_INIT_EVERYTHING);
FILE *f = fopen(argv[1], "rb");
if (!f) { printf("ABORTING: Rom not found.\n"); exit(1); }
fseek(f, 0, SEEK_END);
const int filesize = ftell(f);
fseek(f, 0, SEEK_SET);
uint8_t *buffer = (uint8_t*)malloc(filesize);
fread(buffer, filesize, 1, f);
fclose(f);
mem::init(buffer, filesize);
sm83dis::loadSymbols();
sm83::reset();
ppu::init();
display::init();
debug::init();
ui::menu::init();
ui::menu::setexitcallback(actions::exitMenu);
int menu = ui::menu::addsubmenu("FILE");
ui::menu::addoption(menu, "LOAD ROM", nullptr);
ui::menu::addoption(menu, "SAVE ROM", nullptr);
ui::menu::addseparator(menu);
ui::menu::addoption(menu, "LOAD STATE", nullptr);
ui::menu::addoption(menu, "SAVE STATE", nullptr);
menu = ui::menu::addsubmenu("SCREEN");
ui::menu::addoption(menu, "DEC ZOOM", actions::decZoom);
ui::menu::addoption(menu, "INC ZOOM", actions::incZoom);
ui::menu::addbooloption(menu, "FULLSCREEN", display::getFullscreen(), actions::fullscreen);
menu = ui::menu::addsubmenu("EMULATION");
ui::menu::addbooloption(menu, "STOP ON INVALID OP", sm83::getOption(SM83_OPTION_STOP_ON_INVALID), actions::decZoom);
ui::menu::addoption(menu, "SHOW ANALYZER", actions::showAnalyzer);
//zx_ula::sound_init();
//debug::stop();
bool should_exit = false;
SDL_Event e;
time = SDL_GetTicks();
t_states = 0;
while (!should_exit)
{
while (SDL_PollEvent(&e))
{
bool result = true;
if (e.type == SDL_QUIT) { should_exit=true; break; }
else if (e.type == SDL_MOUSEBUTTONDOWN) result = ui::window::sendEvent(e.button.windowID, &e);
else if (e.type == SDL_MOUSEBUTTONUP) result = ui::window::sendEvent(e.button.windowID, &e);
else if (e.type == SDL_MOUSEMOTION) result = ui::window::sendEvent(e.motion.windowID, &e);
else if (e.type == SDL_WINDOWEVENT) result = ui::window::sendEvent(e.window.windowID, &e);
else if (e.type == SDL_MOUSEWHEEL) result = ui::window::sendEvent(e.wheel.windowID, &e);
else if (e.type == SDL_TEXTINPUT) result = ui::window::sendEvent(e.text.windowID, &e);
else if (e.type == SDL_KEYDOWN) {
if (e.key.keysym.scancode==SDL_SCANCODE_F5) {
if (debug::debugging()) {
debug::history::gototop();
const uint8_t dt = sm83::step();
debug::cont();
ppu::refresh(dt);
}
} else if (e.key.keysym.scancode==SDL_SCANCODE_F8) {
if (!debug::debugging()) {
debug::stop();
display::redraw();
} else {
debug::show();
}
} else if (e.key.keysym.scancode==SDL_SCANCODE_F10) {
if (debug::debugging()) {
debug::show();
debug::history::gototop();
const uint8_t dt = sm83::step();
debug::refresh();
ppu::refresh(dt);
display::redraw();
//z80analyze::refresh();
}
} else if (e.key.keysym.scancode==SDL_SCANCODE_F11) {
if (debug::debugging()) {
debug::show();
debug::history::gototop();
const uint8_t dt = debug::next();
debug::refresh();
ppu::refresh(dt);
display::redraw();
//z80analyze::refresh();
}
} else if (e.key.keysym.scancode==SDL_SCANCODE_F12) {
if (debug::debugging()) {
debug::show();
debug::history::gototop();
const uint8_t dt = debug::stepout();
debug::refresh();
ppu::refresh(dt);
display::redraw();
//z80analyze::refresh();
}
}
result = ui::window::sendEvent(e.key.windowID, &e);
}
else if (e.type == SDL_MOUSEBUTTONUP && e.button.button==1) ui::setClicked(true);
if (!result)
should_exit = true; break;
}
if (!debug::debugging() && !debug::paused()) {
bool fastload=false;
// En cada bucle fem 10 pasos de la CPU, sino s'ofega
for (int i=0;i<20;++i) {
if (debug::isbreak(sm83::getPC(), 9)) {
debug::stop();
display::redraw();
break;
} else {
uint8_t dt = sm83::step();
t_states += dt;
//zx_ula::sound_update(dt);
ppu::refresh(dt);
if (debug::debugging()) break;
}
}
if (t_states>=update_freq)
{
while (SDL_GetTicks()<(time+125)) {}
t_states -= update_freq;
time = SDL_GetTicks();
//z80analyze::refresh();
}
//z80analyze::refresh(true);
} else if (!debug::debugging() && debug::paused()) {
display::redraw(false);
ui::menu::show();
display::present();
}
ui::setClicked(false);
}
return 0;
}
+82
View File
@@ -0,0 +1,82 @@
#include "mbc1.h"
#include "mem.h"
#include "sm83.h"
#include <stdlib.h>
#include <SDL2/SDL.h>
namespace mbc1
{
#define ROM_BANK_SIZE 0x4000
#define RAM_BANK_SIZE 0x2000
uint8_t *rom;
uint8_t exram[4 * RAM_BANK_SIZE];
uint8_t current_rom_bank = 1;
uint8_t current_ram_bank = 0;
bool ram_enabled = false;
bool banking_mode = false; // false = ROM banking mode, true = RAM banking mode
void reset()
{
for (int i=0; i<4*RAM_BANK_SIZE; ++i) { exram[i] = 0; }
}
uint8_t readRom(uint16_t address)
{
if (address < 0x4000) {
// ROM Bank 0
return rom[address];
} else {
// Switchable ROM bank
uint32_t banked_address = (current_rom_bank * ROM_BANK_SIZE) + (address - 0x4000);
return rom[banked_address];
}
}
void writeRom(uint16_t address, uint8_t value)
{
if (address < 0x2000) {
// Enable/disable RAM
ram_enabled = (value & 0x0F) == 0x0A;
} else if (address < 0x4000) {
// Select ROM bank
value &= 0x1F; // Lower 5 bits are used
if (value == 0) value = 1; // Bank 0 is not allowed
current_rom_bank = (current_rom_bank & 0x60) | value;
} else if (address < 0x6000) {
// Select RAM bank or upper bits of ROM bank
if (banking_mode) {
current_ram_bank = value & 0x03; // 2 bits for RAM bank
} else {
current_rom_bank = (current_rom_bank & 0x1F) | ((value & 0x03) << 5);
}
} else {
// Select banking mode
banking_mode = value & 0x01;
}
}
uint8_t readRam(uint16_t address)
{
if (ram_enabled) {
uint32_t banked_address = (current_ram_bank * RAM_BANK_SIZE) + (address - 0xa000);
return exram[banked_address];
}
return 0xff; // Return open bus value when RAM is disabled
}
void writeRam(uint16_t address, uint8_t value)
{
if (ram_enabled) {
uint32_t banked_address = (current_ram_bank * RAM_BANK_SIZE) + (address - 0xa000);
exram[banked_address] = value;
}
}
void init(uint8_t *rom, uint32_t rom_size, uint32_t ram_size)
{
mbc1::rom = rom;
reset();
}
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include <stdint.h>
namespace mbc1
{
void init(uint8_t *rom, uint32_t rom_size, uint32_t ram_size);
void reset();
uint8_t readRom(uint16_t address);
void writeRom(uint16_t address, uint8_t value);
uint8_t readRam(uint16_t address);
void writeRam(uint16_t address, uint8_t value);
}
+128
View File
@@ -0,0 +1,128 @@
#include "mbc3.h"
#include "mem.h"
#include "sm83.h"
#include <stdlib.h>
#include <SDL2/SDL.h>
namespace mbc3
{
#define ROM_BANK_SIZE 0x4000
#define RAM_BANK_SIZE 0x2000
uint8_t *rom;
uint8_t exram[4 * RAM_BANK_SIZE];
uint8_t current_rom_bank = 1;
uint8_t current_ram_bank = 0;
bool ram_enabled = false;
bool rtc_mode = false; // false = ROM banking mode, true = RAM banking mode
uint8_t rtc_reg = 0;
uint8_t rtc_latch = 1;
uint8_t RTC[5] {0,0,0,0,0};
uint8_t RTC_latched[5] {0,0,0,0,0};
void reset()
{
for (int i=0; i<4*RAM_BANK_SIZE; ++i) { exram[i] = 0; }
}
void tick()
{
RTC[0]++;
if (RTC[0]==60) {
RTC[0] = 0;
RTC[1]++;
if (RTC[1]==60) {
RTC[1] = 0;
RTC[2]++;
if (RTC[2]==24) {
if (RTC[3]==0xff) {
RTC[3]=0;
if (RTC[4]&0x01) {
RTC[4] = RTC[4] & 0xfe;
RTC[4] = (RTC[4] & 0x80) ? RTC[4] & 0x7f : RTC[4] | 0x80;
} else {
RTC[4] = RTC[4] | 0x01;
}
} else RTC[3]++;
}
}
}
}
void latchClock()
{
for (int i=0; i<5; ++i) RTC_latched[i] = RTC[i];
}
uint8_t readRom(uint16_t address)
{
if (address < 0x4000) {
// ROM Bank 0
return rom[address];
} else {
// Switchable ROM bank
uint32_t banked_address = (current_rom_bank * ROM_BANK_SIZE) + (address - 0x4000);
return rom[banked_address];
}
}
void writeRom(uint16_t address, uint8_t value)
{
if (address < 0x2000) {
// Enable/disable RAM
ram_enabled = (value & 0x0F) == 0x0A;
} else if (address < 0x4000) {
// Select ROM bank
if (value == 0) value = 1; // Bank 0 is not allowed
current_rom_bank = current_rom_bank = (value & 0x7F);
} else if (address < 0x6000) {
// Select RAM bank or upper bits of ROM bank
if (value<=0x03) {
rtc_mode = false;
current_ram_bank = value & 0x03; // 2 bits for RAM bank
} else if ( (value >= 0x8) && (value <= 0x0c) ) {
rtc_mode = true;
rtc_reg = value-8;
}
} else {
// Select banking mode
if ( (rtc_latch == 0) && (value == 1) ) {
latchClock();
}
rtc_latch = value;
}
}
uint8_t readRam(uint16_t address)
{
if (ram_enabled) {
if (rtc_mode) {
return RTC_latched[rtc_reg];
} else {
uint32_t banked_address = (current_ram_bank * RAM_BANK_SIZE) + (address - 0xa000);
return exram[banked_address];
}
}
return 0xff; // Return open bus value when RAM is disabled
}
void writeRam(uint16_t address, uint8_t value)
{
if (ram_enabled) {
if (rtc_mode) {
RTC[rtc_reg] = RTC_latched[rtc_reg] = value;
} else {
uint32_t banked_address = (current_ram_bank * RAM_BANK_SIZE) + (address - 0xa000);
exram[banked_address] = value;
}
}
}
void init(uint8_t *rom, uint32_t rom_size, uint32_t ram_size)
{
mbc3::rom = rom;
reset();
}
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
namespace mbc3
{
void init(uint8_t *rom, uint32_t rom_size, uint32_t ram_size);
void tick();
void reset();
uint8_t readRom(uint16_t address);
void writeRom(uint16_t address, uint8_t value);
uint8_t readRam(uint16_t address);
void writeRam(uint16_t address, uint8_t value);
}
+22
View File
@@ -0,0 +1,22 @@
#include "mbc_none.h"
namespace mbc_none
{
uint8_t *rom;
void reset() { } // nothing to do
uint8_t readRom(uint16_t address) { return rom[address]; }
void writeRom(uint16_t address, uint8_t value) { } // do nothing
uint8_t readRam(uint16_t address) { return 0xff; }
void writeRam(uint16_t address, uint8_t value) { } // do nothing
void init(uint8_t *rom, uint32_t rom_size, uint32_t ram_size)
{
mbc_none::rom = rom;
reset();
}
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include <stdint.h>
namespace mbc_none
{
void init(uint8_t *rom, uint32_t rom_size, uint32_t ram_size);
void reset();
uint8_t readRom(uint16_t address);
void writeRom(uint16_t address, uint8_t value);
uint8_t readRam(uint16_t address);
void writeRam(uint16_t address, uint8_t value);
}
+271
View File
@@ -0,0 +1,271 @@
#include "mem.h"
#include "sm83.h"
#include <stdlib.h>
#include <stdio.h>
#include <SDL2/SDL.h>
#include "apu.h"
#include "mbc_none.h"
#include "mbc1.h"
#include "mbc3.h"
#define DIV hram[0x104] // 0xff04 - 0xfe00
#define TIMA hram[0x105] // 0xff05 - 0xfe00
#define TMA hram[0x106] // 0xff06 - 0xfe00
#define TAC hram[0x107] // 0xff07 - 0xfe00
namespace mem
{
void (*resetMbc)(void);
uint8_t (*readRom)(uint16_t);
void (*writeRom)(uint16_t, uint8_t);
uint8_t (*readRam)(uint16_t);
void (*writeRam)(uint16_t, uint8_t);
void (*tick)(void) = nullptr;;
uint8_t bootrom[256];
uint8_t *rom;
uint8_t vram[8192];
uint8_t wram[8192];
uint8_t hram[512];
uint8_t tags[65536];
char *title = nullptr;
uint8_t mapper_type = 0;
uint32_t rom_size = 0;
uint32_t ram_size = 0;
uint16_t timer = 0;
uint16_t dma_address = 0;
uint8_t dma_pos = 160;
uint16_t dma_dots = 0;
uint16_t div_counter = 0;
uint16_t timer_counter = 0;
void init(uint8_t* rom, const int size)
{
title = (char*)&rom[0x134];
mapper_type = rom[0x147];
rom_size = 32768 * (1 << rom[0x148]);
int sizes[] = { 0, 0, 8, 32, 128, 64};
ram_size = sizes[rom[0x149]] * 1024;
switch (mapper_type)
{
case 0x00:
mbc_none::init(rom, rom_size, ram_size);
mem::resetMbc = mbc_none::reset;
mem::readRom = mbc_none::readRom;
mem::writeRom = mbc_none::writeRom;
mem::readRam = mbc_none::readRam;
mem::writeRam = mbc_none::writeRam;
break;
case 0x01:
case 0x02:
case 0x03:
mbc1::init(rom, rom_size, ram_size);
mem::resetMbc = mbc1::reset;
mem::readRom = mbc1::readRom;
mem::writeRom = mbc1::writeRom;
mem::readRam = mbc1::readRam;
mem::writeRam = mbc1::writeRam;
break;
case 0x0f:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
mbc3::init(rom, rom_size, ram_size);
mem::resetMbc = mbc3::reset;
mem::readRom = mbc3::readRom;
mem::writeRom = mbc3::writeRom;
mem::readRam = mbc3::readRam;
mem::writeRam = mbc3::writeRam;
mem::tick = mbc3::tick;
break;
};
apu::init();
}
void reset()
{
FILE *f = fopen("dmg_boot.bin", "rb");
if (!f) { printf("ABORTING: 'dmg_boot.bin' not found!\n"); exit(1); }
fseek(f, 0, SEEK_END);
const int size = ftell(f);
fseek(f, 0, SEEK_SET);
fread(bootrom, size, 1, f);
fclose(f);
for (int i=0; i<8192; ++i) { vram[i] = 0; }
for (int i=0; i<8192; ++i) { wram[i] = 0; }
for (int i=0; i<512; ++i) { hram[i] = 0; }
for (int i=0; i<65536; ++i) { tags[i] = MEMTAG_NONE; }
resetMbc();
apu::reset();
}
uint8_t getKeypad(uint8_t value)
{
const uint8_t *keys = SDL_GetKeyboardState(NULL);
value = value & 0xf0;
if (value & 0x10) {
if (!keys[SDL_SCANCODE_RETURN]) value = value | 0x8;
if (!keys[SDL_SCANCODE_SPACE]) value = value | 0x4;
if (!keys[SDL_SCANCODE_Z]) value = value | 0x2;
if (!keys[SDL_SCANCODE_X]) value = value | 0x1;
} else if (value & 0x20) {
if (!keys[SDL_SCANCODE_DOWN]) value = value | 0x8;
if (!keys[SDL_SCANCODE_UP]) value = value | 0x4;
if (!keys[SDL_SCANCODE_LEFT]) value = value | 0x2;
if (!keys[SDL_SCANCODE_RIGHT]) value = value | 0x1;
} else {
value = value | 0x0f;
}
return value;
}
uint8_t readMem(uint16_t address)
{
if (address < 0x8000) {
if ( (address < 0x0100) && ((hram[0x150]&0x01)==0) ) return bootrom[address];
return readRom(address);
} else if (address < 0xA000) {
return vram[address - 0x8000];
} else if (address < 0xC000) {
return readRam(address);
} else if (address < 0xE000) {
return wram[address - 0xC000];
} else if (address < 0xFE00) {
return wram[address - 0xE000];
} else {
if (address==0xFF00) {
hram[address - 0XFE00] = getKeypad(hram[address - 0XFE00]);
} else if (address>=0xFF10 && address<=0xFF3F) { return apu::readRegister(address); }
return hram[address - 0XFE00];
}
}
void writeMem(uint16_t address, uint8_t value)
{
if (address < 0x8000) {
writeRom(address, value);
} else if (address < 0xA000) {
vram[address - 0x8000] = value;
} else if (address < 0xC000) {
writeRam(address, value);
} else if (address < 0xE000) {
wram[address - 0xC000] = value;
} else if (address < 0xFE00) {
wram[address - 0xE000] = value;
} else {
if ( (address==0xFF50) && ((value&0x01) != 1) ) {
return; //Only allow disabling boot room
} else if ( (address==0xFF00) ) {
value = value & 0x30;
} else if ( (address==0xFF04) ) {
hram[address-0xFE00] = 0;
return;
} else if ( (address==0xFF0F) ) { // IF
hram[address-0xFE00] = value;
sm83::processInterrupts();
return;
} else if (address>=0xFF10 && address<=0xFF3F) { // APU
apu::writeRegister(address, value);
return;
} else if ( (address==0xFF46) ) { // OAM DMA
mem::init_dma_transfer(value);
}
hram[address - 0xFE00] = value;
}
}
uint8_t getTag(uint16_t address)
{
return tags[address];
}
void setTag(uint16_t address, uint8_t value)
{
tags[address] = value;
}
void saveState(FILE* f)
{
}
void loadState(FILE* f)
{
}
uint8_t *rawVram()
{
return vram;
}
uint8_t *rawHram(uint16_t address)
{
return &hram[address-0xfe00];
}
void init_dma_transfer(uint8_t source)
{
dma_address = source << 8;
dma_pos = 0;
dma_dots = 0;
}
uint16_t timer_frequencies[4] { 256*4, 4*4, 16*4, 64*4 };
void update_mapped(const uint32_t dt)
{
timer+=dt;
if (timer >= 4194304 ) {
timer -= 4194304;
if (mem::tick) mem::tick();
}
// DIV Divider register (0xFF04) (val com a timer bàsic)
div_counter += dt;
if (div_counter>=256) {
div_counter -= 256;
bool bitset = DIV&0x10;
DIV++;
if (bitset && !(DIV&0x10)) apu::incDIVAPU();
}
// Timer
if (TAC&0x4) { // if bit 3 of mem(0xff07) is 1, timer enabled
uint16_t freq = timer_frequencies[TAC&0x03];
timer_counter += dt;
if (timer_counter>=freq) {
timer_counter -= freq;
if (TIMA<255)
TIMA++;
else {
TIMA = TMA;
sm83::interrupt(INTERRUPT_TIMER);
}
}
}
// APU
apu::update(dt);
// OAM DMA
if (dma_pos<160) {
dma_dots += dt;
while (dma_dots >= 4 && dma_pos<160) {
dma_dots -= 4;
hram[dma_pos] = mem::readMem(dma_address|dma_pos);
dma_pos++;
}
}
}
}
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include <stdint.h>
#include <stdio.h>
namespace mem
{
#define MEMTAG_NONE 0x00
#define MEMTAG_DATA 0x01
#define MEMTAG_INST 0x02
#define MEMTAG_CODE 0x04
#define MEMTAG_IGNORE 0x08
#define MEMTAG_TDATA 0x10
#define MEMTAG_TINST 0x20
#define MEMTAG_TREPEAT 0x40
#define MEMTAG_MODIFIED 0x80
#define MEMTAG_KNOWN 0x07
#define MEMTAG_TOUCHED 0x70
#define MBC_NONE 0
#define MBC1 1
#define MBC2 2
#define MBC3 3
#define MBC5 5
#define MBC6 6
#define MBC7 7
void init(uint8_t* rom, const int size);
void reset();
uint8_t readMem(uint16_t address);
void writeMem(uint16_t address, uint8_t value);
uint8_t getTag(uint16_t address);
void setTag(uint16_t address, uint8_t value);
void saveState(FILE* f);
void loadState(FILE* f);
uint8_t *rawVram();
uint8_t *rawHram(uint16_t address);
void init_dma_transfer(uint8_t source);
void update_mapped(const uint32_t dt);
}
+282
View File
@@ -0,0 +1,282 @@
#include "ppu.h"
#include "mem.h"
#include "interrupts.h"
#include "display.h"
namespace ppu
{
struct oam_entry_t
{
uint8_t y, x, tile, attr;
};
uint32_t t_states_total = 70224;
uint32_t t_states_per_scanline = 456;
uint32_t vsync_lines = 10;
uint32_t t_screen = 0;
uint8_t pixels[160*144];
uint16_t dots_in_scanline = 0;
uint8_t line_buffer[160];
uint8_t *_LCDC = nullptr;
uint8_t *_STAT = nullptr;
uint8_t *_SCY = nullptr;
uint8_t *_SCX = nullptr;
uint8_t *_LY = nullptr;
uint8_t *_LYC = nullptr;
uint8_t *_BGP = nullptr;
uint8_t *_WY = nullptr;
uint8_t *_WX = nullptr;
uint8_t *OBP = nullptr;
oam_entry_t *oam = nullptr;
uint8_t *vram = nullptr;
#define LCDC (*_LCDC)
#define STAT (*_STAT)
#define SCY (*_SCY)
#define SCX (*_SCX)
#define LY (*_LY)
#define LYC (*_LYC)
#define BGP (*_BGP)
#define WY (*_WY)
#define WX (*_WX)
#define IF 0xff0f
bool last_interrupt_lcd_state = false;
void init()
{
_LCDC = mem::rawHram(0xff40);
_STAT = mem::rawHram(0xff41);
_SCY = mem::rawHram(0xff42);
_SCX = mem::rawHram(0xff43);
_LY = mem::rawHram(0xff44);
_LYC = mem::rawHram(0xff45);
_BGP = mem::rawHram(0xff47);
_WY = mem::rawHram(0xff4a);
_WX = mem::rawHram(0xff4b);
OBP = mem::rawHram(0xff48);
oam = (oam_entry_t*)mem::rawHram(0xfe00);
vram = mem::rawVram();
}
void fill_line_buffer_bkg()
{
if ((LCDC & 0x1) == 0) {
for (int i=0; i<160; ++i) line_buffer[i]=0;
return;
}
const uint16_t ty = uint8_t(SCY+LY) >> 3;
const uint8_t ly = uint8_t(SCY+LY) & 0x7;
uint16_t tx = SCX >> 3;
uint8_t ox = SCX & 0x7;
uint16_t base_tilemap_address = LCDC&0x8 ? 0x1c00 : 0x1800;
int pi = 0;
while(true) {
uint16_t tilemap_address = base_tilemap_address + tx + (ty<<5);
uint16_t tile = vram[tilemap_address];
uint16_t base_tile_address = 0x0000;
if ( ((LCDC&0x10)==0) && (tile<128) ) base_tile_address = 0x1000;
uint16_t tile_address = base_tile_address + (tile<<4) + (ly*2);
uint8_t a = vram[tile_address];
uint8_t b = vram[tile_address+1];
for (int i=0; i<8; ++i) {
if (ox==0) {
uint8_t index = (a&0x80 ? 1 : 0) + (b&0x80 ? 2 : 0 );
line_buffer[pi++] = (BGP >> (index*2)) & 0x3;
} else {
ox--;
}
a=a<<1; b=b<<1;
if (pi==160) return;
}
tx = (tx+1)&0x1f;
}
}
void fill_line_buffer_win()
{
if ((LCDC & 0x21) != 0x21) return;
if (LY<WY) return;
const uint16_t ty = uint8_t(LY-WY) >> 3;
const uint8_t ly = uint8_t(LY-WY) & 0x7;
uint8_t ox = WX<7 ? 7-WX : 0;
uint16_t tx = 0;
uint16_t base_tilemap_address = LCDC&0x40 ? 0x1c00 : 0x1800;
int pi = WX<7 ? 0 : WX-7;
while(true) {
uint16_t tilemap_address = base_tilemap_address + tx + (ty<<5);
uint16_t tile = vram[tilemap_address];
uint16_t base_tile_address = 0x0000;
if ( ((LCDC&0x10)==0) && (tile<128) ) base_tile_address = 0x1000;
uint16_t tile_address = base_tile_address + (tile<<4) + (ly*2);
uint8_t a = vram[tile_address];
uint8_t b = vram[tile_address+1];
for (int i=0; i<8; ++i) {
if (ox==0) {
uint8_t index = (a&0x80 ? 1 : 0) + (b&0x80 ? 2 : 0 );
line_buffer[pi++] = (BGP >> (index*2)) & 0x3;
} else {
ox--;
}
a=a<<1; b=b<<1;
if (pi==160) return;
}
tx = (tx+1)&0x1f;
}
}
void fill_line_buffer_obj()
{
if ((LCDC & 0x2) == 0) return;
const uint8_t height = (LCDC & 0x4) ? 16 : 8;
uint8_t obj_list[10] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
int num_obj_found=0;
int obj=0;
// Revisem els 40 posibles sprites o fins a trobar 10...
while (obj<40 && num_obj_found<10) {
// Si el sprite està tocant la linea actual l'afegim a la llista
if ( (LY+16 >= (oam[obj].y)) && (LY+16 < (oam[obj].y+height)) ) {
obj_list[num_obj_found]=obj;
num_obj_found++;
}
obj++;
}
// Pintem els sprites en el buffer de sprites
uint8_t pixels[160];
uint8_t x_pos[160];
for (int i=0;i<160;++i) { pixels[i] = 255; x_pos[i] = 255; }
obj=0;
while (obj_list[obj] != 255) {
oam_entry_t *o = &oam[obj_list[obj]];
const uint8_t ly = uint8_t(LY-o->y) & 0xf;
uint16_t tile = height==8 ? o->tile : o->tile & 0xFE; // si es dos tiles de alt, el primer sempre comença en numero parell
uint8_t yflip = o->attr&0x40 ? (height-1)-ly : ly; // està invertit verticalment?
uint16_t tile_address = 0x0000 + (tile<<4) + (yflip*2);
uint8_t a = vram[tile_address];
uint8_t b = vram[tile_address+1];
for (int i=0; i<8; ++i) { // Per a cada pixel de la linea del tile...
if (o->x+i>=168) break; // Si ja estem fora de la pantalla per la dreta, eixim del bucle
if (o->x+i>=8) { // Si està dins de la pantalla...
if (x_pos[o->x+i-8]>o->x) { // Si te una x menor que la que tenía
//uint8_t xflip = o->attr&0x20 ? 8 : 0; // està invertit horitzontalment?
const uint8_t ppos = 1 << ( o->attr&0x20 ? i : 7-i);
const uint8_t val = (a&ppos ? 1 : 0) + (b&ppos ? 2 : 0 ); // agafem el pixel que toca
if (val) { // Si el pixel no es transparent...
const uint8_t color = (OBP[(o->attr>>4)&1] >> (val*2)) & 0x3;
pixels[o->x+i-8] = color | o->attr&0x80;; // el pintem al buffer, amb el flag de prioritat respecte al BKG
x_pos[o->x+i-8] = o->x; // I apuntem la seua x per a comparar després
}
}
}
}
obj++;
}
// Per últim, volquem els pixels que toque al buffer de linea
for (int i=0; i<160; ++i) {
if (pixels[i]!=255) { // si el pixel no es transparent...
if ( !(pixels[i]&0x80) || (line_buffer[i]==0) ) { // Si te prioritat o el color de fondo es 0...
line_buffer[i] = pixels[i]&0x03; // pintem el pixel (llevant el flag de prioritat)
}
}
}
}
void refresh(const uint32_t dt, const bool full)
{
if ((LCDC&0x80)==0) return;
for (int i=0;i<dt;++i)
{
// Açò va volcant els pixels del line_buffer en pantalla
if ( (STAT&0x3)==3) {
uint16_t current_pixel = dots_in_scanline-80;
if (current_pixel<160) {
pixels[current_pixel+LY*160] = line_buffer[current_pixel];
}
}
// gestió de en quin dot i linea estem, i tot el que ha de passar
//uint8_t interrupts = 0x00;
bool current_interrupt_lcd_state = false;
dots_in_scanline++;
if ( (dots_in_scanline==80) && (LY<144) )
{
STAT = (STAT & 0xFC) | 0x3; // Set mode 3
}
else if ( (dots_in_scanline==252) && (LY<144) )
{
STAT = (STAT & 0xFC); // Set mode 0
if (STAT&0x08) current_interrupt_lcd_state = true; // interrupts |= INTERRUPT_LCD;
}
else if (dots_in_scanline==456)
{
dots_in_scanline = 0;
LY++;
if (LY==154) LY=0;
if (LY==144)
{
// Set vblank interrupt
mem::writeMem(IF, mem::readMem(IF) | INTERRUPT_VBLANK);
STAT = (STAT & 0xFC) | 0x01; // Set mode 1
//interrupts |= INTERRUPT_VBLANK;
if (STAT&0x10) current_interrupt_lcd_state = true; //interrupts |= INTERRUPT_LCD;
}
else
{
if (LY<144)
{
STAT = (STAT & 0xFC) | 0x02; // Set mode 2
if (STAT&0x20) current_interrupt_lcd_state = true; // interrupts |= INTERRUPT_LCD;
fill_line_buffer_bkg();
fill_line_buffer_win();
fill_line_buffer_obj();
}
}
if (LY==LYC)
{
STAT = (STAT & 0xFB) | 0x04;
if (STAT&0x40) current_interrupt_lcd_state = true; // interrupts |= INTERRUPT_LCD;
}
else
{
STAT = (STAT & 0xFB);
}
}
if (!last_interrupt_lcd_state && current_interrupt_lcd_state)
{
mem::writeMem(IF, mem::readMem(IF) | INTERRUPT_LCD);
//sm83::interrupt(interrupts);
}
last_interrupt_lcd_state = current_interrupt_lcd_state;
t_screen++;
if (t_screen>=t_states_total)
{
t_screen-=t_states_total;
display::redraw();
}
}
}
const uint8_t *getpixels()
{
return pixels;
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <stdint.h>
namespace ppu
{
void init();
void refresh(const uint32_t dt, const bool full=false);
const uint8_t *getpixels();
}
+1394
View File
File diff suppressed because it is too large Load Diff
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include <stdint.h>
#include "interrupts.h"
namespace sm83
{
#define SM83_OPTION_STOP_ON_INVALID 0
#define SM83_OPTION_BREAK_ON_INTERRUPT 1
#define SM83_OPTION_BREAK_ON_RET 2
#define SM83_NUM_OPTIONS 3
void reset();
void setClock(uint32_t freq);
uint32_t getClock();
void processInterrupts();
void interrupt(uint8_t type);
uint32_t step();
uint8_t *getRegs();
uint16_t getAF();
uint16_t getBC();
uint16_t getDE();
uint16_t getHL();
uint16_t getSP();
uint16_t getPC();
uint8_t getI();
uint8_t getR();
void setPC(const uint16_t addr);
const bool getOption(const int option);
void setOption(const int option, const bool value);
void toggleOption(const int option);
}
+171
View File
@@ -0,0 +1,171 @@
#include "sm83dis.h"
#include "mem.h"
#include <string.h>
#include <stdio.h>
#include <vector>
#include <bits/stdc++.h>
namespace sm83dis
{
char buffer[256];
int opcode_size = 0;
char symbols[65536][15];
std::vector<uint16_t> used_symbols;
// $%04x
// $%02x
// %hhd
const char *base_opcodes[256] = {"nop", "ld bc,$%04x", "ld (bc),a", "inc bc", "inc b", "dec b", "ld b,$%02x", "rlca", "ld ($%04x), sp", "add hl,bc", "ld a,(bc)", "dec bc", "inc c", "dec c", "ld c,$%02x", "rrca", "stop $%02x", "ld de,$%04x", "ld (de),a", "inc de", "inc d", "dec d", "ld d,$%02x", "rla", "jr $%Xx", "add hl,de", "ld a,(de)", "dec de", "inc e", "dec e", "ld e,$%02x", "rra", "jr nz,$%Xx", "ld hl,$%04x", "ld (hl+),a", "inc hl", "inc h", "dec h", "ld h,$%02x", "daa", "jr z,$%Xx", "add hl,hl", "ld a, (hl+)", "dec hl", "inc l", "dec l", "ld l,$%02x", "cpl", "jr nc,$%Xx", "ld sp,$%04x", "ld (hl-),a", "inc sp", "inc (hl)", "dec (hl)", "ld (hl),$%02x", "scf", "jr c,$%Xx", "add hl,sp", "ld a,(hl-)", "dec sp", "inc a", "dec a", "ld a,$%02x", "ccf", "ld b,b", "ld b,c", "ld b,d", "ld b,e", "ld b,h", "ld b,l", "ld b,(hl)", "ld b,a", "ld c,b", "ld c,c", "ld c,d", "ld c,e", "ld c,h", "ld c,l", "ld c,(hl)", "ld c,a", "ld d,b", "ld d,c", "ld d,d", "ld d,e", "ld d,h", "ld d,l", "ld d,(hl)", "ld d,a", "ld e,b", "ld e,c", "ld e,d", "ld e,e", "ld e,h", "ld e,l", "ld e,(hl)", "ld e,a", "ld h,b", "ld h,c", "ld h,d", "ld h,e", "ld h,h", "ld h,l", "ld h,(hl)", "ld h,a", "ld l,b", "ld l,c", "ld l,d", "ld l,e", "ld l,h", "ld l,l", "ld l,(hl)", "ld l,a", "ld (hl),b", "ld (hl),c", "ld (hl),d", "ld (hl),e", "ld (hl),h", "ld (hl),l", "halt", "ld (hl),a", "ld a,b", "ld a,c", "ld a,d", "ld a,e", "ld a,h", "ld a,l", "ld a,(hl)", "ld a,a", "add a,b", "add a,c", "add a,d", "add a,e", "add a,h", "add a,l", "add a,(hl)", "add a,a", "adc a,b", "adc a,c", "adc a,d", "adc a,e", "adc a,h", "adc a,l", "adc a,(hl)", "adc a,a", "sub b", "sub c", "sub d", "sub e", "sub h", "sub l", "sub (hl)", "sub a", "sbc a,b", "sbc a,c", "sbc a,d", "sbc a,e", "sbc a,h", "sbc a,l", "sbc a,(hl)", "sbc a,a", "and b", "and c", "and d", "and e", "and h", "and l", "and (hl)", "and a", "xor b", "xor c", "xor d", "xor e", "xor h", "xor l", "xor (hl)", "xor a", "or b", "or c", "or d", "or e", "or h", "or l", "or (hl)", "or a", "cp b", "cp c", "cp d", "cp e", "cp h", "cp l", "cp (hl)", "cp a", "ret nz", "pop bc", "jp nz,$%04x", "jp $%04x", "call nz,$%04x", "push bc", "add a,$%02x", "rst &00", "ret z", "ret", "jp z,$%04x", "_Bit_", "call z,$%04x", "call $%04x", "adc a,$%02x", "rst $08", "ret nc", "pop de", "jp nc,$%04x", "---", "call nc,$%04x", "push de", "sub a,$%02x", "rst $10", "ret c", "reti", "jp c,$%04x", "---", "call c,$%04x", "---", "sbc a,$%02x", "rst $18", "ldh ($%02x),a", "pop hl", "ldh (c),a", "---", "---", "push hl", "and a,$%02x", "rst $20", "add sp,$%Xx", "jp hl", "ld ($%04x),a", "---", "---", "---", "xor a,$%02x", "rst $28", "ldh a,($%02x)", "pop af", "ld a,(c)", "di", "---", "push af", "or a,$%02x", "rst $30", "ld hl,sp+$%Xx", "ld sp,hl", "ld a,($%04x)", "ei", "---", "---", "cp a,$%02x", "rst $38"};
const char *bit_opcodes[256] = {"rlc b", "rlc c", "rlc d", "rlc e", "rlc h", "rlc l", "rlc (hl)", "rlc a", "rrc b", "rrc c", "rrc d", "rrc e", "rrc h", "rrc l", "rrc (hl)", "rrc a", "rl b", "rl c", "rl d", "rl e", "rl h", "rl l", "rl (hl)", "rl a", "rr b", "rr c", "rr d", "rr e", "rr h", "rr l", "rr (hl)", "rr a", "sla b", "sla c", "sla d", "sla e", "sla h", "sla l", "sla (hl)", "sla a", "sra b", "sra c", "sra d", "sra e", "sra h", "sra l", "sra (hl)", "sra a", "swap b", "swap c", "swap d", "swap e", "swap h", "swap l", "swap (hl)", "swap a", "srl b", "srl c", "srl d", "srl e", "srl h", "srl l", "srl (hl)", "srl a", "bit 0,b", "bit 0,c", "bit 0,d", "bit 0,e", "bit 0,h", "bit 0,l", "bit 0,(hl)", "bit 0,a", "bit 1,b", "bit 1,c", "bit 1,d", "bit 1,e", "bit 1,h", "bit 1,l", "bit 1,(hl)", "bit 1,a", "bit 2,b", "bit 2,c", "bit 2,d", "bit 2,e", "bit 2,h", "bit 2,l", "bit 2,(hl)", "bit 2,a", "bit 3,b", "bit 3,c", "bit 3,d", "bit 3,e", "bit 3,h", "bit 3,l", "bit 3,(hl)", "bit 3,a", "bit 4,b", "bit 4,c", "bit 4,d", "bit 4,e", "bit 4,h", "bit 4,l", "bit 4,(hl)", "bit 4,a", "bit 5,b", "bit 5,c", "bit 5,d", "bit 5,e", "bit 5,h", "bit 5,l", "bit 5,(hl)", "bit 5,a", "bit 6,b", "bit 6,c", "bit 6,d", "bit 6,e", "bit 6,h", "bit 6,l", "bit 6,(hl)", "bit 6,a", "bit 7,b", "bit 7,c", "bit 7,d", "bit 7,e", "bit 7,h", "bit 7,l", "bit 7,(hl)", "bit 7,a", "res 0,b", "res 0,c", "res 0,d", "res 0,e", "res 0,h", "res 0,l", "res 0,(hl)", "res 0,a", "res 1,b", "res 1,c", "res 1,d", "res 1,e", "res 1,h", "res 1,l", "res 1,(hl)", "res 1,a", "res 2,b", "res 2,c", "res 2,d", "res 2,e", "res 2,h", "res 2,l", "res 2,(hl)", "res 2,a", "res 3,b", "res 3,c", "res 3,d", "res 3,e", "res 3,h", "res 3,l", "res 3,(hl)", "res 3,a", "res 4,b", "res 4,c", "res 4,d", "res 4,e", "res 4,h", "res 4,l", "res 4,(hl)", "res 4,a", "res 5,b", "res 5,c", "res 5,d", "res 5,e", "res 5,h", "res 5,l", "res 5,(hl)", "res 5,a", "res 6,b", "res 6,c", "res 6,d", "res 6,e", "res 6,h", "res 6,l", "res 6,(hl)", "res 6,a", "res 7,b", "res 7,c", "res 7,d", "res 7,e", "res 7,h", "res 7,l", "res 7,(hl)", "res 7,a", "set 0,b", "set 0,c", "set 0,d", "set 0,e", "set 0,h", "set 0,l", "set 0,(hl)", "set 0,a", "set 1,b", "set 1,c", "set 1,d", "set 1,e", "set 1,h", "set 1,l", "set 1,(hl)", "set 1,a", "set 2,b", "set 2,c", "set 2,d", "set 2,e", "set 2,h", "set 2,l", "set 2,(hl)", "set 2,a", "set 3,b", "set 3,c", "set 3,d", "set 3,e", "set 3,h", "set 3,l", "set 3,(hl)", "set 3,a", "set 4,b", "set 4,c", "set 4,d", "set 4,e", "set 4,h", "set 4,l", "set 4,(hl)", "set 4,a", "set 5,b", "set 5,c", "set 5,d", "set 5,e", "set 5,h", "set 5,l", "set 5,(hl)", "set 5,a", "set 6,b", "set 6,c", "set 6,d", "set 6,e", "set 6,h", "set 6,l", "set 6,(hl)", "set 6,a", "set 7,b", "set 7,c", "set 7,d", "set 7,e", "set 7,h", "set 7,l", "set 7,(hl)", "set 7,a"};
void loadSymbols()
{
used_symbols.clear();
for (int i=0; i<65536; ++i) symbols[i][0] = 0;
FILE *f = fopen("symbols.txt", "r");
if (!f) return;
while (true) {
uint16_t address;
char tmp[15];
const int result = fscanf(f, "%x %s", &address, tmp);
if (result != 2) break;
strcpy(symbols[address], tmp);
used_symbols.push_back(address);
}
fclose(f);
}
void saveSymbols()
{
FILE *f = fopen("symbols.txt", "w");
for (auto sym_addr : used_symbols)
{
fprintf(f, "0x%x %s\n", sym_addr, symbols[sym_addr]);
}
fclose(f);
}
const char *getBase(const uint16_t pos)
{
if (mem::readMem(pos) == 0xCB) {
opcode_size=2;
return bit_opcodes[mem::readMem(pos+1)];
} else {
opcode_size=1;
return base_opcodes[mem::readMem(pos)];
}
}
void printOpcode(const uint16_t pos)
{
char hex[4];
for (int i=0; i<4;++i)
{
if (opcode_size>i)
sprintf(hex, "%02x ", mem::readMem(pos+i));
else
sprintf(hex, " ");
strcat(buffer, hex);
}
}
const char *getAsm(const uint16_t pos)
{
opcode_size = 0;
char base[256];
strcpy(buffer, getBase(pos));
if (strstr(buffer, "4x"))
{
opcode_size+=2;
//const uint8_t memvalue = mem::readMem(pos);
//const uint16_t address = pos + 1;
const uint16_t word = mem::readMem(pos+1) + (mem::readMem(pos+2)<<8);
if (symbols[word][0]!=0) {
char *p = strstr(buffer, "$");
(*p)='%'; p++;
(*p)='s'; p++;
while (*(p-1) != 0) {
*p = *(p+3);
p++;
}
strcpy(base, buffer);
sprintf(buffer, base, symbols[word]);
} else {
strcpy(base, buffer);
sprintf(buffer, base, word);
}
}
if (strstr(buffer, "2x"))
{
opcode_size+=1;
strcpy(base, buffer);
sprintf(buffer, base, mem::readMem(pos+1));
}
if (strstr(buffer, "Xx"))
{
strcpy(base, buffer);
*strstr(base, "Xx") = '4';
opcode_size+=1;
sprintf(buffer, base, pos + opcode_size + (int8_t)mem::readMem(pos+1));
}
return buffer;
}
const char *getOpcode(const uint16_t pos)
{
opcode_size = 0;
buffer[0]=0;
const char *base = getBase(pos);
if (strstr(base, "4x")) opcode_size+=2;
if (strstr(base, "2x")) opcode_size+=1;
if (strstr(base, "Xx")) opcode_size+=1;
printOpcode(pos);
return buffer;
}
const int getOpcodeSize(const uint16_t pos)
{
opcode_size = 0;
const char *base = getBase(pos);
if (strstr(base, "4x")) opcode_size+=2;
if (strstr(base, "2x")) opcode_size+=1;
if (strstr(base, "Xx")) opcode_size+=1;
return opcode_size;
}
const char *getSymbol(const uint16_t pos)
{
return symbols[pos];
}
void setSymbol(const uint16_t pos, const char *sym)
{
if (sym) {
strcpy(symbols[pos], sym);
} else {
symbols[pos][0] = 0;
}
used_symbols.clear();
for (int i=0; i<65536; ++i) if (symbols[i][0]!=0) used_symbols.push_back(i);
saveSymbols();
}
const int getNumSymbols()
{
return used_symbols.size();
}
const uint16_t getSymbolAddress(const int pos)
{
return used_symbols[pos];
}
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <stdint.h>
namespace sm83dis
{
void loadSymbols();
void saveSymbols();
const char *getAsm(const uint16_t pos);
const char *getOpcode(const uint16_t pos);
const int getOpcodeSize(const uint16_t pos);
const char *getSymbol(const uint16_t pos);
void setSymbol(const uint16_t pos, const char *sym);
const int getNumSymbols();
const uint16_t getSymbolAddress(const int pos);
}
+102
View File
@@ -0,0 +1,102 @@
#include "ui.h"
#include <SDL2/SDL.h>
#include <cstring>
namespace ui
{
uint8_t colors[16][3] = {
{0,0,0},
{0,0,128},
{0,128,0},
{0,128,128},
{128,0,0},
{128,0,128},
{255,128,0},
{128,128,128},
{30,30,30},
{0,0,255},
{0,255,0},
{0,255,255},
{255,0,0},
{255,0,255},
{255,255,0},
{255,255,255},
};
SDL_Renderer *ren = nullptr;
SDL_Texture *tex = nullptr;
uint8_t offset_x = 0;
uint8_t offset_y = 0;
bool clicked = false;
SDL_Texture * createtexture(SDL_Renderer *renderer)
{
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP("font.bmp"));
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
return texture;
}
void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture)
{
if (ren==renderer) return;
ren = renderer;
tex = texture;
offset_x = offset_y = 0;
}
void setoffset(uint8_t x, uint8_t y)
{
offset_x = x;
offset_y = y;
}
void box(int x, int y, int w, int h, uint8_t color)
{
SDL_Rect rect {((offset_x+x)*CHR_W)+3, ((offset_y+y)*CHR_H)+6, w*CHR_W-6, h*CHR_H-13};
SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
SDL_RenderDrawRect(ren, &rect);
}
void printrect(int x, int y, int w, int h, uint8_t color)
{
SDL_Rect rect {(offset_x+x)*CHR_W, (offset_y+y)*CHR_H, w*CHR_W, h*CHR_H};
SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
SDL_RenderFillRect(ren, &rect);
}
void printvoidrect(int x, int y, int w, int h, uint8_t color)
{
SDL_Rect rect {(offset_x+x)*CHR_W, (offset_y+y)*CHR_H, w*CHR_W, h*CHR_H};
SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
SDL_RenderDrawRect(ren, &rect);
}
void printchar(int x, int y, char chr, uint8_t color)
{
if (color != 255) SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
if (chr==32) return;
if (chr<32 || chr>127) chr = '.';
SDL_Rect src {((chr-32)&0xf)*CHR_W, ((chr-32)>>4)*CHR_H, CHR_W, CHR_H};
SDL_Rect dst {(offset_x+x)*CHR_W, (offset_y+y)*CHR_H, CHR_W, CHR_H};
SDL_RenderCopy(ren, tex, &src, &dst);
}
void printtxt(int x, int y, const char *text, uint8_t color)
{
SDL_SetTextureColorMod(tex, colors[color][0], colors[color][1], colors[color][2]);
for (int i=0; i<strlen(text);++i) if (text[i]!=32) printchar(x+i, y, text[i]);
}
void setClicked(const bool value)
{
clicked = value;
}
const bool getClicked()
{
return clicked;
}
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include <SDL2/SDL.h>
namespace ui
{
#define CHR_W 6
#define CHR_H 13
#define COLOR_BLACK 0
#define COLOR_DARK_BLUE 1
#define COLOR_GREEN 2
#define COLOR_TEAL 3
#define COLOR_BROWN 4
#define COLOR_PURPLE 5
#define COLOR_ORANGE 6
#define COLOR_GRAY 7
#define COLOR_DARK 8
#define COLOR_BLUE 9
#define COLOR_LIME 10
#define COLOR_CYAN 11
#define COLOR_RED 12
#define COLOR_MAGENTA 13
#define COLOR_YELLOW 14
#define COLOR_WHITE 15
SDL_Texture * createtexture(SDL_Renderer *renderer);
void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture);
void setoffset(uint8_t x, uint8_t y);
void box(int x, int y, int w, int h, uint8_t color);
void printrect(int x, int y, int w, int h, uint8_t color);
void printvoidrect(int x, int y, int w, int h, uint8_t color);
void printchar(int x, int y, char chr, uint8_t color=255);
void printtxt(int x, int y, const char *text, uint8_t color);
void setClicked(const bool value);
const bool getClicked();
}
+142
View File
@@ -0,0 +1,142 @@
#include "ui_menu.h"
#include "ui.h"
#include <vector>
#include <string>
namespace ui
{
namespace menu
{
#define OPTION_TYPE_NORMAL 0
#define OPTION_TYPE_SEPARATOR 1
#define OPTION_TYPE_BOOLEAN 2
struct option_t
{
std::string label;
int type;
int value;
int (*callback)(int);
};
struct menu_t
{
std::string label;
SDL_Rect rect;
std::vector<option_t> options;
};
void(*exit_callback)(void) = nullptr;
std::vector<menu_t> menus;
int visible_menu = -1;
int menu_x = 0;
void init()
{
// No se si hi ha algo que fer ací...
}
void show()
{
int mx, my;
Uint32 mb = SDL_GetMouseState(&mx, &my);
mx=mx/CHR_W; my=my/CHR_H;
int w;
ui::printrect(0, 0, 320, 1, COLOR_BLACK);
int opt_pos=1;
int index=0;
for (auto &menu : menus)
{
const int text_size = (menu.label.size()+2);
uint8_t text_color = COLOR_WHITE;
if (my<1 && mx>=opt_pos && mx<opt_pos+text_size)
{
ui::printrect(opt_pos-1, 0, text_size, 1, COLOR_DARK_BLUE);
text_color = COLOR_WHITE;
visible_menu = index;
menu_x = opt_pos-1;
}
ui::printtxt(opt_pos, 0, menu.label.c_str(), text_color);
opt_pos+=text_size;
index++;
}
if (visible_menu!=-1)
{
opt_pos=2;
menu_t &m = menus[visible_menu];
ui::printrect(menu_x, 1, 22, m.options.size()+2, COLOR_BLACK);
ui::box(menu_x, 1, 22, m.options.size()+2, COLOR_WHITE);
for (auto &option : m.options)
{
if (option.type!=OPTION_TYPE_SEPARATOR)
{
const int text_size = (option.label.size()+2);
uint8_t text_color = COLOR_WHITE;
if (my==opt_pos && mx>=menu_x && mx<menu_x+text_size)
{
ui::printrect(menu_x+1, opt_pos, 20, 1, COLOR_DARK_BLUE);
text_color = COLOR_WHITE;
if (ui::getClicked())
{
if (option.callback) option.value = option.callback(option.value);
if (exit_callback) exit_callback();
return;
}
}
ui::printtxt(menu_x+1, opt_pos, option.label.c_str(), text_color);
if (option.type==OPTION_TYPE_BOOLEAN)
{
const char *check = option.value?"[X]":"[ ]";
ui::printtxt(menu_x+18, opt_pos, check, text_color);
}
}
opt_pos++;
}
}
}
void setexitcallback(void(*callback)(void))
{
exit_callback = callback;
}
const int addsubmenu(const char *label)
{
menu_t m;
m.label = label;
m.rect = {0, 0, 100, 0};
menus.push_back(m);
return menus.size()-1;
}
void addoption(int menu, const char *label, int(*callback)(int))
{
option_t op;
op.type = OPTION_TYPE_NORMAL;
op.label = label;
op.callback = callback;
menus[menu].options.push_back(op);
menus[menu].rect.h += 15;
}
void addbooloption(int menu, const char *label, bool default_value, int(*callback)(int))
{
option_t op;
op.type = OPTION_TYPE_BOOLEAN;
op.label = label;
op.value = default_value?1:0;
op.callback = callback;
menus[menu].options.push_back(op);
menus[menu].rect.h += 15;
}
void addseparator(int menu)
{
option_t op;
op.type = OPTION_TYPE_SEPARATOR;
menus[menu].options.push_back(op);
}
}
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
namespace ui
{
namespace menu
{
void init();
void show();
void setexitcallback(void(*callback)(void));
const int addsubmenu(const char *label);
void addoption(int menu, const char *label, int(*callback)(int));
void addbooloption(int menu, const char *label, bool default_value, int(*callback)(int));
void addseparator(int menu);
}
}
+33
View File
@@ -0,0 +1,33 @@
#include "ui_window.h"
#include <vector>
namespace ui
{
namespace window
{
struct window_t
{
Uint32 window;
bool (*callback)(SDL_Event*);
};
std::vector<window_t> windows;
void registerWindow(Uint32 window, bool(*callback)(SDL_Event*))
{
for (auto win : windows) if (win.window == window) return;
windows.push_back((window_t){window, callback});
}
void unregisterWindow(Uint32 window)
{
for (auto win = windows.begin(); win != windows.end(); win++) if ((*win).window == window) { windows.erase(win); return; }
}
bool sendEvent(Uint32 window, SDL_Event *e)
{
for (auto win : windows) if (win.window == window) return win.callback(e);
return true;
}
}
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <SDL2/SDL.h>
namespace ui
{
namespace window
{
void registerWindow(Uint32 window, bool(*callback)(SDL_Event *e));
void unregisterWindow(Uint32 window);
bool sendEvent(Uint32 window, SDL_Event *e);
}
}