; 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
				