It was a near thing. Our street credibility could have been shattered. Apricot File hacks would have nudged one another in the pub, tittering and jeering at how Langford had sold out to the big publicity interests, and grovelled for a British Microhype Award -- As Sponsored By The Sunday Times.
The crackly voice over the phone said, "We're the British Microcomputing Awards." (I managed to bite back the instinctive response of "No thanks, we've already got one.") "We understand you, er, do a disk."
"Yes indeed! Our disk gives its buyers unlimited super powers, boundless sexual potency, and an uncanny ability to understand several sentences in the SuperWriter manual. Make the cheque out to --"
"No, it's the awards, you're in line for best utility software. Can we have all your literature?"
Blimey, I thought. "It's already in the post," I said. "What's the address?"
The second call came to my esteemed colleague. "We want free copies of all your software," they said ingratiatingly. "Will it run on the Xen?"
"Er, we're fairly certain it will but haven't been able to try."
"Oh! Why don't you ask Apricot to give you a Xen? That's what we did."
It is nice to know that innocence still survives in this cruel world.
"Where do we send the software?" my partner asked.
Not feeling like walking all the way downstairs to check the street number, the awards lady said: "I don't know yet, we'll ring tomorrow...."
The third and final phone call came many days later.
"Why haven't you sent us your software?"
"We stayed in all day for you to ring with the address, but you didn't."
"Oh, well, it's too late now."
And that, dear readers, is at least one of the reasons why our little disk of SuperWriter utilities failed to be up there with the finalists: GEM, Sidekick and Windows.
Graphics without GSX
Some issues ago, I foolishly promised to spill selected beans on this subject. One important development has since emerged: Sydney Harrod's Apri-Soft Exchange has published (on a members-only disk, but membership is only £10 and the disk then comes free) Eli Napchan's GSXLIB.PAS. This is a still developing library of procedures to allow GSX graphics to be used from Turbo Pascal. Once your Turbo program has interfaced with GSX, you can in theory use all its powerful functions without having to re-invent the wheel....
Meanwhile, though unsure whether its square cross-section is optimal, I've been fiddling with a prototype wheel -- a Turbo program to handle the PC/Xi graphics screen without any taint of GSX. It should be possible, with the usual months of agonizing effort, to imitate the technique in MSBASIC or whatever.
Step one is to set "graphics mode" on the PC/Xi screen, by sending Escape followed by } (hex 7D) to get a screen of 50 columns by 25 rows.
Step two is to "point" the screen in the direction of the graphics memory. The PC/Xi screen memory starts at F000H and contains not actual characters but "pointers" to locations in the font area at 0800H. We can write in a different set of pointers, to make each screen cell point at a different cell of the graphics memory (2800H to C7FFH). The graphics area then effectively becomes an array of 1250 individually user-definable characters.
In step three, we get in amongst this area of memory and start mucking about with it.
Let's have a guided tour of my small Turbo program VIEW.PAS, which merely looks at graphics memory. (To provide something interesting to look at, it also loads up a 40,000 byte pretty picture from file. I've sent Apricot File some samples.
VIEWing with alarm
Program VIEW {Copyright (c) Ansible Information, 1985-6}; Type ScreenCell = Array[0..31] of Byte; ScreenArray = Array[0..24,0..49] of ScreenCell; ScreenBuffer = Array[0..1919] of Integer; AnyString = String[80]; {The "Screen" types are chosen to have the same "shape" as, respectively, a single cell of the graphics screen, the whole of graphics memory, and one whole visible PC/Xi screen.} Const GraphicsOn= ^['}'; {Sets graphics mode} ResetScreen= ^['z'^['('; {Reset at high intensity} {This Escape-z reset conveniently restores all the normal screen pointers, so we don't need to bother with the details of that.} Var Screen: ScreenArray absolute 0:$2800; {Graphics screen} ScreenPoint: ScreenBuffer absolute $F000:0; {Normal screen memory} GraphFile: File of ScreenArray; FileName: AnyString; Key: Char; Index: Integer; {Absolute variables are used to define the array contents as equivalent to and identical with the graphics and screen memory. Changing an element in Screen instantly writes the appropriate value to graphics memory. If you're a newcomer to Turbo, you'll probably gnash your teeth at the ease with which a file can be set up.} Procedure Graphics; {Set graphics mode and fill screen with pointers.} begin Write(GraphicsOn); For Index:= 0 to 1249 do ScreenPoint[Index]:= Index+320 end; {The 320 is just one of those, er, arbitrary constants which result from the way the Apricot was designed. The result: screen cells point to different and successive chunks of graphics memory.} Procedure Crash(Message:AnyString); {Abort on error.} begin Writeln(Message); Halt end; Begin {MAIN PROGRAM -- at last!} If ParamCount=0 then {Look for a file name on the command line.} Crash('Syntax is VIEW FILENAME, where FILENAME.SCR is a saved screen.') else begin FileName:= ParamStr(1); If Pos('.',FileName)=0 then FileName:= FileName+'.scr'; {This is just finicky: it gives a default extension of .SCR to the file, which is assumed to contain a saved graphics screen.} Assign(GraphFile,FileName); {$I-} Reset(GraphFile); If IOresult>0 then Crash('Cannot find/open the file "'+FileName+'".') else begin Writeln('Now loading file "',FileName,'"....'); Read(GraphFile,Screen); Close(GraphFile); {$I+} If IOresult>0 then Crash('File "'+FileName+'" is not in a suitable format.') else Graphics end {This also shows how to open and read a Turbo file. The $I- turns off input error checking so that we stay in control of error handling: if the file doesn't exist or doesn't contain enough data for a graphics screen, the function IOresult returns a non-zero value. The two key lines here are "Read(GraphFile,Screen)" -- since GraphFile is a "File of ScreenArray" we can read straight from it into the graphics memory array -- and "Graphics", which calls the already defined procedure.} end; Read(Kbd,Key); {"Strike a key to continue"} Writeln(ResetScreen,'Leaving VIEW.COM.'); end.What now, little man?
This is a simple, passive program. The next obvious step is to ditch everything between the MAIN PROGRAM and "Writeln(ResetScreen..." lines, to call "Graphics" as the first line of the main program, and to start playing around -- writing values into the "Screen" array. There are 40,000 graphic bytes to tinker with. Examples:
Screen[0,0,0]:= 255; {set the whole of the top-left byte "solid"} Screen[24,49,31]:= 255; {ditto for bottom right} Screen[0,0,0]:= Screen(0,0,0) or 1; {turn on one bit of that byte}You can switch any of the 320,000 individual bits or graphic dots by variations on that last line: substitute 2, 4, 8, 16, 32, 64 or 128 for that 1, and repeat for any cell. Yes, this cries out for a procedure to automate the process so you can just write "Plot(X,Y);" where X runs to 0 to 799 and Y from 0 to 399. This is left as an exercise for the student. Translation: [a] I've no room; [b] I'm too lazy to describe it today.
Lastly, a Turbo trick which you may not know. To fill our graphics screen with solid green you need to put 255 into every byte of "Screen". (To wipe it to black, fill it with 0. Values in between give pretty but repetitive pinstripe patterns.) The obvious Turbo command is:
FillChar(Screen,40000,255);-- which gives a compilation error. You aren't allowed to specify an integer constant greater than 32767. But for reasons known only to God and Borland International, the following works happily:
FillChar(Screen,4*10000,255);An integer by any other name....