Final Report

EEL 4914 Electrical and Computer Engineering Design

(Senior Design)

Spring Semester 2003

 

 

MSX Network Interface

 

 

 

Submitted by:

Bruno Barreyra

David Regal

 

 

 

 

 

 

 

Final Report [       ]

 

Topic Index:_______________

 


Table of Contents

 

Technical Objectives. 2

Study Plan. 2

Expected Outcome. 3

Study Organization. 4

Supplementary Information. 4

Engineering analysis. 5

Data acquired. 6

Artwork. 6

Results. 9

Compare proposed objectives and quantitative expectations with achieved. 9

Problems encountered. 9

High-level market analysis. 11

Employment opportunities. 12

Actual Schedule. 13

Driver code. 13

Reference list 26

 

Technical Objectives

Project goals:

  • Design a network cartridge for the MSX computer
    • Optimize performance
    • Marketable product
  • Develop driver for the UZIX Operating System, an UNIX clone

 

Pedagogical goals:

  • Experience the full design flow of a commercial product
    • Schematic Entry
    • Layout Design
    • Driver writing
    • Validation
  • Successfully working in a team
  • Improve Bruno’s crappy time management

Study Plan

Required knowledge:

  • Digital Circuits
  • C programming
  • Operating Systems

 

Tools:

  • Either Protel or Eagle
  • Max+Plus II
  • Hitech-C Compiler for Zilog Z-80

 

Prototype:

  • The first prototype will consist of an adapted ISA prototyping card that fits in the MSX slot. This first board will not include the network chip, but will only assure that the interfacing is correct as designed
  • Once the design (schematic and PCB layout) is complete, the PCB board will be sent to fabrication and possibly assembly
  • The final product will be a plastic enclosed standard MSX sized cartridge with an RJ-45 jack for network connection

 

Resources:

  • MSX Turbo-R computer for testing
  • Linux system for developing driver and cross-compiling

 

Fabrication:

  • Outsource to commercial service

Expected Outcome

The final product will reside on a standard-sized MSX cartridge, and will be plug-and-play. The main goal is integration with the UZIX operating system [1], which has full TCP/IP networking capabilities, including telnet, FTP, IRC and web-browsing. It would be possible in the future to provide extensions to the MSX’s BASIC language to program the network card, with great pedagogical possibilities.

 

The main limiting factor for performance in our project is CPU power. The standard MSX computer runs at a clock speed of 3.58 MHz, and, at this speed, decoding network packets is quite processor intensive. The units we will work with are the Panasonic MSX Turbo-R computers. Aside from the standard 3.58 MHz Z-80 processor, these units also have a 7.xx MHz R800 CPU. This CPU is fully compatible with the Z-80 and, at this clock speed, is actually equivalent to a 28 MHz Z-80 processor. Even then, the machine will not be a speed demon on the network. Speed will be the major deciding factor on design choices.

 

Although the MSX will lag in performance numbers compared to today’s powerful PC’s, for its own use the performance will be more than adequate. Most software written for the MSX is quite small, so the amount of data that will be transferred in actual use will be small. For example, the popular game “Nemesis” written by Konami occupies a total of 128KB. A previous prototype of a network card, using a different controller, achieved 3.99KB/s on a MSX Turbo-R. It would take a total of 32 seconds to download the game. In comparison, the download of the popular game for the PC platform, Unreal Tournament 2003, is a 140MB file, and takes around 13 minutes to download on a home broadband connection, assuming optimal connection speed. Some people will swear over their dead mother that Nemesis is a better game.

Study Organization

Meetings:

            We will have meetings after deadlines usually on Thursday nights.

Supplementary Information

This project can be thought of as having:

 

            25% Hardware design and implementation

            35% Software design and implementation

            15% Design Integration

            15% Test and validation

            10% Documentation


Engineering analysis

 

To analysis the project, research and testing was done to be sure all the conceptual blocks fit together and that the hardware could be obtained.  Organization is critical and testing individual parts of the system seemed logical.  We wanted to ensure each block was functional before testing the overall system.  Please see [fig. 4] for the block diagram.

 

When the MSX computers arrived, we wanted to see if they were working.  Also, we wanted to see if we had correct address and data signals.  This was done by saudering a ribbon cable to the flex expansion port A on the Altera board and saudering a chopped-off ISA prototyping card on the other end of the ribbon cable to plug into the MSX cartridge slot.

 

In MaxplusII, the address and control signals decoding was designed and downloaded into the board.  When testing time came, the MSX performed well flashing the BCD LEDs on the Altera board faster than the human eye could detect.  Our address and signal decoding algorithm was correct.

 

 

 

 

 


[fig. 1] Connected through Altera’s Flex port A, we tested our decoding algorithm.  A loop in BASIC made lights blink on the Altera board.

 

Next we tested the UZIX functional block by making a boot disk and loading it on the MSX.  With kernel at 0.2.1, it was stable and very UNIX like.  The MSX OS would work with our MSX computers.

 

To ensure timing was correct, we downloaded datasheets for both the MSX Z80 processor and the CS8900 chip.  For the MSX computer, the timing diagrams were vague and in order to have accurate measurements, the MSX was connected to a LSA.  We encountered a problem.  The set-up time was not enough for the CS8900.  So the CS8900 would only work when the MSX was in non-turbo mode.

 

This is as much analysis that could be done without our working board.

Data acquired

 

Many specification sheets were downloaded for the CS8900 Ethernet controller [2].  There are timing diagrams and register information as well as an overall schematic of how to design with the CS8900.

 

Datasheets for the MSX Z80 microprocessor clock cycles and op codes [3]. 

Artwork

 

The MSX computers.  We have both the Turbo models ST and MT.

 

[fig. 2] GT model (Panasonic FS-A1GT MSX Turbo R) [3]

 

 

[fig.3 ] ST model (Panasonic FS-A1ST MSX Turbo R) [3]

 

[fig. 4] Functional block diagram of the project.

 

[fig. 5] Bake ‘em.- even the smallest sauder paste lines caused some sauder bridges on the CS8900 IC [4]

 

[fig. 6] Send out PCB on left and UF’s milled board on right

 

[fig. 7] Complete MSX network interface card (top)

 

[fig. 8] Complete MSX network interface card (bottom)

 

[fig. 9] MSX on the network

 

[fig. 10] Bruno working on software

 

Results

 

Hardware works so our design was correct.  Preliminary testing gave PHY transfer rates of 50 kB/s.  Skipping the TCP/IP stack and testing on a LAN, our hacked version of ping had an average round-trip time of 7 ms.

 

For controlling our board, the UZIX works is not as easy to develop in as the MSX’s native operating system, MSX-DOS (MS-DOS).  Also, MSX-DOS runs a little faster than UZIX.  We thought maybe this would be improved once the code is incorporated as a module into UZIX.  This did not happen.  Once it was a module, to run the board the TCP/IP stack has to be transversed.  Thus it is slower than working on the hardware layer.

 

Compare proposed objectives and quantitative expectations with achieved

 

All expectations and objectives were met.  However, we would like to improve the software.

 

Driver was written in both C and assembly.  If we are to increase the speed, the program should only be in assembly.  Also, to cut down on the number of op codes fetched, and thus increase the speed, in the next design CS8900’s Memory Mode should be used instead of I/O mode.

 

Problems encountered

 

Sauder shorts are nearly impossible to detect on the small pins of the main IC chip, CS8900, and to debug the chip, it is also hard because there are no sections can be isolated and debugged with a voltammeter. With a total of 4 boards, we tried hand saudering and baking the CS8900 chip.  Baking worked better and it is the only working board.  A toaster oven was used with the following recipe [4]:

 

4 min.

200 deg.

Warm up board and allow temperatures to equalize.

2 min.

325 deg.

Bring temperature up to saturation.

30 sec +

450 deg.

Temperature raised until solder melts and beads at individual pins, then held for 30 additional seconds.

 

 

Tap the oven before cool down...

[table 1]  Recipe. Nothing like fresh sauder paste baking in the oven. Smells good doesn’t it?

 

Could not use the milled board from UF’s lab because even with careful saudering, the vias created a tiny bump which so that the CS8900 chip would not lay flat on the board (see [fig. 11]).  So we had to wait for the send out PCBs to come in, assimilate them quickly, and then begin testing the driver code.

 

[fig. 11] Saudering vias raised the CS8900 IC.  ‘X’ is where the CS8900 IC should go.

 

The diagram of the clock cycles of the MSX were vague, so a LSA was put on the MSX and it would discovered that the MSX does not give enough set-up time for the CS8900 in Turbo mode.  To overcome this problem, some glue logic (2 two-input OR gates) was used.

 

Having a hard drive or additional 3.5” floppy would really speed up driver development.  To boot UZIX on the MSX, a floppy drive is used.  Also, the driver is being developed on this floppy.  So any changes to the driver code, the MSX has to be turned off, new driver code copied to the floppy, and reboot the MSX with the UZIX floppy.  On a 90’s processor running around 7 MHz, rebooting takes a while.  Repeat this a hundred times and it becomes very tedious.  On one of the MSX, we used the floppy drive so much that is started groaning (drive belt suspected) and we switched to the other MSX.

 

No debugging tool can be used after a certain point of writing the driver.  Then debugging becomes changing a little piece of code at a time, plugging the card in, running the program, and seeing what works.  This is a long process as can be seen from the steps below.

 

Steps taken any time the driver code is changed:

  1. Compile on Linux PC
  2. Turn off MSX  
  3. Take floppy disc out of MSX and
  4. Put floppy into the PC
  5. Copy compiled driver to floppy
  6. Put floppy back into MSX
  7. Wait 10 seconds for boot-up
  8. Run program – yell happy sounds or agony
  9. Go to step 1 and start over

High-level market analysis

 

 

Variable costs

 

 

 

 

 

 

 

 

 

 

 

 

[$USD]

[$USD]

 

[$USD]

 

Description

Type

Qty./Board

Cost/Unit

Bulk cost

Bulk Qty.

Cost

1

0.1 uF

Cap

9

 

$0.10

$1.96

20

$0.88

2

68 pF

Cap

1

 

$0.05

$0.53

10

$0.05

3

24.9 Ohm

R

2

 

$0.12

$1.17

10

$0.23

4

680 Ohm

R

2

 

$0.09

$0.94

10

$0.19

5

4.7 kOhm

R

0

 

$0.09

$0.94

10

$0.00

6

100 Ohm

R

1

 

$0.09

$0.94

10

$0.09

7

4.99 kOhm

R

1

 

$0.09

$0.94

10

$0.09

8

Molex RJ-45

Connector

1

estimate

$0.50

 

 

$0.50

9

HALO transformer

IC

1

estimate

$1.00

 

 

$1.00

10

CS8900

IC

1

 

$7.00

 

 

$7.00

11

PCB rev 1

Board

1

 

$7.40

$37.00

5 PCBs s/h

$7.40

 

 

 

 

 

 

 

 

 

 

Total

 

 

 

 

 

 

$17.45

 

 

 

 

 

 

 

 

 

 

Variable costs

 

 

 

 

 

 

 

 

Labor description

 

Hours

 

 

 

 

 

1

lay sauder paste

 

0.15

 

$6.00

 

 

$0.90

2

bake CS8900

 

0.2

 

$6.00

 

 

$1.20

3

test & fix CS8900

 

1

 

$6.00

 

 

$6.00

4

sauder discrete parts

 

0.5

 

$6.00

 

 

$3.00

5

Make flpy disk for driver

 

0.05

 

$6.00

 

 

$0.30

 

 

 

 

 

 

 

 

 

 

Total

 

 

 

 

 

 

$11.40

 

Total variable

 

 

 

 

 

 

$28.85

 

 

 

 

 

 

 

 

 

 

Fixed costs

 

 

 

 

 

 

 

1

Toaster oven

Equipment

1

 

$20.00

 

 

$20.00

2

MSX GT model

Equipment

1

 

$100.00

 

 

$100.00

3

MSX ST model

Equipment

1

 

$100.00

 

 

$100.00

 

MSX connectors

Equipment

3

 

$1.48

$4.45

 

$4.45

 

 

 

 

 

 

 

 

 

 

Total fixed

 

 

 

 

 

 

$224.45

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Break even analysis with expected units sold

 

Expected sold =

100

 

 

Price/card to break even =

$31.09

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Break even analysis with expected units sold

 

Expected sold =

50

 

 

Price/card to break even =

$33.33

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Break even analysis with expected units sold

 

Expected sold =

10

 

 

Price/card to break even =

$51.29

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Note: Could outsource the labor of assimilating the card if a discount price could be negotiated. 

 

 

As of last inquiry (March 2003), Tel-test costs around $30/board which is much higher than in-house labor and would make the price/card too high.

[table 2]  Cost analysis and break-even prices

 

Employment opportunities

 

Working on the hardware and LLC layer in the ISO model, there are many computer networking skills gained.  Jobs could be found in any of the networking device companies such as:

  • Nortel
  • Netgear
  • Cisco
  • Linksys

 

Also embedded applications with Linux was learned so we could find a job consulting companies that use Linux or developing stand-alone embedded applications that us Linux/UNIX as the operating system.

 


Actual Schedule

 

Because of a mistake in the cs8900 schematic drawn in Protel, the layout of the PCB took longer than expected.  Also, we couldn’t use the milled board from UF’s lab so we had to wait until the “send out” PCBs came back before beginning assimilation and testing with driver code. 

 

Once the PCBs were assimilated, without shorts on the CS8900 chip, we began developing driver code and the schedule was followed as planned.

Driver code

 

Programming was done in assembly and C.  The C program contains assembly macros and is the overall driver for the program.  The macros access the registers on the Crystal cs8900 chip.

 

Before writing the C program, testing was done in BASIC using the MSX’s native OS MSX-DOS.  Once it was test in BASIC, assembly was written and compiled.  Then that assembly code was perfected and written as macros controlled by a C program. 

 

With this driver, the card can fully interface with the UZIX operating system.  To make a module requires an additional file and different compilation.

 

Two files were written for this project, cs8900.c and cs8900.h and are listed below.

 

 

 

Text Box: cs8900.c

 

#ifndef UZIX_MODULE

#include <stdio.h>

#include <malloc.h>

#include "cs8900.h"

#endif

 

struct ethernet_frame tx_buffer;

struct ethernet_frame rx_buffer;

 

/* have this more or less hard-coded in advance */

uint8_t arp_request[42] = {

      0xff, 0xff, 0xff, 0xff, 0xff, 0xff,

      0xf0, 0xde, 0xba, 0x00, 0x00, 0x01,

      0x08, 0x06, 0x00, 0x01, 0x08, 0x00,

      0x06, 0x04, 0x00, 0x01, 0xf0, 0xde,

      0xba, 0x00, 0x00, 0x01, 0xc0, 0xa8,

      0x01, 0x09, 0xff, 0xff, 0xff, 0xff,

      0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };

 

uint8_t arp_reply[42] = {

      0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

      0xf0, 0xde, 0xba, 0x00, 0x00, 0x01,

      0x08, 0x06, 0x00, 0x01, 0x08, 0x00,

      0x06, 0x04, 0x00, 0x02, 0xf0, 0xde,

      0xba, 0x00, 0x00, 0x01, 0xc0, 0xa8,

      0x01, 0x09, 0x00, 0x00, 0x00, 0x00,

      0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

 

uint8_t ethernet_header[14] = {

      0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

      0xf0, 0xde, 0xba, 0x00, 0x00, 0x01,

      0x08, 0x00};

 

struct arp_packet *request = (struct arp_packet *) (arp_request + 14);

struct arp_packet *reply = (struct arp_packet *) (arp_reply + 14);

 

/* to save time, we store the IP address on the pre-prepared ARP packet above */

uint8_t *ip_addr = arp_reply + 28;

uint8_t *hw_addr = arp_reply + 6;

uint8_t netmask[4];

uint8_t subnet[4];

uint8_t gateway[4];

 

#define MAX_REC_BUF     8           /* must be power of 2!!! */

 

uint8_t *rec_buff[MAX_REC_BUF];

uint16_t rec_buff_sizes[MAX_REC_BUF];

uint8_t rec_buff_idx = 0;

uint8_t rec_buff_last = 0;

 

/*  Searches through table to see if ARP entry exists.  If it does then

 * MAC address is updated.  If a entry does not exist, then add in an

 * appropiate spot.

 * Depends on :

 *  1.) static time

 *  2.) #define MAX_ENTRIES

 * Be sure "time" is initialized to 0

 */

void add_arp_entry(uint8_t * hw_addr, uint8_t * ip_addr )

{

      /* Variables used for finding oldest entry */

      int longest_time=0;

      int temp_index;

      /*Counters */

      int i=0;

      int j=0;

 

      if (((ip_addr[0] & netmask[0]) != subnet[0]) ||

                  ((ip_addr[1] & netmask[1]) != subnet[1]) ||

                  ((ip_addr[2] & netmask[2]) != subnet[2]))

            return;

     

      mytime++;

 

      for(i=0; i < ARP_CACHE_SIZE; i++){

            /* Check in reverse order to speed up search */

            for(j=3; j>=0; j--){

                  if (ip_addr[j] != arp_cache[i].ip_addr[j]) break;

                 

                  /* If all 4 terms of the IP matched, then match will = 4. Update the MAC addr */

                  memcpy(arp_cache[i].hw_addr, hw_addr, 6);

                  arp_cache[i].timer = mytime;

                  return;

            }

      }

 

      /*try to find a 0*/

      for(i=0; i < ARP_CACHE_SIZE; i++)

            if(   arp_cache[i].ip_addr[0] == 0 &&     arp_cache[i].ip_addr[1] == 0

                  && arp_cache[i].ip_addr[2] == 0 && arp_cache[i].ip_addr[3] == 0)

                  goto replace;

 

      if(i == ARP_CACHE_SIZE)

      {

            /* if the program made it here, there are no empty slots.  Replace oldest.*/

            for(i=0; i < ARP_CACHE_SIZE; i++)

            {

                  if(mytime - arp_cache[i].timer > longest_time)

                  {

                        longest_time = mytime - arp_cache[i].timer;

                        temp_index = i;

                  }

            }

            i = temp_index;

      }

 

replace:

      /* "i" now is the index to the oldest or blank entry. Replace it.*/

      memcpy(arp_cache[i].ip_addr, ip_addr, 4);

      memcpy(arp_cache[i].hw_addr, hw_addr, 6);

      arp_cache[i].timer = mytime;

 

      if (tx_buffer.valid = 1)

            if ( ((struct ip_packet *) tx_buffer.data)->target_ip_addr[3] == ip_addr[3] &&

                  ((struct ip_packet *) tx_buffer.data)->target_ip_addr[2] == ip_addr[2] &&

                  ((struct ip_packet *) tx_buffer.data)->target_ip_addr[1] == ip_addr[1] &&

                  ((struct ip_packet *) tx_buffer.data)->target_ip_addr[0] == ip_addr[0])

            {

                  /* send packet that was waiting in buffer */

                  memcpy(ethernet_header, ip_addr, 6);

                  writeFrame(tx_buffer.data, tx_buffer.length);

                  tx_buffer.valid = 0;

            }

 

      return;

}

 

void do_arp_reply (struct ethernet_frame *frame)

{

      struct arp_packet *arp_frame = (struct arp_packet *) (frame->data + 14);

 

      if (arp_frame->target_ip_addr[3] != ip_addr[3])

            return;

      else if (arp_frame->target_ip_addr[2] != ip_addr[2])

            return;

      else if (arp_frame->target_ip_addr[1] != ip_addr[1])

            return;

      else if (arp_frame->target_ip_addr[0] != ip_addr[0])

            return;

 

      /* FIXME: send_packet should fill in the target HW address */

      memcpy(arp_reply + 32, frame->data + 6, 6);

      memcpy(arp_reply + 0, frame->data + 6, 6);

      memcpy(reply->target_ip_addr, arp_frame->source_ip_addr, 4);

 

      sendFrame(arp_reply, 42);

 

      add_arp_entry(arp_frame->source_hw_addr, arp_frame->source_ip_addr);

 

      return;

}

 

void do_arp_request (uint8_t *ip)

{

      memcpy(request->target_ip_addr, ip, 4);

      sendFrame(arp_request, 42);

 

      return;

}

 

uint8_t * resolve_ip(uint8_t *ip)

{

      uint8_t i;

     

      for (i = 0 ; i < ARP_CACHE_SIZE; i++)

            if (  arp_cache[i].ip_addr[3] == ip[3] &&

                  arp_cache[i].ip_addr[2] == ip[2] &&

                  arp_cache[i].ip_addr[1] == ip[1] &&

                  arp_cache[i].ip_addr[0] == ip[0] )

                  return arp_cache[i].hw_addr;

 

      /* not on cache, do a request */

      do_arp_request(ip);

 

      return NULL;

}

 

int send_packet(uint8_t *packet, uint16_t length)

{

      struct ip_packet *frame = (struct ip_packet *) packet;

      uint8_t * target_hw_addr;

     

      if (((frame->target_ip_addr[0] & netmask[0]) != subnet[0]) ||

                  ((frame->target_ip_addr[1] & netmask[1]) != subnet[1]) ||

                  ((frame->target_ip_addr[2] & netmask[2]) != subnet[2]))

      {

            /* out of subnet, send it to gateway */

            target_hw_addr = resolve_ip(gateway);

      }

      else

      {

            /* we are on same subnet, resolve target IP address */

            target_hw_addr = resolve_ip(frame->target_ip_addr);

      }

 

      /* address not yet on cache, come back later */

      if (target_hw_addr == NULL)

      {

            memcpy(tx_buffer.data, packet, length);

            tx_buffer.length = length;

            tx_buffer.valid = 1;

            return 1;

      }

 

      memcpy(ethernet_header, target_hw_addr, 6);

      writeFrame(packet, length);

 

      return 0;

}

 

#ifndef UZIX_MODULE

     

void do_icmp_reply (uint8_t *data, uint16_t size)

{

      struct ip_packet *packet = (struct ip_packet *)data;

      /*add_arp_entry(data + 6, data + 0x1a);*/

 

      memcpy(packet->target_ip_addr, packet->source_ip_addr, 4);

      memcpy(packet->source_ip_addr, ip_addr, 4);

      data[20] = 0; /* PING reply */

 

      send_packet(data, size);

}

 

#endif

 

 

uint8_t * getPacket(uint16_t * length)

{

#asm

      ; poll for a received packet

      setRegister 0124h

 

      in a, (0Dh)

      and 1

        jp z, fimzis

 

      ; reread RxStatus

      in a, (1)

      in a, (0)

#endasm

 

      /* this reads the frame */

      /* fix it: make it all ASM */

      rx_buffer.length = getFrame(rx_buffer.data);

      *length = rx_buffer.length - 14;

 

      switch ( rx_buffer.data[13] ) /* FIXME: check the whole 16-bit number */

      {

            case 0x00:

                  return rx_buffer.data + 14;

 

            case 0x06: /* ARP request */

                  switch (rx_buffer.data[0x15])

                  {

                        case 1:           /* ARP request */

                              do_arp_reply(&rx_buffer);

                              break;

 

                        case 2:           /* ARP reply */

                              add_arp_entry(rx_buffer.data + 0x16, rx_buffer.data + 0x1c);

                              break;

 

                        default:

                              break;

                  }

 

                  break;

 

            case 0x35:  /* Invalid packet */

#ifndef UZIX_MODULE

                  printf("RARP\n");

#endif

                  break;

 

            default:    /* Invalid packet */

                  break;

      }

#asm

fimzis:

#endasm

      return NULL;

}

 

void initialize_eth(unsigned char *ip, unsigned char *netm, unsigned char *gate)

{

      memcpy(ip_addr, ip, 4);

      memcpy(netmask, netm, 4);

      memcpy(gateway, gate, 4);

     

      subnet[0] = ip_addr[0] & netmask[0];

      subnet[1] = ip_addr[1] & netmask[1];

      subnet[2] = ip_addr[2] & netmask[2];

      subnet[3] = ip_addr[3] & netmask[3];

 

      init_cs8900();

 

      /* resolve the gateway right away */

      do_arp_request(gateway);

     

      /* FIXME: select correct size here */

      rx_buffer.data = (uint8_t *) malloc(1518);

      tx_buffer.data = (uint8_t *) malloc(1518);

}

 

#ifndef UZIX_MODULE

 

int main()

{

      uint16_t foo;

      uint8_t *pa;

 

      printf ("CS8900A driver for Uzix v0.1\n");

      printf ("(c)2003 Banana Enterprises\n\n");

 

      if (detect_cs8900())

      {

            fprintf(stderr, "Error: no CS8900A found\n");

            return 1;

      }

 

      printf ("Using defaults: IP address\t192.168.1.9\nNetmask\t255.255.255.0\nGateway\t192.168.1.1\n");

      initialize_eth((uint8_t *)"\xc0\xa8\x01\x09", (uint8_t *)"\xff\xff\xff\xff", (uint8_t *)"\xc0\xa8\x01\x01");

 

      for (;;) {

            _ETH_mainloop();

            pa = _ETH_getpacket(&foo);

            if (pa) {

                  if (pa[0x17-14] == 1) {

                        do_icmp_reply(pa, foo);

                        free(pa);

                  }

            }

      }

}

 

#endif

 

void _ETH_mainloop() {

      uint16_t size;

      uint8_t *pack, *pack2;

     

      if (!(pack = getPacket(&size))) return;

      /* buffer full; discard */

      if (((rec_buff_last + 1) & (MAX_REC_BUF-1)) == rec_buff_idx) return;

      /* no memory; discard */

      if (!(pack2 = malloc(size))) return;

      memcpy(pack2, pack, size);

      rec_buff[rec_buff_last] = pack2;

      rec_buff_sizes[rec_buff_last] = size;

      rec_buff_last = (++rec_buff_last & (MAX_REC_BUF-1));

}

 

uint8_t *_ETH_getpacket(uint16_t *size) {

      uint8_t *pack;

 

      if (rec_buff_last == rec_buff_idx) return NULL;

      *size = rec_buff_sizes[rec_buff_idx];

      pack = rec_buff[rec_buff_idx];

      rec_buff_idx = (++rec_buff_idx & (MAX_REC_BUF-1));

      /* caller is responsible for freeing the buffer with free()! */

      return pack;

}

 

#asm

_detect_cs8900:

      ld a, 0           ; set packet page pointer to 0x0000

      out (PPPntrH),a

      out (PPPntrL),a

     

      readData

 

      ; compare value

      ld hl, 0E63h

      sbc hl, de

 

      ret

 

_init_cs8900:

; Set the MAC address

      setRegister 0158h ; Internal Address Register

      writeData 0DEF0h

     

      setRegister 015Ah ; Internal Address Register

      writeData 000BAh

     

      setRegister 015Ch ; Internal Address Register

      writeData 0100h

 

; allow packets

      setRegister 0104h

 

      ld a, 00001101B

      out (0dh), a

 

; enable receiver and transmitter

      setRegister 0112h

 

      ld a, 11000000B         ; set SerTxOn and SerRxOn

      out (0ch), a

 

      ret

 

_getFrame:

        getParInHL      ; pointer to buffer 

 

; get frame length

      in a, (1)

      ld d, a

      in a, (0)

      ld e, a

 

; save length and divide it by two

      ld b, d

      ld c, e

      srl b

      rr c

 

; load the frame into memory

1:    in a, (1)

      ex af,af'

      in a, (0)

 

      ld (hl), a

      inc hl

      ex af,af'

      ld (hl), a

      inc hl

 

      dec bc

      ld a, b

      or c

 

      jr nz, 1b

 

      ld h, d

      ld l, e

      ret

 

; sendframe

;

; arguments:

;     DE    pointer to packet_data

;     BC    size of packet

;

; returns:

;     nothing

 

_sendFrame:

        getParInDE_BC   ; buffer location, TxLength

 

; bid for buffer space by writing command and frame length

      ex de,hl

      ld c, 04h

      ld a, 0c0h  ; write 00C0h command to TxCMD

      out (c), a

      inc c

      ld a, 0

      out (c), a

      inc c       ; write TxLength

      out (c), e

      inc c

      out (c), d

      srl d

      rr e

; poll for space

      setRegister 0138h ; BusST register

1:    in a, (0dh)

      and 1

      jp z, 1b

 

; write loop

2:    ld c, 0

      outi

      ld c, 1

      outi

 

      dec de

      ld a,d

      or e

      jr nz, 2b

      ret

 

; writeframe

;

; this function writes the ethernet_header and the packet

;

 

_writeFrame:

; bid for buffer space by writing command and frame length

        getParInDE_BC   ; buffer location, TxLength

 

      push hl

 

      ld bc, 14

      add hl, bc  ; add header length

     

      ld c, 04h

      ld a, 0c0h  ; write 00C0h command to TxCMD

      out (c), a

      inc c

      ld a, 0

      out (c), a

 

      inc c       ; write TxLength

      out (c), l

      inc c

      out (c), h

 

; poll for space

      setRegister 0138h ; BusST register

 

1:    in a, (0dh)

      and 1

      jp z, 1b

 

; write header

      ld c, 7

      ld hl, _ethernet_header

 

2:    ld a, (hl)

      out (0), a

      inc hl

 

      ld a, (hl)

      out (1), a

      inc hl

 

      dec c

      jr nz, 2b

 

      pop hl

 

; write loop

      srl h

      rr l

 

3:    ld a, (de)

      out (0), a

      inc de

 

      ld a, (de)

      out (1), a

      inc de

 

      dec hl

 

      ld a,h

      or l

 

      jr nz, 3b

      ret

 

#endasm

Text Box: End cs8900.c

 

 

 

Text Box: End cs8900.h

 

typedef unsigned int uint16_t;

typedef unsigned char uint8_t;

 

#define MAC_ADDRESS_SIZE 6

#define IP_ADDRESS_SIZE 4

#define ARP_CACHE_SIZE 5

 

struct ethernet_frame {

      uint16_t length;

      uint8_t *data;

      uint8_t valid;    /* used only for Tx buffer */

};

 

struct arp_packet {

      uint16_t hw_type;

      uint16_t protocol;

      uint8_t hw_size;

      uint8_t protocol_size;

      uint16_t opcode;

      uint8_t source_hw_addr[MAC_ADDRESS_SIZE];

      uint8_t source_ip_addr[IP_ADDRESS_SIZE];

      uint8_t target_hw_addr[MAC_ADDRESS_SIZE];

      uint8_t target_ip_addr[IP_ADDRESS_SIZE];

};

 

struct ip_packet {

      uint8_t version;

      uint8_t ToS;

      uint16_t length;

      uint16_t identification;

      uint16_t fragment_offset;

      uint8_t TTL;

      uint8_t protocol;

      uint16_t checksum;

      uint8_t source_ip_addr[IP_ADDRESS_SIZE];

      uint8_t target_ip_addr[IP_ADDRESS_SIZE];

      uint8_t *data;

}

struct arp_cache_entry {

      uint8_t hw_addr[MAC_ADDRESS_SIZE];

      uint8_t ip_addr[IP_ADDRESS_SIZE];

      uint8_t timer;

};

 

uint8_t free_entry = 0;

 

struct arp_cache_entry arp_cache[ARP_CACHE_SIZE];

 

uint16_t detect_cs8900();

void init_cs8900();

uint16_t getRxLength();

uint16_t getFrame(uint8_t *);

void sendFrame(uint8_t *, uint16_t);

void initialize_eth(unsigned char *ip, unsigned char *netm, unsigned char *gate);

uint8_t *_ETH_getpacket(uint16_t *size);

void _ETH_mainloop();

uint8_t mytime = 0;

 

#ifdef FUDEBATOR

#asm

MACRO   getParInHL

        ex de,hl

ENDM

 

MACRO   getParInDE_BC

        ; yep! nothing!

ENDM

#endasm

#else

#asm

MACRO   getParInHL

      pop de

      pop hl            ; pointer to buffer

      push hl

      push de

ENDM

 

MACRO   getParInDE_BC

      pop bc

      pop de            ; buffer location

      pop hl            ; TxLength

      push hl

      push de

      push bc

ENDM

 

#endasm

#endif

 

#asm

PPPntrH           EQU   0Bh

PPPntrL           EQU   0Ah

 

MACRO setRegister, addr

      ld a, .high. addr

      out (0Bh), a

      ld a, .low. addr

      out (0Ah), a

ENDM

 

MACRO writeData, data

      ld a, .high. data

      out (0Dh), a

      ld a, .low. data

      out (0Ch), a

ENDM

 

MACRO readData

      in a,(0Dh)  ; loads data

      ld e, a

      in a,(0Ch)

      ld d, a

ENDM

#endasm

Text Box: End cs8900.h

 

 

Reference list

 

  1. UZIX, http://www.uzix.org
  2. Crystal CS8900 datasheets, http://www.cirrus.com/en/
  3. The Ultimate MSX FAQ, http://www.faq.msxnet.org/
  4. Kenneth Maxon , Have you seen my saudering iron?, http://www.seattlerobotics.org/encoder/200006/oven_art.htm

 

 

Index

 


assembly, 3, 9

baking, 9, 10

C, 2, 9

clock speed, 3

Compiler, 2

CPU power, 3

CS8900, 6, 7, 9, 10, 11, 26

Ethernet controller, 6

floppy, 10, 11

Hitech-C, 2

I/O mode, 9

Linux, 3, 10, 12

market analysis, 11

Memory Mode, 9

MSX, 1, 2, 3, 6, 10, 11, 26

MSX-DOS, 9

Nemesis, 3

network, 3

networking device companies, 12

Panasonic MSX Turbo-R, 3

R800 CPU, 3

UZIX, 2, 3, 9, 10, 26

Z-80, 2, 3