MSX Network Interface
Bruno Barreyra
David Regal
Final Report [ ]
Topic Index:_______________
Compare
proposed objectives and quantitative expectations with achieved
Project goals:
Pedagogical goals:
Required knowledge:
Tools:
Prototype:
Resources:
Fabrication:
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.
Meetings:
We will
have meetings after deadlines usually on Thursday nights.
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
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.
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].
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
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.
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.
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:
|
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
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:
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.
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.
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
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.
#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; /*
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
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
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