;; This example shows how to have an accurate mode split with the firmware. ;; ;; Things to note: ;; - We must use express asynchronous fast ticker events, these are executed first after frame flyback interrupts. ;; - Frame flyback interrupts are executed every 50Hz of a second. express asynchronous fast ticker events are executed every 300th of a second ;; and immediately after frame flyback interrupts. ;; ;; ;; - if we used other fast ticker events, we could end up being called much later and subjected to delays from other interrupts ;; and keyboard scanning. ;; ;; ;; - The keyboard is read once every 6th interrupt but is not synchronised with a specific point on the screen. ;; On 6128/664/Plus we can call KL SCAN NEEDED after a VSYNC to synchronise the keyboard with the vsync, but this doesn't work on ;; 464. ;; ;; - When our function is called, we are already effectively working in the alternative register set. ;; ;; - We can't call MC SET MODE because this switches to alternative register set and also disables and enables interrupts. ;; Not good inside our function. ;; ;; ;; Using default CRTC register settings: ;; ;; Reg 0 = 63 (64 chars per line) ;; Reg 9 = 7 (8 lines per char) ;; Reg 4 = 38 (39 char lines per screen) ;; Reg 7 = 30 (vsync triggered on line 30) ;; ;; Also first interrupt occurs 2 lines into vsync and 52 lines after (assuming interrupt is not delayed by user, or disabling interrupt ;; for too long) ;; ;; Then first number is number of lines after vsync start, 2nd is number of lines relative to top-line of screen ;; at which the interrupt will trigger. It triggers at hsync pos defined by Reg 2 so generally happens in the middle of a line ;; ;;first at line 2 -70 ;;next at line 54 -18 ;;next at line 106 34 ;;next at line 158 86 ;;next at line 210 138 ;;next at line 262 190 ;;next at line 314 242 ;; ;; What this means is that the interrupt may not occur exactly on the line you want. You can ;; use CRTC register 7 to move the screen up/down relative and compensate a little, otherwise you will need ;; to include a delay before you change mode to allow for the position you want. ;; firmware functions we use in this example .kl_new_fast_ticker equ &bce0 .mc_wait_flyback equ &bd19 .kl_del_fast_ticker equ &bce6 org &8000 jp split_on jp split_off split_on: ;; synchronise our ticker interrupt with the vsync call mc_wait_flyback halt halt call mc_wait_flyback ld a,6 ld (ticker_counter),a ld hl,modes ld (current_mode_pointer),hl ;; install interrupt ld hl,ticker_event_block ld b,%11000001 ;; near address, express asynchronous event ld c,&80 ;; rom select ld de,ticker_function call kl_new_fast_ticker ;; return to BASIC ret split_off: ld hl,ticker_event_block call kl_del_fast_ticker 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 ;;push bc ;; 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. We need to ensure we are synced with vsync in ;; order that this works correctly. 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 ;; already in alternative register set ;; set mode now to ensure we set it early, so that other interrupt delay is not so important. ;; fetch current C register value stored in A' ex af,af' ld c,a ex af,af' ;; now read mode from table ld a,(hl) ;; clear off mode bits res 1,c res 0,c ;; combine with our wanted mode or c ;; write to hardware ld c,a res 2,a ;; ensure lower rom is active, but we don't want to modify C register ;) ld b,&7f out (c),a ;; now store back to A' ;; later firmware will also use this to set mode/rom once more, but now it is updated ;; with our new values we will get the mode we wanted. ex af,af' ld a,c ;; remember it otherwise firmware sets it back ex af,af' ;; update pointer inc hl ;; store pointer ld (current_mode_pointer),hl ;; ensure express asynchronous event is retriggered correctly ;; see SOFT968 LD HL,ticker_event_block+2 LD (HL),#00 ;;pop bc pop hl pop af 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 0 defb 0 defb 0 defb 1 defb 1