c64 Interrupts Eng Part06
c64 Interrupts Eng Part06
• Changes the color of the screen edges and the display area to
white
• Schedules a new raster interrupt at raster line $96 with irq01
as associated interrupt handler.
- When the raster beam reaches the raster line $96, a raster interrupt
is triggered and the associated interrupt handler (named irq01)
performs the following operations:
• Changes the color of the screen edges and the display area to
black
• Schedules a new raster interrupt at raster line $08 with irq00
as associated interrupt handler.
In register $d012, i.e. the 8 least significant bits of frame register $d012-
$d011, we no longer load the constant IRQSPLIT as a frame line comparison
parameter for irq01, but we read the values of the irq_table created with
the .fill directive of Kick assembler :
This determines how each of the 128 values is calculated. Each of the 128
values is indexed by i, with i varying from 0 to 127 (128 values).
...
...
In the end, this directive allows us to simply calculate 128 raster line
values that vary between 144+64 and 144-64 according to the sine function
indicated above.
Now let's see how to define, in the code, the raster line for the raster
interrupt irq01 using the "SELF MODIFYING CODE" technique.
At the first execution of the irq00, on line 45 of the code, we load with
the immediate addressing the value 127 in the X register of the 6510.
On line 46, we load a value from the irq_table into Y using the contents of
the X-register as an index (absolute indexed addressing) and on the next
line, we store the value of the table in $d012 to define the next raster
interrupt served by the interrupt handler irq01.
on line 48, we find one of the 2 key instructions of the "SELF MODIFYING
CODE": dec idx+1.
The idx label corresponds to the address where the opcode of the ldx
instruction on line 45 is located; the next byte, idx+1, contains the value
127.
Thus, with the decrementing performed on line 48, we only decrement by 1 the
index X of the array starting with the value 127.
As long as the content of the address idx+1 is >=0, we continue the execution
of the program starting from line 52 (BPL instruction at line 49).
When the index - following the successive decrements - reaches the value $00,
an additional "DEC" causes a change in the value contained in the idx+1
location which then becomes equal to $ff ($00-$01=$ff). From then on, the
condition of the conditional jump bpl (line 49) will no longer be verified
since the MSB of the location will be fixed and consequently change, the N
flag of the status register setting it to 1.
Remember that the status register flag N (also called sign bit) does nothing
more than copy the status of the most significant bit (MSB) of the location
concerned, whether the content is a complement to 2 (signed) or an unsigned
number.
To bring the table index back to 127, on line 50, we load 127 into the
Accumulator, whose contents is then stored in the idx+1 address at line 51.
We could also have not used the "SELF MODIFYING CODE" technique, but in this
case, we would have had to define an additional variable in memory to store
the value of the index of the irq_table, which would have lost 1 byte. The
code would be as follows:
Although the savings in this particular example may seem minimal and perhaps
negligible - we remind readers that our objective here is essentially didactic
- there are many other cases in which the above technique proves to be
fundamental to obtain savings in terms of memory and cpu cycles (compact and
faster code).
Here are some screenshots of the execution of the modified routine :
The complete code of the modified program is below:
irq01:
lda #0
ldx $d012
!: cpx $d012
beq !-
sta $d020
sta $d021
lda #<irq00
sta $0314
lda #>irq00
sta $0315
lda #$08
sta $d012
lsr $d019
jmp $ea31
irq00:
lda #$01
sta $d020
sta $d021
lda #<irq01
sta $0314
lda #>irq01
sta $0315
lsr $d019
jmp $ea81
irq_table:
.fill 128,144.0 + 64.0 * sin(i * PI * 2 / 128.0)
Before showing other examples of application, we think it is necessary to
clarify some theoretical aspects encountered previously.
When we dealt with the memory accesses made by the VIC, in particular for
displaying text or graphics, we mentioned the accesses to the video memory
for reading Screen Codes (at least in Text Mode).
On this occasion, we have used the terms "screen memory" and "video matrix"
as synonyms for "video memory".
Figure 1 : Difference between screen memory and video matrix (extract from "Mappa di Memoria del C64")
The video matrix represents the first 1000 bytes of screen memory (default
from $0400 to $07e7) where, in text mode, 1-byte screen codes are stored.
These are indexes or "pointers" to a "Character Set" which contains the binary
description of the shape of all the characters that can be displayed on the
screen (display window).
The "Character Set" can be in ROM if we use the standard C64 characters, in
which case we will use the term "Character Generator ROM" (or more briefly
"Char ROM") or, in the case of user-defined characters, in RAM memory; in
the latter case, we can speak of "User-Defined Character RAM".
The original data sheet of the graphic chip of the VIC-II indicates the above-
mentioned character set with the term "Character Base".
In practice, however, both terms - screen memory and video matrix - can in
principle be considered equivalent and therefore interchangeable.
Still on the subject of the additional memory accesses made by the VIC during
a BAD LINE, we said that in addition to the accesses to the video matrix,
the VIC also reads the color information - 4-bit color code, Fig. 2 - of the
color RAM between the $d800 and $dbff addresses.
The "color RAM" is a fixed memory area that cannot be moved by the user,
unlike the screen memory.
It should be noted that accesses to color RAM do not subtract CPU clock cycles
because the VIC-II has four data pins (i.e., the VIC's 12-bit data bus pins)
connected directly to the color RAM chip (Fig. 3).
The color code will be stored in the four most significant bits of the VIC's
data bus (DB8 to DB11) in both Text Mode - Standard (also called Hires or
Normal or Monocolor), Multicolor and Extended Background (ECM) - and Bitmap
Multicolor mode.
The remaining 8 bits of the graphics chip's data bus (DB0 to DB7) can contain
either the value of the screen code - in Text mode - or, in Bitmap mode,
additional color information (Figs. 4, 5 and 6).
Figure 5: Color source in Extended Background text mode (or ECM: Extended Color Mode). Bits D6 and D7 in the
figure refer to the 2 most significant bits of the character code.
// Scroll Text
// Horizontal text scrolling (HSCroll) Register of $d016 of VIC (bit 0-2)
// By Attilio Capuozzo for "RetroProgramming Italia - RP Italia".
// KickAssembler Version
* = $c000
.label NMIRoutine = $fec1 // The address of the last instruction of the default NMI Handler ($fe47) corresponding to the RTI instruction.
scrolling: // Raster Interrupt Management Label at line 0 (upper half of the screen with "smooth" horizontal scrolling effect)
lda scrollValue,x // Reading the horizontal scroll value of the table
sta $d016 // Store the value of horizontal scrolling in bits 0-2 (values from 0 to 7) of $d016.
stx $fb // Save the counter in memory
lda #$9a // raster line of the next raster interrupt: line 154 (9a)
sta $d012
jmp testTimerAInterrupt
nextRaster: // Managing the lower half of the screen at the raster line 154 (9a)
lda #$08
sta $d016 // We load the default value (08) in $d016 which corresponds to the absence of Horizontal Scrolling (bit 0-2=0).
lda #$00 // raster line of the next raster interrupt: line $00
sta $d012
testTimerAInterrupt:
lda $dc0d // Let's check if a system interrupt request has been generated in the meantime (IRQ of Timer A of CIA1).
and #$01 // So let's check if bit 0 (LSB) of $dc0d is set(=1)
beq exitPullStack // If an IRQ from Timer A is NOT signalled (i.e. bit 0=0 of $dc0d), we exit normally.
jmp $ea31 // otherwise (bit 0=1 of $dc0d) we make a JMP to the system interrupt handler ($ea31) for the Keyboard Scan etc.
exitPullStack:
pla // The following instructions occupy 6 bytes and are the equivalent of Jmp $ea81 or Jmp $febc
tay // the entry points (pointing to the last 6 bytes) of the system interrupt handler (IRQ-->$ea31 and NMI-->$fe47)
pla // Extraction of the stack from the Y, X and A registers + return from interrupt
tax // Pushing to the stack of the 6510's 3 data registers is performed by the system's main IRQ interrupt handler
pla // mapped to ROM address $ff48 and vectorized by the $fffe-$ffff locations of the Kernal ROM (last 8K of the C64 memory card).
rti // The main system IRQ interrupt handler ($ff48) is executed BEFORE the system IRQ handler ($ea31) vectorized in RAM
// by $0314-$0315 (CINV).
scrollValue: // Table of the 14 horizontal scroll values of register $d016 from (0 to 7) + 8 (default value of register $d016)
.byte 8,9,10,11,12,13,14,15
.byte 14,13,12,11,10,9
First of all, we note the trick used in the Setup to disable the forced exit
of the program by pressing the RUN/STOP and RESTORE keys simultaneously:
ldx #<NMIRoutine
ldy #>NMIRoutine
stx $0318
sty $0319
Remember that by pressing the RESTORE key, you trigger an NMI interrupt.
$fec1 corresponds, in fact, to the address of the last instruction (the RTI)
of the System NMI Handler which starts at position $fe47 and is vectorized
by $0318-$0319.
At the beginning of the IRQ Handler IRQRoutine, just after the ACK of the
IRQ Raster Interrupt, we use the BIT (Bit Test) instruction to test the high
bit (MSB) of $d012 via a BMI (Branch if MInus) conditional jump instruction:
bit $d012
bmi nextRaster
We know from the ISA (Instruction Set Architecture) documentation of the 6510
that the BIT instruction performs a volatile AND between the accumulator and
a memory location, i.e. it does not store the result of the above logical
operation in register A.
In addition, the instruction also copies bits 7 and 6 of the operand - the
memory location - into the N (negative) and V (oVerflow) flags, respectively,
of the status register.
And it is this feature that allows us to execute the branching of the next
instruction (BMI), with a test on flag N of the status register.
testTimerAInterrupt:
lda $dc0d
and #$01
We would like to point out that although all CIA1 interrupt sources were
previously disabled in the:
lda #$7f
sta $dc0d
the interrupt flag of Timer A - bit 0 of $dc0d - will also be set in case
the interrupt condition occurs (Underflow of Timer A of CIA 1 as already
seen) but the request will NOT be served and the IRQ interrupt will NOT be
triggered.
exitPullStack:
pla
tay
pla
tax
pla
rti
The above instructions which, in order, retrieve from the stack the 3 data
registers Y, X and A of the 6510 (followed by a RTI) correspond to the last
6 instructions of one of the 2 system interrupt handlers - IRQ or NMI - and
are often replaced by an equivalent JMP $ea81 or JMP $febc.