From nobody Sun Jul 26 11:01:58 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=1782959436; cv=none; d=zohomail.com; s=zohoarc; b=LMAdJ7Ise6GrW4MC/0Q5oGcApRLK/p3itF1GENxJbAhKmbfir9CLVypNknWAuB5M7D3a/fLKyk99SobMmBL+lhG+/bvYjwiA5M3b/LSrD11F2m3OXFwxZOxW3ieMrH7N6x3ZSvqgsaydbXeatBxVebqZ11eFTzz+ONXJA/dO/JE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1782959436; 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=45FDTigWHJbPVebVikGe2L0vVKou1huAPlnrZAfu2mc=; b=Qmddl8eG8HebMjQRq5XrTiJl0V8KfyR5/MUn21fa+jaqiSZ4dcjVz2bhyzARUiudosORc0Jco1Pom3OMpR91zun0DWX4a/wu9pZs4kbhw8iYU0blxJAtUS+EkpLI7cUS/KbaqZs27KLWvWACiToJ10JwL3RRpVJxQdmyi7sbdzs= 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 1782959436619289.4164094246439; Wed, 1 Jul 2026 19:30:36 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wf7BU-0004A8-TT; Wed, 01 Jul 2026 22:29:48 -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 1wf7BT-00049S-Nd for qemu-devel@nongnu.org; Wed, 01 Jul 2026 22:29:47 -0400 Received: from ms-2004d-snip4-11.eps.apple.com ([57.103.74.171] helo=outbound.ms.icloud.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wf7BP-0002Pb-Hq for qemu-devel@nongnu.org; Wed, 01 Jul 2026 22:29:47 -0400 Received: from outbound.ms.icloud.com (unknown [127.0.0.2]) by p00-icloudmta-asmtp-us-west-3a-60-percent-7 (Postfix) with ESMTPS id C30AE1800343; Thu, 02 Jul 2026 02:29:36 +0000 (UTC) Received: from localhost.localdomain (unknown [17.57.154.37]) by p00-icloudmta-asmtp-us-west-3a-60-percent-7 (Postfix) with ESMTPSA id 3D854180011C; Thu, 02 Jul 2026 02:29:35 +0000 (UTC) X-ICL-RepId: 019f20a9-0969-7de9-a6c6-cde098884b5b X-ICL-Out-Info: HUtFAUMHWwJACUgBTUQeDx5WFlZNRAJCTQ5JHVoHRQNGD1YCWgZLVxQEFVsHVghdEkoXXS1aDhwTVhUTC1NWXxUXG1wAFxlRTQhIEwVSB11NVg1HD1geXBQXFUBDXgheH0wcHQ5YBhIATQoONgZZBV4JVgNDBTYSFF1FXggZCF0dGRVaCQpXAEALTgNaB1kCQgtBAVoZXwRFCEgeXxoGWhpYG1wPRARcMFQdHQ5YBgxQTQFDCAoCURxWDVc= Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=me.com; s=1a1hai; t=1782959378; x=1785551378; bh=45FDTigWHJbPVebVikGe2L0vVKou1huAPlnrZAfu2mc=; h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme; b=iwHxwaQ9rdy/hFNXwlK63U5hGPYeD5mBI9wJezjzpKBX9mUrR3UX5GppnRUsx7lTJa2WEkRxAuQuvyFM64dSKkwgczhHbjjsnrRU7a41pLM/Vf1Ch4EDMI/GttyNn8NpT8YQRyMA3dG9/GE0NTNo4pRVWWhgcMsqMUxkJuQuQq9Ac//K9sCZ4OV5zSkNnY0OGMn8XvtKUT7veX0l3VNcvF8IYFltZ64iljOI9Ch6IVk5yTfYQe9dpf1UvV8Bd4vIWkmDnBH29A9CWAiLZfTeJpQKpSo42CElNRV+gJ7R6vcvSPJbY3ItBidGzbjcASWUgC7NhuIJtBQePfNIWd8AEw== From: Matt Jacobson To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, Hanna Reitz , Peter Maydell , Kevin Wolf , Laurent Vivier , Mark Cave-Ayland , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , BALATON Zoltan , Matt Jacobson Subject: [PATCH v3 1/5] hw/block: add Sony/Apple "SuperDrive" diskette drive Date: Wed, 1 Jul 2026 22:29:23 -0400 Message-ID: <20260702022927.46510-2-mhjacobson@me.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260702022927.46510-1-mhjacobson@me.com> References: <20260702022927.46510-1-mhjacobson@me.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-ORIG-GUID: ocS-Pslz7ZA131Gqt9u8m3eR6_r-s_ne X-Proofpoint-GUID: ocS-Pslz7ZA131Gqt9u8m3eR6_r-s_ne X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzAyMDAyMyBTYWx0ZWRfX6x8qQE+N1pgM TT4eL2RBnOhRHtmemdYowCTsXzPr9nB2muJsptJcGu7tEEMVgkWN/IqucSGsSttv/D/sPoxH38e ij7u8fA8yWzx/jap6tzJytwHcA3syM8h3Hjecy7aKrcwhYaztuFGAf+nAK+fJMdQPq1G3JeZ+ry jG8mUexGA78z4HEwgsJx+l01rcfkkYBDb4wX4KwB3S7Zi69WX6PnZPDCTGIkNtWWHei6MMjty8j pODuaIADEFPMythYFQvhlK9iEiBG0WPsH5s5zBreIzuWxpnJLSBZQA6Du9TDTGIDR+eqVD3SdVK I15gWlGdJ4aaldzUaoN 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.74.171; envelope-from=mhjacobson@me.com; helo=outbound.ms.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=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: 1782959439619158500 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 (720 kB and 1440 kB) diskettes. Implement an emulated version, to be controlled by a forthcoming SWIM2 device. For GCR, the emulated drive encodes the BlockBackend's data in the Apple GCR format and exposes it to the controller via sony_drive_read_byte(). The bytes are decoded not by the controller but rather in software by the Macintosh ".Sony" driver. The drive also adds the sync bytes and addressing marks that software expects. For MFM, much less encoding is needed. The sector data passes through from the BlockBackend unmodified, and the drive only adds sync bytes and addresssing marks. Signed-off-by: Matt Jacobson Reviewed-by: BALATON Zoltan --- hw/block/meson.build | 2 +- hw/block/sony_superdrive.c | 1032 ++++++++++++++++++++++++++++ hw/block/trace-events | 10 + include/hw/block/sony_superdrive.h | 55 ++ 4 files changed, 1098 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..196bd3b0dc --- /dev/null +++ b/hw/block/sony_superdrive.c @@ -0,0 +1,1032 @@ +/* + * 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 "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", +}; + +static const char *sony_drive_cmd_name(const uint8_t selector) +{ + if (selector < ARRAY_SIZE(sony_drive_cmd_names) && + sony_drive_cmd_names[selector]) { + return sony_drive_cmd_names[selector]; + } else { + return "UNKNOWN"; + } +} + +#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 bool sony_gcr_decode_byte(const uint8_t value, uint8_t *const out) +{ + if (value < 0x96) { + return false; + } + + const uint8_t result =3D sony_gcr_decode_table[value - 0x96]; + if (result =3D=3D 0xff) { + return false; + } + + *out =3D result; + return true; +} + +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 bool sony_gcr_decode_bytes(const uint8_t *const in, uint8_t *const = out, + const size_t len) +{ + bool ok =3D true; + + for (size_t i =3D 0; i < len; i++) { + if (!sony_gcr_decode_byte(in[i], &out[i])) { + out[i] =3D 0; + ok =3D false; + } + } + + return ok; +} + +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; + + assert(idx <=3D sizeof drive->xfer_buffer); + 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; + if (!sony_gcr_decode_bytes(encoded_buffer, encoded_data, + SONY_GCR_ENCODED_LENGTH)) { + return false; + } + sony_6and2_unpack(encoded_data, sizeof encoded_data, + sector_data, sizeof sector_data); + + uint8_t encoded_checksum[4]; + uint8_t checksum[3]; + if (!sony_gcr_decode_bytes(encoded_buffer + SONY_GCR_ENCODED_LENGTH, + encoded_checksum, 4)) { + return false; + } + 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; + + assert(idx <=3D sizeof drive->xfer_buffer); + 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); + } + + trace_sony_drive_persist(drive, drive->xfer_lba, result); + } + + 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(sectors !=3D 0); + + const uint8_t sector =3D drive->current_sector; + drive->current_sector =3D (drive->current_sector + 1) % sectors; + + if (!sony_validate_address(drive, track, head, sector)) { + trace_sony_drive_prepare(drive, track, head, sector, + drive->gcr_encode, 0); + return false; + } + + 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_name(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: + /* + * This and SET_GCR tell the drive whether to use constant or vari= able + * spindle speed. We just store the value for later retrieval + * (SONY_REG_GCR). + */ + 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_uses_gcr_encoding(const SonyDrive *const drive) +{ + return drive->gcr_encode; +} + +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); + + *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; + } + + if (!drive->xfer_active) { + return false; + } else { + const uint16_t offset =3D drive->xfer_write_position; + if (offset >=3D drive->xfer_length) { + return false; + } + + 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 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 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 1440) { + /* 720 kB (double-sided MFM). */ + drive->cylinders =3D 80; + drive->sectors_per_track =3D 9; + drive->double_sided =3D true; + drive->gcr_encode =3D false; + } 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 dbaa5ca6cb..c5e021738c 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -111,3 +111,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, bool success) "drive=3D%p lb= a=3D0x%"PRIx64" success=3D%d" +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..e15dd433e7 --- /dev/null +++ b/include/hw/block/sony_superdrive.h @@ -0,0 +1,55 @@ +/* + * 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 "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_uses_gcr_encoding(const SonyDrive *drive); +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.54.0 From nobody Sun Jul 26 11:01:58 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=1782959485; cv=none; d=zohomail.com; s=zohoarc; b=jdtvAlfN73YHTFKLMb+vXOvsh1aaeWyOH37s0jPj/bZTusxcRsZhDnnDeZWCYFORvIruhPKDWJVFTgmgGmvBpcyDd+IZ4zV6QHqpQfhXQNvklF+ibpk7V0BXEVJ6g10TQvE9fntS7JgrRNoM3sajh4XRde4q7ERd4VsM3KxzGK0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1782959485; 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=COq6H4N+kGc/Y6SrSsYP8fpipvMlqT9yC11jsb2+MGU=; b=TEzq9i3chgExguptrj1oNfkQb4x3hQ9rYGq3DJMEekqn76+kH/z0fSYlaggMxFBc3/CSTcyRlmrQgZe+FnESTfkbhz6+7gls0glh8zaUoWz02U90VfPmJmWMM/unmEKRJQYqVhkYfZ6A/YY2tyMznT0RmwQQ7/BcfFPxGE/oxTg= 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 1782959485242823.4146203125661; Wed, 1 Jul 2026 19:31:25 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wf7BX-0004BY-2j; Wed, 01 Jul 2026 22:29: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 1wf7BW-0004BM-2C for qemu-devel@nongnu.org; Wed, 01 Jul 2026 22:29:50 -0400 Received: from ms-2002h-snip4-5.eps.apple.com ([57.103.74.38] helo=outbound.ms.icloud.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wf7BR-0002SD-Fi for qemu-devel@nongnu.org; Wed, 01 Jul 2026 22:29:49 -0400 Received: from outbound.ms.icloud.com (unknown [127.0.0.2]) by p00-icloudmta-asmtp-us-west-3a-60-percent-7 (Postfix) with ESMTPS id 61D7F18000B0; Thu, 02 Jul 2026 02:29:38 +0000 (UTC) Received: from localhost.localdomain (unknown [17.57.154.37]) by p00-icloudmta-asmtp-us-west-3a-60-percent-7 (Postfix) with ESMTPSA id 8B22B180013D; Thu, 02 Jul 2026 02:29:36 +0000 (UTC) X-ICL-RepId: 019f20a9-0f76-7975-9158-97c72dae7979 X-ICL-Out-Info: HUtFAUMHWwJACUgBTUQeDx5WFlZNRAJCTQ5JHVoHRQNGD1YCWgZLVxQEFVsHVghdEkoXXS1aDhwTVhUTC1NWXxUXG1wAFxlRTQhIEwVSB11NVg1HD1geXBQXFUBDXgheH0wcHQ5YBhIATQoONgZZBV4JVgNDBTYSFF1FXggZCF0dGRVaCQpXAEALTgNaB1kCQgtBAVoZXwRFCEgeXhoGWhpYG1wPRARcMFQdHQ5YBgxQTQFDCAoCURxWDVc= Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=me.com; s=1a1hai; t=1782959381; x=1785551381; bh=COq6H4N+kGc/Y6SrSsYP8fpipvMlqT9yC11jsb2+MGU=; h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme; b=RNWblcRy83jrhwB2UCXoY/Gbd82sH22n4Z5rad/eWi62/isPkZT4NSYn7UTTN8m5qlNj/fyXtJoZq3u8qQreJlQ7YZI88jXFJB9k3RTiHwAVJhBS3k6O8E1CJ0zr1DfwUK05fd2Tpo4J7UHvrGLs1H3WGBByMt1EltePRS+7CRYZqlVXyBi1P6pNg2mUPDWl4fEIdGI+I+El3lhmcmNcG2jkwrh84dClOzX1BoMXHboel4NplHySLYsBEYt0dyRuByxibT7pq+SQtkI0inyJ9AVUtEcbEP6xdP/QZGCI5Z0YjwuOHGggL0Anl87VMiPkOOTrxDvA7IED8GwfiNI+LA== From: Matt Jacobson To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, Hanna Reitz , Peter Maydell , Kevin Wolf , Laurent Vivier , Mark Cave-Ayland , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , BALATON Zoltan , Matt Jacobson Subject: [PATCH v3 2/5] fifo32: add fifo32_peek() Date: Wed, 1 Jul 2026 22:29:24 -0400 Message-ID: <20260702022927.46510-3-mhjacobson@me.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260702022927.46510-1-mhjacobson@me.com> References: <20260702022927.46510-1-mhjacobson@me.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-ORIG-GUID: VFi0Q6RAHQLuOp_DtyXVQ9jVSACJa84m X-Proofpoint-GUID: VFi0Q6RAHQLuOp_DtyXVQ9jVSACJa84m X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzAyMDAyMyBTYWx0ZWRfX7NlqBUsFwBu2 lDcJq1jNBuDmEPy2d/hYxBL2XHW+M8AXHM5Dzfw34zs/C3T86/M2Lg6xQebnK3jq/utsWnEMobj f65MquM2pnWpBUcu8F+INDw5pc2kOGsdDAxR9Qsuqv5JycUTq1bOObo6i5u9+grlfHDqtbMnM9R GmCc2M+4Ka2SDKpAZoVGkMIKCkZLxpRm5dQZSUA1OVbrBNzhHwfMS3Ly2KJL0BPmE/RpLBNnVgj eSrBRM0KS3rxQIsTs7N7BypO/3Dv9AkHBSXzLXpMkuaOlvBWXXMZ2tMQyaXffdv6LJPEwCihuYJ IFoih5ww2ZWSDKNX6nj 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.74.38; envelope-from=mhjacobson@me.com; helo=outbound.ms.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: 1782959487063158500 Content-Type: text/plain; charset="utf-8" Add fifo32_peek(), which, like the other Fifo32 functions, ultimately just delegates to the Fifo8 code. Adjust fifo32_pop() to use ldl_le_p() too. Signed-off-by: Matt Jacobson --- include/qemu/fifo32.h | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/include/qemu/fifo32.h b/include/qemu/fifo32.h index 4e9fd1b5ef..4e8b739a1e 100644 --- a/include/qemu/fifo32.h +++ b/include/qemu/fifo32.h @@ -15,6 +15,7 @@ #ifndef FIFO32_H #define FIFO32_H =20 +#include "qemu/bswap.h" #include "qemu/fifo8.h" =20 typedef struct { @@ -117,6 +118,25 @@ 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 read =3D fifo8_peek_buf(&fifo->fifo, bytes, sizeof(bytes)); + assert(read =3D=3D sizeof(bytes)); + + return ldl_le_p(bytes); +} + /** * fifo32_pop: * @fifo: fifo to pop from @@ -130,14 +150,11 @@ static inline void fifo32_push_all(Fifo32 *fifo, cons= t uint32_t *data, =20 static inline uint32_t fifo32_pop(Fifo32 *fifo) { - uint32_t ret =3D 0; - int i; - - for (i =3D 0; i < sizeof(uint32_t); i++) { - ret |=3D (fifo8_pop(&fifo->fifo) << (i * 8)); - } + uint8_t bytes[sizeof(uint32_t)]; + uint32_t popped =3D fifo8_pop_buf(&fifo->fifo, bytes, sizeof(bytes)); + assert(popped =3D=3D sizeof(bytes)); =20 - return ret; + return ldl_le_p(bytes); } =20 /** --=20 2.54.0 From nobody Sun Jul 26 11:01:58 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=1782959478; cv=none; d=zohomail.com; s=zohoarc; b=RuxFZ2MjkBBSSXclKL73S8XiZvBYs2qNXy1RE+hS3e4WnktsTxYE+q4J2eeOxklR1E2xUcYMD30zj9ZvH9/A7GCAl5LD6SVHdBsbcXn5FNcFp4CG5s8q5QKXwCoEbv56KnSDvUQcyzJBY2Q2HYzFZr/ObXmX5AIQHDzAAGAoQnA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1782959478; 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=3cJSml2DsvE29YdH9/gzzlwBcOeRgXqMGd0Mln4427k=; b=n18ndzSSh1T0lQIez4vREMD6HlZqet0eW+buwBY1Td10WHt13XG2CtqyCr1vNUAPILX199E0gdKlq2+48grDUHj3VxS2SY00RO1PEFkUlz6w9UN4COCKlhFso6DhmEtGYqb4+YUQCQbO5677r0EAAOQpXnYbayidETVHYIJRlQQ= 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 1782959478828208.57908807279512; Wed, 1 Jul 2026 19:31:18 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wf7BW-0004BX-TG; Wed, 01 Jul 2026 22:29:50 -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 1wf7BU-00049s-Bt for qemu-devel@nongnu.org; Wed, 01 Jul 2026 22:29:48 -0400 Received: from ms-2002g-snip4-8.eps.apple.com ([57.103.74.31] helo=outbound.ms.icloud.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wf7BQ-0002Ui-0I for qemu-devel@nongnu.org; Wed, 01 Jul 2026 22:29:48 -0400 Received: from outbound.ms.icloud.com (unknown [127.0.0.2]) by p00-icloudmta-asmtp-us-west-3a-60-percent-7 (Postfix) with ESMTPS id AC2011800117; Thu, 02 Jul 2026 02:29:39 +0000 (UTC) Received: from localhost.localdomain (unknown [17.57.154.37]) by p00-icloudmta-asmtp-us-west-3a-60-percent-7 (Postfix) with ESMTPSA id D68741800112; Thu, 02 Jul 2026 02:29:37 +0000 (UTC) X-ICL-RepId: 019f20a9-1497-7a78-ae64-db0438e00b34 X-ICL-Out-Info: HUtFAUMHWwJACUgBTUQeDx5WFlZNRAJCTQ5JHVoHRQNGD1YCWgZLVxQEFVsHVghdEkoXXS1aDhwTVhUTC1NWXxUXG1wAFxlRTQhIEwVSB11NVg1HD1geXBQXFUBDXgheH0wcHQ5YBhIATQoONgZZBV4JVgNDBTYSFF1FXggZCF0dGRVaCQpXAEALTgNaB1kCQgtBAVoZXwRFCEgeWRoGWhpYG1wPRARcMFQdHQ5YBgxQTQFDCAoCURxWDVc= Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=me.com; s=1a1hai; t=1782959382; x=1785551382; bh=3cJSml2DsvE29YdH9/gzzlwBcOeRgXqMGd0Mln4427k=; h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme; b=nOSqV2E0RbzWbYJqO1P67tC7Cp7OKLsX6u/Y0ZGeLRKfmbu0EYBs/3vHIt2+9UmXkq4iFEs/ehTz5OMaU0FmuTrflFeeLmW/nuyghVcZo3pADnyI+Nz11MViYLj/FehnjoD4Mp6P+XemRUjS1/1n0RxLQXLtN9YtMDa6+GN97yPSk4miBBYjNd4VUrunYQEoo9FRYA7OsgM2NbUhCzq32nA1UMTciCJaG+B6FJOMHzV0Hih7eWtL/w1OE74/v7zC5dGLB70xs3ETQTSj2Te0ww001sXoYgIAt1vUT7GoDnPhqWqiCGXjay2WZC3iEMvEA6CxDIHwhh0oj+IKp9cddA== From: Matt Jacobson To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, Hanna Reitz , Peter Maydell , Kevin Wolf , Laurent Vivier , Mark Cave-Ayland , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , BALATON Zoltan , Matt Jacobson Subject: [PATCH v3 3/5] hw/block: add SWIM2 floppy controller for q800 Date: Wed, 1 Jul 2026 22:29:25 -0400 Message-ID: <20260702022927.46510-4-mhjacobson@me.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260702022927.46510-1-mhjacobson@me.com> References: <20260702022927.46510-1-mhjacobson@me.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzAyMDAyMyBTYWx0ZWRfX9itk9lrpZukF 5yp5bI3yAc+ViDBHAYQnARfRDiietGS+H+hZDj6gQa+GM3dMOgD6v11NrGc87aGSAywnEx4t4Tg FGj1+QGBJArqEFkfRGkVopFitrq0X4VHuWW2VjsPlhO76qk2Lcq1hnkBiV22LGK2FM9b+ssv0CH iDHt0w8vElA4allNp7QZVmk05hh5K7HOOGnyXPJOCLAEF/y6U2pYHV2IJ0nysnr32OH+FFoBkHu YiwF//9OMNjo1ppT/YBvHxbuRsgl05a3wKZl8bWwbNGoembJZKq/xoDGXmuEE3AgDoJwKeSMUgc 6U96+DwkVEP/YJ4lpyQ X-Proofpoint-GUID: z1iU0G4no8rz_5Qg-SkSoSeaMYLKm0sY X-Proofpoint-ORIG-GUID: z1iU0G4no8rz_5Qg-SkSoSeaMYLKm0sY 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.74.31; envelope-from=mhjacobson@me.com; helo=outbound.ms.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=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: 1782959480897158500 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 | 711 +++++++++++++++++++++++++++++++++++++++ hw/block/trace-events | 9 + include/hw/block/swim2.h | 58 ++++ 4 files changed, 779 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..4ac486904f --- /dev/null +++ b/hw/block/swim2.c @@ -0,0 +1,711 @@ +/* + * 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 "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's transfer stream is GCR en= coded. + * A/UX hits this codepath, because it tries to read in MFM mode before + * falling back to GCR. + */ + if (ctrl->wait_for_mark && sony_drive_uses_gcr_encoding(drive)) { + 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_types[] =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, + }, + { + .name =3D TYPE_SWIM2_BUS, + .parent =3D TYPE_BUS, + .instance_size =3D 0, + .class_init =3D swim2_bus_class_init, + }, + { + .name =3D TYPE_SWIM2_DRIVE, + .parent =3D TYPE_DEVICE, + .instance_size =3D sizeof(SWIM2Drive), + .class_init =3D swim2_drive_class_init, + }, +}; + +DEFINE_TYPES(swim2_types) diff --git a/hw/block/trace-events b/hw/block/trace-events index c5e021738c..c5793c7da8 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -112,6 +112,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, bool success) "drive=3D%p lb= a=3D0x%"PRIx64" success=3D%d" diff --git a/include/hw/block/swim2.h b/include/hw/block/swim2.h new file mode 100644 index 0000000000..b40af25a9f --- /dev/null +++ b/include/hw/block/swim2.h @@ -0,0 +1,58 @@ +/* + * 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 "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.54.0 From nobody Sun Jul 26 11:01:58 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=1782959481; cv=none; d=zohomail.com; s=zohoarc; b=CJyNVELrZVE2cu55f31EvCVUsruTvd3lALmd9MpBvsqijvY446VIDLc1akOCNHNtTC4/cISvxbquGaPahB4W9/tojq5UokjXyXGtaFaLLDaeMABgElRv38hljraCkAiFU6osJ0SO2S9W0dOOBW/bA8n2/UXpwBIHFby8C0s3ZfM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1782959481; 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=slZFG3XWsAGvW3dJCKCnn4VgnlVItIo6kSH2q5+xM6A=; b=DnfkicBVgqn8HL2OmbJxCDeTKoFbSak0cfPCECTFeBmz1gKaDqbLCD+37czSBbkGc3oYB/VNNW5PjIPrUF/0hX5kakPlohAEB43Uhz5Omc/da9XA5w/Iusve9sSIVAbpwOrM73pDDuoWxmCPf4MDs+0vZKaIjI+YG+c4EoGQjtY= 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 1782959481317701.2613829113052; Wed, 1 Jul 2026 19:31:21 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wf7BW-0004BV-QH; Wed, 01 Jul 2026 22:29:50 -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 1wf7BU-0004AC-S6 for qemu-devel@nongnu.org; Wed, 01 Jul 2026 22:29:48 -0400 Received: from ms-2002c-snip4-10.eps.apple.com ([57.103.74.72] helo=outbound.ms.icloud.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wf7BQ-0002WO-R6 for qemu-devel@nongnu.org; Wed, 01 Jul 2026 22:29:48 -0400 Received: from outbound.ms.icloud.com (unknown [127.0.0.2]) by p00-icloudmta-asmtp-us-west-3a-60-percent-7 (Postfix) with ESMTPS id DCCC9180011F; Thu, 02 Jul 2026 02:29:40 +0000 (UTC) Received: from localhost.localdomain (unknown [17.57.154.37]) by p00-icloudmta-asmtp-us-west-3a-60-percent-7 (Postfix) with ESMTPSA id 274C41800133; Thu, 02 Jul 2026 02:29:39 +0000 (UTC) X-ICL-RepId: 019f20a9-193f-76d0-a759-46feb330b00b X-ICL-Out-Info: HUtFAUMHWwJACUgBTUQeDx5WFlZNRAJCTQ5JHVoHRQNGD1YCWgZLVxQEFVsHVghdEkoXXS1aDhwTVhUTC1NWXxUXG1wAFxlRTQhIEwVSB11NVg1HD1geXBQXFUBDXgheH0wcHQ5YBhIATQoONgZZBV4JVgNDBTYSFF1FXggZCF0dGRVaCQpXAEALTgNaB1kCQgtBAVoZXwRFCEgeWBoGWhpYG1wPRARcMFQdHQ5YBgxQTQFDCAoCURxWDVc= Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=me.com; s=1a1hai; t=1782959383; x=1785551383; bh=slZFG3XWsAGvW3dJCKCnn4VgnlVItIo6kSH2q5+xM6A=; h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme; b=IgdcQYVrATgunkNx7IaBe9oX19ZOJP1JRQtQ/xhDvKofzUJcoaE1cOHeatGjGBpqsub5VMzjwSwJ0TcCa3nTJD6iEYTx8rx1L4EHSS5gzFR39sSbnLSG8Lb4x2YeafaKZbV5AxfgMsK6lSxIZmmabKV55ImD9ixME+YGqWO2k2hiXHf59TdyMo+f4WKRDUpgvhKEMEKd6S8lnOd2Z5xjUPYIh0wAP8PiclZoP6fnjRcf/vXWiY9+9mP34tooTsqvdWAU+IZCdpwnzm0YKzY9Dfgo4XZ6yOzIKvubrtxDJbDXCA4km2A9xbqBufngeW2E17IVFKNr4QZCZwb/XKfoUA== From: Matt Jacobson To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, Hanna Reitz , Peter Maydell , Kevin Wolf , Laurent Vivier , Mark Cave-Ayland , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , BALATON Zoltan , Matt Jacobson Subject: [PATCH v3 4/5] hw/m68k: add working floppy controller for q800 Date: Wed, 1 Jul 2026 22:29:26 -0400 Message-ID: <20260702022927.46510-5-mhjacobson@me.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260702022927.46510-1-mhjacobson@me.com> References: <20260702022927.46510-1-mhjacobson@me.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-GUID: nwHQyttg-tzRgX-12fmOD74IpAP8pnFG X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzAyMDAyMyBTYWx0ZWRfX1ujW4/kv33zl GWDBqJSaC/dhttGF4G9cxmh1oY2AS0hnLYtUlAbZ/cGZjkI/P1nH1upuR3ncRbBEvLg5w2rJ6Np kDHnvLdsQ2ulgjnoYxCav64cVs9RxN/yx8u8Y9JMFUVSXSeH6vFmoq1t8H6U2QZ7a+EP8/YV4ZJ f1v/AhfeEoOzk7KYHKgg6HWUdO+NkMo/Tz/RzsIX02DX2F/UEbWSqMx1IZO9tnTEcGjr3Mfiz/r XdE3JUdaqyqk5VlYaYM7S8iWjF0sjBIcwTn12ArYTaV3bzg7G8ckWTAjP0X+OASSrpwjul7C/dV o3vUJmvuDdkYUZmF+fX X-Proofpoint-ORIG-GUID: nwHQyttg-tzRgX-12fmOD74IpAP8pnFG 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.74.72; envelope-from=mhjacobson@me.com; helo=outbound.ms.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: 1782959483101158500 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 | 3 +++ 4 files changed, 24 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..e23735b47c 100644 --- a/include/hw/misc/mac_via.h +++ b/include/hw/misc/mac_via.h @@ -13,6 +13,7 @@ #include "hw/core/sysbus.h" #include "hw/misc/mos6522.h" #include "hw/input/adb.h" +#include "hw/block/swim2.h" #include "qom/object.h" =20 =20 @@ -77,6 +78,8 @@ struct MOS6522Q800VIA1State { =20 /* SETUPTIMEK hack */ int timer_hack_state; + + SWIM2State *swim; }; =20 =20 --=20 2.54.0 From nobody Sun Jul 26 11:01:58 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=1782959465; cv=none; d=zohomail.com; s=zohoarc; b=MRhwbq3GeQtzs94kaTUn7vyRedAdOW8lOzphV9VrfR2FUs3BucFcFqvxTQyMssnon+2MZwPGkUvs0BM/My3t7Ce9hzFlwXCwrspfOQmz8I58ZivC/f3ElHGUDkwI+dvFbNzUEvM4MoJ52eMBSGVDD9ja7z37OB+JpJJRfyql0h8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1782959465; 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=Eu3SVbJYY/o3DOHB5BtYg9ft3oXxE4t6MxAZkiuoT9o=; b=Yr7Iv2+RGTKGmzgoEjtB0R67QUnqrsmQJhlXrKen/8zgXMrAR2lzClwuozoYuJrdwXlCbmjn1Y5OuHg3MkR/qdKNAJNymBQwnZNFBDPUnUVLAFdBrcpVJr44ByxRXz485EPWAD6i4OmYxBAuzb82Vk/rC+vONuW08BGt9o9ydoI= 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 1782959465677616.9109167974315; Wed, 1 Jul 2026 19:31:05 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wf7BZ-0004DE-HW; Wed, 01 Jul 2026 22:29:53 -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 1wf7BX-0004CS-R0 for qemu-devel@nongnu.org; Wed, 01 Jul 2026 22:29:51 -0400 Received: from ms-2002f-snip4-11.eps.apple.com ([57.103.74.63] helo=outbound.ms.icloud.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wf7BU-0002dw-Dg for qemu-devel@nongnu.org; Wed, 01 Jul 2026 22:29:51 -0400 Received: from outbound.ms.icloud.com (unknown [127.0.0.2]) by p00-icloudmta-asmtp-us-west-3a-60-percent-7 (Postfix) with ESMTPS id 28FDC1800108; Thu, 02 Jul 2026 02:29:41 +0000 (UTC) Received: from localhost.localdomain (unknown [17.57.154.37]) by p00-icloudmta-asmtp-us-west-3a-60-percent-7 (Postfix) with ESMTPSA id 56C19180011C; Thu, 02 Jul 2026 02:29:40 +0000 (UTC) X-ICL-RepId: 019f20a9-1e33-7107-a32c-fec4d6d50bf0 X-ICL-Out-Info: HUtFAUMHWwJACUgBTUQeDx5WFlZNRAJCTQ5JHVoHRQNGD1YCWgZLVxQEFVsHVghdEkoXXS1aDhwTVhUTC1NWXxUXG1wAFxlRTQhIEwVSB11NVg1HD1geXBQXFUBDXgheH0wcHQ5YBhIATQoONgZZBV4JVgNDBTYSFF1FXggZCF0dGRVaCQpXAEALTgNaB1kCQgtBAVoZXwRFCEgeWxoGWhpYG1wPRARcMFQdHQ5YBgxQTQFDCAoCURxWDVc= Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=me.com; s=1a1hai; t=1782959385; x=1785551385; bh=Eu3SVbJYY/o3DOHB5BtYg9ft3oXxE4t6MxAZkiuoT9o=; h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme; b=GNT3kU/O46AHDuFVbheIuhFVSRm1r9ccHGal9z9v8bKLTM1JMNl41MXkSP7mVCr/qZrQq2KY2rMxE8aKY6T34qZL3dLosaNK6VnDeF2vOj3Rk+SnNW+9sPiMQvmMzPaa2mhbkOTDXuuYZ+WynyotgCUxmfsbXgnMk08wG60jy3A190lmFnoH2v8Z499UWAWCUNJ21ThMuFnhcImKj0IyyUuLNoi/q1gESKVDsTlvTB62fCcUKjCD92K4otQy+lDP1hIpnZR6fiAYmuq3UexFbTGSrSNkiwrU2CdK6skz9q1S1lk+eBbJF7Fyln6ZoyqQPOw+Vv8Juhs9GMhYtxeWew== From: Matt Jacobson To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, Hanna Reitz , Peter Maydell , Kevin Wolf , Laurent Vivier , Mark Cave-Ayland , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , BALATON Zoltan , Matt Jacobson Subject: [PATCH v3 5/5] hw/block: remove unused SWIM1 floppy controller stub Date: Wed, 1 Jul 2026 22:29:27 -0400 Message-ID: <20260702022927.46510-6-mhjacobson@me.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260702022927.46510-1-mhjacobson@me.com> References: <20260702022927.46510-1-mhjacobson@me.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-GUID: Gde-rbctyKRdPvhq9dwg_O7Yf-whdtbn X-Proofpoint-ORIG-GUID: Gde-rbctyKRdPvhq9dwg_O7Yf-whdtbn X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzAyMDAyMyBTYWx0ZWRfX643Zvl68tH0J e909MzL4Jt1xbf1fwV5oB/mPGQJTixQJbVVtWPQMSiadCJPh24rkXeV8+CKrHZBDXjDdIuXKBLl 7VKjUotdO1QE/lNOKtSeZLewgyMoYjHVq4pY0uLYK6Rbzg5X600Y/WpuzFnA8lCxQ4f+/XDxST6 OMgQhwditUuQDkAw6H2DzXl0/pPk59A8mMJmZQvEzl/Uhfg/vmArvso9Sfj+p40MjNbgpeUeo6l VbnyeAD8tUwzQo56Gsa3nAtkcrlCoIh6v3jIg72LnoVHFfaFIIeToYwYrUo6xPeG5gdumZpUj/C X2SqIniHvILyhOcH3/+ 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.74.63; envelope-from=mhjacobson@me.com; helo=outbound.ms.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_H3=0.001, RCVD_IN_MSPIKE_WL=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: 1782959467062158500 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 93df53d87f..55da29e0cf 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 c5793c7da8..47c4e663ef 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -104,14 +104,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.54.0