; ribos2.p65 - p65 assembly source for RIBOS2: ; Demonstration of the VIC-II raster interrupt on the Commodore 64: ; Alter the border colour in the middle part of the screen only, ; Simplified (KERNAL IRQ vector) version. ; By Chris Pressey, Cat's Eye Technologies. ; This work has been placed in the public domain. ; ----- BEGIN ribos2.p65 ----- ; This is a simplified version of ribos.p65 which uses the Commodore 64 ; KERNAL's support for interrupt handling. Whereas ribos.p65 could run ; with the KERNAL completely disabled, ribos2.p65 relies on it. ; I'll assume you've read through ribos.p65 at least once, and won't ; say much about the code that's shared between the two programs. ; Again, page references are from the 'Commodore 64 Programmer's ; Reference Guide'. .org 0 .word $C000 .org $C000 ; ----- Constants ----- ; The CIA #1 chip. .alias cia1 $dc00 ; pp. 328-331 .alias intr_ctrl cia1+$d ; "CIA Interrupt Control Register ; (Read IRQs/Write Mask)" ; The VIC-II chip. .alias vic $d000 ; Appendix G: .alias vic_ctrl vic+$11 ; "Y SCROLL MODE" .alias vic_raster vic+$12 ; "RASTER" .alias vic_intr vic+$19 ; "Interrupt Request's" (sic) .alias vic_intr_enable vic+$1a ; "Interrupt Request MASKS" .alias border_color vic+$20 ; "BORDER COLOR" ; The address at which the IRQ vector is stored. .alias cinv $0314 ; p. 319, "Vector: Hardware ; IRQ Interrupt" ; ----- Main Routine ----- ; This routine is intended to be called by the user (by, e.g., SYS 49152). ; It installs the raster interrupt handler and returns to the caller. ; Key to installing the interrupt handler is altering the IRQ service ; vector. As I learned from a careful reading of Sheldon Leemon's ; "Mapping the Commodore 64", if we wish to leave the KERNAL intact and ; operational, it gives us an easy way to do that. Whenever it handles ; an IRQ, it has to decide whether the IRQ was generated by a BRK ; instruction, or some external device issuing an interrupt request. In ; either case, it jumps indirectly through its IRQ-handling vector. We ; can simply modify this vector (at $314) to point to our routine. In ; fact, this is the "CINV" vector, which may already be familiar to you ; if you have done any modest amount of C64 machine language programming; ; it is normally called every 1/60 of a second due to the interrupt ; request generated by CIA#1 Timer B. Here we will simply be using it ; to detect when a raster interrupt has occurred, as well. ; First, disable interrupts before changing interrupt vectors. sei ; We obtain the address of the current IRQ service routine in CINV ; and save it in the variable 'saved_irq_vec'. lda cinv sta saved_irq_vec lda cinv+1 sta saved_irq_vec+1 ; We then store the address of our IRQ service routine in its place. lda #our_service_routine sta cinv+1 ; Now we must specify the raster line at which the interrupt gets called, ; setting the ninth bit to zero. lda scanline sta vic_raster lda vic_ctrl and #%01111111 sta vic_ctrl ; Then we enable the raster interrupt on the VIC-II chip. lda #$01 sta vic_intr_enable ; We read the interrupt control port of CIA #1 for good measure. lda intr_ctrl ; And we re-enable interrupts to resume normal operation -- plus our jazz. cli ; Finally, we return to the caller. rts ; ----- Raster Interrupt Service Routine ------ our_service_routine: ; First, we check to see if the current interrupt was caused by the raster. lda vic_intr sta vic_intr and #$01 cmp #$01 beq we_handle_it ; If the interrupt was not caused by the raster, we continue execution as ; normal, with the existing interrupt service routine. jmp (saved_irq_vec) we_handle_it: ; If we got here, the interrupt was caused by the raster, so we do our thing. lda border_color eor #$ff sta border_color ; Now, we make the interrupt trigger on the other scan line next time. lda scanline eor #$ff sta scanline sta vic_raster ; Return to normal operation. If we simply continue with the standard ; interrupt service routine by jumping through saved_irq_req, we will ; confuse it a bit, as it expects to be called 60 times per second, and ; continuing it here would make it occur more frequently. The result ; would be the cursor blinking more frequently and the time of day clock ; running fast. ; So, we issue a plain return from interrupt (RTI) here. But first, we ; must make sure that the state of the system is back to the way it was ; when the interrupt service routine was called. Since it can be called ; from anywhere, and since the code that was interrupted likely cares ; deeply about the values in its registers, the KERNAL routine which ; dispatches through CINV first carefully saves the contents of the ; registers on the stack. We must be equally careful about restoring ; those values before switching back to that interrupted code. pla tay pla tax pla rti ; ----- Variables ----- ; 'scanline' stores the raster line that we want the interrupt to trigger ; on; it gets loaded into the VIC-II's 'vic_raster' register. scanline: .byte %01010101 ; We also reserve space to store the address of the interrupt service ; routine that we are replacing in the IRQ vector, so that we can transfer ; control to it at the end of our routine. .space saved_irq_vec 2 ; ----- END of ribos2.p65 -----