.png)
Optional. The course's own path builds games from tiles and sprites, not freehand pixels - S17 explains why. This is for the times you still want a line: a diagonal beam, a trajectory, a debug overlay, a title screen with a curve in it.
Setting one pixel is already fully covered: S16 gives the bitmap address of pixel (x, y), and this course has since confirmed that a pixel can be set without disturbing its seven neighbours - read the byte, OR the bit in, write it back. Nothing below invents a new hardware fact; it is all built on that.
; --- set pixel (x, y): compute the address, then read-modify-write the bit
; H = ((y/64) * 8) or ((y/8) mod 8)
; L = ((x/8) * 8) or (y mod 8)
; bit = 80h >> (x mod 8)
;
setpx: ; HL holds the address, B holds the bit, per the formula above
LD A,L
OUT (9),A
LD A,H ; no 40h - a read
OUT (9),A
IN A,(8)
OR B
LD C,A
LD A,L
OUT (9),A
LD A,H
ADD A,40h ; the write bit
OUT (9),A
LD A,C
OUT (8),A
RET
Given setpx, drawing a line between two points is a question with nothing
Einstein-specific left in it: which pixels lie between (x1, y1) and (x2,
y2)? That is Bresenham's line algorithm, published in 1965 and reprinted in
essentially every graphics text since - general computer science, not
something this project's discipline has any business "confirming" against
the machine, any more than it would test whether 2 + 2 still equals 4
here.
The shape of it: walk from one endpoint to the other along whichever axis covers more ground, and carry a running error term that decides, pixel by pixel, whether the other axis needs to step too.
; --- drawline: (x1,y1) to (x2,y2), all four in the named bytes below
;
; dx, dy: the absolute distances. sx, sy: +1 or -1, the direction of travel
; on each axis. err: dx - dy to start, adjusted every step. This is the
; textbook Bresenham loop - nothing here is Einstein-specific.
drawline:
LD A,(x1)
LD (px),A
LD A,(y1)
LD (py),A
dl_loop:
; --- plot the current point
; (compute HL/B for (px,py) as in S16, then CALL setpx)
LD A,(px)
LD B,A
LD A,(x2)
CP B
JR NZ,dl_cont
LD A,(py)
LD B,A
LD A,(y2)
CP B
RET Z ; reached the end point - done
dl_cont:
LD A,(err)
ADD A,A ; e2 = 2*err
LD B,A
LD A,(dy)
NEG
CP B
JR Z,dl_stepx ; e2 == -dy: step x only
JR NC,dl_stepx ; e2 > -dy: step x
LD A,(dx)
CP B
JR Z,dl_stepy ; e2 == dx: step y only
JR C,dl_stepy ; e2 < dx: step y (else fall through: step both)
dl_stepx:
LD A,(err)
LD B,A
LD A,(dy)
NEG
ADD A,B
LD (err),A
LD A,(px)
LD B,A
LD A,(sx)
ADD A,B
LD (px),A
dl_stepy:
LD A,(err)
LD B,A
LD A,(dx)
ADD A,B
LD (err),A
LD A,(py)
LD B,A
LD A,(sy)
ADD A,B
LD (py),A
JR dl_loop
This is deliberately a skeleton rather than a ready-to-assemble program:
dx/dy/sx/sy/err need setting up from the two endpoints first (the
usual subtract-and-take-the-sign a reader who has reached S10 already knows
how to write), and the branch structure above needs the two "step both"
paths falling through correctly rather than being sketched as comments. The
point of showing it at all is the shape of the algorithm - error
accumulation trading a division for an addition every step, which is
exactly why it suits a chip with no divide instruction - not a listing to
type in unchanged.
Every step is a setpx, and setpx is a VRAM read, a VRAM write, and the
address arithmetic for both. A line of a hundred pixels is on the order of
a hundred read-modify-write cycles - considerably more work than moving a
sprite, which is two bytes regardless of how far it travels. This is the
same trade S15 introduced for the whole bitmap: total control over every
pixel, paid for one pixel at a time. For a game's moving parts, S19's
sprites almost always win. For the parts that hold still - a diagram, a
decorative curve, a line only ever drawn once - the cost is paid once, and
this is the right tool.
The algorithm's shape here was cross-checked against a published Z80 implementation for VDP-based machines (AFW Software's Power Graphics booklet, itself citing A. Redfern, "Draw the Line", Personal Computer World, March 1989, and H. Schildt, C: The Complete Reference) - useful for the algorithm, not as evidence about the Einstein. That source's actual code is written for a different machine's port numbers and needs adapting before it would run here at all; nothing from it was carried over unverified, and the pixel-address and read-modify-write mechanics above are this project's own confirmed facts, not borrowed ones.