.dw .ul Editor's Forumn .do .uo By Tim Swenson With this issue the QHJ is now 1 year old. It's been fun publishing the QHJ. It's given me a chance to write the kind of articles that I want to write; those aimed at QL programmers. It's also kind of neat to have complete editorial control. Over the last year I have "meet" a number of interesting QL users from all over. Just via e-mail I have meet 4 QL users. With QL users/programmers spread out all over the globe, e-mail is a very effective way to keep in contact. It's far faster then regular mail. For those QL persons interested in getting into e-mail on the Internet, there are a number of Public Access Unix systems around the country that allow users e-mail access for free or a minimal fee. If you are interested, let me know and I'll send you a listing of Public Access Unix systems. Odds are there is one near you. If you are at college, see you Computer Department to see if they have any computers on the Internet. You could get access this way. One note about format. I've dispensed with the Table of Contents. Now that I am using MicroEmacs and a print formating program, I don't know what page each article starts on until I print the issue. In the past I've printed it twice, once to find out the page numbers. To have time and effort, I've cut out the Table of Contents. Since the QHJ is not very big, I foresee any problems. A note of warning to any MicroEmacs users out there. I've run across a bug with both versions 3.8 and 3.9 when you have run out of memory. I ran out of memory while putting this issue together. I deleted a few characters to bring me below the limit and then saved the file. The save took too long and only twice briefly wrote to disk. MicroEmacs said that X lines were written, but upon switching to QDOS, I found that the file had no size. I had to delete quite a number of lines before it would save properly. To prevent this problem, I've recompiled MicroEmacs (3.9) and told it to grab all available memory. I will only use this version of MicroEmacs when doing large files. Well, I wish the best to you all for the new year. May your code be error free and you compiles fast. .dw .ul Core Wars .do .uo By Tim Swenson A while back I took a week off from work to spend time at home finishing some programming projects (hey, I had plenty of leave stored up). One of the projects that I finished was in implementation of Core Wars for the QL. Below is the documentation that I wrote for the program. It details the whole program. INTRODUCTION Core Wars is a game where programs that the user's write, battle with each other in the computer. This game is taken from the Computer Receations column of Scientific American. The game was conceived by A. K. Dewdney. Two programs are loaded into "memory" and then each program is executed in turn, one line at a time. When the computer can not execute a command from one of the programs, it declares that program to be the loser. Programs are written in an assembly-like language called REDCODE. The syntax of REDCODE will be explained later. HOW TO RUN THE PROGRAM Use LRUN to load and run the SuperBasic version. When the program starts you will be asked to enter the file name of the first program. All REDCODE programs are stored in a separate file and then loaded by the program. Enter the name file containing one of the REDCODE programs you want to run. You must include the drive name (flp1_, mdv1_, etc). A number of sample programs are included on the distribution disk. They have the _CW extention. You will be prompted for the file name of the second REDCODE program you want to run. You man run any combination of REDCODE program. The screen will clear and two different windows will appear. As each program executes, the current line of the program will be printed in the window designated for it. It will also print the memory location at which that line is stored. An example is: 392 MOV -1 0 This shows that the command MOV -1 0 was executed a memory location 392. These windows will allow you to see each program as they execute. Below these windows is an area that will display the results of each program as they move through memory. Memory consists of 1000 memory locations. A graphics display of memory had been broken down to two lines of 500 pixels. As each program affects memory, it will be reflected in this graphics display. You will not see the display at first, since empty memory locations are black. Memory locations affected by Program 1 will appear as green, where as those affected by Program 2 will appear as red. This display will vividly show you how the progams are moving through memory and where they stand in relation to each other. When a REDCODE program line can not be executed, the word "ERROR" will be printed in the window of the losing REDCODE program and the Core Wars program will stop and return you to QDOS. REDCODE PROGRAMMING REDCODE is an assembly-like language consisting of commands and operands. Each line of the program has one command and one or two operands, depending on the specific command. Below are the REDCODE commands: MOV A B - Move Move contents of address A to address B ADD A B - Add Add contents of address A to the contents of address B and store results in address B SUB A B - Subtract Subtract contents of address A from contents of address B and store results in address B JMP A - Jump Transfer control (goto) to address A JMZ A - Jump Zero Jump to address A if contents of address B are equal to zero JMG A - Jump Greater Than Zero Jump to address A if contents of address B are greater than zero JMN A - Jump Not Zero Jump to address A if contents of address B are not equal to zero DJZ A B - Decrement Jump Zero Subtract (decrement) 1 from the contents of address B and jump to address A if the contents of address B are equal to zero DJN A B - Decrement Jump Not Zero Decrement 1 from the contents of address B and jump to address A if the contents of address B are not equal to zero CMP A B - Compare Compare contents of address A and B then skip the next instruction if not equal DAT A - Data Non-executing statement. Used to make a memory location useable for storage of data All REDCODE addresses are relative. PC is the Program Counter. The Program Counter controls the execution of the programs and is equivilent to the memory location of the current program line. MOV 0 1 means to put the contents of address PC+0 (the current line) and put it in address PC+1 (the next line). Negative numbers are used to mean lines before the PC. REDCODE does make provisions for direct and indirect addressing. A # before a number is direct addressing and an @ is used for indirect. MOV #0 1 means to put the number 0 in the address PC+1. The indirect commands: DAT 20 MOV 0 @-1 means to put the contents of address PC+0 and store it at the address pointed at by the number at PC-1 (the previous line). This will put the MOV 0 @-1 line at PC+20. Note that you must use a DAT statment and not just store the number 20 at that memory location. This is what the DAT statement is for. Indirect addressing may be used for both A and B operands, direct may be used for A, and direct may only be used for B with the CMP command. All other commands may not use direct addressing of B. The MOV and DAT commands interact differently together. If you MOV #0 20 and address PC+20 is a DAT statement, the DAT 0 is stored at address PC+20. If there is no DAT steatement, the only 0 is stored at PC+20. This second case is used to put "logic bombs" into other programs, so that when the 0 is reached, the Core Wars program has reached an unexecutable command line and that program loses. REDCODE PROGRAM FILES REDCODE programs are stored in a separate file and loaded at runtime. The file is an ASCII file created with an ASCII editor, like ED, or The Editor. Each command line is on a separate line. No blank lines are allowed in the file. The commands must start in the first column. Only 1 space may separate commands and operands. Comments are allowed by using a # in the first column. With a # comment, the entire line is commented out and is ignored by the Core Wars program. See the example REDCODE programs included on the distribution disk to help clarify any questions you may have. Source Code: ** Program Name: CoreWars ** Version: 2.0 ** Author: Timothy Swenson ** Date Created: 17 Oct 1990 ** Date Revised: 19 Oct 1990 ** The is an implementation of Core Wars as found in the ** Computer Recreations column in Scientific American. top_mem=1000 DIM memory$(top_mem,14) @label pc_prg1 = RND(1 TO 1000) pc_prg2 = RND(1 TO 1000) IF ABS(pc_prg1-pc_prg2)=100 THEN GO TO @label OPEN #3,con_512x256a0x0_32 PAPER #3,0 : INK #3,4 : CLS #3 title PRINT #3 PRINT #3,TO 30;"Version 2.0" PRINT #3,TO 26;"By Timothy Swenson"\\ load_prg(pc_prg1) load_prg(pc_prg2) CLS #3 title OPEN #4,scr_200x150a51x51 OPEN #5,scr_200x150a262x51 BORDER #4,2,4 : BORDER #5,2,4 PAPER #4,2 : INK #4,1 PAPER #5,2 : INK #5,1 CLS #4 : CLS #5 CURSOR #3,70,40 PRINT #3," P R O G R A M # 1" CURSOR #3,300,40 PRINT #3," P R O G R A M # 2" REPeat main_loop pc_main = pc_prg1 prog=1 cmd$=memory$(pc_main) PRINT #4,pc_main;" ";cmd$ sep_line evaluate plot_mem pc_prg1 IF pc_prg1=pc_main THEN pc_prg1=pc_prg1+1 ELSE pc_prg1=pc_main END IF pc_main = pc_prg2 prog=2 cmd$=memory$(pc_main) PRINT #5,pc_main;" ";cmd$ sep_line evaluate plot_mem pc_prg2 IF pc_prg2=pc_main THEN pc_prg2=pc_prg2+1 ELSE pc_prg2=pc_main END IF IF pc_prg1>top_mem THEN pc_prg1=pc_prg1-top_mem IF pc_prg2>top_mem THEN pc_prg2=pc_prg2-top_mem END REPeat main_loop STOP DEFine PROCedure title LOCal x,y x=150 : y=10 CSIZE #3,3,1 OVER #3,1 INK #3,2 FOR z = 1 to 5 CURSOR #3,x-z,y-z PRINT #3,"CORE WARS" NEXT z INK #3,4 CURSOR #3,x-6,y-6 PRINT #3,"CORE WARS" CSIZE #3,0,0 END DEFine title DEFine PROCedure load_prg (location) INPUT #3,"Enter file name for Program : ";file_name$ OPEN_IN #6,file_name$ REPeat loop1 INPUT #6,in$ IF EOF(#6) THEN EXIT loop1 IF in$(1)="#" THEN END REPeat loop1 memory$(location)=in$ location=location+1 END REPeat loop1 CLOSE #6 END DEFine load_prg DEFine PROCedure plot_mem (num) LOCal x,y,c IF prog=1 THEN c=4 IF prog=2 THEN c=2 IF num>500 THEN x=num-500 y=235 ELSE x=num y=225 END IF BLOCK #3,1,2,x,y,c END DEFine plot_mem DEFine FuNction get_num(address) LOCal temp$ temp$=memory$(address) IF temp$="" THEN goto_error IF temp$(1 TO 3) <> "DAT" THEN goto_error RETurn temp$(5 TO) END DEFine get_num DEFine PROCedure sep_line LOCal blank IF LEN(cmd$)<5 THEN goto_error arg2 = 0 opcode$=cmd$(1 TO 3) cmd$=cmd$(5 TO) opmodea=1 : opmodeb=1 IF cmd$(1)="#" THEN opmodea=0 IF cmd$(1)=CHR$(64) THEN opmodea=2 IF opmodea<>1 THEN cmd$=cmd$(2 TO) IF (opcode$="JMP") OR (opcode$="DAT") THEN arg1=cmd$ : RETurn blank = " " INSTR cmd$ arg1 = cmd$(1 TO blank-1) cmd$=cmd$(blank+1 TO) IF cmd$(1)="#" THEN opmodeb=0 IF cmd$(1)=CHR$(64) THEN opmodeb=2 IF opmodeb<>1 THEN cmd$=cmd$(2 TO) arg2=cmd$ END DEFine sep_line DEFine PROCedure evaluate IF opmodea=0 THEN a=arg1 IF opmodea=1 THEN a=pc_main+arg1 IF opmodea=2 THEN a=pc_main+get_num(pc_main+arg1) IF opmodeb=0 AND opcode$<>"CMP" THEN PRINT #0,"Direct mode not allowed to second argguement ": STOP IF opmodeb=0 THEN b=arg2 IF opmodeb=1 THEN b=pc_main+arg2 IF opmodeb=2 THEN b=pc_main+get_num(pc_main+arg2) IF a>top_mem THEN a=a-top_mem IF b>top_mem THEN b=b-top_mem IF opcode$="MOV" THEN MOV: RETurn IF opcode$="ADD" THEN ADD: RETurn IF opcode$="SUB" THEN SUBT: RETurn IF opcode$="JMP" THEN JMP: RETurn IF opcode$="JMZ" THEN JMZ: RETurn IF opcode$="JMG" THEN JMG: RETurn IF opcode$="JMN" THEN JMN: RETurn IF opcode$="DJZ" THEN DJZ: RETurn IF opcode$="DJN" THEN DJN: RETurn IF opcode$="CMP" THEN CMP: RETurn IF opcode$="DAT" THEN RETurn goto_error END DEFine evaluate DEFine PROCedure goto_error IF prog=1 THEN PRINT #4," E R R O R" IF prog=2 THEN PRINT #5,"E R R O R" FOR xx = 1 to 4 BEEP 1000,10 NEXT xx STOP END DEFine goto_error DEFine PROCedure MOV IF opmodea=0 THEN temp$=memory$(b)&" " IF temp$(1 TO 3)="DAT" THEN memory$(b)="DAT "&a ELSE memory$(b)=a END IF ELSE memory$(b)=memory$(a) END IF plot_mem b END DEFine MOV DEFine PROCedure ADD LOCal temp IF opmodea=0 THEN temp=get_num(b)+a ELSE temp=get_num(b)+get_num(a) END IF memory$(b)="DAT "&temp END DEFine ADD DEFine PROCedure SUBT LOCal temp IF opmodea=0 THEN temp=get_num(b)-a ELSE temp=get_num(b)+get_num(a) END IF memory$(b)="DAT "&temp END DEFine SUBT DEFine PROCedure JMP pc_main = a END DEFine JMP DEFine PROCedure JMZ LOCal temp temp = get_num(b) IF temp=0 THEN JMP END DEFine JMZ DEFine PROCedure JMG LOCal temp temp = get_num(b) IF temp>0 THEN JMP END DEFine JMG DEFine PROCedure JMN LOCal temp temp = get_num(b) IF temp<>0 THEN JMP END DEFine JMN DEFine PROCedure DJZ LOCal temp temp = get_num(b) temp=temp-1 memory$(b)="DAT "&temp IF temp=0 THEN JMP END DEFine DJZ DEFine PROCedure DJN LOCal temp temp = get_num(b) temp=temp-1 memory$(b)="DAT "&temp IF temp<>0 THEN JMP END DEFine DJN DEFine PROCedure CMP LOCal tempa,tempb IF opmodea=0 THEN tempa=a ELSE tempa=get_num(a) END IF IF opmodeb=0 THEN tempb=b ELSE tempb=get_num(b) END IF IF tempa<>tempb THEN pc_main=pc_main+2 END DEFine CMP DEFine PROCedure list_memory FOR x=1 TO top_mem PRINT memory$(x) NEXT x END DEFine list_memory Example Core War Programs: (indented only for publication. Code must start in the first column) IMP_CW: MOV 0 1 DWARF_CW: DAT -1 ADD #5 -1 MOV #0 @-2 JMP -2 GEMINI_CW: DAT 0 DAT 99 MOV @-2 @-1 CMP -3 #9 JMP 4 ADD #1 -5 ADD #1 -5 JMP -5 MOV #99 93 JMP 93 .dw .ul QLPatch .do .uo By Tim Swenson One of the problems I've encountered in publishing souce code in the QHJ, is the dilemma of publishing a newer version of a program (or bug fixes). I could publish the whole program but this would waste bandwidth if only a small portion of the code has been changed. Another way is to publish the changes to the program and figure a way to have the user edit his version, add the changes and create a newer (second) version. In my dealings with the Unix world, I have heard of a program that does something like this, called Patch. In using Patch, the originator of the program runs the two version of the code through a file comparison utility (like Unix's diff) and creates a diff (or delta) file. The end user takes the diff file, version 1 of the program, and runs it through Patch to create the second version. Patch uses the diff file as a guide to edit the source code. To make this work on the QL, I first needed a file comparison program. A few issues ago, I published FCOMP_C from the C Users Group. With only a few changes to FCOMP_C I was able to get a program that would work for me. I now have FCOMP2_C. Now that I could generate a diff file, I needed a version of Patch. Not having the source code to Patch, I decided to write my own version from scratch, based on the output of FCOMP2_C. Below is the result of that effort, QLPatch. This version is only written in SuperBasic. I plan to port it to C, but I prefer to develop in SuperBasic because it's easier than C. After the source code, I have included an example of two versions of a file and the diff file for them. Take version 1 of the file, the diff file, and run them through QLPatch and see that you indeed get the second version. After this is the diff file for FCOMP_C to create FCOMP2_C. Running this and FCOMP_C though QLPatch will give you a version of FCOMP_C to use with QLPatch. If you encounter any bugs, please let me know. I do plan to use this program for the QHJ and I want it to work as advertised. ** QLPatch ** By Timothy Swenson ** ** This program takes version 1 of a file, a diff file ** created by fcomp_c, and creates version 2 of the ** file. This program can be used to distribute ** changes to a program with out having to redistribute ** all of the source code. You need only distribute those ** lines needed. ** ** This program can be used for any text file, be it a C ** program, Achive program, or a text document. ** TRUE = 1 FALSE = 0 IN_FILE = 4 DIFF_FILE = 5 OUT_FILE = 6 last_insert = FALSE INPUT "Enter Souce File : ";in_file$ INPUT "Enter Diff File : ";diff_file$ INPUT "Enter Output File : ";out_file$ DELETE out_file$ OPEN #IN_FILE,in_file$ OPEN #DIFF_FILE,diff_file$ OPEN_NEW #OUT_FILE,out_file$ in_file_count = 1 REPEAT big_loop IF last_insert = FALSE THEN INPUT #DIFF_FILE,in$ IF EOF(#DIFF_FILE) THEN EXIT big_loop ELSE last_insert = FALSE ENDIF IF LEN(in$) < 13 THEN END REPEAT big_loop IF in$(1 TO 13) = "Deleted line " THEN del_line IF in$(1 to 13) = "Deleted lines" THEN del_lines IF in$(1 to 13) = "Changed line " THEN chang_line IF in$(1 to 13) = "Changed lines" THEN chang_lines IF LEN(in$) < 19 THEN END REPEAT big_loop IF in$(1 to 19) = "Inserted after line" THEN insert END REPEAT big_loop ** add rest of original file to new file REPEAT loop INPUT #IN_FILE,in_file$ IF EOF(#IN_FILE) THEN EXIT loop PRINT #OUT_FILE,in_file$ END REPEAT loop @END PRINT #OUT_FILE CLOSE #IN_FILE CLOSE #DIFF_FILE CLOSE #OUT_FILE ** Start of Functions DEFine FuNction EOS(x$) IF LEN(x$) < 3 THEN RETURN FALSE IF x$(1 to 3) = "To:" THEN RETURN TRUE IF LEN(x$) < 12 THEN RETURN FALSE IF x$(1 to 12) = "Deleted line" THEN RETURN TRUE IF x$(1 to 12) = "Changed line" THEN RETURN TRUE IF LEN(x$) < 19 THEN RETURN FALSE IF x$(1 to 19) = "Inserted after line" THEN RETURN TRUE RETURN FALSE END DEFine EOS DEFine PROCedure del_line LOCal z,x x = in$(14 to) FOR z = in_file_count TO x-1 INPUT #IN_FILE,in_file$ PRINT #OUT_FILE,in_file$ NEXT z INPUT #IN_FILE,in_file$ in_file_count = x+1 END DEFine del_line DEFine PROCedure del_lines LOCal x,y,z,temp temp = "-" INSTR in$ x = in$( 15 TO temp-1) y = in$( temp+1 TO LEN(in$)-1) FOR z = in_file_count TO x-1 INPUT #IN_FILE,in_file$ PRINT #OUT_FILE,in_file$ NEXT z FOR z = x TO y INPUT #IN_FILE,in_file$ NEXT z in_file_count = y+1 END DEFine del_lines DEFine PROCedure insert LOCal x,y,loop x = in$(21 TO) FOR y = in_file_count to x INPUT #IN_FILE,infile$ PRINT #OUT_FILE,infile$ NEXT y in_file_count = x+1 REPEAT loop INPUT #DIFF_FILE,in$ IF EOF(#DIFF_FILE) THEN GOTO @END IF EOS(in$) THEN EXIT loop IF LEN(in$) < 2 THEN PRINT #OUT_FILE,in$ ELSE PRINT #OUT_FILE,in$(2 TO) ENDIF END REPEAT loop last_insert = TRUE END DEFine insert DEFine PROCedure chang_line LOCal x,y x = in$(14 TO) FOR y = in_file_count TO x-1 INPUT #IN_FILE,infile$ PRINT #OUT_FILE,infile$ NEXT y in_file_count = x+1 INPUT #IN_FILE,infile$ INPUT #DIFF_FILE,in$ INPUT #DIFF_FILE,in$ INPUT #DIFF_FILE,in$ PRINT #OUT_FILE,in$(2 TO) END DEFine chang_line DEFine PROCedure chang_lines LOCal x,y,z,temp temp = "-" INSTR in$ x = in$( 15 TO temp-1) y = in$( temp+1 TO LEN(in$)-1) FOR z = in_file_count TO x-1 INPUT #IN_FILE,infile$ PRINT #OUT_FILE,infile$ NEXT z FOR z = x to y INPUT #IN_FILE,in$ INPUT #DIFF_FILE,in$ NEXT z INPUT #DIFF_FILE,in$ REPEAT loop INPUT #DIFF_FILE,in$ IF EOF(#DIFF_FILE) THEN GOTO @END IF EOS(IN$) THEN EXIT loop IF LEN(in$) < 2 THEN PRINT #OUT_FILE,in$ ELSE PRINT #OUT_FILE,in$(2 TO) END IF END REPEAT loop last_insert = TRUE in_file_count = y+1 END DEFine chang_lines Example files: Version 1 File: This is line 1 This is line 2 This is line 3 This is line 4 This is line 5 This is line 6 This is line 7 This is line 8 This is line 9 This is line 10 This is line 11 This is line 12 This is line 13 This is line 14 This is line 15 This is line 16 This is line 17 This is line 18 Version 2 File: This is line 1 This is line 3 This is line 4 add a line here This is line 5 This is line 6 add a line here add a line here This is line 7 This is a line 8 This is line 9 This is line 13 This is line 14 This is a line 15 This is a line 16 This is line 17 This is line 18 add a line here: 1 add a line here: 2 add a line here: 3 Diff File: Deleted line 2 This is line 2 Inserted after line 4: add a line here Inserted after line 6: add a line here add a line here Changed line 8 This is line 8 To: This is a line 8 Deleted lines 10-12: This is line 10 This is line 11 This is line 12 Changed lines 15-16: This is line 15 This is line 16 To: This is a line 15 This is a line 16 Inserted after line 18: add a line here: 1 add a line here: 2 add a line here: 3 Diff File for FCOMP2_C: (some lines wrapped for printing,so be carefull when re-typing.) Inserted after line 0: Inserted after line 13: UPDATED: 11 Nov 91 by Timothy Swenson Added changes so that output will be sent to a file. Output file will be third file arguement. Usage: CRUN "flp1_fcomp2 flp1_file_ver_1 flp1_file_ver_2 flp1_diff" Deleted line 25 Deleted line 28 Changed line 54 #include To: #include Inserted after line 69: FILE *fd; Changed lines 93-94: if(argc != 3) fatal("fcomp requires two file names."); To: if(argc != 4) fatal("fcomp requires three file names."); Inserted after line 95: if (( fd = fopen(argv[3],"w")) == NULL ) { fprintf(stderr,"Cannot open file %s.\n",argv[3]); } Changed line 107 puts("The files are identical."); To: fputs(fd,"The files are identical."); fclose(fd); Inserted after line 144: fclose(fd); Inserted after line 169: fclose(fd); Changed line 207 printf("Inserted after line %d:\n",ep->line1); To: fprintf(fd,"Inserted after line %d:\n",ep->line1); Changed line 218 printf("\nChanged "); To: fprintf(fd,"Changed "); Changed line 220 printf("\nDeleted "); To: fprintf(fd,"Deleted "); Changed line 222 printf("line %d\n",ep->line1); To: fprintf(fd,"line %d\n",ep->line1); Changed line 224 printf("lines %d-%d:\n",ep->line1,a->line1); To: fprintf(fd,"lines %d-%d:\n",ep->line1,a->line1); Changed line 228 printf(" %s",A[ep->line1-1]); To: fprintf(fd," %s",A[ep->line1-1]); Changed line 234 printf("To:\n"); To: fprintf(fd,"To:\n"); Changed line 238 printf(" %s",B[ep->line2-1]); To: fprintf(fd," %s",B[ep->line2-1]); Inserted after line 241: fprintf(fd,"\n"); Inserted after line 247: fclose(fd); Inserted after line 254: fclose(fd); .dw .ul The German Connection .do .uo By Tim Swenson Through various means I have gotten in contact with Franz Herrmann of the German QL User Group "Sinclair QL User Club e. V." After some communications, he sent me a disk with some interesting files. Firstly, there is the IFE (Inter-group Freeware Exchange) database that constains listings to over 53 MB of QL programs. The German group is organizing a collective freeware library with a number of European QL groups (plus 1 American group). This is sort of like the Quanta library, but you do not have to be a member of any group to benefit from the library. I got two files that listed and described the programs and one file that listed the participating groups. There was an executable program that looked like it was supposed to provide easy access to the descriptions, but I could not get it to run (bad paramater error). But since the files are all text, I could still access them. There was also included a couple of the IFE circulars (newsy newsletters) that sort of gave the story of the IFE project. Even thought this is from a German group, all items are in English. The only problem I can see with the IFE is the language barrier. Some of the programs are not in English. You will need to have a knowledge of Italian to run some of the Italian programs, but this is to be expected. Secondly, Franz sent me a text editor called QED but succombed to the same problem as the IFE executable, a bad parameter error. The last item is called QL-ZOO. It is a version of the Unix utility ZOO. ZOO is a file compression utility like PKZIP or compress. As I understand, it will compress and uncompress files that are compatable with the Unix version of ZOO. Unlike the other executables, I was able to get QL-ZOO to run. For those that wish to contact Franz Herrmann his address is: Franz Herrmann Talstraœe 21 W-5460 Ockenfels Germany Franz_Herrmann@bn.maus.de .dw .ul New QL .do .uo Reader Response from Giuseppe Zanetti [ Giuseppe has sent this response via e-mail from Italy. His English is far better than my Italian, but I did make a few touch-ups where needed. - ED] Your idea on the last page of Novembers issue of the QHJ was great ("Why not make a QL-Clone..."). My idea is: CPU is a 68XXX mounted on a single PC-IBM board, so that you can use the PCs hardware (better than QL equivalent) for I/O. I think it may be a static RAM mapped on lowest QL memory, then load QDOS, disabling RAM write pin, emulate an EPROM, and reset 68K. Keyboard, video and other I/O may be shared with PC via a lot of dualport RAM and I/O ports (and IRQ). Other side features would be a 68K QL bus to connect to QL dependent devices. Memory up to 16 Meg. PC IBM Features (PC is used as a slave): QL screen emulator (via VGA), keyboard, mouse, joystick, disks and hard disks would format to QL format (and used as FLP and FSK), networking, scanner and other features. It is also possible to use this board as a Mac emulator. Editor's Response - This idea has possibilities. It is easier than designing a new QL from scratch. Cost would be just for the board and software, if the user already had a PC compatible system. I'd be a little worried that too many kludges may have to be done to get it to work. Designing a new QL from scratch will take longer and cost more, but it would be a cleaner solution. This is just my opinion. Plus sometimes the ideal solution is not feasable.