Tuesday, July 9, 2024

Disassembler for use with WozMon


Disassembler disassembling WozMon

WozMon is a great little monitor program in 256 bytes which Steve Wozniak wrote for his Apple 1 release in 1976.  It was small but powerful allowing for memory inspection, entry of 6502 machine code programs and bytes, and launching of programs in ROM and RAM (jumping to address).

But being small, it is missing features that monitor users have learned to depend on, including a mini-assembler and disassembler.

So here I am today presenting a disassembler I wrote for 6502 which can be integrated with WozMon run command.  Once the disassembler gets control, the Y register contains a pointer to the last character read from the input buffer, and continues parsing for a hex address.  If not found, it simply returns to WozMon.  But if found, it will disassemble 20 statements starting at that address.  (Note: Vic-20 port above, changed to 17 statements due to the smaller screen size).

The syntax may be lengthier than other monitors, but it allows for an integration with the normally ROM based WozMon without modifications.  Plus you get what you pay for.   The disassembler is open source, so feel free to change it.

The disassembler came into being as part of developing a mini-assembler.  First I'd written a prototype in C# .NET. 

The assembler included tables of information for the 6502 instruction set and addressing modes that lent itself to be used as a disassembler (came second) which was prototyped as a 6502 program that disassembles itself.  Third came the disassembler compatible for use with WozMon.

Each prototype and program is one step along the way for the goal of creating a mini-assembler for my 6502 minimal system.  Not there yet, but getting closer with each step!

Also works with original WozMon in Apple 1 style environment

Also works on C64 and C128 systems

Wednesday, June 12, 2024

Strings or Compact directory utility for C64

Directory listings scrolling by too fast?  Want to scan program for string literals?  Here's a stupid (sometimes useful, definitely ugly) utility I created in the monitor for both scenarios.  Compact display and pauses every 24 lines.  Attempts to avoid control characters.

Note: operates on whatever loaded program is in memory, assuming BASIC. 

Shown loading into tape buffer (828), can be relocated. 

This source is an example of parsing a BASIC program.  The beginning of BASIC is grabbed from $2B/2C, and each line of the program is iterated through traveling its linked list ending with a null pointer.  In each line, a string starts with double quotes and ends with double quotes or end of line (zero or nul character).  Multiple strings per lines are possible.   Other memory addresses used include toggling the reverse flag ($C7), setting quote ($D4) and insert ($D8) modes, and counting each time at the left column ($D3).  Outputs string characters via $FFD2, and waits for a key press via $FFE4.

This is a short program that can be studied.  I hope you find it useful and informative.  Enjoy!

Link: strings.prg
Copyright (c) 2024 by David R. Van Wagner

Sunday, June 2, 2024


While adding banking support to my own C64 emulator back in the good 'ole days (April 14, 2020, maybe only good for hunkering down on retro stuff), I also wrote a test program to help verify the results.  This may help you visualize how you can access more RAM and ROM on the C64.

The program works by switching to the RAM only bank writing a signature high byte to four addresses, then cycles through the standard list of banks reading the value at those locations.  Of course the first line for bank zero shows the bytes as expected.  But the other banks tell a different story.

A000-BFFF is BASIC ROM.   Appears ROM is active in banks 3 & 7 only, otherwise is RAM.

C000-CFFF is always RAM.

D000-DFFF is usually I/O (banks 5-7) including color RAM at D8000 (notice the high nibble changes in this run).  May also appear as RAM (banks 0 & 4), CHAR ROM (banks 1-3).

E000-FFFF is BASIC ROM (banks 2, 3, 6, 7), otherwise RAM (banks 0, 1, 4, 5).

It is interesting to see the similarities and differences in this chart.

The key code to switching banks includes


        lda $01

        and #$f8

        ora #bank_selection

        sta $01


Only clear the interrupt flag (cli instruction) once you have returned to the normal bank (7) so IRQ vectors and the KERNAL/BASIC ROMs will work as expected.


Sunday, May 26, 2024

LCD version of 6502 emulators ported to Windows


LCDs and Windows too!

Now the c-simple-emu6502-cbm unified branch that works with LCD systems has been ported to Windows!  This is an emulator that includes a feature to switch between various popular Commodore 8-bit models from the BASIC prompt.

You may ask yourself, hey self, wasn't Windows already a supported system?  And you could answer, yes self, it was.  But only in text mode.  If you wanted to run Commodore BASIC from a command prompt, yes you could do that.  If you wanted to run this emulator with the nifty "GO 128" command in a graphical environment you were required to use those smallish LCDs (well the 7" isn't too smallish).

Now, by porting this C++ project back to Windows again, using GUI elements, it can now look more like a Commodore.  The fully resizable window, and keyboard support make it feel like you have a Commodore right in front of you.  You virtually do!

For now, you need to compile the project in Visual Studio 2022 (Community Edition should work just fine).  It probably also works from Visual Code, but I haven't attempted that yet.

Dependencies include a roms folder (see README.md), optional disks/drive8.d64, and optional disks/drive9.d64

The purpose of this is to ease development of new features, utilizing the feature rich Visual Studio IDE including debug support.

The benefits to other users include being able to test drive the project in an environment they already have - their existing Windows desktop or VM.   And if you love it and think you may enjoy a portable version, you can then invest your dollars into an LCD solution via various online retail websites.   There may even be a search engine out there to help you too.

Are you keeping up with Commodore?  Happy computing!  Spend that money wisely.

Thursday, May 9, 2024

Running SWEET16, Steve Wozniak's "The Dream Machine" from WozMon

The story goes that Steve Wozniak was running out of space in Apple Integer BASIC, and out of necessity, determination, and will power, developed SWEET16 to do 16-bit operations in a more compact fashion.  There is already a great article on SWEET16, the interpretive processing 16-bit supplement to 6502 machine language programs, over at 6502.org.  The article tells the history, instructs how to port, and provides a tutorial on how to use it.  (And see article on Wikipedia, and Steve Wozniak's 1977 article in Byte magazine).

My article will follow the steps to produce a binary to run on my minimal 6502 with MC6850 UART, and run through a few examples to use it together with the Apple-1 Hex Monitor, ported from listings included in the Apple-1 Operation Manual (1976).  The Hex Monitor (or WozMon as people generally refer to it) is the original 256-byte ROM included with the Apple-1 computer.  It only needed to include display memory, edit memory, and run from memory operations to initiate control of that computer.

This article and efforts are inspired by videos produced by Ben Eater and his 6502 breadboard computer.  I wanted to do something similar, and followed instructions he provided to do something similar with my 6502 emulator, but simpler.   

I already had a 6502 emulator.  One version is in C++, is very cross-platform (Windows, Linux, Mac, Arduino, STM32, etc.).  But I had focused on Commodore emulation to match the computers I grew up with learning to program.  While that is working well, it is using Commodore ROMs and some I/O emulation. 

Also inspirational are similar videos produced by Michael Cartwright for his Hopper development environment and 6502 breadboard and PCB computer, inspired by Ben Eater's 6502 breadboard computer.  Michael (sillycowvalley on github) uses the MC6850 UART instead of the MC6821 PIA used in Apple-1, or MOS6522 VIA or MOS6551 UART used by Ben Eater.

I surely didn't want to bit bang serial, and wanted it as simple as possible.  Reviewing resources on the web, including datasheets, it was apparent the MC6850 has an advantage of being rather minimalistic.  It has one data register (read/write), one control register (write), and one status register (read).  While originally intended for use with 6800 series microprocessors, it can be used with others.  Only caveat is that the status is not designed for the 6502 BIT instruction.  But the advantage is that it doesn't have a status bug like the 6551 which intended to replace it.

So, I sat down and added a minimal emulation profile to my emulator including configurable RAM/ROM, and I/O address for the MC6850.  My initial implementation supports polling but not interrupts, which is fine for our purposes today.  It does not include a parallel I/O chip, so no hooking up virtual LEDs.  Wait, what?  Oh yeah, we don't need those (yet?), we exclusively have a terminal console.   But that also means we don't have a way to load or save programs.

Also imagine 60K RAM and 4K ROM here (oops!)

I ported WozMon to the 6502/MC6850 minimum profile so it will serve as our launching off point, similar to how it was utilized on the Apple-1.  Porting required changing the assembler syntax to match my 6502 assembler of choice (ACME), especially note that all the hex values in the file should include # for immediate mode (if extracting from Usenet article instead of Byte article), DFB must stand for define byte, and you do need to include some save/restore register routines.

An advantage of having a minimal system unlike Commodore, is that the 32 consecutive zero-page bytes necessary for SWEET16 are guaranteed to be available because it is a minimal system, addresses are not used by IRQ routines, keyboard handlers, timers, KERNAL I/O, etc.


1. Clone (or download Zip of) the emulator c-simple-6502-cbm.  Note it is not necessary to gather the Commodore ROMs this time around, because we don't need them.

2. Compile using Visual Studio, or make/gcc (Makefile included), or similar.

3. Look for wozmon.bin in the roms/minimal folder (its ported source is in child src folder).

4. Launch the minimal profile

    c-simple-6502-cbm.exe 1 roms/minimal/wozmon.bin

Now WozMon should be running.  You can review the Apple-1 Operation Manual (1976) for more detailed instructions on how to use WozMon.  But if you're quick, you can follow along with the required inputs here.

5. Paste SWEET16 binary into WozMon.   See copyright at start of article, included here for educational purposes, trusting this is fair use and for your own personal use.  Not for commercial use.

# SWEET16 compiled for EE80 start address
EE70: 20 EA EE 68 85 1E 68 85
EE78: 1F 20 7F EE 4C 79 EE E6
EE80: 1E D0 02 E6 1F A9 EF 48
EE88: A0 00 B1 1E 29 0F 0A AA
EE90: 4A 51 1E F0 0B 86 1D 4A
EE98: 4A 4A A8 B9 C8 EE 48 60
EEA0: E6 1E D0 02 E6 1F BD CB
EEA8: EE 48 A5 1D 4A 60 68 68
EEB0: 20 F6 EE 6C 1E 00 B1 1E
EEB8: 95 01 88 B1 1E 95 00 98
EEC0: 38 65 1E 85 1E 90 02 E6
EEC8: 1F 60 00 F7 02 9B 0B 9C
EED0: 23 AD 14 B0 45 B7 4F BE
EED8: 2D C7 59 D0 83 DB 6C 03
EEE0: 31 E6 6E 91 1C E5 63 E5
EEE8: E5 E5 85 20 86 21 84 22
EEF0: 08 68 85 23 D8 60 A5 23
EEF8: 48 A5 20 A6 21 A4 22 28
EF00: 60 10 B3 B5 00 85 00 B5
EF08: 01 85 01 60 A5 00 95 00
EF10: A5 01 95 01 60 A5 00 81
EF18: 00 A0 00 84 1D F6 00 D0
EF20: 02 F6 01 60 A1 00 85 00
EF28: A0 00 84 01 F0 ED A0 00
EF30: F0 06 20 64 EF A1 00 A8
EF38: 20 64 EF A1 00 85 00 84
EF40: 01 A0 00 84 1D 60 20 24
EF48: EF A1 00 85 01 4C 1D EF
EF50: 20 15 EF A5 01 81 00 4C
EF58: 1D EF 20 64 EF A5 00 81
EF60: 00 4C 41 EF B5 00 D0 02
EF68: D6 01 D6 00 60 A0 00 38
EF70: A5 00 F5 00 99 00 00 A5
EF78: 01 F5 01 99 01 00 98 69
EF80: 00 85 1D 60 A5 00 75 00
EF88: 85 00 A5 01 75 01 A0 00
EF90: F0 E9 A5 1E 20 17 EF A5
EF98: 1F 20 17 EF 18 B0 0E B1
EFA0: 1E 10 01 88 65 1E 85 1E
EFA8: 98 65 1F 85 1F 60 B0 EC
EFB0: 60 0A AA B5 01 10 E8 60
EFB8: 0A AA B5 01 30 E1 60 0A
EFC0: AA B5 00 15 01 F0 D8 60
EFC8: 0A AA B5 00 15 01 D0 CF
EFD0: 60 0A AA B5 00 35 01 49
EFD8: FF F0 C4 60 0A AA B5 00
EFE0: 35 01 49 FF D0 B9 60 A2
EFE8: 18 20 64 EF A1 00 85 1F
EFF0: 20 64 EF A1 00 85 1E 60
EFF8: 4C AE EE 00 00 00 00 00

Note: SWEET16 entry point for this port is $EE70, and the operations are in a single page $EF01-$EFFA.   A save registers routine is at $EEEA and restore registers is at $EEF6.   Zero page usage is $00-$1F for the SWEET16 registers, and $20-$23 for saving 8-bit registers.

6. Let's try it out.  Start by pasting the following to enter a program at 

1000: 20 00 FE A9 2A 20 70 EE 
1008: 11 12 34 12 56 78 21 A2
1010: 33 00 8D F8 FF 00

Which is equivalent to

20 00 FE JSR $FE00
A9 2A    LDA #$2A
20 70 EE JSR $EE70
11 12 34 SET R1,$3412
12 56 78 SET R2,$7856
21       LD  R1 
A2       ADD R2
33       ST  R3
00       RTN
00       BRK

JSR $EE70 switches to SWEET16 interpretation, and these are SWEET16 instructions through RTN, before being back in 6502.  The vectors are set such that a BRK will return to WozMon. ($FE00 is my specific UART initialization routine for MC6850, and storing a byte to $FFF8 will send it across the UART).

All this program does is set the R1 and R2 16-bit registers to constant values, the LD instruction loads the 16-bit accumulator from R1, then ADD R2 adds R2 to the accumulator, and the result is stored in R3 by the ST instruction.   RTN returns to normal 6502 operation.

7. To run the program, enter the following into WozMon:


8. Display the results by entering


From the last two steps the output should look something like the following (blank lines added for separation)


1000: 20*\


0000: 68 AC 12 34 56 78 68 AC
0008: 00 00 00 00 00 00 00 00
0010: 00 00 00 00 00 00 00 00
0018: 00 00 00 00 00 06 12 10

And the accumulator (R0) at addresses 0 and 1 now has the value $AC68 which appears to be the sum of the other two 16-bit values, with a copy also stored at R3 (addresses 6/7).  The asterisk is printed to demonstrate registers being saved/restored and 6502 operation returning to normal.

That's just a quick example.   Hopefully you've got a little taste of WozMon and SWEET16. 

Example run of SWEET16

But that's not all!  

I made a Commodore port of WozMon, so why not also port SWEET16 to Commodore and run it there too?

# Commodore port of SWEET16
1540: 85 22 86 23 84 24 08 68
1548: 85 25 d8 60 a5 25 48 a5
1550: 22 a6 23 a4 24 28 60 00
1558: 00 00 00 00 00 00 00 00
1560: 00 00 00 00 00 00 00 00
1568: 00 00 00 00 00 00 00 00
1570: 00 00 00 00 00 00 00 00
1578: 00 00 00 00 00 00 00 00
1580: 20 40 15 68 85 7e 68 85
1588: 7f 20 8f 15 4c 89 15 e6
1590: 7e d0 02 e6 7f a9 16 48
1598: a0 00 b1 7e 29 0f 0a aa
15a0: 4a 51 7e f0 0b 86 7d 4a
15a8: 4a 4a a8 b9 d8 15 48 60
15b0: e6 7e d0 02 e6 7f bd db
15b8: 15 48 a5 7d 4a 60 68 68
15c0: 20 4c 15 6c 7e 00 b1 7e
15c8: 95 61 88 b1 7e 95 60 98
15d0: 38 65 7e 85 7e 90 02 e6
15d8: 7f 60 00 f7 02 9b 0b 9c
15e0: 23 ad 14 b0 45 b7 4f be
15e8: 2d c7 59 d0 83 db 6c 03
15f0: 31 e6 6e 91 1c e5 63 e5
15f8: e5 e5 00 00 00 00 00 00
1600: 00 10 c3 b5 60 85 60 b5
1608: 61 85 61 60 a5 60 95 60
1610: a5 61 95 61 60 a5 60 81
1618: 60 a0 00 84 7d f6 60 d0
1620: 02 f6 61 60 a1 60 85 60
1628: a0 00 84 61 f0 ed a0 00
1630: f0 06 20 64 16 a1 60 a8
1638: 20 64 16 a1 60 85 60 84
1640: 61 a0 00 84 7d 60 20 24
1648: 16 a1 60 85 61 4c 1d 16
1650: 20 15 16 a5 61 81 60 4c
1658: 1d 16 20 64 16 a5 60 81
1660: 60 4c 41 16 b5 60 d0 02
1668: d6 61 d6 60 60 a0 00 38
1670: a5 60 f5 60 99 60 00 a5
1678: 61 f5 61 99 61 00 98 69
1680: 00 85 7d 60 a5 60 75 60
1688: 85 60 a5 61 75 61 a0 00
1690: f0 e9 a5 7e 20 17 16 a5
1698: 7f 20 17 16 18 b0 0e b1
16a0: 7e 10 01 88 65 7e 85 7e
16a8: 98 65 7f 85 7f 60 b0 ec
16b0: 60 0a aa b5 61 10 e8 60
16b8: 0a aa b5 61 30 e1 60 0a
16c0: aa b5 60 15 61 f0 d8 60
16c8: 0a aa b5 60 15 61 d0 cf
16d0: 60 0a aa b5 60 35 61 49
16d8: ff f0 c4 60 0a aa b5 60
16e0: 35 61 49 ff d0 b9 60 a2
16e8: 18 20 64 16 a1 60 85 7f
16f0: 20 64 16 a1 60 85 7e 60
16f8: 4c be 15

This SWEET16 entry point for Commodore changed to $1580 so the sample program has to change to call this entry point instead, to call $FFD2 for screen output instead of using the UART, and jump to WozMon entry at end instead of BRK.   Note that this Commodore port puts the SWEET16 registers at $60 (instead of $00) in zero page.   The Commodore port was trivial, disassembly will show there's nothing secret going on, mostly moving around stuff in zero page, and moving the save/load registers code.

1000: A9 2A 20 80 15 11 12 34
1008: 12 56 78 21 A2 33 00 20
1010: D2 FF 4C 00 14
Vic-20 running WozMon and SWEET16

C64 running WozMon and SWEET16

It appears there's a bug restoring the registers.  That is not an asterisk!!!   But an underscore.   Exercise left for another day...   Update: I had replaced the call to UART_INIT from the other system with starting SWEET16 followed by 6502 code.  Bad, so bad (see screenshot for bad code).  Have since updated the sample program to be correct.   Disassembling here to double check.   The asterisk is now showing up fine!  Would have been doing some crazy stuff interpreting 6502 opcodes as SWEET16.

A9 2A    LDA #$2A
20 80 15 JSR $1580
11 12 34 SET R1,$3412
12 56 78 SET R2,$7856
21       LD  R1 
A2       ADD R2
33       ST  R3
00       RTN
20 D2 FF JSR $FFD2
4C 00 14 JMP $1400

Wednesday, May 1, 2024

C64 RAM locations for programmable characters


Taking the information from Jim Butterfield's interactive program we can determine a few optimal choices for configuring the location of video and character set memory.  They both need to be in the same 16K bank of the C64 memory, video memory needs to be accessible to the KERNAL, and some locations are off limits (offsets $1000-$1FFF within VIC-II 16K banks 0, 2) because the VIC-II only sees D000-DFFF ROM characters there.

Option 1: easiest, video $0400, chars $2000-$2FFF

Option 2: least BASIC free, video $8C00, chars $A000-$AFFF

Option 3: largest free, video $CC00, chars $F000-$FFFF

Option 4: single charset, video $C400, chars $C800-$CFFF

Thanks to Jim Butterfield's SCREENMAP 64 program, it prompts for you to choose between valid options, and then calculates the register and screen page values that you need to change(poke).  Shown above are details for the four options that seem best.  The first three allow for two full sets of characters.  The last one allows for a single character set, or one and a half character sets.

In addition to the steps shown, it may be necessary to install an NMI handler to keep the values set instead of reset to ROM defaults, and it will be necessary to load a font into the desired programable character set address range.

Options 3 and 4 are interesting because they both increase BASIC RAM by 1K more than normal by moving video screen memory elsewhere. Option 1 is the easiest to implement, so is perfect for getting started.


1000-1FFF not available for VIC because maps to D000 ROM
5000-5FFF not available for VIC because maps to D000 ROM
9000-9FFF not available for VIC because maps to D000 ROM
D000-DFFF not available for VIC because maps to D000 ROM

C000-CFFF not recommended for prog. chars. because other RAM in that bank not usually available to KERNAL needed for video screen characters.   So if screen characters are there, only room for one set (2K) instead of two sets (4K)

Option 0 (Normal)
0400-07FF screen memory (1K) = 1024
0800-9FFF BASIC RAM (38K) 
C000-CFFF free (4K)
D000-DFFF character ROM banked under I/O

Option 1 (First 16K)
0400-07FF screen memory (1K) = 1024
0800-1FFF free (6K)
2000-2FFF prog. chars. (4K) = 8192
3000-9FFF BASIC RAM (28K)

4000-7FFF is smack dab in the middle of BASIC RAM, seems like a bad idea unless used with 6502 machine language or other assets around it, and not strictly BASIC.

Option 2 (Third 16K)
0400-8BFF BASIC RAM (34K)
8C00-8FFF screen memory (1K) = 35840
9000-9FFF free (4K)
A000-AFFF prog. chars in RAM bank under BASIC ROM = 40960

Option 3 (Fourth 16K)
0400-9FFF BASIC RAM (39K)
C000-CBFF free (3K)
CC00-CFFF screen memory (1K) = 52224
E000-EFFF = 57344 prog. chars in RAM bank under KERNAL ROM *or* F000-FFFF = 61440 *or* under CHAR ROM D000-DFFF = 53248

Option 4 (Fourth 16K)
0400-9FFF BASIC RAM (39K)
C000-C3FF free (1K) or half lowercase character set (no inverse characters)
C400-C7FF screen memory (1K) = 50176
C800-CFFF prog. chars (1 set, 2K only) = 51200

Tuesday, April 30, 2024

Raster Interrupts for C64


The VIC II chip (video IC) that gives the C64 a lot of its characters (pun intended), includes registers to set up interrupts at certain raster lines.  This allows for video tricks.  Above the background color is changed in the middle of the screen, and changed back when in the borders before drawing the screen again.

This code was leveraged from my character editor source for c64.  That project needed to display multiple different character sets (from ROM and RAM) on the screen simultaneously.   And that is described at the bottom of an earlier blog post.

My approach was to launch Vice x64sc (Commodore 64 emulator) and utilize its built in monitor (ALT-H) to craft prototype code.  The listings following are what I came up with.   Then I used the clipboard to move to a source editor, added labels, and further refined to change from background colors to font changes.

Written by Dave VW 2024-04-27


This part was created in the monitor...

>C:c000  2c 19 d0 10  44 ea ad 19  d0 29 01 f0  3b 2c 00 c1
>C:c010  30 16 10 60  ea a9 00 8d  12 d0 ad 11  d0 29 7f 8d
>C:c020  11 d0 0e 00  c1 4c 3b c0  ce 21 d0 a9  a4 8d 12 d0
>C:c030  ad 11 d0 29  7f 8d 11 d0  4e 00 c1 a9  01 8d 19 d0
>C:c040  ea 68 a8 68  aa 68 40 00  ea 4c 31 ea  a9 40 8d 00
>C:c050  c1 78 a9 80  8d 12 d0 ad  11 d0 29 7f  8d 11 d0 a9
>C:c060  00 8d 14 03  a9 c0 8d 15  03 ad 1a d0  09 01 8d 1a
>C:c070  d0 58 60 00  ad 12 d0 c9  c2 90 f9 a2  0a ca d0 fd
>C:c080  ee 21 d0 4c  15 c0                                

.C:c000  2C 19 D0    BIT $D019
.C:c003  10 44       BPL $C049
.C:c005  EA          NOP
.C:c006  AD 19 D0    LDA $D019
.C:c009  29 01       AND #$01
.C:c00b  F0 3B       BEQ $C048
.C:c00d  2C 00 C1    BIT $C100
.C:c010  30 16       BMI $C028
.C:c012  10 60       BPL $C074
.C:c014  EA          NOP
.C:c015  A9 00       LDA #$00
.C:c017  8D 12 D0    STA $D012
.C:c01a  AD 11 D0    LDA $D011
.C:c01d  29 7F       AND #$7F
.C:c01f  8D 11 D0    STA $D011
.C:c022  0E 00 C1    ASL $C100
.C:c025  4C 3B C0    JMP $C03B
.C:c028  CE 21 D0    DEC $D021
.C:c02b  A9 A4       LDA #$A4
.C:c02d  8D 12 D0    STA $D012
.C:c030  AD 11 D0    LDA $D011
.C:c033  29 7F       AND #$7F
.C:c035  8D 11 D0    STA $D011
.C:c038  4E 00 C1    LSR $C100
.C:c03b  A9 01       LDA #$01
.C:c03d  8D 19 D0    STA $D019
.C:c040  EA          NOP
.C:c041  68          PLA
.C:c042  A8          TAY
.C:c043  68          PLA
.C:c044  AA          TAX
.C:c045  68          PLA
.C:c046  40          RTI
.C:c047  00          BRK
.C:c048  EA          NOP
.C:c049  4C 31 EA    JMP $EA31
.C:c04c  A9 40       LDA #$40
.C:c04e  8D 00 C1    STA $C100
.C:c051  78          SEI
.C:c052  A9 80       LDA #$80
.C:c054  8D 12 D0    STA $D012
.C:c057  AD 11 D0    LDA $D011
.C:c05a  29 7F       AND #$7F
.C:c05c  8D 11 D0    STA $D011
.C:c05f  A9 00       LDA #$00
.C:c061  8D 14 03    STA $0314
.C:c064  A9 C0       LDA #$C0
.C:c066  8D 15 03    STA $0315
.C:c069  AD 1A D0    LDA $D01A
.C:c06c  09 01       ORA #$01
.C:c06e  8D 1A D0    STA $D01A
.C:c071  58          CLI
.C:c072  60          RTS
.C:c073  00          BRK
.C:c074  AD 12 D0    LDA $D012
.C:c077  C9 C2       CMP #$C2
.C:c079  90 F9       BCC $C074
.C:c07b  A2 0A       LDX #$0A
.C:c07d  CA          DEX
.C:c07e  D0 FD       BNE $C07D
.C:c080  EE 21 D0    INC $D021
.C:c083  4C 15 C0    JMP $C015

The initialization routine is at $c04c to install the new IRQ handler $c000.  The code jumps around a bit and has NOP (no instruction) as a by-product of patching the source in the monitor to get the desired functionality.   This is cleaned up a lot more in the sources to both repos.