qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

swim.c (13324B)


      1 /*
      2  * QEMU Macintosh floppy disk controller emulator (SWIM)
      3  *
      4  * Copyright (c) 2014-2018 Laurent Vivier <laurent@vivier.eu>
      5  *
      6  * This work is licensed under the terms of the GNU GPL, version 2.  See
      7  * the COPYING file in the top-level directory.
      8  *
      9  * Only the basic support: it allows to switch from IWM (Integrated WOZ
     10  * Machine) mode to the SWIM mode and makes the linux driver happy.
     11  */
     12 
     13 #include "qemu/osdep.h"
     14 #include "qemu/main-loop.h"
     15 #include "qapi/error.h"
     16 #include "sysemu/block-backend.h"
     17 #include "hw/sysbus.h"
     18 #include "migration/vmstate.h"
     19 #include "hw/block/block.h"
     20 #include "hw/block/swim.h"
     21 #include "hw/qdev-properties.h"
     22 
     23 /* IWM registers */
     24 
     25 #define IWM_PH0L                0
     26 #define IWM_PH0H                1
     27 #define IWM_PH1L                2
     28 #define IWM_PH1H                3
     29 #define IWM_PH2L                4
     30 #define IWM_PH2H                5
     31 #define IWM_PH3L                6
     32 #define IWM_PH3H                7
     33 #define IWM_MTROFF              8
     34 #define IWM_MTRON               9
     35 #define IWM_INTDRIVE            10
     36 #define IWM_EXTDRIVE            11
     37 #define IWM_Q6L                 12
     38 #define IWM_Q6H                 13
     39 #define IWM_Q7L                 14
     40 #define IWM_Q7H                 15
     41 
     42 /* SWIM registers */
     43 
     44 #define SWIM_WRITE_DATA         0
     45 #define SWIM_WRITE_MARK         1
     46 #define SWIM_WRITE_CRC          2
     47 #define SWIM_WRITE_PARAMETER    3
     48 #define SWIM_WRITE_PHASE        4
     49 #define SWIM_WRITE_SETUP        5
     50 #define SWIM_WRITE_MODE0        6
     51 #define SWIM_WRITE_MODE1        7
     52 
     53 #define SWIM_READ_DATA          8
     54 #define SWIM_READ_MARK          9
     55 #define SWIM_READ_ERROR         10
     56 #define SWIM_READ_PARAMETER     11
     57 #define SWIM_READ_PHASE         12
     58 #define SWIM_READ_SETUP         13
     59 #define SWIM_READ_STATUS        14
     60 #define SWIM_READ_HANDSHAKE     15
     61 
     62 #define REG_SHIFT               9
     63 
     64 #define SWIM_MODE_IWM  0
     65 #define SWIM_MODE_SWIM 1
     66 
     67 /* bits in phase register */
     68 
     69 #define SWIM_SEEK_NEGATIVE   0x074
     70 #define SWIM_STEP            0x071
     71 #define SWIM_MOTOR_ON        0x072
     72 #define SWIM_MOTOR_OFF       0x076
     73 #define SWIM_INDEX           0x073
     74 #define SWIM_EJECT           0x077
     75 #define SWIM_SETMFM          0x171
     76 #define SWIM_SETGCR          0x175
     77 #define SWIM_RELAX           0x033
     78 #define SWIM_LSTRB           0x008
     79 #define SWIM_CA_MASK         0x077
     80 
     81 /* Select values for swim_select and swim_readbit */
     82 
     83 #define SWIM_READ_DATA_0     0x074
     84 #define SWIM_TWOMEG_DRIVE    0x075
     85 #define SWIM_SINGLE_SIDED    0x076
     86 #define SWIM_DRIVE_PRESENT   0x077
     87 #define SWIM_DISK_IN         0x170
     88 #define SWIM_WRITE_PROT      0x171
     89 #define SWIM_TRACK_ZERO      0x172
     90 #define SWIM_TACHO           0x173
     91 #define SWIM_READ_DATA_1     0x174
     92 #define SWIM_MFM_MODE        0x175
     93 #define SWIM_SEEK_COMPLETE   0x176
     94 #define SWIM_ONEMEG_MEDIA    0x177
     95 
     96 /* Bits in handshake register */
     97 
     98 #define SWIM_MARK_BYTE       0x01
     99 #define SWIM_CRC_ZERO        0x02
    100 #define SWIM_RDDATA          0x04
    101 #define SWIM_SENSE           0x08
    102 #define SWIM_MOTEN           0x10
    103 #define SWIM_ERROR           0x20
    104 #define SWIM_DAT2BYTE        0x40
    105 #define SWIM_DAT1BYTE        0x80
    106 
    107 /* bits in setup register */
    108 
    109 #define SWIM_S_INV_WDATA     0x01
    110 #define SWIM_S_3_5_SELECT    0x02
    111 #define SWIM_S_GCR           0x04
    112 #define SWIM_S_FCLK_DIV2     0x08
    113 #define SWIM_S_ERROR_CORR    0x10
    114 #define SWIM_S_IBM_DRIVE     0x20
    115 #define SWIM_S_GCR_WRITE     0x40
    116 #define SWIM_S_TIMEOUT       0x80
    117 
    118 /* bits in mode register */
    119 
    120 #define SWIM_CLFIFO          0x01
    121 #define SWIM_ENBL1           0x02
    122 #define SWIM_ENBL2           0x04
    123 #define SWIM_ACTION          0x08
    124 #define SWIM_WRITE_MODE      0x10
    125 #define SWIM_HEDSEL          0x20
    126 #define SWIM_MOTON           0x80
    127 
    128 static void fd_recalibrate(FDrive *drive)
    129 {
    130 }
    131 
    132 static void swim_change_cb(void *opaque, bool load, Error **errp)
    133 {
    134     FDrive *drive = opaque;
    135 
    136     if (!load) {
    137         blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
    138     } else {
    139         if (!blkconf_apply_backend_options(drive->conf,
    140                                            !blk_supports_write_perm(drive->blk),
    141                                            false, errp)) {
    142             return;
    143         }
    144     }
    145 }
    146 
    147 static const BlockDevOps swim_block_ops = {
    148     .change_media_cb = swim_change_cb,
    149 };
    150 
    151 static Property swim_drive_properties[] = {
    152     DEFINE_PROP_INT32("unit", SWIMDrive, unit, -1),
    153     DEFINE_BLOCK_PROPERTIES(SWIMDrive, conf),
    154     DEFINE_PROP_END_OF_LIST(),
    155 };
    156 
    157 static void swim_drive_realize(DeviceState *qdev, Error **errp)
    158 {
    159     SWIMDrive *dev = SWIM_DRIVE(qdev);
    160     SWIMBus *bus = SWIM_BUS(qdev->parent_bus);
    161     FDrive *drive;
    162     int ret;
    163 
    164     if (dev->unit == -1) {
    165         for (dev->unit = 0; dev->unit < SWIM_MAX_FD; dev->unit++) {
    166             drive = &bus->ctrl->drives[dev->unit];
    167             if (!drive->blk) {
    168                 break;
    169             }
    170         }
    171     }
    172 
    173     if (dev->unit >= SWIM_MAX_FD) {
    174         error_setg(errp, "Can't create floppy unit %d, bus supports "
    175                    "only %d units", dev->unit, SWIM_MAX_FD);
    176         return;
    177     }
    178 
    179     drive = &bus->ctrl->drives[dev->unit];
    180     if (drive->blk) {
    181         error_setg(errp, "Floppy unit %d is in use", dev->unit);
    182         return;
    183     }
    184 
    185     if (!dev->conf.blk) {
    186         /* Anonymous BlockBackend for an empty drive */
    187         dev->conf.blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
    188         ret = blk_attach_dev(dev->conf.blk, qdev);
    189         assert(ret == 0);
    190     }
    191 
    192     if (!blkconf_blocksizes(&dev->conf, errp)) {
    193         return;
    194     }
    195 
    196     if (dev->conf.logical_block_size != 512 ||
    197         dev->conf.physical_block_size != 512)
    198     {
    199         error_setg(errp, "Physical and logical block size must "
    200                    "be 512 for floppy");
    201         return;
    202     }
    203 
    204     /*
    205      * rerror/werror aren't supported by fdc and therefore not even registered
    206      * with qdev. So set the defaults manually before they are used in
    207      * blkconf_apply_backend_options().
    208      */
    209     dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO;
    210     dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
    211 
    212     if (!blkconf_apply_backend_options(&dev->conf,
    213                                        !blk_supports_write_perm(dev->conf.blk),
    214                                        false, errp)) {
    215         return;
    216     }
    217 
    218     /*
    219      * 'enospc' is the default for -drive, 'report' is what blk_new() gives us
    220      * for empty drives.
    221      */
    222     if (blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC &&
    223         blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_REPORT) {
    224         error_setg(errp, "fdc doesn't support drive option werror");
    225         return;
    226     }
    227     if (blk_get_on_error(dev->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
    228         error_setg(errp, "fdc doesn't support drive option rerror");
    229         return;
    230     }
    231 
    232     drive->conf = &dev->conf;
    233     drive->blk = dev->conf.blk;
    234     drive->swimctrl = bus->ctrl;
    235 
    236     blk_set_dev_ops(drive->blk, &swim_block_ops, drive);
    237 }
    238 
    239 static void swim_drive_class_init(ObjectClass *klass, void *data)
    240 {
    241     DeviceClass *k = DEVICE_CLASS(klass);
    242     k->realize = swim_drive_realize;
    243     set_bit(DEVICE_CATEGORY_STORAGE, k->categories);
    244     k->bus_type = TYPE_SWIM_BUS;
    245     device_class_set_props(k, swim_drive_properties);
    246     k->desc = "virtual SWIM drive";
    247 }
    248 
    249 static const TypeInfo swim_drive_info = {
    250     .name = TYPE_SWIM_DRIVE,
    251     .parent = TYPE_DEVICE,
    252     .instance_size = sizeof(SWIMDrive),
    253     .class_init = swim_drive_class_init,
    254 };
    255 
    256 static const TypeInfo swim_bus_info = {
    257     .name = TYPE_SWIM_BUS,
    258     .parent = TYPE_BUS,
    259     .instance_size = sizeof(SWIMBus),
    260 };
    261 
    262 static void iwmctrl_write(void *opaque, hwaddr reg, uint64_t value,
    263                           unsigned size)
    264 {
    265     SWIMCtrl *swimctrl = opaque;
    266 
    267     reg >>= REG_SHIFT;
    268 
    269     swimctrl->regs[reg >> 1] = reg & 1;
    270 
    271     if (swimctrl->regs[IWM_Q6] &&
    272         swimctrl->regs[IWM_Q7]) {
    273         if (swimctrl->regs[IWM_MTR]) {
    274             /* data register */
    275             swimctrl->iwm_data = value;
    276         } else {
    277             /* mode register */
    278             swimctrl->iwm_mode = value;
    279             /* detect sequence to switch from IWM mode to SWIM mode */
    280             switch (swimctrl->iwm_switch) {
    281             case 0:
    282                 if (value == 0x57) {
    283                     swimctrl->iwm_switch++;
    284                 }
    285                 break;
    286             case 1:
    287                 if (value == 0x17) {
    288                     swimctrl->iwm_switch++;
    289                 }
    290                 break;
    291             case 2:
    292                 if (value == 0x57) {
    293                     swimctrl->iwm_switch++;
    294                 }
    295                 break;
    296             case 3:
    297                 if (value == 0x57) {
    298                     swimctrl->mode = SWIM_MODE_SWIM;
    299                     swimctrl->iwm_switch = 0;
    300                 }
    301                 break;
    302             }
    303         }
    304     }
    305 }
    306 
    307 static uint64_t iwmctrl_read(void *opaque, hwaddr reg, unsigned size)
    308 {
    309     SWIMCtrl *swimctrl = opaque;
    310 
    311     reg >>= REG_SHIFT;
    312 
    313     swimctrl->regs[reg >> 1] = reg & 1;
    314 
    315     return 0;
    316 }
    317 
    318 static void swimctrl_write(void *opaque, hwaddr reg, uint64_t value,
    319                            unsigned size)
    320 {
    321     SWIMCtrl *swimctrl = opaque;
    322 
    323     if (swimctrl->mode == SWIM_MODE_IWM) {
    324         iwmctrl_write(opaque, reg, value, size);
    325         return;
    326     }
    327 
    328     reg >>= REG_SHIFT;
    329 
    330     switch (reg) {
    331     case SWIM_WRITE_PHASE:
    332         swimctrl->swim_phase = value;
    333         break;
    334     case SWIM_WRITE_MODE0:
    335         swimctrl->swim_mode &= ~value;
    336         break;
    337     case SWIM_WRITE_MODE1:
    338         swimctrl->swim_mode |= value;
    339         break;
    340     case SWIM_WRITE_DATA:
    341     case SWIM_WRITE_MARK:
    342     case SWIM_WRITE_CRC:
    343     case SWIM_WRITE_PARAMETER:
    344     case SWIM_WRITE_SETUP:
    345         break;
    346     }
    347 }
    348 
    349 static uint64_t swimctrl_read(void *opaque, hwaddr reg, unsigned size)
    350 {
    351     SWIMCtrl *swimctrl = opaque;
    352     uint32_t value = 0;
    353 
    354     if (swimctrl->mode == SWIM_MODE_IWM) {
    355         return iwmctrl_read(opaque, reg, size);
    356     }
    357 
    358     reg >>= REG_SHIFT;
    359 
    360     switch (reg) {
    361     case SWIM_READ_PHASE:
    362         value = swimctrl->swim_phase;
    363         break;
    364     case SWIM_READ_HANDSHAKE:
    365         if (swimctrl->swim_phase == SWIM_DRIVE_PRESENT) {
    366             /* always answer "no drive present" */
    367             value = SWIM_SENSE;
    368         }
    369         break;
    370     case SWIM_READ_DATA:
    371     case SWIM_READ_MARK:
    372     case SWIM_READ_ERROR:
    373     case SWIM_READ_PARAMETER:
    374     case SWIM_READ_SETUP:
    375     case SWIM_READ_STATUS:
    376         break;
    377     }
    378 
    379     return value;
    380 }
    381 
    382 static const MemoryRegionOps swimctrl_mem_ops = {
    383     .write = swimctrl_write,
    384     .read = swimctrl_read,
    385     .endianness = DEVICE_NATIVE_ENDIAN,
    386 };
    387 
    388 static void sysbus_swim_reset(DeviceState *d)
    389 {
    390     Swim *sys = SWIM(d);
    391     SWIMCtrl *ctrl = &sys->ctrl;
    392     int i;
    393 
    394     ctrl->mode = 0;
    395     ctrl->iwm_switch = 0;
    396     for (i = 0; i < 8; i++) {
    397         ctrl->regs[i] = 0;
    398     }
    399     ctrl->iwm_data = 0;
    400     ctrl->iwm_mode = 0;
    401     ctrl->swim_phase = 0;
    402     ctrl->swim_mode = 0;
    403     for (i = 0; i < SWIM_MAX_FD; i++) {
    404         fd_recalibrate(&ctrl->drives[i]);
    405     }
    406 }
    407 
    408 static void sysbus_swim_init(Object *obj)
    409 {
    410     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    411     Swim *sbs = SWIM(obj);
    412     SWIMCtrl *swimctrl = &sbs->ctrl;
    413 
    414     memory_region_init_io(&swimctrl->iomem, obj, &swimctrl_mem_ops, swimctrl,
    415                           "swim", 0x2000);
    416     sysbus_init_mmio(sbd, &swimctrl->iomem);
    417 }
    418 
    419 static void sysbus_swim_realize(DeviceState *dev, Error **errp)
    420 {
    421     Swim *sys = SWIM(dev);
    422     SWIMCtrl *swimctrl = &sys->ctrl;
    423 
    424     qbus_init(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev, NULL);
    425     swimctrl->bus.ctrl = swimctrl;
    426 }
    427 
    428 static const VMStateDescription vmstate_fdrive = {
    429     .name = "fdrive",
    430     .version_id = 1,
    431     .minimum_version_id = 1,
    432     .fields = (VMStateField[]) {
    433         VMSTATE_END_OF_LIST()
    434     },
    435 };
    436 
    437 static const VMStateDescription vmstate_swim = {
    438     .name = "swim",
    439     .version_id = 1,
    440     .minimum_version_id = 1,
    441     .fields = (VMStateField[]) {
    442         VMSTATE_INT32(mode, SWIMCtrl),
    443         /* IWM mode */
    444         VMSTATE_INT32(iwm_switch, SWIMCtrl),
    445         VMSTATE_UINT16_ARRAY(regs, SWIMCtrl, 8),
    446         VMSTATE_UINT8(iwm_data, SWIMCtrl),
    447         VMSTATE_UINT8(iwm_mode, SWIMCtrl),
    448         /* SWIM mode */
    449         VMSTATE_UINT8(swim_phase, SWIMCtrl),
    450         VMSTATE_UINT8(swim_mode, SWIMCtrl),
    451         /* Drives */
    452         VMSTATE_STRUCT_ARRAY(drives, SWIMCtrl, SWIM_MAX_FD, 1,
    453                              vmstate_fdrive, FDrive),
    454         VMSTATE_END_OF_LIST()
    455     },
    456 };
    457 
    458 static const VMStateDescription vmstate_sysbus_swim = {
    459     .name = "SWIM",
    460     .version_id = 1,
    461     .fields = (VMStateField[]) {
    462         VMSTATE_STRUCT(ctrl, Swim, 0, vmstate_swim, SWIMCtrl),
    463         VMSTATE_END_OF_LIST()
    464     }
    465 };
    466 
    467 static void sysbus_swim_class_init(ObjectClass *oc, void *data)
    468 {
    469     DeviceClass *dc = DEVICE_CLASS(oc);
    470 
    471     dc->realize = sysbus_swim_realize;
    472     dc->reset = sysbus_swim_reset;
    473     dc->vmsd = &vmstate_sysbus_swim;
    474 }
    475 
    476 static const TypeInfo sysbus_swim_info = {
    477     .name          = TYPE_SWIM,
    478     .parent        = TYPE_SYS_BUS_DEVICE,
    479     .instance_size = sizeof(Swim),
    480     .instance_init = sysbus_swim_init,
    481     .class_init    = sysbus_swim_class_init,
    482 };
    483 
    484 static void swim_register_types(void)
    485 {
    486     type_register_static(&sysbus_swim_info);
    487     type_register_static(&swim_bus_info);
    488     type_register_static(&swim_drive_info);
    489 }
    490 
    491 type_init(swim_register_types)