# ip65 technical reference

# File : drivers/lan91c96.s

     Ethernet driver for SMC LAN91C96 chip

## functions

function | description
--- | ---
eth_init |
    initialize the ethernet adaptor
    inputs: none
    outputs: carry flag is set if there was an error, clear otherwise
eth_rx |
    receive a packet
    inputs: none
    outputs:
     if there was an error receiving the packet (or no packet was ready) then carry flag is set
     if packet was received correctly then carry flag is clear,
     eth_inp contains the received packet,
     and eth_inp_len contains the length of the packet
eth_tx |
     send a packet
    inputs:
     eth_outp: packet to send
     eth_outp_len: length of packet to send
    outputs:
     if there was an error sending the packet then carry flag is set
     otherwise carry flag is cleared

## constants

constants | description | value
--- | --- | ---
eth_driver_io_base |  | fixlan01+1
eth_driver_name |  | "LANceGS (91C96)"

## implementation

    ; Ethernet driver for SMC LAN91C96 chip
    ;

    .ifndef KPR_API_VERSION_NUMBER
      .define EQU     =
      .include "../inc/kipper_constants.i"
    .endif

    .include "../inc/common.i"

      .export eth_init
      .export eth_rx
      .export eth_tx
      .export eth_driver_name
      .export eth_driver_io_base
      .import eth_inp
      .import eth_inp_len
      .import eth_outp
      .import eth_outp_len

      .import cfg_mac

    ; LANceGS hardware addresses
    ethbsr    := $c00E  ; Bank select register             R/W (2B)

    ; Register bank 0
    ethtcr    := $c000  ; Transmission control register    R/W (2B)
    ethephsr  := $c002  ; EPH status register              R/O (2B)
    ethrcr    := $c004  ; Receive control register         R/W (2B)
    ethecr    := $c006  ; Counter register                 R/O (2B)
    ethmir    := $c008  ; Memory information register      R/O (2B)
    ethmcr    := $c00A  ; Memory Config. reg.    +0 R/W +1 R/O (2B)

    ; Register bank 1
    ethcr    := $c000  ; Configuration register           R/W (2B)
    ethbar    := $c002  ; Base address register            R/W (2B)
    ethiar    := $c004  ; Individual address register      R/W (6B)
    ethgpr    := $c00A  ; General address register         R/W (2B)
    ethctr    := $c00C  ; Control register                 R/W (2B)

    ; Register bank 2
    ethmmucr  := $c000  ; MMU command register             W/O (1B)
    ethautotx  := $c001  ; AUTO TX start register           R/W (1B)
    ethpnr    := $c002  ; Packet number register           R/W (1B)
    etharr    := $c003  ; Allocation result register       R/O (1B)
    ethfifo    := $c004  ; FIFO ports register              R/O (2B)
    ethptr    := $c006  ; Pointer register                 R/W (2B)
    ethdata    := $c008  ; Data register                    R/W (4B)
    ethist    := $c00C  ; Interrupt status register        R/O (1B)
    ethack    := $c00C  ; Interrupt acknowledge register   W/O (1B)
    ethmsk    := $c00D  ; Interrupt mask register          R/W (1B)

    ; Register bank 3
    ethmt    := $c000  ; Multicast table                  R/W (8B)
    ethmgmt    := $c008  ; Management interface             R/W (2B)
    ethrev    := $c00A  ; Revision register                R/W (2B)
    ethercv    := $c00C  ; Early RCV register               R/W (2B)

      .segment "IP65ZP" : zeropage

    eth_packet:  .res 2

      .data

    ;initialize the ethernet adaptor
    ;inputs: none
    ;outputs: carry flag is set if there was an error, clear otherwise
    eth_init:
      jsr lan_self_modify
      lda #$01
    fixlan00:
      sta ethbsr    ; Select register bank 1
    fixlan01:
      lda ethcr    ; Read first four bytes - $31, $20, $67, $18
      cmp #$31
      bne lanerror
    fixlan03:
      lda ethbar
      cmp #$67
      bne lanerror
    fixlan04:
      lda ethbar+1
      cmp #$18
      bne lanerror
      ; we have the magic signature

      ; Reset ETH card
      lda #$00    ; Bank 0
    fixlan05:
      sta ethbsr
      lda #%10000000    ; Software reset
    fixlan06:
      sta ethrcr+1

      ldy #$00
    fixlan07:
      sty ethrcr
    fixlan08:
      sty ethrcr+1

      ; Delay
    :  cmp ($FF,x)    ; 6 cycles
      cmp ($FF,x)    ; 6 cycles
      iny      ; 2 cycles
      bne :-      ; 3 cycles
            ; 17 * 256 = 4352 -> 4,4 ms

      ; Enable transmit and receive
      lda #%10000001    ; Enable transmit TXENA, PAD_EN
      ldx #%00000011    ; Enable receive, strip CRC ???
    fixlan09:
      sta ethtcr
    fixlan10:
      stx ethrcr+1

      lda #$01    ; Bank 1
    fixlan11:
      sta ethbsr

    fixlan12:
      lda ethcr+1
      ora #%00010000    ; No wait (IOCHRDY)
    fixlan13:
      sta ethcr+1

      lda #%00001001    ; Auto release
    fixlan14:
      sta ethctr+1

      ; Set MAC address
      lda cfg_mac
      ldx cfg_mac + 1
    fixlan15:
      sta ethiar
    fixlan16:
      stx ethiar + 1
      lda cfg_mac + 2
      ldx cfg_mac + 3
    fixlan17:
      sta ethiar + 2
    fixlan18:
      stx ethiar + 3
      lda cfg_mac + 4
      ldx cfg_mac + 5
    fixlan19:
      sta ethiar + 4
    fixlan20:
      stx ethiar + 5

      ; Set interrupt mask
      lda #$02    ; Bank 2
    fixlan21:
      sta ethbsr

      lda #%00000000    ; No interrupts
    fixlan22:
      sta ethmsk
      clc
      rts

    lanerror:
      sec
      rts

    ;receive a packet
    ;inputs: none
    ;outputs:
    ; if there was an error receiving the packet (or no packet was ready) then carry flag is set
    ; if packet was received correctly then carry flag is clear,
    ; eth_inp contains the received packet,
    ; and eth_inp_len contains the length of the packet
    eth_rx:
    fixlan38:
      lda ethist
      and #%00000001  ; Check receive interrupt
      bne :+
      sec    ; No packet available
      rts

    :  lda #$00
      ldx #%11100000  ; Receive, Auto Increment, Read
    fixlan39:
      sta ethptr
    fixlan40:
      stx ethptr + 1

      ; Last word contains 'last data byte' and $60 or 'fill byte' and $40
    fixlan41:
      lda ethdata  ; Status word
    fixlan42:
      lda ethdata  ; Only need high byte

      ; Move ODDFRM bit into carry:
      ; - Even packet length -> carry clear -> subtract 6 bytes
      ; - Odd packet length  -> carry set   -> subtract 5 bytes
      lsr
      lsr
      lsr
      lsr
      lsr

      ; The packet contains 3 extra words
    fixlan43:
      lda ethdata    ; Total number of bytes
      sbc #$05    ; Actually 5 or 6 depending on carry
      sta eth_inp_len
    fixlan44:
      lda ethdata
      sbc #$00
      sta eth_inp_len+1

      ; Read bytes into buffer
      lda #eth_inp
      sta eth_packet
      stx eth_packet+1
        ldx eth_inp_len+1
        ldy #$00
    lanread:
    fixlan46:
      lda ethdata
      sta (eth_packet),y
      iny
      bne :+
      inc eth_packet+1
    :  cpy eth_inp_len
      bne lanread
      dex
      bpl lanread

      ; Remove and release RX packet from the FIFO
      lda #%10000000
    fixlan47:
      sta ethmmucr

      clc
      rts

    ; send a packet
    ;inputs:
    ; eth_outp: packet to send
    ; eth_outp_len: length of packet to send
    ;outputs:
    ; if there was an error sending the packet then carry flag is set
    ; otherwise carry flag is cleared
    eth_tx:
      lda eth_outp_len + 1  ;
      ora #%00100000
    fixlan23:
      sta ethmmucr  ; Allocate memory for transmission
    fixlan24:
      lda ethist
      and #%00001000  ; Allocation interrupt
      bne :+
      sec
      rts    ; Not able to allocate; bail

    :  lda #%00001000
    fixlan25:
      sta ethack  ; Acknowledge interrupt

    fixlan26:
      lda etharr
    fixlan27:
      sta ethpnr  ; Set packet number

      lda #$00
      ldx #%01000000  ; Auto increment
    fixlan28:
      sta ethptr
    fixlan29:
      stx ethptr + 1

      lda #$00  ; Status written by CSMA
    fixlan30:
      sta ethdata
    fixlan31:
      sta ethdata

      lda eth_outp_len
      eor #$01
      lsr
      lda eth_outp_len
      adc #$05  ; Actually will be 5 or 6 depending on carry
    fixlan32:
      sta ethdata
      lda eth_outp_len + 1
      adc #$00
    fixlan33:
      sta ethdata

      lda #eth_outp
      sta eth_packet
      stx eth_packet + 1
      ldx eth_outp_len + 1
      ldy #$00
    lanwrite:
      lda (eth_packet),y
    fixlan34:
      sta ethdata
      iny
      bne :+
      inc eth_packet + 1
    :  cpy eth_outp_len
      bne lanwrite
      dex
      bpl lanwrite

      lda eth_outp_len  ; Odd packet length?
      lsr
      bcc :+

      lda #%001000000  ; Yes, Odd
      bne fixlan36  ; Always

    :  lda #$00  ; No
    fixlan35:
      sta ethdata  ; Fill byte
    fixlan36:
      sta ethdata  ; Control byte
      lda #%11000000  ; Enqueue packet - transmit
    fixlan37:
      sta ethmmucr

      clc
      rts

    ;
    ; lan_self_modify - make all entry points variable so we can move the
    ;   LANceGS card around in the Apple
    ;
    lan_self_modify:
      lda #$C0  ; FIXME - hardcoded to slot 4
      clc    ; We'll be adding later, so clear carry
      ; Make the accumulator contain slot number plus $80
      ;   i.e. Slot 1 = $90
      ;   i.e. Slot 2 = $A0
      ;   i.e. Slot 3 = $B0
      ;   i.e. Slot 4 = $C0
      ;   i.e. Slot 5 = $D0
      ;   i.e. Slot 6 = $E0
      ;   i.e. Slot 7 = $F0
    ; $C0s0: Save off all ethtcr, ethcr, ethmmucr, and ethmt mods
      sta fixlan01 + 1
      sta fixlan09 + 1
      sta fixlan23 + 1
      sta fixlan37 + 1
    ;  sta fixlan45 + 1  ; Removed
      sta fixlan47 + 1

    ; $C0s1: Save off all ethtcr+1, ethcr+1, ethmmucr+1, and ethmt+1 mods
      adc #$01
    ;  sta fixlan02 + 1  ; Removed
      sta fixlan12 + 1
      sta fixlan13 + 1

    ; $C0s2: Save off all ethephsr, ethbar, and ethpnr mods
      adc #$01
      sta fixlan03 + 1
      sta fixlan27 + 1

    ; $C0s3: Save off all ethephsr+1, ethbar+1, ethpnr+1, and etharr mods
      adc #$01
      sta fixlan04 + 1
      sta fixlan26 + 1

    ; $C0s4: Save off all ethrcr, ethiar, and ethfifo mods
      adc #$01
      sta fixlan07 + 1
      sta fixlan15 + 1

    ; $C0s5: Save off all ethrcr+1, ethiar+1, and ethfifo+1 mods
      adc #$01
      sta fixlan06 + 1
      sta fixlan08 + 1
      sta fixlan10 + 1
      sta fixlan16 + 1

    ; $C0s6: Save off all ethecr, ethptr, and ethiar+2 mods
      adc #$01
      sta fixlan17 + 1
      sta fixlan28 + 1
      sta fixlan39 + 1

    ; $C0s7: Save off all ethecr+1, ethptr+1, and ethiar+3 mods
      adc #$01
      sta fixlan18 + 1
      sta fixlan29 + 1
      sta fixlan40 + 1

    ; $C0s8: Save off all ethmir, ethdata, ethmgmt, and ethiar+4 mods
      adc #$01
      sta fixlan19 + 1
      sta fixlan30 + 1
      sta fixlan31 + 1
      sta fixlan32 + 1
      sta fixlan33 + 1
      sta fixlan34 + 1
      sta fixlan35 + 1
      sta fixlan36 + 1
      sta fixlan41 + 1
      sta fixlan42 + 1
      sta fixlan43 + 1
      sta fixlan44 + 1
      sta fixlan46 + 1

    ; $C0s9: Save off all ethmir+1, ethdata+1, ethmgmt+1, and ethiar+5 mods
      adc #$01
      sta fixlan20 + 1

    ; $C0sA: Save off all ethmcr, ethgpr, and ethrev mods
    ; $C0sB: Save off all ethmcr+1, ethgpr+1, and ethrev+1 mods
      ; None

    ; $C0sC: Save off all ethctr, ethist, ethack, and ethercv mods
      adc #$03  ; Because there were no a or b mods
      sta fixlan24 + 1
      sta fixlan25 + 1
      sta fixlan38 + 1

    ; $C0sD: Save off all ethmsk, ethctr+1 mods
      adc #$01
      sta fixlan14 + 1
      sta fixlan22 + 1

    ; $C0sE: Save off all ethbsr mods
      adc #$01
      sta fixlan00 + 1
      sta fixlan05 + 1
      sta fixlan11 + 1
      sta fixlan21 + 1

      rts

    .rodata
    eth_driver_name:
      .asciiz "LANceGS (91C96)"
    eth_driver_io_base=fixlan01+1

    ; The contents of this file are subject to the Mozilla Public License
    ; Version 1.1 (the "License"); you may not use this file except in
    ; compliance with the License. You may obtain a copy of the License at
    ; http://www.mozilla.org/MPL/
    ;
    ; Software distributed under the License is distributed on an "AS IS"
    ; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    ; License for the specific language governing rights and limitations
    ; under the License.
    ;
    ; The Original Code is ip65.
    ;
    ; The Initial Developer of the Original Code is David Schmidt
    ; Portions created by the Initial Developer is Copyright (C) 2011
    ; All Rights Reserved.
    ; -- LICENSE END --