-
Notifications
You must be signed in to change notification settings - Fork 0
/
bad_apple.ssppu
189 lines (156 loc) · 4.13 KB
/
bad_apple.ssppu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
* Bad Apple Demo
* ==============
* Usage :
* $ youtube-dl -f bestvideo -o "badapple.%(ext)s" FtutLA63Cp8
*
* $ mkdir ba_frames && ffmpeg -i badapple.* -vf scale=128:96:flags=neighbor -r 30 ba_frames/%04d.png
* $ for f in ba_frames/*.png; do convert -depth 1 "$f" "$f.pbm"; done
* $ for f in ba_frames/*.pbm; do python3 ../tools/pbm_to_badflate.py "$f" > "$f.bf"; done
* $ rm -v ba_frames/{*.png,*.pbm}
*
* $ cpp bad_apple.ssppu | python3 ../tools/ssppu_as.py bin > bad_apple.bin
* $ python3 ../tools/bad_apple.py /dev/ttyUSBX bad_apple.bin ba_frames/ 30
*
* I noticed that converting to PBM with ImageMagick instead of ffmpeg produced better results thanks to the
* lack of dithering. It should be possible to disable it in ffmpeg but it sounds like a pain to setup and
* requires a super long command. Using ImageMagick adds an extra step but makes the process easier to understand.
*/
// Variables
#define BUFFER_H 0x60
#define BUFFER_L 0x0
#define MEMCPY_RANGE_START 0x7FFF
#define MEMCPY_RANGE_END 0x7FFE
#define MEMCPY_TRAMPOLINE 0xD0
#define MEMCPY_TRAMPOLINE_LD_OFFSET 0xD1
#define MEMCPY_TRAMPOLINE_ST_OFFSET 0xD3
// MMIO
#define SERIAL_TX 0xD080
#define SERIAL_RX_STATUS 0xD081
#define SERIAL_RX_0 0xD0C0
#define SERIAL_RX_1 0xD0C1
#define SERIAL_CLK 0xD082
#define SERIAL_115200 4
#define DMA_BADFLATE_RAM_BASE 0xC0
#define DMA_BADFLATE_RAM_END 0xC4
#define DMA_BADFLATE_ADDR_H 0xC400
#define DMA_BADFLATE_ADDR_L 0xC401
#define DMA_BADFLATE_STATUS 0xC402
#define DMA_SERIAL_ADDR_H 0xC800
#define DMA_SERIAL_ADDR_L 0xC801
#define DMA_SERIAL_LEN_H 0xC802
#define DMA_SERIAL_LEN_L 0xC803
#define DMA_SERIAL_STATUS 0xC804
#define VIDEO_MODE 0xD0E0
#define VRAM_BASE 0x80
#define VRAM_END 0x86
org 0
entry:
LD Ra #BUFFER_H
ST [DMA_SERIAL_ADDR_H] Ra
ST [DMA_BADFLATE_ADDR_H] Ra
LD Ra #BUFFER_L
ST [DMA_SERIAL_ADDR_L] Ra
ST [DMA_BADFLATE_ADDR_L] Ra
// 1 bit per pixel
LD Ra #1
ST [VIDEO_MODE] Ra
// Byte to inform we are switching speed
CALL wait_for_uart_tx_ready
LD Ra #0xaa
ST [SERIAL_TX] Ra
CALL wait_for_uart_tx_ready
LD Ra #SERIAL_115200
ST [SERIAL_CLK] Ra
new_frame:
read_length:
LD Rb #2
read_length_loop:
LD Ra [SERIAL_RX_STATUS]
XOR nw Ra
JNE read_length_loop
ST [SERIAL_RX_STATUS] Ra
LD Ra [SERIAL_RX_1]
ST [DMA_SERIAL_LEN_L] Ra
LD Ra [SERIAL_RX_0]
ST [DMA_SERIAL_LEN_H] Ra
CALL wait_for_uart_tx_ready
LD Ra [DMA_SERIAL_LEN_H]
ST [SERIAL_TX] Ra
CALL wait_for_uart_tx_ready
LD Ra [DMA_SERIAL_LEN_L]
ST [SERIAL_TX] Ra
read_frame:
ST [DMA_SERIAL_STATUS] Ra
LD Rb #0xFF
wait_for_dma_serial_loop:
LD Ra [DMA_SERIAL_STATUS]
XOR nw Ra
JNE wait_for_dma_serial_loop
ST [SERIAL_RX_STATUS] Ra
memcpy_to_dma_badflate_ram:
LD Ra #DMA_BADFLATE_RAM_BASE
ST [MEMCPY_RANGE_START] Ra
LD Ra #DMA_BADFLATE_RAM_END
ST [MEMCPY_RANGE_END] Ra
CALL memcpy_from_buffer
decompress:
ST [DMA_BADFLATE_STATUS] Ra
LD Rb #0xFF
wait_for_dma_badflate_loop:
LD Ra [DMA_BADFLATE_STATUS]
XOR nw Ra
JNE wait_for_dma_badflate_loop
memcpy_to_vram:
LD Ra #VRAM_BASE
ST [MEMCPY_RANGE_START] Ra
LD Ra #VRAM_END
ST [MEMCPY_RANGE_END] Ra
CALL memcpy_from_buffer
end:
// Byte to inform we finished this frame
CALL wait_for_uart_tx_ready
LD Ra #0xbb
ST [SERIAL_TX] Ra
JMP new_frame
wait_for_uart_tx_ready:
LD Rb #0xFF
wait_for_uart_tx_ready_loop:
LD Ra [SERIAL_TX]
XOR nw Ra
JNE wait_for_uart_tx_ready_loop
RET
memcpy_from_buffer:
LD Ra #BUFFER_H
ST [MEMCPY_TRAMPOLINE_LD_OFFSET] Ra
LD Ra [MEMCPY_RANGE_START]
ST [MEMCPY_TRAMPOLINE_ST_OFFSET] Ra
memcpy_loop_init:
LD Ra #0
memcpy_loop:
// We can't use CALL since we are already in a subroutine and we have only one link register
JMP MEMCPY_TRAMPOLINE
memcpy_resume:
LD Rb #1
ADD Ra
JC memcpy_increment_trampoline
JMP memcpy_loop
memcpy_increment_trampoline:
LD Ra [MEMCPY_TRAMPOLINE_LD_OFFSET]
LD Rb #1
ADD Ra
ST [MEMCPY_TRAMPOLINE_LD_OFFSET] Ra
LD Ra [MEMCPY_TRAMPOLINE_ST_OFFSET]
LD Rb #1
ADD Ra
LD Rb [MEMCPY_RANGE_END]
XOR nw Ra
JE memcpy_end
ST [MEMCPY_TRAMPOLINE_ST_OFFSET] Ra
JMP memcpy_loop_init
memcpy_end:
RET
org MEMCPY_TRAMPOLINE
LD Rb [0xff,Ra]
ST [0xff,Ra] Rb
JMP memcpy_resume