This leaves us with
only two commands remaining. The BRK is a software interrupt, while the NOP
command does nothing. Doing nothing is a handy thing as we will discuss
shortly.
As mentioned in the
previous article, when an IRQ happens, the processor stops what it is doing and
executes the interrupt handler. BRK issues an IRQ but unlike a normal IRQ this
one is not maskable so will always occur. This results in the code continuing
execution at the address that is stored in $FFFE-$FFFF. On early computer systems
this was set up to be a special machine language debugging program called a
monitor. This allowed the programmer to scatter BRK statements through their
code which would trigger the monitor when they were reached. The monitor would
display status information about the program and let the programmer issue
debugging commands for doing things such as changing the values in memory,
changing the execution address, or continuing the program right after the break
point.
When a programmer was
finished with a specific BRK statement, they would simply replace the value at
that location with $EA which is the OPCode for NOP. In other words, in the
early days break points were coded into a program!
As if removing break
points from a program wasn’t a good enough reason to have the NOP command, it
also takes two cycles to execute. This means that if you have timing critical
parts of your program you can use NOP instructions as padding to get the right
timing. With the 2600, timing is very important so this is not something to be
taken for granted.
Testing was a bit
tricky for implementing these instructions. Making sure that the NOP was timing
correctly probably didn’t need testing as that is handled by the emulator but
as the operation of the TIA chip requires precise timing making sure cycles are
working important so for these tests run until a cycle is reached instead of
until a break is reached. This is kind of required as testing the break does
require that the break command actually get executed!
BRK – BReaK
Address Mode
|
Decimal OPCode
|
Hexadecimal OpCode
|
Size
|
Cycles
|
Implied
|
0
|
$00
|
1
|
7
|
Flags affected: I
Usage: Triggers the Break interrupt causing
the program to start executing code from the address stored at $FFFE-$FFFF.
Used for debugging with the IRQ address set to a monitor program.
Test Code:
; BRK ->
requires IRQ set to 512, cycles = 71
LDX
#255 ; 2
TXS
; 4
loop: BRK
; 11, 33, 55, END
;
start here with 22, 44, 66
INX
; 24, 46, 68
JMP
loop ;27, 49, 71
.ORG 512
;
reach here at 11, 33, 55
TAY
; 13, 35, 57
TXA
; 15, 37, 59
RTI
; 22, 44, 66
;x=2,
y = 0, A = 1
Implementation:
pushByteOnStack((state.ipNext
/ 256) and 255)
pushByteOnStack(state.ipNext
and 255)
pushByteOnStack(state.flags
or BREAK_FLAG or INTERRUPT_FLAG)
state.ipNext =
findAbsoluteAddress(0xfffd)
NOP – No OPeration
Address Mode
|
Decimal OPCode
|
Hexadecimal OpCode
|
Size
|
Cycles
|
Implied
|
234
|
$EA
|
1
|
2
|
Flags affected: None
Usage: This command tends to be used for
debugging to replace BRK instructions. It can be used in timing sensitive
programs as a 2 cycle delay.
Test Code:
; NOP -> run for
32 cycles
LDX
#0 ;2
loop:
NOP ; 4, 15, 26
NOP
; 6, 17, 28
NOP
; 8, 19, 30
INX
; 10, 21, 32
BNE
loop ;13, 24
;
expect at 32 cycles for x=3
Implementation:
_->