Compare commits
44 Commits
620cd8d88c
...
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 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
z80
|
||||
.vscode/*
|
||||
build/*
|
||||
BIN
ROBOCOP1.TAP
Normal file
BIN
ROBOCOP1.TAP
Normal file
Binary file not shown.
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
|
||||
172
main.cpp
172
main.cpp
@@ -1,9 +1,11 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "zx_system.h"
|
||||
#include "z80.h"
|
||||
#include "z80dis.h"
|
||||
#include "z80debug.h"
|
||||
#include "zx_ula.h"
|
||||
#include "zx_speaker.h"
|
||||
#include "zx_screen.h"
|
||||
#include "zx_tape.h"
|
||||
#include <SDL2/SDL.h>
|
||||
@@ -12,8 +14,16 @@
|
||||
#include "ui_menu.h"
|
||||
#include "z80analyze.h"
|
||||
#include "ui_window.h"
|
||||
#include "zx_mem.h"
|
||||
#include "z80viewer.h"
|
||||
//#include "zx_128bankviewer.h"
|
||||
//#include "zx_128pageviewer.h"
|
||||
#include "ay-3-8912.h"
|
||||
#include "ay_viewer.h"
|
||||
#include "file.h"
|
||||
#include "gamepad.h"
|
||||
#include "zx_dialog_joystick.h"
|
||||
|
||||
uint8_t memory[65536];
|
||||
uint32_t time = 0;
|
||||
uint32_t t_states = 0;
|
||||
|
||||
@@ -26,6 +36,11 @@ namespace actions
|
||||
zxscreen::refresh(dt);
|
||||
}
|
||||
|
||||
void exitButNotContinue()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
int fastload(int value)
|
||||
{
|
||||
zx_tape::toggleOption(ZXTAPE_OPTION_FAST_LOAD);
|
||||
@@ -67,22 +82,47 @@ namespace actions
|
||||
z80analyze::show();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mode48K(int value)
|
||||
{
|
||||
zx_system::reset(ZX_48K);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mode128K(int value)
|
||||
{
|
||||
zx_system::reset(ZX_128K);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int modePlus3(int value)
|
||||
{
|
||||
zx_system::reset(ZX_PLUS3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int reset(int value)
|
||||
{
|
||||
z80::reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exit(int value)
|
||||
{
|
||||
zx_system::shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int configureJoysticks(int value)
|
||||
{
|
||||
ui::setDialog(dialogs::joysticks::show);
|
||||
ui::menu::exitButNotContinue();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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::setexitcallback(actions::exitMenu);
|
||||
|
||||
@@ -92,9 +132,18 @@ int main(int argc, char *argv[])
|
||||
ui::menu::addseparator(menu);
|
||||
ui::menu::addoption(menu, "LOAD STATE", nullptr);
|
||||
ui::menu::addoption(menu, "SAVE STATE", nullptr);
|
||||
ui::menu::addseparator(menu);
|
||||
ui::menu::addoption(menu, "EXIT", actions::exit);
|
||||
|
||||
menu = ui::menu::addsubmenu("SYSTEM");
|
||||
ui::menu::addoption(menu, "ZX 48K", actions::mode48K);
|
||||
ui::menu::addoption(menu, "ZX 128K/+2", actions::mode128K);
|
||||
ui::menu::addoption(menu, "ZX +2A/+3", actions::modePlus3);
|
||||
ui::menu::addseparator(menu);
|
||||
ui::menu::addoption(menu, "RESET", actions::reset);
|
||||
|
||||
menu = ui::menu::addsubmenu("TAPE");
|
||||
ui::menu::addbooloption(menu, "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);
|
||||
|
||||
menu = ui::menu::addsubmenu("SCREEN");
|
||||
@@ -107,26 +156,54 @@ int main(int argc, char *argv[])
|
||||
menu = ui::menu::addsubmenu("EMULATION");
|
||||
ui::menu::addbooloption(menu, "STOP ON INVALID OP", z80::getOption(Z80_OPTION_STOP_ON_INVALID), actions::decZoom);
|
||||
ui::menu::addoption(menu, "SHOW ANALYZER", actions::showAnalyzer);
|
||||
ui::menu::addoption(menu, "CONFIGURE JOYSTICKS", actions::configureJoysticks);
|
||||
|
||||
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;
|
||||
|
||||
time = SDL_GetTicks();
|
||||
t_states = 0;
|
||||
bool first_time = true;
|
||||
|
||||
while (!should_exit)
|
||||
while (!zx_system::shuttingDown())
|
||||
{
|
||||
while (SDL_PollEvent(&e))
|
||||
{
|
||||
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_MOUSEBUTTONUP) result = ui::window::sendEvent(e.button.windowID, &e);
|
||||
if (e.type == SDL_MOUSEMOTION) result = ui::window::sendEvent(e.motion.windowID, &e);
|
||||
@@ -148,6 +225,7 @@ int main(int argc, char *argv[])
|
||||
} else {
|
||||
z80debug::show();
|
||||
}
|
||||
z80viewer::refreshAll();
|
||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_F10) {
|
||||
if (z80debug::debugging()) {
|
||||
z80debug::show();
|
||||
@@ -157,6 +235,7 @@ int main(int argc, char *argv[])
|
||||
zxscreen::fullrefresh();
|
||||
zxscreen::redraw();
|
||||
z80analyze::refresh();
|
||||
z80viewer::refreshAll();
|
||||
}
|
||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_F11) {
|
||||
if (z80debug::debugging()) {
|
||||
@@ -167,6 +246,7 @@ int main(int argc, char *argv[])
|
||||
zxscreen::refresh(dt);
|
||||
zxscreen::redraw();
|
||||
z80analyze::refresh();
|
||||
z80viewer::refreshAll();
|
||||
}
|
||||
} else if (e.key.keysym.scancode==SDL_SCANCODE_F12) {
|
||||
if (z80debug::debugging()) {
|
||||
@@ -177,6 +257,7 @@ int main(int argc, char *argv[])
|
||||
zxscreen::refresh(dt);
|
||||
zxscreen::redraw();
|
||||
z80analyze::refresh();
|
||||
z80viewer::refreshAll();
|
||||
}
|
||||
}
|
||||
result = ui::window::sendEvent(e.key.windowID, &e);
|
||||
@@ -185,28 +266,13 @@ int main(int argc, char *argv[])
|
||||
if (e.type == SDL_MOUSEBUTTONUP && e.button.button==1) ui::setClicked(true);
|
||||
|
||||
if (!result)
|
||||
should_exit = true; break;
|
||||
zx_system::shutdown(); break;
|
||||
}
|
||||
|
||||
first_time = false;
|
||||
if (!z80debug::debugging() && !z80debug::paused()) {
|
||||
//if (z80::getPC()==0x05C8) zx_tape::go_berserk();
|
||||
bool fastload=false;
|
||||
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
|
||||
|
||||
// En cada bucle fem 10 pasos de la CPU, sino s'ofega (jo en veig 5)
|
||||
for (int i=0;i<5;++i) {
|
||||
if (z80debug::isbreak(z80::getPC(), 9)) {
|
||||
z80debug::stop();
|
||||
@@ -215,32 +281,34 @@ int main(int argc, char *argv[])
|
||||
} else {
|
||||
uint8_t dt = z80::step();
|
||||
t_states += dt;
|
||||
zx_tape::update(dt);
|
||||
|
||||
zx_ula::sound_update(dt);
|
||||
zx_system::update(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)
|
||||
//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);
|
||||
// Esperem a que es compleixca el temps corresponent als t states executats
|
||||
while (SDL_GetTicks()<time+100) {}
|
||||
t_states -= 350000;
|
||||
|
||||
t_states -= update_freq;
|
||||
time = SDL_GetTicks();
|
||||
z80analyze::refresh();
|
||||
z80viewer::refreshAll();
|
||||
}
|
||||
z80analyze::refresh(true);
|
||||
|
||||
|
||||
} else if (!z80debug::debugging() && z80debug::paused()) {
|
||||
} else if (z80debug::paused()) {
|
||||
zxscreen::redraw(false);
|
||||
|
||||
if (ui::hasDialog())
|
||||
ui::callDialog();
|
||||
else
|
||||
ui::menu::show();
|
||||
|
||||
zxscreen::present();
|
||||
}
|
||||
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
|
||||
14
symbols.txt
14
symbols.txt
@@ -1,13 +1 @@
|
||||
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
|
||||
0x6580 START
|
||||
|
||||
72
ui.cpp
72
ui.cpp
@@ -30,6 +30,8 @@ namespace ui
|
||||
|
||||
bool clicked = false;
|
||||
|
||||
void (*dialog)(void) = nullptr;
|
||||
|
||||
SDL_Texture * createtexture(SDL_Renderer *renderer)
|
||||
{
|
||||
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP("font.bmp"));
|
||||
@@ -39,7 +41,6 @@ namespace ui
|
||||
|
||||
void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture)
|
||||
{
|
||||
if (ren==renderer) return;
|
||||
ren = renderer;
|
||||
tex = texture;
|
||||
offset_x = offset_y = 0;
|
||||
@@ -51,6 +52,43 @@ namespace ui
|
||||
offset_y = y;
|
||||
}
|
||||
|
||||
void incoffset(uint8_t x, uint8_t y)
|
||||
{
|
||||
offset_x += x;
|
||||
offset_y += y;
|
||||
}
|
||||
|
||||
uint8_t getOffsetX()
|
||||
{
|
||||
return offset_x;
|
||||
}
|
||||
|
||||
uint8_t getOffsetY()
|
||||
{
|
||||
return offset_y;
|
||||
}
|
||||
|
||||
bool mouseInside(int x, int y, int w, int h)
|
||||
{
|
||||
int mx, my;
|
||||
Uint32 mb = SDL_GetMouseState(&mx, &my);
|
||||
mx=mx/CHR_W; my=my/CHR_H;
|
||||
mx -= offset_x;
|
||||
my -= offset_y;
|
||||
return (mx >= x) && (mx < x+w) && (my >= y) && (my < y+h);
|
||||
}
|
||||
|
||||
void panel(int x, int y, int w, int h, const char *title)
|
||||
{
|
||||
ui::setoffset(x, y);
|
||||
|
||||
ui::box(0,0,w,h,COLOR_WHITE);
|
||||
ui::printrect(2,0, strlen(title)+2,1, COLOR_DARK);
|
||||
ui::printtxt(3,0, title, COLOR_WHITE);
|
||||
|
||||
ui::incoffset(1, 1);
|
||||
}
|
||||
|
||||
void box(int x, int y, int w, int h, uint8_t color)
|
||||
{
|
||||
SDL_Rect rect {((offset_x+x)*CHR_W)+3, ((offset_y+y)*CHR_H)+6, w*CHR_W-6, h*CHR_H-13};
|
||||
@@ -88,6 +126,22 @@ namespace ui
|
||||
for (int i=0; i<strlen(text);++i) if (text[i]!=32) printchar(x+i, y, text[i]);
|
||||
}
|
||||
|
||||
void placechar(int x, int y, char chr, uint8_t color)
|
||||
{
|
||||
if (color != 255) SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
|
||||
if (chr==32) return;
|
||||
if (chr<32 || chr>127) chr = '.';
|
||||
SDL_Rect src {((chr-32)&0xf)*CHR_W, ((chr-32)>>4)*CHR_H, CHR_W, CHR_H};
|
||||
SDL_Rect dst {x, y, CHR_W*2, CHR_H*2};
|
||||
SDL_RenderCopy(ren, tex, &src, &dst);
|
||||
}
|
||||
|
||||
void placetxt(int x, int y, const char *text, uint8_t color)
|
||||
{
|
||||
SDL_SetTextureColorMod(tex, colors[color][0], colors[color][1], colors[color][2]);
|
||||
for (int i=0; i<strlen(text);++i) if (text[i]!=32) placechar(x+i*CHR_W*2, y, text[i]);
|
||||
}
|
||||
|
||||
void setClicked(const bool value)
|
||||
{
|
||||
clicked = value;
|
||||
@@ -98,4 +152,20 @@ namespace ui
|
||||
return clicked;
|
||||
}
|
||||
|
||||
void setDialog(void(*new_dialog)(void))
|
||||
{
|
||||
ui:dialog = new_dialog;
|
||||
}
|
||||
|
||||
bool hasDialog()
|
||||
{
|
||||
return dialog != nullptr;
|
||||
}
|
||||
|
||||
void callDialog()
|
||||
{
|
||||
if (dialog) dialog();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
12
ui.h
12
ui.h
@@ -26,13 +26,25 @@ namespace ui
|
||||
SDL_Texture * createtexture(SDL_Renderer *renderer);
|
||||
void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture);
|
||||
void setoffset(uint8_t x, uint8_t y);
|
||||
void incoffset(uint8_t x, uint8_t y);
|
||||
uint8_t getOffsetX();
|
||||
uint8_t getOffsetY();
|
||||
bool mouseInside(int x, int y, int w, int h);
|
||||
|
||||
void panel(int x, int y, int w, int h, const char *title);
|
||||
void box(int x, int y, int w, int h, uint8_t color);
|
||||
void printrect(int x, int y, int w, int h, uint8_t color);
|
||||
void printvoidrect(int x, int y, int w, int h, uint8_t color);
|
||||
void printchar(int x, int y, char chr, uint8_t color=255);
|
||||
void printtxt(int x, int y, const char *text, uint8_t color);
|
||||
|
||||
void placechar(int x, int y, char chr, uint8_t color=255);
|
||||
void placetxt(int x, int y, const char *text, uint8_t color);
|
||||
|
||||
void setClicked(const bool value);
|
||||
const bool getClicked();
|
||||
|
||||
void setDialog(void(*new_dialog)(void));
|
||||
bool hasDialog();
|
||||
void callDialog();
|
||||
}
|
||||
|
||||
20
ui_menu.cpp
20
ui_menu.cpp
@@ -31,6 +31,7 @@ namespace ui
|
||||
std::vector<menu_t> menus;
|
||||
int visible_menu = -1;
|
||||
int menu_x = 0;
|
||||
bool do_not_exit = false;
|
||||
|
||||
void init()
|
||||
{
|
||||
@@ -39,6 +40,7 @@ namespace ui
|
||||
|
||||
void show()
|
||||
{
|
||||
ui::setoffset(0,0);
|
||||
int mx, my;
|
||||
Uint32 mb = SDL_GetMouseState(&mx, &my);
|
||||
mx=mx/CHR_W; my=my/CHR_H;
|
||||
@@ -52,8 +54,8 @@ namespace ui
|
||||
uint8_t text_color = COLOR_WHITE;
|
||||
if (my<1 && mx>=opt_pos && mx<opt_pos+text_size)
|
||||
{
|
||||
ui::printrect(opt_pos-1, 0, text_size, 1, COLOR_DARK_BLUE);
|
||||
text_color = COLOR_WHITE;
|
||||
ui::printrect(opt_pos-1, 0, text_size, 1, COLOR_WHITE);
|
||||
text_color = COLOR_BLACK;
|
||||
visible_menu = index;
|
||||
menu_x = opt_pos-1;
|
||||
}
|
||||
@@ -73,14 +75,15 @@ namespace ui
|
||||
{
|
||||
const int text_size = (option.label.size()+2);
|
||||
uint8_t text_color = COLOR_WHITE;
|
||||
if (my==opt_pos && mx>=menu_x && mx<menu_x+text_size)
|
||||
if (my==opt_pos && mx>=menu_x && mx<menu_x+20)
|
||||
{
|
||||
ui::printrect(menu_x+1, opt_pos, 20, 1, COLOR_DARK_BLUE);
|
||||
text_color = COLOR_WHITE;
|
||||
ui::printrect(menu_x+1, opt_pos, 20, 1, COLOR_WHITE);
|
||||
text_color = COLOR_BLACK;
|
||||
if (ui::getClicked())
|
||||
{
|
||||
if (option.callback) option.value = option.callback(option.value);
|
||||
if (exit_callback) exit_callback();
|
||||
if (exit_callback && !do_not_exit) exit_callback();
|
||||
do_not_exit = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -102,6 +105,11 @@ namespace ui
|
||||
exit_callback = callback;
|
||||
}
|
||||
|
||||
void exitButNotContinue()
|
||||
{
|
||||
do_not_exit = true;
|
||||
}
|
||||
|
||||
const int addsubmenu(const char *label)
|
||||
{
|
||||
menu_t m;
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace ui
|
||||
void init();
|
||||
void show();
|
||||
void setexitcallback(void(*callback)(void));
|
||||
void exitButNotContinue();
|
||||
const int addsubmenu(const char *label);
|
||||
void addoption(int menu, const char *label, int(*callback)(int));
|
||||
void addbooloption(int menu, const char *label, bool default_value, int(*callback)(int));
|
||||
|
||||
372
z80.cpp
372
z80.cpp
@@ -1,23 +1,29 @@
|
||||
#include "z80.h"
|
||||
#include "z80debug.h"
|
||||
|
||||
#include "zx_mem.h"
|
||||
#include "zx_tape.h"
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
namespace z80
|
||||
{
|
||||
static uint8_t *memory = nullptr;
|
||||
static uint8_t memtag[65536];
|
||||
static uint8_t memtouched[65536];
|
||||
struct port_t
|
||||
{
|
||||
uint16_t value;
|
||||
uint16_t mask;
|
||||
int (*in)(int);
|
||||
void (*out)(int, int);
|
||||
};
|
||||
|
||||
static uint32_t clock = 3500000;
|
||||
static uint32_t t = 0;
|
||||
static uint16_t current_opcode_address = 0;
|
||||
bool options[Z80_NUM_OPTIONS] = { true, false };
|
||||
int calls_stacked = 0;
|
||||
|
||||
int (*in_ports[256])(int);
|
||||
void (*out_ports[256])(int, int);
|
||||
|
||||
#define _rM16(a) (uint16_t*)&memory[a]
|
||||
#define rM16(a) *_rM16(a)
|
||||
//int (*in_ports[256])(int);
|
||||
//void (*out_ports[256])(int, int);
|
||||
std::vector<port_t> ports;
|
||||
|
||||
#define fC 0b00000001
|
||||
#define fN 0b00000010
|
||||
@@ -151,34 +157,31 @@ namespace z80
|
||||
{
|
||||
if (z80debug::isbreak(addr, 2)) z80debug::stop();
|
||||
t+=3;
|
||||
if (memtag[addr] != MEMTAG_IGNORE) {
|
||||
const uint8_t tag = mem::getTag(addr);
|
||||
if ( !(tag&MEMTAG_IGNORE) ) {
|
||||
if (!code) {
|
||||
if ( memtag[addr] == MEMTAG_INST ) {
|
||||
if ( tag & MEMTAG_INST ) {
|
||||
//printf("WARNING! READING DATA FROM CODE!!! $%4X\n", addr);
|
||||
//z80debug::stop();
|
||||
} else {
|
||||
memtag[addr] = MEMTAG_DATA;
|
||||
mem::setTag(addr, tag | MEMTAG_DATA);
|
||||
}
|
||||
} else {
|
||||
if ( (reading_m1) && (memtag[addr] == MEMTAG_DATA) ) {
|
||||
if ( (reading_m1) && ( tag & MEMTAG_DATA ) ) {
|
||||
//printf("WARNING! EXECUTING DATA AS CODE!!! $%4X\n", addr);
|
||||
//z80debug::stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
reading_m1 = false;
|
||||
return memory[addr];
|
||||
return mem::readMem(addr);
|
||||
}
|
||||
|
||||
uint8_t READ_MEM_8()
|
||||
{
|
||||
uint8_t data = READ_MEM_8(rPC, true);
|
||||
if ( (memtag[rPC] != MEMTAG_IGNORE) && (memtag[rPC] != MEMTAG_MIXED) ) {
|
||||
if (memtag[rPC] == MEMTAG_DATA)
|
||||
memtag[rPC] = MEMTAG_MIXED;
|
||||
else
|
||||
memtag[rPC] = MEMTAG_CODE;
|
||||
}
|
||||
const uint8_t data = READ_MEM_8(rPC, true);
|
||||
const uint8_t tag = mem::getTag(rPC);
|
||||
if ( !(tag & MEMTAG_IGNORE) ) mem::setTag(rPC, tag | MEMTAG_CODE);
|
||||
rPC++;
|
||||
return data;
|
||||
}
|
||||
@@ -187,6 +190,17 @@ namespace z80
|
||||
{
|
||||
t+=1;
|
||||
reading_m1 = true;
|
||||
if (rPC==0x056A) {
|
||||
//z80debug::stop();
|
||||
if (zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD)) {
|
||||
rIX = zx_tape::fastLoad(rA2, rIX, rDE);
|
||||
rDE = 0;
|
||||
rA = 0;
|
||||
rPC = 0x05E0;
|
||||
}
|
||||
//zx_tape::rewind();
|
||||
//zx_tape::play();
|
||||
}
|
||||
return READ_MEM_8();
|
||||
}
|
||||
|
||||
@@ -205,18 +219,18 @@ namespace z80
|
||||
const uint8_t WRITE_MEM_8(const uint16_t addr, const uint8_t value)
|
||||
{
|
||||
t+=3;
|
||||
if (addr>=0x4000) memory[addr] = value;
|
||||
if (addr>=0x4000) mem::writeMem(addr, value);
|
||||
if (z80debug::isbreak(addr, 4)) z80debug::stop();
|
||||
//if (z80debug::debugging())
|
||||
z80debug::setmemmodified(addr);
|
||||
if ( (memtag[addr] != MEMTAG_IGNORE) && (memtag[addr] != MEMTAG_MIXED) ) {
|
||||
if (memtag[addr]==MEMTAG_INST) {
|
||||
|
||||
const uint8_t tag = mem::getTag(addr);
|
||||
if ( !(tag & MEMTAG_IGNORE) ) {
|
||||
if ( tag & MEMTAG_INST ) {
|
||||
//printf("WARNING! WRITING DATA OVER CODE!!! $%4X\n", addr);
|
||||
//z80debug::stop();
|
||||
} else if (memtag[addr] == MEMTAG_CODE) {
|
||||
memtag[addr] = MEMTAG_MIXED;
|
||||
} else {
|
||||
memtag[addr] = MEMTAG_DATA;
|
||||
mem::setTag(addr, tag | MEMTAG_DATA | MEMTAG_TDATA);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -717,30 +731,6 @@ namespace z80
|
||||
RETN();
|
||||
}
|
||||
|
||||
void interrupt()
|
||||
{
|
||||
if (!iff1) return;
|
||||
exit_from_halt = true;
|
||||
PUSH(rPC);
|
||||
uint16_t address;
|
||||
if (im==1) {
|
||||
rPC = 0x38;
|
||||
} else if (im==2) {
|
||||
address = (rI<<8) | 0xFE;
|
||||
rPC = READ_MEM_16(address);
|
||||
} else if (im==0) {
|
||||
printf("Interrupt mode 0!\n");
|
||||
z80debug::stop();
|
||||
return;
|
||||
}
|
||||
if (options[Z80_OPTION_BREAK_ON_INTERRUPT]) {
|
||||
printf("Break on interrupt! 0x%2x, PC: 0x%2x\n", address, rPC);
|
||||
z80debug::setcursor(rPC);
|
||||
z80debug::history::store();
|
||||
z80debug::stop();
|
||||
}
|
||||
}
|
||||
|
||||
void RST(uint8_t vec)
|
||||
{
|
||||
PUSH(rPC);
|
||||
@@ -785,6 +775,31 @@ namespace z80
|
||||
}
|
||||
}
|
||||
|
||||
void interrupt()
|
||||
{
|
||||
if (!iff1) return;
|
||||
DI();
|
||||
exit_from_halt = true;
|
||||
PUSH(rPC);
|
||||
uint16_t address;
|
||||
if (im==1) {
|
||||
rPC = 0x38;
|
||||
} else if (im==2) {
|
||||
address = (rI<<8) | 0xFE;
|
||||
rPC = READ_MEM_16(address);
|
||||
} else if (im==0) {
|
||||
printf("Interrupt mode 0!\n");
|
||||
z80debug::stop();
|
||||
return;
|
||||
}
|
||||
if (options[Z80_OPTION_BREAK_ON_INTERRUPT]) {
|
||||
printf("Break on interrupt! 0x%2x, PC: 0x%2x\n", address, rPC);
|
||||
z80debug::setcursor(rPC);
|
||||
z80debug::history::store();
|
||||
z80debug::stop();
|
||||
}
|
||||
}
|
||||
|
||||
static inline const uint8_t RLC(const uint8_t v)
|
||||
{
|
||||
const uint8_t res = (v>>7) | (v<<1);
|
||||
@@ -928,10 +943,11 @@ namespace z80
|
||||
}
|
||||
}
|
||||
|
||||
void CPI()
|
||||
uint8_t CPI()
|
||||
{
|
||||
bool keep_fC = (rF & fC);
|
||||
CP(READ_MEM_8(rHL));
|
||||
const uint8_t hlmem = READ_MEM_8(rHL);
|
||||
CP(hlmem);
|
||||
rHL++;
|
||||
rBC--;
|
||||
t+=2;
|
||||
@@ -939,12 +955,13 @@ namespace z80
|
||||
if (keep_fC) SET_FLAGS(fC);
|
||||
if (rBC!=0) SET_FLAGS(fP);
|
||||
//if (READ_MEM_8(rHL)==rA) SET_FLAGS(fZ);
|
||||
return hlmem;
|
||||
}
|
||||
|
||||
void CPIR()
|
||||
{
|
||||
CPI();
|
||||
if (rBC!=0 && READ_MEM_8(rHL)!=rA)
|
||||
const uint8_t hlmem = CPI();
|
||||
if (rBC!=0 && hlmem!=rA)
|
||||
{
|
||||
rPC-=2;
|
||||
t+=2;
|
||||
@@ -985,16 +1002,31 @@ namespace z80
|
||||
port = (rA<<8) | port;
|
||||
}
|
||||
|
||||
if (in_ports[port&0xff]) {
|
||||
const uint8_t val = (uint8_t)in_ports[port&0xff](port);
|
||||
for (auto i: ports) {
|
||||
if ( ((port ^ i.value) & i.mask) == 0 ) {
|
||||
if (i.in) {
|
||||
const uint8_t val = (uint8_t)i.in(port);
|
||||
if (set_flags) {
|
||||
KEEP_FLAGS(fC);
|
||||
SET_PARITY_FLAG(val);
|
||||
FLAGS_SZXY(val);
|
||||
}
|
||||
return val;
|
||||
} else
|
||||
return 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0xFF;
|
||||
|
||||
//if (in_ports[port&0xff]) {
|
||||
// const uint8_t val = (uint8_t)in_ports[port&0xff](port);
|
||||
// if (set_flags) {
|
||||
// KEEP_FLAGS(fC);
|
||||
// SET_PARITY_FLAG(val);
|
||||
// FLAGS_SZXY(val);
|
||||
// }
|
||||
// return val;
|
||||
//} else
|
||||
// return 0xFF;
|
||||
}
|
||||
|
||||
void OUT(uint8_t val, int port = 0x10000)
|
||||
@@ -1002,8 +1034,16 @@ namespace z80
|
||||
t+=4;
|
||||
if (port == 0x10000) {
|
||||
port = rBC;
|
||||
} else {
|
||||
port = (rA<<8) | port;
|
||||
}
|
||||
if (out_ports[port&0xff]) out_ports[port&0xff](port, val);
|
||||
for (auto i: ports) {
|
||||
if ( ((port ^ i.value) & i.mask) == 0 ) {
|
||||
if (i.out) i.out(port, val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
//if (out_ports[port&0xff]) out_ports[port&0xff](port, val);
|
||||
}
|
||||
|
||||
void INI()
|
||||
@@ -1110,18 +1150,35 @@ namespace z80
|
||||
if (options[Z80_OPTION_STOP_ON_INVALID]) z80debug::stop();
|
||||
}
|
||||
|
||||
void reset(uint8_t* mem)
|
||||
void init(uint32_t freq)
|
||||
{
|
||||
memory = mem;
|
||||
for (int i=0; i<65536; ++i) memtag[i] = MEMTAG_NONE;
|
||||
setClock(freq);
|
||||
reset();
|
||||
clearPorts();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
//mem::reset();
|
||||
|
||||
rPC = iff1 = iff2 = im = 0;
|
||||
rAF = rAF2 = rBC = rBC2 = rDE = rDE2 = rHL = rHL2 = rIX = rIY = rSP = 0xffff;
|
||||
t = 0;
|
||||
for (int i=0; i<256; ++i)
|
||||
{
|
||||
in_ports[i] = nullptr;
|
||||
out_ports[i] = nullptr;
|
||||
}
|
||||
|
||||
void clearPorts()
|
||||
{
|
||||
ports.clear();
|
||||
}
|
||||
|
||||
void setClock(uint32_t freq)
|
||||
{
|
||||
clock = freq;
|
||||
}
|
||||
|
||||
uint32_t getClock()
|
||||
{
|
||||
return clock;
|
||||
}
|
||||
|
||||
void BIT_INSTRUCTIONS();
|
||||
@@ -1131,19 +1188,28 @@ namespace z80
|
||||
void IY_INSTRUCTIONS();
|
||||
void IY_BIT_INSTRUCTIONS();
|
||||
|
||||
void connect_port(int num, int (*in_ptr)(int), void (*out_ptr)(int,int))
|
||||
void connect_port(uint16_t num, uint16_t mask, int (*in_ptr)(int), void (*out_ptr)(int,int))
|
||||
{
|
||||
if (in_ptr) in_ports[num] = in_ptr;
|
||||
if (out_ptr) out_ports[num] = out_ptr;
|
||||
port_t port {num, mask, in_ptr, out_ptr};
|
||||
ports.push_back(port);
|
||||
//if (in_ptr) in_ports in_ptr;
|
||||
//if (out_ptr) out_ports[num] = out_ptr;
|
||||
}
|
||||
|
||||
bool opcode_ignored = false;
|
||||
uint32_t step()
|
||||
{
|
||||
do {
|
||||
opcode_ignored = false;
|
||||
current_opcode_address = rPC;
|
||||
t = 0;
|
||||
const uint8_t opcode = READ_M1();
|
||||
if (memtag[current_opcode_address] != MEMTAG_IGNORE) memtag[current_opcode_address] = MEMTAG_INST;
|
||||
memtouched[current_opcode_address] = memtouched[current_opcode_address] != MEMTAG_NONE ? MEMTAG_REPEAT : MEMTAG_INST;
|
||||
|
||||
uint8_t tag = mem::getTag(current_opcode_address);
|
||||
if ( !(tag & MEMTAG_IGNORE) )
|
||||
tag = tag | MEMTAG_INST;
|
||||
mem::setTag(current_opcode_address, tag | (!(tag&MEMTAG_TOUCHED) ? MEMTAG_TREPEAT : MEMTAG_TINST) );
|
||||
|
||||
uint16_t tmp;
|
||||
|
||||
if (opcode!=0xED && opcode!=0xCB && opcode!=0xDD && opcode!=0xFD ) z80debug::useOpcode(opcode, 0);
|
||||
@@ -1423,6 +1489,8 @@ namespace z80
|
||||
case 0xFF: RST(0x38); break;
|
||||
}
|
||||
|
||||
} while (opcode_ignored);
|
||||
|
||||
if (pending_ei==2) { pending_ei=0; actualEI(); }
|
||||
if (pending_ei==1) pending_ei=2;
|
||||
|
||||
@@ -1713,6 +1781,13 @@ namespace z80
|
||||
}
|
||||
}
|
||||
|
||||
void IgnoreOpcode()
|
||||
{
|
||||
t-=3;
|
||||
rPC--;
|
||||
opcode_ignored=true;
|
||||
}
|
||||
|
||||
void IX_INSTRUCTIONS()
|
||||
{
|
||||
const uint8_t opcode = READ_M1();
|
||||
@@ -1722,73 +1797,73 @@ namespace z80
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case 0x00: INVALID(opcode); break;
|
||||
case 0x01: INVALID(opcode); break;
|
||||
case 0x02: INVALID(opcode); break;
|
||||
case 0x03: INVALID(opcode); break;
|
||||
case 0x00: IgnoreOpcode(); break;
|
||||
case 0x01: IgnoreOpcode(); break;
|
||||
case 0x02: IgnoreOpcode(); break;
|
||||
case 0x03: IgnoreOpcode(); break;
|
||||
case 0x04: INC8(_rB); break;
|
||||
case 0x05: DEC8(_rB); break;
|
||||
case 0x06: rB = READ_MEM_8(); break;
|
||||
case 0x07: INVALID(opcode); break;
|
||||
case 0x08: INVALID(opcode); break;
|
||||
case 0x07: IgnoreOpcode(); break;
|
||||
case 0x08: IgnoreOpcode(); break;
|
||||
case 0x09: ADD16(_rIX, rBC); break;
|
||||
case 0x0A: INVALID(opcode); break;
|
||||
case 0x0B: INVALID(opcode); break;
|
||||
case 0x0A: IgnoreOpcode(); break;
|
||||
case 0x0B: IgnoreOpcode(); break;
|
||||
case 0x0C: INC8(_rC); break;
|
||||
case 0x0D: DEC8(_rC); break;
|
||||
case 0x0E: rC = READ_MEM_8(); break;
|
||||
case 0x0F: INVALID(opcode); break;
|
||||
case 0x0F: IgnoreOpcode(); break;
|
||||
|
||||
case 0x10: INVALID(opcode); break;
|
||||
case 0x11: INVALID(opcode); break;
|
||||
case 0x12: INVALID(opcode); break;
|
||||
case 0x13: INVALID(opcode); break;
|
||||
case 0x10: IgnoreOpcode(); break;
|
||||
case 0x11: IgnoreOpcode(); break;
|
||||
case 0x12: IgnoreOpcode(); break;
|
||||
case 0x13: IgnoreOpcode(); break;
|
||||
case 0x14: INC8(_rD); break;
|
||||
case 0x15: DEC8(_rD); break;
|
||||
case 0x16: rD = READ_MEM_8(); break;
|
||||
case 0x17: INVALID(opcode); break;
|
||||
case 0x18: INVALID(opcode); break;
|
||||
case 0x17: IgnoreOpcode(); break;
|
||||
case 0x18: IgnoreOpcode(); break;
|
||||
case 0x19: ADD16(_rIX, rDE); break;
|
||||
case 0x1A: INVALID(opcode); break;
|
||||
case 0x1B: INVALID(opcode); break;
|
||||
case 0x1A: IgnoreOpcode(); break;
|
||||
case 0x1B: IgnoreOpcode(); break;
|
||||
case 0x1C: INC8(_rE); break;
|
||||
case 0x1D: DEC8(_rE); break;
|
||||
case 0x1E: rE = READ_MEM_8(); break;
|
||||
case 0x1F: INVALID(opcode); break;
|
||||
case 0x1F: IgnoreOpcode(); break;
|
||||
|
||||
case 0x20: INVALID(opcode); break;
|
||||
case 0x20: IgnoreOpcode(); break;
|
||||
case 0x21: rIX = READ_MEM_16(); break;
|
||||
case 0x22: WRITE_MEM_16(READ_MEM_16(),rIX); break;
|
||||
case 0x23: INC16(_rIX); break;
|
||||
case 0x24: INC8(_rIXH); break;
|
||||
case 0x25: DEC8(_rIXH); break;
|
||||
case 0x26: rIXH = READ_MEM_8(); break;
|
||||
case 0x27: INVALID(opcode); break;
|
||||
case 0x28: INVALID(opcode); break;
|
||||
case 0x27: IgnoreOpcode(); break;
|
||||
case 0x28: IgnoreOpcode(); break;
|
||||
case 0x29: ADD16(_rIX, rIX); break;
|
||||
case 0x2A: rIX = READ_MEM_16(READ_MEM_16());break;
|
||||
case 0x2B: DEC16(_rIX); break;
|
||||
case 0x2C: INC8(_rIXL); break;
|
||||
case 0x2D: DEC8(_rIXL); break;
|
||||
case 0x2E: rIXL = READ_MEM_8(); break;
|
||||
case 0x2F: INVALID(opcode); break;
|
||||
case 0x2F: IgnoreOpcode(); break;
|
||||
|
||||
case 0x30: INVALID(opcode); break;
|
||||
case 0x31: INVALID(opcode); break;
|
||||
case 0x32: INVALID(opcode); break;
|
||||
case 0x33: INVALID(opcode); break;
|
||||
case 0x30: IgnoreOpcode(); break;
|
||||
case 0x31: IgnoreOpcode(); break;
|
||||
case 0x32: IgnoreOpcode(); break;
|
||||
case 0x33: IgnoreOpcode(); break;
|
||||
case 0x34: d=READ_MEM_8(); INCMEM8(rIX+d);t+=2; break;
|
||||
case 0x35: d=READ_MEM_8(); DECMEM8(rIX+d);t+=2; break;
|
||||
case 0x36: d=READ_MEM_8(); WRITE_MEM_8(rIX+d, READ_MEM_8()); t+=2; break;
|
||||
case 0x37: INVALID(opcode); break;
|
||||
case 0x38: INVALID(opcode); break;
|
||||
case 0x37: IgnoreOpcode(); break;
|
||||
case 0x38: IgnoreOpcode(); break;
|
||||
case 0x39: ADD16(_rIX, rSP); break;
|
||||
case 0x3A: INVALID(opcode); break;
|
||||
case 0x3B: INVALID(opcode); break;
|
||||
case 0x3A: IgnoreOpcode(); break;
|
||||
case 0x3B: IgnoreOpcode(); break;
|
||||
case 0x3C: INC8(_rA); break;
|
||||
case 0x3D: DEC8(_rA); break;
|
||||
case 0x3E: rA = READ_MEM_8(); break;
|
||||
case 0x3F: INVALID(opcode); break;
|
||||
case 0x3F: IgnoreOpcode(); break;
|
||||
|
||||
case 0x40: rB = rB; break;
|
||||
case 0x41: rB = rC; break;
|
||||
@@ -1847,7 +1922,7 @@ namespace z80
|
||||
case 0x73: d=READ_MEM_8(); WRITE_MEM_8(rIX+d, rE);t+=5;break;
|
||||
case 0x74: d=READ_MEM_8(); WRITE_MEM_8(rIX+d, rH);t+=5;break;
|
||||
case 0x75: d=READ_MEM_8(); WRITE_MEM_8(rIX+d, rL);t+=5;break;
|
||||
case 0x76: INVALID(opcode); break;
|
||||
case 0x76: IgnoreOpcode(); break;
|
||||
case 0x77: d=READ_MEM_8(); WRITE_MEM_8(rIX+d, rA);t+=5;break;
|
||||
case 0x78: rA = rB; break;
|
||||
case 0x79: rA = rC; break;
|
||||
@@ -1932,7 +2007,7 @@ namespace z80
|
||||
case 0xE5: PUSH(rIX); break;
|
||||
case 0xE9: JP(cNO, rIX); break;
|
||||
case 0xF9: rSP = rIX; t+=2; break;
|
||||
default: INVALID(opcode); break;
|
||||
default: IgnoreOpcode(); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2232,7 +2307,7 @@ namespace z80
|
||||
switch (opcode)
|
||||
{
|
||||
case 0x40: rB = IN(); break;
|
||||
case 0x41: OUT(rB, rC); break;
|
||||
case 0x41: OUT(rB); break;
|
||||
case 0x42: SBC16(_rHL, rBC); break;
|
||||
case 0x43: WRITE_MEM_16(READ_MEM_16(), rBC);break;
|
||||
case 0x44: NEG(); break;
|
||||
@@ -2240,42 +2315,42 @@ namespace z80
|
||||
case 0x46: IM(0); break;
|
||||
case 0x47: rI = rA; t++; break;
|
||||
case 0x48: rC = IN(); break;
|
||||
case 0x49: OUT(rC, rC); break;
|
||||
case 0x49: OUT(rC); break;
|
||||
case 0x4A: ADC16(_rHL, rBC); break;
|
||||
case 0x4B: rBC = READ_MEM_16(READ_MEM_16());break;
|
||||
case 0x4D: RETI(); break;
|
||||
case 0x4F: rR = rA; t++; break;
|
||||
|
||||
case 0x50: rD = IN(); break;
|
||||
case 0x51: OUT(rD, rC); break;
|
||||
case 0x51: OUT(rD); break;
|
||||
case 0x52: SBC16(_rHL, rDE); break;
|
||||
case 0x53: WRITE_MEM_16(READ_MEM_16(), rDE);break;
|
||||
case 0x56: IM(1); break;
|
||||
case 0x57: LD_A_I(); break;
|
||||
case 0x58: rE = IN(); break;
|
||||
case 0x59: OUT(rE, rC); break;
|
||||
case 0x59: OUT(rE); break;
|
||||
case 0x5A: ADC16(_rHL, rDE); break;
|
||||
case 0x5B: rDE = READ_MEM_16(READ_MEM_16());break;
|
||||
case 0x5E: IM(2); break;
|
||||
case 0x5F: LD_A_R(); break;
|
||||
|
||||
case 0x60: rH = IN(); break;
|
||||
case 0x61: OUT(rH, rC); break;
|
||||
case 0x61: OUT(rH); break;
|
||||
case 0x62: SBC16(_rHL, rHL); break;
|
||||
case 0x63: WRITE_MEM_16(READ_MEM_16(), rHL);break;
|
||||
case 0x67: RRD(); break;
|
||||
case 0x68: rL = IN(); break;
|
||||
case 0x69: OUT(rL, rC); break;
|
||||
case 0x69: OUT(rL); break;
|
||||
case 0x6A: ADC16(_rHL, rHL); break;
|
||||
case 0x6B: rHL = READ_MEM_16(READ_MEM_16());break;
|
||||
case 0x6F: RLD(); break;
|
||||
|
||||
case 0x70: IN(); break;
|
||||
case 0x71: OUT(0, rC); break;
|
||||
case 0x71: OUT(0); break;
|
||||
case 0x72: SBC16(_rHL, rSP); break;
|
||||
case 0x73: WRITE_MEM_16(READ_MEM_16(), rSP);break;
|
||||
case 0x78: rA = IN(); break;
|
||||
case 0x79: OUT(rA, rC); break;
|
||||
case 0x79: OUT(rA); break;
|
||||
case 0x7A: ADC16(_rHL, rSP); break;
|
||||
case 0x7B: rSP = READ_MEM_16(READ_MEM_16());break;
|
||||
|
||||
@@ -2311,73 +2386,73 @@ namespace z80
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case 0x00: INVALID(opcode); break;
|
||||
case 0x01: INVALID(opcode); break;
|
||||
case 0x02: INVALID(opcode); break;
|
||||
case 0x03: INVALID(opcode); break;
|
||||
case 0x00: IgnoreOpcode(); break;
|
||||
case 0x01: IgnoreOpcode(); break;
|
||||
case 0x02: IgnoreOpcode(); break;
|
||||
case 0x03: IgnoreOpcode(); break;
|
||||
case 0x04: INC8(_rB); break;
|
||||
case 0x05: DEC8(_rB); break;
|
||||
case 0x06: rB = READ_MEM_8(); break;
|
||||
case 0x07: INVALID(opcode); break;
|
||||
case 0x08: INVALID(opcode); break;
|
||||
case 0x07: IgnoreOpcode(); break;
|
||||
case 0x08: IgnoreOpcode(); break;
|
||||
case 0x09: ADD16(_rIY, rBC); break;
|
||||
case 0x0A: INVALID(opcode); break;
|
||||
case 0x0B: INVALID(opcode); break;
|
||||
case 0x0A: IgnoreOpcode(); break;
|
||||
case 0x0B: IgnoreOpcode(); break;
|
||||
case 0x0C: INC8(_rC); break;
|
||||
case 0x0D: DEC8(_rC); break;
|
||||
case 0x0E: rC = READ_MEM_8(); break;
|
||||
case 0x0F: INVALID(opcode); break;
|
||||
case 0x0F: IgnoreOpcode(); break;
|
||||
|
||||
case 0x10: INVALID(opcode); break;
|
||||
case 0x11: INVALID(opcode); break;
|
||||
case 0x12: INVALID(opcode); break;
|
||||
case 0x13: INVALID(opcode); break;
|
||||
case 0x10: IgnoreOpcode(); break;
|
||||
case 0x11: IgnoreOpcode(); break;
|
||||
case 0x12: IgnoreOpcode(); break;
|
||||
case 0x13: IgnoreOpcode(); break;
|
||||
case 0x14: INC8(_rD); break;
|
||||
case 0x15: DEC8(_rD); break;
|
||||
case 0x16: rD = READ_MEM_8(); break;
|
||||
case 0x17: INVALID(opcode); break;
|
||||
case 0x18: INVALID(opcode); break;
|
||||
case 0x17: IgnoreOpcode(); break;
|
||||
case 0x18: IgnoreOpcode(); break;
|
||||
case 0x19: ADD16(_rIY, rDE); break;
|
||||
case 0x1A: INVALID(opcode); break;
|
||||
case 0x1B: INVALID(opcode); break;
|
||||
case 0x1A: IgnoreOpcode(); break;
|
||||
case 0x1B: IgnoreOpcode(); break;
|
||||
case 0x1C: INC8(_rE); break;
|
||||
case 0x1D: DEC8(_rE); break;
|
||||
case 0x1E: rE = READ_MEM_8(); break;
|
||||
case 0x1F: INVALID(opcode); break;
|
||||
case 0x1F: IgnoreOpcode(); break;
|
||||
|
||||
case 0x20: INVALID(opcode); break;
|
||||
case 0x20: IgnoreOpcode(); break;
|
||||
case 0x21: rIY = READ_MEM_16(); break;
|
||||
case 0x22: WRITE_MEM_16(READ_MEM_16(),rIY); break;
|
||||
case 0x23: INC16(_rIY); break;
|
||||
case 0x24: INC8(_rIYH); break;
|
||||
case 0x25: DEC8(_rIYH); break;
|
||||
case 0x26: rIYH = READ_MEM_8(); break;
|
||||
case 0x27: INVALID(opcode); break;
|
||||
case 0x28: INVALID(opcode); break;
|
||||
case 0x27: IgnoreOpcode(); break;
|
||||
case 0x28: IgnoreOpcode(); break;
|
||||
case 0x29: ADD16(_rIY, rIY); break;
|
||||
case 0x2A: rIY = READ_MEM_16(READ_MEM_16());break;
|
||||
case 0x2B: DEC16(_rIY); break;
|
||||
case 0x2C: INC8(_rIYL); break;
|
||||
case 0x2D: DEC8(_rIYL); break;
|
||||
case 0x2E: rIYL = READ_MEM_8(); break;
|
||||
case 0x2F: INVALID(opcode); break;
|
||||
case 0x2F: IgnoreOpcode(); break;
|
||||
|
||||
case 0x30: INVALID(opcode); break;
|
||||
case 0x31: INVALID(opcode); break;
|
||||
case 0x32: INVALID(opcode); break;
|
||||
case 0x33: INVALID(opcode); break;
|
||||
case 0x30: IgnoreOpcode(); break;
|
||||
case 0x31: IgnoreOpcode(); break;
|
||||
case 0x32: IgnoreOpcode(); break;
|
||||
case 0x33: IgnoreOpcode(); break;
|
||||
case 0x34: d=READ_MEM_8(); INCMEM8(rIY+d);t+=2; break;
|
||||
case 0x35: d=READ_MEM_8(); DECMEM8(rIY+d);t+=2; break;
|
||||
case 0x36: d=READ_MEM_8(); WRITE_MEM_8(rIY+d, READ_MEM_8()); t+=2; break;
|
||||
case 0x37: INVALID(opcode); break;
|
||||
case 0x38: INVALID(opcode); break;
|
||||
case 0x37: IgnoreOpcode(); break;
|
||||
case 0x38: IgnoreOpcode(); break;
|
||||
case 0x39: ADD16(_rIY, rSP); break;
|
||||
case 0x3A: INVALID(opcode); break;
|
||||
case 0x3B: INVALID(opcode); break;
|
||||
case 0x3A: IgnoreOpcode(); break;
|
||||
case 0x3B: IgnoreOpcode(); break;
|
||||
case 0x3C: INC8(_rA); break;
|
||||
case 0x3D: DEC8(_rA); break;
|
||||
case 0x3E: rA = READ_MEM_8(); break;
|
||||
case 0x3F: INVALID(opcode); break;
|
||||
case 0x3F: IgnoreOpcode(); break;
|
||||
|
||||
case 0x40: rB = rB; break;
|
||||
case 0x41: rB = rC; break;
|
||||
@@ -2436,7 +2511,7 @@ namespace z80
|
||||
case 0x73: d=READ_MEM_8(); WRITE_MEM_8(rIY+d, rE);t+=5;break;
|
||||
case 0x74: d=READ_MEM_8(); WRITE_MEM_8(rIY+d, rH);t+=5;break;
|
||||
case 0x75: d=READ_MEM_8(); WRITE_MEM_8(rIY+d, rL);t+=5;break;
|
||||
case 0x76: INVALID(opcode); break;
|
||||
case 0x76: IgnoreOpcode(); break;
|
||||
case 0x77: d=READ_MEM_8(); WRITE_MEM_8(rIY+d, rA);t+=5;break;
|
||||
case 0x78: rA = rB; break;
|
||||
case 0x79: rA = rC; break;
|
||||
@@ -2521,7 +2596,7 @@ namespace z80
|
||||
case 0xE5: PUSH(rIY); break;
|
||||
case 0xE9: JP(cNO, rIY); break;
|
||||
case 0xF9: rSP = rIY; t+=2; break;
|
||||
default: INVALID(opcode); break;
|
||||
default: IgnoreOpcode(); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2809,7 +2884,7 @@ namespace z80
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *getMem() { return memory; }
|
||||
//uint8_t *getMem() { return memory; }
|
||||
uint8_t *getRegs() { return regs; }
|
||||
|
||||
uint16_t getAF(const bool alt) { return alt?rAF2:rAF; }
|
||||
@@ -2828,6 +2903,7 @@ namespace z80
|
||||
|
||||
void setPC(const uint16_t addr) { rPC = addr; }
|
||||
|
||||
/*
|
||||
uint8_t getMemTag(const uint16_t addr) { return memtag[addr]; }
|
||||
|
||||
void setMemTag(const uint16_t addr, const uint8_t value) { memtag[addr] = value; }
|
||||
@@ -2852,7 +2928,7 @@ namespace z80
|
||||
//else if (memtouched[i]==MEMTAG_DATA)
|
||||
// memtouched[i] = MEMTAG_REPEAT;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
const bool getOption(const int option)
|
||||
{
|
||||
|
||||
23
z80.h
23
z80.h
@@ -4,26 +4,24 @@
|
||||
|
||||
namespace z80
|
||||
{
|
||||
#define MEMTAG_NONE 0
|
||||
#define MEMTAG_INST 1
|
||||
#define MEMTAG_CODE 2
|
||||
#define MEMTAG_DATA 3
|
||||
#define MEMTAG_REPEAT 4
|
||||
#define MEMTAG_MIXED 5
|
||||
#define MEMTAG_IGNORE 6
|
||||
|
||||
#define Z80_OPTION_STOP_ON_INVALID 0
|
||||
#define Z80_OPTION_BREAK_ON_INTERRUPT 1
|
||||
#define Z80_OPTION_BREAK_ON_RET 2
|
||||
#define Z80_NUM_OPTIONS 3
|
||||
|
||||
void reset(uint8_t* mem);
|
||||
void connect_port(int num, int (*in_ptr)(int), void (*out_ptr)(int,int));
|
||||
void init(uint32_t freq);
|
||||
void reset();
|
||||
void clearPorts();
|
||||
void setClock(uint32_t freq);
|
||||
uint32_t getClock();
|
||||
|
||||
//void reset(uint8_t* mem);
|
||||
void connect_port(uint16_t num, uint16_t mask, int (*in_ptr)(int), void (*out_ptr)(int,int));
|
||||
void interrupt();
|
||||
|
||||
uint32_t step();
|
||||
|
||||
uint8_t *getMem();
|
||||
//uint8_t *getMem();
|
||||
uint8_t *getRegs();
|
||||
|
||||
uint16_t getAF(const bool alt=false);
|
||||
@@ -41,6 +39,8 @@ namespace z80
|
||||
|
||||
|
||||
void setPC(const uint16_t addr);
|
||||
|
||||
/*
|
||||
uint8_t getMemTag(const uint16_t addr);
|
||||
void setMemTag(const uint16_t addr, const uint8_t value);
|
||||
void clearMemTag();
|
||||
@@ -48,6 +48,7 @@ namespace z80
|
||||
uint8_t getMemTouched(const uint16_t addr);
|
||||
void clearMemTouched();
|
||||
void fixMemTouched();
|
||||
*/
|
||||
|
||||
const bool getOption(const int option);
|
||||
void setOption(const int option, const bool value);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "z80analyze.h"
|
||||
#include "z80.h"
|
||||
#include "z80debug.h"
|
||||
#include "zx_mem.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include "ui_window.h"
|
||||
#include "ui.h"
|
||||
@@ -12,6 +13,8 @@ namespace z80analyze
|
||||
SDL_Texture *tex = nullptr;
|
||||
SDL_Texture *uitex = nullptr;
|
||||
uint16_t address = 0;
|
||||
int mx, my;
|
||||
bool needs_refresh = true;
|
||||
|
||||
void refreshTitle();
|
||||
|
||||
@@ -34,18 +37,25 @@ namespace z80analyze
|
||||
{
|
||||
if (e->motion.windowID == SDL_GetWindowID(win)) {
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
mx = e->motion.x/2;
|
||||
my = e->motion.y/2;
|
||||
|
||||
refreshTitle();
|
||||
refresh();
|
||||
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) {
|
||||
z80::clearMemTouched();
|
||||
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();
|
||||
}
|
||||
@@ -70,15 +80,18 @@ namespace z80analyze
|
||||
char tmp[10];
|
||||
void refreshTitle()
|
||||
{
|
||||
int mx, my;
|
||||
SDL_GetMouseState(&mx, &my);
|
||||
mx/=2; my/=2;
|
||||
if (mx>=0 && my>=0 && mx<256 && my<256) {
|
||||
address = mx+my*256;
|
||||
SDL_SetWindowTitle(win, SDL_itoa(address, tmp, 16));
|
||||
}
|
||||
void refresh()
|
||||
}
|
||||
|
||||
void refresh(const bool conditional)
|
||||
{
|
||||
if (!win) return;
|
||||
if (conditional && !needs_refresh) return;
|
||||
needs_refresh = false;
|
||||
|
||||
ui::setrenderer(ren, uitex);
|
||||
|
||||
Uint32 *pixels;
|
||||
@@ -88,23 +101,22 @@ namespace z80analyze
|
||||
{
|
||||
//uint8_t tag = z80::getMemTag(i);
|
||||
//pixels[i] = tag==MEMTAG_NONE ? 0x808080 : tag==MEMTAG_DATA ? 0x0000FF : tag==MEMTAG_MIXED ? 0xFF00FF : 0x00FF00;
|
||||
|
||||
uint8_t tag = z80::getMemTouched(i);
|
||||
pixels[i] = tag==MEMTAG_NONE ? 0x808080 : tag==MEMTAG_DATA ? 0x0000FF : tag==MEMTAG_REPEAT ? 0xFF0000 : 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;
|
||||
|
||||
int mx, my;
|
||||
SDL_GetMouseState(&mx, &my);
|
||||
mx/=2; my/=2;
|
||||
pixels[(mx-2)+(my)*256] = 0x000000;
|
||||
pixels[(mx-1)+(my)*256] = 0x000000;
|
||||
pixels[(mx+1)+(my)*256] = 0x000000;
|
||||
pixels[(mx+2)+(my)*256] = 0x000000;
|
||||
pixels[(mx)+(my-1)*256] = 0x000000;
|
||||
pixels[(mx)+(my-2)*256] = 0x000000;
|
||||
pixels[(mx)+(my+1)*256] = 0x000000;
|
||||
pixels[(mx)+(my+2)*256] = 0x000000;
|
||||
if (mx>=0 && my>=0 && mx<256 && my<256) {
|
||||
if (mx>2) pixels[(mx-2)+(my)*256] = 0x000000;
|
||||
if (mx>1) pixels[(mx-1)+(my)*256] = 0x000000;
|
||||
if (mx<255) pixels[(mx+1)+(my)*256] = 0x000000;
|
||||
if (mx<254) pixels[(mx+2)+(my)*256] = 0x000000;
|
||||
if (my>1) pixels[(mx)+(my-1)*256] = 0x000000;
|
||||
if (my>2) pixels[(mx)+(my-2)*256] = 0x000000;
|
||||
if (my<255) pixels[(mx)+(my+1)*256] = 0x000000;
|
||||
if (my<254) pixels[(mx)+(my+2)*256] = 0x000000;
|
||||
}
|
||||
|
||||
SDL_UnlockTexture(tex);
|
||||
SDL_RenderCopy(ren, tex, NULL, NULL);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace z80analyze
|
||||
{
|
||||
void show();
|
||||
void refresh();
|
||||
void refresh(const bool conditional=false);
|
||||
void hide();
|
||||
void focus();
|
||||
}
|
||||
|
||||
178
z80debug.cpp
178
z80debug.cpp
@@ -2,12 +2,17 @@
|
||||
#include <SDL2/SDL.h>
|
||||
#include "z80.h"
|
||||
#include "z80dis.h"
|
||||
#include "zx_mem.h"
|
||||
#include "z80analyze.h"
|
||||
#include "zx_ula.h"
|
||||
#include "zx_speaker.h"
|
||||
#include "zx_tape.h"
|
||||
#include "zx_disk.h"
|
||||
#include "ui.h"
|
||||
#include "ui_window.h"
|
||||
#include "zx_screen.h"
|
||||
#include "z80analyze.h"
|
||||
#include "ay-3-8912.h"
|
||||
#include "file.h"
|
||||
|
||||
#define RESIZING_NONE 0
|
||||
#define RESIZING_MEMORY 1
|
||||
@@ -25,7 +30,7 @@ namespace z80debug
|
||||
int mem_h = 8;
|
||||
int con_h = 6;
|
||||
int sym_h = 14;
|
||||
|
||||
int zoom = 1;
|
||||
int resizing_type = RESIZING_MEMORY;
|
||||
bool resizing = false;
|
||||
|
||||
@@ -39,7 +44,7 @@ namespace z80debug
|
||||
uint16_t oAF, oBC, oDE, oHL, oAF2, oBC2, oDE2, oHL2, oIX, oIY, oSP, oPC;
|
||||
uint8_t oI, oR;
|
||||
|
||||
bool mem_modified[65536];
|
||||
bool mem_modified[65536]; //[CHECK]
|
||||
|
||||
SDL_Window *win = nullptr;
|
||||
SDL_Renderer *ren = nullptr;
|
||||
@@ -91,13 +96,26 @@ namespace z80debug
|
||||
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)
|
||||
{
|
||||
if (e->type == SDL_MOUSEMOTION) {
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
}
|
||||
if (z80debug::debugging()) {
|
||||
if (e->type==SDL_WINDOWEVENT) {
|
||||
if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) {
|
||||
int w; int h;
|
||||
SDL_GetWindowSize(win, &w, &h);
|
||||
w/=zoom; h/=zoom;
|
||||
midx = (w/CHR_W) - 25;
|
||||
win_h = (h/CHR_H);
|
||||
mem_y = win_h - mem_h - con_h;
|
||||
@@ -108,6 +126,8 @@ namespace z80debug
|
||||
} else if (e->window.event == SDL_WINDOWEVENT_CLOSE) {
|
||||
hide();
|
||||
zxscreen::focus();
|
||||
} else if (e->window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
|
||||
saveWindowConfiguration();
|
||||
}
|
||||
}
|
||||
if (e->type == SDL_MOUSEWHEEL) {
|
||||
@@ -137,18 +157,26 @@ namespace z80debug
|
||||
z80debug::refresh();
|
||||
z80analyze::refresh();
|
||||
} else if (e->key.keysym.scancode==SDL_SCANCODE_F2) {
|
||||
if (e->key.keysym.mod & KMOD_CTRL) {
|
||||
zoom = zoom==1?2:1;
|
||||
hide();
|
||||
show();
|
||||
} else {
|
||||
z80debug::history::goback();
|
||||
z80debug::refresh();
|
||||
z80analyze::refresh();
|
||||
}
|
||||
} else if (e->key.keysym.scancode==SDL_SCANCODE_F3) {
|
||||
z80debug::history::goforward();
|
||||
z80debug::refresh();
|
||||
z80analyze::refresh();
|
||||
/*} else if (e->key.keysym.scancode==SDL_SCANCODE_F6) {
|
||||
z80debug::history::gototop();
|
||||
const uint8_t dt = z80::step();
|
||||
z80debug::refresh();
|
||||
zxscreen::refresh(dt);*/
|
||||
} else if (e->key.keysym.scancode==SDL_SCANCODE_F9) {
|
||||
const uint16_t address = cursor;
|
||||
if (breakpoints[line_address[address]]==0)
|
||||
breakpoints[line_address[address]]=1;
|
||||
else
|
||||
breakpoints[line_address[address]]=0;
|
||||
refresh();
|
||||
} 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--;
|
||||
//z80debug::cursorback();
|
||||
@@ -164,7 +192,6 @@ namespace z80debug
|
||||
}
|
||||
}
|
||||
if (e->type == SDL_MOUSEMOTION) {
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
if (!resizing) {
|
||||
if ( (e->motion.y > (mem_y*CHR_H)-8) && (e->motion.y < (mem_y*CHR_H)+8) && ( e->motion.x < midx*CHR_W ) ) {
|
||||
if (resizing_type != RESIZING_MEMORY) SDL_SetCursor(cur_ns);
|
||||
@@ -193,7 +220,7 @@ namespace z80debug
|
||||
const int opcodesize = z80dis::getOpcodeSize(address);
|
||||
const int byte = (chrx-19)/3;
|
||||
if (byte < opcodesize) {
|
||||
inspected_value = z80::getMem()[address+byte];
|
||||
inspected_value = mem::readMem(address+byte);
|
||||
refresh();
|
||||
}
|
||||
// Si pasa per damunt de l'adreça en el visor de memòria
|
||||
@@ -203,7 +230,7 @@ namespace z80debug
|
||||
// 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 = z80::getMem()[address];
|
||||
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();
|
||||
@@ -219,9 +246,9 @@ namespace z80debug
|
||||
} 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 = z80::getMem()[z80::getBC()]; refresh();
|
||||
} else if ((chrx>=midx+14) && (chrx<midx+16) && (chry==5)) { inspected_value = z80::getMem()[z80::getDE()]; refresh();
|
||||
} else if ((chrx>=midx+22) && (chrx<midx+24) && (chry==5)) { inspected_value = z80::getMem()[z80::getHL()]; 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();
|
||||
@@ -325,8 +352,12 @@ namespace z80debug
|
||||
void show()
|
||||
{
|
||||
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);
|
||||
SDL_RenderSetLogicalSize(ren, 98*CHR_W, 44*CHR_H);
|
||||
ui::window::registerWindow(SDL_GetWindowID(win), eventHandler);
|
||||
tex = ui::createtexture(ren);
|
||||
}
|
||||
@@ -344,6 +375,7 @@ namespace z80debug
|
||||
|
||||
void hide()
|
||||
{
|
||||
saveWindowConfiguration();
|
||||
ui::window::unregisterWindow(SDL_GetWindowID(win));
|
||||
if (tex) SDL_DestroyTexture(tex);
|
||||
if (ren) SDL_DestroyRenderer(ren);
|
||||
@@ -355,7 +387,7 @@ namespace z80debug
|
||||
|
||||
void pause()
|
||||
{
|
||||
zx_ula::sound_disable();
|
||||
speaker::disable();
|
||||
is_paused = true;
|
||||
breakpoints[z80::getPC()] &= ~8;
|
||||
}
|
||||
@@ -375,10 +407,10 @@ namespace z80debug
|
||||
void cont() {
|
||||
zxscreen::setTitle("");
|
||||
is_debugging = is_paused = false;
|
||||
//hide();
|
||||
hide();
|
||||
refresh();
|
||||
zxscreen::focus();
|
||||
zx_ula::sound_enable();
|
||||
speaker::enable();
|
||||
}
|
||||
|
||||
const bool debugging() { return is_debugging; }
|
||||
@@ -388,9 +420,10 @@ namespace z80debug
|
||||
uint16_t find_previous_opcode(uint16_t pc)
|
||||
{
|
||||
pc--;
|
||||
if (z80::getMemTag(pc)!=MEMTAG_CODE && z80::getMemTag(pc)!=MEMTAG_MIXED && 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;
|
||||
/*
|
||||
@@ -410,8 +443,9 @@ namespace z80debug
|
||||
void printDissasemblerLine(const uint16_t address, const int line, const bool heuristics=false)
|
||||
{
|
||||
uint8_t colors[4] = { COLOR_RED, COLOR_CYAN, COLOR_WHITE, COLOR_WHITE};
|
||||
if (z80::getMemTouched(address)!=MEMTAG_INST && z80::getMemTouched(address)!=MEMTAG_REPEAT) colors[3]=COLOR_GRAY;
|
||||
if (z80::getMemTouched(address)==MEMTAG_NONE) colors[1]=COLOR_GRAY;
|
||||
const uint8_t tag = mem::getTag(address);
|
||||
if ( !(tag & (MEMTAG_TINST | MEMTAG_TREPEAT)) ) colors[3]=COLOR_GRAY;
|
||||
if ( !(tag & MEMTAG_TOUCHED) ) colors[1]=COLOR_GRAY;
|
||||
|
||||
if (is_debugging && (address == z80::getPC())) {
|
||||
for (int i=0; i<4;++i) colors[i]=COLOR_YELLOW;
|
||||
@@ -421,14 +455,14 @@ namespace z80debug
|
||||
if (breakpoints[address]&9) ui::printtxt(0,line,"*", colors[0]);
|
||||
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);
|
||||
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 = z80::getMemTag(address+i);
|
||||
const uint32_t color = tag==MEMTAG_NONE ? COLOR_GRAY : tag==MEMTAG_DATA ? COLOR_BLUE : tag==MEMTAG_MIXED ? COLOR_MAGENTA : COLOR_GREEN;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -436,7 +470,7 @@ namespace z80debug
|
||||
ui::printtxt(31,line, z80dis::getAsm(address), colors[3]);
|
||||
} else {
|
||||
ui::printrect(19,line,2,1,COLOR_GRAY);
|
||||
ui::printtxt(19,line, tohex(z80::getMem()[address],2), COLOR_WHITE);
|
||||
ui::printtxt(19,line, tohex(mem::readMem(address),2), COLOR_WHITE);
|
||||
ui::printtxt(31,line, "?????????", COLOR_GRAY);
|
||||
}
|
||||
}
|
||||
@@ -457,8 +491,6 @@ namespace z80debug
|
||||
|
||||
ui::setoffset(1, 1);
|
||||
|
||||
uint8_t *memory = z80::getMem();
|
||||
|
||||
uint16_t pc = cursor; //z80::getPC();
|
||||
if (sync_mem_with_cursor) mem_viewer_pos = cursor;
|
||||
uint16_t pos = pc;
|
||||
@@ -512,9 +544,9 @@ namespace z80debug
|
||||
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(5,4, tohex(memory[z80::getBC()], 2), COLOR_GRAY);
|
||||
ui::printtxt(13,4, tohex(memory[z80::getDE()], 2), COLOR_GRAY);
|
||||
ui::printtxt(21,4, tohex(memory[z80::getHL()], 2), COLOR_GRAY);
|
||||
ui::printtxt(5,4, tohex(mem::readMem(z80::getBC()), 2), COLOR_GRAY);
|
||||
ui::printtxt(13,4, tohex(mem::readMem(z80::getDE()), 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 mod_flags = flags ^ (oAF & 0xFF);
|
||||
@@ -530,8 +562,6 @@ namespace z80debug
|
||||
|
||||
// STACK
|
||||
// ******************************************
|
||||
// TODO: Save if stack element was introduced by a call, push or data
|
||||
// TODO: Click goes to address
|
||||
ui::setoffset(0, 0);
|
||||
ui::box(midx,8,11,12,COLOR_WHITE);
|
||||
ui::printrect(midx+2,8, 8,1, COLOR_DARK);
|
||||
@@ -545,7 +575,8 @@ namespace z80debug
|
||||
c1 = c2 = COLOR_YELLOW;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -618,12 +649,12 @@ namespace z80debug
|
||||
ui::printtxt(1,i, tohex(mem_viewer_cursor, 4), COLOR_CYAN);
|
||||
for (int j=0; j<16; ++j) {
|
||||
|
||||
const uint8_t tag = z80::getMemTag(mem_viewer_cursor);
|
||||
const uint32_t color = tag==MEMTAG_NONE ? COLOR_GRAY : tag==MEMTAG_DATA ? COLOR_BLUE : tag==MEMTAG_MIXED ? COLOR_MAGENTA : COLOR_GREEN;
|
||||
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(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);
|
||||
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++;
|
||||
}
|
||||
//printtxt(5,0, "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", COLOR_WHITE);
|
||||
@@ -786,20 +817,22 @@ namespace z80debug
|
||||
|
||||
if (strcmp(cmd, "s")==0 || strcmp(cmd, "step")==0) {
|
||||
uint8_t dt = z80::step();
|
||||
zx_tape::update(dt);
|
||||
zx_ula::sound_update(dt);
|
||||
|
||||
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) {
|
||||
z80::step();
|
||||
z80debug::cont();
|
||||
z80analyze::refresh();
|
||||
} else if (strcmp(cmd, "r")==0 || strcmp(cmd, "reset")==0) {
|
||||
uint8_t *mem = z80::getMem();
|
||||
for (int i=0x4000; i<=0xffff; ++i) mem[i]=0;
|
||||
z80::reset(mem);
|
||||
z80::connect_port(0xfe, zx_ula::port_in, zx_ula::port_out);
|
||||
//for (int i=0; i<65536; ++i) breakpoints[i]=0;
|
||||
z80::reset();
|
||||
//z80::connect_port(0xfe, 0x0001, zx_ula::port_in, zx_ula::port_out);
|
||||
history::reset();
|
||||
z80debug::refresh();
|
||||
z80analyze::refresh();
|
||||
} else if (strcmp(cmd, "b")==0 || strcmp(cmd, "break")==0) {
|
||||
@@ -874,18 +907,24 @@ namespace z80debug
|
||||
} else if (strcmp(cmd, "play")==0) {
|
||||
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) {
|
||||
getcmd();
|
||||
int address = getnum(cmd);
|
||||
getcmd();
|
||||
int value = getnum(cmd);
|
||||
uint8_t *mem = z80::getMem();
|
||||
mem[address] = value;
|
||||
mem::writeMem(address, value);
|
||||
} else if (strcmp(cmd, "peek")==0) {
|
||||
getcmd();
|
||||
int address = getnum(cmd);
|
||||
uint8_t *mem = z80::getMem();
|
||||
int value = mem[address];
|
||||
int value = mem::readMem(address);
|
||||
char tmp[10];
|
||||
sendToConsoleLog(SDL_itoa(value, tmp, 10));
|
||||
} else if (strcmp(cmd, "reg")==0) {
|
||||
@@ -898,12 +937,13 @@ namespace z80debug
|
||||
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, "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 if (strcmp(cmd, "g")==0 || strcmp(cmd, "goto")==0) {
|
||||
getcmd();
|
||||
int address = getnum(cmd);
|
||||
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);
|
||||
} else if (strcmp(cmd, "sym")==0 || strcmp(cmd, "symbol")==0) {
|
||||
getcmd();
|
||||
@@ -954,8 +994,8 @@ namespace z80debug
|
||||
}
|
||||
} else if (strcmp(cmd, "ignore")==0) {
|
||||
getcmd();
|
||||
int value = getnum(cmd);
|
||||
z80::setMemTag(value, MEMTAG_IGNORE);
|
||||
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) {
|
||||
@@ -963,6 +1003,15 @@ namespace z80debug
|
||||
} 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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -994,6 +1043,7 @@ namespace z80debug
|
||||
|
||||
void loadngo(const char* filename, const char* addr)
|
||||
{
|
||||
/*
|
||||
int address = getnum(addr);
|
||||
if (address<0 || address>65536) { sendToConsoleLog("Illegal offset"); return; }
|
||||
|
||||
@@ -1001,36 +1051,39 @@ namespace z80debug
|
||||
fseek(f, 0, SEEK_END);
|
||||
int size = ftell(f);
|
||||
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);
|
||||
fclose(f);
|
||||
z80::setPC(address);
|
||||
is_debugging = is_paused = false;
|
||||
*/
|
||||
}
|
||||
|
||||
void savestate(const char *filename)
|
||||
{
|
||||
uint8_t *regs = z80::getRegs();
|
||||
uint8_t *memory = z80::getMem();
|
||||
FILE *f = fopen(filename, "wb");
|
||||
fwrite(regs, 31, 1, f);
|
||||
fwrite(&memory[0x4000], 0xc000, 1, f);
|
||||
mem::saveState(f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void loadstate(const char *filename)
|
||||
{
|
||||
uint8_t *regs = z80::getRegs();
|
||||
uint8_t *memory = z80::getMem();
|
||||
FILE *f = fopen(filename, "rb");
|
||||
fread(regs, 31, 1, f);
|
||||
fread(&memory[0x4000], 0xc000, 1, f);
|
||||
mem::loadState(f);
|
||||
fclose(f);
|
||||
history::store();
|
||||
history::gototop();
|
||||
}
|
||||
|
||||
void setcursor(const uint16_t address)
|
||||
{
|
||||
if (z80::getMemTag(address)!=MEMTAG_INST)
|
||||
//if (!debugging()) return;
|
||||
if ( !(mem::getTag(address) & MEMTAG_INST) )
|
||||
cursor = find_previous_opcode(address);
|
||||
else
|
||||
cursor = address;
|
||||
@@ -1120,9 +1173,8 @@ namespace z80debug
|
||||
while (*seq!=0) search_sequence[search_sequence_len++] = (hexchartonum(*(seq++))<<4) + hexchartonum(*(seq++));
|
||||
}
|
||||
int num_found=0;
|
||||
uint8_t *memory = z80::getMem();
|
||||
while (search_pos<65536) {
|
||||
if (search_sequence[num_found] == memory[search_pos]) {
|
||||
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;
|
||||
@@ -1138,6 +1190,12 @@ namespace z80debug
|
||||
|
||||
namespace history
|
||||
{
|
||||
void reset()
|
||||
{
|
||||
buffer[0]=buffer[1]=0;
|
||||
cursor=pos=top=0;
|
||||
}
|
||||
|
||||
void store()
|
||||
{
|
||||
buffer[++top] = z80::getPC();
|
||||
|
||||
@@ -44,6 +44,7 @@ namespace z80debug
|
||||
void search(const char *seq=nullptr);
|
||||
namespace history
|
||||
{
|
||||
void reset();
|
||||
void store();
|
||||
void gototop();
|
||||
void goforward();
|
||||
|
||||
52
z80dis.cpp
52
z80dis.cpp
@@ -1,7 +1,8 @@
|
||||
#include "z80dis.h"
|
||||
#include "z80.h"
|
||||
#include "zx_mem.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "z80.h"
|
||||
#include <vector>
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
@@ -68,44 +69,41 @@ namespace z80dis
|
||||
|
||||
const char *getBase(const uint16_t pos)
|
||||
{
|
||||
const uint8_t *memory = &z80::getMem()[pos];
|
||||
|
||||
if (*memory == 0xCB) {
|
||||
if (mem::readMem(pos) == 0xCB) {
|
||||
opcode_size=2;
|
||||
return cb_opcodes[*(memory+1)];
|
||||
} else if (*memory == 0xDD) {
|
||||
if (*(memory+1) == 0xCB) {
|
||||
return cb_opcodes[mem::readMem(pos+1)];
|
||||
} else if (mem::readMem(pos) == 0xDD) {
|
||||
if (mem::readMem(pos+1) == 0xCB) {
|
||||
opcode_size=4;
|
||||
return ix_bit_opcodes[*(memory+3)];
|
||||
return ix_bit_opcodes[mem::readMem(pos+3)];
|
||||
} else {
|
||||
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;
|
||||
return misc_opcodes[(*(memory+1))-64];
|
||||
} else if (*memory == 0xFD) {
|
||||
if (*(memory+1) == 0xCB) {
|
||||
return misc_opcodes[(mem::readMem(pos+1))-64];
|
||||
} else if (mem::readMem(pos) == 0xFD) {
|
||||
if (mem::readMem(pos+1) == 0xCB) {
|
||||
opcode_size=4;
|
||||
return iy_bit_opcodes[*(memory+3)];
|
||||
return iy_bit_opcodes[mem::readMem(pos+3)];
|
||||
} else {
|
||||
opcode_size=2;
|
||||
return iy_opcodes[*(memory+1)];
|
||||
return iy_opcodes[mem::readMem(pos+1)];
|
||||
}
|
||||
} else {
|
||||
opcode_size=1;
|
||||
return base_opcodes[*memory];
|
||||
return base_opcodes[mem::readMem(pos)];
|
||||
}
|
||||
}
|
||||
|
||||
void printOpcode(const uint16_t pos)
|
||||
{
|
||||
const uint8_t *memory = &z80::getMem()[pos];
|
||||
char hex[4];
|
||||
for (int i=0; i<4;++i)
|
||||
{
|
||||
if (opcode_size>i)
|
||||
sprintf(hex, "%02x ", *(memory+i));
|
||||
sprintf(hex, "%02x ", mem::readMem(pos+i));
|
||||
else
|
||||
sprintf(hex, " ");
|
||||
strcat(buffer, hex);
|
||||
@@ -114,8 +112,6 @@ namespace z80dis
|
||||
|
||||
const char *getAsm(const uint16_t pos)
|
||||
{
|
||||
const uint8_t *memory = &z80::getMem()[pos];
|
||||
|
||||
opcode_size = 0;
|
||||
char base[256];
|
||||
strcpy(buffer, getBase(pos));
|
||||
@@ -123,7 +119,9 @@ namespace z80dis
|
||||
if (strstr(buffer, "4x"))
|
||||
{
|
||||
opcode_size+=2;
|
||||
const uint16_t word = *(uint16_t*)(memory+((*memory==0xFD) || (*memory==0xDD)?2: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) {
|
||||
char *p = strstr(buffer, "$");
|
||||
(*p)='%'; p++;
|
||||
@@ -144,7 +142,7 @@ namespace z80dis
|
||||
{
|
||||
opcode_size = 4;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -152,7 +150,7 @@ namespace z80dis
|
||||
{
|
||||
opcode_size+=1;
|
||||
strcpy(base, buffer);
|
||||
sprintf(buffer, base, *(memory+1));
|
||||
sprintf(buffer, base, mem::readMem(pos+1));
|
||||
}
|
||||
|
||||
if (strstr(buffer, "Xx"))
|
||||
@@ -163,9 +161,9 @@ namespace z80dis
|
||||
|
||||
if (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 {
|
||||
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);
|
||||
if (opcode_size>4) {
|
||||
opcode_size=4;
|
||||
sprintf(buffer, base, (int8_t)*(memory+3));
|
||||
sprintf(buffer, base, (int8_t)mem::readMem(pos+3));
|
||||
} else {
|
||||
sprintf(buffer, base, (int8_t)*(memory+2));
|
||||
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);
|
||||
}
|
||||
133
zx_screen.cpp
133
zx_screen.cpp
@@ -1,11 +1,14 @@
|
||||
#include "zx_screen.h"
|
||||
#include "z80.h"
|
||||
#include "zx_mem.h"
|
||||
#include "zx_ula.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include "zx_tape.h"
|
||||
#include "ui_window.h"
|
||||
#include "z80debug.h"
|
||||
#include "ui.h"
|
||||
#include "file.h"
|
||||
//#include "zx_128mem.h"
|
||||
|
||||
namespace zxscreen
|
||||
{
|
||||
@@ -18,7 +21,14 @@ namespace zxscreen
|
||||
SDL_Texture *tex = 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 full_refresh = true;
|
||||
int fullscreen_scale = 1;
|
||||
@@ -30,45 +40,63 @@ namespace zxscreen
|
||||
uint8_t t_flash = 0;
|
||||
bool flash = false;
|
||||
|
||||
int pixels_draw = 0;
|
||||
uint32_t pixel_base_addr = 0x4000;
|
||||
uint32_t color_base_addr = 0x5800;
|
||||
|
||||
uint16_t pixel_addr[69888];
|
||||
uint16_t color_addr[69888];
|
||||
uint32_t *pixel_addr = nullptr; //[69888];
|
||||
uint32_t *color_addr = nullptr; //[69888];
|
||||
uint8_t zx_pixels[352*296];
|
||||
uint8_t *ptr_pixel = zx_pixels;
|
||||
|
||||
#define SCREEN_SYNC 0xFFFF
|
||||
#define SCREEN_BORDER 0xFFFE
|
||||
|
||||
void saveWindowConfiguration()
|
||||
{
|
||||
file::setConfigValueInteger("screen_zoom", zoom);
|
||||
int x, y;
|
||||
SDL_GetWindowPosition(win, &x, &y);
|
||||
file::setConfigValueInteger("screen_x", x);
|
||||
file::setConfigValueInteger("screen_y", y);
|
||||
}
|
||||
|
||||
void create_tables()
|
||||
{
|
||||
uint32_t count = 0;
|
||||
|
||||
uint16_t *ptr_pixel = pixel_addr;
|
||||
uint16_t *ptr_color = color_addr;
|
||||
if (pixel_addr) free(pixel_addr);
|
||||
if (color_addr) free(color_addr);
|
||||
pixel_addr = (uint32_t*)malloc(t_states_total*sizeof(uint32_t));
|
||||
color_addr = (uint32_t*)malloc(t_states_total*sizeof(uint32_t));
|
||||
|
||||
uint32_t *ptr_pixel = pixel_addr;
|
||||
uint32_t *ptr_color = color_addr;
|
||||
|
||||
// vsync
|
||||
for (int i=0; i<224*16;++i) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; }
|
||||
for (int i=0; i<t_states_per_scanline*vsync_lines;++i) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 16 passa a ser 15 i 224 passa a ser 228
|
||||
|
||||
// Upper border
|
||||
for (int i=0; i<48;++i) {
|
||||
// hsync
|
||||
for (int j=0;j<48;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; }
|
||||
for (int j=0;j<t_states_per_scanline-176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 48 passsa a ser 52
|
||||
//border
|
||||
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; }
|
||||
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
|
||||
}
|
||||
|
||||
// scanlines
|
||||
for (uint8_t y=0; y<192; ++y)
|
||||
{
|
||||
// hsync
|
||||
for (int j=0;j<48;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; }
|
||||
for (int j=0;j<t_states_per_scanline-176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 48 passsa a ser 52
|
||||
|
||||
// Left border
|
||||
for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; }
|
||||
for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
|
||||
|
||||
// Actual screen
|
||||
for (uint8_t x=0;x<32;++x)
|
||||
{
|
||||
uint16_t color = 0x5800 + x + (y>>3)*32;
|
||||
uint16_t address = 0x4000 | (x&0x1f) | ((y&0x7)<<8) | ((y&0x38)<<2) | ((y&0xc0)<<5);
|
||||
uint16_t color = /*0x5800 +*/ x + (y>>3)*32;
|
||||
uint16_t address = /*0x4000 |*/ (x&0x1f) | ((y&0x7)<<8) | ((y&0x38)<<2) | ((y&0xc0)<<5);
|
||||
for (int i=7;i>0;i-=2)
|
||||
{
|
||||
*(ptr_pixel++) = (address & 0x1FFF) | (i << 13);
|
||||
@@ -78,15 +106,15 @@ namespace zxscreen
|
||||
}
|
||||
|
||||
// Right border
|
||||
for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; }
|
||||
for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
|
||||
}
|
||||
|
||||
// Lower border
|
||||
for (int i=0; i<56;++i) {
|
||||
// hsync
|
||||
for (int j=0;j<48;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; }
|
||||
for (int j=0;j<t_states_per_scanline-176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 48 passsa a ser 52
|
||||
//border
|
||||
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; }
|
||||
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
|
||||
}
|
||||
//printf("COUNT: %i\n", count);
|
||||
}
|
||||
@@ -94,8 +122,16 @@ namespace zxscreen
|
||||
bool eventHandler(SDL_Event *e)
|
||||
{
|
||||
if (e->type==SDL_WINDOWEVENT) {
|
||||
//int x, y;
|
||||
//SDL_GetWindowPosition(win, &x, &y);
|
||||
//char tmp[256];
|
||||
//sprintf(tmp, " %ix%i", x, y);
|
||||
//setTitle(tmp);
|
||||
if (e->window.event==SDL_WINDOWEVENT_CLOSE) {
|
||||
saveWindowConfiguration();
|
||||
return false;
|
||||
} else if (e->window.event==SDL_WINDOWEVENT_FOCUS_LOST || e->window.event==SDL_WINDOWEVENT_MOVED) {
|
||||
saveWindowConfiguration();
|
||||
} else if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) {
|
||||
redraw();
|
||||
}
|
||||
@@ -104,6 +140,7 @@ namespace zxscreen
|
||||
if (z80debug::paused()) {
|
||||
if (e->type == SDL_KEYDOWN) {
|
||||
if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) {
|
||||
ui::setDialog(nullptr);
|
||||
const uint8_t dt = z80::step();
|
||||
z80debug::cont();
|
||||
zxscreen::refresh(dt);
|
||||
@@ -128,11 +165,15 @@ namespace zxscreen
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e->type == SDL_MOUSEMOTION) {
|
||||
SDL_ShowCursor(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void reinit()
|
||||
{
|
||||
saveWindowConfiguration();
|
||||
if (win) ui::window::unregisterWindow(SDL_GetWindowID(win));
|
||||
|
||||
if (tex) SDL_DestroyTexture(tex);
|
||||
@@ -140,8 +181,12 @@ namespace zxscreen
|
||||
if (ren) SDL_DestroyRenderer(ren);
|
||||
if (win) SDL_DestroyWindow(win);
|
||||
|
||||
zoom = file::getConfigValueInteger("screen_zoom", 1);
|
||||
const int x = file::getConfigValueInteger("screen_x", SDL_WINDOWPOS_UNDEFINED);
|
||||
const int y = file::getConfigValueInteger("screen_y", SDL_WINDOWPOS_UNDEFINED);
|
||||
|
||||
const int z = fullscreen ? 1 : zoom;
|
||||
win = SDL_CreateWindow("ZX Spectrum Screen", 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);
|
||||
tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 352, 296);
|
||||
uitex = ui::createtexture(ren);
|
||||
@@ -168,9 +213,31 @@ namespace zxscreen
|
||||
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();
|
||||
ptr_pixel = zx_pixels;
|
||||
t_screen = t_flash = 0;
|
||||
flash = false;
|
||||
reinit();
|
||||
}
|
||||
|
||||
@@ -185,48 +252,53 @@ namespace zxscreen
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
} 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;
|
||||
if ((color&0x80) && flash) { c1=c2; c2=color&0x7; }
|
||||
if ((color&0x40)) { c1 |= 0x8; c2 |= 0x8; }
|
||||
uint16_t address = (0x4000) | (pixel_addr[t_screen]&0x1FFF);
|
||||
uint16_t address = /*(0x4000) |*/ (pixel_addr[t_screen]&0x1FFF);
|
||||
uint8_t mask = 1 << (pixel_addr[t_screen]>>13);
|
||||
uint8_t block = memory[address];
|
||||
uint8_t block = *(pixel_mem + address); // z80mem::get()->readMem(pixel_base_addr + address);
|
||||
*(ptr_pixel++)=(block&mask) ? c1 : c2;
|
||||
mask>>=1;
|
||||
*(ptr_pixel++)=(block&mask) ? c1 : c2;
|
||||
}
|
||||
pixels_draw+=2;
|
||||
}
|
||||
t_screen++;
|
||||
if (t_screen>=69888) {
|
||||
pixels_draw=0;
|
||||
if (t_screen>=t_states_total) {
|
||||
t_flash++;
|
||||
if (t_flash==16) { t_flash=0; flash = !flash; }
|
||||
t_screen=0;
|
||||
ptr_pixel = zx_pixels;
|
||||
redraw();
|
||||
z80::interrupt();
|
||||
if (interrupt_enabled) z80::interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fullrefresh()
|
||||
{
|
||||
uint32_t tmp = t_screen;
|
||||
t_screen = 0;
|
||||
uint8_t * tmp_ptr = ptr_pixel;
|
||||
ptr_pixel = zx_pixels;
|
||||
refresh(69888);
|
||||
interrupt_enabled = false;
|
||||
refresh(t_states_total);
|
||||
interrupt_enabled = true;
|
||||
ptr_pixel = tmp_ptr;
|
||||
t_screen = tmp;
|
||||
}
|
||||
|
||||
void debugrefresh(const uint32_t dt)
|
||||
@@ -255,6 +327,9 @@ namespace zxscreen
|
||||
// Pintem la textura a pantalla
|
||||
SDL_RenderCopy(ren, tex, NULL, &dest_rect);
|
||||
|
||||
//zx_128mem* mem = ((zx_128mem*)z80mem::get());
|
||||
//ui::printtxt(0,0,mem->getShadowScreen()?"SHADOW":"NORMAL", COLOR_WHITE);
|
||||
|
||||
if (present)
|
||||
SDL_RenderPresent(ren);
|
||||
else
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#pragma once
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#define SCREEN_MODE_48K 0
|
||||
#define SCREEN_MODE_128K 1
|
||||
|
||||
namespace zxscreen
|
||||
{
|
||||
void init();
|
||||
void init(int mode);
|
||||
void setBaseAddresses(const uint32_t pixeladdr, const uint32_t coloraddr);
|
||||
void reinit();
|
||||
void focus();
|
||||
void refresh(const uint32_t dt);
|
||||
|
||||
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_ula.h"
|
||||
#include "zx_screen.h"
|
||||
#include "zx_mem.h"
|
||||
#include "z80debug.h"
|
||||
#include "z80.h"
|
||||
#include <vector>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -93,7 +95,7 @@ namespace zx_tape
|
||||
pulse_level = 1;
|
||||
}
|
||||
|
||||
void update(const uint8_t dt)
|
||||
void update(const uint32_t dt)
|
||||
{
|
||||
if (!playing) return;
|
||||
|
||||
@@ -201,7 +203,7 @@ namespace zx_tape
|
||||
pulse_pos -= PULSE_LEN_SYNC3;
|
||||
pulse_level = 0;
|
||||
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)
|
||||
{
|
||||
pulse_level = 0;
|
||||
if (pulse_pos >= 3500000)
|
||||
if (pulse_pos >= z80::getClock())
|
||||
{
|
||||
pulse_pos = 0;
|
||||
current_section = PULSE_PILOT;
|
||||
@@ -243,6 +245,27 @@ namespace zx_tape
|
||||
printf("tape loading: %i%\n", percent);
|
||||
}
|
||||
|
||||
uint16_t fastLoad(const uint8_t block_type, const uint16_t address, const uint16_t length)
|
||||
{
|
||||
block_pos=0;
|
||||
current_bit=0;
|
||||
current_section = PULSE_PILOT;
|
||||
current_pulse = 0;
|
||||
pulse_pos = 0;
|
||||
pulse_level = 0;
|
||||
|
||||
if (blocks[current_block].data[0] != block_type ||
|
||||
blocks[current_block].length != length+2) {
|
||||
printf("ERROR: Tape data not consistent with expectation\n");
|
||||
z80debug::stop();
|
||||
}
|
||||
for (int i=0;i<length;++i) {
|
||||
mem::writeMem(address+i, blocks[current_block].data[i+1]);
|
||||
}
|
||||
current_block++;
|
||||
return address + length;
|
||||
}
|
||||
|
||||
const bool getOption(const int option)
|
||||
{
|
||||
return options[option];
|
||||
|
||||
@@ -11,9 +11,10 @@ namespace zx_tape
|
||||
void play();
|
||||
void stop();
|
||||
void rewind();
|
||||
void update(const uint8_t dt);
|
||||
void update(const uint32_t dt);
|
||||
const bool getplaying();
|
||||
void report();
|
||||
uint16_t fastLoad(const uint8_t block_type, const uint16_t address, const uint16_t length);
|
||||
|
||||
const bool getOption(const int option);
|
||||
void setOption(const int option, const bool value);
|
||||
|
||||
111
zx_ula.cpp
111
zx_ula.cpp
@@ -1,8 +1,7 @@
|
||||
#include "zx_ula.h"
|
||||
#include "z80.h"
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#define AUDIO_BUFFER_SIZE 2048
|
||||
|
||||
namespace zx_ula
|
||||
{
|
||||
#define KEY_SHIFT 0
|
||||
@@ -55,16 +54,20 @@ namespace zx_ula
|
||||
|
||||
uint8_t zx_keyboard[40];
|
||||
uint8_t virtual_keyboard[40] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||
char key_to_keyboard[40] {'<','Z','X','C','V','A','S','D','F','G','Q','W','E','R','T','1','2','3','4','5','0','9','8','7','6','P','O','I','U','Y','-','L','K','J','H','.',',','M','N','B'};
|
||||
char key_to_keyboard[40] {
|
||||
'<','Z','X','C','V',
|
||||
'A','S','D','F','G',
|
||||
'Q','W','E','R','T',
|
||||
'1','2','3','4','5',
|
||||
'0','9','8','7','6',
|
||||
'P','O','I','U','Y',
|
||||
'-','L','K','J','H',
|
||||
'.',',','M','N','B'
|
||||
};
|
||||
static uint8_t border_color = 0;
|
||||
static uint8_t ear = 0;
|
||||
static uint8_t mic = 0;
|
||||
|
||||
// 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()
|
||||
{
|
||||
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
|
||||
if (keys[SDL_SCANCODE_BACKSPACE]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_0] = 1;
|
||||
if (keys[SDL_SCANCODE_PERIOD]) zx_keyboard[KEY_SYMBOL] = zx_keyboard[KEY_M] = 1;
|
||||
|
||||
if (keys[SDL_SCANCODE_UP]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_7] = 1;
|
||||
if (keys[SDL_SCANCODE_DOWN]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_6] = 1;
|
||||
if (keys[SDL_SCANCODE_LEFT]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_5] = 1;
|
||||
if (keys[SDL_SCANCODE_RIGHT]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_8] = 1;
|
||||
|
||||
}
|
||||
|
||||
int port_in(int port)
|
||||
@@ -214,93 +223,13 @@ namespace zx_ula
|
||||
}
|
||||
|
||||
uint8_t get_border_color() { return border_color; }
|
||||
void set_border_color(uint8_t col) { border_color = col; }
|
||||
uint8_t get_ear() { return ear; }
|
||||
void set_ear(const uint8_t val) { ear = val; }
|
||||
|
||||
|
||||
|
||||
|
||||
SDL_AudioDeviceID sdlAudioDevice;
|
||||
uint8_t sound_buffer[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)
|
||||
uint8_t get_sample()
|
||||
{
|
||||
//const uint16_t top = sound_pos < len ? sound_pos : len;
|
||||
//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;
|
||||
}
|
||||
return ear*128;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
uint8_t get_border_color();
|
||||
void set_border_color(uint8_t col);
|
||||
uint8_t get_ear();
|
||||
void set_ear(const uint8_t val);
|
||||
|
||||
void sound_init();
|
||||
void sound_enable();
|
||||
void sound_disable();
|
||||
void sound_update(const uint8_t dt);
|
||||
uint8_t get_sample();
|
||||
|
||||
void keydown(const char key);
|
||||
void keyup(const char key);
|
||||
|
||||
Reference in New Issue
Block a user