Table of Contents Editor's Forum .................................... 1 Herb Schaaf's Small C Programs .................... 2 File Comparison ................................... 10 Real Windows for SuperBasic ....................... 15 C Compiler Comparison ............................. 17 Editor's Forum By Timothy Swenson After putting out two issues rather quickly, your all probably wondering what happened to this issue. There is one simple explanation: bad keyboard. My QL feel ill by the most common ailment of older QL systems, a bad connection between the keyboard matrix and the motherboard. My QL went bad in early February, just after I got the last issue out. I tried to buy a keyboard replacement. like Schon, but no one had it (plus I heard some negative reviews about the Schon keyboard). I found that Paul Holmgren had new keyboard matrixes. I sent my check and waited for the matrix to arrive. After a few weeks, I called Paul back. It seemed that the the U.S. Snail mail had lost the package. Paul had to send another keyboard. After about 4 weeks, I am now back up and running. An other item that helped with the delay, was my moving to another apartment. See the back of the Journal for the new address. It's not very, since we stayed in the same apartment complex. Just after mailing the last newsletter, my wife informed me that we will be needed a second bedroom in, oh, about 9 months. When my QL was down, I was faced with the idea that my QL will not last forever. I was pondering about what type of system to move to. I wanted one that would last for a few years, so my older computers were out. The idea of moving to MS-DOS did not appeal to me. I know MS-DOS well, but it's not that interesting. A Unix system would be nice, but thier sort of expensive, plus low on good application software. I finally came to the conclusion that the Atari ST would be a good system to move to. Primarily because it has a QL page 1 emulator available for it. This means that I could move up to newer hardware, but still use QDOS. I don't know how well the emulator is, but I do know that it is twice as fast as a normal QL. One of the members of CATS has an ST with QL, IBM, and MAC emulators. I'll have to talk to him about it. I'm not saying that I will run down and buy an ST in the next month or two. I'm going to stick with my QL for as long as possible. My QL has been pretty reliable so far. No chips have blown, and only one microdrive has gone bad. No big deal since I have gone entirely to disks. The only problem is the keyboard. I'm investigating a product from the German company ABC, that allows the QL to use a standard IBM keyboard. In the November 1990 issue of "Dr. Dobb's Journal", there is an article about Software Patents written by the Leage for Programming Freedom. It's the kind of article that all programmer's should read. I really recommend that every one read it. If you can't find that issue at the local library, let me know and I'll send you a copy. I've been following the Software Patent and "look and feel" copyright suits in the computer press. After reading the above mentioned article, I can see the importance of these current suits. Where I work, I deal with a lot of computer users that are not computer people. Most of them complain that they have to learn a new command set/interface with each program. They would like more programs to work the same. But the software industry is working against this principle. With these patents and suits, programmer's are forced to make thier software as different as possible from the others. If they choose not to, they either have to pay a license fee or go to court. The industry needs to stop looking at just the bottom line and look at what the user really needs. Well, time to get off my soapbox. Hope you enjoy this issue and find it usefull. I encourage all of your to send me a line and tell me how you feel about the Journal. Herb Schaaf's Small C Programs By Herb Schaaf [Editor's Note - Herb Schaaf has sent me a couple of Small C programs via e-mail. He did not include an article about the two programs, so I am just presenting them as is. One is an update of my previous find_c. Be aware that Herb adds a number of bells and whistles in his programs. They are not short and sweet.] /* find1f_c Feb 23 8:45 am * from QL Hacker's Journal Feb 1991 page 1-4 * trying another approach seems to work ungets()? */ #include #define LOOKLEN 21 #define BUFSIZE LOOKLEN /* * see K&R page 79 Ex. 4-7 * */ char buf[BUFSIZE], *s; int fd, b, bufp, len; page 2 main() { char c, l, search[LOOKLEN], lookfor[LOOKLEN], initok[LOOKLEN], file[42]; int file_count, str_len, i, pat_find, fd2, ; /* abcdefghijklmnopqrstuvwxyz 0123456789 xyxyz test string */ /* aabbccaaabbbccc a ab abc aababc vividvivid more_tests */ /* ABC Abc ABc aBC abC aaaaaaaaaxYzaxyZaaaaaaaaaaABC */ file_count = pat_find = bufp = 0; /* Initialize to Zero */ strset(search,'\0'); strset(lookfor,'\0'); strset(initok,'\0'); strset(file,'\0'); strset(buf,'\0'); printf("Enter name of file to be searched : "); gets(file); printf("%s\n",file); printf("Enter string (%d characters or less) to be matched\n",LOOKLEN-1); printf(" Search will be case insensitive \n"); gets(search); if (strlen(search)>=LOOKLEN) { strncpy(initok,search,LOOKLEN-1); strncpy(search,initok,LOOKLEN-1); strset(initok,'\0'); } fd = fopen(file,"r"); if (fd == NULL) { printf("Did not open file\n"); printf("file in use? example: ram3_find1e_c\n"); printf("Any key to exit\n"); getchar(c); exit(1); } fd2 = fopen("ram3_find1f_log","w+"); if (fd2 == NULL) { printf("Did not open ram3_find1f_log file\n"); printf("Any key to exit\n"); getchar(c); exit(1); } else printf("file : ram3_find1f_log is open for w+ \n"); str_len = strlen(search); fputs(search,fd2); strncpy(lookfor,search,str_len); l = lookfor[0]; printf("Searching file: %s\n",file); fprintf(fd2,"\nSearching file: %s\n",file); printf(" looking for %s \n",lookfor); fprintf(fd2,"looking for %s \n",lookfor); while(( c = getch()) != EOF) { ++file_count; page 3 if((lexorder(c,l)) == 0) { strset(initok,'\0'); initok[0] = c; for(i=1; i %s @ %u # %u\n",lookfor,initok ,file_count,pat_find); fprintf(fd2,"%s <==> %s @ %u # %u\n",lookfor,initok ,file_count,pat_find); } } } printf("\nFound %d patterns that match %s\n",pat_find, search); fprintf(fd2,"\nFound %d patterns that match %s\n", pat_find,search); fclose(fd2); fclose(fd); printf("A log of this session is in ram3_find1f_log\nAny key to exit\n"); getchar(); } /* FUNCTION CALLS - getch, ungetch, ungets, strset */ getch() /* * get a pushed back character * */ { if(bufp > 0) { b = buf[--bufp]; return b; } else { b = getc(fd); return b; } } ungetch(b) /* * push character back on input * */ int b; { if(bufp >= BUFSIZE) { printf("ungetch: too many characters\n"); } else { buf[bufp++] = b; } } ungets(s) char *s; { page 4 len = strlen(s); while( (len > 0) ) ungetch(s[--len]); } strset(string,chr) /* * fill string with a character * */ char *chr, *string; { int i, length; length = strlen(string); for(i=0; i char file[42], c; int fd, *msp; main() { printf("Enter name of file to be sent to MSP-10 printer:\n"); gets(file); fd = fopen(file,"r"); if(fd == NULL) { printf("Unable to open file: %s\n",file); printf("\nAny key to exit\n"); getchar(c); exit(1); } else { printf("Ready to send %s to MSP-10\n",file); } msp = fopen("ser1hc","w"); printf("fd = %d msp = %d\n",fd,msp); printf("If above looks ok [SPACE BAR] to continue\n x to exit\n"); c = getchar(); if(c == 'x') { exit(1); } if (msp != NULL) { stdprint(msp); epsfont(msp); fputc(27,msp); /* sets printer for */ fputc(109,msp); /* Standard */ fputc(0,msp); /* EPSON */ fputc(EOF,msp); /* Character Set */ page 5 printfile(fd,msp); fclose(fd); leave(msp); } printf(" + once more to really exit(1); !!\n"); getchar(c); exit(1); } /* E N D O F M A I N - - - - - - - - - - - - - - */ /* FUNCTION CALLS : stdprint(msp) standardize printer to default settings epsfont(msp) set printer for Epson FX styles ibmfont(msp) set printer for ibm styles newline(msp) CR + LF + '\n' leave(msp) close file to printer and leave this exercise printit(c,msp) printer does one character at a time printfile(fd,msp) send entire file to printer epsstd(c,msp) printer does entire Epson standard set epsgra(c,msp) printer does entire Epson graphics set ibmstd(c,msp) printer does entire IBM character set ibmacc(c,msp) printer does entire IBM accented character set */ /* * STDPRINT set printer to power on defaults * */ stdprint(msp) int msp; { printf("@ stdprint : msp = %d\n",msp); if (msp != NULL) { fputc(27,msp); fputc(64,msp); newline(msp); } } /* * EPSFONT select Epson FX fonts * */ epsfont(msp) int msp; { printf("@ epsfont : msp = %d\n",msp); if (msp != NULL) { /* fputs("@ epsfont",msp); */ newline(msp); fputc(EOF,msp); fputc(27,msp); fputc(126,msp); fputc(53,msp); fputc(0,msp); fputc(EOF,msp); } } /* * IBMFONT select IBM Graphics fonts * */ ibmfont(msp) int msp; { printf("@ ibmfont : msp = %d\n",msp); if (msp != NULL) { /* fputs("@ ibmfont",msp); */ newline(msp); page 6 fputc(27,msp); fputc(126,msp); fputc(53,msp); fputc(1,msp); } } /* * NEWLINE line feed and carriage return * */ newline(msp) int msp; { /* printf("@ newline : msp = %d\n",msp); */ if (msp != NULL) { fputc(10,msp); fputc(13,msp); fputc(138,msp); fputc(141,msp); printf("\n"); } } /* * LEAVE leave this program * */ leave(msp) int msp; { printf("\n@ leave : msp = %d\n",msp); if (msp != NULL) { printf("\n\nAny key when you're ready to leave\n"); getchar(); fclose(msp); } } /* * PRINTIT print to screen and to printer * */ printit(c,msp) char c; int msp; { if (msp != NULL) { fputc(c,msp); fputc(c,stdout); } } /* * PRINTFILE prints file to stdout & to printer * */ printfile(fd,msp) int fd, msp; { char c; int i; while(((msp != NULL) && (( c = getc(fd)) != EOF))) { fputc(c,msp); fputc(c,stdout); if(iscntrl(c)) { if( c == 10 ) { fputc(10,msp); fputc(13,msp); fputc(138,msp); fputc(141,msp); } if( c == 9 ) { fputc(27,msp); fputc(102,msp); fputc(0,msp); fputc(3,msp); /* sets printhead 3 spaces right */ } page 7 } } } /* * EPSON STANDARD CHARACTER SET * */ epsstd(c,msp) char c; int msp; { printf("@ epsstd : msp = %d\n",msp); if (msp != NULL) { printf("@ epsstd Doing Standard Epson Character Set\n"); fputc(EOF,msp); stdprint(msp); epsfont(msp); fputc(27,msp); fputc(109,msp); fputc(0,msp); fputc(EOF,msp); for(c=32; c<=126; c++) { printit(c,msp); } newline(msp); for(c=161; c<=254; c++) { printit(c,msp); } newline(msp); } } /* * EPSON GRAPHICS SET * */ epsgra(c,msp) char c; int msp; { printf("@ epsgra : msp = %d\n",msp); if (msp != NULL) { fputs("@ epsgra",msp); fputc(EOF,msp); printf("Doing Epson Graphics Only Characters\n"); stdprint(msp); epsfont(msp); fputc(27,msp); fputc(109,msp); fputc(4,msp); fputc(EOF,msp); for(c=128; c<=159; c++) { printit(c,msp); } newline(msp); } } /* * IBM STANDARD CHARACTER SET * */ ibmstd(c,msp) char c; int msp; { printf("@ ibmstd : msp = %d\n",msp); if (msp != NULL) { fputs("@ ibmstd",msp); newline(msp); printf("Doing IBM Standard Character Set\n"); stdprint(msp); ibmfont(msp); page 8 fputc(27,msp); fputc(55,msp); fputc(EOF,msp); printit(21,msp); for(c=32; c<=126; c++) { printit(c,msp); } newline(msp); fputs("IBM Extended",msp); newline(msp); fputc(EOF,msp); for(c=160; c<=254; c++) { printit(c,msp); } newline(msp); } } /* * IBM ACCENTED CHARACTERS * */ ibmacc(c,msp) char c; int msp; { printf("@ ibmacc : msp = %d\n",msp); if (msp != NULL) { fputs("@ ibmacc",msp); newline(msp); printf("Doing IBM Accented Characters Set\n"); stdprint(msp); ibmfont(msp); fputc(27,msp); fputc(54,msp); for(c=3; c<=6; c++) { printit(c,msp); } for(c=128; c<=159; c++) { printit(c,msp); } newline(msp); } } File Comparison By Timothy Swenson Fcomp_c is another C program from the C User's Journal disk #236 (Highly Portable Utilites). I only had to make a few minor changes to port it to the QL. The biggest change being uncommenting the #define NO_STRING_H. I have tried the program out with two short test files. Below is the two files and the output from fcomp_c. File #1: This is fcomp_c test file number 1. Line number 1 Line number 2 Line number 3 Line number 4 Line number 5 Line number 6 page 9 File #2: This is fcomp_c test file number . Line number 1 Line number 2 Line number 3 This line is added Line number 4 Line number 6 Output From fcomp_c Changed line 1 This is fcomp_c test file number 1. To: This is fcomp_c test file number 2. Inserted after line 3: Inserted after line 6: This line added Deleted line 8 Line number 5 /* HEADER: CUG236; TITLE: Compare Text Files; DATE: 05/17/1987; DESCRIPTION: "Best version of DIFF (file comparator) from Jan '86 issue of Software Practice and Experience."; VERSION: 1.1; KEYWORDS: File Comparator, File Compare, File Comparison, File Comparison Utility; FILENAME: FCOMP.C; SEE-ALSO: DIFF; COMPILERS: vanilla; AUTHORS: Chuck Allison; */ /* fcomp.c: file comparator that beats DIFF! */ #include /* * Portability Note: 8-bit systems often don't have header file string.h. * If your system doesn't have it, uncomment the following #define. */ #define NO_STRING_H /* * Portability Note: Back in K & R days, standard library function malloc() * was called alloc(). Some compilers (e.g. Eco-C under CP/M) haven't made page 10 * the name change. If yours is one of these compilers, uncomment the * following #define: */ /* #define malloc(p) alloc(p) */ /* * Portability Note: The AZTEC C compilers handle the binary/text file * dichotomy differently from most other compilers. Uncomment the following * pair of #defines if you are running AZTEC C: */ /* #define getc(f) agetc(f) #define putc(c,f) aputc(c,f) */ #ifdef NO_STRING_H int strcmp(), strlen(); #else #include #endif #define MAXLINES 1000 #define ORIGIN MAXLINES #define INSERT 1 #define DELETE 2 struct edit { struct edit *link; int op; int line1; int line2; }; char *A[MAXLINES], *B[MAXLINES]; void exit(); void main(argc,argv) int argc; char *argv[]; { int col, d, k, lower, m, max_d, n, row, upper; int last_d[2*MAXLINES+1]; struct edit *new, *script[2*MAXLINES+1]; char *malloc(); int atoi(), in_file(); void exceed(), fatal(), put_scr(); if (argc > 1 && argv[1][0] == '-') { max_d = atoi(&argv[1][1]); ++argv; --argc; } else max_d = 2*MAXLINES; page 11 if(argc != 3) fatal("fcomp requires two file names."); m = in_file(argv[1],A); n = in_file(argv[2],B); for (row=0 ; row upper) { puts("The files are identical."); exit(0); } for (d = 1 ; d <= max_d ; ++d) { for ( k = lower ; k <= upper ; k +=2) { new = (struct edit *) malloc(sizeof(struct edit)); if (new == NULL) exceed(d); if (k == ORIGIN-d || k != ORIGIN+d && last_d[k+1] >= last_d[k-1]) { row = last_d[k+1]+1; new->link = script[k+1]; new->op = DELETE; } else { row = last_d[k-1]; new->link = script[k-1]; new->op = INSERT; } new->line1 = row; new->line2 = col = row + k - ORIGIN; script[k] = new; while(row < m && col < n && strcmp(A[row],B[col]) == 0) { ++row; ++col; } last_d[k] = row; if (row == m && col == n) { put_scr(script[k]); exit(!0); } if (row == m) lower = k+2; if (col == n) upper = k-2; } page 12 --lower; ++upper; } exceed(d); } int in_file(filename,P) char *filename, *P[]; { char buf[100], *malloc(), *save, *b; FILE *fp; int lines = 0; void fatal(); if ((fp = fopen(filename,"r")) == NULL) { fprintf(stderr, "Cannot open file %s.\n",filename); exit(!0); } while(fgets(buf,100,fp) != NULL) { if (lines >= MAXLINES) fatal("File is too large for diff."); if ((save = malloc(strlen(buf)+1)) == NULL) fatal("Not enough room to save the files."); P[lines++] = save; for (b = buf ; *save++ = *b++ ; ) ; } fclose(fp); return(lines); } void put_scr(start) struct edit *start; { struct edit *ep, *behind, *ahead, *a, *b; int change; ahead = start; ep = NULL; while (ahead != NULL) { behind = ep; ep = ahead; ahead = ahead->link; ep->link = behind; } while( ep != NULL) { b = ep; if (ep->op == INSERT) printf("Inserted after line %d:\n",ep->line1); else { do { a = b; b = b->link; } while (b!=NULL && b->op == DELETE && b->line1 == a->line1+1); page 13 change = (b!=NULL && b->op == INSERT && b->line1 == a->line1); if (change) printf("\nChanged "); else printf("\nDeleted "); if (a == ep) printf("line %d\n",ep->line1); else printf("lines %d-%d:\n",ep->line1,a->line1); do { printf(" %s",A[ep->line1-1]); ep = ep->link; } while (ep != b); if (!change) continue; printf("To:\n"); } do { printf(" %s",B[ep->line2-1]); ep = ep->link; } while (ep != NULL && ep->op == INSERT && ep->line1 == b->line1); } } void fatal(msg) char *msg; { fprintf(stderr,"%s\n",msg); exit(!0); } void exceed(d) int d; { fprintf(stderr,"The files differ in at least %d lines. \n",d); exit(!0); } Real Windows for SuperBasic by Timothy Swenson The QUANTA library has a number of routines that will allow real non-destructive windows in SuperBasic. Since I started working with C, I wanted the same feature written to used with both C compilers. Before going straight into C, I decided to test my algorithm in SuperBasic. SuperBasic is far easier to do developmental testing on. I was not initially going for speed, but correctness of my algorithm. I'm glad I used SuperBasic, because my first couple of algorithms were way off the mark. After totally revamping my algorithm, I finally got the program to work. page 14 Below is the SSB code for real windows in SuperBasic. It is slow, even when compiled. I'm presenting it here for it's alroithm and not for actual use. I hope to translate it to both Small C and Lattice C, but so far a have a few details to work out. I hope you find it usefull. Since the program does not keep track of the dimensions of each window created, you can use the procedures to move a piece of the screen to another part of the screen. Save the part you want to move, then give different coordinates when you load it. ** This number may need to be changed as needed DIM store(5000) DEFine PROCedure save_bg (length, height, x, y) LOCal mem, i, j mem = 1 FOR i = 131072+(y*128) TO 131072+((y+height)*128) STEP 128 FOR j = i+(x/4) TO i+((x+length)/4) STEP 2 store(mem) = PEEK_W(j) mem = mem + 1 END FOR j END FOR i END DEFine save_bg DEFine PROCedure load_bg (length, height, x, y) LOCal mem, i, j mem = 1 FOR i = 131072+(y*128) TO 131072+((y+height)*128) STEP 128 FOR j = i+(x/4) TO i+((x+length)/4) STEP 2 POKE_W j,store(mem) mem = mem + 1 END FOR j END FOR i END DEFine load_bg Here is an example program using the procedures: 100 save_bg 80, 100, 40, 20 110 OPEN #3, con_80x100a40x20_32 120 PAPER #3,0: INK #3,7 130 CLS #3 140 PRINT #3,"This is a test" 150 PRINT #3,"of real windows" 160 PAUSE 100 170 CLS #3 180 PRINT #3,"Hit Return" 190 PRINT #3,"to make this" 200 PRINT #3,"go away" 210 INPUT #3,in$ 220 CLOSE #3 230 load_bg 80, 100, 40, 20 C Compiler Comparison page 15 By Tim Swenson A while back I was thinking about the two C compilers available for the QL. The Small C compiler is easy to use, quick to compile, but limited in it's scope of the C language. The Lattice C compiler is not so easy to use, slow to compiler, but supports the full C language. The question is which one to use. Personally, I prefer the Small C compiler. When ever possible I will use it in my projects. After comparing how much time each compler takes to compile, I decided to find out which one produced faster code. With the Lattice C longer compile time, I expected it to spend some of that compile time optimising it's code. I used two simple benchmark programs. One for number crunching (prime numbers) and the other for recursion (fibonacci sequence). The prime number benchmark comes from an the CATS newsletter (Jan. 1990), by Duane Parker. Herb Schaaf wrote the C version for both compilers. The second program is from Dr. Dobb's Journal (see code for issue). Below is the results of these tests: Prime Numbers Small C Lattice C 29000-32767 5 sec 3 sec 1000-32767 32 sec 20 sec Recursion 189 sec 193 sec After I ran the first benchmark, I expected Lattice C to out perform Small C on all benchmarks. I was surprised to see the second results. It looks like Lattice C is faster for number crunching, but for recursive procedure calls, Small C is just a bit faster. The next time you are writting a C program consider which compiler might be better suited for your needs. With recursive programs, you won't loose speed with Small C. /* The Fibonacci benchmark tests recursive procedure calls. From Dr. Dobb's Journal Feb. '89 P. 40 Small C Version */ #include #define NUMBER 24 #define NTIMES 34 main() { int i, value, secs, d1[2], d2[2]; *d1 = date(); printf("%d iterations: ", NTIMES); for (i=1; i <= NTIMES; i++) value = fib(NUMBER); page 16 printf("\n"); *d2 = date(); secs = d2[1] - d1[1]; printf("%d seconds\n",secs); } fib(x) int x; { if ( x > 2 ) return (fib(x-1) + fib(x-2)); else return (1); } /* The Fibonacci benchmark tests recursive procedure calls. From Dr. Dobb's Journal Feb. '89 P. 40 Lattice C Version */ #include #include #define NUMBER 24 #define NTIMES 34 main() { int i, value, secs, d1, d2; d1 = readclock(); printf("%d iterations: ", NTIMES); for (i=1; i <= NTIMES; i++) value = fib(NUMBER); printf("\n"); d2 = readclock(); secs = d2 - d1; printf("%d seconds\n",secs); } fib(x) int x; { if ( x > 2 ) return (fib(x-1) + fib(x-2)); else return (1); } readclock(time) int time; { struct REGS in, out; in.D0 = 19; page 17 QDOS1(&in,&out); time = out.D1; return(time); } page 18