// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*********************************************************************
appldriv.c
Apple 5.25" floppy drive emulation (to be interfaced with applefdc.c)
*********************************************************************/
#include "emu.h"
#include "appldriv.h"
#include "imagedev/flopdrv.h"
#include "formats/ap2_dsk.h"
// our parent's device is the Disk II card (Apple II) or main driver (Mac, IIgs)
// either way, get the drive from there.
#define PARENT_FLOPPY_0 "^floppy0"
#define PARENT_FLOPPY_1 "^floppy1"
#define PARENT_FLOPPY_2 "^floppy2"
#define PARENT_FLOPPY_3 "^floppy3"
static inline apple525_floppy_image_device *get_device(device_t *device)
{
assert(device != nullptr);
assert(device->type() == FLOPPY_APPLE);
return (apple525_floppy_image_device *) downcast<apple525_floppy_image_device *>(device);
}
static int apple525_enable_mask = 1;
legacy_floppy_image_device *apple525_get_subdevice(device_t *device, int drive)
{
switch(drive) {
case 0 : return device->subdevice<legacy_floppy_image_device>(PARENT_FLOPPY_0);
case 1 : return device->subdevice<legacy_floppy_image_device>(PARENT_FLOPPY_1);
case 2 : return device->subdevice<legacy_floppy_image_device>(PARENT_FLOPPY_2);
case 3 : return device->subdevice<legacy_floppy_image_device>(PARENT_FLOPPY_3);
}
return nullptr;
}
device_t *apple525_get_device_by_type(device_t *device, int ftype, int drive)
{
int i;
int cnt = 0;
for (i=0;i<4;i++) {
legacy_floppy_image_device *disk = apple525_get_subdevice(device, i);
if (disk->floppy_get_drive_type()==ftype) {
if (cnt==drive) {
return disk;
}
cnt++;
}
}
return nullptr;
}
void apple525_set_enable_lines(device_t *device,int enable_mask)
{
apple525_enable_mask = enable_mask;
}
/* ----------------------------------------------------------------------- */
static void apple525_load_current_track(device_t *image)
{
int len;
apple525_floppy_image_device *disk;
disk = get_device(image);
len = sizeof(disk->track_data);
disk->floppy_drive_read_track_data_info_buffer(0, disk->track_data, &len);
disk->track_loaded = 1;
disk->track_dirty = 0;
}
static void apple525_save_current_track(device_t *image, int unload)
{
int len;
apple525_floppy_image_device *disk;
disk = get_device(image);
if (disk->track_dirty)
{
len = sizeof(disk->track_data);
disk->floppy_drive_write_track_data_info_buffer(0, disk->track_data, &len);
disk->track_dirty = 0;
}
if (unload)
disk->track_loaded = 0;
}
static void apple525_seek_disk(apple525_floppy_image_device *img, signed int step)
{
int track;
int pseudo_track;
apple525_floppy_image_device *disk;
disk = get_device(img);
apple525_save_current_track(img, false);
track = img->floppy_drive_get_current_track();
pseudo_track = (track * 2) + disk->tween_tracks;
pseudo_track += step;
if (pseudo_track < 0)
pseudo_track = 0;
else if (pseudo_track/2 >= APPLE2_TRACK_COUNT)
pseudo_track = APPLE2_TRACK_COUNT*2-1;
if (pseudo_track/2 != track)
{
img->floppy_drive_seek(pseudo_track/2 - img->floppy_drive_get_current_track());
disk->track_loaded = 0;
}
if (pseudo_track & 1)
disk->tween_tracks = 1;
else
disk->tween_tracks = 0;
}
static void apple525_disk_set_lines(device_t *device,device_t *image, uint8_t new_state)
{
apple525_floppy_image_device *cur_disk;
uint8_t old_state;
unsigned int phase;
cur_disk = get_device(image);
old_state = cur_disk->state;
cur_disk->state = new_state;
if ((new_state & 0x0F) > (old_state & 0x0F))
{
phase = 0;
switch((old_state ^ new_state) & 0x0F)
{
case 1: phase = 0; break;
case 2: phase = 1; break;
case 4: phase = 2; break;
case 8: phase = 3; break;
}
phase -= cur_disk->floppy_drive_get_current_track() * 2;
if (cur_disk->tween_tracks)
phase--;
phase %= 4;
switch(phase)
{
case 1:
apple525_seek_disk(cur_disk, +1);
break;
case 3:
apple525_seek_disk(cur_disk, -1);
break;
}
}
}
int apple525_get_count(device_t *device)
{
int cnt = 0;
if ((device->subdevice("^" FLOPPY_0)!=nullptr) && (device->subdevice<legacy_floppy_image_device>("^" FLOPPY_0)->floppy_get_drive_type() == FLOPPY_TYPE_APPLE) && (get_device(device->subdevice(PARENT_FLOPPY_0))!=nullptr)) cnt++;
if ((device->subdevice("^" FLOPPY_1)!=nullptr) && (device->subdevice<legacy_floppy_image_device>("^" FLOPPY_1)->floppy_get_drive_type() == FLOPPY_TYPE_APPLE) && (get_device(device->subdevice(PARENT_FLOPPY_1))!=nullptr)) cnt++;
if ((device->subdevice("^" FLOPPY_2)!=nullptr) && (device->subdevice<legacy_floppy_image_device>("^" FLOPPY_2)->floppy_get_drive_type() == FLOPPY_TYPE_APPLE) && (get_device(device->subdevice(PARENT_FLOPPY_2))!=nullptr)) cnt++;
if ((device->subdevice("^" FLOPPY_3)!=nullptr) && (device->subdevice<legacy_floppy_image_device>("^" FLOPPY_3)->floppy_get_drive_type() == FLOPPY_TYPE_APPLE) && (get_device(device->subdevice(PARENT_FLOPPY_3))!=nullptr)) cnt++;
return cnt;
}
void apple525_set_lines(device_t *device, uint8_t lines)
{
int i, count;
device_t *image;
count = apple525_get_count(device);
for (i = 0; i < count; i++)
{
if (apple525_enable_mask & (1 << i))
{
image = apple525_get_device_by_type(device, FLOPPY_TYPE_APPLE, i);
if (image)
apple525_disk_set_lines(device,image, lines);
}
}
}
/* reads/writes a byte; write_value is -1 for read only */
static uint8_t apple525_process_byte(device_t *img, int write_value)
{
uint8_t read_value;
apple525_floppy_image_device *disk;
int spinfract_divisor;
int spinfract_dividend;
apple525_floppy_image_device *config = get_device(img);
device_image_interface *image = dynamic_cast<device_image_interface *>(img);
disk = get_device(img);
spinfract_dividend = config->get_dividend();
spinfract_divisor = config->get_divisor();
/* no image initialized for that drive ? */
if (!image->exists())
return 0xFF;
/* check the spin count if reading*/
if (write_value < 0)
{
disk->spin_count++;
disk->spin_count %= spinfract_divisor;
if (disk->spin_count >= spinfract_dividend)
return 0x00;
}
/* load track if need be */
if (disk->track_loaded == 0)
apple525_load_current_track(img);
/* perform the read */
read_value = disk->track_data[disk->position];
/* perform the write, if applicable */
if (write_value >= 0)
{
disk->track_data[disk->position] = write_value;
disk->track_dirty = 1;
}
disk->position++;
disk->position %= ARRAY_LENGTH(disk->track_data);
/* when writing; save the current track after every full sector write */
if ((write_value >= 0) && ((disk->position % APPLE2_NIBBLE_SIZE) == 0))
apple525_save_current_track(img, false);
return read_value;
}
static device_t *apple525_selected_image(device_t *device)
{
int i,count;
count = apple525_get_count(device);
for (i = 0; i < count; i++)
{
if (apple525_enable_mask & (1 << i))
return apple525_get_device_by_type(device, FLOPPY_TYPE_APPLE, i);
}
return nullptr;
}
uint8_t apple525_read_data(device_t *device)
{
device_t *image;
image = apple525_selected_image(device);
return image ? apple525_process_byte(image, -1) : 0xFF;
}
void apple525_write_data(device_t *device,uint8_t data)
{
device_t *image;
image = apple525_selected_image(device);
if (image)
apple525_process_byte(image, data);
}
int apple525_read_status(device_t *device)
{
int i, count, result = 0;
device_image_interface *image;
count = apple525_get_count(device);
for (i = 0; i < count; i++)
{
if (apple525_enable_mask & (1 << i))
{
image = dynamic_cast<device_image_interface *>(apple525_get_device_by_type(device, FLOPPY_TYPE_APPLE, i));
if (image && image->is_readonly())
result = 1;
}
}
return result;
}
// device type definition
const device_type FLOPPY_APPLE = device_creator<apple525_floppy_image_device>;
//-------------------------------------------------
// apple525_floppy_image_device - constructor
//-------------------------------------------------
apple525_floppy_image_device::apple525_floppy_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: legacy_floppy_image_device(mconfig, FLOPPY_APPLE, "Apple Disk II", tag, owner, clock, "floppy_apple", __FILE__)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void apple525_floppy_image_device::device_start()
{
legacy_floppy_image_device::device_start();
floppy_set_type(FLOPPY_TYPE_APPLE);
state = 0;
tween_tracks = 0;
track_loaded = 0;
track_dirty = 0;
position = 0;
spin_count = 0;
memset(track_data, 0, sizeof(track_data));
}
image_init_result apple525_floppy_image_device::call_load()
{
image_init_result result = legacy_floppy_image_device::call_load();
floppy_drive_seek(-999);
floppy_drive_seek(+35/2);
return result;
}
void apple525_floppy_image_device::call_unload()
{
apple525_save_current_track(this, true);
legacy_floppy_image_device::call_unload();
}