nes-proj/platform/c64/net/lan91c96.c

454 lines
12 KiB
C

/*
* 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/06/17 22:41:26 adamdunkels Exp $
*
*/
#include "lan91c96.h"
#include "contiki-net.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;
/*-----------------------------------------------------------------------------------*/
#pragma optimize(push, off)
void lan91c96_init(void)
{
/* Check if high byte is 0x33 */
asm("lda %w", ETHBSR+1);
asm("cmp #$33");
asm("beq %g", L1);
asm("inc $d021"); /* Error */
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("ldy #0");
L2:
asm("cmp ($ff,x)"); /* 6 cycles */
asm("cmp ($ff,x)"); /* 6 cycles */
asm("dey"); /* 2 cycles */
asm("bne %g", 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 %v", uip_ethaddr);
asm("sta %w", ETHIAR);
asm("lda %v+1", uip_ethaddr);
asm("sta %w", ETHIAR+1);
asm("lda %v+2", uip_ethaddr);
asm("sta %w", ETHIAR+2);
asm("lda %v+3", uip_ethaddr);
asm("sta %w", ETHIAR+3);
asm("lda %v+4", uip_ethaddr);
asm("sta %w", ETHIAR+4);
asm("lda %v+5", uip_ethaddr);
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)
u16_t lan91c96_poll(void)
{
#ifdef DEBUG
BANK(0);
printf("RAM: %d ", ((*(unsigned int *)(ETHMIR)) & 0xff00));
BANK(2);
#endif
asm("lda %w", ETHIST);
asm("and #%%00000001"); /* RCV INT */
asm("bne %g", L1);
/* No packet available */
return 0;
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 %v", packet_status); /* High byte only */
asm("lda %w", ETHDATA); /* Total number of bytes */
asm("sta %v", packet_length);
asm("lda %w", ETHDATA);
asm("sta %v+1", packet_length);
/* Last word contain 'last data byte' and 0x60 */
/* or 'fill byte' and 0x40 */
packet_length -= 6; /* The packet contains 3 extra words */
asm("lda %v", packet_status);
asm("and #$10");
asm("beq %g", L2);
packet_length++;
#ifdef DEBUG
printf("RCV: odd number of bytes\n");
#endif
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 #<%v", uip_buf);
asm("sta ptr1");
asm("lda #>%v", uip_buf);
asm("sta ptr1+1");
asm("lda %v+1", packet_length);
asm("sta tmp1");
asm("ldy #0");
L3:
asm("lda %w", ETHDATA);
asm("sta (ptr1),y");
asm("iny");
asm("bne %g", L4);
asm("inc ptr1+1");
L4:
asm("cpy %v", packet_length);
asm("bne %g", L3);
asm("dec tmp1");
asm("bpl %g", L3);
/* 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)
/*-----------------------------------------------------------------------------------*/
#pragma optimize(push, off)
void lan91c96_send(void)
{
/* First 14+40 (IP and TCP header) is send from uip_buf */
/* than data from uip_appdata */
#ifdef DEBUG
printf("SND: send packet\n");
#endif
asm("lda %v+1", uip_len);
asm("ora #%%00100000"); /* Allocate memory for TX */
asm("sta %w", ETHMMUCR);
asm("ldy #8"); /* Wait... */
L1: /* Wait for allocation ready */
asm("lda %w", ETHIST);
asm("and #%%00001000"); /* ALLOC INT */
asm("bne %g", L2);
asm("dey");
asm("bne %g", L1);
#ifdef DEBUG
printf("SND: ERR: memory alloc timeout\n");
#endif
return;
L2:
#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 %v", uip_len);
asm("and #$01");
asm("beq %g", L3);
packet_length = uip_len + 5;
asm("jmp %g", L4);
L3:
packet_length = uip_len + 6; /* +6 for status word, length and ctl byte */
L4:
#ifdef DEBUG
printf("SND: L:%d ", packet_length);
#endif
asm("lda %v", packet_length);
asm("sta %w", ETHDATA);
asm("lda %v+1", packet_length);
asm("sta %w", ETHDATA);
#ifdef DEBUG
print_packet(uip_buf, uip_len);
#endif
/* Send 14+40=54 bytes of header */
if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) {
#ifdef DEBUG
printf("SND: short packet sent.\n");
#endif
asm("ldy #0");
L5:
asm("lda %v,y", uip_buf);
asm("sta %w", ETHDATA);
asm("iny");
asm("cpy %v", uip_len);
asm("bne %g", L5);
} else {
asm("ldy #0");
L6:
asm("lda %v,y", uip_buf);
asm("sta %w", ETHDATA);
asm("iny");
asm("cpy #%b", UIP_LLH_LEN + UIP_TCPIP_HLEN);
asm("bne %g", L6);
packet_length = uip_len - (UIP_LLH_LEN + UIP_TCPIP_HLEN);
asm("lda %v", uip_appdata); /* uip_appdata is pointer */
asm("sta ptr1");
asm("lda %v+1", uip_appdata);
asm("sta ptr1+1");
asm("ldy #0");
L7:
asm("lda (ptr1),y");
asm("sta %w", ETHDATA);
asm("iny");
asm("bne %g", L8);
asm("inc ptr1+1");
L8:
asm("cpy %v", packet_length);
asm("bne %g", L7);
asm("dec %v+1", packet_length);
asm("bpl %g", L7);
}
asm("lda %v", packet_length);
asm("and #$01");
asm("beq %g", L9);
asm("lda #%%00100000");
asm("sta %w", ETHDATA); /* Control byte */
asm("lda #%%11000000"); /* ENQUEUE PACKET - transmit packet */
asm("sta %w", ETHMMUCR);
#ifdef DEBUG
printf("\n## %02x", *(unsigned char *)(ETHIST));
#endif
return;
L9:
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);
#ifdef DEBUG
printf("\n## %02x\n", *(unsigned char *)(ETHIST));
#endif
}
#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 */