mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-01-24 17:31:46 +00:00
Add SixtyPical translations of Ribos and "The PETulant Cursor".
This commit is contained in:
parent
ed917566ea
commit
42864bf125
@ -11,6 +11,8 @@ History of SixtyPical
|
||||
insisting that `goto` only appears in it.
|
||||
* Fixed bug when `--prelude` option was missing.
|
||||
* Fixed bug when reporting line numbers of scanner-level syntax errors.
|
||||
* Translated the small demo project Ribos and "The PETulant Cursor"
|
||||
to SixtyPical and added them to the `eg/c64/` section of the repo.
|
||||
|
||||
0.13
|
||||
----
|
||||
|
14
eg/c64/README.md
Normal file
14
eg/c64/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
This directory contains SixtyPical example programs
|
||||
specifically for the Commodore 64.
|
||||
|
||||
Subdirectories of note:
|
||||
|
||||
* [demo-game](demo-game/): a little game-like program written as a
|
||||
"can we write something you'd see in practice?" test case for SixtyPical.
|
||||
* [ribos](ribos/): a well-commented example of a C64 raster interrupt
|
||||
routine. Originally written with the P65 assembler (now Ophis).
|
||||
The second version of it has been translated to SixtyPical.
|
||||
* [petulant](petulant/) -- "The PETulant Cursor", a tiny (44 bytes)
|
||||
"display hack". Originally written in the late 80's. Rewritten with
|
||||
the P65 assembler (now Ophis) and re-released April 1, 2008 (a
|
||||
hint as to its nature). Translated to SixtyPical, it's 48 bytes.
|
9
eg/c64/petulant/build.sh
Executable file
9
eg/c64/petulant/build.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
sixtypical --output-format=prg --origin=0x02a7 petulant.60p > petulant-60p.prg
|
||||
if [ "x$COMPARE" != "x" ]; then
|
||||
dcc6502 petulant.prg > petulant.prg.disasm.txt
|
||||
dcc6502 petulant-60p.prg > petulant-60p.prg.disasm.txt
|
||||
paste petulant.prg.disasm.txt petulant-60p.prg.disasm.txt | pr -t -e24
|
||||
rm -f *.disasm.txt
|
||||
fi
|
BIN
eg/c64/petulant/petulant-60p.prg
Normal file
BIN
eg/c64/petulant/petulant-60p.prg
Normal file
Binary file not shown.
54
eg/c64/petulant/petulant.60p
Normal file
54
eg/c64/petulant/petulant.60p
Normal file
@ -0,0 +1,54 @@
|
||||
// petulant.60p - The PETulant Cursor, a "display hack" for the Commodore 64
|
||||
// Originally written by Chris Pressey sometime in the late 1980's
|
||||
// Rewritten in P65 assembly and released March 2008
|
||||
// Rewritten in SixtyPical in March 2018
|
||||
// This work is part of the public domain.
|
||||
|
||||
// ----- Types -----
|
||||
|
||||
typedef routine
|
||||
inputs border, blnon, color, background
|
||||
outputs border
|
||||
trashes a, z, n
|
||||
irq_handler
|
||||
|
||||
// ----- Addresses -----
|
||||
|
||||
vector irq_handler cinv @ $314 // hw irq interrupt, 60x per second
|
||||
|
||||
byte blnon @ $cf // was last cursor blink on or off?
|
||||
byte color @ $286 // current foreground colour for text
|
||||
|
||||
byte border @ $d020 // colour of border of the screen
|
||||
byte background @ $d021 // colour of background of the screen
|
||||
|
||||
// ----- Variables -----
|
||||
|
||||
vector irq_handler save_cinv
|
||||
|
||||
// ----- Interrupt Handler -----
|
||||
|
||||
define our_cinv irq_handler
|
||||
{
|
||||
ld a, blnon
|
||||
if not z {
|
||||
ld a, color
|
||||
} else {
|
||||
ld a, background
|
||||
}
|
||||
st a, border
|
||||
goto save_cinv
|
||||
}
|
||||
|
||||
// ----- Main Program -----
|
||||
|
||||
define main routine
|
||||
inputs cinv
|
||||
outputs cinv, save_cinv
|
||||
trashes a, n, z
|
||||
{
|
||||
with interrupts off {
|
||||
copy cinv, save_cinv
|
||||
copy our_cinv, cinv
|
||||
}
|
||||
}
|
52
eg/c64/petulant/petulant.p65
Normal file
52
eg/c64/petulant/petulant.p65
Normal file
@ -0,0 +1,52 @@
|
||||
; petulant.p65 - The PETulant Cursor, a "display hack" for the Commodore 64
|
||||
; Originally written by Chris Pressey sometime in the late 1980's
|
||||
; Rewritten in P65 assembly and released March 2008
|
||||
; This work is part of the public domain.
|
||||
|
||||
; ----- BEGIN petulant.p65 -----
|
||||
|
||||
; PRG file header
|
||||
|
||||
.org 0
|
||||
.word $02a7
|
||||
.org $02a7
|
||||
|
||||
; ----- Constants -----
|
||||
|
||||
.alias cinv $0314 ; hw irq interrupt, 60x per second
|
||||
.alias blnon $cf ; was last cursor blink on or off?
|
||||
.alias color $286 ; current foreground colour for text
|
||||
|
||||
.alias vic $d000 ; base address of VIC-II chip
|
||||
.alias border vic+$20 ; colour of border of the screen
|
||||
.alias background vic+$21 ; colour of background of the screen
|
||||
|
||||
; ----- Start of Program -----
|
||||
|
||||
start: sei ; disable interrupts
|
||||
lda cinv ; save low byte of existing handler
|
||||
sta savecinv
|
||||
lda cinv+1 ; save high byte
|
||||
sta savecinv+1
|
||||
|
||||
lda #<newcinv ; install new interrupt handler
|
||||
sta cinv
|
||||
lda #>newcinv
|
||||
sta cinv+1
|
||||
|
||||
cli ; re-enable interrupts
|
||||
rts
|
||||
|
||||
newcinv: lda blnon ; is the cursor on?
|
||||
beq cursor_off
|
||||
cursor_on: lda color ; yes, get its colour
|
||||
jmp egress
|
||||
cursor_off: lda background ; no, get the background colour
|
||||
egress: sta border ; colour the border
|
||||
jmp (savecinv) ; continue pre-existing interrupt handler
|
||||
|
||||
; ----- Uninitialized Data -----
|
||||
|
||||
.space savecinv 2
|
||||
|
||||
; ----- END of petulant.p65 -----
|
1
eg/c64/petulant/petulant.prg
Normal file
1
eg/c64/petulant/petulant.prg
Normal file
@ -0,0 +1 @@
|
||||
§x<03>Ó<03>Ô©À<C2A9>©<02>X`¥Ïð†LÍ!Ð<> ÐlÓ
|
32
eg/c64/petulant/readme.txt
Normal file
32
eg/c64/petulant/readme.txt
Normal file
@ -0,0 +1,32 @@
|
||||
The PETulant Cursor
|
||||
===================
|
||||
|
||||
This is a tiny (44 bytes long) machine-language demo for the Commodore 64,
|
||||
somewhat in the style of later "display hacks" for the Amiga -- surprising
|
||||
and silly ways to play with the user interface.
|
||||
|
||||
So as not to not spoil the fun, try running it before reading the source.
|
||||
|
||||
To run it, make the file PETULANT.PRG accessible to your favourite Commodore
|
||||
64 (or Commodore 64 emulator) in your favourite way, then
|
||||
|
||||
LOAD "PETULANT.PRG",8,1
|
||||
SYS 679
|
||||
|
||||
For further fun, try changing the text colour (hold the C= or CTRL key while
|
||||
pressing a number) or the background colour (POKE 53281 with a number from
|
||||
0 to 15) while it is running.
|
||||
|
||||
I must have originally wrote this sometime in the late 80's. I disassembled
|
||||
and rewrote it (to make it play more nicely with existing interrupt handlers)
|
||||
in 2008.
|
||||
|
||||
Who knows -- this could actually be useful, in a ridiculously minor way: it
|
||||
makes it much more obvious when control has returned to BASIC immediate mode
|
||||
(e.g., when a program has finished loading.)
|
||||
|
||||
Enjoy!
|
||||
|
||||
-Chris Pressey
|
||||
April 1, 2008
|
||||
Chicago, IL
|
9
eg/c64/ribos/build.sh
Executable file
9
eg/c64/ribos/build.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
sixtypical --output-format=prg --origin=0xc000 ribos2.60p > ribos2-60p.prg
|
||||
if [ "x$COMPARE" != "x" ]; then
|
||||
dcc6502 ribos2.prg > ribos2.prg.disasm.txt
|
||||
dcc6502 ribos2-60p.prg > ribos2-60p.prg.disasm.txt
|
||||
paste ribos2.prg.disasm.txt ribos2-60p.prg.disasm.txt | pr -t -e24
|
||||
rm -f *.disasm.txt
|
||||
fi
|
79
eg/c64/ribos/readme.txt
Normal file
79
eg/c64/ribos/readme.txt
Normal file
@ -0,0 +1,79 @@
|
||||
Ribos
|
||||
=====
|
||||
|
||||
This little demo is intended to be a well-commented example of how to
|
||||
program a raster interrupt in 6502 assembly language on a Commodore 64.
|
||||
|
||||
This (r)aster (i)nterrupt changes the colour of a region of the
|
||||
(bo)rder of the C64 (s)creen; thus, RIBOS. Also, it's the name of a
|
||||
planet from Dr. Who, if that means anything.
|
||||
|
||||
|
||||
How to Run the Demo (using the VICE C64 emulator, x64)
|
||||
------------------------------------------------------
|
||||
|
||||
0. Obtain VICE from http://www.viceteam.org/, install it,
|
||||
and run x64
|
||||
|
||||
1. Mount this project's directory as drive 8:
|
||||
|
||||
Make sure
|
||||
Peripheral settings > Device #8 > Enable IEC Device
|
||||
is checked, then select
|
||||
Peripheral settings > Device #8 > File system directory...
|
||||
and enter the path to the project directory.
|
||||
|
||||
2. LOAD "RIBOS.PRG",8,1
|
||||
|
||||
3. SYS 49152
|
||||
|
||||
4. You should see the colour of the middle of the border change
|
||||
while you get a READY. prompt and can continue working.
|
||||
|
||||
|
||||
How to Assemble the Program (using the p65 assembler)
|
||||
-----------------------------------------------------
|
||||
|
||||
0. Obtain p65 from http://hkn.berkeley.edu/~mcmartin/P65/
|
||||
(I used p65-Perl version 1.1) and install it somewhere
|
||||
on your path. If your Perl interpreter isn't located at
|
||||
/usr/bin/perl, change the first line of p65 appropriately.
|
||||
|
||||
1. p65 -v -t -b ribos.p65 ribos.prg
|
||||
|
||||
The switches aren't necessary, but they make it feel like
|
||||
p65 is doing something difficult and important. It also
|
||||
isn't necessary to add the '.prg' extension on the end of
|
||||
the binary object's filename, since it will appear as a
|
||||
PRG file to VICE anyway, but it's nice as a reminder when
|
||||
you're working in a modern operating system.
|
||||
|
||||
2. Follow the steps under 'How to Run this Demo' to see that
|
||||
it worked.
|
||||
|
||||
|
||||
How it Works
|
||||
------------
|
||||
|
||||
Read the source! I've tried to make it very well-commented,
|
||||
including what happens when you leave out some steps.
|
||||
|
||||
I wrote this demo because it was a long time since I had done any C64
|
||||
programming, and, having just obtained a copy of the 'Commodore 64
|
||||
Programmer's Reference Guide,' I wanted to code something challenging,
|
||||
yet not too involved. I remembered raster interrupts as one of those
|
||||
quintessential C64 low-level graphics tricks, so I decided to try my
|
||||
hand at that. Looking around on the Internet, I found this page:
|
||||
|
||||
http://everything2.com/index.pl?node_id=79254
|
||||
|
||||
Although it's a fairly detailed description, it took me a couple of
|
||||
frustrating hours to implement it successfully - both the everything2
|
||||
article and the Reference Guide were pretty muddy on a couple of
|
||||
points. What I learned in the process is written into the comments.
|
||||
|
||||
Happy raster-interrupting!
|
||||
|
||||
-Chris Pressey
|
||||
April 10, 2007
|
||||
Vancouver, BC
|
291
eg/c64/ribos/ribos.p65
Normal file
291
eg/c64/ribos/ribos.p65
Normal file
@ -0,0 +1,291 @@
|
||||
; ribos.p65 - p65 assembly source for RIBOS:
|
||||
; Demonstration of the VIC-II raster interrupt on the Commodore 64:
|
||||
; Alter the border colour in the middle part of the screen only.
|
||||
; Original (hardware IRQ vector) version.
|
||||
; By Chris Pressey, Cat's Eye Technologies.
|
||||
; This work has been placed in the public domain.
|
||||
|
||||
; ----- BEGIN ribos.p65 -----
|
||||
|
||||
; This source file is intented to be assembled to produce a PRG file
|
||||
; which can be loaded into the C64's memory from a peripheral device.
|
||||
; All C64 PRG files start with a 16-bit word which represents the
|
||||
; location in memory to which they will be loaded. We can provide this
|
||||
; using p65 directives as follows:
|
||||
|
||||
.org 0
|
||||
.word $C000
|
||||
|
||||
; Now the actual assembly starts (at memory location 49152.)
|
||||
|
||||
.org $C000
|
||||
|
||||
; ----- Constants -----
|
||||
|
||||
; We first define some symbolic constants to add some clarity to our
|
||||
; references to hardware registers and other locations in memory.
|
||||
; Descriptions of the registers follow those given in the 'Commodore 64
|
||||
; Programmer's Reference Guide'.
|
||||
|
||||
; 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 irq_vec $fffe ; p. 411
|
||||
|
||||
; The zero-page address of the 6510's I/O register.
|
||||
|
||||
.alias io_ctrl $01 ; p. 310, "6510 On-Chip 8-Bit
|
||||
; Input/Output Register"
|
||||
|
||||
; KERNAL and BASIC ROMs, p. 320
|
||||
|
||||
.alias kernal $e000
|
||||
.alias basic $a000
|
||||
|
||||
; Zero-page addresses that the memory-copy routine uses for
|
||||
; scratch space: "FREKZP", p. 316
|
||||
|
||||
.alias zp $fb
|
||||
.alias stop_at $fd
|
||||
|
||||
; ----- 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. However, under normal circumstances, the address at which
|
||||
; this vector is stored ($ffffe) maps to the C64's KERNAL ROM, which
|
||||
; cannot be changed. So, in order to alter the vector, we must enable
|
||||
; the RAM that underlies the ROM (i.e. the RAM that maps to the same
|
||||
; address space as the KERNAL ROM.) If we were writing a bare-metal
|
||||
; game, and didn't need any KERNAL routines or support, we could just
|
||||
; switch it off. But for this demo, we'd like the raster effect to
|
||||
; occur in the background as we use BASIC and whatnot, so we need to
|
||||
; continue to have access to the KERNAL ROM. So what we do is copy the
|
||||
; KERNAL ROM to the underlying RAM, then switch the RAM for the ROM.
|
||||
|
||||
jsr copy_rom_to_ram
|
||||
|
||||
; Interrupts can occur at any time. If one were to occur while we were
|
||||
; changing the interrupt vector - for example, after we have stored the
|
||||
; low byte of the address but before we have stored the high byte -
|
||||
; unpredictable behaviour would result. To be safe, we disable interrupts
|
||||
; with the 'sei' instruction before changing anything.
|
||||
|
||||
sei
|
||||
|
||||
; We obtain the address of the current IRQ service routine and save it
|
||||
; in the variable 'saved_irq_vec'.
|
||||
|
||||
lda irq_vec ; save low byte
|
||||
sta saved_irq_vec
|
||||
lda irq_vec+1 ; save high byte
|
||||
sta saved_irq_vec+1
|
||||
|
||||
; We then store the address of our IRQ service routine in its place.
|
||||
|
||||
lda #<our_service_routine
|
||||
sta irq_vec
|
||||
lda #>our_service_routine
|
||||
sta irq_vec+1
|
||||
|
||||
; Now we must specify the raster line at which the interrupt gets called.
|
||||
|
||||
lda scanline
|
||||
sta vic_raster
|
||||
|
||||
; Note that the position of the raster line is given by a 9-bit value,
|
||||
; and we can't just assume that, because the raster line we want is less
|
||||
; than 256, that the high bit will automatically be set to zero, because
|
||||
; it won't. We have to explicitly set it high or low. It is found as
|
||||
; the most significant bit of a VIC-II control register which has many
|
||||
; different functions, so we must be careful to preserve all other bits.
|
||||
|
||||
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
|
||||
|
||||
; The article at everything2 suggests that we read the interrupt control
|
||||
; port of the CIA #1 chip, presumably to acknowledge any pending IRQ and
|
||||
; avoid the problem of having some sort of lockup due to a spurious IRQ.
|
||||
; I've tested leaving this out, and the interrupt handler still seems get
|
||||
; installed alright. But, I haven't tested it very demandingly, and it's
|
||||
; likely to open up a race condition that I just haven't encountered (much
|
||||
; like if we were to forget to execute the 'sei' instruction, above.)
|
||||
; So, to play it safe, we read the port here.
|
||||
|
||||
lda intr_ctrl
|
||||
|
||||
; We re-enable interrupts to resume normal operation - normal, that is,
|
||||
; except that our raster interrupt service routine will be called the next
|
||||
; time the raster reaches the line stored in the 'vic_raster' register of
|
||||
; the VIC-II chip.
|
||||
|
||||
cli
|
||||
|
||||
; Finally, we return to the caller.
|
||||
|
||||
rts
|
||||
|
||||
; ----- Raster Interrupt Service Routine ------
|
||||
|
||||
our_service_routine:
|
||||
|
||||
; This is an interrupt service routine (a.k.a. interrupt handler,) and as
|
||||
; such, it can be called from anywhere. Since the code that was interrupted
|
||||
; likely cares deeply about the values in its registers, we must be careful
|
||||
; to save any that we change, and restore them before switching back to it.
|
||||
; In this case, we only affect the processor flags and the accumulator, so
|
||||
; we push them onto the stack.
|
||||
|
||||
php
|
||||
pha
|
||||
|
||||
; The interrupt service routine on the Commodore 64 is very general-purpose,
|
||||
; and may be invoked by any number of different kinds of interrupts. We,
|
||||
; however, only care about a certain kind - the VIC-II's raster interrupt.
|
||||
; We check to see if the current interrupt was caused by the raster by
|
||||
; looking at the low bit of the VIC-II interrupt register. Note that we
|
||||
; immediately store back the value found there before testing it. This is
|
||||
; to acknowledge to the VIC-II chip that we got the interrupt. If we don't
|
||||
; do this, it won't send us another interrupt next time.
|
||||
|
||||
lda vic_intr
|
||||
sta vic_intr
|
||||
and #$01
|
||||
cmp #$01
|
||||
beq we_handle_it
|
||||
|
||||
; If the interrupt was not caused by the raster, we restore the values
|
||||
; of the registers from the stack, and continue execution as normal with
|
||||
; the existing interrupt service routine.
|
||||
|
||||
pla
|
||||
plp
|
||||
jmp (saved_irq_vec)
|
||||
|
||||
we_handle_it:
|
||||
|
||||
; If we got here, the interrupt _was_ caused by the raster. So, we get
|
||||
; to do our thing. To keep things simple, we just invert the border colour.
|
||||
|
||||
lda border_color
|
||||
eor #$ff
|
||||
sta border_color
|
||||
|
||||
; Now, we make the interrupt trigger on a different scan line so that we'll
|
||||
; invert the colour back to normal lower down on the screen.
|
||||
|
||||
lda scanline
|
||||
eor #$ff
|
||||
sta scanline
|
||||
sta vic_raster
|
||||
|
||||
; Restore the registers that we saved at the beginning of this routine.
|
||||
|
||||
pla
|
||||
plp
|
||||
|
||||
; Return to normal operation. Note that we must issue an 'rti' instruction
|
||||
; here, not 'rts', as we are returning from an interrupt.
|
||||
|
||||
rti
|
||||
|
||||
|
||||
; ----- Utility Routine: copy KERNAL ROM to underlying RAM -----
|
||||
|
||||
copy_rom_to_ram:
|
||||
|
||||
; This is somewhat more involved than I let on above. The memory mapping
|
||||
; facilities of the C64 are a bit convoluted. The Programmer's Reference
|
||||
; Guide states on page 261 that the way to map out the KERNAL ROM, and
|
||||
; map in the RAM underlying it, is to set the HIRAM signal on the 6510's
|
||||
; I/O line (which is memory-mapped to address $0001) to 0. This is true.
|
||||
; However, it is not the whole story: setting HIRAM to 0 *also* maps out
|
||||
; BASIC ROM and maps in the RAM underlying *it*. I suppose this makes
|
||||
; sense from a design point of view; after all, BASIC uses the KERNAL, so
|
||||
; there wouldn't be much sense leaving it mapped when the KERNAL is mapped
|
||||
; out. Anyway, what this means for us is that we must copy both of these
|
||||
; ROMs to their respective underlying RAMs if we want to survive returning
|
||||
; to BASIC.
|
||||
|
||||
ldx #>basic
|
||||
ldy #$c0
|
||||
jsr copy_block
|
||||
|
||||
ldx #>kernal
|
||||
ldy #$00
|
||||
jsr copy_block
|
||||
|
||||
; To actually substitute the RAM for the ROM in the memory map, we
|
||||
; set HIRAM (the second least significant bit) to 0.
|
||||
|
||||
lda io_ctrl
|
||||
and #%11111101
|
||||
sta io_ctrl
|
||||
|
||||
rts
|
||||
|
||||
|
||||
; ----- Utility Routine: copy a ROM memory block to the underlying RAM -----
|
||||
|
||||
; Input: x register = high byte of start address (low byte = #$00)
|
||||
; y register = high byte of end address (stops at address $yy00 - 1)
|
||||
|
||||
; This subroutine is a fairly straightforward memory copy loop. A somewhat
|
||||
; counter-intuitive feature is that we immediately store each byte in the
|
||||
; same location where we just read it from. We can do this because, even
|
||||
; when the KERNAL or BASIC ROM is mapped in, writes to those locations still
|
||||
; go to the underlying RAM.
|
||||
|
||||
copy_block: stx zp+1
|
||||
sty stop_at
|
||||
ldy #$00
|
||||
sty zp
|
||||
|
||||
copy_loop: lda (zp), y
|
||||
sta (zp), y
|
||||
iny
|
||||
cpy #$00
|
||||
bne copy_loop
|
||||
ldx zp+1
|
||||
inx
|
||||
stx zp+1
|
||||
cpx stop_at
|
||||
bne copy_loop
|
||||
rts
|
||||
|
||||
; ----- 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 ribos.p65 -----
|
BIN
eg/c64/ribos/ribos.png
Normal file
BIN
eg/c64/ribos/ribos.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
BIN
eg/c64/ribos/ribos.prg
Normal file
BIN
eg/c64/ribos/ribos.prg
Normal file
Binary file not shown.
BIN
eg/c64/ribos/ribos2-60p.prg
Normal file
BIN
eg/c64/ribos/ribos2-60p.prg
Normal file
Binary file not shown.
103
eg/c64/ribos/ribos2.60p
Normal file
103
eg/c64/ribos/ribos2.60p
Normal file
@ -0,0 +1,103 @@
|
||||
// ribos2.60p - SixtyPical 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.
|
||||
|
||||
// For comments, see ribos2.p65.
|
||||
|
||||
// ----- Types -----
|
||||
|
||||
typedef routine
|
||||
inputs border_color, vic_intr, scanline
|
||||
outputs border_color, vic_intr, scanline
|
||||
trashes a, z, n, c, vic_raster
|
||||
irq_handler
|
||||
|
||||
// ----- Addresses -----
|
||||
|
||||
// The CIA #1 chip.
|
||||
|
||||
byte cia1 @ $dc00 // pp. 328-331
|
||||
byte intr_ctrl @ $dc0d // "CIA Interrupt Control Register
|
||||
// (Read IRQs/Write Mask)"
|
||||
|
||||
// The VIC-II chip.
|
||||
|
||||
byte vic @ $d000 // Appendix G:
|
||||
byte vic_ctrl @ $d011 // "Y SCROLL MODE"
|
||||
byte vic_raster @ $d012 // "RASTER"
|
||||
byte vic_intr @ $d019 // "Interrupt Request's" (sic)
|
||||
byte vic_intr_enable @ $d01a // "Interrupt Request MASKS"
|
||||
byte border_color @ $d020 // "BORDER COLOR"
|
||||
|
||||
// The address at which the IRQ vector is stored.
|
||||
|
||||
vector irq_handler cinv @ $314 // p. 319, "Vector: Hardware
|
||||
// IRQ Interrupt"
|
||||
|
||||
// ----- Variables -----
|
||||
|
||||
vector irq_handler saved_irq_vec
|
||||
byte scanline : 85 // %01010101
|
||||
|
||||
// ----- Externals ------
|
||||
|
||||
// An address in ROM which contains "PLA TAY PLA TAX RTI".
|
||||
// ribos2.p65 just contains these instructions itself, but
|
||||
// generating them as part of a SixtyPical program would not
|
||||
// be practical. So we just jump to this location instead.
|
||||
|
||||
routine pla_tay_pla_tax_pla_rti
|
||||
inputs a
|
||||
trashes a
|
||||
@ $EA81
|
||||
|
||||
// ----- Interrupt Handler -----
|
||||
|
||||
define our_service_routine irq_handler
|
||||
{
|
||||
ld a, vic_intr
|
||||
st a, vic_intr
|
||||
and a, 1
|
||||
cmp a, 1
|
||||
if not z {
|
||||
goto saved_irq_vec
|
||||
} else {
|
||||
ld a, border_color
|
||||
xor a, $ff
|
||||
st a, border_color
|
||||
|
||||
ld a, scanline
|
||||
xor a, $ff
|
||||
st a, scanline
|
||||
st a, vic_raster
|
||||
|
||||
trash vic_raster
|
||||
|
||||
goto pla_tay_pla_tax_pla_rti
|
||||
}
|
||||
}
|
||||
|
||||
// ----- Main Program -----
|
||||
|
||||
define main routine
|
||||
inputs cinv, scanline, vic_ctrl, intr_ctrl
|
||||
outputs cinv, saved_irq_vec, vic_ctrl, vic_intr_enable
|
||||
trashes a, n, z, vic_raster
|
||||
{
|
||||
with interrupts off {
|
||||
copy cinv, saved_irq_vec
|
||||
copy our_service_routine, cinv
|
||||
|
||||
ld a, scanline
|
||||
st a, vic_raster
|
||||
ld a, vic_ctrl
|
||||
and a, 127
|
||||
st a, vic_ctrl
|
||||
ld a, 1
|
||||
st a, vic_intr_enable
|
||||
ld a, intr_ctrl
|
||||
}
|
||||
}
|
179
eg/c64/ribos/ribos2.p65
Normal file
179
eg/c64/ribos/ribos2.p65
Normal file
@ -0,0 +1,179 @@
|
||||
; 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
|
||||
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 -----
|
BIN
eg/c64/ribos/ribos2.prg
Normal file
BIN
eg/c64/ribos/ribos2.prg
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user