Windos Ian Briscoe's program will extend the graphics capability of your Spectrum. [ This program was published in Your Computer in two parts, in December 1984 and January 1985. I've joined the two together in this text file. The tables for each part are at the end of that part - part one is a small bit of text with a lot of tables, part two has just one small table but much more main text. At the same site where you got this text, you should also be able to find a "Windos.tzx" file containing all the programs described below. They're not quite in the order described, but in one that is more convenient for loading from tape. For part one, the demo program comes first, followed by the Windos machine code itself, and only then the machine code loader. For part two, first up is the Invert-Screen loader program - I haven't provided pre-loaded machine code for this, because it needs the rest of Windos anyway. Next is the whole demo again, this time with picture 2 using the Extended Basic, followed by the main code again (to save the bother of rewinding the TZX) and the Extended Basic machine code. Note that the Ext-Basic does require that the Interface 1 areas have been initialised before you execute it; if it crashes on you, try any IF1 command (CAT 1 works, even if you have no cartridge loaded) before loading the demo. Finishing off part two are the Extended Basic loader, and the un-MERGEd new version of Picture 2 (which is useless on its own, but as it was a separate listing which had to be typed in, I thought I might as well provide it). The original Extended Basic loader saved its machine code to Microdrive, and its demo loaded the code from Microdrive as well (unlike the main demo, which asks which you prefer), presumably because if you need an Interface 1 anyway, Microdrives are faster than tapes. However, since I was preparing a TZX already and nothing in the code requires a Microdrive (only the IF1 itself is necessary), I've changed this to use tape. Richard Bos, October 2012. ] Ever since the Spectrum was launched, other computers have been brought on to the market with better graphics capa- bilities. Some feeling that the Spectrum could do with some added graphical muscle, armed with my 48K Spectrum with Micro- drive and Hisoft's excellent Devpac, a highly recommended package, I set out to give the Spectrum all these facili- ties and more. The result is Windos, a 5K program packed with practically every feature you could wish for. A whole book could be written describing all the possibilities, but the main functions are: #32- or 64-column windowing. #Four-way window scrolling. #An extended plot function. #Instant colour changes. #Large letters in any of four orientations. #An intelligent paint/unpaint. #An alternative screen at any address. To get Windos up and running, type in listing 1 and save it a few times, then run it. Once run successfully, the code will be automatically saved. Owners of Microdrives may wish to change the appropriate lines to enable saving to cartridge. Most experienced Spectrum users will be familiar with the concept of streams and channels. What I have done is to alter the channel 3 output routine vector, found in the channel information area, and make it point to my own output routine. This means that the ZX Printer is now disabled. LPRINTs, OPEN #2,"p" can be used to enable Print. However, how can this be of any use for accessing the routines? BBC users are used to the VDU statement on their micro, but this can be replaced by: PRINT CHR$ n;CHR$ n; etc to achieve the same effect. Well, this is exactly how the routines are accessed, via control codes. Of course, most of the codes are different from the BBC's and have a diffe- rent number of parameters - see table 1 for a complete run-down. Now back to the original question. To get Windos into operation, first initialise channel 3 by using RANDOMIZE USR 60000 However, this won't clear the system's own variables area which holds the attributes for all 8 windows, each "window map", as I call them, being 45 bytes long, so use RANDOMIZE USR 60003 to initialise it. More information about the window map can be found in table 2. Now you can begin to use the system. Type in the demon- stration program to see some of the many effects possible. Once fully understood, I hope that you will begin to realise the possibilities the system offers. Next month, I will explain how the system works inside. The last part of the series will give Interface 1 owners an Extended Basic to use the system neatly. ___________________________________________________________ Table 1. The Control codes CHR$ 0;CHR$ n; Selects a window. n must be in the range 0 to 7. CHR$ 1;CHR$ tlx;CHR$ tly;CHR$ brx;CHR$ bry; Defines a window's size. Top left x, top left y, bottom right x, bottom right y. CHR$ 2;CHR$ n; Scrolls. In the window map there are two scrolling registers. This scrolls the current window according to these registers. n is the number of times the operation is to be done. CHR$ 3;CHR$ n; Scroll window left n times. Preserves the scroll registers. CHR$ 4;CHR$ n; As above but for right. CHR$ 5;CHR$ n; As above but for up. CHR$ 6;CHR$ n; As above but for down. WARNING. CHR$ 6 is the comma in PRINT statements. Any attempt to use PRINT comma will result in a down scroll. CHR$ 7;CHR$ byte;CHR$ value; Window map poke. There are 45 bytes in a window map (see table 2), numbered 0-44. This enables those bytes to be changed easily. CHR$ 8; Back cursor. CHR$ 9; Forward cursor. CHR$ 10; Down cursor. CHR$ 11; Up cursor. CHR$ 12; Delete. CHR$ 13; Newline. CHR$ 14;CHR$ routine+mode;CHR$ x;CHR$ y; This is the plot command and is a lot like the Beeb's. See table 3. CHR$ 15; COPY the whole screen to the ZX printer. CHR$ 16;CHR$ ink; Define text ink. PRINT INK n; will work but is not temporary. CHR$ 17;CHR$ paper; As above but for paper. CHR$ 18;CHR$ flash; As above but for flash. CHR$ 19;CHR$ bright; As above but for bright. CHR$ 20;CHR$ over; As above but for OVER. CHR$ 21;CHR$ inv; As above but for INVERSE. CHR$ 22;CHR$ x;CHR$ y; Same as PRINT AT y,x but note the x and y are the opposite way around to Sinclair AT control code. CHR$ 23;CHR$ tab; Changes the current x co-ordinate to "tab". CHR$ 24;CHR$ oldink;CHR$ newink; Window instant ink change. Changes any oldink to newink. CHR$ 25;CHR$ oldpaper;CHR$ newpaper; As above but for paper. CHR$ 26; CHR$ bright; Window actual bright set/reset. CHR$ 27; CHR$ flash; Window actual flash set/reset. CHR$ 28;CHR$ width;CHR$ height; Like the CSIZE command on the QL. Defines the width and height of the large characters. See table 2 for more information. CHR$ 29; In the window map there is an address which tells all the Windos routines where the start of the screen is. If this were changed, then any output would not be seen. A Dragon-like invisible screen will be present. So to get the data from the screen memory to the actual display memory, use this command. CHR$ 30; Home cursor. CHR$ 31; Clear the current window. NOTES There is no graphics windowing. CLS uses the print routine. There is normally a visible cursor. ___________________________________________________________ Table 2. The window map. Byte Meaning. 0-3 The window size as set up and in the same order as CHR$ 1; etc. 4 The text colour. 5 The graphics colour. 6-7 The text co-ordinates, with 0,0 being at the top left of the window, NOT the screen. 8-9 The last x,y specified by CHR$ 14;. 10-11 Used internally. 12 Text flags. See table 4. 13 Graphics flags. See table 5 [which was missing]. 14 The attribute used by the cursor, so the cursor can be flashing or not. 15-29 The parameter queue. See next month's article for more details. 30 Current control code. 31-32 Next free space in queue, as an address. 33 Number of parameters to come before execution. 34 Width of large characters. 35 Height of large characters. 36-37 Second to last point specified by CHR$ 14;. 38 Draw pattern. Like the Oric PATTERN command. This is the bit pattern that draw uses to do dotted lines. Normally 255 for solid lines. 39 Text attribute mask. As there is no eg. PAPER 8 allowed, this is used to achieve the same effect. Every bit of this byte that is set means that the equivalent bit of the old attribute to be obliterated by printing will not be destroyed. So to achieve INK 8 set this byte to 7, PAPER 8 is BIN 111000 etc. 40 As above but for graphics. 41 The pixel scroll register. 42 As above but for the attributes. 43-44 This is the address of screen memory. Normally 16384, but can be changed. Normal Intel format applies, i.e. LSB first followed by the MSB. ___________________________________________________________ Table 3. The plotting routines and modes. When using the plot command, CHR$ 14, the first para- meter is the routine added to the plotting mode. They are as follows: Plotting modes. 0 = Normal OR plotting. 1 = XOR or OVER plotting. 2 = UNPLOT. 3 = MOVE. Plotting routines. 0 = PLOT. An ordinary dot plot. 4 = DRAW. Draw a line to x,y. Note that the origin is in the top left corner. 8 = PAINT. Fill an enclosed area. This is an intelligent fill so make sure there is at least 0.5K between Ramtop and your program as the machine stack is used as workspace. 12 = BOX. 16 = TRIANGLE. Draws a triangle using the last and the second to last points plotted as two of the three points, the third being the point specified. 20 = FILLED BOX. Like BOX only filled. Like the other shape routines, the draw routine is used so if a draw pattern - see table 2, byte 38 - is in use then there will be a textured box. 24 = SET. Just sets the plotting mode and the graphics co-ordinates. ___________________________________________________________ Table 4. The text flags. Bit. Meaning. 0-1 00 = Normal over printing. 01 = XOR or OVER 1 printing. 10 = Inverse printing. 11 = OR printing. 2 Scrolling suppression. 0 = Normal scrolling when the cursor tries to go too far up, or too far down. 1 = A Lynx-type wraparound window. 3 Attribute suppression. 0 = Old attributes destroyed. 1 = Old attribute left unchanged. 4 'Form feed' suppression. 0 = Normal newline. 1 = Cursor stays on the same line. 5 Output selection. 0 = Normal 32 or 64 column windowing. 1 = Large letters. To print large letters, this bit must be set. 6 32/64 column selection. 0 = 32 column windows and printing. 1 = 64 column windows and printing. 7 Cursor switch. 0 = Cursor on. 1 = Cursor off. It is useful when working at bit level to use the BIN function. ___________________________________________________________ [Table 5. The graphics flags. This table seems to have gone missing from the article. From the demonstration program and a bit of rummaging around, the following could be made up: The large letter mode uses the graphics flags, not the text flags. As can be seen from lines 6045 onwards, the position for large letter printing must be set using the Plot function, not using AT, and they take the graphics colour. The bits in the graphics flag have (as far as I can make out, so cum grano salis) the following meaning: Bits 0 and 1 are used as a copy of the plotting mode in the last Plot command. This is not useful for Plot itself (since you re-specify them every time you use it), but it does mean that the large letters take on the mode (Over and Inverse) of the last plot. This is the only way to get Over and Inverse for large letters. It is convenient to specify them in the positioning Set Plot which will probably precede their printing. Bits 2 and 3 specify the direction of print, as shown in lines 6045-6048 of the demo. Bit 2 controls normal direction or reversed and bit 3 controls vertical or not, leading to the following combinations: 0 is normal, left-to-right printing (of course); 4 is upside down, right-to-left (not just mirrored); 8 is vertical, turned 90 degrees left (so legible bottom to top); and 12 is vertical, 90 degrees right, printed top to bottom. Bit 5 functions as a temporary attribute mask, as if byte 40 in the window map had been poked with 255 - that is, similar to bit 3 in the text flags. Bits 6 and 7 are used internally, but I have not been able to work out what for, and setting them by hand does not seem to do anything. It looks like bit is 4 never used at all.] ___________________________________________________________ [Part 2] Ian Briscoe continues his explanation of Windos. If you typed in last month's listings, and managed to get them to work perfectly, then you should by now have real- ised that used properly, Windos is a pretty powerful aid to graphics creation. There are a few quirks that I neglected to point out last month - if you try to LIST with the new routines, you will not have a chance to stop the listing, and also no tokens will be printed as tokens, but instead as odd characters. This is not as hopeless as it seems, because you can now set up a character set in RAM, and use codes 128-255 as the user-definable characters. The normal system variable CHARS is the system's charac- ter set pointer, except in 64 column mode, as this has its own font near the start of Windos. In addition, you may have discovered that the Plot command works on a full 256 by 192 grid. Now to the main business. This article is aimed primarily at hackers - i.e. fanatical machine-code buffs who delight in nosing their way through other people's programs and systems, and altering them to their own tastes [Right! And that is _not_ the same thing as a script kiddie, damn it] - and ordinary machine-code addicts. You will see in table 1 that there are a few vector tables, which hold addresses of plotting routines, control codes etc. To start with, we'll look at the control code vectors. Remember the window map and the parameter queue? Well, this is where they come in. Throughout the following, the byte numbers refer to bytes in the window map, numbered 0-44. When a character gets sent to be printed, it first goes into the A register, then through the current channel until it reaches the output routine. When it reaches the one in Windos, one of two things can happen. If A is 32 or above, then an ASCII character is output. However, if A is 31 or below, a fair amount of work get done before anything happens. First the number of para- meters it has are checked, by referring to the 32 byte argument table, one byte for each control code. If this is zero, then the execution address is found by doubling A, adding this to the base address of the control code table, then, in effect, an indirect CALL to the appro- priate routine occurs. However, if the number of parameters is more than 0, then first the queue is initialised. This is where all the parameters are going to be stored before execution. Byte 30 becomes A, and byte 33 becomes the number of parameters left to arrive before execution of the control code. Then, on subsequent outputs, the parameters are queued, from byte 15 onwards, and byte 33 decremented by 1. When this reaches 0, then the code is executed. The code routine uses (IX + 15) etc. to fetch the parameters, since IX holds the base address of the current window map. Then the whole show starts again when the next character comes through. Confused? An example of how to alter one of these codes will show the potential of altering these tables. The control code to be changed is CHR$ 29. Normally, this transfers the screen memory to the display memory, but usually these are one and the same. So, to change it to a code which inverts the whole screen, we first need a screen invert routine. See listing 1. Now we need to change the appropriate vector in the control code vector table. Its address is the Base Address, which is 63947, plus two times the code number, so in this case this is 63947 + (2*29) = 64005. Now using the normal Intel format, 64005 becomes the low byte and 64006 the high byte of the routine address, in this case 65300. The listing shows how it's all done. If we wanted to add a routine which needed parameters then we would have had to have changed the appropriate byte in the argument number table. Providing you know Z-80 assembly language, altering Windos is not at all difficult. At the start of Windos there are four JP addresses. The first two you know, at 60000 and 60003, but 60006 is the entry point to Windos' output routine. Just LD A,n and CALL 60006 within your routines. Every single register is saved, except the I and R registers. Obviously, this slows things down a bit, but the peace of mind of knowing that no registers will be corrupted is worth it. This vector can be altered so that before outputting a character, something else can be done, like a beep, before jumping back to the output routine. This might be useful to someone. The fourth jump is to the copy screen routine. This normally points to a COPY routine for the ZX Printer, which incidentally copies all 192 pixel lines. However, this can be altered so that it jumps to a full- size printer copy routine instead, and this will be useful to those fortunate enough to have real printers. CHR$ 15 uses this, so any change to this would affect CHR$ 15. Determined hackers may like to look around the graphics area of Windos. All the relevant addresses are to be found in table 1. Hopefully, after reading this you will understand and appreciate more fully the thinking behind Windos. I have tried to make it as expandable as possible while still leaving plenty of memory to work with. You may be cursing the inadequacies of Sinclair Basic which make many programs using Windos fairly bulky or tedious to write. Here is a partial solution which will help Inter- face 1 owners no end. In a mere 901 bytes I have added 23 commands to the Spectrum's vocabulary using the very well documented method of extending the Basic, which will not be described here. To keep the size of the Basic down, I used a list to hold the addresses, the command name itself and the length of the name, and I will show you how to add to this list at the end of this article. This method makes adding commands easy. First of all, to get the Basic going type in and save the loader program. Then run it. If there are no errors, saving will begin automatically on Microdrive cartridge. Then, to initialise the Basic, type RANDOMIZE USR 59000 This must be typed every time you load in the Basic, or want to re-initialise, possibly after a NEW. Now, making sure Windos is in memory, type the following command line: *NEW:*INIT: OPEN #2,"p" Windos has just been initialised. If the system crashes, check your listing for errors that the checksuims [sic!] were not able to detect. The full list of commands is as follows: *PUT n,n,n... or *VDU n,n,n...: These are exactly the same and simply output the character codes n directly through the Windos output routine - this is necessary because of the weird things that happen if you don't - Careful of too many parameters, since the machine stack is used to hold them. *PLOT mode + routine,x,y: This is exactly the same as CHR$ 14, except it looks much better! *WPOKE byte,value: This is the window poke. Beware of byte numbers over 44 - you will be poking another window map. *CSIZE width,height: The same as the QL command, and the same as CHR$ 28. *UP, *DOWN, *LEFT, *RIGHT: These four commands set the direction of the large printing. Careful use can lead to some very professional effects. *LARGE: Sets large characters mode. *NORMAL: Resets to either 32 or 64 column mode, depending on what bit 6 of byte 12 is. *32COL: Sets the 32 column mode. *64COL: Sets the 64 column mode. *INIT: Initialises Windos, and interfaces it to the stan- dard Spectrum system. Equivalent to RANDOMIZE USR 60000. *NEW: Resets all eight window maps. Equivalent to RANDOMIZE USR 60003. *DOKE: address,contents: This is a two byte POKE, nothing to do with Windos. *CALL address: Calls the machine-code routine at the specified address. *RENUM start,increment: At last, a renumber command. No GO TOs or GO SUBs done, and the parameters are 8 bit not 16. *RENUM 10,10 is valid, but *RENUM 1000,300 is not. *SCROLL byte 41,byte 42,repeat no: This is a very useful command for setting up first the pixel scroll register and then the attribute register. Then the actual scrolling takes place. The registers remain altered after this command, so *VDU 2,number would result in the same scroll taking place. *WINDOW tlx,tly,brx,bry: This is the same as CHR$ 1, i.e. it defines the window size. *CLS: This is obvious! *GCOL: This sets the graphics attribute. [ *SCREEN: This command was not mentioned in the article, but as can be seen from the demonstration, it selects the active window, just as CHR$ 0.] Note: All of the commands must be preceded by a '*' but thereafter, UPPER or lower case may be used in any order, e.g. *Large is valid. Also, because of the ROM routine NXTCHR, a command spaced out, e.g. * N o r m a l would be accepted. The demonstration is a copy of the subroutine 'picture 2' in the main demo of Windos, and shows how concise program- ming may be achieved. Obviously, the *Plot, *VDU/*Put and *WPoke commands will be used most often and these will cut the size of your programs down a great deal. There now follows an explanation of the method used to enable lots of commands to be added in a relatively small amount of memory. There is a list at the end of the Basic starting at 59707 and finishing at 59900 and it takes the form: LIST DEFB length of following string DEFM "*command" DEFW address of syntax and runtime routine ... DEFB 0 terminates the list Now, the final 0 which terminates the list is at address 59900. There are 99 bytes spare between here and the start of Windos, plenty of room in which to place (a) new command description(s), but make sure that the DEFB always contains the full length of the string following, and that the list is terminated by a 0. To get the base address of the cur- rent window map into IX just use in assembly language, LD IX,(23728) Of course, you need a lot of information and a good assembler before you can start creating the Basic of your dreams. I would suggest that, money permitting, Hisoft's Devpac and Dr Ian Logan's books, Spectrum Microdrive Book and The Complete Spectrum ROM Disassembly are absolute essentials, not forgetting the trusty old Spectrum Manual. If you run out of list space, the address which holds the list pointer is 59019, but take care when changing this, and transferring the old list to its new location, unless of course you don't need the commands for Windos, or Windos, in which case you have 5K of list space available. I hope that you will find a use for Windos and the Exten- ded Basic, but to give you some ideas, why not write a 64 column word processor, or spreadsheet, or try your hand at a text and graphics adventure, using the various graphics routines, and remember that the Sinclair graphics routines still work. Other ideas are a drawing program making use of the alternative screen facility, or for the very ambitious, a multi-tasking language in machine code making use of the windows, which have completely separate identities. ___________________________________________________________ Table 1. Useful addresses 60000 Initialise entry point. 60003 New entry point. 60006 Output routine entry point. 60009 Copy screen routine entry point. 60012-60056 Initial data for the window maps. 60057-60416 The window maps. 60417-61184 The 64 column character set. 61185-65281 WINDOS code and tables. 63915-63946 Argument number table. 63947-64010 The control code routine vectors. 64622-64685 The plotting routine vectors. 64054 The main PLOT subroutine used by other graphics routines. B=x, C=y. 64328 The main DRAW subroutine used by other routines. B=x, C=y. 64684 Equivalent to PLOT k,x,y. A=k, B=-x, C=y. 23728 The base address of the current window map.