summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/coco_vhd.cpp
blob: 407d8b506d53e037465e37f851baf67d65f9bf4e (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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/***************************************************************************

    coco_vhd.c

    Color Computer Virtual Hard Drives

****************************************************************************

    Technical specs on the Virtual Hard Disk interface

    Address       Description
    -------       -----------
    FF80          Logical record number (high byte)
    FF81          Logical record number (middle byte)
    FF82          Logical record number (low byte)
    FF83          Command/status register
    FF84          Buffer address (high byte)
    FF85          Buffer address (low byte)

    Set the other registers, and then issue a command to FF83 as follows:

     0 = read 256-byte sector at LRN
     1 = write 256-byte sector at LRN
     2 = flush write cache (Closes and then opens the image file)

    Error values:

     0 = no error
    -1 = power-on state (before the first command is received)
    -2 = invalid command
     2 = VHD image does not exist
     4 = Unable to open VHD image file
     5 = access denied (may not be able to write to VHD image)

    IMPORTANT: The I/O buffer must NOT cross an 8K MMU bank boundary.

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

#include "emu.h"
#include "coco_vhd.h"
#include "includes/coco.h"
#include "machine/ram.h"


/***************************************************************************
    CONSTANTS
***************************************************************************/

#define VERBOSE 0

#define VHDSTATUS_OK                    0x00
#define VHDSTATUS_NO_VHD_ATTACHED       0x02
#define VHDSTATUS_ACCESS_DENIED         0x05
#define VHDSTATUS_UNKNOWN_COMMAND       0xFE
#define VHDSTATUS_POWER_ON_STATE        0xFF

#define VHDCMD_READ     0
#define VHDCMD_WRITE    1
#define VHDCMD_FLUSH    2


/***************************************************************************
    CORE IMPLEMENTATION
***************************************************************************/

DEFINE_DEVICE_TYPE(COCO_VHD, coco_vhd_image_device, "coco_vhd_image", "CoCo Virtual Hard Disk")

//-------------------------------------------------
//  coco_vhd_image_device - constructor
//-------------------------------------------------

coco_vhd_image_device::coco_vhd_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, COCO_VHD, tag, owner, clock)
	, device_image_interface(mconfig, *this)
	, m_cpu(*this, finder_base::DUMMY_TAG)
{
}

//-------------------------------------------------
//  coco_vhd_image_device - destructor
//-------------------------------------------------

coco_vhd_image_device::~coco_vhd_image_device()
{
}



//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void coco_vhd_image_device::device_start()
{
	m_status = VHDSTATUS_NO_VHD_ATTACHED;
	m_cpu_space = &m_cpu->space(AS_PROGRAM);
}



//-------------------------------------------------
//  call_load
//-------------------------------------------------

image_init_result coco_vhd_image_device::call_load()
{
	m_status = VHDSTATUS_POWER_ON_STATE;
	m_logical_record_number = 0;
	m_buffer_address = 0;
	return image_init_result::PASS;
}



//-------------------------------------------------
//  coco_vhd_readwrite
//-------------------------------------------------

void coco_vhd_image_device::coco_vhd_readwrite(uint8_t data)
{
	int result, i;
	uint32_t bytes_to_read;
	uint32_t bytes_to_write;
	uint64_t seek_position;
	uint64_t total_size;
	char buffer[1024];

	/* access the image */
	if (!exists())
	{
		m_status = VHDSTATUS_NO_VHD_ATTACHED;
		return;
	}

	/* perform the seek */
	seek_position = ((uint64_t) 256) * m_logical_record_number;
	total_size = length();
	result = fseek(std::min(seek_position, total_size), SEEK_SET);
	if (result < 0)
	{
		m_status = VHDSTATUS_ACCESS_DENIED;
		return;
	}

	/* expand the disk, if necessary */
	if (data == VHDCMD_WRITE)
	{
		while(total_size < seek_position)
		{
			memset(buffer, 0, sizeof(buffer));

			bytes_to_write = (uint32_t)std::min(seek_position - total_size, (uint64_t) sizeof(buffer));
			result = fwrite(buffer, bytes_to_write);
			if (result != bytes_to_write)
			{
				m_status = VHDSTATUS_ACCESS_DENIED;
				return;
			}

			total_size += bytes_to_write;
		}
	}

	switch(data)
	{
		case VHDCMD_READ: /* Read sector */
			memset(buffer, 0, 256);
			if (total_size > seek_position)
			{
				bytes_to_read = (uint32_t)std::min((uint64_t) 256, total_size - seek_position);
				result = fread(buffer, bytes_to_read);
				if (result != bytes_to_read)
				{
					m_status = VHDSTATUS_ACCESS_DENIED;
					return;
				}
			}

			/* write the bytes to memory */
			for (i = 0; i < 256; i++)
				m_cpu_space->write_byte(m_buffer_address + i, buffer[i]);

			m_status = VHDSTATUS_OK;
			break;

		case VHDCMD_WRITE: /* Write Sector */
			/* read the bytes from memory */
			for (i = 0; i < 256; i++)
				buffer[i] = m_cpu_space->read_byte(m_buffer_address + i);

			/* and write to disk */
			result = fwrite(buffer, 256);
			if (result != 256)
			{
				m_status = VHDSTATUS_ACCESS_DENIED;
				return;
			}

			m_status = VHDSTATUS_OK;
			break;

		case VHDCMD_FLUSH: /* Flush file cache */
			m_status = VHDSTATUS_OK;
			break;

		default:
			m_status = VHDSTATUS_UNKNOWN_COMMAND;
			break;
	}
}



uint8_t coco_vhd_image_device::read(offs_t offset)
{
	uint8_t result = 0;

	switch(offset)
	{
		case 0xff83 - 0xff80:
			if (VERBOSE)
				logerror("vhd: Status read: %d\n", m_status);
			result = m_status;
			break;
	}
	return result;
}



void coco_vhd_image_device::write(offs_t offset, uint8_t data)
{
	int pos;

	switch(offset)
	{
		case 0xff80 - 0xff80:
		case 0xff81 - 0xff80:
		case 0xff82 - 0xff80:
			pos = ((0xff82 - 0xff80) - offset) * 8;
			m_logical_record_number &= ~(0xFF << pos);
			m_logical_record_number += data << pos;
			if (VERBOSE)
				logerror("vhd: LRN write: %6.6X\n", m_logical_record_number);
			break;

		case 0xff83 - 0xff80:
			coco_vhd_readwrite(data);
			if (VERBOSE)
				logerror("vhd: Command: %d\n", data);
			break;

		case 0xff84 - 0xff80:
			m_buffer_address &= 0xFFFF00FF;
			m_buffer_address += data << 8;
			if (VERBOSE)
				logerror("vhd: BA write: %X (%2.2X..)\n", m_buffer_address, data);
			break;

		case 0xff85 - 0xff80:
			m_buffer_address &= 0xFFFFFF00;
			m_buffer_address += data;
			if (VERBOSE)
				logerror("vhd: BA write: %X (..%2.2X)\n", m_buffer_address, data);
			break;
	}
}