#ifdef __TURBOC__ #include #include #include #include "pctimer.h" #define timer pctimer #include "qic02.h" /*****************************************************\ * Quarter-Inch-Cartridge no. 02 standart error list * \*****************************************************/ static char *err[] = { "udefined error", "Ok", "reset in progress", /* hardware status */ "end of recorded media", "bus parity error", "beginning of media", "marginal block detected", "no data detected", "illegal command", "timeout expired", /* software flag */ "file mark detected", "block not located", "unrecoverable data error", "end of medium", "write protected", "device fault", "cartridge not in place", "drive busy", /* software error codes */ "device not opened", "device already opened", }; char **qic02_errlist = err+1; int qic02_nerr = sizeof(err)/sizeof(*err) - 1; /****************************************************\ * Quarter-Inch-Cartridge no. 02 standart interface * \****************************************************/ extern unsigned long timer(void); extern unsigned long suspend_value(unsigned); extern void suspend(unsigned long); extern void interrupt cthandle(); extern long ptr2abs(void far *); static unsigned long wait03us, wait25ms; #define CT_OPENED 0x08 #define CT_CHECKED 0x10 #define CT_EVENT 0x20 #define CT_PROTECT 0x40 #define MASK_BOARD 0x7f #define SKIP_RESET 0x80 /* Calculate number of ticks by 300 ms units */ #define TIMEOUT(x) (int)((3*119318L*(x) + 65535L) / 65536L) #define MINTIME TIMEOUT(1) #define MAXTIME TIMEOUT(1000) #define WAITIME TIMEOUT(10) #define HALT_CPU /*__emit__(0xf4)*/ #define dim(x) (sizeof(x)/sizeof(*(x))) #define NTRY 3 #define Q2_READBIT 0x80 #define DMA_WRDEV 8 #define DMA_RDDEV 4 static struct _ct_define { char ct_name[8]; WORD valid_base, off_control, off_data, off_launch, off_clear; BYTE mask_reset, val_reset; BYTE mask_event, not_event; BYTE mask_ready, not_ready; BYTE mask_excep, not_excep; BYTE mask_dma, done_dma; BYTE mask_xfer, no_xfer; BYTE set_reset, set_request, set_online; BYTE ienable, denable, dma_valid, rundma[8]; } *ct_current = (struct _ct_define *)0; static struct _ct_define sc499 = { "archive", 0x3f8, 1, 0, 2, 3, 0xf0, 0x50, 0x80, 0x80, 0x40, 0x40, 0x20, 0x20, 0x10, 0x10, 0x08, 0x00, 0x80, 0x40, 0, 0x20, 0x10, 0x0A, { 0, 0, 0, 0, 0, 0, 0, 0 }, }; static struct _ct_define sc409a = { "everex", 0x3ff, 0, 1, UNUSED, UNUSED, 7, 5, 0, 0, 1, 1, 2, 2, 0, 0, 4, 4, 2, 4, 1, 0x40, 0, 0x0E, { 0, 8, 8, 8, 0, 0, 0, 0 }, }; static struct _ct_define wangtek = { "wangtek", 0x3ff, 0, 1, UNUSED, UNUSED, 7, 5, 0, 0, 1, 1, 2, 2, 0, 0, 4, 4, 2, 4, 1, 0, 0, 0x0E, { 0, 8, 8, 16, 0, 0, 0, 0 }, }; static struct _ct_define *ct_list[] = { &sc499, &sc409a, &wangtek }; struct _ct_pic { WORD base; BYTE ack, chain; void interrupt (*old)(); int number; BYTE save; } ct_pic; struct _ct_dma { WORD page, address, counter; WORD mask, mode, clear; BYTE read, write; } ct_dma; BYTE lastcmd = UNUSED; WORD ct_error; volatile struct _ct_psw { BYTE flags; BYTE input; } ct_psw; struct _ct_control { WORD control, data, launch, clear; BYTE mask_reset, val_reset; BYTE mask_event, not_event; BYTE mask_ready, not_ready; BYTE mask_excep, not_excep; BYTE mask_state, inv_state; BYTE mask_xfer, no_xfer; BYTE mask_dma, done_dma; BYTE do_reset, request, online; BYTE int_enable, done_enable, dma_enable; } ct_set; static WORD ct_error; static BYTE statbuf[6]; #define ctlport (ct_set.control) #define cmdport (ct_set.data) #define statport (ct_set.control) #define dataport (ct_set.data) #define DMAGO (ct_set.launch) #define DMACL (ct_set.clear) #define MASK_RESET ct_set.mask_reset #define VAL_RESET ct_set.val_reset #define MASK_READY ct_set.mask_ready #define NOT_READY ct_set.not_ready #define MASK_EXCEP ct_set.mask_excep #define NOT_EXCEP ct_set.not_excep #define MASK_STATE ct_set.mask_state #define INV_STATE ct_set.inv_state #define DIRC ct_set.mask_xfer #define XOFF ct_set.no_xfer #define ON ct_set.online #define REQ ct_set.request #define RESET ct_set.do_reset #define EI ct_set.int_enable #define ED ct_set.done_enable #define DMA ct_set.dma_enable #define INP ct_psw.input #define DMA_MAIN ct_dma.mask #define WAIT_READY(t)\ for ((t)=timer(); ((INP=inportb(statport))&MASK_READY)==NOT_READY;)\ { if (timer()-(t) > MINTIME) goto error; } #define WAIT_BUSY(t)\ for ((t)=timer(); ((INP=inportb(statport))&MASK_READY)!=NOT_READY;)\ { if (timer()-(t) > MINTIME) goto error; } static int ct_iobyte(int c, unsigned char *p, unsigned length, unsigned tout) { register i; register long t, t0; #if 0 /* read-only now */ if (!(c & Q2_READBIT)) goto error; #endif ct_psw.flags &= ~CT_CHECKED; outportb(cmdport, lastcmd=c); outportb(ctlport, REQ); WAIT_READY(t); outportb(ctlport, ON); /* Clear request */ WAIT_BUSY(t); t0 = timer(); i = c & Q2_READBIT ? XOFF : XOFF^DIRC; while (((INP=inportb(statport)) & DIRC) == i) { if (timer()-t0 > tout) goto error; } for (i=0; i MINTIME) { ct_error = Q2E_BUSY; goto end; } HALT_CPU; } for (n=NTRY; n; n--) { if (ct_iobyte(Q2_RDSTAT, statbuf, 6, TIMEOUT(10)) == 6) { ct_error = (statbuf[0] & Q2_ERRFLAG) ? (statbuf[0] & Q2_ERRMASK) << 8 : 0; if (statbuf[1] & Q2_ERRFLAG) ct_error |= statbuf[1] & Q2_ERRMASK; break; } } end: ct_psw.flags |= CT_CHECKED; return ct_error; } static int ct_wait(BYTE mask, BYTE val, unsigned long t0, unsigned tout) { while (((INP=inportb(statport)) & mask) == val) { if ((INP & MASK_EXCEP) != NOT_EXCEP) return ct_test(); if (timer()-t0 > tout) { ct_psw.flags |= CT_CHECKED; return ct_error = (INP&MASK_READY)==NOT_READY ? Q2E_BUSY : Q2E_DEAD; } HALT_CPU; } return 0; } static int ct_reset(void) { register long t; outportb(ctlport, ct_set.do_reset); suspend(wait25ms); outportb(DMACL != UNUSED ? DMACL : ctlport, ON); t = timer(); while ((((INP=inportb(statport)) & MASK_RESET) != VAL_RESET)) { if (timer()-t > MINTIME) goto error; HALT_CPU; } while (!(ct_test() & Q2E_POR)) { if (timer()-t > MAXTIME) goto error; HALT_CPU; } while (ct_test() & Q2E_POR) { if (timer()-t > MAXTIME) goto error; HALT_CPU; } while ((INP=inportb(statport) & MASK_STATE) == INV_STATE) { if (timer()-t > MAXTIME) goto error; HALT_CPU; } return 0; error: return -1; } int ct_errbit(register unsigned m) { register n = 0; if (m) { if (m & Q2E_DEAD) m &= Q2E_DEAD; else if (m & Q2E_HARD) m &= Q2E_HARD; for (n=16; n && !(m & 0x8000); --n, m<<=1); } return n; } static void ct_end(void) { if (!(ct_psw.flags & CT_OPENED)) { errno = EFAULT; } else { if (ct_pic.base != UNUSED) { setvect(ct_pic.number, ct_pic.old); if (ct_pic.save & (1 << (ct_pic.ack & 7))) outportb(ct_pic.base+1, ct_pic.save); } ct_psw.flags = 0; } } static void ct_pause(void) { register long t; for (t=timer(); timer()-t <= MINTIME;) HALT_CPU; } static int ct_begin(register struct init_data *d, int board_type) { register WORD i; static BYTE page_tab[] = {0x87,0x83,0x81,0x82,0x8f,0x8b,0x89,0x8A}; static BYTE addr_tab[] = { 0, 2, 4, 6, 0xC0,0xC4,0xC8,0xCC}; static BYTE cntr_tab[] = { 1, 3, 5, 7, 0xC2,0xC6,0xCA,0xCE}; register j; register long t; if ((i = board_type & MASK_BOARD) >= dim(ct_list)) { errno = ENODEV; return CTE_FAULT; } else { ct_current = ct_list[i]; } i = d->base_address; if (i & ~(ct_current->valid_base)) { errno = EINVDAT; return CTE_FAULT; } else { ct_set.control = i + ct_current->off_control; ct_set.data = i + ct_current->off_data; DMAGO = ct_current->off_launch == UNUSED ? UNUSED : i + ct_current->off_launch; DMACL = ct_current->off_clear == UNUSED ? UNUSED : i + ct_current->off_clear; ct_set.mask_reset = ct_current->mask_reset; ct_set.val_reset = ct_current->val_reset; ct_set.mask_event = ct_current->mask_event; ct_set.not_event = ct_current->not_event; ct_set.mask_ready = ct_current->mask_ready; ct_set.not_ready = ct_current->not_ready; ct_set.mask_excep = ct_current->mask_excep; ct_set.not_excep = ct_current->not_excep; ct_set.mask_state = ct_set.mask_ready | ct_set.mask_excep; ct_set.inv_state = ct_set.not_ready | ct_set.not_excep; ct_set.mask_xfer = ct_current->mask_xfer; ct_set.no_xfer = ct_current->no_xfer; ct_set.mask_dma = ct_current->mask_dma; ct_set.done_dma = ct_current->done_dma; ct_set.online = ct_current->set_online; ct_set.request = ct_current->set_request | ct_set.online; ct_set.do_reset = ct_current->set_reset; } i = d->dma_number; if (i == UNUSED) { ct_dma.page = ct_dma.address = ct_dma.counter = UNUSED; ct_dma.mask = ct_dma.mode = ct_dma.clear = UNUSED; ct_set.dma_enable = 0; } else if (i & ~7 || !((ct_current->dma_valid >> i) & 1)) { errno = EINVDAT; return CTE_FAULT; } else { ct_set.dma_enable = ct_current->rundma[i]; ct_dma.page = page_tab[i]; ct_dma.address = addr_tab[i]; ct_dma.counter = cntr_tab[i]; if (i & ~3) {/* slave device */ ct_dma.mask = 0xd4; ct_dma.mode = 0xd6; ct_dma.clear = 0xd8; } else {/* primary device */ ct_dma.mask = 0xA; ct_dma.mode = 0xB; ct_dma.clear = 0xC; } i &= 3; /* single mode, address increment, no auto-init */ ct_dma.write = 0x40 | DMA_WRDEV | i; ct_dma.read = 0x40 | DMA_RDDEV | i; } i = d->irq_number; if (i == UNUSED) { ct_pic.number = ct_pic.base = UNUSED; ct_set.int_enable = ct_set.done_enable = 0; } else if (i & ~15) { errno = EINVDAT; return CTE_FAULT; } else { ct_set.int_enable = ct_current->ienable; ct_set.done_enable = ct_current->denable ? ct_current->denable : ct_current->ienable; if (i & ~7) {/* slave controller */ ct_pic.number = i + 0x68; ct_pic.base = 0xA0; } else {/* primary controller */ ct_pic.number = i + 8; ct_pic.base = 0x20; } i &= 7; ct_pic.old = getvect(ct_pic.number); ct_pic.ack = 0x60 | i; /* specific EOI */ ct_pic.save = inportb(ct_pic.base + 1); ct_pic.chain = ct_pic.old && ~(ct_pic.save >> i) & 1; } wait03us = suspend_value(3); wait25ms = suspend_value(25000); if ((board_type & SKIP_RESET) == 0) { if (ct_reset()) return (errno = EFAULT, CTE_DEAD); } if (ct_pic.base != UNUSED) { setvect(ct_pic.number, cthandle); if ((i = 1 << (ct_pic.ack & 7)) & ct_pic.save) outportb(ct_pic.base+1, ~i & ct_pic.save); } ct_error = 0; ct_psw.flags |= CT_OPENED; for (t=timer(), j=0;;) { i = ct_test(); if (i & Q2E_WRP) ct_psw.flags |= CT_PROTECT; if (!(i & ~(Q2E_BOM|Q2E_FIL|Q2E_WRP))) return 0; if (timer()-t > MAXTIME) break; if (!(i & Q2E_POR) && ++j >= NTRY) break; ct_pause(); } ct_end(); errno = EFAULT; return ct_errbit(i); } static int ct_issue(int c) { register unsigned long tick; if (ct_wait(MASK_STATE,INV_STATE,timer(),MINTIME) & Q2E_STOP) goto end; ct_psw.flags &= ~CT_CHECKED; outportb(cmdport, lastcmd=c); outportb(ctlport, REQ); if (ct_wait(MASK_READY,NOT_READY,timer(),MINTIME) & Q2E_HARD) goto end; ct_psw.flags &= ~CT_EVENT; INP = (INP & ~ct_set.mask_event) | ct_set.not_event; outportb(ctlport, ON|EI); /* Clear request, interrupt enable */ WAIT_BUSY(tick); return 0; error: ct_error = Q2E_DEAD; ct_psw.flags |= CT_CHECKED; end: return ct_errbit(ct_error); } static int ct_complete(void) { register long t; for (t=timer();;) { if (((INP=inportb(statport)) & MASK_STATE)!=INV_STATE) break; if (timer()-t > MAXTIME) return (ct_error = Q2E_DEAD, CTE_DEAD); HALT_CPU; } return 0; } static int ct_iodma(int c, void *buf, unsigned length) { register unsigned k; register unsigned inp; register long t, t0; long address; address = ptr2abs(buf); if (DMA_MAIN & 0xf0) { /* Secondary DMA requires word alignment */ if (address & 1 || length & 1) return (errno = EINVMEM, CTE_FAULT); address >>= 1; length >>= 1; } length -= 1; /* Check for writing across DMA page */ if (((address+length) ^ address) & ~0xffffL) return (errno = EINVMEM, CTE_FAULT); t0 = timer(); if (lastcmd!=c) { if (DMACL != UNUSED) outportb(DMACL, 0); /********** Issue a command **********/ outportb(cmdport, lastcmd=c); outportb(ctlport, REQ); if (ct_wait(MASK_READY, NOT_READY, (c & Q2_READBIT ? timer() : t0), (c & Q2_READBIT ? MINTIME : MAXTIME)) & Q2E_HARD) goto error; outportb(ctlport, ON|ED|DMA); WAIT_BUSY(t); } /* Clear status */ ct_psw.flags &= ~(CT_EVENT|CT_CHECKED); INP = (INP & ~ct_set.mask_event) | ct_set.not_event; /********** Initialise DMA **********/ outportb(ct_dma.mask, 4|(ct_dma.read & 3)); outportb(ct_dma.mode, c & Q2_READBIT ? ct_dma.read : ct_dma.write); outportb(ct_dma.page, (WORD)(address >> 16)); if (DMA_MAIN & 0xf0) {/* secondary controller */ outport(ct_dma.address, (WORD)address); outport(ct_dma.counter, length); } else { disable(); outportb(ct_dma.clear, 0); outportb(ct_dma.address, (WORD)address); outportb(ct_dma.address, (WORD)address>>8); outportb(ct_dma.counter, length); outportb(ct_dma.counter, length>>8); enable(); } if (c & Q2_READBIT) { if (ct_wait(DIRC,XOFF,t0,MAXTIME) & Q2E_HARD) goto error; } if (DMAGO != UNUSED) outportb(DMAGO, 0); outportb(ct_dma.mask, (ct_dma.read & 3)); /* Wait for the end of operation */ for (;;) { INP = inp = inportb(statport); if ((INP & MASK_EXCEP) != NOT_EXCEP) { if (ct_test() & Q2E_HARD) goto error; break; } if (ct_set.mask_dma) { if ((INP & ct_set.mask_dma) == ct_set.done_dma) break; } else { if ((INP & MASK_READY) != NOT_READY) break; } if (timer()-t0 > MAXTIME) goto error; HALT_CPU; } /********** Get number of bytes transferred **********/ if (DMA_MAIN & 0xf0) {/* secondary controller */ k = inport(ct_dma.counter); k = (length - k) << 1; } else { disable(); outportb(ct_dma.clear, 0); k = inportb(ct_dma.counter); k |= inportb(ct_dma.counter) << 8; enable(); k = length - k; } if (!(c & Q2_READBIT) && (inp & MASK_READY) == NOT_READY && (INP & MASK_READY) == NOT_READY) { (void)ct_wait(MASK_READY,NOT_READY,t0,MAXTIME); } return k; /********** Error occured **********/ error: if (DMACL != UNUSED) outportb(DMACL, 0); return (errno = EFAULT, CTE_FAULT); } static int ct_inout(int c, void *buf, unsigned length) { if (ct_wait(MASK_READY,NOT_READY,timer(), (c & Q2_READBIT ? MAXTIME : WAITIME)) & Q2E_STOP) return -1; return DMA_MAIN == UNUSED ? ct_iobyte(c, buf, length, MAXTIME) : ct_iodma(c, buf, length); } int streamer(register int op, void *ptr, int arg) { register i; register long t; op |= 'z'^'Z'; if (op == '?') { for (op=dim(ct_list); --op >= 0;) { if (stricmp(ct_list[op]->ct_name, ptr) == 0) goto end; } goto end; } else if (op == 'o') {/* open device */ op = ct_psw.flags & CT_OPENED ? (errno=EFAULT, CTE_OPENED) : ct_begin((struct init_data *)ptr, arg); goto end; } if (!(ct_psw.flags & CT_OPENED)) return (errno=EFAULT, CTE_CLOSED); switch (op) { case 'c': /* end - close device */ ct_end(); op = 0; break; case 't': /* read status */ op = ct_errbit(ct_psw.flags&CT_CHECKED ? ct_error : ct_test()); if (ptr) { ((struct qic02_word_status *)ptr)->status = ct_error; ((struct qic02_word_status *)ptr)->error_cnt = (statbuf[2] << 8) | statbuf[3]; ((struct qic02_word_status *)ptr)->underrun_cnt = (statbuf[4] << 8) | statbuf[5]; } ct_psw.flags &= ~CT_CHECKED; break; case 'd': /* "drop online" - write FM and rewind */ if (ct_current == &wangtek) { if (ct_wait(MASK_STATE,INV_STATE,timer(),MINTIME) & Q2E_STOP) { op = ct_errbit(ct_error); break; } ct_psw.flags &= ~CT_CHECKED; lastcmd = 0; /* dummy */ outportb(ctlport, 0); /* drop online */ for (t=timer(); timer()-t <= MAXTIME;) { if (((INP=inportb(statport))&MASK_STATE)!=INV_STATE) break; HALT_CPU; } ct_test(); if (ct_error & Q2E_HARD) goto test_rewind; if (ct_error & Q2E_BOM) { op = 0; break; } } else if (!(ct_psw.flags & CT_PROTECT)) { /* write file mark */ if (ct_issue(Q2_WRMARK)!=0 || ct_complete()!=0) { ct_test(); op = -1; break; } } case 'n': /* rewind */ for (op=0, i=0;;) { if (ct_issue(Q2_REWIND) == 0) { for (t=timer(); timer()-t <= MAXTIME;) { if (((INP=inportb(statport))&MASK_STATE)!=INV_STATE) break; HALT_CPU; } ct_test(); if (ct_error & Q2E_HARD) break; if (ct_error & Q2E_BOM) goto end; } if (++i >= NTRY) break; if (ct_reset()) { ct_error = Q2E_DEAD; break; } } test_rewind: op = ct_error & ~Q2E_WRP; op = op ? ct_errbit(op) : CTE_FAULT; break; case 'i': /* issue a command */ arg &= 255; if (arg == Q2_RDDATA || arg == Q2_WRDATA || arg == Q2_RDSTAT) { errno = EINVAL; op = CTE_FAULT; } else { ct_psw.flags &= ~CT_CHECKED; if ((op = ct_issue(arg)) == 0) op = ct_complete(); } break; case 'f': /* forward space file(s) */ for (op=0; op