mirror of https://github.com/buserror/mii_emu.git
797 lines
25 KiB
C
797 lines
25 KiB
C
/*
|
|
* mii_disk2.c
|
|
*
|
|
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#define _GNU_SOURCE // for asprintf
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include "mii.h"
|
|
#include "mii_bank.h"
|
|
|
|
#include "mii_rom_disk2.h"
|
|
#include "mii_woz.h"
|
|
#include "mii_floppy.h"
|
|
|
|
enum {
|
|
// bits used to address the LSS ROM using lss_mode
|
|
Q7_WRITE_BIT = 3,
|
|
Q6_LOAD_BIT = 2,
|
|
QA_BIT = 1,
|
|
RP_BIT = 0,
|
|
};
|
|
|
|
enum {
|
|
SIG_DR, // data register
|
|
SIG_WR, // write register
|
|
SIG_DRIVE, // selected drive
|
|
SIG_MOTOR, // motor on/off
|
|
SIG_READ_DR,
|
|
SIG_LSS_CLK,
|
|
SIG_LSS_SEQ,
|
|
SIG_LSS_CMD,
|
|
SIG_LSS_WRITE,
|
|
SIG_LSS_LOAD,
|
|
SIG_LSS_QA,
|
|
SIG_LSS_RP, // read pulse
|
|
SIG_LSS_WB, // write bit
|
|
SIG_LSS_RANDOM,
|
|
SIG_WRITE_CYCLE,
|
|
SIG_COUNT
|
|
};
|
|
const char *sig_names[] = {
|
|
"D2_DR", "D2_WR", "DRIVE", "MOTOR", "READ_DR",
|
|
"LSS_CLK", "LSS_SEQ", "LSS_CMD", "LSS_WRITE", "LSS_LOAD",
|
|
"LSS_QA", "LSS_RP", "LSS_WB", "LSS_RANDOM", "WRITE_CYCLE",
|
|
};
|
|
|
|
typedef struct mii_card_disk2_t {
|
|
mii_t * mii;
|
|
mii_dd_t drive[2];
|
|
mii_floppy_t floppy[2];
|
|
uint8_t selected;
|
|
|
|
uint8_t timer_off;
|
|
uint8_t timer_lss;
|
|
|
|
uint8_t write_register;
|
|
uint8_t head : 4; // bits are shifted in there
|
|
uint16_t clock; // LSS clock cycles, read a bit when 0
|
|
uint8_t lss_state : 4, // Sequence state
|
|
lss_mode : 4; // WRITE/LOAD/SHIFT/QA/RP etc
|
|
uint8_t lss_prev_state; // for write bit
|
|
uint8_t lss_skip;
|
|
uint8_t data_register;
|
|
|
|
uint64_t debug_last_write, debug_last_duration;
|
|
mii_vcd_t *vcd;
|
|
mii_signal_t *sig;
|
|
} mii_card_disk2_t;
|
|
|
|
static void
|
|
_mii_disk2_lss_tick(
|
|
mii_card_disk2_t *c );
|
|
static void
|
|
_mii_disk2_vcd_debug(
|
|
mii_card_disk2_t *c,
|
|
int on);
|
|
|
|
// debug, used for mish, only supports one card tho (yet)
|
|
static mii_card_disk2_t *_mish_d2 = NULL;
|
|
|
|
/*
|
|
* This timer is used to turn off the motor after a second
|
|
*/
|
|
static uint64_t
|
|
_mii_floppy_motor_off_cb(
|
|
mii_t * mii,
|
|
void * param )
|
|
{
|
|
mii_card_disk2_t *c = param;
|
|
mii_floppy_t *f = &c->floppy[c->selected];
|
|
// printf("%s drive %d off\n", __func__, c->selected);
|
|
if (c->drive[c->selected].file && f->seed_dirty != f->seed_saved)
|
|
mii_floppy_update_tracks(f, c->drive[c->selected].file);
|
|
f->motor = 0;
|
|
mii_raise_signal(c->sig + SIG_MOTOR, 0);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This (tries) to be called every cycle, it never happends in practice,
|
|
* as all the instructions can use multiple cycles by CPU runs.
|
|
* But I can retreive the overshoot cycles and call the LSS as many times
|
|
* as needed to 'catch up'
|
|
*/
|
|
static uint64_t
|
|
_mii_floppy_lss_cb(
|
|
mii_t * mii,
|
|
void * param )
|
|
{
|
|
mii_card_disk2_t *c = param;
|
|
mii_floppy_t *f = &c->floppy[c->selected];
|
|
if (!f->motor)
|
|
return 0; // stop timer, motor is now off
|
|
// delta is ALWAYS negative, or zero here
|
|
int32_t delta = mii_timer_get(mii, c->timer_lss);
|
|
uint64_t ret = -delta + 1;
|
|
do {
|
|
_mii_disk2_lss_tick(c);
|
|
_mii_disk2_lss_tick(c);
|
|
} while (delta++ < 0);
|
|
return ret;
|
|
}
|
|
|
|
static uint8_t
|
|
_mii_disk2_switch_track(
|
|
mii_t *mii,
|
|
mii_card_disk2_t *c,
|
|
int delta)
|
|
{
|
|
mii_floppy_t *f = &c->floppy[c->selected];
|
|
int qtrack = f->qtrack + delta;
|
|
if (qtrack < 0) qtrack = 0;
|
|
if (qtrack >= MII_FLOPPY_TRACK_COUNT * 4)
|
|
qtrack = (MII_FLOPPY_TRACK_COUNT * 4) -1;
|
|
|
|
if (qtrack == f->qtrack)
|
|
return f->qtrack;
|
|
|
|
uint8_t track_id = f->track_id[f->qtrack];
|
|
// if (track_id != 0xff)
|
|
// printf("NEW TRACK D%d: %d\n", c->selected, track_id);
|
|
uint8_t track_id_new = f->track_id[qtrack];
|
|
if (track_id != track_id_new && track_id != MII_FLOPPY_NOISE_TRACK) {
|
|
if (track_id == 0 && c->vcd)
|
|
_mii_disk2_vcd_debug(c, 0);
|
|
if (f->seed_dirty != f->seed_saved) {
|
|
// mii_floppy_resync_track(f, track_id, 0);
|
|
}
|
|
}
|
|
if (track_id_new >= MII_FLOPPY_TRACK_COUNT)
|
|
track_id_new = MII_FLOPPY_NOISE_TRACK;
|
|
/* adapt the bit position from one track to the others, from WOZ specs */
|
|
if (track_id_new != MII_FLOPPY_NOISE_TRACK) {
|
|
uint32_t track_size = f->tracks[track_id].bit_count;
|
|
uint32_t new_size = f->tracks[track_id_new].bit_count;
|
|
uint32_t new_pos = f->bit_position * new_size / track_size;
|
|
f->bit_position = new_pos;
|
|
}
|
|
f->qtrack = qtrack;
|
|
return f->qtrack;
|
|
}
|
|
|
|
|
|
static int
|
|
_mii_disk2_init(
|
|
mii_t * mii,
|
|
struct mii_slot_t *slot )
|
|
{
|
|
mii_card_disk2_t *c = calloc(1, sizeof(*c));
|
|
slot->drv_priv = c;
|
|
c->mii = mii;
|
|
printf("%s loading in slot %d\n", __func__, slot->id + 1);
|
|
uint16_t addr = 0xc100 + (slot->id * 0x100);
|
|
mii_bank_write(
|
|
&mii->bank[MII_BANK_CARD_ROM],
|
|
addr, mii_rom_disk2, 256);
|
|
|
|
c->sig = mii_alloc_signal(&mii->sig_pool, 0, SIG_COUNT, sig_names);
|
|
for (int i = 0; i < SIG_COUNT; i++)
|
|
c->sig[i].flags |= SIG_FLAG_FILTERED;
|
|
for (int i = 0; i < 2; i++) {
|
|
mii_dd_t *dd = &c->drive[i];
|
|
dd->slot_id = slot->id + 1;
|
|
dd->drive = i + 1;
|
|
dd->slot = slot;
|
|
asprintf((char **)&dd->name, "Disk ][ S:%d D:%d",
|
|
dd->slot_id, dd->drive);
|
|
mii_floppy_init(&c->floppy[i]);
|
|
c->floppy[i].id = i;
|
|
dd->floppy = &c->floppy[i];
|
|
}
|
|
mii_dd_register_drives(&mii->dd, c->drive, 2);
|
|
char *n;
|
|
asprintf(&n, "Disk ][ S:%d motor off", slot->id + 1);
|
|
c->timer_off = mii_timer_register(mii, _mii_floppy_motor_off_cb, c, 0, n);
|
|
asprintf(&n, "Disk ][ S:%d LSS", slot->id + 1);
|
|
c->timer_lss = mii_timer_register(mii, _mii_floppy_lss_cb, c, 0, n);
|
|
_mish_d2 = c;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
_mii_disk2_reset(
|
|
mii_t * mii,
|
|
struct mii_slot_t *slot )
|
|
{
|
|
mii_card_disk2_t *c = slot->drv_priv;
|
|
printf("%s\n", __func__);
|
|
c->selected = 1;
|
|
_mii_floppy_motor_off_cb(mii, c);
|
|
c->selected = 0;
|
|
_mii_floppy_motor_off_cb(mii, c);
|
|
mii_raise_signal(c->sig + SIG_DRIVE, 0);
|
|
}
|
|
|
|
static uint8_t
|
|
_mii_disk2_access(
|
|
mii_t * mii, struct mii_slot_t *slot,
|
|
uint16_t addr, uint8_t byte, bool write)
|
|
{
|
|
mii_card_disk2_t *c = slot->drv_priv;
|
|
mii_floppy_t * f = &c->floppy[c->selected];
|
|
uint8_t ret = 0;
|
|
|
|
if (write) {
|
|
// if (!(byte &0x80))
|
|
// printf("WRITE PC:%04x %4.4x: %2.2x\n", mii->cpu.PC, addr, byte);
|
|
c->write_register = byte;
|
|
mii_raise_signal(c->sig + SIG_WR, byte);
|
|
if (c->vcd) {
|
|
uint8_t delta = c->vcd->cycle - c->debug_last_write;
|
|
|
|
mii_raise_signal_float(
|
|
c->sig + SIG_WRITE_CYCLE, c->debug_last_duration != delta, 0);
|
|
c->debug_last_duration = delta;
|
|
c->debug_last_write = c->vcd->cycle;
|
|
}
|
|
}
|
|
int psw = addr & 0x0F;
|
|
int p = psw >> 1, on = psw & 1;
|
|
int read = 0;
|
|
switch (psw) {
|
|
case 0x00 ... 0x07: {
|
|
if (on) {
|
|
if ((f->stepper + 3) % 4 == p)
|
|
_mii_disk2_switch_track(mii, c, -2);
|
|
else if ((f->stepper + 1) % 4 == p)
|
|
_mii_disk2_switch_track(mii, c, 2);
|
|
f->stepper = p;
|
|
}
|
|
} break;
|
|
case 0x08:
|
|
case 0x09: {
|
|
// motor on/off
|
|
if (on) {
|
|
mii_timer_set(mii, c->timer_off, 0);
|
|
mii_timer_set(mii, c->timer_lss, 1);
|
|
f->motor = 1;
|
|
mii_raise_signal(c->sig + SIG_MOTOR, 1);
|
|
} else {
|
|
int32_t timer = mii_timer_get(mii, c->timer_off);
|
|
mii_timer_set(mii, c->timer_off, timer + 1000000); // one second
|
|
}
|
|
} break;
|
|
case 0x0A:
|
|
case 0x0B: {
|
|
if (on != c->selected) {
|
|
c->selected = on;
|
|
printf("SELECTED DRIVE: %d\n", c->selected);
|
|
c->floppy[on].motor = f->motor;
|
|
f->motor = 0;
|
|
mii_raise_signal(c->sig + SIG_MOTOR, 0);
|
|
}
|
|
} break;
|
|
case 0x0C:
|
|
case 0x0D:
|
|
c->lss_mode = (c->lss_mode & ~(1 << Q6_LOAD_BIT)) | (!!on << Q6_LOAD_BIT);
|
|
if (!(c->lss_mode & (1 << Q7_WRITE_BIT)) && f->heat) {
|
|
uint8_t track_id = f->track_id[f->qtrack];
|
|
uint32_t byte_index = f->bit_position >> 3;
|
|
unsigned int dstb = byte_index / MII_FLOPPY_HM_HIT_SIZE;
|
|
f->heat->read.map[track_id][dstb] = 255;
|
|
f->heat->read.seed++;
|
|
read++;
|
|
}
|
|
break;
|
|
case 0x0E:
|
|
case 0x0F:
|
|
c->lss_mode = (c->lss_mode & ~(1 << Q7_WRITE_BIT)) | (!!on << Q7_WRITE_BIT);
|
|
break;
|
|
}
|
|
/*
|
|
* Here we run one LSS cycle ahead of 'schedule', just because it allows
|
|
* the write protect bit to be loaded if needed, it *has* to run before
|
|
* we return this value, so we marked a skip and run it here.
|
|
*/
|
|
// _mii_disk2_lss_tick(c);
|
|
// c->lss_skip++;
|
|
if (read)
|
|
mii_raise_signal(c->sig + SIG_READ_DR, c->data_register);
|
|
ret = on ? byte : c->data_register;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
_mii_disk2_command(
|
|
mii_t * mii,
|
|
struct mii_slot_t *slot,
|
|
uint32_t cmd,
|
|
void * param)
|
|
{
|
|
mii_card_disk2_t *c = slot->drv_priv;
|
|
int res = -1;
|
|
switch (cmd) {
|
|
case MII_SLOT_DRIVE_COUNT:
|
|
if (param) {
|
|
*(int *)param = 2;
|
|
res = 0;
|
|
}
|
|
break;
|
|
case MII_SLOT_DRIVE_WP ... MII_SLOT_DRIVE_WP + 2 - 1: {
|
|
int drive = cmd - MII_SLOT_DRIVE_WP;
|
|
int *wp = param;
|
|
if (wp) {
|
|
res = 0;
|
|
printf("Drive %d WP: 0x%x set %s\n", drive,
|
|
c->floppy[drive].write_protected,
|
|
*wp ? "ON" : "OFF");
|
|
c->floppy[drive].write_protected =
|
|
(c->floppy[drive].write_protected &
|
|
~(MII_FLOPPY_WP_MANUAL))|
|
|
(*wp ? MII_FLOPPY_WP_MANUAL : 0);
|
|
}
|
|
} break;
|
|
case MII_SLOT_DRIVE_LOAD ... MII_SLOT_DRIVE_LOAD + 2 - 1: {
|
|
int drive = cmd - MII_SLOT_DRIVE_LOAD;
|
|
const char *pathname = param;
|
|
mii_dd_file_t *file = NULL;
|
|
if (pathname && *pathname) {
|
|
if (c->drive[drive].file &&
|
|
!strcmp(c->drive[drive].file->pathname, pathname)) {
|
|
printf("%s D%d Same file, not reloading\n",
|
|
__func__, drive);
|
|
return 0;
|
|
}
|
|
file = mii_dd_file_load(&mii->dd, pathname, O_RDWR);
|
|
if (!file)
|
|
return -1;
|
|
}
|
|
// reinit all tracks, bits, maps etc
|
|
mii_floppy_init(&c->floppy[drive]);
|
|
mii_dd_drive_load(&c->drive[drive], file);
|
|
mii_floppy_load(&c->floppy[drive], file);
|
|
res = 0;
|
|
} break;
|
|
case MII_SLOT_D2_GET_FLOPPY: {
|
|
if (param) {
|
|
mii_floppy_t ** fp = param;
|
|
fp[0] = &c->floppy[0];
|
|
fp[1] = &c->floppy[1];
|
|
res = 0;
|
|
}
|
|
} break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static mii_slot_drv_t _driver = {
|
|
.name = "disk2",
|
|
.desc = "Apple Disk ][",
|
|
.init = _mii_disk2_init,
|
|
.reset = _mii_disk2_reset,
|
|
.access = _mii_disk2_access,
|
|
.command = _mii_disk2_command,
|
|
};
|
|
MI_DRIVER_REGISTER(_driver);
|
|
|
|
static void
|
|
_mii_disk2_vcd_debug(
|
|
mii_card_disk2_t *c,
|
|
int on)
|
|
{
|
|
if (c->vcd) {
|
|
mii_vcd_close(c->vcd);
|
|
c->vcd = NULL;
|
|
printf("VCD OFF\n");
|
|
} else {
|
|
c->vcd = calloc(1, sizeof(*c->vcd));
|
|
// 2Mhz clock
|
|
mii_vcd_init(c->mii, "/tmp/disk2.vcd", c->vcd, 500);
|
|
printf("VCD ON\n");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_DR, 8, "DR");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_WR, 8, "WR");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_DRIVE, 1, "DRIVE");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_MOTOR, 1, "MOTOR");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_READ_DR, 8, "READ");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_CLK, 1, "LSS_CLK");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_SEQ, 4, "LSS_SEQ");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_CMD, 4, "LSS_CMD");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_WRITE, 1, "LSS_W/R");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_LOAD, 1, "LSS_L/S");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_QA, 1, "LSS_QA");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_RP, 1, "LSS_RP");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_WB, 1, "LSS_WB");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_RANDOM, 1, "LSS_RANDOM");
|
|
mii_vcd_add_signal(c->vcd, c->sig + SIG_WRITE_CYCLE, 1, "WRITE_CYCLE");
|
|
mii_vcd_start(c->vcd);
|
|
// put the LSS state in the VCD to start with
|
|
mii_raise_signal(c->sig + SIG_LSS_QA,
|
|
!!(c->lss_mode & (1 << QA_BIT)));
|
|
mii_raise_signal(c->sig + SIG_LSS_WRITE,
|
|
!!(c->lss_mode & (1 << Q7_WRITE_BIT)));
|
|
mii_raise_signal(c->sig + SIG_LSS_LOAD,
|
|
!!(c->lss_mode & (1 << Q6_LOAD_BIT)));
|
|
}
|
|
}
|
|
|
|
/* Sather Infamous Table, pretty much verbatim
|
|
WRITE
|
|
-----------READ PULSE--------- -—------NO READ PULSE--------
|
|
----SHIFT----- -----LOAD----- ----SHIFT----- -----LOAD-----
|
|
SEQ --QA'- --QA-- --QA'- --QA-- --QA'- --QA-- --QA'- --QA--
|
|
0- 18-NOP 18-NOP 18-NOP 18-NOP 18-NOP 18-NOP 18-NOP 18-NOP
|
|
1- 28-NOP 28-NOP 28-NOP 28-NOP 28-NOP 28-NOP 28-NOP 28-NOP
|
|
2- 39-SL0 39-SL0 3B-LD 3B-LD 39-SL0 39-SL0 3B-LD 3B-LD
|
|
3- 48-NOP 48-NOP 48-NOP 48-NOP 48-NOP 48-NOP 48-NOP 48-NOP
|
|
4- 58-NOP 58-NOP 58-NOP 58-NOP 58-NOP 58-NOP 58-NOP 58-NOP
|
|
5- 68-NOP 68-NOP 68-NOP 68-NOP 68-NOP 68-NOP 68-NOP 68-NOP
|
|
6- 78-NOP 78-NOP 78-NOP 78-NOP 78-NOP 78-NOP 78-NOP 78-NOP
|
|
7- 08-NOP 88-NOP 08-NOP 88-NOP 08-NOP 88-NOP 08-NOP 88-NOP
|
|
8- 98-NOP 98-NOP 98-NOP 98-NOP 98-NOP 98-NOP 98-NOP 98-NOP
|
|
9- A8-NOP A8-NOP A8-NOP A8-NOP A8-NOP A8-NOP A8-NOP A8-NOP
|
|
A- B9-SL0 B9-SL0 BB-LD BB-LD B9-SL0 B9-SL0 BB-LD BB-LD
|
|
B- C8-NOP C8-NOP C8-NOP C8-NOP C8-NOP C8-NOP C8-NOP C8-NOP
|
|
C- D8-NOP D8-NOP D8-NOP D8-NOP D8-NOP D8-NOP D8-NOP D8-NOP
|
|
D- E8-NOP E8-NOP E8-NOP E8-NOP E8-NOP E8-NOP E8-NOP E8-NOP
|
|
E- F8-NOP F8-NOP F8-NOP F8-NOP F8-NOP F8-NOP F8-NOP F8-NOP
|
|
F- 88-NOP 08-NOP 88-NOP 08-NOP 88-NOP 08-NOP 88-NOP 08-NOP
|
|
READ
|
|
------------SHIFT------------ -------------LOAD------------
|
|
-----QA'------ ------QA------ -----QA'------ -----QA------
|
|
--RP-- -NORP- --RP-- -NORP- --RP-- -NORP- --RP-- -NORP-
|
|
0- 18-NOP 18-NOP 18-NOP 18-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
1- 2D-SL1 2D-SL1 38-NOP 38-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
2- D8-NOP 38-NOP 08-NOP 28-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
3- D8-NOP 48-NOP 48-NOP 48-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
4- D8-NOP 58-NOP D8-NOP 58-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
5- D8-NOP 68-NOP D8-NOP 68-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
6- D8-NOP 78-NOP D8-NOP 78-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
7- D8-NOP 88-NOP D8-NOP 88-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
8- D8-NOP 98-NOP D8-NOP 98-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
9- D8-NOP 29-SL0 D8-NOP A8-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
A- CD-SL1 BD-SL1 D8-NOP B8-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
B- D9-SL0 59-SL0 D8-NOP C8-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
C- D9-SL0 D9-SL0 D8-NOP A0-CLR 0A-SR 0A-SR 0A-SR 0A-SR
|
|
D- D8-NOP 08-NOP E8-NOP E8-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
E- FD-SL1 FD-SL1 F8-NOP F8-NOP 0A-SR 0A-SR 0A-SR 0A-SR
|
|
F- DD-SL1 4D-SL1 E0-CLR E0-CLR 0A-SR 0A-SR 0A-SR 0A-SR
|
|
*/
|
|
enum {
|
|
WRITE = (1 << Q7_WRITE_BIT),
|
|
LOAD = (1 << Q6_LOAD_BIT),
|
|
QA1 = (1 << QA_BIT),
|
|
RP1 = (1 << RP_BIT),
|
|
/* This keeps the transposed table more readable, as it looks like the book */
|
|
READ = 0, SHIFT = 0, QA0 = 0, RP0 = 0,
|
|
};
|
|
// And this is the same Sather table (Figure 9.11, page 9-20) but transposed
|
|
static const uint8_t lss_rom16s[16][16] = {
|
|
[WRITE|RP0|SHIFT|QA0]={ 0x18,0x28,0x39,0x48,0x58,0x68,0x78,0x08,0x98,0xA8,0xB9,0xC8,0xD8,0xE8,0xF8,0x88 },
|
|
[WRITE|RP0|SHIFT|QA1]={ 0x18,0x28,0x39,0x48,0x58,0x68,0x78,0x88,0x98,0xA8,0xB9,0xC8,0xD8,0xE8,0xF8,0x08 },
|
|
[WRITE|RP0|LOAD|QA0]={ 0x18,0x28,0x3B,0x48,0x58,0x68,0x78,0x08,0x98,0xA8,0xBB,0xC8,0xD8,0xE8,0xF8,0x88 },
|
|
[WRITE|RP0|LOAD|QA1]={ 0x18,0x28,0x3B,0x48,0x58,0x68,0x78,0x88,0x98,0xA8,0xBB,0xC8,0xD8,0xE8,0xF8,0x08 },
|
|
[WRITE|RP1|SHIFT|QA0]={ 0x18,0x28,0x39,0x48,0x58,0x68,0x78,0x08,0x98,0xA8,0xB9,0xC8,0xD8,0xE8,0xF8,0x88 },
|
|
[WRITE|RP1|SHIFT|QA1]={ 0x18,0x28,0x39,0x48,0x58,0x68,0x78,0x88,0x98,0xA8,0xB9,0xC8,0xD8,0xE8,0xF8,0x08 },
|
|
[WRITE|RP1|LOAD|QA0]={ 0x18,0x28,0x3B,0x48,0x58,0x68,0x78,0x08,0x98,0xA8,0xBB,0xC8,0xD8,0xE8,0xF8,0x88 },
|
|
[WRITE|RP1|LOAD|QA1]={ 0x18,0x28,0x3B,0x48,0x58,0x68,0x78,0x88,0x98,0xA8,0xBB,0xC8,0xD8,0xE8,0xF8,0x08 },
|
|
[READ|SHIFT|QA0|RP1]={ 0x18,0x2D,0xD8,0xD8,0xD8,0xD8,0xD8,0xD8,0xD8,0xD8,0xCD,0xD9,0xD9,0xD8,0xFD,0xDD },
|
|
[READ|SHIFT|QA0|RP0]={ 0x18,0x2D,0x38,0x48,0x58,0x68,0x78,0x88,0x98,0x29,0xBD,0x59,0xD9,0x08,0xFD,0x4D },
|
|
[READ|SHIFT|QA1|RP1]={ 0x18,0x38,0x08,0x48,0xD8,0xD8,0xD8,0xD8,0xD8,0xD8,0xD8,0xD8,0xD8,0xE8,0xF8,0xE0 },
|
|
[READ|SHIFT|QA1|RP0]={ 0x18,0x38,0x28,0x48,0x58,0x68,0x78,0x88,0x98,0xA8,0xB8,0xC8,0xA0,0xE8,0xF8,0xE0 },
|
|
[READ|LOAD|QA0|RP1]={ 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A },
|
|
[READ|LOAD|QA0|RP0]={ 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A },
|
|
[READ|LOAD|QA1|RP1]={ 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A },
|
|
[READ|LOAD|QA1|RP0]={ 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A },
|
|
};
|
|
|
|
static const uint8_t SEQUENCER_ROM_16[256] = {
|
|
// See Understanding the Apple IIe, Figure 9.11 The DOS 3.3 Logic State Sequencer
|
|
// Note that the column order here is NOT the same as in Figure 9.11 for Q7 H (Write).
|
|
//
|
|
// Q7 L (Read) Q7 H (Write)
|
|
// Q6 L (Shift) Q6 H (Load) Q6 L (Shift) Q6 H (Load)
|
|
// QA L QA H QA L QA H QA L QA H QA L QA H
|
|
// 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
|
|
0x18, 0x18, 0x18, 0x18, 0x0A, 0x0A, 0x0A, 0x0A, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 0
|
|
0x2D, 0x2D, 0x38, 0x38, 0x0A, 0x0A, 0x0A, 0x0A, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, // 1
|
|
0xD8, 0x38, 0x08, 0x28, 0x0A, 0x0A, 0x0A, 0x0A, 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B, // 2
|
|
0xD8, 0x48, 0x48, 0x48, 0x0A, 0x0A, 0x0A, 0x0A, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, // 3
|
|
0xD8, 0x58, 0xD8, 0x58, 0x0A, 0x0A, 0x0A, 0x0A, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, // 4
|
|
0xD8, 0x68, 0xD8, 0x68, 0x0A, 0x0A, 0x0A, 0x0A, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, // 5
|
|
0xD8, 0x78, 0xD8, 0x78, 0x0A, 0x0A, 0x0A, 0x0A, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, // 6
|
|
0xD8, 0x88, 0xD8, 0x88, 0x0A, 0x0A, 0x0A, 0x0A, 0x08, 0x08, 0x88, 0x88, 0x08, 0x08, 0x88, 0x88, // 7
|
|
0xD8, 0x98, 0xD8, 0x98, 0x0A, 0x0A, 0x0A, 0x0A, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, // 8
|
|
0xD8, 0x29, 0xD8, 0xA8, 0x0A, 0x0A, 0x0A, 0x0A, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, // 9
|
|
0xCD, 0xBD, 0xD8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB, // A
|
|
0xD9, 0x59, 0xD8, 0xC8, 0x0A, 0x0A, 0x0A, 0x0A, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, // B
|
|
0xD9, 0xD9, 0xD8, 0xA0, 0x0A, 0x0A, 0x0A, 0x0A, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, // C
|
|
0xD8, 0x08, 0xE8, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, // D
|
|
0xFD, 0xFD, 0xF8, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, // E
|
|
0xDD, 0x4D, 0xE0, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A, 0x88, 0x88, 0x08, 0x08, 0x88, 0x88, 0x08, 0x08 // F
|
|
// 0 1 1 3 4 5 6 7 8 9 A B C D E F
|
|
};
|
|
|
|
FILE *debug = NULL;
|
|
|
|
static void
|
|
_mii_disk2_lss_tick(
|
|
mii_card_disk2_t *c )
|
|
{
|
|
if (c->lss_skip) {
|
|
c->lss_skip--;
|
|
return;
|
|
}
|
|
mii_floppy_t *f = &c->floppy[c->selected];
|
|
|
|
if (c->vcd)
|
|
c->vcd->cycle += 1;
|
|
c->clock += 4; // 4 is 0.5us.. we run at 2MHz
|
|
|
|
uint8_t track_id = f->track_id[f->qtrack];
|
|
uint8_t * track = f->track_data[track_id];
|
|
uint32_t byte_index = f->bit_position >> 3;
|
|
uint8_t bit_index = 7 - (f->bit_position & 7);
|
|
uint8_t rp = 0;
|
|
|
|
mii_raise_signal(c->sig + SIG_LSS_CLK, c->clock >= f->bit_timing);
|
|
if (c->clock >= f->bit_timing) {
|
|
uint8_t bit = track[byte_index];
|
|
bit = (bit >> bit_index) & 1;
|
|
c->head = (c->head << 1) | bit;
|
|
// see WOZ spec for how we do this here
|
|
if ((c->head & 0xf) == 0) {
|
|
bit = f->track_data[MII_FLOPPY_NOISE_TRACK][byte_index];
|
|
rp = (bit >> bit_index) & 1;
|
|
// printf("RANDOM TRACK %2d %2d %2d : %d\n",
|
|
// track_id, byte_index, bit_index, bit);
|
|
mii_raise_signal_float(c->sig + SIG_LSS_RANDOM, rp, 0);
|
|
} else {
|
|
rp = (c->head >> 1) & 1;
|
|
mii_raise_signal_float(c->sig + SIG_LSS_RANDOM, rp, 1);
|
|
}
|
|
}
|
|
c->lss_mode = (c->lss_mode & ~(1 << RP_BIT)) | (rp << RP_BIT);
|
|
c->lss_mode = (c->lss_mode & ~(1 << QA_BIT)) |
|
|
(!!(c->data_register & 0x80) << QA_BIT);
|
|
|
|
mii_raise_signal(c->sig + SIG_LSS_RP, rp);
|
|
mii_raise_signal(c->sig + SIG_LSS_QA,
|
|
!!(c->lss_mode & (1 << QA_BIT)));
|
|
mii_raise_signal(c->sig + SIG_LSS_WRITE,
|
|
!!(c->lss_mode & (1 << Q7_WRITE_BIT)));
|
|
mii_raise_signal(c->sig + SIG_LSS_LOAD,
|
|
!!(c->lss_mode & (1 << Q6_LOAD_BIT)));
|
|
|
|
const uint8_t *rom = lss_rom16s[c->lss_mode];
|
|
uint8_t cmd = rom[c->lss_state];
|
|
uint8_t next = cmd >> 4;
|
|
uint8_t action = cmd & 0xF;
|
|
|
|
mii_raise_signal(c->sig + SIG_LSS_SEQ, c->lss_state);
|
|
mii_raise_signal(c->sig + SIG_LSS_CMD, action);
|
|
|
|
if (action & 0b1000) { // Table 9.3 in Sather's book
|
|
switch (action & 0b0011) {
|
|
case 1: // SL0/1
|
|
c->data_register <<= 1;
|
|
c->data_register |= !!(action & 0b0100);
|
|
mii_raise_signal(c->sig + SIG_DR, c->data_register);
|
|
break;
|
|
case 2: // SR
|
|
c->data_register = (c->data_register >> 1) |
|
|
(!!f->write_protected << 7);
|
|
mii_raise_signal(c->sig + SIG_DR, c->data_register);
|
|
break;
|
|
case 3: {// LD
|
|
uint8_t track_id = f->track_id[f->qtrack];
|
|
c->data_register = c->write_register;
|
|
mii_raise_signal(c->sig + SIG_DR, c->data_register);
|
|
f->seed_dirty++;
|
|
if (f->heat) {
|
|
uint32_t byte_index = f->bit_position >> 3;
|
|
unsigned int dstb = byte_index/MII_FLOPPY_HM_HIT_SIZE;
|
|
f->heat->write.map[track_id][dstb] = 255;
|
|
f->heat->write.seed++;
|
|
}
|
|
} break;
|
|
}
|
|
} else { // CLR
|
|
c->data_register = 0;
|
|
mii_raise_signal(c->sig + SIG_DR, c->data_register);
|
|
}
|
|
if ((c->lss_mode & (1 << Q7_WRITE_BIT)) &&
|
|
track_id < MII_FLOPPY_TRACK_COUNT) {
|
|
// on state 0 and 8 we write a bit...
|
|
if ((c->lss_state & 0b0111) == 0) {
|
|
uint8_t bit = c->data_register >> 7;
|
|
// uint8_t bit = !!(c->lss_state & 0x8);
|
|
mii_raise_signal_float(c->sig + SIG_LSS_WB, 1, 0);
|
|
if (!f->tracks[track_id].dirty) {
|
|
// printf("DIRTY TRACK %2d \n", track_id);
|
|
f->tracks[track_id].dirty = 1;
|
|
/*
|
|
* This little trick allows to have all the track neatly aligned
|
|
* on bit zero when formatting a floppy or doing a copy, this helps
|
|
* debug quite a bit.
|
|
*/
|
|
if (f->tracks[track_id].virgin) {
|
|
f->tracks[track_id].virgin = 0;
|
|
f->bit_position = 0;
|
|
if (track_id == 0)
|
|
_mii_disk2_vcd_debug(c, 1);
|
|
}
|
|
f->seed_dirty++;
|
|
}
|
|
f->track_data[track_id][byte_index] &= ~(1 << bit_index);
|
|
f->track_data[track_id][byte_index] |= (bit << bit_index);
|
|
} else {
|
|
mii_raise_signal_float(c->sig + SIG_LSS_WB, 0, 0);
|
|
}
|
|
}
|
|
|
|
c->lss_state = next;
|
|
if (c->clock >= f->bit_timing) {
|
|
c->clock -= f->bit_timing;
|
|
f->bit_position = (f->bit_position + 1) % f->tracks[track_id].bit_count;
|
|
}
|
|
}
|
|
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
static void
|
|
_mii_mish_d2(
|
|
void * param,
|
|
int argc,
|
|
const char * argv[])
|
|
{
|
|
// mii_t * mii = param;
|
|
if (!_mish_d2) {
|
|
printf("No Disk ][ card installed\n");
|
|
return;
|
|
}
|
|
static int sel = 0;
|
|
if (!argv[1] || !strcmp(argv[1], "list")) {
|
|
mii_card_disk2_t *c = _mish_d2;
|
|
for (int i = 0; i < 2; i++) {
|
|
mii_floppy_t *f = &c->floppy[i];
|
|
printf("Drive %d %s\n", f->id, f->write_protected ? "WP" : "RW");
|
|
printf("\tMotor: %3s qtrack:%3d Bit %6d/%6d\n",
|
|
f->motor ? "ON" : "OFF", f->qtrack,
|
|
f->bit_position, f->tracks[0].bit_count);
|
|
}
|
|
return;
|
|
}
|
|
if (!strcmp(argv[1], "sel")) {
|
|
if (argv[2]) {
|
|
sel = atoi(argv[2]);
|
|
}
|
|
printf("Selected drive: %d\n", sel);
|
|
return;
|
|
}
|
|
if (!strcmp(argv[1], "wp")) {
|
|
if (argv[2]) {
|
|
int wp = atoi(argv[2]);
|
|
mii_card_disk2_t *c = _mish_d2;
|
|
mii_floppy_t *f = &c->floppy[sel];
|
|
f->write_protected = wp;
|
|
}
|
|
printf("Drive %d Write protected: %d\n", sel,
|
|
_mish_d2->floppy[sel].write_protected);
|
|
return;
|
|
}
|
|
// dump a track, specify track number and number of bytes
|
|
if (!strcmp(argv[1], "track")) {
|
|
mii_card_disk2_t *c = _mish_d2;
|
|
mii_floppy_t *f = &c->floppy[sel];
|
|
if (argv[2]) {
|
|
int track = atoi(argv[2]);
|
|
int count = 256;
|
|
if (argv[3]) {
|
|
if (!strcmp(argv[3], "save")) {
|
|
// save one binary file in tmp with just that track
|
|
uint8_t *data = f->track_data[track];
|
|
char *filename;
|
|
asprintf(&filename, "/tmp/track_%d.bin", track);
|
|
int fd = open(filename, O_CREAT | O_WRONLY, 0666);
|
|
write(fd, data, MII_FLOPPY_DEFAULT_TRACK_SIZE);
|
|
close(fd);
|
|
printf("Saved track %d to %s\n", track, filename);
|
|
free(filename);
|
|
return;
|
|
}
|
|
count = atoi(argv[3]);
|
|
}
|
|
uint8_t *data = f->track_data[track];
|
|
|
|
for (int i = 0; i < count; i += 8) {
|
|
uint8_t *line = data + i;
|
|
#if 1
|
|
for (int bi = 0; bi < 8; bi++) {
|
|
uint8_t b = line[bi];
|
|
for (int bbi = 0; bbi < 8; bbi++) {
|
|
printf("%c", (b & 0x80) ? '1' : '0');
|
|
b <<= 1;
|
|
}
|
|
}
|
|
printf("\n");
|
|
#endif
|
|
for (int bi = 0; bi < 8; bi++)
|
|
printf("%8x", line[bi]);
|
|
printf("\n");
|
|
}
|
|
} else {
|
|
printf("track <track 0-36> [count]\n");
|
|
}
|
|
return;
|
|
}
|
|
if (!strcmp(argv[1], "dirty")) {
|
|
mii_card_disk2_t *c = _mish_d2;
|
|
mii_floppy_t *f = &c->floppy[sel];
|
|
f->seed_dirty = f->seed_saved = rand();
|
|
return;
|
|
}
|
|
if (!strcmp(argv[1], "resync")) {
|
|
mii_card_disk2_t *c = _mish_d2;
|
|
mii_floppy_t *f = &c->floppy[sel];
|
|
printf("Resyncing tracks\n");
|
|
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++)
|
|
mii_floppy_resync_track(f, i, 0);
|
|
return;
|
|
}
|
|
if (!strcmp(argv[1], "map")) {
|
|
mii_card_disk2_t *c = _mish_d2;
|
|
mii_floppy_t *f = &c->floppy[sel];
|
|
|
|
printf("Disk map:\n");
|
|
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++) {
|
|
mii_floppy_track_map_t map = {};
|
|
int r = mii_floppy_map_track(f, i, &map, 0);
|
|
printf("Track %2d: %7s\n",
|
|
i, r == 0 ? "OK" : "Invalid");
|
|
if (r != 0) {
|
|
printf("Track %d has %5d bits\n", i, f->tracks[i].bit_count);
|
|
for (int si = 0; si < 16; si++)
|
|
printf("[%2d hs:%4d h:%5d ds:%3d d:%5d]%s",
|
|
si, map.sector[si].hsync,
|
|
map.sector[si].header,
|
|
map.sector[si].dsync,
|
|
map.sector[si].data,
|
|
si & 1 ? "\n" : " ");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (!strcmp(argv[1], "trace")) {
|
|
if (debug == NULL)
|
|
debug = fopen("/tmp/disk2.log", "w");
|
|
else {
|
|
fclose(debug);
|
|
debug = NULL;
|
|
}
|
|
printf("Debug trace %s\n", debug ? "ON" : "OFF");
|
|
return;
|
|
}
|
|
if (!strcmp(argv[1], "vcd")) {
|
|
mii_card_disk2_t *c = _mish_d2;
|
|
_mii_disk2_vcd_debug(c, !c->vcd);
|
|
}
|
|
}
|
|
|
|
#include "mish.h"
|
|
|
|
MISH_CMD_NAMES(d2, "d2");
|
|
MISH_CMD_HELP(d2,
|
|
"d2: disk ][ internals",
|
|
" <default>: dump status"
|
|
);
|
|
MII_MISH(d2, _mii_mish_d2);
|