{"id":15717867,"url":"https://github.com/celso/c64","last_synced_at":"2025-04-30T13:40:59.307Z","repository":{"id":142329751,"uuid":"229434643","full_name":"celso/c64","owner":"celso","description":"C64 Christmas Demo in 6510 Assembly","archived":false,"fork":false,"pushed_at":"2024-01-05T20:31:19.000Z","size":5910,"stargazers_count":65,"open_issues_count":1,"forks_count":14,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-30T16:37:57.572Z","etag":null,"topics":["assembly","c64","retrocomputing"],"latest_commit_sha":null,"homepage":"","language":"Assembly","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/celso.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-12-21T13:55:48.000Z","updated_at":"2025-01-31T10:23:38.000Z","dependencies_parsed_at":"2024-10-24T14:31:13.831Z","dependency_job_id":"329fdfd7-8d23-457b-b0f1-59b80713f38a","html_url":"https://github.com/celso/c64","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celso%2Fc64","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celso%2Fc64/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celso%2Fc64/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celso%2Fc64/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/celso","download_url":"https://codeload.github.com/celso/c64/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251713223,"owners_count":21631505,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assembly","c64","retrocomputing"],"created_at":"2024-10-03T21:51:30.635Z","updated_at":"2025-04-30T13:40:59.284Z","avatar_url":"https://github.com/celso.png","language":"Assembly","readme":"# C64 Christmas Demo\n\nThis is a pure 6510 assembly program for the Commodore 64 made by Bright Pixel in 2019, because why not.\n\n![Screenshot](img/demo.gif?raw=true)\n\nThe [C64][34] was a famous 8-bit machine in the 80s and the highest-selling single computer model ever.\n\nIts hardware and architecture set it appart from other 8-bit personal computers at the time. Unlike most of the others, the C64 had dedicated advanced MOS chips for graphics and sprites (the [VIC-II][35]), sound (the [SID][36]), I/O (the CIA), and many others.\n\nThese chips were not only powerful for the time, but they could perform their tasks autonomously, independently of what the main CPU, a MOS technology 6510 microprocessor, was doing. For instance, the VIC-II could generate interrupts on automatic sprite collisions. The CPU and the other chips also shared common data and memory BUSes.\n\nTo cope with all these chips inside 64Kbytes of addressable memory, the C64 had something called memory overlay, in which different chips would access different physical data locations for the same memory address. For instance the $D000-$DFFF block could be used for RAM, I/O or access to Character ROM, by the CPU, depending on a $0001 setting. Chips would have to be turned on or off, or instructed to look for data at specific RAM/ROM locations all the time to make the most of the machine as a whole.\n\n![Screenshot](https://upload.wikimedia.org/wikipedia/commons/8/8e/0430_-_C64_Mainboard_ASSY250407_RevB.jpg)\n\nThis was impressive in the 80s, for a relatevily cheap mass-market personal computer.\n\nProgramming the C64 was more than a lot of fun, it was a form of art. Because of the way all this hardware was packed together, handling the machine meant knowing its memory map and registers by heart, and dominating quite a collection of tricks, some of which weren't documented at all. What ended up being written for the C64 by the fervent community of developers all over the world went way beyond the imagination of [Jack Tramiel][37].\n\nToday, in 2019, the cult is still alive. There are vast groups of developers still writing C64 games and demos, restoring and using old machines, or using emulators. The SID sound chip was so revolutionary that it still drives a community of chiptune artists [all over][38] the world. The [High Voltage SID Collection][1] has more than 50,000 songs archived and growing.\n\nAt Bright Pixel, we like to go low-level, and we think that understanding how things work down there, even if we're talking about a 40 years old machine, is enriching, helps us become better computer engineers and better problem solvers. This is especially important in a time when we're flooded with hundreds of high-level frameworks that just \"do the job.\" Until they don't.\n\nThis is a simple demo for the C64:\n\n* It was coded entirely in 6510 assembly.\n* It makes use of the VIC-II graphics, character ROM and sprites.\n* It plays music using the SID chip.\n* Uses raster-based interrupts, perfectly timed.\n* Implements a random number generator.\n\nYou can download the source code for it in [this repository][3], change it and run it a real machine or an emulator. The code is all annotated, and you can use the [issue tracker][32] to ask us questions or make suggestions, we'll be listening.\n\n## Setup\n\n### Assembler\n\nWe used the [Kick Assembler][2] to build the PRG from the source. KA is still maintained up until today, with regular releases launched every couple of months. It supports [MOS 65xx assembler][26], macros, pseudo commands and has a couple of helpers to load SID and graphics files into memory. Unfortunately, you need Java to run it, but it's worth the trouble.\n\nHere's the setup in OSX.\n\nInstall Java\n\n```\nbrew update\nbrew install homebrew/cask/java\n```\n\nInstall Kick Assembler\n\n```\ncurl http://theweb.dk/KickAssembler/KickAssembler.zip -o /tmp/KickAssembler.zip\nsudo unzip /tmp/KickAssembler.zip -d /usr/local/KickAssembler\n```\n\nThis should be the contents of /usr/local/KickAssembler/KickAss.cfg\n\n```\n-showmem\n```\n\nAnd we have this alias in our ~/.bash_profile for convenience.\n\n```\nalias kick=\"java -jar /usr/local/KickAssembler/KickAss.jar\"\n```\n\n### C64 emulator\n\nThere are plenty of Commodore 64 emulators out there.\n\n![Screenshot](img/emulator.png?raw=true)\n\n* [VICE][4], the Versatile Commodore Emulator, is a program that runs on a Unix, Win32, or Mac OS X machines and emulates the C64 (and every other 65xx Commodore machine too).\n* [VirtualC64][5] is an interesting alternative for OSX written from scratch using C++ and native Cocoa and provides a real-time graphical inspector of the CPU, Memory, and the other Chips, while it's running.\n\nWe used VICE. One more bash alias:\n\n```\nalias c64=\"/Applications/x64.app/Contents/MacOS/x64\"\n```\n\n### Debugging\n\nDebugging assembly when things go south can be challenging. Back in the 80s, debugging meant spending hours doing trial and error, rebooting the machine, reloading the code from the [cassette][39] (or [disk drive][40], if you were lucky), and writing code to paper just in case you'd lose it in the process.\n\nLuckily, now we have way better tools.\n\n![Screenshot](img/debugger.png?raw=true)\n\n* The [C64 65XE Debugger][6] is a C64 and Atari XL/XE code and memory debugger that works in real-time and embeds the VICE emulator in the same graphical interface. It allows you to see what's happening with every chip, register, memory block; you can set breakpoints, run the program instruction by instruction, and see what's happening right in the embedded emulator.\n\n* The VICE emulator [built-in monitor][7] can also be used to examine, disassemble, and assemble machine language programs, as well as debug them through breakpoints. It has loads of powerful features.\n\nWhere were these tools in 1986?\n\n### Graphics\n\nDealing with graphics is a lot easier now too.\n\n* [Retropixels][8] is a cross-platform command-line tool to convert any image to Commodore 64 graphical modes and file formats, including Koala (.kla or .koa), which is supported by the Kick Assembler load helpers.\n\n* [Spritemate][9] is an online browser-based Commodore 64 sprite editor and supports importing and exporting of the most common file formats, as well as direct Kick Assembler hexadecimal arrays.\n\n### SID songs\n\nSID is short for the MOS 6581/8580 Sound Interface Device, the programmable sound generator chip inside the C64.\n\nA SID file (song.sid) is a special file format, later popularized by modern age SID Players and emulators, which contains both the data and the 6510 code necessary to play a music song on the SID chip.\n\nHere are a few things you should know about SID and SID files:\n\n* A SID file contains both the data and the code to play the music. The code must reside in a specific RAM address, specified inside the SID file, and changes from music to music, which means that if you want to use another .sid file with this demo, you need to make sure that:\n    * It starts in the same memory address.\n    * You change the code accordingly if it doesn't (advanced).\n    * It doesn't overlap with the rest of the memory we need to run our program (Kick Assembler will warn you if it does). RAM is scarce and musics can be big.\n* You can check the SID file [specification here][12].\n* You should absolutely take a look at the [High Voltage SID Collection][13] and this [SID player \u0026 visualizer][15] ([github][16]) in javascript\n\nKick Assembler has a helper script to load and parse a SID file directly into your project. [loadSid()][14] places the song code and data in the proper RAM location while assembling, and provides the initialization and play subroutines which you can use with your code. Check [here][14] for more information.\n\n### Other resources\n\nThese are handy resources you can use:\n\n* The [Commodore 64 memory map][10] explaining the functioning of all addresses, registers and memory blocks.\n* The [C64 Wiki][11] is the online bible of all things Commodore 64, including detailed information of how the hardware works.\n* Not the bible, but [Codebase64][18] is pretty good too.\n* A Kick Assembler [syntax file][17] for Vim.\n* Understanding the [character and bitmap][19] graphics modes, memory banks, and how the chips interact with each other.\n* 6510 CPU [instructions][20].\n* Great article explaining the VIC-II [screen modes][21].\n\n## The Code\n\nWe've annotated the .asm sources with all the information you need to understand what we're doing, why, and where to find more. You can start by looking at the main [card.asm][22] file and move from there.\n\nTo assemble the sources into an executable PRG file, all you need to do is:\n\n```\njava -jar /usr/local/KickAssembler/KickAss.jar card.asm\n```\n\nAnd the output should be something like this:\n\n![Screenshot](img/assembly.png?raw=true)\n\nHere's a quick run through the main components of this little demo. Find the rest of the information in the [source][22] itself.\n\n**Loading external files with Kick Assembler**\n\nKick Assembler has a couple of helpers to load known file formats into memory at assembly time. We're using two of these, [LoadBinary()][23] and [LoadSid()][24].\n\nLoadBinary is loading the Koala screen bitmaps we previously converted with [retropixels][8], while LoadSid is loading the data and code to play the music.sid file. You can check the [code][22] to see how we handle the music playing and the screen bitmaps in memory.\n\n```\n.var music = LoadSid(\"music.sid\")\n.var picture1 = LoadBinary(\"screen1.koa\", BF_KOALA)\n.var picture2 = LoadBinary(\"screen2.koa\", BF_KOALA)\n```\n\n**Setting and using the interrupts**\n\nWe're using [raster interrupts][26] with our demo. These interrupts trigger at specific scan lines that we set with $D012.\n\nFirst, we need to turn on the interrupts and set the first one when the program starts. This is how:\n\n```\n// disable the interrupts\nsei\n// first interrupt will be irq1 at scan line 240\nsetupirq(240, irq1);\n// interrupt control - enable all interrupts with $7b\nlda #%01111011\nsta $dc0d\n// interrupt control register - enable raster interrupt with $81\nlda #%10000001\nsta $d01a\n// enable interrupts now\ncli\n```\n\nLater we use the irq1 and irq2 interrupts.\n\nirq1 is triggered at scanline 240, and we use it to change the VIC-II to text mode, scroll the bottom text message and play the music.\n\nirq2 is triggered at scanline 10, and we use it to alternate between the two pictures by switching the VIC-II to bitmap mode and pointing it to the right memory banks.\n\nHere's irq1 from the [code][22]:\n\n```\nirq1:\n    ack();\n    // keep scrolling the bottom line\n    jsr scroll_message\n    // keep the music playing\n    jsr music.play\n    // jump to irq2 at line 10\n    setupirq(10, irq2);\n    // over and out\n    exitirq();\n    rti\n```\n\nNotice how each interrupt needs to:\n\n1. Acknowledge it started.\n1. Do its job.\n1. Setup the next interrupt.\n1. Restore the stack before exiting, then exit with [rti][26].\n\n**Random number generator**\n\nThe C64 doesn't have a random number generator, so we need to find a few unpredictable variables to play with to calculate one. This is a clever way to return a random number:\n\n```\n.macro rndGen(mask) {\n    // see http://sta.c64.org/cbm64mem.html\n    // first we read the current raster line (0-255)\n    lda $d012\n    // then we xor it with timer A (low byte)\n    // see https://www.c64-wiki.com/wiki/CIA\n    eor $dc04\n    // then we subtract it with timer A (high byte)\n    sbc $dc05\n    // finally we mask it so we can have a number between 0 and bits^2\n    and #mask\n}\n```\n\n**Using Sprites**\n\nA Sprite or a Movable Object Block (abbreviated to MOB) is a piece of graphics that can move and be assigned attributes independent of other graphics or text on the screen. The VIC-II, which is responsible for this feature of the C-64, supports up to eight sprites.\n\nYou can design your sprites with [Spritemate][28] and export them the Kick Assembler code directly by pressing file::save-file::kick-ass-hex.\n\nYou can check [this page][29] for more information about Sprites and how they work.\n\nAlso, check the [sprites.asm][27] code.\n\nA few things you need to know about Sprites:\n\n* They need to be in a memory address that is divisible by 64\n* They need to be in the same VIC-II video block where you want to display them\n* There's an array of Sprite pointers that contains the address of the sprite, relative to the beginning of the video block, divided by 64 (hence the first point).\n\nWe're using eight sprites with this demo with two different bitmaps. The sprites are displayed on the screen1 only, have fixed horizontal offsets, fall down the screen by changing their vertical offset in the main loop (we're not using the interrupts to handle the sprites), and randomly change colors when they start at vertical offset zero.\n\n**Playing music**\n\nThe music is loaded to the demo using an external SID file and Kick Assembler's LoadSid() helper.\n\nYou can [read above](#sid-songs) for more information on SID and SID files.\n\nPlaying a SID music in Kick Assembler has three parts:\n\n1. You use [LoadSid()][24] to import the .sid file\n2. You call music.init at start-up.\n3. You call music.play in every raster cycle (using one of the interrupts).\n\nAgain, be careful. A SID file contains both the data and the code to play the music. The code must reside in a specific RAM address, specified inside the SID file, and changes from music to music, which means that if you want to use another .sid file with this demo, you need to make sure that:\n\n* It starts in the same memory address.\n* You change the code accordingly if it doesn't (advanced).\n* It doesn't overlap with the rest of the memory we need to run our program (Kick Assembler will warn you if it does). RAM is scarce and musics can be big.\n\n## End\n\nThat's it. We hope you enjoyed reading this. Hopefully, you'll be playing with this demo, changing the source, and making it your own, feel free to use it. We had a lot of fun coding it.\n\nIf you have questions or suggestions, leave them in the [issue tracker][32], we'll be listening.\n\nIf you run the demo or change it in any way,  post it in the social webs, using the tag [#c64brpx][31], or mention [@brpxco][30].\n\nWe also did a [Make Your Own Christmas Card][41] webpage. Check it out.\n\nFinally, we did a [ZX Spectrum demo][33] a few months back; you might want to check it too.\n\nHappy holidays.\n\n\n[1]: https://www.hvsc.c64.org/\n[2]: http://theweb.dk/KickAssembler/\n[3]: https://github.com/brpx/c64\n[4]: http://vice-emu.sourceforge.net/\n[5]: http://www.dirkwhoffmann.de/virtualc64/\n[6]: https://sourceforge.net/projects/c64-debugger/\n[7]: http://vice-emu.sourceforge.net/vice_12.html#SEC271\n[8]: https://github.com/micheldebree/retropixels\n[9]: https://github.com/Esshahn/spritemate\n[10]: http://sta.c64.org/cbm64mem.html\n[11]: https://www.c64-wiki.com/\n[12]: https://www.hvsc.de/download/C64Music/DOCUMENTS/SID_file_format.txt\n[13]: https://www.hvsc.de/\n[14]: http://www.theweb.dk/KickAssembler/webhelp/content/ch12s03.html\n[15]: https://tamats.com/apps/sid/\n[16]: https://github.com/jagenjo/sidviz\n[17]: https://github.com/gryf/kickass-syntax-vim\n[18]: https://codebase64.org/\n[19]: http://www.coding64.org/?p=164\n[20]: http://www.unusedino.de/ec64/technical/aay/c64/bmain.htm\n[21]: https://dustlayer.com/vic-ii/2013/4/26/vic-ii-for-beginners-screen-modes-cheaper-by-the-dozen\n[22]: card.asm\n[23]: http://theweb.dk/KickAssembler/webhelp/content/ch12s02.html\n[24]: http://www.theweb.dk/KickAssembler/webhelp/content/ch12s03.html\n[25]: https://www.c64-wiki.com/wiki/Raster_interrupt\n[26]: http://www.unusedino.de/ec64/technical/aay/c64/brti.htm\n[27]: sprites.asm\n[28]: http://spritemate.com/\n[29]: https://www.c64-wiki.com/wiki/Sprite\n[30]: https://twitter.com/brpxco\n[31]: https://twitter.com/search?q=%23c64brpx\n[32]: https://github.com/brpx/c64/issues\n[33]: https://blog.pixels.camp/writing-a-zx-spectrum-game-6ffff2e5f10f\n[34]: https://en.wikipedia.org/wiki/Commodore_64\n[35]: https://en.wikipedia.org/wiki/MOS_Technology_VIC-II\n[36]: https://en.wikipedia.org/wiki/MOS_Technology_6581\n[37]: https://en.wikipedia.org/wiki/Jack_Tramiel\n[38]: https://www.kickstarter.com/projects/8-bit-symphony/8-bit-symphony-pro-double-orchestral-cd-of-8-bit-classics\n[39]: https://en.wikipedia.org/wiki/Commodore_Datasette\n[40]: https://en.wikipedia.org/wiki/Commodore_1541\n[41]: https://x.brpx.com/\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcelso%2Fc64","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcelso%2Fc64","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcelso%2Fc64/lists"}