From nobody Tue Jun 30 03:39:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=me.com ARC-Seal: i=1; a=rsa-sha256; t=1781740304; cv=none; d=zohomail.com; s=zohoarc; b=DThuS8Y9HVwekzRc+TislSmo57d1gkzy9BH46KDSft0DBhhJNvkVvp17a2sUKPg4gc05FCHBtDkfdWa6QsYmYQVuo4OVIZ+kWiYCkp5IPDS5K5nY898zRtgepz9cheXQef4EJg6miCQeE+1NvHBdO1dCSvJyTwM3cQcszFrWe3c= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781740304; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=HQdZhEVF3TBJqROsUMOB9AaoM+J9m9i344IF0Mr3c7I=; b=bPmroILsN3XR8i+NdjdOtnnnf7Gz6NnAZASSTuDP/7w5bqgIXGh6Rf1rzxzFUwk7Kwo/PUO959Y2qP99kxeKCVheNOeWQfv4FGk7LcFLgXRr2ZUqtZtlHZVGVvEiElpztBfXWDol2Z6Th9Q3SJc00aHOMZN4E2L49rI2/gI/1/I= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781740303989859.7236164186637; Wed, 17 Jun 2026 16:51:43 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wa023-0004Pe-G1; Wed, 17 Jun 2026 19:50:55 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wa01z-0004OX-T7 for qemu-devel@nongnu.org; Wed, 17 Jun 2026 19:50:52 -0400 Received: from p-east2-cluster1-host11-snip4-10.eps.apple.com ([57.103.76.53] helo=outbound.st.icloud.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wa01v-0003uE-3y for qemu-devel@nongnu.org; Wed, 17 Jun 2026 19:50:51 -0400 Received: from outbound.st.icloud.com (unknown [127.0.0.2]) by p00-icloudmta-asmtp-us-east-1a-60-percent-11 (Postfix) with ESMTPS id 8F8291800218; Wed, 17 Jun 2026 23:50:40 +0000 (UTC) Received: from localhost.localdomain (unknown [17.42.251.67]) by p00-icloudmta-asmtp-us-east-1a-60-percent-11 (Postfix) with ESMTPSA id 90A1E1800124; Wed, 17 Jun 2026 23:50:39 +0000 (UTC) X-ICL-Out-Info: HUtFAUMHWwJACUgBTUQeDx5WFlZNRAJCTQ5JHVoHRQNGD1YCWgZLVxQEFVsHVghdEkoXXS1aDhwTVhUTC1NWXxUXG1wAFxlRTQ5YWwhbBA8fTAxRAkIFVl5KDB0EVAddBV1WUAJaS0IES0VoXAVcHEAXSB1faktWFAQVVkNUBF9QVBFXUAtZAkIPSAVcAFkBRQlLAUMFWgZJCFUBQFoDWBFaF1EeWAVyHVxWUAJaVRIEQAhWUF4IXh9MHA== Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=me.com; s=1a1hai; t=1781740243; x=1784332243; bh=HQdZhEVF3TBJqROsUMOB9AaoM+J9m9i344IF0Mr3c7I=; h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme; b=Cf+735P+GNGOCrPaUkjLDD+mh6t6H+KwzyO7Zb4VEnbS3najNWwSrs7T+PDKEqlYOiYRK1HS73eCQ+i7QPQlZMFeg/glN5FFrXRZZEIvPExuGKzmGll5Ku50j+9EJThs2fdNhduOw7i6hqGiz0Ppyp7GTL4S90pXuP4CeHfmG85PzuwSCnwLAxZhMgub56qbuD/Yogj+dqxBEYuM+h149gDI21p1XUUI7BQKoFk9Sa/yvpCsxmE+mcBHup2ygMmT68Hdr8KWp3rPDvZDjEok5A1dXGNNWS+Hp+Q6ASiGbHeJ0i77E/c+FHwfaxmtdAnxwicwUu3vtg7k/7z48I8zmg== From: Matt Jacobson To: qemu-devel@nongnu.org Cc: Kevin Wolf , Hanna Reitz , qemu-block@nongnu.org, Mark Cave-Ayland , Laurent Vivier , Matt Jacobson Subject: [PATCH v2 1/5] hw/block: add Sony/Apple "SuperDrive" diskette drive Date: Wed, 17 Jun 2026 19:50:28 -0400 Message-ID: <20260617235032.21491-2-mhjacobson@me.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260617235032.21491-1-mhjacobson@me.com> References: <20260617235032.21491-1-mhjacobson@me.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-GUID: uH_wTHThyuV7uYej_6TYR_4UM7cfwYpt X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNjE3MDIyNyBTYWx0ZWRfXwr6gGulZTi/k co9NMa0ZTjMvY8GkJQQy/TFEGXoIPi951yHYYoqwFvALJ+s4X3Q+itHHG1oLZbdeUWa8+ee7yR+ udqzTmabelFar7VamfzfglmfgbI4TSgxM1kdUf8Z8Vcg38s+NTKgAawY9NBlA+FC7DJL/PGrSM/ GfGMwrsSJgufMXOXnflryXGoVLd2DGrSi4+/zSgrAUXVzqqiX97itGrlt5qp8Bbx9fbhEEJa5Ea L1OBaUs6YJ3+9YhUuqpjGRa+cdaJkaD/xnau/tsd5Bd7tjOFCvqNXoV4tPFBTjd+/8Pw00vsejM MB5eDzt8BY2DIIyZRdf X-Proofpoint-ORIG-GUID: uH_wTHThyuV7uYej_6TYR_4UM7cfwYpt Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=57.103.76.53; envelope-from=mhjacobson@me.com; helo=outbound.st.icloud.com X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @me.com) X-ZM-MESSAGEID: 1781740306085158500 Content-Type: text/plain; charset="utf-8" The Mac Quadra 800 contains a "SuperDrive" floppy drive that supports both GCR (400 kB and 800 kB) and MFM (1440 kB) diskettes. Implement an emulated version, to be controlled by a forthcoming SWIM2 device. Signed-off-by: Matt Jacobson --- hw/block/meson.build | 2 +- hw/block/sony_superdrive.c | 994 +++++++++++++++++++++++++++++ hw/block/trace-events | 10 + include/hw/block/sony_superdrive.h | 56 ++ 4 files changed, 1061 insertions(+), 1 deletion(-) create mode 100644 hw/block/sony_superdrive.c create mode 100644 include/hw/block/sony_superdrive.h diff --git a/hw/block/meson.build b/hw/block/meson.build index d646323b82..c4b873a906 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -10,7 +10,7 @@ system_ss.add(when: 'CONFIG_PFLASH_CFI01', if_true: files= ('pflash_cfi01.c')) system_ss.add(when: 'CONFIG_PFLASH_CFI02', if_true: files('pflash_cfi02.c'= )) system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80.c')) system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80_sfdp.c')) -system_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c')) +system_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c', 'sony_superdri= ve.c')) system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-block.c')) =20 system_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c')) diff --git a/hw/block/sony_superdrive.c b/hw/block/sony_superdrive.c new file mode 100644 index 0000000000..5d8efcc8de --- /dev/null +++ b/hw/block/sony_superdrive.c @@ -0,0 +1,994 @@ +/* + * QEMU Macintosh SuperDrive floppy disk drive emulator + * + * Copyright (c) 2025 Matt Jacobson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include +#include +#include +#include "qemu/error-report.h" +#include "hw/block/sony_superdrive.h" +#include "system/block-backend.h" +#include "system/block-backend-io.h" +#include "system/blockdev.h" +#include "trace.h" + +/* Sony drive register selects (CA2-CA1-CA0-SEL) */ +#define SONY_REG_DIRTN 0x0 +#define SONY_REG_CSTIN 0x1 /* a.k.a. dipAdr ("disk in place") */ +#define SONY_REG_STEP 0x2 +#define SONY_REG_WRTPRT 0x3 +#define SONY_REG_MOTORON 0x4 +#define SONY_REG_TKO 0x5 +#define SONY_REG_EJECT_L 0x6 +#define SONY_REG_TACH 0x7 +#define SONY_REG_RDDATA0 0x8 +#define SONY_REG_RDDATA1 0x9 +#define SONY_REG_SUPERDRIVE 0xa /* a.k.a. mfmDrvAdr */ +#define SONY_REG_GCR 0xb +#define SONY_REG_SINGLE_SIDE 0xc +#define SONY_REG_READY 0xd +#define SONY_REG_INSTALLED 0xe /* a.k.a. DRVIN, a.k.a. drvExstAdr */ +#define SONY_REG_TWOMEG 0xf + +static const char *const sony_drive_reg_names[] =3D { + [SONY_REG_DIRTN] =3D "DIRTN", + [SONY_REG_CSTIN] =3D "CSTIN", + [SONY_REG_STEP] =3D "STEP", + [SONY_REG_WRTPRT] =3D "WRTPRT", + [SONY_REG_MOTORON] =3D "MOTOR_ON", + [SONY_REG_TKO] =3D "TKO", + [SONY_REG_EJECT_L] =3D "EJECT_L", + [SONY_REG_TACH] =3D "TACH", + [SONY_REG_RDDATA0] =3D "RDDATA0", + [SONY_REG_RDDATA1] =3D "RDDATA1", + [SONY_REG_SUPERDRIVE] =3D "SUPERDRIVE", + [SONY_REG_GCR] =3D "GCR", + [SONY_REG_SINGLE_SIDE] =3D "SINGLE_SIDE", + [SONY_REG_READY] =3D "READY", + [SONY_REG_INSTALLED] =3D "INSTALLED", + [SONY_REG_TWOMEG] =3D "TWOMEG", +}; + +/* Sony drive write commands (CA2-CA1-CA0-SEL) */ +#define SONY_CMD_TRACK_POS 0x0 +#define SONY_CMD_TRACK_NEG 0x8 +#define SONY_CMD_TRACK_STEP 0x2 +#define SONY_CMD_SET_MFM 0x3 +#define SONY_CMD_SET_GCR 0xb +#define SONY_CMD_MOTOR_ON 0x4 +#define SONY_CMD_MOTOR_OFF 0xc +#define SONY_CMD_EJECT 0xe + +static const char *const sony_drive_cmd_names[] =3D { + [SONY_CMD_TRACK_POS] =3D "TRACK_POS", + [SONY_CMD_TRACK_NEG] =3D "TRACK_NEG", + [SONY_CMD_TRACK_STEP] =3D "TRACK_STEP", + [SONY_CMD_SET_MFM] =3D "SET_MFM", + [SONY_CMD_SET_GCR] =3D "SET_GCR", + [SONY_CMD_MOTOR_ON] =3D "MOTOR_ON", + [SONY_CMD_MOTOR_OFF] =3D "MOTOR_OFF", + [SONY_CMD_EJECT] =3D "EJECT", +}; + +#define SONY_GCR_TAG_LENGTH 12 +#define SONY_GCR_DATA_LENGTH 512 +#define SONY_GCR_SECTOR_LENGTH (SONY_GCR_TAG_LENGTH + SONY_GCR_DATA_LENGT= H) +#define SONY_GCR_ENCODED_LENGTH 699 /* i.e., sony_6and2_packed_len(524) */ +#define SONY_GCR_SYNC_LEN 6 +#define SONY_MFM_SECTOR_LENGTH 512 + +static uint8_t sony_drive_sectors_for_track(const SonyDrive *const drive, + const uint8_t track) +{ + if (drive->sectors_per_track) { + return drive->sectors_per_track; + } else { + /* + * 400K/800K GCR media use zone recording with variable sectors per + * track; each zone spans 16 tracks. + */ + static const uint8_t gcr_zone_spt[] =3D { 12, 11, 10, 9, 8 }; + uint8_t zone =3D track / 16; + + if (zone >=3D ARRAY_SIZE(gcr_zone_spt)) { + zone =3D ARRAY_SIZE(gcr_zone_spt) - 1; + } + + return gcr_zone_spt[zone]; + } +} + +static void bitset_set(uint8_t *const bitset, const unsigned int index, + const bool value) +{ + const uint8_t mask =3D (uint8_t)(1u << (index & 0x7)); + + if (value) { + bitset[index >> 3] |=3D mask; + } else { + bitset[index >> 3] &=3D (uint8_t)~mask; + } +} + +static void bitset_set_range(uint8_t *const bitset, const unsigned int ind= ex, + const unsigned int length, const bool value) +{ + for (unsigned int i =3D index; i < index + length; i++) { + bitset_set(bitset, i, value); + } +} + +static bool bitset_get(const uint8_t *const bitset, const unsigned int ind= ex) +{ + const uint8_t mask =3D (uint8_t)(1u << (index & 0x7)); + return (bitset[index >> 3] & mask) !=3D 0; +} + +static const uint8_t sony_gcr_encode_table[] =3D { + 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, + 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, + 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, + 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +}; + +/* 0xff is used as filler for bytes the decoder should never see. */ +static const uint8_t sony_gcr_decode_table[] =3D { + 0x00, 0x01, 0xff, 0xff, 0x02, 0x03, 0xff, 0x04, + 0x05, 0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x07, 0x08, 0xff, 0xff, 0xff, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0xff, 0xff, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0xff, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0xff, 0x1c, + 0x1d, 0x1e, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, + 0x20, 0x21, 0xff, 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28, 0xff, 0xff, 0xff, 0xff, 0xff, 0x29, + 0x2a, 0x2b, 0xff, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0xff, 0xff, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0xff, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, + 0x3e, 0x3f, +}; + +static uint8_t sony_gcr_encode_byte(const uint8_t value) +{ + assert((value & 0xc0) =3D=3D 0); + return sony_gcr_encode_table[value]; +} + +static uint8_t sony_gcr_decode_byte(const uint8_t value) +{ + assert(value & 0x80); + const uint8_t result =3D sony_gcr_decode_table[value - 0x96]; + assert(result !=3D 0xff); + return result; +} + +static void sony_gcr_encode_bytes(const uint8_t *const in, uint8_t *const = out, + const size_t len) +{ + for (size_t i =3D 0; i < len; i++) { + out[i] =3D sony_gcr_encode_byte(in[i]); + } +} + +static void sony_gcr_decode_bytes(const uint8_t *const in, uint8_t *const = out, + const size_t len) +{ + for (size_t i =3D 0; i < len; i++) { + out[i] =3D sony_gcr_decode_byte(in[i]); + } +} + +static size_t sony_6and2_packed_len(const size_t in_size) +{ + const size_t groups =3D in_size / 3; + const size_t rem =3D in_size % 3; + return groups * 4 + (rem ? (rem + 1) : 0); +} + +static void sony_6and2_pack(const uint8_t *in, const size_t in_size, + uint8_t *restrict out, const size_t out_size) +{ + assert(sony_6and2_packed_len(in_size) =3D=3D out_size); + + for (size_t i =3D 0; i < in_size; i +=3D 3) { + /* Allow partial groupings. */ + const bool have_b =3D (i + 1) < in_size; + const bool have_c =3D (i + 2) < in_size; + + const uint8_t a =3D *in++; + const uint8_t b =3D have_b ? *in++ : 0; + const uint8_t c =3D have_c ? *in++ : 0; + + /* High two bits (A7:6, B7:6, C7:6), then low six bits. */ + const uint8_t high =3D (uint8_t)(((a & 0xC0u) >> 2) | + ((b & 0xC0u) >> 4) | + ((c & 0xC0u) >> 6)); + const uint8_t low_a =3D (uint8_t)(a & 0x3Fu); + const uint8_t low_b =3D (uint8_t)(b & 0x3Fu); + const uint8_t low_c =3D (uint8_t)(c & 0x3Fu); + + *out++ =3D high; + *out++ =3D low_a; + + if (have_b) { + *out++ =3D low_b; + } + + if (have_c) { + *out++ =3D low_c; + } + } +} + +static void sony_6and2_unpack(const uint8_t *in, const size_t in_size, + uint8_t *restrict out, const size_t out_size) +{ + assert(sony_6and2_packed_len(out_size) =3D=3D in_size); + + for (size_t i =3D 0; i < in_size; i +=3D 4) { + /* Allow partial groupings. */ + const bool have_b =3D (i + 2) < in_size; + const bool have_c =3D (i + 3) < in_size; + + /* High two bits (A7:6, B7:6, C7:6), then low six bits. */ + const uint8_t high =3D *in++; + const uint8_t low_a =3D *in++; + const uint8_t low_b =3D have_b ? *in++ : 0; + const uint8_t low_c =3D have_c ? *in++ : 0; + + const uint8_t a =3D (uint8_t)((high << 2) & 0xC0u) | low_a; + const uint8_t b =3D (uint8_t)((high << 4) & 0xC0u) | low_b; + const uint8_t c =3D (uint8_t)((high << 6) & 0xC0u) | low_c; + + *out++ =3D a; + + if (have_b) { + *out++ =3D b; + } + + if (have_c) { + *out++ =3D c; + } + } +} + +static void sony_checksum_encode(const uint8_t *in, const size_t in_size, + uint8_t *out, const size_t out_size, + uint8_t *sum_out) +{ + assert(in_size =3D=3D out_size); + + uint16_t sum_a =3D 0, sum_b =3D 0, sum_c =3D 0; + + for (size_t i =3D 0; i < in_size; i +=3D 3) { + const bool have_b =3D (i + 1) < in_size; + const bool have_c =3D (i + 2) < in_size; + + const uint8_t a =3D *in++; + const uint8_t b =3D have_b ? *in++ : 0; + const uint8_t c =3D have_c ? *in++ : 0; + + /* Per-group left-rotate on sum_c. */ + sum_c =3D (uint16_t)((sum_c & 0x00FFu) << 1); + if (sum_c >=3D 0x0100u) { + sum_c++; /* insert 1 on carry */ + } + + /* Add to sum_a; carry from sum_c bumps sum_a. */ + sum_a +=3D a; + if (sum_c >=3D 0x0100u) { + sum_a++; + sum_c &=3D 0x00FFu; + } + const uint8_t a2 =3D a ^ (uint8_t)sum_c; + + /* Add to sum_b; carry from sum_a bumps sum_b. */ + if (have_b) { + sum_b +=3D b; + if (sum_a >=3D 0x0100u) { + sum_b++; + sum_a &=3D 0x00FFu; + } + } + const uint8_t b2 =3D b ^ (uint8_t)sum_a; + + /* Add to sum_c; carry from sum_b bumps sum_c. */ + if (have_c) { + sum_c +=3D c; + if (sum_b >=3D 0x0100u) { + sum_c++; + sum_b &=3D 0x00FFu; + } + } + const uint8_t c2 =3D c ^ (uint8_t)sum_b; + + *out++ =3D a2; + + if (have_b) { + *out++ =3D b2; + } + + if (have_c) { + *out++ =3D c2; + } + } + + *sum_out++ =3D (uint8_t)sum_a; + *sum_out++ =3D (uint8_t)sum_b; + *sum_out++ =3D (uint8_t)sum_c; +} + +static bool sony_checksum_decode(const uint8_t *in, const size_t in_size, + uint8_t *out, const size_t out_size, + const uint8_t *const sum) +{ + assert(in_size =3D=3D out_size); + + uint16_t sum_a =3D 0, sum_b =3D 0, sum_c =3D 0; + + for (size_t i =3D 0; i < in_size; i +=3D 3) { + const bool have_b =3D (i + 1) < in_size; + const bool have_c =3D (i + 2) < in_size; + + const uint8_t a2 =3D *in++; + const uint8_t b2 =3D have_b ? *in++ : 0; + const uint8_t c2 =3D have_c ? *in++ : 0; + + /* Per-group left-rotate on sum_c. */ + sum_c =3D (uint16_t)((sum_c & 0x00FFu) << 1); + if (sum_c >=3D 0x0100u) { + sum_c++; /* insert 1 on carry */ + } + + /* Add to sum_a; carry from sum_c bumps sum_a. */ + const uint8_t a =3D a2 ^ (uint8_t)sum_c; + sum_a +=3D a; + if (sum_c >=3D 0x0100u) { + sum_a++; + sum_c &=3D 0x00FFu; + } + + /* Add to sum_b; carry from sum_a bumps sum_b. */ + const uint8_t b =3D b2 ^ (uint8_t)sum_a; + if (have_b) { + sum_b +=3D b; + if (sum_a >=3D 0x0100u) { + sum_b++; + sum_a &=3D 0x00FFu; + } + } + + /* Add to sum_c; carry from sum_b bumps sum_c. */ + const uint8_t c =3D c2 ^ (uint8_t)sum_b; + if (have_c) { + sum_c +=3D c; + if (sum_b >=3D 0x0100u) { + sum_c++; + sum_b &=3D 0x00FFu; + } + } + + *out++ =3D a; + + if (have_b) { + *out++ =3D b; + } + + if (have_c) { + *out++ =3D c; + } + } + + if (sum) { + return sum[0] =3D=3D (uint8_t)sum_a && + sum[1] =3D=3D (uint8_t)sum_b && + sum[2] =3D=3D (uint8_t)sum_c; + } else { + return false; + } +} + +static bool sony_validate_address(SonyDrive *const drive, const uint8_t tr= ack, + const uint8_t head, const uint8_t sector) +{ + const uint8_t sectors =3D sony_drive_sectors_for_track(drive, track); + assert(sectors > 0); + + if (track >=3D drive->cylinders) { + return false; + } else if (head > 0 && !drive->double_sided) { + return false; + } else if (sector >=3D sectors) { + return false; + } else { + return true; + } +} + +static uint64_t sony_lba(SonyDrive *const drive, const uint8_t track, + const uint8_t head, const uint8_t sector) +{ + const uint8_t heads =3D drive->double_sided ? 2 : 1; + + if (drive->sectors_per_track) { + return ((uint64_t)track * heads + head) + * drive->sectors_per_track + sector; + } else { + uint64_t lba =3D 0; + + for (uint8_t i =3D 0; i < track; i++) { + const uint8_t sectors =3D sony_drive_sectors_for_track(drive, = i); + assert(sectors > 0); + lba +=3D sectors * heads; + } + + const uint8_t sectors =3D sony_drive_sectors_for_track(drive, trac= k); + assert(sectors > 0); + lba +=3D sectors * head; + + lba +=3D sector; + + return lba; + } +} + +static bool sony_drive_prepare_gcr(SonyDrive *const drive, const uint64_t = lba, + const uint8_t track, const uint8_t head, + const uint8_t sector) +{ + assert(head < 2); + const uint8_t format_byte =3D drive->double_sided ? 0x22 : 0x2; + + uint8_t header_vals[5]; + header_vals[0] =3D track & 0x3f; + header_vals[1] =3D sector; + header_vals[2] =3D (head << 5) | ((track >> 6) & 0x1f); + header_vals[3] =3D format_byte; + header_vals[4] =3D header_vals[0] ^ header_vals[1] ^ + header_vals[2] ^ header_vals[3]; + + uint8_t sector_data[SONY_GCR_SECTOR_LENGTH]; + size_t sector_data_pos =3D 0; + sector_data[sector_data_pos++] =3D track; + sector_data[sector_data_pos++] =3D head; + sector_data[sector_data_pos++] =3D sector; + sector_data[sector_data_pos++] =3D format_byte; + sector_data[sector_data_pos++] =3D 0x00; /* reserved */ + sector_data[sector_data_pos++] =3D 0x00; + sector_data[sector_data_pos++] =3D (lba >> 8) & 0xff; + sector_data[sector_data_pos++] =3D lba & 0xff; + sector_data[sector_data_pos++] =3D 0x00; + sector_data[sector_data_pos++] =3D 0x00; + sector_data[sector_data_pos++] =3D 0x00; + sector_data[sector_data_pos++] =3D 0x00; + + const int ret =3D blk_pread(drive->blk, lba * 512, SONY_GCR_DATA_LENGT= H, + sector_data + sector_data_pos, 0); + if (ret < 0) { + return false; + } + sector_data_pos +=3D SONY_GCR_DATA_LENGTH; + assert(sector_data_pos =3D=3D sizeof sector_data); + + /* Fill drive->xfer_buffer. */ + size_t idx =3D 0; + + /* + * Sync field. Note that we don't actually have to replicate the sync= bytes + * as on disk; we can just return what the IWM/SWIM would see. + * We use a six-byte field since that matches the size of what the CPU + * will write. + */ + memset(&drive->xfer_buffer[idx], 0xff, SONY_GCR_SYNC_LEN); + idx +=3D SONY_GCR_SYNC_LEN; + + drive->xfer_buffer[idx++] =3D 0xd5; + drive->xfer_buffer[idx++] =3D 0xaa; + drive->xfer_buffer[idx++] =3D 0x96; + + sony_gcr_encode_bytes(header_vals, &drive->xfer_buffer[idx], + sizeof header_vals); + idx +=3D sizeof header_vals; + + drive->xfer_buffer[idx++] =3D 0xde; + drive->xfer_buffer[idx++] =3D 0xaa; + + drive->xfer_write_position =3D idx; + + /* Sync field. */ + memset(&drive->xfer_buffer[idx], 0xff, SONY_GCR_SYNC_LEN); + idx +=3D SONY_GCR_SYNC_LEN; + + drive->xfer_buffer[idx++] =3D 0xd5; + drive->xfer_buffer[idx++] =3D 0xaa; + drive->xfer_buffer[idx++] =3D 0xad; + drive->xfer_buffer[idx++] =3D sony_gcr_encode_byte(sector); + + /* Sector data. */ + uint8_t sector_checksum[3]; + sony_checksum_encode(sector_data, sizeof sector_data, + sector_data, sizeof sector_data, sector_checksum); + sony_6and2_pack(sector_data, sizeof sector_data, + drive->xfer_buffer + idx, SONY_GCR_ENCODED_LENGTH); + sony_gcr_encode_bytes(drive->xfer_buffer + idx, + drive->xfer_buffer + idx, SONY_GCR_ENCODED_LENGT= H); + idx +=3D SONY_GCR_ENCODED_LENGTH; + + /* Encode and append checksum. */ + sony_6and2_pack(sector_checksum, sizeof sector_checksum, + drive->xfer_buffer + idx, 4); + sony_gcr_encode_bytes(drive->xfer_buffer + idx, + drive->xfer_buffer + idx, 4); + idx +=3D 4; + + /* Trail marks. */ + drive->xfer_buffer[idx++] =3D 0xde; + drive->xfer_buffer[idx++] =3D 0xaa; + + /* Add some empty space; the Mac driver overwrites this area. */ + drive->xfer_buffer[idx++] =3D 0xff; + drive->xfer_buffer[idx++] =3D 0xff; + drive->xfer_buffer[idx++] =3D 0xff; + drive->xfer_buffer[idx++] =3D 0xff; + + drive->xfer_length =3D idx; + drive->xfer_position =3D 0; + drive->xfer_active =3D true; + + return true; +} + +static bool sony_drive_persist_gcr(SonyDrive *const drive) +{ + uint8_t encoded_data[SONY_GCR_ENCODED_LENGTH]; + uint8_t sector_data[SONY_GCR_SECTOR_LENGTH]; + + uint8_t *const encoded_buffer =3D drive->xfer_buffer + SONY_GCR_SYNC_L= EN + 3 + + 5 + 2 + SONY_GCR_SYNC_LEN + 4; + sony_gcr_decode_bytes(encoded_buffer, encoded_data, + SONY_GCR_ENCODED_LENGTH); + sony_6and2_unpack(encoded_data, sizeof encoded_data, + sector_data, sizeof sector_data); + + uint8_t encoded_checksum[4]; + uint8_t checksum[3]; + sony_gcr_decode_bytes(encoded_buffer + SONY_GCR_ENCODED_LENGTH, + encoded_checksum, 4); + sony_6and2_unpack(encoded_checksum, sizeof encoded_checksum, + checksum, sizeof checksum); + const bool result =3D sony_checksum_decode(sector_data, sizeof sector_= data, + sector_data, sizeof sector_da= ta, + checksum); + + if (!result) { + return false; + } else { + const int ret =3D blk_pwrite(drive->blk, drive->xfer_lba * 512, + SONY_GCR_DATA_LENGTH, + §or_data[SONY_GCR_TAG_LENGTH], 0); + return ret >=3D 0; + } +} + +static bool sony_drive_prepare_mfm(SonyDrive *const drive, const uint64_t = lba, + const uint8_t track, const uint8_t head, + const uint8_t sector) +{ + size_t idx =3D 0; + + /* Sync field. */ + memset(drive->xfer_buffer + idx, 0x00, 12); + idx +=3D 12; + + /* Address field. */ + bitset_set_range(drive->xfer_mark_bitset, idx, 3, true); + drive->xfer_buffer[idx++] =3D 0xA1; + drive->xfer_buffer[idx++] =3D 0xA1; + drive->xfer_buffer[idx++] =3D 0xA1; + + drive->xfer_buffer[idx++] =3D 0xFE; + drive->xfer_buffer[idx++] =3D track; + drive->xfer_buffer[idx++] =3D head; + drive->xfer_buffer[idx++] =3D sector + 1; /* NOTE: 1-indexed, unlike G= CR! */ + drive->xfer_buffer[idx++] =3D 0x02; /* 512 bytes/sector */ + drive->xfer_buffer[idx++] =3D 0x00; /* CRC (unimplemented) */ + drive->xfer_buffer[idx++] =3D 0x00; /* CRC (unimplemented) */ + + /* Intra-sector gap. */ + memset(drive->xfer_buffer + idx, 0x4E, 22); + idx +=3D 22; + + /* Sync field. */ + memset(drive->xfer_buffer + idx, 0x00, 12); + idx +=3D 12; + + /* Data mark. */ + bitset_set_range(drive->xfer_mark_bitset, idx, 3, true); + drive->xfer_buffer[idx++] =3D 0xA1; + drive->xfer_buffer[idx++] =3D 0xA1; + drive->xfer_buffer[idx++] =3D 0xA1; + + drive->xfer_write_position =3D idx; + drive->xfer_buffer[idx++] =3D 0xFB; + + /* Data. */ + const int ret =3D blk_pread(drive->blk, lba * 512, SONY_MFM_SECTOR_LEN= GTH, + drive->xfer_buffer + idx, 0); + if (ret < 0) { + return false; + } + idx +=3D SONY_MFM_SECTOR_LENGTH; + + drive->xfer_buffer[idx++] =3D 0; /* CRC (unimplemented) */ + drive->xfer_buffer[idx++] =3D 0; /* CRC (unimplemented) */ + + /* Inter-sector gap. */ + memset(drive->xfer_buffer + idx, 0x4E, 101); + idx +=3D 101; + + drive->xfer_length =3D idx; + drive->xfer_position =3D 0; + drive->xfer_active =3D true; + + return true; +} + +static bool sony_drive_persist_mfm(SonyDrive *const drive) +{ + const size_t offset =3D 12 + 10 + 22 + 12 + 4; + const int ret =3D blk_pwrite(drive->blk, drive->xfer_lba * 512, + SONY_MFM_SECTOR_LENGTH, + drive->xfer_buffer + offset, 0); + return ret >=3D 0; +} + +static void sony_drive_transfer_reset(SonyDrive *const drive) +{ + /* First, persist any dirty xfer. */ + if (drive->xfer_dirty) { + bool result; + + if (drive->gcr_encode) { + result =3D sony_drive_persist_gcr(drive); + } else { + result =3D sony_drive_persist_mfm(drive); + } + + assert(result); + trace_sony_drive_persist(drive, drive->xfer_lba); + } + + drive->xfer_active =3D false; + drive->xfer_dirty =3D false; + drive->xfer_lba =3D UINT64_MAX; + drive->xfer_position =3D 0; + drive->xfer_write_position =3D 0; + drive->xfer_length =3D 0; + memset(drive->xfer_buffer, 0, sizeof drive->xfer_buffer); + memset(drive->xfer_mark_bitset, 0, sizeof drive->xfer_mark_bitset); +} + +static bool sony_drive_prepare(SonyDrive *const drive) +{ + if (!drive->disk_in) { + trace_sony_drive_prepare(drive, 0, 0, 0, 0, 0); + return false; + } + + sony_drive_transfer_reset(drive); + + const uint8_t sectors =3D sony_drive_sectors_for_track(drive, + drive->current_tr= ack); + const uint8_t head =3D drive->sel ? 1 : 0; + const uint8_t track =3D drive->current_track; + + assert(head =3D=3D 0 || drive->double_sided); + assert(sectors !=3D 0); + + const uint8_t sector =3D drive->current_sector; + drive->current_sector =3D (drive->current_sector + 1) % sectors; + assert(sony_validate_address(drive, track, head, sector)); + + const uint64_t lba =3D sony_lba(drive, track, head, sector); + assert(lba < drive->total_sectors); + + bool result; + if (drive->gcr_encode) { + result =3D sony_drive_prepare_gcr(drive, lba, track, head, sector); + } else { + result =3D sony_drive_prepare_mfm(drive, lba, track, head, sector); + } + + drive->xfer_lba =3D lba; + trace_sony_drive_prepare(drive, track, head, sector, drive->gcr_encode, + drive->xfer_length); + return result; +} + +static uint8_t sony_drive_selected_drive_reg(const SonyDrive *const drive) +{ + uint8_t selector =3D 0; + + if (drive->phases & 0x4) { /* CA2 */ + selector |=3D 0x8; + } + if (drive->phases & 0x2) { /* CA1 */ + selector |=3D 0x4; + } + if (drive->phases & 0x1) { /* CA0 */ + selector |=3D 0x2; + } + if (drive->sel) { + selector |=3D 0x1; + } + + return selector; +} + +static void sony_drive_step(SonyDrive *const drive) +{ + if (drive->seek_direction < 0) { + if (drive->current_track > 0) { + drive->current_track--; + } + } else { + if (!drive->cylinders || drive->current_track + 1 < drive->cylinde= rs) { + drive->current_track++; + } + } + + drive->current_sector =3D 0; + sony_drive_transfer_reset(drive); + trace_sony_drive_step(drive, drive->seek_direction, drive->current_tra= ck); +} + +static void sony_drive_strobe_drive(SonyDrive *const drive) +{ + const uint8_t selector =3D sony_drive_selected_drive_reg(drive); + trace_sony_drive_strobe(drive, selector, sony_drive_cmd_names[selector= ]); + + switch (selector) { + case SONY_CMD_TRACK_POS: + drive->seek_direction =3D 1; + break; + case SONY_CMD_TRACK_NEG: + drive->seek_direction =3D -1; + break; + case SONY_CMD_TRACK_STEP: + sony_drive_step(drive); + break; + case SONY_CMD_SET_MFM: + drive->use_gcr =3D false; + break; + case SONY_CMD_SET_GCR: + drive->use_gcr =3D true; + break; + case SONY_CMD_MOTOR_ON: + drive->motor_on =3D true; + break; + case SONY_CMD_MOTOR_OFF: + drive->motor_on =3D false; + sony_drive_transfer_reset(drive); + break; + case SONY_CMD_EJECT: + blk_eject(drive->blk, true); + drive->disk_in =3D false; + sony_drive_reset(drive); + break; + default: + break; + } +} + +bool sony_drive_read_sense(SonyDrive *const drive) +{ + const uint8_t reg =3D sony_drive_selected_drive_reg(drive); + bool value; + + switch (reg) { + case SONY_REG_DIRTN: + /* "Direction". */ + value =3D drive->seek_direction < 0; + break; + case SONY_REG_CSTIN: + /* "Cassette in". */ + value =3D !drive->disk_in; /* sense inverted */ + break; + case SONY_REG_STEP: + value =3D true; /* not emulated; our seeks are instantaneous */ + break; + case SONY_REG_WRTPRT: + value =3D !drive->write_protected; /* sense inverted */ + break; + case SONY_REG_MOTORON: + value =3D !drive->motor_on; /* sense inverted */ + break; + case SONY_REG_TKO: + /* "Track zero". */ + value =3D drive->current_track !=3D 0; /* sense inverted */ + break; + case SONY_REG_EJECT_L: + /* Not pressing eject switch. */ + value =3D false; + break; + case SONY_REG_TACH: + /* Tacho functionality not emulated. */ + value =3D !drive->motor_on; + break; + case SONY_REG_RDDATA0: + case SONY_REG_RDDATA1: + /* Single-bit reads are not emulated. */ + value =3D true; + break; + case SONY_REG_SUPERDRIVE: + value =3D true; + break; + case SONY_REG_GCR: + value =3D !drive->use_gcr; /* sense inverted */ + break; + case SONY_REG_SINGLE_SIDE: + /* We are a double-sided drive. */ + value =3D true; /* sense inverted */ + break; + case SONY_REG_READY: + value =3D !(drive->disk_in && drive->motor_on); /* sense inverted = */ + break; + case SONY_REG_INSTALLED: + value =3D false; /* sense inverted */ + break; + case SONY_REG_TWOMEG: + /* "Two-meg" refers to the unformatted size of HD MFM floppies. */ + value =3D !(drive->hd_media && drive->disk_in); /* sense inverted = */ + break; + default: + value =3D true; + break; + } + + trace_sony_drive_read_sense(drive, reg, sony_drive_reg_names[reg], val= ue); + return value; +} + +void sony_drive_set_inputs(SonyDrive *const drive, const uint8_t phases, + const bool sel, const bool enabled) +{ + trace_sony_drive_set_inputs(drive, phases, sel, enabled); + const bool prev_strobe =3D (drive->phases & 0x8) !=3D 0 && drive->enab= led; + const bool cur_strobe =3D (phases & 0x8) !=3D 0 && enabled; + + drive->phases =3D phases; + drive->sel =3D sel; + drive->enabled =3D enabled; + + if (!prev_strobe && cur_strobe) { + sony_drive_strobe_drive(drive); + } +} + +bool sony_drive_read_byte(SonyDrive *const drive, + uint8_t *const value_out, bool *const is_mark_ou= t) +{ + if (!drive->xfer_active || drive->xfer_position >=3D drive->xfer_lengt= h) { + sony_drive_prepare(drive); + } + + if (!drive->xfer_active) { + return false; + } else { + const uint16_t offset =3D drive->xfer_position; + drive->xfer_position++; + const uint8_t value =3D drive->xfer_buffer[offset]; + const bool is_mark =3D bitset_get(drive->xfer_mark_bitset, offset); + + if (drive->gcr_encode) { + assert(value & 0x80); + } + + *value_out =3D value; + *is_mark_out =3D is_mark; + + trace_sony_drive_read_byte(drive, value, is_mark, + drive->xfer_lba, offset); + return true; + } +} + +bool sony_drive_write_byte(SonyDrive *const drive, const uint8_t value) +{ + if (drive->write_protected) { + return false; + } + + /* + * NOTE: for now, we should't get a write unless we have an active sec= tor. + * We'll have to revisit this to implement duplicating/formatting. + */ + assert(drive->xfer_active); + + if (!drive->xfer_active) { + return false; + } else { + const uint16_t offset =3D drive->xfer_write_position; + assert(offset < drive->xfer_length); + + drive->xfer_buffer[offset] =3D value; + drive->xfer_write_position++; + drive->xfer_dirty =3D true; + + trace_sony_drive_write_byte(drive, value, drive->xfer_lba, offset); + return true; + } +} + +void sony_drive_set_block_backend(SonyDrive *const drive, + BlockBackend *const block) +{ + sony_drive_transfer_reset(drive); + drive->blk =3D block; + drive->disk_in =3D blk_is_inserted(block); + sony_drive_reset(drive); +} + +void sony_drive_reset(SonyDrive *const drive) +{ + sony_drive_transfer_reset(drive); + + drive->motor_on =3D false; + drive->write_protected =3D false; + drive->hd_media =3D false; + drive->double_sided =3D false; + drive->use_gcr =3D true; + drive->gcr_encode =3D true; + drive->cylinders =3D 0; + drive->sectors_per_track =3D 0; + drive->total_sectors =3D 0; + drive->current_track =3D 0; + drive->current_sector =3D 0; + drive->seek_direction =3D 1; + + if (drive->disk_in) { + const int64_t length =3D blk_getlength(drive->blk); + + if (length <=3D 0 || length % 512 !=3D 0) { + warn_report_once("%s: unsupported floppy size %" PRId64, + __func__, length); + drive->disk_in =3D false; + } else { + drive->write_protected =3D !blk_is_writable(drive->blk); + drive->total_sectors =3D length / 512; + + if (drive->total_sectors =3D=3D 1600) { + /* 800 kB (double-sided GCR). */ + drive->cylinders =3D 80; + drive->double_sided =3D true; + drive->gcr_encode =3D true; + } else if (drive->total_sectors =3D=3D 800) { + /* 400 kB (single-sided GCR). */ + drive->cylinders =3D 80; + drive->double_sided =3D false; + drive->gcr_encode =3D true; + } else if (drive->total_sectors =3D=3D 2880) { + /* 1440 kB (double-sided MFM). */ + drive->cylinders =3D 80; + drive->sectors_per_track =3D 18; + drive->hd_media =3D true; + drive->double_sided =3D true; + drive->gcr_encode =3D false; + } else { + warn_report_once("%s: unable to determine geometry for med= ia" + " (%" PRId64 " bytes)", __func__, length); + drive->disk_in =3D false; + } + } + } +} diff --git a/hw/block/trace-events b/hw/block/trace-events index cc9a9f2460..c9eb62bcaa 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -101,3 +101,13 @@ swim_iwmctrl_read(int reg, const char *name, unsigned = size, uint64_t value) "reg swim_iwmctrl_write(int reg, const char *name, unsigned size, uint64_t valu= e) "reg=3D%d [%s] size=3D%u value=3D0x%"PRIx64 swim_switch_to_ism(void) "switch from IWM to ISM mode" swim_switch_to_iwm(void) "switch from ISM to IWM mode" + +# sony_superdrive.c +sony_drive_prepare(void *drive, uint8_t track, uint8_t head, uint8_t secto= r, bool gcr, uint16_t payload_len) "drive=3D%p track=3D%"PRIu8" head=3D%"PR= Iu8" sector=3D%"PRIu8" gcr=3D%d length=3D%"PRIu16 +sony_drive_persist(void *drive, uint64_t lba) "drive=3D%p lba=3D0x%"PRIx64 +sony_drive_strobe(void *drive, uint8_t command, const char *name) "drive= =3D%p cmd=3D0x%02"PRIx8" [%s]" +sony_drive_read_sense(void *drive, uint8_t reg, const char *name, bool val= ue) "drive=3D%p reg=3D0x%02"PRIx8" [%s] value=3D%d" +sony_drive_set_inputs(void *drive, uint8_t phases, bool sel, bool enabled)= "drive=3D%p phases=3D0x%"PRIx8" sel=3D%d enabled=3D%d" +sony_drive_read_byte(void *drive, uint8_t value, bool is_mark, uint64_t lb= a, uint16_t position) "drive=3D%p value=3D0x%02"PRIx8", is_mark=3D%d, lba= =3D0x%"PRIx64", position=3D0x%"PRIx16 +sony_drive_write_byte(void *drive, uint8_t value, uint64_t lba, uint16_t p= osition) "drive=3D%p value=3D0x%02"PRIx8", lba=3D0x%"PRIx64", position=3D0x= %"PRIx16 +sony_drive_step(void *drive, uint8_t seek_direction, uint8_t current_track= ) "drive=3D%p direction=3D%"PRId8", track=3D%"PRId8 diff --git a/include/hw/block/sony_superdrive.h b/include/hw/block/sony_sup= erdrive.h new file mode 100644 index 0000000000..a64bb06a97 --- /dev/null +++ b/include/hw/block/sony_superdrive.h @@ -0,0 +1,56 @@ +/* + * QEMU Macintosh SuperDrive floppy disk drive emulator + * + * Copyright (c) 2025 Matt Jacobson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_BLOCK_SWIM_SONY_H +#define HW_BLOCK_SWIM_SONY_H + +#include +#include +#include "hw/block/block.h" + +typedef struct { + BlockBackend *blk; + + uint8_t phases; + bool sel; + bool enabled; + + bool motor_on; + bool disk_in; + bool write_protected; + bool hd_media; + bool double_sided; + bool use_gcr; + bool gcr_encode; + uint8_t cylinders; + uint8_t sectors_per_track; + uint8_t current_track; + uint8_t current_sector; + int8_t seek_direction; + uint32_t total_sectors; + + bool xfer_active; + bool xfer_dirty; + uint64_t xfer_lba; + uint16_t xfer_position; + uint16_t xfer_write_position; + uint16_t xfer_length; + uint8_t xfer_buffer[1024]; + uint8_t xfer_mark_bitset[1024 / 8]; +} SonyDrive; + +void sony_drive_set_block_backend(SonyDrive *drive, BlockBackend *block); +void sony_drive_reset(SonyDrive *drive); +bool sony_drive_read_sense(SonyDrive *drive); +void sony_drive_set_inputs(SonyDrive *drive, uint8_t phases, + bool sel, bool enabled); +bool sony_drive_read_byte(SonyDrive *drive, + uint8_t *value_out, bool *is_mark_out); +bool sony_drive_write_byte(SonyDrive *const drive, const uint8_t value); + +#endif /* HW_BLOCK_SWIM_SONY_H */ --=20 2.53.0 From nobody Tue Jun 30 03:39:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=me.com ARC-Seal: i=1; a=rsa-sha256; t=1781740326; cv=none; d=zohomail.com; s=zohoarc; b=SsEqGA4xnYC3+CV/tyLa3jIkGUmYI/fziT19il3IyPnGeNkQhd1nN4nRVP0rLqVDtcwLSCIRlpC+qwsbT6evX+Kg0qsTZZYlmkAYAuYD1KGDoU+cmhnj8iPEeqRWFkWluBtWNneYD5ecW95s6aeyt758Y8ySB8/QBwuDbSs55aI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781740326; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=96OjZZvnL7ZcQJPP9u2rImRmMAgyB1VN3+HAPMO28JA=; b=VDO03Z6a4oa8IJ/nS7CqNpedVLxM71SxHs3ZIktSgOOJBelcPz3LaUtZBqyqrpv/265HYvhMnhpu/jYd8rfk533nWjr5nPeONsDOoraTy5J16etGsKzEKKMosw26nYkCEk5UDOavxFChjgEVpQghja3O5W1erv9JWWLqMHPt3P4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781740326291778.5536603230862; Wed, 17 Jun 2026 16:52:06 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wa01z-0004OF-40; Wed, 17 Jun 2026 19:50:51 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wa01w-0004NK-Hu for qemu-devel@nongnu.org; Wed, 17 Jun 2026 19:50:48 -0400 Received: from p-east2-cluster1-host6-snip4-10.eps.apple.com ([57.103.76.103] helo=outbound.st.icloud.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wa01u-0003tZ-0A for qemu-devel@nongnu.org; Wed, 17 Jun 2026 19:50:47 -0400 Received: from outbound.st.icloud.com (unknown [127.0.0.2]) by p00-icloudmta-asmtp-us-east-1a-60-percent-11 (Postfix) with ESMTPS id C30E71800224; Wed, 17 Jun 2026 23:50:40 +0000 (UTC) Received: from localhost.localdomain (unknown [17.42.251.67]) by p00-icloudmta-asmtp-us-east-1a-60-percent-11 (Postfix) with ESMTPSA id 17C381800208; Wed, 17 Jun 2026 23:50:40 +0000 (UTC) X-ICL-Out-Info: HUtFAUMHWwJACUgBTUQeDx5WFlZNRAJCTQ5JHVoHRQNGD1YCWgZLVxQEFVsHVghdEkoXXS1aDhwTVhUTC1NWXxUXG1wAFxlRTQ5YWwhbBA8fTAxRAkIFVl5KDB0EVAddBV1WUAJaS0IES0VoXAVcHEAXSB1faktWFAQVVkNUBF9QVBFXUAtZAkIPSAVcAFkBRQlLAUMFWgZJCFUAQFoDWBFaF1EeWAVyHVxWUAJaVRIEQAhWUF4IXh9MHA== Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=me.com; s=1a1hai; t=1781740242; x=1784332242; bh=96OjZZvnL7ZcQJPP9u2rImRmMAgyB1VN3+HAPMO28JA=; h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme; b=N2SkjMtYlWyVid5HQWZxe8H2gZKhmxkSkOzh0pHjAzX4OXkFellAvTLckyA/FTC+hw6EOhOEUL1uZjROJR/5uyWDGhgt9Y7SBtS3tBR0Ext1mJnosEnRA261HulWmBz99zJbQj/x/1N6HAKZeTNUI6pvWu0TRO76tkS6oWyUrjRMiEIg28LllOnsPNOSABNO+pyM+/eQ8oUfcImD/uYJI7FsX4yFktqDo4tLGnT12Mo4DfkfF3sKhCAi5d5KtFmau4LIz7xOpf9w/rFy3cIwtXvMFFPgC2dqNpATrQoqrOzO5a2AzjQGf0SHVHO3eVhy/wKxTlQb0y779RB2Ix/1XA== From: Matt Jacobson To: qemu-devel@nongnu.org Cc: Kevin Wolf , Hanna Reitz , qemu-block@nongnu.org, Mark Cave-Ayland , Laurent Vivier , Matt Jacobson Subject: [PATCH v2 2/5] fifo32: add fifo32_peek() Date: Wed, 17 Jun 2026 19:50:29 -0400 Message-ID: <20260617235032.21491-3-mhjacobson@me.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260617235032.21491-1-mhjacobson@me.com> References: <20260617235032.21491-1-mhjacobson@me.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-ORIG-GUID: 2erAHf-X_CM3qM4e1e57s3nKrFC_By6W X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNjE3MDIyOCBTYWx0ZWRfX3WXjuXyrUWDW DZtfQSO4Zu+Qy1e2w3/jfVWRcR5t677uqAmZAZ1mtzHrffcl/ESQtdgbaggDqVOiulJTIuravnG 54EuB8pse4GCmst6CMyiIl78Vd/U6ra/piEWRCbMneCXQcyWNr0A9frz/RebW8O/nf1Rk/ZAXfR x+fJblEdmZMlSAS1qeVdFRuw9toCMN1XFcMdn3+yosBb46Wb+GuppcMjHctUt8jo0897vtTQ9pv vbdUVLGiWaoNWnCu7UHLobmMR7AMdSIQN/LTxlqGG7vc3HppFQFZcxHCkOUCl5VTiK8Mh7vHCQp OEJOSHcvwq2kQpi4zIE X-Proofpoint-GUID: 2erAHf-X_CM3qM4e1e57s3nKrFC_By6W Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=57.103.76.103; envelope-from=mhjacobson@me.com; helo=outbound.st.icloud.com X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @me.com) X-ZM-MESSAGEID: 1781740327493158500 Content-Type: text/plain; charset="utf-8" Add fifo32_peek(), which, like the other Fifo32 functions, ultimately just delegates to the Fifo8 code. Signed-off-by: Matt Jacobson --- include/qemu/fifo32.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/qemu/fifo32.h b/include/qemu/fifo32.h index 4e9fd1b5ef..7601bcfbfe 100644 --- a/include/qemu/fifo32.h +++ b/include/qemu/fifo32.h @@ -117,6 +117,31 @@ static inline void fifo32_push_all(Fifo32 *fifo, const= uint32_t *data, } } =20 +/** + * fifo32_peek: + * @fifo: fifo to peek from + * + * Peek the data word at the current head of the FIFO. Clients are respons= ible + * for checking for emptiness using fifo32_is_empty(). + * + * Returns: The peeked 32 bits data word. + */ + +static inline uint32_t fifo32_peek(Fifo32 *fifo) +{ + uint8_t bytes[sizeof(uint32_t)]; + uint32_t ret =3D 0; + int i; + + fifo8_peek_buf(&fifo->fifo, bytes, sizeof bytes); + + for (i =3D 0; i < sizeof(uint32_t); i++) { + ret |=3D (uint32_t)bytes[i] << (i * 8); + } + + return ret; +} + /** * fifo32_pop: * @fifo: fifo to pop from --=20 2.53.0 From nobody Tue Jun 30 03:39:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=me.com ARC-Seal: i=1; a=rsa-sha256; t=1781740336; cv=none; d=zohomail.com; s=zohoarc; b=BPJPWAZtTkmuifov2VL7KD5X2yH/UZGrNWITtrlZmTWZeCUXkhx3aX9o5Ok/0ZwoseLRz7uEMfUHXLzXkbPpFmAOZ6+gex1Ho9TuBUtGIsqQw31k9u2X19nCyY+EJLcW2qHRWv76IMsXcnWghO65qHvrLzzyizX+TShpBE1fFC8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781740336; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=vqknWSCXo8rTtStPNXeiJWu6Ye6zpoLG396fQ+u8jtI=; b=oKq9Fw977vC5YBBlR03S4iFNaJQrtOufai+uyEX3oQlOIf7fuIjqzQIVWTDD00p4Eq6lI1Mtc09mnUJbs27iNzxDbg0qYLg371yszzw3WgRx4MLt93hRk88ymZEDtKaCGwfgCCB3ovnYOYEI8sHQ7NngBYC8eYOAnbD8w0T6vIU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781740336443603.1909965367662; Wed, 17 Jun 2026 16:52:16 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wa02A-0004R3-PE; Wed, 17 Jun 2026 19:51:02 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wa022-0004Pd-Pl for qemu-devel@nongnu.org; Wed, 17 Jun 2026 19:50:54 -0400 Received: from p-east2-cluster1-host1-snip4-1.eps.apple.com ([57.103.76.24] helo=outbound.st.icloud.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wa01x-0003vD-Sj for qemu-devel@nongnu.org; Wed, 17 Jun 2026 19:50:54 -0400 Received: from outbound.st.icloud.com (unknown [127.0.0.2]) by p00-icloudmta-asmtp-us-east-1a-60-percent-11 (Postfix) with ESMTPS id BDAF41800CD7; Wed, 17 Jun 2026 23:50:41 +0000 (UTC) Received: from localhost.localdomain (unknown [17.42.251.67]) by p00-icloudmta-asmtp-us-east-1a-60-percent-11 (Postfix) with ESMTPSA id 9AFA41800150; Wed, 17 Jun 2026 23:50:40 +0000 (UTC) X-ICL-Out-Info: HUtFAUMHWwJACUgBTUQeDx5WFlZNRAJCTQ5JHVoHRQNGD1YCWgZLVxQEFVsHVghdEkoXXS1aDhwTVhUTC1NWXxUXG1wAFxlRTQ5YWwhbBA8fTAxRAkIFVl5KDB0EVAddBV1WUAJaS0IES0VoXAVcHEAXSB1faktWFAQVVkNUBF9QVBFXUAtZAkIPSAVcAFkBRQlLAUMFWgZJCFUHQFoDWBFaF1EeWAVyHVxWUAJaVRIEQAhWUF4IXh9MHA== Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=me.com; s=1a1hai; t=1781740244; x=1784332244; bh=vqknWSCXo8rTtStPNXeiJWu6Ye6zpoLG396fQ+u8jtI=; h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme; b=MQvWCVR9BLiw7kGJq1jK3jWe5/LfBF2v6vvmBRktTyPOP1/n3TSYcJZom7nFQvpUsBYCORmSppFLpHS9kvRdPYhVXeZnsudtIR6mxrGNr5t8iUYmGLAt8pGyOs1t0gI5LRe0hmlnAcrikY2qzWAHD3bTJGzOPkwPf4N6gtYzndBkh0zpB0F+Qo+DTIgENkrY1aR+z2uFfV45Uqwkg++KZvNXGstB5U6eJDYSr9Xdg6VB7kCKFS0DEnaqgcYkgL/a4seqi1bUPCCxfds62m6wGeFH9AVENdoiiTA+LjfGAj5u5qmYs3JtrfeCNzXQ8cS7s52FCuXWt2OHFKyNmHUyDw== From: Matt Jacobson To: qemu-devel@nongnu.org Cc: Kevin Wolf , Hanna Reitz , qemu-block@nongnu.org, Mark Cave-Ayland , Laurent Vivier , Matt Jacobson Subject: [PATCH v2 3/5] hw/block: add SWIM2 floppy controller for q800 Date: Wed, 17 Jun 2026 19:50:30 -0400 Message-ID: <20260617235032.21491-4-mhjacobson@me.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260617235032.21491-1-mhjacobson@me.com> References: <20260617235032.21491-1-mhjacobson@me.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-ORIG-GUID: xLjML2NAr8GdfDEJqd6xDHAKPhJ7hr8v X-Proofpoint-GUID: xLjML2NAr8GdfDEJqd6xDHAKPhJ7hr8v X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNjE3MDIyOCBTYWx0ZWRfX5ndMIWmrTU+o uioEamMQx0YmO89e7NgFEeThMHbwGncRWqRYHLHqsuEWuABf/0ZJsTsROhit7QXI70dO7bS/D0R rEqqpwixeG1hjPpS+uCHCjx7BYq8HxfZtWafzhGCRL/Z2tkv1eVeJCuuQeDMfd6T7zi8bnvQj+K Sn34MBQxWBOp3tgL8gZGod2oOfXSuwhkj4dDTuwgH2hXBizIe6XJystHh2m8d0VdoZbQs3NS3H4 ISLAeyX6No5SSlIx9Yd9u4Oky+BKIXNWAp0pvOK8U77GdoWKeoT1bd6PSvj/D615RBxXzskp9Lk I4N/65ruSnHtndTut0M Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=57.103.76.24; envelope-from=mhjacobson@me.com; helo=outbound.st.icloud.com X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @me.com) X-ZM-MESSAGEID: 1781740337365158500 Content-Type: text/plain; charset="utf-8" The Quadra 800 contains a SWIM2 floppy controller, which is a simplified revision of the original SWIM controller. Implement it based on publicly available specs; a future commit will connect it to q800. Signed-off-by: Matt Jacobson --- hw/block/meson.build | 2 +- hw/block/swim2.c | 722 +++++++++++++++++++++++++++++++++++++++ hw/block/trace-events | 9 + include/hw/block/swim2.h | 60 ++++ 4 files changed, 792 insertions(+), 1 deletion(-) create mode 100644 hw/block/swim2.c create mode 100644 include/hw/block/swim2.h diff --git a/hw/block/meson.build b/hw/block/meson.build index c4b873a906..733e9ffb47 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -10,7 +10,7 @@ system_ss.add(when: 'CONFIG_PFLASH_CFI01', if_true: files= ('pflash_cfi01.c')) system_ss.add(when: 'CONFIG_PFLASH_CFI02', if_true: files('pflash_cfi02.c'= )) system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80.c')) system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80_sfdp.c')) -system_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c', 'sony_superdri= ve.c')) +system_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c', 'swim2.c', 'so= ny_superdrive.c')) system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-block.c')) =20 system_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c')) diff --git a/hw/block/swim2.c b/hw/block/swim2.c new file mode 100644 index 0000000000..f10be40678 --- /dev/null +++ b/hw/block/swim2.c @@ -0,0 +1,722 @@ +/* + * QEMU Macintosh floppy disk controller emulator (SWIM2) + * + * Copyright (c) 2025 Matt Jacobson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include +#include +#include +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/qdev.h" +#include "hw/core/sysbus.h" +#include "hw/block/sony_superdrive.h" +#include "hw/block/swim2.h" +#include "system/block-backend.h" +#include "system/blockdev.h" +#include "trace.h" + +#define SWIM2_MMIO_SIZE 0x2000 +#define SWIM2_REG_SHIFT 9 + +#define SWIM2_FIFO_FLAG_MARK 0x01 + +#define SWIM2_SETUP_INVERT_WRDATA BIT(0) +#define SWIM2_SETUP_3_5_OUTPUT BIT(1) +#define SWIM2_SETUP_GCR_MODE BIT(2) +#define SWIM2_SETUP_CLOCK_DIV_2 BIT(3) +#define SWIM2_SETUP_TEST_MODE BIT(4) +#define SWIM2_SETUP_IBM_DATA_MODE BIT(5) +#define SWIM2_SETUP_GCR_WRITES BIT(6) +#define SWIM2_SETUP_RESERVED BIT(7) + +#define SWIM2_MODE_CLR_FIFO BIT(0) +#define SWIM2_MODE_ENBL1 BIT(1) +#define SWIM2_MODE_ENBL2 BIT(2) +#define SWIM2_MODE_ACTION BIT(3) +#define SWIM2_MODE_WRITE BIT(4) +#define SWIM2_MODE_SIDE BIT(5) +#define SWIM2_MODE_ALWAYS1 BIT(6) +#define SWIM2_MODE_MOTORON BIT(7) + +#define SWIM2_ERROR_UNDERRUN BIT(0) +#define SWIM2_ERROR_MARK_IN_DATA BIT(1) +#define SWIM2_ERROR_OVERRUN BIT(2) +#define SWIM2_ERROR_SHORT BIT(3) +#define SWIM2_ERROR_LONG BIT(4) + +/* + * These are shifted versions of the "SWIM offsets" in HardwareEqu.a in the + * System 7.1 source. + */ +typedef enum { + SWIM2_REG_DATA =3D 0, + SWIM2_REG_MARK =3D 1, + SWIM2_REG_ERROR =3D 2, + SWIM2_REG_PARAMETER =3D 3, + SWIM2_REG_PHASE =3D 4, + SWIM2_REG_SETUP =3D 5, + SWIM2_REG_WRITE_ZEROES =3D 6, + SWIM2_REG_WRITE_ONES =3D 7, +} SWIM2Register; + +#define SWIM2_REG_WRITE_CRC SWIM2_REG_ERROR +#define SWIM2_REG_STATUS SWIM2_REG_WRITE_ZEROES +#define SWIM2_REG_HANDSHAKE SWIM2_REG_WRITE_ONES + +static const char *const swim2_read_reg_names[] =3D { + [SWIM2_REG_DATA] =3D "DATA", + [SWIM2_REG_MARK] =3D "MARK", + [SWIM2_REG_ERROR] =3D "ERROR", + [SWIM2_REG_PARAMETER] =3D "PARAM", + [SWIM2_REG_PHASE] =3D "PHASE", + [SWIM2_REG_SETUP] =3D "SETUP", + [SWIM2_REG_STATUS] =3D "STATUS", + [SWIM2_REG_HANDSHAKE] =3D "HANDSHAKE", +}; + +static const char *const swim2_write_reg_names[] =3D { + [SWIM2_REG_DATA] =3D "DATA", + [SWIM2_REG_MARK] =3D "MARK", + [SWIM2_REG_WRITE_CRC] =3D "WRITE_CRC", + [SWIM2_REG_PARAMETER] =3D "PARAM", + [SWIM2_REG_PHASE] =3D "PHASE", + [SWIM2_REG_SETUP] =3D "SETUP", + [SWIM2_REG_WRITE_ZEROES] =3D "WRITE0", + [SWIM2_REG_WRITE_ONES] =3D "WRITE1", +}; + +static inline const char *swim2_reg_name(const uint8_t reg, + const bool write) +{ + assert(reg < 8); + const char *const *table =3D write ? swim2_write_reg_names : + swim2_read_reg_names; + return table[reg]; +} + +static uint32_t swim2_fifo_pack(const uint8_t data, const bool is_mark) +{ + return data | (is_mark ? BIT(8) : 0); +} + +static uint8_t swim2_fifo_data(const uint32_t entry) +{ + return entry & 0xff; +} + +static bool swim2_fifo_is_mark(const uint32_t entry) +{ + return (entry & BIT(8)) !=3D 0; +} + +static void swim2_set_error(SWIM2State *const ctrl, const uint8_t err) +{ + const uint8_t prev =3D ctrl->error_reg; + ctrl->error_reg |=3D err; + trace_swim2_error_set(prev, ctrl->error_reg, err, ctrl->mode_reg, + (ctrl->mode_reg & SWIM2_MODE_WRITE) !=3D 0, + fifo32_num_used(&ctrl->fifo)); +} + +static void swim2_fifo_clear(SWIM2State *const ctrl) +{ + fifo32_reset(&ctrl->fifo); + trace_swim2_fifo_clear(); +} + +static bool swim2_fifo_push(SWIM2State *const ctrl, const uint8_t data, + const bool is_mark) +{ + if (fifo32_is_full(&ctrl->fifo)) { + return false; + } else { + fifo32_push(&ctrl->fifo, swim2_fifo_pack(data, is_mark)); + trace_swim2_fifo_push(fifo32_num_used(&ctrl->fifo), data, is_mark); + return true; + } +} + +static bool swim2_fifo_pop(SWIM2State *const ctrl, uint8_t *const data, + bool *const is_mark) +{ + if (fifo32_is_empty(&ctrl->fifo)) { + trace_swim2_fifo_pop(fifo32_num_used(&ctrl->fifo), 0, false, false= ); + return false; + } else { + const uint32_t entry =3D fifo32_pop(&ctrl->fifo); + const uint8_t value =3D swim2_fifo_data(entry); + const bool entry_is_mark =3D swim2_fifo_is_mark(entry); + + *data =3D value; + if (is_mark) { + *is_mark =3D entry_is_mark; + } + + trace_swim2_fifo_pop(fifo32_num_used(&ctrl->fifo), value, + entry_is_mark, true); + return true; + } +} + +static SonyDrive *swim2_active_drive(SWIM2State *const ctrl) +{ + uint8_t selected; + + if (ctrl->mode_reg & SWIM2_MODE_ENBL1) { + selected =3D 0; + } else if (ctrl->mode_reg & SWIM2_MODE_ENBL2) { + selected =3D 1; + } else { + return NULL; + } + + if (ctrl->drives[selected]) { + return &ctrl->drives[selected]->sony; + } else { + return NULL; + } +} + +static void swim2_apply_drive_lines(SWIM2State *const ctrl) +{ + SonyDrive *const active =3D swim2_active_drive(ctrl); + + for (uint8_t i =3D 0; i < SWIM2_MAX_FD; i++) { + SWIM2Drive *const entry =3D ctrl->drives[i]; + + if (!entry) { + continue; + } + + SonyDrive *const drive =3D &entry->sony; + + const uint8_t phases =3D (ctrl->phase_reg & 0xF) & + (ctrl->phase_reg >> 4); + const bool head =3D (ctrl->mode_reg & SWIM2_MODE_SIDE); + const bool enabled =3D (drive =3D=3D active) && + ((ctrl->mode_reg & SWIM2_MODE_MOTORON) !=3D 0= ); + + sony_drive_set_inputs(drive, phases, head, enabled); + } +} + +static void swim2_fill_fifo_from_drive(SWIM2State *const ctrl) +{ + SonyDrive *const drive =3D swim2_active_drive(ctrl); + + if (!drive) { + return; + } + + /* + * Don't wait for a mark byte if the drive is in GCR mode. + * A/UX hits this codepath, because it tries to read in MFM mode before + * falling back to GCR. + */ + /* TODO: encapsulate drive->gcr_encode better */ + if (ctrl->wait_for_mark && drive->gcr_encode) { + return; + } + + while (!fifo32_is_full(&ctrl->fifo)) { + uint8_t data; + bool is_mark; + + if (!sony_drive_read_byte(drive, &data, &is_mark)) { + break; + } else if (!ctrl->wait_for_mark || is_mark) { + ctrl->wait_for_mark =3D false; + const bool did_push =3D swim2_fifo_push(ctrl, data, is_mark); + assert(did_push); + } + } +} + +static void swim2_push_fifo_to_drive(SWIM2State *const ctrl) +{ + SonyDrive *const drive =3D swim2_active_drive(ctrl); + + if (!drive) { + /* + * The Mac ROM does this to measure how quickly we can spit bytes = out + * to a drive. Just consume the FIFO without error. + */ + swim2_fifo_clear(ctrl); + } else { + uint8_t data; + bool is_mark; + + while (swim2_fifo_pop(ctrl, &data, &is_mark)) { + if (ctrl->wait_for_mark && is_mark) { + ctrl->wait_for_mark =3D false; + } else if (!ctrl->wait_for_mark && !is_mark) { + sony_drive_write_byte(drive, data); + } + } + } +} + +static void swim2_update_mode(SWIM2State *const ctrl, const uint8_t mask, + const bool set_bits) +{ + const uint8_t prev_mode =3D ctrl->mode_reg; + + if (set_bits) { + ctrl->mode_reg |=3D mask; + } else { + ctrl->mode_reg &=3D ~mask; + ctrl->mode_reg |=3D SWIM2_MODE_ALWAYS1; + } + + if (!set_bits) { + ctrl->parameter_index =3D 0; + } + + if ((mask & SWIM2_MODE_CLR_FIFO) && set_bits) { + swim2_fifo_clear(ctrl); + } + + swim2_apply_drive_lines(ctrl); + + const bool prev_action =3D prev_mode & SWIM2_MODE_ACTION; + const bool action =3D ctrl->mode_reg & SWIM2_MODE_ACTION; + + if (!prev_action && action) { + ctrl->did_handshake =3D false; + ctrl->wait_for_mark =3D !(ctrl->setup_reg & SWIM2_SETUP_GCR_MODE); + + if (ctrl->mode_reg & SWIM2_MODE_WRITE) { + swim2_push_fifo_to_drive(ctrl); + } else { + swim2_fill_fifo_from_drive(ctrl); + } + } +} + +static void swim2_handle_phase_write(SWIM2State *const ctrl, + const uint8_t value) +{ + ctrl->phase_reg =3D value; + swim2_apply_drive_lines(ctrl); +} + +static void swim2_handle_setup_write(SWIM2State *const ctrl, + const uint8_t value) +{ + ctrl->setup_reg =3D value; +} + +/* + * We don't actually do anything with these parameter values other than st= ore + * them. + */ +static void swim2_handle_parameter_write(SWIM2State *const ctrl, + const uint8_t value) +{ + ctrl->parameter_data[ctrl->parameter_index] =3D value; + ctrl->parameter_index =3D (ctrl->parameter_index + 1) & 0x3; +} + +static uint8_t swim2_handle_parameter_read(SWIM2State *const ctrl) +{ + uint8_t value; + + /* Return 0 for parameters 1 and 3, as A/UX expects for a SWIM2. */ + if (ctrl->parameter_index =3D=3D 1 || ctrl->parameter_index =3D=3D 3) { + value =3D 0; + } else { + value =3D ctrl->parameter_data[ctrl->parameter_index]; + } + + ctrl->parameter_index =3D (ctrl->parameter_index + 1) & 0x3; + return value; +} + +static uint8_t swim2_handle_handshake_read(SWIM2State *const ctrl) +{ + SonyDrive *const drive =3D swim2_active_drive(ctrl); + + ctrl->did_handshake =3D true; + + const bool empty =3D fifo32_is_empty(&ctrl->fifo); + const bool full =3D fifo32_is_full(&ctrl->fifo); + const bool error =3D ctrl->error_reg !=3D 0; + const bool sense =3D drive ? sony_drive_read_sense(drive) : true; + const bool mark_next =3D !empty && + swim2_fifo_is_mark(fifo32_peek(&ctrl->fifo)); + + uint8_t value =3D 0; + if (mark_next) { + value |=3D BIT(0); + } + /* bit 1: 1 when invalid CRC (i.e., never, for us) */ + /* bit 2: rddata, not emulated */ + if (sense) { + value |=3D BIT(3); + } + /* bit 4: unused */ + if (error) { + value |=3D BIT(5); + } + + if ((ctrl->mode_reg & SWIM2_MODE_WRITE) !=3D 0) { + if (empty) { + value |=3D BIT(6); + } + + if (!full || error) { + value |=3D BIT(7); + } + } else { + if (full) { + value |=3D BIT(6); + } + + if (!empty) { + value |=3D BIT(7); + } + } + + return value; +} + +static uint64_t swim2_read(void *const opaque, const hwaddr addr, + const unsigned int size) +{ + SWIM2State *const ctrl =3D SWIM2(opaque); + const uint8_t reg =3D (addr >> SWIM2_REG_SHIFT) & 0x7; + uint8_t value =3D 0xff; + + switch (reg) { + case SWIM2_REG_DATA: { + bool is_mark; + + if (!swim2_fifo_pop(ctrl, &value, &is_mark)) { + value =3D 0xff; + swim2_set_error(ctrl, SWIM2_ERROR_OVERRUN); + } else { + if (is_mark) { + swim2_set_error(ctrl, SWIM2_ERROR_MARK_IN_DATA); + } + + swim2_fill_fifo_from_drive(ctrl); + } + + break; + } + case SWIM2_REG_MARK: { + /* + * Allow reading data bytes from here; the specs seem unclear on w= hether + * this is allowed, but the Mac Sony driver does so. + */ + + if (!ctrl->did_handshake) { + /* + * The Mac ROM does something weird when reading an MFM disk. + * Immediately after setting ACTION, it pulls and discards two= bytes + * from MARK. To avoid needing to emulate this with timing, + * simply detect the reads that are not preceded by a HANDSHAKE + * and give back the garbage they seem to expect. + */ + value =3D 0xff; + } else if (!swim2_fifo_pop(ctrl, &value, NULL)) { + value =3D 0xff; + swim2_set_error(ctrl, SWIM2_ERROR_OVERRUN); + } else { + swim2_fill_fifo_from_drive(ctrl); + } + + break; + } + case SWIM2_REG_ERROR: + value =3D ctrl->error_reg; + ctrl->error_reg =3D 0; + break; + case SWIM2_REG_PARAMETER: + value =3D swim2_handle_parameter_read(ctrl); + break; + case SWIM2_REG_PHASE: + value =3D ctrl->phase_reg; + break; + case SWIM2_REG_SETUP: + value =3D ctrl->setup_reg; + break; + case SWIM2_REG_STATUS: + value =3D ctrl->mode_reg; + break; + case SWIM2_REG_HANDSHAKE: + value =3D swim2_handle_handshake_read(ctrl); + break; + default: + value =3D 0xff; + break; + } + + trace_swim2_mmio_read(addr, size, reg, swim2_reg_name(reg, false), val= ue, + ctrl->mode_reg, ctrl->setup_reg, ctrl->phase_reg, + fifo32_num_used(&ctrl->fifo)); + return value; +} + +static void swim2_write(void *const opaque, const hwaddr addr, + const uint64_t data, const unsigned int size) +{ + SWIM2State *const ctrl =3D SWIM2(opaque); + const uint8_t reg =3D (addr >> SWIM2_REG_SHIFT) & 0x7; + const uint8_t value =3D data & 0xff; + + switch (reg) { + case SWIM2_REG_DATA: + if (!swim2_fifo_push(ctrl, value, 0)) { + swim2_set_error(ctrl, SWIM2_ERROR_OVERRUN); + } else if ((ctrl->mode_reg & SWIM2_MODE_ACTION)) { + swim2_push_fifo_to_drive(ctrl); + } + break; + case SWIM2_REG_MARK: + if (!swim2_fifo_push(ctrl, value, SWIM2_FIFO_FLAG_MARK)) { + swim2_set_error(ctrl, SWIM2_ERROR_OVERRUN); + } else if ((ctrl->mode_reg & SWIM2_MODE_ACTION)) { + swim2_push_fifo_to_drive(ctrl); + } + break; + case SWIM2_REG_WRITE_CRC: + /* Nothing to do. */ + break; + case SWIM2_REG_PARAMETER: + swim2_handle_parameter_write(ctrl, value); + break; + case SWIM2_REG_PHASE: + swim2_handle_phase_write(ctrl, value); + break; + case SWIM2_REG_SETUP: + swim2_handle_setup_write(ctrl, value); + break; + case SWIM2_REG_WRITE_ZEROES: + swim2_update_mode(ctrl, value, false); + break; + case SWIM2_REG_WRITE_ONES: + swim2_update_mode(ctrl, value, true); + break; + default: + break; + } + + trace_swim2_mmio_write(addr, size, reg, swim2_reg_name(reg, true), val= ue, + ctrl->mode_reg, ctrl->setup_reg, ctrl->phase_re= g, + fifo32_num_used(&ctrl->fifo)); +} + +void swim2_set_sel(SWIM2State *ctrl, bool sel) +{ + trace_swim2_set_sel(sel); + if (sel) { + ctrl->mode_reg |=3D SWIM2_MODE_SIDE; + } else { + ctrl->mode_reg &=3D ~SWIM2_MODE_SIDE; + } + swim2_apply_drive_lines(ctrl); +} + +static const MemoryRegionOps swim2_mmio_ops =3D { + .read =3D swim2_read, + .write =3D swim2_write, + .endianness =3D DEVICE_BIG_ENDIAN, +}; + +static void swim2_change_cb(void *const opaque, const bool load, + Error **const errp) +{ + SWIM2Drive *const drive =3D opaque; + + if (!load) { + blk_set_perm(drive->conf.blk, 0, BLK_PERM_ALL, &error_abort); + } else if (!blkconf_apply_backend_options(&drive->conf, + !blk_supports_write_perm(drive->conf.blk), false, errp)) { + return; + } + + sony_drive_set_block_backend(&drive->sony, drive->conf.blk); +} + +static void swim2_drive_realize(DeviceState *const dev, Error **const errp) +{ + SWIM2Drive *const drive =3D SWIM2_DRIVE(dev); + SWIM2State *const ctrl =3D SWIM2(dev->parent_bus->parent); + + if (!drive->conf.blk) { + drive->conf.blk =3D blk_new(qemu_get_aio_context(), 0, BLK_PERM_AL= L); + const int ret =3D blk_attach_dev(drive->conf.blk, dev); + assert(ret =3D=3D 0); + } + + if (drive->unit < 0 || drive->unit >=3D SWIM2_MAX_FD) { + error_setg(errp, "unit %d out of range (0-%d)", drive->unit, + SWIM2_MAX_FD - 1); + return; + } + + if (ctrl->drives[drive->unit]) { + error_setg(errp, "floppy unit %d already in use", drive->unit); + return; + } + + if (!blkconf_blocksizes(&drive->conf, errp)) { + /* errp set by blkconf_blocksizes(). */ + return; + } + + if (drive->conf.logical_block_size !=3D 512 || + drive->conf.physical_block_size !=3D 512) { + error_setg(errp, "physical and logical block size must be 512 for" + " floppy"); + return; + } + + drive->conf.rerror =3D BLOCKDEV_ON_ERROR_AUTO; + drive->conf.werror =3D BLOCKDEV_ON_ERROR_AUTO; + + if (!blkconf_apply_backend_options(&drive->conf, + !blk_supports_write_perm(drive->conf.blk), false, errp)) { + /* errp set by blkconf_apply_backend_options(). */ + return; + } + + if (blk_get_on_error(drive->conf.blk, 0) !=3D BLOCKDEV_ON_ERROR_ENOSPC= && + blk_get_on_error(drive->conf.blk, 0) !=3D BLOCKDEV_ON_ERROR_REPORT= ) { + error_setg(errp, "SWIM2 doesn't support drive option werror"); + return; + } + + if (blk_get_on_error(drive->conf.blk, 1) !=3D BLOCKDEV_ON_ERROR_REPORT= ) { + error_setg(errp, "SWIM2 doesn't support drive option rerror"); + return; + } + + ctrl->drives[drive->unit] =3D drive; + sony_drive_set_block_backend(&drive->sony, drive->conf.blk); + + static const BlockDevOps swim2_block_ops =3D { + .change_media_cb =3D swim2_change_cb, + }; + blk_set_dev_ops(drive->conf.blk, &swim2_block_ops, drive); +} + +static void swim2_init(Object *const obj) +{ + SWIM2State *const ctrl =3D SWIM2(obj); + + memory_region_init_io(&ctrl->mmio, obj, &swim2_mmio_ops, ctrl, + "swim2", SWIM2_MMIO_SIZE); + sysbus_init_mmio(&ctrl->parent_obj, &ctrl->mmio); + + for (int i =3D 0; i < SWIM2_MAX_FD; i++) { + ctrl->drives[i] =3D NULL; + } + + memset(ctrl->parameter_data, 0, sizeof ctrl->parameter_data); + ctrl->parameter_index =3D 0; + ctrl->phase_reg =3D 0; + ctrl->setup_reg =3D 0; + ctrl->mode_reg =3D SWIM2_MODE_ALWAYS1; + ctrl->error_reg =3D 0; + ctrl->wait_for_mark =3D false; + fifo32_create(&ctrl->fifo, 2); + swim2_fifo_clear(ctrl); +} + +static void swim2_finalize(Object *const obj) +{ + SWIM2State *const ctrl =3D SWIM2(obj); + + fifo32_destroy(&ctrl->fifo); +} + +static void swim2_realize(DeviceState *const dev, Error **const errp) +{ + SWIM2State *const ctrl =3D SWIM2(dev); + ctrl->bus =3D qbus_new(TYPE_SWIM2_BUS, dev, "SWIM2 dummy bus"); + + for (int unit =3D 0; unit < SWIM2_MAX_FD; unit++) { + DriveInfo *const dinfo =3D drive_get(IF_FLOPPY, 0, unit); + + if (dinfo) { + DeviceState *const drive =3D qdev_new(TYPE_SWIM2_DRIVE); + qdev_prop_set_int32(drive, "unit", unit); + qdev_prop_set_drive_err(drive, "drive", + blk_by_legacy_dinfo(dinfo), &error_abo= rt); + qdev_realize_and_unref(drive, ctrl->bus, &error_abort); + } + } +} + +/* TODO: support migration */ +static void swim2_class_init(ObjectClass *const oc, const void *const opaq= ue) +{ + DeviceClass *const dc =3D DEVICE_CLASS(oc); + dc->realize =3D swim2_realize; + dc->desc =3D "Apple Macintosh SWIM2 floppy diskette drive controller"; +} + +static void swim2_bus_class_init(ObjectClass *const oc, + const void *const opaque) +{ + BusClass *const bc =3D BUS_CLASS(oc); + bc->max_dev =3D 2; +} + +/* TODO: support migration */ +static void swim2_drive_class_init(ObjectClass *const oc, + const void *const opaque) +{ + DeviceClass *const dc =3D DEVICE_CLASS(oc); + dc->bus_type =3D TYPE_SWIM2_BUS; + dc->realize =3D swim2_drive_realize; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + + static const Property swim2_drive_properties[] =3D { + DEFINE_BLOCK_PROPERTIES(SWIM2Drive, conf), + DEFINE_PROP_INT32("unit", SWIM2Drive, unit, -1), + }; + device_class_set_props(dc, swim2_drive_properties); + + dc->desc =3D "Apple Macintosh SuperDrive floppy diskette drive"; +} + +static const TypeInfo swim2_info =3D { + .name =3D TYPE_SWIM2, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(SWIM2State), + .instance_init =3D swim2_init, + .instance_finalize =3D swim2_finalize, + .class_init =3D swim2_class_init, +}; + +static const TypeInfo swim2_bus_info =3D { + .name =3D TYPE_SWIM2_BUS, + .parent =3D TYPE_BUS, + .instance_size =3D 0, + .class_init =3D swim2_bus_class_init, +}; + +static const TypeInfo swim2_drive_info =3D { + .name =3D TYPE_SWIM2_DRIVE, + .parent =3D TYPE_DEVICE, + .instance_size =3D sizeof(SWIM2Drive), + .class_init =3D swim2_drive_class_init, +}; + +static void swim2_register_types(void) +{ + type_register_static(&swim2_info); + type_register_static(&swim2_bus_info); + type_register_static(&swim2_drive_info); +} + +type_init(swim2_register_types) diff --git a/hw/block/trace-events b/hw/block/trace-events index c9eb62bcaa..92d4bd9f17 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -102,6 +102,15 @@ swim_iwmctrl_write(int reg, const char *name, unsigned= size, uint64_t value) "re swim_switch_to_ism(void) "switch from IWM to ISM mode" swim_switch_to_iwm(void) "switch from ISM to IWM mode" =20 +# swim2.c +swim2_mmio_read(uint64_t addr, unsigned size, int reg, const char *name, u= int64_t value, uint8_t mode, uint8_t setup, uint8_t phase, uint32_t fifo_co= unt) "addr=3D0x%"PRIx64" size=3D%u reg=3D%d [%s] value=3D0x%"PRIx64" mode= =3D0x%02x setup=3D0x%02x phase=3D0x%02x fifo=3D%u" +swim2_mmio_write(uint64_t addr, unsigned size, int reg, const char *name, = uint64_t value, uint8_t mode, uint8_t setup, uint8_t phase, uint32_t fifo_c= ount) "addr=3D0x%"PRIx64" size=3D%u reg=3D%d [%s] value=3D0x%"PRIx64" mode= =3D0x%02x setup=3D0x%02x phase=3D0x%02x fifo=3D%u" +swim2_fifo_push(uint32_t count, uint8_t data, uint8_t flags) "fifo=3D%u da= ta=3D0x%02x flags=3D0x%02x" +swim2_fifo_pop(uint32_t count, uint8_t data, uint8_t flags, bool success) = "fifo=3D%u data=3D0x%02x flags=3D0x%02x success=3D%d" +swim2_fifo_clear(void) "clear FIFO" +swim2_set_sel(bool sel) "set sel=3D%d" +swim2_error_set(uint8_t prev, uint8_t curr, uint8_t new_err, uint8_t mode,= bool write_mode, uint32_t fifo_count) "prev=3D0x%02x curr=3D0x%02x new=3D0= x%02x mode=3D0x%02x write=3D%d fifo=3D%u" + # sony_superdrive.c sony_drive_prepare(void *drive, uint8_t track, uint8_t head, uint8_t secto= r, bool gcr, uint16_t payload_len) "drive=3D%p track=3D%"PRIu8" head=3D%"PR= Iu8" sector=3D%"PRIu8" gcr=3D%d length=3D%"PRIu16 sony_drive_persist(void *drive, uint64_t lba) "drive=3D%p lba=3D0x%"PRIx64 diff --git a/include/hw/block/swim2.h b/include/hw/block/swim2.h new file mode 100644 index 0000000000..72be21ea4e --- /dev/null +++ b/include/hw/block/swim2.h @@ -0,0 +1,60 @@ +/* + * QEMU Macintosh floppy disk controller emulator (SWIM2) + * + * Copyright (c) 2025 Matt Jacobson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_BLOCK_SWIM2_H +#define HW_BLOCK_SWIM2_H + +#include +#include +#include "hw/core/qdev.h" +#include "hw/core/sysbus.h" +#include "hw/block/block.h" +#include "hw/block/sony_superdrive.h" +#include "qemu/fifo32.h" +#include "qom/object.h" + +#define TYPE_SWIM2 "swim2" +#define TYPE_SWIM2_BUS "swim2-bus" +#define TYPE_SWIM2_DRIVE "swim2-drive" + +OBJECT_DECLARE_SIMPLE_TYPE(SWIM2State, SWIM2) +OBJECT_DECLARE_SIMPLE_TYPE(SWIM2Drive, SWIM2_DRIVE) + +#define SWIM2_MAX_FD 2 + +struct SWIM2Drive { + DeviceState parent_obj; + + BlockConf conf; + SonyDrive sony; + int32_t unit; +}; + +struct SWIM2State { + SysBusDevice parent_obj; + + MemoryRegion mmio; + BusState *bus; + + SWIM2Drive *drives[SWIM2_MAX_FD]; + + uint8_t parameter_data[4]; + uint8_t parameter_index; + uint8_t phase_reg; + uint8_t setup_reg; + uint8_t mode_reg; + uint8_t error_reg; + bool did_handshake; + bool wait_for_mark; + + Fifo32 fifo; +}; + +void swim2_set_sel(SWIM2State *ctrl, bool sel); + +#endif /* HW_BLOCK_SWIM2_H */ --=20 2.53.0 From nobody Tue Jun 30 03:39:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=me.com ARC-Seal: i=1; a=rsa-sha256; t=1781740303; cv=none; d=zohomail.com; s=zohoarc; b=MmXWt0Rrl71QSI2iBWERLS/YziYw1ciHxbo9iHDtZ9ynMi5eOG9XtKfdMvqYdDshYQRmhM+V2tPTOO9pYe5rO8K5LvBOV6U5hj62TK30lUAWM/YCppUSq4BAmdH/IP0OOtLs3cGqjskDUjsferSa7ZmNfpFsoV9EyG8eRbeWsUY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781740303; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=QxIFPOHU7e9gIqyhQkeZZjwyxbbbOQrk6FsunfvYN9E=; b=AIjHKJspaxNG8WUP/CdrSH55GANCvqgQ2HPKPY80zBatUm6i0Y0Ucddl7Auispmc0ioiJWc8/B2b7YOQAJlluRTxKRLbJKTd0ORsyW/GcE5wrTdd7QuKf07T0aTfz4JtoKnAFARekINTyU7ISUqFXwHEraRf+vmPymqwmLcJyUM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781740303822277.48976932873154; Wed, 17 Jun 2026 16:51:43 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wa022-0004PH-4L; Wed, 17 Jun 2026 19:50:54 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wa01w-0004NL-IR for qemu-devel@nongnu.org; Wed, 17 Jun 2026 19:50:48 -0400 Received: from p-east2-cluster1-host7-snip4-8.eps.apple.com ([57.103.76.71] helo=outbound.st.icloud.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wa01u-0003tp-0G for qemu-devel@nongnu.org; Wed, 17 Jun 2026 19:50:47 -0400 Received: from outbound.st.icloud.com (unknown [127.0.0.2]) by p00-icloudmta-asmtp-us-east-1a-60-percent-11 (Postfix) with ESMTPS id C8A071800CDA; Wed, 17 Jun 2026 23:50:41 +0000 (UTC) Received: from localhost.localdomain (unknown [17.42.251.67]) by p00-icloudmta-asmtp-us-east-1a-60-percent-11 (Postfix) with ESMTPSA id 234391800C16; Wed, 17 Jun 2026 23:50:41 +0000 (UTC) X-ICL-Out-Info: HUtFAUMHWwJACUgATUQeDx5WFlZNRAJCTQ5JHVoHRQNGD1YCWgZLVxQEFVsHVghdEkoXXS1aDhwTVhUTC1NWXxUXG1wAFxlRTQ5YWwhbBA8fTAxRAkIFVl5KDB0EVAddBV1WUAJaS0IES0VoXAVcHEAXSB1faktWFAQVVkNUBF9QVBFXUAtZAkIPSAVcAFkBRQlLAUMFWgZJCFUGQFoDWBFaF1EeWAVyHVxWUAJaVRIEQAhWUF4IXh9MHA== Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=me.com; s=1a1hai; t=1781740242; x=1784332242; bh=QxIFPOHU7e9gIqyhQkeZZjwyxbbbOQrk6FsunfvYN9E=; h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme; b=iCGb2VA+Vy00aS1gfGjSHtgzcQZZ7R6sY9JtHlUjAQwcOUvY0Th650yccUkbzjhrlE7dKii2joucDfDj48NXdt9NdsiNZOPrawHoPLZek4TOyHw88fQ2SdvXS4UId3zyXHBu4mmn8Gml9cenux8ZKVTJ7qEtXL7rw8f6IoPCYVSnJ3VrmU1hoLtzb2CuoNGoS4vY+AR11dFZpYqMnJ6xghipVgsaf2GJU3UwimNoGGSG6CmF6G9cH+YToJWDsRMVdTT2dgAHsYDqhbOptePExa7EsLWhrOhQEsDkWF64FaxFCKk34ur0WIpZaDXMoStGuW4pX9Pw58H+dAPwL7TJ0w== From: Matt Jacobson To: qemu-devel@nongnu.org Cc: Kevin Wolf , Hanna Reitz , qemu-block@nongnu.org, Mark Cave-Ayland , Laurent Vivier , Matt Jacobson Subject: [PATCH v2 4/5] hw/m68k: add working floppy controller for q800 Date: Wed, 17 Jun 2026 19:50:31 -0400 Message-ID: <20260617235032.21491-5-mhjacobson@me.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260617235032.21491-1-mhjacobson@me.com> References: <20260617235032.21491-1-mhjacobson@me.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNjE3MDIyOCBTYWx0ZWRfXwThi3PryvK4x /Y/3lcd336p/bZuTRydYIG4YX0MqDYHzs7Z/qfCGuUE2Vd/BZUkuA6IzOv4lgxm3D3Rl0m4IQur /NB5SKOPNCTEkDBgH18NPrKuMTUkvbx0MNkxx/P+8lB8VWd1/3vF74A6yF6zyyVZ7Zr3bRV5vEB yZka5IGaB6EQ7wUUtR8dxim4QFvTvoapTU51VohbydOFwqQBDdDqwTjUkyxqBXZ6A5tadymMzf3 sNbcw0uLivaI2vednMBtasEfpCvYJzGdTqsALqfjBOmNzIKkZYKYX/GUp4Mawvt0I4hiZFpXWeZ g1PMwVG2ph1Z6Ez0sdl X-Proofpoint-ORIG-GUID: fQAeRe0chWi0TUH8f3v0ER_qmOQwbJTp X-Proofpoint-GUID: fQAeRe0chWi0TUH8f3v0ER_qmOQwbJTp Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=57.103.76.71; envelope-from=mhjacobson@me.com; helo=outbound.st.icloud.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @me.com) X-ZM-MESSAGEID: 1781740306077158501 Content-Type: text/plain; charset="utf-8" Switch the Quadra 800 machine to the working SWIM2 floppy controller. Hook up the HeadSel bit on the VIA to control the head selection (the IWM does not handle this part itself). Signed-off-by: Matt Jacobson --- hw/m68k/q800.c | 5 +++-- hw/misc/mac_via.c | 16 ++++++++++++++++ include/hw/m68k/q800.h | 4 ++-- include/hw/misc/mac_via.h | 4 ++++ 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index ab64250c47..a6997ef802 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -47,7 +47,7 @@ #include "hw/audio/asc.h" #include "hw/nubus/mac-nubus-bridge.h" #include "hw/display/macfb.h" -#include "hw/block/swim.h" +#include "hw/block/swim2.h" #include "net/net.h" #include "net/util.h" #include "qapi/error.h" @@ -338,6 +338,7 @@ static void q800_machine_init(MachineState *machine) /* VIA 1 */ object_initialize_child(OBJECT(machine), "via1", &m->via1, TYPE_MOS6522_Q800_VIA1); + m->via1.swim =3D &m->swim; dinfo =3D drive_get(IF_MTD, 0, 0); if (dinfo) { qdev_prop_set_drive(DEVICE(&m->via1), "drive", @@ -511,7 +512,7 @@ static void q800_machine_init(MachineState *machine) /* SWIM floppy controller */ =20 object_initialize_child(OBJECT(machine), "swim", &m->swim, - TYPE_SWIM); + TYPE_SWIM2); sysbus =3D SYS_BUS_DEVICE(&m->swim); sysbus_realize(sysbus, &error_fatal); memory_region_add_subregion(&m->macio, SWIM_BASE - IO_BASE, diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 26ee230dcc..09197fe519 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -31,6 +31,7 @@ #include "hw/core/qdev-properties-system.h" #include "system/block-backend.h" #include "system/rtc.h" +#include "hw/block/swim2.h" #include "trace.h" #include "qemu/log.h" =20 @@ -1080,6 +1081,17 @@ static void mos6522_q800_via1_write(void *opaque, hw= addr addr, uint64_t val, mos6522_write(ms, addr, val, size); =20 switch (addr) { + case VIA_REG_A: + case VIA_REG_ANH: { + const bool sel =3D (ms->a & VIA1A_vHeadSel) !=3D 0; + + if (v1s->swim) { + swim2_set_sel(v1s->swim, sel); + } + + break; + } + case VIA_REG_B: via1_rtc_update(v1s); via1_adb_update(v1s); @@ -1224,6 +1236,10 @@ static void mos6522_q800_via1_reset_hold(Object *obj= , ResetType type) =20 /* Timer calibration hack */ v1s->timer_hack_state =3D 0; + + if (v1s->swim) { + swim2_set_sel(v1s->swim, false); + } } =20 static void mos6522_q800_via1_realize(DeviceState *dev, Error **errp) diff --git a/include/hw/m68k/q800.h b/include/hw/m68k/q800.h index 77551b9592..2a20314d57 100644 --- a/include/hw/m68k/q800.h +++ b/include/hw/m68k/q800.h @@ -33,7 +33,7 @@ #include "hw/char/escc.h" #include "hw/core/or-irq.h" #include "hw/scsi/esp.h" -#include "hw/block/swim.h" +#include "hw/block/swim2.h" #include "hw/nubus/mac-nubus-bridge.h" #include "hw/display/macfb.h" #include "hw/misc/djmemc.h" @@ -59,7 +59,7 @@ struct Q800MachineState { ESCCState escc; OrIRQState escc_orgate; SysBusESPState esp; - Swim swim; + SWIM2State swim; MacNubusBridge mac_nubus_bridge; MacfbNubusState macfb; DJMEMCState djmemc; diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h index 114f41db4c..a25f219a25 100644 --- a/include/hw/misc/mac_via.h +++ b/include/hw/misc/mac_via.h @@ -35,6 +35,8 @@ #define TYPE_MOS6522_Q800_VIA1 "mos6522-q800-via1" OBJECT_DECLARE_SIMPLE_TYPE(MOS6522Q800VIA1State, MOS6522_Q800_VIA1) =20 +struct SWIM2State; + struct MOS6522Q800VIA1State { /*< private >*/ MOS6522State parent_obj; @@ -77,6 +79,8 @@ struct MOS6522Q800VIA1State { =20 /* SETUPTIMEK hack */ int timer_hack_state; + + struct SWIM2State *swim; }; =20 =20 --=20 2.53.0 From nobody Tue Jun 30 03:39:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=me.com ARC-Seal: i=1; a=rsa-sha256; t=1781740304; cv=none; d=zohomail.com; s=zohoarc; b=kpbj79wiTTLsSmvdmxMUaZb9a2r+5qtP+j2VNhtTWshJnC4seSnfZsL+rZP5raV0I5WEgBHiAq3RL93+KqtBFKEYTPrfEgP6YjBO7/hIJtSvvNQ08fnIQpx4M0KqUOlhcAiu3xMIfLPBCzhWfUaBvIlxpKQHPdXeMEAoo7E0RdM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781740304; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=yTL2gW9VzVcni3HX9ffEPa8wIlj+hVEhHUIOK7bhjUA=; b=Io52HOigtFKaSFmewl/NK2pQ5LQ1SpJOVgHxKDXFbRuXomXsVqIqc+VDiIf0onYUFqoHMjmDlLgOq3aJ948eaIYVSMTqHVM8RPgy4e73lXYCwhpyoHQC2TkExT0BA57mX6r1KLKPubm9F2HixP5wRfT8qRfRvOC5K1uyHjYfdT0= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781740304142931.3106244759258; Wed, 17 Jun 2026 16:51:44 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wa026-0004QN-17; Wed, 17 Jun 2026 19:50:58 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wa01z-0004OQ-Q2 for qemu-devel@nongnu.org; Wed, 17 Jun 2026 19:50:51 -0400 Received: from p-east2-cluster1-host11-snip4-2.eps.apple.com ([57.103.76.45] helo=outbound.st.icloud.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wa01v-0003uA-7u for qemu-devel@nongnu.org; Wed, 17 Jun 2026 19:50:51 -0400 Received: from outbound.st.icloud.com (unknown [127.0.0.2]) by p00-icloudmta-asmtp-us-east-1a-60-percent-11 (Postfix) with ESMTPS id 729D318000A8; Wed, 17 Jun 2026 23:50:42 +0000 (UTC) Received: from localhost.localdomain (unknown [17.42.251.67]) by p00-icloudmta-asmtp-us-east-1a-60-percent-11 (Postfix) with ESMTPSA id 9AE7F1800CD4; Wed, 17 Jun 2026 23:50:41 +0000 (UTC) X-ICL-Out-Info: HUtFAUMHWwJACUgBTUQeDx5WFlZNRAJCTQ5JHVoHRQNGD1YCWgZLVxQEFVsHVghdEkoXXS1aDhwTVhUTC1NWXxUXG1wAFxlRTQ5YWwhbBA8fTAxRAkIFVl5KDB0EVAddBV1WUAJaS0IES0VoXAVcHEAXSB1faktWFAQVVkNUBF9QVBFXUAtZAkIPSAVcAFkBRQlLAUMFWgZJCFUFQFoDWBFaF1EeWAVyHVxWUAJaVRIEQAhWUF4IXh9MHA== Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=me.com; s=1a1hai; t=1781740243; x=1784332243; bh=yTL2gW9VzVcni3HX9ffEPa8wIlj+hVEhHUIOK7bhjUA=; h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme; b=zMc3XMomzrn99qbgXHY6ocuSqJkCT6dm981PIii1HRi6gyjvASSI0xlK0+aBVnGQJT3fBoRlcpaPkYIWzFRvVJxqD+uYUbSM30i15jAkYSj5U4UjmHj+nDHQl9mQ+En+kpD61nQQ4A1ssLJo3lkkutmXQOkAijt9RKElJJbjsFEuG9V9qU0wjvILTLFp5eakz4LhUJjWen9lHn5Nj7Eye2um/gbhj2Qa9dPmgr5f4RrfwLGEnjNicQHH13XELqqzQabfugq72fDXqd0LPzidRbU3Yma6wasffepu02EJp5QUO8UEH9BEsRjpMxT6o7PJwGbyY/NFW/OQhOZym7K+aQ== From: Matt Jacobson To: qemu-devel@nongnu.org Cc: Kevin Wolf , Hanna Reitz , qemu-block@nongnu.org, Mark Cave-Ayland , Laurent Vivier , Matt Jacobson Subject: [PATCH v2 5/5] hw/block: remove unused SWIM1 floppy controller stub Date: Wed, 17 Jun 2026 19:50:32 -0400 Message-ID: <20260617235032.21491-6-mhjacobson@me.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260617235032.21491-1-mhjacobson@me.com> References: <20260617235032.21491-1-mhjacobson@me.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNjE3MDIyOCBTYWx0ZWRfX4+W3U2GQc84i mKWkcofAW03eLi/DCTulmBIFO+xFd31ebAhHf7y7AT8luuh8heshdovOTLR1MsLfkawpOHicMBk qhUnwyHqlNrc4bin2DpFQgfbPRxo3Q1gl/th3J4JbN3aB5zgsrjrOE1v0aMLb9TTXZBSqqkeAhI LDexN7n8xnJiI556J1ECVcqDmA7BiL0lkLZNkozSvy0oHHCBQd9K+82gi5Q08xNL3J5+6dkl5pB 4bynk9OOTQs41TYKQFF8e1zJldm4btLzM9T556Xj+F5CpoyQgOuC+T0iTK6+jsvWmi9n5vrOErx CB277oCwB29kVfT4rHy X-Proofpoint-ORIG-GUID: 5TVFWRLnNRdChQWAFeEiqTZT5HAmPSB8 X-Proofpoint-GUID: 5TVFWRLnNRdChQWAFeEiqTZT5HAmPSB8 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=57.103.76.45; envelope-from=mhjacobson@me.com; helo=outbound.st.icloud.com X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @me.com) X-ZM-MESSAGEID: 1781740306272158500 Content-Type: text/plain; charset="utf-8" This was formerly used in the q800 machine; it's been replaced by a functional SWIM2. Signed-off-by: Matt Jacobson --- MAINTAINERS | 2 - hw/block/meson.build | 2 +- hw/block/swim.c | 577 ---------------------------------------- hw/block/trace-events | 8 - include/hw/block/swim.h | 72 ----- 5 files changed, 1 insertion(+), 660 deletions(-) delete mode 100644 hw/block/swim.c delete mode 100644 include/hw/block/swim.h diff --git a/MAINTAINERS b/MAINTAINERS index 2b5b581e17..e24c7734d8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1394,7 +1394,6 @@ F: hw/m68k/q800-glue.c F: hw/misc/mac_via.c F: hw/nubus/* F: hw/display/macfb.c -F: hw/block/swim.c F: hw/misc/djmemc.c F: hw/misc/iosb.c F: hw/audio/asc.c @@ -1404,7 +1403,6 @@ F: include/standard-headers/asm-m68k/bootinfo-mac.h F: include/hw/misc/mac_via.h F: include/hw/nubus/* F: include/hw/display/macfb.h -F: include/hw/block/swim.h F: include/hw/m68k/q800.h F: include/hw/m68k/q800-glue.h F: include/hw/misc/djmemc.h diff --git a/hw/block/meson.build b/hw/block/meson.build index 733e9ffb47..2a9a0b9b13 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -10,7 +10,7 @@ system_ss.add(when: 'CONFIG_PFLASH_CFI01', if_true: files= ('pflash_cfi01.c')) system_ss.add(when: 'CONFIG_PFLASH_CFI02', if_true: files('pflash_cfi02.c'= )) system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80.c')) system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80_sfdp.c')) -system_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c', 'swim2.c', 'so= ny_superdrive.c')) +system_ss.add(when: 'CONFIG_SWIM', if_true: files('swim2.c', 'sony_superdr= ive.c')) system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-block.c')) =20 system_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c')) diff --git a/hw/block/swim.c b/hw/block/swim.c deleted file mode 100644 index 54b63bfbb5..0000000000 --- a/hw/block/swim.c +++ /dev/null @@ -1,577 +0,0 @@ -/* - * QEMU Macintosh floppy disk controller emulator (SWIM) - * - * Copyright (c) 2014-2018 Laurent Vivier - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Only the basic support: it allows to switch from IWM (Integrated WOZ - * Machine) mode to the SWIM mode and makes the linux driver happy. - */ - -#include "qemu/osdep.h" -#include "qemu/main-loop.h" -#include "qapi/error.h" -#include "system/block-backend.h" -#include "hw/core/sysbus.h" -#include "migration/vmstate.h" -#include "hw/block/block.h" -#include "hw/block/swim.h" -#include "hw/core/qdev-properties.h" -#include "trace.h" - - -/* IWM latch bits */ - -#define IWMLB_PHASE0 0 -#define IWMLB_PHASE1 1 -#define IWMLB_PHASE2 2 -#define IWMLB_PHASE3 3 -#define IWMLB_MOTORON 4 -#define IWMLB_DRIVESEL 5 -#define IWMLB_L6 6 -#define IWMLB_L7 7 - -/* IWM registers */ - -#define IWM_READALLONES 0 -#define IWM_READDATA 1 -#define IWM_READSTATUS0 2 -#define IWM_READSTATUS1 3 -#define IWM_READWHANDSHAKE0 4 -#define IWM_READWHANDSHAKE1 5 -#define IWM_WRITESETMODE 6 -#define IWM_WRITEDATA 7 - -/* SWIM registers */ - -#define SWIM_WRITE_DATA 0 -#define SWIM_WRITE_MARK 1 -#define SWIM_WRITE_CRC 2 -#define SWIM_WRITE_PARAMETER 3 -#define SWIM_WRITE_PHASE 4 -#define SWIM_WRITE_SETUP 5 -#define SWIM_WRITE_MODE0 6 -#define SWIM_WRITE_MODE1 7 - -#define SWIM_READ_DATA 8 -#define SWIM_READ_MARK 9 -#define SWIM_READ_ERROR 10 -#define SWIM_READ_PARAMETER 11 -#define SWIM_READ_PHASE 12 -#define SWIM_READ_SETUP 13 -#define SWIM_READ_STATUS 14 -#define SWIM_READ_HANDSHAKE 15 - -#define REG_SHIFT 9 - -#define SWIM_MODE_STATUS_BIT 6 -#define SWIM_MODE_IWM 0 -#define SWIM_MODE_ISM 1 - -/* bits in phase register */ - -#define SWIM_SEEK_NEGATIVE 0x074 -#define SWIM_STEP 0x071 -#define SWIM_MOTOR_ON 0x072 -#define SWIM_MOTOR_OFF 0x076 -#define SWIM_INDEX 0x073 -#define SWIM_EJECT 0x077 -#define SWIM_SETMFM 0x171 -#define SWIM_SETGCR 0x175 -#define SWIM_RELAX 0x033 -#define SWIM_LSTRB 0x008 -#define SWIM_CA_MASK 0x077 - -/* Select values for swim_select and swim_readbit */ - -#define SWIM_READ_DATA_0 0x074 -#define SWIM_TWOMEG_DRIVE 0x075 -#define SWIM_SINGLE_SIDED 0x076 -#define SWIM_DRIVE_PRESENT 0x077 -#define SWIM_DISK_IN 0x170 -#define SWIM_WRITE_PROT 0x171 -#define SWIM_TRACK_ZERO 0x172 -#define SWIM_TACHO 0x173 -#define SWIM_READ_DATA_1 0x174 -#define SWIM_MFM_MODE 0x175 -#define SWIM_SEEK_COMPLETE 0x176 -#define SWIM_ONEMEG_MEDIA 0x177 - -/* Bits in handshake register */ - -#define SWIM_MARK_BYTE 0x01 -#define SWIM_CRC_ZERO 0x02 -#define SWIM_RDDATA 0x04 -#define SWIM_SENSE 0x08 -#define SWIM_MOTEN 0x10 -#define SWIM_ERROR 0x20 -#define SWIM_DAT2BYTE 0x40 -#define SWIM_DAT1BYTE 0x80 - -/* bits in setup register */ - -#define SWIM_S_INV_WDATA 0x01 -#define SWIM_S_3_5_SELECT 0x02 -#define SWIM_S_GCR 0x04 -#define SWIM_S_FCLK_DIV2 0x08 -#define SWIM_S_ERROR_CORR 0x10 -#define SWIM_S_IBM_DRIVE 0x20 -#define SWIM_S_GCR_WRITE 0x40 -#define SWIM_S_TIMEOUT 0x80 - -/* bits in mode register */ - -#define SWIM_CLFIFO 0x01 -#define SWIM_ENBL1 0x02 -#define SWIM_ENBL2 0x04 -#define SWIM_ACTION 0x08 -#define SWIM_WRITE_MODE 0x10 -#define SWIM_HEDSEL 0x20 -#define SWIM_MOTON 0x80 - -static const char *iwm_reg_names[] =3D { - "READALLONES", "READDATA", "READSTATUS0", "READSTATUS1", - "READWHANDSHAKE0", "READWHANDSHAKE1", "WRITESETMODE", "WRITEDATA" -}; - -static const char *ism_reg_names[] =3D { - "WRITE_DATA", "WRITE_MARK", "WRITE_CRC", "WRITE_PARAMETER", - "WRITE_PHASE", "WRITE_SETUP", "WRITE_MODE0", "WRITE_MODE1", - "READ_DATA", "READ_MARK", "READ_ERROR", "READ_PARAMETER", - "READ_PHASE", "READ_SETUP", "READ_STATUS", "READ_HANDSHAKE" -}; - -static void fd_recalibrate(FDrive *drive) -{ -} - -static void swim_change_cb(void *opaque, bool load, Error **errp) -{ - FDrive *drive =3D opaque; - - if (!load) { - blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort); - } else { - if (!blkconf_apply_backend_options(drive->conf, - !blk_supports_write_perm(drive-= >blk), - false, errp)) { - return; - } - } -} - -static const BlockDevOps swim_block_ops =3D { - .change_media_cb =3D swim_change_cb, -}; - -static const Property swim_drive_properties[] =3D { - DEFINE_PROP_INT32("unit", SWIMDrive, unit, -1), - DEFINE_BLOCK_PROPERTIES(SWIMDrive, conf), -}; - -static void swim_drive_realize(DeviceState *qdev, Error **errp) -{ - SWIMDrive *dev =3D SWIM_DRIVE(qdev); - SWIMBus *bus =3D SWIM_BUS(qdev->parent_bus); - FDrive *drive; - int ret; - - if (dev->unit =3D=3D -1) { - for (dev->unit =3D 0; dev->unit < SWIM_MAX_FD; dev->unit++) { - drive =3D &bus->ctrl->drives[dev->unit]; - if (!drive->blk) { - break; - } - } - } - - if (dev->unit >=3D SWIM_MAX_FD) { - error_setg(errp, "Can't create floppy unit %d, bus supports " - "only %d units", dev->unit, SWIM_MAX_FD); - return; - } - - drive =3D &bus->ctrl->drives[dev->unit]; - if (drive->blk) { - error_setg(errp, "Floppy unit %d is in use", dev->unit); - return; - } - - if (!dev->conf.blk) { - /* Anonymous BlockBackend for an empty drive */ - dev->conf.blk =3D blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); - ret =3D blk_attach_dev(dev->conf.blk, qdev); - assert(ret =3D=3D 0); - } - - if (!blkconf_blocksizes(&dev->conf, errp)) { - return; - } - - if (dev->conf.logical_block_size !=3D 512 || - dev->conf.physical_block_size !=3D 512) - { - error_setg(errp, "Physical and logical block size must " - "be 512 for floppy"); - return; - } - - /* - * rerror/werror aren't supported by fdc and therefore not even regist= ered - * with qdev. So set the defaults manually before they are used in - * blkconf_apply_backend_options(). - */ - dev->conf.rerror =3D BLOCKDEV_ON_ERROR_AUTO; - dev->conf.werror =3D BLOCKDEV_ON_ERROR_AUTO; - - if (!blkconf_apply_backend_options(&dev->conf, - !blk_supports_write_perm(dev->conf.= blk), - false, errp)) { - return; - } - - /* - * 'enospc' is the default for -drive, 'report' is what blk_new() give= s us - * for empty drives. - */ - if (blk_get_on_error(dev->conf.blk, 0) !=3D BLOCKDEV_ON_ERROR_ENOSPC && - blk_get_on_error(dev->conf.blk, 0) !=3D BLOCKDEV_ON_ERROR_REPORT) { - error_setg(errp, "fdc doesn't support drive option werror"); - return; - } - if (blk_get_on_error(dev->conf.blk, 1) !=3D BLOCKDEV_ON_ERROR_REPORT) { - error_setg(errp, "fdc doesn't support drive option rerror"); - return; - } - - drive->conf =3D &dev->conf; - drive->blk =3D dev->conf.blk; - drive->swimctrl =3D bus->ctrl; - - blk_set_dev_ops(drive->blk, &swim_block_ops, drive); -} - -static void swim_drive_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *k =3D DEVICE_CLASS(klass); - k->realize =3D swim_drive_realize; - set_bit(DEVICE_CATEGORY_STORAGE, k->categories); - k->bus_type =3D TYPE_SWIM_BUS; - device_class_set_props(k, swim_drive_properties); - k->desc =3D "virtual SWIM drive"; -} - -static const TypeInfo swim_drive_info =3D { - .name =3D TYPE_SWIM_DRIVE, - .parent =3D TYPE_DEVICE, - .instance_size =3D sizeof(SWIMDrive), - .class_init =3D swim_drive_class_init, -}; - -static const TypeInfo swim_bus_info =3D { - .name =3D TYPE_SWIM_BUS, - .parent =3D TYPE_BUS, - .instance_size =3D sizeof(SWIMBus), -}; - -static void iwmctrl_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - SWIMCtrl *swimctrl =3D opaque; - uint8_t latch, reg, ism_bit; - - addr >>=3D REG_SHIFT; - - /* A3-A1 select a latch, A0 specifies the value */ - latch =3D (addr >> 1) & 7; - if (addr & 1) { - swimctrl->iwm_latches |=3D (1 << latch); - } else { - swimctrl->iwm_latches &=3D ~(1 << latch); - } - - reg =3D (swimctrl->iwm_latches & 0xc0) >> 5 | - (swimctrl->iwm_latches & 0x10) >> 4; - - swimctrl->iwmregs[reg] =3D value; - trace_swim_iwmctrl_write(reg, iwm_reg_names[reg], size, value); - - switch (reg) { - case IWM_WRITESETMODE: - /* detect sequence to switch from IWM mode to SWIM mode */ - ism_bit =3D (value & (1 << SWIM_MODE_STATUS_BIT)); - - switch (swimctrl->iwm_switch) { - case 0: - if (ism_bit) { /* 1 */ - swimctrl->iwm_switch++; - } - break; - case 1: - if (!ism_bit) { /* 0 */ - swimctrl->iwm_switch++; - } - break; - case 2: - if (ism_bit) { /* 1 */ - swimctrl->iwm_switch++; - } - break; - case 3: - if (ism_bit) { /* 1 */ - swimctrl->iwm_switch++; - - swimctrl->mode =3D SWIM_MODE_ISM; - swimctrl->swim_mode |=3D (1 << SWIM_MODE_STATUS_BIT); - swimctrl->iwm_switch =3D 0; - trace_swim_switch_to_ism(); - - /* Switch to ISM registers */ - memory_region_del_subregion(&swimctrl->swim, &swimctrl->iw= m); - memory_region_add_subregion(&swimctrl->swim, 0x0, - &swimctrl->ism); - } - break; - } - break; - default: - break; - } -} - -static uint64_t iwmctrl_read(void *opaque, hwaddr addr, unsigned size) -{ - SWIMCtrl *swimctrl =3D opaque; - uint8_t latch, reg, value; - - addr >>=3D REG_SHIFT; - - /* A3-A1 select a latch, A0 specifies the value */ - latch =3D (addr >> 1) & 7; - if (addr & 1) { - swimctrl->iwm_latches |=3D (1 << latch); - } else { - swimctrl->iwm_latches &=3D ~(1 << latch); - } - - reg =3D (swimctrl->iwm_latches & 0xc0) >> 5 | - (swimctrl->iwm_latches & 0x10) >> 4; - - switch (reg) { - case IWM_READALLONES: - value =3D 0xff; - break; - default: - value =3D 0; - break; - } - - trace_swim_iwmctrl_read(reg, iwm_reg_names[reg], size, value); - return value; -} - -static const MemoryRegionOps swimctrl_iwm_ops =3D { - .write =3D iwmctrl_write, - .read =3D iwmctrl_read, - .endianness =3D DEVICE_BIG_ENDIAN, -}; - -static void ismctrl_write(void *opaque, hwaddr reg, uint64_t value, - unsigned size) -{ - SWIMCtrl *swimctrl =3D opaque; - - reg >>=3D REG_SHIFT; - - trace_swim_ismctrl_write(reg, ism_reg_names[reg], size, value); - - switch (reg) { - case SWIM_WRITE_PHASE: - swimctrl->swim_phase =3D value; - break; - case SWIM_WRITE_MODE0: - swimctrl->swim_mode &=3D ~value; - /* Any access to MODE0 register resets PRAM index */ - swimctrl->pram_idx =3D 0; - - if (!(swimctrl->swim_mode & (1 << SWIM_MODE_STATUS_BIT))) { - /* Clearing the mode bit switches to IWM mode */ - swimctrl->mode =3D SWIM_MODE_IWM; - swimctrl->iwm_latches =3D 0; - trace_swim_switch_to_iwm(); - - /* Switch to IWM registers */ - memory_region_del_subregion(&swimctrl->swim, &swimctrl->ism); - memory_region_add_subregion(&swimctrl->swim, 0x0, - &swimctrl->iwm); - } - break; - case SWIM_WRITE_MODE1: - swimctrl->swim_mode |=3D value; - break; - case SWIM_WRITE_PARAMETER: - swimctrl->pram[swimctrl->pram_idx++] =3D value; - swimctrl->pram_idx &=3D 0xf; - break; - case SWIM_WRITE_DATA: - case SWIM_WRITE_MARK: - case SWIM_WRITE_CRC: - case SWIM_WRITE_SETUP: - break; - } -} - -static uint64_t ismctrl_read(void *opaque, hwaddr reg, unsigned size) -{ - SWIMCtrl *swimctrl =3D opaque; - uint32_t value =3D 0; - - reg >>=3D REG_SHIFT; - - switch (reg) { - case SWIM_READ_PHASE: - value =3D swimctrl->swim_phase; - break; - case SWIM_READ_HANDSHAKE: - if (swimctrl->swim_phase =3D=3D SWIM_DRIVE_PRESENT) { - /* always answer "no drive present" */ - value =3D SWIM_SENSE; - } - break; - case SWIM_READ_PARAMETER: - value =3D swimctrl->pram[swimctrl->pram_idx++]; - swimctrl->pram_idx &=3D 0xf; - break; - case SWIM_READ_STATUS: - value =3D swimctrl->swim_status & ~(1 << SWIM_MODE_STATUS_BIT); - if (swimctrl->swim_mode =3D=3D SWIM_MODE_ISM) { - value |=3D (1 << SWIM_MODE_STATUS_BIT); - } - break; - case SWIM_READ_DATA: - case SWIM_READ_MARK: - case SWIM_READ_ERROR: - case SWIM_READ_SETUP: - break; - } - - trace_swim_ismctrl_read(reg, ism_reg_names[reg], size, value); - return value; -} - -static const MemoryRegionOps swimctrl_ism_ops =3D { - .write =3D ismctrl_write, - .read =3D ismctrl_read, - .endianness =3D DEVICE_BIG_ENDIAN, -}; - -static void sysbus_swim_reset(DeviceState *d) -{ - Swim *sys =3D SWIM(d); - SWIMCtrl *ctrl =3D &sys->ctrl; - int i; - - ctrl->mode =3D 0; - ctrl->iwm_switch =3D 0; - memset(ctrl->iwmregs, 0, sizeof(ctrl->iwmregs)); - - ctrl->swim_phase =3D 0; - ctrl->swim_mode =3D 0; - memset(ctrl->ismregs, 0, sizeof(ctrl->ismregs)); - for (i =3D 0; i < SWIM_MAX_FD; i++) { - fd_recalibrate(&ctrl->drives[i]); - } -} - -static void sysbus_swim_init(Object *obj) -{ - SysBusDevice *sbd =3D SYS_BUS_DEVICE(obj); - Swim *sbs =3D SWIM(obj); - SWIMCtrl *swimctrl =3D &sbs->ctrl; - - memory_region_init(&swimctrl->swim, obj, "swim", 0x2000); - memory_region_init_io(&swimctrl->iwm, obj, &swimctrl_iwm_ops, swimctrl, - "iwm", 0x2000); - memory_region_init_io(&swimctrl->ism, obj, &swimctrl_ism_ops, swimctrl, - "ism", 0x2000); - sysbus_init_mmio(sbd, &swimctrl->swim); -} - -static void sysbus_swim_realize(DeviceState *dev, Error **errp) -{ - Swim *sys =3D SWIM(dev); - SWIMCtrl *swimctrl =3D &sys->ctrl; - - qbus_init(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev, NULL); - swimctrl->bus.ctrl =3D swimctrl; - - /* Default register set is IWM */ - memory_region_add_subregion(&swimctrl->swim, 0x0, &swimctrl->iwm); -} - -static const VMStateDescription vmstate_fdrive =3D { - .name =3D "fdrive", - .version_id =3D 1, - .minimum_version_id =3D 1, - .fields =3D (const VMStateField[]) { - VMSTATE_END_OF_LIST() - }, -}; - -static const VMStateDescription vmstate_swim =3D { - .name =3D "swim", - .version_id =3D 1, - .minimum_version_id =3D 1, - .fields =3D (const VMStateField[]) { - VMSTATE_INT32(mode, SWIMCtrl), - /* IWM mode */ - VMSTATE_INT32(iwm_switch, SWIMCtrl), - VMSTATE_UINT8(iwm_latches, SWIMCtrl), - VMSTATE_UINT8_ARRAY(iwmregs, SWIMCtrl, 8), - /* SWIM mode */ - VMSTATE_UINT8_ARRAY(ismregs, SWIMCtrl, 16), - VMSTATE_UINT8(swim_phase, SWIMCtrl), - VMSTATE_UINT8(swim_mode, SWIMCtrl), - /* Drives */ - VMSTATE_STRUCT_ARRAY(drives, SWIMCtrl, SWIM_MAX_FD, 1, - vmstate_fdrive, FDrive), - VMSTATE_END_OF_LIST() - }, -}; - -static const VMStateDescription vmstate_sysbus_swim =3D { - .name =3D "SWIM", - .version_id =3D 1, - .fields =3D (const VMStateField[]) { - VMSTATE_STRUCT(ctrl, Swim, 0, vmstate_swim, SWIMCtrl), - VMSTATE_END_OF_LIST() - } -}; - -static void sysbus_swim_class_init(ObjectClass *oc, const void *data) -{ - DeviceClass *dc =3D DEVICE_CLASS(oc); - - dc->realize =3D sysbus_swim_realize; - device_class_set_legacy_reset(dc, sysbus_swim_reset); - dc->vmsd =3D &vmstate_sysbus_swim; -} - -static const TypeInfo sysbus_swim_info =3D { - .name =3D TYPE_SWIM, - .parent =3D TYPE_SYS_BUS_DEVICE, - .instance_size =3D sizeof(Swim), - .instance_init =3D sysbus_swim_init, - .class_init =3D sysbus_swim_class_init, -}; - -static void swim_register_types(void) -{ - type_register_static(&sysbus_swim_info); - type_register_static(&swim_bus_info); - type_register_static(&swim_drive_info); -} - -type_init(swim_register_types) diff --git a/hw/block/trace-events b/hw/block/trace-events index 92d4bd9f17..be3915f201 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -94,14 +94,6 @@ m25p80_read_sfdp(void *s, uint32_t addr, uint8_t v) "[%p= ] Read SFDP 0x%"PRIx32"=3D m25p80_binding(void *s) "[%p] Binding to IF_MTD drive" m25p80_binding_no_bdrv(void *s) "[%p] No BDRV - binding to RAM" =20 -# swim.c -swim_ismctrl_read(int reg, const char *name, unsigned size, uint64_t value= ) "reg=3D%d [%s] size=3D%u value=3D0x%"PRIx64 -swim_ismctrl_write(int reg, const char *name, unsigned size, uint64_t valu= e) "reg=3D%d [%s] size=3D%u value=3D0x%"PRIx64 -swim_iwmctrl_read(int reg, const char *name, unsigned size, uint64_t value= ) "reg=3D%d [%s] size=3D%u value=3D0x%"PRIx64 -swim_iwmctrl_write(int reg, const char *name, unsigned size, uint64_t valu= e) "reg=3D%d [%s] size=3D%u value=3D0x%"PRIx64 -swim_switch_to_ism(void) "switch from IWM to ISM mode" -swim_switch_to_iwm(void) "switch from ISM to IWM mode" - # swim2.c swim2_mmio_read(uint64_t addr, unsigned size, int reg, const char *name, u= int64_t value, uint8_t mode, uint8_t setup, uint8_t phase, uint32_t fifo_co= unt) "addr=3D0x%"PRIx64" size=3D%u reg=3D%d [%s] value=3D0x%"PRIx64" mode= =3D0x%02x setup=3D0x%02x phase=3D0x%02x fifo=3D%u" swim2_mmio_write(uint64_t addr, unsigned size, int reg, const char *name, = uint64_t value, uint8_t mode, uint8_t setup, uint8_t phase, uint32_t fifo_c= ount) "addr=3D0x%"PRIx64" size=3D%u reg=3D%d [%s] value=3D0x%"PRIx64" mode= =3D0x%02x setup=3D0x%02x phase=3D0x%02x fifo=3D%u" diff --git a/include/hw/block/swim.h b/include/hw/block/swim.h deleted file mode 100644 index e8998f57c4..0000000000 --- a/include/hw/block/swim.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * QEMU Macintosh floppy disk controller emulator (SWIM) - * - * Copyright (c) 2014-2018 Laurent Vivier - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef SWIM_H -#define SWIM_H - -#include "hw/block/block.h" -#include "hw/core/sysbus.h" -#include "qom/object.h" - -#define SWIM_MAX_FD 2 - -typedef struct SWIMCtrl SWIMCtrl; - -#define TYPE_SWIM_DRIVE "swim-drive" -OBJECT_DECLARE_SIMPLE_TYPE(SWIMDrive, SWIM_DRIVE) - -struct SWIMDrive { - DeviceState qdev; - int32_t unit; - BlockConf conf; -}; - -#define TYPE_SWIM_BUS "swim-bus" -OBJECT_DECLARE_SIMPLE_TYPE(SWIMBus, SWIM_BUS) - -struct SWIMBus { - BusState bus; - struct SWIMCtrl *ctrl; -}; - -typedef struct FDrive { - SWIMCtrl *swimctrl; - BlockBackend *blk; - BlockConf *conf; -} FDrive; - -struct SWIMCtrl { - MemoryRegion swim; - MemoryRegion iwm; - MemoryRegion ism; - FDrive drives[SWIM_MAX_FD]; - int mode; - /* IWM mode */ - int iwm_switch; - uint8_t iwm_latches; - uint8_t iwmregs[8]; - /* SWIM mode */ - uint8_t ismregs[16]; - uint8_t swim_phase; - uint8_t swim_mode; - uint8_t swim_status; - uint8_t pram[16]; - uint8_t pram_idx; - SWIMBus bus; -}; - -#define TYPE_SWIM "swim" -OBJECT_DECLARE_SIMPLE_TYPE(Swim, SWIM) - -struct Swim { - SysBusDevice parent_obj; - SWIMCtrl ctrl; -}; -#endif --=20 2.53.0