Search code examples
network-programmingx86nasmosdevreal-mode

How to list network devices in NASM (custom OS)


I have a custom, DOS-like OS built in NASM completely (no C code). It is very modest (it does have a FAT file system, few apps, is in real mode, etc.) I want to write a command that will list all the network devices (network cards) that are currently connected.

My assumptions go like this: I will need to write a driver for the network card (I'd put it manually inside kernel for simplicity, so dynamic loading would NOT exist), but it would be enough for that driver to just provide the name of the card, the network card wouldn't actually need to work. How do I tell the OS to connect that function to precisely that one network card? This is what I'm in the blue about, I have no idea how the OS usually matches a piece of hardware to code (its driver(s)).


Solution

  • Since it appears from your comments that you have Dosbox supporting an NE2000 card then the code below should detect the presence of an NE2000 card (A port base of 0x300 is assumed). The code is a DOS COM program I wrote and should be compilable with a command like nasm ne2kchk.asm -fbin -o ne2kchk.com

    NS_DATAPORT    EQU    0x10    ; NatSemi-defined port window offset.
    NE_DATAPORT    EQU    0x10    ; NatSemi-defined port window offset.
    NS_RESET       EQU    0x1f    ; Issue a read to reset, a write to clear.
    
    NE1SM_START_PG EQU    0x20    ; First page of TX buffer
    NE1SM_STOP_PG  EQU    0x40    ; Last page +1 of RX ring
    NESM_START_PG  EQU    0x40    ; First page of TX buffer
    NESM_STOP_PG   EQU    0x80    ; Last page +1 of RX ring
    
    E8390_CMD      EQU    0x00    ; The command register (for all pages)
    E8390_STOP     EQU    0x01    ; Stop and reset the chip
    E8390_START    EQU    0x02    ; Start the chip, clear reset
    E8390_RREAD    EQU    0x08    ; Remote read
    E8390_NODMA    EQU    0x20    ; Remote DMA
    E8390_PAGE0    EQU    0x00    ; Select page chip registers
    E8390_PAGE1    EQU    0x40    ; using the two high-order bits
    E8390_PAGE2    EQU    0x80
    E8390_PAGE3    EQU    0xC0    ; Page 3 is invalid on the real 8390.
    
    E8390_RXOFF    EQU    0x20    ; EN0_RXCR: Accept no packets
    E8390_TXOFF    EQU    0x02    ; EN0_TXCR: Transmitter off
    
                   ; Page 0 register offsets.
    EN0_CLDALO     EQU    0x01    ; Low byte of current local dma addr  RD
    EN0_STARTPG    EQU    0x01    ; Starting page of ring bfr WR
    EN0_CLDAHI     EQU    0x02    ; High byte of current local dma addr     RD
    EN0_STOPPG     EQU    0x02    ; Ending page +1 of ring bfr WR
    EN0_BOUNDARY   EQU    0x03    ; Boundary page of ring bfr RD WR
    EN0_TSR        EQU    0x04    ; Transmit status reg RD
    EN0_TPSR       EQU    0x04    ; Transmit starting page WR
    EN0_NCR        EQU    0x05    ; Number of collision reg RD
    EN0_TCNTLO     EQU    0x05    ; Low     byte of tx byte count WR
    EN0_FIFO       EQU    0x06    ; FIFO RD
    EN0_TCNTHI     EQU    0x06    ; High byte of tx byte count WR
    EN0_ISR        EQU    0x07    ; Interrupt status reg RD WR
    EN0_CRDALO     EQU    0x08    ; low byte of current remote dma address RD
    EN0_RSARLO     EQU    0x08    ; Remote start address reg 0
    EN0_CRDAHI     EQU    0x09    ; high byte, current remote dma address RD
    EN0_RSARHI     EQU    0x09    ; Remote start address reg 1
    EN0_RCNTLO     EQU    0x0a    ; Remote byte count reg WR
    EN0_RCNTHI     EQU    0x0b    ; Remote byte count reg WR
    EN0_RSR        EQU    0x0c    ; rx status reg RD
    EN0_RXCR       EQU    0x0c    ; RX configuration reg WR
    EN0_TXCR       EQU    0x0d    ; TX configuration reg WR
    EN0_COUNTER0   EQU    0x0d    ; Rcv alignment error counter RD
    EN0_DCFG       EQU    0x0e    ; Data configuration reg WR
    EN0_COUNTER1   EQU    0x0e    ; Rcv CRC error counter RD
    EN0_IMR        EQU    0x0f    ; Interrupt mask reg WR
    EN0_COUNTER2   EQU    0x0f    ; Rcv missed frame error counter RD
    
    PORT_BASE      EQU    0x300   ; Default base port
    
    [BITS 16]
    
        org 0x100
    
    section .text
    
    start:
        push   cs
        pop    ds
    
        ; Probe for NE2000 card
    
        ; Try non destructive test first
        mov    dx, PORT_BASE+E8390_CMD
        in     al, dx
        cmp    al, 0xff
        jz     .s_notfound
    
        ; Attempt potentially destuctive tests
        mov    al, E8390_NODMA | E8390_PAGE1 | E8390_STOP
        mov    dx, PORT_BASE+E8390_CMD
        out    dx, al    ; Receive alignment error counter
        mov    dx, PORT_BASE+EN0_COUNTER0
        in     al, dx
        mov    cl, al    ; Save to REGD (CL)
        mov    al, 0xff
        out    dx, al
        mov    al, E8390_NODMA | E8390_PAGE0
        mov    dx, PORT_BASE+E8390_CMD
        out    dx, al
        mov    dx, PORT_BASE+EN0_COUNTER0
        in     al, dx    ; Clear the counter by reading.
        test   al, al
        jz     .s_found  ; If al is clear then card was found
    
        ; Card not found
    .s_notfound:
        xchg   al, cl    ; Temporarily save al to avoid clobber
        out    dx, al
        mov    ah, 0x09
        mov    dx, notfound_str
        int    0x21
        xchg   al, cl    ; Restore al. al = error value to return
        jmp    .s_exit
    
        ; Card found
    .s_found:
        mov    ah, 0x09
        mov    dx, found_str
        int    0x21
        xor    al, al    ; Clear the error code
    
        ; exit with al = errcode
    .s_exit:
        mov    ah, 0x4C
        int    0x21
    
    notfound_str db "NE2000 not found", 0x0a, 0x0d, "$"
    found_str    db "NE2000 found", 0x0a, 0x0d, "$"
    

    The code above is an adaptation of "C" code that I found in the Debian nictool code available here . It can be found in the file ne2k-diags.c. It seems more information (datasheets) are available for the rtl8019 which is a clone of the NE2000. The 8390NIC that is at the heart of these devices is documented here. The 8390 documentation discusses how to send and receive data. An excerpt of the "C" code that I based mine on was:

    printf("Checking the ethercard at %#3x.\n", port_base);
    {   int regd;
        long ioaddr = port_base;
    
        outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
        regd = inb_p(ioaddr + 0x0d);
        printk("  Receive alignment error counter (%#lx) is %2.2x\n",
               ioaddr + 0x0d, regd);
        outb_p(0xff, ioaddr + 0x0d);
        outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
        inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
        if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
            outb(regd, ioaddr + 0x0d);  /* Restore the old values. */
            printk("  Failed initial NE2000 probe, value %2.2x.\n",
                   inb(ioaddr + EN0_COUNTER0));
        } else
            printk("  Passed initial NE2000 probe, value %2.2x.\n",
                   inb(ioaddr + EN0_COUNTER0));
    
    }
    

    The code above is the initial probe but there is more "C" code in the same file that tries to detect some of the specific variants and query the card signature.