diff --git a/abusimbel.tap b/abusimbel.tap new file mode 100644 index 0000000..652cac9 Binary files /dev/null and b/abusimbel.tap differ diff --git a/main.cpp b/main.cpp index d823d23..9e4544e 100644 --- a/main.cpp +++ b/main.cpp @@ -5,18 +5,11 @@ #include "z80debug.h" #include "zx_ula.h" #include "zx_screen.h" +#include "zx_tape.h" #include #include uint8_t memory[65536]; -uint32_t t = 0; -uint16_t ts = 0; -uint8_t ft = 0; -uint32_t fps=0; -uint32_t fps_time=0; - - -uint8_t test = 0; int main(int argc, char *argv[]) { @@ -33,6 +26,8 @@ int main(int argc, char *argv[]) zx_ula::sound_init(); + zx_tape::load("abusimbel.tap"); + bool should_exit = false; SDL_Event e; @@ -44,27 +39,22 @@ int main(int argc, char *argv[]) if (z80debug::debugging()) { if ((e.type==SDL_WINDOWEVENT) && ((e.window.event==SDL_WINDOWEVENT_SHOWN) || (e.window.event==SDL_WINDOWEVENT_EXPOSED))) { z80debug::refresh(); - zxscreen::refresh(); + zxscreen::refresh(0, true); } if (e.type == SDL_KEYDOWN) { if (e.key.keysym.scancode==SDL_SCANCODE_ESCAPE) { should_exit=true; break; } else if (e.key.keysym.scancode==SDL_SCANCODE_F10) { - t += z80::step(); - if (t>=69888) { t=0; z80::interrupt(); } + const uint8_t dt = z80::step(); z80debug::refresh(); - zxscreen::refresh(); + zxscreen::refresh(dt, true); } else if (e.key.keysym.scancode==SDL_SCANCODE_F11) { - t += z80debug::next(); - fps=0; - fps_time = SDL_GetTicks(); - zxscreen::refresh(); + const uint8_t dt = z80debug::next(); + zxscreen::refresh(dt, true); } else if (e.key.keysym.scancode==SDL_SCANCODE_F5) { - t += z80::step(); + const uint8_t dt = z80::step(); z80debug::cont(); - fps=0; - fps_time = SDL_GetTicks(); - zxscreen::refresh(); + zxscreen::refresh(dt); } else if (e.key.keysym.scancode==SDL_SCANCODE_RETURN) { z80debug::executeConsole(); } else if (e.key.keysym.scancode==SDL_SCANCODE_BACKSPACE) { @@ -78,7 +68,10 @@ int main(int argc, char *argv[]) if (e.type == SDL_KEYDOWN) { if (e.key.keysym.scancode==SDL_SCANCODE_F8) { z80debug::stop(); - zxscreen::refresh(); + zxscreen::refresh(0, true); + } + if (e.key.keysym.scancode==SDL_SCANCODE_F12) { + zx_tape::play(); } } } @@ -86,30 +79,12 @@ int main(int argc, char *argv[]) if (!z80debug::debugging()) { if (z80debug::isbreak(z80::getPC(), 9)) { z80debug::stop(); - zxscreen::refresh(); + zxscreen::refresh(0, true); } else { - uint8_t t_states = z80::step(); - t += t_states; - ts += t_states; - if (ts>=400) { - ts-=400; - zx_ula::sound_update(); - } - if (t>=69888) { - ft++; - if (ft==16) { ft=0; zxscreen::flash(); } -/* - fps++; - if (SDL_GetTicks() - fps_time >= 1000) { - printf("FPS: %i\n", fps); - fps = 0; - fps_time = SDL_GetTicks(); - } -*/ - t=0; - zxscreen::refresh(); - z80::interrupt(); - } + uint8_t dt = z80::step(); + zx_tape::update(dt); + zx_ula::sound_update(dt); + zxscreen::refresh(dt); } } } diff --git a/zx_screen.cpp b/zx_screen.cpp index e998a2c..66b223e 100644 --- a/zx_screen.cpp +++ b/zx_screen.cpp @@ -13,9 +13,9 @@ namespace zxscreen SDL_Renderer *ren = nullptr; SDL_Texture *tex = nullptr; - bool _flash = false; - - void flash() { _flash = not _flash; } + uint32_t t_screen = 0; + uint8_t t_flash = 0; + bool flash = false; void show() { @@ -23,11 +23,18 @@ namespace zxscreen win = SDL_CreateWindow("ZX Spectrum Screen", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 352, 288, SDL_WINDOW_RESIZABLE); ren = SDL_CreateRenderer(win, -1, 0); tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 352, 288); - refresh(); + refresh(0); } - void refresh() + void refresh(const uint8_t dt, const bool force_refresh) { + t_screen += dt; + if (t_screen>=69888) { + t_flash++; + if (t_flash==16) { t_flash=0; flash = !flash; } + t_screen=0; + } else if (!force_refresh) return; + const uint8_t* memory = z80::getMem(); const uint8_t border_color = zx_ula::get_border_color(); //memory+=0x4000; @@ -53,7 +60,7 @@ namespace zxscreen uint16_t address = 0x4000 | (x&0x1f) | ((y&0x7)<<8) | ((y&0x38)<<2) | ((y&0xc0)<<5); uint8_t block = memory[address]; 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; } for (int i=0;i<8;++i) { *(pixels++)=(block&0x80) ? palette[c1] : palette[c2]; @@ -71,5 +78,7 @@ namespace zxscreen SDL_UnlockTexture(tex); SDL_RenderCopy(ren, tex, NULL, NULL); SDL_RenderPresent(ren); + + if (t_screen==0) z80::interrupt(); } } diff --git a/zx_screen.h b/zx_screen.h index 276bf7b..16eca18 100644 --- a/zx_screen.h +++ b/zx_screen.h @@ -1,8 +1,8 @@ #pragma once +#include namespace zxscreen { void show(); - void refresh(); - void flash(); + void refresh(const uint8_t dt, const bool force_refresh=false); } diff --git a/zx_tape.cpp b/zx_tape.cpp new file mode 100644 index 0000000..714276b --- /dev/null +++ b/zx_tape.cpp @@ -0,0 +1,212 @@ +#include "zx_tape.h" +#include "zx_ula.h" +#include +#include +#include + +namespace zx_tape +{ + #define PULSE_PILOT 0 + #define PULSE_SYNC1 1 + #define PULSE_SYNC2 2 + #define PULSE_DATA 3 + #define PULSE_CHECKSUM 4 + #define PULSE_SYNC3 5 + #define PULSE_WAIT 6 + + #define PULSE_LEN_ZERO 855 + #define PULSE_LEN_ONE 1710 + + #define PULSE_LEN_PILOT 2168 + #define PULSE_LEN_SYNC1 667 + #define PULSE_LEN_SYNC2 735 + #define PULSE_LEN_SYNC3 954 + + #define PULSE_COUNT_PILOT_HEADER 8063 + #define PULSE_COUNT_PILOT_DATA 3223 + + struct block_t + { + uint16_t length; + uint8_t flag; + uint8_t *data; + uint8_t checksum; + }; + + bool playing = false; + bool loaded = false; + + std::vector blocks; + uint8_t current_block = 0; + uint16_t block_pos=0; + uint8_t current_bit=0; + + uint8_t current_section = PULSE_PILOT; + uint16_t current_pulse = 0; + uint32_t pulse_pos = 0; + uint8_t pulse_level = 0; + + void load(const char* filename) + { + //[TODO] Free memory that might be taken by previous tape + + FILE *f = fopen(filename, "rb"); + if (!f) return; + while (!feof(f)) + { + block_t block; + fread(&block.length, 2, 1, f); + fread(&block.flag, 1, 1, f); + block.length -= 2; // substract flag and checksum + block.data = (uint8_t *)malloc(block.length); + fread(block.data, block.length, 1, f); + fread(&block.checksum, 1, 1, f); + blocks.push_back(block); + } + fclose(f); + + loaded = true; + playing = false; + rewind(); + } + + void play() + { + if (!loaded) return; + playing = true; + } + + void stop() + { + playing = false; + } + + void rewind() + { + if (!loaded) return; + current_block = block_pos = current_bit = 0; + current_section = PULSE_PILOT; + current_pulse = pulse_pos = 0; + pulse_level = 1; + } + + void update(const uint8_t dt) + { + if (!playing) return; + + pulse_pos += dt; + + if (current_section == PULSE_PILOT) + { + const uint16_t pulse_count = blocks[current_block].flag<128 ? PULSE_COUNT_PILOT_HEADER : PULSE_COUNT_PILOT_DATA; + if (pulse_pos >= PULSE_LEN_PILOT ) + { + pulse_pos -= PULSE_LEN_PILOT; + current_pulse++; + pulse_level = pulse_level?0:1; + if (current_pulse >= pulse_count) + { + current_pulse = 0; + pulse_level = 1; + current_section = PULSE_SYNC1; + } + } + } + + if (current_section == PULSE_SYNC1) + { + if (pulse_pos >= PULSE_LEN_SYNC1) + { + pulse_pos -= PULSE_LEN_SYNC1; + pulse_level = 0; + current_section = PULSE_SYNC2; + } + } + + if (current_section == PULSE_SYNC2) + { + if (pulse_pos >= PULSE_LEN_SYNC2) + { + pulse_pos -= PULSE_LEN_SYNC2; + pulse_level = 1; + current_section = PULSE_DATA; + } + } + + if (current_section == PULSE_DATA) + { + const uint8_t datum = blocks[current_block].data[block_pos]; + const uint16_t pulse_len = (datum & (0x80>>current_bit)) == 0 ? PULSE_LEN_ZERO : PULSE_LEN_ONE; + if (pulse_pos >= pulse_len) + { + pulse_pos -= pulse_len; + pulse_level--; + if (pulse_level>=2) + { + pulse_level = 1; + current_bit++; + if (current_bit>=8) + { + current_bit = 0; + block_pos++; + if (block_pos>=blocks[current_block].length) + { + block_pos = 0; + current_section = PULSE_CHECKSUM; + } + } + } + } + } + + if (current_section == PULSE_CHECKSUM) + { + const uint8_t datum = blocks[current_block].checksum; + const uint16_t pulse_len = (datum & (0x80>>current_bit)) == 0 ? PULSE_LEN_ZERO : PULSE_LEN_ONE; + if (pulse_pos >= pulse_len) + { + pulse_pos -= pulse_len; + pulse_level--; + if (pulse_level>=2) + { + pulse_level = 1; + current_bit++; + if (current_bit>=8) + { + current_bit = 0; + current_section = PULSE_SYNC3; + } + } + } + } + + if (current_section == PULSE_SYNC3) + { + if (pulse_pos >= PULSE_LEN_SYNC3) + { + pulse_pos -= PULSE_LEN_SYNC3; + pulse_level = 0; + current_section = PULSE_WAIT; + } + + } + + if (current_section == PULSE_WAIT) + { + pulse_level = 0; + if (pulse_pos >= 3500000) + { + current_section = PULSE_PILOT; + pulse_level = 1; + current_block++; + if (current_block>=blocks.size()) + { + stop(); + rewind(); + } + } + } + + zx_ula::set_ear(pulse_level); + } +} diff --git a/zx_tape.h b/zx_tape.h new file mode 100644 index 0000000..a929b9c --- /dev/null +++ b/zx_tape.h @@ -0,0 +1,11 @@ +#pragma once +#include + +namespace zx_tape +{ + void load(const char* filename); + void play(); + void stop(); + void rewind(); + void update(const uint8_t dt); +} diff --git a/zx_ula.cpp b/zx_ula.cpp index bd5241a..cebd42b 100644 --- a/zx_ula.cpp +++ b/zx_ula.cpp @@ -11,7 +11,7 @@ namespace zx_ula { const uint8_t *keys = SDL_GetKeyboardState(NULL); const uint8_t h_addr = (port>>8); - uint8_t result = 0xff; + uint8_t result = ear ? 0xbf : 0xff; if (!(h_addr & ~0xfe)) { result &= ~( @@ -97,6 +97,7 @@ namespace zx_ula uint8_t get_border_color() { return border_color; } uint8_t get_ear() { return ear; } + void set_ear(const uint8_t val) { ear = val; } @@ -104,6 +105,7 @@ namespace zx_ula SDL_AudioDeviceID sdlAudioDevice; uint8_t sound_buffer[1024]; uint16_t sound_pos; + uint16_t t_sound=0; void audioCallback(void * userdata, uint8_t * stream, int len) { @@ -128,8 +130,12 @@ namespace zx_ula SDL_PauseAudioDevice(sdlAudioDevice, 1); } - void sound_update() + void sound_update(const uint8_t dt) { - sound_buffer[sound_pos++] = ear*128; + t_sound += dt; + if (t_sound>=400) { + t_sound-=400; + sound_buffer[sound_pos++] = ear*128; + } } } \ No newline at end of file diff --git a/zx_ula.h b/zx_ula.h index 3a9e7ac..3b6991e 100644 --- a/zx_ula.h +++ b/zx_ula.h @@ -9,9 +9,10 @@ namespace zx_ula uint8_t get_border_color(); uint8_t get_ear(); + void set_ear(const uint8_t val); void sound_init(); void sound_enable(); void sound_disable(); - void sound_update(); + void sound_update(const uint8_t dt); }