/* * tfe.h - TFE ("The final ethernet" emulation. * * Written by * Spiro Trikaliotis * * This file is part of VICE, the Versatile Commodore Emulator. * See README for copyright notice. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA. * */ /* Emulate a Uthernet 1 card (adapted from VICE's TFE support) */ #include "StdAfx.h" #include "Uthernet1.h" #include "YamlHelper.h" #include "Log.h" #include "Memory.h" #include "Interface.h" #include "Tfe/tfearch.h" #include "Tfe/tfesupp.h" #include "Tfe/NetworkBackend.h" #include "Tfe/PCapBackend.h" /* Makros for reading and writing the visible TFE register: */ #define GET_TFE_8( _xxx_ ) \ ( assert(_xxx_> 8) & 0xff; \ } while (0) /* Makros for reading and writing the PacketPage register: */ #define GET_PP_8( _xxx_ ) \ (assert(_xxx_> 8) & 0xFF; \ } while (0) #define SET_PP_32( _xxx_, _val_ ) \ do { \ assert(_xxx_> 8) & 0xFF; \ tfe_packetpage[_xxx_+2] = (_val_>>16) & 0xFF; \ tfe_packetpage[_xxx_+3] = (_val_>>24) & 0xFF; \ } while (0) /* ------------------------------------------------------------------------- */ /* debugging functions */ #ifdef TFE_DEBUG_FRAMES std::string debug_outbuffer(size_t length, const unsigned char* buffer) { std::string outbuffer; outbuffer.reserve(length * 3); for (size_t i = 0; i < length; i++) { StrAppendByteAsHex(outbuffer, buffer[i]); outbuffer += ((i+1)%16==0)?'*':(((i+1)%8==0)?'-':' '); } return outbuffer; } #endif #ifdef TFE_DEBUG_DUMP #define NUMBER_PER_LINE 8 void Uthernet1::tfe_debug_output_general( const char *what, WORD (Uthernet1::*getFunc)(int), int count ) { if (!g_fh) return; fprintf(g_fh, "%s contents:\n", what); for (int i = 0; i < count; i += 2*NUMBER_PER_LINE) { fprintf(g_fh, "%04X: ", i); for (int j = 0; j < NUMBER_PER_LINE; j++) { fprintf(g_fh, "%04X, ", (this->*getFunc)(i+j+j)); } fputc('\n', g_fh); } } WORD Uthernet1::tfe_debug_output_io_getFunc( int i ) { return GET_TFE_16(i); } void Uthernet1::tfe_debug_output_io( void ) { tfe_debug_output_general( "TFE I/O", &Uthernet1::tfe_debug_output_io_getFunc, TFE_COUNT_IO_REGISTER ); } #define TFE_DEBUG_OUTPUT_IO() tfe_debug_output_io() WORD Uthernet1::tfe_debug_output_pp_getFunc( int i ) { return GET_PP_16(i); } void Uthernet1::tfe_debug_output_pp( void ) { tfe_debug_output_general( "PacketPage", &Uthernet1::tfe_debug_output_pp_getFunc, 0x0160 /* MAX_PACKETPAGE_ARRAY */ ); } #define TFE_DEBUG_OUTPUT_PP() tfe_debug_output_pp() #define TFE_DEBUG_OUTPUT_REG() \ do { TFE_DEBUG_OUTPUT_IO(); TFE_DEBUG_OUTPUT_PP(); } while (0) #else #define TFE_DEBUG_OUTPUT_IO() #define TFE_DEBUG_OUTPUT_PP() #define TFE_DEBUG_OUTPUT_REG() #endif Uthernet1::Uthernet1(UINT slot) : Card(CT_Uthernet, slot) { if (m_slot != SLOT3) // fixme ThrowErrorInvalidSlot(); Init(); } void Uthernet1::Init(void) { // Initialise all state member variables // in the same order as the header file to ease maintenance memset( tfe_ia_mac, 0, sizeof(tfe_ia_mac) ); memset( tfe_hash_mask, 0, sizeof(tfe_hash_mask) ); tfe_recv_broadcast = 0; tfe_recv_mac = 0; tfe_recv_multicast = 0; tfe_recv_correct = 0; tfe_recv_promiscuous = 0; tfe_recv_hashfilter = 0; #ifdef TFE_DEBUG_WARN tfe_started_tx = 0; #endif /* initialize visible IO register and PacketPage registers */ memset( tfe, 0, sizeof(tfe) ); txcollect_buffer = TFE_PP_ADDR_TX_FRAMELOC; rx_buffer = TFE_PP_ADDR_RXSTATUS; memset( tfe_packetpage, 0, sizeof(tfe_packetpage) ); tfe_packetpage_ptr = 0; /* according to page 19 unless stated otherwise */ SET_PP_32(TFE_PP_ADDR_PRODUCTID, 0x0700630E ); /* p.41: 0E630007 for Rev. B; reversed order! */ SET_PP_16(TFE_PP_ADDR_IOBASE, 0x0300); SET_PP_16(TFE_PP_ADDR_INTNO, 0x0004); /* xxxx xxxx xxxx x100b */ SET_PP_16(TFE_PP_ADDR_DMA_CHAN, 0x0003); /* xxxx xxxx xxxx xx11b */ #if 0 /* not needed since all memory is initialized with 0 */ SET_PP_16(TFE_PP_ADDR_DMA_SOF, 0x0000); SET_PP_16(TFE_PP_ADDR_DMA_FC, 0x0000); /* x000h */ SET_PP_16(TFE_PP_ADDR_RXDMA_BC, 0x0000); SET_PP_32(TFE_PP_ADDR_MEMBASE, 0x0000); /* xxx0 0000h */ SET_PP_32(TFE_PP_ADDR_BPROM_BASE, 0x00000000); /* xxx0 0000h */ SET_PP_32(TFE_PP_ADDR_BPROM_MASK, 0x00000000); /* xxx0 0000h */ SET_PP_16(TFE_PP_ADDR_SE_ISQ, 0x0000); /* p. 51 */ #endif /* according to descriptions of the registers, see definitions of TFE_PP_ADDR_CC_... and TFE_PP_ADDR_SE_... above! */ SET_PP_16(TFE_PP_ADDR_CC_RXCFG, 0x0003); SET_PP_16(TFE_PP_ADDR_CC_RXCTL, 0x0005); SET_PP_16(TFE_PP_ADDR_CC_TXCFG, 0x0007); SET_PP_16(TFE_PP_ADDR_CC_TXCMD, 0x0009); SET_PP_16(TFE_PP_ADDR_CC_BUFCFG, 0x000B); SET_PP_16(TFE_PP_ADDR_CC_LINECTL, 0x0013); SET_PP_16(TFE_PP_ADDR_CC_SELFCTL, 0x0015); SET_PP_16(TFE_PP_ADDR_CC_BUSCTL, 0x0017); SET_PP_16(TFE_PP_ADDR_CC_TESTCTL, 0x0019); SET_PP_16(TFE_PP_ADDR_SE_ISQ, 0x0000); SET_PP_16(TFE_PP_ADDR_SE_RXEVENT, 0x0004); SET_PP_16(TFE_PP_ADDR_SE_TXEVENT, 0x0008); SET_PP_16(TFE_PP_ADDR_SE_BUFEVENT, 0x000C); SET_PP_16(TFE_PP_ADDR_SE_RXMISS, 0x0010); SET_PP_16(TFE_PP_ADDR_SE_TXCOL, 0x0012); SET_PP_16(TFE_PP_ADDR_SE_LINEST, 0x0014); SET_PP_16(TFE_PP_ADDR_SE_SELFST, 0x0016); SET_PP_16(TFE_PP_ADDR_SE_BUSST, 0x0018); SET_PP_16(TFE_PP_ADDR_SE_TDR, 0x001C); TFE_DEBUG_OUTPUT_REG(); } void Uthernet1::tfe_sideeffects_write_pp_on_txframe(WORD ppaddress) { if (ppaddress==TFE_PP_ADDR_TX_FRAMELOC+GET_PP_16(TFE_PP_ADDR_TXLENGTH)-1) { /* we have collected the whole frame, now start transmission */ WORD txcmd = GET_PP_16(TFE_PP_ADDR_TXCMD); WORD txlen = GET_PP_16(TFE_PP_ADDR_TXLENGTH); WORD busst = GET_PP_16(TFE_PP_ADDR_SE_BUSST); if ( (txlen>MAX_TXLENGTH) || ((txlen>MAX_TXLENGTH-4) && (!(txcmd&0x1000))) || (txlenMAX_TXLENGTH) || ((txlength>MAX_TXLENGTH-4) && (!(txcommand&0x1000))) ) { /* txlength too big, mark an error */ SET_PP_16(TFE_PP_ADDR_SE_BUSST, (GET_PP_16(TFE_PP_ADDR_SE_BUSST) | 0x80) & ~0x100); } else { /* all right, signal that we're ready for the next frame */ SET_PP_16(TFE_PP_ADDR_SE_BUSST, (GET_PP_16(TFE_PP_ADDR_SE_BUSST) & ~0x80) | 0x100); } } break; case TFE_PP_ADDR_LOG_ADDR_FILTER: case TFE_PP_ADDR_LOG_ADDR_FILTER+2: case TFE_PP_ADDR_LOG_ADDR_FILTER+4: case TFE_PP_ADDR_LOG_ADDR_FILTER+6: { unsigned int pos = 8 * (ppaddress - TFE_PP_ADDR_LOG_ADDR_FILTER + oddaddress); DWORD *p = (pos < 32) ? &tfe_hash_mask[0] : &tfe_hash_mask[1]; *p &= ~(0xFF << pos); /* clear out relevant bits */ *p |= GET_PP_8(ppaddress+oddaddress) << pos; tfe_arch_set_hashfilter(tfe_hash_mask); } break; case TFE_PP_ADDR_MAC_ADDR: case TFE_PP_ADDR_MAC_ADDR+2: case TFE_PP_ADDR_MAC_ADDR+4: /* the MAC address has been changed */ tfe_ia_mac[ppaddress-TFE_PP_ADDR_MAC_ADDR+oddaddress] = GET_PP_8(ppaddress+oddaddress); tfe_arch_set_mac(tfe_ia_mac); break; } } /* This is called *before* the relevant octets are read */ void Uthernet1::tfe_sideeffects_read_pp(WORD ppaddress) { assert((ppaddress & 1) == 0); switch (ppaddress) { case TFE_PP_ADDR_SE_RXEVENT: /* reading this before all octets of the frame are read performs an "implied skip" */ { WORD ret_val = tfe_receive(); /* RXSTATUS and RXEVENT are the same, except that RXSTATUS buffers the old value while RXEVENT sets a new value whenever it is called */ SET_PP_16(TFE_PP_ADDR_RXSTATUS, ret_val); SET_PP_16(TFE_PP_ADDR_SE_RXEVENT, ret_val); } break; case TFE_PP_ADDR_SE_BUSST: break; case TFE_PP_ADDR_TXCMD: #ifdef TFE_DEBUG_WARN if(g_fh) fprintf(g_fh, "WARNING! Read write-only register TFE_PP_ADDR_TXCMD: IGNORED\n"); #endif break; case TFE_PP_ADDR_TXLENGTH: #ifdef TFE_DEBUG_WARN if(g_fh) fprintf(g_fh, "WARNING! Read write-only register TFE_PP_ADDR_TXLENGTH: IGNORED\n"); #endif break; } } void Uthernet1::tfe_proceed_rx_buffer(int oddaddress) { /* According to the CS8900 spec, the handling is the following: first read H, then L, then H, then L. Now, we're inside the RX frame, now, we always get L then H, until the end is reached. even odd TFE_PP_ADDR_RXSTATUS: - proceed 1) TFE_PP_ADDR_RXLENGTH: - proceed TFE_PP_ADDR_RX_FRAMELOC: 2),3) - TFE_PP_ADDR_RX_FRAMELOC+2: proceed - TFE_PP_ADDR_RX_FRAMELOC+4: like TFE_PP_ADDR_RX_FRAMELOC+2 1) set status "Inside FRAMELOC" FALSE 2) set status "Inside FRAMELOC" TRUE if it is not already 3) if "Inside FRAMELOC", proceed */ static int inside_frameloc; int proceed = 0; if (rx_buffer==TFE_PP_ADDR_RX_FRAMELOC+GET_PP_16(TFE_PP_ADDR_RXLENGTH)) { /* we've read all that is available, go to start again */ rx_buffer = TFE_PP_ADDR_RXSTATUS; inside_frameloc = 0; } else { switch (rx_buffer) { case TFE_PP_ADDR_RXSTATUS: if (oddaddress) { proceed = 1; inside_frameloc = 0; } break; case TFE_PP_ADDR_RXLENGTH: if (oddaddress) { proceed = 1; } break; case TFE_PP_ADDR_RX_FRAMELOC: if (oddaddress==0) { if (inside_frameloc) { proceed = 1; } else { inside_frameloc = 1; } } break; default: proceed = (oddaddress==0) ? 1 : 0; break; } } if (proceed) { SET_TFE_16(TFE_ADDR_RXTXDATA, GET_PP_16(rx_buffer)); rx_buffer += 2; } } BYTE REGPARM1 Uthernet1::tfe_read(WORD ioaddress) { BYTE retval; assert( ioaddress < TFE_COUNT_IO_REGISTER); switch (ioaddress) { case TFE_ADDR_TXCMD: case TFE_ADDR_TXCMD+1: case TFE_ADDR_TXLENGTH: case TFE_ADDR_TXLENGTH+1: #ifdef TFE_DEBUG_WARN if(g_fh) fprintf(g_fh, "WARNING! Reading write-only TFE register $%02X!\n", ioaddress); #endif /* @SRT TODO: Verify with reality */ retval = GET_TFE_8(ioaddress); break; case TFE_ADDR_RXTXDATA2: case TFE_ADDR_RXTXDATA2+1: case TFE_ADDR_PP_DATA2: case TFE_ADDR_PP_DATA2+1: #ifdef TFE_DEBUG_WARN if(g_fh) fprintf(g_fh, "WARNING! Reading not supported TFE register $%02X!\n", ioaddress); #endif /* @SRT TODO */ retval = GET_TFE_8(ioaddress); break; case TFE_ADDR_PP_DATA: case TFE_ADDR_PP_DATA+1: /* make sure the TFE register have the correct content */ { WORD ppaddress = tfe_packetpage_ptr & (MAX_PACKETPAGE_ARRAY-1); /* perform side-effects the read may perform */ tfe_sideeffects_read_pp( ppaddress ); /* [3] make sure the data matches the real value - [1] assumes this! */ SET_TFE_16( TFE_ADDR_PP_DATA, GET_PP_16(ppaddress) ); } #ifdef TFE_DEBUG_LOAD if(g_fh) fprintf(g_fh, "reading PP Ptr: $%04X => $%04X.", tfe_packetpage_ptr, GET_PP_16(tfe_packetpage_ptr) ); #endif retval = GET_TFE_8(ioaddress); break; case TFE_ADDR_INTSTQUEUE: case TFE_ADDR_INTSTQUEUE+1: SET_TFE_16( TFE_ADDR_INTSTQUEUE, GET_PP_16(0x0120) ); retval = GET_TFE_8(ioaddress); break; case TFE_ADDR_RXTXDATA: case TFE_ADDR_RXTXDATA+1: /* we're trying to read a new 16 bit word, get it from the receive buffer */ tfe_proceed_rx_buffer(ioaddress & 0x01); retval = GET_TFE_8(ioaddress); break; default: retval = GET_TFE_8(ioaddress); break; }; #ifdef TFE_DEBUG_LOAD if(g_fh) fprintf(g_fh, "read [$%02X] => $%02X.", ioaddress, retval); #endif return retval; } void REGPARM2 Uthernet1::tfe_store(WORD ioaddress, BYTE byte) { assert( ioaddress < TFE_COUNT_IO_REGISTER); switch (ioaddress) { case TFE_ADDR_RXTXDATA: case TFE_ADDR_RXTXDATA+1: SET_PP_8(txcollect_buffer, byte); tfe_sideeffects_write_pp_on_txframe(txcollect_buffer++); break; case TFE_ADDR_INTSTQUEUE: case TFE_ADDR_INTSTQUEUE+1: #ifdef TFE_DEBUG_WARN if(g_fh) fprintf(g_fh, "WARNING! Writing read-only TFE register $%02X!\n", ioaddress); #endif /* @SRT TODO: Verify with reality */ /* do nothing */ return; case TFE_ADDR_RXTXDATA2: case TFE_ADDR_RXTXDATA2+1: case TFE_ADDR_PP_DATA2: case TFE_ADDR_PP_DATA2+1: #ifdef TFE_DEBUG_WARN if(g_fh) fprintf(g_fh, "WARNING! Writing not supported TFE register $%02X!\n", ioaddress); #endif /* do nothing */ return; case TFE_ADDR_TXCMD: case TFE_ADDR_TXCMD+1: SET_TFE_8(ioaddress, byte); SET_PP_8((ioaddress-TFE_ADDR_TXCMD)+TFE_PP_ADDR_TXCMD, byte); /* perform the mapping to PP+0144 */ tfe_sideeffects_write_pp(TFE_PP_ADDR_TXCMD, ioaddress-TFE_ADDR_TXCMD); break; case TFE_ADDR_TXLENGTH: case TFE_ADDR_TXLENGTH+1: SET_TFE_8(ioaddress, byte); SET_PP_8((ioaddress-TFE_ADDR_TXLENGTH)+TFE_PP_ADDR_TXLENGTH, byte ); /* perform the mapping to PP+0144 */ tfe_sideeffects_write_pp(TFE_PP_ADDR_TXLENGTH, ioaddress-TFE_ADDR_TXLENGTH); break; /* #define TFE_ADDR_TXCMD 0x04 * -W Maps to PP+0144 * #define TFE_ADDR_TXLENGTH 0x06 * -W Maps to PP+0146 * #define TFE_ADDR_INTSTQUEUE 0x08 * R- Interrupt status queue, maps to PP + 0120 * */ case TFE_ADDR_PP_DATA: case TFE_ADDR_PP_DATA+1: /* [2] make sure the data matches the real value - [1] assumes this! */ SET_TFE_16(TFE_ADDR_PP_DATA, GET_PP_16(tfe_packetpage_ptr)); /* FALL THROUGH */ default: SET_TFE_8(ioaddress, byte); } #ifdef TFE_DEBUG_STORE if(g_fh) fprintf(g_fh, "store [$%02X] <= $%02X.", ioaddress, (int)byte); #endif /* now check if we have to do any side-effects */ switch (ioaddress) { case TFE_ADDR_PP_PTR: case TFE_ADDR_PP_PTR+1: tfe_packetpage_ptr = GET_TFE_16(TFE_ADDR_PP_PTR); #ifdef TFE_DEBUG_STORE if(g_fh) fprintf(g_fh, "set PP Ptr to $%04X.", tfe_packetpage_ptr); #endif if ((tfe_packetpage_ptr & 1) != 0) { #ifdef TFE_DEBUG_WARN if(g_fh) fprintf(g_fh, "WARNING! PacketPage register set to odd address $%04X (not allowed!)\n", tfe_packetpage_ptr ); #endif /* #ifdef TFE_DEBUG_WARN */ /* "correct" the address to the next lower address REMARK: I don't know how a real cs8900a will behave in this case, since it is not allowed. Nevertheless, this "correction" prevents assert()s to fail. */ tfe_packetpage_ptr -= 1; } /* [1] The TFE_ADDR_PP_DATA does not need to be modified here, since it will be modified just before a read or store operation is to be performed. See [2] and [3] */ break; case TFE_ADDR_PP_DATA: case TFE_ADDR_PP_DATA+1: { WORD ppaddress = tfe_packetpage_ptr & (MAX_PACKETPAGE_ARRAY-1); #ifdef TFE_DEBUG_STORE if(g_fh) fprintf(g_fh, "before writing to PP Ptr: $%04X <= $%04X.", ppaddress, GET_PP_16(ppaddress) ); #endif { WORD tmpIoAddr = ioaddress & ~1; /* word-align the address */ SET_PP_16(ppaddress, GET_TFE_16(tmpIoAddr)); } /* perform side-effects the write may perform */ /* the addresses are always aligned on the whole 16-bit-word */ tfe_sideeffects_write_pp(ppaddress, ioaddress-TFE_ADDR_PP_DATA); #ifdef TFE_DEBUG_STORE if(g_fh) fprintf(g_fh, "after writing to PP Ptr: $%04X <= $%04X.", ppaddress, GET_PP_16(ppaddress) ); #endif } break; } TFE_DEBUG_OUTPUT_REG(); } #ifdef TFE_DEBUG_FRAMES #define return( _x_ ) \ { \ int retval = _x_; \ \ if(g_fh) fprintf(g_fh, "%s correct_mac=%u, broadcast=%u, multicast=%u, hashed=%u, hash_index=%u", (retval? "+++ ACCEPTED":"--- rejected"), *pcorrect_mac, *pbroadcast, *pmulticast, *phashed, *phash_index); \ \ return retval; \ } #endif /* This is a helper for tfe_receive() to determine if the received frame should be accepted according to the settings. This function is even allowed to be called in tfearch.c from tfe_arch_receive() if necessary, which is the reason why its prototype is included here in tfearch.h. */ int Uthernet1::tfe_should_accept(unsigned char *buffer, int length, int *phashed, int *phash_index, int *pcorrect_mac, int *pbroadcast, int *pmulticast) { int hashreg; /* Hash Register (for hash computation) */ assert(length>=6); /* we need at least 6 octets since the DA has this length */ /* first of all, delete any status */ *phashed = 0; *phash_index = 0; *pcorrect_mac = 0; *pbroadcast = 0; *pmulticast = 0; #ifdef TFE_DEBUG_FRAMES if(g_fh) fprintf(g_fh, "tfe_should_accept called with %02X:%02X:%02X:%02X:%02X:%02X, length=%4u and buffer %s", tfe_ia_mac[0], tfe_ia_mac[1], tfe_ia_mac[2], tfe_ia_mac[3], tfe_ia_mac[4], tfe_ia_mac[5], length, debug_outbuffer(length, buffer).c_str() ); #endif if ( buffer[0]==tfe_ia_mac[0] && buffer[1]==tfe_ia_mac[1] && buffer[2]==tfe_ia_mac[2] && buffer[3]==tfe_ia_mac[3] && buffer[4]==tfe_ia_mac[4] && buffer[5]==tfe_ia_mac[5] ) { /* this is our individual address (IA) */ *pcorrect_mac = 1; /* if we don't want "correct MAC", we might have the chance * that this address fits the hash index */ if (tfe_recv_mac || tfe_recv_promiscuous) return(1); } if ( buffer[0]==0xFF && buffer[1]==0xFF && buffer[2]==0xFF && buffer[3]==0xFF && buffer[4]==0xFF && buffer[5]==0xFF ) { /* this is a broadcast address */ *pbroadcast = 1; /* broadcasts cannot be accepted by the hash filter */ return((tfe_recv_broadcast || tfe_recv_promiscuous) ? 1 : 0); } /* now check if DA passes the hash filter */ /* RGJ added (const char *) for AppleWin */ hashreg = (~crc32_buf((const char *)buffer,6) >> 26) & 0x3F; *phashed = (tfe_hash_mask[(hashreg>=32)?1:0] & (1 << (hashreg&0x1F))) ? 1 : 0; if (*phashed) { *phash_index = hashreg; if (buffer[0] & 0x80) { /* we have a multicast address */ *pmulticast = 1; /* if the multicast address fits into the hash filter, * the hashed bit has to be clear */ *phashed = 0; return((tfe_recv_multicast || tfe_recv_promiscuous) ? 1 : 0); } return((tfe_recv_hashfilter || tfe_recv_promiscuous) ? 1 : 0); } return(tfe_recv_promiscuous ? 1 : 0); } #ifdef TFE_DEBUG_FRAMES #undef return #endif WORD Uthernet1::tfe_receive(void) { WORD ret_val = 0x0004; BYTE buffer[MAX_RXLENGTH]; int multicast = 0; int ready; #ifdef TFE_DEBUG_FRAMES if(g_fh) fprintf( g_fh, ""); #endif do { ready = 1 ; /* assume we will find a good frame */ int len = networkBackend->receive( sizeof(buffer), /* size of buffer */ buffer /* where to store a frame */ ); if (len > 0) { assert((len&1) == 0); /* length has to be even! */ int hashed = 0; int hash_index = 0; int broadcast = 0; int correct_mac = 0; int crc_error = 0; int rx_ok = 1; /* determine ourself the type of frame */ if (!tfe_should_accept(buffer, len, &hashed, &hash_index, &correct_mac, &broadcast, &multicast)) { /* if we should not accept this frame, just do nothing * now, look for another one */ ready = 0; /* try another frame */ continue; } /* we did receive a frame, return that status */ ret_val |= rx_ok ? 0x0100 : 0; ret_val |= multicast ? 0x0200 : 0; if (!multicast) { ret_val |= hashed ? 0x0040 : 0; } if (hashed && rx_ok) { /* we have the 2nd, special format with hash index: */ assert(hash_index < 64); ret_val |= hash_index << 9; } else { /* we have the regular format */ ret_val |= correct_mac ? 0x0400 : 0; ret_val |= broadcast ? 0x0800 : 0; ret_val |= crc_error ? 0x1000 : 0; ret_val |= (lenMAX_RXLENGTH) ? 0x4000 : 0; } /* discard any octets that are beyond the MAX_RXLEN */ if (len>MAX_RXLENGTH) { len = MAX_RXLENGTH; } if (rx_ok) { int i; /* set relevant parts of the PP area to correct values */ SET_PP_16(TFE_PP_ADDR_RXLENGTH, len); for (i=0;itransmit(txlength, txframe); } // Go via TfeIoCxxx() instead of directly calling IO_Null() to include this specific (slot-3) _DEBUG check static BYTE __stdcall TfeIoCxxx (WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles) { #ifdef _DEBUG const UINT slot = (address >> 8) & 0xf; if (!IS_APPLE2 && slot == SLOT3) { // Derived from UTAIIe:5-28 // // INTCXROM SLOTC3ROM TFE floating bus? // 0 0 N (internal ROM) // 0 1 Y // 1 0 N (internal ROM) // 1 1 N (internal ROM) if (! (!MemCheckINTCXROM() && MemCheckSLOTC3ROM()) ) { _ASSERT(0); // Card ROM disabled, so IO_Cxxx() returns the internal ROM } } #endif return IO_Null(programcounter, address, write, value,nCycles); } static BYTE __stdcall TfeIo (WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles) { UINT uSlot = ((address & 0xff) >> 4) - 8; Uthernet1* pCard = (Uthernet1*) MemGetSlotParameters(uSlot); BYTE ret = 0; if (write) { pCard->tfe_store((WORD)(address & 0x0f), value); } else { ret = pCard->tfe_read((WORD)(address & 0x0f)); } return ret; } void Uthernet1::InitializeIO(LPBYTE pCxRomPeripheral) { const std::string interfaceName = PCapBackend::GetRegistryInterface(m_slot); networkBackend = GetFrame().CreateNetworkBackend(interfaceName); if (!networkBackend->isValid()) { // Interface doesn't exist or user picked an interface that isn't Ethernet! GetFrame().FrameMessageBox("Uthernet interface isn't valid!\nReconfigure the Interface via 'Ethernet Settings'.", "Uthernet Interface", MB_ICONEXCLAMATION | MB_SETFOREGROUND); } RegisterIoHandler(m_slot, TfeIo, TfeIo, TfeIoCxxx, TfeIoCxxx, this, NULL); } void Uthernet1::Reset(const bool powerCycle) { if (powerCycle) { Init(); } } void Uthernet1::Update(const ULONG nExecutedCycles) { networkBackend->update(nExecutedCycles); } /* ------------------------------------------------------------------------- */ /* snapshot support functions */ #define SS_YAML_KEY_ENABLED "Enabled" #define SS_YAML_KEY_NETWORK_INTERFACE "Network Interface" #define SS_YAML_KEY_STARTED_TX "Started Tx" #define SS_YAML_KEY_CANNOT_USE "Cannot Use" #define SS_YAML_KEY_TXCOLLECT_BUFFER "Tx Collect Buffer" #define SS_YAML_KEY_RX_BUFFER "Rx Buffer" #define SS_YAML_KEY_CS8900A_REGS "CS8900A Registers" #define SS_YAML_KEY_PACKETPAGE_REGS "PacketPage Registers" static const UINT kUNIT_VERSION = 1; const std::string& Uthernet1::GetSnapshotCardName(void) { static const std::string name("Uthernet"); return name; } void Uthernet1::SaveSnapshot(class YamlSaveHelper& yamlSaveHelper) { YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION); YamlSaveHelper::Label unit(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE); yamlSaveHelper.SaveBool(SS_YAML_KEY_ENABLED, networkBackend->isValid() ? true : false); yamlSaveHelper.SaveString(SS_YAML_KEY_NETWORK_INTERFACE, networkBackend->getInterfaceName()); yamlSaveHelper.SaveBool(SS_YAML_KEY_STARTED_TX, tfe_started_tx ? true : false); yamlSaveHelper.SaveBool(SS_YAML_KEY_CANNOT_USE, PCapBackend::tfe_is_npcap_loaded() ? false : false); yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_TXCOLLECT_BUFFER, txcollect_buffer); yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_RX_BUFFER, rx_buffer); { YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_CS8900A_REGS); yamlSaveHelper.SaveMemory(tfe, TFE_COUNT_IO_REGISTER); } { YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_PACKETPAGE_REGS); yamlSaveHelper.SaveMemory(tfe_packetpage, MAX_PACKETPAGE_ARRAY); } } bool Uthernet1::LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT version) { if (version < 1 || version > kUNIT_VERSION) ThrowErrorInvalidVersion(version); yamlLoadHelper.LoadBool(SS_YAML_KEY_ENABLED); // FIXME: what is the point of this? PCapBackend::SetRegistryInterface(m_slot, yamlLoadHelper.LoadString(SS_YAML_KEY_NETWORK_INTERFACE)); tfe_started_tx = yamlLoadHelper.LoadBool(SS_YAML_KEY_STARTED_TX) ? true : false; // it is meaningless to restore this boolean flag // as it depends on the availability of npcap on *this* pc const bool tfe_cannot_use = yamlLoadHelper.LoadBool(SS_YAML_KEY_CANNOT_USE); txcollect_buffer = yamlLoadHelper.LoadUint(SS_YAML_KEY_TXCOLLECT_BUFFER); rx_buffer = yamlLoadHelper.LoadUint(SS_YAML_KEY_RX_BUFFER); if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_CS8900A_REGS)) throw std::runtime_error("Card: Expected key: " SS_YAML_KEY_CS8900A_REGS); memset(tfe, 0, TFE_COUNT_IO_REGISTER); yamlLoadHelper.LoadMemory(tfe, TFE_COUNT_IO_REGISTER); yamlLoadHelper.PopMap(); if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_PACKETPAGE_REGS)) throw std::runtime_error("Card: Expected key: " SS_YAML_KEY_PACKETPAGE_REGS); memset(tfe_packetpage, 0, MAX_PACKETPAGE_ARRAY); yamlLoadHelper.LoadMemory(tfe_packetpage, MAX_PACKETPAGE_ARRAY); yamlLoadHelper.PopMap(); // Side effects after PackagePage has been loaded tfe_packetpage_ptr = GET_TFE_16(TFE_ADDR_PP_PTR); tfe_sideeffects_write_pp(TFE_PP_ADDR_CC_RXCTL, 0); // set the 6 tfe_recv_* vars for (UINT i = 0; i < 8; i++) tfe_sideeffects_write_pp((TFE_PP_ADDR_LOG_ADDR_FILTER + i) & ~1, i & 1); // set tfe_hash_mask for (UINT i = 0; i < 6; i++) tfe_sideeffects_write_pp((TFE_PP_ADDR_MAC_ADDR + i) & ~1, i & 1); // set tfe_ia_mac return true; }