M/C Part 7 Number One.. The Scrolly Cylinder Type Thing ============================================ This is a prime example of something which we see all the time on fancy computers, but we could never imagine an 8-bit machine like the coupe to manage. Unless we cheat, that is. Basically this routine takes a mode 4 screen and wraps it around a 64 line cylinder to create a 3D effect. With a little more effort it can be animated to produce a very nice effect indeed. Firstly, we have to work out how we'll go about solving the problem, and this is my solution. The routine will only alter the graphics vertically, so we can forget about any effect on the piccy horizontally by the curve of the cylinder. To create a curved image, therefore, we have to be sneaky. There is a table of offsets at the end of the program and this is used in the following way... 1> The first line of the piccy is copied onto the screen. 2> Instead of just moving onto the next line of data, we jump the number of lines specified in the offset table. 3> We move onto the next line of screen and repeat the process for all 64 lines. The routine manages to distort the image in such a way that it looks round on the screen. Now, the proggie.. ORG #E000 ; In the space after a screen in HMPR SCROLLY DI LD (SPSTORE),SP LD SP,STKSPACE IN A,(LMPR) EX AF,AF' ; See below LD A,%00101011 ; Page 11 with no ROM OUT (LMPR),A LD HL,(SCROLLPOS) LD DE,#8000 ; Top of screen - you could change ; this to another area. LD IX,OFFSETS LD B,32 ; No. of lines in top half SC1 PUSH BC LD C,(IX+0) LD B,(IX+1) INC IX INC IX ADD HL,BC ; Add on the offset CALL WRAPROUND INC L INC L INC L INC L INC E INC E INC E INC E LD BC,120 ; LDIR is a block instruction that LDIR ; copies BC bytes from HL to DE. INC L INC L INC L INC HL INC E INC E INC E INC DE POP BC DJNZ SC1 LD B,32 ; No. of lines in lower half SC2 PUSH BC CALL WRAPROUND INC L INC L INC L INC L INC E INC E INC E INC E LD BC,120 LDIR INC L INC L INC L INC HL INC E INC E INC E INC DE DEC IX DEC IX LD C,(IX+0) LD B,(IX+1) ADD HL,BC POP BC DJNZ SC2 LD HL,(SCROLLPOS) ; This bit adds 1 line to the LD DE,128 ; start of the graphic data, ADD HL,DE ; to give an impression of CALL WRAPROUND ; rotation. LD (SCROLLPOS),HL EX AF,AF' OUT (LMPR),A LD SP,(SPSTORE) EI RET WRAPROUND LD A,H ; This subroutine alters HL CP 96 ; if it goes over the end of RET C ; the graphic data, so that SUB 96 ; it 'wraps wround'. LD H,A RET SPSTORE DW 0 DS 14 STKSPACE DW 0 OFFSETS DW 384,384,256,256,256,128,256,128,128 DW 128,0,128,0,0,128,0 DW 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 SCROLLPOS DW 0 A couple of points - all the sections with lots of INC L's and INC E's are to avoid copying 4 bytes (8 pixels) at either edge of the screen. If the background of the piccy is left black (or another colour), and in these 16 pixels the egdes of a roller are drawn, the whole effect is much better. LDIR is a block instruction that copies BC bytes from HL to DE, and moves both HL and DE on that amount of bytes. BC becomes zero. I don't think I've mentioned it in any previous issue, but aside from the regular registers, there is an alternative set. Only one set can be used at any one time, and the commands for switching between them are:- EX AF,AF' - Switches just for A and F, used above to store the original accumulator value. EXX - Switches for all the rest. If you reckon the final effect isn't just right, you could change the values in the offset table. Each entry must be a multiple of 128. Also, you can change the size of the roller. There are two LD B,32 instructions - one for each half of the roller. If you do change this, make sure there are enough entries in the offset table (half the total number of lines) Number Two.. The Expansion and Compression Routines =================================================== I remember seeing a BASIC expansion routine in FRED a while ago. That was pretty slow (in fact, VERY slow!) due to the fact it was written in BASIC. Here is a routine to take a window 128 pixels long and 96 pixels down, and to expand it to fill the entire screen. ORG #4000 ; In system heap, like last month EXPAND DI IN A,(VMPR) AND 31 OUT (HMPR),A LD HL,(SEED) ; System var set by RANDOMIZE LD DE,#E000 LD A,96 EX1 LD BC,64 ; Firstly, copy the window into LDIR ; the 8k workspace after the LD BC,128-64 ; screen ADD HL,BC DEC A JR NZ,EX1 ; LD HL,#8000 LD DE,#E000 LD C,96 EX2 LD B,64 EX3 LD A,(DE) ; Get byte AND %11110000 ; Isolate 1st nibble LD (HL),A ; Store it RLCA ; Rotate into other nibble RLCA RLCA RLCA OR (HL) ; Merge together LD (HL),A ; And plant on screen SET 7,L ; Move onto next line LD (HL),A ; And store it there too. RES 7,L ; Move back INC L ; And on one byte LD A,(DE) AND %00001111 ; Repeat the whole process for the LD (HL),A ; other nibble. RLCA RLCA RLCA RLCA OR (HL) LD (HL),A INC L INC DE DJNZ EX3 ; Repeat this for the whole line INC H ; Move down two lines on screen. DEC C JR NZ,EX2 ; And go round another 95 times EI RET SEED EQU #5C76 This is pretty simple - copy the window into a workspace, then take each pixel in turn and plot it into the four pixels onto the new screen. Right, now the squash routine. It does exactly the same, but in reverse. ORG #4000 SQUASH DI IN A,(VMPR) ; Put screen in HMPR AND 31 OUT (HMPR),A LD HL,#8000 LD DE,#E000 LD C,96 ; 96 lines in squashed version, SQ1 LD B,64 ; each 64 bytes wide. SQ2 LD A,(HL) ; Get 1st nibble of 1st byte, and INC L ; second nibble of next byte. XOR (HL) ; Merge then together - see below. AND %11110000 ; And store (in temporary XOR (HL) ; workspace) LD (DE),A INC DE DJNZ SQ2 POP HL INC H ; Move on two lines; ie. miss one RES 7,L ; out to compress. DEC C JR NZ,SQ1 ; COPY LD DE,#8000 ; Screen address to copy to. LD HL,#E000 LD A,96 C1 LD BC,64 ; Copy from workspace. LDIR EX DE,HL ; These lines blank outthe rest of LD B,64 ; the line - can be omitted. C2 LD (HL),C ; . INC HL ; . DJNZ C2 ; . EX DE,HL ; . DEC A JR NZ,C1 EX DE,HL ; And these blank out the rest of LD DE,#B001 ; the screen. Again, they might LD (HL),0 ; be unwanted. LD BC,#2FFF ; . LDIR ; . EI RET A couple of points again - if you want to copy to a different bit of the screen, omit the lines for blanking bits out, or you'll get awfully confused. There is a handy little bit of code above called a BIT MERGE - the lines XOR (HL); AND %11110000; XOR (HL) These three steps are useful when we want to copy some bits of one register over only some of the bits of another. Eg. if we had a screen address in HL and wanted to add on only the second bit of the accumulator, we'd go: XOR (HL) AND %11110000 ; Show the bits of (HL) to preserve XOR (HL)