My attempt at a chunky graphics routine for the ZX81 that allows three colors: white, gray and black. This program demonstrates its use.
;
; 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
; ---