+49
-1
app/app.config.ts
+49
-1
app/app.config.ts
···
16
16
link: {
17
17
variants: {
18
18
active: {
19
-
false: 'text-zelda-alttp-triforce-gold-500 hover:text-zelda-alttp-triforce-gold-500',
19
+
false: 'text-zelda-alttp-triforce-gold hover:text-alttp-white',
20
20
},
21
21
},
22
22
compoundVariants: [
···
68
68
},
69
69
},
70
70
},
71
+
prose: {
72
+
a: {
73
+
base: 'text-zelda-alttp-triforce-gold hover:text-zelda-alttp-triforce-gold-50',
74
+
},
75
+
h1: {
76
+
slots: {
77
+
base: 'text-[64px] font-normal',
78
+
},
79
+
},
80
+
h2: {
81
+
slots: {
82
+
base: 'text-[48px] font-normal',
83
+
leading: 'top-5.5',
84
+
},
85
+
},
86
+
h3: {
87
+
slots: {
88
+
base: 'text-[32px] font-normal',
89
+
leading: 'top-2 ',
90
+
},
91
+
},
92
+
93
+
pre: {
94
+
slots: {
95
+
filename: 'text-base',
96
+
},
97
+
},
98
+
99
+
code: {
100
+
base: 'alttp-text text-base font-normal',
101
+
variants: {
102
+
color: {
103
+
primary: 'text-alttp-white',
104
+
},
105
+
},
106
+
},
107
+
108
+
codeGroup: {
109
+
slots: {
110
+
trigger: 'text-base alttp-text',
111
+
},
112
+
},
113
+
codeCollapse: {
114
+
slots: {
115
+
trigger: 'text-base',
116
+
},
117
+
},
118
+
},
71
119
},
72
120
})
+10
app/assets/components/border-alttp.css
+10
app/assets/components/border-alttp.css
···
1
+
.border-alttp {
2
+
border-image-source: url('/images/sprites/alttp-textbox-css-sprite.png');
3
+
border-image-slice: 7 6;
4
+
border-image-width: 21px 18px;
5
+
border-image-repeat: stretch;
6
+
border-style: solid;
7
+
border-width: 21px 18px;
8
+
image-rendering: crisp-edges;
9
+
image-rendering: pixelated;
10
+
}
+19
app/assets/components/img-link-da-flute-boi.css
+19
app/assets/components/img-link-da-flute-boi.css
···
1
+
/*
2
+
This is a little hacky, but it works.
3
+
4
+
ContentSurround expects you use an icon, but we're not and we're using
5
+
an image instead.
6
+
*/
7
+
.link-da-flute-boi-img {
8
+
background: url('/images/link-da-flute-boi.png') no-repeat;
9
+
background-color: initial;
10
+
background-size: contain;
11
+
width: 100px;
12
+
height: 156px;
13
+
mask-image: none;
14
+
-webkit-mask-image: none;
15
+
16
+
&:where(.i-lucide\:arrow-left) {
17
+
transform: scaleX(-1);
18
+
}
19
+
}
+43
app/assets/components/text-alttp.css
+43
app/assets/components/text-alttp.css
···
1
+
.alttp-text {
2
+
font-family: 'alttp', serif;
3
+
color: var(--color-alttp-white);
4
+
5
+
image-rendering: crisp-edges;
6
+
image-rendering: pixelated;
7
+
-webkit-font-smoothing: none;
8
+
font-smooth: never;
9
+
10
+
text-shadow:
11
+
var(--ui-alttp-text-drop-shadow-size-neg) 0 var(--ui-alttp-text-shadow-color),
12
+
var(--ui-alttp-text-drop-shadow-size) 0 var(--ui-alttp-text-shadow-color),
13
+
0 var(--ui-alttp-text-drop-shadow-size-neg) var(--ui-alttp-text-shadow-color),
14
+
0 var(--ui-alttp-text-drop-shadow-size) var(--ui-alttp-text-shadow-color),
15
+
var(--ui-alttp-text-drop-shadow-size-neg) var(--ui-alttp-text-drop-shadow-size-neg)
16
+
var(--ui-alttp-text-shadow-color),
17
+
var(--ui-alttp-text-drop-shadow-size) var(--ui-alttp-text-drop-shadow-size-neg)
18
+
var(--ui-alttp-text-shadow-color),
19
+
var(--ui-alttp-text-drop-shadow-size-neg) var(--ui-alttp-text-drop-shadow-size)
20
+
var(--ui-alttp-text-shadow-color),
21
+
var(--ui-alttp-text-drop-shadow-size) var(--ui-alttp-text-drop-shadow-size)
22
+
var(--ui-alttp-text-shadow-color);
23
+
24
+
& > ::selection {
25
+
text-shadow:
26
+
var(--ui-alttp-text-drop-shadow-size-neg) 0 var(--ui-alttp-text-shadow-color),
27
+
var(--ui-alttp-text-drop-shadow-size) 0 var(--ui-alttp-text-shadow-color),
28
+
0 var(--ui-alttp-text-drop-shadow-size-neg) var(--ui-alttp-text-shadow-color),
29
+
0 var(--ui-alttp-text-drop-shadow-size) var(--ui-alttp-text-shadow-color),
30
+
var(--ui-alttp-text-drop-shadow-size-neg) var(--ui-alttp-text-drop-shadow-size-neg)
31
+
var(--ui-alttp-text-shadow-color),
32
+
var(--ui-alttp-text-drop-shadow-size) var(--ui-alttp-text-drop-shadow-size-neg)
33
+
var(--ui-alttp-text-shadow-color),
34
+
var(--ui-alttp-text-drop-shadow-size-neg) var(--ui-alttp-text-drop-shadow-size)
35
+
var(--ui-alttp-text-shadow-color),
36
+
var(--ui-alttp-text-drop-shadow-size) var(--ui-alttp-text-drop-shadow-size)
37
+
var(--ui-alttp-text-shadow-color);
38
+
}
39
+
40
+
& :where(code) {
41
+
@apply text-shadow-none selection:text-shadow-none;
42
+
}
43
+
}
+12
app/assets/components/triforce-cursor.css
+12
app/assets/components/triforce-cursor.css
+49
-117
app/assets/main.css
+49
-117
app/assets/main.css
···
1
1
@import 'tailwindcss' theme(static);
2
2
@import '@nuxt/ui';
3
-
@import './animations/rotate.css';
4
-
@import './animations/blink.css';
5
3
6
4
@theme static {
5
+
--color-zelda-alttp-triforce-gold: oklch(88.805% 0.18375 98.426);
6
+
--color-zelda-alttp-triforce-gold-50: oklch(96.262% 0.08423 100.53);
7
+
--color-zelda-alttp-triforce-gold-100: oklch(95.496% 0.10611 101.23);
8
+
--color-zelda-alttp-triforce-gold-200: oklch(93.726% 0.14167 100.89);
9
+
--color-zelda-alttp-triforce-gold-300: oklch(92.35% 0.16808 100.65);
10
+
--color-zelda-alttp-triforce-gold-400: oklch(91.137% 0.18324 99.835);
11
+
--color-zelda-alttp-triforce-gold-500: oklch(88.805% 0.18375 98.426);
12
+
--color-zelda-alttp-triforce-gold-600: oklch(73.431% 0.15199 98.538);
13
+
--color-zelda-alttp-triforce-gold-700: oklch(57.198% 0.11845 98.731);
14
+
--color-zelda-alttp-triforce-gold-800: oklch(39.696% 0.08231 99.146);
15
+
--color-zelda-alttp-triforce-gold-900: oklch(19.883% 0.04143 100.69);
16
+
--color-zelda-alttp-triforce-gold-950: oklch(0% 0 none);
17
+
18
+
--color-alttp-white: oklch(97.913% 0 none);
19
+
--color-alttp-white-50: oklch(100% 0 none);
20
+
--color-alttp-white-100: oklch(100% 0 none);
21
+
--color-alttp-white-200: oklch(100% 0 none);
22
+
--color-alttp-white-300: oklch(100% 0 none);
23
+
--color-alttp-white-400: oklch(100% 0 none);
24
+
--color-alttp-white-500: oklch(97.913% 0 none);
25
+
--color-alttp-white-600: oklch(89.449% 0 none);
26
+
--color-alttp-white-700: oklch(80.78% 0 none);
27
+
--color-alttp-white-800: oklch(71.871% 0 none);
28
+
--color-alttp-white-900: oklch(62.675% 0 none);
29
+
--color-alttp-white-950: oklch(57.951% 0 none);
30
+
7
31
--font-sans: 'alttp', 'Comic Sans MS', sans-serif;
8
32
--font-serif: 'alttp', 'Comic Sans MS', serif;
9
33
--ui-radius: 0rem;
10
-
--ui-primary: var(--ui-color-blue-400);
11
-
--ui-alttp-text-drop-shadow-size: 0.0625rem;
12
-
--ui-alttp-text-drop-shadow-size-neg: -0.0625rem;
13
-
--ui-alttp-text-shadow-color: #000071;
14
34
15
-
--color-zelda-alttp-triforce-gold-50: #feffe7;
16
-
--color-zelda-alttp-triforce-gold-100: #fcffc1;
17
-
--color-zelda-alttp-triforce-gold-200: #fdff86;
18
-
--color-zelda-alttp-triforce-gold-300: #fffa41;
19
-
--color-zelda-alttp-triforce-gold-400: #ffed0d;
20
-
--color-zelda-alttp-triforce-gold-500: #fada00;
21
-
--color-zelda-alttp-triforce-gold-600: #d1a400;
22
-
--color-zelda-alttp-triforce-gold-700: #a67602;
23
-
--color-zelda-alttp-triforce-gold-800: #895c0a;
24
-
--color-zelda-alttp-triforce-gold-900: #744b0f;
25
-
--color-zelda-alttp-triforce-gold-950: #442704;
35
+
--ui-alttp-text-drop-shadow-size: 1px;
36
+
--ui-alttp-text-drop-shadow-size-neg: -1px;
37
+
--ui-alttp-text-shadow-color: #000071;
26
38
}
27
39
28
40
@layer theme {
29
41
.light {
30
-
--ui-primary: var(--ui-color-primary-400);
31
42
--ui-bg: var(--ui-color-primary-800);
32
43
--ui-bg-muted: oklch(from var(--ui-primary) calc(l - 0.5) c h / 0.5);
33
44
--ui-bg-elevated: var(--color-primary-900);
34
45
--ui-bg-accented: var(--color-primary-950);
35
46
--ui-bg-inverted: var(--color-primary-100);
36
47
37
-
--ui-text: var(--color-neutral-100);
38
-
--ui-text-muted: var(--color-neutral-300);
39
-
--ui-text-highlighted: var(--color-neutral-100);
48
+
--ui-text: var(--color-alttp-white);
49
+
--ui-text-muted: var(--color-alttp-white-700);
50
+
--ui-text-highlighted: var(--color-alttp-white-50);
40
51
}
41
52
42
53
.dark {
···
57
68
::selection {
58
69
background-color: var(--ui-primary);
59
70
}
71
+
72
+
pre > code ::selection {
73
+
background-color: var(--ui-bg-elevated);
74
+
}
60
75
}
61
76
62
77
@layer base {
63
-
:root {
78
+
:root,
79
+
:host,
80
+
body {
64
81
scrollbar-width: thin;
65
82
scrollbar-gutter: stable;
66
-
}
67
-
68
-
body {
69
-
@apply subpixel-antialiased;
70
-
}
71
-
72
-
pre > code {
73
-
@apply text-shadow-none selection:text-shadow-none;
74
-
}
75
-
}
76
-
77
-
@layer utilities {
78
-
.alttp-text {
79
-
font-family: 'alttp', serif;
80
-
color: #f8f8f8;
81
-
82
-
image-rendering: crisp-edges;
83
+
text-rendering: optimizeSpeed;
83
84
image-rendering: pixelated;
84
85
-webkit-font-smoothing: none;
86
+
-moz-osx-font-smoothing: none;
85
87
font-smooth: never;
86
-
87
-
text-shadow:
88
-
var(--ui-alttp-text-drop-shadow-size-neg) 0 var(--ui-alttp-text-shadow-color),
89
-
var(--ui-alttp-text-drop-shadow-size) 0 var(--ui-alttp-text-shadow-color),
90
-
0 var(--ui-alttp-text-drop-shadow-size-neg) var(--ui-alttp-text-shadow-color),
91
-
0 var(--ui-alttp-text-drop-shadow-size) var(--ui-alttp-text-shadow-color),
92
-
var(--ui-alttp-text-drop-shadow-size-neg) var(--ui-alttp-text-drop-shadow-size-neg)
93
-
var(--ui-alttp-text-shadow-color),
94
-
var(--ui-alttp-text-drop-shadow-size) var(--ui-alttp-text-drop-shadow-size-neg)
95
-
var(--ui-alttp-text-shadow-color),
96
-
var(--ui-alttp-text-drop-shadow-size-neg) var(--ui-alttp-text-drop-shadow-size)
97
-
var(--ui-alttp-text-shadow-color),
98
-
var(--ui-alttp-text-drop-shadow-size) var(--ui-alttp-text-drop-shadow-size)
99
-
var(--ui-alttp-text-shadow-color);
100
-
101
-
& > ::selection {
102
-
text-shadow:
103
-
var(--ui-alttp-text-drop-shadow-size-neg) 0 var(--ui-alttp-text-shadow-color),
104
-
var(--ui-alttp-text-drop-shadow-size) 0 var(--ui-alttp-text-shadow-color),
105
-
0 var(--ui-alttp-text-drop-shadow-size-neg) var(--ui-alttp-text-shadow-color),
106
-
0 var(--ui-alttp-text-drop-shadow-size) var(--ui-alttp-text-shadow-color),
107
-
var(--ui-alttp-text-drop-shadow-size-neg) var(--ui-alttp-text-drop-shadow-size-neg)
108
-
var(--ui-alttp-text-shadow-color),
109
-
var(--ui-alttp-text-drop-shadow-size) var(--ui-alttp-text-drop-shadow-size-neg)
110
-
var(--ui-alttp-text-shadow-color),
111
-
var(--ui-alttp-text-drop-shadow-size-neg) var(--ui-alttp-text-drop-shadow-size)
112
-
var(--ui-alttp-text-shadow-color),
113
-
var(--ui-alttp-text-drop-shadow-size) var(--ui-alttp-text-drop-shadow-size)
114
-
var(--ui-alttp-text-shadow-color);
115
-
}
116
-
}
117
-
118
-
.cursor::after {
119
-
content: 'โผ';
120
-
animation: blink 0.8s step-start infinite;
121
-
margin-left: 0.25rem;
122
-
color: var(--color-zelda-alttp-triforce-gold-500);
123
-
}
124
-
125
-
.cursor:hover {
126
-
&::after {
127
-
color: var(--ui-text-muted);
128
-
}
129
88
}
89
+
}
130
90
131
-
.border-alttp {
132
-
border-image-source: url('/images/sprites/alttp-textbox-css-sprite.png');
133
-
border-image-slice: 7 6;
134
-
border-image-width: 21px 18px;
135
-
border-image-repeat: stretch;
136
-
border-style: solid;
137
-
border-width: 21px 18px;
138
-
image-rendering: crisp-edges;
139
-
image-rendering: pixelated;
140
-
}
141
-
142
-
.font-no-smoothing {
143
-
text-rendering: optimizeSpeed;
144
-
image-rendering: pixelated;
145
-
}
146
-
147
-
/*
148
-
This is a little hacky, but it works.
149
-
150
-
ContentSurround expects you use an icon, but we're not and we're using
151
-
an image instead.
152
-
*/
153
-
.link-da-flute-boi-img {
154
-
background: url('/images/link-da-flute-boi.png') no-repeat;
155
-
background-color: initial;
156
-
background-size: contain;
157
-
width: 100px;
158
-
height: 156px;
159
-
mask-image: none;
160
-
-webkit-mask-image: none;
161
-
162
-
&:where(.i-lucide\:arrow-left) {
163
-
transform: scaleX(-1);
164
-
}
165
-
}
91
+
@layer utilities {
92
+
@import './animations/rotate.css';
93
+
@import './animations/blink.css';
94
+
@import './components/triforce-cursor.css';
95
+
@import './components/border-alttp.css';
96
+
@import './components/img-link-da-flute-boi.css';
97
+
@import './components/text-alttp.css';
166
98
}
-23
app/components/content/ProseA.vue
-23
app/components/content/ProseA.vue
···
1
-
<template>
2
-
<u-link :href="props.href" :target="props.target" :attrs="$attrs">
3
-
<slot />
4
-
</u-link>
5
-
</template>
6
-
7
-
<script setup lang="ts">
8
-
import type { PropType } from 'vue'
9
-
10
-
const props = defineProps({
11
-
href: {
12
-
type: String,
13
-
default: '',
14
-
},
15
-
target: {
16
-
type: String as PropType<
17
-
'_blank' | '_parent' | '_self' | '_top' | (string & object) | null | undefined
18
-
>,
19
-
default: undefined,
20
-
required: false,
21
-
},
22
-
})
23
-
</script>
-3
app/components/content/ProseImg.vue
-3
app/components/content/ProseImg.vue
+1
-1
app/pages/[...slug].vue
+1
-1
app/pages/[...slug].vue
···
13
13
<content-renderer :value="page" class="grow-0 w-full sm:px-6" />
14
14
</div> -->
15
15
<post-toc :page="page" />
16
-
<content-renderer :value="page" class="grow-0 w-full sm:px-6" />
16
+
<content-renderer :value="page" class="sm:px-6 overflow-wrap" />
17
17
<u-content-surround :surround="surroundings" class="border-alttp bg-black" />
18
18
</post-detail>
19
19
<u-skeleton v-else class="flex-1" />
+2
-2
app/pages/list.vue
+2
-2
app/pages/list.vue
···
28
28
<span class="py-2">{{ format_date(post.date) }} - {{ post.title }}</span>
29
29
</div>
30
30
</u-link>
31
-
<div class="border-alttp bg-black" v-if="post.meta.excerpt">
32
-
<content-renderer :value="post.meta.excerpt" :excerpt="true" />
31
+
<div class="border-alttp bg-black" v-if="post.excerpt">
32
+
<content-renderer :value="post.excerpt" :excerpt="true" />
33
33
<u-link :to="post.path" prefetch-on="interaction">
34
34
<div class="alttp-text cursor hover:text-muted text-base">
35
35
<span>{{ t('posts.go_to_post') }}</span>
+8
-1
content.config.ts
+8
-1
content.config.ts
···
3
3
4
4
const schema = v.object({
5
5
title: v.string(),
6
+
excerpt: v.optional(
7
+
v.object({
8
+
type: v.string(),
9
+
children: v.optional(v.unknown()),
10
+
value: v.optional(v.array(v.unknown())),
11
+
}),
12
+
),
6
13
description: v.string(),
7
14
date: v.string(),
8
15
time: v.string(),
9
16
time_zone: v.string(),
10
-
encrypted: v.boolean(),
17
+
encrypted: v.optional(v.boolean()),
11
18
})
12
19
13
20
export default defineContentConfig({
+113
-37
content/blog/en/2026-02-27.md
+113
-37
content/blog/en/2026-02-27.md
···
18
18
### MG-Zero and gm112 combine their half-braincells together to form one braincell
19
19
20
20
During a conversation with Steve, we had talked about Ocarina of Time 2D and how
21
-
nice it would have been to do a panel on it. That discussion evolved into a
22
-
project that went down a rabbit hole of learning how to make a game on the
23
-
Game Boy. The idea was to make an Ocarina of Time 2D demo that would
24
-
play into the hopelessness of Demo 4 (I will write an article about this later).
21
+
nice it would have been to do a panel on it. To give a spotlight to the community
22
+
of where OoT2D was born. That discussion evolved into a project that went down
23
+
a rabbit hole of learning how to make a game on the Game Boy. The idea was to
24
+
make an Ocarina of Time 2D demo that would play into the hopelessness
25
+
of Demo 4 (I will write an article about this later).
26
+
27
+
Thankfully, I had already had been helping another friend of mine, Aaron,
28
+
with theirown Game Boy project, Dearborn, which has a similar playstyle to
29
+
2D Zeldas.
25
30
26
-
Thankfully, I had already had been helping another friend of mine with their
27
-
own Game Boy project, Dearborn, which has a similar playstyle to 2D Zeldas.
28
31
Aaron had been working on Dearborn, and then reached out to me to help with
29
32
developing the game. Since we were in pre-production, we experimented with
30
33
a couple of different ideas. One was a turned based combat system demo, which
···
59
62
60
63
### Figuring out the audio situation
61
64
62
-
For streaming the audio, originally I had written a simple bash script that
63
-
converted the WAV files into a C array. And then, I wrote a function that
64
-
DMA'd the array into the APU's Channel 3 effectively. The first atttempts
65
-
at this did not work, due to timing issues, logic that was too dumb, and
66
-
honestly I had no idea what I was doing. Ultimately, I decided to try
67
-
using GBVM's sound fx functions, seeing that it had a 3 second limitation.
68
-
It seemed if you timed it right, you could queue up multiple samples of
69
-
sounds and chain together a complete song! At this point, there was
70
-
no optimization, so the CPU was spent entirely on sending these commands.
71
-
Luckily, most of that time was spent idling, so it was simple enough to
72
-
move the commands into a GBVM script that ran on a separate "thread" that
73
-
instead no-ops. At this point, it was possible for the player to interact
74
-
with the game while they were being rick rolled. \:D
75
-
76
-
TOOD: Rework this section, and talk about setting up the audio.
77
-
78
-
<details class="bg-info p-4">
79
-
<summary>Click to see code! \:D</summary>
80
-
81
-
```asm
65
+
[GB Studio](https://www.gbstudio.dev/docs/assets/sound-effects/) noted
66
+
that audio had to be formatted a certain way. Using [yt-dlp](https://github.com/yt-dlp/yt-dlp){target="\_blank"}
67
+
and [ffmpeg](https://www.ffmpeg.org/){target="\_blank"} from the
68
+
AUR and started with a script to generate the WAV files.
69
+
70
+
::code-collapse
71
+
72
+
::code-group{name="Click to see code! :D"}
73
+
74
+
```bash [convert.sh]
75
+
#!/usr/bin/env bash
76
+
set -e
77
+
78
+
INPUT="$1"
79
+
OUTDIR="$2"
80
+
81
+
if [[ -z "$INPUT" || -z "$OUTDIR" ]]; then
82
+
echo "Usage: $0 input.webm output_dir"
83
+
exit 1
84
+
fi
85
+
86
+
mkdir -p "$OUTDIR"
87
+
88
+
ffmpeg -i "$INPUT" \
89
+
-filter_complex "\
90
+
[0:a]\
91
+
dcshift=0,\
92
+
pan=mono|c0=0.5*c0+0.5*c1,\
93
+
highpass=60,\
94
+
lowpass=3300,\
95
+
equalizer=f=400:t=q:w=1:g=-1.5,\
96
+
equalizer=f=1200:t=q:w=1:g=1.2,\
97
+
equalizer=f=1800:t=q:w=1:g=1.8,\
98
+
equalizer=f=2500:t=q:w=1:g=-3.2,\
99
+
volume=3.0\
100
+
[aud]; \
101
+
anoisesrc=color=white:amplitude=0.0007 \
102
+
[noise]; \
103
+
[aud][noise]amix=inputs=2:weights=1 0.15:duration=shortest,\
104
+
alimiter=limit=0.86\
105
+
[out]" \
106
+
-map "[out]" \
107
+
-ac 1 \
108
+
-ar 8000 \
109
+
-c:a pcm_u8 \
110
+
-f segment \
111
+
-segment_time 3 \
112
+
-reset_timestamps 1 \
113
+
"$OUTDIR/never-gonna-give-you-up-%03d.wav"
114
+
```
115
+
116
+
```bash [output.log]
117
+
ls -1 | wc -l
118
+
119
+
# 72
120
+
```
121
+
122
+
::
123
+
124
+
::
125
+
126
+
With around 72 output files, I removed 3 of them and replaced the last
127
+
one with a silence file. The next step was to see if we could playback the wave
128
+
files. My first attempt involved queuing up two GBVM `VM_SFX_PLAY` commands,
129
+
which didn't work on its own. Then I tried using a C-style array, and manually
130
+
using a DMA transfer to the APU's Channel 3. This worked, but my implementation
131
+
stole the CPU.
132
+
133
+
Going back to the drawing board, I felt the `VM_SFX_PLAY` approach could work.
134
+
Being the simpleton I am, I remembered that for the demo, I implemented a
135
+
function that played back a sound effect on the noise channel on a separate
136
+
script thread for a sword swing sound.
137
+
138
+
If that worked, then surely I could do the same thing, except with many WAV files!
139
+
140
+
::code-collapse
141
+
142
+
```asm [rick-roll.asm]
82
143
.macro PLAY_WAIT bank, snd, mask
83
144
VM_SFX_PLAY bank, snd, mask, .SFX_PRIORITY_NORMAL
84
145
VM_SET_CONST 0, 174
···
95
156
VM_RET_FAR
96
157
```
97
158
98
-
</details>
159
+
::
99
160
100
161
This bone headed bit of code was stuffed into a GBVM Script in the project file,
101
162
and thankfully it was good enough to handle playback. Shoutouts to the GBVM and
102
163
GB Studio developers for making this possible! Psst. See if you can find the
103
164
hidden easter egg in the ROM or code!
104
165
166
+
At this point I began testing in other emulators.
167
+
168
+
### Emulator Accuracy
169
+
170
+
This has to be called out, but upon realizing that I might have a problem with
171
+
audio playback on hardware, I resorted to testing the ROM on multiple emulators.
172
+
Up until this point, I had used [SkyEmu](https://skyemu.app/){target="\_blank"}
173
+
for testing. But, it seems every other emulator I tried, had issues with audio
174
+
playback. Audio pops, no playback, etc, it felt like maybe I was walking into a
175
+
brick wall once I tried this on a real Game Boy. Many hours and nights were
176
+
spent trying to figure out what was going on.
177
+
178
+
At this point my shipment of flashcarts had arrived, and I was able to test the
179
+
ROM on real hardware, and all seemed well! At this point, I wrote off the audio
180
+
issues in other emulators as something that's just out of scope to deal with,
181
+
especially given the simple approach to playing back Rick Astley's "Never Gonna
182
+
Give You Up". ;p
183
+
105
184
### Departing
106
185
107
186
A few months pass...
···
158
237
things, and also the people who were rooming with us at the hotel! Everyone was
159
238
chill.
160
239
161
-
TODO: Finish this section.
240
+
We made it to the hotel room, and I plopped on the bed and busted
241
+
out the goods. >: )
162
242
163
243
### Flashing Game Boy Carts
164
244
245
+
Because of missing some of the object data, I decided
246
+
to scrap Kikori Forest, the Great Deku Tree, and just make a simple
247
+
single room for the rick roll. After fiddling with the scene script,
248
+
a ROM flashing extravaganza happened! haha
249
+
165
250
{
166
251
fit="cover"
167
252
quality="90"
···
172
257
loading="lazy"
173
258
}
174
259
175
-
We made it to the hotel room, and I plopped on the bed and busted
176
-
out the goods. >: ) Because of missing some of the object data, I decided
177
-
to scrap Kikori Forest, the Great Deku Tree, and just make a simple
178
-
single room for the rick roll.
179
-
180
-
TODO: Finish this section
181
-
182
260
## Unleashing Rick Astley onto MAGFest
183
261
184
-
## The Rick Roll
185
-
186
262
To describe the rick roll, essentially just imagine a legitimate
187
263
startup sequence that hits you with an 8-bit rendering of the raw
188
264
waveforms of Rick Astley's "Never Gonna Give You Up". Haha, with
+1
package.json
+1
package.json
+10
pnpm-lock.yaml
+10
pnpm-lock.yaml
···
39
39
'@iconify-json/pixel':
40
40
specifier: ^1.2.1
41
41
version: 1.2.1
42
+
'@iconify-json/vscode-icons':
43
+
specifier: ^1.2.44
44
+
version: 1.2.44
42
45
'@js-temporal/polyfill':
43
46
specifier: ^0.5.1
44
47
version: 0.5.1
···
1036
1039
'@iconify-json/pixel@1.2.1':
1037
1040
resolution: {integrity: sha512-XwzURAMyZ/BJKeygh4PloKo9cUvS8GfcePueApzwrePvmwCwYT8SC581AQLxJHJl2FZjGWmUMVQ2FN3b9/cYyw==}
1038
1041
1042
+
'@iconify-json/vscode-icons@1.2.44':
1043
+
resolution: {integrity: sha512-3fLOIRRtsm6HD6UPJ3Y6/UztqxNTYgKA8VxrWeg1C+042MD3A/06CkWSsii1pz/f1zl0+YxvCHIVM3tXUlho+A==}
1044
+
1039
1045
'@iconify/collections@1.0.653':
1040
1046
resolution: {integrity: sha512-XT+u9JpO+o8dB2qrcI2FrNAZlDH0+flUqcOye4HkYnB+nPLjBXjFbJJTfODOCJzonSw/5axd5w/8fyY1g52d/w==}
1041
1047
···
8596
8602
dependencies:
8597
8603
'@iconify/types': 2.0.0
8598
8604
8605
+
'@iconify-json/vscode-icons@1.2.44':
8606
+
dependencies:
8607
+
'@iconify/types': 2.0.0
8608
+
8599
8609
'@iconify/collections@1.0.653':
8600
8610
dependencies:
8601
8611
'@iconify/types': 2.0.0
History
1 round
0 comments
gm112.bsky.social
submitted
#0
1 commit
expand
collapse
content: add more content for 2026-02-27 post feat: add navigation between pages fix: address theme issues #5 chore: cleanup styles chore: remove unnecessary prose components
expand 0 comments
pull request successfully merged