10 MODE4 20 VDU23,1,0;0;0;0; 30 P."A"
Now we must set up the printer. The first thing is that we don't want the printer to automatically print a line feed after every carriage return. This is because line-feed feeds the paper upwards by more than eight dots and we want each band (eight dots high) to butt up against the previous one. To do this we use *FX6,10 to disable the printer line-feed. Note that some printers are set up so that they automatically print a line-feed whenever a carriage return is sent. If your printer is set up to do this then the setting must be altered. This is done on Epson printers by changing the position of a small switch inside the printer. The printer manual gives details on how to do this. If you don't do this then the printer will split up your screen dumps into bands with gaps between them.
We then need to turn the printer on with VDU2. While we are doing this we can also tell the printer to go into a graphics mode where dots are printed horizontally 1/80 of an inch apart. As the dots are automatically printer 1/72 of an inch apart vertically in all graphics modes this will make our eight-by-eight pixel character come out approximately square. We also need to tell the printer that we are going to send it eight bytes of graphics. The codes to do this are 27,42,4,8,0. Here, the first three numbers put the printer into the desired graphics mode and the last two are the number of bytes of graphics that we are sending to it. Remember that it is up to you to find out the correct control sequences for your make, model and mark of printer. To resume:
40 *FX6,10 50 VDU2,1,27,1,42,1,4,1,8,1,0
Notice that we use VDU1,X so that the data doesn't appear on the screen. Now we are ready to dump the character. Our letter A is stored on the screen as eight horizontal bytes (stored in addresses &5800 to &5807).
To suit the printer, we need to code the A into eight vertical bytes. To do this we must set up a bit mask which, using AND, will first mask out everything but bit 7, the leftmost bit, of each byte, then bit 6, and so on until all eight columns have been printed. We can store the current value of this mask in the variable A% - the initial value must be 128 to mask out everything but bit 7. We can use this mask on each of the eight bytes of the character in turn, to extract the leftmost bits, making these into a vertical byte to send to the printer. Then we can set the mask to 64 to extract the next column, and so on until all eight columns have been printed.
For each column we first set a variable, say B%, to zero. We then check the relevant bit of &5800 using the mask. If it is set then we add 1 to B%. We can then check the relevant bit of &5801, and so on. We want the top bit of the column (from &5800) to be stored in bit 7 or B%, and so on. If, before each byte is checked with the mask, we shift B% left one bit by multiplying it by two, and then we add 1 if the bit is set, then, after all the eight bytes have been checked the first bit we extracted (from &5800) will be in bit 7; the next bit (from &5801) will be in bit 6; and so on.
When we have done this to get the first column, we send B% to the printer and divide A% by two to move the mask right one pixel for the next column. We go on doing this until A% has reached zero and we have thus printed eight columns. Finally, we must send a carriage return to the printer and turn the printer off.
45 P%=&5800 60 A%=128 70 B%=0 80 FORY%=0TO7 90 B%=B%*2 100 IFP%?Y%ANDA%THENB%=B%+1 110 NEXT 120 VDU1,B% 130 A%=A%/2 140 IFA%>0THEN70 150 VDU1,13,3
This will dump one character from the screen. Notice that, because we have used a separate variable to P% to store the address of the first byte of the character (line 45), we can print any character anywhere on the screen by simply changing P%. So, to print a whole band (which corresponds to a line of text on the screen) we need only alter the initial setting of the printer to say that we are going to send 40 characters of eight bytes each, or &140 bytes (line 50 below), add eight to P% after each character has been sent, and repeat this 40 times.
50 VDU2,1,27,1,42,1,4,1,&40,1,&1 55 FORX%=1TO40 143 P%=P%+8 147 NEXT
To make a complete screen dump we now need to feed the paper upwards by eight dots (each dot takes up 1/72 of an inch vertically) to print the next band. The Epson printers provide the command that will feed the paper upwards by n/216 of an inch. We want to feed by 8/72 so n has to be 24. Because of the way the screen is laid out, P% already points to the start of the next band when we get to line 150; so, if P% is less than &8000 (the end of the screen), then we can go back to line 50 and print the next band down.
150 VDU1,13,1,27,1,74,1,24 160 IFP%<&8000THEN50 170 VDU3
This program can then be added to an existing program, or a screen can be *SAVEd on disc and this routine can be used as a separate dump program by *LOADing the screen at the beginning.
10000 DEFPROCass 10010 DIMmc%150 10020 oswrch=&FFEE 10030 osbyte=&FFF4 10040 FORpass%=0TO2STEP2 10050 P%=mc% 10060 [OPTpass% 10070 .dump SEI 10080 LDA #6 10090 LDX #10 10100 LDY #0 10110 JSR osbyte
Before we carry on any further, we should notice that almost every VDU command in the BASIC program was in the form VDU1,X. To save space it would be sensible to write a subroutine which outputs first a 1 then the byte we want to send to the printer.
20000 .out PHA 20010 LDA #1 20020 JSR oswrch 20030 PLA 20040 JMP oswrch
This routine should be entered with the byte to be sent to the printer in the accumulator.
Going back to the main routine, we can set up P% in &70 and &71 and turn on the printer.
10120 LDA #0 10130 STA &70 10140 LDA #&58 10150 STA &71 10160 LDA #2 10170 JSR oswrch
Now we must initialise the printer for each band.
10180 .band LDA #27 10190 JSR out 10200 LDA #42 10210 JSR out 10220 LDA #4 10230 JSR out 10240 LDA #&40 10250 JSR out 10260 LDA #&1 10270 JSR out
We can use the X register to count the 40 characters that make up a horizontal band. We can use &72 to store the bit mask, A%; &73 to store the byte sent to the printer, B%; and the Y register for the loop, Y%.
10280 LDX #40 10290 .char LDA #128 10300 STA &72 10310 .column LDA #0 10320 STA &73 10330 LDY #0
Next we need to shift &73 (B%) left. Then we must load a byte from the screen and mask it with &72. If there is a pixel in this position then we must set bit zero of &73. Then we can repeat the loop until Y is eight.
10340 .pixel ASL &73 10350 LDA (&70),Y 10360 AND &72 10370 BEQ not 10380 LDA &73 10390 ORA #1 10400 STA &73 10410 .not INY 10420 CPY #8 10430 BNE pixel
We can then send the column of eight pixels stored at &73 to the pointer.
10440 LDA &73 10450 JSR out
Next we must shift the mask right one bit and repeat until we have dumped eight columns or a complete text character.
10460 LSR &72 10470 BNE column
Now we must add eight to &70 and &71 for the next character position and repeat back to the character routine 40 times to print one complete band.
10480 LDA &70 10490 CLC 10500 ADC #8 10510 STA &70 10520 BCC skip 10530 INC &71 10540 .skip DEX 10550 BNE char
Lastly we need to feed the paper upwards and repeat the band routine until the content of &71 is &80 or bigger. Then we can turn the printer off, enable interrupts and end the subroutine.
10560 LDA #13 10570 JSR out 10580 LDA #27 10590 JSR out 10600 LDA #74 10610 JSR out 10620 LDA #24 10630 JSR out 10640 LDA &71 10650 BPL band 10660 LDA #3 10670 JSR oswrch 10680 CLI 10690 RTS 10750 ] 10760 NEXT 10770 ENDPROC
And that's it.
As an example, try adding the following lines at the beginning of the assembly code:
10 MODE4 20 VDU23,1,0;0;0;0;29,640;512; 30 PROCass 40 S=1.1 50 P%=4 60 FORA=0TOPI*21STEPPI/20 70 PLOTP%,636*SIN(A),508*COS(A*S) 80 P%=5 90 NEXT 100 CALLdump 110 END
Mode 4 is very similar to Mode 0. To convert this program to run in Mode 0 only six lines need to be changed.
10 MODE 0 10140 LDA #&30 10220 LDA #1 10240 LDA #&80 10260 LDA #2 10280 LDX #80
Notice that the Epson does not provide a graphics mode that has 160 pixels to the inch horizontally, so we have to use the closest mode; this gives only 120 pixels to the inch. This has the effect of stretching the screen dump slightly horizontally.
This means that, as a Mode 2 screen is 160 pixels by 256 pixels, we will dump 960 (=6*160) by 512 (=2*256) dots on the printer.
Before we can do anything, we must know how a Mode 2 screen is laid out in the memory. As Mode 2 is a 16-colour mode, the computer must use four bits to store the colour of each pixel on the screen. Thus it can store two pixels (2x4=8 bits) in one byte. If there are 40960 (160x256) pixels on the screen, this means that to store the complete screen we need 20480 bytes or 20K.
The two pixels stored in each byte are always adjacent to each other and appear in a horizontal line on the screen. We would expect to find that the first 80 consecutive bytes (=160 pixels) of the screen memory make up the top row of pixels. However, this unfortunately is not so. The screen memory is organised in terms of character positions. One character is made up of 64 (8x8) pixels. This means that 32 bytes of screen memory are needed to make up each character. Horizontally, these are split up into four columns, each two pixels (or one byte) wide. Each column is then made up vertically of eight consecutive bytes.
The set of 32-byte characters is laid out, as would be expected, in 32 rows of 20 characters. So, for our purposes, we can look at the screen as divided into 32 rows. Each row is stored as 640 bytes and consists of 80 columns, each column being eight bytes tall. The first byte of the screen is stored at &3000 and the last at &7FFF. Of course, all this changes for different modes.
Mode 2 screen organisation
Finally, we must examine how the two Mode 2 pixels are stored in each screen memory byte. Again, we would expect the most significant four bits of the byte to hold the colour of the first pixel and the least significant four bits to hold the colour of the second pixel. Yet again, things are slightly more complex! The four two-bit numbers which represent the colours of the two pixels are 'interleaved' - first a bit from pixel one (the left-hand pixel) then a bit from pixel two (the right-hand pixel) then a bit from pixel one again, and so on.
It is obvious from this diagram that ours is not an easy task. Remember that we need to send graphics to the printer in bands of eight vertical dots (corresponding to the eight vertical dots on the printer head). This means that we need to group four screen pixels (each two dots high) above each other. Let's first of all assume that we have found the colours of each of these pixels (ignoring the most significant bit to exclude flashing colours) and we have stored the four of them, from top to bottom, in addresses &74 to &77. Now let's try and write a routine to dump these four pixels to the printer.
As often with machine code, the easiest way to do this is with a look-up table. This will contain data on the pattern of dots for each colour. However, we need to think carefully about how to arrange this information in a table. As the printer deals in vertical columns, the sensible way to split up the 12 dots per pixel is into six columns, devoting one byte of table for each column. Although we are wasting memory by only using two bits in each byte of the table, it will speed up the program if we use one byte of table for each column.
We now have to decide how to order these columns. The obvious way is to store them as six bytes for black followed by six bytes for red, etcetera. However, we must consider how we are going to address this table. To print the four vertical pixels we are going to send six bytes to the printer. The first byte will be made up of the first columns of each of the relevant colour patterns, the second byte will be made up of the second column of patterns, and so on. So, to send the first byte to the printer we are going to need access to the eight bytes that contain the first columns of each of the eight colour patterns. Then for the second byte we will need the eight bytes of the second columns and so on. Thus it makes sense to store the eight bytes that contain the first columns of each of the colour patterns, grouped together in the table.
To make the program easier we can store the data in groups according to the colours but just read them into the table in our preferred order.
10000DEFPROCass 10010DIMmc%300,D%47 10020RESTORE20000 10030FORA%=0TO7 10040FORB%=0TO5 10050READD%?(A%+B%*8) 10060NEXT, 20000 DATA3,3,3,3,3,3:REM Black 20010 DATA3,2,1,3,1,2:REM Red 20020 DATA0,1,1,0,2,2:REM Green 20030 DATA0,0,1,0,0,0:REM Yellow 20040 DATA3,3,1,3,3,2:REM Blue 20050 DATA1,2,1,2,1,2:REM Magenta 20060 DATA0,1,0,0,2,0:REM Cyan 20070 DATA0,0,0,0,0,0:REM White
Now the routine to print four vertical pixels should be relatively easy. First we are going to need, from the previous program, the routine for sending a byte to the printer.
10070 oswrch=&FFEE 14000 .out PHA 14010 LDA #1 14020 JSR oswrch 14030 PLA 14040 JMP oswrch
Now we can start. The first job is to set up the address of the start of the table in &7A and &7B. Then we can use post-indexed indirect addressing to look up the bytes for the first column of each pixel by setting the Y register to the colour number. Then when we have sent this byte to the printer we can add eight to the contents of &7A and &7B so that, taken together, they point to the address of the eight second-column bytes. We can use the X register to count the number of the bytes we have sent to the printer.
13000 .print LDA #D%MOD256 13010 STA &7A 13020 LDA #D%DIV256 13030 STA &7B 13040 LDX #6
Next we can set Y to the contents of &74 (the top pixel colour) and load in the two bits for the top two dots of the first column. These must eventually be the most significant two bits of the byte so we must shift them left. We shift left the bytes we are calculating, by two bits each time a pixel has been calculated, and so save a lot of effort:
13050 .prloop LDY &74 13060 LDA (&7A),Y 13070 ASL A 13080 ASL A 13090 STA &79 13100 LDY &75 13110 LDA (&7A),Y 13120 ORA &79 13130 ASL A 13140 ASL A 13150 STA &79 13160 LDY &76 13170 LDA (&7A),Y 13180 ORA &79 13190 ASL A 13200 ASL A 13210 STA &79 13220 LDY &77 13230 LDA (&7A),Y 13240 ORA &79 13250 JSR out
We have now sent the first of our six bytes to the printer and all that remains is to add eight to &7A and &7B and go back for the next byte.
13260 LDA &74 13270 CLC 13280 ADC #8 13290 STA &7A 13300 BCC skip2 13310 INC &7B 13320 .skip2 DEX 13330 BNE prloop 13340 RTS
This is all very well, but we still need to find out the four colours from the screen. We could use the operating system command that is equivalent to the BASIC function POINT, but this is slow and it is faster to work it out ourselves.
Let's now look at the main program and let's assume that we have already written a routine that will dump a whole band of Mode 2 graphics, four pixels high, given the address of the top left-hand corner of the line on the screen stored in &70 and &71.
Firstly, we need to disable interrupts and the printer line-feed, and turn the printer on.
10080 FORpass%=0TO2STEP2 10090 P%=mc% 10100 [OPTpass% 10110 .dump SEI 10120 LDA #6 10130 LDX #10 10140 LDY #0 10150 JSR &FFF4 10160 LDA #2 10170 JSR oswrch
Next, we need to store the address of the top left-hand corner of the screen in &70 and &71. Then to print the top half of a text line we need only our BAND routine.
10180 LDA #0 10190 STA &70 10200 LDA #&30 10210 STA &71 10220 .textlin JSR band
To print the bottom half we need to add four to the start address and call LINE again.
10230 LDA &70 10240 CLC 10250 ADC #4 10260 STA &70 10270 BCC skip 10280 INC &71 10290 .skip JSR band
To move on to the next line we need to add &280 to the address of the previous line, but we have already added four so we only need add &27C. If the answer is less than &8000 then we can go back and dump the next text line. Otherwise we must turn off the printer, re-enable the interrupts and end the routine.
10300 LDA &70 10310 CLC 10320 ADC #&7C 10330 STA &70 10340 LDA &71 10350 ADC #2 10360 STA &71 10370 BPL textlin 10380 LDA #3 10390 JSR oswrch 10400 CLI 10410 RTS
Now all we have to do is make our assumptions concrete and actually write BAND.
Firstly, we need to make a copy of &70 and &71 in &72 and &73. Then we need to initialise the printer to accept a 960-column band of graphics.
11000 .band LDA &70 11010 STA &72 11020 LDA &71 11030 STA &73 11040 LDA #27 11050 JSR out 11060 LDA #42 11070 JSR out 11080 LDA #1 11090 JSR out 11100 LDA #&C0 11110 JSR out 11120 LDA #3 11130 JSR out
Now we need to count the 80 groups of pixels that we must send to the printer (160 pixels per line and two pixels per byte). As we need both the X and Y registers we use location &79 to hold a count.
11140 LDA #80 11150 STA &78
Before we carry on, we need two routines that will extract the colours of the two pixels from a byte. Let us deal first with the left-hand pixel stored in the odd-numbered bits. We can arrange for the three bits of interest to us to be in bits 2, 4 and 6 (least significant to most significant) of the accumulator, using ASL once; then we can do two left-shifts to transfer the most significant bit in bit 6 into the carry flag. Then we can ROL the carry flag into a memory byte previously set to zero (&79). This leaves the next bit in bit 6 of the accumulator so we can repeat this a further two times to leave our three bits in bits 0-2 of location &79. If this sounds complicated, try following the code through:
12000 .colour1 LDA (&72),Y 12010 .colour ASL A 12020 INY 12030 LDX #0 12040 STX &79 12050 LDX #3 12060 .coloop ASL A 12070 ASL A 12080 ROL &79 12090 DEX 12100 BNE coloop 12110 LDA &79 12120 RTS
The INY command loads the next byte down on the next call of the routine. If we want to extract the colour of the other pixel we need only load the byte into the accumulator and shift it left one bit so that the bits we want occupy the same positions as for the other pixel. Then we can jump to the label COLOUR, conveniently placed in the previous routine, to finish the job.
12130 .colour2 LDA (&72),Y 12140 ASL A 12150 JMP colour
Now we can go back to BAND where we were just about to dump a line of graphics.
Firstly, we must set Y to zero so that we are ready to load the top byte. We can then call COLOUR1 to find the colour of the left-hand pixel and store this information in &74 ready for PRINT. We can then do the same for the other four pixels and call PRINT.
11160 .column LDY #0 11170 JSR colour1 11180 STA &74 11190 JSR colour1 11200 STA &75 11210 JSR colour1 11220 STA &76 11230 JSR colour1 11240 STA &77 11250 JSR print
We do the same for the four right-hand pixels.
11260 LDY #0 11270 JSR colour2 11280 STA &74 11290 JSR colour2 11300 STA &75 11310 JSR colour2 11320 STA &76 11330 JSR colour2 11340 STA &77 11350 JSR print
Now, to print the next set of four bytes, we need to add eight to &72 and &73 and repeat until we have printed all 80 groups of pixels.
11360 LDA &72 11370 CLC 11380 ADC #8 11390 STA &72 11400 BCC skip1 11410 INC &73 11420 .skip1 DEC &79 11430 BNE column
Lastly we need to feed the paper and return.
11440 LDA #13 11450 JSR out 11460 LDA #27 11470 JSR out 11480 LDA #74 11490 JSR out 11500 LDA #24 11510 JMP out 15000 ] 15010 NEXT 15020 ENDPROC
At last we have finished. Try the following example to check that the routine works.
10 MODE 2 20 VDU23,1,0;0;0;0; 30 PROCass 40 COLOUR135 50 CLS 60 COLOUR128 70 RESTORE180 80 P%=4 90 FORA%=0TO8 100 READB% 110 GCOL0,B% 120 MOVE0,0 130 PLOTP%,1000*SIN(A%*PI/16),1000*COS(A%*PI/16) 140 P%=85 150 NEXT 160 CALLdump 170 END 180 DATA0,0,4,1,5,2,6,3,7
1 2 1 2 1 2 1 2 1 2 3 4 3 4 3 4 3 4 3 4 5 6 5 6 5 6 5 6 5 6 1 2 1 2 1 2 1 2 1 2 3 4 3 4 3 4 3 4 3 4 5 6 5 6 5 6 5 6 5 6 1 2 1 2 1 2 1 2 1 2 3 4 3 4 3 4 3 4 3 4 5 6 5 6 5 6 5 6 5 6 1 2 1 2 1 2 1 2 1 2 3 4 3 4 3 4 3 4 3 4 5 6 5 6 5 6 5 6 5 6 1 2 1 2 1 2 1 2 1 2 3 4 3 4 3 4 3 4 3 4 5 6 5 6 5 6 5 6 5 6 1 2 1 2 1 2 1 2 1 2 3 4 3 4 3 4 3 4 3 4 5 6 5 6 5 6 5 6 5 6 1 2 1 2 1 2 1 2 1 2 3 4 3 4 3 4 3 4 3 4 5 6 5 6 5 6 5 6 5 6 1 2 1 2 1 2 1 2 1 2 3 4 3 4 3 4 3 4 3 4 5 6 5 6 5 6 5 6 5 6
It is very difficult to try and use direct screen access to dump the screen in this example (though you are welcome to try if you are feeling masochistic) so we will use OSWORD call 9 (the operating system equivalent of POINT).
The first thing we need, then, is a routine to set up a parameter block and call OSWORD 9. Let us state that the X coordinate should be stored in &70 and &71 and the Y coordinate in &72 and &73. This means that our OSWORD parameter block can be taken as starting at &70 and the colour will be returned in &74. The first job is to save the X and Y registers and set up the three registers for the call.
20000 .point STX &76 20010 STY &77 20020 LDA #9 20030 LDY #0 20040 LDX #&70 20050 JSR &FFF1
Now we can reload the X and Y registers with their original values (saved at &76 and &77). We also need to check if the point was on the screen. This is because there are 256 pixels vertically on the screen and we are printing in lots of 24. Now, 24 into 256 doesn't go, so the last line is going to drop off the bottom and when this happens POINT must return black. The OSWORD call we're using will return 255 so we need only check that the colour is positive, and, if not, return zero.
20060 LDX &76 20070 LDY &77 20080 LDA &74 20090 BPL point1 20100 LDA #0 20110 .point1 RTS
We also need our old faithful routine, OUT.
20120 .out PHA 20130 LDA #1 20140 JSR oswrch 20150 PLA 20160 JMP oswrch
Now, the main routine. We reset the graphics windows and origin, as we are using POINT; otherwise, we might end up dumping the wrong part of the screen. Also, we can disable interrupts.
10000 DEFPROCass 10010 DIMmc%300 10020 oswrch=&FFEE 10030 FORpass%=0TO2STEP2 10040 P%=mc% 10050 [OPTpass% 10060 .dump SEI 10070 LDA #26 10080 JSR oswrch
Next we must disable printer line-feeds and turn on the printer.
10090 LDA #6 10100 LDX #10 10110 LDY #0 10120 JSR &FFF4 10130 LDA #2 10140 JSR oswrch
Now we must set the Y coordinate of the top pixel of the screen in &78 and &79 ready to work down the screen.
10150 LDA #&FC 10160 STA &78 10170 LDA #3 10180 STA &79
For the moment let's assume that we already have a routine BAND that prints one pass of the print head, i.e. given the coordinates of the top left-hand pixel of a band (320 pixels by 24 pixels) it prints one sixth of the dots on that band. The X coordinate needs to be in &70 and &71 and the Y coordinate in &78 and &79. Also, we need to tell the routine whether it is printing odd-numbered or even-numbered dots, horizontally, on that pass. We can do that by supplying a mask in &7A - either 255 for the even dots or zero for the odd ones. The reason for this will be become obvious in a moment. Armed with this routine, we can finish the main routine.
The first job, as we are going to print three interlaced passes vertically, is to set the X register to count these.
10190 .loop1 LDX #3
Next, we need to set up the X coordinate as zero and the mask as 255 for the even pixels.
10200 .loop2 LDA #0 10210 STA &70 10220 STA &71 10230 LDA #255 10240 STA &7A 10250 JSR band
Next, for the odd pixels we need to set the X coordinate to four and the mask to zero.
10260 LDA #4 10270 STA &70 10280 LDA #0 10290 STA &71 10300 STA &7A 10310 JSR band
Now we must feed the paper 1/216 of an inch ready for the next interleaved set of pixels. We also need to move the Y coordinate down one pixel and then repeat the whole process.
10320 LDA #27 10330 JSR out 10340 LDA #74 10350 JSR out 10360 LDA #1 10370 JSR out 10380 LDA &78 10390 SEC 10400 SBC #4 10410 STA &78 10420 BCS skip3 10430 DEC &79 10440 .skip3 DEX 10450 BNE loop2
We have now printed a whole band 24 dots high and need to feed the paper onward 24 dots (less the three we have already fed it).
10460 LDA #27 10470 JSR out 10480 LDA #74 10490 JSR out 10500 LDA #21 10510 JSR out
We then need to move the Y coordinate down 24 pixels (less the three we have already moved it); and, if Y coordinate is still on the screen, we must go back to dump the next band.
10520 LDA &78 10530 SEC 10540 SBC #84 10550 STA &78 10560 LDA &79 10570 SBC #0 10580 STA &79 10590 BPL loop1
All that remains is to disable the printer, re-enable the interrupts again and exit.
10600 LDA #3 10610 JSR oswrch 10620 CLI 10630 RTS
Now we need to write BAND. The first thing this routine needs to do is to set up the printer to receive 320 bytes of graphics.
10640 .band LDA #127 10650 JSR out 10660 LDA #42 10670 JSR out 10680 LDA #3 10690 JSR out 10700 LDA #&40 10710 JSR out 10720 LDA #1 10730 JSR out
Next we need to make a temporary working copy of the Y coordinate in &72 and &73 for POINT to use.
10740 .line1 LDA &78 10750 STA &72 10760 LDA &79 10770 STA &73
We now need to work out a byte. Again, we are going to use the technique of shifting a byte of memory left while setting bit one to the colour of pixel and repeating this eight times. So first we need to set the byte (&75) to zero. Then we need the Y register to count the eight times. The first job inside the loop is to shift &75 left a bit. Then we can use POINT to return the colour of the pixel. If it is zero then we can leave &75 alone as we have already set all the bits to zero. Otherwise, we need to set bit zero to one. As this will always be zero to start with we can simply use the command INC to set it to one.
10780 LDA #0 10790 STA &75 10800 LDY #8 10810 .line2 ASL &75 10820 JSR point 10830 BEQ skip1 10840 INC &75 10850 .skip1
Next we need to move down three pixels to allow for the interleave, and repeat the process.
10850 .skip1 LDA &72 10860 SEC 10870 SBC #12 10880 STA &72 10890 BCS skip2 10900 DEC &73 10910 .skip2 DEY 10920 BNE line2
We are now ready to send a byte to the printer. However, if &7A is set to 255, then we want to send the byte followed by a zero; if it is set to zero, then we want to send a zero followed by the byte. So, as the first byte to send to the printer, we can use (&75 AND &7A). For the second byte we can use (&75 AND (&7A EOR 255)).
10930 LDA &7A 10940 AND &75
Now all that remains to do is move on to the next-pixel-but-one horizontally by adding eight to the X coordinate; and then, if the answer is less than 1280 or &500, go back and send the next pair of bytes. If the end of the line has been reached then we must send a carriage return and exit from the routine.
10950 JSR out 10960 LDA &7A 10970 EOR #255 10980 AND &75 10990 JSR out 11000 LDA &70 11010 CLC 11020 ADC #8 11030 STA &70 11040 LDA &71 11050 ADC #0 11060 STA &71 11070 CMP #5 11080 BNE line1 11090 LDA #13 11100 JMP out 25000 ] 25010 NEXT 25020 ENDPROC
We now have a finished program. To try it out you can use the example program from the large Mode 4 dump routine.
Now that we have discovered the maximum resolution of the printer it is well worth going back to the Mode 2 dump. Let us consider using pixels represented as six dots by three dots. If we dump a Mode 2 screen at the highest resolution using this system then it will be about 4 inches by 3.5 inches. Also, we should be able to make a grey scale that will handle 16 levels of brightness. This means that we can use each of the 16 colours that the BBC Micro will handle in Mode 2. Obviously we can't represent flashing colours so the best way to make use of this is not to try and print exactly what is on the screen but to represent each colour from 0 to 15 as a shade with 0 darkest and 15 brightest.
It turns out that with six-by-three dots at the highest resolution the third row can always be left blank and we still get a good range of colours. This speeds printing up as we will only have to print four interleaved lines. But if we try to print the same pattern for each pixel, one above the other, they will tend to produce visible vertical lines. So we will use TWO sets of patterns and use alternate sets for alternate rows of pixels. The patterns we will use are as below:
As we are going to dump all the top rows then all the bottom rows, it would seem sensible to block all the top rows in one group and all the bottom rows in one group. Within each group we can group the entries into all the column ones then all the column twos, etcetera. We can store the two alternate sets of patterns, in this format, one after the other. We are going to build up the bytes to send to the printer by shifting them left and ORing them with 1 or 0. For this reason it will be easiest if we use one whole byte of a table to store each of the dot patterns. These bytes will either be 1 for a dot or 0 for no dot. The data looks as set out below.
20000 DATA1,1,1,0,0,0,1,1 20010 DATA1,1,0,0,0,0,0,0 20020 DATA1,1,1,1,1,1,1,0 20030 DATA0,0,0,0,0,0,0,0 20040 DATA1,0,0,0,0,0,0,0 20050 DATA0,0,1,0,0,0,0,0 20060 DATA1,1,1,1,1,1,0,0 20070 DATA0,0,0,1,0,0,0,0 20080 DATA1,1,0,0,0,0,0,1 20090 DATA1,0,0,1,1,0,0,0 20100 DATA1,1,1,1,1,0,0,0 20110 DATA0,0,0,0,0,0,0,0 20120 DATA1,1,1,1,1,1,0,0 20130 DATA0,0,0,0,0,0,0,0 20140 DATA1,1,0,0,0,0,0,0 20150 DATA0,0,0,0,0,0,0,0 20160 DATA1,1,1,1,0,0,0,1 20170 DATA1,0,0,0,0,0,0,0 20180 DATA1,1,1,0,0,0,1,0 20190 DATA0,1,0,0,0,0,0,0 20200 DATA1,1,1,1,1,1,1,0 20210 DATA0,0,0,0,0,0,0,0 20220 DATA1,0,0,0,0,0,0,0 20230 DATA0,0,0,0,0,0,0,0 20040 DATA1,1,1,0,0,0,1,1 20250 DATA1,1,0,0,0,0,0,0 20260 DATA1,1,1,1,1,1,1,0 20270 DATA0,0,0,1,1,0,0,0 20280 DATA1,0,0,0,0,0,0,0 20290 DATA0,0,0,0,0,1,1,0 20300 DATA1,1,1,1,1,1,0,0 20310 DATA0,0,0,0,0,1,0,0 20320 DATA1,1,0,0,0,0,0,1 20330 DATA0,0,1,0,0,0,0,0 20340 DATA1,1,1,1,0,0,0,0 20350 DATA0,0,0,0,0,0,0,0 20360 DATA1,1,1,1,1,1,0,0 20370 DATA1,1,0,0,0,0,0,0 20380 DATA1,1,0,0,0,0,0,0 20390 DATA0,0,0,0,0,0,0,0 20400 DATA1,1,1,1,1,0,0,1 20410 DATA0,0,0,0,0,0,0,0 20420 DATA1,1,1,0,0,0,1,0 20430 DATA1,1,0,0,0,0,0,0 20440 DATA1,1,1,1,1,1,1,0 20450 DATA0,0,0,0,0,0,0,0 20460 DATA1,0,0,0,0,0,0,0 20470 DATA0,0,0,0,0,0,0,0
So our first job is to read this into a reserved area of memory.
10000 DEFPROCass 10010 DIMmc%1000,D%383 10020 oswrch=&FFEE 10030 FOR A%=0TO383 10040 READD%?A% 10050 NEXT 10060 FORpass%=0TO2STEP2 10070 P%=mc% 10080 [OPTpass%
We will again need our trust routines, POINT and OUT. This time we don't need to worry about the point being off the screen as we are printing at eight pixels per band and eight goes into 256 exactly.
18000 .point STX &76 18010 STY &77 18020 LDA #9 18030 LDY #0 18040 LDX #&70 18050 JSR &FFF1 18060 LDX &76 18070 LDY &77 18080 LDA &74 18090 RTS 19000 .out PHA 19010 LDA #1 19020 JSR oswrch 19030 PLA 19040 JMP oswrch 19050 ] 19060 NEXT 19070 ENDPROC
Now for the main routine. As with the previous routine we need first of all to disable the interrupts, reset windows, disable printer line-feeds and turn the printer on.
10090 .dump SEI 10100 LDA #26 10110 JSR oswrch 10120 LDA #6 10130 LDX #10 10140 LDY #0 10150 JSR &FFF4 10160 LDA #2 10170 JSR oswrch
Next we need to set up the Y coordinate of the top of the screen in &78 and &79.
10180 LDA #&FC 10190 STA &78 10200 LDA #3 10210 STA &79
Before we carry on, we need a routine to print one pass of the printer head. We can specify that on entry the Y coordinate of the top left-hand corner of the band is in &78 and &79; that the contents of &7B are zero for the top row and 96 for the bottom row (this allows us to add this to the table address to take care of which row we are printing); and that &7A contains a mask which is 255 for the even-numbered dots and zero for the odd numbered ones.
The first job, as always, is to set the printer to the right graphics mode - here, quadruple-density with 960 (&3C0) dots across.
15000 .band LDA #27 15010 JSR out 15020 LDA #42 15030 JSR out 15040 LDA #3 15050 JSR out 15060 LDA #&C0 15070 JSR out 15080 LDA #3 15090 JSR out
Next we must set the X coordinate to zero.
15100 LDA #0 15110 STA &70 15120 STA &71
Next, for each column of pixels we send to the printer, we need to set up the address of the table in &7C and &7D. This will be D% plus the contents of &7B.
15130 .column LDA #D%MOD256 15140 CLC 15150 ADC &7B 15160 STA &7C 15170 LDA #D%DIV256 15180 ADC #0 15190 STA &7D
Now we are ready to send six bytes to the printer. We can use the X register to count the bytes. For each byte we need to make a temporary working copy of the contents of &78 and &79 in &72 and &73.
15200 LDX #6 15210 .byte LDA &7B 15220 STA &72 15230 LDA &79 15240 STA &73
We are going to work on the byte in &75 so we need to set it to zero for starters. Then we can count the bits we have worked on, with the Y register.
15250 LDA #0 15260 STA &75 15270 LDY #8
Now for every bit we calculate we first need to shift &75 left a bit. Then we must find the colour of the pixel.
15280 .bit ASL &75 15290 JSR point
We are going to use the Y register to point into the table so we need to save the Y register at &76 until we have finished with the table. We have the colour of the pixel in the accumulator, so, by transferring it to the Y register we can use it directly to point into the table. However, if we are calculating an odd-numbered bit then we need to use the second set of shade patterns; these are 192 bytes further on in the table. We need to check bit 0 of the bit count which we have temporarily stored at &76; if it is one, we must add 192 to the Y register. Then we can load a byte from the table. At this point we have finished with the Y register and we can reload its original bit count value.
15300 STY &78 15310 TAY 15320 LDA #1 15330 BIT &76 15340 BEQ skip1 15350 TYA 15360 CLC 15370 ADC #192 15380 TAY 15390 .skip1 LDA (&7C),Y 15400 LDY &76
This byte we need to OR with the byte we are calculating. We then need to move down a pixel and, if we haven't already finished the byte, go back and calculate the next bit.
15410 ORA &75 15420 STA &75 15430 LDA &72 15440 SEC 15450 SBC #4 15460 STA &72 15470 BCS skip2 15480 DEC &73 15490 .skip2 DEY 15500 BNE bit
Now we can send the byte to the printer. However, we must only send alternate bytes so we must mask it with &7A and reverse the mask in &7A ready for the next byte.
15510 LDA &75 15520 AND &7A 15530 JSR out 15540 LDA &7A 15550 EOR #255 15560 STA &7A
Next we must move the start of the table to the next column. Then, unless we have printed all six, we must go back and print the next column.
15570 LDA &7C 15580 CLC 15590 ADC #16 15600 STA &7C 15610 BCC skip3 15620 INC &7D 15630 .skip3 DEX 15640 BNE byte
We have now dumped a column of eight pixels and can move on to the next column. If we have printed a whole line then we can send a carriage return and exit the routine.
15650 LDA &70 15660 CLC 15670 ADC #8 15680 STA &70 15690 LDA &71 15700 ADC #0 15710 STA &71 15720 CMP #5 15730 BNE column 15740 LDA #13 15750 JMP out
We can now finish off the main routine. For every text line, we must first set &7B to zero for the top row. Then, for each row, we must make two pases: first with the mask set to 255, then with the mask set to zero.
10220 .textlin LDA #0 10230 STA &7B 10240 .row LDA #255 10250 STA &7A 10260 JSR band 10270 LDA #0 10280 STA &7A 10290 JSR band
Next we must feed the paper a fraction and set &7B to 96 for the second row. By EXCLUSIVE Oring &7B with 96 we can use this to count the two rows.
10300 LDA #27 10310 JSR out 10320 LDA #74 10330 JSR out 10340 LDA #1 10350 JSR out 10360 LDA &7B 10370 EOR #96 10380 STA &7B 10390 BNE row
Now we can feed the paper up the rest of the line. Because the printer will not feed accurately at 1/216 of an inch it tends to feed more than this, so the two feeds we have performed will have fed closer to 3/216 of an inch. To correct this we need to feed only a further 21/216 of an inch. This may not be necessary on some printers, so you should experiment.
10400 LDA #27 10410 JSR out 10420 LDA #74 10430 JSR out 10440 LDA #21 10450 JSR out
All that remains is to move down to the next text line and repeat until we have dumped the whole screen; then we turn off the printer, re-enable the interrupts, and exit in the usual fashion.
10460 LDA &78 10470 SEC 10480 SBC #32 10490 STA &78 10500 LDA &79 10510 SBC #0 10520 STA &79 10530 BPL textlin 10540 LDA #3 10550 JSR oswrch 10560 CLI 10570 RTS
To try out the dump routine, add the following lines to the assembly code:
10 MODE2 20 VDU23,1,0;0;0;0; 30 PROCass 40 COLOUR135 50 CLS 60 COLOUR128 70 P%=4 80 FORA%=0TO16 90 GCOL0,A%-1 100 MOVE0,0 110 PLOTP%,1000*SIN(A%*PI/32),1000*COS (A%*PI/32) 120 P%=85 130 NEXT 140 CALLdump 150 END
If you want to produce larger pictures it is relatively easy to join two or more dumps together. We have only looked at a selection of the screen dumps that can be written but these should give you an idea of how to go about writing any others you may need.