Skip to content

Instantly share code, notes, and snippets.

@ErnestoBorio
Last active November 24, 2019 23:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ErnestoBorio/917de83ac3d121c41cb799a19720062c to your computer and use it in GitHub Desktop.
Save ErnestoBorio/917de83ac3d121c41cb799a19720062c to your computer and use it in GitHub Desktop.
C64 Raster IRQ test
// For KickAssembler
#import "ptz.asm"
BasicUpstart2(Start)
.memblock "Start"
Start:
DisableInterrupts()
lda #%01111111 // Bitmask to clear interrupts
sta IRQInterrupts // Disable IRQ system interrupts
sta NMIInterrupts // Disable NMI system interrupts
ScreenControl1(_screen_on | _25_rows | _text_mode | 3) // value is vertical scroll
ScreenControl2(_hires_mode | _40_columns)
Copy #%10100 : VICMemory // Screen memory at $400, character memory at $2000
Copy #BLACK : BackgroundColor
Copy #LIGHT_GREEN : BorderColor
EnableRasterInterrupts(_raster_line)
CopyWord(IRQRoutine, IRQVector) // Hookup the IRQ routine
Copy #100 : RasterLine // schedule raster interrupts at given scanline
// Acknowledge any pending interrupts
lda IRQInterrupts
lda NMIInterrupts
AcknowledgeIRQ()
EnableInterrupts()
InfiniteLoop()
rts
.memblock "IRQRoutine"
IRQRoutine:
Copy #YELLOW : BackgroundColor // lda #YELLOW; sta $D021
Copy #BLACK : BackgroundColor // lda #BLACK; sta $D021
AcknowledgeIRQ() // dec $D019
ReturnFromIRQ() // jmp $EA81
/************************************************
* General dev
************************************************/
// Sleeps for the specified CPU cycles inserting NOPs
.macro Sleep(cycles) {
.if (cycles <= 1) .error "Can't sleep for less than 2 cycles."
.var loops = floor(cycles/2) // How many 2 cycle NOPs are needed
.if (mod(cycles, 2) == 1) { // If cycles is odd
.eval loops-- // Replace one 2 cycle NOP
.byte NOP_ZP, $EB // With a 3 cycle NOP to sleep odd cycles (bytes $04 $EB)
}
.for (var i = 0; i < loops; i++) {
nop
}
}
.pseudocommand Copy source : target {
lda source
sta target
}
// Copies 2 bytes in consecutive addresses big, endian.
.macro CopyWord(data, address) {
lda #<data
sta address
lda #>data
sta address+1
}
.macro InfiniteLoop() {
jmp *
}
.macro DisableInterrupts() {
sei
}
.macro EnableInterrupts() {
cli
}
// Acknowledges Raster IRQ (And possible collisions too)
.macro AcknowledgeIRQ() {
dec IRQStatus
}
// Restores registers from the stack and rti
.macro ReturnFromIRQ() {
// jmp $EA81 // This Kernal routine does the same
pla
tay
pla
tax
pla
rti
}
// Shifts A the specified many bits
.macro ShiftRightA(bits) {
.for (var i = 0; i < bits; i++) {
lsr
}
}
.macro ShiftLeftA(bits) {
.for (var i = 0; i < bits; i++) {
asl
}
}
/************************************************
* Game dev
************************************************/
.const IRQVector = $314 // 2 byte vector to IRQ routine
.const SpritePointers = $7F8 // 8 byte pointers to the sprite patterns, in multiples of $40
// VIC-II
.const SpriteEnable = $D015 // bitmap to enable or disable sprites
.const BorderColor = $D020
.const BackgroundColor = $D021
.const SpriteX = $D000 // address of Sprite 0's X coordinate, 8 less significant bits
.const SpriteY = $D001 // address of Sprite 0's Y coordinate
.const SpriteXHighBits = $D010 // Bit 8 (most significant) of X coordinates of all 8 sprites
.const ScreenControl1 = $D011 // Vertical scroll, screen height, screen on, text/bitmap mode, current raster line
.const RasterLine = $D012 // 8 less significant bits of current raster line
.const ScreenControl2 = $D016 // Horizontal scroll, 38/40 columns, hires/multicolor mode
.const VICMemory = $D018 // Set address of character or bitmap screen data
.const IRQStatus = $D019 // IRQ status and acknowledgement
.const RasterInterrupts = $D01A // Raster, sprite collisions and lightpen interrupts
.const IRQInterrupts = $DC0D // IRQ system interrupts
.const NMIInterrupts = $DD0D // NMI system interrupts
.const VICBank = $DD00 // VIC Bank selector $0, $4000, $8000, $C000. Also serial bus I/O
// Kernal
.const ClearScreen = $E544
.const DefaultIRQRoutine = $EA31 // jmp here to go back to flashing cursor and BASIC CLI
.const ReturnFromIRQ = $EA81 // Restores the stack and registers and does RTI
// Use these constants summed or OR'd to enable them with EnableRasterInterrupts()
.const _raster_line = 1
.const _sprite_bg_collision = 2
.const _sprite_sprite_collision = 4
.const _lightpen = 8
.macro EnableRasterInterrupts(mask) {
lda mask
sta RasterInterrupts
}
// Set sprite's X coordinate, 8 less significant bits
.pseudocommand SpriteX index : x {
lda x
ldx index
sta SpriteX, x
}
// Set sprite's Y coordinate
.pseudocommand SpriteY index : y {
lda y
ldx index
sta SpriteY, x
}
// Set memory address where the sprite's bitmap is
.macro SpriteAddress(index, address) {
.if (index > 7) {
.error "Sprite index "+ index +" out of bounds. (0..7)"
}
.if (mod(address, $40) != 0) {
.error "Sprite address $"+ toHexString(address) +" should be multiple of $40 (64)."
}
.var place = address / $40
lda #place
sta SpritePointers + index
// .print "SpriteAddress() place: $"+ toHexString(place)
}
// Used by sprite enable and disable macros
.function CalculateSpriteMask(indices) {
.var mask = 0
.for (var i=0; i < indices.size(); i++) {
.var index = indices.get(i)
.var spriteBit = 1 << index
.eval mask = mask | spriteBit
}
// .print "Sprite bitmask: "+ toBinaryString(mask, 8)
.return mask
}
// Enable sprites given in list
.macro SpriteEnable(indices) {
lda SpriteEnable
ora #CalculateSpriteMask(indices)
sta SpriteEnable
}
// Disable sprites given in list
.macro SpriteDisable(indices) {
lda #CalculateSpriteMask(indices)
eor #$FF
and SpriteEnable
sta SpriteEnable
}
.const _24_rows = 0
.const _25_rows = %1000
.const _screen_off = 0
.const _screen_on = %10000
.const _text_mode = 0
.const _bitmap_mode = %100000
.const _extended_background = %1000000
// Set the Screen Control 1 register with the values above
.macro ScreenControl1(mask) {
lda #mask
sta ScreenControl1 // $D011
}
.const _38_columns = 0
.const _40_columns = %1000
.const _multicolor_mode = %10000
.const _hires_mode = 0
// Set the Screen Control 2 register with the values above
.macro ScreenControl2(mask) {
lda #mask
sta ScreenControl2 // $D016
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment