;; The previous multi-mode example would not always be accurate. ;; Sometimes, a mode change could be delayed by up to 8 scanlines. ;; ;; The reason: ;; 1. The keyboard is scanned by the firmware 1 time out of every 6 interrupts. ;; However, the position within the frame is not synchronised to any specific event. ;; i.e. it is not synchronised to the vsync. ;; ;; Sometimes the keyboard interrupt can get delayed, or shifted from it's original ;; position defined when the computer starts up. ;; ;; When it moves position, it is scanned before our interrupt is executed. ;; ;; Vsync (frame flyback) interrupts are always executed before the keyboard is scanned, ;; so these do not suffer from the same problem. ;; ;; So to solve this problem we need to synchronise and reset when the keyboard ;; should be read. KL SCAN NEEDED does this for us. It signals the keyboard should ;; be scanned at the next interrupt AND the counter used to indicate when it should next ;; be scanned is also reset. ;; ;; 2. The keyboard interrupt may go out of sync when the disc drive is used or the tape ;; is used, because these disable interrupts. ;; ;; This example only works on 664/6128. ;; firmware functions we use in this example .kl_new_fast_ticker equ &bce0 .mc_set_mode equ &bd1c .mc_wait_flyback equ &bd19 .kl_init_event equ &bcef .kl_scan_needed equ &b92a ;; this code must be in the range &4000-&bfff ;; to work correctly. ;; ;; assemble, then from BASIC: ;; ;; call &8000 org &8000 ;; wait for a screen refresh ;; we do this to synchronise our effect ;; and so that the first ticker interrupt is in a predictable place ;; The following waits for the *start* of the vsync. But will also return ;; if the vsync has already started. call mc_wait_flyback ;; two interrupts so we are outside of the vsync (one interrupt could trigger ;; within the vsync) halt halt ;; this should now wait until the start call mc_wait_flyback ;; now reset key scan to ensure it happens at each vsync ;; NOTE: This will get upset if the disc/tape are used, but so will ;; the position of our interrupts and modes. ;; So in this case the interrupt must be disabled and re-enable in the same ;; way to ensure the accurate timing. call kl_scan_needed ld a,6 ld (ticker_counter),a ld hl,modes ld (current_mode_pointer),hl ;; install interrupt ld hl,ticker_event_block ld b,%10000010 ;; asynchronous event, priority 1 ld c,&80 ;; rom select ld de,ticker_function call kl_new_fast_ticker ;; return to BASIC ret ;; this is initialised by ;; the firmware; holds runtime state of ticker interrupt .ticker_event_block defs 10 ;; this is the function called each 1/300th of a second .ticker_function push af push hl ;; The 1/300th of a second interrupt effectively splits ;; the screen into 6 sections of equal height. Each section ;; spans the entire width of the screen. ;; ;; We want to ensure that the effect is stationary so we reset ;; every 6 calls of this function. ld a,(ticker_counter) dec a ld (ticker_counter),a or a jr nz,ticker_function2 ld a,6 ld (ticker_counter),a ld hl,modes ld (current_mode_pointer),hl .ticker_function2 ;; get pointer to current mode ld hl,(current_mode_pointer) ;; get the mode ld a,(hl) ;; update pointer inc hl ;; store pointer ld (current_mode_pointer),hl ;; set mode. This will take effect at the start of the next horizontal ;; scan-line. This firmware function talks direct to the hardware. ;; The screen is not cleared and the other firmware functions for ;; plotting to the screen have no knowledge of the new mode. call mc_set_mode pop af pop hl ret .ticker_counter defb 0 .current_mode_pointer defw modes ;; The 1/300th of a second interrupt effectively splits ;; the screen into 6 sections of equal height. Each section ;; spans the entire width of the screen. ;; ;; video mode for each of the 6 sections of the screen. .modes defb 0 defb 1 defb 2 defb 0 defb 1 defb 2