forked from MirrorRepos/RomWBW
committed by
GitHub
5 changed files with 329 additions and 21 deletions
@ -0,0 +1,300 @@ |
|||
; 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,0fdffh |
|||
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 LD C,CIODEV_CONSOLE ; CONSOLE UNIT TO C |
|||
LD B,BF_CIOIST ; HBIOS FUNC: INPUT STATUS |
|||
RST 08 ; HBIOS RETURNS STATUS IN A |
|||
; RESTORE REGISTERS (AF IS OUTPUT) |
|||
JR Z,waitch |
|||
; Return to the loader |
|||
LD A,BID_BOOT ; BOOT BANK |
|||
LD HL,0 ; ADDRESS ZERO |
|||
CALL HB_BNKCALL ; DOES NOT RETURN |
|||
HALT |
|||
|
|||
_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 PUSH AF |
|||
PUSH BC |
|||
PUSH DE |
|||
PUSH HL |
|||
LD E,A ; OUTPUT CHAR TO E |
|||
LD C,CIODEV_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 |
|||
|
|||
welcome .db "Generating a Mandelbrot set" |
|||
.db 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 |
|||
|
|||
Loading…
Reference in new issue