diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 113 | ||||
-rw-r--r-- | src/config.c | 222 | ||||
-rw-r--r-- | src/config.h | 68 | ||||
-rw-r--r-- | src/disk.c | 610 | ||||
-rw-r--r-- | src/disk.h | 39 | ||||
-rw-r--r-- | src/diskimg.c | 263 | ||||
-rw-r--r-- | src/diskimg.h | 99 | ||||
-rw-r--r-- | src/main.c | 140 | ||||
-rw-r--r-- | src/serial.c | 472 | ||||
-rw-r--r-- | src/serial.h | 134 | ||||
-rw-r--r-- | src/siorc.example | 17 | ||||
-rw-r--r-- | src/tmp | bin | 0 -> 92176 bytes | |||
-rw-r--r-- | src/token.c | 257 | ||||
-rw-r--r-- | src/token.h | 71 | ||||
-rw-r--r-- | src/util.c | 165 | ||||
-rw-r--r-- | src/util.h | 82 |
16 files changed, 2752 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..639860c --- /dev/null +++ b/src/Makefile @@ -0,0 +1,113 @@ +# atarisio - A UNIX back end for an Atari SIO2PC lead. +# +# Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# ------------------------------------------------------------------------- +# +# $Id: Makefile,v 1.1.1.1 2007-05-07 02:05:54 ianc Exp $ +# + + +# This CFLAGS assumes that gcc is being used. +# Simply comment out if not, and replace as needed. +# +# Note that the -pthread is *IMPORTANT* - this is a FreeBSD extension +# that links with the appropriate libc for pthreads. Consult your OS +# documentation on what to do on other systems. +# +# +CFLAGS = -g -Wall -Werror -pthread + +TARGET = atarisio + +SOURCE = main.c \ + token.c \ + config.c \ + serial.c \ + disk.c \ + diskimg.c \ + util.c + +OBJECTS = main.o \ + token.o \ + serial.o \ + config.o \ + disk.o \ + diskimg.o \ + util.o + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + $(CC) $(CFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS) + +clean: + rm -f $(TARGET) $(OBJECTS) core + +depend: + makedepend -- $(CFLAGS) -- $(SOURCE) + if test -e Makefile ; then rm -f Makefile.bak ; fi + +# DO NOT DELETE THIS LINE -- make depend depends on it + +main.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h +main.o: /usr/include/sys/_types.h /usr/include/machine/_types.h +main.o: /usr/include/limits.h /usr/include/sys/limits.h +main.o: /usr/include/machine/_limits.h /usr/include/sys/syslimits.h +main.o: /usr/include/stdio.h /usr/include/string.h /usr/include/strings.h +main.o: /usr/include/errno.h /usr/include/unistd.h /usr/include/sys/types.h +main.o: /usr/include/machine/endian.h /usr/include/sys/select.h +main.o: /usr/include/sys/_sigset.h /usr/include/sys/_timeval.h +main.o: /usr/include/sys/timespec.h /usr/include/sys/unistd.h +main.o: /usr/include/fcntl.h serial.h util.h disk.h config.h token.h +token.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h +token.o: /usr/include/sys/_types.h /usr/include/machine/_types.h +token.o: /usr/include/stdio.h /usr/include/string.h /usr/include/strings.h +token.o: /usr/include/ctype.h /usr/include/runetype.h token.h util.h +config.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h +config.o: /usr/include/sys/_types.h /usr/include/machine/_types.h +config.o: /usr/include/stdio.h /usr/include/string.h /usr/include/strings.h +config.o: config.h util.h token.h +serial.o: /usr/include/pthread.h /usr/include/sys/cdefs.h +serial.o: /usr/include/sys/types.h /usr/include/machine/endian.h +serial.o: /usr/include/sys/_types.h /usr/include/machine/_types.h +serial.o: /usr/include/sys/select.h /usr/include/sys/_sigset.h +serial.o: /usr/include/sys/_timeval.h /usr/include/sys/timespec.h +serial.o: /usr/include/sys/time.h /usr/include/time.h +serial.o: /usr/include/sys/signal.h /usr/include/machine/signal.h +serial.o: /usr/include/machine/trap.h /usr/include/limits.h +serial.o: /usr/include/sys/limits.h /usr/include/machine/_limits.h +serial.o: /usr/include/sys/syslimits.h /usr/include/sched.h +serial.o: /usr/include/stdio.h /usr/include/string.h /usr/include/strings.h +serial.o: /usr/include/ctype.h /usr/include/runetype.h /usr/include/unistd.h +serial.o: /usr/include/sys/unistd.h /usr/include/termios.h +serial.o: /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h +serial.o: /usr/include/sys/ttydefaults.h /usr/include/fcntl.h +serial.o: /usr/include/errno.h serial.h /usr/include/stdlib.h util.h +disk.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h +disk.o: /usr/include/sys/_types.h /usr/include/machine/_types.h +disk.o: /usr/include/stdio.h /usr/include/string.h /usr/include/strings.h +disk.o: /usr/include/ctype.h /usr/include/runetype.h disk.h util.h diskimg.h +disk.o: config.h token.h serial.h +diskimg.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h +diskimg.o: /usr/include/sys/_types.h /usr/include/machine/_types.h +diskimg.o: /usr/include/stdio.h /usr/include/string.h /usr/include/strings.h +diskimg.o: diskimg.h util.h +util.o: /usr/include/stdio.h /usr/include/sys/cdefs.h +util.o: /usr/include/sys/_types.h /usr/include/machine/_types.h +util.o: /usr/include/string.h /usr/include/strings.h /usr/include/ctype.h +util.o: /usr/include/runetype.h util.h /usr/include/stdlib.h diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..6d05005 --- /dev/null +++ b/src/config.c @@ -0,0 +1,222 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Config file + +*/ +static const char ident[]="$Id$"; + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "config.h" +#include "token.h" + +static const char ident_h[]=ATARIOSIO_CONFIG_H; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + + +/* ---------------------------------------- CONFIG +*/ +static char *serial_path=NULL; +static char *prompt=NULL; + +static const struct +{ + const char *name; + void *var; + int is_int; +} config[]= { + {"device", &serial_path, FALSE}, + {"prompt", &prompt, FALSE}, + {NULL, NULL, FALSE} + }; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static void Parse(FILE *fp) +{ + char buff[1024]; + + while(fgets(buff,sizeof buff,fp)) + { + size_t l; + + l=strlen(buff); + + if (buff[l-1]=='\n') + buff[--l]=0; + + if (l>0 && buff[0]!='#') + TokenRun(buff); + } +} + + +static void Set(int argc, char *argv[]) +{ + int f; + + for(f=0;config[f].name;f++) + { + if (strcmp(config[f].name,argv[1])==0) + { + if (config[f].is_int) + { + int *i; + + i=config[f].var; + *i=atoi(argv[2]); + } + else + { + char **p; + + p=config[f].var; + + if (*p) + free(*p); + + *p=StrCopy(argv[2]); + } + } + } +} + + +static void Show(int argc, char *argv[]) +{ + int f; + + if (argc==2) + { + for(f=0;config[f].name;f++) + { + if (strcmp(config[f].name,argv[1])==0) + { + if (config[f].is_int) + { + int *i; + + i=config[f].var; + printf("%s = %d\n",argv[1],*i); + } + else + { + char **p; + + p=config[f].var; + printf("%s = %s\n",argv[1],*p ? *p:"undefined"); + } + } + } + } + else + { + for(f=0;config[f].name;f++) + { + printf("%s = ",config[f].name); + + if (config[f].is_int) + { + int *i; + + i=config[f].var; + printf("%d\n",*i); + } + else + { + char **p; + + p=config[f].var; + printf("%s\n",*p ? *p:"undefined"); + } + } + } +} + + +/* ---------------------------------------- EXPORTED INTERFACES +*/ +int ConfigRead(void) +{ + static Command cmd[2]= + { + {"set",3,3,"set variable value","Set a variable",Set}, + {"show",1,2,"show [variable]","Show variables",Show} + }; + + FILE *fp; + char path[FILENAME_MAX]={0}; + + serial_path=StrCopy("/dev/cuaa0"); + prompt=StrCopy("SIO% "); + + if (getenv("HOME")) + strcpy(path,getenv("HOME")); + + strcat(path,"/.siorc"); + + if (!(fp=fopen(path,"r"))) + return FALSE; + + TokenRegister(2,cmd); + + Parse(fp); + fclose(fp); + + return TRUE; +} + + +int IConfig(IConfigVar v) +{ + static const int *vars[]= + { + NULL + }; + + return *vars[v]; +} + + +const char *SConfig(SConfigVar v) +{ + static char **vars[]= + { + &serial_path, + &prompt + }; + + return (const char *)*vars[v]; +} + + +/* END OF FILE */ diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..8e58514 --- /dev/null +++ b/src/config.h @@ -0,0 +1,68 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Config file + +*/ + +#ifndef ATARIOSIO_CONFIG_H +#define ATARIOSIO_CONFIG_H "$Id$" + +#include "util.h" + + +/* Integer settings +*/ +typedef enum +{ + CONF_NONE +} IConfigVar; + + +/* String settings +*/ +typedef enum +{ + CONF_DEVICE, + CONF_PROMPT +} SConfigVar; + + +/* Read config file +*/ +int ConfigRead(void); + + +/* Get integer setting +*/ +int IConfig(IConfigVar v); + + +/* Get string setting +*/ +const char *SConfig(SConfigVar v); + + +#endif + + +/* END OF FILE */ diff --git a/src/disk.c b/src/disk.c new file mode 100644 index 0000000..d058154 --- /dev/null +++ b/src/disk.c @@ -0,0 +1,610 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Disk handling + +*/ +static const char ident[]="$Id$"; + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include "disk.h" +#include "diskimg.h" +#include "config.h" +#include "token.h" +#include "serial.h" + +static const char ident_h[]=ATARIOSIO_DISK_H; + + + +/* ---------------------------------------- MACROS +*/ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define HEX_DUMP_LINE 16 + +#define MAX_DRIVE 8 + + +/* ---------------------------------------- STATICS +*/ +static DiskImg drive[MAX_DRIVE]={0}; +static DiskInfo info[MAX_DRIVE]; +static uchar *buff[MAX_DRIVE]={0}; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static int Word(uchar lo, uchar hi) +{ + return ((int)hi<<8)|(int)lo; +} + + +static int ParseDrive(const char *p) +{ + int d; + + if (*p++!='d') + return -1; + + d=(*p-'0')-1; + + if (d<0 || d>=MAX_DRIVE) + return -1; + + return d; +} + + + +static void DoMount(int d, const char *p) +{ + drive[d]=DiskImgLoad(p); + + if (!drive[d]) + printf("Disk image error: %s\n",DiskImgError()); + else + { + DiskImgInfo(drive[d],info+d); + buff[d]=Malloc(info[d].bytes_per_sector); + } +} + + +static void DoUnmount(int d) +{ + if (drive[d]) + DiskImgFree(drive[d]); + + drive[d]=NULL; + free(buff[d]); +} + + +static void ShowDir(const uchar *p, unsigned len) +{ + unsigned f; + + if (!p) + return; + + f=0; + + while(f<len) + { + uchar flag; + + flag=p[f]; + + if (flag&0x40) + { + int secs; + int i; + + secs=Word(p[f+1],p[f+2]); + + for(i=5;i<16;i++) + { + putchar(p[f+i]); + } + + printf(" %4d sectors\n",secs); + } + + f+=16; + } +} + + +/* ---------------------------------------- COMMAND HANDLERS +*/ +static void Mount(int argc, char *argv[]) +{ + int d; + + if (argc==1) + { + for(d=0;d<MAX_DRIVE;d++) + if (drive[d]) + printf("d%d %s\n",d+1,DiskImgPath(drive[d])); + } + else if (argc==3) + { + if ((d=ParseDrive(argv[1]))==-1) + { + printf("Invalid drive specifier\n"); + return; + } + + if (drive[d]) + { + printf("Disk already mounted\n"); + return; + } + + DoMount(d,argv[2]); + } + else + { + printf("Invalid arguments\n"); + } +} + + +static void Unmount(int argc, char *argv[]) +{ + int d; + + if (argc==1) + { + for(d=0;d<MAX_DRIVE;d++) + { + if (drive[d]) + { + printf("Unmounting %s from d%d\n",DiskImgPath(drive[d]),d+1); + DoUnmount(d); + } + } + } + else + { + if ((d=ParseDrive(argv[1]))==-1) + { + printf("Invalid drive specifier\n"); + return; + } + + if (!drive[d]) + { + printf("Disk not mounted\n"); + return; + } + + DoUnmount(d); + } +} + + +static void ReadOnly(int argc, char *argv[]) +{ + int d; + int flag; + + if (!YesNo(argv[2],&flag)) + { + printf("Invalid parameter\n"); + return; + } + + if ((d=ParseDrive(argv[1]))==-1) + { + printf("Invalid drive specifier\n"); + return; + } + + if (!drive[d]) + { + printf("Disk not mounted\n"); + return; + } +} + + +static void Info(int argc, char *argv[]) +{ + int d; + + if ((d=ParseDrive(argv[1]))==-1) + { + printf("Invalid drive specifier\n"); + return; + } + + if (!drive[d]) + { + printf("Disk not mounted\n"); + return; + } + + printf("Sectors : %d\n",info[d].sectors); + printf("Bytes per sector: %d\n",info[d].bytes_per_sector); + printf("Write protect : %s\n",info[d].write_protect ? "ON":"OFF"); +} + + +static void Sector(int argc, char *argv[]) +{ + const uchar *p; + int d; + + if ((d=ParseDrive(argv[1]))==-1) + { + printf("Invalid drive specifier\n"); + return; + } + + if (!drive[d]) + { + printf("Disk not mounted\n"); + return; + } + + if (!(p=DiskImgGetSector(drive[d],GetInt(argv[2])))) + { + printf("Invalid sector\n"); + return; + } + + HexDump(p,info[d].bytes_per_sector); +} + + +static void New(int argc, char *argv[]) +{ + DiskImg img; + DiskInfo inf; + + img=DiskImgNew(GetInt(argv[2]),GetInt(argv[3])); + + if (!img) + { + printf("Failed to create image\n"); + return; + } + + DiskImgInfo(img,&inf); + + if (argc==5) + { + uchar *buff; + FILE *fp; + int rd; + unsigned sec; + + buff=Malloc(inf.bytes_per_sector); + + if (!(fp=fopen(argv[4],"rb"))) + { + printf("Failed to open %s\n",argv[4]); + DiskImgFree(img); + free(buff); + return; + } + + sec=0; + + do + { + rd=fread(buff,1,inf.bytes_per_sector,fp); + + if (rd>0) + { + DiskImgPutSector(img,sec++,buff); + } + } while(rd==inf.bytes_per_sector); + + fclose(fp); + + free(buff); + } + + if (!DiskImgSave(img,argv[1])) + { + printf("Failed to save image\n"); + } + + DiskImgFree(img); +} + + +static void Ls(int argc, char *argv[]) +{ + int d; + unsigned f; + + if ((d=ParseDrive(argv[1]))==-1) + { + printf("Invalid drive specifier\n"); + return; + } + + if (!drive[d]) + { + printf("Disk not mounted\n"); + return; + } + + for(f=360;f<368;f++) + ShowDir(DiskImgGetSector(drive[d],f),info[d].bytes_per_sector); +} + + +static void Save(int argc, char *argv[]) +{ + int d; + + if ((d=ParseDrive(argv[1]))==-1) + { + printf("Invalid drive specifier\n"); + return; + } + + if (!drive[d]) + { + printf("Disk not mounted\n"); + return; + } + + if (argc==2) + DiskImgSave(drive[d],NULL); + else + DiskImgSave(drive[d],argv[2]); +} + + +/* ---------------------------------------- COMMAND TABLE +*/ +static Command cmd[]= +{ + { + "mount", + 1,3, + "mount [dN file]", + "Mount a disk image", + Mount + }, + { + "umount", + 1,2, + "unmount [dN]", + "Unmount a disk image", + Unmount + }, + { + "readonly", + 3,3, + "readonly dN on|off", + "Set a disk read only/writeable", + ReadOnly + }, + { + "info", + 2,2, + "info dN", + "Returns info on a mounted disk", + Info + }, + { + "sector", + 3,3, + "sector dN sector", + "Hex dumps supplied sector", + Sector + }, + { + "new", + 4,5, + "new path sectors secsize [file]", + "Creates a new disk file, with optional data", + New + }, + { + "ls", + 2,2, + "ls dN", + "Lists directory on disk", + Ls + }, + { + "save", + 2,3, + "save dN [file]", + "Saves a disk back to, er, disk", + Save + } +}; + + +/* ---------------------------------------- SIO HANDLER +*/ +static void StatusHandler(int d, const SIOCommand *cmd) +{ + static uchar status[4]={0x00, 0x00, 0x01, 0x00}; + + usleep(TIME_ACK); + + if (!drive[d]) + { + cmd->putchar(SIO_NACK); + return; + } + + cmd->putchar(SIO_ACK); + + if (info[d].bytes_per_sector==128) + { + if (info[d].sectors>720) + status[0]=0x80; + else + status[0]=0x10; + } + else + status[0]=0x60; + + if (info[d].write_protect) + status[0]|=8; + else + status[0]&=~8; + + usleep(TIME_ACK_TO_COMPLETE); + cmd->putchar(SIO_COMPLETE); + usleep(TIME_COMPLETE_TO_DATA); + cmd->write(status,4); +} + + +static void PutHandler(int d, const SIOCommand *cmd) +{ + int sec; + size_t len; + + sec=Word(cmd->aux1,cmd->aux2); + + usleep(TIME_ACK); + + if (!drive[d] || info[d].write_protect || sec>info[d].sectors) + { + cmd->putchar(SIO_NACK); + return; + } + + if (sec<3) + len=128; + else + len=info[d].bytes_per_sector; + + sec--; + + cmd->putchar(SIO_ACK); + if (cmd->read(buff[d],len)) + { + cmd->putchar(SIO_ACK); + cmd->putchar(SIO_COMPLETE); + } + else + { + cmd->putchar(SIO_NACK); + } +} + + +static void GetHandler(int d, const SIOCommand *cmd) +{ + int sec; + size_t len; + + sec=Word(cmd->aux1,cmd->aux2); + + usleep(TIME_ACK); + + if (!drive[d] || info[d].write_protect || sec>info[d].sectors) + { + cmd->putchar(SIO_NACK); + return; + } + + if (sec<3) + len=128; + else + len=info[d].bytes_per_sector; + + sec--; + + cmd->putchar(SIO_ACK); + usleep(TIME_ACK_TO_COMPLETE); + cmd->putchar(SIO_COMPLETE); + usleep(TIME_COMPLETE_TO_DATA); + + cmd->write(DiskImgGetSector(drive[d],sec),len); +} + + +static void SIOHandler(const SIOCommand *cmd) +{ + switch(cmd->cmd) + { + case eRead: + GetHandler(cmd->device-0x31,cmd); + break; + + case eWrite: + PutHandler(cmd->device-0x31,cmd); + break; + + case eStatus: + StatusHandler(cmd->device-0x31,cmd); + break; + + case ePut: + PutHandler(cmd->device-0x31,cmd); + break; + + case eFormat: + break; + + case eVerifySec: + break; + + case eDownload: + case eReadAddr: + case eReadSpin: + case eMotorOn: + break; + } +} + + +/* ---------------------------------------- EXPORTED INTERFACES +*/ +void DiskInit(void) +{ + int f; + + TokenRegister(sizeof cmd/sizeof cmd[0],cmd); + + for(f=0x31;f<0x31+MAX_DRIVE;f++) + SerialRegister(f,SIOHandler); +} + + +/* END OF FILE */ diff --git a/src/disk.h b/src/disk.h new file mode 100644 index 0000000..952f6ba --- /dev/null +++ b/src/disk.h @@ -0,0 +1,39 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Disk handling + +*/ + +#ifndef ATARIOSIO_DISK_H +#define ATARIOSIO_DISK_H "$Id$" + +#include "util.h" + +/* Initialise disk routines +*/ +void DiskInit(void); + +#endif + + +/* END OF FILE */ diff --git a/src/diskimg.c b/src/diskimg.c new file mode 100644 index 0000000..74e0243 --- /dev/null +++ b/src/diskimg.c @@ -0,0 +1,263 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Disk handling + +*/ +static const char ident[]="$Id$"; + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "diskimg.h" + +static const char ident_h[]=ATARIOSIO_DISKIMG_H; + + + +/* ---------------------------------------- MACROS +*/ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define MAGIC ('N'+'I'+'C'+'K'+'A'+'T'+'A'+'R'+'I') + +#define WRITE_PROTECT 0x01 + + +/* ---------------------------------------- TYPES +*/ +struct sDiskImg +{ + char *path; + unsigned sectors; + unsigned bps; + int flags; + uchar *data; +}; + + +/* ---------------------------------------- STATICS +*/ +static char error[256]; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static unsigned GetWord(FILE *fp) +{ + unsigned w; + int i; + + if ((i=getc(fp))==EOF) + return 0; + + w=i; + + if ((i=getc(fp))==EOF) + return 0; + + w|=i<<8; + + return w; +} + + +static void PutWord(FILE *fp,int w) +{ + uchar c; + + c=(w&0xff); + + putc(c,fp); + + c=(w>>8); + + putc(c,fp); +} + + +/* ---------------------------------------- EXPORTED INTERFACES +*/ +DiskImg DiskImgLoad(const char *path) +{ + FILE *fp; + DiskImg img; + unsigned long hi,lo; + int f; + + if (!(fp=fopen(path,"rb"))) + { + strcpy(error,"Disk file does not exist"); + return NULL; + } + + if (GetWord(fp)!=MAGIC) + { + strcpy(error,"Not an ATR disk file"); + return NULL; + } + + img=Malloc(sizeof *img); + + img->path=StrCopy(path); + + lo=GetWord(fp); + + img->bps=GetWord(fp); + + img->flags=getc(fp); + + hi=GetWord(fp); + + hi=(hi<<16)|lo; + + img->sectors=(hi*16)/img->bps; + + img->data=Malloc(img->sectors*img->bps); + + for(f=0;f<7;f++) + getc(fp); + + for(f=0;f<img->sectors*img->bps;f++) + { + int i; + + if ((i=getc(fp))==EOF) + { + free(img->path); + free(img->data); + free(img); + strcpy(error,"Corrupt disk file - too short"); + return NULL; + } + + img->data[f]=i; + } + + return img; +} + + +const char *DiskImgError(void) +{ + return error; +} + + +const uchar *DiskImgGetSector(DiskImg img, unsigned sector) +{ + if (sector>=img->sectors) + return NULL; + + return img->data+sector*img->bps; +} + + +int DiskImgPutSector(DiskImg img, unsigned sector, const uchar *data) +{ + if (sector>=img->sectors) + return FALSE; + + memcpy(img->data+sector*img->bps,data,img->bps); + + return TRUE; +} + +DiskImg DiskImgNew(unsigned sectors, unsigned bytes_per_sector) +{ + DiskImg img; + + img=Malloc(sizeof *img); + + img->path=StrCopy("blank.atr"); + img->sectors=sectors; + img->bps=bytes_per_sector; + img->flags=0; + img->data=Malloc(sectors*bytes_per_sector); + + return img; +} + + +int DiskImgSave(DiskImg img, const char *path) +{ + unsigned long l; + FILE *fp; + int f; + + if (path) + fp=fopen(path,"wb"); + else + fp=fopen(img->path,"wb"); + + if (!fp) + return FALSE; + + l=(img->sectors/img->bps)/16; + + PutWord(fp,MAGIC); + PutWord(fp,l&0xffff); + PutWord(fp,img->bps); + putc(0,fp); + PutWord(fp,l>>16); + + for(f=0;f<7;f++) + putc(0,fp); + + for(f=0;f<img->sectors*img->bps;f++) + { + putc(img->data[f],fp); + } + + return TRUE; +} + + +void DiskImgFree(DiskImg img) +{ + free(img->path); + free(img->data); + free(img); +} + + +void DiskImgInfo(DiskImg img, DiskInfo *info) +{ + info->sectors=img->sectors; + info->bytes_per_sector=img->bps; + info->write_protect=img->flags&WRITE_PROTECT; +} + + +const char *DiskImgPath(DiskImg img) +{ + return img->path; +} + + +/* END OF FILE */ diff --git a/src/diskimg.h b/src/diskimg.h new file mode 100644 index 0000000..f355c50 --- /dev/null +++ b/src/diskimg.h @@ -0,0 +1,99 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Disk handling + +*/ + +#ifndef ATARIOSIO_DISKIMG_H +#define ATARIOSIO_DISKIMG_H "$Id$" + +#include "util.h" + + +/* Type representing a disk +*/ +struct sDiskImg; +typedef struct sDiskImg *DiskImg; + + +/* Type representing disk config +*/ +typedef struct +{ + unsigned sectors; + unsigned bytes_per_sector; + int write_protect; +} DiskInfo; + + +/* Load a disk - returns NULL for error. +*/ +DiskImg DiskImgLoad(const char *path); + + +/* Why the last operation failed +*/ +const char *DiskImgError(void); + + +/* Read a sector (NULL if invalid sector) +*/ +const uchar *DiskImgGetSector(DiskImg img, unsigned sector); + + +/* Write a sector (returns FALSE for invalid sector) +*/ +int DiskImgPutSector(DiskImg img, unsigned sector, + const uchar *data); + + +/* Create a blank disk +*/ +DiskImg DiskImgNew(unsigned sectors, unsigned bytes_per_sector); + + +/* Write a disk image - silently ignored for read only disks. Returns TRUE + for success. If path is NULL then the path the file was loaded from is used. +*/ +int DiskImgSave(DiskImg img, const char *path); + + +/* Free up a disk image +*/ +void DiskImgFree(DiskImg img); + + +/* Returns info on a disk +*/ +void DiskImgInfo(DiskImg img, DiskInfo *info); + + +/* Returns path to a disk +*/ +const char *DiskImgPath(DiskImg img); + + +#endif + + +/* END OF FILE */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..3c9ef78 --- /dev/null +++ b/src/main.c @@ -0,0 +1,140 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + +*/ +static const char id[]="$Id$"; + +#include <stdlib.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#include "serial.h" +#include "disk.h" +#include "config.h" +#include "token.h" +#include "util.h" + + +/* ---------------------------------------- MACROS +*/ +#define TRUE 1 +#define FALSE 0 + + +/* ---------------------------------------- STATIC +*/ +static int quit=FALSE; + + +/* ---------------------------------------- COMMAND HANDLERS +*/ + +static void Quit(int argc, char *argv[]) +{ + quit=TRUE; +} + + +static void Start(int argc, char *argv[]) +{ + SerialOpen(SConfig(CONF_DEVICE)); +} + + +static void Stop(int argc, char *argv[]) +{ + SerialClose(); +} + + +static void Debug(int argc, char *argv[]) +{ + int flag; + + if (YesNo(argv[1],&flag)) + { + SerialDebug(flag); + } + else + { + printf("Invalid argument\n"); + } +} + + +/* ---------------------------------------- COMMAND TABLE +*/ + +static Command cmd[]= +{ + {"quit", 1,1, "quit", "Exit atarisio", Quit}, + {"start", 1,1, "start", "Start emulation", Start}, + {"stop", 1,1, "stop", "Stop emulation", Stop}, + {"debug", 2,2, "debug on|off", "Switch debug on/off", Debug} +}; + + +/* ---------------------------------------- MAIN +*/ +int main(int argc, char *argv[]) +{ + char buff[1024]; + + TokenRegister(sizeof cmd/sizeof cmd[0],cmd); + SerialInit(); + DiskInit(); + + printf("atarsio, Copyright (C) 2004 Ian Cowburn\n"); + printf("atarsio comes with with ABSOLUTELY NO WARRANTY.\n"); + + if (!ConfigRead()) + { + fprintf(stderr,"Failed to read ~/.siorc\n"); + return EXIT_FAILURE; + } + + if (!quit) + { + printf("%s",SConfig(CONF_PROMPT)); + fflush(stdout); + + while(fgets(buff,sizeof buff,stdin)) + { + TokenRun(buff); + + if (quit) + break; + + printf("%s",SConfig(CONF_PROMPT)); + fflush(stdout); + } + } + + return EXIT_SUCCESS; +} + + +/* END OF FILE */ diff --git a/src/serial.c b/src/serial.c new file mode 100644 index 0000000..6f25892 --- /dev/null +++ b/src/serial.c @@ -0,0 +1,472 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Serial wrappers. + +*/ +static const char ident[]="$Id$"; + +#include <pthread.h> + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <termios.h> +#include <fcntl.h> +#include <errno.h> +#include <time.h> + +#include "serial.h" +#include "token.h" + +static const char ident_h[]=ATARISIO_SERIAL_H; + + +/* ---------------------------------------- MACROS +*/ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define WHICH (pthread_equal(pthread_self(),thread) ? "READ":"MAIN") + +/* Just as I'm always forgetting how many... +*/ +#define MILLI2MICRO(x) ((x)*1000) + + +/* ---------------------------------------- EXTERN +*/ +const uchar SIO_ACK=0x41; +const uchar SIO_NACK=0x4e; +const uchar SIO_COMPLETE=0x43; +const uchar SIO_ERROR=0x45; + + +/* ---------------------------------------- STATICS +*/ +static int fd=-1; +static SIOCallback devtable[256]={0}; +static int debug=FALSE; +static int thread_created=FALSE; + +static int do_debug; + +static pthread_mutex_t mutex; +static pthread_t thread; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static void Error(const char *p) +{ + perror(p); + + if (fd!=-1) + { + if (pthread_equal(pthread_self(),thread)) + { + printf("WARNING: Reader thread exiting - " + "serial port is still open\n"); + pthread_mutex_unlock(&mutex); + pthread_exit(NULL); + } + else + { + printf("Trying to closing serial device\n"); + SerialClose(); + } + } +} + + +static void Fatal(const char *p) +{ + fprintf(stderr,"FATAL: "); + perror(p); + exit(EXIT_FAILURE); +} + + +static void Lock(void) +{ + /* printf("[LOCK %s]\n",WHICH); */ + pthread_mutex_lock(&mutex); +} + + +static void Unlock(void) +{ + /* printf("[UNLOCK %s]\n",WHICH); */ + pthread_mutex_unlock(&mutex); +} + + +void SerialPutchar(uchar c) +{ + if (do_debug) + printf("PUTCHAR: %2.2x (%c)\n",c,isprint(c) ? c:'?'); + + if (write(fd,&c,1)==-1) + { + Error("write:"); + return; + } +} + + +void SerialWrite(const uchar *p, size_t len) +{ + uchar csum; + + if (do_debug) + { + printf("SENDING:\n"); + HexDump(p,len); + } + + csum=Checksum(p,len); + + while(len>0) + { + ssize_t w; + + w=write(fd,p,len); + + if (w==-1) + { + Error("write:"); + return; + } + + p+=w; + len-=w; + } + + write(fd,&csum,1); + + if (do_debug) + printf("CHECKSUM: %2.2x\n",csum); +} + + +int SerialRead(uchar *p, size_t len) +{ + uchar *orig; + uchar csum; + + orig=p; + + while(len>0) + { + ssize_t r; + + r=read(fd,p,len); + + if (r==-1) + { + Error("read:"); + return FALSE; + } + + p+=r; + len-=r; + } + + if (read(fd,&csum,1)!=1) + { + Error("read:"); + return FALSE; + } + + return Checksum(orig,len)==csum; +} + + +/* ---------------------------------------- READER +*/ +static void *ReaderThread(void *p) +{ + SIOCommand cmd; + time_t start; + int st; + int no; + uchar frame[5]; + uchar c; + uchar csum; + + cmd.putchar=SerialPutchar; + cmd.write=SerialWrite; + cmd.read=SerialRead; + + while(TRUE) + { + Lock(); + do_debug=debug; + Unlock(); + + no=0; + + while(no<5) + { + if (read(fd,&c,1)==-1) + Error("read"); + + if (ioctl(fd,TIOCMGET,&st)==-1) + Error("ioctl(TIOCMGET)"); + + if (st&TIOCM_RNG) + { + if (do_debug) + printf("Got byte %d (%c) and RNG set\n", + c,isprint(c) ? c:'?'); + + if (no==0) + start=time(NULL); + + frame[no++]=c; + } + else if (do_debug) + { + printf("Got byte %d (%c) and RNG *NOT* set\n", + c,isprint(c) ? c:'?'); + } + } + + if ((time(NULL)-start)>1) + { + printf("Reader: Got 5 bytes, but too slowly - ignored\n"); + } + else + { + Lock(); + + csum=Checksum(frame,4); + + if (csum==frame[4]) + { + if (do_debug) + { + printf("Frame:\n"); + HexDump(frame,5); + } + + if (devtable[frame[0]]) + { + /* Fill up the command structure + */ + cmd.device=frame[0]; + cmd.cmd=frame[1]; + cmd.aux1=frame[2]; + cmd.aux2=frame[3]; + + /* Let the device do its work + */ + if (do_debug) + printf("Calling handler for device %2.2x\n",cmd.device); + + devtable[cmd.device](&cmd); + } + } + else + { + printf("Reader: Bad checksum, expected %2.2x. Frame:\n",csum); + HexDump(frame,5); + } + + Unlock(); + } + } +} + + +/* ---------------------------------------- COMMAND HANDLERS +*/ +static void Register(int argc, char *argv[]) +{ + int f; + + for(f=0;f<256;f++) + if (devtable[f]) + printf("Device %2.2x regsitered\n",f); +} + + +/* ---------------------------------------- COMMAND TABLE +*/ +static Command cmd[]= +{ + { + "register", + 1,1, + "register", + "Show registered devices", + Register + } +}; + + +/* ---------------------------------------- INTERFACES +*/ +void SerialInit(void) +{ + if (pthread_mutex_init(&mutex,NULL)==-1) + Fatal("pthread_mutex_init"); + + TokenRegister(sizeof cmd/sizeof cmd[0],cmd); +} + + +void SerialOpen(const char *path) +{ + struct termios ios; + + if (fd!=-1) + { + printf("Serial device already open\n"); + return; + } + + fd=open(path,O_RDWR|O_NOCTTY|O_NDELAY); + + if (fd==-1) + { + Error(path); + return; + } + + if (fcntl(fd,F_SETFL,0)==-1) + { + Error("fcntl(F_SETFL,0)"); + return; + } + + if (tcgetattr(fd,&ios)==-1) + { + Error("tcgetattr"); + return; + } + + cfsetispeed(&ios,B19200); + cfsetospeed(&ios,B19200); + + ios.c_cflag|=(CLOCAL|CREAD); + + ios.c_cflag&=~PARENB; + ios.c_cflag&=~CSTOPB; + ios.c_cflag&=~CSIZE; + ios.c_cflag|=CS8; + + /* Not that it'll work if we can't disable it... + */ +#ifdef CRTSCTS + ios.c_cflag&=~CRTSCTS; +#endif + + ios.c_lflag&=~(ICANON|ECHO|ECHOE|ISIG); + + ios.c_iflag&=~(IXON|IXOFF|IXANY); + + ios.c_oflag&=~OPOST; + + if (tcsetattr(fd,TCSANOW,&ios)) + { + Error("tcsetattr"); + return; + } + + if (pthread_create(&thread,NULL,ReaderThread,NULL)==-1) + Fatal("pthread_create"); + + thread_created=TRUE; +} + + +void SerialRegister(int device, SIOCallback func) +{ + if (device<0 || device>255) + { + printf("Illegal device number %d registered\n",device); + return; + } + + Lock(); + devtable[device]=func; + Unlock(); +} + + +void SerialDeregister(int device) +{ + if (device<0 || device>255) + { + printf("Illegal device number %d deregistered\n",device); + return; + } + + Lock(); + devtable[device]=NULL; + Unlock(); +} + + +void SerialDebug(int mode) +{ + Lock(); + debug=mode; + Unlock(); +} + + +void SerialClose(void) +{ + if (fd==-1) + { + printf("Serial not open\n"); + } + + Lock(); + + if (thread_created) + { + printf("Cancelling reader thread..."); + fflush(stdout); + pthread_cancel(thread); + pthread_join(thread,NULL); + printf("Done\n"); + } + + Unlock(); + + close(fd); + + fd=-1; + thread_created=FALSE; +} + + +/* END OF FILE */ diff --git a/src/serial.h b/src/serial.h new file mode 100644 index 0000000..740bed7 --- /dev/null +++ b/src/serial.h @@ -0,0 +1,134 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Serial wrappers. + +*/ + +#ifndef ATARISIO_SERIAL_H +#define ATARISIO_SERIAL_H "$Id$" + +#include <stdlib.h> +#include "util.h" + + +/* ---------------------------------------- TYPES +*/ + +/* Private callbacks furnished to registered devices to read/write serial + data. On errors these will not return. Note that SWrite appends the sent + data with its checksum. SRead reads len+1, the last byte being the checksum. + It returns TRUE if the checksum matches. +*/ +typedef void (*SPutchar)(uchar c); +typedef void (*SWrite)(const uchar *p, size_t len); +typedef int (*SRead)(uchar *p, size_t len); + + +/* Constants (usable with SWrite) for Atari ACK, COMPLETE and ERROR repsonses +*/ +extern const uchar SIO_ACK; +extern const uchar SIO_NACK; +extern const uchar SIO_COMPLETE; +extern const uchar SIO_ERROR; + + +/* Microsecond timings +*/ +#define TIME_ACK 85 +#define TIME_ACK_TO_COMPLETE 255 +#define TIME_COMPLETE_TO_DATA 425 + + +/* A command from the Atari +*/ +typedef enum +{ + eRead = 0x52, + eWrite = 0x57, + eStatus = 0x53, + ePut = 0x50, + eFormat = 0x21, + eDownload = 0x20, + eReadAddr = 0x54, + eReadSpin = 0x51, + eMotorOn = 0x55, + eVerifySec = 0x56 +} ESIOCmdType; + + +typedef struct +{ + uchar device; /* The device ID */ + ESIOCmdType cmd; /* The command frame type */ + uchar aux1; /* Data */ + uchar aux2; /* Data */ + SPutchar putchar; /* To write a char to the serial port */ + SWrite write; /* To write to the serial port */ + SRead read; /* To read from the serial port */ +} SIOCommand; + + +/* Interface for recieving SIO commands. Note that this interface is expected + to all serial IO responses, including the initial ACK (in case the device + wants to NACK instead). +*/ +typedef void (*SIOCallback)(const SIOCommand* cmd); + + +/* ---------------------------------------- INTERFACES +*/ + +/* Initialise the serial routines. Must be called first. +*/ +void SerialInit(void); + + +/* Opens the passed serial device and configures it for SIO2PC usage. +*/ +void SerialOpen(const char *path); + + +/* Registers a command handler +*/ +void SerialRegister(int device, SIOCallback func); + + +/* Deregisters a command handler +*/ +void SerialDeregister(int device); + + +/* Switch debug mode on/off +*/ +void SerialDebug(int mode); + + +/* Close the serial device +*/ +void SerialClose(void); + + +#endif + + +/* END OF FILE */ diff --git a/src/siorc.example b/src/siorc.example new file mode 100644 index 0000000..e1d4217 --- /dev/null +++ b/src/siorc.example @@ -0,0 +1,17 @@ +# Example rc file +# +# $Id: siorc.example,v 1.1.1.1 2007-05-07 02:05:54 ianc Exp $ +# + +# Path to the serial device +# +set device /dev/cuaa0 + +# Load up initial disks +# +mount d1 /files/atari/dosdisk + + +# Open up the device +# +start Binary files differdiff --git a/src/token.c b/src/token.c new file mode 100644 index 0000000..b71a6cb --- /dev/null +++ b/src/token.c @@ -0,0 +1,257 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Command parsing and execution + +*/ +static const char ident[]="$Id$"; + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "token.h" + +static const char ident_h[]=ATARIOSIO_TOKEN_H; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define MAX_ARGS 128 + + +/* ---------------------------------------- STATICS +*/ + +static int no_cmds=0; +static Command *cmd_table=NULL; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static int QCompare(const void *a, const void *b) +{ + const Command *ca; + const Command *cb; + + ca=a; + cb=b; + + return strcmp(ca->cmd,cb->cmd); +} + + +static int BCompare(const void *a, const void *b) +{ + const Command *cb; + + cb=b; + + return strcmp(a,cb->cmd); +} + + +static char *GetToken(char **ptr) +{ + char qu=0; + char *tok; + char *p; + + p=*ptr; + + while(*p && isspace(*p)) + p++; + + if (!*p) + return NULL; + + if (*p=='\'' || *p=='"') + qu=*p++; + + tok=p; + + if (qu) + { + while(*p && *p!=qu) + p++; + + if (*p) + *p++=0; + else + printf("Warning: unterminated quotes\n"); + } + else + { + while(*p && !isspace(*p)) + p++; + + if (*p) + *p++=0; + } + + *ptr=p; + + return tok; +} + +static int Split(char *p, char **argv) +{ + int argc=0; + + while((argv[argc]=GetToken(&p))) + { + if (argc==MAX_ARGS) + { + argv[argc]=0; + return argc; + } + + argc++; + } + + return argc; +} + + +static void ShowHelp(const char *usage, const char *help) +{ + int l; + + printf("%s ",usage); + + for(l=strlen(usage);l<35;l++) + putchar('.'); + + printf(" %s\n",help); +} + + +static void HelpCommand(int argc, char *argv[]) +{ + if (argc==1) + { + int f; + + for(f=0;f<no_cmds;f++) + ShowHelp(cmd_table[f].usage,cmd_table[f].help); + } + else + { + Command *cmd; + + cmd=bsearch(argv[1],cmd_table,no_cmds,sizeof *cmd_table,BCompare); + + if (!cmd) + printf("Unknown command: %s\n",argv[1]); + else + ShowHelp(cmd->usage,cmd->help); + } +} + + +static void Init(void) +{ + static Command cmd[]= + { + {"help",1,2,"help [command]","Get help on commands",HelpCommand} + }; + + static int init=FALSE; + + if (!init) + { + init=TRUE; + TokenRegister(sizeof cmd/sizeof cmd[0],cmd); + } +} + + +/* ---------------------------------------- EXPORTED INTERFACES +*/ +void TokenRegister(int no, const Command *cmds) +{ + Init(); + + cmd_table=Realloc(cmd_table,sizeof(*cmds)*(no+no_cmds)); + memcpy(cmd_table+no_cmds,cmds,sizeof(*cmds)*no); + no_cmds+=no; + qsort(cmd_table,no_cmds,sizeof *cmd_table,QCompare); +} + + +int TokenRun(const char *line) +{ + char *p; + char *argv[MAX_ARGS]; + int argc; + int res; + int l; + + if (!(l=strlen(line))) + return FALSE; + + res=FALSE; + + p=StrCopy(line); + + if (p[l-1]=='\n') + p[--l]=0; + + argc=Split(p,argv); + + if (argc) + { + Command *cmd; + + cmd=bsearch(argv[0],cmd_table,no_cmds,sizeof *cmd_table,BCompare); + + if (cmd) + { + if (argc>=cmd->min_args && argc<=cmd->max_args) + { + res=TRUE; + cmd->func(argc,argv); + } + else + { + printf("usage: %s\n",cmd->usage); + } + } + else + { + printf("Unrecognised command %s\n",argv[0]); + } + } + + free(p); + + return res; +} + + +/* END OF FILE */ diff --git a/src/token.h b/src/token.h new file mode 100644 index 0000000..b143aa5 --- /dev/null +++ b/src/token.h @@ -0,0 +1,71 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Command parsing and execution + +*/ + +#ifndef ATARIOSIO_TOKEN_H +#define ATARIOSIO_TOKEN_H "$Id$" + +#include "util.h" + + +/* ---------------------------------------- TYPES +*/ + +/* A command callback (note argv[0] is the command itself) +*/ +typedef void (*TokenHandler)(int argc, char *argv[]); + + +/* A command. min and max args have to include the command itself. +*/ +typedef struct +{ + const char *cmd; + int min_args; + int max_args; + const char *usage; + const char *help; + TokenHandler func; +} Command; + + + +/* ---------------------------------------- INTERFACES +*/ + +/* Register commands +*/ +void TokenRegister(int no, const Command *cmds); + + +/* Parse a command line. Returns TRUE if command parsed and run. +*/ +int TokenRun(const char *line); + + +#endif + + +/* END OF FILE */ diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..9ea5749 --- /dev/null +++ b/src/util.c @@ -0,0 +1,165 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Global utilities and types + +*/ +static const char ident[]="$Id$"; + +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <ctype.h> +#include "util.h" + +static const char ident_h[]=ATARISIO_UTIL_H; + +#define HEX_DUMP_LINE 16 + + +/* ---------------------------------------- INTERFACES +*/ +void *Malloc(size_t size) +{ + void *new=malloc(size); + + if (!new) + { + fprintf(stderr,"malloc failed for %lu bytes\n",(unsigned long)size); + exit(EXIT_FAILURE); + } + + return new; +} + + +void *Realloc(void *p, size_t size) +{ + void *new=realloc(p,size); + + if (!new) + { + fprintf(stderr,"realloc failed for %lu bytes\n",(unsigned long)size); + exit(EXIT_FAILURE); + } + + return new; +} + + +char *StrCopy(const char *source) +{ + return strcpy(Malloc(strlen(source)+1),source); +} + + +int YesNo(const char *str, int *flag) +{ + if (strcasecmp(str,"on")==0 || + strcasecmp(str,"yes")==0 || + strcasecmp(str,"1")==0) + { + *flag=1; + return 1; + } + + if (strcasecmp(str,"off")==0 || + strcasecmp(str,"no")==0 || + strcasecmp(str,"0")==0) + { + *flag=0; + return 1; + } + + return 0; +} + + +void HexDump(const uchar *p, size_t len) +{ + char asc[HEX_DUMP_LINE+1]; + size_t f; + + f=0; + + while(f<len) + { + if ((f%HEX_DUMP_LINE)==0) + printf("%4.4x: ",f); + + printf(" %2.2x",p[f]); + + if (isprint(p[f])) + asc[f%HEX_DUMP_LINE]=p[f]; + else + asc[f%HEX_DUMP_LINE]='.'; + + asc[(f%HEX_DUMP_LINE)+1]=0; + + f++; + + if ((f%HEX_DUMP_LINE)==0) + printf(" %s\n",asc); + } + + if (f%HEX_DUMP_LINE) + { + while(f%HEX_DUMP_LINE) + { + printf(" "); + f++; + } + + printf(" %s\n",asc); + } +} + + +int GetInt(const char *p) +{ + long l; + + l=strtol(p,NULL,0); + + return l; +} + + +uchar Checksum(const uchar *p, size_t len) +{ + int sum; + size_t f; + + sum=0; + + for(f=0;f<len;f++) + { + sum+=p[f]; + sum=(sum&0xff)+(sum>>8); + } + + return (uchar)(sum&0xff); +} + + + +/* END OF FILE */ diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..304e02f --- /dev/null +++ b/src/util.h @@ -0,0 +1,82 @@ +/* + + atarisio - A UNIX backend for an Atari SIO2PC lead. + + Copyright (C) 2004 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Global utilities and types + +*/ + +#ifndef ATARISIO_UTIL_H +#define ATARISIO_UTIL_H "$Id$" + +#include <stdlib.h> + +/* ---------------------------------------- TYPES +*/ + +typedef unsigned char uchar; + + +/* ---------------------------------------- INTERFACES +*/ + +/* Returns result from malloc(size), calling exit() if it fails. +*/ +void *Malloc(size_t size); + + +/* Returns result from realloc(p,size), calling exit() if it fails. +*/ +void *Realloc(void *p, size_t size); + + +/* Copies a string. The result must be freed. +*/ +char *StrCopy(const char *source); + + +/* Set flag TRUE/FALSE depending on the contents of str. + 'on', 'yes' and '1' are TRUE, 'off', 'no' and '0' false. + + Returns FALSE is str is invalid. +*/ +int YesNo(const char *str, int *flag); + + +/* Hex dump to stdout +*/ +void HexDump(const uchar *p, size_t len); + + +/* Get an integer from a string +*/ +int GetInt(const char *p); + + +/* Returns an Atari serial style checksum. +*/ +uchar Checksum(const uchar *p, size_t len); + + +#endif + + +/* END OF FILE */ |