Rudimentary paddle support, fix audio stuttering

Mouse/Trackpad drag across the screen emulates paddles
Click on the screen emulates button 0
Ignore reads from C020 (tape out)
Add BRA instruction for 65C02 (maybe not necessary)
Disable audio in FAST mode
Throttle a little better when audio is enabled - stopped stutter
This commit is contained in:
Brad Grantham 2016-11-29 22:17:17 -08:00
parent 0687587428
commit ed8dfa33ea
3 changed files with 115 additions and 28 deletions

View File

@ -37,6 +37,8 @@ volatile bool exit_on_memory_fallthrough = true;
volatile bool run_fast = false;
volatile bool pause_cpu = false;
const float paddle_max_pulse_seconds = .00282;
typedef unsigned long long clk_t;
struct system_clock
{
@ -573,11 +575,15 @@ struct MAINboard : board_base
display_write_func display_write;
typedef std::function<void (char *audiobuffer, size_t dist)> audio_flush_func;
audio_flush_func audio_flush;
MAINboard(system_clock& clk_, unsigned char rom_image[32768], display_write_func display_write_, audio_flush_func audio_flush_) :
typedef std::function<tuple<float, bool> (int num)> get_paddle_func;
get_paddle_func get_paddle;
clk_t paddles_clock_out[4];
MAINboard(system_clock& clk_, unsigned char rom_image[32768], display_write_func display_write_, audio_flush_func audio_flush_, get_paddle_func get_paddle_) :
clk(clk_),
internal_C800_ROM_selected(true),
display_write(display_write_),
audio_flush(audio_flush_)
audio_flush(audio_flush_),
get_paddle(get_paddle_)
{
std::copy(rom_image + rom_D000.base - 0x8000, rom_image + rom_D000.base - 0x8000 + rom_D000.size, rom_D000.memory.begin());
std::copy(rom_image + rom_E000.base - 0x8000, rom_image + rom_E000.base - 0x8000 + rom_E000.size, rom_E000.memory.begin());
@ -657,11 +663,6 @@ struct MAINboard : board_base
return true;
}
}
if(ignore_mmio.find(addr) != ignore_mmio.end()) {
if(debug & DEBUG_RW) printf("read %04X, ignored, return 0x00\n", addr);
data = 0x00;
return true;
}
if((addr & 0xFFF0) == 0xC080) {
C08X_bank = ((addr >> 3) & 1) ? BANK1 : BANK2;
C08X_write_RAM = addr & 1;
@ -670,19 +671,16 @@ struct MAINboard : board_base
if(debug & DEBUG_SWITCH) printf("write %04X switch, %s, %d write_RAM, %d read_RAM\n", addr, (C08X_bank == BANK1) ? "BANK1" : "BANK2", C08X_write_RAM, C08X_read_RAM);
data = 0x00;
return true;
}
if(addr == 0xC011) {
} else if(addr == 0xC011) {
data = (C08X_bank == BANK2) ? 0x80 : 0x0;
data = 0x00;
if(debug & DEBUG_SWITCH) printf("read BSRBANK2, return 0x%02X\n", data);
return true;
}
if(addr == 0xC012) {
} else if(addr == 0xC012) {
data = C08X_read_RAM ? 0x80 : 0x0;
if(debug & DEBUG_SWITCH) printf("read BSRREADRAM, return 0x%02X\n", data);
return true;
}
if(addr == 0xC000) {
} else if(addr == 0xC000) {
if(!keyboard_buffer.empty()) {
data = 0x80 | keyboard_buffer[0];
} else {
@ -690,16 +688,18 @@ struct MAINboard : board_base
}
if(debug & DEBUG_RW) printf("read KBD, return 0x%02X\n", data);
return true;
}
if(addr == 0xC030) {
} else if(addr == 0xC020) {
if(debug & DEBUG_RW) printf("read TAPE, force 0x00\n");
data = 0x00;
return true;
} else if(addr == 0xC030) {
if(debug & DEBUG_RW) printf("read SPKR, force 0x00\n");
fill_flush_audio();
data = 0x00;
speaker_energized = !speaker_energized;
return true;
}
if(addr == 0xC010) {
} else if(addr == 0xC010) {
// reset keyboard latch
if(!keyboard_buffer.empty()) {
keyboard_buffer.pop_front();
@ -707,6 +707,31 @@ struct MAINboard : board_base
data = 0x0;
if(debug & DEBUG_RW) printf("read KBDSTRB, return 0x%02X\n", data);
return true;
} else if(addr == 0xC070) {
for(int i = 0; i < 4; i++) {
float value;
bool button;
tie(value, button) = get_paddle(i);
paddles_clock_out[i] = clk + value * paddle_max_pulse_seconds * machine_clock_rate;
}
data = 0x0;
return true;
} else if(addr >= 0xC064 && addr <= 0xC067) {
int num = addr - 0xC064;
data = (clk < paddles_clock_out[num]) ? 0xff : 0x00;
return true;
} else if(addr >= 0xC061 && addr <= 0xC063) {
int num = addr - 0xC061;
float value;
bool button;
tie(value, button) = get_paddle(num);
data = button ? 0xff : 0x0;
return true;
}
if(ignore_mmio.find(addr) != ignore_mmio.end()) {
if(debug & DEBUG_RW) printf("read %04X, ignored, return 0x00\n", addr);
data = 0x00;
return true;
}
printf("unhandled MMIO Read at %04X\n", addr);
fflush(stdout); exit(0);
@ -1007,7 +1032,7 @@ struct CPU6502
2, 5, -1, -1, -1, 4, 6, -1, 2, 4, -1, -1, -1, 4, 7, -1,
6, 6, -1, -1, -1, 3, 5, -1, 4, 2, 2, -1, 5, 4, 6, -1,
2, 5, -1, -1, -1, 4, 6, -1, 2, 4, -1, -1, -1, 4, 7, -1,
-1, 6, -1, -1, 3, 3, 3, -1, 2, -1, 2, -1, 4, 4, 4, -1,
2, 6, -1, -1, 3, 3, 3, -1, 2, -1, 2, -1, 4, 4, 4, -1,
2, 6, -1, -1, 4, 4, 4, -1, 2, 5, 2, -1, -1, 5, -1, -1,
2, 6, 2, -1, 3, 3, 3, -1, 2, 2, 2, -1, 4, 4, 4, -1,
2, 5, -1, -1, 4, 4, 4, -1, 2, 4, 2, -1, 4, 4, 4, -1,
@ -1225,6 +1250,15 @@ struct CPU6502
break;
}
case 0x80: { // BRA - 65C02! (Choplifter)
int rel = (read_pc_inc(bus) + 128) % 256 - 128;
clk++;
if((pc + rel) / 256 != pc / 256)
clk++;
pc += rel;
break;
}
case 0x10: { // BPL
int rel = (read_pc_inc(bus) + 128) % 256 - 128;
if(!isset(N)) {
@ -2520,19 +2554,20 @@ int main(int argc, char **argv)
MAINboard::display_write_func display = [](int addr, unsigned char data)->bool{return APPLE2Einterface::write(addr, data);};
MAINboard::audio_flush_func audio;
MAINboard::get_paddle_func paddle = [](int num)->tuple<float, bool>{return APPLE2Einterface::get_paddle(num);};
if(have_audio)
audio = [aodev](char *buf, size_t sz){
// static char prev_sample;
// for(int i = 0; i < sz; i++)
// if(buf[i] != prev_sample) {
ao_play(aodev, buf, sz);
if(!run_fast) ao_play(aodev, buf, sz);
// break;
// }
// prev_sample = buf[sz - 1];
};
else
audio = [](char *buf, size_t sz){};
mainboard = new MAINboard(clk, b, display, audio);
mainboard = new MAINboard(clk, b, display, audio, paddle);
bus.board = mainboard;
bus.reset();
@ -2570,7 +2605,7 @@ int main(int argc, char **argv)
APPLE2Einterface::start();
chrono::time_point<chrono::system_clock> then;
chrono::time_point<chrono::system_clock> then = std::chrono::system_clock::now();
while(1) {
if(!debugging) {
@ -2603,7 +2638,7 @@ int main(int argc, char **argv)
if(run_fast)
clocks_per_slice = machine_clock_rate / 5;
else
clocks_per_slice = millis_per_slice * machine_clock_rate / 1000;
clocks_per_slice = millis_per_slice * machine_clock_rate / 1000 * 1.05;
}
clk_t prev_clock = clk;
while(clk - prev_clock < clocks_per_slice) {
@ -2624,7 +2659,7 @@ int main(int argc, char **argv)
int page = mainboard->PAGE2 ? 1 : 0;
APPLE2Einterface::set_switches(mode, mainboard->MIXED, page);
APPLE2Einterface::iterate();
chrono::time_point<chrono::system_clock> now;
chrono::time_point<chrono::system_clock> now = std::chrono::system_clock::now();
auto elapsed_millis = chrono::duration_cast<chrono::milliseconds>(now - then);
if(!run_fast || pause_cpu)

View File

@ -97,6 +97,16 @@ GLuint hires_to_screen_location;
GLuint hires_x_offset_location;
GLuint hires_y_offset_location;
float paddle_values[4] = {0, 0, 0, 0};
bool paddle_buttons[4] = {false, false, false, false};
tuple<float,bool> get_paddle(int num)
{
if(num < 0 || num > 3)
make_tuple(-1, false);
return make_tuple(paddle_values[num], paddle_buttons[num]);
}
static void CheckOpenGL(const char *filename, int line)
{
int glerr;
@ -481,6 +491,7 @@ struct widget
virtual tuple<float, float> get_min_dimensions() const = 0;
virtual void draw(double now, float to_screen[9], float x, float y, float w, float h) = 0;
virtual bool click(double now, float x, float y) = 0;
virtual void hover(double now, float x, float y) = 0;
virtual void drag(double now, float x, float y) = 0;
virtual void release(double now, float x, float y) = 0;
};
@ -498,6 +509,7 @@ struct spacer : public widget
}
virtual void draw(double now, float to_screen[9], float x, float y, float w, float h) {}
virtual bool click(double now, float x, float y) { return false; }
virtual void hover(double now, float x, float y) {}
virtual void drag(double now, float x, float y) {}
virtual void release(double now, float x, float y) {}
};
@ -533,9 +545,13 @@ struct centering : public widget
{
return child->click(now, x - (w - cw) / 2, y - (h - ch) / 2); // XXX should limit to cw,ch too
}
virtual void hover(double now, float x, float y)
{
child->hover(now, x - (w - cw) / 2, y - (h - ch) / 2);
}
virtual void drag(double now, float x, float y)
{
child->click(now, x - (w - cw) / 2, y - (h - ch) / 2);
child->drag(now, x - (w - cw) / 2, y - (h - ch) / 2);
}
virtual void release(double now, float x, float y)
{
@ -605,6 +621,14 @@ struct widgetbox : public widget
}
return false;
}
virtual void hover(double now, float x, float y)
{
for(auto it = children.begin(); it != children.end(); it++) {
placed_widget& child = *it;
if(x >= child.x && x < child.x + child.w && y >= child.y && y < child.y + child.h)
child.widg->hover(now, x - child.x, y - child.y);
}
}
virtual void drag(double now, float x, float y)
{
focus.widg->click(now, x - focus.x, y - focus.y);
@ -626,6 +650,8 @@ void set(float v[4], float x, float y, float z, float w)
struct apple2screen : public widget
{
float w, h;
float x, y;
apple2screen() { }
virtual tuple<float, float> get_min_dimensions() const
@ -633,8 +659,12 @@ struct apple2screen : public widget
return make_tuple(280, 192);
}
virtual void draw(double now, float to_screen[9], float x, float y, float w, float h)
virtual void draw(double now, float to_screen[9], float x_, float y_, float w_, float h_)
{
x = x_;
y = y_;
w = w_;
h = h_;
long long elapsed_millis = now * 1000;
set_shader(to_screen, display_mode, false, (elapsed_millis / 300) % 2, x, y);
@ -656,14 +686,25 @@ struct apple2screen : public widget
float w, h;
tie(w, h) = get_min_dimensions();
if(x >= 0 && y >= 0 & x < w && y < h) {
paddle_buttons[0] = true;
return true;
// XXX paddle button 1
}
return false;
}
virtual void drag(double now, float x, float y) { }
virtual void release(double now, float x, float y) { }
virtual void hover(double now, float x_, float y_)
{
paddle_values[0] = max(0.0f, min(1.0f, x_ / w));
paddle_values[1] = max(0.0f, min(1.0f, y_ / h));
}
virtual void release(double now, float x, float y)
{
paddle_buttons[0] = false;
}
};
struct text_widget : public widget
@ -725,6 +766,8 @@ struct text_widget : public widget
virtual void drag(double now, float x, float y) { }
virtual void hover(double now, float x, float y) { }
virtual void release(double now, float x, float y) { }
};
@ -771,6 +814,8 @@ struct momentary : public text_widget
return false;
}
virtual void hover(double now, float x, float y) {}
virtual void drag(double now, float x, float y)
{
float w, h;
@ -848,6 +893,8 @@ struct toggle : public text_widget
return false;
}
virtual void hover(double now, float x, float y) { }
virtual void drag(double now, float x, float y)
{
float w, h;
@ -1086,12 +1133,15 @@ static void motion(GLFWwindow *window, double x, double y)
gOldMouseX = x;
gOldMouseY = y;
float wx, wy;
tie(wx, wy) = window_to_widget(x, y);
if(gButtonPressed == 1) {
if(widget_clicked) {
float wx, wy;
tie(wx, wy) = window_to_widget(x, y);
widget_clicked->drag(0.0, wx, wy);
}
} else {
ui->hover(0.0, wx, wy);
}
redraw(window);
}

View File

@ -50,4 +50,6 @@ enum DisplayMode {TEXT, LORES, HIRES};
void set_switches(DisplayMode mode, bool mixed, int page);
bool write(int addr, unsigned char data);
std::tuple<float,bool> get_paddle(int num);
};