Annotation of previous/src/mo.c, revision 1.1.1.4

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:  
1.1.1.2   root        6:  Canon Magneto-Optical Disk Drive and NeXT Optical Storage Processor emulation.
                      7:   
1.1       root        8:  NeXT Optical Storage Processor uses Reed-Solomon algorithm for error correction.
1.1.1.2   root        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)
1.1       root       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"
1.1.1.2   root       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
1.1       root       37: 
                     38: #define IO_SEG_MASK    0x1FFFF
                     39: 
                     40: 
1.1.1.2   root       41: /* Registers */
                     42: 
1.1       root       43: struct {
1.1.1.2   root       44:     Uint8 tracknuml;
                     45:     Uint8 tracknumh;
                     46:     Uint8 sector_num;
1.1       root       47:     Uint8 sector_count;
                     48:     Uint8 intstatus;
                     49:     Uint8 intmask;
                     50:     Uint8 ctrlr_csr2;
                     51:     Uint8 ctrlr_csr1;
1.1.1.2   root       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;
1.1       root       62: 
1.1.1.2   root       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: 
1.1.1.3   root      173: static Uint32 get_logical_sector(Uint32 sector_id) {
1.1.1.2   root      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:     }
1.1       root      183: 
1.1.1.2   root      184:     return (tracknum*MO_SEC_PER_TRACK)+sectornum;
                    185: }
1.1       root      186: 
                    187: 
                    188: 
1.1.1.2   root      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 */
1.1.1.3   root      201: #define SECTOR_IO_DELAY 1250
                    202: #define CMD_DELAY       40
1.1.1.2   root      203: 
1.1.1.3   root      204: #define SEEK_TIMING 1
1.1.1.2   root      205: 
1.1.1.3   root      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);
1.1.1.2   root      252: 
1.1.1.3   root      253: static int sector_increment = 0;
1.1.1.2   root      254: 
1.1.1.3   root      255: static void osp_interrupt(Uint8 interrupt);
1.1.1.2   root      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: }
1.1       root      266: 
1.1.1.2   root      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: }
1.1       root      271: 
1.1.1.2   root      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;
1.1       root      303:     }
1.1.1.2   root      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());
1.1       root      305: }
                    306: 
1.1.1.2   root      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: }
1.1       root      312: 
1.1.1.2   root      313: void MO_IntStatus_Write(void) {
1.1       root      314:     Uint8 val = IoMem[IoAccessCurrentAddress & IO_SEG_MASK];
1.1.1.2   root      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());
1.1       root      356:     
1.1.1.2   root      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());
1.1       root      368:     
1.1.1.2   root      369:     mo_formatter_cmd();
                    370: }
1.1       root      371: 
1.1.1.2   root      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: 
1.1.1.3   root      489: #if 0
1.1.1.2   root      490: /* Register debugging */
1.1.1.3   root      491: static void print_regs(void) {
1.1.1.2   root      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: }
1.1.1.3   root      511: #endif
1.1.1.2   root      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();
1.1       root      680:             }
                    681:             break;
1.1.1.2   root      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();
1.1       root      696:             break;
1.1.1.2   root      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();
1.1       root      704:             }
                    705:             break;
1.1.1.2   root      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:             }
1.1       root      717:             break;
1.1.1.2   root      718:             
                    719:         default:
                    720:             abort();
1.1       root      721:             break;
1.1.1.2   root      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);
1.1       root      840:             
1.1.1.2   root      841:             if (mo.ecc_cnt==0) {
                    842:                 mo.ecc_cnt=num_errors;
1.1       root      843:             }
1.1.1.2   root      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 */
1.1.1.3   root      883:     CycInt_AddRelativeInterruptUsCycles(ECC_DELAY, 80, INTERRUPT_ECC_IO);
1.1.1.2   root      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:     }
1.1.1.3   root      895:     CycInt_AddRelativeInterruptUsCycles(ECC_DELAY, 80, INTERRUPT_ECC_IO);
1.1.1.2   root      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;
1.1.1.3   root      904:     CycInt_AddRelativeInterruptUsCycles(ECC_DELAY, 80, INTERRUPT_ECC_IO);
1.1.1.2   root      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:         }
1.1.1.3   root      915:         CycInt_AddRelativeInterruptUsCycles(ECC_DELAY, 80, INTERRUPT_ECC_IO);
1.1.1.2   root      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:                 }
1.1       root      944:             }
1.1.1.2   root      945:             if (ecc_buffer[eccin].size==ecc_buffer[eccin].limit) {
                    946:                 ecc_state=ECC_STATE_ECCING;
1.1       root      947:             }
1.1.1.2   root      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;
1.1       root      986:             }
1.1.1.2   root      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;
1.1       root      994:             }
1.1.1.2   root      995:             if (ecc_buffer[eccout].size==0) {
                    996:                 dma_mo_write_memory(); /* Flush buffer */
                    997:                 ecc_sequence_done();
                    998:                 return;
1.1       root      999:             }
                   1000:             break;
1.1.1.2   root     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!");
1.1       root     1010:             break;
1.1.1.2   root     1011: 
1.1       root     1012:         default:
1.1.1.2   root     1013:             Log_Printf(LOG_WARN, "[OSP] Warning: ECC was reset while busy!");
                   1014:             return;
                   1015:     }
                   1016:     
1.1.1.3   root     1017:     CycInt_AddRelativeInterruptUsCycles(ECC_DELAY, 80, INTERRUPT_ECC_IO);
1.1.1.2   root     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:     
1.1.1.3   root     1031:     File_Read(ecc_buffer[eccin].data, MO_SECTORSIZE_DISK, sector_num*MO_SECTORSIZE_DISK, modrv[dnum].dsk);
1.1.1.2   root     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) {
1.1.1.3   root     1043:         File_Write(ecc_buffer[eccout].data, MO_SECTORSIZE_DISK, sector_num*MO_SECTORSIZE_DISK, modrv[dnum].dsk);
1.1.1.2   root     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:     
1.1.1.3   root     1063:     File_Write(erase_buf, MO_SECTORSIZE_DISK, sector_num*MO_SECTORSIZE_DISK, modrv[dnum].dsk);
1.1.1.2   root     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:     
1.1.1.3   root     1072:     File_Read(ecc_buffer[eccin].data, MO_SECTORSIZE_DISK, sector_num*MO_SECTORSIZE_DISK, modrv[dnum].dsk);
1.1.1.2   root     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;
1.1       root     1170:     }
1.1.1.2   root     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;
1.1       root     1309: }
                   1310: 
1.1.1.2   root     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
1.1.1.4 ! root     1321:     if ((Uint32)seek_time>modrv[dnum].head_pos) {
1.1.1.2   root     1322:         seek_time=seek_time-modrv[dnum].head_pos;
                   1323:     } else {
                   1324:         seek_time=modrv[dnum].head_pos-seek_time;
                   1325:     }
1.1.1.3   root     1326:     if (seek_time>95000) {
                   1327:         seek_time=95000;
1.1.1.2   root     1328:     }
1.1.1.3   root     1329:     seek_time+=5000;
                   1330: 
                   1331:     mo_set_signals(true, false, seek_time);
1.1.1.2   root     1332: #else
                   1333:     mo_set_signals(true, false, CMD_DELAY);
                   1334: #endif
                   1335: }
1.1       root     1336: 
1.1.1.2   root     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;
1.1       root     1368:             break;
1.1.1.2   root     1369:         case RJ_VERIFY:
                   1370:             modrv[dnum].head=VERIFY_HEAD;
1.1       root     1371:             break;
1.1.1.2   root     1372:         case RJ_WRITE:
                   1373:             modrv[dnum].head=WRITE_HEAD;
                   1374:             break;
                   1375:         case RJ_ERASE:
                   1376:             modrv[dnum].head=ERASE_HEAD;
1.1       root     1377:             break;
                   1378:             
                   1379:         default:
1.1.1.2   root     1380:             modrv[dnum].head=NO_HEAD;
1.1       root     1381:             break;
                   1382:     }
1.1.1.2   root     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
1.1.1.3   root     1394:     mo_set_signals(true, false, 1600);
1.1.1.2   root     1395: #else
                   1396:     mo_set_signals(true, false, CMD_DELAY);
                   1397: #endif
1.1       root     1398: }
                   1399: 
1.1.1.2   root     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: }
1.1       root     1426: 
1.1.1.2   root     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;
1.1.1.3   root     1476:     mo_set_signals(true, false, 1600000);
1.1.1.2   root     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");
1.1.1.3   root     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:         }
1.1.1.2   root     1517:     } else {
                   1518:         modrv[drv].dsk = File_Open(ConfigureParams.MO.drive[drv].szImageName, "rb+");
1.1.1.3   root     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:         }
1.1.1.2   root     1536:     }
1.1.1.3   root     1537: 
1.1.1.2   root     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? */
1.1.1.3   root     1557:         CycInt_AddRelativeInterruptUsCycles(SECTOR_IO_DELAY, 400, INTERRUPT_MO_IO);
1.1.1.2   root     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:     
1.1       root     1577:     int i;
1.1.1.2   root     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:     }
1.1.1.3   root     1592:     CycInt_AddRelativeInterruptUsCycles(SECTOR_IO_DELAY, 400, INTERRUPT_MO_IO);
1.1.1.2   root     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);
1.1       root     1608: }
                   1609: 
1.1.1.2   root     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;
1.1.1.3   root     1690:         CycInt_AddRelativeInterruptUsCycles(delay, CMD_DELAY, INTERRUPT_MO);
1.1.1.2   root     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");
1.1.1.3   root     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:                     }
1.1.1.2   root     1734:                 } else {
                   1735:                     modrv[i].dsk = File_Open(ConfigureParams.MO.drive[i].szImageName, "rb+");
1.1.1.3   root     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:                     }
1.1.1.2   root     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.