Annotation of previous_trunk/src/mo.c, revision 1.1

1.1     ! root        1: /*  Previous - mo.c
        !             2:  
        !             3:  This file is distributed under the GNU Public License, version 2 or at
        !             4:  your option any later version. Read the file gpl.txt for details.
        !             5:  
        !             6:  Canon Magneto-Optical Disk Drive and NeXT Optical Storage Processor emulation.
        !             7:   
        !             8:  NeXT Optical Storage Processor uses Reed-Solomon algorithm for error correction.
        !             9:  It has two 1296 (128?) byte internal buffers and uses double-buffering to perform
        !            10:  error correction.
        !            11:   
        !            12:  */
        !            13: 
        !            14: /* TODO:
        !            15:  * - Fix soft timeouts when polling empty second drive
        !            16:  * - Add realistic seek timings
        !            17:  * - Check drive error handling (attn conditions)
        !            18:  */
        !            19: 
        !            20: #include "ioMem.h"
        !            21: #include "ioMemTables.h"
        !            22: #include "m68000.h"
        !            23: #include "configuration.h"
        !            24: #include "mo.h"
        !            25: #include "sysReg.h"
        !            26: #include "dma.h"
        !            27: #include "floppy.h"
        !            28: #include "file.h"
        !            29: #include "rs.h"
        !            30: #include "statusbar.h"
        !            31: 
        !            32: 
        !            33: #define LOG_MO_REG_LEVEL    LOG_DEBUG
        !            34: #define LOG_MO_CMD_LEVEL    LOG_DEBUG
        !            35: #define LOG_MO_ECC_LEVEL    LOG_DEBUG
        !            36: #define LOG_MO_IO_LEVEL     LOG_DEBUG
        !            37: 
        !            38: #define IO_SEG_MASK    0x1FFFF
        !            39: 
        !            40: 
        !            41: /* Registers */
        !            42: 
        !            43: struct {
        !            44:     Uint8 tracknuml;
        !            45:     Uint8 tracknumh;
        !            46:     Uint8 sector_num;
        !            47:     Uint8 sector_count;
        !            48:     Uint8 intstatus;
        !            49:     Uint8 intmask;
        !            50:     Uint8 ctrlr_csr2;
        !            51:     Uint8 ctrlr_csr1;
        !            52:     Uint8 csrl;
        !            53:     Uint8 csrh;
        !            54:     Uint8 err_stat;
        !            55:     Uint8 ecc_cnt;
        !            56:     Uint8 init;
        !            57:     Uint8 format;
        !            58:     Uint8 mark;
        !            59:     Uint8 flag[7];
        !            60: } mo;
        !            61: int sector_counter;
        !            62: 
        !            63: struct {
        !            64:     Uint16 status;
        !            65:     Uint16 dstat;
        !            66:     Uint16 estat;
        !            67:     Uint16 hstat;
        !            68:     
        !            69:     Uint8 head;
        !            70:     
        !            71:     Uint32 head_pos;
        !            72:     Uint32 ho_head_pos;
        !            73:     Uint32 sec_offset;
        !            74:     
        !            75:     FILE* dsk;
        !            76:     
        !            77:     bool spinning;
        !            78:     bool spiraling;
        !            79:     bool seeking;
        !            80:     
        !            81:     bool attn;
        !            82:     bool complete;
        !            83:     
        !            84:     bool protected;
        !            85:     bool inserted;
        !            86:     bool connected;
        !            87: } modrv[MO_MAX_DRIVES];
        !            88: 
        !            89: int dnum;
        !            90: 
        !            91: 
        !            92: #define NO_HEAD     0
        !            93: #define READ_HEAD   1
        !            94: #define WRITE_HEAD  2
        !            95: #define ERASE_HEAD  3
        !            96: #define VERIFY_HEAD 4
        !            97: #define RF_HEAD     5
        !            98: 
        !            99: 
        !           100: /* Sector increment and number */
        !           101: #define MOSEC_NUM_MASK      0x0F /* rw */
        !           102: #define MOSEC_INCR_MASK     0xF0 /* wo */
        !           103: 
        !           104: /* Interrupt status */
        !           105: #define MOINT_CMD_COMPL     0x01 /* ro */
        !           106: #define MOINT_ATTN          0x02 /* ro */
        !           107: #define MOINT_OPER_COMPL    0x04 /* rw */
        !           108: #define MOINT_ECC_DONE      0x08 /* rw */
        !           109: #define MOINT_TIMEOUT       0x10 /* rw */
        !           110: #define MOINT_READ_FAULT    0x20 /* rw */
        !           111: #define MOINT_PARITY_ERR    0x40 /* rw */
        !           112: #define MOINT_DATA_ERR      0x80 /* rw */
        !           113: #define MOINT_RESET         0x01 /* wo */
        !           114: #define MOINT_GPO           0x02 /* wo */
        !           115: 
        !           116: #define MOINT_OSP_MASK      0xFC
        !           117: #define MOINT_MO_MASK       0x03
        !           118: 
        !           119: /* Controller CSR 2 */
        !           120: #define MOCSR2_DRIVE_SEL    0x01
        !           121: #define MOCSR2_ECC_CMP      0x02
        !           122: #define MOCSR2_BUF_TOGGLE   0x04
        !           123: #define MOCSR2_CLR_BUFP     0x08
        !           124: #define MOCSR2_ECC_BLOCKS   0x10
        !           125: #define MOCSR2_ECC_MODE     0x20
        !           126: #define MOCSR2_ECC_DIS      0x40
        !           127: #define MOCSR2_SECT_TIMER   0x80
        !           128: 
        !           129: /* Controller CSR 1 */
        !           130: /* see below (formatter commands) */
        !           131: 
        !           132: /* Drive CSR (lo and hi) */
        !           133: /* see below (drive commands) */
        !           134: 
        !           135: /* Data error status */
        !           136: #define ERRSTAT_ECC         0x01
        !           137: #define ERRSTAT_CMP         0x02
        !           138: #define ERRSTAT_TIMING      0x04
        !           139: #define ERRSTAT_STARVE      0x08
        !           140: 
        !           141: /* Init */
        !           142: #define MOINIT_ID_MASK      0x03
        !           143: #define MOINIT_EVEN_PAR     0x04
        !           144: #define MOINIT_DMA_STV_ENA  0x08
        !           145: #define MOINIT_25_MHZ       0x10
        !           146: #define MOINIT_ID_CMP_TRK   0x20
        !           147: #define MOINIT_ECC_STV_DIS  0x40
        !           148: #define MOINIT_SEC_GREATER  0x80
        !           149: 
        !           150: #define MOINIT_ID_34    0
        !           151: #define MOINIT_ID_234   1
        !           152: #define MOINIT_ID_1234  3
        !           153: #define MOINIT_ID_0     2
        !           154: 
        !           155: /* Format */
        !           156: #define MOFORM_RD_GATE_NOM  0x06
        !           157: #define MOFORM_WR_GATE_NOM  0x30
        !           158: 
        !           159: #define MOFORM_RD_GATE_MIN  0x00
        !           160: #define MOFORM_RD_GATE_MAX  0x0F
        !           161: #define MOFORM_RD_GATE_MASK 0x0F
        !           162: 
        !           163: 
        !           164: /* Disk layout */
        !           165: #define MO_SEC_PER_TRACK    16
        !           166: #define MO_TRACK_OFFSET     4096 /* offset to first logical sector of kernel driver is 4149 */
        !           167: #define MO_TRACK_LIMIT      (19819-(MO_TRACK_OFFSET)) /* no more tracks beyond this offset */
        !           168: 
        !           169: #define MO_SECTORSIZE_DISK  1296 /* size of encoded sector, like stored on disk */
        !           170: #define MO_SECTORSIZE_DATA  1024 /* size of decoded sector, like handled by software */
        !           171: 
        !           172: 
        !           173: static Uint32 get_logical_sector(Uint32 sector_id) {
        !           174:     Sint32 tracknum = (sector_id&0xFFFF00)>>8;
        !           175:     Uint8 sectornum = sector_id&0x0F;
        !           176: 
        !           177:     tracknum-=MO_TRACK_OFFSET;
        !           178:     if (tracknum<0 || tracknum>=MO_TRACK_LIMIT) {
        !           179:         Log_Printf(LOG_WARN, "MO disk %i: Error! Bad sector (%i)! Disk limit exceeded.", dnum,
        !           180:                    (tracknum*MO_SEC_PER_TRACK)+mo.sector_num);
        !           181:         abort();
        !           182:     }
        !           183: 
        !           184:     return (tracknum*MO_SEC_PER_TRACK)+sectornum;
        !           185: }
        !           186: 
        !           187: 
        !           188: 
        !           189: /* Functions */
        !           190: void mo_formatter_cmd(void);
        !           191: void mo_formatter_cmd2(void);
        !           192: void mo_drive_cmd(void);
        !           193: 
        !           194: void mo_reset(void);
        !           195: void osp_select(int drive);
        !           196: 
        !           197: void MO_Init(void);
        !           198: void MO_Uninit(void);
        !           199: 
        !           200: /* Experimental */
        !           201: #define SECTOR_IO_DELAY 1250
        !           202: #define CMD_DELAY       40
        !           203: 
        !           204: #define SEEK_TIMING 1
        !           205: 
        !           206: static void mo_set_signals(bool complete, bool attn, int delay);
        !           207: static void mo_push_signals(bool complete, bool attn, int drive);
        !           208: static void osp_poll_mo_signals(void);
        !           209: 
        !           210: static void ecc_read(void);
        !           211: static void ecc_write(void);
        !           212: static void ecc_verify(void);
        !           213: 
        !           214: static void mo_read_sector(Uint32 sector_id);
        !           215: static void mo_write_sector(Uint32 sector_id);
        !           216: static void mo_erase_sector(Uint32 sector_id);
        !           217: static void mo_verify_sector(Uint32 sector_id);
        !           218: 
        !           219: static void mo_seek(Uint16 command);
        !           220: static void mo_high_order_seek(Uint16 command);
        !           221: static void mo_jump_head(Uint16 command);
        !           222: static void mo_recalibrate(void);
        !           223: static void mo_return_drive_status(void);
        !           224: static void mo_return_track_addr(void);
        !           225: static void mo_return_extended_status(void);
        !           226: static void mo_return_hardware_status(void);
        !           227: static void mo_return_version(void);
        !           228: static void mo_select_head(int head);
        !           229: static void mo_reset_attn_status(void);
        !           230: static void mo_stop_spinning(void);
        !           231: static void mo_start_spinning(void);
        !           232: static void mo_eject_disk(int drv);
        !           233: static void mo_start_spiraling(void);
        !           234: static void mo_stop_spiraling(void);
        !           235: static void mo_self_diagnostic(void);
        !           236: 
        !           237: static Uint32 get_logical_sector(Uint32 sector_id);
        !           238: static void fmt_sector_done(void);
        !           239: static bool fmt_match_id(Uint32 sector_id);
        !           240: static void fmt_io(Uint32 sector_id);
        !           241: static void ecc_toggle_buffer(void);
        !           242: static void ecc_clear_buffer(void);
        !           243: static void ecc_decode(void);
        !           244: static void ecc_encode(void);
        !           245: static void ecc_sequence_done(void);
        !           246: static bool mo_drive_empty(void);
        !           247: static bool mo_protected(void);
        !           248: static void mo_unimplemented_cmd(void);
        !           249: static void mo_spiraling_operation(void);
        !           250: static Uint32 get_logical_sector(Uint32 sector_id);
        !           251: static void mo_insert_disk(int drv);
        !           252: 
        !           253: static int sector_increment = 0;
        !           254: 
        !           255: static void osp_interrupt(Uint8 interrupt);
        !           256: 
        !           257: 
        !           258: /* ------------------------ OPTICAL STORAGE PROCESSOR ------------------------ */
        !           259: 
        !           260: /* OSP registers */
        !           261: 
        !           262: void MO_TrackNumH_Read(void) { // 0x02012000
        !           263:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.tracknumh;
        !           264:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Track number hi read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           265: }
        !           266: 
        !           267: void MO_TrackNumH_Write(void) {
        !           268:     mo.tracknumh=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           269:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Track number hi write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           270: }
        !           271: 
        !           272: void MO_TrackNumL_Read(void) { // 0x02012001
        !           273:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.tracknuml;
        !           274:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Track number lo read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           275: }
        !           276: 
        !           277: void MO_TrackNumL_Write(void) {
        !           278:     mo.tracknuml=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           279:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Track number lo write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           280: }
        !           281: 
        !           282: void MO_SectorIncr_Read(void) { // 0x02012002
        !           283:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.sector_num&MOSEC_NUM_MASK;
        !           284:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Sector increment and number read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           285: }
        !           286: 
        !           287: void MO_SectorIncr_Write(void) {
        !           288:     Uint8 val = IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           289:     mo.sector_num = val&MOSEC_NUM_MASK;
        !           290:     sector_increment = (val&MOSEC_INCR_MASK)>>4;
        !           291:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Sector increment and number write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           292: }
        !           293: 
        !           294: void MO_SectorCnt_Read(void) { // 0x02012003
        !           295:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = sector_counter&0xFF;
        !           296:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Sector count read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           297: }
        !           298: 
        !           299: void MO_SectorCnt_Write(void) {
        !           300:     sector_counter=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           301:     if (sector_counter==0) {
        !           302:         sector_counter=0x100;
        !           303:     }
        !           304:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Sector count write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           305: }
        !           306: 
        !           307: void MO_IntStatus_Read(void) { // 0x02012004
        !           308:     osp_poll_mo_signals();
        !           309:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.intstatus;
        !           310:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Interrupt status read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           311: }
        !           312: 
        !           313: void MO_IntStatus_Write(void) {
        !           314:     Uint8 val = IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           315:     osp_poll_mo_signals();
        !           316:     mo.intstatus &= ~(val&MOINT_OSP_MASK);
        !           317:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Interrupt status write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           318: 
        !           319:     if ((mo.intstatus&mo.intmask)==0) {
        !           320:         set_interrupt(INT_DISK, RELEASE_INT);
        !           321:     }
        !           322:     if (ConfigureParams.System.nMachineType==NEXT_CUBE030) {
        !           323:         set_floppy_select(val&MOINT_GPO, true);
        !           324:     }
        !           325:     if (val&MOINT_RESET) {
        !           326:         Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Hard reset\n");
        !           327:         mo_reset();
        !           328:     }
        !           329: }
        !           330: 
        !           331: void MO_IntMask_Read(void) { // 0x02012005
        !           332:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.intmask;
        !           333:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Interrupt mask read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           334: }
        !           335: 
        !           336: void MO_IntMask_Write(void) {
        !           337:     mo.intmask=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           338:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Interrupt mask write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           339: 
        !           340:     osp_poll_mo_signals();
        !           341:     if ((mo.intstatus&mo.intmask)==0) {
        !           342:         set_interrupt(INT_DISK, RELEASE_INT);
        !           343:     } else {
        !           344:         set_interrupt(INT_DISK, SET_INT);
        !           345:     }
        !           346: }
        !           347: 
        !           348: void MOctrl_CSR2_Read(void) { // 0x02012006
        !           349:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.ctrlr_csr2;
        !           350:        Log_Printf(LOG_MO_REG_LEVEL,"[MO Controller] CSR2 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           351: }
        !           352: 
        !           353: void MOctrl_CSR2_Write(void) {
        !           354:     mo.ctrlr_csr2=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           355:        Log_Printf(LOG_MO_REG_LEVEL,"[MO Controller] CSR2 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           356:     
        !           357:     mo_formatter_cmd2();
        !           358: }
        !           359: 
        !           360: void MOctrl_CSR1_Read(void) { // 0x02012007
        !           361:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.ctrlr_csr1;
        !           362:        Log_Printf(LOG_MO_REG_LEVEL,"[MO Controller] CSR1 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           363: }
        !           364: 
        !           365: void MOctrl_CSR1_Write(void) {
        !           366:     mo.ctrlr_csr1=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           367:        Log_Printf(LOG_MO_REG_LEVEL,"[MO Controller] CSR1 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           368:     
        !           369:     mo_formatter_cmd();
        !           370: }
        !           371: 
        !           372: void MO_CSR_H_Read(void) { // 0x02012009
        !           373:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.csrh;
        !           374:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] CSR hi read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           375: }
        !           376: 
        !           377: void MO_CSR_H_Write(void) {
        !           378:     mo.csrh=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           379:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] CSR hi write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           380: }
        !           381: 
        !           382: void MO_CSR_L_Read(void) { // 0x02012008
        !           383:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.csrl;
        !           384:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] CSR lo read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           385: }
        !           386: 
        !           387: void MO_CSR_L_Write(void) {
        !           388:     mo.csrl=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           389:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] CSR lo write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           390:     
        !           391:     mo_drive_cmd();
        !           392: }
        !           393: 
        !           394: void MO_ErrStat_Read(void) { // 0x0201200a
        !           395:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.err_stat;
        !           396:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Error status read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           397: }
        !           398: 
        !           399: void MO_EccCnt_Read(void) { // 0x0201200b
        !           400:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.ecc_cnt;
        !           401:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] ECC count read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           402: }
        !           403: 
        !           404: void MO_Init_Write(void) { // 0x0201200c
        !           405:     mo.init=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           406:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Init write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           407: }
        !           408: 
        !           409: void MO_Format_Write(void) { // 0x0201200d
        !           410:     mo.format=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           411:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Format write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           412: }
        !           413: 
        !           414: void MO_Mark_Write(void) { // 0x0201200e
        !           415:     mo.mark=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           416:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Mark write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           417: }
        !           418: 
        !           419: void MO_Flag0_Read(void) { // 0x02012010
        !           420:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.flag[0];
        !           421:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 0 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           422: }
        !           423: 
        !           424: void MO_Flag0_Write(void) {
        !           425:     mo.flag[0]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           426:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 0 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           427: }
        !           428: 
        !           429: void MO_Flag1_Read(void) { // 0x02012011
        !           430:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.flag[1];
        !           431:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 1 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           432: }
        !           433: 
        !           434: void MO_Flag1_Write(void) {
        !           435:     mo.flag[1]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           436:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 1 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           437: }
        !           438: 
        !           439: void MO_Flag2_Read(void) { // 0x02012012
        !           440:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.flag[2];
        !           441:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 2 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           442: }
        !           443: 
        !           444: void MO_Flag2_Write(void) {
        !           445:     mo.flag[2]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           446:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 2 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           447: }
        !           448: 
        !           449: void MO_Flag3_Read(void) { // 0x02012013
        !           450:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.flag[3];
        !           451:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 3 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           452: }
        !           453: 
        !           454: void MO_Flag3_Write(void) {
        !           455:     mo.flag[3]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           456:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 3 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           457: }
        !           458: 
        !           459: void MO_Flag4_Read(void) { // 0x02012014
        !           460:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.flag[4];
        !           461:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 4 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           462: }
        !           463: 
        !           464: void MO_Flag4_Write(void) {
        !           465:     mo.flag[4]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           466:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 4 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           467: }
        !           468: 
        !           469: void MO_Flag5_Read(void) { // 0x02012015
        !           470:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.flag[5];
        !           471:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 5 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           472: }
        !           473: 
        !           474: void MO_Flag5_Write(void) {
        !           475:     mo.flag[5]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           476:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 5 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           477: }
        !           478: 
        !           479: void MO_Flag6_Read(void) { // 0x02012016
        !           480:     IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = mo.flag[6];
        !           481:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 6 read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           482: }
        !           483: 
        !           484: void MO_Flag6_Write(void) {
        !           485:     mo.flag[6]=IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
        !           486:        Log_Printf(LOG_MO_REG_LEVEL,"[MO] Flag 6 write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress & IO_SEG_MASK], m68k_getpc());
        !           487: }
        !           488: 
        !           489: #if 0
        !           490: /* Register debugging */
        !           491: static void print_regs(void) {
        !           492:     int i;
        !           493:     Log_Printf(LOG_WARN,"sector ID:  %02X%02X%02X",mo.tracknumh,mo.tracknuml,mo.sector_num);
        !           494:     Log_Printf(LOG_WARN,"head pos:   %04X",modrv[dnum].head_pos);
        !           495:     Log_Printf(LOG_WARN,"sector cnt: %02X",sector_counter&0xFF);
        !           496:     Log_Printf(LOG_WARN,"intstatus:  %02X",mo.intstatus);
        !           497:     Log_Printf(LOG_WARN,"intmask:    %02X",mo.intmask);
        !           498:     Log_Printf(LOG_WARN,"ctrlr csr2: %02X",mo.ctrlr_csr2);
        !           499:     Log_Printf(LOG_WARN,"ctrlr csr1: %02X",mo.ctrlr_csr1);
        !           500:     Log_Printf(LOG_WARN,"drive csrl: %02X",mo.csrl);
        !           501:     Log_Printf(LOG_WARN,"drive csrh: %02X",mo.csrh);
        !           502:     Log_Printf(LOG_WARN,"errstat:    %02X",mo.err_stat);
        !           503:     Log_Printf(LOG_WARN,"ecc count:  %02X",mo.ecc_cnt);
        !           504:     Log_Printf(LOG_WARN,"init:       %02X",mo.init);
        !           505:     Log_Printf(LOG_WARN,"format:     %02X",mo.format);
        !           506:     Log_Printf(LOG_WARN,"mark:       %02X",mo.mark);
        !           507:     for (i=0; i<7; i++) {
        !           508:         Log_Printf(LOG_WARN,"flag %i:     %02X",i+1,mo.flag[i]);
        !           509:     }
        !           510: }
        !           511: #endif
        !           512: 
        !           513: void osp_interrupt(Uint8 interrupt) {
        !           514:     mo.intstatus|=interrupt;
        !           515:     if (interrupt&mo.intmask) {
        !           516:         set_interrupt(INT_DISK, SET_INT);
        !           517:     }
        !           518: }
        !           519: 
        !           520: 
        !           521: enum {
        !           522:     ECC_MODE_READ,
        !           523:     ECC_MODE_WRITE,
        !           524:     ECC_MODE_VERIFY
        !           525: } ecc_mode;
        !           526: 
        !           527: enum {
        !           528:     ECC_STATE_FILLING,
        !           529:     ECC_STATE_DRAINING,
        !           530:     ECC_STATE_ECCING,
        !           531:     ECC_STATE_WAITING,
        !           532:     ECC_STATE_DONE
        !           533: } ecc_state;
        !           534: 
        !           535: /* Formatter commands */
        !           536: 
        !           537: #define FMT_RESET       0x00
        !           538: #define FMT_ECC_READ    0x80
        !           539: #define FMT_ECC_WRITE   0x40
        !           540: #define FMT_RD_STAT     0x20
        !           541: #define FMT_ID_READ     0x10
        !           542: #define FMT_VERIFY      0x08
        !           543: #define FMT_ERASE       0x04
        !           544: #define FMT_READ        0x02
        !           545: #define FMT_WRITE       0x01
        !           546: 
        !           547: enum {
        !           548:     FMT_MODE_READ,
        !           549:     FMT_MODE_WRITE,
        !           550:     FMT_MODE_ERASE,
        !           551:     FMT_MODE_VERIFY,
        !           552:     FMT_MODE_READ_ID,
        !           553:     FMT_MODE_IDLE
        !           554: } fmt_mode;
        !           555: 
        !           556: bool write_timing;
        !           557: 
        !           558: void mo_formatter_cmd(void) {
        !           559:     
        !           560:     if (mo.ctrlr_csr1==FMT_RESET) {
        !           561:         Log_Printf(LOG_MO_CMD_LEVEL,"[OSP] Formatter command: Reset (%02X)\n", mo.ctrlr_csr1);
        !           562:         if (fmt_mode!=FMT_MODE_IDLE) {
        !           563:             Log_Printf(LOG_WARN,"[OSP] Warning: Formatter reset while busy!\n");
        !           564:         }
        !           565:         fmt_mode = FMT_MODE_IDLE;
        !           566:         ecc_state = ECC_STATE_DONE;
        !           567:         mo.ecc_cnt=0;
        !           568:         mo.err_stat=0;
        !           569:         return;
        !           570:     }
        !           571:     if (mo.ctrlr_csr1&FMT_ECC_READ) {
        !           572:         Log_Printf(LOG_MO_CMD_LEVEL,"[OSP] Formatter command: ECC Read (%02X)\n", mo.ctrlr_csr1);
        !           573:         ecc_read();
        !           574:     }
        !           575:     if (mo.ctrlr_csr1&FMT_ECC_WRITE) {
        !           576:         Log_Printf(LOG_MO_CMD_LEVEL,"[OSP] Formatter command: ECC Write (%02X)\n", mo.ctrlr_csr1);
        !           577:         ecc_write();
        !           578:     }
        !           579:     if (mo.ctrlr_csr1&FMT_RD_STAT) {
        !           580:         Log_Printf(LOG_MO_CMD_LEVEL,"[OSP] Formatter command: Read Status (%02X)\n", mo.ctrlr_csr1);
        !           581:         mo.csrh = (modrv[dnum].status>>8)&0xFF;
        !           582:         mo.csrl = modrv[dnum].status&0xFF;
        !           583:     }
        !           584:     if (mo.ctrlr_csr1&FMT_ID_READ) {
        !           585:         Log_Printf(LOG_MO_CMD_LEVEL,"[OSP] Formatter command: ID Read (%02X)\n", mo.ctrlr_csr1);
        !           586:         fmt_mode = FMT_MODE_READ_ID;
        !           587:     }
        !           588:     if (mo.ctrlr_csr1&FMT_VERIFY) {
        !           589:         Log_Printf(LOG_MO_CMD_LEVEL,"[OSP] Formatter command: Verify (%02X)\n", mo.ctrlr_csr1);
        !           590:         fmt_mode = FMT_MODE_VERIFY;
        !           591:     }
        !           592:     if (mo.ctrlr_csr1&FMT_ERASE) {
        !           593:         Log_Printf(LOG_MO_CMD_LEVEL,"[OSP] Formatter command: Erase (%02X)\n", mo.ctrlr_csr1);
        !           594:         fmt_mode = FMT_MODE_ERASE;
        !           595:     }
        !           596:     if (mo.ctrlr_csr1&FMT_READ) {
        !           597:         Log_Printf(LOG_MO_CMD_LEVEL,"[OSP] Formatter command: Read (%02X)\n", mo.ctrlr_csr1);
        !           598:         fmt_mode = FMT_MODE_READ;
        !           599:     }
        !           600:     if (mo.ctrlr_csr1&FMT_WRITE) {
        !           601:         Log_Printf(LOG_MO_CMD_LEVEL,"[OSP] Formatter command: Write (%02X)\n", mo.ctrlr_csr1);
        !           602:         write_timing = false;
        !           603:         fmt_mode = FMT_MODE_WRITE;
        !           604:     }
        !           605: }
        !           606: 
        !           607: void fmt_sector_done(void) {
        !           608:     Uint16 track = (mo.tracknumh<<8)|mo.tracknuml;
        !           609:     mo.sector_num+=sector_increment;
        !           610:     track+=mo.sector_num/MO_SEC_PER_TRACK;
        !           611:     mo.sector_num%=MO_SEC_PER_TRACK;
        !           612:     mo.tracknumh = (track>>8)&0xFF;
        !           613:     mo.tracknuml = track&0xFF;
        !           614:     /* CHECK: decrement with sector_increment value? */
        !           615:     sector_counter--;
        !           616:     /* Check if the operation is complete */
        !           617:     if (sector_counter==0) {
        !           618:         fmt_mode = FMT_MODE_IDLE;
        !           619:         osp_interrupt(MOINT_OPER_COMPL);
        !           620:     }
        !           621: }
        !           622: 
        !           623: int sector_timer=0;
        !           624: #define SECTOR_TIMEOUT_COUNT    100 /* FIXME: what is the correct value? */
        !           625: bool fmt_match_id(Uint32 sector_id) {
        !           626:     if ((mo.init&MOINIT_ID_MASK)==MOINIT_ID_0) {
        !           627:         Log_Printf(LOG_MO_CMD_LEVEL, "[OSP] Sector ID matching disabled!");
        !           628:         abort(); /* CHECK: this routine is critical to disk image corruption, check if it gives correct results */
        !           629:         return true;
        !           630:     }
        !           631:     
        !           632:     Uint32 fmt_id = (mo.tracknumh<<16)|(mo.tracknuml<<8)|mo.sector_num;
        !           633:     
        !           634:     if (mo.init&MOINIT_ID_CMP_TRK) {
        !           635:         Log_Printf(LOG_MO_CMD_LEVEL, "[OSP] Compare only track ID.");
        !           636:         fmt_id=(fmt_id>>8)&0xFFFF;
        !           637:         sector_id=(sector_id>>8)&0xFFFF;
        !           638:     }
        !           639:     
        !           640:     if (sector_id==fmt_id) {
        !           641:         sector_timer=0;
        !           642:         return true;
        !           643:     } else {
        !           644:         Log_Printf(LOG_MO_CMD_LEVEL, "[OSP] Sector ID mismatch (Sector ID=%06X, Looking for %06X)",
        !           645:                    sector_id,fmt_id);
        !           646:         if (mo.ctrlr_csr2&MOCSR2_SECT_TIMER) {
        !           647:             sector_timer++;
        !           648:             if (sector_timer>SECTOR_TIMEOUT_COUNT) {
        !           649:                 Log_Printf(LOG_WARN, "[OSP] Sector timeout!");
        !           650:                 sector_timer=0;
        !           651:                 fmt_mode=FMT_MODE_IDLE;
        !           652:                 osp_interrupt(MOINT_TIMEOUT);
        !           653:             }
        !           654:         }
        !           655:         return false;
        !           656:     }
        !           657: }
        !           658: 
        !           659: void fmt_io(Uint32 sector_id) {
        !           660: 
        !           661:     switch (fmt_mode) {
        !           662:         case FMT_MODE_IDLE:
        !           663:             return;
        !           664:         case FMT_MODE_READ_ID:
        !           665:             mo.tracknumh = (sector_id>>16)&0xFF;
        !           666:             mo.tracknuml = (sector_id>>8)&0xFF;
        !           667:             mo.sector_num = sector_id&0x0F;
        !           668:             osp_interrupt(MOINT_OPER_COMPL);
        !           669:             return;
        !           670:         case FMT_MODE_READ:
        !           671:             if (modrv[dnum].head!=READ_HEAD) {
        !           672:                 abort();
        !           673:             }
        !           674:             if (fmt_match_id(sector_id)) {
        !           675:                 /* First read sector from disk to ECC buffer */
        !           676:                 mo_read_sector(sector_id);
        !           677:                 /* Then decode data and write to memory using DMA */
        !           678:                 ecc_read();
        !           679:                 fmt_sector_done();
        !           680:             }
        !           681:             break;
        !           682:         case FMT_MODE_WRITE:
        !           683:             if (modrv[dnum].head!=WRITE_HEAD) {
        !           684:                 abort();
        !           685:             }
        !           686:             /* WARNING: first sector must be mismatch to pre-fill the ECC buffer for writing */
        !           687:             if (fmt_match_id(sector_id) && write_timing) {
        !           688:                 /* Write sector from ECC buffer to disk */
        !           689:                 mo_write_sector(sector_id);
        !           690:                 fmt_sector_done();
        !           691:             } else {
        !           692:                 write_timing = true;
        !           693:             }
        !           694:             /* (Re)fill ECC buffer from memory using DMA and encode data */
        !           695:             ecc_write();
        !           696:             break;
        !           697:         case FMT_MODE_ERASE:
        !           698:             if (modrv[dnum].head!=ERASE_HEAD) {
        !           699:                 abort();
        !           700:             }
        !           701:             if (fmt_match_id(sector_id)) {
        !           702:                 mo_erase_sector(sector_id);
        !           703:                 fmt_sector_done();
        !           704:             }
        !           705:             break;
        !           706:         case FMT_MODE_VERIFY:
        !           707:             if (modrv[dnum].head!=VERIFY_HEAD) {
        !           708:                 abort();
        !           709:             }
        !           710:             if (fmt_match_id(sector_id)) {
        !           711:                 /* First read sector from disk to ECC buffer */
        !           712:                 mo_verify_sector(sector_id);
        !           713:                 /* Then verify data */
        !           714:                 ecc_verify();
        !           715:                 fmt_sector_done();
        !           716:             }
        !           717:             break;
        !           718:             
        !           719:         default:
        !           720:             abort();
        !           721:             break;
        !           722:     }
        !           723: }
        !           724: 
        !           725: 
        !           726: /* Drive selection (formatter command 2) */
        !           727: void osp_select(int drive) {
        !           728:     Log_Printf(LOG_MO_CMD_LEVEL, "[OSP] Selecting drive %i",drive);
        !           729:     dnum=drive;
        !           730:     if (!modrv[dnum].connected) {
        !           731:         Log_Printf(LOG_MO_CMD_LEVEL, "[OSP] Selection failed! Drive %i not connected.",drive);
        !           732:     }
        !           733: }
        !           734: 
        !           735: /* Check for MO drive signals (used for interrupt status register) */
        !           736: void osp_poll_mo_signals(void) {
        !           737:     if (modrv[dnum].complete) {
        !           738:         mo.intstatus |= MOINT_CMD_COMPL;
        !           739:     } else {
        !           740:         mo.intstatus &= ~MOINT_CMD_COMPL;
        !           741:     }
        !           742:     if (modrv[dnum].attn) {
        !           743:         mo.intstatus |= MOINT_ATTN;
        !           744:     } else {
        !           745:         mo.intstatus &= ~MOINT_ATTN;
        !           746:     }
        !           747: }
        !           748: 
        !           749: void ecc_toggle_buffer(void) {
        !           750:     if (eccin==0) {
        !           751:         eccout=0;
        !           752:         eccin=1;
        !           753:     } else {
        !           754:         eccout=1;
        !           755:         eccin=0;
        !           756:     }
        !           757:     Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] ECC switching buffer (in: %i, out: %i)",eccin,eccout);
        !           758: }
        !           759: 
        !           760: void ecc_clear_buffer(void) {
        !           761:     ecc_buffer[eccin].size=ecc_buffer[eccout].size=0;
        !           762:     ecc_buffer[eccin].limit=ecc_buffer[eccout].limit=MO_SECTORSIZE_DATA;
        !           763: }
        !           764: 
        !           765: void mo_formatter_cmd2(void) {
        !           766:     if (mo.ctrlr_csr2&MOCSR2_BUF_TOGGLE) {
        !           767:         Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] Toggle ECC buffer.");
        !           768:         ecc_toggle_buffer();
        !           769:     }
        !           770:     if (mo.ctrlr_csr2&MOCSR2_ECC_CMP) {
        !           771:         Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] ECC compare.");
        !           772:     }
        !           773:     if (mo.ctrlr_csr2&MOCSR2_CLR_BUFP) {
        !           774:         Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] Clear ECC buffer.");
        !           775:         ecc_clear_buffer();
        !           776:     }
        !           777:     if (mo.ctrlr_csr2&MOCSR2_ECC_BLOCKS) {
        !           778:         Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] ECC blocks.");
        !           779:     }
        !           780:     if (mo.ctrlr_csr2&MOCSR2_ECC_MODE) {
        !           781:         Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] ECC decoding mode.");
        !           782:     } else {
        !           783:         Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] ECC encoding mode.");
        !           784:     }
        !           785:     if (mo.ctrlr_csr2&MOCSR2_SECT_TIMER) {
        !           786:         Log_Printf(LOG_MO_CMD_LEVEL, "[OSP] Sector timer enabled.");
        !           787:     } else {
        !           788:         Log_Printf(LOG_MO_CMD_LEVEL, "[OSP] Sector timer disabled.");
        !           789:     }
        !           790:     if (mo.ctrlr_csr2&MOCSR2_ECC_DIS) {
        !           791:         Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] Disable ECC passthrough.");
        !           792:     }
        !           793:     
        !           794:     osp_select(mo.ctrlr_csr2&MOCSR2_DRIVE_SEL);
        !           795: }
        !           796: 
        !           797: 
        !           798: /* ECC emulation:
        !           799:  *
        !           800:  * read     mem <----- buf <-de-- disk
        !           801:  * write    mem -----> buf --en-> disk
        !           802:  * verify              buf <-de-- disk
        !           803:  *
        !           804:  * ecc_dis
        !           805:  * read     mem <-en-- buf
        !           806:  * write    mem -----> buf
        !           807:  *
        !           808:  * ecc_dis|ecc_mode
        !           809:  * read     mem <----- buf
        !           810:  * write    mem --de-> buf
        !           811:  *
        !           812:  */
        !           813: 
        !           814: #define ECC_DELAY SECTOR_IO_DELAY/5 /* must be a fraction of sector delay */
        !           815: 
        !           816: bool ecc_repeat=false; /* This is for ECC blocks */
        !           817: 
        !           818: int eccin=0;
        !           819: int eccout=1;
        !           820: 
        !           821: void ecc_decode(void) {
        !           822:     if (mo.ctrlr_csr2&MOCSR2_ECC_DIS && !(mo.ctrlr_csr2&MOCSR2_ECC_MODE)) {
        !           823:         Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] ECC decoding disabled.");
        !           824:         if (mo.ctrlr_csr2&MOCSR2_ECC_BLOCKS && ecc_repeat==true) {
        !           825:             ecc_toggle_buffer();
        !           826:         }
        !           827:     } else if (ecc_buffer[eccin].size==MO_SECTORSIZE_DISK) {
        !           828:         Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] ECC decoding buffer.");
        !           829:         ecc_buffer[eccin].limit=ecc_buffer[eccin].size=MO_SECTORSIZE_DATA;
        !           830:         
        !           831:         int num_errors = rs_decode(ecc_buffer[eccin].data);
        !           832:         
        !           833:         if (num_errors<0) {
        !           834:             Log_Printf(LOG_WARN, "[OSP] ECC: Sector has uncorrectable errors!");
        !           835:             mo.err_stat = ERRSTAT_ECC;
        !           836:             osp_interrupt(MOINT_DATA_ERR);
        !           837:             /* CHECK: Stop ECC and formatter? */
        !           838:         } else {
        !           839:             Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] ECC: Number of corrected errors: %i\n",num_errors);
        !           840:             
        !           841:             if (mo.ecc_cnt==0) {
        !           842:                 mo.ecc_cnt=num_errors;
        !           843:             }
        !           844:         }
        !           845:         
        !           846:         ecc_toggle_buffer();
        !           847:     } else {
        !           848:         Log_Printf(LOG_WARN, "[OSP] ECC buffer is not ready (%i bytes)!",ecc_buffer[eccin].size);
        !           849:         abort();
        !           850:     }
        !           851: }
        !           852: void ecc_encode(void) {
        !           853:     if (mo.ctrlr_csr2&MOCSR2_ECC_DIS && mo.ctrlr_csr2&MOCSR2_ECC_MODE) {
        !           854:         Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] ECC encoding disabled.");
        !           855:         if (mo.ctrlr_csr2&MOCSR2_ECC_BLOCKS && ecc_repeat==false) {
        !           856:             ecc_toggle_buffer();
        !           857:         }
        !           858:     } else if (ecc_buffer[eccin].limit==MO_SECTORSIZE_DATA) {
        !           859:         Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] ECC encoding buffer.");
        !           860:         ecc_buffer[eccin].limit=ecc_buffer[eccin].size=MO_SECTORSIZE_DISK;
        !           861: 
        !           862:         rs_encode(ecc_buffer[eccin].data);
        !           863: 
        !           864:         ecc_toggle_buffer();
        !           865:     } else {
        !           866:         Log_Printf(LOG_WARN, "[OSP] ECC buffer is not ready (%i bytes)!",ecc_buffer[eccin].size);
        !           867:         abort();
        !           868:     }
        !           869: }
        !           870: 
        !           871: void ecc_write(void) {
        !           872:     if (ecc_state!=ECC_STATE_DONE) {
        !           873:         Log_Printf(LOG_MO_ECC_LEVEL,"[OSP] Warning: ECC not accepting command (busy %i)", ecc_state);
        !           874:         return;
        !           875:     }
        !           876:     ecc_mode=ECC_MODE_WRITE;
        !           877:     ecc_state=ECC_STATE_FILLING;
        !           878:     if (mo.ctrlr_csr2&MOCSR2_ECC_BLOCKS) {
        !           879:         ecc_repeat=true;
        !           880:     }
        !           881:     ecc_buffer[eccin].size=0; /* FIXME: find a better place for this */
        !           882:     ecc_buffer[eccin].limit=MO_SECTORSIZE_DATA; /* and this */
        !           883:     CycInt_AddRelativeInterruptUsCycles(ECC_DELAY, 80, INTERRUPT_ECC_IO);
        !           884: }
        !           885: void ecc_read(void) {
        !           886:     if (ecc_state!=ECC_STATE_DONE) {
        !           887:         Log_Printf(LOG_WARN,"[OSP] Warning: ECC not accepting command (busy %i)", ecc_state);
        !           888:         return;
        !           889:     }
        !           890:     ecc_mode=ECC_MODE_READ;
        !           891:     ecc_state=ECC_STATE_ECCING;
        !           892:     if (mo.ctrlr_csr2&MOCSR2_ECC_BLOCKS) {
        !           893:         ecc_repeat=true;
        !           894:     }
        !           895:     CycInt_AddRelativeInterruptUsCycles(ECC_DELAY, 80, INTERRUPT_ECC_IO);
        !           896: }
        !           897: void ecc_verify(void) {
        !           898:     if (ecc_state!=ECC_STATE_DONE) {
        !           899:         Log_Printf(LOG_WARN,"[OSP] Warning: ECC not accepting command (busy %i)", ecc_state);
        !           900:         return;
        !           901:     }
        !           902:     ecc_mode=ECC_MODE_VERIFY;
        !           903:     ecc_state=ECC_STATE_ECCING;
        !           904:     CycInt_AddRelativeInterruptUsCycles(ECC_DELAY, 80, INTERRUPT_ECC_IO);
        !           905: }
        !           906: void ecc_sequence_done(void) {
        !           907:     if (ecc_repeat==true) {
        !           908:         ecc_repeat=false;
        !           909:         if (ecc_mode==ECC_MODE_WRITE) {
        !           910:             ecc_buffer[eccin].size=0; /* FIXME: find a better place for this */
        !           911:             ecc_state=ECC_STATE_FILLING;
        !           912:         } else {
        !           913:             ecc_state=ECC_STATE_ECCING;
        !           914:         }
        !           915:         CycInt_AddRelativeInterruptUsCycles(ECC_DELAY, 80, INTERRUPT_ECC_IO);
        !           916:         return;
        !           917:     }
        !           918: 
        !           919:     ecc_state=ECC_STATE_DONE;
        !           920:     if (mo.ctrlr_csr2&MOCSR2_ECC_DIS) {
        !           921:         osp_interrupt(MOINT_ECC_DONE);
        !           922:     }
        !           923: }
        !           924: Uint32 old_size;
        !           925: void ECC_IO_Handler(void) {
        !           926:     CycInt_AcknowledgeInterrupt();
        !           927:     
        !           928:     switch (ecc_state) {
        !           929:         case ECC_STATE_FILLING:
        !           930:             if (ecc_buffer[eccin].size<ecc_buffer[eccin].limit) {
        !           931:                 if (mo.ctrlr_csr2&MOCSR2_ECC_MODE) {
        !           932:                     ecc_buffer[eccin].limit=MO_SECTORSIZE_DISK;
        !           933:                 } else {
        !           934:                     ecc_buffer[eccin].limit=MO_SECTORSIZE_DATA;
        !           935:                 }
        !           936:                 old_size=ecc_buffer[eccin].size;
        !           937:                 dma_mo_read_memory();
        !           938:                 
        !           939:                 if (ecc_buffer[eccin].size==old_size) {
        !           940:                     Log_Printf(LOG_WARN,"[OSP] No more data! ECC starve! (%i byte)", old_size);
        !           941:                     mo.err_stat = ERRSTAT_STARVE;
        !           942:                     osp_interrupt(MOINT_DATA_ERR);
        !           943:                 }
        !           944:             }
        !           945:             if (ecc_buffer[eccin].size==ecc_buffer[eccin].limit) {
        !           946:                 ecc_state=ECC_STATE_ECCING;
        !           947:             }
        !           948:             break;
        !           949:         case ECC_STATE_ECCING:
        !           950:             if (ecc_mode==ECC_MODE_WRITE) {
        !           951:                 if (mo.ctrlr_csr2&MOCSR2_ECC_DIS) {
        !           952:                     ecc_decode();
        !           953:                     ecc_sequence_done();
        !           954:                     return;
        !           955:                 } else { /* Go to disk write */
        !           956:                     ecc_encode();
        !           957:                     ecc_state=ECC_STATE_WAITING;
        !           958:                     if (sector_counter==1) {
        !           959:                         osp_interrupt(MOINT_ECC_DONE);
        !           960:                     }
        !           961:                     break;
        !           962:                 }
        !           963:             } else { /* mode is read or verify */
        !           964:                 if (mo.ctrlr_csr2&MOCSR2_ECC_DIS) {
        !           965:                     if (mo.ctrlr_csr2&MOCSR2_ECC_BLOCKS) {
        !           966:                         ecc_buffer[eccin].limit=ecc_buffer[eccin].size=MO_SECTORSIZE_DATA;
        !           967:                     }
        !           968:                     ecc_encode();
        !           969:                 } else { /* From disk read */
        !           970:                     if (ecc_buffer[eccin].size!=MO_SECTORSIZE_DISK) {
        !           971:                         Log_Printf(LOG_WARN, "[OSP] ECC waiting for disk read!");
        !           972:                         break; /* Loop and wait for disk read */
        !           973:                     }
        !           974:                     ecc_decode();
        !           975:                     if (sector_counter==0) {
        !           976:                         osp_interrupt(MOINT_ECC_DONE);
        !           977:                     }
        !           978:                     if (ecc_mode==ECC_MODE_VERIFY) {
        !           979:                         ecc_clear_buffer();
        !           980:                         ecc_sequence_done();
        !           981:                         return;
        !           982:                     }
        !           983:                 }
        !           984:                 ecc_state=ECC_STATE_DRAINING;
        !           985:                 break;
        !           986:             }
        !           987:         case ECC_STATE_DRAINING:
        !           988:             old_size=ecc_buffer[eccout].size;
        !           989:             dma_mo_write_memory();
        !           990:             if (ecc_buffer[eccout].size==old_size) {
        !           991:                 Log_Printf(LOG_WARN,"[OSP] DMA not ready! Stopping.");
        !           992:                 ecc_sequence_done();
        !           993:                 return;
        !           994:             }
        !           995:             if (ecc_buffer[eccout].size==0) {
        !           996:                 dma_mo_write_memory(); /* Flush buffer */
        !           997:                 ecc_sequence_done();
        !           998:                 return;
        !           999:             }
        !          1000:             break;
        !          1001:         case ECC_STATE_WAITING:
        !          1002:             if (ecc_buffer[eccout].size==0) {
        !          1003:                 ecc_sequence_done();
        !          1004:                 if (sector_counter>0) {
        !          1005:                     ecc_write();
        !          1006:                 }
        !          1007:                 return;
        !          1008:             }
        !          1009:             Log_Printf(LOG_MO_ECC_LEVEL, "[OSP] ECC waiting for disk write!");
        !          1010:             break;
        !          1011: 
        !          1012:         default:
        !          1013:             Log_Printf(LOG_WARN, "[OSP] Warning: ECC was reset while busy!");
        !          1014:             return;
        !          1015:     }
        !          1016:     
        !          1017:     CycInt_AddRelativeInterruptUsCycles(ECC_DELAY, 80, INTERRUPT_ECC_IO);
        !          1018: }
        !          1019: 
        !          1020: 
        !          1021: /* ------------------------ MAGNETO-OPTICAL DISK DRIVE ------------------------ */
        !          1022: 
        !          1023: /* I/O functions */
        !          1024: 
        !          1025: void mo_read_sector(Uint32 sector_id) {
        !          1026:     Uint32 sector_num = get_logical_sector(sector_id);
        !          1027:     
        !          1028:     Log_Printf(LOG_MO_IO_LEVEL, "MO disk %i: Read sector at offset %i (%i sectors remaining)",
        !          1029:                dnum, sector_num, sector_counter-1);
        !          1030:     
        !          1031:     File_Read(ecc_buffer[eccin].data, MO_SECTORSIZE_DISK, sector_num*MO_SECTORSIZE_DISK, modrv[dnum].dsk);
        !          1032:     
        !          1033:     ecc_buffer[eccin].limit = ecc_buffer[eccin].size = MO_SECTORSIZE_DISK;
        !          1034: }
        !          1035: 
        !          1036: void mo_write_sector(Uint32 sector_id) {
        !          1037:     Uint32 sector_num = get_logical_sector(sector_id);
        !          1038:     
        !          1039:     Log_Printf(LOG_MO_IO_LEVEL, "MO disk %i: Write sector at offset %i (%i sectors remaining)",
        !          1040:                dnum, sector_num, sector_counter-1);
        !          1041:     
        !          1042:     if (ecc_buffer[eccout].limit==MO_SECTORSIZE_DISK) {
        !          1043:         File_Write(ecc_buffer[eccout].data, MO_SECTORSIZE_DISK, sector_num*MO_SECTORSIZE_DISK, modrv[dnum].dsk);
        !          1044: 
        !          1045:         ecc_buffer[eccout].size = 0;
        !          1046:         ecc_buffer[eccout].limit = MO_SECTORSIZE_DATA;
        !          1047:     } else {
        !          1048:         Log_Printf(LOG_WARN, "MO disk %i: Incomplete write (in: size=%i limit=%i, out: size=%i limit=%i)!", dnum,
        !          1049:                    ecc_buffer[eccin].size, ecc_buffer[eccin].limit, ecc_buffer[eccout].size, ecc_buffer[eccout].limit);
        !          1050:         abort();
        !          1051:     }
        !          1052: }
        !          1053: 
        !          1054: void mo_erase_sector(Uint32 sector_id) {
        !          1055:     Uint32 sector_num = get_logical_sector(sector_id);
        !          1056:     
        !          1057:     Log_Printf(LOG_MO_IO_LEVEL, "MO disk %i: Erase sector at offset %i (%i sectors remaining)",
        !          1058:                dnum, sector_num, sector_counter-1);
        !          1059:     
        !          1060:     Uint8 erase_buf[MO_SECTORSIZE_DISK];
        !          1061:     memset(erase_buf, 0xFF, MO_SECTORSIZE_DISK);
        !          1062:     
        !          1063:     File_Write(erase_buf, MO_SECTORSIZE_DISK, sector_num*MO_SECTORSIZE_DISK, modrv[dnum].dsk);
        !          1064: }
        !          1065: 
        !          1066: void mo_verify_sector(Uint32 sector_id) {
        !          1067:     Uint32 sector_num = get_logical_sector(sector_id);
        !          1068:     
        !          1069:     Log_Printf(LOG_MO_IO_LEVEL, "MO disk %i: Verify sector at offset %i (%i sectors remaining)",
        !          1070:                dnum, sector_num, sector_counter-1);
        !          1071:     
        !          1072:     File_Read(ecc_buffer[eccin].data, MO_SECTORSIZE_DISK, sector_num*MO_SECTORSIZE_DISK, modrv[dnum].dsk);
        !          1073:     
        !          1074:     ecc_buffer[eccin].limit = ecc_buffer[eccin].size = MO_SECTORSIZE_DISK;
        !          1075: }
        !          1076: 
        !          1077: 
        !          1078: /* Drive commands */
        !          1079: 
        !          1080: #define DRV_SEK     0x0000 /* seek (last 12 bits are track position) */
        !          1081: #define DRV_HOS     0xA000 /* high order seek (last 4 bits are high order (<<12) track position) */
        !          1082: #define DRV_REC     0x1000 /* recalibrate */
        !          1083: #define DRV_RDS     0x2000 /* return drive status */
        !          1084: #define DRV_RCA     0x2200 /* return current track address */
        !          1085: #define DRV_RES     0x2800 /* return extended status */
        !          1086: #define DRV_RHS     0x2A00 /* return hardware status */
        !          1087: #define DRV_RGC     0x3000 /* return general config */
        !          1088: #define DRV_RVI     0x3F00 /* return drive version information */
        !          1089: #define DRV_SRH     0x4100 /* select read head */
        !          1090: #define DRV_SVH     0x4200 /* select verify head */
        !          1091: #define DRV_SWH     0x4300 /* select write head */
        !          1092: #define DRV_SEH     0x4400 /* select erase head */
        !          1093: #define DRV_SFH     0x4500 /* select RF head */
        !          1094: #define DRV_RID     0x5000 /* reset attn and status */
        !          1095: #define DRV_RJ      0x5100 /* relative jump (see below) */
        !          1096: #define DRV_SPM     0x5200 /* stop motor */
        !          1097: #define DRV_STM     0x5300 /* start motor */
        !          1098: #define DRV_LC      0x5400 /* lock cartridge */
        !          1099: #define DRV_ULC     0x5500 /* unlock cartridge */
        !          1100: #define DRV_EC      0x5600 /* eject */
        !          1101: #define DRV_SOO     0x5900 /* spiral operation on */
        !          1102: #define DRV_SOF     0x5A00 /* spiral operation off */
        !          1103: #define DRV_RSD     0x8000 /* request self-diagnostic */
        !          1104: #define DRV_SD      0xB000 /* send data (last 12 bits used) */
        !          1105: 
        !          1106: /* Relative jump:
        !          1107:  * bits 0 to 3: offset (signed -8 (0x8) to +7 (0x7)
        !          1108:  * bits 4 to 6: head select
        !          1109:  */
        !          1110: 
        !          1111: /* Head select for relative jump */
        !          1112: #define RJ_READ     0x10
        !          1113: #define RJ_VERIFY   0x20
        !          1114: #define RJ_WRITE    0x30
        !          1115: #define RJ_ERASE    0x40
        !          1116: 
        !          1117: /* Drive status information */
        !          1118: 
        !          1119: /* Disk status (returned for DRV_RDS) */
        !          1120: #define DS_INSERT   0x0004 /* load completed */
        !          1121: #define DS_RESET    0x0008 /* power on reset */
        !          1122: #define DS_SEEK     0x0010 /* address fault */
        !          1123: #define DS_CMD      0x0020 /* invalid or unimplemented command */
        !          1124: #define DS_INTFC    0x0040 /* interface fault */
        !          1125: #define DS_I_PARITY 0x0080 /* interface parity error */
        !          1126: #define DS_STOPPED  0x0200 /* not spinning */
        !          1127: #define DS_SIDE     0x0400 /* media upside down */
        !          1128: #define DS_SERVO    0x0800 /* servo not ready */
        !          1129: #define DS_POWER    0x1000 /* laser power alarm */
        !          1130: #define DS_WP       0x2000 /* disk write protected */
        !          1131: #define DS_EMPTY    0x4000 /* no disk inserted */
        !          1132: #define DS_BUSY     0x8000 /* execute busy */
        !          1133: 
        !          1134: /* Extended status (returned for DRV_RES) */
        !          1135: #define ES_RF       0x0002 /* RF detected */
        !          1136: #define ES_WR_INH   0x0008 /* write inhibit (high temperature) */
        !          1137: #define ES_WRITE    0x0010 /* write mode failed */
        !          1138: #define ES_COARSE   0x0020 /* coarse seek failed */
        !          1139: #define ES_TEST     0x0040 /* test write failed */
        !          1140: #define ES_SLEEP    0x0080 /* sleep/wakeup failed */
        !          1141: #define ES_LENS     0x0100 /* lens out of range */
        !          1142: #define ES_TRACKING 0x0200 /* tracking servo failed */
        !          1143: #define ES_PLL      0x0400 /* PLL failed */
        !          1144: #define ES_FOCUS    0x0800 /* focus failed */
        !          1145: #define ES_SPEED    0x1000 /* not at speed */
        !          1146: #define ES_STUCK    0x2000 /* disk cartridge stuck */
        !          1147: #define ES_ENCODER  0x4000 /* linear encoder failed */
        !          1148: #define ES_LOST     0x8000 /* tracing failure */
        !          1149: 
        !          1150: /* Hardware status (returned for DRV_RHS) */
        !          1151: #define HS_LASER    0x0040 /* laser power failed */
        !          1152: #define HS_INIT     0x0080 /* drive init failed */
        !          1153: #define HS_TEMP     0x0100 /* high drive temperature */
        !          1154: #define HS_CLAMP    0x0200 /* spindle clamp misaligned */
        !          1155: #define HS_STOP     0x0400 /* spindle stop timeout */
        !          1156: #define HS_TEMPSENS 0x0800 /* temperature sensor failed */
        !          1157: #define HS_LENSPOS  0x1000 /* lens position failure */
        !          1158: #define HS_SERVOCMD 0x2000 /* servo command failure */
        !          1159: #define HS_SERVOTO  0x4000 /* servo timeout failure */
        !          1160: #define HS_HEAD     0x8000 /* head select failure */
        !          1161: 
        !          1162: /* Version information (returned for DRV_RVI) */
        !          1163: #define VI_VERSION  0x0880
        !          1164: 
        !          1165: void mo_drive_cmd(void) {
        !          1166: 
        !          1167:     if (!modrv[dnum].connected) {
        !          1168:         Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Drive %i not connected.\n", dnum);
        !          1169:         return;
        !          1170:     }
        !          1171: 
        !          1172:     Uint16 command = (mo.csrh<<8) | mo.csrl;
        !          1173:     
        !          1174:     /* Command in progress */
        !          1175:     modrv[dnum].complete=false;
        !          1176:     
        !          1177:     if ((command&0xF000)==DRV_SEK) {
        !          1178:         Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Seek (%04X)\n", command);
        !          1179:         mo_seek(command);
        !          1180:     } else if ((command&0xF000)==DRV_SD) {
        !          1181:         Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Send Data (%04X)\n", command);
        !          1182:         abort();
        !          1183:     } else if ((command&0xFF00)==DRV_RJ) {
        !          1184:         Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Relative Jump (%04X)\n", command);
        !          1185:         mo_jump_head(command);
        !          1186:     } else if ((command&0xFFF0)==DRV_HOS) {
        !          1187:         Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: High Order Seek (%04X)\n", command);
        !          1188:         mo_high_order_seek(command);
        !          1189:     } else {
        !          1190:     
        !          1191:         switch (command&0xFFFF) {
        !          1192:             case DRV_REC:
        !          1193:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Recalibrate (%04X)\n", command);
        !          1194:                 mo_recalibrate();
        !          1195:                 break;
        !          1196:             case DRV_RDS:
        !          1197:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Return Drive Status (%04X)\n", command);
        !          1198:                 mo_return_drive_status();
        !          1199:                 break;
        !          1200:             case DRV_RCA:
        !          1201:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Return Current Track Address (%04X)\n", command);
        !          1202:                 mo_return_track_addr();
        !          1203:                 break;
        !          1204:             case DRV_RES:
        !          1205:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Return Extended Status (%04X)\n", command);
        !          1206:                 mo_return_extended_status();
        !          1207:                 break;
        !          1208:             case DRV_RHS:
        !          1209:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Return Hardware Status (%04X)\n", command);
        !          1210:                 mo_return_hardware_status();
        !          1211:                 break;
        !          1212:             case DRV_RGC:
        !          1213:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Return General Config (%04X)\n", command);
        !          1214:                 abort();
        !          1215:                 break;
        !          1216:             case DRV_RVI:
        !          1217:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Return Version Information (%04X)\n", command);
        !          1218:                 mo_return_version();
        !          1219:                 break;
        !          1220:             case DRV_SRH:
        !          1221:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Select Read Head (%04X)\n", command);
        !          1222:                 mo_select_head(READ_HEAD);
        !          1223:                 break;
        !          1224:             case DRV_SVH:
        !          1225:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Select Verify Head (%04X)\n", command);
        !          1226:                 mo_select_head(VERIFY_HEAD);
        !          1227:                 break;
        !          1228:             case DRV_SWH:
        !          1229:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Select Write Head (%04X)\n", command);
        !          1230:                 mo_select_head(WRITE_HEAD);
        !          1231:                 break;
        !          1232:             case DRV_SEH:
        !          1233:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Select Erase Head (%04X)\n", command);
        !          1234:                 mo_select_head(ERASE_HEAD);
        !          1235:                 break;
        !          1236:             case DRV_SFH:
        !          1237:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Select RF Head (%04X)\n", command);
        !          1238:                 mo_select_head(RF_HEAD);
        !          1239:                 break;
        !          1240:             case DRV_RID:
        !          1241:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Reset Attn and Status (%04X)\n", command);
        !          1242:                 mo_reset_attn_status();
        !          1243:                 break;
        !          1244:             case DRV_SPM:
        !          1245:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Stop Spindle Motor (%04X)\n", command);
        !          1246:                 mo_stop_spinning();
        !          1247:                 break;
        !          1248:             case DRV_STM:
        !          1249:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Start Spindle Motor (%04X)\n", command);
        !          1250:                 mo_start_spinning();
        !          1251:                 break;
        !          1252:             case DRV_LC:
        !          1253:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Lock Cartridge (%04X)\n", command);
        !          1254:                 abort();
        !          1255:                 break;
        !          1256:             case DRV_ULC:
        !          1257:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Unlock Cartridge (%04X)\n", command);
        !          1258:                 abort();
        !          1259:                 break;
        !          1260:             case DRV_EC:
        !          1261:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Eject (%04X)\n", command);
        !          1262:                 mo_eject_disk(-1);
        !          1263:                 break;
        !          1264:             case DRV_SOO:
        !          1265:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Start Spiraling (%04X)\n", command);
        !          1266:                 mo_start_spiraling();
        !          1267:                 break;
        !          1268:             case DRV_SOF:
        !          1269:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Stop Spiraling (%04X)\n", command);
        !          1270:                 mo_stop_spiraling();
        !          1271:                 break;
        !          1272:             case DRV_RSD:
        !          1273:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Request Self-Diagnostic (%04X)\n", command);
        !          1274:                 mo_self_diagnostic();
        !          1275:                 break;
        !          1276:                 
        !          1277:             default:
        !          1278:                 Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Unimplemented command! (%04X)\n", command);
        !          1279:                 mo_unimplemented_cmd();
        !          1280:                 break;
        !          1281:         }
        !          1282:     }
        !          1283:     Statusbar_BlinkLed(DEVICE_LED_OD);
        !          1284: }
        !          1285: 
        !          1286: 
        !          1287: bool mo_drive_empty(void) {
        !          1288:     if (!modrv[dnum].inserted) {
        !          1289:         Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Drive %i: No disk inserted.\n", dnum);
        !          1290:         modrv[dnum].dstat |= DS_EMPTY;
        !          1291:         mo_set_signals(true, true, CMD_DELAY);
        !          1292:         return true;
        !          1293:     } else {
        !          1294:         return false;
        !          1295:     }
        !          1296: }
        !          1297: 
        !          1298: bool mo_protected(void) {
        !          1299:     if (modrv[dnum].protected) {
        !          1300:         if (modrv[dnum].head==ERASE_HEAD || modrv[dnum].head==WRITE_HEAD) {
        !          1301:             Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Drive command: Drive %i: Disk is write protected!\n", dnum);
        !          1302:             modrv[dnum].dstat|=DS_WP;
        !          1303:             modrv[dnum].head=NO_HEAD;
        !          1304:             mo_set_signals(true, true, CMD_DELAY);
        !          1305:             return true;
        !          1306:         }
        !          1307:     }
        !          1308:     return false;
        !          1309: }
        !          1310: 
        !          1311: void mo_seek(Uint16 command) {
        !          1312: #if SEEK_TIMING
        !          1313:     int seek_time=modrv[dnum].head_pos;
        !          1314: #endif
        !          1315:     if (mo_drive_empty()) {
        !          1316:         return;
        !          1317:     }
        !          1318:     modrv[dnum].seeking = true;
        !          1319:     modrv[dnum].head_pos = (modrv[dnum].ho_head_pos&0xF000) | (command&0x0FFF);
        !          1320: #if SEEK_TIMING
        !          1321:     if ((Uint32)seek_time>modrv[dnum].head_pos) {
        !          1322:         seek_time=seek_time-modrv[dnum].head_pos;
        !          1323:     } else {
        !          1324:         seek_time=modrv[dnum].head_pos-seek_time;
        !          1325:     }
        !          1326:     if (seek_time>95000) {
        !          1327:         seek_time=95000;
        !          1328:     }
        !          1329:     seek_time+=5000;
        !          1330: 
        !          1331:     mo_set_signals(true, false, seek_time);
        !          1332: #else
        !          1333:     mo_set_signals(true, false, CMD_DELAY);
        !          1334: #endif
        !          1335: }
        !          1336: 
        !          1337: void mo_high_order_seek(Uint16 command) {
        !          1338:     if (mo_drive_empty()) {
        !          1339:         return;
        !          1340:     }
        !          1341:     if ((command&0xF)>4) {
        !          1342:         modrv[dnum].dstat|=DS_SEEK;
        !          1343:         mo_set_signals(true, true, CMD_DELAY);
        !          1344:     } else {
        !          1345:         modrv[dnum].ho_head_pos = (command&0xF)<<12;
        !          1346:         mo_set_signals(true, false, CMD_DELAY);
        !          1347:     }
        !          1348: }
        !          1349: 
        !          1350: void mo_jump_head(Uint16 command) {
        !          1351:     if (mo_drive_empty()) {
        !          1352:         return;
        !          1353:     }
        !          1354:     modrv[dnum].seeking = true;
        !          1355: 
        !          1356:     int offset = command&0x7;
        !          1357:     if (command&0x8) {
        !          1358:         offset = 8 - offset;
        !          1359:         modrv[dnum].head_pos-=offset;
        !          1360:     } else {
        !          1361:         modrv[dnum].head_pos+=offset;
        !          1362:     }
        !          1363:     modrv[dnum].sec_offset=0;
        !          1364:     
        !          1365:     switch (command&0xF0) {
        !          1366:         case RJ_READ:
        !          1367:             modrv[dnum].head=READ_HEAD;
        !          1368:             break;
        !          1369:         case RJ_VERIFY:
        !          1370:             modrv[dnum].head=VERIFY_HEAD;
        !          1371:             break;
        !          1372:         case RJ_WRITE:
        !          1373:             modrv[dnum].head=WRITE_HEAD;
        !          1374:             break;
        !          1375:         case RJ_ERASE:
        !          1376:             modrv[dnum].head=ERASE_HEAD;
        !          1377:             break;
        !          1378:             
        !          1379:         default:
        !          1380:             modrv[dnum].head=NO_HEAD;
        !          1381:             break;
        !          1382:     }
        !          1383:     Log_Printf(LOG_MO_CMD_LEVEL,"[MO] Relative Jump: %i sectors %s (%s head)\n", offset*16,
        !          1384:                (command&0x8)?"back":"forward",
        !          1385:                (command&0xF0)==RJ_READ?"read":
        !          1386:                (command&0xF0)==RJ_VERIFY?"verify":
        !          1387:                (command&0xF0)==RJ_WRITE?"write":
        !          1388:                (command&0xF0)==RJ_ERASE?"erase":"unknown");
        !          1389:     
        !          1390:     if (mo_protected()) {
        !          1391:         return;
        !          1392:     }
        !          1393: #if SEEK_TIMING
        !          1394:     mo_set_signals(true, false, 1600);
        !          1395: #else
        !          1396:     mo_set_signals(true, false, CMD_DELAY);
        !          1397: #endif
        !          1398: }
        !          1399: 
        !          1400: void mo_recalibrate(void) {
        !          1401:     if (mo_drive_empty()) {
        !          1402:         return;
        !          1403:     }
        !          1404:     modrv[dnum].head_pos = 0;
        !          1405:     modrv[dnum].sec_offset = 0;
        !          1406:     modrv[dnum].spiraling = false;
        !          1407:     
        !          1408:     mo_set_signals(true, false, CMD_DELAY);
        !          1409: }
        !          1410: 
        !          1411: void mo_return_drive_status(void) {
        !          1412:     if (!modrv[dnum].spinning) {
        !          1413:         modrv[dnum].dstat|=DS_STOPPED;
        !          1414:     }
        !          1415:     if (!modrv[dnum].inserted) {
        !          1416:         modrv[dnum].dstat|=DS_EMPTY;
        !          1417:     }
        !          1418:     modrv[dnum].status = modrv[dnum].dstat;
        !          1419:     mo_set_signals(true, false, CMD_DELAY);
        !          1420: }
        !          1421: 
        !          1422: void mo_return_track_addr(void) {
        !          1423:     modrv[dnum].status = modrv[dnum].head_pos;
        !          1424:     mo_set_signals(true, false, CMD_DELAY);
        !          1425: }
        !          1426: 
        !          1427: void mo_return_extended_status(void) {
        !          1428:     modrv[dnum].status = modrv[dnum].estat;
        !          1429:     mo_set_signals(true, false, CMD_DELAY);
        !          1430: }
        !          1431: 
        !          1432: void mo_return_hardware_status(void) {
        !          1433:     modrv[dnum].status = modrv[dnum].hstat;
        !          1434:     mo_set_signals(true, false, CMD_DELAY);
        !          1435: }
        !          1436: 
        !          1437: void mo_return_version(void) {
        !          1438:     modrv[dnum].status = VI_VERSION;
        !          1439:     mo_set_signals(true, false, CMD_DELAY);
        !          1440: }
        !          1441: 
        !          1442: void mo_select_head(int head) {
        !          1443:     if (mo_drive_empty()) {
        !          1444:         return;
        !          1445:     }
        !          1446:     modrv[dnum].head = head;
        !          1447:     if (mo_protected()) {
        !          1448:         return;
        !          1449:     }
        !          1450:     mo_set_signals(true, false, CMD_DELAY);
        !          1451: }
        !          1452: 
        !          1453: void mo_reset_attn_status(void) {
        !          1454:     modrv[dnum].dstat=modrv[dnum].estat=modrv[dnum].hstat=0;
        !          1455:     modrv[dnum].attn=false;
        !          1456:     mo_set_signals(true, false, CMD_DELAY);
        !          1457: }
        !          1458: 
        !          1459: void mo_stop_spinning(void) {
        !          1460:     if (mo_drive_empty()) {
        !          1461:         return;
        !          1462:     }
        !          1463:     Statusbar_AddMessage("Stop magneto-optical disk spin.", 0);
        !          1464:     modrv[dnum].spinning=false;
        !          1465:     modrv[dnum].spiraling=false;
        !          1466:     mo_set_signals(true, false, CMD_DELAY);
        !          1467: }
        !          1468: 
        !          1469: void mo_start_spinning(void) {
        !          1470:     if (mo_drive_empty()) {
        !          1471:         return;
        !          1472:     }
        !          1473:     Statusbar_AddMessage("Spin-up magneto-optical disk.", 0);
        !          1474:     modrv[dnum].dstat &= ~DS_STOPPED;
        !          1475:     modrv[dnum].spinning=true;
        !          1476:     mo_set_signals(true, false, 1600000);
        !          1477: }
        !          1478: 
        !          1479: void mo_eject_disk(int drv) {
        !          1480:     if (drv<0) { /* Called from emulator, else called from GUI */
        !          1481:         drv=dnum;
        !          1482:         if (mo_drive_empty())
        !          1483:             return;
        !          1484:         
        !          1485:         Statusbar_AddMessage("Ejecting magneto-optical disk.", 0);
        !          1486:         mo_set_signals(true, false, CMD_DELAY);
        !          1487:     }
        !          1488: 
        !          1489:     Log_Printf(LOG_WARN, "MO disk %i: Eject",drv);
        !          1490:     
        !          1491:     File_Close(modrv[drv].dsk);
        !          1492:     modrv[drv].dsk=NULL;
        !          1493:     modrv[drv].inserted=false;
        !          1494:     modrv[drv].spinning=false;
        !          1495:     modrv[drv].spiraling=false;
        !          1496:     
        !          1497:     ConfigureParams.MO.drive[drv].bDiskInserted=false;
        !          1498:     ConfigureParams.MO.drive[drv].szImageName[0]='\0';
        !          1499: }
        !          1500: 
        !          1501: void mo_insert_disk(int drv) {
        !          1502:     Log_Printf(LOG_WARN, "MO disk %i: Insert",drv);
        !          1503:     
        !          1504:     if (ConfigureParams.MO.drive[drv].bWriteProtected) {
        !          1505:         modrv[drv].dsk = File_Open(ConfigureParams.MO.drive[drv].szImageName, "rb");
        !          1506:         if (modrv[drv].dsk == NULL) {
        !          1507:             Log_Printf(LOG_WARN, "MO Disk%i: Cannot open image file %s\n",
        !          1508:                        drv, ConfigureParams.MO.drive[drv].szImageName);
        !          1509:             modrv[drv].inserted=false;
        !          1510:             modrv[drv].protected=false;
        !          1511:             Statusbar_AddMessage("Cannot insert magneto-optical disk.", 0);
        !          1512:             return;
        !          1513:         } else {
        !          1514:             modrv[drv].inserted=true;
        !          1515:             modrv[drv].protected=true;
        !          1516:         }
        !          1517:     } else {
        !          1518:         modrv[drv].dsk = File_Open(ConfigureParams.MO.drive[drv].szImageName, "rb+");
        !          1519:         if (modrv[drv].dsk == NULL) {
        !          1520:             modrv[drv].dsk = File_Open(ConfigureParams.MO.drive[drv].szImageName, "rb");
        !          1521:             if (modrv[drv].dsk == NULL) {
        !          1522:                 Log_Printf(LOG_WARN, "MO Disk%i: Cannot open image file %s\n",
        !          1523:                            drv, ConfigureParams.MO.drive[drv].szImageName);
        !          1524:                 modrv[drv].inserted=false;
        !          1525:                 modrv[drv].protected=false;
        !          1526:                 Statusbar_AddMessage("Cannot insert magneto-optical disk.", 0);
        !          1527:                 return;
        !          1528:             } else {
        !          1529:                 modrv[drv].inserted=true;
        !          1530:                 modrv[drv].protected=true;
        !          1531:             }
        !          1532:         } else {
        !          1533:             modrv[drv].inserted=true;
        !          1534:             modrv[drv].protected=false;
        !          1535:         }
        !          1536:     }
        !          1537: 
        !          1538:     Statusbar_AddMessage("Inserting magneto-optical disk.", 0);
        !          1539:     modrv[drv].dstat&=~DS_EMPTY;
        !          1540:     modrv[drv].dstat|=DS_INSERT;
        !          1541:     modrv[drv].spinning=false;
        !          1542:     modrv[drv].spiraling=false;
        !          1543:     mo_push_signals(true, false, drv);
        !          1544: }
        !          1545: 
        !          1546: void mo_start_spiraling(void) {
        !          1547:     if (mo_drive_empty()) {
        !          1548:         return;
        !          1549:     }
        !          1550:     if (!modrv[dnum].spinning) {
        !          1551:         modrv[dnum].dstat|=DS_STOPPED;
        !          1552:         mo_set_signals(true, true, CMD_DELAY);
        !          1553:         return;
        !          1554:     }
        !          1555: 
        !          1556:     if (!modrv[0].spiraling && !modrv[1].spiraling) { /* periodic disk operation already active? */
        !          1557:         CycInt_AddRelativeInterruptUsCycles(SECTOR_IO_DELAY, 400, INTERRUPT_MO_IO);
        !          1558:     }
        !          1559:     modrv[dnum].spiraling=true;
        !          1560: 
        !          1561:     mo_set_signals(true, false, CMD_DELAY);
        !          1562: }
        !          1563: 
        !          1564: void mo_stop_spiraling(void) {
        !          1565:     if (mo_drive_empty()) {
        !          1566:         return;
        !          1567:     }
        !          1568:     modrv[dnum].spiraling=false;
        !          1569:     mo_set_signals(true, false, CMD_DELAY);
        !          1570: }
        !          1571: 
        !          1572: void mo_spiraling_operation(void) {
        !          1573:     if (!modrv[0].spiraling && !modrv[1].spiraling) { /* this stops periodic disk operation */
        !          1574:         return; /* nothing to do */
        !          1575:     }
        !          1576:     
        !          1577:     int i;
        !          1578:     for (i=0; i<MO_MAX_DRIVES; i++) {
        !          1579:         if (modrv[i].spiraling && !modrv[i].seeking) {
        !          1580:             
        !          1581:             /* If the drive is selected, connect to formatter */
        !          1582:             if (i==dnum) {
        !          1583:                 fmt_io((modrv[i].head_pos<<8)|modrv[i].sec_offset);
        !          1584:             }
        !          1585:             
        !          1586:             /* Continue spiraling */
        !          1587:             modrv[i].sec_offset++;
        !          1588:             modrv[i].head_pos+=modrv[i].sec_offset/MO_SEC_PER_TRACK;
        !          1589:             modrv[i].sec_offset%=MO_SEC_PER_TRACK;
        !          1590:         }
        !          1591:     }
        !          1592:     CycInt_AddRelativeInterruptUsCycles(SECTOR_IO_DELAY, 400, INTERRUPT_MO_IO);
        !          1593: }
        !          1594: 
        !          1595: void mo_self_diagnostic(void) {
        !          1596:     mo_set_signals(true, false, CMD_DELAY);
        !          1597: }
        !          1598: 
        !          1599: void MO_IO_Handler(void) {
        !          1600:     CycInt_AcknowledgeInterrupt();
        !          1601: 
        !          1602:     mo_spiraling_operation();
        !          1603: }
        !          1604: 
        !          1605: void mo_unimplemented_cmd(void) {
        !          1606:     modrv[dnum].dstat|=DS_CMD;
        !          1607:     mo_set_signals(true, true, CMD_DELAY);
        !          1608: }
        !          1609: 
        !          1610: void mo_reset(void) {
        !          1611:     int i;
        !          1612:     for (i=0; i<MO_MAX_DRIVES; i++) {
        !          1613:         if (modrv[i].connected) {
        !          1614:             modrv[i].head=NO_HEAD;
        !          1615:             modrv[i].head_pos=modrv[i].ho_head_pos=0;
        !          1616:             modrv[i].sec_offset=0;
        !          1617:             
        !          1618:             modrv[i].dstat=DS_RESET;
        !          1619:             modrv[i].estat=modrv[i].hstat=0;
        !          1620:             if (modrv[i].inserted) { /* CHECK: really spin up on reset? */
        !          1621:                 modrv[i].spinning=true;
        !          1622:             } else {
        !          1623:                 modrv[i].spinning=false;
        !          1624:             }
        !          1625:             modrv[i].spiraling=false;
        !          1626:             
        !          1627:             if (!modrv[i].inserted) {
        !          1628:                 modrv[i].dstat|=DS_EMPTY;
        !          1629:             } else if (!modrv[i].spinning) {
        !          1630:                 modrv[i].dstat|=DS_STOPPED;
        !          1631:             }
        !          1632:             modrv[i].attn=false;
        !          1633:             modrv[i].complete=true;
        !          1634:         }
        !          1635:     }
        !          1636: }
        !          1637: 
        !          1638: 
        !          1639: /* MO drive signals */
        !          1640: 
        !          1641: void mo_push_signals(bool complete, bool attn, int drive) {
        !          1642:     if (drive<0) {
        !          1643:         Log_Printf(LOG_WARN, "[MO] Error: No drive specified for delayed interrupt.");
        !          1644:         abort();
        !          1645:     }
        !          1646:     bool interrupt = false;
        !          1647:     
        !          1648:     if (!modrv[drive].complete) {
        !          1649:         modrv[drive].complete=complete;
        !          1650:         if (modrv[drive].complete) {
        !          1651:             if (drive==dnum && mo.intmask&MOINT_CMD_COMPL) {
        !          1652:                 interrupt=true;
        !          1653:             }
        !          1654:         }
        !          1655:     }
        !          1656:     if (!modrv[drive].attn) {
        !          1657:         modrv[drive].attn=attn;
        !          1658:         if (modrv[drive].attn) {
        !          1659:             if (drive==dnum && mo.intmask&MOINT_ATTN) {
        !          1660:                 interrupt=true;
        !          1661:             }
        !          1662:         }
        !          1663:     }
        !          1664:     
        !          1665:     modrv[drive].seeking=false;
        !          1666: 
        !          1667:     if (interrupt) {
        !          1668:         set_interrupt(INT_DISK, SET_INT);
        !          1669:     }
        !          1670: }
        !          1671: 
        !          1672: bool delayed_compl;
        !          1673: bool delayed_attn;
        !          1674: int delayed_drive=-1;
        !          1675: 
        !          1676: void mo_set_signals(bool complete, bool attn, int delay) {
        !          1677:     if (delay>0) {
        !          1678:         if (delayed_drive>=0) {
        !          1679:             if (delayed_drive!=dnum) {
        !          1680:                 Log_Printf(LOG_WARN, "[MO] Warning: Delayed interrupt from other drive (%i) in progress!",delayed_drive);
        !          1681:                 mo_push_signals(delayed_compl, delayed_attn, delayed_drive);
        !          1682:                 CycInt_RemovePendingInterrupt(INTERRUPT_MO);
        !          1683:             } else {
        !          1684:                 Log_Printf(LOG_WARN, "[MO] Warning: Delayed interrupt already in progress!");
        !          1685:             }
        !          1686:         }
        !          1687:         delayed_drive=dnum;
        !          1688:         delayed_compl=complete;
        !          1689:         delayed_attn=attn;
        !          1690:         CycInt_AddRelativeInterruptUsCycles(delay, CMD_DELAY, INTERRUPT_MO);
        !          1691:     } else {
        !          1692:         mo_push_signals(complete, attn, dnum);
        !          1693:     }
        !          1694: }
        !          1695: 
        !          1696: void MO_InterruptHandler(void) {
        !          1697:     CycInt_AcknowledgeInterrupt();
        !          1698:     
        !          1699:     mo_push_signals(delayed_compl,delayed_attn,delayed_drive);
        !          1700:     
        !          1701:     delayed_compl=false;
        !          1702:     delayed_attn=false;
        !          1703:     delayed_drive=-1;
        !          1704: }
        !          1705: 
        !          1706: 
        !          1707: /* Initialize/Uninitialize MO disks */
        !          1708: void MO_Init(void) {
        !          1709:     Log_Printf(LOG_WARN, "Loading magneto-optical disks:");
        !          1710:     int i;
        !          1711:     
        !          1712:     for (i=0; i<MO_MAX_DRIVES; i++) {
        !          1713:         modrv[i].spinning=false;
        !          1714:         modrv[i].spiraling=false;
        !          1715:         /* Check if files exist. */
        !          1716:         if (ConfigureParams.MO.drive[i].bDriveConnected) {
        !          1717:             modrv[i].connected=true;
        !          1718:             modrv[i].complete=true;
        !          1719:             modrv[i].attn=false;
        !          1720:             modrv[i].dstat=modrv[i].estat=modrv[i].hstat=0;
        !          1721:             if (ConfigureParams.MO.drive[i].bDiskInserted &&
        !          1722:                 File_Exists(ConfigureParams.MO.drive[i].szImageName)) {
        !          1723:                 if (ConfigureParams.MO.drive[i].bWriteProtected) {
        !          1724:                     modrv[i].dsk = File_Open(ConfigureParams.MO.drive[i].szImageName, "rb");
        !          1725:                     if (modrv[i].dsk == NULL) {
        !          1726:                         Log_Printf(LOG_WARN, "MO Disk%i: Cannot open image file %s\n",
        !          1727:                                    i, ConfigureParams.MO.drive[i].szImageName);
        !          1728:                         modrv[i].inserted=false;
        !          1729:                         modrv[i].protected=false;
        !          1730:                     } else {
        !          1731:                         modrv[i].inserted=true;
        !          1732:                         modrv[i].protected=true;
        !          1733:                     }
        !          1734:                 } else {
        !          1735:                     modrv[i].dsk = File_Open(ConfigureParams.MO.drive[i].szImageName, "rb+");
        !          1736:                     if (modrv[i].dsk == NULL) {
        !          1737:                         modrv[i].dsk = File_Open(ConfigureParams.MO.drive[i].szImageName, "rb");
        !          1738:                         if (modrv[i].dsk == NULL) {
        !          1739:                             Log_Printf(LOG_WARN, "MO Disk%i: Cannot open image file %s\n",
        !          1740:                                        i, ConfigureParams.MO.drive[i].szImageName);
        !          1741:                             modrv[i].inserted=false;
        !          1742:                             modrv[i].protected=false;
        !          1743:                         } else {
        !          1744:                             modrv[i].inserted=true;
        !          1745:                             modrv[i].protected=true;
        !          1746:                         }
        !          1747:                     } else {
        !          1748:                         modrv[i].inserted=true;
        !          1749:                         modrv[i].protected=false;
        !          1750:                     }
        !          1751:                 }
        !          1752:             } else {
        !          1753:                 modrv[i].dsk = NULL;
        !          1754:                 modrv[i].inserted=false;
        !          1755:             }
        !          1756:         } else {
        !          1757:             modrv[i].connected=false;
        !          1758:             modrv[i].complete=false;
        !          1759:             modrv[i].attn=false;
        !          1760:         }
        !          1761: 
        !          1762:         Log_Printf(LOG_WARN, "MO Disk%i: %s\n",i,ConfigureParams.MO.drive[i].szImageName);
        !          1763:     }
        !          1764:     
        !          1765:     /* Initialize formatter variables */
        !          1766:     ecc_state=ECC_STATE_DONE;
        !          1767: }
        !          1768: 
        !          1769: void MO_Uninit(void) {
        !          1770:     if (modrv[0].dsk)
        !          1771:         File_Close(modrv[0].dsk);
        !          1772:     if (modrv[1].dsk) {
        !          1773:         File_Close(modrv[1].dsk);
        !          1774:     }
        !          1775:     modrv[0].dsk = modrv[1].dsk = NULL;
        !          1776:     modrv[0].inserted = modrv[1].inserted = false;
        !          1777: }
        !          1778: 
        !          1779: void MO_Insert(int drive) {
        !          1780:     Log_Printf(LOG_WARN, "Loading magneto-optical disk:");
        !          1781:     Log_Printf(LOG_WARN, "MO Disk%i: %s\n",drive,ConfigureParams.MO.drive[drive].szImageName);
        !          1782: 
        !          1783:     mo_insert_disk(drive);
        !          1784: }
        !          1785: 
        !          1786: void MO_Eject(int drive) {
        !          1787:     Log_Printf(LOG_WARN, "Unloading magneto-optical disk %i",drive);
        !          1788: 
        !          1789:     mo_eject_disk(drive);
        !          1790: }
        !          1791: 
        !          1792: void MO_Reset(void) {
        !          1793:     MO_Uninit();
        !          1794:     MO_Init();
        !          1795: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.