File: TsxParser.hh

package info (click to toggle)
openmsx 20.0%2Bdfsg-1.2
  • links: PTS
  • area: main
  • in suites: forky, trixie
  • size: 27,544 kB
  • sloc: cpp: 236,922; xml: 49,948; tcl: 15,056; python: 5,385; perl: 281; sh: 77; makefile: 53
file content (137 lines) | stat: -rw-r--r-- 5,150 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Based on code written by:
//   (2017) NataliaPC aka @ishwin74
//   Under GPL License

#ifndef TSXPARSER_HH
#define TSXPARSER_HH

#include "endian.hh"

#include <array>
#include <cstdint>
#include <optional>
#include <span>
#include <string>
#include <vector>

class TsxParser {
public:
	// output settings
	static constexpr unsigned TZX_Z80_FREQ       = 3500000; // 3.5MHz (according to TZX spec: not 3.579545MHz)
	static constexpr unsigned OUTPUT_FREQUENCY   = 58900;   // ~ = Z80_FREQ * 4 / T_STATES_MSX_PULSE
	static constexpr float    T_STATES_MSX_PULSE = 238.f;

	enum class FileType {
		ASCII, BINARY, BASIC, UNKNOWN,
	};

public:
	explicit TsxParser(std::span<const uint8_t> file);

	[[nodiscard]] std::vector<int8_t>&& stealOutput() { return std::move(output); }
	[[nodiscard]] std::optional<FileType> getFirstFileType() const { return firstFileType; }
	[[nodiscard]] const std::vector<std::string>& getMessages() const { return messages; }

private:
	struct Block10 {
		Endian::UA_L16  pauseMs;     // Pause after this block in milliseconds
		Endian::UA_L16  len;         // Length of data that follow
		//uint8_t       data[];      // Data as in .TAP files
	};
	struct Block11 {
		Endian::UA_L16  pilot;       // Length of PILOT pulse {2168}
		Endian::UA_L16  sync1;       // Length of SYNC first pulse {667}
		Endian::UA_L16  sync2;       // Length of SYNC second pulse {735}
		Endian::UA_L16  zero;        // Length of ZERO bit pulse {855}
		Endian::UA_L16  one;         // Length of ONE bit pulse {1710}
		Endian::UA_L16  pilotLen;    // Length of PILOT tone (number of pulses) {8063 header (flag<128), 3223 data (flag>=128)}
		uint8_t         lastBits;    // Used bits in the last byte (other bits should be 0) {8}
		Endian::UA_L16  pauseMs;     // Pause after this block (ms.) {1000}
		Endian::UA_L24  len;         // Length of data that follow
		//uint8_t       data[];      // Data as in .TAP files
	};
	struct Block12 {
		Endian::UA_L16  len;         // Length of one pulse in T-states
		Endian::UA_L16  pulses;      // Number of pulses
	};
	struct Block13 {
		uint8_t         num;         // Number of pulses
		//Endian::UA_L16 pulses[];   // [Array] Pulses' lengths
	};
	struct Block15 {
		Endian::UA_L16  bitTstates;  // Number of T-states per sample (bit of data)
		Endian::UA_L16  pauseMs;     // Pause after this block (ms.) {1000}
		uint8_t         lastBits;    // Used bits (samples) in last byte of data (1-8)
		Endian::UA_L24  len;         // Length of samples data
		//uint8_t       samples[];   // [Array] Samples data. Each bit represents a state on the EAR port (i.e. one sample). MSb is played first.
	};
	struct Block20 {
		Endian::UA_L16  pauseMs;     // Silence pause in milliseconds
	};
	struct Block21 {
		uint8_t         len;         // Length of the group name string
		//char          name[];      // The group name (ASCII)
	};
	struct Block30 {
		uint8_t         len;         // Length of the text description
		//char          text[];      // [Array] Text description in ASCII format
	};
	struct Block32 {
		Endian::UA_L16  blockLen;    // Length of the whole block (without these two bytes)
		uint8_t         num;         // Number of text strings
		//uint8_t       list[];      // [Array] List of text strings
	};
	struct Block35 {
		std::array<char, 0x10> label;// Identification string (in ASCII)
		Endian::UA_L32  len;         // Length of the custom info
		//uint8_t       data[];      // [Array] Custom info
	};
	struct Block4B {
		Endian::UA_L32  blockLen;    // Block length without these four bytes
		Endian::UA_L16  pauseMs;     // Pause after this block in milliseconds
		Endian::UA_L16  pilot;       // Duration of a PILOT pulse in T-states {same as ONE pulse}
		Endian::UA_L16  pulses;      // Number of pulses in the PILOT tone
		Endian::UA_L16  bit0len;     // Duration of a ZERO pulse in T-states {=2*pilot}
		Endian::UA_L16  bit1len;     // Duration of a ONE pulse in T-states {=pilot}
		uint8_t         bitCfg;
		uint8_t         byteCfg;
		//uint8_t       data[];      // [Array]
	};

	void processBlock10(const Block10& b);
	void processBlock11(const Block11& b);
	void processBlock12(const Block12& b);
	void processBlock13(const Block13& b);
	void processBlock15(const Block15& b);
	void processBlock20(const Block20& b);
	void processBlock21(const Block21& b);
	void processBlock30(const Block30& b);
	void processBlock32(const Block32& b);
	void processBlock35(const Block35& b);
	void processBlock4B(const Block4B& b);

	void writeSample(uint32_t tStates, int8_t value);
	void writePulse(uint32_t tStates);
	void writePulses(uint32_t count, uint32_t tStates);
	void writeSilence(int s);

	template<typename T> std::span<const T> get(size_t count);
	template<typename T> const T& get();

	[[noreturn]] void error(std::string msg) const;

private:
	// The parsed result is stored here
	std::vector<int8_t> output;
	std::vector<std::string> messages;
	std::optional<FileType> firstFileType;

	// The (remaining) part of the input file
	std::span<const uint8_t> buf;

	// Intermediate state while writing the waveform
	float  accumBytes = 0.f;
	int8_t currentValue = 127;
};

#endif