ZX81 Assembly Listing for cgfxdd.asm


ZX81 assembly listing for **CHUNKYGFX**SLR/2022*

**CHUNKYGFX**SLR/2022* (cgfxdd.asm)

My attempt at a chunky graphics routine for the ZX81 that allows three colors: white, gray and black. This program demonstrates its use.


ASSEMBLY PROGRAM LISTING

;
; Chunky GFX - Draw Demo
; Steven Reid (c) 2022
; v1 11/24/2022 - initial build
; v2 12/04/2022 - imported final routine and cleanup
;

; +++
;
; Header and startup
;

        ; start up stuff
        ORG 16514               ; stored in REM at top (ZX81)
        jr start                ; needed for z80asm

; title and copyright (will show when listed)
copy:
        DB _as,_c_,_h_,_u_,_n_,_k_,_y_,_g_,_f_,_x_
        DB _as,_as,_s_,_l_,_r_,_sl,_2_,_0_,_2_,_2_
        DB _as,$76,$76  ; *CHUNKYGFX*SLR/2022*

start:
        call slow               ; SLOW is required.
        call cls                ; clear screen / exapnd screen

; end header and startup
;
; ---

; +++
;
; Main Program
;

        ; chukygfx max width / height
        max_width:      EQU 31
        half_width:     EQU 15  ; jszeddy doesn't like uneven division
        max_height:     EQU 47
        half_height:    EQU 23  ; jszeddy doesn't like uneven division

        ; set pen color
        ld a,1  ; hash
        ld (pen_color),a

        ; draw top left most point
        ld bc,0*256+1
        call cplot

        ; draw bottom right most point
        ld bc,max_width*256+max_height
        call cplot

        ; draw a circle (well, oval)
        ld bc,half_width*256+half_height
        ld a,13         ; radius
        call draw_circle

        ; set pen color
        ld a,2  ; black
        ld (pen_color),a

        ; draw top right most point
        ld bc,max_width*256+1
        call cplot

        ; darw bottom left most point
        ld bc,0*256+max_height
        call cplot

        ; draw a circle (well, oval)
        ld bc,half_width*256+half_height
        ld a,5          ; radius
        call draw_circle

        ; draw horizontal line over center
        ld bc,0+(half_width-15)*256+(half_height)
        ld de,0+(half_width+16)*256+(half_height)
        call draw_line

        ; draw vertical line over center
        ld bc,0+(half_width)*256+(half_height-20)
        ld de,0+(half_width)*256+(half_height+20)
        call draw_line

        ; set pen color
        ld a,2  ; black
        ld (pen_color),a

        ; draw center point
        ld bc,0+(half_width)*256+(half_height)
        call cplot

        ; this loop draws a series of lines as they spin
draw_loop:

        ; rotate pen color
        ld a,(pen_color)        ; grab current value
        inc a                   ; add 1
        cp 3                    ; is it 3?
        jr nz,savepc            ; if no, jump to save it
        xor a                   ; otherwise set pen color to 0
savepc:  
        ld (pen_color),a

        ; line spin
        ld bc,0+(half_width-12)*256+(half_height)
        ld de,0+(half_width+12)*256+(half_height)
        ld (y1_var),bc
        ld (x_var),de

        ld b,4
line_loop:
        push bc

        ld bc,(y1_var)
        ld de,(x_var)
        call draw_line

        ld h,3
        ld a,(x1_var)
        add a,h
        ld b,a
        ld a,(y1_var)
        add a,h
        ld c,a
        ld (y1_var),bc

        ld h,256-3
        ld a,(y_var)
        add a,h
        ld d,a
        ld a,(x_var)
        add a,h
        ld e,a
        ld (x_var),de

        pop bc
        djnz line_loop        ; not done yet!

        ld b,4
line_loop2:
        push bc

        ld bc,(y1_var)
        ld de,(x_var)
        call draw_line

        ld h,3
        ld a,(x1_var)
        add a,h
        ld b,a
        ld a,(y1_var)
        ld h,256-3
        add a,h
        ld c,a
        ld (y1_var),bc

        ld a,(y_var)
        add a,h
        ld d,a
        ld a,(x_var)
        ld h,3
        add a,h
        ld e,a
        ld (x_var),de

        pop bc
        djnz line_loop2        ; not done yet!

        ld hl,$0999
        call delay

        jp draw_loop

; End of loop!
;


; +++
;
; Routines
;

; +++
; Chunky Plot
;
;       This is a 3 color version of plot that uses
;       white, black and hash (gray).
;
;       set pen_color to 0 (white), 1 (gray), or 2 (black)
;       set bc register to x/y coordinates (preserved)
;       all other registers are destroied
;

;variables
pen_color:      DB 0

; Primary entry point - preserves BC
cplot:

        push bc                 ; save plot points
        call chunky_plot        ; call plot routine
        pop bc                  ; restore plot points

        ret             ; and done!

; Secondary entry point - doesn't preserve BC
chunky_plot:
        ; make sure not out of bounds
        ld a,b          ; ld a with x
        bit 7,a
        ret nz          ; out of bounds
        sub 32
        ret p           ; out of bounds

        ld a,c          ; ld a with y
        bit 7,a
        ret nz          ; out of bounds
        sub 48
        ret p           ; out of bounds

        ; get character on screen
        ld hl,(d_file)
        inc hl
        ld a,c          ; load a wity y
        sra a           ; divide by 2
        or a            ; cp 0
        jr z,cp_findx   ; already at y,skip
        ld de,33

cp_findy:
        add hl,de
        dec a
        jr nz, cp_findy

cp_findx:
        ld d,0
        ld e,b          ; load e with x
        add hl,de

        ld a,(hl)       ; grab charater
        push hl         ; save screen location

        ; find char and convert to 4 bit normlized format
        ld hl,cp_char_table+10          ; start at end of table
        ld b,10                         ; walk backwards
cp_find_new_char_loop:
        cp (hl)                         ; did we match?
        jr z,cp_get_top_or_bottom       ; yes, skip ahead!
        dec hl                          ; try next character
        djnz cp_find_new_char_loop      ; no? Keep searching

cp_get_top_or_bottom:
        ld a,(pen_color)
        ld d,a                          ; grab and set pen color

        ; d = pen color
        ; b = normalized char
        ; c = y coordinate

        ld a,c          ; get y coord
        rrca            ; check if even or odd
        jp nc,cp_is_even

cp_is_odd:
        ld a,%00001100  ; mask out bottom
        and b
        add a,d         ; add in pen color
        jp cp_print_char

cp_is_even:
        ld a,%00000011  ; mask out top
        and b
        sla d           ; shift pen color to top
        sla d
        add a,d         ; add in pen color

cp_print_char:
        ; a = new character
        ld l,a          ; extend to 16 bits
        ld h,0
        ld bc,cp_char_table
        add hl,bc       ; get destination
        ld a,(hl)       ; and grab new character

        pop hl          ; restore screen location
        ld (hl),a       ; load new character to screen

        ret             ; and done!

        ; Here is the conversion table to the characters:
cp_char_table:
        DB %00000000 ;  0 - 00000000 - white/white      00
        DB %00001001 ;  1 - 00000001 - white/hash       09
        DB %10000011 ;  2 - 00000010 - white/black      83
        DB %11111111 ;  3 - 00000011 - not used
        DB %00001010 ;  4 - 00000100 - hash/white       0A
cp_hash_hash_char:      ; label so I can change this
        DB %00001000 ;  5 - 00000101 - hash/hash        08
        DB %10001010 ;  6 - 00000110 - hash/black       8A (inverted hash)
        DB %11111111 ;  7 - 00000111 - not used
        DB %00000011 ;  8 - 00001000 - black/white      03
        DB %10001001 ;  9 - 00001001 - black/hash       89 (inverted hash)
        DB %10000000 ; 10 - 00001010 - black/black      80

; end chunky plot
; ---

; +++
; Other Math Routines
;

absA:           ; get absolute value of a
        or a
        ret p
        neg
        ret

sgnA:           ; get sign of A (-1, 0 or 1)
        or a    ; is a 0?
        jp z,sgnA_zero
        jp p,sgnA_plus
sgnA_minus:
        xor a
        dec a
        ret
sgnA_zero:
        xor a
        ret
sgnA_plus:
        xor a
        inc a
        ret

; end math routines
; ---

; +++
; Draw_Line
;

; variables
u_var: DB 0
v_var: DB 0
b_var: DB 0
a_var: DB 0
d_var: DB 0
c_var: DB 0
d1x_var: DB 0
d1y_var: DB 0
d2y_var: DB 0
d2x_var: DB 0
m_var: DB 0
n_var: DB 0
s_var: DB 0

draw_line:
        ; save a, b
        ld hl,b_var
        ld (hl),c
        inc hl
        ld (hl),b
        ; save c, d
        ld hl,d_var
        ld (hl),e
        inc hl
        ld (hl),d

; from page 121 of the Sinclair Basic book.
; this looks rather brute force, but I don't much mind as it is
; simple math. Curious how fast this runs.
;1000 LET U=C-A
        ld a,(a_var)
        ld b,a
        ld a,(c_var)
        sub b           ; a-b = c-a
        ld (u_var),a
;1005 REM U SHOWS HOW MANY STEPS ALONG WE NEED TO GO
;1010 LET V=D-B
        ld a,(b_var)
        ld b,a
        ld a,(d_var)
        sub b           ; a-b = d-b
        ld (v_var),a
;1015 REM V SHOWS HOW MANY STEPS UP
;1020 LET D1X=SGN U
        ld a,(u_var)
        call sgnA
        ld (d1x_var),a
;1030 LET D1Y=SGN V
        ld a,(v_var)
        call sgnA
        ld (d1y_var),a
;1035 REM (D1X,D1Y) IS A SINGLE STEP IN A DIAGONAL DIRECTION
;1040 LET D2X=SGN U
        ld a,(u_var)
        call sgnA
        ld (d2x_var),a
;1050 LET D2Y=0
        xor a
        ld (d2y_var),a
;1055 REM (D2X,D2Y) IS A SINGLE STEP LEFT OR RIGHT
;1060 LET M=ABS U
        ld a,(u_var)
        call absA
        ld (m_var),a
        ld b,a          ; b = m
;1070 LET N=ABS V
        ld a,(v_var)
        call absA
        ld (n_var),a    ; a = n
;1080 IF M>N THEN GOTO 1130
        cp b            ; n<m?
        jr c,line_1130
;1090 LET D2X=0
        xor a
        ld (d2x_var),a
;1100 LET D2Y=SGN V
        ld a,(v_var)
        call sgnA
        ld (d2y_var),a
;1105 REM NOW (D2X,D2Y) IS A SINGLE STEP UP OR DOWN
;1110 LET M=ABS V
        ld a,(v_var)
        call absA
        ld (m_var),a
;1120 LET N=ABS U
        ld a,(u_var)
        call absA
        ld (n_var),a
line_1130:
;1130 REM M IS THE LARGER OF ABS U & ABS V, N IS THE SMALLER
;1140 LET S=INT (M/2)
        ld a,(m_var)
        srl a
        ld (s_var),a
;1145 REM WE WANT TO MOVE FROM (A,B) TO (C,D) IN M STEPS USING N UP- DOWN OR RIGHT-LEFT STEPS D2, & M-N DIAGONAL STEPS D1, DISTRIBUTED AS EVENLY AS POSSIBLE
;1150 FOR I=0 TO M
        ld a,(m_var)
        ld b,a
i_loop:
        push bc
;1160 PLOT A,B
        ld bc,(b_var)   ; x1 in h, y1 in l
        call cplot
;        ld hl,300
;        call newpause
;1170 LET S=S+N
        ld hl,s_var
        ld a,(n_var)
        add a,(hl)
        ld (hl),a
;1180 IF S<M THEN GOTO 1230
        ld a,(m_var)
        ld b,a
        ld a,(hl)
        cp b            ; compare s to m
        jr c,line_1230
;1190 LET S=S-M
        ld hl,s_var
        ld a,(m_var)
        ld b,a
        ld a,(hl)
        sub b
        ld (hl),a
;1200 LET A=A+D1X
        ld hl,a_var
        ld a,(d1x_var)
        add a,(hl)
        ld (hl),a
;1210 LET B=B+D1Y
        ld hl,b_var
        ld a,(d1y_var)
        add a,(hl)
        ld (hl),a
;1215 REM A DIAGONAL STEP
;1220 GOTO 1250
        jr line_1250
line_1230:
;1230 LET A=A+D2X
        ld hl,a_var
        ld a,(d2x_var)
        add a,(hl)
        ld (hl),a
;1240 LET B=B+D2Y
        ld hl,b_var
        ld a,(d2y_var)
        add a,(hl)
        ld (hl),a
;1245 REM AN UP-DOWN OR RIGHT-LEFT STEP
;1250 NEXT I
line_1250:
        pop bc
        djnz i_loop
;1260 RETURN
        ret

; end draw line
;---

; +++
; Draw Circle
;       moving the circle into a routine
;       paramters:
;               bc = x,y coords (center)
;               a = radius
;       variables:
;               x, y
;               x1, y1
;               p
;               py
;               pxy

;variables
x_var:          DB 0
y_var:          DB 0

p_var:          DB 0
y1_var:         DB 0
x1_var:         DB 0

draw_circle:
        ; save x, y
        ld hl,x_var
        ld (hl),b
        inc hl
        ld (hl),c

        ; set p and y1 to zero
        ld (x1_var),a   ; set x1 to r
        xor a
        ld (p_var),a    ; set p to 0
        ld (y1_var),a   ; set y1 to 0

plot_loop:
        ; Plot the coordinates in each sector
        ; X+X1,Y+Y1
        ld hl,(y1_var)   ; x1 in h, y1 in l

        ld a,(x_var)
        add a,h         ; x+x1
        ld b,a

        ld a,(y_var)
        add a,l         ; y+y1
        ld c,a

        call cplot

        ; X-X1,Y+Y1
        ld hl,(y1_var)   ; x1 in h, y1 in l

        ld a,(x_var)
        sub h         ; x+x1
        ld b,a

        ld a,(y_var)
        add a,l         ; y+y1
        ld c,a

        call cplot

        ; X+X1,Y-Y1
        ld hl,(y1_var)   ; x1 in h, y1 in l

        ld a,(x_var)
        add a,h         ; x+x1
        ld b,a

        ld a,(y_var)
        sub l         ; y-y1
        ld c,a

        call cplot

        ; X-X1,Y-Y1
        ld hl,(y1_var)   ; x1 in h, y1 in l

        ld a,(x_var)
        sub h         ; x-x1
        ld b,a

        ld a,(y_var)
        sub l         ; y-y1
        ld c,a

        call cplot

        ; X+Y1,Y+X1
        ld hl,(y1_var)   ; x1 in h, y1 in l

        ld a,(x_var)
        add a,l         ; x+y1
        ld b,a

        ld a,(y_var)
        add a,h         ; y+x1
        ld c,a

        call cplot

        ; X-Y1,Y+X1
        ld hl,(y1_var)   ; x1 in h, y1 in l

        ld a,(x_var)
        sub l         ; x-y1
        ld b,a

        ld a,(y_var)
        add a,h         ; y+x1
        ld c,a

        call cplot

        ; X+Y1,Y-X1
        ld hl,(y1_var)   ; x1 in h, y1 in l

        ld a,(x_var)
        add a,l         ; x+y1
        ld b,a

        ld a,(y_var)
        sub h         ; y-x1
        ld c,a

        call cplot

        ; X-Y1,Y-X1
        ld hl,(y1_var)   ; x1 in h, y1 in l

        ld a,(x_var)
        sub l         ; x-y1
        ld b,a

        ld a,(y_var)
        sub h         ; y-x1
        ld c,a

        call cplot

        ; calculate changes
        ld hl,(y1_var)   ; x1 in h, y1 in l
        ; PY=P+Y1+Y1+1
        ld a,(p_var)
        inc a
        add a,l
        add a,l
        ld b,a          ; save py into b
        ld (p_var),a    ; save py for later
        ; PXY=PY-X1-X1+1
        inc a
        sub h
        sub h
        ld c,a          ; save pxy into c

        ld hl,y1_var    ; y1=y1+1
        inc (hl)

        ; okay, test if we need to change position
        ld a,b          ; set a to py
        call absA
        ld b,a          ; b now holds abs py
        ld a,c          ; set a to pxy
        call absA       ; a is now abs pxy
        cp b            ; check if abs pxy >= abs py
        jp nc,skipahead ; no carry if true


        ld a,c          ; set a to pxy
        ld (p_var),a    ; and store in p!

        ld hl,x1_var    ; x1=x1-1
        dec (hl)

skipahead:
        ld hl,(y1_var)  ; x1 in h, y1 in l
        ld a,h          ; a is now x1
        cp l            ; is x1>=y1?
        ret c           ; if no we are all done, return!
        jp plot_loop    ; it is, then we keep going!

; end Draw_Circle
; ---

; +++
; Break
;
; preserves state, but will exit if SPACE is pushed

check_break:
        exx                     ; save register states

        ; did the player press break key (space)?
        call $0f46              ; was break pressed? (break-1 ROM routine)
        jr nc,break             ; no, exit as normal

        exx                     ; restore registers
        ret                     ; and return

        ; yes, exit the program as normal
break:
        rst $0008               ; call ERROR-1 reset
        DB $ff                  ; with error code 0 (normal exit)

; end break
; ---

; +++
; Delay
;
; set bc to speed
; uses check_break to exit

delay_count: DW $0000
delay:
        ld (delay_count),hl     ; save delay

        call check_break

        ; check if done
        ld hl,(delay_count)     ; grab what to test
        dec hl                  ; subtract 1
        ld a,h                  ; check if done
        or l
        jr nz,delay             ; not zero, keep going!

        ret                     ; pause is done!

; end delay
; ---

; +++
;
; Data and Defines
;

; ZX81 system vars
d_file:         EQU $400c
df_cc:          EQU 16398
last_k:         EQU 16421
margin:         EQU 16424
s_posn:         EQU 16441
frames:         EQU 16436

; ZX81 ROM functions
kscan:          EQU $02bb
findchar:       EQU $07bd
stop:           EQU $0cdc
slow:           EQU $0f2b
fast:           EQU $02e7
save:           EQU $02f9
printat:        EQU $08f5
pause:          EQU $0f35
cls:            EQU $0a2a

; ZX81 Characters (not ASCII)
_sp:            EQU $00
_qu:            EQU $0b
_lb:            EQU $0c
_dl:            EQU $0d
_cl:            EQU $0e
_lp:            EQU $10
_rp:            EQU $11
_gt:            EQU $12
_lt:            EQU $13
_eq:            EQU $14
_pl:            EQU $15
_mi:            EQU $16
_as:            EQU $17
_sl:            EQU $18
_sc:            EQU $19
_cm:            EQU $1a
_pr:            EQU $1b
_0_:            EQU $1c
_1_:            EQU $1d
_2_:            EQU $1e
_3_:            EQU $1f
_4_:            EQU $20
_5_:            EQU $21
_6_:            EQU $22
_7_:            EQU $23
_8_:            EQU $24
_9_:            EQU $25
_a_:            EQU $26
_b_:            EQU $27
_c_:            EQU $28
_d_:            EQU $29
_e_:            EQU $2a
_f_:            EQU $2b
_g_:            EQU $2c
_h_:            EQU $2d
_i_:            EQU $2e
_j_:            EQU $2f
_k_:            EQU $30
_l_:            EQU $31
_m_:            EQU $32
_n_:            EQU $33
_o_:            EQU $34
_p_:            EQU $35
_q_:            EQU $36
_r_:            EQU $37
_s_:            EQU $38
_t_:            EQU $39
_u_:            EQU $3a
_v_:            EQU $3b
_w_:            EQU $3c
_x_:            EQU $3d
_y_:            EQU $3e
_z_:            EQU $3f

; end defines
; ---