
         Q L   H A C K E R ' S   J O U R N A L
      ===========================================
           Supporting  All  QL  Programmers
      ===========================================
         #19                        November 1994
      
    The QL Hacker's Journal (QHJ) is published by Tim
Swenson as a service to the QL Community.  The QHJ is
freely distributable.  Past issues are available on disk,
via e-mail, or via the Anon-FTP server, garbo.uwasa.fi. 
The QHJ is always on the look out for article submissions.

        QL Hacker's Journal
     c/o Tim Swenson
     5615 Botkins Rd 
     Huber Heights, OH 45424 USA
     (513) 233-2178
     swensotc@ss2.sews.wpafb.af.mil
     swensotc@p2.ams.wpafb.af.mil
     tswenson@dgis.dtic.dla.mil
          

.bd
.ul
EDITOR'S FORUMN
.bo
.uo

   Well, it looks like another issues has taken longer that
I would like.  It's been one of those "too busy to program"
type of dry spells.

   The only key item of news is that as I am writing this
the latest version of C68 v 4.2 has partialy come out.  I
received the zipped version of C68 Runtimes disk #1.  Dave
Walker should be sending the rest of the disk out soon. 
Since I have Internet access I am on Dave's mailing list for
updates of C68.  Dave prefers to distribute the package as a
number of large mail messages.  When I get them, I convert
them back into a binary file and get them over to the QL.  I
then send a copy of the disks to Don Walterman of QBOX-USA
and to Bob Dyl of the IQLR.  If I remember, I can upload
them to a number of QL FTP servers on the net.  The key ones
are maya.dei.unipd.it and ftp.nvg.unit.no.

   For those interested I have a new Sinclair Internet
Resources List that has a number of Sinclair World Wide Web
sites listed.  The amazing thing is that there is a ZX81
home page out there.  If you would like a copy of the list,
send me a note.  I tried posting it to comp.sys.sinclair,
but I don't know if I was successfull.

   The last item of note is that I have finally broken down
and bought some new hardware.  I went out and picked up a
new HP Deskjet 520 inkjet printer.  It's output is almost
laser quality, esp. with the specially designed inkjet
paper.  This is not something I normally do.  My last
printer, a DWP-230 daisy wheel, cost a grand total of $1 at
a garage sale.  And it has worked for 2 years with out
failure.  It was only when I thought it died that I
considered buying the Deskjet. (I forgot that these diable
HI-TYPE II printers do run out of ribbon and will not print
after that.  And I thought it was dead.)

    That's all I have for now.  Happy Hacking.
      
.bd
.ul
DISPLAYING TI GRAPHICS FILES
.bo
.uo

[ Jeff Kuhlman has written an interesting program to display
a TI-Artist instance file on the QL.  I don't know how many
have been wanting to do this, but here it is.  On the disk
that Jeff sent me, were many example files.  If you are
interested in this, I can provide a disk with all of the
sample files. - ED ]

/* SHOWTIA_C - a c68 file for the QL to display */
/* TI-ARTIST instances.  This is NOT the proper way */
/* to program in that I write directly to the screen */
/* This is because: a) it's a LOT faster and */
/*                  b) I couldn't figure out how to */
/*                     scale the window properly */
/* December 10, 1993 Ver 1.0 Jeffrey A. Kuhlmann */

#include "qlib.h"   /* because I use QDOS calls */
#include "stdio.h"  /* because I open files */
#include "string.h" /* because I use string functions */
#include "stdlib.h" /* because I use atoi() function */

char infn[81];  /* input file name */
char str1[81];  /* input string from file */
FILE *inf,*outf; /* files to open */
int arry[20];   /* utility array */
char rows[81];  /* row string */
char cols[81];  /* columns string */
int ctr,end,delim,col,row;
int rctr,cctr,cptr;
int chardat[8],chrptr;
long qres;
char *scrn; /* pointer to absolute screen memory */
char *_endmsg=NULL;        /* omit end message */

    main(argc,argv)
int argc;
char *argv[];
     {
if(argc<2)
{
puts("TI-ARTIST VIEWER FOR QDOS");
puts("Press any key when done");
puts("viewing picture...");
puts("TI-Artist file name? (w/o _I)");
gets(infn);  /* get filename from user */
}
else strcpy(infn,argv[1]);
strcat(infn,"_I"); /* add necessary _i to name */
inf=fopen(infn,"r");  /* try to open file */
/* this function forces 4 color, monitor mode */
mt_dmode("4","0");   
/* open whole screen for access */
outf=fopen("con_512x256a0x0_32","w");
qres=fgetchid(outf);
if (!inf)
 {printf("Can't open %s",infn);
 exit(7);}  /* error opening file */
sd_clear(qres,-1);         /* clear screen */
/* for(ctr=0;ctr<16;ctr++)puts(""); */
/* needed only when end message */
/* enabled */
fgets(str1,81,inf);/* read # of rows, cols needed */
rowcol();
for(rctr=0;rctr<row;rctr++)
 for(cctr=0;cctr<col;cctr++)
  {if(feof(inf)){puts("EOF encountered - task
            aborted\n"); exit(7);}
   fgets(str1,81,inf);
   defchr();               /* display dots */
 }

        /*  while(!poll(0));  */   /* wait for a key */
/* sd_pos(qres,-1,1,10); */
            fclose(inf);
            fclose(outf);

exit(0);
}

/* rowcol() takes the first line of the TIA file & */
/*  figures out the total */
/* rows and columns used */
void  rowcol()
{
end=strlen(str1);
         /* find ',' */
          delim=strfnd(",",str1,1);
for(ctr=0;ctr<=delim-1;ctr++)cols[ctr]=str1[ctr];
cols[ctr]=0;
col=atoi(cols);
for(ctr=delim;ctr<end;ctr++)rows[ctr-delim]=str1[ctr+1];
rows[ctr]=0;
row=atoi(rows);
return;
}

/* DEFine CHaRacter */
/* more correctly, plot points */
void defchr()
{
int temp,temptr,more;
end=strlen(str1);
temptr=0;
chrptr=0;
for(ctr=0;ctr<end;ctr++)
{
 temp=str1[ctr];
 if(temp!=',')rows[temptr++]=temp;
   else {rows[temptr]=0; chardat[chrptr]=atoi(rows);
         temptr=0;
         chrptr=chrptr+1; }
rows[temptr]=0;chardat[chrptr]=atoi(rows);
  /* define last one */
}

for(ctr=0;ctr<8;ctr++)   /* 8 pixels high */
{
/* The next line is the bad way of doing things!!!! */
scrn=131072+(rctr*8+ctr)*128+(cctr+10)*2;
*scrn=chardat[ctr];
}
return;
}


.bd
.ul
DISPLAYING QL SCREENS IN MS-DOS
.bo
.uo

[ It's not normally the policy of the QHJ to publish
programs that are written for other platforms than the QL. 
But in this case, I can make an exception.  The following
program by Jeff Kuhlman is designed to display a QL screen
file on an MS-DOS computer.  I've tried it with my MS-DOS
laptop (CGA display) and with a 486 (VGA display) and it
works fine on both computers.

   As more and more QLers are buying PC's, programs like
this are becoming more usefull. - ED]

/* SHOWGR.C - A 'C' PROGRAM TO DISPLAY '_GR' FILES
 FROM THE MDOS PROMPT */
/* JEFFREY A. KUHLMANN 27FEB94 */

#include "graphics.h"
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "io.h"
#include "conio.h"

char infn[81];  /* input file name */
char str1[81];
char buf[128*200+128];
FILE inf;
int arry[20];   /* utility array */
char rows[81];  /* row string */
char cols[81];  /* columns string */
int ctr,end,delim,col,row;
int rctr,cctr,cptr;
int chardat[8],chrptr;
char hexdat[81];
int hex[4];
int blank[4],where,pval,ccc;
int mptr,fd,gm;

main(argc,argv)
int argc;
char **argv;
{
gm=5;                   /* Graphic Mode */
if (argc<2) /* If no file name given on command line, */
{                       /* ask for one */
setvmode(2);
puts("QL SCREEN VIEWER FOR MDOS");
puts("Press any key when done");
puts("viewing picture...");
puts("QL SCREEN file name? (w/o .SCR)");
gets(infn);
}
else strcpy(infn,argv[1]);
strcat(infn,".SCR");    /* add required extension */
setvmode(gm);           /* change video mode */
fd=open(infn,O_BINARY); /* open file for reading */
mptr=0;
if (fd==-1) {setvmode(2); printf("Can't open %s",infn);
    exit(0);}
read(fd,buf,128*200);
   /* ^ read first 200 lines into a buffer */
 for(rctr=0;rctr<200;rctr++)
   /* ^ 200 pixels in 'y' direction */
  for(cctr=0;cctr<128;cctr++)
   /* ^ each QL line is 128 bytes */
  { pval=buf[(rctr)*128+cctr];
    if(rctr&1)               
     /* ^ need to take into account odd scan line */
    poke(0xba00,(rctr-1)*40+cctr/2+8,pval);
    else
    poke(0xb800,rctr*40+cctr/2+8,pval);
 }
while(!kbhit());    /* wait for keypress */
setvmode(2);        /* restore 80 col. mode */
exit(0);
}


.ul
.bd
RECENT FREEWARE RELEASES
.bo
.uo

   A number of freeware programming packages have come my
way.  I have copies if anyone is interested.  Most if these
programs should be available on the Internet.  If not, I
will try to put them out there.

   BISON  -  Port by Dave Woodman
      BISON is another version of YACC (Yet Another
Compiler Compiler).  BISON is used to create a language
parser.
   
   FLEX   -  Port by Dave Woodman
      FLEX is another version of LEX, a lexical analyzer
creater.

   INTERCAL  -  Port by Dave Woodman
      INTERCAL is a programming language designed to have
nothing is common with any other major language.
   
   DIFF   -  Port by Dave Walker
      DIFF is a program that will show the differences
between two files.   
   
   RCS  -  Port by Dave Walker
      RCS stands for Revision Control System.  RCS is a
number of tools that are used to keep track of various
changes to text files.  Mostly it's used on source code. 
It lets you back track a few versions if you need to.  It
can also be used in a programming shop where more than one
person may be editing the same code at the same time.  It
lets one person check out the code and only they are
allowed to update any changes.

   INFOCOM READER  -  By Luke Roberts
   
   The program name is really ZIP.  Most INFOCOM games are
stored in a data file and a game file reader is used to play
the game.  ZIP is a QDOS port of such a reader.  With this
program, you can take almost any INFOCOM game and run it on
the QL.  Luke has tested it with a number of games already.

   For those that don't know what INFOCOM is; INFOCOM is a
game company that put out a number of text-based adventure
games back in the early to mid-80's.  A number of their
popular games are Zork (I - III), Deadline, Planetfall,
HitchHikers Guide to the Galaxy, and Suspended.

   In other news, I've heard from Dave Walker that a new
version of C68 v. 4.20 will be out sometime before
Christmas.  I don't have any details on any of the changes
or upgrades.  Since I am now of Dave's distribution list, I
should receive the new version as soon as Dave releases it. 
For the U.S. readers, once I get it, I will send a copy to
Don Walterman so he can put it up on QBOX-USA.  He said that
he and John should be putting a hard disk on QBOX-USA soon,
so he will have lots of disk space C68 and other stuff.


.bd
.ul   
BIG NUMBERS
.bo
.uo

   Jon Bently writes a column called "Software
Explorations" for the magazine Unix Review.  In the November
1994 issue, Jon discusses how to handle big digit numbers. 
He starts off by discussing how to store the number, in
decimal or binary.  He opts for decimal storage and the use
of a simple array to store the number in.

   Jon writes up a few simple routines to handle big
numbers.  They are:

   pow2   - raises 2 to the power of X.
   bnset1 - sets the global array that holds the number
being created.
   bnmult - multiply the number with a constant.
   bnprint - print the big number.
   
   All of these routines us a global array that stores the
number as it is being worked on.  You could adapt them to
pass the array to each function.

   The article goes on to refine some of the routines listed
above.  Plus, Jon mentions that a real big number library
would have a lot more routines than the ones listed above. 
Bnadd, bnsub, bndiv, and bncomp would round out a library.

   In porting this program to the QL using Small-C, I opted
to make the variables INT and not LONG as all of the example
programs Jon created used.  This would only affect the size
of the big numbers that this port could use.  Using C68 and
LONG would solve this problem.

   Below is the code from the article, with an example
main() to test it with.  I'm sure this code is very simple
and is only a start for those interested in seriously
handling large numbers.

#include <stdio_h>
   
int x[1000];  /* global array for storage
                 of resultant number */
int topdig;
      
main()
{
   int c;
   
pow2(100);
bnprint();

 while ( ( c = getchar() ) == "" )
          ;  /* pause screen */
   
}
   
pow2 ( n )
   int n;
{
   bnset1( 1 + n/3);
   while ( n -- > 0 )
      bnmult(2);
}
   
bnset1(maxdig)
   int maxdig;
{
   int i;
   
   topdig = maxdig;
   for ( i = 0; i < topdig; i++)
      x[i] = 0;
   
   x[0] = 1;
}
   
bnmult( a )
   int a;

   int i, carry;

   carry = 0;
   for ( i = 0; i < topdig; i++ ) {
      x[i] = a * x[i] + carry;
      carry = 0;
      if ( x[i] >= 10 ) {
         x[i] -= 10;
         carry = 1;
      }
   }
   if (carry)
      x[topdig++] = carry;
}
   
bnprint ()
{
   int i;

   for ( i = topdig-1; i >= 0; i--)
      printf("%d", x[i]);
   printf("\n");

}
   

.bd
.ul
DYNAMIC WINDOWS - ANOTHER APPROACH
.bo
.uo

   In a back issue of the QHJ I wrote a SuperBasic program
that saves a section of the screen to memory and brings it
back again.  This allows you to save a section of the screen
where you are about the put a new window, draw to window,
and then bring back what was under the window when you are
done with the window.

   Jonathan Vanderwall brings another approach to this
problem.  He is interested in using drop-down menus (which
are just specialty windows).  He first saves the entire
screen to ramdisk, draws the pull-down menus, and when done,
uses LBYTES to reload the screen.  He says that he gets
pretty good responses for this routines.

   This is sort of a brute force approach to the problem,
but it does reach a solution.  I thought this approach was
unelegant and a bit of a kludge, but if I found problems
writing the routines I wanted, then I would use this as a
backup.  When developing sometimes you take the path of
least resistance, even if it's not the cleanest path.


.bd
.ul
SOUNDEX
.bo
.uo

   Soundex is an algorithm for storing words, primarily
names, in a number format so that they could be easily look
up, and words that sound alike will have almost the same
soundex result.  This is sort of the thing they use when you
call information and ask for a persons number.  They will
type it up, and it will return all persons that sound
similar to the name you gave (just in case you don't know
the exact spelling).

   This program comes out of the Sept 1994 issue of the
C/C++ Users Journal.

   I've run this program through C68, but it does not like
the continue and break statements in the for loop.  I've
looked in the first edition of K&R and it supports this
contruct, so I'm puzzled as to why C68 does not support the
construct.  I am presenting the program here for the
following reasons:

   1] It's an interesting simple algorithm to tinker with.
   2] It's a simple enough program that it would be reworked
into almost any language.
   3] Because it should compile in C68 as defined by K&R.
   
   I'm hoping that either the answer to why it won't compile
is easy or that it will bring a discussion on possible
weaknesses in C68.

/* soundex.c
     Jeff Rossen */

#include <stdio_h>
#include <ctype_h>

#define MODIFIED_VERSION /* remove for original */

#define MAX_LENGTH 20 /* max 3 of char to check */
#define SOUNDEX_LENGTH 4 /* length of Soundex code */

#define ALPHA_OFFSET 65

int main(int argc, char *argv[])
{
   char code[SOUNDEX_LENGTH+1];

   soundex(argv[1], code);
   printf("The code is %s \n",code);

   return(0);
}

int soundex(char *in_name, char *code)
{
   /* ctable contains the Soundex value for each
      letter in alphabetical order.  0 represents
      letters which are to be ignored. */

   static char ctable[] =
     {"01230120022455012623010202"};
/*     abcdefghijklmnopqrstuvwxyz     */

   char name[MAX_LENGTH+1];
   char prior = ' ', c;
   short i, y=0;

     /* convert all to uppercase */
   for(i=0; in_name[i] && i < MAX_LENGTH; i++)
      name[i] = toupper(in_name[i]);
   name[i]='\0';

     /* generate 1st character of Soundex code */
   code[0] = name[0];
   y=1;
   code[y]='\0';

#ifdef MODIFIED_VERSION
   if (name[0] == 'K')   /* modification */
      code[0]= 'C';
   else if (name[0] == 'P' && name[1] == 'H')
      code[0]='F';
#endif

   /* loop through rest of name until code complete *.
   for(i=1; name[i]; i++) {

      if (! isalpha(name[i])) /* skip non-alpha */
         continue;

          /* skip successive occurance */
      if (name[i] == prior)
         continue;
      prior = name[i];

            /* lookup letter in table */
      c = name[i] - ALPHA-OFFSET;

      if (ctable[c] == 0) /* ignore this letter */
         continue;

      code[y++] = ctable[c];  /* add to code */
      code[y] = '\0';

      if (strlen(code) >= SOUNDEX_LENGTH)
         break;   /* code is complete */

   }
   while (strnlen(code) < SOUNDEX_LENGTH) {
      code[y++] = '0'; /* pad code with zeros */
      code[y] = '\0';
   }

   return(0);
}


