ZX81 Assembly Program Listing
*PICTURE ASM*SLR/2021** (picasm.asm)
;
; The Picture
; v1.00 Steven Reid (c) 2021
; This is an assembly version of my old BASIC drawring routine.
;
org 16514 ; stored in REM at top
jr start ; needed for z80asm
; title and copyright
copy:
db $00,$27,$3e,$00,$38,$39,$2a,$3b,$2a,$33
db $00,$37,$2a,$2e,$29,$00,$1e,$1c,$1e,$1d
db $00,$17,$00,$17,$00,$39,$2d,$2a,$00,$35
db $2e,$28,$39,$3a,$37,$2a,$00,$2e,$33,$00
db $26,$38,$32,$00,$17,$00,$17,$76,$76
; BY STEVEN REID 2021
; * * THE PICTURE IN ASM * *
; macros
stop: equ $0cdc
slow: equ $0f2b
printat: equ $08f5
pause: equ $0f35
; system vars
d_file: equ $400c
;
; Start of program!
;
start:
ld hl,picturedata ; start of picture data
mainloop:
ld a,(hl) ; check if we are at the end of the array
cp $ff
; jp z,waitloop
jp z,stop ; all done
ld de,linecoords ; coordiantes (to)
ld bc,4 ; 4 to copy!
ldir ; copy for coordinates
push hl
call draw_line ; and go draw the line!
pop hl
jr mainloop ; loop until out of data!
;waitloop:
; ld bc,1000
; call pause
; jp start
; jp z,stop ; all done
; End of loop!
;
;x can be 0 to 63
;y can be 0 to 45
picturedata: ; start x1,y1 and end x2,y2 - 4 bytes per line
db 25,38,61,38 ; boat bottom
db 60,38,55,41
db 55,41,30,41
db 30,41,25,39
db 48,38,48,8 ; mast
db 48,8,45,11 ; flag
db 45,11,48,11
db 48,12,26,35 ; sales
db 26,35,60,35
db 59,35,48,12
db 0,22,38,22 ; horizon
db 53,22,64,22
db 4,4,9,4 ; sun
db 8,4,10,5
db 9,5,9,7
db 9,6,7,8
db 8,8,4,8
db 4,8,2,7
db 2,7,2,6
db 2,6,4,5
db $ff ; array done!
;
; Routines
;
;start code line
zxplot:
;alter pixel nominated by plotx and ploty
;plotmodes are flag2 bit 1. 0 = plot, 1 = unplot
;x can be 0 to 63
;y can be 0 to 45
call calculate_origin
nozxplotcolour:
ld a,(flag2)
bit 1,a
jp nz,unplot_pixel
call plot_pixel
ret
; end zxplot
calculate_origin:
; work out what square on the screen the pixel appears at
ld a,(plotx)
bit 7,a
ret nz
sub 64
ret p
ld a,(ploty)
bit 7,a
ret nz
sub 46
ret p
ld a,$00
ld (plotsector),a
ld a,(plotx)
srl a
ld (plotx),a
jr nc,pixelleft
ld a,$01
ld (plotsector),a ;pixel is in right half
pixelleft:
ld a,(ploty)
srl a
ld (ploty),a
jr nc,pixelup
ld a,(plotsector)
set 1,a
ld (plotsector),a ;pixel is in lower half
pixelup:
;now find the correct screen position
ld hl,(d_file)
inc hl
ld de,33
ld a,(ploty)
cp $0
jr z,findx
ld b,a
findy:
add hl,de
djnz findy
findx:
ld de,$00
ld a,(plotx)
ld e,a
add hl,de
ld (plot_origin), hl
ret
plot_pixel:
; plot the pixel in the co-ord from plot_origin (square on screen) and
; plotsector (actual pixel)
; 0= top left, 1 = top right, 2 = bottom left, 3 = bottom right
ld hl,(plot_origin)
ld a,(plotsector)
cp $00
jr z,plottl
cp $01
jr z,plottr
cp $02
jr z,plotbl
cp $03
jr z,plotbr
ret
plottl: ;top left quadrant
ld a,(hl)
plottlok:
bit 7,a
jr nz,tlinvert
set 0,a
ld (hl),a
ret
tlinvert:
res 0,a
ld (hl),a
ret
plottr: ;top right quadrant
ld a,(hl)
plottrok:
bit 7,a
jr nz,trinvert
set 1,a
ld (hl),a
ret
trinvert:
res 1,a
ld (hl),a
ret
plotbl: ;bottom left quadrant
ld a,(hl)
plotblok:
bit 7,a
jr nz,blinvert
set 2,a
ld (hl),a
ret
blinvert:
res 2,a
ld (hl),a
ret
plotbr: ;bottom right quadrant
ld a,(hl)
bit 7,a ;collision detect
ret nz
;plotbrok:
set 7,a
xor $07 ;invert all other bits
ld (hl),a
ret ; plot pixel
unplot_pixel:
; unplot the pixel in the co-ord from plot_origin (square on screen) and
; plotsector (actual pixel)
; 0= top left, 1 = top right, 2 = bottom left, 3 = bottom right
ld hl,(plot_origin)
ld a,(plotsector)
cp $00
jr z,uplottl
cp $01
jr z,uplottr
cp $02
jr z,uplotbl
cp $03
jr z,uplotbr
ret
uplottl: ;top left quadrant
ld a,(hl)
bit 7,a
jr nz,utlinvert
res 0,a
ld (hl),a
ret
utlinvert:
set 0,a
ld (hl),a
ret
uplottr: ;top right quadrant
ld a,(hl)
bit 7,a
jr nz,utrinvert
res 1,a
ld (hl),a
ret
utrinvert:
set 1,a
ld (hl),a
ret
uplotbl: ;bottom left quadrant
ld a,(hl)
bit 7,a
jr nz,ublinvert
res 2,a
ld (hl),a
ret
ublinvert:
set 2,a
ld (hl),a
ret
uplotbr: ;bottom right quadrant
ld a,(hl)
bit 7,a
ret z
res 7,a
xor $07 ;invert all other bits
ld (hl),a
ret
ret ;unplot pixel
draw_line_high:
; bresenham's algorithm
; for up down lines
ld a,(x0p) ;dx = x1 - x0
ld b,a
ld a,(x1p)
sub b
ld (dx),a
ld a,(y0p) ;dy = y1 - y0
ld b,a
ld a,(y1p)
sub b
ld (dy),a
ld hl,xi ;xi =1
ld (hl),$1
ld a,(dx)
bit 7,a ;if dx<0?
jr z,dx_not_negh
;dx is < 0
ld a,$ff ;xi =-1
ld (xi),a
ld a,(dx) ; dx = -dx
neg
ld (dx),a
dx_not_negh:
ld a,(dy) ;dis = (2 * dx) - dy
ld b,a
ld a,(dx)
sla a ;dx*2
sub b
ld (dis),a
;x = x0
ld a,(x0p)
ld (x),a
ld a,(y0p) ;for y from y0 to y1
ld (y),a
ld b,a
ld a,(y1p)
sub b
jr z,zero_line ;just plot the origin if line length is zero
ld b,a ;b contains y1-y0
draw_line_looph:
push bc
ld a,(y)
ld (ploty),a
inc a
ld (y),a
ld a,(x)
ld (plotx),a
call zxplot ;ploy x,y
;call single_step
ld a,(dis) ;if dis > 0
bit 7,a
jr nz,dis_lt0h
;x = x + xi
ld a,(xi)
ld b,a
ld a,(x)
add a,b
ld (x),a
;dis = dis + (2 * (dx - dy))
ld a,(dy)
ld b,a
ld a,(dx)
sub b
sla a
ld b,a
ld a,(dis)
add a,b
ld (dis),a
jr end_draw_line_looph
dis_lt0h:
;dis = dis + 2*dx
ld a,(dx)
sla a
ld b,a
ld a,(dis)
add a,b
ld (dis),a
end_draw_line_looph:
pop bc
djnz draw_line_looph
ld hl,flag2
res 1,(hl)
ret ;draw_line_high
zero_line:
ld a,(x0)
ld (plotx),a
ld a,(y0)
ld (ploty),a
call zxplot
ret ;zero_line
draw_line_low:
; bresenham's algorithm
ld a,(x0p) ;dx = x1 - x0
ld b,a
ld a,(x1p)
sub b
ld (dx),a
ld a,(y0p) ;dy = y1 - y0
ld b,a
ld a,(y1p)
sub b
ld (dy),a
ld hl,yi ;yi =1
ld (hl),1
bit 7,a ;if dy<0?
jr z,dy_not_neg
;dy is < 0
ld a,$ff ;yi =-1
ld (yi),a
ld a,(dy) ; dy = -dy
neg
ld (dy),a
dy_not_neg:
ld a,(dx) ;dis = (2 * dy) - dx
ld b,a
ld a,(dy)
sla a ;dy*2
sub b
ld (dis),a
;y = y0
ld a,(y0p)
ld (y),a
ld a,(x0p) ;for x from x0 to x1
ld (x),a
ld b,a
ld a,(x1p)
sub b
jr z,zero_line ;just plot the origin if line length is zero
ld b,a ;b contains x1-x0
draw_line_loop:
push bc
ld a,(x)
ld (plotx),a
inc a
ld (x),a
ld a,(y)
ld (ploty),a
call zxplot ;ploy x,y
ld a,(dis) ;if dis > 0
bit 7,a
jr nz,dis_lt0
;call print_star
;y = y + yi
ld a,(yi)
ld b,a
ld a,(y)
add a,b
ld (y),a
;dis = dis + (2 * (dy - dx))
ld a,(dx)
ld b,a
ld a,(dy)
sub b
sla a
ld b,a
ld a,(dis)
add a,b
ld (dis),a
jr end_draw_line_loop
dis_lt0:
;dis = dis + 2*dy
ld a,(dy)
sla a
ld b,a
ld a,(dis)
add a,b
ld (dis),a
end_draw_line_loop:
pop bc
djnz draw_line_loop
ld hl,flag2
res 1,(hl)
ret ;draw_line_low
undraw_line:
ld hl,flag2
set 1,(hl)
draw_line:
;will abort if both start and end are the same
ld a,(x0)
ld (x0p),a
ld a,(x1)
ld (x1p),a
ld a,(y0)
ld (y0p),a
ld a,(y1)
ld (y1p),a
;work out what configuration of the algorithm
;is correct to draw the line
;if abs(y1 - y0) < abs(x1 - x0)
ld a,(y0)
ld b,a
ld a,(y1)
sub b ;a= y1-y0
jr nc,y1gty0 ;nc = (y1-y0) >0
neg ;change to abs value
y1gty0:
ld c,a ;c = abs(y1 - y0) ;works so far
ld a,(x0)
ld b,a
ld a,(x1)
sub b
jr nc,x1gtx0
neg
x1gtx0:
;a = abs(x1 - x0)
sub c ;c = abs(y1 - y0)
jr c,check_drawlinehigh ;abs(y1 - y0) < abs(x1 - x0) so use draw line high
;if x0 > x1 ;draw line low, else swap x0,y0 and x1,y1
ld a,(x1)
ld b,a
ld a,(x0)
sub b
jp c,draw_line_low
;x1 > x0 so swap x0,x1 and y0,y1
call swap_x0_x1_y0_y1
jp draw_line_low
check_drawlinehigh:
;if y0 > y1
ld a,(y1)
ld b,a
ld a,(y0)
sub b
jp c,draw_line_high
;plotlinehigh(x1, y1, x0, y0)
;x1 > x0 so swap x0,x1 and y0,y1
call swap_x0_x1_y0_y1
jp draw_line_high
;draw_line
swap_x0_x1_y0_y1:
ld a,(y0)
ld b,a
ld a,(y1)
ld (y0p),a
ld a,b
ld (y1p),a
ld a,(x0)
ld b,a
ld a,(x1)
ld (x0p),a
ld a,b
ld (x1p),a
ret
get_cosine:
;same as sine plus 90 degrees
sub $40
get_sine: ;take value of the a register and return the sine value from table
;first, work out what quadrant we're in
cp $40
jr nc,sine_quad2
;angle is top right quadrant
;count angle sine from bottom of table
ld hl,sine_value_bottom
ld de,$00
ld e,a
add hl,de
ld a,(hl)
ret
sine_quad2:
cp $80
jr nc,sine_quad3
;angle is bottom right quadrant
;count angle sine from top of table
sub $40
ld hl,sine_value_top
ld de,$00
ld e,a
scf
ccf
sbc hl,de
ld a,(hl)
ret
sine_quad3:
cp $c0
jr nc,sine_quad4
;causing problems at $80
;angle is bottom left quadrant
;count negative angle sine from bottom of table
sub $80
ld hl,sine_value_bottom
ld de,$00
ld e,a
add hl,de
ld a,(hl)
set 7,a
ret
sine_quad4:
;angle is top left quadrant
;count negative angle sine from top of table
;causing problems at $ff
sub $c0
ld hl,sine_value_top
ld de,$00
ld e,a
scf
ccf
sbc hl,de
ld a,(hl)
set 7,a
ret ;get_sine
calculate_scaled_value:
;a register contains sine value
;multiply line_length by the sine and divide by 128
;to get the displacement
ld c,a ;preserve original displacement
res 7,a
cp $00
jr z,calculate_zero_value
ld b,a ;sine value
ld a,(line_length)
ld hl,$00
ld l,a
push hl
pop de
;now multiply line length by the sine value
mult_sine_loop:
add hl,de
djnz mult_sine_loop
;divide result in hl register by 128
ld b,$07
divide_by_128_loop:
srl l
srl h
jr nc,nocarry_l_reg
set 7,l
nocarry_l_reg:
djnz divide_by_128_loop
bit 7,c
ret z
ld a,l
neg
calculate_zero_value:
ld l,a
ret ;calculate_scaled_value
;variables
flag2:
db $00
plotmode:
db $00
plotx:
db $00
ploty:
db $00
plotsector:
db $00
plot_origin:
db $00, $00
line_length:
db $00
;line drawing x and y. variables ending in p are the parameters
;passed to the line draw program
linecoords:
x0:
db $00
y0:
db $00
x1:
db $00
y1:
db $00
x0p:
db $00
y0p:
db $00
x1p:
db $00
y1p:
db $00
;used for bresenham's line algorithm
dx:
db $00
dy:
db $00
xi:
db $00
yi:
db $00
dis:
db $00
x:
db $00
y:
db $00
sine_value_bottom: ;64 values f0r the sine of a value of 128, so a line of length
;128 would have a x length of 3 pixels at an angle of 1 unit of 63
db $00,$03,$06,$09,$0c,$10,$13,$16,$19,$1c,$1f,$22,$25,$28,$2b
db $2e,$31,$33,$36,$39,$3c,$3f,$41,$44,$47,$49,$4c,$4e,$51,$53
db $55,$58,$5a,$5c,$5e,$60,$62,$64,$66,$68,$6a,$6b,$6d,$6f,$70
db $71,$73,$74,$75,$76,$78,$79,$7a,$7a,$7b,$7c,$7d,$7d,$7e,$7e
db $7e,$7f,$7f
sine_value_top:
db $7f