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

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

unix.superglobalmegacorp.com

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