<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://learn.cemetech.net/index.php?action=history&amp;feed=atom&amp;title=Z80%3ADrawing_Routines</id>
	<title>Z80:Drawing Routines - Revision history</title>
	<link rel="self" type="application/atom+xml" href="http://learn.cemetech.net/index.php?action=history&amp;feed=atom&amp;title=Z80%3ADrawing_Routines"/>
	<link rel="alternate" type="text/html" href="http://learn.cemetech.net/index.php?title=Z80:Drawing_Routines&amp;action=history"/>
	<updated>2026-07-27T04:02:21Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://learn.cemetech.net/index.php?title=Z80:Drawing_Routines&amp;diff=134&amp;oldid=prev</id>
		<title>KermMartian: Created page with &quot;There are a limited number of ways for your programs to communicate with a user. You can use text, graphics, and rarely sound. With text, the OS offers us many tools and it do...&quot;</title>
		<link rel="alternate" type="text/html" href="http://learn.cemetech.net/index.php?title=Z80:Drawing_Routines&amp;diff=134&amp;oldid=prev"/>
		<updated>2016-02-05T06:37:28Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;There are a limited number of ways for your programs to communicate with a user. You can use text, graphics, and rarely sound. With text, the OS offers us many tools and it do...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;There are a limited number of ways for your programs to communicate with a user. You can use text, graphics, and rarely sound. With text, the OS offers us many tools and it doesn&amp;#039;t need to be too fast, but with graphics, the OS routines often do not do the job. Here are a few routines to get the job done.&lt;br /&gt;
&lt;br /&gt;
= Circle =&lt;br /&gt;
A good circle routine is always sure to impress users, especially those accustomed to BASIC. This does not use formulas of the nature you may be used to for drawing circles. As well, this routine uses some SMC, so if you are using this in an app, be aware that the subroutine PlotPixel will need to be in RAM (or at least part of it).&lt;br /&gt;
&lt;br /&gt;
    gBufLSB = 40h     ;different for the 83. Good for 83+/84+ and SE models.&lt;br /&gt;
    gBufMSB = 93h    ;different for the 83. Good for 83+/84+ and SE models.&lt;br /&gt;
    FastCircle:&lt;br /&gt;
    ;Inputs:&lt;br /&gt;
    ;     BC = (x,y)&lt;br /&gt;
    ;     H  = radius&lt;br /&gt;
    ;     A = method&lt;br /&gt;
    ;        0 = Pixel Off&lt;br /&gt;
    ;        1 = Pixel On&lt;br /&gt;
    ;        2 = Pixel Change&lt;br /&gt;
         ld de,$A62F     ;2F,A6 = &amp;#039;cpl \ and (hl)&amp;#039;&lt;br /&gt;
         dec a&lt;br /&gt;
         jr z,$+5&lt;br /&gt;
         ld de,$B600     ;00,B6 = &amp;#039;nop \ or (hl)&amp;#039;&lt;br /&gt;
         dec a&lt;br /&gt;
         jr z,$+5&lt;br /&gt;
         ld de,$AE00     ;00,B6 = &amp;#039;nop \ xor (hl)&amp;#039;&lt;br /&gt;
         ld (smc_PlotType)&lt;br /&gt;
         ld a,h&lt;br /&gt;
         or a&lt;br /&gt;
         ret z&lt;br /&gt;
         ret m&lt;br /&gt;
         di&lt;br /&gt;
         ld e,0&lt;br /&gt;
         ld d,a&lt;br /&gt;
         ld l,e&lt;br /&gt;
         add hl,hl&lt;br /&gt;
         jr Loop+4&lt;br /&gt;
    Loop:&lt;br /&gt;
         ex af,af&amp;#039;&lt;br /&gt;
         call Plot4Pix&lt;br /&gt;
         call Plot4Pix&lt;br /&gt;
         inc d&lt;br /&gt;
         sub l&lt;br /&gt;
         jr nc,YIsGood&lt;br /&gt;
    DecY:&lt;br /&gt;
         dec e&lt;br /&gt;
         inc l&lt;br /&gt;
         add a,h&lt;br /&gt;
    YIsGood:&lt;br /&gt;
         ex af,af&amp;#039;&lt;br /&gt;
         ld a,e&lt;br /&gt;
         sub d&lt;br /&gt;
         jp p,Loop&lt;br /&gt;
         ret&lt;br /&gt;
    &lt;br /&gt;
    Plot4Pix:&lt;br /&gt;
         inc l&lt;br /&gt;
         push af&lt;br /&gt;
         push hl&lt;br /&gt;
         push bc&lt;br /&gt;
         ld a,e&lt;br /&gt;
         ld e,d&lt;br /&gt;
         ld d,a&lt;br /&gt;
         push de&lt;br /&gt;
         add a,b&lt;br /&gt;
         cp 96&lt;br /&gt;
         call c,Plot2Pix       ;DC****&lt;br /&gt;
         ld a,b&lt;br /&gt;
         sub d&lt;br /&gt;
         call p,Plot2Pix&lt;br /&gt;
         pop de&lt;br /&gt;
         pop bc&lt;br /&gt;
         pop hl&lt;br /&gt;
         pop af&lt;br /&gt;
         ret&lt;br /&gt;
    plot2Pix:&lt;br /&gt;
         push bc&lt;br /&gt;
         ld b,a&lt;br /&gt;
         push bc&lt;br /&gt;
         ld a,c&lt;br /&gt;
         add a,e&lt;br /&gt;
         ld c,a&lt;br /&gt;
    ;c+e&lt;br /&gt;
         call p,PlotPixel&lt;br /&gt;
         pop bc&lt;br /&gt;
         ld a,c&lt;br /&gt;
         sub e&lt;br /&gt;
         ld c,a&lt;br /&gt;
    ;c-e&lt;br /&gt;
         call p,PlotPixel&lt;br /&gt;
         pop bc&lt;br /&gt;
         ret&lt;br /&gt;
    PlotPixel:&lt;br /&gt;
    ;Input:&lt;br /&gt;
    ;     b is X&lt;br /&gt;
    ;     c is y&lt;br /&gt;
    ;Output:&lt;br /&gt;
    ;     HL points to byte&lt;br /&gt;
           cp 64 \ ret nc&lt;br /&gt;
           ld a,b&lt;br /&gt;
           cp 96 \ ret nc&lt;br /&gt;
           ld l,c&lt;br /&gt;
           ld h,0&lt;br /&gt;
           ld b,h&lt;br /&gt;
           add hl,hl&lt;br /&gt;
           add hl,bc&lt;br /&gt;
           add hl,hl&lt;br /&gt;
           add hl,hl&lt;br /&gt;
           ld b,a&lt;br /&gt;
           rrca \ rrca \ rrca&lt;br /&gt;
           and 0Fh&lt;br /&gt;
           add a,gBufLSB&lt;br /&gt;
           ld c,a&lt;br /&gt;
           ld a,b&lt;br /&gt;
           ld b,gBufMSB&lt;br /&gt;
           add hl,bc&lt;br /&gt;
           and 7&lt;br /&gt;
           ld b,a&lt;br /&gt;
           inc b&lt;br /&gt;
           ld a,1&lt;br /&gt;
             rrca&lt;br /&gt;
             djnz $-1&lt;br /&gt;
    PixelType:&lt;br /&gt;
           nop&lt;br /&gt;
           or (hl)&lt;br /&gt;
           ld (hl),a&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Pixel Plotting =&lt;br /&gt;
Sometimes, we need to plot pixels. This routine returns a pointer to the byte the pixel is located on, as well as a mask for the pixel. If it is out of bounds, it returns the c flag reset, else it is set. To use this routine, you can do:&lt;br /&gt;
&lt;br /&gt;
    ;turn the pixel ON&lt;br /&gt;
         call GetPixelLoc&lt;br /&gt;
         ret nc&lt;br /&gt;
         or (hl)&lt;br /&gt;
         ld (hl),a&lt;br /&gt;
         ret&lt;br /&gt;
    ;turn the pixel OFF&lt;br /&gt;
         call GetPixelLoc&lt;br /&gt;
         ret nc&lt;br /&gt;
         cpl         ;invert the bits of A&lt;br /&gt;
         and (hl)&lt;br /&gt;
         ld (hl),a&lt;br /&gt;
         ret&lt;br /&gt;
    ;invert the pixel&lt;br /&gt;
         call GetPixelLoc&lt;br /&gt;
         ret nc&lt;br /&gt;
         xor (hl)&lt;br /&gt;
         ret&lt;br /&gt;
    ;test the pixel (nz=ON, z=Off)&lt;br /&gt;
         call GetPixelLoc&lt;br /&gt;
         ret nc            ;also happens to return nz if c is reset&lt;br /&gt;
         and (hl)&lt;br /&gt;
         ret&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    gBufLSB = 40h     ;different for the 83. Good for 83+/84+ and SE models.&lt;br /&gt;
    gBufMSB = 93h    ;different for the 83. Good for 83+/84+ and SE models.&lt;br /&gt;
    GetPixelLoc:&lt;br /&gt;
    ;Input:&lt;br /&gt;
    ;     b is X&lt;br /&gt;
    ;     c is y&lt;br /&gt;
    ;Output:&lt;br /&gt;
    ;     HL points to byte&lt;br /&gt;
    ;     A is the mask&lt;br /&gt;
    ;     nc if not computed, c if computed&lt;br /&gt;
           cp 64 \ ret nc&lt;br /&gt;
           ld a,b&lt;br /&gt;
           cp 96 \ ret nc&lt;br /&gt;
           ld l,c&lt;br /&gt;
           ld h,0&lt;br /&gt;
           ld b,h&lt;br /&gt;
           add hl,hl&lt;br /&gt;
           add hl,bc&lt;br /&gt;
           add hl,hl&lt;br /&gt;
           add hl,hl&lt;br /&gt;
           ld b,a&lt;br /&gt;
           rrca \ rrca \ rrca&lt;br /&gt;
           and 0Fh&lt;br /&gt;
           add a,gBufLSB&lt;br /&gt;
           ld c,a&lt;br /&gt;
           ld a,b&lt;br /&gt;
           ld b,gBufMSB&lt;br /&gt;
           add hl,bc&lt;br /&gt;
           and 7&lt;br /&gt;
           ld b,a&lt;br /&gt;
           ld a,1&lt;br /&gt;
           inc b&lt;br /&gt;
           rrca&lt;br /&gt;
           djnz $-1&lt;br /&gt;
           scf&lt;br /&gt;
           ret&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Rectangle =&lt;br /&gt;
The OS routines automatically update the LCD area that it is drawn to, which can be a pain to assembly programmers. It also slows it down. Here is a quick routine, riddled with SMCL&lt;br /&gt;
&lt;br /&gt;
    gBufLSB = 40h     ;different for the 83. Good for 83+/84+ and SE models.&lt;br /&gt;
    gBufMSB = 93h    ;different for the 83. Good for 83+/84+ and SE models.&lt;br /&gt;
    RectData   equ OP1&lt;br /&gt;
    Rectangle:&lt;br /&gt;
    ;Inputs:&lt;br /&gt;
    ;     D = x&lt;br /&gt;
    ;     E = y&lt;br /&gt;
    ;     B = height&lt;br /&gt;
    ;     C = width&lt;br /&gt;
    ;     A = Method&lt;br /&gt;
    ;         0=Erase&lt;br /&gt;
    ;         1=OR&lt;br /&gt;
    ;         2=XOR&lt;br /&gt;
         dec a&lt;br /&gt;
         jr nz,$+6&lt;br /&gt;
           ld a,$B6   ;B6 = &amp;#039;or (hl)&amp;#039;&lt;br /&gt;
           jr RectPattern&lt;br /&gt;
         dec a&lt;br /&gt;
         jr nz,$+6&lt;br /&gt;
           ld a,$AE   ;AE = &amp;#039;xor (hl)&amp;#039;&lt;br /&gt;
           jr RectPattern&lt;br /&gt;
         ld a,-1&lt;br /&gt;
         ld (smc_Erase0),a&lt;br /&gt;
         ld a,2Fh     ;2F = CPL&lt;br /&gt;
         ld (smc_Erase1),a&lt;br /&gt;
         ld a,0Ch     ;0C = INC C&lt;br /&gt;
         ld (smc_Erase2),a&lt;br /&gt;
         ld a,$A6     ;A6 = &amp;#039;and (hl)&amp;#039;&lt;br /&gt;
    RectPattern:&lt;br /&gt;
         ld (smc_Logic),a&lt;br /&gt;
         push bc&lt;br /&gt;
         push de&lt;br /&gt;
         push bc&lt;br /&gt;
    ; First I want to clear the RectData buffer&lt;br /&gt;
         ld hl,RectData&lt;br /&gt;
         .db 1&lt;br /&gt;
    smc_Erase0:&lt;br /&gt;
         .dw 0C00h      ;changed to 0CFF for Erase&lt;br /&gt;
           ld (hl),c&lt;br /&gt;
           inc l&lt;br /&gt;
           djnz $-2&lt;br /&gt;
    &lt;br /&gt;
         ld l,78h&lt;br /&gt;
    ; Now I want to make the rectangle pattern.&lt;br /&gt;
    ; Since D is the x coordinate, we need to find which bit&lt;br /&gt;
    ; the edge starts on and set each bit after that as well.&lt;br /&gt;
         ld a,d&lt;br /&gt;
         and 7&lt;br /&gt;
         ld b,a&lt;br /&gt;
         ld a,80h&lt;br /&gt;
         jr z,$+5&lt;br /&gt;
           rrca&lt;br /&gt;
           djnz $-1&lt;br /&gt;
         add a,a&lt;br /&gt;
         dec a&lt;br /&gt;
    smc_Erase1:&lt;br /&gt;
         nop&lt;br /&gt;
         ld (hl),a&lt;br /&gt;
    ; Now we need to fill in the rest of the buffer&lt;br /&gt;
    ; First we need to know how many bits need to be set &lt;br /&gt;
         ex (sp),hl&lt;br /&gt;
         ld a,d&lt;br /&gt;
         neg&lt;br /&gt;
         and 7&lt;br /&gt;
         ld b,a&lt;br /&gt;
         ld a,l&lt;br /&gt;
    smc_Erase2:&lt;br /&gt;
         dec c      ;changed to &amp;#039;inc c&amp;#039; for Erase&lt;br /&gt;
         sub b&lt;br /&gt;
         ex (sp),hl&lt;br /&gt;
         jr z,PatternFinished&lt;br /&gt;
         jr c,LoadLastByte&lt;br /&gt;
         inc l&lt;br /&gt;
         ld (hl),c&lt;br /&gt;
         sub 8&lt;br /&gt;
         jr nc,$-4&lt;br /&gt;
    LoadLastByte:&lt;br /&gt;
         and 7&lt;br /&gt;
         ld b,a&lt;br /&gt;
         ld a,80h&lt;br /&gt;
         jr z,$+5&lt;br /&gt;
           rrca&lt;br /&gt;
           djnz $-1&lt;br /&gt;
         add a,a&lt;br /&gt;
         dec a&lt;br /&gt;
         xor (hl)&lt;br /&gt;
    smc_Erase3:&lt;br /&gt;
         nop&lt;br /&gt;
         ld (hl),a&lt;br /&gt;
    PatternFinished:&lt;br /&gt;
    ; At this point, the whole pattern is filled in. Bien.&lt;br /&gt;
    ; Now we can worry about where to start drawing the pattern.&lt;br /&gt;
    ; For this, we use DE for (x,y)&lt;br /&gt;
    ; B is 0, so I cheat.&lt;br /&gt;
         ld a,d&lt;br /&gt;
         ld d,b&lt;br /&gt;
         ld h,d&lt;br /&gt;
         ld l,e&lt;br /&gt;
         add hl,hl&lt;br /&gt;
         add hl,de&lt;br /&gt;
         add hl,hl&lt;br /&gt;
         add hl,hl&lt;br /&gt;
         and %11111000&lt;br /&gt;
         rrca \ rrca \ rrca&lt;br /&gt;
         add a,gBufLSB         ;plotSScreen = 9340h&lt;br /&gt;
         ld e,a&lt;br /&gt;
         ld d,gBufMSB&lt;br /&gt;
         add hl,de&lt;br /&gt;
    &lt;br /&gt;
    ; Now HL points to where to draw in the graph buffer.&lt;br /&gt;
    ; All we do now is treat the pattern as a 12-byte wide sprite with&lt;br /&gt;
    ; the same bytes repeated over and over.&lt;br /&gt;
         pop bc     ;b is the height.&lt;br /&gt;
         ld d,84h   ;RectData = 8478h&lt;br /&gt;
    DrawLoopStart:&lt;br /&gt;
         ld e,78h&lt;br /&gt;
         ld c,12&lt;br /&gt;
    DrawLoop:&lt;br /&gt;
         ld a,(de)&lt;br /&gt;
    smc_Logic:&lt;br /&gt;
         or (hl)&lt;br /&gt;
         ld (hl),a&lt;br /&gt;
         inc hl&lt;br /&gt;
         inc e&lt;br /&gt;
         dec c&lt;br /&gt;
         jr nz,DrawLoop&lt;br /&gt;
         djnz DrawLoopStart&lt;br /&gt;
         pop de&lt;br /&gt;
         pop bc&lt;br /&gt;
         ret&lt;br /&gt;
&lt;br /&gt;
= Grayscale LCD Update =&lt;br /&gt;
This code uses SMC, so it should be used in RAM. However, the SMC used can easily be removed for use in an app. This requires the use of a minor buffer and major buffer and it can use different levels of saturation from each buffer by changing GrayMask. This is designed to handle 2,3, or 4 levels of grayscale and each of the grayscale bit masks is meant to display each pixel for a certain percentage of the frames displayed. This routine must be continuously called to update the LCD in order for it to appear gray.&lt;br /&gt;
&lt;br /&gt;
    ;===============================================================&lt;br /&gt;
    BufferToLCD:&lt;br /&gt;
    ;===============================================================&lt;br /&gt;
    ;Inputs:&lt;br /&gt;
    ;     MajBuf points to the main buffer to use. Probably plotSScreen.&lt;br /&gt;
    ;     MinBuf points to the backgfor round buffer to use. Probably saveSScreen.&lt;br /&gt;
    ;     GrayMask holds the gray mask. Zero takes from the major,&lt;br /&gt;
    ;       buffer, one takes from the minor buffer.&lt;br /&gt;
    ;Outputs:&lt;br /&gt;
    ;&lt;br /&gt;
    ;Defines:&lt;br /&gt;
    &lt;br /&gt;
    GrayMask50    equ %1010101010101010   ;1   1/2   1/2   0&lt;br /&gt;
    GrayMask67    equ %1001001001001001   ;1   2/3   1/3   0&lt;br /&gt;
    GrayMask75    equ %1000100010001000   ;1   3/4   1/4   0&lt;br /&gt;
    GrayMask83    equ %1000001000001000   ;1   5/6   1/6   0&lt;br /&gt;
    GrayMask92    equ %1000000000001000   ;1  11/12  1/12  0&lt;br /&gt;
    Graymask100   equ -1&lt;br /&gt;
    GrayMask0     equ 0&lt;br /&gt;
    GrayMask8     equ 1-GrayMaks92&lt;br /&gt;
    GrayMask17    equ 1-GrayMask83&lt;br /&gt;
    GrayMask25    equ 1-GrayMask75&lt;br /&gt;
    GrayMask33    equ 1-GrayMask67&lt;br /&gt;
    #define     LCDDelay()    in a,(16) \ rlca \ jr c,$-3&lt;br /&gt;
    ;===============================================================&lt;br /&gt;
         .db 21h           ;ld hl,**&lt;br /&gt;
    GrayMask:&lt;br /&gt;
         .dw GrayMask50        ;replace with whatever you like&lt;br /&gt;
         add hl,hl&lt;br /&gt;
         jr nc,$+4&lt;br /&gt;
           set 4,l&lt;br /&gt;
         ld (GrayMask),hl&lt;br /&gt;
         ld hl,MajBuf&lt;br /&gt;
         ld ix,MinBuf&lt;br /&gt;
    setrow:&lt;br /&gt;
         LCDDelay()&lt;br /&gt;
         ld a,$80&lt;br /&gt;
         out (16),a&lt;br /&gt;
         ld de,12&lt;br /&gt;
         ld a,$20&lt;br /&gt;
    col:&lt;br /&gt;
         ld (smc_Var2),a&lt;br /&gt;
         LCDDelay()&lt;br /&gt;
         ld a,(smc_Var2)&lt;br /&gt;
         out (10h),a&lt;br /&gt;
         ld b,64&lt;br /&gt;
    row:&lt;br /&gt;
         ld (smc_Var1),hl       ;16&lt;br /&gt;
         .db 21h            ;10        ld hl,**&lt;br /&gt;
    GrayMask:               ;  &lt;br /&gt;
         .dw GrayMask50     ;--&lt;br /&gt;
         add hl,hl          ;11&lt;br /&gt;
         jr nc,$+4          ;12|15&lt;br /&gt;
           set 4,l          ;--&lt;br /&gt;
         ld (GrayMask),hl   ;16&lt;br /&gt;
         ld a,(ix)          ;19&lt;br /&gt;
         .db 21h            ;10        ld hl,**&lt;br /&gt;
    smc_Var1:                   ;  &lt;br /&gt;
         .dw 0              ;--&lt;br /&gt;
         xor (hl)           ;7&lt;br /&gt;
         and c              ;4&lt;br /&gt;
         xor (hl)           ;7&lt;br /&gt;
         add hl,de          ;11&lt;br /&gt;
         add ix,de          ;15&lt;br /&gt;
    &lt;br /&gt;
    ;========================================================&lt;br /&gt;
    ;Not needed because the stuff before takes &amp;gt;130 cycles&lt;br /&gt;
    ;        push af&lt;br /&gt;
    ;        LCDDelay()&lt;br /&gt;
    ;        pop af&lt;br /&gt;
    ;========================================================&lt;br /&gt;
         out ($11),a&lt;br /&gt;
         djnz row&lt;br /&gt;
         .db 3Eh             ;ld a,*&lt;br /&gt;
    smc_Var2:&lt;br /&gt;
         .db 20h&lt;br /&gt;
         inc a&lt;br /&gt;
         dec h&lt;br /&gt;
         dec h&lt;br /&gt;
         dec h&lt;br /&gt;
         inc hl&lt;br /&gt;
         dec ixh&lt;br /&gt;
         dec ixh&lt;br /&gt;
         dec ixh&lt;br /&gt;
         inc ix&lt;br /&gt;
         cp $2c&lt;br /&gt;
         jp nz,col&lt;br /&gt;
         ret&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{lowercase}}&lt;br /&gt;
[[Category:Z80 Assembly]]&lt;br /&gt;
[[Category:Z80 Heaven]]&lt;/div&gt;</summary>
		<author><name>KermMartian</name></author>
	</entry>
</feed>