| /* |
| * uIP lan91c96 (smc9194) driver |
| * Based on cs8900a driver, copyrighted (c) 2001, by Adam Dunkels |
| * Copyright (c) 2003, Josef Soucek |
| * All rights reserved. |
| * |
| * Ethernet card for Commodore 64, based on lan91c96 chip |
| * is a device created by IDE64 Project team. |
| * More information: http://ide64.come.to |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote |
| * products derived from this software without specific prior |
| * written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
| * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
| * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * $Id: lan91c96.c,v 1.1 2006/04/17 15:02:40 kthacker Exp $ |
| * |
| */ |
| |
| #define UIP_ETHADDR0 0x00 |
| #define UIP_ETHADDR1 0x0d |
| #define UIP_ETHADDR2 0x60 |
| #define UIP_ETHADDR3 0x80 |
| #define UIP_ETHADDR4 0x3d |
| #define UIP_ETHADDR5 0xb9 |
| |
| #include "lan91c96.h" |
| #include "uip.h" |
| #include "uip_arp.h" |
| |
| #include <stdio.h> |
| |
| // #define DEBUG |
| |
| #define ETHBASE 0xde10 |
| |
| #define ETHBSR ETHBASE+0x0e //Bank select register R/W (2B) |
| |
| /* Register bank 0 */ |
| |
| #define ETHTCR ETHBASE //Transmition control register R/W (2B) |
| #define ETHEPHSR ETHBASE+2 //EPH status register R/O (2B) |
| #define ETHRCR ETHBASE+4 //Receive control register R/W (2B) |
| #define ETHECR ETHBASE+6 //Counter register R/O (2B) |
| #define ETHMIR (ETHBASE+8) //Memory information register R/O (2B) |
| #define ETHMCR ETHBASE+0x0a //Memory Config. reg. +0 R/W +1 R/O (2B) |
| |
| /* Register bank 1 */ |
| |
| #define ETHCR ETHBASE //Configuration register R/W (2B) |
| #define ETHBAR ETHBASE+2 //Base address register R/W (2B) |
| #define ETHIAR ETHBASE+4 //Individual address register R/W (6B) |
| #define ETHGPR ETHBASE+0x0a //General address register R/W (2B) |
| #define ETHCTR ETHBASE+0x0c //Control register R/W (2B) |
| |
| /* Register bank 2 */ |
| |
| #define ETHMMUCR ETHBASE //MMU command register W/O (1B) |
| #define ETHAUTOTX ETHBASE+1 //AUTO TX start register R/W (1B) |
| #define ETHPNR ETHBASE+2 //Packet number register R/W (1B) |
| #define ETHARR ETHBASE+3 //Allocation result register R/O (1B) |
| #define ETHFIFO ETHBASE+4 //FIFO ports register R/O (2B) |
| #define ETHPTR ETHBASE+6 //Pointer register R/W (2B) |
| #define ETHDATA ETHBASE+8 //Data register R/W (4B) |
| #define ETHIST (ETHBASE+0x0c) //Interrupt status register R/O (1B) |
| #define ETHACK ETHBASE+0x0c //Interrupt acknowledge register W/O (1B) |
| #define ETHMSK ETHBASE+0x0d //Interrupt mask register R/W (1B) |
| |
| /* Register bank 3 */ |
| |
| #define ETHMT ETHBASE //Multicast table R/W (8B) |
| #define ETHMGMT ETHBASE+8 //Management interface R/W (2B) |
| #define ETHREV ETHBASE+0x0a //Revision register R/W (2B) |
| #define ETHERCV ETHBASE+0x0c //Early RCV register R/W (2B) |
| |
| #define BANK(num) asm("lda #%b",num); asm("sta %w",ETHBSR); |
| |
| #ifdef DEBUG |
| static void print_packet(u8_t *, u16_t); |
| #endif |
| |
| static u8_t packet_status; |
| static u16_t packet_length; |
| |
| extern u16_t uip_len; |
| |
| |
| #pragma optimize(push, off) |
| void lan91c96_init(void) |
| { |
| /* Check if high byte is 0x33 */ |
| asm("lda %w", ETHBSR+1); |
| asm("cmp #$33"); |
| asm("beq @L1"); |
| |
| asm("inc $d021"); // Error |
| |
| asm("@L1:"); |
| |
| /* Reset ETH card */ |
| BANK(0); |
| asm("lda #%%10000000"); //Software reset |
| asm("sta %w", ETHRCR+1); |
| |
| asm("lda #0"); |
| asm("sta %w", ETHRCR); |
| asm("sta %w", ETHRCR+1); |
| |
| /* delay */ |
| asm("ldx #0"); |
| asm("@L2:"); |
| asm("cmp ($ff,x)"); //6 cycles |
| asm("cmp ($ff,x)"); //6 cycles |
| asm("dex"); //2 cycles |
| asm("bne @L2"); //3 cycles |
| //17*256=4352 => 4,4 ms |
| |
| /* Enable transmit and receive */ |
| asm("lda #%%10000001"); //Enable transmit TXENA, PAD_EN |
| asm("sta %w", ETHTCR); |
| asm("lda #%%00000011"); //Enable receive, strip CRC ??? |
| asm("sta %w", ETHRCR+1); |
| |
| BANK(1); |
| asm("lda %w", ETHCR+1); |
| asm("ora #%%00010000"); //No wait (IOCHRDY) |
| asm("sta %w", ETHCR+1); |
| |
| asm("lda #%%00001001"); //Auto release |
| asm("sta %w", ETHCTR+1); |
| |
| /* Set MAC address */ |
| asm("lda #%b", (unsigned)UIP_ETHADDR0); |
| asm("sta %w", ETHIAR); |
| asm("lda #%b", (unsigned)UIP_ETHADDR1); |
| asm("sta %w", ETHIAR+1); |
| asm("lda #%b", (unsigned)UIP_ETHADDR2); |
| asm("sta %w", ETHIAR+2); |
| asm("lda #%b", (unsigned)UIP_ETHADDR3); |
| asm("sta %w", ETHIAR+3); |
| asm("lda #%b", (unsigned)UIP_ETHADDR4); |
| asm("sta %w", ETHIAR+4); |
| asm("lda #%b", (unsigned)UIP_ETHADDR5); |
| asm("sta %w", ETHIAR+5); |
| |
| BANK(2); |
| asm("lda #%%00001111"); //RCV INT, ALLOC INT, TX INT, TX EMPTY |
| asm("sta %w", ETHMSK); |
| } |
| #pragma optimize(pop) |
| |
| |
| #pragma optimize(push, off) |
| #if UIP_BUFSIZE > 255 |
| u16_t lan91c96_poll(void) |
| #else |
| u8_t lan91c96_poll(void) |
| #endif |
| { |
| // ####### |
| // BANK(0); |
| // printf("RAM: %d ", ((*(unsigned int *)ETHMIR) & 0xff00)); |
| // BANK(2); |
| // ####### |
| |
| asm("lda %w", ETHIST); |
| asm("and #%%00000001"); //RCV INT |
| asm("bne @L1"); |
| |
| /* No packet available */ |
| return 0; |
| |
| asm("@L1:"); |
| |
| #ifdef DEBUG |
| printf("RCV: IRQ\n"); |
| #endif |
| |
| asm("lda #0"); |
| asm("sta %w", ETHPTR); |
| asm("lda #%%11100000"); //RCV,AUTO INCR.,READ |
| asm("sta %w", ETHPTR+1); |
| |
| asm("lda %w", ETHDATA); //Status word |
| asm("lda %w", ETHDATA); |
| asm("sta _packet_status"); //High byte only |
| |
| asm("lda %w", ETHDATA); //Total number of bytes |
| asm("sta _packet_length"); |
| asm("lda %w", ETHDATA); |
| asm("sta _packet_length+1"); |
| |
| /* Last word contain 'last data byte' and 0x60 */ |
| /* or 'fill byte' and 0x40 */ |
| |
| packet_length -= 6; //The packet contains 3 extra words |
| |
| asm("lda _packet_status"); |
| asm("and #$10"); |
| asm("beq @L2"); |
| |
| packet_length++; |
| |
| #ifdef DEBUG |
| printf("RCV: odd number of bytes\n"); |
| #endif |
| |
| asm("@L2:"); |
| |
| #ifdef DEBUG |
| printf("RCV: L:%d ST-HIGH:0x%02x ",packet_length,packet_status); |
| #endif |
| |
| if (packet_length > UIP_BUFSIZE) |
| { |
| /* Remove and release RX packet from FIFO*/ |
| asm("lda #%%10000000"); |
| asm("sta %w", ETHMMUCR); |
| |
| #ifdef DEBUG |
| printf("RCV: UIP_BUFSIZE exceeded - packet dropped!\n"); |
| #endif |
| |
| return 0; |
| } |
| |
| asm("lda #<_uip_buf"); |
| asm("sta ptr1"); |
| asm("lda #>_uip_buf"); |
| asm("sta ptr1+1"); |
| |
| asm("ldy #0"); |
| asm("ldx _packet_length+1"); |
| asm("beq @RE1"); //packet_length < 256 |
| |
| asm("@RL1:"); |
| asm("lda %w", ETHDATA); |
| asm("sta (ptr1),y"); |
| asm("iny"); |
| asm("bne @RL1"); |
| asm("inc ptr1+1"); |
| asm("dex"); |
| asm("bne @RL1"); |
| |
| asm("@RE1:"); |
| asm("lda %w", ETHDATA); |
| asm("sta (ptr1),y"); |
| asm("iny"); |
| asm("cpy _packet_length"); |
| asm("bne @RE1"); |
| |
| /* Remove and release RX packet from FIFO*/ |
| asm("lda #%%10000000"); |
| asm("sta %w", ETHMMUCR); |
| |
| #ifdef DEBUG |
| // print_packet(uip_buf, packet_length); |
| #endif |
| |
| return packet_length; |
| } |
| #pragma optimize(pop) |
| |
| /* First 40+14 (IP nad TCP header) is send from uip_buf */ |
| /* than data from uip_appdata */ |
| |
| #pragma optimize(push, off) |
| void lan91c96_send(void) |
| { |
| #ifdef DEBUG |
| printf("SND: send packet\n"); |
| #endif |
| |
| #if UIP_BUFSIZE > 255 |
| asm("lda _uip_len+1"); |
| #else |
| asm("lda #0"); |
| #endif |
| asm("ora #%%00100000"); //Allocate memory for TX |
| asm("sta %w", ETHMMUCR); |
| |
| asm("ldx #8"); //Wait... |
| asm("@L1:"); //Wait for allocation ready |
| asm("lda %w", ETHIST); |
| asm("and #%%00001000"); //ALLOC INT |
| asm("bne @X1"); |
| asm("dex"); |
| asm("bne @L1"); |
| |
| #ifdef DEBUG |
| printf("SND: ERR: memory alloc timeout\n"); |
| #endif |
| |
| return; |
| |
| asm("@X1:"); |
| #ifdef DEBUG |
| printf("SND: packet memory allocated\n"); |
| #endif |
| |
| asm("lda #%%00001000"); //Acknowledge int, is it necessary ??? |
| asm("sta %w", ETHACK); |
| |
| asm("lda %w", ETHARR); |
| asm("sta %w", ETHPNR); //Set packet address |
| |
| asm("lda #0"); |
| asm("sta %w", ETHPTR); |
| asm("lda #%%01000000"); //AUTO INCR. |
| asm("sta %w", ETHPTR+1); |
| |
| #ifdef DEBUG |
| printf("SND: L:%d ", uip_len); |
| #endif |
| |
| asm("lda #0"); //Status written by CSMA |
| asm("sta %w", ETHDATA); |
| asm("sta %w", ETHDATA); |
| |
| asm("lda _uip_len"); |
| asm("and #$01"); |
| asm("beq @SD1"); |
| |
| packet_length=uip_len+5; |
| asm("jmp @LC1"); |
| |
| asm("@SD1:"); |
| |
| packet_length=uip_len+6; //+6 for status word, length and ctl byte |
| |
| asm("@LC1:"); |
| |
| // printf("SND: L:%d ", packet_length); |
| |
| asm("lda _packet_length"); |
| asm("sta %w", ETHDATA); |
| asm("lda _packet_length+1"); |
| asm("sta %w", ETHDATA); |
| |
| #ifdef DEBUG |
| // print_packet(uip_buf, uip_len); |
| #endif |
| |
| /* Send 40+14=54 bytes of header */ |
| |
| if(uip_len <= 54) { |
| |
| #ifdef DEBUG |
| printf("SND: short packet sent.\n"); |
| #endif |
| |
| asm("ldx _uip_len"); |
| asm("ldy #0"); |
| asm("@WL1:"); |
| asm("lda _uip_buf,y"); |
| asm("sta %w", ETHDATA); |
| asm("iny"); |
| asm("dex"); |
| asm("bne @WL1"); |
| |
| } else { |
| |
| asm("ldx #54"); |
| asm("ldy #0"); |
| asm("@WL2:"); |
| asm("lda _uip_buf,y"); |
| asm("sta %w", ETHDATA); |
| asm("iny"); |
| asm("dex"); |
| asm("bne @WL2"); |
| |
| uip_len -= 54; |
| |
| asm("lda _uip_appdata"); //uip_appdata is pointer |
| asm("sta ptr1"); |
| asm("lda _uip_appdata+1"); |
| asm("sta ptr1+1"); |
| |
| asm("ldy #0"); |
| #if UIP_BUFSIZE > 255 |
| asm("ldx _uip_len+1"); |
| #else |
| asm("ldx #0"); |
| #endif |
| asm("beq @RE1"); //packet_length < 256 |
| |
| asm("@RL1:"); |
| asm("lda (ptr1),y"); |
| asm("sta %w", ETHDATA); |
| asm("iny"); |
| asm("bne @RL1"); |
| asm("inc ptr1+1"); |
| asm("dex"); |
| asm("bne @RL1"); |
| |
| asm("@RE1:"); |
| asm("lda (ptr1),y"); |
| asm("sta %w", ETHDATA); |
| asm("iny"); |
| asm("cpy _uip_len"); |
| asm("bne @RE1"); |
| |
| } |
| |
| asm("lda _uip_len"); |
| asm("and #$01"); |
| asm("beq @R3"); |
| |
| asm("lda #%%00100000"); |
| asm("sta %w", ETHDATA); //Control byte |
| |
| asm("lda #%%11000000"); //ENQUEUE PACKET - transmit packet |
| asm("sta %w", ETHMMUCR); |
| |
| // printf("\n## %02x", *(unsigned char *)ETHIST); |
| |
| return; |
| |
| asm("@R3:"); |
| |
| asm("lda #0"); |
| asm("sta %w", ETHDATA); //Fill byte |
| asm("sta %w", ETHDATA); //Control byte |
| |
| asm("lda #%%11000000"); //ENQUEUE PACKET - transmit packet |
| asm("sta %w", ETHMMUCR); |
| |
| // printf("\n## %02x\n", *(unsigned char *)ETHIST); |
| return; |
| } |
| #pragma optimize(pop) |
| |
| #ifdef DEBUG |
| static void print_packet(u8_t *buf, u16_t length) |
| { |
| int i; |
| int remainder; |
| int lines; |
| u8_t a; |
| int cur; |
| int address=0; |
| |
| printf("\nPacket of length %d \n", length ); |
| |
| lines = length / 8; |
| remainder = length % 8; |
| |
| for ( i = 0; i < lines ; i ++ ) { |
| printf(":%04x ", address=i*8); |
| |
| for ( cur = 0; cur < 8; cur ++ ) { |
| a = *(buf ++ ); |
| printf("%02x ", a); |
| } |
| printf("\n"); |
| } |
| |
| printf(":%04x ", address+8); |
| |
| for ( i = 0; i < remainder ; i++ ) { |
| a = *(buf ++ ); |
| printf("%02x ", a); |
| } |
| printf("\n"); |
| } |
| #endif /* DEBUG */ |
| |