]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
Emulate TDMD/DPOLL correctly (Fixes Linux Ariadne driver), add full packet logging...
authorToni Wilen <twilen@winuae.net>
Sat, 27 Jun 2026 12:07:57 +0000 (15:07 +0300)
committerToni Wilen <twilen@winuae.net>
Sat, 27 Jun 2026 12:07:57 +0000 (15:07 +0300)
a2065.cpp

index 13b51dc40ecdebec15c2edd98c06a06fbb26adeb..33e9813f23bbdd9fbac726b4a1cd9873f0865cbe 100644 (file)
--- a/a2065.cpp
+++ b/a2065.cpp
@@ -32,6 +32,7 @@ int log_a2065 = 0;
 static int log_transmit = 1;
 static int log_receive = 1;
 int a2065_promiscuous = 0;
+int a2065_mungemac = 1;
 
 #define RECEIVE_QUEUE_SIZE 256
 #define RECEIVE_DRAIN_LIMIT 16
@@ -167,12 +168,12 @@ static int dofakemac (uae_u8 *packet)
        if (!memcmp(fakemac, realmac, 6)) {
                return 1;
        }
-       if (!memcmp (packet, fakemac, 6)) {
-               memcpy (packet, realmac, 6);
+       if (!memcmp(packet, fakemac, 6)) {
+               memcpy(packet, realmac, 6);
                return 1;
        }
-       if (!memcmp (packet, realmac, 6)) {
-               memcpy (packet, fakemac, 6);
+       if (!memcmp(packet, realmac, 6)) {
+               memcpy(packet, fakemac, 6);
                return 1;
        }
        return 0;
@@ -182,29 +183,32 @@ static int dofakemac (uae_u8 *packet)
 // We have to do this because drivers are hardcoded to
 // Commodore's MAC address range.
 
-static int mungepacket (uae_u8 *packet, int len)
+static int mungepacket(uae_u8 *packet, int len)
 {
        uae_u8 *data;
        uae_u16 type;
        int ret = 0;
 
+       if (!a2065_mungemac) {
+               return 0;
+       }
        if (len < 20)
                return 0;
        if (!memcmp(fakemac, realmac, 6))
                return len;
 #if DUMPPACKET
-       dumppacket (_T("pre:"), packet, len);
+       dumppacket(_T("pre:"), packet, len);
 #endif
        data = packet + 14;
        type = (packet[12] << 8) | packet[13];
        // switch destination mac
-       ret |= dofakemac (packet);
+       ret |= dofakemac(packet);
        // switch source mac
-       ret |= dofakemac (packet + 6);
+       ret |= dofakemac(packet + 6);
        if (type == 0x0806) { // ARP?
                if (((data[0] << 8) | data[1]) == 1 && data[4] == 6) { // Ethernet and LEN=6?
-                       ret |= dofakemac (data + 8); // sender
-                       ret |= dofakemac (data + 8 + 6 + 4); // target
+                       ret |= dofakemac(data + 8); // sender
+                       ret |= dofakemac(data + 8 + 6 + 4); // target
                }
        } else if (type == 0x0800) { // IPv4?
                int proto = data[9];
@@ -218,7 +222,7 @@ static int mungepacket (uae_u8 *packet, int len)
                        int dp = (data[2] << 8) | data[3];
                        int len2 = (data[4] << 8) | data[5];
                        if (sp == 67 || sp == 68 || dp == 67 || dp == 68)
-                               udpcrc |= dofakemac (data + 36); // DHCP CHADDR
+                               udpcrc |= dofakemac(data + 36); // DHCP CHADDR
                        if (udpcrc && (data[6] || data[7])) {
                                // fix UDP checksum
                                int i;
@@ -240,14 +244,14 @@ static int mungepacket (uae_u8 *packet, int len)
                                if (sum == 0)
                                        sum = 0xffff;
                                data[6] = sum >> 8;
-                               data[7] = sum >> 0;     
+                               data[7] = sum >> 0;
                                ret |= 1;
                        }
                        // this all just to translate single DHCP MAC..
                }
        }
 #if DUMPPACKET
-       dumppacket (_T("post:"), packet, len);
+       dumppacket(_T("post:"), packet, len);
 #endif
        return ret;
 }
@@ -259,10 +263,15 @@ static void rethink_a2065(void)
        bool wasintr = (csr[0] & CSR0_INTR) != 0;
        csr[0] &= ~CSR0_INTR;
        uae_u16 mask = csr[0];
-       if (AM79C960)
+       if (AM79C960) {
                mask &= (~csr[3]) & (0x4000 | 0x1000 | 0x800 | 0x400 | 0x200 | 0x100);
-       if (mask & (CSR0_BABL | CSR0_MISS | CSR0_MERR | CSR0_RINT | CSR0_TINT | CSR0_IDON))
+//             if ((csr[4] & 0x0008) && !(csr[4] & 0x0004)) { // TXSTRT
+//                     csr[0] |= CSR0_INTR;
+//             }
+       }
+       if (mask & (CSR0_BABL | CSR0_MISS | CSR0_MERR | CSR0_RINT | CSR0_TINT | CSR0_IDON)) {
                csr[0] |= CSR0_INTR;
+       }
        if ((csr[0] & (CSR0_INTR | CSR0_INEA)) == (CSR0_INTR | CSR0_INEA)) {
                safe_interrupt_set(IRQ_SOURCE_A2065, 0, false);
                if (log_a2065 > 1 && !wasintr) {
@@ -282,6 +291,12 @@ static int mcfilter (const uae_u8 *data)
        return 1; // just allow everything
 }
 
+static uae_u16 get_ram_word_volatile(uae_u32 offset)
+{
+       volatile const uae_u8 *ram = boardram;
+       return ((uae_u16)ram[offset & RAM_MASK] << 8) |
+               ram[(offset + 1) & RAM_MASK];
+}
 static uae_u8 get_ram_byte(uae_u32 offset)
 {
        return boardram[offset & RAM_MASK];
@@ -300,6 +315,15 @@ static void put_ram_word(uae_u32 offset, uae_u16 v)
        put_ram_byte(offset + 1, (uae_u8)v);
 }
 
+static void log_packet(const uae_u8 *p, int size)
+{
+       write_log(" \n");
+       for (int i = 0; i < size; i++) {
+               write_log("%02X", p[i]);
+       }
+       write_log("\n");
+}
+
 static void gotfunc2(void *devv, const uae_u8 *databuf, int len)
 {
        int i;
@@ -316,10 +340,11 @@ static void gotfunc2(void *devv, const uae_u8 *databuf, int len)
        srcmac = databuf + 6;
 
        if (log_a2065 > 1 && log_receive) {
-               write_log (_T("7790<!DST:%02X.%02X.%02X.%02X.%02X.%02X SRC:%02X.%02X.%02X.%02X.%02X.%02X E=%04X S=%d\n"),
+               write_log(_T("7790<!DST:%02X.%02X.%02X.%02X.%02X.%02X SRC:%02X.%02X.%02X.%02X.%02X.%02X E=%04X S=%d"),
                        dstmac[0], dstmac[1], dstmac[2], dstmac[3], dstmac[4], dstmac[5],
-                       srcmac[6], srcmac[7], srcmac[8], srcmac[9], srcmac[10], srcmac[11],
+                       srcmac[0], srcmac[1], srcmac[2], srcmac[3], srcmac[4], srcmac[5],
                        (databuf[12] << 8) | databuf[13], len);
+               log_packet(databuf, len);
        }
 
        if (!(csr[0] & CSR0_RXON)) // receiver off?
@@ -385,19 +410,21 @@ static void gotfunc2(void *devv, const uae_u8 *databuf, int len)
        dstmac = d;
        srcmac = d + 6;
        if (log_a2065 && log_receive) {
-               if (memcmp (dstmac, realmac, sizeof realmac) == 0) {
-                       write_log (_T("7990<-DST:%02X.%02X.%02X.%02X.%02X.%02X SRC:%02X.%02X.%02X.%02X.%02X.%02X E=%04X S=%d\n"),
+               if (memcmp(dstmac, realmac, sizeof realmac) == 0) {
+                       write_log(_T("7990<-DST:%02X.%02X.%02X.%02X.%02X.%02X SRC:%02X.%02X.%02X.%02X.%02X.%02X E=%04X S=%d"),
                                dstmac[0], dstmac[1], dstmac[2], dstmac[3], dstmac[4], dstmac[5],
-                               srcmac[6], srcmac[7], srcmac[8], srcmac[9], srcmac[10], srcmac[11],
+                               srcmac[0], srcmac[1], srcmac[2], srcmac[3], srcmac[4], srcmac[5],
                                (d[12] << 8) | d[13], len);
+                       log_packet(d, len);
                }
        }
-       if (mungepacket (d, len)) {
+       if (mungepacket(d, len)) {
                if (log_a2065 && log_receive) {
-                       write_log (_T("7990<*DST:%02X.%02X.%02X.%02X.%02X.%02X SRC:%02X.%02X.%02X.%02X.%02X.%02X E=%04X S=%d\n"),
+                       write_log(_T("7990<*DST:%02X.%02X.%02X.%02X.%02X.%02X SRC:%02X.%02X.%02X.%02X.%02X.%02X E=%04X S=%d"),
                                dstmac[0], dstmac[1], dstmac[2], dstmac[3], dstmac[4], dstmac[5],
-                               srcmac[6], srcmac[7], srcmac[8], srcmac[9], srcmac[10], srcmac[11],
+                               srcmac[0], srcmac[1], srcmac[2], srcmac[3], srcmac[4], srcmac[5],
                                (d[12] << 8) | d[13], len);
+                       log_packet(d, len);
                }
        }
 
@@ -511,8 +538,8 @@ static bool receive_space_available(int len)
        for (int i = 0; i < am_rdr_rlen; i++) {
                const int offset = (rdr_offset + i) % am_rdr_rlen;
                const uae_u32 off = am_rdr_rdra + offset * 8;
-               const uae_u16 rmd1 = get_ram_word(off + 2);
-               const uae_u16 rmd2 = get_ram_word(off + 4);
+               const uae_u16 rmd1 = get_ram_word_volatile(off + 2);
+               const uae_u16 rmd2 = get_ram_word_volatile(off + 4);
                if (!(rmd1 & RX_OWN))
                        return false;
                needed -= 65536 - rmd2;
@@ -590,24 +617,29 @@ static void do_transmit (void)
        uae_u32 addr, bufaddr;
        uae_u16 tmd0, tmd1, tmd2, tmd3;
        uae_u32 off;
+       bool found = false;
 
        err = 0;
        size = 0;
        outsize = 0;
 
-       if (!am_tdr_tlen)
+       if (!am_tdr_tlen) {
                return;
+       }
 
        tdr_offset %= am_tdr_tlen;
        bufaddr = am_tdr_tdra + tdr_offset * 8;
        off = bufaddr;
        tmd1 = get_ram_word(off + 2);
        if (!(tmd1 & TX_OWN) || !(tmd1 & TX_STP)) {
-               tdr_offset++;
+               if (!AM79C960 || !(csr[4] & 0x1000)) { // DPOLL
+                       tdr_offset++;
+               }
                return;
        }
-       if (!(tmd1 & TX_ENP) && log_a2065 > 0)
+       if (!(tmd1 & TX_ENP) && log_a2065 > 0) {
                write_log (_T("7990: chained transmit!?\n"));
+       }
 
        add_fcs = tmd1 & TX_ADD_FCS;
 
@@ -625,6 +657,7 @@ static void do_transmit (void)
                        tmd3 |= TX_BUFF | TX_UFLO;
                        tmd1 |= TX_ERR;
                        csr[0] &= ~CSR0_TXON;
+                       csr[0] &= ~CSR0_TDMD;
                        write_log (_T("7990: TRANSMIT OWN NOT SET\n"));
                        err = 1;
                } else {
@@ -643,6 +676,7 @@ static void do_transmit (void)
                                }       
                        }
                        tdr_offset++;
+                       found = true;
                }
                put_ram_word(off + 2, tmd1);
                put_ram_word(off + 6, tmd3);
@@ -664,36 +698,52 @@ static void do_transmit (void)
                if ((am_mode & MODE_DTCR) && !add_fcs)
                        outsize -= 4; // do not include checksum bytes
                if (log_a2065 && log_transmit) {
-                       write_log (_T("7990->DST:%02X.%02X.%02X.%02X.%02X.%02X SRC:%02X.%02X.%02X.%02X.%02X.%02X E=%04X S=%d ADDR=%04X\n"),
+                       write_log (_T("7990->DST:%02X.%02X.%02X.%02X.%02X.%02X SRC:%02X.%02X.%02X.%02X.%02X.%02X E=%04X S=%d ADDR=%04X"),
                                d[0], d[1], d[2], d[3], d[4], d[5],
                                d[6], d[7], d[8], d[9], d[10], d[11],
                                (d[12] << 8) | d[13], outsize, bufaddr);
+                       log_packet(d, outsize);
                }
                transmitlen = outsize;
                if (mungepacket (d, transmitlen)) {
                        if (log_a2065 && log_transmit) {
-                               write_log (_T("7990*>DST:%02X.%02X.%02X.%02X.%02X.%02X SRC:%02X.%02X.%02X.%02X.%02X.%02X E=%04X S=%d\n"),
+                               write_log (_T("7990*>DST:%02X.%02X.%02X.%02X.%02X.%02X SRC:%02X.%02X.%02X.%02X.%02X.%02X E=%04X S=%d"),
                                        d[0], d[1], d[2], d[3], d[4], d[5],
                                        d[6], d[7], d[8], d[9], d[10], d[11],
                                        (d[12] << 8) | d[13], outsize);
                        }
+                       log_packet(d, outsize);
                }
                ethernet_trigger (td, sysdata);
        }
+
        csr[0] |= CSR0_TINT;
+       if (!found) {
+               csr[0] &= ~CSR0_TDMD;
+       }
+       if (AM79C960) {
+               csr[4] |= 0x0008; // TXSTRT
+       }
        devices_rethink_all(rethink_a2065);
 }
 
-static void check_transmit(bool tdmd)
+static void check_transmit(void)
 {
        if (transmitlen > 0)
                return;
-       if (!(csr[0] & CSR0_TXON))
-               return;
-       if (AM79C960 && !tdmd && (csr[4] & 0x1000)) // DPOLL
+       if (!(csr[0] & CSR0_TXON)) {
+               csr[0] &= ~CSR0_TDMD;
                return;
+       }
+       if (AM79C960) {
+               // TDMD == 0 and DPOLL = 1: transmit disabled
+               if (!(csr[0] & CSR0_TDMD) && (csr[4] & 0x1000)) { // DPOLL
+                       return;
+               }
+       }
+
        transmitnow = 0;
-       do_transmit ();
+       do_transmit();
 }
 
 static void a2065_hsync_handler(void)
@@ -703,8 +753,8 @@ static void a2065_hsync_handler(void)
        receive_queue_drain();
 
        cnt--;
-       if (cnt < 0 || transmitnow) {
-               check_transmit(false);
+       if (cnt < 0 || transmitnow || (csr[0] & CSR0_TDMD)) {
+               check_transmit();
                cnt = 15;
        }
 }
@@ -869,18 +919,12 @@ static void chip_wput (uaecptr addr, uae_u16 v)
                                        write_log(_T("7990: INIT. %04X -> %04X -> %04X\n"), oreg, v, csr[0]);
                        }
 
-                       if ((csr[0] & CSR0_STRT)) {
-                               if (am_initialized) {
-                                       if (csr[0] & CSR0_TDMD)
-                                               check_transmit(true);
-                               } else if (AM79C960) {
+                       if (csr[0] & CSR0_STRT) {
+                               if (AM79C960 && !am_initialized) {
                                        chip_init2();
                                        am_initialized = 1;
-                                       if (csr[0] & CSR0_TDMD)
-                                               check_transmit(true);
                                }
                        }
-                       csr[0] &= ~CSR0_TDMD;
 
                        devices_rethink_all(rethink_a2065);
                        break;
@@ -914,7 +958,15 @@ static void chip_wput (uaecptr addr, uae_u16 v)
 
                        // Am79C960 extra
 
-               // logical address filter
+               case 4: // test and features
+                       v &= ~(0x80 | 0x40); // Reserved
+                       // JAB. TXSTRT, RCVCCO, MFCOM cleared when writing 1.
+                       csr[4] &= ~(v & (0x0002 | 0x0008 | 0x0020 | 0x0200));
+                       csr[4] &= 0x0002 | 0x0008 | 0x0020 | 0x0200;
+                       csr[4] |= v & ~(0x0002 | 0x0008 | 0x0020 | 0x0200);
+                       v = csr[4];
+                       break;
+                       // logical address filter
                case 8:
                        am_ladrf &= 0x0000ffffffffffff;
                        am_ladrf |= (uae_u64)v << 48;
@@ -1186,14 +1238,11 @@ static bool a2065_config (struct autoconfig_info *aci)
        if (!receive_buffer) {
                receive_buffer = xcalloc(uae_u8, MAX_PACKET_SIZE * RECEIVE_QUEUE_SIZE);
        }
-       receive_queue_clear();
 
        if (!aci) {
                return false;
        }
 
-       device_add_reset(a2065_reset);
-
        if (aci->postinit) {
                configured = expamem_board_pointer >> 16;
                return true;
@@ -1262,15 +1311,20 @@ static bool a2065_config (struct autoconfig_info *aci)
        aci->addrbank = &a2065_bank;
        aci->autoconfig_automatic = true;
        aci->autoconfigp = aci->autoconfig_bytes;
+
        if (!aci->doinit)
                return true;
 
+       configured = 0;
+
        alloc_expansion_bank(&a2065_bank, aci);
        boardram = a2065_bank.baseaddr + RAM_OFFSET;
 
+       receive_queue_clear();
        device_add_hsync(a2065_hsync_handler);
        device_add_rethink(rethink_a2065);
        device_add_exit(a2065_free, NULL);
+       device_add_reset(a2065_reset);
 
        return true;
 }