Compare commits

...

52 Commits

Author SHA1 Message Date
afa59cdb5a - [FIX] [zx_dialog_joystick] No es podia seleccionar quin joystick configurar
- [FIX] al recrear una finestra de vegades es perdia la textura de les fonts de la UI.
2025-08-16 18:22:02 +02:00
6837392c53 - [NEW] Ja funciona el joystick tipus Kempston 2025-08-16 16:59:26 +02:00
1fdfeedacc - [NEW] Es pot accedir al dialeg de configuracio dels joysticks des del menú 2025-08-16 11:23:16 +02:00
8201d51668 - [NEW] Dialeg per a configurar els joysticks 2025-08-16 10:09:42 +02:00
864c6b929d - [NEW] Soport per a joystick tipo Sinclair 2025-08-15 22:58:53 +02:00
0530c255c1 - Quatre mamonaes que he fet en el portatil 2025-08-15 18:17:09 +02:00
78a4c84db2 - [FIX] [zx_disk] Usava el index dels sectors en compte del id.
- [ONGOING] READ DELETED DATA command (0x0c) (no funciona)
2025-08-07 19:02:31 +02:00
5516f463bf - [NEW] Càrrega de disks des del debuger
- [FIX] Al carregar un nou disk s'alliberava mal la memòria
2025-08-07 17:53:01 +02:00
b69da56526 - Afegit lagueirtofile pa compilar FASTuosament
- [NEW] [zx_disk] funcions de debug
- [FIX] current_byte era un uint8_t i tenia que ser al menys uint16_t, per això fallava el READ DATA.
2025-08-07 16:06:42 +02:00
884e509d67 - [NEW] [zx_disk] READ DATA command (0x06) 2025-08-07 10:35:49 +02:00
8b36807e3b - [NEW] [zx_disk] Càrrega d'arxius .DSK (nomes extended, hardcoded)
- [NEW] [zx_disk] READ ID command (0x0A)
2025-08-06 20:02:54 +02:00
6791916f75 [NEW] [zx_disk] SEEK command (0x0f) [completat] 2025-08-06 10:31:02 +02:00
90b34749d1 - [NEW] [zx_disk] RECALIBRATE command (0x07)
- [NEW] [zx_disk] SENSE INTERRUPT STATUS command (0x08)
- [ONGOING] [zx_disk] SEEK command (0x0f)
2025-08-05 17:05:32 +02:00
40c825cdb9 - [NEW] [zx_disk] SPECIFY command (0x03)
- [NEW] [zx_disk] SENSE DRIVE STATUS command (0x04)
2025-08-05 11:31:36 +02:00
ade97a9a70 - [NEW] Mode +2A/+3 funcionant, a falta de la unitat de disc 2025-08-05 08:24:45 +02:00
9eb8662ec7 - Llevats mòduls que ja no s'usaven i afegits DSKs del goldenaxe, normals i amb protecció 2025-07-31 13:36:07 +02:00
1db0c52e1a - Enorme reestructuració del codi per a que el fluxe comence a ser mes racional
- [NEW] mòdul zx_system per a gestionar la vida i canvi de systemes (48K, 128K...)
2025-07-30 13:01:01 +02:00
2775da3d53 - [NEW] Càrrega "instantànea" de TAPs 2025-07-29 12:51:40 +02:00
2a0febc6b7 - [NEW] Les finestres guarden la posició i el zoom 2025-07-29 11:29:35 +02:00
913450fadb - [NEW] Zoom per a la pantalla retina
- Treballant en la càrrega instantànea de TAPs
2025-07-29 10:05:06 +02:00
fee07b6e1b - [NEW] [ui] incoffset() i panel()
- [NEW] mòdul ay_viewer
- [NEW] [ay-3-8912] afegits mètodes de debug
- [FIX] [ay-3-8912]Arreglats uns quants tipus de dades
- [FIX] [ay-3-8912]Arreglat el càlcul de la frequència (en realitat amplitud) dels tonos
- [FIX] [ay-3-8912] Arreglat el algoritme de càlcul del roido
2025-07-25 13:08:20 +02:00
662583be36 - Ja sona! pero les freqüències estàn mal (massa altes). Demà més. 2025-07-24 22:30:47 +02:00
9725e58d92 - Adaptat el mòdul 'ay-3-8912' per a usar zx_speaker, pero encara no fa roidets, a vore si demà trobe el perquè 2025-07-24 21:37:01 +02:00
91a8933544 - [NEW] mòdul 'zx_speaker' per a unificar la eixida de só 2025-07-24 19:39:57 +02:00
780afbc6a8 - [FIX] Arreglat el audio del 48K amb lo aprés del emu de gameboy 2025-07-24 18:41:09 +02:00
1cde51f2d4 - Treballant en el só 2025-07-24 12:42:23 +02:00
42da652aef - [NEW] Implementada decodificació parcial dels ports, tal i com ho fa el spectrum 2025-07-24 08:37:20 +02:00
0df17bf4c9 - [NEW] Comencem a implementar el AY-3-8912 2025-07-23 22:00:58 +02:00
6e3e8e9b69 - [FIX] Corregit el acces als port amb OUT
- [NEW] Nou model de gestió de memòria
2025-07-23 13:51:51 +02:00
3fd28136f6 - [NEW] ui::placetext() i ui::placechar, pa ficar text en qualsevol pixel
- [NEW] [Z80Analize] backspace esborra tots els tags
- [NEW] [zx-128bankviewer] Es mostra quina pàgina de memòria està asignada a cada bank
- [FIX] [zx_128mem] es filtra el port al que escolta la memòria
- [NEW] [zx_screen] Es mostra en quina pantalla estem (normal o shadow)
- [FIX] [zx_screen] tots els tipos per al calcul de adreces passats a uint32_t
- [NEW] [zx_ula] afegides combinacions de cursors per a major comoditat
2025-07-22 13:31:21 +02:00
4b4e1df8f9 - What's the story morning commit 2025-07-22 06:27:56 +02:00
13354b855d - the big mergecheit 2025-07-21 19:25:29 +02:00
300f95803e - [FIX] No calculava be l'adreça de la ROM actual en 128K
- [FIX] La ROM accedeix al pot 0x7ffd "the torerous menner", he tingut que ficar un apanyo
2025-07-21 14:00:31 +02:00
ab476a19b1 - [NEW] Implementat el mòdul de memòria per al ZX Spectrum 128K
- Primera prova falla, mirar la conexió al port 0x7ffd, que pareix que no ana.
2024-12-20 13:18:55 +01:00
184389a89e - [NEW] zx_screen preparat per al ZX Spectrum 128k
- [NEW] Afegida rom del ZX Spectrum 128K
2024-12-20 12:15:26 +01:00
fe36a970c2 - [NEW] Frequència del Z80 configurable 2024-12-20 11:24:58 +01:00
4a0e2b3b7d - [NEW] Adaptat zx_screen per a que reba el offset en memoria on està la memòria de video, per a ser mes modular. Pero crec que les diferencies en el timing van a fer necessari un modul nou per a cada spectrum. Ja vorem quan estiga mes descansat. 2024-12-19 22:33:58 +01:00
68843ab6b3 - [FIX] Solventats els bugs de la memòria modular amb classes 2024-12-19 20:57:28 +01:00
dbd80694aa - [NEW] Treballant en modularitzar la memòria, per a començar a implementar soport per als demes Spectrums 2024-12-19 17:36:22 +01:00
da4c692283 - [NEW] Implementació cutre i que no funciona de encendre el cassette al fer LOAD"", pero es que me pire a casa 2024-12-18 17:34:12 +01:00
b45c93d8a2 - [FIX] Ja ignora correctament combinacions incorrectes de opcodes DD i FD. 2024-12-18 14:53:44 +01:00
6f45044a9a - [CHG] En proves: fer un IN a un port no usat ara torna 0x00 en compte de 0xFF. Probablement hi haurà que revertir-ho.
- [NEW] Amb F9 es pot ficar o llevar un breakpoint en l'adreça on estiga el cursor del desensamblador.
- [NEW] Nou comando de la consola "show analyzer"
- [FIX] Quan es fa un full refresh mentres se debugga no ha de causar interrupcions. A més, ara mantenim els t_states i el punter a pantalla.
- [FIX] La instrucció CPIR llegia mal la memòria apuntada per HL al considerar si hi havia coincidencia
2024-12-18 13:22:56 +01:00
bdec53eb97 - [FIX] Els opcodes DD amb paràmetre de 16 bits mostraven mal l'adreça
- [FIX] Al carregar un estat el contador de programa no mantenia l'adreça correcta
- [NEW] Afegit al analitzador la visualització de escritura de dades
- [NEW] Deshabilite les interrupcions al entrar a una interrupció. No se si fa falta.
- [NEW] Afegit el Batman de Jon Ritman pa provar
2024-12-17 17:40:42 +01:00
dfcc0a26fe - [FIX] El cursor es mostra sempre damunt de les finestres zx_screen i z80debug
- [FIX] el dibuixat del crosshair en la finestra z80analyze causaba potencialment una escritura fora de rang
- [NEW] Refresc de la finestra z80analyze millorat
2024-12-17 12:03:04 +01:00
620cd8d88c - [FIX] Si no hi havia coincidencia la busqueda no paraba mai
- [FIX] RLCA i RRCA sempre activaben el flag de carry, independentment del valor
2024-12-17 09:32:55 +01:00
83b6782078 [NEW] Inclos un mode de pantalla en que s'actualitze al instant en compte de segons el retraç del crt, per a debuggar que se pinta 2024-12-16 16:31:53 +01:00
f7a7b0692d - [NEW] Inspector de memòria. Pasar per damunt d'un valor per a vore'l en 16bits, les dos parts de 8 bits, i amb signe. 2024-12-15 12:44:23 +01:00
0a758bbb33 - [NEW] F12 to StepOut() on debugger (break on RET, RETI or RETN)
- [FIX] Fixed visualizacion of some IX and IY opcodes
- [NEW] Scroll on memory viewer with mouse wheel
- [NEW] While debugging, on each step the screen refreshes, so screen redraw can be seen while happening
- [NEW] Search for sequences of bytes. Example: "search AB1140" to search for the sequece $AB $11 $40. "search next" to continue searching.
2024-12-14 20:46:46 +01:00
14d047cbb9 - [NEW] En el debugger, en el visor de memòria, tambe es veu cada byte del color del seu tag
- [NEW] En el debugger, en el visor de breakpoints, se marca uno si estem en eixe breakpoint
- [NEW] Opció per a sincronitzar el cursor del visor de memòria amb el cursor del desensamblador
- [NEW] Click on breakpoint to goto its address
- [FIX] IX, IX bit, IY i IY bit opcodes with displacement wher shown wrong on the disassembler
- [NEW] Symbols module shows a symbol highlighted if the cursor is on its address
- [NEW] Click on a symbol to move the cursor to its address
2024-12-14 12:22:30 +01:00
6768c01c81 - [NEW] La posició del cursor se marca amb un requadre blau (no relleno) en el desensamblador
- [NEW] setcursor corregeix l'adreça si no coincideix amb una instrucció
- [FIX] Corregits els opcodes FD34 i FD35
- [NEW] Ara en el analitzador es borra o es fixa la memòria amb DELETE i RETURN
- [NEW] Fent click en un pixel del analitzador du a la posició de memòria que toca en el desensamblador
- [NEW] ui::printvoidrect()
- [NEW] la finestra del analitzador pilla el foco nomes amb pasar per damunt
- [NEW] Fent click en una linea del desensamblador fica o lleva un breakpoint en eixa adreça
- [FIX] les accions en el analitzador actualitzen la finestra del debugger
- [FIX] El cursor del analitzador nomes es deu moure al estar damunt de al finestra del analitzador
- [FIX] El desensamblador, quan el tag de memoria era MIXED no reconixia part de la instrucció
- [FIX] El desensamblador usava el color incorrecte per a codi amb el tag REPEAT
2024-12-14 09:31:52 +01:00
c9aceeb387 - [NEW] Més opcions de control del etiquetat de la memòria
- [FIX] Les instruccions DD34 i DD35 no pillaven un byte signed, sino unsigned. Han de haber-ne més. REPASAR.
- [ONGOING] Preparant el analyzer per a tindre diverses visualitzacions de la memòria
- [NEW] El debugger ara mostra el etiquetat de la memòria en el desensamblador
- [FIX] El cursor ja se torna a vore en el debugger
2024-12-13 13:52:41 +01:00
8c197d5519 - [FIX] Al fer break on interrupt de vegades se passava de instruccions
- [NEW] el analitzador pot mostrar les instruccions repetides des de l'ultim estat
- [NEW] gestió de opcodes usats
2024-12-12 22:44:44 +01:00
56 changed files with 4069 additions and 777 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
z80 z80
.vscode/* .vscode/*
build/*

BIN
128k.rom Normal file

Binary file not shown.

BIN
ROBOCOP1.TAP Normal file

Binary file not shown.

255
ay-3-8912.cpp Normal file
View 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
View 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
View 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
View 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

Binary file not shown.

BIN
batmanritman.tap Normal file

Binary file not shown.

View File

@@ -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
View 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
View 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
View 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
View 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

Binary file not shown.

BIN
goldenaxe1_lock.dsk Normal file

Binary file not shown.

BIN
goldenaxe2.dsk Normal file

Binary file not shown.

BIN
goldenaxe2_lock.dsk Normal file

Binary file not shown.

5
lagueirtofile Normal file
View File

@@ -0,0 +1,5 @@
libs = -lSDL2
cppflags = -g
executable = z80
sourcepath = .
buildpath = build

195
main.cpp
View File

@@ -1,9 +1,11 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include "zx_system.h"
#include "z80.h" #include "z80.h"
#include "z80dis.h" #include "z80dis.h"
#include "z80debug.h" #include "z80debug.h"
#include "zx_ula.h" #include "zx_ula.h"
#include "zx_speaker.h"
#include "zx_screen.h" #include "zx_screen.h"
#include "zx_tape.h" #include "zx_tape.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
@@ -12,8 +14,16 @@
#include "ui_menu.h" #include "ui_menu.h"
#include "z80analyze.h" #include "z80analyze.h"
#include "ui_window.h" #include "ui_window.h"
#include "zx_mem.h"
#include "z80viewer.h"
//#include "zx_128bankviewer.h"
//#include "zx_128pageviewer.h"
#include "ay-3-8912.h"
#include "ay_viewer.h"
#include "file.h"
#include "gamepad.h"
#include "zx_dialog_joystick.h"
uint8_t memory[65536];
uint32_t time = 0; uint32_t time = 0;
uint32_t t_states = 0; uint32_t t_states = 0;
@@ -26,6 +36,11 @@ namespace actions
zxscreen::refresh(dt); zxscreen::refresh(dt);
} }
void exitButNotContinue()
{
// do nothing
}
int fastload(int value) int fastload(int value)
{ {
zx_tape::toggleOption(ZXTAPE_OPTION_FAST_LOAD); zx_tape::toggleOption(ZXTAPE_OPTION_FAST_LOAD);
@@ -56,27 +71,58 @@ namespace actions
return zxscreen::getFullscreen(); return zxscreen::getFullscreen();
} }
int fullrefresh(int value)
{
zxscreen::toggleFullRefresh();
return zxscreen::getFullRefresh();
}
int showAnalyzer(int value) int showAnalyzer(int value)
{ {
z80analyze::show(); z80analyze::show();
return 0; return 0;
} }
int mode48K(int value)
{
zx_system::reset(ZX_48K);
return 0;
}
int mode128K(int value)
{
zx_system::reset(ZX_128K);
return 0;
}
int modePlus3(int value)
{
zx_system::reset(ZX_PLUS3);
return 0;
}
int reset(int value)
{
z80::reset();
return 0;
}
int exit(int value)
{
zx_system::shutdown();
return 0;
}
int configureJoysticks(int value)
{
ui::setDialog(dialogs::joysticks::show);
ui::menu::exitButNotContinue();
return 0;
}
} }
int main(int argc, char *argv[]) void init_menu()
{ {
FILE* f = fopen("48.rom", "rb");
fread(memory, 1024, 16, f);
fclose(f);
z80dis::loadSymbols();
z80::reset(memory);
z80::connect_port(0xfe, zx_ula::port_in, zx_ula::port_out);
SDL_Init(SDL_INIT_EVERYTHING);
z80debug::init();
zxscreen::init();
ui::menu::init(); ui::menu::init();
ui::menu::setexitcallback(actions::exitMenu); ui::menu::setexitcallback(actions::exitMenu);
@@ -86,39 +132,78 @@ int main(int argc, char *argv[])
ui::menu::addseparator(menu); ui::menu::addseparator(menu);
ui::menu::addoption(menu, "LOAD STATE", nullptr); ui::menu::addoption(menu, "LOAD STATE", nullptr);
ui::menu::addoption(menu, "SAVE STATE", nullptr); ui::menu::addoption(menu, "SAVE STATE", nullptr);
ui::menu::addseparator(menu);
ui::menu::addoption(menu, "EXIT", actions::exit);
menu = ui::menu::addsubmenu("SYSTEM");
ui::menu::addoption(menu, "ZX 48K", actions::mode48K);
ui::menu::addoption(menu, "ZX 128K/+2", actions::mode128K);
ui::menu::addoption(menu, "ZX +2A/+3", actions::modePlus3);
ui::menu::addseparator(menu);
ui::menu::addoption(menu, "RESET", actions::reset);
menu = ui::menu::addsubmenu("TAPE"); menu = ui::menu::addsubmenu("TAPE");
ui::menu::addbooloption(menu, "BERSERK MODE", zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD), actions::fastload); ui::menu::addbooloption(menu, "FAST LOAD", zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD), actions::fastload);
ui::menu::addbooloption(menu, "STOP AT END", zx_tape::getOption(ZXTAPE_OPTION_STOP_AT_END), actions::stopatend); ui::menu::addbooloption(menu, "STOP AT END", zx_tape::getOption(ZXTAPE_OPTION_STOP_AT_END), actions::stopatend);
menu = ui::menu::addsubmenu("SCREEN"); menu = ui::menu::addsubmenu("SCREEN");
ui::menu::addoption(menu, "DEC ZOOM", actions::decZoom); ui::menu::addoption(menu, "DEC ZOOM", actions::decZoom);
ui::menu::addoption(menu, "INC ZOOM", actions::incZoom); ui::menu::addoption(menu, "INC ZOOM", actions::incZoom);
ui::menu::addbooloption(menu, "FULLSCREEN", zxscreen::getFullscreen(), actions::fullscreen); ui::menu::addbooloption(menu, "FULLSCREEN", zxscreen::getFullscreen(), actions::fullscreen);
ui::menu::addseparator(menu);
ui::menu::addbooloption(menu, "FULL REFRESH", zxscreen::getFullRefresh(), actions::fullrefresh);
menu = ui::menu::addsubmenu("EMULATION"); menu = ui::menu::addsubmenu("EMULATION");
ui::menu::addbooloption(menu, "STOP ON INVALID OP", z80::getOption(Z80_OPTION_STOP_ON_INVALID), actions::decZoom); ui::menu::addbooloption(menu, "STOP ON INVALID OP", z80::getOption(Z80_OPTION_STOP_ON_INVALID), actions::decZoom);
ui::menu::addoption(menu, "SHOW ANALYZER", actions::showAnalyzer); ui::menu::addoption(menu, "SHOW ANALYZER", actions::showAnalyzer);
ui::menu::addoption(menu, "CONFIGURE JOYSTICKS", actions::configureJoysticks);
zx_ula::sound_init(); }
zx_tape::load("fernandomartin.tap"); int main(int argc, char *argv[])
{
file::setConfigFolder("z80");
SDL_Init(SDL_INIT_EVERYTHING);
init_menu();
z80debug::init();
if (argc==3) { z80debug::loadngo(argv[1], argv[2]); } //uint32_t update_freq =
zx_system::init(ZX_48K);
gamepad::init();
zx_tape::load("ROBOCOP1.TAP");
//if (argc==3) { z80debug::loadngo(argv[1], argv[2]); }
//z80debug::stop();
bool should_exit = false;
SDL_Event e; SDL_Event e;
time = SDL_GetTicks(); time = SDL_GetTicks();
t_states = 0; t_states = 0;
bool first_time = true;
while (!should_exit) while (!zx_system::shuttingDown())
{ {
while (SDL_PollEvent(&e)) while (SDL_PollEvent(&e))
{ {
bool result = true; bool result = true;
if (e.type == SDL_QUIT) { should_exit=true; break; } if (e.type == SDL_QUIT) { zx_system::shutdown(); break; }
if (e.type == SDL_JOYDEVICEADDED) {
const uint8_t index = gamepad::add(e.jdevice.which, GAMEPAD_TYPE_ANY);
printf("JOYSTICK CONECTAT: %i\n", e.jdevice.which);
if (!first_time) dialogs::joysticks::init(index);
}
if (e.type == SDL_JOYDEVICEREMOVED) {
gamepad::remove(e.jdevice.which);
printf("JOYSTICK DESCONECTAT: %i\n", e.jdevice.which);
}
if (e.type == SDL_CONTROLLERBUTTONDOWN) {
gamepad::buttonDown(e.jbutton.which, e.jbutton.button);
}
if (e.type == SDL_CONTROLLERBUTTONUP) {
gamepad::buttonUp(e.jbutton.which, e.jbutton.button);
}
if (e.type == SDL_MOUSEBUTTONDOWN) result = ui::window::sendEvent(e.button.windowID, &e); if (e.type == SDL_MOUSEBUTTONDOWN) result = ui::window::sendEvent(e.button.windowID, &e);
if (e.type == SDL_MOUSEBUTTONUP) result = ui::window::sendEvent(e.button.windowID, &e); if (e.type == SDL_MOUSEBUTTONUP) result = ui::window::sendEvent(e.button.windowID, &e);
if (e.type == SDL_MOUSEMOTION) result = ui::window::sendEvent(e.motion.windowID, &e); if (e.type == SDL_MOUSEMOTION) result = ui::window::sendEvent(e.motion.windowID, &e);
@@ -140,15 +225,17 @@ int main(int argc, char *argv[])
} else { } else {
z80debug::show(); z80debug::show();
} }
z80viewer::refreshAll();
} else if (e.key.keysym.scancode==SDL_SCANCODE_F10) { } else if (e.key.keysym.scancode==SDL_SCANCODE_F10) {
if (z80debug::debugging()) { if (z80debug::debugging()) {
z80debug::show(); z80debug::show();
z80debug::history::gototop(); z80debug::history::gototop();
const uint8_t dt = z80::step(); const uint8_t dt = z80::step();
z80debug::refresh(); z80debug::refresh();
zxscreen::refresh(dt); zxscreen::fullrefresh();
zxscreen::redraw(); zxscreen::redraw();
z80analyze::refresh(); z80analyze::refresh();
z80viewer::refreshAll();
} }
} else if (e.key.keysym.scancode==SDL_SCANCODE_F11) { } else if (e.key.keysym.scancode==SDL_SCANCODE_F11) {
if (z80debug::debugging()) { if (z80debug::debugging()) {
@@ -159,6 +246,18 @@ int main(int argc, char *argv[])
zxscreen::refresh(dt); zxscreen::refresh(dt);
zxscreen::redraw(); zxscreen::redraw();
z80analyze::refresh(); z80analyze::refresh();
z80viewer::refreshAll();
}
} else if (e.key.keysym.scancode==SDL_SCANCODE_F12) {
if (z80debug::debugging()) {
z80debug::show();
z80debug::history::gototop();
const uint8_t dt = z80debug::stepout();
z80debug::refresh();
zxscreen::refresh(dt);
zxscreen::redraw();
z80analyze::refresh();
z80viewer::refreshAll();
} }
} }
result = ui::window::sendEvent(e.key.windowID, &e); result = ui::window::sendEvent(e.key.windowID, &e);
@@ -167,28 +266,13 @@ int main(int argc, char *argv[])
if (e.type == SDL_MOUSEBUTTONUP && e.button.button==1) ui::setClicked(true); if (e.type == SDL_MOUSEBUTTONUP && e.button.button==1) ui::setClicked(true);
if (!result) if (!result)
should_exit = true; break; zx_system::shutdown(); break;
} }
first_time = false;
if (!z80debug::debugging() && !z80debug::paused()) { if (!z80debug::debugging() && !z80debug::paused()) {
//if (z80::getPC()==0x05C8) zx_tape::go_berserk();
bool fastload=false; // En cada bucle fem 10 pasos de la CPU, sino s'ofega (jo en veig 5)
if (zx_tape::getplaying() && zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD)) { fastload=true; time = SDL_GetTicks(); }
while (zx_tape::getplaying() && zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD))
{
// zx_tape::update(z80::step());
uint8_t dt = z80::step();
t_states += dt;
zx_tape::update(dt);
if (SDL_GetTicks()-time>=1000) {
time = SDL_GetTicks();
zx_tape::report();
}
//zx_ula::sound_update(dt);
//zxscreen::refresh(dt);
}
if (fastload) { printf("%i\n", SDL_GetTicks()-time); t_states=0; }
// En cada bucle fem 10 pasos de la CPU, sino s'ofega
for (int i=0;i<5;++i) { for (int i=0;i<5;++i) {
if (z80debug::isbreak(z80::getPC(), 9)) { if (z80debug::isbreak(z80::getPC(), 9)) {
z80debug::stop(); z80debug::stop();
@@ -197,31 +281,34 @@ int main(int argc, char *argv[])
} else { } else {
uint8_t dt = z80::step(); uint8_t dt = z80::step();
t_states += dt; t_states += dt;
zx_tape::update(dt); zx_system::update(dt);
zx_ula::sound_update(dt);
zxscreen::refresh(dt); zxscreen::refresh(dt);
if (z80debug::debugging()) break;
} }
} }
if (t_states>=350000) uint32_t update_freq = z80::getClock()/10;
if (t_states>=update_freq)
{ {
//if (SDL_GetTicks()>=time+1000) // Esperem a que es compleixca el temps corresponent als t states executats
//printf("%i\n", SDL_GetTicks()-(time+1000));
//else
// printf("%i\n", SDL_GetTicks()-(time+1000));
//t_states = 0;
//printf("%i: %i\n", SDL_GetTicks()-(time+1000), t_states);
while (SDL_GetTicks()<time+100) {} while (SDL_GetTicks()<time+100) {}
t_states -= 350000;
t_states -= update_freq;
time = SDL_GetTicks(); time = SDL_GetTicks();
z80analyze::refresh(); z80analyze::refresh();
z80viewer::refreshAll();
} }
z80analyze::refresh(true);
} else if (!z80debug::debugging() && z80debug::paused()) { } else if (z80debug::paused()) {
zxscreen::redraw(false); zxscreen::redraw(false);
ui::menu::show();
if (ui::hasDialog())
ui::callDialog();
else
ui::menu::show();
zxscreen::present(); zxscreen::present();
} }
ui::setClicked(false); ui::setClicked(false);

BIN
plus3.rom Normal file

Binary file not shown.

13
symbols-glaurung.txt Normal file
View 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

View File

@@ -1,2 +1 @@
0xf72d MAINLOOP 0x6580 START
0xf864 CHK_KEYB

79
ui.cpp
View File

@@ -30,6 +30,8 @@ namespace ui
bool clicked = false; bool clicked = false;
void (*dialog)(void) = nullptr;
SDL_Texture * createtexture(SDL_Renderer *renderer) SDL_Texture * createtexture(SDL_Renderer *renderer)
{ {
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP("font.bmp")); SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP("font.bmp"));
@@ -39,7 +41,6 @@ namespace ui
void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture) void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture)
{ {
if (ren==renderer) return;
ren = renderer; ren = renderer;
tex = texture; tex = texture;
offset_x = offset_y = 0; offset_x = offset_y = 0;
@@ -51,6 +52,43 @@ namespace ui
offset_y = y; offset_y = y;
} }
void incoffset(uint8_t x, uint8_t y)
{
offset_x += x;
offset_y += y;
}
uint8_t getOffsetX()
{
return offset_x;
}
uint8_t getOffsetY()
{
return offset_y;
}
bool mouseInside(int x, int y, int w, int h)
{
int mx, my;
Uint32 mb = SDL_GetMouseState(&mx, &my);
mx=mx/CHR_W; my=my/CHR_H;
mx -= offset_x;
my -= offset_y;
return (mx >= x) && (mx < x+w) && (my >= y) && (my < y+h);
}
void panel(int x, int y, int w, int h, const char *title)
{
ui::setoffset(x, y);
ui::box(0,0,w,h,COLOR_WHITE);
ui::printrect(2,0, strlen(title)+2,1, COLOR_DARK);
ui::printtxt(3,0, title, COLOR_WHITE);
ui::incoffset(1, 1);
}
void box(int x, int y, int w, int h, uint8_t color) void box(int x, int y, int w, int h, uint8_t color)
{ {
SDL_Rect rect {((offset_x+x)*CHR_W)+3, ((offset_y+y)*CHR_H)+6, w*CHR_W-6, h*CHR_H-13}; SDL_Rect rect {((offset_x+x)*CHR_W)+3, ((offset_y+y)*CHR_H)+6, w*CHR_W-6, h*CHR_H-13};
@@ -65,6 +103,13 @@ namespace ui
SDL_RenderFillRect(ren, &rect); SDL_RenderFillRect(ren, &rect);
} }
void printvoidrect(int x, int y, int w, int h, uint8_t color)
{
SDL_Rect rect {(offset_x+x)*CHR_W, (offset_y+y)*CHR_H, w*CHR_W, h*CHR_H};
SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
SDL_RenderDrawRect(ren, &rect);
}
void printchar(int x, int y, char chr, uint8_t color) void printchar(int x, int y, char chr, uint8_t color)
{ {
if (color != 255) SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255); if (color != 255) SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
@@ -81,6 +126,22 @@ namespace ui
for (int i=0; i<strlen(text);++i) if (text[i]!=32) printchar(x+i, y, text[i]); for (int i=0; i<strlen(text);++i) if (text[i]!=32) printchar(x+i, y, text[i]);
} }
void placechar(int x, int y, char chr, uint8_t color)
{
if (color != 255) SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
if (chr==32) return;
if (chr<32 || chr>127) chr = '.';
SDL_Rect src {((chr-32)&0xf)*CHR_W, ((chr-32)>>4)*CHR_H, CHR_W, CHR_H};
SDL_Rect dst {x, y, CHR_W*2, CHR_H*2};
SDL_RenderCopy(ren, tex, &src, &dst);
}
void placetxt(int x, int y, const char *text, uint8_t color)
{
SDL_SetTextureColorMod(tex, colors[color][0], colors[color][1], colors[color][2]);
for (int i=0; i<strlen(text);++i) if (text[i]!=32) placechar(x+i*CHR_W*2, y, text[i]);
}
void setClicked(const bool value) void setClicked(const bool value)
{ {
clicked = value; clicked = value;
@@ -91,4 +152,20 @@ namespace ui
return clicked; return clicked;
} }
void setDialog(void(*new_dialog)(void))
{
ui:dialog = new_dialog;
}
bool hasDialog()
{
return dialog != nullptr;
}
void callDialog()
{
if (dialog) dialog();
}
} }

13
ui.h
View File

@@ -26,12 +26,25 @@ namespace ui
SDL_Texture * createtexture(SDL_Renderer *renderer); SDL_Texture * createtexture(SDL_Renderer *renderer);
void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture); void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture);
void setoffset(uint8_t x, uint8_t y); void setoffset(uint8_t x, uint8_t y);
void incoffset(uint8_t x, uint8_t y);
uint8_t getOffsetX();
uint8_t getOffsetY();
bool mouseInside(int x, int y, int w, int h);
void panel(int x, int y, int w, int h, const char *title);
void box(int x, int y, int w, int h, uint8_t color); void box(int x, int y, int w, int h, uint8_t color);
void printrect(int x, int y, int w, int h, uint8_t color); void printrect(int x, int y, int w, int h, uint8_t color);
void printvoidrect(int x, int y, int w, int h, uint8_t color);
void printchar(int x, int y, char chr, uint8_t color=255); void printchar(int x, int y, char chr, uint8_t color=255);
void printtxt(int x, int y, const char *text, uint8_t color); void printtxt(int x, int y, const char *text, uint8_t color);
void placechar(int x, int y, char chr, uint8_t color=255);
void placetxt(int x, int y, const char *text, uint8_t color);
void setClicked(const bool value); void setClicked(const bool value);
const bool getClicked(); const bool getClicked();
void setDialog(void(*new_dialog)(void));
bool hasDialog();
void callDialog();
} }

View File

@@ -31,6 +31,7 @@ namespace ui
std::vector<menu_t> menus; std::vector<menu_t> menus;
int visible_menu = -1; int visible_menu = -1;
int menu_x = 0; int menu_x = 0;
bool do_not_exit = false;
void init() void init()
{ {
@@ -39,6 +40,7 @@ namespace ui
void show() void show()
{ {
ui::setoffset(0,0);
int mx, my; int mx, my;
Uint32 mb = SDL_GetMouseState(&mx, &my); Uint32 mb = SDL_GetMouseState(&mx, &my);
mx=mx/CHR_W; my=my/CHR_H; mx=mx/CHR_W; my=my/CHR_H;
@@ -52,8 +54,8 @@ namespace ui
uint8_t text_color = COLOR_WHITE; uint8_t text_color = COLOR_WHITE;
if (my<1 && mx>=opt_pos && mx<opt_pos+text_size) if (my<1 && mx>=opt_pos && mx<opt_pos+text_size)
{ {
ui::printrect(opt_pos-1, 0, text_size, 1, COLOR_DARK_BLUE); ui::printrect(opt_pos-1, 0, text_size, 1, COLOR_WHITE);
text_color = COLOR_WHITE; text_color = COLOR_BLACK;
visible_menu = index; visible_menu = index;
menu_x = opt_pos-1; menu_x = opt_pos-1;
} }
@@ -73,14 +75,15 @@ namespace ui
{ {
const int text_size = (option.label.size()+2); const int text_size = (option.label.size()+2);
uint8_t text_color = COLOR_WHITE; uint8_t text_color = COLOR_WHITE;
if (my==opt_pos && mx>=menu_x && mx<menu_x+text_size) if (my==opt_pos && mx>=menu_x && mx<menu_x+20)
{ {
ui::printrect(menu_x+1, opt_pos, 20, 1, COLOR_DARK_BLUE); ui::printrect(menu_x+1, opt_pos, 20, 1, COLOR_WHITE);
text_color = COLOR_WHITE; text_color = COLOR_BLACK;
if (ui::getClicked()) if (ui::getClicked())
{ {
if (option.callback) option.value = option.callback(option.value); if (option.callback) option.value = option.callback(option.value);
if (exit_callback) exit_callback(); if (exit_callback && !do_not_exit) exit_callback();
do_not_exit = false;
return; return;
} }
} }
@@ -102,6 +105,11 @@ namespace ui
exit_callback = callback; exit_callback = callback;
} }
void exitButNotContinue()
{
do_not_exit = true;
}
const int addsubmenu(const char *label) const int addsubmenu(const char *label)
{ {
menu_t m; menu_t m;

View File

@@ -7,6 +7,7 @@ namespace ui
void init(); void init();
void show(); void show();
void setexitcallback(void(*callback)(void)); void setexitcallback(void(*callback)(void));
void exitButNotContinue();
const int addsubmenu(const char *label); const int addsubmenu(const char *label);
void addoption(int menu, const char *label, int(*callback)(int)); void addoption(int menu, const char *label, int(*callback)(int));
void addbooloption(int menu, const char *label, bool default_value, int(*callback)(int)); void addbooloption(int menu, const char *label, bool default_value, int(*callback)(int));

994
z80.cpp

File diff suppressed because it is too large Load Diff

27
z80.h
View File

@@ -4,22 +4,24 @@
namespace z80 namespace z80
{ {
#define MEMTAG_NONE 0
#define MEMTAG_INST 1
#define MEMTAG_CODE 2
#define MEMTAG_DATA 3
#define Z80_OPTION_STOP_ON_INVALID 0 #define Z80_OPTION_STOP_ON_INVALID 0
#define Z80_OPTION_BREAK_ON_INTERRUPT 1 #define Z80_OPTION_BREAK_ON_INTERRUPT 1
#define Z80_NUM_OPTIONS 2 #define Z80_OPTION_BREAK_ON_RET 2
#define Z80_NUM_OPTIONS 3
void reset(uint8_t* mem); void init(uint32_t freq);
void connect_port(int num, int (*in_ptr)(int), void (*out_ptr)(int,int)); void reset();
void clearPorts();
void setClock(uint32_t freq);
uint32_t getClock();
//void reset(uint8_t* mem);
void connect_port(uint16_t num, uint16_t mask, int (*in_ptr)(int), void (*out_ptr)(int,int));
void interrupt(); void interrupt();
uint32_t step(); uint32_t step();
uint8_t *getMem(); //uint8_t *getMem();
uint8_t *getRegs(); uint8_t *getRegs();
uint16_t getAF(const bool alt=false); uint16_t getAF(const bool alt=false);
@@ -37,13 +39,20 @@ namespace z80
void setPC(const uint16_t addr); void setPC(const uint16_t addr);
/*
uint8_t getMemTag(const uint16_t addr); uint8_t getMemTag(const uint16_t addr);
void setMemTag(const uint16_t addr, const uint8_t value);
void clearMemTag(); void clearMemTag();
uint8_t getMemTouched(const uint16_t addr); uint8_t getMemTouched(const uint16_t addr);
void clearMemTouched(); void clearMemTouched();
void fixMemTouched();
*/
const bool getOption(const int option); const bool getOption(const int option);
void setOption(const int option, const bool value); void setOption(const int option, const bool value);
void toggleOption(const int option); void toggleOption(const int option);
void resetStackedCalls();
} }

View File

@@ -1,5 +1,7 @@
#include "z80analyze.h" #include "z80analyze.h"
#include "z80.h" #include "z80.h"
#include "z80debug.h"
#include "zx_mem.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include "ui_window.h" #include "ui_window.h"
#include "ui.h" #include "ui.h"
@@ -10,6 +12,9 @@ namespace z80analyze
SDL_Renderer *ren = nullptr; SDL_Renderer *ren = nullptr;
SDL_Texture *tex = nullptr; SDL_Texture *tex = nullptr;
SDL_Texture *uitex = nullptr; SDL_Texture *uitex = nullptr;
uint16_t address = 0;
int mx, my;
bool needs_refresh = true;
void refreshTitle(); void refreshTitle();
@@ -17,12 +22,43 @@ namespace z80analyze
{ {
if (e->type == SDL_MOUSEBUTTONUP) if (e->type == SDL_MOUSEBUTTONUP)
{ {
z80::clearMemTouched(); //if (z80::getMemTag(address)!=MEMTAG_INST) address = find_previous_opcode(address);
z80debug::setcursor(address);
z80debug::refresh();
/*if (e->button.button == 1)
z80::clearMemTouched();
//z80::clearMemTag();
else
z80::fixMemTouched();*/
refresh(); refresh();
} }
if (e->type == SDL_MOUSEMOTION) if (e->type == SDL_MOUSEMOTION)
{ {
refreshTitle(); if (e->motion.windowID == SDL_GetWindowID(win)) {
SDL_ShowCursor(SDL_DISABLE);
mx = e->motion.x/2;
my = e->motion.y/2;
refreshTitle();
needs_refresh=true;
focus();
}
}
if (e->type == SDL_KEYDOWN) {
if (e->key.keysym.scancode == SDL_SCANCODE_RETURN) {
/*
z80::fixMemTouched();
refresh();
z80debug::refresh();
*/
} else if (e->key.keysym.scancode == SDL_SCANCODE_BACKSPACE) {
const uint32_t size = mem::getSize();
//for (int i=0; i<size; ++i) mem::setTag(i, mem::getTag(i) & ~MEMTAG_TOUCHED);
for (int i=0; i<size; ++i) mem::setTag(i, MEMTAG_NONE);
refresh();
z80debug::refresh();
}
} }
return true; return true;
} }
@@ -44,14 +80,18 @@ namespace z80analyze
char tmp[10]; char tmp[10];
void refreshTitle() void refreshTitle()
{ {
int mx, my; if (mx>=0 && my>=0 && mx<256 && my<256) {
SDL_GetMouseState(&mx, &my); address = mx+my*256;
mx/=2; my/=2; SDL_SetWindowTitle(win, SDL_itoa(address, tmp, 16));
SDL_SetWindowTitle(win, SDL_itoa(mx+my*256, tmp, 16)); }
} }
void refresh()
void refresh(const bool conditional)
{ {
if (!win) return; if (!win) return;
if (conditional && !needs_refresh) return;
needs_refresh = false;
ui::setrenderer(ren, uitex); ui::setrenderer(ren, uitex);
Uint32 *pixels; Uint32 *pixels;
@@ -59,10 +99,25 @@ namespace z80analyze
SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch); SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch);
for (int i=0; i<65536; ++i) for (int i=0; i<65536; ++i)
{ {
uint8_t tag = z80::getMemTouched(i); //uint8_t tag = z80::getMemTag(i);
pixels[i] = tag==MEMTAG_NONE ? 0x808080 : tag==MEMTAG_DATA ? 0x0000FF : 0x00FF00; //pixels[i] = tag==MEMTAG_NONE ? 0x808080 : tag==MEMTAG_DATA ? 0x0000FF : tag==MEMTAG_MIXED ? 0xFF00FF : 0x00FF00;
uint32_t none_color = i<0x4000 ? 0x101010 : i<0x5800 ? 0x202020 : i<0x5b00 ? 0x404040 : 0x808080;
uint8_t tag = mem::getTag(i);
pixels[i] = !(tag & MEMTAG_TOUCHED) ? none_color : (tag & MEMTAG_TINST) ? 0x00FF00 : (tag & MEMTAG_TREPEAT) ? 0xFF0000 : 0x0000FF;
} }
pixels[z80::getPC()] = 0xFF0000; pixels[z80::getPC()] = 0xFFFFFF;
if (mx>=0 && my>=0 && mx<256 && my<256) {
if (mx>2) pixels[(mx-2)+(my)*256] = 0x000000;
if (mx>1) pixels[(mx-1)+(my)*256] = 0x000000;
if (mx<255) pixels[(mx+1)+(my)*256] = 0x000000;
if (mx<254) pixels[(mx+2)+(my)*256] = 0x000000;
if (my>1) pixels[(mx)+(my-1)*256] = 0x000000;
if (my>2) pixels[(mx)+(my-2)*256] = 0x000000;
if (my<255) pixels[(mx)+(my+1)*256] = 0x000000;
if (my<254) pixels[(mx)+(my+2)*256] = 0x000000;
}
SDL_UnlockTexture(tex); SDL_UnlockTexture(tex);
SDL_RenderCopy(ren, tex, NULL, NULL); SDL_RenderCopy(ren, tex, NULL, NULL);
SDL_RenderPresent(ren); SDL_RenderPresent(ren);
@@ -77,4 +132,13 @@ namespace z80analyze
SDL_DestroyRenderer(ren); ren = nullptr; SDL_DestroyRenderer(ren); ren = nullptr;
SDL_DestroyWindow(win); win = nullptr; SDL_DestroyWindow(win); win = nullptr;
} }
void focus()
{
if (win) {
SDL_RaiseWindow(win);
refresh();
}
}
} }

View File

@@ -3,6 +3,7 @@
namespace z80analyze namespace z80analyze
{ {
void show(); void show();
void refresh(); void refresh(const bool conditional=false);
void hide(); void hide();
void focus();
} }

View File

@@ -2,12 +2,17 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include "z80.h" #include "z80.h"
#include "z80dis.h" #include "z80dis.h"
#include "zx_mem.h"
#include "z80analyze.h"
#include "zx_ula.h" #include "zx_ula.h"
#include "zx_speaker.h"
#include "zx_tape.h" #include "zx_tape.h"
#include "zx_disk.h"
#include "ui.h" #include "ui.h"
#include "ui_window.h" #include "ui_window.h"
#include "zx_screen.h" #include "zx_screen.h"
#include "z80analyze.h" #include "ay-3-8912.h"
#include "file.h"
#define RESIZING_NONE 0 #define RESIZING_NONE 0
#define RESIZING_MEMORY 1 #define RESIZING_MEMORY 1
@@ -20,12 +25,12 @@ namespace z80debug
int midx = 58; int midx = 58;
int mem_y = 20; int mem_y = 20;
int con_y = 28; int con_y = 28;
int sym_y = 20; int sym_y = 24;
int win_h = 44; int win_h = 44;
int mem_h = 8; int mem_h = 8;
int con_h = 6; int con_h = 6;
int sym_h = 14; int sym_h = 14;
int zoom = 1;
int resizing_type = RESIZING_MEMORY; int resizing_type = RESIZING_MEMORY;
bool resizing = false; bool resizing = false;
@@ -39,7 +44,7 @@ namespace z80debug
uint16_t oAF, oBC, oDE, oHL, oAF2, oBC2, oDE2, oHL2, oIX, oIY, oSP, oPC; uint16_t oAF, oBC, oDE, oHL, oAF2, oBC2, oDE2, oHL2, oIX, oIY, oSP, oPC;
uint8_t oI, oR; uint8_t oI, oR;
bool mem_modified[65536]; bool mem_modified[65536]; //[CHECK]
SDL_Window *win = nullptr; SDL_Window *win = nullptr;
SDL_Renderer *ren = nullptr; SDL_Renderer *ren = nullptr;
@@ -62,6 +67,19 @@ namespace z80debug
uint16_t cursor = 0; uint16_t cursor = 0;
uint8_t use[7][256];
uint16_t line_address[256];
uint16_t line_breakpoint_address[256];
int num_breakpoint_lines=0;
bool sync_mem_with_cursor = false;
uint8_t search_sequence[32];
int search_sequence_len=0;
int search_pos=0;
uint16_t inspected_value = 255;
char temp[256]; char temp[256];
const char *tohex(int value, int numdigits) const char *tohex(int value, int numdigits)
{ {
@@ -69,24 +87,47 @@ namespace z80debug
else if (numdigits==4) sprintf(temp, "%04X", value); else if (numdigits==4) sprintf(temp, "%04X", value);
return temp; return temp;
} }
const char *todec(int value, bool sign)
{
if (sign)
sprintf(temp, "%i", int8_t(value));
else
sprintf(temp, "%u", value);
return temp;
}
void saveWindowConfiguration()
{
file::setConfigValueInteger("debug_zoom", zoom);
int x, y;
SDL_GetWindowPosition(win, &x, &y);
file::setConfigValueInteger("debug_x", x);
file::setConfigValueInteger("debug_y", y);
}
bool eventHandler(SDL_Event *e) bool eventHandler(SDL_Event *e)
{ {
if (e->type == SDL_MOUSEMOTION) {
SDL_ShowCursor(SDL_ENABLE);
}
if (z80debug::debugging()) { if (z80debug::debugging()) {
if (e->type==SDL_WINDOWEVENT) { if (e->type==SDL_WINDOWEVENT) {
if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) { if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) {
int w; int h; int w; int h;
SDL_GetWindowSize(win, &w, &h); SDL_GetWindowSize(win, &w, &h);
w/=zoom; h/=zoom;
midx = (w/CHR_W) - 25; midx = (w/CHR_W) - 25;
win_h = (h/CHR_H); win_h = (h/CHR_H);
mem_y = win_h - mem_h - con_h; mem_y = win_h - mem_h - con_h;
con_y = win_h - con_h; con_y = win_h - con_h;
sym_y = win_h - sym_h; sym_h = win_h - sym_y;
z80debug::refresh(); z80debug::refresh();
zxscreen::redraw(); zxscreen::redraw();
} else if (e->window.event == SDL_WINDOWEVENT_CLOSE) { } else if (e->window.event == SDL_WINDOWEVENT_CLOSE) {
hide(); hide();
zxscreen::focus(); zxscreen::focus();
} else if (e->window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
saveWindowConfiguration();
} }
} }
if (e->type == SDL_MOUSEWHEEL) { if (e->type == SDL_MOUSEWHEEL) {
@@ -98,6 +139,14 @@ namespace z80debug
z80debug::cursorfwd(); z80debug::cursorfwd();
z80debug::refresh(); z80debug::refresh();
} }
} else if (e->wheel.mouseX<midx*CHR_W && e->wheel.mouseY>=mem_y*CHR_H && e->wheel.mouseY<con_y*CHR_H) {
if (e->wheel.y>0) {
mem_viewer_pos-=0x10;
z80debug::refresh();
} else if (e->wheel.y<0) {
mem_viewer_pos+=0x10;
z80debug::refresh();
}
} }
} }
if (e->type == SDL_KEYDOWN) { if (e->type == SDL_KEYDOWN) {
@@ -106,17 +155,28 @@ namespace z80debug
} else if (e->key.keysym.scancode==SDL_SCANCODE_F1) { } else if (e->key.keysym.scancode==SDL_SCANCODE_F1) {
z80debug::history::gototop(); z80debug::history::gototop();
z80debug::refresh(); z80debug::refresh();
z80analyze::refresh();
} else if (e->key.keysym.scancode==SDL_SCANCODE_F2) { } else if (e->key.keysym.scancode==SDL_SCANCODE_F2) {
z80debug::history::goback(); if (e->key.keysym.mod & KMOD_CTRL) {
z80debug::refresh(); zoom = zoom==1?2:1;
hide();
show();
} else {
z80debug::history::goback();
z80debug::refresh();
z80analyze::refresh();
}
} else if (e->key.keysym.scancode==SDL_SCANCODE_F3) { } else if (e->key.keysym.scancode==SDL_SCANCODE_F3) {
z80debug::history::goforward(); z80debug::history::goforward();
z80debug::refresh(); z80debug::refresh();
/*} else if (e->key.keysym.scancode==SDL_SCANCODE_F6) { z80analyze::refresh();
z80debug::history::gototop(); } else if (e->key.keysym.scancode==SDL_SCANCODE_F9) {
const uint8_t dt = z80::step(); const uint16_t address = cursor;
z80debug::refresh(); if (breakpoints[line_address[address]]==0)
zxscreen::refresh(dt);*/ breakpoints[line_address[address]]=1;
else
breakpoints[line_address[address]]=0;
refresh();
} else if (e->key.keysym.scancode==SDL_SCANCODE_UP) { } else if (e->key.keysym.scancode==SDL_SCANCODE_UP) {
if (console_history_nav != console_history_pos+1 && console_history[console_history_nav-1][0]!=0) console_history_nav--; if (console_history_nav != console_history_pos+1 && console_history[console_history_nav-1][0]!=0) console_history_nav--;
//z80debug::cursorback(); //z80debug::cursorback();
@@ -146,6 +206,55 @@ namespace z80debug
if (resizing_type != RESIZING_NONE) SDL_SetCursor(cur_df); if (resizing_type != RESIZING_NONE) SDL_SetCursor(cur_df);
resizing_type = RESIZING_NONE; resizing_type = RESIZING_NONE;
} }
const int chrx = e->motion.x/CHR_W;
const int chry = e->motion.y/CHR_H;
//printf("%ix%i\n", chrx, chry);
// Si pasa per damunt de l'adreça en el desensamblador
if ( (chrx>1) && (chrx<6) && (chry<mem_y-1) && (chry>0) ) {
inspected_value = line_address[chry-1];
refresh();
// Si pasa per damunt dels bytes que formen el opcode en el desensamblador
} else if ((chrx>=19) && (chrx<30) && (chry<mem_y-1) && (chry>0)) {
const uint16_t address = line_address[chry-1];
const int opcodesize = z80dis::getOpcodeSize(address);
const int byte = (chrx-19)/3;
if (byte < opcodesize) {
inspected_value = mem::readMem(address+byte);
refresh();
}
// Si pasa per damunt de l'adreça en el visor de memòria
} else if ((chrx>1) && (chrx<6) && (chry>mem_y) && (chry<mem_y+mem_h-1)) {
inspected_value = mem_viewer_pos + (chry-mem_y-1)*16;
refresh();
// Si pasa per damunt d'un byte en el visor de memòria
} else if ((chrx>6) && (chrx<55) && (chry>mem_y) && (chry<mem_y+mem_h-1)) {
const uint16_t address = mem_viewer_pos + (chry-mem_y-1)*16 + (chrx-7)/3;
inspected_value = mem::readMem(address);
refresh();
// Si pasa per damunt d'un registre
} else if ((chrx>=midx+4) && (chrx<midx+8) && (chry==1)) { inspected_value = z80::getAF(); refresh();
} else if ((chrx>=midx+4) && (chrx<midx+8) && (chry==2)) { inspected_value = z80::getBC(); refresh();
} else if ((chrx>=midx+4) && (chrx<midx+8) && (chry==3)) { inspected_value = z80::getDE(); refresh();
} else if ((chrx>=midx+4) && (chrx<midx+8) && (chry==4)) { inspected_value = z80::getHL(); refresh();
} else if ((chrx>=midx+12) && (chrx<midx+16) && (chry==1)) { inspected_value = z80::getAF(true); refresh();
} else if ((chrx>=midx+12) && (chrx<midx+16) && (chry==2)) { inspected_value = z80::getBC(true); refresh();
} else if ((chrx>=midx+12) && (chrx<midx+16) && (chry==3)) { inspected_value = z80::getDE(true); refresh();
} else if ((chrx>=midx+12) && (chrx<midx+16) && (chry==4)) { inspected_value = z80::getHL(true); refresh();
} else if ((chrx>=midx+20) && (chrx<midx+24) && (chry==1)) { inspected_value = z80::getIX(); refresh();
} else if ((chrx>=midx+20) && (chrx<midx+24) && (chry==2)) { inspected_value = z80::getIY(); refresh();
} else if ((chrx>=midx+20) && (chrx<midx+24) && (chry==3)) { inspected_value = z80::getSP(); refresh();
} else if ((chrx>=midx+20) && (chrx<midx+24) && (chry==4)) { inspected_value = z80::getPC(); refresh();
} else if ((chrx>=midx+6) && (chrx<midx+8) && (chry==5)) { inspected_value = mem::readMem(z80::getBC()); refresh();
} else if ((chrx>=midx+14) && (chrx<midx+16) && (chry==5)) { inspected_value = mem::readMem(z80::getDE()); refresh();
} else if ((chrx>=midx+22) && (chrx<midx+24) && (chry==5)) { inspected_value = mem::readMem(z80::getHL()); refresh();
} else if ((chrx>=midx+17) && (chrx<midx+19) && (chry==6)) { inspected_value = z80::getI(); refresh();
} else if ((chrx>=midx+22) && (chrx<midx+24) && (chry==6)) { inspected_value = z80::getR(); refresh();
}
} else { } else {
if (resizing_type==RESIZING_MEMORY) { if (resizing_type==RESIZING_MEMORY) {
const int new_mem_y = e->motion.y/CHR_H; const int new_mem_y = e->motion.y/CHR_H;
@@ -177,6 +286,38 @@ namespace z80debug
} }
} }
if (e->type == SDL_MOUSEBUTTONDOWN) { if (e->type == SDL_MOUSEBUTTONDOWN) {
const int chrx = e->button.x/CHR_W;
const int chry = e->button.y/CHR_H;
//printf("%ix%i\n", chrx, chry);
if ( (e->button.x<midx*CHR_W) && (e->button.y<(mem_y-1)*CHR_H) && (e->button.y>CHR_H) ) {
const uint16_t address = ((e->button.y)/CHR_H)-1;
if (breakpoints[line_address[address]]==0)
breakpoints[line_address[address]]=1;
else
breakpoints[line_address[address]]=0;
refresh();
}
if (chrx>=midx+12 && chry>=9 && chry<sym_y-1) {
const int breakpoint_selected = chry-9;
if (num_breakpoint_lines>breakpoint_selected) {
cursor = line_breakpoint_address[breakpoint_selected];
refresh();
}
}
if (chrx>=10 && chrx<=12 && chry==mem_y) {
sync_mem_with_cursor = ! sync_mem_with_cursor;
refresh();
}
if (chrx>=midx+2 && chry>=sym_y+1) {
const int line = chry-(sym_y+1);
if (line<z80dis::getNumSymbols()) {
cursor = z80dis::getSymbolAddress(line);
refresh();
}
}
if (!resizing && resizing_type != RESIZING_NONE) { if (!resizing && resizing_type != RESIZING_NONE) {
resizing = true; resizing = true;
} }
@@ -211,12 +352,17 @@ namespace z80debug
void show() void show()
{ {
if (!win) { if (!win) {
win = SDL_CreateWindow("Z80 Debugger", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 98*CHR_W, 44*CHR_H, SDL_WINDOW_RESIZABLE); zoom = file::getConfigValueInteger("debug_zoom", 1);
const int x = file::getConfigValueInteger("debug_x", SDL_WINDOWPOS_UNDEFINED);
const int y = file::getConfigValueInteger("debug_y", SDL_WINDOWPOS_UNDEFINED);
win = SDL_CreateWindow("Z80 Debugger", x, y, 98*CHR_W*zoom, 44*CHR_H*zoom, SDL_WINDOW_SHOWN);
ren = SDL_CreateRenderer(win, -1, 0); ren = SDL_CreateRenderer(win, -1, 0);
SDL_RenderSetLogicalSize(ren, 98*CHR_W, 44*CHR_H);
ui::window::registerWindow(SDL_GetWindowID(win), eventHandler); ui::window::registerWindow(SDL_GetWindowID(win), eventHandler);
tex = ui::createtexture(ren); tex = ui::createtexture(ren);
} }
focus(); focus();
z80analyze::refresh();
} }
void focus() void focus()
@@ -229,6 +375,7 @@ namespace z80debug
void hide() void hide()
{ {
saveWindowConfiguration();
ui::window::unregisterWindow(SDL_GetWindowID(win)); ui::window::unregisterWindow(SDL_GetWindowID(win));
if (tex) SDL_DestroyTexture(tex); if (tex) SDL_DestroyTexture(tex);
if (ren) SDL_DestroyRenderer(ren); if (ren) SDL_DestroyRenderer(ren);
@@ -240,7 +387,7 @@ namespace z80debug
void pause() void pause()
{ {
zx_ula::sound_disable(); speaker::disable();
is_paused = true; is_paused = true;
breakpoints[z80::getPC()] &= ~8; breakpoints[z80::getPC()] &= ~8;
} }
@@ -250,18 +397,20 @@ namespace z80debug
//history::gototop(); //history::gototop();
zxscreen::setTitle(" (stopped)"); zxscreen::setTitle(" (stopped)");
pause(); pause();
z80::resetStackedCalls();
is_debugging = true; is_debugging = true;
show(); show();
/*refresh();*/ /*refresh();*/
if ( zxscreen::getFullRefresh()) zxscreen::fullrefresh();
} }
void cont() { void cont() {
zxscreen::setTitle(""); zxscreen::setTitle("");
is_debugging = is_paused = false; is_debugging = is_paused = false;
//hide(); hide();
refresh(); refresh();
zxscreen::focus(); zxscreen::focus();
zx_ula::sound_enable(); speaker::enable();
} }
const bool debugging() { return is_debugging; } const bool debugging() { return is_debugging; }
@@ -271,9 +420,10 @@ namespace z80debug
uint16_t find_previous_opcode(uint16_t pc) uint16_t find_previous_opcode(uint16_t pc)
{ {
pc--; pc--;
if (z80::getMemTag(pc)!=MEMTAG_CODE && z80::getMemTag(pc)!=MEMTAG_INST) return pc; uint8_t tag = mem::getTag(pc);
if ( !(tag & (MEMTAG_CODE | MEMTAG_INST) ) ) return pc;
while (z80::getMemTag(pc)!=MEMTAG_INST) pc--; while ( !(mem::getTag(pc) & MEMTAG_INST) ) pc--;
return pc; return pc;
/* /*
@@ -292,9 +442,10 @@ namespace z80debug
void printDissasemblerLine(const uint16_t address, const int line, const bool heuristics=false) void printDissasemblerLine(const uint16_t address, const int line, const bool heuristics=false)
{ {
uint8_t colors[4] = { COLOR_RED, COLOR_CYAN, COLOR_GRAY, COLOR_WHITE}; uint8_t colors[4] = { COLOR_RED, COLOR_CYAN, COLOR_WHITE, COLOR_WHITE};
if (z80::getMemTouched(address)!=MEMTAG_INST) colors[3]=COLOR_GRAY; const uint8_t tag = mem::getTag(address);
if (z80::getMemTouched(address)==MEMTAG_NONE) colors[1]=COLOR_GRAY; if ( !(tag & (MEMTAG_TINST | MEMTAG_TREPEAT)) ) colors[3]=COLOR_GRAY;
if ( !(tag & MEMTAG_TOUCHED) ) colors[1]=COLOR_GRAY;
if (is_debugging && (address == z80::getPC())) { if (is_debugging && (address == z80::getPC())) {
for (int i=0; i<4;++i) colors[i]=COLOR_YELLOW; for (int i=0; i<4;++i) colors[i]=COLOR_YELLOW;
@@ -304,13 +455,22 @@ namespace z80debug
if (breakpoints[address]&9) ui::printtxt(0,line,"*", colors[0]); if (breakpoints[address]&9) ui::printtxt(0,line,"*", colors[0]);
ui::printtxt(1,line,tohex(address,4), colors[1]); ui::printtxt(1,line,tohex(address,4), colors[1]);
if ( (z80::getMemTag(address)==MEMTAG_INST) || heuristics ) { if ( (tag & MEMTAG_INST) || heuristics ) {
const char *sym = z80dis::getSymbol(address); const char *sym = z80dis::getSymbol(address);
if (sym[0]!=0) ui::printtxt(7,line, sym, COLOR_YELLOW); if (sym[0]!=0) ui::printtxt(7,line, sym, COLOR_YELLOW);
const int opcodesize = z80dis::getOpcodeSize(address);
for (int i=0; i<opcodesize; ++i) {
const uint8_t tag = mem::getTag(address+i);
const uint32_t color = !(tag & MEMTAG_KNOWN) ? COLOR_GRAY : (tag & MEMTAG_DATA) ? ( (tag & (MEMTAG_CODE|MEMTAG_INST)) ? COLOR_MAGENTA : COLOR_BLUE ) : COLOR_GREEN;
ui::printrect(19+i*3,line,2,1,color);
}
ui::printtxt(19,line, z80dis::getOpcode(address), colors[2]); ui::printtxt(19,line, z80dis::getOpcode(address), colors[2]);
ui::printtxt(31,line, z80dis::getAsm(address), colors[3]); ui::printtxt(31,line, z80dis::getAsm(address), colors[3]);
} else { } else {
ui::printtxt(19,line, tohex(z80::getMem()[address],2), COLOR_GRAY); ui::printrect(19,line,2,1,COLOR_GRAY);
ui::printtxt(19,line, tohex(mem::readMem(address),2), COLOR_WHITE);
ui::printtxt(31,line, "?????????", COLOR_GRAY); ui::printtxt(31,line, "?????????", COLOR_GRAY);
} }
} }
@@ -331,21 +491,24 @@ namespace z80debug
ui::setoffset(1, 1); ui::setoffset(1, 1);
uint8_t *memory = z80::getMem();
uint16_t pc = cursor; //z80::getPC(); uint16_t pc = cursor; //z80::getPC();
if (sync_mem_with_cursor) mem_viewer_pos = cursor;
uint16_t pos = pc; uint16_t pos = pc;
int num_lines = mem_y-2; int num_lines = mem_y-2;
line_address[(num_lines/2)-1] = pos;
ui::printvoidrect(0,(num_lines/2)-1, midx-2,1, COLOR_BLUE);
printDissasemblerLine(pc, (num_lines/2)-1, true); printDissasemblerLine(pc, (num_lines/2)-1, true);
for (int i=num_lines/2;i<num_lines;++i) { for (int i=num_lines/2;i<num_lines;++i) {
pos += z80dis::getOpcodeSize(pos); pos += z80dis::getOpcodeSize(pos);
line_address[i] = pos;
printDissasemblerLine(pos, i, true); printDissasemblerLine(pos, i, true);
} }
pos = pc; pos = pc;
for (int i=(num_lines/2)-2;i>=0;--i) { for (int i=(num_lines/2)-2;i>=0;--i) {
pos = find_previous_opcode(pos); pos = find_previous_opcode(pos);
line_address[i] = pos;
printDissasemblerLine(pos, i); printDissasemblerLine(pos, i);
} }
@@ -381,9 +544,9 @@ namespace z80debug
ui::printtxt(16,5, tohex(z80::getI(), 2), oI != z80::getI() ? COLOR_RED : COLOR_GRAY); ui::printtxt(16,5, tohex(z80::getI(), 2), oI != z80::getI() ? COLOR_RED : COLOR_GRAY);
ui::printtxt(21,5, tohex(z80::getR(), 2), oR != z80::getR() ? COLOR_RED : COLOR_GRAY); ui::printtxt(21,5, tohex(z80::getR(), 2), oR != z80::getR() ? COLOR_RED : COLOR_GRAY);
ui::printtxt(5,4, tohex(memory[z80::getBC()], 2), COLOR_GRAY); ui::printtxt(5,4, tohex(mem::readMem(z80::getBC()), 2), COLOR_GRAY);
ui::printtxt(13,4, tohex(memory[z80::getDE()], 2), COLOR_GRAY); ui::printtxt(13,4, tohex(mem::readMem(z80::getDE()), 2), COLOR_GRAY);
ui::printtxt(21,4, tohex(memory[z80::getHL()], 2), COLOR_GRAY); ui::printtxt(21,4, tohex(mem::readMem(z80::getHL()), 2), COLOR_GRAY);
const uint8_t flags = (z80::getAF() & 0xFF); const uint8_t flags = (z80::getAF() & 0xFF);
const uint8_t mod_flags = flags ^ (oAF & 0xFF); const uint8_t mod_flags = flags ^ (oAF & 0xFF);
@@ -399,21 +562,21 @@ namespace z80debug
// STACK // STACK
// ****************************************** // ******************************************
ui::setoffset(0, 0); ui::setoffset(0, 0);
ui::box(midx,8,11,sym_y-8,COLOR_WHITE); ui::box(midx,8,11,12,COLOR_WHITE);
ui::printrect(midx+2,8, 8,1, COLOR_DARK); ui::printrect(midx+2,8, 8,1, COLOR_DARK);
ui::printtxt(midx+3,8, "STACK:", COLOR_WHITE); ui::printtxt(midx+3,8, "STACK:", COLOR_WHITE);
ui::setoffset(midx+1, 9); ui::setoffset(midx+1, 9);
uint16_t sp = z80::getSP()-(((sym_y-12)>>1)<<1); uint16_t sp = z80::getSP();//-(((sym_y-12)>>1)<<1);
for (int i=0; i<sym_y-10; ++i) { for (int i=0; i<10; ++i) {
uint8_t c1=COLOR_CYAN, c2=COLOR_GRAY; uint8_t c1=COLOR_CYAN, c2=COLOR_GRAY;
if (sp == z80::getSP()) { if (sp == z80::getSP()) {
ui::printrect(0,i,9,1,COLOR_BLUE); ui::printrect(0,i,9,1,COLOR_BLUE);
c1 = c2 = COLOR_YELLOW; c1 = c2 = COLOR_YELLOW;
} }
ui::printtxt(0,i, tohex(sp, 4), c1); ui::printtxt(0,i, tohex(sp, 4), c1);
ui::printtxt(5,i, tohex(*((uint16_t*)&memory[sp]),4), c2); uint16_t value = mem::readMem(sp) + (mem::readMem(sp+1)<<8);
ui::printtxt(5,i, tohex(value, 4), c2);
sp+=2; sp+=2;
} }
@@ -422,40 +585,76 @@ namespace z80debug
// ****************************************** // ******************************************
ui::setoffset(0, 0); ui::setoffset(0, 0);
ui::box(midx+11,8,14,sym_y-8,COLOR_WHITE); ui::box(midx+11,8,14,12,COLOR_WHITE);
ui::printrect(midx+13,8, 9,1, COLOR_DARK); ui::printrect(midx+13,8, 9,1, COLOR_DARK);
ui::printtxt(midx+14,8, "BREAKS:", COLOR_WHITE); ui::printtxt(midx+14,8, "BREAKS:", COLOR_WHITE);
ui::setoffset(midx+12, 9); ui::setoffset(midx+12, 9);
int line=0; int line=0;
num_breakpoint_lines=0;
for (int i=0; i<65536; ++i) { for (int i=0; i<65536; ++i) {
if (breakpoints[i]&7) { if (breakpoints[i]&7) {
ui::printtxt(2,line, tohex(i,4), COLOR_WHITE); line_breakpoint_address[line] = i;
ui::printtxt(7,line, breakpoints[i]&1?"x":"-", COLOR_GRAY); num_breakpoint_lines++;
ui::printtxt(8,line, breakpoints[i]&2?"r":"-", COLOR_GRAY); uint8_t colors[2] = {COLOR_WHITE, COLOR_GRAY};
ui::printtxt(9,line, breakpoints[i]&4?"w":"-", COLOR_GRAY); if (i==cursor) {
ui::printrect(1,line,10,1,COLOR_BLUE);
colors[0]=colors[1]=COLOR_YELLOW;
}
ui::printtxt(2,line, tohex(i,4), colors[0]);
ui::printtxt(7,line, breakpoints[i]&1?"x":"-", colors[1]);
ui::printtxt(8,line, breakpoints[i]&2?"r":"-", colors[1]);
ui::printtxt(9,line, breakpoints[i]&4?"w":"-", colors[1]);
line++; line++;
if (line>9) break; if (line>9) break;
} }
} }
// INSPECTOR
// ******************************************
ui::setoffset(0, 0);
ui::box(midx,20,25,4,COLOR_WHITE);
ui::printrect(midx+2,20,12,1, COLOR_DARK);
ui::printtxt(midx+3,20, "INSPECTOR:", COLOR_WHITE);
ui::setoffset(midx+1, 21);
ui::printtxt(1,0,"VALUE:", COLOR_GRAY); ui::printtxt(8,0,"$", COLOR_CYAN);
ui::printtxt(9,0,tohex(inspected_value,4), COLOR_CYAN);
ui::printtxt(14,0,todec(inspected_value,false), COLOR_WHITE);
ui::printtxt(20,0,todec(inspected_value,true), COLOR_WHITE);
ui::printtxt(1,1,"H:", COLOR_GRAY); ui::printtxt(4,1,"$", COLOR_CYAN);
ui::printtxt(5,1,tohex(inspected_value>>8,2), COLOR_CYAN);
ui::printtxt(8,1,todec(inspected_value>>8,false), COLOR_WHITE);
ui::printtxt(12,1,"L:", COLOR_GRAY); ui::printtxt(15,1,"$", COLOR_CYAN);
ui::printtxt(16,1,tohex(inspected_value&0xff,2), COLOR_CYAN);
ui::printtxt(19,1,todec(inspected_value&0xff,false), COLOR_WHITE);
// MEMORY // MEMORY
// ****************************************** // ******************************************
ui::setoffset(0, 0); ui::setoffset(0, 0);
ui::box(0,mem_y,midx,mem_h,COLOR_WHITE); ui::box(0,mem_y,midx,mem_h,COLOR_WHITE);
ui::printrect(2,mem_y, 9,1, COLOR_DARK); ui::printrect(2,mem_y, 11,1, COLOR_DARK);
ui::printtxt(3,mem_y, "MEMORY:", COLOR_WHITE); ui::printtxt(3,mem_y, "MEMORY:", COLOR_WHITE);
ui::printvoidrect(10,mem_y,3,1,COLOR_WHITE);
if (sync_mem_with_cursor) ui::printchar(11,mem_y,'X',COLOR_WHITE);
ui::setoffset(1, mem_y+1); ui::setoffset(1, mem_y+1);
uint16_t mem_viewer_cursor = mem_viewer_pos; uint16_t mem_viewer_cursor = mem_viewer_pos;
for (int i=0; i<mem_h-2; ++i) { for (int i=0; i<mem_h-2; ++i) {
ui::printtxt(1,i, tohex(mem_viewer_cursor, 4), COLOR_CYAN); ui::printtxt(1,i, tohex(mem_viewer_cursor, 4), COLOR_CYAN);
for (int j=0; j<16; ++j) { for (int j=0; j<16; ++j) {
ui::printtxt(6+j*3, i, tohex(memory[mem_viewer_cursor],2), mem_modified[mem_viewer_cursor] ? COLOR_RED : COLOR_WHITE);
ui::printchar(54+j, i, memory[mem_viewer_cursor], mem_modified[mem_viewer_cursor] ? COLOR_BROWN : COLOR_GRAY); const uint8_t tag = mem::getTag(mem_viewer_cursor);
const uint32_t color = !(tag & MEMTAG_KNOWN) ? COLOR_GRAY : (tag & MEMTAG_DATA) ? ( (tag & (MEMTAG_CODE|MEMTAG_INST)) ? COLOR_MAGENTA : COLOR_BLUE ) : COLOR_GREEN;
ui::printrect(6+j*3,i,2,1,color);
ui::printrect(54+j,i,1,1,color);
ui::printtxt(6+j*3, i, tohex(mem::readMem(mem_viewer_cursor), 2), ( tag & MEMTAG_MODIFIED) ? COLOR_RED : COLOR_WHITE);
ui::printchar(54+j, i, mem::readMem(mem_viewer_cursor), ( tag & MEMTAG_MODIFIED) ? COLOR_BROWN : COLOR_GRAY);
mem_viewer_cursor++; mem_viewer_cursor++;
} }
//printtxt(5,0, "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", COLOR_WHITE); //printtxt(5,0, "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", COLOR_WHITE);
@@ -495,11 +694,18 @@ namespace z80debug
ui::setoffset(midx+1, sym_y+1); ui::setoffset(midx+1, sym_y+1);
const int num_symbols = z80dis::getNumSymbols(); const int num_symbols = z80dis::getNumSymbols();
for (int i=0;i<num_symbols;++i) { for (int i=0;i<num_symbols;++i) {
uint8_t colors[2] = {COLOR_GRAY, COLOR_WHITE};
const uint16_t address = z80dis::getSymbolAddress(i); const uint16_t address = z80dis::getSymbolAddress(i);
ui::printtxt(1, i, tohex(address,4), COLOR_GRAY); if (address==cursor) {
ui::printtxt(6, i, z80dis::getSymbol(address), COLOR_WHITE); ui::printrect(0,line,23,1,COLOR_BLUE);
colors[0]=colors[1]=COLOR_YELLOW;
}
ui::printtxt(1, i, tohex(address,4), colors[0]);
ui::printtxt(6, i, z80dis::getSymbol(address), colors[1]);
} }
if (!is_debugging) { if (!is_debugging) {
SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(ren, 0, 0, 0, 128); SDL_SetRenderDrawColor(ren, 0, 0, 0, 128);
@@ -610,17 +816,25 @@ namespace z80debug
getcmd(); getcmd();
if (strcmp(cmd, "s")==0 || strcmp(cmd, "step")==0) { if (strcmp(cmd, "s")==0 || strcmp(cmd, "step")==0) {
z80::step(); uint8_t dt = z80::step();
zx_system::update(dt);
//zx_tape::update(dt);
//audio::update(dt);
//speaker::update(dt);
zxscreen::fullrefresh();
z80analyze::refresh();
} else if (strcmp(cmd, "c")==0 || strcmp(cmd, "cont")==0) { } else if (strcmp(cmd, "c")==0 || strcmp(cmd, "cont")==0) {
z80::step(); z80::step();
z80debug::cont(); z80debug::cont();
z80analyze::refresh();
} else if (strcmp(cmd, "r")==0 || strcmp(cmd, "reset")==0) { } else if (strcmp(cmd, "r")==0 || strcmp(cmd, "reset")==0) {
uint8_t *mem = z80::getMem(); z80::reset();
for (int i=0x4000; i<=0xffff; ++i) mem[i]=0; //z80::connect_port(0xfe, 0x0001, zx_ula::port_in, zx_ula::port_out);
z80::reset(mem); history::reset();
z80::connect_port(0xfe, zx_ula::port_in, zx_ula::port_out);
//for (int i=0; i<65536; ++i) breakpoints[i]=0;
z80debug::refresh(); z80debug::refresh();
z80analyze::refresh();
} else if (strcmp(cmd, "b")==0 || strcmp(cmd, "break")==0) { } else if (strcmp(cmd, "b")==0 || strcmp(cmd, "break")==0) {
getcmd(); getcmd();
if (cmd[0] == 0) { breakpoints[z80::getPC()]=1; return; } if (cmd[0] == 0) { breakpoints[z80::getPC()]=1; return; }
@@ -682,6 +896,7 @@ namespace z80debug
//strcpy(address, cmd); //strcpy(address, cmd);
//loadngo(filename, address); //loadngo(filename, address);
loadstate(filename); loadstate(filename);
z80analyze::refresh();
} else if (strcmp(cmd, "t")==0 || strcmp(cmd, "tape")==0) { } else if (strcmp(cmd, "t")==0 || strcmp(cmd, "tape")==0) {
getcmd(); getcmd();
if (strcmp(cmd, "load")==0) { if (strcmp(cmd, "load")==0) {
@@ -692,18 +907,24 @@ namespace z80debug
} else if (strcmp(cmd, "play")==0) { } else if (strcmp(cmd, "play")==0) {
zx_tape::play(); zx_tape::play();
} }
} else if (strcmp(cmd, "disk")==0) {
getcmd();
if (strcmp(cmd, "load")==0) {
getcmd();
char filename[256];
strcpy(filename, cmd);
zx_disk::load(filename);
}
} else if (strcmp(cmd, "poke")==0) { } else if (strcmp(cmd, "poke")==0) {
getcmd(); getcmd();
int address = getnum(cmd); int address = getnum(cmd);
getcmd(); getcmd();
int value = getnum(cmd); int value = getnum(cmd);
uint8_t *mem = z80::getMem(); mem::writeMem(address, value);
mem[address] = value;
} else if (strcmp(cmd, "peek")==0) { } else if (strcmp(cmd, "peek")==0) {
getcmd(); getcmd();
int address = getnum(cmd); int address = getnum(cmd);
uint8_t *mem = z80::getMem(); int value = mem::readMem(address);
int value = mem[address];
char tmp[10]; char tmp[10];
sendToConsoleLog(SDL_itoa(value, tmp, 10)); sendToConsoleLog(SDL_itoa(value, tmp, 10));
} else if (strcmp(cmd, "reg")==0) { } else if (strcmp(cmd, "reg")==0) {
@@ -716,12 +937,13 @@ namespace z80debug
else if (strcmp(cmd, "d")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[5] = value; } else if (strcmp(cmd, "d")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[5] = value; }
else if (strcmp(cmd, "l")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[6] = value; } else if (strcmp(cmd, "l")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[6] = value; }
else if (strcmp(cmd, "h")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[7] = value; } else if (strcmp(cmd, "h")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[7] = value; }
else if (strcmp(cmd, "pc")==0) { getcmd(); int value = getnum(cmd); z80::getRegs()[24] = value&0xff; z80::getRegs()[25] = (value>>8)&0xff; }
else { sendToConsoleLog("Syntax error: invalid register"); return; } else { sendToConsoleLog("Syntax error: invalid register"); return; }
} else if (strcmp(cmd, "g")==0 || strcmp(cmd, "goto")==0) { } else if (strcmp(cmd, "g")==0 || strcmp(cmd, "goto")==0) {
getcmd(); getcmd();
int address = getnum(cmd); int address = getnum(cmd);
if (address<0 || address>=65536) { sendToConsoleLog("Illegal address"); return; } if (address<0 || address>=65536) { sendToConsoleLog("Illegal address"); return; }
if (z80::getMemTag(address)!=MEMTAG_INST) address = find_previous_opcode(address); if ( !(mem::getTag(address) & MEMTAG_INST) ) address = find_previous_opcode(address);
z80debug::setcursor(address); z80debug::setcursor(address);
} else if (strcmp(cmd, "sym")==0 || strcmp(cmd, "symbol")==0) { } else if (strcmp(cmd, "sym")==0 || strcmp(cmd, "symbol")==0) {
getcmd(); getcmd();
@@ -759,6 +981,37 @@ namespace z80debug
sendToConsoleLog("Keyup sent for key: "); sendToConsoleLog("Keyup sent for key: ");
sendMoreToConsoleLog(cmd); sendMoreToConsoleLog(cmd);
} }
} else if (strcmp(cmd, "opcodes")==0) {
getcmd();
if (strcmp(cmd, "clear")==0 || strcmp(cmd, "reset")==0) {
clearUsedOpcodes();
} else if (strcmp(cmd, "mark")==0) {
markUsedOpcodes();
} else if (strcmp(cmd, "count")==0) {
printf("Num opcodes used: %i\n", getNumOpcodesUsed());
} else if (strcmp(cmd, "print")==0) {
printOpcodesUsed();
}
} else if (strcmp(cmd, "ignore")==0) {
getcmd();
const int address = getnum(cmd);
mem::setTag(address, mem::getTag(address) | MEMTAG_IGNORE);
} else if (strcmp(cmd, "search")==0) {
getcmd();
if (strcmp(cmd, "next")==0) {
search();
} else {
search(cmd);
}
} else if (strcmp(cmd, "show")==0) {
getcmd();
if (strcmp(cmd, "analyzer")==0) {
z80analyze::show();
} else {
sendToConsoleLog("Unrecognized window. Usage: Show [analyzer]");
}
} else {
sendToConsoleLog("Unrecognized command.");
} }
} }
@@ -775,6 +1028,14 @@ namespace z80debug
return t; return t;
} }
uint32_t stepout()
{
z80::setOption(Z80_OPTION_BREAK_ON_RET, true);
const uint32_t t = z80::step();
cont();
return t;
}
void setmemmodified(const uint16_t addr) void setmemmodified(const uint16_t addr)
{ {
mem_modified[addr] = true; mem_modified[addr] = true;
@@ -782,6 +1043,7 @@ namespace z80debug
void loadngo(const char* filename, const char* addr) void loadngo(const char* filename, const char* addr)
{ {
/*
int address = getnum(addr); int address = getnum(addr);
if (address<0 || address>65536) { sendToConsoleLog("Illegal offset"); return; } if (address<0 || address>65536) { sendToConsoleLog("Illegal offset"); return; }
@@ -789,36 +1051,42 @@ namespace z80debug
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
int size = ftell(f); int size = ftell(f);
fseek(f, 0, SEEK_SET); fseek(f, 0, SEEK_SET);
uint8_t *memory = z80::getMem(); uint32_t memsize;
uint8_t *memory = mem::getRawPointer(&memsize);
fread(memory+address, size, 1, f); fread(memory+address, size, 1, f);
fclose(f); fclose(f);
z80::setPC(address); z80::setPC(address);
is_debugging = is_paused = false; is_debugging = is_paused = false;
*/
} }
void savestate(const char *filename) void savestate(const char *filename)
{ {
uint8_t *regs = z80::getRegs(); uint8_t *regs = z80::getRegs();
uint8_t *memory = z80::getMem();
FILE *f = fopen(filename, "wb"); FILE *f = fopen(filename, "wb");
fwrite(regs, 31, 1, f); fwrite(regs, 31, 1, f);
fwrite(&memory[0x4000], 0xc000, 1, f); mem::saveState(f);
fclose(f); fclose(f);
} }
void loadstate(const char *filename) void loadstate(const char *filename)
{ {
uint8_t *regs = z80::getRegs(); uint8_t *regs = z80::getRegs();
uint8_t *memory = z80::getMem();
FILE *f = fopen(filename, "rb"); FILE *f = fopen(filename, "rb");
fread(regs, 31, 1, f); fread(regs, 31, 1, f);
fread(&memory[0x4000], 0xc000, 1, f); mem::loadState(f);
fclose(f); fclose(f);
history::store();
history::gototop();
} }
void setcursor(const uint16_t address) void setcursor(const uint16_t address)
{ {
cursor = address; //if (!debugging()) return;
if ( !(mem::getTag(address) & MEMTAG_INST) )
cursor = find_previous_opcode(address);
else
cursor = address;
} }
void cursorfwd() void cursorfwd()
@@ -831,9 +1099,103 @@ namespace z80debug
cursor = find_previous_opcode(cursor); cursor = find_previous_opcode(cursor);
} }
void useOpcode(const uint8_t opcode, const uint8_t base)
{
if (use[base][opcode]<255) use[base][opcode]++;
}
void clearUsedOpcodes()
{
for (int i=0; i<7; ++i) {
for (int j=0; j<256; ++j) {
use[i][j]=0;
}
}
}
void markUsedOpcodes()
{
for (int i=0; i<7; ++i) {
for (int j=0; j<256; ++j) {
if (use[i][j]==1) use[i][j]++;
}
}
}
const int getNumOpcodesUsed()
{
int count = 0;
for (int i=0; i<7; ++i) {
for (int j=0; j<256; ++j) {
if (use[i][j]==1) count++;
}
}
return count;
}
void printOpcodesUsed()
{
for (int i=0; i<7; ++i) {
for (int j=0; j<256; ++j) {
if (use[i][j]==1) {
switch (i) {
case 1: printf("ED "); break;
case 2: printf("CB "); break;
case 3: printf("DD "); break;
case 4: printf("DD CB "); break;
case 5: printf("FD "); break;
case 6: printf("FD CB "); break;
}
printf("%2X\n",j);
}
}
}
}
uint8_t hexchartonum(char chr)
{
int num = chr-48;
if (num<0) return 0;
if (num>9) {
num-=7;
if (num>15) {
num-=32;
if (num<10 || num>15) return 0;
}
}
return uint8_t(num);
}
void search(const char *seq)
{
if (seq) {
search_pos = 0;
search_sequence_len=0;
while (*seq!=0) search_sequence[search_sequence_len++] = (hexchartonum(*(seq++))<<4) + hexchartonum(*(seq++));
}
int num_found=0;
while (search_pos<65536) {
if (search_sequence[num_found] == mem::readMem(search_pos)) {
num_found++; search_pos++;
if (num_found==search_sequence_len) {
mem_viewer_pos=search_pos-search_sequence_len;
return;
}
} else {
num_found=0; search_pos++;
}
}
sendToConsoleLog("Sequence not found.");
}
namespace history namespace history
{ {
void reset()
{
buffer[0]=buffer[1]=0;
cursor=pos=top=0;
}
void store() void store()
{ {
buffer[++top] = z80::getPC(); buffer[++top] = z80::getPC();

View File

@@ -24,6 +24,7 @@ namespace z80debug
const bool isbreak(const uint16_t address, const uint8_t type=1); const bool isbreak(const uint16_t address, const uint8_t type=1);
uint32_t next(); uint32_t next();
uint32_t stepout();
void savestate(const char *filename); void savestate(const char *filename);
void loadstate(const char *filename); void loadstate(const char *filename);
@@ -34,8 +35,16 @@ namespace z80debug
void cursorfwd(); void cursorfwd();
void cursorback(); void cursorback();
void useOpcode(const uint8_t opcode, const uint8_t base);
void clearUsedOpcodes();
void markUsedOpcodes();
const int getNumOpcodesUsed();
void printOpcodesUsed();
void search(const char *seq=nullptr);
namespace history namespace history
{ {
void reset();
void store(); void store();
void gototop(); void gototop();
void goforward(); void goforward();

View File

@@ -1,7 +1,8 @@
#include "z80dis.h" #include "z80dis.h"
#include "z80.h"
#include "zx_mem.h"
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include "z80.h"
#include <vector> #include <vector>
#include <bits/stdc++.h> #include <bits/stdc++.h>
@@ -68,44 +69,41 @@ namespace z80dis
const char *getBase(const uint16_t pos) const char *getBase(const uint16_t pos)
{ {
const uint8_t *memory = &z80::getMem()[pos]; if (mem::readMem(pos) == 0xCB) {
if (*memory == 0xCB) {
opcode_size=2; opcode_size=2;
return cb_opcodes[*(memory+1)]; return cb_opcodes[mem::readMem(pos+1)];
} else if (*memory == 0xDD) { } else if (mem::readMem(pos) == 0xDD) {
if (*(memory+1) == 0xCB) { if (mem::readMem(pos+1) == 0xCB) {
opcode_size=4; opcode_size=4;
return ix_bit_opcodes[*(memory+3)]; return ix_bit_opcodes[mem::readMem(pos+3)];
} else { } else {
opcode_size=2; opcode_size=2;
return ix_opcodes[*(memory+1)]; return ix_opcodes[mem::readMem(pos+1)];
} }
} else if (*memory == 0xED) { } else if (mem::readMem(pos) == 0xED) {
opcode_size=2; opcode_size=2;
return misc_opcodes[(*(memory+1))-64]; return misc_opcodes[(mem::readMem(pos+1))-64];
} else if (*memory == 0xFD) { } else if (mem::readMem(pos) == 0xFD) {
if (*(memory+1) == 0xCB) { if (mem::readMem(pos+1) == 0xCB) {
opcode_size=4; opcode_size=4;
return iy_bit_opcodes[*(memory+3)]; return iy_bit_opcodes[mem::readMem(pos+3)];
} else { } else {
opcode_size=2; opcode_size=2;
return iy_opcodes[*(memory+1)]; return iy_opcodes[mem::readMem(pos+1)];
} }
} else { } else {
opcode_size=1; opcode_size=1;
return base_opcodes[*memory]; return base_opcodes[mem::readMem(pos)];
} }
} }
void printOpcode(const uint16_t pos) void printOpcode(const uint16_t pos)
{ {
const uint8_t *memory = &z80::getMem()[pos];
char hex[4]; char hex[4];
for (int i=0; i<4;++i) for (int i=0; i<4;++i)
{ {
if (opcode_size>i) if (opcode_size>i)
sprintf(hex, "%02x ", *(memory+i)); sprintf(hex, "%02x ", mem::readMem(pos+i));
else else
sprintf(hex, " "); sprintf(hex, " ");
strcat(buffer, hex); strcat(buffer, hex);
@@ -114,8 +112,6 @@ namespace z80dis
const char *getAsm(const uint16_t pos) const char *getAsm(const uint16_t pos)
{ {
const uint8_t *memory = &z80::getMem()[pos];
opcode_size = 0; opcode_size = 0;
char base[256]; char base[256];
strcpy(buffer, getBase(pos)); strcpy(buffer, getBase(pos));
@@ -123,7 +119,9 @@ namespace z80dis
if (strstr(buffer, "4x")) if (strstr(buffer, "4x"))
{ {
opcode_size+=2; opcode_size+=2;
const uint16_t word = *(uint16_t*)(memory+1); const uint8_t memvalue = mem::readMem(pos);
const uint16_t address = pos + ((memvalue==0xFD) || (memvalue==0xDD) || (memvalue==0xED)?2:1);
const uint16_t word = mem::readMem(address) + (mem::readMem(address+1)<<8);
if (symbols[word][0]!=0) { if (symbols[word][0]!=0) {
char *p = strstr(buffer, "$"); char *p = strstr(buffer, "$");
(*p)='%'; p++; (*p)='%'; p++;
@@ -144,7 +142,7 @@ namespace z80dis
{ {
opcode_size = 4; opcode_size = 4;
strcpy(base, buffer); strcpy(base, buffer);
sprintf(buffer, base, *(memory+2), (int8_t)*(memory+3)); sprintf(buffer, base, mem::readMem(pos+2), (int8_t)mem::readMem(pos+3));
return buffer; return buffer;
} }
@@ -152,7 +150,7 @@ namespace z80dis
{ {
opcode_size+=1; opcode_size+=1;
strcpy(base, buffer); strcpy(base, buffer);
sprintf(buffer, base, *(memory+1)); sprintf(buffer, base, mem::readMem(pos+1));
} }
if (strstr(buffer, "Xx")) if (strstr(buffer, "Xx"))
@@ -163,9 +161,9 @@ namespace z80dis
if (opcode_size>4) { if (opcode_size>4) {
opcode_size=4; opcode_size=4;
sprintf(buffer, base, pos + opcode_size + (int8_t)*(memory+2)); sprintf(buffer, base, pos + opcode_size + (int8_t)mem::readMem(pos+2));
} else { } else {
sprintf(buffer, base, pos + opcode_size + (int8_t)*(memory+1)); sprintf(buffer, base, pos + opcode_size + (int8_t)mem::readMem(pos+1));
} }
} }
@@ -175,9 +173,9 @@ namespace z80dis
strcpy(base, buffer); strcpy(base, buffer);
if (opcode_size>4) { if (opcode_size>4) {
opcode_size=4; opcode_size=4;
sprintf(buffer, base, (int8_t)*(memory+2)); sprintf(buffer, base, (int8_t)mem::readMem(pos+3));
} else { } else {
sprintf(buffer, base, (int8_t)*(memory+1)); sprintf(buffer, base, (int8_t)mem::readMem(pos+2));
} }
} }

33
z80viewer.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 &sector = 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 &sector = 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
View 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
View 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
View 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);
}

View File

@@ -1,11 +1,14 @@
#include "zx_screen.h" #include "zx_screen.h"
#include "z80.h" #include "z80.h"
#include "zx_mem.h"
#include "zx_ula.h" #include "zx_ula.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include "zx_tape.h" #include "zx_tape.h"
#include "ui_window.h" #include "ui_window.h"
#include "z80debug.h" #include "z80debug.h"
#include "ui.h" #include "ui.h"
#include "file.h"
//#include "zx_128mem.h"
namespace zxscreen namespace zxscreen
{ {
@@ -18,8 +21,16 @@ namespace zxscreen
SDL_Texture *tex = nullptr; SDL_Texture *tex = nullptr;
SDL_Texture *uitex = nullptr; SDL_Texture *uitex = nullptr;
uint8_t zoom = 1; int mode = SCREEN_MODE_48K;
uint32_t t_states_total = 69888;
uint32_t t_states_per_scanline = 224;
uint32_t vsync_lines = 16;
bool interrupt_enabled = true;
uint8_t zoom = 2;
bool fullscreen = false; bool fullscreen = false;
bool full_refresh = true;
int fullscreen_scale = 1; int fullscreen_scale = 1;
SDL_Rect dest_rect; SDL_Rect dest_rect;
@@ -29,45 +40,63 @@ namespace zxscreen
uint8_t t_flash = 0; uint8_t t_flash = 0;
bool flash = false; bool flash = false;
int pixels_draw = 0; uint32_t pixel_base_addr = 0x4000;
uint32_t color_base_addr = 0x5800;
uint16_t pixel_addr[69888]; uint32_t *pixel_addr = nullptr; //[69888];
uint16_t color_addr[69888]; uint32_t *color_addr = nullptr; //[69888];
uint8_t zx_pixels[352*296]; uint8_t zx_pixels[352*296];
uint8_t *ptr_pixel = zx_pixels; uint8_t *ptr_pixel = zx_pixels;
#define SCREEN_SYNC 0xFFFF
#define SCREEN_BORDER 0xFFFE
void saveWindowConfiguration()
{
file::setConfigValueInteger("screen_zoom", zoom);
int x, y;
SDL_GetWindowPosition(win, &x, &y);
file::setConfigValueInteger("screen_x", x);
file::setConfigValueInteger("screen_y", y);
}
void create_tables() void create_tables()
{ {
uint32_t count = 0; uint32_t count = 0;
uint16_t *ptr_pixel = pixel_addr; if (pixel_addr) free(pixel_addr);
uint16_t *ptr_color = color_addr; if (color_addr) free(color_addr);
pixel_addr = (uint32_t*)malloc(t_states_total*sizeof(uint32_t));
color_addr = (uint32_t*)malloc(t_states_total*sizeof(uint32_t));
uint32_t *ptr_pixel = pixel_addr;
uint32_t *ptr_color = color_addr;
// vsync // vsync
for (int i=0; i<224*16;++i) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; } for (int i=0; i<t_states_per_scanline*vsync_lines;++i) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 16 passa a ser 15 i 224 passa a ser 228
// Upper border // Upper border
for (int i=0; i<48;++i) { for (int i=0; i<48;++i) {
// hsync // hsync
for (int j=0;j<48;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; } for (int j=0;j<t_states_per_scanline-176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 48 passsa a ser 52
//border //border
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; } for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
} }
// scanlines // scanlines
for (uint8_t y=0; y<192; ++y) for (uint8_t y=0; y<192; ++y)
{ {
// hsync // hsync
for (int j=0;j<48;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; } for (int j=0;j<t_states_per_scanline-176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 48 passsa a ser 52
// Left border // Left border
for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; } for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
// Actual screen // Actual screen
for (uint8_t x=0;x<32;++x) for (uint8_t x=0;x<32;++x)
{ {
uint16_t color = 0x5800 + x + (y>>3)*32; uint16_t color = /*0x5800 +*/ x + (y>>3)*32;
uint16_t address = 0x4000 | (x&0x1f) | ((y&0x7)<<8) | ((y&0x38)<<2) | ((y&0xc0)<<5); uint16_t address = /*0x4000 |*/ (x&0x1f) | ((y&0x7)<<8) | ((y&0x38)<<2) | ((y&0xc0)<<5);
for (int i=7;i>0;i-=2) for (int i=7;i>0;i-=2)
{ {
*(ptr_pixel++) = (address & 0x1FFF) | (i << 13); *(ptr_pixel++) = (address & 0x1FFF) | (i << 13);
@@ -77,15 +106,15 @@ namespace zxscreen
} }
// Right border // Right border
for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; } for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
} }
// Lower border // Lower border
for (int i=0; i<56;++i) { for (int i=0; i<56;++i) {
// hsync // hsync
for (int j=0;j<48;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; } for (int j=0;j<t_states_per_scanline-176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 48 passsa a ser 52
//border //border
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; } for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
} }
//printf("COUNT: %i\n", count); //printf("COUNT: %i\n", count);
} }
@@ -93,8 +122,16 @@ namespace zxscreen
bool eventHandler(SDL_Event *e) bool eventHandler(SDL_Event *e)
{ {
if (e->type==SDL_WINDOWEVENT) { if (e->type==SDL_WINDOWEVENT) {
//int x, y;
//SDL_GetWindowPosition(win, &x, &y);
//char tmp[256];
//sprintf(tmp, " %ix%i", x, y);
//setTitle(tmp);
if (e->window.event==SDL_WINDOWEVENT_CLOSE) { if (e->window.event==SDL_WINDOWEVENT_CLOSE) {
saveWindowConfiguration();
return false; return false;
} else if (e->window.event==SDL_WINDOWEVENT_FOCUS_LOST || e->window.event==SDL_WINDOWEVENT_MOVED) {
saveWindowConfiguration();
} else if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) { } else if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) {
redraw(); redraw();
} }
@@ -103,6 +140,7 @@ namespace zxscreen
if (z80debug::paused()) { if (z80debug::paused()) {
if (e->type == SDL_KEYDOWN) { if (e->type == SDL_KEYDOWN) {
if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) { if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) {
ui::setDialog(nullptr);
const uint8_t dt = z80::step(); const uint8_t dt = z80::step();
z80debug::cont(); z80debug::cont();
zxscreen::refresh(dt); zxscreen::refresh(dt);
@@ -127,11 +165,15 @@ namespace zxscreen
} }
} }
} }
if (e->type == SDL_MOUSEMOTION) {
SDL_ShowCursor(true);
}
return true; return true;
} }
void reinit() void reinit()
{ {
saveWindowConfiguration();
if (win) ui::window::unregisterWindow(SDL_GetWindowID(win)); if (win) ui::window::unregisterWindow(SDL_GetWindowID(win));
if (tex) SDL_DestroyTexture(tex); if (tex) SDL_DestroyTexture(tex);
@@ -139,8 +181,12 @@ namespace zxscreen
if (ren) SDL_DestroyRenderer(ren); if (ren) SDL_DestroyRenderer(ren);
if (win) SDL_DestroyWindow(win); if (win) SDL_DestroyWindow(win);
zoom = file::getConfigValueInteger("screen_zoom", 1);
const int x = file::getConfigValueInteger("screen_x", SDL_WINDOWPOS_UNDEFINED);
const int y = file::getConfigValueInteger("screen_y", SDL_WINDOWPOS_UNDEFINED);
const int z = fullscreen ? 1 : zoom; const int z = fullscreen ? 1 : zoom;
win = SDL_CreateWindow("ZX Spectrum Screen", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 352*z, 296*z, fullscreen?SDL_WINDOW_FULLSCREEN_DESKTOP:SDL_WINDOW_SHOWN); win = SDL_CreateWindow("ZX Spectrum Screen", x, y, 352*z, 296*z, fullscreen?SDL_WINDOW_FULLSCREEN_DESKTOP:SDL_WINDOW_ALLOW_HIGHDPI);
ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED); ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 352, 296); tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 352, 296);
uitex = ui::createtexture(ren); uitex = ui::createtexture(ren);
@@ -167,9 +213,31 @@ namespace zxscreen
focus(); focus();
} }
void init() void setBaseAddresses(const uint32_t pixeladdr, const uint32_t coloraddr)
{ {
pixel_base_addr = pixeladdr;
color_base_addr = coloraddr;
}
void init(int mode)
{
zxscreen::mode = mode;
if (mode==SCREEN_MODE_48K) {
setBaseAddresses(0x0000, 0x1800);
t_states_total = 69888;
t_states_per_scanline = 224;
vsync_lines = 16;
} else if (mode==SCREEN_MODE_128K) {
setBaseAddresses(0x14000, 0x15800);
t_states_total = 70908;
t_states_per_scanline = 228;
vsync_lines = 15;
}
create_tables(); create_tables();
ptr_pixel = zx_pixels;
t_screen = t_flash = 0;
flash = false;
reinit(); reinit();
} }
@@ -184,55 +252,58 @@ namespace zxscreen
void refresh(const uint32_t dt) void refresh(const uint32_t dt)
{ {
const uint8_t* memory = z80::getMem(); const uint8_t* pixel_mem = mem::rawPtr(pixel_base_addr);
const uint8_t* color_mem = mem::rawPtr(color_base_addr);
const uint8_t border_color = zx_ula::get_border_color(); const uint8_t border_color = zx_ula::get_border_color();
for (int i=0;i<dt;++i) for (int i=0;i<dt;++i)
{ {
if (color_addr[t_screen] != 0) if (color_addr[t_screen] != SCREEN_SYNC)
{ {
if (color_addr[t_screen] == 1) { if (color_addr[t_screen] == SCREEN_BORDER) {
*(ptr_pixel++) = border_color; *(ptr_pixel++) = border_color;
*(ptr_pixel++) = border_color; *(ptr_pixel++) = border_color;
//pixels_draw+=2;
} else { } else {
uint8_t color = memory[color_addr[t_screen]]; uint8_t color = *(color_mem + color_addr[t_screen]); // z80mem::get()->readMem(color_base_addr + color_addr[t_screen]);
uint8_t c1 = color&0x7, c2 = (color>>3)&0x7; uint8_t c1 = color&0x7, c2 = (color>>3)&0x7;
if ((color&0x80) && flash) { c1=c2; c2=color&0x7; } if ((color&0x80) && flash) { c1=c2; c2=color&0x7; }
if ((color&0x40)) { c1 |= 0x8; c2 |= 0x8; } if ((color&0x40)) { c1 |= 0x8; c2 |= 0x8; }
uint16_t address = (0x4000) | (pixel_addr[t_screen]&0x1FFF); uint16_t address = /*(0x4000) |*/ (pixel_addr[t_screen]&0x1FFF);
uint8_t mask = 1 << (pixel_addr[t_screen]>>13); uint8_t mask = 1 << (pixel_addr[t_screen]>>13);
uint8_t block = memory[address]; uint8_t block = *(pixel_mem + address); // z80mem::get()->readMem(pixel_base_addr + address);
*(ptr_pixel++)=(block&mask) ? c1 : c2; *(ptr_pixel++)=(block&mask) ? c1 : c2;
mask>>=1; mask>>=1;
*(ptr_pixel++)=(block&mask) ? c1 : c2; *(ptr_pixel++)=(block&mask) ? c1 : c2;
} }
pixels_draw+=2;
} }
t_screen++; t_screen++;
/*if (pixels_draw>352*296) if (t_screen>=t_states_total) {
{
printf("PIXELS OVERFLOW: %i\n", pixels_draw);
}*/
if (t_screen>=69888) {
//printf("PIXELS DRAWN: %i\n", pixels_draw);
pixels_draw=0;
t_flash++; t_flash++;
if (t_flash==16) { t_flash=0; flash = !flash; } if (t_flash==16) { t_flash=0; flash = !flash; }
t_screen=0; t_screen=0;
ptr_pixel = zx_pixels; ptr_pixel = zx_pixels;
redraw(); redraw();
//while (SDL_GetTicks()-time < 20) {} if (interrupt_enabled) z80::interrupt();
//time = SDL_GetTicks();
z80::interrupt();
} }
} }
} }
void fullrefresh() void fullrefresh()
{ {
uint32_t tmp = t_screen;
t_screen = 0; t_screen = 0;
refresh(69888); uint8_t * tmp_ptr = ptr_pixel;
ptr_pixel = zx_pixels;
interrupt_enabled = false;
refresh(t_states_total);
interrupt_enabled = true;
ptr_pixel = tmp_ptr;
t_screen = tmp;
}
void debugrefresh(const uint32_t dt)
{
if (full_refresh) fullrefresh(); else refresh(dt);
} }
void redraw(const bool present) void redraw(const bool present)
@@ -256,6 +327,9 @@ namespace zxscreen
// Pintem la textura a pantalla // Pintem la textura a pantalla
SDL_RenderCopy(ren, tex, NULL, &dest_rect); SDL_RenderCopy(ren, tex, NULL, &dest_rect);
//zx_128mem* mem = ((zx_128mem*)z80mem::get());
//ui::printtxt(0,0,mem->getShadowScreen()?"SHADOW":"NORMAL", COLOR_WHITE);
if (present) if (present)
SDL_RenderPresent(ren); SDL_RenderPresent(ren);
else else
@@ -316,6 +390,17 @@ namespace zxscreen
return fullscreen; return fullscreen;
} }
void toggleFullRefresh()
{
full_refresh = !full_refresh;
}
const bool getFullRefresh()
{
return full_refresh;
}
SDL_Renderer *getrenderer() SDL_Renderer *getrenderer()
{ {
return ren; return ren;

View File

@@ -1,13 +1,18 @@
#pragma once #pragma once
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#define SCREEN_MODE_48K 0
#define SCREEN_MODE_128K 1
namespace zxscreen namespace zxscreen
{ {
void init(); void init(int mode);
void setBaseAddresses(const uint32_t pixeladdr, const uint32_t coloraddr);
void reinit(); void reinit();
void focus(); void focus();
void refresh(const uint32_t dt); void refresh(const uint32_t dt);
void fullrefresh(); void fullrefresh();
void debugrefresh();
void redraw(const bool present=true); void redraw(const bool present=true);
void present(); void present();
void setTitle(const char* title); void setTitle(const char* title);
@@ -17,5 +22,8 @@ namespace zxscreen
void toggleFullscreen(); void toggleFullscreen();
const bool getFullscreen(); const bool getFullscreen();
void toggleFullRefresh();
const bool getFullRefresh();
SDL_Renderer *getrenderer(); SDL_Renderer *getrenderer();
} }

82
zx_speaker.cpp Normal file
View 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
View 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
View 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
View 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);
}

View File

@@ -1,7 +1,9 @@
#include "zx_tape.h" #include "zx_tape.h"
#include "zx_ula.h" #include "zx_ula.h"
#include "zx_screen.h" #include "zx_screen.h"
#include "zx_mem.h"
#include "z80debug.h" #include "z80debug.h"
#include "z80.h"
#include <vector> #include <vector>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -93,7 +95,7 @@ namespace zx_tape
pulse_level = 1; pulse_level = 1;
} }
void update(const uint8_t dt) void update(const uint32_t dt)
{ {
if (!playing) return; if (!playing) return;
@@ -201,7 +203,7 @@ namespace zx_tape
pulse_pos -= PULSE_LEN_SYNC3; pulse_pos -= PULSE_LEN_SYNC3;
pulse_level = 0; pulse_level = 0;
current_section = PULSE_WAIT; current_section = PULSE_WAIT;
printf("going to pulse_wait..%i.\n", current_block); printf("going to pulse_wait..%i (%i).\n", current_block, blocks[current_block].length);
} }
} }
@@ -209,7 +211,7 @@ namespace zx_tape
if (current_section == PULSE_WAIT) if (current_section == PULSE_WAIT)
{ {
pulse_level = 0; pulse_level = 0;
if (pulse_pos >= 3500000) if (pulse_pos >= z80::getClock())
{ {
pulse_pos = 0; pulse_pos = 0;
current_section = PULSE_PILOT; current_section = PULSE_PILOT;
@@ -243,6 +245,27 @@ namespace zx_tape
printf("tape loading: %i%\n", percent); printf("tape loading: %i%\n", percent);
} }
uint16_t fastLoad(const uint8_t block_type, const uint16_t address, const uint16_t length)
{
block_pos=0;
current_bit=0;
current_section = PULSE_PILOT;
current_pulse = 0;
pulse_pos = 0;
pulse_level = 0;
if (blocks[current_block].data[0] != block_type ||
blocks[current_block].length != length+2) {
printf("ERROR: Tape data not consistent with expectation\n");
z80debug::stop();
}
for (int i=0;i<length;++i) {
mem::writeMem(address+i, blocks[current_block].data[i+1]);
}
current_block++;
return address + length;
}
const bool getOption(const int option) const bool getOption(const int option)
{ {
return options[option]; return options[option];

View File

@@ -11,9 +11,10 @@ namespace zx_tape
void play(); void play();
void stop(); void stop();
void rewind(); void rewind();
void update(const uint8_t dt); void update(const uint32_t dt);
const bool getplaying(); const bool getplaying();
void report(); void report();
uint16_t fastLoad(const uint8_t block_type, const uint16_t address, const uint16_t length);
const bool getOption(const int option); const bool getOption(const int option);
void setOption(const int option, const bool value); void setOption(const int option, const bool value);

View File

@@ -1,8 +1,7 @@
#include "zx_ula.h" #include "zx_ula.h"
#include "z80.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#define AUDIO_BUFFER_SIZE 2048
namespace zx_ula namespace zx_ula
{ {
#define KEY_SHIFT 0 #define KEY_SHIFT 0
@@ -55,16 +54,20 @@ namespace zx_ula
uint8_t zx_keyboard[40]; uint8_t zx_keyboard[40];
uint8_t virtual_keyboard[40] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; uint8_t virtual_keyboard[40] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
char key_to_keyboard[40] {'<','Z','X','C','V','A','S','D','F','G','Q','W','E','R','T','1','2','3','4','5','0','9','8','7','6','P','O','I','U','Y','-','L','K','J','H','.',',','M','N','B'}; char key_to_keyboard[40] {
'<','Z','X','C','V',
'A','S','D','F','G',
'Q','W','E','R','T',
'1','2','3','4','5',
'0','9','8','7','6',
'P','O','I','U','Y',
'-','L','K','J','H',
'.',',','M','N','B'
};
static uint8_t border_color = 0; static uint8_t border_color = 0;
static uint8_t ear = 0; static uint8_t ear = 0;
static uint8_t mic = 0; static uint8_t mic = 0;
// this variable keeps track of the last "1" written to the sound buffer, so when streaming it in the audio callback, if there
// are only zeros remaining in the buffer, we just discard it. It's a poor man's method of keeping the buffer size in control.
// This is useless when there are music, sounds, tape loading, etc... as 1's and 0's keep alternating
static uint16_t last_1 = 0;
void update_zx_keyboard() void update_zx_keyboard()
{ {
const uint8_t *keys = SDL_GetKeyboardState(NULL); const uint8_t *keys = SDL_GetKeyboardState(NULL);
@@ -121,6 +124,12 @@ namespace zx_ula
// Keys in a normal keyboard that ara combinations in the zx one // Keys in a normal keyboard that ara combinations in the zx one
if (keys[SDL_SCANCODE_BACKSPACE]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_0] = 1; if (keys[SDL_SCANCODE_BACKSPACE]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_0] = 1;
if (keys[SDL_SCANCODE_PERIOD]) zx_keyboard[KEY_SYMBOL] = zx_keyboard[KEY_M] = 1; if (keys[SDL_SCANCODE_PERIOD]) zx_keyboard[KEY_SYMBOL] = zx_keyboard[KEY_M] = 1;
if (keys[SDL_SCANCODE_UP]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_7] = 1;
if (keys[SDL_SCANCODE_DOWN]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_6] = 1;
if (keys[SDL_SCANCODE_LEFT]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_5] = 1;
if (keys[SDL_SCANCODE_RIGHT]) zx_keyboard[KEY_SHIFT] = zx_keyboard[KEY_8] = 1;
} }
int port_in(int port) int port_in(int port)
@@ -214,93 +223,13 @@ namespace zx_ula
} }
uint8_t get_border_color() { return border_color; } uint8_t get_border_color() { return border_color; }
void set_border_color(uint8_t col) { border_color = col; }
uint8_t get_ear() { return ear; } uint8_t get_ear() { return ear; }
void set_ear(const uint8_t val) { ear = val; } void set_ear(const uint8_t val) { ear = val; }
uint8_t get_sample()
SDL_AudioDeviceID sdlAudioDevice;
uint8_t sound_buffer[AUDIO_BUFFER_SIZE];
uint16_t sound_pos=0;
uint16_t sound_start=0;
uint16_t t_sound=0;
uint32_t samples_generated=0;
uint32_t samples_time =0;
uint32_t samples_t=0;
void audioCallback(void * userdata, uint8_t * stream, int len)
{ {
//const uint16_t top = sound_pos < len ? sound_pos : len; return ear*128;
//if (top<len) printf("top: %i, len: %i, pos: %i\n", top, len, sound_pos);
//memcpy(stream, sound_buffer, top);
for (int i=0;i<len;++i)
{
stream[i] = sound_buffer[(sound_start++)&(AUDIO_BUFFER_SIZE-1)];
}
//if (top<len) memchr(&stream[top], sound_buffer[top-1], len-top);
/*if (top<sound_pos)
{
if (last_1>top)
{
memcpy(sound_buffer, &sound_buffer[top], sound_pos-top);
sound_pos=sound_pos-top;
last_1=last_1-top;
}
else
{
sound_pos=last_1=0;
}
}
else
{
sound_pos=last_1=0;
}*/
}
void sound_init()
{
SDL_AudioSpec audioSpec{11025, AUDIO_U8, 1, 0, AUDIO_BUFFER_SIZE>>2, 0, 0, &audioCallback, NULL};
sdlAudioDevice = SDL_OpenAudioDevice(NULL, 0, &audioSpec, NULL, 0);
sound_enable();
samples_time=SDL_GetTicks();
}
void sound_enable()
{
SDL_PauseAudioDevice(sdlAudioDevice, 0);
}
void sound_disable()
{
SDL_PauseAudioDevice(sdlAudioDevice, 1);
}
void sound_update(const uint8_t dt)
{
t_sound += dt;
samples_t += dt;
if (t_sound>=317) {
t_sound-=317;
samples_generated++;
if (samples_t >=3500000) {
//printf("%i\n", samples_generated);
samples_generated=0;
samples_t = 0;
}
/*if (SDL_GetTicks()>=samples_time+1000) {
printf("%i\n", samples_generated);
samples_generated=0;
samples_time = SDL_GetTicks();
}*/
//sound_pos = (sound_pos+1) & 0x3ff;
//sound_buffer[sound_pos] = ear*128;
//if (sound_pos>=AUDIO_BUFFER_SIZE) sound_pos = last_1 = 0;
sound_buffer[(sound_pos++)&(AUDIO_BUFFER_SIZE-1)] = ear*128;
//if (ear) last_1 = sound_pos;
}
} }
const int getKey(const char key) const int getKey(const char key)

View File

@@ -8,13 +8,11 @@ namespace zx_ula
void port_out(int port, int val); void port_out(int port, int val);
uint8_t get_border_color(); uint8_t get_border_color();
void set_border_color(uint8_t col);
uint8_t get_ear(); uint8_t get_ear();
void set_ear(const uint8_t val); void set_ear(const uint8_t val);
void sound_init(); uint8_t get_sample();
void sound_enable();
void sound_disable();
void sound_update(const uint8_t dt);
void keydown(const char key); void keydown(const char key);
void keyup(const char key); void keyup(const char key);