M/C Part 12 This month, um, I think we'll have a look at another graphics routine, since my scrolly cylinder stuff a few months back was kind of OK. If you've bought Impatience (and if you haven't, why not?) you will have sat in awe (or something) of the spinning Fred logo at the start of Triltex. I know this is not something everyone wants to do (I might be wrong), but sometimes learning how problems are solved can be useful. Right, now basically the routine takes a block of graphics data (in this case, the Fred logo) and dumps it onto the screen. As it does so, it occasionally "skips" lines of data, giving a "shrinking" effect. If we change the number of lines to miss out every frame, we can control the effect. The method I used was to take a table which held one value for each frame. This value is equlivilant to the number of data lines to skip every time we dump a line to the screen. Confused? Well, if we want to skip one line, the value is 1.00; if we want to skip two lines, the value is 2.00; if we want to skip 1 and a half lines the value is 1.50. Of course, we can't actually jump 1.5 lines on, and continue dumping the graphics, because the image would become garbage. Instead we ignore the fractional part when it comes to dumping. Let me show you how easy this is: Oh, and before I go on, I'd better explain how I use fractions. Well, the good old Z80 can do 8-bit calculations very easily using the accumulator. It can do 16-bit calculations almost as well using HL, and this is what we use. Normally, HL contains a whole number between 0 and 65535: H holds the most significant byte (MSB), and L the least significant byte (LSB). Since the line numbers in this routine will never exceed 255, surely the line number can be held just as well in H. This means that L can be taken as the fractional part of the number. Bit 7 of L will have the value 1/2; bit 6 will be worth 1/4, bit 5 -> 1/8 and so on. If we use this method of storing the values in the above table, we can have HL (or DE, BC etc) accumulating a fractional line number value. The integer part of this is simply H. The only complication to this is that on the SAM, lines are 128 bytes long. To make provision for this, bits 7 -> 0 of H, and bit 7 of L are actually the line number's integer part. Bits 6 -> 0 of L are the fraction. Eg. We have a "skip" value of 1.5. Using HL as the pointer to the data, and DE as a contiuously increasing pointer to the screen: 1) Store this value somewhere. 2) Mask out the fractional part of L (a logical AND with 128). 3) Dump the line from HL to DE, using LDIR. 4) Recover to stored line value, which will contain a fraction. 5) Add on 1.5, and loop until finished. HL starts at, say, 0, and this is the line number the data is dumped from. Next time round HL holds %0000000011000000 (ie. 1.5), but since bits 6 -> 0 are ignored when the line is dumped, it is dumped from line 1. Next time round HL holds %0000000110000000 (ie. 3.0), and so on. Now for the code. One little point - since the logo is supposed to be rotating in mid-air, the point on the screen at which to start dumping will move up and down, so the routine calculates this. It also means that, if the image is getting smaller, the old image has to be erased. Since a dummy screen is used, this is not a problem. Oh, and when the image goes upside down, the data needs to be read backwards (using a negative skip value). NB. The full routine sits over the ROM with its own interrupt handler, so that the screen can go in at #8000, and two screens are used - at pages 10/11 and 14/15. The Fred logo is 50 lines tall. The table of skip values also contains the height, in lines, of the image with each skip value. spin IN A,(vmpr) AND 31 XOR 4 ; Find the dummy screen OUT (hmpr),A ; Put it in hmpr. LD HL,fred_screen ; Fred_screen is the address ; of where we are dumping to LD DE,fred_screen+1 LD BC,6399 LD (HL),0 ; Erase the image. LDIR LD HL,(data_start); Get the address of where ; the data starts - it can ; be read from either end. LD DE,(next_logo_line) ; Get the starting screen ; address. LD IX,no_lines ; Contents of (no_lines) ; equals actual height of ; image on screen. sp1 LD BC,128 PUSH HL ; Store line number, ; including fraction. LD A,L AND 128 ; Mask out the fraction. LD L,A LDIR ; Dump a line. POP HL ; Recover real line number. LD BC,(logo_step) ; Get the skip value, ADD HL,BC ; and add it on. DEC (IX+0) ; Decrement the counter, JR NZ,sp1 ; and loop until all lines ; have been dumped. LD HL,(logo_line_pl) ; Get the location in the ; table. sp2 LD A,(HL) ; Get the height of the image ; There are special token ; values: ; 253 - start from data end. ; 254 - start from data top. ; 255 - go back and start ; from the beginning ; of this table. CP 253 ; Any other value is a height JR C,sp5 CP 254 JR Z,sp3 CP 255 JR Z,sp4 LD BC,logo_data+6272 ; End of data LD (data_start),BC INC HL INC HL JR sp2 sp3 LD BC,logo_data ; Start of data LD (data_start),BC INC HL INC HL JR sp2 sp4 LD HL,logo_lines ; Start of value table LD (logo_line_pl),HL JR sp2 sp5 LD (no_lines),A ; Store the height. INC HL INC HL LD C,(HL) ; Get the skip value INC HL LD B,(HL) INC HL LD (logo_step),BC ; Store it. LD (logo_line_pl),HL ; Also store the current ; position in the table. SRL ; SRL shifts acts like ; RRA, except that the MSB ; is always filled with 0, ; not with the carry. ; This bit calculates the new ; start of the image on the ; screen: take the height, ; half it, take this away ; from half the maximum ; height, and add on the ; screen position all the ; frames start at:- NEG ADD A,25+ystart LD C,0 SCF RRA RR C LD B,A LD (next_logo_line),BC IN A,(vmpr) ; Switch screens. XOR 4 OUT (vmpr),A LD A,(exit_flag) ; This could be a key press ; checker. AND A RET NZ JR spin logo_data EQU ???? ; Anywhere you like. no_lines DB 50 next_logo_line DW fred_screen logo_step DW 128 ; ie. 1.00 logo_line_pl DW logo_lines data_start DW logo_data fred_screen EQU #8000 ; Top of the screen ystart EQU 0 ; "" "" "" ""