With the preliminaries all done, it was time to write Snake. First, I had to find a bit of flash and RAM that were unused to store my code and variables. That wasn't hard: the existing firmware easily has more than 64K of free flash and 28K of unused RAM. To modify what the keyboard does, I modify the variables the original firmware has in RAM; for example, the PWM values and the keys pressed are both just arrays in RAM. With the JTAG/SWD port working again, finding out what RAM variable did what wasn't hard either. My code gets called because I added some hooks in the firmware: for example, the original firmware has a function that gets called when a key is pressed. If I'm interested in that event, I can just redirect the jump to my own subroutine, which then will call the original function.
After some hacking (and swearing because one or two updates broke the keyboard until I restored the backupped firmware), I finally could play a nice game of Snake: