SixtyPical/eg/c64/ribos/ribos2.p65

182 lines
6.1 KiB
Plaintext

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