Skip to content

Telenet Server on the Z80

February 12, 2016

16-02-12 Zdonuts

This is actually the bad Telnet code just ported over to the Z80 – it doesn’t use the driver framework – it’s single-user with a blocking tgetline.

I’m glad I did it though, I learned something about Z88dk/SDCC I/O conventions that will simplify my code. I had been using hand-coded in/out(port,value) routines to do basic Z80 I/O. SDCC has a concept of Special Function Registers(sfr’s) that you can declare and use like the following:

__sfr __at 0x81    spiSMB;    //  spi second data byte for 16 bit transfers
spiSMB=value;
    instead of
out(0x81,value);

As with the earlier spi experiment the changes needed to go from 1802 to Z80 were mostly at the lowest level in hspi2.c although I put the special function register declarations in hspi2.h and w5500code had to be changed to accommodate the little-endian orientation of the Z80. I have dropped back to having the spi routines completely in C which i bet will be fast enough with the sfr’s effectively inlining the I/O. For the drivers I may need to lower them again.

//from hspi2.h
#ifdef __CPUTYPEZ80
void	spiInit();	//spi initialization - z80/tp3465 only
__sfr __at 0x81            spiSMB;    //  spi second data byte for 16 bit transfers
__sfr __at 0x81            spiFMB;    //  spi first data byte
__sfr __at 0x8d            spiCKR;    //  spi clock rate register
__sfr __at 0x8a            spiCSR;    //  spi chip select register
__sfr __at 0x8f            spiPDR;    //  spi port direction register
#endif
//from hspi2.c
#ifdef __CPUTYPEZ80
void spiInit(){//spi hardware init - Z80 only
	spiPDR=0xfe;	//port direction CS0 is output
	spiCSR=0xFF;	//cs0 inactive
	spiCKR=0x00;	//clockrate is 4mhz
}

void spiSend(unsigned char value){ //this is for output only
	spiFMB=value;
}
void spiSendN(unsigned char* loc, unsigned int N){ //this is for output only
	while(N!=0){
		spiFMB=*loc;
		N--;
		loc++;
	}
}
void spiReceiveN(unsigned char* loc, unsigned int N){ //this is for output only
	while(N!=0){
		spiFMB=N&0xFF;
		N--; //this is deliberately separating the write and read 
		*loc=spiFMB;
		loc++;
	}
}
#endif



/*****************************************************************************
//  File Name    : w5500Zdonuts.c
//  Version      : 1
//  Description  : Donuts Game over Telnet on Z80
//  Author       : Bill Rowe
//  Target       : Olduino/Z
//  Compiler     : Z88dk/SDCC
//  Created		 : Feb 11,2016
*****************************************************************************/
#include <stdio.h> //for printf etc.
#include <stdlib.h>
#include <string.h>
#include <cpuz80spd4port40.h> //defines processor type, speed, and host port
#include <olduino.h> //for digitalRead, digitalWrite, delay
#include <hspi2.h> //hardware spi header
#include <w5500data.h>  //wiznet definitions
#include <w5500simplestcode.h>  //wiznet code definitions
union IPaddr thisip={182}; //the ip that a form/request came from
#define MAX_BUF 512
unsigned char buf[MAX_BUF];			//memory buffer for incoming & outgoing data
	static unsigned char Inst1[]=
		"I AM THINKING OF A 3 DIGIT NUMBER.\n\rTRY TO GUESS " //50
		"MY NUMBER AND I WILL GIVE YOU CLUES AS FOLLOWS:\n\r";
	static unsigned char Inst2[]=
		"...PICO - ONE DIGIT IS IN THE WRONG PLACE\n\r"
		"...FERMI - ONE DIGIT IS IN THE CORRECT PLACE\n\r"
		"...DOUGHNUTS - NO DIGIT IS CORRECT\n\r";
unsigned char secret[4]="145";
unsigned char guess [4]={0};
unsigned int pages=0, games=0, sessions=0,turns=0;
char pnbuf[8];
unsigned int tbrnd(){//random numbers ala tiny basic R:=R*2345+6789
	static unsigned int R=1;
	R=R*58653+13849;//R=R*2345+6789;
	//printf("%d\n",R);
	return R;
}
void setsecret(){//set the global secret to a 3 digit random number
	int i=0,d1=0,d2,d3;
	while(d1==0){
		d1=tbrnd()%10;
	}
	d2=tbrnd()%10;
	while(d1==d2){
		d2=tbrnd()%10;
	}
	d3=tbrnd()%10;
	while(d3==d1||d3==d2){
		d3=tbrnd()%10;
	}
	secret[0]=d1+0x30;secret[1]=d2+0x30;secret[2]=d3+0x30;secret[3]=0;
	printf("secret=%s\n",secret);
}
void bagelsinit(){
	games++;
	pages++;
	setsecret();
	printf("IP: %d.%d.%d.%d,slot %d,secret %s\n",
		thisip.c[0],thisip.c[1],thisip.c[2],thisip.c[3],0,secret);
}
void phexblk(unsigned char * buf,unsigned int len){
	int i;
	for (i=0;i<len;i++){
	printf("%cx",buf[i]);
	}
}
unsigned char tgetcraw(){//get a raw character from telnet
	//unsigned int rsize;
	while(wizGetCtl16(SnRX_RSR)==0);
	//rsize=wizGetCtl16(SnRX_RSR); //get the size of the received data
	//printf("rawsize=%d ",rsize);
	recv0(buf,1);flush(1);
	//printf(" rawchar=%cx\n",rsize,buf[0]);
	return buf[0];
}
unsigned char tgetchar(){//get a character from telnet
	unsigned char craw;
	craw=tgetcraw();
	while (craw==255){//negotiation sequence
		craw=tgetcraw();craw=tgetcraw();
		craw=tgetcraw();
	}
	return craw;
}
unsigned int tgetline(unsigned char * linebuf, unsigned int maxlen){
	unsigned char c,tgotlen=0;
	c=tgetchar();
	while(c!='\r' && c!='\n'){
		*linebuf=c;
		if (tgotlen<maxlen){
			linebuf++; tgotlen++;
		}
		c=tgetchar();
	}
	if (c='\r'){c=tgetchar();} //swallow the lf of a cr,lf pair
	*linebuf=0; //terminate linebuf
	return tgotlen;
}
void sendresp(int pico, int fermi){
	unsigned int i;
	pages++;
	sendlit("  I say ");
	if (fermi==3){
		sendlit("YOU GOT IT!\r\n");
	} else {//once we're here we're going to send the guess form
		if (pico<0){//flag for duplicate digits
			sendconst("Oh, maybe I didn't tell you.  No two digits are the same.\r\n");
		} else if ((pico+fermi)==0){
			sendlit("Doughnuts!");
		} else{
			for(i=0;i<pico;i++) sendlit("Pico ");
			for(i=0;i<fermi;i++) sendlit("Fermi ");
		}
		sendlit("\r\n");
		sendconst("Try Again\r\n:"); 	// ask for another guess
	}
}
void bagelsturn(unsigned char guess[]){
	int i, /*ipslot,*/ pico=0, fermi=0;
	turns++; //count the turn
	for (i=0;i<3;i++){
		if (guess[i]==secret[i]){
			fermi++;
		}
	}
	if (guess[0]==secret[1]||guess[0]==secret[2]){pico++;}
	if (guess[1]==secret[0]||guess[1]==secret[2]){pico++;}
	if (guess[2]==secret[0]||guess[2]==secret[1]){pico++;}
	if (guess[0]==guess[1]||guess[0]==guess[2]||guess[1]==guess[2]||strlen((char *)guess)!=3){pico=-1;}
	sendresp(pico, fermi);
}

void handlesession(){
	unsigned char line[32]; unsigned int linelen;
	bagelsinit();
	sendconst(Inst1); sendconst(Inst2);
	linelen=tgetline(line,32);
	while((linelen<4)||strcmp("quit",line)!=0){
		bagelsturn(line);
		linelen=tgetline(line,32);
	}
	sendconst("I Say Byebye\r\n ");
	wizCmd(CR_DISCON);// Disconnect the connection- we're done here
}

void main(void){
	unsigned char socket0status,connectstatus=0,listening=0,prevstat=0xff;
    unsigned char ip_addr[] = {169,254,180,2};
	delay(500);
	spiInit();
	enablewiz();
    wiz_Init(ip_addr); //initialize the wiznet chip
	while(1){  // Loop forever
		socket0status=wizGetCtl8(SnSR); //socket 0 status
		if (socket0status!=prevstat){
			printf("s0 stat change to %X\n\r",socket0status);
			prevstat=socket0status;
		}
		switch (socket0status){
			case SOCK_CLOSED: //initial condition
				socket0_init();	//initialize socket 0
				break;
			case SOCK_LISTEN:
				listening=1;
				break;
			case SOCK_ESTABLISHED: //someone wants to talk to the server
				if(connectstatus==0){
					connectstatus=1;
					thisip.l=getip();
					printf("Hello to: %d.%d.%d.%d\n",
							thisip.c[0],thisip.c[1],thisip.c[2],thisip.c[3]);
					sendconst("Oh Hello!!\n\r");
				}
				handlesession();
				break;
			//following are cases where we have to reset and reopen the socket
			case SOCK_FIN_WAIT: case SOCK_CLOSING: case SOCK_TIME_WAIT:
			case SOCK_CLOSE_WAIT: case SOCK_LAST_ACK:
				wizCmd(CR_CLOSE);
				break;
		}
		delay(100);
	}
}
#include <olduino.c>
#include <hspi2z88dk.c>
#include <w5500simplestcode.c>
Advertisements
Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: