summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/astrocde/cassette.cpp
blob: 3e4cc80deb86ecc56502bbf72c67774120e2d6a7 (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
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
//
// Decoding is done in hardware by an external box, and decoded bits are fed to bit 0 of the controller port,
// with sync bits being fed to bit 1. The current HLE is not remotely accurate to hardware, but works.

#include "emu.h"
#include "cassette.h"

//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

DEFINE_DEVICE_TYPE(ASTROCADE_CASSETTE, astrocade_cassette_device, "astrocade_cass", "Bally Astrocade Cassette")


//**************************************************************************
//    Bally Astrocade cassette input
//**************************************************************************

astrocade_cassette_device::astrocade_cassette_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, ASTROCADE_CASSETTE, tag, owner, clock)
	, device_astrocade_ctrl_interface(mconfig, *this)
	, m_cassette(*this, "cassette")
{
}

astrocade_cassette_device::~astrocade_cassette_device()
{
}

void astrocade_cassette_device::device_start()
{
	save_item(NAME(m_cass_wave));
	save_item(NAME(m_cass_delta));
	save_item(NAME(m_cass_wave_ticks));
	save_item(NAME(m_cass_cycles));
	save_item(NAME(m_cass_mark));
}

void astrocade_cassette_device::device_reset()
{
	m_cass_wave = 0.0;
	m_cass_delta = 0.0;
	m_cass_wave_ticks = 0;
	m_cass_cycles = 0;
	m_cass_mark = false;
}

uint8_t astrocade_cassette_device::read_handle()
{
	if (m_cass_data.size())
	{
		const uint8_t data = m_cass_data.front();
		m_cass_data.pop();
		return data;
	}
	return 0;
}

uint8_t astrocade_cassette_device::read_knob()
{
	return 0;
}

TIMER_DEVICE_CALLBACK_MEMBER(astrocade_cassette_device::check_cassette_wave)
{
	if (m_cassette->get_state() & CASSETTE_MOTOR_DISABLED)
		return;

	double old_cass_wave = m_cass_wave;
	m_cass_wave = m_cassette->input();

	bool cycled = false;
	if (old_cass_wave != m_cass_wave)
	{
		double old_delta = m_cass_delta;
		m_cass_delta = m_cass_wave - old_cass_wave;
		if (old_delta < 0.0 && m_cass_delta > 0.0)
		{
			cycled = true;
		}
	}

	if (cycled)
	{
		m_cass_mark = m_cass_wave_ticks <= 30;

		m_cass_wave_ticks = 0;

		m_cass_cycles++;
		if (m_cass_mark)
		{
			if (m_cass_cycles >= 8)
			{
				m_cass_data.push(1);
				m_cass_cycles = 0;
			}
		}
		else
		{
			if (m_cass_cycles >= 4)
			{
				m_cass_data.push(0);
				m_cass_cycles = 0;
			}
		}
	}

	m_cass_wave_ticks++;
}

TIMER_DEVICE_CALLBACK_MEMBER(astrocade_cassette_device::pulse_cassette_clock)
{
	if (m_cass_data.size())
	{
		write_ltpen(1);
		write_ltpen(0);
	}
}

void astrocade_cassette_device::device_add_mconfig(machine_config &config)
{
	CASSETTE(config, m_cassette);
	m_cassette->set_default_state(CASSETTE_STOPPED);
	m_cassette->set_interface("astrocade_cass");

	TIMER(config, "kcs_hle").configure_periodic(FUNC(astrocade_cassette_device::check_cassette_wave), attotime::from_hz(48000));
	TIMER(config, "kcs_clk").configure_periodic(FUNC(astrocade_cassette_device::pulse_cassette_clock), attotime::from_hz(300));
}