Merge branch 'master' of git://github.com/deater/dos33fsprogs

This commit is contained in:
Vince Weaver 2017-01-04 14:08:50 -05:00
commit f1aab6e953
12 changed files with 1007 additions and 436 deletions

View File

@ -1,6 +1,7 @@
include ../Makefile.inc include ../Makefile.inc
all: asoft_detoken tokenize_asoft integer_detoken asoft_compact all: asoft_detoken tokenize_asoft integer_detoken asoft_compact bin2data
asoft_compact: asoft_compact.o asoft_compact: asoft_compact.o
$(CC) $(LFLAGS) -o asoft_compact asoft_compact.o $(CC) $(LFLAGS) -o asoft_compact asoft_compact.o
@ -14,6 +15,12 @@ asoft_detoken: asoft_detoken.o
asoft_detoken.o: asoft_detoken.c asoft_detoken.o: asoft_detoken.c
$(CC) $(CFLAGS) -c asoft_detoken.c $(CC) $(CFLAGS) -c asoft_detoken.c
bin2data: bin2data.o
$(CC) $(LFLAGS) -o bin2data bin2data.o
bin2data.o: bin2data.c
$(CC) $(CFLAGS) -c bin2data.c
integer_detoken: integer_detoken.o integer_detoken: integer_detoken.o
$(CC) $(LFLAGS) -o integer_detoken integer_detoken.o $(CC) $(LFLAGS) -o integer_detoken integer_detoken.o
@ -31,7 +38,7 @@ install:
cp asoft_detoken tokenize_asoft integer_detoken $(INSTALL_LOC) cp asoft_detoken tokenize_asoft integer_detoken $(INSTALL_LOC)
clean: clean:
rm -f *~ *.o asoft_detoken tokenize_asoft \ rm -f *~ *.o asoft_detoken tokenize_asoft bin2data \
integer_detoken asoft_compact integer_detoken asoft_compact

View File

@ -29,3 +29,10 @@ asoft_compact: tries to compress your Applesoft basic program
to make it as small as possible to make it as small as possible
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bin2data: takes binary image and converts it to suitable
BASIC to poke into memory.
Useful for getting machine language routines usable
in BASIC programs

View File

@ -0,0 +1,55 @@
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
int main(int argc, char **argv) {
int address=0x300;
int bytes=0,line=10,i;
struct stat file_info;
int fd;
unsigned char c;
if (argc<2) {
printf("Usage:\t%s binfile [addr]\n\n",argv[0]);
return -1;
}
if (argc>2) {
address=strtol(argv[2],NULL,0);
}
if (stat(argv[1],&file_info)<0) {
fprintf(stderr,"Could not stat file %s\n\n",argv[1]);
return -1;
}
bytes=(int)file_info.st_size;
fd=open(argv[1],O_RDONLY);
if (fd<0) {
fprintf(stderr,"Could not open file %s\n\n",argv[1]);
return -1;
}
printf("%d FOR I=0 TO %d: READ X: POKE %d+I,X:NEXT I\n",
line,bytes-1,address);
line+=10;
for(i=0;i<bytes;i++) {
read(fd,&c,1);
if (i%16==0) {
printf("%d DATA ",line);
line+=10;
}
printf("%d",c);
if ((i%16!=15) && (i!=(bytes-1))) printf(",");
else printf("\n");
}
close(fd);
return 0;
}

View File

@ -14,7 +14,7 @@ dos33: dos33.o
$(CC) $(LFLAGS) -o dos33 dos33.o $(CC) $(LFLAGS) -o dos33 dos33.o
dos33.o: dos33.c dos33.h dos33.o: dos33.c dos33.h
$(CC) $(CFLAGS) -c dos33.c $(CC) $(CFLAGS) -g -c dos33.c
dos33_text2ascii: dos33_text2ascii.o dos33_text2ascii: dos33_text2ascii.o
$(CC) $(LFLGAS) -o dos33_text2ascii dos33_text2ascii.o $(CC) $(LFLGAS) -o dos33_text2ascii dos33_text2ascii.o

View File

@ -429,10 +429,14 @@ found_one:
#define ERROR_IMAGE_NOT_FOUND 4 #define ERROR_IMAGE_NOT_FOUND 4
#define ERROR_CATALOG_FULL 5 #define ERROR_CATALOG_FULL 5
#define ADD_RAW 0
#define ADD_BINARY 1
/* creates file apple_filename on the image from local file filename */ /* creates file apple_filename on the image from local file filename */
/* returns ?? */ /* returns ?? */
static int dos33_add_file(int fd,char type,char *filename, static int dos33_add_file(int fd, char dos_type,
char *apple_filename) { int file_type, int address, int length,
char *filename, char *apple_filename) {
int free_space,file_size,needed_sectors; int free_space,file_size,needed_sectors;
struct stat file_info; struct stat file_info;
@ -441,6 +445,7 @@ static int dos33_add_file(int fd,char type,char *filename,
int catalog_track,catalog_sector,sectors_used=0; int catalog_track,catalog_sector,sectors_used=0;
int input_fd; int input_fd;
int result; int result;
int first_write=1;
if (apple_filename[0]<64) { if (apple_filename[0]<64) {
fprintf(stderr,"Error! First char of filename " fprintf(stderr,"Error! First char of filename "
@ -471,6 +476,12 @@ static int dos33_add_file(int fd,char type,char *filename,
if (debug) printf("Filesize: %d\n",file_size); if (debug) printf("Filesize: %d\n",file_size);
if (file_type==ADD_BINARY) {
if (debug) printf("Adding 4 bytes for size/offset\n");
if (length==0) length=file_size;
file_size+=4;
}
/* We need to round up to nearest sector size */ /* We need to round up to nearest sector size */
/* Add an extra sector for the T/S list */ /* Add an extra sector for the T/S list */
/* Then add extra sector for a T/S list every 122*256 bytes (~31k) */ /* Then add extra sector for a T/S list every 122*256 bytes (~31k) */
@ -506,88 +517,105 @@ static int dos33_add_file(int fd,char type,char *filename,
i=0; i=0;
while (i<size_in_sectors) { while (i<size_in_sectors) {
/* Create new T/S list if necessary */ /* Create new T/S list if necessary */
if (i%TSL_MAX_NUMBER==0) { if (i%TSL_MAX_NUMBER==0) {
old_ts_list=ts_list; old_ts_list=ts_list;
/* allocate a sector for the new list */ /* allocate a sector for the new list */
ts_list=dos33_allocate_sector(fd); ts_list=dos33_allocate_sector(fd);
sectors_used++;
if (ts_list<0) return -1;
/* clear the t/s sector */
for(x=0;x<BYTES_PER_SECTOR;x++) {
sector_buffer[x]=0;
}
lseek(fd,DISK_OFFSET((ts_list>>8)&0xff,ts_list&0xff),SEEK_SET);
result=write(fd,sector_buffer,BYTES_PER_SECTOR);
if (i==0) {
initial_ts_list=ts_list;
}
else {
/* we aren't the first t/s list so do special stuff */
/* load in the old t/s list */
lseek(fd,
DISK_OFFSET(get_high_byte(old_ts_list),
get_low_byte(old_ts_list)),
SEEK_SET);
result=read(fd,&sector_buffer,BYTES_PER_SECTOR);
/* point from old ts list to new one we just made */
sector_buffer[TSL_NEXT_TRACK]=get_high_byte(ts_list);
sector_buffer[TSL_NEXT_SECTOR]=get_low_byte(ts_list);
/* set offset into file */
sector_buffer[TSL_OFFSET_H]=get_high_byte((i-122)*256);
sector_buffer[TSL_OFFSET_L]=get_low_byte((i-122)*256);
/* write out the old t/s list with updated info */
lseek(fd,
DISK_OFFSET(get_high_byte(old_ts_list),
get_low_byte(old_ts_list)),
SEEK_SET);
result=write(fd,sector_buffer,BYTES_PER_SECTOR);
}
}
/* allocate a sector */
data_ts=dos33_allocate_sector(fd);
sectors_used++; sectors_used++;
if (ts_list<0) return -1;
/* clear the t/s sector */ if (data_ts<0) return -1;
for(x=0;x<BYTES_PER_SECTOR;x++) sector_buffer[x]=0;
lseek(fd,DISK_OFFSET((ts_list>>8)&0xff,ts_list&0xff),SEEK_SET);
result=write(fd,sector_buffer,BYTES_PER_SECTOR);
if (i==0) initial_ts_list=ts_list;
else {
/* we aren't the first t/s list so do special stuff */
/* load in the old t/s list */ /* clear sector */
lseek(fd, for(x=0;x<BYTES_PER_SECTOR;x++) sector_buffer[x]=0;
DISK_OFFSET(get_high_byte(old_ts_list),
get_low_byte(old_ts_list)),
SEEK_SET);
result=read(fd,&sector_buffer,BYTES_PER_SECTOR); /* read from input */
if ((first_write) && (file_type==ADD_BINARY)) {
/* point from old ts list to new one we just made */ first_write=0;
sector_buffer[TSL_NEXT_TRACK]=get_high_byte(ts_list); sector_buffer[0]=address&0xff;
sector_buffer[TSL_NEXT_SECTOR]=get_low_byte(ts_list); sector_buffer[1]=(address>>8)&0xff;
sector_buffer[2]=(length)&0xff;
/* set offset into file */ sector_buffer[3]=((length)>>8)&0xff;
sector_buffer[TSL_OFFSET_H]=get_high_byte((i-122)*256); bytes_read=read(input_fd,sector_buffer+4,
sector_buffer[TSL_OFFSET_L]=get_low_byte((i-122)*256); BYTES_PER_SECTOR-4);
bytes_read+=4;
/* write out the old t/s list with updated info */ }
lseek(fd, else {
DISK_OFFSET(get_high_byte(old_ts_list), bytes_read=read(input_fd,sector_buffer,
get_low_byte(old_ts_list)), BYTES_PER_SECTOR);
SEEK_SET); }
first_write=0;
result=write(fd,sector_buffer,BYTES_PER_SECTOR); if (bytes_read<0) fprintf(stderr,"Error reading bytes!\n");
}
}
/* allocate a sector */
data_ts=dos33_allocate_sector(fd);
sectors_used++;
if (data_ts<0) return -1;
/* clear sector */
for(x=0;x<BYTES_PER_SECTOR;x++) sector_buffer[x]=0;
/* read from input */ /* write to disk image */
bytes_read=read(input_fd,sector_buffer,BYTES_PER_SECTOR); lseek(fd,DISK_OFFSET((data_ts>>8)&0xff,data_ts&0xff),SEEK_SET);
if (bytes_read<0) fprintf(stderr,"Error reading bytes!\n"); result=write(fd,sector_buffer,BYTES_PER_SECTOR);
/* write to disk image */ if (debug) printf("Writing %i bytes to %i/%i\n",
lseek(fd,DISK_OFFSET((data_ts>>8)&0xff,data_ts&0xff),SEEK_SET); bytes_read,(data_ts>>8)&0xff,data_ts&0xff);
result=write(fd,sector_buffer,BYTES_PER_SECTOR);
// printf("Writing %i bytes to %i/%i\n",bytes_read,(data_ts>>8)&0xff,
// data_ts&0xff);
/* add to T/s table */
/* add to T/s table */
/* read in t/s list */
/* read in t/s list */ lseek(fd,DISK_OFFSET((ts_list>>8)&0xff,ts_list&0xff),SEEK_SET);
lseek(fd,DISK_OFFSET((ts_list>>8)&0xff,ts_list&0xff),SEEK_SET); result=read(fd,sector_buffer,BYTES_PER_SECTOR);
result=read(fd,sector_buffer,BYTES_PER_SECTOR);
/* point to new data sector */
/* point to new data sector */ sector_buffer[((i%TSL_MAX_NUMBER)*2)+TSL_LIST]=(data_ts>>8)&0xff;
sector_buffer[((i%TSL_MAX_NUMBER)*2)+TSL_LIST]=(data_ts>>8)&0xff; sector_buffer[((i%TSL_MAX_NUMBER)*2)+TSL_LIST+1]=(data_ts&0xff);
sector_buffer[((i%TSL_MAX_NUMBER)*2)+TSL_LIST+1]=(data_ts&0xff);
/* write t/s list back out */
/* write t/s list back out */ lseek(fd,DISK_OFFSET((ts_list>>8)&0xff,ts_list&0xff),SEEK_SET);
lseek(fd,DISK_OFFSET((ts_list>>8)&0xff,ts_list&0xff),SEEK_SET); result=write(fd,sector_buffer,BYTES_PER_SECTOR);
result=write(fd,sector_buffer,BYTES_PER_SECTOR);
i++;
i++; }
}
/* Add new file to Catalog */ /* Add new file to Catalog */
@ -632,43 +660,43 @@ continue_parsing_catalog:
goto continue_parsing_catalog; goto continue_parsing_catalog;
got_a_dentry: got_a_dentry:
// printf("Adding file at entry %i of catalog 0x%x:0x%x\n", // printf("Adding file at entry %i of catalog 0x%x:0x%x\n",
// i,catalog_track,catalog_sector); // i,catalog_track,catalog_sector);
/* Point entry to initial t/s list */
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)]=(initial_ts_list>>8)&0xff;
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+1]=(initial_ts_list&0xff);
/* set file type */
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_TYPE]=
dos33_char_to_type(type,0);
// printf("Pointing T/S to %x/%x\n",(initial_ts_list>>8)&0xff,initial_ts_list&0xff); /* Point entry to initial t/s list */
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)]=(initial_ts_list>>8)&0xff;
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+1]=(initial_ts_list&0xff);
/* set file type */
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_TYPE]=
dos33_char_to_type(dos_type,0);
/* copy over filename */ // printf("Pointing T/S to %x/%x\n",(initial_ts_list>>8)&0xff,initial_ts_list&0xff);
for(x=0;x<strlen(apple_filename);x++) {
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_NAME+x]=
apple_filename[x]^0x80;
}
/* pad out the filename with spaces */
for(x=strlen(apple_filename);x<FILE_NAME_SIZE;x++) {
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_NAME+x]=' '^0x80;
}
/* fill in filesize in sectors */
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_SIZE_L]=
sectors_used&0xff;
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_SIZE_H]=
(sectors_used>>8)&0xff;
/* write out catalog sector */
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
result=write(fd,sector_buffer,BYTES_PER_SECTOR);
if (result<0) fprintf(stderr,"Error on I/O\n"); /* copy over filename */
for(x=0;x<strlen(apple_filename);x++) {
return 0; sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_NAME+x]=
apple_filename[x]^0x80;
}
/* pad out the filename with spaces */
for(x=strlen(apple_filename);x<FILE_NAME_SIZE;x++) {
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_NAME+x]=' '^0x80;
}
/* fill in filesize in sectors */
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_SIZE_L]=
sectors_used&0xff;
sector_buffer[CATALOG_FILE_LIST+(i*CATALOG_ENTRY_SIZE)+FILE_SIZE_H]=
(sectors_used>>8)&0xff;
/* write out catalog sector */
lseek(fd,DISK_OFFSET(catalog_track,catalog_sector),SEEK_SET);
result=write(fd,sector_buffer,BYTES_PER_SECTOR);
if (result<0) fprintf(stderr,"Error on I/O\n");
return 0;
} }
/* load a file. fts=entry/track/sector */ /* load a file. fts=entry/track/sector */
@ -1174,10 +1202,13 @@ static int dos33_rename_hello(int fd, char *new_name) {
return 0; return 0;
} }
static int display_help(char *name) { static void display_help(char *name, int version_only) {
printf("\ndos33 version %s\n",VERSION); printf("\ndos33 version %s\n",VERSION);
printf("by Vince Weaver <vince@deater.net>\n"); printf("by Vince Weaver <vince@deater.net>\n");
printf("\n"); printf("\n");
if (version_only) return;
printf("Usage: %s [-h] [-y] disk_image COMMAND [options]\n",name); printf("Usage: %s [-h] [-y] disk_image COMMAND [options]\n",name);
printf("\t-h : this help message\n"); printf("\t-h : this help message\n");
printf("\t-y : always answer yes for anying warning questions\n"); printf("\t-y : always answer yes for anying warning questions\n");
@ -1187,7 +1218,7 @@ static int display_help(char *name) {
printf("\tCATALOG\n"); printf("\tCATALOG\n");
printf("\tLOAD apple_file <local_file>\n"); printf("\tLOAD apple_file <local_file>\n");
printf("\tSAVE type local_file <apple_file>\n"); printf("\tSAVE type local_file <apple_file>\n");
printf("\tBSAVE type local_file <apple_file>\n"); printf("\tBSAVE [-a addr] [-l len] local_file <apple_file>\n");
printf("\tDELETE apple_file\n"); printf("\tDELETE apple_file\n");
printf("\tLOCK apple_file\n"); printf("\tLOCK apple_file\n");
printf("\tUNLOCK apple_file\n"); printf("\tUNLOCK apple_file\n");
@ -1200,24 +1231,26 @@ static int display_help(char *name) {
printf("\tCOPY\n"); printf("\tCOPY\n");
#endif #endif
printf("\n"); printf("\n");
return 0; return;
} }
#define COMMAND_UNKNOWN 0 #define COMMAND_LOAD 0
#define COMMAND_LOAD 1 #define COMMAND_SAVE 1
#define COMMAND_SAVE 2 #define COMMAND_CATALOG 2
#define COMMAND_CATALOG 3 #define COMMAND_DELETE 3
#define COMMAND_DELETE 4 #define COMMAND_UNDELETE 4
#define COMMAND_UNDELETE 5 #define COMMAND_LOCK 5
#define COMMAND_LOCK 6 #define COMMAND_UNLOCK 6
#define COMMAND_UNLOCK 7 #define COMMAND_INIT 7
#define COMMAND_INIT 8 #define COMMAND_RENAME 8
#define COMMAND_RENAME 9 #define COMMAND_COPY 9
#define COMMAND_COPY 10 #define COMMAND_DUMP 10
#define COMMAND_DUMP 11 #define COMMAND_HELLO 11
#define COMMAND_HELLO 12 #define COMMAND_BSAVE 12
#define COMMAND_BSAVE 13 #define COMMAND_BLOAD 13
#define MAX_COMMAND 14 #define MAX_COMMAND 14
#define COMMAND_UNKNOWN 255
static struct command_type { static struct command_type {
int type; int type;
@ -1242,7 +1275,7 @@ static int lookup_command(char *name) {
int which=COMMAND_UNKNOWN,i; int which=COMMAND_UNKNOWN,i;
for(i=1;i<MAX_COMMAND;i++) { for(i=0;i<MAX_COMMAND;i++) {
if(!strncmp(name,commands[i].name,strlen(commands[i].name))) { if(!strncmp(name,commands[i].name,strlen(commands[i].name))) {
which=commands[i].type; which=commands[i].type;
break; break;
@ -1252,6 +1285,21 @@ static int lookup_command(char *name) {
} }
static int truncate_filename(char *out, char *in) {
int truncated=0;
/* Truncate filename if too long */
if (strlen(in)>30) {
fprintf(stderr,"Warning! Truncating %s to 30 chars\n",in);
truncated=1;
}
strncpy(out,in,30);
out[30]='\0';
return truncated;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
char image[BUFSIZ]; char image[BUFSIZ];
@ -1261,85 +1309,105 @@ int main(int argc, char **argv) {
int command,catalog_entry; int command,catalog_entry;
char temp_string[BUFSIZ]; char temp_string[BUFSIZ];
char apple_filename[31],new_filename[31]; char apple_filename[31],new_filename[31];
char output_filename[BUFSIZ]; char local_filename[BUFSIZ];
char *result_string; char *result_string;
int always_yes=0,firstarg=1,extra_ops=0; int always_yes=0;
char *temp,*endptr;
int c;
int address=0, length=0;
/* Check command line arguments */ /* Check command line arguments */
/* Ugh I should use getopt() or something similar here */ while ((c = getopt (argc, argv,"a:l:hvy"))!=-1) {
switch (c) {
if (argc<2) { case 'a':
display_help(argv[0]); address=strtol(optarg,&endptr,0);
goto exit_program; if (debug) printf("Address=%d\n",address);
break;
case 'l':
length=strtol(optarg,&endptr,0);
if (debug) printf("Length=%d\n",address);
break;
case 'v':
display_help(argv[0],1);
return 0;
case 'h': display_help(argv[0],0);
return 0;
case 'y':
always_yes=1;
break;
}
} }
if (!strncmp(argv[1],"-h",2)) { if (optind==argc) {
display_help(argv[1]); fprintf(stderr,"ERROR! Must specify disk image!\n\n");
goto exit_program; return -1;
}
if (!strncmp(argv[1],"-y",2)) {
always_yes=1;
extra_ops++;
firstarg++;
}
if (argc<3) {
printf("\nInvalid arguments!\n");
display_help(argv[0]);
goto exit_program;
} }
/* get argument 1, which is image name */ /* get argument 1, which is image name */
strncpy(image,argv[firstarg],BUFSIZ); strncpy(image,argv[optind],BUFSIZ);
dos_fd=open(image,O_RDWR); dos_fd=open(image,O_RDWR);
if (dos_fd<0) { if (dos_fd<0) {
fprintf(stderr,"Error opening disk_image: %s\n",image); fprintf(stderr,"Error opening disk_image: %s\n",image);
exit(4); return -1;
} }
/* Check argument #2 which is command */ /* Move to next argument */
strncpy(temp_string,argv[firstarg+1],BUFSIZ); optind++;
if (optind==argc) {
fprintf(stderr,"ERROR! Must specify command!\n\n");
return -2;
}
/* Grab command */
strncpy(temp_string,argv[optind],BUFSIZ);
/* Make command be uppercase */ /* Make command be uppercase */
for(i=0;i<strlen(temp_string);i++) { for(i=0;i<strlen(temp_string);i++) {
temp_string[i]=toupper(temp_string[i]); temp_string[i]=toupper(temp_string[i]);
} }
/* Move to next argument */
optind++;
command=lookup_command(temp_string); command=lookup_command(temp_string);
switch(command) { switch(command) {
case COMMAND_UNKNOWN: case COMMAND_UNKNOWN:
display_help(argv[0]); fprintf(stderr,"ERROR! Unknown command %s\n",temp_string);
goto exit_program; fprintf(stderr,"\tTry \"%s -h\" for help.\n\n",argv[0]);
goto exit_and_close;
break; break;
/* Load a file from disk image to local machine */ /* Load a file from disk image to local machine */
case COMMAND_LOAD: case COMMAND_LOAD:
/* check and make sure we have apple_filename */ /* check and make sure we have apple_filename */
if (argc<4+extra_ops) { if (argc==optind) {
fprintf(stderr,"Error! Need apple file_name\n"); fprintf(stderr,"Error! Need apple file_name\n");
fprintf(stderr,"%s %s LOAD apple_filename\n", fprintf(stderr,"%s %s LOAD apple_filename\n",
argv[0],image); argv[0],image);
goto exit_and_close; goto exit_and_close;
} }
/* Truncate filename if too long */
if (strlen(argv[firstarg+2])>30) { truncate_filename(apple_filename,argv[optind]);
fprintf(stderr,"Warning! Truncating %s to 30 chars\n",
argv[firstarg+2]); if (debug) printf("\tApple filename: %s\n",apple_filename);
}
strncpy(apple_filename,argv[firstarg+2],30);
apple_filename[30]='\0';
/* get output filename */ /* get output filename */
if (argc==5+extra_ops) { optind++;
strncpy(output_filename,argv[firstarg+3],BUFSIZ); if (argc>=optind) {
strncpy(local_filename,argv[optind],BUFSIZ);
} }
else { else {
strncpy(output_filename,apple_filename,30); strncpy(local_filename,apple_filename,30);
} }
if (debug) printf("\tOutput filename: %s\n",local_filename);
/* get the entry/track/sector for file */ /* get the entry/track/sector for file */
catalog_entry=dos33_check_file_exists(dos_fd, catalog_entry=dos33_check_file_exists(dos_fd,
apple_filename, apple_filename,
@ -1350,243 +1418,276 @@ int main(int argc, char **argv) {
goto exit_and_close; goto exit_and_close;
} }
dos33_load_file(dos_fd,catalog_entry,output_filename); dos33_load_file(dos_fd,catalog_entry,local_filename);
break; break;
case COMMAND_CATALOG: case COMMAND_CATALOG:
/* get first catalog */ /* get first catalog */
catalog_entry=dos33_get_catalog_ts(dos_fd); catalog_entry=dos33_get_catalog_ts(dos_fd);
printf("\nDISK VOLUME %i\n\n",sector_buffer[VTOC_DISK_VOLUME]); printf("\nDISK VOLUME %i\n\n",sector_buffer[VTOC_DISK_VOLUME]);
while(catalog_entry>0) { while(catalog_entry>0) {
catalog_entry=dos33_find_next_file(dos_fd,catalog_entry); catalog_entry=dos33_find_next_file(dos_fd,catalog_entry);
if (catalog_entry>0) { if (catalog_entry>0) {
dos33_print_file_info(dos_fd,catalog_entry); dos33_print_file_info(dos_fd,catalog_entry);
catalog_entry+=(1<<16); /* why 1<<16 ? */
/* dos33_find_next_file() handles wrapping issues */ catalog_entry+=(1<<16);
} /* dos33_find_next_file() handles wrapping issues */
} }
printf("\n"); }
break; printf("\n");
break;
case COMMAND_SAVE: case COMMAND_SAVE:
/* argv3 == type == A,B,T,I,N,L etc */ /* argv3 == type == A,B,T,I,N,L etc */
/* argv4 == name of local file */ /* argv4 == name of local file */
/* argv5 == optional name of file on disk image */ /* argv5 == optional name of file on disk image */
if (argc<5+extra_ops) { if (argc==optind) {
fprintf(stderr,"Error! Need type and file_name\n"); fprintf(stderr,"Error! Need type and file_name\n");
fprintf(stderr,"%s %s SAVE type " fprintf(stderr,"%s %s SAVE type "
"file_name apple_filename\n", "file_name apple_filename\n\n",
argv[0],image); argv[0],image);
goto exit_and_close; goto exit_and_close;
} }
type=argv[firstarg+2][0]; type=argv[optind][0];
optind++;
case COMMAND_BSAVE:
if (debug) printf("\ttype=%c\n",type);
if (argc==optind) {
fprintf(stderr,"Error! Need file_name\n");
if (command==COMMAND_BSAVE) {
fprintf(stderr,"%s %s BSAVE "
"file_name apple_filename\n\n",
argv[0],image);
if (argc==6+extra_ops) {
if (strlen(argv[firstarg+4])>30) {
fprintf(stderr,
"Warning! Truncating filename "
"to 30 chars!\n");
} }
strncpy(apple_filename,argv[firstarg+4],30); else {
apple_filename[30]=0; fprintf(stderr,"%s %s SAVE type "
"file_name apple_filename\n\n",
argv[0],image);
}
goto exit_and_close;
}
strncpy(local_filename,argv[optind],BUFSIZ);
optind++;
if (debug) printf("\tLocal filename: %s\n",local_filename);
if (argc>optind) {
/* apple filename specified */
truncate_filename(apple_filename,argv[optind]);
} }
else { else {
/* If no filename specified for apple name */ /* If no filename specified for apple name */
/* Then use the input name. Note, we strip */ /* Then use the input name. Note, we strip */
/* everything up to the last slash so useless */ /* everything up to the last slash so useless */
/* path info isn't used */ /* path info isn't used */
{
char *temp;
temp=argv[firstarg+3]+(strlen(argv[firstarg+3])-1);
while(temp!=argv[firstarg+3]) {
temp--;
if (*temp == '/') {
temp++;
break;
}
}
if (strlen(temp)>30) {
fprintf(stderr,
"Warning! Truncating filename to 30 chars!\n");
}
strncpy(apple_filename,temp,30);
apple_filename[30]=0;
}
}
catalog_entry=dos33_check_file_exists(dos_fd,apple_filename,
FILE_NORMAL);
if (catalog_entry>=0) {
fprintf(stderr,"Warning! %s exists!\n",apple_filename);
if (!always_yes) {
printf("Over-write (y/n)?");
result_string=fgets(temp_string,BUFSIZ,stdin);
if ((result_string==NULL) || (temp_string[0]!='y')) {
printf("Exiting early...\n");
goto exit_and_close;
}
}
fprintf(stderr,"Deleting previous version...\n");
dos33_delete_file(dos_fd,catalog_entry);
}
dos33_add_file(dos_fd,type,argv[firstarg+3],apple_filename); temp=local_filename+(strlen(local_filename)-1);
while(temp!=local_filename) {
temp--;
if (*temp == '/') {
temp++;
break;
}
}
truncate_filename(apple_filename,temp);
}
if (debug) printf("\tApple filename: %s\n",apple_filename);
catalog_entry=dos33_check_file_exists(dos_fd,apple_filename,
FILE_NORMAL);
if (catalog_entry>=0) {
fprintf(stderr,"Warning! %s exists!\n",apple_filename);
if (!always_yes) {
printf("Over-write (y/n)?");
result_string=fgets(temp_string,BUFSIZ,stdin);
if ((result_string==NULL) || (temp_string[0]!='y')) {
printf("Exiting early...\n");
goto exit_and_close;
}
}
fprintf(stderr,"Deleting previous version...\n");
dos33_delete_file(dos_fd,catalog_entry);
}
if (command==COMMAND_SAVE) {
dos33_add_file(dos_fd,type,
ADD_RAW, address, length,
local_filename,apple_filename);
}
else {
dos33_add_file(dos_fd,type,
ADD_BINARY, address, length,
local_filename,apple_filename);
}
break;
case COMMAND_DELETE:
if (argc==optind) {
fprintf(stderr,"Error! Need file_name\n");
fprintf(stderr,"%s %s DELETE apple_filename\n",
argv[0],image);
goto exit_and_close;
}
truncate_filename(apple_filename,argv[optind]);
catalog_entry=dos33_check_file_exists(dos_fd,
apple_filename,
FILE_NORMAL);
if (catalog_entry<0) {
fprintf(stderr, "Error! File %s does not exist\n",
apple_filename);
goto exit_and_close;
}
dos33_delete_file(dos_fd,catalog_entry);
break; break;
case COMMAND_DELETE: case COMMAND_DUMP:
if (argc+extra_ops<4) { printf("Dumping %s!\n",image);
fprintf(stderr,"Error! Need file_name\n"); dos33_dump(dos_fd);
fprintf(stderr,"%s %s DELETE apple_filename\n",argv[0],image); break;
goto exit_and_close;
}
catalog_entry=dos33_check_file_exists(dos_fd,argv[firstarg+2],
FILE_NORMAL);
if (catalog_entry<0) {
fprintf(stderr,
"Error! File %s does not exist\n",argv[firstarg+2]);
goto exit_and_close;
}
dos33_delete_file(dos_fd,catalog_entry);
break;
case COMMAND_DUMP:
printf("Dumping %s!\n",argv[firstarg]);
dos33_dump(dos_fd);
break;
case COMMAND_LOCK:
case COMMAND_UNLOCK:
/* check and make sure we have apple_filename */
if (argc<4+extra_ops) {
fprintf(stderr,"Error! Need apple file_name\n");
fprintf(stderr,"%s %s LOCK apple_filename\n",argv[0],image);
goto exit_and_close;
}
/* Truncate filename if too long */
if (strlen(argv[firstarg+2])>30) {
fprintf(stderr,
"Warning! Truncating %s to 30 chars\n",argv[firstarg+2]);
}
strncpy(apple_filename,argv[firstarg+2],30);
apple_filename[30]='\0';
/* get the entry/track/sector for file */
catalog_entry=dos33_check_file_exists(dos_fd,
apple_filename,
FILE_NORMAL);
if (catalog_entry<0) {
fprintf(stderr,"Error! %s not found!\n",apple_filename);
goto exit_and_close;
}
dos33_lock_file(dos_fd,catalog_entry,command==COMMAND_LOCK); case COMMAND_LOCK:
case COMMAND_UNLOCK:
break; /* check and make sure we have apple_filename */
if (argc==optind) {
case COMMAND_RENAME: fprintf(stderr,"Error! Need apple file_name\n");
/* check and make sure we have apple_filename */ fprintf(stderr,"%s %s %s apple_filename\n",
if (argc<5+extra_ops) { argv[0],image,temp_string);
fprintf(stderr,"Error! Need two filenames\n"); goto exit_and_close;
fprintf(stderr,"%s %s LOCK apple_filename_old " }
"apple_filename_new\n",
argv[0],image);
goto exit_and_close;
}
/* Truncate filename if too long */
if (strlen(argv[firstarg+2])>30) {
fprintf(stderr,
"Warning! Truncating %s to 30 chars\n",argv[firstarg+2]);
}
strncpy(apple_filename,argv[firstarg+2],30);
apple_filename[30]='\0';
/* Truncate filename if too long */
if (strlen(argv[firstarg+3])>30) {
fprintf(stderr,
"Warning! Truncating %s to 30 chars\n",argv[firstarg+3]);
}
strncpy(new_filename,argv[firstarg+3],30);
new_filename[30]='\0';
/* get the entry/track/sector for file */
catalog_entry=dos33_check_file_exists(dos_fd,
apple_filename,
FILE_NORMAL);
if (catalog_entry<0) {
fprintf(stderr,"Error! %s not found!\n",apple_filename);
goto exit_and_close;
}
dos33_rename_file(dos_fd,catalog_entry,new_filename); truncate_filename(apple_filename,argv[optind]);
break; /* get the entry/track/sector for file */
catalog_entry=dos33_check_file_exists(dos_fd,
apple_filename,
FILE_NORMAL);
if (catalog_entry<0) {
fprintf(stderr,"Error! %s not found!\n",
apple_filename);
goto exit_and_close;
}
dos33_lock_file(dos_fd,catalog_entry,command==COMMAND_LOCK);
break;
case COMMAND_RENAME:
/* check and make sure we have apple_filename */
if (argc==optind) {
fprintf(stderr,"Error! Need two filenames\n");
fprintf(stderr,"%s %s LOCK apple_filename_old "
"apple_filename_new\n",
argv[0],image);
goto exit_and_close;
}
/* Truncate filename if too long */
truncate_filename(apple_filename,argv[optind]);
optind++;
if (argc==optind) {
fprintf(stderr,"Error! Need two filenames\n");
fprintf(stderr,"%s %s LOCK apple_filename_old "
"apple_filename_new\n",
argv[0],image);
goto exit_and_close;
}
truncate_filename(new_filename,argv[optind]);
/* get the entry/track/sector for file */
catalog_entry=dos33_check_file_exists(dos_fd,
apple_filename,
FILE_NORMAL);
if (catalog_entry<0) {
fprintf(stderr,"Error! %s not found!\n",
apple_filename);
goto exit_and_close;
}
dos33_rename_file(dos_fd,catalog_entry,new_filename);
break;
case COMMAND_UNDELETE: case COMMAND_UNDELETE:
/* check and make sure we have apple_filename */ /* check and make sure we have apple_filename */
if (argc<4) { if (argc==optind) {
fprintf(stderr,"Error! Need apple file_name\n"); fprintf(stderr,"Error! Need apple file_name\n");
fprintf(stderr,"%s %s LOCK apple_filename\n",argv[0],image); fprintf(stderr,"%s %s UNDELETE apple_filename\n\n",
argv[0],image);
goto exit_and_close; goto exit_and_close;
} }
/* Truncate filename if too long */ /* Truncate filename if too long */
/* what to do about last char ? */ /* what to do about last char ? */
if (strlen(argv[firstarg+2])>30) {
fprintf(stderr, truncate_filename(apple_filename,argv[optind]);
"Warning! Truncating %s to 30 chars\n",argv[firstarg+2]);
}
strncpy(apple_filename,argv[firstarg+2],30);
apple_filename[30]='\0';
/* get the entry/track/sector for file */ /* get the entry/track/sector for file */
catalog_entry=dos33_check_file_exists(dos_fd, catalog_entry=dos33_check_file_exists(dos_fd,
apple_filename, apple_filename,
FILE_DELETED); FILE_DELETED);
if (catalog_entry<0) { if (catalog_entry<0) {
fprintf(stderr,"Error! %s not found!\n",apple_filename); fprintf(stderr,"Error! %s not found!\n",
apple_filename);
goto exit_and_close; goto exit_and_close;
} }
dos33_undelete_file(dos_fd,catalog_entry,apple_filename); dos33_undelete_file(dos_fd,catalog_entry,apple_filename);
break; break;
case COMMAND_HELLO:
if (argc+extra_ops<4) { case COMMAND_HELLO:
fprintf(stderr,"Error! Need file_name\n"); if (argc==optind) {
fprintf(stderr,"%s %s HELLO apple_filename\n",argv[0],image); fprintf(stderr,"Error! Need file_name\n");
goto exit_and_close; fprintf(stderr,"%s %s HELLO apple_filename\n\n",
} argv[0],image);
catalog_entry=dos33_check_file_exists(dos_fd,argv[firstarg+2],
FILE_NORMAL);
if (catalog_entry<0) {
fprintf(stderr,
"Warning! File %s does not exist\n",argv[firstarg+2]);
}
dos33_rename_hello(dos_fd,argv[firstarg+2]);
break;
case COMMAND_INIT:
/* use common code from mkdos33fs? */
case COMMAND_COPY:
/* use temp file? Walking a sector at a time seems a pain */
default:
fprintf(stderr,"Sorry, unsupported command\n");
goto exit_and_close; goto exit_and_close;
}
truncate_filename(apple_filename,argv[optind]);
catalog_entry=dos33_check_file_exists(dos_fd,
apple_filename,
FILE_NORMAL);
if (catalog_entry<0) {
fprintf(stderr,
"Warning! File %s does not exist\n",
apple_filename);
}
dos33_rename_hello(dos_fd,apple_filename);
break;
case COMMAND_INIT:
/* use common code from mkdos33fs? */
case COMMAND_COPY:
/* use temp file? Walking a sector at a time seems a pain */
default:
fprintf(stderr,"Sorry, unsupported command %s\n\n",temp_string);
goto exit_and_close;
} }
exit_and_close: exit_and_close:
close(dos_fd); close(dos_fd);
exit_program:
return 0; return 0;
} }

View File

@ -1,2 +1,4 @@
Test hello
Test init
Test copy Test copy
Test over-writing existing file Test over-writing existing file

View File

@ -4,38 +4,33 @@ MAKEB = ../dos33fs-utils/make_b
all: ethernet.dsk all: ethernet.dsk
memcpy: memcpy.o
ld65 -o memcpy memcpy.o -t none
memcpy.o: memcpy.s
ca65 -o memcpy.o memcpy.s -l memcpy.lst
SETUP.BAS: setup.bas SETUP.BAS: setup.bas
$(TXT2BAS) < setup.bas > SETUP.BAS $(TXT2BAS) < setup.bas > SETUP.BAS
WEBSERVER.BAS: webserver.bas WEBSERVER.BAS: webserver.bas
$(TXT2BAS) < webserver.bas > WEBSERVER.BAS $(TXT2BAS) < webserver.bas > WEBSERVER.BAS
about.html: ./c/about.html
$(MAKEB) ./c/about.html about.html 0xc000
index.html: ./c/index.html
$(MAKEB) ./c/index.html index.html 0xc000
favicon.ico: ./c/favicon.ico
$(MAKEB) ./c/favicon.ico favicon.ico 0xc000
vmw_logo.png: ./c/vmw_logo.png
$(MAKEB) ./c/vmw_logo.png vmw_logo.png 0xc000
R.TXT: request.txt R.TXT: request.txt
$(MAKEB) request.txt R.TXT 0xc000 $(MAKEB) request.txt R.TXT 0xc000
ethernet.dsk: SETUP.BAS \ ethernet.dsk: SETUP.BAS \
WEBSERVER.BAS \ WEBSERVER.BAS \
R.TXT \ R.TXT \
about.html index.html favicon.ico vmw_logo.png ./c/about.html ./c/index.html ./c/favicon.ico ./c/vmw_logo.png
$(DOS33) -y ethernet.dsk SAVE A SETUP.BAS $(DOS33) -y ethernet.dsk SAVE A SETUP.BAS
$(DOS33) -y ethernet.dsk SAVE A WEBSERVER.BAS $(DOS33) -y ethernet.dsk SAVE A WEBSERVER.BAS
$(DOS33) -y ethernet.dsk SAVE B R.TXT $(DOS33) -y ethernet.dsk BSAVE R.TXT
$(DOS33) -y ethernet.dsk SAVE B about.html $(DOS33) -y ethernet.dsk BSAVE -a 0x4000 ./c/about.html
$(DOS33) -y ethernet.dsk SAVE B index.html $(DOS33) -y ethernet.dsk BSAVE -a 0x4000 ./c/index.html
$(DOS33) -y ethernet.dsk SAVE B favicon.ico $(DOS33) -y ethernet.dsk BSAVE -a 0x4000 ./c/favicon.ico
$(DOS33) -y ethernet.dsk SAVE B vmw_logo.png $(DOS33) -y ethernet.dsk BSAVE -a 0x4000 ./c/vmw_logo.png
clean: clean:
rm -f *~ *.BAS R.TXT rm -f *~ *.BAS R.TXT *.o *.lst memcpy

View File

@ -1,2 +1,29 @@
Working on getting the Uthernet II card to do something interesting. Working on getting the Uthernet II card to do something interesting.
http://a2retrosystems.com/
Hardware Background:
This board has a WIZnet W5100 on board. You can get raw Ethernet
packets on the board, but I am using it in hardware TCP/IP mode
where I set up the MAC/IP and then get raw packets from a TCP socket
(up to 4 sockets can be active at once).
Webserver:
Included is a webserver written for Fall 2015 ECE435
It is written entirely in Applesoft BASIC
It runs very slowly, but works.
firefox and wget can get files just fine,
although they tend to send duplicate requests for some reason.
You can serve arbitrary png, jpg, txt, or html files, however
they currently have to be less than 8kB.
Much of the slowness is using peek/poke as a memcpy routine.
Of course this would all be faster in assembly language, but
what's the fun of that.

Binary file not shown.

66
ethernet/memcpy.s Normal file
View File

@ -0,0 +1,66 @@
.define EQU =
PTR EQU $06
PTRH EQU $07
WRAPL EQU $08
WRAPH EQU $09
SIZEL EQU $0A
SIZEH EQU $0B
tx_copy:
lda #0 ; always copying from 0x4000
sta PTR
lda #$40
sta PTR+1
ldx #SIZEH ; number of 256-byte blocks
beq copy_remainder ; if none, skip ahead
ldy #0
copy256:
lda (PTR),y
sta $C0B7 ; change based on uthernet slot
cmp WRAPH,x
bne nowrap256
cmp WRAPL,y
bne nowrap256
lda #$40
sta $C0B5
lda #$00
sta $C0B6 ; wrap tx buffer address to 0x4000
nowrap256:
iny
bne copy256
inc PTR+1 ; update 16-bit pointer
dex ; finish a 256 byte block
bne copy256
ldx #SIZEL
copy_remainder:
lda (PTR),y
sta $C0B7 ; change based on uthernet slot
cmp WRAPL,y
bne nowrap_r
lda #$40
sta $C0B5
lda #$00
sta $C0B6 ; wrap tx buffer address to 0x4000
nowrap_r:
iny
dex
bne copy_remainder
rts

46
ethernet/memcpy.txt Normal file
View File

@ -0,0 +1,46 @@
Test 1:
5 FOR J=1 to 1000: NEXT J
10 PRINT CHR$(7)
20 FOR I=16384 to 20479
30 POKE 20480,PEEK(I)
40 NEXT I
100 PRINT CHR$(7)
Time (linapple2) 38s
All one one line
5 FOR J=1 to 1000: NEXT J
10 PRINT CHR$(7)
20 FOR I=16384 to 20479
30 POKE 20480,PEEK(I)
40 NEXT I
100 PRINT CHR$(7)
Time (linapple2) 37s
Assembly language:
PTR EQU $06
lda #0
sta PTR
lda #$40
sta PTR+1
ldx #8
ldy #0
copy_loop:
lda (PTR),y
sta $5000
iny
bne copy_loop
dex
bne copy_loop
rts
Runs more or less instantaenously

View File

@ -1,30 +1,48 @@
' Applesoft BASIC Webserver
' by Vince Weaver <vince@deater.net>
'
1 REM *** Setup UTHERNET II - W5100 1 REM *** Setup UTHERNET II - W5100
2 REM *** Assumes slot 5 ($C0D0) ' SLOT0=$C080 49280 SLOT4=$C0C0 49344
3 SLOT=49360: REM *** $C0D0 ' SLOT1=$C090 49296 SLOT5=$C0D0 49360
4 MR=SLOT+4: REM *** MODE REGISTER C0D4 ' SLOT2=$C0A0 49312 SLOT6=$C0E0 49376
5 HA=SLOT+5:LA=SLOT+6: REM *** HIGH/LOW ADDR $C0D5,$C0D6 ' SLOT3=$C0B0 49328 SLOT7=$C0F0 49392
7 DP=SLOT+7: REM *** DATA PORT $C0D7 '
' Set up the memory addresses to use
'
2 REM *** OURS IS IN SLOT3 ($C0B0)
3 SLOT=49328: REM *** $C0B0
4 MR=SLOT+4: REM *** MODE REGISTER C0B4
5 HA=SLOT+5:LA=SLOT+6: REM *** HIGH/LOW ADDR $C0B5,$C0B6
7 DP=SLOT+7: REM *** DATA PORT $C0B7
'
' Init the W5100
'
10 REM *** Init W5100 10 REM *** Init W5100
12 POKE MR,128 : REM RESET W5100 12 POKE MR,128 : REM RESET W5100
14 POKE MR,3 : REM AUTOINCREMENT 14 POKE MR,3 : REM AUTOINCREMENT
20 REM *** Setup MAC Address 20 REM *** Setup MAC Address 41:50:50:4c:45:32
21 REM *** 41:50:50:4c:45:32
22 POKE HA,0:POKE LA,9 22 POKE HA,0:POKE LA,9
23 POKE DP,65 23 POKE DP,65:POKE DP,80:POKE DP,80:POKE DP,76:POKE DP,69:POKE DP,50
23 POKE DP,80 30 REM *** Setup IP Address 192.168.8.15
23 POKE DP,80
23 POKE DP,76
23 POKE DP,69
23 POKE DP,50
30 REM *** Setup IP Address
31 REM *** 192.168.8.15
32 POKE LA,15 32 POKE LA,15
33 POKE DP,192 33 POKE DP,192:POKE DP,168:POKE DP,8:POKE DP,15
34 POKE DP,168
35 POKE DP,8
36 POKE DP,15
40 PRINT "UTHERNET II READY: 192.168.8.15" 40 PRINT "UTHERNET II READY: 192.168.8.15"
100 REM *** Setup Socket '
' Setup Machine Language Memcpy routine
' NOTE! This code assumes the Uthernet is in slot 3
' FIXME: patch on the fly once it works
' See Appendix 1 at the end of this for more details
'
50 FOR I=0 TO 72: READ X: POKE 768+I,X:NEXT I
51 DATA 169,0,133,6,169,64,133,7,162,11,240,36,160,0,177,6
52 DATA 141,183,192,213,9,208,15,217,8,0,208,10,169,64,141,181
53 DATA 192,169,0,141,182,192,200,208,229,230,7,202,208,224,162,10
54 DATA 177,6,141,183,192,217,8,0,208,10,169,64,141,181,192,169
55 DATA 0,141,182,192,200,202,208,232,96
'
' Setup Socket 0
'
100 REM *** Setup Socket 0
102 PRINT "** Setting up Socket 0" 102 PRINT "** Setting up Socket 0"
105 POKE HA,0:POKE LA,26: REM RX MEMSIZE 105 POKE HA,0:POKE LA,26: REM RX MEMSIZE
110 POKE DP,3: REM 8kB RX buffer 110 POKE DP,3: REM 8kB RX buffer
@ -36,106 +54,353 @@
303 PRINT "** Setting up to use TCP port 80" 303 PRINT "** Setting up to use TCP port 80"
305 POKE HA,4: POKE LA,4: REM *** 0x404 port 305 POKE HA,4: POKE LA,4: REM *** 0x404 port
310 POKE DP,0:POKE DP, 80: REM *** http port 80 310 POKE DP,0:POKE DP, 80: REM *** http port 80
'
' OPEN the socket
'
400 REM *** OPEN socket 400 REM *** OPEN socket
404 PRINT "** OPENing socket" 404 PRINT "** OPENing socket"
405 POKE HA,4: POKE LA,1: REM *** 0x401 command register 405 POKE HA,4: POKE LA,1: REM *** 0x401 command register
410 POKE DP, 1: REM *** OPEN 410 POKE DP, 1: REM *** OPEN
' '
' Check return value
'
500 REM *** Check if opened 500 REM *** Check if opened
505 POKE HA,4: POKE LA,3: REM *** 0x403 status register 505 POKE HA,4: POKE LA,3: REM *** 0x403 status register
510 RE=PEEK(DP) 510 RE=PEEK(DP)
515 PRINT "** STATUS IS ";RE; 515 PRINT "** STATUS IS ";RE;
520 IF RE=19 THEN PRINT " OPENED":GOTO 600 520 IF RE=19 THEN PRINT " OPENED":GOTO 600
530 IF RE=0 THEN PRINT " CLOSED, ERROR" 530 IF RE=0 THEN PRINT " CLOSED, ERROR": GOTO 5000
540 POKE HA,4: POKE LA,1: REM *** 0x401 command register 540 PRINT "UNKNOWN ERROR ";RE
550 POKE DP, 16: REM *** CLOSE 550 GOTO 5000
560 END '
' LISTEN on the socket
' '
600 REM *** Connection opened, Listen 600 REM *** Connection opened, Listen
605 POKE HA,4: POKE LA,1: REM *** 0x401 command register 605 POKE HA,4: POKE LA,1: REM *** 0x401 command register
610 POKE DP, 2: REM *** LISTEN 610 POKE DP, 2: REM *** LISTEN
'
' Check return value
'
620 REM *** Check if successful 620 REM *** Check if successful
625 POKE HA,4: POKE LA,3: REM *** 0x403 status register 625 POKE HA,4: POKE LA,3: REM *** 0x403 status register
630 RE=PEEK(DP) 630 RE=PEEK(DP)
635 PRINT "** STATUS IS ";RE; 635 PRINT "** STATUS IS ";RE;
640 IF RE=20 THEN PRINT " LISTENING":GOTO 700 640 IF RE=20 THEN PRINT " LISTENING":GOTO 700
650 IF RE=0 THEN PRINT " CLOSED, ERROR" 650 IF RE=0 THEN PRINT " CLOSED, ERROR":GOTO 5000
655 PRINT "UNKNOWN" 655 PRINT "UNKNOWN ERROR ";RE
660 POKE HA,4: POKE LA,1: REM *** 0x401 command register 675 GOTO 5000
670 POKE DP, 16: REM *** CLOSE '
675 END ' Wait for incoming connection
' '
700 REM *** Wait for incoming connection 700 REM *** Wait for incoming connection
705 POKE HA,4: POKE LA,1: REM *** 0x401 command register 705 POKE HA,4: POKE LA,1: REM *** 0x401 command register
710 POKE DP, 2: REM *** LISTEN 710 POKE DP, 2: REM *** LISTEN
'
' Check for result
'
720 REM *** Check if successful 720 REM *** Check if successful
725 POKE HA,4: POKE LA,3: REM *** 0x403 status register 725 POKE HA,4: POKE LA,3: REM *** 0x403 status register
730 RE=PEEK(DP) 730 RE=PEEK(DP)
740 IF RE=23 THEN GOTO 800 740 IF RE=23 THEN GOTO 800: REM ESTABLISHED
745 IF RE<>20 THEN PRINT "WAITING: STATUS=";RE 745 IF RE<>20 THEN PRINT "WAITING: UNEXPECTED STATUS=";RE
750 GOTO 700: REM *** Repeat until connected 750 GOTO 700: REM *** Repeat until connected
' '
800 PRINT "CONNECTED" ' Established, repeat waiting for incoming data
'
800 PRINT "ESTABLISHED"
802 POKE HA,4: POKE LA,38: REM *** 0x426 Received Size 802 POKE HA,4: POKE LA,38: REM *** 0x426 Received Size
805 SH=PEEK(DP):SL=PEEK(DP) 805 SH=PEEK(DP):SL=PEEK(DP)
810 SI=(SH*256)+SL 810 SI=(SH*256)+SL
820 IF SI<>0 THEN GOTO 900 820 IF SI<>0 THEN GOTO 900
'
' Should we delay? busy polling seems wasteful
'
830 REM DELAY? 830 REM DELAY?
840 GOTO 800 840 GOTO 802
'
' We have some data, let's read it
' '
900 POKE HA,4: POKE LA,40: REM *** 0x428 Received ptr 900 POKE HA,4: POKE LA,40: REM *** 0x428 Received ptr
905 OH=PEEK(DP):OL=PEEK(DP) 905 OH=PEEK(DP):OL=PEEK(DP)
910 RO=(OH*256)+OL 910 RF=(OH*256)+OL
920 REM *** SHOULD MASK WITH 0x1ff but how? 920 REM *** MASK WITH 0x1ff
930 RA=RO+24576:REM $6000 925 R%=RF/8192:RM=RF-(8192*R%)
940 PRINT "READ OFFSET=";RO;" READ ADDRESS=";RA;" READ SIZE=";SI 930 RA=RM+24576:REM $6000
940 PRINT "READ OFFSET=";RM;" READ ADDRESS=";RA;" READ SIZE=";SI
'
' Check for buffer wraparound
'
942 BW=0
945 IF (SI+TA>=32768) THEN BW=1:BO=32768-TA:PRINT "RX BUFFER WRAPAROUND IN ";BO
'
' Print received packet
' '
1000 REM *** PRINT PACKET 1000 REM *** PRINT PACKET
1005 POKE HA,RA/256: POKE LA,RA-((RA/256)*256) 1001 FL=1:FL$=""
1003 R%=RA/256
1005 POKE HA,R%: POKE LA,RA-(R%*256)
1010 FOR I=1 TO SI 1010 FOR I=1 TO SI
1020 C=PEEK(DP) 1020 C=PEEK(DP):C$=CHR$(C)
1030 IF C<>13 THEN PRINT CHR$(C); 1025 IF FL=1 THEN FL$=FL$+C$
1027 IF C=10 THEN FL=0
1030 IF C<>10 THEN PRINT C$;
1032 IF BW=0 THEN GOTO 1040
1033 BO=BO-1: IF BO=0 THEN POKE HA,96:POKE LA,0:BW=0
1040 NEXT I 1040 NEXT I
' '
' Deal with first line
'
1050 PRINT "FIRST LINE=";FL$
1060 IF LEFT$(FL$,3)<>"GET" GOTO 7000
1065 N$=""
1070 FOR I=6 TO LEN(FL$)
1075 M$=MID$(FL$,I,1)
1080 IF M$=" " GOTO 1090
1085 N$=N$+M$
1087 NEXT I
1090 IF N$="" THEN N$="index.html"
1095 PRINT "SENDING FILE: ";N$
'
' TODO: handle wraparound of 8kb buffer ' TODO: handle wraparound of 8kb buffer
' '
'
' Update read pointer
'
1100 REM *** Update read pointer 1100 REM *** Update read pointer
'"HTTP/1.1 200 OK\r\n" 1110 POKE HA,4: POKE LA,40: REM *** 0x428 Received ptr
'"Date: %s\r\n" 1120 RA=RF+SI
'"Server: VMW-web\r\n" 1130 R%=RA/256
'"Last-Modified: %s\r\n" 1140 POKE DP,R%: POKE DP,RA-(R%*256)
'"Content-Length: %ld\r\n" 1150 REM *** RECEIVE
'"Content-Type: %s\r\n" 1160 POKE HA,4: POKE LA,1: REM *** 0x401 command register
'"\r\n", 1170 POKE DP, 64: REM *** RECV
1200 REM *** SEND RESPONSE '
1205 A$="HTTP/1.1 200 OK"+CHR$(13)+CHR$(10) ' Load file from disk
1210 A$=A$+"Server: VMW-web"+CHR$(13)+CHR$(10) '
1220 A$=A$+"Content-Length: 65"+CHR$(13)+CHR$(10) 1200 REM *** LOAD FILE
1230 A$=A$+"Content-Type: text/html"+CHR$(13)+CHR$(10) 1202 X$=RIGHT$(N$,3):M$="text/html"
1250 A$=A$+CHR$(13)+CHR$(10) 1203 IF X$="txt" THEN M$="text/plain"
1260 A$=A$+"<html><head>test</head><body><h3>Apple2 Test</h3></body></html>" 1204 IF X$="png" THEN M$="image/png"
1270 A$=A$+CHR$(13)+CHR$(10) 1205 IF X$="jpg" THEN M$="image/jpg"
1280 PRINT "SENDING:":PRINT A$ 1206 IF X$="ico" THEN M$="image/x-icon"
' TODO: read TX free size reg (0x420) 1207 IF N$="teapot.html" GOTO 9000
1900 POKE HA,4: POKE LA,34: REM *** 0x422 TX read ptr 1208 ONERR GOTO 8000
1209 PRINT "LOADING ";N$
1210 PRINT CHR$(4)+"BLOAD ";N$
1215 POKE 216,0: REM CANCEL ONERR
1220 FS=PEEK(43616)+256*PEEK(43617): REM FILESIZE
1225 PRINT "DONE LOADING"
' assume loaded at 0x4000, text page 2
' and that max size is 8kb
1240 A$="HTTP/1.1 200 OK"+CHR$(13)+CHR$(10)
1250 A$=A$+"Server: VMW-web"+CHR$(13)+CHR$(10)
1260 A$=A$+"Content-Length: "+STR$(FS)+CHR$(13)+CHR$(10)
1280 A$=A$+"Content-Type: "+M$+CHR$(13)+CHR$(10)+CHR$(13)+CHR$(10)
'
1380 PRINT "SENDING:":PRINT A$
1385 C=0
'
' read TX free size reg (0x420)
'
1700 SI=LEN(A$)+FS
1710 IF (SI>8192) THEN PRINT "FILE TOO BIG!": REM GOTO 403?
1800 POKE HA,4: POKE LA,32: REM *** 0x420 FREESIZE
1810 OH=PEEK(DP):OL=PEEK(DP)
1815 FR=(OH*256)+OL
1820 PRINT "FREE: ";FR
1830 IF SI>FR GOTO 1800: REM REPEAT UNTIL FREE
'
' Read tx offset
'
1900 POKE HA,4: POKE LA,36: REM *** 0x424 TX write ptr
1905 OH=PEEK(DP):OL=PEEK(DP) 1905 OH=PEEK(DP):OL=PEEK(DP)
1910 TF=(OH*256)+OL 1910 TF=(OH*256)+OL
1920 REM *** SHOULD MASK WITH 0x1ff 1920 REM *** MASK WITH 0x1ff
1925 T%=TF/8192:TF=TF-(8192*T%) 1925 T%=TF/8192:TM=TF-(8192*T%)
1930 TA=TF+16384:REM $4000 1930 TA=TM+16384:REM $4000
1935 SI=LEN(A$) 1940 PRINT "OH/OL=";OH;"/";OL;" TX OFFSET=";TM;" TX ADDRESS=";TA;" TX SIZE=";SI
1940 PRINT "TX OFFSET=";TF;" TX ADDRESS=";TA;" TX SIZE=";SI '
' Check for buffer wraparound
'
1942 BW=0:BO=0
1945 IF (SI+TA>=24576) THEN BW=1:BO=24576-TA:PRINT "TX BUFFER WRAPAROUND IN ";BO
'
' Write data to TX buffer
' First write header
'
2000 T%=TA/256 2000 T%=TA/256
2005 POKE HA,T%: POKE LA,TA-(T%*256) 2005 POKE HA,T%: POKE LA,TA-(T%*256)
2010 FOR I=1 TO SI 2010 FOR I=1 TO LEN(A$)
2020 POKE DP,ASC(MID$(A$,I,1)) 2015 POKE DP,ASC(MID$(A$,I,1))
2040 NEXT I 2017 IF BW=0 THEN GOTO 2020
2018 BO=BO-1: IF BO=0 THEN POKE HA,64:POKE LA,0:BW=0
2020 NEXT I
'
' Write disk part
'
2025 FOR I=1 TO FS
2026 C=C+1: IF C=50 THEN PRINT ".";:C=0
2030 POKE DP,PEEK(16383+I)
2032 IF BW=0 THEN GOTO 2035
2033 BO=BO-1: IF BO=0 THEN POKE HA,64:POKE LA,0:BW=0
2035 NEXT I
2040 PRINT
'
' The above is slow
' Intead use our machine language routine
'
'2025 B%=BO/256:POKE 9,B%:POKE 8,BO-(B%*256)
'2027 B%=FS/256:POKE 11,B%:POKE 10,FS-(B%*256)
'2030 CALL 768
'
' Update TX write ptr
'
2050 REM ** UPDATE TX WRITE PTR 2050 REM ** UPDATE TX WRITE PTR
2060 POKE HA,4: POKE LA,36: REM *** 0x424 TX write ptr 2060 POKE HA,4: POKE LA,36: REM *** 0x424 TX write ptr
2075 TA=TA+SI 2075 TA=TF+SI
2080 T%=TA/256 2080 T%=TA/256
2085 POKE HA,T%: POKE LA,TA-(T%*256) 2085 POKE DP,T%: POKE DP,TA-(T%*256)
2090 PRINT "UPDATE TX TO ";T%;"/";TA-(T%*256)
'
' SEND packet
'
2100 REM *** SEND 2100 REM *** SEND
2102 PRINT "SENDING"
2105 POKE HA,4: POKE LA,1: REM *** 0x401 command register 2105 POKE HA,4: POKE LA,1: REM *** 0x401 command register
2110 POKE DP, 32: REM *** SEND 2110 POKE DP, 32: REM *** SEND
5000 REM *** CLOSE '
' Return to reading
'
4000 REM *** Check if successful
4010 POKE HA,4: POKE LA,3: REM *** 0x403 status register
4020 RE=PEEK(DP)
4030 PRINT "STATUS AFTER SEND ";RE
4035 IF RE=28 THEN GOTO 6000: REM CLOSE_WAIT
4040 IF RE=0 THEN GOTO 400: REM CLOSED
4060 REM *** RECEIVE
4075 POKE HA,4: POKE LA,1: REM *** 0x401 command register
4080 POKE DP, 64: REM *** RECV
4090 GOTO 800
'
' Close the socket
'
5000 REM *** CLOSE AND EXIT
5010 POKE HA,4: POKE LA,1: REM *** 0x401 command register
5020 POKE DP, 16: REM *** CLOSE
5030 END
6000 REM *** CLOSE AND RELISTEN
6010 POKE HA,4: POKE LA,1: REM *** 0x401 command register
6020 POKE DP, 16: REM *** CLOSE
' Check status?
6030 GOTO 400
'
'
' ERROR MESSAGES
'
'
7000 REM 400 BAD REQUEST
7005 S$="400 Bad Request"
7010 M$="<html><head><title>400 Bad Request</title></head><body><h1>Bad Request</h1><p>Your browser sent a request that this server could not understand.<br /></p></body></html>"+CHR$(13)+CHR$(10)
7020 GOTO 9100
8000 REM 404 NOT FOUND
8003 POKE 216,0: REM CANCEL ONERR
8004 PRINT "DISK ERROR: ";PEEK(222)
8005 S$="404 Not Found"
8010 M$="<html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>File not found.<br /></p></body></html>"+CHR$(13)+CHR$(10)
8020 GOTO 9100
9000 REM 418 TEAPOT
9005 S$="418 I'm a Teapot"
9010 M$="<html><head><title>418 I'm a Teapot</title></head><body><h1>I'm a Teapot</h1><p>Short *and* stout.<br /></p></body></html>"+CHR$(13)+CHR$(10)
'
' Make header
'
9100 A$="HTTP/1.1 "+S$+CHR$(13)+CHR$(10)+"Server: VMW-web"+CHR$(13)+CHR$(10)
9105 A$=A$+"Content-Length: "+STR$(LEN(M$))+CHR$(13)+CHR$(10)
9110 A$=A$+"Connection: close"+CHR$(13)+CHR$(10)+"Content-Type: text/html; charset=iso-8859-1"+CHR$(13)+CHR$(10)+CHR$(13)+CHR$(10)
' Poke as if we had loaded from disk
9200 FS=LEN(M$)
9210 FOR I=1 TO FS
9220 POKE 16383+I,ASC(MID$(M$,I,1))
9300 NEXT I
9310 GOTO 1380
'
' STATUSES
' p28 of W5100 manual
' 0x0 0 SOCK_CLOSED
' 0x13 SOCK_INIT
' 0x14 SOCK_LISTEN
' 0x17 23 SOCK_ESTABLISHED
' 0x1C 28 SOCK_CLOSE_WAIT
' 0x22 SOCK_UDP
' 0x32 SOCK_IPRAW
' 0x42 SOCK_MACRAW
' 0x5f SOCK_PPOE
'
' Appendix 1: The memcpy machine code
'
'
'PTR EQU $06
'PTRH EQU $07
'
'WRAPL EQU $08
'WRAPH EQU $09
'
'SIZEL EQU $0A
'SIZEH EQU $0B
'
'tx_copy:
'
' lda #0 ; always copying from 0x4000
' sta PTR
' lda #$40
' sta PTR+1
'
' ldx #SIZEH ; number of 256-byte blocks
' beq copy_remainder ; if none, skip ahead
'
' ldy #0
'copy256:
' lda (PTR),y
' sta $C0B7 ; change based on uthernet slot
'
' cmp WRAPH,x
' bne nowrap256
'
' cmp WRAPL,y
' bne nowrap256
'
' lda #$40
' sta $C0B5
' lda #$00
' sta $C0B6 ; wrap tx buffer address to 0x4000
'
'nowrap256:
' iny
' bne copy256
'
' inc PTR+1 ; update 16-bit pointer
' dex ; finish a 256 byte block
' bne copy256
'
' ldx #SIZEL
'copy_remainder:
' lda (PTR),y
' sta $C0B7 ; change based on uthernet slot
'
' cmp WRAPL,y
' bne nowrap_r
'
' lda #$40
' sta $C0B5
' lda #$00
' sta $C0B6 ; wrap tx buffer address to 0x4000
'
'nowrap_r:
' iny
' dex
' bne copy_remainder
'
' rts