diff --git a/src/machine/m_ps1_hdc_varcem.c b/src/machine/m_ps1_hdc_varcem.c deleted file mode 100644 index b2bd5222a..000000000 --- a/src/machine/m_ps1_hdc_varcem.c +++ /dev/null @@ -1,1483 +0,0 @@ -/* - * VARCem Virtual ARchaeological Computer EMulator. - * An emulator of (mostly) x86-based PC systems and devices, - * using the ISA,EISA,VLB,MCA and PCI system buses, roughly - * spanning the era between 1981 and 1995. - * - * This file is part of the VARCem Project. - * - * Implementation of the PS/1 Model 2011 disk controller. - * - * XTA is the acronym for 'XT-Attached', which was basically - * the XT-counterpart to what we know now as IDE (which is - * also named ATA - AT Attachment.) The basic ideas was to - * put the actual drive controller electronics onto the drive - * itself, and have the host machine just talk to that using - * a simpe, standardized I/O path- hence the name IDE, for - * Integrated Drive Electronics. - * - * In the ATA version of IDE, the programming interface of - * the IBM PC/AT (which used the Western Digitial 1002/1003 - * controllers) was kept, and, so, ATA-IDE assumes a 16bit - * data path: it reads and writes 16bit words of data. The - * disk drives for this bus commonly have an 'A' suffix to - * identify them as 'ATBUS'. - * - * In XTA-IDE, which is slightly older, the programming - * interface of the IBM PC/XT (which used the MFM controller - * from Xebec) was kept, and, so, it uses an 8bit data path. - * Disk drives for this bus commonly have the 'X' suffix to - * mark them as being for this XTBUS variant. - * - * So, XTA and ATA try to do the same thing, but they use - * different ways to achive their goal. - * - * Also, XTA is **not** the same as XTIDE. XTIDE is a modern - * variant of ATA-IDE, but retro-fitted for use on 8bit XT - * systems: an extra register is used to deal with the extra - * data byte per transfer. XTIDE uses regular IDE drives, - * and uses the regular ATA/IDE programming interface, just - * with the extra register. - * - * NOTE: We should probably find a nicer way to integrate our Disk - * Type table with the main code, so the user can only select - * items from that list... - * - * Version: @(#)m_ps1_hdc.c 1.0.15 2021/03/18 - * - * Author: Fred N. van Kempen, - * - * Based on my earlier HD20 driver for the EuroPC. - * Thanks to Marco Bortolin for the help and feedback !! - * - * Copyright 2017-2021 Fred N. van Kempen. - * - * Redistribution and use in source and binary forms, with - * or without modification, are permitted provided that the - * following conditions are met: - * - * 1. Redistributions of source code must retain the entire - * above notice, this list of conditions and the following - * disclaimer. - * - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names - * of its contributors may be used to endorse or promote - * products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#define __USE_LARGEFILE64 -#define _LARGEFILE_SOURCE -#define _LARGEFILE64_SOURCE -#include -#include -#include -#include -#include -#define dbglog hdc_log -#include "../emu.h" -#include "../timer.h" -#include "../io.h" -#include "../device.h" -#include "../devices/system/dma.h" -#include "../devices/system/pic.h" -#include "../devices/disk/hdc.h" -#include "../devices/disk/hdd.h" -#include "../ui/ui.h" -#include "../plat.h" -#include "machine.h" -#include "m_ps1.h" - - -#define HDC_TIME (50*TIMER_USEC) -#define HDC_TYPE_USER 47 /* user drive type */ -#define PS1_HDD_NUM 1 /* we support 1 drive */ - - -enum { - STATE_IDLE = 0, - STATE_RECV, - STATE_RDATA, - STATE_RDONE, - STATE_SEND, - STATE_SDATA, - STATE_SDONE, - STATE_FINIT, - STATE_FDONE -}; - - -/* Command values. These deviate from the XTA ones. */ -#define CMD_READ_SECTORS 0x01 /* regular read-date */ -#define CMD_READ_VERIFY 0x02 /* read for verify, no data */ -#define CMD_READ_EXT 0x03 /* read extended (ecc) */ -#define CMD_READ_ID 0x05 /* read ID mark on cyl */ -#define CMD_RECALIBRATE 0x08 /* recalibrate to track0 */ -#define CMD_WRITE_SECTORS 0x09 /* regular write-data */ -#define CMD_WRITE_VERIFY 0x0a /* write-data with verify */ -#define CMD_WRITE_EXT 0x0b /* write extended (ecc) */ -#define CMD_FORMAT_DRIVE 0x0d /* format entire disk */ -#define CMD_SEEK 0x0e /* seek */ -#define CMD_FORMAT_TRACK 0x0f /* format one track */ - -/* Attachment Status register (reg 2R) values (IBM PS/1 2011.) */ -#define ASR_TX_EN 0x01 /* transfer enable */ -#define ASR_INT_REQ 0x02 /* interrupt request */ -#define ASR_BUSY 0x04 /* busy */ -#define ASR_DIR 0x08 /* direction */ -#define ASR_DATA_REQ 0x10 /* data request */ - -/* Attachment Control register (2W) values (IBM PS/1 2011.) */ -#define ACR_DMA_EN 0x01 /* DMA enable */ -#define ACR_INT_EN 0x02 /* interrupt enable */ -#define ACR_RESET 0x80 /* reset */ - -/* Interrupt Status register (4R) values (IBM PS/1 2011.) */ -#define ISR_EQUIP_CHECK 0x01 /* internal hardware error */ -#define ISR_ERP_INVOKED 0x02 /* error recovery invoked */ -#define ISR_CMD_REJECT 0x20 /* command reject */ -#define ISR_INVALID_CMD 0x40 /* invalid command */ -#define ISR_TERMINATION 0x80 /* termination error */ - -/* Attention register (4W) values (IBM PS/1 2011.) */ -#define ATT_DATA 0x10 /* data request */ -#define ATT_SSB 0x20 /* sense summary block */ -#define ATT_CSB 0x40 /* command specify block */ -#define ATT_CCB 0x80 /* command control block */ - - -/* - * Define the Sense Summary Block. - * - * The sense summary block contains the current status of the - * drive. The information in the summary block is updated after - * each command is completed, after an error, or before the - * block is transferred. - */ -#pragma pack(push,1) -typedef struct { - /* Status byte 0. */ - uint8_t track_0 :1, /* T0 */ - mbz1 :1, /* 0 */ - mbz2 :1, /* 0 */ - cylinder_err :1, /* CE */ - write_fault :1, /* WF */ - mbz3 :1, /* 0 */ - seek_end :1, /* SE */ - not_ready :1; /* NR */ - - /* Status byte 1. */ - uint8_t id_not_found :1, /* ID */ - mbz4 :1, /* 0 */ - mbz5 :1, /* 0 */ - wrong_cyl :1, /* WC */ - all_bit_set :1, /* BT */ - mark_not_found :1, /* AM */ - ecc_crc_err :1, /* ET */ - ecc_crc_field :1; /* EF */ - - /* Status byte 2. */ - uint8_t headsel_state :4, /* headsel state[4] */ - defective_sector:1, /* DS */ - retried_ok :1, /* RG */ - need_reset :1, /* RR */ -#if 1 - valid :1; /* 0 (abused as VALID) */ -#else - mbz6 :1; /* 0 */ -#endif - - /* Most recent ID field seen. */ - uint8_t last_cyl_low; /* Cyl_Low[8] */ - uint8_t last_head :4, /* HD[4] */ - mbz7 :1, /* 0 */ - last_cyl_high :2, /* Cyl_high[2] */ - last_def_sect :1; /* DS */ - uint8_t last_sect; /* Sect[8] */ - - uint8_t sect_size; /* Size[8] = 02 */ - - /* Current position. */ - uint8_t curr_cyl_high :2, /* Cyl_High_[2] */ - mbz8 :1, /* 0 */ - mbz9 :1, /* 0 */ - curr_head :4; /* HD_2[4] */ - uint8_t curr_cyl_low; /* Cyl_Low_2[8] */ - - uint8_t sect_corr; /* sectors corrected */ - - uint8_t retries; /* retries */ - - /* - * This byte shows the progress of the controller through the - * last command. It allows the system to monitor the controller - * and determine if a reset is needed. When the transfer of the - * control block is started, the value is set to hex 00. The - * progress indicated by this byte is: - * - * 1. Set to hex 01 after the control block is successfully - * transferred. - * - * 2. Set to hex 02 when the command is valid and the drive - * is ready. - * - * 3. Set to hex 03 when the head is in the correct track. - * The most-significant four bits (high nibble) are then - * used to indicate the successful stages of the data - * transfer: - * - * Bit 7 A sector was transferred between the system - * and the sector buffer. - * - * Bit 6 A sector was transferred between the controller - * and the sector buffer. - * - * Bit 5 An error was detected and error recovery - * procedures have been started. - * - * Bit 4 The controller has completed the operation - * and is now not busy. - * - * 4. When the transfer is complete, the low nibble equals hex 4 - * and the high nibble is unchanged. - */ - uint8_t cmd_syndrome; /* command syndrome */ - - uint8_t drive_type; /* drive type */ - - uint8_t rsvd; /* reserved byte */ -} ssb_t; -#pragma pack(pop) - -/* - * Define the Format Control Block. - * - * The format control block (FCB) specifies the ID data used - * in formatting the track. It is used by the Format Track - * and Format Disk commands and contains five bytes for each - * sector formatted on that track. - * - * When the Format Disk command is used, the control block - * contains the sector information of all sectors for head 0, - * cylinder 0. The drive will use the same block to format - * the rest of the disk and automatically increment the head - * number and cylinder number for the remaining tracks. The - * sector numbers, sector size, and the fill byte will be - * the same for each track. - * - * The drive formats the sector IDs on the disk in the same - * order as they are specified in the control block. - * Therefore, sector interleaving is accomplished by filling - * in the control block with the desired interleave. - * - * For example, when formatting 17 sectors per track with an - * interleave of 2, the control block has the first 5 bytes - * with a sector number of 1, the second with a sector number - * of 10, the third with a sector number of 2, and continuing - * until all 17 sectors for that track are defined. - * - * The format for the format control block is described in - * the following. The five bytes are repeated for each - * sector on the track. The control block must contain an - * even number of bytes. If an odd number of sectors are - * being formatted, an additional byte is sent with all - * bits 0. - */ -#pragma pack(push,1) -typedef struct { - uint8_t cyl_high :2, /* cylinder [9:8] bits */ - defective_sector:1, /* DS */ - mbz1 :1, /* 0 */ - head :4; /* head number */ - - uint8_t cyl_low; /* cylinder [7:0] bits */ - - uint8_t sector; /* sector number */ - - uint8_t mbz2 :1, /* 0 */ - mbo :1, /* 1 */ - mbz3 :6; /* 000000 */ - - uint8_t fill; /* filler byte */ -} fcb_t; -#pragma pack(pop) - -/* - * Define the Command Control Block. - * - * The system specifies the operation by sending the 6-byte - * command control block to the controller. It can be sent - * through a DMA or PIO operation. - */ -#pragma pack(push,1) -typedef struct { - uint8_t ec_p :1, /* EC/P (ecc/park) */ - mbz1 :1, /* 0 */ - auto_seek :1, /* AS (auto-seek) */ - no_data :1, /* ND (no data) */ - cmd :4; /* command code[4] */ - - uint8_t cyl_high :2, /* cylinder [9:8] bits */ - mbz2 :2, /* 00 */ - head :4; /* head number */ - - uint8_t cyl_low; /* cylinder [7:0] bits */ - - uint8_t sector; /* sector number */ - - uint8_t mbz3 :1, /* 0 */ - mbo1 :1, /* 1 */ - mbz4 :6; /* 000000 */ - - uint8_t count; /* blk count/interleave */ -} ccb_t; -#pragma pack(pop) - -/* Define the hard drive geometry table. */ -typedef struct { - uint16_t cyl; - uint8_t hpc; - uint8_t spt; - int16_t wpc; - int16_t lz; -} geom_t; - -/* Define an attached drive. */ -typedef struct { - int8_t id, /* drive ID on bus */ - present, /* drive is present */ - hdd_num, /* index to global disk table */ - type; /* drive type ID */ - - uint16_t cur_cyl; /* last known position of heads */ - - uint8_t spt, /* active drive parameters */ - hpc; - uint16_t tracks; - - uint8_t cfg_spt, /* configured drive parameters */ - cfg_hpc; - uint16_t cfg_tracks; -} drive_t; - - -typedef struct { - uint16_t base; /* controller base I/O address */ - int8_t irq; /* controller IRQ channel */ - int8_t dma; /* controller DMA channel */ - - /* Registers. */ - uint8_t attn, /* ATTENTION register */ - ctrl, /* Control register (ACR) */ - status, /* Status register (ASR) */ - intstat; /* Interrupt Status register (ISR) */ - - priv_t sys; /* handle to system board */ - - /* Controller state. */ - tmrval_t callback; - int8_t state, /* controller state */ - reset; /* reset state counter */ - - /* Data transfer. */ - int16_t buf_idx, /* buffer index and pointer */ - buf_len; - uint8_t *buf_ptr; - - /* Current operation parameters. */ - ssb_t ssb; /* sense block */ - ccb_t ccb; /* command control block */ - uint16_t track; /* requested track# */ - uint8_t head, /* requested head# */ - sector; /* requested sector# */ - int count; /* requested sector count */ - - drive_t drives[PS1_HDD_NUM]; /* the attached drive(s) */ - - uint8_t data[512]; /* data buffer */ - uint8_t sector_buf[512]; /* sector buffer */ -} hdc_t; - - -/* - * IBM hard drive types 1-44. - * - * We need these to translate the selected disk's - * geometry back to a valid type through the SSB. - * - * Cyl. Head Sect. Write Land - * p-comp Zone - */ -static const geom_t ibm_type_table[] = { - { 0, 0, 0, 0, 0 }, /* 0 (none) */ - { 306, 4, 17, 128, 305 }, /* 1 10 MB */ - { 615, 4, 17, 300, 615 }, /* 2 20 MB */ - { 615, 6, 17, 300, 615 }, /* 3 31 MB */ - { 940, 8, 17, 512, 940 }, /* 4 62 MB */ - { 940, 6, 17, 512, 940 }, /* 5 47 MB */ - { 615, 4, 17, -1, 615 }, /* 6 20 MB */ - { 462, 8, 17, 256, 511 }, /* 7 31 MB */ - { 733, 5, 17, -1, 733 }, /* 8 30 MB */ - { 900, 15, 17, -1, 901 }, /* 9 112 MB */ - { 820, 3, 17, -1, 820 }, /* 10 20 MB */ - { 855, 5, 17, -1, 855 }, /* 11 35 MB */ - { 855, 7, 17, -1, 855 }, /* 12 50 MB */ - { 306, 8, 17, 128, 319 }, /* 13 20 MB */ - { 733, 7, 17, -1, 733 }, /* 14 43 MB */ - { 0, 0, 0, 0, 0 }, /* 15 (rsvd) */ - { 612, 4, 17, 0, 663 }, /* 16 20 MB */ - { 977, 5, 17, 300, 977 }, /* 17 41 MB */ - { 977, 7, 17, -1, 977 }, /* 18 57 MB */ - { 1024, 7, 17, 512, 1023 }, /* 19 59 MB */ - { 733, 5, 17, 300, 732 }, /* 20 30 MB */ - { 733, 7, 17, 300, 732 }, /* 21 43 MB */ - { 733, 5, 17, 300, 733 }, /* 22 30 MB */ - { 306, 4, 17, 0, 336 }, /* 23 10 MB */ - { 612, 4, 17, 305, 663 }, /* 24 20 MB */ - { 306, 4, 17, -1, 340 }, /* 25 10 MB */ - { 612, 4, 17, -1, 670 }, /* 26 20 MB */ - { 698, 7, 17, 300, 732 }, /* 27 41 MB */ - { 976, 5, 17, 488, 977 }, /* 28 40 MB */ - { 306, 4, 17, 0, 340 }, /* 29 10 MB */ - { 611, 4, 17, 306, 663 }, /* 30 20 MB */ - { 732, 7, 17, 300, 732 }, /* 31 43 MB */ - { 1023, 5, 17, -1, 1023 }, /* 32 42 MB */ - { 614, 4, 25, -1, 663 }, /* 33 30 MB */ - { 775, 2, 27, -1, 900 }, /* 34 20 MB */ - { 921, 2, 33, -1, 1000 }, /* 35 30 MB * */ - { 402, 4, 26, -1, 460 }, /* 36 20 MB */ - { 580, 6, 26, -1, 640 }, /* 37 44 MB */ - { 845, 2, 36, -1, 1023 }, /* 38 30 MB * */ - { 769, 3, 36, -1, 1023 }, /* 39 41 MB * */ - { 531, 4, 39, -1, 532 }, /* 40 40 MB */ - { 577, 2, 36, -1, 1023 }, /* 41 20 MB */ - { 654, 2, 32, -1, 674 }, /* 42 20 MB */ - { 923, 5, 36, -1, 1023 }, /* 43 81 MB */ - { 531, 8, 39, -1, 532 } /* 44 81 MB */ -}; - - -/* FIXME: we should use the disk/hdd_table.c code with custom tables! */ -static int -ibm_drive_type(drive_t *drive) -{ - const geom_t *ptr; - int i; - - for (i = 0; i < (sizeof(ibm_type_table) / sizeof(geom_t)); i++) { - ptr = &ibm_type_table[i]; - if ((drive->tracks == ptr->cyl) && - (drive->hpc == ptr->hpc) && (drive->spt == ptr->spt)) return(i); - } - - return(HDC_TYPE_USER); -} - - -static void -set_intr(hdc_t *dev, int raise) -{ - if (raise) { - dev->status |= ASR_INT_REQ; - if (dev->ctrl & ACR_INT_EN) - picint(1 << dev->irq); - } else { - dev->status &= ~ASR_INT_REQ; - picintc(1 << dev->irq); - } -} - - -/* Get the logical (block) address of a CHS triplet. */ -static int -get_sector(hdc_t *dev, drive_t *drive, off64_t *addr) -{ - if (drive->cur_cyl != dev->track) { - DEBUG("HDC: get_sector: wrong cylinder %d/%d\n", - drive->cur_cyl, dev->track); - dev->ssb.wrong_cyl = 1; - return(1); - } - - if (dev->head >= drive->hpc) { - DEBUG("HDC: get_sector: past end of heads\n"); - dev->ssb.cylinder_err = 1; - return(1); - } - - if (dev->sector > drive->spt) { - DEBUG("HDC: get_sector: past end of sectors\n"); - dev->ssb.mark_not_found = 1; - return(1); - } - - /* Calculate logical address (block number) of desired sector. */ - *addr = ((((off64_t) dev->track*drive->hpc) + \ - dev->head)*drive->spt) + dev->sector - 1; - - return(0); -} - - -static void -next_sector(hdc_t *dev, drive_t *drive) -{ - if (++dev->sector > drive->spt) { - dev->sector = 1; - if (++dev->head >= drive->hpc) { - dev->head = 0; - dev->track++; - if (++drive->cur_cyl >= drive->tracks) { - drive->cur_cyl = drive->tracks-1; - dev->ssb.cylinder_err = 1; - } - } - } -} - - -#if defined(ENABLE_HDC_LOG) && defined(_DEBUG) -static void -dump_ssb(ssb_t *ssb) -{ - char temp[1024]; - char *sp = temp; - uint8_t *ptr = (uint8_t *)ssb; - int i; - - sprintf(temp, "Current SSB:\n ["); - sp += strlen(sp); - for (i = 0; i < sizeof(ssb_t); i++) { - sprintf(sp, " %02X", *ptr++); - sp += strlen(sp); - } - sprintf(sp, " ]\n"); sp += strlen(sp); - - sprintf(sp, " Status 0: T0=%d CE=%d WF=%d SE=%d NR=%d\n", - ssb->track_0, ssb->cylinder_err, ssb->write_fault, - ssb->seek_end, ssb->not_ready); - sp += strlen(sp); - - sprintf(sp, " Status 1: ID=%d WC=%d BT=%d AM=%d ET=%d EF=%d\n", - ssb->id_not_found, ssb->wrong_cyl, ssb->all_bit_set, - ssb->mark_not_found, ssb->ecc_crc_err, ssb->ecc_crc_field); - sp += strlen(sp); - - sprintf(sp, " Status 2: HEADSEL=%d DS=%d RG=%d RR=%d\n", - ssb->headsel_state, ssb->defective_sector, ssb->retried_ok, - ssb->need_reset); - sp += strlen(sp); - - sprintf(sp, " Last : CYL=%d HEAD=%d SECTOR=%d DS=%d SIZE=%d\n", - (ssb->last_cyl_high<<8)|ssb->last_cyl_low, ssb->last_head, - ssb->last_sect, ssb->last_def_sect, (128<sect_size)); - sp += strlen(sp); - - sprintf(sp, " Current : CYL=%d HEAD=%d CORR=%d RETR=%d\n", - (ssb->curr_cyl_high<<8)|ssb->curr_cyl_low, ssb->curr_head, - ssb->sect_corr, ssb->retries); - sp += strlen(sp); - - sprintf(sp, " Misc : Syndrome=%02X DRIVE_TYPE=%d\n", - ssb->cmd_syndrome, ssb->drive_type); - - DBGLOG(1, "HDC: %s\n", temp); -} - - -static void -dump_ccb(ccb_t *ccb) -{ - char temp[1024]; - char *sp = temp; - - sprintf(temp, "Incoming CCB:\n"); - sp += strlen(sp); - sprintf(sp, " CMD=%02X EC/P=%d DS=%d AS=%d ND=%d\n", - ccb->cmd, ccb->ec_p, ccb->mbz1, ccb->auto_seek, ccb->no_data); - sp += strlen(sp); - sprintf(sp, " CYL=%d HEAD=%d SECTOR=%d COUNT=%d\n", - ((ccb->cyl_high<<8)|ccb->cyl_low), ccb->head, ccb->sector, ccb->count); - - DBGLOG(1, "HDC: %s\n", temp); -}; - - -static void -dump_fcb(fcb_t *fcb, int count) -{ - char temp[1024]; - char *sp = temp; - - sprintf(temp, "Incoming FCB:\n"); - - while (count--) { - sp += strlen(sp); - sprintf(sp, " CYL=%4d HEAD=%2d DS=%d", - ((fcb->cyl_high << 8) | fcb->cyl_low), - fcb->head, fcb->defective_sector); - sp += strlen(sp); - sprintf(sp, " SECTOR=%2d FILL=%02X\n", fcb->sector, fcb->fill); - fcb++; - } - - DBGLOG(1, "HDC: %s\n", temp); -} -#endif - - -/* Finish up. Repeated all over, so a function it is now. */ -static void -do_finish(hdc_t *dev) -{ - dev->state = STATE_IDLE; - - dev->attn &= ~(ATT_CCB | ATT_DATA); - - dev->status = 0x00; - - set_intr(dev, 1); -} - - -/* Seek to a cylinder. */ -static int -do_seek(hdc_t *dev, drive_t *drive, uint16_t cyl) -{ - if (cyl >= drive->tracks) { - dev->ssb.cylinder_err = 1; - return(1); - } - - dev->track = cyl; - drive->cur_cyl = dev->track; - - return(0); -} - - -/* Format a track or an entire drive. */ -static void -do_format(hdc_t *dev, drive_t *drive, ccb_t *ccb) -{ - int start_cyl, end_cyl; - int intr = 0, val; - off64_t addr; -#if defined(ENABLE_HDC_LOG) && defined(_DEBUG) - fcb_t *fcb; -#endif - - /* Get the parameters from the CCB. */ - if (ccb->cmd == CMD_FORMAT_DRIVE) { - start_cyl = 0; - end_cyl = drive->tracks; - } else { - start_cyl = (ccb->cyl_low | (ccb->cyl_high << 8)); - end_cyl = start_cyl + 1; - } - - switch (dev->state) { - case STATE_IDLE: - /* Ready to transfer the FCB data in. */ - dev->state = STATE_RDATA; - dev->buf_idx = 0; - dev->buf_ptr = dev->data; - dev->buf_len = ccb->count * sizeof(fcb_t); - if (dev->buf_len & 1) - dev->buf_len++; /* must be even */ - - /* Enable for PIO or DMA, as needed. */ -#if 0 /*NOT_USED*/ - if (dev->ctrl & ACR_DMA_EN) - dev->callback = HDC_TIME; - else -#endif - dev->status |= ASR_DATA_REQ; - break; - - case STATE_RDATA: - /* Perform DMA. */ - while (dev->buf_idx < dev->buf_len) { - val = dma_channel_read(dev->dma); - if (val == DMA_NODATA) { - ERRLOG("HDC: CMD_FORMAT out of data (idx=%d, len=%d)!\n", dev->buf_idx, dev->buf_len); - dev->intstat |= ISR_EQUIP_CHECK; - dev->ssb.need_reset = 1; - intr = 1; - break; - } - dev->buf_ptr[dev->buf_idx] = (val & 0xff); - dev->buf_idx++; - } - dev->state = STATE_RDONE; - dev->callback = HDC_TIME; - break; - - case STATE_RDONE: - if (! (dev->ctrl & ACR_DMA_EN)) - dev->status &= ~ASR_DATA_REQ; - -#if defined(ENABLE_HDC_LOG) && defined(_DEBUG) - /* Point to the FCB we got. */ - fcb = (fcb_t *)dev->data; - dump_fcb(fcb, ccb->count); -#endif - dev->state = STATE_FINIT; - /*FALLTHROUGH*/ - - case STATE_FINIT: -do_fmt: -#ifdef ENABLE_HDC_LOG - DEBUG("HDC: format_%s(%d) %d,%d\n", - (ccb->cmd==CMD_FORMAT_DRIVE)?"drive":"track", - drive->id, dev->track, dev->head); -#endif - - /* Activate the status icon. */ - hdd_active(drive->hdd_num, 1); - - /* Seek to cylinder. */ - if (do_seek(dev, drive, start_cyl)) { - intr = 1; - break; - } - dev->head = ccb->head; - dev->sector = 1; - - /* Get address of sector to write. */ - if (get_sector(dev, drive, &addr)) { - intr = 1; - break; - } - - /* - * For now, we don't use the info from - * the FCB, although we should at least - * use it's "filler byte" value... - */ -#if 0 - hdd_image_zero_ex(drive->hdd_num, addr, fcb->fill, drive->spt); -#else - hdd_image_zero(drive->hdd_num, addr, drive->spt); -#endif - - /* Done with this track. */ - dev->state = STATE_FDONE; - /*FALLTHROUGH*/ - - case STATE_FDONE: - /* One more track done. */ - if (++start_cyl == end_cyl) { - intr = 1; - break; - } - - /* De-activate the status icon. */ - hdd_active(SB_DISK|drive->hdd_num, 0); - - /* This saves us a LOT of code. */ - dev->state = STATE_FINIT; - goto do_fmt; - } - - /* If we errored out, go back idle. */ - if (intr) { - /* De-activate the status icon. */ - hdd_active(drive->hdd_num, 0); - - do_finish(dev); - } -} - - -/* Execute the CCB we just received. */ -static void -hdc_callback(void *priv) -{ - hdc_t *dev = (hdc_t *)priv; - ccb_t *ccb = &dev->ccb; - drive_t *drive; - off64_t addr; - int no_data = 0; - int val; - - /* Cancel timer. */ - dev->callback = 0; - - /* Clear the SSB error bits. */ - dev->ssb.track_0 = 0; - dev->ssb.cylinder_err = 0; - dev->ssb.write_fault = 0; - dev->ssb.seek_end = 0; - dev->ssb.not_ready = 0; - dev->ssb.id_not_found = 0; - dev->ssb.wrong_cyl = 0; - dev->ssb.all_bit_set = 0; - dev->ssb.mark_not_found = 0; - dev->ssb.ecc_crc_err = 0; - dev->ssb.ecc_crc_field = 0; - dev->ssb.valid = 1; - - /* We really only support one drive, but ohwell. */ - drive = &dev->drives[0]; - - switch (ccb->cmd) { - case CMD_READ_VERIFY: - no_data = 1; - /*FALLTHROUGH*/ - - case CMD_READ_SECTORS: -#if defined(ENABLE_HDC_LOG) && defined(_DEBUG) - if (dev->state == STATE_IDLE) dump_ccb(ccb); -#endif - if (! drive->present) { - dev->ssb.not_ready = 1; - do_finish(dev); - return; - } - - switch (dev->state) { - case STATE_IDLE: - /* Seek to cylinder if requested. */ - if (ccb->auto_seek) { - if (do_seek(dev, drive, - (ccb->cyl_low|(ccb->cyl_high<<8)))) { - do_finish(dev); - return; - } - } - dev->head = ccb->head; - dev->sector = ccb->sector; - - /* Get sector count and size. */ - dev->count = (int)ccb->count; - dev->buf_len = (128 << dev->ssb.sect_size); - - dev->state = STATE_SEND; - /*FALLTHROUGH*/ - - case STATE_SEND: - /* Activate the status icon. */ - hdd_active(drive->hdd_num, 1); - -do_send: -#ifdef ENABLE_HDC_LOG - DEBUG("HDC: read_%s(%d: %d,%d,%d) cnt=%d\n", - (no_data)?"verify":"sector", drive->id, - dev->track, dev->head, dev->sector, - dev->count); -#endif - - /* Get address of sector to load. */ - if (get_sector(dev, drive, &addr)) { - /* De-activate the status icon. */ - hdd_active(drive->hdd_num, 0); - do_finish(dev); - return; - } - - /* Read the block from the image. */ - hdd_image_read(drive->hdd_num, addr, 1, - (uint8_t *)dev->sector_buf); - - /* Ready to transfer the data out. */ - dev->state = STATE_SDATA; - dev->buf_idx = 0; - if (no_data) { - /* Delay a bit, no actual transfer. */ - dev->callback = HDC_TIME; - } else { - if (dev->ctrl & ACR_DMA_EN) { - /* DMA enabled. */ - dev->buf_ptr = dev->sector_buf; - dev->callback = HDC_TIME; - } else { - /* No DMA, do PIO. */ - dev->status |= (ASR_DATA_REQ|ASR_DIR); - - /* Copy from sector to data. */ - memcpy(dev->data, - dev->sector_buf, - dev->buf_len); - dev->buf_ptr = dev->data; - } - } - break; - - case STATE_SDATA: - if (! no_data) { - /* Perform DMA. */ - while (dev->buf_idx < dev->buf_len) { - val = dma_channel_write(dev->dma, - *dev->buf_ptr++); - if (val == DMA_NODATA) { - ERRLOG("HDC: CMD_READ_SECTORS out of data (idx=%d, len=%d)!\n", dev->buf_idx, dev->buf_len); - - /* De-activate the status icon. */ - hdd_active(drive->hdd_num, 0); - - dev->intstat |= ISR_EQUIP_CHECK; - dev->ssb.need_reset = 1; - do_finish(dev); - return; - } - dev->buf_idx++; - } - } - dev->state = STATE_SDONE; - dev->callback = HDC_TIME; - break; - - case STATE_SDONE: - dev->buf_idx = 0; - if (--dev->count == 0) { -#ifdef ENABLE_HDC_LOG - DEBUG("HDC: read_%s(%d) DONE\n", - (no_data)?"verify":"sector", - drive->id); -#endif - /* De-activate the status icon. */ - hdd_active(drive->hdd_num, 0); - - if (! (dev->ctrl & ACR_DMA_EN)) - dev->status &= ~(ASR_DATA_REQ|ASR_DIR); - dev->ssb.cmd_syndrome = 0xD4; - do_finish(dev); - return; - } - - /* Addvance to next sector. */ - next_sector(dev, drive); - - /* This saves us a LOT of code. */ - dev->state = STATE_SEND; - goto do_send; - } - break; - - case CMD_READ_EXT: /* READ_EXT */ - case CMD_READ_ID: /* READ_ID */ -#if defined(ENABLE_HDC_LOG) && defined(_DEBUG) - if (dev->state == STATE_IDLE) dump_ccb(ccb); -#endif - if (! drive->present) { - dev->ssb.not_ready = 1; - do_finish(dev); - return; - } - - dev->intstat |= ISR_INVALID_CMD; - do_finish(dev); - break; - - case CMD_RECALIBRATE: /* RECALIBRATE */ -#if defined(ENABLE_HDC_LOG) && defined(_DEBUG) - dump_ccb(ccb); -#endif -#ifdef ENABLE_HDC_LOG - DEBUG("HDC: recalibrate(%d) ready=%d\n", - drive->id, drive->present); -#endif - if (drive->present) { - dev->track = drive->cur_cyl = 0; - } else { - dev->ssb.not_ready = 1; - dev->intstat |= ISR_TERMINATION; - } - - do_finish(dev); - break; - - case CMD_WRITE_VERIFY: - no_data = 1; - /*FALLTHROUGH*/ - - case CMD_WRITE_SECTORS: -#if defined(ENABLE_HDC_LOG) && defined(_DEBUG) - dump_ccb(ccb); -#endif - if (! drive->present) { - dev->ssb.not_ready = 1; - do_finish(dev); - return; - } - - switch (dev->state) { - case STATE_IDLE: - /* Seek to cylinder if requested. */ - if (ccb->auto_seek) { - if (do_seek(dev, drive, - (ccb->cyl_low|(ccb->cyl_high<<8)))) { - do_finish(dev); - return; - } - } - dev->head = ccb->head; - dev->sector = ccb->sector; - - /* Get sector count and size. */ - dev->count = (int)ccb->count; - dev->buf_len = (128 << dev->ssb.sect_size); - - dev->state = STATE_RECV; - /*FALLTHROUGH*/ - - case STATE_RECV: - /* Activate the status icon. */ - hdd_active(drive->hdd_num, 1); -do_recv: -#ifdef ENABLE_HDC_LOG - DEBUG("HDC write_%s(%d: %d,%d,%d) cnt=%d\n", - (no_data)?"verify":"sector", drive->id, - dev->track, dev->head, dev->sector, - dev->count); -#endif - /* Ready to transfer the data in. */ - dev->state = STATE_RDATA; - dev->buf_idx = 0; - if (no_data) { - /* Delay a bit, no actual transfer. */ - dev->callback = HDC_TIME; - } else { - if (dev->ctrl & ACR_DMA_EN) { - /* DMA enabled. */ - dev->buf_ptr = dev->sector_buf; - dev->callback = HDC_TIME; - } else { - /* No DMA, do PIO. */ - dev->buf_ptr = dev->data; - dev->status |= ASR_DATA_REQ; - } - } - break; - - case STATE_RDATA: - if (! no_data) { - /* Perform DMA. */ - while (dev->buf_idx < dev->buf_len) { - val = dma_channel_read(dev->dma); - if (val == DMA_NODATA) { - ERRLOG("HDC: CMD_WRITE_SECTORS out of data (idx=%d, len=%d)!\n", dev->buf_idx, dev->buf_len); - - /* De-activate the status icon. */ - hdd_active(drive->hdd_num, 0); - - dev->intstat |= ISR_EQUIP_CHECK; - dev->ssb.need_reset = 1; - do_finish(dev); - return; - } - dev->buf_ptr[dev->buf_idx] = (val & 0xff); - dev->buf_idx++; - } - } - dev->state = STATE_RDONE; - dev->callback = HDC_TIME; - break; - - case STATE_RDONE: - /* Copy from data to sector if PIO. */ - if (! (dev->ctrl & ACR_DMA_EN)) - memcpy(dev->sector_buf, - dev->data, - dev->buf_len); - - /* Get address of sector to write. */ - if (get_sector(dev, drive, &addr)) { - /* De-activate the status icon. */ - hdd_active(drive->hdd_num, 0); - - do_finish(dev); - return; - } - - /* Write the block to the image. */ - hdd_image_write(drive->hdd_num, addr, 1, - (uint8_t *)dev->sector_buf); - - dev->buf_idx = 0; - if (--dev->count == 0) { -#ifdef ENABLE_HDC_LOG - DEBUG("HDC: write_%s(%d) DONE\n", - (no_data)?"verify":"sector", - drive->id); -#endif - /* De-activate the status icon. */ - hdd_active(drive->hdd_num, 0); - - if (! (dev->ctrl & ACR_DMA_EN)) - dev->status &= ~ASR_DATA_REQ; - dev->ssb.cmd_syndrome = 0xD4; - do_finish(dev); - return; - } - - /* Advance to next sector. */ - next_sector(dev, drive); - - /* This saves us a LOT of code. */ - dev->state = STATE_RECV; - goto do_recv; - } - break; - - case CMD_FORMAT_DRIVE: - case CMD_FORMAT_TRACK: -#if defined(ENABLE_HDC_LOG) && defined(_DEBUG) - if (dev->state == STATE_IDLE) dump_ccb(ccb); -#endif - do_format(dev, drive, ccb); - break; - - case CMD_SEEK: -#if defined(ENABLE_HDC_LOG) && defined(_DEBUG) - dump_ccb(ccb); -#endif - if (! drive->present) { - dev->ssb.not_ready = 1; - do_finish(dev); - return; - } - - if (ccb->ec_p == 1) { - /* Park the heads. */ - val = do_seek(dev, drive, drive->tracks-1); - } else { - /* Seek to cylinder. */ - val = do_seek(dev, drive, - (ccb->cyl_low|(ccb->cyl_high<<8))); - } -#ifdef ENABLE_HDC_LOG - DEBUG("HDC: %s(%d) cyl=%d, err=%d\n", - (ccb->ec_p)?"park":"seek", - drive->id, drive->cur_cyl, val); -#endif - if (! val) - dev->ssb.seek_end = 1; - do_finish(dev); - break; - - default: - ERRLOG("HDC: unknown command - %02x\n", ccb->cmd); -#if defined(ENABLE_HDC_LOG) && defined(_DEBUG) - dump_ccb(ccb); -#endif - dev->intstat |= ISR_INVALID_CMD; - do_finish(dev); - } -} - - -/* Prepare to send the SSB block. */ -static void -hdc_send_ssb(hdc_t *dev) -{ - drive_t *drive; - - /* We only support one drive, really, but ohwell. */ - drive = &dev->drives[0]; - - if (! dev->ssb.valid) { - /* Create a valid SSB. */ - memset(&dev->ssb, 0x00, sizeof(dev->ssb)); - dev->ssb.sect_size = 0x02; /* 512 bytes */ - dev->ssb.drive_type = drive->type; - } - - /* Update position fields. */ - dev->ssb.track_0 = !!(dev->track == 0); - dev->ssb.last_cyl_low = dev->ssb.curr_cyl_low; - dev->ssb.last_cyl_high = dev->ssb.curr_cyl_high; - dev->ssb.last_head = dev->ssb.curr_head; - dev->ssb.curr_cyl_high = ((dev->track >> 8) & 0x03); - dev->ssb.curr_cyl_low = (dev->track & 0xff); - dev->ssb.curr_head = (dev->head & 0x0f); - - dev->ssb.headsel_state = dev->ssb.curr_head; - dev->ssb.last_sect = dev->sector; - - /* We abuse an unused MBZ bit, so clear it. */ - dev->ssb.valid = 0; - - /* Set up the transfer buffer for the SSB. */ - dev->buf_idx = 0; - dev->buf_len = sizeof(dev->ssb); - dev->buf_ptr = (uint8_t *)&dev->ssb; - -#if defined(ENABLE_HDC_LOG) && defined(_DEBUG) - dump_ssb(&dev->ssb); -#endif - - /* Done with the SSB. */ - dev->attn &= ~ATT_SSB; -} - - -/* Read one of the controller registers. */ -static uint8_t -hdc_read(uint16_t port, priv_t priv) -{ - hdc_t *dev = (hdc_t *)priv; - uint8_t ret = 0xff; - - /* TRM: tell system board we are alive. */ - ps1_set_feedback(dev->sys); - - switch (port & 7) { - case 0: /* DATA register */ - if (dev->state == STATE_SDATA) { - if (dev->buf_idx > dev->buf_len) { - ERRLOG("HDC: read with empty buffer!\n"); - dev->state = STATE_IDLE; - dev->intstat |= ISR_INVALID_CMD; - dev->status &= (ASR_TX_EN|ASR_DATA_REQ|ASR_DIR); - set_intr(dev, 1); - break; - } - - ret = dev->buf_ptr[dev->buf_idx]; - if (++dev->buf_idx == dev->buf_len) { - /* Data block sent OK. */ - dev->status &= ~(ASR_TX_EN|ASR_DATA_REQ|ASR_DIR); - dev->state = STATE_IDLE; - } - } - break; - - case 2: /* ASR */ - ret = dev->status; - break; - - case 4: /* ISR */ - ret = dev->intstat; - dev->intstat = 0x00; - break; - } - - return(ret); -} - - -/* Write to one of the controller registers. */ -static void -hdc_write(uint16_t port, uint8_t val, priv_t priv) -{ - hdc_t *dev = (hdc_t *)priv; - - /* TRM: tell system board we are alive. */ - ps1_set_feedback(dev->sys); - - switch (port & 7) { - case 0: /* DATA register */ - if (dev->state == STATE_RDATA) { - if (dev->buf_idx >= dev->buf_len) { - ERRLOG("HDC: write with full buffer!\n"); - dev->intstat |= ISR_INVALID_CMD; - dev->status &= ~ASR_DATA_REQ; - set_intr(dev, 1); - break; - } - - /* Store the data into the buffer. */ - dev->buf_ptr[dev->buf_idx] = val; - if (++dev->buf_idx == dev->buf_len) { - /* We got all the data we need. */ - dev->status &= ~ASR_DATA_REQ; - dev->state = STATE_IDLE; - - /* If we were receiving a CCB, execute it. */ - if (dev->attn & ATT_CCB) { - /* - * If we were already busy with - * a CCB, then it must have had - * some new data using PIO. - */ - if (dev->status & ASR_BUSY) - dev->state = STATE_RDONE; - else - dev->status |= ASR_BUSY; - - /* Schedule command execution. */ - dev->callback = HDC_TIME; - } - } - } - break; - - case 2: /* ACR */ - dev->ctrl = val; - if (val & ACR_INT_EN) - set_intr(dev, 0); /* clear IRQ */ - - if (dev->reset != 0) { - if (++dev->reset == 3) { - dev->reset = 0; - - set_intr(dev, 1); - } - break; - } - - if (val & ACR_RESET) - dev->reset = 1; - break; - - case 4: /* ATTN */ - dev->status &= ~ASR_INT_REQ; - if (val & ATT_DATA) { - /* Dunno. Start PIO/DMA now? */ - } - - if (val & ATT_SSB) { - if (dev->attn & ATT_CCB) { - /* Hey now, we're still busy for you! */ - dev->intstat |= ISR_INVALID_CMD; - set_intr(dev, 1); - break; - } - - /* OK, prepare for sending an SSB. */ - dev->attn |= ATT_SSB; - - /* Grab or initialize an SSB to send. */ - hdc_send_ssb(dev); - - dev->state = STATE_SDATA; - dev->status |= (ASR_TX_EN|ASR_DATA_REQ|ASR_DIR); - set_intr(dev, 1); - } - - if (val & ATT_CCB) { - dev->attn |= ATT_CCB; - - /* Set up the transfer buffer for a CCB. */ - dev->buf_idx = 0; - dev->buf_len = sizeof(dev->ccb); - dev->buf_ptr = (uint8_t *)&dev->ccb; - - dev->state = STATE_RDATA; - dev->status |= ASR_DATA_REQ; - set_intr(dev, 1); - } - break; - } -} - - -static void -hdc_close(priv_t priv) -{ - hdc_t *dev = (hdc_t *)priv; - drive_t *drive; - int d; - - /* Remove the I/O handler. */ - io_removehandler(dev->base, 5, - hdc_read,NULL,NULL, hdc_write,NULL,NULL, dev); - - /* Close all disks and their images. */ - for (d = 0; d < PS1_HDD_NUM; d++) { - drive = &dev->drives[d]; - - if (drive->present) - hdd_image_close(drive->hdd_num); - } - - /* Release the device. */ - free(dev); -} - - -static priv_t -hdc_init_ps1(UNUSED(const device_t *info), void *parent) -{ - drive_t *drive; - hdc_t *dev; - int c, i; - - /* Allocate and initialize device block. */ - dev = (hdc_t *)mem_alloc(sizeof(hdc_t)); - memset(dev, 0x00, sizeof(hdc_t)); - dev->sys = (priv_t)parent; - - /* Set up controller parameters for PS/1 2011. */ - dev->base = 0x0320; - dev->irq = 14; - dev->dma = 3; - - DEBUG("HDC: initializing (I/O=%04X, IRQ=%d, DMA=%d)\n", - dev->base, dev->irq, dev->dma); - - /* Load any disks for this device class. */ - c = 0; - for (i = 0; i < HDD_NUM; i++) { - if ((hdd[i].bus == HDD_BUS_IDE) && (hdd[i].bus_id.ide_channel < PS1_HDD_NUM)) { - drive = &dev->drives[hdd[i].bus_id.ide_channel]; - - if (! hdd_image_load(i)) { - drive->present = 0; - continue; - } - drive->id = c; - - /* These are the "hardware" parameters (from the image.) */ - drive->cfg_spt = (uint8_t)(hdd[i].spt & 0xff); - drive->cfg_hpc = (uint8_t)(hdd[i].hpc & 0xff); - drive->cfg_tracks = (uint16_t)hdd[i].tracks; - - /* Use them as "active" parameters until overwritten. */ - drive->spt = drive->cfg_spt; - drive->hpc = drive->cfg_hpc; - drive->tracks = drive->cfg_tracks; - - drive->type = ibm_drive_type(drive); - drive->hdd_num = i; - drive->present = 1; - - INFO("HDC: drive%d (type %d: cyl=%d,hd=%d,spt=%d), disk %d\n", - hdd[i].bus_id.ide_channel, drive->type, - drive->tracks, drive->hpc, drive->spt, i); - - if (++c > PS1_HDD_NUM) break; - } - } - - /* Sectors are 1-based. */ - dev->sector = 1; - - /* Enable the I/O block. */ - io_sethandler(dev->base, 5, - hdc_read,NULL,NULL, hdc_write,NULL,NULL, dev); - - /* Create a timer for command delays. */ - timer_add(hdc_callback, dev, &dev->callback, &dev->callback); - - return((priv_t)dev); -} - - -const device_t ps1_hdc_device = { - "PS/1 2011 Fixed Disk Controller", - DEVICE_ISA | DEVICE_PS2, - 0, - NULL, - hdc_init_ps1, hdc_close, NULL, - NULL, NULL, NULL, NULL, - NULL -};