Oct 4, 2026 · Updated Oct 11, 2026 · @Craig Taylor
Chapter 7 described the 45GS02 in isolation. Real programs run on a machine full of state that someone else set up: the KERNAL's memory map, BASIC's choice of bank, the current I/O personality, the CPU speed, and interrupt handlers already installed. MEGA65 machine language is largely the craft of taking control of that state, changing it deliberately, and handing it back intact.
This chapter collects the techniques that are specific to the MEGA65 rather than to the 6502 family in general:
Two styles of program appear here. A cooperative program runs alongside BASIC and the KERNAL, called with SYS and returning with RTS; it must leave everything as it found it. A takeover program, such as a game or demo, replaces the system's map and interrupt handlers entirely and never returns. Most techniques apply to both, but where they differ the text says so.
The examples use the same assembler syntax as Chapter 7:
.byte and .word for data, [zp],Z
for flat indirect addressing, and labels throughout.
A cooperative routine called by SYS starts in a known state: the mapping described in Chapter 6, with bank 0 low memory, the selected bank in the middle, and the KERNAL and I/O on top. Its only obligation is to restore anything it changes, including B, Z and the E flag, before RTS.
A takeover program cannot rely on whatever state it inherits. Its
first job is to set every banking mechanism, the I/O personality and the
CPU state to values it chooses. The following skeleton does that,
leaving all of bank 0 visible as RAM with I/O at $D000:
start sei ; no interrupts while the machine changes
lda #$35
sta $01 ; C64 port: RAM everywhere except I/O
lda #$00
tax
tay
taz
map ; MAP: no offsets, nothing mapped
eom
lda #$47
sta $d02f
lda #$53
sta $d02f ; VIC-IV I/O personality
lda #$00
sta $d030 ; no C65 ROM banking
tab ; base page = page 0
see ; 8-bit stack in page 1
cld
lda #65
sta $00 ; full CPU speed
lda #$7f
sta $dc0d ; disable CIA 1 interrupts
sta $dd0d ; disable CIA 2 interrupts
lda $dc0d ; acknowledge any pending
lda $dd0d
The order matters in three places:
$D030. The ROM-banking
bits of $D030 exist only in the VIC-III and VIC-IV
personalities; in the VIC-II personality the address holds only the
C128 2 MHz bit. So the knock must come first. MAP takes precedence over every other banking mechanism, but
only for the 8KB blocks it maps (MEGA65 Book, p. K-7). The skeleton maps
no blocks, so any ROM bits left set in $D030 would still
bring ROM into those areas; clearing $D030 removes
them.$01 and $D030 in charge of every block
MAP does not map. Only when all three are set does the program know
exactly what it sees.The single MAP in the skeleton clears the offsets and block enables
but not MAP's megabyte bytes, which only the two-MAP form described
below changes. They are zero after reset and the KERNAL leaves them at
zero, so this is normally safe (MEGA65 Book, p. 14-41); a program that
may start after other software has used them can clear them first with
LDA #0 : LDX #$0F : LDY #0 : LDZ #$0F : MAP before the
skeleton's own MAP.
The MEGA65 Book describes a fuller version of the same procedure for
programs that want every byte of chip RAM. It also removes I/O from
$D000-$DFFF by clearing bits 0-2 of $01, moves
colour data so the first 2KB of colour RAM can be used as general
memory, and disables the ROM write-protection on banks 2 and 3 (MEGA65
Book, pp. 14-39, 14-42).
For that last step, use the Hypervisor's rom_writeenable
service, which always leaves the ROM writable:
lda #$02
sta $d641 ; rom_writeenable
clv ; single-cycle instruction after the trap
The Book's example uses trap $70 on $D640
instead, but that service toggles the protection: if something has
already switched it off, it switches it back on, and the program's
writes to $20000-$3FFFF are then silently ignored.
LDA #$00 : STA $D641 turns the protection back on (MEGA65
Book, Hypervisor services appendix; mega65-core, Hyppo mem.asm).
After this the program owns the machine. The KERNAL is gone, so the
hardware vectors at $FFFA-$FFFF are now plain RAM in bank
0. The program must store its own NMI and IRQ vectors there before it
executes CLI, as shown in the raster interrupt section below. The
RESTORE key raises an NMI, so even a program with no use for NMIs needs
a vector pointing at an RTI.
As a starting point, place this skeleton after a BASIC header at
$2001, as described in Chapter 6, so the program loads and
runs with RUN.
The MEGA65 inherited a speed control from each machine it is
compatible with, and all of them still work. Two bits do most of the
work in the VIC-III and VIC-IV personalities: FAST, bit 6 of
$D031, and VFAST, bit 6 of $D054.
| Speed | FAST ($D031 bit 6) |
VFAST ($D054 bit 6) |
Other |
|---|---|---|---|
| 1 MHz | 0 | 0 | |
| 2 MHz | 0 | 0 | C128 2 MHz bit set |
| 3.5 MHz | 1 | 0 | |
| 40.5 MHz | 1 | 1 |
The C128 bit is bit 0 of $D030 in the VIC-II
personality. In the C65 personality that address is the C65 banking
register, which is one reason the personality matters when changing
speed.
$00 shortcutThe CPU port's direction register at $00 has a special
meaning for two values. Writing $41, decimal 65, forces
full speed regardless of the bits above. Writing $40,
decimal 64, removes the force and returns control to them. Neither value
changes the port's direction bits. This is the simplest method and works
in every personality:
lda #$41
sta $00 ; force 40.5 MHz
The CPU catches these values only when the write reaches address
$0000 with the normal mapping there. If MAP covers
$0000-$1FFF, or the base page register B is not
$00 so that sta $00 lands elsewhere, the
shortcut does nothing, and a takeover program that maps the lowest
block must use the bits below instead.
To select a particular speed, release the force and set the bits. The 45GS02's TSB and TRB instructions set and clear bits in memory without disturbing the rest:
lda #$40
sta $00 ; release forced full speed
trb $d054 ; VFAST off
tsb $d031 ; FAST on: 3.5 MHz
Writing to $D031 has a side effect worth knowing. It is
one of the VIC-IV's hot registers, along with $D011,
$D016, $D018 and the VIC bank bits of
$DD00: any write to one of them, even writing back the
value already there, recalculates all the VIC-IV's display settings from
their VIC-II equivalents. FAST lives in $D031, so changing
speed this way can undo a program's VIC-IV screen setup (MEGA65 Book, p.
Q-17). Either clear HOTREG, bit 7 of $D05D, as described in
Chapter 12, or change speed with the $00 shortcut
instead.
From BASIC, SPEED 1, SPEED 3 and
SPEED select 1, 3.5 and 40.5 MHz, and FAST is a synonym;
RSPEED reports the current speed as 1, 3 or 40. Both have no effect, and
RSPEED reports wrongly, once POKE 0,65 has forced full
speed (MEGA65 Book, pp. B-106, B-225, B-245).
A program cannot always assume the speed it set is the speed it gets. RUN/STOP-RESTORE resets the speed to 40.5 MHz (MEGA65 ROM change log), and the user can change speed in the Freezer.
Nor is 1 MHz an exact copy of a C64. At 1, 2 and 3.5 MHz each instruction takes the 6502's number of cycles, but the memory accesses inside it may fall on different cycles, and timing can differ by up to about 7 microseconds. That is harmless for nearly all software, including many fast loaders, but tricks that depend on the exact cycle of a write, such as VIC-II variable screen positioning, are unlikely to work (MEGA65 Book, p. K-5). Timing-critical code should synchronise to the raster rather than counting cycles, at any speed.
Chapter 5 described MAP's registers. This section shows how to use it for the most common job: opening an 8KB window onto memory outside the first megabyte, such as attic RAM.
The three ways to reach distant memory suit different jobs:
| Method | Best for | Cost |
|---|---|---|
Flat [zp],Z |
Scattered reads and writes | Pointer setup; two extra cycles per load and three per store |
| DMA | Moving or filling blocks | Building a DMA list; CPU waits while it runs |
| MAP | Running code from, or working intensively in, a region | Changes what the CPU sees; affects every access in that block |
MAP is the only one of the three that lets the CPU execute code held outside the 64KB window, and it lets ordinary absolute and indexed instructions work on distant data at full speed.
To make attic RAM at $8000000 appear at
$4000-$5FFF, the lower half needs megabyte
$80, and an offset within that megabyte that turns CPU
address $4000 into $00000. That offset is
$00000 - $4000, which wraps within 20 bits to
$FC000. So A holds $C0, offset bits 15-8, and
the low nibble of X holds $F, offset bits 19-16. Block 2 is
enabled by bit 6 of X:
lda #$80 ; megabyte $80: attic RAM
ldx #$0f ; X = $0F: A selects the lower megabyte
ldy #$00
ldz #$00
map
lda #$c0 ; lower offset bits 15-8
ldx #$4f ; enable block 2 ($4000), offset bits 19-16 = $F
ldy #$00
ldz #$00 ; upper half unmapped
map
eom
The megabyte selection persists until changed, so the second MAP sets
the offset within megabyte $80. Only one EOM is needed,
after the final MAP; interrupts stay blocked between the two. Moving the
window is just a matter of repeating the second MAP with a different
offset. To return to the first megabyte, select megabyte 0 the same
way.
In general, the offset for a window is
(target - window) AND $FFFFF, where target is the address
within the selected megabyte and window is the CPU address of the block.
A takes offset bits 15-8, and the low nibble of X takes bits 19-16.
Moving the window up by 8KB means adding $2000 to the
offset, so $FC000 becomes $FE000, then
$00000, and so on.
The 45GS02 has no instruction to read the current MAP settings back,
but the Hypervisor does. Trap $74, hyppo_get_mapping,
writes the MAP state, both halves including their megabyte bytes, as six
bytes to a page-aligned address in the bottom 32KB of bank 0, with the
page number in Y (MEGA65 Book, pp. 14-41, 14-42):
ldy #$62 ; save the map at $6200-$6205
lda #$74 ; hyppo_get_mapping
sta $d640
clv
bcc error
A cooperative routine can use this to save the map it inherited,
change it, and later restore it with the matching service, trap
$76, hyppo_set_mapping, which loads the map from the same
six-byte layout at page Y (MEGA65 Book, Appendix N). Flat addressing and
DMA remain simpler when they suffice, since they leave the map alone,
but MAP is no longer off-limits to code that must return cleanly to
BASIC. A takeover program can still simply keep a copy of the values it
last set.
Chapter 5 showed a single fill job. DMAgic fills memory at 40.5MB per second and copies at 20.25MB per second, against about 3.5MB and 1.7MB per second on a C65 (MEGA65 Book, p. P-3), so almost any bulk data movement is worth handing to it. This section covers the options that make it useful beyond simple blocks.
All examples use enhanced jobs, started by writing the low byte of
the list address to $D705, and the F018A list layout from
Chapter 5: command, count, source, destination, modulo. Two details of
the trigger registers matter. Writing the bank register
$D702 also clears the megabyte register $D704,
so write $D702 first and $D704 after it. And
BASIC 65 itself uses DMA to scroll the screen, so a program should
rewrite all the list address registers before each job rather than
assume they still hold its values (MEGA65 Book, pp. P-3, P-20, P-23).
Two other triggers exist: $D706 takes the list address in
the current CPU memory map, and $D707 runs a list placed
directly after the instruction that triggers it, continuing execution
after the list (MEGA65 Book, p. P-24).
The three address bytes in a DMA list cover only the first megabyte:
low byte, high byte, and a bank in the low nibble of the third byte.
Option $80 sets the source megabyte and option
$81 the destination megabyte. This copies 16KB of level
data from attic RAM at $8010000 to bank 4:
level .byte $0a ; F018A format
.byte $80, $80 ; source megabyte $80 (attic RAM)
.byte $81, $00 ; destination megabyte $00 (chip RAM)
.byte $00 ; end of options
.byte $00 ; command: copy
.word $4000 ; 16KB
.word $0000 ; source address within bank
.byte $01 ; source bank 1: $8010000
.word $0000 ; destination address within bank
.byte $04 ; destination bank 4: $0040000
.word $0000 ; modulo, unused
Addresses wrap within the selected megabyte during a job: a copy that
runs past the end of a megabyte continues at its start. To let a long
copy or fill cross a megabyte boundary, include option $01
in the job, or set bit 1 of $D703 to make that the default
for every job. Both work from core 0.97 and are ignored in hypervisor
mode (mega65-core, gs4510.vhdl; mega65-core PR #830).
Colour RAM at $FF80000 is reached the same way, with
megabyte $FF and bank $08.
Normally DMAgic steps one byte at a time. Options $83
and $85 set the step, in whole bytes, for the source and
destination. With a destination step of 80, a fill draws a vertical line
down an 80-column text screen:
vline .byte $0a
.byte $85, 80 ; destination step: 80 bytes, one row
.byte $00
.byte $03 ; command: fill
.word 25 ; 25 rows
.word $00a0 ; fill with screen code $A0, a solid block
.byte $00
.word $0800+40 ; column 40 of the screen at $0800
.byte $00
.word $0000
Steps are useful wherever data is interleaved: a column of a screen or bitmap, every second byte of the two-byte colour RAM cells used by the VIC-IV's enhanced text modes, or one channel of interleaved sample data.
Bit 2 of the command byte marks a job as chained: when it finishes, DMAgic continues with the list that immediately follows, until it reaches a job without the chain bit. A single trigger can then perform a whole sequence of operations, such as clearing the screen, clearing colour RAM and copying in a new character set. In enhanced mode each job in the chain begins with its own option bytes, so each can use different megabytes and steps. Options carry over from one job to the next until the chain ends, though: a job that needs an option back at its default must set it again, because leaving it out does not reset it.
clear .byte $0a, $00 ; job 1 options: F018A, end
.byte $07 ; fill, chained (bit 2 set)
.word 2000
.word $0020 ; spaces
.byte $00
.word $0800
.byte $00
.word $0000
.byte $0a ; job 2 options: F018A
.byte $81, $ff ; destination megabyte $FF
.byte $00
.byte $03 ; fill, not chained: last job
.word 2000
.word $0001 ; colour 1, white
.byte $00
.word $0000
.byte $08 ; bank 8 of megabyte $FF: colour RAM
.word $0000
The option list offers more than megabytes and steps (MEGA65 Book, p.
P-14). Option $07 turns on a transparent value and
$86 sets it: bytes equal to that value are skipped instead
of copied, leaving the destination unchanged. That turns a DMA copy into
a simple blitter for shapes with a background colour:
blit .byte $0a
.byte $07 ; enable transparency
.byte $86, $00 ; bytes of $00 are not written
.byte $00 ; end of options
.byte $00 ; command: copy
.word 64 ; one FCM character's 64 bytes
.word $2000 ; source $42000
.byte $04
.word $0040 ; destination $40040
.byte $04
.word $0000
Options $82 and $84 add fractional steps,
in 256ths of a byte, which DMAgic uses for texture scaling, and options
$87 to $8F and $97 to
$9F configure a line-drawing mode that steps the
destination or source along a slope. The Book quotes rates of up to 20
million pixels per second for scaling and 40 million for line drawing;
its DMA chapter documents the parameters (MEGA65 Book, pp. P-13,
P-14). On release 0.97 cores a line comes out two pixels shorter than
its count, so add 2. Development cores fix this, let the direction bit
reverse the major axis, and add line scaling through bit 4 of option
$8F or $9F (mega65-core PR #887).
Not every F018 operation exists. Only copy and fill are implemented: the command values for swap and mix, and the minterm bits that mix used, do nothing useful on the MEGA65 (MEGA65 Book, pp. P-6, P-24).
The CPU stops while a job runs and resumes at the instruction after the trigger. There is no need to poll for completion, but because the DMA controller is part of the CPU, a job cannot be interrupted, and a long one delays interrupts too (MEGA65 Book, p. P-3). A raster interrupt that must fire on time should not compete with a very large transfer; split big jobs into pieces timed to fit between interrupts. The command byte has a bit documented as "yield to interrupts", bit 3, but the Book's own text says jobs cannot be interrupted, so do not rely on it.
Audio DMA is the exception: it keeps playing during an ordinary job, stealing the cycles it needs from it, so sound does not stop while DMA runs (MEGA65 Book, p. P-3).
A raster interrupt fires when the video chip reaches a chosen line of the display. It is the standard way to change colours, scroll registers or screen modes partway down the screen, and to run code once per frame at a steady rate. The VIC-IV supports the VIC-II's raster interrupt registers unchanged, so the C64 technique carries over.
| Register | Purpose |
|---|---|
$D012 |
Raster compare, bits 7-0 |
$D011 bit 7 |
Raster compare, bit 8 |
$D01A bit 0 |
Enable the raster interrupt |
$D019 bit 0 |
Raster interrupt has occurred; write 1 to acknowledge |
This continues from the takeover skeleton earlier in the chapter, with interrupts disabled, CIA interrupts off and the KERNAL switched out. It installs its own vectors and changes the border colour at two raster lines, splitting the border into two colours:
lda #<nmi
sta $fffa
lda #>nmi
sta $fffb
lda #<irq
sta $fffe
lda #>irq
sta $ffff
lda #$00
sta line ; first interrupt at line 0
sta $d012
lda $d011
and #$7f ; compare bit 8 = 0
sta $d011
lda #$01
sta $d01a ; enable raster interrupt
sta $d019 ; clear any pending one
cli
main jmp main ; the main program runs here
irq pha
lda line
bne lower
lda #$06 ; blue at the top
sta $d020
lda #$90
bra set
lower lda #$02 ; red from line $90 down
sta $d020
lda #$00
set sta line
sta $d012 ; next interrupt line
lda #$01
sta $d019 ; acknowledge
pla
rti
nmi rti ; ignore RESTORE
line .byte 0
The handler alternates between two lines, setting the next compare value each time. It saves only A because it changes only A. A handler that uses X, Y or Z must save those too, and one that changes B must restore it before RTI.
On a C64, raster code at 1 MHz suffers from jitter: the interrupt can start a few cycles late depending on which instruction it interrupted, which shows as a ragged edge on colour changes. At 40.5 MHz the same few cycles are a tiny fraction of a scan line, so most raster effects are clean without the elaborate stabilisation C64 code needs. Code that runs at 1 MHz for compatibility still has the C64's jitter.
The VIC-II-style registers count VIC-II raster lines. The VIC-IV also works in physical display lines, which allows finer positioning (MEGA65 Book, pp. Q-43, Q-44):
| Register | Name | Purpose |
|---|---|---|
$D052, low bits of $D053 |
FNRASTER | Current physical raster line (read) |
$D053 bit 7 |
FNRST | Reads back the compare mode: 0 physical, 1 VIC-II lines |
$D079, low bits of $D07A |
RASCMP | Physical raster compare value, used when the compare mode is physical |
$D07A bit 6 |
EXTIRQS | Enable extra interrupt sources, such as raster X position |
$D06F bits 5-0 |
RASLINE0 | First VIC-II raster line within the physical display |
The compare mode is selected by FNRSTCMP, bit 7 of
$D07A: clear for physical lines, set for VIC-II lines. FNRST
reads back the same bit. There are
roughly twice as many physical lines as VIC-II lines in the standard
display, so a physical compare can split the screen between two VIC-II
lines.
A program that keeps the KERNAL running can still use raster
interrupts, by chaining into the KERNAL's handler instead of replacing
the hardware vector. In C64 mode this works exactly as on a C64, through
the vector at $0314, with the handler ending in a jump to
the KERNAL's own handler so that keyboard scanning continues. Chapter 11
gives the equivalent MEGA65-mode vector.
The KERNAL loads files from disk drives and disk images. The Hypervisor can load files straight from the SD card's FAT32 file system, without a disk image, and it works even when a takeover program has switched the KERNAL out. It can also load directly into attic RAM; the KERNAL can too only from ROM 920398, through SETBNK's 28-bit form.
All Hypervisor services follow the same convention: put the service
number in A, write it to $D640, and follow with CLV. The
CPU may or may not execute the byte after the trap when the service
returns, which is why a harmless CLV goes there; NOP also works, but it
can act as a prefix to the next instruction, so CLV is safer. On return,
carry set means success; carry clear means failure, with the error code
in A. Registers not used to return values are preserved, and the trap
register exists only in the MEGA65 I/O personality (MEGA65 Book, pp.
N-4, N-5). The Book documents Hyppo version 1.2.
| A | Service | Inputs |
|---|---|---|
$2E |
hyppo_setname | Y: page of a zero-terminated ASCII file name |
$36 |
hyppo_loadfile | X, Y, Z: bits 7-0, 15-8 and 23-16 of the load address in chip memory |
$3E |
hyppo_loadfile_attic | X, Y, Z: bits 7-0, 15-8 and 23-16 of the load address in attic RAM |
hyppo_setname has strict requirements: the name must start on a page
boundary somewhere in $0000-$7E00, must be shorter than 63
characters, and must end with a zero byte. Only Y is passed, the page
number. The name is ASCII, and Hyppo does not convert from PETSCII; it
also sets the Hypervisor's transfer area to that page as a side effect
(MEGA65 Book, p. N-38).
The load services load to $00ZZYYXX in chip memory or
$08ZZYYXX in attic RAM, and can load files up to 16MB,
wrapping around at the end of the 16MB range. The commonest failure is
error $88, file not found in the current directory (MEGA65
Book, pp. N-27, N-28).
load ldy #>fname ; page of the file name
lda #$2e ; hyppo_setname
sta $d640
clv
bcc error
ldx #$00 ; load address $0040000
ldy #$00
ldz #$04
lda #$36 ; hyppo_loadfile
sta $d640
clv
bcc error
rts
error ; A holds the error code, e.g. $88 file not found
rts
.align 256 ; the name must start a page (syntax varies)
fname .text "LEVEL1.BIN", 0
The Hypervisor ignores case when it matches names, but it needs
ASCII codes. Many assemblers' text directives produce PETSCII, which
swaps the cases: a PETSCII directive such as ACME's !pet
turns capitals into codes $C1-$DA, which the Hypervisor
will not match. Use a directive that stores text unconverted (mega65-core,
Hyppo dos.asm).
The file is loaded exactly as stored. A file saved as a PRG carries a two-byte load address at the front, and the load call places those two bytes in memory too; for raw data it is simpler to save files without them.
Hyppo reads files from the FAT32 file system on the SD cards, in the
current directory; it does not read floppy disks or disk images (MEGA65
Book, p. N-38). Hypervisor services have changed between versions, and
newer ones may not exist on an older Hypervisor, so a program that
depends on them should check the version first with hyppo_getversion,
service $00.
Machine language fails silently: a wrong address or a missing RTS usually means a hung machine with no message. The MEGA65 offers several tools for finding out what happened, each suited to a different kind of problem.
The MONITOR introduced in Chapter 7 is an extended version of the C65's monitor (MEGA65 Book, p. O-6). Beyond assembling and disassembling, it can display and change registers, fill, compare, transfer and search memory, and load and save memory to disk. Because it understands 28-bit addresses, it can inspect attic RAM, colour RAM and the I/O personalities directly. The MEGA65 Book lists every command (MEGA65 Book, p. O-6).
The simplest breakpoint is a BRK instruction. Place one where you want execution to stop, run the program, and the monitor appears with the registers as they were at that point. This works only while the KERNAL and its vectors are in place, because the KERNAL's BRK handler starts the monitor; a takeover program's BRK goes to its own interrupt handler. Replace the BRK with the original instruction afterwards. Check the stack pointer and B in the register display first: a wrong B or a runaway stack is a common cause of crashes.
For timing problems and code that runs inside interrupts, the oldest
trick still works best. An INC $D020 at the start of a
routine and a DEC $D020 at the end turn the border a
different colour while the routine runs. The height of the coloured band
shows how much of the frame the routine takes, and a band that stops
moving shows where a program has hung.
Holding RESTORE for about a second and releasing it stops any program and opens the Freezer, which saves the complete machine state. From there you can examine memory in the frozen program, change settings, or resume. This works even when a takeover program has disabled interrupts and switched the KERNAL out, because the Freezer runs under the Hypervisor rather than the KERNAL.
Matrix Mode is a debugging console run by a small helper processor in
the core, separate from the main CPU; the Hypervisor only switches it on
and off (mega65-core, monitor_top.v; MEGA65 Book, monitor
appendix). Hold MEGA and press TAB to open it over the running display,
and press the same combination again to close it. Because it runs on
its own processor, it works even when the program has hung. Its R
command, or just RETURN, shows all the CPU registers, including the
MAPHI and MAPLO values that no instruction can read, which makes it the
quickest way to see what a program has done to the memory map (MEGA65
Book, pp. 14-22, O-14).
Matrix Mode can also step through a program. t1 pauses
the CPU in trace mode, and each press of RETURN then runs one
instruction and shows the registers; t0 lets the program
run again. From core 0.97, interrupts are blocked while stepping with
t1, so an IRQ does not take the step into the interrupt
handler; t2 pauses with interrupts still allowed. The
W command sets a watchpoint that stops the program when a
chosen byte is written (MEGA65 Book, monitor appendix; mega65-core PR #848).
On release 0.97 cores, single-stepping or inspecting memory from Matrix Mode while a program runs INQ, DEQ or one of the Q register shift instructions can make the program skip a byte and crash; development cores fix this (mega65-core PR #943). The same console is available over a UART serial or JTAG connection, where cross-development tools on a PC can also load programs and inspect memory.
For larger projects, a PC-based emulator with a debugger, such as Xemu, is often quicker still. Its monitor offers an execution breakpoint and a watchpoint that stops when a chosen byte changes, the fastest way to find out which piece of code is overwriting a variable; Matrix Mode's W command does the same on real hardware (Xemu; MEGA65 Book, monitor appendix). An emulator's timing may not match the hardware in every detail, so timing-critical code should always be checked on real hardware.
$01, MAP, the I/O personality, $D030, B and
the stack themselves, then install their own vectors. MAP takes
precedence over other banking, but only for the blocks it maps.$D031 and VFAST in
$D054 select 1, 3.5 or 40.5 MHz; writing $41
or $40 to $00 forces or releases full speed,
and BASIC has SPEED and RSPEED. $D031 is a hot register.
Slow modes match the 6502's cycle counts but not its exact access
timing.$74 copies it to
memory, and Matrix Mode shows it.Chapter 9 turns to combining machine language with BASIC 65.