Drawing

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'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.

Circle

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).

gBufLSB = 40h     ;different for the 83. Good for 83+/84+ and SE models.
gBufMSB = 93h    ;different for the 83. Good for 83+/84+ and SE models.
FastCircle:
;Inputs:
;     BC = (x,y)
;     H  = radius
;     A = method
;        0 = Pixel Off
;        1 = Pixel On
;        2 = Pixel Change
     ld de,$A62F     ;2F,A6 = 'cpl \ and (hl)'
     dec a
     jr z,$+5
     ld de,$B600     ;00,B6 = 'nop \ or (hl)'
     dec a
     jr z,$+5
     ld de,$AE00     ;00,B6 = 'nop \ xor (hl)'
     ld (smc_PlotType)
     ld a,h
     or a
     ret z
     ret m
     di
     ld e,0
     ld d,a
     ld l,e
     add hl,hl
     jr Loop+4
Loop:
     ex af,af'
     call Plot4Pix
     call Plot4Pix
     inc d
     sub l
     jr nc,YIsGood
DecY:
     dec e
     inc l
     add a,h
YIsGood:
     ex af,af'
     ld a,e
     sub d
     jp p,Loop
     ret

Plot4Pix:
     inc l
     push af
     push hl
     push bc
     ld a,e
     ld e,d
     ld d,a
     push de
     add a,b
     cp 96
     call c,Plot2Pix       ;DC****
     ld a,b
     sub d
     call p,Plot2Pix
     pop de
     pop bc
     pop hl
     pop af
     ret
plot2Pix:
     push bc
     ld b,a
     push bc
     ld a,c
     add a,e
     ld c,a
;c+e
     call p,PlotPixel
     pop bc
     ld a,c
     sub e
     ld c,a
;c-e
     call p,PlotPixel
     pop bc
     ret
PlotPixel:
;Input:
;     b is X
;     c is y
;Output:
;     HL points to byte
       cp 64 \ ret nc
       ld a,b
       cp 96 \ ret nc
       ld l,c
       ld h,0
       ld b,h
       add hl,hl
       add hl,bc
       add hl,hl
       add hl,hl
       ld b,a
       rrca \ rrca \ rrca
       and 0Fh
       add a,gBufLSB
       ld c,a
       ld a,b
       ld b,gBufMSB
       add hl,bc
       and 7
       ld b,a
       inc b
       ld a,1
         rrca
         djnz $-1
PixelType:
       nop
       or (hl)
       ld (hl),a
       ret

Pixel Plotting

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:

;turn the pixel ON
     call GetPixelLoc
     ret nc
     or (hl)
     ld (hl),a
     ret
;turn the pixel OFF
     call GetPixelLoc
     ret nc
     cpl         ;invert the bits of A
     and (hl)
     ld (hl),a
     ret
;invert the pixel
     call GetPixelLoc
     ret nc
     xor (hl)
     ret
;test the pixel (nz=ON, z=Off)
     call GetPixelLoc
     ret nc            ;also happens to return nz if c is reset
     and (hl)
     ret
gBufLSB = 40h     ;different for the 83. Good for 83+/84+ and SE models.
gBufMSB = 93h    ;different for the 83. Good for 83+/84+ and SE models.
GetPixelLoc:
;Input:
;     b is X
;     c is y
;Output:
;     HL points to byte
;     A is the mask
;     nc if not computed, c if computed
       cp 64 \ ret nc
       ld a,b
       cp 96 \ ret nc
       ld l,c
       ld h,0
       ld b,h
       add hl,hl
       add hl,bc
       add hl,hl
       add hl,hl
       ld b,a
       rrca \ rrca \ rrca
       and 0Fh
       add a,gBufLSB
       ld c,a
       ld a,b
       ld b,gBufMSB
       add hl,bc
       and 7
       ld b,a
       ld a,1
       inc b
       rrca
       djnz $-1
       scf
       ret

Rectangle

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

gBufLSB = 40h     ;different for the 83. Good for 83+/84+ and SE models.
gBufMSB = 93h    ;different for the 83. Good for 83+/84+ and SE models.
RectData   equ OP1
Rectangle:
;Inputs:
;     D = x
;     E = y
;     B = height
;     C = width
;     A = Method
;         0=Erase
;         1=OR
;         2=XOR
     dec a
     jr nz,$+6
       ld a,$B6   ;B6 = 'or (hl)'
       jr RectPattern
     dec a
     jr nz,$+6
       ld a,$AE   ;AE = 'xor (hl)'
       jr RectPattern
     ld a,-1
     ld (smc_Erase0),a
     ld a,2Fh     ;2F = CPL
     ld (smc_Erase1),a
     ld a,0Ch     ;0C = INC C
     ld (smc_Erase2),a
     ld a,$A6     ;A6 = 'and (hl)'
RectPattern:
     ld (smc_Logic),a
     push bc
     push de
     push bc
; First I want to clear the RectData buffer
     ld hl,RectData
     .db 1
smc_Erase0:
     .dw 0C00h      ;changed to 0CFF for Erase
       ld (hl),c
       inc l
       djnz $-2

     ld l,78h
; Now I want to make the rectangle pattern.
; Since D is the x coordinate, we need to find which bit
; the edge starts on and set each bit after that as well.
     ld a,d
     and 7
     ld b,a
     ld a,80h
     jr z,$+5
       rrca
       djnz $-1
     add a,a
     dec a
smc_Erase1:
     nop
     ld (hl),a
; Now we need to fill in the rest of the buffer
; First we need to know how many bits need to be set 
     ex (sp),hl
     ld a,d
     neg
     and 7
     ld b,a
     ld a,l
smc_Erase2:
     dec c      ;changed to 'inc c' for Erase
     sub b
     ex (sp),hl
     jr z,PatternFinished
     jr c,LoadLastByte
     inc l
     ld (hl),c
     sub 8
     jr nc,$-4
LoadLastByte:
     and 7
     ld b,a
     ld a,80h
     jr z,$+5
       rrca
       djnz $-1
     add a,a
     dec a
     xor (hl)
smc_Erase3:
     nop
     ld (hl),a
PatternFinished:
; At this point, the whole pattern is filled in. Bien.
; Now we can worry about where to start drawing the pattern.
; For this, we use DE for (x,y)
; B is 0, so I cheat.
     ld a,d
     ld d,b
     ld h,d
     ld l,e
     add hl,hl
     add hl,de
     add hl,hl
     add hl,hl
     and %11111000
     rrca \ rrca \ rrca
     add a,gBufLSB         ;plotSScreen = 9340h
     ld e,a
     ld d,gBufMSB
     add hl,de

; Now HL points to where to draw in the graph buffer.
; All we do now is treat the pattern as a 12-byte wide sprite with
; the same bytes repeated over and over.
     pop bc     ;b is the height.
     ld d,84h   ;RectData = 8478h
DrawLoopStart:
     ld e,78h
     ld c,12
DrawLoop:
     ld a,(de)
smc_Logic:
     or (hl)
     ld (hl),a
     inc hl
     inc e
     dec c
     jr nz,DrawLoop
     djnz DrawLoopStart
     pop de
     pop bc
     ret

Grayscale LCD Update

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.

;===============================================================
BufferToLCD:
;===============================================================
;Inputs:
;     MajBuf points to the main buffer to use. Probably plotSScreen.
;     MinBuf points to the backgfor round buffer to use. Probably saveSScreen.
;     GrayMask holds the gray mask. Zero takes from the major,
;       buffer, one takes from the minor buffer.
;Outputs:
;
;Defines:

GrayMask50    equ %1010101010101010   ;1   1/2   1/2   0
GrayMask67    equ %1001001001001001   ;1   2/3   1/3   0
GrayMask75    equ %1000100010001000   ;1   3/4   1/4   0
GrayMask83    equ %1000001000001000   ;1   5/6   1/6   0
GrayMask92    equ %1000000000001000   ;1  11/12  1/12  0
Graymask100   equ -1
GrayMask0     equ 0
GrayMask8     equ 1-GrayMaks92
GrayMask17    equ 1-GrayMask83
GrayMask25    equ 1-GrayMask75
GrayMask33    equ 1-GrayMask67
#define     LCDDelay()    in a,(16) \ rlca \ jr c,$-3
;===============================================================
     .db 21h           ;ld hl,**
GrayMask:
     .dw GrayMask50        ;replace with whatever you like
     add hl,hl
     jr nc,$+4
       set 4,l
     ld (GrayMask),hl
     ld hl,MajBuf
     ld ix,MinBuf
setrow:
     LCDDelay()
     ld a,$80
     out (16),a
     ld de,12
     ld a,$20
col:
     ld (smc_Var2),a
     LCDDelay()
     ld a,(smc_Var2)
     out (10h),a
     ld b,64
row:
     ld (smc_Var1),hl       ;16
     .db 21h            ;10        ld hl,**
GrayMask:               ;  
     .dw GrayMask50     ;--
     add hl,hl          ;11
     jr nc,$+4          ;12|15
       set 4,l          ;--
     ld (GrayMask),hl   ;16
     ld a,(ix)          ;19
     .db 21h            ;10        ld hl,**
smc_Var1:                   ;  
     .dw 0              ;--
     xor (hl)           ;7
     and c              ;4
     xor (hl)           ;7
     add hl,de          ;11
     add ix,de          ;15

;========================================================
;Not needed because the stuff before takes >130 cycles
;        push af
;        LCDDelay()
;        pop af
;========================================================
     out ($11),a
     djnz row
     .db 3Eh             ;ld a,*
smc_Var2:
     .db 20h
     inc a
     dec h
     dec h
     dec h
     inc hl
     dec ixh
     dec ixh
     dec ixh
     inc ix
     cp $2c
     jp nz,col
     ret
Unless otherwise stated, the content of this page is licensed under GNU Free Documentation License.