Compare commits
52 Commits
c70c3652bf
...
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 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
z80
|
z80
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
build/*
|
||||||
BIN
ROBOCOP1.TAP
Normal file
BIN
ROBOCOP1.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;
|
|
||||||
}
|
|
||||||
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
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
|
||||||
195
main.cpp
195
main.cpp
@@ -1,9 +1,11 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "zx_system.h"
|
||||||
#include "z80.h"
|
#include "z80.h"
|
||||||
#include "z80dis.h"
|
#include "z80dis.h"
|
||||||
#include "z80debug.h"
|
#include "z80debug.h"
|
||||||
#include "zx_ula.h"
|
#include "zx_ula.h"
|
||||||
|
#include "zx_speaker.h"
|
||||||
#include "zx_screen.h"
|
#include "zx_screen.h"
|
||||||
#include "zx_tape.h"
|
#include "zx_tape.h"
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
@@ -12,8 +14,16 @@
|
|||||||
#include "ui_menu.h"
|
#include "ui_menu.h"
|
||||||
#include "z80analyze.h"
|
#include "z80analyze.h"
|
||||||
#include "ui_window.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 time = 0;
|
||||||
uint32_t t_states = 0;
|
uint32_t t_states = 0;
|
||||||
|
|
||||||
@@ -26,6 +36,11 @@ namespace actions
|
|||||||
zxscreen::refresh(dt);
|
zxscreen::refresh(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void exitButNotContinue()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
int fastload(int value)
|
int fastload(int value)
|
||||||
{
|
{
|
||||||
zx_tape::toggleOption(ZXTAPE_OPTION_FAST_LOAD);
|
zx_tape::toggleOption(ZXTAPE_OPTION_FAST_LOAD);
|
||||||
@@ -56,27 +71,58 @@ namespace actions
|
|||||||
return zxscreen::getFullscreen();
|
return zxscreen::getFullscreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fullrefresh(int value)
|
||||||
|
{
|
||||||
|
zxscreen::toggleFullRefresh();
|
||||||
|
return zxscreen::getFullRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
int showAnalyzer(int value)
|
int showAnalyzer(int value)
|
||||||
{
|
{
|
||||||
z80analyze::show();
|
z80analyze::show();
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
void init_menu()
|
||||||
{
|
{
|
||||||
FILE* f = fopen("48.rom", "rb");
|
|
||||||
fread(memory, 1024, 16, f);
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
z80dis::loadSymbols();
|
|
||||||
z80::reset(memory);
|
|
||||||
z80::connect_port(0xfe, zx_ula::port_in, zx_ula::port_out);
|
|
||||||
|
|
||||||
SDL_Init(SDL_INIT_EVERYTHING);
|
|
||||||
z80debug::init();
|
|
||||||
zxscreen::init();
|
|
||||||
|
|
||||||
ui::menu::init();
|
ui::menu::init();
|
||||||
ui::menu::setexitcallback(actions::exitMenu);
|
ui::menu::setexitcallback(actions::exitMenu);
|
||||||
|
|
||||||
@@ -86,39 +132,78 @@ int main(int argc, char *argv[])
|
|||||||
ui::menu::addseparator(menu);
|
ui::menu::addseparator(menu);
|
||||||
ui::menu::addoption(menu, "LOAD STATE", nullptr);
|
ui::menu::addoption(menu, "LOAD STATE", nullptr);
|
||||||
ui::menu::addoption(menu, "SAVE 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");
|
menu = ui::menu::addsubmenu("TAPE");
|
||||||
ui::menu::addbooloption(menu, "BERSERK MODE", zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD), actions::fastload);
|
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);
|
ui::menu::addbooloption(menu, "STOP AT END", zx_tape::getOption(ZXTAPE_OPTION_STOP_AT_END), actions::stopatend);
|
||||||
|
|
||||||
menu = ui::menu::addsubmenu("SCREEN");
|
menu = ui::menu::addsubmenu("SCREEN");
|
||||||
ui::menu::addoption(menu, "DEC ZOOM", actions::decZoom);
|
ui::menu::addoption(menu, "DEC ZOOM", actions::decZoom);
|
||||||
ui::menu::addoption(menu, "INC ZOOM", actions::incZoom);
|
ui::menu::addoption(menu, "INC ZOOM", actions::incZoom);
|
||||||
ui::menu::addbooloption(menu, "FULLSCREEN", zxscreen::getFullscreen(), actions::fullscreen);
|
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");
|
menu = ui::menu::addsubmenu("EMULATION");
|
||||||
ui::menu::addbooloption(menu, "STOP ON INVALID OP", z80::getOption(Z80_OPTION_STOP_ON_INVALID), actions::decZoom);
|
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, "SHOW ANALYZER", actions::showAnalyzer);
|
||||||
|
ui::menu::addoption(menu, "CONFIGURE JOYSTICKS", actions::configureJoysticks);
|
||||||
|
|
||||||
zx_ula::sound_init();
|
}
|
||||||
|
|
||||||
zx_tape::load("fernandomartin.tap");
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
file::setConfigFolder("z80");
|
||||||
|
SDL_Init(SDL_INIT_EVERYTHING);
|
||||||
|
init_menu();
|
||||||
|
z80debug::init();
|
||||||
|
|
||||||
if (argc==3) { z80debug::loadngo(argv[1], argv[2]); }
|
//uint32_t update_freq =
|
||||||
|
zx_system::init(ZX_48K);
|
||||||
|
gamepad::init();
|
||||||
|
zx_tape::load("ROBOCOP1.TAP");
|
||||||
|
|
||||||
|
//if (argc==3) { z80debug::loadngo(argv[1], argv[2]); }
|
||||||
|
|
||||||
|
//z80debug::stop();
|
||||||
|
|
||||||
bool should_exit = false;
|
|
||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
|
|
||||||
time = SDL_GetTicks();
|
time = SDL_GetTicks();
|
||||||
t_states = 0;
|
t_states = 0;
|
||||||
|
bool first_time = true;
|
||||||
|
|
||||||
while (!should_exit)
|
while (!zx_system::shuttingDown())
|
||||||
{
|
{
|
||||||
while (SDL_PollEvent(&e))
|
while (SDL_PollEvent(&e))
|
||||||
{
|
{
|
||||||
bool result = true;
|
bool result = true;
|
||||||
|
|
||||||
if (e.type == SDL_QUIT) { should_exit=true; break; }
|
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_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_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_MOUSEMOTION) result = ui::window::sendEvent(e.motion.windowID, &e);
|
||||||
@@ -140,15 +225,17 @@ int main(int argc, char *argv[])
|
|||||||
} else {
|
} else {
|
||||||
z80debug::show();
|
z80debug::show();
|
||||||
}
|
}
|
||||||
|
z80viewer::refreshAll();
|
||||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_F10) {
|
} else if (e.key.keysym.scancode==SDL_SCANCODE_F10) {
|
||||||
if (z80debug::debugging()) {
|
if (z80debug::debugging()) {
|
||||||
z80debug::show();
|
z80debug::show();
|
||||||
z80debug::history::gototop();
|
z80debug::history::gototop();
|
||||||
const uint8_t dt = z80::step();
|
const uint8_t dt = z80::step();
|
||||||
z80debug::refresh();
|
z80debug::refresh();
|
||||||
zxscreen::refresh(dt);
|
zxscreen::fullrefresh();
|
||||||
zxscreen::redraw();
|
zxscreen::redraw();
|
||||||
z80analyze::refresh();
|
z80analyze::refresh();
|
||||||
|
z80viewer::refreshAll();
|
||||||
}
|
}
|
||||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_F11) {
|
} else if (e.key.keysym.scancode==SDL_SCANCODE_F11) {
|
||||||
if (z80debug::debugging()) {
|
if (z80debug::debugging()) {
|
||||||
@@ -159,6 +246,18 @@ int main(int argc, char *argv[])
|
|||||||
zxscreen::refresh(dt);
|
zxscreen::refresh(dt);
|
||||||
zxscreen::redraw();
|
zxscreen::redraw();
|
||||||
z80analyze::refresh();
|
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);
|
result = ui::window::sendEvent(e.key.windowID, &e);
|
||||||
@@ -167,28 +266,13 @@ int main(int argc, char *argv[])
|
|||||||
if (e.type == SDL_MOUSEBUTTONUP && e.button.button==1) ui::setClicked(true);
|
if (e.type == SDL_MOUSEBUTTONUP && e.button.button==1) ui::setClicked(true);
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
should_exit = true; break;
|
zx_system::shutdown(); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
first_time = false;
|
||||||
if (!z80debug::debugging() && !z80debug::paused()) {
|
if (!z80debug::debugging() && !z80debug::paused()) {
|
||||||
//if (z80::getPC()==0x05C8) zx_tape::go_berserk();
|
|
||||||
bool fastload=false;
|
// En cada bucle fem 10 pasos de la CPU, sino s'ofega (jo en veig 5)
|
||||||
if (zx_tape::getplaying() && zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD)) { fastload=true; time = SDL_GetTicks(); }
|
|
||||||
while (zx_tape::getplaying() && zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD))
|
|
||||||
{
|
|
||||||
// zx_tape::update(z80::step());
|
|
||||||
uint8_t dt = z80::step();
|
|
||||||
t_states += dt;
|
|
||||||
zx_tape::update(dt);
|
|
||||||
if (SDL_GetTicks()-time>=1000) {
|
|
||||||
time = SDL_GetTicks();
|
|
||||||
zx_tape::report();
|
|
||||||
}
|
|
||||||
//zx_ula::sound_update(dt);
|
|
||||||
//zxscreen::refresh(dt);
|
|
||||||
}
|
|
||||||
if (fastload) { printf("%i\n", SDL_GetTicks()-time); t_states=0; }
|
|
||||||
// En cada bucle fem 10 pasos de la CPU, sino s'ofega
|
|
||||||
for (int i=0;i<5;++i) {
|
for (int i=0;i<5;++i) {
|
||||||
if (z80debug::isbreak(z80::getPC(), 9)) {
|
if (z80debug::isbreak(z80::getPC(), 9)) {
|
||||||
z80debug::stop();
|
z80debug::stop();
|
||||||
@@ -197,31 +281,34 @@ int main(int argc, char *argv[])
|
|||||||
} else {
|
} else {
|
||||||
uint8_t dt = z80::step();
|
uint8_t dt = z80::step();
|
||||||
t_states += dt;
|
t_states += dt;
|
||||||
zx_tape::update(dt);
|
zx_system::update(dt);
|
||||||
|
|
||||||
zx_ula::sound_update(dt);
|
|
||||||
zxscreen::refresh(dt);
|
zxscreen::refresh(dt);
|
||||||
|
if (z80debug::debugging()) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t_states>=350000)
|
uint32_t update_freq = z80::getClock()/10;
|
||||||
|
if (t_states>=update_freq)
|
||||||
{
|
{
|
||||||
//if (SDL_GetTicks()>=time+1000)
|
// Esperem a que es compleixca el temps corresponent als t states executats
|
||||||
//printf("%i\n", SDL_GetTicks()-(time+1000));
|
|
||||||
//else
|
|
||||||
// printf("%i\n", SDL_GetTicks()-(time+1000));
|
|
||||||
//t_states = 0;
|
|
||||||
//printf("%i: %i\n", SDL_GetTicks()-(time+1000), t_states);
|
|
||||||
while (SDL_GetTicks()<time+100) {}
|
while (SDL_GetTicks()<time+100) {}
|
||||||
t_states -= 350000;
|
|
||||||
|
t_states -= update_freq;
|
||||||
time = SDL_GetTicks();
|
time = SDL_GetTicks();
|
||||||
z80analyze::refresh();
|
z80analyze::refresh();
|
||||||
|
z80viewer::refreshAll();
|
||||||
}
|
}
|
||||||
|
z80analyze::refresh(true);
|
||||||
|
|
||||||
|
|
||||||
} else if (!z80debug::debugging() && z80debug::paused()) {
|
} else if (z80debug::paused()) {
|
||||||
zxscreen::redraw(false);
|
zxscreen::redraw(false);
|
||||||
ui::menu::show();
|
|
||||||
|
if (ui::hasDialog())
|
||||||
|
ui::callDialog();
|
||||||
|
else
|
||||||
|
ui::menu::show();
|
||||||
|
|
||||||
zxscreen::present();
|
zxscreen::present();
|
||||||
}
|
}
|
||||||
ui::setClicked(false);
|
ui::setClicked(false);
|
||||||
|
|||||||
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,2 +1 @@
|
|||||||
0xf72d MAINLOOP
|
0x6580 START
|
||||||
0xf864 CHK_KEYB
|
|
||||||
|
|||||||
79
ui.cpp
79
ui.cpp
@@ -30,6 +30,8 @@ namespace ui
|
|||||||
|
|
||||||
bool clicked = false;
|
bool clicked = false;
|
||||||
|
|
||||||
|
void (*dialog)(void) = nullptr;
|
||||||
|
|
||||||
SDL_Texture * createtexture(SDL_Renderer *renderer)
|
SDL_Texture * createtexture(SDL_Renderer *renderer)
|
||||||
{
|
{
|
||||||
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP("font.bmp"));
|
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP("font.bmp"));
|
||||||
@@ -39,7 +41,6 @@ namespace ui
|
|||||||
|
|
||||||
void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture)
|
void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture)
|
||||||
{
|
{
|
||||||
if (ren==renderer) return;
|
|
||||||
ren = renderer;
|
ren = renderer;
|
||||||
tex = texture;
|
tex = texture;
|
||||||
offset_x = offset_y = 0;
|
offset_x = offset_y = 0;
|
||||||
@@ -51,6 +52,43 @@ namespace ui
|
|||||||
offset_y = y;
|
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)
|
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_Rect rect {((offset_x+x)*CHR_W)+3, ((offset_y+y)*CHR_H)+6, w*CHR_W-6, h*CHR_H-13};
|
||||||
@@ -65,6 +103,13 @@ namespace ui
|
|||||||
SDL_RenderFillRect(ren, &rect);
|
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)
|
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 (color != 255) SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
|
||||||
@@ -81,6 +126,22 @@ namespace ui
|
|||||||
for (int i=0; i<strlen(text);++i) if (text[i]!=32) printchar(x+i, y, text[i]);
|
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)
|
void setClicked(const bool value)
|
||||||
{
|
{
|
||||||
clicked = value;
|
clicked = value;
|
||||||
@@ -91,4 +152,20 @@ namespace ui
|
|||||||
return clicked;
|
return clicked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setDialog(void(*new_dialog)(void))
|
||||||
|
{
|
||||||
|
ui:dialog = new_dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasDialog()
|
||||||
|
{
|
||||||
|
return dialog != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void callDialog()
|
||||||
|
{
|
||||||
|
if (dialog) dialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
13
ui.h
13
ui.h
@@ -26,12 +26,25 @@ namespace ui
|
|||||||
SDL_Texture * createtexture(SDL_Renderer *renderer);
|
SDL_Texture * createtexture(SDL_Renderer *renderer);
|
||||||
void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture);
|
void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture);
|
||||||
void setoffset(uint8_t x, uint8_t y);
|
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 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 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 printchar(int x, int y, char chr, uint8_t color=255);
|
||||||
void printtxt(int x, int y, const char *text, uint8_t color);
|
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);
|
void setClicked(const bool value);
|
||||||
const bool getClicked();
|
const bool getClicked();
|
||||||
|
|
||||||
|
void setDialog(void(*new_dialog)(void));
|
||||||
|
bool hasDialog();
|
||||||
|
void callDialog();
|
||||||
}
|
}
|
||||||
|
|||||||
20
ui_menu.cpp
20
ui_menu.cpp
@@ -31,6 +31,7 @@ namespace ui
|
|||||||
std::vector<menu_t> menus;
|
std::vector<menu_t> menus;
|
||||||
int visible_menu = -1;
|
int visible_menu = -1;
|
||||||
int menu_x = 0;
|
int menu_x = 0;
|
||||||
|
bool do_not_exit = false;
|
||||||
|
|
||||||
void init()
|
void init()
|
||||||
{
|
{
|
||||||
@@ -39,6 +40,7 @@ namespace ui
|
|||||||
|
|
||||||
void show()
|
void show()
|
||||||
{
|
{
|
||||||
|
ui::setoffset(0,0);
|
||||||
int mx, my;
|
int mx, my;
|
||||||
Uint32 mb = SDL_GetMouseState(&mx, &my);
|
Uint32 mb = SDL_GetMouseState(&mx, &my);
|
||||||
mx=mx/CHR_W; my=my/CHR_H;
|
mx=mx/CHR_W; my=my/CHR_H;
|
||||||
@@ -52,8 +54,8 @@ namespace ui
|
|||||||
uint8_t text_color = COLOR_WHITE;
|
uint8_t text_color = COLOR_WHITE;
|
||||||
if (my<1 && mx>=opt_pos && mx<opt_pos+text_size)
|
if (my<1 && mx>=opt_pos && mx<opt_pos+text_size)
|
||||||
{
|
{
|
||||||
ui::printrect(opt_pos-1, 0, text_size, 1, COLOR_DARK_BLUE);
|
ui::printrect(opt_pos-1, 0, text_size, 1, COLOR_WHITE);
|
||||||
text_color = COLOR_WHITE;
|
text_color = COLOR_BLACK;
|
||||||
visible_menu = index;
|
visible_menu = index;
|
||||||
menu_x = opt_pos-1;
|
menu_x = opt_pos-1;
|
||||||
}
|
}
|
||||||
@@ -73,14 +75,15 @@ namespace ui
|
|||||||
{
|
{
|
||||||
const int text_size = (option.label.size()+2);
|
const int text_size = (option.label.size()+2);
|
||||||
uint8_t text_color = COLOR_WHITE;
|
uint8_t text_color = COLOR_WHITE;
|
||||||
if (my==opt_pos && mx>=menu_x && mx<menu_x+text_size)
|
if (my==opt_pos && mx>=menu_x && mx<menu_x+20)
|
||||||
{
|
{
|
||||||
ui::printrect(menu_x+1, opt_pos, 20, 1, COLOR_DARK_BLUE);
|
ui::printrect(menu_x+1, opt_pos, 20, 1, COLOR_WHITE);
|
||||||
text_color = COLOR_WHITE;
|
text_color = COLOR_BLACK;
|
||||||
if (ui::getClicked())
|
if (ui::getClicked())
|
||||||
{
|
{
|
||||||
if (option.callback) option.value = option.callback(option.value);
|
if (option.callback) option.value = option.callback(option.value);
|
||||||
if (exit_callback) exit_callback();
|
if (exit_callback && !do_not_exit) exit_callback();
|
||||||
|
do_not_exit = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,6 +105,11 @@ namespace ui
|
|||||||
exit_callback = callback;
|
exit_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void exitButNotContinue()
|
||||||
|
{
|
||||||
|
do_not_exit = true;
|
||||||
|
}
|
||||||
|
|
||||||
const int addsubmenu(const char *label)
|
const int addsubmenu(const char *label)
|
||||||
{
|
{
|
||||||
menu_t m;
|
menu_t m;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace ui
|
|||||||
void init();
|
void init();
|
||||||
void show();
|
void show();
|
||||||
void setexitcallback(void(*callback)(void));
|
void setexitcallback(void(*callback)(void));
|
||||||
|
void exitButNotContinue();
|
||||||
const int addsubmenu(const char *label);
|
const int addsubmenu(const char *label);
|
||||||
void addoption(int menu, const char *label, int(*callback)(int));
|
void addoption(int menu, const char *label, int(*callback)(int));
|
||||||
void addbooloption(int menu, const char *label, bool default_value, int(*callback)(int));
|
void addbooloption(int menu, const char *label, bool default_value, int(*callback)(int));
|
||||||
|
|||||||
27
z80.h
27
z80.h
@@ -4,22 +4,24 @@
|
|||||||
|
|
||||||
namespace z80
|
namespace z80
|
||||||
{
|
{
|
||||||
#define MEMTAG_NONE 0
|
|
||||||
#define MEMTAG_INST 1
|
|
||||||
#define MEMTAG_CODE 2
|
|
||||||
#define MEMTAG_DATA 3
|
|
||||||
|
|
||||||
#define Z80_OPTION_STOP_ON_INVALID 0
|
#define Z80_OPTION_STOP_ON_INVALID 0
|
||||||
#define Z80_OPTION_BREAK_ON_INTERRUPT 1
|
#define Z80_OPTION_BREAK_ON_INTERRUPT 1
|
||||||
#define Z80_NUM_OPTIONS 2
|
#define Z80_OPTION_BREAK_ON_RET 2
|
||||||
|
#define Z80_NUM_OPTIONS 3
|
||||||
|
|
||||||
void reset(uint8_t* mem);
|
void init(uint32_t freq);
|
||||||
void connect_port(int num, int (*in_ptr)(int), void (*out_ptr)(int,int));
|
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();
|
void interrupt();
|
||||||
|
|
||||||
uint32_t step();
|
uint32_t step();
|
||||||
|
|
||||||
uint8_t *getMem();
|
//uint8_t *getMem();
|
||||||
uint8_t *getRegs();
|
uint8_t *getRegs();
|
||||||
|
|
||||||
uint16_t getAF(const bool alt=false);
|
uint16_t getAF(const bool alt=false);
|
||||||
@@ -37,13 +39,20 @@ namespace z80
|
|||||||
|
|
||||||
|
|
||||||
void setPC(const uint16_t addr);
|
void setPC(const uint16_t addr);
|
||||||
|
|
||||||
|
/*
|
||||||
uint8_t getMemTag(const uint16_t addr);
|
uint8_t getMemTag(const uint16_t addr);
|
||||||
|
void setMemTag(const uint16_t addr, const uint8_t value);
|
||||||
void clearMemTag();
|
void clearMemTag();
|
||||||
|
|
||||||
uint8_t getMemTouched(const uint16_t addr);
|
uint8_t getMemTouched(const uint16_t addr);
|
||||||
void clearMemTouched();
|
void clearMemTouched();
|
||||||
|
void fixMemTouched();
|
||||||
|
*/
|
||||||
|
|
||||||
const bool getOption(const int option);
|
const bool getOption(const int option);
|
||||||
void setOption(const int option, const bool value);
|
void setOption(const int option, const bool value);
|
||||||
void toggleOption(const int option);
|
void toggleOption(const int option);
|
||||||
|
|
||||||
|
void resetStackedCalls();
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "z80analyze.h"
|
#include "z80analyze.h"
|
||||||
#include "z80.h"
|
#include "z80.h"
|
||||||
|
#include "z80debug.h"
|
||||||
|
#include "zx_mem.h"
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include "ui_window.h"
|
#include "ui_window.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
@@ -10,6 +12,9 @@ namespace z80analyze
|
|||||||
SDL_Renderer *ren = nullptr;
|
SDL_Renderer *ren = nullptr;
|
||||||
SDL_Texture *tex = nullptr;
|
SDL_Texture *tex = nullptr;
|
||||||
SDL_Texture *uitex = nullptr;
|
SDL_Texture *uitex = nullptr;
|
||||||
|
uint16_t address = 0;
|
||||||
|
int mx, my;
|
||||||
|
bool needs_refresh = true;
|
||||||
|
|
||||||
void refreshTitle();
|
void refreshTitle();
|
||||||
|
|
||||||
@@ -17,12 +22,43 @@ namespace z80analyze
|
|||||||
{
|
{
|
||||||
if (e->type == SDL_MOUSEBUTTONUP)
|
if (e->type == SDL_MOUSEBUTTONUP)
|
||||||
{
|
{
|
||||||
z80::clearMemTouched();
|
//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();
|
refresh();
|
||||||
}
|
}
|
||||||
if (e->type == SDL_MOUSEMOTION)
|
if (e->type == SDL_MOUSEMOTION)
|
||||||
{
|
{
|
||||||
refreshTitle();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -44,14 +80,18 @@ namespace z80analyze
|
|||||||
char tmp[10];
|
char tmp[10];
|
||||||
void refreshTitle()
|
void refreshTitle()
|
||||||
{
|
{
|
||||||
int mx, my;
|
if (mx>=0 && my>=0 && mx<256 && my<256) {
|
||||||
SDL_GetMouseState(&mx, &my);
|
address = mx+my*256;
|
||||||
mx/=2; my/=2;
|
SDL_SetWindowTitle(win, SDL_itoa(address, tmp, 16));
|
||||||
SDL_SetWindowTitle(win, SDL_itoa(mx+my*256, tmp, 16));
|
}
|
||||||
}
|
}
|
||||||
void refresh()
|
|
||||||
|
void refresh(const bool conditional)
|
||||||
{
|
{
|
||||||
if (!win) return;
|
if (!win) return;
|
||||||
|
if (conditional && !needs_refresh) return;
|
||||||
|
needs_refresh = false;
|
||||||
|
|
||||||
ui::setrenderer(ren, uitex);
|
ui::setrenderer(ren, uitex);
|
||||||
|
|
||||||
Uint32 *pixels;
|
Uint32 *pixels;
|
||||||
@@ -59,10 +99,25 @@ namespace z80analyze
|
|||||||
SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch);
|
SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch);
|
||||||
for (int i=0; i<65536; ++i)
|
for (int i=0; i<65536; ++i)
|
||||||
{
|
{
|
||||||
uint8_t tag = z80::getMemTouched(i);
|
//uint8_t tag = z80::getMemTag(i);
|
||||||
pixels[i] = tag==MEMTAG_NONE ? 0x808080 : tag==MEMTAG_DATA ? 0x0000FF : 0x00FF00;
|
//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()] = 0xFF0000;
|
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_UnlockTexture(tex);
|
||||||
SDL_RenderCopy(ren, tex, NULL, NULL);
|
SDL_RenderCopy(ren, tex, NULL, NULL);
|
||||||
SDL_RenderPresent(ren);
|
SDL_RenderPresent(ren);
|
||||||
@@ -77,4 +132,13 @@ namespace z80analyze
|
|||||||
SDL_DestroyRenderer(ren); ren = nullptr;
|
SDL_DestroyRenderer(ren); ren = nullptr;
|
||||||
SDL_DestroyWindow(win); win = nullptr;
|
SDL_DestroyWindow(win); win = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void focus()
|
||||||
|
{
|
||||||
|
if (win) {
|
||||||
|
SDL_RaiseWindow(win);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace z80analyze
|
namespace z80analyze
|
||||||
{
|
{
|
||||||
void show();
|
void show();
|
||||||
void refresh();
|
void refresh(const bool conditional=false);
|
||||||
void hide();
|
void hide();
|
||||||
|
void focus();
|
||||||
}
|
}
|
||||||
|
|||||||
482
z80debug.cpp
482
z80debug.cpp
@@ -2,12 +2,17 @@
|
|||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include "z80.h"
|
#include "z80.h"
|
||||||
#include "z80dis.h"
|
#include "z80dis.h"
|
||||||
|
#include "zx_mem.h"
|
||||||
|
#include "z80analyze.h"
|
||||||
#include "zx_ula.h"
|
#include "zx_ula.h"
|
||||||
|
#include "zx_speaker.h"
|
||||||
#include "zx_tape.h"
|
#include "zx_tape.h"
|
||||||
|
#include "zx_disk.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "ui_window.h"
|
#include "ui_window.h"
|
||||||
#include "zx_screen.h"
|
#include "zx_screen.h"
|
||||||
#include "z80analyze.h"
|
#include "ay-3-8912.h"
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
#define RESIZING_NONE 0
|
#define RESIZING_NONE 0
|
||||||
#define RESIZING_MEMORY 1
|
#define RESIZING_MEMORY 1
|
||||||
@@ -20,12 +25,12 @@ namespace z80debug
|
|||||||
int midx = 58;
|
int midx = 58;
|
||||||
int mem_y = 20;
|
int mem_y = 20;
|
||||||
int con_y = 28;
|
int con_y = 28;
|
||||||
int sym_y = 20;
|
int sym_y = 24;
|
||||||
int win_h = 44;
|
int win_h = 44;
|
||||||
int mem_h = 8;
|
int mem_h = 8;
|
||||||
int con_h = 6;
|
int con_h = 6;
|
||||||
int sym_h = 14;
|
int sym_h = 14;
|
||||||
|
int zoom = 1;
|
||||||
int resizing_type = RESIZING_MEMORY;
|
int resizing_type = RESIZING_MEMORY;
|
||||||
bool resizing = false;
|
bool resizing = false;
|
||||||
|
|
||||||
@@ -39,7 +44,7 @@ namespace z80debug
|
|||||||
uint16_t oAF, oBC, oDE, oHL, oAF2, oBC2, oDE2, oHL2, oIX, oIY, oSP, oPC;
|
uint16_t oAF, oBC, oDE, oHL, oAF2, oBC2, oDE2, oHL2, oIX, oIY, oSP, oPC;
|
||||||
uint8_t oI, oR;
|
uint8_t oI, oR;
|
||||||
|
|
||||||
bool mem_modified[65536];
|
bool mem_modified[65536]; //[CHECK]
|
||||||
|
|
||||||
SDL_Window *win = nullptr;
|
SDL_Window *win = nullptr;
|
||||||
SDL_Renderer *ren = nullptr;
|
SDL_Renderer *ren = nullptr;
|
||||||
@@ -62,6 +67,19 @@ namespace z80debug
|
|||||||
|
|
||||||
uint16_t cursor = 0;
|
uint16_t cursor = 0;
|
||||||
|
|
||||||
|
uint8_t use[7][256];
|
||||||
|
|
||||||
|
uint16_t line_address[256];
|
||||||
|
uint16_t line_breakpoint_address[256];
|
||||||
|
int num_breakpoint_lines=0;
|
||||||
|
bool sync_mem_with_cursor = false;
|
||||||
|
|
||||||
|
uint8_t search_sequence[32];
|
||||||
|
int search_sequence_len=0;
|
||||||
|
int search_pos=0;
|
||||||
|
|
||||||
|
uint16_t inspected_value = 255;
|
||||||
|
|
||||||
char temp[256];
|
char temp[256];
|
||||||
const char *tohex(int value, int numdigits)
|
const char *tohex(int value, int numdigits)
|
||||||
{
|
{
|
||||||
@@ -69,24 +87,47 @@ namespace z80debug
|
|||||||
else if (numdigits==4) sprintf(temp, "%04X", value);
|
else if (numdigits==4) sprintf(temp, "%04X", value);
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
const char *todec(int value, bool sign)
|
||||||
|
{
|
||||||
|
if (sign)
|
||||||
|
sprintf(temp, "%i", int8_t(value));
|
||||||
|
else
|
||||||
|
sprintf(temp, "%u", value);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveWindowConfiguration()
|
||||||
|
{
|
||||||
|
file::setConfigValueInteger("debug_zoom", zoom);
|
||||||
|
int x, y;
|
||||||
|
SDL_GetWindowPosition(win, &x, &y);
|
||||||
|
file::setConfigValueInteger("debug_x", x);
|
||||||
|
file::setConfigValueInteger("debug_y", y);
|
||||||
|
}
|
||||||
|
|
||||||
bool eventHandler(SDL_Event *e)
|
bool eventHandler(SDL_Event *e)
|
||||||
{
|
{
|
||||||
|
if (e->type == SDL_MOUSEMOTION) {
|
||||||
|
SDL_ShowCursor(SDL_ENABLE);
|
||||||
|
}
|
||||||
if (z80debug::debugging()) {
|
if (z80debug::debugging()) {
|
||||||
if (e->type==SDL_WINDOWEVENT) {
|
if (e->type==SDL_WINDOWEVENT) {
|
||||||
if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) {
|
if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) {
|
||||||
int w; int h;
|
int w; int h;
|
||||||
SDL_GetWindowSize(win, &w, &h);
|
SDL_GetWindowSize(win, &w, &h);
|
||||||
|
w/=zoom; h/=zoom;
|
||||||
midx = (w/CHR_W) - 25;
|
midx = (w/CHR_W) - 25;
|
||||||
win_h = (h/CHR_H);
|
win_h = (h/CHR_H);
|
||||||
mem_y = win_h - mem_h - con_h;
|
mem_y = win_h - mem_h - con_h;
|
||||||
con_y = win_h - con_h;
|
con_y = win_h - con_h;
|
||||||
sym_y = win_h - sym_h;
|
sym_h = win_h - sym_y;
|
||||||
z80debug::refresh();
|
z80debug::refresh();
|
||||||
zxscreen::redraw();
|
zxscreen::redraw();
|
||||||
} else if (e->window.event == SDL_WINDOWEVENT_CLOSE) {
|
} else if (e->window.event == SDL_WINDOWEVENT_CLOSE) {
|
||||||
hide();
|
hide();
|
||||||
zxscreen::focus();
|
zxscreen::focus();
|
||||||
|
} else if (e->window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
|
||||||
|
saveWindowConfiguration();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (e->type == SDL_MOUSEWHEEL) {
|
if (e->type == SDL_MOUSEWHEEL) {
|
||||||
@@ -98,6 +139,14 @@ namespace z80debug
|
|||||||
z80debug::cursorfwd();
|
z80debug::cursorfwd();
|
||||||
z80debug::refresh();
|
z80debug::refresh();
|
||||||
}
|
}
|
||||||
|
} else if (e->wheel.mouseX<midx*CHR_W && e->wheel.mouseY>=mem_y*CHR_H && e->wheel.mouseY<con_y*CHR_H) {
|
||||||
|
if (e->wheel.y>0) {
|
||||||
|
mem_viewer_pos-=0x10;
|
||||||
|
z80debug::refresh();
|
||||||
|
} else if (e->wheel.y<0) {
|
||||||
|
mem_viewer_pos+=0x10;
|
||||||
|
z80debug::refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (e->type == SDL_KEYDOWN) {
|
if (e->type == SDL_KEYDOWN) {
|
||||||
@@ -106,17 +155,28 @@ namespace z80debug
|
|||||||
} else if (e->key.keysym.scancode==SDL_SCANCODE_F1) {
|
} else if (e->key.keysym.scancode==SDL_SCANCODE_F1) {
|
||||||
z80debug::history::gototop();
|
z80debug::history::gototop();
|
||||||
z80debug::refresh();
|
z80debug::refresh();
|
||||||
|
z80analyze::refresh();
|
||||||
} else if (e->key.keysym.scancode==SDL_SCANCODE_F2) {
|
} else if (e->key.keysym.scancode==SDL_SCANCODE_F2) {
|
||||||
z80debug::history::goback();
|
if (e->key.keysym.mod & KMOD_CTRL) {
|
||||||
z80debug::refresh();
|
zoom = zoom==1?2:1;
|
||||||
|
hide();
|
||||||
|
show();
|
||||||
|
} else {
|
||||||
|
z80debug::history::goback();
|
||||||
|
z80debug::refresh();
|
||||||
|
z80analyze::refresh();
|
||||||
|
}
|
||||||
} else if (e->key.keysym.scancode==SDL_SCANCODE_F3) {
|
} else if (e->key.keysym.scancode==SDL_SCANCODE_F3) {
|
||||||
z80debug::history::goforward();
|
z80debug::history::goforward();
|
||||||
z80debug::refresh();
|
z80debug::refresh();
|
||||||
/*} else if (e->key.keysym.scancode==SDL_SCANCODE_F6) {
|
z80analyze::refresh();
|
||||||
z80debug::history::gototop();
|
} else if (e->key.keysym.scancode==SDL_SCANCODE_F9) {
|
||||||
const uint8_t dt = z80::step();
|
const uint16_t address = cursor;
|
||||||
z80debug::refresh();
|
if (breakpoints[line_address[address]]==0)
|
||||||
zxscreen::refresh(dt);*/
|
breakpoints[line_address[address]]=1;
|
||||||
|
else
|
||||||
|
breakpoints[line_address[address]]=0;
|
||||||
|
refresh();
|
||||||
} else if (e->key.keysym.scancode==SDL_SCANCODE_UP) {
|
} else if (e->key.keysym.scancode==SDL_SCANCODE_UP) {
|
||||||
if (console_history_nav != console_history_pos+1 && console_history[console_history_nav-1][0]!=0) console_history_nav--;
|
if (console_history_nav != console_history_pos+1 && console_history[console_history_nav-1][0]!=0) console_history_nav--;
|
||||||
//z80debug::cursorback();
|
//z80debug::cursorback();
|
||||||
@@ -146,6 +206,55 @@ namespace z80debug
|
|||||||
if (resizing_type != RESIZING_NONE) SDL_SetCursor(cur_df);
|
if (resizing_type != RESIZING_NONE) SDL_SetCursor(cur_df);
|
||||||
resizing_type = RESIZING_NONE;
|
resizing_type = RESIZING_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int chrx = e->motion.x/CHR_W;
|
||||||
|
const int chry = e->motion.y/CHR_H;
|
||||||
|
//printf("%ix%i\n", chrx, chry);
|
||||||
|
// Si pasa per damunt de l'adreça en el desensamblador
|
||||||
|
if ( (chrx>1) && (chrx<6) && (chry<mem_y-1) && (chry>0) ) {
|
||||||
|
inspected_value = line_address[chry-1];
|
||||||
|
refresh();
|
||||||
|
// Si pasa per damunt dels bytes que formen el opcode en el desensamblador
|
||||||
|
} else if ((chrx>=19) && (chrx<30) && (chry<mem_y-1) && (chry>0)) {
|
||||||
|
const uint16_t address = line_address[chry-1];
|
||||||
|
const int opcodesize = z80dis::getOpcodeSize(address);
|
||||||
|
const int byte = (chrx-19)/3;
|
||||||
|
if (byte < opcodesize) {
|
||||||
|
inspected_value = mem::readMem(address+byte);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
// Si pasa per damunt de l'adreça en el visor de memòria
|
||||||
|
} else if ((chrx>1) && (chrx<6) && (chry>mem_y) && (chry<mem_y+mem_h-1)) {
|
||||||
|
inspected_value = mem_viewer_pos + (chry-mem_y-1)*16;
|
||||||
|
refresh();
|
||||||
|
// Si pasa per damunt d'un byte en el visor de memòria
|
||||||
|
} else if ((chrx>6) && (chrx<55) && (chry>mem_y) && (chry<mem_y+mem_h-1)) {
|
||||||
|
const uint16_t address = mem_viewer_pos + (chry-mem_y-1)*16 + (chrx-7)/3;
|
||||||
|
inspected_value = mem::readMem(address);
|
||||||
|
refresh();
|
||||||
|
// Si pasa per damunt d'un registre
|
||||||
|
} else if ((chrx>=midx+4) && (chrx<midx+8) && (chry==1)) { inspected_value = z80::getAF(); refresh();
|
||||||
|
} else if ((chrx>=midx+4) && (chrx<midx+8) && (chry==2)) { inspected_value = z80::getBC(); refresh();
|
||||||
|
} else if ((chrx>=midx+4) && (chrx<midx+8) && (chry==3)) { inspected_value = z80::getDE(); refresh();
|
||||||
|
} else if ((chrx>=midx+4) && (chrx<midx+8) && (chry==4)) { inspected_value = z80::getHL(); refresh();
|
||||||
|
} else if ((chrx>=midx+12) && (chrx<midx+16) && (chry==1)) { inspected_value = z80::getAF(true); refresh();
|
||||||
|
} else if ((chrx>=midx+12) && (chrx<midx+16) && (chry==2)) { inspected_value = z80::getBC(true); refresh();
|
||||||
|
} else if ((chrx>=midx+12) && (chrx<midx+16) && (chry==3)) { inspected_value = z80::getDE(true); refresh();
|
||||||
|
} else if ((chrx>=midx+12) && (chrx<midx+16) && (chry==4)) { inspected_value = z80::getHL(true); refresh();
|
||||||
|
} else if ((chrx>=midx+20) && (chrx<midx+24) && (chry==1)) { inspected_value = z80::getIX(); refresh();
|
||||||
|
} else if ((chrx>=midx+20) && (chrx<midx+24) && (chry==2)) { inspected_value = z80::getIY(); refresh();
|
||||||
|
} else if ((chrx>=midx+20) && (chrx<midx+24) && (chry==3)) { inspected_value = z80::getSP(); refresh();
|
||||||
|
} else if ((chrx>=midx+20) && (chrx<midx+24) && (chry==4)) { inspected_value = z80::getPC(); refresh();
|
||||||
|
|
||||||
|
} else if ((chrx>=midx+6) && (chrx<midx+8) && (chry==5)) { inspected_value = mem::readMem(z80::getBC()); refresh();
|
||||||
|
} else if ((chrx>=midx+14) && (chrx<midx+16) && (chry==5)) { inspected_value = mem::readMem(z80::getDE()); refresh();
|
||||||
|
} else if ((chrx>=midx+22) && (chrx<midx+24) && (chry==5)) { inspected_value = mem::readMem(z80::getHL()); refresh();
|
||||||
|
|
||||||
|
} else if ((chrx>=midx+17) && (chrx<midx+19) && (chry==6)) { inspected_value = z80::getI(); refresh();
|
||||||
|
} else if ((chrx>=midx+22) && (chrx<midx+24) && (chry==6)) { inspected_value = z80::getR(); refresh();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (resizing_type==RESIZING_MEMORY) {
|
if (resizing_type==RESIZING_MEMORY) {
|
||||||
const int new_mem_y = e->motion.y/CHR_H;
|
const int new_mem_y = e->motion.y/CHR_H;
|
||||||
@@ -177,6 +286,38 @@ namespace z80debug
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (e->type == SDL_MOUSEBUTTONDOWN) {
|
if (e->type == SDL_MOUSEBUTTONDOWN) {
|
||||||
|
const int chrx = e->button.x/CHR_W;
|
||||||
|
const int chry = e->button.y/CHR_H;
|
||||||
|
//printf("%ix%i\n", chrx, chry);
|
||||||
|
if ( (e->button.x<midx*CHR_W) && (e->button.y<(mem_y-1)*CHR_H) && (e->button.y>CHR_H) ) {
|
||||||
|
const uint16_t address = ((e->button.y)/CHR_H)-1;
|
||||||
|
if (breakpoints[line_address[address]]==0)
|
||||||
|
breakpoints[line_address[address]]=1;
|
||||||
|
else
|
||||||
|
breakpoints[line_address[address]]=0;
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
if (chrx>=midx+12 && chry>=9 && chry<sym_y-1) {
|
||||||
|
const int breakpoint_selected = chry-9;
|
||||||
|
if (num_breakpoint_lines>breakpoint_selected) {
|
||||||
|
cursor = line_breakpoint_address[breakpoint_selected];
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chrx>=10 && chrx<=12 && chry==mem_y) {
|
||||||
|
sync_mem_with_cursor = ! sync_mem_with_cursor;
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chrx>=midx+2 && chry>=sym_y+1) {
|
||||||
|
const int line = chry-(sym_y+1);
|
||||||
|
if (line<z80dis::getNumSymbols()) {
|
||||||
|
cursor = z80dis::getSymbolAddress(line);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!resizing && resizing_type != RESIZING_NONE) {
|
if (!resizing && resizing_type != RESIZING_NONE) {
|
||||||
resizing = true;
|
resizing = true;
|
||||||
}
|
}
|
||||||
@@ -211,12 +352,17 @@ namespace z80debug
|
|||||||
void show()
|
void show()
|
||||||
{
|
{
|
||||||
if (!win) {
|
if (!win) {
|
||||||
win = SDL_CreateWindow("Z80 Debugger", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 98*CHR_W, 44*CHR_H, SDL_WINDOW_RESIZABLE);
|
zoom = file::getConfigValueInteger("debug_zoom", 1);
|
||||||
|
const int x = file::getConfigValueInteger("debug_x", SDL_WINDOWPOS_UNDEFINED);
|
||||||
|
const int y = file::getConfigValueInteger("debug_y", SDL_WINDOWPOS_UNDEFINED);
|
||||||
|
win = SDL_CreateWindow("Z80 Debugger", x, y, 98*CHR_W*zoom, 44*CHR_H*zoom, SDL_WINDOW_SHOWN);
|
||||||
ren = SDL_CreateRenderer(win, -1, 0);
|
ren = SDL_CreateRenderer(win, -1, 0);
|
||||||
|
SDL_RenderSetLogicalSize(ren, 98*CHR_W, 44*CHR_H);
|
||||||
ui::window::registerWindow(SDL_GetWindowID(win), eventHandler);
|
ui::window::registerWindow(SDL_GetWindowID(win), eventHandler);
|
||||||
tex = ui::createtexture(ren);
|
tex = ui::createtexture(ren);
|
||||||
}
|
}
|
||||||
focus();
|
focus();
|
||||||
|
z80analyze::refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void focus()
|
void focus()
|
||||||
@@ -229,6 +375,7 @@ namespace z80debug
|
|||||||
|
|
||||||
void hide()
|
void hide()
|
||||||
{
|
{
|
||||||
|
saveWindowConfiguration();
|
||||||
ui::window::unregisterWindow(SDL_GetWindowID(win));
|
ui::window::unregisterWindow(SDL_GetWindowID(win));
|
||||||
if (tex) SDL_DestroyTexture(tex);
|
if (tex) SDL_DestroyTexture(tex);
|
||||||
if (ren) SDL_DestroyRenderer(ren);
|
if (ren) SDL_DestroyRenderer(ren);
|
||||||
@@ -240,7 +387,7 @@ namespace z80debug
|
|||||||
|
|
||||||
void pause()
|
void pause()
|
||||||
{
|
{
|
||||||
zx_ula::sound_disable();
|
speaker::disable();
|
||||||
is_paused = true;
|
is_paused = true;
|
||||||
breakpoints[z80::getPC()] &= ~8;
|
breakpoints[z80::getPC()] &= ~8;
|
||||||
}
|
}
|
||||||
@@ -250,18 +397,20 @@ namespace z80debug
|
|||||||
//history::gototop();
|
//history::gototop();
|
||||||
zxscreen::setTitle(" (stopped)");
|
zxscreen::setTitle(" (stopped)");
|
||||||
pause();
|
pause();
|
||||||
|
z80::resetStackedCalls();
|
||||||
is_debugging = true;
|
is_debugging = true;
|
||||||
show();
|
show();
|
||||||
/*refresh();*/
|
/*refresh();*/
|
||||||
|
if ( zxscreen::getFullRefresh()) zxscreen::fullrefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cont() {
|
void cont() {
|
||||||
zxscreen::setTitle("");
|
zxscreen::setTitle("");
|
||||||
is_debugging = is_paused = false;
|
is_debugging = is_paused = false;
|
||||||
//hide();
|
hide();
|
||||||
refresh();
|
refresh();
|
||||||
zxscreen::focus();
|
zxscreen::focus();
|
||||||
zx_ula::sound_enable();
|
speaker::enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool debugging() { return is_debugging; }
|
const bool debugging() { return is_debugging; }
|
||||||
@@ -271,9 +420,10 @@ namespace z80debug
|
|||||||
uint16_t find_previous_opcode(uint16_t pc)
|
uint16_t find_previous_opcode(uint16_t pc)
|
||||||
{
|
{
|
||||||
pc--;
|
pc--;
|
||||||
if (z80::getMemTag(pc)!=MEMTAG_CODE && z80::getMemTag(pc)!=MEMTAG_INST) return pc;
|
uint8_t tag = mem::getTag(pc);
|
||||||
|
if ( !(tag & (MEMTAG_CODE | MEMTAG_INST) ) ) return pc;
|
||||||
|
|
||||||
while (z80::getMemTag(pc)!=MEMTAG_INST) pc--;
|
while ( !(mem::getTag(pc) & MEMTAG_INST) ) pc--;
|
||||||
|
|
||||||
return pc;
|
return pc;
|
||||||
/*
|
/*
|
||||||
@@ -292,9 +442,10 @@ namespace z80debug
|
|||||||
|
|
||||||
void printDissasemblerLine(const uint16_t address, const int line, const bool heuristics=false)
|
void printDissasemblerLine(const uint16_t address, const int line, const bool heuristics=false)
|
||||||
{
|
{
|
||||||
uint8_t colors[4] = { COLOR_RED, COLOR_CYAN, COLOR_GRAY, COLOR_WHITE};
|
uint8_t colors[4] = { COLOR_RED, COLOR_CYAN, COLOR_WHITE, COLOR_WHITE};
|
||||||
if (z80::getMemTouched(address)!=MEMTAG_INST) colors[3]=COLOR_GRAY;
|
const uint8_t tag = mem::getTag(address);
|
||||||
if (z80::getMemTouched(address)==MEMTAG_NONE) colors[1]=COLOR_GRAY;
|
if ( !(tag & (MEMTAG_TINST | MEMTAG_TREPEAT)) ) colors[3]=COLOR_GRAY;
|
||||||
|
if ( !(tag & MEMTAG_TOUCHED) ) colors[1]=COLOR_GRAY;
|
||||||
|
|
||||||
if (is_debugging && (address == z80::getPC())) {
|
if (is_debugging && (address == z80::getPC())) {
|
||||||
for (int i=0; i<4;++i) colors[i]=COLOR_YELLOW;
|
for (int i=0; i<4;++i) colors[i]=COLOR_YELLOW;
|
||||||
@@ -304,13 +455,22 @@ namespace z80debug
|
|||||||
if (breakpoints[address]&9) ui::printtxt(0,line,"*", colors[0]);
|
if (breakpoints[address]&9) ui::printtxt(0,line,"*", colors[0]);
|
||||||
ui::printtxt(1,line,tohex(address,4), colors[1]);
|
ui::printtxt(1,line,tohex(address,4), colors[1]);
|
||||||
|
|
||||||
if ( (z80::getMemTag(address)==MEMTAG_INST) || heuristics ) {
|
if ( (tag & MEMTAG_INST) || heuristics ) {
|
||||||
const char *sym = z80dis::getSymbol(address);
|
const char *sym = z80dis::getSymbol(address);
|
||||||
if (sym[0]!=0) ui::printtxt(7,line, sym, COLOR_YELLOW);
|
if (sym[0]!=0) ui::printtxt(7,line, sym, COLOR_YELLOW);
|
||||||
|
|
||||||
|
const int opcodesize = z80dis::getOpcodeSize(address);
|
||||||
|
for (int i=0; i<opcodesize; ++i) {
|
||||||
|
const uint8_t tag = mem::getTag(address+i);
|
||||||
|
const uint32_t color = !(tag & MEMTAG_KNOWN) ? COLOR_GRAY : (tag & MEMTAG_DATA) ? ( (tag & (MEMTAG_CODE|MEMTAG_INST)) ? COLOR_MAGENTA : COLOR_BLUE ) : COLOR_GREEN;
|
||||||
|
ui::printrect(19+i*3,line,2,1,color);
|
||||||
|
}
|
||||||
|
|
||||||
ui::printtxt(19,line, z80dis::getOpcode(address), colors[2]);
|
ui::printtxt(19,line, z80dis::getOpcode(address), colors[2]);
|
||||||
ui::printtxt(31,line, z80dis::getAsm(address), colors[3]);
|
ui::printtxt(31,line, z80dis::getAsm(address), colors[3]);
|
||||||
} else {
|
} else {
|
||||||
ui::printtxt(19,line, tohex(z80::getMem()[address],2), COLOR_GRAY);
|
ui::printrect(19,line,2,1,COLOR_GRAY);
|
||||||
|
ui::printtxt(19,line, tohex(mem::readMem(address),2), COLOR_WHITE);
|
||||||
ui::printtxt(31,line, "?????????", COLOR_GRAY);
|
ui::printtxt(31,line, "?????????", COLOR_GRAY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,21 +491,24 @@ namespace z80debug
|
|||||||
|
|
||||||
ui::setoffset(1, 1);
|
ui::setoffset(1, 1);
|
||||||
|
|
||||||
uint8_t *memory = z80::getMem();
|
|
||||||
|
|
||||||
uint16_t pc = cursor; //z80::getPC();
|
uint16_t pc = cursor; //z80::getPC();
|
||||||
|
if (sync_mem_with_cursor) mem_viewer_pos = cursor;
|
||||||
uint16_t pos = pc;
|
uint16_t pos = pc;
|
||||||
int num_lines = mem_y-2;
|
int num_lines = mem_y-2;
|
||||||
|
line_address[(num_lines/2)-1] = pos;
|
||||||
|
ui::printvoidrect(0,(num_lines/2)-1, midx-2,1, COLOR_BLUE);
|
||||||
printDissasemblerLine(pc, (num_lines/2)-1, true);
|
printDissasemblerLine(pc, (num_lines/2)-1, true);
|
||||||
|
|
||||||
for (int i=num_lines/2;i<num_lines;++i) {
|
for (int i=num_lines/2;i<num_lines;++i) {
|
||||||
pos += z80dis::getOpcodeSize(pos);
|
pos += z80dis::getOpcodeSize(pos);
|
||||||
|
line_address[i] = pos;
|
||||||
printDissasemblerLine(pos, i, true);
|
printDissasemblerLine(pos, i, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = pc;
|
pos = pc;
|
||||||
for (int i=(num_lines/2)-2;i>=0;--i) {
|
for (int i=(num_lines/2)-2;i>=0;--i) {
|
||||||
pos = find_previous_opcode(pos);
|
pos = find_previous_opcode(pos);
|
||||||
|
line_address[i] = pos;
|
||||||
printDissasemblerLine(pos, i);
|
printDissasemblerLine(pos, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,9 +544,9 @@ namespace z80debug
|
|||||||
ui::printtxt(16,5, tohex(z80::getI(), 2), oI != z80::getI() ? COLOR_RED : COLOR_GRAY);
|
ui::printtxt(16,5, tohex(z80::getI(), 2), oI != z80::getI() ? COLOR_RED : COLOR_GRAY);
|
||||||
ui::printtxt(21,5, tohex(z80::getR(), 2), oR != z80::getR() ? COLOR_RED : COLOR_GRAY);
|
ui::printtxt(21,5, tohex(z80::getR(), 2), oR != z80::getR() ? COLOR_RED : COLOR_GRAY);
|
||||||
|
|
||||||
ui::printtxt(5,4, tohex(memory[z80::getBC()], 2), COLOR_GRAY);
|
ui::printtxt(5,4, tohex(mem::readMem(z80::getBC()), 2), COLOR_GRAY);
|
||||||
ui::printtxt(13,4, tohex(memory[z80::getDE()], 2), COLOR_GRAY);
|
ui::printtxt(13,4, tohex(mem::readMem(z80::getDE()), 2), COLOR_GRAY);
|
||||||
ui::printtxt(21,4, tohex(memory[z80::getHL()], 2), COLOR_GRAY);
|
ui::printtxt(21,4, tohex(mem::readMem(z80::getHL()), 2), COLOR_GRAY);
|
||||||
|
|
||||||
const uint8_t flags = (z80::getAF() & 0xFF);
|
const uint8_t flags = (z80::getAF() & 0xFF);
|
||||||
const uint8_t mod_flags = flags ^ (oAF & 0xFF);
|
const uint8_t mod_flags = flags ^ (oAF & 0xFF);
|
||||||
@@ -399,21 +562,21 @@ namespace z80debug
|
|||||||
|
|
||||||
// STACK
|
// STACK
|
||||||
// ******************************************
|
// ******************************************
|
||||||
|
|
||||||
ui::setoffset(0, 0);
|
ui::setoffset(0, 0);
|
||||||
ui::box(midx,8,11,sym_y-8,COLOR_WHITE);
|
ui::box(midx,8,11,12,COLOR_WHITE);
|
||||||
ui::printrect(midx+2,8, 8,1, COLOR_DARK);
|
ui::printrect(midx+2,8, 8,1, COLOR_DARK);
|
||||||
ui::printtxt(midx+3,8, "STACK:", COLOR_WHITE);
|
ui::printtxt(midx+3,8, "STACK:", COLOR_WHITE);
|
||||||
ui::setoffset(midx+1, 9);
|
ui::setoffset(midx+1, 9);
|
||||||
uint16_t sp = z80::getSP()-(((sym_y-12)>>1)<<1);
|
uint16_t sp = z80::getSP();//-(((sym_y-12)>>1)<<1);
|
||||||
for (int i=0; i<sym_y-10; ++i) {
|
for (int i=0; i<10; ++i) {
|
||||||
uint8_t c1=COLOR_CYAN, c2=COLOR_GRAY;
|
uint8_t c1=COLOR_CYAN, c2=COLOR_GRAY;
|
||||||
if (sp == z80::getSP()) {
|
if (sp == z80::getSP()) {
|
||||||
ui::printrect(0,i,9,1,COLOR_BLUE);
|
ui::printrect(0,i,9,1,COLOR_BLUE);
|
||||||
c1 = c2 = COLOR_YELLOW;
|
c1 = c2 = COLOR_YELLOW;
|
||||||
}
|
}
|
||||||
ui::printtxt(0,i, tohex(sp, 4), c1);
|
ui::printtxt(0,i, tohex(sp, 4), c1);
|
||||||
ui::printtxt(5,i, tohex(*((uint16_t*)&memory[sp]),4), c2);
|
uint16_t value = mem::readMem(sp) + (mem::readMem(sp+1)<<8);
|
||||||
|
ui::printtxt(5,i, tohex(value, 4), c2);
|
||||||
sp+=2;
|
sp+=2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,40 +585,76 @@ namespace z80debug
|
|||||||
// ******************************************
|
// ******************************************
|
||||||
|
|
||||||
ui::setoffset(0, 0);
|
ui::setoffset(0, 0);
|
||||||
ui::box(midx+11,8,14,sym_y-8,COLOR_WHITE);
|
ui::box(midx+11,8,14,12,COLOR_WHITE);
|
||||||
ui::printrect(midx+13,8, 9,1, COLOR_DARK);
|
ui::printrect(midx+13,8, 9,1, COLOR_DARK);
|
||||||
ui::printtxt(midx+14,8, "BREAKS:", COLOR_WHITE);
|
ui::printtxt(midx+14,8, "BREAKS:", COLOR_WHITE);
|
||||||
ui::setoffset(midx+12, 9);
|
ui::setoffset(midx+12, 9);
|
||||||
|
|
||||||
int line=0;
|
int line=0;
|
||||||
|
num_breakpoint_lines=0;
|
||||||
for (int i=0; i<65536; ++i) {
|
for (int i=0; i<65536; ++i) {
|
||||||
if (breakpoints[i]&7) {
|
if (breakpoints[i]&7) {
|
||||||
ui::printtxt(2,line, tohex(i,4), COLOR_WHITE);
|
line_breakpoint_address[line] = i;
|
||||||
ui::printtxt(7,line, breakpoints[i]&1?"x":"-", COLOR_GRAY);
|
num_breakpoint_lines++;
|
||||||
ui::printtxt(8,line, breakpoints[i]&2?"r":"-", COLOR_GRAY);
|
uint8_t colors[2] = {COLOR_WHITE, COLOR_GRAY};
|
||||||
ui::printtxt(9,line, breakpoints[i]&4?"w":"-", COLOR_GRAY);
|
if (i==cursor) {
|
||||||
|
ui::printrect(1,line,10,1,COLOR_BLUE);
|
||||||
|
colors[0]=colors[1]=COLOR_YELLOW;
|
||||||
|
}
|
||||||
|
ui::printtxt(2,line, tohex(i,4), colors[0]);
|
||||||
|
ui::printtxt(7,line, breakpoints[i]&1?"x":"-", colors[1]);
|
||||||
|
ui::printtxt(8,line, breakpoints[i]&2?"r":"-", colors[1]);
|
||||||
|
ui::printtxt(9,line, breakpoints[i]&4?"w":"-", colors[1]);
|
||||||
line++;
|
line++;
|
||||||
if (line>9) break;
|
if (line>9) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// INSPECTOR
|
||||||
|
// ******************************************
|
||||||
|
|
||||||
|
ui::setoffset(0, 0);
|
||||||
|
ui::box(midx,20,25,4,COLOR_WHITE);
|
||||||
|
ui::printrect(midx+2,20,12,1, COLOR_DARK);
|
||||||
|
ui::printtxt(midx+3,20, "INSPECTOR:", COLOR_WHITE);
|
||||||
|
ui::setoffset(midx+1, 21);
|
||||||
|
|
||||||
|
ui::printtxt(1,0,"VALUE:", COLOR_GRAY); ui::printtxt(8,0,"$", COLOR_CYAN);
|
||||||
|
ui::printtxt(9,0,tohex(inspected_value,4), COLOR_CYAN);
|
||||||
|
ui::printtxt(14,0,todec(inspected_value,false), COLOR_WHITE);
|
||||||
|
ui::printtxt(20,0,todec(inspected_value,true), COLOR_WHITE);
|
||||||
|
ui::printtxt(1,1,"H:", COLOR_GRAY); ui::printtxt(4,1,"$", COLOR_CYAN);
|
||||||
|
ui::printtxt(5,1,tohex(inspected_value>>8,2), COLOR_CYAN);
|
||||||
|
ui::printtxt(8,1,todec(inspected_value>>8,false), COLOR_WHITE);
|
||||||
|
ui::printtxt(12,1,"L:", COLOR_GRAY); ui::printtxt(15,1,"$", COLOR_CYAN);
|
||||||
|
ui::printtxt(16,1,tohex(inspected_value&0xff,2), COLOR_CYAN);
|
||||||
|
ui::printtxt(19,1,todec(inspected_value&0xff,false), COLOR_WHITE);
|
||||||
|
|
||||||
// MEMORY
|
// MEMORY
|
||||||
// ******************************************
|
// ******************************************
|
||||||
|
|
||||||
ui::setoffset(0, 0);
|
ui::setoffset(0, 0);
|
||||||
ui::box(0,mem_y,midx,mem_h,COLOR_WHITE);
|
ui::box(0,mem_y,midx,mem_h,COLOR_WHITE);
|
||||||
ui::printrect(2,mem_y, 9,1, COLOR_DARK);
|
ui::printrect(2,mem_y, 11,1, COLOR_DARK);
|
||||||
ui::printtxt(3,mem_y, "MEMORY:", COLOR_WHITE);
|
ui::printtxt(3,mem_y, "MEMORY:", COLOR_WHITE);
|
||||||
|
|
||||||
|
ui::printvoidrect(10,mem_y,3,1,COLOR_WHITE);
|
||||||
|
if (sync_mem_with_cursor) ui::printchar(11,mem_y,'X',COLOR_WHITE);
|
||||||
|
|
||||||
ui::setoffset(1, mem_y+1);
|
ui::setoffset(1, mem_y+1);
|
||||||
|
|
||||||
uint16_t mem_viewer_cursor = mem_viewer_pos;
|
uint16_t mem_viewer_cursor = mem_viewer_pos;
|
||||||
for (int i=0; i<mem_h-2; ++i) {
|
for (int i=0; i<mem_h-2; ++i) {
|
||||||
ui::printtxt(1,i, tohex(mem_viewer_cursor, 4), COLOR_CYAN);
|
ui::printtxt(1,i, tohex(mem_viewer_cursor, 4), COLOR_CYAN);
|
||||||
for (int j=0; j<16; ++j) {
|
for (int j=0; j<16; ++j) {
|
||||||
ui::printtxt(6+j*3, i, tohex(memory[mem_viewer_cursor],2), mem_modified[mem_viewer_cursor] ? COLOR_RED : COLOR_WHITE);
|
|
||||||
ui::printchar(54+j, i, memory[mem_viewer_cursor], mem_modified[mem_viewer_cursor] ? COLOR_BROWN : COLOR_GRAY);
|
const uint8_t tag = mem::getTag(mem_viewer_cursor);
|
||||||
|
const uint32_t color = !(tag & MEMTAG_KNOWN) ? COLOR_GRAY : (tag & MEMTAG_DATA) ? ( (tag & (MEMTAG_CODE|MEMTAG_INST)) ? COLOR_MAGENTA : COLOR_BLUE ) : COLOR_GREEN;
|
||||||
|
ui::printrect(6+j*3,i,2,1,color);
|
||||||
|
ui::printrect(54+j,i,1,1,color);
|
||||||
|
ui::printtxt(6+j*3, i, tohex(mem::readMem(mem_viewer_cursor), 2), ( tag & MEMTAG_MODIFIED) ? COLOR_RED : COLOR_WHITE);
|
||||||
|
ui::printchar(54+j, i, mem::readMem(mem_viewer_cursor), ( tag & MEMTAG_MODIFIED) ? COLOR_BROWN : COLOR_GRAY);
|
||||||
mem_viewer_cursor++;
|
mem_viewer_cursor++;
|
||||||
}
|
}
|
||||||
//printtxt(5,0, "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", COLOR_WHITE);
|
//printtxt(5,0, "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", COLOR_WHITE);
|
||||||
@@ -495,11 +694,18 @@ namespace z80debug
|
|||||||
ui::setoffset(midx+1, sym_y+1);
|
ui::setoffset(midx+1, sym_y+1);
|
||||||
const int num_symbols = z80dis::getNumSymbols();
|
const int num_symbols = z80dis::getNumSymbols();
|
||||||
for (int i=0;i<num_symbols;++i) {
|
for (int i=0;i<num_symbols;++i) {
|
||||||
|
uint8_t colors[2] = {COLOR_GRAY, COLOR_WHITE};
|
||||||
const uint16_t address = z80dis::getSymbolAddress(i);
|
const uint16_t address = z80dis::getSymbolAddress(i);
|
||||||
ui::printtxt(1, i, tohex(address,4), COLOR_GRAY);
|
if (address==cursor) {
|
||||||
ui::printtxt(6, i, z80dis::getSymbol(address), COLOR_WHITE);
|
ui::printrect(0,line,23,1,COLOR_BLUE);
|
||||||
|
colors[0]=colors[1]=COLOR_YELLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui::printtxt(1, i, tohex(address,4), colors[0]);
|
||||||
|
ui::printtxt(6, i, z80dis::getSymbol(address), colors[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!is_debugging) {
|
if (!is_debugging) {
|
||||||
SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
|
SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
|
||||||
SDL_SetRenderDrawColor(ren, 0, 0, 0, 128);
|
SDL_SetRenderDrawColor(ren, 0, 0, 0, 128);
|
||||||
@@ -610,17 +816,25 @@ namespace z80debug
|
|||||||
getcmd();
|
getcmd();
|
||||||
|
|
||||||
if (strcmp(cmd, "s")==0 || strcmp(cmd, "step")==0) {
|
if (strcmp(cmd, "s")==0 || strcmp(cmd, "step")==0) {
|
||||||
z80::step();
|
uint8_t dt = z80::step();
|
||||||
|
|
||||||
|
zx_system::update(dt);
|
||||||
|
//zx_tape::update(dt);
|
||||||
|
//audio::update(dt);
|
||||||
|
//speaker::update(dt);
|
||||||
|
zxscreen::fullrefresh();
|
||||||
|
|
||||||
|
z80analyze::refresh();
|
||||||
} else if (strcmp(cmd, "c")==0 || strcmp(cmd, "cont")==0) {
|
} else if (strcmp(cmd, "c")==0 || strcmp(cmd, "cont")==0) {
|
||||||
z80::step();
|
z80::step();
|
||||||
z80debug::cont();
|
z80debug::cont();
|
||||||
|
z80analyze::refresh();
|
||||||
} else if (strcmp(cmd, "r")==0 || strcmp(cmd, "reset")==0) {
|
} else if (strcmp(cmd, "r")==0 || strcmp(cmd, "reset")==0) {
|
||||||
uint8_t *mem = z80::getMem();
|
z80::reset();
|
||||||
for (int i=0x4000; i<=0xffff; ++i) mem[i]=0;
|
//z80::connect_port(0xfe, 0x0001, zx_ula::port_in, zx_ula::port_out);
|
||||||
z80::reset(mem);
|
history::reset();
|
||||||
z80::connect_port(0xfe, zx_ula::port_in, zx_ula::port_out);
|
|
||||||
//for (int i=0; i<65536; ++i) breakpoints[i]=0;
|
|
||||||
z80debug::refresh();
|
z80debug::refresh();
|
||||||
|
z80analyze::refresh();
|
||||||
} else if (strcmp(cmd, "b")==0 || strcmp(cmd, "break")==0) {
|
} else if (strcmp(cmd, "b")==0 || strcmp(cmd, "break")==0) {
|
||||||
getcmd();
|
getcmd();
|
||||||
if (cmd[0] == 0) { breakpoints[z80::getPC()]=1; return; }
|
if (cmd[0] == 0) { breakpoints[z80::getPC()]=1; return; }
|
||||||
@@ -682,6 +896,7 @@ namespace z80debug
|
|||||||
//strcpy(address, cmd);
|
//strcpy(address, cmd);
|
||||||
//loadngo(filename, address);
|
//loadngo(filename, address);
|
||||||
loadstate(filename);
|
loadstate(filename);
|
||||||
|
z80analyze::refresh();
|
||||||
} else if (strcmp(cmd, "t")==0 || strcmp(cmd, "tape")==0) {
|
} else if (strcmp(cmd, "t")==0 || strcmp(cmd, "tape")==0) {
|
||||||
getcmd();
|
getcmd();
|
||||||
if (strcmp(cmd, "load")==0) {
|
if (strcmp(cmd, "load")==0) {
|
||||||
@@ -692,18 +907,24 @@ namespace z80debug
|
|||||||
} else if (strcmp(cmd, "play")==0) {
|
} else if (strcmp(cmd, "play")==0) {
|
||||||
zx_tape::play();
|
zx_tape::play();
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(cmd, "disk")==0) {
|
||||||
|
getcmd();
|
||||||
|
if (strcmp(cmd, "load")==0) {
|
||||||
|
getcmd();
|
||||||
|
char filename[256];
|
||||||
|
strcpy(filename, cmd);
|
||||||
|
zx_disk::load(filename);
|
||||||
|
}
|
||||||
} else if (strcmp(cmd, "poke")==0) {
|
} else if (strcmp(cmd, "poke")==0) {
|
||||||
getcmd();
|
getcmd();
|
||||||
int address = getnum(cmd);
|
int address = getnum(cmd);
|
||||||
getcmd();
|
getcmd();
|
||||||
int value = getnum(cmd);
|
int value = getnum(cmd);
|
||||||
uint8_t *mem = z80::getMem();
|
mem::writeMem(address, value);
|
||||||
mem[address] = value;
|
|
||||||
} else if (strcmp(cmd, "peek")==0) {
|
} else if (strcmp(cmd, "peek")==0) {
|
||||||
getcmd();
|
getcmd();
|
||||||
int address = getnum(cmd);
|
int address = getnum(cmd);
|
||||||
uint8_t *mem = z80::getMem();
|
int value = mem::readMem(address);
|
||||||
int value = mem[address];
|
|
||||||
char tmp[10];
|
char tmp[10];
|
||||||
sendToConsoleLog(SDL_itoa(value, tmp, 10));
|
sendToConsoleLog(SDL_itoa(value, tmp, 10));
|
||||||
} else if (strcmp(cmd, "reg")==0) {
|
} else if (strcmp(cmd, "reg")==0) {
|
||||||
@@ -716,12 +937,13 @@ namespace z80debug
|
|||||||
else if (strcmp(cmd, "d")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[5] = value; }
|
else if (strcmp(cmd, "d")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[5] = value; }
|
||||||
else if (strcmp(cmd, "l")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[6] = value; }
|
else if (strcmp(cmd, "l")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[6] = value; }
|
||||||
else if (strcmp(cmd, "h")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[7] = value; }
|
else if (strcmp(cmd, "h")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[7] = value; }
|
||||||
|
else if (strcmp(cmd, "pc")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[24] = value&0xff; z80::getRegs()[25] = (value>>8)&0xff; }
|
||||||
else { sendToConsoleLog("Syntax error: invalid register"); return; }
|
else { sendToConsoleLog("Syntax error: invalid register"); return; }
|
||||||
} else if (strcmp(cmd, "g")==0 || strcmp(cmd, "goto")==0) {
|
} else if (strcmp(cmd, "g")==0 || strcmp(cmd, "goto")==0) {
|
||||||
getcmd();
|
getcmd();
|
||||||
int address = getnum(cmd);
|
int address = getnum(cmd);
|
||||||
if (address<0 || address>=65536) { sendToConsoleLog("Illegal address"); return; }
|
if (address<0 || address>=65536) { sendToConsoleLog("Illegal address"); return; }
|
||||||
if (z80::getMemTag(address)!=MEMTAG_INST) address = find_previous_opcode(address);
|
if ( !(mem::getTag(address) & MEMTAG_INST) ) address = find_previous_opcode(address);
|
||||||
z80debug::setcursor(address);
|
z80debug::setcursor(address);
|
||||||
} else if (strcmp(cmd, "sym")==0 || strcmp(cmd, "symbol")==0) {
|
} else if (strcmp(cmd, "sym")==0 || strcmp(cmd, "symbol")==0) {
|
||||||
getcmd();
|
getcmd();
|
||||||
@@ -759,6 +981,37 @@ namespace z80debug
|
|||||||
sendToConsoleLog("Keyup sent for key: ");
|
sendToConsoleLog("Keyup sent for key: ");
|
||||||
sendMoreToConsoleLog(cmd);
|
sendMoreToConsoleLog(cmd);
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(cmd, "opcodes")==0) {
|
||||||
|
getcmd();
|
||||||
|
if (strcmp(cmd, "clear")==0 || strcmp(cmd, "reset")==0) {
|
||||||
|
clearUsedOpcodes();
|
||||||
|
} else if (strcmp(cmd, "mark")==0) {
|
||||||
|
markUsedOpcodes();
|
||||||
|
} else if (strcmp(cmd, "count")==0) {
|
||||||
|
printf("Num opcodes used: %i\n", getNumOpcodesUsed());
|
||||||
|
} else if (strcmp(cmd, "print")==0) {
|
||||||
|
printOpcodesUsed();
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "ignore")==0) {
|
||||||
|
getcmd();
|
||||||
|
const int address = getnum(cmd);
|
||||||
|
mem::setTag(address, mem::getTag(address) | MEMTAG_IGNORE);
|
||||||
|
} else if (strcmp(cmd, "search")==0) {
|
||||||
|
getcmd();
|
||||||
|
if (strcmp(cmd, "next")==0) {
|
||||||
|
search();
|
||||||
|
} else {
|
||||||
|
search(cmd);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "show")==0) {
|
||||||
|
getcmd();
|
||||||
|
if (strcmp(cmd, "analyzer")==0) {
|
||||||
|
z80analyze::show();
|
||||||
|
} else {
|
||||||
|
sendToConsoleLog("Unrecognized window. Usage: Show [analyzer]");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendToConsoleLog("Unrecognized command.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -775,6 +1028,14 @@ namespace z80debug
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t stepout()
|
||||||
|
{
|
||||||
|
z80::setOption(Z80_OPTION_BREAK_ON_RET, true);
|
||||||
|
const uint32_t t = z80::step();
|
||||||
|
cont();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
void setmemmodified(const uint16_t addr)
|
void setmemmodified(const uint16_t addr)
|
||||||
{
|
{
|
||||||
mem_modified[addr] = true;
|
mem_modified[addr] = true;
|
||||||
@@ -782,6 +1043,7 @@ namespace z80debug
|
|||||||
|
|
||||||
void loadngo(const char* filename, const char* addr)
|
void loadngo(const char* filename, const char* addr)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
int address = getnum(addr);
|
int address = getnum(addr);
|
||||||
if (address<0 || address>65536) { sendToConsoleLog("Illegal offset"); return; }
|
if (address<0 || address>65536) { sendToConsoleLog("Illegal offset"); return; }
|
||||||
|
|
||||||
@@ -789,36 +1051,42 @@ namespace z80debug
|
|||||||
fseek(f, 0, SEEK_END);
|
fseek(f, 0, SEEK_END);
|
||||||
int size = ftell(f);
|
int size = ftell(f);
|
||||||
fseek(f, 0, SEEK_SET);
|
fseek(f, 0, SEEK_SET);
|
||||||
uint8_t *memory = z80::getMem();
|
uint32_t memsize;
|
||||||
|
uint8_t *memory = mem::getRawPointer(&memsize);
|
||||||
fread(memory+address, size, 1, f);
|
fread(memory+address, size, 1, f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
z80::setPC(address);
|
z80::setPC(address);
|
||||||
is_debugging = is_paused = false;
|
is_debugging = is_paused = false;
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void savestate(const char *filename)
|
void savestate(const char *filename)
|
||||||
{
|
{
|
||||||
uint8_t *regs = z80::getRegs();
|
uint8_t *regs = z80::getRegs();
|
||||||
uint8_t *memory = z80::getMem();
|
|
||||||
FILE *f = fopen(filename, "wb");
|
FILE *f = fopen(filename, "wb");
|
||||||
fwrite(regs, 31, 1, f);
|
fwrite(regs, 31, 1, f);
|
||||||
fwrite(&memory[0x4000], 0xc000, 1, f);
|
mem::saveState(f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadstate(const char *filename)
|
void loadstate(const char *filename)
|
||||||
{
|
{
|
||||||
uint8_t *regs = z80::getRegs();
|
uint8_t *regs = z80::getRegs();
|
||||||
uint8_t *memory = z80::getMem();
|
|
||||||
FILE *f = fopen(filename, "rb");
|
FILE *f = fopen(filename, "rb");
|
||||||
fread(regs, 31, 1, f);
|
fread(regs, 31, 1, f);
|
||||||
fread(&memory[0x4000], 0xc000, 1, f);
|
mem::loadState(f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
history::store();
|
||||||
|
history::gototop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setcursor(const uint16_t address)
|
void setcursor(const uint16_t address)
|
||||||
{
|
{
|
||||||
cursor = address;
|
//if (!debugging()) return;
|
||||||
|
if ( !(mem::getTag(address) & MEMTAG_INST) )
|
||||||
|
cursor = find_previous_opcode(address);
|
||||||
|
else
|
||||||
|
cursor = address;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cursorfwd()
|
void cursorfwd()
|
||||||
@@ -831,9 +1099,103 @@ namespace z80debug
|
|||||||
cursor = find_previous_opcode(cursor);
|
cursor = find_previous_opcode(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void useOpcode(const uint8_t opcode, const uint8_t base)
|
||||||
|
{
|
||||||
|
if (use[base][opcode]<255) use[base][opcode]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearUsedOpcodes()
|
||||||
|
{
|
||||||
|
for (int i=0; i<7; ++i) {
|
||||||
|
for (int j=0; j<256; ++j) {
|
||||||
|
use[i][j]=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void markUsedOpcodes()
|
||||||
|
{
|
||||||
|
for (int i=0; i<7; ++i) {
|
||||||
|
for (int j=0; j<256; ++j) {
|
||||||
|
if (use[i][j]==1) use[i][j]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int getNumOpcodesUsed()
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
for (int i=0; i<7; ++i) {
|
||||||
|
for (int j=0; j<256; ++j) {
|
||||||
|
if (use[i][j]==1) count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printOpcodesUsed()
|
||||||
|
{
|
||||||
|
for (int i=0; i<7; ++i) {
|
||||||
|
for (int j=0; j<256; ++j) {
|
||||||
|
if (use[i][j]==1) {
|
||||||
|
switch (i) {
|
||||||
|
case 1: printf("ED "); break;
|
||||||
|
case 2: printf("CB "); break;
|
||||||
|
case 3: printf("DD "); break;
|
||||||
|
case 4: printf("DD CB "); break;
|
||||||
|
case 5: printf("FD "); break;
|
||||||
|
case 6: printf("FD CB "); break;
|
||||||
|
}
|
||||||
|
printf("%2X\n",j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t hexchartonum(char chr)
|
||||||
|
{
|
||||||
|
int num = chr-48;
|
||||||
|
if (num<0) return 0;
|
||||||
|
if (num>9) {
|
||||||
|
num-=7;
|
||||||
|
if (num>15) {
|
||||||
|
num-=32;
|
||||||
|
if (num<10 || num>15) return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uint8_t(num);
|
||||||
|
}
|
||||||
|
void search(const char *seq)
|
||||||
|
{
|
||||||
|
if (seq) {
|
||||||
|
search_pos = 0;
|
||||||
|
search_sequence_len=0;
|
||||||
|
while (*seq!=0) search_sequence[search_sequence_len++] = (hexchartonum(*(seq++))<<4) + hexchartonum(*(seq++));
|
||||||
|
}
|
||||||
|
int num_found=0;
|
||||||
|
while (search_pos<65536) {
|
||||||
|
if (search_sequence[num_found] == mem::readMem(search_pos)) {
|
||||||
|
num_found++; search_pos++;
|
||||||
|
if (num_found==search_sequence_len) {
|
||||||
|
mem_viewer_pos=search_pos-search_sequence_len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
num_found=0; search_pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendToConsoleLog("Sequence not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace history
|
namespace history
|
||||||
{
|
{
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
buffer[0]=buffer[1]=0;
|
||||||
|
cursor=pos=top=0;
|
||||||
|
}
|
||||||
|
|
||||||
void store()
|
void store()
|
||||||
{
|
{
|
||||||
buffer[++top] = z80::getPC();
|
buffer[++top] = z80::getPC();
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace z80debug
|
|||||||
|
|
||||||
const bool isbreak(const uint16_t address, const uint8_t type=1);
|
const bool isbreak(const uint16_t address, const uint8_t type=1);
|
||||||
uint32_t next();
|
uint32_t next();
|
||||||
|
uint32_t stepout();
|
||||||
|
|
||||||
void savestate(const char *filename);
|
void savestate(const char *filename);
|
||||||
void loadstate(const char *filename);
|
void loadstate(const char *filename);
|
||||||
@@ -34,8 +35,16 @@ namespace z80debug
|
|||||||
void cursorfwd();
|
void cursorfwd();
|
||||||
void cursorback();
|
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
|
namespace history
|
||||||
{
|
{
|
||||||
|
void reset();
|
||||||
void store();
|
void store();
|
||||||
void gototop();
|
void gototop();
|
||||||
void goforward();
|
void goforward();
|
||||||
|
|||||||
52
z80dis.cpp
52
z80dis.cpp
@@ -1,7 +1,8 @@
|
|||||||
#include "z80dis.h"
|
#include "z80dis.h"
|
||||||
|
#include "z80.h"
|
||||||
|
#include "zx_mem.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "z80.h"
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <bits/stdc++.h>
|
#include <bits/stdc++.h>
|
||||||
|
|
||||||
@@ -68,44 +69,41 @@ namespace z80dis
|
|||||||
|
|
||||||
const char *getBase(const uint16_t pos)
|
const char *getBase(const uint16_t pos)
|
||||||
{
|
{
|
||||||
const uint8_t *memory = &z80::getMem()[pos];
|
if (mem::readMem(pos) == 0xCB) {
|
||||||
|
|
||||||
if (*memory == 0xCB) {
|
|
||||||
opcode_size=2;
|
opcode_size=2;
|
||||||
return cb_opcodes[*(memory+1)];
|
return cb_opcodes[mem::readMem(pos+1)];
|
||||||
} else if (*memory == 0xDD) {
|
} else if (mem::readMem(pos) == 0xDD) {
|
||||||
if (*(memory+1) == 0xCB) {
|
if (mem::readMem(pos+1) == 0xCB) {
|
||||||
opcode_size=4;
|
opcode_size=4;
|
||||||
return ix_bit_opcodes[*(memory+3)];
|
return ix_bit_opcodes[mem::readMem(pos+3)];
|
||||||
} else {
|
} else {
|
||||||
opcode_size=2;
|
opcode_size=2;
|
||||||
return ix_opcodes[*(memory+1)];
|
return ix_opcodes[mem::readMem(pos+1)];
|
||||||
}
|
}
|
||||||
} else if (*memory == 0xED) {
|
} else if (mem::readMem(pos) == 0xED) {
|
||||||
opcode_size=2;
|
opcode_size=2;
|
||||||
return misc_opcodes[(*(memory+1))-64];
|
return misc_opcodes[(mem::readMem(pos+1))-64];
|
||||||
} else if (*memory == 0xFD) {
|
} else if (mem::readMem(pos) == 0xFD) {
|
||||||
if (*(memory+1) == 0xCB) {
|
if (mem::readMem(pos+1) == 0xCB) {
|
||||||
opcode_size=4;
|
opcode_size=4;
|
||||||
return iy_bit_opcodes[*(memory+3)];
|
return iy_bit_opcodes[mem::readMem(pos+3)];
|
||||||
} else {
|
} else {
|
||||||
opcode_size=2;
|
opcode_size=2;
|
||||||
return iy_opcodes[*(memory+1)];
|
return iy_opcodes[mem::readMem(pos+1)];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
opcode_size=1;
|
opcode_size=1;
|
||||||
return base_opcodes[*memory];
|
return base_opcodes[mem::readMem(pos)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void printOpcode(const uint16_t pos)
|
void printOpcode(const uint16_t pos)
|
||||||
{
|
{
|
||||||
const uint8_t *memory = &z80::getMem()[pos];
|
|
||||||
char hex[4];
|
char hex[4];
|
||||||
for (int i=0; i<4;++i)
|
for (int i=0; i<4;++i)
|
||||||
{
|
{
|
||||||
if (opcode_size>i)
|
if (opcode_size>i)
|
||||||
sprintf(hex, "%02x ", *(memory+i));
|
sprintf(hex, "%02x ", mem::readMem(pos+i));
|
||||||
else
|
else
|
||||||
sprintf(hex, " ");
|
sprintf(hex, " ");
|
||||||
strcat(buffer, hex);
|
strcat(buffer, hex);
|
||||||
@@ -114,8 +112,6 @@ namespace z80dis
|
|||||||
|
|
||||||
const char *getAsm(const uint16_t pos)
|
const char *getAsm(const uint16_t pos)
|
||||||
{
|
{
|
||||||
const uint8_t *memory = &z80::getMem()[pos];
|
|
||||||
|
|
||||||
opcode_size = 0;
|
opcode_size = 0;
|
||||||
char base[256];
|
char base[256];
|
||||||
strcpy(buffer, getBase(pos));
|
strcpy(buffer, getBase(pos));
|
||||||
@@ -123,7 +119,9 @@ namespace z80dis
|
|||||||
if (strstr(buffer, "4x"))
|
if (strstr(buffer, "4x"))
|
||||||
{
|
{
|
||||||
opcode_size+=2;
|
opcode_size+=2;
|
||||||
const uint16_t word = *(uint16_t*)(memory+1);
|
const uint8_t memvalue = mem::readMem(pos);
|
||||||
|
const uint16_t address = pos + ((memvalue==0xFD) || (memvalue==0xDD) || (memvalue==0xED)?2:1);
|
||||||
|
const uint16_t word = mem::readMem(address) + (mem::readMem(address+1)<<8);
|
||||||
if (symbols[word][0]!=0) {
|
if (symbols[word][0]!=0) {
|
||||||
char *p = strstr(buffer, "$");
|
char *p = strstr(buffer, "$");
|
||||||
(*p)='%'; p++;
|
(*p)='%'; p++;
|
||||||
@@ -144,7 +142,7 @@ namespace z80dis
|
|||||||
{
|
{
|
||||||
opcode_size = 4;
|
opcode_size = 4;
|
||||||
strcpy(base, buffer);
|
strcpy(base, buffer);
|
||||||
sprintf(buffer, base, *(memory+2), (int8_t)*(memory+3));
|
sprintf(buffer, base, mem::readMem(pos+2), (int8_t)mem::readMem(pos+3));
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +150,7 @@ namespace z80dis
|
|||||||
{
|
{
|
||||||
opcode_size+=1;
|
opcode_size+=1;
|
||||||
strcpy(base, buffer);
|
strcpy(base, buffer);
|
||||||
sprintf(buffer, base, *(memory+1));
|
sprintf(buffer, base, mem::readMem(pos+1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strstr(buffer, "Xx"))
|
if (strstr(buffer, "Xx"))
|
||||||
@@ -163,9 +161,9 @@ namespace z80dis
|
|||||||
|
|
||||||
if (opcode_size>4) {
|
if (opcode_size>4) {
|
||||||
opcode_size=4;
|
opcode_size=4;
|
||||||
sprintf(buffer, base, pos + opcode_size + (int8_t)*(memory+2));
|
sprintf(buffer, base, pos + opcode_size + (int8_t)mem::readMem(pos+2));
|
||||||
} else {
|
} else {
|
||||||
sprintf(buffer, base, pos + opcode_size + (int8_t)*(memory+1));
|
sprintf(buffer, base, pos + opcode_size + (int8_t)mem::readMem(pos+1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,9 +173,9 @@ namespace z80dis
|
|||||||
strcpy(base, buffer);
|
strcpy(base, buffer);
|
||||||
if (opcode_size>4) {
|
if (opcode_size>4) {
|
||||||
opcode_size=4;
|
opcode_size=4;
|
||||||
sprintf(buffer, base, (int8_t)*(memory+2));
|
sprintf(buffer, base, (int8_t)mem::readMem(pos+3));
|
||||||
} else {
|
} else {
|
||||||
sprintf(buffer, base, (int8_t)*(memory+1));
|
sprintf(buffer, base, (int8_t)mem::readMem(pos+2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
159
zx_screen.cpp
159
zx_screen.cpp
@@ -1,11 +1,14 @@
|
|||||||
#include "zx_screen.h"
|
#include "zx_screen.h"
|
||||||
#include "z80.h"
|
#include "z80.h"
|
||||||
|
#include "zx_mem.h"
|
||||||
#include "zx_ula.h"
|
#include "zx_ula.h"
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include "zx_tape.h"
|
#include "zx_tape.h"
|
||||||
#include "ui_window.h"
|
#include "ui_window.h"
|
||||||
#include "z80debug.h"
|
#include "z80debug.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
|
#include "file.h"
|
||||||
|
//#include "zx_128mem.h"
|
||||||
|
|
||||||
namespace zxscreen
|
namespace zxscreen
|
||||||
{
|
{
|
||||||
@@ -18,8 +21,16 @@ namespace zxscreen
|
|||||||
SDL_Texture *tex = nullptr;
|
SDL_Texture *tex = nullptr;
|
||||||
SDL_Texture *uitex = nullptr;
|
SDL_Texture *uitex = nullptr;
|
||||||
|
|
||||||
uint8_t zoom = 1;
|
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 fullscreen = false;
|
||||||
|
bool full_refresh = true;
|
||||||
int fullscreen_scale = 1;
|
int fullscreen_scale = 1;
|
||||||
SDL_Rect dest_rect;
|
SDL_Rect dest_rect;
|
||||||
|
|
||||||
@@ -29,45 +40,63 @@ namespace zxscreen
|
|||||||
uint8_t t_flash = 0;
|
uint8_t t_flash = 0;
|
||||||
bool flash = false;
|
bool flash = false;
|
||||||
|
|
||||||
int pixels_draw = 0;
|
uint32_t pixel_base_addr = 0x4000;
|
||||||
|
uint32_t color_base_addr = 0x5800;
|
||||||
|
|
||||||
uint16_t pixel_addr[69888];
|
uint32_t *pixel_addr = nullptr; //[69888];
|
||||||
uint16_t color_addr[69888];
|
uint32_t *color_addr = nullptr; //[69888];
|
||||||
uint8_t zx_pixels[352*296];
|
uint8_t zx_pixels[352*296];
|
||||||
uint8_t *ptr_pixel = zx_pixels;
|
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()
|
void create_tables()
|
||||||
{
|
{
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
|
|
||||||
uint16_t *ptr_pixel = pixel_addr;
|
if (pixel_addr) free(pixel_addr);
|
||||||
uint16_t *ptr_color = color_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
|
// 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
|
// Upper border
|
||||||
for (int i=0; i<48;++i) {
|
for (int i=0; i<48;++i) {
|
||||||
// hsync
|
// 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
|
//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
|
// scanlines
|
||||||
for (uint8_t y=0; y<192; ++y)
|
for (uint8_t y=0; y<192; ++y)
|
||||||
{
|
{
|
||||||
// hsync
|
// 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
|
// 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
|
// Actual screen
|
||||||
for (uint8_t x=0;x<32;++x)
|
for (uint8_t x=0;x<32;++x)
|
||||||
{
|
{
|
||||||
uint16_t color = 0x5800 + x + (y>>3)*32;
|
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 address = /*0x4000 |*/ (x&0x1f) | ((y&0x7)<<8) | ((y&0x38)<<2) | ((y&0xc0)<<5);
|
||||||
for (int i=7;i>0;i-=2)
|
for (int i=7;i>0;i-=2)
|
||||||
{
|
{
|
||||||
*(ptr_pixel++) = (address & 0x1FFF) | (i << 13);
|
*(ptr_pixel++) = (address & 0x1FFF) | (i << 13);
|
||||||
@@ -77,15 +106,15 @@ namespace zxscreen
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Right border
|
// 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
|
// Lower border
|
||||||
for (int i=0; i<56;++i) {
|
for (int i=0; i<56;++i) {
|
||||||
// hsync
|
// 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
|
//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);
|
||||||
}
|
}
|
||||||
@@ -93,8 +122,16 @@ namespace zxscreen
|
|||||||
bool eventHandler(SDL_Event *e)
|
bool eventHandler(SDL_Event *e)
|
||||||
{
|
{
|
||||||
if (e->type==SDL_WINDOWEVENT) {
|
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) {
|
if (e->window.event==SDL_WINDOWEVENT_CLOSE) {
|
||||||
|
saveWindowConfiguration();
|
||||||
return false;
|
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)) {
|
} else if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) {
|
||||||
redraw();
|
redraw();
|
||||||
}
|
}
|
||||||
@@ -103,6 +140,7 @@ namespace zxscreen
|
|||||||
if (z80debug::paused()) {
|
if (z80debug::paused()) {
|
||||||
if (e->type == SDL_KEYDOWN) {
|
if (e->type == SDL_KEYDOWN) {
|
||||||
if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) {
|
if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) {
|
||||||
|
ui::setDialog(nullptr);
|
||||||
const uint8_t dt = z80::step();
|
const uint8_t dt = z80::step();
|
||||||
z80debug::cont();
|
z80debug::cont();
|
||||||
zxscreen::refresh(dt);
|
zxscreen::refresh(dt);
|
||||||
@@ -127,11 +165,15 @@ namespace zxscreen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (e->type == SDL_MOUSEMOTION) {
|
||||||
|
SDL_ShowCursor(true);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reinit()
|
void reinit()
|
||||||
{
|
{
|
||||||
|
saveWindowConfiguration();
|
||||||
if (win) ui::window::unregisterWindow(SDL_GetWindowID(win));
|
if (win) ui::window::unregisterWindow(SDL_GetWindowID(win));
|
||||||
|
|
||||||
if (tex) SDL_DestroyTexture(tex);
|
if (tex) SDL_DestroyTexture(tex);
|
||||||
@@ -139,8 +181,12 @@ namespace zxscreen
|
|||||||
if (ren) SDL_DestroyRenderer(ren);
|
if (ren) SDL_DestroyRenderer(ren);
|
||||||
if (win) SDL_DestroyWindow(win);
|
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;
|
const int z = fullscreen ? 1 : zoom;
|
||||||
win = SDL_CreateWindow("ZX Spectrum Screen", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 352*z, 296*z, fullscreen?SDL_WINDOW_FULLSCREEN_DESKTOP:SDL_WINDOW_SHOWN);
|
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);
|
ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
|
||||||
tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 352, 296);
|
tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 352, 296);
|
||||||
uitex = ui::createtexture(ren);
|
uitex = ui::createtexture(ren);
|
||||||
@@ -167,9 +213,31 @@ namespace zxscreen
|
|||||||
focus();
|
focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void init()
|
void setBaseAddresses(const uint32_t pixeladdr, const uint32_t coloraddr)
|
||||||
{
|
{
|
||||||
|
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();
|
create_tables();
|
||||||
|
ptr_pixel = zx_pixels;
|
||||||
|
t_screen = t_flash = 0;
|
||||||
|
flash = false;
|
||||||
reinit();
|
reinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,55 +252,58 @@ namespace zxscreen
|
|||||||
|
|
||||||
void refresh(const uint32_t dt)
|
void refresh(const uint32_t dt)
|
||||||
{
|
{
|
||||||
const uint8_t* memory = z80::getMem();
|
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();
|
const uint8_t border_color = zx_ula::get_border_color();
|
||||||
|
|
||||||
for (int i=0;i<dt;++i)
|
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;
|
||||||
*(ptr_pixel++) = border_color;
|
*(ptr_pixel++) = border_color;
|
||||||
//pixels_draw+=2;
|
|
||||||
} else {
|
} else {
|
||||||
uint8_t color = memory[color_addr[t_screen]];
|
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;
|
uint8_t c1 = color&0x7, c2 = (color>>3)&0x7;
|
||||||
if ((color&0x80) && flash) { c1=c2; c2=color&0x7; }
|
if ((color&0x80) && flash) { c1=c2; c2=color&0x7; }
|
||||||
if ((color&0x40)) { c1 |= 0x8; c2 |= 0x8; }
|
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 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;
|
*(ptr_pixel++)=(block&mask) ? c1 : c2;
|
||||||
mask>>=1;
|
mask>>=1;
|
||||||
*(ptr_pixel++)=(block&mask) ? c1 : c2;
|
*(ptr_pixel++)=(block&mask) ? c1 : c2;
|
||||||
}
|
}
|
||||||
pixels_draw+=2;
|
|
||||||
}
|
}
|
||||||
t_screen++;
|
t_screen++;
|
||||||
/*if (pixels_draw>352*296)
|
if (t_screen>=t_states_total) {
|
||||||
{
|
|
||||||
printf("PIXELS OVERFLOW: %i\n", pixels_draw);
|
|
||||||
}*/
|
|
||||||
if (t_screen>=69888) {
|
|
||||||
//printf("PIXELS DRAWN: %i\n", pixels_draw);
|
|
||||||
pixels_draw=0;
|
|
||||||
t_flash++;
|
t_flash++;
|
||||||
if (t_flash==16) { t_flash=0; flash = !flash; }
|
if (t_flash==16) { t_flash=0; flash = !flash; }
|
||||||
t_screen=0;
|
t_screen=0;
|
||||||
ptr_pixel = zx_pixels;
|
ptr_pixel = zx_pixels;
|
||||||
redraw();
|
redraw();
|
||||||
//while (SDL_GetTicks()-time < 20) {}
|
if (interrupt_enabled) z80::interrupt();
|
||||||
//time = SDL_GetTicks();
|
|
||||||
z80::interrupt();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fullrefresh()
|
void fullrefresh()
|
||||||
{
|
{
|
||||||
|
uint32_t tmp = t_screen;
|
||||||
t_screen = 0;
|
t_screen = 0;
|
||||||
refresh(69888);
|
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)
|
void redraw(const bool present)
|
||||||
@@ -256,6 +327,9 @@ namespace zxscreen
|
|||||||
// Pintem la textura a pantalla
|
// Pintem la textura a pantalla
|
||||||
SDL_RenderCopy(ren, tex, NULL, &dest_rect);
|
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)
|
if (present)
|
||||||
SDL_RenderPresent(ren);
|
SDL_RenderPresent(ren);
|
||||||
else
|
else
|
||||||
@@ -316,6 +390,17 @@ namespace zxscreen
|
|||||||
return fullscreen;
|
return fullscreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void toggleFullRefresh()
|
||||||
|
{
|
||||||
|
full_refresh = !full_refresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool getFullRefresh()
|
||||||
|
{
|
||||||
|
return full_refresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SDL_Renderer *getrenderer()
|
SDL_Renderer *getrenderer()
|
||||||
{
|
{
|
||||||
return ren;
|
return ren;
|
||||||
|
|||||||
10
zx_screen.h
10
zx_screen.h
@@ -1,13 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#define SCREEN_MODE_48K 0
|
||||||
|
#define SCREEN_MODE_128K 1
|
||||||
|
|
||||||
namespace zxscreen
|
namespace zxscreen
|
||||||
{
|
{
|
||||||
void init();
|
void init(int mode);
|
||||||
|
void setBaseAddresses(const uint32_t pixeladdr, const uint32_t coloraddr);
|
||||||
void reinit();
|
void reinit();
|
||||||
void focus();
|
void focus();
|
||||||
void refresh(const uint32_t dt);
|
void refresh(const uint32_t dt);
|
||||||
void fullrefresh();
|
void fullrefresh();
|
||||||
|
void debugrefresh();
|
||||||
void redraw(const bool present=true);
|
void redraw(const bool present=true);
|
||||||
void present();
|
void present();
|
||||||
void setTitle(const char* title);
|
void setTitle(const char* title);
|
||||||
@@ -17,5 +22,8 @@ namespace zxscreen
|
|||||||
void toggleFullscreen();
|
void toggleFullscreen();
|
||||||
const bool getFullscreen();
|
const bool getFullscreen();
|
||||||
|
|
||||||
|
void toggleFullRefresh();
|
||||||
|
const bool getFullRefresh();
|
||||||
|
|
||||||
SDL_Renderer *getrenderer();
|
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);
|
||||||
|
}
|
||||||
29
zx_tape.cpp
29
zx_tape.cpp
@@ -1,7 +1,9 @@
|
|||||||
#include "zx_tape.h"
|
#include "zx_tape.h"
|
||||||
#include "zx_ula.h"
|
#include "zx_ula.h"
|
||||||
#include "zx_screen.h"
|
#include "zx_screen.h"
|
||||||
|
#include "zx_mem.h"
|
||||||
#include "z80debug.h"
|
#include "z80debug.h"
|
||||||
|
#include "z80.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -93,7 +95,7 @@ namespace zx_tape
|
|||||||
pulse_level = 1;
|
pulse_level = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(const uint8_t dt)
|
void update(const uint32_t dt)
|
||||||
{
|
{
|
||||||
if (!playing) return;
|
if (!playing) return;
|
||||||
|
|
||||||
@@ -201,7 +203,7 @@ namespace zx_tape
|
|||||||
pulse_pos -= PULSE_LEN_SYNC3;
|
pulse_pos -= PULSE_LEN_SYNC3;
|
||||||
pulse_level = 0;
|
pulse_level = 0;
|
||||||
current_section = PULSE_WAIT;
|
current_section = PULSE_WAIT;
|
||||||
printf("going to pulse_wait..%i.\n", current_block);
|
printf("going to pulse_wait..%i (%i).\n", current_block, blocks[current_block].length);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -209,7 +211,7 @@ namespace zx_tape
|
|||||||
if (current_section == PULSE_WAIT)
|
if (current_section == PULSE_WAIT)
|
||||||
{
|
{
|
||||||
pulse_level = 0;
|
pulse_level = 0;
|
||||||
if (pulse_pos >= 3500000)
|
if (pulse_pos >= z80::getClock())
|
||||||
{
|
{
|
||||||
pulse_pos = 0;
|
pulse_pos = 0;
|
||||||
current_section = PULSE_PILOT;
|
current_section = PULSE_PILOT;
|
||||||
@@ -243,6 +245,27 @@ namespace zx_tape
|
|||||||
printf("tape loading: %i%\n", percent);
|
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)
|
const bool getOption(const int option)
|
||||||
{
|
{
|
||||||
return options[option];
|
return options[option];
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ namespace zx_tape
|
|||||||
void play();
|
void play();
|
||||||
void stop();
|
void stop();
|
||||||
void rewind();
|
void rewind();
|
||||||
void update(const uint8_t dt);
|
void update(const uint32_t dt);
|
||||||
const bool getplaying();
|
const bool getplaying();
|
||||||
void report();
|
void report();
|
||||||
|
uint16_t fastLoad(const uint8_t block_type, const uint16_t address, const uint16_t length);
|
||||||
|
|
||||||
const bool getOption(const int option);
|
const bool getOption(const int option);
|
||||||
void setOption(const int option, const bool value);
|
void setOption(const int option, const bool value);
|
||||||
|
|||||||
111
zx_ula.cpp
111
zx_ula.cpp
@@ -1,8 +1,7 @@
|
|||||||
#include "zx_ula.h"
|
#include "zx_ula.h"
|
||||||
|
#include "z80.h"
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#define AUDIO_BUFFER_SIZE 2048
|
|
||||||
|
|
||||||
namespace zx_ula
|
namespace zx_ula
|
||||||
{
|
{
|
||||||
#define KEY_SHIFT 0
|
#define KEY_SHIFT 0
|
||||||
@@ -55,16 +54,20 @@ namespace zx_ula
|
|||||||
|
|
||||||
uint8_t zx_keyboard[40];
|
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};
|
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'};
|
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 border_color = 0;
|
||||||
static uint8_t ear = 0;
|
static uint8_t ear = 0;
|
||||||
static uint8_t mic = 0;
|
static uint8_t mic = 0;
|
||||||
|
|
||||||
// this variable keeps track of the last "1" written to the sound buffer, so when streaming it in the audio callback, if there
|
|
||||||
// are only zeros remaining in the buffer, we just discard it. It's a poor man's method of keeping the buffer size in control.
|
|
||||||
// This is useless when there are music, sounds, tape loading, etc... as 1's and 0's keep alternating
|
|
||||||
static uint16_t last_1 = 0;
|
|
||||||
|
|
||||||
void update_zx_keyboard()
|
void update_zx_keyboard()
|
||||||
{
|
{
|
||||||
const uint8_t *keys = SDL_GetKeyboardState(NULL);
|
const uint8_t *keys = SDL_GetKeyboardState(NULL);
|
||||||
@@ -121,6 +124,12 @@ namespace zx_ula
|
|||||||
// Keys in a normal keyboard that ara combinations in the zx one
|
// 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_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_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)
|
int port_in(int port)
|
||||||
@@ -214,93 +223,13 @@ namespace zx_ula
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t get_border_color() { return border_color; }
|
uint8_t get_border_color() { return border_color; }
|
||||||
|
void set_border_color(uint8_t col) { border_color = col; }
|
||||||
uint8_t get_ear() { return ear; }
|
uint8_t get_ear() { return ear; }
|
||||||
void set_ear(const uint8_t val) { ear = val; }
|
void set_ear(const uint8_t val) { ear = val; }
|
||||||
|
|
||||||
|
uint8_t get_sample()
|
||||||
|
|
||||||
|
|
||||||
SDL_AudioDeviceID sdlAudioDevice;
|
|
||||||
uint8_t sound_buffer[AUDIO_BUFFER_SIZE];
|
|
||||||
uint16_t sound_pos=0;
|
|
||||||
uint16_t sound_start=0;
|
|
||||||
uint16_t t_sound=0;
|
|
||||||
uint32_t samples_generated=0;
|
|
||||||
uint32_t samples_time =0;
|
|
||||||
uint32_t samples_t=0;
|
|
||||||
|
|
||||||
void audioCallback(void * userdata, uint8_t * stream, int len)
|
|
||||||
{
|
{
|
||||||
//const uint16_t top = sound_pos < len ? sound_pos : len;
|
return ear*128;
|
||||||
//if (top<len) printf("top: %i, len: %i, pos: %i\n", top, len, sound_pos);
|
|
||||||
//memcpy(stream, sound_buffer, top);
|
|
||||||
for (int i=0;i<len;++i)
|
|
||||||
{
|
|
||||||
stream[i] = sound_buffer[(sound_start++)&(AUDIO_BUFFER_SIZE-1)];
|
|
||||||
}
|
|
||||||
//if (top<len) memchr(&stream[top], sound_buffer[top-1], len-top);
|
|
||||||
/*if (top<sound_pos)
|
|
||||||
{
|
|
||||||
if (last_1>top)
|
|
||||||
{
|
|
||||||
memcpy(sound_buffer, &sound_buffer[top], sound_pos-top);
|
|
||||||
sound_pos=sound_pos-top;
|
|
||||||
last_1=last_1-top;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sound_pos=last_1=0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sound_pos=last_1=0;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void sound_init()
|
|
||||||
{
|
|
||||||
SDL_AudioSpec audioSpec{11025, AUDIO_U8, 1, 0, AUDIO_BUFFER_SIZE>>2, 0, 0, &audioCallback, NULL};
|
|
||||||
sdlAudioDevice = SDL_OpenAudioDevice(NULL, 0, &audioSpec, NULL, 0);
|
|
||||||
sound_enable();
|
|
||||||
samples_time=SDL_GetTicks();
|
|
||||||
}
|
|
||||||
|
|
||||||
void sound_enable()
|
|
||||||
{
|
|
||||||
SDL_PauseAudioDevice(sdlAudioDevice, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sound_disable()
|
|
||||||
{
|
|
||||||
SDL_PauseAudioDevice(sdlAudioDevice, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sound_update(const uint8_t dt)
|
|
||||||
{
|
|
||||||
t_sound += dt;
|
|
||||||
samples_t += dt;
|
|
||||||
if (t_sound>=317) {
|
|
||||||
t_sound-=317;
|
|
||||||
samples_generated++;
|
|
||||||
if (samples_t >=3500000) {
|
|
||||||
//printf("%i\n", samples_generated);
|
|
||||||
samples_generated=0;
|
|
||||||
samples_t = 0;
|
|
||||||
}
|
|
||||||
/*if (SDL_GetTicks()>=samples_time+1000) {
|
|
||||||
printf("%i\n", samples_generated);
|
|
||||||
samples_generated=0;
|
|
||||||
samples_time = SDL_GetTicks();
|
|
||||||
}*/
|
|
||||||
//sound_pos = (sound_pos+1) & 0x3ff;
|
|
||||||
//sound_buffer[sound_pos] = ear*128;
|
|
||||||
|
|
||||||
//if (sound_pos>=AUDIO_BUFFER_SIZE) sound_pos = last_1 = 0;
|
|
||||||
|
|
||||||
sound_buffer[(sound_pos++)&(AUDIO_BUFFER_SIZE-1)] = ear*128;
|
|
||||||
//if (ear) last_1 = sound_pos;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const int getKey(const char key)
|
const int getKey(const char key)
|
||||||
|
|||||||
6
zx_ula.h
6
zx_ula.h
@@ -8,13 +8,11 @@ namespace zx_ula
|
|||||||
void port_out(int port, int val);
|
void port_out(int port, int val);
|
||||||
|
|
||||||
uint8_t get_border_color();
|
uint8_t get_border_color();
|
||||||
|
void set_border_color(uint8_t col);
|
||||||
uint8_t get_ear();
|
uint8_t get_ear();
|
||||||
void set_ear(const uint8_t val);
|
void set_ear(const uint8_t val);
|
||||||
|
|
||||||
void sound_init();
|
uint8_t get_sample();
|
||||||
void sound_enable();
|
|
||||||
void sound_disable();
|
|
||||||
void sound_update(const uint8_t dt);
|
|
||||||
|
|
||||||
void keydown(const char key);
|
void keydown(const char key);
|
||||||
void keyup(const char key);
|
void keyup(const char key);
|
||||||
|
|||||||
Reference in New Issue
Block a user