Home > Making stable raster routines (C64 and VIC-20) by Marko Makela (Marko.Makela@HUT.FI)

Making stable raster routines (C64 and VIC-20) by Marko Makela (Marko.Makela@HUT.FI)

Making stable raster routines (C64 and VIC-20) by Marko Makela (Marko.Makela@HUT.FI)



Preface



Too many graphical effects, also called raster effects, have been

coded in a very sloppy way. For instance, if there are any color bars

on the screen in a game or demo, the colors often jitter a bit,

e.g. they are not stable. And also, it is far too easy to make

virtually any demo crash by hitting the Restore key, or at least cause

visual distortions on the screen.



As late as a year ago I still hadn't coded a stable raster interrupt

routine myself. But then I had to do it, since I was researching the

video chip timing details together with my German friend Andreas

Boose. It was ashaming that we had the same level of knowledge when

it came to the hardware, but he was the only of us who had written a

stable raster routine. Well, finally I made me to start coding. I

used the same double-interrupt idea as Andreas used in his routine.



After a couple of errors my routine worked, and I understood how it

works exactly. (This is something that separates us normal coders

from demo people: They often code by instinct; by patching the routine

until it works, without knowing exactly what is happening. That's why

demos often rely on weird things, like crash if the memory is not

initialized properly.)



In this article, I document two methods of creating stable raster

routines on Commodore computers. The principles apply for most 8-bit

computers, not only Commodores, but raster effects are very rarely

seen on other computers.





Background



What are raster effects? They are effects, where you change the

screen appearance while it is being drawn. For instance, you can set

the screen color to white in the top of the screen, and to black in

the middle of the screen. In that way, you will get a picture whose

top half is white and bottom half black. Normally such effects are

implemented with interrupt routines that are executed synchronized

with the screen refresh.



The video chip on the Commodore 64 and many other videochips have a

special interrupt feature called the Raster interrupt. It will

generate an IRQ in the beginning of a specified raster line. On other

computers, like the VIC-20, there is no Raster interrupt, but you can

generate the interrupts with a timer, provided that the timer and the

videochip are clocked from the same source.



Even if the processor gets an interrupt signal at the same position on

each video frame, it won't always be executing the first instruction

of the interrupt routine at the same screen position. The NMOS 6502

machine instructions can take 2 to 9 machine cycles to execute, and if

the main program contains instructions of very varying lengths, the

beginning position of the interrupt can jump between 7 different

positions. This is why you need to synchronize the raster routine

when doing serious effects.



Also, executing the interrupt sequence will take 7 additional cycles,

and the interrupt sequence will only start after the current

instruction if the interrupt arrived at least two cycles before the

end of the current instruction. It is even possible that an interrupt

arrives while interrupts are disabled and the processor is just

starting to execute a CLI instruction. Alas, the processor will not

jump to the interrupt right after the CLI, but it will execute the

next instruction before jumping to it. This is natural, since the CLI

takes only two cycles. But anyway, this is only a constant in our

equation, and actually out of the scope of this article.



How to synchronize a raster interrupt routine? The only way is to

check the current screen position and delay appropriately many cycles.

There are several ways of doing this, some of which are very awful and

inefficient. The ugliest ways of doing this on the Commodore 64 I

know are busy-waiting several raster lines and polling the raster line

value, or using the Light pen feature, which will fail if the user

presses the fire button on Joystick port 1. Here I will present two

ways, both very elegant in my opinion.





Using an auxiliary timer



On the VIC-20, there is no Raster interrupt feature in the video chip.

All you can do is to use a timer for generating raster interrupts.

And if you use two timers running at a constant phase difference, you

can get full synchronization. The first timer generates the raster

interrupt, and the second timer, the auxiliary timer, tells the raster

routine where it is running. Actually you could even use the first

timer also for the checking, but the code will look nicer in the way I

will be presenting now. Besides, you can use the auxiliary timer idea

even when real raster interrupts are available.



The major drawback of using an auxiliary timer is initializing it.

The initialization routine must synchronize with the screen, that is,

wait for the beginning of the wanted raster line. To accomplish this,

the routine must first wait for a raster line that occurs a bit

earlier. About the only way to do this is with a loop like



LDA #value

loop CMP raster

BNE loop



One round of this loop will take 4+3=7 cycles to execute, assuming

that absolute addressing is being used. The loop will be finished if

the raster register contains the wanted value while the processor

reads it on the last cycle of the CMP instruction. The raster

register can actually have changed already on the first cycle of the

BNE instruction on the previous run of the loop, that is 7 cycles

earlier!



Because of this, the routine must poll the raster register for several

raster lines, always consuming one cycle more if the raster register

changed too early. As the synchronization can be off at most by 7

cycles, a loop of 7 raster register value changes would do, but I made

the loop a bit longer in my VIC-20 routine. (Well, I have to admit it,

I was too lazy to make it work only with 7 rounds.)



After the initialization routine is fully synchronized the screen, it

can set up the timer(s) and interrupts and exit. The auxiliary timer

in my VIC-20 demo routine is several dozens of cycles after the

primary timer, see the source code for comments. It is arranged so

that the auxiliary timer will be at least 0 when it is being read in

the raster routine. The raster routine will wait as many extra cycles

as the auxiliary timer reads, however at most 15 cycles.





Using double raster interrupt



On the Commodore 64, I have never seen the auxiliary timer scheme

being used. Actually I haven't seen it being used anywhere, I was

probably the first one who made a stable raster interrupt routine on

the VIC-20. Instead, the double interrupt method is becoming the

standard on the C64 side.



The double interrupt method is based entirely on the Raster interrupt

feature of the video chip. In the first raster interrupt routine, the

program sets up another raster interrupt on a further line, changes

the interrupt vector and enables interrupts.



In the place where the second raster interrupt will occur, there will

be 2-byte instructions in the first interrupt routine. In this way,

the beginning of the next raster interrupt will be off at most by one

cycle. Some coders might not care about this one cycle, but if you

can do it right, why wouldn't you do it right until the end?



At the beginning of the second raster interrupt routine, you will read

the raster line counter register at the point where it is about to

change. When the raster routine is being executed, there are two

possibilities: Either the raster counter has just changed, or it will

change on the next cycle. So, you just need to compare if the

register changed one cycle too early or not, and delay a cycle when

needed. This is easily accomplished with a branch to the next address.



Of course, somewhere in your second raster interrupt routine you must

restore the original raster interrupt position and set the interrupt

vector to point to the first interrupt routine.





Applying in practice



I almost forgot my complaints about demos crashing when you actively

hit the Restore key. On the VIC-20, you can disable NMI interrupts

generated by the Restore key, and on the C64, you can generate an NMI

interrupt with the CIA2 timer and leave the NMI-line low, so that no

further high-to-low transitions will be recognized on the line. The

example programs demonstrate how to do this.



So far, this article has been pretty theoretical. To apply these

results in practice, you must definitely know how many CPU clock

cycles the video chip consumes while drawing a scan line. This is

fairly easy to measure with a timer interrupt, if you patch the

interrupt handler so that it changes the screen color on each run.

Set the timer interval to LINES*COLUMNS cycles, where LINES is the

amount of raster lines and COLUMNS is your guess for the amount of

clock cycles spent in a raster line.



If your guess is right, the color will always be changed in the same

screen position (neglecting the 7-cycle jitter). When adjusting the

timer, remember that the timers on the 6522 VIA require 2 cycles for

re-loading, and the ones on the 6526 CIA need one extra cycle. Keep

trying different timer values until you the screen color changes at

one fixed position.



Commodore used several different values for LINES and COLUMNS on its

videochips. They never managed to make the screen refresh rate

exactly 50 or 60 Hertz, but they didn't hesitate to claim that their

computers comply with the PAL-B or NTSC-M standards. In the following

tables I have gathered some information of some Commodore video chips.





NTSC-M systems:



Chip Crystal Dot Processor Cycles/ Lines/

Host ID freq/Hz clock/Hz clock/Hz line frame

------ -------- -------- -------- --------- ------- ------

VIC-20 6560-101 14318181 4090909 1022727 65 261

C64 6567R56A 14318181 8181818 1022727 64 262

C64 6567R8 14318181 8181818 1022727 65 263



Later NTSC-M video chips were most probably like the 6567R8. Note

that the processor clock is a 14th of the crystal frequency on all

NTSC-M systems.



PAL-B systems:



Chip Crystal Dot Processor Cycles/ Lines/

Host ID freq/Hz clock/Hz clock/Hz line frame

------ -------- -------- -------- --------- ------- ------

VIC-20 6561-101 4433618 4433618 1108405 71 312

C64 6569 17734472 7881988 985248 63 312



On the PAL-B VIC-20, the crystal frequency is simultaneously the dot

clock, which is BTW a 4th of the crystal frequency used on the C64.

On the C64, the crystal frequency is divided by 18 to generate the

processor clock, which in turn is multiplied by 8 to generate the

dot clock.



The basic timings are the same on all 6569 revisions, and also on

any later C64 and C128 video chips. If I remember correctly, these

values were the same on the C16 videochip TED as well.



Note that the dot clock is 4 times the processor clock on the VIC-20,

and 8 times that on the C64. That is, one processor cycle is half a

character wide on the VIC-20, and a full character on a C64. I don't

have exact measurements of the VIC-20 timing, but it seems that while

the VIC-20 videochips draw the characters on the screen, it first

reads the character code, and then, on the following video cycle, the

appearance on the current character line. There are no bad lines,

like on the C64, where the character codes (and colors) are fetched on

every 8th raster line.



Those ones who got upset when I said that Commodore has never managed

to make a fully PAL-B or NTSC-M compliant 8-bit computer should take a

closer look at the "Lines/frame" columns. If that does not convince

you, calculate the raster line rate and the screen refresh rate from

the values in the table and see that they don't comply with the

standards. To calculate the line rate, divide the processor clock

frequency by the amount of cycles per line. To get the screen refresh

rate, divide that frequency by the amount of raster lines.





The Code



OK, enough theory and background. Here are the two example programs,

one for the VIC-20 and one for the C64. In order to fully understand

them, you need to know the exact execution times of NMOS 6502

instructions. (All 8-bit Commodore computers use the NMOS 6502

processor core, except the C65 prototype, which used a inferior CMOS

version with all nice poorly-documented features removed.) You should

check the 64doc document, available on my WWW pages at

http://www.hut.fi/~msmakela/cbm/emul/x64/64doc.html, or via FTP at

ftp.funet.fi:/pub/cbm/documents/64doc. I can also e-mail it to you on

request.



Also, I have written a complete description of the video timing on the

6567R56A, 6567R8 and 6569 video chips, which could maybe be turned

into another C=Hacking article. The document is currently partially

in English and partially in German. The English part is available

from ftp.funet.fi as /pub/cbm/documents/pal.timing, and I can send

copies of the German part (screen resolution, sprite disturbance

measurements, and more precise timing information) via e-mail.



The code is written for the DASM assembler, or more precisely for a

extended ANSI C port of it made by Olaf Seibert. This excellent

cross-assembler is available at ftp.funet.fi in /pub/cbm/programming.



First the raster demo for the VIC-20. Note that on the VIC-20, the

$9004 register contains the upper 8 bits of the raster counter. So,

this register changes only on every second line. I have tested the

program on my 6561-101-based VIC-20, but not on an NTSC-M system.



It was hard to get in contact with NTSC-M VIC-20 owners. Daniel

Dallmann, who has a NTSC-M VIC-20, although he lives in Germany, ran

my test to determine the amount of cycles per line and lines per frame

on the 6560-101. Unfortunately, the second VIA of his VIC-20 is

partially broken, and because of this, this program did not work on

his computer. Craig Bruce ran the program once, and he reported that

it almost worked. I corrected a little bug in the code, so that now

the display should be stable on an NTSC-M system, too. But the actual

raster effect, six 16*16-pixel boxes centered at the top border, are

very likely to be off their position.





processor 6502



NTSC = 1

PAL = 2



;SYSTEM = NTSC ; 6560-101: 65 cycles per raster line, 261 lines

SYSTEM = PAL ; 6561-101: 71 cycles per raster line, 312 lines



#if SYSTEM & PAL

LINES = 312

CYCLES_PER_LINE = 71

#endif

#if SYSTEM & NTSC

LINES = 261

CYCLES_PER_LINE = 65

#endif

TIMER_VALUE = LINES * CYCLES_PER_LINE - 2



.org $1001 ; for the unexpanded Vic-20



; The BASIC line



basic:

.word 0$ ; link to next line

.word 1995 ; line number

.byte $9E ; SYS token



; SYS digits



.if (* + 8) / 10000

.byte $30 + (* + 8) / 10000

.endif

.if (* + 7) / 1000

.byte $30 + (* + 7) % 10000 / 1000

.endif

.if (* + 6) / 100

.byte $30 + (* + 6) % 1000 / 100

.endif

.if (* + 5) / 10

.byte $30 + (* + 5) % 100 / 10

.endif

.byte $30 + (* + 4) % 10

0$:

.byte 0,0,0 ; end of BASIC program



start:

lda #$7f

sta $912e ; disable and acknowledge interrupts

sta $912d

sta $911e ; disable NMIs (Restore key)



;synchronize with the screen

sync:

ldx #28 ; wait for this raster line (times 2)

0$:

cpx $9004

bne 0$ ; at this stage, the inaccuracy is 7 clock cycles

; the processor is in this place 2 to 9 cycles

; after $9004 has changed

ldy #9

bit $24

1$:

ldx $9004

txa

bit $24

#if SYSTEM & PAL

ldx #24

#endif

#if SYSTEM & NTSC

bit $24

ldx #21

#endif

dex

bne *-1 ; first spend some time (so that the whole

cmp $9004 ; loop will be 2 raster lines)

bcs *+2 ; save one cycle if $9004 changed too late

dey

bne 1$

; now it is fully synchronized

; 6 cycles have passed since last $9004 change

; and we are on line 2(28+9)=74



;initialize the timers

timers:

lda #$40 ; enable Timer A free run of both VIAs

sta $911b

sta $912b



lda #<TIMER_VALUE

ldx #>TIMER_VALUE

sta $9116 ; load the timer low byte latches

sta $9126



#if SYSTEM & PAL

ldy #7 ; make a little delay to get the raster effect to the

dey ; right place

bne *-1

nop

nop

#endif

#if SYSTEM & NTSC

ldy #6

dey

bne *-1

bit $24

#endif



stx $9125 ; start the IRQ timer A

; 6560-101: 65 cycles from $9004 change

; 6561-101: 77 cycles from $9004 change

ldy #10 ; spend some time (1+5*9+4=55 cycles)

dey ; before starting the reference timer

bne *-1

stx $9115 ; start the reference timer



pointers:

lda #<irq ; set the raster IRQ routine pointer

sta $314

lda #>irq

sta $315

lda #$c0

sta $912e ; enable Timer A underflow interrupts

rts ; return



irq:

; irq (event) ; > 7 + at least 2 cycles of last instruction (9 to 16 total)

; pha ; 3

; txa ; 2

; pha ; 3

; tya ; 2

; pha ; 3

; tsx ; 2

; lda $0104,x ; 4

; and #xx ; 2

; beq ; 3

; jmp ($314) ; 5

; ---

; 38 to 45 cycles delay at this stage



lda $9114 ; get the NMI timer A value

; (42 to 49 cycles delay at this stage)

; sta $1e00 ; uncomment these if you want to monitor

; ldy $9115 ; the reference timer on the screen

; sty $1e01

cmp #8 ; are we more than 7 cycles ahead of time?

bcc 0$

pha ; yes, spend 8 extra cycles

pla

and #7 ; and reset the high bit

0$:

cmp #4

bcc 1$

bit $24 ; waste 4 cycles

and #3

1$:

cmp #2 ; spend the rest of the cycles

bcs *+2

bcs *+2

lsr

bcs *+2 ; now it has taken 82 cycles from the beginning of the IRQ



effect:

ldy #16 ; perform amazing video effect

lda $900f

tax

eor #$f7

0$:

sta $900f

stx $900f

sta $900f

stx $900f

sta $900f

stx $900f

sta $900f

stx $900f

sta $900f

stx $900f

sta $900f

stx $900f

pha

pla

#if SYSTEM & PAL

pha

pla

nop

#endif

#if SYSTEM & NTSC

bit $24

#endif

nop

dey

bne 0$ ; end of amazing video effect



jmp $eabf ; return to normal IRQ





And after you have recovered from the schock of seeing a VIC-20

program, here is an example for the C64. It does also something

noteworthy; it removes the side borders on a normal screen while

displaying all eight sprites. Well, it cannot remove the borders on

bad lines, and the bad lines look pretty bad. But I could use the

program for what I wanted: I measured the sprite distortions on all

videochip types I had at hand. (FYI: the sprites 0-2 get distorted at

the very right of the screen, and the sprites 6 and 7 are invisible at

the very left of the screen. You will need a monitor with horizontal

size controls to witness these effects.)



This program is really robust, it installs itself nicely to the

interrupt routine chain. It even has an entry point for deinstalling

itself. But in its robustness it uses self-modifying code to store

the original interrupt routine address. :-)



The code also relies on the page boundaries in being where they are.

The cycles are counted so that the branches "irqloop" must take 4

cycles. If the "irqloop" comes to the same CPU page with the branch

instructions, you must add one cycle to the loop in a way or another.

When coding the routine, I noticed again how stupid assembly coding

can be, especially conditional assembling. In a machine language

monitor you have far better control on page boundaries. BTW, you

might wonder why I disable the Restore key in a subroutine at the end

and not in the beginning of the program. Well, the routine was so

long that it would have affected the "irqloop" page boundaries. And I

didn't want to risk the modified programs working on all three

different videochip types on the first try.





In the code, there are some comments that document the video timing,

like this one:



;3s4s5s6s7srrrrrgggggggggggggggggggggggggggggggggggggggg--||0s1s2s Phi-1 VIC-II

;ssssssssss ||ssssss Phi-2 VIC-II

;==========xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx||XXX====== Phi-2 6510

; ^ now we are here



The two vertical bars "|" denote optional cycles. On PAL-B systems

(63 cycles per line), they are not present. On 6567R56A, which has 64

cycles per line, there is one additional cycle on this position, and

the 6567R8 has two additional cycles there.



The numbers 0 through 7 are sprite pointer fetches (from the end of

the character matrix, e.g. the text screen), the "s" characters denote

sprite image fetches, the "r"s are memory refresh, and the "g" are

graphics fetches. The two idle video chip cycles are marked with "-".

On the processor timing line, the "=" signs show halted CPU, "x" means

free bus, and "X" means that the processor will be halted at once,

unless it is performing write cycles.



processor 6502



; Select the video timing (processor clock cycles per raster line)

CYCLES = 65 ; 6567R8 and above, NTSC-M

;CYCLES = 64 ; 6567R5 6A, NTSC-M

;CYCLES = 63 ; 6569 (all revisions), PAL-B



cinv = $314

cnmi = $318

raster = 52 ; start of raster interrupt

m = $fb ; zero page variable



.org $801

basic:

.word 0$ ; link to next line

.word 1995 ; line number

.byte $9E ; SYS token



; SYS digits



.if (* + 8) / 10000

.byte $30 + (* + 8) / 10000

.endif

.if (* + 7) / 1000

.byte $30 + (* + 7) % 10000 / 1000

.endif

.if (* + 6) / 100

.byte $30 + (* + 6) % 1000 / 100

.endif

.if (* + 5) / 10

.byte $30 + (* + 5) % 100 / 10

.endif

.byte $30 + (* + 4) % 10



0$:

.byte 0,0,0 ; end of BASIC program



start:

jmp install

jmp deinstall



install: ; install the raster routine

jsr restore ; Disable the Restore key (disable NMI interrupts)

checkirq:

lda cinv ; check the original IRQ vector

ldx cinv+1 ; (to avoid multiple installation)

cmp #<irq1

bne irqinit

cpx #>irq1

beq skipinit

irqinit:

sei

sta oldirq ; store the old IRQ vector

stx oldirq+1

lda #<irq1

ldx #>irq1

sta cinv ; set the new interrupt vector

stx cinv+1

skipinit:

lda #$1b

sta $d011 ; set the raster interrupt location

lda #raster

sta $d012

ldx #$e

clc

adc #3

tay

lda #0

sta m

0$:

lda m

sta $d000,x ; set the sprite X

adc #24

sta m

tya

sta $d001,x ; and Y coordinates

dex

dex

bpl 0$

lda #$7f

sta $dc0d ; disable timer interrupts

sta $dd0d

ldx #1

stx $d01a ; enable raster interrupt

lda $dc0d ; acknowledge CIA interrupts

lsr $d019 ; and video interrupts

ldy #$ff

sty $d015 ; turn on all sprites

cli

rts



deinstall:

sei ; disable interrupts

lda #$1b

sta $d011 ; restore text screen mode

lda #$81

sta $dc0d ; enable Timer A interrupts on CIA 1

lda #0

sta $d01a ; disable video interrupts

lda oldirq

sta cinv ; restore old IRQ vector

lda oldirq+1

sta cinv+1

bit $dd0d ; re-enable NMI interrupts

cli

rts



; Auxiliary raster interrupt (for syncronization)

irq1:

; irq (event) ; > 7 + at least 2 cycles of last instruction (9 to 16 total)

; pha ; 3

; txa ; 2

; pha ; 3

; tya ; 2

; pha ; 3

; tsx ; 2

; lda $0104,x ; 4

; and #xx ; 2

; beq ; 3

; jmp ($314) ; 5

; ---

; 38 to 45 cycles delay at this stage

lda #<irq2

sta cinv

lda #>irq2

sta cinv+1

nop ; waste at least 12 cycles

nop ; (up to 64 cycles delay allowed here)

nop

nop

nop

nop

inc $d012 ; At this stage, $d012 has already been incremented by one.

lda #1

sta $d019 ; acknowledge the first raster interrupt

cli ; enable interrupts (the second interrupt can now occur)

ldy #9

dey

bne *-1 ; delay

nop ; The second interrupt will occur while executing these

nop ; two-cycle instructions.

nop

nop

nop

oldirq = * + 1 ; Placeholder for self-modifying code

jmp * ; Return to the original interrupt



; Main raster interrupt

irq2:

; irq (event) ; 7 + 2 or 3 cycles of last instruction (9 or 10 total)

; pha ; 3

; txa ; 2

; pha ; 3

; tya ; 2

; pha ; 3

; tsx ; 2

; lda $0104,x ; 4

; and #xx ; 2

; beq ; 3

; jmp (cinv) ; 5

; ---

; 38 or 39 cycles delay at this stage

lda #<irq1

sta cinv

lda #>irq1

sta cinv+1

ldx $d012

nop

#if CYCLES - 63

#if CYCLES - 64

nop ; 6567R8, 65 cycles/line

bit $24

#else

nop ; 6567R56A, 64 cycles/line

nop

#endif

#else

bit $24 ; 6569, 63 cycles/line

#endif

cpx $d012 ; The comparison cycle is executed CYCLES or CYCLES+1 cycles

; after the interrupt has occurred.

beq *+2 ; Delay by one cycle if $d012 hadn't changed.

; Now exactly CYCLES+3 cycles have passed since the interrupt.

dex

dex

stx $d012 ; restore original raster interrupt position

ldx #1

stx $d019 ; acknowledge the raster interrupt

ldx #2

dex

bne *-1

nop

nop

lda #20 ; set the amount of raster lines-1 for the loop

sta m

ldx #$c8

irqloop:

ldy #2

dey

bne *-1 ; delay

dec $d016 ; narrow the screen (exact timing required)

;3s4s5s6s7srrrrrgggggggggggggggggggggggggggggggggggggggg--||0s1s2s Phi-1 VIC-II

;ssssssssss ||ssssss Phi-2 VIC-II

;==========xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx||XXX====== Phi-2 6510

; ^ now we are here

stx $d016 ; expand the screen

#if CYCLES - 63

#if CYCLES - 64

bit $24 ; 6567R8

#else

nop ; 6567R56A

#endif

#else

nop ; 6569

#endif

dec m

bmi endirq

clc

lda $d011

sbc $d012

and #7

bne irqloop ; This instruction takes 4 cycles instead of 3,

; because the page boundary is crossed.

badline:

dec m

nop

nop

nop

nop

dec $d016

;3s4s5s6s7srrrrrgggggggggggggggggggggggggggggggggggggggg--||0s1s2s Phi-1 VIC-II

;ssssssssss cccccccccccccccccccccccccccccccccccccccc ||ssssss Phi-2 VIC-II

;==========xXXX========================================||***====== Phi-2 6510

; ^ we are here

stx $d016

;3s4s5s6s7srrrrrgggggggggggggggggggggggggggggggggggggggg--||0s1s2s Phi-1 VIC-II

;ssssssssss ||ssssss Phi-2 VIC-II

;==========xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx||XXX====== Phi-2 6510

; ^ ^^- we are here (6569)

; | \- or here (6567R56A)

; \- or here (6567R8)

ldy #2

dey

bne *-1

nop

nop

#if CYCLES - 63

#if CYCLES - 64

nop ; 6567R8, 65 cycles/line

nop

nop

#else

bit $24 ; 6567R56A, 64 cycles/line

#endif

#else

nop ; 6569, 63 cycles/line

#endif

dec m

bpl irqloop ; This is a 4-cycle branch (page boundary crossed)

endirq:

jmp $ea81 ; return to the auxiliary raster interrupt



restore: ; disable the Restore key

lda cnmi

ldy cnmi+1

pha

lda #<nmi ; Set the NMI vector

sta cnmi

lda #>nmi

sta cnmi+1

ldx #$81

stx $dd0d ; Enable CIA 2 Timer A interrupt

ldx #0

stx $dd05

inx

stx $dd04 ; Prepare Timer A to count from 1 to 0.

ldx #$dd

stx $dd0e ; Cause an interrupt.

nmi = * + 1

lda #$40 ; RTI placeholder

pla

sta cnmi

sty cnmi+1 ; restore original NMI vector (although it won't be used)

rts



Set Home | Add to Favorites

All Rights Reserved Powered by Free Document Search and Download

Copyright © 2011
This site does not host pdf,doc,ppt,xls,rtf,txt files all document are the property of their respective owners. complaint#downhi.com
TOP