.dw .ul The Editor's Forum .do .uo This issue of the QHJ is a little late. It's been three months since the last issue. I try to get an issue out every two months. The reason for the delay is the arrival of my daughter, Caitlan Anne. The Tech Specs are: born 27 Sept, 5 lbs 2 oz, blonde hair and blue eyes. We have had family staying with us helping with the baby since we brought Caitlan home. Between the baby and family, I've found little time for computers. I'll try to still put out an issue every two months. To help in this I could use articles from readers. I'm also having a problem finding topics for articles. If you have an article or a topic, send me a note. A little help is nice to get. .dw .ul Italian Software .uo .do By Timothy Swenson While looking at the archives for the comp.sources.misc newsgroup on Usenet I noticed that there had been a posting on tools for the QL. I was able to download the posting and get the tools. The programs came from Giuseppe "Beppe" Zanetti of Italy. Giuseppe has written a number of programs, including the ones posted to comp.sources.misc. Below is a list of the programs in the posting: BOOT_BFP, INDENTER_BAS, CODER_BAS, TOKEN_BAS, TOBORLAN_BAS, TEST1_PAS, TEST2_PAS. A SuperBasic to Pascal converter. COLORS_ASM - Creates affect of 16 colors by alternating between two screens. NEWQL_C - QL screen reader for MS-DOS. BUFFIN_ASM, SERBUFFE_ASM - Multitasking serial buffer and buffin() function. TEXTPROC_BAS - Split Quill files in columns and justify text. I have only tried out the Pascal convertor and the text processor. The text processor works as advertised. It takes a Quill document as input and outputs it to the screen or a file, formatted to the column width you specify. It also allows you to control justification and line spacing. The program seems to strip out all Quill formating characters and finds just the pure text in the document. Kind of a neat little program to use and to see how it handles Quill files. I tried to get the Pascal converter to work. The boot program expects the other programs as compiled tasks. I got around this by calling each program in turn. The Tokeniser works, but the program stopped with an "invalid expression" error about half way through the coder. I'll be contacting Giuseppe about fixing this and/or giving it try myself. The program does have some promise. One caveot: the output file is designed for TurboPascal and not QL Pascal. With a little effort one can change the TurboPascal program so it will compile under QL Pascal. It would take some work, but the program could be adapted to output directly to QL Pascal. One stumbling block with the above programs is that all the prompts are in Italian (big surprise :-) ). With some guessing and looking at the code, I was able to get around this limitation. You might find a Italian/English dictionary helpfull. Giuseppe also has a number of other programs that are available directly from him. The programs are for both the QL and MS-DOS. Most programs are public domain (a contribution is up to you) and one is commercial (but cheap). IMPRESS DTP Compiled SB $15 Desktop Publishing for 128K QL IMPRESS CLIP Shareware HTPAINTER Shareware Simple but efficent painter SOCCER MANAGER 2 Shareware BIG COPIER Shareware File copier for single drive system TIFFS Shareware Images scanned with ScanMan Plus SCR_OFF Shareware CUT2QL (MS-DOS) Shareware Converts CUT files to QL Sbytes. Giuseppe can be reached at: via Vergani, 11-35031 Abano Terme (Padova) Italy Tel ++39-49-638225 (please call at 8 pm in Italy) beppe@alessia.dei.unipd.it The programs that come in the QL Tools posting are available from the QHJ. .dw .ul Dutch Connection 2 .do .uo By Timothy Swenson Last issue Mark Martin reported on a Dutch BBS that was being run on a QL. I forgot to mention that the same person that wrote the QL BBS also wrote a number of other programs, that Mark has sent me. The three major programs on the disk are: UNZIP96, QVIEW, and QBOX. UNZIP96 is a program that will unzip programs zipped with PKZIP, the popular MS-DOS utility. I have tried this and it really does work. QVIEW is a terminal program. Since the documentation is in Dutch, I have yet to try it. QBOX is the QL BBS. It's not a plug 'n play system. The designer found some problems with the QL serial ports and basically had to design his own serial port. It's all detailed in the QBOX documentation, including any other problems running a BBS on a QL. Of the above programs, I find UNZIP96 the most usefull. With this utility, you can download a Zipped MS-DOS file (using xmodem) and unzip it on the QL and use the files. In the past, one would have to use an MS-DOS system (or emulator) to unzip the files before they could be used on the QL. All of the files that I have recieved from Mark Martin are available from the QHJ. .ul .dw RPN Calculator .do .uo by Timothy Swenson Reverse Polish Notation (RPN) is mathmatical convention for handling expressions. RPN is most commonly found in Hewlett-Packard calculators. RPN, like Forth and Postscript, is stack oriented. Operators only handle data stored on the stack. The infix (or normal) expression 3 + 5 would be expressed as 3 5 +. Note the operator goes at the end of the expression. What really happens is that the 3 is pushed on the stack, then the 5 is pushed, and finally, when the + is encountered, it takes (pops) two numbers off the stack, adds them and puts the results back on the stack. For the expression (1 + 2) * 3, you would enter 1 2 + 3 *. You don't need ()'s to control what operation takes place first. RPN executes each operator as it comes across it. Below is a Structured SuperBasic program that implements a simple RPN calculator. The left part of the screen is used for entry and results. The right part of the screen is used to show the current stack. With RPN, you can put a lot of numbers on the stack, then enter the operators for them. ** Reverse Polish Notation Calculator T = 0 : F = 0 CLS #1 : CLS #2 num = 0 sx = 0 DIM stack(30) stackp = 1 num_entered = F REPeat loop in$=INKEY$(-1) IF (in$ INSTR "0123456789") THEN num = (10*num) + in$ PRINT #2,in$; num_entered = T END IF IF in$ = "+" THEN IF num_entered = T THEN push(num) num_entered = F END IF plus : num = 0 END IF IF in$ = "-" THEN IF num_entered = T THEN push(num) num_entered = F END IF minus : num = 0 END IF IF in$ = "*" THEN IF num_entered = T THEN push(num) num_entered = F END IF times : num = 0 END IF IF in$ = "/" THEN IF num_entered = T THEN push(num) num_entered = F END IF divide : num = 0 END IF IF in$ = "^" THEN IF num_entered = T THEN push(num) num_entered = F END IF power : num = 0 END IF IF in$ = CHR$(10) THEN push(num) : num = 0 : PRINT #2 END REPeat loop DEFine PROCedure push(x) sx = sx + 1 AT sx,0 : PRINT x stackp = stackp + 1 stack(stackp) = x END DEFine push DEFine FuNction pop LOCal temp IF stackp = 0 THEN PRINT #2,"Stack Error" STOP ELSE temp = stack(stackp) AT sx,0 : PRINT " " sx = sx - 1 IF sx = -1 THEN sx = 0 stackp = stackp - 1 RETURN temp END IF END DEFine pop DEFine PROCedure plus LOCal temp1, temp2 PRINT #2 : PRINT #2," +" temp1 = pop temp2 = pop temp1 = temp1 + temp2 push(temp1) PRINT #2," =" PRINT #2,temp1 END DEFine plus DEFine PROCedure minus LOCal temp1, temp2 PRINT #2 : PRINT #2," -" temp1 = pop temp2 = pop temp1 = temp1 - temp2 push(temp1) PRINT #2," =" PRINT #2,temp1 END DEFine minus DEFine PROCedure times LOCal temp1, temp2 PRINT #2 : PRINT #2," *" temp1 = pop temp2 = pop temp1 = temp1 * temp2 push(temp1) PRINT #2," =" PRINT #2,temp1 END DEFine times DEFine PROCedure divide LOCal temp1, temp2 PRINT #2 : PRINT #2," /" temp1 = pop temp2 = pop IF temp2 = 0 THEN PRINT #2,"Divide by Zero Error" ELSE temp1 = temp1 / temp2 push(temp1) PRINT #2," =" PRINT #2,temp1 END IF END DEFine divide DEFine PROCedure power LOCal temp1, temp2 PRINT #2 : PRINT #2," ^" temp1 = pop temp2 = pop IF temp2 = 0 THEN PRINT #2,"Negative Power Error" ELSE temp1 = temp1 ^ temp2 push(temp1) PRINT #2," =" PRINT #2,temp1 END IF END DEFine power .dw .ul Substring Searching in C .do .uo By Timothy Swenson The December 1988 issue of "Computer Language" had an article on substring searching in C, based on an APL algorithm. One interesting feature of the substring search function is that it returned a array that pointed to all occurances of the substring in the string. Some algorithms only return the first occurance of the substring. The short article does not go into much detail, so I will just present the code. The program has been successfully compiled using C68. It should compile fine under Lattice C and with some work it can be converted to Small-C. STRSRCH_C: /* From December 1988 Computer Language */ /* This C program is based on the follwoing APL program: */ /* $ [0] VEC #is STRING STRSRCH SUBSTG:#io:ARY [1] | returns all postitions of SUBSTG witin STRING [2] #io #is 0 [3] STRING #is.STRING & SUBSTG #is.SUBSTG [4] ARY #is #and/(#iota #rho SUBSTG)#theta STRING #outer =SUBSTG [5] VEC #is ARY/#iota #rho STRING $ */ /* STRSRCH returns a pointer to an integer vector that terminates with -1. any postitive number preceding the -1 is a position of the substg within the string. A negative number preceding the -1 represents an error. */ typedef char * char_ptr; #define sizvec 15 #define NOMEM ( char_ptr )0 int *strsrch( string, substg ) char_ptr string, substg; { register int x, y; static int vec[ sizvec + 1 ]; /* leave room for -1 */ unsigned int sizstg, sizsub, endsiz, *ary; char_ptr startmem, malloc( ); int strlen( ); sizstg = strlen( string ); sizsub = strlen( substg ); endsiz = sizstg - sizsub + 1; /* check boundary conditions: */ if ( ( sizstg == 0 ) && ( sizsub == 0 ) ) { vec[ 0 ] = -5; vec[ 1 ] = -1; return( vec ); } if ( sizsub == 0 ) { vec[ 0 ] = -3; vec[ 1 ] = -1; return( vec ); } if ( sizstg == 0 ) { vec[ 0 ] = -2; vec[ 1 ] = -1; return( vec ); } if ( sizsub > sizstg ) { vec[ 0 ] = -6; vec[ 1 ] = -1; return( vec ); } if ( NOMEM == ( ary = startmem = malloc( endsiz*sizeof( int)))) { vec[ 0 ] = -9; vec[ 1 ] = -1; return( vec ); } /* Start of Algorithm */ for( x = 0; x < endsiz; x++ ) *ary++ = string[ x ] == substg[ 0 ]; for( y = 1, ary = startmem; y < sizsub; y++, ary=startmem ) for( x = y; x < (endsiz + y); x++ ) *ary++ &= string[ x ] == substg[ y ]; for ( y = 0, x = 0; (x < endsiz) && (y < sizvec); x++ ) if ( *ary++ ) vec[ y++ ] = x; vec[ y ] = -1; free( startmem ); return( vec ); } STRTEST_C: /* This function displays: */ /* The positions are: 4 29 */ #include #include "strsrch_c" int main( ) { static char *stg1 = "The files are located in the file cabinet."; static char *stg2 = "file"; int *ptr; ptr = strsrch( stg1, stg2 ); printf("The postitions are: "); while( -1 != *ptr ) printf("%d ",*ptr++ ); printf("\n"); return( 0 ); } .dw .ul Levenstein Distance .do .uo By Timothy Swenson The Levenstein Distance is a measure of how close two strings are to each other. The Levenstein Algoritm is used to calculate the Levenstein Distance. The Levenstein Algorithm takes two strings and determines what it would take to transform one string into the other, using deletions, additions, and changing characters. The more that must be done, the less alike the two strings are. In QHJ #1 the Ratcliff/Obershelp algorithm for inexact pattern matching was discussed. This algorithm determines how close two strings are by recursively finding the largest equal substrings in the two strings. The larger substrings found, the closer the two strings are. This program comes from the May 1991 issue of the C Users Journal. The text accompanying the article explains the program better than I do. If you have questions, you should refer back to the original article. /* ldistance() Determine to what extent two charcter strings are (un)equal using the 'Levenstein distance' algorithm. */ #include #include #include int addition = 1; int change = 3; int deletion = 5; #define COMP_LEN 20 #define ARR_SIZE COMP_LEN + 1 #define SMALLEST_OF(x,y,z) ( (xCOMP_LEN ? COMP_LEN : strlen(requested)); f_len = (strlen(found)>COMP_LEN ? COMP_LEN : strlen(found)); distance[0][0] = 0; for (j = 1; j <= ARR_SIZE; j++) distance[0][j] = distance[0][j-1] + addition; for (j = 1; j <= ARR_SIZE; j++) distance[j][0] = distance[j-1][0] + deletion; for (i = 1; i <= r_len; i++) for (j = 1; j <= f_len; j++) distance[i][j] = SMALLEST_OF( (distance[i-1][j-1] + ZERO_IF_EQUAL(i,j)), (distance[i][j-1] + addition), (distance[i-1][j] + deletion) ); return( distance[r_len][f_len] ); } int main() { int result; printf("Comparing '%s' and '%s' : \n","pennsylvania", "pencilvaneya"); result = ldistance("pennsylvania","pencilvaneya"); printf(" Result = %d\n",result); } .dw .ul QDOS Rights .do .uo By Timothy Swenson A new item I learned this month is that the QDOS rights to North America is held by Mechanical Affinity. I wrote Frank Davis trying to learn more about this. I specif- ically asked him if the rights to QDOS include creating a new QL. He responded by saying that he was not too sure. The uniqueness of the QL is not in its hardware but lies in its operating system, QDOS. If one could implement QDOS (Including SuperBasic with QDOS calls) on another hardware platform, most of us would gladly support the new machine ($costs excluded). This has been done with QL emulators for the Atari ST and the Amiga. If one had the know-how and the ambition, QDOS could be implemented on a new 68000 platform in a language like C. It could be disk-based and not ROM based (I don't know what this will do to QDOS calls. I guess QDOS could be loaded from disk into low memory where it resided now) making it easier for updates and cheaper to produce. Changes could be done to QDOS to make it more "standard" with such things as command line arguements. As it stands, QDOS and SuperBasic are really one thing. QDOS could be separated from SuperBasic, yet still allow SuperBasic to use QDOS commands. This would be similar to MS-DOS Batch files and UNIX shell scripts. The possiblity and opportunity is there, it's just a matter of finding someone to do it. It would take a lot of effort to do this. The English QLAW group (working on a "SuperQL") might have the know-how, but not the opportunity. As far as I understand it, Amstrad still has the rights to QDOS in the rest of the world. Oh well, it's nice to ponder on it. Sometimes I wish I had the know-how, time, and (yes) ambition to do it. I confess that I'm a bit lazy. The initial writing of code is the hardest part. Sometimes it takes me a while to work up the energy to do it. .dw .ul C68 Compiler Benchmarks .do .uo By Timothy Swenson In QHJ #3, I reported on some benchmarks that I ran on Lattice C and Small-C. Now that I had C68, I decided to run the benchmarks against it to see how it compares with the other two compilers. Here is how all three compilers compare: Prime Numbers Small-C Lattice C C68 29000-32767 5 Sec 3 Sec 6 Sec 1000-32767 32 Sec 20 Sec 42 Sec Recursion 189 Sec 193 Sec 227 Sec As it turns out, C68 seems to be the slowest compiler on all of the benchmarks. Since C68 was written for a variety of systems, it's optimzer may not be tweaked for the QL. If you have any applications that are speed dependent you might be better off using Lattice C. For porting software from MS-DOS or UNIX to the QL, C68 is the way to go. Also C68 provides full QDOS support, whereas Lattice C provides only access to traps (you have to write your own functions to utilise the traps). Not having the time to really get into C, I still find Small-C good enough for my uses. Small-C's compiler is hands-down the fastest of the three.