Inside the original QL, there were supposed to be two screen areas. As it turned out, the final product only had one, but some memory was still left around for the second. Unfortunately, the second screen's memory has been partially overwritten by the system variables and so cannot be safely used. To all intents and purposes, we can ignore that second screen and concentrate on the primary screen itself. This is the one we can all use.
Nowadays, we have all sorts of screen modes and resolutions and with the coming of the Q40 & Q60, we have numerous colours as well. As an old lag, I deal in mode 4 and mode 8 only but as I use a QXL mostly (I am awaiting delivery of QPC 2 even as I type, and hopefully it will have arrived by the time you read this!) I also have more resolution that the old 512 by 256 that the original QL was limited to.
I also have no documentation regarding the resolutions available on other emulators, cards etc so I cannot deal with those here - perhaps someone with more details/knowlege could write a follow up article for an Aurora, Super Gold Card, Q40 etc. (Please!)
In the old days, 512 by 256 was the best you could expect - and only on 4 colours - red, black, green and white. If you wanted more colours, you only had 256 by 256 to play with, however you did get to use blue, yellow, magenta and cyan as well - it was a trade off, as with most things computer related.
OK, here is how it was in the old days .... the screen starts at address $20000 or 131072 in the QL's memory. Each line on the screen, all 256 of them, use 128 bytes to hold the colour information for the pixels in the line. This implies that a QL screen takes up 32K of memory, and indeed this is the case. To get the screen memory address of pixel x,y (x = dots across and y = dots down) a calculation similar to the following was used :
address = 131072 + (y * 128) + INT(x / 4)
This is because each scan line (or row down the screen) starts 128 bytes on from the previous line hence (y * 128). Each row has 512 pixels in it (even in mode 8!) so the dots across are 512/128 = 4. This is why the dots across (or x) must be divided by 4.
Don't ever assume that the two paragraphs above are true. The various new cards and graphics modes have chagned all of the above. On my QXL, I can see the screen at the above address only when I run it in QL 512 by 256 mode. The other modes use more memory and in different places, so any program that writes to the screen at the original addresses will probably cause carnage within the QXL and lead to unexplained crashes later on - if not straight away. It must always be assumed the the old ways have gone forever and we must always calculate the screen start address and how long a scan line is before trying to access the memory.
For those of you who care about these things, the base of the acreen address is at offset $32 in the channel definition block, while the size, in bytes, of a scan line is at offset $64. (Except is QDOS version is less than 1.03, in which case, the scan line size is always 128 bytes.)
How to get this information? Easy, given the following code which assumes that A0.L holds a channel id for a scr_ or con_ channel :
scr_stuff moveq #sd_extop,d0 ; Trap code moveq #-1,d3 ; Timout lea extop,a2 ; Routine to call via sd_extop trap #3 ; Do it tst.l d0 ; OK? bne.s done ; No, bale out D1 = A1 = garbage got_them move.w d1,-(a7) ; Need to check qdos, save scan_line moveq #mt_inf,d0 ; Trap to get qdos version trap #1 ; Get it (no errors) move.w (a7)+,d1 ; Retrieve scan_line value andi.l #$ff00ffff,d2 ; D2 = qdos, mask out the dot in "1.03" etc cmpi.l #$31003034,d2 ; Test for "1x03" where x = don't care bcs.s too_old ; Less than 1.03 is too old done rts ; Finished too_old move.w #128,d1 ; Must be 128 bytes rts ; All done extop move.w $64(a0),d1 ; Fetch the scan_line length move.l $32(a0),a1 ; Fetch the screen base moveq #0,d0 ; No errors rts ; done
So given that we have a channel id in A0 we can extract the required information from the channel definition block by using the SD_EXTOP trap. This trap takes the address of a routine to call in A2, parameters for the routine in D1, D2 and A1, a channel id in A0 and returns with D1 and A1 holding values returned from the routine called and an error code in D0.
The way we are using it here we don't need any parameters on the way in, but coming out, D1.W holds the scan_line size and A2.L holds the address for the start of the screen memory.
The actual routine itself get presented with the channel definition block's address in A0, not the channel id. Within the routine we copy the acreen base address into A1 and the scan_line size into D1.W and return.
On exit, we need to know if the scan_line size is correct so we call QDOS again to get the version of QDOS in D2. As this corrupts D1 we first save it on the stack. After the trap, D2 holds the ASCII representation of the QDOS version, for example "1.02" or "2.10" or possibly "1m03" for some 'foreign' ROMS (Foreign as in not UK!).
To test for the version we simply mask out the dot or the 'm' or whatever from D2 and if the version is less than 1x03, we simple set D1.W to 128 as this is the only value allowed. All other QDOS versions from 1x03 onwards have the correct scan_line size in D1.W.
So, on exit, A1.L holds the screen address and D1.W holds the scan_line size in bytes. This scan width is useful because we can use it to discover the maximum width of the screen in pixels, provided we know the mode - and I am talking abount mode 4 and 8 only here because that is all I know about!
If we have, as I have on my QXL, a scan_line of 160 bytes, what is this telling me? It says that the number of pixels across the screen will fit into one scan_line of 160 bytes. In mode 4 I know that one word of memory holds the data for 8 individual pixels. In mode 8, I know that one word in memory holds the data for 4 pixels. (Or, as My wife Alison refers to them, 'pixies'.)
As there are 16 bits in a word we can assume correctly that two bits hold the data for mode 4 pixels and 4 bits hold the data for mode 8 pixels. Thus we have 160 bytes times 8 bits and divided by 4 to give 640 pixels across in mode 4. In mode 8 the answer will be 320 BUT the screen width is always the mode 4 width. Only the pixels double up in mode 8, so plotting point 639,0 in mode 8 still works! (or is it 0,639 - I can never remember!)
Our calculation above still works because the memory address of a pixel is now :
screen_base + (y * screen_width) + INT(x / 4)
and this works even on a QXL. We come back to this later.