//*************************************************************************** // PPC 6 Code in C sanitized for LINUX // Original x86 ASM by Ron, Converted to C by Clive //*************************************************************************** #define inp(x) inb((x)) #define inpw(x) inw((x)) #define inpd(x) inl((x)) #define outp(x,y) outb((y),(x)) #define outpw(x,y) outw((y),(x)) #define outpd(x,y) outl((y),(x)) #define min(a,b) (((a) < (b)) ? (a) : (b)) #define max(a,b) (((a) > (b)) ? (a) : (b)) #ifndef NULL #define NULL ((void *)0) #endif typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long DWORD; typedef unsigned long ULONG; //*************************************************************************** #define port_stb 1 #define port_afd 2 #define cmd_stb port_afd #define port_init 4 #define data_stb port_init #define port_sel 8 #define port_int 16 #define port_dir 0x20 #define ECR_EPP 0x80 #define ECR_BI 0x20 //*************************************************************************** // 60772 Commands #define ACCESS_REG 0x00 #define ACCESS_PORT 0x40 #define ACCESS_READ 0x00 #define ACCESS_WRITE 0x20 // 60772 Command Prefix #define CMD_PREFIX_SET 0xe0 // Special command that modifies the next command's operation #define CMD_PREFIX_RESET 0xc0 // Resets current cmd modifier reg bits #define PREFIX_IO16 0x01 // perform 16-bit wide I/O #define PREFIX_FASTWR 0x04 // enable PPC mode fast-write #define PREFIX_BLK 0x08 // enable block transfer mode // 60772 Registers #define REG_STATUS 0x00 // status register #define STATUS_IRQA 0x01 // Peripheral IRQA line #define STATUS_EEPROM_DO 0x40 // Serial EEPROM data bit #define REG_VERSION 0x01 // PPC version register (read) #define REG_HWCFG 0x02 // Hardware Config register #define REG_RAMSIZE 0x03 // Size of RAM Buffer #define RAMSIZE_128K 0x02 #define REG_EEPROM 0x06 // EEPROM control register #define EEPROM_SK 0x01 // eeprom SK bit #define EEPROM_DI 0x02 // eeprom DI bit #define EEPROM_CS 0x04 // eeprom CS bit #define EEPROM_EN 0x08 // eeprom output enable #define REG_BLKSIZE 0x08 // Block transfer len (24 bit) //*************************************************************************** typedef struct ppc_storage { WORD lpt_addr; // LPT base address BYTE ppc_id; BYTE mode; // operating mode // 0 = PPC Uni SW // 1 = PPC Uni FW // 2 = PPC Bi SW // 3 = PPC Bi FW // 4 = EPP Byte // 5 = EPP Word // 6 = EPP Dword BYTE ppc_flags; BYTE org_data; // original LPT data port contents BYTE org_ctrl; // original LPT control port contents BYTE cur_ctrl; // current control port contents } PPC; //*************************************************************************** // ppc_flags #define fifo_wait 0x10 //*************************************************************************** // DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES #define PPCMODE_UNI_SW 0 #define PPCMODE_UNI_FW 1 #define PPCMODE_BI_SW 2 #define PPCMODE_BI_FW 3 #define PPCMODE_EPP_BYTE 4 #define PPCMODE_EPP_WORD 5 #define PPCMODE_EPP_DWORD 6 //*************************************************************************** WORD select(PPC *ppc); void deselect(PPC *ppc); void send_cmd(PPC *ppc, BYTE cmd); void wr_data_byte(PPC *ppc, BYTE data); BYTE rd_data_byte(PPC *ppc); BYTE ppc6_rd_port(PPC *ppc, BYTE port); void ppc6_wr_port(PPC *ppc, BYTE port, BYTE data); BYTE ppc6_rd_reg(PPC *ppc, BYTE reg); void ppc6_wr_reg(PPC *ppc, BYTE reg, BYTE data); BYTE ppc6_version(PPC *ppc); void rd_data_blk(PPC *ppc, BYTE *data, DWORD count); void wait_for_fifo(PPC *ppc); void wr_data_blk(PPC *ppc, BYTE *data, DWORD count); void ppc6_rd_port16_blk(PPC *ppc, BYTE port, BYTE *data, DWORD length); WORD ppc6_rd_port16(PPC *ppc, BYTE port); void ppc6_wr_port16(PPC *ppc, BYTE port, WORD data); void ppc6_wr_port16_blk(PPC *ppc, BYTE port, BYTE *data, DWORD length); BYTE rd_eeprom_reg(PPC *ppc); void wr_eeprom_reg(PPC *ppc, BYTE data); void ppc6_eeprom_start(PPC *ppc); void ppc6_eeprom_end(PPC *ppc); void set_cs(PPC *ppc); void reset_cs(PPC *ppc); BYTE read_do_bit(PPC *ppc); void ready_wait(PPC *ppc); void snd_bit(PPC *ppc, BYTE bit); WORD ppc6_eeprom_read(PPC *ppc, BYTE addr); BYTE ppc6_irq_test(PPC *ppc); BYTE ppc6_rd_extout(PPC *ppc); void ppc6_wr_extout(PPC *ppc, BYTE regdata); WORD ppc6_open(PPC *ppc); void ppc6_close(PPC *ppc); //*************************************************************************** WORD select(PPC *ppc) { BYTE i, j, k; i = inp(ppc->lpt_addr + 1); if (i & 1) outp(ppc->lpt_addr + 1, i); ppc->org_data = inp(ppc->lpt_addr); ppc->org_ctrl = inp(ppc->lpt_addr + 2) & 0x5F; // readback ctrl ppc->cur_ctrl = ppc->org_ctrl; ppc->cur_ctrl |= port_sel; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); if (ppc->org_data == 'b') outp(ppc->lpt_addr, 'x'); outp(ppc->lpt_addr, 'b'); outp(ppc->lpt_addr, 'p'); outp(ppc->lpt_addr, ppc->ppc_id); outp(ppc->lpt_addr, ~ppc->ppc_id); ppc->cur_ctrl &= ~port_sel; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); ppc->cur_ctrl = (ppc->cur_ctrl & port_int) | port_init; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); i = ppc->mode & 0x0C; if (i == 0) i = (ppc->mode & 2) | 1; outp(ppc->lpt_addr, i); ppc->cur_ctrl |= port_sel; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); // DELAY ppc->cur_ctrl |= port_afd; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); j = ((i & 0x08) << 4) | ((i & 0x07) << 3); k = inp(ppc->lpt_addr + 1) & 0xB8; if (j == k) { ppc->cur_ctrl &= ~port_afd; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); k = (inp(ppc->lpt_addr + 1) & 0xB8) ^ 0xB8; if (j == k) { if (i & 4) // EPP { ppc->cur_ctrl &= ~(port_sel | port_init); outp(ppc->lpt_addr + 2, ppc->cur_ctrl); } else { ppc->cur_ctrl &= ~port_sel; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); } return(1); } } outp(ppc->lpt_addr + 2, ppc->org_ctrl); outp(ppc->lpt_addr, ppc->org_data); return(0); // FAIL } //*************************************************************************** void deselect(PPC *ppc) { if (ppc->mode & 4) // EPP { ppc->cur_ctrl |= port_init; } else // PPC/ECP { ppc->cur_ctrl |= port_sel; } outp(ppc->lpt_addr + 2, ppc->cur_ctrl); outp(ppc->lpt_addr, ppc->org_data); outp(ppc->lpt_addr + 2, (ppc->org_ctrl | port_sel)); outp(ppc->lpt_addr + 2, ppc->org_ctrl); } //*************************************************************************** void send_cmd(PPC *ppc, BYTE cmd) { switch(ppc->mode) { case PPCMODE_UNI_SW : case PPCMODE_UNI_FW : case PPCMODE_BI_SW : case PPCMODE_BI_FW : { outp(ppc->lpt_addr, cmd); ppc->cur_ctrl ^= cmd_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); break; } case PPCMODE_EPP_BYTE : case PPCMODE_EPP_WORD : case PPCMODE_EPP_DWORD : { outp(ppc->lpt_addr + 3, cmd); break; } } } //*************************************************************************** void wr_data_byte(PPC *ppc, BYTE data) { switch(ppc->mode) { case PPCMODE_UNI_SW : case PPCMODE_UNI_FW : case PPCMODE_BI_SW : case PPCMODE_BI_FW : { outp(ppc->lpt_addr, data); ppc->cur_ctrl ^= data_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); break; } case PPCMODE_EPP_BYTE : case PPCMODE_EPP_WORD : case PPCMODE_EPP_DWORD : { outp(ppc->lpt_addr + 4, data); break; } } } //*************************************************************************** BYTE rd_data_byte(PPC *ppc) { BYTE data; switch(ppc->mode) { case PPCMODE_UNI_SW : case PPCMODE_UNI_FW : { ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); // DELAY data = inp(ppc->lpt_addr + 1); data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3); ppc->cur_ctrl |= port_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); // DELAY data |= inp(ppc->lpt_addr + 1) & 0xB8; break; } case PPCMODE_BI_SW : case PPCMODE_BI_FW : { ppc->cur_ctrl |= port_dir; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); ppc->cur_ctrl = (ppc->cur_ctrl | port_stb) ^ data_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); data = inp(ppc->lpt_addr); ppc->cur_ctrl &= ~port_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); ppc->cur_ctrl &= ~port_dir; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); break; } case PPCMODE_EPP_BYTE : case PPCMODE_EPP_WORD : case PPCMODE_EPP_DWORD : { outp(ppc->lpt_addr + 2, (ppc->cur_ctrl | port_dir)); data = inp(ppc->lpt_addr + 4); outp(ppc->lpt_addr + 2, ppc->cur_ctrl); break; } } return(data); } //*************************************************************************** BYTE ppc6_rd_port(PPC *ppc, BYTE port) { send_cmd(ppc,(BYTE)(port | ACCESS_PORT | ACCESS_READ)); return(rd_data_byte(ppc)); } //*************************************************************************** void ppc6_wr_port(PPC *ppc, BYTE port, BYTE data) { send_cmd(ppc,(BYTE)(port | ACCESS_PORT | ACCESS_WRITE)); wr_data_byte(ppc, data); } //*************************************************************************** BYTE ppc6_rd_reg(PPC *ppc, BYTE reg) { send_cmd(ppc,(BYTE)(reg | ACCESS_REG | ACCESS_READ)); return(rd_data_byte(ppc)); } //*************************************************************************** void ppc6_wr_reg(PPC *ppc, BYTE reg, BYTE data) { send_cmd(ppc,(BYTE)(reg | ACCESS_REG | ACCESS_WRITE)); wr_data_byte(ppc, data); } //*************************************************************************** BYTE ppc6_version(PPC *ppc) { send_cmd(ppc,(REG_VERSION | ACCESS_REG | ACCESS_READ)); return(rd_data_byte(ppc) & 0x3F); } //*************************************************************************** void rd_data_blk(PPC *ppc, BYTE *data, DWORD count) { switch(ppc->mode) { case PPCMODE_UNI_SW : case PPCMODE_UNI_FW : { while(count) { BYTE d; ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); // DELAY d = inp(ppc->lpt_addr + 1); d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3); ppc->cur_ctrl |= port_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); // DELAY d |= inp(ppc->lpt_addr + 1) & 0xB8; *data++ = d; count--; } break; } case PPCMODE_BI_SW : case PPCMODE_BI_FW : { ppc->cur_ctrl |= port_dir; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); ppc->cur_ctrl |= port_stb; while(count) { ppc->cur_ctrl ^= data_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); *data++ = inp(ppc->lpt_addr); count--; } ppc->cur_ctrl &= ~port_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); ppc->cur_ctrl &= ~port_dir; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); break; } case PPCMODE_EPP_BYTE : { outp(ppc->lpt_addr + 2, (ppc->cur_ctrl | port_dir)); // DELAY while(count) { *data++ = inp(ppc->lpt_addr + 4); count--; } outp(ppc->lpt_addr + 2, ppc->cur_ctrl); break; } case PPCMODE_EPP_WORD : { outp(ppc->lpt_addr + 2, (ppc->cur_ctrl | port_dir)); // DELAY while(count > 1) { *((WORD *)data) = inpw(ppc->lpt_addr + 4); data += 2; count -= 2; } while(count) { *data++ = inp(ppc->lpt_addr + 4); count--; } outp(ppc->lpt_addr + 2, ppc->cur_ctrl); break; } case PPCMODE_EPP_DWORD : { outp(ppc->lpt_addr + 2, (ppc->cur_ctrl | port_dir)); // DELAY while(count > 3) { *((DWORD *)data) = inpd(ppc->lpt_addr + 4); data += 4; count -= 4; } while(count) { *data++ = inp(ppc->lpt_addr + 4); count--; } outp(ppc->lpt_addr + 2, ppc->cur_ctrl); break; } } } //*************************************************************************** void wait_for_fifo(PPC *ppc) { int i; if (ppc->ppc_flags & fifo_wait) { for(i=0; i<20; i++) inp(ppc->lpt_addr + 1); } } //*************************************************************************** void wr_data_blk(PPC *ppc, BYTE *data, DWORD count) { switch(ppc->mode) { case PPCMODE_UNI_SW : case PPCMODE_BI_SW : { while(count--) { outp(ppc->lpt_addr, *data++); ppc->cur_ctrl ^= data_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); } break; } case PPCMODE_UNI_FW : case PPCMODE_BI_FW : { BYTE this, last; send_cmd(ppc,(CMD_PREFIX_SET | PREFIX_FASTWR)); ppc->cur_ctrl |= port_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); last = *data; outp(ppc->lpt_addr, last); while(count) { this = *data++; count--; if (this == last) { ppc->cur_ctrl ^= data_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); } else { outp(ppc->lpt_addr, this); last = this; } } ppc->cur_ctrl &= ~port_stb; outp(ppc->lpt_addr + 2, ppc->cur_ctrl); send_cmd(ppc,(CMD_PREFIX_RESET | PREFIX_FASTWR)); break; } case PPCMODE_EPP_BYTE : { while(count) { outp(ppc->lpt_addr + 4, *data++); count--; } wait_for_fifo(ppc); break; } case PPCMODE_EPP_WORD : { while(count > 1) { outpw(ppc->lpt_addr + 4, *((WORD *)data)); data += 2; count -= 2; } while(count) { outp(ppc->lpt_addr + 4, *data++); count--; } wait_for_fifo(ppc); break; } case PPCMODE_EPP_DWORD : { while(count > 3) { outpd(ppc->lpt_addr + 4, *((DWORD *)data)); data += 4; count -= 4; } while(count) { outp(ppc->lpt_addr + 4, *data++); count--; } wait_for_fifo(ppc); break; } } } //*************************************************************************** void ppc6_rd_port16_blk(PPC *ppc, BYTE port, BYTE *data, DWORD length) { length = length << 1; send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE)); wr_data_byte(ppc,(BYTE)length); wr_data_byte(ppc,(BYTE)(length >> 8)); wr_data_byte(ppc,0); send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK)); send_cmd(ppc, (BYTE)(port | ACCESS_PORT | ACCESS_READ)); rd_data_blk(ppc, data, length); send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK)); } //*************************************************************************** WORD ppc6_rd_port16(PPC *ppc, BYTE port) { WORD data; send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16)); send_cmd(ppc, (BYTE)(port | ACCESS_PORT | ACCESS_READ)); data = rd_data_byte(ppc); send_cmd(ppc, (BYTE)(port | ACCESS_PORT | ACCESS_READ)); data += (WORD)rd_data_byte(ppc) << 8; send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16)); return(data); } //*************************************************************************** void ppc6_wr_port16(PPC *ppc, BYTE port, WORD data) { send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16)); send_cmd(ppc, (BYTE)(port | ACCESS_PORT | ACCESS_WRITE)); wr_data_byte(ppc, (BYTE)data); send_cmd(ppc, (BYTE)(port | ACCESS_PORT | ACCESS_WRITE)); wr_data_byte(ppc, (BYTE)(data >> 8)); send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16)); } //*************************************************************************** void ppc6_wr_port16_blk(PPC *ppc, BYTE port, BYTE *data, DWORD length) { length = length << 1; send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE)); wr_data_byte(ppc,(BYTE)length); wr_data_byte(ppc,(BYTE)(length >> 8)); wr_data_byte(ppc,0); send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK)); send_cmd(ppc, (BYTE)(port | ACCESS_PORT | ACCESS_WRITE)); wr_data_blk(ppc, data, length); send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK)); } //*************************************************************************** BYTE rd_eeprom_reg(PPC *ppc) { send_cmd(ppc, (REG_EEPROM | ACCESS_REG | ACCESS_READ)); return(rd_data_byte(ppc)); } //*************************************************************************** void wr_eeprom_reg(PPC *ppc, BYTE data) { send_cmd(ppc, (REG_EEPROM | ACCESS_REG | ACCESS_WRITE)); wr_data_byte(ppc, data); } //*************************************************************************** void ppc6_eeprom_start(PPC *ppc) { wr_eeprom_reg(ppc, EEPROM_EN); } //*************************************************************************** void ppc6_eeprom_end(PPC *ppc) { wr_eeprom_reg(ppc, 0); } //*************************************************************************** void set_cs(PPC *ppc) { wr_eeprom_reg(ppc, (BYTE)(rd_eeprom_reg(ppc) | EEPROM_CS)); } //*************************************************************************** void reset_cs(PPC *ppc) { wr_eeprom_reg(ppc, (BYTE)(rd_eeprom_reg(ppc) & ~EEPROM_CS)); } //*************************************************************************** BYTE read_do_bit(PPC *ppc) { send_cmd(ppc, (REG_STATUS | ACCESS_REG | ACCESS_READ)); if (rd_data_byte(ppc) & STATUS_EEPROM_DO) return(1); else return(0); } //*************************************************************************** void ready_wait(PPC *ppc) { set_cs(ppc); while(read_do_bit(ppc)); reset_cs(ppc); } //*************************************************************************** void snd_bit(PPC *ppc, BYTE bit) { BYTE eereg; eereg = rd_eeprom_reg(ppc); eereg &= ~(EEPROM_SK | EEPROM_DI); if (bit & 1) eereg |= EEPROM_DI; wr_eeprom_reg(ppc, eereg); eereg |= EEPROM_SK; wr_eeprom_reg(ppc, eereg); eereg &= ~EEPROM_SK; wr_eeprom_reg(ppc, eereg); } //*************************************************************************** WORD ppc6_eeprom_read(PPC *ppc, BYTE addr) { int i; WORD data; set_cs(ppc); snd_bit(ppc, 1); // Start bit snd_bit(ppc, 1); // opcode 10 (read) snd_bit(ppc, 0); for(i=0; i<6; i++) snd_bit(ppc, (BYTE)((addr >> (5 - i)) & 1)); data = 0; for(i=0; i<16; i++) { snd_bit(ppc,0); data = (data << 1) | read_do_bit(ppc); } reset_cs(ppc); return(data); } //*************************************************************************** BYTE ppc6_irq_test(PPC *ppc) { send_cmd(ppc,(REG_STATUS | ACCESS_REG | ACCESS_READ)); return(rd_data_byte(ppc) & STATUS_IRQA); } //*************************************************************************** BYTE ppc6_rd_extout(PPC *ppc) { send_cmd(ppc,(REG_VERSION | ACCESS_REG | ACCESS_READ)); return((rd_data_byte(ppc) & 0xC0) >> 6); } //*************************************************************************** void ppc6_wr_extout(PPC *ppc, BYTE regdata) { send_cmd(ppc,(REG_VERSION | ACCESS_REG | ACCESS_WRITE)); wr_data_byte(ppc, (BYTE)((regdata & 0x03) << 6)); } //*************************************************************************** WORD ppc6_open(PPC *ppc) { WORD ret; ret = select(ppc); if (ret == 0) return(ret); ppc->ppc_flags &= ~fifo_wait; send_cmd(ppc, (ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE)); wr_data_byte(ppc, RAMSIZE_128K); send_cmd(ppc, (ACCESS_REG | ACCESS_READ | REG_VERSION)); if ((rd_data_byte(ppc) & 0x3F) == 0x0C) ppc->ppc_flags |= fifo_wait; return(ret); } //*************************************************************************** void ppc6_close(PPC *ppc) { deselect(ppc); } //***************************************************************************