Skip to content

Instantly share code, notes, and snippets.

@gabonator
Created February 11, 2023 14:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gabonator/22e2bee8f1b1e582a912029a818dec0d to your computer and use it in GitHub Desktop.
Save gabonator/22e2bee8f1b1e582a912029a818dec0d to your computer and use it in GitHub Desktop.
Novation mini mk3 hack - image editor
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
extern "C" uint32_t LED_BUFFER[];
extern "C" uint8_t MODE;
extern "C" int32_t TICKMS;
enum {Width = 32};
uint32_t image[Width][8] = {0};
uint32_t color = 0xab00cd;
uint8_t prevMode;
int scroll;
int scrollTarget;
int32_t scrollLast{0};
const static uint32_t palette[] = {
0x000000, 0xff0000, 0x00ff00, 0x0000ff,
0xffff00, 0xff00ff, 0x00ffff, 0xffffff};
uint32_t& pixel(int x, int y)
{
uint8_t f = (y+1)*10 + x+1;
return LED_BUFFER[f];
}
extern "C" void old_updateScreen();
extern "C" void new_updateScreen()
{
if (MODE != 5)
old_updateScreen();
}
extern "C" void old_loop();
extern "C" void new_loop()
{
old_loop();
if (MODE != 5)
{
prevMode = MODE;
return;
}
if (prevMode != 5)
{
prevMode = MODE;
for (int y=0; y<8; y++)
for (int x=0; x<8; x++)
pixel(x, y) = image[x+scroll][y];
for (int y=0; y<8; y++)
pixel(8, y) = palette[y];
pixel(2, -1) = 0x404040;
pixel(3, -1) = 0x404040;
}
if (scroll != scrollTarget)
{
if (scrollLast == 0)
scrollLast = TICKMS;
int passed = TICKMS - scrollLast;
if (passed > 50)
{
scrollLast = TICKMS;
if (scrollTarget > scroll)
scroll++;
if (scrollTarget < scroll)
scroll--;
for (int y=0; y<8; y++)
for (int x=0; x<8; x++)
pixel(x, y) = image[x+scroll][y];
}
}
}
extern "C" void old_onKeyDown(int, int);
extern "C" void new_onKeyDown(int a, int b)
{
// top buttons: 1 2 3 4 5
// right buttons: 19 29 39..89
old_onKeyDown(a, b);
if (MODE != 5)
return;
if (a == 3 && scrollTarget > 0)
scrollTarget -= 8;
if (a == 4 && scrollTarget < Width-8)
scrollTarget += 8;
if ((a%10) == 9 && a >= 19 && a <= 89)
color = palette[(a-19)/10];
int x = a%10;
int y = a/10;
if (x>=1 && x<=8 && y >= 1 && y <= 8)
{
x--;
y--;
if (pixel(x, y) == color)
image[x+scroll][y] = 0;
else
image[x+scroll][y] = color;
pixel(x, y) = image[x+scroll][y];
}
}
extern unsigned long _sidata;
extern unsigned long _sdata;
extern unsigned long _edata;
extern unsigned long _sbss;
extern unsigned long _ebss;
extern "C" void old_startup();
extern "C" void new_startup()
{
unsigned long *src, *dst;
src = &_sidata;
dst = &_sdata;
if (src != dst)
while(dst < &_edata)
*(dst++) = *(src++);
dst = &_sbss;
while(dst < &_ebss)
*(dst++) = 0;
old_startup();
}
MEMORY
{
rom (rx) : ORIGIN = 0x0801C4B0, LENGTH = 4K
ram (rwx) : ORIGIN = 0x2000d000, LENGTH = 2K
}
SECTIONS
{
.imports (NOLOAD) : {
/* TIM1_UP_TIM10_IRQHandle 0800c0a4 ed c0 01 08 addr LAB_0801c0ec+1 */
fw_base = 0x0800c000;
ptr_timer = 0x0800c0a4;
old_timer = 0x0801c0ed;
LED_BUFFER = 0x20000304;
REFRESH_BUFFER = 0x20006754;
MODE = 0x2000058c;
ptr_onKeyDown = 0x0800fd08;
old_onKeyDown = 0x0800fc5d;
ptr_loop = 0x0800cde6;
old_loop = 0x0800fcee;
ptr_updateScreen = 0x0800fe16;
old_updateScreen = 0x0800f9e0;
TICKMS = 0x200009c0; /* millisecond timer */
ptr_startup = 0x0800cc4a;
old_startup = 0x0800d98c;
}
.text : {
*(.text*)
*(.rodata*)
_sidata = .;
} >rom
.data : {
_sdata = .;
*(.data)
_edata = .;
} >ram AT >rom
.bss : {
_sbss = .;
*(.bss)
_ebss = .;
} >ram
}
var fs = require("fs");
var buf1 = fs.readFileSync("LPMiniMK3-407.bin");
var buf2 = fs.readFileSync("code.bin");
var buf3 = fs.readFileSync("code.dat");
var pad = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0])
if (buf1.length + pad.length != 0x104b0)
throw "error"
var buf = Buffer.concat([buf1, pad, buf2, buf3]);
var symbols = fs.readFileSync("code.txt").toString().split("\n").map(x=>x.split(" "));
function getSymbol(name)
{
var addr = symbols.find(x=>x[2] == name)[0];
console.log(name, addr);
if (!addr)
throw "no symbol"
var sym = parseInt("0x"+addr);
console.log(`${name} at 0x${sym.toString(16)}`);
return sym;
}
var firmwareBase = getSymbol("fw_base");
var vectorPtr = getSymbol("ptr_onKeyDown");
var vectorOrg = getSymbol("old_onKeyDown");
var vectorNew = getSymbol("new_onKeyDown") | 1;
swizzle(vectorPtr, vectorOrg, vectorNew);
var vectorPtr = getSymbol("ptr_startup");
var vectorOrg = getSymbol("old_startup");
var vectorNew = getSymbol("new_startup") | 1;
redirect(vectorPtr, vectorOrg, vectorNew);
redirect(getSymbol("ptr_loop"), getSymbol("old_loop"), getSymbol("new_loop"));
redirect(getSymbol("ptr_updateScreen"), getSymbol("old_updateScreen"), getSymbol("new_updateScreen"));
function swizzle(ptr, pold, pnew)
{
console.log(`swizzling at ${ptr.toString(16)} from ${pold.toString(16)} to ${pnew.toString(16)}`);
ptr -= firmwareBase;
if (buf[ptr+0] != ((pold>>0)&0xff) ||
buf[ptr+1] != ((pold>>8)&0xff) ||
buf[ptr+2] != ((pold>>16)&0xff) ||
buf[ptr+3] != ((pold>>24)&0xff))
throw "error";
buf[ptr+0] = (pnew>>0)&0xff;
buf[ptr+1] = (pnew>>8)&0xff;
buf[ptr+2] = (pnew>>16)&0xff;
buf[ptr+3] = (pnew>>24)&0xff;
}
function nop(base, pairs)
{
for (var i=0; i<pairs; i++)
{
buf[base+i*2-firmwareBase] = 0x00;
buf[base+i*2+1-firmwareBase] = 0xbf;
}
}
function redirect(address, oldtarget, newtarget)
{
console.log(`swizzling at ${address.toString(16)} from ${oldtarget.toString(16)} to ${newtarget.toString(16)}`);
baddr = address - firmwareBase;
var bytes = [buf[baddr], buf[baddr+1], buf[baddr+2], buf[baddr+3]]
var curtarget = thumbBlDisassembly(bytes, address);
if (oldtarget != curtarget)
throw "error";
console.log("target", oldtarget.toString(16), curtarget.toString(16));
var newbytes = thumbBlAssembly(address, newtarget);
if ((thumbBlDisassembly(newbytes, address)|1) != (newtarget|1))
throw "error";
buf[baddr+0] = newbytes[0];
buf[baddr+1] = newbytes[1];
buf[baddr+2] = newbytes[2];
buf[baddr+3] = newbytes[3];
}
function thumbBlAssembly(origin, target)
{
var offset = target - (origin + 4);
if (offset < 0)
throw "error";
offset >>= 1;
offset &= 0x00FFFFFF;
var words = [0xF000 | ((offset >> 11) & 0x7FF),
0xF800 | (offset & 0x7FF)];
return [words[0] & 255, words[0] >> 8, words[1] & 255, words[1] >> 8];
}
function thumbBlDisassembly(bytes, address)
{
var words = [bytes[0] | (bytes[1] << 8), bytes[2] | (bytes[3] << 8)]
console.log(address.toString(16), words[0].toString(16), words[1].toString(16));
if ((words[0] & 0xf800) != 0xf000 || (words[1] & 0xf800) != 0xf800)
throw "error";
var offset = (words[1] & 0x7ff) | ((words[0] & 0x7ff)<<11)
if (offset & 0x200000)
offset |= 0xfff00000
console.log("ofs", offset.toString(16));
offset <<= 1;
return address+offset+4;
}
fs.writeFileSync("final.bin", buf)
set -e
arm-none-eabi-g++ -fno-exceptions -fno-rtti -fno-common -Wno-psabi -Os -g -mcpu=cortex-m4 -mlittle-endian \
-mfpu=fpv4-sp-d16 -mthumb -nostartfiles -ffunction-sections -T code.lds code.cpp -o code.elf
arm-none-eabi-objdump -S -marm -d ./code.elf > code.s
arm-none-eabi-nm ./code.elf > code.txt
arm-none-eabi-objcopy -j .text -O binary ./code.elf code.bin
arm-none-eabi-objcopy -j .data -O binary ./code.elf code.dat
node combine.js
./bintosyx /minimk3 444 ./final.bin final.syx
rm code.txt code.elf code.bin code.dat final.bin code.s
#flash with (up up down down left right left right): https://fw.mat1jaczyyy.com/firmware
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment