git @ Cat's Eye Technologies SixtyPical / master eg / c64 / ribos / ribos2.p65
master

Tree @master (Download .tar.gz)

ribos2.p65 @masterraw · history · blame

; 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.

; SPDX-FileCopyrightText:  Chris Pressey, the author of this work, has dedicated it to the public domain.
; For more information, please refer to <https://unlicense.org/>
; SPDX-License-Identifier: Unlicense

; ----- 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
                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 -----