Initial commit: C64 project skeleton with oscar64 submodule
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# Interrupts (IRQ / NMI / BRK)
|
||||
|
||||
Source: https://www.c64-wiki.com/wiki/Interrupt
|
||||
|
||||
The C64 with its 6510 CPU supports two types of interrupt: **IRQ** (Interrupt Request, maskable) and **NMI** (Non-Maskable Interrupt). The CPU has the option of ignoring IRQ (via the I flag in the status register, set with `SEI`, cleared with `CLI`), but must respond to NMI.
|
||||
|
||||
The first thing the CPU does for either is push the program counter and status register onto the stack. Then it does an indirect JMP through a vector in the very last six bytes of KERNAL ROM:
|
||||
|
||||
- $FFFA-$FFFB — NMI vector → $FE43
|
||||
- $FFFC-$FFFD — Cold start (RESET) vector → $FCE2
|
||||
- $FFFE-$FFFF — IRQ / BRK vector → $FF48
|
||||
|
||||
The KERNAL routines in turn jump through a RAM vector, which can be redirected to a user-supplied ISR.
|
||||
|
||||
## IRQ flow
|
||||
|
||||
1. CPU pushes PC and P, jumps via ($FFFE) to $FF48.
|
||||
2. $FF48 pushes A, X, Y onto the stack:
|
||||
|
||||
```asm
|
||||
.C:ff48 48 PHA ; push A
|
||||
.C:ff49 8A TXA
|
||||
.C:ff4a 48 PHA ; push X
|
||||
.C:ff4b 98 TYA
|
||||
.C:ff4c 48 PHA ; push Y
|
||||
; Stack now (top to bottom): PC_hi, PC_lo, P, A, X, Y
|
||||
.C:ff4d BA TSX
|
||||
.C:ff4e BD 04 01 LDA $0104,X ; load saved P
|
||||
.C:ff51 29 10 AND #$10 ; test BREAK flag
|
||||
.C:ff53 F0 03 BEQ $FF58
|
||||
.C:ff55 6C 16 03 JMP ($0316) ; BRK vector
|
||||
.C:ff58 6C 14 03 JMP ($0314) ; IRQ vector
|
||||
```
|
||||
|
||||
3. Default IRQ vector at $0314-$0315 = $EA31 (KERNAL IRQ routine: maintains jiffy clock, scans keyboard for RUN/STOP, blinks cursor).
|
||||
4. KERNAL exits via $EA81 (pops A/X/Y and RTI).
|
||||
|
||||
So a custom IRQ routine has the stack laid out as: PC_hi, PC_lo, P, A, X, Y (top to bottom). Your ISR ends with `PLA : TAY : PLA : TAX : PLA : RTI` (or just `JMP $EA81` if you want the KERNAL to handle the standard jobs first).
|
||||
|
||||
## NMI flow
|
||||
|
||||
1. CPU pushes PC and P, jumps via ($FFFA) to $FE43.
|
||||
2. NMI routine sets the I flag (masking further IRQs), saves A/X/Y, then jumps via $0318-$0319 (NMI RAM vector).
|
||||
3. Default NMI vector at $0318-$0319 = $FE47.
|
||||
4. If a cartridge is present, NMI is handed to it via vector at $8002-$8003.
|
||||
5. RUN/STOP+RESTORE triggers NMI which is treated as a soft reset (BASIC warm start).
|
||||
|
||||
## Interrupt sources on the C64
|
||||
|
||||
- **CIA 1** (IRQ): Timer A/B underflow, TOD=alarm, serial byte complete, FLAG pin (cassette)
|
||||
- **CIA 2** (NMI): mostly RS-232 via FLAG pin, RESTORE key
|
||||
- **VIC-II** (IRQ): raster match, sprite-sprite collision, sprite-data collision, light pen
|
||||
|
||||
## Tricks
|
||||
|
||||
- Disable interrupts in BASIC (kills keyboard): `POKE 56334, PEEK(56334) AND 254`
|
||||
- Re-enable: `POKE 56334, PEEK(56334) OR 1`
|
||||
- RUN/STOP+RESTORE: triggers NMI → soft reset
|
||||
@@ -0,0 +1,57 @@
|
||||
# Joysticks (Control Ports)
|
||||
|
||||
Source: https://www.c64-wiki.com/wiki/Joystick
|
||||
|
||||
A joystick is a gaming control device. The C64 uses the standard first seen on the Atari 2600 — eight directions and one fire button. The stick mechanically activates four switches (up/down/left/right); pushing diagonally activates two. Some joysticks have two fire buttons but they appear identical to software.
|
||||
|
||||
The switches and button connect to the CIA #1 ports A and B (in parallel with the keyboard matrix), which is why a joystick — especially on port 1 — can cause the machine to "type" characters when you operate it.
|
||||
|
||||
## Reading a joystick
|
||||
|
||||
- Port #1 (right port): read via $DC01 (CIA 1 PRB)
|
||||
- Port #2 (left port): read via $DC00 (CIA 1 PRA)
|
||||
|
||||
In each byte, the bits are active-low (0 = pressed):
|
||||
|
||||
- Bit 0 (1) — Up
|
||||
- Bit 1 (2) — Down
|
||||
- Bit 2 (4) — Left
|
||||
- Bit 3 (8) — Right
|
||||
- Bit 4 (16) — Fire
|
||||
|
||||
## Typical values (rest position, no buttons)
|
||||
|
||||
| Position | Port 1 ($DC01) | Port 2 ($DC00) | +Fire (Port 1) | +Fire (Port 2) |
|
||||
|----------|----------------|----------------|----------------|----------------|
|
||||
| middle | 255 | 127 | 239 | 111 |
|
||||
| up | 254 | 126 | 238 | 110 |
|
||||
| down | 253 | 125 | 237 | 109 |
|
||||
| left | 251 | 123 | 235 | 107 |
|
||||
| right | 247 | 119 | 231 | 103 |
|
||||
| up+left | 250 | 122 | 234 | 106 |
|
||||
| up+right | 246 | 118 | 230 | 102 |
|
||||
| down+left| 249 | 121 | 233 | 105 |
|
||||
| down+right|245 | 117 | 229 | 101 |
|
||||
|
||||
## Keyboard collision
|
||||
|
||||
Keyboard scanning uses the same CIA port bits, so reading the joystick will "see" pressed keys. To disable the keyboard while polling: `POKE 56322, 224` (write %11100000 to CIA 1 DDRA so PRA pins are inputs, leaving only the rows set as output — wait, that is the opposite of "disable keyboard". The actual recipe to disable keyboard scanning is to disable CIA 1 interrupts or to set all keyboard columns to inputs).
|
||||
|
||||
The simpler approach is to mask out the keyboard bits and only test the low 5 bits of the joystick.
|
||||
|
||||
## Analog inputs
|
||||
|
||||
The control ports also provide +5V and two analog lines (designed for paddles) — the SID reads these as 8-bit values via $D419 (X) and $D41A (Y).
|
||||
|
||||
## Sample BASIC polling
|
||||
|
||||
```basic
|
||||
10 J = NOT PEEK(56321)
|
||||
20 PRINT CHR$(147);"JOYSTICKTEST"
|
||||
30 IF (J AND 1) THEN PRINT "1-U ";
|
||||
35 IF (J AND 2) THEN PRINT "1-D ";
|
||||
40 IF (J AND 4) THEN PRINT "1-L ";
|
||||
45 IF (J AND 8) THEN PRINT "1-R ";
|
||||
50 IF (J AND 16) THEN PRINT "1-F ";
|
||||
55 GOTO 10
|
||||
```
|
||||
@@ -0,0 +1,96 @@
|
||||
# Raster Interrupts
|
||||
|
||||
Source: https://www.c64-wiki.com/wiki/Raster_interrupt
|
||||
|
||||
A raster interrupt is an interrupt trigger signal that the VIC-II can supply, if desired, to the CPU whenever the raster in the VIC's video signal reaches a specific line. With machine code programming, this mechanism can be exploited to perform many kinds of VIC "trickery" — having both text and high-res graphics on screen simultaneously, displaying more than eight hardware-supported sprites at once. This is heavily used on the C64/C128 for computer games and demos.
|
||||
|
||||
The Atari 800, MSX, and Amstrad CPC also support raster interrupts. The 80-column mode on the C128/VDC cannot.
|
||||
|
||||
## Setting up a raster interrupt
|
||||
|
||||
By default, the system is set up to receive timer-based signals from CIA-1's Timer A. To use the VIC raster interrupt:
|
||||
|
||||
```asm
|
||||
Init SEI ; disable IRQ
|
||||
LDA #%01111111
|
||||
STA $DC0D ; switch off interrupt signals from CIA-1
|
||||
|
||||
AND $D011 ; clear MSB of VIC raster
|
||||
STA $D011
|
||||
|
||||
STA $DC0D ; acknowledge pending CIA-1 IRQs
|
||||
STA $DD0D ; acknowledge pending CIA-2 NMIs
|
||||
|
||||
LDA #210 ; set raster line where interrupt shall occur
|
||||
STA $D012
|
||||
|
||||
LDA #<Irq
|
||||
STA $0314 ; set IRQ vector
|
||||
LDA #>Irq
|
||||
STA $0315
|
||||
|
||||
LDA #%00000001
|
||||
STA $D01A ; enable raster interrupt
|
||||
|
||||
CLI ; re-enable IRQ
|
||||
RTS
|
||||
```
|
||||
|
||||
Note: enabling raster interrupts from the VIC takes place *after* setting up everything else. The routine starts with `SEI` because if the interrupt is enabled before e.g. the vector is re-directed, an interrupt may occur while the vector is being altered, sending the CPU to a "random" address and crashing the system.
|
||||
|
||||
## Single ISR example (wiggle border)
|
||||
|
||||
```asm
|
||||
Irq LDA #$07
|
||||
STA $D020 ; border = yellow
|
||||
|
||||
LDX #$90 ; delay ~half a millisecond
|
||||
Pause: DEX
|
||||
BNE Pause
|
||||
|
||||
LDA #$00
|
||||
STA $D020 ; border = black
|
||||
|
||||
ASL $D019 ; acknowledge raster IRQ
|
||||
JMP $EA31 ; into KERNAL standard ISR (handles cursor blink, etc.)
|
||||
```
|
||||
|
||||
The yellow stripe across the border is exactly 8 raster lines wide, set by the pause loop length.
|
||||
|
||||
## Multiple raster ISRs (split-screen: hires top, text bottom)
|
||||
|
||||
The "concatenated" pattern — two routines handling two different raster lines, each setting up the next:
|
||||
|
||||
```asm
|
||||
Irq LDA $D011
|
||||
AND #%11011111
|
||||
STA $D011 ; switch to text mode
|
||||
|
||||
LDA #<Irq2
|
||||
STA $0314
|
||||
LDA #>Irq2
|
||||
STA $0315
|
||||
|
||||
LDA #$0
|
||||
STA $D012 ; next IRQ at line 0
|
||||
|
||||
ASL $D019 ; acknowledge
|
||||
JMP $EA31 ; into KERNAL ISR
|
||||
|
||||
Irq2 LDA $D011
|
||||
ORA #%00100000
|
||||
STA $D011 ; switch to bitmap mode
|
||||
|
||||
LDA #<Irq
|
||||
STA $0314
|
||||
LDA #>Irq
|
||||
STA $0315
|
||||
|
||||
LDA #210
|
||||
STA $D012 ; next IRQ at line 210
|
||||
|
||||
ASL $D019
|
||||
JMP $EA81 ; shorter ROM routine — just restore regs and RTI
|
||||
```
|
||||
|
||||
Only one of the chained routines needs to jump to the full KERNAL ISR; the others can exit through $EA81 (the register-restore stub) for speed.
|
||||
Reference in New Issue
Block a user