Commit 092f9061 authored by Sergio Costas's avatar Sergio Costas

Merge branch 'better_z80'

parents 70ab1dff ab9d286c
## VERSIONS ##
* Version 3.8.0 (2017-03-06)
* Updated the Z80 emulator and the screen emulation, which now is so precise that can run flawlessly the SHOCK demo both in 48K and +3 mode
* Removed the code that removed the DC in the audio output, because the sound was really ugly
* Version 3.7.0 (2017-03-04)
* Now the interrupt line is kept low exactly 32tstates
* Now the EI as prefix works fine
......
CC=g++ -c -O2
CPP=g++ -c -O2
LN=g++ -O2
CC=g++ -c -O2 -g
CPP=g++ -c -O2 -g
LN=g++ -O2 -g
CFLAGS += `pkg-config --cflags sdl libpulse-simple alsa` -D D_SOUND_PULSE -D D_SOUND_ALSA -D D_SOUND_OSS
CPPFLAGS += `pkg-config --cflags sdl libpulse-simple alsa` -D D_SOUND_PULSE -D D_SOUND_ALSA -D D_SOUND_OSS
LDFLAGS += `pkg-config --libs sdl libpulse-simple alsa`
fbzx: cargador.o cmdline.o computer.o emulator.o keyboard.o llscreen.o llsound.o menus.o microdrive.o mouse.o osd.o screen.o signals.o spk_ay.o tape.o Z80free.o Z80free_codes.o Z80free_codesCB.o Z80free_codesED.o Z80free_codesDD.o Z80free_codesFD.o Z80free_codesDDCB.o Z80free_codesFDCB.o
fbzx: cargador.o cmdline.o computer.o emulator.o keyboard.o llscreen.o llsound.o menus.o microdrive.o mouse.o osd.o screen.o signals.o spk_ay.o tape.o Z80free.o Z80free_codes.o Z80free_codesCB.o Z80free_codesED.o Z80free_codesDD.o Z80free_codesFD.o Z80free_codesDDCB.o Z80free_codesFDCB.o
$(LN) -o fbzx cargador.o cmdline.o computer.o emulator.o keyboard.o llscreen.o llsound.o menus.o microdrive.o mouse.o osd.o screen.o signals.o spk_ay.o tape.o Z80free.o Z80free_codes.o Z80free_codesCB.o Z80free_codesED.o Z80free_codesDD.o Z80free_codesFD.o Z80free_codesDDCB.o Z80free_codesFDCB.o $(LDFLAGS)
Z80free.o: z80free/Z80free.c z80free/Z80free.h
......@@ -78,4 +78,3 @@ spk_ay.o: spk_ay.cpp spk_ay.hh llsound.hh computer.hh screen.hh signals.hh llscr
tape.o: tape.cpp z80free/Z80free.h tape.hh signals.hh emulator.hh screen.hh osd.hh computer.hh llscreen.hh
$(CPP) $(CPPFLAGS) -o tape.o tape.cpp
......@@ -61,7 +61,8 @@ computer::computer() {
this->sound_bit = 0;
this->contended_zone = false;
this->memcontended_zone = 0;
this->cicles_counter=0;
this->interr = 0;
......@@ -94,10 +95,11 @@ bool computer::callback_receiver(string signal_received, class Signals *object)
byte computer::bus_empty () {
if (ordenador->current_mode != MODE_P3)
return (screen->bus_value);
else
if (ordenador->current_mode != MODE_P3) {
return (screen->get_bus_value(this->cicles_counter));
} else {
return (255); // +2A and +3 returns always 255
}
}
/* calls all the routines that emulates the computer, runing them for 'tstados'
......@@ -127,23 +129,51 @@ void computer::emulate (int tstados) {
// check if there's contention and waits the right number of tstates
void computer::do_contention() {
void computer::do_contention(bool io, word addr) {
if (!this->contended_zone) {
if (this->memcontended_zone == 0) {
return;
}
if (this->cicles_counter<14335) {
if (!io) {
switch (ordenador->current_mode) {
case MODE_128K:
case MODE_P2:
case MODE_128K_SPA:
if (((this->mport1 & 0x01) == 1) && (addr >= 0xC000)) { // pages 1, 3, 5 and 7 and access to upper 16K block
this->emulate(this->memcontended_zone); // are contended
return;
}
break;
case MODE_P3:
if (this->mport2 & 0x01) {
if ((this->mport2 & 0x06) == 0x00) { // 0, 1, 2 and 3, which are not contended
return;
}
if ((this->mport2 & 0x06) == 0x02) { // 4, 5, 6 and 7, which are all contended
this->emulate(this->memcontended_zone);
return;
}
// here it can be 4, 5, 6 and 3, or 4, 7, 6 and 3. In both cases, there is contention only in the first 48K
if (addr < 0xC000) {
this->emulate(this->memcontended_zone); // are contended
}
return;
}
if (((this->mport1 & 0x04) == 4) && (addr >= 0xC000)) { // pages 4, 5, 6 and 7 and access to upper 16K block
this->emulate(this->memcontended_zone); // are contended
return;
}
break;
}
}
if ((addr & 0xC000) == 0x4000) {
this->emulate(this->memcontended_zone);
return;
}
int ccicles = (this->cicles_counter - 14335) % 8;
if (ccicles>5) {
if (((addr & 0x0001) == 0) && io){
this->emulate(this->memcontended_zone);
return;
}
this->emulate(6-ccicles);
}
// resets the computer and loads the right ROMs
......@@ -184,28 +214,20 @@ void ResetComputer () {
screen->reset(ordenador->current_mode);
microdrive->reset();
mouse->reset();
OOTape->rewind();
}
void extra_contention(bool IO) {
void extra_contention() {
int tstates;
if (IO) {
tstates = 3;
} else if (procesador.M1) {
tstates = 4;
} else {
tstates = 3;
}
ordenador->emulate(tstates);
ordenador->contended_cicles += tstates;
ordenador->emulate(procesador.subtstates);
ordenador->contended_cicles += procesador.subtstates;
procesador.subtstates = 0;
}
void Z80free_Wr (word Addr, byte Value) {
extra_contention(false);
if ((Addr & 0xC000) == 0x4000) {
ordenador->do_contention();
}
extra_contention();
ordenador->do_contention(false, Addr);
ordenador->write_memory(Addr,Value);
}
......@@ -231,28 +253,22 @@ void computer::write_memory (uint16_t Addr, uint8_t Value) {
*(ordenador->block3 + Addr) = (unsigned char) Value;
break;
}
}
byte Z80free_Rd (word Addr) {
extra_contention(false);
extra_contention();
if((microdrive->mdr_active)&&(microdrive->mdr_paged)&&(Addr<8192)) // Interface I
return((byte)ordenador->shadowrom[Addr]);
switch (ordenador->other_ret) {
case 1:
if (ordenador->other_ret == 1) {
ordenador->other_ret = 0;
return (201); // RET instruction
break;
default:
if ((Addr & 0xC000) == 0x4000) {
ordenador->do_contention();
}
return (ordenador->read_memory(Addr));
}
ordenador->do_contention(false, Addr);
return (ordenador->read_memory(Addr));
}
uint8_t computer::read_memory(uint16_t Addr) {
......@@ -285,22 +301,19 @@ void Z80free_Out (word Port, byte Value) {
// Microdrive access
register word maskport;
extra_contention(true);
extra_contention();
if (((Port&0x0001)==0)||((Port>=0x4000)&&(Port<0x8000))) {
if (ordenador->current_mode != MODE_P3) {
ordenador->do_contention();
}
if (ordenador->current_mode != MODE_P3) {
ordenador->do_contention(true, Port);
}
// ULAPlus
if (Port == 0xBF3B) {
ordenador->do_contention();
screen->set_ulaplus_register(Value);
return;
}
if (Port == 0xFF3B) {
ordenador->do_contention();
screen->set_ulaplus_value(Value);
}
......@@ -356,17 +369,14 @@ byte Z80free_In (word Port) {
static unsigned int temporal_io;
byte pines;
extra_contention(true);
if (((Port&0x0001)==0)||((Port>=0x4000)&&(Port<0x8000))) {
if (ordenador->current_mode != MODE_P3) {
ordenador->do_contention();
}
extra_contention();
if (ordenador->current_mode != MODE_P3) {
ordenador->do_contention(true, Port);
}
temporal_io = (unsigned int) Port;
if (Port == 0xFF3B) {
ordenador->do_contention();
return (screen->read_ulaplus_value());
}
......@@ -429,15 +439,15 @@ byte Z80free_In (word Port) {
}
}
if ((temporal_io == 0xFFFD)&&(spk_ay->ay_emul))
if ((temporal_io == 0xFFFD)&&(spk_ay->ay_emul)) {
return (spk_ay->get_value());
}
// Microdrive access
if(((Port & 0x0018) != 0x0018) && (microdrive->mdr_active))
return(microdrive->in(Port));
if(((Port & 0x0018) != 0x0018) && (microdrive->mdr_active)) {
return(microdrive->in(Port));
}
pines=ordenador->bus_empty();
......
......@@ -55,7 +55,7 @@ public:
bool dblscan;
bool bw;
bool contended_zone; // 0-> no contention; 1-> contention possible
int memcontended_zone; // memory contended tstates from this instant up to the next non-contended block
int cicles_counter; // counts how many pixel clock cicles passed since las interrupt
int contended_cicles; // cicles used during contention (must not be counted after ending the execution of an instruction)
......@@ -70,6 +70,7 @@ public:
unsigned char bus_counter;
unsigned char bus_value;
unsigned char bus_value2;
bool issue_3; // 2= 48K issue 2, 3= 48K issue 3
enum CurrentMode current_mode;
unsigned char port254;
......@@ -103,7 +104,7 @@ public:
bool callback_receiver(string, class Signals *);
byte bus_empty();
void emulate(int);
void do_contention();
void do_contention(bool io, word addr);
uint8_t read_memory(uint16_t Addr);
void write_memory (uint16_t Addr, uint8_t Value);
};
......
......@@ -44,7 +44,6 @@ char salir;
char path_snaps[2049];
char path_taps[2049];
char path_mdrs[2049];
unsigned int jump_frames, curr_frames;
string filenames[5];
string load_a_rom(string *filenames) {
......@@ -438,9 +437,6 @@ int main(int argc,char *argv[]) {
gamefile = "";
jump_frames = parse.jump;
curr_frames = 0;
printf("Computer init\n");
printf("Modo: %d\n",ordenador->current_mode);
......@@ -500,12 +496,13 @@ int main(int argc,char *argv[]) {
printf("BPP: %d\n",llscreen->bpp);
debug_var = false;
while(salir) {
do {
ordenador->contended_cicles = 0;
tstados=Z80free_ustep(&procesador);
if(tstados<0) {
if((tstados - ordenador->contended_cicles)<0) {
printf("Error %X\n",procesador.PC);
exit(1);
}
......@@ -596,7 +593,7 @@ int main(int argc,char *argv[]) {
microdrive->mdr_paged = 2;
}
if(ordenador->interr==1) {
if(ordenador->interr>=1) {
keyboard->read_keyboard (NULL); // read the physical keyboard
ordenador->interr=0;
}
......
......@@ -34,7 +34,6 @@ extern Z80FREE procesador;
extern char path_snaps[2049];
extern char path_taps[2049];
extern char path_mdrs[2049];
extern unsigned int jump_frames,curr_frames;
void load_rom(char);
void load_main_game(const char *nombre);
......
......@@ -14,7 +14,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#include "llsound.hh"
......@@ -205,18 +205,18 @@ int LLSound::init_pulse() {
#ifdef D_SOUND_ALSA
int LLSound::init_alsa() {
int err;
snd_pcm_hw_params_t *hw_params;
unsigned int resample,samplerate;
snd_pcm_uframes_t bufferSize;
err = snd_pcm_open( &_soundDevice, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, 0 );
if (err<0) {
return -1;
}
if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
snd_pcm_close (_soundDevice);
return -2;
......@@ -227,12 +227,12 @@ int LLSound::init_alsa() {
return -2;
}
if ((err = snd_pcm_hw_params_set_access (_soundDevice, hw_params,SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
snd_pcm_close (_soundDevice);
return -3;
}
if (snd_pcm_hw_params_set_format (_soundDevice, hw_params,SND_PCM_FORMAT_U8) >= 0) {
this->sign=0;
this->format=0;
......@@ -255,7 +255,7 @@ int LLSound::init_alsa() {
snd_pcm_close (_soundDevice);
return -3;
}
// Disable resampling.
resample = 0;
err = snd_pcm_hw_params_set_rate_resample(_soundDevice, hw_params, resample);
......@@ -263,7 +263,7 @@ int LLSound::init_alsa() {
snd_pcm_close (_soundDevice);
return -3;
}
if ((err = snd_pcm_hw_params_set_channels (_soundDevice, hw_params, 1)) >= 0) {
this->channels=1;
} else if ((err = snd_pcm_hw_params_set_channels (_soundDevice, hw_params, 2)) >= 0) {
......@@ -272,7 +272,7 @@ int LLSound::init_alsa() {
snd_pcm_close (_soundDevice);
return -3;
}
samplerate=48000;
if ((err = snd_pcm_hw_params_set_rate_near (_soundDevice, hw_params, &samplerate, 0)) < 0) {
snd_pcm_close (_soundDevice);
......@@ -286,7 +286,7 @@ int LLSound::init_alsa() {
}
this->freq=samplerate;
err = snd_pcm_hw_params (_soundDevice, hw_params);
if (err<0) {
return -3;
......@@ -294,7 +294,7 @@ int LLSound::init_alsa() {
//snd_pcm_hw_params_get_buffer_size( hw_params, &bufferSize );
this->buffer_len=bufferSize;
started_sound=0;
return 0;
......@@ -308,7 +308,7 @@ int LLSound::init_alsa() {
#ifdef D_SOUND_OSS
int LLSound::init_oss() {
int parameter;
int parameter2;
int bytes;
......@@ -328,12 +328,12 @@ int LLSound::init_oss() {
this->buffer_len=2048/(this->channels*bytes);
parameter=0x0002000C ; // two buffers with 4096 bytes each one
if(ioctl(audio_fd,SNDCTL_DSP_SETFRAGMENT, &parameter)==-1)
return (-6);
// set format
if(ioctl(audio_fd,SNDCTL_DSP_GETFMTS, &parameter2)==-1)
return (-2);
parameter = 2; // we want mono audio
......@@ -341,9 +341,9 @@ int LLSound::init_oss() {
return (-4);
this->channels = parameter;
// Priority: U8, S8, U16LE, S16LE, U16BE, U16LE
if(parameter2 & AFMT_S16_BE) {
parameter = AFMT_S16_BE;
}
......@@ -362,14 +362,14 @@ int LLSound::init_oss() {
if(parameter2 & AFMT_U8) {
parameter = AFMT_U8;
}
bytes=0; //8 bits
retval=ioctl(audio_fd,SNDCTL_DSP_SETFMT,&parameter);
if(retval != 0) {
return (-3);
}
switch(parameter) {
case AFMT_U8:
this->sign=0;
......@@ -384,7 +384,7 @@ int LLSound::init_oss() {
case AFMT_U16_LE:
this->sign=0;
this->format=1;
bytes=2;
bytes=2;
break;
case AFMT_S16_LE:
this->sign=-128;
......@@ -402,7 +402,7 @@ int LLSound::init_oss() {
bytes=2;
break;
}
parameter=48000; // we want, by default, 48000 samples per second
if(ioctl(audio_fd,SNDCTL_DSP_SPEED, &parameter)==-1)
return (-5);
......@@ -413,21 +413,39 @@ int LLSound::init_oss() {
parameter=bi.bytes/4;
this->buffer_len = parameter/(this->channels * bytes);
return(0);
return(0);
}
#endif
void LLSound::remove_dc(unsigned char *sound_buffer,int size){
static float oldsample=0.0;
static float origsample;
static int i;
static unsigned char origsamples[FILTER_SAMPLES];
int i;
int j;
int v;
int m;
unsigned char sample;
m = 0;
for (i=0;i<size;i++) {
m += sound_buffer[i];
}
m /= size;
for (i=0;i<size;i++) {
origsample=((float)(sound_buffer[i]))-128;
oldsample=(origsample+oldsample*999.0)*0.001+1e-6;
sound_buffer[i]=(int)(origsample-oldsample)*0.98+128;
sample = sound_buffer[i];
v = (int) sample;
/*v -= m;
v += 128;*/
for(j=0;j<FILTER_SAMPLES;j++) {
v += (int) origsamples[j];
if (j != 0) {
origsamples[j-1] = origsamples[j];
}
}
v /= (FILTER_SAMPLES + 1);
origsamples[FILTER_SAMPLES - 1] = sample;
sound_buffer[i] = (unsigned char) v;
}
}
......
......@@ -14,7 +14,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#ifndef SOUND_H
......@@ -28,6 +28,8 @@
#define D_SOUND_OSS
#endif
#define FILTER_SAMPLES 8
enum e_soundtype {SOUND_NO, SOUND_OSS, SOUND_ALSA, SOUND_PULSEAUDIO, SOUND_AUTOMATIC};
extern class LLSound *llsound;
......
......@@ -56,7 +56,7 @@ void help_menu() {
llscreen->clear_screen();
llscreen->print_string("FBZX (3.7.0)",-1,1,15,0);
llscreen->print_string("FBZX (3.8.0)",-1,1,15,0);
llscreen->print_string("Available keys",-1,2,14,0);
llscreen->print_string("Shift:Caps Shift Ctrl:Symbol Shift",-1,4,11,0);
......
This diff is collapsed.
......@@ -21,6 +21,7 @@ class Screen {
unsigned int translate[6144],translate2[6144];
unsigned int *p_translt,*p_translt2;
unsigned char *pixel; // current address
unsigned char *max_pixel; // max address
unsigned char *base_pixel;
char flash;
int currline,currpix;
......@@ -29,27 +30,39 @@ class Screen {
int next_line; // cuantity to add when we reach the end of line to go to next line
int next_scanline; // cuantity to add to pass to the next scanline
int first_line; // first line to start to paint
int last_line; // last line to paint
int first_pixel; // first pixel of a line to paint
int last_pixel; // last pixel of a line to paint
int last_line;
int first_column;
int last_column;
int next_pixel; // next pixel
int jump_pixel;
int contador_flash;
int tstados_counter; // counts tstates leaved to the next call
int tstados_counter2; // counts tstates for empty bus
int int_counter; // counts the interrupt duration
int pixancho,pixalto; // maximum pixel value for width and height
int pixborde_top; // number of scanlines in border top
int tstate_contention;
int tstate_contention2;
int offset;
int offset2;
int hoffset;
int offset_p3;
int tstates_screen;
bool screen_snow; // 0-> no emulate snow; 1-> emulate snow
uint8_t ulaplus_reg; // contains the last selected register in the ULAPlus
void paint_pixels (uint8_t octet,uint8_t ink, uint8_t paper);
uint8_t bus_value;
uint8_t user_ink;
uint8_t user_paper;
uint8_t user_pixels;
public:
uint8_t border;
bool ulaplus; // ULAPlus inactive or active
uint8_t bus_value;
Screen();
void set_memory_pointers ();
......@@ -58,6 +71,7 @@ public:
void set_ulaplus_register(uint8_t);
void set_ulaplus_value(uint8_t);
uint8_t read_ulaplus_value();
uint8_t get_bus_value(int tstados);
};
......
......@@ -104,108 +104,105 @@ void SPK_AY::play_ay (int tstados) {
this->tst_ay2 -= 128;
env_period=2*(((unsigned int) this->ay_registers[11]) + 256 * ((unsigned int) (this->ay_registers[12])));
if (!env_period) env_period = 1;
if (this->aych_envel<env_period) // to check
this->aych_envel++;
else {
this->aych_envel = 0;
if (this->ay_envel_way & 0x02) // start cycle?
switch ((this->
ay_registers[13]) & 0x0F)
{
case 0:
case 1:
case 2:
case 3:
case 8:
case 9:
case 10:
case 11:
this->ay_envel_way = 4; // cycle started and decrementing
this->ay_envel_value = 16;
break;
default:
this->ay_envel_way = 5; // cycle started and incrementing
this->ay_envel_value = -1;
}
if (this->ay_envel_way & 0x04) // cycle started?
if (!env_period) {
env_period = 1;
}
if (this->aych_envel<env_period) { // to check
this->aych_envel++;
} else {
this->aych_envel = 0;
if (this->ay_envel_way & 0x02) // start cycle?
switch ((this->
ay_registers[13]) & 0x0F)
{
switch ((this->
ay_registers[13]) & 0x0F)
{
case 0:
case 1:
case 2:
case 3:
case 9:
this->ay_envel_value--;
if (this->
ay_envel_value == 0)
this->ay_envel_way = 0; // end
break;
case 4:
case 5:
case 6:
case 7:
case 15:
case 0:
case 1:
case 2:
case 3:
case 8:
case 9:
case 10:
case 11:
this->ay_envel_way = 4; // cycle started and decrementing
this->ay_envel_value = 16;
break;
default:
this->ay_envel_way = 5; // cycle started and incrementing
this->ay_envel_value = -1;
}
if (this->ay_envel_way & 0x04) { // cycle started?
switch ((this->ay_registers[13]) & 0x0F) {
case 0:
case 1:
case 2:
case 3:
case 9:
this->ay_envel_value--;
if (this->ay_envel_value == 0) {
this->ay_envel_way = 0; // end
}
break;
case 4:
case 5:
case 6:
case 7:
case 15:
this->ay_envel_value++;
if (this->ay_envel_value == 16) {
this->ay_envel_value = 0;
this->ay_envel_way = 0; // end
}
break;
case 8:
this->ay_envel_value--;
if (this->ay_envel_value == -1) {
this->ay_envel_value = 15; // repeat
}
break;
case 10:
case 14:
if (this->ay_envel_way & 0x01) { //incrementing?
this->ay_envel_value++;
if (this->
ay_envel_value == 16)
{
this->
ay_envel_value
= 0;
this->ay_envel_way = 0; // end
}
break;
case 8:
} else {
this->ay_envel_value