This document is designed to assist Digiboard customers in converting custom device drivers from the PC/Xe with a 64K memory window to the newer 4 port/8 port boards with a memory window interface. The procedures for configuring the board and loading the BIOS and FEP are discussed along with how to access channel buffers. Not all cases where a driver has to access memory are discussed, but it is hoped that these examples will illustrate how to interface to the Digiboard with a memory window interface. In the discussion to follow the following notation will be used: devb[i] The ith byte of board I/O space memb[i] The 8-bit byte of board memory at board relative address i as viewed by the host. memw[i] The 16-bit word of board memory at board relative address i as viewed by the host. CONFIGURATION: The 4 port/8 port boards can be distinquished from the Pc/Xe by reading devb[0]. The port will decode as follows: bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 REV1 REV0 0 0 INTP RESP MEMEN 0 where REV1,0 is 00 for the PC/Xe and 01 for the 4 port/8 port boards INTP - interrupt 80186 processor RESP - reset 80186 processor MEMEN - 64K memory enable The original PC/Xe configured the board address and interrupt vectors using switches located on the board. On the new boards these variables are configured by the driver in I/O device registers at startup. The following pseudo-code can be used to configure the board. Set bitfields in configuration word t to select the interrupt number. disabled => 0000h, 3 => 0001h, 5 => 0002h, 7 => 0003h, 10 => 0004h, 11 => 0005h, 12 => 0006h, 15 => 0007h. Set bitfields FFE0h of t according to the bitfields FFE000h of the base address of the board. Set bit 0010h in t if 8K windowing option is desired. devb[2] = t and 00FFh # configure board registers devb[3] = t/100h For example, a board with interrupt bector 5 and base address 00D8000h the following settings would occur: t = 0D80h + 0002h + 0010 # base address and interrupt and windowing devb[2] = 92h = t and 00FFh devb[3] = 0Dh = t/100h MEMORY WINDOWING: In fixed memory window products memory was enabled by placing 0x02 in the io port base register. devb[0] = 0x02 ; Memory window products enable a single page of memory by selecting the memory page in the lower bits of the second io port and a 1 in the upper most bit to enable memory. devb[1] = 0x80 ; enables window zero devb[1] = 0x87 ; enables window seven LOADING THE BIOS: The BIOS is loaded into the upper region of memory(specifically at 0xff800). For the old board this is done with the following code: devb[0] = 0x06 ; /* hold reset and enable memory */ mem_ptr = board_addr+(0xff800-membase); for(i = 0; i < n; i++) { *mem_ptr++ = bios[i]; } board_addr + 0xC00 = 0; /* clear bios start flag */ devb[0] = 0x02 ; /* release reset to start BIOS */ Where: board_addr is the base address of the board as seen by the host mem_ptr is a pointer to board memory membase is the base address of dual ported memory. (0xf0000 for 64K) bios[] is a char array containing the BIOS code. For the new board this is done with the following code: devb[0] = 0x04 ; /* board held in reset */ devb[1] = 0x87 ; /* Select page 7 and enable memory */ mem_ptr = board_addr + 0x1800 ; for(i = 0; i != n; i++) { *mem_ptr++ = bios[i]; } devb[1] = 0x80 ; /* point to window 0 for global data */ board_addr + 0xC00 = 0 ; /* clear bios start flag */ devb[0] = 0x00 ; /* release reset to start bios */ LOADING THE FEP: The FEP code is loaded at location 0x2000 as seen by the PC/Xe For the old board this is done as follows: mem_ptr = board_addr + 0x2000 ; for(i = 0; i < n; i++) { *mem_ptr++ = fep[i]; } board_addr + 0xc40 = 02 ; /* Command */ board_addr + 0xc42 = 0x200 ; /* From */ board_addr + 0xc44 = 0 ; board_addr + 0xc46 = 0x200 ; /* To */ board_addr + 0xc48 = 0; board_addr + 0xc4a = 0x2000 ; /* FEP/OS size */ devb[0] = 0x0a ; /* Interrupt to BIOS */ devb[0] = 0x02 ; Where: fep[] is a char array containing the FEP code. For the new board the procedure is as follows: devb[1] = 0x81 ; /* point at window 1 */ mem_ptr = board_addr ; for( i = 0; i != n; i++) { *fep_ptr++ = fep[i] ; } devb[1] = 0x80 ; /* point at window 0 */ board_addr + 0xc40 = 02 ; /* Command */ board_addr + 0xc42 = 0x200 ; /* From */ board_addr + 0xc44 = 0 ; board_addr + 0xc46 = 0x200 ; /* To */ board_addr + 0xc48 = 0; board_addr + 0xc4a = 0x2000 ; /* FEP/OS size */ devb[0] = 0x08 ; /* Interrupt to BIOS */ devb[0] = 0x00 ; ACCESSING CHANNEL BUFFERS: When using the old board, the method to access a channel receive buffer is as follows: /* get board address from channel structure receive segment */ chan_rcv = board_addr + (chan_ptr->rseg - base_seg) << 4 ; /* get character from head of buffer */ while (chan_ptr->rout != chan_ptr->rin) { rcv_char = chan_rcv + chan_ptr->rout ; chan_ptr->rout = (chan_ptr->rout + 1) & chan_ptr->rmax ; } Where: chan_rcv is the start of the channel receive buffer. chan_ptr is a pointer to the channel structure. base_seg is the memory segment for the base of dual ported memory as seen by the PC/Xe. The procedure for the new board is as follows: /* enable window 0 */ devb[1] = 0x80 ; rin_sav = chan_ptr->rin ; rout_sav = chan_ptr->rout ; rmax_sav = chan_ptr->rmax ; reg_sav = chan_ptr->rseg ; while(rin_sav != rout_sav) { /* locate offset to data from window base */ win_offset=((rseg_sav <<4) & 0x1fff) + rout_sav ; /* select correct window */ devb[1] = 0x80 | (rseg_sav >>9)) ; /* read in character */ rcv_char = board_addr + win_offset ; /* update local copy of buffer pointer */ rout_sav = (rout_sav + 1) & rmax_sav ; } /* reselect base window */ devb[1] = 0x80 ; chan_ptr->rout = rout_sav ; /* update out pointer */ Where: rin_sav is a local copy of the receive input pointer rout_sav is a local copy of the receive output pointer rmax_sav is a local copy of the buffer size mask rseg_sav is a local copy of the buffer segment as seen by the PC/Xe win_offset is the offset into the current window in bytes