- [NEW] Step by step execution with F6 - [NEW] Memory is tagged as code or data while executing, so later it can be properly disassembled - [NEW] "reg X value" to set the value of X register - [FIX] IX opcode table had errors - [FIX] opcodes with two parameters where printed incorrectly on the disassembler - [FIX] opcodes can't be lager than 4 bytes - [CHG] Berserk mode and fernando martin TAP by default, to help with debugging
246 lines
7.0 KiB
C++
246 lines
7.0 KiB
C++
#include "zx_tape.h"
|
|
#include "zx_ula.h"
|
|
#include "zx_screen.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 berserk_mode = 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 uint8_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.\n", current_block);
|
|
}
|
|
|
|
}
|
|
|
|
if (current_section == PULSE_WAIT)
|
|
{
|
|
pulse_level = 0;
|
|
if (pulse_pos >= 3500000)
|
|
{
|
|
pulse_pos = 0;
|
|
current_section = PULSE_PILOT;
|
|
pulse_level = 1;
|
|
current_block++;
|
|
|
|
if (current_block>=blocks.size())
|
|
{
|
|
printf("end\n");
|
|
stop();
|
|
rewind();
|
|
}
|
|
else
|
|
{
|
|
//zxscreen::fullrefresh();
|
|
printf("going to pulse_pilot on block %i...\n", current_block);
|
|
}
|
|
}
|
|
}
|
|
|
|
zx_ula::set_ear(pulse_level);
|
|
}
|
|
|
|
void setberserk(const bool value) { berserk_mode = value; }
|
|
const bool getberserk() { return berserk_mode; }
|
|
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);
|
|
}
|
|
}
|