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= 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
; ---