;-------------------------------------------------------------------------------
; ZX X-MAS '19 - IM 2 LIBRARY
;
;       (/&&&/\&\  /%/  \&\  /%/   /%)(#)(&\ /%###&\ /###)  ()/%|  /&&&\
;         /%/  \&\/%/    \&\/%/,--,|#||#||#| |#| |#| |&&\     |#| (&___&)
;        /%/   /%/\&\    /%/\&\'--'|#||#||#| |###|#|  \&&|    |#|    (&&)
;       /&&&/)/%/  \&\  /%/  \&\   |#||#||#| |#| |#| (###/    |#| (&&&&/
;
; Copyright AbaddoN (C)2018-2019
; Code and gfx by: G.o.D. / AbaddoN
; Mzx by: ern0 / AbaddoN
;-------------------------------------------------------------------------------

; MEMORY MAPPING:
; $FD01-$FDFC IM 2 routine
; $FDFD-$FDFF	JMP instruction to IM 2 routine
; $FE00-$FF00	IM2 address table ($101 bytes filled with $FD-s)
;
; PROCEDURES / ROUTINES
; 1. IM2_INIT: IM 2 initialization (DI must be executed before calling it)
; 2. IM2_TERMINATE: set back to IM 1 and original I register (use in DI state)
; 3. IM2_SET_ROUTINE: change IM2 routine address (use in DI state)
;
; FOR ALLOCATING TO OTHER MEMORY AREA:
; 1. IM2_TABLE_ADDR contains pointer the IM 2 address table. Recommended to
;    place it in non-contended memory (above $8000 in ZX48 / bank 2 in 128k/+2)
;    Address: must be aligned to $100
;    Length:  $101 bytes
;    Data:    this memory area will be filled with IM2_MAGIC_BYTE, therefore the
;             Z80 will call $100*IM2_MAGIC_BYTE + IM2_MAGIC_BYTE in every IRQ.
; 2. IM2_MAGIC_BYTE contains the address of IM2 JP instruction address.
;    Address: $100*IM2_MAGIC_BYTE + IM2_MAGIC_BYTE
;    Length:  $3 bytes (only a JP to the IM2 handler routine)
; 3. IM2_PROC_ADDR is the real IM 2 routine, the JP instruction mentioned above
;    will be set here.

; $FD04-$FDFC
IM2_PROC_ADDR			EQU	$FD04
; $FDFD-$FDFF	JMP instruction to IM2 routine
; $FE00-$FF00	IM2 address table (filled with $FD-s)
IM2_TABLE_ADDR			EQU	$FE00

IM2_MAGIC_BYTE	EQU	$FD			; IM2 entry point is $FDFD addr.
IM2_JMP_OPCODE	EQU	$C3			; op.code of JP instruction

; Initialize IM 2 and save previous interrupt state.
; Saves I register to set back IM 1 at the finalization process.
; Assumes DI (interrupts were disabled) / does not enables interrupts
; in:  DE: IM2 routine address
; out: -
; mod: AF,BC,DE,HL,I
IM2_INIT:
	push	de
	ld	a,i
	ld	(IM2_TERMINATE+1),a		; store previous I register
	ld	hl,IM2_TABLE_ADDR		; setup int. table: fill 257 bytes with $FD
	ld	de,IM2_TABLE_ADDR+1
	ld	bc,$100
	ld	(hl),IM2_MAGIC_BYTE
	ld	a,h
	ldir

	pop	de
	call	IM2_SET_ROUTINE

	ld	i,a					; set interrupt mode 2
	im	2
	ret

; Terminate IM 2 and restores previous interrupt state.
; in:  -
; out: -
; mod: A,I
IM2_TERMINATE:
	ld	a,$13				; #self modified code - stored I register
	ld	i,a
	im	1
	ret

IM2_SET_ROUTINE:
; Set IM2 routine address.
; in:  DE: IM2 routine address
; out: -
; mod: HL
	ld	hl,$100*IM2_MAGIC_BYTE + IM2_MAGIC_BYTE
	ld	(hl),IM2_JMP_OPCODE
	inc	hl
	ld	(hl),e
	inc	hl
	ld	(hl),d
	ret
