summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats/atom_tap.cpp
blob: 881433ef1a8a6417c3cd399c6e162001e6fcdb56 (plain) (blame)
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
138
139
140
141
142
143
144
145
146
147
148
149
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/*********************************************************************

    formats/atom_tap.c

    Cassette code for Acorn Atom tap files (Kansas City Standard)

*********************************************************************/

/*

    http://beebwiki.jonripley.com/Acorn_cassette_format#Atom

    The Atom supports the System series format (as a 'nameless file') and its own format. The data consists of files separated
    by carrier breaks and the appropriate leaders and trailers, and further subdivided into blocks separated by inter-block gaps.
    An Atom block consists of the following sequence of bytes:

        Four synchronisation bytes (&2A).
        File name (one to thirteen characters).
        One end of file name marker byte (&0D).
        Block flag, one byte.
        Block number, two bytes, high byte first.
        Data block length ? 1, one byte.
        Execution address, two bytes, high byte first.
        Load address, two bytes, high byte first.
        Data, 0 to 256 bytes.
        Checksum, one byte.

    The block flag is set as follows:

        Bit 7 is set if this block is not the last block of a file.
        Bit 6 is set if the block contains data. If clear then the 'data block length' field is invalid.
        Bit 5 is set if this block is not the first block of a file.
        Bits 4 to 0 are undefined. Normally their contents are:
        in the first block, bits 15 to 11 of the end address (=1 + the last address saved in the file);
        in subsequent blocks, bits 6 to 2 of the previous block flag.

    The checksum is a simple sum (modulo 256) of all bytes from the start of the filename to the end of the data.

*/

#include <assert.h>

#include "atom_tap.h"
#include "csw_cas.h"
#include "uef_cas.h"
/***************************************************************************
    PARAMETERS
***************************************************************************/

#define LOG 1

/***************************************************************************
    IMPLEMENTATION
***************************************************************************/

/*-------------------------------------------------
    CassetteModulation atom_tap_modulation
-------------------------------------------------*/

static const struct CassetteModulation atom_tap_modulation =
{
	CASSETTE_MODULATION_SINEWAVE,
	1200.0 - 300, 1200.0, 1200.0 + 300,
	2400.0 - 600, 2400.0, 2400.0 + 600
};

/*-------------------------------------------------
    cassette_image_read_uint8 - read tape data
-------------------------------------------------*/

static uint8_t cassette_image_read_uint8(cassette_image *cassette, uint64_t offset)
{
	uint8_t data;
	cassette_image_read(cassette, &data, offset, 1);
	return data;
}

/*-------------------------------------------------
    atom_tap_identify - identify cassette
-------------------------------------------------*/

static cassette_image::error atom_tap_identify(cassette_image *cassette, struct CassetteOptions *opts)
{
	return cassette_modulation_identify( cassette, &atom_tap_modulation, opts);
}

/*-------------------------------------------------
    atom_tap_load - load cassette
-------------------------------------------------*/

#define MODULATE(_value) \
	for (int i = 0; i < (_value ? 8 : 4); i++) { \
		err = cassette_put_modulated_data_bit(cassette, 0, time_index, _value, &atom_tap_modulation, &time_displacement);\
		if (err != cassette_image::error::SUCCESS) return err;\
		time_index += time_displacement;\
	}

#define BIT(x,n) (((x)>>(n))&1)

static cassette_image::error atom_tap_load(cassette_image *cassette)
{
	cassette_image::error err;
	uint64_t image_size = cassette_image_size(cassette);
	uint64_t image_pos = 0;
	double time_index = 0.0;
	double time_displacement;

	while (image_pos < image_size)
	{
		uint8_t data = cassette_image_read_uint8(cassette, image_pos);

		/* start bit */
		MODULATE(0);

		/* data bits */
		for (int bit = 0; bit < 8; bit++)
		{
			MODULATE(BIT(data, bit));
		}

		/* stop bits */
		MODULATE(1);
		MODULATE(1);

		image_pos++;
	}

	return cassette_image::error::SUCCESS;
}

/*-------------------------------------------------
    CassetteFormat atom_tap_cassette_format
-------------------------------------------------*/

const struct CassetteFormat atom_tap_format =
{
	"tap",
	atom_tap_identify,
	atom_tap_load,
	nullptr
};

CASSETTE_FORMATLIST_START( atom_cassette_formats )
	CASSETTE_FORMAT(atom_tap_format)
	CASSETTE_FORMAT(csw_cassette_format)
	CASSETTE_FORMAT(uef_cassette_format)
CASSETTE_FORMATLIST_END