You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

337 lines
12 KiB

; Adapted from https://rosettacode.org/wiki/Mandelbrot_set#Z80_Assembly
; by Phillip Summers difficultylevelhigh@gmail.com
;
; WBWROM SBV V2 Easteregg
;
; Compute a Mandelbrot set on a simple Z80 computer.
;
; Porting this program to another Z80 platform should be easy and straight-
; forward: The only dependencies on my homebrew machine are the system-calls
; used to print strings and characters. These calls are performed by loading
; IX with the number of the system-call and performing an RST 08. To port this
; program to another operating system just replace these system-calls with
; the appropriate versions. Only three system-calls are used in the following:
; _crlf: Prints a CR/LF, _puts: Prints a 0-terminated string (the adress of
; which is expected in HL), and _putc: Print a single character which is
; expected in A. RST 0 give control back to the monitor.
;
#include "std.asm"
cr .equ 0dh
lf .equ 0ah
eos .equ 00h
.org EGG_LOC
scale .equ 256 ; Do NOT change this - the
; arithmetic routines rely on
; this scaling factor! :-)
divergent .equ scale * 4
ld sp,HBX_LOC
ld hl, welcome ; Print a welcome message
call _puts
; for (y = <initial_value> ; y <= y_end; y += y_step)
; {
outer_loop ld hl, (y_end) ; Is y <= y_end?
ld de, (y)
and a ; Clear carry
sbc hl, de ; Perform the comparison
jp m, mandel_end ; End of outer loop reached
; for (x = x_start; x <= x_end; x += x_step)
; {
ld hl, (x_start) ; x = x_start
ld (x), hl
inner_loop ld hl, (x_end) ; Is x <= x_end?
ld de, (x)
and a
sbc hl, de
jp m, inner_loop_end ; End of inner loop reached
; z_0 = z_1 = 0;
ld hl, 0
ld (z_0), hl
ld (z_1), hl
; for (iteration = iteration_max; iteration; iteration--)
; {
ld a, (iteration_max)
ld b, a
iteration_loop push bc ; iteration -> stack
; z2 = (z_0 * z_0 - z_1 * z_1) / SCALE;
ld de, (z_1) ; Compute DE HL = z_1 * z_1
ld b,d
ld c,e
call mul_16
ld (z_0_square_low), hl ; z_0 ** 2 is needed later again
ld (z_0_square_high), de
ld de, (z_0) ; Compute DE HL = z_0 * z_0
ld b,d
ld c,e
call mul_16
ld (z_1_square_low), hl ; z_1 ** 2 will be also needed
ld (z_1_square_high), de
and a ; Compute subtraction
ld bc, (z_0_square_low)
sbc hl, bc
ld (scratch_0), hl ; Save lower 16 bit of result
ld h,d
ld l,e
ld bc, (z_0_square_high)
sbc hl, bc
ld bc, (scratch_0) ; HL BC = z_0 ** 2 - z_1 ** 2
ld c, b ; Divide by scale = 256
ld b, l ; Discard the rest
push bc ; We need BC later
; z3 = 2 * z0 * z1 / SCALE;
ld hl, (z_0) ; Compute DE HL = 2 * z_0 * z_1
add hl, hl
ld d,h
ld e,l
ld bc, (z_1)
call mul_16
ld b, e ; Divide by scale (= 256)
ld c, h ; BC contains now z_3
; z1 = z3 + y;
ld hl, (y)
add hl, bc
ld (z_1), hl
; z_0 = z_2 + x;
pop bc ; Here BC is needed again :-)
ld hl, (x)
add hl, bc
ld (z_0), hl
; if (z0 * z0 / SCALE + z1 * z1 / SCALE > 4 * SCALE)
ld hl, (z_0_square_low) ; Use the squares computed
ld de, (z_1_square_low) ; above
add hl, de
ld b,h ; BC contains lower word of sum
ld c,l
ld hl, (z_0_square_high)
ld de, (z_1_square_high)
adc hl, de
ld h, l ; HL now contains (z_0 ** 2 +
ld l, b ; z_1 ** 2) / scale
ld bc, divergent
and a
sbc hl, bc
; break;
jp c, iteration_dec ; No break
pop bc ; Get latest iteration counter
jr iteration_end ; Exit loop
; iteration++;
iteration_dec pop bc ; Get iteration counter
djnz iteration_loop ; We might fall through!
; }
iteration_end
; printf("%c", display[iteration % 7]);
ld a, b
and $7 ; lower three bits only (c = 0)
sbc hl, hl
ld l, a
ld de, display ; Get start of character array
add hl, de ; address and load the
ld a, (hl) ; character to be printed
call _putc ; Print the character
ld de, (x_step) ; x += x_step
ld hl, (x)
add hl, de
ld (x), hl
jp inner_loop
; }
; printf("\n");
inner_loop_end call _putcrlf ; Print a CR/LF pair
ld de, (y_step) ; y += y_step
ld hl, (y)
add hl, de
ld (y), hl ; Store new y-value
jp outer_loop
; }
mandel_end ld hl, finished ; Print finished-message
call _puts
; GET CONSOLE INPUT STATUS VIA HBIOS
waitch
#IF (BIOS == BIOS_WBW)
LD C,CIO_CONSOLE ; CONSOLE UNIT TO C
LD B,BF_CIOIN ; HBIOS FUNC: INPUT CHAR
RST 08 ; DO IT
;; RETURN TO THE LOADER
;LD A,BID_BOOT ; BOOT BANK
;LD HL,0 ; ADDRESS ZERO
;CALL HB_BNKCALL ; DOES NOT RETURN
LD B,BF_SYSRESET ; SYSTEM RESTART
LD C,BF_SYSRES_WARM ; WARM START
CALL $FFF0 ; CALL HBIOS
HALT
#ENDIF
#IF (BIOS == BIOS_UNA)
LD B,0 ; CONSOLE UNIT TO B
LD C,BF_CIOIN ; UBIOS FUNC: INPUT CHAR
CALL $FFFD ; DO IT
; RETURN TO THE LOADER
LD BC,$01FB ; UNA FUNC = SET BANK
LD DE,0 ; ROM BANK 0
CALL $FFFD ; DO IT
JP 0 ; JUMP TO RESTART ADDRESS
#ENDIF
_putcrlf ld hl, crlf
_puts push af
puts0 ld a,(hl)
cp eos
jr z,puts1
call _putc
inc hl
jr puts0
puts1 pop af
ret
_putc
#IF (BIOS == BIOS_WBW)
PUSH AF
PUSH BC
PUSH DE
PUSH HL
LD E,A ; OUTPUT CHAR TO E
LD C,CIO_CONSOLE ; CONSOLE UNIT TO C
LD B,BF_CIOOUT ; HBIOS FUNC: OUTPUT CHAR
RST 08 ; HBIOS OUTPUTS CHARACTDR
POP HL
POP DE
POP BC
POP AF
RET
#ENDIF
#IF (BIOS == BIOS_UNA)
PUSH AF
PUSH BC
PUSH DE
PUSH HL
LD E,A ; OUTPUT CHAR TO E
LD B,0 ; CONSOLE UNIT TO B
LD C,BF_CIOOUT ; UBIOS FUNC: OUTPUT CHAR
CALL $FFFD ; UBIOS OUTPUTS CHARACTDR
POP HL
POP DE
POP BC
POP AF
RET
#ENDIF
welcome .db "Generating a Mandelbrot set..."
.db cr, lf, cr, lf, eos
finished .db "Computation finished."
crlf .db cr, lf, eos
iteration_max .db 10 ; How many iterations
x .dw 0 ; x-coordinate
x_start .dw -2 * scale ; Minimum x-coordinate
x_end .dw 5 * scale / 10 ; Maximum x-coordinate
x_step .dw 4 * scale / 100 ; x-coordinate step-width
y .dw -1 * scale ; Minimum y-coordinate
y_end .dw 1 * scale ; Maximum y-coordinate
y_step .dw 1 * scale / 10 ; y-coordinate step-width
z_0 .dw 0 ;0
z_1 .dw 0 ;0
scratch_0 .dw 0
z_0_square_high .dw 0
z_0_square_low .dw 0
z_1_square_high .dw 0
z_1_square_low .dw 0
display .db " .-+*=#@" ; 8 characters for the display
;
; Compute DEHL = BC * DE (signed): This routine is not too clever but it
; works. It is based on a standard 16-by-16 multiplication routine for unsigned
; integers. At the beginning the sign of the result is determined based on the
; signs of the operands which are negated if necessary. Then the unsigned
; multiplication takes place, followed by negating the result if necessary.
;
mul_16 xor a ; Clear carry and A (-> +)
bit 7, b ; Is BC negative?
jr z, bc_positive ; No
sub c ; A is still zero, complement
ld c, a
ld a, 0
sbc a, b
ld b, a
scf ; Set carry (-> -)
bc_positive bit 7, D ; Is DE negative?
jr z, de_positive ; No
push af ; Remember carry for later!
xor a
sub e
ld e, a
ld a, 0
sbc a, d
ld d, a
pop af ; Restore carry for complement
ccf ; Complement Carry (-> +/-?)
de_positive push af ; Remember state of carry
and a ; Start multiplication
sbc hl, hl
ld a, 16 ; 16 rounds
mul_16_loop add hl, hl
rl e
rl d
jr nc, mul_16_exit
add hl, bc
jr nc, mul_16_exit
inc de
mul_16_exit dec a
jr nz, mul_16_loop
pop af ; Restore carry from beginning
ret nc ; No sign inversion necessary
xor a ; Complement DE HL
sub l
ld l, a
ld a, 0
sbc a, h
ld h, a
ld a, 0
sbc a, e
ld e, a
ld a, 0
sbc a, d
ld d, a
ret
lastbyte .equ $
SLACK .EQU (EGG_END - lastbyte)
.FILL SLACK,'e'
;
.ECHO "EASTEREGG space remaining: "
.ECHO SLACK
.ECHO " bytes.\n"
.end