; Learn Multi platform Z80 Assembly Programming... With Vampires!

;Please see my website at	www.chibiakumas.com/z80/
;for the 'textbook', useful resources and video tutorials

;File		Monitor
;Version	V1.0b
;Date		2018/4/1
;Content	Provides simple ability to show the Program Counter, and to show register values without altering z80 state
;Requires	ShowHex function

;Changelog	Code shown in youtube vid had a bug - it was showing more registers than exited, resulting in
;		non-existant reg pair DIxxxx showing
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;include this somewhere
;read "..\SrcAll\Multiplatform_ShowHex.asm"

;Options
;Monitor_Full equ 1				;*** FULL monitor takes more ram, but shows all registers
;Monitor_Pause equ 1 				;*** Pause after showing debugging info

;Usage
;	call Monitor


;Magic trick to work out if Interrupts are enabled or disabled
	ifdef Monitor_Full

MonitorEICheck:
	ld bc,&44F3		;F3 is DI

	ifdef BuildENT
		call TestInterrupts
		ret c
	else
		ld a,i			;strange - but this sets PO flag inf ints were enabled
		ret po 			;this is tricky - it will return if Interrupts disabled
	endif

	ld c,&FB		;FB is EI
	inc b 			;convert D->E for the label
	ret

	ifdef BuildENT
	;Thanks to BruceTanner on the EnterpriseForever forums, who suggested using this... which works more reliably on the Enterprise (and maybe others that use a NMOS Z80)
TestInterrupts:
		XOR 	A			;Push a zero on stack and pop it
		PUSH 	AF			; back off so we can tell if it gets
		POP 	AF			; overwitten with IRQ return address.
		LD 	A,R			;Get IRQ state
		DI
		RET 	PE			;Return if interrupts enabled
		DEC 	SP			;If they appear disabled then check
		DEC 	SP			; whether the zero is still below stack
		POP 	AF
		OR 	A			;If not then interrupts must have been 
		RET 	NZ			; enabled so return with carry clear.
		SCF				;Return with carry set to indicate
		RET	
		endif
	endif ;Monitor_Full

Monitor:
;This is a dirty trick to check if interrupts are enabled or not 
	ifdef Monitor_Full
		push bc
		push af
			call MonitorEICheck
			ld a,c
			ld (Monitor_EI_Reenable_2byte),a	;DI /EI at end of monitor
			ld a,b
			ld (Monitor_Special),a			;letter D/E for visible string
			ld a,&c9
			ld (Monitor_EI_Reenable_2byte+1),a
		pop af
		pop bc
		di
	endif

	; Push all registers onto the stack - we'll pop them back and put them onscreen

	ifdef Monitor_Full		;Monitor FULL includes all registers
		exx			
		ex af,af'		;Backup all the registers.
		push HL
		PUSH DE
		PUSH BC
		Push AF

		ld a,I
		ld h,a
		ld a,R
		ld l,a
		push hl

		exx
		ex af,af'
		push IY
		push IX

	endif
	push HL
	PUSH DE
	PUSH BC
	Push AF

;	ld (MonitorStackRestore_Plus2-2),sp	;We need to backup the true stack pointer, we're going to be messin' around!

	call NewLine


	ifdef Monitor_Full
		ld hl,12*2	;NO of bytes we pushed in FULL mode
	else
		ld hl,12-7 *2	;NO of bytes we pushed in NORMAL mode
	endif
	add hl,sp	;work out the original stack pointer address, and push that too!
	push hl
	ld (Monitor_NextMonitorPos_2Byte),sp	;Store the current stack pointer, so we know where the legit data is.

	ifdef Monitor_Full
		ld b,13				;Set our loop counter to the number of regs
	else
		ld b,13-7
	endif 
	ld hl,Monitor_NextMonitorPos_2Byte	;Pointer to the pushed values - we use this to read them out the stack.
	ld e,(hl)
	inc hl
	ld d,(hl)
	ld hl,Monitor_Text 
	

MonitorAgain:

	;push bc
		ld a,(hl)
		call PrintChar		;Shiw the 2 char label
		inc hl
		ld a,(hl)
		call PrintChar
;	push de
		inc de
		ld a,(de)		;Show the 1st Hex Byte
		call ShowHex
;	pop de
	dec de
	ld a,(de)			;Load the 2nd HEX byte
	inc de
	inc de
		call ShowHex		;Show the 2nd Hex Byte
		ld a,32			;Show two spaces
		call PrintChar
		call PrintChar
;	pop bc
;	inc bc
	inc hl

	ifdef BuildGBC
		bit 0,b
		call nz,NewLine
	endif 
	ifdef BuildGMB
		bit 0,b
		call nz,NewLine
	endif 
	
	z_djnz MonitorAgain
	ifdef Monitor_Full

		ld a,(Monitor_Special)
		call PrintChar
		ld a,"I"
		call PrintChar
	endif


;	ld sp,&0000 MonitorStackRestore_Plus2

	pop hl				;this is the SP we pushed before.

	ifdef Monitor_Pause
		call WaitChar			;Do a WaitChar if we're supposed to pause
	endif
	Pop AF				;Restore all the registers.
	Pop BC
	pop DE
	pop HL
	ifdef Monitor_Full


		pop IX
		pop IY

		;I and R
		inc SP
		inc SP

		exx
		ex af,af'
		Pop AF
		Pop BC
		pop DE
		pop HL
		exx
		ex af,af'
	endif


	ifdef Monitor_Full				;SelfModifying code will change this to EI or DI depending if interrputs are needed
		call Monitor_EI_Reenable_2byte
	endif
	ret

Monitor_Text:			;Labels of the register - must be 2 char!
	db "S","p"
	db "A","f"
	db "B","c"
	db "D","e"
	db "H","l"
	ifdef Monitor_Full

		db "I","x"
		db "I","y"
		db "i","R"
		db "A","'"
		db "B","'"
		db "D","'"
		db "H","'"

	endif
	db "P","c"

