;; This example shows how to use the hardware to
;; change the video mode.
;;
;; NOTE: This example can't be used with BASIC.


org &8000
nolist

start:

;; synchronise with start of vsync

ld b,&f5
;; this loop will pass if VSYNC is already active, or wait
;; until it's active
i1: in a,(c)
rra
jr nc,i1
;; this loop will pass when VSYNC has ended
i2: in a,(c)
rra
jr c,i2
;; because VSYNC is not active, this loop will then 
;; wait for it to become active
i3: in a,(c)
rra
jr nc,i3

di
im 1					;; Z80 interrupt mode 1
;; install interrupt
ld a,&c3				;; JP instruction
ld hl,interrupt
ld (&0038),a
ld (&0039),hl
ei

loop: jp loop

interrupt:
;; store registers we want to change
push bc
push af
push hl

;; vsync active? (int triggered 2 HSYNC after VSYNC starts)
;; NOTE: Vsync must be at least 2 lines long. Normally it's 8 lines, but fixed to 16 lines
;; on some CRTC.
ld b,&f5
in a,(c)
rra
ld a,0				;; counter
jr c,interrupt2
;; not in VSYNC, increment counter
ld a,(interrupt_count)
inc a
interrupt2:
;; store new counter value
ld (interrupt_count),a

;; use counter to lookup in array

add a,modes AND 255
ld l,a
ld a,modes/256
adc a,0
ld h,a
ld a,(hl)			;; read mode from array

or %10001100		;; combine disable lower and upper ROM with mode from table
out (c),a			;; set mode (and rom state)

;; restore registers we changed
pop hl
pop af
pop bc
ei				;; re-enable interrupts
reti				;; return from interrupt. 

;; this number keeps track of the section of the screen we 
;; are on
interrupt_count:
defb 0

;; this is the list of modes, one per section of the screen
modes:
defb 0
defb 1
defb 2
defb 0
defb 1
defb 2

end start