Skip to content

Instantly share code, notes, and snippets.

@dan-rodrigues
Last active June 10, 2024 08:10
Show Gist options
  • Save dan-rodrigues/1e2805267d364e72979cbd3c45c58e8a to your computer and use it in GitHub Desktop.
Save dan-rodrigues/1e2805267d364e72979cbd3c45c58e8a to your computer and use it in GitHub Desktop.
neo homebrew sample
#include <stdint.h>
#include <stdbool.h>
// Reg defines, to extract to some header
#define REG_VRAMADDR (*((volatile uint16_t *)0x3c0000))
#define REG_VRAMDATA (*((volatile uint16_t *)0x3c0002))
#define REG_VRAMINC (*((volatile uint16_t *)0x3c0004))
#define REG_LSPCMODE (*((volatile uint16_t *)0x3c0006))
#define REG_INT_TIMER (*((volatile uint32_t *)0x3c0008))
#define REG_INT_TIMER_HI (*((volatile uint16_t *)0x3c0008))
#define REG_INT_TIMER_LO (*((volatile uint16_t *)0x3c000a))
#define REG_INT_TIMER_PAUSE (*((volatile uint16_t *)0x3c000e))
// ---
#define REG_WATCHDOG (*((volatile uint8_t *)0x300001))
// ---
#define PALETTE ((volatile uint16_t *)0x400000)
// ---
// Quick and dirty prog headers
// These would mostly be handled by the linker script rather than being fully defined here
typedef struct M68kVectors {
uint32_t reset_sp;
uint32_t irq_vectors[63];
} __attribute__((packed)) M68kVectors;
void user_entry(void);
typedef struct ProgHeader {
uint8_t recognition_code_1[7];
uint8_t system_type;
uint16_t ngh;
uint32_t prog_size;
uint32_t wram_backup;
uint16_t wram_backup_size;
uint8_t eyectacher_config;
uint8_t eyectacher_sprite_bank;
const uint8_t *jp_softdips;
const uint8_t *us_softdips;
const uint8_t *eu_softdips;
uint16_t user_jmp_op;
const void *user_entry;
// ...
uint8_t padding[0x5a];
// ...
const void *recognition_code_2;
} __attribute__((packed)) ProgHeader;
__attribute__((used))
__attribute__ ((section (".vectors")))
static const M68kVectors vectors = {
.reset_sp = 0x010f300,
.irq_vectors = {
// BERR
// ...
// VBL etc...
// ...
}
};
static const uint32_t recognition_code_2[] = {
0x76004A6D, 0x0A146600, 0x003C206D, 0x0A043E2D,
0x0A0813C0, 0x00300001, 0x32100C01, 0x00FF671A,
0x30280002, 0xB02D0ACE, 0x66103028, 0x0004B02D,
0x0ACF6606, 0xB22D0AD0, 0x67085088, 0x51CFFFD4,
0x36074E75, 0x206D0A04, 0x3E2D0A08, 0x3210E049,
0x0C0100FF, 0x671A3010, 0xB02D0ACE, 0x66123028,
0x0002E048, 0xB02D0ACF, 0x6606B22D, 0x0AD06708,
0x588851CF, 0xFFD83607,
0x4e750000
};
__attribute__((used))
__attribute__ ((section (".prog_header")))
static const ProgHeader header = {
.recognition_code_1 = { 'N', 'E', 'O', '-', 'G', 'E', 'O'},
.system_type = 0,
.ngh = 0x4040,
.recognition_code_2 = recognition_code_2,
.eyectacher_config = 2,
.user_jmp_op = 0x4ef9,
.user_entry = user_entry
};
// ---
void vram_clear(uint16_t address, uint16_t length, uint16_t data) {
REG_WATCHDOG = 0;
REG_VRAMADDR = address;
REG_VRAMINC = 1;
for (uint16_t i = 0; i < length; i++) {
REG_VRAMDATA = data;
}
REG_WATCHDOG = 0;
}
void fix_clear() {
vram_clear(0x7000, 0x500, ' ');
}
void pal_clear() {
REG_WATCHDOG = 0;
for (uint16_t i = 0; i < 0x1000; i++) {
PALETTE[i] = 0x8000;
}
REG_WATCHDOG = 0;
}
void pal_write(uint8_t palette, uint8_t index, uint16_t data) {
PALETTE[palette * 0x10 + index] = data;
}
uint16_t fix_location_vram(uint8_t x, uint8_t y) {
return 0x7000 + x * 0x20 + y;
}
void fix_write(uint8_t x, uint8_t y, const char *string) {
REG_VRAMINC = 0x20;
char c = 0;
const char *source = string;
// Top half of 8x16 glyph
REG_VRAMADDR = fix_location_vram(x, y);
while (c = (*source++)) {
REG_VRAMDATA = c | 0x100;
}
// Bottom half of 8x16 glyph
REG_VRAMADDR = fix_location_vram(x, y + 1);
source = string;
while (c = (*source++)) {
REG_VRAMDATA = c | 0x200;
}
}
__attribute__((noreturn))
void user_entry() {
vram_clear(0x0000, 0x8000, 0x0000);
fix_clear();
pal_clear();
pal_write(0, 1, 0x7fff);
pal_write(0, 2, 0x8000);
const char *greeting = "Hello world!";
const uint8_t x = 14;
const uint8_t y = 14;
fix_write(x, y, greeting);
while (true) {
REG_WATCHDOG = 0;
}
__builtin_unreachable();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment