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