Compare commits
83 Commits
b05ce14a95
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| afa59cdb5a | |||
| 6837392c53 | |||
| 1fdfeedacc | |||
| 8201d51668 | |||
| 864c6b929d | |||
| 0530c255c1 | |||
| 78a4c84db2 | |||
| 5516f463bf | |||
| b69da56526 | |||
| 884e509d67 | |||
| 8b36807e3b | |||
| 6791916f75 | |||
| 90b34749d1 | |||
| 40c825cdb9 | |||
| ade97a9a70 | |||
| 9eb8662ec7 | |||
| 1db0c52e1a | |||
| 2775da3d53 | |||
| 2a0febc6b7 | |||
| 913450fadb | |||
| fee07b6e1b | |||
| 662583be36 | |||
| 9725e58d92 | |||
| 91a8933544 | |||
| 780afbc6a8 | |||
| 1cde51f2d4 | |||
| 42da652aef | |||
| 0df17bf4c9 | |||
| 6e3e8e9b69 | |||
| 3fd28136f6 | |||
| 4b4e1df8f9 | |||
| 13354b855d | |||
| 300f95803e | |||
| ab476a19b1 | |||
| 184389a89e | |||
| fe36a970c2 | |||
| 4a0e2b3b7d | |||
| 68843ab6b3 | |||
| dbd80694aa | |||
| da4c692283 | |||
| b45c93d8a2 | |||
| 6f45044a9a | |||
| bdec53eb97 | |||
| dfcc0a26fe | |||
| 620cd8d88c | |||
| 83b6782078 | |||
| f7a7b0692d | |||
| 0a758bbb33 | |||
| 14d047cbb9 | |||
| 6768c01c81 | |||
| c9aceeb387 | |||
| 8c197d5519 | |||
| c70c3652bf | |||
| 52de24a076 | |||
| 231bb1f1ac | |||
| 1c6bf95953 | |||
| 18949140fd | |||
| 5f6ebbff31 | |||
| 085712437e | |||
| f08fbf9e8b | |||
| f462afe56c | |||
| 68d53af1b4 | |||
| 2f4e79bc50 | |||
| 8fd2eecb85 | |||
| edf8728b04 | |||
| 970aaa518f | |||
| 80a8d3b0cd | |||
| c0f9fa9933 | |||
| cce38449a5 | |||
| b1d04f21f7 | |||
| e0bb34052f | |||
| 486bd648d3 | |||
| 2ebe1916a4 | |||
| eee5753a7f | |||
| c84b8c7a6a | |||
| bb947f25b5 | |||
| 810cdf4ecb | |||
| cbbf39c6cc | |||
| 18406d4332 | |||
| 4a9b13126b | |||
| c6b6830c5c | |||
| 7cb6ae527b | |||
| 7eb5df248f |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
z80
|
||||
.vscode/*
|
||||
build/*
|
||||
BIN
ROBOCOP1.TAP
Normal file
BIN
ROBOCOP1.TAP
Normal file
Binary file not shown.
BIN
alien8.sav
Normal file
BIN
alien8.sav
Normal file
Binary file not shown.
BIN
alien8.tap
Normal file
BIN
alien8.tap
Normal file
Binary file not shown.
255
ay-3-8912.cpp
Normal file
255
ay-3-8912.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
#include "ay-3-8912.h"
|
||||
#include "z80.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include "zx_ula.h"
|
||||
|
||||
#define MIXER_REG_A_TONE 0x01
|
||||
#define MIXER_REG_B_TONE 0x02
|
||||
#define MIXER_REG_C_TONE 0x04
|
||||
#define MIXER_REG_A_NOISE 0x08
|
||||
#define MIXER_REG_B_NOISE 0x10
|
||||
#define MIXER_REG_C_NOISE 0x20
|
||||
#define MIXER_REG_PORT_A 0x40
|
||||
#define MIXER_REG_PORT_B 0x80
|
||||
|
||||
#define ENVELOPE_HOLD 0x01
|
||||
#define ENVELOPE_ALTERNATE 0x02
|
||||
#define ENVELOPE_ATTACK 0x04
|
||||
#define ENVELOPE_CONTINUE 0x08
|
||||
|
||||
namespace audio
|
||||
{
|
||||
bool enabled = false;
|
||||
float cycles_per_sample;
|
||||
|
||||
uint8_t volume_table[16] {0,2,3,4,6,8,11,16,23,32,45,64,90,128,180,255};
|
||||
SDL_AudioDeviceID sdlAudioDevice;
|
||||
|
||||
uint8_t selected_register {0};
|
||||
uint8_t registers[16];
|
||||
|
||||
uint32_t channel_a_tone_freq;
|
||||
uint32_t channel_a_tone_freq_counter;
|
||||
uint8_t channel_a_tone_level;
|
||||
uint8_t channel_a_level;
|
||||
|
||||
uint32_t channel_b_tone_freq;
|
||||
uint32_t channel_b_tone_freq_counter;
|
||||
uint8_t channel_b_tone_level;
|
||||
uint8_t channel_b_level;
|
||||
|
||||
uint32_t channel_c_tone_freq;
|
||||
uint32_t channel_c_tone_freq_counter;
|
||||
uint8_t channel_c_tone_level;
|
||||
uint8_t channel_c_level;
|
||||
|
||||
uint32_t noise_freq;
|
||||
uint32_t noise_freq_counter;
|
||||
uint8_t noise_level;
|
||||
uint32_t shiftreg;
|
||||
|
||||
uint32_t envelope_freq;
|
||||
uint32_t envelope_freq_counter;
|
||||
int8_t envelope_volume;
|
||||
int8_t envelope_direction;
|
||||
|
||||
void select_register(int port, int val)
|
||||
{
|
||||
selected_register = val & 0xf;
|
||||
}
|
||||
|
||||
int read_register(int port)
|
||||
{
|
||||
return registers[selected_register];
|
||||
}
|
||||
|
||||
void write_register(int port, int val)
|
||||
{
|
||||
registers[selected_register] = val;
|
||||
|
||||
switch (selected_register) {
|
||||
case 0:
|
||||
case 1: {
|
||||
uint32_t freq = uint32_t(registers[0]) + (uint32_t(registers[1]) * 256);
|
||||
channel_a_tone_freq = (freq==0?1:freq)<<4;
|
||||
channel_a_tone_freq_counter = 0;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
case 3: {
|
||||
uint32_t freq = uint32_t(registers[2]) + (uint32_t(registers[3]) * 256);
|
||||
channel_b_tone_freq = (freq==0?1:freq)<<4;
|
||||
channel_b_tone_freq_counter = 0;
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
case 5: {
|
||||
uint32_t freq = uint32_t(registers[4]) + (uint32_t(registers[5]) * 256);
|
||||
channel_c_tone_freq = (freq==0?1:freq)<<4;
|
||||
channel_c_tone_freq_counter = 0;
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
uint32_t freq = (registers[6] & 0x1f);
|
||||
noise_freq = (freq==0?1:freq)<<4;
|
||||
noise_freq_counter = 0;
|
||||
break;
|
||||
}
|
||||
case 11:
|
||||
case 12: {
|
||||
uint32_t freq = registers[11] | (registers[12] << 8);
|
||||
envelope_freq = (freq==0?1:freq)<<4;
|
||||
break;
|
||||
}
|
||||
case 13:
|
||||
if (registers[13]&ENVELOPE_ATTACK) {
|
||||
envelope_volume = 0;
|
||||
envelope_direction = 1;
|
||||
} else {
|
||||
envelope_volume = 15;
|
||||
envelope_direction = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
reset();
|
||||
z80::connect_port(0xfffd, 0xffff, audio::read_register, audio::select_register);
|
||||
z80::connect_port(0xbffd, 0xffff, nullptr, audio::write_register);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
enabled = true;
|
||||
selected_register = 0;
|
||||
for (int i=0; i<16;++i) registers[i]=0;
|
||||
}
|
||||
|
||||
void update(uint32_t dt)
|
||||
{
|
||||
if (!enabled) return;
|
||||
//dt = dt >> 1;
|
||||
|
||||
// Oscillate (0-1) channel A tone level given its frequency
|
||||
channel_a_tone_freq_counter+=dt;
|
||||
if (channel_a_tone_freq_counter >= channel_a_tone_freq) {
|
||||
channel_a_tone_freq_counter -= channel_a_tone_freq;
|
||||
channel_a_tone_level = channel_a_tone_level ^ 1;
|
||||
}
|
||||
|
||||
// Oscillate (0-1) channel B tone level given its frequency
|
||||
channel_b_tone_freq_counter+=dt;
|
||||
if (channel_b_tone_freq_counter >= channel_b_tone_freq) {
|
||||
channel_b_tone_freq_counter -= channel_b_tone_freq;
|
||||
channel_b_tone_level = channel_b_tone_level ^ 1;
|
||||
}
|
||||
|
||||
// Oscillate (0-1) channel C tone level given its frequency
|
||||
channel_c_tone_freq_counter+=dt;
|
||||
if (channel_c_tone_freq_counter >= channel_c_tone_freq) {
|
||||
channel_c_tone_freq_counter -= channel_c_tone_freq;
|
||||
channel_c_tone_level = channel_c_tone_level ^ 1;
|
||||
}
|
||||
|
||||
// Oscillate (0-1) noise level given its frequency and shift register
|
||||
noise_freq_counter+=dt;
|
||||
if (noise_freq_counter >= noise_freq) {
|
||||
noise_freq_counter -= noise_freq;
|
||||
//noise_level = noise_level ^ shiftreg;
|
||||
//uint32_t newbit = shiftreg ^ (shiftreg >> 3);
|
||||
//shiftreg = ((shiftreg >> 1) & 0xffff) | ((newbit << 16) & 0x10000);
|
||||
shiftreg = (shiftreg * 2 + 1) ^ (((shiftreg >> 16) ^ (shiftreg >> 13)) & 1);
|
||||
noise_level = ((shiftreg >> 16) & 1);
|
||||
}
|
||||
|
||||
|
||||
// Develop (0-15) envelope volume given its frequency and shape
|
||||
envelope_freq_counter+=dt;
|
||||
if (envelope_freq_counter >= envelope_freq) {
|
||||
envelope_freq_counter -= envelope_freq;
|
||||
|
||||
envelope_volume += envelope_direction;
|
||||
if ( (envelope_volume > 15) || (envelope_volume < 0) ) {
|
||||
switch(registers[13]&0xf) {
|
||||
case 8:
|
||||
case 12:
|
||||
envelope_volume &= 0x0f;
|
||||
break;
|
||||
case 10:
|
||||
case 14:
|
||||
envelope_direction = -envelope_direction;
|
||||
envelope_volume += envelope_direction;
|
||||
case 11:
|
||||
case 13:
|
||||
envelope_direction = 0;
|
||||
envelope_volume = 15;
|
||||
default:
|
||||
envelope_direction = 0;
|
||||
envelope_volume = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t get_sample()
|
||||
{
|
||||
// Mix tone and noise on channel A given register 7 values
|
||||
channel_a_level = 1;
|
||||
if ((registers[7] & MIXER_REG_A_TONE)==0) channel_a_level &= channel_a_tone_level;
|
||||
if ((registers[7] & MIXER_REG_A_NOISE)==0) channel_a_level &= noise_level;
|
||||
|
||||
// Mix tone and noise on channel B given register 7 values
|
||||
channel_b_level = 1;
|
||||
if ((registers[7] & MIXER_REG_B_TONE)==0) channel_b_level &= channel_b_tone_level;
|
||||
if ((registers[7] & MIXER_REG_B_NOISE)==0) channel_b_level &= noise_level;
|
||||
|
||||
// Mix tone and noise on channel C given register 7 values
|
||||
channel_c_level = 1;
|
||||
if ((registers[7] & MIXER_REG_C_TONE)==0) channel_c_level &= channel_c_tone_level;
|
||||
if ((registers[7] & MIXER_REG_C_NOISE)==0) channel_c_level &= noise_level;
|
||||
|
||||
//zx_ula::set_border_color(channel_a_tone_level&0xf);
|
||||
|
||||
const uint8_t channel_a_volume = (registers[8]&0x10) ? envelope_volume : registers[8]&0xf;
|
||||
const uint8_t channel_b_volume = (registers[9]&0x10) ? envelope_volume : registers[9]&0xf;
|
||||
const uint8_t channel_c_volume = (registers[10]&0x10) ? envelope_volume : registers[10]&0xf;
|
||||
|
||||
const uint8_t channel_a_sample = volume_table[(channel_a_level&1) * channel_a_volume] >> 1;
|
||||
const uint8_t channel_b_sample = volume_table[(channel_b_level&1) * channel_b_volume] >> 1;
|
||||
const uint8_t channel_c_sample = volume_table[(channel_c_level&1) * channel_c_volume] >> 1;
|
||||
|
||||
uint8_t sample = (channel_a_sample+channel_b_sample+channel_c_sample)&0xff;
|
||||
return sample;
|
||||
}
|
||||
|
||||
namespace debug
|
||||
{
|
||||
uint8_t get_register(uint8_t num) { return registers[num]; }
|
||||
uint32_t get_channel_a_tone_freq() { return channel_a_tone_freq; }
|
||||
uint32_t get_channel_a_tone_freq_counter() { return channel_a_tone_freq_counter; }
|
||||
uint8_t get_channel_a_tone_level() { return channel_a_tone_level; }
|
||||
uint8_t get_channel_a_level() { return channel_a_level; }
|
||||
|
||||
uint32_t get_channel_b_tone_freq() { return channel_b_tone_freq; }
|
||||
uint32_t get_channel_b_tone_freq_counter() { return channel_b_tone_freq_counter; }
|
||||
uint8_t get_channel_b_tone_level() { return channel_b_tone_level; }
|
||||
uint8_t get_channel_b_level() { return channel_b_level; }
|
||||
|
||||
uint32_t get_channel_c_tone_freq() { return channel_c_tone_freq; }
|
||||
uint32_t get_channel_c_tone_freq_counter() { return channel_c_tone_freq_counter; }
|
||||
uint8_t get_channel_c_tone_level() { return channel_c_tone_level; }
|
||||
uint8_t get_channel_c_level() { return channel_c_level; }
|
||||
|
||||
uint32_t get_noise_freq() { return noise_freq; }
|
||||
uint32_t get_noise_freq_counter() { return noise_freq_counter; }
|
||||
uint8_t get_noise_level() { return noise_level; }
|
||||
uint32_t get_shiftreg() { return shiftreg; }
|
||||
|
||||
uint32_t get_envelope_freq() { return envelope_freq; }
|
||||
uint32_t get_envelope_freq_counter() { return envelope_freq_counter; }
|
||||
int8_t get_envelope_volume() { return envelope_volume; }
|
||||
int8_t get_envelope_direction() { return envelope_direction; }
|
||||
}
|
||||
|
||||
}
|
||||
45
ay-3-8912.h
Normal file
45
ay-3-8912.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace audio
|
||||
{
|
||||
void select_register(int port, int val);
|
||||
int read_register(int port);
|
||||
void write_register(int port, int val);
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
void update(uint32_t dt);
|
||||
|
||||
uint8_t get_sample();
|
||||
|
||||
namespace debug
|
||||
{
|
||||
uint8_t get_register(uint8_t num);
|
||||
uint32_t get_channel_a_tone_freq();
|
||||
uint32_t get_channel_a_tone_freq_counter();
|
||||
uint8_t get_channel_a_tone_level();
|
||||
uint8_t get_channel_a_level();
|
||||
|
||||
uint32_t get_channel_b_tone_freq();
|
||||
uint32_t get_channel_b_tone_freq_counter();
|
||||
uint8_t get_channel_b_tone_level();
|
||||
uint8_t get_channel_b_level();
|
||||
|
||||
uint32_t get_channel_c_tone_freq();
|
||||
uint32_t get_channel_c_tone_freq_counter();
|
||||
uint8_t get_channel_c_tone_level();
|
||||
uint8_t get_channel_c_level();
|
||||
|
||||
uint32_t get_noise_freq();
|
||||
uint32_t get_noise_freq_counter();
|
||||
uint8_t get_noise_level();
|
||||
uint32_t get_shiftreg();
|
||||
|
||||
uint32_t get_envelope_freq();
|
||||
uint32_t get_envelope_freq_counter();
|
||||
int8_t get_envelope_volume();
|
||||
int8_t get_envelope_direction();
|
||||
}
|
||||
}
|
||||
88
ay_viewer.cpp
Normal file
88
ay_viewer.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "ay_viewer.h"
|
||||
#include "ui.h"
|
||||
#include "ay-3-8912.h"
|
||||
|
||||
void ay_viewer::show()
|
||||
{
|
||||
if (!win)
|
||||
{
|
||||
win = SDL_CreateWindow("AY-3-8912 Viewer", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 512, 512, SDL_WINDOW_SHOWN);
|
||||
ren = SDL_CreateRenderer(win, -1, 0);
|
||||
tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 512, 512);
|
||||
uitex = ui::createtexture(ren);
|
||||
|
||||
//ui::window::registerWindow(SDL_GetWindowID(win), handleEvent);
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
void ay_viewer::refresh()
|
||||
{
|
||||
if (!win) return;
|
||||
|
||||
ui::setrenderer(ren, uitex);
|
||||
|
||||
/*
|
||||
Uint32 *pixels;
|
||||
int pitch;
|
||||
SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch);
|
||||
for (int i=0; i<65536; ++i)
|
||||
{
|
||||
//uint8_t tag = z80::getMemTag(i);
|
||||
//pixels[i] = tag==MEMTAG_NONE ? 0x808080 : tag==MEMTAG_DATA ? 0x0000FF : tag==MEMTAG_MIXED ? 0xFF00FF : 0x00FF00;
|
||||
uint32_t none_color = i<0x4000 ? 0x101010 : i<0x5800 ? 0x202020 : i<0x5b00 ? 0x404040 : 0x808080;
|
||||
uint8_t tag = mem::getTag(i);
|
||||
pixels[i] = !(tag & MEMTAG_TOUCHED) ? none_color : (tag & MEMTAG_TINST) ? 0x00FF00 : (tag & MEMTAG_TREPEAT) ? 0xFF0000 : 0x0000FF;
|
||||
}
|
||||
pixels[z80::getPC()] = 0xFFFFFF;
|
||||
|
||||
SDL_UnlockTexture(tex);
|
||||
SDL_RenderCopy(ren, tex, NULL, NULL);
|
||||
*/
|
||||
SDL_SetRenderDrawColor(ren, 30, 30, 30, 255);
|
||||
SDL_RenderClear(ren);
|
||||
|
||||
char temp[256];
|
||||
|
||||
ui::panel(0,0,9,18,"REG:");
|
||||
|
||||
for (int i=0; i<16; ++i) {
|
||||
sprintf(temp, "%2u: %3u", i, audio::debug::get_register(i));
|
||||
ui::printtxt(0,i,temp, COLOR_WHITE);
|
||||
}
|
||||
|
||||
ui::panel(9,0,18,18,"CHANNEL A:");
|
||||
sprintf(temp, "TONE FREQ: %4u", audio::debug::get_channel_a_tone_freq());
|
||||
ui::printtxt(0,0,temp, COLOR_WHITE);
|
||||
sprintf(temp, "COUNTER: %4u", audio::debug::get_channel_a_tone_freq_counter());
|
||||
ui::printtxt(0,1,temp, COLOR_WHITE);
|
||||
sprintf(temp, "TONE LEVL: %1u", audio::debug::get_channel_a_tone_level());
|
||||
ui::printtxt(0,2,temp, COLOR_WHITE);
|
||||
sprintf(temp, "LEVEL: %1u", audio::debug::get_channel_a_level());
|
||||
ui::printtxt(0,3,temp, COLOR_WHITE);
|
||||
|
||||
|
||||
SDL_RenderPresent(ren);
|
||||
}
|
||||
|
||||
void ay_viewer::hide()
|
||||
{
|
||||
//ui::window::unregisterWindow(SDL_GetWindowID(win));
|
||||
SDL_DestroyTexture(uitex); uitex = nullptr;
|
||||
SDL_DestroyTexture(tex); tex = nullptr;
|
||||
SDL_DestroyRenderer(ren); ren = nullptr;
|
||||
SDL_DestroyWindow(win); win = nullptr;
|
||||
}
|
||||
|
||||
void ay_viewer::focus()
|
||||
{
|
||||
if (win) {
|
||||
SDL_RaiseWindow(win);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
bool ay_viewer::handleEvent(SDL_Event *e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
12
ay_viewer.h
Normal file
12
ay_viewer.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "z80viewer.h"
|
||||
|
||||
class ay_viewer : public z80viewer
|
||||
{
|
||||
public:
|
||||
void show();
|
||||
void refresh();
|
||||
void hide();
|
||||
void focus();
|
||||
bool handleEvent(SDL_Event *e);
|
||||
};
|
||||
BIN
batman.sav
Normal file
BIN
batman.sav
Normal file
Binary file not shown.
BIN
batmanritman.tap
Normal file
BIN
batmanritman.tap
Normal file
Binary file not shown.
@@ -1,51 +0,0 @@
|
||||
#include <ncurses.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
initscr();
|
||||
|
||||
if (has_colors() == FALSE) {
|
||||
endwin();
|
||||
printf("Your terminal does not support color\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int w, h;
|
||||
getmaxyx(stdscr, h, w);
|
||||
|
||||
start_color();
|
||||
//init_pair(1, COLOR_WHITE, COLOR_BLACK);
|
||||
init_pair(1, COLOR_YELLOW, COLOR_BLUE);
|
||||
|
||||
WINDOW *win_regs = newwin(10,20,0,0);
|
||||
WINDOW *win_mem = newwin(10,w-20,0,20);
|
||||
WINDOW *win_code = newwin(h-10,w,10,0);
|
||||
refresh();
|
||||
|
||||
box(win_mem,0,0);
|
||||
mvwprintw(win_mem, 0, 2, " MEMORY: ");
|
||||
mvwprintw(win_mem, 1, 2, "%ix%i", w, h);
|
||||
wrefresh(win_mem);
|
||||
|
||||
box(win_regs,0,0);
|
||||
mvwprintw(win_regs, 0, 2, " REGISTERS: ");
|
||||
mvwprintw(win_regs, 1, 2, "AF: 00");
|
||||
mvwprintw(win_regs, 2, 2, "BC: 00");
|
||||
mvwprintw(win_regs, 3, 2, "DE: 00");
|
||||
mvwprintw(win_regs, 4, 2, "HL: 00");
|
||||
wrefresh(win_regs);
|
||||
|
||||
box(win_code,0,0);
|
||||
wattron(win_code, COLOR_PAIR(1));
|
||||
mvwprintw(win_code, 1, 1, "Hello world %s !!!", "hola");
|
||||
wattroff(win_code, COLOR_PAIR(1));
|
||||
wrefresh(win_code);
|
||||
|
||||
refresh();
|
||||
|
||||
getch();
|
||||
endwin();
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
fernandomartin.tap
Normal file
BIN
fernandomartin.tap
Normal file
Binary file not shown.
275
file.cpp
Normal file
275
file.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include "file.h"
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
#define DEFAULT_FILENAME "data.jf2"
|
||||
#define DEFAULT_FOLDER "data/"
|
||||
#define CONFIG_FILENAME "config.txt"
|
||||
|
||||
namespace file
|
||||
{
|
||||
struct file_t
|
||||
{
|
||||
std::string path;
|
||||
uint32_t size;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
std::vector<file_t> toc;
|
||||
|
||||
/* El std::map me fa coses rares, vaig a usar un good old std::vector amb una estructura key,value propia i au, que sempre funciona */
|
||||
struct keyvalue_t {
|
||||
std::string key, value;
|
||||
};
|
||||
|
||||
char *resource_filename = NULL;
|
||||
char *resource_folder = NULL;
|
||||
int file_source = SOURCE_FILE;
|
||||
char scratch[255];
|
||||
static std::string config_folder;
|
||||
std::vector<keyvalue_t> config;
|
||||
|
||||
void setResourceFilename(const char *str) {
|
||||
if (resource_filename != NULL) free(resource_filename);
|
||||
resource_filename = (char*)malloc(strlen(str)+1);
|
||||
strcpy(resource_filename, str);
|
||||
}
|
||||
|
||||
void setResourceFolder(const char *str) {
|
||||
if (resource_folder != NULL) free(resource_folder);
|
||||
resource_folder = (char*)malloc(strlen(str)+1);
|
||||
strcpy(resource_folder, str);
|
||||
}
|
||||
|
||||
void setSource(const int src) {
|
||||
file_source = src%2; // mod 2 so it always is a valid value, 0 (file) or 1 (folder)
|
||||
if (src==SOURCE_FOLDER && resource_folder==NULL) setResourceFolder(DEFAULT_FOLDER);
|
||||
}
|
||||
|
||||
bool getDictionary() {
|
||||
if (resource_filename == NULL) setResourceFilename(DEFAULT_FILENAME);
|
||||
|
||||
std::ifstream fi (resource_filename, std::ios::binary);
|
||||
if (!fi.is_open()) return false;
|
||||
char header[4];
|
||||
fi.read(header, 4);
|
||||
uint32_t num_files, toc_offset;
|
||||
fi.read((char*)&num_files, 4);
|
||||
fi.read((char*)&toc_offset, 4);
|
||||
fi.seekg(toc_offset);
|
||||
|
||||
for (uint i=0; i<num_files; ++i)
|
||||
{
|
||||
uint32_t file_offset, file_size;
|
||||
fi.read( (char*)&file_offset, 4 );
|
||||
fi.read( (char*)&file_size, 4 );
|
||||
uint8_t path_size;
|
||||
fi.read( (char*)&path_size, 1 );
|
||||
char file_name[path_size+1];
|
||||
fi.read( file_name, path_size );
|
||||
file_name[path_size] = 0;
|
||||
std::string filename = file_name;
|
||||
toc.push_back({filename, file_size, file_offset});
|
||||
}
|
||||
fi.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
char *getFilenameWithFolder(const char* filename) {
|
||||
strcpy(scratch, resource_folder);
|
||||
strcat(scratch, filename);
|
||||
return scratch;
|
||||
}
|
||||
|
||||
FILE *getFilePointer(const char *resourcename, int& filesize, const bool binary) {
|
||||
|
||||
if (file_source==SOURCE_FILE and toc.size()==0) {
|
||||
if (not getDictionary()) setSource(SOURCE_FOLDER);
|
||||
}
|
||||
|
||||
FILE *f;
|
||||
|
||||
if (file_source==SOURCE_FILE) {
|
||||
bool found = false;
|
||||
uint32_t count = 0;
|
||||
while( !found && count < toc.size() ) {
|
||||
found = ( std::string(resourcename) == toc[count].path );
|
||||
if( !found ) count++;
|
||||
}
|
||||
|
||||
if( !found ) {
|
||||
perror("El recurs no s'ha trobat en l'arxiu de recursos");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
filesize = toc[count].size;
|
||||
|
||||
f = fopen(resource_filename, binary?"rb":"r");
|
||||
if (not f) {
|
||||
perror("No s'ha pogut obrir l'arxiu de recursos");
|
||||
exit(1);
|
||||
}
|
||||
fseek(f, toc[count].offset, SEEK_SET);
|
||||
} else {
|
||||
f = fopen(getFilenameWithFolder(resourcename), binary?"rb":"r");
|
||||
fseek(f, 0, SEEK_END);
|
||||
filesize = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
char *getFileBuffer(const char *resourcename, int& filesize, const bool zero_terminate) {
|
||||
FILE *f = getFilePointer(resourcename, filesize, true);
|
||||
char* buffer = (char*)malloc(zero_terminate?filesize+1:filesize);
|
||||
fread(buffer, filesize, 1, f);
|
||||
if (zero_terminate) buffer[filesize]=0;
|
||||
fclose(f);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Crea la carpeta del sistema donde guardar datos
|
||||
void setConfigFolder(const char *foldername)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
config_folder = std::string(getenv("APPDATA")) + "/" + foldername;
|
||||
#elif __APPLE__
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
const char *homedir = pw->pw_dir;
|
||||
config_folder = std::string(homedir) + "/Library/Application Support/" + foldername;
|
||||
#elif __linux__
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
const char *homedir = pw->pw_dir;
|
||||
config_folder = std::string(homedir) + "/." + foldername;
|
||||
#endif
|
||||
|
||||
struct stat st = {0};
|
||||
if (stat(config_folder.c_str(), &st) == -1)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int ret = mkdir(config_folder.c_str());
|
||||
#else
|
||||
int ret = mkdir(config_folder.c_str(), S_IRWXU);
|
||||
#endif
|
||||
|
||||
if (ret == -1)
|
||||
{
|
||||
printf("ERROR CREATING CONFIG FOLDER.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *getConfigFolder() {
|
||||
std::string folder = config_folder + "/";
|
||||
return folder.c_str();
|
||||
}
|
||||
|
||||
void loadConfigValues() {
|
||||
config.clear();
|
||||
std::string config_file = config_folder + "/config.txt";
|
||||
FILE *f = fopen(config_file.c_str(), "r");
|
||||
if (!f) return;
|
||||
|
||||
char line[1024];
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
char *value = strchr(line, '=');
|
||||
if (value) {
|
||||
*value='\0'; value++;
|
||||
value[strlen(value)-1] = '\0';
|
||||
config.push_back({line, value});
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void saveConfigValues() {
|
||||
std::string config_file = config_folder + "/config.txt";
|
||||
FILE *f = fopen(config_file.c_str(), "w");
|
||||
if (f) {
|
||||
for (auto pair : config) {
|
||||
fprintf(f, "%s=%s\n", pair.key.c_str(), pair.value.c_str());
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
const char* getConfigValueString(const char *key) {
|
||||
if (config.empty()) loadConfigValues();
|
||||
for (auto pair : config) {
|
||||
if (pair.key == std::string(key)) {
|
||||
strcpy(scratch, pair.value.c_str());
|
||||
return scratch;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int getConfigValueInteger(const char *key, const int default_value)
|
||||
{
|
||||
const char* value = getConfigValueString(key);
|
||||
if (!value) return default_value;
|
||||
return atoi(value);
|
||||
}
|
||||
|
||||
const float getConfigValueFloat(const char *key, const float default_value)
|
||||
{
|
||||
const char* value = getConfigValueString(key);
|
||||
if (!value) return default_value;
|
||||
return atof(value);
|
||||
}
|
||||
|
||||
const bool getConfigValueBool(const char *key, const bool default_value)
|
||||
{
|
||||
const char* value = getConfigValueString(key);
|
||||
if (!value) return default_value;
|
||||
return strcmp(value, "true")==0?true:false;
|
||||
}
|
||||
|
||||
void setConfigValueString(const char* key, const char* value) {
|
||||
if (config.empty()) loadConfigValues();
|
||||
for (auto &pair : config) {
|
||||
if (pair.key == std::string(key)) {
|
||||
pair.value = value;
|
||||
saveConfigValues();
|
||||
return;
|
||||
}
|
||||
}
|
||||
config.push_back({key, value});
|
||||
saveConfigValues();
|
||||
return;
|
||||
}
|
||||
|
||||
void setConfigValueInteger(const char* key, const int value)
|
||||
{
|
||||
char tmp[256];
|
||||
sprintf(tmp, "%i", value);
|
||||
setConfigValueString(key, tmp);
|
||||
}
|
||||
|
||||
void setConfigValueFloat(const char* key, const float value)
|
||||
{
|
||||
char tmp[256];
|
||||
sprintf(tmp, "%.2f", value);
|
||||
setConfigValueString(key, tmp);
|
||||
}
|
||||
|
||||
void setConfigValueBool(const char* key, const bool value)
|
||||
{
|
||||
setConfigValueString(key, value?"true":"false");
|
||||
}
|
||||
|
||||
}
|
||||
27
file.h
Normal file
27
file.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <stdio.h>
|
||||
|
||||
#define SOURCE_FILE 0
|
||||
#define SOURCE_FOLDER 1
|
||||
|
||||
namespace file
|
||||
{
|
||||
void setConfigFolder(const char *foldername);
|
||||
const char *getConfigFolder();
|
||||
|
||||
void setResourceFilename(const char *str);
|
||||
void setResourceFolder(const char *str);
|
||||
void setSource(const int src);
|
||||
|
||||
FILE *getFilePointer(const char *resourcename, int& filesize, const bool binary=false);
|
||||
char *getFileBuffer(const char *resourcename, int& filesize, const bool zero_terminate=false);
|
||||
|
||||
const char* getConfigValueString(const char *key);
|
||||
const int getConfigValueInteger(const char *key, const int default_value=0);
|
||||
const float getConfigValueFloat(const char *key, const float default_value=0.0f);
|
||||
const bool getConfigValueBool(const char *key, const bool default_value=false);
|
||||
void setConfigValueString(const char* key, const char* value);
|
||||
void setConfigValueInteger(const char* key, const int value);
|
||||
void setConfigValueFloat(const char* key, const float value);
|
||||
void setConfigValueBool(const char* key, const bool value);
|
||||
}
|
||||
203
gamepad.cpp
Normal file
203
gamepad.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
#include "gamepad.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include "z80.h"
|
||||
#include "zx_ula.h"
|
||||
#include <vector>
|
||||
|
||||
#define GAMEPAD_BUTTON_LEFT 0
|
||||
#define GAMEPAD_BUTTON_RIGHT 1
|
||||
#define GAMEPAD_BUTTON_UP 2
|
||||
#define GAMEPAD_BUTTON_DOWN 3
|
||||
#define GAMEPAD_BUTTON_FIRE 4
|
||||
|
||||
namespace gamepad
|
||||
{
|
||||
struct gamepad_t
|
||||
{
|
||||
SDL_GameController *gamepad;
|
||||
int32_t index;
|
||||
uint8_t type;
|
||||
bool buttons[5] { false, false, false, false, false };
|
||||
};
|
||||
|
||||
std::vector<gamepad_t> gamepads;
|
||||
bool kempston_active = false;
|
||||
|
||||
int kempston_port_in(int port)
|
||||
{
|
||||
for (auto gamepad: gamepads)
|
||||
{
|
||||
if (gamepad.type == GAMEPAD_TYPE_KEMPSTON)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
if (gamepad.buttons[GAMEPAD_BUTTON_FIRE]) result |= 0x10;
|
||||
if (gamepad.buttons[GAMEPAD_BUTTON_UP]) result |= 0x08;
|
||||
if (gamepad.buttons[GAMEPAD_BUTTON_DOWN]) result |= 0x04;
|
||||
if (gamepad.buttons[GAMEPAD_BUTTON_LEFT]) result |= 0x02;
|
||||
if (gamepad.buttons[GAMEPAD_BUTTON_RIGHT]) result |= 0x01;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
z80::connect_port(0x1f, 0x001f, kempston_port_in, nullptr);
|
||||
}
|
||||
|
||||
uint8_t getNextType()
|
||||
{
|
||||
uint8_t next_type = GAMEPAD_TYPE_SINCLAIR_1;
|
||||
bool found = false;
|
||||
while (!found)
|
||||
{
|
||||
found = true;
|
||||
for (auto gamepad : gamepads)
|
||||
{
|
||||
if (gamepad.type == next_type)
|
||||
{
|
||||
found = false;
|
||||
next_type++;
|
||||
if (next_type == GAMEPAD_TYPE_CUSTOM) return next_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return next_type;
|
||||
}
|
||||
|
||||
uint8_t add(int32_t index, uint8_t type)
|
||||
{
|
||||
if (SDL_IsGameController(index)) {
|
||||
gamepad_t gamepad;
|
||||
gamepad.gamepad = SDL_GameControllerOpen(index);
|
||||
if (SDL_GameControllerGetAttached(gamepad.gamepad) == SDL_TRUE) SDL_GameControllerEventState(SDL_ENABLE);
|
||||
gamepad.index = index;
|
||||
gamepad.type = ( (type == GAMEPAD_TYPE_ANY) ? getNextType() : type );
|
||||
gamepads.push_back(gamepad);
|
||||
}
|
||||
return gamepads.size()-1;
|
||||
}
|
||||
|
||||
void remove(int32_t index)
|
||||
{
|
||||
SDL_GameController *game_controller = SDL_GameControllerFromInstanceID(index);
|
||||
for (int i=0; i<gamepads.size(); ++i)
|
||||
{
|
||||
if (gamepads[i].gamepad == game_controller) {
|
||||
gamepads.erase(gamepads.begin()+i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_GameControllerClose(game_controller);
|
||||
}
|
||||
|
||||
void buttonDown(int32_t index, uint8_t button)
|
||||
{
|
||||
SDL_GameController *game_controller = SDL_GameControllerFromInstanceID(index);
|
||||
for (auto& gamepad : gamepads)
|
||||
{
|
||||
if (gamepad.gamepad == game_controller)
|
||||
{
|
||||
switch(button)
|
||||
{
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: gamepad.buttons[GAMEPAD_BUTTON_LEFT]=true; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: gamepad.buttons[GAMEPAD_BUTTON_RIGHT]=true; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: gamepad.buttons[GAMEPAD_BUTTON_DOWN]=true; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: gamepad.buttons[GAMEPAD_BUTTON_UP]=true; break;
|
||||
case SDL_CONTROLLER_BUTTON_A: gamepad.buttons[GAMEPAD_BUTTON_FIRE]=true; break;
|
||||
}
|
||||
|
||||
switch(gamepad.type)
|
||||
{
|
||||
case GAMEPAD_TYPE_SINCLAIR_1:
|
||||
switch(button)
|
||||
{
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: zx_ula::keydown('1'); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: zx_ula::keydown('2'); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: zx_ula::keydown('3'); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: zx_ula::keydown('4'); break;
|
||||
case SDL_CONTROLLER_BUTTON_A: zx_ula::keydown('5'); break;
|
||||
}
|
||||
break;
|
||||
case GAMEPAD_TYPE_SINCLAIR_2:
|
||||
switch(button)
|
||||
{
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: zx_ula::keydown('6'); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: zx_ula::keydown('7'); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: zx_ula::keydown('8'); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: zx_ula::keydown('9'); break;
|
||||
case SDL_CONTROLLER_BUTTON_A: zx_ula::keydown('0'); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void buttonUp(int32_t index, uint8_t button)
|
||||
{
|
||||
SDL_GameController *game_controller = SDL_GameControllerFromInstanceID(index);
|
||||
for (auto& gamepad : gamepads)
|
||||
{
|
||||
if (gamepad.gamepad == game_controller)
|
||||
{
|
||||
switch(button)
|
||||
{
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: gamepad.buttons[GAMEPAD_BUTTON_LEFT]=false; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: gamepad.buttons[GAMEPAD_BUTTON_RIGHT]=false; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: gamepad.buttons[GAMEPAD_BUTTON_DOWN]=false; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: gamepad.buttons[GAMEPAD_BUTTON_UP]=false; break;
|
||||
case SDL_CONTROLLER_BUTTON_A: gamepad.buttons[GAMEPAD_BUTTON_FIRE]=false; break;
|
||||
}
|
||||
|
||||
switch(gamepad.type)
|
||||
{
|
||||
case GAMEPAD_TYPE_SINCLAIR_1:
|
||||
switch(button)
|
||||
{
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: zx_ula::keyup('1'); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: zx_ula::keyup('2'); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: zx_ula::keyup('3'); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: zx_ula::keyup('4'); break;
|
||||
case SDL_CONTROLLER_BUTTON_A: zx_ula::keyup('5'); break;
|
||||
}
|
||||
break;
|
||||
case GAMEPAD_TYPE_SINCLAIR_2:
|
||||
switch(button)
|
||||
{
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: zx_ula::keyup('6'); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: zx_ula::keyup('7'); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: zx_ula::keyup('8'); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: zx_ula::keyup('9'); break;
|
||||
case SDL_CONTROLLER_BUTTON_A: zx_ula::keyup('0'); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint8_t getNumGamepads()
|
||||
{
|
||||
return gamepads.size();
|
||||
}
|
||||
|
||||
uint8_t getGamepadType(uint8_t index)
|
||||
{
|
||||
if (gamepads.size()>index) return gamepads[index].type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setGamepadType(uint8_t index, uint8_t type)
|
||||
{
|
||||
if (gamepads.size()>index) gamepads[index].type = type;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
22
gamepad.h
Normal file
22
gamepad.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#define GAMEPAD_TYPE_ANY 0
|
||||
#define GAMEPAD_TYPE_SINCLAIR_1 1
|
||||
#define GAMEPAD_TYPE_SINCLAIR_2 2
|
||||
#define GAMEPAD_TYPE_KEMPSTON 3
|
||||
#define GAMEPAD_TYPE_FULLER 4
|
||||
#define GAMEPAD_TYPE_CUSTOM 5
|
||||
|
||||
namespace gamepad
|
||||
{
|
||||
void init();
|
||||
uint8_t add(int32_t index, uint8_t type);
|
||||
void remove(int32_t index);
|
||||
void buttonDown(int32_t index, uint8_t button);
|
||||
void buttonUp(int32_t index, uint8_t button);
|
||||
|
||||
uint8_t getNumGamepads();
|
||||
uint8_t getGamepadType(uint8_t index);
|
||||
void setGamepadType(uint8_t index, uint8_t type);
|
||||
}
|
||||
BIN
glaurung.sav
Normal file
BIN
glaurung.sav
Normal file
Binary file not shown.
BIN
glaurung.tap
Normal file
BIN
glaurung.tap
Normal file
Binary file not shown.
BIN
goldenaxe1.dsk
Normal file
BIN
goldenaxe1.dsk
Normal file
Binary file not shown.
BIN
goldenaxe1_lock.dsk
Normal file
BIN
goldenaxe1_lock.dsk
Normal file
Binary file not shown.
BIN
goldenaxe2.dsk
Normal file
BIN
goldenaxe2.dsk
Normal file
Binary file not shown.
BIN
goldenaxe2_lock.dsk
Normal file
BIN
goldenaxe2_lock.dsk
Normal file
Binary file not shown.
5
lagueirtofile
Normal file
5
lagueirtofile
Normal file
@@ -0,0 +1,5 @@
|
||||
libs = -lSDL2
|
||||
cppflags = -g
|
||||
executable = z80
|
||||
sourcepath = .
|
||||
buildpath = build
|
||||
337
main.cpp
337
main.cpp
@@ -1,100 +1,317 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "zx_system.h"
|
||||
#include "z80.h"
|
||||
#include "z80dis.h"
|
||||
#include "z80debug.h"
|
||||
#include "zx_ula.h"
|
||||
#include "zx_speaker.h"
|
||||
#include "zx_screen.h"
|
||||
#include "zx_tape.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include <string.h>
|
||||
#include "ui.h"
|
||||
#include "ui_menu.h"
|
||||
#include "z80analyze.h"
|
||||
#include "ui_window.h"
|
||||
#include "zx_mem.h"
|
||||
#include "z80viewer.h"
|
||||
//#include "zx_128bankviewer.h"
|
||||
//#include "zx_128pageviewer.h"
|
||||
#include "ay-3-8912.h"
|
||||
#include "ay_viewer.h"
|
||||
#include "file.h"
|
||||
#include "gamepad.h"
|
||||
#include "zx_dialog_joystick.h"
|
||||
|
||||
uint8_t memory[65536];
|
||||
uint32_t time = 0;
|
||||
uint32_t t_states = 0;
|
||||
|
||||
namespace actions
|
||||
{
|
||||
void exitMenu()
|
||||
{
|
||||
const uint8_t dt = z80::step();
|
||||
z80debug::cont();
|
||||
zxscreen::refresh(dt);
|
||||
}
|
||||
|
||||
void exitButNotContinue()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
int fastload(int value)
|
||||
{
|
||||
zx_tape::toggleOption(ZXTAPE_OPTION_FAST_LOAD);
|
||||
return zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD);
|
||||
}
|
||||
|
||||
int stopatend(int value)
|
||||
{
|
||||
zx_tape::toggleOption(ZXTAPE_OPTION_STOP_AT_END);
|
||||
return zx_tape::getOption(ZXTAPE_OPTION_STOP_AT_END);
|
||||
}
|
||||
|
||||
int decZoom(int value)
|
||||
{
|
||||
zxscreen::decZoom();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int incZoom(int value)
|
||||
{
|
||||
zxscreen::incZoom();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fullscreen(int value)
|
||||
{
|
||||
zxscreen::toggleFullscreen();
|
||||
return zxscreen::getFullscreen();
|
||||
}
|
||||
|
||||
int fullrefresh(int value)
|
||||
{
|
||||
zxscreen::toggleFullRefresh();
|
||||
return zxscreen::getFullRefresh();
|
||||
}
|
||||
|
||||
int showAnalyzer(int value)
|
||||
{
|
||||
z80analyze::show();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mode48K(int value)
|
||||
{
|
||||
zx_system::reset(ZX_48K);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mode128K(int value)
|
||||
{
|
||||
zx_system::reset(ZX_128K);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int modePlus3(int value)
|
||||
{
|
||||
zx_system::reset(ZX_PLUS3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int reset(int value)
|
||||
{
|
||||
z80::reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exit(int value)
|
||||
{
|
||||
zx_system::shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int configureJoysticks(int value)
|
||||
{
|
||||
ui::setDialog(dialogs::joysticks::show);
|
||||
ui::menu::exitButNotContinue();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void init_menu()
|
||||
{
|
||||
ui::menu::init();
|
||||
ui::menu::setexitcallback(actions::exitMenu);
|
||||
|
||||
int menu = ui::menu::addsubmenu("FILE");
|
||||
ui::menu::addoption(menu, "LOAD TAPE", nullptr);
|
||||
ui::menu::addoption(menu, "SAVE TAPE", nullptr);
|
||||
ui::menu::addseparator(menu);
|
||||
ui::menu::addoption(menu, "LOAD STATE", nullptr);
|
||||
ui::menu::addoption(menu, "SAVE STATE", nullptr);
|
||||
ui::menu::addseparator(menu);
|
||||
ui::menu::addoption(menu, "EXIT", actions::exit);
|
||||
|
||||
menu = ui::menu::addsubmenu("SYSTEM");
|
||||
ui::menu::addoption(menu, "ZX 48K", actions::mode48K);
|
||||
ui::menu::addoption(menu, "ZX 128K/+2", actions::mode128K);
|
||||
ui::menu::addoption(menu, "ZX +2A/+3", actions::modePlus3);
|
||||
ui::menu::addseparator(menu);
|
||||
ui::menu::addoption(menu, "RESET", actions::reset);
|
||||
|
||||
menu = ui::menu::addsubmenu("TAPE");
|
||||
ui::menu::addbooloption(menu, "FAST LOAD", zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD), actions::fastload);
|
||||
ui::menu::addbooloption(menu, "STOP AT END", zx_tape::getOption(ZXTAPE_OPTION_STOP_AT_END), actions::stopatend);
|
||||
|
||||
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", zxscreen::getFullscreen(), actions::fullscreen);
|
||||
ui::menu::addseparator(menu);
|
||||
ui::menu::addbooloption(menu, "FULL REFRESH", zxscreen::getFullRefresh(), actions::fullrefresh);
|
||||
|
||||
menu = ui::menu::addsubmenu("EMULATION");
|
||||
ui::menu::addbooloption(menu, "STOP ON INVALID OP", z80::getOption(Z80_OPTION_STOP_ON_INVALID), actions::decZoom);
|
||||
ui::menu::addoption(menu, "SHOW ANALYZER", actions::showAnalyzer);
|
||||
ui::menu::addoption(menu, "CONFIGURE JOYSTICKS", actions::configureJoysticks);
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
FILE* f = fopen("48.rom", "rb");
|
||||
fread(memory, 1024, 16, f);
|
||||
fclose(f);
|
||||
|
||||
z80::reset(memory);
|
||||
z80::connect_port(0xfe, zx_ula::port_in, zx_ula::port_out);
|
||||
|
||||
file::setConfigFolder("z80");
|
||||
SDL_Init(SDL_INIT_EVERYTHING);
|
||||
z80debug::show();
|
||||
zxscreen::show();
|
||||
init_menu();
|
||||
z80debug::init();
|
||||
|
||||
zx_ula::sound_init();
|
||||
//uint32_t update_freq =
|
||||
zx_system::init(ZX_48K);
|
||||
gamepad::init();
|
||||
zx_tape::load("ROBOCOP1.TAP");
|
||||
|
||||
zx_tape::load("manic.tap");
|
||||
//if (argc==3) { z80debug::loadngo(argv[1], argv[2]); }
|
||||
|
||||
//z80debug::stop();
|
||||
|
||||
bool should_exit = false;
|
||||
SDL_Event e;
|
||||
|
||||
while (!should_exit)
|
||||
time = SDL_GetTicks();
|
||||
t_states = 0;
|
||||
bool first_time = true;
|
||||
|
||||
while (!zx_system::shuttingDown())
|
||||
{
|
||||
while (SDL_PollEvent(&e))
|
||||
{
|
||||
if (e.type == SDL_QUIT) { should_exit=true; break; }
|
||||
if (z80debug::debugging()) {
|
||||
if ((e.type==SDL_WINDOWEVENT) && ((e.window.event==SDL_WINDOWEVENT_SHOWN) || (e.window.event==SDL_WINDOWEVENT_EXPOSED))) {
|
||||
z80debug::refresh();
|
||||
zxscreen::redraw();
|
||||
}
|
||||
if (e.type == SDL_KEYDOWN) {
|
||||
if (e.key.keysym.scancode==SDL_SCANCODE_ESCAPE) {
|
||||
should_exit=true; break;
|
||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_F10) {
|
||||
const uint8_t dt = z80::step();
|
||||
z80debug::refresh();
|
||||
zxscreen::refresh(dt);
|
||||
zxscreen::redraw();
|
||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_F11) {
|
||||
const uint8_t dt = z80debug::next();
|
||||
zxscreen::refresh(dt);
|
||||
zxscreen::redraw();
|
||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_F5) {
|
||||
bool result = true;
|
||||
|
||||
if (e.type == SDL_QUIT) { zx_system::shutdown(); break; }
|
||||
if (e.type == SDL_JOYDEVICEADDED) {
|
||||
const uint8_t index = gamepad::add(e.jdevice.which, GAMEPAD_TYPE_ANY);
|
||||
printf("JOYSTICK CONECTAT: %i\n", e.jdevice.which);
|
||||
if (!first_time) dialogs::joysticks::init(index);
|
||||
}
|
||||
if (e.type == SDL_JOYDEVICEREMOVED) {
|
||||
gamepad::remove(e.jdevice.which);
|
||||
printf("JOYSTICK DESCONECTAT: %i\n", e.jdevice.which);
|
||||
}
|
||||
if (e.type == SDL_CONTROLLERBUTTONDOWN) {
|
||||
gamepad::buttonDown(e.jbutton.which, e.jbutton.button);
|
||||
}
|
||||
if (e.type == SDL_CONTROLLERBUTTONUP) {
|
||||
gamepad::buttonUp(e.jbutton.which, e.jbutton.button);
|
||||
}
|
||||
if (e.type == SDL_MOUSEBUTTONDOWN) result = ui::window::sendEvent(e.button.windowID, &e);
|
||||
if (e.type == SDL_MOUSEBUTTONUP) result = ui::window::sendEvent(e.button.windowID, &e);
|
||||
if (e.type == SDL_MOUSEMOTION) result = ui::window::sendEvent(e.motion.windowID, &e);
|
||||
if (e.type == SDL_WINDOWEVENT) result = ui::window::sendEvent(e.window.windowID, &e);
|
||||
if (e.type == SDL_MOUSEWHEEL) result = ui::window::sendEvent(e.wheel.windowID, &e);
|
||||
if (e.type == SDL_TEXTINPUT) result = ui::window::sendEvent(e.text.windowID, &e);
|
||||
if (e.type == SDL_KEYDOWN) {
|
||||
if (e.key.keysym.scancode==SDL_SCANCODE_F5) {
|
||||
if (z80debug::debugging()) {
|
||||
z80debug::history::gototop();
|
||||
const uint8_t dt = z80::step();
|
||||
z80debug::cont();
|
||||
zxscreen::refresh(dt);
|
||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_RETURN) {
|
||||
z80debug::executeConsole();
|
||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_BACKSPACE) {
|
||||
z80debug::DeleteCharConsole();
|
||||
}
|
||||
}
|
||||
if (e.type == SDL_TEXTINPUT) {
|
||||
z80debug::sendToConsole(e.text.text);
|
||||
}
|
||||
} else {
|
||||
if (e.type == SDL_KEYDOWN) {
|
||||
if (e.key.keysym.scancode==SDL_SCANCODE_F8) {
|
||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_F8) {
|
||||
if (!z80debug::debugging()) {
|
||||
z80debug::stop();
|
||||
zxscreen::redraw();
|
||||
} else {
|
||||
z80debug::show();
|
||||
}
|
||||
if (e.key.keysym.scancode==SDL_SCANCODE_F12) {
|
||||
zx_tape::play();
|
||||
z80viewer::refreshAll();
|
||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_F10) {
|
||||
if (z80debug::debugging()) {
|
||||
z80debug::show();
|
||||
z80debug::history::gototop();
|
||||
const uint8_t dt = z80::step();
|
||||
z80debug::refresh();
|
||||
zxscreen::fullrefresh();
|
||||
zxscreen::redraw();
|
||||
z80analyze::refresh();
|
||||
z80viewer::refreshAll();
|
||||
}
|
||||
if (e.key.keysym.scancode==SDL_SCANCODE_F11) {
|
||||
zx_tape::rewind();
|
||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_F11) {
|
||||
if (z80debug::debugging()) {
|
||||
z80debug::show();
|
||||
z80debug::history::gototop();
|
||||
const uint8_t dt = z80debug::next();
|
||||
z80debug::refresh();
|
||||
zxscreen::refresh(dt);
|
||||
zxscreen::redraw();
|
||||
z80analyze::refresh();
|
||||
z80viewer::refreshAll();
|
||||
}
|
||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_F12) {
|
||||
if (z80debug::debugging()) {
|
||||
z80debug::show();
|
||||
z80debug::history::gototop();
|
||||
const uint8_t dt = z80debug::stepout();
|
||||
z80debug::refresh();
|
||||
zxscreen::refresh(dt);
|
||||
zxscreen::redraw();
|
||||
z80analyze::refresh();
|
||||
z80viewer::refreshAll();
|
||||
}
|
||||
}
|
||||
result = ui::window::sendEvent(e.key.windowID, &e);
|
||||
}
|
||||
|
||||
if (e.type == SDL_MOUSEBUTTONUP && e.button.button==1) ui::setClicked(true);
|
||||
|
||||
if (!result)
|
||||
zx_system::shutdown(); break;
|
||||
}
|
||||
if (!z80debug::debugging()) {
|
||||
if (z80debug::isbreak(z80::getPC(), 9)) {
|
||||
z80debug::stop();
|
||||
zxscreen::redraw();
|
||||
} else {
|
||||
//if (z80::getPC()==0x05C8) zx_tape::go_berserk();
|
||||
uint8_t dt = z80::step();
|
||||
zx_tape::update(dt);
|
||||
//if (!zx_tape::berserk()) {
|
||||
zx_ula::sound_update(dt);
|
||||
//}
|
||||
zxscreen::refresh(dt);
|
||||
|
||||
first_time = false;
|
||||
if (!z80debug::debugging() && !z80debug::paused()) {
|
||||
|
||||
// En cada bucle fem 10 pasos de la CPU, sino s'ofega (jo en veig 5)
|
||||
for (int i=0;i<5;++i) {
|
||||
if (z80debug::isbreak(z80::getPC(), 9)) {
|
||||
z80debug::stop();
|
||||
zxscreen::redraw();
|
||||
break;
|
||||
} else {
|
||||
uint8_t dt = z80::step();
|
||||
t_states += dt;
|
||||
zx_system::update(dt);
|
||||
zxscreen::refresh(dt);
|
||||
if (z80debug::debugging()) break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t update_freq = z80::getClock()/10;
|
||||
if (t_states>=update_freq)
|
||||
{
|
||||
// Esperem a que es compleixca el temps corresponent als t states executats
|
||||
while (SDL_GetTicks()<time+100) {}
|
||||
|
||||
t_states -= update_freq;
|
||||
time = SDL_GetTicks();
|
||||
z80analyze::refresh();
|
||||
z80viewer::refreshAll();
|
||||
}
|
||||
z80analyze::refresh(true);
|
||||
|
||||
|
||||
} else if (z80debug::paused()) {
|
||||
zxscreen::redraw(false);
|
||||
|
||||
if (ui::hasDialog())
|
||||
ui::callDialog();
|
||||
else
|
||||
ui::menu::show();
|
||||
|
||||
zxscreen::present();
|
||||
}
|
||||
ui::setClicked(false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
13
symbols-glaurung.txt
Normal file
13
symbols-glaurung.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
0x5e38 STACK
|
||||
0x9a01 DRW_ROOM
|
||||
0x9f00 DRW_SCO_HERO
|
||||
0x9f0f VAR_VIDES
|
||||
0x9f8d DRW_SCO_INV
|
||||
0xa00a DRW_SCO_LIVS
|
||||
0xa01a DRW_SCO_NBAG
|
||||
0xa022 DRW_SCO_NARW
|
||||
0xa02a DRW_SCO_PNTS
|
||||
0xc661 CLEAR_SCR
|
||||
0xed5e MAINLOOP
|
||||
0xf2b1 DRW_HERO
|
||||
0xfed5 COUNT500
|
||||
1
symbols.txt
Normal file
1
symbols.txt
Normal file
@@ -0,0 +1 @@
|
||||
0x6580 START
|
||||
27
test.asm
Normal file
27
test.asm
Normal file
@@ -0,0 +1,27 @@
|
||||
.org $8000
|
||||
|
||||
ei
|
||||
START:
|
||||
ld a, 2
|
||||
out ($fe), a
|
||||
ld b, 255
|
||||
ld de, TILES
|
||||
ld hl, $4000
|
||||
LOOP:
|
||||
ld a, (de)
|
||||
ld (hl), a
|
||||
inc e
|
||||
inc h
|
||||
djnz LOOP
|
||||
ld a, (de)
|
||||
ld hl, $5800
|
||||
ld (hl), a
|
||||
|
||||
ld a, 0
|
||||
out ($fe), a
|
||||
|
||||
halt
|
||||
jr START
|
||||
|
||||
TILES: db $81, $42, $24, $18, $18, $24, $42, $81, $4d
|
||||
|
||||
171
ui.cpp
Normal file
171
ui.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "ui.h"
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
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;
|
||||
|
||||
void (*dialog)(void) = nullptr;
|
||||
|
||||
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)
|
||||
{
|
||||
ren = renderer;
|
||||
tex = texture;
|
||||
offset_x = offset_y = 0;
|
||||
}
|
||||
|
||||
void setoffset(uint8_t x, uint8_t y)
|
||||
{
|
||||
offset_x = x;
|
||||
offset_y = y;
|
||||
}
|
||||
|
||||
void incoffset(uint8_t x, uint8_t y)
|
||||
{
|
||||
offset_x += x;
|
||||
offset_y += y;
|
||||
}
|
||||
|
||||
uint8_t getOffsetX()
|
||||
{
|
||||
return offset_x;
|
||||
}
|
||||
|
||||
uint8_t getOffsetY()
|
||||
{
|
||||
return offset_y;
|
||||
}
|
||||
|
||||
bool mouseInside(int x, int y, int w, int h)
|
||||
{
|
||||
int mx, my;
|
||||
Uint32 mb = SDL_GetMouseState(&mx, &my);
|
||||
mx=mx/CHR_W; my=my/CHR_H;
|
||||
mx -= offset_x;
|
||||
my -= offset_y;
|
||||
return (mx >= x) && (mx < x+w) && (my >= y) && (my < y+h);
|
||||
}
|
||||
|
||||
void panel(int x, int y, int w, int h, const char *title)
|
||||
{
|
||||
ui::setoffset(x, y);
|
||||
|
||||
ui::box(0,0,w,h,COLOR_WHITE);
|
||||
ui::printrect(2,0, strlen(title)+2,1, COLOR_DARK);
|
||||
ui::printtxt(3,0, title, COLOR_WHITE);
|
||||
|
||||
ui::incoffset(1, 1);
|
||||
}
|
||||
|
||||
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 placechar(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 {x, y, CHR_W*2, CHR_H*2};
|
||||
SDL_RenderCopy(ren, tex, &src, &dst);
|
||||
}
|
||||
|
||||
void placetxt(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) placechar(x+i*CHR_W*2, y, text[i]);
|
||||
}
|
||||
|
||||
void setClicked(const bool value)
|
||||
{
|
||||
clicked = value;
|
||||
}
|
||||
|
||||
const bool getClicked()
|
||||
{
|
||||
return clicked;
|
||||
}
|
||||
|
||||
void setDialog(void(*new_dialog)(void))
|
||||
{
|
||||
ui:dialog = new_dialog;
|
||||
}
|
||||
|
||||
bool hasDialog()
|
||||
{
|
||||
return dialog != nullptr;
|
||||
}
|
||||
|
||||
void callDialog()
|
||||
{
|
||||
if (dialog) dialog();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
50
ui.h
Normal file
50
ui.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#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 incoffset(uint8_t x, uint8_t y);
|
||||
uint8_t getOffsetX();
|
||||
uint8_t getOffsetY();
|
||||
bool mouseInside(int x, int y, int w, int h);
|
||||
|
||||
void panel(int x, int y, int w, int h, const char *title);
|
||||
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 placechar(int x, int y, char chr, uint8_t color=255);
|
||||
void placetxt(int x, int y, const char *text, uint8_t color);
|
||||
|
||||
void setClicked(const bool value);
|
||||
const bool getClicked();
|
||||
|
||||
void setDialog(void(*new_dialog)(void));
|
||||
bool hasDialog();
|
||||
void callDialog();
|
||||
}
|
||||
151
ui_menu.cpp
Normal file
151
ui_menu.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "ui_menu.h"
|
||||
#include "ui.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "zx_screen.h"
|
||||
|
||||
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;
|
||||
bool do_not_exit = false;
|
||||
|
||||
void init()
|
||||
{
|
||||
// No se si hi ha algo que fer ací...
|
||||
}
|
||||
|
||||
void show()
|
||||
{
|
||||
ui::setoffset(0,0);
|
||||
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_WHITE);
|
||||
text_color = COLOR_BLACK;
|
||||
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+20)
|
||||
{
|
||||
ui::printrect(menu_x+1, opt_pos, 20, 1, COLOR_WHITE);
|
||||
text_color = COLOR_BLACK;
|
||||
if (ui::getClicked())
|
||||
{
|
||||
if (option.callback) option.value = option.callback(option.value);
|
||||
if (exit_callback && !do_not_exit) exit_callback();
|
||||
do_not_exit = false;
|
||||
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;
|
||||
}
|
||||
|
||||
void exitButNotContinue()
|
||||
{
|
||||
do_not_exit = true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
ui_menu.h
Normal file
16
ui_menu.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
namespace ui
|
||||
{
|
||||
namespace menu
|
||||
{
|
||||
void init();
|
||||
void show();
|
||||
void setexitcallback(void(*callback)(void));
|
||||
void exitButNotContinue();
|
||||
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
ui_window.cpp
Normal file
33
ui_window.cpp
Normal 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
ui_window.h
Normal file
12
ui_window.h
Normal 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);
|
||||
}
|
||||
}
|
||||
12
valgrind-sup.txt
Normal file
12
valgrind-sup.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
ignore_unversioned_libs
|
||||
Memcheck:Leak
|
||||
...
|
||||
obj:*/lib*/lib*.so
|
||||
}
|
||||
{
|
||||
ignore_versioned_libs
|
||||
Memcheck:Leak
|
||||
...
|
||||
obj:*/lib*/lib*.so.*
|
||||
}
|
||||
2
valgrind.sh
Executable file
2
valgrind.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
valgrind --tool=memcheck --leak-check=full --leak-resolution=high --suppressions=valgrind-sup.txt ./z80
|
||||
42
z80.h
42
z80.h
@@ -4,14 +4,26 @@
|
||||
|
||||
namespace z80
|
||||
{
|
||||
void reset(uint8_t* mem);
|
||||
void connect_port(int num, int (*in_ptr)(int), void (*out_ptr)(int,int));
|
||||
#define Z80_OPTION_STOP_ON_INVALID 0
|
||||
#define Z80_OPTION_BREAK_ON_INTERRUPT 1
|
||||
#define Z80_OPTION_BREAK_ON_RET 2
|
||||
#define Z80_NUM_OPTIONS 3
|
||||
|
||||
void init(uint32_t freq);
|
||||
void reset();
|
||||
void clearPorts();
|
||||
void setClock(uint32_t freq);
|
||||
uint32_t getClock();
|
||||
|
||||
//void reset(uint8_t* mem);
|
||||
void connect_port(uint16_t num, uint16_t mask, int (*in_ptr)(int), void (*out_ptr)(int,int));
|
||||
void interrupt();
|
||||
|
||||
uint32_t step();
|
||||
|
||||
uint8_t *getMem();
|
||||
|
||||
//uint8_t *getMem();
|
||||
uint8_t *getRegs();
|
||||
|
||||
uint16_t getAF(const bool alt=false);
|
||||
uint16_t getBC(const bool alt=false);
|
||||
uint16_t getDE(const bool alt=false);
|
||||
@@ -21,4 +33,26 @@ namespace z80
|
||||
uint16_t getIY();
|
||||
uint16_t getSP();
|
||||
uint16_t getPC();
|
||||
|
||||
uint8_t getI();
|
||||
uint8_t getR();
|
||||
|
||||
|
||||
void setPC(const uint16_t addr);
|
||||
|
||||
/*
|
||||
uint8_t getMemTag(const uint16_t addr);
|
||||
void setMemTag(const uint16_t addr, const uint8_t value);
|
||||
void clearMemTag();
|
||||
|
||||
uint8_t getMemTouched(const uint16_t addr);
|
||||
void clearMemTouched();
|
||||
void fixMemTouched();
|
||||
*/
|
||||
|
||||
const bool getOption(const int option);
|
||||
void setOption(const int option, const bool value);
|
||||
void toggleOption(const int option);
|
||||
|
||||
void resetStackedCalls();
|
||||
}
|
||||
144
z80analyze.cpp
Normal file
144
z80analyze.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
#include "z80analyze.h"
|
||||
#include "z80.h"
|
||||
#include "z80debug.h"
|
||||
#include "zx_mem.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include "ui_window.h"
|
||||
#include "ui.h"
|
||||
|
||||
namespace z80analyze
|
||||
{
|
||||
SDL_Window *win = nullptr;
|
||||
SDL_Renderer *ren = nullptr;
|
||||
SDL_Texture *tex = nullptr;
|
||||
SDL_Texture *uitex = nullptr;
|
||||
uint16_t address = 0;
|
||||
int mx, my;
|
||||
bool needs_refresh = true;
|
||||
|
||||
void refreshTitle();
|
||||
|
||||
bool handleEvent(SDL_Event *e)
|
||||
{
|
||||
if (e->type == SDL_MOUSEBUTTONUP)
|
||||
{
|
||||
//if (z80::getMemTag(address)!=MEMTAG_INST) address = find_previous_opcode(address);
|
||||
z80debug::setcursor(address);
|
||||
z80debug::refresh();
|
||||
|
||||
/*if (e->button.button == 1)
|
||||
z80::clearMemTouched();
|
||||
//z80::clearMemTag();
|
||||
else
|
||||
z80::fixMemTouched();*/
|
||||
refresh();
|
||||
}
|
||||
if (e->type == SDL_MOUSEMOTION)
|
||||
{
|
||||
if (e->motion.windowID == SDL_GetWindowID(win)) {
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
mx = e->motion.x/2;
|
||||
my = e->motion.y/2;
|
||||
|
||||
refreshTitle();
|
||||
needs_refresh=true;
|
||||
focus();
|
||||
}
|
||||
}
|
||||
if (e->type == SDL_KEYDOWN) {
|
||||
if (e->key.keysym.scancode == SDL_SCANCODE_RETURN) {
|
||||
/*
|
||||
z80::fixMemTouched();
|
||||
refresh();
|
||||
z80debug::refresh();
|
||||
*/
|
||||
} else if (e->key.keysym.scancode == SDL_SCANCODE_BACKSPACE) {
|
||||
const uint32_t size = mem::getSize();
|
||||
//for (int i=0; i<size; ++i) mem::setTag(i, mem::getTag(i) & ~MEMTAG_TOUCHED);
|
||||
for (int i=0; i<size; ++i) mem::setTag(i, MEMTAG_NONE);
|
||||
refresh();
|
||||
z80debug::refresh();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void show()
|
||||
{
|
||||
if (!win)
|
||||
{
|
||||
win = SDL_CreateWindow("Z80 Analyzer", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 512, 512, SDL_WINDOW_SHOWN);
|
||||
ren = SDL_CreateRenderer(win, -1, 0);
|
||||
tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 256, 256);
|
||||
uitex = ui::createtexture(ren);
|
||||
|
||||
ui::window::registerWindow(SDL_GetWindowID(win), handleEvent);
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
char tmp[10];
|
||||
void refreshTitle()
|
||||
{
|
||||
if (mx>=0 && my>=0 && mx<256 && my<256) {
|
||||
address = mx+my*256;
|
||||
SDL_SetWindowTitle(win, SDL_itoa(address, tmp, 16));
|
||||
}
|
||||
}
|
||||
|
||||
void refresh(const bool conditional)
|
||||
{
|
||||
if (!win) return;
|
||||
if (conditional && !needs_refresh) return;
|
||||
needs_refresh = false;
|
||||
|
||||
ui::setrenderer(ren, uitex);
|
||||
|
||||
Uint32 *pixels;
|
||||
int pitch;
|
||||
SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch);
|
||||
for (int i=0; i<65536; ++i)
|
||||
{
|
||||
//uint8_t tag = z80::getMemTag(i);
|
||||
//pixels[i] = tag==MEMTAG_NONE ? 0x808080 : tag==MEMTAG_DATA ? 0x0000FF : tag==MEMTAG_MIXED ? 0xFF00FF : 0x00FF00;
|
||||
uint32_t none_color = i<0x4000 ? 0x101010 : i<0x5800 ? 0x202020 : i<0x5b00 ? 0x404040 : 0x808080;
|
||||
uint8_t tag = mem::getTag(i);
|
||||
pixels[i] = !(tag & MEMTAG_TOUCHED) ? none_color : (tag & MEMTAG_TINST) ? 0x00FF00 : (tag & MEMTAG_TREPEAT) ? 0xFF0000 : 0x0000FF;
|
||||
}
|
||||
pixels[z80::getPC()] = 0xFFFFFF;
|
||||
|
||||
if (mx>=0 && my>=0 && mx<256 && my<256) {
|
||||
if (mx>2) pixels[(mx-2)+(my)*256] = 0x000000;
|
||||
if (mx>1) pixels[(mx-1)+(my)*256] = 0x000000;
|
||||
if (mx<255) pixels[(mx+1)+(my)*256] = 0x000000;
|
||||
if (mx<254) pixels[(mx+2)+(my)*256] = 0x000000;
|
||||
if (my>1) pixels[(mx)+(my-1)*256] = 0x000000;
|
||||
if (my>2) pixels[(mx)+(my-2)*256] = 0x000000;
|
||||
if (my<255) pixels[(mx)+(my+1)*256] = 0x000000;
|
||||
if (my<254) pixels[(mx)+(my+2)*256] = 0x000000;
|
||||
}
|
||||
|
||||
SDL_UnlockTexture(tex);
|
||||
SDL_RenderCopy(ren, tex, NULL, NULL);
|
||||
SDL_RenderPresent(ren);
|
||||
refreshTitle();
|
||||
}
|
||||
|
||||
void hide()
|
||||
{
|
||||
ui::window::unregisterWindow(SDL_GetWindowID(win));
|
||||
SDL_DestroyTexture(uitex); uitex = nullptr;
|
||||
SDL_DestroyTexture(tex); tex = nullptr;
|
||||
SDL_DestroyRenderer(ren); ren = nullptr;
|
||||
SDL_DestroyWindow(win); win = nullptr;
|
||||
}
|
||||
|
||||
void focus()
|
||||
{
|
||||
if (win) {
|
||||
SDL_RaiseWindow(win);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
9
z80analyze.h
Normal file
9
z80analyze.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
namespace z80analyze
|
||||
{
|
||||
void show();
|
||||
void refresh(const bool conditional=false);
|
||||
void hide();
|
||||
void focus();
|
||||
}
|
||||
1218
z80debug.cpp
1218
z80debug.cpp
File diff suppressed because it is too large
Load Diff
33
z80debug.h
33
z80debug.h
@@ -3,18 +3,51 @@
|
||||
|
||||
namespace z80debug
|
||||
{
|
||||
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 reset();
|
||||
void store();
|
||||
void gototop();
|
||||
void goforward();
|
||||
void goback();
|
||||
}
|
||||
}
|
||||
139
z80dis.cpp
139
z80dis.cpp
File diff suppressed because one or more lines are too long
7
z80dis.h
7
z80dis.h
@@ -4,7 +4,14 @@
|
||||
|
||||
namespace z80dis
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
33
z80viewer.cpp
Normal file
33
z80viewer.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "z80viewer.h"
|
||||
#include "ui.h"
|
||||
#include "ui_window.h"
|
||||
|
||||
std::vector<viewer_t> z80viewer::viewers;
|
||||
|
||||
void z80viewer::registerViewer(const char *name, z80viewer *viewer)
|
||||
{
|
||||
viewer_t v;
|
||||
strcpy(v.name, name);
|
||||
v.viewer = viewer;
|
||||
viewers.push_back( v );
|
||||
}
|
||||
|
||||
z80viewer *z80viewer::getViewer(const char *name)
|
||||
{
|
||||
for (auto v : viewers)
|
||||
{
|
||||
if (strcmp(name, v.name)==0) return v.viewer;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void z80viewer::refreshAll()
|
||||
{
|
||||
for (auto v : viewers) v.viewer->refresh();
|
||||
}
|
||||
|
||||
bool z80viewer::handleEvents(SDL_Event *e)
|
||||
{
|
||||
for (auto v : viewers) v.viewer->handleEvent(e);
|
||||
return true;
|
||||
}
|
||||
34
z80viewer.h
Normal file
34
z80viewer.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include <SDL2/SDL.h>
|
||||
#include <vector>
|
||||
|
||||
class z80viewer;
|
||||
|
||||
struct viewer_t
|
||||
{
|
||||
char name[12];
|
||||
z80viewer *viewer;
|
||||
};
|
||||
|
||||
class z80viewer
|
||||
{
|
||||
public:
|
||||
virtual void show() = 0;
|
||||
virtual void refresh() = 0;
|
||||
virtual void hide() = 0;
|
||||
virtual void focus() = 0;
|
||||
virtual bool handleEvent(SDL_Event *e) = 0;
|
||||
|
||||
static void registerViewer(const char *name, z80viewer *viewer);
|
||||
static z80viewer *getViewer(const char *name);
|
||||
static void refreshAll();
|
||||
static bool handleEvents(SDL_Event *e);
|
||||
|
||||
protected:
|
||||
SDL_Window *win = nullptr;
|
||||
SDL_Renderer *ren = nullptr;
|
||||
SDL_Texture *tex = nullptr;
|
||||
SDL_Texture *uitex = nullptr;
|
||||
|
||||
static std::vector<viewer_t> viewers;
|
||||
};
|
||||
80
zx_128bankviewer.cpp
Normal file
80
zx_128bankviewer.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "zx_128bankviewer.h"
|
||||
#include "z80.h"
|
||||
#include "zx_mem.h"
|
||||
//#include "zx_128mem.h"
|
||||
#include "ui.h"
|
||||
//#include "ui_window.h"
|
||||
|
||||
void zx_128bankviewer::show()
|
||||
{
|
||||
if (!win)
|
||||
{
|
||||
win = SDL_CreateWindow("Z80 Bank Viewer", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 128, 512, SDL_WINDOW_SHOWN);
|
||||
ren = SDL_CreateRenderer(win, -1, 0);
|
||||
tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 128, 512);
|
||||
uitex = ui::createtexture(ren);
|
||||
|
||||
//ui::window::registerWindow(SDL_GetWindowID(win), handleEvent);
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
||||
void zx_128bankviewer::refresh()
|
||||
{
|
||||
if (!win) return;
|
||||
|
||||
ui::setrenderer(ren, uitex);
|
||||
|
||||
Uint32 *pixels;
|
||||
int pitch;
|
||||
SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch);
|
||||
for (int i=0; i<65536; ++i)
|
||||
{
|
||||
//uint8_t tag = z80::getMemTag(i);
|
||||
//pixels[i] = tag==MEMTAG_NONE ? 0x808080 : tag==MEMTAG_DATA ? 0x0000FF : tag==MEMTAG_MIXED ? 0xFF00FF : 0x00FF00;
|
||||
uint32_t none_color = i<0x4000 ? 0x101010 : i<0x5800 ? 0x202020 : i<0x5b00 ? 0x404040 : 0x808080;
|
||||
uint8_t tag = mem::getTag(i);
|
||||
pixels[i] = !(tag & MEMTAG_TOUCHED) ? none_color : (tag & MEMTAG_TINST) ? 0x00FF00 : (tag & MEMTAG_TREPEAT) ? 0xFF0000 : 0x0000FF;
|
||||
}
|
||||
pixels[z80::getPC()] = 0xFFFFFF;
|
||||
|
||||
SDL_UnlockTexture(tex);
|
||||
SDL_RenderCopy(ren, tex, NULL, NULL);
|
||||
|
||||
/*
|
||||
char temp[256];
|
||||
zx_128mem* mem = ((zx_128mem*)z80mem::get());
|
||||
sprintf(temp, "%u", mem->getPage(0));
|
||||
ui::placetxt(1,1,temp, COLOR_WHITE);
|
||||
sprintf(temp, "%u", mem->getPage(1));
|
||||
ui::placetxt(1,129,temp, COLOR_WHITE);
|
||||
sprintf(temp, "%u", mem->getPage(2));
|
||||
ui::placetxt(1,257,temp, COLOR_WHITE);
|
||||
sprintf(temp, "%u", mem->getPage(3));
|
||||
ui::placetxt(1,385,temp, COLOR_WHITE);
|
||||
*/
|
||||
|
||||
SDL_RenderPresent(ren);
|
||||
}
|
||||
|
||||
void zx_128bankviewer::hide()
|
||||
{
|
||||
//ui::window::unregisterWindow(SDL_GetWindowID(win));
|
||||
SDL_DestroyTexture(tex); tex = nullptr;
|
||||
SDL_DestroyRenderer(ren); ren = nullptr;
|
||||
SDL_DestroyWindow(win); win = nullptr;
|
||||
}
|
||||
|
||||
void zx_128bankviewer::focus()
|
||||
{
|
||||
if (win) {
|
||||
SDL_RaiseWindow(win);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
bool zx_128bankviewer::handleEvent(SDL_Event *e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
12
zx_128bankviewer.h
Normal file
12
zx_128bankviewer.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "z80viewer.h"
|
||||
|
||||
class zx_128bankviewer : public z80viewer
|
||||
{
|
||||
public:
|
||||
void show();
|
||||
void refresh();
|
||||
void hide();
|
||||
void focus();
|
||||
bool handleEvent(SDL_Event *e);
|
||||
};
|
||||
61
zx_128pageviewer.cpp
Normal file
61
zx_128pageviewer.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "zx_128pageviewer.h"
|
||||
#include "z80.h"
|
||||
#include "zx_mem.h"
|
||||
|
||||
void zx_128pageviewer::show()
|
||||
{
|
||||
if (!win)
|
||||
{
|
||||
win = SDL_CreateWindow("ZX128 Page Viewer", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 128, SDL_WINDOW_SHOWN);
|
||||
ren = SDL_CreateRenderer(win, -1, 0);
|
||||
tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 1024, 128);
|
||||
|
||||
//ui::window::registerWindow(SDL_GetWindowID(win), handleEvent);
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
||||
void zx_128pageviewer::refresh()
|
||||
{
|
||||
if (!win) return;
|
||||
|
||||
Uint32 *pixels;
|
||||
int pitch;
|
||||
const uint8_t *tags = mem::rawTagPtr(0);
|
||||
SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch);
|
||||
for (int i=0; i<131072; ++i)
|
||||
{
|
||||
const int x = (i&0x7f) + (((i>>14)&0x07)*128);
|
||||
const int y = (i>>7)&0x7f;
|
||||
uint32_t none_color = 0x808080; //i<0x4000 ? 0x101010 : i<0x5800 ? 0x202020 : i<0x5b00 ? 0x404040 : 0x808080;
|
||||
uint8_t tag = tags[i];
|
||||
pixels[x+y*1024] = !(tag & MEMTAG_TOUCHED) ? none_color : (tag & MEMTAG_TINST) ? 0x00FF00 : (tag & MEMTAG_TREPEAT) ? 0xFF0000 : 0x0000FF;
|
||||
}
|
||||
//pixels[z80::getPC()] = 0xFFFFFF;
|
||||
|
||||
SDL_UnlockTexture(tex);
|
||||
SDL_RenderCopy(ren, tex, NULL, NULL);
|
||||
SDL_RenderPresent(ren);
|
||||
}
|
||||
|
||||
void zx_128pageviewer::hide()
|
||||
{
|
||||
//ui::window::unregisterWindow(SDL_GetWindowID(win));
|
||||
SDL_DestroyTexture(tex); tex = nullptr;
|
||||
SDL_DestroyRenderer(ren); ren = nullptr;
|
||||
SDL_DestroyWindow(win); win = nullptr;
|
||||
}
|
||||
|
||||
void zx_128pageviewer::focus()
|
||||
{
|
||||
if (win) {
|
||||
SDL_RaiseWindow(win);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
bool zx_128pageviewer::handleEvent(SDL_Event *e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
13
zx_128pageviewer.h
Normal file
13
zx_128pageviewer.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "z80viewer.h"
|
||||
|
||||
class zx_128pageviewer : public z80viewer
|
||||
{
|
||||
public:
|
||||
void show();
|
||||
void refresh();
|
||||
void hide();
|
||||
void focus();
|
||||
|
||||
bool handleEvent(SDL_Event *e);
|
||||
};
|
||||
81
zx_dialog_joystick.cpp
Normal file
81
zx_dialog_joystick.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "zx_dialog_joystick.h"
|
||||
#include "ui.h"
|
||||
#include "z80debug.h"
|
||||
#include "gamepad.h"
|
||||
|
||||
namespace dialogs
|
||||
{
|
||||
namespace joysticks
|
||||
{
|
||||
int32_t selected = 0;
|
||||
|
||||
void init(int32_t index)
|
||||
{
|
||||
if (index != -1) selected = index;
|
||||
ui::setDialog(dialogs::joysticks::show);
|
||||
z80debug::pause();
|
||||
}
|
||||
|
||||
void show()
|
||||
{
|
||||
uint8_t back_color = COLOR_DARK;
|
||||
uint8_t front_color = COLOR_WHITE;
|
||||
|
||||
ui::setoffset(0,0);
|
||||
ui::printrect(36, 15, 46, 12, COLOR_DARK);
|
||||
ui::panel(36,15,46,12,"CONFIGURE JOYSTICKS:");
|
||||
|
||||
if (ui::mouseInside(34,9,7,1)) {
|
||||
back_color = COLOR_WHITE;
|
||||
front_color = COLOR_BLACK;
|
||||
if (ui::getClicked()) { ui::setDialog(nullptr); z80debug::cont(); }
|
||||
}
|
||||
ui::printrect(34, 9, 7, 1, back_color);
|
||||
ui::printtxt(35,9,"CLOSE", front_color);
|
||||
|
||||
ui::panel(38,17,20,8,"JOYSTICKS:");
|
||||
const int num_gamepads = gamepad::getNumGamepads();
|
||||
if (selected>=num_gamepads) selected = 0;
|
||||
for (int i=0; i<num_gamepads; ++i)
|
||||
{
|
||||
char name[] = "JOYSTICK 0";
|
||||
name[9] = 48+i;
|
||||
back_color = COLOR_DARK;
|
||||
front_color = COLOR_WHITE;
|
||||
if (ui::mouseInside(0,i,18,1)) {
|
||||
back_color = COLOR_BLACK;
|
||||
if (ui::getClicked()) {
|
||||
selected = i;
|
||||
}
|
||||
}
|
||||
if (selected==i) {
|
||||
back_color = COLOR_WHITE;
|
||||
front_color = COLOR_BLACK;
|
||||
}
|
||||
ui::printrect(0,i,18,1, back_color);
|
||||
ui::printtxt(0,i,name, front_color);
|
||||
}
|
||||
ui::panel(60,17,20,8,"TYPE:");
|
||||
const int selected_type = gamepad::getGamepadType(selected);
|
||||
const char* types[5] = {"SINCLAIR 1", "SINCLAIR 2", "KEMPSTON", "FULLER", "CUSTOM"};
|
||||
for (int i=0;i<5;++i)
|
||||
{
|
||||
back_color = COLOR_DARK;
|
||||
front_color = COLOR_WHITE;
|
||||
if (ui::mouseInside(0,i,18,1)) {
|
||||
back_color = COLOR_BLACK;
|
||||
if (ui::getClicked()) {
|
||||
gamepad::setGamepadType(selected, i+1);
|
||||
}
|
||||
}
|
||||
if (i==selected_type-1) {
|
||||
back_color = COLOR_WHITE;
|
||||
front_color = COLOR_BLACK;
|
||||
}
|
||||
ui::printrect(0,i,18,1, back_color);
|
||||
ui::printtxt(0,i,types[i],front_color);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
zx_dialog_joystick.h
Normal file
11
zx_dialog_joystick.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
namespace dialogs
|
||||
{
|
||||
namespace joysticks
|
||||
{
|
||||
void init(int32_t index);
|
||||
void show();
|
||||
}
|
||||
}
|
||||
694
zx_disk.cpp
Normal file
694
zx_disk.cpp
Normal file
@@ -0,0 +1,694 @@
|
||||
#include "zx_disk.h"
|
||||
#include "z80.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ZX_FDC_MODE_IDLE 0
|
||||
#define ZX_FDC_MODE_COMMAND 1
|
||||
#define ZX_FDC_MODE_EXECUTION 2
|
||||
#define ZX_FDC_MODE_RESULT 3
|
||||
|
||||
#define ZX_FDC_DATA_DIRECTION_INPUT 0x00
|
||||
#define ZX_FDC_DATA_DIRECTION_OUTPUT 0x40
|
||||
|
||||
#define ZX_FDC_COMMAND_MASK 0x1F
|
||||
#define ZX_FDC_COMMAND_SPECIFY 0x03
|
||||
#define ZX_FDC_COMMAND_SENSE_DRIVE_STATUS 0x04
|
||||
#define ZX_FDC_COMMAND_RECALIBRATE 0x07
|
||||
#define ZX_FDC_COMMAND_SENSE_INTERRUPT_STATUS 0x08
|
||||
#define ZX_FDC_COMMAND_SEEK 0x0F
|
||||
#define ZX_FDC_COMMAND_READ_ID 0x0A
|
||||
#define ZX_FDC_COMMAND_READ_DATA 0x06
|
||||
#define ZX_FDC_COMMAND_READ_DELETED_DATA 0x0C
|
||||
|
||||
#define ZX_FDC_MAIN_DRIVE0_BUSY 1
|
||||
#define ZX_FDC_MAIN_DRIVE1_BUSY 2
|
||||
#define ZX_FDC_MAIN_DRIVE2_BUSY 4
|
||||
#define ZX_FDC_MAIN_DRIVE3_BUSY 8
|
||||
#define ZX_FDC_MAIN_BUSY 16
|
||||
#define ZX_FDC_MAIN_EXEC_MODE 32
|
||||
#define ZX_FDC_MAIN_DATA_DIR 64 // 0: CPU->FDC 1: FDC->CPU
|
||||
#define ZX_FDC_MAIN_DATA_READY 128
|
||||
|
||||
|
||||
namespace zx_disk
|
||||
{
|
||||
struct sector_t
|
||||
{
|
||||
uint8_t id { 0 };
|
||||
uint8_t size { 0 };
|
||||
uint8_t st1 { 0 };
|
||||
uint8_t st2 { 0 };
|
||||
uint16_t data_length{ 0 };
|
||||
uint8_t *data { nullptr };
|
||||
};
|
||||
|
||||
struct track_t
|
||||
{
|
||||
uint8_t size { 0 };
|
||||
uint8_t sector_size { 0 };
|
||||
uint8_t num_sectors { 0 };
|
||||
uint8_t filler_byte { 0x35 };
|
||||
sector_t *sectors { nullptr };
|
||||
};
|
||||
|
||||
struct disk_t
|
||||
{
|
||||
uint8_t num_tracks { 0 };
|
||||
uint8_t num_sides { 0 };
|
||||
track_t *tracks { nullptr };
|
||||
};
|
||||
|
||||
disk_t disk;
|
||||
|
||||
uint8_t mode = ZX_FDC_MODE_IDLE;
|
||||
uint8_t call_count = 0;
|
||||
//uint8_t data_direction = 0x00; // 0x40
|
||||
uint8_t fdd0busy = 0;
|
||||
bool command_success = false;
|
||||
uint8_t srt, hlt, hut, nd;
|
||||
bool seeking = false;
|
||||
|
||||
uint8_t current_head = 0;
|
||||
uint8_t current_drive = 0;
|
||||
uint8_t current_track = 0;
|
||||
uint8_t current_sector = 0;
|
||||
uint16_t current_byte = 0;
|
||||
|
||||
uint16_t bytes_to_read = 0;
|
||||
uint8_t eot = 0;
|
||||
uint8_t st1 = 0;
|
||||
uint8_t st2 = 0;
|
||||
|
||||
int zx_fdc_main_status_port_in(int port);
|
||||
int zx_fdc_data_port_in(int port);
|
||||
void zx_fdc_data_port_out(int port, int val);
|
||||
|
||||
void init()
|
||||
{
|
||||
z80::connect_port(0x2ffd, 0xf002, zx_fdc_main_status_port_in, nullptr);
|
||||
z80::connect_port(0x3ffd, 0xf002, zx_fdc_data_port_in, zx_fdc_data_port_out);
|
||||
load("goldenaxe1.dsk");
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void load(const char *filename)
|
||||
{
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if (!f) return;
|
||||
fseek(f, 0, SEEK_END);
|
||||
const int file_size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
uint8_t *buffer = (uint8_t*)malloc(file_size);
|
||||
fread(buffer, file_size, 1, f);
|
||||
fclose(f);
|
||||
|
||||
if (disk.tracks) {
|
||||
for (int i=0; i<disk.num_tracks; ++i) {
|
||||
for (int j=0; j<disk.tracks[i].num_sectors; ++j) {
|
||||
free(disk.tracks[i].sectors[j].data);
|
||||
}
|
||||
free(disk.tracks[i].sectors);
|
||||
}
|
||||
free(disk.tracks);
|
||||
}
|
||||
|
||||
// Hardcoded to read extended disks. Also, no security checks at all. Fuck the police.
|
||||
disk.num_tracks = buffer[0x30];
|
||||
disk.num_sides = buffer[0x31];
|
||||
disk.tracks = (track_t*)malloc(disk.num_tracks*sizeof(track_t));
|
||||
for (int i=0; i<disk.num_tracks; ++i) disk.tracks[i].size = buffer[0x34+i];
|
||||
uint32_t pos = 0x100;
|
||||
for (int i=0; i<disk.num_tracks; ++i)
|
||||
{
|
||||
track_t &track = disk.tracks[i];
|
||||
track.sector_size = buffer[pos+0x14];
|
||||
track.num_sectors = buffer[pos+0x15];
|
||||
track.filler_byte = buffer[pos+0x17];
|
||||
track.sectors = (sector_t*)malloc(track.num_sectors*sizeof(sector_t));
|
||||
pos += 0x18;
|
||||
for (int j=0; j<track.num_sectors; ++j)
|
||||
{
|
||||
sector_t §or = track.sectors[j];
|
||||
sector.id = buffer[pos+0x02];
|
||||
sector.size = buffer[pos+0x03];
|
||||
sector.st1 = buffer[pos+0x04];
|
||||
sector.st2 = buffer[pos+0x05];
|
||||
sector.data_length = buffer[pos+0x06] + uint16_t((buffer[pos+0x07])<<8);
|
||||
if (sector.data_length==0) sector.data_length = uint16_t(sector.size)<<8;
|
||||
pos += 8;
|
||||
}
|
||||
if (pos&0xff) pos = (pos & 0xffffff00) + 0x100;
|
||||
for (int j=0; j<track.num_sectors; ++j)
|
||||
{
|
||||
sector_t §or = track.sectors[j];
|
||||
const uint16_t size = uint16_t(sector.size)<<8;
|
||||
sector.data = (uint8_t*)malloc(size);
|
||||
for (int k=0; k<size; ++k) sector.data[k] = buffer[pos++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t find_sector_id(uint8_t sector_id)
|
||||
{
|
||||
track_t &track = disk.tracks[current_track];
|
||||
for (int j=0; j<track.num_sectors; ++j)
|
||||
{
|
||||
if (track.sectors[j].id == sector_id) return j;
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
uint8_t main_status_register()
|
||||
{
|
||||
const uint8_t val = (fdd0busy) |
|
||||
((mode==ZX_FDC_MODE_IDLE) ? 0x00 : 0x10) |
|
||||
((mode==ZX_FDC_MODE_EXECUTION) ? 0x20 : 0x00) |
|
||||
((mode==ZX_FDC_MODE_RESULT) ? 0x40 : 0x00) |
|
||||
(0x80);
|
||||
return val;
|
||||
|
||||
}
|
||||
|
||||
int zx_fdc_main_status_port_in(int port)
|
||||
{
|
||||
//if (mode != ZX_FDC_MODE_EXECUTION) printf("FDC port 0x2ffd IN\n");
|
||||
return main_status_register();
|
||||
}
|
||||
|
||||
uint8_t ST0()
|
||||
{
|
||||
return current_drive | (current_head<<2) | (seeking ? 0x20 : 0x00) | (fdd0busy ? 0x00 : 0x80);
|
||||
}
|
||||
|
||||
uint8_t ST3()
|
||||
{
|
||||
const bool ready = (current_head==0) && (current_drive==0);
|
||||
return current_drive | (current_head<<2) | ((current_track==0)?0x10:0) |
|
||||
(ready?0x20:0) | 0x00; // TS
|
||||
}
|
||||
|
||||
void start_command(uint8_t command);
|
||||
|
||||
uint8_t (*process_current_command)(uint8_t) = nullptr;
|
||||
uint8_t process_command_unknown(uint8_t command);
|
||||
uint8_t process_command_specify(uint8_t command);
|
||||
uint8_t process_command_sense_drive_status(uint8_t command);
|
||||
uint8_t process_command_recalibrate(uint8_t command);
|
||||
uint8_t process_command_sense_interrupt_status(uint8_t command);
|
||||
uint8_t process_command_seek(uint8_t command);
|
||||
uint8_t process_command_read_id(uint8_t command);
|
||||
uint8_t process_command_read_data(uint8_t command);
|
||||
|
||||
int zx_fdc_data_port_in(int port)
|
||||
{
|
||||
//if (mode != ZX_FDC_MODE_EXECUTION) printf("FDC port 0x3ffd IN\n");
|
||||
if (mode == ZX_FDC_MODE_COMMAND) {
|
||||
printf("IGNORED!\n");
|
||||
return 0;
|
||||
}
|
||||
if (process_current_command) return process_current_command(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void zx_fdc_data_port_out(int port, int val)
|
||||
{
|
||||
//printf("FDC port 0x3ffd OUT: 0x%02x\n", val);
|
||||
if (mode == ZX_FDC_MODE_RESULT) {
|
||||
printf("IGNORED!\n");
|
||||
return;
|
||||
}
|
||||
if (process_current_command)
|
||||
process_current_command(val);
|
||||
else
|
||||
start_command(val);
|
||||
}
|
||||
|
||||
void start_command(uint8_t command)
|
||||
{
|
||||
if ((fdd0busy) && ((command & ZX_FDC_COMMAND_MASK) != ZX_FDC_COMMAND_SENSE_INTERRUPT_STATUS)) return;
|
||||
|
||||
mode = ZX_FDC_MODE_COMMAND;
|
||||
call_count = 1;
|
||||
debug::composeCommand(command);
|
||||
switch(command & ZX_FDC_COMMAND_MASK)
|
||||
{
|
||||
case ZX_FDC_COMMAND_SPECIFY:
|
||||
process_current_command = process_command_specify;
|
||||
break;
|
||||
case ZX_FDC_COMMAND_SENSE_DRIVE_STATUS:
|
||||
process_current_command = process_command_sense_drive_status;
|
||||
break;
|
||||
case ZX_FDC_COMMAND_RECALIBRATE:
|
||||
process_current_command = process_command_recalibrate;
|
||||
fdd0busy = 1;
|
||||
break;
|
||||
case ZX_FDC_COMMAND_SENSE_INTERRUPT_STATUS:
|
||||
process_current_command = process_command_sense_interrupt_status;
|
||||
mode = ZX_FDC_MODE_RESULT;
|
||||
call_count = 0;
|
||||
break;
|
||||
case ZX_FDC_COMMAND_SEEK:
|
||||
process_current_command = process_command_seek;
|
||||
fdd0busy = 1;
|
||||
seeking = true;
|
||||
break;
|
||||
case ZX_FDC_COMMAND_READ_ID:
|
||||
process_current_command = process_command_read_id;
|
||||
break;
|
||||
case ZX_FDC_COMMAND_READ_DATA:
|
||||
process_current_command = process_command_read_data;
|
||||
break;
|
||||
case ZX_FDC_COMMAND_READ_DELETED_DATA:
|
||||
process_current_command = process_command_read_data;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
debug::printCommand();
|
||||
process_current_command = process_command_unknown;
|
||||
mode = ZX_FDC_MODE_RESULT;
|
||||
call_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===================================================================
|
||||
// FDC COMMAND: UNKNOWN
|
||||
// ===================================================================
|
||||
uint8_t process_command_unknown(uint8_t command)
|
||||
{
|
||||
process_current_command = nullptr;
|
||||
mode = ZX_FDC_MODE_IDLE;
|
||||
const uint8_t val = ST0();
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
debug::printResult();
|
||||
return val;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// FDC COMMAND: SPECIFY (0x03)
|
||||
// ===================================================================
|
||||
uint8_t process_command_specify(uint8_t command)
|
||||
{
|
||||
debug::composeCommand(command);
|
||||
switch (call_count) {
|
||||
case 1:
|
||||
srt = (command & 0xf0) >> 4;
|
||||
hut = command & 0x0f;
|
||||
call_count++;
|
||||
break;
|
||||
case 2:
|
||||
hlt = (command & 0xfe) >> 1;
|
||||
nd = command & 0x01;
|
||||
call_count=0;
|
||||
process_current_command = nullptr;
|
||||
mode = ZX_FDC_MODE_IDLE;
|
||||
debug::printCommand();
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// FDC COMMAND: SENSE DRIVE STATUS (0x04)
|
||||
// ===================================================================
|
||||
uint8_t process_command_sense_drive_status(uint8_t command)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ZX_FDC_MODE_COMMAND:
|
||||
current_head = (command & 0x4)>>2;
|
||||
current_drive = command & 0x3;
|
||||
call_count = 0;
|
||||
mode = ZX_FDC_MODE_RESULT;
|
||||
debug::composeCommand(command);
|
||||
debug::printCommand();
|
||||
break;
|
||||
case ZX_FDC_MODE_RESULT:
|
||||
{
|
||||
process_current_command = nullptr;
|
||||
mode = ZX_FDC_MODE_IDLE;
|
||||
const uint8_t val = ST3();
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
debug::printResult();
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// FDC COMMAND: RECALIBRATE (0x07)
|
||||
// ===================================================================
|
||||
uint8_t process_command_recalibrate(uint8_t command)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ZX_FDC_MODE_COMMAND:
|
||||
current_drive = command & 0x3;
|
||||
call_count = 0;
|
||||
current_track = 0;
|
||||
seeking = true;
|
||||
process_current_command = nullptr;
|
||||
mode = ZX_FDC_MODE_IDLE;
|
||||
debug::composeCommand(command);
|
||||
debug::printCommand();
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// FDC COMMAND: SENSE INTERRUPT STATUS (0x08)
|
||||
// ===================================================================
|
||||
uint8_t process_command_sense_interrupt_status(uint8_t command)
|
||||
{
|
||||
switch (call_count)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
debug::printCommand();
|
||||
call_count++;
|
||||
const uint8_t val = ST0();
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 1:
|
||||
process_current_command = nullptr;
|
||||
fdd0busy = 0;
|
||||
seeking = false;
|
||||
call_count = 0;
|
||||
mode = ZX_FDC_MODE_IDLE;
|
||||
//printf("--> (returning 0x%02x)\n", current_track);
|
||||
debug::composeResult(current_track);
|
||||
debug::printResult();
|
||||
return current_track;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// FDC COMMAND: SEEK (0x0F)
|
||||
// ===================================================================
|
||||
uint8_t process_command_seek(uint8_t command)
|
||||
{
|
||||
debug::composeCommand(command);
|
||||
switch (call_count)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
current_head = (command & 0x4)>>2;
|
||||
current_drive = command & 0x3;
|
||||
call_count++;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
current_track = command;
|
||||
process_current_command = nullptr;
|
||||
call_count = 0;
|
||||
mode = ZX_FDC_MODE_IDLE;
|
||||
debug::printCommand();
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// FDC COMMAND: READ ID (0x0A)
|
||||
// ===================================================================
|
||||
uint8_t process_command_read_id(uint8_t command)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ZX_FDC_MODE_COMMAND:
|
||||
current_head = (command & 0x4)>>2;
|
||||
current_drive = command & 0x3;
|
||||
call_count = 0;
|
||||
current_sector = 0;
|
||||
mode = ZX_FDC_MODE_RESULT;
|
||||
debug::composeCommand(command);
|
||||
debug::printCommand();
|
||||
break;
|
||||
case ZX_FDC_MODE_RESULT:
|
||||
{
|
||||
switch (call_count)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
call_count++;
|
||||
fdd0busy = 1;
|
||||
const uint8_t val = ST0();
|
||||
fdd0busy = 0;
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
call_count++;
|
||||
const uint8_t val = 0x00; // ST1
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
call_count++;
|
||||
const uint8_t val = 0x00; // ST2
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
call_count++;
|
||||
const uint8_t val = current_track;
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
call_count++;
|
||||
const uint8_t val = current_head;
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
call_count++;
|
||||
const uint8_t val = disk.tracks[current_track].sectors[current_sector].id;
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
call_count = 0;
|
||||
process_current_command = nullptr;
|
||||
mode = ZX_FDC_MODE_IDLE;
|
||||
const uint8_t val = disk.tracks[current_track].sectors[current_sector].size;
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
debug::printResult();
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// FDC COMMAND: READ DATA (0x06)
|
||||
// ===================================================================
|
||||
uint8_t process_command_read_data(uint8_t command)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ZX_FDC_MODE_COMMAND:
|
||||
debug::composeCommand(command);
|
||||
switch (call_count)
|
||||
{
|
||||
case 1:
|
||||
call_count++;
|
||||
current_head = (command & 0x4)>>2;
|
||||
current_drive = command & 0x3;
|
||||
break;
|
||||
case 2:
|
||||
call_count++;
|
||||
current_track = command;
|
||||
break;
|
||||
case 3:
|
||||
call_count++;
|
||||
current_head = command;
|
||||
break;
|
||||
case 4:
|
||||
call_count++;
|
||||
current_sector = find_sector_id(command);
|
||||
break;
|
||||
case 5:
|
||||
call_count++;
|
||||
bytes_to_read = command;
|
||||
break;
|
||||
case 6:
|
||||
call_count++;
|
||||
eot = command;
|
||||
break;
|
||||
case 7:
|
||||
call_count++;
|
||||
break;
|
||||
case 8:
|
||||
if ( (bytes_to_read==0) && (command != 0xff) ) bytes_to_read = command;
|
||||
bytes_to_read = bytes_to_read << 8;
|
||||
current_byte = 0;
|
||||
call_count = 0;
|
||||
mode = ZX_FDC_MODE_EXECUTION;
|
||||
debug::printCommand();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZX_FDC_MODE_EXECUTION:
|
||||
{
|
||||
const uint8_t val = disk.tracks[current_track].sectors[current_sector].data[current_byte];
|
||||
|
||||
current_byte++;
|
||||
if (current_byte == disk.tracks[current_track].sectors[current_sector].data_length) {
|
||||
current_byte = 0;
|
||||
current_sector = disk.tracks[current_track].sectors[current_sector].id;
|
||||
current_sector++;
|
||||
current_sector = find_sector_id(current_sector);
|
||||
if (current_sector == 0xff) {
|
||||
current_track++;
|
||||
current_sector = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bytes_to_read--;
|
||||
if (bytes_to_read==0) {
|
||||
st1 = disk.tracks[current_track].sectors[current_sector].st1;
|
||||
st2 = disk.tracks[current_track].sectors[current_sector].st2;
|
||||
mode = ZX_FDC_MODE_RESULT;
|
||||
}
|
||||
|
||||
return val;
|
||||
break;
|
||||
}
|
||||
|
||||
case ZX_FDC_MODE_RESULT:
|
||||
{
|
||||
switch (call_count)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
call_count++;
|
||||
fdd0busy = 1;
|
||||
const uint8_t val = ST0();
|
||||
fdd0busy = 0;
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
call_count++;
|
||||
const uint8_t val = st1;
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
call_count++;
|
||||
const uint8_t val = st2;
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
call_count++;
|
||||
const uint8_t val = current_track;
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
call_count++;
|
||||
const uint8_t val = current_head;
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
call_count++;
|
||||
const uint8_t val = disk.tracks[current_track].sectors[current_sector].id;
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
return val;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
call_count = 0;
|
||||
process_current_command = nullptr;
|
||||
mode = ZX_FDC_MODE_IDLE;
|
||||
const uint8_t val = disk.tracks[current_track].sectors[current_sector].size;
|
||||
//printf("--> (returning 0x%02x)\n", val);
|
||||
debug::composeResult(val);
|
||||
debug::printResult();
|
||||
return val;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
namespace debug
|
||||
{
|
||||
uint8_t values[9];
|
||||
uint8_t num_values = 0;
|
||||
|
||||
void composeCommand(const uint8_t value)
|
||||
{
|
||||
values[num_values++] = value;
|
||||
}
|
||||
|
||||
void composeResult(const uint8_t value)
|
||||
{
|
||||
values[num_values++] = value;
|
||||
}
|
||||
|
||||
void printCommand()
|
||||
{
|
||||
printf("FDC COMMAND %02x: ( ", values[0] & 0x1f);
|
||||
for (int i=0; i<num_values; ++i) printf("%02x ", values[i]);
|
||||
printf(")\n");
|
||||
num_values = 0;
|
||||
}
|
||||
|
||||
void printResult()
|
||||
{
|
||||
printf("FDC RESULT: ( ");
|
||||
for (int i=0; i<num_values; ++i) printf("%02x ", values[i]);
|
||||
printf(")\n");
|
||||
num_values = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
18
zx_disk.h
Normal file
18
zx_disk.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
namespace zx_disk
|
||||
{
|
||||
void init();
|
||||
void reset();
|
||||
void load(const char *filename);
|
||||
|
||||
namespace debug
|
||||
{
|
||||
void composeCommand(const uint8_t value);
|
||||
void composeResult(const uint8_t value);
|
||||
|
||||
void printCommand();
|
||||
void printResult();
|
||||
}
|
||||
}
|
||||
240
zx_mem.cpp
Normal file
240
zx_mem.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
#include "zx_mem.h"
|
||||
#include <stdlib.h>
|
||||
#include "z80.h"
|
||||
#include "zx_screen.h"
|
||||
|
||||
#define ZX_128MEM_PAGE 0x07
|
||||
#define ZX_128MEM_SCREEN 0x08
|
||||
#define ZX_128MEM_ROM 0x10
|
||||
#define ZX_128MEM_DISPAG 0x20
|
||||
|
||||
#define ZX_2A_3_PAGING_MODE 0x01
|
||||
#define ZX_2A_3_HROM 0x04
|
||||
#define ZX_2A_3_SPECIAL 0x06
|
||||
|
||||
namespace mem
|
||||
{
|
||||
uint8_t mode = ZX_48K;
|
||||
uint32_t ram_size = 48*1024;
|
||||
uint32_t rom_size = 16*1024;
|
||||
uint8_t *ram = nullptr;
|
||||
uint8_t *rom = nullptr;
|
||||
uint8_t *slot[8];
|
||||
bool writable[8];
|
||||
|
||||
uint8_t config_128K = 0;
|
||||
uint8_t config_2A_3 = 0;
|
||||
|
||||
void zx_128_port_out(int port, int val);
|
||||
void zx_2A_3_port_out(int port, int val);
|
||||
|
||||
void init(uint8_t mode)
|
||||
{
|
||||
mem::mode = mode;
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
if (ram) free(ram);
|
||||
if (rom) free(rom);
|
||||
|
||||
FILE* f;
|
||||
switch(mode)
|
||||
{
|
||||
case ZX_48K:
|
||||
ram_size = 48*1024;
|
||||
ram = (uint8_t*)malloc(ram_size);
|
||||
for (int i=0; i<ram_size; ++i) ram[i] = 0;
|
||||
//for (int i=0; i<65536; ++i) tags[i] = MEMTAG_NONE;
|
||||
|
||||
rom_size = 16*1024;
|
||||
rom = (uint8_t*)malloc(rom_size);
|
||||
f = fopen("48.rom", "rb");
|
||||
fread(rom, rom_size, 1, f);
|
||||
fclose(f);
|
||||
|
||||
slot[0] = &rom[0*8192]; slot[1] = &rom[1*8192];
|
||||
slot[2] = &ram[0*8192]; slot[3] = &ram[1*8192];
|
||||
slot[4] = &ram[2*8192]; slot[5] = &ram[3*8192];
|
||||
slot[6] = &ram[4*8192]; slot[7] = &ram[5*8192];
|
||||
|
||||
writable[0] = writable[1] = false;
|
||||
for (int i=2;i<8;++i) writable[i] = true;
|
||||
break;
|
||||
case ZX_128K:
|
||||
ram_size = 128*1024;
|
||||
ram = (uint8_t*)malloc(ram_size);
|
||||
for (int i=0x0000; i<ram_size; ++i) ram[i] = 0;
|
||||
|
||||
rom_size = 32*1024;
|
||||
rom = (uint8_t*)malloc(rom_size);
|
||||
f = fopen("128k.rom", "rb");
|
||||
fread(rom, rom_size, 1, f);
|
||||
fclose(f);
|
||||
|
||||
slot[0] = &rom[0*8192]; slot[1] = &rom[1*8192];
|
||||
slot[2] = &ram[10*8192]; slot[3] = &ram[11*8192];
|
||||
slot[4] = &ram[4*8192]; slot[5] = &ram[5*8192];
|
||||
slot[6] = &ram[0*8192]; slot[7] = &ram[1*8192];
|
||||
|
||||
writable[0] = writable[1] = false;
|
||||
for (int i=2;i<8;++i) writable[i] = true;
|
||||
|
||||
z80::connect_port(0x7ffd, 0x8002, nullptr, mem::zx_128_port_out);
|
||||
break;
|
||||
case ZX_PLUS3:
|
||||
ram_size = 128*1024;
|
||||
ram = (uint8_t*)malloc(ram_size);
|
||||
for (int i=0x0000; i<ram_size; ++i) ram[i] = 0;
|
||||
|
||||
rom_size = 64*1024;
|
||||
rom = (uint8_t*)malloc(rom_size);
|
||||
f = fopen("plus3.rom", "rb");
|
||||
fread(rom, rom_size, 1, f);
|
||||
fclose(f);
|
||||
|
||||
slot[0] = &rom[0*8192]; slot[1] = &rom[1*8192];
|
||||
slot[2] = &ram[10*8192]; slot[3] = &ram[11*8192];
|
||||
slot[4] = &ram[4*8192]; slot[5] = &ram[5*8192];
|
||||
slot[6] = &ram[0*8192]; slot[7] = &ram[1*8192];
|
||||
|
||||
writable[0] = writable[1] = false;
|
||||
for (int i=2;i<8;++i) writable[i] = true;
|
||||
|
||||
z80::connect_port(0x7ffd, 0xc002, nullptr, mem::zx_128_port_out);
|
||||
z80::connect_port(0x1ffd, 0xf002, nullptr, mem::zx_2A_3_port_out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t readMem(uint16_t address)
|
||||
{
|
||||
const uint8_t slot_num = address >> 13;
|
||||
const uint16_t displacement = address & 0x1fff;
|
||||
return slot[slot_num][displacement];
|
||||
}
|
||||
|
||||
void writeMem(uint16_t address, uint8_t value)
|
||||
{
|
||||
const uint8_t slot_num = address >> 13;
|
||||
if (!writable[slot_num]) return;
|
||||
|
||||
const uint16_t displacement = address & 0x1fff;
|
||||
slot[slot_num][displacement] = value;
|
||||
}
|
||||
|
||||
void loadMem(uint16_t address, uint16_t len, uint8_t *buffer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint8_t getTag(uint16_t address)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setTag(uint16_t address, uint8_t value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void saveState(FILE* f)
|
||||
{
|
||||
fwrite(ram, 0xc000, 1, f);
|
||||
}
|
||||
|
||||
void loadState(FILE* f)
|
||||
{
|
||||
fread(ram, 0xc000, 1, f);
|
||||
}
|
||||
|
||||
uint32_t getSize()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t *rawPtr(uint32_t address)
|
||||
{
|
||||
return &ram[address];
|
||||
}
|
||||
|
||||
uint8_t *rawTagPtr(uint32_t address)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void zx_128_port_out(int port, int val)
|
||||
{
|
||||
if (port != 0x7ffd) return;
|
||||
if (config_128K & ZX_128MEM_DISPAG) return;
|
||||
|
||||
const bool shadow = config_128K & ZX_128MEM_SCREEN;
|
||||
config_128K = val;
|
||||
|
||||
if (config_2A_3 & ZX_2A_3_PAGING_MODE) return;
|
||||
|
||||
if (config_128K & ZX_128MEM_SCREEN) {
|
||||
if (!shadow) zxscreen::setBaseAddresses(0x4000*7, 0x1800+0x4000*7);
|
||||
} else {
|
||||
if (shadow) zxscreen::setBaseAddresses(0x4000*5, 0x1800+0x4000*5);
|
||||
}
|
||||
|
||||
uint8_t hrom = (config_2A_3 & ZX_2A_3_HROM) ? 4 : 0;
|
||||
if (config_128K & ZX_128MEM_ROM) {
|
||||
slot[0] = &rom[(hrom+2)*8192]; slot[1] = &rom[(hrom+3)*8192];
|
||||
} else {
|
||||
slot[0] = &rom[(hrom+0)*8192]; slot[1] = &rom[(hrom+1)*8192];
|
||||
}
|
||||
|
||||
const uint8_t slot3 = (config_128K&ZX_128MEM_PAGE)*2;
|
||||
slot[6] = &ram[slot3*8192]; slot[7] = &ram[(slot3+1)*8192];
|
||||
}
|
||||
|
||||
void zx_2A_3_port_out(int port, int val)
|
||||
{
|
||||
if (port != 0x1ffd) return;
|
||||
if (config_128K & ZX_128MEM_DISPAG) return;
|
||||
|
||||
config_2A_3 = val;
|
||||
if (config_2A_3 & ZX_2A_3_PAGING_MODE) {
|
||||
for (int i=0;i<8;++i) writable[i] = true;
|
||||
switch ((config_2A_3 & ZX_2A_3_SPECIAL)>>1)
|
||||
{
|
||||
case 0:
|
||||
slot[0] = &ram[0*8192]; slot[1] = &ram[1*8192];
|
||||
slot[2] = &ram[2*8192]; slot[3] = &ram[3*8192];
|
||||
slot[4] = &ram[4*8192]; slot[5] = &ram[5*8192];
|
||||
slot[6] = &ram[6*8192]; slot[7] = &ram[7*8192];
|
||||
break;
|
||||
case 1:
|
||||
slot[0] = &ram[8*8192]; slot[1] = &ram[9*8192];
|
||||
slot[2] = &ram[10*8192]; slot[3] = &ram[11*8192];
|
||||
slot[4] = &ram[12*8192]; slot[5] = &ram[13*8192];
|
||||
slot[6] = &ram[14*8192]; slot[7] = &ram[15*8192];
|
||||
break;
|
||||
case 2:
|
||||
slot[0] = &ram[8*8192]; slot[1] = &ram[9*8192];
|
||||
slot[2] = &ram[10*8192]; slot[3] = &ram[11*8192];
|
||||
slot[4] = &ram[12*8192]; slot[5] = &ram[13*8192];
|
||||
slot[6] = &ram[6*8192]; slot[7] = &ram[7*8192];
|
||||
break;
|
||||
case 3:
|
||||
slot[0] = &ram[8*8192]; slot[1] = &ram[9*8192];
|
||||
slot[2] = &ram[14*8192]; slot[3] = &ram[15*8192];
|
||||
slot[4] = &ram[12*8192]; slot[5] = &ram[13*8192];
|
||||
slot[6] = &ram[6*8192]; slot[7] = &ram[7*8192];
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
writable[0] = writable[1] = false;
|
||||
for (int i=2;i<8;++i) writable[i] = true;
|
||||
uint8_t hrom = (config_2A_3 & ZX_2A_3_HROM) ? 4 : 0;
|
||||
if (config_128K & ZX_128MEM_ROM) {
|
||||
slot[0] = &rom[(hrom+2)*8192]; slot[1] = &rom[(hrom+3)*8192];
|
||||
} else {
|
||||
slot[0] = &rom[(hrom+0)*8192]; slot[1] = &rom[(hrom+1)*8192];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
zx_mem.h
Normal file
40
zx_mem.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "zx_system.h"
|
||||
|
||||
#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
|
||||
|
||||
namespace mem
|
||||
{
|
||||
void init(uint8_t mode);
|
||||
void reset();
|
||||
|
||||
uint8_t readMem(uint16_t address);
|
||||
void writeMem(uint16_t address, uint8_t value);
|
||||
|
||||
void loadMem(uint16_t address, uint16_t len, uint8_t *buffer);
|
||||
|
||||
uint8_t getTag(uint16_t address);
|
||||
void setTag(uint16_t address, uint8_t value);
|
||||
|
||||
void saveState(FILE* f);
|
||||
void loadState(FILE* f);
|
||||
|
||||
uint32_t getSize();
|
||||
|
||||
uint8_t *rawPtr(uint32_t address);
|
||||
uint8_t *rawTagPtr(uint32_t address);
|
||||
}
|
||||
350
zx_screen.cpp
350
zx_screen.cpp
@@ -1,7 +1,14 @@
|
||||
#include "zx_screen.h"
|
||||
#include "z80.h"
|
||||
#include "zx_mem.h"
|
||||
#include "zx_ula.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include "zx_tape.h"
|
||||
#include "ui_window.h"
|
||||
#include "z80debug.h"
|
||||
#include "ui.h"
|
||||
#include "file.h"
|
||||
//#include "zx_128mem.h"
|
||||
|
||||
namespace zxscreen
|
||||
{
|
||||
@@ -12,6 +19,20 @@ namespace zxscreen
|
||||
SDL_Window *win = nullptr;
|
||||
SDL_Renderer *ren = nullptr;
|
||||
SDL_Texture *tex = nullptr;
|
||||
SDL_Texture *uitex = nullptr;
|
||||
|
||||
int mode = SCREEN_MODE_48K;
|
||||
|
||||
uint32_t t_states_total = 69888;
|
||||
uint32_t t_states_per_scanline = 224;
|
||||
uint32_t vsync_lines = 16;
|
||||
bool interrupt_enabled = true;
|
||||
|
||||
uint8_t zoom = 2;
|
||||
bool fullscreen = false;
|
||||
bool full_refresh = true;
|
||||
int fullscreen_scale = 1;
|
||||
SDL_Rect dest_rect;
|
||||
|
||||
uint32_t time=0;
|
||||
|
||||
@@ -19,45 +40,63 @@ namespace zxscreen
|
||||
uint8_t t_flash = 0;
|
||||
bool flash = false;
|
||||
|
||||
int pixels_draw = 0;
|
||||
uint32_t pixel_base_addr = 0x4000;
|
||||
uint32_t color_base_addr = 0x5800;
|
||||
|
||||
uint16_t pixel_addr[69888];
|
||||
uint16_t color_addr[69888];
|
||||
uint32_t *pixel_addr = nullptr; //[69888];
|
||||
uint32_t *color_addr = nullptr; //[69888];
|
||||
uint8_t zx_pixels[352*296];
|
||||
uint8_t *ptr_pixel = zx_pixels;
|
||||
|
||||
#define SCREEN_SYNC 0xFFFF
|
||||
#define SCREEN_BORDER 0xFFFE
|
||||
|
||||
void saveWindowConfiguration()
|
||||
{
|
||||
file::setConfigValueInteger("screen_zoom", zoom);
|
||||
int x, y;
|
||||
SDL_GetWindowPosition(win, &x, &y);
|
||||
file::setConfigValueInteger("screen_x", x);
|
||||
file::setConfigValueInteger("screen_y", y);
|
||||
}
|
||||
|
||||
void create_tables()
|
||||
{
|
||||
uint32_t count = 0;
|
||||
|
||||
uint16_t *ptr_pixel = pixel_addr;
|
||||
uint16_t *ptr_color = color_addr;
|
||||
if (pixel_addr) free(pixel_addr);
|
||||
if (color_addr) free(color_addr);
|
||||
pixel_addr = (uint32_t*)malloc(t_states_total*sizeof(uint32_t));
|
||||
color_addr = (uint32_t*)malloc(t_states_total*sizeof(uint32_t));
|
||||
|
||||
uint32_t *ptr_pixel = pixel_addr;
|
||||
uint32_t *ptr_color = color_addr;
|
||||
|
||||
// vsync
|
||||
for (int i=0; i<224*16;++i) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; }
|
||||
for (int i=0; i<t_states_per_scanline*vsync_lines;++i) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 16 passa a ser 15 i 224 passa a ser 228
|
||||
|
||||
// Upper border
|
||||
for (int i=0; i<48;++i) {
|
||||
// hsync
|
||||
for (int j=0;j<48;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; }
|
||||
for (int j=0;j<t_states_per_scanline-176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 48 passsa a ser 52
|
||||
//border
|
||||
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; }
|
||||
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
|
||||
}
|
||||
|
||||
// scanlines
|
||||
for (uint8_t y=0; y<192; ++y)
|
||||
{
|
||||
// hsync
|
||||
for (int j=0;j<48;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; }
|
||||
for (int j=0;j<t_states_per_scanline-176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 48 passsa a ser 52
|
||||
|
||||
// Left border
|
||||
for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; }
|
||||
for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
|
||||
|
||||
// Actual screen
|
||||
for (uint8_t x=0;x<32;++x)
|
||||
{
|
||||
uint16_t color = 0x5800 + x + (y>>3)*32;
|
||||
uint16_t address = 0x4000 | (x&0x1f) | ((y&0x7)<<8) | ((y&0x38)<<2) | ((y&0xc0)<<5);
|
||||
uint16_t color = /*0x5800 +*/ x + (y>>3)*32;
|
||||
uint16_t address = /*0x4000 |*/ (x&0x1f) | ((y&0x7)<<8) | ((y&0x38)<<2) | ((y&0xc0)<<5);
|
||||
for (int i=7;i>0;i-=2)
|
||||
{
|
||||
*(ptr_pixel++) = (address & 0x1FFF) | (i << 13);
|
||||
@@ -67,88 +106,303 @@ namespace zxscreen
|
||||
}
|
||||
|
||||
// Right border
|
||||
for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; }
|
||||
for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
|
||||
}
|
||||
|
||||
// Lower border
|
||||
for (int i=0; i<56;++i) {
|
||||
// hsync
|
||||
for (int j=0;j<48;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; }
|
||||
for (int j=0;j<t_states_per_scanline-176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 48 passsa a ser 52
|
||||
//border
|
||||
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; }
|
||||
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
|
||||
}
|
||||
printf("COUNT: %i\n", count);
|
||||
//printf("COUNT: %i\n", count);
|
||||
}
|
||||
|
||||
void show()
|
||||
bool eventHandler(SDL_Event *e)
|
||||
{
|
||||
if (win) return;
|
||||
win = SDL_CreateWindow("ZX Spectrum Screen", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 352, 296, SDL_WINDOW_SHOWN);
|
||||
ren = SDL_CreateRenderer(win, -1, 0);
|
||||
if (e->type==SDL_WINDOWEVENT) {
|
||||
//int x, y;
|
||||
//SDL_GetWindowPosition(win, &x, &y);
|
||||
//char tmp[256];
|
||||
//sprintf(tmp, " %ix%i", x, y);
|
||||
//setTitle(tmp);
|
||||
if (e->window.event==SDL_WINDOWEVENT_CLOSE) {
|
||||
saveWindowConfiguration();
|
||||
return false;
|
||||
} else if (e->window.event==SDL_WINDOWEVENT_FOCUS_LOST || e->window.event==SDL_WINDOWEVENT_MOVED) {
|
||||
saveWindowConfiguration();
|
||||
} else if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) {
|
||||
redraw();
|
||||
}
|
||||
}
|
||||
if (!z80debug::debugging()) {
|
||||
if (z80debug::paused()) {
|
||||
if (e->type == SDL_KEYDOWN) {
|
||||
if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) {
|
||||
ui::setDialog(nullptr);
|
||||
const uint8_t dt = z80::step();
|
||||
z80debug::cont();
|
||||
zxscreen::refresh(dt);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (e->type == SDL_KEYDOWN) {
|
||||
if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) {
|
||||
z80debug::pause();
|
||||
zxscreen::redraw();
|
||||
} else if (e->key.keysym.scancode==SDL_SCANCODE_F1) {
|
||||
zxscreen::decZoom();
|
||||
} else if (e->key.keysym.scancode==SDL_SCANCODE_F2) {
|
||||
zxscreen::incZoom();
|
||||
} else if (e->key.keysym.scancode==SDL_SCANCODE_F3) {
|
||||
zxscreen::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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e->type == SDL_MOUSEMOTION) {
|
||||
SDL_ShowCursor(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void reinit()
|
||||
{
|
||||
saveWindowConfiguration();
|
||||
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);
|
||||
|
||||
zoom = file::getConfigValueInteger("screen_zoom", 1);
|
||||
const int x = file::getConfigValueInteger("screen_x", SDL_WINDOWPOS_UNDEFINED);
|
||||
const int y = file::getConfigValueInteger("screen_y", SDL_WINDOWPOS_UNDEFINED);
|
||||
|
||||
const int z = fullscreen ? 1 : zoom;
|
||||
win = SDL_CreateWindow("ZX Spectrum Screen", x, y, 352*z, 296*z, fullscreen?SDL_WINDOW_FULLSCREEN_DESKTOP:SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
|
||||
tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 352, 296);
|
||||
create_tables();
|
||||
redraw();
|
||||
uitex = ui::createtexture(ren);
|
||||
|
||||
ui::window::registerWindow(SDL_GetWindowID(win), eventHandler);
|
||||
|
||||
if (fullscreen)
|
||||
{
|
||||
int w, h;
|
||||
SDL_GetWindowSize(win, &w, &h);
|
||||
fullscreen_scale = h/296;
|
||||
dest_rect.w = 352 * fullscreen_scale;
|
||||
dest_rect.h = 296 * 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 = 352 * zoom;
|
||||
dest_rect.h = 296 * zoom;
|
||||
}
|
||||
|
||||
focus();
|
||||
}
|
||||
|
||||
void refresh(const uint8_t dt)
|
||||
void setBaseAddresses(const uint32_t pixeladdr, const uint32_t coloraddr)
|
||||
{
|
||||
const uint8_t* memory = z80::getMem();
|
||||
pixel_base_addr = pixeladdr;
|
||||
color_base_addr = coloraddr;
|
||||
}
|
||||
|
||||
void init(int mode)
|
||||
{
|
||||
zxscreen::mode = mode;
|
||||
if (mode==SCREEN_MODE_48K) {
|
||||
setBaseAddresses(0x0000, 0x1800);
|
||||
t_states_total = 69888;
|
||||
t_states_per_scanline = 224;
|
||||
vsync_lines = 16;
|
||||
} else if (mode==SCREEN_MODE_128K) {
|
||||
setBaseAddresses(0x14000, 0x15800);
|
||||
t_states_total = 70908;
|
||||
t_states_per_scanline = 228;
|
||||
vsync_lines = 15;
|
||||
}
|
||||
|
||||
create_tables();
|
||||
ptr_pixel = zx_pixels;
|
||||
t_screen = t_flash = 0;
|
||||
flash = false;
|
||||
reinit();
|
||||
}
|
||||
|
||||
void focus()
|
||||
{
|
||||
if (win)
|
||||
{
|
||||
SDL_RaiseWindow(win);
|
||||
redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void refresh(const uint32_t dt)
|
||||
{
|
||||
const uint8_t* pixel_mem = mem::rawPtr(pixel_base_addr);
|
||||
const uint8_t* color_mem = mem::rawPtr(color_base_addr);
|
||||
const uint8_t border_color = zx_ula::get_border_color();
|
||||
|
||||
for (int i=0;i<dt;++i)
|
||||
{
|
||||
if (color_addr[t_screen] != 0)
|
||||
if (color_addr[t_screen] != SCREEN_SYNC)
|
||||
{
|
||||
if (color_addr[t_screen] == 1)
|
||||
{
|
||||
if (color_addr[t_screen] == SCREEN_BORDER) {
|
||||
*(ptr_pixel++) = border_color;
|
||||
*(ptr_pixel++) = border_color;
|
||||
//pixels_draw+=2;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
uint8_t color = memory[color_addr[t_screen]];
|
||||
} else {
|
||||
uint8_t color = *(color_mem + color_addr[t_screen]); // z80mem::get()->readMem(color_base_addr + color_addr[t_screen]);
|
||||
uint8_t c1 = color&0x7, c2 = (color>>3)&0x7;
|
||||
if ((color&0x80) && flash) { c1=c2; c2=color&0x7; }
|
||||
if ((color&0x40)) { c1 |= 0x8; c2 |= 0x8; }
|
||||
uint16_t address = (0x4000) | (pixel_addr[t_screen]&0x1FFF);
|
||||
uint16_t address = /*(0x4000) |*/ (pixel_addr[t_screen]&0x1FFF);
|
||||
uint8_t mask = 1 << (pixel_addr[t_screen]>>13);
|
||||
uint8_t block = memory[address];
|
||||
uint8_t block = *(pixel_mem + address); // z80mem::get()->readMem(pixel_base_addr + address);
|
||||
*(ptr_pixel++)=(block&mask) ? c1 : c2;
|
||||
mask>>=1;
|
||||
*(ptr_pixel++)=(block&mask) ? c1 : c2;
|
||||
}
|
||||
pixels_draw+=2;
|
||||
}
|
||||
t_screen++;
|
||||
/*if (pixels_draw>352*296)
|
||||
{
|
||||
printf("PIXELS OVERFLOW: %i\n", pixels_draw);
|
||||
}*/
|
||||
if (t_screen>=69888) {
|
||||
//printf("PIXELS DRAWN: %i\n", pixels_draw);
|
||||
pixels_draw=0;
|
||||
if (t_screen>=t_states_total) {
|
||||
t_flash++;
|
||||
if (t_flash==16) { t_flash=0; flash = !flash; }
|
||||
t_screen=0;
|
||||
ptr_pixel = zx_pixels;
|
||||
redraw();
|
||||
//while (SDL_GetTicks()-time < 20) {}
|
||||
//time = SDL_GetTicks();
|
||||
z80::interrupt();
|
||||
if (interrupt_enabled) z80::interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void redraw()
|
||||
void fullrefresh()
|
||||
{
|
||||
uint32_t tmp = t_screen;
|
||||
t_screen = 0;
|
||||
uint8_t * tmp_ptr = ptr_pixel;
|
||||
ptr_pixel = zx_pixels;
|
||||
interrupt_enabled = false;
|
||||
refresh(t_states_total);
|
||||
interrupt_enabled = true;
|
||||
ptr_pixel = tmp_ptr;
|
||||
t_screen = tmp;
|
||||
}
|
||||
|
||||
void debugrefresh(const uint32_t dt)
|
||||
{
|
||||
if (full_refresh) fullrefresh(); else refresh(dt);
|
||||
}
|
||||
|
||||
void redraw(const bool present)
|
||||
{
|
||||
if (zx_tape::getplaying() && zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD)) return;
|
||||
|
||||
ui::setrenderer(ren, uitex);
|
||||
|
||||
Uint32* pixels;
|
||||
int pitch;
|
||||
SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch);
|
||||
for (int i=0; i<352*296;++i) *(pixels++) = palette[zx_pixels[i]];
|
||||
SDL_UnlockTexture(tex);
|
||||
SDL_RenderCopy(ren, tex, NULL, NULL);
|
||||
|
||||
if (fullscreen)
|
||||
{
|
||||
SDL_SetRenderDrawColor(ren, 0, 0, 0, 255);
|
||||
SDL_RenderClear(ren);
|
||||
}
|
||||
|
||||
// Pintem la textura a pantalla
|
||||
SDL_RenderCopy(ren, tex, NULL, &dest_rect);
|
||||
|
||||
//zx_128mem* mem = ((zx_128mem*)z80mem::get());
|
||||
//ui::printtxt(0,0,mem->getShadowScreen()?"SHADOW":"NORMAL", COLOR_WHITE);
|
||||
|
||||
if (present)
|
||||
SDL_RenderPresent(ren);
|
||||
else
|
||||
{
|
||||
SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(ren, 0, 0, 0, 128);
|
||||
SDL_Rect rect {0,0,352*zoom,296*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, "ZX Spectrum Screen");
|
||||
strcat(tmp, title);
|
||||
SDL_SetWindowTitle(win, tmp);
|
||||
}
|
||||
|
||||
void setZoom(const int value)
|
||||
{
|
||||
if (value < 1) return;
|
||||
SDL_DisplayMode dm;
|
||||
SDL_GetCurrentDisplayMode(0, &dm);
|
||||
|
||||
if (352*value > dm.w) return;
|
||||
if (296*value > dm.h) return;
|
||||
|
||||
zoom = value;
|
||||
reinit();
|
||||
}
|
||||
|
||||
void incZoom()
|
||||
{
|
||||
setZoom(zoom+1);
|
||||
}
|
||||
|
||||
void decZoom()
|
||||
{
|
||||
setZoom(zoom-1);
|
||||
}
|
||||
|
||||
void toggleFullscreen()
|
||||
{
|
||||
fullscreen = !fullscreen;
|
||||
reinit();
|
||||
}
|
||||
|
||||
const bool getFullscreen()
|
||||
{
|
||||
return fullscreen;
|
||||
}
|
||||
|
||||
void toggleFullRefresh()
|
||||
{
|
||||
full_refresh = !full_refresh;
|
||||
}
|
||||
|
||||
const bool getFullRefresh()
|
||||
{
|
||||
return full_refresh;
|
||||
}
|
||||
|
||||
|
||||
SDL_Renderer *getrenderer()
|
||||
{
|
||||
return ren;
|
||||
}
|
||||
}
|
||||
|
||||
28
zx_screen.h
28
zx_screen.h
@@ -1,9 +1,29 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#define SCREEN_MODE_48K 0
|
||||
#define SCREEN_MODE_128K 1
|
||||
|
||||
namespace zxscreen
|
||||
{
|
||||
void show();
|
||||
void refresh(const uint8_t dt);
|
||||
void redraw();
|
||||
void init(int mode);
|
||||
void setBaseAddresses(const uint32_t pixeladdr, const uint32_t coloraddr);
|
||||
void reinit();
|
||||
void focus();
|
||||
void refresh(const uint32_t dt);
|
||||
void fullrefresh();
|
||||
void debugrefresh();
|
||||
void redraw(const bool present=true);
|
||||
void present();
|
||||
void setTitle(const char* title);
|
||||
|
||||
void incZoom();
|
||||
void decZoom();
|
||||
void toggleFullscreen();
|
||||
const bool getFullscreen();
|
||||
|
||||
void toggleFullRefresh();
|
||||
const bool getFullRefresh();
|
||||
|
||||
SDL_Renderer *getrenderer();
|
||||
}
|
||||
|
||||
82
zx_speaker.cpp
Normal file
82
zx_speaker.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "zx_speaker.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include "z80.h"
|
||||
#include <vector>
|
||||
|
||||
namespace speaker
|
||||
{
|
||||
uint16_t sampling_freq = 44100;
|
||||
uint16_t audio_buffer_size = 2048;
|
||||
SDL_AudioDeviceID sdlAudioDevice = 0;
|
||||
uint8_t *sound_buffer = nullptr;
|
||||
uint16_t sound_pos=0;
|
||||
uint16_t t_sound=0;
|
||||
std::vector<uint8_t(*)()> sources;
|
||||
|
||||
float cycles_per_sample;
|
||||
|
||||
void init(const uint16_t freq, const uint16_t buffer_size)
|
||||
{
|
||||
if (sound_buffer || sdlAudioDevice) quit();
|
||||
|
||||
sampling_freq = freq;
|
||||
audio_buffer_size = buffer_size;
|
||||
|
||||
SDL_AudioSpec audioSpec{sampling_freq, AUDIO_U8, 1, 0, (uint16_t)(audio_buffer_size>>2), 0, 0, NULL, NULL};
|
||||
sdlAudioDevice = SDL_OpenAudioDevice(NULL, 0, &audioSpec, NULL, 0);
|
||||
|
||||
cycles_per_sample = z80::getClock() / sampling_freq;
|
||||
sound_buffer = (uint8_t*)malloc(audio_buffer_size);
|
||||
|
||||
enable();
|
||||
}
|
||||
|
||||
void quit()
|
||||
{
|
||||
disable();
|
||||
sources.clear();
|
||||
if (sound_buffer) {
|
||||
free(sound_buffer);
|
||||
sound_buffer = nullptr;
|
||||
}
|
||||
if (sdlAudioDevice) {
|
||||
SDL_CloseAudioDevice(sdlAudioDevice);
|
||||
sdlAudioDevice = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void enable()
|
||||
{
|
||||
SDL_PauseAudioDevice(sdlAudioDevice, 0);
|
||||
}
|
||||
|
||||
void disable()
|
||||
{
|
||||
SDL_PauseAudioDevice(sdlAudioDevice, 1);
|
||||
}
|
||||
|
||||
void register_source(uint8_t(*callback)())
|
||||
{
|
||||
sources.push_back(callback);
|
||||
}
|
||||
|
||||
void update(const uint32_t dt)
|
||||
{
|
||||
t_sound += dt;
|
||||
if (t_sound>=cycles_per_sample) {
|
||||
t_sound-=cycles_per_sample;
|
||||
|
||||
uint32_t sample = 0;
|
||||
for (auto callback : sources) sample += callback();
|
||||
sample /= sources.size();
|
||||
|
||||
sound_buffer[(sound_pos++)&(audio_buffer_size-1)] = sample;
|
||||
}
|
||||
if (sound_pos>=1000) {
|
||||
SDL_QueueAudio(sdlAudioDevice, sound_buffer, sound_pos);
|
||||
sound_pos = 0;
|
||||
while (SDL_GetQueuedAudioSize(sdlAudioDevice) > (audio_buffer_size<<1) ) {}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
zx_speaker.h
Normal file
12
zx_speaker.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
namespace speaker
|
||||
{
|
||||
void init(const uint16_t freq = 44100, const uint16_t buffer_size = 2048);
|
||||
void quit();
|
||||
void enable();
|
||||
void disable();
|
||||
void register_source(uint8_t(*callback)());
|
||||
void update(const uint32_t dt);
|
||||
}
|
||||
123
zx_system.cpp
Normal file
123
zx_system.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "zx_system.h"
|
||||
#include "z80.h"
|
||||
#include "zx_mem.h"
|
||||
#include "zx_ula.h"
|
||||
#include "zx_screen.h"
|
||||
#include "zx_tape.h"
|
||||
#include "zx_speaker.h"
|
||||
#include "ay-3-8912.h"
|
||||
#include "zx_disk.h"
|
||||
#include <vector>
|
||||
|
||||
namespace zx_system
|
||||
{
|
||||
bool resetting = true;
|
||||
bool shutting_down = false;
|
||||
uint8_t current_mode = ZX_48K;
|
||||
uint8_t new_mode = ZX_48K;
|
||||
std::vector<void(*)(uint32_t)> updatables;
|
||||
|
||||
int init(const uint8_t mode)
|
||||
{
|
||||
updatables.clear();
|
||||
z80::clearPorts();
|
||||
resetting = false;
|
||||
switch(mode)
|
||||
{
|
||||
case ZX_NOCHANGE:
|
||||
{
|
||||
z80::reset();
|
||||
break;
|
||||
|
||||
}
|
||||
case ZX_48K:
|
||||
{
|
||||
const uint32_t clock = 3500000;
|
||||
z80::init(clock);
|
||||
z80::connect_port(0xfe, 0x0001, zx_ula::port_in, zx_ula::port_out);
|
||||
mem::init(ZX_48K);
|
||||
zxscreen::init(SCREEN_MODE_48K);
|
||||
speaker::init();
|
||||
speaker::register_source(zx_ula::get_sample);
|
||||
|
||||
registerUpdatable(zx_tape::update);
|
||||
registerUpdatable(speaker::update);
|
||||
//registerUpdatable(zxscreen::refresh);
|
||||
|
||||
return clock / 10;
|
||||
break;
|
||||
}
|
||||
case ZX_128K:
|
||||
{
|
||||
const uint32_t clock = 3546900;
|
||||
z80::init(clock);
|
||||
z80::connect_port(0xfe, 0x0001, zx_ula::port_in, zx_ula::port_out);
|
||||
mem::init(ZX_128K);
|
||||
zxscreen::init(SCREEN_MODE_128K);
|
||||
audio::init();
|
||||
speaker::init();
|
||||
speaker::register_source(zx_ula::get_sample);
|
||||
speaker::register_source(audio::get_sample);
|
||||
|
||||
registerUpdatable(zx_tape::update);
|
||||
registerUpdatable(audio::update);
|
||||
registerUpdatable(speaker::update);
|
||||
//registerUpdatable(zxscreen::refresh);
|
||||
|
||||
return clock / 10;
|
||||
break;
|
||||
}
|
||||
case ZX_PLUS3:
|
||||
{
|
||||
const uint32_t clock = 3546900;
|
||||
z80::init(clock);
|
||||
z80::connect_port(0xfe, 0x0001, zx_ula::port_in, zx_ula::port_out);
|
||||
mem::init(ZX_PLUS3);
|
||||
zxscreen::init(SCREEN_MODE_128K);
|
||||
audio::init();
|
||||
speaker::init();
|
||||
speaker::register_source(zx_ula::get_sample);
|
||||
speaker::register_source(audio::get_sample);
|
||||
zx_disk::init();
|
||||
|
||||
registerUpdatable(zx_tape::update);
|
||||
registerUpdatable(audio::update);
|
||||
registerUpdatable(speaker::update);
|
||||
//registerUpdatable(zxscreen::refresh);
|
||||
|
||||
return clock / 10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void reset(const uint8_t mode)
|
||||
{
|
||||
new_mode = mode;
|
||||
resetting = true;
|
||||
}
|
||||
|
||||
void shutdown()
|
||||
{
|
||||
shutting_down = true;
|
||||
}
|
||||
|
||||
const bool shuttingDown()
|
||||
{
|
||||
if (resetting) init(new_mode);
|
||||
return shutting_down;
|
||||
}
|
||||
|
||||
void registerUpdatable(void(*callback)(uint32_t))
|
||||
{
|
||||
updatables.push_back(callback);
|
||||
}
|
||||
|
||||
void update(uint32_t dt)
|
||||
{
|
||||
for (auto& call : updatables) call(dt);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
19
zx_system.h
Normal file
19
zx_system.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#define ZX_NOCHANGE 0x00
|
||||
#define ZX_48K 0x01
|
||||
#define ZX_128K 0x02
|
||||
#define ZX_PLUS3 0x03
|
||||
#define ZX_NEXT 0x04
|
||||
|
||||
namespace zx_system
|
||||
{
|
||||
int init(const uint8_t mode);
|
||||
void reset(const uint8_t mode);
|
||||
void shutdown();
|
||||
const bool shuttingDown();
|
||||
|
||||
void registerUpdatable(void(*callback)(uint32_t));
|
||||
void update(uint32_t dt);
|
||||
}
|
||||
75
zx_tape.cpp
75
zx_tape.cpp
@@ -1,5 +1,9 @@
|
||||
#include "zx_tape.h"
|
||||
#include "zx_ula.h"
|
||||
#include "zx_screen.h"
|
||||
#include "zx_mem.h"
|
||||
#include "z80debug.h"
|
||||
#include "z80.h"
|
||||
#include <vector>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -33,7 +37,7 @@ namespace zx_tape
|
||||
|
||||
bool playing = false;
|
||||
bool loaded = false;
|
||||
bool berserk_mode = false;
|
||||
bool options[ZXTAPE_NUM_OPTIONS] = { true, true };
|
||||
|
||||
std::vector<block_t> blocks;
|
||||
uint8_t current_block = 0;
|
||||
@@ -47,8 +51,9 @@ namespace zx_tape
|
||||
|
||||
void load(const char* filename)
|
||||
{
|
||||
//[TODO] Free memory that might be taken by previous tape
|
||||
|
||||
if (!blocks.empty()) for (auto block : blocks) free(block.data);
|
||||
blocks.clear();
|
||||
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if (!f) return;
|
||||
while (!feof(f))
|
||||
@@ -78,7 +83,7 @@ namespace zx_tape
|
||||
void stop()
|
||||
{
|
||||
playing = false;
|
||||
berserk_mode = false;
|
||||
//berserk_mode = false;
|
||||
}
|
||||
|
||||
void rewind()
|
||||
@@ -90,7 +95,7 @@ namespace zx_tape
|
||||
pulse_level = 1;
|
||||
}
|
||||
|
||||
void update(const uint8_t dt)
|
||||
void update(const uint32_t dt)
|
||||
{
|
||||
if (!playing) return;
|
||||
|
||||
@@ -113,6 +118,7 @@ namespace zx_tape
|
||||
current_pulse = 0;
|
||||
pulse_level = 1;
|
||||
current_section = PULSE_SYNC1;
|
||||
printf("going to pulse_sync1..%i.\n", current_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,6 +130,7 @@ namespace zx_tape
|
||||
pulse_pos -= PULSE_LEN_SYNC1;
|
||||
pulse_level = 0;
|
||||
current_section = PULSE_SYNC2;
|
||||
printf("going to pulse_sync2..%i.\n", current_block);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +141,7 @@ namespace zx_tape
|
||||
pulse_pos -= PULSE_LEN_SYNC2;
|
||||
pulse_level = 1;
|
||||
current_section = PULSE_DATA;
|
||||
printf("going to pulse_data..%i.\n", current_block);
|
||||
}
|
||||
}
|
||||
static int level[2] = {0, 0};
|
||||
@@ -160,6 +168,7 @@ namespace zx_tape
|
||||
{
|
||||
block_pos = 0;
|
||||
current_section = PULSE_SYNC3;
|
||||
printf("going to pulse_sync3..%i.\n", current_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,6 +203,7 @@ namespace zx_tape
|
||||
pulse_pos -= PULSE_LEN_SYNC3;
|
||||
pulse_level = 0;
|
||||
current_section = PULSE_WAIT;
|
||||
printf("going to pulse_wait..%i (%i).\n", current_block, blocks[current_block].length);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -201,16 +211,24 @@ namespace zx_tape
|
||||
if (current_section == PULSE_WAIT)
|
||||
{
|
||||
pulse_level = 0;
|
||||
if (pulse_pos >= 3500000)
|
||||
if (pulse_pos >= z80::getClock())
|
||||
{
|
||||
pulse_pos = 0;
|
||||
current_section = PULSE_PILOT;
|
||||
pulse_level = 1;
|
||||
current_block++;
|
||||
|
||||
if (current_block>=blocks.size())
|
||||
{
|
||||
printf("end\n");
|
||||
stop();
|
||||
rewind();
|
||||
if (options[ZXTAPE_OPTION_STOP_AT_END]) z80debug::stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
//zxscreen::fullrefresh();
|
||||
printf("going to pulse_pilot on block %i...\n", current_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,7 +236,48 @@ namespace zx_tape
|
||||
zx_ula::set_ear(pulse_level);
|
||||
}
|
||||
|
||||
void go_berserk() { berserk_mode = true; }
|
||||
const bool berserk() { return berserk_mode; }
|
||||
const bool getplaying() { return playing; }
|
||||
|
||||
void report()
|
||||
{
|
||||
if (current_block >= blocks.size()) return;
|
||||
const int percent = (float(block_pos)/float(blocks[current_block].length))*100;
|
||||
printf("tape loading: %i%\n", percent);
|
||||
}
|
||||
|
||||
uint16_t fastLoad(const uint8_t block_type, const uint16_t address, const uint16_t length)
|
||||
{
|
||||
block_pos=0;
|
||||
current_bit=0;
|
||||
current_section = PULSE_PILOT;
|
||||
current_pulse = 0;
|
||||
pulse_pos = 0;
|
||||
pulse_level = 0;
|
||||
|
||||
if (blocks[current_block].data[0] != block_type ||
|
||||
blocks[current_block].length != length+2) {
|
||||
printf("ERROR: Tape data not consistent with expectation\n");
|
||||
z80debug::stop();
|
||||
}
|
||||
for (int i=0;i<length;++i) {
|
||||
mem::writeMem(address+i, blocks[current_block].data[i+1]);
|
||||
}
|
||||
current_block++;
|
||||
return address + length;
|
||||
}
|
||||
|
||||
const bool getOption(const int option)
|
||||
{
|
||||
return options[option];
|
||||
}
|
||||
|
||||
void setOption(const int option, const bool value)
|
||||
{
|
||||
options[option] = value;
|
||||
}
|
||||
|
||||
void toggleOption(const int option)
|
||||
{
|
||||
options[option] = !options[option];
|
||||
}
|
||||
}
|
||||
|
||||
15
zx_tape.h
15
zx_tape.h
@@ -3,11 +3,20 @@
|
||||
|
||||
namespace zx_tape
|
||||
{
|
||||
#define ZXTAPE_OPTION_FAST_LOAD 0
|
||||
#define ZXTAPE_OPTION_STOP_AT_END 1
|
||||
#define ZXTAPE_NUM_OPTIONS 2
|
||||
|
||||
void load(const char* filename);
|
||||
void play();
|
||||
void stop();
|
||||
void rewind();
|
||||
void update(const uint8_t dt);
|
||||
void go_berserk();
|
||||
const bool berserk();
|
||||
void update(const uint32_t dt);
|
||||
const bool getplaying();
|
||||
void report();
|
||||
uint16_t fastLoad(const uint8_t block_type, const uint16_t address, const uint16_t length);
|
||||
|
||||
const bool getOption(const int option);
|
||||
void setOption(const int option, const bool value);
|
||||
void toggleOption(const int option);
|
||||
}
|
||||
|
||||
144
zx_ula.cpp
144
zx_ula.cpp
@@ -1,4 +1,5 @@
|
||||
#include "zx_ula.h"
|
||||
#include "z80.h"
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
namespace zx_ula
|
||||
@@ -52,7 +53,17 @@ namespace zx_ula
|
||||
#define KEY_B 39
|
||||
|
||||
uint8_t zx_keyboard[40];
|
||||
|
||||
uint8_t virtual_keyboard[40] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||
char key_to_keyboard[40] {
|
||||
'<','Z','X','C','V',
|
||||
'A','S','D','F','G',
|
||||
'Q','W','E','R','T',
|
||||
'1','2','3','4','5',
|
||||
'0','9','8','7','6',
|
||||
'P','O','I','U','Y',
|
||||
'-','L','K','J','H',
|
||||
'.',',','M','N','B'
|
||||
};
|
||||
static uint8_t border_color = 0;
|
||||
static uint8_t ear = 0;
|
||||
static uint8_t mic = 0;
|
||||
@@ -62,63 +73,69 @@ namespace zx_ula
|
||||
const uint8_t *keys = SDL_GetKeyboardState(NULL);
|
||||
|
||||
// Normal keypresses
|
||||
zx_keyboard[KEY_SHIFT] = keys[SDL_SCANCODE_LSHIFT] | keys[SDL_SCANCODE_RSHIFT];
|
||||
zx_keyboard[KEY_Z] = keys[SDL_SCANCODE_Z];
|
||||
zx_keyboard[KEY_X] = keys[SDL_SCANCODE_X];
|
||||
zx_keyboard[KEY_C] = keys[SDL_SCANCODE_C];
|
||||
zx_keyboard[KEY_V] = keys[SDL_SCANCODE_V];
|
||||
zx_keyboard[KEY_SHIFT] = virtual_keyboard[KEY_SHIFT] | keys[SDL_SCANCODE_LSHIFT] | keys[SDL_SCANCODE_RSHIFT];
|
||||
zx_keyboard[KEY_Z] = virtual_keyboard[KEY_Z] | keys[SDL_SCANCODE_Z];
|
||||
zx_keyboard[KEY_X] = virtual_keyboard[KEY_X] | keys[SDL_SCANCODE_X];
|
||||
zx_keyboard[KEY_C] = virtual_keyboard[KEY_C] | keys[SDL_SCANCODE_C];
|
||||
zx_keyboard[KEY_V] = virtual_keyboard[KEY_V] | keys[SDL_SCANCODE_V];
|
||||
|
||||
zx_keyboard[KEY_A] = keys[SDL_SCANCODE_A];
|
||||
zx_keyboard[KEY_S] = keys[SDL_SCANCODE_S];
|
||||
zx_keyboard[KEY_D] = keys[SDL_SCANCODE_D];
|
||||
zx_keyboard[KEY_F] = keys[SDL_SCANCODE_F];
|
||||
zx_keyboard[KEY_G] = keys[SDL_SCANCODE_G];
|
||||
zx_keyboard[KEY_A] = virtual_keyboard[KEY_A] | keys[SDL_SCANCODE_A];
|
||||
zx_keyboard[KEY_S] = virtual_keyboard[KEY_S] | keys[SDL_SCANCODE_S];
|
||||
zx_keyboard[KEY_D] = virtual_keyboard[KEY_D] | keys[SDL_SCANCODE_D];
|
||||
zx_keyboard[KEY_F] = virtual_keyboard[KEY_F] | keys[SDL_SCANCODE_F];
|
||||
zx_keyboard[KEY_G] = virtual_keyboard[KEY_G] | keys[SDL_SCANCODE_G];
|
||||
|
||||
zx_keyboard[KEY_Q] = keys[SDL_SCANCODE_Q];
|
||||
zx_keyboard[KEY_W] = keys[SDL_SCANCODE_W];
|
||||
zx_keyboard[KEY_E] = keys[SDL_SCANCODE_E];
|
||||
zx_keyboard[KEY_R] = keys[SDL_SCANCODE_R];
|
||||
zx_keyboard[KEY_T] = keys[SDL_SCANCODE_T];
|
||||
zx_keyboard[KEY_Q] = virtual_keyboard[KEY_Q] | keys[SDL_SCANCODE_Q];
|
||||
zx_keyboard[KEY_W] = virtual_keyboard[KEY_W] | keys[SDL_SCANCODE_W];
|
||||
zx_keyboard[KEY_E] = virtual_keyboard[KEY_E] | keys[SDL_SCANCODE_E];
|
||||
zx_keyboard[KEY_R] = virtual_keyboard[KEY_R] | keys[SDL_SCANCODE_R];
|
||||
zx_keyboard[KEY_T] = virtual_keyboard[KEY_T] | keys[SDL_SCANCODE_T];
|
||||
|
||||
zx_keyboard[KEY_1] = keys[SDL_SCANCODE_1] | keys[SDL_SCANCODE_KP_1];
|
||||
zx_keyboard[KEY_2] = keys[SDL_SCANCODE_2] | keys[SDL_SCANCODE_KP_2];
|
||||
zx_keyboard[KEY_3] = keys[SDL_SCANCODE_3] | keys[SDL_SCANCODE_KP_3];
|
||||
zx_keyboard[KEY_4] = keys[SDL_SCANCODE_4] | keys[SDL_SCANCODE_KP_4];
|
||||
zx_keyboard[KEY_5] = keys[SDL_SCANCODE_5] | keys[SDL_SCANCODE_KP_5];
|
||||
zx_keyboard[KEY_1] = virtual_keyboard[KEY_1] | keys[SDL_SCANCODE_1] | keys[SDL_SCANCODE_KP_1];
|
||||
zx_keyboard[KEY_2] = virtual_keyboard[KEY_2] | keys[SDL_SCANCODE_2] | keys[SDL_SCANCODE_KP_2];
|
||||
zx_keyboard[KEY_3] = virtual_keyboard[KEY_3] | keys[SDL_SCANCODE_3] | keys[SDL_SCANCODE_KP_3];
|
||||
zx_keyboard[KEY_4] = virtual_keyboard[KEY_4] | keys[SDL_SCANCODE_4] | keys[SDL_SCANCODE_KP_4];
|
||||
zx_keyboard[KEY_5] = virtual_keyboard[KEY_5] | keys[SDL_SCANCODE_5] | keys[SDL_SCANCODE_KP_5];
|
||||
|
||||
zx_keyboard[KEY_0] = keys[SDL_SCANCODE_0] | keys[SDL_SCANCODE_KP_0];
|
||||
zx_keyboard[KEY_9] = keys[SDL_SCANCODE_9] | keys[SDL_SCANCODE_KP_9];
|
||||
zx_keyboard[KEY_8] = keys[SDL_SCANCODE_8] | keys[SDL_SCANCODE_KP_8];
|
||||
zx_keyboard[KEY_7] = keys[SDL_SCANCODE_7] | keys[SDL_SCANCODE_KP_7];
|
||||
zx_keyboard[KEY_6] = keys[SDL_SCANCODE_6] | keys[SDL_SCANCODE_KP_6];
|
||||
zx_keyboard[KEY_0] = virtual_keyboard[KEY_0] | keys[SDL_SCANCODE_0] | keys[SDL_SCANCODE_KP_0];
|
||||
zx_keyboard[KEY_9] = virtual_keyboard[KEY_9] | keys[SDL_SCANCODE_9] | keys[SDL_SCANCODE_KP_9];
|
||||
zx_keyboard[KEY_8] = virtual_keyboard[KEY_8] | keys[SDL_SCANCODE_8] | keys[SDL_SCANCODE_KP_8];
|
||||
zx_keyboard[KEY_7] = virtual_keyboard[KEY_7] | keys[SDL_SCANCODE_7] | keys[SDL_SCANCODE_KP_7];
|
||||
zx_keyboard[KEY_6] = virtual_keyboard[KEY_6] | keys[SDL_SCANCODE_6] | keys[SDL_SCANCODE_KP_6];
|
||||
|
||||
zx_keyboard[KEY_P] = keys[SDL_SCANCODE_P];
|
||||
zx_keyboard[KEY_O] = keys[SDL_SCANCODE_O];
|
||||
zx_keyboard[KEY_I] = keys[SDL_SCANCODE_I];
|
||||
zx_keyboard[KEY_U] = keys[SDL_SCANCODE_U];
|
||||
zx_keyboard[KEY_Y] = keys[SDL_SCANCODE_Y];
|
||||
zx_keyboard[KEY_P] = virtual_keyboard[KEY_P] | keys[SDL_SCANCODE_P];
|
||||
zx_keyboard[KEY_O] = virtual_keyboard[KEY_O] | keys[SDL_SCANCODE_O];
|
||||
zx_keyboard[KEY_I] = virtual_keyboard[KEY_I] | keys[SDL_SCANCODE_I];
|
||||
zx_keyboard[KEY_U] = virtual_keyboard[KEY_U] | keys[SDL_SCANCODE_U];
|
||||
zx_keyboard[KEY_Y] = virtual_keyboard[KEY_Y] | keys[SDL_SCANCODE_Y];
|
||||
|
||||
zx_keyboard[KEY_RETURN] = keys[SDL_SCANCODE_KP_ENTER] | keys[SDL_SCANCODE_RETURN];
|
||||
zx_keyboard[KEY_L] = keys[SDL_SCANCODE_L];
|
||||
zx_keyboard[KEY_K] = keys[SDL_SCANCODE_K];
|
||||
zx_keyboard[KEY_J] = keys[SDL_SCANCODE_J];
|
||||
zx_keyboard[KEY_H] = keys[SDL_SCANCODE_H];
|
||||
zx_keyboard[KEY_RETURN] = virtual_keyboard[KEY_RETURN] | keys[SDL_SCANCODE_KP_ENTER] | keys[SDL_SCANCODE_RETURN];
|
||||
zx_keyboard[KEY_L] = virtual_keyboard[KEY_L] | keys[SDL_SCANCODE_L];
|
||||
zx_keyboard[KEY_K] = virtual_keyboard[KEY_K] | keys[SDL_SCANCODE_K];
|
||||
zx_keyboard[KEY_J] = virtual_keyboard[KEY_J] | keys[SDL_SCANCODE_J];
|
||||
zx_keyboard[KEY_H] = virtual_keyboard[KEY_H] | keys[SDL_SCANCODE_H];
|
||||
|
||||
zx_keyboard[KEY_SPACE] = keys[SDL_SCANCODE_SPACE];
|
||||
zx_keyboard[KEY_SYMBOL] = keys[SDL_SCANCODE_LCTRL] | keys[SDL_SCANCODE_RCTRL];
|
||||
zx_keyboard[KEY_M] = keys[SDL_SCANCODE_M];
|
||||
zx_keyboard[KEY_N] = keys[SDL_SCANCODE_N];
|
||||
zx_keyboard[KEY_B] = keys[SDL_SCANCODE_B];
|
||||
zx_keyboard[KEY_SPACE] = virtual_keyboard[KEY_SPACE] | keys[SDL_SCANCODE_SPACE];
|
||||
zx_keyboard[KEY_SYMBOL] = virtual_keyboard[KEY_SYMBOL] | keys[SDL_SCANCODE_LCTRL] | keys[SDL_SCANCODE_RCTRL];
|
||||
zx_keyboard[KEY_M] = virtual_keyboard[KEY_M] | keys[SDL_SCANCODE_M];
|
||||
zx_keyboard[KEY_N] = virtual_keyboard[KEY_N] | keys[SDL_SCANCODE_N];
|
||||
zx_keyboard[KEY_B] = virtual_keyboard[KEY_B] | keys[SDL_SCANCODE_B];
|
||||
|
||||
// Keys in a normal keyboard that ara combinations in the zx one
|
||||
if (keys[SDL_SCANCODE_BACKSPACE]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_0] = 1;
|
||||
if (keys[SDL_SCANCODE_PERIOD]) zx_keyboard[KEY_SYMBOL] = zx_keyboard[KEY_M] = 1;
|
||||
|
||||
if (keys[SDL_SCANCODE_UP]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_7] = 1;
|
||||
if (keys[SDL_SCANCODE_DOWN]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_6] = 1;
|
||||
if (keys[SDL_SCANCODE_LEFT]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_5] = 1;
|
||||
if (keys[SDL_SCANCODE_RIGHT]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_8] = 1;
|
||||
|
||||
}
|
||||
|
||||
int port_in(int port)
|
||||
{
|
||||
const uint8_t h_addr = (port>>8);
|
||||
uint8_t result = ear ? 0xbf : 0xff;
|
||||
uint8_t result = ear ? 0xff : 0xbf;
|
||||
|
||||
update_zx_keyboard();
|
||||
|
||||
@@ -200,53 +217,36 @@ namespace zx_ula
|
||||
void port_out(int port, int val)
|
||||
{
|
||||
border_color = val & 0x7;
|
||||
mic = (val&0x08)==0;
|
||||
ear = val&0x10;
|
||||
mic = (val&0x08)?0:1;
|
||||
ear = val&0x10?1:0;
|
||||
//printf("EAR:%i MIC:%i\n", ear, mic);
|
||||
}
|
||||
|
||||
uint8_t get_border_color() { return border_color; }
|
||||
void set_border_color(uint8_t col) { border_color = col; }
|
||||
uint8_t get_ear() { return ear; }
|
||||
void set_ear(const uint8_t val) { ear = val; }
|
||||
|
||||
|
||||
|
||||
|
||||
SDL_AudioDeviceID sdlAudioDevice;
|
||||
uint8_t sound_buffer[1024];
|
||||
uint16_t sound_pos;
|
||||
uint16_t t_sound=0;
|
||||
|
||||
void audioCallback(void * userdata, uint8_t * stream, int len)
|
||||
uint8_t get_sample()
|
||||
{
|
||||
const uint16_t top = sound_pos < len ? sound_pos : len;
|
||||
memcpy(stream, sound_buffer, top);
|
||||
if (top<len) memchr(&stream[top], sound_buffer[top-1], len-top);
|
||||
sound_pos=0;
|
||||
return ear*128;
|
||||
}
|
||||
|
||||
void sound_init()
|
||||
const int getKey(const char key)
|
||||
{
|
||||
SDL_AudioSpec audioSpec{11025, AUDIO_U8, 1, 0, 220, 0, 0, &audioCallback, NULL};
|
||||
sdlAudioDevice = SDL_OpenAudioDevice(NULL, 0, &audioSpec, NULL, 0);
|
||||
for (int i=0; i<40; ++i) if (key_to_keyboard[i] == key) return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sound_enable()
|
||||
void keydown(const char key)
|
||||
{
|
||||
SDL_PauseAudioDevice(sdlAudioDevice, 0);
|
||||
virtual_keyboard[getKey(key)] = 1;
|
||||
}
|
||||
|
||||
void sound_disable()
|
||||
void keyup(const char key)
|
||||
{
|
||||
SDL_PauseAudioDevice(sdlAudioDevice, 1);
|
||||
virtual_keyboard[getKey(key)] = 0;
|
||||
}
|
||||
|
||||
void sound_update(const uint8_t dt)
|
||||
{
|
||||
t_sound += dt;
|
||||
if (t_sound>=317) {
|
||||
t_sound-=317;
|
||||
sound_buffer[sound_pos++] = ear*128;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
9
zx_ula.h
9
zx_ula.h
@@ -8,11 +8,12 @@ namespace zx_ula
|
||||
void port_out(int port, int val);
|
||||
|
||||
uint8_t get_border_color();
|
||||
void set_border_color(uint8_t col);
|
||||
uint8_t get_ear();
|
||||
void set_ear(const uint8_t val);
|
||||
|
||||
void sound_init();
|
||||
void sound_enable();
|
||||
void sound_disable();
|
||||
void sound_update(const uint8_t dt);
|
||||
uint8_t get_sample();
|
||||
|
||||
void keydown(const char key);
|
||||
void keyup(const char key);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user