Files
z80/zx_tape.cpp
Raimon Zamora 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

284 lines
8.0 KiB
C++

#include "zx_tape.h"
#include "zx_ula.h"
#include "zx_screen.h"
#include "zx_mem.h"
#include "z80debug.h"
#include "z80.h"
#include <vector>
#include <stdio.h>
#include <stdlib.h>
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 *data;
};
bool playing = false;
bool loaded = false;
bool options[ZXTAPE_NUM_OPTIONS] = { true, true };
std::vector<block_t> 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)
{
if (!blocks.empty()) for (auto block : blocks) free(block.data);
blocks.clear();
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 = !playing; //true;
}
void stop()
{
playing = false;
//berserk_mode = 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 uint32_t dt)
{
if (!playing) return;
pulse_pos += dt;
if (current_section == PULSE_PILOT)
{
const uint16_t pulse_count = blocks[current_block].data[0]<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 && pulse_level==0)
{
pulse_level = 0;
}
else if (current_pulse >= pulse_count)
{
current_pulse = 0;
pulse_level = 1;
current_section = PULSE_SYNC1;
printf("going to pulse_sync1..%i.\n", current_block);
}
}
}
if (current_section == PULSE_SYNC1)
{
if (pulse_pos >= PULSE_LEN_SYNC1)
{
pulse_pos -= PULSE_LEN_SYNC1;
pulse_level = 0;
current_section = PULSE_SYNC2;
printf("going to pulse_sync2..%i.\n", current_block);
}
}
if (current_section == PULSE_SYNC2)
{
if (pulse_pos >= PULSE_LEN_SYNC2)
{
pulse_pos -= PULSE_LEN_SYNC2;
pulse_level = 1;
current_section = PULSE_DATA;
printf("going to pulse_data..%i.\n", current_block);
}
}
static int level[2] = {0, 0};
if (current_section == PULSE_DATA)
{
level[pulse_level]+=dt;
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 =0;//-= pulse_len;
pulse_level--;
if (pulse_level>=2)
{
pulse_level = 1;
//printf("%i\n",current_bit);
level[0]=level[1]=0;
current_bit++;
if (current_bit>=8)
{
current_bit = 0;
block_pos++;
if (block_pos>=blocks[current_block].length)
{
block_pos = 0;
current_section = PULSE_SYNC3;
printf("going to pulse_sync3..%i.\n", current_block);
}
}
}
}
}
/*if (current_section == PULSE_CHECKSUM)
{
const uint8_t datum = 1;//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;
printf("going to pulse_wait..%i (%i).\n", current_block, blocks[current_block].length);
}
}
if (current_section == PULSE_WAIT)
{
pulse_level = 0;
if (pulse_pos >= z80::getClock())
{
pulse_pos = 0;
current_section = PULSE_PILOT;
pulse_level = 1;
current_block++;
if (current_block>=blocks.size())
{
printf("end\n");
stop();
rewind();
if (options[ZXTAPE_OPTION_STOP_AT_END]) z80debug::stop();
}
else
{
//zxscreen::fullrefresh();
printf("going to pulse_pilot on block %i...\n", current_block);
}
}
}
zx_ula::set_ear(pulse_level);
}
const bool getplaying() { return playing; }
void report()
{
if (current_block >= blocks.size()) return;
const int percent = (float(block_pos)/float(blocks[current_block].length))*100;
printf("tape loading: %i%\n", percent);
}
uint16_t fastLoad(const uint8_t block_type, const uint16_t address, const uint16_t length)
{
block_pos=0;
current_bit=0;
current_section = PULSE_PILOT;
current_pulse = 0;
pulse_pos = 0;
pulse_level = 0;
if (blocks[current_block].data[0] != block_type ||
blocks[current_block].length != length+2) {
printf("ERROR: Tape data not consistent with expectation\n");
z80debug::stop();
}
for (int i=0;i<length;++i) {
mem::writeMem(address+i, blocks[current_block].data[i+1]);
}
current_block++;
return address + length;
}
const bool getOption(const int option)
{
return options[option];
}
void setOption(const int option, const bool value)
{
options[option] = value;
}
void toggleOption(const int option)
{
options[option] = !options[option];
}
}